From 0da75c13111fdb7aaa0912467e847b7bac792e27 Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Thu, 23 Jul 2026 16:37:12 +0200 Subject: [PATCH] 0.1.0 --- .cargo/config.toml | 3 + .settings/org.eclipse.core.resources.prefs | 2 + .vscode/extensions.json | 3 + CHANGELOG.md | 23 + Cargo.toml | 140 + README.md | 39 + ROADMAP.md | 27 + RULES.md | 239 + RUST_RULES.md | 134 + clippy.toml | 39 + config/README.md | 62 + config/example.config.json | 5211 +++++++++++++++++ config/schema.config.json | 774 +++ docs/ACCOUNT_ONLY_CANDIDATES.md | 25 + docs/AGAVE_LOCAL_NODE.md | 210 + docs/APPLICATION_CRATES.md | 28 + docs/ARCHITECTURE.md | 108 + docs/BACKFILL_HTTP.md | 145 + docs/BAGS_FM.md | 26 + docs/CODE_REUSE.md | 41 + docs/CONFIGURATION.md | 114 + docs/CORE_EXTRACTION_CONTRACTS.md | 148 + docs/CORE_EXTRACTION_SELECTION_AND_REPLAY.md | 456 ++ docs/CORE_PROGRAM_IDS.md | 63 + docs/CORE_STORE.md | 73 + docs/DATABASE.md | 155 + docs/DECODER_MATERIALIZATION_CONTRACTS.md | 179 + docs/DECODER_SURFACE_AUDIT.md | 49 + docs/DELTA_WORKFLOW.md | 58 + docs/DEVELOPMENT_ORDER.md | 91 + docs/DEVNET_EXECUTION.md | 119 + docs/EXECUTION_MODEL.md | 387 ++ docs/EXECUTOR_SURFACE_MATRIX.md | 130 + docs/FOUNDATION_CLOSURE.md | 78 + docs/HISTORICAL_DATA_ACQUISITION_PLAN.md | 355 ++ docs/IDL_SURFACE_CLASSIFICATION.md | 107 + docs/INSTRUCTION_REPLAY_CONTRACTS.md | 112 + docs/LIVE_SOURCE_STRATEGY.md | 104 + docs/LOGGING.md | 106 + docs/MATERIALIZATIONS.md | 68 + docs/METAPLEX_TOKEN_METADATA_MATRIX.json | 1612 +++++ docs/NATIVE_SOLANA_CLOSURE_AUDIT.md | 113 + docs/NATIVE_SOLANA_DECODER_MATRIX.json | 155 + docs/NATIVE_SOLANA_EXECUTION_MATRIX.json | 1279 ++++ docs/NATIVE_SOLANA_MATERIALIZATION_AUDIT.md | 85 + docs/NATIVE_SOLANA_PROGRAMS.md | 233 + docs/NOMENCLATURE.md | 106 + docs/POSTGRES_STORE.md | 91 + docs/PROGRAM_CODE_INDEX.md | 42 + docs/PROGRAM_IDS.md | 15 + docs/PROGRAM_ID_CONSTANTS_AUDIT.md | 18 + docs/PROGRAM_NAMING.md | 152 + docs/PROGRAM_REGISTRY_CONTROL.md | 328 ++ docs/PROJECT_OBJECTIVES.md | 20 + docs/RAW_STORAGE_LIFECYCLE.md | 115 + docs/RAW_STORE.md | 147 + docs/REPLAY_PIPELINE.md | 80 + docs/RPC_ENDPOINT_ROLES.md | 77 + docs/RUST_WORKSPACE_RULE_AUDIT.md | 236 + docs/SOLANA_INTERFACE_DEPENDENCIES.md | 272 + docs/SOLANA_NATIVE_RPC_CLOSURE_AUDIT.md | 106 + docs/SOLANA_STANDARD_RPC_MATRIX.json | 430 ++ docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json | 199 + docs/SPL_ELGAMAL_REGISTRY_MATRIX.json | 121 + docs/SPL_MEMO_MATRIX.json | 266 + ...TOKEN_2022_CONFIDENTIAL_EXECUTION_AUDIT.md | 110 + docs/SPL_TOKEN_2022_MATRIX.json | 2698 +++++++++ docs/SPL_TOKEN_2022_SOURCE_AUDIT.md | 251 + docs/SPL_TOKEN_2022_VALIDATION_MATRIX.json | 146 + docs/SPL_TOKEN_MATRIX.json | 1440 +++++ docs/SQL_DEMOS.md | 109 + docs/SURFACE_CRATE_MATRIX.md | 272 + docs/TRACING_CONTRACT.md | 168 + docs/TRANSACTION_ACQUISITION_MODEL.md | 137 + docs/V0_4_6_VALIDATION.md | 66 + docs/VERSION_HEADER_AUDIT.md | 30 + docs/WS_LISTENERS.md | 69 + docs/legacy-khadhroony-bot2/CHANGELOG.md | 208 + docs/legacy-khadhroony-bot2/README.md | 293 + docs/legacy-khadhroony-bot2/ROADMAP.md | 1439 +++++ docs/legacy-khadhroony-bot2/RULES.md | 231 + docs/legacy-khadhroony-bot2/RUST_RULES.md | 134 + idls/001.README.md | 8 + ...BihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p.json | 1 + ...VGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4.json | 1 + ...Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY.json | 1 + ...NEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2.json | 1 + ...ivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s.json | 1 + ...Pc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3.json | 1 + ...PZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe.json | 1 + ...kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr.json | 1 + ...spfHX9JwWJsAb9epbkA8hmpSEAtxXy1V27QBH.json | 1 + ...HA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH.json | 1 + ...9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9.json | 1 + ...eSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT.json | 1 + ...sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn.json | 1 + ...ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1.json | 1 + ...wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5.json | 1 + ...HkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn.json | 1 + ...UfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ.json | 1 + ...pU4t19KYj8CfnbtRpnT8gtk4dvTHxVRwc2r7..json | 1 + ...KmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw.json | 1 + ...j8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M.json | 1 + ...AxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu.json | 1 + ...pjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X.json | 1 + ...ucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn.json | 1 + ...GBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu.json | 1 + ...ecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr.json | 1 + ...2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB.json | 1 + ...bZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4.json | 1 + ...FNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc.json | 1 + ...pWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr.json | 1 + ...g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD.json | 1 + ...R8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr.json | 1 + ...spG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd.json | 1 + ...siCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6.json | 1 + ...48px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn.json | 1 + ...9kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c.json | 1 + ...f31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA.json | 1 + ...SgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD.json | 1 + ...CCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ.json | 1 + ...BfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq.json | 1 + ...zsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM.json | 1 + ...cBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx.json | 1 + ...ZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG.json | 1 + ...LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN.json | 1 + ...RxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo.json | 1 + ...67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB.json | 1 + ...CLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi.json | 1 + ...VNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG.json | 1 + ...NsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P.json | 1 + ...pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u.json | 1 + ...RgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma.json | 1 + ...B8m7sLD2xi6snefSasaswsKCxx5Tifjondogm.json | 1 + ...AfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb.json | 1 + ...2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF.json | 1 + ...MiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.json | 1 + ...Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq.json | 1 + ...bS7nMMy5fbD42kXiUzGg8XQzJ972Xtk1cjWih.json | 0 ...v7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint.json | 1 + ...B6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json | 1 + ...ecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P.json | 1 + ...y6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA.json | 1 + ...EMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd.json | 1 + ...5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK.json | 1 + ...L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C.json | 1 + ...sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj.json | 1 + ...mn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE.json | 1 + ...MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB.json | 1 + ...p65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf.json | 1 + ...3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6.json | 1 + ...d8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ.json | 1 + ...HZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW.json | 1 + ...UCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m.json | 1 + ...dwDryooaGtiocG1u3xcYbRpiJzb283XfCZsDp.json | 1 + ...uy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ.json | 1 + ...bXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki.json | 1 + ...76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb.json | 1 + ...qBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD.json | 1 + kb-app-demo/Cargo.toml | 23 + kb-app-demo/src/main.rs | 14 + kb-config/Cargo.toml | 15 + kb-config/src/lib.rs | 23 + kb-core/Cargo.toml | 12 + kb-core/README.md | 23 + kb-core/src/error.rs | 197 + kb-core/src/lib.rs | 21 + kb-core/src/module.rs | 31 + kb-lib/Cargo.toml | 22 + kb-lib/src/decoder.rs | 34 + kb-lib/src/decoder/adapter.rs | 6 + .../decoder/adapter/saber_decimal_wrapper.rs | 10 + kb-lib/src/decoder/admin.rs | 7 + kb-lib/src/decoder/admin/jupiter_lock.rs | 10 + kb-lib/src/decoder/admin/pump_fees.rs | 10 + kb-lib/src/decoder/amm.rs | 31 + kb-lib/src/decoder/amm/aldrin_v1.rs | 10 + kb-lib/src/decoder/amm/aldrin_v2.rs | 10 + kb-lib/src/decoder/amm/alphaq.rs | 10 + kb-lib/src/decoder/amm/believe.rs | 10 + kb-lib/src/decoder/amm/bonk_swap.rs | 10 + kb-lib/src/decoder/amm/fluxbeam.rs | 10 + kb-lib/src/decoder/amm/goon_fi.rs | 10 + kb-lib/src/decoder/amm/goosefx_gamma.rs | 10 + kb-lib/src/decoder/amm/goosefx_v2.rs | 10 + kb-lib/src/decoder/amm/guac_swap.rs | 10 + kb-lib/src/decoder/amm/lifinity_swap_v2.rs | 10 + .../src/decoder/amm/metadao_futarchy_amm.rs | 10 + kb-lib/src/decoder/amm/metadao_v0_5.rs | 10 + kb-lib/src/decoder/amm/meteora_damm_v1.rs | 10 + kb-lib/src/decoder/amm/meteora_damm_v2.rs | 10 + kb-lib/src/decoder/amm/obric_v2.rs | 10 + kb-lib/src/decoder/amm/one_dex.rs | 10 + kb-lib/src/decoder/amm/pump_swap.rs | 10 + kb-lib/src/decoder/amm/raydium_lp_v4.rs | 10 + kb-lib/src/decoder/amm/solfi.rs | 10 + kb-lib/src/decoder/amm/solfi_v2.rs | 10 + kb-lib/src/decoder/amm/vertigo.rs | 10 + kb-lib/src/decoder/amm/virtuals.rs | 10 + kb-lib/src/decoder/amm/woofi.rs | 10 + kb-lib/src/decoder/amm/zero_fi.rs | 10 + kb-lib/src/decoder/amm/zora.rs | 10 + kb-lib/src/decoder/anchor.rs | 6 + kb-lib/src/decoder/anchor/core.rs | 10 + kb-lib/src/decoder/api.rs | 50 + kb-lib/src/decoder/api/constants.rs | 5 + kb-lib/src/decoder/api/contracts.rs | 576 ++ kb-lib/src/decoder/api/decoder.rs | 74 + kb-lib/src/decoder/bridge.rs | 9 + .../circle_cctp_token_messenger_minter.rs | 10 + .../circle_cctp_token_messenger_minter_v2.rs | 10 + .../src/decoder/bridge/layer_zero_endpoint.rs | 10 + .../src/decoder/bridge/layer_zero_executor.rs | 10 + kb-lib/src/decoder/clmm.rs | 11 + kb-lib/src/decoder/clmm/byreal.rs | 10 + kb-lib/src/decoder/clmm/fusion.rs | 10 + kb-lib/src/decoder/clmm/orca_whirlpool.rs | 10 + kb-lib/src/decoder/clmm/pancake_swap.rs | 10 + kb-lib/src/decoder/clmm/raydium.rs | 10 + kb-lib/src/decoder/clmm/stabble.rs | 10 + kb-lib/src/decoder/cpmm.rs | 6 + kb-lib/src/decoder/cpmm/raydium.rs | 10 + kb-lib/src/decoder/dlmm.rs | 6 + kb-lib/src/decoder/dlmm/meteora.rs | 10 + kb-lib/src/decoder/fees.rs | 7 + kb-lib/src/decoder/fees/bags_fee_share_v1.rs | 10 + kb-lib/src/decoder/fees/bags_fee_share_v2.rs | 10 + kb-lib/src/decoder/governance.rs | 6 + .../decoder/governance/metadao_bid_wall.rs | 10 + kb-lib/src/decoder/launchpad.rs | 14 + kb-lib/src/decoder/launchpad/boop_fun.rs | 10 + kb-lib/src/decoder/launchpad/metadao_ico.rs | 10 + kb-lib/src/decoder/launchpad/meteora_dbc.rs | 10 + kb-lib/src/decoder/launchpad/moonit.rs | 10 + .../src/decoder/launchpad/orca_wavebreak.rs | 10 + kb-lib/src/decoder/launchpad/printr.rs | 10 + kb-lib/src/decoder/launchpad/pump_fun.rs | 10 + .../src/decoder/launchpad/pump_pumpup_ai.rs | 10 + .../decoder/launchpad/raydium_launchlab.rs | 10 + kb-lib/src/decoder/lending.rs | 12 + kb-lib/src/decoder/lending/clone.rs | 10 + .../decoder/lending/jupiter_lend_borrow.rs | 10 + .../src/decoder/lending/jupiter_lend_earn.rs | 10 + .../lending/jupiter_lend_flash_loan.rs | 10 + .../decoder/lending/jupiter_lend_liquidity.rs | 10 + kb-lib/src/decoder/lending/kamino.rs | 10 + kb-lib/src/decoder/lending/marginfi_v2.rs | 10 + kb-lib/src/decoder/lock.rs | 6 + kb-lib/src/decoder/lock/raydium_lp.rs | 10 + kb-lib/src/decoder/metadata.rs | 7 + .../metadata/metaplex_token_metadata.rs | 10 + .../src/decoder/metadata/spl_name_service.rs | 10 + kb-lib/src/decoder/nft.rs | 6 + kb-lib/src/decoder/nft/metaplex_bubblegum.rs | 10 + kb-lib/src/decoder/orderbook.rs | 8 + .../decoder/orderbook/jupiter_limit_order.rs | 10 + .../orderbook/jupiter_limit_order_v2.rs | 10 + kb-lib/src/decoder/orderbook/openbook_v2.rs | 10 + kb-lib/src/decoder/perpetuals.rs | 8 + kb-lib/src/decoder/perpetuals/drift_v2.rs | 10 + kb-lib/src/decoder/perpetuals/jupiter.rs | 10 + kb-lib/src/decoder/perpetuals/zeta.rs | 10 + kb-lib/src/decoder/router.rs | 11 + .../src/decoder/router/dflow_aggregator_v4.rs | 10 + .../decoder/router/jupiter_aggregator_v4.rs | 10 + .../decoder/router/jupiter_aggregator_v6.rs | 10 + kb-lib/src/decoder/router/jupiter_dca.rs | 10 + kb-lib/src/decoder/router/okx_labs_v1.rs | 10 + kb-lib/src/decoder/router/okx_labs_v2.rs | 10 + kb-lib/src/decoder/rwa.rs | 6 + kb-lib/src/decoder/rwa/ondo_global_markets.rs | 10 + kb-lib/src/decoder/solana.rs | 6 + kb-lib/src/decoder/solana/core.rs | 10 + kb-lib/src/decoder/spl.rs | 14 + kb-lib/src/decoder/spl/account_compression.rs | 10 + .../decoder/spl/associated_token_account.rs | 10 + kb-lib/src/decoder/spl/elgamal_registry.rs | 10 + kb-lib/src/decoder/spl/memo.rs | 10 + kb-lib/src/decoder/spl/noop.rs | 10 + kb-lib/src/decoder/spl/single_pool.rs | 10 + kb-lib/src/decoder/spl/stake_pool.rs | 10 + kb-lib/src/decoder/spl/token.rs | 10 + kb-lib/src/decoder/spl/token_2022.rs | 10 + kb-lib/src/decoder/stable.rs | 9 + .../src/decoder/stable/swap_hylo_exchange.rs | 10 + .../src/decoder/stable/swap_jupiter_stable.rs | 10 + kb-lib/src/decoder/stable/swap_numeraire.rs | 10 + kb-lib/src/decoder/stable/swap_stabble.rs | 10 + kb-lib/src/decoder/staking.rs | 8 + kb-lib/src/decoder/staking/kamino_farm.rs | 10 + .../src/decoder/staking/marinade_finance.rs | 10 + kb-lib/src/decoder/staking/solayer.rs | 10 + kb-lib/src/decoder/treasury.rs | 6 + .../treasury/helium_treasury_management.rs | 10 + kb-lib/src/decoder/vault.rs | 11 + kb-lib/src/decoder/vault/carrot_defi.rs | 10 + .../src/decoder/vault/hylo_stability_pool.rs | 10 + kb-lib/src/decoder/vault/kamino.rs | 10 + kb-lib/src/decoder/vault/kamino_v2.rs | 10 + kb-lib/src/decoder/vault/kamino_yvaults.rs | 10 + kb-lib/src/decoder/vault/meteora.rs | 10 + kb-lib/src/decoder/vesting.rs | 6 + kb-lib/src/decoder/vesting/streamflow.rs | 10 + kb-lib/src/decoder/wallet.rs | 6 + .../wallet/jupiter_apepro_smart_wallet.rs | 10 + kb-lib/src/decoder/weighted.rs | 6 + kb-lib/src/decoder/weighted/swap_stabble.rs | 10 + kb-lib/src/executor.rs | 33 + kb-lib/src/executor/adapter.rs | 6 + .../executor/adapter/saber_decimal_wrapper.rs | 10 + kb-lib/src/executor/admin.rs | 7 + kb-lib/src/executor/admin/jupiter_lock.rs | 10 + kb-lib/src/executor/admin/pump_fees.rs | 10 + kb-lib/src/executor/amm.rs | 31 + kb-lib/src/executor/amm/aldrin_v1.rs | 10 + kb-lib/src/executor/amm/aldrin_v2.rs | 10 + kb-lib/src/executor/amm/alphaq.rs | 10 + kb-lib/src/executor/amm/believe.rs | 10 + kb-lib/src/executor/amm/bonk_swap.rs | 10 + kb-lib/src/executor/amm/fluxbeam.rs | 10 + kb-lib/src/executor/amm/goon_fi.rs | 10 + kb-lib/src/executor/amm/goosefx_gamma.rs | 10 + kb-lib/src/executor/amm/goosefx_v2.rs | 10 + kb-lib/src/executor/amm/guac_swap.rs | 10 + kb-lib/src/executor/amm/lifinity_swap_v2.rs | 10 + .../src/executor/amm/metadao_futarchy_amm.rs | 10 + kb-lib/src/executor/amm/metadao_v0_5.rs | 10 + kb-lib/src/executor/amm/meteora_damm_v1.rs | 10 + kb-lib/src/executor/amm/meteora_damm_v2.rs | 10 + kb-lib/src/executor/amm/obric_v2.rs | 10 + kb-lib/src/executor/amm/one_dex.rs | 10 + kb-lib/src/executor/amm/pump_swap.rs | 10 + kb-lib/src/executor/amm/raydium_lp_v4.rs | 10 + kb-lib/src/executor/amm/solfi.rs | 10 + kb-lib/src/executor/amm/solfi_v2.rs | 10 + kb-lib/src/executor/amm/vertigo.rs | 10 + kb-lib/src/executor/amm/virtuals.rs | 10 + kb-lib/src/executor/amm/woofi.rs | 10 + kb-lib/src/executor/amm/zero_fi.rs | 10 + kb-lib/src/executor/amm/zora.rs | 10 + kb-lib/src/executor/api.rs | 57 + kb-lib/src/executor/api/execution.rs | 496 ++ kb-lib/src/executor/api/executor.rs | 104 + kb-lib/src/executor/bridge.rs | 9 + .../circle_cctp_token_messenger_minter.rs | 10 + .../circle_cctp_token_messenger_minter_v2.rs | 10 + .../executor/bridge/layer_zero_endpoint.rs | 10 + .../executor/bridge/layer_zero_executor.rs | 10 + kb-lib/src/executor/clmm.rs | 11 + kb-lib/src/executor/clmm/byreal.rs | 10 + kb-lib/src/executor/clmm/fusion.rs | 10 + kb-lib/src/executor/clmm/orca_whirlpool.rs | 10 + kb-lib/src/executor/clmm/pancake_swap.rs | 10 + kb-lib/src/executor/clmm/raydium.rs | 10 + kb-lib/src/executor/clmm/stabble.rs | 10 + kb-lib/src/executor/cpmm.rs | 6 + kb-lib/src/executor/cpmm/raydium.rs | 10 + kb-lib/src/executor/dlmm.rs | 6 + kb-lib/src/executor/dlmm/meteora.rs | 10 + kb-lib/src/executor/fees.rs | 7 + kb-lib/src/executor/fees/bags_fee_share_v1.rs | 10 + kb-lib/src/executor/fees/bags_fee_share_v2.rs | 10 + kb-lib/src/executor/governance.rs | 6 + .../executor/governance/metadao_bid_wall.rs | 10 + kb-lib/src/executor/launchpad.rs | 14 + kb-lib/src/executor/launchpad/boop_fun.rs | 10 + kb-lib/src/executor/launchpad/metadao_ico.rs | 10 + kb-lib/src/executor/launchpad/meteora_dbc.rs | 10 + kb-lib/src/executor/launchpad/moonit.rs | 10 + .../src/executor/launchpad/orca_wavebreak.rs | 10 + kb-lib/src/executor/launchpad/printr.rs | 10 + kb-lib/src/executor/launchpad/pump_fun.rs | 10 + .../src/executor/launchpad/pump_pumpup_ai.rs | 10 + .../executor/launchpad/raydium_launchlab.rs | 10 + kb-lib/src/executor/lending.rs | 12 + kb-lib/src/executor/lending/clone.rs | 10 + .../executor/lending/jupiter_lend_borrow.rs | 10 + .../src/executor/lending/jupiter_lend_earn.rs | 10 + .../lending/jupiter_lend_flash_loan.rs | 10 + .../lending/jupiter_lend_liquidity.rs | 10 + kb-lib/src/executor/lending/kamino.rs | 10 + kb-lib/src/executor/lending/marginfi_v2.rs | 10 + kb-lib/src/executor/lock.rs | 6 + kb-lib/src/executor/lock/raydium_lp.rs | 10 + kb-lib/src/executor/metadata.rs | 7 + .../metadata/metaplex_token_metadata.rs | 10 + .../src/executor/metadata/spl_name_service.rs | 10 + kb-lib/src/executor/nft.rs | 6 + kb-lib/src/executor/nft/metaplex_bubblegum.rs | 10 + kb-lib/src/executor/orderbook.rs | 8 + .../executor/orderbook/jupiter_limit_order.rs | 10 + .../orderbook/jupiter_limit_order_v2.rs | 10 + kb-lib/src/executor/orderbook/openbook_v2.rs | 10 + kb-lib/src/executor/perpetuals.rs | 8 + kb-lib/src/executor/perpetuals/drift_v2.rs | 10 + kb-lib/src/executor/perpetuals/jupiter.rs | 10 + kb-lib/src/executor/perpetuals/zeta.rs | 10 + kb-lib/src/executor/router.rs | 11 + .../executor/router/dflow_aggregator_v4.rs | 10 + .../executor/router/jupiter_aggregator_v4.rs | 10 + .../executor/router/jupiter_aggregator_v6.rs | 10 + kb-lib/src/executor/router/jupiter_dca.rs | 10 + kb-lib/src/executor/router/okx_labs_v1.rs | 10 + kb-lib/src/executor/router/okx_labs_v2.rs | 10 + kb-lib/src/executor/rwa.rs | 6 + .../src/executor/rwa/ondo_global_markets.rs | 10 + kb-lib/src/executor/solana.rs | 6 + kb-lib/src/executor/solana/core.rs | 10 + kb-lib/src/executor/spl.rs | 14 + .../src/executor/spl/account_compression.rs | 10 + .../executor/spl/associated_token_account.rs | 10 + kb-lib/src/executor/spl/elgamal_registry.rs | 10 + kb-lib/src/executor/spl/memo.rs | 10 + kb-lib/src/executor/spl/noop.rs | 10 + kb-lib/src/executor/spl/single_pool.rs | 10 + kb-lib/src/executor/spl/stake_pool.rs | 10 + kb-lib/src/executor/spl/token.rs | 10 + kb-lib/src/executor/spl/token_2022.rs | 10 + kb-lib/src/executor/stable.rs | 9 + .../src/executor/stable/swap_hylo_exchange.rs | 10 + .../executor/stable/swap_jupiter_stable.rs | 10 + kb-lib/src/executor/stable/swap_numeraire.rs | 10 + kb-lib/src/executor/stable/swap_stabble.rs | 10 + kb-lib/src/executor/staking.rs | 8 + kb-lib/src/executor/staking/kamino_farm.rs | 10 + .../src/executor/staking/marinade_finance.rs | 10 + kb-lib/src/executor/staking/solayer.rs | 10 + kb-lib/src/executor/treasury.rs | 6 + .../treasury/helium_treasury_management.rs | 10 + kb-lib/src/executor/vault.rs | 11 + kb-lib/src/executor/vault/carrot_defi.rs | 10 + .../src/executor/vault/hylo_stability_pool.rs | 10 + kb-lib/src/executor/vault/kamino.rs | 10 + kb-lib/src/executor/vault/kamino_v2.rs | 10 + kb-lib/src/executor/vault/kamino_yvaults.rs | 10 + kb-lib/src/executor/vault/meteora.rs | 10 + kb-lib/src/executor/vesting.rs | 6 + kb-lib/src/executor/vesting/streamflow.rs | 10 + kb-lib/src/executor/wallet.rs | 6 + .../wallet/jupiter_apepro_smart_wallet.rs | 10 + kb-lib/src/executor/weighted.rs | 6 + kb-lib/src/executor/weighted/swap_stabble.rs | 10 + kb-lib/src/lib.rs | 198 + kb-lib/src/materializer.rs | 28 + kb-lib/src/materializer/admin.rs | 6 + kb-lib/src/materializer/admin/core.rs | 10 + kb-lib/src/materializer/api.rs | 31 + kb-lib/src/materializer/api/contracts.rs | 412 ++ kb-lib/src/materializer/api/materializer.rs | 22 + kb-lib/src/materializer/bridge.rs | 6 + kb-lib/src/materializer/bridge/core.rs | 10 + kb-lib/src/materializer/compliance.rs | 6 + kb-lib/src/materializer/compliance/audit.rs | 10 + kb-lib/src/materializer/fees.rs | 6 + kb-lib/src/materializer/fees/core.rs | 10 + kb-lib/src/materializer/governance.rs | 6 + kb-lib/src/materializer/governance/core.rs | 10 + kb-lib/src/materializer/lending.rs | 6 + kb-lib/src/materializer/lending/core.rs | 10 + kb-lib/src/materializer/lifecycle.rs | 6 + kb-lib/src/materializer/lifecycle/core.rs | 10 + kb-lib/src/materializer/liquidity.rs | 6 + kb-lib/src/materializer/liquidity/core.rs | 10 + kb-lib/src/materializer/metadata.rs | 6 + kb-lib/src/materializer/metadata/core.rs | 10 + kb-lib/src/materializer/nft.rs | 6 + kb-lib/src/materializer/nft/core.rs | 10 + kb-lib/src/materializer/oracle.rs | 6 + kb-lib/src/materializer/oracle/core.rs | 10 + kb-lib/src/materializer/orderbook.rs | 6 + kb-lib/src/materializer/orderbook/core.rs | 10 + kb-lib/src/materializer/perpetuals.rs | 6 + kb-lib/src/materializer/perpetuals/core.rs | 10 + kb-lib/src/materializer/pool.rs | 6 + kb-lib/src/materializer/pool/state.rs | 10 + kb-lib/src/materializer/rewards.rs | 6 + kb-lib/src/materializer/rewards/core.rs | 10 + kb-lib/src/materializer/risk.rs | 6 + kb-lib/src/materializer/risk/core.rs | 10 + kb-lib/src/materializer/routing.rs | 6 + kb-lib/src/materializer/routing/core.rs | 10 + kb-lib/src/materializer/staking.rs | 6 + kb-lib/src/materializer/staking/core.rs | 10 + kb-lib/src/materializer/token.rs | 7 + kb-lib/src/materializer/token/accounts.rs | 10 + .../src/materializer/token/metadata_risk.rs | 10 + kb-lib/src/materializer/trades.rs | 6 + kb-lib/src/materializer/trades/core.rs | 10 + kb-lib/src/materializer/transaction.rs | 6 + .../materializer/transaction/annotations.rs | 10 + kb-lib/src/materializer/vault.rs | 6 + kb-lib/src/materializer/vault/core.rs | 10 + kb-lib/src/model.rs | 81 + kb-lib/src/model/canonical_transaction.rs | 819 +++ kb-lib/src/model/decoded.rs | 87 + kb-lib/src/model/materialized.rs | 80 + kb-lib/src/model/nomenclature.rs | 99 + kb-lib/src/model/observation.rs | 31 + kb-lib/src/model/replay.rs | 126 + kb-lib/src/model/solana.rs | 43 + kb-logging/Cargo.toml | 12 + kb-logging/src/constants.rs | 5 + kb-logging/src/lib.rs | 18 + kb-pipeline/Cargo.toml | 19 + kb-pipeline/src/lib.rs | 13 + kb-program-ids/Cargo.toml | 12 + kb-program-ids/src/lib.rs | 22 + kb-rpc/Cargo.toml | 15 + kb-rpc/src/lib.rs | 13 + kb-store/Cargo.toml | 15 + kb-store/src/core.rs | 11 + kb-store/src/lib.rs | 14 + kb-store/src/postgres.rs | 9 + kb-wallet/Cargo.toml | 15 + kb-wallet/src/lib.rs | 13 + migration/README.md | 7 + .../khadhroony-bot2-reference/CHANGELOG.md | 208 + .../khadhroony-bot2-reference/Cargo.toml | 388 ++ migration/khadhroony-bot2-reference/README.md | 293 + .../khadhroony-bot2-reference/ROADMAP.md | 1439 +++++ migration/khadhroony-bot2-reference/RULES.md | 231 + .../khadhroony-bot2-reference/RUST_RULES.md | 134 + .../khadhroony-bot2-reference/clippy.toml | 39 + .../config/README.md | 62 + .../config/example.config.json | 5211 +++++++++++++++++ .../config/schema.config.json | 774 +++ .../docs/ACCOUNT_ONLY_CANDIDATES.md | 25 + .../docs/AGAVE_LOCAL_NODE.md | 210 + .../docs/APPLICATION_CRATES.md | 28 + .../docs/ARCHITECTURE.md | 108 + .../docs/BACKFILL_HTTP.md | 145 + .../khadhroony-bot2-reference/docs/BAGS_FM.md | 26 + .../docs/CODE_REUSE.md | 41 + .../docs/CONFIGURATION.md | 114 + .../docs/CORE_EXTRACTION_CONTRACTS.md | 148 + .../CORE_EXTRACTION_SELECTION_AND_REPLAY.md | 456 ++ .../docs/CORE_PROGRAM_IDS.md | 63 + .../docs/CORE_STORE.md | 73 + .../docs/DATABASE.md | 155 + .../docs/DECODER_MATERIALIZATION_CONTRACTS.md | 179 + .../docs/DECODER_SURFACE_AUDIT.md | 49 + .../docs/DELTA_WORKFLOW.md | 58 + .../docs/DEVELOPMENT_ORDER.md | 91 + .../docs/DEVNET_EXECUTION.md | 119 + .../docs/EXECUTION_MODEL.md | 387 ++ .../docs/EXECUTOR_SURFACE_MATRIX.md | 130 + .../docs/FOUNDATION_CLOSURE.md | 78 + .../docs/HISTORICAL_DATA_ACQUISITION_PLAN.md | 355 ++ .../docs/IDL_SURFACE_CLASSIFICATION.md | 107 + .../docs/INSTRUCTION_REPLAY_CONTRACTS.md | 112 + .../docs/LIVE_SOURCE_STRATEGY.md | 104 + .../khadhroony-bot2-reference/docs/LOGGING.md | 106 + .../docs/MATERIALIZATIONS.md | 68 + .../docs/METAPLEX_TOKEN_METADATA_MATRIX.json | 1612 +++++ .../docs/NATIVE_SOLANA_CLOSURE_AUDIT.md | 113 + .../docs/NATIVE_SOLANA_DECODER_MATRIX.json | 155 + .../docs/NATIVE_SOLANA_EXECUTION_MATRIX.json | 1279 ++++ .../NATIVE_SOLANA_MATERIALIZATION_AUDIT.md | 85 + .../docs/NATIVE_SOLANA_PROGRAMS.md | 233 + .../docs/NOMENCLATURE.md | 106 + .../docs/POSTGRES_STORE.md | 91 + .../docs/PROGRAM_CODE_INDEX.md | 42 + .../docs/PROGRAM_IDS.md | 15 + .../docs/PROGRAM_ID_CONSTANTS_AUDIT.md | 18 + .../docs/PROGRAM_NAMING.md | 152 + .../docs/PROGRAM_REGISTRY_CONTROL.md | 328 ++ .../docs/PROJECT_OBJECTIVES.md | 20 + .../docs/RAW_STORAGE_LIFECYCLE.md | 115 + .../docs/RAW_STORE.md | 147 + .../docs/REPLAY_PIPELINE.md | 80 + .../docs/RPC_ENDPOINT_ROLES.md | 77 + .../docs/RUST_WORKSPACE_RULE_AUDIT.md | 236 + .../docs/SOLANA_INTERFACE_DEPENDENCIES.md | 272 + .../docs/SOLANA_NATIVE_RPC_CLOSURE_AUDIT.md | 106 + .../docs/SOLANA_STANDARD_RPC_MATRIX.json | 430 ++ .../SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json | 199 + .../docs/SPL_ELGAMAL_REGISTRY_MATRIX.json | 121 + .../docs/SPL_MEMO_MATRIX.json | 266 + ...TOKEN_2022_CONFIDENTIAL_EXECUTION_AUDIT.md | 110 + .../docs/SPL_TOKEN_2022_MATRIX.json | 2698 +++++++++ .../docs/SPL_TOKEN_2022_SOURCE_AUDIT.md | 251 + .../SPL_TOKEN_2022_VALIDATION_MATRIX.json | 146 + .../docs/SPL_TOKEN_MATRIX.json | 1440 +++++ .../docs/SQL_DEMOS.md | 109 + .../docs/SURFACE_CRATE_MATRIX.md | 272 + .../docs/TRACING_CONTRACT.md | 168 + .../docs/TRANSACTION_ACQUISITION_MODEL.md | 137 + .../docs/V0_4_6_VALIDATION.md | 66 + .../docs/VERSION_HEADER_AUDIT.md | 30 + .../docs/WS_LISTENERS.md | 69 + .../idls/001.README.md | 8 + ...BihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p.json | 1 + ...VGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4.json | 1 + ...Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY.json | 1 + ...NEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2.json | 1 + ...ivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s.json | 1 + ...Pc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3.json | 1 + ...PZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe.json | 1 + ...kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr.json | 1 + ...spfHX9JwWJsAb9epbkA8hmpSEAtxXy1V27QBH.json | 1 + ...HA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH.json | 1 + ...9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9.json | 1 + ...eSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT.json | 1 + ...sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn.json | 1 + ...ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1.json | 1 + ...wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5.json | 1 + ...HkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn.json | 1 + ...UfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ.json | 1 + ...pU4t19KYj8CfnbtRpnT8gtk4dvTHxVRwc2r7..json | 1 + ...KmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw.json | 1 + ...j8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M.json | 1 + ...AxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu.json | 1 + ...pjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X.json | 1 + ...ucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn.json | 1 + ...GBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu.json | 1 + ...ecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr.json | 1 + ...2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB.json | 1 + ...bZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4.json | 1 + ...FNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc.json | 1 + ...pWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr.json | 1 + ...g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD.json | 1 + ...R8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr.json | 1 + ...spG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd.json | 1 + ...siCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6.json | 1 + ...48px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn.json | 1 + ...9kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c.json | 1 + ...f31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA.json | 1 + ...SgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD.json | 1 + ...CCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ.json | 1 + ...BfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq.json | 1 + ...zsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM.json | 1 + ...cBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx.json | 1 + ...ZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG.json | 1 + ...LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN.json | 1 + ...RxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo.json | 1 + ...67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB.json | 1 + ...CLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi.json | 1 + ...VNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG.json | 1 + ...NsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P.json | 1 + ...pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u.json | 1 + ...RgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma.json | 1 + ...B8m7sLD2xi6snefSasaswsKCxx5Tifjondogm.json | 1 + ...AfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb.json | 1 + ...2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF.json | 1 + ...MiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.json | 1 + ...Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq.json | 1 + ...bS7nMMy5fbD42kXiUzGg8XQzJ972Xtk1cjWih.json | 0 ...v7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint.json | 1 + ...B6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json | 1 + ...ecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P.json | 1 + ...y6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA.json | 1 + ...EMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd.json | 1 + ...5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK.json | 1 + ...L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C.json | 1 + ...sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj.json | 1 + ...mn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE.json | 1 + ...MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB.json | 1 + ...p65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf.json | 1 + ...3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6.json | 1 + ...d8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ.json | 1 + ...HZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW.json | 1 + ...UCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m.json | 1 + ...dwDryooaGtiocG1u3xcYbRpiJzb283XfCZsDp.json | 1 + ...uy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ.json | 1 + ...bXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki.json | 1 + ...76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb.json | 1 + ...qBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD.json | 1 + .../kb_app_cli/Cargo.toml | 22 + .../kb_app_cli/README.md | 17 + .../kb_app_cli/src/main.rs | 12 + .../kb_app_demo/Cargo.toml | 68 + .../kb_app_demo/README.md | 144 + .../kb_app_demo/build.rs | 12 + .../kb_app_demo/capabilities/default.json | 25 + .../kb_app_demo/frontend/demo_backfill.html | 188 + .../kb_app_demo/frontend/demo_config.html | 93 + .../frontend/demo_core_extraction.html | 160 + .../frontend/demo_decode_replay.html | 166 + .../frontend/demo_execution_solana_core.html | 73 + .../frontend/demo_execution_spl.html | 427 ++ .../kb_app_demo/frontend/demo_http.html | 96 + .../kb_app_demo/frontend/demo_sql_diag.html | 115 + .../frontend/demo_sql_pg_core.html | 102 + .../kb_app_demo/frontend/demo_sql_pg_raw.html | 102 + .../frontend/demo_sql_replay_candidates.html | 443 ++ .../kb_app_demo/frontend/demo_ws.html | 113 + .../frontend/fonts/DOS_Amazigh.ttf | Bin 0 -> 24672 bytes .../kb_app_demo/frontend/imgs/logo.png | Bin 0 -> 7759 bytes .../kb_app_demo/frontend/imgs/splash.png | Bin 0 -> 625668 bytes .../kb_app_demo/frontend/main.html | 131 + .../kb_app_demo/frontend/sass/_app.scss | 385 ++ .../frontend/sass/_bootswatch.scss | 160 + .../frontend/sass/_fontawesome.scss | 19 + .../kb_app_demo/frontend/sass/_simplebar.scss | 248 + .../kb_app_demo/frontend/sass/_variables.scss | 95 + .../kb_app_demo/frontend/sass/main.scss | 10 + .../kb_app_demo/frontend/sass/splash.scss | 126 + .../kb_app_demo/frontend/splash.html | 23 + .../kb_app_demo/frontend/ts/demo_backfill.ts | 267 + .../kb_app_demo/frontend/ts/demo_config.ts | 84 + .../frontend/ts/demo_core_extraction.ts | 185 + .../frontend/ts/demo_decode_replay.ts | 340 ++ .../frontend/ts/demo_execution_solana_core.ts | 31 + .../frontend/ts/demo_execution_spl.ts | 881 +++ .../kb_app_demo/frontend/ts/demo_http.ts | 181 + .../kb_app_demo/frontend/ts/demo_sql_diag.ts | 60 + .../frontend/ts/demo_sql_pg_core.ts | 42 + .../frontend/ts/demo_sql_pg_raw.ts | 42 + .../frontend/ts/demo_sql_replay_candidates.ts | 800 +++ .../frontend/ts/demo_sql_tables.ts | 68 + .../kb_app_demo/frontend/ts/demo_ws.ts | 325 + .../kb_app_demo/frontend/ts/frontend_log.ts | 97 + .../kb_app_demo/frontend/ts/main.ts | 256 + .../kb_app_demo/frontend/ts/splash.ts | 130 + .../kb_app_demo/icons/favicon.ico | Bin 0 -> 458870 bytes .../kb_app_demo/icons/favicon.png | Bin 0 -> 7759 bytes .../kb_app_demo/package.json | 34 + .../kb_app_demo/src/app_state.rs | 217 + .../kb_app_demo/src/constants.rs | 7 + .../kb_app_demo/src/demo_backfill.rs | 457 ++ .../kb_app_demo/src/demo_config.rs | 27 + .../kb_app_demo/src/demo_core_extraction.rs | 267 + .../kb_app_demo/src/demo_decode_replay.rs | 953 +++ .../src/demo_execution_solana_core.rs | 664 +++ .../kb_app_demo/src/demo_execution_spl.rs | 71 + .../kb_app_demo/src/demo_http.rs | 361 ++ .../kb_app_demo/src/demo_spl_ata.rs | 447 ++ .../kb_app_demo/src/demo_spl_token.rs | 493 ++ .../kb_app_demo/src/demo_spl_token_2022.rs | 423 ++ .../kb_app_demo/src/demo_sql_common.rs | 349 ++ .../kb_app_demo/src/demo_sql_diag.rs | 38 + .../kb_app_demo/src/demo_sql_pg_core.rs | 22 + .../kb_app_demo/src/demo_sql_pg_raw.rs | 22 + .../src/demo_sql_replay_candidates.rs | 409 ++ .../kb_app_demo/src/demo_ws.rs | 869 +++ .../kb_app_demo/src/frontend_log.rs | 191 + .../kb_app_demo/src/lib.rs | 338 ++ .../kb_app_demo/src/main.rs | 43 + .../kb_app_demo/src/main_window.rs | 54 + .../kb_app_demo/src/splash.rs | 80 + .../kb_app_demo/src/tauri.rs | 2258 +++++++ .../kb_app_demo/tauri.conf.json | 53 + .../kb_app_demo/tsconfig.json | 31 + .../kb_app_demo/vite.config.ts | 97 + .../kb_config/Cargo.toml | 19 + .../kb_config/README.md | 68 + .../kb_config/src/lib.rs | 70 + .../kb_config/src/settings.rs | 1530 +++++ .../kb_core/Cargo.toml | 14 + .../kb_core/README.md | 23 + .../kb_core/src/error.rs | 197 + .../kb_core/src/lib.rs | 21 + .../kb_core/src/module.rs | 31 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../kb_decoder_admin_jupiter_lock/Cargo.toml | 18 + .../kb_decoder_admin_jupiter_lock/README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../kb_decoder_admin_jupiter_lock/src/lib.rs | 16 + .../kb_decoder_admin_pump_fees/Cargo.toml | 18 + .../kb_decoder_admin_pump_fees/README.md | 17 + .../src/constants.rs | 7 + .../kb_decoder_admin_pump_fees/src/decoder.rs | 42 + .../kb_decoder_admin_pump_fees/src/lib.rs | 16 + .../kb_decoder_amm_aldrin_v1/Cargo.toml | 18 + .../kb_decoder_amm_aldrin_v1/README.md | 14 + .../kb_decoder_amm_aldrin_v1/src/constants.rs | 7 + .../kb_decoder_amm_aldrin_v1/src/decoder.rs | 41 + .../kb_decoder_amm_aldrin_v1/src/lib.rs | 16 + .../kb_decoder_amm_aldrin_v2/Cargo.toml | 18 + .../kb_decoder_amm_aldrin_v2/README.md | 14 + .../kb_decoder_amm_aldrin_v2/src/constants.rs | 7 + .../kb_decoder_amm_aldrin_v2/src/decoder.rs | 41 + .../kb_decoder_amm_aldrin_v2/src/lib.rs | 16 + .../kb_decoder_amm_alphaq/Cargo.toml | 18 + .../kb_decoder_amm_alphaq/README.md | 14 + .../kb_decoder_amm_alphaq/src/constants.rs | 7 + .../kb_decoder_amm_alphaq/src/decoder.rs | 41 + .../kb_decoder_amm_alphaq/src/lib.rs | 16 + .../kb_decoder_amm_believe/Cargo.toml | 18 + .../kb_decoder_amm_believe/README.md | 14 + .../kb_decoder_amm_believe/src/constants.rs | 7 + .../kb_decoder_amm_believe/src/decoder.rs | 41 + .../kb_decoder_amm_believe/src/lib.rs | 16 + .../kb_decoder_amm_bonk_swap/Cargo.toml | 18 + .../kb_decoder_amm_bonk_swap/README.md | 14 + .../kb_decoder_amm_bonk_swap/src/constants.rs | 7 + .../kb_decoder_amm_bonk_swap/src/decoder.rs | 41 + .../kb_decoder_amm_bonk_swap/src/lib.rs | 16 + .../kb_decoder_amm_fluxbeam/Cargo.toml | 18 + .../kb_decoder_amm_fluxbeam/README.md | 14 + .../kb_decoder_amm_fluxbeam/src/constants.rs | 7 + .../kb_decoder_amm_fluxbeam/src/decoder.rs | 41 + .../kb_decoder_amm_fluxbeam/src/lib.rs | 16 + .../kb_decoder_amm_goon_fi/Cargo.toml | 18 + .../kb_decoder_amm_goon_fi/README.md | 14 + .../kb_decoder_amm_goon_fi/src/constants.rs | 7 + .../kb_decoder_amm_goon_fi/src/decoder.rs | 41 + .../kb_decoder_amm_goon_fi/src/lib.rs | 16 + .../kb_decoder_amm_goosefx_gamma/Cargo.toml | 18 + .../kb_decoder_amm_goosefx_gamma/README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../kb_decoder_amm_goosefx_gamma/src/lib.rs | 16 + .../kb_decoder_amm_goosefx_v2/Cargo.toml | 18 + .../kb_decoder_amm_goosefx_v2/README.md | 14 + .../src/constants.rs | 7 + .../kb_decoder_amm_goosefx_v2/src/decoder.rs | 41 + .../kb_decoder_amm_goosefx_v2/src/lib.rs | 16 + .../kb_decoder_amm_guac_swap/Cargo.toml | 18 + .../kb_decoder_amm_guac_swap/README.md | 14 + .../kb_decoder_amm_guac_swap/src/constants.rs | 7 + .../kb_decoder_amm_guac_swap/src/decoder.rs | 41 + .../kb_decoder_amm_guac_swap/src/lib.rs | 16 + .../Cargo.toml | 18 + .../kb_decoder_amm_lifinity_swap_v2/README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../kb_decoder_amm_metadao_v0_5/Cargo.toml | 18 + .../kb_decoder_amm_metadao_v0_5/README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../kb_decoder_amm_metadao_v0_5/src/lib.rs | 16 + .../kb_decoder_amm_meteora_damm_v1/Cargo.toml | 18 + .../kb_decoder_amm_meteora_damm_v1/README.md | 17 + .../src/constants.rs | 7 + .../src/decoder.rs | 42 + .../kb_decoder_amm_meteora_damm_v1/src/lib.rs | 16 + .../kb_decoder_amm_meteora_damm_v2/Cargo.toml | 18 + .../kb_decoder_amm_meteora_damm_v2/README.md | 17 + .../src/constants.rs | 7 + .../src/decoder.rs | 42 + .../kb_decoder_amm_meteora_damm_v2/src/lib.rs | 16 + .../kb_decoder_amm_obric_v2/Cargo.toml | 18 + .../kb_decoder_amm_obric_v2/README.md | 14 + .../kb_decoder_amm_obric_v2/src/constants.rs | 7 + .../kb_decoder_amm_obric_v2/src/decoder.rs | 41 + .../kb_decoder_amm_obric_v2/src/lib.rs | 16 + .../kb_decoder_amm_one_dex/Cargo.toml | 18 + .../kb_decoder_amm_one_dex/README.md | 14 + .../kb_decoder_amm_one_dex/src/constants.rs | 7 + .../kb_decoder_amm_one_dex/src/decoder.rs | 41 + .../kb_decoder_amm_one_dex/src/lib.rs | 16 + .../kb_decoder_amm_pump_swap/Cargo.toml | 18 + .../kb_decoder_amm_pump_swap/README.md | 17 + .../kb_decoder_amm_pump_swap/src/constants.rs | 7 + .../kb_decoder_amm_pump_swap/src/decoder.rs | 42 + .../kb_decoder_amm_pump_swap/src/lib.rs | 16 + .../kb_decoder_amm_raydium_lp_v4/Cargo.toml | 18 + .../kb_decoder_amm_raydium_lp_v4/README.md | 17 + .../src/constants.rs | 7 + .../src/decoder.rs | 42 + .../kb_decoder_amm_raydium_lp_v4/src/lib.rs | 16 + .../kb_decoder_amm_solfi/Cargo.toml | 18 + .../kb_decoder_amm_solfi/README.md | 14 + .../kb_decoder_amm_solfi/src/constants.rs | 7 + .../kb_decoder_amm_solfi/src/decoder.rs | 41 + .../kb_decoder_amm_solfi/src/lib.rs | 16 + .../kb_decoder_amm_solfi_v2/Cargo.toml | 18 + .../kb_decoder_amm_solfi_v2/README.md | 14 + .../kb_decoder_amm_solfi_v2/src/constants.rs | 7 + .../kb_decoder_amm_solfi_v2/src/decoder.rs | 41 + .../kb_decoder_amm_solfi_v2/src/lib.rs | 16 + .../kb_decoder_amm_vertigo/Cargo.toml | 18 + .../kb_decoder_amm_vertigo/README.md | 14 + .../kb_decoder_amm_vertigo/src/constants.rs | 7 + .../kb_decoder_amm_vertigo/src/decoder.rs | 41 + .../kb_decoder_amm_vertigo/src/lib.rs | 16 + .../kb_decoder_amm_virtuals/Cargo.toml | 18 + .../kb_decoder_amm_virtuals/README.md | 14 + .../kb_decoder_amm_virtuals/src/constants.rs | 7 + .../kb_decoder_amm_virtuals/src/decoder.rs | 41 + .../kb_decoder_amm_virtuals/src/lib.rs | 16 + .../kb_decoder_amm_woofi/Cargo.toml | 18 + .../kb_decoder_amm_woofi/README.md | 14 + .../kb_decoder_amm_woofi/src/constants.rs | 7 + .../kb_decoder_amm_woofi/src/decoder.rs | 41 + .../kb_decoder_amm_woofi/src/lib.rs | 16 + .../kb_decoder_amm_zero_fi/Cargo.toml | 18 + .../kb_decoder_amm_zero_fi/README.md | 14 + .../kb_decoder_amm_zero_fi/src/constants.rs | 7 + .../kb_decoder_amm_zero_fi/src/decoder.rs | 41 + .../kb_decoder_amm_zero_fi/src/lib.rs | 16 + .../kb_decoder_amm_zora/Cargo.toml | 18 + .../kb_decoder_amm_zora/README.md | 14 + .../kb_decoder_amm_zora/src/constants.rs | 7 + .../kb_decoder_amm_zora/src/decoder.rs | 41 + .../kb_decoder_amm_zora/src/lib.rs | 16 + .../kb_decoder_anchor/Cargo.toml | 17 + .../kb_decoder_anchor/README.md | 17 + .../kb_decoder_anchor/src/constants.rs | 5 + .../kb_decoder_anchor/src/decoder.rs | 42 + .../kb_decoder_anchor/src/lib.rs | 17 + .../kb_decoder_api/Cargo.toml | 23 + .../kb_decoder_api/README.md | 25 + .../kb_decoder_api/src/constants.rs | 5 + .../kb_decoder_api/src/contracts.rs | 577 ++ .../kb_decoder_api/src/decoder.rs | 74 + .../kb_decoder_api/src/lib.rs | 56 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../kb_decoder_clmm_byreal/Cargo.toml | 18 + .../kb_decoder_clmm_byreal/README.md | 14 + .../kb_decoder_clmm_byreal/src/constants.rs | 7 + .../kb_decoder_clmm_byreal/src/decoder.rs | 41 + .../kb_decoder_clmm_byreal/src/lib.rs | 16 + .../kb_decoder_clmm_fusion/Cargo.toml | 18 + .../kb_decoder_clmm_fusion/README.md | 14 + .../kb_decoder_clmm_fusion/src/constants.rs | 7 + .../kb_decoder_clmm_fusion/src/decoder.rs | 41 + .../kb_decoder_clmm_fusion/src/lib.rs | 16 + .../kb_decoder_clmm_orca_whirlpool/Cargo.toml | 18 + .../kb_decoder_clmm_orca_whirlpool/README.md | 17 + .../src/constants.rs | 7 + .../src/decoder.rs | 42 + .../kb_decoder_clmm_orca_whirlpool/src/lib.rs | 16 + .../kb_decoder_clmm_pancake_swap/Cargo.toml | 18 + .../kb_decoder_clmm_pancake_swap/README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../kb_decoder_clmm_pancake_swap/src/lib.rs | 16 + .../kb_decoder_clmm_raydium/Cargo.toml | 18 + .../kb_decoder_clmm_raydium/README.md | 17 + .../kb_decoder_clmm_raydium/src/constants.rs | 7 + .../kb_decoder_clmm_raydium/src/decoder.rs | 42 + .../kb_decoder_clmm_raydium/src/lib.rs | 16 + .../kb_decoder_clmm_stabble/Cargo.toml | 18 + .../kb_decoder_clmm_stabble/README.md | 14 + .../kb_decoder_clmm_stabble/src/constants.rs | 7 + .../kb_decoder_clmm_stabble/src/decoder.rs | 41 + .../kb_decoder_clmm_stabble/src/lib.rs | 16 + .../kb_decoder_cpmm_raydium/Cargo.toml | 18 + .../kb_decoder_cpmm_raydium/README.md | 17 + .../kb_decoder_cpmm_raydium/src/constants.rs | 7 + .../kb_decoder_cpmm_raydium/src/decoder.rs | 42 + .../kb_decoder_cpmm_raydium/src/lib.rs | 16 + .../kb_decoder_dlmm_meteora/Cargo.toml | 18 + .../kb_decoder_dlmm_meteora/README.md | 17 + .../kb_decoder_dlmm_meteora/src/constants.rs | 7 + .../kb_decoder_dlmm_meteora/src/decoder.rs | 42 + .../kb_decoder_dlmm_meteora/src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 42 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 42 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../kb_decoder_launchpad_boop_fun/Cargo.toml | 18 + .../kb_decoder_launchpad_boop_fun/README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../kb_decoder_launchpad_boop_fun/src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 17 + .../src/constants.rs | 7 + .../src/decoder.rs | 42 + .../src/lib.rs | 16 + .../kb_decoder_launchpad_moonit/Cargo.toml | 18 + .../kb_decoder_launchpad_moonit/README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../kb_decoder_launchpad_moonit/src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../kb_decoder_launchpad_printr/Cargo.toml | 18 + .../kb_decoder_launchpad_printr/README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../kb_decoder_launchpad_printr/src/lib.rs | 16 + .../kb_decoder_launchpad_pump_fun/Cargo.toml | 18 + .../kb_decoder_launchpad_pump_fun/README.md | 17 + .../src/constants.rs | 7 + .../src/decoder.rs | 42 + .../kb_decoder_launchpad_pump_fun/src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 17 + .../src/constants.rs | 7 + .../src/decoder.rs | 42 + .../src/lib.rs | 16 + .../kb_decoder_lending_clone/Cargo.toml | 18 + .../kb_decoder_lending_clone/README.md | 14 + .../kb_decoder_lending_clone/src/constants.rs | 7 + .../kb_decoder_lending_clone/src/decoder.rs | 41 + .../kb_decoder_lending_clone/src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../kb_decoder_lending_kamino/Cargo.toml | 18 + .../kb_decoder_lending_kamino/README.md | 14 + .../src/constants.rs | 7 + .../kb_decoder_lending_kamino/src/decoder.rs | 41 + .../kb_decoder_lending_kamino/src/lib.rs | 16 + .../kb_decoder_lending_marginfi_v2/Cargo.toml | 18 + .../kb_decoder_lending_marginfi_v2/README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../kb_decoder_lending_marginfi_v2/src/lib.rs | 16 + .../kb_decoder_lock_raydium_lp/Cargo.toml | 18 + .../kb_decoder_lock_raydium_lp/README.md | 14 + .../src/constants.rs | 7 + .../kb_decoder_lock_raydium_lp/src/decoder.rs | 41 + .../kb_decoder_lock_raydium_lp/src/lib.rs | 16 + .../Cargo.toml | 26 + .../README.md | 178 + .../src/account.rs | 2595 ++++++++ .../src/canonical.rs | 417 ++ .../src/constants.rs | 258 + .../src/decoder.rs | 3055 ++++++++++ .../src/instruction.rs | 2732 +++++++++ .../src/lib.rs | 226 + .../Cargo.toml | 18 + .../README.md | 17 + .../src/constants.rs | 7 + .../src/decoder.rs | 55 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 17 + .../src/constants.rs | 7 + .../src/decoder.rs | 42 + .../src/lib.rs | 16 + .../kb_decoder_perpetuals_drift_v2/Cargo.toml | 18 + .../kb_decoder_perpetuals_drift_v2/README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../kb_decoder_perpetuals_drift_v2/src/lib.rs | 16 + .../kb_decoder_perpetuals_jupiter/Cargo.toml | 18 + .../kb_decoder_perpetuals_jupiter/README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../kb_decoder_perpetuals_jupiter/src/lib.rs | 16 + .../kb_decoder_perpetuals_zeta/Cargo.toml | 18 + .../kb_decoder_perpetuals_zeta/README.md | 14 + .../src/constants.rs | 7 + .../kb_decoder_perpetuals_zeta/src/decoder.rs | 41 + .../kb_decoder_perpetuals_zeta/src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../kb_decoder_router_jupiter_dca/Cargo.toml | 18 + .../kb_decoder_router_jupiter_dca/README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../kb_decoder_router_jupiter_dca/src/lib.rs | 16 + .../kb_decoder_router_okx_labs_v1/Cargo.toml | 18 + .../kb_decoder_router_okx_labs_v1/README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../kb_decoder_router_okx_labs_v1/src/lib.rs | 16 + .../kb_decoder_router_okx_labs_v2/Cargo.toml | 18 + .../kb_decoder_router_okx_labs_v2/README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../kb_decoder_router_okx_labs_v2/src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../kb_decoder_solana_core/Cargo.toml | 34 + .../kb_decoder_solana_core/README.md | 116 + .../kb_decoder_solana_core/src/accounts.rs | 137 + .../src/address_lookup_table.rs | 488 ++ .../src/compute_budget.rs | 883 +++ .../kb_decoder_solana_core/src/config.rs | 487 ++ .../kb_decoder_solana_core/src/constants.rs | 67 + .../kb_decoder_solana_core/src/decoder.rs | 613 ++ .../kb_decoder_solana_core/src/event.rs | 101 + .../kb_decoder_solana_core/src/feature.rs | 279 + .../kb_decoder_solana_core/src/lib.rs | 201 + .../kb_decoder_solana_core/src/loaders.rs | 1322 +++++ .../kb_decoder_solana_core/src/payload.rs | 328 ++ .../kb_decoder_solana_core/src/precompiles.rs | 1608 +++++ .../kb_decoder_solana_core/src/slashing.rs | 654 +++ .../kb_decoder_solana_core/src/stake.rs | 1327 +++++ .../kb_decoder_solana_core/src/system.rs | 685 +++ .../kb_decoder_solana_core/src/vote.rs | 1058 ++++ .../kb_decoder_solana_core/src/zk_elgamal.rs | 708 +++ .../src/zk_token_proof.rs | 932 +++ .../Cargo.toml | 18 + .../README.md | 17 + .../src/constants.rs | 7 + .../src/decoder.rs | 55 + .../src/lib.rs | 16 + .../Cargo.toml | 25 + .../README.md | 40 + .../src/constants.rs | 20 + .../src/decoder.rs | 619 ++ .../src/instruction.rs | 460 ++ .../src/lib.rs | 39 + .../Cargo.toml | 25 + .../kb_decoder_spl_elgamal_registry/README.md | 13 + .../src/constants.rs | 11 + .../src/decoder.rs | 256 + .../src/lib.rs | 38 + .../src/registry.rs | 191 + .../src/state.rs | 76 + .../kb_decoder_spl_memo/Cargo.toml | 24 + .../kb_decoder_spl_memo/README.md | 55 + .../kb_decoder_spl_memo/src/constants.rs | 21 + .../kb_decoder_spl_memo/src/decoder.rs | 438 ++ .../kb_decoder_spl_memo/src/lib.rs | 33 + .../kb_decoder_spl_memo/src/memo.rs | 375 ++ .../kb_decoder_spl_noop/Cargo.toml | 18 + .../kb_decoder_spl_noop/README.md | 17 + .../kb_decoder_spl_noop/src/constants.rs | 7 + .../kb_decoder_spl_noop/src/decoder.rs | 55 + .../kb_decoder_spl_noop/src/lib.rs | 16 + .../kb_decoder_spl_single_pool/Cargo.toml | 18 + .../kb_decoder_spl_single_pool/README.md | 17 + .../src/constants.rs | 7 + .../kb_decoder_spl_single_pool/src/decoder.rs | 55 + .../kb_decoder_spl_single_pool/src/lib.rs | 16 + .../kb_decoder_spl_stake_pool/Cargo.toml | 18 + .../kb_decoder_spl_stake_pool/README.md | 14 + .../src/constants.rs | 7 + .../kb_decoder_spl_stake_pool/src/decoder.rs | 41 + .../kb_decoder_spl_stake_pool/src/lib.rs | 16 + .../kb_decoder_spl_token/Cargo.toml | 25 + .../kb_decoder_spl_token/README.md | 89 + .../kb_decoder_spl_token/src/constants.rs | 50 + .../kb_decoder_spl_token/src/decoder.rs | 512 ++ .../kb_decoder_spl_token/src/lib.rs | 37 + .../kb_decoder_spl_token/src/token.rs | 1005 ++++ .../kb_decoder_spl_token_2022/Cargo.toml | 26 + .../kb_decoder_spl_token_2022/README.md | 30 + .../src/constants.rs | 72 + .../kb_decoder_spl_token_2022/src/decoder.rs | 1268 ++++ .../kb_decoder_spl_token_2022/src/lib.rs | 48 + .../kb_decoder_spl_token_2022/src/state.rs | 1647 ++++++ .../kb_decoder_spl_token_2022/src/token.rs | 2603 ++++++++ .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../kb_decoder_stable_swap_stabble/Cargo.toml | 18 + .../kb_decoder_stable_swap_stabble/README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../kb_decoder_stable_swap_stabble/src/lib.rs | 16 + .../kb_decoder_staking_kamino_farm/Cargo.toml | 18 + .../kb_decoder_staking_kamino_farm/README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../kb_decoder_staking_kamino_farm/src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../kb_decoder_staking_solayer/Cargo.toml | 18 + .../kb_decoder_staking_solayer/README.md | 14 + .../src/constants.rs | 7 + .../kb_decoder_staking_solayer/src/decoder.rs | 41 + .../kb_decoder_staking_solayer/src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../kb_decoder_vault_carrot_defi/Cargo.toml | 18 + .../kb_decoder_vault_carrot_defi/README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../kb_decoder_vault_carrot_defi/src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../kb_decoder_vault_kamino/Cargo.toml | 18 + .../kb_decoder_vault_kamino/README.md | 14 + .../kb_decoder_vault_kamino/src/constants.rs | 7 + .../kb_decoder_vault_kamino/src/decoder.rs | 41 + .../kb_decoder_vault_kamino/src/lib.rs | 16 + .../kb_decoder_vault_kamino_v2/Cargo.toml | 18 + .../kb_decoder_vault_kamino_v2/README.md | 14 + .../src/constants.rs | 7 + .../kb_decoder_vault_kamino_v2/src/decoder.rs | 41 + .../kb_decoder_vault_kamino_v2/src/lib.rs | 16 + .../Cargo.toml | 18 + .../kb_decoder_vault_kamino_yvaults/README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../kb_decoder_vault_meteora/Cargo.toml | 18 + .../kb_decoder_vault_meteora/README.md | 17 + .../kb_decoder_vault_meteora/src/constants.rs | 7 + .../kb_decoder_vault_meteora/src/decoder.rs | 42 + .../kb_decoder_vault_meteora/src/lib.rs | 16 + .../kb_decoder_vesting_streamflow/Cargo.toml | 18 + .../kb_decoder_vesting_streamflow/README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../kb_decoder_vesting_streamflow/src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../Cargo.toml | 18 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/decoder.rs | 41 + .../src/lib.rs | 16 + .../kb_execution_api/Cargo.toml | 19 + .../kb_execution_api/README.md | 101 + .../kb_execution_api/src/execution.rs | 496 ++ .../kb_execution_api/src/executor.rs | 104 + .../kb_execution_api/src/lib.rs | 61 + .../kb_execution_safety/Cargo.toml | 19 + .../kb_execution_safety/README.md | 55 + .../kb_execution_safety/src/lib.rs | 18 + .../kb_execution_safety/src/safety.rs | 698 +++ .../kb_execution_solana/Cargo.toml | 35 + .../kb_execution_solana/README.md | 151 + .../kb_execution_solana/src/constants.rs | 11 + .../kb_execution_solana/src/lib.rs | 35 + .../kb_execution_solana/src/nonce.rs | 276 + .../kb_execution_solana/src/transaction.rs | 982 ++++ .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../kb_executor_admin_jupiter_lock/Cargo.toml | 19 + .../kb_executor_admin_jupiter_lock/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_admin_jupiter_lock/src/lib.rs | 17 + .../kb_executor_admin_pump_fees/Cargo.toml | 19 + .../kb_executor_admin_pump_fees/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_admin_pump_fees/src/lib.rs | 17 + .../kb_executor_amm_aldrin_v1/Cargo.toml | 19 + .../kb_executor_amm_aldrin_v1/README.md | 15 + .../src/constants.rs | 5 + .../kb_executor_amm_aldrin_v1/src/executor.rs | 49 + .../kb_executor_amm_aldrin_v1/src/lib.rs | 17 + .../kb_executor_amm_aldrin_v2/Cargo.toml | 19 + .../kb_executor_amm_aldrin_v2/README.md | 15 + .../src/constants.rs | 5 + .../kb_executor_amm_aldrin_v2/src/executor.rs | 49 + .../kb_executor_amm_aldrin_v2/src/lib.rs | 17 + .../kb_executor_amm_alphaq/Cargo.toml | 19 + .../kb_executor_amm_alphaq/README.md | 15 + .../kb_executor_amm_alphaq/src/constants.rs | 5 + .../kb_executor_amm_alphaq/src/executor.rs | 49 + .../kb_executor_amm_alphaq/src/lib.rs | 17 + .../kb_executor_amm_believe/Cargo.toml | 19 + .../kb_executor_amm_believe/README.md | 15 + .../kb_executor_amm_believe/src/constants.rs | 5 + .../kb_executor_amm_believe/src/executor.rs | 49 + .../kb_executor_amm_believe/src/lib.rs | 17 + .../kb_executor_amm_bonk_swap/Cargo.toml | 19 + .../kb_executor_amm_bonk_swap/README.md | 15 + .../src/constants.rs | 5 + .../kb_executor_amm_bonk_swap/src/executor.rs | 49 + .../kb_executor_amm_bonk_swap/src/lib.rs | 17 + .../kb_executor_amm_fluxbeam/Cargo.toml | 19 + .../kb_executor_amm_fluxbeam/README.md | 15 + .../kb_executor_amm_fluxbeam/src/constants.rs | 5 + .../kb_executor_amm_fluxbeam/src/executor.rs | 49 + .../kb_executor_amm_fluxbeam/src/lib.rs | 17 + .../kb_executor_amm_goon_fi/Cargo.toml | 19 + .../kb_executor_amm_goon_fi/README.md | 15 + .../kb_executor_amm_goon_fi/src/constants.rs | 5 + .../kb_executor_amm_goon_fi/src/executor.rs | 49 + .../kb_executor_amm_goon_fi/src/lib.rs | 17 + .../kb_executor_amm_goosefx_gamma/Cargo.toml | 19 + .../kb_executor_amm_goosefx_gamma/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_amm_goosefx_gamma/src/lib.rs | 17 + .../kb_executor_amm_goosefx_v2/Cargo.toml | 19 + .../kb_executor_amm_goosefx_v2/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_amm_goosefx_v2/src/lib.rs | 17 + .../kb_executor_amm_guac_swap/Cargo.toml | 19 + .../kb_executor_amm_guac_swap/README.md | 15 + .../src/constants.rs | 5 + .../kb_executor_amm_guac_swap/src/executor.rs | 49 + .../kb_executor_amm_guac_swap/src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../kb_executor_amm_metadao_v0_5/Cargo.toml | 19 + .../kb_executor_amm_metadao_v0_5/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_amm_metadao_v0_5/src/lib.rs | 17 + .../Cargo.toml | 19 + .../kb_executor_amm_meteora_damm_v1/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../kb_executor_amm_meteora_damm_v2/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../kb_executor_amm_obric_v2/Cargo.toml | 19 + .../kb_executor_amm_obric_v2/README.md | 15 + .../kb_executor_amm_obric_v2/src/constants.rs | 5 + .../kb_executor_amm_obric_v2/src/executor.rs | 49 + .../kb_executor_amm_obric_v2/src/lib.rs | 17 + .../kb_executor_amm_one_dex/Cargo.toml | 19 + .../kb_executor_amm_one_dex/README.md | 15 + .../kb_executor_amm_one_dex/src/constants.rs | 5 + .../kb_executor_amm_one_dex/src/executor.rs | 49 + .../kb_executor_amm_one_dex/src/lib.rs | 17 + .../kb_executor_amm_pump_swap/Cargo.toml | 19 + .../kb_executor_amm_pump_swap/README.md | 15 + .../src/constants.rs | 5 + .../kb_executor_amm_pump_swap/src/executor.rs | 49 + .../kb_executor_amm_pump_swap/src/lib.rs | 17 + .../kb_executor_amm_raydium_lp_v4/Cargo.toml | 19 + .../kb_executor_amm_raydium_lp_v4/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_amm_raydium_lp_v4/src/lib.rs | 17 + .../kb_executor_amm_solfi/Cargo.toml | 19 + .../kb_executor_amm_solfi/README.md | 15 + .../kb_executor_amm_solfi/src/constants.rs | 5 + .../kb_executor_amm_solfi/src/executor.rs | 49 + .../kb_executor_amm_solfi/src/lib.rs | 17 + .../kb_executor_amm_solfi_v2/Cargo.toml | 19 + .../kb_executor_amm_solfi_v2/README.md | 15 + .../kb_executor_amm_solfi_v2/src/constants.rs | 5 + .../kb_executor_amm_solfi_v2/src/executor.rs | 49 + .../kb_executor_amm_solfi_v2/src/lib.rs | 17 + .../kb_executor_amm_vertigo/Cargo.toml | 19 + .../kb_executor_amm_vertigo/README.md | 15 + .../kb_executor_amm_vertigo/src/constants.rs | 5 + .../kb_executor_amm_vertigo/src/executor.rs | 49 + .../kb_executor_amm_vertigo/src/lib.rs | 17 + .../kb_executor_amm_virtuals/Cargo.toml | 19 + .../kb_executor_amm_virtuals/README.md | 15 + .../kb_executor_amm_virtuals/src/constants.rs | 5 + .../kb_executor_amm_virtuals/src/executor.rs | 49 + .../kb_executor_amm_virtuals/src/lib.rs | 17 + .../kb_executor_amm_woofi/Cargo.toml | 19 + .../kb_executor_amm_woofi/README.md | 15 + .../kb_executor_amm_woofi/src/constants.rs | 5 + .../kb_executor_amm_woofi/src/executor.rs | 49 + .../kb_executor_amm_woofi/src/lib.rs | 17 + .../kb_executor_amm_zero_fi/Cargo.toml | 19 + .../kb_executor_amm_zero_fi/README.md | 15 + .../kb_executor_amm_zero_fi/src/constants.rs | 5 + .../kb_executor_amm_zero_fi/src/executor.rs | 49 + .../kb_executor_amm_zero_fi/src/lib.rs | 17 + .../kb_executor_amm_zora/Cargo.toml | 19 + .../kb_executor_amm_zora/README.md | 15 + .../kb_executor_amm_zora/src/constants.rs | 5 + .../kb_executor_amm_zora/src/executor.rs | 49 + .../kb_executor_amm_zora/src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 51 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 53 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../kb_executor_clmm_byreal/Cargo.toml | 19 + .../kb_executor_clmm_byreal/README.md | 15 + .../kb_executor_clmm_byreal/src/constants.rs | 5 + .../kb_executor_clmm_byreal/src/executor.rs | 49 + .../kb_executor_clmm_byreal/src/lib.rs | 17 + .../kb_executor_clmm_fusion/Cargo.toml | 19 + .../kb_executor_clmm_fusion/README.md | 15 + .../kb_executor_clmm_fusion/src/constants.rs | 5 + .../kb_executor_clmm_fusion/src/executor.rs | 49 + .../kb_executor_clmm_fusion/src/lib.rs | 17 + .../Cargo.toml | 19 + .../kb_executor_clmm_orca_whirlpool/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../kb_executor_clmm_pancake_swap/Cargo.toml | 19 + .../kb_executor_clmm_pancake_swap/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_clmm_pancake_swap/src/lib.rs | 17 + .../kb_executor_clmm_raydium/Cargo.toml | 19 + .../kb_executor_clmm_raydium/README.md | 15 + .../kb_executor_clmm_raydium/src/constants.rs | 5 + .../kb_executor_clmm_raydium/src/executor.rs | 49 + .../kb_executor_clmm_raydium/src/lib.rs | 17 + .../kb_executor_clmm_stabble/Cargo.toml | 19 + .../kb_executor_clmm_stabble/README.md | 15 + .../kb_executor_clmm_stabble/src/constants.rs | 5 + .../kb_executor_clmm_stabble/src/executor.rs | 49 + .../kb_executor_clmm_stabble/src/lib.rs | 17 + .../kb_executor_cpmm_raydium/Cargo.toml | 19 + .../kb_executor_cpmm_raydium/README.md | 15 + .../kb_executor_cpmm_raydium/src/constants.rs | 5 + .../kb_executor_cpmm_raydium/src/executor.rs | 49 + .../kb_executor_cpmm_raydium/src/lib.rs | 17 + .../kb_executor_dlmm_meteora/Cargo.toml | 19 + .../kb_executor_dlmm_meteora/README.md | 15 + .../kb_executor_dlmm_meteora/src/constants.rs | 5 + .../kb_executor_dlmm_meteora/src/executor.rs | 49 + .../kb_executor_dlmm_meteora/src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 14 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 14 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../kb_executor_launchpad_boop_fun/Cargo.toml | 19 + .../kb_executor_launchpad_boop_fun/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_launchpad_boop_fun/src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../kb_executor_launchpad_moonit/Cargo.toml | 19 + .../kb_executor_launchpad_moonit/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_launchpad_moonit/src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../kb_executor_launchpad_printr/Cargo.toml | 19 + .../kb_executor_launchpad_printr/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_launchpad_printr/src/lib.rs | 17 + .../kb_executor_launchpad_pump_fun/Cargo.toml | 19 + .../kb_executor_launchpad_pump_fun/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_launchpad_pump_fun/src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../kb_executor_lending_clone/Cargo.toml | 19 + .../kb_executor_lending_clone/README.md | 15 + .../src/constants.rs | 5 + .../kb_executor_lending_clone/src/executor.rs | 49 + .../kb_executor_lending_clone/src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../kb_executor_lending_kamino/Cargo.toml | 19 + .../kb_executor_lending_kamino/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_lending_kamino/src/lib.rs | 17 + .../Cargo.toml | 19 + .../kb_executor_lending_marginfi_v2/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../kb_executor_lock_raydium_lp/Cargo.toml | 19 + .../kb_executor_lock_raydium_lp/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_lock_raydium_lp/src/lib.rs | 17 + .../Cargo.toml | 21 + .../README.md | 15 + .../src/constants.rs | 9 + .../src/executor.rs | 72 + .../src/lib.rs | 18 + .../Cargo.toml | 20 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/executor.rs | 92 + .../src/lib.rs | 16 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 51 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../kb_executor_perpetuals_drift_v2/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../kb_executor_perpetuals_jupiter/Cargo.toml | 19 + .../kb_executor_perpetuals_jupiter/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_perpetuals_jupiter/src/lib.rs | 17 + .../kb_executor_perpetuals_zeta/Cargo.toml | 19 + .../kb_executor_perpetuals_zeta/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_perpetuals_zeta/src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../kb_executor_router_jupiter_dca/Cargo.toml | 19 + .../kb_executor_router_jupiter_dca/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_router_jupiter_dca/src/lib.rs | 17 + .../kb_executor_router_okx_labs_v1/Cargo.toml | 19 + .../kb_executor_router_okx_labs_v1/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_router_okx_labs_v1/src/lib.rs | 17 + .../kb_executor_router_okx_labs_v2/Cargo.toml | 19 + .../kb_executor_router_okx_labs_v2/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_router_okx_labs_v2/src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../kb_executor_solana_core/Cargo.toml | 40 + .../kb_executor_solana_core/README.md | 915 +++ .../src/address_lookup_table.rs | 579 ++ .../kb_executor_solana_core/src/builder.rs | 2161 +++++++ .../kb_executor_solana_core/src/constants.rs | 7 + .../kb_executor_solana_core/src/executor.rs | 651 ++ .../kb_executor_solana_core/src/intent.rs | 2472 ++++++++ .../kb_executor_solana_core/src/lib.rs | 310 + .../kb_executor_solana_core/src/loader_v3.rs | 1121 ++++ .../kb_executor_solana_core/src/loader_v4.rs | 884 +++ .../src/native_admin.rs | 530 ++ .../src/precompiles.rs | 1247 ++++ .../kb_executor_solana_core/src/slashing.rs | 489 ++ .../kb_executor_solana_core/src/stake.rs | 1979 +++++++ .../kb_executor_solana_core/src/validation.rs | 184 + .../kb_executor_solana_core/src/vote.rs | 2316 ++++++++ .../kb_executor_solana_core/src/zk_elgamal.rs | 441 ++ .../Cargo.toml | 20 + .../README.md | 14 + .../src/constants.rs | 7 + .../src/executor.rs | 92 + .../src/lib.rs | 16 + .../Cargo.toml | 25 + .../README.md | 35 + .../src/builder.rs | 490 ++ .../src/constants.rs | 7 + .../src/executor.rs | 284 + .../src/intent.rs | 122 + .../src/lib.rs | 34 + .../Cargo.toml | 29 + .../README.md | 6 + .../src/builder.rs | 279 + .../src/constants.rs | 7 + .../src/executor.rs | 163 + .../src/intent.rs | 82 + .../src/lib.rs | 30 + .../kb_executor_spl_memo/Cargo.toml | 28 + .../kb_executor_spl_memo/README.md | 50 + .../kb_executor_spl_memo/src/builder.rs | 501 ++ .../kb_executor_spl_memo/src/constants.rs | 7 + .../kb_executor_spl_memo/src/executor.rs | 318 + .../kb_executor_spl_memo/src/intent.rs | 112 + .../kb_executor_spl_memo/src/lib.rs | 34 + .../kb_executor_spl_noop/Cargo.toml | 20 + .../kb_executor_spl_noop/README.md | 14 + .../kb_executor_spl_noop/src/constants.rs | 7 + .../kb_executor_spl_noop/src/executor.rs | 92 + .../kb_executor_spl_noop/src/lib.rs | 16 + .../kb_executor_spl_single_pool/Cargo.toml | 20 + .../kb_executor_spl_single_pool/README.md | 14 + .../src/constants.rs | 7 + .../src/executor.rs | 92 + .../kb_executor_spl_single_pool/src/lib.rs | 16 + .../kb_executor_spl_stake_pool/Cargo.toml | 19 + .../kb_executor_spl_stake_pool/README.md | 15 + .../src/constants.rs | 7 + .../src/executor.rs | 49 + .../kb_executor_spl_stake_pool/src/lib.rs | 16 + .../kb_executor_spl_token/Cargo.toml | 28 + .../kb_executor_spl_token/README.md | 98 + .../kb_executor_spl_token/src/builder.rs | 1179 ++++ .../kb_executor_spl_token/src/constants.rs | 15 + .../kb_executor_spl_token/src/executor.rs | 241 + .../kb_executor_spl_token/src/intent.rs | 457 ++ .../kb_executor_spl_token/src/lib.rs | 90 + .../kb_executor_spl_token_2022/Cargo.toml | 32 + .../kb_executor_spl_token_2022/README.md | 139 + .../kb_executor_spl_token_2022/src/builder.rs | 4937 ++++++++++++++++ .../src/confidential.rs | 576 ++ .../src/constants.rs | 13 + .../src/executor.rs | 332 ++ .../kb_executor_spl_token_2022/src/intent.rs | 1612 +++++ .../kb_executor_spl_token_2022/src/lib.rs | 265 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../kb_executor_stable_swap_stabble/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../kb_executor_staking_kamino_farm/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../kb_executor_staking_solayer/Cargo.toml | 19 + .../kb_executor_staking_solayer/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_staking_solayer/src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 51 + .../src/lib.rs | 17 + .../kb_executor_vault_carrot_defi/Cargo.toml | 19 + .../kb_executor_vault_carrot_defi/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_vault_carrot_defi/src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../kb_executor_vault_kamino/Cargo.toml | 19 + .../kb_executor_vault_kamino/README.md | 15 + .../kb_executor_vault_kamino/src/constants.rs | 5 + .../kb_executor_vault_kamino/src/executor.rs | 49 + .../kb_executor_vault_kamino/src/lib.rs | 17 + .../kb_executor_vault_kamino_v2/Cargo.toml | 19 + .../kb_executor_vault_kamino_v2/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_vault_kamino_v2/src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 17 + .../kb_executor_vault_meteora/Cargo.toml | 19 + .../kb_executor_vault_meteora/README.md | 15 + .../src/constants.rs | 5 + .../kb_executor_vault_meteora/src/executor.rs | 49 + .../kb_executor_vault_meteora/src/lib.rs | 17 + .../kb_executor_vesting_streamflow/Cargo.toml | 19 + .../kb_executor_vesting_streamflow/README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../kb_executor_vesting_streamflow/src/lib.rs | 17 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 51 + .../src/lib.rs | 16 + .../Cargo.toml | 19 + .../README.md | 15 + .../src/constants.rs | 5 + .../src/executor.rs | 49 + .../src/lib.rs | 16 + .../kb_logging/Cargo.toml | 18 + .../kb_logging/README.md | 38 + .../kb_logging/src/config.rs | 58 + .../kb_logging/src/constants.rs | 7 + .../kb_logging/src/lib.rs | 27 + .../kb_logging/src/tracing_runtime.rs | 1110 ++++ .../kb_materializer_admin/Cargo.toml | 23 + .../kb_materializer_admin/README.md | 25 + .../kb_materializer_admin/src/constants.rs | 10 + .../kb_materializer_admin/src/lib.rs | 23 + .../kb_materializer_admin/src/materializer.rs | 1151 ++++ .../kb_materializer_api/Cargo.toml | 19 + .../kb_materializer_api/README.md | 25 + .../kb_materializer_api/src/contracts.rs | 416 ++ .../kb_materializer_api/src/lib.rs | 35 + .../kb_materializer_api/src/materializer.rs | 22 + .../kb_materializer_bridge/Cargo.toml | 17 + .../kb_materializer_bridge/README.md | 14 + .../kb_materializer_bridge/src/lib.rs | 12 + .../src/materializer.rs | 35 + .../Cargo.toml | 20 + .../README.md | 19 + .../src/constants.rs | 7 + .../src/lib.rs | 16 + .../src/materializer.rs | 588 ++ .../kb_materializer_fees/Cargo.toml | 21 + .../kb_materializer_fees/README.md | 16 + .../kb_materializer_fees/src/lib.rs | 14 + .../kb_materializer_fees/src/materializer.rs | 441 ++ .../kb_materializer_governance/Cargo.toml | 17 + .../kb_materializer_governance/README.md | 14 + .../kb_materializer_governance/src/lib.rs | 12 + .../src/materializer.rs | 35 + .../kb_materializer_lending/Cargo.toml | 17 + .../kb_materializer_lending/README.md | 14 + .../kb_materializer_lending/src/lib.rs | 12 + .../src/materializer.rs | 35 + .../kb_materializer_lifecycle/Cargo.toml | 21 + .../kb_materializer_lifecycle/README.md | 36 + .../src/constants.rs | 7 + .../kb_materializer_lifecycle/src/lib.rs | 16 + .../src/materializer.rs | 993 ++++ .../kb_materializer_liquidity/Cargo.toml | 17 + .../kb_materializer_liquidity/README.md | 17 + .../kb_materializer_liquidity/src/lib.rs | 12 + .../src/materializer.rs | 35 + .../kb_materializer_metadata/Cargo.toml | 21 + .../kb_materializer_metadata/README.md | 45 + .../kb_materializer_metadata/src/lib.rs | 18 + .../src/materializer.rs | 585 ++ .../kb_materializer_nft/Cargo.toml | 17 + .../kb_materializer_nft/README.md | 14 + .../kb_materializer_nft/src/lib.rs | 12 + .../kb_materializer_nft/src/materializer.rs | 35 + .../kb_materializer_oracle/Cargo.toml | 17 + .../kb_materializer_oracle/README.md | 14 + .../kb_materializer_oracle/src/lib.rs | 12 + .../src/materializer.rs | 35 + .../kb_materializer_orderbook/Cargo.toml | 17 + .../kb_materializer_orderbook/README.md | 17 + .../kb_materializer_orderbook/src/lib.rs | 12 + .../src/materializer.rs | 35 + .../kb_materializer_perpetuals/Cargo.toml | 17 + .../kb_materializer_perpetuals/README.md | 14 + .../kb_materializer_perpetuals/src/lib.rs | 12 + .../src/materializer.rs | 35 + .../kb_materializer_pool_state/Cargo.toml | 17 + .../kb_materializer_pool_state/README.md | 17 + .../kb_materializer_pool_state/src/lib.rs | 12 + .../src/materializer.rs | 35 + .../kb_materializer_rewards/Cargo.toml | 17 + .../kb_materializer_rewards/README.md | 17 + .../kb_materializer_rewards/src/lib.rs | 12 + .../src/materializer.rs | 35 + .../kb_materializer_risk/Cargo.toml | 21 + .../kb_materializer_risk/README.md | 18 + .../kb_materializer_risk/src/constants.rs | 11 + .../kb_materializer_risk/src/lib.rs | 20 + .../kb_materializer_risk/src/materializer.rs | 646 ++ .../kb_materializer_routing/Cargo.toml | 17 + .../kb_materializer_routing/README.md | 14 + .../kb_materializer_routing/src/lib.rs | 12 + .../src/materializer.rs | 35 + .../kb_materializer_staking/Cargo.toml | 20 + .../kb_materializer_staking/README.md | 56 + .../kb_materializer_staking/src/constants.rs | 7 + .../kb_materializer_staking/src/lib.rs | 16 + .../src/materializer.rs | 886 +++ .../kb_materializer_token_accounts/Cargo.toml | 22 + .../kb_materializer_token_accounts/README.md | 29 + .../src/constants.rs | 11 + .../kb_materializer_token_accounts/src/lib.rs | 22 + .../src/materializer.rs | 835 +++ .../Cargo.toml | 17 + .../README.md | 14 + .../src/lib.rs | 12 + .../src/materializer.rs | 35 + .../kb_materializer_trades/Cargo.toml | 17 + .../kb_materializer_trades/README.md | 17 + .../kb_materializer_trades/src/lib.rs | 12 + .../src/materializer.rs | 35 + .../Cargo.toml | 21 + .../README.md | 47 + .../src/constants.rs | 13 + .../src/lib.rs | 22 + .../src/materializer.rs | 618 ++ .../kb_materializer_vault/Cargo.toml | 17 + .../kb_materializer_vault/README.md | 14 + .../kb_materializer_vault/src/lib.rs | 12 + .../kb_materializer_vault/src/materializer.rs | 35 + .../kb_model/Cargo.toml | 21 + .../kb_model/README.md | 32 + .../kb_model/src/canonical_transaction.rs | 819 +++ .../kb_model/src/decoded.rs | 87 + .../kb_model/src/lib.rs | 77 + .../kb_model/src/materialized.rs | 80 + .../kb_model/src/nomenclature.rs | 99 + .../kb_model/src/observation.rs | 31 + .../kb_model/src/solana.rs | 43 + .../kb_pipeline/Cargo.toml | 60 + .../kb_pipeline/README.md | 214 + .../kb_pipeline/src/backfill.rs | 2017 +++++++ .../kb_pipeline/src/constants.rs | 7 + .../kb_pipeline/src/core_extraction.rs | 1844 ++++++ .../kb_pipeline/src/decode_replay.rs | 2293 ++++++++ .../kb_pipeline/src/lib.rs | 323 + .../kb_pipeline/src/plan.rs | 44 + .../kb_pipeline/src/solana_ata_execution.rs | 1092 ++++ .../kb_pipeline/src/solana_ata_stateful.rs | 1304 +++++ .../src/solana_elgamal_registry_stateful.rs | 406 ++ .../kb_pipeline/src/solana_execution.rs | 1448 +++++ .../kb_pipeline/src/solana_memo_execution.rs | 918 +++ .../kb_pipeline/src/solana_stateful.rs | 1785 ++++++ .../src/solana_token_2022_correlation.rs | 242 + .../src/solana_token_2022_crypto_preflight.rs | 337 ++ .../src/solana_token_2022_devnet_execution.rs | 968 +++ .../src/solana_token_2022_devnet_scenarios.rs | 180 + ...lana_token_2022_execution_orchestration.rs | 331 ++ .../src/solana_token_2022_preflight.rs | 445 ++ .../solana_token_2022_proof_orchestration.rs | 271 + .../src/solana_token_2022_stateful.rs | 803 +++ .../src/solana_token_2022_validation.rs | 440 ++ .../kb_pipeline/src/solana_token_execution.rs | 1406 +++++ .../kb_pipeline/src/solana_token_lifecycle.rs | 1801 ++++++ .../kb_pipeline/src/solana_token_stateful.rs | 1910 ++++++ .../kb_program_ids/Cargo.toml | 14 + .../kb_program_ids/README.md | 30 + .../kb_program_ids/src/lib.rs | 1584 +++++ .../kb_rpc/Cargo.toml | 27 + .../kb_rpc/README.md | 209 + .../kb_rpc/src/client.rs | 22 + .../kb_rpc/src/constants.rs | 41 + .../kb_rpc/src/endpoint_role.rs | 190 + .../kb_rpc/src/execution_rpc.rs | 2753 +++++++++ .../kb_rpc/src/get_signatures_for_address.rs | 236 + .../kb_rpc/src/get_transaction.rs | 736 +++ .../kb_rpc/src/http_client.rs | 383 ++ .../kb_rpc/src/http_pool.rs | 279 + .../kb_rpc/src/json_rpc.rs | 315 + .../kb_rpc/src/lib.rs | 462 ++ .../kb_rpc/src/standard_http.rs | 499 ++ .../kb_rpc/src/standard_http_accounts.rs | 215 + .../kb_rpc/src/standard_http_blocks.rs | 548 ++ .../kb_rpc/src/standard_http_cluster.rs | 505 ++ .../kb_rpc/src/standard_http_economics.rs | 309 + .../kb_rpc/src/standard_http_tokens.rs | 241 + .../kb_rpc/src/standard_http_transactions.rs | 155 + .../kb_rpc/src/standard_methods.rs | 670 +++ .../kb_rpc/src/standard_ws.rs | 1274 ++++ .../kb_rpc/src/validation.rs | 77 + .../kb_rpc/src/ws_client.rs | 386 ++ .../kb_rpc/src/ws_pool.rs | 266 + .../kb_rpc/src/ws_session.rs | 1699 ++++++ .../get_transaction_equivalent_a.json | 39 + .../get_transaction_equivalent_b.json | 30 + .../get_transaction_legacy_failure.json | 37 + .../get_transaction_legacy_success.json | 80 + .../get_transaction_optional_absent.json | 28 + ...get_transaction_v0_alt_cpi_token_2022.json | 88 + .../kb_store_core/Cargo.toml | 21 + .../kb_store_core/README.md | 222 + .../kb_store_core/src/db_error.rs | 27 + .../kb_store_core/src/dtos.rs | 103 + .../kb_store_core/src/dtos/README.md | 25 + .../kb_store_core/src/dtos/core_dtos.rs | 1023 ++++ .../src/dtos/core_extraction_dtos.rs | 289 + .../kb_store_core/src/dtos/decode_dtos.rs | 684 +++ .../kb_store_core/src/dtos/event_dtos.rs | 143 + .../kb_store_core/src/dtos/ledger_dtos.rs | 73 + .../kb_store_core/src/dtos/raw_dtos.rs | 606 ++ .../kb_store_core/src/dtos/store_dtos.rs | 125 + .../kb_store_core/src/entities.rs | 32 + .../kb_store_core/src/entities/README.md | 26 + .../src/entities/core_entities.rs | 166 + .../src/entities/event_entities.rs | 46 + .../src/entities/ledger_entities.rs | 37 + .../src/entities/raw_entities.rs | 78 + .../kb_store_core/src/health.rs | 58 + .../kb_store_core/src/lib.rs | 159 + .../kb_store_core/src/pagination.rs | 75 + .../kb_store_core/src/repositories.rs | 25 + .../kb_store_core/src/repositories/README.md | 29 + .../src/repositories/storage_repositories.rs | 237 + .../kb_store_pg/Cargo.toml | 25 + .../kb_store_pg/README.md | 246 + .../maintenance/drop_raw_core_store.sql | 21 + .../0001_canonical_transaction_store.sql | 94 + .../migrations/0002_core_store.sql | 196 + .../migrations/0003_processing_ledger.sql | 41 + .../0004_decode_materialization_store.sql | 132 + .../kb_store_pg/src/constants.rs | 7 + .../kb_store_pg/src/lib.rs | 118 + .../kb_store_pg/src/migrations.rs | 1158 ++++ .../kb_store_pg/src/pg_store.rs | 704 +++ .../kb_store_pg/src/queries.rs | 52 + .../kb_store_pg/src/queries/README.md | 18 + .../src/queries/core_extraction_queries.rs | 1017 ++++ .../kb_store_pg/src/queries/core_queries.rs | 1234 ++++ .../src/queries/decode_pipeline_queries.rs | 1323 +++++ .../kb_store_pg/src/queries/health_queries.rs | 77 + .../kb_store_pg/src/queries/raw_queries.rs | 503 ++ .../src/queries/replay_candidate_queries.rs | 474 ++ .../src/queries/table_diagnostics_queries.rs | 194 + .../kb_store_pg/src/replay_candidates.rs | 380 ++ .../kb_store_pg/src/repositories.rs | 10 + .../kb_store_pg/src/repositories/README.md | 17 + .../core_extraction_repository.rs | 53 + .../core_transaction_repository.rs | 118 + .../decode_pipeline_repository.rs | 107 + .../raw_transaction_repository.rs | 63 + .../repositories/store_health_repository.rs | 31 + .../kb_store_pg/src/test_serial.rs | 15 + .../kb_store_sqlite/Cargo.toml | 17 + .../kb_store_sqlite/README.md | 17 + .../kb_store_sqlite/src/lib.rs | 12 + .../kb_store_sqlite/src/store.rs | 18 + .../kb_wallet/Cargo.toml | 24 + .../kb_wallet/README.md | 25 + .../kb_wallet/src/constants.rs | 9 + .../kb_wallet/src/lib.rs | 26 + .../kb_wallet/src/wallet.rs | 545 ++ .../kb_worker/Cargo.toml | 18 + .../kb_worker/README.md | 17 + .../kb_worker/src/main.rs | 12 + .../prompts/.gitignore | 3 + .../prompts/001_version_session_template.md | 60 + .../002_program_registry_and_naming_audit.md | 23 + .../003_v0_1_logging_config_profiles.md | 39 + .../004_v0_2_sql_storage_materialization.md | 242 + .../005_v0_2_2_pg_connection_migrations.md | 58 + .../prompts/005_v0_3_rpc_ingestion_ws.md | 40 + .../prompts/006_v0_4_wallet_execution_demo.md | 25 + .../007_v0_5_core_decoders_listeners.md | 23 + .../008_v0_6_priority_dex_router_surfaces.md | 48 + .../009_v0_2_1_store_core_contracts.md | 119 + ...2_postgres_connection_health_migrations.md | 52 + .../prompts/011_v0_2_3_raw_store_minimal.md | 167 + .../prompts/012_v0_2_4_core_store_minimal.md | 92 + .../013_v0_2_5_db_diagnostics_tauri.md | 50 + .../014_v0_3_0_agave_local_research.md | 210 + .../015_v0_3_1_canonical_store_migration.md | 48 + ...6_v0_3_2_canonical_transaction_contract.md | 112 + .../017_v0_3_3_http_backfill_free_tier.md | 186 + ...018_v0_3_4_canonical_to_core_extraction.md | 335 ++ .../019_v0_4_0_decoder_infrastructure.md | 288 + .../020_v0_4_1_native_solana_programs.md | 691 +++ .../021_v0_4_2_executor_solana_core.md | 281 + .../prompts/022_v0_4_3_spl_memo.md | 401 ++ .../prompts/023_v0_4_4_spl_token.md | 447 ++ ...024_v0_4_5_spl_associated_token_account.md | 244 + .../prompts/025_v0_4_6_spl_token_2022.md | 338 ++ .../026_v0_4_7_metaplex_token_metadata.md | 155 + .../prompts/README.md | 51 + .../registry/.gitignore | 3 + .../registry/core_program_id_seed.toml | 366 ++ .../registry/program_registry_seed.toml | 1919 ++++++ .../khadhroony-bot2-reference/rustfmt.toml | 23 + .../audit_khadhroony_workspace_rules.py | 116 + .../scripts/audit_rust_export_completeness.py | 174 + .../scripts/audit_rust_general_rules.py | 100 + .../scripts/audit_rust_workspace_rules.py | 33 + .../devnet/load_elgamal_registry_fixture.sh | 39 + .../scripts/devnet/load_spl_fixture.sh | 24 + .../prepare_elgamal_registry_fixture.sh | 120 + .../scripts/devnet/prepare_spl_fixture.sh | 105 + .../scripts/run_agave_local_research.sh | 30 + .../khadhroony-bot2-reference/sql/README.md | 8 + .../sql/schema/.gitignore | 2 + .../sql/validation/.gitignore | 2 + .../sql/validation/000_core_integrity.sql | 107 + migration/module-map.json | 1626 +++++ rustfmt.toml | 23 + 2159 files changed, 230833 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 .settings/org.eclipse.core.resources.prefs create mode 100644 .vscode/extensions.json create mode 100644 CHANGELOG.md create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 ROADMAP.md create mode 100644 RULES.md create mode 100644 RUST_RULES.md create mode 100644 clippy.toml create mode 100644 config/README.md create mode 100644 config/example.config.json create mode 100644 config/schema.config.json create mode 100644 docs/ACCOUNT_ONLY_CANDIDATES.md create mode 100644 docs/AGAVE_LOCAL_NODE.md create mode 100644 docs/APPLICATION_CRATES.md create mode 100644 docs/ARCHITECTURE.md create mode 100644 docs/BACKFILL_HTTP.md create mode 100644 docs/BAGS_FM.md create mode 100644 docs/CODE_REUSE.md create mode 100644 docs/CONFIGURATION.md create mode 100644 docs/CORE_EXTRACTION_CONTRACTS.md create mode 100644 docs/CORE_EXTRACTION_SELECTION_AND_REPLAY.md create mode 100644 docs/CORE_PROGRAM_IDS.md create mode 100644 docs/CORE_STORE.md create mode 100644 docs/DATABASE.md create mode 100644 docs/DECODER_MATERIALIZATION_CONTRACTS.md create mode 100644 docs/DECODER_SURFACE_AUDIT.md create mode 100644 docs/DELTA_WORKFLOW.md create mode 100644 docs/DEVELOPMENT_ORDER.md create mode 100644 docs/DEVNET_EXECUTION.md create mode 100644 docs/EXECUTION_MODEL.md create mode 100644 docs/EXECUTOR_SURFACE_MATRIX.md create mode 100644 docs/FOUNDATION_CLOSURE.md create mode 100644 docs/HISTORICAL_DATA_ACQUISITION_PLAN.md create mode 100644 docs/IDL_SURFACE_CLASSIFICATION.md create mode 100644 docs/INSTRUCTION_REPLAY_CONTRACTS.md create mode 100644 docs/LIVE_SOURCE_STRATEGY.md create mode 100644 docs/LOGGING.md create mode 100644 docs/MATERIALIZATIONS.md create mode 100644 docs/METAPLEX_TOKEN_METADATA_MATRIX.json create mode 100644 docs/NATIVE_SOLANA_CLOSURE_AUDIT.md create mode 100644 docs/NATIVE_SOLANA_DECODER_MATRIX.json create mode 100644 docs/NATIVE_SOLANA_EXECUTION_MATRIX.json create mode 100644 docs/NATIVE_SOLANA_MATERIALIZATION_AUDIT.md create mode 100644 docs/NATIVE_SOLANA_PROGRAMS.md create mode 100644 docs/NOMENCLATURE.md create mode 100644 docs/POSTGRES_STORE.md create mode 100644 docs/PROGRAM_CODE_INDEX.md create mode 100644 docs/PROGRAM_IDS.md create mode 100644 docs/PROGRAM_ID_CONSTANTS_AUDIT.md create mode 100644 docs/PROGRAM_NAMING.md create mode 100644 docs/PROGRAM_REGISTRY_CONTROL.md create mode 100644 docs/PROJECT_OBJECTIVES.md create mode 100644 docs/RAW_STORAGE_LIFECYCLE.md create mode 100644 docs/RAW_STORE.md create mode 100644 docs/REPLAY_PIPELINE.md create mode 100644 docs/RPC_ENDPOINT_ROLES.md create mode 100644 docs/RUST_WORKSPACE_RULE_AUDIT.md create mode 100644 docs/SOLANA_INTERFACE_DEPENDENCIES.md create mode 100644 docs/SOLANA_NATIVE_RPC_CLOSURE_AUDIT.md create mode 100644 docs/SOLANA_STANDARD_RPC_MATRIX.json create mode 100644 docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json create mode 100644 docs/SPL_ELGAMAL_REGISTRY_MATRIX.json create mode 100644 docs/SPL_MEMO_MATRIX.json create mode 100644 docs/SPL_TOKEN_2022_CONFIDENTIAL_EXECUTION_AUDIT.md create mode 100644 docs/SPL_TOKEN_2022_MATRIX.json create mode 100644 docs/SPL_TOKEN_2022_SOURCE_AUDIT.md create mode 100644 docs/SPL_TOKEN_2022_VALIDATION_MATRIX.json create mode 100644 docs/SPL_TOKEN_MATRIX.json create mode 100644 docs/SQL_DEMOS.md create mode 100644 docs/SURFACE_CRATE_MATRIX.md create mode 100644 docs/TRACING_CONTRACT.md create mode 100644 docs/TRANSACTION_ACQUISITION_MODEL.md create mode 100644 docs/V0_4_6_VALIDATION.md create mode 100644 docs/VERSION_HEADER_AUDIT.md create mode 100644 docs/WS_LISTENERS.md create mode 100644 docs/legacy-khadhroony-bot2/CHANGELOG.md create mode 100644 docs/legacy-khadhroony-bot2/README.md create mode 100644 docs/legacy-khadhroony-bot2/ROADMAP.md create mode 100644 docs/legacy-khadhroony-bot2/RULES.md create mode 100644 docs/legacy-khadhroony-bot2/RUST_RULES.md create mode 100644 idls/001.README.md create mode 100644 idls/bonkswap.BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p.json create mode 100644 idls/boop_fun.boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4.json create mode 100644 idls/bubblegum.BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY.json create mode 100644 idls/byreal_clmm.REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2.json create mode 100644 idls/carrot.CarrotwivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s.json create mode 100644 idls/cctp_v1.CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3.json create mode 100644 idls/cctp_v2.CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe.json create mode 100644 idls/clone.C1onEW2kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr.json create mode 100644 idls/dflow_v4.DF1ow4tspfHX9JwWJsAb9epbkA8hmpSEAtxXy1V27QBH.json create mode 100644 idls/drift_v2.dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH.json create mode 100644 idls/fusion_amm.fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9.json create mode 100644 idls/goosefx_gamma.GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT.json create mode 100644 idls/goosefx_v2.GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn.json create mode 100644 idls/guacswap.Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1.json create mode 100644 idls/helium_treasury_management.treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5.json create mode 100644 idls/hylo_exchange.HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn.json create mode 100644 idls/hylo_stability_pool.HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ.json create mode 100644 idls/jito_tip_distribution.4R3gSG8BpU4t19KYj8CfnbtRpnT8gtk4dvTHxVRwc2r7..json create mode 100644 idls/jupiter_aprepro_smart_wallet.JSW99DKmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw.json create mode 100644 idls/jupiter_dca.DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M.json create mode 100644 idls/jupiter_limit_order.jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu.json create mode 100644 idls/jupiter_limit_order_v2.j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X.json create mode 100644 idls/jupiter_locker.LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn.json create mode 100644 idls/jupiter_perpetuals.PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu.json create mode 100644 idls/jupiter_stable.JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr.json create mode 100644 idls/jupiter_v4.JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB.json create mode 100644 idls/jupiter_v6.JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4.json create mode 100644 idls/kamino.6LtLpnUFNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc.json create mode 100644 idls/kamino_farms.FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr.json create mode 100644 idls/kamino_lending.KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD.json create mode 100644 idls/kamino_vault.kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr.json create mode 100644 idls/kamino_vault_v2.KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd.json create mode 100644 idls/layerzero_endpoint.76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6.json create mode 100644 idls/layerzero_executor.6doghB248px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn.json create mode 100644 idls/lifinity_amm_v2.2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c.json create mode 100644 idls/marginfi_v2.MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA.json create mode 100644 idls/marinade_finance.MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD.json create mode 100644 idls/metadao_amm_v0.5.AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ.json create mode 100644 idls/metadao_futarchy.FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq.json create mode 100644 idls/metadao_launchpad.moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM.json create mode 100644 idls/metdao_bid_wall.WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx.json create mode 100644 idls/meteora_damm_v2.cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG.json create mode 100644 idls/meteora_dbc.dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN.json create mode 100644 idls/meteora_dlmm.LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo.json create mode 100644 idls/meteora_pools_amm.Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB.json create mode 100644 idls/meteora_vault.24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi.json create mode 100644 idls/moonit.MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG.json create mode 100644 idls/numeraire.NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P.json create mode 100644 idls/okx_dex_router.proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u.json create mode 100644 idls/okx_lab_v1.6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma.json create mode 100644 idls/ondo_gm.XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm.json create mode 100644 idls/openbook_v2.opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb.json create mode 100644 idls/orca_wavebreak.waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF.json create mode 100644 idls/orca_whirlpool.whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.json create mode 100644 idls/pancakeswap.HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq.json create mode 100644 idls/phoenix_eternal.EtrnLzgbS7nMMy5fbD42kXiUzGg8XQzJ972Xtk1cjWih.json create mode 100644 idls/printr.T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint.json create mode 100644 idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json create mode 100644 idls/pump_fun.6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P.json create mode 100644 idls/pump_swap.pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA.json create mode 100644 idls/pumpup.PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd.json create mode 100644 idls/raydium_clmm.CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK.json create mode 100644 idls/raydium_cpmm.CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C.json create mode 100644 idls/raydium_launchlab.LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj.json create mode 100644 idls/raydium_lock.LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE.json create mode 100644 idls/saber_decimal.DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB.json create mode 100644 idls/squads.SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf.json create mode 100644 idls/stabble_clmm.6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6.json create mode 100644 idls/stabble_stable_swap.swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ.json create mode 100644 idls/stabble_weighted_swap.swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW.json create mode 100644 idls/streamflow.strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m.json create mode 100644 idls/tensor-cnft.TCMPhJdwDryooaGtiocG1u3xcYbRpiJzb283XfCZsDp.json create mode 100644 idls/vertigo.vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ.json create mode 100644 idls/virtuals.5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki.json create mode 100644 idls/woofi.WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb.json create mode 100644 idls/zeta.ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD.json create mode 100644 kb-app-demo/Cargo.toml create mode 100644 kb-app-demo/src/main.rs create mode 100644 kb-config/Cargo.toml create mode 100644 kb-config/src/lib.rs create mode 100644 kb-core/Cargo.toml create mode 100644 kb-core/README.md create mode 100644 kb-core/src/error.rs create mode 100644 kb-core/src/lib.rs create mode 100644 kb-core/src/module.rs create mode 100644 kb-lib/Cargo.toml create mode 100644 kb-lib/src/decoder.rs create mode 100644 kb-lib/src/decoder/adapter.rs create mode 100644 kb-lib/src/decoder/adapter/saber_decimal_wrapper.rs create mode 100644 kb-lib/src/decoder/admin.rs create mode 100644 kb-lib/src/decoder/admin/jupiter_lock.rs create mode 100644 kb-lib/src/decoder/admin/pump_fees.rs create mode 100644 kb-lib/src/decoder/amm.rs create mode 100644 kb-lib/src/decoder/amm/aldrin_v1.rs create mode 100644 kb-lib/src/decoder/amm/aldrin_v2.rs create mode 100644 kb-lib/src/decoder/amm/alphaq.rs create mode 100644 kb-lib/src/decoder/amm/believe.rs create mode 100644 kb-lib/src/decoder/amm/bonk_swap.rs create mode 100644 kb-lib/src/decoder/amm/fluxbeam.rs create mode 100644 kb-lib/src/decoder/amm/goon_fi.rs create mode 100644 kb-lib/src/decoder/amm/goosefx_gamma.rs create mode 100644 kb-lib/src/decoder/amm/goosefx_v2.rs create mode 100644 kb-lib/src/decoder/amm/guac_swap.rs create mode 100644 kb-lib/src/decoder/amm/lifinity_swap_v2.rs create mode 100644 kb-lib/src/decoder/amm/metadao_futarchy_amm.rs create mode 100644 kb-lib/src/decoder/amm/metadao_v0_5.rs create mode 100644 kb-lib/src/decoder/amm/meteora_damm_v1.rs create mode 100644 kb-lib/src/decoder/amm/meteora_damm_v2.rs create mode 100644 kb-lib/src/decoder/amm/obric_v2.rs create mode 100644 kb-lib/src/decoder/amm/one_dex.rs create mode 100644 kb-lib/src/decoder/amm/pump_swap.rs create mode 100644 kb-lib/src/decoder/amm/raydium_lp_v4.rs create mode 100644 kb-lib/src/decoder/amm/solfi.rs create mode 100644 kb-lib/src/decoder/amm/solfi_v2.rs create mode 100644 kb-lib/src/decoder/amm/vertigo.rs create mode 100644 kb-lib/src/decoder/amm/virtuals.rs create mode 100644 kb-lib/src/decoder/amm/woofi.rs create mode 100644 kb-lib/src/decoder/amm/zero_fi.rs create mode 100644 kb-lib/src/decoder/amm/zora.rs create mode 100644 kb-lib/src/decoder/anchor.rs create mode 100644 kb-lib/src/decoder/anchor/core.rs create mode 100644 kb-lib/src/decoder/api.rs create mode 100644 kb-lib/src/decoder/api/constants.rs create mode 100644 kb-lib/src/decoder/api/contracts.rs create mode 100644 kb-lib/src/decoder/api/decoder.rs create mode 100644 kb-lib/src/decoder/bridge.rs create mode 100644 kb-lib/src/decoder/bridge/circle_cctp_token_messenger_minter.rs create mode 100644 kb-lib/src/decoder/bridge/circle_cctp_token_messenger_minter_v2.rs create mode 100644 kb-lib/src/decoder/bridge/layer_zero_endpoint.rs create mode 100644 kb-lib/src/decoder/bridge/layer_zero_executor.rs create mode 100644 kb-lib/src/decoder/clmm.rs create mode 100644 kb-lib/src/decoder/clmm/byreal.rs create mode 100644 kb-lib/src/decoder/clmm/fusion.rs create mode 100644 kb-lib/src/decoder/clmm/orca_whirlpool.rs create mode 100644 kb-lib/src/decoder/clmm/pancake_swap.rs create mode 100644 kb-lib/src/decoder/clmm/raydium.rs create mode 100644 kb-lib/src/decoder/clmm/stabble.rs create mode 100644 kb-lib/src/decoder/cpmm.rs create mode 100644 kb-lib/src/decoder/cpmm/raydium.rs create mode 100644 kb-lib/src/decoder/dlmm.rs create mode 100644 kb-lib/src/decoder/dlmm/meteora.rs create mode 100644 kb-lib/src/decoder/fees.rs create mode 100644 kb-lib/src/decoder/fees/bags_fee_share_v1.rs create mode 100644 kb-lib/src/decoder/fees/bags_fee_share_v2.rs create mode 100644 kb-lib/src/decoder/governance.rs create mode 100644 kb-lib/src/decoder/governance/metadao_bid_wall.rs create mode 100644 kb-lib/src/decoder/launchpad.rs create mode 100644 kb-lib/src/decoder/launchpad/boop_fun.rs create mode 100644 kb-lib/src/decoder/launchpad/metadao_ico.rs create mode 100644 kb-lib/src/decoder/launchpad/meteora_dbc.rs create mode 100644 kb-lib/src/decoder/launchpad/moonit.rs create mode 100644 kb-lib/src/decoder/launchpad/orca_wavebreak.rs create mode 100644 kb-lib/src/decoder/launchpad/printr.rs create mode 100644 kb-lib/src/decoder/launchpad/pump_fun.rs create mode 100644 kb-lib/src/decoder/launchpad/pump_pumpup_ai.rs create mode 100644 kb-lib/src/decoder/launchpad/raydium_launchlab.rs create mode 100644 kb-lib/src/decoder/lending.rs create mode 100644 kb-lib/src/decoder/lending/clone.rs create mode 100644 kb-lib/src/decoder/lending/jupiter_lend_borrow.rs create mode 100644 kb-lib/src/decoder/lending/jupiter_lend_earn.rs create mode 100644 kb-lib/src/decoder/lending/jupiter_lend_flash_loan.rs create mode 100644 kb-lib/src/decoder/lending/jupiter_lend_liquidity.rs create mode 100644 kb-lib/src/decoder/lending/kamino.rs create mode 100644 kb-lib/src/decoder/lending/marginfi_v2.rs create mode 100644 kb-lib/src/decoder/lock.rs create mode 100644 kb-lib/src/decoder/lock/raydium_lp.rs create mode 100644 kb-lib/src/decoder/metadata.rs create mode 100644 kb-lib/src/decoder/metadata/metaplex_token_metadata.rs create mode 100644 kb-lib/src/decoder/metadata/spl_name_service.rs create mode 100644 kb-lib/src/decoder/nft.rs create mode 100644 kb-lib/src/decoder/nft/metaplex_bubblegum.rs create mode 100644 kb-lib/src/decoder/orderbook.rs create mode 100644 kb-lib/src/decoder/orderbook/jupiter_limit_order.rs create mode 100644 kb-lib/src/decoder/orderbook/jupiter_limit_order_v2.rs create mode 100644 kb-lib/src/decoder/orderbook/openbook_v2.rs create mode 100644 kb-lib/src/decoder/perpetuals.rs create mode 100644 kb-lib/src/decoder/perpetuals/drift_v2.rs create mode 100644 kb-lib/src/decoder/perpetuals/jupiter.rs create mode 100644 kb-lib/src/decoder/perpetuals/zeta.rs create mode 100644 kb-lib/src/decoder/router.rs create mode 100644 kb-lib/src/decoder/router/dflow_aggregator_v4.rs create mode 100644 kb-lib/src/decoder/router/jupiter_aggregator_v4.rs create mode 100644 kb-lib/src/decoder/router/jupiter_aggregator_v6.rs create mode 100644 kb-lib/src/decoder/router/jupiter_dca.rs create mode 100644 kb-lib/src/decoder/router/okx_labs_v1.rs create mode 100644 kb-lib/src/decoder/router/okx_labs_v2.rs create mode 100644 kb-lib/src/decoder/rwa.rs create mode 100644 kb-lib/src/decoder/rwa/ondo_global_markets.rs create mode 100644 kb-lib/src/decoder/solana.rs create mode 100644 kb-lib/src/decoder/solana/core.rs create mode 100644 kb-lib/src/decoder/spl.rs create mode 100644 kb-lib/src/decoder/spl/account_compression.rs create mode 100644 kb-lib/src/decoder/spl/associated_token_account.rs create mode 100644 kb-lib/src/decoder/spl/elgamal_registry.rs create mode 100644 kb-lib/src/decoder/spl/memo.rs create mode 100644 kb-lib/src/decoder/spl/noop.rs create mode 100644 kb-lib/src/decoder/spl/single_pool.rs create mode 100644 kb-lib/src/decoder/spl/stake_pool.rs create mode 100644 kb-lib/src/decoder/spl/token.rs create mode 100644 kb-lib/src/decoder/spl/token_2022.rs create mode 100644 kb-lib/src/decoder/stable.rs create mode 100644 kb-lib/src/decoder/stable/swap_hylo_exchange.rs create mode 100644 kb-lib/src/decoder/stable/swap_jupiter_stable.rs create mode 100644 kb-lib/src/decoder/stable/swap_numeraire.rs create mode 100644 kb-lib/src/decoder/stable/swap_stabble.rs create mode 100644 kb-lib/src/decoder/staking.rs create mode 100644 kb-lib/src/decoder/staking/kamino_farm.rs create mode 100644 kb-lib/src/decoder/staking/marinade_finance.rs create mode 100644 kb-lib/src/decoder/staking/solayer.rs create mode 100644 kb-lib/src/decoder/treasury.rs create mode 100644 kb-lib/src/decoder/treasury/helium_treasury_management.rs create mode 100644 kb-lib/src/decoder/vault.rs create mode 100644 kb-lib/src/decoder/vault/carrot_defi.rs create mode 100644 kb-lib/src/decoder/vault/hylo_stability_pool.rs create mode 100644 kb-lib/src/decoder/vault/kamino.rs create mode 100644 kb-lib/src/decoder/vault/kamino_v2.rs create mode 100644 kb-lib/src/decoder/vault/kamino_yvaults.rs create mode 100644 kb-lib/src/decoder/vault/meteora.rs create mode 100644 kb-lib/src/decoder/vesting.rs create mode 100644 kb-lib/src/decoder/vesting/streamflow.rs create mode 100644 kb-lib/src/decoder/wallet.rs create mode 100644 kb-lib/src/decoder/wallet/jupiter_apepro_smart_wallet.rs create mode 100644 kb-lib/src/decoder/weighted.rs create mode 100644 kb-lib/src/decoder/weighted/swap_stabble.rs create mode 100644 kb-lib/src/executor.rs create mode 100644 kb-lib/src/executor/adapter.rs create mode 100644 kb-lib/src/executor/adapter/saber_decimal_wrapper.rs create mode 100644 kb-lib/src/executor/admin.rs create mode 100644 kb-lib/src/executor/admin/jupiter_lock.rs create mode 100644 kb-lib/src/executor/admin/pump_fees.rs create mode 100644 kb-lib/src/executor/amm.rs create mode 100644 kb-lib/src/executor/amm/aldrin_v1.rs create mode 100644 kb-lib/src/executor/amm/aldrin_v2.rs create mode 100644 kb-lib/src/executor/amm/alphaq.rs create mode 100644 kb-lib/src/executor/amm/believe.rs create mode 100644 kb-lib/src/executor/amm/bonk_swap.rs create mode 100644 kb-lib/src/executor/amm/fluxbeam.rs create mode 100644 kb-lib/src/executor/amm/goon_fi.rs create mode 100644 kb-lib/src/executor/amm/goosefx_gamma.rs create mode 100644 kb-lib/src/executor/amm/goosefx_v2.rs create mode 100644 kb-lib/src/executor/amm/guac_swap.rs create mode 100644 kb-lib/src/executor/amm/lifinity_swap_v2.rs create mode 100644 kb-lib/src/executor/amm/metadao_futarchy_amm.rs create mode 100644 kb-lib/src/executor/amm/metadao_v0_5.rs create mode 100644 kb-lib/src/executor/amm/meteora_damm_v1.rs create mode 100644 kb-lib/src/executor/amm/meteora_damm_v2.rs create mode 100644 kb-lib/src/executor/amm/obric_v2.rs create mode 100644 kb-lib/src/executor/amm/one_dex.rs create mode 100644 kb-lib/src/executor/amm/pump_swap.rs create mode 100644 kb-lib/src/executor/amm/raydium_lp_v4.rs create mode 100644 kb-lib/src/executor/amm/solfi.rs create mode 100644 kb-lib/src/executor/amm/solfi_v2.rs create mode 100644 kb-lib/src/executor/amm/vertigo.rs create mode 100644 kb-lib/src/executor/amm/virtuals.rs create mode 100644 kb-lib/src/executor/amm/woofi.rs create mode 100644 kb-lib/src/executor/amm/zero_fi.rs create mode 100644 kb-lib/src/executor/amm/zora.rs create mode 100644 kb-lib/src/executor/api.rs create mode 100644 kb-lib/src/executor/api/execution.rs create mode 100644 kb-lib/src/executor/api/executor.rs create mode 100644 kb-lib/src/executor/bridge.rs create mode 100644 kb-lib/src/executor/bridge/circle_cctp_token_messenger_minter.rs create mode 100644 kb-lib/src/executor/bridge/circle_cctp_token_messenger_minter_v2.rs create mode 100644 kb-lib/src/executor/bridge/layer_zero_endpoint.rs create mode 100644 kb-lib/src/executor/bridge/layer_zero_executor.rs create mode 100644 kb-lib/src/executor/clmm.rs create mode 100644 kb-lib/src/executor/clmm/byreal.rs create mode 100644 kb-lib/src/executor/clmm/fusion.rs create mode 100644 kb-lib/src/executor/clmm/orca_whirlpool.rs create mode 100644 kb-lib/src/executor/clmm/pancake_swap.rs create mode 100644 kb-lib/src/executor/clmm/raydium.rs create mode 100644 kb-lib/src/executor/clmm/stabble.rs create mode 100644 kb-lib/src/executor/cpmm.rs create mode 100644 kb-lib/src/executor/cpmm/raydium.rs create mode 100644 kb-lib/src/executor/dlmm.rs create mode 100644 kb-lib/src/executor/dlmm/meteora.rs create mode 100644 kb-lib/src/executor/fees.rs create mode 100644 kb-lib/src/executor/fees/bags_fee_share_v1.rs create mode 100644 kb-lib/src/executor/fees/bags_fee_share_v2.rs create mode 100644 kb-lib/src/executor/governance.rs create mode 100644 kb-lib/src/executor/governance/metadao_bid_wall.rs create mode 100644 kb-lib/src/executor/launchpad.rs create mode 100644 kb-lib/src/executor/launchpad/boop_fun.rs create mode 100644 kb-lib/src/executor/launchpad/metadao_ico.rs create mode 100644 kb-lib/src/executor/launchpad/meteora_dbc.rs create mode 100644 kb-lib/src/executor/launchpad/moonit.rs create mode 100644 kb-lib/src/executor/launchpad/orca_wavebreak.rs create mode 100644 kb-lib/src/executor/launchpad/printr.rs create mode 100644 kb-lib/src/executor/launchpad/pump_fun.rs create mode 100644 kb-lib/src/executor/launchpad/pump_pumpup_ai.rs create mode 100644 kb-lib/src/executor/launchpad/raydium_launchlab.rs create mode 100644 kb-lib/src/executor/lending.rs create mode 100644 kb-lib/src/executor/lending/clone.rs create mode 100644 kb-lib/src/executor/lending/jupiter_lend_borrow.rs create mode 100644 kb-lib/src/executor/lending/jupiter_lend_earn.rs create mode 100644 kb-lib/src/executor/lending/jupiter_lend_flash_loan.rs create mode 100644 kb-lib/src/executor/lending/jupiter_lend_liquidity.rs create mode 100644 kb-lib/src/executor/lending/kamino.rs create mode 100644 kb-lib/src/executor/lending/marginfi_v2.rs create mode 100644 kb-lib/src/executor/lock.rs create mode 100644 kb-lib/src/executor/lock/raydium_lp.rs create mode 100644 kb-lib/src/executor/metadata.rs create mode 100644 kb-lib/src/executor/metadata/metaplex_token_metadata.rs create mode 100644 kb-lib/src/executor/metadata/spl_name_service.rs create mode 100644 kb-lib/src/executor/nft.rs create mode 100644 kb-lib/src/executor/nft/metaplex_bubblegum.rs create mode 100644 kb-lib/src/executor/orderbook.rs create mode 100644 kb-lib/src/executor/orderbook/jupiter_limit_order.rs create mode 100644 kb-lib/src/executor/orderbook/jupiter_limit_order_v2.rs create mode 100644 kb-lib/src/executor/orderbook/openbook_v2.rs create mode 100644 kb-lib/src/executor/perpetuals.rs create mode 100644 kb-lib/src/executor/perpetuals/drift_v2.rs create mode 100644 kb-lib/src/executor/perpetuals/jupiter.rs create mode 100644 kb-lib/src/executor/perpetuals/zeta.rs create mode 100644 kb-lib/src/executor/router.rs create mode 100644 kb-lib/src/executor/router/dflow_aggregator_v4.rs create mode 100644 kb-lib/src/executor/router/jupiter_aggregator_v4.rs create mode 100644 kb-lib/src/executor/router/jupiter_aggregator_v6.rs create mode 100644 kb-lib/src/executor/router/jupiter_dca.rs create mode 100644 kb-lib/src/executor/router/okx_labs_v1.rs create mode 100644 kb-lib/src/executor/router/okx_labs_v2.rs create mode 100644 kb-lib/src/executor/rwa.rs create mode 100644 kb-lib/src/executor/rwa/ondo_global_markets.rs create mode 100644 kb-lib/src/executor/solana.rs create mode 100644 kb-lib/src/executor/solana/core.rs create mode 100644 kb-lib/src/executor/spl.rs create mode 100644 kb-lib/src/executor/spl/account_compression.rs create mode 100644 kb-lib/src/executor/spl/associated_token_account.rs create mode 100644 kb-lib/src/executor/spl/elgamal_registry.rs create mode 100644 kb-lib/src/executor/spl/memo.rs create mode 100644 kb-lib/src/executor/spl/noop.rs create mode 100644 kb-lib/src/executor/spl/single_pool.rs create mode 100644 kb-lib/src/executor/spl/stake_pool.rs create mode 100644 kb-lib/src/executor/spl/token.rs create mode 100644 kb-lib/src/executor/spl/token_2022.rs create mode 100644 kb-lib/src/executor/stable.rs create mode 100644 kb-lib/src/executor/stable/swap_hylo_exchange.rs create mode 100644 kb-lib/src/executor/stable/swap_jupiter_stable.rs create mode 100644 kb-lib/src/executor/stable/swap_numeraire.rs create mode 100644 kb-lib/src/executor/stable/swap_stabble.rs create mode 100644 kb-lib/src/executor/staking.rs create mode 100644 kb-lib/src/executor/staking/kamino_farm.rs create mode 100644 kb-lib/src/executor/staking/marinade_finance.rs create mode 100644 kb-lib/src/executor/staking/solayer.rs create mode 100644 kb-lib/src/executor/treasury.rs create mode 100644 kb-lib/src/executor/treasury/helium_treasury_management.rs create mode 100644 kb-lib/src/executor/vault.rs create mode 100644 kb-lib/src/executor/vault/carrot_defi.rs create mode 100644 kb-lib/src/executor/vault/hylo_stability_pool.rs create mode 100644 kb-lib/src/executor/vault/kamino.rs create mode 100644 kb-lib/src/executor/vault/kamino_v2.rs create mode 100644 kb-lib/src/executor/vault/kamino_yvaults.rs create mode 100644 kb-lib/src/executor/vault/meteora.rs create mode 100644 kb-lib/src/executor/vesting.rs create mode 100644 kb-lib/src/executor/vesting/streamflow.rs create mode 100644 kb-lib/src/executor/wallet.rs create mode 100644 kb-lib/src/executor/wallet/jupiter_apepro_smart_wallet.rs create mode 100644 kb-lib/src/executor/weighted.rs create mode 100644 kb-lib/src/executor/weighted/swap_stabble.rs create mode 100644 kb-lib/src/lib.rs create mode 100644 kb-lib/src/materializer.rs create mode 100644 kb-lib/src/materializer/admin.rs create mode 100644 kb-lib/src/materializer/admin/core.rs create mode 100644 kb-lib/src/materializer/api.rs create mode 100644 kb-lib/src/materializer/api/contracts.rs create mode 100644 kb-lib/src/materializer/api/materializer.rs create mode 100644 kb-lib/src/materializer/bridge.rs create mode 100644 kb-lib/src/materializer/bridge/core.rs create mode 100644 kb-lib/src/materializer/compliance.rs create mode 100644 kb-lib/src/materializer/compliance/audit.rs create mode 100644 kb-lib/src/materializer/fees.rs create mode 100644 kb-lib/src/materializer/fees/core.rs create mode 100644 kb-lib/src/materializer/governance.rs create mode 100644 kb-lib/src/materializer/governance/core.rs create mode 100644 kb-lib/src/materializer/lending.rs create mode 100644 kb-lib/src/materializer/lending/core.rs create mode 100644 kb-lib/src/materializer/lifecycle.rs create mode 100644 kb-lib/src/materializer/lifecycle/core.rs create mode 100644 kb-lib/src/materializer/liquidity.rs create mode 100644 kb-lib/src/materializer/liquidity/core.rs create mode 100644 kb-lib/src/materializer/metadata.rs create mode 100644 kb-lib/src/materializer/metadata/core.rs create mode 100644 kb-lib/src/materializer/nft.rs create mode 100644 kb-lib/src/materializer/nft/core.rs create mode 100644 kb-lib/src/materializer/oracle.rs create mode 100644 kb-lib/src/materializer/oracle/core.rs create mode 100644 kb-lib/src/materializer/orderbook.rs create mode 100644 kb-lib/src/materializer/orderbook/core.rs create mode 100644 kb-lib/src/materializer/perpetuals.rs create mode 100644 kb-lib/src/materializer/perpetuals/core.rs create mode 100644 kb-lib/src/materializer/pool.rs create mode 100644 kb-lib/src/materializer/pool/state.rs create mode 100644 kb-lib/src/materializer/rewards.rs create mode 100644 kb-lib/src/materializer/rewards/core.rs create mode 100644 kb-lib/src/materializer/risk.rs create mode 100644 kb-lib/src/materializer/risk/core.rs create mode 100644 kb-lib/src/materializer/routing.rs create mode 100644 kb-lib/src/materializer/routing/core.rs create mode 100644 kb-lib/src/materializer/staking.rs create mode 100644 kb-lib/src/materializer/staking/core.rs create mode 100644 kb-lib/src/materializer/token.rs create mode 100644 kb-lib/src/materializer/token/accounts.rs create mode 100644 kb-lib/src/materializer/token/metadata_risk.rs create mode 100644 kb-lib/src/materializer/trades.rs create mode 100644 kb-lib/src/materializer/trades/core.rs create mode 100644 kb-lib/src/materializer/transaction.rs create mode 100644 kb-lib/src/materializer/transaction/annotations.rs create mode 100644 kb-lib/src/materializer/vault.rs create mode 100644 kb-lib/src/materializer/vault/core.rs create mode 100644 kb-lib/src/model.rs create mode 100644 kb-lib/src/model/canonical_transaction.rs create mode 100644 kb-lib/src/model/decoded.rs create mode 100644 kb-lib/src/model/materialized.rs create mode 100644 kb-lib/src/model/nomenclature.rs create mode 100644 kb-lib/src/model/observation.rs create mode 100644 kb-lib/src/model/replay.rs create mode 100644 kb-lib/src/model/solana.rs create mode 100644 kb-logging/Cargo.toml create mode 100644 kb-logging/src/constants.rs create mode 100644 kb-logging/src/lib.rs create mode 100644 kb-pipeline/Cargo.toml create mode 100644 kb-pipeline/src/lib.rs create mode 100644 kb-program-ids/Cargo.toml create mode 100644 kb-program-ids/src/lib.rs create mode 100644 kb-rpc/Cargo.toml create mode 100644 kb-rpc/src/lib.rs create mode 100644 kb-store/Cargo.toml create mode 100644 kb-store/src/core.rs create mode 100644 kb-store/src/lib.rs create mode 100644 kb-store/src/postgres.rs create mode 100644 kb-wallet/Cargo.toml create mode 100644 kb-wallet/src/lib.rs create mode 100644 migration/README.md create mode 100644 migration/khadhroony-bot2-reference/CHANGELOG.md create mode 100644 migration/khadhroony-bot2-reference/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/README.md create mode 100644 migration/khadhroony-bot2-reference/ROADMAP.md create mode 100644 migration/khadhroony-bot2-reference/RULES.md create mode 100644 migration/khadhroony-bot2-reference/RUST_RULES.md create mode 100644 migration/khadhroony-bot2-reference/clippy.toml create mode 100644 migration/khadhroony-bot2-reference/config/README.md create mode 100644 migration/khadhroony-bot2-reference/config/example.config.json create mode 100644 migration/khadhroony-bot2-reference/config/schema.config.json create mode 100644 migration/khadhroony-bot2-reference/docs/ACCOUNT_ONLY_CANDIDATES.md create mode 100644 migration/khadhroony-bot2-reference/docs/AGAVE_LOCAL_NODE.md create mode 100644 migration/khadhroony-bot2-reference/docs/APPLICATION_CRATES.md create mode 100644 migration/khadhroony-bot2-reference/docs/ARCHITECTURE.md create mode 100644 migration/khadhroony-bot2-reference/docs/BACKFILL_HTTP.md create mode 100644 migration/khadhroony-bot2-reference/docs/BAGS_FM.md create mode 100644 migration/khadhroony-bot2-reference/docs/CODE_REUSE.md create mode 100644 migration/khadhroony-bot2-reference/docs/CONFIGURATION.md create mode 100644 migration/khadhroony-bot2-reference/docs/CORE_EXTRACTION_CONTRACTS.md create mode 100644 migration/khadhroony-bot2-reference/docs/CORE_EXTRACTION_SELECTION_AND_REPLAY.md create mode 100644 migration/khadhroony-bot2-reference/docs/CORE_PROGRAM_IDS.md create mode 100644 migration/khadhroony-bot2-reference/docs/CORE_STORE.md create mode 100644 migration/khadhroony-bot2-reference/docs/DATABASE.md create mode 100644 migration/khadhroony-bot2-reference/docs/DECODER_MATERIALIZATION_CONTRACTS.md create mode 100644 migration/khadhroony-bot2-reference/docs/DECODER_SURFACE_AUDIT.md create mode 100644 migration/khadhroony-bot2-reference/docs/DELTA_WORKFLOW.md create mode 100644 migration/khadhroony-bot2-reference/docs/DEVELOPMENT_ORDER.md create mode 100644 migration/khadhroony-bot2-reference/docs/DEVNET_EXECUTION.md create mode 100644 migration/khadhroony-bot2-reference/docs/EXECUTION_MODEL.md create mode 100644 migration/khadhroony-bot2-reference/docs/EXECUTOR_SURFACE_MATRIX.md create mode 100644 migration/khadhroony-bot2-reference/docs/FOUNDATION_CLOSURE.md create mode 100644 migration/khadhroony-bot2-reference/docs/HISTORICAL_DATA_ACQUISITION_PLAN.md create mode 100644 migration/khadhroony-bot2-reference/docs/IDL_SURFACE_CLASSIFICATION.md create mode 100644 migration/khadhroony-bot2-reference/docs/INSTRUCTION_REPLAY_CONTRACTS.md create mode 100644 migration/khadhroony-bot2-reference/docs/LIVE_SOURCE_STRATEGY.md create mode 100644 migration/khadhroony-bot2-reference/docs/LOGGING.md create mode 100644 migration/khadhroony-bot2-reference/docs/MATERIALIZATIONS.md create mode 100644 migration/khadhroony-bot2-reference/docs/METAPLEX_TOKEN_METADATA_MATRIX.json create mode 100644 migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_CLOSURE_AUDIT.md create mode 100644 migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_DECODER_MATRIX.json create mode 100644 migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_EXECUTION_MATRIX.json create mode 100644 migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_MATERIALIZATION_AUDIT.md create mode 100644 migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_PROGRAMS.md create mode 100644 migration/khadhroony-bot2-reference/docs/NOMENCLATURE.md create mode 100644 migration/khadhroony-bot2-reference/docs/POSTGRES_STORE.md create mode 100644 migration/khadhroony-bot2-reference/docs/PROGRAM_CODE_INDEX.md create mode 100644 migration/khadhroony-bot2-reference/docs/PROGRAM_IDS.md create mode 100644 migration/khadhroony-bot2-reference/docs/PROGRAM_ID_CONSTANTS_AUDIT.md create mode 100644 migration/khadhroony-bot2-reference/docs/PROGRAM_NAMING.md create mode 100644 migration/khadhroony-bot2-reference/docs/PROGRAM_REGISTRY_CONTROL.md create mode 100644 migration/khadhroony-bot2-reference/docs/PROJECT_OBJECTIVES.md create mode 100644 migration/khadhroony-bot2-reference/docs/RAW_STORAGE_LIFECYCLE.md create mode 100644 migration/khadhroony-bot2-reference/docs/RAW_STORE.md create mode 100644 migration/khadhroony-bot2-reference/docs/REPLAY_PIPELINE.md create mode 100644 migration/khadhroony-bot2-reference/docs/RPC_ENDPOINT_ROLES.md create mode 100644 migration/khadhroony-bot2-reference/docs/RUST_WORKSPACE_RULE_AUDIT.md create mode 100644 migration/khadhroony-bot2-reference/docs/SOLANA_INTERFACE_DEPENDENCIES.md create mode 100644 migration/khadhroony-bot2-reference/docs/SOLANA_NATIVE_RPC_CLOSURE_AUDIT.md create mode 100644 migration/khadhroony-bot2-reference/docs/SOLANA_STANDARD_RPC_MATRIX.json create mode 100644 migration/khadhroony-bot2-reference/docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json create mode 100644 migration/khadhroony-bot2-reference/docs/SPL_ELGAMAL_REGISTRY_MATRIX.json create mode 100644 migration/khadhroony-bot2-reference/docs/SPL_MEMO_MATRIX.json create mode 100644 migration/khadhroony-bot2-reference/docs/SPL_TOKEN_2022_CONFIDENTIAL_EXECUTION_AUDIT.md create mode 100644 migration/khadhroony-bot2-reference/docs/SPL_TOKEN_2022_MATRIX.json create mode 100644 migration/khadhroony-bot2-reference/docs/SPL_TOKEN_2022_SOURCE_AUDIT.md create mode 100644 migration/khadhroony-bot2-reference/docs/SPL_TOKEN_2022_VALIDATION_MATRIX.json create mode 100644 migration/khadhroony-bot2-reference/docs/SPL_TOKEN_MATRIX.json create mode 100644 migration/khadhroony-bot2-reference/docs/SQL_DEMOS.md create mode 100644 migration/khadhroony-bot2-reference/docs/SURFACE_CRATE_MATRIX.md create mode 100644 migration/khadhroony-bot2-reference/docs/TRACING_CONTRACT.md create mode 100644 migration/khadhroony-bot2-reference/docs/TRANSACTION_ACQUISITION_MODEL.md create mode 100644 migration/khadhroony-bot2-reference/docs/V0_4_6_VALIDATION.md create mode 100644 migration/khadhroony-bot2-reference/docs/VERSION_HEADER_AUDIT.md create mode 100644 migration/khadhroony-bot2-reference/docs/WS_LISTENERS.md create mode 100644 migration/khadhroony-bot2-reference/idls/001.README.md create mode 100644 migration/khadhroony-bot2-reference/idls/bonkswap.BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p.json create mode 100644 migration/khadhroony-bot2-reference/idls/boop_fun.boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4.json create mode 100644 migration/khadhroony-bot2-reference/idls/bubblegum.BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY.json create mode 100644 migration/khadhroony-bot2-reference/idls/byreal_clmm.REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2.json create mode 100644 migration/khadhroony-bot2-reference/idls/carrot.CarrotwivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s.json create mode 100644 migration/khadhroony-bot2-reference/idls/cctp_v1.CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3.json create mode 100644 migration/khadhroony-bot2-reference/idls/cctp_v2.CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe.json create mode 100644 migration/khadhroony-bot2-reference/idls/clone.C1onEW2kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr.json create mode 100644 migration/khadhroony-bot2-reference/idls/dflow_v4.DF1ow4tspfHX9JwWJsAb9epbkA8hmpSEAtxXy1V27QBH.json create mode 100644 migration/khadhroony-bot2-reference/idls/drift_v2.dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH.json create mode 100644 migration/khadhroony-bot2-reference/idls/fusion_amm.fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9.json create mode 100644 migration/khadhroony-bot2-reference/idls/goosefx_gamma.GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT.json create mode 100644 migration/khadhroony-bot2-reference/idls/goosefx_v2.GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn.json create mode 100644 migration/khadhroony-bot2-reference/idls/guacswap.Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1.json create mode 100644 migration/khadhroony-bot2-reference/idls/helium_treasury_management.treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5.json create mode 100644 migration/khadhroony-bot2-reference/idls/hylo_exchange.HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn.json create mode 100644 migration/khadhroony-bot2-reference/idls/hylo_stability_pool.HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ.json create mode 100644 migration/khadhroony-bot2-reference/idls/jito_tip_distribution.4R3gSG8BpU4t19KYj8CfnbtRpnT8gtk4dvTHxVRwc2r7..json create mode 100644 migration/khadhroony-bot2-reference/idls/jupiter_aprepro_smart_wallet.JSW99DKmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw.json create mode 100644 migration/khadhroony-bot2-reference/idls/jupiter_dca.DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M.json create mode 100644 migration/khadhroony-bot2-reference/idls/jupiter_limit_order.jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu.json create mode 100644 migration/khadhroony-bot2-reference/idls/jupiter_limit_order_v2.j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X.json create mode 100644 migration/khadhroony-bot2-reference/idls/jupiter_locker.LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn.json create mode 100644 migration/khadhroony-bot2-reference/idls/jupiter_perpetuals.PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu.json create mode 100644 migration/khadhroony-bot2-reference/idls/jupiter_stable.JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr.json create mode 100644 migration/khadhroony-bot2-reference/idls/jupiter_v4.JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB.json create mode 100644 migration/khadhroony-bot2-reference/idls/jupiter_v6.JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4.json create mode 100644 migration/khadhroony-bot2-reference/idls/kamino.6LtLpnUFNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc.json create mode 100644 migration/khadhroony-bot2-reference/idls/kamino_farms.FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr.json create mode 100644 migration/khadhroony-bot2-reference/idls/kamino_lending.KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD.json create mode 100644 migration/khadhroony-bot2-reference/idls/kamino_vault.kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr.json create mode 100644 migration/khadhroony-bot2-reference/idls/kamino_vault_v2.KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd.json create mode 100644 migration/khadhroony-bot2-reference/idls/layerzero_endpoint.76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6.json create mode 100644 migration/khadhroony-bot2-reference/idls/layerzero_executor.6doghB248px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn.json create mode 100644 migration/khadhroony-bot2-reference/idls/lifinity_amm_v2.2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c.json create mode 100644 migration/khadhroony-bot2-reference/idls/marginfi_v2.MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA.json create mode 100644 migration/khadhroony-bot2-reference/idls/marinade_finance.MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD.json create mode 100644 migration/khadhroony-bot2-reference/idls/metadao_amm_v0.5.AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ.json create mode 100644 migration/khadhroony-bot2-reference/idls/metadao_futarchy.FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq.json create mode 100644 migration/khadhroony-bot2-reference/idls/metadao_launchpad.moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM.json create mode 100644 migration/khadhroony-bot2-reference/idls/metdao_bid_wall.WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx.json create mode 100644 migration/khadhroony-bot2-reference/idls/meteora_damm_v2.cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG.json create mode 100644 migration/khadhroony-bot2-reference/idls/meteora_dbc.dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN.json create mode 100644 migration/khadhroony-bot2-reference/idls/meteora_dlmm.LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo.json create mode 100644 migration/khadhroony-bot2-reference/idls/meteora_pools_amm.Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB.json create mode 100644 migration/khadhroony-bot2-reference/idls/meteora_vault.24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi.json create mode 100644 migration/khadhroony-bot2-reference/idls/moonit.MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG.json create mode 100644 migration/khadhroony-bot2-reference/idls/numeraire.NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P.json create mode 100644 migration/khadhroony-bot2-reference/idls/okx_dex_router.proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u.json create mode 100644 migration/khadhroony-bot2-reference/idls/okx_lab_v1.6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma.json create mode 100644 migration/khadhroony-bot2-reference/idls/ondo_gm.XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm.json create mode 100644 migration/khadhroony-bot2-reference/idls/openbook_v2.opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb.json create mode 100644 migration/khadhroony-bot2-reference/idls/orca_wavebreak.waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF.json create mode 100644 migration/khadhroony-bot2-reference/idls/orca_whirlpool.whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.json create mode 100644 migration/khadhroony-bot2-reference/idls/pancakeswap.HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq.json create mode 100644 migration/khadhroony-bot2-reference/idls/phoenix_eternal.EtrnLzgbS7nMMy5fbD42kXiUzGg8XQzJ972Xtk1cjWih.json create mode 100644 migration/khadhroony-bot2-reference/idls/printr.T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint.json create mode 100644 migration/khadhroony-bot2-reference/idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json create mode 100644 migration/khadhroony-bot2-reference/idls/pump_fun.6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P.json create mode 100644 migration/khadhroony-bot2-reference/idls/pump_swap.pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA.json create mode 100644 migration/khadhroony-bot2-reference/idls/pumpup.PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd.json create mode 100644 migration/khadhroony-bot2-reference/idls/raydium_clmm.CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK.json create mode 100644 migration/khadhroony-bot2-reference/idls/raydium_cpmm.CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C.json create mode 100644 migration/khadhroony-bot2-reference/idls/raydium_launchlab.LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj.json create mode 100644 migration/khadhroony-bot2-reference/idls/raydium_lock.LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE.json create mode 100644 migration/khadhroony-bot2-reference/idls/saber_decimal.DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB.json create mode 100644 migration/khadhroony-bot2-reference/idls/squads.SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf.json create mode 100644 migration/khadhroony-bot2-reference/idls/stabble_clmm.6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6.json create mode 100644 migration/khadhroony-bot2-reference/idls/stabble_stable_swap.swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ.json create mode 100644 migration/khadhroony-bot2-reference/idls/stabble_weighted_swap.swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW.json create mode 100644 migration/khadhroony-bot2-reference/idls/streamflow.strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m.json create mode 100644 migration/khadhroony-bot2-reference/idls/tensor-cnft.TCMPhJdwDryooaGtiocG1u3xcYbRpiJzb283XfCZsDp.json create mode 100644 migration/khadhroony-bot2-reference/idls/vertigo.vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ.json create mode 100644 migration/khadhroony-bot2-reference/idls/virtuals.5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki.json create mode 100644 migration/khadhroony-bot2-reference/idls/woofi.WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb.json create mode 100644 migration/khadhroony-bot2-reference/idls/zeta.ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD.json create mode 100644 migration/khadhroony-bot2-reference/kb_app_cli/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_app_cli/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_app_cli/src/main.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/build.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/capabilities/default.json create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_backfill.html create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_config.html create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_core_extraction.html create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_decode_replay.html create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_execution_solana_core.html create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_execution_spl.html create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_http.html create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_sql_diag.html create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_sql_pg_core.html create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_sql_pg_raw.html create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_sql_replay_candidates.html create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_ws.html create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/fonts/DOS_Amazigh.ttf create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/imgs/logo.png create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/imgs/splash.png create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/main.html create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_app.scss create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_bootswatch.scss create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_fontawesome.scss create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_simplebar.scss create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_variables.scss create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/main.scss create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/splash.scss create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/splash.html create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_backfill.ts create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_config.ts create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_core_extraction.ts create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_decode_replay.ts create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_execution_solana_core.ts create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_execution_spl.ts create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_http.ts create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_diag.ts create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_pg_core.ts create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_pg_raw.ts create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_replay_candidates.ts create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_tables.ts create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_ws.ts create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/frontend_log.ts create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/main.ts create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/splash.ts create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/icons/favicon.ico create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/icons/favicon.png create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/package.json create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/app_state.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/demo_backfill.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/demo_config.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/demo_core_extraction.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/demo_decode_replay.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/demo_execution_solana_core.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/demo_execution_spl.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/demo_http.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/demo_spl_ata.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/demo_spl_token.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/demo_spl_token_2022.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_common.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_diag.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_pg_core.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_pg_raw.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_replay_candidates.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/demo_ws.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/frontend_log.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/main.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/main_window.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/splash.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/src/tauri.rs create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/tauri.conf.json create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/tsconfig.json create mode 100644 migration/khadhroony-bot2-reference/kb_app_demo/vite.config.ts create mode 100644 migration/khadhroony-bot2-reference/kb_config/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_config/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_config/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_config/src/settings.rs create mode 100644 migration/khadhroony-bot2-reference/kb_core/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_core/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_core/src/error.rs create mode 100644 migration/khadhroony-bot2-reference/kb_core/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_core/src/module.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_believe/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_believe/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_believe/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_believe/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_believe/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_zora/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_zora/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_zora/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_zora/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_amm_zora/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_anchor/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_anchor/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_anchor/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_anchor/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_anchor/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_api/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_api/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_api/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_api/src/contracts.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_api/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_api/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_clone/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_clone/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_clone/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_clone/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_clone/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/account.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/canonical.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/instruction.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/accounts.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/address_lookup_table.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/compute_budget.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/config.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/event.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/feature.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/loaders.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/payload.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/precompiles.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/slashing.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/stake.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/system.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/vote.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/zk_elgamal.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/zk_token_proof.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/src/instruction.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/registry.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/state.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_memo/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_memo/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_memo/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_memo/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_memo/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_memo/src/memo.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_noop/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_noop/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_noop/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_noop/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_noop/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_token/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_token/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_token/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_token/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_token/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_token/src/token.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/state.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/token.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/src/decoder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_execution_api/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_execution_api/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_execution_api/src/execution.rs create mode 100644 migration/khadhroony-bot2-reference/kb_execution_api/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_execution_api/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_execution_safety/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_execution_safety/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_execution_safety/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_execution_safety/src/safety.rs create mode 100644 migration/khadhroony-bot2-reference/kb_execution_solana/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_execution_solana/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_execution_solana/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_execution_solana/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_execution_solana/src/nonce.rs create mode 100644 migration/khadhroony-bot2-reference/kb_execution_solana/src/transaction.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_believe/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_believe/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_believe/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_believe/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_believe/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_solfi/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_solfi/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_solfi/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_solfi/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_solfi/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_woofi/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_woofi/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_woofi/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_woofi/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_woofi/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_zora/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_zora/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_zora/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_zora/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_amm_zora/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_clone/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_clone/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_clone/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_clone/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_clone/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_kamino/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_kamino/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_kamino/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_kamino/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_kamino/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_solana_core/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_solana_core/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_solana_core/src/address_lookup_table.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_solana_core/src/builder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_solana_core/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_solana_core/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_solana_core/src/intent.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_solana_core/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_solana_core/src/loader_v3.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_solana_core/src/loader_v4.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_solana_core/src/native_admin.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_solana_core/src/precompiles.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_solana_core/src/slashing.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_solana_core/src/stake.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_solana_core/src/validation.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_solana_core/src/vote.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_solana_core/src/zk_elgamal.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/builder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/intent.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/builder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/intent.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_memo/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_memo/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/builder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/intent.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_noop/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_noop/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_noop/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_noop/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_noop/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_token/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_token/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_token/src/builder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_token/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_token/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_token/src/intent.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_token/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/builder.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/confidential.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/intent.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_staking_solayer/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_staking_solayer/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_staking_solayer/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_staking_solayer/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_staking_solayer/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_kamino/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_kamino/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_kamino/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_kamino/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_kamino/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_meteora/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_meteora/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_meteora/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_meteora/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vault_meteora/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/src/executor.rs create mode 100644 migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_logging/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_logging/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_logging/src/config.rs create mode 100644 migration/khadhroony-bot2-reference/kb_logging/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_logging/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_logging/src/tracing_runtime.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_admin/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_admin/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_admin/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_admin/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_admin/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_api/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_api/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_api/src/contracts.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_api/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_api/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_bridge/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_bridge/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_bridge/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_bridge/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_fees/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_fees/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_fees/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_fees/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_governance/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_governance/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_governance/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_governance/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_lending/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_lending/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_lending/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_lending/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_lifecycle/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_lifecycle/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_lifecycle/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_lifecycle/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_lifecycle/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_liquidity/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_liquidity/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_liquidity/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_liquidity/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_metadata/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_metadata/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_metadata/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_metadata/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_nft/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_nft/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_nft/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_nft/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_oracle/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_oracle/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_oracle/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_oracle/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_orderbook/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_orderbook/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_orderbook/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_orderbook/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_perpetuals/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_perpetuals/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_perpetuals/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_perpetuals/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_pool_state/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_pool_state/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_pool_state/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_pool_state/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_rewards/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_rewards/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_rewards/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_rewards/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_risk/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_risk/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_risk/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_risk/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_risk/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_routing/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_routing/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_routing/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_routing/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_staking/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_staking/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_staking/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_staking/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_staking/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_token_accounts/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_token_accounts/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_token_accounts/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_token_accounts/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_token_accounts/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_token_metadata_risk/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_token_metadata_risk/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_token_metadata_risk/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_token_metadata_risk/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_trades/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_trades/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_trades/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_trades/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_vault/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_vault/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_vault/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_materializer_vault/src/materializer.rs create mode 100644 migration/khadhroony-bot2-reference/kb_model/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_model/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_model/src/canonical_transaction.rs create mode 100644 migration/khadhroony-bot2-reference/kb_model/src/decoded.rs create mode 100644 migration/khadhroony-bot2-reference/kb_model/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_model/src/materialized.rs create mode 100644 migration/khadhroony-bot2-reference/kb_model/src/nomenclature.rs create mode 100644 migration/khadhroony-bot2-reference/kb_model/src/observation.rs create mode 100644 migration/khadhroony-bot2-reference/kb_model/src/solana.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/backfill.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/core_extraction.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/decode_replay.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/plan.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/solana_ata_execution.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/solana_ata_stateful.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/solana_elgamal_registry_stateful.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/solana_execution.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/solana_memo_execution.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/solana_stateful.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_correlation.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_crypto_preflight.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_devnet_execution.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_devnet_scenarios.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_execution_orchestration.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_preflight.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_proof_orchestration.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_stateful.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_validation.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_execution.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_lifecycle.rs create mode 100644 migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_stateful.rs create mode 100644 migration/khadhroony-bot2-reference/kb_program_ids/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_program_ids/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_program_ids/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/client.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/endpoint_role.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/execution_rpc.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/get_signatures_for_address.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/get_transaction.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/http_client.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/http_pool.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/json_rpc.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/standard_http.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_accounts.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_blocks.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_cluster.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_economics.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_tokens.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_transactions.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/standard_methods.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/standard_ws.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/validation.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/ws_client.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/ws_pool.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/src/ws_session.rs create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_equivalent_a.json create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_equivalent_b.json create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_legacy_failure.json create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_legacy_success.json create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_optional_absent.json create mode 100644 migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_v0_alt_cpi_token_2022.json create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/db_error.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/dtos.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/dtos/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/dtos/core_dtos.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/dtos/core_extraction_dtos.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/dtos/decode_dtos.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/dtos/event_dtos.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/dtos/ledger_dtos.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/dtos/raw_dtos.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/dtos/store_dtos.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/entities.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/entities/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/entities/core_entities.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/entities/event_entities.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/entities/ledger_entities.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/entities/raw_entities.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/health.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/pagination.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/repositories.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/repositories/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_store_core/src/repositories/storage_repositories.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/maintenance/drop_raw_core_store.sql create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/migrations/0001_canonical_transaction_store.sql create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/migrations/0002_core_store.sql create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/migrations/0003_processing_ledger.sql create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/migrations/0004_decode_materialization_store.sql create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/migrations.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/pg_store.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/queries.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/queries/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/queries/core_extraction_queries.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/queries/core_queries.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/queries/decode_pipeline_queries.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/queries/health_queries.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/queries/raw_queries.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/queries/replay_candidate_queries.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/queries/table_diagnostics_queries.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/replay_candidates.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/repositories.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/core_extraction_repository.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/core_transaction_repository.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/decode_pipeline_repository.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/raw_transaction_repository.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/store_health_repository.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_pg/src/test_serial.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_sqlite/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_store_sqlite/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_store_sqlite/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_store_sqlite/src/store.rs create mode 100644 migration/khadhroony-bot2-reference/kb_wallet/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_wallet/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_wallet/src/constants.rs create mode 100644 migration/khadhroony-bot2-reference/kb_wallet/src/lib.rs create mode 100644 migration/khadhroony-bot2-reference/kb_wallet/src/wallet.rs create mode 100644 migration/khadhroony-bot2-reference/kb_worker/Cargo.toml create mode 100644 migration/khadhroony-bot2-reference/kb_worker/README.md create mode 100644 migration/khadhroony-bot2-reference/kb_worker/src/main.rs create mode 100644 migration/khadhroony-bot2-reference/prompts/.gitignore create mode 100644 migration/khadhroony-bot2-reference/prompts/001_version_session_template.md create mode 100644 migration/khadhroony-bot2-reference/prompts/002_program_registry_and_naming_audit.md create mode 100644 migration/khadhroony-bot2-reference/prompts/003_v0_1_logging_config_profiles.md create mode 100644 migration/khadhroony-bot2-reference/prompts/004_v0_2_sql_storage_materialization.md create mode 100644 migration/khadhroony-bot2-reference/prompts/005_v0_2_2_pg_connection_migrations.md create mode 100644 migration/khadhroony-bot2-reference/prompts/005_v0_3_rpc_ingestion_ws.md create mode 100644 migration/khadhroony-bot2-reference/prompts/006_v0_4_wallet_execution_demo.md create mode 100644 migration/khadhroony-bot2-reference/prompts/007_v0_5_core_decoders_listeners.md create mode 100644 migration/khadhroony-bot2-reference/prompts/008_v0_6_priority_dex_router_surfaces.md create mode 100644 migration/khadhroony-bot2-reference/prompts/009_v0_2_1_store_core_contracts.md create mode 100644 migration/khadhroony-bot2-reference/prompts/010_v0_2_2_postgres_connection_health_migrations.md create mode 100644 migration/khadhroony-bot2-reference/prompts/011_v0_2_3_raw_store_minimal.md create mode 100644 migration/khadhroony-bot2-reference/prompts/012_v0_2_4_core_store_minimal.md create mode 100644 migration/khadhroony-bot2-reference/prompts/013_v0_2_5_db_diagnostics_tauri.md create mode 100644 migration/khadhroony-bot2-reference/prompts/014_v0_3_0_agave_local_research.md create mode 100644 migration/khadhroony-bot2-reference/prompts/015_v0_3_1_canonical_store_migration.md create mode 100644 migration/khadhroony-bot2-reference/prompts/016_v0_3_2_canonical_transaction_contract.md create mode 100644 migration/khadhroony-bot2-reference/prompts/017_v0_3_3_http_backfill_free_tier.md create mode 100644 migration/khadhroony-bot2-reference/prompts/018_v0_3_4_canonical_to_core_extraction.md create mode 100644 migration/khadhroony-bot2-reference/prompts/019_v0_4_0_decoder_infrastructure.md create mode 100644 migration/khadhroony-bot2-reference/prompts/020_v0_4_1_native_solana_programs.md create mode 100644 migration/khadhroony-bot2-reference/prompts/021_v0_4_2_executor_solana_core.md create mode 100644 migration/khadhroony-bot2-reference/prompts/022_v0_4_3_spl_memo.md create mode 100644 migration/khadhroony-bot2-reference/prompts/023_v0_4_4_spl_token.md create mode 100644 migration/khadhroony-bot2-reference/prompts/024_v0_4_5_spl_associated_token_account.md create mode 100644 migration/khadhroony-bot2-reference/prompts/025_v0_4_6_spl_token_2022.md create mode 100644 migration/khadhroony-bot2-reference/prompts/026_v0_4_7_metaplex_token_metadata.md create mode 100644 migration/khadhroony-bot2-reference/prompts/README.md create mode 100644 migration/khadhroony-bot2-reference/registry/.gitignore create mode 100644 migration/khadhroony-bot2-reference/registry/core_program_id_seed.toml create mode 100644 migration/khadhroony-bot2-reference/registry/program_registry_seed.toml create mode 100644 migration/khadhroony-bot2-reference/rustfmt.toml create mode 100755 migration/khadhroony-bot2-reference/scripts/audit_khadhroony_workspace_rules.py create mode 100755 migration/khadhroony-bot2-reference/scripts/audit_rust_export_completeness.py create mode 100755 migration/khadhroony-bot2-reference/scripts/audit_rust_general_rules.py create mode 100755 migration/khadhroony-bot2-reference/scripts/audit_rust_workspace_rules.py create mode 100755 migration/khadhroony-bot2-reference/scripts/devnet/load_elgamal_registry_fixture.sh create mode 100755 migration/khadhroony-bot2-reference/scripts/devnet/load_spl_fixture.sh create mode 100755 migration/khadhroony-bot2-reference/scripts/devnet/prepare_elgamal_registry_fixture.sh create mode 100755 migration/khadhroony-bot2-reference/scripts/devnet/prepare_spl_fixture.sh create mode 100755 migration/khadhroony-bot2-reference/scripts/run_agave_local_research.sh create mode 100644 migration/khadhroony-bot2-reference/sql/README.md create mode 100644 migration/khadhroony-bot2-reference/sql/schema/.gitignore create mode 100644 migration/khadhroony-bot2-reference/sql/validation/.gitignore create mode 100644 migration/khadhroony-bot2-reference/sql/validation/000_core_integrity.sql create mode 100644 migration/module-map.json create mode 100644 rustfmt.toml diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..1dfe6d6 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,3 @@ +[build] +target-dir = "../target" # path of where to place generated artifacts +build-dir = "../target" # path of where to place intermediate build artifacts \ No newline at end of file diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000..99f26c0 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..24d7cc6 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"] +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..556dd40 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,23 @@ +# CHANGELOG + +## 0.1.0-pre.001 + +- Renommage de `kb_wallet` en `kb-wallet`. +- Restauration de `kb-core/src/error.rs` et `kb-core/src/module.rs`. +- Réduction de `kb-core/src/lib.rs` à la déclaration des modules et aux réexports documentés. + +## 0.1.0-pre.000 + +- Création du workspace consolidé `khadhroony-bot3`. +- Ajout de `kb-lib` pour les décodeurs, exécuteurs et matérialisateurs. +- Fusion architecturale prévue des stores dans `kb-store`. +- Conservation du workspace précédent comme référence de migration non compilée. + +## 0.1.0-pre.002 + +- Portage des modèles partagés de `kb_model` dans `kb-lib::model`. +- Portage des contrats complets de `kb_decoder_api` dans `kb-lib::decoder::api`. +- Portage des contrats complets de `kb_materializer_api` dans `kb-lib::materializer::api`. +- Portage des contrats complets de `kb_execution_api` dans `kb-lib::executor::api`. +- Ajout du contrat neutre `CoreInstructionReplayInput` dans `kb-lib` pour éviter une dépendance cyclique avec `kb-store`. +- `kb-lib/src/lib.rs` reste une façade de modules et de réexports. diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e62abd9 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,140 @@ +# file: Cargo.toml +# version: 1 + +[workspace] +resolver = "3" +members = [ + "kb-core", + "kb-config", + "kb-lib", + "kb-logging", + "kb-program-ids", + "kb-pipeline", + "kb-rpc", + "kb-store", + "kb-wallet", + "kb-app-demo", +] + +[workspace.package] +version = "0.1.0" +edition = "2024" +license = "MIT" +repository = "https://git.sasedev.com/Sasedev/khadhroony-bot3" +authors = ["SinuS von SifriduS "] +publish = false + +[workspace.dependencies] +argon2 = { version = "^0.5", features = ["std", "zeroize"] } +async-trait = { version = "^0.1", features = [] } +base64 = { version = "^0.22", features = [] } +bytemuck = { version = "^1.25", features = ["derive"] } +borsh_0_10 = { package = "borsh", version = "^0.10" } +borsh = { version = "^1.7", features = ["ascii", "bson", "bytes", "default", "derive", "de_strict_order", "borsh-derive", "indexmap", "std", "rc"] } +bs58 = { version = "^0.5", features = ["cb58", "check", "default"] } +chacha20poly1305 = { version = "^0.11", features = ["std", "stream"] } +chrono = { version = "^0.4", features = ["serde"] } +fs2 = { version = "^0.4", features = [] } +futures-util = { version = "^0.3", features = ["default", "futures-sink"] } +jsonschema = { version = "^0.48", features = [] } +mpl-token-metadata = { version = "^5.1", features = ["serde"] } +rand = { version = "^0.10", features = ["chacha", "serde", "std", "sys_rng"] } +reqwest = { version = "^0.13", default-features = false, features = ["charset", "cookies", "deflate", "form", "gzip", "http2", "json", "multipart", "query", "rustls", "socks", "stream", "zstd"] } +rustls = { version = "^0.23", features = ["aws-lc-rs"] } +serde = { version = "^1.0", features = ["derive"] } +serde_json = { version = "^1.0", features = [] } +sha2 = { version = "^0.11", features = ["zeroize"] } +solana-account-decoder-client-types = { version = "^4.1", features = ["zstd"] } +solana-address-lookup-table-interface = { version = "^3.1", features = ["serde", "wincode"] } +solana-compute-budget-interface = { version = "^3.0", features = ["borsh", "serde"] } +solana-config-interface = { version = "^2.0", features = ["serde"] } +solana-ed25519-program = { version = "^3.0", features = [] } +solana-feature-gate-interface = { version = "^4.0", features = ["serde"] } +solana-feature-set-interface = { version = "^4.0", features = [] } +solana-hash = { version = "^4.5", features = ["atomic", "borsh", "copy", "decode", "default", "rand", "sanitize", "serde", "std", "wincode"] } +solana-instruction = { version = "^3.4", features = ["borsh", "default", "serde", "std", "syscalls", "wincode"] } +solana-keypair = { version = "^3.1", features = ["seed-derivable"] } +solana-loader-v2-interface = { version = "^3.0", features = ["serde"] } +solana-loader-v3-interface = { version = "^8.0", features = ["serde", "wincode"] } +solana-loader-v4-interface = { version = "^3.1", features = ["serde"] } +solana-message = { version = "^4.3", features = ["blake3", "default", "serde", "std", "wincode"] } +solana-nonce = { version = "^3.2", features = ["serde", "wincode"] } +solana-pubkey = { version = "^4.2", features = ["alloc", "borsh", "bytemuck", "curve25519", "default", "rand", "serde", "sha2", "std", "wincode"] } +solana-rpc-client-api = { version = "^4.1", features = [] } +solana-sdk = { version = "^4.0", features = ["full"] } +solana-sdk-ids = { version = "^3.1", features = [] } +solana-secp256k1-program = { version = "^3.0", default-features = false, features = ["serde"] } +solana-secp256r1-program = { version = "^3.0", features = [] } +solana-signer = { version = "^3.0", features = ["default", "std"] } +solana-stake-interface = { version = "^4.3", features = ["borsh", "codama", "serde", "sysvar", "wincode"] } +solana-system-interface = { version = "^3.2", features = ["alloc", "serde", "std", "solana-instruction", "wincode"] } +solana-sysvar = { version = "^4.1", features = ["serde", "wincode"] } +solana-transaction = { version = "^4.1", features = ["blake3", "default", "serde", "std", "verify", "wincode"] } +solana-transaction-status-client-types = { version = "^4.1", features = [] } +solana-vote-interface = { version = "^6.0", features = ["serde", "wincode"] } +solana-zk-elgamal-proof-interface = { version = "^0.1", features = [] } +solana-zk-sdk-pod = { version = "^0.1", features = ["serde"] } +spl-associated-token-account-interface = { version = "^2.0", features = ["borsh"] } +spl-elgamal-registry-interface = { version = "^0.2", features = [] } +spl-instruction-padding-interface = { version = "^1.0", features = [] } +spl-memo-interface = { version = "^2.1", features = [] } +spl-nonce-interface = { version = "^0.1", features = [] } +spl-token-confidential-transfer-proof-extraction = { version = "^0.6", features = [] } +spl-transfer-hook-interface = { version = "^2.1", features = [] } +spl-token-metadata-interface = { version = "^1.0", features = ["serde", "serde-traits"] } +spl-token-group-interface = { version = "^0.7", features = [] } +spl-token-interface = { version = "^3.0", features = [] } +spl-token-2022-interface = { version = "^3.1", features = ["serde"] } +sqlx = { version = "^0.9", features = ["bigdecimal", "chrono", "default", "derive", "json", "mysql", "postgres", "runtime-tokio", "sqlite", "tls-rustls", "uuid"] } +tauri = { version = "^2.11", features = ["default", "tray-icon"] } +tauri-build = { version = "^2.6", features = [] } +tauri-plugin-tracing = { version = "^0.3", default-features = false, features = [] } +tempfile = { version = "^3.27", features = [] } +tokio = { version = "^1.52", features = ["full"] } +tokio-stream = { version = "^0.1", features = ["full"] } +tokio-tungstenite = { version = "^0.30", default-features = false, features = ["connect", "handshake", "rustls-tls-webpki-roots", "stream", "url"] } +tracing = { version = "^0.1", features = [] } +tracing-appender = { version = "^0.2", features = [] } +tracing-subscriber = { version = "^0.3", features = ["ansi", "env-filter", "chrono", "serde", "json"] } +wincode = { version = "^0.5", features = ["alloc", "bytes", "derive", "indexmap", "smallvec", "uuid", "uuid-serde-compat"] } +ts-rs = { version = "^12.0", features = ["bigdecimal", "bson", "bytes", "chrono", "indexmap", "ordered-float", "serde_json", "tokio", "url", "uuid"] } +yellowstone-grpc-client = { version = "^13.1", features = [] } +yellowstone-grpc-proto = { version = "^12.5", features = [] } +uuid = { version = "^1.23", features = ["v4", "serde"] } +zeroize = { version = "^1.9", features = ["derive", "serde", "std"] } + +[workspace.lints.rust] +unsafe_code = "forbid" +unreachable_pub = "deny" +missing_docs = "warn" + +[workspace.lints.clippy] +manual_range_patterns = "allow" +unwrap_used = "deny" +expect_used = "deny" +implicit_return = "deny" +needless_return = "allow" +useless_vec = "deny" +question_mark = "deny" +question_mark_used = "deny" +needless_match = "allow" +manual_ok_err = "allow" +manual_unwrap_or = "allow" +manual_map = "allow" +match_like_matches_macro = "allow" +single_match = "allow" +manual_unwrap_or_default = "allow" +manual_find = "allow" +explicit_counter_loop = "allow" +get_first = "allow" +implicit_saturating_sub = "allow" + +[profile.dev] +incremental = true # Compile your binary in smaller steps. + +[profile.release] +codegen-units = 1 # Allows LLVM to perform better optimization. +lto = true # Enables link-time-optimizations. +opt-level = 3 # s Prioritizes small binary size. Use `3` if you prefer speed. +panic = "abort" # Higher performance by disabling panic handlers. +strip = true # Ensures debug symbols are removed. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ede4886 --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ +# Khadhroony Bot3 + +Khadhroony Bot3 is the consolidated successor workspace to `khadhroony-bot2`. + +## Workspace crates + +- `kb-core`: foundation errors and shared primitives. +- `kb-config`: configuration contracts. +- `kb-lib`: all decoder, executor and materializer APIs and implementations. +- `kb-logging`: logging and tracing runtime. +- `kb-program-ids`: Solana program identifier registry. +- `kb-pipeline`: ingestion, decoding, materialization and execution orchestration. +- `kb-rpc`: HTTP and WebSocket Solana transports. +- `kb-store`: store-neutral contracts and PostgreSQL implementation modules. +- `kb-wallet`: wallet and signer support. +- `kb-app-demo`: the only binary retained during the initial migration. + +## Migration principle + +The previous workspace is preserved under `migration/khadhroony-bot2-reference`. The new workspace compiles independently as a controlled scaffold. Public APIs are ported into `kb-lib` module by module, with compatibility recorded in `migration/module-map.json`. + +## Validation + +```bash +cargo fmt --all +cargo check --workspace +cargo test --workspace +cargo clippy --workspace --all-targets +``` + +## État de migration `0.1.0-pre.001` + +- `kb-wallet` remplace définitivement le scaffold `kb_wallet`. +- `kb-core` conserve l’architecture de `kb_core` : implémentations dans `error.rs` et `module.rs`, façade et réexports dans `lib.rs`. +- Les prochaines APIs consolidées de `kb-lib` suivront la même règle : aucun contrat métier ne sera défini directement dans `lib.rs`. + +## Contrats consolidés + +À partir de `0.1.0-pre.002`, `kb-lib` porte les modèles et contrats fondamentaux autrefois répartis entre `kb_model`, `kb_decoder_api`, `kb_materializer_api` et `kb_execution_api`. Les implémentations restent dans des modules dédiés ; `kb-lib/src/lib.rs` ne contient que les déclarations de modules et les réexports publics. diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..398eaea --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,27 @@ +# ROADMAP — khadhroony-bot3 + +## 0.1.0 — Consolidation du workspace + +- [x] Créer les dix crates initiales du workspace avec le nom canonique `kb-wallet`. +- [x] Regrouper les contrats decoder/executor/materializer dans `kb-lib`. +- [x] Générer l’arborescence des modules à partir des anciennes crates. +- [x] Conserver une copie de référence complète de `khadhroony-bot2`. +- [x] Restaurer l’architecture modulaire de `kb-core` à partir de `kb_core`, avec `lib.rs` limité aux modules et réexports. +- [ ] Porter exactement les contrats publics de `kb_decoder_api`. +- [ ] Porter exactement les contrats publics des APIs d’exécution et de matérialisation. +- [ ] Porter les modèles de `kb_model` vers `kb-lib`. +- [ ] Porter les décodeurs Solana core, SPL et Metaplex. +- [ ] Porter leurs matérialisateurs et exécuteurs. +- [ ] Fusionner `kb_store_core` et `kb_store_pg` dans `kb-store`. +- [ ] Adapter `kb-pipeline` aux nouveaux chemins publics. +- [ ] Adapter `kb-app-demo` et rétablir les validations fonctionnelles. +- [ ] Auditer rétrospectivement la couverture historique des décodeurs et matérialisateurs. + +### 0.1.0-pre.002 — Contrats fondamentaux consolidés + +- [x] Porter les modèles partagés de `kb_model` dans `kb-lib`. +- [x] Porter les contrats publics de décodage dans `kb-lib::decoder::api`. +- [x] Porter les contrats publics de matérialisation dans `kb-lib::materializer::api`. +- [x] Porter les contrats publics d'exécution dans `kb-lib::executor::api`. +- [x] Maintenir `lib.rs` comme façade sans implémentation métier. +- [ ] Valider le workspace avec Cargo sur la machine de développement. diff --git a/RULES.md b/RULES.md new file mode 100644 index 0000000..5df856a --- /dev/null +++ b/RULES.md @@ -0,0 +1,239 @@ + + + +# Règles spécifiques à `khadhroony-bot2` + +Ce fichier contient uniquement les règles propres au projet et au workspace `khadhroony-bot2`. + +Les règles Rust générales et réutilisables dans tous les projets sont définies dans [`RUST_RULES.md`](RUST_RULES.md). Les deux fichiers sont normatifs et cumulatifs. En cas de conflit, la règle la plus stricte s'applique ; une règle spécifique au workspace ne peut jamais assouplir une règle générale sans exception explicitement documentée. + +Toute livraison doit exécuter l'audit `python3 scripts/audit_rust_workspace_rules.py`. Tant que l'audit global n'est pas propre, les écarts existants doivent être résorbés par lots de correctifs avant toute nouvelle prérelease. + +## Règles de nommage + +- Tous les noms de fichiers et de répertoires doivent être écrits en anglais. +- Les noms de fichiers et de répertoires ne doivent contenir aucun accent, espace ou caractère spécial inutile. +- Les noms internes doivent utiliser le format `snake_case` lorsque c'est applicable. +- Les crates Rust utilisent le préfixe `kb_`. +- Les crates de décodeur utilisent le format `kb_decoder__`. +- Les crates de matérialisation utilisent le format `kb_materializer_`. +- Le crate de journalisation s'appelle `kb_logging`. + +## Règles Tauri et TypeScript + +- Toute structure ou énumération Rust exposée au frontend TypeScript doit importer le trait `TS` avec `use ts_rs::TS;` puis dériver `TS`. +- Les types Rust exportés vers TypeScript doivent utiliser des noms stables et explicites. +- Les bindings générés doivent être produits dans un dossier dédié, généralement `../frontend/ts/bindings` ou `#[ts(export, export_to = "../frontend/ts/bindings/MyStruct.ts")]`. +- Les types purement internes au backend ne doivent pas être exportés vers TypeScript par défaut. +- Un payload de commande ou d’événement Tauri traverse une frontière JSON et ne doit jamais exiger un `bigint` JavaScript. Pour un entier Rust borné et représentable par l’UI, utiliser un override TS-rs `number`; si l’exactitude au-delà de `Number.MAX_SAFE_INTEGER` est nécessaire, sérialiser explicitement une chaîne décimale côté Rust et exporter `string`. Ne jamais construire un `BigInt` dans un objet passé à `invoke` ou `emit`. +- Corriger le type Rust/TS-rs source puis régénérer les bindings ; ne pas considérer une modification manuelle isolée d’un fichier généré comme un correctif durable. + +## Règles d'architecture + +- Les décodeurs ne dépendent pas de PostgreSQL, SQLite, RPC, Tauri, wallet, stratégie ou matérialisateurs. +- Les matérialisateurs ne dépendent pas du RPC, du wallet, de Tauri ou des décodeurs spécifiques. +- Pour chaque programme Solana, quelle que soit sa famille, un décodeur doit couvrir maximalement tout wire officiellement identifiable de sa surface, qu'il soit historique, courant, récent, expérimental ou publié avant son déploiement généralisé. Ces statuts doivent rester explicites et ne constituent pas un motif pour supprimer le décodage. +- Une observation lisible issue d'une transaction échouée peut conserver une intention non commitée ; elle ne doit jamais être présentée comme une mutation réussie. +- Pour chaque programme Solana, les matérialisateurs doivent projeter maximalement tous les faits métier stables et prouvés par le décodage, y compris les états historiques, obsolètes, dépréciés ou seulement rencontrables pendant un backfill. Le statut historique interdit l'exécution, mais ne constitue jamais à lui seul un motif de non-matérialisation. +- Toute projection historique doit conserver explicitement son lifecycle, sa version de layout, sa provenance, son slot ou ordre d'observation lorsqu'ils sont connus, et ne doit jamais écraser silencieusement un état actif plus récent. +- Une même réalité métier doit avoir une seule projection canonique. Les snapshots de comptes sont la source autoritative de l'état final lorsqu'ils sont disponibles ; les instructions et événements corrélés restent des observations de mutation et ne doivent pas produire un doublon concurrent du même état. +- Les matérialisateurs doivent refuser les transactions non commitées pour les mutations d'état, tout en pouvant conserver séparément une intention non commitée lorsque le modèle métier le prévoit explicitement. +- Toute absence volontaire de projection doit être documentée avec une justification technique précise. Un matérialisateur ne doit inventer ni état final, ni montant, ni autorité, ni frais, ni agrégation que l'observation ne prouve pas. +- Pour chaque programme Solana, les exécuteurs doivent couvrir maximalement les opérations officiellement appelables et les opérations expérimentales publiées lorsque leurs contrats exacts et garde-fous sont prouvés. Les opérations historiques, dépréciées ou obsolètes doivent rester décodables et matérialisables, mais ne doivent jamais être exposées à l'exécution ; cette exclusion doit être explicite dans la matrice et le README. +- Les stores exposent leurs comportements via les traits de `kb_store_core`. +- `kb_store_pg` est le store de production. +- `kb_store_sqlite` sert aux tests, imports et corpus locaux. +- `kb_wallet` reste isolé du reste du système. +- Les transactions raw sont immuables et restent la source d'audit. +- Les replays doivent être ciblés par module, version, programme, surface, discriminator, slot ou signatures. +- La documentation du projet ne doit pas référencer le nom de l'ancien workspace. + +## Règles de documentation projet + +- `README.md` décrit le projet, son rôle, ses objectifs et son organisation. +- `ROADMAP.md` contient les futures étapes, versions et changements prévus. +- `ROADMAP.md` conserve une structure documentaire normale par phases, cases cochées et journal de préversions ; les étiquettes conversationnelles de priorité ne doivent pas y être ajoutées. +- Les réponses de livraison doivent reprendre la liste exhaustive des tâches avec les rubriques conversationnelles **Validé**, **En cours**, **Next** et **Planifié**, sans réduire le suivi à un résumé ambigu. +- `CHANGELOG.md` n'est modifié qu'après validation d'une version, avec un paragraphe ajouté à la fin décrivant ce qui a été fait ou ajouté. +- Chaque crate Rust doit avoir un `README.md` ou `001.README.md` expliquant son rôle dans l'écosystème. +- Le README d'une crate opérationnelle doit inventorier ses types, traits, constantes et fonctions publics utiles, expliquer leurs paramètres, résultats, effets et frontières, puis fournir au moins un exemple d'utilisation lorsque l'API est destinée à être appelée directement. +- Une API publique ajoutée ou modifiée n'est pas considérée comme documentée tant que le README de sa crate n'a pas été synchronisé ; les bindings générés seuls ne remplacent pas cette documentation. + +## Nomenclature métier + +- `protocol_code` désigne la famille : `pump`, `raydium`, `meteora`, `orca`, `jupiter`, etc. +- `surface_code` désigne la surface concrète : `pump_swap`, `raydium_amm_v4`, `meteora_dlmm`, etc. +- `event_code` suit le format `.`. +- Les familles d'événements principales sont : `trade`, `liquidity`, `lifecycle`, `fee`, `admin`, `reward`, `orderbook`, `token_account`, `pool_state`, `routing`, `risk`, `audit`, `unknown`. + +## Base de données + +- Les schémas PostgreSQL cibles sont : `raw`, `core`, `obs`, `decode`, `mat`, `catalog`, `agg`, `ops`, `wallet`. +- Les colonnes JSONB doivent se terminer par `_jsonb`. +- Les montants bruts on-chain doivent se terminer par `_raw`. +- La transaction canonique raw reste la source rejouable et ne doit pas être supprimée après traitement sans politique de rétention explicite. + +## Nommage canonique des surfaces + +Les nouvelles surfaces de programmes doivent utiliser un nom canonique basé sur la fonction réelle du programme : + +```text +__[_vN] +``` + +Les crates de décodage doivent utiliser : + +```text +kb_decoder___[_vN] +``` + +Exemples : + +- `kb_decoder_amm_raydium_cpmm` ; +- `kb_decoder_clmm_raydium` ; +- `kb_decoder_dlmm_meteora` ; +- `kb_decoder_router_jupiter_aggregator_v6` ; +- `kb_decoder_orderbook_openbook_v2` ; +- `kb_decoder_metadata_metaplex_token_metadata` ; +- `kb_decoder_nft_metaplex_bubblegum`. + +Les noms historiques ou issus d'IDL doivent être conservés dans le registre, mais ne doivent pas créer de nouvelles crates si une crate canonique existe déjà. + +Aucun renommage massif de crates n'est autorisé sans étape de contrôle dédiée et sans `cargo build` validé. + +## Règles de nommage des surfaces Solana + +- Le nom canonique d'une surface doit commencer par une fonction réelle : `amm`, `clmm`, `dlmm`, `router`, `orderbook`, `launchpad`, `lending`, `vault`, `staking`, `bridge`, `perpetuals`, `oracle`, `nft`, `metadata`, `admin`, etc. +- Le préfixe `program_` est interdit pour les nouveaux noms canoniques. +- Les surfaces non classifiées doivent utiliser `unknown_*` et ne doivent pas avoir de crate cible tant que leur fonction n'est pas validée. +- Les programmes Solana/SPL primitifs doivent rester séparés des surfaces DEX/router dans la documentation de contrôle. +- `damm` ne doit pas être utilisé comme préfixe fonctionnel ; utiliser `amm_meteora_damm_v1` ou `amm_meteora_damm_v2`. + +## Registre des identifiants core + +- Les identifiants Solana/SPL primitifs doivent être tenus à jour dans `registry/core_program_id_seed.toml`. +- Les sysvars et comptes natifs connus ne doivent pas être classés comme surfaces DEX/router. +- `spl_token`, `spl_token_2022` et `associated_token_account` restent dans leurs crates spécialisées. +- `stake_pool` doit être réservé à un futur crate spécialisé plutôt que mélangé avec le programme `stake`. + +## Index court de programme + +- Les noms de crates ne doivent pas commencer par un index hexadécimal. +- Un champ `registry_code` optionnel peut être ajouté au registre pour l'UI, PostgreSQL ou les matrices. +- Le nom canonique reste la source principale : fonction + famille + identifiant + version. + +## Comptes non exécutables + +- Une adresse de compte, de pool, de PDA ou de vault ne doit pas générer une crate de décodeur. +- Le vrai `program_id` propriétaire doit être prouvé avant création d'une surface canonique. + +## Constantes Rust + +- Les fichiers `program_ids.rs` sont interdits dans les nouveaux crates. Utiliser `constants.rs`. +- Les `program_id` publics doivent être réexportés depuis `lib.rs`. +- Dans une crate, les modules internes doivent appeler les constantes réexportées via `crate::CONSTANT_NAME`. +- Les discriminants, sélecteurs, longueurs Borsh et constantes internes futures doivent être placés dans `constants.rs` et rester `pub(crate)` sauf besoin d'API publique explicite. + +## Règles de tracing + +- Une crate est opérationnelle lorsqu’elle effectue des I/O, orchestre un pipeline, décode, matérialise, exécute, applique une politique runtime ou prend une décision mutable observable. +- Toute crate opérationnelle ajoutée ou modifiée doit dépendre de `tracing` depuis le workspace et déclarer exactement un `pub(crate) const TRACING_TARGET` dans `src/constants.rs`. +- La valeur canonique de `TRACING_TARGET` est le nom exact du package Cargo. Les targets historiques `khbot.*`, les suffixes de module et les targets de fenêtre sont interdits dans les nouvelles modifications. +- Les macros `tracing` doivent utiliser `target: crate::TRACING_TARGET` et des champs structurés stables. La granularité interne passe par `action`, `stage`, `window`, `campaign_id`, `signature`, `instruction_path`, `program_id`, `processor_name`, `processor_version`, `status` et `error_code`. +- Les crates passives de types, contrats, DTO, API sans exécution, registres ou constantes restent sans dépendance `tracing`. `kb_config` reste une exception de bootstrap tant que sa validation précède l’installation du subscriber. +- Il est interdit d’ajouter `tracing` sans événement réel ou de conserver un faux target uniquement consommé par `let _target`. +- Une décision interne doit être journalisée par la crate responsable ; `kb_app_demo` ne journalise que les frontières Tauri/UI, les actions utilisateur et les résumés d’orchestration. +- Tout input sélectionné sans décodeur compatible, tout résultat de décodage `failed` ou `unsupported`, tout résultat de matérialisation `failed`, toute validation de résultat invalide et toute erreur de persistance doivent émettre un événement `error` avant le retour ou la persistance terminale. +- Une transaction Solana échouée mais correctement décodée n’est pas une erreur du logiciel. Une décision `ignored`, un refus de matérialisation conforme à la politique ou une annulation coopérative ne doivent pas être promus artificiellement au niveau `error`. +- Les erreurs de décodage et de matérialisation doivent conserver au minimum, lorsque disponibles : `campaign_id`, `signature`, `slot`, `instruction_path`, `program_id`, processor ou materializer avec version, `input_key`, `input_hash` ou hash du payload, statut, code et diagnostic borné. +- Les payloads complets, DSN non masqués, secrets, clés privées et données non bornées sont interdits dans les logs. +- Chaque profil doit router les événements vers les sorties globales `debug.log`, `info.log`, `error.jsonl` et `app.log`, puis vers `debug.log`, `info.log` et `error.jsonl` dans un répertoire propre à chaque crate utilisant `tracing`. +- L’ajout ou la suppression de `tracing.workspace = true` dans une crate impose la mise à jour simultanée de la matrice de routes, de ses tests et de `docs/TRACING_CONTRACT.md`. +- Le contrat détaillé est défini dans `docs/TRACING_CONTRACT.md`. +- L’audit mécanique spécifique au projet est exécuté par `python3 scripts/audit_khadhroony_workspace_rules.py`. Il couvre notamment `TRACING_TARGET` et l’usage obligatoire de `solana_pubkey::Pubkey` à la place de `solana_address::Address`. + +## Règles de réutilisation des interfaces Solana et SPL + +- Les dépendances déclarées dans `[workspace.dependencies]` forment un catalogue de versions et de features autorisées ; elles ne doivent être ajoutées à une crate consommatrice que lorsqu’un type, un encodeur, un décodeur ou un identifiant officiel est réellement utilisé. + +- Dans un `Cargo.toml`, placer dans `[dependencies]` toute crate référencée par le code de bibliothèque compilé en production. Réserver `[dev-dependencies]` aux références contenues exclusivement dans `#[cfg(test)]`, les tests d’intégration, benches ou exemples. Une dépendance de test vers un décodeur ou matérialiseur concret est légitime lorsqu’elle sert uniquement à éprouver une orchestration générique fondée sur les traits API ; elle ne prouve pas qu’une capacité de production manque. Toute promotion de `dev-dependencies` vers `dependencies` doit être motivée par un appel runtime réel, et toute dépendance runtime inutilisée doit être supprimée. +- Les registres de composition runtime des applications doivent énumérer explicitement chaque décodeur et matérialiseur concret activé. Toute nouvelle surface instructionnelle dotée d’un `EventMaterializer` doit être ajoutée au registre applicatif et couverte par un test d’inventaire ordonné ; une crate présente dans le workspace ou dans `kb_pipeline` n’est pas activée automatiquement. Les matérialiseurs exclusivement stateful qui n’implémentent pas `EventMaterializer` restent routés par leurs APIs de snapshots dédiées. +- Les corrélations instruction/état doivent produire une issue explicite (`confirmed`, `contradicted` ou `not_applicable`) et ne doivent jamais transformer automatiquement une configuration observée en violation, score ou conclusion métier. +- Une interface officielle Solana ou SPL étroite doit être préférée à `solana-sdk` lorsque son contrat suffit. +- L’ordre de préférence des formats est : schéma officiel `wincode`, schéma officiel Borsh, puis parseur local borné reproduisant exactement le runtime lorsque l’interface officielle n’expose que `bincode`. +- Aucun nouveau code ne doit dépendre directement de `bincode`. Une interface officielle uniquement disponible derrière une feature `bincode` ne justifie pas l’activation de cette feature ; le layout doit alors être prouvé depuis les sources officielles et implémenté localement avec des bornes et des tests. +- Les exécuteurs doivent utiliser les builders officiels disponibles, préserver l’ordre exact des metas, borner toute liste de comptes variable avant l’appel au builder et refuser les doublons lorsque leur répétition n’a pas de sémantique publiée. +- Les features des interfaces doivent rester minimales et explicites. Une feature `serde`, `wincode`, `borsh`, `std`, `alloc` ou équivalente n’est activée que si la crate consommatrice l’utilise réellement. +- Les crates applicatives ne doivent pas dépendre d’interfaces RPC ou transactionnelles uniquement pour relayer des types ; ces dépendances appartiennent à `kb_rpc`, aux modèles communs justifiés ou à la crate opérationnelle propriétaire. +- Toute exception et toute implémentation locale doivent être documentées dans `docs/SOLANA_INTERFACE_DEPENDENCIES.md` avec la raison, la source officielle et la stratégie de test. + +## Règles des exécuteurs + +- Les crates d'exécution utilisent le préfixe `kb_executor_`. +- Une surface classifiée peut avoir un décodeur `kb_decoder_` et un exécuteur `kb_executor_`. +- Les exécuteurs ne doivent pas dépendre des décodeurs. +- Les exécuteurs doivent passer par `kb_execution_api` pour leur contrat public. +- Les garde-fous communs doivent être placés dans `kb_execution_safety`. +- Aucun exécuteur ne doit envoyer de transaction sans simulation et validation explicite. +- Les surfaces non classifiées ne doivent pas avoir de crate exécuteur. +- Pour chaque programme Solana et contrairement aux décodeurs, les exécuteurs ne construisent pas les opérations obsolètes ou purement historiques. Ils couvrent uniquement les opérations courantes et expérimentales officiellement constructibles, avec un statut exact `Supported` ou `Unsupported(reason)`. +- Le statut expérimental, récent ou non encore déployé partout n'interdit pas un builder universel lorsqu'un wire officiel exact existe. Le déploiement, la simulation et l'autorisation d'envoi restent trois décisions séparées ; la bibliothèque ne doit pas imposer artificiellement un cluster. + + +- Les champs JSON exposés à TypeScript ne doivent pas utiliser directement `serde_json::Value` avec `TS-rs`; utiliser une chaîne JSON sérialisée (`std::string::String`) ou un type Rust typé exportable. +- Les APIs qui gardent des payloads dynamiques doivent fournir des helpers explicites basés sur `serde_json::to_string` et `serde_json::to_string_pretty`. + +## Règles de configuration + +- La configuration applicative commune doit passer par `kb_config`. +- Les fichiers JSON de configuration ne doivent pas contenir de commentaires. +- Les secrets ne doivent pas être écrits en clair dans le dépôt. +- Les valeurs sensibles doivent utiliser des variables d'environnement ou un stockage chiffré dédié. +- Les structures de configuration exposées à Tauri doivent dériver `TS`. + +## Ordre de développement cible + +- `kb_logging` doit être stabilisé avant les logs avancés des autres crates. +- `kb_config` doit être stabilisé avant les stores, RPC, wallet, applications et workers. +- Les contrats SQL et de matérialisation doivent être définis avant les gros décodeurs DEX. +- Les implémentations détaillées des matérialisateurs doivent suivre les sorties réelles des décodeurs correspondants. +- `kb_app_demo` doit fournir des validations live après chaque capacité majeure, sans créer une application Tauri séparée par crate. + +## Règles de livraison ChatGPT + +- Après le squelette initial, ChatGPT doit fournir uniquement des zips delta sauf demande explicite de zip complet. +- Un delta qui modifie la racine du workspace ou plusieurs modules doit être nommé `khadhroony-bot2_vX.Y.Z-pre.abc-delta.zip`. +- Un delta qui ne modifie qu'un seul module Rust doit être nommé `kb_modulename_vX.Y.Z-pre.abc-delta.zip`. +- Lorsqu'un delta `pre.abc` a déjà été livré et qu'un correctif est nécessaire, le numéro `abc` ne doit pas être incrémenté. Le correctif doit être nommé `khadhroony-bot2_vX.Y.Z-pre.abc-delta-fix-001.zip`, puis `-fix-002.zip`, etc. Pour un seul module, utiliser la même règle avec le préfixe `kb_modulename`. +- Un correctif doit indiquer dans `delta.md` le delta de base et les correctifs antérieurs à appliquer. Il ne doit pas réutiliser silencieusement le nom ou l'empreinte d'une archive déjà livrée. +- Le numéro `pre.abc` suivant est réservé à une nouvelle tranche fonctionnelle, pas à la réparation d'une archive existante. +- Chaque zip delta ou correctif doit contenir un fichier `delta.md` non versionné à la racine du zip. +- `delta.md` doit lister les fichiers ajoutés, les fichiers modifiés, les fichiers à supprimer manuellement, les validations exécutées et les validations non exécutées. +- Le zip delta ne doit pas contenir de fichiers inchangés. +- Le zip delta ne doit pas modifier `CHANGELOG.md` sauf validation explicite d'une version. +- Les suppressions de fichiers ou dossiers doivent être indiquées dans `delta.md`, car l'extraction d'un zip ne supprime pas automatiquement les anciens fichiers. +- Les prompts de session doivent rappeler ce format de livraison et la règle `delta-fix-NNN`. + +## Constantes des décodeurs + +- Chaque crate `kb_decoder_*` classifié avec un `program_id` doit avoir un fichier `src/constants.rs`. +- `src/constants.rs` contient les `PROGRAM_ID`, puis les discriminators, sélecteurs, opcodes, constantes Borsh et constantes de décodage quand elles seront connues. +- `src/lib.rs` doit réexporter les constantes nécessaires avec `pub use crate::constants::...`. +- `src/decoder.rs` doit utiliser les constantes réexportées par la crate, par exemple `crate::AMM_PUMP_SWAP_PROGRAM_ID`, et non `crate::constants::AMM_PUMP_SWAP_PROGRAM_ID`. +- Les literals de `program_id` ne doivent pas rester dans `program_ids()`, sauf dans un fichier `constants.rs`. + +## Identifiants de programmes + +Les `program_id` connus doivent être définis une seule fois dans `kb_program_ids`. Les crates de décodeur, d’exécuteur, de store, d’application ou d’outil doivent référencer directement `kb_program_ids::XXX_PROGRAM_ID`. Les fichiers `constants.rs` locaux ne doivent pas redéfinir ces chaînes ; ils restent réservés aux constantes internes du module, par exemple discriminators, opcodes, seeds, index de comptes ou layouts. + +## Validation frontend Tauri + +- Pour `kb_app_demo`, ne pas lancer `npm --prefix kb_app_demo run build` séparément : la validation frontend de développement est réalisée par `cargo tauri dev -c kb_app_demo/tauri.conf.json`, qui démarre et pilote le serveur Vite. + +## Architecture khadhroony-bot3 + +- Les décodeurs, exécuteurs et matérialisateurs résident exclusivement dans `kb-lib`. +- Les modules peuvent posséder leur propre fichier de constantes. +- Toute API publique portée depuis une ancienne crate doit être réexportée au crate root de `kb-lib`. +- `kb-store` regroupe les contrats store-neutral et PostgreSQL. +- Seul `kb-app-demo` est conservé comme binaire pendant la migration initiale. diff --git a/RUST_RULES.md b/RUST_RULES.md new file mode 100644 index 0000000..c1262df --- /dev/null +++ b/RUST_RULES.md @@ -0,0 +1,134 @@ + + + +# Règles Rust générales + +Ce fichier contient les règles normatives applicables à tous les projets Rust. Elles sont indépendantes de `khadhroony-bot2` et doivent pouvoir être réutilisées telles quelles dans un autre workspace. + +## En-têtes et versions de fichiers + +- Tout fichier texte qui supporte des commentaires commence par une ligne indiquant son chemin relatif dans le projet, puis une ligne `version` entière. +- La version d'un fichier est incrémentée à chaque modification après validation de sa version précédente. +- Les fichiers Rust utilisent `// file: ...` et `// version: N`. +- Les fichiers Markdown utilisent `` et ``. +- Tous les fichiers texte se terminent par exactement une fin de ligne. + +## Langue et documentation + +- Les commentaires et rustdocs du code sont rédigés en anglais. +- Les documents Markdown du projet sont rédigés dans la langue documentaire choisie par le projet. +- Tout élément `pub` ou `pub(crate)` possède une rustdoc utile au point de déclaration. +- Toute réexportation `pub use` ou `pub(crate) use` dans `lib.rs` ou `main.rs` possède également sa propre rustdoc copiée ou reformulée de manière équivalente. La documentation du point d'entrée doit permettre de comprendre l'API sans ouvrir le module interne. + +## Édition et lints obligatoires + +- L'édition Rust cible est Rust 2024, sauf contrainte explicitement documentée. +- Chaque `lib.rs` et `main.rs` contient : + - `#![warn(missing_docs)]` ; + - `#![deny(unreachable_pub)]` ; + - `#![forbid(unsafe_code)]`. +- Les lints Clippy obligatoires sont déclarés au niveau workspace et hérités par toutes les crates. +- Les règles minimales sont : interdiction de `unwrap`, `expect`, `?`, retours implicites, code `unsafe`, APIs publiques inaccessibles et imports globaux non justifiés. +- Les tests peuvent disposer d'exceptions limitées pour `unwrap` et `expect` uniquement lorsqu'elles sont explicitement autorisées par la configuration Clippy. + +La configuration workspace doit au minimum déclarer : + +```toml +[workspace.lints.rust] +missing_docs = "warn" +unreachable_pub = "deny" +unsafe_code = "forbid" + +[workspace.lints.clippy] +unwrap_used = "deny" +expect_used = "deny" +implicit_return = "deny" +needless_return = "allow" +useless_vec = "deny" +question_mark = "deny" +question_mark_used = "deny" +needless_match = "allow" +manual_ok_err = "allow" +manual_unwrap_or = "allow" +manual_map = "allow" +match_like_matches_macro = "allow" +single_match = "allow" +manual_unwrap_or_default = "allow" +manual_find = "allow" +explicit_counter_loop = "allow" +get_first = "allow" +implicit_saturating_sub = "allow" +``` + +## Formatage + +- `cargo fmt --all` est exécuté après application de chaque delta ou correctif et avant les tests. +- Les fichiers Rust ne contiennent pas de lignes vides à l'intérieur d'une fonction, d'une structure, d'une énumération ou d'une implémentation courte. +- Les lignes vides séparent uniquement les fonctions, blocs `impl`, types et sections logiques. +- Les exports de `lib.rs` ou `main.rs` ne contiennent aucune ligne vide à l'intérieur d'une série homogène de `pub use` ou `pub(crate) use`. +- Les séries `pub use` et `pub(crate) use` forment deux groupes séparés lorsqu'elles coexistent. + +## Imports et chemins + +- `use` est interdit pour les constantes, fonctions, structures, énumérations, unions, alias de types, modules et macros. +- `use` est autorisé uniquement pour un trait lorsque la résolution de méthode, une macro de dérivation ou une contrainte de langage l'exige réellement. +- Un import de trait doit rester étroit et ne doit jamais importer simultanément des éléments non-traits par accolades. +- Les imports globaux, glob imports et imports groupés par accolades sont interdits. +- Pour un élément fourni par une crate externe au workspace, utiliser directement le chemin public le plus court exposé par cette crate. Exemple : utiliser `external_crate::Struct`, jamais `external_crate::module::Struct` si `external_crate::Struct` existe, et jamais `use external_crate::Struct`. +- Pour un élément `pub` déclaré dans le workspace : + - il est réexporté depuis le `lib.rs` ou `main.rs` de sa crate propriétaire ; + - depuis une autre crate, il est appelé via `owner_crate::Item` ; + - depuis sa propre crate, il est appelé via `crate::Item`, même depuis son module de déclaration. +- Pour un élément `pub(crate)` : + - il est réexporté depuis le `lib.rs` ou `main.rs` via `pub(crate) use` ; + - il est appelé via `crate::Item`, même depuis son module de déclaration. +- Un élément strictement privé à un module n'est pas réexporté et est appelé par son nom local uniquement. Il est interdit d'utiliser un chemin long comme `crate::module::helper` ou `owner_crate::module::helper` pour un helper privé du module courant. +- Dans un sous-module de tests, un élément privé du module parent est appelé via `super::Item`. Un élément `pub` ou `pub(crate)` continue d’être appelé via le point d’entrée de crate le plus court, par exemple `crate::Item`. +- Les exports ne sont jamais groupés avec des accolades. Un type, une fonction, une constante ou un alias correspond à une ligne de réexport distincte. + +## Visibilité et API de crate + +- Aucun `pub mod` n'est autorisé. Les modules restent privés et l'API est constituée par des réexports explicites. +- Un élément `pub` inaccessible depuis le point d'entrée de sa crate est une erreur de conception, pas un simple avertissement. +- Un élément `pub(crate)` utilisé hors de son module est réexporté au niveau du point d'entrée de la crate. +- Les chemins internes de modules ne font pas partie de l'API stable. +- Les méthodes inhérentes publiques restent appelées via le type réexporté ; les fonctions libres publiques sont appelées via le point d'entrée de crate. + +## Helpers et réutilisation + +- Un helper répété dans plusieurs modules d'une même crate est déplacé dans un module commun explicite, généralement `helper.rs` ou un module spécialisé plus précis. +- Le nom `helpers.rs` ou `helper.rs` n'est utilisé que si aucune responsabilité métier plus précise ne convient. +- Un helper général réutilisable par plusieurs crates est déplacé dans une crate commune appropriée et exposé publiquement. +- Les types d'erreur généraux, identifiants de programme, primitives de validation et fonctions de sérialisation communes ne doivent pas être dupliqués entre crates. +- La mutualisation ne doit pas créer de dépendance cyclique ni déplacer un comportement métier spécifique dans une crate générique. + +## Gestion des erreurs et contrôle de flux + +- `unwrap`, `expect` et `panic` sont interdits dans le code de production. +- L'opérateur `?` est interdit dans les chemins de production ; utiliser des `match` explicites avec erreurs contextualisées. +- `anyhow` et `thiserror` ne sont pas utilisés par défaut. +- Les erreurs publiques sont typées lorsque leur contrat est stable ; les diagnostics dynamiques restent bornés. +- Les retours sont explicites conformément au lint `clippy::implicit_return`. + +## Sécurité et dépendances + +- Le code `unsafe` est interdit. +- Une dépendance n'est ajoutée que si elle est réellement utilisée dans le chemin de compilation concerné. +- Une dépendance utilisée uniquement dans les tests reste dans `[dev-dependencies]`. +- Les features sont minimales et explicites. + +## Contrôle avant livraison + +Chaque livraison Rust exécute au minimum, dans cet ordre : + +```bash +cargo fmt --all +python3 scripts/audit_rust_general_rules.py +python3 scripts/audit_khadhroony_workspace_rules.py +cargo test --workspace +cargo clippy --workspace --all-targets +``` + +Un projet peut utiliser une sélection de tests plus étroite pendant le développement, mais la fermeture d'une version exige le contrôle global. + +Le wrapper `python3 scripts/audit_rust_workspace_rules.py` exécute les deux audits sans fusionner leurs responsabilités. diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 0000000..1f35ef0 --- /dev/null +++ b/clippy.toml @@ -0,0 +1,39 @@ +# file: clippy.toml +# version: 1 + +msrv = "1.85.0" + +# The project favors explicit control flow and visible intent. +# These settings complement the coding rules already enforced manually +# in code review: no `?`, no `unwrap`, no `expect`, explicit error paths. + +too-many-arguments-threshold = 16 +type-complexity-threshold = 250 +single-char-binding-names-threshold = 3 +trivial-copy-size-limit = 16 +pass-by-value-size-limit = 256 +stack-size-threshold = 512000 +vec-box-size-threshold = 4096 +max-fn-params-bools = 2 +max-include-file-size = 1048576 +cognitive-complexity-threshold = 25 +too-large-for-stack = 2048 +enum-variant-size-threshold = 200 +large-error-threshold = 128 +avoid-breaking-exported-api = true +allow-unwrap-in-tests = true +allow-expect-in-tests = true +allow-useless-vec-in-tests = true + +disallowed-macros = [] +disallowed-methods = [] +disallowed-names = ["foo", "bar", "baz", "tmp"] +disallowed-types = [] +allowed-idents-below-min-chars = [ + "id", + "tx", + "rx", + "ms", + "pcm", + "vad", +] diff --git a/config/README.md b/config/README.md new file mode 100644 index 0000000..6e80344 --- /dev/null +++ b/config/README.md @@ -0,0 +1,62 @@ + + + +# Configuration locale + +Ce dossier contient les exemples et le schéma de configuration JSON. + +## Règles + +- Le fichier versionné doit rester sans secret. +- Les placeholders de clés restent directement dans les URLs. +- Le champ `active_profile` sélectionne un seul profil actif. +- Un profil actif doit être présent et unique. +- Les profils non actifs peuvent rester dans le fichier pour les tests devnet, mainnet lecture seule, backfill ou future production. +- Le contrat courant couvre HTTP JSON-RPC Solana et WebSocket JSON-RPC générique. +- Helius `transactionSubscribe` et Yellowstone gRPC seront ajoutés plus tard dans `kb_rpc`, avec évolution explicite du schéma quand les transports seront réellement utilisés. +- Les IDLs restent des artefacts de développement et ne font pas partie de la configuration runtime. + +## Fichiers + +- `example.config.json` : exemple complet avec profils `local_devnet`, `mainnet_research` et `mainnet`. +- `schema.config.json` : JSON Schema de validation du fichier de configuration. + +## Phase `0.3.x` + +`0.3.x` utilise les endpoints HTTP des profils de recherche existants pour : + +```text +getSignaturesForAddress +getTransaction +getSignatureStatuses +``` + +Aucun profil Helius payant ou Yellowstone n’est activé à ce stade. + +Les sources futures devront déclarer séparément : + +- provider ; +- protocole ; +- rôle ; +- limites de streams et filtres ; +- authentification ; +- région éventuelle. + +## Évolution du schéma + +`schema.config.json` est le contrat de validation runtime. Il évolue seulement lorsqu’un champ est réellement utilisé par l’exécution. + +## Logging de développement + +L’exemple configure, pour chacun des trois profils, quatre fichiers globaux puis trois fichiers dédiés par crate opérationnelle utilisant `tracing` : `debug.log`, `info.log` et `error.jsonl`. + +`pre.020` rend `kb_materializer_admin` et `kb_materializer_compliance_audit` opérationnelles. `pre.021` active ensuite `kb_materializer_staking`. Elles rejoignent donc la console et disposent de leurs routes propres sous : + +```text +logs//kb_materializer_admin/ +logs//kb_materializer_compliance_audit/ +logs//kb_materializer_staking/ +``` + +Le test de configuration découvre dynamiquement toutes les crates déclarant `tracing.workspace = true` et vérifie leurs trois routes dans chaque profil. Les logs ne doivent contenir ni secret, ni DSN non masqué, ni payload Config ou bytecode complet. + diff --git a/config/example.config.json b/config/example.config.json new file mode 100644 index 0000000..267cae1 --- /dev/null +++ b/config/example.config.json @@ -0,0 +1,5211 @@ +{ + "active_profile": "mainnet_research", + "profiles": [ + { + "name": "local_devnet", + "app": { + "name": "khadhroony-bot2", + "environment": "development", + "auto_reconnect_default": false + }, + "logging": { + "default_level": "warn", + "targets": [ + { + "name": "console_local_devnet_compact", + "enabled": true, + "sink": "console", + "level": "debug", + "path": "", + "rotation": "none", + "format": "compact", + "ansi": true, + "targets": [ + "kb_app_demo", + "kb_decoder_metadata_metaplex_token_metadata", + "kb_decoder_solana_core", + "kb_decoder_spl_associated_token_account", + "kb_decoder_spl_elgamal_registry", + "kb_decoder_spl_memo", + "kb_decoder_spl_token", + "kb_decoder_spl_token_2022", + "kb_execution_solana", + "kb_executor_metadata_metaplex_token_metadata", + "kb_executor_metadata_spl_name_service", + "kb_executor_solana_core", + "kb_executor_spl_account_compression", + "kb_executor_spl_associated_token_account", + "kb_executor_spl_elgamal_registry", + "kb_executor_spl_memo", + "kb_executor_spl_noop", + "kb_executor_spl_single_pool", + "kb_executor_spl_token", + "kb_executor_spl_token_2022", + "kb_logging", + "kb_materializer_admin", + "kb_materializer_compliance_audit", + "kb_materializer_lifecycle", + "kb_materializer_risk", + "kb_materializer_staking", + "kb_materializer_token_accounts", + "kb_materializer_transaction_annotations", + "kb_pipeline", + "kb_rpc", + "kb_store_pg", + "kb_wallet" + ] + }, + { + "name": "file_local_devnet_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "*" + ] + }, + { + "name": "file_local_devnet_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "*" + ] + }, + { + "name": "file_local_devnet_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "*" + ] + }, + { + "name": "file_local_devnet_app", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/app.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_app_demo_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_app_demo/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_app_demo_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_app_demo/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_app_demo_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_app_demo/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_decoder_solana_core_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_decoder_solana_core/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_solana_core" + ] + }, + { + "name": "file_kb_decoder_solana_core_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_decoder_solana_core/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_solana_core" + ] + }, + { + "name": "file_kb_decoder_solana_core_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_decoder_solana_core/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_solana_core" + ] + }, + { + "name": "file_kb_decoder_spl_memo_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_decoder_spl_memo/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_memo" + ] + }, + { + "name": "file_kb_decoder_spl_memo_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_decoder_spl_memo/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_memo" + ] + }, + { + "name": "file_kb_decoder_spl_memo_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_decoder_spl_memo/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_memo" + ] + }, + { + "name": "file_kb_decoder_spl_associated_token_account_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_decoder_spl_associated_token_account/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_associated_token_account" + ] + }, + { + "name": "file_kb_decoder_spl_associated_token_account_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_decoder_spl_associated_token_account/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_associated_token_account" + ] + }, + { + "name": "file_kb_decoder_spl_associated_token_account_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_decoder_spl_associated_token_account/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_associated_token_account" + ] + }, + { + "name": "file_kb_decoder_spl_token_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_decoder_spl_token/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token" + ] + }, + { + "name": "file_kb_decoder_spl_token_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_decoder_spl_token/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token" + ] + }, + { + "name": "file_kb_decoder_spl_token_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_decoder_spl_token/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_token" + ] + }, + { + "name": "file_kb_executor_metadata_spl_name_service_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_metadata_spl_name_service/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_spl_name_service" + ] + }, + { + "name": "file_kb_executor_metadata_spl_name_service_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_metadata_spl_name_service/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_spl_name_service" + ] + }, + { + "name": "file_kb_executor_metadata_spl_name_service_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_metadata_spl_name_service/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_metadata_spl_name_service" + ] + }, + { + "name": "file_kb_executor_solana_core_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_solana_core/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_solana_core" + ] + }, + { + "name": "file_kb_executor_solana_core_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_solana_core/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_solana_core" + ] + }, + { + "name": "file_kb_executor_solana_core_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_solana_core/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_solana_core" + ] + }, + { + "name": "file_kb_execution_solana_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_execution_solana/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_execution_solana" + ] + }, + { + "name": "file_kb_execution_solana_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_execution_solana/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_execution_solana" + ] + }, + { + "name": "file_kb_execution_solana_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_execution_solana/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_execution_solana" + ] + }, + { + "name": "file_kb_executor_spl_account_compression_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_spl_account_compression/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_account_compression" + ] + }, + { + "name": "file_kb_executor_spl_account_compression_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_spl_account_compression/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_account_compression" + ] + }, + { + "name": "file_kb_executor_spl_account_compression_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_spl_account_compression/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_account_compression" + ] + }, + { + "name": "file_kb_executor_spl_memo_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_spl_memo/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_memo" + ] + }, + { + "name": "file_kb_executor_spl_memo_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_spl_memo/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_memo" + ] + }, + { + "name": "file_kb_executor_spl_memo_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_spl_memo/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_memo" + ] + }, + { + "name": "file_kb_executor_spl_associated_token_account_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_spl_associated_token_account/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_associated_token_account" + ] + }, + { + "name": "file_kb_executor_spl_associated_token_account_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_spl_associated_token_account/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_associated_token_account" + ] + }, + { + "name": "file_kb_executor_spl_associated_token_account_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_spl_associated_token_account/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_associated_token_account" + ] + }, + { + "name": "file_kb_executor_spl_token_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_spl_token/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token" + ] + }, + { + "name": "file_kb_executor_spl_token_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_spl_token/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token" + ] + }, + { + "name": "file_kb_executor_spl_token_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_spl_token/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_token" + ] + }, + { + "name": "file_kb_executor_spl_noop_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_spl_noop/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_noop" + ] + }, + { + "name": "file_kb_executor_spl_noop_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_spl_noop/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_noop" + ] + }, + { + "name": "file_kb_executor_spl_noop_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_spl_noop/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_noop" + ] + }, + { + "name": "file_kb_executor_spl_single_pool_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_spl_single_pool/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_single_pool" + ] + }, + { + "name": "file_kb_executor_spl_single_pool_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_spl_single_pool/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_single_pool" + ] + }, + { + "name": "file_kb_executor_spl_single_pool_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_spl_single_pool/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_single_pool" + ] + }, + { + "name": "file_kb_logging_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_logging/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_logging" + ] + }, + { + "name": "file_kb_logging_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_logging/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_logging" + ] + }, + { + "name": "file_kb_logging_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_logging/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_logging" + ] + }, + { + "name": "file_kb_materializer_admin_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_materializer_admin/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_admin" + ] + }, + { + "name": "file_kb_materializer_admin_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_materializer_admin/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_admin" + ] + }, + { + "name": "file_kb_materializer_admin_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_materializer_admin/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_admin" + ] + }, + { + "name": "file_kb_materializer_compliance_audit_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_materializer_compliance_audit/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_compliance_audit" + ] + }, + { + "name": "file_kb_materializer_compliance_audit_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_materializer_compliance_audit/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_compliance_audit" + ] + }, + { + "name": "file_kb_materializer_compliance_audit_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_materializer_compliance_audit/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_compliance_audit" + ] + }, + { + "name": "file_kb_materializer_lifecycle_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_materializer_lifecycle/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_lifecycle" + ] + }, + { + "name": "file_kb_materializer_lifecycle_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_materializer_lifecycle/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_lifecycle" + ] + }, + { + "name": "file_kb_materializer_lifecycle_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_materializer_lifecycle/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_lifecycle" + ] + }, + { + "name": "file_kb_materializer_transaction_annotations_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_materializer_transaction_annotations/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_transaction_annotations" + ] + }, + { + "name": "file_kb_materializer_transaction_annotations_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_materializer_transaction_annotations/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_transaction_annotations" + ] + }, + { + "name": "file_kb_materializer_transaction_annotations_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_materializer_transaction_annotations/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_transaction_annotations" + ] + }, + { + "name": "file_kb_materializer_staking_debug", + "enabled": true, + "level": "debug", + "format": "human", + "path": "logs/devnet/kb_materializer_staking/debug.log", + "targets": [ + "kb_materializer_staking" + ], + "sink": "file", + "rotation": "daily", + "ansi": false + }, + { + "name": "file_kb_materializer_staking_info", + "enabled": true, + "level": "info", + "format": "human", + "path": "logs/devnet/kb_materializer_staking/info.log", + "targets": [ + "kb_materializer_staking" + ], + "sink": "file", + "rotation": "daily", + "ansi": false + }, + { + "name": "file_kb_materializer_staking_error", + "enabled": true, + "level": "error", + "format": "json", + "path": "logs/devnet/kb_materializer_staking/error.jsonl", + "targets": [ + "kb_materializer_staking" + ], + "sink": "file", + "rotation": "daily", + "ansi": false + }, + { + "name": "file_kb_pipeline_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_pipeline/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_pipeline" + ] + }, + { + "name": "file_kb_pipeline_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_pipeline/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_pipeline" + ] + }, + { + "name": "file_kb_pipeline_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_pipeline/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_pipeline" + ] + }, + { + "name": "file_kb_rpc_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_rpc/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_rpc" + ] + }, + { + "name": "file_kb_rpc_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_rpc/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_rpc" + ] + }, + { + "name": "file_kb_rpc_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_rpc/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_rpc" + ] + }, + { + "name": "file_kb_store_pg_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_store_pg/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_store_pg" + ] + }, + { + "name": "file_kb_store_pg_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_store_pg/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_store_pg" + ] + }, + { + "name": "file_kb_store_pg_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_store_pg/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_store_pg" + ] + }, + { + "name": "file_kb_wallet_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_wallet/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_wallet" + ] + }, + { + "name": "file_kb_wallet_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_wallet/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_wallet" + ] + }, + { + "name": "file_kb_wallet_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_wallet/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_wallet" + ] + }, + { + "name": "file_kb_materializer_token_accounts_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_materializer_token_accounts/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_token_accounts" + ] + }, + { + "name": "file_kb_materializer_token_accounts_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_materializer_token_accounts/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_token_accounts" + ] + }, + { + "name": "file_kb_materializer_token_accounts_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_materializer_token_accounts/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_token_accounts" + ] + }, + { + "name": "file_kb_materializer_risk_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_materializer_risk/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_risk" + ] + }, + { + "name": "file_kb_materializer_risk_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_materializer_risk/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_risk" + ] + }, + { + "name": "file_kb_materializer_risk_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_materializer_risk/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_risk" + ] + }, + { + "name": "file_kb_decoder_spl_token_2022_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_decoder_spl_token_2022/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token_2022" + ] + }, + { + "name": "file_kb_decoder_spl_token_2022_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_decoder_spl_token_2022/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token_2022" + ] + }, + { + "name": "file_kb_decoder_spl_token_2022_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_decoder_spl_token_2022/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_token_2022" + ] + }, + { + "name": "file_kb_decoder_metadata_metaplex_token_metadata_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_decoder_metadata_metaplex_token_metadata/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_decoder_metadata_metaplex_token_metadata_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_decoder_metadata_metaplex_token_metadata/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_decoder_metadata_metaplex_token_metadata_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_decoder_metadata_metaplex_token_metadata/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_decoder_spl_elgamal_registry_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_decoder_spl_elgamal_registry/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_decoder_spl_elgamal_registry_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_decoder_spl_elgamal_registry/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_decoder_spl_elgamal_registry_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_decoder_spl_elgamal_registry/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_metadata_metaplex_token_metadata_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_metadata_metaplex_token_metadata/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_executor_metadata_metaplex_token_metadata_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_metadata_metaplex_token_metadata/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_executor_metadata_metaplex_token_metadata_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_metadata_metaplex_token_metadata/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_executor_spl_elgamal_registry_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_spl_elgamal_registry/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_spl_elgamal_registry_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_spl_elgamal_registry/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_spl_elgamal_registry_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_spl_elgamal_registry/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_spl_token_2022_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_spl_token_2022/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token_2022" + ] + }, + { + "name": "file_kb_executor_spl_token_2022_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_spl_token_2022/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token_2022" + ] + }, + { + "name": "file_kb_executor_spl_token_2022_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_spl_token_2022/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_token_2022" + ] + } + ], + "target_filters": [ + { + "target": "hyper", + "level": "warn" + }, + { + "target": "reqwest", + "level": "warn" + }, + { + "target": "tao", + "level": "warn" + }, + { + "target": "wry", + "level": "warn" + } + ] + }, + "database": { + "enabled": true, + "backend": "postgres", + "postgres": { + "url": "postgres://solana:solana@localhost:5432/solana", + "max_connections": 8, + "connect_timeout_ms": 5000, + "auto_initialize_schema": true + }, + "sqlite": { + "path": "data/local.sqlite", + "create_if_missing": true, + "busy_timeout_ms": 5000, + "max_connections": 1, + "auto_initialize_schema": true, + "use_wal": true + } + }, + "data": { + "wallets_directory": "wallets", + "logs_directory": "logs" + }, + "solana": { + "http_endpoints": [ + { + "name": "devnet_public_http", + "enabled": true, + "provider": "solana-public", + "cluster": "devnet", + "url": "https://api.devnet.solana.com", + "connect_timeout_ms": 4000, + "request_timeout_ms": 10000, + "max_idle_connections_per_host": 4, + "roles": [ + { + "role": "http_queries", + "enabled": true, + "request_kinds": [ + "get_account_info", + "get_balance", + "get_block_commitment", + "get_block_height", + "get_block_production", + "get_blocks", + "get_blocks_with_limit", + "get_cluster_nodes", + "get_epoch_info", + "get_epoch_schedule", + "get_fee_for_message", + "get_first_available_block", + "get_genesis_hash", + "get_health", + "get_highest_snapshot_slot", + "get_identity", + "get_inflation_governor", + "get_inflation_rate", + "get_inflation_reward", + "get_largest_accounts", + "get_latest_blockhash", + "get_leader_schedule", + "get_max_retransmit_slot", + "get_max_shred_insert_slot", + "get_minimum_balance_for_rent_exemption", + "get_multiple_accounts", + "get_recent_performance_samples", + "get_recent_prioritization_fees", + "get_signature_statuses", + "get_slot", + "get_slot_leader", + "get_slot_leaders", + "get_stake_activation", + "get_stake_minimum_delegation", + "get_supply", + "get_token_account_balance", + "get_token_accounts_by_delegate", + "get_token_accounts_by_owner", + "get_token_largest_accounts", + "get_token_supply", + "get_transaction", + "get_transaction_count", + "get_version", + "get_vote_accounts", + "is_blockhash_valid", + "request_airdrop" + ], + "priority": 10, + "requests_per_second": 8, + "burst_capacity": 16, + "max_concurrent_requests": 8, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 1500 + }, + { + "role": "http_transactions", + "enabled": true, + "request_kinds": [ + "simulate_transaction", + "send_transaction", + "get_signature_statuses" + ], + "priority": 20, + "requests_per_second": 2, + "burst_capacity": 2, + "max_concurrent_requests": 2, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 3000 + } + ] + } + ], + "ws_endpoints": [ + { + "name": "devnet_public_ws", + "enabled": true, + "provider": "solana-public", + "cluster": "devnet", + "url": "wss://api.devnet.solana.com", + "connect_timeout_ms": 5000, + "request_timeout_ms": 10000, + "unsubscribe_timeout_ms": 1500, + "write_channel_capacity": 256, + "event_channel_capacity": 1024, + "auto_reconnect": false, + "roles": [ + { + "role": "slot_notifications", + "enabled": true, + "request_kinds": [ + "slot_subscribe", + "slots_updates_subscribe" + ], + "priority": 10, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 16, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "program_logs", + "enabled": true, + "request_kinds": [ + "logs_subscribe_mentions", + "program_subscribe", + "account_subscribe", + "signature_subscribe", + "block_subscribe", + "vote_subscribe" + ], + "priority": 20, + "requests_per_second": 2, + "burst_capacity": 4, + "max_concurrent_requests": 2, + "max_subscriptions": 64, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "root_notifications", + "enabled": true, + "request_kinds": [ + "root_subscribe" + ], + "priority": 15, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 16, + "pause_after_rate_limit_ms": 3000 + } + ] + } + ], + "listeners": { + "enabled": true, + "default_commitment": "confirmed", + "log_listeners": [ + { + "name": "pump_swap_logs", + "enabled": false, + "endpoint_role": "program_logs", + "program_id": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA", + "purpose": "Détecter les swaps, créations de pool et changements de liquidité Pump Swap." + } + ], + "program_listeners": [ + { + "name": "pump_fun_program_accounts", + "enabled": false, + "endpoint_role": "program_logs", + "program_id": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", + "purpose": "Détecter les changements de comptes utiles à la création ou migration de tokens Pump Fun." + } + ], + "account_listeners": [] + } + }, + "wallet": { + "wallet_dir": "wallets/temporary/local_devnet", + "cluster": "devnet", + "temporary_wallet_enabled": true, + "temporary_wallet_alias": "local-devnet-operator", + "temporary_wallet_persist": true, + "localnet_send_enabled": false, + "devnet_send_enabled": true, + "testnet_send_enabled": false, + "mainnet_send_enabled": false + }, + "execution": { + "dry_run_default": true, + "require_simulation": true, + "require_operator_confirmation": true, + "localnet_max_spend_lamports": 0, + "devnet_max_spend_lamports": 100000000, + "testnet_max_spend_lamports": 0, + "mainnet_max_spend_lamports": 0, + "max_fee_lamports": 1000000, + "max_compute_unit_price_micro_lamports": 100000, + "recent_blockhash_max_age_slots": 150, + "send_max_retries": 3, + "confirmation_poll_interval_ms": 500, + "confirmation_max_attempts": 120, + "devnet_airdrop_max_lamports": 2000000000 + }, + "demo": { + "live_demo_enabled": true, + "trading_demo_enabled": false + } + }, + { + "name": "mainnet_research", + "app": { + "name": "khadhroony-bot2", + "environment": "research", + "auto_reconnect_default": false + }, + "logging": { + "default_level": "warn", + "targets": [ + { + "name": "console_mainnet_research_compact", + "enabled": true, + "sink": "console", + "level": "debug", + "path": "", + "rotation": "none", + "format": "compact", + "ansi": true, + "targets": [ + "kb_app_demo", + "kb_decoder_metadata_metaplex_token_metadata", + "kb_decoder_solana_core", + "kb_decoder_spl_associated_token_account", + "kb_decoder_spl_elgamal_registry", + "kb_decoder_spl_memo", + "kb_decoder_spl_token", + "kb_decoder_spl_token_2022", + "kb_execution_solana", + "kb_executor_metadata_metaplex_token_metadata", + "kb_executor_metadata_spl_name_service", + "kb_executor_solana_core", + "kb_executor_spl_account_compression", + "kb_executor_spl_associated_token_account", + "kb_executor_spl_elgamal_registry", + "kb_executor_spl_memo", + "kb_executor_spl_noop", + "kb_executor_spl_single_pool", + "kb_executor_spl_token", + "kb_executor_spl_token_2022", + "kb_logging", + "kb_materializer_admin", + "kb_materializer_compliance_audit", + "kb_materializer_lifecycle", + "kb_materializer_risk", + "kb_materializer_staking", + "kb_materializer_token_accounts", + "kb_materializer_transaction_annotations", + "kb_pipeline", + "kb_rpc", + "kb_store_pg", + "kb_wallet" + ] + }, + { + "name": "file_mainnet_research_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "*" + ] + }, + { + "name": "file_mainnet_research_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "*" + ] + }, + { + "name": "file_mainnet_research_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "*" + ] + }, + { + "name": "file_mainnet_research_app", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/app.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_app_demo_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_app_demo/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_app_demo_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_app_demo/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_app_demo_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_app_demo/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_decoder_solana_core_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_decoder_solana_core/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_solana_core" + ] + }, + { + "name": "file_kb_decoder_solana_core_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_decoder_solana_core/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_solana_core" + ] + }, + { + "name": "file_kb_decoder_solana_core_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_decoder_solana_core/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_solana_core" + ] + }, + { + "name": "file_kb_decoder_spl_memo_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_decoder_spl_memo/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_memo" + ] + }, + { + "name": "file_kb_decoder_spl_memo_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_decoder_spl_memo/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_memo" + ] + }, + { + "name": "file_kb_decoder_spl_memo_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_decoder_spl_memo/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_memo" + ] + }, + { + "name": "file_kb_decoder_spl_associated_token_account_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_decoder_spl_associated_token_account/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_associated_token_account" + ] + }, + { + "name": "file_kb_decoder_spl_associated_token_account_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_decoder_spl_associated_token_account/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_associated_token_account" + ] + }, + { + "name": "file_kb_decoder_spl_associated_token_account_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_decoder_spl_associated_token_account/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_associated_token_account" + ] + }, + { + "name": "file_kb_decoder_spl_token_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_decoder_spl_token/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token" + ] + }, + { + "name": "file_kb_decoder_spl_token_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_decoder_spl_token/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token" + ] + }, + { + "name": "file_kb_decoder_spl_token_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_decoder_spl_token/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_token" + ] + }, + { + "name": "file_kb_executor_metadata_spl_name_service_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_metadata_spl_name_service/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_spl_name_service" + ] + }, + { + "name": "file_kb_executor_metadata_spl_name_service_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_metadata_spl_name_service/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_spl_name_service" + ] + }, + { + "name": "file_kb_executor_metadata_spl_name_service_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_metadata_spl_name_service/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_metadata_spl_name_service" + ] + }, + { + "name": "file_kb_executor_solana_core_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_solana_core/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_solana_core" + ] + }, + { + "name": "file_kb_executor_solana_core_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_solana_core/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_solana_core" + ] + }, + { + "name": "file_kb_executor_solana_core_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_solana_core/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_solana_core" + ] + }, + { + "name": "file_kb_execution_solana_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_execution_solana/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_execution_solana" + ] + }, + { + "name": "file_kb_execution_solana_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_execution_solana/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_execution_solana" + ] + }, + { + "name": "file_kb_execution_solana_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_execution_solana/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_execution_solana" + ] + }, + { + "name": "file_kb_executor_spl_account_compression_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_spl_account_compression/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_account_compression" + ] + }, + { + "name": "file_kb_executor_spl_account_compression_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_spl_account_compression/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_account_compression" + ] + }, + { + "name": "file_kb_executor_spl_account_compression_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_spl_account_compression/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_account_compression" + ] + }, + { + "name": "file_kb_executor_spl_memo_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_spl_memo/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_memo" + ] + }, + { + "name": "file_kb_executor_spl_memo_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_spl_memo/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_memo" + ] + }, + { + "name": "file_kb_executor_spl_memo_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_spl_memo/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_memo" + ] + }, + { + "name": "file_kb_executor_spl_associated_token_account_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_spl_associated_token_account/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_associated_token_account" + ] + }, + { + "name": "file_kb_executor_spl_associated_token_account_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_spl_associated_token_account/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_associated_token_account" + ] + }, + { + "name": "file_kb_executor_spl_associated_token_account_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_spl_associated_token_account/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_associated_token_account" + ] + }, + { + "name": "file_kb_executor_spl_token_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_spl_token/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token" + ] + }, + { + "name": "file_kb_executor_spl_token_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_spl_token/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token" + ] + }, + { + "name": "file_kb_executor_spl_token_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_spl_token/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_token" + ] + }, + { + "name": "file_kb_executor_spl_noop_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_spl_noop/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_noop" + ] + }, + { + "name": "file_kb_executor_spl_noop_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_spl_noop/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_noop" + ] + }, + { + "name": "file_kb_executor_spl_noop_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_spl_noop/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_noop" + ] + }, + { + "name": "file_kb_executor_spl_single_pool_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_spl_single_pool/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_single_pool" + ] + }, + { + "name": "file_kb_executor_spl_single_pool_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_spl_single_pool/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_single_pool" + ] + }, + { + "name": "file_kb_executor_spl_single_pool_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_spl_single_pool/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_single_pool" + ] + }, + { + "name": "file_kb_logging_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_logging/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_logging" + ] + }, + { + "name": "file_kb_logging_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_logging/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_logging" + ] + }, + { + "name": "file_kb_logging_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_logging/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_logging" + ] + }, + { + "name": "file_kb_materializer_admin_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_materializer_admin/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_admin" + ] + }, + { + "name": "file_kb_materializer_admin_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_materializer_admin/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_admin" + ] + }, + { + "name": "file_kb_materializer_admin_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_materializer_admin/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_admin" + ] + }, + { + "name": "file_kb_materializer_compliance_audit_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_materializer_compliance_audit/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_compliance_audit" + ] + }, + { + "name": "file_kb_materializer_compliance_audit_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_materializer_compliance_audit/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_compliance_audit" + ] + }, + { + "name": "file_kb_materializer_compliance_audit_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_materializer_compliance_audit/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_compliance_audit" + ] + }, + { + "name": "file_kb_materializer_lifecycle_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_materializer_lifecycle/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_lifecycle" + ] + }, + { + "name": "file_kb_materializer_lifecycle_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_materializer_lifecycle/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_lifecycle" + ] + }, + { + "name": "file_kb_materializer_lifecycle_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_materializer_lifecycle/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_lifecycle" + ] + }, + { + "name": "file_kb_materializer_transaction_annotations_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_materializer_transaction_annotations/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_transaction_annotations" + ] + }, + { + "name": "file_kb_materializer_transaction_annotations_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_materializer_transaction_annotations/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_transaction_annotations" + ] + }, + { + "name": "file_kb_materializer_transaction_annotations_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_materializer_transaction_annotations/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_transaction_annotations" + ] + }, + { + "name": "file_kb_materializer_staking_debug", + "enabled": true, + "level": "debug", + "format": "human", + "path": "logs/mainnet_research/kb_materializer_staking/debug.log", + "targets": [ + "kb_materializer_staking" + ], + "sink": "file", + "rotation": "daily", + "ansi": false + }, + { + "name": "file_kb_materializer_staking_info", + "enabled": true, + "level": "info", + "format": "human", + "path": "logs/mainnet_research/kb_materializer_staking/info.log", + "targets": [ + "kb_materializer_staking" + ], + "sink": "file", + "rotation": "daily", + "ansi": false + }, + { + "name": "file_kb_materializer_staking_error", + "enabled": true, + "level": "error", + "format": "json", + "path": "logs/mainnet_research/kb_materializer_staking/error.jsonl", + "targets": [ + "kb_materializer_staking" + ], + "sink": "file", + "rotation": "daily", + "ansi": false + }, + { + "name": "file_kb_pipeline_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_pipeline/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_pipeline" + ] + }, + { + "name": "file_kb_pipeline_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_pipeline/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_pipeline" + ] + }, + { + "name": "file_kb_pipeline_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_pipeline/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_pipeline" + ] + }, + { + "name": "file_kb_rpc_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_rpc/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_rpc" + ] + }, + { + "name": "file_kb_rpc_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_rpc/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_rpc" + ] + }, + { + "name": "file_kb_rpc_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_rpc/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_rpc" + ] + }, + { + "name": "file_kb_store_pg_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_store_pg/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_store_pg" + ] + }, + { + "name": "file_kb_store_pg_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_store_pg/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_store_pg" + ] + }, + { + "name": "file_kb_store_pg_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_store_pg/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_store_pg" + ] + }, + { + "name": "file_kb_wallet_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_wallet/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_wallet" + ] + }, + { + "name": "file_kb_wallet_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_wallet/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_wallet" + ] + }, + { + "name": "file_kb_wallet_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_wallet/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_wallet" + ] + }, + { + "name": "file_kb_materializer_token_accounts_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_materializer_token_accounts/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_token_accounts" + ] + }, + { + "name": "file_kb_materializer_token_accounts_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_materializer_token_accounts/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_token_accounts" + ] + }, + { + "name": "file_kb_materializer_token_accounts_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_materializer_token_accounts/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_token_accounts" + ] + }, + { + "name": "file_kb_materializer_risk_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_materializer_risk/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_risk" + ] + }, + { + "name": "file_kb_materializer_risk_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_materializer_risk/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_risk" + ] + }, + { + "name": "file_kb_materializer_risk_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_materializer_risk/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_risk" + ] + }, + { + "name": "file_kb_decoder_spl_token_2022_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_decoder_spl_token_2022/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token_2022" + ] + }, + { + "name": "file_kb_decoder_spl_token_2022_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_decoder_spl_token_2022/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token_2022" + ] + }, + { + "name": "file_kb_decoder_spl_token_2022_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_decoder_spl_token_2022/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_token_2022" + ] + }, + { + "name": "file_kb_decoder_metadata_metaplex_token_metadata_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_decoder_metadata_metaplex_token_metadata/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_decoder_metadata_metaplex_token_metadata_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_decoder_metadata_metaplex_token_metadata/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_decoder_metadata_metaplex_token_metadata_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_decoder_metadata_metaplex_token_metadata/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_decoder_spl_elgamal_registry_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_decoder_spl_elgamal_registry/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_decoder_spl_elgamal_registry_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_decoder_spl_elgamal_registry/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_decoder_spl_elgamal_registry_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_decoder_spl_elgamal_registry/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_metadata_metaplex_token_metadata_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_metadata_metaplex_token_metadata/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_executor_metadata_metaplex_token_metadata_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_metadata_metaplex_token_metadata/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_executor_metadata_metaplex_token_metadata_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_metadata_metaplex_token_metadata/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_executor_spl_elgamal_registry_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_spl_elgamal_registry/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_spl_elgamal_registry_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_spl_elgamal_registry/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_spl_elgamal_registry_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_spl_elgamal_registry/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_spl_token_2022_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_spl_token_2022/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token_2022" + ] + }, + { + "name": "file_kb_executor_spl_token_2022_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_spl_token_2022/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token_2022" + ] + }, + { + "name": "file_kb_executor_spl_token_2022_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_spl_token_2022/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_token_2022" + ] + } + ], + "target_filters": [ + { + "target": "hyper", + "level": "warn" + }, + { + "target": "reqwest", + "level": "warn" + }, + { + "target": "tao", + "level": "warn" + }, + { + "target": "wry", + "level": "warn" + } + ] + }, + "database": { + "enabled": true, + "backend": "postgres", + "postgres": { + "url": "postgres://solana:solana@localhost:5432/solana", + "max_connections": 16, + "connect_timeout_ms": 5000, + "auto_initialize_schema": true + }, + "sqlite": { + "path": "data/mainnet-import.sqlite", + "create_if_missing": true, + "busy_timeout_ms": 5000, + "max_connections": 1, + "auto_initialize_schema": true, + "use_wal": true + } + }, + "data": { + "wallets_directory": "wallets", + "logs_directory": "logs" + }, + "solana": { + "http_endpoints": [ + { + "name": "mainnet_public_http", + "enabled": true, + "provider": "solana-public", + "cluster": "mainnet-beta", + "url": "https://api.mainnet.solana.com", + "connect_timeout_ms": 4000, + "request_timeout_ms": 10000, + "max_idle_connections_per_host": 4, + "roles": [ + { + "role": "http_queries", + "enabled": true, + "request_kinds": [ + "get_account_info", + "get_balance", + "get_block_commitment", + "get_block_height", + "get_block_production", + "get_blocks", + "get_blocks_with_limit", + "get_cluster_nodes", + "get_epoch_info", + "get_epoch_schedule", + "get_fee_for_message", + "get_first_available_block", + "get_genesis_hash", + "get_health", + "get_highest_snapshot_slot", + "get_identity", + "get_inflation_governor", + "get_inflation_rate", + "get_inflation_reward", + "get_largest_accounts", + "get_latest_blockhash", + "get_leader_schedule", + "get_max_retransmit_slot", + "get_max_shred_insert_slot", + "get_minimum_balance_for_rent_exemption", + "get_multiple_accounts", + "get_recent_performance_samples", + "get_recent_prioritization_fees", + "get_signature_statuses", + "get_slot", + "get_slot_leader", + "get_slot_leaders", + "get_stake_activation", + "get_stake_minimum_delegation", + "get_supply", + "get_token_account_balance", + "get_token_accounts_by_delegate", + "get_token_accounts_by_owner", + "get_token_largest_accounts", + "get_token_supply", + "get_transaction_count", + "get_version", + "get_vote_accounts", + "is_blockhash_valid", + "request_airdrop" + ], + "priority": 50, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "http_fallback", + "enabled": true, + "request_kinds": [ + "get_account_info", + "get_balance", + "get_block_commitment", + "get_block_height", + "get_block_production", + "get_blocks", + "get_blocks_with_limit", + "get_cluster_nodes", + "get_epoch_info", + "get_epoch_schedule", + "get_fee_for_message", + "get_first_available_block", + "get_genesis_hash", + "get_health", + "get_highest_snapshot_slot", + "get_identity", + "get_inflation_governor", + "get_inflation_rate", + "get_inflation_reward", + "get_largest_accounts", + "get_latest_blockhash", + "get_leader_schedule", + "get_max_retransmit_slot", + "get_max_shred_insert_slot", + "get_minimum_balance_for_rent_exemption", + "get_multiple_accounts", + "get_recent_performance_samples", + "get_recent_prioritization_fees", + "get_signature_statuses", + "get_slot", + "get_slot_leader", + "get_slot_leaders", + "get_stake_activation", + "get_stake_minimum_delegation", + "get_supply", + "get_token_account_balance", + "get_token_accounts_by_delegate", + "get_token_accounts_by_owner", + "get_token_largest_accounts", + "get_token_supply", + "get_transaction_count", + "get_version", + "get_vote_accounts", + "is_blockhash_valid", + "request_airdrop" + ], + "priority": 90, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 3000 + } + ] + }, + { + "name": "helius_primary_http", + "enabled": true, + "provider": "helius", + "cluster": "mainnet-beta", + "url": "https://mainnet.helius-rpc.com/?api-key=95e73621-b539-4a79-af39-3b0877a13857", + "connect_timeout_ms": 4000, + "request_timeout_ms": 10000, + "max_idle_connections_per_host": 8, + "roles": [ + { + "role": "http_queries", + "enabled": true, + "request_kinds": [ + "get_account_info", + "get_balance", + "get_block_commitment", + "get_block_height", + "get_block_production", + "get_blocks", + "get_blocks_with_limit", + "get_cluster_nodes", + "get_epoch_info", + "get_epoch_schedule", + "get_fee_for_message", + "get_first_available_block", + "get_genesis_hash", + "get_health", + "get_highest_snapshot_slot", + "get_identity", + "get_inflation_governor", + "get_inflation_rate", + "get_inflation_reward", + "get_largest_accounts", + "get_latest_blockhash", + "get_leader_schedule", + "get_max_retransmit_slot", + "get_max_shred_insert_slot", + "get_minimum_balance_for_rent_exemption", + "get_multiple_accounts", + "get_recent_performance_samples", + "get_recent_prioritization_fees", + "get_signature_statuses", + "get_slot", + "get_slot_leader", + "get_slot_leaders", + "get_stake_activation", + "get_stake_minimum_delegation", + "get_supply", + "get_token_account_balance", + "get_token_accounts_by_delegate", + "get_token_accounts_by_owner", + "get_token_largest_accounts", + "get_token_supply", + "get_transaction_count", + "get_version", + "get_vote_accounts", + "is_blockhash_valid", + "request_airdrop" + ], + "priority": 10, + "requests_per_second": 8, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 1500 + }, + { + "role": "http_heavy", + "enabled": true, + "request_kinds": [ + "get_block", + "get_program_accounts", + "get_signatures_for_address", + "get_transaction" + ], + "priority": 20, + "requests_per_second": 2, + "burst_capacity": 1, + "max_concurrent_requests": 2, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 1500 + }, + { + "role": "history_backfill", + "enabled": true, + "request_kinds": [ + "get_block", + "get_program_accounts", + "get_signatures_for_address", + "get_transaction" + ], + "priority": 30, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 1500 + }, + { + "role": "http_transactions", + "enabled": false, + "request_kinds": [ + "simulate_transaction", + "send_transaction", + "get_signature_statuses" + ], + "priority": 40, + "requests_per_second": 1, + "burst_capacity": 1, + "max_concurrent_requests": 1, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 3000 + } + ] + } + ], + "ws_endpoints": [ + { + "name": "mainnet_public_ws_slots", + "enabled": true, + "provider": "solana-public", + "cluster": "mainnet-beta", + "url": "wss://api.mainnet.solana.com", + "connect_timeout_ms": 5000, + "request_timeout_ms": 10000, + "unsubscribe_timeout_ms": 1500, + "write_channel_capacity": 256, + "event_channel_capacity": 1024, + "auto_reconnect": false, + "roles": [ + { + "role": "slot_notifications", + "enabled": true, + "request_kinds": [ + "slot_subscribe", + "slots_updates_subscribe" + ], + "priority": 10, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 16, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "root_notifications", + "enabled": true, + "request_kinds": [ + "root_subscribe" + ], + "priority": 20, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 16, + "pause_after_rate_limit_ms": 3000 + } + ] + }, + { + "name": "helius_primary_ws_programs", + "enabled": true, + "provider": "helius", + "cluster": "mainnet-beta", + "url": "wss://mainnet.helius-rpc.com/?api-key=95e73621-b539-4a79-af39-3b0877a13857", + "connect_timeout_ms": 5000, + "request_timeout_ms": 10000, + "unsubscribe_timeout_ms": 1500, + "write_channel_capacity": 512, + "event_channel_capacity": 4096, + "auto_reconnect": false, + "roles": [ + { + "role": "logs_subscribe", + "enabled": true, + "request_kinds": [ + "logs_subscribe_mentions" + ], + "priority": 10, + "requests_per_second": 2, + "burst_capacity": 4, + "max_concurrent_requests": 2, + "max_subscriptions": 128, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "program_subscribe", + "enabled": true, + "request_kinds": [ + "program_subscribe" + ], + "priority": 20, + "requests_per_second": 2, + "burst_capacity": 4, + "max_concurrent_requests": 2, + "max_subscriptions": 128, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "account_subscribe", + "enabled": true, + "request_kinds": [ + "account_subscribe" + ], + "priority": 30, + "requests_per_second": 2, + "burst_capacity": 4, + "max_concurrent_requests": 2, + "max_subscriptions": 128, + "pause_after_rate_limit_ms": 3000 + } + ] + } + ], + "listeners": { + "enabled": true, + "default_commitment": "confirmed", + "log_listeners": [ + { + "name": "pump_swap_logs", + "enabled": true, + "endpoint_role": "program_logs", + "program_id": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA", + "purpose": "Détecter les swaps, créations de pool et changements de liquidité Pump Swap." + }, + { + "name": "raydium_cpmm_logs", + "enabled": true, + "endpoint_role": "program_logs", + "program_id": "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C", + "purpose": "Détecter les créations de pools et swaps Raydium CPMM." + } + ], + "program_listeners": [ + { + "name": "pump_fun_program_accounts", + "enabled": true, + "endpoint_role": "program_logs", + "program_id": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", + "purpose": "Détecter les changements de comptes utiles à la création ou migration de tokens Pump Fun." + } + ], + "account_listeners": [] + } + }, + "wallet": { + "wallet_dir": "wallets/temporary/mainnet_research", + "cluster": "mainnet-beta", + "temporary_wallet_enabled": false, + "temporary_wallet_alias": "mainnet_research-disabled", + "temporary_wallet_persist": false, + "localnet_send_enabled": false, + "devnet_send_enabled": false, + "testnet_send_enabled": false, + "mainnet_send_enabled": false + }, + "execution": { + "dry_run_default": true, + "require_simulation": true, + "require_operator_confirmation": true, + "localnet_max_spend_lamports": 0, + "devnet_max_spend_lamports": 0, + "testnet_max_spend_lamports": 0, + "mainnet_max_spend_lamports": 0, + "max_fee_lamports": 1000000, + "max_compute_unit_price_micro_lamports": 100000, + "recent_blockhash_max_age_slots": 150, + "send_max_retries": 3, + "confirmation_poll_interval_ms": 500, + "confirmation_max_attempts": 120, + "devnet_airdrop_max_lamports": 0 + }, + "demo": { + "live_demo_enabled": true, + "trading_demo_enabled": false + } + }, + { + "name": "mainnet", + "app": { + "name": "khadhroony-bot2", + "environment": "mainnet", + "auto_reconnect_default": false + }, + "logging": { + "default_level": "warn", + "targets": [ + { + "name": "console_mainnet_compact", + "enabled": true, + "sink": "console", + "level": "debug", + "path": "", + "rotation": "none", + "format": "compact", + "ansi": true, + "targets": [ + "kb_app_demo", + "kb_decoder_metadata_metaplex_token_metadata", + "kb_decoder_solana_core", + "kb_decoder_spl_associated_token_account", + "kb_decoder_spl_elgamal_registry", + "kb_decoder_spl_memo", + "kb_decoder_spl_token", + "kb_decoder_spl_token_2022", + "kb_execution_solana", + "kb_executor_metadata_metaplex_token_metadata", + "kb_executor_metadata_spl_name_service", + "kb_executor_solana_core", + "kb_executor_spl_account_compression", + "kb_executor_spl_associated_token_account", + "kb_executor_spl_elgamal_registry", + "kb_executor_spl_memo", + "kb_executor_spl_noop", + "kb_executor_spl_single_pool", + "kb_executor_spl_token", + "kb_executor_spl_token_2022", + "kb_logging", + "kb_materializer_admin", + "kb_materializer_compliance_audit", + "kb_materializer_lifecycle", + "kb_materializer_risk", + "kb_materializer_staking", + "kb_materializer_token_accounts", + "kb_materializer_transaction_annotations", + "kb_pipeline", + "kb_rpc", + "kb_store_pg", + "kb_wallet" + ] + }, + { + "name": "file_mainnet_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "*" + ] + }, + { + "name": "file_mainnet_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "*" + ] + }, + { + "name": "file_mainnet_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "*" + ] + }, + { + "name": "file_mainnet_app", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/app.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_app_demo_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_app_demo/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_app_demo_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_app_demo/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_app_demo_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_app_demo/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_decoder_solana_core_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_decoder_solana_core/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_solana_core" + ] + }, + { + "name": "file_kb_decoder_solana_core_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_decoder_solana_core/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_solana_core" + ] + }, + { + "name": "file_kb_decoder_solana_core_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_decoder_solana_core/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_solana_core" + ] + }, + { + "name": "file_kb_decoder_spl_memo_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_decoder_spl_memo/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_memo" + ] + }, + { + "name": "file_kb_decoder_spl_memo_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_decoder_spl_memo/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_memo" + ] + }, + { + "name": "file_kb_decoder_spl_memo_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_decoder_spl_memo/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_memo" + ] + }, + { + "name": "file_kb_decoder_spl_associated_token_account_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_decoder_spl_associated_token_account/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_associated_token_account" + ] + }, + { + "name": "file_kb_decoder_spl_associated_token_account_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_decoder_spl_associated_token_account/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_associated_token_account" + ] + }, + { + "name": "file_kb_decoder_spl_associated_token_account_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_decoder_spl_associated_token_account/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_associated_token_account" + ] + }, + { + "name": "file_kb_decoder_spl_token_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_decoder_spl_token/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token" + ] + }, + { + "name": "file_kb_decoder_spl_token_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_decoder_spl_token/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token" + ] + }, + { + "name": "file_kb_decoder_spl_token_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_decoder_spl_token/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_token" + ] + }, + { + "name": "file_kb_executor_metadata_spl_name_service_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_metadata_spl_name_service/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_spl_name_service" + ] + }, + { + "name": "file_kb_executor_metadata_spl_name_service_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_metadata_spl_name_service/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_spl_name_service" + ] + }, + { + "name": "file_kb_executor_metadata_spl_name_service_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_metadata_spl_name_service/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_metadata_spl_name_service" + ] + }, + { + "name": "file_kb_executor_solana_core_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_solana_core/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_solana_core" + ] + }, + { + "name": "file_kb_executor_solana_core_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_solana_core/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_solana_core" + ] + }, + { + "name": "file_kb_executor_solana_core_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_solana_core/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_solana_core" + ] + }, + { + "name": "file_kb_execution_solana_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_execution_solana/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_execution_solana" + ] + }, + { + "name": "file_kb_execution_solana_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_execution_solana/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_execution_solana" + ] + }, + { + "name": "file_kb_execution_solana_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_execution_solana/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_execution_solana" + ] + }, + { + "name": "file_kb_executor_spl_account_compression_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_spl_account_compression/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_account_compression" + ] + }, + { + "name": "file_kb_executor_spl_account_compression_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_spl_account_compression/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_account_compression" + ] + }, + { + "name": "file_kb_executor_spl_account_compression_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_spl_account_compression/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_account_compression" + ] + }, + { + "name": "file_kb_executor_spl_memo_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_spl_memo/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_memo" + ] + }, + { + "name": "file_kb_executor_spl_memo_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_spl_memo/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_memo" + ] + }, + { + "name": "file_kb_executor_spl_memo_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_spl_memo/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_memo" + ] + }, + { + "name": "file_kb_executor_spl_associated_token_account_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_spl_associated_token_account/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_associated_token_account" + ] + }, + { + "name": "file_kb_executor_spl_associated_token_account_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_spl_associated_token_account/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_associated_token_account" + ] + }, + { + "name": "file_kb_executor_spl_associated_token_account_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_spl_associated_token_account/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_associated_token_account" + ] + }, + { + "name": "file_kb_executor_spl_token_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_spl_token/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token" + ] + }, + { + "name": "file_kb_executor_spl_token_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_spl_token/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token" + ] + }, + { + "name": "file_kb_executor_spl_token_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_spl_token/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_token" + ] + }, + { + "name": "file_kb_executor_spl_noop_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_spl_noop/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_noop" + ] + }, + { + "name": "file_kb_executor_spl_noop_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_spl_noop/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_noop" + ] + }, + { + "name": "file_kb_executor_spl_noop_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_spl_noop/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_noop" + ] + }, + { + "name": "file_kb_executor_spl_single_pool_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_spl_single_pool/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_single_pool" + ] + }, + { + "name": "file_kb_executor_spl_single_pool_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_spl_single_pool/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_single_pool" + ] + }, + { + "name": "file_kb_executor_spl_single_pool_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_spl_single_pool/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_single_pool" + ] + }, + { + "name": "file_kb_logging_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_logging/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_logging" + ] + }, + { + "name": "file_kb_logging_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_logging/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_logging" + ] + }, + { + "name": "file_kb_logging_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_logging/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_logging" + ] + }, + { + "name": "file_kb_materializer_admin_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_materializer_admin/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_admin" + ] + }, + { + "name": "file_kb_materializer_admin_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_materializer_admin/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_admin" + ] + }, + { + "name": "file_kb_materializer_admin_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_materializer_admin/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_admin" + ] + }, + { + "name": "file_kb_materializer_compliance_audit_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_materializer_compliance_audit/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_compliance_audit" + ] + }, + { + "name": "file_kb_materializer_compliance_audit_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_materializer_compliance_audit/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_compliance_audit" + ] + }, + { + "name": "file_kb_materializer_compliance_audit_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_materializer_compliance_audit/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_compliance_audit" + ] + }, + { + "name": "file_kb_materializer_lifecycle_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_materializer_lifecycle/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_lifecycle" + ] + }, + { + "name": "file_kb_materializer_lifecycle_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_materializer_lifecycle/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_lifecycle" + ] + }, + { + "name": "file_kb_materializer_lifecycle_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_materializer_lifecycle/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_lifecycle" + ] + }, + { + "name": "file_kb_materializer_transaction_annotations_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_materializer_transaction_annotations/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_transaction_annotations" + ] + }, + { + "name": "file_kb_materializer_transaction_annotations_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_materializer_transaction_annotations/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_transaction_annotations" + ] + }, + { + "name": "file_kb_materializer_transaction_annotations_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_materializer_transaction_annotations/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_transaction_annotations" + ] + }, + { + "name": "file_kb_materializer_staking_debug", + "enabled": true, + "level": "debug", + "format": "human", + "path": "logs/mainnet/kb_materializer_staking/debug.log", + "targets": [ + "kb_materializer_staking" + ], + "sink": "file", + "rotation": "daily", + "ansi": false + }, + { + "name": "file_kb_materializer_staking_info", + "enabled": true, + "level": "info", + "format": "human", + "path": "logs/mainnet/kb_materializer_staking/info.log", + "targets": [ + "kb_materializer_staking" + ], + "sink": "file", + "rotation": "daily", + "ansi": false + }, + { + "name": "file_kb_materializer_staking_error", + "enabled": true, + "level": "error", + "format": "json", + "path": "logs/mainnet/kb_materializer_staking/error.jsonl", + "targets": [ + "kb_materializer_staking" + ], + "sink": "file", + "rotation": "daily", + "ansi": false + }, + { + "name": "file_kb_pipeline_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_pipeline/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_pipeline" + ] + }, + { + "name": "file_kb_pipeline_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_pipeline/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_pipeline" + ] + }, + { + "name": "file_kb_pipeline_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_pipeline/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_pipeline" + ] + }, + { + "name": "file_kb_rpc_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_rpc/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_rpc" + ] + }, + { + "name": "file_kb_rpc_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_rpc/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_rpc" + ] + }, + { + "name": "file_kb_rpc_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_rpc/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_rpc" + ] + }, + { + "name": "file_kb_store_pg_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_store_pg/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_store_pg" + ] + }, + { + "name": "file_kb_store_pg_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_store_pg/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_store_pg" + ] + }, + { + "name": "file_kb_store_pg_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_store_pg/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_store_pg" + ] + }, + { + "name": "file_kb_wallet_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_wallet/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_wallet" + ] + }, + { + "name": "file_kb_wallet_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_wallet/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_wallet" + ] + }, + { + "name": "file_kb_wallet_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_wallet/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_wallet" + ] + }, + { + "name": "file_kb_materializer_token_accounts_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_materializer_token_accounts/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_token_accounts" + ] + }, + { + "name": "file_kb_materializer_token_accounts_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_materializer_token_accounts/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_token_accounts" + ] + }, + { + "name": "file_kb_materializer_token_accounts_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_materializer_token_accounts/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_token_accounts" + ] + }, + { + "name": "file_kb_materializer_risk_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_materializer_risk/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_risk" + ] + }, + { + "name": "file_kb_materializer_risk_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_materializer_risk/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_risk" + ] + }, + { + "name": "file_kb_materializer_risk_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_materializer_risk/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_risk" + ] + }, + { + "name": "file_kb_decoder_spl_token_2022_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_decoder_spl_token_2022/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token_2022" + ] + }, + { + "name": "file_kb_decoder_spl_token_2022_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_decoder_spl_token_2022/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token_2022" + ] + }, + { + "name": "file_kb_decoder_spl_token_2022_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_decoder_spl_token_2022/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_token_2022" + ] + }, + { + "name": "file_kb_decoder_metadata_metaplex_token_metadata_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_decoder_metadata_metaplex_token_metadata/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_decoder_metadata_metaplex_token_metadata_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_decoder_metadata_metaplex_token_metadata/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_decoder_metadata_metaplex_token_metadata_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_decoder_metadata_metaplex_token_metadata/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_decoder_spl_elgamal_registry_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_decoder_spl_elgamal_registry/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_decoder_spl_elgamal_registry_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_decoder_spl_elgamal_registry/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_decoder_spl_elgamal_registry_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_decoder_spl_elgamal_registry/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_metadata_metaplex_token_metadata_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_metadata_metaplex_token_metadata/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_executor_metadata_metaplex_token_metadata_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_metadata_metaplex_token_metadata/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_executor_metadata_metaplex_token_metadata_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_metadata_metaplex_token_metadata/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_executor_spl_elgamal_registry_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_spl_elgamal_registry/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_spl_elgamal_registry_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_spl_elgamal_registry/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_spl_elgamal_registry_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_spl_elgamal_registry/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_spl_token_2022_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_spl_token_2022/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token_2022" + ] + }, + { + "name": "file_kb_executor_spl_token_2022_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_spl_token_2022/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token_2022" + ] + }, + { + "name": "file_kb_executor_spl_token_2022_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_spl_token_2022/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_token_2022" + ] + } + ], + "target_filters": [ + { + "target": "hyper", + "level": "warn" + }, + { + "target": "reqwest", + "level": "warn" + }, + { + "target": "tao", + "level": "warn" + }, + { + "target": "wry", + "level": "warn" + } + ] + }, + "database": { + "enabled": true, + "backend": "postgres", + "postgres": { + "url": "postgres://solana:solana@localhost:5432/solana", + "max_connections": 16, + "connect_timeout_ms": 5000, + "auto_initialize_schema": true + }, + "sqlite": { + "path": "data/mainnet.sqlite", + "create_if_missing": true, + "busy_timeout_ms": 5000, + "max_connections": 1, + "auto_initialize_schema": true, + "use_wal": true + } + }, + "data": { + "wallets_directory": "wallets", + "logs_directory": "logs" + }, + "solana": { + "http_endpoints": [ + { + "name": "mainnet_public_http", + "enabled": true, + "provider": "solana-public", + "cluster": "mainnet-beta", + "url": "https://api.mainnet.solana.com", + "connect_timeout_ms": 4000, + "request_timeout_ms": 10000, + "max_idle_connections_per_host": 4, + "roles": [ + { + "role": "http_queries", + "enabled": true, + "request_kinds": [ + "get_account_info", + "get_balance", + "get_block_commitment", + "get_block_height", + "get_block_production", + "get_blocks", + "get_blocks_with_limit", + "get_cluster_nodes", + "get_epoch_info", + "get_epoch_schedule", + "get_fee_for_message", + "get_first_available_block", + "get_genesis_hash", + "get_health", + "get_highest_snapshot_slot", + "get_identity", + "get_inflation_governor", + "get_inflation_rate", + "get_inflation_reward", + "get_largest_accounts", + "get_latest_blockhash", + "get_leader_schedule", + "get_max_retransmit_slot", + "get_max_shred_insert_slot", + "get_minimum_balance_for_rent_exemption", + "get_multiple_accounts", + "get_recent_performance_samples", + "get_recent_prioritization_fees", + "get_signature_statuses", + "get_slot", + "get_slot_leader", + "get_slot_leaders", + "get_stake_activation", + "get_stake_minimum_delegation", + "get_supply", + "get_token_account_balance", + "get_token_accounts_by_delegate", + "get_token_accounts_by_owner", + "get_token_largest_accounts", + "get_token_supply", + "get_transaction_count", + "get_version", + "get_vote_accounts", + "is_blockhash_valid", + "request_airdrop" + ], + "priority": 50, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "http_fallback", + "enabled": true, + "request_kinds": [ + "get_account_info", + "get_balance", + "get_block_commitment", + "get_block_height", + "get_block_production", + "get_blocks", + "get_blocks_with_limit", + "get_cluster_nodes", + "get_epoch_info", + "get_epoch_schedule", + "get_fee_for_message", + "get_first_available_block", + "get_genesis_hash", + "get_health", + "get_highest_snapshot_slot", + "get_identity", + "get_inflation_governor", + "get_inflation_rate", + "get_inflation_reward", + "get_largest_accounts", + "get_latest_blockhash", + "get_leader_schedule", + "get_max_retransmit_slot", + "get_max_shred_insert_slot", + "get_minimum_balance_for_rent_exemption", + "get_multiple_accounts", + "get_recent_performance_samples", + "get_recent_prioritization_fees", + "get_signature_statuses", + "get_slot", + "get_slot_leader", + "get_slot_leaders", + "get_stake_activation", + "get_stake_minimum_delegation", + "get_supply", + "get_token_account_balance", + "get_token_accounts_by_delegate", + "get_token_accounts_by_owner", + "get_token_largest_accounts", + "get_token_supply", + "get_transaction_count", + "get_version", + "get_vote_accounts", + "is_blockhash_valid", + "request_airdrop" + ], + "priority": 90, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 3000 + } + ] + }, + { + "name": "helius_primary_http", + "enabled": true, + "provider": "helius", + "cluster": "mainnet-beta", + "url": "https://mainnet.helius-rpc.com/?api-key=95e73621-b539-4a79-af39-3b0877a13857", + "connect_timeout_ms": 4000, + "request_timeout_ms": 10000, + "max_idle_connections_per_host": 8, + "roles": [ + { + "role": "http_queries", + "enabled": true, + "request_kinds": [ + "get_account_info", + "get_balance", + "get_block_commitment", + "get_block_height", + "get_block_production", + "get_blocks", + "get_blocks_with_limit", + "get_cluster_nodes", + "get_epoch_info", + "get_epoch_schedule", + "get_fee_for_message", + "get_first_available_block", + "get_genesis_hash", + "get_health", + "get_highest_snapshot_slot", + "get_identity", + "get_inflation_governor", + "get_inflation_rate", + "get_inflation_reward", + "get_largest_accounts", + "get_latest_blockhash", + "get_leader_schedule", + "get_max_retransmit_slot", + "get_max_shred_insert_slot", + "get_minimum_balance_for_rent_exemption", + "get_multiple_accounts", + "get_recent_performance_samples", + "get_recent_prioritization_fees", + "get_signature_statuses", + "get_slot", + "get_slot_leader", + "get_slot_leaders", + "get_stake_activation", + "get_stake_minimum_delegation", + "get_supply", + "get_token_account_balance", + "get_token_accounts_by_delegate", + "get_token_accounts_by_owner", + "get_token_largest_accounts", + "get_token_supply", + "get_transaction_count", + "get_version", + "get_vote_accounts", + "is_blockhash_valid", + "request_airdrop" + ], + "priority": 10, + "requests_per_second": 8, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 1500 + }, + { + "role": "http_heavy", + "enabled": true, + "request_kinds": [ + "get_block", + "get_program_accounts", + "get_signatures_for_address", + "get_transaction" + ], + "priority": 20, + "requests_per_second": 2, + "burst_capacity": 1, + "max_concurrent_requests": 2, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 1500 + }, + { + "role": "history_backfill", + "enabled": true, + "request_kinds": [ + "get_block", + "get_program_accounts", + "get_signatures_for_address", + "get_transaction" + ], + "priority": 30, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 1500 + }, + { + "role": "http_transactions", + "enabled": false, + "request_kinds": [ + "simulate_transaction", + "send_transaction", + "get_signature_statuses" + ], + "priority": 40, + "requests_per_second": 1, + "burst_capacity": 1, + "max_concurrent_requests": 1, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 3000 + } + ] + } + ], + "ws_endpoints": [ + { + "name": "mainnet_public_ws_slots", + "enabled": true, + "provider": "solana-public", + "cluster": "mainnet-beta", + "url": "wss://api.mainnet.solana.com", + "connect_timeout_ms": 5000, + "request_timeout_ms": 10000, + "unsubscribe_timeout_ms": 1500, + "write_channel_capacity": 256, + "event_channel_capacity": 1024, + "auto_reconnect": false, + "roles": [ + { + "role": "slot_notifications", + "enabled": true, + "request_kinds": [ + "slot_subscribe", + "slots_updates_subscribe" + ], + "priority": 10, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 16, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "root_notifications", + "enabled": true, + "request_kinds": [ + "root_subscribe" + ], + "priority": 20, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 16, + "pause_after_rate_limit_ms": 3000 + } + ] + }, + { + "name": "helius_primary_ws_programs", + "enabled": true, + "provider": "helius", + "cluster": "mainnet-beta", + "url": "wss://mainnet.helius-rpc.com/?api-key=95e73621-b539-4a79-af39-3b0877a13857", + "connect_timeout_ms": 5000, + "request_timeout_ms": 10000, + "unsubscribe_timeout_ms": 1500, + "write_channel_capacity": 512, + "event_channel_capacity": 4096, + "auto_reconnect": false, + "roles": [ + { + "role": "logs_subscribe", + "enabled": true, + "request_kinds": [ + "logs_subscribe_mentions" + ], + "priority": 10, + "requests_per_second": 2, + "burst_capacity": 4, + "max_concurrent_requests": 2, + "max_subscriptions": 128, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "program_subscribe", + "enabled": true, + "request_kinds": [ + "program_subscribe" + ], + "priority": 20, + "requests_per_second": 2, + "burst_capacity": 4, + "max_concurrent_requests": 2, + "max_subscriptions": 128, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "account_subscribe", + "enabled": true, + "request_kinds": [ + "account_subscribe" + ], + "priority": 30, + "requests_per_second": 2, + "burst_capacity": 4, + "max_concurrent_requests": 2, + "max_subscriptions": 128, + "pause_after_rate_limit_ms": 3000 + } + ] + } + ], + "listeners": { + "enabled": true, + "default_commitment": "confirmed", + "log_listeners": [ + { + "name": "pump_swap_logs", + "enabled": true, + "endpoint_role": "program_logs", + "program_id": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA", + "purpose": "Détecter les swaps, créations de pool et changements de liquidité Pump Swap." + }, + { + "name": "raydium_cpmm_logs", + "enabled": true, + "endpoint_role": "program_logs", + "program_id": "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C", + "purpose": "Détecter les créations de pools et swaps Raydium CPMM." + } + ], + "program_listeners": [ + { + "name": "pump_fun_program_accounts", + "enabled": true, + "endpoint_role": "program_logs", + "program_id": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", + "purpose": "Détecter les changements de comptes utiles à la création ou migration de tokens Pump Fun." + } + ], + "account_listeners": [] + } + }, + "wallet": { + "wallet_dir": "wallets/temporary/mainnet", + "cluster": "mainnet-beta", + "temporary_wallet_enabled": false, + "temporary_wallet_alias": "mainnet-disabled", + "temporary_wallet_persist": false, + "localnet_send_enabled": false, + "devnet_send_enabled": false, + "testnet_send_enabled": false, + "mainnet_send_enabled": false + }, + "execution": { + "dry_run_default": true, + "require_simulation": true, + "require_operator_confirmation": true, + "localnet_max_spend_lamports": 0, + "devnet_max_spend_lamports": 0, + "testnet_max_spend_lamports": 0, + "mainnet_max_spend_lamports": 0, + "max_fee_lamports": 1000000, + "max_compute_unit_price_micro_lamports": 100000, + "recent_blockhash_max_age_slots": 150, + "send_max_retries": 3, + "confirmation_poll_interval_ms": 500, + "confirmation_max_attempts": 120, + "devnet_airdrop_max_lamports": 0 + }, + "demo": { + "live_demo_enabled": true, + "trading_demo_enabled": false + } + } + ] +} diff --git a/config/schema.config.json b/config/schema.config.json new file mode 100644 index 0000000..984b88b --- /dev/null +++ b/config/schema.config.json @@ -0,0 +1,774 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://khadhroony.local/schema/config.schema.json", + "title": "Khadhroony Bot2 configuration", + "type": "object", + "additionalProperties": false, + "required": [ + "active_profile", + "profiles" + ], + "properties": { + "active_profile": { + "$ref": "#/$defs/non_empty_string" + }, + "profiles": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/profile" + } + } + }, + "$defs": { + "non_empty_string": { + "type": "string", + "minLength": 1 + }, + "log_level": { + "type": "string", + "enum": [ + "trace", + "debug", + "info", + "warn", + "error", + "off" + ] + }, + "profile": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "app", + "logging", + "database", + "data", + "solana", + "wallet", + "execution", + "demo" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "app": { + "$ref": "#/$defs/app_section" + }, + "logging": { + "$ref": "#/$defs/logging" + }, + "database": { + "$ref": "#/$defs/database" + }, + "data": { + "$ref": "#/$defs/data" + }, + "solana": { + "$ref": "#/$defs/solana" + }, + "wallet": { + "$ref": "#/$defs/wallet" + }, + "execution": { + "$ref": "#/$defs/execution" + }, + "demo": { + "$ref": "#/$defs/demo" + } + } + }, + "app_section": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "environment", + "auto_reconnect_default" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "environment": { + "$ref": "#/$defs/non_empty_string" + }, + "auto_reconnect_default": { + "type": "boolean" + } + } + }, + "logging": { + "type": "object", + "additionalProperties": false, + "required": [ + "default_level", + "targets", + "target_filters" + ], + "properties": { + "default_level": { + "$ref": "#/$defs/log_level" + }, + "targets": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/log_target" + } + }, + "target_filters": { + "type": "array", + "items": { + "$ref": "#/$defs/log_target_filter" + } + } + } + }, + "log_target": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "enabled", + "sink", + "level", + "path", + "rotation", + "format", + "ansi", + "targets" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "enabled": { + "type": "boolean" + }, + "sink": { + "type": "string", + "enum": [ + "console", + "file" + ] + }, + "level": { + "$ref": "#/$defs/log_level" + }, + "path": { + "type": "string" + }, + "rotation": { + "type": "string", + "enum": [ + "none", + "never", + "daily", + "hourly" + ] + }, + "format": { + "type": "string", + "enum": [ + "human", + "compact", + "pretty", + "json" + ] + }, + "ansi": { + "type": "boolean" + }, + "targets": { + "type": "array", + "items": { + "$ref": "#/$defs/non_empty_string" + } + } + } + }, + "log_target_filter": { + "type": "object", + "additionalProperties": false, + "required": [ + "target", + "level" + ], + "properties": { + "target": { + "$ref": "#/$defs/non_empty_string" + }, + "level": { + "$ref": "#/$defs/log_level" + } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": [ + "enabled", + "backend", + "postgres", + "sqlite" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "backend": { + "type": "string", + "enum": [ + "postgres", + "sqlite" + ] + }, + "postgres": { + "$ref": "#/$defs/postgres" + }, + "sqlite": { + "$ref": "#/$defs/sqlite" + } + } + }, + "postgres": { + "type": "object", + "additionalProperties": false, + "required": [ + "url", + "max_connections", + "connect_timeout_ms", + "auto_initialize_schema" + ], + "properties": { + "url": { + "$ref": "#/$defs/non_empty_string" + }, + "max_connections": { + "type": "integer", + "minimum": 1 + }, + "connect_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "auto_initialize_schema": { + "type": "boolean" + } + } + }, + "sqlite": { + "type": "object", + "additionalProperties": false, + "required": [ + "path", + "create_if_missing", + "busy_timeout_ms", + "max_connections", + "auto_initialize_schema", + "use_wal" + ], + "properties": { + "path": { + "$ref": "#/$defs/non_empty_string" + }, + "create_if_missing": { + "type": "boolean" + }, + "busy_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "max_connections": { + "type": "integer", + "minimum": 1 + }, + "auto_initialize_schema": { + "type": "boolean" + }, + "use_wal": { + "type": "boolean" + } + } + }, + "data": { + "type": "object", + "additionalProperties": false, + "required": [ + "wallets_directory", + "logs_directory" + ], + "properties": { + "wallets_directory": { + "$ref": "#/$defs/non_empty_string" + }, + "logs_directory": { + "$ref": "#/$defs/non_empty_string" + } + } + }, + "solana": { + "type": "object", + "additionalProperties": false, + "required": [ + "http_endpoints", + "ws_endpoints", + "listeners" + ], + "properties": { + "http_endpoints": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/http_endpoint" + } + }, + "ws_endpoints": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/ws_endpoint" + } + }, + "listeners": { + "$ref": "#/$defs/listeners" + } + } + }, + "http_endpoint": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "enabled", + "provider", + "cluster", + "url", + "connect_timeout_ms", + "request_timeout_ms", + "max_idle_connections_per_host", + "roles" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "enabled": { + "type": "boolean" + }, + "provider": { + "$ref": "#/$defs/non_empty_string" + }, + "cluster": { + "$ref": "#/$defs/non_empty_string" + }, + "url": { + "type": "string", + "pattern": "^https?://" + }, + "connect_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "request_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "max_idle_connections_per_host": { + "type": "integer", + "minimum": 1 + }, + "roles": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/endpoint_role" + } + } + } + }, + "ws_endpoint": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "enabled", + "provider", + "cluster", + "url", + "connect_timeout_ms", + "request_timeout_ms", + "unsubscribe_timeout_ms", + "write_channel_capacity", + "event_channel_capacity", + "auto_reconnect", + "roles" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "enabled": { + "type": "boolean" + }, + "provider": { + "$ref": "#/$defs/non_empty_string" + }, + "cluster": { + "$ref": "#/$defs/non_empty_string" + }, + "url": { + "type": "string", + "pattern": "^wss?://" + }, + "connect_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "request_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "unsubscribe_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "write_channel_capacity": { + "type": "integer", + "minimum": 1 + }, + "event_channel_capacity": { + "type": "integer", + "minimum": 1 + }, + "auto_reconnect": { + "type": "boolean" + }, + "roles": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/endpoint_role" + } + } + } + }, + "endpoint_role": { + "type": "object", + "additionalProperties": false, + "required": [ + "role", + "enabled", + "request_kinds", + "priority", + "requests_per_second", + "burst_capacity", + "max_concurrent_requests", + "max_subscriptions", + "pause_after_rate_limit_ms" + ], + "properties": { + "role": { + "$ref": "#/$defs/non_empty_string" + }, + "enabled": { + "type": "boolean" + }, + "request_kinds": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/non_empty_string" + } + }, + "priority": { + "type": "integer" + }, + "requests_per_second": { + "type": "integer", + "minimum": 1 + }, + "burst_capacity": { + "type": "integer", + "minimum": 1 + }, + "max_concurrent_requests": { + "type": "integer", + "minimum": 1 + }, + "max_subscriptions": { + "type": "integer" + }, + "pause_after_rate_limit_ms": { + "type": "integer", + "minimum": 1 + } + } + }, + "listeners": { + "type": "object", + "additionalProperties": false, + "required": [ + "enabled", + "default_commitment", + "log_listeners", + "program_listeners", + "account_listeners" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "default_commitment": { + "$ref": "#/$defs/non_empty_string" + }, + "log_listeners": { + "type": "array", + "items": { + "$ref": "#/$defs/log_listener" + } + }, + "program_listeners": { + "type": "array", + "items": { + "$ref": "#/$defs/program_listener" + } + }, + "account_listeners": { + "type": "array", + "items": { + "$ref": "#/$defs/account_listener" + } + } + } + }, + "log_listener": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "enabled", + "endpoint_role", + "program_id", + "purpose" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "enabled": { + "type": "boolean" + }, + "endpoint_role": { + "$ref": "#/$defs/non_empty_string" + }, + "program_id": { + "$ref": "#/$defs/non_empty_string" + }, + "purpose": { + "$ref": "#/$defs/non_empty_string" + } + } + }, + "program_listener": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "enabled", + "endpoint_role", + "program_id", + "purpose" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "enabled": { + "type": "boolean" + }, + "endpoint_role": { + "$ref": "#/$defs/non_empty_string" + }, + "program_id": { + "$ref": "#/$defs/non_empty_string" + }, + "purpose": { + "$ref": "#/$defs/non_empty_string" + } + } + }, + "account_listener": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "enabled", + "endpoint_role", + "account_pubkey", + "purpose" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "enabled": { + "type": "boolean" + }, + "endpoint_role": { + "$ref": "#/$defs/non_empty_string" + }, + "account_pubkey": { + "$ref": "#/$defs/non_empty_string" + }, + "purpose": { + "$ref": "#/$defs/non_empty_string" + } + } + }, + "wallet": { + "type": "object", + "additionalProperties": false, + "required": [ + "wallet_dir", + "cluster", + "temporary_wallet_enabled", + "temporary_wallet_alias", + "temporary_wallet_persist", + "localnet_send_enabled", + "devnet_send_enabled", + "testnet_send_enabled", + "mainnet_send_enabled" + ], + "properties": { + "wallet_dir": { + "$ref": "#/$defs/non_empty_string" + }, + "cluster": { + "$ref": "#/$defs/non_empty_string" + }, + "temporary_wallet_enabled": { + "type": "boolean" + }, + "temporary_wallet_alias": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]*$" + }, + "temporary_wallet_persist": { + "type": "boolean" + }, + "localnet_send_enabled": { + "type": "boolean" + }, + "devnet_send_enabled": { + "type": "boolean" + }, + "testnet_send_enabled": { + "type": "boolean" + }, + "mainnet_send_enabled": { + "type": "boolean" + } + } + }, + "execution": { + "type": "object", + "additionalProperties": false, + "required": [ + "dry_run_default", + "require_simulation", + "require_operator_confirmation", + "localnet_max_spend_lamports", + "devnet_max_spend_lamports", + "testnet_max_spend_lamports", + "mainnet_max_spend_lamports", + "max_fee_lamports", + "max_compute_unit_price_micro_lamports", + "recent_blockhash_max_age_slots", + "send_max_retries", + "confirmation_poll_interval_ms", + "confirmation_max_attempts", + "devnet_airdrop_max_lamports" + ], + "properties": { + "dry_run_default": { + "type": "boolean" + }, + "require_simulation": { + "type": "boolean" + }, + "require_operator_confirmation": { + "type": "boolean" + }, + "localnet_max_spend_lamports": { + "type": "integer", + "minimum": 0 + }, + "devnet_max_spend_lamports": { + "type": "integer", + "minimum": 0 + }, + "testnet_max_spend_lamports": { + "type": "integer", + "minimum": 0 + }, + "mainnet_max_spend_lamports": { + "type": "integer", + "minimum": 0 + }, + "max_fee_lamports": { + "type": "integer", + "minimum": 1 + }, + "max_compute_unit_price_micro_lamports": { + "type": "integer", + "minimum": 0 + }, + "recent_blockhash_max_age_slots": { + "type": "integer", + "minimum": 1 + }, + "send_max_retries": { + "type": "integer", + "minimum": 0, + "maximum": 4294967295 + }, + "confirmation_poll_interval_ms": { + "type": "integer", + "minimum": 1, + "maximum": 60000 + }, + "confirmation_max_attempts": { + "type": "integer", + "minimum": 1, + "maximum": 10000 + }, + "devnet_airdrop_max_lamports": { + "type": "integer", + "minimum": 0 + } + } + }, + "demo": { + "type": "object", + "additionalProperties": false, + "required": [ + "live_demo_enabled", + "trading_demo_enabled" + ], + "properties": { + "live_demo_enabled": { + "type": "boolean" + }, + "trading_demo_enabled": { + "type": "boolean" + } + } + } + } +} diff --git a/docs/ACCOUNT_ONLY_CANDIDATES.md b/docs/ACCOUNT_ONLY_CANDIDATES.md new file mode 100644 index 0000000..b235af9 --- /dev/null +++ b/docs/ACCOUNT_ONLY_CANDIDATES.md @@ -0,0 +1,25 @@ + + + +# Candidats qui ne sont pas encore des program_id + +Certains identifiants vus dans les explorateurs peuvent être des comptes de configuration, de pool, d'autorité, de registre, de vault ou de PDA. Ils ne doivent pas devenir des crates de décodeur tant que leur owner executable n'est pas prouvé. + +## Bags + +| Identifiant | Source supposée | Statut | Décision | +|------------------------------------------------|-----------------|-----------------------|-----------------------------------------------------------------------------------------------------------| +| `BAGSB9TpGrZxQbEsrEznv5jXXdwyP6AXerN8aVRiAmcv` | Bags | `account_not_program` | Ne pas créer `kb_decoder_bags` comme surface canonique tant que le vrai `program_id` n'est pas identifié. | + +## Règle + +Avant de créer une crate de décodeur : + +1. vérifier que l'adresse est bien un programme exécutable ; +2. vérifier l'owner et les transactions qui l'invoquent ; +3. rattacher les comptes non exécutables à leur `program_id` propriétaire ; +4. documenter les comptes importants dans une matrice séparée du registre des programmes. + +## Bags Fee Share + +Les programmes Fee Share V1/V2 sont maintenant documentés dans `docs/BAGS_FM.md`. Le compte `BAGSB9TpGrZxQbEsrEznv5jXXdwyP6AXerN8aVRiAmcv` reste un compte observé, pas une surface exécutable canonique. diff --git a/docs/AGAVE_LOCAL_NODE.md b/docs/AGAVE_LOCAL_NODE.md new file mode 100644 index 0000000..e7b3361 --- /dev/null +++ b/docs/AGAVE_LOCAL_NODE.md @@ -0,0 +1,210 @@ + + + +# Nœud Agave local expérimental + +Ce document cadre le jalon `0.3.x`. L'objectif est de mesurer si un nœud Agave local non votant peut devenir une source temps réel utile avant de construire l'ingestion, le backfill et l'extracteur raw vers core en `0.4.x`. + +## Positionnement + +`khadhroony-bot2` ne doit pas embarquer un validateur. Le validateur reste un process externe démarré manuellement ou par script de développement. + +```text +agave-validator local non votant + -> HTTP RPC local + -> WebSocket RPC local + -> demo_ws ou future démo Agave + -> mesures de capacité et de retard + -> décision pour l'ingestion 0.4.x +``` + +Le jalon `0.3.x` est une phase de recherche. Il ne doit pas écrire de raw de production, ne doit pas extraire vers core et ne doit pas déclencher d'achat/revente. + +## Hypothèse à tester + +Un nœud local Agave non votant avec ledger pruné pourrait permettre : + +- `blockSubscribe` local avec transactions complètes ; +- moins de `getTransaction` temps réel ; +- moins de listeners par programme ; +- filtrage local par program id, logs, instructions, balances et discriminators ; +- usage des providers externes surtout pour backfill historique, réparation et `sendTransaction`. + +Cette hypothèse doit être mesurée. Elle ne doit pas devenir une décision d'architecture sans chiffres locaux. + +## Commande de départ + +La commande exacte dépend de la version Agave installée, du snapshot disponible, des ports libres et des ressources locales. La commande de départ à documenter et ajuster est : + +```bash +agave-validator \ + --no-voting \ + --full-rpc-api \ + --enable-rpc-transaction-history \ + --rpc-pubsub-enable-block-subscription \ + --limit-ledger-size \ + --ledger ./data/agave-ledger \ + --accounts ./data/agave-accounts \ + --rpc-port 8899 \ + --dynamic-port-range 8000-8020 +``` + +`` est une limite de shreds, pas une durée exacte ni un nombre exact de blocks. La fenêtre réellement conservée dépend du débit réseau, de la purge, des snapshots, de la vitesse disque et de l'état local du nœud. + +## Préparation locale + +Créer les dossiers explicitement pour éviter de mélanger le ledger expérimental avec d'autres données : + +```bash +mkdir -p ./data/agave-ledger ./data/agave-accounts ./logs/agave_local_research +``` + +Avant chaque essai, noter : + +```text +version agave-validator +commande exacte +valeur N_SHREDS +heure de lancement +état initial du dossier ledger +état initial du dossier accounts +profil de configuration utilisé +endpoint externe de référence +``` + +Ne pas supprimer automatiquement `./data/agave-ledger` ou `./data/agave-accounts` dans le projet. Toute purge doit rester une action manuelle et documentée dans le compte rendu d'essai. + +## Profil de configuration + +`config/example.config.json` contient le profil `agave_local_research`, non actif par défaut. + +```text +HTTP local: http://127.0.0.1:8899 +WS local: ws://127.0.0.1:8900 +``` + +Pour l'utiliser, changer `active_profile` dans un fichier local non versionné ou lancer l'application avec `KB_CONFIG_PATH` pointant vers une copie locale. + +Le profil expose les rôles suivants : + +| Rôle | Usage | +|----------------------|-----------------------------------------------------------| +| `local_node_status` | santé, version et retard du nœud local | +| `http_queries` | lectures RPC légères locales | +| `http_heavy` | `getBlock`, `getTransaction`, `getProgramAccounts` locaux | +| `block_stream` | probe `blockSubscribe` local | +| `slot_notifications` | `slotSubscribe`, `slotsUpdatesSubscribe`, `rootSubscribe` | +| `logs_subscribe` | `logsSubscribe` par mention de programme | +| `program_subscribe` | `programSubscribe` par programme | +| `account_subscribe` | `accountSubscribe` par compte précis | + +Le fallback externe est volontairement séparé. Il ne doit pas masquer un échec local pendant les mesures. + +## Probes minimaux + +Les probes `0.3.2` devront couvrir au minimum : + +| Famille | Méthode | Résultat attendu | +|---------|-------------------------------------------------|--------------------------------------------------| +| HTTP | `getVersion` | version du nœud ou erreur explicite | +| HTTP | `getHealth` | état du nœud ou erreur explicite | +| HTTP | `getSlot` | slot local | +| HTTP | `getBlockHeight` | block height local | +| HTTP | `getMaxShredInsertSlot` | slot maximal de shred inséré | +| WS | `blockSubscribe` avec `transactionDetails=none` | `supported`, `unsupported`, `timeout` ou `error` | +| WS | `slotSubscribe` | subscription id puis notifications | +| WS | `slotsUpdatesSubscribe` | subscription id puis notifications | +| WS | `rootSubscribe` | subscription id puis notifications | +| WS | `logsSubscribe` `mentions` | subscription id ou refus provider | +| WS | `programSubscribe` | subscription id ou refus provider | +| WS | `accountSubscribe` | subscription id ou refus provider | + +Le probe minimal `blockSubscribe` ne doit pas laisser un flux ouvert : + +```text +1. ouvrir la WebSocket ; +2. envoyer blockSubscribe avec transactionDetails=none ; +3. attendre un subscription id ou une erreur ; +4. désabonner immédiatement ; +5. classer supported / unsupported / timeout / error ; +6. journaliser la réponse brute tronquée si nécessaire. +``` + +Paramètres de départ à tester dans `demo_ws` : + +```text +role: block_stream +method: blockSubscribe +filterJson: "all" +configJson: {"commitment":"confirmed","encoding":"json","transactionDetails":"none","showRewards":false} +``` + +Si `"all"` est refusé par la version RPC testée, documenter l'erreur et tester ensuite un filtre plus ciblé par programme dans `0.3.4`. + +## Mesures à collecter + +| Mesure | Commande ou source indicative | Rôle | +|-----------------------------|----------------------------------------------|-----------------------------------| +| taille ledger | `du -sh ./data/agave-ledger` | calibrer `--limit-ledger-size` | +| taille accounts | `du -sh ./data/agave-accounts` | vérifier le coût disque | +| CPU | `pidstat`, `top`, `htop` | mesurer le coût du nœud local | +| RAM | `pidstat`, `free`, `ps` | vérifier la faisabilité locale | +| réseau RX/TX | `nload`, `iftop`, `/proc/net/dev` | mesurer le coût mainnet | +| slot local | `getSlot` local | vérifier l'avancement | +| slot externe | `getSlot` provider de référence | calculer le retard | +| lag slots | slot externe moins slot local | détecter un décrochage | +| lag secondes | lag slots multiplié par estimation slot time | estimer l'impact trading | +| débit WebSocket | compte notifications par seconde | comparer les modes live | +| taille moyenne notification | taille texte JSON ou bytes WS | dimensionner raw_ws_notifications | +| reconnects | logs `kb_app_demo.demo_ws` et nœud | détecter l'instabilité | +| erreurs provider ou Agave | logs et réponses JSON-RPC | classer les limites | + +La mesure de lag secondes reste approximative tant que le slot time local n'est pas calculé sur fenêtre glissante. Le chiffre utile pour la décision reste le couple `lag slots` plus `lag secondes estimées`. + +## Erreurs à documenter sans les masquer + +| Catégorie | Exemples à noter | +|------------|---------------------------------------------------------------------------------------------| +| snapshot | téléchargement lent, snapshot incompatible, absence de snapshot | +| ledger | corruption, purge trop agressive, croissance disque excessive | +| accounts | croissance inattendue, I/O saturée, chemin invalide | +| ports | `8899` occupé, port WS indisponible, `dynamic-port-range` trop étroit | +| ressources | CPU saturé, RAM insuffisante, disque saturé, réseau insuffisant | +| retard | slot local qui n'avance plus, lag croissant, root très en retard | +| RPC | méthode non supportée, timeouts, erreurs JSON-RPC | +| WebSocket | connexion refusée, fermeture serveur, subscription refusée, notifications trop volumineuses | + +Un essai qui échoue est exploitable s'il indique précisément la commande, le contexte, l'erreur et la ressource bloquante. + +## Table de résultats à remplir + +| Date | Agave | N_SHREDS | durée | ledger | accounts | CPU moy/max | RAM moy/max | RX/TX | slot local | slot réf. | lag slots | lag sec. | blockSubscribe none | remarques | +|-----------|-----------|-----------|-----------|-----------|-----------|-------------|-------------|-----------|------------|-----------|-----------|-----------|---------------------|-----------| +| À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | + +## Critères de décision pour `0.4.x` + +Le nœud local peut être retenu comme source live principale uniquement si les conditions suivantes sont mesurées : + +- le slot local suit l'endpoint externe avec un retard stable et acceptable ; +- le ledger et les accounts restent soutenables sur le disque local ; +- `blockSubscribe` est supporté et stable avec au moins `transactionDetails=none` ; +- le mode `full` ou `signatures` ne bloque pas la WebView ni le process Rust lorsqu'il sera testé ; +- les reconnects et timeouts restent rares ou récupérables ; +- un fallback externe reste disponible pour backfill, réparation et envoi de transaction. + +Si une de ces conditions échoue, `0.4.x` doit privilégier `slotSubscribe + getBlock`, les listeners ciblés ou une stratégie hybride, sans dépendance obligatoire à Agave local. + +## Sortie attendue de `0.3.x` + +La fin de `0.3.x` doit produire une décision documentée : + +```text +source temps réel principale retenue +fallback temps réel retenu +politique raw_ws_notifications +faisabilité du nœud local +limites ressources observées +prochain design de l'ingestion 0.4.x +``` + diff --git a/docs/APPLICATION_CRATES.md b/docs/APPLICATION_CRATES.md new file mode 100644 index 0000000..2a060af --- /dev/null +++ b/docs/APPLICATION_CRATES.md @@ -0,0 +1,28 @@ + + + +# Crates applicatifs + +Les crates applicatifs sont les points d'entrée exécutables ou Tauri du workspace. Ils orchestrent les crates internes mais ne portent pas la logique métier profonde. + +## Nomenclature + +- Les applications ponctuelles ou interactives utilisent le préfixe `kb_app_`. +- `kb_app_cli` contient les commandes de contrôle, import, replay et diagnostic. +- `kb_app_demo` contient la démonstration Tauri. +- `kb_worker` est l'exception autorisée, car il représente un processus de travail long-running. + +## Responsabilités autorisées + +- Lire la configuration. +- Initialiser `kb_logging`. +- Appeler les APIs publiques de `kb_pipeline`. +- Afficher ou exposer les résultats. +- Retourner des codes de sortie ou événements UI. + +## Responsabilités interdites + +- Décoder directement une instruction DEX. +- Écrire directement dans PostgreSQL sans passer par les traits de stockage. +- Réimplémenter les règles de matérialisation. +- Charger ou manipuler des secrets wallet hors `kb_wallet`. diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..dcb96ec --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,108 @@ + + + +# Architecture + +L'architecture cible est organisée en couches strictes afin d'éviter de recréer un monolithe. Les noms `raw`, `core`, `obs`, `decode`, `mat`, `agg` et `ops` désignent des couches logiques, pas des schémas PostgreSQL applicatifs. + +## Chaîne principale + +```text +sources RPC HTTP / WebSocket / gRPC + -> kb_rpc + -> kb_model transaction canonique + -> raw + -> core + -> obs + -> decode + -> mat + -> agg + -> strategy + -> execution + +sources historiques déjà indexées + -> kb_external_sources + -> candidats de signatures + -> kb_pipeline + -> hydratation canonique via kb_rpc +``` + +## Responsabilités + +- `kb_rpc` gère les méthodes et flux RPC Solana : JSON-RPC HTTP, WebSocket, Helius/LaserStream et Yellowstone gRPC lorsque ces surfaces seront activées. +- `kb_external_sources` est une crate future réservée aux APIs REST, exports et imports historiques externes, par exemple Solscan Pro ou un CSV officiel. Elle ne produit jamais directement une transaction canonique et ne doit pas être confondue avec un futur index interne PostgreSQL. +- `kb_pipeline` combine la découverte de signatures par `kb_rpc` ou `kb_external_sources` avec l’hydratation canonique par `kb_rpc`. +- `kb_model` définit la transaction Solana canonique indépendante du fournisseur. +- `raw` conserve une transaction canonique unique et rejouable par signature. +- `obs` conserve les observations techniques de source, programme, instruction et discriminator. +- `core` expose les structures Solana normalisées pour les décodeurs. +- `decode` contient les événements protocolairement compris. +- `mat` contient les projections métier. +- `agg` contient les agrégations temporelles ou analytiques. +- `ops` trace les modules, versions et traitements. + +## Acquisition multi-source + +```text +getTransaction JSON-RPC +transactionSubscribe Helius +Yellowstone gRPC +logsSubscribe + hydration + | + v +transaction canonique identique +``` + +Les détails de fournisseur restent dans `kb_sol_obs_transaction_observations`. Ils ne contaminent pas les décodeurs ni les tables core. + +`kb_rpc` ne doit pas être renommée en `kb_com` ou `kb_transport` pour accueillir des sources historiques externes. Ces noms seraient trop génériques et mélangeraient RPC Solana, APIs REST indexées, imports CSV et orchestration métier. Le nom `kb_external_sources` évite la confusion avec l’indexation locale et décrit explicitement une frontière de fournisseurs externes plutôt qu’une simple action de récupération. Si des primitives HTTP réellement communes apparaissent plus tard, elles pourront être extraites dans une crate technique dédiée sans modifier la frontière fonctionnelle entre `kb_rpc` et `kb_external_sources`. + +## Convention DB associée + +Quand une couche logique devient une table Solana PostgreSQL, elle est encodée dans le nom de table : + +```text +kb_sol__ +``` + +Exemples : + +```text +kb_sol_raw_transactions +kb_sol_obs_transaction_observations +kb_sol_core_transactions +kb_sol_obs_program_observations +kb_sol_decode_decoded_events +kb_sol_mat_trade_events +kb_sol_ops_processing_ledger +``` + +Les formes qualifiées héritées, écrites ici avec `DOT` (`raw DOT sol_transactions`, `core DOT sol_instructions`, `obs DOT program_observations`), sont interdites. + +## Frontières interdites + +- Un décodeur ne dépend pas du store concret. +- Un décodeur ne dépend pas du fournisseur ou du transport d’acquisition. +- Un matérialisateur ne dépend pas du RPC. +- Le wallet ne dépend pas des décodeurs. +- L'application de démonstration ne doit pas contourner les APIs de pipeline. +- Une observation de source ne doit pas dupliquer le payload canonique complet. + +## Frontière extraction canonique vers core + +Depuis `0.3.4`, `kb_pipeline` contient un extracteur pur qui dépend de `kb_model` et des contrats `kb_store_core`, mais pas de PostgreSQL ni de Tauri. `kb_store_pg` implémente la transaction atomique et `kb_app_demo` ne fait qu’orchestrer la requête opérateur. + +```text +kb_model::CanonicalTransaction + | + v +kb_pipeline::core_extraction + | CoreExtractionBundle + v +kb_store_core::CoreExtractionStore + | + v +kb_store_pg::PostgresStore +``` + +Les futurs décodeurs consommeront les inputs core contextualisés ; ils ne reliront pas directement le JSON canonique pour reconstruire les comptes, CPI, logs ou balances. diff --git a/docs/BACKFILL_HTTP.md b/docs/BACKFILL_HTTP.md new file mode 100644 index 0000000..db6ce6f --- /dev/null +++ b/docs/BACKFILL_HTTP.md @@ -0,0 +1,145 @@ + + + +# Backfill HTTP transactionnel + +## Objectif + +Le jalon `0.3.3` fournit un moteur de backfill réutilisable qui transforme les réponses HTTP Solana standard en transactions canoniques indépendantes du fournisseur. + +```text +signatures explicites +ou getSignaturesForAddress + -> getTransaction + -> kb_model::CanonicalTransaction + -> kb_sol_raw_transactions + -> kb_sol_obs_transaction_observations +``` + +Aucun décodeur DEX, aucune extraction core et aucune source temps réel payante ne sont exécutés dans ce jalon. + +## Sources de signatures + +Le moteur accepte quatre catégories : + +- liste explicite de signatures, une signature par ligne ; +- historique d’un `program_id` ; +- historique d’un mint de token ; +- historique d’une adresse de pool. + +Les trois modes par adresse utilisent la même méthode standard `getSignaturesForAddress`. La catégorie est conservée dans le `filter_code` des observations afin de distinguer `program_before`, `program_after`, `program_latest`, et les équivalents token/pool. + +## Sens de pagination + +### Avant une signature + +Avec une ancre, le paramètre RPC `before` sélectionne les transactions plus anciennes que cette signature. Sans ancre, le mode `Before` omet `before` et commence sur la page la plus récente retournée par le RPC. Le moteur poursuit la pagination jusqu’à atteindre la limite demandée, une page incomplète, la limite de pages ou une demande d’arrêt. + +### Après une signature + +Le moteur transmet directement la signature d’ancrage dans le paramètre RPC `until`. Les pages suivantes combinent le même `until` avec un curseur `before` correspondant à la dernière signature de la page précédente. + +Le mode `after` utilise des pages RPC de 1 000 signatures afin que la limite par défaut de 20 pages puisse couvrir jusqu’à 20 000 signatures plus récentes que l’ancre. Le moteur ne conserve que les `X` candidates les plus proches de l’ancre, en plus de l’ensemble de déduplication borné par `max_pages × 1 000`. + +Une page incomplète signifie que la borne `until` a été atteinte. Si toutes les pages autorisées sont pleines, la campagne échoue avec le nombre de signatures inspectées et demande d’augmenter `max_pages`. Une erreur RPC de borne inconnue est propagée sans être transformée en résultat vide. + +## Limites et retries + +Le moteur combine : + +- les limites saisies par l’opérateur ; +- `requests_per_second` du rôle sélectionné ; +- `max_concurrent_requests` du rôle sélectionné ; +- `pause_after_rate_limit_ms` après une erreur 429 ; +- un backoff borné pour les autres erreurs temporaires. + +Le rôle HTTP doit supporter à la fois : + +```text +get_signatures_for_address +get_transaction +``` + +Le rôle recommandé reste `history_backfill`. + +## Persistance + +Une signature déjà présente dans `kb_sol_raw_transactions` est ignorée avant l’appel `getTransaction`. + +Chaque tentative d’hydratation produit une observation légère : + +- provider et endpoint ; +- protocole `solana_http_json_rpc` ; +- méthode `getTransaction` ; +- origine `backfill` ; +- commitment, session et filtre ; +- timestamps ; +- taille et hash du payload source lorsque disponibles ; +- statut `persisted`, `missing` ou `failed` ; +- erreur normalisée lorsque nécessaire. + +Le payload source complet n’est jamais dupliqué dans la table d’observations. + +## Démo Tauri et arrêt coopératif + +La fenêtre `demo_backfill` regroupe dans des accordéons Bootstrap 5 : + +1. paramètres communs ; +2. signatures explicites ; +3. programme ; +4. token ; +5. pool ; +6. journal et résumé JSON. + +Une seule campagne peut fonctionner à la fois. Le bouton `Arrêter` pose un drapeau d’annulation coopérative lu entre les pages, les candidats, le pacer et les retries. + +Depuis la préversion de clôture `0.3.4-pre.011`, l’annulation ne se limite plus aux frontières entre appels : les futures RPC `getSignaturesForAddress` et `getTransaction` sont mises en concurrence avec un observateur d’arrêt, l’attente du pacer est annulable et une pause de retry, y compris après un 429, est interrompue par sondage borné. L’abandon de la future HTTP empêche une campagne arrêtée d’attendre un timeout réseau complet. + +Le moteur maintient une file bornée par la concurrence effective et cesse d’admettre de nouveaux candidats dès que l’arrêt est demandé. Les candidats non démarrés ne sont ni journalisés comme traités, ni inclus dans `candidates_completed`. + +Le résumé distingue : + +- `candidates_selected` : candidats découverts ; +- `candidates_started` : candidats admis dans la file d’exécution ; +- `candidates_completed` : candidats arrivés à un résultat terminal ; +- `candidates_cancelled` : candidats démarrés puis interrompus avant un résultat terminal ; +- `candidates_not_started` : candidats jamais admis après la demande d’arrêt. + +Les invariants attendus sont : + +```text +candidates_selected = candidates_started + candidates_not_started +candidates_started = candidates_completed + candidates_cancelled +``` + +Pour une campagne `before`, `resume_before_signature` correspond à la dernière signature terminée dans un préfixe contigu de la liste ordonnée. Une transaction terminée hors ordre ne fait pas avancer seule ce curseur. Si aucun candidat n’a terminé, le curseur reste la signature d’ancrage initiale. Cette règle interdit de sauter les candidats non traités lors d’une reprise. + +Le calcul de cette frontière est couvert par les tests unitaires. Une campagne réelle de 500 candidats a déjà validé l’arrêt et la séparation entre terminés, annulés et non démarrés. Le rejeu manuel depuis `resume_before_signature` reste un contrôle opératoire recommandé, mais il ne constitue plus un jalon séparé ni un blocage pour `0.4.0`. + +## Validation finale `0.3.3` + +Validations locales : + +```text +cargo test -p kb_rpc : 54 tests passés +cargo test -p kb_pipeline : 10 tests passés +cargo test -p kb_store_core: 31 tests passés +cargo test -p kb_store_pg : 32 tests passés +cargo test -p kb_app_demo : 38 tests passés +cargo clippy --all-targets : validé +``` + +Les campagnes Tauri ont validé : + +- signatures explicites ; +- programme avant/après ; +- token avant/après ; +- pool avant/après ; +- parcours profond `program_after` avec 5 516 signatures indexées parcourues pour 5 transactions hydratées ; +- arrêt d’une campagne de 500 candidats avec 7 démarrés, 3 terminés, 4 annulés et 493 non démarrés. + +Les diagnostics PostgreSQL ont confirmé 62 transactions canoniques et 62 observations avant les derniers tests d’arrêt. Les tables core restent volontairement vides jusqu’à `0.3.4`. + +## Recherche latest sans ancre + +Pour programme, token et pool, une direction `before` avec ancre absente produit un filtre `*_latest`. La première page est demandée sans `before`, puis les pages suivantes utilisent normalement la dernière signature reçue comme curseur. Une direction `after` sans ancre est refusée, car la borne `until` ne peut pas être déterminée. En cas d’arrêt avant le premier candidat terminé, une campagne `*_latest` reprend depuis la page la plus récente et ne fabrique aucun curseur. diff --git a/docs/BAGS_FM.md b/docs/BAGS_FM.md new file mode 100644 index 0000000..221e47b --- /dev/null +++ b/docs/BAGS_FM.md @@ -0,0 +1,26 @@ + + + +# Bags.fm + +## Décision de classification + +`BAGSB9TpGrZxQbEsrEznv5jXXdwyP6AXerN8aVRiAmcv` reste classé comme compte observé et non comme `program_id` exécutable prouvé. + +En revanche, la documentation publique Bags liste deux programmes Fee Share : + +| Surface canonique | Program id | Statut | +|--------------------------|------------------------------------------------|---------| +| `fees_bags_fee_share_v1` | `FEEhPbKVKnco9EXnaY3i4R5rQVUx91wgVfu8qokixywi` | legacy | +| `fees_bags_fee_share_v2` | `FEE2tBhCKAt7shrod19QttSVREUYPiyMzoku1mL1gqVK` | current | + +## Crates réservés + +- `kb_decoder_fees_bags_fee_share_v1` +- `kb_executor_fees_bags_fee_share_v1` +- `kb_decoder_fees_bags_fee_share_v2` +- `kb_executor_fees_bags_fee_share_v2` + +## Rôle dans le trading assisté + +Ces surfaces ne sont pas des DEX directs. Elles peuvent cependant être utiles pour détecter des configurations de frais, claims, partenaires, vaults de frais ou comportements liés à des tokens lancés via Bags. diff --git a/docs/CODE_REUSE.md b/docs/CODE_REUSE.md new file mode 100644 index 0000000..2d04b62 --- /dev/null +++ b/docs/CODE_REUSE.md @@ -0,0 +1,41 @@ + + + +# Réutilisation de code + +Ce document liste les composants de l'ancien workspace qui peuvent servir de référence pour le nouveau workspace. Le code ne doit pas être copié mécaniquement : chaque portion reprise doit être adaptée aux nouvelles frontières de crates, aux règles de nommage, aux règles `crate::` et au backend PostgreSQL. + +## Composants candidats + +| Composant | Cible dans le nouveau workspace | Décision provisoire | +|--------------------------------------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------| +| Client RPC HTTP | `kb_rpc` | Réutiliser les idées : retry, backoff, limites, traces, idempotence. Réécrire l'API selon les nouveaux modèles. | +| Pool RPC HTTP | `kb_rpc` | Reprendre le principe de sélection d'endpoint et de contrôle de débit. Isoler les métriques et les erreurs dans `kb_core`. | +| Client WebSocket | `kb_rpc` | Reprendre la gestion ping/pong, subscribe/unsubscribe, shutdown explicite et reconnexion contrôlée. | +| Pool WebSocket | `kb_rpc` | Garder l'idée de pool, mais éviter toute dépendance vers les décodeurs ou le stockage concret. | +| Ledger de replay | `kb_pipeline` et `kb_store_core` | Réécrire en versionné par module, version, scope et input hash. | +| Validation SQL | `validation_sql/` | Porter seulement les contrôles encore utiles : doublons de signatures, events failed, non-swap vers trade, coverage résiduelle. | +| Découvertes de discriminators | `docs/DECODER_SURFACE_AUDIT.md`, `idls/`, futurs catalogues | Conserver comme corpus de décision, pas comme support automatique. | +| Matérialisations trade/liquidity/lifecycle | `kb_materializer_*` | Reprendre les règles validées, mais séparer strictement decode et materialize. | + +## Interdictions + +- Ne pas importer directement l'ancien schéma SQLite dans les nouveaux décodeurs. +- Ne pas faire dépendre `kb_rpc` de `kb_store_pg`. +- Ne pas faire dépendre un décodeur de `kb_pipeline`. +- Ne pas conserver une fonction monolithique de replay global. + +## Critère d'acceptation + +Une portion reprise est acceptée seulement si elle respecte les règles suivantes : + +- responsabilité limitée à un crate cible ; +- documentation de code en anglais ; +- absence de `use` hors traits nécessaires ; +- erreurs typées via `kb_core` ; +- tracing target stable ; +- tests unitaires ou validation de build. + +## Interfaces officielles actuelles + +La réutilisation prioritaire ne concerne pas uniquement l’ancien code. Les enums, types, IDs et encodeurs publiés dans les interfaces officielles Solana/SPL doivent être utilisés lorsqu’ils exposent un contrat compatible avec les règles du workspace. La matrice, les exceptions `bincode` et l’ordre `wincode`/Borsh/parser borné sont maintenus dans `docs/SOLANA_INTERFACE_DEPENDENCIES.md`. diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md new file mode 100644 index 0000000..e85e3bd --- /dev/null +++ b/docs/CONFIGURATION.md @@ -0,0 +1,114 @@ + + + +# Configuration + +La configuration sert de contrat commun entre les applications Tauri, les workers, les tests locaux, les stores, le RPC, le wallet et les exécuteurs. + +## Fichier principal + +```text +config/example.config.json +``` + +Ce fichier ne contient pas de commentaires, car JSON ne le permet pas. + +## Profils + +La configuration racine contient plusieurs profils et un seul profil actif : + +```json +{ + "active_profile": "local_devnet", + "profiles": [] +} +``` + +Les profils fournis sont `local_devnet`, `mainnet_research` et `mainnet`. + +## Logging + +Chaque sortie définit son nom, activation, sink, niveau, chemin, rotation, format, ANSI et liste de targets. Toute crate opérationnelle disposant d'un target `tracing` canonique doit recevoir ses fichiers `debug.log`, `info.log` et `error.jsonl` dédiés dans chaque profil. + +`kb_wallet` devient une crate opérationnelle en `0.4.2-pre.003`; ses logs ne contiennent jamais de secret ou d'octets de keypair. + +## Endpoints HTTP et WS + +Le modèle principal conserve les endpoints HTTP JSON-RPC et WebSocket Solana standards. Les rôles permettent de sélectionner un endpoint selon le type de requête et d'appliquer débit, burst, concurrence, subscriptions et pause 429. + +Les transports gRPC, Helius enrichi et LaserStream restent planifiés dans leurs jalons dédiés. + +## URL et clés API + +Une clé API doit rester sous forme de placeholder dans l'URL : + +```text +https://mainnet.helius-rpc.com/?api-key=${HELIUS_API_KEY} +``` + +Aucune clé réelle ne doit être committée. + +## Wallet + +Le bloc `wallet` contient : + +```text +wallet_dir +temporary_wallet_enabled +temporary_wallet_alias +temporary_wallet_persist +cluster +localnet_send_enabled +devnet_send_enabled +testnet_send_enabled +mainnet_send_enabled +``` + +Le profil local devnet persiste le wallet sous : + +```text +wallets/temporary/local_devnet/local-devnet-operator.json +``` + +Cette persistance est nécessaire pour conserver la même adresse entre plusieurs sessions, recevoir un airdrop et signer une séquence de tests. Le fichier contient un keypair Solana standard non chiffré : il est exclu du dépôt, protégé par permissions privées sur Unix et réservé au laboratoire local/devnet. + +La validation refuse : + +- un alias vide, trop long ou contenant un séparateur de chemin ; +- `temporary_wallet_persist = true` lorsque le wallet temporaire est désactivé ; +- un wallet temporaire actif sur `mainnet-beta`. + +## Exécution + +Le bloc `execution` contient : + +```text +dry_run_default +require_simulation +require_operator_confirmation +localnet_max_spend_lamports +devnet_max_spend_lamports +testnet_max_spend_lamports +mainnet_max_spend_lamports +max_fee_lamports +max_compute_unit_price_micro_lamports +recent_blockhash_max_age_slots +send_max_retries +confirmation_poll_interval_ms +confirmation_max_attempts +devnet_airdrop_max_lamports +``` + +Règles : + +- toute dépense configurée exige la simulation ; +- tout envoi activé exige un plafond de dépense positif pour le cluster correspondant ; +- mainnet exige la confirmation opérateur ; +- le plafond de frais et l'âge maximal du blockhash doivent être strictement positifs ; +- l’intervalle et le nombre de polls de confirmation sont strictement bornés ; +- le plafond d’airdrop Devnet doit rester nul dans les profils non Devnet ; +- `dry_run_default` reste actif dans tous les profils fournis. + +## Règle de sécurité + +Aucun secret ne doit être écrit dans le dépôt, les logs, PostgreSQL ou un payload Tauri. Les futurs backends chiffrés ou hardware wallet resteront confinés dans `kb_wallet` et exposeront uniquement une interface de signature. diff --git a/docs/CORE_EXTRACTION_CONTRACTS.md b/docs/CORE_EXTRACTION_CONTRACTS.md new file mode 100644 index 0000000..1a06582 --- /dev/null +++ b/docs/CORE_EXTRACTION_CONTRACTS.md @@ -0,0 +1,148 @@ + + + +# Contrats d’extraction core Solana + +## État de clôture `0.3.4-pre.011` + +Le jalon transforme une transaction canonique déjà persistée en graphe core normalisé et rejouable : + +```text +kb_sol_raw_transactions.canonical_json + -> kb_model::CanonicalTransaction + -> validation version/signature/slot/hash + -> kb_pipeline::extract_raw_transaction_to_core + -> CoreExtractionBundle + -> transaction PostgreSQL atomique +``` + +Le payload fournisseur contenu à l’origine dans une réponse RPC n’est jamais relu depuis les observations. La seule source fonctionnelle de l’extracteur est le document canonique de `kb_sol_raw_transactions`. + +## Unité atomique + +Une signature produit un `CoreExtractionBundle` contenant : + +- une transaction core liée par `raw_transaction_id` ; +- les account keys statiques et chargées par ALT ; +- les instructions top-level ; +- les inner instructions ; +- les logs ordonnés ; +- les changements de balances natifs et token ; +- l’identité du processor dans le ledger. + +Le repository PostgreSQL supprime puis recrée le graphe de la signature dans une transaction unique. Un échec avant le commit laisse intact l’état précédemment validé. Le test PostgreSQL de clôture provoque volontairement une violation d’unicité après insertion de la transaction et d’une première account key, puis vérifie l’absence totale de graphe partiel, de changement raw et de succès ledger. + +## Validation de l’entrée + +L’extraction refuse explicitement : + +- un `canonical_json` absent ; +- un `canonical_json_hash` absent ; +- une version différente de `CANONICAL_TRANSACTION_FORMAT_VERSION` ; +- un JSON non désérialisable en `CanonicalTransaction` ; +- une signature ou un slot différent de la ligne raw ; +- un hash recalculé différent du hash stocké ; +- un indice de programme ou de compte hors de l’espace résolu. + +L’identité du ledger est : + +```text +stage = core_extraction +processor_name = canonical_to_core +processor_version = 1 +input_key = signature +input_hash = canonical_json_hash +``` + +## Account keys + +L’espace d’indices est construit strictement dans cet ordre : + +```text +static account keys +loaded writable addresses +loaded readonly addresses +``` + +Pour les comptes statiques, les flags `signer` et `writable` sont dérivés du header Solana. Les loaded writable sont non-signers et writable. Les loaded readonly sont non-signers et readonly. `executable` reste `NULL`, car cette information n’est pas contenue de manière fiable dans le contrat canonique actuel. + +## Instructions + +Les chemins sont déterministes : + +```text +0 +1 +2 +0/0 +0/1 +2/0 +``` + +`accounts_json` conserve, dans l’ordre original, chaque indice et la clé publique résolue. `payload_json` conserve `programIdIndex`, `dataBase64` et `stackHeight`. Un SHA-256 stable du payload JSON est persisté dans `payload_json_hash`. + +## Logs + +Chaque log conserve : + +- son `log_index` global ; +- son texte original ; +- un SHA-256 du texte ; +- un `program_id` et un `instruction_path` uniquement lorsque la pile `invoke/success/failed` permet un rattachement déterministe. + +Lorsque la reconstruction est ambiguë, le lien reste `NULL` plutôt que d’inventer une relation. + +## Balances + +Les changements natifs utilisent les valeurs entières en lamports et stockent pre, post et delta comme chaînes décimales dans JSONB afin d’éviter toute perte de précision. + +Les changements SPL et Token-2022 utilisent exclusivement `amount` et `decimals`. Les valeurs UI du fournisseur ne sont pas utilisées comme source de calcul. Pour un compte créé ou fermé pendant la transaction, le côté absent est représenté explicitement par un montant brut nul avec les mêmes décimales. + +Le `balance_change_index` est déterministe : balances natives par index de compte, puis balances token triées par `(account_index, mint, program_id)`. + +## Modes de sélection + +La campagne peut sélectionner des transactions raw par signatures explicites, état `received`, plage de slots ou programme déjà présent dans les instructions top-level core. Les filtres, limites et scénarios de validation sont détaillés dans `docs/CORE_EXTRACTION_SELECTION_AND_REPLAY.md`. + +Le mode par programme ne découvre pas un programme nouveau : il dépend des lignes déjà présentes dans `kb_sol_core_instructions` et ne couvre pas actuellement les occurrences exclusivement inner. + +## Idempotence et replay + +En mode normal, une entrée ledger `succeeded` ayant la même version et le même hash provoque un skip. + +Un changement de version ou de hash entraîne une nouvelle extraction. Le mode `force_replay` ignore le skip et remplace atomiquement le graphe existant de la signature. + +## Orchestration + +`kb_pipeline::execute_core_extraction` fournit : + +- sélection par signatures ; +- lot raw en état `received` ; +- plage de slots ; +- replay par program id déjà présent dans core ; +- limite et concurrence bornées ; +- arrêt coopératif avant admission de nouveaux candidats ; +- résumé `selected`, `started`, `completed`, `skipped`, `extracted`, `failed`, `cancelled_candidates` et `not_started`. + +La logique de transformation ne dépend ni de Tauri ni d’un décodeur protocolaire. + +## Transactions échouées + +Une transaction Solana ayant `failed = true` reste extraite intégralement lorsque ses métadonnées sont disponibles. Les instructions, inner instructions et logs exécutés avant l’erreur constituent des observations utiles pour les futurs décodeurs. + +Le futur pipeline doit distinguer : + +```text +observation décodée dans une transaction échouée +mutation d’état confirmée dans une transaction réussie +``` + +Une transaction échouée peut produire des observations d’intention, de cause d’échec, de compute budget ou de surface appelée. Elle ne doit pas produire automatiquement un trade, une modification de liquidité ou une candle présentés comme réussis. + +## Validation réelle + +Le skip et le force replay ont été validés sur 14 signatures : `14 skipped`, puis `14 extracted` avec force replay, puis `14 skipped`. Les tables core restent à 70 transactions et le ledger totalise 84 tentatives. Les requêtes `sql/validation/000_core_integrity.sql` ne retournent aucune anomalie. + +## Hors périmètre + +`0.3.4` n’effectue aucun décodage Solana Core/SPL, aucun décodage DEX, aucune matérialisation métier et aucune compaction du JSON canonique. diff --git a/docs/CORE_EXTRACTION_SELECTION_AND_REPLAY.md b/docs/CORE_EXTRACTION_SELECTION_AND_REPLAY.md new file mode 100644 index 0000000..34aee86 --- /dev/null +++ b/docs/CORE_EXTRACTION_SELECTION_AND_REPLAY.md @@ -0,0 +1,456 @@ + + + +# Sélection et replay de l’extraction core + +## Objet + +Ce document décrit les modes disponibles dans `demo_core_extraction`, leur effet réel sur PostgreSQL et la différence entre : + +- acquisition d’une transaction depuis le réseau ; +- extraction du document canonique vers les tables core ; +- futur décodage protocolaire ; +- future matérialisation métier. + +Dans `0.3.4`, le mot « replay » désigne uniquement le rejeu de l’étape suivante : + +```text +kb_sol_raw_transactions.canonical_json + -> kb_model::CanonicalTransaction + -> CoreExtractionBundle + -> kb_sol_core_transactions + -> kb_sol_core_account_keys + -> kb_sol_core_instructions + -> kb_sol_core_inner_instructions + -> kb_sol_core_logs + -> kb_sol_core_balance_changes + -> kb_sol_ops_processing_ledger +``` + +Aucun appel RPC n’est effectué par cette étape. Aucun décodeur Solana Core, SPL, DEX ou autre protocole n’est exécuté. Aucune ligne métier n’est matérialisée. + +## Cycle complet d’une transaction + +Le cycle prévu est séparé en étapes rejouables : + +```text +réseau RPC + -> acquisition HTTP ou WebSocket + -> transaction canonique raw + -> extraction structurelle core + -> observation et classification + -> décodage protocolaire + -> matérialisation métier + -> agrégations et stratégies +``` + +`0.3.4` couvre uniquement la transition `raw canonique -> core structurel`. + +Les lignes de `kb_sol_core_instructions` sont créées avec `processing_state = pending`. Elles constituent l’entrée des futurs décodeurs prévus à partir de `0.4.x`. + +## Paramètres communs + +### Transactions maximales + +`limit` borne le nombre de transactions sélectionnées par PostgreSQL avant démarrage de la campagne. + +La sélection est ordonnée par : + +```text +slot ASC, signature ASC +``` + +Cette règle s’applique aussi au mode signatures explicites. L’ordre des lignes collées dans la zone de texte n’est donc pas l’ordre d’exécution garanti. + +### Concurrence + +`max_concurrent_extractions` borne le nombre d’extractions admises simultanément. + +Chaque signature reste une unité transactionnelle indépendante. Une transaction Solana produit son graphe core dans une transaction PostgreSQL atomique. + +### Force replay + +Sans `force_replay`, une signature est ignorée lorsque le ledger contient déjà un succès ayant exactement : + +```text +stage = core_extraction +processor_name = canonical_to_core +processor_version = version courante +input_key = signature +input_hash = canonical_json_hash courant +status = succeeded +``` + +Avec `force_replay`, ce skip est désactivé. Le graphe core existant de la signature est supprimé puis recréé dans la même transaction PostgreSQL. Les autres signatures ne sont pas touchées. + +Le force replay ne relance pas `getTransaction` et ne remplace pas le document canonique raw. Il reconstruit uniquement le core depuis le raw déjà présent. + +## Mode signatures explicites + +### Entrée + +La zone de texte accepte une signature par ligne. + +Avant l’appel au pipeline, l’interface : + +- supprime les espaces autour de chaque ligne ; +- ignore les lignes vides ; +- supprime les doublons en conservant la première occurrence ; +- valide chaque valeur comme signature Solana. + +### Sélection PostgreSQL + +Le mode sélectionne uniquement les signatures déjà présentes dans `kb_sol_raw_transactions`. + +Une signature absente de la table raw n’est pas téléchargée automatiquement. Elle n’apparaît simplement pas dans les candidats sélectionnés. Le compteur `selected` peut donc être inférieur au nombre de signatures saisies. + +Aucun filtre de `processing_state` n’est appliqué. Ce mode peut sélectionner une transaction raw : + +- `received` ; +- `core_extracted` ; +- `failed`. + +Le ledger décide ensuite entre skip, extraction ou retry. + +### Usages principaux + +- vérifier l’idempotence sur un échantillon connu ; +- forcer la reconstruction de quelques signatures ; +- retenter explicitement des transactions raw en échec ; +- comparer deux versions de l’extracteur ; +- préparer plus tard un corpus ciblé pour un décodeur. + +### Point d’attention + +Si le nombre de signatures existantes dépasse `limit`, seules les premières selon `slot ASC, signature ASC` sont retenues. + +## Mode transactions en attente + +### Filtre réel + +Le mode pending sélectionne : + +```text +kb_sol_raw_transactions.processing_state = received +``` + +Il ne sélectionne ni les lignes déjà `core_extracted`, ni les lignes `failed`. + +### Transition après succès + +Après commit du graphe core : + +```text +received -> core_extracted +``` + +La même ligne ne sera donc plus sélectionnée lors d’une campagne pending suivante. + +### Transition après échec + +Après un échec d’extraction persistable : + +```text +received -> failed +``` + +La raison est stockée dans `lifecycle_reason` et le ledger reçoit un statut `failed`. + +Une ligne `failed` doit être retentée par signature explicite ou par un futur mode dédié aux erreurs. Une nouvelle campagne pending ne la reprend pas. + +### Usages principaux + +- vider progressivement la file des transactions canoniques nouvellement acquises ; +- exécuter l’extraction courante après un backfill HTTP ; +- traiter un lot borné sans sélectionner manuellement les signatures. + +### Conséquence sur le corpus actuel + +Après la campagne réelle de 70 transactions : + +```text +selected = 70 +extracted = 70 +failed = 0 +``` + +les 70 lignes raw sont normalement passées à `core_extracted`. Une nouvelle campagne pending doit donc sélectionner zéro transaction tant qu’aucune nouvelle transaction raw `received` n’a été acquise. + +## Mode plage de slots + +### Filtre réel + +Le mode sélectionne toutes les transactions raw dont le slot se trouve dans l’intervalle inclusif : + +```text +min_slot <= slot <= max_slot +``` + +Aucun filtre de `processing_state` n’est appliqué. + +### Comportement normal + +Les transactions déjà à jour sont sélectionnées puis comptées comme `skipped` par le ledger. Les transactions reçues, en échec, ou dont la version/hash a changé peuvent être extraites. + +### Comportement avec force replay + +Toutes les transactions sélectionnées sont reconstruites dans la limite configurée. + +### Usages principaux + +- rejouer un intervalle temporel connu ; +- vérifier une régression sur un bloc ou une période ; +- reconstruire un corpus après changement de version de l’extracteur. + +## Mode programme déjà indexé dans core + +### Filtre réel actuel + +Le mode sélectionne les signatures raw pour lesquelles il existe déjà une ligne correspondante dans : + +```text +kb_sol_core_instructions +``` + +avec un `program_id` exactement égal à la valeur demandée. + +La requête actuelle ne recherche pas dans : + +- `kb_sol_core_inner_instructions` ; +- `kb_sol_core_logs` ; +- `kb_sol_core_account_keys` ; +- le document canonique raw. + +### Conséquences + +Ce mode ne peut pas découvrir un programme pour la première fois. Il fonctionne uniquement après une première extraction core ayant déjà résolu au moins une instruction top-level de ce programme. + +Une transaction où le programme apparaît exclusivement en inner instruction n’est pas sélectionnée par ce mode dans son état actuel. + +### Usages principaux + +- reconstruire les transactions d’un programme déjà indexé après changement de version ; +- vérifier une correction de résolution des comptes ou instructions ; +- préparer un sous-corpus structurel avant le futur replay d’un décodeur. + +### Évolution recommandée + +Le futur sélecteur de corpus devrait distinguer explicitement : + +- programme top-level ; +- programme inner ; +- programme observé dans les logs ; +- union de toutes les occurrences core fiables. + +Le mode d’extraction actuel peut rester strictement basé sur les instructions top-level, à condition que cette limite demeure visible dans l’interface. + +## Interprétation des compteurs + +| Compteur | Signification | +|-----------------------|-------------------------------------------------------------| +| `selected` | Candidats retournés par PostgreSQL après filtres et limite. | +| `started` | Candidats admis dans la file d’exécution bornée. | +| `completed` | Candidats arrivés à un résultat terminal. | +| `skipped` | Ledger déjà à jour pour la même version et le même hash. | +| `extracted` | Graphe core écrit et commit réussi. | +| `failed` | Extraction ou persistance terminée en erreur. | +| `cancelledCandidates` | Candidats admis mais annulés avant résultat terminal. | +| `notStarted` | Candidats sélectionnés mais jamais admis après arrêt. | +| `cancelled` | Une demande d’arrêt coopératif a été observée. | + +Pour une campagne terminée normalement : + +```text +completed = skipped + extracted + failed +selected = completed + cancelledCandidates + notStarted +``` + +## Scénarios de validation recommandés + +### Vérifier le skip + +1. Extraire 5 à 10 signatures déjà présentes dans core depuis un outil de sélection. +2. Les coller dans le mode signatures explicites. +3. Laisser `force_replay` désactivé. + +Résultat attendu : + +```text +selected = N +skipped = N +extracted = 0 +failed = 0 +``` + +### Vérifier le force replay + +1. Réutiliser exactement les mêmes signatures. +2. Activer `force_replay`. + +Résultat attendu : + +```text +selected = N +skipped = 0 +extracted = N +failed = 0 +``` + +Les cardinalités globales des tables core doivent rester stables pour ces signatures. Le `attempt_count` du ledger doit augmenter. + +### Vérifier un changement de version ou de hash + +Un changement réel de `processor_version` ou de `canonical_json_hash` doit provoquer une nouvelle extraction même sans `force_replay`. + +Cette vérification doit être effectuée dans une base de test dédiée. Le hash canonique d’une ligne de production ne doit pas être modifié manuellement. + +### Vérifier le rollback + +Le rollback atomique doit être validé par un test PostgreSQL avec injection d’une erreur entre les insertions du graphe et le commit. + +La méthode recommandée est un test d’intégration sur `solana_test`, et non une corruption volontaire de la base principale. + +## Sélecteur de corpus dans l’application + +La fenêtre read-only dédiée est : + +```text +demo_sql_replay_candidates +``` + +Elle est accessible depuis le menu principal et depuis l’en-tête de `demo_core_extraction`. `demo_sql_diag` reste limité à la connexion, au profil actif, aux migrations et aux cardinalités. + +Le sélecteur ne déclenche aucune acquisition RPC, extraction core, opération de décodage ou matérialisation. Il lit uniquement les tables PostgreSQL déjà alimentées. + +## Tableau des transactions et signatures + +Les filtres PostgreSQL disponibles sont : + +- fragment de signature ; +- slot minimum et maximum inclusifs ; +- état raw `received`, `core_extracted`, `decoded`, `materialized` ou `failed` ; +- statut ledger `not_started`, `running`, `succeeded` ou `failed` ; +- program ID exact ; +- portée du programme : `any`, `outer`, `inner` ou `logs` ; +- entité exacte : `mint`, `owner` ou `account_key` ; +- limite ; +- ordre par slot croissant ou décroissant. + +Chaque résultat affiche : + +- signature et slot ; +- état raw et rétention ; +- présence de la transaction core et éventuel échec Solana ; +- dernier statut du ledger `core_extraction` ; +- version et nombre de tentatives ; +- nombres d’instructions et de programmes outer/inner ; +- date de dernière mise à jour raw. + +Le filtre de programme inspecte les instructions outer, les inner instructions et les logs auxquels un `program_id` a pu être rattaché de manière prudente. Une occurrence dans les logs n’est donc visible que lorsque le rattachement core a produit un `program_id` non nul. + +## Tableau des programmes + +Le tableau agrège : + +```text +kb_sol_core_instructions.program_id +kb_sol_core_inner_instructions.program_id +kb_sol_core_logs.program_id +``` + +Pour chaque programme, il expose : + +- le `program_id` ; +- le nombre de transactions distinctes ; +- le nombre d’instructions outer ; +- le nombre d’instructions inner ; +- le nombre de logs reliés ; +- le slot minimum et maximum. + +La sélection d’un programme peut alimenter le filtre programme du tableau des transactions, puis charger les signatures correspondantes. + +## Tableau des entités + +Le tableau agrège trois familles : + +- `mint` depuis `kb_sol_core_balance_changes.mint` ; +- `owner` depuis `kb_sol_core_balance_changes.owner` ; +- `account_key` depuis `kb_sol_core_account_keys.account_key`. + +Chaque ligne indique le nombre de transactions distinctes, le nombre d’occurrences et la plage de slots. Une entité sélectionnée peut alimenter le filtre du tableau des transactions. + +Les account keys ne sont pas présentées comme wallet, pool, mint ou token account sans classification supplémentaire. + +## DataTables, copie et exports + +La fenêtre utilise cinq tableaux DataTables avec intégration Bootstrap 5 et extension Select : + +1. transactions et signatures ; +2. programmes ; +3. mints ; +4. owners ; +5. account keys. + +Chaque tableau affiche 10 lignes par défaut. Une checkbox par ligne et une checkbox de header permettent de sélectionner le jeu filtré. Les filtres possèdent un bouton de reset qui restaure les valeurs par défaut, efface la recherche locale, désélectionne les lignes et recharge PostgreSQL. + +Les signatures, program IDs, mints, owners et account keys sont tronqués visuellement sous la forme `préfixe…suffixe`. La valeur complète reste utilisée par le tri, la recherche, les tooltips, la copie et l’export. Un bouton de copie individuel évite les espaces parasites liés à une sélection manuelle. + +La règle d’export est la suivante : + +1. lorsqu’une ou plusieurs lignes sont sélectionnées, seules ces lignes sont exportées ; +2. sans sélection, toutes les lignes correspondant au filtre local DataTables sont exportées ; +3. la limite PostgreSQL reste la borne supérieure du corpus chargé. + +Le frontend construit un CSV UTF-8 avec BOM, séparateur `;` et fins de ligne CRLF. Une commande Rust l’écrit dans : + +```text +data/exports_csv/ +``` + +Le backend ajoute un suffixe numérique lorsque le nom existe déjà. Ce chemin a été validé sous Tauri/Linux et ne dépend pas du téléchargement HTML du WebView. + +La copie multiligne des signatures est directement compatible avec la zone `Signatures explicites` de `demo_core_extraction`. + +## Garanties et limites + +- commandes Tauri read-only ; +- requêtes SQL statiques et paramétrées ; +- aucune zone permettant d’exécuter du SQL libre ; +- limite obligatoire comprise entre `1` et `5 000` ; +- filtres exacts pour programme et entité afin d’éviter une sélection ambiguë ; +- recherche locale DataTables appliquée après le chargement serveur ; +- aucune découverte de transaction absente du raw ; +- aucun décodage de protocole ; +- aucun changement d’état raw, core ou ledger. + +Les futurs écrans de `0.4.x` pourront réutiliser le même principe pour les replays de décodeurs et de matérialiseurs. +## Utilisation du sélecteur SQL + +`demo_sql_replay_candidates` aide à constituer des corpus sans requête manuelle dans pgAdmin ou DBeaver. Il reste séparé du worker : il lit les tables raw/core/ledger, puis produit des signatures ou des filtres réutilisables. + +Dans l’onglet programmes, « Utiliser comme filtre transactions » prend exactement un programme coché. À défaut de sélection, une unique ligne restant après le filtre local est acceptée. L’action renseigne le program ID exact, choisit la portée `any`, ouvre l’onglet transactions et recharge la liste. + +Le sélecteur des programmes connus est alimenté par `kb_program_ids`, mais la saisie libre reste disponible pour les programmes observés qui ne sont pas encore enregistrés. + +Les exports CSV sont écrits dans `data/exports_csv/` par le backend Rust. Ils servent à l’audit et à la conservation d’un corpus ; pour rejouer immédiatement des signatures, la copie multiligne reste le chemin le plus direct. + +Les notions de pool et de paire ne sont pas encore disponibles à ce stade structurel. Une account key core ne suffit pas à prouver qu’un compte représente un pool ou une paire. Cette navigation sera ajoutée après matérialisation de catalogues sémantiques par les décodeurs. + + +## Validation réelle de l’idempotence + +Un échantillon de 14 signatures a produit les résultats suivants : + +```text +mode normal : extracted=0, skipped=14 +force replay: extracted=14, skipped=0 +mode normal : extracted=0, skipped=14 +``` + +Le nombre de transactions core est resté à 70. Le ledger contient 70 lignes `succeeded` et 84 tentatives, soit les 70 tentatives initiales plus les 14 remplacements forcés. Les requêtes d’intégrité n’ont détecté aucune anomalie. + +## Politique future pour les transactions échouées + +Les transactions on-chain échouées restent candidates au décodage. Elles conservent des informations sur les programmes appelés, les instructions exécutées avant l’erreur, les logs, le compute consommé et la cause d’échec. + +Les décodeurs devront produire des observations marquées par le statut de la transaction. Les matérialiseurs ne devront pas convertir ces observations en mutations d’état réussies, trades confirmés, changements de liquidité ou candles normales. diff --git a/docs/CORE_PROGRAM_IDS.md b/docs/CORE_PROGRAM_IDS.md new file mode 100644 index 0000000..7374b9b --- /dev/null +++ b/docs/CORE_PROGRAM_IDS.md @@ -0,0 +1,63 @@ + + + +# Identifiants core Solana et SPL + +Ce fichier sépare les identifiants primitifs Solana/SPL du registre des surfaces applicatives. Cette séparation évite de traiter `system`, `token`, `memo`, `sysvar` ou les loaders comme des DEX/routers. + +## Règle de classification + +- Les programmes runtime natifs, loaders et précompiles relèvent de `kb_decoder_solana_core`. +- Chaque programme SPL vérifié conserve une frontière spécialisée : Token, Token-2022, ATA, Memo, Stake Pool, Single Pool, Account Compression, Noop ou Name Service. +- Les sysvars sont des comptes connus à reconnaître pendant l'extraction, pas des surfaces DEX à décoder comme des programmes applicatifs. +- `stake_pool` et `single_pool` sont distincts du Stake Program natif et possèdent leurs propres crates. + +## Table de contrôle + +| Canonique | Program ID | Source | Type | Crate cible | Statut | +|--------------------------------------------|------------------------------------------------|-----------------------------------|----------------------|-------------------------------------------|-------------------------------------| +| `core_solana_address_lookup_table_v1` | `AddressLookupTab1e1111111111111111111111111` | `address_lookup_table` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_bpf_loader_deprecated_v1` | `BPFLoader1111111111111111111111111111111111` | `bpf_loader_deprecated` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_bpf_loader_v2` | `BPFLoader2111111111111111111111111111111111` | `bpf_loader` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_bpf_loader_upgradeable_v1` | `BPFLoaderUpgradeab1e11111111111111111111111` | `bpf_loader_upgradeable` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_compute_budget_v1` | `ComputeBudget111111111111111111111111111111` | `compute_budget` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_config_v1` | `Config1111111111111111111111111111111111111` | `config` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_ed25519_v1` | `Ed25519SigVerify111111111111111111111111111` | `ed25519` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_feature_v1` | `Feature111111111111111111111111111111111111` | `feature` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_incinerator_v1` | `1nc1nerator11111111111111111111111111111111` | `incinerator` | `well_known_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_loader_v4` | `LoaderV411111111111111111111111111111111111` | `loader_v4` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_native_loader_v1` | `NativeLoader1111111111111111111111111111111` | `native_loader` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_secp256k1_v1` | `KeccakSecp256k11111111111111111111111111111` | `secp256k1` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_secp256r1_v1` | `Secp256r1SigVerify1111111111111111111111111` | `secp256r1` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_slashing_v1` | `S1ashing11111111111111111111111111111111111` | `slashing` | `stateless_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_stake_config_v1` | `StakeConfig11111111111111111111111111111111` | `stake_config` | `well_known_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_stake_v1` | `Stake11111111111111111111111111111111111111` | `stake` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_system_v1` | `11111111111111111111111111111111` | `system` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_sysvar_clock_v1` | `SysvarC1ock11111111111111111111111111111111` | `sysvar_clock` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_epoch_rewards_v1` | `SysvarEpochRewards1111111111111111111111111` | `sysvar_epoch_rewards` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_epoch_schedule_v1` | `SysvarEpochSchedu1e111111111111111111111111` | `sysvar_epoch_schedule` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_fees_v1` | `SysvarFees111111111111111111111111111111111` | `sysvar_fees` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_instructions_v1` | `Sysvar1nstructions1111111111111111111111111` | `sysvar_instructions` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_last_restart_slot_v1` | `SysvarLastRestartS1ot1111111111111111111111` | `sysvar_last_restart_slot` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_recent_blockhashes_v1` | `SysvarRecentB1ockHashes11111111111111111111` | `sysvar_recent_blockhashes` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_rent_v1` | `SysvarRent111111111111111111111111111111111` | `sysvar_rent` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_rewards_v1` | `SysvarRewards111111111111111111111111111111` | `sysvar_rewards` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_slot_hashes_v1` | `SysvarS1otHashes111111111111111111111111111` | `sysvar_slot_hashes` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_slot_history_v1` | `SysvarS1otHistory11111111111111111111111111` | `sysvar_slot_history` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_stake_history_v1` | `SysvarStakeHistory1111111111111111111111111` | `sysvar_stake_history` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_v1` | `Sysvar1111111111111111111111111111111111111` | `sysvar` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_vote_v1` | `Vote111111111111111111111111111111111111111` | `vote` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_zk_elgamal_proof_v1` | `ZkE1Gama1Proof11111111111111111111111111111` | `zk_elgamal_proof` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_zk_token_proof_v1` | `ZkTokenProof1111111111111111111111111111111` | `zk_token_proof` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_spl_associated_token_account_v1` | `ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL` | `associated_token_account` | `program` | `kb_decoder_spl_associated_token_account` | `core_handled` | +| `core_spl_account_compression_v1` | `cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK` | `spl_account_compression` | `program` | `kb_decoder_spl_account_compression` | `core_specialized_reserved_current` | +| `core_spl_memo_v1` | `Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo` | `spl_memo_v1` | `program` | `kb_decoder_spl_memo` | `core_specialized_reserved_current` | +| `core_spl_memo_v3` | `MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr` | `spl_memo_v3` | `program` | `kb_decoder_spl_memo` | `core_specialized_reserved_current` | +| `core_spl_memo_v4` | `Memo4c2pN8afCj432Lb7RMVKi9PbQnnW7ewFFaV3oAH` | `spl_memo_v4` | `program` | `kb_decoder_spl_memo` | `core_specialized_reserved_current` | +| `core_spl_noop_v1` | `noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV` | `spl_noop` | `program` | `kb_decoder_spl_noop` | `core_specialized_reserved_current` | +| `core_spl_single_pool_v1` | `SVSPxpvHdN29nkVg9rPapPNDddN5DipNLRUFhyjFThE` | `spl_single_pool` | `program` | `kb_decoder_spl_single_pool` | `core_specialized_reserved_current` | +| `core_spl_name_service_v1` | `namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX` | `spl_name_service` | `program` | `kb_decoder_metadata_spl_name_service` | `core_specialized_reserved_current` | +| `core_spl_stake_pool_v1` | `SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy` | `stake_pool` | `program` | `kb_decoder_spl_stake_pool` | `core_specialized_reserved_current` | +| `core_spl_token_2022_v2022` | `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` | `token_2022` | `program` | `kb_decoder_spl_token_2022` | `core_handled` | +| `core_spl_token_2022_elgamal_registry_v1` | `regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg` | `spl_token_2022_elgamal_registry` | `program` | `kb_decoder_spl_token_2022` | `core_specialized_reserved_current` | +| `core_spl_token_v1` | `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA` | `token` | `program` | `kb_decoder_spl_token` | `core_handled` | diff --git a/docs/CORE_STORE.md b/docs/CORE_STORE.md new file mode 100644 index 0000000..6cf07fc --- /dev/null +++ b/docs/CORE_STORE.md @@ -0,0 +1,73 @@ + + + +# Store core Solana + +## Évolution `0.3.4` + +Le store core historique devient alimenté par un extracteur transactionnel à partir de `kb_sol_raw_transactions.canonical_json`. + +| Table | Rôle | +|----------------------------------|------------------------------------------------------------| +| `kb_sol_core_transactions` | Transaction normalisée, statut et lien vers la ligne raw. | +| `kb_sol_core_account_keys` | Espace résolu statique + ALT avec flags Solana. | +| `kb_sol_core_instructions` | Instructions top-level et payload canonique hashé. | +| `kb_sol_core_inner_instructions` | Inner instructions rattachées à un chemin parent. | +| `kb_sol_core_logs` | Logs ordonnés, texte hashé et lien d’invocation optionnel. | +| `kb_sol_core_balance_changes` | Deltas natifs, SPL Token et Token-2022. | +| `kb_sol_ops_processing_ledger` | Version, hash, statut et tentatives du processor. | + +## Écriture atomique + +Une extraction réussie exécute dans la même transaction PostgreSQL : + +1. validation du bundle ; +2. recherche d’un graphe core existant ; +3. suppression en cascade de ce graphe pour la signature ; +4. insertion de la transaction et de toutes ses lignes filles ; +5. passage de la ligne raw à `core_extracted` ; +6. upsert du ledger en `succeeded` ; +7. commit. + +Une erreur déclenche le rollback implicite de la transaction. L’enregistrement terminal d’un échec utilise une transaction dédiée et marque la ligne raw `failed` avec un diagnostic rejouable. + +## Clés d’idempotence + +Les contraintes actives restent : + +```text +core transaction : signature +account key : signature + account_index +instruction top-level : signature + instruction_path +inner instruction : signature + instruction_path +log : signature + log_index +balance change : signature + balance_change_index +ledger : stage + processor_name + processor_version + input_key +``` + +Le ledger ajoute `input_hash` à la décision de skip sans l’ajouter à la clé unique. Un nouvel hash met à jour la même identité de processor et augmente `attempt_count`. + +## Sélection raw + +Le contrat `CoreExtractionSelectionFilter` permet : + +- une liste de signatures ; +- une plage inclusive de slots ; +- un état raw ; +- un program id déjà indexé dans les instructions core ; +- une limite stricte. + +Le filtre par program id est un mode de replay. Il ne peut pas découvrir un programme dans une transaction qui n’a jamais encore été extraite. + +## Repositories + +`CoreExtractionStore` isole le pipeline du backend : + +```text +list_raw_transactions_for_core_extraction +is_core_extraction_current +persist_core_extraction +mark_core_extraction_failed +``` + +Les repositories historiques d’insertion unitaire restent disponibles, mais le worker `0.3.4` utilise exclusivement l’écriture atomique du bundle. diff --git a/docs/DATABASE.md b/docs/DATABASE.md new file mode 100644 index 0000000..df13aaa --- /dev/null +++ b/docs/DATABASE.md @@ -0,0 +1,155 @@ + + + +# Base de données + +PostgreSQL est le backend principal prévu pour `khadhroony-bot2`. `0.2.0` fixe uniquement les conventions de stockage : les migrations complètes, repositories SQL et diagnostics applicatifs sont reportés aux jalons suivants. + +## Décision PostgreSQL + +Le projet ne crée pas de schémas PostgreSQL applicatifs explicites. + +Le store utilise le schéma courant du profil PostgreSQL, généralement `public`. Le choix du schéma reste donc une responsabilité de configuration ou d'administration PostgreSQL, pas une responsabilité des migrations applicatives. + +Interdits, écrits avec `DOT` pour que les audits textuels simples ne confondent pas documentation et usage réel : + +```text +raw DOT kb_sol_rpc_transactions +core DOT kb_sol_transactions +obs DOT kb_sol_program_observations +decode DOT kb_sol_decoded_events +mat DOT kb_sol_trade_events +catalog DOT kb_sol_tokens +ops DOT kb_sol_processing_ledger +``` + +Autorisés : + +```text +kb_sol_raw_transactions +kb_sol_core_transactions +kb_sol_obs_program_observations +kb_sol_decode_decoded_events +kb_sol_mat_trade_events +kb_sol_catalog_tokens +kb_sol_ops_processing_ledger +``` + +## Format canonique des tables Solana + +Toutes les tables Solana applicatives suivent ce format : + +```text +kb_sol__ +``` + +Domaines autorisés au départ : + +| Domaine | Rôle logique | Exemple de table | +|-----------|-------------------------------------------------------------------------------------|---------------------------------------| +| `raw` | transactions Solana canoniques, source-indépendantes et rejouables | `kb_sol_raw_transactions` | +| `core` | extraction Solana générique normalisée | `kb_sol_core_instructions` | +| `obs` | observations techniques de source, programmes, instructions, logs et discriminators | `kb_sol_obs_transaction_observations` | +| `decode` | événements décodés par les décodeurs | `kb_sol_decode_decoded_events` | +| `mat` | projections métier matérialisées | `kb_sol_mat_trade_events` | +| `catalog` | tokens, pools, paires et références stables | `kb_sol_catalog_pairs` | +| `agg` | agrégations temporelles ou analytiques | `kb_sol_agg_pair_candles` | +| `ops` | ledger de traitement, migrations applicatives et diagnostics | `kb_sol_ops_processing_ledger` | +| `wallet` | métadonnées wallet non sensibles | `kb_sol_wallet_accounts` | + +Le domaine reste un préfixe de nom de table, jamais un schéma PostgreSQL. + + +## Décision de phasage à partir de `0.3.x` + +`0.3.x` corrige le modèle d’acquisition avant les décodeurs : transaction canonique source-indépendante, observations de source légères, backfill HTTP sur comptes gratuits et extraction vers `core`. + +Les flux payants Helius `transactionSubscribe` et Yellowstone gRPC sont reportés à `0.10.x`, après les décodeurs Core, Pump, Meteora, Raydium, Orca et Jupiter. Ils devront alimenter exactement le même contrat canonique que `getTransaction`. + +## Tables candidates initiales + +Ces tables sont candidates, pas toutes implémentées en `0.2.0`. + +| Table | Jalons pressentis | Rôle | +|---------------------------------------|--------------------|--------------------------------------------------------------------------------------------------------------------------| +| `kb_sol_raw_rpc_transactions` | `0.2.3` historique | Ancienne table validée puis remplacée pendant `0.3.1`; absente de la baseline SQL active. | +| `kb_sol_raw_transactions` | `0.3.1` | Stocker une transaction Solana canonique unique par signature, indépendante de la source. | +| `kb_sol_raw_ws_notifications` | `0.2.3` historique | Ancienne table supprimée pendant `0.3.1` après conversion des métadonnées utiles; absente de la baseline active. | +| `kb_sol_obs_transaction_observations` | `0.3.1` | Stocker source, protocole, méthode, timestamps, taille, latences et statuts sans dupliquer le payload transactionnel. | +| `kb_sol_core_transactions` | `0.2.4` | Ligne transaction normalisée liée à une signature. | +| `kb_sol_core_account_keys` | `0.2.4` | Comptes résolus, y compris loaded addresses. | +| `kb_sol_core_instructions` | `0.2.4` | Instructions top-level normalisées. | +| `kb_sol_core_inner_instructions` | `0.2.4` | Instructions internes normalisées. | +| `kb_sol_core_logs` | `0.2.4` | Logs de transaction et ordre d'apparition. | +| `kb_sol_core_balance_changes` | `0.2.4` | Deltas SOL/SPL dérivés du metadata RPC. | +| `kb_sol_obs_program_observations` | `0.3.4+` | Observations par programme invoqué, après stabilisation de l'ingestion et de l'extraction. | +| `kb_sol_obs_instruction_observations` | `0.3.4+` | Observations par instruction, discriminator et surface candidate, après stabilisation de l'ingestion et de l'extraction. | +| `kb_sol_decode_decoded_events` | `0.4.x+` | Événements décodés par surface et version de décodeur. | +| `kb_sol_mat_trade_events` | `0.5.x+` | Trades matérialisés. | +| `kb_sol_catalog_tokens` | `0.5.x+` | Catalogue de tokens observés. | +| `kb_sol_catalog_pools` | `0.5.x+` | Catalogue de pools observés. | +| `kb_sol_catalog_pairs` | `0.5.x+` | Catalogue de paires tradables. | +| `kb_sol_ops_processing_ledger` | `0.3.4+` | Ledger de traitements par module, version, input et statut, après stabilisation de l'ingestion. | + +## Règles SQL générales + +- `id` est la clé primaire technique, sauf exception explicitement documentée. +- `created_at` est le timestamp d'insertion. +- `updated_at` existe seulement si la table est mutable. +- `slot` est un `BIGINT` côté SQL ; les conversions Rust doivent être explicites. +- `signature` est un texte non vide quand applicable. +- `program_id` est un texte non vide quand applicable. +- `canonical_json` est un `JSONB` réservé à la représentation canonique source-indépendante de la transaction. +- `payload_json` est un `JSONB` réservé au payload décodé, enrichi ou matérialisé. +- Les index minimaux sont ajoutés selon l'usage : `signature`, `slot`, `program_id`, `created_at`. +- Les noms d'index utilisent un préfixe fonctionnel : `ux_` pour les index uniques et `ix_` pour les index non uniques, par exemple `ux_kb_sol_raw_transactions_signature` ou `ix_kb_sol_obs_transaction_observations_signature`. +- Les contraintes de clés nommées utilisent un préfixe fonctionnel : `pk_` pour les clés primaires et `fk_` pour les clés étrangères. Les contraintes de validation peuvent utiliser `ck_`. +- Les contraintes métier doivent rester strictes mais réversibles tant que les corpus ne sont pas stabilisés. + +## Principe raw et cycle de vie + +La couche `raw` conserve une seule représentation canonique de chaque transaction Solana. Elle ne conserve pas une copie complète par transport ou fournisseur. + +Les adaptateurs `kb_rpc` convertissent JSON-RPC, Helius WebSocket ou Yellowstone Protobuf vers le même contrat `kb_model`. Le payload canonique reste la source rejouable pour l’extraction `core`, les décodeurs et les matérialisateurs. + +La couche `obs` conserve les acquisitions successives : fournisseur, endpoint, protocole, méthode, commitment, timestamps, taille et statut. `kb_sol_obs_transaction_observations` ne contient pas de `raw_json` complet. + +Les états de rétention s’appliquent à la transaction canonique. Les observations techniques ont une politique de conservation séparée, car elles sont petites et utiles aux mesures de couverture et de latence. + +Le contrat détaillé est décrit dans `docs/TRANSACTION_ACQUISITION_MODEL.md`. Le cycle de vie est décrit dans `docs/RAW_STORAGE_LIFECYCLE.md`. Le découpage core destiné aux décodeurs est décrit dans `docs/CORE_EXTRACTION_CONTRACTS.md`. + +## Replay partiel + +Le replay ne doit pas être limité à la transaction complète. Après extraction `core`, l'unité de scheduling principale devient l'instruction normalisée. Cela permet de traiter uniquement les instructions `Pending`, `Failed` ou `ReplayRequested`, sans rejouer toute une transaction déjà extraite. + +Le décodage ne doit cependant pas être limité au payload de l'instruction. Les décodeurs doivent recevoir une entrée contextualisée : instruction ciblée, comptes résolus, inner instructions, logs, deltas de balance et statut de transaction. + +Les contrats de replay par instruction sont décrits dans `docs/INSTRUCTION_REPLAY_CONTRACTS.md`. Les contrats d'extraction core sont décrits dans `docs/CORE_EXTRACTION_CONTRACTS.md`. + +Chaque table dérivée doit pouvoir être reconstruite par au moins un des axes suivants quand l'information existe : + +- module et version ; +- signature ; +- plage de slots ; +- `program_id` ; +- surface candidate ; +- discriminator ; +- état dans `kb_sol_ops_processing_ledger`. + +## Frontière Rust store + +`kb_store_core` définit les contrats indépendants du backend : DTOs, entities, health, pagination, erreurs et traits repository. + +`kb_store_pg` implémente PostgreSQL : pool, migrations, requêtes SQL, repositories concrets et diagnostics du schéma courant. + +Les structures Rust ne doivent pas être placées dans `queries/`. Les requêtes SQL, les binds et l'exécution SQL restent dans `queries/`; les types proches des lignes SQL restent dans `entities/`; les contrats applicatifs restent dans `dtos/`; les APIs de stockage restent dans `repositories/`. + +## Statut `0.2.0` + +`0.2.0` est un jalon de cadrage. Il ne doit pas introduire un grand schéma SQL. Les migrations présentes avant cette convention doivent être neutralisées ou remplacées avant d'être exécutées sur une base réelle. + +## Ajout `0.3.4` — ledger de traitement + +La table `kb_sol_ops_processing_ledger` devient la source de vérité de l’idempotence par processor. Pour l’extraction core, elle mémorise la signature, le hash canonique, la version, le statut, le nombre de tentatives et le dernier diagnostic. + +L’écriture du succès ledger, le passage raw à `core_extracted` et le graphe core sont commités ensemble. Un statut `failed` reste rejouable et ne doit jamais être interprété comme une transaction canonique invalide de façon définitive. diff --git a/docs/DECODER_MATERIALIZATION_CONTRACTS.md b/docs/DECODER_MATERIALIZATION_CONTRACTS.md new file mode 100644 index 0000000..a50fc64 --- /dev/null +++ b/docs/DECODER_MATERIALIZATION_CONTRACTS.md @@ -0,0 +1,179 @@ + + + +# Contrats communs de décodage et de matérialisation + +## Objet + +La version `0.4.0` introduit l’infrastructure backend-agnostique utilisée par les futurs décodeurs Solana Core, SPL et protocolaires. Elle ne cherche pas encore à décoder maximalement un protocole précis. + +Le flux commun est : + +```text +core instruction contextualisée + -> dispatch déterministe + -> observation décodée versionnée + -> persistance atomique decode + couverture + ledger + -> matérialisation optionnelle explicitement autorisée + -> persistance atomique mat + ledger +``` + +## Input contextualisé + +`CoreInstructionReplayInput` reste l’unique contrat d’entrée commun. Son contrat passe à la version `2` dans `0.4.1-pre.014`. Il contient la signature, le slot, le statut et l’erreur on-chain, le chemin stable de l’instruction, le program ID, les comptes résolus dans leur ordre original, le payload brut déterministe et son hash, toutes les instructions outer ordonnées par index numérique, les inner instructions descendantes, les logs reliés prudemment, les changements de balances et la version du contrat core. + +`outer_instructions_json` est un tableau stable dont chaque entrée contient `instructionIndex`, `instructionPath`, `programId`, `payloadJson` et `payloadHash`. L’instruction cible est incluse. Cette projection provient des tables core existantes et ne nécessite aucune migration SQL. + +Le hash de replay est calculé à partir de la sérialisation JSON canonique de cet input. Le skip exige la même étape, le même processor, la même version, la même clé d’entrée, le même hash et un statut ledger réussi. + +Le passage au contrat `2` modifie légitimement les hashes existants, car les payloads outer participent désormais à la sérialisation. Le pipeline reste en version `1` : la version du contrat core et le nouveau contexte suffisent à invalider les anciens skips. Un force replay est requis après mise à niveau. + +## Contrat de décodeur + +`InstructionDecoder` expose : + +- une identité stable `name/version` ; +- les programmes et surfaces supportés ; +- une matrice de couverture déclarée ; +- une reconnaissance déterministe ; +- un résultat terminal `decoded`, `ignored`, `unsupported` ou `failed` ; +- zéro ou plusieurs observations typées uniquement pour `decoded` ; +- une preuve, une confiance et des diagnostics structurés. + +Le dispatch est ordonné par compatibilité exacte, priorité déclarée, surface, code d’entrée et identité stable. Il ne dépend ni du nom de la crate ni de l’ordre d’enregistrement implicite. + +Avant la lecture du store, le pipeline calcule le périmètre effectif des programmes à partir de l’union des `program_id` déclarés par les décodeurs activés. Lorsque l’opérateur fournit un filtre explicite, ce filtre doit être un sous-ensemble exact de ces programmes ; une valeur incompatible est refusée avant toute sélection SQL. Cette règle empêche un décodeur natif de consommer par défaut des instructions SPL ou protocolaires et évite de rejouer indéfiniment le même lot `unmatched` sans rapport avec les processors choisis. + +Une instruction inconnue d’un programme reconnu doit être classée sans produire de faux événement. Un `unmatched` reste possible lorsque le program ID est supporté mais que la reconnaissance contextuelle refuse l’input ; il ne modifie pas globalement le lifecycle de l’instruction pour ne pas empêcher un autre décodeur futur de la traiter. + +## Transactions on-chain échouées + +Une transaction échouée reste décodable lorsqu’un input structurel existe. Toute observation conserve : + +```text +transaction_failed +transaction_error +observation_committed +``` + +`observation_committed` doit être faux lorsque la transaction a été annulée. Ces observations peuvent décrire une instruction tentée, un événement loggé avant l’erreur, une classification d’échec, une consommation de compute ou une surface appelée. + +Elles ne peuvent pas produire automatiquement un trade réussi, une modification de liquidité réussie, un changement confirmé de catalogue ou une candle normale. `EventMaterializer` applique une politique explicite par famille. Les familles `trade`, `liquidity` et `lifecycle` sont refusées avant l’appel au matérialiseur lorsque la source est échouée ou non commitée. Une seconde barrière valide ensuite les familles de sortie et n’autorise, dans ce contexte, que les matérialisations d’audit ou de risque. + +## Persistance + +Les tables actives sont : + +- `kb_sol_decode_events` ; +- `kb_sol_decode_coverage_declarations` ; +- `kb_sol_decode_coverage_observations` ; +- `kb_sol_mat_events` ; +- `kb_sol_ops_processing_ledger`. + +Aucun schéma PostgreSQL applicatif explicite n’est créé. Les payloads et preuves JSONB utilisent des colonnes suffixées par `_jsonb`. + +Le décodage persiste dans une transaction unique les observations, la couverture, l’état lifecycle de l’instruction et le ledger. La matérialisation persiste également ses sorties et son ledger dans une transaction unique. Un rollback à la dernière étape doit donc supprimer toutes les écritures précédentes de la même tentative. + +Toute exécution non skippée remplace atomiquement les sorties du même processor, de la même version et de la même clé d’entrée. Le force replay contourne uniquement le skip version/hash ; il ne supprime jamais les sorties d’un autre processor, d’une autre version ou d’une autre clé. Les versions antérieures restent disponibles pour l’audit. + +Lorsqu’un force replay contient une liste explicite de signatures, le pipeline retire le filtre de lifecycle pour cette sélection bornée. Le même contournement est autorisé sans signatures uniquement lorsque l’opérateur active explicitement le mode « toutes les signatures » ; la sélection reste alors limitée par les programmes compatibles, les instruction paths, les slots éventuels et la limite. Sans signatures ni cette autorisation explicite, la requête est refusée. + +Les déclarations de couverture sont synchronisées comme un snapshot du couple processor/version. Le résultat de persistance distingue les lignes réellement insérées, modifiées, supprimées du snapshot ou strictement inchangées ; une déclaration identique est comptée comme `skipped`, pas comme une nouvelle insertion. + +## Couverture + +La couverture compare les entrées déclarées et observées, puis agrège : + +- reconnaissance ; +- décodage ; +- matérialisation ; +- erreurs ; +- inconnues ; +- transactions réussies ; +- transactions échouées. + +Une surface ne peut pas être considérée comme clôturée tant que toutes les instructions, événements et discriminants connus, y compris historiques et non liés au trading, ne sont pas classifiés. + +## Traçabilité structurée + +Chaque campagne reçoit un `campaign_id` process-local stable, propagé de la démo jusqu’aux décisions de dispatch, de ledger, de décodage, de matérialisation et de persistance. Des spans imbriqués de campagne, input et processor transmettent aussi ce contexte aux événements émis par les décodeurs et les stores sans modifier leurs contrats. Elle doit permettre de reconstruire précisément les décisions prises sans requête SQL libre. Les événements `debug` utilisent un champ `action` stable et conservent au minimum, selon l’étape : + +- le nombre de signatures, un échantillon borné et la sélection normalisée ; +- les filtres demandés puis les `program_id` et états effectivement appliqués ; +- la signature, le slot, l’instruction path, le program ID et la clé d’input ; +- le processor et sa version ; +- le hash déterministe pertinent ; +- la décision de dispatch, y compris lorsque `recognize` n’est pas appelé à cause d’un program ID incompatible ; +- le résultat de reconnaissance et de décodage ; +- le contrôle du ledger, le motif exact du skip ou son contournement par force replay ; +- le début et la validation de la persistance PostgreSQL ; +- le statut terminal et les compteurs par processor. + +Les targets stables sont : + +```text +kb_app_demo.demo_decode_replay +kb_app_demo.frontend.demo_decode_replay +kb_pipeline.decode_replay +kb_store_pg.decode_pipeline +kb_decoder_solana_core +``` + +Les signatures, slots, paths et program IDs sont des identifiants publics on-chain. Les listes complètes de signatures ne sont pas répétées à chaque couche : les événements de campagne conservent un compteur et un petit échantillon, tandis que les événements par input conservent la signature exacte. Les traces ne doivent pas enregistrer de DSN, secret, clé privée ni payload brut complet. Le payload d’instruction est représenté par son hash déterministe. + +## Orchestration + +`kb_pipeline::execute_decode_replay` fournit : + +- sélection bornée par signatures, états, slots, program IDs et instruction paths ; +- zéro, un ou plusieurs décodeurs compatibles selon la politique choisie ; +- concurrence bornée ; +- arrêt coopératif ; +- skip version/hash ; +- force replay ; +- résumé par processor et statut ; +- matérialisation optionnelle après succès de la persistance decode. + +La fenêtre Tauri `demo_decode_replay` ne contient aucune logique SQL libre. Elle construit une requête typée, appelle le pipeline et affiche les diagnostics read-only. + +## Projections natives actives + +`kb_materializer_lifecycle::LifecycleMaterializer` est enregistré dans `demo_decode_replay` à partir de `0.4.1-pre.003`. Son applicabilité est filtrée par surface, entrée et paramètres avant toute consultation du ledger ou application de politique. `pre.017` étend ses familles acceptées à `Lifecycle`, `Admin` et `Audit`, mais uniquement pour des observations exactes qui produisent réellement une mutation lifecycle. `pre.018` ajoute l’initialisation/la fermeture des rapports Slashing sous `slashing_violation_report`, sans transformer le rapport en pénalité de stake. Il applique toujours `SuccessfulCommittedOnly`. + +Les sorties stables actives sont : + +- `address_lookup_table::0` pour les cinq mutations ALT ; +- `program_loader:::0` pour les mutations loader stables ; +- `feature_gate:revoke_pending_activation:0` pour la révocation Feature Gate ; +- `durable_nonce_account::0` pour initialize, advance, authorize, upgrade et withdraw du System Program ; +- `zk_proof_context::0` pour l’initialisation d’un contexte ZK ElGamal demandée par une vérification réussie et pour sa fermeture. + +`authorize_nonce_account` reste décodé en famille `Admin`, mais sa mutation d’autorité durable est promue en sortie `Lifecycle`. Les vérifications ZK ElGamal restent des événements `Audit`; elles ne sont acceptées par le matérialiseur que lorsque `contextStateRequested = true`. Une preuve sans contexte n’est jamais proposée au matérialiseur. + +Le retrait d’un nonce account conserve une sémantique conditionnelle. Le runtime détruit le compte lorsque la totalité du solde est retirée ; l’instruction seule ne suffit pas à reconstruire de manière certaine l’état final transactionnel du compte. La projection décrit donc l’opération commitée, conserve le montant demandé et indique explicitement que l’état final n’est pas capturé. Les deltas SOL ne sont pas dupliqués. + +La création d’un contexte ZK conserve le compte cible, son autorité et le type de preuve, sans matérialiser les octets de preuve. La fermeture conserve le compte, la destination des lamports et l’autorité, puis décrit le reset vers le System Program. L’ancien ZK Token Proof Program n’est jamais matérialisé : son runtime actuel est un stub sans effet et sa sémantique historique n’est pas attribuable à une transaction sans preuve de version. + +Le hash d’entrée matérialiseur reste dérivé de l’observation décodée complète. Le ledger conserve séparément les processors `solana_native_lifecycle`, `solana_native_admin` et `solana_native_compliance_audit`, leur version, la clé d’input et le hash. Une transaction échouée ou une observation non commitée est refusée avant toute sortie mutable. + +`pre.020` répartit les responsabilités : create/allocate System restent dans lifecycle ; assignations System, Config `store` et changements d’autorité Loader appartiennent à `kb_materializer_admin` ; writes/copies de bytecode Loader appartiennent à `kb_materializer_compliance_audit`. Les transferts SOL ne sont pas rematérialisés, car les balance changes core en sont la source canonique. + +## Projections natives restantes + +La couverture maximale d’un décodeur n’implique pas une matérialisation systématique. Une projection n’est ajoutée que lorsqu’elle possède une identité stable, un état cible explicite, une politique d’idempotence et suffisamment de contexte pour ne pas reconstruire une mutation fictive. + +Les prochaines projections sont réparties par propriétaire : + +- `kb_materializer_lifecycle` : créations et allocations System, sans dupliquer les deltas SOL ; +- `kb_materializer_admin` : assignations System, écritures Config opaques commitées et changements d’autorité Loader ; +- `kb_materializer_compliance_audit` : écritures et copies de bytecode Loader, avec hash et préfixe borné sans payload complet ; +- `kb_materializer_staking` : intentions/transitions Stake et Vote commitées. `pre.021` active des projections instructionnelles pour comptes Stake/Vote, autorités, lockup, vote state, retraits et rewards ; un snapshot final exige toujours l’état antérieur/suivant du compte, les crédits cumulés et les sysvars ; +- un matérialiseur transactionnel Compute Budget : profil fusionnant toutes les instructions Compute Budget du message. + +Les surfaces suivantes restent volontairement en decode/audit : + +- précompiles de signature, qui décrivent une vérification runtime sans état métier durable ; +- preuves ZK sans compte de contexte, qui n’ont pas de mutation persistante à projeter ; +- ancien ZK Token Proof Program, dont le runtime Agave `v4.1.1` est un stub sans effet. + +Chaque ajout futur doit indiquer le matérialiseur propriétaire, la source d’état et la politique de transaction, au lieu d’étendre automatiquement `solana_native_lifecycle`. diff --git a/docs/DECODER_SURFACE_AUDIT.md b/docs/DECODER_SURFACE_AUDIT.md new file mode 100644 index 0000000..f59f31b --- /dev/null +++ b/docs/DECODER_SURFACE_AUDIT.md @@ -0,0 +1,49 @@ + + + +# Audit des surfaces de décodage + +Ce document suit les noms de crates de décodeurs réservés, les aliases probables et les surfaces à consolider. Il s'appuie sur les IDL locales du dossier `idls/` et sur les matrices de conception historiques importées dans l'archive de travail. + +## Règle de décision + +Un nom de décodeur devient canonique seulement si au moins un des critères suivants est vérifié : + +- une IDL locale existe avec un `program_id` explicite ; +- le `program_id` est prouvé par corpus local ; +- le rôle de la surface est stable : DEX effectif, router, orderbook, launchpad, vault, lending, staking, etc. ; +- le nom respecte la nomenclature `_` sans alias marketing ambigu. + +Un nom historique sans `program_id` local reste en statut `alias_candidate` ou `watchlist`. Il ne doit pas recevoir de logique métier avant consolidation. + +## Aliases et doublons à traiter + +| Groupe | Canonique recommandé | Crates ou noms concurrents | Statut | Décision provisoire | +|------------------------|-----------------------------------------------------|-------------------------------------------------------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| OKX Labs | `kb_decoder_okx_lab_v1` | `kb_decoder_okx_v1`, `kb_decoder_onchain_labs_dex_v1` | `alias_candidate` | L'IDL locale existe pour `okx_lab_v1` avec le programme `6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma`. Les noms `okx_v1` et `onchain_labs_dex_v1` ne doivent pas être implémentés séparément sans preuve contraire. | +| OKX Router | `kb_decoder_okx_dex_router` | `kb_decoder_okx_v2`, `kb_decoder_onchain_labs_dex_v2` | `alias_candidate` | L'IDL locale existe pour `okx_dex_router` avec le programme `proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u`. Les noms `okx_v2` et `onchain_labs_dex_v2` restent des noms historiques à vérifier. | +| GooseFX | `kb_decoder_goosefx_v2`, `kb_decoder_goosefx_gamma` | `kb_decoder_goosefx_v1` | `watchlist` | `goosefx_v2` et `goosefx_gamma` ont des IDL locales et des programmes distincts. `goosefx_v1` reste une entrée historique sans IDL locale dans l'archive courante. | +| Fusion AMM | `kb_decoder_fusion_amm` | `kb_decoder_fusionamm` | `alias_candidate` | L'IDL locale se nomme `fusion_amm`, mais son metadata interne peut utiliser `fusionamm`. Le crate canonique doit rester lisible : `fusion_amm`. | +| PancakeSwap | `kb_decoder_pancakeswap` | `kb_decoder_pancake_swap` | `alias_candidate` | L'IDL locale se nomme `pancakeswap`. Le crate avec underscore supplémentaire doit rester non canonique. | +| Orca Wavebreak | `kb_decoder_orca_wavebreak` | `kb_decoder_wavebreak` | `alias_candidate` | L'IDL locale se nomme `orca_wavebreak`. Le nom sans protocole est trop ambigu. | +| Jupiter Limit Order v2 | `kb_decoder_jupiter_limit_order_v2` | `kb_decoder_jupiter_limit_order_2` | `alias_candidate` | L'IDL locale utilise `jupiter_limit_order_v2`. Le suffixe `_2` ne doit pas être utilisé. | +| dFlow | `kb_decoder_dflow_v4` | `kb_decoder_dflow_aggregator_v4` | `alias_candidate` | L'IDL locale se nomme `dflow_v4`. Le nom `dflow_aggregator_v4` peut rester comme alias historique mais ne doit pas porter une implémentation distincte sans `program_id` différent. | +| Meteora DAMM v1 | `kb_decoder_meteora_damm_v1` | `kb_decoder_meteora_pools_amm` | `alias_candidate` | Les matrices historiques indiquent que `meteora_pools` peut être un alias de DAMM v1. La consolidation doit être décidée par `program_id`, corpus et naming final. | +| Raydium LaunchLab | `kb_decoder_raydium_launchlab` | `kb_decoder_raydium_launchpad` | `naming_mismatch` | L'IDL locale se nomme `raydium_launchlab`. L'ancien nom `launchpad` décrit la famille métier, mais pas forcément la surface canonique. | +| Raydium Lock | `kb_decoder_raydium_lock` | `kb_decoder_raydium_liquidity_locking` | `naming_mismatch` | L'IDL locale se nomme `raydium_lock`. Le nom `liquidity_locking` est descriptif. La surface canonique doit être confirmée avant implémentation. | +| Raydium Pool v4 | `kb_decoder_raydium_amm_v4` | `kb_decoder_raydium_pool_v4` | `audit_only` | La note historique indique que `raydium_pool_v4` ne doit pas être promu comme décodeur autonome sans `program_id` distinct et corpus local. | + +## Actions recommandées + +- Ne pas supprimer immédiatement les crates aliases tant que le squelette sert de matrice de réservation. +- Ne pas implémenter deux décodeurs pour le même `program_id`. +- Ajouter dans chaque décodeur un mapping explicite `surface_code`, `program_code` et `program_id` dès que le corpus commence. +- Déplacer les aliases historiques vers une table de catalogue ou un document de watchlist quand le support réel commence. +- Supprimer ou fusionner les crates aliases seulement dans un delta dédié, avec validation `cargo build` et manifeste de suppression. + +## Surfaces à ajouter ou renommer plus tard + +- Ajouter `kb_decoder_raydium_launchlab` si la décision est de refléter strictement le nom de l'IDL locale. +- Vérifier si `kb_decoder_raydium_launchpad` doit devenir un alias documentaire ou rester une surface métier. +- Vérifier si `kb_decoder_meteora_pools_amm` doit être fusionné dans `kb_decoder_meteora_damm_v1`. +- Vérifier si les crates `okx_v1`, `okx_v2`, `onchain_labs_dex_v1` et `onchain_labs_dex_v2` doivent être supprimés après confirmation des deux programmes OKX canoniques. diff --git a/docs/DELTA_WORKFLOW.md b/docs/DELTA_WORKFLOW.md new file mode 100644 index 0000000..776aafc --- /dev/null +++ b/docs/DELTA_WORKFLOW.md @@ -0,0 +1,58 @@ + + + +# Workflow delta + +Après le squelette initial, les livraisons doivent être faites sous forme de zip delta. + +## Nommage des zips + +Un delta qui touche la racine du workspace ou plusieurs modules doit utiliser : + +```text +khadhroony-bot2_vX.Y.Z-pre.abc.zip +``` + +Un delta qui ne touche qu'un seul module Rust doit utiliser : + +```text +kb_modulename_vX.Y.Z-pre.abc.zip +``` + +Exemples : + +```text +khadhroony-bot2_v0.1.0-pre.001.zip +kb_logging_v0.1.0-pre.002.zip +``` + +## Fichier `delta.md` + +Chaque zip delta doit contenir un fichier `delta.md` non versionné à la racine du zip. + +`delta.md` doit indiquer : + +- les fichiers ajoutés ; +- les fichiers modifiés ; +- les fichiers à supprimer manuellement ; +- les validations exécutées ; +- les validations non exécutées ; +- les remarques de compatibilité ou d'application du delta. + +## Contenu d'un delta + +Un delta contient uniquement : + +- les fichiers ajoutés ; +- les fichiers modifiés ; +- `delta.md`. + +Il ne doit pas contenir de fichiers inchangés. + +## Règles + +- Ne pas renvoyer un zip complet sauf demande explicite. +- Ne pas modifier `CHANGELOG.md` avant validation d'une version. +- Placer les évolutions prévues dans `ROADMAP.md`. +- Garder `README.md` descriptif. +- Documenter les suppressions dans `delta.md`, car un zip ne supprime pas les anciens fichiers à l'extraction. diff --git a/docs/DEVELOPMENT_ORDER.md b/docs/DEVELOPMENT_ORDER.md new file mode 100644 index 0000000..c93464d --- /dev/null +++ b/docs/DEVELOPMENT_ORDER.md @@ -0,0 +1,91 @@ + + + +# Ordre de développement + +## Principe + +La priorité est de rendre chaque transaction exploitable avant de payer un flux temps réel enrichi. Le développement suit donc l’ordre : acquisition gratuite, transaction canonique, extraction core, décodeurs, matérialisations, puis sources live payantes. + +## Ordre cible après `0.2.x` + +- [x] `0.1.x` : logging, configuration, clients HTTP/WS standard, rôles et démos RPC. +- [x] `0.2.0` à `0.2.5` : conventions PostgreSQL, stores raw/core historiques et diagnostics SQL. +- [x] `0.3.0` : cadrage Agave local historique, ensuite abandonné comme axe actif après mesures réelles. +- [x] `0.3.1` : transaction canonique, observations légères, validation PostgreSQL réelle et consolidation de la baseline SQL. +- [x] `0.3.2` : modèle canonique et adaptateur HTTP `getTransaction`. +- [x] `0.3.3` : backfill gratuit par signatures, programme, token et pool, avec parcours avant/après et arrêt coopératif borné. +- [~] `0.3.4` : extraction core, navigateur de candidats, intégrité et replay validés ; derniers tests automatisés de rollback/annulation à exécuter. +- [ ] `0.4.0` : infrastructure commune de décodage et de matérialisation. +- [ ] `0.4.1+` : décodeurs et matérialisations Solana Core/SPL. +- [ ] `0.5.x` : Pump. +- [ ] `0.6.x` : Meteora. +- [ ] `0.7.x` : Raydium. +- [ ] `0.8.x` : Orca. +- [ ] `0.9.x` : Jupiter, routeurs et surfaces complémentaires. +- [ ] `0.10.x` : Helius `transactionSubscribe`, Yellowstone gRPC, fallback standard et comparaison fournisseur. +- [ ] `0.11.x` : wallet, sécurité et démos devnet. +- [ ] `0.12.x` : listeners métier et trading assisté. + +## Ordre technique de stockage + +1. Maintenir une baseline SQL courante ne contenant que les tables actives. +2. Normaliser chaque source vers un contrat canonique dans `kb_model`. +3. Écrire une transaction unique dans `kb_sol_raw_transactions`. +4. Écrire une observation légère dans `kb_sol_obs_transaction_observations`. +5. Extraire les tables `kb_sol_core_*` depuis la transaction canonique. +6. Ajouter les observations programme/instruction nécessaires aux décodeurs. +7. Ajouter les tables `kb_sol_decode_*` et `kb_sol_mat_*` par surface. +8. Ajouter les sources live payantes seulement lorsque les décodeurs peuvent exploiter immédiatement le flux. + +## Séquence par surface + +```text +program ids + -> corpus de signatures + -> backfill gratuit + -> decodeur maximal + -> événements stables + -> matérialisation complète + -> replay et anti-régression + -> listener live +``` + +## Démos + +`kb_app_demo` reste le shell unique pour : + +- diagnostics DB ; +- backfills ; +- inspection canonique/core ; +- couverture des décodeurs ; +- comparaison future des sources live ; +- wallet et sécurité. + +## Contraintes permanentes + +- Ne pas créer de schémas PostgreSQL applicatifs explicites. +- Utiliser le format `kb_sol__`. +- Ne pas placer de structures Rust dans `queries/`. +- Ne pas faire dépendre les décodeurs du store concret. +- Ne pas faire dépendre les décodeurs du fournisseur d’acquisition. +- Intégrer Helius et Yellowstone dans `kb_rpc`, sans crate provider séparée. +- Ne pas modifier `CHANGELOG.md` avant validation locale du jalon. + +## État de clôture `0.3.4-pre.011` + +L’ordre technique est concrétisé jusqu’à l’écriture core et aux outils de replay : + +```text +backfill HTTP +-> transaction canonique +-> sélection raw bornée +-> extraction core pure +-> commit PostgreSQL atomique +-> ledger version/hash +-> sélection et export de corpus +``` + +Les validations réelles couvrent 70 transactions core, l’intégrité SQL, l’écriture CSV, le skip et le force replay sur 14 signatures. `pre.011` automatise le dernier contrôle de rollback PostgreSQL et rend les attentes réseau/retry annulables. + +L’ancien jalon `0.3.5` est supprimé. Après validation de `pre.011` et mise à jour du changelog, la prochaine version active est directement `0.4.0`. diff --git a/docs/DEVNET_EXECUTION.md b/docs/DEVNET_EXECUTION.md new file mode 100644 index 0000000..ad48772 --- /dev/null +++ b/docs/DEVNET_EXECUTION.md @@ -0,0 +1,119 @@ + + + +# Validation d’exécution Devnet + +Ce document décrit le premier parcours réseau réel de la couche d’exécution native et sa fenêtre Tauri Devnet. Le test backend reste opt-in et aucune activation Mainnet n’est autorisée. + +## Périmètre + +Le parcours exécute une instruction System transfer depuis le wallet temporaire persistant du profil `local_devnet` vers un destinataire éphémère créé uniquement pour le test. + +```text +wallet persistant + -> vérification du genesis hash Devnet + -> contrôle du destinataire et du minimum rent-exempt + -> lecture du solde + -> airdrop plafonné si nécessaire + -> plan System transfer + -> blockhash et estimation des frais + -> simulation exacte + -> signature locale + -> envoi avec preflight + -> confirmation bornée + -> getTransaction + -> insertion canonique + -> extraction core + -> decode replay System +``` + +Les clés privées restent dans `kb_wallet`. Elles ne sont ni sérialisées dans les résumés, ni envoyées à une WebView, ni écrites dans PostgreSQL ou les logs. + +## Préconditions + +- le profil `local_devnet` doit rester configuré sur le genesis hash Devnet officiel ; +- `temporary_wallet_enabled`, `temporary_wallet_persist` et `devnet_send_enabled` doivent être actifs ; +- la simulation et la confirmation opérateur doivent rester obligatoires ; +- PostgreSQL doit être disponible pour la validation canonique/core/decode ; +- le rôle `http_queries` doit accepter `getTransaction` et les lectures d’exécution ; +- le rôle `http_transactions` doit accepter simulation, envoi et lecture des statuts. + +Le wallet persistant par défaut est créé sous : + +```text +wallets/temporary/local_devnet/local-devnet-operator.json +``` + +Ce fichier n’est pas chiffré. Il est exclusivement destiné à Localnet/Devnet et ne doit jamais recevoir de fonds réels. + +## Exécution du test réel + +Depuis la racine du workspace : + +```bash +KB_DEVNET_EXECUTION_TEST=1 \ +KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \ +KB_DEVNET_WALLET_DIR='./wallets/temporary/local_devnet' \ +KB_DEVNET_AIRDROP_LAMPORTS=0 \ +KB_DEVNET_TRANSFER_LAMPORTS=1000000 \ +cargo test -p kb_pipeline optional_devnet_system_transfer_from_env -- --nocapture +``` + +Variables : + +- `KB_DEVNET_EXECUTION_TEST=1` autorise explicitement le test réseau ; +- `KB_POSTGRES_TEST_URL` sélectionne la base de validation ; +- `KB_DEVNET_WALLET_DIR` peut isoler le wallet utilisé par le test ; +- `KB_DEVNET_AIRDROP_LAMPORTS` fixe le montant maximal demandé au faucet lorsque le solde est insuffisant ; +- `KB_DEVNET_TRANSFER_LAMPORTS` fixe le transfert System réel. + +Les valeurs restent soumises aux plafonds de `ExecutionConfig`. Une valeur d’airdrop supérieure à `devnet_airdrop_max_lamports` ou un transfert supérieur à `devnet_max_spend_lamports` est refusé avant tout appel mutable. + +## Résultat attendu + +Pour un destinataire inexistant, le pipeline appelle `getAccountInfo`, puis `getMinimumBalanceForRentExemption` avec une longueur de données nulle. Le transfert est refusé avant simulation lorsqu’il est inférieur au minimum retourné. Lorsqu’une simulation échoue malgré ces contrôles, l’erreur remontée conserve l’erreur runtime structurée et les vingt premières lignes de logs. + +Le test doit confirmer : + +- cluster classifié `Devnet` ; +- simulation exacte réussie ; +- signature locale vérifiée ; +- signature RPC identique à la signature calculée ; +- statut `confirmed` ou `finalized` ; +- transaction canonique présente ou déjà existante ; +- extraction core terminée ou déjà courante ; +- decode replay System terminé sans input unmatched ou failed. + +L’airdrop peut être absent lorsque le wallet possède déjà un solde suffisant. Le destinataire reste éphémère et sa clé privée n’est pas persistée. Après signature, tout échec ultérieur conserve la signature dans le résumé avec un diagnostic de l’étape concernée. + +## Fenêtre Tauri + +`demo_execution_solana_core` est accessible depuis la fenêtre principale. Elle expose uniquement les profils Devnet utilisant un wallet temporaire persistant. Elle permet de générer une adresse destinataire jetable, de simuler exactement le message ou de signer/envoyer après confirmation opérateur. Le plan, les comptes, les signataires, la simulation, la confirmation et les résultats canonical/core/decode sont affichés en lecture seule. La clé privée du wallet source reste exclusivement dans le backend Rust. + +Un airdrop demandé par la fenêtre reste soumis au plafond du profil et peut échouer lorsque le faucet public est limité. Un wallet déjà financé peut être utilisé avec une valeur d’airdrop nulle. + +## Limites + +- le faucet Devnet peut appliquer des limites temporaires ; +- `getTransaction` peut devenir disponible après la confirmation de statut, d’où les retries bornés ; +- le parcours utilise actuellement une transaction legacy avec recent blockhash ; +- durable nonce, ALT et transactions v0 seront validés dans des tranches dédiées ; +- aucune opération Mainnet n’est autorisée par ce test. + +## Validation de clôture `0.4.2` + +Le 13 juillet 2026, la commande complète a été exécutée avec : + +```bash +KB_DEVNET_EXECUTION_TEST=1 \ +KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \ +KB_DEVNET_WALLET_DIR='./wallets/temporary/local_devnet' \ +KB_DEVNET_AIRDROP_LAMPORTS=0 \ +KB_DEVNET_TRANSFER_LAMPORTS=1000000 \ +cargo test -p kb_pipeline -- --nocapture +``` + +Les 56 tests de `kb_pipeline` ont réussi, y compris `optional_devnet_system_transfer_from_env`. Le wallet était déjà financé, aucun airdrop n’a été demandé et le parcours a confirmé la chaîne complète jusqu’au replay post-exécution. Les 45 tests de `kb_store_pg` ont également réussi avec PostgreSQL réel. + +Cette validation ne généralise pas l’autorisation à toutes les opérations administratives stateful. Elle prouve le parcours d’orchestration commun ; ALT, Config, Feature, Slashing, ZK, Stake, Vote et loaders restent soumis à leurs contrôles spécifiques avant toute future activation mutable. + diff --git a/docs/EXECUTION_MODEL.md b/docs/EXECUTION_MODEL.md new file mode 100644 index 0000000..95cc292 --- /dev/null +++ b/docs/EXECUTION_MODEL.md @@ -0,0 +1,387 @@ + + + +# Modèle d'exécution + +Ce document décrit la couche d'exécution universelle du workspace. + +## Séparation des responsabilités + +Un décodeur lit une transaction existante. Un exécuteur prépare une opération future. Les deux couches peuvent partager une surface canonique, mais elles ne doivent pas dépendre l'une de l'autre. + +```text +kb_decoder_amm_pump_swap -> observation, décodage, audit +kb_executor_amm_pump_swap -> construction future d'instructions d'achat/vente +``` + +## Bibliothèques universelles + +Le trading est le premier consommateur du workspace, pas la limite de ses crates. Les exécuteurs doivent pouvoir être réutilisés par une CLI, un worker, un outil d'administration, une application de jeu, une application Tauri ou un service autonome. + +Cette universalité implique : + +- un intent typé indépendant de l'interface utilisateur ; +- un plan déterministe sans I/O ; +- des builders couvrant les opérations officiellement appelables, y compris administratives ou dangereuses ; +- des politiques de sécurité séparées du builder ; +- une exposition UI volontairement plus étroite que la capacité de la bibliothèque ; +- aucune dépendance vers les décodeurs pour construire ou signer une transaction. + +Une opération dangereuse ne doit pas être supprimée de l'exécuteur. Elle doit être implémentée, testée, classifiée et soumise à une politique renforcée. `kb_app_demo` peut ne jamais l'exposer. + +## Couches + +- `kb_execution_api` définit les contrats communs des exécuteurs. +- `kb_execution_safety` regroupe les validations avant simulation, signature ou envoi. +- `kb_execution_solana` assemble les plans en messages/transactions Solana et orchestre la signature sans RPC. +- `kb_executor_solana_core` construit les instructions des programmes natifs. +- `kb_wallet` isole les secrets et fournit des signataires. +- `kb_rpc` fournit simulation, envoi et confirmation. +- `kb_pipeline` peut orchestrer la validation post-exécution par réingestion et replay. +- `kb_app_demo` ne fait qu'orchestrer une sélection sûre de ces capacités. + +## Pipeline obligatoire + +```text +intent + -> execution plan + -> policy checks + -> transaction build + -> simulation / dry-run + -> signature + -> send + -> confirmation + -> post-execution decode replay validation +``` + +Construction, signature, envoi et validation ne doivent jamais être fusionnés dans une fonction opaque. + +## Politique de couverture + +`Supported` signifie que le builder et son contrat de comptes/signataires sont implémentés. `Unsupported(reason)` reste acceptable uniquement lorsqu'une surface n'est pas appelable par transaction, a été retirée du runtime, ne dispose pas encore d'une source officielle suffisante ou constitue une dette temporaire explicitement planifiée. + +L'objectif de clôture de `kb_executor_solana_core` est de ne laisser aucune instruction native officiellement appelable oubliée. Les variantes historiques uniquement décodables ne doivent pas être artificiellement réémises. + +## Adaptateurs RPC de simulation + +`0.4.2-pre.004` ajoute dans `kb_rpc` les premières lectures nécessaires avant signature : + +```text +getGenesisHash +getLatestBlockhash +getFeeForMessage +simulateTransaction +``` + +Le genesis hash sert à classifier les clusters publics connus. Le réseau de production est nommé Mainnet dans le contrat public du workspace ; le hash est inchangé et certains endpoints/CLI conservent encore l’alias historique `mainnet-beta`. Un hash inconnu reste explicite et n’est pas automatiquement assimilé à Devnet ou Mainnet. Le blockhash et sa dernière block height valide sont conservés séparément. L’estimation des frais accepte une valeur nulle lorsque le message référence un blockhash expiré. + +La simulation accepte une transaction base64 non signée lorsque `sigVerify = false`; `replaceRecentBlockhash = true` permet au nœud de remplacer le blockhash avant simulation. Les erreurs runtime restent un résultat de simulation typé et ne sont pas confondues avec une erreur HTTP ou JSON-RPC. + +`kb_rpc` ne fabrique pas le contexte de sécurité : cluster attendu, âge du blockhash, nonce account et nonce authority sont fournis explicitement lors de la conversion vers `ExecutionSimulationResult`. Depuis `pre.013`, `getAccountInfo` possède aussi un mode données complètes borné : le mode metadata-only conserve `dataSlice.length = 0`, tandis que `confirmed_with_data(max_data_bytes)` exige un tuple base64 complet dont la longueur décodée égale `space`. + +## Assemblage et signature Solana + +`0.4.2-pre.005` introduit `kb_execution_solana`, frontière commune entre les exécuteurs et les adaptateurs RPC. La crate convertit les `PlannedInstruction` en instructions SDK, compile le message avec son fee payer et sa source de blockhash, puis vérifie que les signataires réellement compilés correspondent exactement au contrat du plan. + +La transaction non signée fournit deux sorties distinctes : + +```text +message base64 -> getFeeForMessage +transaction non signée -> simulateTransaction(sigVerify=false, replaceRecentBlockhash=false) +``` + +Le résultat de simulation est lié au hash exact du message et à la valeur de blockhash ou nonce compilée. Une simulation avec remplacement de blockhash reste utile au diagnostic, mais ne peut pas autoriser la signature du message original. La signature refuse un résultat provenant d’un autre message ou d’une autre valeur nonce, réévalue `kb_execution_safety`, exige la décision `Allow`, puis résout tous les signataires via l’interface Solana `Signer`. Les signataires manquants, supplémentaires ou dupliqués sont refusés. La transaction signée reste en mémoire/backend et n’est ni envoyée ni exposée automatiquement à Tauri. + +La sérialisation transactionnelle utilise le schéma officiel `wincode` fourni par les types Solana et applique la limite réseau de 1 232 octets. Aucun appel direct à `bincode` n’est ajouté. + +### Chemin durable nonce réel — `pre.013` + +Le lifecycle d’un compte nonce et la consommation d’un durable nonce sont deux contrats distincts. Les opérations `initialize`, `advance`, `authorize`, `withdraw` et `upgrade` administrent le compte avec une politique `Latest`. Une transaction métier utilisant un nonce durable suit le chemin stateful suivant : + +```text +getAccountInfo complet et borné à la taille nonce officielle + -> validation owner System Program / non-executable / space exact + -> décodage wincode Versions::Current(State::Initialized) + -> concordance compte et autorité avec ExecutionBlockhashPolicy + -> Message::new_with_nonce + -> AdvanceNonceAccount en instruction 0 + -> valeur nonce comme recent_blockhash du message + -> simulation exacte sans remplacement + -> signature du même hash de message et de la même valeur nonce +``` + +`kb_executor_solana_core` ajoute l’autorité nonce aux signataires requis du plan métier. `kb_execution_solana` refuse les états legacy ou non initialisés, conserve le plan source immuable, expose un plan effectif avec l’avance injectée et exige que le SDK reconnaisse la transaction comme durable nonce. La lecture RPC, l’assemblage, la simulation et la signature restent des étapes séparées. + + +### Builders Address Lookup Table — `pre.015` + +`kb_executor_solana_core` couvre les cinq opérations client de l’interface Address Lookup Table : création, extension, gel, désactivation et fermeture. Le builder stateless garantit le program ID, le payload, l’ordre des comptes et les signataires exacts, mais ne prétend pas connaître l’état courant du compte ni le coût de rent calculé par le runtime. + +```text +intent ALT typé + -> builder officiel exact + -> plan avec autorité/payer explicites + -> lecture RPC stateful du compte et du slot + -> contrôle owner / autorité / état / capacité / cooldown + -> calcul du top-up rent-exempt et simulation exacte + -> politique de dépense + -> signature et envoi +``` + +La création dérive la table depuis l’autorité et un slot récent. L’extension conserve l’ordre des adresses, exige une liste non vide et borne l’input à la capacité maximale officielle ; la capacité restante et le top-up effectif doivent être contrôlés sur le compte réel. Le gel est irréversible. La fermeture n’est autorisée qu’après désactivation et expiration du cooldown lié aux slots. `requested_spend_lamports` reste nul dans le plan ALT parce qu’aucun montant de rent n’est encodé directement dans l’instruction ; l’orchestrateur doit calculer le top-up à partir du compte réel et du minimum rent-exempt, le comparer au plafond de dépense, puis confirmer l’instruction par simulation. + + +### Précompiles de signature — `pre.016` + +Les précompiles Ed25519, secp256k1 et secp256r1 sont des vérifications natives, pas des signatures de transaction. Leur plan ne déclare aucun compte applicatif ni signataire propre ; le fee payer signe uniquement la transaction. Le programme métier qui consomme la preuve doit inspecter l’instruction correspondante et appliquer lui-même son contrat d’autorisation. + +```text +message + signature + clé/adresse + -> builder inline ou table d’offsets officielle + -> instruction précompile sans comptes + -> positionnement transactionnel exact + -> programme consommateur qui inspecte le sysvar d’instructions + -> simulation exacte + -> signature Solana du fee payer et des signataires métier distincts +``` + +Les formes inline Ed25519 et secp256r1 utilisent `u16::MAX` comme référence à leurs propres données. secp256k1 encode des index d’instruction `u8` sans sentinelle ; la forme inline et les références locales du builder exigent donc que l’instruction secp256k1 soit à l’index transactionnel `0`. Un futur assembleur multi-plans doit refuser ou réécrire explicitement toute combinaison qui violerait cette position. + +Les tables avancées acceptent des références externes et un buffer local ajouté après les offsets. Le builder borne le nombre d’entrées à 255, la taille totale à 65 535 octets et les plages locales connues. Il ne lit pas les données des autres instructions : leur cohérence cryptographique est vérifiée par le runtime et leur signification métier par le programme consommateur. + +La signature secp256r1 est fournie au format compact `r || s`; le builder exige des composants non nuls et la forme low-S imposée par le runtime. Le builder secp256k1 reçoit la signature compacte, le recovery ID et l’adresse Ethereum déjà dérivée ; il ne manipule aucune clé privée et n’active aucun helper `bincode`. Le runtime secp256k1 ne garantit pas la canonicalité low-S : lorsqu’elle est requise, cette politique appartient au programme consommateur ou à une validation métier explicite. + + +### Config, Feature, Slashing et ZK ElGamal — `pre.017` + +Les opérations administratives natives restent des plans déterministes sans accès RPC implicite. + +```text +état/rent fourni par l’orchestrateur + -> builder exact + -> plan et signataires + -> lecture stateful de confirmation + -> simulation exacte +``` + +Config encode manuellement le contrat `ConfigKeys + données sérialisées` afin de ne pas activer le helper `bincode` de l’interface. Feature reproduit les séquences officielles d’activation et de révocation. Les montants de rent sont des entrées explicites comptabilisées dans le plafond de dépense. + +La preuve de bloc dupliqué Slashing est indivisible : transfert de préfinancement, instruction Ed25519 et instruction Slashing doivent conserver leurs positions. Le plan refuse donc durable nonce et exige un recent blockhash normal. Le compte de preuve, la fenêtre temporelle, le montant rent-exempt et la rétention avant fermeture relèvent de l’orchestrateur stateful. + +ZK ElGamal accepte une preuve inline ou une preuve dans un compte, avec création optionnelle d’un contexte déjà préalloué. La fermeture exige l’autorité comme signataire. Le builder vérifie discriminant, taille et comptes, mais ne crée pas le contexte et ne recalcule pas la preuve cryptographique. L’ancien ZK Token Proof est historique et reste `decode-only`. + +## Stake et Vote dans la bibliothèque + +`pre.018` ajoute les vingt-quatre helpers clients actuels de `solana-stake-interface 4.3.1` sous forme de plans déterministes. Les formes composées conservent l’ordre officiel : création System puis initialisation Stake, délégation ajoutée en dernier, allocation/assignation avant split. Les rôles staker/withdrawer sont typés par `StakeAuthorizationKind`. + +`pre.019` ajoute les vingt variantes wire Vote actuelles et quatre créations composées legacy/V2. Les autorités Ed25519/BLS, commissions, collecteurs, votes, switch proofs, compact updates et TowerSync sont encodés avec l’enum officielle `VoteInstruction` et `wincode`. Les créations, retraits et dépôts de récompenses déclarent leurs lamports dans le plafond de dépense. + +La construction reste stateless. Avant simulation puis envoi, l’orchestrateur doit charger et valider l’état des comptes Stake/Vote, les autorités, le lockup, le rent, le minimum de délégation, les epochs d’activation/désactivation, la compatibilité merge/split/move, la version Vote, les collecteurs et l’admissibilité runtime de la tour. `Redelegate` reste historique `decode-only`, car l’interface officielle le déclare déprécié et non activable. + +## Loaders dans la bibliothèque + +`pre.020` ajoute onze plans Loader v3 et neuf plans Loader v4. Loader v3 utilise les helpers de `solana-loader-v3-interface 8.x` et leur schéma `wincode` pour créer des buffers, écrire, déployer, upgrader, modifier les autorités, fermer et étendre. Loader v4 conserve le contrat officiel des sept discriminants, mais son wire est construit localement et explicitement parce que les helpers publiés sont conditionnés par `bincode` et qu’aucun schéma `wincode` n’est exposé. + +Les plans de création conservent l’ordre atomique System puis Loader. Les écritures sont bornées, les offsets contrôlés contre les dépassements, les comptes/signataires reproduisent l’interface officielle et les lamports explicitement transférés sont inclus dans `requested_spend_lamports`. Les loyers calculés dynamiquement par le runtime ne sont pas inventés par le builder. + +Avant simulation, l’orchestrateur stateful doit vérifier owner, état courant, autorité, taille/capacité, rent, existence des comptes Program/ProgramData/buffer, relations dérivées et admissibilité de fermeture ou d’extension. Les opérations Loader restent hors de la démo opérateur. BPF Loader v1/v2 sont historiques `decode-only`; Native Loader correspond au déploiement du logiciel validator et n’expose pas d’instruction client autonome. + + +## Préflight stateful natif — `pre.022` + +Les builders restent déterministes et sans I/O. `kb_pipeline::inspect_solana_core_stateful_readiness` ajoute une étape séparée avant la simulation pour les opérations dont l’admissibilité dépend de comptes ou de l’époque courante. + +```text +SolanaCoreOperation + -> plan stateless + -> préflight stateful Localnet/Devnet + -> rapport Ready / Blocked / NotRequired + -> simulation exacte +``` + +Le préflight vérifie le genesis hash attendu et utilise uniquement des RPC typés. Il couvre : + +- ALT : PDA de création, slot récent, owner, autorité, capacité, rent top-up, état actif/désactivé et cooldown de fermeture ; +- Config : absence/réutilisation contrôlée, allocation calculée, owner et espace disponible ; +- Feature : compte absent ou System réutilisable, rent mesuré et état pending avant révocation ; +- Slashing : compte de preuve complet et borné, deux shreds length-prefixed, fenêtre d’un epoch, rent du rapport, destination retenue et délai de fermeture ; +- ZK ElGamal : plage de preuve, owner/space/rent du contexte préalloué, état zéro avant écriture, puis autorité et discriminant avant fermeture. + +Le rapport contient des codes de contrôle stables, des faits mesurés et le plus haut slot de contexte observé. Il ne signe, ne simule et n’envoie aucune transaction. Testnet et Mainnet sont refusés dans cette couche tant que les parcours Localnet/Devnet ne sont pas validés. + +## Préflight stateful SPL Token classique — `0.4.4-pre.005` + +Le même découpage s’applique à SPL Token : `kb_executor_spl_token` construit un plan universel sans I/O, puis `kb_pipeline::inspect_spl_token_stateful_readiness` vérifie l’état Localnet ou Devnet avant simulation. Le décodeur ne réalise aucune lecture RPC. + +Le préflight lit des données complètes bornées et reproduit les layouts classiques Mint, Account et Multisig. Il contrôle notamment owner programme, initialisation, mint/decimals, état frozen, solde et réserve native, delegate/allowance, autorités spécialisées, seuil multisig et rent des comptes préparés. Les montants restent des entiers bruts exacts. Une précondition métier invalide produit `Blocked`; une erreur de transport ou de forme reste une erreur `kb_core`. + +La preuve réseau est séparée en deux niveaux. Le test opt-in de `pre.005` vérifie en lecture seule un `TransferChecked` sur des comptes Devnet fournis explicitement. Le scénario opérateur complet doit ensuite créer des comptes classiques sans ATA, simuler par défaut, et ne signer/envoyer qu’avec une autorisation distincte ; après confirmation, il doit réutiliser hydratation canonique, extraction core, replay, projections et second replay idempotent. + +`pre.005-delta-fix-001` relie ce préflight au chemin d’exécution commun. La simulation constitue une API autonome sans store et conserve le plan, le rapport stateful, le blockhash, les frais et le résultat runtime. La soumission est une seconde API : elle refuse tout signataire absent du wallet de profil, puis applique confirmation, hydratation, extraction, decode replay, requête matérialisée bornée et preuve d’idempotence. Cette restriction de signataire appartient à l’orchestrateur actuel, pas à l’exécuteur universel ni au wire multisig. + +Le 15 juillet 2026, ce parcours a réussi en simulation puis avec un envoi `TransferChecked` réel sur Devnet, entre deux comptes auxiliaires classiques créés sans dépendance ATA. `pre.005-delta-fix-002` durcit la preuve opt-in : résultat d’envoi présent, confirmation `confirmed` ou `finalized`, premier replay sans échec ni refus, matérialisation Token présente, second replay sans échec, refus ou nouvelle sortie, et signature imprimée pour l’audit. + +`0.4.4-pre.006` compose ce contrat sans le contourner. La préparation interne génère trois keypairs éphémères, mesure le rent exact puis construit trois `SystemCreateAccount` officiels avec le propriétaire Token classique et les tailles 82/165/165. Chaque création est simulée, signée par le payeur et le nouveau compte, confirmée, puis relue pour vérifier owner, rent, taille, absence d'executable et données entièrement nulles. Les clés de création peuvent ensuite disparaître : les initialisations Token n'en ont pas besoin. Le lifecycle n'utilise pas ATA. Après autorisation destructive explicite, il exécute onze transactions séparées : initialisation du mint, initialisation des deux comptes, mint checked, transfer checked, approve checked, revoke, burn checked des deux soldes puis fermeture des deux comptes. Une étape incomplète arrête la séquence ; la suivante n'est construite qu'après confirmation, hydratation, extraction, décodage, matérialisation et second replay idempotent de la précédente. + +Une confirmation RPC interrompue ne rend pas la séquence aveuglément rejouable : les initialisations Token ne sont pas idempotentes. La reprise exige donc l'index de la prochaine étape et la signature confirmée de son prédécesseur. Le pipeline hydrate cette signature, extrait le core, impose le decode Token commité, vérifie l'opération matérialisée attendue et un second replay sans sortie avant de construire l'étape suivante. Un délai borné entre étapes réduit les rafales vers le RPC public ; il ne transforme pas un statut inconnu en succès. + +Cette reprise a été exercée sur Devnet le 15 juillet 2026 après un throttling `429` puis une confirmation momentanément incomplète. L'initialisation du compte destination déjà finalisée a été récupérée depuis sa signature canonique ; les étapes 3 à 10 ont ensuite achevé `MintToChecked`, `TransferChecked`, `ApproveChecked`, `Revoke`, deux `BurnChecked` et deux `CloseAccount`. Les neuf étapes récupérées ou envoyées ont chacune validé une matérialisation et un second replay idempotent. + +`0.4.4-pre.007` n'ajoute aucune nouvelle couche d'exécution. La fenêtre Tauri existante construit un intent `TransferChecked` mince, puis appelle le même `execute_devnet_spl_token`. Le journal opérateur interroge au maximum 500 lignes via `MaterializedEventFilter`; les filtres exacts mint, compte et opération sont appliqués au payload typé en mémoire. La frontière UI conserve slots et montants bruts sous forme de chaînes et ne revendique aucun snapshot final ni agrégat OHLC. + +`docs/NATIVE_SOLANA_EXECUTION_MATRIX.json` constitue l’inventaire machine-readable de clôture. Un test Rust de `kb_executor_solana_core` charge cette matrice et impose dix-huit surfaces, 109 opérations appelables exactement égales à `SOLANA_CORE_OPERATION_CODES`, ainsi qu’une justification non vide pour chaque surface sans builder client. Le validateur Python provisoire a été supprimé. + +## Préflight stateful ATA — `0.4.5-pre.004` + +`kb_executor_spl_associated_token_account` construit exactement un builder officiel parmi +`Create`, `CreateIdempotent` et `RecoverNested`. Le Token Program classique ou Token-2022 fait +partie de l’intent et de la dérivation ; la bibliothèque ne lie toujours pas un plan à un endpoint +ou à un wallet concret. + +`kb_pipeline::inspect_spl_associated_token_account_stateful_readiness` est la frontière I/O +Localnet/Devnet. Elle vérifie le genesis, le Program ID ATA, la simulation obligatoire, les mints, +les PDA, l’absence stricte pour `Create`, la compatibilité absence/compte existant pour +`CreateIdempotent`, les trois comptes Token de `RecoverNested`, tous les signataires et le solde du +payer couvrant le plafond rent-plus-frais. + +Les comptes Token-2022 peuvent dépasser 165 octets. Le préflight valide leur préfixe Token Account +commun et laisse les extensions opaques ; il ne commence donc pas `0.4.6`. Le rent minimal du +compte de base et le plafond sont contrôlés avant simulation. La taille et le rent exacts imposés +par des extensions restent vérifiés par le runtime pendant la simulation obligatoire. + +Le rapport `Ready`/`Blocked` ne signe, ne simule et n’envoie rien. L’orchestration de soumission, +confirmation et replay reste une tranche séparée afin qu’un statut RPC inconnu ne provoque jamais +une recréation ATA aveugle. + +## Orchestration ATA Devnet — `0.4.5-pre.005` + +`execute_devnet_spl_associated_token_account` réutilise les frontières communes validées : wallet +persistant de profil, plan typé, préflight stateful, message exact, frais, simulation liée au hash, +signature après autorisation, envoi et confirmation. Après confirmation, la signature déjà connue +est hydratée par tentatives bornées ; chaque tentative RPC autorise deux reprises internes pour les +réponses transitoires telles que `429`, sans reconstruire ni renvoyer l’instruction ATA. + +Le replay sélectionne toujours le Program ID ATA. Pour une cible SPL Token classique, il sélectionne +aussi le Program ID Token afin de décoder les CPI internes, tout en laissant chaque matérialiseur +posséder uniquement ses faits. Pour Token-2022, il ne prétend pas décoder les instructions ou +extensions CPI générales réservées à `0.4.6`. La seconde passe inclut l’état matérialisé et doit ne +produire aucune nouvelle sortie. + +La bibliothèque d’exécution ATA reste indépendante du cluster et du wallet. L’orchestrateur Devnet +fourni ne peut signer qu’avec le wallet persistant du profil ; un `RecoverNested` exigeant une autre +clé est donc refusé comme signataire indisponible. La fenêtre Tauri existante expose seulement les +modes représentatifs `Create` et `CreateIdempotent`, la dérivation readonly et un journal ATA borné. + +Le 16 juillet 2026, le parcours classique `CreateIdempotent` a été confirmé deux fois sur le même +ATA Devnet. La première signature `2H9V…CPbP` et la seconde `3wWT…fv6q` ont chacune réussi +simulation, confirmation, hydratation canonique, extraction, décodage, projection lifecycle unique +et second replay sans nouvelle sortie. La seconde exécution prouve le chemin compte existant +compatible ; elle reste un fait transactionnel distinct et non un doublon de replay. + +Le garde-fou Token-2022 a également refusé correctement le Program ID `TokenzQd…` lorsqu’il était +fourni à la place d’un compte mint : owner, layout Mint et état initialisé ne correspondaient pas. +La preuve positive utilise ensuite le mint contrôlé `3DxK…KTE`, initialisé sur 82 octets et possédé +par Token-2022. `CreateIdempotent` a créé puis réutilisé l’ATA `6WfC…C9Y`, compte de 170 octets +initialisé pour `DwuA…g8B2` avec l’extension `ImmutableOwner`. Les signatures `bMMj…QAJ` au slot +476702448 et `4RZK…VSG` au slot 476703343 ont chacune été simulées, confirmées, hydratées, décodées +et matérialisées une fois, puis ignorées au second replay. L’état préalable observé établit la +réutilisation de la seconde transaction, tandis que la projection parent conserve correctement +`created_or_reused` car l’instruction seule ne distingue pas ces branches. + +`RecoverNested` reste volontairement hors UI. Son parcours contrôlé opt-in reçoit deux mints et une +fixture nested déjà préparée, puis réutilise le même orchestrateur afin de vérifier simulation, +confirmation, fermeture du nested ATA, destination cohérente, deux projections ATA/risk et second +replay idempotent sans introduire de décodeur Token-2022 général. + +La preuve Devnet classique du 16 juillet 2026 utilise `3tdX…FbF5`, ATA wrapped SOL du wallet, +comme owner du nested ATA `AAyv…gti5`. Celui-ci contenait exactement 1 token, soit +1 000 000 000 unités brutes du mint `3fd5…hC9A`, et la destination `69TP…fyWp` était vide. La +signature `37LG…HskM` a transféré le montant complet, fermé le nested ATA, conservé l’owner ATA et +la destination initialisés, produit exactement les deux sorties parent `nested_ata_recovered` et +`nested_ata_anti_pattern_recovered`, puis réussi le second replay sans doublon. Les mutations CPI +Token de transfert et fermeture ne sont pas recopiées dans les projections parent ATA. + +## Soumission et confirmation RPC + +`0.4.2-pre.008` complète la frontière réseau sans fusionner les étapes : + +```text +SignedSolanaTransaction + -> sendTransaction avec preflight obligatoire + -> contrôle de la signature primaire retournée + -> getSignatureStatuses borné + -> getBlockHeight pour détecter l’expiration + -> confirmed / finalized / failed / expired / timed_out +``` + +`sendTransaction` signifie uniquement que le nœud a accepté la transaction signée pour relay. La confirmation reste une opération séparée. La politique borne le nombre de polls et leur intervalle, conserve le dernier slot et la dernière block height observée, puis termine explicitement sur succès, erreur runtime, expiration du recent blockhash ou timeout. + +`requestAirdrop` et `getBalance` sont ajoutés comme primitives génériques de laboratoire. Le plafond d’airdrop Devnet appartient à la configuration et sera appliqué par l’orchestrateur du parcours réel ; la méthode RPC reste indépendante de Tauri et du wallet concret. + +## Wallet temporaire persistant + +`0.4.2-pre.003` introduit un signer de laboratoire dans `kb_wallet` : + +```text +wallets/temporary//.json +``` + +Il peut être généré en mémoire ou persisté afin de conserver la même adresse entre plusieurs démarrages, recevoir un airdrop devnet, créer des comptes et signer les transactions de validation. Le fichier utilise le format JSON Solana standard, reste exclu du dépôt et reçoit des permissions privées sur Unix. + +Ce backend n'est pas un wallet de production : il n'est pas chiffré et les profils fournis l'interdisent sur mainnet. Les futurs backends chiffrés, coffre système et hardware wallet devront conserver la même frontière de signature afin que les exécuteurs ne changent pas. + +## Garde-fous obligatoires + +- dry-run activé par défaut ; +- simulation RPC obligatoire avant envoi ; +- cluster réel comparé au cluster attendu ; +- contrôle du wallet source et des signataires requis ; +- plafond de dépense, de frais et de compute-unit price ; +- fraîcheur du blockhash ou état nonce durable complet, courant, initialisé et concordant ; +- confirmation explicite pour tout envoi mainnet ; +- journalisation séparée des plans et résultats ; +- validation post-exécution par transaction canonique, extraction core, décodage et matérialisation. + +## Exposition dans `kb_app_demo` + +La démo exposera uniquement un sous-ensemble opérationnel sûr : transfert, création/allocate/assign contrôlés, Compute Budget et durable nonce après ajout d’un orchestrateur stateful validé sur cluster. Stake, Vote, loaders, changement d'autorité, Feature, Slashing et opérations ZK peuvent être disponibles dans les crates sans apparaître dans la fenêtre Tauri. + + +## Orchestration Devnet et validation post-exécution + +`0.4.2-pre.009` ajoute dans `kb_pipeline` une orchestration backend bornée pour le premier parcours System transfer sur Devnet. La fonction publique ne remplace aucune couche : elle appelle successivement les contrats existants et conserve leurs résultats séparés. + +```text +classification du genesis hash + -> wallet temporaire persistant + -> solde / airdrop plafonné + -> PreparedExecutionPlan System transfer + -> getLatestBlockhash + -> getFeeForMessage + -> simulateTransaction exact + -> signature locale + -> sendTransaction + -> getSignatureStatuses + getBlockHeight + -> getTransaction + -> transaction canonique + -> extraction core + -> decode replay ciblé +``` + +Le mode par défaut reste une simulation seule. Il ne signe pas, n’envoie pas et ne fabrique pas de signature vide. Le parcours soumis exige simultanément l’autorisation `submit`, la confirmation opérateur lorsque le profil l’impose, l’activation Devnet du wallet, un plafond de dépense et une simulation exacte réussie. + +Le financement par faucet est une étape distincte, uniquement disponible sur Devnet et bornée par `devnet_airdrop_max_lamports`. Il est déclenché seulement lorsque le solde du wallet persistant ne couvre pas le transfert plus le plafond de frais. L’airdrop est lui-même confirmé avant de poursuivre. + +`0.4.2-pre.010` ajoute une précondition liée au destinataire. Le mode metadata-only de `getAccountInfo` détermine si l’adresse existe déjà. Lorsqu’elle est absente, `getMinimumBalanceForRentExemption(0)` fournit le minimum nécessaire à la création implicite d’un compte System sans données ; un transfert inférieur est refusé avant simulation. Les échecs de simulation conservent désormais l’erreur runtime et un extrait borné des logs. La fenêtre `demo_execution_solana_core` ne réimplémente aucune de ces règles : elle sélectionne un profil Devnet, construit la requête et relaie les événements du pipeline. + +Après une confirmation terminale, le pipeline hydrate exclusivement la signature envoyée. Une transaction on-chain échouée reste éligible à l’insertion canonique et au décodage comme intention échouée. Une expiration ou un timeout arrête la validation post-exécution sans prétendre que l’envoi a été confirmé. + +Une erreur avant signature reste un `Err`. Dès qu’une signature locale existe, l’orchestrateur conserve cette signature et transforme les erreurs d’envoi, de confirmation ou de replay en diagnostics dans le résumé. Cette règle évite qu’un appelant perde la référence d’une transaction potentiellement diffusée. + +L’orchestrateur est une API de bibliothèque et ne dépend pas de Tauri. La fenêtre `demo_execution_solana_core` expose uniquement une sélection Devnet réduite de ce backend ; les opérations administratives natives restent disponibles à terme dans les bibliothèques sans être nécessairement proposées dans l’interface. + +## État de clôture `0.4.2` + +Le parcours représentatif Devnet a validé la chaîne plan → simulation exacte → signature → envoi → confirmation → hydratation canonique → extraction core → decode replay. Les builders administratifs supplémentaires restent disponibles dans la bibliothèque, mais ne reçoivent aucune autorisation Mainnet implicite. + +La règle de clôture est donc la suivante : complétude du wire et des plans offline, politique de sécurité et préflight stateful présents, puis preuve cluster obligatoire avant toute exposition mutable supplémentaire. Un statut `future` dans la matrice désigne cette preuve d’activation, pas une instruction manquante. diff --git a/docs/EXECUTOR_SURFACE_MATRIX.md b/docs/EXECUTOR_SURFACE_MATRIX.md new file mode 100644 index 0000000..90c57e6 --- /dev/null +++ b/docs/EXECUTOR_SURFACE_MATRIX.md @@ -0,0 +1,130 @@ + + + +# Matrice des exécuteurs + +## Matrice canonique machine-readable — `pre.022` + +Le fichier `docs/NATIVE_SOLANA_EXECUTION_MATRIX.json` est désormais la source vérifiable pour la clôture de l’exécuteur natif. Il contient exactement : + +- 18 surfaces natives ; +- 14 surfaces `callable` ; +- 4 surfaces `non_invocable` ou historiques ; +- 109 opérations callable, chacune munie de sa source de construction, politique, préflight stateful, contrat de test, décision cluster et exposition UI. + +Les quatre classifications sans builder client sont BPF Loader v1, BPF Loader v2, Native Loader et l’ancien ZK Token Proof historique. Un test Rust charge la matrice et la compare à `SOLANA_CORE_OPERATION_CODES`; toute omission, duplication ou opération inconnue fait échouer `cargo test -p kb_executor_solana_core`. Le validateur Python provisoire a été supprimé. + +Le préflight stateful est actif pour Address Lookup Table, Config, Feature, Slashing et ZK ElGamal. Stake, Vote et Loaders restent marqués `future` dans la matrice pour leur preuve d’activation stateful approfondie ; cela ne retire aucun builder de la surface appelable, mais interdit toute exposition mutable sans validation cluster dédiée. + + +Cette matrice suit la symétrie entre surfaces de décodage et surfaces de construction d’instructions. Elle ne confond pas réservation d’une crate, disponibilité d’un builder, autorisation d’envoi et exposition dans une interface. + +## Socle d’exécution + +| Crate | Rôle | +|-----------------------|------------------------------------------------------------------------------| +| `kb_execution_api` | Contrats provider-neutral des capacités, politiques, plans et résultats. | +| `kb_execution_safety` | Garde-fous stateless avant simulation, signature ou envoi. | +| `kb_execution_solana` | Compilation Solana, blockhash/nonce, preuve de simulation et signature. | +| `kb_rpc` | Acquisition, simulation, envoi et confirmation JSON-RPC ; aucune clé privée. | +| `kb_wallet` | Résolution des signataires derrière un backend explicite. | + +## Règle mécanique + +Pour chaque décodeur de programme ou de protocole classifié : + +```text +kb_decoder_ -> kb_executor_ +``` + +Deux crates de décodage ne suivent volontairement pas cette règle : + +- `kb_decoder_api` est le contrat commun des décodeurs, pas un programme appelable ; +- `kb_decoder_anchor` est un helper technique partagé, pas une surface on-chain autonome. + +L’audit du workspace `0.4.2-pre.014` trouve : + +```text +104 crates kb_decoder_* +102 crates kb_executor_* +102 paires mécaniques exactes +2 exceptions techniques documentées +0 exécuteur orphelin +``` + +## Répartition des 102 surfaces appariées + +| Famille | Paires | +|---------------------------------------------------------------------------------------------------------------|----------:| +| AMM | 26 | +| Launchpad | 9 | +| SPL | 8 | +| Lending | 7 | +| CLMM | 6 | +| Router | 6 | +| Vault | 6 | +| Bridge | 4 | +| Stable | 4 | +| Orderbook | 3 | +| Perpetuals | 3 | +| Staking | 3 | +| Admin | 2 | +| Fees | 2 | +| Adapter, CPMM, DLMM, Governance, Lock, Metadata, NFT, RWA, Solana Core, Treasury, Vesting, Wallet et Weighted | 1 chacune | + +## Niveaux de maturité + +Une paire doit utiliser un statut explicite : + +| Statut | Signification | +|------------------|--------------------------------------------------------------------------------------------------| +| `reserved` | Crate et identité réservées ; aucun builder utilisable. | +| `implementing` | Contrat officiel et politiques en cours de validation. | +| `complete` | Toutes les opérations client officiellement appelables du périmètre sont construites et testées. | +| `not_applicable` | Surface non invocable, retirée ou purement technique, avec justification officielle. | + +`Unsupported(reason)` est acceptable pendant l’implémentation ou pour une impossibilité officielle précise. Il ne doit pas masquer une opération callable oubliée. + +## Politique de jalon + +Chaque version qui active un décodeur doit également : + +1. identifier la crate `kb_executor_` correspondante ; +2. attribuer un statut de maturité à l’exécuteur ; +3. lister les opérations client appelables et les opérations historiques decode-only ; +4. définir autorités, signataires, coûts, slippage ou autres garde-fous ; +5. comparer les payloads et comptes à un builder, une IDL ou un layout officiel ; +6. ajouter les tests offline puis localnet/devnet nécessaires ; +7. documenter l’API publique de la crate dans son README ; +8. décider séparément quelles opérations sont visibles dans `kb_app_demo` ou activables par une stratégie. + +Une opération dangereuse peut rester hors UI, mais elle reste dans le périmètre de la bibliothèque lorsqu’elle est officiellement appelable. + +## Programme Solana Core + +`kb_executor_solana_core` est la première surface passée de `reserved` à une implémentation opérationnelle. Après `pre.021`, elle expose toujours 109 opérations : 17 System, 4 Compute Budget, 5 Address Lookup Table, 6 précompiles de signature, 2 Config, 2 Feature, 2 Slashing, 3 ZK ElGamal, 24 Stake, 24 Vote, 11 Loader v3 et 9 Loader v4. + +| Surface native | Statut à la clôture `0.4.2` | Décision | +|-------------------------------|-----------------------------------------------------|-------------------------------------------------------------------------------------------------| +| System, Compute Budget | `complete` pour les builders client actuels | orchestration durable nonce séparée ; exposition mutable toujours soumise aux politiques | +| Address Lookup Table | `complete` stateless | autorité, rent, capacité et cooldown contrôlés par le préflight stateful | +| Ed25519, secp256k1, secp256r1 | `complete` | formes inline et offsets ; aucune autorisation métier implicite | +| Config | `complete` builder | wire local vérifié sans helper `bincode`; rent/état contrôlés avant envoi | +| Feature | `complete` builder | activation et révocation ; état/rent contrôlés avant envoi | +| Slashing | `complete` builder | close et plan atomique duplicate-block ; compte de preuve et fenêtres epoch stateful | +| ZK ElGamal Proof | `complete` builder | douze preuves inline/account et fermeture de contexte | +| ZK Token Proof historique | `not_applicable` | programme retiré/stub runtime, conservé uniquement pour décodage historique | +| Stake | `complete` pour les 24 helpers clients actuels | validations compte/rent/epoch exigées avant exposition ; Redelegate historique `decode-only` | +| Vote | `complete` pour les 20 variantes wire + 4 créations | validations account/rent/autorités/BLS/tour exigées avant exposition | +| Loader v3 upgradeable | `complete` pour 11 opérations client | interface officielle `wincode`; état, rent, owner et capacité restent stateful | +| Loader v4 | `complete` pour 9 plans client | wire officiel reproduit sans activer les helpers `bincode`; état/autorité/rent restent stateful | +| BPF Loader v1/v2 | `historical_decode_only` | builders dépréciés historiques non promus dans l’exécuteur moderne | +| Native Loader direct | `non_invocable_by_client_instruction` | déploiement lié au logiciel validator, aucun builder transactionnel inventé | + +`pre.021` ne change aucune capacité : elle centralise les validations communes `pub(crate)` et élimine les implémentations identiques détectées dans les modules System, ALT, Config/Feature, Slashing, ZK, Stake, Vote et Loader. + +La clôture `0.4.2` confirme la matrice machine-readable des dix-huit surfaces natives, avec pour chaque opération : builder/layout officiel, politique, tests offline, statut de validation cluster et décision d’exposition UI. + +## Versions futures + +Le `ROADMAP.md` associe désormais explicitement les exécuteurs aux séries Pump, Meteora, Raydium, Orca, Jupiter/routeurs et à chaque jalon ultérieur de décodeur. La symétrie de crates ne vaut pas activation : les listeners et stratégies ne peuvent demander que des capacités explicitement validées et autorisées. diff --git a/docs/FOUNDATION_CLOSURE.md b/docs/FOUNDATION_CLOSURE.md new file mode 100644 index 0000000..2c7d760 --- /dev/null +++ b/docs/FOUNDATION_CLOSURE.md @@ -0,0 +1,78 @@ + + + +# Clôture de la fondation `0.3.x` + +## Décision de version + +Le jalon `0.3.5` n’est plus conservé comme version autonome. Ses outils d’intégrité, de sélection de corpus et de replay ont été réalisés pendant `0.3.4`. + +La séquence retenue est : + +```text +0.3.4 -> clôture raw/canonical/core/replay +0.4.0 -> infrastructure de décodage et matérialisation +``` + +## Validations réelles acquises + +| Contrôle | Résultat | +|-------------------------|--------------------------------------------------| +| Extraction pending | 70 extraites, 0 échec | +| Intégrité SQL | 7 contrôles sans ligne anormale | +| Ledger initial | 70 succès, 70 tentatives | +| Skip sur échantillon | 14 skips, 0 extraction | +| Force replay | 14 extractions, 0 skip | +| Skip après replay | 14 skips, 0 extraction | +| Cardinalité finale | 70 transactions core | +| Ledger final | 70 succès, 84 tentatives | +| CSV natif | écrit dans `data/exports_csv/` sous Tauri/Linux | +| Tests application | `kb_app_demo` 60 tests, `kb_program_ids` 2 tests | +| Tests pipeline finaux | `kb_pipeline` 24 tests | +| Tests PostgreSQL finaux | `kb_store_pg` 39 tests, rollback inclus | +| Clippy final | `cargo clippy --all-targets` sans avertissement | + +Ces résultats confirment que le force-replay remplace le graphe ciblé sans créer de transaction supplémentaire et que le ledger comptabilise la tentative additionnelle. + +## Contrôles automatisés finaux validés + +### Rollback PostgreSQL + +Le test `optional_postgres_core_extraction_rolls_back_partial_graph_from_env` injecte une violation d’unicité après le début de la persistance. Il vérifie : + +- absence de transaction core partielle ; +- absence d’account key partielle ; +- raw toujours `received` ; +- aucune entrée ledger réussie ; +- replay corrigé possible ; +- nettoyage des fixtures. + +### Annulation backfill + +Les appels réseau et attentes suivantes sont maintenant annulables : + +- `getSignaturesForAddress` en vol ; +- `getTransaction` en vol ; +- attente du pacer ; +- pause de retry standard ; +- pause configurée après 429. + +Les tests offline couvrent une attente déjà annulée et l’abandon coopératif d’une future longue. + +## Clôture acquise + +Les commandes finales ont été exécutées avec succès le 7 juillet 2026 : + +```text +kb_pipeline: 24 passed, 0 failed +kb_store_pg avec PostgreSQL réel: 39 passed, 0 failed +cargo clippy --all-targets: aucun avertissement +``` + +`0.3.4` est inscrite dans `CHANGELOG.md`, le jalon `0.3.x` est clôturé et `0.4.0` devient le jalon actif. La session suivante doit démarrer avec `prompts/019_v0_4_0_decoder_infrastructure.md`. + +## Éléments reportés sans blocage + +Les corpus exhaustifs de protocoles ne sont pas nécessaires pour prouver l’indépendance de l’extracteur core. Ils sont constitués dans leurs versions respectives : Pump `0.5.x`, Meteora `0.6.x`, Raydium `0.7.x` et Orca `0.8.x`. + +Le rejeu manuel d’un backfill `before` depuis `resume_before_signature` reste recommandé lors d’une prochaine campagne longue. La logique de frontière contiguë, les compteurs d’arrêt et une campagne réelle interrompue sont déjà validés. diff --git a/docs/HISTORICAL_DATA_ACQUISITION_PLAN.md b/docs/HISTORICAL_DATA_ACQUISITION_PLAN.md new file mode 100644 index 0000000..3a5378a --- /dev/null +++ b/docs/HISTORICAL_DATA_ACQUISITION_PLAN.md @@ -0,0 +1,355 @@ + + + +# Plan différé — acquisition historique gratuite et worker de campagnes + +## Statut + +Ce document conserve un projet futur volontairement retiré du `ROADMAP.md` actif. + +Aucune version n'est attribuée à ce chantier. Il ne doit pas retarder les décodeurs, matérialisateurs et exécuteurs Solana Core, SPL, AMM, launchpads, orderbooks, routers ou autres surfaces prioritaires. + +Le nom **W2** est provisoire. Il désigne ici un worker manuel d'acquisition historique, distinct du worker temps réel destiné au trading. Le nom final des crates et binaires sera décidé au moment de l'activation du chantier. + +## 1. Motivation + +Le projet aura besoin de deux voies d'acquisition complémentaires : + +```text +W1 — temps réel + -> fournisseurs fiables ou payants + -> faible latence + -> détection de créations de tokens, pools et pairs + -> changements de prix/liquidité + -> déclencheurs d'achat, vente et gestion du risque + +W2 — historique manuel + -> sources gratuites ou très économiques + -> campagnes lentes et reprenables + -> corpus vieux d'un an, deux ans ou davantage + -> données destinées à l'analyse, aux tests et à la détection de patterns + -> aucune consommation implicite du budget réservé au trading +``` + +W2 ne remplace ni `kb_rpc`, ni le pipeline canonique, ni les fournisseurs temps réel. Il sert à alimenter progressivement PostgreSQL avec des données historiques dont la latence n'est pas critique. + +## 2. Principes non négociables + +- `kb_rpc` reste la frontière des protocoles Solana : JSON-RPC HTTP/WS standard, extensions fournisseur officiellement prises en charge, Yellowstone/gRPC et transports similaires. +- Une source historique parlant JSON-RPC standard peut réutiliser `kb_rpc`, mais sa politique de campagne, son coût, sa provenance et ses fallbacks restent hors de `kb_rpc`. +- Les APIs REST indexées, exports, datasets analytiques, archives CAR ou outils externes appartiennent à des crates de sources historiques séparées. +- W2 fonctionne en `free_only` par défaut. Aucun fournisseur payant ne doit être interrogé sans autorisation explicite de la campagne. +- Les endpoints et crédits de W1 ne doivent jamais être utilisés comme fallback silencieux de W2. +- Toute donnée destinée au pipeline doit finir par être normalisée dans le contrat canonique existant. +- Une source externe peut découvrir une signature, un slot ou un candidat ; elle ne devient pas pour autant la source canonique de la transaction. +- Les campagnes doivent être bornées, annulables, reprenables, dédupliquées et auditées. +- Une page vide ou une réponse `null` provenant d'une source best-effort ne prouve pas nécessairement l'absence historique de la donnée. + +## 3. Séparation découverte / hydratation + +W2 doit séparer deux responsabilités. + +### 3.1 Découverte + +Trouver des candidats à partir de : + +- adresses ou Program IDs ; +- signatures explicites ; +- plages de slots ou périodes ; +- pools, vaults ou token accounts ; +- programmes DEX/launchpads ; +- datasets indexés capables de filtrer par comptes, instructions ou timestamps. + +### 3.2 Hydratation + +Récupérer la transaction ou le bloc brut correspondant, puis suivre le pipeline existant : + +```text +candidate + -> transaction ou bloc brut + -> insertion raw canonique + -> core extraction + -> decode replay + -> materialization + -> agrégations et corpus d'analyse +``` + +La source de découverte et la source d'hydratation peuvent être différentes. + +Exemple : + +```text +BigQuery découvre les signatures d'une période + -> Old Faithful hydrate les anciennes transactions + -> un RPC public hydrate les transactions encore disponibles + -> PostgreSQL ignore les signatures déjà présentes + -> aucun crédit Helius n'est consommé +``` + +## 4. Architecture candidate + +Les noms ci-dessous sont provisoires et ne constituent pas encore des réservations de crates : + +```text +kb_historical_api + -> capacités, campagnes, candidats, provenance, coût et complétude + +kb_historical_source_rpc + -> profils RPC publics/best-effort utilisant les contrats de kb_rpc + +kb_historical_source_old_faithful + -> intégration avec un processus faithful-cli externe ou une archive locale + +kb_historical_source_bigquery + -> découverte indexée par requêtes analytiques bornées + +kb_historical_source_solscan + -> API officielle optionnelle, avec clé et budget explicites + +kb_worker_historical + -> orchestration manuelle, reprise, quotas, déduplication et import canonique +``` + +Une alternative consiste à garder les contrats et l'orchestration dans des modules internes d'une crate plus compacte. Ce choix devra être tranché après les prototypes et mesures, pas avant. + +## 5. Sources candidates + +### 5.1 RPC utilisé par l'Explorer Solana + +L'Explorer officiel repose sur des appels JSON-RPC Solana standards. Une source `rpc_best_effort` peut donc utiliser des endpoints publics ou communautaires pour : + +- `getSignaturesForAddress` ; +- `getTransaction` ; +- `getBlocks` ; +- `getBlock` ; +- réparations ciblées par signature ou slot. + +Cette source convient aux campagnes étroites et lentes. Elle n'offre pas de SLA, d'index arbitraire ni de garantie de rétention complète. Le site HTML ne doit pas être scrapé. + +Références de recherche : + +- +- +- + +### 5.2 Old Faithful + +Old Faithful est la piste prioritaire pour l'histoire profonde. L'intégration initiale doit privilégier un processus externe `faithful-cli` ou un serveur JSON-RPC local, afin d'éviter un couplage prématuré au format CAR, à l'implémentation Go et à sa licence. + +Usages visés : + +- hydratation de transactions anciennes ; +- scans bornés par époque ou slots ; +- constitution progressive d'un corpus local durable ; +- fallback gratuit lorsque les RPC récents ont purgé les données. + +Références de recherche : + +- +- + +### 5.3 BigQuery ou dataset analytique équivalent + +Un dataset indexé peut servir de moteur de découverte et réduire massivement le nombre d'appels RPC. Il doit être évalué pour : + +- schéma et fraîcheur ; +- instructions outer/inner disponibles ; +- comptes et balances pré/post ; +- précision des filtres ; +- coût réel des requêtes ; +- capacité à exporter des signatures et slots de manière reprenable. + +Il ne remplace pas PostgreSQL ni le pipeline canonique. + +Référence de recherche : + +- + +### 5.4 Solscan + +Solscan fournit un index fonctionnellement intéressant, mais l'intégration durable doit utiliser uniquement son API officielle et respecter ses quotas et conditions. + +Cette source serait : + +- optionnelle ; +- désactivée par défaut ; +- configurée par clé ; +- classée `free_quota` ou `paid` selon le plan ; +- interdite dans une campagne `free_only` si elle entraîne un coût. + +Les endpoints privés du site, cookies, jetons internes et scraping HTML ne doivent pas être utilisés. + +Références de recherche : + +- +- + +### 5.5 Fournisseurs payants + +Helius, Shyft, Chainstack, Triton ou équivalents peuvent être ajoutés ultérieurement comme fallback explicitement autorisé. Ils ne doivent pas être activés dans la politique par défaut de W2 et ne doivent jamais consommer les crédits de W1 à l'insu de l'opérateur. + +## 6. Stablecoins et corpus de marché + +Une campagne naïve `getSignaturesForAddress(MINT)` ne suffit pas à reconstruire toute l'activité d'un stablecoin. Les transferts classiques peuvent référencer les token accounts sans inclure systématiquement le mint dans les comptes de l'instruction. + +Pour constituer des séries utiles à un analyseur de patterns, la stratégie prioritaire doit être : + +```text +stablecoin + -> pools et pairs pertinents + -> vaults et token accounts des pools + -> Program IDs des AMM/CLMM/DLMM/routers + -> transactions candidates + -> décodage des swaps et changements de liquidité + -> matérialisation trades/pools + -> bougies et séries temporelles +``` + +Les transferts génériques du token restent un corpus séparé, beaucoup plus volumineux et moins directement utile au prix. + +## 7. Contrats fonctionnels candidats + +### 7.1 Capacités + +```rust +pub struct HistoricalSourceCapabilities { + pub signatures_by_address: bool, + pub transactions_by_signature: bool, + pub blocks_by_slot: bool, + pub slot_ranges: bool, + pub time_ranges: bool, + pub program_filter: bool, + pub token_filter: bool, + pub instruction_filter: bool, + pub indexed_results: bool, + pub full_history: bool, +} +``` + +### 7.2 Budget + +```rust +pub enum HistoricalSourceCost { + Free, + FreeQuota, + Paid, +} + +pub enum HistoricalBudgetPolicy { + FreeOnly, + FreeThenPaid, + ExplicitSourcesOnly, +} +``` + +`FreeOnly` doit être la valeur de départ de toute campagne manuelle. + +### 7.3 Complétude et provenance + +```rust +pub enum HistoricalCompleteness { + Complete, + Partial, + Unknown, + Pruned, +} + +pub struct HistoricalProvenance { + pub source_name: String, + pub source_kind: String, + pub method: String, + pub fetched_at_unix_ms: i64, + pub paid: bool, +} +``` + +Les types définitifs devront respecter les conventions du workspace, les bornes PostgreSQL et les règles TS-rs si une UI est ajoutée. + +## 8. Campagnes et observabilité + +Une campagne doit conserver au minimum : + +- identifiant stable ; +- cible et filtres ; +- source de découverte et source d'hydratation ; +- politique de coût ; +- curseur et checkpoint de reprise ; +- plage temporelle ou de slots ; +- concurrence, rate limit et backoff ; +- candidats découverts ; +- signatures déjà présentes ; +- transactions hydratées, absentes, pruned ou invalides ; +- erreurs et fallbacks ; +- volume réseau et coût estimé ; +- état `running`, `paused`, `cancelled`, `completed` ou `failed`. + +Les événements `tracing` doivent être émis par les crates responsables. Les secrets, clés API, payloads complets non bornés et URLs contenant des credentials sont interdits dans les logs. + +## 9. Étude préalable obligatoire + +Avant toute activation dans le ROADMAP, exécuter un benchmark reproductible avec : + +- une transaction récente ; +- une transaction vieille d'environ un an ; +- une transaction vieille d'environ deux ans ; +- une adresse peu active ; +- un Program ID très actif ; +- un pool stablecoin ; +- une plage de slots bornée. + +Mesurer pour chaque source : + +- profondeur historique ; +- taux de réponse `null` ou pruned ; +- cohérence de pagination ; +- latence et débit soutenable ; +- limitations et `Retry-After` ; +- filtres disponibles ; +- complétude des transactions et métadonnées ; +- coût ; +- conditions d'utilisation ; +- capacité de reprise et stabilité du contrat. + +## 10. Phases futures sans numéro de version + +### Phase R0 — recherche + +- valider les sources, licences, conditions d'utilisation, coûts et corpus ; +- produire une matrice comparative et un prototype jetable ; +- choisir le nom final de W2. + +### Phase R1 — contrats et ledger de campagne + +- définir les capacités, budgets, candidats, provenance et checkpoints ; +- ajouter les migrations PostgreSQL seulement après stabilisation du contrat. + +### Phase R2 — source RPC gratuite + +- réutiliser `kb_rpc` pour les méthodes standards ; +- ajouter quotas, backoff, déduplication et frontières de complétude. + +### Phase R3 — archive profonde + +- intégrer `faithful-cli` comme processus externe ou service local ; +- valider les imports par époque/slots et la cohérence avec le canonique. + +### Phase R4 — découverte indexée + +- intégrer BigQuery ou une source analytique équivalente après benchmark ; +- exporter uniquement les candidats nécessaires. + +### Phase R5 — sources commerciales facultatives et UI + +- ajouter Solscan ou d'autres APIs officielles avec budget explicite ; +- ajouter une UI de campagne manuelle seulement lorsque les contrats backend sont stables. + +## 11. Conditions avant retour dans le ROADMAP + +Ce chantier ne revient dans le `ROADMAP.md` que lorsque : + +- les décodeurs/matérialisateurs/exécuteurs prioritaires ont suffisamment progressé ; +- au moins deux sources gratuites ont été testées réellement ; +- une stratégie de licence et de conditions d'utilisation est validée ; +- la frontière avec `kb_rpc`, `kb_pipeline` et PostgreSQL est décidée ; +- la politique `free_only` est testable et empêche réellement tout fallback payant ; +- un prompt de session dédié peut être écrit sans hypothèse majeure non vérifiée. diff --git a/docs/IDL_SURFACE_CLASSIFICATION.md b/docs/IDL_SURFACE_CLASSIFICATION.md new file mode 100644 index 0000000..eef15ea --- /dev/null +++ b/docs/IDL_SURFACE_CLASSIFICATION.md @@ -0,0 +1,107 @@ + + + +# Classification des surfaces IDL + +Ce fichier résume les reclassements déduits des fichiers présents dans `idls/`. Il ne remplace pas une validation on-chain ; il sert à stabiliser la nomenclature et les crates réservés. + +## Décisions principales + +- `program_ids.rs` est remplacé par `constants.rs` dans les crates core/SPL, afin de regrouper plus tard les discriminants, sélecteurs et constantes internes non publiques. +- Les constantes réexportées par le `lib.rs` d’une crate doivent être appelées via `crate::CONSTANT_NAME` depuis les modules internes de cette même crate. +- Les surfaces IDL classifiées reçoivent un nom canonique fonctionnel : `amm_*`, `clmm_*`, `router_*`, `vault_*`, `lending_*`, etc. +- Les comptes non prouvés comme `program_id`, comme `BAGSB...`, restent hors workspace de décodage. Les vrais programmes Bags Fee Share V1/V2 sont classés séparément dans `docs/BAGS_FM.md`. + +## Reclassements notables + +| Canonique | Ancien nom/source | Justification IDL | +|--------------------------------------|-------------------------------|----------------------------------------------------------------------------------------| +| `vault_carrot_defi` | `carrot_defi` | IDL orientée vault : `initVault`, `issue`, `redeem`, `addAsset`, `removeStrategy`. | +| `lending_clone` | `clone` | IDL orientée lending : borrow positions, collateral, pool parameters, oracle updates. | +| `clmm_fusion` | `fusion_amm` | IDL avec positions, liquidity, fees et limit orders ; plus proche CLMM que simple AMM. | +| `stable_swap_hylo_exchange` | `hylo_exchange` | IDL stable/levercoin : mint, redeem, swap stable/lever. | +| `vault_hylo_stability_pool` | `hylo_stability_pool` | IDL de stability pool : user deposit/withdraw et rebalances. | +| `wallet_jupiter_apepro_smart_wallet` | `jupiter_apepro_smart_wallet` | IDL smart wallet avec approvals, `postSwap` et withdraw. | +| `vault_kamino_yvaults` | `kamino` | IDL `yvaults`, stratégies, collateral info et shares metadata. | +| `governance_metadao_bid_wall` | `metadao_bid_wall` | Surface MetaDAO liée à la gouvernance/futarchy plutôt qu’à un AMM direct. | +| `stable_swap_numeraire` | `numeraire` | IDL avec pool stable virtuel, add/remove liquidity et metadata LP. | +| `rwa_ondo_global_markets` | `ondo_global_markets` | IDL RWA/roles/attestation/burn ; pas un DEX. | +| `clmm_pancake_swap` | `pancake_swap` | IDL `amm_v3` avec positions et liquidity : classé CLMM. | +| `adapter_saber_decimal_wrapper` | `sabre_decimal_wrapper` | IDL wrapper d’adaptation de décimales : initializeWrapper, deposit, withdraw. | + +## Table IDL triée par canonique + +| Canonique | Source | Program ID | IDL | Crate cible | Statut | +|------------------------------------------------|----------------------------------|------------------------------------------------|---------------------------------------------------------------------------------|-----------------------------------------------------------|---------------------| +| `adapter_saber_decimal_wrapper` | `sabre_decimal_wrapper` | `DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB` | `saber_decimal.DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB.json` | `kb_decoder_adapter_saber_decimal_wrapper` | `canonical_current` | +| `admin_jupiter_lock` | `jupiter_lock` | `LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn` | `jupiter_locker.LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn.json` | `kb_decoder_admin_jupiter_lock` | `canonical_current` | +| `admin_pump_fees` | `pump_fees` | `pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ` | `pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json` | `kb_decoder_admin_pump_fees` | `canonical_current` | +| `amm_bonk_swap` | `bonk_swap` | `BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p` | `bonkswap.BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p.json` | `kb_decoder_amm_bonk_swap` | `canonical_current` | +| `amm_goosefx_gamma` | `goose_fx_gamma` | `GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT` | `goosefx_gamma.GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT.json` | `kb_decoder_amm_goosefx_gamma` | `canonical_current` | +| `amm_goosefx_v2` | `goose_fx_v2` | `GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn` | `goosefx_v2.GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn.json` | `kb_decoder_amm_goosefx_v2` | `canonical_current` | +| `amm_guac_swap` | `guac_swap` | `Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1` | `guacswap.Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1.json` | `kb_decoder_amm_guac_swap` | `canonical_current` | +| `amm_lifinity_swap_v2` | `lifinity_swap_v2` | `2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c` | `lifinity_amm_v2.2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c.json` | `kb_decoder_amm_lifinity_swap_v2` | `canonical_current` | +| `amm_metadao_futarchy_amm` | `metadao_futarchy_amm` | `FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq` | `metadao_futarchy.FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq.json` | `kb_decoder_amm_metadao_futarchy_amm` | `canonical_current` | +| `amm_metadao_v0_5` | `metadao_amm_v0_5` | `AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ` | `metadao_amm_v0.5.AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ.json` | `kb_decoder_amm_metadao_v0_5` | `canonical_current` | +| `amm_meteora_damm_v1` | `meteora_damm_v1` | `Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB` | `meteora_pools_amm.Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB.json` | `kb_decoder_amm_meteora_damm_v1` | `canonical_current` | +| `amm_meteora_damm_v2` | `meteora_damm_v2` | `cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG` | `meteora_damm_v2.cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG.json` | `kb_decoder_amm_meteora_damm_v2` | `canonical_current` | +| `amm_pump_swap` | `pump_swap` | `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA` | `pump_swap.pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA.json` | `kb_decoder_amm_pump_swap` | `canonical_current` | +| `amm_vertigo` | `vertigo` | `vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ` | `vertigo.vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ.json` | `kb_decoder_amm_vertigo` | `canonical_current` | +| `amm_virtuals` | `virtuals` | `5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki` | `virtuals.5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki.json` | `kb_decoder_amm_virtuals` | `canonical_current` | +| `amm_woofi` | `woofi` | `WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb` | `woofi.WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb.json` | `kb_decoder_amm_woofi` | `canonical_current` | +| `bridge_circle_cctp_token_messenger_minter` | `cctp_token_messenger_minter` | `CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3` | `cctp_v1.CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3.json` | `kb_decoder_bridge_circle_cctp_token_messenger_minter` | `canonical_current` | +| `bridge_circle_cctp_token_messenger_minter_v2` | `cctp_token_messenger_minter_v2` | `CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe` | `cctp_v2.CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe.json` | `kb_decoder_bridge_circle_cctp_token_messenger_minter_v2` | `canonical_current` | +| `bridge_layer_zero_endpoint` | `layer_zero_endpoint` | `76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6` | `layerzero_endpoint.76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6.json` | `kb_decoder_bridge_layer_zero_endpoint` | `canonical_current` | +| `bridge_layer_zero_executor` | `layer_zero_executor` | `6doghB248px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn` | `layerzero_executor.6doghB248px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn.json` | `kb_decoder_bridge_layer_zero_executor` | `canonical_current` | +| `clmm_byreal` | `byreal_clmm` | `REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2` | `byreal_clmm.REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2.json` | `kb_decoder_clmm_byreal` | `canonical_current` | +| `clmm_fusion` | `fusion_amm` | `fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9` | `fusion_amm.fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9.json` | `kb_decoder_clmm_fusion` | `canonical_current` | +| `clmm_orca_whirlpool` | `orca_whirlpool` | `whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc` | `orca_whirlpool.whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.json` | `kb_decoder_clmm_orca_whirlpool` | `canonical_current` | +| `clmm_orca_whirlpool` | `orca_whirlpool` | `whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc` | `orca_whirlpool.whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.json` | `kb_decoder_clmm_orca_whirlpool` | `canonical_current` | +| `clmm_pancake_swap` | `pancake_swap` | `HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq` | `pancakeswap.HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq.json` | `kb_decoder_clmm_pancake_swap` | `canonical_current` | +| `clmm_raydium` | `raydium_clmm` | `CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK` | `raydium_clmm.CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK.json` | `kb_decoder_clmm_raydium` | `canonical_current` | +| `clmm_stabble` | `stabble_clmm` | `6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6` | `stabble_clmm.6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6.json` | `kb_decoder_clmm_stabble` | `canonical_current` | +| `cpmm_raydium` | `raydium_cpmm` | `CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C` | `raydium_cpmm.CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C.json` | `kb_decoder_cpmm_raydium` | `canonical_current` | +| `dlmm_meteora` | `meteora_dlmm` | `LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo` | `meteora_dlmm.LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo.json` | `kb_decoder_dlmm_meteora` | `canonical_current` | +| `governance_metadao_bid_wall` | `metadao_bid_wall` | `WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx` | `metdao_bid_wall.WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx.json` | `kb_decoder_governance_metadao_bid_wall` | `canonical_current` | +| `launchpad_boop_fun` | `boop_fun` | `boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4` | `boop_fun.boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4.json` | `kb_decoder_launchpad_boop_fun` | `canonical_current` | +| `launchpad_metadao_ico` | `metadao_ico` | `moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM` | `metadao_launchpad.moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM.json` | `kb_decoder_launchpad_metadao_ico` | `canonical_current` | +| `launchpad_meteora_dbc` | `meteora_dbc` | `dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN` | `meteora_dbc.dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN.json` | `kb_decoder_launchpad_meteora_dbc` | `canonical_current` | +| `launchpad_moonit` | `moonit` | `MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG` | `moonit.MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG.json` | `kb_decoder_launchpad_moonit` | `canonical_current` | +| `launchpad_orca_wavebreak` | `orca_wavebreak` | `waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF` | `orca_wavebreak.waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF.json` | `kb_decoder_launchpad_orca_wavebreak` | `canonical_current` | +| `launchpad_printr` | `printr` | `T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint` | `printr.T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint.json` | `kb_decoder_launchpad_printr` | `canonical_current` | +| `launchpad_pump_fun` | `pump_fun` | `6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P` | `pump_fun.6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P.json` | `kb_decoder_launchpad_pump_fun` | `canonical_current` | +| `launchpad_pump_pumpup_ai` | `pumpup_ai` | `PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd` | `pumpup.PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd.json` | `kb_decoder_launchpad_pump_pumpup_ai` | `canonical_current` | +| `launchpad_raydium_launchlab` | `raydium_launchlab` | `LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj` | `raydium_launchlab.LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj.json` | `kb_decoder_launchpad_raydium_launchlab` | `canonical_current` | +| `lending_clone` | `clone` | `C1onEW2kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr` | `clone.C1onEW2kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr.json` | `kb_decoder_lending_clone` | `canonical_current` | +| `lending_kamino` | `kamino_lending` | `KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD` | `kamino_lending.KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD.json` | `kb_decoder_lending_kamino` | `canonical_current` | +| `lending_marginfi_v2` | `marginfi_v2` | `MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA` | `marginfi_v2.MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA.json` | `kb_decoder_lending_marginfi_v2` | `canonical_current` | +| `lock_raydium_lp` | `raydium_lock_lp` | `LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE` | `raydium_lock.LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE.json` | `kb_decoder_lock_raydium_lp` | `canonical_current` | +| `nft_metaplex_bubblegum` | `bubblegum` | `BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY` | `bubblegum.BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY.json` | `kb_decoder_nft_metaplex_bubblegum` | `canonical_current` | +| `orderbook_jupiter_limit_order` | `jupiter_limit_order` | `jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu` | `jupiter_limit_order.jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu.json` | `kb_decoder_orderbook_jupiter_limit_order` | `canonical_current` | +| `orderbook_jupiter_limit_order_v2` | `jupiter_limit_order_v2` | `j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X` | `jupiter_limit_order_v2.j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X.json` | `kb_decoder_orderbook_jupiter_limit_order_v2` | `canonical_current` | +| `orderbook_openbook_v2` | `openbook_v2` | `opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb` | `openbook_v2.opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb.json` | `kb_decoder_orderbook_openbook_v2` | `canonical_current` | +| `perpetuals_drift_v2` | `drift_v2` | `dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH` | `drift_v2.dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH.json` | `kb_decoder_perpetuals_drift_v2` | `canonical_current` | +| `perpetuals_jupiter` | `jupiter_perpetuals` | `PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu` | `jupiter_perpetuals.PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu.json` | `kb_decoder_perpetuals_jupiter` | `canonical_current` | +| `perpetuals_zeta` | `zeta` | `ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD` | `zeta.ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD.json` | `kb_decoder_perpetuals_zeta` | `canonical_current` | +| `router_jupiter_aggregator_v4` | `jupiter_agregator_v4` | `JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB` | `jupiter_v4.JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB.json` | `kb_decoder_router_jupiter_aggregator_v4` | `canonical_current` | +| `router_jupiter_aggregator_v6` | `jupiter_agregator_v6` | `JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4` | `jupiter_v6.JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4.json` | `kb_decoder_router_jupiter_aggregator_v6` | `canonical_current` | +| `router_jupiter_dca` | `jupiter_dca` | `DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M` | `jupiter_dca.DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M.json` | `kb_decoder_router_jupiter_dca` | `canonical_current` | +| `router_okx_labs_v1` | `okx_labs_v1` | `6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma` | `okx_lab_v1.6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma.json` | `kb_decoder_router_okx_labs_v1` | `canonical_current` | +| `router_okx_labs_v2` | `okx_labs_v2` | `proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u` | `okx_dex_router.proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u.json` | `kb_decoder_router_okx_labs_v2` | `canonical_current` | +| `rwa_ondo_global_markets` | `ondo_global_markets` | `XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm` | `ondo_gm.XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm.json` | `kb_decoder_rwa_ondo_global_markets` | `canonical_current` | +| `stable_swap_hylo_exchange` | `hylo_exchange` | `HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn` | `hylo_exchange.HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn.json` | `kb_decoder_stable_swap_hylo_exchange` | `canonical_current` | +| `stable_swap_jupiter_stable` | `jupiter_stable` | `JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr` | `jupiter_stable.JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr.json` | `kb_decoder_stable_swap_jupiter_stable` | `canonical_current` | +| `stable_swap_numeraire` | `numeraire` | `NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P` | `numeraire.NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P.json` | `kb_decoder_stable_swap_numeraire` | `canonical_current` | +| `stable_swap_stabble` | `stabble_stable_swap` | `swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ` | `stabble_stable_swap.swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ.json` | `kb_decoder_stable_swap_stabble` | `canonical_current` | +| `staking_kamino_farm` | `kamino_farm` | `FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr` | `kamino_farms.FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr.json` | `kb_decoder_staking_kamino_farm` | `canonical_current` | +| `staking_marinade_finance` | `marinade_finance` | `MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD` | `marinade_finance.MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD.json` | `kb_decoder_staking_marinade_finance` | `canonical_current` | +| `treasury_helium_treasury_management` | `helium_treasury_management` | `treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5` | `helium_treasury_management.treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5.json` | `kb_decoder_treasury_helium_treasury_management` | `canonical_current` | +| `vault_carrot_defi` | `carrot_defi` | `CarrotwivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s` | `carrot.CarrotwivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s.json` | `kb_decoder_vault_carrot_defi` | `canonical_current` | +| `vault_hylo_stability_pool` | `hylo_stability_pool` | `HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ` | `hylo_stability_pool.HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ.json` | `kb_decoder_vault_hylo_stability_pool` | `canonical_current` | +| `vault_kamino` | `kamino_vault` | `kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr` | `kamino_vault.kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr.json` | `kb_decoder_vault_kamino` | `canonical_current` | +| `vault_kamino_v2` | `kamino_vault_v2` | `KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd` | `kamino_vault_v2.KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd.json` | `kb_decoder_vault_kamino_v2` | `canonical_current` | +| `vault_kamino_yvaults` | `kamino` | `6LtLpnUFNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc` | `kamino.6LtLpnUFNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc.json` | `kb_decoder_vault_kamino_yvaults` | `canonical_current` | +| `vault_meteora` | `meteora_vault` | `24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi` | `meteora_vault.24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi.json` | `kb_decoder_vault_meteora` | `canonical_current` | +| `vesting_streamflow` | `streamflow` | `strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m` | `streamflow.strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m.json` | `kb_decoder_vesting_streamflow` | `canonical_current` | +| `wallet_jupiter_apepro_smart_wallet` | `jupiter_apepro_smart_wallet` | `JSW99DKmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw` | `jupiter_aprepro_smart_wallet.JSW99DKmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw.json` | `kb_decoder_wallet_jupiter_apepro_smart_wallet` | `canonical_current` | +| `weighted_swap_stabble` | `stabble_weighted_swap` | `swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW` | `stabble_weighted_swap.swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW.json` | `kb_decoder_weighted_swap_stabble` | `canonical_current` | diff --git a/docs/INSTRUCTION_REPLAY_CONTRACTS.md b/docs/INSTRUCTION_REPLAY_CONTRACTS.md new file mode 100644 index 0000000..6204d21 --- /dev/null +++ b/docs/INSTRUCTION_REPLAY_CONTRACTS.md @@ -0,0 +1,112 @@ + + + +# Contrats de replay par instruction + +Ce document complète le cadrage `0.2.1`. Il ne crée pas encore le schéma SQL complet, mais il verrouille l'idée suivante : le scheduling de replay doit cibler les instructions normalisées, tandis que le décodage doit recevoir une instruction avec son contexte Solana extrait. + +## Motivation + +Une transaction Solana peut contenir plusieurs instructions top-level et plusieurs inner instructions. Certaines instructions peuvent être déjà décodées, d'autres non. Rejouer toute la transaction à chaque correction de décodeur serait plus coûteux et moins précis. + +Le pipeline doit donc pouvoir faire : + +```text +raw transaction +→ extraction core transaction +→ extraction account keys, instructions, inner instructions, logs, balances +→ sélection des instructions non traitées +→ reconstruction d'un replay input contextualisé +→ decode/materialize uniquement sur ces inputs +``` + +## Scheduling instruction-level + +L'état `CoreInstructionProcessingState` prépare cette granularité. + +| État | Sens | +|-------------------|---------------------------------------------------------------| +| `Pending` | Instruction disponible pour premier traitement. | +| `Decoded` | Instruction décodée par au moins un décodeur. | +| `Materialized` | Instruction ayant produit une projection métier. | +| `Ignored` | Instruction ignorée volontairement par la politique courante. | +| `Failed` | Instruction en échec, à diagnostiquer. | +| `ReplayRequested` | Instruction à rejouer même si un traitement précédent existe. | + +Les états ne remplacent pas le ledger par module/version. Ils servent de vue rapide et de filtre de scheduling. + +## Décodage contextualisé + +Le décodeur ne doit pas être limité à `CoreInstructionRow.payload_json`. + +Certains protocoles Solana exigent de lire : + +- les inner instructions ; +- les logs Anchor ou non Anchor ; +- les balances avant/après ; +- les comptes résolus ; +- les loaded addresses ; +- les erreurs de transaction ; +- des séquences de logs liées à un CPI ; +- des instructions voisines de la même transaction. + +`CoreInstructionReplayInput` représente ce contrat de lecture : une instruction ciblée plus un contexte extrait depuis les tables `core`. Depuis le contrat `2`, ce contexte inclut également toutes les instructions outer de la signature, ordonnées numériquement et munies de leur payload retenu et de son hash. + +## Sélection de replay + +`CoreInstructionReplayFilter` permet de sélectionner les instructions par : + +- état de traitement ; +- `program_id` ; +- plage de slots. + +L'usage standard pour un worker de decode sera : + +```text +processing_state = Pending ou ReplayRequested +program_id = programme ciblé par le décodeur +plage de slots = optionnelle +``` + +Le repository charge les logs, balances, account keys et instructions outer nécessaires pour retourner `CoreInstructionReplayInput`. La liste outer inclut la cible et utilise les champs stables `instructionIndex`, `instructionPath`, `programId`, `payloadJson` et `payloadHash`. Cette extension lit les tables existantes et ne demande aucune migration. + +## Marquage de cycle de vie + +`CoreInstructionLifecycleMark` permet à un module de changer l'état d'une instruction après traitement. + +Exemples : + +- un décodeur reconnu marque l'instruction `Decoded` ; +- un materializer complet marque l'instruction `Materialized` ; +- un décodeur non concerné peut marquer `Ignored` dans un contexte contrôlé ; +- une erreur de parsing marque `Failed` ; +- une correction de décodeur peut remettre `ReplayRequested`. + +## Relation avec `kb_sol_ops_processing_ledger` + +Le processing ledger garde le détail par module, version et input. L'état sur l'instruction reste le dernier état opérationnel visible. + +Pour éviter les doublons, l'input key du ledger devra être stable, par exemple : + +```text +signature:instruction_path:program_id:processor_name:processor_version +``` + +La forme exacte n'est pas figée en `0.2.1`, mais elle doit rester déterministe. + +## Rétention du payload d'instruction et des logs + +Le payload JSON d'une instruction et le texte des logs sont utiles au début. Plus tard, après décodage et matérialisation fiables, ils pourront être compactés ou purgés avec conservation d'un hash. + +Les entities `CoreInstructionRow` et `CoreLogRow` préparent ce cas en autorisant : + +- payload ou texte présent ; +- payload ou texte absent ; +- hash conservé ; +- état de traitement consultable pour replay. + +La purge effective ne doit pas être implémentée avant que les diagnostics de replay soient fiables. + +## Décision pour `0.2.1` + +`0.2.1` ajoute les contrats Rust nécessaires, mais ne crée pas encore les tables SQL. Les migrations réelles restent prévues pour `0.2.3` et `0.2.4`. diff --git a/docs/LIVE_SOURCE_STRATEGY.md b/docs/LIVE_SOURCE_STRATEGY.md new file mode 100644 index 0000000..b9d6bda --- /dev/null +++ b/docs/LIVE_SOURCE_STRATEGY.md @@ -0,0 +1,104 @@ + + + +# Stratégie des sources de transactions + +## Décision actuelle + +Les sources temps réel payantes ne sont plus l’axe actif de `0.3.x`. + +La priorité est : + +```text +backfills HTTP sur comptes gratuits + -> transaction canonique + -> extraction core + -> décodeurs Core/Pump/Meteora/Raydium/Orca/Jupiter +``` + +Helius Developer, Triton, Chainstack, Shyft ou un autre provider seront testés seulement lorsque le pipeline pourra décoder et matérialiser immédiatement les transactions reçues. + +## Sources futures + +### HTTP JSON-RPC + +Usage : + +- backfill historique ; +- hydratation de signatures ; +- réparation ; +- validation ponctuelle. + +Méthodes principales : + +```text +getSignaturesForAddress +getTransaction +getSignatureStatuses +getSlot +``` + +### Helius WebSocket enrichi + +`transactionSubscribe` est une extension Helius, pas une méthode Solana standard. + +Elle sera implémentée dans des modules internes de `kb_rpc`, sans créer de crate séparée, puis convertie vers le modèle canonique commun. + +### Yellowstone gRPC + +Yellowstone fournit l’équivalent fonctionnel d’un flux de transactions complètes filtrées. Les endpoints Triton, Chainstack, Shyft ou compatibles doivent être supportés par configuration dans `kb_rpc`. + +### Solana WebSocket standard + +`logsSubscribe` par mention ou avec filtre `"all"` reste une solution de probe, de fallback ou de comparaison. Une notification de logs ne devient durable qu’après hydratation de la transaction ou enregistrement d’une observation technique légère. + +## Stockage commun + +Toutes les sources convergent vers : + +```text +kb_sol_raw_transactions +kb_sol_obs_transaction_observations +``` + +Les décodeurs ne connaissent ni le fournisseur, ni le protocole, ni la méthode d’acquisition. + +## Ordre de mise en œuvre + +```text +0.3.x transaction canonique, observations, backfill gratuit, extraction core +0.4.x décodeurs Core/SPL +0.5.x Pump +0.6.x Meteora +0.7.x Raydium +0.8.x Orca +0.9.x Jupiter et routeurs +0.10.x Helius transactionSubscribe, Yellowstone gRPC et comparaison +``` + +## Sécurité de reprise + +Pendant toute perte du flux principal : + +```text +trading = disabled +``` + +Le replay ou le backfill après reconnexion sert à remettre la base en cohérence. Un événement `replayed`, `backfilled` ou `repaired` ne doit pas déclencher rétroactivement un ordre. + +## Critères de choix futur + +La décision provider devra comparer : + +- couverture par programme et CPI ; +- latence par signature ; +- coût par Go, crédit ou forfait ; +- nombre de filtres et streams ; +- régions ; +- reconnexion et fenêtre de replay ; +- pertes et backpressure ; +- portabilité du protocole ; +- qualité du support. + +Les timings seront mesurés dans `kb_sol_obs_transaction_observations`. + diff --git a/docs/LOGGING.md b/docs/LOGGING.md new file mode 100644 index 0000000..d7f21b7 --- /dev/null +++ b/docs/LOGGING.md @@ -0,0 +1,106 @@ + + + +# Logging + +La journalisation est centralisée dans `kb_logging` et repose sur des événements `tracing` structurés. + +## Objectifs + +- séparer console, fichiers globaux et fichiers par crate ; +- conserver un fichier JSONL dédié aux erreurs ; +- retrouver immédiatement un échec de décodage ou de matérialisation ; +- corréler RPC, pipeline, store et processor sans dupliquer les décisions dans Tauri ; +- activer le debug d’une crate sans dépendre de son découpage interne en modules. + +## Arborescence d’un profil + +Pour un profil stocké sous `logs//`, la configuration crée : + +```text +logs//debug.log +logs//info.log +logs//error.jsonl +logs//app.log +logs///debug.log +logs///info.log +logs///error.jsonl +``` + +Les routes sont en rotation quotidienne. `tracing-appender` insère la date dans le nom physique produit à partir du préfixe et du suffixe configurés. + +Les niveaux sont cumulatifs : + +- `debug.log` reçoit `debug`, `info`, `warn` et `error` ; +- `info.log` reçoit `info`, `warn` et `error` ; +- `error.jsonl` reçoit uniquement `error` ; +- `app.log` reçoit les événements `kb_app_demo` à partir de `debug`. + +`error.jsonl` utilise le format JSON pour permettre la recherche par champ. Les autres fichiers utilisent le format humain sans ANSI. + +## Targets canoniques + +Le target est exactement le nom de la crate et provient de `src/constants.rs` : + +```text +kb_app_demo +kb_decoder_solana_core +kb_executor_metadata_spl_name_service +kb_executor_solana_core +kb_executor_spl_account_compression +kb_executor_spl_memo +kb_executor_spl_noop +kb_executor_spl_single_pool +kb_logging +kb_materializer_admin +kb_materializer_compliance_audit +kb_materializer_lifecycle +kb_materializer_staking +kb_pipeline +kb_rpc +kb_store_pg +``` + +La granularité passe par les champs `action`, `stage`, `window`, `campaign_id`, `signature`, `instruction_path`, `program_id`, `processor_name`, `status` et `error_code`. + +## Corrélation de campagne + +Les campagnes de backfill, extraction core et replay decode utilisent des identifiants stables : + +```text +capture_session_id +campaign_id +signature +slot +instruction_path +program_id +input_key +input_hash +processor_name +processor_version +``` + +Le pipeline journalise la sélection et l’agrégation. Le RPC journalise le transport et l’endpoint sélectionné sans exposer l’URL secrète. Le store journalise la persistance et les rollbacks. Le décodeur et chacun des matérialiseurs lifecycle, admin et compliance audit journalisent leur décision propre. + +## Fichier d’erreurs + +Les erreurs de décodage et de matérialisation sont émises au niveau `error` par la crate responsable, puis éventuellement par la frontière qui constate l’échec terminal. Elles apparaissent donc dans : + +- `logs//error.jsonl` ; +- `logs///error.jsonl`. + +Cette duplication volontaire fournit une vue globale et une vue isolée par composant. Les champs de corrélation permettent de regrouper les événements d’un même défaut. + +Une transaction on-chain échouée mais correctement décodée n’est pas écrite dans `error.jsonl` uniquement à cause de `meta.err`. En revanche, un payload déclaré compatible mais `failed` ou `unsupported` indique une lacune du code ou une évolution du protocole et doit être visible dans le fichier d’erreurs. + +## Frontend et Tauri + +Le frontend peut continuer à transmettre un identifiant logique tel que `kb_app_demo.frontend.demo_decode_replay`. Le backend le valide et le conserve dans le champ `frontend_target`, mais l’événement est émis avec le target canonique `kb_app_demo`. + +`kb_app_demo` ne doit pas recopier les détails internes d’un retry RPC, d’un parsing binaire ou d’un commit SQL. Il journalise l’invocation, la progression visible et le résumé reçu de la crate opérationnelle. + +## Sécurité + +Les logs ne contiennent jamais de clé privée, seed phrase, DSN non masqué, URL fournisseur secrète ou payload complet non borné. Pour un payload problématique, conserver la taille, un préfixe borné et un SHA-256. + +Le contrat normatif complet se trouve dans `docs/TRACING_CONTRACT.md`. diff --git a/docs/MATERIALIZATIONS.md b/docs/MATERIALIZATIONS.md new file mode 100644 index 0000000..e8eb5c6 --- /dev/null +++ b/docs/MATERIALIZATIONS.md @@ -0,0 +1,68 @@ + + + +# Catalogue des matérialisations + +Les matérialisations transforment des événements décodés en événements métier exploitables par la base de données, les validations, les agrégations et les futurs signaux de stratégie. + +## Matérialisations prioritaires + +- `trade` : swaps, buys, sells, fills et conversions entre actifs. +- `liquidity` : dépôts, retraits, ajout ou retrait de liquidité, bootstrap de pool. +- `lifecycle` : création, initialisation, migration, fermeture ou changement d'état d'un pool, marché, compte ou vault. +- `fee` : collecte, claim, sweep, buyback, mise à jour de configuration de frais. +- `admin` : changement d'autorité, update de config, pause, unpause, transfert de rôle. +- `token_account` : création ATA, transfert, mint, burn, close account, approve, revoke. +- `pool_state` : snapshots de réserves, ticks, bins, prix, liquidité active ou virtual reserves. +- `orderbook` : place order, cancel order, fill, settle funds, consume events. +- `reward` : émission, claim, distribution, farming, incentives. +- `risk` : signaux dérivés à partir des événements admin, liquidité, metadata, authority ou anomalies. + +## Projections natives actives dans `0.4.1` + +`kb_materializer_lifecycle` produit des événements lifecycle idempotents à partir d'observations exactes, réussies et commitées : + +| Domaine | Sortie stable | Entrées couvertes | +|----------------------|--------------------------------------------|--------------------------------------------------------------------------------------------------------------| +| Address Lookup Table | `address_lookup_table::0` | create, freeze, extend, deactivate, close | +| Program loaders | `program_loader:::0` | finalize immuable, initialize/deploy/upgrade/close/extend upgradeable, set length/deploy/retract/finalize v4 | +| Feature Gate | `feature_gate:revoke_pending_activation:0` | revoke pending activation | +| Comptes System | `system_account::0` | create, create with seed, create allow prefund, allocate, allocate with seed | +| Durable nonce System | `durable_nonce_account::0` | initialize, advance, authorize, upgrade, withdraw | +| Contexte ZK ElGamal | `zk_proof_context::0` | initialisation demandée après vérification réussie, fermeture | +| Rapport Slashing | `slashing_violation_report::0` | initialisation après preuve duplicate block acceptée, fermeture après rétention | + +La famille décodée n'impose pas à elle seule la famille matérialisée. `authorize_nonce_account` est un événement `Admin`, tandis qu'une vérification ZK ElGamal est un événement `Audit`; ces observations ne produisent une sortie `Lifecycle` que lorsque leur mutation durable exacte est reconnue. Une transaction échouée ou non commitée est toujours refusée. + +`kb_materializer_admin` produit des sorties `Admin` pour les assignations System, les écritures Config opaques et les changements d’autorité Loader. `kb_materializer_compliance_audit` produit des sorties `ComplianceAudit` pour les écritures/copies de bytecode Loader. `kb_materializer_staking` produit les projections instructionnelles Stake/Vote commitées : comptes stake/vote, autorités, lockup, délégation, vote state, retraits et dépôt de rewards. Les données complètes ne sont jamais recopiées : seules les clés, autorités, offsets, tailles, hashes, préfixes et paramètres bornés disponibles sont conservés. + +Le retrait d'un nonce account conserve une sémantique conditionnelle : le runtime ferme le compte uniquement lorsque la totalité du solde est retirée. La projection décrit l'opération commitée et ne prétend pas reconstruire l'état final transactionnel du compte. Les deltas SOL restent la responsabilité du graphe core. + +Une preuve ZK n'est jamais matérialisée. Seul le lifecycle du compte de contexte est projeté lorsqu'il existe. Un rapport Slashing conserve le signal de violation et son lifecycle, mais pas le contenu du compte de preuve externe ; `penaltyAppliedByProgram` reste faux car le programme ne retire pas lui-même du stake. L'ancien ZK Token Proof Program reste en decode/audit, car le runtime actuel est sans effet et aucun historique mainnet fiable n'a été observé. + +## Matérialisations spécialisées à prévoir + +- `nft` : NFT classiques, programmables et compressés. +- `metadata` : metadata Metaplex, creators, collection, update authority, URI et symbol. +- `oracle` : prix, publisher, confidence, staleness et sources d'oracle. +- `lending` : borrow, repay, collateral, liquidation et health factor. +- `staking` avancé : snapshots de comptes Stake/Vote avant/après, activation par epoch, crédits Vote cumulés et réconciliation avec sysvars. +- `governance` : proposal, vote, execute et update de realm/config. +- `bridge` : lock, mint wrapped asset, burn, redeem et message verification. +- `perpetuals` : position, funding, liquidation, collateral et PnL. +- `vault` : share mint/burn, deposit, withdraw, rebalance et fee collection. +- `routing` : route, route leg, quote, slippage, aggregator hop. +- `compliance_audit` : événements d'audit non directement matérialisables en trading. +- `token_metadata_risk` : signaux de risque issus des métadonnées de tokens, par exemple autorité mutable, creators suspects, symbol/URI incohérents ou changements de metadata. + +## Projections natives encore à terminer avant clôture + +Toutes les sémantiques matérialisables ne sont pas encore projetées. Les lots restants sont explicites : + +- Compute Budget : profil transactionnel agrégé regroupant toutes les instructions Compute Budget du message. + +Les précompiles de signature et les preuves ZK sans contexte restent des observations d'audit déjà suffisantes. Une projection supplémentaire n'est justifiée que si un consommateur `compliance_audit` exige une table dédiée. L'ancien ZK Token Proof no-op reste sans matérialisation. + +## Règle de conception + +Une matérialisation spécialisée ne doit être ajoutée que si la table générique ne suffit pas pour représenter correctement la sémantique du protocole. Le modèle commun reste prioritaire, puis les tables spécialisées complètent ce modèle. Chaque projection doit posséder une identité stable, un état cible explicite, une politique d'idempotence et une règle documentée pour les transactions échouées. diff --git a/docs/METAPLEX_TOKEN_METADATA_MATRIX.json b/docs/METAPLEX_TOKEN_METADATA_MATRIX.json new file mode 100644 index 0000000..ecd2123 --- /dev/null +++ b/docs/METAPLEX_TOKEN_METADATA_MATRIX.json @@ -0,0 +1,1612 @@ +{ + "schema_version": 2, + "milestone": "0.4.7-pre.025", + "surface_code": "metadata_metaplex_token_metadata", + "program_id": "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s", + "program_family": "independent_metaplex_foundation_program", + "decode_status": "instruction_inventory_exhaustive_0_through_57_and_account_key_inventory_exhaustive_0_through_14", + "runtime_registration": "not_enabled_in_pre_008", + "official_sources": [ + { + "kind": "repository", + "value": "metaplex-foundation/mpl-token-metadata", + "ref": "main", + "status": "verified_2026-07-20" + }, + { + "kind": "rust_sdk", + "value": "mpl-token-metadata", + "version_requirement": "^5.1", + "observed_release": "5.1.1" + }, + { + "kind": "idl", + "value": "idls/token_metadata.json", + "idl_version": "1.14.0", + "sha": "5df4a24f62c2743125be096cc174680790c92c18" + }, + { + "kind": "generated_instruction_inventory", + "value": "clients/rust/src/generated/instructions/mod.rs", + "sha": "3c42eec629f82e44ba690a7c4bd2177cc78f1d49" + } + ], + "wire_contract": { + "instruction_discriminator": "u8", + "argument_encoding": "borsh", + "unknown_discriminator": "unsupported", + "truncated_payload": "failed", + "non_canonical_suffix": "pending_per_variant", + "foreign_program_id": "incompatible" + }, + "bounds": { + "max_instruction_bytes": 65536, + "max_accounts": 256, + "max_string_bytes": 4096, + "max_uri_bytes": 4096, + "max_creators": 64, + "max_authorization_payload_bytes": 65536, + "max_diagnostics": 64, + "instruction_payload_bytes_max": 16384, + "instruction_accounts_max": 64, + "name_bytes_max": 32, + "symbol_bytes_max": 10, + "uri_bytes_max": 200, + "creators_max": 5, + "seller_fee_basis_points_max": 10000 + }, + "current_sdk_instruction_modules": [ + "approve_collection_authority", + "approve_use_authority", + "bubblegum_set_collection_size", + "burn", + "burn_edition_nft", + "burn_nft", + "burn_v1", + "close_accounts", + "close_escrow_account", + "collect", + "convert_master_edition_v1_to_v2", + "create", + "create_escrow_account", + "create_master_edition_v3", + "create_metadata_account_v3", + "create_v1", + "delegate", + "delegate_authority_item_v1", + "delegate_collection_item_v1", + "delegate_collection_v1", + "delegate_data_item_v1", + "delegate_data_v1", + "delegate_locked_transfer_v1", + "delegate_print_delegate_v1", + "delegate_programmable_config_item_v1", + "delegate_programmable_config_v1", + "delegate_sale_v1", + "delegate_staking_v1", + "delegate_standard_v1", + "delegate_transfer_v1", + "delegate_utility_v1", + "deprecated_mint_new_edition_from_master_edition_via_printing_token", + "freeze_delegated_account", + "lock", + "lock_v1", + "migrate", + "mint", + "mint_new_edition_from_master_edition_via_token", + "mint_new_edition_from_master_edition_via_vault_proxy", + "mint_v1", + "print", + "print_v1", + "print_v2", + "puff_metadata", + "remove_creator_verification", + "resize", + "revoke", + "revoke_authority_item_v1", + "revoke_collection_authority", + "revoke_collection_item_v1", + "revoke_collection_v1", + "revoke_data_item_v1", + "revoke_data_v1", + "revoke_locked_transfer_v1", + "revoke_migration_v1", + "revoke_print_delegate_v1", + "revoke_programmable_config_item_v1", + "revoke_programmable_config_v1", + "revoke_sale_v1", + "revoke_staking_v1", + "revoke_standard_v1", + "revoke_transfer_v1", + "revoke_use_authority", + "revoke_utility_v1", + "set_and_verify_collection", + "set_and_verify_sized_collection_item", + "set_collection_size", + "set_token_standard", + "sign_metadata", + "thaw_delegated_account", + "transfer", + "transfer_out_of_escrow", + "transfer_v1", + "unlock", + "unlock_v1", + "unverify", + "unverify_collection", + "unverify_collection_v1", + "unverify_creator_v1", + "unverify_sized_collection_item", + "update", + "update_as_authority_item_delegate_v2", + "update_as_collection_delegate_v2", + "update_as_collection_item_delegate_v2", + "update_as_data_delegate_v2", + "update_as_data_item_delegate_v2", + "update_as_programmable_config_delegate_v2", + "update_as_programmable_config_item_delegate_v2", + "update_as_update_authority_v2", + "update_metadata_account_v2", + "update_primary_sale_happened_via_token", + "update_v1", + "use", + "use_v1", + "utilize", + "verify", + "verify_collection", + "verify_collection_v1", + "verify_creator_v1", + "verify_sized_collection_item" + ], + "historical_idl": { + "instruction_count": 58, + "discriminator_range": "0..57", + "status": "names_accounts_arguments_and_legacy_optional_rules_pending_exhaustive_extraction" + }, + "pda_families": [ + { + "code": "metadata", + "seed_shape": [ + "metadata", + "program_id", + "mint" + ], + "status": "implemented_pre_025" + }, + { + "code": "edition_or_master_edition", + "seed_shape": [ + "metadata", + "program_id", + "mint", + "edition" + ], + "status": "implemented_pre_026" + }, + { + "code": "edition_marker", + "status": "implemented_pre_027" + }, + { + "code": "collection_authority_record", + "status": "implemented_pre_029" + }, + { + "code": "use_authority_record", + "status": "implemented_pre_029" + }, + { + "code": "token_record", + "status": "implemented_pre_028" + }, + { + "code": "metadata_delegate_record", + "status": "implemented_pre_029", + "seeds": [ + "metadata", + "program_id", + "mint", + "metadata_delegate_role_seed", + "update_authority", + "delegate" + ] + }, + { + "code": "token_owned_escrow", + "status": "implemented_pre_030", + "token_owner_seeds": [ + "metadata", + "program_id", + "base_token", + "0", + "escrow" + ], + "creator_seeds": [ + "metadata", + "program_id", + "base_token", + "1", + "creator", + "escrow" + ] + }, + { + "code": "reservation_list_historical", + "status": "implemented_pre_031" + }, + { + "code": "holder_delegate_record", + "status": "implemented_pre_029", + "seeds": [ + "metadata", + "program_id", + "mint", + "holder_delegate_role_seed", + "owner", + "delegate" + ] + } + ], + "account_layouts": [ + { + "name": "Metadata", + "status": "implemented_pre_025" + }, + { + "name": "MasterEditionV1", + "status": "implemented_pre_026_historical_decode_only" + }, + { + "name": "MasterEditionV2", + "status": "implemented_pre_026" + }, + { + "name": "Edition", + "status": "implemented_pre_026" + }, + { + "name": "EditionMarker", + "status": "implemented_pre_027" + }, + { + "name": "EditionMarkerV2", + "status": "implemented_pre_027_experimental" + }, + { + "name": "TokenRecord", + "status": "implemented_pre_028" + }, + { + "name": "MetadataDelegateRecord", + "status": "implemented_pre_029" + }, + { + "name": "HolderDelegateRecord", + "status": "implemented_pre_029" + }, + { + "name": "CollectionAuthorityRecord", + "status": "implemented_pre_029" + }, + { + "name": "UseAuthorityRecord", + "status": "implemented_pre_029" + }, + { + "name": "TokenOwnedEscrow", + "status": "implemented_pre_030" + }, + { + "name": "ReservationListV1V2", + "status": "implemented_pre_031_decode_only", + "max_entries": 200, + "v1_max_bytes": 6949, + "v2_max_bytes": 9749, + "pda_seeds": [ + "metadata", + "program_id", + "master_edition", + "reservation", + "resource" + ], + "execution": "historical_never_execute" + } + ], + "provenance_boundaries": [ + "metaplex_token_metadata", + "token_2022_embedded_metadata", + "metaplex_core", + "metaplex_bubblegum" + ], + "external_json": { + "enabled": false, + "canonical_replay_dependency": false, + "status": "contract_reserved", + "ssrf_policy": "required_before_activation" + }, + "execution": { + "enabled": false, + "reason": "builders_accounts_preconditions_and_postconditions_not_yet_exhaustively_proved" + }, + "next_proofs": [ + "materialize Metadata facts with one canonical owner per fact and no duplication", + "materialize historical account state with explicit lifecycle and provenance" + ], + "implemented_instruction_groups": [ + { + "pre_release": "0.4.7-pre.002", + "group": "metadata_create_update_v2_v3", + "instructions": [ + { + "name": "UpdateMetadataAccountV2", + "code": "update_metadata_account_v2", + "discriminator": 15, + "status": "current", + "decode": "implemented", + "materialize": "pending", + "execute": "pending" + }, + { + "name": "CreateMetadataAccountV3", + "code": "create_metadata_account_v3", + "discriminator": 33, + "status": "current", + "decode": "implemented", + "materialize": "pending", + "execute": "pending" + } + ], + "wire": "official_borsh_mpl_token_metadata_5_1_1", + "runtime_registration": "pending_full_group_validation" + }, + { + "pre_release": "0.4.7-pre.003", + "group": "collection_verification_legacy_current", + "instructions": [ + { + "name": "VerifyCollection", + "code": "verify_collection", + "discriminator": 18, + "status": "current", + "decode": "implemented", + "materialize": "pending", + "execute": "pending" + }, + { + "name": "UnverifyCollection", + "code": "unverify_collection", + "discriminator": 22, + "status": "current", + "decode": "implemented", + "materialize": "pending", + "execute": "pending" + }, + { + "name": "SetAndVerifyCollection", + "code": "set_and_verify_collection", + "discriminator": 25, + "status": "current", + "decode": "implemented", + "materialize": "pending", + "execute": "pending" + } + ], + "wire": "single_u8_discriminator_official_mpl_token_metadata_5_1_1", + "account_contract": "ordered_required_accounts_plus_optional_collection_authority_record_and_bounded_remaining_accounts", + "runtime_registration": "pending_broader_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.004", + "group": "sized_collection_item_verification", + "instructions": [ + { + "name": "VerifySizedCollectionItem", + "code": "verify_sized_collection_item", + "discriminator": 30, + "status": "current", + "decode": "implemented", + "materialize": "pending", + "execute": "pending" + }, + { + "name": "UnverifySizedCollectionItem", + "code": "unverify_sized_collection_item", + "discriminator": 31, + "status": "current", + "decode": "implemented", + "materialize": "pending", + "execute": "pending" + }, + { + "name": "SetAndVerifySizedCollectionItem", + "code": "set_and_verify_sized_collection_item", + "discriminator": 32, + "status": "current", + "decode": "implemented", + "materialize": "pending", + "execute": "pending" + } + ], + "wire": "single_u8_discriminator_official_mpl_token_metadata_5_1_1", + "account_contract": "ordered_required_accounts_plus_optional_collection_authority_record; collection metadata is writable and collection authority is readonly signer", + "runtime_registration": "pending_broader_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.005", + "group": "collection_authority_administration", + "instructions": [ + { + "name": "ApproveCollectionAuthority", + "code": "approve_collection_authority", + "discriminator": 23, + "status": "current", + "wire": "u8 discriminator only", + "required_accounts": 7, + "optional_accounts": [ + "rent" + ], + "defaulted_accounts": [ + "system_program" + ], + "execution_policy": "future_current_surface" + }, + { + "name": "RevokeCollectionAuthority", + "code": "revoke_collection_authority", + "discriminator": 24, + "status": "current", + "wire": "u8 discriminator only", + "required_accounts": 5, + "optional_accounts": [], + "execution_policy": "future_current_surface" + } + ], + "provenance": "mpl-token-metadata 5.1.1 generated builders", + "materialization": "reserved_for_admin_and_lifecycle_pre_release" + }, + { + "pre_release": "0.4.7-pre.006", + "group": "use_authority_and_utilization", + "instructions": [ + { + "name": "Utilize", + "code": "utilize", + "discriminator": 19, + "status": "current", + "wire": "u8 discriminator + u64 number_of_uses", + "required_accounts": 9, + "optional_accounts": [ + "use_authority_record", + "burner" + ], + "execution_policy": "future_current_surface" + }, + { + "name": "ApproveUseAuthority", + "code": "approve_use_authority", + "discriminator": 20, + "status": "current", + "wire": "u8 discriminator + u64 number_of_uses", + "required_accounts": 10, + "optional_accounts": [ + "rent" + ], + "defaulted_accounts": [ + "token_program", + "system_program" + ], + "execution_policy": "future_current_surface" + }, + { + "name": "RevokeUseAuthority", + "code": "revoke_use_authority", + "discriminator": 21, + "status": "current", + "wire": "u8 discriminator only", + "required_accounts": 8, + "optional_accounts": [ + "rent" + ], + "defaulted_accounts": [ + "token_program", + "system_program" + ], + "execution_policy": "future_current_surface" + } + ], + "provenance": "mpl-token-metadata 5.1.1 generated builders", + "materialization": "reserved_for_metadata_admin_lifecycle_and_risk_audit" + }, + { + "pre_release": "0.4.7-pre.007", + "group": "creator_signature_and_primary_sale", + "instructions": [ + { + "name": "UpdatePrimarySaleHappenedViaToken", + "code": "update_primary_sale_happened_via_token", + "discriminator": 4, + "status": "current", + "wire": "u8 discriminator only", + "required_accounts": 3, + "execution_policy": "future_current_surface" + }, + { + "name": "SignMetadata", + "code": "sign_metadata", + "discriminator": 7, + "status": "current", + "wire": "u8 discriminator only", + "required_accounts": 2, + "execution_policy": "future_current_surface" + } + ], + "provenance": "mpl-token-metadata 5.1.1 generated builders", + "materialization": "reserved_for_metadata_and_creator_state_pre_release" + }, + { + "pre_release": "0.4.7-pre.008", + "group": "metadata_normalization_and_creator_unverification", + "instructions": [ + { + "name": "PuffMetadata", + "code": "puff_metadata", + "discriminator": 14, + "status": "current", + "wire": "u8 discriminator only", + "required_accounts": 1, + "execution_policy": "future_current_surface" + }, + { + "name": "RemoveCreatorVerification", + "code": "remove_creator_verification", + "discriminator": 28, + "status": "current", + "wire": "u8 discriminator only", + "required_accounts": 2, + "execution_policy": "future_current_surface" + }, + { + "name": "SetTokenStandard", + "code": "set_token_standard", + "discriminator": 35, + "status": "current", + "wire": "u8 discriminator only", + "required_accounts": 3, + "optional_accounts": [ + "edition" + ], + "execution_policy": "future_current_surface" + } + ], + "provenance": "mpl-token-metadata 5.1.1 generated builders", + "materialization": "reserved_for_metadata_creator_and_asset_classification_pre_release" + }, + { + "pre_release": "0.4.7-pre.009", + "group": "historical_metadata_create_v2", + "instructions": [ + { + "name": "CreateMetadataAccountV2", + "code": "create_metadata_account_v2", + "discriminator": 16, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + } + ], + "wire": "u8 discriminator + historical DataV2 + bool is_mutable using Borsh 0.10", + "account_contract": "metadata, mint, mint_authority signer, payer writable signer, update_authority, system_program, optional rent", + "provenance": "historical Metaplex Token Metadata instruction surface; current SDK DataV2 type reused only for exact compatible wire fields", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.010", + "group": "historical_metadata_v1_create_update", + "instructions": [ + { + "name": "CreateMetadataAccount", + "code": "create_metadata_account", + "discriminator": 0, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + }, + { + "name": "UpdateMetadataAccount", + "code": "update_metadata_account", + "discriminator": 1, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + } + ], + "wire": "u8 discriminator + historical Data/Option fields using Borsh 0.10", + "provenance": "historical generated Metaplex Token Metadata surface", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.011", + "group": "master_edition_creation_and_v1_conversion", + "instructions": [ + { + "name": "CreateMasterEditionV3", + "code": "create_master_edition_v3", + "discriminator": 17, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "ConvertMasterEditionV1ToV2", + "code": "convert_master_edition_v1_to_v2", + "discriminator": 12, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + } + ], + "wire": "CreateMasterEditionV3: u8 discriminator + Option max_supply; conversion: u8 discriminator only", + "account_contract": "exact generated Rust SDK account order and signer/writable flags; optional rent accepted for CreateMasterEditionV3", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.012", + "group": "printed_edition_mint_token_and_vault_proxy", + "instructions": [ + { + "name": "MintNewEditionFromMasterEditionViaToken", + "code": "mint_new_edition_from_master_edition_via_token", + "discriminator": 11, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "MintNewEditionFromMasterEditionViaVaultProxy", + "code": "mint_new_edition_from_master_edition_via_vault_proxy", + "discriminator": 13, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + } + ], + "wire": "u8 discriminator + MintNewEditionFromMasterEditionViaTokenArgs { edition: u64 }", + "account_contract": "exact generated Rust SDK account order and signer/writable flags; optional rent accepted after the required program accounts", + "edition_marker_contract": "edition marker PDA is writable and corresponds to floor(edition / EDITION_MARKER_BIT_SIZE); PDA derivation validation remains planned with account decoding", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.013", + "group": "collection_size_and_bubblegum_bridge", + "instructions": [ + { + "name": "SetCollectionSize", + "code": "set_collection_size", + "discriminator": 34, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "BubblegumSetCollectionSize", + "code": "bubblegum_set_collection_size", + "discriminator": 36, + "status": "bridge_current", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_until_bubblegum_boundary_proof" + } + ], + "wire": "u8 discriminator + SetCollectionSizeArgs { size: u64 }", + "account_contract": "exact generated Rust SDK account order and signer/writable flags; optional collection authority record accepted", + "provenance_boundary": "BubblegumSetCollectionSize is decoded as a Token Metadata bridge instruction and does not claim complete Bubblegum coverage", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.014", + "group": "programmable_delegate_and_revoke", + "instructions": [ + { + "name": "Delegate", + "code": "delegate", + "discriminator": 44, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_and_authorization_rules_proof" + }, + { + "name": "Revoke", + "code": "revoke", + "discriminator": 45, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_and_authorization_rules_proof" + } + ], + "wire": "u8 discriminator + DelegateArgs or RevokeArgs Borsh enum; DelegateArgs may contain optional AuthorizationData", + "account_contract": "14 positional accounts with Metaplex program placeholders for absent optional accounts; exact signer/writable invariants on authority, payer, metadata and optional writable records", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.015", + "group": "programmable_transfer_lock_unlock_burn_close", + "instructions": [ + { + "name": "Burn", + "code": "burn", + "discriminator": 41, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "Lock", + "code": "lock", + "discriminator": 46, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "Unlock", + "code": "unlock", + "discriminator": 47, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "Transfer", + "code": "transfer", + "discriminator": 49, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "CloseAccounts", + "code": "close_accounts", + "discriminator": 57, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + } + ], + "wire": "Borsh enums TransferArgs/LockArgs/UnlockArgs/BurnArgs; CloseAccounts discriminator only", + "account_contract": "exact generated Rust SDK positional accounts and signer/writable invariants; Metaplex program placeholders accepted for absent optional programmable accounts", + "runtime_registration": "pending_full_decoder_and_account_validation" + }, + { + "pre_release": "0.4.7-pre.016", + "group": "legacy_delegated_freeze_thaw_and_inventory_audit", + "instructions": [ + { + "name": "FreezeDelegatedAccount", + "code": "freeze_delegated_account", + "discriminator": 26, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + }, + { + "name": "ThawDelegatedAccount", + "code": "thaw_delegated_account", + "discriminator": 27, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + } + ], + "wire": "u8 discriminator only", + "account_contract": "delegate writable signer, token account writable, edition/mint/token program readonly", + "inventory_audit": "official generated Rust SDK instruction module index audited; remaining modules are tracked before account-state decoding", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.017", + "group": "legacy_nft_burn_and_printing_token_mint", + "instructions": [ + { + "name": "BurnNft", + "code": "burn_nft", + "discriminator": 29, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + }, + { + "name": "BurnEditionNft", + "code": "burn_edition_nft", + "discriminator": 37, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + }, + { + "name": "DeprecatedMintNewEditionFromMasterEditionViaPrintingToken", + "code": "deprecated_mint_new_edition_from_master_edition_via_printing_token", + "discriminator": 3, + "status": "deprecated_historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + } + ], + "wire": "u8 discriminator only", + "account_contract": "exact generated Rust SDK positional signer/writable contracts; BurnNft collection metadata, deprecated printing-token reservation list are optional trailing accounts", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.018", + "group": "escrow_transfer_and_collect", + "instructions": [ + { + "name": "CreateEscrowAccount", + "code": "create_escrow_account", + "discriminator": 38, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "CloseEscrowAccount", + "code": "close_escrow_account", + "discriminator": 39, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "TransferOutOfEscrow", + "code": "transfer_out_of_escrow", + "discriminator": 40, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "Collect", + "code": "collect", + "discriminator": 54, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_authority_proof" + } + ], + "wire": "Create/Close/Collect discriminator only; TransferOutOfEscrow discriminator + amount u64", + "account_contract": "exact generated Rust SDK signer/writable contracts with optional trailing authority on create and transfer", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.019", + "group": "modern_create_wrapper", + "instructions": [ + { + "name": "Create", + "code": "create", + "discriminator": 42, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + } + ], + "wire": "u8 discriminator + CreateArgs enum", + "account_contract": "nine positional accounts with Metaplex placeholders for optional master edition and SPL Token Program", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.020", + "group": "modern_mint_wrapper", + "instructions": [ + { + "name": "Mint", + "code": "mint", + "discriminator": 43, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + } + ], + "wire": "u8 discriminator + MintArgs::V1 { amount: u64, authorization_data: Option }", + "account_contract": "fifteen positional accounts with Metaplex placeholders for optional token owner, master edition, token record, delegate record and authorization-rule accounts", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.021", + "group": "modern_print_wrapper", + "instructions": [ + { + "name": "Print", + "code": "print", + "discriminator": 55, + "variants": [ + "V1", + "V2" + ], + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + } + ], + "wire": "u8 discriminator + PrintArgs::{V1,V2} { edition: u64 }", + "account_contract": "eighteen positional accounts with a Metaplex placeholder or writable Token Record at position 6", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.022", + "group": "modern_update_wrapper", + "instructions": [ + { + "name": "Update", + "code": "update", + "discriminator": 50, + "variants": [ + "V1", + "AsUpdateAuthorityV2", + "AsAuthorityItemDelegateV2", + "AsCollectionDelegateV2", + "AsDataDelegateV2", + "AsProgrammableConfigDelegateV2", + "AsDataItemDelegateV2", + "AsCollectionItemDelegateV2", + "AsProgrammableConfigItemDelegateV2" + ], + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + } + ], + "wire": "u8 discriminator + UpdateArgs enum", + "account_contract": "eleven positional accounts with Metaplex placeholders for optional delegate record, token, edition and authorization-rule accounts", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.023", + "group": "modern_use_verify_unverify_wrappers", + "instructions": [ + { + "name": "Use", + "code": "use", + "discriminator": 51, + "variants": [ + "V1" + ], + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "Verify", + "code": "verify", + "discriminator": 52, + "variants": [ + "CreatorV1", + "CollectionV1" + ], + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "Unverify", + "code": "unverify", + "discriminator": 53, + "variants": [ + "CreatorV1", + "CollectionV1" + ], + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + } + ], + "wire": "u8 discriminator + UseArgs or VerificationArgs enum", + "account_contract": "twelve positional accounts for Use; eight positional accounts for Verify/Unverify, with Metaplex placeholders for optional accounts", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.024", + "group": "final_instruction_inventory_resize_migrate_and_legacy_gaps", + "instructions": [ + { + "name": "DeprecatedCreateMasterEdition", + "code": "deprecated_create_master_edition", + "discriminator": 2, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden" + }, + { + "name": "DeprecatedSetReservationList", + "code": "deprecated_set_reservation_list", + "discriminator": 5, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden" + }, + { + "name": "DeprecatedCreateReservationList", + "code": "deprecated_create_reservation_list", + "discriminator": 6, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden" + }, + { + "name": "DeprecatedMintPrintingTokensViaToken", + "code": "deprecated_mint_printing_tokens_via_token", + "discriminator": 8, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden" + }, + { + "name": "DeprecatedMintPrintingTokens", + "code": "deprecated_mint_printing_tokens", + "discriminator": 9, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden" + }, + { + "name": "CreateMasterEdition", + "code": "create_master_edition", + "discriminator": 10, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden" + }, + { + "name": "Migrate", + "code": "migrate", + "discriminator": 48, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "Resize", + "code": "resize", + "discriminator": 56, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + } + ], + "wire": "u8 discriminator only", + "account_contract": "exact IDL/generated positional signer and writable flags", + "runtime_registration": "pending_full_decoder_validation", + "inventory_proof": { + "covered_discriminators": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57 + ], + "missing_discriminators": [], + "coverage_entries": 58 + } + } + ], + "implemented_account_groups": [ + { + "group": "metadata_account_v1", + "layouts": [ + "MetadataV1" + ], + "owner_validation": "exact_metaplex_token_metadata_program", + "pda_seeds": [ + "metadata", + "program_id", + "mint" + ], + "bounds": { + "account_bytes": 1024, + "name_bytes": 32, + "symbol_bytes": 10, + "uri_bytes": 200, + "creators": 5 + }, + "runtime_registration": "not_enabled" + }, + { + "group": "edition_and_master_edition_accounts", + "status": "implemented_pre_026", + "layouts": [ + "DeprecatedMasterEditionV1", + "MasterEditionV2", + "EditionV1" + ], + "owner": "mpl_token_metadata_program", + "pda_seeds": [ + "metadata", + "program_id", + "mint", + "edition" + ], + "bounds": { + "max_account_bytes": 128, + "edition_v1_exact_bytes": 41 + }, + "policy": { + "DeprecatedMasterEditionV1": "historical_decode_only", + "MasterEditionV2": "active_state", + "EditionV1": "active_state" + } + }, + { + "group": "edition_marker_accounts", + "status": "implemented_pre_027", + "layouts": [ + "EditionMarker", + "EditionMarkerV2" + ], + "owner": "mpl_token_metadata_program", + "pda_seeds": { + "EditionMarker": [ + "metadata", + "program_id", + "mint", + "edition", + "edition_div_248_decimal" + ], + "EditionMarkerV2": [ + "metadata", + "program_id", + "mint", + "edition", + "marker" + ] + }, + "bitmap": { + "EditionMarker": { + "ledger_bytes": 31, + "editions_per_group": 248, + "bit_order": "most_significant_bit_first" + }, + "EditionMarkerV2": { + "ledger": "borsh_vec_u8", + "max_decoder_ledger_bytes": 1048576, + "bit_order": "most_significant_bit_first" + } + }, + "runtime_registration": "not_enabled" + }, + { + "group": "token_record_account", + "status": "implemented_pre_028", + "layouts": [ + "TokenRecord" + ], + "owner": "mpl_token_metadata_program", + "length": 80, + "pda_seeds": [ + "metadata", + "program_id", + "mint", + "token_record", + "token_account" + ], + "fields": [ + "bump", + "state", + "rule_set_revision", + "delegate", + "delegate_role", + "locked_transfer" + ], + "token_states": [ + "Unlocked", + "Locked", + "Listed" + ], + "delegate_roles": [ + "Sale", + "Transfer", + "Utility", + "Staking", + "Standard", + "LockedTransfer", + "Migration" + ], + "runtime_registration": "not_enabled" + }, + { + "group": "delegate_and_authority_records", + "status": "implemented_pre_029", + "layouts": [ + "MetadataDelegateRecord", + "HolderDelegateRecord", + "CollectionAuthorityRecord", + "UseAuthorityRecord" + ], + "owner": "mpl_token_metadata_program", + "lengths": { + "MetadataDelegateRecord": 98, + "HolderDelegateRecord": 98, + "CollectionAuthorityRecord": "3_or_35", + "UseAuthorityRecord": 10 + }, + "pda_seeds": { + "MetadataDelegateRecord": [ + "metadata", + "program_id", + "mint", + "metadata_delegate_role_seed", + "update_authority", + "delegate" + ], + "HolderDelegateRecord": [ + "metadata", + "program_id", + "mint", + "holder_delegate_role_seed", + "owner", + "delegate" + ], + "CollectionAuthorityRecord": [ + "metadata", + "program_id", + "mint", + "collection_authority", + "authority" + ], + "UseAuthorityRecord": [ + "metadata", + "program_id", + "mint", + "user", + "authority" + ] + }, + "metadata_delegate_role_seeds": [ + "authority_item_delegate", + "collection_delegate", + "use_delegate", + "data_delegate", + "programmable_config_delegate", + "data_item_delegate", + "collection_item_delegate", + "prog_config_item_delegate" + ], + "holder_delegate_role_seeds": [ + "print_delegate" + ], + "runtime_registration": "not_enabled" + }, + { + "group": "token_owned_escrow_account", + "status": "implemented_pre_030", + "layouts": [ + "TokenOwnedEscrow" + ], + "owner": "mpl_token_metadata_program", + "lengths": { + "TokenOwner": 35, + "Creator": 67 + }, + "fields": [ + "base_token", + "authority", + "bump" + ], + "pda_seeds": { + "TokenOwner": [ + "metadata", + "program_id", + "base_token", + "0", + "escrow" + ], + "Creator": [ + "metadata", + "program_id", + "base_token", + "1", + "creator", + "escrow" + ] + }, + "stateful_instruction_effects": { + "CreateEscrowAccount": "creates canonical escrow snapshot", + "CloseEscrowAccount": "closes canonical escrow snapshot", + "TransferOutOfEscrow": "changes escrow-held attribute state without changing account identity", + "Migrate": "may create or update related programmable records; correlation pending", + "Resize": "changes allocated account bytes without changing canonical identity" + }, + "runtime_registration": "not_enabled" + }, + { + "group": "historical_reservation_lists", + "status": "implemented_pre_031_decode_only", + "layouts": [ + "ReservationListV1", + "ReservationListV2" + ], + "max_entries": 200, + "padding_policy": "zero_only" + }, + { + "release": "0.4.7-pre.032", + "name": "exhaustive_account_key_audit", + "status": "implemented", + "published_discriminants": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14 + ], + "missing": [], + "future_unknown_policy": "fail_closed" + } + ], + "account_key_inventory": [ + { + "key": "Uninitialized", + "discriminant": 0, + "decoder": "reject_uninitialized", + "lifecycle": "sentinel" + }, + { + "key": "EditionV1", + "discriminant": 1, + "decoder": "decode_edition_account", + "lifecycle": "active" + }, + { + "key": "MasterEditionV1", + "discriminant": 2, + "decoder": "decode_edition_account", + "lifecycle": "historical_materializable" + }, + { + "key": "ReservationListV1", + "discriminant": 3, + "decoder": "decode_reservation_list_account", + "lifecycle": "historical_materializable" + }, + { + "key": "MetadataV1", + "discriminant": 4, + "decoder": "decode_metadata_account", + "lifecycle": "active" + }, + { + "key": "ReservationListV2", + "discriminant": 5, + "decoder": "decode_reservation_list_account", + "lifecycle": "historical_materializable" + }, + { + "key": "MasterEditionV2", + "discriminant": 6, + "decoder": "decode_edition_account", + "lifecycle": "active" + }, + { + "key": "EditionMarker", + "discriminant": 7, + "decoder": "decode_edition_marker_account", + "lifecycle": "active" + }, + { + "key": "UseAuthorityRecord", + "discriminant": 8, + "decoder": "decode_use_authority_record_account", + "lifecycle": "active" + }, + { + "key": "CollectionAuthorityRecord", + "discriminant": 9, + "decoder": "decode_collection_authority_record_account", + "lifecycle": "active" + }, + { + "key": "TokenOwnedEscrow", + "discriminant": 10, + "decoder": "decode_token_owned_escrow_account", + "lifecycle": "active" + }, + { + "key": "TokenRecord", + "discriminant": 11, + "decoder": "decode_token_record_account", + "lifecycle": "active" + }, + { + "key": "MetadataDelegate", + "discriminant": 12, + "decoder": "decode_metadata_delegate_record_account", + "lifecycle": "active" + }, + { + "key": "EditionMarkerV2", + "discriminant": 13, + "decoder": "decode_edition_marker_account", + "lifecycle": "active_experimental" + }, + { + "key": "HolderDelegate", + "discriminant": 14, + "decoder": "decode_holder_delegate_record_account", + "lifecycle": "active" + } + ], + "account_inventory_proof": { + "published_key_discriminants": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14 + ], + "missing_discriminants": [], + "published_variants": 15, + "decoded_layout_variants": 14, + "explicit_sentinel_rejections": 1, + "unknown_future_policy": "fail_closed_until_inventory_and_decoder_are_updated", + "status": "implemented_pre_032" + }, + "canonical_account_model": { + "status": "implemented_pre_033", + "canonical_version": 1, + "stable_identity": "program_id:account_address", + "lifecycle_classes": [ + "active", + "active_experimental", + "historical" + ], + "materialization_policies": { + "active": "active_state", + "active_experimental": "active_state", + "historical": "historical_state" + }, + "execution_policies": { + "active": "current_candidate", + "active_experimental": "experimental_candidate", + "historical": "never_historical" + }, + "provenance_fields": [ + "source", + "slot", + "write_version", + "signature", + "instruction_index", + "inner_instruction_index", + "committed" + ], + "observation_sources": [ + "live", + "replay", + "backfill", + "repair", + "synthetic" + ], + "historical_policy": "historical layouts remain materializable during backfill/replay but are never executable" + }, + "materialization_policy": { + "decoder_rule": "decode every identifiable historical/current/experimental published wire and account layout", + "materializer_rule": "materialize every stable proven fact including historical/deprecated state with explicit lifecycle and provenance", + "executor_rule": "execute current or experimental callable operations only; historical and obsolete operations are never executable", + "state_authority": "owned account snapshots are authoritative for final state; instruction observations do not duplicate the same state", + "implemented_pre_034": [ + "MetadataV1 canonical active-state projection", + "creator/collection/uses/token-standard/programmable-config bounded payload retention", + "committed provenance requirement", + "stable account-based output identity" + ] + }, + "next_proof": "edition, marker, delegate, token-record, escrow and historical-state materialization without duplicates" +} diff --git a/docs/NATIVE_SOLANA_CLOSURE_AUDIT.md b/docs/NATIVE_SOLANA_CLOSURE_AUDIT.md new file mode 100644 index 0000000..746998f --- /dev/null +++ b/docs/NATIVE_SOLANA_CLOSURE_AUDIT.md @@ -0,0 +1,113 @@ + + + +# Audit de clôture Solana natif `0.4.1` + +## Objet + +Ce document fixe l’état final du jalon `0.4.1` : couverture native, preuves synthétiques, corpus mainnet observés et limites assumées. + +L’absence de transaction mainnet n’autorise ni à supprimer une surface officielle, ni à prétendre qu’elle a été observée. Elle impose une validation synthétique documentée et une tentative de découverte bornée lorsque la surface est officiellement déclarée. + +## Surfaces couvertes + +`kb_decoder_solana_core` couvre 18 surfaces exécutables natives ou assimilées : + +1. System Program ; +2. Vote Program ; +3. Stake Program ; +4. Config Program ; +5. Compute Budget Program ; +6. Address Lookup Table Program ; +7. ZK ElGamal Proof Program ; +8. Feature Program ; +9. ancien ZK Token Proof Program ; +10. Native Loader ; +11. BPF Loader v1 ; +12. BPF Loader v2 ; +13. BPF Loader Upgradeable ; +14. Loader v4 ; +15. Ed25519 precompile ; +16. secp256k1 precompile ; +17. secp256r1 precompile ; +18. Slashing Program. + +`StakeConfig11111111111111111111111111111111` est volontairement classé comme compte natif historique non exécutable. + +## Divergence Slashing corrigée + +Agave `v4.1.1` déclare le programme stateless Slashing sous : + +```text +S1ashing11111111111111111111111111111111111 +``` + +La surface a été ajoutée dans `pre.018` : deux instructions exactes, dispatch sans fallback, couverture déclarée et projection de rapport. Aucun corpus public observable n’a été trouvé ; la validation repose donc sur les sources officielles et les fixtures synthétiques. + +## Invariants automatiques + +Les tests garantissent que : + +- chaque entrée de `kb_program_ids::native_program_ids()` possède exactement une surface de décodeur ; +- chaque surface possède au moins une déclaration de couverture ; +- aucune déclaration n’utilise le fallback `unclassified_native_instruction` ; +- chaque déclaration référence une surface réellement déclarée ; +- les identités programme/surface/entrée sont uniques ; +- le Slashing Program est dispatché vers son parseur exact ; +- ZK Token Proof historique passe par le fallback actuel no-op sans prétendre vérifier une preuve. + +## Corpus et replays validés + +| Programme | Résultat de clôture | +|---------------------------|------------------------------------------------------------------------------| +| System | 817 inputs décodés, 258 sorties, 52 refus attendus sur transactions échouées | +| Config | 96 inputs décodés, 96 sorties admin | +| Stake | 448 inputs décodés, 447 sorties, 1 refus attendu | +| Vote | 600 inputs décodés, 600 sorties | +| Compute Budget | 2 746 inputs décodés, 1 603 profils transactionnels | +| Address Lookup Table | 104 inputs décodés, 104 sorties lifecycle | +| ZK ElGamal | 151 inputs décodés, 139 sorties de contexte | +| Slashing | zéro corpus public observable, validation synthétique | +| Feature | zéro corpus observable dans les campagnes adressées, validation synthétique | +| Loaders rares | zéro corpus observable pour certaines générations, validation synthétique | +| ZK Token Proof historique | zéro corpus public observable, runtime actuel no-op, validation synthétique | + +Les campagnes de clôture rapportées ont `unmatched = 0`, `failedInputs = 0`, `unsupported = 0` et ne produisent pas d’erreurs opérationnelles dans les journaux fournis. + +## Backfills + +Aucun backfill supplémentaire n’est requis pour clôturer `0.4.1`. + +Le mode `program_latest` ajouté dans `pre.019` reste disponible pour de futures recherches sans signature d’ancrage. Il ne doit pas être utilisé pour inventer un corpus absent sur des surfaces feature-gated ou historiquement rares. + +## Matérialisations + +La clôture instructionnelle est documentée dans `docs/NATIVE_SOLANA_MATERIALIZATION_AUDIT.md`. + +Aucune projection native stable immédiatement dérivable du graphe core actuel n’est identifiée comme manquante. Les snapshots finaux de comptes et métriques runtime restent différés. + +## Validations finales + +Validations utilisateur finales rapportées pour la clôture : + +```text +kb_program_ids 5 tests +kb_decoder_solana_core 114 tests +kb_materializer_lifecycle 16 tests +kb_materializer_admin 7 tests +kb_materializer_compliance_audit 7 tests +kb_materializer_staking 8 tests +kb_config 36 tests +kb_pipeline 45 tests +kb_store_pg 45 tests avec PostgreSQL réel +kb_app_demo 78 tests +cargo clippy --all-targets propre +``` + +Le démarrage Tauri validé après `pre.021-fix.001` confirme les 53 routes de logging et l’initialisation PostgreSQL. `pre.022` ne modifie pas la configuration Tauri. + +## Décision + +`0.4.1` est clôturée. + +La prochaine session doit utiliser `prompts/022_v0_4_2_executor_solana_core.md` et démarrer `0.4.2` sur l’infrastructure d’exécution et `kb_executor_solana_core`. diff --git a/docs/NATIVE_SOLANA_DECODER_MATRIX.json b/docs/NATIVE_SOLANA_DECODER_MATRIX.json new file mode 100644 index 0000000..b34fe57 --- /dev/null +++ b/docs/NATIVE_SOLANA_DECODER_MATRIX.json @@ -0,0 +1,155 @@ +{ + "file": "docs/NATIVE_SOLANA_DECODER_MATRIX.json", + "version": 1, + "release": "0.4.2-pre.023", + "surface_count": 18, + "coverage_entry_count": 121, + "registry_contract": "Exact equality with kb_program_ids::native_program_ids and SolanaCoreDecoder::surfaces", + "completeness_contract": "Each surface has exact declared coverage count plus a dedicated official-enum or audited-wire completeness test", + "surfaces": [ + { + "surface_code": "solana_native_address_lookup_table", + "program_id": "AddressLookupTab1e1111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 5, + "source_contract": "solana-address-lookup-table-interface@3.1.x official instruction enum", + "completeness_test": "address_lookup_table::tests::coverage_uses_official_encoder_discriminants" + }, + { + "surface_code": "solana_native_bpf_loader_deprecated", + "program_id": "BPFLoader1111111111111111111111111111111111", + "runtime_status": "historical", + "expected_coverage_entries": 2, + "source_contract": "solana-loader-v2-interface@3.0.0 immutable write/finalize layout", + "completeness_test": "loaders::tests::coverage_declares_every_loader_entry" + }, + { + "surface_code": "solana_native_bpf_loader", + "program_id": "BPFLoader2111111111111111111111111111111111", + "runtime_status": "historical", + "expected_coverage_entries": 2, + "source_contract": "solana-loader-v2-interface@3.0.0 immutable write/finalize layout", + "completeness_test": "loaders::tests::coverage_declares_every_loader_entry" + }, + { + "surface_code": "solana_native_bpf_loader_upgradeable", + "program_id": "BPFLoaderUpgradeab1e11111111111111111111111", + "runtime_status": "current_with_historical_variants", + "expected_coverage_entries": 8, + "source_contract": "solana-loader-v3-interface@8.0.1 with wincode exact wire layout", + "completeness_test": "loaders::tests::coverage_declares_every_loader_entry" + }, + { + "surface_code": "solana_native_compute_budget", + "program_id": "ComputeBudget111111111111111111111111111111", + "runtime_status": "current_with_historical_variants", + "expected_coverage_entries": 6, + "source_contract": "solana-compute-budget-interface@3.0.0 plus historical RequestUnitsDeprecated", + "completeness_test": "compute_budget::tests::coverage_declares_current_and_historical_entries" + }, + { + "surface_code": "solana_native_config", + "program_id": "Config1111111111111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 1, + "source_contract": "solana-config-interface@2.0.0 generic bounded store contract", + "completeness_test": "config::tests::coverage_declares_one_generic_store_surface" + }, + { + "surface_code": "solana_native_ed25519", + "program_id": "Ed25519SigVerify111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 1, + "source_contract": "Agave Ed25519 precompile exact offset-table layout", + "completeness_test": "precompiles::tests::coverage_declares_exactly_three_signature_precompile_surfaces" + }, + { + "surface_code": "solana_native_feature", + "program_id": "Feature111111111111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 1, + "source_contract": "solana-feature-gate-interface revoke-pending-activation instruction contract", + "completeness_test": "feature::tests::coverage_declares_the_single_official_instruction" + }, + { + "surface_code": "solana_native_loader_v4", + "program_id": "LoaderV411111111111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 7, + "source_contract": "solana-loader-v4-interface@3.1.0 official instruction enum", + "completeness_test": "loaders::tests::coverage_declares_every_loader_entry" + }, + { + "surface_code": "solana_native_loader", + "program_id": "NativeLoader1111111111111111111111111111111", + "runtime_status": "runtime_dispatch", + "expected_coverage_entries": 1, + "source_contract": "Agave native-loader runtime direct-invocation behavior", + "completeness_test": "loaders::tests::coverage_declares_every_loader_entry" + }, + { + "surface_code": "solana_native_secp256k1", + "program_id": "KeccakSecp256k11111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 1, + "source_contract": "Agave secp256k1 precompile exact offset-table layout", + "completeness_test": "precompiles::tests::coverage_declares_exactly_three_signature_precompile_surfaces" + }, + { + "surface_code": "solana_native_secp256r1", + "program_id": "Secp256r1SigVerify1111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 1, + "source_contract": "Agave secp256r1 precompile exact offset-table layout", + "completeness_test": "precompiles::tests::coverage_declares_exactly_three_signature_precompile_surfaces" + }, + { + "surface_code": "solana_native_slashing", + "program_id": "S1ashing11111111111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 2, + "source_contract": "SIMD-0204 and Agave Slashing builtin instruction contract", + "completeness_test": "slashing::tests::coverage_declares_exactly_two_official_instructions" + }, + { + "surface_code": "solana_native_stake", + "program_id": "Stake11111111111111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 18, + "source_contract": "solana-stake-interface@4.3.x official StakeInstruction variants", + "completeness_test": "stake::tests::coverage_declares_every_official_stake_instruction" + }, + { + "surface_code": "solana_native_system", + "program_id": "11111111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 14, + "source_contract": "solana-system-interface@3.2.x official SystemInstruction variants", + "completeness_test": "system::tests::coverage_declares_all_current_system_variants" + }, + { + "surface_code": "solana_native_vote", + "program_id": "Vote111111111111111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 20, + "source_contract": "solana-vote-interface@6.0.x official VoteInstruction variants", + "completeness_test": "vote::tests::coverage_declares_every_official_vote_instruction" + }, + { + "surface_code": "solana_native_zk_elgamal_proof", + "program_id": "ZkE1Gama1Proof11111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 13, + "source_contract": "solana-zk-elgamal-proof-interface official ProofInstruction variants", + "completeness_test": "zk_elgamal::tests::coverage_declares_all_thirteen_official_instructions" + }, + { + "surface_code": "solana_native_zk_token_proof", + "program_id": "ZkTokenProof1111111111111111111111111111111", + "runtime_status": "historical_with_current_noop", + "expected_coverage_entries": 18, + "source_contract": "audited historical ZK Token Proof wire table plus current runtime no-op behavior", + "completeness_test": "zk_token_proof::tests::coverage_declares_seventeen_historical_entries_and_current_noop_fallback" + } + ] +} diff --git a/docs/NATIVE_SOLANA_EXECUTION_MATRIX.json b/docs/NATIVE_SOLANA_EXECUTION_MATRIX.json new file mode 100644 index 0000000..1aabe9f --- /dev/null +++ b/docs/NATIVE_SOLANA_EXECUTION_MATRIX.json @@ -0,0 +1,1279 @@ +{ + "file": "docs/NATIVE_SOLANA_EXECUTION_MATRIX.json", + "version": 1, + "release": "0.4.2-pre.022", + "surface_count": 18, + "callable_operation_count": 109, + "surfaces": [ + { + "surface": "system", + "program_ids": [ + "11111111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.system.transfer", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.transfer_many", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.create_account", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.create_account_with_seed", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.allocate", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.allocate_with_seed", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.assign", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.assign_with_seed", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.transfer_with_seed", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.create_account_allow_prefund", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.nonce.create", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.nonce.create_with_seed", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.nonce.initialize", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.nonce.advance", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.nonce.authorize", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.nonce.withdraw", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.nonce.upgrade", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + } + ] + }, + { + "surface": "compute_budget", + "program_ids": [ + "ComputeBudget111111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.compute_budget.request_heap_frame", + "construction_source": "solana-compute-budget-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "resource ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "offline_only", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.compute_budget.set_compute_unit_limit", + "construction_source": "solana-compute-budget-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "resource ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "offline_only", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.compute_budget.set_compute_unit_price", + "construction_source": "solana-compute-budget-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "resource ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "offline_only", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.compute_budget.set_loaded_accounts_data_size_limit", + "construction_source": "solana-compute-budget-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "resource ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "offline_only", + "ui_exposure": "safe_subset" + } + ] + }, + { + "surface": "address_lookup_table", + "program_ids": [ + "AddressLookupTab1e1111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.address_lookup_table.create", + "construction_source": "solana-address-lookup-table-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "required", + "policy": "authority, capacity, rent and cooldown", + "unit_test_contract": "address_lookup_table::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.address_lookup_table.extend", + "construction_source": "solana-address-lookup-table-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "required", + "policy": "authority, capacity, rent and cooldown", + "unit_test_contract": "address_lookup_table::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.address_lookup_table.freeze", + "construction_source": "solana-address-lookup-table-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "required", + "policy": "authority, capacity, rent and cooldown", + "unit_test_contract": "address_lookup_table::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.address_lookup_table.deactivate", + "construction_source": "solana-address-lookup-table-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "required", + "policy": "authority, capacity, rent and cooldown", + "unit_test_contract": "address_lookup_table::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.address_lookup_table.close", + "construction_source": "solana-address-lookup-table-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "required", + "policy": "authority, capacity, rent and cooldown", + "unit_test_contract": "address_lookup_table::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "ed25519", + "program_ids": [ + "Ed25519SigVerify111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.ed25519.verify_inline", + "construction_source": "solana-ed25519-program official layout", + "construction_kind": "official_layout", + "stateful_preflight": "none", + "policy": "bounded offsets and payload", + "unit_test_contract": "precompiles::tests", + "cluster_validation": "offline_only", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.ed25519.verify_offsets", + "construction_source": "solana-ed25519-program official layout", + "construction_kind": "official_layout", + "stateful_preflight": "none", + "policy": "bounded offsets and payload", + "unit_test_contract": "precompiles::tests", + "cluster_validation": "offline_only", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "secp256k1", + "program_ids": [ + "KeccakSecp256k11111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.secp256k1.verify_inline", + "construction_source": "solana-secp256k1-program official layout", + "construction_kind": "official_layout", + "stateful_preflight": "none", + "policy": "index zero and bounded offsets", + "unit_test_contract": "precompiles::tests", + "cluster_validation": "offline_only", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.secp256k1.verify_offsets", + "construction_source": "solana-secp256k1-program official layout", + "construction_kind": "official_layout", + "stateful_preflight": "none", + "policy": "index zero and bounded offsets", + "unit_test_contract": "precompiles::tests", + "cluster_validation": "offline_only", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "secp256r1", + "program_ids": [ + "Secp256r1SigVerify1111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.secp256r1.verify_inline", + "construction_source": "solana-secp256r1-program official layout", + "construction_kind": "official_layout", + "stateful_preflight": "none", + "policy": "low-S and bounded offsets", + "unit_test_contract": "precompiles::tests", + "cluster_validation": "offline_only", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.secp256r1.verify_offsets", + "construction_source": "solana-secp256r1-program official layout", + "construction_kind": "official_layout", + "stateful_preflight": "none", + "policy": "low-S and bounded offsets", + "unit_test_contract": "precompiles::tests", + "cluster_validation": "offline_only", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "config", + "program_ids": [ + "Config1111111111111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.config.create_account", + "construction_source": "solana-config-interface wire contract", + "construction_kind": "official_wire", + "stateful_preflight": "required", + "policy": "owner, allocation and rent", + "unit_test_contract": "native_admin::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.config.store", + "construction_source": "solana-config-interface wire contract", + "construction_kind": "official_wire", + "stateful_preflight": "required", + "policy": "owner, allocation and rent", + "unit_test_contract": "native_admin::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "feature", + "program_ids": [ + "Feature111111111111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.feature.activate", + "construction_source": "solana-feature-gate-interface official sequences", + "construction_kind": "official_builder", + "stateful_preflight": "required", + "policy": "pending state and rent", + "unit_test_contract": "native_admin::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.feature.revoke_pending_activation", + "construction_source": "solana-feature-gate-interface official sequences", + "construction_kind": "official_builder", + "stateful_preflight": "required", + "policy": "pending state and rent", + "unit_test_contract": "native_admin::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "slashing", + "program_ids": [ + "S1ashing11111111111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.slashing.close_violation_report", + "construction_source": "solana-slashing-program interface and SIMD-0204", + "construction_kind": "official_wire", + "stateful_preflight": "required", + "policy": "proof shape, epoch window and retention", + "unit_test_contract": "slashing::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.slashing.submit_duplicate_block_proof", + "construction_source": "solana-slashing-program interface and SIMD-0204", + "construction_kind": "official_wire", + "stateful_preflight": "required", + "policy": "proof shape, epoch window and retention", + "unit_test_contract": "slashing::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "zk_elgamal", + "program_ids": [ + "ZkE1Gama1Proof11111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.zk_elgamal.verify_inline", + "construction_source": "solana-zk-elgamal-proof-interface POD contract", + "construction_kind": "official_wire", + "stateful_preflight": "required", + "policy": "proof size and context owner/space/rent", + "unit_test_contract": "zk_elgamal::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.zk_elgamal.verify_from_account", + "construction_source": "solana-zk-elgamal-proof-interface POD contract", + "construction_kind": "official_wire", + "stateful_preflight": "required", + "policy": "proof size and context owner/space/rent", + "unit_test_contract": "zk_elgamal::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.zk_elgamal.close_context_state", + "construction_source": "solana-zk-elgamal-proof-interface POD contract", + "construction_kind": "official_wire", + "stateful_preflight": "required", + "policy": "proof size and context owner/space/rent", + "unit_test_contract": "zk_elgamal::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "stake", + "program_ids": [ + "Stake11111111111111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.stake.initialize", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.initialize_checked", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.create_account", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.create_account_with_seed", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.create_account_checked", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.create_account_with_seed_checked", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.split", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.split_with_seed", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.merge", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.create_account_and_delegate", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.create_account_with_seed_and_delegate", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.authorize", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.authorize_checked", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.authorize_with_seed", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.authorize_checked_with_seed", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.delegate", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.withdraw", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.deactivate", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.set_lockup", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.set_lockup_checked", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.get_minimum_delegation", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.deactivate_delinquent", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.move_stake", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.move_lamports", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "vote", + "program_ids": [ + "Vote111111111111111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.vote.initialize_account", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.initialize_account_v2", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.create_account", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.create_account_with_seed", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.create_account_v2", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.create_account_with_seed_v2", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.authorize", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.authorize_checked", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.authorize_with_seed", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.authorize_checked_with_seed", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.vote", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.vote_switch", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.withdraw", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.update_validator_identity", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.update_commission", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.update_vote_state", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.update_vote_state_switch", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.compact_update_vote_state", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.compact_update_vote_state_switch", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.tower_sync", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.tower_sync_switch", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.update_commission_collector", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.update_commission_bps", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.deposit_delegator_rewards", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "loader_v3", + "program_ids": [ + "BPFLoaderUpgradeab1e11111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.loader_v3.create_buffer", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.write", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.deploy_with_max_program_len", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.upgrade", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.set_buffer_authority", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.set_buffer_authority_checked", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.set_upgrade_authority", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.set_upgrade_authority_checked", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.close", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.close_any", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.extend_program", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "loader_v4", + "program_ids": [ + "LoaderV411111111111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.loader_v4.create_buffer", + "construction_source": "solana-loader-v4-interface official discriminants and accounts", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "owner, authority, lifecycle and rent", + "unit_test_contract": "loader_v4::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v4.set_program_length", + "construction_source": "solana-loader-v4-interface official discriminants and accounts", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "owner, authority, lifecycle and rent", + "unit_test_contract": "loader_v4::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v4.write", + "construction_source": "solana-loader-v4-interface official discriminants and accounts", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "owner, authority, lifecycle and rent", + "unit_test_contract": "loader_v4::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v4.copy", + "construction_source": "solana-loader-v4-interface official discriminants and accounts", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "owner, authority, lifecycle and rent", + "unit_test_contract": "loader_v4::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v4.deploy", + "construction_source": "solana-loader-v4-interface official discriminants and accounts", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "owner, authority, lifecycle and rent", + "unit_test_contract": "loader_v4::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v4.deploy_from_source", + "construction_source": "solana-loader-v4-interface official discriminants and accounts", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "owner, authority, lifecycle and rent", + "unit_test_contract": "loader_v4::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v4.retract", + "construction_source": "solana-loader-v4-interface official discriminants and accounts", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "owner, authority, lifecycle and rent", + "unit_test_contract": "loader_v4::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v4.transfer_authority", + "construction_source": "solana-loader-v4-interface official discriminants and accounts", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "owner, authority, lifecycle and rent", + "unit_test_contract": "loader_v4::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v4.finalize", + "construction_source": "solana-loader-v4-interface official discriminants and accounts", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "owner, authority, lifecycle and rent", + "unit_test_contract": "loader_v4::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "bpf_loader_v1", + "program_ids": [ + "BPFLoader1111111111111111111111111111111111" + ], + "execution_status": "non_invocable", + "classification": "historical_decode_only", + "operations": [], + "official_reason": "Deprecated immutable loader generation retained for historical decoding; no current client builder is exposed." + }, + { + "surface": "bpf_loader_v2", + "program_ids": [ + "BPFLoader2111111111111111111111111111111111" + ], + "execution_status": "non_invocable", + "classification": "historical_decode_only", + "operations": [], + "official_reason": "Immutable loader generation retained for historical decoding; deployment administration moved to newer loader interfaces." + }, + { + "surface": "native_loader", + "program_ids": [ + "NativeLoader1111111111111111111111111111111" + ], + "execution_status": "non_invocable", + "classification": "validator_software_loader", + "operations": [], + "official_reason": "Native Loader loads validator-native programs and has no standalone client instruction contract." + }, + { + "surface": "zk_token_proof_historical", + "program_ids": [ + "ZkTokenProof1111111111111111111111111111111" + ], + "execution_status": "non_invocable", + "classification": "historical_decode_only", + "operations": [], + "official_reason": "Historical ZK Token Proof instructions are retained for decoding; the current runtime surface is a no-op stub and no modern builder is invented." + } + ] +} diff --git a/docs/NATIVE_SOLANA_MATERIALIZATION_AUDIT.md b/docs/NATIVE_SOLANA_MATERIALIZATION_AUDIT.md new file mode 100644 index 0000000..5e161e6 --- /dev/null +++ b/docs/NATIVE_SOLANA_MATERIALIZATION_AUDIT.md @@ -0,0 +1,85 @@ + + + +# Audit des matérialisations Solana natives + +## Conclusion + +`0.4.1` clôt les matérialisations natives instructionnelles stables. Les projections actives couvrent les mutations ou profils qui peuvent être déduits de façon déterministe depuis le graphe core transactionnel et les événements décodés. + +Les sorties ne sont pas des snapshots finaux de comptes ou de runtime. Les états finaux qui exigent l’état précédent/suivant d’un compte, les sysvars historiques ou des métriques runtime restent différés jusqu’à enrichissement explicite du contrat core. + +## Projections actives par matérialiseur + +| Matérialiseur | Surfaces | Projection | +|------------------------------------|----------------------|---------------------------------------------------------------------------------------------------------------------| +| `kb_materializer_lifecycle` | Address Lookup Table | lifecycle create/freeze/extend/deactivate/close | +| `kb_materializer_lifecycle` | Feature | révocation de feature gate | +| `kb_materializer_lifecycle` | Loaders | déploiement, finalisation et transitions lifecycle stables | +| `kb_materializer_lifecycle` | System | durable nonce, create account et allocate | +| `kb_materializer_lifecycle` | ZK ElGamal | création ou fermeture d’un compte de contexte | +| `kb_materializer_lifecycle` | Slashing | initialisation ou fermeture d’un rapport de violation | +| `kb_materializer_admin` | System | assign et assign with seed | +| `kb_materializer_admin` | Config | écriture opaque bornée avec clés, taille, SHA-256 et préfixe | +| `kb_materializer_admin` | Loaders | changements d’autorité | +| `kb_materializer_compliance_audit` | Loaders | écritures/copies de bytecode bornées par offsets, tailles, SHA-256 et préfixes | +| `kb_materializer_compliance_audit` | Compute Budget | profil transactionnel agrégé, last-write-wins, une seule sortie par transaction | +| `kb_materializer_staking` | Stake | compte, délégation, désactivation, split, merge, withdraw, move stake/lamports, autorités et lockup instructionnels | +| `kb_materializer_staking` | Vote | compte, autorités, identité, commission, vote state, retraits et dépôt de rewards instructionnels | + +## Politique des transactions échouées + +Les transactions on-chain échouées restent décodables comme intentions ou diagnostics. Les matérialiseurs mutables utilisent la politique `SuccessfulCommittedOnly` et refusent les observations non commitées au lieu de produire une mutation réussie fictive. + +Le profil Compute Budget est une sortie `ComplianceAudit`, pas une mutation d’état ; il peut donc être matérialisé même lorsque la transaction échoue. Il décrit le budget demandé par les instructions, pas les unités réellement consommées. + +## Absences volontaires + +Les surfaces suivantes ne produisent pas de projection dédiée dans `kb_sol_mat_events` à la clôture de `0.4.1` : + +- Ed25519 ; +- secp256k1 ; +- secp256r1 ; +- preuves ZK sans compte de contexte ; +- ancien ZK Token Proof historique/no-op. + +Leur décodage est déjà un événement d’audit borné. Une matérialisation supplémentaire serait une duplication sans consommateur métier dédié. + +## États finaux différés + +Les éléments suivants ne doivent pas être présentés comme disponibles dans `0.4.1` : + +- état final exact d’un compte Stake ou Vote ; +- activation/désactivation Stake par epoch ; +- crédits Vote cumulés ; +- rewards recalculées par epoch ; +- état final d’un compte System après lamport drain conditionnel ; +- bytecode final complet d’un loader ; +- unités compute réellement consommées ; +- frais finaux réellement facturés ; +- résultat cryptographique recalculé pour signature ou preuve ZK. + +Ces états exigent une extension du contexte core ou un audit de comptes/snapshots dédié. + +## Validations finales `0.4.1` + +Les validations utilisateur finales couvrent : + +| Surface | Corpus / preuve | Résultat | +|----------------------|--------------------------------|----------------------------------------------------------------| +| System | replay ciblé | 817 inputs décodés, 258 sorties, 52 refus attendus | +| Config | replay ciblé | 96 inputs décodés, 96 sorties admin | +| Stake | replay ciblé | 448 inputs décodés, 447 sorties, 1 refus attendu | +| Vote | replay ciblé | 600 inputs décodés, 600 sorties | +| Compute Budget | replay ciblé | 2 746 inputs décodés, 1 603 profils | +| ZK ElGamal | replay ciblé | 151 inputs décodés, 139 sorties de contexte | +| Address Lookup Table | inventaire/replay ciblé | 104 inputs décodés, 104 sorties lifecycle | +| Slashing | sources officielles + fixtures | aucun corpus public observable, validation synthétique assumée | + +Toutes les campagnes de clôture rapportées se terminent avec `unmatched = 0`, `failedInputs = 0`, `unsupported = 0` et des journaux `error.jsonl` vides. + +## Décision de clôture + +Aucune projection native instructionnelle stable n’est identifiée comme manquante dans le périmètre `0.4.1`. + +La suite `0.4.2` peut se concentrer sur l’exécution : plans, simulation, signature, envoi et validation post-exécution, sans rouvrir le jalon de décodage/matérialisation natif. diff --git a/docs/NATIVE_SOLANA_PROGRAMS.md b/docs/NATIVE_SOLANA_PROGRAMS.md new file mode 100644 index 0000000..0a9e2ad --- /dev/null +++ b/docs/NATIVE_SOLANA_PROGRAMS.md @@ -0,0 +1,233 @@ + + + +# Inventaire et couverture des programmes Solana natifs + +## Périmètre + +Le registre `kb_program_ids::native_program_ids()` contient dix-huit surfaces exécutables : programmes runtime natifs, loaders, précompiles et programmes historiques encore observables. Les sysvars et comptes natifs non exécutables restent suivis séparément dans `registry/core_program_id_seed.toml`. + +Memo ne fait pas partie de ce périmètre. Les IDs officiels v1 `Memo1U...`, v3 `MemoSq...` et v4 `Memo4c...` sont réservés dans `kb_decoder_spl_memo`. + +## État du phasage + +| Phase | Contenu | État | +|-------|---------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------| +| A | modèle d’événement, lecture bornée, validation des comptes et diagnostics | implémenté dans `0.4.1-pre.001` | +| B | System Program et Compute Budget | implémenté, replay mainnet validé dans `pre.005` | +| C | Address Lookup Table et loaders | implémenté dans `pre.003` puis `pre.006` | +| D | Vote, Stake, Config et programmes historiques | implémenté : Config/Feature `pre.008`, Vote `pre.010` validé dans `pre.011`, Stake `pre.013` validé mainnet | +| E | précompiles et preuves ZK | précompiles signature `pre.014`, ZK ElGamal Proof `pre.015`, ancien ZK Token Proof `pre.016` | +| F | matérialisations justifiées, diagnostics globaux et clôture | nonce System/contextes ZK `pre.017`; Slashing et invariants de couverture `pre.018`; corpus final à valider | + +Le processor conserve l’identité `solana_native_classifier@0.4.1` pendant les deltas préparatoires. Les nouveaux décodeurs nécessitent donc un force replay ciblé ou global ; la version ne sera relevée qu’avec un changement explicite du contrat de processor. + +## Sources normatives couvertes + +| Surface | Source officielle | Sérialisation retenue | Couverture | +|---------------------------|---------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|-----------------------------------------| +| System Program | `solana-system-interface 3.2.0`, enum `SystemInstruction` | schéma `wincode` officiel compatible avec le format bincode historique, lecture exacte sans octets suffixes | 14 variantes | +| Compute Budget actuel | `solana-compute-budget-interface 3.0.0` + runtime `solana-compute-budget-instruction 4.1.1` | tag `u8` puis entier little-endian/Borsh ; désérialisation runtime unchecked, suffixe accepté et audité | 5 entrées, dont le tag réservé `Unused` | +| Compute Budget historique | ancien `solana-sdk`, `RequestUnitsDeprecated` | tag `0`, `u32 units`, `u32 additional_fee` | 1 variante historique | +| Address Lookup Table | `solana-address-lookup-table-interface 3.1.0`, enum `ProgramInstruction` | schéma `wincode` officiel, lecture exacte | 5 variantes | +| BPF Loader immuable | `solana-loader-v2-interface 3.0.0`, enum `LoaderInstruction` | tag `u32 LE`, offset `u32`, longueur de vector `u64`, lecture exacte | write, finalize sur deux générations | +| BPF Loader upgradeable | `solana-loader-v3-interface 8.0.1`, enum `UpgradeableLoaderInstruction` | wire format historique compatible bincode, champs booléens suffixes avec valeurs historiques documentées | 8 variantes | +| Loader v4 | `solana-loader-v4-interface 3.1.0`, enum `LoaderV4Instruction` | tag et entiers little-endian, tailles exactes | 7 variantes | +| Config Program | `solana-config-interface 2.0.0` et processor `solana-config-program 2.2.20` | `ConfigKeys` en `solana_short_vec`, clés `(Pubkey, bool)`, puis payload typé opaque pour le décodeur générique | 1 écriture générique `store` | +| Feature Gate | `solana-feature-gate-interface 4.0.0`, `FeatureGateInstruction` | tag `u8` exact `0`, sans suffixe | 1 variante | +| Vote Program | `solana-vote-interface ^6.0`, enum `VoteInstruction` | schéma officiel `wincode`, discriminant historique `u32 LE`, lecture exacte sans octets suffixes | 20 variantes | +| Stake Program | `solana-stake-interface ^4.3`, enum `StakeInstruction` | miroir wire privé `wincode` exact du layout historique ; types sémantiques officiels consommés | 18 variantes | +| ZK ElGamal Proof | `solana-zk-elgamal-proof-interface ^0.1`, `ProofInstruction`, types `Pod`, runtime Agave | tag `u8`; preuve inline de taille officielle ou référence compte `u32 LE` sur instruction exacte de 5 octets | 13 variantes | +| ZK Token Proof historique | miroir wire local audité contre `solana-zk-token-sdk 3.1.14`, Agave `v2.0.0`/`v4.1.1` | tag `u8`; layout historique inline ou offset compte sur 5 octets ; fallback runtime actuel sans effet | 17 variantes + fallback no-op | +| Slashing stateless | Agave `v4.1.1` + `solana-program/slashing@fe8da3a` | tag `u8`; fermeture sur 1 octet ou preuve duplicate block sur 305 octets exacts | 2 variantes | + +Les versions de crates sont utilisées comme références reproductibles. Un futur changement d’interface doit entraîner un changement de version du décodeur et un replay ledger/version/hash. + +## System Program + +Program ID : `11111111111111111111111111111111`. + +| Tag historique compatible bincode/wincode `u32 LE` | Entry code | Paramètres structurés | Comptes principaux | État | +|---------------------------------------------------:|--------------------------------|------------------------------------|----------------------------------------|--------| +| 0 | `create_account` | lamports, space, owner | funding, new account | décodé | +| 1 | `assign` | owner | assigned account | décodé | +| 2 | `transfer` | lamports | funding, recipient | décodé | +| 3 | `create_account_with_seed` | base, seed, lamports, space, owner | funding, new account, base optionnelle | décodé | +| 4 | `advance_nonce_account` | aucun | nonce, recent blockhashes, authority | décodé | +| 5 | `withdraw_nonce_account` | lamports | nonce, recipient, sysvars, authority | décodé | +| 6 | `initialize_nonce_account` | authority | nonce, recent blockhashes, rent | décodé | +| 7 | `authorize_nonce_account` | authority | nonce, authority | décodé | +| 8 | `allocate` | space | allocated account | décodé | +| 9 | `allocate_with_seed` | base, seed, space, owner | derived account, base | décodé | +| 10 | `assign_with_seed` | base, seed, owner | derived account, base | décodé | +| 11 | `transfer_with_seed` | lamports, from seed, from owner | funding, base, recipient | décodé | +| 12 | `upgrade_nonce_account` | aucun | nonce account | décodé | +| 13 | `create_account_allow_prefund` | lamports, space, owner | new account, funding conditionnel | décodé | + +Le décodeur utilise le schéma `wincode` dérivé directement par `solana-system-interface 3.2.0`. `wincode::deserialize_exact` conserve la compatibilité octet pour octet avec le format historique de cette enum et refuse les octets suffixes. Le crate n’utilise aucune dépendance directe à `bincode`. + +`pre.017` matérialise les cinq opérations durable nonce sous `durable_nonce_account::0`. Initialize, advance, authorize et upgrade conservent leur transition explicite. Withdraw conserve le montant demandé et la règle officielle de fermeture conditionnelle : le compte n’est détruit que lorsque la totalité de son solde est retirée. La projection ne prétend pas reconstruire l’état final transactionnel du compte et ne duplique pas les deltas SOL du graphe core. + +Les flags signer/writable sont conservés sous deux formes : privilèges attendus par le contrat officiel et privilèges réellement résolus depuis core. Ils ne sont pas confondus, car une CPI signée par PDA ne se reflète pas nécessairement comme signer statique de la transaction. + +## Compute Budget + +Program ID : `ComputeBudget111111111111111111111111111111`. + +| Tag `u8` | Entry code | Taille décodée minimale | Paramètres | Statut | +|---------:|---------------------------------------|------------------------:|--------------------------|-----------------------| +| 0 | `unused_reserved` | 1 | aucun | `ignored` | +| 0 | `request_units_deprecated` | 9 | units, additional fee | `decoded`, historique | +| 1 | `request_heap_frame` | 5 | bytes `u32` | `decoded` | +| 2 | `set_compute_unit_limit` | 5 | compute unit limit `u32` | `decoded` | +| 3 | `set_compute_unit_price` | 9 | micro-lamports `u64` | `decoded` | +| 4 | `set_loaded_accounts_data_size_limit` | 5 | bytes `u32` | `decoded` | + +Le runtime Agave traite les variantes modernes avec `solana_borsh::v1::try_from_slice_unchecked`. Les octets suffixes sont donc acceptés après les 5 ou 9 octets utiles. Le décodeur reproduit cette règle et conserve `trailingDataByteLength`, `trailingDataSha256`, `trailingDataPrefixHex` et `trailingDataSemantics = ignored_by_runtime_borsh_unchecked`. Les payloads trop courts restent `failed`. Le tag `0` demeure dispatché par taille afin de distinguer le tag réservé actuel de la variante historique à neuf octets ; toute autre taille pour ce tag reste `failed`. Un tag inconnu est `unsupported` avec diagnostic borné et hash du payload disponible pour un replay futur. + + +## Address Lookup Table + +Program ID : `AddressLookupTab1e1111111111111111111111111`. + +| Tag `u32 LE` | Entry code | Paramètres | Comptes | État | +|-------------:|---------------------------|------------------------------------------------------------------|--------------------------------------------------|--------| +| 0 | `create_lookup_table` | recent slot, bump seed, politique historique du signer authority | table, authority, payer, System Program | décodé | +| 1 | `freeze_lookup_table` | aucun | table, authority | décodé | +| 2 | `extend_lookup_table` | adresses ajoutées, count, présence du financement | table, authority, paire optionnelle payer/System | décodé | +| 3 | `deactivate_lookup_table` | aucun | table, authority | décodé | +| 4 | `close_lookup_table` | aucun | table, authority, recipient | décodé | + +Le décodeur refuse une paire optionnelle d’extension incomplète et les octets suffixes. La création conserve `expectedSigner = null` pour l’autorité, car l’encodeur officiel actuel ne la marque plus signer tandis que les runtimes historiques antérieurs à v1.12 l’exigeaient. + +La projection `solana_native_lifecycle` produit une sortie `address_lookup_table::0` uniquement si l’observation est exacte, réussie et commitée. Les intentions issues de transactions échouées restent dans decode et sont refusées avant matérialisation. + +## Programmes runtime natifs principaux + +| Code canonique | Program ID | État au début de `pre.018` | +|--------------------|-----------------------------------------------|--------------------------------------------------------------------------------| +| `vote` | `Vote111111111111111111111111111111111111111` | 20 variantes décodées ; 500 `tower_sync` validés mainnet | +| `stake` | `Stake11111111111111111111111111111111111111` | 18 variantes décodées ; 339 instructions mainnet sur neuf entry codes observés | +| `config` | `Config1111111111111111111111111111111111111` | écriture générique `store` décodée ; corpus ciblé à inventorier | +| `zk_elgamal_proof` | `ZkE1Gama1Proof11111111111111111111111111111` | 13 variantes ; 151 instructions mainnet et 139 contextes matérialisés | + +## Loaders + +| Surface | Program ID | Variantes | Statut `pre.006` | +|------------------------|-----------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------|---------------------------------------------| +| Native Loader | `NativeLoader1111111111111111111111111111111` | invocation directe opaque | `ignored`, historique, aucun faux événement | +| BPF Loader deprecated | `BPFLoader1111111111111111111111111111111111` | write, finalize | décodé | +| BPF Loader v2 | `BPFLoader2111111111111111111111111111111111` | write, finalize | décodé | +| BPF Loader upgradeable | `BPFLoaderUpgradeab1e11111111111111111111111` | initialize buffer, write, deploy with max data length, upgrade, set authority, close, extend program, set authority checked | décodé | +| Loader v4 | `LoaderV411111111111111111111111111111111111` | write, copy, set program length, deploy, retract, transfer authority, finalize | décodé | + +Les payloads `write` ne recopient pas le bytecode dans l’événement : ils conservent offset, longueur et SHA-256. Les tailles de vectors, booléens, octets suffixes et paires de comptes optionnelles sont validés avant toute lecture. Les anciennes formes upgradeable sans le booléen suffixe sont acceptées avec la valeur par défaut officielle et marquées comme telles. + +La projection lifecycle produit `program_loader:::0` pour les mutations réussies et commitées : finalize immuable, initialize/deploy/upgrade/close/extend upgradeable, puis set length/deploy/retract/finalize v4. Les écritures, copies et changements d’autorité restent des événements decode de familles Audit ou Admin. + +## Slashing Program stateless + +Program ID : `S1ashing11111111111111111111111111111111111`. + +Agave `v4.1.1` le déclare comme stateless builtin derrière `enshrine_slashing_program`, avec un hash de build vérifié correspondant à la release officielle `solana-program/slashing@fe8da3a`. + +| Tag `u8` | Entry code | Taille exacte | Effet runtime observé | +|---------:|--------------------------|--------------:|------------------------------------------------------------------------| +| 0 | `close_violation_report` | 1 | ferme le rapport après au moins trois epochs et transfère ses lamports | +| 1 | `duplicate_block_proof` | 305 | vérifie une preuve externe puis stocke un rapport de violation | + +La preuve duplicate block est lue depuis un compte externe à l’offset `u64` de l’instruction. Le replay transactionnel ne possède pas le contenu historique de ce compte. Le décodeur conserve l’offset, le slot, les clés, racines et signatures intégrées à l’instruction, résume l’instruction Ed25519 précédente et ne prétend ni relire le compte ni recalculer les signatures. + +Une transaction réussie prouve seulement l’acceptation runtime, l’assignation/allocation du PDA préfinancé et l’écriture du rapport. Le programme ne brûle ni ne retire lui-même du stake : la projection `slashing_violation_report` conserve donc `penaltyAppliedByProgram = false` et décrit un signal destiné à une application de pénalité externe au programme. + +## Précompiles de signature `pre.014` + +| Code canonique | Program ID | Format | État `pre.014` | +|----------------|-----------------------------------------------|---------------------------------------------------------------------------|-----------------------------------------------------------------------------| +| `ed25519` | `Ed25519SigVerify111111111111111111111111111` | header 2 octets, offsets 14 octets, clé 32, signature 64 | décodé, zéro signature accepté seulement sur 2 octets | +| `secp256k1` | `KeccakSecp256k11111111111111111111111111111` | header 1 octet, offsets 11 octets, adresse 20, signature 64 + recovery ID | décodé, index `u8` explicites, zéro signature accepté seulement sur 1 octet | +| `secp256r1` | `Secp256r1SigVerify1111111111111111111111111` | header 2 octets, offsets 14 octets, clé compressée 33, signature 64 | décodé, zéro signature refusé, maximum 8 | + +Le contrat core `2` fournit les payloads de toutes les instructions outer. Ed25519 et secp256r1 interprètent `u16::MAX` comme la cible ; secp256k1 résout toujours un index explicite. Les références aux inner instructions ne sont pas autorisées. Les offsets et longueurs utilisent des additions vérifiées avant chaque slice. + +Chaque observation conserve les index bruts et résolus, la provenance, les longueurs, SHA-256 et préfixes bornés des composants, ainsi que `runtimeVerification`. Aucun message complet n’est recopié, aucune signature n’est recalculée et aucune sortie métier n’est matérialisée. Une transaction échouée reste `decoded` si le layout est valide, mais porte `not_asserted_transaction_failed` et reste non commitée. + +## Surfaces historiques ou complémentaires + +| Code canonique | Program ID | État après `pre.008` | +|------------------|-----------------------------------------------|-----------------------------------------------------------------------------------------------------| +| `feature` | `Feature111111111111111111111111111111111111` | `revoke_pending_activation` décodé et lifecycle matérialisable | +| `zk_token_proof` | `ZkTokenProof1111111111111111111111111111111` | 17 layouts historiques décodés + fallback `current_runtime_noop_invocation`; validation synthétique | + + + +### Ancien ZK Token Proof Program + +`pre.016` couvre les discriminants `0..16` de l’interface historique : fermeture du contexte, zero balance, withdraw, égalités de ciphertext/commitment, transfer, transfer with fee, validité de pubkey, range proofs simples et batchés, validité de grouped ciphertext à deux ou trois handles et fee sigma. Les tailles de preuve et de contexte sont maintenant conservées dans une table locale auditée contre les types `Pod` de `solana-zk-token-sdk 3.1.14`, sans dépendre de cette crate dépréciée. + +Le runtime historique Agave `v2.0.0` distinguait une preuve inline d’une preuve stockée dans le premier compte lorsque l’instruction faisait exactement cinq octets. Il imposait des arités distinctes pour le contexte, refusait les vérifications comme inner instructions et plaçait certains chemins derrière `enable_zk_proof_from_account` ou `enable_zk_transfer_with_fee`. Le runtime Agave `v4.1.1` est au contraire un stub qui retourne succès sans mutation. Le décodeur conserve ces deux références sans prétendre connaître la version runtime d’une transaction donnée. + +Aucune signature mainnet n’a été trouvée pour ce program ID lors du jalon. La surface est donc validée par fixtures synthétiques et sources officielles, sans critère de backfill. Les preuves inline sont seulement mesurées, hashées et préfixées de façon bornée ; les preuves externes conservent l’offset et la taille attendue. Aucune preuve n’est recalculée et aucune sortie n’est matérialisée. + +### Stake Program + +`pre.013` couvre les dix-huit variantes de `StakeInstruction`, tags `u32 LE` `0..17` : initialize, authorize, delegate, split, withdraw, deactivate, set lockup, merge, autorisations avec seed, variantes checked, minimum delegation, deactivate delinquent, `redelegate`, move stake et move lamports. `Redelegate` reste déclaré historique : le processor actuel renvoie `InvalidInstructionData` avant tout traitement de comptes ; une occurrence réelle doit donc provenir d’une transaction échouée et reste non commitée. + +L’enum officielle de `solana-stake-interface ^4.3` conserve le contrat Serde/Bincode historique, mais n’implémente pas directement `SchemaRead`/`SchemaWrite`. Pour respecter l’interdiction workspace de dépendre de `bincode`, le décodeur utilise un miroir wire privé, ordonné exactement comme l’enum officielle, décodé par `wincode::deserialize_exact`. Les tests vérifient manuellement les tags, pubkeys, entiers, enums imbriquées et longueurs de chaînes du layout historique. Les types sémantiques et le program ID officiels restent consommés depuis l’interface. + +Le processor BPF actuel supporte deux familles de comptes. Les layouts historiques sont détectés par la première sysvar héritée, généralement Clock ou Rent ; les layouts modernes retirent ces sysvars et placent directement les autorités aux positions attendues. `Split`, `SetLockup` et `SetLockupChecked` conservent volontairement les contraintes historiques plus souples du runtime. Le décodeur expose `accountLayout`, conserve les comptes additionnels et les privilèges attendus/observés, mais ne prétend pas rejouer les vérifications dépendant de l’état du stake account, du lockup, de la délégation ou des sysvars courantes. Aucune matérialisation Stake n’est ajoutée dans ce delta. + +### Compte Stake Config historique + +`StakeConfig11111111111111111111111111111111` n’est pas un programme exécutable. L’interface Stake officielle le déclare comme identifiant déprécié du compte de configuration historique du Stake Program. Il reste dans `kb_program_ids` comme `STAKE_CONFIG_ACCOUNT_ID` et dans le registre core avec `identifier_kind = well_known_account`, mais il est exclu des surfaces du décodeur et de l’exécuteur. + +### Config Program + +Le Config Program ne possède pas un enum d’instructions discriminé comparable à System ou Stake. Chaque invocation stocke un préfixe `ConfigKeys` sérialisé avec `solana_short_vec`, suivi d’un payload spécifique au type de configuration. Le décodeur valide la longueur compacte, les clés, les booléens signataires et l’ordre des comptes signataires, puis conserve le reste par taille, préfixe borné et SHA-256 avec la sémantique explicite `opaque_program_specific`. + +### Vote Program + +`pre.010` utilise `solana-vote-interface ^6.0` avec les features `serde` et `wincode`. Le discriminant wire historique reste un `u32` little-endian compris entre `0` et `19`, et `wincode::deserialize_exact` rejette les octets résiduels. `VoteInitV2` contient les autorités, la clé BLS, sa preuve de possession et les deux commissions en points de base ; les collecteurs Inflation Rewards et Block Revenue sont les comptes d’instruction 2 et 3. + +La couverture comprend : initialisations V1/V2, autorisations simples/checked/seed, vote et vote switch, mises à jour d’état normales/compactes, tower sync, retrait, identité, commissions historiques et en points de base, collecteurs différenciés et dépôt de récompenses délégateurs. Les tableaux BLS V2 sont conservés en hexadécimal exact. Les sysvars Rent, Clock et Slot Hashes sont vérifiées aux positions imposées par l’interface. Les privilèges attendus et observés restent distincts afin de ne pas confondre les signataires statiques et les signatures PDA de CPI. + +Aucune projection matérialisée spécifique à Vote n’est ajoutée dans ce delta : la mutation réelle dépend de l’état du vote account et du runtime. Une transaction échouée produit uniquement une observation non commitée. + +## ZK ElGamal Proof `pre.015` + +Program ID : `ZkE1Gama1Proof11111111111111111111111111111`. + +Le décodeur consomme `solana-zk-elgamal-proof-interface ^0.1` pour l’enum `ProofInstruction` et les tailles exactes des types `Pod`. Il couvre `CloseContextState` et les douze preuves : zero ciphertext, égalités ciphertext/ciphertext et ciphertext/commitment, validité de clé publique, percentage with cap, range proofs 64/128/256 bits et validités grouped/batched à deux ou trois handles. + +Deux modes de transport sont distingués conformément au runtime : + +- instruction de cinq octets : tag puis offset `u32 LE` vers un compte contenant la preuve ; +- autre taille exacte : preuve inline immédiatement après le tag, avec taille égale au type `ProofData` officiel. + +Pour une preuve inline, les blocs contexte et preuve sont séparés par leurs tailles `Pod`, puis conservés uniquement sous forme de longueur, SHA-256 et préfixe hexadécimal borné. Le décodeur ne recalcule aucune preuve cryptographique. Pour une transaction réussie, il indique seulement que l’instruction a été acceptée par le runtime. Pour une transaction échouée, aucune validité n’est affirmée et l’observation reste non commitée. + +Le mode référencé par compte a une limite historique structurelle : le graphe core de transaction ne contient pas le contenu du compte à l’instant d’exécution. L’événement conserve donc le compte source, l’offset, le type et la taille attendue, mais marque les octets de preuve comme indisponibles. Il ne tente ni lecture RPC actuelle, qui pourrait être divergente, ni reconstruction. + +Les comptes optionnels de contexte sont résolus selon le mode : positions 0/1 pour une preuve inline et 1/2 après le compte de preuve pour une preuve externe. La fermeture du contexte conserve le contexte, la destination des lamports et l’autorité signataire, accepte les octets suffixes comme le runtime et refuse que contexte et destination désignent le même compte. + +`pre.017` matérialise le lifecycle du compte de contexte sous `zk_proof_context::0`. Une vérification n’est éligible que lorsque `contextStateRequested = true`, que la transaction a réussi et que l’observation est commitée. La sortie conserve le compte, l’autorité et le type de preuve, mais jamais la preuve elle-même. La fermeture réussie décrit la récupération des lamports, la remise à zéro des données et le retour de l’owner au System Program. Les preuves sans contexte restent en decode/audit. + +## Statuts et politique d’échec + +- `decoded` signifie que le format complet a été validé et qu’une observation structurée a été produite. +- `ignored` est réservé à une entrée comprise mais sans événement utile, comme le tag Compute Budget `Unused`. +- `unsupported` conserve une variante inconnue ou une surface non encore implémentée sans faux événement. +- `failed` indique un payload absent, invalide, tronqué, non canonique ou des comptes incohérents. +- `unmatched` reste un défaut de dispatch et ne doit pas apparaître pour une sélection bornée aux surfaces déclarées. + +Une transaction on-chain échouée reste décodable comme intention. L’observation porte `transactionSucceeded = false` et `observation_committed = false`. Le matérialiseur `solana_native_lifecycle` est actif pour ALT, les mutations loader listées, Feature Gate, les durable nonce accounts System et les comptes de contexte ZK ElGamal. Il accepte les familles `Lifecycle`, `Admin` et `Audit` uniquement au niveau exact surface/entrée/paramètres, puis applique `SuccessfulCommittedOnly`. Une observation non commitée est refusée et une preuve ZK sans contexte est exclue avant ledger et politique. + +## Validation mainnet `pre.005` + +Les correctifs de `pre.005` ont été validés par les tests ciblés, `cargo clippy --all-targets` et deux campagnes Tauri. Le force replay ciblé a décodé 3/3 inputs sans échec. Le replay global a sélectionné, démarré et terminé 476 inputs : 476 décodés, zéro `failed`, `unsupported`, `unmatched` et zéro faux `materializationRefused`. Aucun output n’était attendu, car le corpus ne contenait pas d’opération ALT éligible. + +## Validation `pre.006`, `pre.008`, Vote mainnet et anomalie Compute Budget + +Les tests `pre.006`, Clippy et le replay Tauri global ont été validés : 476 inputs sélectionnés, 476 décodés, aucun échec, `unsupported`, `unmatched` ou refus de matérialisation. Les validations utilisateur de `pre.008` couvrent 256 tests ciblés, PostgreSQL réel, Clippy et un démarrage/replay Tauri propre. Le corpus initial ne contenait que System et Compute Budget : loaders, Config et Feature restent en attente d’un corpus réel. + +Le backfill Vote de 500 signatures a inséré 500 transactions et l’extraction core a terminé 500/500 sans échec. Le replay global suivant contient 500 instructions Vote réelles, toutes reconnues et décodées comme `tower_sync`, sans échec Vote. L’unique `failed` de la campagne provient d’un `set_compute_unit_limit` de 12 octets : le runtime l’accepte grâce à la désérialisation Borsh unchecked, alors que le décodeur strict exigeait encore 5 octets exacts. `pre.011` corrige cet écart. Le processor reste `solana_native_classifier@0.4.1` : un force replay est requis pour remplacer le résultat `failed` déjà enregistré. diff --git a/docs/NOMENCLATURE.md b/docs/NOMENCLATURE.md new file mode 100644 index 0000000..b045003 --- /dev/null +++ b/docs/NOMENCLATURE.md @@ -0,0 +1,106 @@ + + + +# Nomenclature + +Ce document fixe les noms internes utilisés dans le workspace. + +## Programmes + +- `program_id` : adresse Solana réelle. +- `program_code` : nom interne stable du programme. +- `protocol_code` : famille protocolaire. +- `surface_code` : surface concrète. + +## Événements + +Le format canonique est : + +```text +. +``` + +Exemples : + +```text +pump_swap.buy +raydium_amm_v4.swap_base_in +meteora_dlmm.swap +jupiter_v6.route +``` + +## Tables Solana PostgreSQL + +Les tables Solana PostgreSQL utilisent le schéma courant du profil PostgreSQL, généralement `public`. Le projet ne crée pas de schémas applicatifs `raw`, `core`, `obs`, `decode`, `mat`, `catalog`, `agg`, `ops` ou `wallet`. + +Le format canonique est : + +```text +kb_sol__ +``` + +Domaines autorisés au départ : + +```text +raw +core +obs +decode +mat +catalog +agg +ops +wallet +``` + +Exemples valides : + +```text +kb_sol_raw_transactions +kb_sol_core_transactions +kb_sol_obs_transaction_observations +kb_sol_obs_program_observations +kb_sol_decode_decoded_events +kb_sol_mat_trade_events +kb_sol_catalog_tokens +kb_sol_ops_processing_ledger +``` + +Exemples interdits, écrits avec `DOT` pour que les audits textuels simples ne confondent pas documentation et usage réel : + +```text +raw DOT kb_sol_rpc_transactions +core DOT kb_sol_transactions +obs DOT kb_sol_program_observations +decode DOT kb_sol_decoded_events +mat DOT kb_sol_trade_events +catalog DOT kb_sol_tokens +ops DOT kb_sol_processing_ledger +``` + +## Surfaces de programmes + +Le nom canonique d'une surface de programme doit suivre le format : + +```text +__[_vN] +``` + +Le nom de crate correspondant doit suivre le format : + +```text +kb_decoder___[_vN] +``` + +Les anciens noms issus des IDL, des explorateurs ou des anciennes matrices doivent être conservés dans le registre sous `source_code` ou `normalized_code`, mais ne doivent pas être utilisés comme nouvelle référence stable si un nom canonique existe. + +Voir aussi : + +- `docs/DATABASE.md` ; +- `docs/PROGRAM_NAMING.md` ; +- `docs/PROGRAM_REGISTRY_CONTROL.md` ; +- `registry/program_registry_seed.toml`. + +Le préfixe `program_` est interdit pour les surfaces canoniques. Les entrées dont la fonction est inconnue doivent rester en `unknown_*` avec un statut de classification, sans création de crate cible. + +Les programmes core Solana/SPL sont documentés séparément des DEX et routers, même s'ils restent dans le registre machine. diff --git a/docs/POSTGRES_STORE.md b/docs/POSTGRES_STORE.md new file mode 100644 index 0000000..356c9e2 --- /dev/null +++ b/docs/POSTGRES_STORE.md @@ -0,0 +1,91 @@ + + + +# Store PostgreSQL canonique + +## Baseline active + +PostgreSQL reste le backend principal. Le store utilise le schéma courant du profil, généralement `public`, et ne qualifie pas les tables par un schéma applicatif explicite. + +La baseline active contient : + +```text +kb_sol_raw_transactions +kb_sol_obs_transaction_observations +kb_sol_core_transactions +kb_sol_core_account_keys +kb_sol_core_instructions +kb_sol_core_inner_instructions +kb_sol_core_logs +kb_sol_core_balance_changes +kb_sol_ops_processing_ledger +kb_sol_decode_events +kb_sol_decode_coverage_declarations +kb_sol_decode_coverage_observations +kb_sol_mat_events +``` + +## Initialisation + +Le projet utilise encore des DDL idempotents gérés par `kb_store_pg`. `_sqlx_migrations` n’est pas requis pour démarrer l’application. + +Les fichiers SQL actifs sont conservés comme représentation lisible de la baseline : + +```text +0001_canonical_transaction_store.sql +0002_core_store.sql +0003_processing_ledger.sql +0004_decode_materialization_store.sql +``` + +Au démarrage Tauri, `initialize_store_schema()` applique une seule fois, dans l’ordre, les DDL raw, core puis decode/mat. Les connexions ouvertes ensuite par les commandes de démo désactivent `auto_initialize_schema` et ne relancent plus les migrations. Les méthodes spécialisées restent disponibles pour les tests et outils qui demandent explicitement l’initialisation d’un sous-ensemble dépendant. + +## Extraction canonique vers core + +`CoreExtractionStore` sélectionne les lignes raw, vérifie le ledger puis persiste un `CoreExtractionBundle` dans une transaction PostgreSQL unique. + +Le mode normal skippe la combinaison déjà réussie : + +```text +processor version + signature + canonical hash +``` + +Le replay forcé remplace uniquement le graphe de la signature ciblée. Les autres signatures ne sont pas modifiées. + +## Tests PostgreSQL + +Les tests qui utilisent `KB_POSTGRES_TEST_URL` doivent être lancés sur une base dédiée ou suivis d’un nettoyage explicite. + +Depuis `0.4.1-pre.021`, les tests optionnels qui partagent une base PostgreSQL réelle acquièrent un verrou process-local test-only. Ce verrou évite les interblocages intermittents entre campagnes de tests parallèles sans changer le code runtime ni le schéma SQL. + +Les tests optionnels couvrent notamment le rollback atomique core/decode, la persistance réelle du ledger et la sémantique exacte des déclarations de couverture. `optional_postgres_core_extraction_rolls_back_partial_graph_from_env` : + +1. insère une ligne raw isolée ; +2. tente de persister un bundle contenant deux account keys avec le même index ; +3. attend une violation de l’index unique après le début de la transaction ; +4. vérifie qu’aucune transaction core, account key ou entrée ledger n’a été conservée et que le raw reste `received` ; +5. rejoue un bundle corrigé ; +6. vérifie le passage à `core_extracted` et le ledger `succeeded` ; +7. nettoie ses lignes de test. + +Commande de clôture : + +```bash +KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \ + cargo test -p kb_store_pg -- --nocapture +``` + +Les contrôles réels déjà effectués sur la base principale ont confirmé : + +- 70 transactions raw et core ; +- 70 entrées ledger `succeeded` ; +- 84 tentatives après un force replay de 14 signatures ; +- aucune duplication ni ligne fille orpheline ; +- cardinalité core inchangée après remplacement forcé. + + +## Couverture et skip decode + +`persist_decode_coverage_declarations()` synchronise le snapshot déclaré d’un processor/version et retourne des compteurs exacts : insertion réelle, modification/suppression du snapshot ou déclaration inchangée. + +Le test PostgreSQL `optional_postgres_same_version_and_hash_is_current_from_env` persiste un résultat `unsupported` puis vérifie que le même stage, processor, version, input key et input hash est reconnu comme courant par le ledger. diff --git a/docs/PROGRAM_CODE_INDEX.md b/docs/PROGRAM_CODE_INDEX.md new file mode 100644 index 0000000..ebf88f7 --- /dev/null +++ b/docs/PROGRAM_CODE_INDEX.md @@ -0,0 +1,42 @@ + + + +# Index court des programmes + +L'idée d'un préfixe hexadécimal `00` à `ff` peut être utile comme identifiant court interne, mais elle ne doit pas remplacer le nom canonique lisible. + +## Décision + +Le nom de crate reste descriptif : + +```text +kb_decoder_amm_pump_swap +kb_decoder_router_jupiter_aggregator_v6 +kb_decoder_clmm_orca_whirlpool +``` + +Un code court optionnel peut être ajouté dans le registre : + +```toml +registry_code = "00" +canonical_surface_code = "amm_pump_swap" +target_crate = "kb_decoder_amm_pump_swap" +``` + +## Pourquoi ne pas mettre `00_` dans les crates + +- Le code `00` ne décrit pas la fonction du programme. +- L'ordre peut changer quand de nouvelles surfaces sont ajoutées. +- Deux cent cinquante-six valeurs peuvent devenir insuffisantes si toutes les surfaces, sysvars, routers, vaults, perps, bridges et variantes historiques sont incluses. +- Les renommages de crates deviendraient fréquents et inutiles. + +## Usage acceptable + +Le code court est acceptable pour : + +- affichage UI compact ; +- clé stable optionnelle dans PostgreSQL ; +- tri manuel dans les matrices ; +- identifiant de configuration humain court. + +Le code court ne doit pas servir de préfixe principal de nommage Rust. diff --git a/docs/PROGRAM_IDS.md b/docs/PROGRAM_IDS.md new file mode 100644 index 0000000..03828c4 --- /dev/null +++ b/docs/PROGRAM_IDS.md @@ -0,0 +1,15 @@ + + + +# Identifiants de programmes + +`kb_program_ids` est la source unique des `program_id` connus du workspace. + +Les décodeurs et exécuteurs ne doivent pas redéfinir les mêmes chaînes dans leurs propres fichiers `constants.rs`. Ils doivent utiliser directement `kb_program_ids::XXX_PROGRAM_ID`. + +Les fichiers `constants.rs` locaux restent utiles pour les constantes propres à une surface : discriminators, opcodes, seeds, index de comptes, tailles de payload ou constantes Borsh. + +## Audit de génération + +Le crate central a été alimenté à partir des constantes existantes des décodeurs/exécuteurs et des fichiers registry. + diff --git a/docs/PROGRAM_ID_CONSTANTS_AUDIT.md b/docs/PROGRAM_ID_CONSTANTS_AUDIT.md new file mode 100644 index 0000000..32df785 --- /dev/null +++ b/docs/PROGRAM_ID_CONSTANTS_AUDIT.md @@ -0,0 +1,18 @@ + + + +# Audit des constantes `PROGRAM_ID` des décodeurs + +Ce document décrit la correction appliquée aux crates de décodeurs classifiés. Chaque décodeur associé à un `program_id` dans `registry/program_registry_seed.toml` doit exposer ce `program_id` via `src/constants.rs`, le réexporter depuis `src/lib.rs`, puis l’utiliser dans `program_ids()` via `crate::XXX_PROGRAM_ID`. + +## Règle retenue + +- `src/constants.rs` contient les identifiants stables du programme, puis plus tard les discriminants, sélecteurs, discriminators Anchor, codes d’instruction et constantes de décodage. +- `src/lib.rs` réexporte les constantes publiques nécessaires. +- `src/decoder.rs` ne doit pas contenir de literal de `program_id` dans `program_ids()`. +- Les décodeurs génériques comme `kb_decoder_anchor` ne doivent pas inventer de `program_id`. + +## Portée + +La correction couvre les décodeurs classifiés présents dans le workspace et associés à un `program_id` non vide dans le registre. Les crates core Solana/SPL disposaient déjà de `constants.rs`. + diff --git a/docs/PROGRAM_NAMING.md b/docs/PROGRAM_NAMING.md new file mode 100644 index 0000000..ace9bad --- /dev/null +++ b/docs/PROGRAM_NAMING.md @@ -0,0 +1,152 @@ + + + +# Nommage canonique des programmes et surfaces + +Le nommage doit permettre de distinguer la fonction réelle du programme, sa famille protocolaire et sa version. + +## Format recommandé + +```text +__[_vN] +``` + +Le nom de crate correspondant est : + +```text +kb_decoder___[_vN] +``` + + +## Préfixes fonctionnels autorisés + +Le préfixe doit décrire la fonction principale du programme, pas seulement le fait qu'il s'agit d'un programme Solana. + +Exemples de préfixes acceptés : + +```text +core +token +amm +cpmm +clmm +dlmm +stable_swap +weighted_swap +orderbook +router +launchpad +lending +vault +staking +bridge +perpetuals +oracle +nft +metadata +admin +fees +governance +treasury +lock +vesting +unknown +``` + +Le préfixe `program` est interdit pour les nouveaux noms canoniques. Une entrée non classifiée doit utiliser `unknown_*` et ne doit pas déclencher la création d'une crate cible avant classification. + +## Cas Meteora DAMM + +`damm` est conservé comme identifiant de surface Meteora, mais pas comme préfixe fonctionnel. La forme attendue est donc `amm_meteora_damm_v1` ou `amm_meteora_damm_v2`. En revanche `cpmm`, `clmm`, `dlmm`, `stable_swap` et `weighted_swap` sont des mécanismes et peuvent être utilisés comme préfixes fonctionnels. + +## Colonnes à conserver + +Le registre ne doit pas perdre les anciens noms. Chaque programme doit conserver : + +- `program_id` : adresse Solana réelle ; +- `source_code` : nom brut issu de la liste, d’un IDL ou d’une ancienne documentation ; +- `normalized_code` : nom nettoyé sans faute évidente ; +- `canonical_surface_code` : nom cible stable ; +- `current_crate` : crate existant si la surface est déjà réservée ; +- `target_crate` : nom de crate à atteindre après migration contrôlée ; +- `registry_status` : statut de contrôle. + +## Exemples de normalisation + +| Nom source | Fonction | Famille | Nom canonique | +|---------------------------|-------------|------------|------------------------------------| +| `raydium_lp_v4` | `amm` | `raydium` | `amm_raydium_lp_v4` | +| `raydium_cpmm` | `cpmm` | `raydium` | `cpmm_raydium` | +| `raydium_clmm` | `clmm` | `raydium` | `clmm_raydium` | +| `meteora_dlmm` | `dlmm` | `meteora` | `dlmm_meteora` | +| `meteora_damm_v2` | `amm` | `meteora` | `amm_meteora_damm_v2` | +| `jupiter_agregator_v6` | `router` | `jupiter` | `router_jupiter_aggregator_v6` | +| `okx_labs_v2` | `router` | `okx` | `router_okx_labs_v2` | +| `openbook_v2` | `orderbook` | `openbook` | `orderbook_openbook_v2` | +| `phoenix` | `orderbook` | `phoenix` | `orderbook_phoenix_v1` | +| `pump_swap` | `amm` | `pump` | `amm_pump_swap` | +| `pump_fun` | `launchpad` | `pump` | `launchpad_pump_fun` | +| `pump_fees` | `admin` | `pump` | `admin_pump_fees` | +| `orca_whirlpool` | `clmm` | `orca` | `clmm_orca_whirlpool` | +| `metaplex_token_metadata` | `metadata` | `metaplex` | `metadata_metaplex_token_metadata` | +| `bubblegum` | `nft` | `metaplex` | `nft_metaplex_bubblegum` | + + +## Exceptions conservées + +Les programmes Solana/SPL de base restent groupés dans des crates techniques déjà lisibles : + +- `kb_decoder_solana_core` ; +- `kb_decoder_spl_token` ; +- `kb_decoder_spl_token_2022` ; +- `kb_decoder_spl_associated_token_account`. + +Ces crates ne sont pas renommées en `kb_decoder_core_*` ou `kb_decoder_token_*`, car elles servent de couche primitive avant les surfaces DEX/router. + +## Politique de migration + +La migration physique des dossiers ne doit pas être faite en masse. Pour chaque rename : + +1. prouver que le `program_id` correspond à la surface ; +2. créer ou renommer une seule crate ; +3. mettre à jour `Cargo.toml` ; +4. mettre à jour le registre ; +5. exécuter `cargo build` ; +6. supprimer l’ancien alias seulement après validation. + + + +## Préfixe hexadécimal + +Les préfixes `00` à `ff` sont interdits comme préfixes principaux de crate. Ils peuvent exister uniquement comme champ de registre optionnel `registry_code`. Le nom canonique doit rester lisible et fonctionnel. + +Exemple accepté : + +```toml +registry_code = "01" +canonical_surface_code = "amm_pump_swap" +target_crate = "kb_decoder_amm_pump_swap" +``` + +Exemple refusé : + +```text +kb_decoder_01_pump_swap +``` + +## Fichiers `constants.rs` + +Les crates peuvent avoir un fichier `src/constants.rs` pour regrouper les `program_id`, discriminants, sélecteurs, longueurs Borsh et autres constantes techniques. Les `program_id` publics sont réexportés dans `lib.rs`. Les constantes internes futures, par exemple des discriminants ou tailles de payload, doivent rester `pub(crate)` sauf besoin explicite d'API publique. + +Depuis un module interne de la même crate, une constante réexportée par `lib.rs` doit être appelée via `crate::CONSTANT_NAME`. Le chemin `crate::constants::CONSTANT_NAME` reste réservé aux constantes non réexportées. + +## Préfixes ajoutés après inspection IDL + +- `adapter` : wrapper technique ou adaptation de format, par exemple `adapter_saber_decimal_wrapper`. +- `rwa` : programme d'actifs réels tokenisés ou marchés globaux, par exemple `rwa_ondo_global_markets`. +- `vesting` : programme de vesting, streaming ou timelock. +- `wallet` : smart wallet applicatif ou surface d'approbation/exécution. + +## Préfixe `fees` + +Le préfixe `fees` est autorisé pour les programmes dont le rôle principal est la configuration, distribution ou réclamation de frais. Il doit être préféré à `admin` lorsque la surface est explicitement centrée sur le partage de frais. diff --git a/docs/PROGRAM_REGISTRY_CONTROL.md b/docs/PROGRAM_REGISTRY_CONTROL.md new file mode 100644 index 0000000..5afb9ab --- /dev/null +++ b/docs/PROGRAM_REGISTRY_CONTROL.md @@ -0,0 +1,328 @@ + + + +# Contrôle du registre des programmes + +Ce document transforme la liste de programmes fournie en registre contrôlable. Il ne valide pas encore chaque `program_id` sur chaîne ; il sert de base interne pour détecter les doublons, les aliases, les surfaces mal nommées et les crates manquantes. + +## Résumé + +- Entrées du registre : 150. +- `program_id` distincts : 149. +- Doublons exacts de `program_id` : 1. +- Registre machine : `registry/program_registry_seed.toml`. +- La colonne `Canonique` est la référence de tri et de migration. + +## Correction de principe + +La table de contrôle est maintenant triée par `Canonique`, avec `Canonique` en première colonne. C'est le nom qui doit piloter les futures crates, les noms de surfaces et les tables de couverture. + +Les programmes Solana/SPL de base ne sont plus mélangés avec les DEX, routers et protocoles applicatifs. Ils sont listés dans un tableau séparé parce qu'ils relèvent de la couche core : `kb_decoder_solana_core`, `kb_decoder_spl_token`, `kb_decoder_spl_token_2022` et futurs décodeurs primitifs. + +## Pourquoi `amm` et pas `damm` en préfixe ? + +Le préfixe doit représenter la fonction générique du programme. `amm` signifie Automated Market Maker. `damm` est un nom de surface Meteora, généralement interprété comme Dynamic AMM. Donc `damm` ne doit pas être utilisé comme préfixe fonctionnel général. + +La forme recommandée est : + +```text +amm_meteora_damm_v1 +amm_meteora_damm_v2 +``` + +et non : + +```text +damm_meteora_v1 +damm_meteora_v2 +``` + +À l'inverse, `dlmm`, `clmm`, `cpmm`, `stable_swap` et `weighted_swap` sont des types de mécanismes et peuvent être utilisés comme préfixes fonctionnels quand ils décrivent mieux la surface. + +## Pourquoi supprimer `program_` ? + +Le préfixe `program_` était un fallback technique. Il n'indique aucune fonction : ni AMM, ni router, ni lending, ni bridge, ni NFT. Il est donc insuffisant pour une nomenclature stable. + +Nouvelle règle : + +- aucune nouvelle surface canonique ne doit commencer par `program_` ; +- une surface non classifiée utilise temporairement `unknown_*` ; +- une surface `unknown_*` ne doit pas devenir une crate cible tant que sa fonction réelle n'est pas décidée ; +- le statut attendu est `needs_classification` ou `current_alias_needs_classification`. + +## Programmes core Solana/SPL + +Ces identifiants sont détaillés dans `docs/CORE_PROGRAM_IDS.md`. Ils sont couverts par les décodeurs core/primitifs ou par des crates spécialisées SPL futures, pas par une crate DEX dédiée. + +| Canonique | Program ID | Source | Type | Crate cible | Statut | +|--------------------------------------------|------------------------------------------------|-----------------------------------|----------------------|-------------------------------------------|-------------------------------------| +| `core_solana_address_lookup_table_v1` | `AddressLookupTab1e1111111111111111111111111` | `address_lookup_table` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_bpf_loader_deprecated_v1` | `BPFLoader1111111111111111111111111111111111` | `bpf_loader_deprecated` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_bpf_loader_v2` | `BPFLoader2111111111111111111111111111111111` | `bpf_loader` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_bpf_loader_upgradeable_v1` | `BPFLoaderUpgradeab1e11111111111111111111111` | `bpf_loader_upgradeable` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_compute_budget_v1` | `ComputeBudget111111111111111111111111111111` | `compute_budget` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_config_v1` | `Config1111111111111111111111111111111111111` | `config` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_ed25519_v1` | `Ed25519SigVerify111111111111111111111111111` | `ed25519` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_feature_v1` | `Feature111111111111111111111111111111111111` | `feature` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_incinerator_v1` | `1nc1nerator11111111111111111111111111111111` | `incinerator` | `well_known_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_loader_v4` | `LoaderV411111111111111111111111111111111111` | `loader_v4` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_native_loader_v1` | `NativeLoader1111111111111111111111111111111` | `native_loader` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_secp256k1_v1` | `KeccakSecp256k11111111111111111111111111111` | `secp256k1` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_secp256r1_v1` | `Secp256r1SigVerify1111111111111111111111111` | `secp256r1` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_stake_config_v1` | `StakeConfig11111111111111111111111111111111` | `stake_config` | `well_known_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_stake_v1` | `Stake11111111111111111111111111111111111111` | `stake` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_system_v1` | `11111111111111111111111111111111` | `system` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_sysvar_clock_v1` | `SysvarC1ock11111111111111111111111111111111` | `sysvar_clock` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_epoch_rewards_v1` | `SysvarEpochRewards1111111111111111111111111` | `sysvar_epoch_rewards` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_epoch_schedule_v1` | `SysvarEpochSchedu1e111111111111111111111111` | `sysvar_epoch_schedule` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_fees_v1` | `SysvarFees111111111111111111111111111111111` | `sysvar_fees` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_instructions_v1` | `Sysvar1nstructions1111111111111111111111111` | `sysvar_instructions` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_last_restart_slot_v1` | `SysvarLastRestartS1ot1111111111111111111111` | `sysvar_last_restart_slot` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_recent_blockhashes_v1` | `SysvarRecentB1ockHashes11111111111111111111` | `sysvar_recent_blockhashes` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_rent_v1` | `SysvarRent111111111111111111111111111111111` | `sysvar_rent` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_rewards_v1` | `SysvarRewards111111111111111111111111111111` | `sysvar_rewards` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_slot_hashes_v1` | `SysvarS1otHashes111111111111111111111111111` | `sysvar_slot_hashes` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_slot_history_v1` | `SysvarS1otHistory11111111111111111111111111` | `sysvar_slot_history` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_stake_history_v1` | `SysvarStakeHistory1111111111111111111111111` | `sysvar_stake_history` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_v1` | `Sysvar1111111111111111111111111111111111111` | `sysvar` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_vote_v1` | `Vote111111111111111111111111111111111111111` | `vote` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_zk_elgamal_proof_v1` | `ZkE1Gama1Proof11111111111111111111111111111` | `zk_elgamal_proof` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_zk_token_proof_v1` | `ZkTokenProof1111111111111111111111111111111` | `zk_token_proof` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_spl_associated_token_account_v1` | `ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL` | `associated_token_account` | `program` | `kb_decoder_spl_associated_token_account` | `core_handled` | +| `core_spl_account_compression_v1` | `cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK` | `spl_account_compression` | `program` | `kb_decoder_spl_account_compression` | `core_specialized_reserved_current` | +| `core_spl_memo_v1` | `Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo` | `spl_memo_v1` | `program` | `kb_decoder_spl_memo` | `core_specialized_reserved_current` | +| `core_spl_memo_v3` | `MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr` | `spl_memo_v3` | `program` | `kb_decoder_spl_memo` | `core_specialized_reserved_current` | +| `core_spl_memo_v4` | `Memo4c2pN8afCj432Lb7RMVKi9PbQnnW7ewFFaV3oAH` | `spl_memo_v4` | `program` | `kb_decoder_spl_memo` | `core_specialized_reserved_current` | +| `core_spl_noop_v1` | `noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV` | `spl_noop` | `program` | `kb_decoder_spl_noop` | `core_specialized_reserved_current` | +| `core_spl_single_pool_v1` | `SVSPxpvHdN29nkVg9rPapPNDddN5DipNLRUFhyjFThE` | `spl_single_pool` | `program` | `kb_decoder_spl_single_pool` | `core_specialized_reserved_current` | +| `core_spl_name_service_v1` | `namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX` | `spl_name_service` | `program` | `kb_decoder_metadata_spl_name_service` | `core_specialized_reserved_current` | +| `core_spl_stake_pool_v1` | `SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy` | `stake_pool` | `program` | `kb_decoder_spl_stake_pool` | `core_specialized_reserved_current` | +| `core_spl_token_2022_v2022` | `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` | `token_2022` | `program` | `kb_decoder_spl_token_2022` | `core_handled` | +| `core_spl_token_2022_elgamal_registry_v1` | `regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg` | `spl_token_2022_elgamal_registry` | `program` | `kb_decoder_spl_token_2022` | `core_specialized_reserved_current` | +| `core_spl_token_v1` | `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA` | `token` | `program` | `kb_decoder_spl_token` | `core_handled` | + + +## DEX, AMM, CPMM, CLMM, DLMM, stable swap, weighted swap et orderbooks + +| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut | +|------------------------------------|------------------------------------------------|--------------------------|--------------------------|-------------------------------------|-----------------------------------------------|------------------------| +| `amm_aldrin_v1` | `AMM55ShdkoGRB5jVYPjWziwk8m5MpwyDgsMWHaMSQWH6` | `aldrin_amm_v1` | `aldrin_amm_v1` | `-` | `kb_decoder_amm_aldrin_v1` | `reserved_missing` | +| `amm_aldrin_v2` | `CURVGoZn8zycx6FXwwevgBTB2gVvdbGTEpvMJDbgs2t4` | `aldrin_amm_v2` | `aldrin_amm_v2` | `-` | `kb_decoder_amm_aldrin_v2` | `reserved_missing` | +| `amm_alphaq` | `ALPHAQmeA7bjrVuccPsYPiCvsi428SNwte66Srvs4pHA` | `alphaq` | `alphaq` | `kb_decoder_alphaq` | `kb_decoder_amm_alphaq` | `current_alias` | +| `amm_believe` | `5qWya6UjwWnGVhdSBL3hyZ7B45jbk6Byt1hwd7ohEGXE` | `believe` | `believe` | `kb_decoder_believe` | `kb_decoder_amm_believe` | `current_alias` | +| `amm_bonk_swap` | `BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p` | `bonk_swap` | `bonk_swap` | `kb_decoder_bonkswap` | `kb_decoder_amm_bonk_swap` | `current_alias` | +| `amm_cropper_finance` | `CTMAxxk34HjKWxQ3QLZK1HpaLXmBveao3ESePXbiyfzh` | `cropper_finance` | `cropper_finance` | `-` | `kb_decoder_amm_cropper_finance` | `reserved_missing` | +| `amm_dexlab_swap` | `DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6N` | `dexlab_swap` | `dexlab_swap` | `-` | `kb_decoder_amm_dexlab_swap` | `reserved_missing` | +| `amm_fluxbeam` | `FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X` | `fluxbeam` | `fluxbeam` | `kb_decoder_fluxbeam` | `kb_decoder_amm_fluxbeam` | `current_alias` | +| `amm_fusion` | `fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9` | `fusion_amm` | `fusion_amm` | `kb_decoder_fusion_amm` | `kb_decoder_amm_fusion` | `current_alias` | +| `amm_goon_fi` | `goonERTdGsjnkZqWuVjs73BZ3Pb9qoCUdBUL17BnS5j` | `goon_fi` | `goon_fi` | `kb_decoder_goonfi` | `kb_decoder_amm_goon_fi` | `current_alias` | +| `amm_goon_fi_v2` | `goonuddtQRrWqqn5nFyczVKaie28f3kDkHWkHtURSLE` | `goon_fi_v2` | `goon_fi_v2` | `-` | `kb_decoder_amm_goon_fi_v2` | `reserved_missing` | +| `amm_goosefx_gamma` | `GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT` | `goose_fx_gamma` | `goosefx_gamma` | `kb_decoder_goosefx_gamma` | `kb_decoder_amm_goosefx_gamma` | `current_alias` | +| `amm_goosefx_v2` | `GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn` | `goose_fx_v2` | `goosefx_v2` | `kb_decoder_goosefx_v2` | `kb_decoder_amm_goosefx_v2` | `current_alias` | +| `amm_guac_swap` | `Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1` | `guac_swap` | `guac_swap` | `kb_decoder_guacswap` | `kb_decoder_amm_guac_swap` | `current_alias` | +| `amm_heaven_dex` | `HEAVENoP2qxoeuF8Dj2oT1GHEnu49U5mJYkdeC8BAX2o` | `heaven_dex` | `heaven_dex` | `-` | `kb_decoder_amm_heaven_dex` | `reserved_missing` | +| `amm_hylo_exchange` | `HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn` | `hylo_exchange` | `hylo_exchange` | `kb_decoder_hylo_exchange` | `kb_decoder_amm_hylo_exchange` | `current_alias` | +| `amm_invariant_swap` | `HyaB3W9q6XdA5xwpU4XnSZV94htfmbmqJXZcEbRaJutt` | `invariant_swap` | `invariant_swap` | `-` | `kb_decoder_amm_invariant_swap` | `reserved_missing` | +| `amm_lifinity_swap` | `EewxydAPCCVuNEyrVN68PuSYdQ7wKn27V9Gjeoi8dy3S` | `lifinity_swap` | `lifinity_swap` | `-` | `kb_decoder_amm_lifinity_swap` | `reserved_missing` | +| `amm_lifinity_swap_v2` | `2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c` | `lifinity_swap_v2` | `lifinity_swap_v2` | `-` | `kb_decoder_amm_lifinity_swap_v2` | `reserved_missing` | +| `amm_marcopolo_swap` | `9tKE7Mbmj4mxDjWatikzGAtkoWosiiZX9y6J4Hfm2R8H` | `marcopolo_swap` | `marcopolo_swap` | `-` | `kb_decoder_amm_marcopolo_swap` | `reserved_missing` | +| `amm_metadao_futarchy_amm` | `FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq` | `metadao_futarchy_amm` | `metadao_futarchy_amm` | `-` | `kb_decoder_amm_metadao_futarchy_amm` | `reserved_missing` | +| `amm_metadao_v0_5` | `AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ` | `metadao_amm_v0_5` | `metadao_amm_v0_5` | `kb_decoder_metadao_amm_v0_5` | `kb_decoder_amm_metadao_v0_5` | `current_alias` | +| `amm_meteora_damm_v1` | `Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB` | `meteora_damm_v1` | `meteora_damm_v1` | `kb_decoder_meteora_damm_v1` | `kb_decoder_amm_meteora_damm_v1` | `current_alias` | +| `amm_meteora_damm_v2` | `cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG` | `meteora_damm_v2` | `meteora_damm_v2` | `kb_decoder_meteora_damm_v2` | `kb_decoder_amm_meteora_damm_v2` | `current_alias` | +| `amm_obric_v2` | `obriQD1zbpyLz95G5n7nJe6a4DPjpFwa5XYPoNm113y` | `obric_v2` | `obric_v2` | `kb_decoder_obric_v2` | `kb_decoder_amm_obric_v2` | `current_alias` | +| `amm_one_dex` | `DEXYosS6oEGvk8uCDayvwEZz4qEyDJRf9nFgYCaqPMTm` | `one_dex` | `one_dex` | `kb_decoder_one_dex` | `kb_decoder_amm_one_dex` | `current_alias` | +| `amm_orca_token_swap` | `DjVE6JNiYqPL2QXyCUUh8rNjHrbz9hXHNYt99MQ59qw1` | `orca_token_swap` | `orca_token_swap` | `-` | `kb_decoder_amm_orca_token_swap` | `reserved_missing` | +| `amm_orca_token_swap_v2` | `9W959DqEETiGZocYWCQPaJ6sBmUzgfxXfqGeTEdp3aQP` | `orca_token_swap_v2` | `orca_token_swap_v2` | `-` | `kb_decoder_amm_orca_token_swap_v2` | `reserved_missing` | +| `amm_pancake_swap` | `HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq` | `pancake_swap` | `pancake_swap` | `kb_decoder_pancake_swap` | `kb_decoder_amm_pancake_swap` | `current_alias` | +| `amm_pump_swap` | `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA` | `pump_swap` | `pump_swap` | `kb_decoder_pump_swap` | `kb_decoder_amm_pump_swap` | `current_alias` | +| `amm_raydium_lp_amm` | `5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h` | `raydium_lp_amm` | `raydium_lp_amm` | `-` | `kb_decoder_amm_raydium_lp_amm` | `reserved_missing` | +| `amm_raydium_lp_v2` | `RVKd61ztZW9GUwhRbbLoYVRE5Xf1B2tVscKqwZqXgEr` | `raydium_lp_v2` | `raydium_lp_v2` | `-` | `kb_decoder_amm_raydium_lp_v2` | `reserved_missing` | +| `amm_raydium_lp_v3` | `27haf8L6oxUeXrHrgEgsexjSY5hbVUWEmvv9Nyxg8vQv` | `raydium_lp_v3` | `raydium_lp_v3` | `-` | `kb_decoder_amm_raydium_lp_v3` | `reserved_missing` | +| `amm_raydium_lp_v4` | `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8` | `raydium_lp_v4` | `raydium_lp_v4` | `-` | `kb_decoder_amm_raydium_lp_v4` | `reserved_missing` | +| `amm_saros` | `SSwapUtytfBdBn1b9NUGG6foMVPtcWgpRU32HToDUZr` | `saros_amm` | `saros_amm` | `-` | `kb_decoder_amm_saros` | `reserved_missing` | +| `amm_solfi` | `SoLFiHG9TfgtdUXUjWAxi3LtvYuFyDLVhBWxdMZxyCe` | `solfi` | `solfi` | `kb_decoder_solfi` | `kb_decoder_amm_solfi` | `current_alias` | +| `amm_solfi_v2` | `SV2EYYJyRz2YhfXwXnhNAevDEui5Q6yrfyo13WtupPF` | `solfi_v2` | `solfi_v2` | `kb_decoder_solfi_v2` | `kb_decoder_amm_solfi_v2` | `current_alias` | +| `amm_step_finance_swap` | `SSwpMgqNDsyV7mAgN9ady4bDVu5ySjmmXejXvy2vLt1` | `step_finance_swap` | `step_finance_swap` | `-` | `kb_decoder_amm_step_finance_swap` | `reserved_missing` | +| `amm_stepn_dooar_swap` | `Dooar9JkhdZ7J3LHN3A7YCuoGRUggXhQaG4kijfLGU2j` | `stepn_dooar_swap` | `stepn_dooar_swap` | `-` | `kb_decoder_amm_stepn_dooar_swap` | `reserved_missing` | +| `amm_swap_v1` | `SwaPpA9LAaLfeLi3a68M4DjnLqgtticKg6CnyNwgAC8` | `swap` | `swap` | `-` | `kb_decoder_amm_swap_v1` | `reserved_missing` | +| `amm_vertigo` | `vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ` | `vertigo` | `vertigo` | `kb_decoder_vertigo` | `kb_decoder_amm_vertigo` | `current_alias` | +| `amm_virtuals` | `5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki` | `virtuals` | `virtuals` | `kb_decoder_virtuals` | `kb_decoder_amm_virtuals` | `current_alias` | +| `amm_woofi` | `WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb` | `woofi` | `woofi` | `kb_decoder_woofi` | `kb_decoder_amm_woofi` | `current_alias` | +| `amm_zero_fi` | `ZERor4xhbUycZ6gb9ntrhqscUcZmAbQDjEAtCf4hbZY` | `zero_fi` | `zero_fi` | `kb_decoder_zerofi` | `kb_decoder_amm_zero_fi` | `current_alias` | +| `amm_zora` | `zoRabwLGd5zXaV7Gxacppw8tcceXEiTrSKyNLSaSTUc` | `zora` | `zora` | `kb_decoder_zora` | `kb_decoder_amm_zora` | `current_alias` | +| `clmm_byreal` | `REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2` | `byreal_clmm` | `byreal_clmm` | `kb_decoder_byreal_clmm` | `kb_decoder_clmm_byreal` | `current_alias` | +| `clmm_crema_finance` | `CLMM9tUoggJu2wagPkkqs9eFG4BWhVBZWkP1qv3Sp7tR` | `crema_finance` | `crema_finance` | `-` | `kb_decoder_clmm_crema_finance` | `reserved_missing` | +| `clmm_cropper_whirlpool` | `H8W3ctz92svYg6mkn1UtGfu2aQr2fnUFHM1RhScEtQDt` | `cropper_whirlpool` | `cropper_whirlpool` | `-` | `kb_decoder_clmm_cropper_whirlpool` | `reserved_missing` | +| `clmm_orca_whirlpool` | `whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc` | `orca_whirlpool` | `orca_whirlpool` | `kb_decoder_orca_whirlpool` | `kb_decoder_clmm_orca_whirlpool` | `duplicate_program_id` | +| `clmm_orca_whirlpool` | `whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc` | `orca_whirlpool` | `orca_whirlpool` | `kb_decoder_orca_whirlpool` | `kb_decoder_clmm_orca_whirlpool` | `duplicate_program_id` | +| `clmm_raydium` | `CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK` | `raydium_clmm` | `raydium_clmm` | `kb_decoder_raydium_clmm` | `kb_decoder_clmm_raydium` | `current_alias` | +| `clmm_stabble` | `6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6` | `stabble_clmm` | `stabble_clmm` | `kb_decoder_stabble_clmm` | `kb_decoder_clmm_stabble` | `current_alias` | +| `cpmm_raydium` | `CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C` | `raydium_cpmm` | `raydium_cpmm` | `kb_decoder_raydium_cpmm` | `kb_decoder_cpmm_raydium` | `current_alias` | +| `dlmm_meteora` | `LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo` | `meteora_dlmm` | `meteora_dlmm` | `kb_decoder_meteora_dlmm` | `kb_decoder_dlmm_meteora` | `current_alias` | +| `orderbook_jupiter_limit_order` | `jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu` | `jupiter_limit_order` | `jupiter_limit_order` | `kb_decoder_jupiter_limit_order` | `kb_decoder_orderbook_jupiter_limit_order` | `current_alias` | +| `orderbook_jupiter_limit_order_v2` | `j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X` | `jupiter_limit_order_v2` | `jupiter_limit_order_v2` | `kb_decoder_jupiter_limit_order_v2` | `kb_decoder_orderbook_jupiter_limit_order_v2` | `current_alias` | +| `orderbook_manifest_v1` | `MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms` | `manifest` | `manifest` | `-` | `kb_decoder_orderbook_manifest_v1` | `reserved_missing` | +| `orderbook_openbook_v2` | `opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb` | `openbook_v2` | `openbook_v2` | `kb_decoder_openbook_v2` | `kb_decoder_orderbook_openbook_v2` | `current_alias` | +| `orderbook_phoenix_v1` | `PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY` | `phoenix` | `phoenix` | `-` | `kb_decoder_orderbook_phoenix_v1` | `reserved_missing` | +| `stable_swap_jupiter_stable` | `JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr` | `jupiter_stable` | `jupiter_stable` | `kb_decoder_jupiter_stable` | `kb_decoder_stable_swap_jupiter_stable` | `current_alias` | +| `stable_swap_mercurial` | `MERLuDFBMmsHnsBPZw2sDQZHvXFMwp8EdjudcU2HKky` | `mercurial_stable_swap` | `mercurial_stable_swap` | `-` | `kb_decoder_stable_swap_mercurial` | `reserved_missing` | +| `stable_swap_saber` | `SSwpkEEcbUqx4vtoEByFjSkhKdCT862DNVb52nZg1UZ` | `sabre_statble_swap` | `sabre_stable_swap` | `-` | `kb_decoder_stable_swap_saber` | `reserved_missing` | +| `stable_swap_stabble` | `swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ` | `stabble_stable_swap` | `stabble_stable_swap` | `kb_decoder_stabble_stable_swap` | `kb_decoder_stable_swap_stabble` | `current_alias` | +| `weighted_swap_stabble` | `swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW` | `stabble_weighted_swap` | `stabble_weighted_swap` | `kb_decoder_stabble_weighted_swap` | `kb_decoder_weighted_swap_stabble` | `current_alias` | + + +## Routers et surfaces de routing + +| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut | +|--------------------------------|------------------------------------------------|-------------------------|-------------------------|----------------------------------|-------------------------------------------|--------------------| +| `router_dflow_aggregator_v4` | `DF1ow4tspfHX9WJsAb9epbkA8hmpSEAtxXy1V27QBH` | `dflow_agregator_v4` | `dflow_aggregator_v4` | `kb_decoder_dflow_aggregator_v4` | `kb_decoder_router_dflow_aggregator_v4` | `current_alias` | +| `router_jupiter_aggregator_v4` | `JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB` | `jupiter_agregator_v4` | `jupiter_aggregator_v4` | `-` | `kb_decoder_router_jupiter_aggregator_v4` | `reserved_missing` | +| `router_jupiter_aggregator_v6` | `JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4` | `jupiter_agregator_v6` | `jupiter_aggregator_v6` | `-` | `kb_decoder_router_jupiter_aggregator_v6` | `reserved_missing` | +| `router_jupiter_dca` | `DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M` | `jupiter_dca` | `jupiter_dca` | `kb_decoder_jupiter_dca` | `kb_decoder_router_jupiter_dca` | `current_alias` | +| `router_okx_labs_v1` | `6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma` | `okx_labs_v1` | `okx_labs_v1` | `kb_decoder_okx_lab_v1` | `kb_decoder_router_okx_labs_v1` | `current_alias` | +| `router_okx_labs_v2` | `proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u` | `okx_labs_v2` | `okx_labs_v2` | `-` | `kb_decoder_router_okx_labs_v2` | `reserved_missing` | +| `router_raydium_amm_routing` | `routeUGWgWzqBWFcrCfv8tritsqukccJPu3q5GPP3xS` | `raydium_amm_routing` | `raydium_amm_routing` | `-` | `kb_decoder_router_raydium_amm_routing` | `reserved_missing` | +| `router_sanctum` | `stkitrT1Uoy18Dk1fTrgPw8W6MVzoCfYoAFT4MLsmhq` | `sanctum_router` | `sanctum_router` | `-` | `kb_decoder_router_sanctum` | `reserved_missing` | +| `router_tessera_v` | `TessVdML9pBGgG9yGks7o4HewRaXVAMuoVj4x83GLQH` | `tessera_v` | `tessera_v` | `-` | `kb_decoder_router_tessera_v` | `reserved_missing` | +| `router_titan_exchange_router` | `T1TANpTeScyeqVzzgNViGDNrkQ6qHz9KrSBS4aNXvGT` | `titan_exchange_router` | `titan_exchange_router` | `-` | `kb_decoder_router_titan_exchange_router` | `reserved_missing` | + + +## Launchpads et bonding curves + +| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut | +|--------------------------------------|------------------------------------------------|----------------------------|----------------------------|-----------------------------|-------------------------------------------------|--------------------| +| `launchpad_boop_fun` | `boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4` | `boop_fun` | `boop_fun` | `kb_decoder_boop_fun` | `kb_decoder_launchpad_boop_fun` | `current_alias` | +| `launchpad_letsbonk_fun` | `FfYek5vEz23cMkWsdJwG2oa6EphsvXSHrGpdALN4g6W1` | `letsbonk_fun` | `letsbonk_fun` | `-` | `kb_decoder_launchpad_letsbonk_fun` | `reserved_missing` | +| `launchpad_metadao_ico` | `moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM` | `metadao_ico` | `metadao_ico` | `-` | `kb_decoder_launchpad_metadao_ico` | `reserved_missing` | +| `launchpad_meteora_dbc` | `dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN` | `meteora_dbc` | `meteora_dbc` | `kb_decoder_meteora_dbc` | `kb_decoder_launchpad_meteora_dbc` | `current_alias` | +| `launchpad_moonit` | `MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG` | `moonit` | `moonit` | `kb_decoder_moonit` | `kb_decoder_launchpad_moonit` | `current_alias` | +| `launchpad_moonshot_token_authority` | `7rtiKSUDLBm59b1SBmD9oajcP8xE64vAGSMbAN5CXy1q` | `moonshot_token_authority` | `moonshot_token_authority` | `-` | `kb_decoder_launchpad_moonshot_token_authority` | `reserved_missing` | +| `launchpad_orca_wavebreak` | `waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF` | `orca_wavebreak` | `orca_wavebreak` | `kb_decoder_orca_wavebreak` | `kb_decoder_launchpad_orca_wavebreak` | `current_alias` | +| `launchpad_printr` | `T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint` | `printr` | `printr` | `kb_decoder_printr` | `kb_decoder_launchpad_printr` | `current_alias` | +| `launchpad_pump_fun` | `6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P` | `pump_fun` | `pump_fun` | `kb_decoder_pump_fun` | `kb_decoder_launchpad_pump_fun` | `current_alias` | +| `launchpad_pump_pumpup_ai` | `PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd` | `pumpup_ai` | `pumpup_ai` | `-` | `kb_decoder_launchpad_pump_pumpup_ai` | `reserved_missing` | +| `launchpad_raydium_launchlab` | `LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj` | `raydium_launchlab` | `raydium_launchlab` | `-` | `kb_decoder_launchpad_raydium_launchlab` | `reserved_missing` | + + +## Lending, vault, staking, bridge, perps, oracle et treasury + +| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut | +|---------------------------------------------------|------------------------------------------------|-------------------------------------------|-------------------------------------------|---------------------------------|--------------------------------------------------------------|--------------------| +| `bridge_circle_cctp_token_messenger_minter` | `CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3` | `cctp_token_messenger_minter` | `cctp_token_messenger_minter` | `-` | `kb_decoder_bridge_circle_cctp_token_messenger_minter` | `reserved_missing` | +| `bridge_circle_cctp_token_messenger_minter_v2` | `CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe` | `cctp_token_messenger_minter_v2` | `cctp_token_messenger_minter_v2` | `-` | `kb_decoder_bridge_circle_cctp_token_messenger_minter_v2` | `reserved_missing` | +| `bridge_de_bridge_destination` | `dst5MGcFPoBeREFAA5E3tU5ij8m5uVYwkzkSAbsLbNo` | `de_bridge_destination` | `de_bridge_destination` | `-` | `kb_decoder_bridge_de_bridge_destination` | `reserved_missing` | +| `bridge_de_bridge_source` | `src5qyZHqTqecJV4aY6Cb6zDZLMDzrDKKezs22MPHr4` | `de_bridge_source` | `de_bridge_source` | `-` | `kb_decoder_bridge_de_bridge_source` | `reserved_missing` | +| `bridge_layer_zero_endpoint` | `76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6` | `layer_zero_endpoint` | `layer_zero_endpoint` | `-` | `kb_decoder_bridge_layer_zero_endpoint` | `reserved_missing` | +| `bridge_layer_zero_executor` | `6doghB248px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn` | `layer_zero_executor` | `layer_zero_executor` | `-` | `kb_decoder_bridge_layer_zero_executor` | `reserved_missing` | +| `bridge_wormhole` | `wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb` | `whormhole_bridge` | `wormhole_bridge` | `-` | `kb_decoder_bridge_wormhole` | `reserved_missing` | +| `lending_jupiter_lend_borrow` | `jupr81YtYssSyPt8jbnGuiWon5f6x9TcDEFxYe3Bdzi` | `jupiter_lend_borrow` | `jupiter_lend_borrow` | `-` | `kb_decoder_lending_jupiter_lend_borrow` | `reserved_missing` | +| `lending_jupiter_lend_earn` | `jup3YeL8QhtSx1e253b2FDvsMNC87fDrgQZivbrndc9` | `jupiter_lend_earn` | `jupiter_lend_earn` | `-` | `kb_decoder_lending_jupiter_lend_earn` | `reserved_missing` | +| `lending_jupiter_lend_flash_loan` | `jupgfSgfuAXv4B6R2Uxu85Z1qdzgju79s6MfZekN6XS` | `jupiter_lend_flash_loan` | `jupiter_lend_flash_loan` | `-` | `kb_decoder_lending_jupiter_lend_flash_loan` | `reserved_missing` | +| `lending_jupiter_lend_liquidity` | `jupeiUmn818Jg1ekPURTpr4mFo29p46vygyykFJ3wZC` | `jupiter_lend_liquidity` | `jupiter_lend_liquidity` | `-` | `kb_decoder_lending_jupiter_lend_liquidity` | `reserved_missing` | +| `lending_kamino` | `KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD` | `kamino_lending` | `kamino_lending` | `-` | `kb_decoder_lending_kamino` | `reserved_missing` | +| `lending_marginfi` | `MFLQPPPPjNinkdKoy2odNFBhvpY43XtCDZjBwG2fwn5` | `marginfi` | `marginfi` | `-` | `kb_decoder_lending_marginfi` | `reserved_missing` | +| `lending_marginfi_v2` | `MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA` | `marginfi_v2` | `marginfi_v2` | `-` | `kb_decoder_lending_marginfi_v2` | `reserved_missing` | +| `lending_sharky_fi` | `SHARKobtfF1bHhxD2eqftjHBdVSCbKo9JtgK71FhELP` | `sharky_fi` | `sharky_fi` | `-` | `kb_decoder_lending_sharky_fi` | `reserved_missing` | +| `lending_solend_protocol` | `So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo` | `solend_protocol` | `solend_protocol` | `-` | `kb_decoder_lending_solend_protocol` | `reserved_missing` | +| `oracle_jupiter_prediction_market` | `3ZZuTbwC6aJbvteyVxXUS7gtFYdf7AuXeitx6VyvjvUp` | `jupiter_prediction_market` | `jupiter_prediction_market` | `-` | `kb_decoder_oracle_jupiter_prediction_market` | `reserved_missing` | +| `perpetuals_drift_v2` | `dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH` | `drift_v2` | `drift_v2` | `kb_decoder_drift_v2` | `kb_decoder_perpetuals_drift_v2` | `current_alias` | +| `perpetuals_jupiter` | `PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu` | `jupiter_perpetuals` | `jupiter_perpetuals` | `kb_decoder_jupiter_perpetuals` | `kb_decoder_perpetuals_jupiter` | `current_alias` | +| `perpetuals_zeta` | `ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD` | `zeta` | `zeta` | `kb_decoder_zeta` | `kb_decoder_perpetuals_zeta` | `current_alias` | +| `perpetuals_zeta_matching_engine` | `zDEXqXEG7gAyxb1Kg9mK5fPnUdENCGKzWrM21RMdWRq` | `zeta_matching_engine` | `zeta_matching_engine` | `-` | `kb_decoder_perpetuals_zeta_matching_engine` | `reserved_missing` | +| `staking_jito_tip_distribution` | `4R3gSG8BpU4t19KYj8CfnbtRpnT8gtk4dvTHxVRwc2r7` | `jito_tip_distribution` | `jito_tip_distribution` | `-` | `kb_decoder_staking_jito_tip_distribution` | `reserved_missing` | +| `staking_kamino_farm` | `FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr` | `kamino_farm` | `kamino_farm` | `-` | `kb_decoder_staking_kamino_farm` | `reserved_missing` | +| `staking_marinade_finance` | `MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD` | `marinade_finance` | `marinade_finance` | `-` | `kb_decoder_staking_marinade_finance` | `reserved_missing` | +| `staking_sanctum_multi_validator_spl_stake_pool` | `SPMBzsVUuoHA4Jm6KunbsotaahvVikZs1JyTW6iJvbn` | `sanctum_multi_validator_spl_stake_pool` | `sanctum_multi_validator_spl_stake_pool` | `-` | `kb_decoder_staking_sanctum_multi_validator_spl_stake_pool` | `reserved_missing` | +| `staking_sanctum_single_validator_spl_stake_pool` | `SP12tWFxD9oJsVWNavTTBZvMbA6gkAmxtVgxdqvyvhY` | `sanctum_single_validator_spl_stake_pool` | `sanctum_single_validator_spl_stake_pool` | `-` | `kb_decoder_staking_sanctum_single_validator_spl_stake_pool` | `reserved_missing` | +| `staking_solayer` | `sSo1iU21jBrU9VaJ8PJib1MtorefUV4fzC9GURa2KNn` | `solayer` | `solayer` | `kb_decoder_solayer` | `kb_decoder_staking_solayer` | `current_alias` | +| `treasury_helium_treasury_management` | `treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5` | `helium_treasury_management` | `helium_treasury_management` | `-` | `kb_decoder_treasury_helium_treasury_management` | `reserved_missing` | +| `vault_kamino` | `kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr` | `kamino_vault` | `kamino_vault` | `-` | `kb_decoder_vault_kamino` | `reserved_missing` | +| `vault_kamino_v2` | `KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd` | `kamino_vault_v2` | `kamino_vault_v2` | `-` | `kb_decoder_vault_kamino_v2` | `reserved_missing` | +| `vault_meteora` | `24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi` | `meteora_vault` | `meteora_vault` | `kb_decoder_meteora_vault` | `kb_decoder_vault_meteora` | `current_alias` | +| `vesting_streamflow` | `strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m` | `streamflow` | `streamflow` | `-` | `kb_decoder_vesting_streamflow` | `reserved_missing` | + + +## NFT, metadata, admin, governance et locks + +| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut | +|------------------------------------|------------------------------------------------|---------------------------|---------------------------|------------------------|-----------------------------------------------|--------------------| +| `lock_raydium_lp` | `LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE` | `raydium_lock_lp` | `raydium_lock_lp` | `-` | `kb_decoder_lock_raydium_lp` | `reserved_missing` | +| `admin_jupiter_lock` | `LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn` | `jupiter_lock` | `jupiter_lock` | `-` | `kb_decoder_admin_jupiter_lock` | `reserved_missing` | +| `admin_pump_fees` | `pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ` | `pump_fees` | `pump_fees` | `kb_decoder_pump_fees` | `kb_decoder_admin_pump_fees` | `current_alias` | +| `metadata_metaplex_token_metadata` | `metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s` | `metaplex_token_metadata` | `metaplex_token_metadata` | `-` | `kb_decoder_metadata_metaplex_token_metadata` | `reserved_missing` | +| `metadata_name_service` | `namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX` | `name_service` | `name_service` | `-` | `kb_decoder_metadata_spl_name_service` | `reserved_missing` | +| `nft_metaplex_bubblegum` | `BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY` | `bubblegum` | `bubblegum` | `-` | `kb_decoder_nft_metaplex_bubblegum` | `reserved_missing` | +| `nft_metaplex_mpl_core` | `CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d` | `mpl_core` | `mpl_core` | `-` | `kb_decoder_nft_metaplex_mpl_core` | `reserved_missing` | + + +## Surfaces à classifier + +Ces entrées ont un `program_id`, mais leur fonction réelle n'est pas encore assez claire pour créer une crate canonique. Elles doivent être étudiées avant renommage. + +| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut | +|---------------------------------------|------------------------------------------------|-------------------------------|-------------------------------|----------------------------------|-------------|--------------------------------------| +| `unknown_aquifer` | `AQU1FRd7papthgdrwPTTq5JacJh8YtwEXaBfKU3bTz45` | `aquifer` | `aquifer` | `-` | `-` | `needs_classification` | +| `unknown_axiom_trade` | `FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9` | `axiom_trade` | `axiom_trade` | `-` | `-` | `needs_classification` | +| `unknown_bison_fi` | `BiSoNHVpsVZW2F7rx2eQ59yQwKxzU5NvBcmKshCSUypi` | `bison_fi` | `bison_fi` | `kb_decoder_bisonfi` | `-` | `current_alias_needs_classification` | +| `unknown_carrot_defi` | `CarrotwivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s` | `carrot_defi` | `carrot_defi` | `-` | `-` | `needs_classification` | +| `unknown_clone` | `C1onEW2kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr` | `clone` | `clone` | `kb_decoder_clone` | `-` | `current_alias_needs_classification` | +| `unknown_gavel` | `srAMMzfVHVAtgSJc8iH6CfKzuWuUTzLHVCE81QU1rgi` | `gavel` | `gavel` | `kb_decoder_gavel` | `-` | `current_alias_needs_classification` | +| `unknown_hawk_fi` | `FqGg2Y1FNxMiGd51Q6UETixQWkF5fB92MysbYogRJb3P` | `hawk_fi` | `hawk_fi` | `-` | `-` | `needs_classification` | +| `unknown_humidi_fi` | `9H6tua7jkLhdm3w8BvgpTn5LZNU7g4ZynDmCiNN3q6Rp` | `humidi_fi` | `humidi_fi` | `-` | `-` | `needs_classification` | +| `unknown_hylo_stability_pool` | `HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ` | `hylo_stability_pool` | `hylo_stability_pool` | `kb_decoder_hylo_stability_pool` | `-` | `current_alias_needs_classification` | +| `unknown_jup_studio_authority` | `8rE9CtCjwhSmbwL5fbJBtRFsS3ohfMcDFeTTC7t4ciUA` | `jup_studio_authority` | `jup_studio_authority` | `-` | `-` | `needs_classification` | +| `unknown_jupiter_apepro_smart_wallet` | `JSW99DKmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw` | `jupiter_apepro_smart_wallet` | `jupiter_apepro_smart_wallet` | `-` | `-` | `needs_classification` | +| `unknown_kamino` | `6LtLpnUFNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc` | `kamino` | `kamino` | `-` | `-` | `needs_classification` | +| `unknown_metadao_bid_wall` | `WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx` | `metadao_bid_wall` | `metadao_bid_wall` | `-` | `-` | `needs_classification` | +| `unknown_numeraire` | `NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P` | `numeraire` | `numeraire` | `kb_decoder_numeraire` | `-` | `current_alias_needs_classification` | +| `unknown_ondo_global_markets` | `XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm` | `ondo_global_markets` | `ondo_global_markets` | `-` | `-` | `needs_classification` | +| `unknown_ore_v3` | `oreV3EG1i9BEgiAJ8b177Z2S2rMarzak4NMv1kULvWv` | `ore_v3` | `ore_v3` | `-` | `-` | `needs_classification` | +| `unknown_penguin_finance` | `PSwapMdSai8tjrEXcxFeQth87xC4rRsa4VA5mhGhXkP` | `pinguin_finance` | `penguin_finance` | `-` | `-` | `needs_classification` | +| `unknown_sabre_decimal_wrapper` | `DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB` | `sabre_decimal_wrapper` | `sabre_decimal_wrapper` | `-` | `-` | `needs_classification` | +| `unknown_sanctum_s_controller` | `5ocnV1qiCgaQR8Jb8xWnVbApfaygJ8tNoZfgPwsgx9kx` | `sanctum_s_controller` | `sanctum_s_controller` | `-` | `-` | `needs_classification` | +| `unknown_scorch` | `SCoRcH8c2dpjvcJD6FiPbCSQyQgu3PcUAWj2Xxx3mqn` | `scorch` | `scorch` | `kb_decoder_scorch` | `-` | `current_alias_needs_classification` | +| `unknown_swig` | `swigypWHEksbC64pWKwah1WTeh9JXwx8H1rJHLdbQMB` | `swig` | `swig` | `-` | `-` | `needs_classification` | + + +## Autres surfaces + +| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut | +|-----------|------------|--------|-----------|----------------|-------------|--------| + + +## Doublons et aliases à surveiller + +- Les noms `okx_v1`, `okx_v2`, `onchain_labs_dex_v1`, `onchain_labs_dex_v2` ne doivent pas être retenus comme noms canoniques tant que les `program_id` prouvés sont `okx_labs_v1` et `okx_labs_v2`. +- `goosefx_gamma` et `goosefx_v2` ne doivent pas être fusionnés tant que les `program_id` restent distincts. +- `raydium_pool_v4` doit être traité comme alias de documentation de `amm_raydium_lp_v4` si le `program_id` est `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8`. +- `meteora_pools_amm` est un alias documentaire probable de `amm_meteora_damm_v1` si le `program_id` est `Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB`. + +## Règle de migration + +Aucun renommage physique massif ne doit être fait dans un delta général. Pour chaque surface : + +1. confirmer le `program_id` ; +2. confirmer la fonction réelle ; +3. choisir le `canonical_surface_code` ; +4. créer ou renommer une seule crate ; +5. mettre à jour `Cargo.toml` ; +6. exécuter `cargo build` ; +7. mettre à jour le registre ; +8. supprimer l'ancien alias seulement après validation. + + +## Comptes qui ne sont pas des programmes + +Les comptes qui ne sont pas prouvés comme programmes exécutables doivent rester hors du registre de surfaces. Le cas `BAGSB9TpGrZxQbEsrEznv5jXXdwyP6AXerN8aVRiAmcv` est documenté dans `docs/ACCOUNT_ONLY_CANDIDATES.md`. + +## Index hexadécimal optionnel + +Un préfixe `00` à `ff` ne doit pas remplacer le nom canonique. Il peut être ajouté comme champ `registry_code` dans le registre, mais les crates doivent rester descriptives. Voir `docs/PROGRAM_CODE_INDEX.md`. + +## Bags Fee Share + +| Canonique | Program id | Source | Fonction | Crate cible | Statut | +|--------------------------|------------------------------------------------|-------------------|----------|-------------------------------------|---------------------| +| `fees_bags_fee_share_v1` | `FEEhPbKVKnco9EXnaY3i4R5rQVUx91wgVfu8qokixywi` | Bags Fee Share V1 | `fees` | `kb_decoder_fees_bags_fee_share_v1` | `canonical_current` | +| `fees_bags_fee_share_v2` | `FEE2tBhCKAt7shrod19QttSVREUYPiyMzoku1mL1gqVK` | Bags Fee Share V2 | `fees` | `kb_decoder_fees_bags_fee_share_v2` | `canonical_current` | diff --git a/docs/PROJECT_OBJECTIVES.md b/docs/PROJECT_OBJECTIVES.md new file mode 100644 index 0000000..820a2c0 --- /dev/null +++ b/docs/PROJECT_OBJECTIVES.md @@ -0,0 +1,20 @@ + + + +# Objectifs du projet + +## Objectif court terme + +L'objectif court terme est de construire un système de trading assisté sur Solana. Le programme doit aider l'opérateur à observer les créations de tokens, créations de pools, migrations, changements de liquidité, swaps, claims de frais et opérations administratives qui peuvent influencer une décision d'achat ou de revente. + +Le système ne doit pas commencer par une stratégie autonome. Il doit d'abord fournir une observation fiable, des démos live, des garde-fous d'exécution et une validation post-transaction. + +## Objectif long terme + +L'objectif long terme est de construire une bibliothèque d'analyse Solana capable de rivaliser progressivement avec des outils d'exploration et d'analyse comme les indexeurs publics : parser la blockchain, classifier les programmes, décoder les instructions, matérialiser les événements métier, agréger les états et rendre les surfaces inconnues auditables. + +Ce second objectif impose de conserver une architecture large : raw immuable, registres de programmes, décodeurs par surface, matérialisateurs transversaux, exécuteurs séparés, tests anti-régression et documentation de classification. + +## Conséquence sur le squelette + +Le squelette doit rester plus large que le besoin immédiat de trading. En revanche, le développement actif doit rester priorisé : logging, configuration, stockage, RPC, wallet, démos live, puis surfaces prioritaires pour les listeners et l'exécution contrôlée. diff --git a/docs/RAW_STORAGE_LIFECYCLE.md b/docs/RAW_STORAGE_LIFECYCLE.md new file mode 100644 index 0000000..8cd800e --- /dev/null +++ b/docs/RAW_STORAGE_LIFECYCLE.md @@ -0,0 +1,115 @@ + + + +# Cycle de vie du stockage transactionnel Solana + +## Principe général + +Le stockage rejouable repose désormais sur une transaction Solana canonique unique par signature. Les formats d’origine JSON-RPC, WebSocket Helius ou Yellowstone Protobuf sont des formats de transport, pas des modèles de stockage métier. + +Le cycle est : + +```text +acquisition + -> normalisation canonique + -> persistance raw canonique + -> extraction core + -> décodage + -> matérialisation + -> compaction / archivage / purge éventuelle +``` + +## Transaction canonique + +`kb_sol_raw_transactions` conserve suffisamment d’information pour : + +- auditer une transaction ; +- reconstruire les tables `core` ; +- rejouer de nouvelles versions de décodeurs ; +- rejouer de nouvelles versions de matérialiseurs ; +- comparer plusieurs sources sans dépendre de leur format. + +La ligne est unique par signature. `canonical_format_version` permet de faire évoluer le contrat sans ambiguïté. + +## Observations d’acquisition + +`kb_sol_obs_transaction_observations` conserve les preuves techniques d’acquisition : + +- fournisseur et endpoint ; +- protocole et méthode ; +- origine live/backfill/replay/réparation ; +- commitment ; +- session et filtre ; +- timestamps de détection, réception, normalisation et persistance ; +- taille du message ; +- statut et erreur éventuelle. + +Cette table ne conserve pas le payload complet. Elle peut contenir plusieurs lignes pour une même signature. + +## Ancienne table WebSocket + +`kb_sol_raw_ws_notifications` est une table historique de `0.2.3`, supprimée pendant la transition validée `0.3.1`. Elle n’existe plus dans la baseline SQL active. + +Les nouvelles notifications WebSocket ne doivent pas être stockées intégralement par défaut. Une notification de logs peut rester temporairement dans une queue mémoire jusqu’à l’hydratation de la transaction. + +## États de rétention + +Les états de rétention de `kb_sol_raw_transactions` sont : + +```text +full +compacted +archived +purged +``` + +### `full` + +Le document canonique complet est présent dans PostgreSQL. + +### `compacted` + +Une représentation réduite ou un stockage externe permet encore l’audit minimal, mais le payload canonique complet n’est plus dans la ligne chaude. + +### `archived` + +Le payload a été déplacé vers un stockage d’archive durable. + +### `purged` + +Le payload n’est plus disponible. Le hash canonique, la signature, le slot et les données dérivées doivent rester suffisants pour les diagnostics autorisés. + +## États de traitement + +```text +received +core_extracted +decoded +materialized +failed +``` + +L’état décrit la progression de la transaction canonique, pas celle de chaque observation de source. + +## Conditions avant compaction ou purge + +Aucune purge destructive ne doit être activée avant validation de : + +- l’extraction core idempotente ; +- la couverture des décodeurs prioritaires ; +- la reconstruction depuis archive ; +- la stabilité du hash canonique ; +- la conservation du ledger de traitement ; +- la stratégie de sauvegarde PostgreSQL. + +## Payloads fournisseur de diagnostic + +Les payloads complets spécifiques à un fournisseur peuvent être exportés temporairement pour une session de test. Ils ne doivent pas devenir une dépendance du replay métier. + +Un export de diagnostic doit être : + +- opt-in ; +- borné en durée et en taille ; +- associé à une session ; +- supprimable indépendamment de PostgreSQL. + diff --git a/docs/RAW_STORE.md b/docs/RAW_STORE.md new file mode 100644 index 0000000..2e84e62 --- /dev/null +++ b/docs/RAW_STORE.md @@ -0,0 +1,147 @@ + + + +# Store transactionnel Solana canonique + +## Historique + +`0.2.3` a créé deux tables minimales : + +```text +kb_sol_raw_rpc_transactions +kb_sol_raw_ws_notifications +``` + +Ces tables restent un jalon historique validé dans `CHANGELOG.md`. La transition a été appliquée et contrôlée sur PostgreSQL réel pendant `0.3.1`. + +Le modèle actif est : + +```text +kb_sol_raw_transactions +kb_sol_obs_transaction_observations +``` + +## Objectif cible + +Le store principal conserve une transaction Solana source-indépendante par signature. Les sources HTTP, WebSocket Helius et Yellowstone gRPC ne doivent pas produire plusieurs copies complètes du même contenu. + +```text +source provider + -> normalisation canonique + -> transaction unique + -> observations multiples +``` + +## Table `kb_sol_raw_transactions` + +Rôle : stocker la représentation canonique rejouable de la transaction et de ses metadata. + +Colonnes principales visées : + +| Colonne | Rôle | +|----------------------------|-----------------------------------------| +| `id` | clé primaire technique | +| `signature` | signature Solana unique | +| `slot` | slot de la transaction | +| `canonical_format_version` | version du contrat canonique | +| `canonical_json` | transaction et metadata normalisées | +| `canonical_json_hash` | hash déterministe du document canonique | +| `retention_state` | état de rétention | +| `processing_state` | état d’extraction et de traitement | +| `lifecycle_reason` | raison technique optionnelle | +| `created_at` | première insertion | +| `updated_at` | dernière évolution autorisée | + +Le contenu canonique ne contient pas de nom de fournisseur, endpoint, subscription id ou format de transport. + +`kb_model::CanonicalTransaction` fournit la sérialisation déterministe et le hash SHA-256. `kb_store_core::RawTransactionInsert::from_canonical` construit le DTO de persistance avec la signature primaire, le slot, `canonical_format_version`, `canonical_json` et `canonical_json_hash`. Le transport RPC ne dépend donc pas du backend PostgreSQL. + +## Table `kb_sol_obs_transaction_observations` + +Rôle : enregistrer chaque détection ou acquisition d’une signature sans recopier le payload complet. + +Colonnes principales visées : + +| Colonne | Rôle | +|-----------------------|-------------------------------------------------------| +| `id` | clé primaire technique | +| `observation_key` | clé idempotente de l’observation | +| `raw_transaction_id` | lien optionnel vers `kb_sol_raw_transactions` | +| `signature` | signature détectée ou reçue | +| `slot` | slot quand connu | +| `provider` | fournisseur | +| `endpoint_code` | endpoint de configuration | +| `protocol` | protocole de transport | +| `acquisition_method` | méthode ou type de stream | +| `origin` | `live`, `backfill`, `replay`, `repair` ou `migration` | +| `commitment` | commitment demandé ou reçu | +| `capture_session_id` | session de mesure ou d’ingestion | +| `filter_code` | filtre logique utilisé | +| `detected_at` | premier signal reçu | +| `received_at` | transaction complète reçue | +| `normalized_at` | fin de normalisation | +| `persisted_at` | fin de persistance | +| `payload_size_bytes` | taille du message source | +| `source_payload_hash` | hash optionnel du message source | +| `status` | résultat technique | +| `error_code` | erreur normalisée optionnelle | +| `error_message` | message de diagnostic optionnel | + +Cette table ne possède pas de `raw_json`, de `canonical_json` ni de payload Protobuf complet. + +## Notifications WebSocket + +`kb_sol_raw_ws_notifications` n’est plus conservée comme cible durable. + +Pour `logsSubscribe` : + +1. recevoir la signature et le slot ; +2. mémoriser temporairement le timestamp de détection ; +3. hydrater par `getTransaction` si nécessaire ; +4. écrire la transaction canonique ; +5. écrire une observation avec `detected_at`, `received_at` et le statut d’hydratation. + +Les payloads complets de notifications peuvent être exportés temporairement pour un benchmark borné, mais ils ne doivent pas être dupliqués en PostgreSQL. + +## Fusion par signature + +La signature est la clé d’unicité de la transaction canonique. + +- hash identique : conserver la ligne et ajouter une observation ; +- metadata plus complètes : appliquer un enrichissement déterministe ; +- différence incompatible : enregistrer un conflit et ne pas écraser silencieusement ; +- transaction absente ou erreur provider : écrire uniquement l’observation technique. + +## Cycle de vie + +Les états de rétention de la transaction canonique restent : + +```text +full +compacted +archived +purged +``` + +Les états de traitement restent : + +```text +received +core_extracted +decoded +materialized +failed +``` + +Les observations sont append-only et suivent une rétention technique distincte. + +## Baseline SQL active + +Après validation de la transition historique, le dossier `kb_store_pg/migrations/` a été consolidé : + +```text +0001_canonical_transaction_store.sql +0002_core_store.sql +``` + +La baseline ne contient que les tables et colonnes actives. Les anciens noms RPC/WS ne sont plus recréés. Le DDL runtime conserve temporairement un chemin de compatibilité idempotent pour les workspaces encore issus de `pre.001`. diff --git a/docs/REPLAY_PIPELINE.md b/docs/REPLAY_PIPELINE.md new file mode 100644 index 0000000..09d7cde --- /dev/null +++ b/docs/REPLAY_PIPELINE.md @@ -0,0 +1,80 @@ + + + +# Pipeline de replay + +Le replay ne doit pas être global par défaut. Chaque campagne reçoit un `campaign_id` et cible un ensemble explicite de processors, une version et un périmètre borné d’inputs. + +## Étapes + +```text +ingest -> extract -> observe -> decode -> materialize -> aggregate -> validate +``` + +Chaque étape doit être rejouable séparément avec son propre ledger versionné. + +## Sélection de décodage + +La sélection opérateur peut utiliser : + +- des signatures ; +- une plage de slots ; +- des `program_id` ; +- des instruction paths ; +- des états lifecycle ; +- une limite stricte. + +Le pipeline calcule ensuite `effective_program_ids` comme l’union des programmes déclarés par les décodeurs activés. Sans filtre program ID explicite, cette union est appliquée automatiquement au store. Avec un filtre explicite, chaque valeur doit être supportée par au moins un décodeur activé, sinon la campagne est refusée avant la requête SQL. + +Cette règle évite qu’un classifieur natif sélectionne des instructions SPL, Jupiter, Pump ou d’autres programmes non compatibles. Elle empêche également la resélection infinie d’un lot entièrement `unmatched` uniquement parce que les processors choisis ne couvrent pas ses programmes. + +## Force replay + +Le force replay contourne le skip processor/version/input/hash, mais reste borné par une liste de signatures explicites ou par une autorisation opérateur « toutes les signatures » combinée aux autres filtres et à la limite. Une requête sans l’un de ces deux scopes est refusée. + +Lorsqu’une campagne contient des signatures explicites, `force_replay=true` retire le filtre lifecycle pour ces signatures. Les inputs déjà terminaux peuvent alors être réellement rejoués. Le mode explicite « toutes les signatures » retire également ce filtre, mais conserve obligatoirement le scope des programmes compatibles, les slots et instruction paths éventuels ainsi que la limite. Sans signatures ni autorisation « toutes les signatures », la requête est refusée. + +Le remplacement ne touche que les sorties du processor, de la version et de l’input ciblés. Les autres processors et les autres versions restent intacts. + +## Skip + +Sans force replay, un input est skippé uniquement lorsque le ledger contient un succès ayant exactement : + +```text +stage +processor_name +processor_version +input_key +input_hash +``` + +Une campagne de validation doit pouvoir montrer successivement : + +```text +premier passage -> decoded/ignored/unsupported +second passage -> skipped +force replay -> exécution sans skip +passage suivant -> skipped +``` + +## Unmatched + +`unmatched` signifie qu’aucun décodeur compatible n’a accepté l’input après le filtre exact de programme. L’instruction n’est pas marquée globalement comme définitivement ignorée, car un futur processor peut la supporter. Les décodeurs doivent classer comme `unsupported` les instructions inconnues de leurs propres programmes afin de produire une couverture exploitable. + +## Traçabilité + +Les logs de campagne conservent : + +```text +campaign_id +signature_count +signature_sample +decoder_names +requested_program_ids +effective_program_ids +requested_processing_states +effective_processing_states +force_replay +``` + +Les logs par input conservent ensuite la signature exacte, le path, le programme, le processor, le hash et la décision terminale. diff --git a/docs/RPC_ENDPOINT_ROLES.md b/docs/RPC_ENDPOINT_ROLES.md new file mode 100644 index 0000000..813116f --- /dev/null +++ b/docs/RPC_ENDPOINT_ROLES.md @@ -0,0 +1,77 @@ + + + +# Rôles des endpoints RPC et streams + +Les rôles servent à sélectionner un endpoint selon l’opération, le provider, le protocole et les limites locales. + +## Rôles HTTP actifs + +| Rôle | Usage | +|---------------------|-----------------------------------------------------------------| +| `http_queries` | lectures courantes | +| `history_backfill` | `getSignaturesForAddress`, `getTransaction` et backfills ciblés | +| `http_transactions` | simulation, envoi et confirmation | +| `http_fallback` | endpoint de secours | +| `http_heavy` | appels lourds | + +`0.3.x` utilise principalement `history_backfill` avec les comptes gratuits. + +## Rôles WebSocket standard existants + +| Rôle | Usage | +|-------------------------|-------------------------------| +| `slot_notifications` | progression et santé du flux | +| `logs_subscribe` | logs ciblés | +| `program_subscribe` | comptes détenus par programme | +| `account_subscribe` | comptes précis | +| `program_logs` | compatibilité historique | +| `account_notifications` | compatibilité historique | + +## Rôles futurs `0.10.x` + +| Rôle | Usage | +|----------------------------------|-----------------------------------------------| +| `helius_transaction_stream` | extension Helius `transactionSubscribe` | +| `yellowstone_transaction_stream` | filtre transactions Yellowstone gRPC | +| `logs_all_probe` | comparaison temporaire `logsSubscribe("all")` | +| `realtime_transaction_hydration` | hydratation HTTP et réparation | + +Les transports Helius et Yellowstone restent implémentés dans `kb_rpc`. Les rôles ne créent pas de dépendance provider dans les décodeurs. + +## Request kinds actifs pour le backfill + +```text +get_signatures_for_address +get_transaction +get_signature_statuses +get_slot +get_version +``` + +## Request kinds futurs + +```text +transaction_subscribe +transaction_unsubscribe +yellowstone_transaction_subscribe +logs_subscribe_all +logs_unsubscribe +``` + +## Limites par rôle + +```text +requests_per_second +burst_capacity +max_concurrent_requests +max_subscriptions +pause_after_rate_limit_ms +``` + +Pour les streams gRPC, la configuration future ajoutera les limites de streams, filtres, adresses et taille de queue sans casser les rôles HTTP/WS existants. + +## Preuve runtime + +Un rôle ou un plan configuré ne garantit pas la capacité distante. La réponse du provider reste la preuve runtime. Les refus de plan, filtres invalides et limitations doivent être normalisés comme erreurs de `kb_rpc`. + diff --git a/docs/RUST_WORKSPACE_RULE_AUDIT.md b/docs/RUST_WORKSPACE_RULE_AUDIT.md new file mode 100644 index 0000000..83206a2 --- /dev/null +++ b/docs/RUST_WORKSPACE_RULE_AUDIT.md @@ -0,0 +1,236 @@ + + + +# Audit des règles Rust du workspace + +Violations détectées : **616**. + +## Synthèse + +| Code | Nombre | +|------------|-------:| +| `RUST010` | 156 | +| `RUST011` | 16 | +| `RUST013` | 19 | +| `RUST021` | 1 | +| `TRACE002` | 32 | +| `TRACE003` | 202 | +| `TRACE004` | 190 | + +## Premiers écarts + +- `RUST010` `kb_app_demo/src/demo_backfill.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_backfill.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_backfill.rs:8` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_config.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_config.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_core_extraction.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_core_extraction.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_core_extraction.rs:8` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_decode_replay.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_decode_replay.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_decode_replay.rs:8` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_execution_solana_core.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_execution_solana_core.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_execution_solana_core.rs:8` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_execution_spl.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_http.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_http.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_spl_ata.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_spl_token.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_spl_token_2022.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_sql_common.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_sql_common.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_sql_diag.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_sql_pg_core.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_sql_pg_raw.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_sql_replay_candidates.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_ws.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_ws.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_ws.rs:8` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_ws.rs:993` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/frontend_log.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/lib.rs:13` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/lib.rs:14` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/main.rs:12` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/splash.rs:9` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/splash.rs:10` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_config/src/settings.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_config/src/settings.rs:1223` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_api/src/contracts.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_metadata_metaplex_token_metadata/src/canonical.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_metadata_metaplex_token_metadata/src/decoder.rs:144` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_metadata_metaplex_token_metadata/src/instruction.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_metadata_metaplex_token_metadata/src/instruction.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/address_lookup_table.rs:326` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/compute_budget.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/compute_budget.rs:673` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/config.rs:322` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/feature.rs:167` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/loaders.rs:992` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/payload.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/payload.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/slashing.rs:461` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/stake.rs:805` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/system.rs:438` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/vote.rs:707` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/zk_elgamal.rs:522` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/zk_token_proof.rs:693` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_associated_token_account/src/decoder.rs:335` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_associated_token_account/src/instruction.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_associated_token_account/src/instruction.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_elgamal_registry/src/decoder.rs:95` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_elgamal_registry/src/decoder.rs:162` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_elgamal_registry/src/registry.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_elgamal_registry/src/registry.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_elgamal_registry/src/state.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_elgamal_registry/src/state.rs:49` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_memo/src/decoder.rs:160` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_memo/src/memo.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_memo/src/memo.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_token/src/decoder.rs:144` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_token/src/token.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_token/src/token.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_token_2022/src/decoder.rs:144` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_token_2022/src/state.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_token_2022/src/state.rs:916` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_token_2022/src/token.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_token_2022/src/token.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_execution_api/src/execution.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_execution_api/src/executor.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_execution_safety/src/safety.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_execution_solana/src/nonce.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_execution_solana/src/transaction.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_execution_solana/src/transaction.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_execution_solana/src/transaction.rs:654` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_execution_solana/src/transaction.rs:655` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/address_lookup_table.rs:321` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/builder.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/builder.rs:1374` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/intent.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/native_admin.rs:381` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/precompiles.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/slashing.rs:346` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/stake.rs:1497` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/validation.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/validation.rs:108` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/vote.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/vote.rs:1846` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_associated_token_account/src/intent.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_elgamal_registry/src/builder.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_elgamal_registry/src/builder.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_elgamal_registry/src/intent.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_memo/src/builder.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_memo/src/builder.rs:194` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_memo/src/intent.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_token/src/builder.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_token/src/intent.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_token_2022/src/builder.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_token_2022/src/confidential.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_token_2022/src/confidential.rs:425` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_token_2022/src/intent.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_logging/src/tracing_runtime.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_logging/src/tracing_runtime.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_logging/src/tracing_runtime.rs:8` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_logging/src/tracing_runtime.rs:676` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_logging/src/tracing_runtime.rs:677` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_model/src/canonical_transaction.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_model/src/canonical_transaction.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_model/src/decoded.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_model/src/materialized.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_model/src/nomenclature.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_model/src/observation.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_model/src/solana.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/backfill.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/backfill.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/backfill.rs:8` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/core_extraction.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/core_extraction.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/core_extraction.rs:8` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/decode_replay.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/decode_replay.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/solana_elgamal_registry_stateful.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/solana_elgamal_registry_stateful.rs:227` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/solana_stateful.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/solana_token_stateful.rs:392` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/solana_token_stateful.rs:470` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/execution_rpc.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/execution_rpc.rs:2251` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/get_transaction.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/standard_http.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/ws_client.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/ws_client.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/ws_session.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/ws_session.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/ws_session.rs:1219` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/ws_session.rs:1220` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_store_pg/src/queries/core_extraction_queries.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_store_pg/src/queries/core_queries.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_store_pg/src/queries/decode_pipeline_queries.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_store_pg/src/queries/table_diagnostics_queries.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_wallet/src/wallet.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_wallet/src/wallet.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_wallet/src/wallet.rs:261` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_wallet/src/wallet.rs:294` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_wallet/src/wallet.rs:299` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_wallet/src/wallet.rs:411` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_wallet/src/wallet.rs:524` — ordinary `use` requires a trait-import justification marker +- `RUST011` `kb_executor_solana_core/src/lib.rs:129` — grouped re-export is forbidden +- `RUST011` `kb_executor_solana_core/src/lib.rs:137` — grouped re-export is forbidden +- `RUST011` `kb_executor_solana_core/src/lib.rs:145` — grouped re-export is forbidden +- `RUST011` `kb_executor_solana_core/src/lib.rs:159` — grouped re-export is forbidden +- `RUST011` `kb_executor_spl_token/src/lib.rs:29` — grouped re-export is forbidden +- `RUST011` `kb_executor_spl_token_2022/src/lib.rs:16` — grouped re-export is forbidden +- `RUST011` `kb_executor_spl_token_2022/src/lib.rs:47` — grouped re-export is forbidden +- `RUST011` `kb_rpc/src/lib.rs:167` — grouped re-export is forbidden +- `RUST011` `kb_rpc/src/lib.rs:175` — grouped re-export is forbidden +- `RUST011` `kb_rpc/src/lib.rs:180` — grouped re-export is forbidden +- `RUST011` `kb_rpc/src/lib.rs:188` — grouped re-export is forbidden +- `RUST011` `kb_rpc/src/lib.rs:198` — grouped re-export is forbidden +- `RUST011` `kb_rpc/src/lib.rs:204` — grouped re-export is forbidden +- `RUST011` `kb_rpc/src/lib.rs:209` — grouped re-export is forbidden +- `RUST011` `kb_rpc/src/lib.rs:232` — grouped re-export is forbidden +- `RUST011` `kb_rpc/src/lib.rs:257` — grouped re-export is forbidden +- `RUST013` `kb_decoder_metadata_metaplex_token_metadata/src/canonical.rs:6` — grouped imports are forbidden +- `RUST013` `kb_decoder_spl_token_2022/src/state.rs:916` — grouped imports are forbidden +- `RUST013` `kb_executor_solana_core/src/lib.rs:129` — grouped imports are forbidden +- `RUST013` `kb_executor_solana_core/src/lib.rs:137` — grouped imports are forbidden +- `RUST013` `kb_executor_solana_core/src/lib.rs:145` — grouped imports are forbidden +- `RUST013` `kb_executor_solana_core/src/lib.rs:159` — grouped imports are forbidden +- `RUST013` `kb_executor_spl_token/src/lib.rs:29` — grouped imports are forbidden +- `RUST013` `kb_executor_spl_token_2022/src/confidential.rs:425` — grouped imports are forbidden +- `RUST013` `kb_executor_spl_token_2022/src/lib.rs:16` — grouped imports are forbidden +- `RUST013` `kb_executor_spl_token_2022/src/lib.rs:47` — grouped imports are forbidden +- `RUST013` `kb_rpc/src/lib.rs:167` — grouped imports are forbidden +- `RUST013` `kb_rpc/src/lib.rs:175` — grouped imports are forbidden +- `RUST013` `kb_rpc/src/lib.rs:180` — grouped imports are forbidden +- `RUST013` `kb_rpc/src/lib.rs:188` — grouped imports are forbidden +- `RUST013` `kb_rpc/src/lib.rs:198` — grouped imports are forbidden +- `RUST013` `kb_rpc/src/lib.rs:204` — grouped imports are forbidden +- `RUST013` `kb_rpc/src/lib.rs:209` — grouped imports are forbidden +- `RUST013` `kb_rpc/src/lib.rs:232` — grouped imports are forbidden +- `RUST013` `kb_rpc/src/lib.rs:257` — grouped imports are forbidden +- `RUST021` `kb_app_demo/src/lib.rs:42` — crate-root re-export requires adjacent rustdoc +- `TRACE002` `kb_app_demo/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use` +- `TRACE002` `kb_decoder_metadata_metaplex_token_metadata/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use` +- `TRACE002` `kb_decoder_solana_core/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use` +- `TRACE002` `kb_decoder_spl_associated_token_account/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use` +- `TRACE002` `kb_decoder_spl_elgamal_registry/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use` +- `TRACE002` `kb_decoder_spl_memo/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use` +- `TRACE002` `kb_decoder_spl_token/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use` +- `TRACE002` `kb_decoder_spl_token_2022/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use` + +Le rapport est tronqué aux 200 premiers écarts sur 616. + +## Stratégie de correction + +La mise en conformité est découpée avant toute nouvelle prérelease : + +1. `pre.035-delta-fix-002` : séparation des règles et audit automatisé reproductible ; +2. correctifs suivants : points d'entrée de crates, réexports `pub`/`pub(crate)`, rustdocs et chemins `crate::...` ; +3. correctifs suivants : imports de traits et suppression des imports ordinaires/groupés ; +4. correctifs suivants : `TRACING_TARGET`, dépendances `tracing`, macros et matrice de routes ; +5. correctifs suivants : helpers dupliqués et mutualisation inter-crates ; +6. fermeture : `cargo fmt --all`, audit strict, tests workspace et Clippy global. + +Aucune nouvelle prérelease ne doit être créée tant que cette séquence n'est pas terminée et validée localement. diff --git a/docs/SOLANA_INTERFACE_DEPENDENCIES.md b/docs/SOLANA_INTERFACE_DEPENDENCIES.md new file mode 100644 index 0000000..4b1cd93 --- /dev/null +++ b/docs/SOLANA_INTERFACE_DEPENDENCIES.md @@ -0,0 +1,272 @@ + + + +# Dépendances d’interfaces Solana et SPL + +## Objectif + +Le workspace privilégie les interfaces officielles étroites afin d’éviter de recopier des enums, layouts, IDs ou builders déjà publiés par Solana ou SPL. Les dépendances de `[workspace.dependencies]` constituent un catalogue de versions cohérentes ; elles ne sont pas automatiquement ajoutées à toutes les crates. + +## Règle de consommation + +Une crate ajoute une interface uniquement lorsqu’elle utilise réellement au moins un de ses éléments : + +- enum d’instruction ou d’état ; +- encodeur/décodeur officiel ; +- type de compte ou paramètre ; +- program ID ou sysvar ID ; +- modèle RPC appartenant à sa responsabilité. + +Une déclaration à la racine ne justifie pas une dépendance inutilisée dans une crate feuille. Les features restent minimales et explicites. + +## Ordre des formats + +1. `wincode` lorsqu’il est officiellement exposé par l’interface ; +2. Borsh lorsqu’il correspond au contrat officiel ou au comportement du runtime ; +3. parseur local borné lorsque l’interface officielle ne fournit son helper qu’avec `bincode`. + +### Compatibilité de version `wincode` + +Le workspace reste épinglé sur `wincode ^0.5`. Les crates Solana modulaires actuellement utilisées avec la feature `wincode` publient leurs implémentations `SchemaRead`/`SchemaWrite` contre `wincode 0.5.x`. Ces traits sont nominalement distincts de ceux de `wincode 0.6.x` : une dépendance directe migrée seule vers `0.6` ne peut donc ni désérialiser `solana_nonce::versions::Versions`, ni sérialiser `solana_transaction::Transaction`. Cargo peut charger les deux versions simultanément, mais les implémentations de traits ne sont pas interchangeables et l’orphan rule interdit de les réimplémenter localement pour ces types externes. + +La migration vers `wincode 0.6` doit attendre que l’ensemble des crates Solana consommées migre de façon cohérente. Une double dépendance aliasée n’est acceptable que pour des types propres au workspace ; tous les appels portant sur des types Solana doivent utiliser la même version `0.5.x` que celle employée par leurs crates d’origine. Avant toute migration, vérifier avec `cargo tree -d` et `cargo tree -i wincode@`. + + +Le workspace n’ajoute pas de dépendance directe à `bincode`. Une feature officielle `bincode` seule n’est pas activée pour contourner cette règle. + +## Interfaces utilisées dans `kb_decoder_solana_core` + +| Surface | Interface | Features utiles | Stratégie actuelle | +|---------------------------|---------------------------------------------------|----------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------| +| Address Lookup Table | `solana-address-lookup-table-interface ^3.1` | `serde`, `wincode` | enum officielle et `wincode` exact | +| Compute Budget | `solana-compute-budget-interface ^3.0` | `borsh`, `serde` | enum officielle pour les fixtures ; parser borné aligné sur le Borsh unchecked du runtime | +| System Program | `solana-system-interface ^3.2` | `alloc`, `serde`, `std`, `solana-instruction`, `wincode` | enum officielle et `wincode` | +| Vote Program | `solana-vote-interface ^6.0` | `serde`, `wincode` | enum officielle et `wincode::deserialize_exact` | +| Stake Program | `solana-stake-interface ^4.3` | `borsh`, `serde`, `wincode` | types/ID officiels + miroir wire privé `wincode` exact, compatible avec le layout historique | +| Précompiles signature | sources runtime Agave et crates programme `3.0.0` | aucune nouvelle dépendance consommée | parseurs locaux bornés des tables d’offsets, résolution outer via contrat core `2`, sans cryptographie ni `bincode` | +| ZK ElGamal Proof | `solana-zk-elgamal-proof-interface ^0.1` | aucune feature additionnelle | enum `ProofInstruction` et tailles `Pod` officielles ; comportement des comptes vérifié contre le runtime Agave | +| ZK Token Proof historique | aucune dépendance runtime dédiée | miroir wire local borné | tags et tailles historiques audités contre `solana-zk-token-sdk 3.1.14`; comparaison Agave `v2.0.0`/`v4.1.1` | +| Slashing Program | Agave `v4.1.1`, `solana-program/slashing@fe8da3a` | aucune dépendance ajoutée | parseur local borné du contrat release vérifié ; compte de preuve externe non relu et aucune cryptographie locale | + +## Décision pour les précompiles de signature + +Les crates officielles Ed25519, secp256k1 et secp256r1 exposent les IDs, constantes et structures de layout, mais `kb_program_ids` centralise déjà les IDs et le delta n’exécute aucune vérification cryptographique. Ajouter ces dépendances uniquement pour recopier des tailles ou des structures `Pod` constituerait une dépendance sans consommation fonctionnelle. + +`kb_decoder_solana_core` reproduit les layouts exacts depuis les sources runtime Agave : table Ed25519/secp256r1 de 14 octets, table secp256k1 de 11 octets, sentinelle `u16::MAX` uniquement pour Ed25519/secp256r1, index `u8` toujours explicite pour secp256k1, règles zéro signature et limite secp256r1. Les fixtures manuelles rendent chaque offset visible et les tests couvrent les références inter-instructions et les bornes. Aucune dépendance `bincode` n’est ajoutée. + +## Transaction client dans `kb_execution_solana` + +`kb_execution_solana` n’importe plus l’agrégat `solana-sdk`. Sa frontière transactionnelle utilise les crates modulaires réellement consommées : `solana-instruction`, `solana-message`, `solana-transaction`, `solana-hash`, `solana-pubkey`, `solana-keypair`, `solana-signer`, `solana-nonce` et `solana-system-interface`. Les exécuteurs spécifiques conservent la même politique et ajoutent uniquement l’interface de programme nécessaire. + +Le message à signer provient de `solana_transaction::Transaction::message_data()`. La transaction complète utilise `wincode::serialize`, supporté officiellement par les types modulaires `Message` et `Transaction`. Cette stratégie remplace tout appel direct à `bincode` et reproduit le wire attendu par `getFeeForMessage`, `simulateTransaction` et `sendTransaction`. La taille sérialisée est refusée au-delà de 1 232 octets. + +## Identifiants natifs, Vote et loaders dans l’exécuteur + +`solana-sdk-ids` est conservé intentionnellement comme registre officiel modulaire des adresses natives et sysvars. Les chemins `solana_sdk_ids::sysvar::{clock,rent,slot_hashes,instructions}` ne constituent pas un retour au SDK monolithique : ils fournissent uniquement des `Pubkey` canoniques. `solana-instructions-sysvar` ne doit être ajouté que lorsqu’une crate appelle réellement ses fonctions d’introspection d’instructions, pas pour remplacer un simple ID officiel. + +`kb_executor_solana_core` consomme `solana-vote-interface ^6.0` avec `serde` et `wincode` pour construire les vingt variantes wire Vote actuelles. Il utilise `solana-instruction` pour `Instruction`/`AccountMeta`, `solana-pubkey::Pubkey` pour les adresses publiques, `solana-hash::Hash` pour les hashes et `solana-sdk-ids` pour Rent, Clock et SlotHashes. Les quatre créations composées utilisent `solana-system-interface`; aucune dépendance `solana-sdk` ni feature `bincode` n’est ajoutée à la crate. + +`kb_decoder_solana_core` et `kb_executor_solana_core` consomment désormais `solana-loader-v3-interface ^8.0` avec `wincode`. Le décodeur désérialise l’enum officielle de façon exacte tout en conservant la compatibilité du booléen optionnel historique des dernières variantes. L’exécuteur appelle les helpers officiels pour onze plans : création de buffer, écriture, déploiement, upgrade, changements d’autorité, fermetures et extension. + +`solana-loader-v4-interface ^3.1` publie l’enum et les comptes officiels, mais ses helpers de construction sont conditionnés par la feature `bincode` et l’enum ne fournit pas de schéma `wincode`. Pour respecter l’interdiction de `bincode`, `kb_executor_solana_core` reproduit localement le wire borné des sept discriminants (`u32 LE`, champs numériques et vecteur borné) et les métadonnées de comptes exactes pour neuf plans. La crate feuille n’ajoute donc pas une dépendance inutilisée à l’interface v4; le catalogue workspace la conserve comme source normative/versionnée. + +## Interfaces officielles non encore consommées + +| Interface | Propriétaire futur | Décision | +|------------------------------------------|------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------| +| `solana-feature-gate-interface ^4.0` | `kb_decoder_solana_core` | peut fournir les types officiels ; l’instruction actuelle reste un tag d’un octet vérifié depuis le builder/processor | +| `solana-feature-set-interface ^4.0` | classification/diagnostics natifs | ajouter seulement lorsqu’un type est consommé | +| `solana-loader-v4-interface ^3.1` | source normative Loader v4 | helpers officiels conditionnés par `bincode`; conserver le miroir wire local borné tant qu’aucun schéma `wincode` n’est publié | +| interfaces SPL | crates `kb_decoder_spl_*` et `kb_executor_spl_*` correspondantes | ne pas les rattacher au décodeur natif ni à l’application | +| types RPC/account/transaction status | `kb_rpc` | ne pas les ajouter à `kb_app_demo` ; l’application consomme les DTO du workspace | + +## Exceptions locales actuelles + +### SPL Memo v1, v3 et v4 + +`kb_decoder_spl_memo` et `kb_executor_spl_memo` consomment `spl-memo-interface ^2.1` sans feature additionnelle. La version effectivement résolue pendant les validations est 2.1.0. Elle publie les trois IDs exacts et un builder générique recevant explicitement le Program ID. Ce builder place les octets exacts du message dans `Instruction::data` et transforme chaque pubkey fournie en compte readonly signer, sans sérialisation intermédiaire. + +L’exécuteur appelle ce builder pour les trois générations et compare chaque plan au résultat officiel. Les différences runtime restent explicites : v1 ignore les comptes, v3/v4 les vérifient. La bibliothèque ne lie toutefois aucune génération à un cluster ou au dry-run : la simulation obligatoire établit la disponibilité effective du Program ID ciblé, puis les politiques communes gouvernent l’autorisation d’envoi et Mainnet. La démo du jalon retient séparément v4 sur Devnet. La taille packet finale reste contrôlée par `kb_execution_solana` après compilation du message. + +Le décodeur ne dépend pas du builder pour lire le wire : il analyse directement les octets base64 retenus par le core, avec une borne de 4 096 octets, puis valide l’UTF‑8. La source historique v1 prouve que les comptes sont ignorés ; les processors v3/v4 exigent au contraire que chaque compte fourni soit signer avant de valider l’UTF‑8. Ces règles restent séparées dans `docs/SPL_MEMO_MATRIX.json` et les tests comparent les trois IDs locaux aux constantes de l’interface officielle. + +L’archive source `0.4.2` ne contenait pas de `Cargo.lock`. Les compilations et validations `0.4.3` ont résolu `spl-memo-interface 2.1.0`, sans mise à jour automatique de la contrainte workspace. Cette version exacte a construit les trois générations et le parcours v4 Devnet réel. + +### SPL Token classique + +Le catalogue workspace déclare `spl-token-interface ^3.0` sans feature. Le tag officiel +`interface@v3.0.0` publie la version `3.0.0`, sans feature optionnelle ni dépendance à `bincode`. +`kb_decoder_spl_token` consomme l'ID officiel et utilise `TokenInstruction::unpack` dans les tests +différentiels. Le wire de production reste un parseur local borné afin de conserver les tailles +consommées, les suffixes acceptés par les processors, les diagnostics UTF-8 et la structure interne +de `Batch`, que l'enum officielle représente seulement par son tag parent. + +La surface compte 28 tags : `0..24`, `38` (`WithdrawExcessLamports`), `45` +(`UnwrapLamports`) et `255` (`Batch`). L'interface `3.0.0` fournit un builder pour chacun. La source +historique `program/src/processor.rs` ne suffit toutefois pas à prouver les deux nouveaux tags : le +processor p-token `1.0.0` sous le même Program ID implémente explicitement `UnwrapLamports` et +`Batch`. La publication de l'interface, la constructibilité, le binaire déployé par cluster et le +résultat de simulation restent quatre preuves séparées dans `docs/SPL_TOKEN_MATRIX.json`. + +Le processor p-token lit les champs numériques avec des contrôles de longueur minimale et peut +donc ignorer des octets suffixes selon l'opération. Le décodeur les conserve et les signale au lieu +de les accepter silencieusement. `Batch` est découpé selon les paires `account_count/data_len`, +interdit les données enfant vides et appelle uniquement le processor interne, ce qui rend un batch +imbriqué invalide. Le décodeur ajoute des bornes défensives de 64 enfants, 512 comptes cumulés et +16 384 octets par instruction. + +La résolution `3.0.0` et les tests différentiels du décodeur ont été confirmés dans le workspace le +15 juillet 2026. `kb_executor_spl_token` consomme les builders officiels de cette même interface. +Les variantes `InitializeMint`, `InitializeAccount`, `InitializeMultisig` et `InitializeAccount2` +restent decode-only dans la politique d'exécution ; les opérations anciennes toujours publiées et +utilisables, comme `Transfer` ou `SetAuthority`, ne sont pas confondues avec des opérations +obsolètes. + +Les signatures officielles de `unwrap_lamports` et `batch` ont été auditées au commit `46eaacd`. +Leur constructibilité est donc prouvée. Leur déploiement sur le programme classique Devnet a été +observé le 16 juillet 2026 par deux simulations sans signature ni envoi : `Batch` avec un enfant +`TransferChecked` de montant nul a réussi en 270 compute units, puis `UnwrapLamports` d'un lamport +depuis un compte wrapped SOL auxiliaire classique a réussi en 140 compute units. Localnet, Testnet +et Mainnet restent des preuves séparées. L'exécuteur ne bloque aucun cluster et laisse +l'autorisation d'envoi aux politiques communes. + +Sur Devnet, le programme classique a également confirmé un `TransferChecked` complet et un +lifecycle sans ATA couvrant `InitializeAccount3`, `MintToChecked`, `TransferChecked`, +`ApproveChecked`, `Revoke`, `BurnChecked` et `CloseAccount`. Sur Mainnet, un corpus canonique de 370 +instructions a été décodé et matérialisé sans entrée unsupported, failed ou unmatched. Localnet n'a +pas été exercé. La matrice conserve donc indépendamment la version d'interface, le processor de +référence, le builder, les observations réelles et le résultat de simulation/soumission par +instruction. + +### SPL Associated Token Account + +Le catalogue workspace déclare `spl-associated-token-account-interface ^2.0` avec la feature +`borsh`. Cargo a résolu la version `2.0.0` pendant le jalon `0.4.5`. Les crates +`kb_decoder_spl_associated_token_account` et `kb_executor_spl_associated_token_account` consomment +réellement l'enum, les builders, le Program ID et le helper officiel de dérivation ; le pipeline et +la démo consomment ensuite leurs contrats typés sans recopier l'interface. + +L'interface publie exactement trois variantes Borsh actuelles : `Create` (`0`), +`CreateIdempotent` (`1`) et `RecoverNested` (`2`). Le processor accepte aussi historiquement une +donnée vide comme `Create`; cette forme reste décodable mais n'est pas produite par le builder +actuel. La dérivation canonique utilise les seeds `[wallet, Token Program ID, mint]` sous le Program +ID ATA. Le Token Program fait donc partie de l'adresse et distingue nécessairement les ATA +classiques des ATA Token-2022 d'un même wallet et mint. + +Le décodeur conserve les comptes, flags, doublons, chemins outer/inner, transactions échouées, +adresses observées et dérivées, ainsi que les rôles distincts de `RecoverNested`. Les tests +différentiels comparent les trois builders et les vecteurs PDA au helper officiel. L'exécuteur +utilise les mêmes builders pour les trois variantes, impose simulation et dry-run par défaut, puis +laisse au pipeline stateful les contrôles de mint, owner, compte existant, rent, signataires et +postconditions. + +Les preuves Devnet du 16 juillet 2026 couvrent `CreateIdempotent` pour SPL Token classique et +Token-2022, y compris création, réutilisation, replay post-exécution idempotent et ATA Token-2022 +avec extension `ImmutableOwner`. Elles couvrent aussi un `RecoverNested` classique contrôlé : le +solde brut a été transféré vers l'ATA wallet, le compte nested a été fermé et les deux faits +lifecycle/risk ont été matérialisés sans dupliquer les CPI SPL Token. Le décodage général des +instructions et extensions Token-2022 reste réservé à `0.4.6`. + +### Token-2022 — audit initial `0.4.6-pre.001` + +Le catalogue workspace déclare `spl-token-2022-interface ^3.1` avec `serde` et +`spl-elgamal-registry-interface ^0.2` sans feature additionnelle. La résolution opérateur confirme +respectivement `3.1.1` avec `default + serde` et `0.2.1` avec `default`, consommées par +`kb_decoder_spl_token_2022` pour poursuivre l'audit. La matrice inventorie 48 tags de premier +niveau et 29 types TLV de production, dont le récent `PermissionedBurnExtension` `46` et son TLV +`PermissionedBurn` `28`. + +Les sous-discriminants, comptes, layouts, tailles, builders et processors restent explicitement +`pending`. L'interface ElGamal `0.2.1` confirme le Program ID +`regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg` et la seed PDA `elgamal-registry`. Le registre +garde toutefois une matrice, un dispatch et une politique d'exécution séparés du programme natif +ZK ElGamal Proof. Les TLV Token Metadata/Token Group et les pointeurs Transfer Hook ne donnent +aucun Program ID universel aux programmes externes qui implémentent ces interfaces. + +L'interface Token-2022 `3.1.1` publie le commit exact +`e18f9c6f9bf6044b934f48e3090e8e59e4820f02` (`interface@v3.1.1`). Au même commit, le processor +porte la version `11.0.0` et son lock source résout Token Group `0.7.2`, Token Metadata `1.0.0` et +le registre ElGamal `0.2.1`. Son dispatch tente d'abord les 48 tags Token-2022, puis les cinq +discriminants Token Metadata et les quatre discriminants Token Group. Cette incorporation ne donne +pas de Program ID universel aux interfaces : elle décrit seulement leur exécution directe par +Token-2022. L'égalité avec les binaires cluster reste à prouver séparément. + +Le `Cargo.lock` opérateur confirme le graphe réellement consommé par l'interface `3.1.1` : ZK +ElGamal Proof `0.1.3`, extraction de preuve confidentielle `0.6.1`, Token Group `0.7.2`, Token +Metadata `1.0.1` et TLV `0.9.1`. Token Metadata apparaît directement sous +`spl-token-2022-interface` dans `cargo tree -p ... -e features`. Le processor source `11.0.0` +audité figeait encore Token Metadata `1.0.0`. La publication officielle `1.0.1` ne change ni wire, +ni discriminant, ni builder : elle ajoute seulement, avant Borsh, une validation bornée des +longueurs de chaînes de `Initialize`, `UpdateField` et `RemoveKey`. Cette garde plus sûre devient +normative pour le décodeur ; `UpdateAuthority` et `Emit` restent sans chaîne. + +### ZK ElGamal Proof + +`kb_decoder_solana_core` consomme directement `solana-zk-elgamal-proof-interface ^0.1`. L’enum `ProofInstruction` fournit les treize discriminants et les types de `proof_data` fournissent les tailles `Pod` exactes pour séparer le contexte du corps de preuve inline. Le runtime Agave reste la source normative pour la sélection entre preuve inline et preuve stockée dans un compte, les positions de comptes optionnels et l’acceptation des octets suffixes de `CloseContextState`. + +Aucune dépendance à `bytemuck` n’est ajoutée directement : le décodeur ne convertit pas les octets en preuve cryptographique et n’appelle aucun vérificateur. Il utilise uniquement `size_of` sur les types officiels, puis conserve longueur, SHA-256 et préfixe borné. Le contenu d’un compte de preuve externe n’est pas récupéré par RPC pendant un replay historique. + +### ZK Token Proof historique + +`kb_decoder_solana_core` ne dépend plus de `solana-zk-token-sdk`. Cette crate historique est dépréciée et entraînait `bincode` transitivement. Le workspace conserve une table wire locale limitée aux tags `0..16`, aux tailles exactes de contexte/preuve et à l’offset `u32 LE`, auditée contre la version historique `3.1.14`; aucun code de preuve ni sérialiseur historique n’est recopié. + +Le comportement est documenté depuis deux références runtime distinctes : Agave `v2.0.0` pour le vérificateur historique et Agave `v4.1.1` pour le stub actuel sans effet. Aucun corpus mainnet n’ayant été observé, les tests sont synthétiques et n’exigent pas de backfill. + +### Stake Program + +`solana-stake-interface 4.3.1` publie `StakeInstruction` avec Serde et conserve ses builders derrière la feature `bincode`. La feature `wincode` fournit les schémas nécessaires aux types d’état, mais pas directement à l’enum d’instruction. Le décodeur et l’exécuteur consomment donc l’ID et les types sémantiques officiels, avec un miroir wire privé commun dans son ordre de variantes et de champs. Aucun helper `bincode` n’est activé. + +L’exécuteur reproduit les vingt-quatre helpers clients actuels : initialize/create, checked/seed, split/merge, create-and-delegate, quatre authorize, delegate, withdraw, deactivate, lockup, minimum delegation, delinquent deactivation et move. Les helpers `redelegate` et `redelegate_with_seed` sont explicitement exclus parce que la source officielle les marque dépréciés et « will not be enabled » ; ils restent uniquement dans le miroir historique du décodeur. + +Le contrat des comptes est aligné sur les builders officiels. Les contrôles stateful du processor BPF — état de compte, rent, epochs, compatibilité et autorités courantes — restent à l’orchestration localnet/devnet. + +### Config Program + +`solana-config-interface 2.x` expose son module d’instruction derrière sa feature `bincode`. Le décodeur et l’exécuteur conservent donc un wire local borné pour `ConfigKeys` : compact-u16 canonique, booléens stricts, ordre exact des signataires, rejet des doublons et payload métier opaque. Le builder de création reproduit la taille `max_config_space + serialized_size(ConfigKeys)` et l’initialisation `ConfigKeys vide + T::default()` à partir d’octets déjà sérialisés ; il n’active aucun helper `bincode`. + +### BPF Loader v2 + +`solana-loader-v2-interface 3.x` publie ses helpers d’instruction derrière `bincode`. Le décodeur conserve le layout local vérifié tant qu’aucun schéma officiel `wincode` n’est disponible. + +### Feature Gate + +`solana-feature-gate-interface 4.x` publie la séquence d’activation `transfer -> allocate -> assign` et `RevokePendingActivation`, mais les helpers restent derrière sa feature `bincode`. L’exécuteur utilise `Feature::size_of()` et reproduit ces instructions à partir des interfaces System étroites ; la révocation reste exactement `vec![0]` avec feature, incinerator et System Program dans l’ordre officiel. + +### Slashing Program + +La source normative est SIMD-0204 et le contrat du programme Slashing publié : deux instructions, un payload DuplicateBlockProof de 305 octets, un report PDA dérivé de `node_pubkey + slot LE + type`, et une instruction Ed25519 précédente dont les offsets pointent vers le payload Slashing. Aucune crate de sérialisation générale n’est nécessaire. L’exécuteur construit le plan atomique et laisse au pipeline stateful le compte de preuve, le rent et les fenêtres epoch. + +### ZK ElGamal Proof dans l’exécuteur + +`kb_executor_solana_core` dépend directement de `solana-zk-elgamal-proof-interface ^0.1` pour les types POD et leurs tailles. Les layouts d’instruction sont simples et officiels : discriminant + preuve inline, ou discriminant + offset `u32 LE`; les comptes de contexte suivent `ContextStateInfo`. La fermeture reproduit le builder officiel. Aucune vérification cryptographique n’est recalculée côté client. + +### Loaders v3 et v4 + +La migration Loader v3 est terminée dans `pre.020` : le décodeur utilise `wincode::deserialize_exact` et l’exécuteur utilise les helpers officiels de `solana-loader-v3-interface 8.x`. Les payloads tronqués, suffixés, les longueurs et les contrats de comptes restent bornés par les tests. + +Loader v4 conserve un parser et un constructeur locaux limités aux sept discriminants publiés. Le layout est suffisamment petit pour être audité explicitement : tag `u32 LE`, `Write { offset, Vec }`, `Copy { destination_offset, source_offset, length }`, `SetProgramLength { new_size }` et quatre variantes unitaires. Cette exception évite d’activer `bincode` uniquement pour appeler les helpers de l’interface. + +## `solana-sdk` + +`solana-sdk` avec la feature `full` peut rester disponible au niveau workspace pour les applications ou outils qui ont réellement besoin de l’agrégat complet. Les crates feuille doivent préférer les interfaces étroites afin de réduire les features transitives, les temps de compilation et les risques de dépendances inutilisées. + +## Vérification avant ajout + +Pour chaque nouvelle interface : + +1. lire ses features et ses modules réellement compilés ; +2. identifier le format officiel de sérialisation ; +3. confirmer la version compatible avec Agave ciblée ; +4. ajouter la dépendance uniquement à la crate propriétaire ; +5. créer une fixture depuis l’encodeur officiel lorsque disponible ; +6. tester les payloads tronqués, inconnus, suffixés et les comptes invalides ; +7. documenter toute divergence entre l’interface et le runtime. + +### Metaplex Token Metadata — audit et premier décodage `0.4.7-pre.002` + +Le catalogue workspace déclare `mpl-token-metadata ^5.1` avec la seule feature `serde`. La publication officielle résolue reste `5.1.1`; elle fournit les modules générés `accounts`, `instructions`, `types` et `errors` issus de l’IDL Metaplex, sans introduire Anchor. La crate officielle utilise Borsh `< 1.0`; le décodeur référence donc explicitement `borsh 0.10` sous l’alias workspace `borsh_0_10`, distinct de Borsh 1.x utilisé par d’autres interfaces du workspace. + +Le dépôt officiel `metaplex-foundation/mpl-token-metadata` confirme le Program ID `metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s`. L’IDL officiel audité porte la version `1.14.0` et le blob `5df4a24f62c2743125be096cc174680790c92c18`; l’inventaire Rust généré des instructions porte le blob `3c42eec629f82e44ba690a7c4bd2177cc78f1d49`. + +`kb_decoder_metadata_metaplex_token_metadata` implémente désormais `InstructionDecoder` pour `CreateMetadataAccountV3` et `UpdateMetadataAccountV2`. Les arguments sont lus avec les types Borsh officiels et projetés avec la feature `serde`; les payloads, chaînes, créateurs, frais, comptes et suffixes sont bornés. L’enregistrement dans le registre runtime reste différé jusqu’à validation de ce premier groupe. Les metadata Token-2022 incorporées, Metaplex Core, Bubblegum et le JSON externe restent des provenances ou composants distincts. + diff --git a/docs/SOLANA_NATIVE_RPC_CLOSURE_AUDIT.md b/docs/SOLANA_NATIVE_RPC_CLOSURE_AUDIT.md new file mode 100644 index 0000000..5341fe4 --- /dev/null +++ b/docs/SOLANA_NATIVE_RPC_CLOSURE_AUDIT.md @@ -0,0 +1,106 @@ + + + +# Audit de clôture native, exécution et RPC — `0.4.2` + +## Décision + +Le jalon `0.4.2` est clôturé. L’audit distingue quatre contrats indépendants : + +1. les opérations futures constructibles par `kb_executor_solana_core` ; +2. les instructions passées décodables par `kb_decoder_solana_core` ; +3. les méthodes standard HTTP/WebSocket exposées par `kb_rpc` ; +4. l’orchestration de sécurité, de simulation, d’envoi et de replay post-exécution. + +Une méthode transportable via JSON brut n’est pas considérée comme un adaptateur typé. Un builder stateless n’autorise pas à lui seul une opération dépendant d’un compte, d’une autorité, du rent, d’un slot ou d’un epoch. Une opération dangereuse peut être complète dans la bibliothèque tout en restant absente de l’UI et désactivée sur Mainnet. + +## Résultat synthétique + +| Domaine | Inventaire canonique | Contrat final `0.4.2` | +|-------------------------|-------------------------------------------------------------------------:|------------------------------------------------------------------------------------------------| +| Exécuteur natif | 18 surfaces, 14 appelables, 4 non invocables/historiques, 109 opérations | égalité imposée par test Rust ; builders comparés aux interfaces ou layouts officiels | +| Décodeur natif | 18 surfaces, 121 déclarations | égalité imposée avec `kb_program_ids::native_program_ids()` et `SolanaCoreDecoder::coverage()` | +| HTTP JSON-RPC | 52 méthodes | 52 contrats typés configurables, 0 méthode standard limitée au JSON brut | +| WebSocket JSON-RPC | 9 paires / 18 méthodes | 9 requêtes, 9 notifications et 9 runtimes persistants typés | +| Préflight stateful | ALT, Config, Feature, Slashing, ZK ElGamal | rapports `NotRequired` / `Ready` / `Blocked`, Localnet/Devnet uniquement | +| Parcours mutable validé | System transfer Devnet | simulation, signature, envoi, confirmation, canonical, core et decode replay réussis | + +## Exécuteur Solana Core + +`docs/NATIVE_SOLANA_EXECUTION_MATRIX.json` et `SOLANA_CORE_OPERATION_CODES` imposent : + +- exactement 18 surfaces ; +- exactement 14 surfaces appelables et 4 surfaces sans instruction client ; +- exactement 109 codes d’opération uniques ; +- Program IDs présents dans `kb_program_ids` ; +- contrat de construction, politique, tests, validation cluster et décision UI pour chaque opération ; +- distribution des préflights `27 none / 14 required / 68 future`. + +Les surfaces sans builder client sont BPF Loader v1, BPF Loader v2, Native Loader et l’ancien ZK Token Proof Program. Stake `Redelegate` reste historique `decode-only`. Cette classification évite d’inventer des instructions qui ne sont plus ou n’ont jamais été appelables par un client moderne. + +Les entrées `future` ne signifient pas « non implémenté ». Elles signifient que le wire et le plan sont couverts offline, mais que l’activation mutable exige encore une preuve stateful par scénario sur Localnet/Devnet. Aucune de ces opérations n’est activée sur Mainnet par `0.4.2`. + +## Décodeur Solana Core + +`docs/NATIVE_SOLANA_DECODER_MATRIX.json` couvre exactement les 18 surfaces du registre natif et 121 déclarations. Les tests de complétude parcourent les enums officielles ou les tables wire auditées pour System, Compute Budget, ALT, Stake, Vote, loaders, précompiles, Slashing et preuves ZK. + +Stake Config reste un compte bien connu non exécutable. Il ne possède ni dispatch d’instruction ni plan d’exécution. + +## HTTP JSON-RPC standard + +`docs/SOLANA_STANDARD_RPC_MATRIX.json` et `STANDARD_HTTP_METHODS` enregistrent 52 méthodes. Chacune possède un contrat de requête/résultat propre à `kb_rpc`. Les options facultatives restent indépendantes : commitment, `minContextSlot`, encodage, `dataSlice`, filtres, contexte, tri, niveau de détail, rewards et version maximale de transaction. + +Les DTO Agave ne font pas partie de l’API publique. La voie JSON brute reste une primitive interne ou d’extension fournisseur, pas un substitut à une méthode standard absente. + +## WebSocket JSON-RPC standard + +Les neuf paires standard possèdent paramètres, notifications et runtime persistant dans `kb_rpc`. `WsSession` fournit : + +- une socket multiplexée ; +- des identifiants locaux stables et distants remappables ; +- unsubscribe explicite ; +- timeouts bornés ; +- ping/pong et fermeture ; +- reconnexion exponentielle bornée ; +- réabonnement ; +- suppression terminale de `signatureSubscribe`. + +`blockSubscribe`, `slotsUpdatesSubscribe` et `voteSubscribe` sont désactivées par défaut, mais activables lorsqu’un endpoint les annonce explicitement. La première souscription réelle sert de probe paresseux. Une réponse `Method not found`, `not enabled`, `unavailable` ou `unsupported` désactive uniquement la capacité concernée pour la session ; les erreurs de paramètres, d’authentification ou de rate limit ne la désactivent pas. + +## Frontière Tauri et TS-rs + +Les payloads actifs de `kb_app_demo` et `kb_config` transmis par JSON utilisent des `number`, pas des `bigint`. Les valeurs d’identifiants sont contrôlées avec `Number.isSafeInteger` avant invocation. Les types génériques hors frontière Tauri conservent leurs contrats exacts lorsqu’ils ne sont pas sérialisés par `JSON.stringify`. + +Le navigateur SQL applique maintenant la limite maximale reçue du backend avant l’appel Tauri. Une valeur supérieure à 5 000 produit un diagnostic frontend et n’atteint plus PostgreSQL. + +## Preuves de validation + +Validations fournies le 13 juillet 2026 : + +| Crate / contrôle | Résultat | +|------------------------------------|----------:| +| `kb_program_ids` | 5 tests | +| `kb_decoder_solana_core` | 116 tests | +| `kb_executor_solana_core` | 88 tests | +| `kb_execution_api` | 22 tests | +| `kb_execution_safety` | 15 tests | +| `kb_execution_solana` | 12 tests | +| `kb_rpc` | 113 tests | +| `kb_pipeline` | 56 tests | +| `kb_config` | 41 tests | +| `kb_app_demo` | 88 tests | +| `kb_store_pg` avec PostgreSQL réel | 45 tests | +| `cargo clippy --all-targets` | propre | + +Le test Devnet opt-in a exécuté un transfert de 1 000 000 lamports avec airdrop nul depuis un wallet préfinancé. Il a confirmé simulation, signature, envoi, confirmation, hydratation canonique, extraction core et decode replay. `cargo tauri dev` a démarré Vite, initialisé les treize tables attendues et exercé les sessions WebSocket. + +L’archive de logs Mainnet/Tauri confirme des unsubscribe réels pour `slot`, `root` et `program`. Le refus de sélectionner un autre endpoint tant qu’une session est active est un garde-fou volontaire. L’ancien échec `JSON.stringify cannot serialize BigInt` n’est plus présent. + +## Limites conservées après clôture + +- Mainnet reste désactivé par défaut et exige une politique explicite. +- Les opérations stateful administratives non exercées sur cluster restent hors UI et bloquées avant toute future activation mutable. +- Le parcours validé utilise une transaction legacy avec recent blockhash ; durable nonce et transactions v0 sont couverts par les bibliothèques mais nécessitent leurs propres parcours opérateur avant exposition. +- Les méthodes WebSocket instables dépendent du nœud et peuvent être désactivées automatiquement à l’exécution. + +La clôture de `0.4.2` n’ouvre aucune version suivante et ne fixe aucun plan de sources historiques externes. diff --git a/docs/SOLANA_STANDARD_RPC_MATRIX.json b/docs/SOLANA_STANDARD_RPC_MATRIX.json new file mode 100644 index 0000000..440df20 --- /dev/null +++ b/docs/SOLANA_STANDARD_RPC_MATRIX.json @@ -0,0 +1,430 @@ +{ + "file": "docs/SOLANA_STANDARD_RPC_MATRIX.json", + "version": 3, + "release": "0.4.2-pre.025", + "canonical_reference": "https://solana.com/docs/rpc", + "http_method_count": 52, + "http_typed_adapter_count": 52, + "http_raw_json_count": 0, + "ws_subscription_pair_count": 9, + "ws_method_count": 18, + "ws_unstable_subscription_count": 3, + "http_methods": [ + { + "method": "getAccountInfo", + "category": "accounts", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getBalance", + "category": "accounts", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getLargestAccounts", + "category": "accounts", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getMinimumBalanceForRentExemption", + "category": "accounts", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getMultipleAccounts", + "category": "accounts", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getProgramAccounts", + "category": "accounts", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getTokenAccountBalance", + "category": "tokens", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getTokenAccountsByDelegate", + "category": "tokens", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getTokenAccountsByOwner", + "category": "tokens", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getTokenLargestAccounts", + "category": "tokens", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getTokenSupply", + "category": "tokens", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getFeeForMessage", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getLatestBlockhash", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getRecentPrioritizationFees", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getSignaturesForAddress", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getSignatureStatuses", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getTransaction", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getTransactionCount", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "isBlockhashValid", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "requestAirdrop", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "sendTransaction", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "simulateTransaction", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getBlock", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getBlockCommitment", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getBlockHeight", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getBlockProduction", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getBlocks", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getBlocksWithLimit", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getBlockTime", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getFirstAvailableBlock", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getRecentPerformanceSamples", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "minimumLedgerSlot", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getClusterNodes", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getEpochInfo", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getEpochSchedule", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getGenesisHash", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getHealth", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getHighestSnapshotSlot", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getIdentity", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getLeaderSchedule", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getMaxRetransmitSlot", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getMaxShredInsertSlot", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getSlot", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getSlotLeader", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getSlotLeaders", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getVersion", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getVoteAccounts", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getInflationGovernor", + "category": "economics", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getInflationRate", + "category": "economics", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getInflationReward", + "category": "economics", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getStakeMinimumDelegation", + "category": "economics", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getSupply", + "category": "economics", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + } + ], + "ws_subscriptions": [ + { + "subscribe_method": "accountSubscribe", + "unsubscribe_method": "accountUnsubscribe", + "notification_method": "accountNotification", + "stability": "stable", + "request_contract": "typed_adapter", + "persistent_runtime": true, + "library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription", + "notification_contract": "typed_adapter", + "explicit_capability_required": false + }, + { + "subscribe_method": "blockSubscribe", + "unsubscribe_method": "blockUnsubscribe", + "notification_method": "blockNotification", + "stability": "unstable", + "request_contract": "typed_adapter", + "persistent_runtime": true, + "library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription", + "notification_contract": "typed_adapter", + "explicit_capability_required": true + }, + { + "subscribe_method": "logsSubscribe", + "unsubscribe_method": "logsUnsubscribe", + "notification_method": "logsNotification", + "stability": "stable", + "request_contract": "typed_adapter", + "persistent_runtime": true, + "library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription", + "notification_contract": "typed_adapter", + "explicit_capability_required": false + }, + { + "subscribe_method": "programSubscribe", + "unsubscribe_method": "programUnsubscribe", + "notification_method": "programNotification", + "stability": "stable", + "request_contract": "typed_adapter", + "persistent_runtime": true, + "library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription", + "notification_contract": "typed_adapter", + "explicit_capability_required": false + }, + { + "subscribe_method": "rootSubscribe", + "unsubscribe_method": "rootUnsubscribe", + "notification_method": "rootNotification", + "stability": "stable", + "request_contract": "typed_adapter", + "persistent_runtime": true, + "library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription", + "notification_contract": "typed_adapter", + "explicit_capability_required": false + }, + { + "subscribe_method": "signatureSubscribe", + "unsubscribe_method": "signatureUnsubscribe", + "notification_method": "signatureNotification", + "stability": "stable", + "request_contract": "typed_adapter", + "persistent_runtime": true, + "library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription", + "notification_contract": "typed_adapter", + "explicit_capability_required": false + }, + { + "subscribe_method": "slotSubscribe", + "unsubscribe_method": "slotUnsubscribe", + "notification_method": "slotNotification", + "stability": "stable", + "request_contract": "typed_adapter", + "persistent_runtime": true, + "library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription", + "notification_contract": "typed_adapter", + "explicit_capability_required": false + }, + { + "subscribe_method": "slotsUpdatesSubscribe", + "unsubscribe_method": "slotsUpdatesUnsubscribe", + "notification_method": "slotsUpdatesNotification", + "stability": "unstable", + "request_contract": "typed_adapter", + "persistent_runtime": true, + "library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription", + "notification_contract": "typed_adapter", + "explicit_capability_required": true + }, + { + "subscribe_method": "voteSubscribe", + "unsubscribe_method": "voteUnsubscribe", + "notification_method": "voteNotification", + "stability": "unstable", + "request_contract": "typed_adapter", + "persistent_runtime": true, + "library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription", + "notification_contract": "typed_adapter", + "explicit_capability_required": true + } + ], + "ws_typed_request_count": 9, + "ws_typed_notification_count": 9, + "ws_persistent_runtime_count": 9 +} diff --git a/docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json b/docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json new file mode 100644 index 0000000..646a034 --- /dev/null +++ b/docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json @@ -0,0 +1,199 @@ +{ + "file": "docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json", + "version": 9, + "schemaVersion": 1, + "programId": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", + "systemProgramId": "11111111111111111111111111111111", + "supportedTokenPrograms": [ + {"code": "spl_token", "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "scope": "classic_token_account_lifecycle_only"}, + {"code": "spl_token_2022", "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", "scope": "ata_lifecycle_only_without_general_token_2022_instruction_or_extension_decode"} + ], + "interface": { + "workspaceConstraint": "^2.0", + "resolvedVersion": "2.0.0", + "resolutionEvidence": "docs_rs_latest_published_version_and_official_interface_tag", + "workspaceCargoResolutionStatus": "confirmed_by_operator_cargo_tree_2026-07-16", + "features": ["borsh"], + "bincodeEnabled": false, + "officialTag": "interface@v2.0.0", + "releaseCommit": "250da0e21a9853a7afddba85658ed8a9119c71c9", + "releaseDate": "2025-08-08", + "instructionSource": "https://github.com/solana-program/associated-token-account/blob/interface@v2.0.0/interface/src/instruction.rs", + "addressSource": "https://github.com/solana-program/associated-token-account/blob/interface@v2.0.0/interface/src/address.rs", + "publishedDocumentation": "https://docs.rs/spl-associated-token-account-interface/2.0.0/spl_associated_token_account_interface/" + }, + "processor": { + "auditedRelease": "program@v8.0.0", + "releaseCommit": "0b867b5", + "releaseDate": "2025-10-29", + "source": "https://github.com/solana-program/associated-token-account/blob/program@v8.0.0/program/src/processor.rs", + "deploymentBinaryEquality": {"localnet": "not_tested", "devnet": "not_proven", "mainnet": "not_proven"}, + "note": "Interface publication, processor source, cluster deployment and successful execution are tracked independently." + }, + "wire": { + "format": "Borsh enum with unit variants encoded as one u8 ordinal", + "strictSuffixPolicy": "reject", + "legacyEmptyCreate": {"decodeSupport": true, "builderEmits": false, "encodingHex": "", "status": "historical_runtime_and_transaction_parser_compatibility_to_be_reconfirmed_by_cluster_corpus"}, + "unknownOrTruncatedPolicy": "bounded diagnostic without lifecycle projection" + }, + "pdaDerivation": { + "programId": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", + "seedOrder": ["wallet_address_bytes[32]", "token_program_id_bytes[32]", "mint_address_bytes[32]"], + "algorithm": "Pubkey::find_program_address", + "validationPolicy": "preserve observed address and report exact match or mismatch; never replace an observed account silently", + "differentialVectors": [ + {"wallet": "11111111111111111111111111111111", "mint": "So11111111111111111111111111111111111111112", "tokenProgramId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "expectedAta": "aqxoAhCwpy3oB1BpNw9hL1HdLYLgPpbPjzxDrrQj3Fs", "expectedBump": 254, "evidence": "PublicKey.findProgramAddressSync with the canonical official seed order"}, + {"wallet": "11111111111111111111111111111111", "mint": "So11111111111111111111111111111111111111112", "tokenProgramId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", "expectedAta": "2sZUUBGq1i6aE47ZoxCaCW89jmYm2EXLPPmNMgMDXHMS", "expectedBump": 250, "evidence": "PublicKey.findProgramAddressSync with the canonical official seed order"} + ] + }, + "limits": {"exactVariantCount": 3, "createRequiredAccountCount": 6, "recoverNestedRequiredAccountCount": 7, "maximumDiagnosticBytes": 256, "extraAccounts": "preserved_and_reported; never assigned an invented role"}, + "executionPreflight": { + "clusters": ["localnet", "devnet"], + "programIds": ["associated_token_program", "selected_token_program", "system_program_for_creation"], + "stateChecks": ["mint_exists_initialized_and_owned_by_selected_token_program", "canonical_pda_derivation", "strict_create_absence", "idempotent_absence_or_existing_account_compatibility", "recover_nested_three_account_relationships", "required_signers_available", "payer_balance_covers_rent_plus_fee_ceiling"], + "token2022Boundary": "base Mint and Token Account prefixes are validated while suffix extensions remain opaque; simulation determines extension-dependent runtime acceptance and exact account size", + "simulation": "required_before_signing_or_sending", + "defaultMode": "dry_run" + }, + "surfaceEquality": {"matrixVariantCount": 3, "officialInterfaceVariantCount": 3, "compiledDecoderCoverageStatus": "three_exact_compiled_instruction_declarations_operator_validated", "compiledExecutorCapabilityStatus": "three_exact_current_capabilities_operator_validated", "requiredFinalInvariant": "matrix == official interface enum == decoder coverage == executor capability declarations"}, + "instructions": [ + { + "name": "create", + "discriminant": 0, + "canonicalEncodingHex": "00", + "acceptedDecodeEncodingsHex": ["", "00"], + "status": "current_with_legacy_empty_wire_compatibility", + "accounts": [ + {"position": 0, "role": "funding_account", "signer": true, "writable": true, "expectedProgram": "system_account"}, + {"position": 1, "role": "associated_token_account", "signer": false, "writable": true, "expectedProgram": "derived_pda_before_creation_or_supported_token_program_after_creation"}, + {"position": 2, "role": "wallet_owner", "signer": false, "writable": false, "expectedProgram": "unconstrained_wallet_address"}, + {"position": 3, "role": "mint", "signer": false, "writable": false, "expectedProgram": "account_owned_by_token_program_at_runtime"}, + {"position": 4, "role": "system_program", "signer": false, "writable": false, "expectedAddress": "11111111111111111111111111111111"}, + {"position": 5, "role": "token_program", "signer": false, "writable": false, "allowedAddresses": ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"]} + ], + "addressRules": ["associated_token_account = PDA(wallet_owner, token_program, mint)", "observed associated_token_account is retained on mismatch"], + "tokenProgramDifferences": {"classic": "create and initialize a classic token account", "token2022": "size is obtained from the Token-2022 mint and immutable-owner initialization is performed before account initialization; extensions themselves remain out of decoder scope"}, + "runtimeRules": ["fails if the associated account already exists", "funding account pays rent and creation cost", "mint and created account must be coherent with the selected token program"], + "event": "spl_associated_token_account.create", + "authorizedProjections": ["token_accounts:ata_created"], + "forbiddenProjections": ["token_balance_snapshot", "duplicate_spl_token_initialize_account_cpi", "token_2022_extension_fact"], + "officialBuilder": {"available": true, "function": "create_associated_token_account", "canonicalWireOnly": true}, + "executorSupport": {"status": "supported", "operationCode": "spl_associated_token_account.create", "builder": "create_associated_token_account", "simulation": "required", "defaultMode": "dry_run"}, + "proofs": {"synthetic": ["canonical_00", "legacy_empty", "classic_token_program", "token_2022_program", "wrong_ata", "missing_account", "extra_account", "duplicate_account", "wrong_flags", "unknown_suffix", "outer", "inner_cpi", "committed", "failed_intent"], "localnet": "not_tested", "devnet": "not_tested", "mainnetSignatures": []} + }, + { + "name": "create_idempotent", + "discriminant": 1, + "canonicalEncodingHex": "01", + "acceptedDecodeEncodingsHex": ["01"], + "status": "current", + "accounts": [ + {"position": 0, "role": "funding_account", "signer": true, "writable": true, "expectedProgram": "system_account"}, + {"position": 1, "role": "associated_token_account", "signer": false, "writable": true, "expectedProgram": "derived_pda_or_supported_token_program"}, + {"position": 2, "role": "wallet_owner", "signer": false, "writable": false}, + {"position": 3, "role": "mint", "signer": false, "writable": false}, + {"position": 4, "role": "system_program", "signer": false, "writable": false, "expectedAddress": "11111111111111111111111111111111"}, + {"position": 5, "role": "token_program", "signer": false, "writable": false, "allowedAddresses": ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"]} + ], + "addressRules": ["associated_token_account = PDA(wallet_owner, token_program, mint)", "existing account must retain the selected token program, wallet owner and mint"], + "tokenProgramDifferences": {"classic": "existing classic account is accepted only when owner and mint match", "token2022": "existing Token-2022 account is accepted only when owner and mint match; no extension semantics are inferred"}, + "runtimeRules": ["creates when absent", "succeeds without recreating when an initialized compatible ATA already exists", "fails when an existing account has a different wallet owner, mint or account owner program"], + "event": "spl_associated_token_account.create_idempotent", + "authorizedProjections": ["token_accounts:ata_created_or_reused_idempotently"], + "forbiddenProjections": ["token_balance_snapshot", "duplicate_spl_token_initialize_account_cpi", "token_2022_extension_fact"], + "officialBuilder": {"available": true, "function": "create_associated_token_account_idempotent", "canonicalWireOnly": true}, + "executorSupport": {"status": "supported", "operationCode": "spl_associated_token_account.create_idempotent", "builder": "create_associated_token_account_idempotent", "simulation": "required", "defaultMode": "dry_run"}, + "proofs": {"synthetic": ["canonical_01", "absent_account", "compatible_existing_account", "conflicting_existing_owner", "conflicting_existing_mint", "conflicting_existing_program", "classic_token_program", "token_2022_program", "wrong_ata", "wrong_flags", "unexpected_suffix", "outer", "inner_cpi", "committed", "failed_intent"], "localnet": "not_tested", "devnet": ["5Vw4QFpMa8oJ8MsLrvDg4RFzxqUh27mTFRFzzwJFoA4uAry8rZQc6GP92aidzWugn3CeZ5NYNxDp7tZ6bh8XyN1P", "2H9VfhDySdXMteazm2P3s2fTttJBQzMJinQtKqsLtkjDg7zbFFgiWG81UxRQF4va3bbR2pUV4QGo2qErisR8CPbP", "3wWTkx45LoDR3UaU8VUfsWxwHuLFyJyhrnDrmijCa7pXPdmJKz3rQ1LdyDvb78dvNRiKqw5ee7HuZ6Gjj1Gpfv6q", "bMMjoHcwbaTvLXerdQ4kbGXGb7wydiTyvSEwwmoKKLQcRWWBzqGUMYHZztonNnER1iWZ7yKp9p3neeMhypX4QAJ", "4RZKA3KwEbSqgU8UnemTgvd3fBgJmoSK2nmaaTNP8nJGTVh6DrFwrHVZYCBvLFvhvzUdi9p1KRFe4hHqYt7JbVSG"], "mainnetSignatures": ["2F9RvuCkevfJN7ju252hAWoCE58wagCXCWHQF9Wsdtsxvp3HcuNJQFCwU7zGqe3z3b8pv3vPp4xY5EH7KLcaHQCY"], "realCorpusStatus": "controlled classic and Token-2022 CreateIdempotent creation then compatible-existing reuse confirmed, materialized once per signature and replayed idempotently; search-discovered Token-2022 outer transaction remains additional corpus"} + }, + { + "name": "recover_nested", + "discriminant": 2, + "canonicalEncodingHex": "02", + "acceptedDecodeEncodingsHex": ["02"], + "status": "current_recovery_operation", + "accounts": [ + {"position": 0, "role": "nested_associated_token_account", "signer": false, "writable": true}, + {"position": 1, "role": "nested_mint", "signer": false, "writable": false}, + {"position": 2, "role": "wallet_nested_mint_associated_token_account", "signer": false, "writable": true}, + {"position": 3, "role": "owner_associated_token_account", "signer": false, "writable": false}, + {"position": 4, "role": "owner_mint", "signer": false, "writable": false}, + {"position": 5, "role": "wallet_owner", "signer": true, "writable": true}, + {"position": 6, "role": "token_program", "signer": false, "writable": false, "allowedAddresses": ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"]} + ], + "addressRules": ["owner_associated_token_account = PDA(wallet_owner, token_program, owner_mint)", "nested_associated_token_account = PDA(owner_associated_token_account, token_program, nested_mint)", "wallet_nested_mint_associated_token_account = PDA(wallet_owner, token_program, nested_mint)"], + "tokenProgramDifferences": {"classic": "transfer the complete nested balance and close the nested classic token account", "token2022": "same ATA address relationships through Token-2022 CPIs; extension-specific transfer or close restrictions can prevent recovery and are not decoded as ATA facts"}, + "runtimeRules": ["all three derived addresses must match their observed accounts", "owner and nested token accounts must be initialized and coherent with their respective mints", "wallet owner must sign", "complete nested token balance is moved to the wallet destination ATA", "nested account is closed and its lamports are returned to the wallet"], + "event": "spl_associated_token_account.recover_nested", + "authorizedProjections": ["token_accounts:nested_ata_recovered", "risk:nested_ata_anti_pattern_recovered"], + "forbiddenProjections": ["duplicate_spl_token_transfer_cpi", "duplicate_spl_token_close_account_cpi", "token_balance_snapshot", "token_2022_extension_fact"], + "officialBuilder": {"available": true, "function": "recover_nested", "canonicalWireOnly": true}, + "executorSupport": {"status": "supported", "operationCode": "spl_associated_token_account.recover_nested", "builder": "recover_nested", "simulation": "required", "defaultMode": "dry_run"}, + "proofs": {"synthetic": ["canonical_02", "classic_token_program", "token_2022_program", "valid_three_pda_relationships", "wrong_nested_ata", "wrong_destination_ata", "wrong_owner_ata", "owner_and_nested_mints_not_inverted", "missing_account", "extra_account", "duplicate_account", "wrong_flags", "unexpected_suffix", "outer", "inner_cpi", "committed", "failed_intent"], "localnet": "not_tested", "devnet": ["37LG6GdMRp5ECG8qf1RSYxmriJk75UtCwqwzXZWpArebkKo6BQvGhYEnbDh2A6KWiUaHK28Zy1gGGZ5zihvrHskM"], "mainnetSignatures": [], "realCorpusStatus": "controlled classic RecoverNested confirmed with positive raw balance transfer, nested ATA closure, owner and destination post-validation, two distinct ATA lifecycle and risk projections, and idempotent second replay", "controlledDevnetFixture": {"walletOwner": "DwuAapPLg6pEJ4QhmxvPg8rzkavmYdg5FW4dfEMcg8B2", "ownerMint": "So11111111111111111111111111111111111111112", "ownerAssociatedTokenAccount": "3tdXq1W7vEbbkxQmT1r2fc349uYti98zpwLKhp7FFbF5", "nestedMint": "3fd5CqP1w4Y1xeMoAe3apA7Jf5SbL3vboxv7ihyXhC9A", "nestedAssociatedTokenAccount": "AAyvGeKpq6i3kxMcScJLcN2u5iyAsspr731LhgXygti5", "walletNestedMintAssociatedTokenAccount": "69TP2QoSx2inueFAEb6npjumH5o3wGJQDSwwAJLLfyWp", "rawAmountTransferred": "1000000000", "nestedAccountClosed": true, "destinationUiBalanceAfter": "1", "materializedOutputCount": 2}} + } + ], + "offlineCorpus": { + "status": "implemented_and_operator_validated_in_decoder_unit_tests", + "wire": ["create_00", "legacy_empty_create", "create_idempotent_01", "recover_nested_02", "unknown_tag", "rejected_suffix"], + "tokenPrograms": ["spl_token_classic", "token_2022"], + "locations": ["outer", "inner_cpi"], + "transactionOutcomes": ["committed", "failed_uncommitted_intent"], + "accountShapes": ["canonical", "missing", "extra", "duplicate", "wrong_flags", "wrong_derived_address", "unsupported_token_program", "wrong_system_program"], + "derivation": ["create_single_pda", "recover_nested_three_independent_pda_relationships", "official_helper_differential_vectors"], + "limits": "diagnostic count and payload prefix are bounded; no arbitrary payload or account repair" + }, + "materializationContract": { + "committedOnly": true, + "idempotenceIdentity": "signature + instruction_path + program_id + normalized_operation + ata", + "projectionOwners": [ + { + "crate": "kb_materializer_token_accounts", + "facts": [ + "ata_created", + "ata_created_or_reused_idempotently", + "nested_ata_recovered" + ], + "justification": "ATA creation and recovery are stable token-account lifecycle facts; this crate already owns instruction-level token-account mutations." + }, + { + "crate": "kb_materializer_risk", + "facts": [ + "nested_ata_anti_pattern_recovered" + ], + "justification": "A committed RecoverNested proves the distinct stable risk fact that a nested ATA anti-pattern existed and was recovered; no score or speculative severity is added." + } + ], + "explicitNoProjection": [ + { + "crate": "kb_materializer_lifecycle", + "reason": "Native program lifecycle remains owned here; duplicating ATA lifecycle would overlap kb_materializer_token_accounts." + }, + { + "crate": "kb_materializer_admin", + "reason": "ATA instructions do not change a mint authority, freeze authority, close authority or multisig configuration." + }, + { + "crate": "kb_materializer_fees", + "reason": "ATA wire does not prove an exact rent or network-fee amount; core balance changes and execution validation retain those facts." + } + ], + "implementationStatus": "pre_004 executor and stateful preflight operator validated; pre_005 classic and Token-2022 CreateIdempotent Devnet orchestration plus Tauri validated; pre_006 classic RecoverNested Devnet orchestration validated", + "idempotentOutcomeLimit": "CreateIdempotent parent proves successful ensure semantics but cannot distinguish created from reused without sibling CPI correlation; the projection preserves created_or_reused and invents neither boolean.", + "parentChildOwnership": "ATA parent owns only associated-address lifecycle and the distinct nested-ATA risk fact; SPL Token or Token-2022 CPI observations own token initialize, transfer and close mutations.", + "noSnapshotClaim": true, + "databaseMigrationStatus": "not_justified" + }, + "executionContract": {"simulationRequired": true, "dryRunDefault": true, "clusterPolicyOwnedByCommonLayers": true, "rentAndFeeCeilingRequired": true, "signerDeduplicationDoesNotChangeMetaOrder": true, "confirmedStateReadRetryBound": 20, "canonicalHydrationRpcRetryBoundPerAttempt": 2, "postValidation": {"create": "ATA exists, is owned by selected token program, and contains expected wallet owner and mint", "createIdempotent": "same invariant whether the ATA was created or reused", "recoverNested": "owner ATA and destination remain coherent, nested ATA is absent, and no balance value is invented by ATA projection"}}, + "deploymentAudit": { + "localnet": {"status": "not_tested", "evidence": []}, + "devnet": {"status": "classic and Token-2022 CreateIdempotent creation and compatible-existing reuse confirmed; classic RecoverNested positive-balance recovery confirmed", "evidence": ["5Vw4QFpMa8oJ8MsLrvDg4RFzxqUh27mTFRFzzwJFoA4uAry8rZQc6GP92aidzWugn3CeZ5NYNxDp7tZ6bh8XyN1P", "2H9VfhDySdXMteazm2P3s2fTttJBQzMJinQtKqsLtkjDg7zbFFgiWG81UxRQF4va3bbR2pUV4QGo2qErisR8CPbP", "3wWTkx45LoDR3UaU8VUfsWxwHuLFyJyhrnDrmijCa7pXPdmJKz3rQ1LdyDvb78dvNRiKqw5ee7HuZ6Gjj1Gpfv6q", "bMMjoHcwbaTvLXerdQ4kbGXGb7wydiTyvSEwwmoKKLQcRWWBzqGUMYHZztonNnER1iWZ7yKp9p3neeMhypX4QAJ", "4RZKA3KwEbSqgU8UnemTgvd3fBgJmoSK2nmaaTNP8nJGTVh6DrFwrHVZYCBvLFvhvzUdi9p1KRFe4hHqYt7JbVSG", "37LG6GdMRp5ECG8qf1RSYxmriJk75UtCwqwzXZWpArebkKo6BQvGhYEnbDh2A6KWiUaHK28Zy1gGGZ5zihvrHskM"], "controlledToken2022Fixture": {"mint": "3DxKAUfCZkR9oRfMXeRNpiVL464eKbTieymBrVhsoKTE", "mintOwner": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", "mintBaseLength": 82, "walletOwner": "DwuAapPLg6pEJ4QhmxvPg8rzkavmYdg5FW4dfEMcg8B2", "associatedTokenAccount": "6WfCJPt7GZQyMJAUS1AMHJrD7Vid8e9xUEQVgi5Q9C9Y", "accountLength": 170, "accountExtension": "immutable_owner", "creationSlot": 476702448, "existingReuseSlot": 476703343}}, + "mainnet": {"status": "search_discovered_token_2022_create_idempotent_not_yet_replayed", "evidence": ["2F9RvuCkevfJN7ju252hAWoCE58wagCXCWHQF9Wsdtsxvp3HcuNJQFCwU7zGqe3z3b8pv3vPp4xY5EH7KLcaHQCY"]}, + "rateLimitRecovery": "preserve confirmed signatures and resume bounded hydration; never recreate accounts blindly after HTTP 429" + }, + "nonClaims": [ + "No general Token-2022 instruction or extension decoding is implemented in 0.4.5.", + "No Token balance or final account snapshot is reconstructed from ATA instructions.", + "No cluster binary equality is inferred from interface publication.", + "The controlled Token-2022 proof covers ATA lifecycle only and does not claim general Token-2022 instruction or extension decoding.", + "No dedicated SQL table is justified by this audit tranche." + ] +} diff --git a/docs/SPL_ELGAMAL_REGISTRY_MATRIX.json b/docs/SPL_ELGAMAL_REGISTRY_MATRIX.json new file mode 100644 index 0000000..2320573 --- /dev/null +++ b/docs/SPL_ELGAMAL_REGISTRY_MATRIX.json @@ -0,0 +1,121 @@ +{ + "file": "docs/SPL_ELGAMAL_REGISTRY_MATRIX.json", + "version": 1, + "programId": "regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg", + "interface": { + "crate": "spl-elgamal-registry-interface", + "resolvedVersion": "0.2.1", + "auditedCommitPrefix": "e18f9c6" + }, + "technicalBoundaries": { + "token2022ProgramId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "zkElgamalProofProgramId": "ZkE1Gama1Proof11111111111111111111111111111", + "registryDecodesProofInstructions": false, + "token2022DecoderOwnsRegistryInstructions": false + }, + "pda": { + "seedUtf8": "elgamal-registry", + "ownerAddressIsSeed": true, + "programIdIsDerivationProgram": true + }, + "instructions": [ + { + "name": "create_registry", + "tag": 0, + "wire": { + "exactLengthBytes": 2, + "fields": [ + { + "name": "tag", + "type": "u8", + "value": 0 + }, + { + "name": "proofInstructionOffset", + "type": "i8" + } + ] + }, + "accounts": [ + { + "position": 0, + "role": "registry_account", + "writable": true, + "signer": false + }, + { + "position": 1, + "role": "wallet_owner", + "writable": false, + "signer": true + }, + { + "position": 2, + "role": "system_program", + "writable": false, + "signer": false + }, + { + "position": 3, + "role": "instructions_sysvar_or_proof_context", + "writable": false, + "signer": false + } + ], + "builder": "create_registry", + "status": "current" + }, + { + "name": "update_registry", + "tag": 1, + "wire": { + "exactLengthBytes": 2, + "fields": [ + { + "name": "tag", + "type": "u8", + "value": 1 + }, + { + "name": "proofInstructionOffset", + "type": "i8" + } + ] + }, + "accounts": [ + { + "position": 0, + "role": "registry_account", + "writable": true, + "signer": false + }, + { + "position": 1, + "role": "instructions_sysvar_or_proof_context", + "writable": false, + "signer": false + }, + { + "position": 2, + "role": "registry_owner", + "writable": false, + "signer": true + } + ], + "builder": "update_registry", + "status": "current" + } + ], + "proofLocationSemantics": { + "offsetZero": "context_state_account", + "offsetNonZero": "relative_zk_proof_instruction", + "publishedInlineBuilderOffset": 1 + }, + "decoder": { + "crate": "kb_decoder_spl_elgamal_registry", + "coverageEqualityRequired": true, + "unknownTagPolicy": "failed", + "trailingBytesPolicy": "failed", + "failedTransactionCommitted": false + } +} diff --git a/docs/SPL_MEMO_MATRIX.json b/docs/SPL_MEMO_MATRIX.json new file mode 100644 index 0000000..5a14217 --- /dev/null +++ b/docs/SPL_MEMO_MATRIX.json @@ -0,0 +1,266 @@ +{ + "file": "docs/SPL_MEMO_MATRIX.json", + "version": 8, + "matrixVersion": 4, + "scope": "SPL Memo v1, v3 et v4", + "interface": { + "crate": "spl-memo-interface", + "requestedVersion": "^2.1", + "auditedPublishedVersion": "2.1.0", + "lockfilePresentInInputArchive": false, + "builder": "spl_memo_interface::instruction::build_memo", + "builderAcceptsExplicitProgramId": true, + "wire": "octets exacts du message, sans préfixe ni discriminator", + "accountMetas": "ordre fourni, readonly, signer=true" + }, + "generations": [ + { + "generation": "v1", + "programId": "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo", + "surfaceCode": "spl_memo_v1", + "status": "historique immuable", + "instructionData": "octets UTF-8 bruts", + "emptyPayload": "accepte", + "accounts": "acceptes mais ignores par le runtime v1", + "signerRequirement": "aucune", + "accountOrder": "sans effet runtime mais conserve par le decodeur", + "duplicateAccounts": "sans effet runtime mais conserves par le decodeur", + "runtimeLogs": [], + "utf8Failure": "InvalidInstructionData", + "builderOfficial": "builder generique 2.1.0 avec program ID v1 explicite", + "currentInvocation": { + "mainnet": "transactions reelles observees et rejouees; aucun envoi execute dans cette tranche", + "devnet": "non verifie", + "localnet": "binaire historique non embarque" + }, + "realCorpusSignatures": [ + "imGSCgR83S3tFPTcZPqWLWRcL1piFX1vkYMRVsRdJeBM4p7viDb4V6Hq2x51ovTmbMgGBMdn8kyeNiAg9Ph6MfE", + "65J2cmiZAB5h7B3kyzhponNZQzMCbbxVn1A8cydvKKq1FYxYLEarG7ydjuuxFatXCG7Foju21XMUdRkT96jYBS3C" + ] + }, + { + "generation": "v3", + "programId": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr", + "surfaceCode": "spl_memo_v3", + "status": "historique encore largement observe", + "instructionData": "octets UTF-8 bruts", + "emptyPayload": "accepte", + "accounts": "zero ou plusieurs; chaque compte fourni doit etre signer", + "signerRequirement": "tous les comptes fournis", + "accountOrder": "ordre de parcours et de logs conserve", + "duplicateAccounts": "parcourus et journalises dans leur ordre; aucun rejet dedie", + "runtimeLogs": [ + "Signed by pour chaque compte signer", + "Invalid UTF-8, from byte en cas d'erreur", + "Memo (len ): en cas de succes" + ], + "utf8Failure": "InvalidInstructionData", + "missingSignerFailure": "MissingRequiredSignature avant validation UTF-8", + "builderOfficial": "builder generique 2.1.0 avec program ID v3 explicite", + "currentInvocation": { + "mainnet": "transactions reelles observees et rejouees; aucun envoi execute dans cette tranche", + "devnet": "non verifie", + "localnet": "possible avec binaire officiel, non execute" + }, + "realCorpusSignatures": [ + "2DkiJJVAyJ1aJHn2ff8JhKA5C8wbakw76LZvgxmcQ1qtHNDprGvA6TntXXK5FSAeRMZyrDt8ujAkGMgBVcEhjsmT", + "3G18ZUSP29Aw12NwnAYRYgix87fa559nMzaBa3LSkCQZjJ5AYEjSDnQHLbgU3QZax2efmeJHdxu1LamSCxCJzg37" + ] + }, + { + "generation": "v4", + "programId": "Memo4c2pN8afCj432Lb7RMVKi9PbQnnW7ewFFaV3oAH", + "surfaceCode": "spl_memo_v4", + "status": "generation courante publiee avec implementation Pinocchio", + "instructionData": "octets UTF-8 bruts", + "emptyPayload": "accepte", + "accounts": "zero ou plusieurs; chaque compte fourni doit etre signer", + "signerRequirement": "tous les comptes fournis", + "accountOrder": "ordre de parcours et de logs conserve", + "duplicateAccounts": "aucun rejet dedie prouve; ordre et doublons conserves", + "runtimeLogs": [ + "signataires puis diagnostic UTF-8 ou memo selon l'implementation officielle" + ], + "utf8Failure": "InvalidInstructionData", + "missingSignerFailure": "MissingRequiredSignature avant validation UTF-8", + "builderOfficial": "builder generique 2.1.0 avec program ID v4 explicite", + "currentInvocation": { + "mainnet": "transactions reelles observees et rejouees; aucun envoi execute dans cette tranche", + "devnet": "simulation et trois executions v4 confirmees avec post-validation complete le 2026-07-15", + "localnet": "binaire officiel disponible, non execute" + }, + "realCorpusSignatures": [ + "2vgR56q2NR6ehu1uNnpt3te3RqhX2sXryXi4SpBHKUyVaeERr1HLYSLvDFRzd3eDeNejjLX9hVZJZ5GvtsTtBr1c", + "53aHhj6rt77hCkadHedpWS7GstdER5XqQrHDutZ7gAQrDvAxcDcktrUtwxZcCbTV2FnLzELqfwoJgvabjdLPWkQ5", + "3NxmPdF9W3GBiD8qF2VzvUhEDrTM48EEkaosTtzVceRhQc4sGEojDFppBsXYgqK9GCrCUo4wzAWciprsqZEmRyee", + "2njzDJUBar6TTPPcuwEDdkiW8kVLUsZXfdvUQNP7TC6La44E4kZtLFfdJ1Q8nPNJbKa9Vuby4TtiuLX2rQQTa65C" + ] + } + ], + "decoderContract": { + "maximumRetainedPayloadBytes": 4096, + "diagnosticHexPrefixBytes": 32, + "completeTextRetainedWhenUtf8Valid": true, + "failedTransactionRuntimeValidation": "not_proven_transaction_failed", + "failedTransactionCommitted": false, + "unknownProgramProducesObservation": false + }, + "materializationContract": { + "crate": "kb_materializer_transaction_annotations", + "processorName": "transaction_annotations", + "materializedFamily": "transaction_annotation", + "acceptedEntries": ["add_memo", "memo_intent", "invalid_memo_attempt"], + "projectedEntry": "add_memo", + "transactionPolicy": "SuccessfulCommittedOnly", + "failedOrUncommittedOutput": false, + "store": "kb_sol_mat_events", + "newMigrationRequired": false, + "idempotence": "ledger processor/version/input hash plus deterministic output key", + "readModel": { + "contract": "DecodePipelineStore.list_materialized_events", + "demo": "demo_decode_replay transaction annotation journal", + "boundedMaximumRows": 500, + "filters": ["processor exact", "family exact", "partial signature"], + "visualization": "table journal, not OHLC" + }, + "decodedOnlyFields": [ + "complete accounts and writable flags", + "payload hex diagnostic prefix", + "UTF-8 error offset", + "transaction error", + "decoder proof" + ] + }, + "executorContract": { + "crate": "kb_executor_spl_memo", + "operationCode": "spl_memo.add_memo", + "typedIntent": "SplMemoExecutionIntent", + "officialBuilder": "spl_memo_interface::instruction::build_memo", + "supportedGenerations": ["v4"], + "decodeOnlyGenerations": ["v1", "v3"], + "maximumMessageBytes": 566, + "maximumSignerOccurrences": 32, + "preserveSignerOrderAndDuplicates": true, + "inventWritableAccounts": false, + "requestedSpendLamports": 0, + "simulationRequired": true, + "dryRunDefault": true, + "allClustersConstructible": true, + "historicalGenerationSendEnabled": false, + "mainnetSendGovernedByCommonPolicy": true, + "clusterAvailabilityProvenBySimulation": true, + "demoCluster": "devnet", + "demoGeneration": "v4", + "postExecutionValidation": [ + "canonical_insert", + "core_extraction", + "memo_decode_replay", + "transaction_annotation_materialization" + ] + }, + "requiredCorpusCases": [ + "empty_payload", + "ascii", + "unicode_multibyte", + "near_practical_transaction_limit", + "invalid_utf8", + "zero_signer", + "one_signer", + "multiple_signers", + "non_signer_account", + "duplicate_accounts", + "additional_writable_account", + "outer_instruction", + "inner_instruction", + "successful_transaction", + "failed_transaction", + "malformed_base64", + "payload_over_decoder_limit" + ], + "mainnetReplayValidation": { + "date": "2026-07-14", + "acquisition": { + "programCampaigns": 3, + "canonicalTransactionsInserted": 300, + "missingTransactions": 0 + }, + "coreExtraction": { + "selected": 300, + "extracted": 300, + "failed": 0 + }, + "generations": [ + { + "generation": "v1", + "decodedInstructions": 102, + "materializedAnnotations": 69, + "refusedUncommittedIntents": 33 + }, + { + "generation": "v3", + "decodedInstructions": 439, + "materializedAnnotations": 422, + "refusedUncommittedIntents": 17, + "secondNonForcedReplaySelected": 0 + }, + { + "generation": "v4", + "decodedInstructions": 100, + "materializedAnnotations": 100, + "refusedUncommittedIntents": 0 + } + ], + "decodeFailures": 0, + "unmatchedInstructions": 0, + "operationalErrorLogEntries": 0 + }, + "devnetExecutionValidation": { + "date": "2026-07-15", + "generation": "v4", + "programId": "Memo4c2pN8afCj432Lb7RMVKi9PbQnnW7ewFFaV3oAH", + "simulationOnlyValidated": true, + "submittedTransactions": 3, + "confirmedTransactions": 3, + "canonicalTransactionsInserted": 3, + "coreTransactionsExtracted": 3, + "decodedInstructions": 3, + "materializedAnnotations": 3, + "annotationPayloadLengthBytes": 34, + "secondReplaySkipped": 3, + "secondReplayNewMaterializedOutputs": 0, + "decodeFailures": 0, + "materializationRefusals": 0, + "signatures": [ + "53aHhj6rt77hCkadHedpWS7GstdER5XqQrHDutZ7gAQrDvAxcDcktrUtwxZcCbTV2FnLzELqfwoJgvabjdLPWkQ5", + "3NxmPdF9W3GBiD8qF2VzvUhEDrTM48EEkaosTtzVceRhQc4sGEojDFppBsXYgqK9GCrCUo4wzAWciprsqZEmRyee", + "2njzDJUBar6TTPPcuwEDdkiW8kVLUsZXfdvUQNP7TC6La44E4kZtLFfdJ1Q8nPNJbKa9Vuby4TtiuLX2rQQTa65C" + ] + }, + "sources": [ + { + "kind": "official_interface_source", + "url": "https://docs.rs/spl-memo-interface/2.1.0/src/spl_memo_interface/instruction.rs.html" + }, + { + "kind": "official_interface_ids", + "url": "https://docs.rs/spl-memo-interface/2.1.0/src/spl_memo_interface/lib.rs.html" + }, + { + "kind": "official_v1_historical_source", + "url": "https://github.com/solana-program/memo/commit/479839141a99b7d0c3a35bc648c0fa1e4704ea3e" + }, + { + "kind": "official_v3_published_source", + "url": "https://docs.rs/spl-memo/3.0.0/src/spl_memo/processor.rs.html" + }, + { + "kind": "official_current_source", + "url": "https://github.com/solana-program/memo/blob/main/program/src/processor.rs" + } + ], + "documentedLimitations": [ + "v1 et v3 restent decodees depuis un corpus Mainnet reel et leur wire officiel reste audite, mais l'executeur les classe decode-only et ne construit que v4", + "l'invocabilite Localnet depend du deploiement explicite du binaire de generation demande", + "les differences de cout v3/v4 ne sont pas transformees en garantie statique; chaque plan reste soumis a la simulation et au plafond de frais" + ] +} diff --git a/docs/SPL_TOKEN_2022_CONFIDENTIAL_EXECUTION_AUDIT.md b/docs/SPL_TOKEN_2022_CONFIDENTIAL_EXECUTION_AUDIT.md new file mode 100644 index 0000000..f7670fa --- /dev/null +++ b/docs/SPL_TOKEN_2022_CONFIDENTIAL_EXECUTION_AUDIT.md @@ -0,0 +1,110 @@ + + + +# Audit d’exécution Confidential Transfer Token-2022 + +## Frontières de programme + +Les instructions Confidential Transfer restent des instructions du programme Token-2022. Les preuves associées sont des instructions distinctes du programme natif ZK ElGamal Proof ou des comptes de contexte déjà vérifiés. Le registre ElGamal reste une troisième surface avec son propre Program ID. + +Aucun builder Token-2022 ne doit incorporer silencieusement une preuve, déchiffrer un solde ou générer une clé privée. + +## Modes de preuve + +Chaque preuve est fournie selon un seul mode explicite : + +- `instruction_offset` : offset relatif signé non nul vers une instruction ZK distincte de la même transaction ; +- `context_state_account` : compte readonly contenant le contexte public d’une preuve déjà vérifiée ; le wire Token-2022 encode alors l’offset `0`. + +Un offset inline égal à zéro est invalide dans le contrat de l’exécuteur, car il rendrait le mode ambigu. + +## Inventaire audité + +| Opération | Preuves ordonnées | Classe d’exécution | +|-------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------| +| InitializeMint | aucune | builder public | +| UpdateMint | aucune | builder public | +| ConfigureAccount | PubkeyValidity | entrées cryptographiques appelant | +| ApproveAccount | aucune | builder public | +| EmptyAccount | ZeroCiphertext | entrées cryptographiques appelant | +| Deposit | aucune | builder public | +| Withdraw | CiphertextCommitmentEquality, BatchedRangeProofU64 | entrées cryptographiques appelant | +| Transfer | CiphertextCommitmentEquality, BatchedGroupedCiphertext3HandlesValidity, BatchedRangeProofU128 | entrées cryptographiques appelant | +| ApplyPendingBalance | aucune | builder public | +| EnableConfidentialCredits | aucune | builder public | +| DisableConfidentialCredits | aucune | builder public | +| EnableNonConfidentialCredits | aucune | builder public | +| DisableNonConfidentialCredits | aucune | builder public | +| TransferWithFee | CiphertextCommitmentEquality, BatchedGroupedCiphertext3HandlesValidity, PercentageWithFee, BatchedGroupedCiphertext2HandlesValidity, BatchedRangeProofU256 | entrées cryptographiques appelant | +| ConfigureAccountWithRegistry | PubkeyValidity | entrées cryptographiques appelant | + +La classe « builder public » signifie seulement que l’instruction Token-2022 ne nécessite pas de ciphertext ou preuve nouvellement généré. Elle ne dispense ni du préflight, ni de la simulation, ni des autorités et postconditions. + +## Plan de livraison + +- `pre.048` : types, modes de preuve, ordre des preuves et classification de support ; +- `pre.049` : builders publics Initialize/Update/Approve/Deposit/Apply/credit toggles ; +- `pre.050` : clés ElGamal et balances déchiffrables opaques, Initialize/Update Mint et Apply Pending Balance ; +- `pre.051` : Configure Account et Configure Account With Registry ; +- `pre.052` : Empty Account avec preuve ZeroCiphertext inline ou context-state ; +- `pre.053` : Withdraw avec Equality et Range U64 ; +- `pre.054` : Transfer avec Equality, Grouped Ciphertext Validity à trois handles et Range U128 ; +- `pre.055` : audit exact de Transfer With Fee, cinq preuves, comptes et payload ; +- `pre.056` : builder Transfer With Fee ; +- tranches ultérieures : Confidential Transfer Fee, Confidential Mint/Burn et Permissioned Confidential Burn. + +## `pre.053` — Withdraw + +`Withdraw` exige deux références de preuve dans l’ordre officiel : `CiphertextCommitmentEquality`, puis `BatchedRangeProofU64`. Les modes inline et context-state peuvent être combinés. L’exécuteur transporte la nouvelle balance déchiffrable opaque sans produire ni vérifier localement les secrets ou preuves. + + +## `pre.054` — Transfer + +`Transfer` transporte une nouvelle balance source déchiffrable de 36 octets et deux ciphertexts auditeur ElGamal de 64 octets. Il exige exactement trois références de preuve dans l'ordre officiel : `CiphertextCommitmentEquality`, `BatchedGroupedCiphertext3HandlesValidity`, puis `BatchedRangeProofU128`. Chaque preuve peut être inline ou context-state ; l'Instructions Sysvar n'est ajouté qu'une fois lorsqu'au moins un offset est utilisé. Aucune donnée cryptographique n'est générée par l'exécuteur. + + +## `pre.055` — audit Transfer With Fee + +`TransferWithFee` conserve le même payload cryptographique Token-2022 que `Transfer` : une balance source déchiffrable de 36 octets et deux ciphertexts auditeur ElGamal de 64 octets. Il n’ajoute pas de ciphertext withheld ou de montant de frais au payload Token-2022. Les informations de frais et le ciphertext de frais appartiennent aux contextes publics des preuves `PercentageWithFee` et `BatchedGroupedCiphertext2HandlesValidity`. + +Le wire exact contient le tag externe `27`, le sous-tag `13`, puis 169 octets de payload : `36 + 64 + 64 + 5 offsets i8`. La donnée complète mesure donc 171 octets. + +Les cinq preuves restent ordonnées : + +1. `CiphertextCommitmentEquality` ; +2. `BatchedGroupedCiphertext3HandlesValidity` pour le montant transféré ; +3. `PercentageWithFee` ; +4. `BatchedGroupedCiphertext2HandlesValidity` pour le ciphertext de frais ; +5. `BatchedRangeProofU256`. + +L’ordre des comptes est source writable, mint readonly, destination writable, Instructions Sysvar si au moins une preuve est inline, comptes context-state dans l’ordre des cinq preuves, autorité, puis signataires multisig. Aucun type opaque Token-2022 supplémentaire n’est nécessaire avant le builder de `pre.056`. + +## Builder Transfer With Fee — pre.056 + +Le builder `inner_transfer_with_fee` est activé avec les cinq preuves dans l’ordre officiel. Le payload reste limité à la nouvelle balance déchiffrable, aux deux ciphertexts auditeur et aux cinq offsets. Les comptes context-state suivent le même ordre que les preuves et l’Instructions Sysvar n’est présent qu’une fois lorsqu’au moins un offset inline est utilisé. + +## Confidential Transfer Fee public builder tranche (`pre.057`) + +The proof-free builder surface is restricted to subdiscriminants 0, 3, 4 and 5 +of the Token-2022 `ConfidentialTransferFeeExtension` envelope (external tag 37). +Initialization accepts one writable mint, an optional configuration authority +and one exact 32-byte ElGamal public key. Enable/disable harvest preserve simple +or multisig authority metas. Permissionless harvest preserves an ordered, +non-empty, duplicate-free source list bounded to 255 accounts. Withdrawals remain +separate because they require ciphertext-equality proof context. + +## Confidential Transfer Fee — retrait depuis plusieurs comptes (`pre.059`) + +Le builder `inner_withdraw_withheld_tokens_from_accounts` conserve l’ordre officiel suivant : Mint readonly, destination writable, Instructions Sysvar ou compte context-state, autorité withdraw-withheld, signataires multisig, puis sources writable. Le payload encode `num_token_accounts: u8`, l’offset de preuve et la nouvelle balance déchiffrable destination. + +La preuve requise est `CiphertextCiphertextEquality`. La liste des sources est non vide, unique, ordonnée et limitée à 255 comptes. Cette opération est explicitement sensible au front-running : une mutation d’un withheld ciphertext source après génération de la preuve fait échouer la transaction. Le parcours harvest-vers-Mint puis retrait-depuis-Mint reste l’alternative recommandée lorsque la stabilité des comptes sources ne peut pas être garantie. + +## `pre.060` — audit Confidential Mint/Burn + +L'interface publie six sous-instructions contiguës : `InitializeMint = 0`, `RotateSupplyElGamalPubkey = 1`, `UpdateDecryptableSupply = 2`, `Mint = 3`, `Burn = 4` et `ApplyPendingBurn = 5`. Cette famille reste dans l'enveloppe Token-2022 Confidential Mint/Burn et ne doit pas être confondue avec Permissioned Burn. + +`InitializeMint` transporte une clé publique ElGamal de supply et une supply déchiffrable initiale. `RotateSupplyElGamalPubkey` transporte la nouvelle clé et exige `CiphertextCiphertextEquality`. `UpdateDecryptableSupply` et `ApplyPendingBurn` n'exigent aucune preuve, mais restent soumis aux autorités et préconditions d'état. + +`Mint` et `Burn` exigent exactement trois preuves dans l'ordre : `CiphertextCommitmentEquality`, `BatchedGroupedCiphertext3HandlesValidity`, puis `BatchedRangeProofU128`. Les deux opérations transportent deux ciphertexts auditeur ; `Mint` transporte la nouvelle supply déchiffrable, tandis que `Burn` transporte la nouvelle balance disponible déchiffrable du compte source. Aucun secret, ciphertext ou preuve n'est généré par l'exécuteur. + +Le découpage prévu est : builders publics et rotation dans `pre.061`, puis Mint/Burn prouvés dans `pre.062`. Permissioned Confidential Burn reste une tranche indépendante. diff --git a/docs/SPL_TOKEN_2022_MATRIX.json b/docs/SPL_TOKEN_2022_MATRIX.json new file mode 100644 index 0000000..2639640 --- /dev/null +++ b/docs/SPL_TOKEN_2022_MATRIX.json @@ -0,0 +1,2698 @@ +{ + "file": "docs/SPL_TOKEN_2022_MATRIX.json", + "version": "0.4.6-pre.026", + "schemaVersion": 1, + "matrixStatus": "exact_decoder_leaf_coverage_proven_pending_materialization_executor_preflight_and_cluster_corpus_completion", + "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "strictProgramBoundary": { + "acceptedProgramIds": [ + "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + ], + "rejectedDistinctSurfaces": [ + "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", + "ZkE1Gama1Proof11111111111111111111111111111", + "regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg" + ], + "externalInterfaceRule": "Transfer Hook, Token Group and Token Metadata implementations are dispatched only by an independently audited executable Program ID. A Token-2022 TLV pointer remains a link, not a decode of its target program." + }, + "interfaceAudit": { + "workspaceConstraint": "^3.1", + "workspaceFeatures": [ + "serde" + ], + "workspaceCargoLockPresentInProvidedArchive": true, + "workspaceCargoLockSha256": "248c02af8c78d3bb6ad747460fee0011f8141feef83364cf697e35e44bd4ceb9", + "workspaceCargoToolAvailableInAuditEnvironment": false, + "resolvedVersion": "3.1.1", + "resolutionStatus": "confirmed_by_operator_cargo_tree_2026-07-16", + "resolvedFeatures": [ + "default", + "serde" + ], + "leafConsumer": "kb_decoder_spl_token_2022", + "operatorEvidence": "cargo tree -i spl-token-2022-interface -e features", + "publishedCandidateAudited": "3.1.1", + "publishedCandidateStatus": "matches_confirmed_workspace_resolution", + "publishedDocumentation": "https://docs.rs/spl-token-2022-interface/3.1.1/spl_token_2022_interface/", + "officialRepository": "https://github.com/solana-program/token-2022", + "sourcePaths": [ + "interface/src/instruction.rs", + "interface/src/extension/mod.rs", + "interface/src/extension/*/instruction.rs", + "program/src/processor.rs", + "program/src/extension/*/processor.rs" + ], + "candidateDirectDependenciesRelevantToAudit": { + "solana-zk-elgamal-proof-interface": "^0.1.3", + "spl-token-confidential-transfer-proof-extraction": "^0.6.0", + "spl-token-group-interface": "^0.7.2", + "spl-token-metadata-interface": "^1.0.0", + "spl-type-length-value": "^0.9.1" + }, + "operatorWorkspaceResolvedDependencies": { + "solana-zk-elgamal-proof-interface": "0.1.3", + "spl-token-confidential-transfer-proof-extraction": "0.6.1", + "spl-token-group-interface": "0.7.2", + "spl-token-metadata-interface": "1.0.1", + "spl-type-length-value": "0.9.1" + }, + "operatorWorkspaceDependencyEvidence": { + "source": "attached Cargo.lock plus cargo tree -p spl-token-2022-interface -e features", + "tokenGroupConsumption": "direct dependency of spl-token-2022-interface 3.1.1", + "tokenMetadataConsumption": "direct dependency of spl-token-2022-interface 3.1.1", + "processorSourceLockMetadataVersion": "1.0.0", + "operatorWorkspaceMetadataVersion": "1.0.1", + "metadataVersionDriftStatus": "unpack_hardening_only_wire_and_builders_unchanged_by_source_diff" + }, + "operatorCommandsRequired": [ + "cargo tree -p kb_decoder_spl_token_2022 -e features", + "cargo tree -p kb_executor_spl_token_2022 -e features", + "cargo tree -i spl-token-2022-interface -e features", + "cargo tree -i spl-elgamal-registry-interface -e features", + "cargo metadata --format-version 1 --locked", + "cargo test -p kb_decoder_spl_token_2022", + "cargo clippy --all-targets" + ], + "sourceCommit": "e18f9c6f9bf6044b934f48e3090e8e59e4820f02", + "sourceTag": "interface@v3.1.1", + "sourceCommitDate": "2026-06-26" + }, + "operatorValidation": { + "date": "2026-07-16", + "cargoTestPackage": { + "command": "cargo test -p kb_decoder_spl_token_2022", + "status": "passed", + "passed": 8, + "failed": 0 + }, + "cargoClippyAllTargets": { + "command": "cargo clippy --all-targets", + "status": "clean" + } + }, + "tokenMetadataVersionDriftAudit": { + "processorSourceVersion": "1.0.0", + "operatorWorkspaceVersion": "1.0.1", + "releaseDate": "2026-06-29", + "releaseCommit": "7038969", + "changePullRequest": 87, + "mergedCommit": "fc97c60", + "changedProductionFile": "interface/src/instruction.rs", + "productionDiff": { + "additions": 78, + "deletions": 0 + }, + "wireDiscriminantsChanged": false, + "buildersChanged": false, + "changeKind": "pre_borsh_string_length_validation_before_existing_try_from_slice", + "affectedInstructions": { + "initialize": [ + "name", + "symbol", + "uri" + ], + "update_field": [ + "field_key_when_tag_3", + "value" + ], + "remove_key": [ + "key" + ] + }, + "unaffectedInstructions": [ + "update_authority", + "emit" + ], + "decoderRequirement": "validate every little_endian_u32 string length against remaining bounded input before allocation or UTF-8 materialization", + "malformedPolicy": "bounded invalid_instruction diagnostic without semantic event", + "evidence": "official interface@v1.0.1 release and solana-program/token-metadata pull request 87 files diff" + }, + "processorAudit": { + "auditedVersion": "11.0.0", + "sourceCommit": "e18f9c6f9bf6044b934f48e3090e8e59e4820f02", + "sourceAnchor": "same repository commit as interface@v3.1.1", + "programManifestInterfaceRequirement": "3.1.0 path dependency resolved to workspace interface 3.1.1 at the audited commit", + "dispatchStatus": "top_level_extension_and_embedded_interface_fallbacks_confirmed", + "resolvedInterfaceDependenciesFromSourceCargoLock": { + "spl-token-group-interface": "0.7.2", + "spl-token-metadata-interface": "1.0.0", + "spl-elgamal-registry-interface": "0.2.1" + }, + "deploymentBinaryEquality": { + "localnet": "not_tested", + "devnet": "not_proven", + "mainnet": "not_proven" + }, + "corpus": { + "offline": "not_started", + "devnet": "not_started", + "mainnet": "not_started" + }, + "rule": "Interface publication, processor implementation, builder constructibility, cluster deployment, observation, simulation and submission are independent proofs." + }, + "wireContract": { + "topLevelEncoding": "ordered_union: first try one_u8_PodTokenInstruction; if it fails try exact_8_byte_TokenMetadataInstruction discriminator; if that fails try exact_8_byte_TokenGroupInstruction discriminator", + "topLevelSuffixPolicy": "processor-specific exact decode: fixed Pod payloads use exact bytemuck sizes; metadata uses bounded Borsh and try_from_slice; group uses exact bytemuck sizes; remaining per-variant proof suffix rules pending", + "outerAndInner": true, + "failedTransactionPolicy": "preserve a bounded structured intent when the wire is readable; committed remains false", + "unknownInstructionPolicy": "bounded opaque diagnostic without semantic event", + "batch": { + "tag": 255, + "recordPrefix": [ + "account_count_u8", + "instruction_data_length_u8" + ], + "nestedBatchAllowed": false, + "bounds": "pending_workspace_policy_and_processor_audit" + } + }, + "baseInstructionWireAudit": { + "status": "published_interface_3_1_1_pack_and_unpack_exact_processor_suffix_acceptance_pending_per_variant", + "rowCount": 33, + "scope": "top-level u8 forms that are not extension-family envelopes", + "sizeIncludesTopLevelTag": true, + "sizeRuleMeaning": "canonical TokenInstruction::pack output for fixed forms; public unpack may accept a longer buffer when interfaceConsumption says its returned suffix is discarded", + "integerEndianness": "little_endian", + "compactOptions": { + "pubkey": { + "none": "one byte 0", + "some": "one byte 1 followed by 32-byte pubkey" + }, + "u64": { + "none": "one byte 0", + "some": "one byte 1 followed by u64_le" + }, + "invalidDiscriminantPolicy": "interface_unpack_rejects_values_other_than_0_or_1" + }, + "publicUnpackContract": { + "entryPoint": "TokenInstruction::unpack calls unpack_with_rest and discards the returned remainder", + "fixedPrefixConsequence": "a successful public interface unpack is not proof that the whole instruction buffer was consumed", + "decoderRequirement": "retain and diagnose every unconsumed bounded suffix until the exact processor path proves acceptance or rejection", + "runtimeClaim": "none_from_public_unpack_alone" + }, + "rows": [ + { + "tag": 0, + "name": "initialize_mint", + "sizeRule": "35_or_67", + "payload": "decimals_u8+mint_authority_32+compact_freeze_authority", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_mint" + }, + { + "tag": 1, + "name": "initialize_account", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_account" + }, + { + "tag": 2, + "name": "initialize_multisig", + "sizeRule": "exact_2", + "payload": "m_u8", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_multisig" + }, + { + "tag": 3, + "name": "transfer", + "sizeRule": "exact_9", + "payload": "amount_u64_le", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "transfer", + "status": "deprecated" + }, + { + "tag": 4, + "name": "approve", + "sizeRule": "exact_9", + "payload": "amount_u64_le", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "approve" + }, + { + "tag": 5, + "name": "revoke", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "revoke" + }, + { + "tag": 6, + "name": "set_authority", + "sizeRule": "3_or_35", + "payload": "authority_type_u8+compact_new_authority", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "set_authority" + }, + { + "tag": 7, + "name": "mint_to", + "sizeRule": "exact_9", + "payload": "amount_u64_le", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "mint_to" + }, + { + "tag": 8, + "name": "burn", + "sizeRule": "exact_9", + "payload": "amount_u64_le", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "burn" + }, + { + "tag": 9, + "name": "close_account", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "close_account" + }, + { + "tag": 10, + "name": "freeze_account", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "freeze_account" + }, + { + "tag": 11, + "name": "thaw_account", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "thaw_account" + }, + { + "tag": 12, + "name": "transfer_checked", + "sizeRule": "exact_10", + "payload": "amount_u64_le+decimals_u8", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "transfer_checked" + }, + { + "tag": 13, + "name": "approve_checked", + "sizeRule": "exact_10", + "payload": "amount_u64_le+decimals_u8", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "approve_checked" + }, + { + "tag": 14, + "name": "mint_to_checked", + "sizeRule": "exact_10", + "payload": "amount_u64_le+decimals_u8", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "mint_to_checked" + }, + { + "tag": 15, + "name": "burn_checked", + "sizeRule": "exact_10", + "payload": "amount_u64_le+decimals_u8", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "burn_checked" + }, + { + "tag": 16, + "name": "initialize_account2", + "sizeRule": "exact_33", + "payload": "owner_pubkey_32", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_account2" + }, + { + "tag": 17, + "name": "sync_native", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "sync_native" + }, + { + "tag": 18, + "name": "initialize_account3", + "sizeRule": "exact_33", + "payload": "owner_pubkey_32", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_account3" + }, + { + "tag": 19, + "name": "initialize_multisig2", + "sizeRule": "exact_2", + "payload": "m_u8", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_multisig2" + }, + { + "tag": 20, + "name": "initialize_mint2", + "sizeRule": "35_or_67", + "payload": "decimals_u8+mint_authority_32+compact_freeze_authority", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_mint2" + }, + { + "tag": 21, + "name": "get_account_data_size", + "sizeRule": "1_plus_2n_n_greater_or_equal_0", + "payload": "all_remaining_known_extension_type_u16_le", + "interfaceConsumption": "entire_remainder_rejected_if_odd_or_unknown_extension_type", + "officialBuilder": "get_account_data_size" + }, + { + "tag": 22, + "name": "initialize_immutable_owner", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_immutable_owner" + }, + { + "tag": 23, + "name": "amount_to_ui_amount", + "sizeRule": "exact_9", + "payload": "amount_u64_le", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "amount_to_ui_amount" + }, + { + "tag": 24, + "name": "ui_amount_to_amount", + "sizeRule": "minimum_1", + "payload": "entire_remaining_utf8_bytes_empty_allowed_by_interface", + "interfaceConsumption": "entire_remainder_utf8_validated", + "officialBuilder": "ui_amount_to_amount", + "decoderBound": "required_before_utf8_materialization" + }, + { + "tag": 25, + "name": "initialize_mint_close_authority", + "sizeRule": "2_or_34", + "payload": "compact_close_authority", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_mint_close_authority" + }, + { + "tag": 29, + "name": "reallocate", + "sizeRule": "1_plus_2n_n_greater_or_equal_0", + "payload": "all_remaining_known_extension_type_u16_le", + "interfaceConsumption": "entire_remainder_rejected_if_odd_or_unknown_extension_type", + "officialBuilder": "reallocate" + }, + { + "tag": 31, + "name": "create_native_mint", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "create_native_mint" + }, + { + "tag": 32, + "name": "initialize_non_transferable_mint", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_non_transferable_mint" + }, + { + "tag": 35, + "name": "initialize_permanent_delegate", + "sizeRule": "exact_33", + "payload": "delegate_pubkey_32", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_permanent_delegate" + }, + { + "tag": 38, + "name": "withdraw_excess_lamports", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "withdraw_excess_lamports" + }, + { + "tag": 45, + "name": "unwrap_lamports", + "sizeRule": "2_or_10", + "payload": "compact_amount_u64_le", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "unwrap_lamports" + }, + { + "tag": 255, + "name": "batch", + "sizeRule": "minimum_1", + "payload": "entire_remaining_batch_record_wire", + "interfaceConsumption": "entire_remainder_preserved_as_batch_payload", + "officialBuilder": "no_dedicated_rust_interface_helper_identified", + "processorRecordAudit": "pending_bounds_accounts_and_nested_batch_rejection" + } + ], + "remainingProof": "exact processor helper path, suffix acceptance, accounts and flags for every row" + }, + "baseInstructionProcessorSuffixAudit": { + "status": "confirmed_from_exact_processor_11_0_0_source_commit", + "sourceCommit": "e18f9c6f9bf6044b934f48e3090e8e59e4820f02", + "sources": [ + { + "path": "interface/src/instruction.rs", + "sha256": "672b3890cb58e5700a67f202491069ed44dffcbfa0e7d0b83a0cfbbb05b3a287" + }, + { + "path": "program/src/pod_instruction.rs", + "sha256": "7b546580a90ab8d1f52143c21da931d26f8b68c7d2050b4eaf7307fc9c49c7e5" + }, + { + "path": "program/src/processor.rs", + "sha256": "9d11b834aabdb8c824ca59f7ea7bda4d217565a58359fbfaf37e5a35e8f3032d" + } + ], + "partition": [ + { + "policy": "exact_total_length_via_decode_instruction_data", + "tags": [ + 2, + 3, + 4, + 7, + 8, + 12, + 13, + 14, + 15, + 16, + 18, + 19, + 23, + 35 + ], + "count": 14, + "suffix": "rejected", + "proof": "decode_instruction_data requires input length equal to one plus size_of Pod payload" + }, + { + "policy": "compact_option_prefix_only", + "tags": [ + 0, + 6, + 20, + 25, + 45 + ], + "count": 5, + "suffix": "accepted_after_complete_none_or_some_prefix", + "proof": "specialized Pod plus compact-option helpers read the required prefix and do not require exhaustion" + }, + { + "policy": "tag_only_dispatch", + "tags": [ + 1, + 5, + 9, + 10, + 11, + 17, + 22, + 31, + 32, + 38 + ], + "count": 10, + "suffix": "accepted_without_inspection", + "proof": "processor branch invokes no instruction-data decoder after decode_instruction_type reads byte zero" + }, + { + "policy": "entire_remainder_is_payload", + "tags": [ + 21, + 24, + 29 + ], + "count": 3, + "suffix": "not_applicable_remainder_consumed", + "proof": "extension-type vectors parse all two-byte chunks; UI amount parses all remaining bytes as UTF-8", + "constraints": { + "extensionTypeVectors": "empty allowed; odd final chunk or unknown ExtensionType rejected", + "uiAmount": "empty allowed by UTF-8 parser; malformed UTF-8 rejected" + } + }, + { + "policy": "batch_record_stream", + "tags": [ + 255 + ], + "count": 1, + "suffix": "entire_remainder_must_be_complete_record_stream", + "proof": "top-level process routes tag 255 to process_batch and nested _process_inner rejects tag 255", + "record": { + "headerBytes": 2, + "accountCount": "u8", + "instructionDataLengthIncludingDiscriminator": "u8", + "minimumOuterInstructionBytes": 4, + "zeroInstructionDataLength": "rejected_by_inner_dispatch", + "truncatedHeaderOrPayload": "rejected", + "nestedBatch": "rejected", + "embeddedMetadataOrGroupFallback": "processor_dispatch_allows_when_record_length_fits_u8", + "maximumAccountsPerRecordFromWire": 255, + "maximumInstructionDataBytesPerRecordFromWire": 255, + "recordCountProcessorBound": "none_explicit_beyond_transaction_instruction_data_and_compute_limits", + "decoderRecordCountBound": "required" + } + } + ], + "partitionTagCount": 33, + "partitionTagsUnique": true, + "decoderPolicy": { + "exactPod": "reject suffix", + "acceptedRuntimeSuffix": "preserve bounded suffix and emit an explicit accepted_suffix diagnostic without inventing fields", + "fullRemainder": "decode only after global byte and element bounds", + "batch": "bound record count, account count, per-record data and total data before nested leaf decode" + }, + "remainingProof": "exact account order, signer and writable flags, optional and multisig tails for every base form" + }, + "baseInstructionBuilderAccountAudit": { + "status": "exact_official_interface_3_1_1_builder_metas_for_32_constructible_forms_and_explicit_batch_gap", + "source": { + "path": "interface/src/instruction.rs", + "commit": "e18f9c6f9bf6044b934f48e3090e8e59e4820f02", + "sha256": "672b3890cb58e5700a67f202491069ed44dffcbfa0e7d0b83a0cfbbb05b3a287" + }, + "rowCount": 33, + "flagLegend": { + "writable": "canonical AccountMeta writable flag emitted by the official builder", + "signer": "canonical AccountMeta signer flag; conditional_authority means true only when the multisig signer tail is empty" + }, + "multisigAuthorityTail": { + "builderAcceptedRange": "zero selects a single signing authority; a non-empty signer_pubkeys slice is appended without an explicit builder length check", + "runtimeStoredMultisigPositions": "1..11", + "meta": { + "role": "multisig_signer", + "writable": false, + "signer": true + }, + "authorityMetaWhenTailPresent": { + "writable": false, + "signer": false + }, + "authorityMetaWhenTailEmpty": { + "writable": false, + "signer": true + }, + "duplicatesPreserved": true + }, + "rows": [ + { + "tag": 0, + "name": "initialize_mint", + "builder": "initialize_mint", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "rent_sysvar", + "writable": false, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 1, + "name": "initialize_account", + "builder": "initialize_account", + "accounts": [ + { + "index": 0, + "role": "token_account", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "owner", + "writable": false, + "signer": false + }, + { + "index": 3, + "role": "rent_sysvar", + "writable": false, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 2, + "name": "initialize_multisig", + "builder": "initialize_multisig", + "accounts": [ + { + "index": 0, + "role": "multisig", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "rent_sysvar", + "writable": false, + "signer": false + } + ], + "tail": "multisig_configuration_keys_readonly_non_signer_1_to_11" + }, + { + "tag": 3, + "name": "transfer", + "builder": "transfer", + "accounts": [ + { + "index": 0, + "role": "source", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "destination", + "writable": true, + "signer": false + }, + { + "index": 2, + "role": "authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail", + "status": "deprecated" + }, + { + "tag": 4, + "name": "approve", + "builder": "approve", + "accounts": [ + { + "index": 0, + "role": "source", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "delegate", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "owner", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 5, + "name": "revoke", + "builder": "revoke", + "accounts": [ + { + "index": 0, + "role": "source", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "owner_or_delegate", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 6, + "name": "set_authority", + "builder": "set_authority", + "accounts": [ + { + "index": 0, + "role": "owned_account_or_mint", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "current_authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 7, + "name": "mint_to", + "builder": "mint_to", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "destination", + "writable": true, + "signer": false + }, + { + "index": 2, + "role": "mint_authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 8, + "name": "burn", + "builder": "burn", + "accounts": [ + { + "index": 0, + "role": "source", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": true, + "signer": false + }, + { + "index": 2, + "role": "authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 9, + "name": "close_account", + "builder": "close_account", + "accounts": [ + { + "index": 0, + "role": "source_account_or_mint", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "destination", + "writable": true, + "signer": false + }, + { + "index": 2, + "role": "close_authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 10, + "name": "freeze_account", + "builder": "freeze_account", + "accounts": [ + { + "index": 0, + "role": "token_account", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "freeze_authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 11, + "name": "thaw_account", + "builder": "thaw_account", + "accounts": [ + { + "index": 0, + "role": "token_account", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "freeze_authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 12, + "name": "transfer_checked", + "builder": "transfer_checked", + "accounts": [ + { + "index": 0, + "role": "source", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "destination", + "writable": true, + "signer": false + }, + { + "index": 3, + "role": "authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 13, + "name": "approve_checked", + "builder": "approve_checked", + "accounts": [ + { + "index": 0, + "role": "source", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "delegate", + "writable": false, + "signer": false + }, + { + "index": 3, + "role": "owner", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 14, + "name": "mint_to_checked", + "builder": "mint_to_checked", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "destination", + "writable": true, + "signer": false + }, + { + "index": 2, + "role": "mint_authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 15, + "name": "burn_checked", + "builder": "burn_checked", + "accounts": [ + { + "index": 0, + "role": "source", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": true, + "signer": false + }, + { + "index": 2, + "role": "authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 16, + "name": "initialize_account2", + "builder": "initialize_account2", + "accounts": [ + { + "index": 0, + "role": "token_account", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "rent_sysvar", + "writable": false, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 17, + "name": "sync_native", + "builder": "sync_native_or_sync_native_with_rent_sysvar", + "accounts": [ + { + "index": 0, + "role": "native_token_account", + "writable": true, + "signer": false + } + ], + "optionalAccounts": [ + { + "index": 1, + "role": "rent_sysvar", + "writable": false, + "signer": false + } + ], + "tail": "none_after_optional_rent" + }, + { + "tag": 18, + "name": "initialize_account3", + "builder": "initialize_account3", + "accounts": [ + { + "index": 0, + "role": "token_account", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": false, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 19, + "name": "initialize_multisig2", + "builder": "initialize_multisig2", + "accounts": [ + { + "index": 0, + "role": "multisig", + "writable": true, + "signer": false + } + ], + "tail": "multisig_configuration_keys_readonly_non_signer_1_to_11" + }, + { + "tag": 20, + "name": "initialize_mint2", + "builder": "initialize_mint2", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": true, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 21, + "name": "get_account_data_size", + "builder": "get_account_data_size", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": false, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 22, + "name": "initialize_immutable_owner", + "builder": "initialize_immutable_owner", + "accounts": [ + { + "index": 0, + "role": "token_account", + "writable": true, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 23, + "name": "amount_to_ui_amount", + "builder": "amount_to_ui_amount", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": false, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 24, + "name": "ui_amount_to_amount", + "builder": "ui_amount_to_amount", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": false, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 25, + "name": "initialize_mint_close_authority", + "builder": "initialize_mint_close_authority", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": true, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 29, + "name": "reallocate", + "builder": "reallocate", + "accounts": [ + { + "index": 0, + "role": "token_account", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "payer", + "writable": true, + "signer": true + }, + { + "index": 2, + "role": "system_program", + "writable": false, + "signer": false + }, + { + "index": 3, + "role": "owner", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail", + "processorAccountProof": "pending_exact_reallocate_module_reconfirmation" + }, + { + "tag": 31, + "name": "create_native_mint", + "builder": "create_native_mint", + "accounts": [ + { + "index": 0, + "role": "payer", + "writable": true, + "signer": true + }, + { + "index": 1, + "role": "native_mint_pda", + "writable": true, + "signer": false + }, + { + "index": 2, + "role": "system_program", + "writable": false, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 32, + "name": "initialize_non_transferable_mint", + "builder": "initialize_non_transferable_mint", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": true, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 35, + "name": "initialize_permanent_delegate", + "builder": "initialize_permanent_delegate", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": true, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 38, + "name": "withdraw_excess_lamports", + "builder": "withdraw_excess_lamports", + "accounts": [ + { + "index": 0, + "role": "source_account_or_mint", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "destination", + "writable": true, + "signer": false + }, + { + "index": 2, + "role": "authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 45, + "name": "unwrap_lamports", + "builder": "unwrap_lamports", + "accounts": [ + { + "index": 0, + "role": "native_source", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "destination", + "writable": true, + "signer": false + }, + { + "index": 2, + "role": "owner_or_delegate", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 255, + "name": "batch", + "builder": null, + "accounts": "concatenated_record_slices_each_sized_by_account_count_u8", + "tail": "outer_accounts_not_assigned_to_a_record_are_not_rejected_by_processor", + "builderStatus": "enum_pack_only_no_dedicated_rust_account_meta_builder" + } + ], + "processorFactsAlreadyConfirmed": { + "authorityValidation": "runtime-recognized 355-byte Token, inline Token or Token-2022 multisig consumes the remaining accounts as signer candidates; otherwise the authority account itself must sign and remaining accounts are ignored", + "multisigCandidateMatching": "only initialized stored signer positions count; candidate must carry signer flag; a stored position is counted at most once", + "initializeMultisig": "all accounts after multisig and optional rent become configuration keys; runtime validates m and n independently in 1..11 but does not enforce builder rule m <= n", + "syncNative": "second account is optional; when present it is parsed as Rent; accounts after it are ignored", + "batch": "each record receives its declared account prefix; a nested Batch is rejected; outer accounts left after the final record are not checked for exhaustion" + }, + "decoderRequirements": [ + "preserve_original_account_order_flags_and_duplicates", + "separate_builder_canonical_flags_from_processor_runtime_acceptance", + "preserve_multisig_candidate_tail_even_when_single_authority_runtime_ignores_it", + "diagnose_initialize_multisig_m_greater_than_n_as_processor_accepted_but_builder_rejected", + "diagnose_batch_unassigned_outer_accounts", + "never_invent_owner_mint_or_authority_from_state_not_present_in_wire_or_account_contract" + ], + "remainingProof": "processor account consumption for reallocate and exhaustive extra-account behavior by each row" + }, + "accountStateContract": { + "baseLengths": { + "mint": 82, + "account": 165, + "multisig": 355 + }, + "extendedMintPrefix": "82-byte Mint, padding through byte 164, AccountType at byte 165, then TLV", + "extendedAccountPrefix": "165-byte Account, AccountType at byte 165, then TLV", + "multisigRule": "identified by exact 355-byte base length and never treated as an extended Mint or Account", + "accountType": { + "encoding": "u8", + "values": [ + { + "value": 0, + "name": "uninitialized" + }, + { + "value": 1, + "name": "mint" + }, + { + "value": 2, + "name": "account" + } + ] + }, + "tlvHeader": { + "type": "u16_le", + "length": "u16_le", + "headerBytes": 4 + }, + "parserRequirements": [ + "bounded_iteration", + "preserve_order", + "preserve_unknown_type_and_bounded_bytes", + "detect_duplicate_types", + "detect_truncation", + "detect_invalid_padding", + "validate_account_type", + "validate_known_fixed_length", + "preserve_variable_length_without_invention" + ], + "finalSnapshotClaimedFromInstruction": false + }, + "surfaceEquality": { + "topLevelInstructionCount": 48, + "productionExtensionTypeCount": 29, + "compiledDecoderCoverage": "exact_100_of_100_routable_leaves", + "compiledExecutorCoverage": "partial_by_explicit_capability", + "materializationOwnership": "partial_by_unique_owner_phase_d", + "requiredFinalInvariant": "matrix == resolved interface == resolved processor dispatch == decoder coverage == projection ownership == executor capabilities", + "extensionDispatchFamilyCount": 15, + "extensionSubInstructionCount": 58, + "baseAndStandaloneTopLevelLeafCount": 33, + "embeddedTokenMetadataInstructionCount": 5, + "embeddedTokenGroupInstructionCount": 4, + "embeddedInterfaceFallbackCount": 9, + "processorRoutableLeafInstructionCount": 100 + }, + "instructionDefaults": { + "encoding": "u8_tag_plus_variant_payload", + "minimumSize": "pending_per_variant_audit", + "exactSizeOrSuffixRule": "pending_per_variant_audit", + "accounts": "pending_exact_order_flags_and_optional_accounts", + "stateTarget": "pending", + "decodedFields": "pending", + "authorizedProjections": [], + "executorSupport": { + "status": "not_started", + "reason": "decoder_matrix_and_projection_validation_required_first" + }, + "outer": true, + "inner": true, + "success": "decode", + "failure": "decode_as_uncommitted_intent_when_readable", + "proofs": { + "synthetic": [], + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested", + "realSignatures": [] + } + }, + "instructions": [ + { + "tag": 0, + "name": "initialize_mint", + "family": "base", + "status": "published_candidate_current", + "payload": "decimals_u8_mint_authority_pubkey_freeze_authority_coption" + }, + { + "tag": 1, + "name": "initialize_account", + "family": "base", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 2, + "name": "initialize_multisig", + "family": "base", + "status": "published_candidate_current", + "payload": "m_u8" + }, + { + "tag": 3, + "name": "transfer", + "family": "base", + "status": "published_candidate_deprecated", + "payload": "amount_u64_le" + }, + { + "tag": 4, + "name": "approve", + "family": "base", + "status": "published_candidate_current", + "payload": "amount_u64_le" + }, + { + "tag": 5, + "name": "revoke", + "family": "base", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 6, + "name": "set_authority", + "family": "base", + "status": "published_candidate_current", + "payload": "authority_type_u8_and_coption_pubkey" + }, + { + "tag": 7, + "name": "mint_to", + "family": "base", + "status": "published_candidate_current", + "payload": "amount_u64_le" + }, + { + "tag": 8, + "name": "burn", + "family": "base", + "status": "published_candidate_current", + "payload": "amount_u64_le" + }, + { + "tag": 9, + "name": "close_account", + "family": "base", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 10, + "name": "freeze_account", + "family": "base", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 11, + "name": "thaw_account", + "family": "base", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 12, + "name": "transfer_checked", + "family": "base", + "status": "published_candidate_current", + "payload": "amount_u64_le_decimals_u8" + }, + { + "tag": 13, + "name": "approve_checked", + "family": "base", + "status": "published_candidate_current", + "payload": "amount_u64_le_decimals_u8" + }, + { + "tag": 14, + "name": "mint_to_checked", + "family": "base", + "status": "published_candidate_current", + "payload": "amount_u64_le_decimals_u8" + }, + { + "tag": 15, + "name": "burn_checked", + "family": "base", + "status": "published_candidate_current", + "payload": "amount_u64_le_decimals_u8" + }, + { + "tag": 16, + "name": "initialize_account2", + "family": "base", + "status": "published_candidate_current", + "payload": "owner_pubkey" + }, + { + "tag": 17, + "name": "sync_native", + "family": "base", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 18, + "name": "initialize_account3", + "family": "base", + "status": "published_candidate_current", + "payload": "owner_pubkey" + }, + { + "tag": 19, + "name": "initialize_multisig2", + "family": "base", + "status": "published_candidate_current", + "payload": "m_u8" + }, + { + "tag": 20, + "name": "initialize_mint2", + "family": "base", + "status": "published_candidate_current", + "payload": "decimals_u8_mint_authority_pubkey_freeze_authority_coption" + }, + { + "tag": 21, + "name": "get_account_data_size", + "family": "base", + "status": "published_candidate_current", + "payload": "zero_or_more_extension_type_u16_le" + }, + { + "tag": 22, + "name": "initialize_immutable_owner", + "family": "immutable_owner", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 23, + "name": "amount_to_ui_amount", + "family": "base", + "status": "published_candidate_current", + "payload": "amount_u64_le" + }, + { + "tag": 24, + "name": "ui_amount_to_amount", + "family": "base", + "status": "published_candidate_current", + "payload": "utf8_remainder" + }, + { + "tag": 25, + "name": "initialize_mint_close_authority", + "family": "mint_close_authority", + "status": "published_candidate_current", + "payload": "coption_pubkey" + }, + { + "tag": 26, + "name": "transfer_fee_extension", + "family": "transfer_fee", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 27, + "name": "confidential_transfer_extension", + "family": "confidential_transfer", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 28, + "name": "default_account_state_extension", + "family": "default_account_state", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 29, + "name": "reallocate", + "family": "account_extension_management", + "status": "published_candidate_current", + "payload": "zero_or_more_extension_type_u16_le" + }, + { + "tag": 30, + "name": "memo_transfer_extension", + "family": "memo_transfer", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 31, + "name": "create_native_mint", + "family": "native_mint", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 32, + "name": "initialize_non_transferable_mint", + "family": "non_transferable", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 33, + "name": "interest_bearing_mint_extension", + "family": "interest_bearing_mint", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 34, + "name": "cpi_guard_extension", + "family": "cpi_guard", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 35, + "name": "initialize_permanent_delegate", + "family": "permanent_delegate", + "status": "published_candidate_current", + "payload": "delegate_pubkey" + }, + { + "tag": 36, + "name": "transfer_hook_extension", + "family": "transfer_hook", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 37, + "name": "confidential_transfer_fee_extension", + "family": "confidential_transfer_fee", + "status": "published_candidate_current", + "payload": "exact_subdiscriminant_and_variant_payload", + "decoderStatus": "structured_exact_wire", + "publicWire": { + "initializeConfidentialTransferFeeConfig": "tag,sub=0,authority[32],withdrawWithheldAuthorityElgamalPubkey[32]", + "withdrawWithheldTokensFromMint": "tag,sub=1,proofInstructionOffset[i8],newDecryptableAvailableBalance[36]", + "withdrawWithheldTokensFromAccounts": "tag,sub=2,numTokenAccounts[u8],proofInstructionOffset[i8],newDecryptableAvailableBalance[36]", + "harvestWithheldTokensToMint": "tag,sub=3", + "enableHarvestToMint": "tag,sub=4", + "disableHarvestToMint": "tag,sub=5" + } + }, + { + "tag": 38, + "name": "withdraw_excess_lamports", + "family": "base", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 39, + "name": "metadata_pointer_extension", + "family": "metadata_pointer", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 40, + "name": "group_pointer_extension", + "family": "group_pointer", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 41, + "name": "group_member_pointer_extension", + "family": "group_member_pointer", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 42, + "name": "confidential_mint_burn_extension", + "family": "confidential_mint_burn", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 43, + "name": "scaled_ui_amount_extension", + "family": "scaled_ui_amount", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 44, + "name": "pausable_extension", + "family": "pausable", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 45, + "name": "unwrap_lamports", + "family": "native_mint", + "status": "published_candidate_current", + "payload": "coption_u64_le" + }, + { + "tag": 46, + "name": "permissioned_burn_extension", + "family": "permissioned_burn", + "status": "published_candidate_recent", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 255, + "name": "batch", + "family": "batch", + "status": "published_candidate_current", + "payload": "bounded_sequence_pending_processor_limits" + } + ], + "extensionTypeDefaults": { + "encoding": "u16_le", + "tlvHeaderBytes": 4, + "length": "pending_exact_size_of_audit", + "padding": "pending_exact_account_len_audit", + "repeatAllowed": false, + "unknownPolicy": "preserve_bounded_opaque_entry", + "instructionFamilies": [], + "authoritiesAndFields": "pending_exact_layout_audit", + "authorizedProjections": [], + "executorSupport": "not_started", + "proofs": { + "synthetic": [], + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + } + }, + "extensionTypes": [ + { + "type": 0, + "name": "uninitialized", + "accountType": "uninitialized", + "length": 0, + "variableLength": false, + "status": "padding_sentinel" + }, + { + "type": 1, + "name": "transfer_fee_config", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 2, + "name": "transfer_fee_amount", + "accountType": "account", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 3, + "name": "mint_close_authority", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 4, + "name": "confidential_transfer_mint", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 5, + "name": "confidential_transfer_account", + "accountType": "account", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 6, + "name": "default_account_state", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 7, + "name": "immutable_owner", + "accountType": "account", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 8, + "name": "memo_transfer", + "accountType": "account", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 9, + "name": "non_transferable", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 10, + "name": "interest_bearing_config", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 11, + "name": "cpi_guard", + "accountType": "account", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 12, + "name": "permanent_delegate", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 13, + "name": "non_transferable_account", + "accountType": "account", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 14, + "name": "transfer_hook", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 15, + "name": "transfer_hook_account", + "accountType": "account", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 16, + "name": "confidential_transfer_fee_config", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 17, + "name": "confidential_transfer_fee_amount", + "accountType": "account", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 18, + "name": "metadata_pointer", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 19, + "name": "token_metadata", + "accountType": "mint", + "length": "variable", + "variableLength": true, + "status": "published_external_interface_state" + }, + { + "type": 20, + "name": "group_pointer", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 21, + "name": "token_group", + "accountType": "mint", + "variableLength": false, + "status": "published_external_interface_state" + }, + { + "type": 22, + "name": "group_member_pointer", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 23, + "name": "token_group_member", + "accountType": "mint", + "variableLength": false, + "status": "published_external_interface_state" + }, + { + "type": 24, + "name": "confidential_mint_burn", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 25, + "name": "scaled_ui_amount", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 26, + "name": "pausable", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 27, + "name": "pausable_account", + "accountType": "account", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 28, + "name": "permissioned_burn", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate_recent" + } + ], + "knownExtensionCompatibility": { + "requiredAccountExtensions": { + "transfer_fee_config": [ + "transfer_fee_amount" + ], + "non_transferable": [ + "non_transferable_account", + "immutable_owner" + ], + "transfer_hook": [ + "transfer_hook_account" + ], + "pausable": [ + "pausable_account" + ] + }, + "invalidMintCombinations": [ + "confidential_transfer_fee_config_without_transfer_fee_config_and_confidential_transfer_mint", + "transfer_fee_config_plus_confidential_transfer_mint_without_confidential_transfer_fee_config", + "confidential_mint_burn_without_confidential_transfer_mint", + "scaled_ui_amount_with_interest_bearing_config", + "non_transferable_plus_confidential_transfer_mint_without_confidential_mint_burn" + ] + }, + "materializationAudit": { + "existingOwnersInspected": [ + "token_accounts", + "admin", + "fees", + "compliance_audit", + "risk", + "lifecycle" + ], + "currentToken2022InstructionClaims": [], + "provisionalOwnership": { + "token_accounts": [ + "committed_instructional_mint_and_account_lifecycle" + ], + "admin": [ + "committed_authority_and_configuration_changes" + ], + "fees": [ + "instruction_proven_transfer_fee_parameters_and_withheld_movements_only" + ], + "compliance_audit": [ + "memo_hook_cpi_guard_and_confidential_operation_facts" + ], + "risk": [ + "stable_fact_only_no_score" + ], + "lifecycle": [ + "no_token_2022_claim_until_non_overlap_is_proven" + ] + }, + "forbiddenClaims": [ + "final_balance_snapshot", + "final_withheld_total", + "accrued_interest", + "decrypted_confidential_state", + "duplicate_parent_and_cpi_fact" + ] + }, + "elgamalRegistryBoundary": { + "workspaceConstraint": "^0.2", + "resolvedVersion": "0.2.1", + "resolvedFeatures": [ + "default" + ], + "resolutionStatus": "confirmed_by_operator_cargo_tree_2026-07-16", + "leafConsumer": "kb_decoder_spl_token_2022_for_audit_only_pending_dedicated_crate_decision", + "operatorEvidence": "cargo tree -i spl-elgamal-registry-interface -e features", + "programId": "regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg", + "programIdEvidence": "spl-elgamal-registry-interface 0.2.1 declare_id", + "registryAddressSeedUtf8": "elgamal-registry", + "status": "identity_confirmed_separate_instruction_processor_and_cluster_audit_pending", + "matrix": "separate_matrix_required", + "sharedInstructionEnum": false, + "sharedDecoder": false, + "sharedExecutor": false, + "nativeZkElgamalProofProgramId": "ZkE1Gama1Proof11111111111111111111111111111", + "requiredInstructionAudit": [ + "instruction discriminants and exact wire", + "registry PDA derivation and wallet ownership relation", + "ElGamal public key and proof-account references", + "create_or_update lifecycle and authority/signature contract", + "cluster deployment and constructible builder status" + ], + "token2022DependencyAudit": "Confidential Token-2022 instructions may reference registry accounts or ElGamal public keys, but registry instructions remain a separate Program ID surface." + }, + "processorDispatch": { + "precedence": [ + "pod_token_instruction_u8", + "token_metadata_interface_8_byte_discriminator", + "token_group_interface_8_byte_discriminator", + "invalid_instruction" + ], + "source": "program/src/processor.rs Processor::_process_inner at commit e18f9c6f9bf6044b934f48e3090e8e59e4820f02", + "extensionFamilies": [ + { + "tag": 26, + "family": "transfer_fee", + "subInstructionCount": 6, + "subInstructions": [ + "initialize_transfer_fee_config", + "transfer_checked_with_fee", + "withdraw_withheld_tokens_from_mint", + "withdraw_withheld_tokens_from_accounts", + "harvest_withheld_tokens_to_mint", + "set_transfer_fee" + ] + }, + { + "tag": 27, + "family": "confidential_transfer", + "subInstructionCount": 15, + "subInstructions": [ + "initialize_mint", + "update_mint", + "configure_account", + "approve_account", + "empty_account", + "deposit", + "withdraw", + "transfer", + "apply_pending_balance", + "enable_confidential_credits", + "disable_confidential_credits", + "enable_non_confidential_credits", + "disable_non_confidential_credits", + "transfer_with_fee", + "configure_account_with_registry" + ] + }, + { + "tag": 28, + "family": "default_account_state", + "subInstructionCount": 2, + "subInstructions": [ + "initialize", + "update" + ] + }, + { + "tag": 30, + "family": "memo_transfer", + "subInstructionCount": 2, + "subInstructions": [ + "enable", + "disable" + ] + }, + { + "tag": 33, + "family": "interest_bearing_mint", + "subInstructionCount": 2, + "subInstructions": [ + "initialize", + "update_rate" + ] + }, + { + "tag": 34, + "family": "cpi_guard", + "subInstructionCount": 2, + "subInstructions": [ + "enable", + "disable" + ] + }, + { + "tag": 36, + "family": "transfer_hook", + "subInstructionCount": 2, + "subInstructions": [ + "initialize", + "update" + ] + }, + { + "tag": 37, + "family": "confidential_transfer_fee", + "subInstructionCount": 6, + "subInstructions": [ + "initialize_confidential_transfer_fee_config", + "withdraw_withheld_tokens_from_mint", + "withdraw_withheld_tokens_from_accounts", + "harvest_withheld_tokens_to_mint", + "enable_harvest_to_mint", + "disable_harvest_to_mint" + ] + }, + { + "tag": 39, + "family": "metadata_pointer", + "subInstructionCount": 2, + "subInstructions": [ + "initialize", + "update" + ] + }, + { + "tag": 40, + "family": "group_pointer", + "subInstructionCount": 2, + "subInstructions": [ + "initialize", + "update" + ] + }, + { + "tag": 41, + "family": "group_member_pointer", + "subInstructionCount": 2, + "subInstructions": [ + "initialize", + "update" + ] + }, + { + "tag": 42, + "family": "confidential_mint_burn", + "subInstructionCount": 6, + "subInstructions": [ + "initialize_mint", + "rotate_supply_elgamal_pubkey", + "update_decryptable_supply", + "mint", + "burn", + "apply_pending_burn" + ] + }, + { + "tag": 43, + "family": "scaled_ui_amount", + "subInstructionCount": 2, + "subInstructions": [ + "initialize", + "update_multiplier" + ] + }, + { + "tag": 44, + "family": "pausable", + "subInstructionCount": 3, + "subInstructions": [ + "initialize", + "pause", + "resume" + ] + }, + { + "tag": 46, + "family": "permissioned_burn", + "subInstructionCount": 4, + "subInstructions": [ + "initialize", + "burn", + "burn_checked", + "confidential_burn" + ] + } + ], + "embeddedInterfaceFallbacks": [ + { + "interface": "spl-token-metadata-interface", + "resolvedByProcessorCargoLock": "1.0.0", + "name": "initialize", + "discriminatorHex": "d2e11ea258b84d8d", + "encoding": "8_byte_sha256_prefix_plus_bounded_borsh", + "accounts": [ + { + "index": 0, + "role": "metadata", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "update_authority", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "mint", + "writable": false, + "signer": false + }, + { + "index": 3, + "role": "mint_authority", + "writable": false, + "signer": true + } + ] + }, + { + "interface": "spl-token-metadata-interface", + "resolvedByProcessorCargoLock": "1.0.0", + "name": "update_field", + "discriminatorHex": "dde9312db5cadcc8", + "encoding": "8_byte_sha256_prefix_plus_bounded_borsh", + "accounts": [ + { + "index": 0, + "role": "metadata", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "update_authority", + "writable": false, + "signer": true + } + ] + }, + { + "interface": "spl-token-metadata-interface", + "resolvedByProcessorCargoLock": "1.0.0", + "name": "remove_key", + "discriminatorHex": "ea122038598d25b5", + "encoding": "8_byte_sha256_prefix_plus_bounded_borsh", + "accounts": [ + { + "index": 0, + "role": "metadata", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "update_authority", + "writable": false, + "signer": true + } + ] + }, + { + "interface": "spl-token-metadata-interface", + "resolvedByProcessorCargoLock": "1.0.0", + "name": "update_authority", + "discriminatorHex": "d7e4a6e45464567b", + "encoding": "8_byte_sha256_prefix_plus_exact_borsh", + "accounts": [ + { + "index": 0, + "role": "metadata", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "current_update_authority", + "writable": false, + "signer": true + } + ] + }, + { + "interface": "spl-token-metadata-interface", + "resolvedByProcessorCargoLock": "1.0.0", + "name": "emit", + "discriminatorHex": "faa6b4fa0d0cb846", + "encoding": "8_byte_sha256_prefix_plus_exact_borsh", + "accounts": [ + { + "index": 0, + "role": "metadata", + "writable": false, + "signer": false + } + ] + }, + { + "interface": "spl-token-group-interface", + "resolvedByProcessorCargoLock": "0.7.2", + "name": "initialize_group", + "discriminatorHex": "79716c2736330004", + "encoding": "8_byte_sha256_prefix_plus_exact_pod", + "accounts": [ + { + "index": 0, + "role": "group", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "mint_authority", + "writable": false, + "signer": true + } + ] + }, + { + "interface": "spl-token-group-interface", + "resolvedByProcessorCargoLock": "0.7.2", + "name": "update_group_max_size", + "discriminatorHex": "6c25ab8ff81e126e", + "encoding": "8_byte_sha256_prefix_plus_exact_pod", + "accounts": [ + { + "index": 0, + "role": "group", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "update_authority", + "writable": false, + "signer": true + } + ] + }, + { + "interface": "spl-token-group-interface", + "resolvedByProcessorCargoLock": "0.7.2", + "name": "update_group_authority", + "discriminatorHex": "a1695801edddd8cb", + "encoding": "8_byte_sha256_prefix_plus_exact_pod", + "accounts": [ + { + "index": 0, + "role": "group", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "current_update_authority", + "writable": false, + "signer": true + } + ] + }, + { + "interface": "spl-token-group-interface", + "resolvedByProcessorCargoLock": "0.7.2", + "name": "initialize_member", + "discriminatorHex": "9820deb0dfed7486", + "encoding": "8_byte_sha256_prefix_plus_exact_pod", + "accounts": [ + { + "index": 0, + "role": "member", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "member_mint", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "member_mint_authority", + "writable": false, + "signer": true + }, + { + "index": 3, + "role": "group", + "writable": true, + "signer": false + }, + { + "index": 4, + "role": "group_update_authority", + "writable": false, + "signer": true + } + ] + } + ], + "ownershipRule": "These nine interface instructions belong to the Token-2022 decoder only when the outer or inner executable program ID is Tokenz. A CPI to a third-party implementation belongs to that third-party Program ID decoder." + }, + "extensionImplementationStatus": { + "status": "all_published_extension_instruction_leaves_exact_wire_decoded", + "decodedLeafCount": 58, + "groups": [ + { + "envelopeTag": 26, + "group": "transfer_fee", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_transfer_fee_config", + "transfer_checked_with_fee", + "withdraw_withheld_tokens_from_mint", + "withdraw_withheld_tokens_from_accounts", + "harvest_withheld_tokens_to_mint", + "set_transfer_fee" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 28, + "group": "default_account_state", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_default_account_state", + "update_default_account_state" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 30, + "group": "memo_transfer", + "status": "exact_wire_decoded", + "leafInstructions": [ + "enable_required_transfer_memos", + "disable_required_transfer_memos" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 34, + "group": "cpi_guard", + "status": "exact_wire_decoded", + "leafInstructions": [ + "enable_cpi_guard", + "disable_cpi_guard" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 33, + "group": "interest_bearing_mint", + "status": "exact_wire_decoded_projected_and_official_builders_enabled", + "leafInstructions": [ + "initialize_interest_bearing_mint", + "update_interest_bearing_rate" + ], + "executorStatus": "official_interface_builders_enabled" + } + ], + "remainingOpaqueEnvelopeTags": [], + "nonClaim": "All 58 published extension instruction leaves and all nine embedded Metadata/Group interface leaves are decoded exactly; this does not claim materialization, executor, proof-context, cluster availability, or stateful preflight completion.", + "baseExtensionOperations": { + "status": "decoded_projected_and_official_builders_enabled", + "operations": [ + "reallocate", + "initialize_non_transferable_mint", + "initialize_permanent_delegate" + ] + }, + "embeddedInterfaceDecodedLeafCount": 9, + "processorDispatchEquality": "33 base leaves + 58 extension leaves + 5 metadata leaves + 4 group leaves = 100 routable leaves", + "embeddedInterfaceStatus": "exact_decoder_dispatch_added_for_all_nine_fallbacks", + "decodedExtensionGroups": [ + { + "envelopeTag": 27, + "group": "confidential_transfer", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_confidential_transfer_mint", + "update_confidential_transfer_mint", + "configure_confidential_transfer_account", + "approve_confidential_transfer_account", + "empty_confidential_transfer_account", + "deposit_confidential_tokens", + "withdraw_confidential_tokens", + "transfer_confidential_tokens", + "apply_confidential_pending_balance", + "enable_confidential_credits", + "disable_confidential_credits", + "enable_non_confidential_credits", + "disable_non_confidential_credits", + "transfer_confidential_tokens_with_fee", + "configure_confidential_transfer_account_with_registry" + ], + "executorStatus": "public_and_confidential_transfer_builders_progressively_enabled_through_transfer" + }, + { + "envelopeTag": 36, + "group": "transfer_hook", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_transfer_hook", + "update_transfer_hook_program_id" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 37, + "group": "confidential_transfer_fee", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_confidential_transfer_fee_config", + "withdraw_confidential_withheld_tokens_from_mint", + "withdraw_confidential_withheld_tokens_from_accounts", + "harvest_confidential_withheld_tokens_to_mint", + "enable_confidential_harvest_to_mint", + "disable_confidential_harvest_to_mint" + ], + "executorStatus": "pending_after_projection_and_stateful_preflight" + }, + { + "envelopeTag": 39, + "group": "metadata_pointer", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_metadata_pointer", + "update_metadata_pointer" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 40, + "group": "group_pointer", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_group_pointer", + "update_group_pointer" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 41, + "group": "group_member_pointer", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_group_member_pointer", + "update_group_member_pointer" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 42, + "group": "confidential_mint_burn", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_confidential_mint_burn", + "rotate_confidential_supply_elgamal_pubkey", + "update_confidential_decryptable_supply", + "confidential_mint", + "confidential_burn", + "apply_pending_confidential_burn" + ], + "executorStatus": "pending_after_projection_and_stateful_preflight" + }, + { + "envelopeTag": 43, + "group": "scaled_ui_amount", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_scaled_ui_amount", + "update_scaled_ui_amount_multiplier" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 44, + "group": "pausable", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_pausable_config", + "pause_token_mint", + "resume_token_mint" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 46, + "group": "permissioned_burn", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_permissioned_burn", + "permissioned_burn", + "permissioned_burn_checked", + "permissioned_confidential_burn" + ], + "executorStatus": "pending_after_projection_and_stateful_preflight" + } + ], + "exactExtensionLeafCount": 30 + } +} diff --git a/docs/SPL_TOKEN_2022_SOURCE_AUDIT.md b/docs/SPL_TOKEN_2022_SOURCE_AUDIT.md new file mode 100644 index 0000000..00d2b2a --- /dev/null +++ b/docs/SPL_TOKEN_2022_SOURCE_AUDIT.md @@ -0,0 +1,251 @@ + + + +# Audit des sources Token-2022 résolues + +## Ancrage reproductible + +L'interface Cargo résolue `spl-token-2022-interface 3.1.1` publie le commit +`e18f9c6f9bf6044b934f48e3090e8e59e4820f02`, tagué `interface@v3.1.1` le 26 juin 2026. +L'audit est effectué sur ce commit détaché, jamais sur la branche `main` mouvante. + +Le `Cargo.lock` du workspace opérateur, SHA-256 +`248c02af8c78d3bb6ad747460fee0011f8141feef83364cf697e35e44bd4ceb9`, confirme que +`kb_decoder_spl_token_2022 0.4.5` consomme directement les interfaces Token-2022 et registre +ElGamal. Pour `spl-token-2022-interface 3.1.1`, il résout exactement : + +- `solana-zk-elgamal-proof-interface 0.1.3` ; +- `spl-token-confidential-transfer-proof-extraction 0.6.1` ; +- `spl-token-group-interface 0.7.2` ; +- `spl-token-metadata-interface 1.0.1` ; +- `spl-type-length-value 0.9.1`. + +La sortie `cargo tree -p spl-token-2022-interface -e features` prouve en particulier le chemin de +consommation direct de Token Metadata `1.0.1`. Cette version diffère du `1.0.0` figé dans le +`Cargo.lock` du processor source audité : les discriminants, layouts et builders devront donc être +comparés différentiellement avant le décodeur, sans supposer leur égalité. + +La comparaison officielle clôt cette dérive : `interface@v1.0.1`, publiée le 29 juin 2026 au +commit court `7038969`, contient le correctif fusionné `fc97c60` de la pull request `87`. Le diff +de production ajoute 78 lignes dans `interface/src/instruction.rs` sans suppression. Les +discriminants, structures de payload et builders existants ne changent pas ; `unpack()` valide +désormais les longueurs Borsh `u32 LE` avant `try_from_slice` pour : + +- `Initialize` : `name`, `symbol`, `uri` ; +- `UpdateField` : la chaîne de `Field::Key` lorsque le tag vaut `3`, puis `value` ; +- `RemoveKey` : `key`, après l'octet booléen `idempotent`. + +`UpdateAuthority` et `Emit` ne transportent pas de chaîne et restent inchangés. Le décodeur devra +appliquer la garde `1.0.1` avant toute allocation, même si le processor source figé résolvait +encore `1.0.0`. + +Au même commit : + +- `program/Cargo.toml` déclare le processor `spl-token-2022 11.0.0` ; +- sa dépendance path demande l'interface `3.1.0`, satisfaite par le package workspace `3.1.1` ; +- le `Cargo.lock` source résout `spl-token-group-interface 0.7.2`, + `spl-token-metadata-interface 1.0.0` et `spl-elgamal-registry-interface 0.2.1` ; +- l'égalité du binaire déployé avec cette source reste non prouvée sur Localnet, Devnet et Mainnet. + +## Dispatch réellement implémenté + +`Processor::_process_inner` applique cet ordre : + +1. `PodTokenInstruction`, discriminant `u8` ; +2. si ce décodage échoue, `TokenMetadataInstruction`, discriminant SPL de huit octets ; +3. si ce décodage échoue, `TokenGroupInstruction`, discriminant SPL de huit octets ; +4. sinon `InvalidInstruction`. + +La surface routable contient donc : + +| Famille | Enveloppes ou formes | +|------------------------------------------------------|---------------------:| +| Tags Token-2022 `u8` | 48 | +| Familles d'extensions parmi ces tags | 15 | +| Sous-instructions de ces familles | 58 | +| Formes Token-2022 hors enveloppes | 33 | +| Instructions Token Metadata exécutées par Token-2022 | 5 | +| Instructions Token Group exécutées par Token-2022 | 4 | +| Formes feuilles routables par le processor | 100 | + +Les 48 tags ne constituent donc pas, seuls, une déclaration de couverture maximale. Les quinze +tags d'extension sont des enveloppes et doivent être remplacés par leurs 58 sous-instructions dans +le comptage des formes feuilles. + +## Interfaces incorporées au processor + +Les neuf discriminants ci-dessous sont exécutés par Token-2022 lorsqu'ils ciblent le Program ID +`TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` : + +| Interface | Instruction | Discriminant hexadécimal | +|------------------------|--------------------------|--------------------------| +| Token Metadata `1.0.0` | `initialize` | `d2e11ea258b84d8d` | +| Token Metadata `1.0.0` | `update_field` | `dde9312db5cadcc8` | +| Token Metadata `1.0.0` | `remove_key` | `ea122038598d25b5` | +| Token Metadata `1.0.0` | `update_authority` | `d7e4a6e45464567b` | +| Token Metadata `1.0.0` | `emit` | `faa6b4fa0d0cb846` | +| Token Group `0.7.2` | `initialize_group` | `79716c2736330004` | +| Token Group `0.7.2` | `update_group_max_size` | `6c25ab8ff81e126e` | +| Token Group `0.7.2` | `update_group_authority` | `a1695801edddd8cb` | +| Token Group `0.7.2` | `initialize_member` | `9820deb0dfed7486` | + +Le propriétaire du décodage dépend toujours du Program ID exécutable : une CPI vers un programme +tiers implémentant la même interface appartient au décodeur de ce programme tiers. Un TLV pointeur +reste un lien et ne transfère pas la propriété du fait métier. + +## Wire public des instructions hors enveloppes + +Les 33 tags qui ne sont pas des enveloppes d'extension ont maintenant une ligne machine-readable +dans `baseInstructionWireAudit`. Les tailles incluent toujours le tag `u8`. L'interface `3.1.1` +encode les options d'instruction de manière compacte, distincte du `COption` à quatre octets des +états de compte : `0` tient sur un octet ; `1` est suivi de 32 octets pour une clé publique ou de +huit octets pour un `u64`. + +Ce point donne notamment les tailles exactes suivantes : + +- `InitializeMint` et `InitializeMint2` : 35 octets sans freeze authority, 67 avec ; +- `SetAuthority` : 3 octets sans nouvelle autorité, 35 avec ; +- `InitializeMintCloseAuthority` : 2 octets sans autorité, 34 avec ; +- `UnwrapLamports` : 2 octets sans montant, 10 avec. + +`GetAccountDataSize` et `Reallocate` consomment tout le suffixe par éléments `ExtensionType u16 LE` ; +un suffixe impair ou un type inconnu est rejeté par l'unpacker d'interface. `UiAmountToAmount` +consomme tout le suffixe comme UTF-8, y compris une chaîne vide au seul niveau de l'interface. +`Batch` conserve tout le suffixe comme wire de records, dont les bornes et la validation processor +restent à auditer. + +La distinction critique est conservée dans la matrice : `TokenInstruction::unpack()` appelle +`unpack_with_rest()` puis ignore le reste retourné. Pour les formes à préfixe fixe ou sans payload, +un succès de cet unpacker public ne prouve donc pas la consommation de tout le buffer, ni +l'acceptation du suffixe par le runtime. Le futur décodeur devra préserver et diagnostiquer tout +suffixe borné jusqu'à ce que le chemin exact du processor figé en établisse la règle variante par +variante. + +## Règles de suffixe du processor figé + +La comparaison a ensuite été menée sur les trois fichiers exacts du commit, avec leurs empreintes +SHA-256 enregistrées dans la matrice : + +- `interface/src/instruction.rs` : `672b3890…b3a287` ; +- `program/src/pod_instruction.rs` : `7b546580…49c7e5` ; +- `program/src/processor.rs` : `9d11b834…3032d`. + +Les 33 formes hors enveloppes forment une partition complète et sans doublon : + +| Règle processor | Tags | Nombre | +|------------------------------------------------|----------------------------------------|-------:| +| longueur totale Pod exacte | `2,3,4,7,8,12,13,14,15,16,18,19,23,35` | 14 | +| préfixe Pod + option compacte, suffixe accepté | `0,6,20,25,45` | 5 | +| tag seul, suffixe non inspecté et accepté | `1,5,9,10,11,17,22,31,32,38` | 10 | +| reste entier interprété comme payload | `21,24,29` | 3 | +| flux de records `Batch` | `255` | 1 | + +`decode_instruction_data()` exige exactement `1 + size_of::()` octets. À l'inverse, les +helpers privés des options compactes lisent la structure Pod, puis `0`, ou `1` suivi de la valeur, +sans vérifier l'épuisement du buffer. Pour les dix branches sans données, le processor n'appelle +aucun décodeur après la lecture du premier octet. Ces quinze formes acceptent donc réellement un +suffixe, même si celui-ci ne porte aucune sémantique publiée. Le décodeur devra le préserver sous +forme bornée et le diagnostiquer explicitement, sans créer de champ métier. + +Pour `GetAccountDataSize` et `Reallocate`, chaque morceau de deux octets doit former un +`ExtensionType` connu ; un dernier morceau incomplet ou un type inconnu échoue. Pour +`UiAmountToAmount`, tout le reste doit être UTF-8, la chaîne vide restant admise à ce niveau. + +`Batch` ne peut pas être vide : le plus petit wire total contient le tag externe, un header de deux +octets et au moins un discriminant interne, soit quatre octets. Chaque record encode en `u8` son +nombre de comptes et la longueur totale de son instruction interne. Un header ou payload tronqué +échoue ; une longueur interne nulle échoue au dispatch ; un `Batch` interne est rejeté par +`_process_inner`. Les fallbacks Token Metadata et Token Group restent en revanche joignables dans +un record lorsque leur wire tient dans les 255 octets. Le processor n'impose pas de nombre maximal +explicite de records au-delà des limites de transaction et de calcul : le décodeur devra donc poser +sa propre borne. + +## Comptes publiés par les builders de base + +La matrice contient désormais une ligne ordonnée pour chacune des 33 formes hors enveloppes. Elle +enregistre les metas exactes des 32 helpers Rust publiés : rôle, `writable`, `signer`, compte +optionnel et queue multisig. `Batch` reste la seule forme sans helper Rust dédié pour construire +ses metas ; `TokenInstruction::pack()` ne produit que ses données. + +Les autorités simples et multisig partagent le contrat classique des builders : l'autorité est +signataire lorsque la liste `signer_pubkeys` est vide ; sinon elle ne signe pas et chaque élément +de la queue est ajouté en lecture seule avec le flag signataire. Le builder ne borne pas la taille +de cette queue pour les opérations d'autorité et conserve ordre et doublons. Le runtime ne peut +toutefois satisfaire qu'un multisig stockant de un à onze positions. + +`InitializeMultisig` et `InitializeMultisig2` ont un contrat différent : les clés de configuration +ne signent pas. Le builder exige `m` et `n` dans `1..=11` ainsi que `m <= n`. Le processor consomme +tous les comptes restants comme clés de configuration, valide séparément `m` et `n`, mais ne +répète pas le contrôle `m <= n`. Une instruction manuelle `m > n` peut donc initialiser un état que +le builder refuse. Le décodeur doit conserver ce fait et le diagnostiquer sans le normaliser. + +`SyncNative` publie deux helpers : le premier fournit seulement le compte natif modifiable ; le +second ajoute Rent en lecture seule. Le processor traite effectivement le deuxième compte comme +optionnel, mais ignore ceux qui suivraient. `Batch` découpe les comptes selon le compteur `u8` de +chaque record ; les comptes externes restant après le dernier record ne sont pas rejetés. Ils +devront rester visibles comme comptes non assignés. + +Le contrat builder de `Reallocate` est fixé — compte à agrandir modifiable, payer modifiable et +signataire, System Program, propriétaire conditionnel puis queue multisig — mais la consommation +runtime détaillée reste ouverte jusqu'à reconfirmation du module séparé +`program/src/extension/reallocate.rs` au même commit. Cette limite empêche de déclarer l'audit +processor des comptes entièrement clos. + +## Sources auditées + +- dépôt `solana-program/token-2022`, commit figé ci-dessus ; +- `interface/src/instruction.rs` et `interface/src/extension/**/instruction.rs` ; +- `interface/src/extension/mod.rs` et les états d'extension ; +- `program/src/processor.rs` et `program/src/extension/**/processor.rs` ; +- dépôt `solana-program/token-metadata`, commit + `552935e00710478541e3394a8b2624c9ba7f503f`, tag `interface@v1.0.0` ; +- dépôt `solana-program/token-group`, commit + `2645064953937118572735993ed0ac1d07db5f14`, tag `interface@v0.7.2`. + +## Limites restantes + +Cette tranche confirme le wire public, les règles runtime de suffixe et les metas officielles des +builders des 33 formes hors enveloppes. Elle ne clôt pas encore l'audit par variante : consommation +processor complète des comptes, données de preuve, builders des extensions, statuts de +construction et compatibilité cluster doivent encore être renseignés avant le décodeur. + +## Première tranche verticale d'extensions + +Le décodeur structure désormais exactement douze feuilles issues de quatre enveloppes : les six +instructions Transfer Fee, les deux Default Account State, les deux Memo Transfer et les deux CPI +Guard. Les discriminants, champs numériques, options compactes et règles de longueur proviennent +des interfaces figées au commit audité. Les onze autres enveloppes restent opaques et ne sont pas +comptées comme décodées sémantiquement. + +Cette tranche ne déclare encore aucune capacité exécuteur d'extension. Conformément à l'ordre du +jalon, les projections propriétaires et leurs tests de non-duplication doivent être validés avant +d'activer les builders officiels correspondants. + +## Dispatch incorporé Token Metadata et Token Group + +Le dispatch de production ne peut pas se limiter au premier octet de `TokenInstruction`. Après +échec du décodage Token-2022 principal, le processor `11.0.0` audité tente successivement les +interfaces Token Metadata puis Token Group. Les neuf discriminants déjà inventoriés dans la +matrice sont donc désormais reconnus par le décodeur Token-2022 lorsque le Program ID réellement +exécuté est `Tokenz...` : cinq feuilles Metadata et quatre feuilles Group. + +Les chaînes Borsh Metadata sont bornées avant allocation et décodage. Les layouts Pod Group sont +traités avec une longueur exacte. Cette intégration ne décode pas un programme tiers implémentant +les mêmes interfaces : une CPI vers un autre Program ID reste la propriété du décodeur de ce +programme. + +L'égalité de surface à maintenir est désormais explicitement compilée et documentée : + +```text +33 feuilles hors enveloppes + 58 feuilles d'extensions + 5 Metadata + 4 Group = 100 +``` + +## Audit ElGamal restant + +La présence de `spl-elgamal-registry-interface 0.2.1`, de son Program ID `regVY...` et de la seed +`elgamal-registry` ne suffit pas à revendiquer sa couverture. La tranche dédiée doit encore fixer +les discriminants et le wire exacts, la dérivation PDA, la relation wallet/owner, les clés ElGamal, +les références de preuve, le lifecycle create/update, les autorités et signataires, les builders +actuels ou expérimentaux et les preuves de déploiement cluster. Toute référence au registre depuis +une instruction confidentielle Token-2022 reste un lien inter-programme et non une instruction du +registre. diff --git a/docs/SPL_TOKEN_2022_VALIDATION_MATRIX.json b/docs/SPL_TOKEN_2022_VALIDATION_MATRIX.json new file mode 100644 index 0000000..8cedd27 --- /dev/null +++ b/docs/SPL_TOKEN_2022_VALIDATION_MATRIX.json @@ -0,0 +1,146 @@ +{ + "matrixVersion": 2, + "milestone": "0.4.6", + "status": "partially_confirmed", + "statusVocabulary": [ + "not_run", + "simulated", + "submitted", + "confirmed", + "unavailable", + "failed" + ], + "scenarios": [ + { + "id": "offline_full_regression", + "environment": "offline", + "status": "confirmed", + "requiredEvidence": [ + "test_suite", + "clippy" + ], + "evidence": [ + { + "kind": "test_suite", + "value": "kb_pipeline 120/120" + }, + { + "kind": "test_suite", + "value": "kb_executor_spl_token_2022 44/44" + }, + { + "kind": "test_suite", + "value": "kb_executor_spl_elgamal_registry 6/6" + }, + { + "kind": "clippy", + "value": "cargo clippy --all-targets clean" + } + ] + }, + { + "id": "token_2022_public_devnet", + "environment": "devnet", + "status": "not_run", + "requiredEvidence": [ + "simulation", + "signature", + "canonical_hydration", + "core_extraction", + "decode_replay", + "materialization", + "second_replay" + ] + }, + { + "id": "elgamal_registry_devnet", + "environment": "devnet", + "status": "not_run", + "requiredEvidence": [ + "simulation", + "signature", + "canonical_hydration", + "core_extraction", + "decode_replay", + "materialization", + "second_replay" + ] + }, + { + "id": "confidential_transfer_localnet_or_devnet", + "environment": "localnet_or_devnet", + "status": "not_run", + "requiredEvidence": [ + "simulation", + "proof_mode", + "signature", + "canonical_hydration", + "core_extraction", + "decode_replay", + "materialization", + "second_replay" + ] + }, + { + "id": "confidential_mint_burn_localnet_or_devnet", + "environment": "localnet_or_devnet", + "status": "not_run", + "requiredEvidence": [ + "simulation", + "proof_mode", + "signature", + "canonical_hydration", + "core_extraction", + "decode_replay", + "materialization", + "second_replay" + ] + }, + { + "id": "permissioned_confidential_burn_localnet_or_devnet", + "environment": "localnet_or_devnet", + "status": "not_run", + "requiredEvidence": [ + "simulation", + "proof_mode", + "signature", + "canonical_hydration", + "core_extraction", + "decode_replay", + "materialization", + "second_replay" + ] + }, + { + "id": "mainnet_observation_corpus", + "environment": "mainnet_observation", + "status": "not_run", + "requiredEvidence": [ + "signatures", + "outer_cpi", + "success_failure" + ] + }, + { + "id": "postgres_double_replay", + "environment": "postgres", + "status": "not_run", + "requiredEvidence": [ + "postgres_tests", + "first_replay", + "second_replay", + "no_duplicates" + ] + }, + { + "id": "tauri_smoke", + "environment": "tauri", + "status": "not_run", + "requiredEvidence": [ + "startup", + "routes", + "journal" + ] + } + ] +} diff --git a/docs/SPL_TOKEN_MATRIX.json b/docs/SPL_TOKEN_MATRIX.json new file mode 100644 index 0000000..2030d4c --- /dev/null +++ b/docs/SPL_TOKEN_MATRIX.json @@ -0,0 +1,1440 @@ +{ + "file": "docs/SPL_TOKEN_MATRIX.json", + "version": 3, + "schemaVersion": 2, + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "interface": { + "workspaceConstraint": "^3.0", + "expectedResolvedVersion": "3.0.0", + "cargoResolutionStatus": "validated_by_workspace_cargo_2026-07-15", + "features": [], + "bincodeEnabled": false, + "officialTag": "interface@v3.0.0", + "sourceCommit": "46eaacd", + "instructionSource": "https://github.com/solana-program/token/blob/interface@v3.0.0/interface/src/instruction.rs", + "stateSource": "https://github.com/solana-program/token/blob/interface@v3.0.0/interface/src/state.rs", + "legacyProcessorSource": "https://github.com/solana-program/token/blob/interface@v3.0.0/program/src/processor.rs", + "pTokenRelease": "p-token@v1.0.0" + }, + "limits": { + "minSigners": 1, + "maxSigners": 11, + "maxBatchInstructions": 64, + "maxBatchAccounts": 512, + "maxInstructionBytes": 16384, + "nestedBatchAllowed": false + }, + "deploymentAudit": { + "localnet": { + "status": "not_tested", + "evidence": [] + }, + "devnet": { + "status": "partially_validated", + "evidence": [ + "TransferChecked simulated and submitted with canonical post-validation on 2026-07-15", + "5Q71KeomfhHBueDsD92C7L21vHUpFGZnZzCFPGaCDAMHx6yV4Jy2fMDzCpze8mNr2c1GLSRT93mmBaYy2RGZ95ZY", + "Controlled no-ATA lifecycle validated InitializeAccount3, MintToChecked, TransferChecked, ApproveChecked, Revoke, BurnChecked and CloseAccount with canonical replay and idempotence on 2026-07-15", + "Batch tag 255 simulation-only succeeded on the classic program with one zero-amount TransferChecked child on 2026-07-16; 270 compute units; no runtime error", + "UnwrapLamports tag 45 simulation-only succeeded for one lamport on a non-associated classic native token account on 2026-07-16; 140 compute units; no runtime error" + ] + }, + "mainnet": { + "status": "real_corpus_decode_and_materialization_validated", + "evidence": [ + "370 classic SPL Token instructions decoded without unsupported, failed or unmatched input on 2026-07-15", + "371 committed materializations; 8 refused outputs matched 8 failed transactions; second replay selected 0 inputs", + "Observed materialized operations: approve 9, burn_checked 1, close_account 79, initialize_account 3, sync_native 56, transfer 2, transfer_checked 212" + ] + }, + "note": "Publication in interface 3.0.0 is tracked separately from processor deployment and successful simulation." + }, + "corpusAudit": { + "mainnet": { + "source": "real canonical transactions replayed from PostgreSQL", + "observedNormalizedOperations": { + "approve": 9, + "burn_checked": 1, + "close_account": 79, + "initialize_account": 3, + "sync_native": 56, + "transfer": 2, + "transfer_checked": 212 + }, + "materializedFamilies": { + "token_account": 362, + "risk": 9 + }, + "invariants": { + "uncommittedOutputs": 0, + "duplicatedBatchParents": 0, + "classicTokenFeeOutputs": 0, + "secondReplaySelected": 0 + }, + "normalizedOperationCaveat": "initialize_account is intentionally not assigned to a specific wire tag because the materialized operation name normalizes historical and current initialization variants" + }, + "devnet": { + "source": "controlled opt-in execution scenarios without ATA", + "transferChecked": "simulation, submission, confirmation, canonical hydration, core extraction, decode, one materialization and idempotent replay validated", + "lifecycle": "raw 82/165/165 account preparation followed by eleven simulation-first Token transactions; recovery after public RPC throttling was validated", + "recentInstructionProbe": { + "date": "2026-07-16", + "submission": false, + "batch": { + "success": true, + "computeUnits": 270, + "child": "TransferChecked amount=0", + "runtimeError": null + }, + "unwrapLamports": { + "success": true, + "computeUnits": 140, + "amountLamports": "1", + "nativeAccount": "3DjMgMbaNU4nLbv8avkZuTPXc31khcfp1bMPV2AcJkZk", + "runtimeError": null + } + } + } + }, + "instructions": [ + { + "name": "initialize_mint", + "tag": 0, + "status": "historical", + "layout": "1 + u8 + Pubkey + COption; 35 or 67 bytes", + "allowedSizes": [ + 35, + 67 + ], + "accounts": [ + "mint(w)", + "rent_sysvar" + ], + "authorityForm": "not_applicable", + "decodedFields": [ + "decimals", + "mint_authority", + "freeze_authority" + ], + "limits": [], + "event": "spl_token.initialize_mint", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "initialize_mint" + }, + "executorSupport": { + "status": "unsupported", + "reasonCode": "execution_spl_token_historical_variant_decode_only", + "reason": "obsolete Rent-dependent initialization variant; the decoder remains exhaustive and the executor uses the current no-Rent builder" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "initialize_account", + "tag": 1, + "status": "historical", + "layout": "tag only; 1 byte", + "allowedSizes": null, + "accounts": [ + "token_account(w)", + "mint", + "owner", + "rent_sysvar" + ], + "authorityForm": "not_applicable", + "decodedFields": [], + "limits": [], + "event": "spl_token.initialize_account", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "initialize_account" + }, + "executorSupport": { + "status": "unsupported", + "reasonCode": "execution_spl_token_historical_variant_decode_only", + "reason": "obsolete Rent-dependent initialization variant; the decoder remains exhaustive and the executor uses the current no-Rent builder" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "initialize_multisig", + "tag": 2, + "status": "historical", + "layout": "1 + u8; 2 bytes", + "allowedSizes": null, + "accounts": [ + "multisig_account(w)", + "rent_sysvar", + "members[1..11]" + ], + "authorityForm": "not_applicable", + "decodedFields": [ + "m", + "n_from_accounts" + ], + "limits": [ + "1 <= M <= N <= 11" + ], + "event": "spl_token.initialize_multisig", + "projections": [ + "token_accounts", + "admin", + "risk" + ], + "officialBuilder": { + "available": true, + "function": "initialize_multisig" + }, + "executorSupport": { + "status": "unsupported", + "reasonCode": "execution_spl_token_historical_variant_decode_only", + "reason": "obsolete Rent-dependent initialization variant; the decoder remains exhaustive and the executor uses the current no-Rent builder" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "transfer", + "tag": 3, + "status": "historical", + "layout": "1 + u64 LE; 9 bytes", + "allowedSizes": null, + "accounts": [ + "source(w)", + "destination(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "amount_raw" + ], + "limits": [], + "event": "spl_token.transfer", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "transfer" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.transfer", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "real_transaction_decoded_and_materialized_2026-07-15" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "5hNLki1wTmRtSkfi3DPmNPB91mGaCBmJ2KDY7bvvaQXPPNFdgYgJcuqQtmsidccbdZo73Du13K8rDbwvZd2fHfvh" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "approve", + "tag": 4, + "status": "historical", + "layout": "1 + u64 LE; 9 bytes", + "allowedSizes": null, + "accounts": [ + "source(w)", + "delegate", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "amount_raw" + ], + "limits": [], + "event": "spl_token.approve", + "projections": [ + "token_accounts", + "risk" + ], + "officialBuilder": { + "available": true, + "function": "approve" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.approve", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "real_transaction_decoded_and_materialized_2026-07-15" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "25GkUzBPcXWgBi6nAu8RAHRsohidKFVCVhmBMdy3Xr9JNZdjvJeiFBmdtQpyadEVx4BfVJZLyifwf9xsDyBEyh84" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "revoke", + "tag": 5, + "status": "historical", + "layout": "tag only; 1 byte", + "allowedSizes": null, + "accounts": [ + "source(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [], + "limits": [], + "event": "spl_token.revoke", + "projections": [ + "token_accounts", + "risk" + ], + "officialBuilder": { + "available": true, + "function": "revoke" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.revoke", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "submission_confirmation_and_replay_validated_2026-07-15", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "3osU5jfqDPdMqQKjTnRtvJLaQBxkwRCAJeSU1zEGCMHC5jFHAd5dV4SnHGK2hc6aBkLNLSzvqD7VgVjLxFfhk4MM" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "set_authority", + "tag": 6, + "status": "historical", + "layout": "1 + AuthorityType u8 + COption; 3 or 35 bytes", + "allowedSizes": [ + 3, + 35 + ], + "accounts": [ + "authority_target(w)", + "current_authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "authority_type", + "new_authority" + ], + "limits": [], + "event": "spl_token.set_authority", + "projections": [ + "admin", + "risk" + ], + "officialBuilder": { + "available": true, + "function": "set_authority" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.set_authority", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "mint_to", + "tag": 7, + "status": "historical", + "layout": "1 + u64 LE; 9 bytes", + "allowedSizes": null, + "accounts": [ + "mint(w)", + "destination(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "amount_raw" + ], + "limits": [], + "event": "spl_token.mint_to", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "mint_to" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.mint_to", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "burn", + "tag": 8, + "status": "historical", + "layout": "1 + u64 LE; 9 bytes", + "allowedSizes": null, + "accounts": [ + "source(w)", + "mint(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "amount_raw" + ], + "limits": [], + "event": "spl_token.burn", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "burn" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.burn", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "close_account", + "tag": 9, + "status": "historical", + "layout": "tag only; 1 byte", + "allowedSizes": null, + "accounts": [ + "source(w)", + "destination(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [], + "limits": [], + "event": "spl_token.close_account", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "close_account" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.close_account", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "submission_confirmation_and_replay_validated_2026-07-15", + "mainnet": "real_transaction_decoded_and_materialized_2026-07-15" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "27iYTpo9X5QNyQCXnfeHiVZqnqhszvrgsUDeC63ukjEF4KtYGPNJ4o4Vg4FGuzke5oHxybDnVvxFFMeqnHF2rSCB", + "hVS2YtL8zEbFPM1tyisWNScBoEkzfxDGeZKeZBpxA8rgEg6mQZXV67xdq6YUkhQX4GrfhPm87gvxupi9k56P9J8", + "4XUCaYXpeXPMBUmfHEWTzgiyoQUZNnF9jaZDAdrgi2qXABi6ZFBcuCUhgHQXqWtUb7V1gEVWXWmvZJKuyaNDqGrp" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "freeze_account", + "tag": 10, + "status": "historical", + "layout": "tag only; 1 byte", + "allowedSizes": null, + "accounts": [ + "token_account(w)", + "mint", + "freeze_authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [], + "limits": [], + "event": "spl_token.freeze_account", + "projections": [ + "token_accounts", + "risk" + ], + "officialBuilder": { + "available": true, + "function": "freeze_account" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.freeze_account", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "thaw_account", + "tag": 11, + "status": "historical", + "layout": "tag only; 1 byte", + "allowedSizes": null, + "accounts": [ + "token_account(w)", + "mint", + "freeze_authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [], + "limits": [], + "event": "spl_token.thaw_account", + "projections": [ + "token_accounts", + "risk" + ], + "officialBuilder": { + "available": true, + "function": "thaw_account" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.thaw_account", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "transfer_checked", + "tag": 12, + "status": "current", + "layout": "1 + u64 LE + u8; 10 bytes", + "allowedSizes": null, + "accounts": [ + "source(w)", + "mint", + "destination(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "amount_raw", + "decimals" + ], + "limits": [], + "event": "spl_token.transfer_checked", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "transfer_checked" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.transfer_checked", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "simulation_submission_confirmation_and_replay_validated_2026-07-15", + "mainnet": "real_transaction_decoded_and_materialized_2026-07-15" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "5Q71KeomfhHBueDsD92C7L21vHUpFGZnZzCFPGaCDAMHx6yV4Jy2fMDzCpze8mNr2c1GLSRT93mmBaYy2RGZ95ZY", + "2Xum6TUAKvu92gBUTCYS3LFtnYbMiR3bDq5VnrRS846ZgicsURqJrbD6LsJAJJD2jG3TPXVsMLY8UJx8NxDqPPbF", + "2125FHp6VjKaFApgDebwww1dGj1SFayKCwZPTwEieRWgKKGLe27Yjk9qKGT9zzc5Pc3Lsu9CRt8TDkJKdgJ1qpRP" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "approve_checked", + "tag": 13, + "status": "current", + "layout": "1 + u64 LE + u8; 10 bytes", + "allowedSizes": null, + "accounts": [ + "source(w)", + "mint", + "delegate", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "amount_raw", + "decimals" + ], + "limits": [], + "event": "spl_token.approve_checked", + "projections": [ + "token_accounts", + "risk" + ], + "officialBuilder": { + "available": true, + "function": "approve_checked" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.approve_checked", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "submission_confirmation_and_replay_validated_2026-07-15", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "4dC5AeizWUMSToEH8xjWjuUqK7GjBUEPD5kWGdi9LpdVBRni38dqocXjA8Wcx2SnLSAUKPaV3JbzKdpWAEu8ZXAR" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "mint_to_checked", + "tag": 14, + "status": "current", + "layout": "1 + u64 LE + u8; 10 bytes", + "allowedSizes": null, + "accounts": [ + "mint(w)", + "destination(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "amount_raw", + "decimals" + ], + "limits": [], + "event": "spl_token.mint_to_checked", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "mint_to_checked" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.mint_to_checked", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "submission_confirmation_and_replay_validated_2026-07-15", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "3yr7CtHeuQPFWFp424Q3NdPA1CUNm1xKd5z6hy1sW5HmbXnkudg4wXFmNhHo8mBJok1xHqqF9uaftqZugj4Aiw3f" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "burn_checked", + "tag": 15, + "status": "current", + "layout": "1 + u64 LE + u8; 10 bytes", + "allowedSizes": null, + "accounts": [ + "source(w)", + "mint(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "amount_raw", + "decimals" + ], + "limits": [], + "event": "spl_token.burn_checked", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "burn_checked" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.burn_checked", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "submission_confirmation_and_replay_validated_2026-07-15", + "mainnet": "real_transaction_decoded_and_materialized_2026-07-15" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "34nijQGRnUoDj6hmdh8fWw5p33dEBxDPG5n4x8QBE4TdzbzathYgCmxzJvutyHhgXj3dqwZgcVdbjtmqZKNDHC3i", + "5sZpdTv1YYLpQ8AynL2eMMeFVwJfCPp8Pwf2KC93XQDM6NiQo9FVvZaL5qp1EFqY98k6ZFTVEirhKoUP7xck8bDg", + "5mk53ez5rJfCPAGUW3uHbYJ1bkBhPNRKi9NAuk6gknXp9CQtKtqUxkVdy6dnxmRTMmewwuFY865V9J72TrwESH5g" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "initialize_account2", + "tag": 16, + "status": "historical", + "layout": "1 + Pubkey; 33 bytes", + "allowedSizes": null, + "accounts": [ + "token_account(w)", + "mint", + "rent_sysvar" + ], + "authorityForm": "not_applicable", + "decodedFields": [ + "owner" + ], + "limits": [], + "event": "spl_token.initialize_account2", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "initialize_account2" + }, + "executorSupport": { + "status": "unsupported", + "reasonCode": "execution_spl_token_historical_variant_decode_only", + "reason": "obsolete Rent-dependent initialization variant; the decoder remains exhaustive and the executor uses the current no-Rent builder" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "sync_native", + "tag": 17, + "status": "current", + "layout": "tag only; 1 byte", + "allowedSizes": null, + "accounts": [ + "native_token_account(w)", + "optional_rent_sysvar" + ], + "authorityForm": "not_applicable", + "decodedFields": [], + "limits": [], + "event": "spl_token.sync_native", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "sync_native" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.sync_native", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "real_transaction_decoded_and_materialized_2026-07-15" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "27iYTpo9X5QNyQCXnfeHiVZqnqhszvrgsUDeC63ukjEF4KtYGPNJ4o4Vg4FGuzke5oHxybDnVvxFFMeqnHF2rSCB" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "initialize_account3", + "tag": 18, + "status": "current", + "layout": "1 + Pubkey; 33 bytes", + "allowedSizes": null, + "accounts": [ + "token_account(w)", + "mint" + ], + "authorityForm": "not_applicable", + "decodedFields": [ + "owner" + ], + "limits": [], + "event": "spl_token.initialize_account3", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "initialize_account3" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.initialize_account", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "submission_confirmation_and_replay_validated_2026-07-15", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "3yxsCf8tc1H2P9E3m6Kgu7J3XVeBw7wePuRTHvCaidQ74bsa2TP5xg4hpXAw1jkRSB3LrkLrXgx5eqvCQqRsXcfq" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "initialize_multisig2", + "tag": 19, + "status": "current", + "layout": "1 + u8; 2 bytes", + "allowedSizes": null, + "accounts": [ + "multisig_account(w)", + "members[1..11]" + ], + "authorityForm": "not_applicable", + "decodedFields": [ + "m", + "n_from_accounts" + ], + "limits": [ + "1 <= M <= N <= 11" + ], + "event": "spl_token.initialize_multisig2", + "projections": [ + "token_accounts", + "admin", + "risk" + ], + "officialBuilder": { + "available": true, + "function": "initialize_multisig2" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.initialize_multisig", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "initialize_mint2", + "tag": 20, + "status": "current", + "layout": "1 + u8 + Pubkey + COption; 35 or 67 bytes", + "allowedSizes": [ + 35, + 67 + ], + "accounts": [ + "mint(w)" + ], + "authorityForm": "not_applicable", + "decodedFields": [ + "decimals", + "mint_authority", + "freeze_authority" + ], + "limits": [], + "event": "spl_token.initialize_mint2", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "initialize_mint2" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.initialize_mint", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "get_account_data_size", + "tag": 21, + "status": "current", + "layout": "tag; runtime ignores trailing data", + "allowedSizes": null, + "accounts": [ + "mint" + ], + "authorityForm": "not_applicable", + "decodedFields": [], + "limits": [], + "event": "spl_token.get_account_data_size", + "projections": [], + "officialBuilder": { + "available": true, + "function": "get_account_data_size" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.get_account_data_size", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": true, + "coreReplayValidation": "unavailable_in_current_core_input" + }, + "notes": [] + }, + { + "name": "initialize_immutable_owner", + "tag": 22, + "status": "current", + "layout": "tag only; classic runtime compatibility no-op", + "allowedSizes": null, + "accounts": [ + "token_account(w)" + ], + "authorityForm": "not_applicable", + "decodedFields": [], + "limits": [], + "event": "spl_token.initialize_immutable_owner", + "projections": [], + "officialBuilder": { + "available": true, + "function": "initialize_immutable_owner" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.initialize_immutable_owner", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [ + "Classic SPL Token compatibility no-op; no Token-2022 extension is inferred." + ] + }, + { + "name": "amount_to_ui_amount", + "tag": 23, + "status": "current", + "layout": "1 + u64 LE; 9 bytes", + "allowedSizes": null, + "accounts": [ + "mint" + ], + "authorityForm": "not_applicable", + "decodedFields": [ + "amount_raw" + ], + "limits": [], + "event": "spl_token.amount_to_ui_amount", + "projections": [], + "officialBuilder": { + "available": true, + "function": "amount_to_ui_amount" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.amount_to_ui_amount", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": true, + "coreReplayValidation": "unavailable_in_current_core_input" + }, + "notes": [] + }, + { + "name": "ui_amount_to_amount", + "tag": 24, + "status": "current", + "layout": "1 + UTF-8 remainder; at least 1 byte at wire layer", + "allowedSizes": null, + "accounts": [ + "mint" + ], + "authorityForm": "not_applicable", + "decodedFields": [ + "ui_amount_utf8" + ], + "limits": [], + "event": "spl_token.ui_amount_to_amount", + "projections": [], + "officialBuilder": { + "available": true, + "function": "ui_amount_to_amount" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.ui_amount_to_amount", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": true, + "coreReplayValidation": "unavailable_in_current_core_input" + }, + "notes": [] + }, + { + "name": "withdraw_excess_lamports", + "tag": 38, + "status": "current", + "layout": "tag only; 1 byte", + "allowedSizes": null, + "accounts": [ + "source(w)", + "destination(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [], + "limits": [], + "event": "spl_token.withdraw_excess_lamports", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "withdraw_excess_lamports" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.withdraw_excess_lamports", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "unwrap_lamports", + "tag": 45, + "status": "recent", + "layout": "1 + COption; 2 or 10 bytes", + "allowedSizes": [ + 2, + 10 + ], + "accounts": [ + "native_token_account(w)", + "destination(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "optional_amount_lamports" + ], + "limits": [], + "event": "spl_token.unwrap_lamports", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "unwrap_lamports" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.unwrap_lamports", + "constructibility": "official_builder_proven", + "clusterDeployment": "devnet_simulation_succeeded_2026-07-16" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "simulation_succeeded_2026-07-16", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [ + "Published by interface 3.0.0; cluster deployment requires separate simulation evidence.", + "Devnet simulation-only succeeded for one lamport from a controlled non-associated wrapped SOL account; 140 compute units and no runtime error." + ] + }, + { + "name": "batch", + "tag": 255, + "status": "recent", + "layout": "1 + repeated(u8 account_count, u8 data_len, instruction_data[data_len])", + "allowedSizes": null, + "accounts": [ + "concatenated sub-instruction account slices" + ], + "authorityForm": "not_applicable", + "decodedFields": [ + "ordered_sub_instructions", + "account_slices" + ], + "limits": [ + "64 sub-instructions", + "512 total accounts", + "255 bytes per child", + "no nested Batch" + ], + "event": "spl_token.batch", + "projections": [ + "child_dependent" + ], + "officialBuilder": { + "available": true, + "function": "batch" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.batch", + "constructibility": "official_builder_proven", + "clusterDeployment": "devnet_simulation_succeeded_2026-07-16" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "simulation_succeeded_2026-07-16", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": true, + "coreReplayValidation": "unavailable_in_current_core_input" + }, + "notes": [ + "Child identities derive from parent instruction path and child position.", + "Devnet simulation-only succeeded with one zero-amount TransferChecked child; 270 compute units and no runtime error." + ] + } + ] +} diff --git a/docs/SQL_DEMOS.md b/docs/SQL_DEMOS.md new file mode 100644 index 0000000..51e7000 --- /dev/null +++ b/docs/SQL_DEMOS.md @@ -0,0 +1,109 @@ + + + +# Démos SQL + +## Fenêtres disponibles + +| Fenêtre | Rôle | +|------------------------------|------------------------------------------------------------------------------------------------------| +| `demo_sql_diag` | Profil, backend, DSN masqué, santé PostgreSQL et tables attendues. | +| `demo_sql_pg_raw` | Transactions canoniques et observations d’acquisition. | +| `demo_sql_pg_core` | Transactions, comptes, instructions, inner instructions, logs, balances et ledger. | +| `demo_sql_replay_candidates` | Exploration read-only, filtrage et export des signatures, programmes, mints, owners et account keys. | + +Depuis `0.3.4`, le diagnostic core inclut : + +```text +kb_sol_ops_processing_ledger +``` + +La fenêtre `demo_core_extraction` exécute le worker puis permet d’ouvrir les diagnostics raw/core et le sélecteur de candidats. Les fenêtres SQL restent read-only et ne modifient aucune donnée. + +## Sélecteur de candidats replay + +`demo_sql_replay_candidates` sépare l’exploration en cinq tableaux DataTables avec intégration Bootstrap 5 : + +1. transactions et signatures ; +2. programmes observés dans les instructions outer, inner et dans les logs reliés ; +3. mints ; +4. owners ; +5. account keys. + +Les requêtes PostgreSQL sont statiques, paramétrées et limitées à `5 000` lignes au maximum par chargement. Aucun éditeur SQL libre n’est exposé. + +### Transactions et signatures + +Filtres serveur disponibles : + +- fragment de signature ; +- plage inclusive de slots ; +- état raw ; +- statut du ledger `core_extraction` ; +- program ID exact avec portée `any`, `outer`, `inner` ou `logs` ; +- mint, owner ou account key exact ; +- limite et ordre des slots. + +Le tableau affiche aussi la présence du graphe core, le statut du ledger, sa version et son nombre de tentatives, ainsi que les cardinalités outer/inner. + +### Programmes + +Le tableau agrège les occurrences issues de : + +```text +kb_sol_core_instructions +kb_sol_core_inner_instructions +kb_sol_core_logs +``` + +Chaque ligne expose le code connu de `kb_program_ids` lorsqu’il existe, le nombre de transactions distinctes, d’instructions outer, d’instructions inner, de logs reliés et la plage de slots observée. Le filtre propose une liste des programmes enregistrés tout en conservant une saisie libre pour les identifiants inconnus ou partiels. + +Pour appliquer un programme au tableau des transactions, il faut cocher exactement une ligne puis utiliser « Utiliser comme filtre transactions ». Lorsqu’aucune ligne n’est cochée, l’action accepte aussi une seule ligne restant après le filtre local DataTables. + +### Mints, owners et comptes + +Les trois familles disposent de tableaux et filtres indépendants. Les mints et owners proviennent de `kb_sol_core_balance_changes`; les account keys proviennent de `kb_sol_core_account_keys` et ne sont pas classifiées automatiquement comme wallet, pool, mint ou token account. + +Chaque tableau affiche transactions distinctes, occurrences, occurrences par transaction, slots minimum/maximum et étendue. Une valeur sélectionnée peut être appliquée directement au filtre du tableau des transactions. + +### Sélection et export + +Dans chaque tableau : + +- la première colonne contient une case à cocher gérée par l’extension DataTables Select ; +- la case du header sélectionne ou désélectionne toutes les lignes correspondant au filtre local ; +- une sélection explicite est prioritaire ; +- sans sélection, la copie et l’export utilisent toutes les lignes correspondant au filtre local DataTables ; +- les identifiants peuvent être copiés dans le presse-papiers sous forme multiligne ; +- le frontend construit un CSV diagnostique avec BOM UTF-8, séparateur `;` et CRLF ; +- une commande Rust bornée écrit le fichier dans `data/exports_csv/` et ajoute un suffixe lorsque le nom existe déjà ; +- les valeurs longues sont tronquées visuellement, exposées intégralement par tooltip et copiables cellule par cellule ; +- tous les tableaux affichent 10 lignes par défaut et possèdent un reset complet des filtres. + +La copie multiligne des signatures peut être collée telle quelle dans le mode `Signatures explicites` de `demo_core_extraction`. L’export ne dépend plus du téléchargement HTML du WebView. + +### Lecture des colonnes transactions + +Les cardinalités sont séparées en quatre colonnes : `Outer inst.`, `Inner inst.`, `Outer pr.` et `Inner pr.`. Les deux premières comptent les instructions ; les deux dernières comptent les program IDs distincts. + +La colonne `Core` décrit le graphe structurel : + +- `absent` : aucune ligne `kb_sol_core_transactions` ; +- `présent` : graphe core présent et transaction Solana réussie ; +- `échec on-chain` : graphe core présent, mais la transaction Solana elle-même a retourné une erreur. + +`échec on-chain` n’est donc pas un échec de l’extracteur. Le tri DataTables utilise un rang numérique distinct pour ces trois états. + +### Pools et paires + +Aucun onglet pool/pair n’est ajouté dans `0.3.4`. Le core connaît des account keys mais ne peut pas déterminer de façon fiable leur rôle sémantique. Les futurs décodeurs et matérialiseurs alimenteront les tables catalogues de pools et de paires ; l’onglet pourra alors reposer sur des identifiants explicites plutôt que sur une heuristique. + +## Contrôles recommandés après extraction + +- comparer le nombre de transactions raw `core_extracted` et de transactions core ; +- vérifier l’absence de lignes filles orphelines ; +- vérifier l’unicité des chemins et indices ; +- comparer les statuts ledger avec les graphes core présents ; +- examiner les lignes raw `failed` et leur `lifecycle_reason`. + +Les requêtes prêtes à l’emploi sont dans `sql/validation/000_core_integrity.sql`. diff --git a/docs/SURFACE_CRATE_MATRIX.md b/docs/SURFACE_CRATE_MATRIX.md new file mode 100644 index 0000000..b465fa3 --- /dev/null +++ b/docs/SURFACE_CRATE_MATRIX.md @@ -0,0 +1,272 @@ + + + +# Matrice des crates réservés + +Ce document liste les crates actuellement présents dans le workspace. Un crate réservé ne signifie pas que le décodage, la matérialisation ou l’exécution est validé. + +## Principes + +- Les surfaces non classifiées ne doivent pas avoir de crate. +- Les décodeurs et exécuteurs sont réservés par surface canonique lorsque la surface est classifiée. +- Les détails de validation restent dans `registry/program_registry_seed.toml` et les docs spécialisées. + +## Solana + +| Surface | Décodeur | Exécuteur | +|---------------|--------------------------|---------------------------| +| `solana_core` | `kb_decoder_solana_core` | `kb_executor_solana_core` | + +## Spl + +| Surface | Décodeur | Exécuteur | +|--------------------------------|-------------------------------------------|--------------------------------------------| +| `spl_account_compression` | `kb_decoder_spl_account_compression` | `kb_executor_spl_account_compression` | +| `spl_associated_token_account` | `kb_decoder_spl_associated_token_account` | `kb_executor_spl_associated_token_account` | +| `spl_memo` | `kb_decoder_spl_memo` | `kb_executor_spl_memo` | +| `spl_name_service` | `kb_decoder_metadata_spl_name_service` | `kb_executor_metadata_spl_name_service` | +| `spl_noop` | `kb_decoder_spl_noop` | `kb_executor_spl_noop` | +| `spl_single_pool` | `kb_decoder_spl_single_pool` | `kb_executor_spl_single_pool` | +| `spl_stake_pool` | `kb_decoder_spl_stake_pool` | `kb_executor_spl_stake_pool` | +| `spl_token` | `kb_decoder_spl_token` | `kb_executor_spl_token` | +| `spl_token_2022` | `kb_decoder_spl_token_2022` | `kb_executor_spl_token_2022` | + + +## Amm + +| Surface | Décodeur | Exécuteur | +|----------------------------|---------------------------------------|----------------------------------------| +| `amm_aldrin_v1` | `kb_decoder_amm_aldrin_v1` | `kb_executor_amm_aldrin_v1` | +| `amm_aldrin_v2` | `kb_decoder_amm_aldrin_v2` | `kb_executor_amm_aldrin_v2` | +| `amm_alphaq` | `kb_decoder_amm_alphaq` | `kb_executor_amm_alphaq` | +| `amm_believe` | `kb_decoder_amm_believe` | `kb_executor_amm_believe` | +| `amm_bonk_swap` | `kb_decoder_amm_bonk_swap` | `kb_executor_amm_bonk_swap` | +| `amm_fluxbeam` | `kb_decoder_amm_fluxbeam` | `kb_executor_amm_fluxbeam` | +| `amm_goon_fi` | `kb_decoder_amm_goon_fi` | `kb_executor_amm_goon_fi` | +| `amm_goosefx_gamma` | `kb_decoder_amm_goosefx_gamma` | `kb_executor_amm_goosefx_gamma` | +| `amm_goosefx_v2` | `kb_decoder_amm_goosefx_v2` | `kb_executor_amm_goosefx_v2` | +| `amm_guac_swap` | `kb_decoder_amm_guac_swap` | `kb_executor_amm_guac_swap` | +| `amm_lifinity_swap_v2` | `kb_decoder_amm_lifinity_swap_v2` | `kb_executor_amm_lifinity_swap_v2` | +| `amm_metadao_futarchy_amm` | `kb_decoder_amm_metadao_futarchy_amm` | `kb_executor_amm_metadao_futarchy_amm` | +| `amm_metadao_v0_5` | `kb_decoder_amm_metadao_v0_5` | `kb_executor_amm_metadao_v0_5` | +| `amm_meteora_damm_v1` | `kb_decoder_amm_meteora_damm_v1` | `kb_executor_amm_meteora_damm_v1` | +| `amm_meteora_damm_v2` | `kb_decoder_amm_meteora_damm_v2` | `kb_executor_amm_meteora_damm_v2` | +| `amm_obric_v2` | `kb_decoder_amm_obric_v2` | `kb_executor_amm_obric_v2` | +| `amm_one_dex` | `kb_decoder_amm_one_dex` | `kb_executor_amm_one_dex` | +| `amm_pump_swap` | `kb_decoder_amm_pump_swap` | `kb_executor_amm_pump_swap` | +| `amm_raydium_lp_v4` | `kb_decoder_amm_raydium_lp_v4` | `kb_executor_amm_raydium_lp_v4` | +| `amm_solfi` | `kb_decoder_amm_solfi` | `kb_executor_amm_solfi` | +| `amm_solfi_v2` | `kb_decoder_amm_solfi_v2` | `kb_executor_amm_solfi_v2` | +| `amm_vertigo` | `kb_decoder_amm_vertigo` | `kb_executor_amm_vertigo` | +| `amm_virtuals` | `kb_decoder_amm_virtuals` | `kb_executor_amm_virtuals` | +| `amm_woofi` | `kb_decoder_amm_woofi` | `kb_executor_amm_woofi` | +| `amm_zero_fi` | `kb_decoder_amm_zero_fi` | `kb_executor_amm_zero_fi` | +| `amm_zora` | `kb_decoder_amm_zora` | `kb_executor_amm_zora` | + +## Cpmm + +| Surface | Décodeur | Exécuteur | +|----------------|---------------------------|----------------------------| +| `cpmm_raydium` | `kb_decoder_cpmm_raydium` | `kb_executor_cpmm_raydium` | + +## Clmm + +| Surface | Décodeur | Exécuteur | +|-----------------------|----------------------------------|-----------------------------------| +| `clmm_byreal` | `kb_decoder_clmm_byreal` | `kb_executor_clmm_byreal` | +| `clmm_fusion` | `kb_decoder_clmm_fusion` | `kb_executor_clmm_fusion` | +| `clmm_orca_whirlpool` | `kb_decoder_clmm_orca_whirlpool` | `kb_executor_clmm_orca_whirlpool` | +| `clmm_pancake_swap` | `kb_decoder_clmm_pancake_swap` | `kb_executor_clmm_pancake_swap` | +| `clmm_raydium` | `kb_decoder_clmm_raydium` | `kb_executor_clmm_raydium` | +| `clmm_stabble` | `kb_decoder_clmm_stabble` | `kb_executor_clmm_stabble` | + +## Dlmm + +| Surface | Décodeur | Exécuteur | +|----------------|---------------------------|----------------------------| +| `dlmm_meteora` | `kb_decoder_dlmm_meteora` | `kb_executor_dlmm_meteora` | + +## Stable Swap + +| Surface | Décodeur | Exécuteur | +|------------------------------|-----------------------------------------|------------------------------------------| +| `stable_swap_hylo_exchange` | `kb_decoder_stable_swap_hylo_exchange` | `kb_executor_stable_swap_hylo_exchange` | +| `stable_swap_jupiter_stable` | `kb_decoder_stable_swap_jupiter_stable` | `kb_executor_stable_swap_jupiter_stable` | +| `stable_swap_numeraire` | `kb_decoder_stable_swap_numeraire` | `kb_executor_stable_swap_numeraire` | +| `stable_swap_stabble` | `kb_decoder_stable_swap_stabble` | `kb_executor_stable_swap_stabble` | + +## Weighted Swap + +| Surface | Décodeur | Exécuteur | +|-------------------------|------------------------------------|-------------------------------------| +| `weighted_swap_stabble` | `kb_decoder_weighted_swap_stabble` | `kb_executor_weighted_swap_stabble` | + +## Launchpad + +| Surface | Décodeur | Exécuteur | +|-------------------------------|------------------------------------------|-------------------------------------------| +| `launchpad_boop_fun` | `kb_decoder_launchpad_boop_fun` | `kb_executor_launchpad_boop_fun` | +| `launchpad_metadao_ico` | `kb_decoder_launchpad_metadao_ico` | `kb_executor_launchpad_metadao_ico` | +| `launchpad_meteora_dbc` | `kb_decoder_launchpad_meteora_dbc` | `kb_executor_launchpad_meteora_dbc` | +| `launchpad_moonit` | `kb_decoder_launchpad_moonit` | `kb_executor_launchpad_moonit` | +| `launchpad_orca_wavebreak` | `kb_decoder_launchpad_orca_wavebreak` | `kb_executor_launchpad_orca_wavebreak` | +| `launchpad_printr` | `kb_decoder_launchpad_printr` | `kb_executor_launchpad_printr` | +| `launchpad_pump_fun` | `kb_decoder_launchpad_pump_fun` | `kb_executor_launchpad_pump_fun` | +| `launchpad_pump_pumpup_ai` | `kb_decoder_launchpad_pump_pumpup_ai` | `kb_executor_launchpad_pump_pumpup_ai` | +| `launchpad_raydium_launchlab` | `kb_decoder_launchpad_raydium_launchlab` | `kb_executor_launchpad_raydium_launchlab` | + +## Router + +| Surface | Décodeur | Exécuteur | +|--------------------------------|-------------------------------------------|--------------------------------------------| +| `router_dflow_aggregator_v4` | `kb_decoder_router_dflow_aggregator_v4` | `kb_executor_router_dflow_aggregator_v4` | +| `router_jupiter_aggregator_v4` | `kb_decoder_router_jupiter_aggregator_v4` | `kb_executor_router_jupiter_aggregator_v4` | +| `router_jupiter_aggregator_v6` | `kb_decoder_router_jupiter_aggregator_v6` | `kb_executor_router_jupiter_aggregator_v6` | +| `router_jupiter_dca` | `kb_decoder_router_jupiter_dca` | `kb_executor_router_jupiter_dca` | +| `router_okx_labs_v1` | `kb_decoder_router_okx_labs_v1` | `kb_executor_router_okx_labs_v1` | +| `router_okx_labs_v2` | `kb_decoder_router_okx_labs_v2` | `kb_executor_router_okx_labs_v2` | + +## Orderbook + +| Surface | Décodeur | Exécuteur | +|------------------------------------|-----------------------------------------------|------------------------------------------------| +| `orderbook_jupiter_limit_order` | `kb_decoder_orderbook_jupiter_limit_order` | `kb_executor_orderbook_jupiter_limit_order` | +| `orderbook_jupiter_limit_order_v2` | `kb_decoder_orderbook_jupiter_limit_order_v2` | `kb_executor_orderbook_jupiter_limit_order_v2` | +| `orderbook_openbook_v2` | `kb_decoder_orderbook_openbook_v2` | `kb_executor_orderbook_openbook_v2` | + +## Admin + +| Surface | Décodeur | Exécuteur | +|----------------------|---------------------------------|----------------------------------| +| `admin_jupiter_lock` | `kb_decoder_admin_jupiter_lock` | `kb_executor_admin_jupiter_lock` | +| `admin_pump_fees` | `kb_decoder_admin_pump_fees` | `kb_executor_admin_pump_fees` | + +## Fees + +| Surface | Décodeur | Exécuteur | +|--------------------------|-------------------------------------|--------------------------------------| +| `fees_bags_fee_share_v1` | `kb_decoder_fees_bags_fee_share_v1` | `kb_executor_fees_bags_fee_share_v1` | +| `fees_bags_fee_share_v2` | `kb_decoder_fees_bags_fee_share_v2` | `kb_executor_fees_bags_fee_share_v2` | + +## Lock + +| Surface | Décodeur | Exécuteur | +|-------------------|------------------------------|-------------------------------| +| `lock_raydium_lp` | `kb_decoder_lock_raydium_lp` | `kb_executor_lock_raydium_lp` | + +## Adapter + +| Surface | Décodeur | Exécuteur | +|---------------------------------|--------------------------------------------|---------------------------------------------| +| `adapter_saber_decimal_wrapper` | `kb_decoder_adapter_saber_decimal_wrapper` | `kb_executor_adapter_saber_decimal_wrapper` | + +## Governance + +| Surface | Décodeur | Exécuteur | +|-------------------------------|------------------------------------------|-------------------------------------------| +| `governance_metadao_bid_wall` | `kb_decoder_governance_metadao_bid_wall` | `kb_executor_governance_metadao_bid_wall` | + +## Bridge + +| Surface | Décodeur | Exécuteur | +|------------------------------------------------|-----------------------------------------------------------|------------------------------------------------------------| +| `bridge_circle_cctp_token_messenger_minter` | `kb_decoder_bridge_circle_cctp_token_messenger_minter` | `kb_executor_bridge_circle_cctp_token_messenger_minter` | +| `bridge_circle_cctp_token_messenger_minter_v2` | `kb_decoder_bridge_circle_cctp_token_messenger_minter_v2` | `kb_executor_bridge_circle_cctp_token_messenger_minter_v2` | +| `bridge_layer_zero_endpoint` | `kb_decoder_bridge_layer_zero_endpoint` | `kb_executor_bridge_layer_zero_endpoint` | +| `bridge_layer_zero_executor` | `kb_decoder_bridge_layer_zero_executor` | `kb_executor_bridge_layer_zero_executor` | + +## Rwa + +| Surface | Décodeur | Exécuteur | +|---------------------------|--------------------------------------|---------------------------------------| +| `rwa_ondo_global_markets` | `kb_decoder_rwa_ondo_global_markets` | `kb_executor_rwa_ondo_global_markets` | + +## Lending + +| Surface | Décodeur | Exécuteur | +|-----------------------------------|----------------------------------------------|-----------------------------------------------| +| `lending_clone` | `kb_decoder_lending_clone` | `kb_executor_lending_clone` | +| `lending_jupiter_lend_borrow` | `kb_decoder_lending_jupiter_lend_borrow` | `kb_executor_lending_jupiter_lend_borrow` | +| `lending_jupiter_lend_earn` | `kb_decoder_lending_jupiter_lend_earn` | `kb_executor_lending_jupiter_lend_earn` | +| `lending_jupiter_lend_flash_loan` | `kb_decoder_lending_jupiter_lend_flash_loan` | `kb_executor_lending_jupiter_lend_flash_loan` | +| `lending_jupiter_lend_liquidity` | `kb_decoder_lending_jupiter_lend_liquidity` | `kb_executor_lending_jupiter_lend_liquidity` | +| `lending_kamino` | `kb_decoder_lending_kamino` | `kb_executor_lending_kamino` | +| `lending_marginfi_v2` | `kb_decoder_lending_marginfi_v2` | `kb_executor_lending_marginfi_v2` | + +## Staking + +| Surface | Décodeur | Exécuteur | +|----------------------------|---------------------------------------|----------------------------------------| +| `staking_kamino_farm` | `kb_decoder_staking_kamino_farm` | `kb_executor_staking_kamino_farm` | +| `staking_marinade_finance` | `kb_decoder_staking_marinade_finance` | `kb_executor_staking_marinade_finance` | +| `staking_solayer` | `kb_decoder_staking_solayer` | `kb_executor_staking_solayer` | + +## Vault + +| Surface | Décodeur | Exécuteur | +|-----------------------------|----------------------------------------|-----------------------------------------| +| `vault_carrot_defi` | `kb_decoder_vault_carrot_defi` | `kb_executor_vault_carrot_defi` | +| `vault_hylo_stability_pool` | `kb_decoder_vault_hylo_stability_pool` | `kb_executor_vault_hylo_stability_pool` | +| `vault_kamino` | `kb_decoder_vault_kamino` | `kb_executor_vault_kamino` | +| `vault_kamino_v2` | `kb_decoder_vault_kamino_v2` | `kb_executor_vault_kamino_v2` | +| `vault_kamino_yvaults` | `kb_decoder_vault_kamino_yvaults` | `kb_executor_vault_kamino_yvaults` | +| `vault_meteora` | `kb_decoder_vault_meteora` | `kb_executor_vault_meteora` | + +## Vesting + +| Surface | Décodeur | Exécuteur | +|----------------------|---------------------------------|----------------------------------| +| `vesting_streamflow` | `kb_decoder_vesting_streamflow` | `kb_executor_vesting_streamflow` | + +## Nft + +| Surface | Décodeur | Exécuteur | +|--------------------------|-------------------------------------|--------------------------------------| +| `nft_metaplex_bubblegum` | `kb_decoder_nft_metaplex_bubblegum` | `kb_executor_nft_metaplex_bubblegum` | + +## Treasury + +| Surface | Décodeur | Exécuteur | +|---------------------------------------|--------------------------------------------------|---------------------------------------------------| +| `treasury_helium_treasury_management` | `kb_decoder_treasury_helium_treasury_management` | `kb_executor_treasury_helium_treasury_management` | + +## Wallet + +| Surface | Décodeur | Exécuteur | +|--------------------------------------|-------------------------------------------------|--------------------------------------------------| +| `wallet_jupiter_apepro_smart_wallet` | `kb_decoder_wallet_jupiter_apepro_smart_wallet` | `kb_executor_wallet_jupiter_apepro_smart_wallet` | + +## Perpetuals + +| Surface | Décodeur | Exécuteur | +|-----------------------|----------------------------------|-----------------------------------| +| `perpetuals_drift_v2` | `kb_decoder_perpetuals_drift_v2` | `kb_executor_perpetuals_drift_v2` | +| `perpetuals_jupiter` | `kb_decoder_perpetuals_jupiter` | `kb_executor_perpetuals_jupiter` | +| `perpetuals_zeta` | `kb_decoder_perpetuals_zeta` | `kb_executor_perpetuals_zeta` | + +## Matérialisateurs + +| Crate | +|---------------------------------------| +| `kb_materializer_admin` | +| `kb_materializer_bridge` | +| `kb_materializer_compliance_audit` | +| `kb_materializer_fees` | +| `kb_materializer_governance` | +| `kb_materializer_lending` | +| `kb_materializer_lifecycle` | +| `kb_materializer_liquidity` | +| `kb_materializer_metadata` | +| `kb_materializer_nft` | +| `kb_materializer_oracle` | +| `kb_materializer_orderbook` | +| `kb_materializer_perpetuals` | +| `kb_materializer_pool_state` | +| `kb_materializer_rewards` | +| `kb_materializer_risk` | +| `kb_materializer_routing` | +| `kb_materializer_staking` | +| `kb_materializer_token_accounts` | +| `kb_materializer_token_metadata_risk` | +| `kb_materializer_trades` | +| `kb_materializer_vault` | diff --git a/docs/TRACING_CONTRACT.md b/docs/TRACING_CONTRACT.md new file mode 100644 index 0000000..b098342 --- /dev/null +++ b/docs/TRACING_CONTRACT.md @@ -0,0 +1,168 @@ + + + +# Contrat de tracing par crate + +## Objectif + +Chaque événement est émis par la crate qui prend réellement la décision. `kb_app_demo` journalise les frontières Tauri, les actions utilisateur, l’état des fenêtres et les résumés d’orchestration ; il ne recopie pas les décisions internes du RPC, du pipeline, du store, d’un décodeur, d’un matérialiseur ou d’un exécuteur. + +## Classification des crates + +Une crate est opérationnelle lorsqu’elle réalise au moins une des actions suivantes : + +- I/O réseau ou base de données ; +- orchestration concurrente, retry, pacing ou annulation ; +- décodage ou matérialisation ; +- construction ou simulation d’un plan d’exécution ; +- décision runtime mutable ayant un effet observable. + +Une crate passive contient uniquement des types, contrats, DTO, traits sans implémentation, registres ou constantes. Elle ne dépend pas de `tracing`. `kb_config` reste une exception de bootstrap : son chargement et sa validation précèdent l’installation du subscriber. + +## Cible canonique + +Toute crate qui déclare `tracing.workspace = true` doit : + +- posséder `src/constants.rs` ; +- définir exactement une constante `pub(crate) const TRACING_TARGET` ; +- utiliser comme valeur le nom exact du package Cargo ; +- appeler les macros avec `target: crate::constants::TRACING_TARGET` ; +- déplacer la granularité interne dans des champs structurés. + +Exemple : + +```rust +tracing::error!( + target: crate::constants::TRACING_TARGET, + action = "decoder_outcome_failure", + campaign_id = %campaign_id, + signature = %input.signature, + instruction_path = %input.instruction_path, + program_id = %input.program_id, + processor_name = %identity.name, + processor_version = %identity.version, + status = ?result.status, + diagnostics = ?result.diagnostics, + "contextual instruction was not decoded successfully" +); +``` + +Les targets `khbot.*`, les targets suffixés par module et les targets de fenêtre ne sont plus utilisés par les crates opérationnelles migrées. Les identifiants frontend historiques restent acceptés comme valeurs du champ `frontend_target`, mais l’événement Rust est émis sous `kb_app_demo`. + +## Crates actuellement routées + +La configuration couvre toutes les crates qui déclarent actuellement `tracing.workspace = true` : + +```text +kb_app_demo +kb_decoder_solana_core +kb_decoder_spl_associated_token_account +kb_decoder_spl_memo +kb_decoder_spl_token +kb_decoder_spl_token_2022 +kb_executor_metadata_spl_name_service +kb_execution_solana +kb_executor_solana_core +kb_executor_spl_account_compression +kb_executor_spl_associated_token_account +kb_executor_spl_memo +kb_executor_spl_noop +kb_executor_spl_single_pool +kb_logging +kb_materializer_admin +kb_materializer_compliance_audit +kb_materializer_lifecycle +kb_materializer_staking +kb_materializer_transaction_annotations +kb_pipeline +kb_rpc +kb_store_pg +kb_wallet +``` + +Le test `every_tracing_crate_has_one_canonical_target_constant` détecte automatiquement toute crate avec `tracing.workspace = true` et vérifie la présence d’une unique constante canonique. Le test de `kb_config` vérifie parallèlement la matrice de routes de chaque profil. + +Les routes sont filtrées au niveau de leur writer plutôt que par un `Filtered` layer distinct. Un seul filtre d’admission agrégé empêche en amont le formatage des événements refusés par toutes les routes. Cette architecture préserve toutes les sorties globales et par crate au-delà de la limite interne de 64 identifiants de filtres de `tracing-subscriber`. Le test `more_than_64_routes_compose_without_filtered_layer_ids` protège explicitement ce contrat. + +## Granularité et corrélation + +Le target identifie la crate. Les champs décrivent l’action et le contexte : + +```text +action +stage +window +campaign_id +capture_session_id +signature +slot +instruction_path +program_id +processor_name +processor_version +materializer_name +materializer_version +input_key +input_hash +status +decision +error_code +``` + +Les spans de campagne et d’input utilisent eux aussi le target canonique `kb_pipeline`. Les événements des autres crates conservent leur propre target et reprennent les identifiants de corrélation utiles dans leurs champs. + +## Politique des erreurs de décodage et de matérialisation + +Un événement `error` est obligatoire pour : + +- un input sélectionné sans décodeur compatible ; +- un résultat de décodeur `failed` ; +- un résultat de décodeur `unsupported` après dispatch vers une surface déclarée compatible ; +- un résultat de décodeur invalide au regard du contrat API ; +- un résultat de matérialiseur `failed` ; +- une erreur de persistance decode, couverture, ledger ou matérialisation ; +- une campagne qui termine avec `unmatched > 0` ou `failed_inputs > 0`. + +L’événement doit permettre de retrouver rapidement l’input et la cause. Il conserve, lorsque disponibles, la campagne, la signature, le slot, le chemin d’instruction, le program ID, l’identité et la version du processor, la clé/hash d’input, le statut et les diagnostics structurés. + +Les cas suivants ne sont pas des erreurs logicielles : + +- transaction Solana échouée mais payload correctement décodé ; +- observation non commitée conformément au statut on-chain ; +- matérialisation `refused` par la politique de transaction ; +- entrée volontairement `ignored` avec justification ; +- annulation coopérative demandée par l’opérateur. + +Ils sont journalisés en `debug`, `info` ou `warn` selon leur impact. + +## Responsabilité + +- `kb_rpc` : sélection d’endpoint, requêtes, réponses bornées, retry, rate limit et transport. +- `kb_execution_solana` : assemblage du message, contrôle du contrat de signataires, liaison de simulation et signature transactionnelle. +- `kb_store_pg` : transactions SQL, commit/rollback, compteurs et erreurs de persistance. +- `kb_pipeline` : sélection, dispatch, concurrence, annulation, backfill et agrégation. +- décodeur : reconnaissance, validation du format, décision et diagnostic borné. +- matérialiseur : applicabilité exacte, politique de transaction et sorties produites. +- exécuteur : support, construction, simulation et garde-fous. +- application : invocation Tauri, fenêtre, progression utilisateur et résumé final. + +## Données interdites + +Ne jamais journaliser : + +- clé privée, seed phrase ou transaction à signer ; +- DSN ou URL contenant un secret non masqué ; +- payload brut complet ou réponse RPC volumineuse ; +- donnée dynamique non bornée. + +Conserver à la place la taille, un préfixe borné et un SHA-256 lorsque l’audit du payload est nécessaire. + +## Évolution + +L’ajout ou la suppression de `tracing.workspace = true` impose dans le même delta : + +1. la constante canonique ou sa suppression ; +2. les événements réels de la crate ; +3. la mise à jour de `config/example.config.json` ; +4. la mise à jour des tests de contrat ; +5. la mise à jour de la liste ci-dessus. diff --git a/docs/TRANSACTION_ACQUISITION_MODEL.md b/docs/TRANSACTION_ACQUISITION_MODEL.md new file mode 100644 index 0000000..e2776bd --- /dev/null +++ b/docs/TRANSACTION_ACQUISITION_MODEL.md @@ -0,0 +1,137 @@ + + + +# Modèle d’acquisition des transactions Solana + +## Décision + +Le pipeline ne conserve pas un payload complet différent pour chaque fournisseur. Toutes les sources doivent produire une représentation canonique commune avant l’écriture principale. + +```text +getTransaction JSON-RPC +Helius transactionSubscribe JSON +Yellowstone gRPC Protobuf +logsSubscribe + getTransaction +backfill ou réparation + | + v +adaptateur de source dans kb_rpc + | + v +transaction Solana canonique + | + +--> kb_sol_raw_transactions + | + +--> kb_sol_obs_transaction_observations +``` + +## Transaction canonique + +`kb_sol_raw_transactions` contient une seule ligne par signature. Le contenu est indépendant du fournisseur et ne doit contenir aucun champ propre à Helius, Triton, Chainstack, Shyft ou un endpoint particulier. + +Le document canonique doit inclure, quand la source les fournit : + +- signature ; +- slot ; +- version de transaction ; +- message header ; +- static account keys ; +- Address Lookup Tables ; +- loaded writable et readonly addresses ; +- recent blockhash ; +- outer instructions ; +- inner instructions ; +- logs ; +- statut et erreur ; +- fee et compute units ; +- balances SOL avant/après ; +- balances SPL avant/après ; +- rewards ; +- return data ; +- block time. + +Les public keys et signatures restent en base58, avec validation stricte de leur taille décodée : 32 octets pour les clés et blockhashes, 64 octets pour les signatures. Les données binaires d’instruction restent en base64. Les montants SPL conservent le montant entier brut normalisé sans zéros non significatifs, les décimales et une représentation décimale fixe recalculée localement, sans dépendre de `uiAmount` ou `uiAmountString` du fournisseur. + +Depuis `0.3.2`, ce contrat est matérialisé par `kb_model::CanonicalTransaction` avec `canonical_format_version = 1`. La sérialisation trie récursivement les clés des objets JSON avant calcul d’un hash SHA-256 sur le document compact. Les tableaux conservent l’ordre Solana d’origine. Les détails de source, timestamps locaux et identifiants de subscription sont exclus du document hashé. + +L’adaptateur HTTP standard demande `encoding = json`, puis convertit explicitement les payloads d’instruction reçus en base58 vers la représentation base64 canonique. Les champs optionnels absents et `null` sont normalisés vers le même état interne afin de stabiliser le hash entre réponses compatibles. + +## Observations de source + +`kb_sol_obs_transaction_observations` décrit comment et quand une transaction a été détectée ou reçue. Elle ne duplique pas la transaction complète. + +Champs minimaux visés : + +| Champ | Rôle | +|-----------------------|----------------------------------------------------------------------------------------------| +| `observation_key` | clé idempotente de l’observation | +| `raw_transaction_id` | lien optionnel vers la transaction canonique | +| `signature` | signature observée | +| `slot` | slot observé quand connu | +| `provider` | fournisseur, par exemple `helius`, `triton`, `chainstack`, `shyft` | +| `endpoint_code` | endpoint de configuration utilisé | +| `protocol` | `solana_http_json_rpc`, `solana_ws_json_rpc`, `helius_ws` ou `yellowstone_grpc` | +| `acquisition_method` | `getTransaction`, `transactionSubscribe`, `yellowstone_transactions`, `logs_hydration`, etc. | +| `origin` | `live`, `backfill`, `replay`, `repair` ou `migration` | +| `commitment` | commitment demandé ou observé | +| `capture_session_id` | session ou campagne de capture | +| `filter_code` | profil de filtre utilisé | +| `detected_at` | réception du premier signal, par exemple le log | +| `received_at` | réception de la transaction complète | +| `normalized_at` | fin de normalisation canonique | +| `persisted_at` | fin d’écriture durable | +| `payload_size_bytes` | taille du message source reçu | +| `source_payload_hash` | hash optionnel du message source sans le conserver | +| `status` | `detected`, `received`, `normalized`, `persisted`, `failed` ou `missing` | +| `error_code` | code technique normalisé optionnel | +| `error_message` | message de diagnostic optionnel | + +Les observations permettent de comparer les sources par signature, sans multiplier le volume de stockage transactionnel. + +## Notifications WebSocket + +`kb_sol_raw_ws_notifications` n’est plus une cible durable. + +Une notification `logsSubscribe` peut être conservée temporairement en mémoire jusqu’à l’hydratation de la transaction. La base conserve ensuite seulement : + +- les timestamps utiles ; +- la signature et le slot ; +- la source et la méthode ; +- la taille ; +- le statut de l’hydratation ; +- le lien éventuel vers la transaction canonique. + +Les notifications non transactionnelles comme `slotSubscribe`, `accountSubscribe` ou `programSubscribe` pourront avoir des tables métier dédiées seulement si un besoin durable apparaît. Elles ne doivent pas être entassées dans une table générique de payloads WebSocket. + +## Fusion multi-source + +Pour une signature déjà présente : + +1. normaliser le nouveau message ; +2. calculer le hash canonique ; +3. ajouter l’observation de source ; +4. si le hash est identique, ne pas dupliquer la transaction ; +5. si la nouvelle source apporte uniquement des champs auparavant absents, appliquer un enrichissement déterministe ; +6. si des champs incompatibles diffèrent, ne pas écraser silencieusement et enregistrer un conflit technique. + +Les différences liées au commitment ou à la disponibilité progressive des metadata doivent être distinguées d’un conflit réel. + +## Raw provider facultatif + +Un payload fournisseur complet peut être exporté de façon bornée pour une campagne de diagnostic, par exemple en NDJSON ou Protobuf, mais il ne fait pas partie du stockage PostgreSQL transactionnel normal. + +Ces exports doivent être : + +- explicitement activés ; +- limités en durée et en volume ; +- associés à une session de capture ; +- supprimables sans affecter les replays métier. + +## Frontières des crates + +- `kb_rpc` contient les transports et adaptateurs HTTP, WebSocket Helius et Yellowstone gRPC. +- `kb_model` contient le contrat canonique source-indépendant. +- `kb_store_core` contient les DTOs et repositories de transactions et observations. +- `kb_store_pg` contient la migration, les queries et les repositories PostgreSQL. +- les décodeurs ne dépendent jamais de la source d’acquisition. + diff --git a/docs/V0_4_6_VALIDATION.md b/docs/V0_4_6_VALIDATION.md new file mode 100644 index 0000000..994b85f --- /dev/null +++ b/docs/V0_4_6_VALIDATION.md @@ -0,0 +1,66 @@ + + + +# Validation finale `0.4.6` + +## Périmètre + +Le jalon couvre Token-2022 et le registre ElGamal comme programmes distincts, ainsi que la régression des décodeurs Solana Core/SPL livrés auparavant. Il ne transforme pas Anchor, Metaplex ou les futurs DEX en surfaces implicitement couvertes. + +## Résultats de tests + +| Suite | Résultat | +|------------------------------------|---------:| +| `kb_store_core` | 41/41 | +| `kb_store_pg` avec PostgreSQL réel | 46/46 | +| `kb_pipeline` | 124/124 | +| `kb_app_demo` | 118/118 | +| `cargo clippy --all-targets` | propre | + +Les contrôles SQL sur `kb_sol_decode_events` et `kb_sol_mat_events` ne révèlent aucun doublon d’identité. + +## Token-2022 et ElGamal Registry + +- 55 instructions Token-2022 Mainnet ont été décodées sans échec persistant. +- Huit opérations publiques Token-2022 ont été confirmées sur Devnet : `MintToChecked`, `TransferChecked`, `ApproveChecked`, `Revoke`, `BurnChecked`, `FreezeAccount`, `ThawAccount` et `CloseAccount`. +- Chaque parcours Devnet a été simulé, confirmé, hydraté, extrait, décodé, matérialisé puis rejoué idempotemment. +- ElGamal Registry est validé synthétiquement/offline pour le wire, le PDA, l’état, les lectures RPC, les préflights et les preuves. +- La validation publique ElGamal Registry reste indisponible sur Devnet/Mainnet au 20 juillet 2026 ; aucune preuve réseau n’est revendiquée. + +## Loader immuable et Loader v4 + +Le parser `Write` du loader immuable lit désormais la longueur vectorielle en `u32` et les données à l’offset 12. Le replay Mainnet a supprimé 68 diagnostics `loader_vector_length_mismatch`. + +Les rejets persistants restants sont tous issus de transactions Solana échouées : + +| Diagnostic | Nombre | +|----------------------------------|-------:| +| `immutable_loader_tag_truncated` | 31 | +| `loader_v4_tag_truncated` | 44 | +| `loader_accounts_invalid` | 1 | + +Quatre variantes Loader v4 inconnues sont persistées comme traitement réussi avec résultat métier `Unsupported`. Elles ne sont pas assimilées à des décodages complets. + +## Replay des signatures incomplètes + +Le mode `incomplete_signatures` sélectionne les signatures contenant au moins une instruction `pending`, `failed`, `replay_requested` ou `unsupported`. La limite est appliquée au nombre de signatures avant expansion vers les instructions compatibles. Ce mode ne nécessite pas de force replay global. + +Deux campagnes PostgreSQL réelles successives ont produit exactement : + +```text +selected=81 +started=81 +completed=81 +unmatched=0 +notStarted=0 +failedInputs=76 +decoded=1 +unsupported=4 +failed=76 +``` + +L’instruction décodée appartient à une signature incomplète contenant également une instruction valide. La répétition identique du second passage confirme la sélection stable ; les événements et matérialisations restent idempotents. + +## Conclusion + +Tous les décodeurs livrés jusqu’à `0.4.6` respectent leur contrat sur les tests synthétiques, les scénarios stateful et les données réseau observées. Les payloads malformés sont rejetés fail-closed et les variantes inconnues sont classées explicitement `Unsupported`. Cette conclusion ne prétend pas que chaque variante rare possible a été observée sur un cluster public. diff --git a/docs/VERSION_HEADER_AUDIT.md b/docs/VERSION_HEADER_AUDIT.md new file mode 100644 index 0000000..9836fea --- /dev/null +++ b/docs/VERSION_HEADER_AUDIT.md @@ -0,0 +1,30 @@ + + + +# Audit des en-têtes de version + +Ce document regroupe les commandes utiles pour vérifier les en-têtes `file:` et `version:`. + +## Lignes `version` différentes de 1 + +La commande suivante utilise deux passes pour éviter les problèmes de lookahead et d'expansion d'historique Bash : + +```bash +rg -n -P '^\s*(//|#||\*/)?\s*$' +``` + +## Fichiers concernés uniquement + +```bash +rg -n -P '^\s*(//|#||\*/)?\s*$' | cut -d: -f1 | sort -u +``` + +## Fichiers commentables sans ligne `version` + +```bash +find . -path './target' -prune -o -path './node_modules' -prune -o -path './dist' -prune -o -path './idls' -prune -o -type f \( -name '*.rs' -o -name '*.toml' -o -name '*.sql' -o -name '*.md' -o -name '*.ts' -o -name '*.js' -o -name '*.html' -o -name '*.scss' -o -name '*.sass' \) -print | while read -r f; do + if ! head -n 5 "$f" | rg -q 'version\s*:'; then + echo "$f" + fi + done +``` diff --git a/docs/WS_LISTENERS.md b/docs/WS_LISTENERS.md new file mode 100644 index 0000000..e341a77 --- /dev/null +++ b/docs/WS_LISTENERS.md @@ -0,0 +1,69 @@ + + + +# Listeners WebSocket + +## Statut + +Les listeners temps réel ne dirigent plus `0.3.x`. Ils seront repris après les décodeurs Core, Pump, Meteora, Raydium, Orca et Jupiter. + +## Méthodes déjà présentes dans `demo_ws` + +```text +slotSubscribe +slotsUpdatesSubscribe +rootSubscribe +logsSubscribe +accountSubscribe +programSubscribe +signatureSubscribe +blockSubscribe +voteSubscribe +``` + +L’audit confirme encore l’absence de : + +```text +transactionSubscribe +transactionUnsubscribe +request kind transaction_subscribe +classification logs_subscribe_all +``` + +Ces ajouts sont reportés à `0.10.x`. + +## Modèle futur + +```text +Helius transactionSubscribe +Yellowstone gRPC transactions +logsSubscribe + hydration + | + v +transaction canonique commune + | + +--> kb_sol_raw_transactions + +--> kb_sol_obs_transaction_observations +``` + +Le payload complet de la notification WebSocket ne doit pas être stocké durablement lorsqu’une transaction canonique existe. + +## Cas d’usage + +Les flux devront détecter rapidement : + +- création de token ou pool ; +- premiers swaps ; +- migration ; +- changement de liquidité ; +- activité administrative dangereuse ; +- frais et buybacks ; +- changements sur les comptes critiques. + +## Sécurité + +- Aucun ordre direct depuis la couche transport. +- Toute perte du flux principal désactive le trading. +- Les événements rejoués ou backfillés mettent à jour l’état sans déclencher rétroactivement un ordre. +- Les queues, tailles, retries et exports de diagnostic restent bornés. + diff --git a/docs/legacy-khadhroony-bot2/CHANGELOG.md b/docs/legacy-khadhroony-bot2/CHANGELOG.md new file mode 100644 index 0000000..b08dcc6 --- /dev/null +++ b/docs/legacy-khadhroony-bot2/CHANGELOG.md @@ -0,0 +1,208 @@ + + + +# Changelog + +Ce fichier est réservé aux versions validées. Les changements non validés restent décrits dans `ROADMAP.md` ou `RULES.md`. + +## 0.0.1 — projet vide + +Initialisation du dépôt comme point de départ vide. Cette version sert uniquement de repère historique avant la création du squelette modulaire. + +## 0.0.2 — squelette stabilisé + +Validation du squelette initial de `khadhroony-bot2` : workspace Rust modulaire, règles de développement, nomenclature des surfaces, registres de programmes, configuration multi-profils, couche d'exécution réservée, décodeurs/exécuteurs/matérialisateurs réservés, documentation de cadrage, prompts de session et application Tauri de démonstration en squelette. Cette version devient la base propre avant le développement `0.1.x` consacré à `kb_logging` et `kb_config`. + +## 0.1.0 — `kb_logging` réel + +Validation du jalon `kb_logging` : API publique minimale, initialisation `tracing_subscriber`, routes console, fichier humain, fichier JSON et fichier erreurs, filtrage par target et niveau, nettoyage ANSI des fichiers pour les messages issus de WebView Tauri, support des globs `crate.*` et `crate::*`, et couverture unitaire renforcée. Validation locale effectuée : `cargo test -p kb_logging` avec 14 tests passés et `cargo clippy -p kb_logging --all-targets` sans erreur. `cargo fmt` n'est pas retenu comme validation obligatoire du jalon, car la configuration `rustfmt.toml` contient des options nightly non applicables sur stable et le formatage courant est géré depuis Eclipse. + +## 0.1.1 — `kb_config` typé et validé + +Validation du jalon `kb_config` : configuration JSON multi-profils avec un seul profil actif, schéma `config/schema.config.json` validé via `jsonschema`, désérialisation typée, validation métier des profils, endpoints HTTP JSON-RPC et WebSocket Solana standard, listeners standards, sorties de logging configurables, stores PostgreSQL/SQLite, wallet, exécution et sections de démonstration. Les placeholders de clés restent directement dans les URLs et les champs runtime hors périmètre avant `v1.x` sont refusés par le schéma. Les exports TS-rs suivent la convention `../frontend/ts/bindings/kb_config/settings/.ts`. Validation locale effectuée : `cargo test --tests -p kb_config` avec 35 tests passés, dont les tests générateurs TS-rs `export_bindings_*`, et `cargo clippy` annoncé sans erreur pour le jalon. + +## 0.1.2 — liaison `kb_logging`, `kb_config` et démo Tauri + +Validation du jalon d’intégration Tauri : `kb_app_demo` charge le profil actif via `kb_config`, initialise `kb_logging` depuis cette configuration, conserve un `AppState` global, garde `demo_config` comme fenêtre de démonstration isolée et réémet les logs frontend vers `tracing` avec des targets statiques. La fenêtre principale rend le README workspace en HTML via `markdown-it`, expose un dropdown de démos, et la fenêtre configuration affiche le profil actif, la configuration complète et le schéma JSON avec un viewer JSON interactif. Le verrou mono-instance est appliqué uniquement dans `kb_app_demo/src/main.rs` afin de préserver l’entrée mobile par `kb_app_demo_lib::run()`. Le provider Rustls par défaut est initialisé dans la librairie pour desktop et mobile. Le splashscreen est aligné sur le modèle de `khadhroony-bobobot/kb_demo_app`, avec fade-in/fade-out, durée minimale extensible et attente initiale avant fade-in pour éviter l’apparition instantanée. `kb_core` expose maintenant l’erreur et le résultat communs sous `kb_core::Error` et `kb_core::Result`. Validation locale effectuée : `cargo test -p kb_core` avec 2 tests passés, `cargo test -p kb_config` avec 35 tests passés, `cargo test -p kb_logging` avec 14 tests passés, `cargo test -p kb_app_demo` avec 12 tests passés, `cargo clippy --all-targets` sans erreur et validation visuelle via `cargo tauri dev -c kb_app_demo/tauri.conf.json`. + +## 0.1.3 — clients HTTP/WS Solana standard, rôles et pools + +Validation du jalon RPC initial : `kb_rpc` expose les contrats JSON-RPC 2.0 partagés, le routage par rôle et `request_kind`, les clients HTTP JSON-RPC et WebSocket Solana standard, ainsi que les pools HTTP/WS par rôle. `kb_app_demo` ajoute les fenêtres dédiées `demo_http` et `demo_ws`, les listes contrôlées de rôles et méthodes, le refresh explicite des pools, le profil `mainnet` Helius, la connexion WebSocket persistante conservée dans `AppState`, les subscriptions multiples sur une même socket, l’unsubscribe individuel, la déconnexion globale à l’arrêt de l’application et le throttling des notifications WebSocket vers la WebView pour éviter les freezes sur les programmes très bavards. Les transports gRPC, Helius enhanced WebSocket, Helius gRPC et LaserStream restent hors périmètre avant les versions ultérieures. Validation locale effectuée : `cargo test -p kb_rpc` avec 39 tests passés, `cargo test -p kb_app_demo` avec 28 tests passés, `cargo clippy --all-targets` sans erreur, et validation visuelle des démos HTTP/WS via `cargo tauri dev -c kb_app_demo/tauri.conf.json`. + +## 0.2.0 — conventions PostgreSQL et contrats DB + +Validation du jalon de cadrage stockage : conventions PostgreSQL définies autour du schéma courant/default du profil, généralement `public`, sans espaces applicatifs séparés, et convention de tables Solana `kb_sol__` avec les domaines `raw`, `core`, `obs`, `decode`, `mat`, `catalog`, `agg`, `ops` et `wallet` intégrés au nom de table. Les règles SQL de base sont documentées pour `id`, `created_at`, `updated_at`, `slot`, `signature`, `program_id`, `raw_json` et `payload_json`, avec index minimaux et contraintes strictes mais réversibles. Les responsabilités de `kb_store_core` et `kb_store_pg` sont clarifiées, avec séparation `entities/`, `dtos/`, `queries/` et `repositories/`, et interdiction de placer des structures métier ou DB dans les requêtes. Les migrations historiques de brouillon sont neutralisées afin de ne pas créer d'espace PostgreSQL applicatif ni de table qualifiée par espace logique. Validation locale effectuée : `cargo test -p kb_config` avec 35 tests passés, `cargo test -p kb_app_demo` avec 28 tests passés et `cargo clippy --all-targets` sans erreur. `kb_store_core` et `kb_store_pg` ne disposent pas encore de tests à ce stade documentaire. + +## 0.2.1 — contrats `kb_store_core` et replay instruction-level + +Validation du jalon de contrats storage : `kb_store_core` expose les types communs backend-agnostiques pour pagination, healthcheck, migrations, erreurs de stockage, DTOs applicatifs, entities proches SQL et traits repositories sans implémentation PostgreSQL. Les contrats couvrent le raw RPC et WebSocket, le cycle de vie raw `Full` / `Compacted` / `Archived` / `Purged`, les états de traitement raw, la signature optionnelle des notifications WebSocket, la déduplication par `notification_key`, le lien futur optionnel notification vers transaction canonique, ainsi que le replay par instruction. Le replay est défini comme un scheduling au niveau instruction, mais le décodage reçoit un contexte extrait complet via `CoreInstructionReplayInput` : instruction ciblée, account keys, logs, balance changes et contexte transactionnel minimal. Les repositories restent abstraits et ne créent encore ni pool PostgreSQL, ni migration SQL réelle. Validation locale effectuée : `cargo test -p kb_store_core` avec 28 tests passés et `cargo clippy --all-targets` sans avertissement. Les validations précédentes du jalon ont également confirmé `kb_store_pg` sans test actif, `kb_config` avec 35 tests passés et `kb_app_demo` avec 28 tests passés. + +## 0.2.2 — infrastructure PostgreSQL minimale + +Validation du jalon d'infrastructure PostgreSQL : `kb_store_pg` expose maintenant une première implémentation réelle avec options de connexion, pool `sqlx::PgPool`, construction depuis la configuration active, masquage du DSN pour les diagnostics, healthcheck `SELECT 1`, lecture du schema courant, lecture de la version PostgreSQL et snapshot de migrations non destructif. La stratégie de migrations reste volontairement cadrée sans créer les tables Solana lourdes, qui sont reportées à `0.2.3` pour le raw store minimal. Les validateurs de noms de tables imposent la convention `kb_sol__` et refusent les schemas explicites. Validation locale effectuée : `cargo test -p kb_store_pg` avec 11 tests passés, test PostgreSQL réel optionnel `optional_postgres_healthcheck_from_env` validé avec `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana`, `cargo test -p kb_store_core` avec 28 tests passés, `cargo test -p kb_config` avec 35 tests passés, `cargo test -p kb_app_demo` avec 28 tests passés et `cargo clippy --all-targets` sans avertissement après remplacement local de l'exception `allow` par `expect` sur l'incompatibilité connue `async_trait` / `clippy::implicit_return`. + +## 0.2.3 — raw store Solana minimal + +Validation du jalon raw store minimal : `kb_store_pg` crée et initialise les deux premières tables Solana réelles, `kb_sol_raw_rpc_transactions` et `kb_sol_raw_ws_notifications`, sans créer de schema PostgreSQL applicatif explicite. Le raw RPC est inséré de façon idempotente par signature, les notifications WebSocket sont dédupliquées par `notification_key`, la signature des notifications reste optionnelle et le lien vers la transaction RPC canonique est préparé pour les étapes suivantes. Les tables conservent les champs de cycle de vie `retention_state`, `processing_state`, `processed_at` et `processing_reason`, avec `raw_json` nullable côté SQL afin de permettre les futures politiques `compacted`, `archived` et `purged` sans migration cassante. Les requêtes et l'implémentation PostgreSQL de `RawTransactionStore` couvrent les insertions, lookup de signature, lookup de notification et marquage lifecycle. Validation locale effectuée : `cargo test -p kb_store_pg` avec 21 tests passés, incluant le test PostgreSQL réel optionnel `optional_postgres_raw_store_roundtrip_from_env` avec `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana`, `cargo test -p kb_store_core` avec 28 tests passés, `cargo test -p kb_config` avec 35 tests passés, `cargo test -p kb_app_demo` avec 28 tests passés et `cargo clippy --all-targets` sans avertissement. Une correction locale du test raw PostgreSQL a été intégrée pour appeler directement `test_signature()` depuis le module de test. + +## 0.2.4 — core Solana normalisé minimal + +Validation du jalon core store minimal : `kb_store_pg` crée et initialise maintenant les tables core `kb_sol_core_transactions`, `kb_sol_core_account_keys`, `kb_sol_core_instructions`, `kb_sol_core_inner_instructions`, `kb_sol_core_logs` et `kb_sol_core_balance_changes`, en complément du raw store `0.2.3`, sans schema PostgreSQL applicatif explicite. Les migrations et l'initializer utilisent des noms SQL préfixés et explicites pour les contraintes et index : `pk_`, `fk_`, `ck_`, `ux_` et `ix_`. L'initialisation raw/core est sérialisée par un verrou PostgreSQL `pg_advisory_xact_lock` afin d'éviter les courses concurrentes pendant les tests ou les démarrages parallèles. `CoreTransactionStore` dispose de l'implémentation PostgreSQL minimale pour insérer les transactions core, account keys, instructions, inner instructions, logs et deltas de balances, puis reconstruire `CoreInstructionReplayInput` pour les futurs décodeurs. Le script de maintenance `kb_store_pg/maintenance/drop_raw_core_store.sql` permet de réinitialiser explicitement les tables raw/core locales dans l'ordre inverse des dépendances. Validation locale effectuée : `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana cargo test -p kb_store_pg -- --nocapture` avec 30 tests passés, création vérifiée des 8 tables dans PostgreSQL, réinitialisation validée via le script de maintenance, puis `cargo clippy --all-targets` sans avertissement. Le worker d'extraction raw RPC vers core, les observations et le ledger ops sont conservés pour les jalons suivants. + +## 0.2.5 — diagnostics SQL PostgreSQL dans `kb_app_demo` + +Validation du jalon diagnostics SQL : `kb_app_demo` ajoute les fenêtres `demo_sql_diag`, `demo_sql_pg_raw` et `demo_sql_pg_core` pour inspecter en lecture seule l’état PostgreSQL courant. Le démarrage Tauri tente l’initialisation raw/core lorsque `database.postgres.auto_initialize_schema` est actif, journalise les tables créées ou déjà présentes via le splashscreen et `tracing`, puis les fenêtres exposent le profil actif, le backend, le DSN masqué, le schema courant, le healthcheck, l’état migrations et les statistiques de tables raw/core. Les payloads JSON des trois fenêtres utilisent le viewer interactif déjà utilisé par `demo_config`, sans ajouter de nouvelle dépendance npm. Le jalon ne lance ni extracteur raw RPC vers core, ni décodage, ni matérialisation ; ces traitements restent reportés à `0.3.x`. Validation locale effectuée : `cargo test -p kb_store_pg` avec 30 tests passés, `cargo test -p kb_config` avec 35 tests passés, `cargo test -p kb_app_demo` avec 32 tests passés, `cargo clippy --all-targets` sans avertissement, et validation visuelle des trois fenêtres via `cargo tauri dev -c kb_app_demo/tauri.conf.json`. + +## 0.3.0 — cadrage Agave local et sources temps réel + +Validation du jalon de cadrage `0.3.x` : la phase ingestion/backfill/extraction raw vers core est reportée à `0.4.x`, tandis que `0.3.x` devient une phase d’expérimentation Agave local non votant et de stratégie live source. Le jalon documente le profil manuel `agave_local_research`, la commande de départ `agave-validator`, les probes RPC/WS attendus, les mesures ledger/accounts/CPU/RAM/réseau/lag, les règles de fallback vers les providers externes et les critères de décision avant `0.4.x`. Le profil Agave reste désactivé par défaut afin de préserver `mainnet_research` comme base stable, et aucune ingestion de production, aucun backfill historique, aucun décodage DEX, aucune matérialisation et aucune opération de trading ne sont introduits dans ce jalon. Validation locale du delta `0.3.0-pre.001` confirmée par l’utilisateur avant mise à jour du changelog. + +## 0.3.1 — transaction canonique et observations d’acquisition + +Validation du jalon de stockage canonique : `kb_sol_raw_transactions` conserve désormais une transaction source-indépendante par signature avec `canonical_json`, `canonical_json_hash` et `canonical_format_version`, tandis que `kb_sol_obs_transaction_observations` conserve uniquement les informations de provenance, méthode, session, timestamps, taille, statut et erreur sans dupliquer le payload complet. La lignée core utilise `raw_transaction_id`; les DTOs, entities, repositories, requêtes et diagnostics Tauri ont été alignés. La migration réelle depuis le schéma `0.2.x` a été contrôlée sur PostgreSQL 17.10 : les anciennes tables `kb_sol_raw_rpc_transactions` et `kb_sol_raw_ws_notifications` sont absentes, les tables canoniques sont présentes et les colonnes actives portent les nouveaux noms. Après cette validation, les fichiers SQL ont été consolidés en une baseline propre ne contenant que `0001_canonical_transaction_store.sql` et `0002_core_store.sql`; les fichiers SQL actifs n’embarquent plus les anciennes définitions de tables. Validation locale effectuée : `cargo test -p kb_app_demo` avec 32 tests passés, `cargo test -p kb_store_core` avec 30 tests passés, `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana cargo test -p kb_store_pg -- --nocapture` avec 32 tests passés, `cargo clippy --all-targets` sans avertissement, démarrage Tauri validé et diagnostics des huit tables raw/obs/core confirmés. Les fixtures PostgreSQL de test ont été nettoyées manuellement après validation. + +## 0.3.2 — contrat canonique et adaptateur HTTP + +Validation du jalon transactionnel canonique : `kb_model` expose désormais un modèle source-indépendant couvrant les transactions legacy et version 0, le message, les comptes statiques et chargés par Address Lookup Tables, les instructions externes et internes, les logs, le statut et les erreurs, les frais, compute units et cost units, les balances SOL et SPL/Token-2022, les rewards, les return data et le block time lorsqu’ils sont disponibles. Les signatures et clés publiques restent en base58, les données d’instruction sont normalisées en base64, les montants SPL bruts sont canonicalisés sans perte et la sérialisation déterministe trie récursivement les objets JSON avant calcul du hash SHA-256. `kb_rpc` fournit l’adaptateur `getTransaction` JSON-RPC et l’interface commune préparant les futurs adaptateurs Helius WebSocket et Yellowstone gRPC dans la crate existante, tandis que `kb_store_core::RawTransactionInsert::from_canonical` relie le modèle au store canonique. Les fixtures offline couvrent legacy/v0, succès/échec, ALT, CPI, SPL Token et Token-2022, et les formes fournisseur équivalentes produisent le même hash. Validation locale effectuée : `cargo test -p kb_model` avec 23 tests passés, `cargo test -p kb_rpc` avec 48 tests passés, `cargo test -p kb_store_core` avec 31 tests passés, `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana cargo test -p kb_store_pg -- --nocapture` avec 32 tests passés, puis `cargo clippy --all-targets` sans avertissement après ajout des retours explicites imposés par `clippy::implicit_return`. Les fixtures PostgreSQL du roundtrip ont été nettoyées manuellement après validation. + +## 0.3.3 — backfill HTTP gratuit et `demo_backfill` + +Validation du jalon de backfill HTTP : `kb_rpc` expose désormais `getSignaturesForAddress` avec pagination `before` / `until`, tandis que `getTransaction` alimente le modèle canonique sans conserver durablement le payload fournisseur. `kb_pipeline` orchestre les campagnes de signatures explicites et les recherches avant ou après une signature pour un programme, un token ou un pool, avec déduplication, limites de pages, reprise exportable, concurrence, temporisation, retries bornés, arrêt coopératif et journalisation détaillée. Le parcours `after` utilise la borne RPC `until`, des pages pouvant atteindre 1 000 signatures et une sélection bornée en mémoire afin de ne conserver que les `X` signatures directement postérieures à l’ancre dans l’historique de l’adresse. `kb_app_demo` ajoute la fenêtre `demo_backfill`, organisée en accordéons Bootstrap 5, avec paramètres communs, textarea de signatures, formulaires programme/token/pool, journal, résumé et commande d’arrêt. Chaque transaction complète est insérée une seule fois dans `kb_sol_raw_transactions`; chaque tentative d’acquisition produit une observation légère dans `kb_sol_obs_transaction_observations`, tandis que les résultats `missing` et `failed` ne créent aucun faux payload canonique. + +La correction finale de l’arrêt coopératif remplace la création globale des futures par une file bornée à la concurrence effective, distingue les candidats sélectionnés, démarrés, terminés, annulés et non démarrés, et calcule `resume_before_signature` depuis le dernier préfixe contigu réellement terminé. Une campagne réelle de 500 candidats a validé l’arrêt avec 7 candidats démarrés, 3 terminés, 4 annulés et 493 non démarrés, sans journalisation artificielle des candidats restants. Validation locale effectuée : `cargo test -p kb_rpc` avec 54 tests passés, `cargo test -p kb_pipeline` avec 10 tests passés, `cargo test -p kb_store_core` avec 31 tests passés, `cargo test -p kb_store_pg` avec 32 tests passés, `cargo test -p kb_app_demo` avec 38 tests passés et `cargo clippy --all-targets` sans avertissement. Les campagnes Tauri réelles ont validé les signatures explicites, programme avant/après, token avant/après et pool avant/après ; le cas `program_after` a parcouru 5 516 signatures indexées avant de sélectionner les 5 signatures les plus proches de l’ancre. Les diagnostics PostgreSQL ont confirmé 62 transactions canoniques et 62 observations, sans alimentation prématurée des tables core, dont l’extraction reste prévue pour `0.3.4`. + +## 0.3.4 — extraction canonique vers core et clôture de la fondation `0.3.x` + +Validation du jalon d’extraction structurelle : `kb_pipeline` transforme les transactions canoniques en graphes core atomiques comprenant transactions, account keys statiques et ALT, instructions outer et inner à chemins stables, logs reliés prudemment et deltas SOL/SPL Token/Token-2022. `kb_store_core` et `kb_store_pg` exposent et implémentent les contrats de persistance, le ledger versionné `kb_sol_ops_processing_ledger`, le skip par version/hash, le force replay et le rollback complet. `kb_app_demo` ajoute `demo_core_extraction` et le navigateur read-only `demo_sql_replay_candidates` avec filtres, sélection, copie et exports CSV sous `data/exports_csv/`; `kb_program_ids` fournit un registre énumérable de 182 programmes. Les transactions Solana échouées restent extractibles et seront décodables comme observations d’intention ou d’échec, sans être matérialisées automatiquement comme mutations réussies. Validation réelle effectuée sur PostgreSQL 17.10 : 70 transactions extraites sans erreur, sept contrôles d’intégrité sans anomalie, replay normal de 14 signatures entièrement ignoré, force replay des mêmes 14 signatures sans duplication, second replay entièrement ignoré, cardinalité finale de 70 transactions core et ledger final de 70 succès pour 84 tentatives. Validation locale finale : `cargo test -p kb_pipeline` avec 24 tests passés, `KB_POSTGRES_TEST_URL=... cargo test -p kb_store_pg -- --nocapture` avec 39 tests passés dont le rollback/replay corrigé, `cargo test -p kb_app_demo` avec 60 tests passés, `cargo test -p kb_program_ids` avec 2 tests passés et `cargo clippy --all-targets` sans avertissement. L’ancien jalon `0.3.5` est absorbé dans cette version ; la suite active devient `0.4.0` pour l’infrastructure commune de décodage et de matérialisation. + +## 0.4.0 — infrastructure commune de décodage et de matérialisation + +Validation du socle commun de replay contextualisé : `kb_decoder_api` et `kb_materializer_api` travaillent sur les inputs core complets, `kb_pipeline` sélectionne uniquement les programmes compatibles avec les processors activés, effectue un dispatch déterministe par programme et chemin d’instruction, persiste atomiquement couverture, ledger, événements décodés et sorties matérialisées, et applique le skip par processor/version/hash. Le force replay est autorisé uniquement avec des signatures explicites ou avec l’autorisation opérateur « toutes les signatures » bornée par les filtres et la limite. La matérialisation est refusée lorsqu’aucun matérialiseur n’est enregistré. Le classifieur `solana_native_classifier` déclare les 18 surfaces natives/runtime/loaders/précompiles connues sans produire de faux décodage. + +`kb_app_demo` ajoute `demo_decode_replay`, ses diagnostics decode/ledger/couverture et les contrôles de replay normal, force replay ciblé, replay global explicitement autorisé, concurrence bornée et arrêt coopératif. Chaque campagne possède un `campaign_id` propagé par spans jusqu’au pipeline, au store PostgreSQL et au décodeur. Les migrations raw/core/decode sont appliquées une seule fois au démarrage Tauri ; les commandes de démo réutilisent ensuite le schéma initialisé. Les validations frontend du backfill bloquent localement les requêtes incomplètes sans produire de faux événements backend `ERROR`. + +Validation locale finale : `cargo test -p kb_config` avec 36 tests passés, `cargo test -p kb_pipeline` avec 41 tests passés, `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana_test cargo test -p kb_store_pg -- --nocapture` avec 44 tests passés, `cargo test -p kb_app_demo` avec 75 tests passés et `cargo clippy --all-targets` sans avertissement. Les campagnes Tauri réelles ont validé neuf skips version/hash, neuf force replays ciblés, un second skip cohérent, un replay global strictement limité à cinq inputs, l’annulation puis le redémarrage d’une campagne, et la corrélation complète des traces. Le correctif final d’annulation garantit `started + not_started = selected` et `completed = started`; une campagne réelle a terminé avec 382 inputs sélectionnés, 45 démarrés/terminés, 337 non démarrés, 45 dispatchs `unsupported`, aucun unmatched, aucun échec et aucun événement dans `errors.jsonl`. La couverture PostgreSQL confirme 18 déclarations natives, 382 observations Compute Budget reconnues sans erreur et 94 observations System reconnues sans erreur. `0.4.1` devient le jalon actif pour le décodage maximal des programmes Solana natifs, loaders et précompiles. + +## 0.4.1 — programmes Solana natifs, loaders, précompiles et matérialisations natives + +Validation du jalon `0.4.1` : `kb_decoder_solana_core` couvre désormais les programmes Solana natifs/runtime, historiques, loaders, précompiles et surfaces ZK explicitement incluses dans le périmètre. Le registre exécutable natif contient 18 surfaces après reclassification de `StakeConfig11111111111111111111111111111111` comme compte historique non exécutable et ajout du stateless Slashing Program `S1ashing11111111111111111111111111111111111` observé dans Agave `v4.1.1` derrière feature gate. Les surfaces décodées incluent System, Vote, Stake, Config, Compute Budget, Address Lookup Table, Feature, ZK ElGamal Proof, l’ancien ZK Token Proof historique/no-op, les loaders Native/BPF v1/v2/Upgradeable/v4, Ed25519, secp256k1, secp256r1 et Slashing. SPL Memo, SPL Token, Token-2022, Stake Pool, Single Pool, Account Compression, Noop et SPL Name Service restent réservés aux jalons suivants. + +Le jalon a étendu le contrat de replay instructionnel avec les instructions outer ordonnées afin de résoudre les références inter-instructions des précompiles et du profil Compute Budget. Les décodeurs bornent les offsets, tailles, compteurs, références d’instructions, comptes requis et payloads suffixes sans recalcul cryptographique pour les signatures ou preuves ZK. L’ancien `solana-zk-token-sdk` déprécié a été retiré ; le miroir ZK Token historique repose maintenant sur une table wire locale auditée, sans dépendance directe à `bincode`. + +Les matérialisations natives sont clôturées au niveau instructionnel stable. `kb_materializer_lifecycle` projette Address Lookup Table, lifecycle des loaders, révocation Feature, durable nonce, comptes System create/allocate, contextes ZK ElGamal et rapports Slashing. `kb_materializer_admin` projette assignations System, écritures Config opaques et changements d’autorité Loader. `kb_materializer_compliance_audit` projette écritures/copies de bytecode Loader et profils Compute Budget transactionnels agrégés. `kb_materializer_staking` projette les intentions commitées Stake/Vote : comptes, autorités, lockup, délégation, désactivation, split, merge, retraits, move stake/lamports, identité Vote, commission, vote state et dépôt de rewards. Les sorties ne prétendent pas reconstruire les snapshots finaux de comptes, les sysvars historiques, les crédits Vote cumulés, l’activation Stake par epoch, les unités compute consommées ou les frais finaux runtime. + +`kb_app_demo` enregistre les matérialiseurs natifs `solana_native_lifecycle`, `solana_native_admin`, `solana_native_compliance_audit` et `solana_native_staking`. Les profils de logging montent à 53 routes avec fichiers dédiés pour les crates opérationnelles ajoutées. `demo_backfill` accepte maintenant les modes `program_latest`, `token_latest` et `pool_latest` : une recherche « avant, plus anciennes » sans ancre commence sur les signatures les plus récentes, tandis que la direction « après, plus récentes » conserve une ancre obligatoire. + +Les validations utilisateur finales confirment : `kb_program_ids` 5 tests, `kb_decoder_solana_core` 114 tests, `kb_materializer_lifecycle` 16 tests, `kb_materializer_admin` 7 tests, `kb_materializer_compliance_audit` 7 tests, `kb_materializer_staking` 8 tests, `kb_config` 36 tests, `kb_pipeline` 45 tests, `kb_store_pg` 45 tests avec PostgreSQL réel, `kb_app_demo` 78 tests et `cargo clippy --all-targets` sans avertissement. Le correctif de sérialisation test-only des tests PostgreSQL réels élimine l’interblocage intermittent observé lors des tests optionnels concurrents. + +Les campagnes mainnet ciblées validées couvrent notamment : System 817 inputs décodés avec 258 sorties et 52 refus attendus sur transactions échouées ; Config 96/96 avec 96 sorties admin ; Stake 448/448 avec 447 sorties et un refus attendu ; Vote 600/600 avec 600 sorties ; Compute Budget 2 746/2 746 avec 1 603 profils transactionnels matérialisés ; ZK ElGamal 151/151 avec 139 sorties de contexte ; ALT 104/104 avec 104 sorties lifecycle ; Slashing sans corpus observable, validé par sources officielles et fixtures synthétiques. Tous ces replays se terminent avec `unmatched = 0`, `failedInputs = 0`, `unsupported = 0` et journaux `error.jsonl` vides dans les archives fournies. + +La suite active devient `0.4.2` : infrastructure d’exécution et `kb_executor_solana_core`, avec séparation stricte entre plan, simulation, signature, envoi et validation post-exécution. Les exécuteurs SPL suivront ensuite la politique décodeur maximal → matérialisateurs → exécuteur limité par opération. + +## 0.4.2 — infrastructure d’exécution native et RPC Solana standard + +Validation du jalon d’exécution native. `kb_execution_api`, `kb_execution_safety`, `kb_execution_solana`, `kb_wallet`, `kb_executor_solana_core`, `kb_rpc` et `kb_pipeline` séparent explicitement intent, plan, politique, lecture stateful, simulation, signature, envoi, confirmation et validation post-exécution. Le wallet temporaire de laboratoire reste confiné à Localnet/Devnet ; dry-run et simulation sont obligatoires par défaut, les plafonds de dépense/frais sont vérifiés et Mainnet reste désactivé sans politique et confirmation explicites. + +`kb_executor_solana_core` couvre 18 surfaces natives avec 109 opérations appelables : System, Compute Budget, Address Lookup Table, précompiles Ed25519/secp256k1/secp256r1, Config, Feature, Slashing, ZK ElGamal, Stake, Vote, Loader v3 et Loader v4. BPF Loader v1/v2, Native Loader et l’ancien ZK Token Proof sont classés sans builder client ; Stake `Redelegate` reste historique `decode-only`. Chaque builder est comparé à un encodeur officiel ou à un layout wire audité. La matrice `docs/NATIVE_SOLANA_EXECUTION_MATRIX.json` est vérifiée par un test Rust contre les 109 codes compilés. `kb_decoder_solana_core` conserve l’égalité exacte avec les 18 Program IDs natifs et 121 déclarations de couverture. + +Le préflight stateful de `kb_pipeline` inspecte ALT, Config, Feature, Slashing et les contextes ZK ElGamal sur Localnet/Devnet et retourne `NotRequired`, `Ready` ou `Blocked`. Les opérations administratives non validées réellement par un scénario cluster restent derrière ce garde-fou, hors de l’UI et sans activation Mainnet. Le parcours Devnet représentatif System transfer a été validé avec un wallet persistant préfinancé et 1 000 000 lamports : simulation exacte, signature, envoi, confirmation, `getTransaction`, insertion canonique, extraction core et decode replay ciblé ont réussi. + +`kb_rpc` expose des contrats propres et configurables pour les 52 méthodes HTTP standard et les neuf paires WebSocket standard. Les options sont composables sans exposer les DTO Agave. `WsSession` gère multiplexage, unsubscribe, timeout, reconnexion bornée et réabonnement. `blockSubscribe`, `slotsUpdatesSubscribe` et `voteSubscribe` sont activables par endpoint ; un rejet serveur indiquant une méthode absente ou non activée désactive automatiquement la capacité pour la session. `kb_app_demo` ne contient plus la boucle de transport WebSocket. + +La frontière Tauri/TS-rs a été corrigée pour ne plus transmettre de `bigint` dans les payloads JSON actifs. Les identifiants WebSocket sont contrôlés contre `Number.isSafeInteger` et les limites du navigateur SQL sont validées avant l’invocation backend. Les logs réels confirment les souscriptions/désabonnements WebSocket et le garde-fou de changement d’endpoint avec session active. + +Validation finale fournie par l’utilisateur : `kb_program_ids` 5 tests, `kb_decoder_solana_core` 116, `kb_executor_solana_core` 88, `kb_execution_api` 22, `kb_execution_safety` 15, `kb_execution_solana` 12, `kb_rpc` 113, `kb_pipeline` 56, `kb_config` 41, `kb_app_demo` 88 et `kb_store_pg` 45 avec PostgreSQL réel. Le test Devnet opt-in et le replay post-exécution sont réussis, `cargo clippy --all-targets` est propre et `cargo tauri dev` démarre avec Vite et initialise correctement PostgreSQL. Aucun jalon suivant n’est ouvert par cette clôture. + +## 0.4.3 — SPL Memo v1, v3 et v4 + +Validation complète de la surface SPL Memo. `kb_decoder_spl_memo` reconnaît exactement les Program IDs v1, v3 et v4, décode le payload UTF-8 brut, sa longueur et son SHA-256, conserve les comptes ordonnés et leurs flags, distingue les règles historiques de signataires, les chemins outer/inner, les transactions réussies ou échouées et les tentatives invalides sans produire de faux état commité. La matrice machine-readable `docs/SPL_MEMO_MATRIX.json` relie les contrats officiels, les fixtures synthétiques et le corpus Mainnet réel des trois générations. + +`kb_materializer_transaction_annotations` projette uniquement les Memo réussis et commités dans la famille `transaction_annotation`, avec texte borné, génération, Program ID, signataires vérifiés, provenance et clé d’idempotence. Les comptes complets et diagnostics d’audit restent dans le decode. La persistance réutilise `kb_sol_mat_events` et le ledger commun sans nouvelle table ; `demo_decode_replay` expose un journal typé et borné filtrable par signature. + +`kb_executor_spl_memo` fournit les intents typés v1/v3/v4 et construit les trois générations avec `spl-memo-interface 2.1.0`, payload exact et signataires readonly ordonnés. La bibliothèque reste universelle sur les clusters ; simulation obligatoire, dry-run par défaut, plafond de frais, autorisation opérateur et politiques Mainnet appartiennent à l’infrastructure commune. Le parcours Memo v4 Devnet réutilise wallet, RPC, safety, transaction Solana, confirmation, hydratation canonique, extraction core et decode replay. Trois transactions réelles ont chacune produit une annotation et un second replay idempotent `skipped`. + +La clôture utilisateur confirme : `kb_program_ids` 5 tests, `kb_decoder_spl_memo` 9, `kb_materializer_transaction_annotations` 7, `kb_executor_spl_memo` 15, `kb_execution_api` 22, `kb_execution_safety` 15, `kb_execution_solana` 12, `kb_pipeline` 61, `kb_rpc` 113, `kb_app_demo` 96 et `kb_store_pg` 46 avec PostgreSQL réel. Le test opt-in Devnet avec envoi est réussi et `cargo clippy --all-targets` est propre. La suite active devient `0.4.4 — SPL Token` selon `prompts/023_v0_4_4_spl_token.md`; l’acquisition historique différée reste hors ROADMAP actif. + +## 0.4.4 — SPL Token classique + +Validation complète du programme SPL Token classique `Tokenkeg...`. `kb_decoder_spl_token` +reconnaît exactement les 28 tags publiés par `spl-token-interface 3.0.0`, conserve le wire borné, +les comptes ordonnés, outer/inner paths, autorités simples ou multisig, montants bruts, decimals, +transactions échouées non commitées et diagnostics sans inventer mint ni état final. `Batch` +conserve son parent et ses enfants déterministes sans accepter les batchs imbriqués. La matrice +`docs/SPL_TOKEN_MATRIX.json` relie interface, processor, builders, corpus et preuves cluster. + +Les projections stables produisent des événements instructionnels `token_account`, `admin` et +`risk`, sans fee classique fictive, sans OHLC et sans snapshot final reconstruit. Le replay Mainnet +de 370 instructions a produit 371 sorties, huit refus correspondant exactement à huit transactions +échouées, puis un second replay idempotent sans nouvelle sortie. Le journal Tauri expose des filtres +bornés par signature, mint, compte, famille et opération, avec montants JSON sans perte. + +`kb_executor_spl_token` expose 24 opérations typées courantes ou récentes et laisse les quatre +initialisations Rent obsolètes en decode-only. Le préflight stateful Localnet/Devnet valide layouts, +relations mint/comptes, soldes, native reserve, délégations, autorités, multisig et rent hors du +décodeur et de l'exécuteur. Le parcours Devnet `TransferChecked` a réussi simulation, envoi, +confirmation, hydratation, extraction, replay, matérialisation et second replay idempotent. Un +lifecycle contrôlé sans ATA a ensuite initialisé mint et comptes, minté, transféré, approuvé, +révoqué, brûlé puis fermé les comptes avec récupération sûre après interruption RPC. + +Les tags récents `Batch` et `UnwrapLamports` ont été simulés avec succès sur le programme classique +Devnet, sans signature ni envoi, respectivement en 270 et 140 compute units. La clôture Tauri charge +77 routes de logs, réussit le préflight et la simulation `TransferChecked`, bloque l'envoi sans +confirmation, affiche la matérialisation commitée exacte et simule Memo v4 sans erreur runtime. + +La régression finale communiquée confirme notamment : `kb_program_ids` 5 tests, +`kb_decoder_spl_token` 8, `kb_executor_spl_token` 15, `kb_decoder_spl_memo` 9, +`kb_materializer_transaction_annotations` 7, `kb_execution_api` 22, `kb_execution_safety` 15, +`kb_execution_solana` 12, `kb_pipeline` 81, `kb_rpc` 113, `kb_config` 41, `kb_app_demo` 102 et +`kb_store_pg` 46 avec PostgreSQL réel. `cargo clippy --all-targets` est propre. La suite active +devient `0.4.5 — SPL Associated Token Account` selon +`prompts/024_v0_4_5_spl_associated_token_account.md`. + +## 0.4.5 — SPL Associated Token Account + +Validation complète du programme SPL Associated Token Account +`ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL`. Le décodeur consomme +`spl-associated-token-account-interface 2.0.0`, reconnaît exactement `Create`, +`CreateIdempotent`, `RecoverNested` et la forme historique vide de `Create`, puis conserve comptes +ordonnés, flags, doublons, chemins outer/inner, transactions échouées et diagnostics bornés. Les +adresses observées sont comparées aux PDA canoniques dérivés depuis wallet, Token Program ID et +mint ; une incohérence reste décodable et n'est jamais corrigée silencieusement. + +`kb_materializer_token_accounts` possède exclusivement le lifecycle de l'adresse associée : créée, +créée ou réutilisée idempotemment, ou récupérée depuis une imbrication. `kb_materializer_risk` +possède le constat distinct d'anti-pattern nested récupéré, sans score arbitraire. Les CPI SPL Token +restent propriétaires des initialisations, transferts et fermetures, et les matérialiseurs lifecycle +général, admin et fees ne produisent aucun doublon. Les transactions non commitées ou invalides sont +refusées comme mutations. + +`kb_executor_spl_associated_token_account` expose les trois variantes actuelles pour SPL Token +classique et Token-2022 avec les builders officiels, metas dans l'ordre exact, signataires uniques, +simulation obligatoire, dry-run par défaut et coûts rent/frais plafonnés. Le pipeline stateful +contrôle mints, owners, Program IDs, dérivations, comptes existants, rent, signataires et +postconditions avant et après l'exécution. La fenêtre Tauri Solana existante ajoute création ATA, +dérivation readonly et journal lifecycle borné ; `RecoverNested` reste volontairement hors UI. + +Les scénarios Devnet contrôlés du 16 juillet 2026 ont validé la création et la réutilisation +idempotente d'ATA classiques et Token-2022, puis un `RecoverNested` classique transférant +1 000 000 000 unités brutes vers l'ATA wallet et fermant le nested ATA. Chaque envoi a suivi +simulation, confirmation, hydratation canonique, extraction, decode, matérialisation et second +replay idempotent. L'ATA Token-2022 observé mesure 170 octets et expose `ImmutableOwner`, sans que le +jalon ne commence le décodage général de ses extensions. + +La clôture confirme 5 tests Program IDs, 8 tests décodeur Token classique, 15 tests exécuteur Token, +9 tests Memo, 7 tests annotations, 9 tests matérialiseur token accounts, 6 tests risk, 17 tests +lifecycle, 9 tests admin, 3 tests fees, 10 tests décodeur ATA, 10 tests exécuteur ATA, 22 tests API +d'exécution, 15 tests safety, 12 tests transaction Solana, 90 tests pipeline, 113 tests RPC, 41 tests +configuration, 111 tests démo et 46 tests PostgreSQL réels. `cargo clippy --all-targets` est propre +et `cargo tauri dev -c kb_app_demo/tauri.conf.json` a démarré Vite, Tauri et PostgreSQL. La suite +active devient `0.4.6 — Token-2022, extensions et registre ElGamal` selon +`prompts/025_v0_4_6_spl_token_2022.md`. + +## 0.4.6 — Token-2022, extensions et registre ElGamal + +Validation du jalon Token-2022 et ElGamal Registry avec frontières techniques séparées. Token-2022 couvre les instructions de base, les familles d’extensions identifiables, les états Mint/Account/Multisig et TLV, les metadata/group incorporés, les parcours confidentiels, les matérialisations propriétaires et les exécuteurs simulation-first avec préflights stateful et preuves inline/context-state. Huit opérations publiques Token-2022 ont été confirmées sur Devnet, hydratées, extraites, décodées, matérialisées et rejouées idempotemment. Le corpus Mainnet contient 55 instructions Token-2022 décodées sans échec. +Le registre ElGamal dispose de son décodeur, de sa matrice, de son parser d’état de 64 octets, de sa matérialisation administrative, de ses builders `CreateRegistry`/`UpdateRegistry`, de son PDA, de ses préflights et de son orchestration de preuves. La validation réseau publique reste indisponible sur Devnet/Mainnet au 20 juillet 2026 ; la validation synthétique/offline est conservée sans inventer de preuve cluster. +La validation Mainnet a corrigé le wire `Write` du loader immuable : longueur `u32` et données à l’offset 12, supprimant 68 diagnostics `loader_vector_length_mismatch`. Les 76 rejets restants sont tous issus de transactions Solana échouées et restent explicitement fail-closed ; quatre tags Loader v4 inconnus sont classés `Unsupported`. Un timeout ponctuel du pool PostgreSQL a été réparé par replay ciblé. +Le replay de `kb_app_demo` ajoute `incomplete_signatures`, qui sélectionne seulement les signatures contenant une instruction `pending`, `failed`, `replay_requested` ou `unsupported`, limite d’abord les signatures puis étend leurs instructions compatibles sans force replay global. Deux campagnes PostgreSQL réelles ont produit le même résultat : 81 entrées, 1 décodée, 4 unsupported, 76 failed, aucun unmatched et aucun doublon decode/mat. Validation finale : `kb_store_core` 41/41, `kb_store_pg` 46/46 avec PostgreSQL réel, `kb_pipeline` 124/124, `kb_app_demo` 118/118 et `cargo clippy --all-targets` propre. La suite active devient `0.4.7 — Metaplex Token Metadata` selon `prompts/026_v0_4_7_metaplex_token_metadata.md`. Anchor n’étant pas requis par les programmes SPL de `0.4.x`, son infrastructure commune est planifiée en `0.4.18`, immédiatement avant Meteora. + diff --git a/docs/legacy-khadhroony-bot2/README.md b/docs/legacy-khadhroony-bot2/README.md new file mode 100644 index 0000000..2b10c8e --- /dev/null +++ b/docs/legacy-khadhroony-bot2/README.md @@ -0,0 +1,293 @@ + + + +# Khadhroony Bot2 + +Khadhroony Bot2 est un workspace Rust modulaire pour observer, décoder, matérialiser, exécuter et analyser des transactions Solana. + +Le projet poursuit deux objectifs complémentaires. À court terme, il doit fournir un trading assisté contrôlable depuis des démos live Tauri. À long terme, il doit devenir une bibliothèque d'indexation et d'analyse Solana capable de classifier un grand nombre de programmes et de surfaces. + +Le socle technique doit permettre de : + +- ingérer des transactions Solana depuis RPC HTTP, WebSocket ou futures sources spécialisées ; +- normaliser chaque source vers une transaction canonique source-indépendante ; +- conserver la transaction canonique comme source d’audit et de replay ; +- conserver séparément les observations légères de provenance et de timing ; +- extraire les instructions, inner instructions, logs, comptes résolus et deltas de balances ; +- détecter les programmes impliqués dans chaque transaction ; +- décoder les programmes Solana de base, les programmes SPL, les surfaces DEX, les orderbooks, les routers et les protocoles annexes ; +- matérialiser les événements décodés en tables métier exploitables ; +- rejouer uniquement les modules nécessaires grâce à un ledger de traitement par module et version ; +- alimenter des agrégations, diagnostics, signaux de risque et futures règles de stratégie ; +- construire des plans d'exécution séparés des décodeurs ; +- tester les fonctionnalités live via `kb_app_demo` avant automatisation. + +## Architecture générale + +Le workspace suit une chaîne de traitement en couches : + +```text +canonical transaction -> source observations -> Solana extraction -> decoding -> materialization -> aggregation -> strategy +``` + +Chaque couche doit être indépendante autant que possible. Les décodeurs ne dépendent pas du stockage, du RPC, de Tauri, du wallet ou des matérialisateurs. Les matérialisateurs travaillent sur les événements décodés et produisent des événements métier normalisés. + +## Rôle des dossiers principaux + +- `kb_core` contient les primitives partagées minimales. +- `kb_model` contient les types communs du modèle interne. +- `kb_config` définit le contrat JSON multi-profils, le schéma runtime, les validations typées et les exports TS-rs destinés aux applications Tauri. +- `kb_rpc` contient les protocoles et clients RPC Solana HTTP, WebSocket et futurs flux Helius/Yellowstone. +- `kb_execution_solana` assemble les plans communs en transactions Solana, lie la simulation au message exact et résout les signataires via l’interface Solana `Signer`; `kb_wallet` fournit actuellement le backend temporaire. +- Le projet différé d’acquisition historique gratuite est décrit dans `docs/HISTORICAL_DATA_ACQUISITION_PLAN.md`. Il reste hors du ROADMAP actif tant que les décodeurs, matérialisateurs et exécuteurs prioritaires ne sont pas suffisamment avancés. +- `kb_store_core` définit les traits de stockage. +- `kb_store_pg` fournit le backend PostgreSQL cible. +- `kb_store_sqlite` reste limité aux tests et imports locaux. +- `kb_decoder_api` définit le contrat des décodeurs. +- `kb_materializer_api` définit le contrat des matérialisateurs. +- `kb_materializer_*` contient les projections métier par famille : trades, liquidité, NFT, oracle, lending, routing, compliance audit, etc. +- `kb_pipeline` orchestre backfill, extraction canonique vers core, décodage, matérialisation, agrégation et validation. +- `kb_app_demo` sert de démonstrateur Tauri, sans logique métier lourde. Il charge le profil actif, initialise `kb_logging`, rend le README en HTML et expose une fenêtre dédiée à la configuration. +- Chaque crate opérationnelle doit émettre ses propres événements `tracing` avec une cible canonique égale à son nom Cargo ; `kb_app_demo` reste limité aux frontières UI/Tauri et aux résumés. +- Chaque profil écrit des fichiers globaux `debug.log`, `info.log`, `error.jsonl` et `app.log`, ainsi que `debug.log`, `info.log` et `error.jsonl` dans un répertoire par crate utilisant `tracing`. +- Les interfaces officielles Solana/SPL étroites sont privilégiées ; `wincode` puis Borsh sont préférés, et un parser local borné reste autorisé lorsque l’interface officielle n’expose que `bincode`. + + +## État `0.4.1` validé + +`0.4.1` clôt le décodage et les matérialisations instructionnelles stables des programmes Solana natifs, historiques, loaders, précompiles et surfaces ZK du périmètre core. + +`kb_decoder_solana_core` couvre maintenant 18 surfaces exécutables natives : System, Vote, Stake, Config, Compute Budget, Address Lookup Table, Feature, ZK ElGamal Proof, l’ancien ZK Token Proof historique/no-op, Native Loader, BPF Loader v1/v2/Upgradeable/v4, Ed25519, secp256k1, secp256r1 et Slashing. `StakeConfig11111111111111111111111111111111` est classé comme compte historique non exécutable, sans dispatch ni plan d’exécution. + +Les matérialisations natives actives sont réparties par famille : + +- `kb_materializer_lifecycle` : Address Lookup Table, lifecycle loaders, Feature revoke, durable nonce, comptes System create/allocate, contextes ZK ElGamal et rapports Slashing ; +- `kb_materializer_admin` : assignations System, écritures Config opaques, changements d’autorité Loader ; +- `kb_materializer_compliance_audit` : écritures/copies de bytecode Loader et profil transactionnel Compute Budget ; +- `kb_materializer_staking` : projections instructionnelles Stake/Vote commitées, sans prétendre reconstruire les snapshots finaux. + +Les précompiles de signature, les preuves ZK sans compte de contexte et l’ancien ZK Token Proof restent en decode/audit, sans duplication dans `kb_sol_mat_events` tant qu’aucun consommateur dédié ne l’exige. Les transactions échouées restent décodées comme intentions ou échecs, mais les matérialiseurs mutables appliquent `SuccessfulCommittedOnly`. + +Les validations finales de `0.4.1` couvrent les tests unitaires, PostgreSQL réel, Clippy et des replays mainnet ciblés : System, Config, Stake, Vote, Compute Budget, ALT, ZK ElGamal et Slashing synthétique. Les replays terminent sans `unmatched`, `failedInputs` ou `unsupported`; les archives de logs fournies ont des `error.jsonl` vides. + +## Couche d'exécution + +Le workspace développe une couche d'exécution séparée du décodage. Les crates `kb_executor_*` sont des bibliothèques universelles : elles construisent des plans pour les applications de trading, les CLI, les workers, les outils d'administration et de futurs exécutables indépendants. + +Les opérations dangereuses ne sont pas supprimées des bibliothèques. Elles doivent être encodées, testées et protégées par des politiques renforcées, tandis que `kb_app_demo` peut volontairement ne pas les exposer. Les exécuteurs ne dépendent ni du wallet, ni du RPC, ni des décodeurs ; l'orchestration assemble ensuite plan, sécurité, simulation, signature, envoi et validation post-exécution. + +## Démos live + +`kb_app_demo` reste le shell Tauri unique pour valider les capacités du workspace : configuration, logs, DB, RPC, wallet, listeners, décodage, matérialisation, exécution et validation post-transaction. + +## Documentation + +- `RUST_RULES.md` contient les règles Rust générales réutilisables ; `RULES.md` contient les règles spécifiques et normatives du workspace. +- `ROADMAP.md` contient les étapes futures et changements prévus. +- `CHANGELOG.md` ne reçoit une entrée qu'après validation d'une version. +- `docs/` contient les notes d'architecture et de conception, notamment `TRACING_CONTRACT.md`, `SOLANA_INTERFACE_DEPENDENCIES.md`, `NATIVE_SOLANA_CLOSURE_AUDIT.md`, `NATIVE_SOLANA_MATERIALIZATION_AUDIT.md` et `DEVNET_EXECUTION.md`. +- `validation_sql/` contient les contrôles SQL de validation. + +## Jalon validé `0.4.2` + +`0.4.2` valide l’infrastructure d’exécution et `kb_executor_solana_core`. L’exécuteur expose **109 opérations déterministes** : dix-sept System, quatre Compute Budget, cinq Address Lookup Table, six formes de précompile, deux Config, deux Feature, deux Slashing, trois ZK ElGamal, vingt-quatre Stake, vingt-quatre Vote, onze Loader v3 et neuf Loader v4. La surface Vote couvre les vingt variantes wire actuelles de `solana-vote-interface 6.0.3` ainsi que quatre plans composés de création legacy/V2, avec ou sans seed. Loader v3 consomme `solana-loader-v3-interface 8.x` et son schéma `wincode`; Loader v4 reproduit explicitement son wire officiel sans activer les helpers conditionnés par `bincode`. BPF Loader v1/v2 restent historiques `decode-only`, Native Loader ne possède pas de builder client et l’ancien ZK Token Proof reste historique. Les opérations nécessitant un état courant restent protégées par un préflight séparé et ne sont pas activées sur Mainnet par ce jalon. + +`kb_pipeline` fournit le préflight stateful Localnet/Devnet pour ALT, Config, Feature, Slashing et les contextes ZK ElGamal. Le rapport `Ready`/`Blocked`/`NotRequired` expose les contrôles et faits mesurés avant simulation. `docs/NATIVE_SOLANA_EXECUTION_MATRIX.json` inventorie les dix-huit surfaces et les 109 opérations appelables, avec égalité imposée par un test Rust. La matrice décodeur couvre les mêmes dix-huit surfaces et 121 déclarations de couverture. + +`kb_rpc` possède des contrats propres et configurables pour les 52 méthodes HTTP standard et les neuf paires WebSocket standard. Les options restent composables champ par champ et aucun DTO Agave n’est exposé par l’API publique. La session WebSocket persistante gère multiplexage, unsubscribe, timeout, reconnexion et réabonnement. `blockSubscribe`, `slotsUpdatesSubscribe` et `voteSubscribe` sont activables par endpoint ; lorsqu’un nœud les rejette comme indisponibles, la capacité concernée est désactivée automatiquement pour la session et les appels suivants sont refusés localement. + +`kb_wallet` fournit un wallet temporaire en mémoire ou persistant sous `wallets/temporary/`, avec fichiers non écrasés, permissions privées et secrets confinés côté Rust. `kb_execution_solana` assemble les transactions, lie la simulation au hash exact du message et signe seulement après autorisation de `kb_execution_safety`. `kb_pipeline` orchestre le parcours backend Devnet complet : contrôle du destinataire et du minimum rent-exempt, financement plafonné, System transfer, simulation exacte, signature, envoi, confirmation, hydratation canonique, extraction core et decode replay ciblé. Ce parcours a été validé réellement sur Devnet avec un transfert de 1 000 000 lamports et PostgreSQL réel. + +La frontière Tauri/TS-rs n’émet plus de `bigint` dans les payloads JSON actifs. Les identifiants WebSocket sont contrôlés contre `Number.isSafeInteger`, et les limites du navigateur SQL sont validées côté frontend avant l’appel backend. `cargo tauri dev` avec Vite constitue la validation frontend retenue ; aucun `npm run build` séparé n’est exigé pour cette clôture. + +## Jalon validé `0.4.3` + +Le prompt `prompts/022_v0_4_3_spl_memo.md` reste l’historique de travail du jalon. Le décodeur contextualisé distingue v1, v3 et v4 avec payload UTF‑8 ou tentative invalide, comptes ordonnés, signataires, outer/inner, hash et intentions non commitées. La projection dédiée `kb_materializer_transaction_annotations` transforme uniquement les Memo réussis et commités en annotations consultables et idempotentes ; les faits d’audit détaillés restent dans le decode. `demo_decode_replay` expose ces sorties comme un journal borné filtrable par signature, avec slot, instruction path, génération, texte, hash et signataires vérifiés. + +`kb_executor_spl_memo` expose maintenant un intent typé `AddMemo` et appelle le builder officiel `spl-memo-interface 2.1.0` avec l’ID explicite v1/v3/v4. Le wire reste le texte UTF‑8 brut et chaque signer est readonly, ordonné et visible avant signature. La bibliothèque construit les trois générations sur tout cluster ; la disponibilité effective du programme est vérifiée par la simulation obligatoire, et Mainnet reste régi par les garde-fous communs plutôt que désactivé dans l’exécuteur. La démo opérateur reste volontairement limitée à Memo v4 Devnet et réutilise la fenêtre Solana existante. Elle montre la simulation, l’envoi explicitement confirmé, l’hydratation, l’extraction, le replay, l’annotation et l’idempotence avec des DTO Tauri JSON sans `bigint` actif. + +Le parcours a été validé le 15 juillet 2026 avec une simulation seule puis trois transactions v4 Devnet confirmées. Chaque signature a produit une insertion canonique, une extraction core, un événement Memo, une annotation `transaction_annotation` et un second replay `skipped` sans nouvelle sortie. Le corpus Mainnet réel couvre séparément v1, v3 et v4 ; aucun envoi historique v1/v3 sur Devnet n’est revendiqué ni requis. + +La matrice `docs/SPL_MEMO_MATRIX.json` conserve les différences historiques, le corpus Mainnet réel, les preuves Devnet v4 et les limitations explicitement non revendiquées pour v1/v3. Le worker d’acquisition historique reste un projet différé indépendant, sans numéro de version attribué. + +La validation finale confirme 5 tests `kb_program_ids`, 9 tests décodeur Memo, 7 tests matérialiseur d’annotations, 15 tests exécuteur Memo, 22 tests API d’exécution, 15 tests safety, 12 tests Solana, 61 tests pipeline, 113 tests RPC, 96 tests démo et 46 tests PostgreSQL réels. Le test opt-in Devnet avec envoi et `cargo clippy --all-targets` sont propres. La version active devient `0.4.4 — SPL Token` selon `prompts/023_v0_4_4_spl_token.md`. + +## Jalon validé `0.4.4` + +Les premières tranches SPL Token établissent la matrice machine-readable des 28 tags publiés par +`spl-token-interface 3.0.0` et remplacent le scaffold du décodeur classique. Le wire local borné +conserve montants bruts, decimals explicites, autorités, comptes ordonnés, chemins outer/inner, +transactions échouées non commitées, suffixes et diagnostics. `Batch` produit un parent d'audit et +des enfants à identités dérivées stables sans accepter de batch imbriqué. + +Les matérialiseurs actifs produisent un journal instructionnel `token_account`, des faits admin et +des constats de risque sans score ; aucune fee classique n'est inventée. Le replay Mainnet validé de +370 instructions a produit 371 sorties, huit refus expliqués par huit transactions échouées et un +second replay idempotent sans nouvelle sélection. + +`kb_executor_spl_token` expose 24 opérations typées courantes ou récentes avec montants JSON en +chaînes, autorités simples/multisig et builders officiels. Quatre initialisations Rent obsolètes +restent decode-only. `UnwrapLamports` et `Batch` sont constructibles selon l'interface publiée ; leur +déploiement sur le programme classique Devnet a été observé le 16 juillet 2026 par deux simulations +réussies sans signature ni envoi. Localnet, Testnet et Mainnet restent non testés pour ces deux tags. + +`0.4.4-pre.005` ajoute le préflight stateful Localnet/Devnet dans `kb_pipeline`. Les lectures RPC +restent hors du décodeur et de l’exécuteur ; elles valident les layouts Mint/Account/Multisig, +l’état, les relations de mint, les soldes, les réserves wrapped SOL, les délégations, les autorités, +les seuils multisig et le rent avant simulation. Les tests standards sont offline et un test Devnet +read-only reste opt-in. La démo Token réutilise la fenêtre d’exécution Solana existante : son panneau +`TransferChecked` expose préflight, simulation, confirmation et validation post-exécution, tandis +qu’un journal matérialisé borné accepte des filtres typés par signature, mint, compte, famille et +opération sans créer une WebView par opération. + +Le correctif fonctionnel de `pre.005` ajoute l’orchestration Devnet générique : simulation autonome +après préflight, puis envoi explicitement confirmé lorsque le wallet du profil résout tous les +signataires. Une transaction envoyée est confirmée, hydratée, extraite, décodée et matérialisée, +avant un second replay idempotent. Cette couche ne restreint pas les builders universels aux seuls +clusters ou signataires disponibles dans la démo. + +Le parcours `TransferChecked` a été validé réellement sur Devnet le 15 juillet 2026 avec deux +comptes Token auxiliaires du même mint et un wallet isolé correspondant exactement à l’autorité. +Le test opt-in impose désormais la présence de la soumission et de la confirmation, l’absence +d’échec ou de refus au premier replay, au moins une matérialisation Token et un second replay sans +nouvelle sortie ; il imprime aussi la signature afin qu’elle puisse rejoindre le corpus audité. + +`0.4.4-pre.006` ajoute un lifecycle destructif explicitement autorisé pour trois comptes classiques +sans ATA. Son correctif crée d'abord les comptes bruts via trois instructions System officielles, +avec keypairs éphémères, rent et tailles 82/165/165 vérifiés, puis onze transactions initialisent le mint et deux comptes Token, mintent et +transfèrent un montant contrôlé, créent puis révoquent une délégation, brûlent exactement les deux +soldes et ferment les comptes Token. Chaque étape est simulée, confirmée, hydratée, décodée, +matérialisée et rejouée une seconde fois avant que l'étape suivante ne soit admise. + +Le correctif de reprise du lifecycle traite explicitement les confirmations RPC interrompues : une +signature prédécesseur confirmée est hydratée, extraite, décodée, rapprochée de l'opération attendue +et rejouée idempotemment avant de reprendre à l'index demandé. Un délai borné sépare ensuite les +étapes afin de réduire la pression sur les endpoints Devnet publics, sans masquer leurs erreurs. + +Le lifecycle Devnet réel a été validé le 15 juillet 2026 après une interruption du RPC public : la +reprise a récupéré l'initialisation déjà confirmée, puis achevé mint, transfer, approve/revoke, deux +burn et deux close. Chaque étape récupérée ou envoyée a produit exactement une matérialisation et un +second replay idempotent. La fenêtre opérateur `pre.007` permet désormais de rejouer le parcours +représentatif `TransferChecked` et de consulter ces faits `token_account`, `admin` et `risk` comme +événements chronologiques, sans inventer un état final de compte. + +L'audit `pre.008` reporte dans `docs/SPL_TOKEN_MATRIX.json` les preuves par instruction. Le corpus +Mainnet réel couvre `Transfer`, `Approve`, `CloseAccount`, `TransferChecked`, `BurnChecked` et +`SyncNative`; ses trois initialisations restent volontairement référencées sous leur nom matérialisé +normalisé sans inventer une variante wire. Le lifecycle Devnet prouve `InitializeAccount3`, +`MintToChecked`, `TransferChecked`, `ApproveChecked`, `Revoke`, `BurnChecked` et `CloseAccount`. +Localnet n'a pas été exécuté, et la publication p-token des tags `45`/`255` n'est pas assimilée à +leur déploiement sur un cluster public. + +La validation Tauri de `pre.007` a démarré Vite et l'application, réussi le préflight et la +simulation `TransferChecked`, puis refusé correctement l'envoi sans confirmation opérateur. Avec +confirmation, un second refus a identifié que le wallet du profil et l'autorité Token étaient deux +clés distinctes. La démo ne tente jamais de signer à la place d'une autorité externe ; pour envoyer, +les comptes Token doivent appartenir au wallet persistant sélectionné, comme dans le scénario +backend Devnet déjà confirmé et matérialisé. + +La clôture du 16 juillet 2026 confirme les régressions ciblées, PostgreSQL réel, Clippy et Tauri. +Les validations finales couvrent notamment 5 tests Program IDs, 8 tests décodeur Token, 15 tests +exécuteur Token, 9 tests décodeur Memo, 7 tests annotations, 22 tests API d'exécution, 15 tests +safety, 12 tests transaction Solana, 81 tests pipeline, 113 tests RPC, 41 tests configuration, +102 tests démo et 46 tests PostgreSQL réels. Tauri charge 77 routes de logs, simule +`TransferChecked`, bloque l'envoi non confirmé, restitue sa matérialisation commitée exacte et +simule Memo v4 sans erreur runtime. + +## Jalon validé `0.4.5` + +`0.4.5` clôt la surface SPL Associated Token Account avec +`spl-associated-token-account-interface 2.0.0`. Le décodeur reconnaît exactement `Create`, +`CreateIdempotent`, `RecoverNested` et la forme historique vide de `Create`; il conserve comptes +ordonnés, flags, doublons, outer/inner paths, transactions échouées non commitées et diagnostics +bornés. Les PDA sont dérivés et validés explicitement avec les seeds +`[wallet, Token Program ID, mint]`, ce qui distingue les ATA classiques des ATA Token-2022 sans +prétendre décoder les extensions de ce dernier. + +Le lifecycle ATA est possédé par `kb_materializer_token_accounts` : création normale, création ou +réutilisation idempotente non distinguable depuis le parent seul, et récupération nested. +`kb_materializer_risk` possède séparément le constat factuel d'anti-pattern nested récupéré. Les +matérialiseurs lifecycle général, admin et fees déclarent explicitement leur absence de projection, +et les CPI SPL Token conservent seules leurs initialisations, transferts et fermetures. Les +transactions échouées ou structurellement invalides ne produisent aucune mutation. + +`kb_executor_spl_associated_token_account` construit les trois variantes actuelles avec les +builders officiels, pour SPL Token classique ou Token-2022. Simulation et dry-run sont obligatoires +par défaut. Le préflight Localnet/Devnet contrôle cluster, mints, Program IDs, dérivations, comptes +existants, signataires, rent et plafonds ; la post-validation vérifie création/réutilisation ou les +conditions de fermeture/transfert de `RecoverNested`. + +Les preuves Devnet du 16 juillet 2026 couvrent deux parcours complets `CreateIdempotent` classiques, +la création puis la réutilisation d'un ATA Token-2022 avec `ImmutableOwner`, et un +`RecoverNested` classique transférant 1 000 000 000 unités brutes avant fermeture du nested ATA. +Chaque soumission a été simulée, confirmée, hydratée, extraite, décodée et matérialisée, puis rejouée +sans doublon. La fenêtre Tauri unique expose création ATA et journal lifecycle borné ; +`RecoverNested` reste couvert hors UI par un test opt-in contrôlé. + +La validation finale confirme les régressions Token/Memo, 10 tests décodeur ATA, 10 tests exécuteur +ATA, 90 tests pipeline, 113 tests RPC, 41 tests configuration, 111 tests démo, 46 tests PostgreSQL +réels et `cargo clippy --all-targets` propre. Les versions Rust, npm et Tauri sont synchronisées en +`0.4.5`. + +## Jalon validé `0.4.6` + +`0.4.6` clôt Token-2022 et le registre ElGamal comme surfaces séparées. Token-2022 couvre les instructions de base, les extensions identifiables, les états Mint/Account/Multisig et TLV, les matérialisations propriétaires et les exécuteurs simulation-first. Le registre ElGamal conserve son propre Program ID, sa matrice, son état de 64 octets, son PDA et ses opérations administratives ; il n’est pas fusionné avec Token-2022 ni avec le programme natif ZK ElGamal Proof. + +La validation publique Token-2022 comprend huit opérations Devnet confirmées et 55 instructions Mainnet décodées sans échec. La validation réseau ElGamal Registry reste indisponible sur les clusters publics au 20 juillet 2026, mais ses builders, parsers, préflights, preuves et scénarios stateful sont validés synthétiquement/offline. Le bilan détaillé est publié dans `docs/V0_4_6_VALIDATION.md`. + +Le replay contextualisé dispose désormais du mode `incomplete_signatures`. Il sélectionne uniquement les signatures contenant au moins une instruction `pending`, `failed`, `replay_requested` ou `unsupported`, applique la limite aux signatures avant expansion et réévalue leurs instructions compatibles sans activer le force replay global. Deux campagnes PostgreSQL réelles ont sélectionné 81 entrées chacune : 1 décodée, 4 unsupported et 76 rejets fail-closed appartenant tous à des transactions Solana échouées. + +La régression finale confirme `kb_store_core` 41/41, `kb_store_pg` 46/46 avec PostgreSQL réel, `kb_pipeline` 124/124, `kb_app_demo` 118/118 et `cargo clippy --all-targets` propre. + +## Jalon actif `0.4.7` + +La suite active est Metaplex Token Metadata selon `prompts/026_v0_4_7_metaplex_token_metadata.md`. La préversion `0.4.7-pre.001` ouvre l’audit avec les crates `kb_decoder_metadata_metaplex_token_metadata` et `kb_executor_metadata_metaplex_token_metadata`, ainsi que `docs/METAPLEX_TOKEN_METADATA_MATRIX.json`, sans activation runtime tant que le wire complet n’est pas prouvé. L’exécuteur existe comme frontière audit-only parce que le SDK officiel fournit des builders, mais il n’expose encore aucune construction de transaction. Ce programme indépendant complète les metadata externes des mints SPL Token classique et Token-2022 sans être absorbé par leurs décodeurs. La priorité porte sur la frontière exacte du Program ID, les PDA et layouts versionnés, les NFT/collections/programmable assets, la provenance des metadata et le replay PostgreSQL idempotent. + +Anchor n’est pas requis par les programmes SPL planifiés dans `0.4.x`. Son infrastructure commune est reportée à `0.4.18`, immédiatement avant Meteora, afin d’être conçue à partir des besoins réels des protocoles Anchor sans retarder Metaplex et les autres surfaces Core/SPL. + +## Backend de données + +PostgreSQL est le backend principal prévu. SQLite reste présent uniquement pour les tests, les imports ponctuels ou les petits corpus locaux. + +Les tables Solana PostgreSQL utilisent le schéma courant du profil, généralement `public`. Le projet ne crée pas de schémas applicatifs explicites comme `raw`, `core`, `obs`, `decode`, `mat`, `catalog`, `agg`, `ops` ou `wallet`. La séparation logique se fait dans le nom physique de table au format `kb_sol__`, par exemple `kb_sol_raw_transactions`. + +## Replay des signatures incomplètes + +La fenêtre de replay contextualisé de `kb_app_demo` expose un mode `signatures_incomplete`. Il sélectionne uniquement les signatures contenant au moins une instruction `pending`, `failed`, `replay_requested` ou un résultat `unsupported`, puis réévalue les instructions compatibles de ces signatures sans activer le force replay global. La limite porte sur le nombre de signatures incomplètes avant expansion. La matérialisation reste optionnelle et idempotente. + +Ce mode est destiné aux backfills croissants : une correction de décodeur peut reprendre seulement les signatures incomplètes au lieu de rejouer toutes les signatures déjà réussies. + +## Livraison par delta + +Après le squelette initial, les modifications doivent être livrées sous forme de zip delta contenant uniquement les fichiers ajoutés ou modifiés. Un delta multi-module ou racine utilise `khadhroony-bot2_vX.Y.Z-pre.abc-delta.zip`. Un delta limité à un module Rust utilise `kb_modulename_vX.Y.Z-pre.abc-delta.zip`. Un correctif conserve le même numéro `pre.abc` et ajoute `-delta-fix-001.zip`, puis `-fix-002.zip`, au lieu d’incrémenter la préversion. Chaque archive contient un `delta.md` non versionné listant les fichiers ajoutés, modifiés, à supprimer manuellement, ainsi que les validations exécutées ou non exécutées. + +## Surfaces réservées + +Le workspace réserve dès le squelette les crates de décodage pour les DEX, launchpads, orderbooks, routers, perps et surfaces candidates recensés dans `idls/` et dans la documentation de conception. Ces crates restent des points d'ancrage tant que les discriminants, corpus et règles de matérialisation ne sont pas validés. + + +## Registres de programmes + +Le workspace distingue deux registres : + +- `registry/program_registry_seed.toml` pour les surfaces applicatives et protocolaires ; +- `registry/core_program_id_seed.toml` pour les programmes primitifs, loaders, sysvars et comptes natifs Solana/SPL. + +Les comptes non exécutables observés pendant la recherche restent documentés dans `docs/ACCOUNT_ONLY_CANDIDATES.md`. Bags dispose maintenant aussi d'une note dédiée dans `docs/BAGS_FM.md` pour les programmes Fee Share V1/V2 confirmés. + +## Classification des surfaces + +La classification des IDL locales est suivie dans `docs/IDL_SURFACE_CLASSIFICATION.md`. Le registre machine principal reste `registry/program_registry_seed.toml`. + +## Configuration, endpoints et listeners + +Le workspace utilise `kb_config` pour charger plusieurs profils de configuration, avec un seul profil actif. Le fichier JSON est d'abord validé par `config/schema.config.json`, puis désérialisé et validé par les règles métier Rust. + +Le pipeline normalise toutes les sources vers une transaction canonique commune. HTTP JSON-RPC est utilisé d’abord pour les backfills gratuits. Helius `transactionSubscribe` et Yellowstone gRPC seront ajoutés plus tard dans `kb_rpc`, après les décodeurs prioritaires, sans créer de crate provider séparée. La fenêtre `demo_backfill` permet de lancer des campagnes bornées par liste de signatures, programme, token ou pool. En direction « avant, plus anciennes », une ancre vide commence sur la page la plus récente ; la direction « après, plus récentes » exige toujours une signature d’ancrage. + +Les listeners WebSocket doivent permettre la détection temps réel des créations de token, créations de pool, migrations, swaps, changements de liquidité et changements sur les tokens ou pools surveillés. Les exports TS-rs de `kb_config` servent uniquement à partager les types Rust/TypeScript avec les applications Tauri comme `kb_app_demo`, puis les futures applications wallet ou finales. Les payloads Tauri partagés, comme la page de configuration, doivent aussi être exportés par TS-rs depuis leur crate applicative. + +### Metaplex Token Metadata `0.4.7-pre.016` + +L’inventaire officiel complet des modules d’instruction a été réaudité avant le passage aux comptes on-chain. `FreezeDelegatedAccount` et `ThawDelegatedAccount` historiques sont désormais décodés avec leurs contrats exacts ; les autres surfaces encore absentes restent planifiées avant toute déclaration de couverture exhaustive. + diff --git a/docs/legacy-khadhroony-bot2/ROADMAP.md b/docs/legacy-khadhroony-bot2/ROADMAP.md new file mode 100644 index 0000000..f21cd46 --- /dev/null +++ b/docs/legacy-khadhroony-bot2/ROADMAP.md @@ -0,0 +1,1439 @@ + + + +# Roadmap + +Ce fichier contient les changements futurs et le phasage prévu. Il ne remplace pas le changelog : le changelog ne reçoit que les versions validées. + +## Version 0.0.x — stabilisation du squelette + +- [x] Réserver les crates core, décodeurs, matérialisateurs, exécuteurs et applications. +- [x] Valider que le workspace compile après ajout de la couche d'exécution. +- [x] Conserver `CHANGELOG.md` pour les versions validées uniquement. +- [x] Réserver `prompts/` pour les futures sessions ChatGPT par version. +- [x] Documenter les objectifs court terme et long terme. +- [x] Ajouter `kb_config` et `config/example.config.json` comme base de configuration. +- [x] Ajouter les surfaces Bags Fee Share V1/V2 confirmées. +- [x] Définir le format obligatoire des zips delta ChatGPT. +- [x] Mettre à jour `CHANGELOG.md` pour `0.0.1` et `0.0.2`. +- [x] Exécuter `cargo build`. +- [x] Exécuter `cargo test --workspace`. +- [x] Exécuter `cargo clippy --workspace --all-targets`. +- [x] Faire le commit `v0.0.2` comme squelette stabilisé. + +## Version 0.1.x — logging et configuration + +### 0.1.0 — `kb_logging` + +- [x] Finaliser l'API publique minimale de `kb_logging`. +- [x] Définir les routes de logs console, fichier humain, fichier JSON et fichier erreurs. +- [x] Router les logs vers console, fichier humain, fichier JSON et fichier erreurs. +- [x] Gérer les niveaux par target et par sortie. +- [x] Conserver le nettoyage ANSI des fichiers via writer dédié pour les messages issus de WebView Tauri. +- [x] Ajouter la normalisation des globs `crate::*` en plus des globs `crate.*`. +- [x] Ajouter les tests unitaires de configuration des targets et expressions de filtres. +- [x] Ajouter les tests unitaires de sélection des routes console, fichier humain, fichier JSON et fichier erreurs. +- [x] Ajouter les tests unitaires du writer sans ANSI. +- [x] Valider localement `cargo test -p kb_logging` : 14 tests passés. +- [x] Valider localement `cargo clippy -p kb_logging --all-targets`. +- [x] Reporter `0.1.0` dans `CHANGELOG.md` après validation. + +### 0.1.1 — `kb_config` + +- [x] Finaliser les structures typées de configuration. +- [x] Normaliser les exports TS-rs avec `export_to = "../frontend/ts/bindings/kb_config/settings/*.ts"`. +- [x] Charger un fichier JSON avec plusieurs profils. +- [x] Garantir un seul profil actif. +- [x] Ajouter `config/schema.config.json`. +- [x] Valider le JSON brut via `jsonschema`. +- [x] Valider les profils absents, dupliqués ou incohérents. +- [x] Exclure du schéma les transports avancés hors périmètre avant `v1.x`. +- [x] Conserver les placeholders de clés directement dans les URLs. +- [x] Supprimer `ProfileConfig.enabled` : `active_profile` est l’unique sélection de profil. +- [x] Retirer `data.idls_directory` du contrat runtime : les IDLs restent des artefacts de développement. +- [x] Conserver `kb_core` comme dépendance anticipée pour les erreurs communes workspace. +- [x] Valider `config/example.config.json`. +- [x] Ajouter les tests de parsing, validation et sérialisation. +- [x] Valider localement `cargo test --tests -p kb_config` : 35 tests passés. +- [x] Valider localement `cargo clippy -p kb_config --all-targets`. +- [x] Reporter `0.1.1` dans `CHANGELOG.md` après validation. + +### 0.1.2 — liaison `kb_logging`, `kb_config` et démo Tauri + +- [x] Initialiser `kb_logging` depuis le profil actif. +- [x] Appliquer les niveaux, formats et chemins de fichiers depuis la configuration. +- [x] Vérifier la création des fichiers de logs configurés et conserver la rotation comme amélioration future. +- [x] Conserver `main.html` comme fenêtre d'accueil. +- [x] Rendre le README workspace en HTML dans la fenêtre `main` sans afficher les commentaires de métadonnées `file/version`. +- [x] Ajouter dans `main.html` un dropdown vers les fenêtres de démonstration. +- [x] Créer `kb_app_demo/src/demo_config.rs` comme fenêtre de démo uniquement. +- [x] Créer `kb_app_demo/frontend/demo_config.html`. +- [x] Créer `kb_app_demo/frontend/ts/demo_config.ts`. +- [x] Réutiliser le même format visuel que la fenêtre principale. +- [x] Afficher le profil actif, la configuration complète et le schéma JSON. +- [x] Ajouter un viewer JSON interactif pour le profil actif, la configuration complète et le schéma JSON. +- [x] Désactiver le toolbar natif du viewer JSON pour éviter les boutons d’indentation non configurables. +- [x] Exporter `DemoConfigPayload` via TS-rs et l'importer côté TypeScript. +- [x] Déclarer `demo_config` dans `capabilities/default.json` et `vite.config.ts`, puis créer la fenêtre à la demande depuis Rust pour éviter une fenêtre cachée bloquante. +- [x] Corriger la fermeture de l’application lorsque `demo_config` n’a jamais été ouverte. +- [x] Ajouter un pont `emit_frontend_log` pour réémettre les logs WebView avec des targets `tracing` statiques. +- [x] Router `kb_app_demo.frontend::*` dans la console et le fichier debug de `kb_app_demo`. +- [x] Ajouter `AppState` comme état applicatif global dans `kb_app_demo/src/app_state.rs`. +- [x] Retirer l’initialisation runtime globale de `demo_config`, qui reste une simple fenêtre de démo. +- [x] Ajouter un verrou mono-instance uniquement dans `kb_app_demo/src/main.rs`, non appliqué à `kb_app_demo_lib::run()` pour préserver l’entrée mobile. +- [x] Déplacer l’installation du provider Rustls par défaut dans `kb_app_demo_lib::run()` pour desktop et mobile. +- [x] Ajouter un splashscreen avec fade-in/fade-out et durée minimale extensible pour les vérifications longues futures. +- [x] Aligner le branchement du splashscreen dans `tauri_builder.setup` sur le modèle de `khadhroony-bobobot/kb_demo_app`. +- [x] Corriger `SplashOrder.duration_ms` en `u32` pour éviter un type BigInt côté TypeScript. +- [x] Corriger l’animation du splash avec `requestAnimationFrame`, une opacité initiale explicite et une attente initiale de `1500 ms` avant fade-in. +- [x] Valider visuellement le fade-in/fade-out dans `cargo tauri dev`. +- [x] Valider localement `cargo test -p kb_core` : 2 tests passés. +- [x] Valider localement `cargo test -p kb_config` : 35 tests passés. +- [x] Valider localement `cargo test -p kb_logging` : 14 tests passés. +- [x] Valider localement `cargo test -p kb_app_demo` : 12 tests passés. +- [x] Valider localement `cargo clippy --all-targets`. +- [x] Valider localement `cargo tauri dev -c kb_app_demo/tauri.conf.json` depuis la racine du workspace. +- [x] Reporter `0.1.2` dans `CHANGELOG.md` après validation. + +### 0.1.3 — clients HTTP/WS Solana standard, rôles et pools + +- [x] Explorer `khadhroony-bobobot/kb_lib` et les anciennes démos HTTP/WS comme source d'adaptation. +- [x] Remonter depuis `0.3.x` les points déjà nécessaires : réutilisation des pools HTTP/WS validés et finalisation initiale de `kb_rpc`. +- [x] Ajouter les contrats JSON-RPC 2.0 partagés entre HTTP et WebSocket. +- [x] Créer ou finaliser le client HTTP JSON-RPC Solana standard dans `kb_rpc`. +- [x] Créer ou finaliser le client WebSocket Solana standard court dans `kb_rpc`. +- [x] Ajouter la gestion de pool HTTP par rôle d'endpoint. +- [x] Ajouter la gestion de pool WebSocket par rôle d'endpoint. +- [x] Finaliser le premier modèle de rôles d'endpoints exploitable par les pools. +- [x] Associer chaque rôle à un ou plusieurs types de requêtes via `request_kinds`. +- [x] Router les requêtes HTTP et les requêtes WebSocket par rôle. +- [x] Préparer les contrats nécessaires aux futurs pools HTTP et WebSocket persistants. +- [x] Ajouter une fenêtre de démo HTTP JSON-RPC dédiée dans `kb_app_demo`. +- [x] Ajouter une fenêtre de démo WebSocket dédiée dans `kb_app_demo`. +- [x] Remplacer les champs libres `role`/`method` des démos HTTP/WS par des listes contrôlées issues de la config et des méthodes supportées. +- [x] Ajouter une connexion/déconnexion explicite dans `demo_ws`. +- [x] Réutiliser la même connexion WebSocket pour plusieurs souscriptions tant que l’endpoint sélectionné reste identique. +- [x] Conserver la connexion WebSocket dans `AppState` quand la fenêtre `demo_ws` est fermée puis recréée. +- [x] Ne plus déconnecter automatiquement `demo_ws` à la fermeture de la fenêtre ; réserver la fermeture au bouton déconnecter ou à l’arrêt global de l’application. +- [x] Remplacer l’affichage JSON échappé des démos par des zones texte readonly avec bouton de copie. +- [x] Ajouter le refresh explicite des pools HTTP/WS depuis les fenêtres de démo. +- [x] Ajouter un profil `mainnet` Helius avec les paramètres issus de l’ancien `config.json` fourni. +- [x] Élargir les listes de méthodes HTTP/WS de démonstration aux commandes Solana standard courantes. +- [x] Ajouter un champ `Params JSON complet` à la démo HTTP pour tester les méthodes multi-paramètres. +- [x] Ajouter l’unsubscribe explicite d’une subscription WebSocket sélectionnée sans fermer la socket persistante. +- [x] Ajouter une limitation côté Rust des notifications WebSocket envoyées à la WebView pour éviter le gel sur les subscriptions très bavardes. +- [x] Borner côté TypeScript l'historique affiché dans `demo_ws`. +- [x] Ajouter les tests unitaires offline de `kb_rpc` pour JSON-RPC, rôles, endpoints désactivés, snapshots, round-robin HTTP/WS et génération d’IDs. +- [x] Ajouter les liens correspondants dans le dropdown de la fenêtre principale. +- [x] Conserver les démos dans des fichiers Rust, HTML et TypeScript séparés comme dans `khadhroony-bobobot/kb_demo_app`. +- [x] Confirmer que gRPC, Helius enhanced WebSocket, Helius gRPC et LaserStream restent hors scope avant `v1.x` ou `v2.x`. +- [x] Reporter vers les versions RPC ultérieures les wrappers métier spécialisés HTTP/WS à partir des commandes RPC déjà couvertes dans `khadhroony-bobobot`. +- [x] Conserver les limites comme contrat de configuration et reporter vers `0.4.x` leur enforcement runtime par token bucket et pause 429. +- [x] Reporter vers `0.7.x` la généralisation des clients WebSocket persistants de production avec registry, unsubscribe contrôlé et relay d'événements durables. +- [x] Valider localement `cargo test -p kb_rpc` : 39 tests passés. +- [x] Valider localement `cargo test -p kb_app_demo` : 28 tests passés. +- [x] Valider localement `cargo clippy --all-targets`. +- [x] Valider visuellement `demo_http` dans `cargo tauri dev -c kb_app_demo/tauri.conf.json`. +- [x] Valider visuellement `demo_ws` dans `cargo tauri dev -c kb_app_demo/tauri.conf.json`. +- [x] Valider que le pool HTTP alterne entre endpoints compatibles avec le profil `mainnet_research`. +- [x] Valider que `programSubscribe` très bavard reste utilisable grâce au throttling UI et que l’unsubscribe fonctionne. +- [x] Reporter `0.1.3` dans `CHANGELOG.md` après validation. + +## Version 0.2.x — SQL, stockage et conventions DB + +### 0.2.0 — conventions storage, préfixes et contrats + +- [x] Auditer les crates existantes concernées par le stockage : `kb_store_core`, `kb_store_pg`, `kb_config`, `kb_core`, `kb_app_demo` et, si nécessaire seulement, `kb_rpc`. +- [x] Définir le rôle exact de chaque crate touchée avant d'ajouter des migrations ou des requêtes. +- [x] Valider que PostgreSQL utilise le schema courant/default, généralement `public`, sans schemas applicatifs explicites. +- [x] Valider le préfixe de table `kb_sol__` pour les tables Solana du nouveau projet. +- [x] Définir les domaines intégrés au nom de table : `raw`, `core`, `obs`, `decode`, `mat`, `catalog`, `agg`, `ops`, `wallet`. +- [x] Documenter les tables candidates initiales sans les implémenter toutes immédiatement. +- [x] Utiliser uniquement des noms de tables sans préfixe de schema explicite, par exemple `kb_sol_raw_rpc_transactions`, `kb_sol_core_transactions`, `kb_sol_obs_program_observations`, `kb_sol_decode_decoded_events`, `kb_sol_mat_trade_events`, `kb_sol_catalog_tokens` et `kb_sol_ops_processing_ledger`. +- [x] Définir la séparation stricte `entities/`, `dtos/`, `queries/`, `repositories/`. +- [x] Interdire les structures Rust dans `queries/` : les structures appartiennent à `entities/` ou `dtos/`. +- [x] Définir la convention `Entity` comme représentation proche d'une ligne SQL. +- [x] Définir la convention `Dto` comme contrat applicatif, Tauri ou repository. +- [x] Définir la convention `InsertDto` / `UpsertDto` pour les écritures. +- [x] Définir les règles de nommage SQL : `id`, `created_at`, `updated_at`, `slot`, `signature`, `program_id`, `raw_json`, `payload_json`. +- [x] Définir les contraintes minimales : clés primaires, index uniques, index par signature, slot, program id et temps d'insertion. +- [x] Définir les migrations comme artefacts PostgreSQL versionnés dans `kb_store_pg`. +- [x] Préparer la page Tauri future de diagnostic DB sans l'implémenter si le store n'est pas encore prêt. +- [x] Mettre à jour `kb_store_core/README.md` avec les conventions validées. +- [x] Mettre à jour `kb_store_pg/README.md` avec les conventions PostgreSQL validées. +- [x] Mettre à jour ou créer un document de prompt pour ouvrir la suite `0.2.x`. +- [x] Neutraliser les migrations héritées qui créaient des schemas applicatifs ou des tables qualifiées comme `raw DOT sol_transactions`. +- [x] Ne pas modifier `CHANGELOG.md` avant validation locale du jalon. + +### 0.2.1 — `kb_store_core` contrats et types communs + +- [x] Créer les modules publics minimaux de `kb_store_core` sans dépendre de PostgreSQL. +- [x] Ajouter les erreurs storage en s'appuyant sur `kb_core::Error` et `kb_core::Result`. +- [x] Ajouter les types communs : pagination, tri, limites, healthcheck, statut migration. +- [x] Ajouter les premiers DTO génériques : raw RPC transaction, raw WS notification, core transaction, core instruction. +- [x] Ajouter les contrats core pour account keys, logs et balance changes extraits. +- [x] Ajouter `CoreInstructionReplayInput` pour fournir aux décodeurs une instruction avec contexte extrait. +- [x] Ajouter les contrats de cycle de vie raw : état de rétention, état de traitement et mark lifecycle. +- [x] Ajouter les contrats de replay par instruction : état instruction, filtre de replay et lifecycle mark. +- [x] Ajouter les premiers traits repository sans implémentation SQL. +- [x] Étendre les repositories pour préparer la sélection des instructions non traitées. +- [x] Ajouter tests unitaires offline pour pagination, validation DTO et erreurs. +- [x] Documenter le cycle de vie raw et la future compaction/purge sans l'activer. +- [x] Documenter le split core des instructions, logs, account keys et balances avant les migrations. +- [x] Valider localement `cargo test -p kb_store_core` : 28 tests passés. +- [x] Valider localement `cargo clippy --all-targets` sans avertissement. +- [x] Reporter `0.2.1` dans `CHANGELOG.md` après validation locale. + +### 0.2.2 — `kb_store_pg` connexion PostgreSQL et migrations + +- [x] Ajouter la lecture effective de la configuration PostgreSQL depuis le profil actif. +- [x] Créer les options de connexion PostgreSQL minimales dans `kb_store_pg`. +- [x] Créer le pool PostgreSQL via `sqlx::PgPool`. +- [x] Ajouter le masquage DSN pour les diagnostics. +- [x] Ajouter un healthcheck PostgreSQL minimal basé sur `SELECT 1`. +- [x] Lire le schema courant PostgreSQL via `current_schema()`. +- [x] Lire la version PostgreSQL pour les diagnostics. +- [x] Ajouter une stratégie de migrations PostgreSQL explicite et non destructive. +- [x] Ne pas créer de schemas PostgreSQL applicatifs ; utiliser le schema par défaut du profil, généralement `public`. +- [x] Valider les noms de tables `kb_sol__` et refuser les schemas explicites. +- [x] Ajouter des tests offline pour options, masquage DSN et validation des noms de tables. +- [x] Ajouter un test PostgreSQL réel optionnel contrôlé par `KB_POSTGRES_TEST_URL`. +- [x] Valider localement `cargo test -p kb_store_pg` : 11 tests passés. +- [x] Valider localement le test PostgreSQL réel optionnel `optional_postgres_healthcheck_from_env` avec `postgres://solana:solana@localhost:5432/solana`. +- [x] Valider localement `cargo test -p kb_store_core` : 28 tests passés. +- [x] Valider localement `cargo test -p kb_config` : 35 tests passés. +- [x] Valider localement `cargo test -p kb_app_demo` : 28 tests passés. +- [x] Valider localement `cargo clippy --all-targets` sans avertissement après correction de l'exception `async_trait` / `clippy::implicit_return`. +- [x] Reporter `0.2.2` dans `CHANGELOG.md` après validation. + +### 0.2.3 — raw store Solana minimal + +- [x] Créer `kb_sol_raw_rpc_transactions` pour stocker le raw RPC immuable. +- [x] Créer `kb_sol_raw_ws_notifications` pour stocker les notifications WebSocket brutes utiles. +- [x] Prévoir `notification_key`, signature optionnelle et lien optionnel vers la transaction RPC canonique. +- [x] Créer les queries et repositories PostgreSQL associés aux DTOs `0.2.1`. +- [x] Ajouter insert idempotent par signature pour les transactions raw. +- [x] Ajouter insert append-only dédupliqué par `notification_key` pour les notifications WS raw. +- [x] Ajouter index minimaux par signature, slot, subscription kind, lien RPC et date d'insertion. +- [x] Ajouter une initialisation idempotente `PostgresStore::initialize_raw_store_schema()`. +- [x] Préparer `auto_initialize_schema` pour appliquer le schéma raw minimal après connexion quand le profil le demande. +- [x] Documenter que `0.2.3` ne purge pas encore physiquement `raw_json`. +- [x] Valider localement `cargo test -p kb_store_pg` : 21 tests passés. +- [x] Valider localement le test PostgreSQL réel optionnel `optional_postgres_raw_store_roundtrip_from_env` avec `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana`. +- [x] Valider localement `cargo test -p kb_store_core` : 28 tests passés. +- [x] Valider localement `cargo test -p kb_config` : 35 tests passés. +- [x] Valider localement `cargo test -p kb_app_demo` : 28 tests passés. +- [x] Valider localement `cargo clippy --all-targets` sans avertissement. +- [x] Reporter `0.2.3` dans `CHANGELOG.md` après validation. + +### 0.2.4 — core Solana normalisé minimal + +- [x] Créer les tables core minimales : `kb_sol_core_transactions`, `kb_sol_core_account_keys`, `kb_sol_core_instructions`, `kb_sol_core_inner_instructions`, `kb_sol_core_logs`, `kb_sol_core_balance_changes`. +- [x] Construire `CoreInstructionReplayInput` depuis les tables `core` pour les décodeurs. +- [x] Marquer les instructions insérées comme `Pending` pour permettre un replay instruction-level. +- [x] Préparer le replay partiel par état, slot et program id via `CoreInstructionReplayFilter`. +- [x] Ajouter une initialisation idempotente raw/core via `PostgresStore::initialize_store_schema()`. +- [x] Sérialiser l'initialisation raw/core par verrou PostgreSQL `pg_advisory_xact_lock`. +- [x] Normaliser les noms de contraintes et d'index PostgreSQL avec les préfixes `pk_`, `fk_`, `ck_`, `ux_` et `ix_`. +- [x] Ajouter un script SQL de maintenance pour supprimer les tables raw/core dans l'ordre inverse des dépendances. +- [x] Valider localement le script `kb_store_pg/maintenance/drop_raw_core_store.sql` sur la base PostgreSQL locale. +- [x] Valider localement `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana cargo test -p kb_store_pg -- --nocapture` : 30 tests passés. +- [x] Valider localement la création des 8 tables raw/core dans PostgreSQL. +- [x] Valider localement `cargo clippy --all-targets` sans avertissement. +- [x] Reporter le worker/extracteur raw RPC vers core vers les jalons d'ingestion/backfill, car `0.2.4` valide uniquement le stockage core et les inputs de replay. +- [x] Reporter les observations minimales et le ledger ops vers un jalon dédié après stabilisation de l'extraction. +- [x] Reporter l'extension du replay par module, version, signature, surface et discriminator après l'introduction des domaines `decode` et `ops`. +- [x] Reporter `0.2.4` dans `CHANGELOG.md` après validation. + +### 0.2.5 — diagnostics SQL dans `kb_app_demo` + +- [x] Ajouter `demo_sql_diag` pour le diagnostic PostgreSQL global : profil actif, backend, DSN masqué, schema courant, tables détectées, version migrations et healthcheck. +- [x] Ajouter `demo_sql_pg_raw` pour les tables `kb_sol_raw_rpc_transactions` et `kb_sol_raw_ws_notifications`. +- [x] Ajouter `demo_sql_pg_core` pour les tables core normalisées `kb_sol_core_*`. +- [x] Ajouter les boutons de refresh read-only dans chaque fenêtre. +- [x] Ajouter les liens dans le dropdown de la fenêtre principale. +- [x] Tenter l'initialisation raw/core au démarrage Tauri quand `database.postgres.auto_initialize_schema` est actif. +- [x] Afficher dans le splashscreen les tables créées, déjà présentes, manquantes ou en erreur, avec logs `tracing` associés. +- [x] Remplacer les payloads JSON textuels des démos SQL par le viewer JSON interactif déjà utilisé par `demo_config`. +- [x] Garantir que les démos SQL ne lancent ni extracteur, ni décodage, ni matérialisation. +- [x] Documenter à l’époque que l’extracteur raw RPC vers core était reporté à `0.4.x`; ce phasage historique est remplacé par le recadrage `0.3.x` ci-dessous. +- [x] Valider localement `cargo test -p kb_store_pg` : 30 tests passés. +- [x] Valider localement `cargo test -p kb_config` : 35 tests passés. +- [x] Valider localement `cargo test -p kb_app_demo` : 32 tests passés. +- [x] Valider localement `cargo clippy --all-targets` sans avertissement. +- [x] Valider visuellement `demo_sql_diag`, `demo_sql_pg_raw` et `demo_sql_pg_core` via `cargo tauri dev -c kb_app_demo/tauri.conf.json`. +- [x] Reporter `0.2.5` dans `CHANGELOG.md` après validation. + +## Version 0.3.x — fondation transactionnelle canonique et backfill gratuit + +`0.3.0` reste le jalon historique de cadrage Agave local validé. Les essais matériels ont ensuite montré qu’un nœud Agave mainnet complet n’était pas une source légère adaptée à cette phase. `0.3.1` réoriente donc la série vers une transaction canonique indépendante de la source, des observations d’acquisition légères, le backfill HTTP gratuit et l’extraction core. + +```text +sources HTTP / WebSocket / gRPC + -> adaptateur de source dans kb_rpc + -> transaction Solana canonique indépendante du provider + -> kb_sol_raw_transactions + -> kb_sol_obs_transaction_observations + -> extraction core + -> décodeurs et matérialiseurs +``` + +Après validation réelle de la transition `0.2.x -> 0.3.1`, les fichiers SQL ont été consolidés en une baseline propre avec `0001_canonical_transaction_store.sql` et `0002_core_store.sql`. `0.3.4` ajoute uniquement `0003_processing_ledger.sql`; l’historique des anciennes tables reste dans le changelog et la documentation, pas dans le DDL actif. + +### 0.3.0 — cadrage Agave local et sources temps réel — clôturé + +- [x] Documenter l’hypothèse d’un nœud Agave local non votant comme source temps réel. +- [x] Préparer le profil manuel `agave_local_research`, les probes RPC/WS et les mesures de ressources. +- [x] Conserver `mainnet_research` comme profil actif stable et ne lancer aucune ingestion de production. +- [x] Valider localement le delta `0.3.0-pre.001` avant mise à jour du changelog. +- [x] Mesurer l’impact réel du snapshot, d’AccountsDB, du ledger, de la RAM, du swap et du réseau. +- [x] Abandonner temporairement Agave local et `blockSubscribe` comme axe actif à cause du coût matériel et opérationnel. +- [x] Reporter les conclusions historiques dans `CHANGELOG.md`. + +### 0.3.1 — transaction canonique et observations d’acquisition — clôturé + +- [x] Remplacer la cible `kb_sol_raw_rpc_transactions` par `kb_sol_raw_transactions`. +- [x] Remplacer la cible `kb_sol_raw_ws_notifications` par `kb_sol_obs_transaction_observations`. +- [x] Renommer `raw_json` en `canonical_json` et `raw_json_hash` en `canonical_json_hash`. +- [x] Ajouter `canonical_format_version`. +- [x] Renommer `kb_sol_core_transactions.raw_rpc_transaction_id` en `raw_transaction_id`. +- [x] Stocker dans les observations uniquement la provenance, le protocole, la méthode, le commitment, la session, le filtre, les timestamps, la taille, les hashes techniques, les statuts et les erreurs. +- [x] Interdire la duplication durable du payload complet dans les observations. +- [x] Aligner DTOs, entities, repositories, requêtes, diagnostics SQL et démos Tauri. +- [x] Valider manuellement la migration réelle depuis le schéma `0.2.x` sur PostgreSQL 17.10. +- [x] Vérifier l’absence de `kb_sol_raw_rpc_transactions` et `kb_sol_raw_ws_notifications`. +- [x] Vérifier la présence de `kb_sol_raw_transactions`, `kb_sol_obs_transaction_observations`, `canonical_json`, `canonical_json_hash`, `canonical_format_version` et `raw_transaction_id`. +- [x] Consolider les migrations SQL en deux fichiers ne contenant que les tables actives. +- [x] Conserver temporairement dans le DDL runtime le chemin de compatibilité idempotent pour les workspaces encore en `pre.001`, sans recréer les anciennes tables. +- [x] Valider `cargo test -p kb_app_demo` : 32 tests passés. +- [x] Valider `cargo test -p kb_store_core` : 30 tests passés. +- [x] Valider `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana cargo test -p kb_store_pg -- --nocapture` : 32 tests passés. +- [x] Valider `cargo clippy --all-targets` sans avertissement. +- [x] Valider le démarrage Tauri et les diagnostics des huit tables raw/obs/core. +- [x] Nettoyer manuellement les fixtures PostgreSQL de test. +- [x] Vérifier et rationaliser les prompts `0.3.x`. +- [x] Reporter `0.3.1` dans `CHANGELOG.md`. + +### 0.3.2 — contrat canonique et adaptateur HTTP — clôturé + +- [x] Définir le type canonique dans `kb_model` et ses conversions explicites. +- [x] Conserver les signatures et public keys en base58, les payloads d’instruction en base64 et les valeurs numériques sans perte. +- [x] Inclure transaction legacy/v0, message header, static account keys, Address Lookup Tables, loaded addresses, outer instructions, inner instructions, logs, statut, erreurs, fee, compute units, cost units, balances SOL/SPL, rewards, return data et block time quand disponibles. +- [x] Garantir une sérialisation déterministe pour `canonical_json_hash` avec tri récursif des objets JSON et SHA-256. +- [x] Ajouter dans `kb_rpc` un adaptateur `getTransaction` JSON-RPC vers le modèle canonique. +- [x] Définir une interface interne commune pour les futurs adaptateurs Helius WebSocket et Yellowstone gRPC, sans créer de crate séparée. +- [x] Ajouter des fixtures offline legacy et v0, succès et échec, ALT, CPI, Token et Token-2022. +- [x] Tester l’idempotence : une même transaction provenant de plusieurs réponses HTTP doit produire le même hash canonique. +- [x] Valider `cargo test -p kb_model` : 23 tests passés. +- [x] Valider `cargo test -p kb_rpc` : 48 tests passés. +- [x] Valider `cargo test -p kb_store_core` : 31 tests passés. +- [x] Valider `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana cargo test -p kb_store_pg -- --nocapture` : 32 tests passés avec roundtrip PostgreSQL réel. +- [x] Ajouter les retours explicites requis dans `kb_model/src/canonical_transaction.rs`, `kb_rpc/src/get_transaction.rs` et `kb_store_core/src/dtos/raw_dtos.rs`. +- [x] Valider `cargo clippy --all-targets` sans avertissement. +- [x] Nettoyer manuellement les fixtures PostgreSQL après validation. +- [x] Reporter `0.3.2` dans `CHANGELOG.md` après validation. + +### 0.3.3 — backfill HTTP sur comptes gratuits — clôturé + +- [x] Ajouter `getSignaturesForAddress` paginé avec `before`, `until`, limite, curseur de reprise et borne `max_pages`. +- [x] Ajouter le backfill d’une liste explicite de signatures séparées par des retours à la ligne. +- [x] Ajouter les campagnes avant/après une signature pour un programme, un token et un pool. +- [x] Hydrater chaque signature avec `getTransaction` via les pools HTTP existants. +- [x] Appliquer le débit, la concurrence et la pause 429 depuis le rôle configuré, avec retries bornés. +- [x] Écrire une seule ligne canonique par signature dans `kb_sol_raw_transactions`. +- [x] Écrire une observation légère par tentative `getTransaction` dans `kb_sol_obs_transaction_observations`. +- [x] Conserver les statuts `missing` et `failed` sans créer de faux payload canonique. +- [x] Ignorer avant hydratation les signatures déjà présentes dans le store canonique. +- [x] Ajouter `demo_backfill` avec accordéons Bootstrap 5, journal, résumé et arrêt coopératif. +- [x] Corriger le parcours `after` avec la borne RPC `until`, la pagination `before`, des pages de 1 000 et une sélection bornée des signatures les plus proches de l’ancre. +- [x] Documenter le fonctionnement dans `docs/BACKFILL_HTTP.md`. +- [x] Valider `cargo test -p kb_rpc` : 54 tests passés. +- [x] Valider `cargo test -p kb_pipeline` : 10 tests passés après le correctif final d’arrêt/reprise. +- [x] Valider `cargo test -p kb_store_core` : 31 tests passés. +- [x] Valider `cargo test -p kb_store_pg` : 32 tests passés. +- [x] Valider `cargo test -p kb_app_demo` et les exports TS-rs : 38 tests passés. +- [x] Valider `cargo clippy --all-targets` sans avertissement. +- [x] Valider visuellement les modes signatures explicites, programme avant/après, token avant/après et pool avant/après dans Tauri. +- [x] Valider le parcours profond `program_after` : 5 516 signatures indexées parcourues pour sélectionner et hydrater les 5 signatures directement postérieures à l’ancre. +- [x] Valider la persistance PostgreSQL réelle : 62 transactions canoniques et 62 observations, sans ligne core avant `0.3.4`. +- [x] Détecter l’arrêt opérateur pendant une campagne de 500 candidats et confirmer l’interruption des nouveaux appels `getTransaction`. +- [x] Remplacer la création globale des futures par une file d’exécution bornée à la concurrence effective. +- [x] Séparer `candidates_started`, `candidates_completed`, `candidates_cancelled` et `candidates_not_started`. +- [x] Calculer le curseur de reprise `before` depuis le dernier préfixe contigu réellement terminé. +- [x] Valider localement le correctif `0.3.3-pre.004` avec `cargo test -p kb_pipeline`, `cargo test -p kb_app_demo` et `cargo clippy --all-targets`. +- [x] Rejouer l’arrêt d’une campagne de 500 candidats et vérifier qu’aucun candidat non démarré n’est journalisé comme traité : 7 démarrés, 3 terminés, 4 annulés et 493 non démarrés. +- [x] Reporter la validation finale de `0.3.3` dans `CHANGELOG.md`. + +### 0.3.4 — extraction transaction canonique vers core et clôture `0.3.x` — clôturé + +- [x] Ajouter le worker `kb_sol_raw_transactions -> kb_sol_core_*` dans `kb_pipeline`. +- [x] Résoudre les account keys statiques et les loaded addresses dans un ordre canonique unique. +- [x] Extraire outer instructions, inner instructions, logs, balances, statut et contexte transactionnel. +- [x] Conserver des hashes déterministes pour les payloads d’instruction et le texte des logs. +- [x] Garantir l’idempotence par signature, indices stables et instruction path. +- [x] Ajouter le ledger `kb_sol_ops_processing_ledger` pour l’extracteur et ses versions. +- [x] Persister le graphe core, l’état raw et le succès ledger dans une transaction PostgreSQL unique. +- [x] Permettre le replay par signature, lot pending, plage de slots et program id déjà indexé. +- [x] Ajouter le mode normal, le force replay, la concurrence bornée et l’arrêt coopératif. +- [x] Ajouter `demo_core_extraction` avec accordéons Bootstrap 5, journal et résumé JSON. +- [x] Ajouter `demo_sql_replay_candidates` avec cinq tableaux read-only : transactions, programmes, mints, owners et account keys. +- [x] Ajouter les filtres PostgreSQL bornés par raw, ledger, slots, programme outer/inner/logs, mint, owner et account key. +- [x] Ajouter sélection par checkbox, reset des filtres, copie individuelle/multiligne et export CSV natif. +- [x] Écrire les CSV dans `data/exports_csv/` avec BOM UTF-8, séparateur `;`, CRLF et nom non écrasant. +- [x] Valider l’écriture CSV native dans Tauri/Linux : `replay_transactions.csv` écrit dans le workspace. +- [x] Ajouter un registre énumérable de 182 program IDs dans `kb_program_ids` et l’exposer à l’interface. +- [x] Ajouter les requêtes SQL d’intégrité raw/core/ledger. +- [x] Valider une extraction pending réelle : 70 extraites, 0 skip, 0 échec. +- [x] Valider l’intégrité après extraction : aucun doublon, orphelin, hash incohérent, ledger incohérent ou chemin inner invalide. +- [x] Valider le skip réel sur 14 signatures : 0 extraction, 14 skips. +- [x] Valider le force replay réel sur les mêmes 14 signatures : 14 extractions, 0 skip, cardinalité core inchangée. +- [x] Valider un second skip après force replay : 0 extraction, 14 skips. +- [x] Confirmer le ledger réel : 70 lignes `succeeded`, 84 tentatives après le force replay. +- [x] Ajouter un test PostgreSQL optionnel qui provoque une erreur intermédiaire, vérifie le rollback complet puis rejoue le bundle corrigé. +- [x] Rendre annulables les appels `getSignaturesForAddress`, `getTransaction`, l’attente du pacer et les pauses de retry/429. +- [x] Ajouter les tests offline d’annulation d’une opération longue et d’une pause de retry déjà annulée. +- [x] Conserver les transactions Solana échouées dans core et les rendre éligibles au futur décodage comme observations d’intention/échec. +- [x] Interdire aux futurs matérialiseurs d’interpréter automatiquement les événements d’une transaction échouée comme mutations d’état réussies. +- [x] Fusionner l’ancien périmètre `0.3.5` dans `0.3.4`; les corpus exhaustifs sont reportés aux versions protocolaires concernées. +- [x] Considérer le sélecteur read-only, les requêtes d’intégrité et le replay core comme outils de clôture de la fondation. +- [x] Valider `cargo test -p kb_pipeline` après l’ajout des tests d’annulation : 24 tests passés. +- [x] Valider `KB_POSTGRES_TEST_URL=... cargo test -p kb_store_pg -- --nocapture` avec le test de rollback : 39 tests passés. +- [x] Valider `cargo clippy --all-targets` sans avertissement. +- [x] Reporter `0.3.4` dans `CHANGELOG.md` et basculer le jalon actif vers `0.4.0`. + +Les contrôles autrefois prévus dans `0.3.5` sont reclassés ainsi : la stabilité canonique entre formes fournisseur est déjà couverte par `0.3.2`; le coût et les limites du backfill sont documentés par `0.3.3`; les corpus exhaustifs Pump, Meteora, Raydium et Orca appartiennent à leurs versions protocolaires; le rejeu réel d’un curseur `before` reste un contrôle opératoire utile mais ne bloque pas l’infrastructure de décodage, car la frontière contiguë et l’arrêt réel ont déjà été validés. + +## Version 0.4.x — décodeurs et matérialisations Solana Core/SPL + +### 0.4.0 — infrastructure de décodage core — clôturé + +- [x] Utiliser `prompts/019_v0_4_0_decoder_infrastructure.md` comme contrat de session. +- [x] Stabiliser les API `kb_decoder_api` et `kb_materializer_api` sur les inputs core contextualisés. +- [x] Ajouter le dispatch déterministe par `program_id`, surface, instruction path, payload/discriminator et contexte inner/logs. +- [x] Ajouter le replay borné par signatures, slots, program IDs, instruction paths, processor/version/hash et force replay. +- [x] Ajouter la persistance atomique des observations décodées, sorties matérialisées, couvertures et ledger. +- [x] Ajouter la politique explicite pour les transactions on-chain échouées et le refus des mutations métier supposées réussies. +- [x] Ajouter la matrice machine-readable déclarée/observée/reconnue/décodée/matérialisée/erreur, séparée par succès ou échec on-chain. +- [x] Ajouter `demo_decode_replay` sans SQL libre, avec sélection, processors, concurrence, journal, arrêt et diagnostics. +- [x] Ajouter le classifieur initial de toutes les surfaces natives connues sans faux décodage. +- [x] Borner automatiquement la sélection SQL aux program IDs déclarés par les décodeurs activés et refuser les filtres incompatibles. +- [x] Permettre au force replay borné par signatures de resoumettre les inputs quel que soit leur lifecycle courant. +- [x] Ajouter l’autorisation explicite « toutes les signatures » bornée par les filtres et la limite. +- [x] Refuser tout force replay sans signatures ni autorisation « toutes les signatures ». +- [x] Désactiver/refuser la matérialisation lorsqu’aucun matérialiseur n’est enregistré. +- [x] Corréler les traces par `campaign_id`, signature, instruction path, program ID et processor/version. +- [x] Valider localement les champs obligatoires du backfill avant invocation Tauri. +- [x] Ne plus réappliquer les DDL à chaque commande Tauri et synchroniser précisément les snapshots de couverture. +- [x] Valider `cargo test -p kb_config` : 36 tests passés. +- [x] Valider `cargo test -p kb_pipeline` : 41 tests passés. +- [x] Valider PostgreSQL réel `cargo test -p kb_store_pg -- --nocapture` : 44 tests passés. +- [x] Valider `cargo test -p kb_app_demo` : 75 tests passés. +- [x] Valider `cargo clippy --all-targets` sans avertissement. +- [x] Valider le build frontend de production avec `(cd kb_app_demo && npm run build)`. +- [x] Valider dans Tauri le skip version/hash, le force replay ciblé, le second skip, le replay global limité, l’annulation et le redémarrage. +- [x] Corriger le résumé d’annulation afin que `completed = started` et `started + not_started = selected`. +- [x] Valider la campagne finale : 382 sélectionnés, 45 démarrés/terminés, 337 non démarrés, aucun unmatched ni échec. +- [x] Valider les diagnostics `0.4.0` : 18 déclarations alors exposées, 382 observations Compute Budget et 94 observations System reconnues sans erreur ; Stake Config a ensuite été reclassé, ramenant le registre exécutable à 17 surfaces dans `pre.008`. +- [x] Reporter `0.4.0` dans `CHANGELOG.md` et basculer le jalon actif vers `0.4.1`. + +### 0.4.1 — programmes Solana natifs, loaders et précompiles — clôturé + +- [x] Utiliser `prompts/020_v0_4_1_native_solana_programs.md` comme contrat de session. +- [x] Auditer l’état réel de `kb_decoder_solana_core`, des types decode, du store, de la couverture et de la démo avant modification. +- [x] Vérifier chaque format d’instruction depuis les sources officielles actuelles ; ne jamais inventer un discriminant, un ordre de champs ou une sémantique. +- [x] Définir une taxonomie stable des événements natifs et les types partagés nécessaires sans créer de dépendance vers PostgreSQL, RPC ou Tauri. +- [x] Décoder maximalement `system`, y compris nonce accounts, seeds, allocate/assign, transfers et `CreateAccountAllowPrefund`, depuis `solana-system-interface 3.2.0`. +- [x] Décoder maximalement `compute_budget`, y compris `RequestUnitsDeprecated`, le tag réservé actuel et les limites modernes de `solana-compute-budget-interface 3.0.0` ; `pre.011` reproduit aussi l’acceptation runtime des octets suffixes via Borsh unchecked et les conserve pour audit. +- [x] Décoder maximalement `address_lookup_table` : create, freeze, extend, deactivate et close depuis `solana-address-lookup-table-interface 3.1.0`. +- [x] Ajouter une matérialisation générique et idempotente du lifecycle ALT, limitée aux observations exactes, réussies et commitées. +- [x] Valider ALT sur corpus mainnet réel : 104 instructions core, 104 inputs décodés, 104 sorties lifecycle, zéro échec ou unsupported. +- [x] Décoder maximalement `vote` depuis `solana-vote-interface ^6.0` avec `serde` + `wincode` : 20 variantes, autorisations, identité, commissions, withdraw, votes/state updates, formes compactes/tower, V2/BLS et récompenses délégateurs ; les deux collecteurs V2 proviennent des comptes d’instruction et non de `VoteInitV2`. +- [x] Décoder maximalement `stake` dans `pre.013` : 18 tags officiels, initialize/authorize/delegate/split/merge/withdraw/deactivate/lockup, variantes checked/seed, minimum delegation, deactivate delinquent, move stake/lamports et `redelegate` historique désactivé. +- [x] Reclasser `StakeConfig11111111111111111111111111111111` comme compte natif historique non exécutable et le retirer du dispatch/domaine d’exécution. +- [x] Valider `pre.013` sur le corpus Stake mainnet : 2 601/2 601 inputs décodés, zéro `failed`, `unsupported` ou `unmatched`, dont 339 instructions Stake réparties sur neuf entry codes observés. +- [x] Décoder `config` comme une écriture générique `ConfigKeys` + payload opaque borné et `feature` comme `revoke_pending_activation` selon leurs interfaces officielles. +- [x] Valider `pre.008` par tests ciblés, PostgreSQL réel, Clippy et démarrage/replay Tauri : 476/476 décodés, aucun échec, `unsupported` ou `unmatched`. +- [x] Valider Config sur corpus réel : 95 instructions décodées, zéro échec ou unsupported. Conserver Feature en validation synthétique faute d’invocation core observable dans les campagnes adressées. +- [x] Valider Vote par tests ciblés et replay mainnet : 51 tests décodeur réussis, 500 instructions Vote réelles décodées, toutes `tower_sync`, zéro échec Vote ; les 20 variantes restent couvertes par fixtures `wincode` officielles. +- [x] Valider `pre.011` après correction Compute Budget : 52 tests natifs, Clippy propre et force replay mainnet de 1 265/1 265 inputs décodés, avec zéro `failed`, `unsupported` ou `unmatched`. +- [x] Stabiliser dans `pre.012` le tracing transversal avant de reprendre les décodeurs : target canonique égal au package Cargo, matrice globale et par crate, `error.jsonl` dédié, erreurs decode/materialize structurées et responsabilité laissée à la crate opérationnelle. +- [x] Auditer les crates utilisant `tracing.workspace = true` et couvrir les treize crates alors actives par un test de constante canonique et par les routes des trois profils. +- [x] Ajouter le tracing propriétaire manquant sur les clients/pools HTTP et WebSocket ainsi que sur le backfill ; promouvoir les inputs decode unmatched, statuts failed/unsupported et échecs de matérialisation/persistance au niveau `error`. +- [x] Formaliser dans `docs/SOLANA_INTERFACE_DEPENDENCIES.md` la politique d’interfaces officielles : dépendance seulement au point de consommation, priorité `wincode` puis Borsh, parser borné lorsque l’interface n’expose que `bincode`. +- [x] Passer `CoreInstructionReplayInput` au contrat `2` avec une liste stable et numériquement ordonnée des instructions outer, sans migration SQL, afin de résoudre les références inter-instructions des précompiles. +- [x] Décoder `zk_elgamal_proof` dans `pre.015` selon `solana-zk-elgamal-proof-interface ^0.1` et le runtime Agave : treize discriminants, preuves inline ou référencées par compte, contexte optionnel et fermeture du contexte, sans recalcul cryptographique. +- [x] Décoder dans `pre.016` l’ancien `zk_token_proof` comme compatibilité historique, puis retirer dans `pre.019` la dépendance dépréciée `solana-zk-token-sdk` au profit d’un miroir wire local borné des dix-sept discriminants et tailles auditées. +- [x] Décoder les loaders `native_loader`, `bpf_loader_deprecated`, `bpf_loader`, `bpf_loader_upgradeable` et `loader_v4` ; classer l’invocation Native Loader opaque comme `ignored` sans sémantique inventée. +- [x] Décoder dans `pre.014` les précompiles `ed25519`, `secp256k1` et `secp256r1`, y compris compteurs, tables d’offsets, références outer courantes/externes, recovery ID secp256k1 et payloads structurés bornés sans recalcul cryptographique. +- [x] Conserver Memo hors périmètre natif et réserver `kb_decoder_spl_memo` pour ses générations v1, v3 et v4. +- [x] Distinguer `recognized`, `decoded`, `ignored`, `unsupported` et `failed` sans convertir une reconnaissance partielle en succès de décodage ; l’audit `pre.017` confirme les statuts terminaux séparés du pipeline et des observations. +- [x] Conserver la politique des transactions échouées : événements d’intention/échec autorisés, mutations réussies interdites par défaut ; `SuccessfulCommittedOnly` protège toutes les projections lifecycle natives. +- [x] Matérialiser les projections natives génériques déjà stabilisées : ALT, lifecycle loaders, révocation Feature, durable nonce, contextes ZK ElGamal et rapports Slashing. Ne pas les présenter comme la totalité des matérialisations natives possibles. +- [x] Implémenter dans `pre.017` les projections natives manquantes réellement justifiées : lifecycle des durable nonce accounts System et lifecycle des comptes de contexte ZK ElGamal, sans matérialiser les preuves. +- [x] Concevoir dans `pre.017` le contrat de `kb_materializer_staking` pour les projections Stake/Vote nécessitant un état durable : état précédent/suivant, délégation, désactivation, retrait, autorités, lockup, identité/commission Vote et récompenses ; ne pas les ajouter au matérialiseur générique. +- [x] Ajouter dans `pre.022` un profil Compute Budget transactionnel agrégé ; conserver les précompiles de signature et les preuves ZK sans contexte en decode/audit tant qu’aucun consommateur n’exige une projection dédiée. +- [x] Documenter dans `pre.019` l’audit complet des projections actives, différées et volontairement absentes dans `docs/NATIVE_SOLANA_MATERIALIZATION_AUDIT.md`. +- [x] Couvrir chaque variante déclarée par des fixtures officielles, dérivées des encodeurs SDK ou explicitement alignées sur le wire runtime ; l’audit `pre.018` inclut aussi les deux instructions Slashing. +- [x] Clore l’inventaire corpus : ALT et Config validés réellement ; Feature, loaders v3/v4, Slashing et ZK Token Proof validés synthétiquement faute d’invocation observable ; campagne Slashing retournée proprement à zéro signature. +- [x] Couvrir les payloads tronqués, discriminants inconnus, comptes invalides, offsets précompile invalides et transactions on-chain échouées sur toutes les surfaces dont le runtime expose ces cas. +- [x] Ajouter dans `pre.014` les fixtures adversariales des trois précompiles : références courantes, externes et mixtes, signatures multiples, messages vides/volumineux, headers/tables tronqués, compteurs invalides, index ou offsets hors bornes, Base64 absent/invalide, débordement borné et transaction échouée non commitée. +- [x] Ajouter dans `pre.001`/`pre.002` les fixtures officielles System/Compute Budget, les limites numériques, payloads tronqués, tags inconnus, comptes manquants, déterminisme et transactions échouées. +- [x] Ajouter dans `pre.003` ALT exact, la projection lifecycle ALT, les IDs SPL vérifiés et les crates réservées manquantes. +- [x] Analyser le replay mainnet `pre.004` : 476 inputs, 475 décodés, un faux échec System dû à un compte additionnel et 11 faux refus de matérialisation hors surface ALT. +- [x] Corriger dans `pre.005` le décodage System afin de conserver les comptes additionnels validement résolus avec le rôle `additional_account`. +- [x] Ajouter dans `pre.005` une sélection de matérialiseur par observation exacte, avant ledger et politique de transaction. +- [x] Réserver les exécuteurs manquants pour Memo, Single Pool, Account Compression, Noop et SPL Name Service. +- [x] Formaliser puis rendre obligatoire le contrat de tracing par crate dans `docs/TRACING_CONTRACT.md`. +- [x] Valider `pre.005` : cinq exécuteurs SPL réservés, 42 tests pipeline, 22 tests natifs, 4 tests lifecycle, 76 tests démo et clippy propre. +- [x] Valider le force replay ciblé puis global : 3/3 et 476/476 décodés, zéro échec, unsupported, unmatched et refus de matérialisation. +- [x] Ajouter dans `pre.006` les loaders immuables v1/v2, le loader upgradeable, Loader v4, le classement Native Loader opaque et la projection lifecycle des loaders. +- [x] Valider `pre.006` : 31 tests natifs, 6 tests lifecycle, 42 tests pipeline, 76 tests démo, Clippy propre et replay global Tauri de 476/476 inputs sans échec, unsupported, unmatched ni refus de matérialisation. +- [x] Rejouer System, Compute Budget et ALT sur le corpus PostgreSQL réel : campagne globale `pre.005` de 476 inputs, 476 décodés, aucun failed, unsupported, unmatched ou faux refus de matérialisation. +- [x] Valider `pre.017` : 12 tests lifecycle, 42 tests pipeline, 45 tests PostgreSQL réel, 76 tests démo, Clippy propre, 484 instructions System décodées avec 27 sorties nonce et 27 refus attendus de transactions échouées, puis 151 instructions ZK ElGamal avec 139 sorties de contexte. +- [x] Auditer Agave `v4.1.1` et ajouter dans `pre.018` le stateless Slashing Program absent du registre : deux instructions exactes, dispatch sans fallback, projection de rapport et absence explicite de pénalité appliquée par le programme. +- [x] Ajouter des invariants automatiques garantissant une surface par ID natif, au moins une couverture par surface, des identités de couverture uniques et zéro fallback `unclassified_native_instruction`. +- [x] Ajouter dans `pre.019` les modes `program_latest`, `token_latest` et `pool_latest` : une recherche « avant, plus anciennes » sans ancre commence sur la page de signatures la plus récente ; la direction `after` conserve une ancre obligatoire. +- [x] Retirer dans `pre.019` `solana-zk-token-sdk` et sa dépendance transitive à `bincode`; conserver uniquement un miroir wire local audité des tags et tailles historiques. +- [x] `pre.020` : matérialiser System create/allocate/assign, les écritures Config, les changements d’autorité Loader et les mutations de bytecode Loader sans dupliquer les balances core ni les payloads complets. +- [x] Activer `kb_materializer_admin` pour `assign`/`assign_with_seed`, Config `store`, Loader v3 `set_authority`/`set_authority_checked` et Loader v4 `transfer_authority`, avec politique `SuccessfulCommittedOnly`. +- [x] Activer `kb_materializer_compliance_audit` pour les `write` Loader v1/v2/v3/v4 et `copy` Loader v4, en conservant uniquement comptes, offsets, tailles, SHA-256 et préfixes bornés. +- [x] Enregistrer les trois matérialiseurs natifs dans `demo_decode_replay` et ajouter les routes `debug`/`info`/`error.jsonl` dédiées aux deux crates nouvellement opérationnelles. +- [x] `pre.021` : activer `kb_materializer_staking` pour les intentions/transitions commitées Stake/Vote, avec limites explicites sur l'absence de snapshots de comptes, et sérialiser les tests PostgreSQL réels pour éviter les interblocages intermittents. +- [x] `pre.022` : produire le profil Compute Budget transactionnel agrégé via `kb_materializer_compliance_audit` et confirmer la politique d'absence de projection pour signatures/ZK sans contexte. +- [x] Clôturer `0.4.1` avec 18 surfaces natives, matérialisations instructionnelles stables, logs propres et prompt de reprise `0.4.2`. +- [x] `pre.023` : replay global ciblé, validations finales, documentation de clôture, prompt `0.4.2` et mise à jour du `CHANGELOG.md`. +- [x] Confirmer que les précompiles de signature, les preuves ZK sans contexte et l’ancien ZK Token Proof restent volontairement sans projection dédiée, hors contexte matérialisable. +- [x] Ne pas étendre `demo_decode_replay` au-delà des matérialiseurs natifs enregistrés : inspection suffisante via résumés, couverture et requêtes SQL de validation. +- [x] Valider les replays ciblés avec corrélation par `campaign_id` pour System, Config, Stake, Vote, Compute Budget, ALT, ZK ElGamal et les surfaces synthétiques sans corpus. +- [x] Exécuter les tests unitaires des crates touchées, PostgreSQL réel, Clippy et campagnes Tauri/réelles avant mise à jour du changelog. +- [x] Mettre à jour `CHANGELOG.md` uniquement après validation complète de `0.4.1`. + +### 0.4.2 — infrastructure d’exécution et `kb_executor_solana_core` — clôturée + +#### Principes de portée + +- [x] Les crates d’exécution sont des bibliothèques universelles réutilisables par `kb_app_demo`, les workers, les CLI et de futurs exécutables non liés au trading. +- [x] La dangerosité d’une opération ne justifie pas son absence du builder : elle doit être encodée, testée et protégée par une politique dédiée, puis éventuellement rester non exposée dans `kb_app_demo`. +- [x] L’objectif est de couvrir toutes les instructions natives officiellement appelables. `Unsupported(reason)` ne doit subsister que pour une surface non invocable par transaction, retirée du runtime, insuffisamment documentée officiellement ou encore temporairement non implémentée avec dette explicite au ROADMAP. +- [x] Les exécuteurs restent indépendants du wallet, du RPC, des décodeurs et de Tauri ; ils produisent des plans déterministes et laissent simulation, signature, envoi et validation à l’orchestration. + +#### Tranches validées + +- [x] `pre.001` : ajouter les contrats typés de capacité, politique, plan préparé, simulation contextualisée, envoi, confirmation et diagnostic post-exécution sans casser les exécuteurs réservés historiques. +- [x] `pre.001` : implémenter les builders officiels System transfer/create account/allocate/assign et Compute Budget set unit limit/set unit price. +- [x] `pre.001` : remplacer `Maybe` dans `kb_executor_solana_core` par `Yes/No` pour le pont historique et `Supported/Unsupported(reason)` pour la voie typée. +- [x] `pre.001` : ajouter les contrôles de plan et pré-envoi pour simulation obligatoire, dry-run, cluster, mainnet, blockhash/nonce, signataires et plafonds. +- [x] `pre.002` : aligner le registre de l’exécuteur sur les dix-huit surfaces natives en incluant Slashing et corriger les retours explicites Clippy. +- [x] Valider localement les fondations : `kb_execution_api` 21 tests, `kb_execution_safety` 13 tests, `kb_executor_solana_core` 10 tests, `kb_pipeline` 45 tests, `kb_app_demo` 78 tests, `kb_config` 36 tests et Clippy global propre. +- [x] `pre.003` : étendre la configuration wallet/exécution à localnet, devnet, testnet et mainnet, ajouter dry-run, plafonds de frais/priorité et âge maximal du blockhash. +- [x] `pre.003` : remplacer le placeholder `kb_wallet` par un wallet temporaire en mémoire ou persistant, stocké sous `wallets/`, créé sans écrasement, chargé de façon bornée et utilisable comme `Signer` sans exposer les octets privés. +- [x] `pre.003` : protéger les fichiers wallet par permissions Unix privées, effacement des buffers secrets sérialisés, `.gitignore` bloquant et interdiction de sélectionner le wallet temporaire dans un profil mainnet. +- [x] Valider localement `pre.003` : `kb_wallet` 6 tests, `kb_config` 40 tests, `kb_store_pg` 45 tests offline et PostgreSQL réel, `kb_app_demo` 78 tests et Clippy global propre. + +#### Suite d’orchestration + +- [x] `pre.004` : ajouter dans `kb_rpc` les adaptateurs typés `getGenesisHash`, `getLatestBlockhash`, `getFeeForMessage` et `simulateTransaction`, avec routage par rôle, classification des clusters publics, diagnostics runtime et conversion vers `ExecutionSimulationResult`, sans signature ni envoi. +- [x] `pre.004` : documenter dans `kb_executor_solana_core/README.md` chaque fonction publique, les six opérations constructibles, leurs paramètres, résultats, effets et frontières. +- [x] Valider localement `pre.004` : `kb_rpc` 63 tests, `kb_execution_api` 21 tests, `kb_execution_safety` 13 tests, `kb_executor_solana_core` 10 tests, `kb_config` 40 tests et Clippy global propre. +- [x] `pre.005` : ajouter `kb_execution_solana` pour assembler les instructions planifiées en transaction legacy, produire les payloads base64 de frais/simulation, lier la simulation au hash exact du message, résoudre exactement les signataires requis via l’interface Solana `Signer` et signer séparément hors Tauri. +- [x] `pre.005` : renommer la constante interne en `MAINNET_GENESIS_HASH` sans changer sa valeur ; conserver `mainnet-beta` seulement comme alias historique des endpoints/CLI et renommer le variant public en `Mainnet` sérialisé `mainnet`. +- [x] `pre.005` : distinguer la simulation exacte de la simulation avec remplacement de blockhash et interdire qu’un résultat `replaceRecentBlockhash=true` autorise la signature du message original. +- [x] Valider localement les tests ciblés de `pre.005`/`pre.006` : `kb_execution_solana` 7 tests, `kb_execution_api` 22 tests, `kb_execution_safety` 15 tests, `kb_rpc` 63 tests, `kb_executor_solana_core` 10 tests, `kb_wallet` 6 tests et `kb_config` 40 tests. +- [x] `pre.006` : corriger la compilation croisée de `pre.005`, supprimer l’avertissement TS-rs sur l’alias historique `mainnet_beta`, utiliser le trait public `TypedInstructionExecutor` dans les tests d’assemblage, resynchroniser le contrat de simulation RPC et rendre les erreurs de routage tracing explicites. +- [x] `pre.007` : supprimer le dernier opérateur `?` introduit dans un chemin de production de `kb_execution_api`, le remplacer par un `match` avec propagation explicite et auditer les fichiers d’exécution `0.4.2` contre `?`, `unwrap` et `expect` de production. +- [x] Valider localement `pre.007` : `kb_execution_api` 22 tests et Clippy global propre. +- [x] `pre.008` : ajouter les contrats RPC typés `getBalance`, `requestAirdrop`, `sendTransaction`, `getSignatureStatuses` et `getBlockHeight`, avec routage par rôle, préflight obligatoire, contrôle de la signature retournée et confirmation bornée jusqu’à succès, échec, expiration ou timeout. +- [x] `pre.008` : étendre la configuration avec retries d’envoi, intervalle/nombre maximal de polls et plafond d’airdrop devnet ; construire directement les politiques RPC depuis `ExecutionConfig`. +- [x] Valider localement `pre.008` : `kb_rpc` 69 tests, `kb_execution_api` 22 tests, `kb_config` 40 tests, `kb_execution_solana` 7 tests, `kb_execution_safety` 15 tests, `kb_wallet` 6 tests, `kb_executor_solana_core` 10 tests, `kb_store_pg` 45 tests avec PostgreSQL réel et Clippy global propre. +- [x] `pre.009` : orchestrer le premier parcours backend Devnet complet avec wallet persistant, airdrop plafonné, plan System transfer, simulation exacte, signature, envoi, confirmation, `getTransaction`, insertion canonique, extraction core et decode replay ciblé. +- [x] Valider `pre.009` offline : `kb_pipeline` 49 tests, `kb_rpc` 69 tests, `kb_execution_api` 22 tests, `kb_execution_safety` 15 tests, `kb_execution_solana` 7 tests, `kb_executor_solana_core` 10 tests, `kb_wallet` 6 tests, `kb_config` 40 tests, `kb_store_pg` 45 tests avec PostgreSQL réel et Clippy global propre. +- [x] Valider le parcours Devnet réel avec le wallet persistant préfinancé et un transfert de 1 000 000 lamports : simulation, signature, envoi, confirmation et replay post-exécution réussis. +- [x] `pre.010` : ajouter `getAccountInfo` et `getMinimumBalanceForRentExemption`, refuser avant simulation un transfert insuffisant vers une adresse inexistante et conserver l’erreur runtime ainsi que les logs lorsque la simulation échoue. +- [x] `pre.010` : ajouter `demo_execution_solana_core`, limité aux profils Devnet persistants, avec plan, simulation exacte, confirmation opérateur, envoi, progression et diagnostics post-exécution sans exposition de clé privée. +- [x] Valider localement `pre.010` : `kb_rpc` 70 tests, `kb_pipeline` 51 tests, `kb_app_demo` 87 tests, `kb_execution_api` 22 tests, `kb_execution_safety` 15 tests, `kb_execution_solana` 7 tests, `kb_executor_solana_core` 10 tests, `kb_wallet` 6 tests, `kb_config` 40 tests, `kb_store_pg` 45 tests avec PostgreSQL réel et Clippy global propre. +- [x] Valider visuellement `demo_execution_solana_core` dans `cargo tauri dev` : simulation dry-run, refus sans confirmation opérateur, envoi Devnet confirmé, insertion canonique 1, extraction core 1 et decode replay 1 sans échec. +- [x] `pre.011` : ajouter les builders System `create_account_with_seed`, `transfer_with_seed`, `allocate_with_seed`, `assign_with_seed` et `create_account_allow_prefund`, avec validation des dérivations et signataires exacts. +- [x] Valider localement `pre.011` : `kb_executor_solana_core` 14 tests, `kb_app_demo` 87 tests et Clippy global propre. Le build frontend séparé n’est pas requis lorsque Vite est déjà validé par le serveur de développement Tauri. +- [x] `pre.012` : ajouter `transfer_many`, les créations de compte nonce normale/seedée et les builders de lifecycle nonce initialize/advance/authorize/withdraw/upgrade, avec plans multi-instructions et politique recent-blockhash explicite. +- [x] Valider localement `pre.012` : `kb_executor_solana_core` 19 tests, `kb_execution_solana` 7 tests, `kb_execution_api` 22 tests et Clippy global propre. +- [x] `pre.013` : ajouter la lecture RPC complète et bornée d’un compte nonce, sa validation System Program/Current/Initialized via `solana-nonce` et `wincode`, l’assemblage officiel `Message::new_with_nonce` avec `AdvanceNonceAccount` en première instruction, la valeur nonce comme blockhash et la preuve de simulation liée au message et à cette valeur. +- [x] Valider localement `pre.013` : `kb_executor_solana_core` 20 tests, `kb_execution_solana` 12 tests, `kb_execution_api` 22 tests, `kb_rpc` 70 tests et Clippy global propre. +- [x] `pre.014` : compléter Compute Budget avec `RequestHeapFrame` et `SetLoadedAccountsDataSizeLimit`, synchroniser les capacités et exports TS-rs, rendre normative la documentation des APIs publiques dans les README et détailler le phasage des exécuteurs futurs. +- [x] Valider localement `pre.014` : `kb_executor_solana_core` 21 tests, `kb_execution_api` 22 tests, `kb_execution_safety` 15 tests, `kb_execution_solana` 12 tests, `kb_rpc` 70 tests et Clippy global propre. +- [x] `pre.015` : ajouter les cinq builders officiels Address Lookup Table create/extend/freeze/deactivate/close, avec contrats d’intent, capacités exactes, exports TS-rs, contrôles statiques et documentation de la frontière stateful. +- [x] Valider localement `pre.015` : `kb_executor_solana_core` 25 tests, `kb_execution_api` 22 tests, `kb_execution_safety` 15 tests, `kb_execution_solana` 12 tests, `kb_rpc` 70 tests et Clippy global propre. +- [x] `pre.016` : ajouter les builders déterministes Ed25519, secp256k1 et secp256r1 dans deux formes par précompile — inline et table d’offsets — avec layouts officiels, références externes, bornes, exports TS-rs et documentation des contraintes de positionnement. +- [x] Valider localement `pre.016` : `kb_executor_solana_core` 31 tests, `kb_execution_api` 22 tests, `kb_execution_safety` 15 tests, `kb_execution_solana` 12 tests, `kb_rpc` 70 tests et Clippy global propre. +- [x] `pre.017` : ajouter les builders Config create/store, Feature activate/revoke, Slashing close/duplicate-block proof atomique et ZK ElGamal inline/account/close ; classer l’ancien ZK Token Proof comme historique `decode-only`, sans `Unsupported` générique. +- [x] `pre.018` : corriger les quatre avertissements Clippy de tests de `pre.017`, ajouter les vingt-quatre opérations Stake actuelles avec miroir wire `wincode`, plans composés officiels, capacités exactes, exports TS-rs et documentation stateful. +- [x] Valider localement `pre.017` : `kb_executor_solana_core` 46 tests, `kb_execution_api` 22, `kb_execution_safety` 15, `kb_execution_solana` 12 et `kb_rpc` 70. Clippy ne signale que quatre `cloned_ref_to_slice_refs` de tests, corrigés dans `pre.018`. + +#### Exhaustivité des builders natifs + +- [x] Compléter System avec les variantes seed, transferts seed, `create_account_allow_prefund` et le helper multi-instructions `transfer_many`. +- [x] Implémenter les builders officiels de création et lifecycle durable nonce : create/create-with-seed, initialize, advance, authorize, withdraw et upgrade, exécutés avec un recent blockhash normal. +- [x] Ajouter dans `kb_execution_solana` l’assemblage d’une transaction utilisant réellement un nonce durable : lecture complète bornée, validation exacte de l’état courant initialisé, `AdvanceNonceAccount` en première instruction, valeur nonce comme blockhash et preuve de simulation liée au message exact. +- [x] `pre.014` : compléter Compute Budget avec les quatre variantes client actuelles `request_heap_frame`, `set_compute_unit_limit`, `set_compute_unit_price` et `set_loaded_accounts_data_size_limit`, comparées aux builders officiels et protégées par des limites explicites. Conserver `Unused` et `RequestUnitsDeprecated` comme compatibilité de décodage seulement. +- [x] `pre.014` : rendre normative la documentation des APIs publiques dans les README des crates opérationnelles, documenter les contrats `kb_execution_api`, `kb_execution_safety`, `kb_execution_solana`, `kb_executor_solana_core` et auditer l’appariement des 102 surfaces décodeur/exécuteur réservées. +- [x] Valider localement `pre.014` : `kb_executor_solana_core` 21 tests, `kb_execution_api` 22 tests, `kb_execution_safety` 15 tests, `kb_execution_solana` 12 tests, `kb_rpc` 70 tests et Clippy global propre. +- [x] Implémenter les builders stateless Address Lookup Table create/extend/freeze/deactivate/close contre l’interface officielle, avec signataires exacts, ordre conservé et bornes d’input. +- [x] Ajouter l’orchestration ALT stateful : lecture/décodage du compte, autorité et état courants, capacité restante, slot récent, rent top-up mesuré, cooldown de fermeture et lifecycle localnet/devnet. +- [x] Implémenter les précompiles Ed25519, secp256k1 et secp256r1 avec formes inline et offsets, layouts officiels déterministes, références inter-instructions, bornes de taille et aucune dépendance `bincode` directe. +- [x] Valider localement `pre.016` et confirmer les six builders par tests officiels, exports TS-rs et Clippy global. +- [x] Implémenter Config create/store, Feature activate/revoke, les deux instructions Slashing et les douze vérifications plus fermeture ZK ElGamal officiellement appelables, même lorsqu’elles ne seront pas proposées dans la démo opérateur. +- [x] Classer l’ancien ZK Token Proof comme programme historique `decode-only` : aucun builder moderne ne doit être inventé pour le stub retiré du runtime actif. +- [x] Ajouter les orchestrations stateful de `pre.022` : rent et état Config/Feature, compte de preuve et fenêtre temporelle Slashing, préallocation/owner/space des contextes ZK ElGamal. +- [x] Implémenter Stake maximalement pour les vingt-quatre helpers clients actuels : create/initialize, checked/seed, split/merge, délégation, autorités, lockup, withdraw, deactivate, delinquent et move. Classer `Redelegate`/`redelegate_with_seed` comme historiques désactivés `decode-only`. +- [x] Implémenter dans `pre.019` Vote maximalement : vingt variantes wire actuelles, quatre créations composées legacy/V2 avec ou sans seed, autorités Ed25519/BLS, votes, TowerSync, commissions, collecteurs, retrait et dépôt de récompenses, avec tests unitaires avant tout essai devnet. +- [x] Implémenter dans `pre.020` les opérations Loader réellement constructibles : onze plans Loader v3 via `solana-loader-v3-interface`/`wincode` et neuf plans Loader v4 au wire officiel reproduit sans activer ses helpers `bincode`; conserver BPF Loader v1/v2 historiques en `decode-only` et Native Loader sans builder client. +- [x] Traiter dans `pre.020` la dette post-`0.4.1` : migrer le parser Loader v3 upgradeable vers `solana-loader-v3-interface ^8.0` avec `wincode`, préserver la compatibilité du booléen optionnel historique et ne pas activer les helpers `bincode` de Config, Loader v2 ou Loader v4. +- [x] Consolider dans `pre.021` les helpers internes répétés de `kb_executor_solana_core` : neuf parsers de clés, cinq parsers d’ID natif, validations communes de comptes distincts, montants positifs, longueurs exactes, dérivations seedées, wrappers d’erreur Loader et appender d’offsets `u16`; conserver séparées les sérialisations et validations dont le contrat reste propre à une surface. +- [x] Ajouter une matrice machine-readable programme/opération/builder officiel/politique/tests unitaires/tests cluster/exposition UI, validée automatiquement contre les 109 constantes d’opération et les 18 surfaces natives. + +#### Phasage restant de l’exhaustivité native + +- [x] `pre.015` : builders stateless Address Lookup Table create/extend/freeze/deactivate/close, avec autorité, ordre de comptes, payer optionnel et contrats de lifecycle exacts ; conserver rent, capacité, slot et cooldown dans la tranche stateful `pre.022`. +- [x] `pre.016` : builders Ed25519, secp256k1 et secp256r1, avec formes inline/offsets, références d’instructions, comparaison aux constructeurs ou layouts officiels, low-S secp256r1 et position secp256k1 explicitement bornée. +- [x] Validation locale de `pre.016` avant passage à `pre.017`. +- [x] `pre.017` : Config, Feature, Slashing et surfaces ZK officiellement appelables ; classification explicite de l’ancien ZK Token Proof comme historique `decode-only`. +- [x] Validation locale de `pre.017` avant passage à `pre.018`, avec quatre avertissements Clippy de tests reportés et corrigés dans le lot suivant. +- [x] `pre.018` : Stake maximal — vingt-quatre helpers clients actuels, autorités, lockup, création/rent fourni, délégation, merge/split, retraits, move et variantes checked/seed ; `Redelegate` reste historique désactivé. +- [x] Valider localement `pre.018` : `kb_executor_solana_core` 57 tests, `kb_execution_api` 22, `kb_execution_safety` 15, `kb_execution_solana` 12, `kb_rpc` 70 et Clippy global propre. +- [x] `pre.019` : Vote maximal — vingt variantes wire, quatre créations composées, initialisation legacy/V2, vote/update, autorisations, commissions, collecteurs, identité, retrait, récompenses, compact update et TowerSync. +- [x] Valider localement `pre.019` : `kb_executor_solana_core` 70 tests, `kb_execution_api` 22, `kb_execution_safety` 15, `kb_execution_solana` 12, `kb_rpc` 70 et Clippy global propre. +- [x] `pre.020` : onze opérations Loader v3 et neuf opérations Loader v4 réellement constructibles, avec déploiement/upgrade/autorités/fermeture hors UI; BPF Loader v1/v2 restent historiques `decode-only` et Native Loader est classé sans instruction client. +- [x] Valider localement `pre.020` : `kb_executor_solana_core` 81 tests, `kb_decoder_solana_core` 115, `kb_execution_api` 22, `kb_execution_safety` 15, `kb_execution_solana` 12 et `kb_rpc` 70. +- [x] `pre.021` : centraliser dans un module privé les parsers `Pubkey`/program ID et les validations communes de longueur, montant positif, comptes distincts, dérivation seedée et erreurs partagées ; supprimer les implémentations de production identiques sans modifier les 109 opérations ni leur wire. +- [x] Valider localement `pre.021` après `fix.001` : `kb_executor_solana_core` 86 tests et `cargo clippy --all-targets` propre ; ce Clippy couvre également les changements Loader de `pre.020`. +- [x] `pre.022` : implémenter le préflight stateful Localnet/Devnet pour ALT, Config, Feature, Slashing et contextes ZK ElGamal ; ajouter `getEpochInfo` typé et la matrice machine-readable complète des 18 surfaces natives avec classification de toute opération non invocable. +- [x] Valider localement `pre.022` : `kb_executor_solana_core` 87 tests, `kb_pipeline` 56 tests, `kb_rpc` 71 tests, `kb_execution_api` 22 tests, `kb_execution_safety` 15 tests, `kb_execution_solana` 12 tests, validateur de matrice réussi et `cargo clippy --all-targets` propre. +- [x] `pre.023` : remplacer le validateur Python de la matrice d’exécution par un test Rust fondé sur `SOLANA_CORE_OPERATION_CODES`; ajouter une matrice décodeur exacte de 18 surfaces/121 déclarations et l’inventaire canonique des 52 méthodes HTTP et neuf paires WebSocket standard. +- [x] Valider localement `pre.023` : `kb_executor_solana_core` 88 tests, `kb_decoder_solana_core` 116 tests, `kb_rpc` 78 tests et `cargo clippy --all-targets` propre. +- [x] `pre.024` : ajouter les adaptateurs configurables propres à `kb_rpc` des 38 méthodes HTTP standard auparavant limitées au contrat JSON brut ; conserver toutes les options officielles sélectionnables indépendamment, ne pas exposer les DTO Agave et imposer `52 typed / 0 raw` dans la matrice RPC. +- [x] Valider localement `pre.024` : `kb_rpc` 98 tests après correction explicite du type de `Vec` dans un test, puis `cargo clippy --all-targets` propre. +- [x] `pre.025` : déplacer le runtime WebSocket persistant de `kb_app_demo` vers `kb_rpc`, typer paramètres et notifications des neuf paires standard et garder les trois surfaces instables derrière une capacité explicite. +- [x] Valider localement `pre.025` après `fix.001` : `kb_rpc` 113 tests, `kb_app_demo` 87 tests et `cargo clippy --all-targets` propre ; confirmer la désactivation dynamique des méthodes WebSocket instables rejetées par le nœud. +- [x] `pre.026` : auditer les logs Tauri de clôture et identifier l’unique erreur applicative réelle comme une incompatibilité TS-rs `u64 -> bigint` lors de `demo_ws_unsubscribe`; les avertissements de confirmation opérateur et de requête SQL lente restent attendus et non bloquants. + +- [x] `pre.027` : premières projections Transfer Fee publiques dans `kb_materializer_fees`; validation 4/4, Token-2022 50/50, ElGamal Registry 5/5 et Clippy propre. +- [x] `pre.028` : sept feuilles Confidential Transfer Fee classées dans `EventFamily::Fee` et projetées sans déchiffrement ; validation Token-2022 50/50, ElGamal Registry 5/5, fees 6/6 et Clippy propre. +- [ ] `pre.029` : projeter les snapshots bornés Mint/Account/Multisig Token-2022, leur inventaire TLV ordonné et les états confidentiels publics dans `kb_materializer_token_accounts`. +- [x] `pre.026` : aligner les payloads Tauri WebSocket, backfill, SQL et configuration sur des nombres JSON, supprimer la construction frontend de `BigInt`, borner l’identifiant d’unsubscribe à `Number.isSafeInteger` et ajouter des tests de non-régression sur les déclarations TS-rs. +- [x] Valider la correction TS-rs de `pre.026` : `kb_config` 41 tests, `kb_app_demo` 88 tests, `cargo clippy --all-targets` propre, démarrage Tauri/Vite réussi et souscriptions/désabonnements WebSocket réels sans erreur `BigInt`. +- [x] `pre.026`/`pre.027` : terminer les audits README/API, enregistrer les validations offline, PostgreSQL, Devnet et RPC réelles, conserver les opérations administratives stateful derrière leurs préflights et sans activation Mainnet, puis clôturer la documentation et le `CHANGELOG.md`. + +#### Critères de clôture de l’exécuteur natif + +- [x] Chaque surface native du registre possède soit tous ses builders client officiellement appelables, soit une classification documentée `non invocable`, `retirée` ou `historique decode-only` dans la matrice canonique. +- [x] La couverture bibliothèque est indépendante de l’exposition dans `kb_app_demo` ; masquer une opération dangereuse dans l’UI ne permet pas de la considérer comme hors périmètre. +- [x] Toute API publique utile des crates d’exécution est inventoriée dans le README de sa crate avec rôle, paramètres, résultat, effets, limites et exemple minimal ; l’audit final couvre `kb_execution_api`, `kb_execution_safety`, `kb_execution_solana`, `kb_executor_solana_core`, `kb_rpc`, `kb_pipeline` et `kb_app_demo`. +- [x] Chaque builder est comparé offline à un encodeur ou layout officiel. Les opérations stateful non exercées réellement restent bloquées par le préflight, hors UI dangereuse et sans activation Mainnet ; une validation Localnet/Devnet par opération reste obligatoire avant toute future activation mutable. +- [x] La matrice machine-readable ne contient aucune opération callable sans statut, politique, tests offline, validation cluster et décision d’exposition UI ; le test Rust impose l’égalité exacte avec les 109 codes compilés. + +#### Critères de clôture du décodeur natif et de `kb_rpc` + +- [x] Les 18 Program IDs natifs du registre partagé correspondent exactement aux 18 surfaces du décodeur. +- [x] Les 121 déclarations de couverture sont comptées par surface et reliées à un test d’enum officielle ou de layout wire audité. +- [x] Les 52 méthodes HTTP et neuf paires WebSocket standard sont inventoriées sans omission dans un registre compilé et une matrice JSON. +- [x] Les 52 méthodes HTTP possèdent un contrat typé dédié ; la voie JSON brute ne constitue plus le contrat principal d’une méthode standard. +- [x] Les neuf paires WebSocket possèdent paramètres, notifications et runtime persistant dans `kb_rpc`, sans logique de transport lourde dans `kb_app_demo`. + +#### Démo et clôture + +- [x] Ajouter `demo_execution_solana_core` après le premier parcours backend Devnet validé ; exposer le transfert contrôlé et masquer les opérations administratives dangereuses. +- [x] Afficher plan, comptes, signataires requis, plafonds, cluster réel, simulation, signature/envoi et validation post-exécution sans jamais transférer de clé privée à la WebView. +- [x] Conserver `0.4.2` limité à la démo System transfer validée ; les créations/allocation/assignation et le nonce ne sont volontairement pas ajoutés à l’UI sans orchestrateur stateful cluster dédié. +- [x] Valider les builders offline contre les encodeurs officiels, valider le parcours Devnet représentatif et conserver Mainnet désactivé par défaut ; les autres opérations stateful restent conditionnées à une preuve cluster ultérieure avant exposition. +- [x] Clôturer `0.4.2` par tests ciblés, PostgreSQL réel, Clippy, parcours Devnet avec replay post-exécution, smoke tests RPC/Tauri, documentation finale et mise à jour du `CHANGELOG.md`. + +#### Validation finale observée — 13 juillet 2026 + +- [x] `kb_program_ids` 5 tests, `kb_decoder_solana_core` 116, `kb_executor_solana_core` 88, `kb_execution_api` 22, `kb_execution_safety` 15 et `kb_execution_solana` 12. +- [x] `kb_rpc` 113 tests, `kb_pipeline` 56, `kb_config` 41 et `kb_app_demo` 88. +- [x] `kb_store_pg` 45 tests avec PostgreSQL réel, migrations et roundtrips optionnels réussis. +- [x] Parcours Devnet opt-in réussi avec wallet persistant préfinancé, transfert de 1 000 000 lamports, simulation, signature, envoi, confirmation, hydratation canonique, extraction core et decode replay. +- [x] `cargo clippy --all-targets` sans avertissement. +- [x] `cargo tauri dev -c kb_app_demo/tauri.conf.json` validé avec Vite, initialisation PostgreSQL et sessions WebSocket réelles. +- [x] Les logs de clôture confirment les unsubscribe `slot`, `root` et `program`. Le refus de changer d’endpoint avec une session active est un garde-fou attendu. La limite SQL supérieure à 5 000 est désormais refusée dans le frontend avant l’appel backend. + +La clôture de `0.4.2` n’active pas Mainnet et n’étend pas l’UI aux opérations administratives. La suite active reprend avec `0.4.3 — SPL Memo v1, v3 et v4`. Le projet d’acquisition historique gratuite est volontairement retiré du ROADMAP actif et conservé séparément dans `docs/HISTORICAL_DATA_ACQUISITION_PLAN.md`, sans version attribuée. + +### Politique commune de tous les programmes Solana + +Pour chaque programme Solana, natif, SPL, DEX, launchpad ou autre, l'ordre obligatoire est : décodeur maximal et corpus, décision/ajout des matérialisateurs concernés, puis exécuteur limité aux opérations explicitement supportées. Le décodeur conserve tout wire identifiable, historique, courant, récent ou expérimental. Les matérialisateurs projettent maximalement les faits stables sans dupliquer le core ni une autre projection. L'exécuteur exclut les opérations obsolètes ou purement historiques, mais peut construire les opérations courantes ou expérimentales officiellement encodées indépendamment de leur déploiement par cluster. Une absence de matérialisation ou d'exécution doit être une décision documentée, jamais un oubli. Chaque exécuteur est validé après le décodeur, avec builder officiel, simulation/dry-run, politiques d'autorisation et tests devnet avant mainnet. + +#### Audit rétroactif des programmes déjà clôturés + +- [x] Solana natif : le décodeur conserve les wires historiques ; les absences de projection des précompiles et preuves ZK sont documentées ; BPF Loader v1/v2, ancien ZK Token Proof et Stake Redelegate restent `decode-only`, tandis que l'exécuteur couvre les opérations officiellement appelables. +- [x] SPL Memo : le décodeur v1/v3/v4 et la matérialisation des annotations commitées respectent la politique maximale sans doublon. +- [x] Corriger le contrat de `kb_executor_spl_memo` : v1 et v3 historiques deviennent `Unsupported(reason)`/`decode-only`; v4 reste la seule génération courante constructible et exécutée par la démo. +- [ ] Valider par Cargo la matrice, les capacités compilées et la régression Memo/exécution avant de commencer le code des matérialisateurs SPL Token. + +### 0.4.3 — SPL Memo v1, v3 et v4 + +- [x] Réserver `kb_decoder_spl_memo` et `kb_executor_spl_memo` pour les trois générations sans produire de faux événement ni activer d’envoi. +- [x] Suivre `prompts/022_v0_4_3_spl_memo.md` et auditer les contrats officiels/historiques exacts des trois program IDs ; `docs/SPL_MEMO_MATRIX.json` prouve notamment que v1 ignore les comptes alors que v3/v4 exigent chaque compte comme signer. +- [x] Implémenter le décodeur maximal synthétique : génération, payload UTF-8 ou tentative invalide, longueur/hash, comptes signataires ordonnés, outer/inner, transaction réussie ou échouée et couverture machine-readable ; compilation, 9 tests et Clippy validés après `pre.001-delta-fix-001`. +- [x] Matérialiser les annotations Memo commitées dans un domaine dédié, sans détourner une projection métier incompatible ni produire de mutation réussie pour une transaction échouée ; `pre.002` validé par les tests ciblés, le store PostgreSQL réel et Clippy. +- [x] Remplacer l’exécuteur réservé par un contrat typé exact pour les générations réellement constructibles, avec builders/layouts officiels, signataires déclarés, simulation/dry-run et validation post-exécution Devnet. +- [x] Clôturer le jalon avec corpus synthétique et réel, replay idempotent, tests ciblés, PostgreSQL réel si le store est touché, Clippy et documentation des trois crates concernées. + +#### `0.4.3-pre.001` — audit et décodeur Memo contextualisé + +- [x] Remplacer `DecoderSupport::Maybe` par une reconnaissance exacte des trois IDs et déplacer le target tracing vers `src/constants.rs`. +- [x] Implémenter le wire brut borné, l’UTF‑8, le texte complet, longueur, SHA-256, préfixe diagnostic, comptes ordonnés, signataires et chemin outer/inner. +- [x] Séparer `add_memo`, `memo_intent` et `invalid_memo_attempt`, sans jamais marquer committed une transaction échouée. +- [x] Enregistrer `spl_memo` dans `demo_decode_replay` à côté du décodeur natif. +- [x] Valider la compilation et les tests sur une machine disposant de Cargo : 9 tests Memo, 41 tests config, 89 tests démo et Clippy propre après `delta-fix-001`; le corpus réel reste requis pour la clôture globale du jalon. + +#### `0.4.3-pre.002` — annotations de transaction Memo + +- [x] Créer `kb_materializer_transaction_annotations` et la famille explicite `TransactionAnnotation`, sans détourner les projections existantes. +- [x] Projeter uniquement `add_memo` réussi et commité avec identité, texte, longueur, hash, signataires vérifiés, provenance et clé d’idempotence. +- [x] Refuser les transactions échouées et observations non commitées ; laisser les comptes complets, writable, diagnostics et preuves uniquement dans le decode. +- [x] Réutiliser `kb_sol_mat_events` et le ledger commun sans migration SQL ni duplication core/decode. +- [x] Enregistrer le matérialiseur dans `demo_decode_replay` et déclarer son target tracing dans les trois profils. +- [x] Valider les tests ciblés, Clippy et PostgreSQL réel : modèle 23, Memo 9, matérialiseur 7, API matérialiseur 2, pipeline 56, config 41, démo 89, store PostgreSQL 45 et Clippy propre. +- [x] Valider `pre.002-delta-fix-001` côté compilation et persistance : store core 40, Memo 9, pipeline 57, démo 93 et store PostgreSQL réel 46 ; corriger dans `delta-fix-002` l’assertion de matrice du matérialiseur, la garde de mutex signalée par Clippy et la limite de 64 filtres qui empêchait le démarrage Tauri avec 65 routes. +- [x] Valider le parcours opérateur Mainnet de `pre.002-delta-fix-002` : trois backfills de 100 transactions, core extraction 300/300, replay v1 102/102 avec 69 annotations et 33 refus, v3 439/439 avec 422 annotations et 17 refus, v4 100/100 avec 100 annotations, zéro unmatched ou failed input ; second replay v3 non forcé sélectionnant zéro entrée. +- [x] Valider le démarrage Tauri avec 65 routes de logging sans dépassement de la limite de filtres ni erreur opérationnelle. +- [x] Valider le test logging de `pre.002-delta-fix-003` : 17 tests réussis, y compris la composition de plus de 64 routes ; Clippy a encore identifié la durée lexicale d’un `MutexGuard` dans le test pipeline malgré son `drop` explicite. +- [x] Valider `pre.002-delta-fix-004` : pipeline 57 tests et `cargo clippy --all-targets` propres après fermeture lexicale du `MutexGuard`. + +#### `0.4.3-pre.003` — contrat typé et wire de l’exécuteur Memo + +- [x] Remplacer le plan réservé par `SplMemoGeneration`, `SplMemoOperation::AddMemo`, `SplMemoExecutionIntent` et `SplMemoSigner`. +- [x] À la clôture initiale de `0.4.3`, construire v1/v3/v4 avec `spl_memo_interface::instruction::build_memo`; cette capacité historique est ensuite remplacée par l'audit transversal de `0.4.4`, qui conserve uniquement v4 exécutable. +- [x] Conserver les doublons dans les metas tout en dédupliquant les signataires transactionnels requis. +- [x] Imposer simulation, plafond de frais, dépense nulle hors frais et validation post-exécution complète sans lier la bibliothèque à un cluster. +- [x] Valider alors la séparation cluster/simulation/politique commune ; l'audit transversal ultérieur classe v1/v3 `decode-only` sans modifier le caractère universel du builder v4. +- [x] Valider `pre.003` : exécuteur Memo 14, API 22, safety 15, Solana 12 et `cargo clippy --all-targets` propres. +- [x] Valider `pre.003-delta-fix-001` après retrait des restrictions réseau placées à tort dans la bibliothèque universelle : exécuteur 15 tests et Clippy propre. +- [x] Ajouter un parcours opérateur Devnet v4 réutilisant simulation, confirmation, hydratation, core extraction, decode replay, projection et idempotence. +- [x] Réutiliser le journal générique des matérialisations et la fenêtre d’exécution Devnet pour Memo v4 avec DTO Tauri JSON sans `bigint` actif. + +#### `0.4.3-pre.004` — orchestration backend Memo v4 Devnet + +- [x] Ajouter un request simulation-first, un plan Memo v4 à dépense nulle hors frais et une autorisation d’envoi explicite. +- [x] Réutiliser le wallet persistant, la vérification du genesis Devnet, le blockhash, les frais, la simulation liée au message, la signature et la confirmation communs. +- [x] Hydrater la signature exacte, extraire le core, rejouer `spl_memo`, vérifier la projection `transaction_annotation` puis exécuter un second replay non forcé. +- [x] Valider `pre.004-delta-fix-001` : pipeline 61, exécuteur Memo 15, API 22, safety 15, Solana 12 et Clippy propres ; test opt-in Devnet réussi en simulation puis avec envoi. + +#### `0.4.3-pre.005` — démonstration Tauri Memo v4 Devnet + +- [x] Étendre la fenêtre Solana Devnet existante sans créer une nouvelle WebView ni déplacer de logique métier dans l’application. +- [x] Exposer message UTF-8, signer wallet readonly, simulation, confirmation opérateur et diagnostic complet sans clé privée ni `bigint` Tauri. +- [x] Afficher confirmation, validation canonique/core/decode, nombre d’annotations et preuve d’idempotence. +- [x] Valider 96 tests démo, 61 tests pipeline, Clippy propre et démarrage Tauri/Vite. +- [x] Exécuter trois Memo v4 Devnet réels : trois confirmations, trois insertions canoniques, trois extractions, trois annotations et trois seconds replays `skipped`, sans échec ni nouvelle matérialisation. + +#### `0.4.3-pre.006` — consolidation documentaire et corpus Devnet + +- [x] Reporter dans `docs/SPL_MEMO_MATRIX.json` les trois signatures v4 Devnet confirmées, leurs annotations de 34 octets et leurs seconds replays idempotents. +- [x] Synchroniser les README du décodeur, du matérialiseur, de l’exécuteur, du pipeline et de la démo avec les limites réellement validées. +- [x] Conserver comme non-revendications explicites l’envoi v1/v3 sur Devnet, le déploiement Localnet des binaires historiques et les coûts hors simulation/plafond de frais. + +#### `0.4.3-pre.007` — clôture et prompt SPL Token + +- [x] Enregistrer les validations finales fournies par l’utilisateur, y compris PostgreSQL réel, le parcours Devnet opt-in avec envoi et Clippy propre. +- [x] Clôturer `0.4.3` dans le ROADMAP et le `CHANGELOG.md` sans modifier le code ni le schéma PostgreSQL. +- [x] Ajouter `prompts/023_v0_4_4_spl_token.md` et rendre `0.4.4 — SPL Token` actif. + +#### Validation finale observée — 15 juillet 2026 + +- [x] `kb_program_ids` 5 tests, `kb_decoder_spl_memo` 9, `kb_materializer_transaction_annotations` 7 et `kb_executor_spl_memo` 15. +- [x] `kb_execution_api` 22 tests, `kb_execution_safety` 15 et `kb_execution_solana` 12. +- [x] `kb_pipeline` 61 tests, `kb_rpc` 113 et `kb_app_demo` 96. +- [x] `kb_store_pg` 46 tests avec PostgreSQL réel, migrations et requêtes de matérialisation typées réussies. +- [x] Test opt-in Devnet exécuté avec `KB_DEVNET_MEMO_EXECUTION_TEST=1` et `KB_DEVNET_MEMO_SUBMIT=1` : simulation, envoi, confirmation, hydratation, extraction, replay, annotation et idempotence réussis. +- [x] `cargo clippy --all-targets` sans avertissement. +- [x] Démo Tauri/Vite validée avec simulation Memo et trois envois v4 Devnet réels ; chaque transaction a produit une annotation et un second replay `skipped`. + +`0.4.3` est clôturé. La suite active devient `0.4.4 — SPL Token` selon `prompts/023_v0_4_4_spl_token.md`. Le worker d’acquisition historique reste différé et hors ROADMAP actif. + +### 0.4.4 — SPL Token + +- [x] `0.4.4-pre.001` — auditer les 28 tags publiés par `spl-token-interface 3.0.0` et implémenter le décodeur contextualisé classique : wire borné, comptes, autorités, multisig, inner instructions, transactions échouées, return data explicitement indisponible et `Batch` non imbriqué. +- [x] Valider `pre.001-delta-fix-001` le 15 juillet 2026 : 7 tests `kb_decoder_spl_token` réussis et `cargo clippy --all-targets` propre ; le correctif conserve le manifeste parent `Batch`, aligne la fixture multisig et autorise les listes explicites de tags via `workspace.lints.clippy.manual_range_patterns`. + +#### `0.4.4-pre.002` — contrat documentaire et politique d'exécution + +- [x] Rendre normative la couverture historique/courante/récente/expérimentale des décodeurs, la matérialisation maximale sans doublons et l'exclusion des opérations obsolètes dans les exécuteurs. +- [x] Détailler les responsabilités `token_accounts`, `admin`, `risk` et `fees`, ainsi que le futur journal métier et la démo opérateur. +- [x] Corriger la matrice et le test Memo : v1/v3 decode-only, v4 exécutable sur tout cluster sous gouvernance commune ; 16 tests Memo et Clippy global validés après `fix-002`. + +#### `0.4.4-pre.003` — matérialisations SPL Token classiques + +- [x] Activer `kb_materializer_token_accounts` pour les initialisations de mint, compte et multisig, transferts, approvals/revocations, mint, burn, freeze/thaw, close, sync native et unwrap, uniquement lorsqu'ils sont réussis et commités. +- [x] Produire un journal de mutations instructionnelles avec signature, slot, chemin stable, opération, comptes, mint explicitement connu ou `null`, montant brut en chaîne, decimals wire éventuels, autorité, provenance et clé d'idempotence ; ne pas prétendre reconstruire le snapshot final du compte. +- [x] Étendre `kb_materializer_admin` sans régresser ses projections natives, pour `set_authority` et les configurations multisig Token clairement administratives. +- [x] Activer `kb_materializer_risk` pour des constats factuels : délégation/allowance, revoke, freeze/thaw, révocation d'autorité et configuration multisig structurellement faible ; ne produire aucun score arbitraire. +- [x] Conserver `kb_materializer_fees` inactif pour SPL Token classique et tester/documenter explicitement `no projection`, car ni les transfer fees Token-2022 ni les frais réseau core n'appartiennent à cette surface. +- [x] Dédupliquer les sorties parent/enfants de `Batch` par leur chemin dérivé et leur `output_key`, refuser toute observation non commitée et réutiliser `kb_sol_mat_events` avec le ledger commun. +- [x] Enregistrer les matérialisateurs actifs dans le replay commun et valider leur idempotence, sans migration PostgreSQL si le contrat générique suffit. +- [x] Valider `pre.003` le 15 juillet 2026 : tests des quatre matérialiseurs, pipeline, config et démo réussis, Clippy global propre ; replay Mainnet de 370 instructions, 371 sorties, huit refus correspondant exactement à huit transactions échouées, puis second replay idempotent avec zéro sélection. + +#### `0.4.4-pre.004` — contrat typé et builders de `kb_executor_spl_token` + +- [x] Remplacer le scaffold par des intents typés pour les 24 opérations courantes et expérimentales officiellement constructibles ; classer les quatre initialisations historiques obsolètes en `Unsupported(reason)` au lieu de les construire. +- [x] Appeler les builders officiels 3.0.0, préserver l'ordre des metas et séparer autorité simple, multisig, signataires transactionnels, montants bruts en chaînes, decimals, rent, SOL transféré et frais réseau. +- [x] Construire `UnwrapLamports` et `Batch` après preuve exacte de leurs signatures et layouts officiels ; séparer explicitement constructibilité, déploiement par cluster et autorisation d'envoi. +- [x] Imposer simulation, dry-run par défaut, plafond de frais et validation post-exécution, sans dépendance vers RPC, wallet, Tauri ou le décodeur. +- [x] Valider `pre.004-delta-fix-001` le 15 juillet 2026 : 15 tests `kb_executor_spl_token`, 22 tests API, 15 tests safety, 12 tests Solana, 61 tests pipeline et `cargo clippy --all-targets` propres. + +#### `0.4.4-pre.005` — préflight stateful et orchestration + +- [x] Ajouter dans le pipeline les lectures bornées des comptes Mint/Account/Multisig nécessaires aux préconditions : owner, mint, decimals, état, solde, native reserve, delegate/allowance, autorités, seuil M/N et rent des comptes à initialiser. +- [x] Séparer le test réseau du test standard : layouts et bornes restent offline ; `KB_DEVNET_SPL_TOKEN_PREFLIGHT_TEST=1` active un contrôle réel de `TransferChecked` sur des comptes Devnet explicitement fournis, sans signature ni dépense. +- [x] Valider le socle `pre.005` le 15 juillet 2026 : 67 tests `kb_pipeline`, 15 tests `kb_executor_spl_token`, Clippy global propre et préflight `TransferChecked` Devnet réel `Ready` sur deux comptes auxiliaires contrôlés du même mint. +- [x] `pre.005-delta-fix-001` : réutiliser l'orchestration commune pour préflight obligatoire, simulation liée au message exact, signature/envoi autorisé, confirmation, hydratation canonique, extraction core, replay Token, vérification des projections et second replay idempotent. +- [x] Exposer séparément une API de simulation sans store ni signature et refuser l'envoi si les signataires requis ne sont pas tous résolus par le wallet persistant du profil. +- [x] Valider le test opt-in d'orchestration le 15 juillet 2026 sur les comptes Devnet contrôlés : simulation seule puis envoi réel de `TransferChecked`, avec wallet isolé explicitement aligné sur l'autorité Token, confirmation, hydratation canonique, extraction core, replay et matérialisation réussis ; le contrat renforcé a produit la signature `5Q71KeomfhHBueDsD92C7L21vHUpFGZnZzCFPGaCDAMHx6yV4Jy2fMDzCpze8mNr2c1GLSRT93mmBaYy2RGZ95ZY`, une matérialisation et un second replay sans échec, refus ni nouvelle sortie. + +#### `0.4.4-pre.006` — lifecycle contrôlé Devnet sans ATA + +- [x] Ajouter un orchestrateur séquentiel pour des comptes Mint/Account rent-exempt, non initialisés et possédés par le programme Token classique, sans création ATA implicite. +- [x] Enchaîner onze transactions simulation-first : `InitializeMint2`, deux `InitializeAccount3`, `MintToChecked`, `TransferChecked`, `ApproveChecked`, `Revoke`, deux `BurnChecked` puis deux `CloseAccount`. +- [x] Exiger la même autorité simple résolue par le wallet de profil, une confirmation destructive explicite, la conservation exacte des montants bruts et une validation canonique/idempotente complète avant de passer à l'étape suivante. +- [x] `pre.006-delta-fix-001` : remplacer la dépendance à l'ancienne commande CLI `solana create-account` par une préparation interne qui génère trois keypairs éphémères, mesure le rent, construit et simule trois `SystemCreateAccount`, exige les deux signatures, confirme puis vérifie owner Token, tailles 82/165/165 et données entièrement nulles. +- [x] `pre.006-delta-fix-002` : rendre une séquence partielle reprenable depuis un index explicite seulement après récupération canonique de la signature prédécesseur, correspondance de l'opération matérialisée, second replay idempotent, puis pacing borné et émission immédiate des signatures suivantes. +- [x] Valider le lifecycle réel Devnet le 15 juillet 2026 : préparation interne des comptes 82/165/165, reprise contrôlée après throttling du RPC public, puis `MintToChecked`, `TransferChecked`, `ApproveChecked`, `Revoke`, deux `BurnChecked` et deux `CloseAccount` confirmés ; chaque étape récupérée ou envoyée produit exactement une matérialisation et un second replay idempotent. Le scénario multisig reste candidat pour l'audit final si son coût demeure raisonnable. + +#### `0.4.4-pre.007` — journal métier et démo opérateur + +- [x] Étendre la fenêtre d'exécution Devnet existante, sans nouvelle WebView par opération, avec un panneau Token représentatif `TransferChecked`, simulation par défaut et confirmation explicite avant envoi ; le lifecycle complet reste exercé par son scénario opt-in dédié. +- [x] Conserver une seule fenêtre d'exécution : le panneau Token réutilise le profil, le wallet, le verrou d'exécution, la simulation et la confirmation déjà communs aux parcours System et Memo. +- [x] Ajouter une vue bornée du journal `kb_sol_mat_events`, filtrable par signature, mint, compte, famille et opération, avec détail JSON typé et montants bruts sans perte. +- [x] Visualiser les initialisations, transferts, délégations, mint/burn, freeze/thaw, changements d'autorité, close et opérations wrapped SOL comme événements chronologiques ; ne pas afficher d'OHLC ni de faux solde final. +- [x] Afficher séparément résultat de préflight, simulation, confirmation, extraction, décodage, matérialisations attendues et preuve d'idempotence. +- [x] Valider `pre.007` le 15 juillet 2026 : 102 tests `kb_app_demo`, 80 tests pipeline, 15 tests exécuteur Token et Clippy global propres ; `cargo tauri dev` a démarré Vite et Tauri, chargé le journal borné et simulé `TransferChecked`. Le refus sans confirmation puis le refus d'un envoi exigeant une autorité externe au wallet de profil prouvent les deux garde-fous attendus, sans invalider le parcours backend Devnet déjà envoyé et matérialisé. + +#### `0.4.4-pre.008` — corpus, audit cluster et clôture + +- [x] Documenter dans la matrice les signatures Mainnet représentatives et le lifecycle Devnet, avec statut séparé pour interface, processor, builder, simulation, soumission et déploiement ; conserver Localnet non testé et `UnwrapLamports`/`Batch` sans revendication de déploiement cluster. +- [x] `pre.008-delta-fix-001` : ajouter le probe Devnet simulation-only de `Batch` et `UnwrapLamports` ; valider 81 tests pipeline et Clippy global propre le 16 juillet 2026. +- [x] `pre.008-delta-fix-002` : enregistrer les deux simulations Devnet réussies, sans signature ni envoi : `Batch` avec un enfant `TransferChecked` de montant nul en 270 compute units et `UnwrapLamports` d'un lamport depuis un compte wrapped SOL auxiliaire classique en 140 compute units, sans erreur runtime. Devnet expose donc les deux tags récents et aucun binaire Localnet p-token n'est requis pour pallier une absence de déploiement. +- [x] `pre.008-delta-fix-003` : aligner le test de non-invention cluster sur la preuve Devnet réelle tout en exigeant que Localnet/Mainnet restent non testés et que les signatures réelles restent vides pour les deux simulations sans envoi ; validation ciblée 8/8 et Clippy global propre le 16 juillet 2026. +- [x] `pre.008-delta-fix-004` : aligner les assertions Memo sur `matrixVersion = 4` et compléter, dans les trois profils exemple, les routes console et fichier de `kb_executor_spl_token` ; validation 9/9 Memo, 7/7 annotations, 41/41 configuration, 102/102 démo et Clippy global propre le 16 juillet 2026. +- [x] Exécuter la régression ciblée, PostgreSQL réel, Clippy global et Tauri/Vite : 77 routes chargées, simulation `TransferChecked` réussie, refus sans confirmation vérifié, matérialisation commitée exacte consultée et simulation Memo v4 réussie ; synchroniser README/CHANGELOG et clôturer `0.4.4` avant ATA. +- [x] `pre.008-delta-fix-005` : publier la clôture documentaire `0.4.4`, activer `0.4.5 — SPL Associated Token Account` et fournir `prompts/024_v0_4_5_spl_associated_token_account.md` sans commencer son implémentation. + +### 0.4.5 — SPL Associated Token Account + +Règle de couverture du jalon : décoder maximalement toute variante officiellement identifiable, qu'elle soit historique, courante ou expérimentale ; matérialiser maximalement chaque fait stable et prouvé sans chevauchement entre propriétaires ; exécuter toute variante actuelle officiellement constructible, en excluant seulement les opérations historiques, obsolètes ou qui ne sont plus d'actualité. Un remaniement reste autorisé lorsqu'il est nécessaire pour respecter ces contrats, à condition d'être borné, justifié, documenté et couvert par les régressions. + +- [x] `pre.001` : auditer `spl-associated-token-account-interface 2.0.0`, les trois variantes Borsh publiées, les builders officiels, l'ordre et les flags des comptes, la dérivation PDA `[wallet, Token Program ID, mint]` et le processor de référence `program@v8.0.0` ; publier `docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json` et quatre tests d'égalité interface/matrice/builders/PDA. Résolution Cargo `2.0.0`, 4/4 tests et Clippy global validés le 16 juillet 2026 ; version workspace corrigée à `0.4.5` ; tests de matrice réintégrés dans `decoder.rs` pour conserver l'organisation homogène des décodeurs existants. Corpus canonique et égalité des binaires cluster encore non revendiqués. +- [x] `pre.002` : remplacer le scaffold par le décodeur ATA exact pour toutes les variantes officiellement identifiables de l'interface et du processor résolus, actuellement `Create`, `CreateIdempotent` et `RecoverNested` ; couvrir wire Borsh et `Create` historique vide, comptes ordonnés, flags, doublons, dérivations PDA, Token classique/Token-2022, outer/CPI, succès/échec, données inconnues/tronquées/suffixées et diagnostics bornés ; compléter le corpus offline et l'égalité matrice/couverture compilée sans commencer l'exécuteur. Validation opérateur du 16 juillet 2026 : ATA 10/10, configuration 41/41, pipeline 81/81, application 102/102 et Clippy global propre après `pre.002-delta-fix-001`. +- [x] `pre.003` : matérialiser maximalement les faits ATA commités avec propriétaires explicites : étendre `kb_materializer_token_accounts` pour `ata_created`, `ata_created_or_reused_idempotently` et `nested_ata_recovered`, puis étendre `kb_materializer_risk` pour le fait distinct `nested_ata_anti_pattern_recovered` sans score arbitraire. Documenter et tester l'absence de projection dans `kb_materializer_lifecycle` afin d'éviter le doublon de lifecycle, dans `kb_materializer_admin` car aucune autorité n'est modifiée et dans `kb_materializer_fees` car le wire ATA ne prouve aucun montant exact de rent ou de frais. Validation opérateur du 16 juillet 2026 : token_accounts 9/9, risk 6/6, lifecycle 17/17, admin 9/9, fees 3/3, décodeur ATA 10/10, application 103/103, pipeline 81/81 et Clippy global propre. Le replay PostgreSQL réel reste réservé à la validation cluster/finale. +- [x] `pre.004` : remplacer le scaffold exécuteur par les trois variantes actuelles officiellement constructibles `Create`, `CreateIdempotent` et `RecoverNested` ; comparer chaque plan au builder officiel et ajouter le préflight stateful pour dérivation, mint, compte existant, RecoverNested, rent, plafonds et signataires. `pre.004-delta-fix-001` ajoute les types `solana_pubkey::Pubkey` explicites après l'échec d'inférence `E0282`. Validation opérateur du 16 juillet 2026 : exécuteur ATA 10/10, pipeline 84/84 et Clippy global propre. +- [x] `pre.005` : valider l'orchestration Devnet simulation-first, la post-validation stateful adaptée aux créations et à RecoverNested, la reprise bornée après `429`, la confirmation, l'extraction, le replay ATA et CPI Token classique, la matérialisation et le second replay idempotent ; intégrer Create/CreateIdempotent et le journal lifecycle borné dans la fenêtre Solana existante. Validation opérateur du 16 juillet 2026 : décodeur ATA 10/10, exécuteur ATA 10/10, pipeline 90/90, configuration 41/41, application 111/111 et Clippy global propre ; `cargo tauri dev -c kb_app_demo/tauri.conf.json` démarre Vite, Tauri, les 83 routes de journalisation et le schéma PostgreSQL réel. Deux soumissions Devnet contrôlées `CreateIdempotent` classiques ont été simulées, confirmées, extraites, décodées et matérialisées exactement une fois, puis leur second replay n'a produit aucun doublon. Le mint Token-2022 contrôlé `3DxK…KTE` a ensuite créé puis réutilisé l'ATA `6WfC…C9Y` aux slots 476702448 et 476703343 ; les deux signatures ont réussi le parcours complet et leur second replay sans doublon. L'ATA indépendant mesure 170 octets, appartient à Token-2022, conserve le wallet/mint attendus et expose `ImmutableOwner`. +- [x] `pre.006` : valider le test opt-in Devnet `RecoverNested` hors UI avec fixture classique contrôlée, préflight des trois PDA, simulation, transfert de 1 000 000 000 unités brutes, fermeture du nested ATA, conservation de l'owner ATA, projections lifecycle/risk distinctes, deux matérialisations exactes et second replay idempotent sous la signature `37LG…HskM`. Les suites pipeline 90/90, décodeur ATA 10/10, exécuteur ATA 10/10 et Clippy global restent propres après `pre.006-delta-fix-001`. +- [x] `pre.006-delta-fix-003` : clôturer `0.4.5` après la régression complète. Validation opérateur du 16 juillet 2026 : Program IDs 5/5, Token classique decode 8/8 et exécuteur 15/15, Memo 9/9, annotations 7/7, token accounts 9/9, risk 6/6, lifecycle 17/17, admin 9/9, fees 3/3, API d'exécution 22/22, safety 15/15, transaction Solana 12/12, pipeline 90/90, RPC 113/113, configuration 41/41, application 111/111 et PostgreSQL réel 46/46 ; Clippy global propre. Les manifests Cargo héritent tous de `workspace.package.version = 0.4.5`, l'interface ATA résolue reste `2.0.0`, et les versions npm/Tauri de la démo sont synchronisées en `0.4.5`. README, matrice, dépendances d'interface, CHANGELOG et prompt `0.4.6` sont publiés seulement après ces preuves. +- [x] Décoder maximalement `Create`, `CreateIdempotent`, `RecoverNested` et la forme historique vide officiellement identifiable. +- [x] Couvrir les ATA ciblant SPL Token et Token-2022 sans commencer le décodage général Token-2022. +- [x] Matérialiser maximalement le lifecycle ATA et le fait de risque nested sans dupliquer les changements Token, puis développer l'exécuteur maximal des opérations actuelles après validation des projections. + +### 0.4.6 — Token-2022, extensions et registre ElGamal — clôturé + +`0.4.6` clôt la couverture Token-2022 et le registre ElGamal comme deux frontières techniques distinctes. Token-2022 conserve son Program ID, sa matrice, ses événements, ses états TLV, ses matérialisateurs et ses capacités d’exécution propres. Le registre ElGamal conserve séparément son Program ID, son état de 64 octets, son PDA, ses opérations administratives et ses preuves. Le programme natif ZK ElGamal Proof reste une troisième surface indépendante. + +- [x] Résoudre et documenter les interfaces réellement utilisées : `spl-token-2022-interface 3.1.1`, `spl-elgamal-registry-interface 0.2.1`, `solana-zk-elgamal-proof-interface 0.1.3`, Group `0.7.2`, Metadata `1.0.1` et TLV `0.9.1`. +- [x] Publier `docs/SPL_TOKEN_2022_MATRIX.json` et `docs/SPL_ELGAMAL_REGISTRY_MATRIX.json` avec frontières, wire, comptes, états, capacités et preuves séparés. +- [x] Décoder les instructions Token-2022 de base, les familles d’extensions identifiables, Token Metadata/Group incorporés, les parcours confidentiels et les instructions outer/CPI, sans fusionner les implémentations tierces. +- [x] Parser les préfixes Mint, Account et Multisig, le discriminant de type de compte et la région TLV bornée, y compris metadata variable, groupes et extensions publiques/confidentielles sans inventer de valeur déchiffrée. +- [x] Décoder et parser séparément le registre ElGamal, avec `create_registry`, `update_registry`, PDA officiel, owner, taille et état de 64 octets. +- [x] Attribuer chaque fait stable à un matérialiseur propriétaire unique : token accounts, metadata, admin, fees, risk/compliance ou annotations ; préserver la provenance et refuser les transactions échouées. +- [x] Exposer les exécuteurs typés Token-2022 et ElGamal Registry avec simulation obligatoire, dry-run par défaut, signataires exacts, préflights stateful, preuves inline/context-state, plafonds et postconditions explicites. +- [x] Valider sur Devnet huit opérations publiques Token-2022 : `MintToChecked`, `TransferChecked`, `ApproveChecked`, `Revoke`, `BurnChecked`, `FreezeAccount`, `ThawAccount` et `CloseAccount`, avec hydratation, extraction, replay, matérialisation et second replay idempotent. +- [x] Valider ElGamal Registry synthétiquement/offline : builders, exact-wire, PDA, état, lectures RPC bornées, preuves et orchestration. La validation réseau publique reste explicitement indisponible sur Devnet/Mainnet au 20 juillet 2026 ; aucune preuve cluster n’est inventée. +- [x] Valider le corpus Mainnet/PostgreSQL : 55 instructions Token-2022 décodées sans échec, aucune duplication decode/mat, et correction du wire `Write` du loader immuable sur 68 instructions Mainnet. +- [x] Qualifier les rejets natifs restants : 31 `immutable_loader_tag_truncated`, 44 `loader_v4_tag_truncated` et 1 `loader_accounts_invalid`, tous dans des transactions Solana échouées ; quatre variantes Loader v4 inconnues restent explicitement `Unsupported`. +- [x] Ajouter le mode `incomplete_signatures` dans le replay de `kb_app_demo` : sélection des signatures contenant au moins une instruction `pending`, `failed`, `replay_requested` ou `unsupported`, limite appliquée aux signatures, expansion aux instructions compatibles et réévaluation sans force replay global. +- [x] Valider deux campagnes PostgreSQL réelles `incomplete_signatures` identiques : 81 entrées sélectionnées, 1 décodée, 4 unsupported, 76 failed, aucun unmatched, aucun not-started et aucune duplication de matérialisation. +- [x] Valider la régression finale : `kb_store_core` 41/41, `kb_store_pg` 46/46 avec PostgreSQL réel, `kb_pipeline` 124/124, `kb_app_demo` 118/118 et `cargo clippy --all-targets` propre. +- [x] Publier le bilan final dans `docs/V0_4_6_VALIDATION.md` et préparer `prompts/026_v0_4_7_metaplex_token_metadata.md`. + +#### Modèle de couverture des tokens et actifs numériques + +- [x] Les tokens fongibles et les NFT basés sur un mint restent décodés au niveau des mouvements par `kb_decoder_spl_token` ou `kb_decoder_spl_token_2022`; il n’existe pas d’instruction Token distincte nommée « NFT ». +- [x] La classification fongible/NFT/SFT est une projection dérivée de la supply, des decimals, des authorities et des standards metadata observés; elle ne doit jamais remplacer l’adresse canonique du mint ou de l’actif. +- [x] Les NFT classiques et programmables liés à un mint sont couverts par le futur `kb_decoder_metadata_metaplex_token_metadata` (`0.4.7`). +- [x] Les actifs Metaplex Core, qui ne sont pas des mints SPL Token classiques, sont couverts séparément en `0.4.8`. +- [x] Les NFT compressés exigent Bubblegum en plus d’Account Compression et Noop; leur couverture complète est planifiée en `0.4.9`. +- [x] Les metadata Token-2022 incorporées, Metaplex externes et implémentations tierces de Token Metadata Interface conservent leur provenance et ne sont jamais fusionnées silencieusement. + +### 0.4.7 — Metaplex Token Metadata + +Ce jalon est prioritaire immédiatement après Token-2022. Metaplex Token Metadata n'est pas un programme SPL officiel : il s'agit d'un programme indépendant de la Metaplex Foundation, utilisé comme standard de metadata externe pour SPL Token classique et Token-2022. Son Program ID canonique est `metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s` et doit être revérifié depuis les sources officielles au début du jalon. + +### Audit et dépendances + +- [x] Lire `RULES.md`, les contrats decode/materialization/execution/replay, les matrices SPL Token/Token-2022, `docs/SOLANA_INTERFACE_DEPENDENCIES.md` et le prompt `026` avant tout code. +- [x] Ouvrir l’audit des crates et sources officielles Metaplex réellement disponibles : SDK Rust `mpl-token-metadata ^5.1`, Program ID, IDL `1.14.0`, inventaire généré des instructions et séparation maintenu/historique. Les comptes, builders et PDA détaillés restent à prouver par variante. +- [x] Créer `docs/METAPLEX_TOKEN_METADATA_MATRIX.json` comme matrice d’audit machine-readable avant le décodage maximal : sources officielles figées, inventaire SDK courant, frontière historique IDL, familles de PDA/comptes et statuts de preuve. L’enrichissement exhaustif par instruction et compte reste requis avant l’activation runtime. +- [x] Ne pas introduire Anchor dans ce jalon : Token Metadata utilise ses propres layouts Borsh et interfaces ; l'infrastructure Anchor reste en `0.4.18`, juste avant les protocoles qui en dépendent réellement. +- [ ] Conserver `wincode ^0.5` tant que les crates Solana utilisées exposent leurs implémentations `SchemaRead`/`SchemaWrite` contre `wincode 0.5.x`; ne pas migrer isolément le workspace vers `0.6`. + +### Journal de préversions + +- `0.4.7-pre.001` : ouverture de l’audit officiel, ajout de `mpl-token-metadata ^5.1`, création de la matrice initiale et des squelettes `kb_decoder_metadata_metaplex_token_metadata` et `kb_executor_metadata_metaplex_token_metadata`; aucun décodeur runtime ni builder/exécuteur actif. +- `0.4.7-pre.002` : activer le dispatcher contextualisé et le décodage Borsh exact du premier groupe création/mise à jour (`CreateMetadataAccountV3`, `UpdateMetadataAccountV2`), avec comptes ordonnés, outer/CPI, transactions échouées non committées, bornes et diagnostics fail-closed; garder l’enregistrement runtime, la matérialisation et l’exécution pour les groupes suivants. +- `0.4.7-pre.003` : ajouter le groupe de vérification de collection (`VerifyCollection`, `UnverifyCollection`, `SetAndVerifyCollection`) avec discriminateurs officiels, comptes ordonnés, autorité de collection, payer/update authority, `collection_authority_record` optionnel, outer/CPI, échecs non committés et diagnostics fail-closed; conserver matérialisation, exécution et registre runtime pour des préversions dédiées. +- `0.4.7-pre.004` : décodage du groupe sized collection item (`VerifySizedCollectionItem`, `UnverifySizedCollectionItem`, `SetAndVerifySizedCollectionItem`). +- `0.4.7-pre.005` : décoder l’administration des autorités de collection (`ApproveCollectionAuthority`, `RevokeCollectionAuthority`) avec records PDA, autorités distinctes, comptes système/rent et contrats signer/writable exacts. +- `0.4.7-pre.006` : décoder les usages et autorités d’usage (`ApproveUseAuthority`, `RevokeUseAuthority`, `Utilize`) avec compteurs `u64`, `use_authority_record`, burner, comptes Token/ATA, comptes système/rent, outer/CPI et échecs non committés. +- `0.4.7-pre.007` : décoder la signature de creator et la déclaration de première vente (`SignMetadata`, `UpdatePrimarySaleHappenedViaToken`) avec comptes ordonnés, signer/writable exacts, outer/CPI, échecs non committés et diagnostics fail-closed. +- `0.4.7-pre.008` : décoder la normalisation et la classification metadata (`PuffMetadata`, `RemoveCreatorVerification`, `SetTokenStandard`) avec edition optionnelle, comptes ordonnés et diagnostics fail-closed. +- `0.4.7-pre.009` : décoder `CreateMetadataAccountV2` historique (discriminant `16`) avec `DataV2`, mutabilité, comptes exacts et politique non exécutable. +- `0.4.7-pre.010` : décoder `CreateMetadataAccount` et `UpdateMetadataAccount` historiques (discriminants `0` et `1`) avec layouts `Data` V1 bornés, comptes exacts et politique non exécutable. +- `0.4.7-pre.011` : décoder `CreateMasterEditionV3` et la conversion historique `ConvertMasterEditionV1ToV2`, avec `max_supply`, comptes exacts et politique d’exécution distincte. +- `0.4.7-pre.012` : décoder le mint d’éditions imprimées via token et la variante historique vault proxy (`MintNewEditionFromMasterEditionViaToken`, `MintNewEditionFromMasterEditionViaVaultProxy`), avec numéro d’édition `u64`, edition marker et comptes exacts. +- `0.4.7-pre.013` : décoder `SetCollectionSize` et le bridge `BubblegumSetCollectionSize`, avec taille `u64`, autorités/records exacts et frontière Bubblegum explicite. +- `0.4.7-pre.014` : décoder `Delegate` et `Revoke` programmables, leurs enums Borsh, 14 comptes positionnels et placeholders optionnels. +- `0.4.7-pre.015` : décoder `Transfer`, `Lock`, `Unlock`, `Burn` et `CloseAccounts`, avec enums Borsh, montants `u64`, comptes programmables exacts et transactions échouées non committées. +- `0.4.7-pre.016` : réauditer exhaustivement l’index officiel des instructions Rust avant le décodage des comptes, puis décoder `FreezeDelegatedAccount` et `ThawDelegatedAccount` historiques avec discriminants `26`/`27`, cinq comptes exacts, outer/CPI, échecs non committés et politique decode-only. +- `0.4.7-pre.017` : décoder `BurnNft`, `BurnEditionNft` et `DeprecatedMintNewEditionFromMasterEditionViaPrintingToken` historiques avec discriminateurs `29`/`37`/`3`, comptes exacts, comptes optionnels de fin, outer/CPI, échecs non committés et politique decode-only. +- `0.4.7-pre.018` : décoder `CreateEscrowAccount`, `CloseEscrowAccount`, `TransferOutOfEscrow` et `Collect`, avec montant `u64`, authorities optionnelles, comptes exacts et diagnostics fail-closed. +- `0.4.7-pre.019` : décoder le wrapper moderne `Create` (`42`) avec `CreateArgs::V1`, neuf comptes positionnels et placeholders optionnels exacts. +- `0.4.7-pre.020` : décoder le wrapper moderne `Mint` (`43`) avec `MintArgs::V1`, montant `u64`, `AuthorizationData`, quinze positions et placeholders optionnels exacts. +- `0.4.7-pre.021` : décoder le wrapper moderne `Print` (`55`) avec `PrintArgs::V1`/`V2`, édition `u64`, dix-huit positions et Token Record optionnel exact. +- `0.4.7-pre.022` : décoder le wrapper moderne `Update` (`50`) et toutes les variantes `UpdateArgs` V1/V2, avec onze positions et placeholders optionnels exacts. +- `0.4.7-pre.023` : décoder les wrappers modernes `Use` (`51`), `Verify` (`52`) et `Unverify` (`53`) avec leurs enums Borsh, comptes positionnels, placeholders optionnels, outer/CPI et échecs non committés. +- `0.4.7-pre.024` : décoder `Resize` (`56`), `Migrate` (`48`) et les six discriminateurs historiques manquants `2`/`5`/`6`/`8`/`9`/`10`, puis prouver une couverture exhaustive des 58 discriminateurs `0..=57`. +- `0.4.7-pre.025` : décoder le compte `Metadata`, vérifier owner, discriminant `MetadataV1`, bornes officielles et PDA canonique `[metadata, program_id, mint]`, sans encore enregistrer le décodeur stateful dans le pipeline. +- `0.4.7-pre.026` : décoder `DeprecatedMasterEditionV1`, `MasterEditionV2` et `EditionV1`, vérifier owner, layouts bornés et PDA commun `[metadata, program_id, mint, edition]`, tout en conservant V1 comme état historique decode-only. +- `0.4.7-pre.027` : décoder `EditionMarker` et `EditionMarkerV2`, vérifier owner, bitmaps, groupes de 248 éditions, seeds PDA V1/V2 et longueurs Borsh exactes. +- `0.4.7-pre.028` : décoder `TokenRecord`, vérifier owner, layout fixe de 80 octets, PDA mint/token account, bump stocké, états programmables, delegate, rôle et locked transfer. +- `0.4.7-pre.029` : décoder `MetadataDelegateRecord`, `HolderDelegateRecord`, `CollectionAuthorityRecord` et `UseAuthorityRecord`, avec longueurs, rôles-seeds, PDA et bumps exacts. +- `0.4.7-pre.030` : décoder `TokenOwnedEscrow`, distinguer `TokenOwner`/`Creator`, vérifier les deux familles de seeds PDA, longueurs Borsh et bump stocké, puis documenter les transitions stateful escrow/migrate/resize sans les matérialiser prématurément. +- `0.4.7-pre.031` : décoder `ReservationListV1/V2` historiques, préserver compteurs, supply snapshot, caches et PDA dépendant de `resource`, avec limites officielles et politique decode-only. + +- `0.4.7-pre.032` : inventaire exhaustif des quinze variantes `Key`, liaison de chaque layout à son décodeur, sentinelle `Uninitialized` explicitement rejetée et audit fail-closed des futures variantes upstream. +- `0.4.7-pre.033` : ajouter l’enveloppe canonique des snapshots, l’identité stable `program_id:account`, la version de layout, les classes actif/historique/expérimental et la provenance live/replay/backfill/repair ; rendre explicite que les comptes historiques sont matérialisables mais jamais exécutables. +- `0.4.7-pre.034` : règles décodeur/matérialiseur/exécuteur renforcées ; projection canonique commitée de `MetadataV1`, snapshot owned autoritatif et absence de doublon d’état avec les observations d’instructions. +- `0.4.7-pre.035` : matérialiser les autres comptes Metaplex actifs, expérimentaux et historiques avec identité stable, lifecycle, provenance et propriétaire unique des faits. + +#### Audit canonique des comptes `0.4.7` + +- [x] Auditer les quinze variantes `Key` publiées (`0..=14`) et relier chaque compte réel à un décodeur borné. +- [x] Rejeter explicitement `Key::Uninitialized` et toute variante future inconnue sans projection partielle. +- [x] Vérifier par test les discriminateurs Borsh du SDK épinglé, l’unicité de l’inventaire et sa correspondance avec la matrice. +- [x] Définir l’identité stable canonique `program_id:account`, le nom de layout, le discriminant et la version canonique. +- [x] Distinguer explicitement lifecycle, politique de matérialisation et politique d’exécution. +- [x] Autoriser la matérialisation des comptes historiques comme état historique de backfill/replay, sans jamais les exposer à l’exécuteur. +- [x] Conserver source, slot, write version, signature, index outer/inner et statut committed dans la provenance canonique. + +#### Découpage prévisionnel restant de `0.4.7` + +- `pre.036` : intégration du décodeur de comptes dans `kb_pipeline`, routage owner/discriminant, replay, fermeture de comptes, ordering et déduplication. +- `pre.037` : corrélation instructions/comptes avant-après et vérification stateful des observations décodées. +- `pre.038` : exécuteur moderne de base (`Create`, `Update`, `Mint`, `Print`, `Verify`, `Unverify`, `Use`, `Resize`, `Migrate`) avec builders officiels et simulation-first. +- `pre.039` : exécuteur programmable (`Delegate`, `Revoke`, `Transfer`, `Lock`, `Unlock`, `Burn`) avec Token Records et Authorization Rules. +- `pre.040` : exécuteur collection/use/admin, en excluant formellement les opérations historiques. +- `pre.041` : preflight stateful complet, postconditions et replay de confirmation idempotent. +- `pre.042` : intégration `kb_app_demo`, bindings et scénarios PostgreSQL/replay/dry-run. +- `pre.043` : corpus Mainnet de signatures et comptes historiques/actifs. +- `pre.044` : validations Devnet et Localnet des opérations encore appelables. +- `pre.045` : audit de non-duplication et conflits de provenance Metaplex externe / Token-2022 incorporée / Metaplex Core. +- `pre.046` : audit final, documentation, changelog, prompt de session suivant et clôture de `0.4.7`. + +### Frontière du décodeur + +- [x] Créer le squelette canonique `kb_decoder_metadata_metaplex_token_metadata`, borné au Program ID Metaplex vérifié, sans enregistrement runtime ni prétention de décodage dans `0.4.7-pre.001`. +- [x] Créer le squelette canonique `kb_executor_metadata_metaplex_token_metadata`, audit-only et sans builder runtime avant validation exacte des comptes, autorités, coûts, confirmations et postconditions stateful. +- [ ] Réutiliser le Program ID déjà enregistré dans `kb_program_ids`, puis ajouter les capacités au registre runtime et aux options Tauri seulement après validation du premier corpus, sans heuristique fondée seulement sur un PDA ou un discriminant. +- [ ] Préserver signature, slot, outer/CPI, chemin d'instruction, statut de transaction, payload hash, comptes ordonnés, signers/writable et provenance de layout. +- [ ] Produire des diagnostics stables et bornés pour discriminant inconnu, payload tronqué/suffixé, comptes manquants/supplémentaires, PDA incohérent, owner incorrect, enum future et chaîne/vecteur hors limites. +- [ ] Classer les variantes futures ou non prouvées en `Unsupported`, et les payloads structurellement invalides en `Failed`, sans projection partielle trompeuse. + +### Décodage des instructions + +- [x] Inventorier et couvrir les générations historiques et actuelles publiées de création et mise à jour de metadata (`CreateMetadataAccount`, V2, V3, `UpdateMetadataAccount`, V2). +- [ ] Couvrir création de master edition, edition, edition marker et mint d'éditions, avec limites numériques et PDA exacts. + - [x] Décoder `CreateMasterEditionV3` et `ConvertMasterEditionV1ToV2` avec wire et comptes exacts. + - [x] Décoder le mint d'éditions via token et vault proxy, avec numéro d'édition `u64`, edition marker et comptes exacts. + - [ ] Décoder et valider les comptes edition/edition marker ainsi que leurs PDA dans la tranche de décodage d’état. +- [x] Couvrir vérification/dé-vérification de créateurs et collections, collection size/details et autorités de collection. + - [x] Décoder `SetCollectionSize` et `BubblegumSetCollectionSize` avec taille `u64`, flags exacts et record optionnel. + - [ ] Valider les PDA/owners des collection authority records lors du décodage des comptes on-chain. +- [x] Couvrir uses, approve/revoke use authority et consommation d'usage avec comptes exacts. +- [x] Couvrir délégations et révocations metadata/collection/use/sale/transfer/utility/staking/programmable lorsqu'elles sont officiellement définies. +- [x] Couvrir les opérations programmables actuelles : transfer, lock, unlock, burn, delegate, revoke et authorization data/rule set lorsque le wire exact est prouvé. La validation stateful des rule sets et PDA reste planifiée séparément. +- [x] Couvrir les migrations et instructions historiques encore observables sans les confondre avec les variantes actuelles. + - [x] Décoder `FreezeDelegatedAccount` et `ThawDelegatedAccount` historiques avec leurs cinq comptes exacts et les conserver decode-only. + - [x] Décoder les burns NFT historiques et le mint déprécié via printing token (`BurnNft`, `BurnEditionNft`, `DeprecatedMintNewEditionFromMasterEditionViaPrintingToken`). + - [x] Décoder les instructions escrow/collect (`CreateEscrowAccount`, `CloseEscrowAccount`, `TransferOutOfEscrow`, `Collect`) avec wire et comptes exacts. + - [x] Décoder le wrapper moderne `Create` (`42`) avec `CreateArgs::V1`, neuf comptes positionnels et placeholders optionnels exacts. + - [x] Décoder le wrapper moderne `Mint` (`43`) avec `MintArgs::V1`, quinze comptes positionnels et placeholders optionnels exacts. + - [x] Décoder le wrapper moderne `Print` (`55`) avec `PrintArgs::V1`/`V2`, dix-huit comptes positionnels et Token Record optionnel exact. + - [x] Décoder le wrapper moderne `Update` (`50`) et ses variantes `V1`/`As…V2` avec wire et comptes exacts. + - [x] Décoder les wrappers modernes `Use`, `Verify` et `Unverify` avec leurs variantes et contrats positionnels exacts. + - [x] Décoder `Resize` et `Migrate`, couvrir les six discriminateurs historiques manquants `2`/`5`/`6`/`8`/`9`/`10` et prouver l’inventaire exhaustif `0..=57`. +- [x] Préserver tous les champs wire significatifs : autorités, creators, seller fee basis points, collection, uses, token standard, collection details, programmable config et authorization data pour les instructions actuellement couvertes. + +### Intégration `kb_pipeline` et `kb_app_demo` + +- [ ] Enregistrer le décodeur dans le registre runtime de `kb_pipeline` après stabilisation de la matrice d’instructions et des comptes. +- [ ] Ajouter les tests `kb_pipeline` pour outer/CPI, transactions échouées, replay, déduplication et routage vers les matérialiseurs. +- [ ] Ajouter les tests `kb_pipeline` de matérialisation idempotente et d’orchestration d’exécution simulation-first. +- [ ] Enregistrer décodeur, matérialiseurs et exécuteur dans `kb_app_demo`, puis couvrir les campagnes replay et les scénarios PostgreSQL. +- [ ] Valider dans `kb_app_demo` les refus explicites des instructions historiques/non supportées et les commandes Tauri éventuelles. + +### Décodage des comptes et PDA + +- [ ] Parser et versionner `Metadata`, `MasterEditionV1/V2`, `Edition`, `EditionMarker`, `EditionMarkerV2`, `TokenRecord`, `MetadataDelegateRecord`, `CollectionAuthorityRecord`, `UseAuthorityRecord` et tout autre compte officiel confirmé. + - [x] `pre.025` : parser `MetadataV1`, conserver les champs descriptifs et administratifs, et produire une projection JSON bornée. + - [x] `pre.026` : parser `DeprecatedMasterEditionV1`, `MasterEditionV2` et `EditionV1`, conserver supply/max supply, printing mints historiques, parent et numéro d’édition. + - [x] `pre.027` : parser `EditionMarker` et `EditionMarkerV2`, conserver ledger, groupe V1, index d’octet, masque et état de l’édition demandée. + - [x] `pre.028` : parser `TokenRecord`, conserver bump, état programmable, révision du rule set, delegate, rôle et locked transfer. + - [x] `pre.029` : parser les records Metadata/Holder Delegate, Collection Authority et Use Authority avec leurs champs exacts. + - [x] `pre.030` : parser `TokenOwnedEscrow`, conserver base token, autorité TokenOwner/Creator et bump. + - [x] `pre.031` : parser `ReservationListV1/V2`, conserver Master Edition, supply snapshot, réservations et caches historiques en decode-only. +- [ ] Vérifier l'owner du compte avant tout parsing et refuser les comptes étrangers même si leur payload ressemble au layout attendu. + - [x] `pre.025` : appliquer cette frontière au compte `Metadata` avant toute désérialisation. + - [x] `pre.026` : appliquer la même frontière aux comptes edition/master edition avant toute désérialisation. + - [x] `pre.027` : appliquer cette frontière aux Edition Markers avant toute lecture du discriminant ou du ledger. + - [x] `pre.028` : appliquer cette frontière au Token Record avant toute lecture de l’état programmable. + - [x] `pre.029` : appliquer cette frontière aux quatre familles de records avant désérialisation. + - [x] `pre.030` : appliquer cette frontière à Token Owned Escrow avant lecture de l’autorité. + - [x] `pre.031` : appliquer cette frontière aux Reservation Lists avant lecture de leur génération historique. +- [ ] Vérifier les PDA canoniques et l'ordre exact des seeds pour metadata, edition/master edition, edition markers, token records, delegates, collection authorities et use authorities. + - [x] `pre.025` : valider le PDA Metadata exact `[b"metadata", program_id, mint]` et conserver le bump. + - [x] `pre.026` : valider le PDA edition/master edition exact `[b"metadata", program_id, mint, b"edition"]` et conserver le bump. + - [x] `pre.027` : valider le PDA V1 `[metadata, program_id, mint, edition, edition/248 décimal]` et le PDA V2 `[metadata, program_id, mint, edition, marker]`. + - [x] `pre.028` : valider le PDA Token Record `[metadata, program_id, mint, token_record, token_account]` et le bump stocké. + - [x] `pre.029` : valider les PDA exacts des records Metadata/Holder Delegate, Collection Authority et Use Authority. + - [x] `pre.030` : valider les PDA escrow TokenOwner `[metadata, program_id, base_token, 0, escrow]` et Creator `[metadata, program_id, base_token, 1, creator, escrow]`, ainsi que le bump stocké. + - [x] `pre.031` : valider le PDA Reservation List `[metadata, program_id, master_edition, reservation, resource]` avec contexte resource explicite. +- [ ] Borner strictement noms, symboles, URI, creators, options, enums et données d'autorisation ; distinguer taille minimale, taille exacte et compte extensible. + - [x] `pre.025` : borner Metadata à 1 024 octets, name à 32, symbol à 10, URI à 200 et creators à 5. + - [x] `pre.026` : borner edition/master edition à 128 octets et exiger exactement 41 octets pour `EditionV1`. + - [x] `pre.027` : exiger 32 octets pour V1, une longueur Borsh sans suffixe pour V2 et un ledger V2 borné à 1 048 576 octets. + - [x] `pre.028` : exiger exactement 80 octets pour Token Record et refuser troncature, suffixe, clé ou bump incohérents. + - [x] `pre.029` : exiger 98 octets pour les delegate records, 10 pour Use Authority et les deux tailles Borsh Collection Authority. + - [x] `pre.030` : exiger 35 octets pour TokenOwner et 67 pour Creator, sans suffixe. + - [x] `pre.031` : limiter les listes à 200 entrées, 6 949 octets pour V1 et 9 749 pour V2, avec padding nul uniquement. +- [ ] Corréler mint, token account, edition, collection et rule set uniquement lorsque toutes les identités sont prouvées. +- [ ] Conserver explicitement la provenance `metaplex_token_metadata`, distincte de `token_2022_embedded_metadata` et de `metaplex_core`. + +### Matérialisation + +- [ ] Étendre `kb_materializer_metadata` comme propriétaire unique des faits descriptifs : nom, symbole, URI, seller fee, creators, collection, uses, token standard, mutabilité, collection details, programmable config et provenance. + - [x] `pre.034` : matérialiser `MetadataV1` comme snapshot owned autoritatif, stable et commité, sans doublon avec les observations d’instructions. + - [x] `pre.035` : matérialiser Edition/Master Edition, Edition Marker et Reservation Lists historiques avec lifecycle et provenance explicites. +- [ ] Étendre `kb_materializer_admin` pour update authority, collection authority, delegates, rule set authority et changements d'autorité prouvés. + - [x] `pre.035` : projeter les Delegate Records et Authority Records avec `kb_materializer_admin` déclaré comme propriétaire unique du fait administratif. +- [ ] Étendre `kb_materializer_lifecycle` pour création, mise à jour, édition, verify/unverify, mint d'édition, burn, lock/unlock et transitions programmables prouvées. + - [x] `pre.035` : projeter Token Record et Token Owned Escrow avec `kb_materializer_lifecycle` déclaré comme propriétaire unique du fait de lifecycle. +- [ ] Étendre `kb_materializer_risk`/`compliance_audit` pour creators non vérifiés, metadata mutable, royalties, délégations sensibles, rule sets et conflits de provenance, sans dupliquer les faits metadata. +- [ ] Définir une identité canonique stable par mint/asset + source + compte metadata, avec `event_key`/`output_key` idempotents. + - [x] `pre.033–035` : utiliser `program_id:account` et un `output_key` stable par compte pour les snapshots actifs, expérimentaux et historiques. +- [ ] Conserver simultanément metadata Metaplex externe et metadata Token-2022 incorporée ; exposer source, autorité, slot, priorité de lecture et conflits sans fusion silencieuse. +- [ ] Refuser toute matérialisation engagée issue d'une transaction Solana en échec. + +### Exécuteur et préflight + +- [ ] Créer ou finaliser `kb_executor_metadata_metaplex_token_metadata` uniquement après audit des builders officiels et de leurs comptes exacts. +- [ ] Définir des intents typés pour les opérations officiellement constructibles ; retourner `Unsupported(reason)` pour les surfaces sans builder/wire suffisamment prouvé. +- [ ] Préserver l'ordre exact des metas, signers, writable, PDA, authorities, payer, token program et system/sysvar programs. +- [ ] Imposer simulation-first, dry-run par défaut, plafond de frais/dépense et confirmation opérateur dédiée pour burn, authority changes, verify/unverify, delegate/revoke, lock/unlock et opérations programmables. +- [ ] Ajouter les prélectures stateful : owner, layout/version, PDA, mint/token account, update authority, collection authority, delegate, token standard, rule set, état locked/burned et cohérence avec SPL Token/Token-2022. +- [ ] Définir des postconditions exactes et un replay de confirmation idempotent avant d'autoriser une opération comme réussie. +- [ ] Ne jamais télécharger le JSON externe référencé par URI dans le chemin critique de construction, simulation ou confirmation on-chain. + +### JSON externe et sécurité + +- [ ] Concevoir le fetch HTTP/IPFS/Arweave comme composant optionnel séparé du décodeur canonique on-chain. +- [ ] Borner timeout, taille, content type, redirections, nombre de tentatives, cache et concurrence ; conserver URL finale, hash, provenance et date d'observation. +- [ ] Bloquer SSRF, localhost, réseaux privés/link-local et schémas non autorisés ; ne jamais faire confiance aux champs du JSON distant. +- [ ] Ne jamais faire dépendre le statut `Decoded` on-chain de la disponibilité ou de la validité du contenu externe. +- [ ] Autoriser une première livraison sans fetch actif si l'isolation et les politiques réseau ne sont pas encore entièrement prouvées. + +### Corpus, replay et validation + +- [ ] Ajouter un corpus synthétique couvrant chaque instruction, compte, PDA et branche de diagnostic, avec valeurs limites et variantes historiques/currentes. +- [ ] Ajouter des signatures Mainnet réelles pour token fongible, NFT, SFT, collection sized/unsized, master edition, edition et programmable NFT. +- [ ] Couvrir outer et CPI, transactions réussies et échouées, comptes dupliqués/manquants, mauvais Program ID/owner/PDA, payload tronqué/suffixé et discriminant inconnu. +- [ ] Ajouter des cas de conflit entre metadata Metaplex externe et metadata Token-2022 incorporée, sans perte de provenance. +- [ ] Enregistrer le décodeur et les materializers dans `kb_pipeline` et `kb_app_demo`, avec démonstration de lecture des observations/projections. +- [ ] Vérifier le replay PostgreSQL idempotent, l'absence de doublons decode/materialization et le mode `incomplete_signatures` sans `forceReplay` global. +- [ ] Valider avec PostgreSQL réel, tests ciblés, tests pipeline/app demo, bindings Tauri si modifiés et `cargo clippy --all-targets` propre. + +### Documentation et clôture + +- [ ] Mettre à jour les README des crates touchées, `README.md`, `docs/SOLANA_INTERFACE_DEPENDENCIES.md`, la matrice dédiée et les contrats de provenance metadata. +- [ ] Documenter précisément les surfaces observées sur Mainnet, celles seulement synthétiques, les variantes `Unsupported` et les limites du fetch externe. +- [ ] Mettre à jour `CHANGELOG.md` et cocher ce jalon seulement après validations réelles. +- [ ] Préparer le prompt suivant `0.4.8 — Metaplex Core` sans mélanger les actifs Core avec les mints SPL/Token-2022. + +### 0.4.8 — Metaplex Core + +Metaplex Core est un standard d’actifs indépendant qui ne repose pas sur un mint SPL Token pour représenter chaque NFT. Il doit donc disposer de sa propre frontière de programme et ne pas être traité comme une simple variante de metadata SPL. + +- [ ] Créer `kb_decoder_metaplex_core` avec Program IDs, comptes Asset/Collection et plugins audités depuis les sources Metaplex officielles. +- [ ] Décoder les opérations et états des actifs Core, collections, authorities, royalties, attributes et plugins sans les fusionner avec Metaplex Token Metadata. +- [ ] Matérialiser une identité d’actif canonique avec provenance `metaplex_core`, distincte des mints SPL Token/Token-2022. +- [ ] Ajouter exécuteur, corpus, démo et replay seulement après validation exacte des comptes et autorités. + +### 0.4.9 — SPL Account Compression, Noop et Metaplex Bubblegum + +Ce jalon remonte avant les pools de stake : Bubblegum dépend directement d’Account Compression et de Noop, et complète la couverture NFT ouverte par Metaplex Token Metadata et Metaplex Core. + +- [x] Réserver `kb_executor_spl_account_compression`, `kb_executor_spl_noop` et `kb_executor_nft_metaplex_bubblegum` sans activer d’envoi. +- [ ] Implémenter `kb_decoder_spl_account_compression` et `kb_decoder_spl_noop`. +- [ ] Finaliser `kb_decoder_nft_metaplex_bubblegum` pour les instructions, arbres, feuilles et metadata des NFT compressés ; ne pas considérer Compression/Noop seuls comme un décodeur NFT complet. +- [ ] Corréler Bubblegum, Account Compression et Noop sans fusionner leurs frontières de programme ni dupliquer les événements. +- [ ] Matérialiser l’identité des actifs compressés avec arbre, leaf index, asset id, owner/delegate, collection et metadata prouvées. +- [ ] N’activer les exécuteurs qu’après validation des preuves Merkle, coûts et builders officiels. + +### 0.4.10 — SPL Stake Pool + +- [ ] Décoder maximalement `SPoo1...` dans `kb_decoder_spl_stake_pool`. +- [ ] Distinguer le pool SPL du Stake Program natif, adapter `staking`, `reward`, `fee`, `admin` et `lifecycle`, puis développer `kb_executor_spl_stake_pool` après validation du décodeur. + +### 0.4.11 — SPL Single Pool + +- [x] Réserver `kb_executor_spl_single_pool` sans activer d’envoi. +- [ ] Implémenter `kb_decoder_spl_single_pool` pour `SVSPx...`. +- [ ] Couvrir et matérialiser lifecycle, dépôts/retraits, mint LST et récompenses, puis activer `kb_executor_spl_single_pool` seulement après validation des projections. + +### 0.4.12 — SPL Token Wrap + +Token Wrap est un programme SPL actuel et audité, destiné au wrapping bidirectionnel entre SPL Token classique et Token-2022. Il ne doit pas être classé avec les programmes historiques. + +- [ ] Ajouter le Program ID canonique `TwRapQCDhWkZRrDaHfZGuHxkZ91gHDRkyuzNqeU5MgR` à `kb_program_ids` après vérification finale des sources officielles. +- [ ] Créer `kb_decoder_spl_token_wrap` et auditer instructions, comptes d’état, PDA, mints source/wrapped et contrats de conversion. +- [ ] Matérialiser le lien canonique entre mint source et mint wrapped sans fusionner leurs balances, supplies, authorities ou metadata. +- [ ] Créer `kb_executor_spl_token_wrap` uniquement après preuve des builders, coûts, decimals, ratios et conditions de fermeture/unwrapping. +- [ ] Ajouter corpus réel, démo, préflight et replay PostgreSQL idempotent. + +### 0.4.13 — SPL Token Swap + +SPL Token Swap reste dans la série `0.4.x` : il s’agit d’un programme SPL autonome avec Program ID propre, même si son domaine fonctionnel est un AMM. Les phases AMM ultérieures couvriront les protocoles tiers et réutiliseront les abstractions établies ici. + +- [ ] Vérifier et enregistrer le Program ID officiel `SwapsVeCiPHMUAtzQWZw7RjsKjgCjhwU55QGu4U1Szw` dans `kb_program_ids`. +- [ ] Créer `kb_decoder_spl_token_swap` et auditer instructions, courbes, fees, états de pool, autorités et compatibilité Token classique/Token-2022. +- [ ] Matérialiser pools, liquidité, swaps, fees et lifecycle sans dupliquer les mouvements Token sous-jacents. +- [ ] Créer `kb_executor_spl_token_swap` seulement après preuve des courbes, slippage, comptes ordonnés, builders et simulations. +- [ ] Ajouter corpus réel, démo, préflight et replay PostgreSQL idempotent. + +### 0.4.14 — SPL Token Lending + +SPL Token Lending reste également dans `0.4.x` : c’est un programme SPL autonome avec Program ID propre. Les phases Lending ultérieures couvriront Solend, Kamino et les autres protocoles dérivés ou indépendants. + +- [ ] Vérifier et enregistrer le Program ID officiel `6TvznH3B2e3p2mbhufNBpgSrLx6UkgvxtVQvopEZ2kuH` dans `kb_program_ids`. +- [ ] Créer `kb_decoder_spl_token_lending` et auditer markets, reserves, obligations, collateral, liquidations, flash loans, oracles et autorités. +- [ ] Matérialiser lending, borrowing, collateral, interest, liquidation, fees et risk sans dupliquer les transferts Token. +- [ ] Créer `kb_executor_spl_token_lending` uniquement après validation des calculs, oracles, slippage, health factor et simulation. +- [ ] Ajouter corpus réel, démo, préflight et replay PostgreSQL idempotent. + +### 0.4.15 — SPL Name Service + +- [x] Réserver `kb_executor_metadata_spl_name_service` sans activer d’envoi. +- [ ] Implémenter `kb_decoder_metadata_spl_name_service` pour `namesLPne...`. +- [ ] Décoder puis matérialiser metadata/admin/lifecycle des noms, puis activer `kb_executor_metadata_spl_name_service` après validation des autorités et coûts. + +### 0.4.16 — SPL Governance et programmes utilitaires déployés + +- [ ] Auditer les déploiements et versions encore pertinents de SPL Governance, Feature Proposal, Record, Shared Memory et Instruction Padding depuis leurs dépôts maintenus ou forks officiels identifiés. +- [ ] Créer des jalons internes séparés par Program ID ; ne pas regrouper leurs wire formats dans un décodeur générique « SPL utilities ». +- [ ] Prioriser Governance si des stratégies ou materializations de vote/DAO en dépendent ; conserver Feature Proposal, Record, Shared Memory et Instruction Padding comme surfaces d’infrastructure à priorité moindre. +- [ ] Distinguer strictement les programmes déployés, les interfaces sans Program ID universel et les implémentations tierces. + +### 0.4.17 — SPL historique ou archivé + +- [ ] Auditer Managed Token, Stateless Asks, Token Upgrade, Binary Option et Binary Oracle Pair avant toute réservation de crate ou Program ID. +- [ ] Auditer Token Collection et les anciennes implémentations Token Metadata/Token Group uniquement comme sources historiques ou compatibilité, sans les confondre avec les interfaces actuelles. +- [ ] Ne pas attribuer un Program ID universel aux interfaces Token Group, Token Metadata ou Transfer Hook ; dispatcher vers les implémentations observées. + +### 0.4.18 — infrastructure de décodage Anchor + +Anchor est un framework et un ensemble de conventions, pas un programme Solana unique. Les programmes SPL planifiés dans `0.4.x` utilisent leurs propres interfaces et n’exigent pas cette couche. Le jalon Anchor est donc placé immédiatement avant Meteora afin de fournir l’infrastructure commune requise par Meteora, Pump, Raydium et les autres programmes Anchor, sans remplacer leurs décodeurs protocolaires ni accepter un discriminant isolé comme preuve d’identité. + +- [ ] Auditer les versions Anchor réellement rencontrées, les discriminateurs d’instruction et de compte, les événements `Program data:`, les erreurs, les IDL historiques/courantes et les différences entre générations. +- [ ] Définir un registre versionné `Program ID + provenance IDL + hash/version + cluster + plage de slots` ; refuser tout décodage lorsqu’aucun descripteur vérifié ne correspond au programme et à sa version observée. +- [ ] Créer une couche commune pour les enveloppes, comptes, événements et erreurs Anchor, tout en laissant aux crates Meteora/Pump/Raydium leurs Program IDs, leur sémantique métier, leurs invariants, leurs matérialisations et leurs exécuteurs. +- [ ] Décoder outer et CPI avec chemins exacts, comptes ordonnés, discriminateurs vérifiés, arguments Borsh bornés et politique explicite pour suffixes, troncatures et versions inconnues. +- [ ] Extraire les événements Anchor depuis les logs sans les confondre avec des logs texte arbitraires, conserver l’ordre d’émission et rattacher chaque événement à l’instruction/CPI source lorsque la pile d’invocation le permet. +- [ ] Décoder les comptes Anchor uniquement avec owner/program ID, discriminant et layout IDL vérifiés ; conserver les comptes inconnus ou versions futures comme `Unsupported`, jamais comme état partiellement inventé. +- [ ] Définir les contrats de fallback pour les programmes sans IDL publique, les IDL obsolètes, les upgrades et les collisions de discriminateurs entre Program IDs. +- [ ] Ajouter une matrice machine-readable, des corpus synthétiques/Mainnet et des tests anti-collision prouvant qu’un même discriminant sous deux Program IDs ne partage jamais silencieusement le même sens. +- [ ] Raccorder `incomplete_signatures` afin qu’une nouvelle IDL ou version de descripteur puisse reprendre uniquement les signatures Anchor non complètement décodées. +- [ ] Préparer `0.5.x — Meteora` : les décodeurs Meteora doivent consommer cette infrastructure pour le wire commun, tout en conservant leurs frontières protocolaires propres. + +### 0.4.19 — clôture Solana Core/SPL et metadata adjacente + +- [ ] Exécuter le replay complet des corpus `0.3.x` et des corpus SPL dédiés. +- [ ] Vérifier couverture, matérialisations, idempotence, versions de processor et absence de régression. +- [ ] Produire la documentation des événements core/SPL disponibles pour les stratégies. + +## Version 0.5.x — Meteora + +Les surfaces AMM et de liquidité précèdent le launchpad DBC dans l’ordre de développement. + +### 0.5.0 — corpus et surfaces Meteora + +- [ ] Constituer les corpus dédiés DLMM, DAMM v1, DAMM v2, DBC et Vault. +- [ ] Vérifier tous les Program IDs dans `kb_program_ids`. +- [ ] Auditer IDL/interfaces, comptes, événements, autorités et frais. +- [ ] Établir la matrice complète instructions/events/materializations/executors. + +### 0.5.1 — Meteora DLMM + +- [ ] Décoder maximalement instructions, événements, bins, positions et lifecycle. +- [ ] Matérialiser pools, bins, positions, liquidité, swaps, fees et changements administratifs. +- [ ] Implémenter `kb_executor_dlmm_meteora` après validation du décodeur, avec slippage, limites de liquidité, autorités et coûts bornés. + +### 0.5.2 — Meteora DAMM v1 + +- [ ] Décoder maximalement instructions, événements et états de pool. +- [ ] Matérialiser pools, liquidité, swaps, fees et lifecycle sans dupliquer les mouvements Token. +- [ ] Implémenter `kb_executor_amm_meteora_damm_v1` pour les opérations client officiellement appelables. + +### 0.5.3 — Meteora DAMM v2 + +- [ ] Décoder maximalement instructions, événements et états de pool. +- [ ] Matérialiser pools, liquidité, swaps, fees et lifecycle. +- [ ] Implémenter `kb_executor_amm_meteora_damm_v2` avec quotes, slippage, plafonds et simulation. + +### 0.5.4 — Meteora DBC + +- [ ] Décoder maximalement le launchpad DBC après stabilisation des AMM et Vault. +- [ ] Matérialiser créations, courbes, migrations, liquidité, paramètres et risques observables. +- [ ] Implémenter `kb_executor_launchpad_meteora_dbc` pour les opérations client officiellement appelables. + +### 0.5.5 — Meteora Vault + +- [ ] Auditer et décoder les surfaces Vault et complémentaires réellement confirmées. +- [ ] Matérialiser dépôts, retraits, parts, stratégies, fees et lifecycle. +- [ ] Implémenter `kb_executor_vault_meteora` uniquement pour les opérations appelables validées. + +### 0.5.6 — régression Meteora + +- [ ] Fusionner les corpus Meteora et rejouer toutes les surfaces. +- [ ] Vérifier interactions, idempotence et régressions croisées. +- [ ] Valider les cinq exécuteurs offline et sur localnet/devnet, puis enregistrer coûts, autorités et exposition dans la matrice. + +## Version 0.6.x — Pump + +Les surfaces AMM Pump Swap sont traitées avant le launchpad Pump Fun. + +### 0.6.0 — corpus et surfaces Pump + +- [ ] Constituer des corpus dédiés Pump Swap, Pump Fun et Pump Fees. +- [ ] Vérifier tous les Program IDs dans `kb_program_ids`. +- [ ] Établir la matrice complète instructions/events/materializations. + +### 0.6.1 — Pump Swap + +- [ ] Décoder toutes les instructions et événements connus. +- [ ] Matérialiser trades, liquidité, lifecycle et changements administratifs. +- [ ] Implémenter `kb_executor_amm_pump_swap` maximalement pour les opérations client appelables, avec quotes/plafonds/slippage et comparaison au contrat officiel. + +### 0.6.2 — Pump Fun + +- [ ] Décoder toutes les instructions et événements connus, y compris les entrées non directement liées au trading. +- [ ] Matérialiser créations, migrations, trades, liquidité, paramètres et risques observables. +- [ ] Implémenter `kb_executor_launchpad_pump_fun` après stabilisation du décodeur : builders officiels/IDL validés, autorités, coûts, slippage, simulation et tests devnet ; l’exposition trading reste une décision séparée. + +### 0.6.3 — Pump Fees + +- [ ] Décoder toutes les instructions et événements connus. +- [ ] Matérialiser les signaux de frais, buyback, partage, donation et changements d’autorité utiles à la gestion du risque. +- [ ] Implémenter `kb_executor_admin_pump_fees` pour les opérations administratives officiellement appelables, avec politiques d’autorité renforcées et aucune exposition UI par défaut. + +### 0.6.4 — régression Pump + +- [ ] Fusionner les corpus Pump et rejouer toutes les surfaces. +- [ ] Vérifier les interactions et régressions croisées. +- [ ] Valider les trois exécuteurs Pump offline et sur devnet/localnet, puis enregistrer leur statut exact dans la matrice décodeur/matérialiseur/exécuteur. + +## Version 0.7.x — Raydium + +Les surfaces AMM précèdent le launchpad LaunchLab. + +### 0.7.0 — corpus et surfaces Raydium + +- [ ] Constituer les corpus AMM v4, CPMM, CLMM, Stable Swap et LaunchLab. +- [ ] Vérifier tous les Program IDs et versions de programme dans `kb_program_ids`. +- [ ] Auditer instructions, événements, états, autorités, fees et courbes. +- [ ] Établir la matrice complète instructions/events/materializations/executors. + +### 0.7.1 — Raydium AMM v4 + +- [ ] Décoder maximalement instructions, événements et comptes historiques/courants. +- [ ] Matérialiser pools, liquidité, swaps, fees et lifecycle. +- [ ] Implémenter `kb_executor_amm_raydium_lp_v4` après preuve des builders et limites. + +### 0.7.2 — Raydium CPMM + +- [ ] Décoder maximalement instructions, événements et états CPMM. +- [ ] Matérialiser pools, liquidité, swaps, fees et lifecycle. +- [ ] Implémenter `kb_executor_cpmm_raydium` avec quotes, slippage et plafonds. + +### 0.7.3 — Raydium CLMM + +- [ ] Décoder maximalement instructions, événements, ticks, positions et rewards. +- [ ] Matérialiser pools, ticks, positions, liquidité, swaps, fees et rewards. +- [ ] Implémenter `kb_executor_clmm_raydium` avec bornes de ticks, slippage et coûts. + +### 0.7.4 — Raydium Stable Swap + +- [ ] Auditer les surfaces actuelles et historiques confirmées sans inventer de programme générique. +- [ ] Décoder et matérialiser uniquement les contrats dont le Program ID et le wire sont prouvés. +- [ ] N’activer un exécuteur qu’après identification d’une interface ou crate canonique. + +### 0.7.5 — Raydium LaunchLab + +- [ ] Décoder maximalement LaunchLab après stabilisation des AMM. +- [ ] Matérialiser créations, migrations, liquidité, paramètres et risques observables. +- [ ] Implémenter `kb_executor_launchpad_raydium_launchlab` pour les opérations client appelables. + +### 0.7.6 — régression Raydium + +- [ ] Fusionner les corpus Raydium et rejouer toutes les surfaces. +- [ ] Vérifier interactions, idempotence et régressions croisées. +- [ ] Valider les exécuteurs confirmés offline/localnet/devnet et enregistrer coûts, slippage et autorités. + +## Version 0.8.x — Orca + +La surface AMM Whirlpool précède le launchpad Wavebreak. + +### 0.8.0 — corpus et surfaces Orca + +- [ ] Constituer les corpus Whirlpool, Wavebreak et surfaces complémentaires confirmées. +- [ ] Vérifier tous les Program IDs et versions dans `kb_program_ids`. +- [ ] Auditer instructions, événements, comptes, authorities, fees et matrice commune. + +### 0.8.1 — Orca Whirlpool + +- [ ] Décoder maximalement instructions, événements, ticks, positions et rewards. +- [ ] Matérialiser pools, ticks, positions, liquidité, swaps, fees et rewards. +- [ ] Implémenter `kb_executor_clmm_orca_whirlpool` avec slippage, bornes de ticks, positions, liquidité et autorités. + +### 0.8.2 — Orca Wavebreak + +- [ ] Décoder maximalement le launchpad après stabilisation de Whirlpool. +- [ ] Matérialiser créations, migrations, liquidité, paramètres et risques observables. +- [ ] Implémenter `kb_executor_launchpad_orca_wavebreak` pour les opérations client officiellement appelables. + +### 0.8.3 — régression Orca + +- [ ] Fusionner les corpus Orca et rejouer toutes les surfaces. +- [ ] Vérifier interactions, idempotence et régressions croisées. +- [ ] Valider les exécuteurs offline/localnet/devnet et enregistrer leur statut dans la matrice. + +## Version 0.9.x — Jupiter, routeurs et surfaces complémentaires + +- [ ] Décoder Jupiter Aggregator v4/v6 et les routes multi-hop, puis implémenter `kb_executor_router_jupiter_aggregator_v4` et `kb_executor_router_jupiter_aggregator_v6` après validation des contrats encore appelables. +- [ ] Décoder Jupiter DCA puis implémenter `kb_executor_router_jupiter_dca` pour les opérations officiellement appelables. +- [ ] Décoder Jupiter Limit Order v1/v2 puis implémenter `kb_executor_orderbook_jupiter_limit_order` et `kb_executor_orderbook_jupiter_limit_order_v2`. +- [ ] Décoder Jupiter Perpetuals puis implémenter `kb_executor_perpetuals_jupiter` avec politiques de marge, collateral, levier, prix et liquidation renforcées. +- [ ] Traiter les surfaces Jupiter lending/admin/wallet selon les crates réservées correspondantes ; chaque décodeur validé reçoit un statut d’exécuteur explicite, pas une promesse générique Jupiter. +- [ ] Décoder Jupiter même lorsque la source temps réel n’est pas directement abonnée au program id Jupiter. +- [ ] Ajouter OKX/Onchain Labs et DFlow après validation des program ids, puis leurs exécuteurs réservés `kb_executor_router_okx_labs_v1`, `kb_executor_router_okx_labs_v2` et `kb_executor_router_dflow_aggregator_v4`. +- [ ] Ajouter Bags Fee Share v1/v2 puis `kb_executor_fees_bags_fee_share_v1` et `kb_executor_fees_bags_fee_share_v2`, avec garde-fous administratifs et aucune exposition trading implicite. +- [ ] Mesurer quelles routes Jupiter sont déjà reçues via les DEX sous-jacents. +- [ ] Valider les exécuteurs routeur/orderbook/perpetuals offline puis localnet/devnet ; l’activation live dépend de quotes exactes, slippage, limites de dépense et validation post-exécution. + +## Version 0.10.x — démonstrations Devnet Solana Core + +Le scan de `kb_decoder_solana_core` et `kb_executor_solana_core` confirme que System, Compute Budget, Address Lookup Table, Stake et Vote appartiennent tous au domaine Solana Core. Les exécuteurs sont disponibles, mais `kb_pipeline` et `DemoExecutionSolanaCore` n’orchestrent actuellement qu’un transfert System de bout en bout. + +- [x] Confirmer les capacités exactes déjà compilées : quatre opérations Compute Budget, cinq opérations Address Lookup Table, vingt-quatre helpers Stake actuels et vingt-quatre opérations Vote actuelles. +- [ ] Ajouter un accordéon par famille seulement lorsque la famille dispose d’une démonstration utile ; conserver System affiché directement tant qu’il reste la seule orchestration réseau complète. +- [ ] Ajouter en premier une transaction System enrichie de `SetComputeUnitLimit` et `SetComputeUnitPrice`, avec plan, simulation, soumission, hydratation et replay. +- [ ] Ajouter ensuite le lifecycle Address Lookup Table contrôlé : create, extend, deactivate, attente du cooldown puis close ; freeze doit utiliser une table jetable distincte car il est irréversible. +- [ ] Ajouter un lifecycle Stake contrôlé sur compte temporaire : create/initialize, delegate vers un vote account Devnet existant, deactivate, attente d’epoch puis withdraw/close. +- [ ] Ajouter Vote en dernier : création d’un vote account temporaire et opérations administratives sûres ; ne pas tenter de produire artificiellement des votes de consensus sans validator contrôlé. +- [ ] Mutualiser l’orchestration réseau générique afin d’éviter une fonction pipeline spécifique par opération et conserver simulation-first, autorisation explicite, plafonds, postconditions et second replay. + +## Version 0.11.x — sources temps réel interchangeables + +Cette série commence seulement lorsque les transactions reçues peuvent être décodées et matérialisées immédiatement. + +### 0.11.0 — contrat de stream commun dans `kb_rpc` + +- [ ] Ajouter une interface interne de flux transactionnel source-indépendante dans `kb_rpc`. +- [ ] Normaliser toutes les sources vers le même modèle canonique `kb_model`. +- [ ] Garantir que les décodeurs et stores ne dépendent d’aucun provider. + +### 0.11.1 — extension Helius WebSocket + +- [ ] Ajouter `transactionSubscribe` et `transactionUnsubscribe` dans des modules internes Helius de `kb_rpc`. +- [ ] Ajouter `accountInclude`, `accountExclude`, `accountRequired`, `vote`, `failed` et options de transaction complète. +- [ ] Tester offline avant tout abonnement payant. +- [ ] Acheter Helius Developer seulement pour la campagne mainnet réelle. + +### 0.11.2 — Yellowstone gRPC + +- [ ] Ajouter le client Yellowstone gRPC dans `kb_rpc` avec `tonic` et Protobuf. +- [ ] Supporter Triton, Chainstack, Shyft et autres endpoints compatibles par configuration. +- [ ] Ajouter filtres transaction `account_include`, `account_exclude`, `account_required`, `vote` et `failed`. +- [ ] Normaliser `SubscribeUpdateTransaction` vers la transaction canonique. + +### 0.11.3 — fallback Solana standard + +- [ ] Ajouter `logsSubscribe` par mention et `logsSubscribe("all")` comme probes/fallbacks mesurés. +- [ ] Ajouter l’hydratation `getTransaction` bornée. +- [ ] Ne jamais stocker le payload complet des notifications quand la transaction canonique est disponible. + +### 0.11.4 — comparaison fournisseur et coût + +- [ ] Comparer Helius `transactionSubscribe`, Yellowstone gRPC et logs + hydration. +- [ ] Mesurer couverture, latence, octets, crédits/coût, doublons, pertes et reconnexions. +- [ ] Utiliser `kb_sol_obs_transaction_observations` pour les comparaisons de timing par signature. +- [ ] Commencer avec Meteora, Pump et Raydium ; ajouter Orca ensuite ; garder Jupiter hors filtre initial. +- [ ] Choisir la source principale, le fallback et le fournisseur selon les mesures. + +### 0.11.5 — ingestion live durable + +- [ ] Ajouter sessions persistantes, backpressure, reconnexion et réparation des trous. +- [ ] Désactiver le trading pendant toute perte de source ou phase de rattrapage. +- [ ] Marquer les origines `live`, `replayed`, `backfilled` et `repaired`. +- [ ] Interdire par défaut toute décision de trading depuis un événement non `live`. + +## Version 0.12.x — wallet, sécurité et démos devnet + +- [ ] Promouvoir `kb_wallet` du backend temporaire `0.4.2` vers des backends de production chiffrés, coffre système ou hardware wallet. +- [ ] Lire l’adresse wallet locale et les soldes SOL/SPL. +- [ ] Généraliser les parcours wallet et soldes SOL/SPL au-delà du laboratoire d’exécution native validé en `0.4.2`. +- [ ] Finaliser `kb_execution_safety` minimal. +- [ ] Ajouter simulation obligatoire, limites de dépense, slippage, dry-run et validation post-transaction. +- [ ] Ajouter les pages Tauri wallet, balances et transferts. + +## Version 0.13.x — listeners et trading assisté + +- [ ] Relier les transactions décodées live aux listeners métier. +- [ ] Notifier créations de tokens/pools, migrations, liquidité, swaps, frais et opérations administratives dangereuses. +- [ ] Ajouter des conditions manuelles d’achat/revente sans automatisation complète. +- [ ] Ajouter heartbeat opérateur et état global `TRADING_DISABLED` / `LIVE_READY`. +- [ ] Interdire tout envoi mainnet tant que les garde-fous ne sont pas validés. +- [ ] N’autoriser les listeners ou stratégies à demander que des capacités d’exécuteur explicitement activées ; une crate réservée ou un builder hors UI ne constitue pas une autorisation de trading. + +## Développement parallèle par surface + +Pour chaque surface prioritaire : + +- [ ] Décodeur maximal, y compris instructions obsolètes ou non directement liées au trading. +- [ ] Corpus de signatures dédié. +- [ ] Événements décodés stables. +- [ ] Matérialisation complète de tout ce qui est exploitable. +- [ ] Tests synthétiques. +- [ ] Replay et SQL anti-régression. +- [ ] Statut d’exécuteur obligatoire dans le même jalon : `réservé`, `en implémentation`, `complet`, ou `non applicable` avec justification officielle. +- [ ] Pour toute surface appelable, implémenter l’exécuteur après stabilisation du décodeur et des garde-fous ; couvrir maximalement les opérations client même si elles restent hors `kb_app_demo`. +- [ ] Synchroniser le README de l’exécuteur avec ses types, constantes, opérations, paramètres, effets, erreurs, exemple d’usage et limites d’exposition. + +## Objectif long terme — bibliothèque d’analyse Solana + +- [ ] Étendre les registres à toutes les surfaces observées. +- [ ] Ajouter classification automatique des programmes inconnus. +- [ ] Ajouter matérialisations NFT, metadata, oracle, lending, staking, governance, bridge, perpetuals, vault, routing, risk et compliance audit. +- [ ] Ajouter agrégations comparables à un explorateur : tokens, pools, pairs, volumes, états, risques, anomalies et historiques. +- [ ] Ajouter replay partiel par module, version, slot, signature, program id, surface et discriminator. + +## Transports futurs + +- [ ] Étudier shred delivery/RabbitStream uniquement après stabilisation des flux transactionnels complets. +- [ ] Ne pas confondre les transactions pré-exécution sans metadata avec la source canonique de décodage. diff --git a/docs/legacy-khadhroony-bot2/RULES.md b/docs/legacy-khadhroony-bot2/RULES.md new file mode 100644 index 0000000..e0aeec2 --- /dev/null +++ b/docs/legacy-khadhroony-bot2/RULES.md @@ -0,0 +1,231 @@ + + + +# Règles spécifiques à `khadhroony-bot2` + +Ce fichier contient uniquement les règles propres au projet et au workspace `khadhroony-bot2`. + +Les règles Rust générales et réutilisables dans tous les projets sont définies dans [`RUST_RULES.md`](RUST_RULES.md). Les deux fichiers sont normatifs et cumulatifs. En cas de conflit, la règle la plus stricte s'applique ; une règle spécifique au workspace ne peut jamais assouplir une règle générale sans exception explicitement documentée. + +Toute livraison doit exécuter l'audit `python3 scripts/audit_rust_workspace_rules.py`. Tant que l'audit global n'est pas propre, les écarts existants doivent être résorbés par lots de correctifs avant toute nouvelle prérelease. + +## Règles de nommage + +- Tous les noms de fichiers et de répertoires doivent être écrits en anglais. +- Les noms de fichiers et de répertoires ne doivent contenir aucun accent, espace ou caractère spécial inutile. +- Les noms internes doivent utiliser le format `snake_case` lorsque c'est applicable. +- Les crates Rust utilisent le préfixe `kb_`. +- Les crates de décodeur utilisent le format `kb_decoder__`. +- Les crates de matérialisation utilisent le format `kb_materializer_`. +- Le crate de journalisation s'appelle `kb_logging`. + +## Règles Tauri et TypeScript + +- Toute structure ou énumération Rust exposée au frontend TypeScript doit importer le trait `TS` avec `use ts_rs::TS;` puis dériver `TS`. +- Les types Rust exportés vers TypeScript doivent utiliser des noms stables et explicites. +- Les bindings générés doivent être produits dans un dossier dédié, généralement `../frontend/ts/bindings` ou `#[ts(export, export_to = "../frontend/ts/bindings/MyStruct.ts")]`. +- Les types purement internes au backend ne doivent pas être exportés vers TypeScript par défaut. +- Un payload de commande ou d’événement Tauri traverse une frontière JSON et ne doit jamais exiger un `bigint` JavaScript. Pour un entier Rust borné et représentable par l’UI, utiliser un override TS-rs `number`; si l’exactitude au-delà de `Number.MAX_SAFE_INTEGER` est nécessaire, sérialiser explicitement une chaîne décimale côté Rust et exporter `string`. Ne jamais construire un `BigInt` dans un objet passé à `invoke` ou `emit`. +- Corriger le type Rust/TS-rs source puis régénérer les bindings ; ne pas considérer une modification manuelle isolée d’un fichier généré comme un correctif durable. + +## Règles d'architecture + +- Les décodeurs ne dépendent pas de PostgreSQL, SQLite, RPC, Tauri, wallet, stratégie ou matérialisateurs. +- Les matérialisateurs ne dépendent pas du RPC, du wallet, de Tauri ou des décodeurs spécifiques. +- Pour chaque programme Solana, quelle que soit sa famille, un décodeur doit couvrir maximalement tout wire officiellement identifiable de sa surface, qu'il soit historique, courant, récent, expérimental ou publié avant son déploiement généralisé. Ces statuts doivent rester explicites et ne constituent pas un motif pour supprimer le décodage. +- Une observation lisible issue d'une transaction échouée peut conserver une intention non commitée ; elle ne doit jamais être présentée comme une mutation réussie. +- Pour chaque programme Solana, les matérialisateurs doivent projeter maximalement tous les faits métier stables et prouvés par le décodage, y compris les états historiques, obsolètes, dépréciés ou seulement rencontrables pendant un backfill. Le statut historique interdit l'exécution, mais ne constitue jamais à lui seul un motif de non-matérialisation. +- Toute projection historique doit conserver explicitement son lifecycle, sa version de layout, sa provenance, son slot ou ordre d'observation lorsqu'ils sont connus, et ne doit jamais écraser silencieusement un état actif plus récent. +- Une même réalité métier doit avoir une seule projection canonique. Les snapshots de comptes sont la source autoritative de l'état final lorsqu'ils sont disponibles ; les instructions et événements corrélés restent des observations de mutation et ne doivent pas produire un doublon concurrent du même état. +- Les matérialisateurs doivent refuser les transactions non commitées pour les mutations d'état, tout en pouvant conserver séparément une intention non commitée lorsque le modèle métier le prévoit explicitement. +- Toute absence volontaire de projection doit être documentée avec une justification technique précise. Un matérialisateur ne doit inventer ni état final, ni montant, ni autorité, ni frais, ni agrégation que l'observation ne prouve pas. +- Pour chaque programme Solana, les exécuteurs doivent couvrir maximalement les opérations officiellement appelables et les opérations expérimentales publiées lorsque leurs contrats exacts et garde-fous sont prouvés. Les opérations historiques, dépréciées ou obsolètes doivent rester décodables et matérialisables, mais ne doivent jamais être exposées à l'exécution ; cette exclusion doit être explicite dans la matrice et le README. +- Les stores exposent leurs comportements via les traits de `kb_store_core`. +- `kb_store_pg` est le store de production. +- `kb_store_sqlite` sert aux tests, imports et corpus locaux. +- `kb_wallet` reste isolé du reste du système. +- Les transactions raw sont immuables et restent la source d'audit. +- Les replays doivent être ciblés par module, version, programme, surface, discriminator, slot ou signatures. +- La documentation du projet ne doit pas référencer le nom de l'ancien workspace. + +## Règles de documentation projet + +- `README.md` décrit le projet, son rôle, ses objectifs et son organisation. +- `ROADMAP.md` contient les futures étapes, versions et changements prévus. +- `ROADMAP.md` conserve une structure documentaire normale par phases, cases cochées et journal de préversions ; les étiquettes conversationnelles de priorité ne doivent pas y être ajoutées. +- Les réponses de livraison doivent reprendre la liste exhaustive des tâches avec les rubriques conversationnelles **Validé**, **En cours**, **Next** et **Planifié**, sans réduire le suivi à un résumé ambigu. +- `CHANGELOG.md` n'est modifié qu'après validation d'une version, avec un paragraphe ajouté à la fin décrivant ce qui a été fait ou ajouté. +- Chaque crate Rust doit avoir un `README.md` ou `001.README.md` expliquant son rôle dans l'écosystème. +- Le README d'une crate opérationnelle doit inventorier ses types, traits, constantes et fonctions publics utiles, expliquer leurs paramètres, résultats, effets et frontières, puis fournir au moins un exemple d'utilisation lorsque l'API est destinée à être appelée directement. +- Une API publique ajoutée ou modifiée n'est pas considérée comme documentée tant que le README de sa crate n'a pas été synchronisé ; les bindings générés seuls ne remplacent pas cette documentation. + +## Nomenclature métier + +- `protocol_code` désigne la famille : `pump`, `raydium`, `meteora`, `orca`, `jupiter`, etc. +- `surface_code` désigne la surface concrète : `pump_swap`, `raydium_amm_v4`, `meteora_dlmm`, etc. +- `event_code` suit le format `.`. +- Les familles d'événements principales sont : `trade`, `liquidity`, `lifecycle`, `fee`, `admin`, `reward`, `orderbook`, `token_account`, `pool_state`, `routing`, `risk`, `audit`, `unknown`. + +## Base de données + +- Les schémas PostgreSQL cibles sont : `raw`, `core`, `obs`, `decode`, `mat`, `catalog`, `agg`, `ops`, `wallet`. +- Les colonnes JSONB doivent se terminer par `_jsonb`. +- Les montants bruts on-chain doivent se terminer par `_raw`. +- La transaction canonique raw reste la source rejouable et ne doit pas être supprimée après traitement sans politique de rétention explicite. + +## Nommage canonique des surfaces + +Les nouvelles surfaces de programmes doivent utiliser un nom canonique basé sur la fonction réelle du programme : + +```text +__[_vN] +``` + +Les crates de décodage doivent utiliser : + +```text +kb_decoder___[_vN] +``` + +Exemples : + +- `kb_decoder_amm_raydium_cpmm` ; +- `kb_decoder_clmm_raydium` ; +- `kb_decoder_dlmm_meteora` ; +- `kb_decoder_router_jupiter_aggregator_v6` ; +- `kb_decoder_orderbook_openbook_v2` ; +- `kb_decoder_metadata_metaplex_token_metadata` ; +- `kb_decoder_nft_metaplex_bubblegum`. + +Les noms historiques ou issus d'IDL doivent être conservés dans le registre, mais ne doivent pas créer de nouvelles crates si une crate canonique existe déjà. + +Aucun renommage massif de crates n'est autorisé sans étape de contrôle dédiée et sans `cargo build` validé. + +## Règles de nommage des surfaces Solana + +- Le nom canonique d'une surface doit commencer par une fonction réelle : `amm`, `clmm`, `dlmm`, `router`, `orderbook`, `launchpad`, `lending`, `vault`, `staking`, `bridge`, `perpetuals`, `oracle`, `nft`, `metadata`, `admin`, etc. +- Le préfixe `program_` est interdit pour les nouveaux noms canoniques. +- Les surfaces non classifiées doivent utiliser `unknown_*` et ne doivent pas avoir de crate cible tant que leur fonction n'est pas validée. +- Les programmes Solana/SPL primitifs doivent rester séparés des surfaces DEX/router dans la documentation de contrôle. +- `damm` ne doit pas être utilisé comme préfixe fonctionnel ; utiliser `amm_meteora_damm_v1` ou `amm_meteora_damm_v2`. + +## Registre des identifiants core + +- Les identifiants Solana/SPL primitifs doivent être tenus à jour dans `registry/core_program_id_seed.toml`. +- Les sysvars et comptes natifs connus ne doivent pas être classés comme surfaces DEX/router. +- `spl_token`, `spl_token_2022` et `associated_token_account` restent dans leurs crates spécialisées. +- `stake_pool` doit être réservé à un futur crate spécialisé plutôt que mélangé avec le programme `stake`. + +## Index court de programme + +- Les noms de crates ne doivent pas commencer par un index hexadécimal. +- Un champ `registry_code` optionnel peut être ajouté au registre pour l'UI, PostgreSQL ou les matrices. +- Le nom canonique reste la source principale : fonction + famille + identifiant + version. + +## Comptes non exécutables + +- Une adresse de compte, de pool, de PDA ou de vault ne doit pas générer une crate de décodeur. +- Le vrai `program_id` propriétaire doit être prouvé avant création d'une surface canonique. + +## Constantes Rust + +- Les fichiers `program_ids.rs` sont interdits dans les nouveaux crates. Utiliser `constants.rs`. +- Les `program_id` publics doivent être réexportés depuis `lib.rs`. +- Dans une crate, les modules internes doivent appeler les constantes réexportées via `crate::CONSTANT_NAME`. +- Les discriminants, sélecteurs, longueurs Borsh et constantes internes futures doivent être placés dans `constants.rs` et rester `pub(crate)` sauf besoin d'API publique explicite. + +## Règles de tracing + +- Une crate est opérationnelle lorsqu’elle effectue des I/O, orchestre un pipeline, décode, matérialise, exécute, applique une politique runtime ou prend une décision mutable observable. +- Toute crate opérationnelle ajoutée ou modifiée doit dépendre de `tracing` depuis le workspace et déclarer exactement un `pub(crate) const TRACING_TARGET` dans `src/constants.rs`. +- La valeur canonique de `TRACING_TARGET` est le nom exact du package Cargo. Les targets historiques `khbot.*`, les suffixes de module et les targets de fenêtre sont interdits dans les nouvelles modifications. +- Les macros `tracing` doivent utiliser `target: crate::TRACING_TARGET` et des champs structurés stables. La granularité interne passe par `action`, `stage`, `window`, `campaign_id`, `signature`, `instruction_path`, `program_id`, `processor_name`, `processor_version`, `status` et `error_code`. +- Les crates passives de types, contrats, DTO, API sans exécution, registres ou constantes restent sans dépendance `tracing`. `kb_config` reste une exception de bootstrap tant que sa validation précède l’installation du subscriber. +- Il est interdit d’ajouter `tracing` sans événement réel ou de conserver un faux target uniquement consommé par `let _target`. +- Une décision interne doit être journalisée par la crate responsable ; `kb_app_demo` ne journalise que les frontières Tauri/UI, les actions utilisateur et les résumés d’orchestration. +- Tout input sélectionné sans décodeur compatible, tout résultat de décodage `failed` ou `unsupported`, tout résultat de matérialisation `failed`, toute validation de résultat invalide et toute erreur de persistance doivent émettre un événement `error` avant le retour ou la persistance terminale. +- Une transaction Solana échouée mais correctement décodée n’est pas une erreur du logiciel. Une décision `ignored`, un refus de matérialisation conforme à la politique ou une annulation coopérative ne doivent pas être promus artificiellement au niveau `error`. +- Les erreurs de décodage et de matérialisation doivent conserver au minimum, lorsque disponibles : `campaign_id`, `signature`, `slot`, `instruction_path`, `program_id`, processor ou materializer avec version, `input_key`, `input_hash` ou hash du payload, statut, code et diagnostic borné. +- Les payloads complets, DSN non masqués, secrets, clés privées et données non bornées sont interdits dans les logs. +- Chaque profil doit router les événements vers les sorties globales `debug.log`, `info.log`, `error.jsonl` et `app.log`, puis vers `debug.log`, `info.log` et `error.jsonl` dans un répertoire propre à chaque crate utilisant `tracing`. +- L’ajout ou la suppression de `tracing.workspace = true` dans une crate impose la mise à jour simultanée de la matrice de routes, de ses tests et de `docs/TRACING_CONTRACT.md`. +- Le contrat détaillé est défini dans `docs/TRACING_CONTRACT.md`. +- L’audit mécanique spécifique au projet est exécuté par `python3 scripts/audit_khadhroony_workspace_rules.py`. Il couvre notamment `TRACING_TARGET` et l’usage obligatoire de `solana_pubkey::Pubkey` à la place de `solana_address::Address`. + +## Règles de réutilisation des interfaces Solana et SPL + +- Les dépendances déclarées dans `[workspace.dependencies]` forment un catalogue de versions et de features autorisées ; elles ne doivent être ajoutées à une crate consommatrice que lorsqu’un type, un encodeur, un décodeur ou un identifiant officiel est réellement utilisé. + +- Dans un `Cargo.toml`, placer dans `[dependencies]` toute crate référencée par le code de bibliothèque compilé en production. Réserver `[dev-dependencies]` aux références contenues exclusivement dans `#[cfg(test)]`, les tests d’intégration, benches ou exemples. Une dépendance de test vers un décodeur ou matérialiseur concret est légitime lorsqu’elle sert uniquement à éprouver une orchestration générique fondée sur les traits API ; elle ne prouve pas qu’une capacité de production manque. Toute promotion de `dev-dependencies` vers `dependencies` doit être motivée par un appel runtime réel, et toute dépendance runtime inutilisée doit être supprimée. +- Les registres de composition runtime des applications doivent énumérer explicitement chaque décodeur et matérialiseur concret activé. Toute nouvelle surface instructionnelle dotée d’un `EventMaterializer` doit être ajoutée au registre applicatif et couverte par un test d’inventaire ordonné ; une crate présente dans le workspace ou dans `kb_pipeline` n’est pas activée automatiquement. Les matérialiseurs exclusivement stateful qui n’implémentent pas `EventMaterializer` restent routés par leurs APIs de snapshots dédiées. +- Les corrélations instruction/état doivent produire une issue explicite (`confirmed`, `contradicted` ou `not_applicable`) et ne doivent jamais transformer automatiquement une configuration observée en violation, score ou conclusion métier. +- Une interface officielle Solana ou SPL étroite doit être préférée à `solana-sdk` lorsque son contrat suffit. +- L’ordre de préférence des formats est : schéma officiel `wincode`, schéma officiel Borsh, puis parseur local borné reproduisant exactement le runtime lorsque l’interface officielle n’expose que `bincode`. +- Aucun nouveau code ne doit dépendre directement de `bincode`. Une interface officielle uniquement disponible derrière une feature `bincode` ne justifie pas l’activation de cette feature ; le layout doit alors être prouvé depuis les sources officielles et implémenté localement avec des bornes et des tests. +- Les exécuteurs doivent utiliser les builders officiels disponibles, préserver l’ordre exact des metas, borner toute liste de comptes variable avant l’appel au builder et refuser les doublons lorsque leur répétition n’a pas de sémantique publiée. +- Les features des interfaces doivent rester minimales et explicites. Une feature `serde`, `wincode`, `borsh`, `std`, `alloc` ou équivalente n’est activée que si la crate consommatrice l’utilise réellement. +- Les crates applicatives ne doivent pas dépendre d’interfaces RPC ou transactionnelles uniquement pour relayer des types ; ces dépendances appartiennent à `kb_rpc`, aux modèles communs justifiés ou à la crate opérationnelle propriétaire. +- Toute exception et toute implémentation locale doivent être documentées dans `docs/SOLANA_INTERFACE_DEPENDENCIES.md` avec la raison, la source officielle et la stratégie de test. + +## Règles des exécuteurs + +- Les crates d'exécution utilisent le préfixe `kb_executor_`. +- Une surface classifiée peut avoir un décodeur `kb_decoder_` et un exécuteur `kb_executor_`. +- Les exécuteurs ne doivent pas dépendre des décodeurs. +- Les exécuteurs doivent passer par `kb_execution_api` pour leur contrat public. +- Les garde-fous communs doivent être placés dans `kb_execution_safety`. +- Aucun exécuteur ne doit envoyer de transaction sans simulation et validation explicite. +- Les surfaces non classifiées ne doivent pas avoir de crate exécuteur. +- Pour chaque programme Solana et contrairement aux décodeurs, les exécuteurs ne construisent pas les opérations obsolètes ou purement historiques. Ils couvrent uniquement les opérations courantes et expérimentales officiellement constructibles, avec un statut exact `Supported` ou `Unsupported(reason)`. +- Le statut expérimental, récent ou non encore déployé partout n'interdit pas un builder universel lorsqu'un wire officiel exact existe. Le déploiement, la simulation et l'autorisation d'envoi restent trois décisions séparées ; la bibliothèque ne doit pas imposer artificiellement un cluster. + + +- Les champs JSON exposés à TypeScript ne doivent pas utiliser directement `serde_json::Value` avec `TS-rs`; utiliser une chaîne JSON sérialisée (`std::string::String`) ou un type Rust typé exportable. +- Les APIs qui gardent des payloads dynamiques doivent fournir des helpers explicites basés sur `serde_json::to_string` et `serde_json::to_string_pretty`. + +## Règles de configuration + +- La configuration applicative commune doit passer par `kb_config`. +- Les fichiers JSON de configuration ne doivent pas contenir de commentaires. +- Les secrets ne doivent pas être écrits en clair dans le dépôt. +- Les valeurs sensibles doivent utiliser des variables d'environnement ou un stockage chiffré dédié. +- Les structures de configuration exposées à Tauri doivent dériver `TS`. + +## Ordre de développement cible + +- `kb_logging` doit être stabilisé avant les logs avancés des autres crates. +- `kb_config` doit être stabilisé avant les stores, RPC, wallet, applications et workers. +- Les contrats SQL et de matérialisation doivent être définis avant les gros décodeurs DEX. +- Les implémentations détaillées des matérialisateurs doivent suivre les sorties réelles des décodeurs correspondants. +- `kb_app_demo` doit fournir des validations live après chaque capacité majeure, sans créer une application Tauri séparée par crate. + +## Règles de livraison ChatGPT + +- Après le squelette initial, ChatGPT doit fournir uniquement des zips delta sauf demande explicite de zip complet. +- Un delta qui modifie la racine du workspace ou plusieurs modules doit être nommé `khadhroony-bot2_vX.Y.Z-pre.abc-delta.zip`. +- Un delta qui ne modifie qu'un seul module Rust doit être nommé `kb_modulename_vX.Y.Z-pre.abc-delta.zip`. +- Lorsqu'un delta `pre.abc` a déjà été livré et qu'un correctif est nécessaire, le numéro `abc` ne doit pas être incrémenté. Le correctif doit être nommé `khadhroony-bot2_vX.Y.Z-pre.abc-delta-fix-001.zip`, puis `-fix-002.zip`, etc. Pour un seul module, utiliser la même règle avec le préfixe `kb_modulename`. +- Un correctif doit indiquer dans `delta.md` le delta de base et les correctifs antérieurs à appliquer. Il ne doit pas réutiliser silencieusement le nom ou l'empreinte d'une archive déjà livrée. +- Le numéro `pre.abc` suivant est réservé à une nouvelle tranche fonctionnelle, pas à la réparation d'une archive existante. +- Chaque zip delta ou correctif doit contenir un fichier `delta.md` non versionné à la racine du zip. +- `delta.md` doit lister les fichiers ajoutés, les fichiers modifiés, les fichiers à supprimer manuellement, les validations exécutées et les validations non exécutées. +- Le zip delta ne doit pas contenir de fichiers inchangés. +- Le zip delta ne doit pas modifier `CHANGELOG.md` sauf validation explicite d'une version. +- Les suppressions de fichiers ou dossiers doivent être indiquées dans `delta.md`, car l'extraction d'un zip ne supprime pas automatiquement les anciens fichiers. +- Les prompts de session doivent rappeler ce format de livraison et la règle `delta-fix-NNN`. + +## Constantes des décodeurs + +- Chaque crate `kb_decoder_*` classifié avec un `program_id` doit avoir un fichier `src/constants.rs`. +- `src/constants.rs` contient les `PROGRAM_ID`, puis les discriminators, sélecteurs, opcodes, constantes Borsh et constantes de décodage quand elles seront connues. +- `src/lib.rs` doit réexporter les constantes nécessaires avec `pub use crate::constants::...`. +- `src/decoder.rs` doit utiliser les constantes réexportées par la crate, par exemple `crate::AMM_PUMP_SWAP_PROGRAM_ID`, et non `crate::constants::AMM_PUMP_SWAP_PROGRAM_ID`. +- Les literals de `program_id` ne doivent pas rester dans `program_ids()`, sauf dans un fichier `constants.rs`. + +## Identifiants de programmes + +Les `program_id` connus doivent être définis une seule fois dans `kb_program_ids`. Les crates de décodeur, d’exécuteur, de store, d’application ou d’outil doivent référencer directement `kb_program_ids::XXX_PROGRAM_ID`. Les fichiers `constants.rs` locaux ne doivent pas redéfinir ces chaînes ; ils restent réservés aux constantes internes du module, par exemple discriminators, opcodes, seeds, index de comptes ou layouts. + +## Validation frontend Tauri + +- Pour `kb_app_demo`, ne pas lancer `npm --prefix kb_app_demo run build` séparément : la validation frontend de développement est réalisée par `cargo tauri dev -c kb_app_demo/tauri.conf.json`, qui démarre et pilote le serveur Vite. diff --git a/docs/legacy-khadhroony-bot2/RUST_RULES.md b/docs/legacy-khadhroony-bot2/RUST_RULES.md new file mode 100644 index 0000000..c1262df --- /dev/null +++ b/docs/legacy-khadhroony-bot2/RUST_RULES.md @@ -0,0 +1,134 @@ + + + +# Règles Rust générales + +Ce fichier contient les règles normatives applicables à tous les projets Rust. Elles sont indépendantes de `khadhroony-bot2` et doivent pouvoir être réutilisées telles quelles dans un autre workspace. + +## En-têtes et versions de fichiers + +- Tout fichier texte qui supporte des commentaires commence par une ligne indiquant son chemin relatif dans le projet, puis une ligne `version` entière. +- La version d'un fichier est incrémentée à chaque modification après validation de sa version précédente. +- Les fichiers Rust utilisent `// file: ...` et `// version: N`. +- Les fichiers Markdown utilisent `` et ``. +- Tous les fichiers texte se terminent par exactement une fin de ligne. + +## Langue et documentation + +- Les commentaires et rustdocs du code sont rédigés en anglais. +- Les documents Markdown du projet sont rédigés dans la langue documentaire choisie par le projet. +- Tout élément `pub` ou `pub(crate)` possède une rustdoc utile au point de déclaration. +- Toute réexportation `pub use` ou `pub(crate) use` dans `lib.rs` ou `main.rs` possède également sa propre rustdoc copiée ou reformulée de manière équivalente. La documentation du point d'entrée doit permettre de comprendre l'API sans ouvrir le module interne. + +## Édition et lints obligatoires + +- L'édition Rust cible est Rust 2024, sauf contrainte explicitement documentée. +- Chaque `lib.rs` et `main.rs` contient : + - `#![warn(missing_docs)]` ; + - `#![deny(unreachable_pub)]` ; + - `#![forbid(unsafe_code)]`. +- Les lints Clippy obligatoires sont déclarés au niveau workspace et hérités par toutes les crates. +- Les règles minimales sont : interdiction de `unwrap`, `expect`, `?`, retours implicites, code `unsafe`, APIs publiques inaccessibles et imports globaux non justifiés. +- Les tests peuvent disposer d'exceptions limitées pour `unwrap` et `expect` uniquement lorsqu'elles sont explicitement autorisées par la configuration Clippy. + +La configuration workspace doit au minimum déclarer : + +```toml +[workspace.lints.rust] +missing_docs = "warn" +unreachable_pub = "deny" +unsafe_code = "forbid" + +[workspace.lints.clippy] +unwrap_used = "deny" +expect_used = "deny" +implicit_return = "deny" +needless_return = "allow" +useless_vec = "deny" +question_mark = "deny" +question_mark_used = "deny" +needless_match = "allow" +manual_ok_err = "allow" +manual_unwrap_or = "allow" +manual_map = "allow" +match_like_matches_macro = "allow" +single_match = "allow" +manual_unwrap_or_default = "allow" +manual_find = "allow" +explicit_counter_loop = "allow" +get_first = "allow" +implicit_saturating_sub = "allow" +``` + +## Formatage + +- `cargo fmt --all` est exécuté après application de chaque delta ou correctif et avant les tests. +- Les fichiers Rust ne contiennent pas de lignes vides à l'intérieur d'une fonction, d'une structure, d'une énumération ou d'une implémentation courte. +- Les lignes vides séparent uniquement les fonctions, blocs `impl`, types et sections logiques. +- Les exports de `lib.rs` ou `main.rs` ne contiennent aucune ligne vide à l'intérieur d'une série homogène de `pub use` ou `pub(crate) use`. +- Les séries `pub use` et `pub(crate) use` forment deux groupes séparés lorsqu'elles coexistent. + +## Imports et chemins + +- `use` est interdit pour les constantes, fonctions, structures, énumérations, unions, alias de types, modules et macros. +- `use` est autorisé uniquement pour un trait lorsque la résolution de méthode, une macro de dérivation ou une contrainte de langage l'exige réellement. +- Un import de trait doit rester étroit et ne doit jamais importer simultanément des éléments non-traits par accolades. +- Les imports globaux, glob imports et imports groupés par accolades sont interdits. +- Pour un élément fourni par une crate externe au workspace, utiliser directement le chemin public le plus court exposé par cette crate. Exemple : utiliser `external_crate::Struct`, jamais `external_crate::module::Struct` si `external_crate::Struct` existe, et jamais `use external_crate::Struct`. +- Pour un élément `pub` déclaré dans le workspace : + - il est réexporté depuis le `lib.rs` ou `main.rs` de sa crate propriétaire ; + - depuis une autre crate, il est appelé via `owner_crate::Item` ; + - depuis sa propre crate, il est appelé via `crate::Item`, même depuis son module de déclaration. +- Pour un élément `pub(crate)` : + - il est réexporté depuis le `lib.rs` ou `main.rs` via `pub(crate) use` ; + - il est appelé via `crate::Item`, même depuis son module de déclaration. +- Un élément strictement privé à un module n'est pas réexporté et est appelé par son nom local uniquement. Il est interdit d'utiliser un chemin long comme `crate::module::helper` ou `owner_crate::module::helper` pour un helper privé du module courant. +- Dans un sous-module de tests, un élément privé du module parent est appelé via `super::Item`. Un élément `pub` ou `pub(crate)` continue d’être appelé via le point d’entrée de crate le plus court, par exemple `crate::Item`. +- Les exports ne sont jamais groupés avec des accolades. Un type, une fonction, une constante ou un alias correspond à une ligne de réexport distincte. + +## Visibilité et API de crate + +- Aucun `pub mod` n'est autorisé. Les modules restent privés et l'API est constituée par des réexports explicites. +- Un élément `pub` inaccessible depuis le point d'entrée de sa crate est une erreur de conception, pas un simple avertissement. +- Un élément `pub(crate)` utilisé hors de son module est réexporté au niveau du point d'entrée de la crate. +- Les chemins internes de modules ne font pas partie de l'API stable. +- Les méthodes inhérentes publiques restent appelées via le type réexporté ; les fonctions libres publiques sont appelées via le point d'entrée de crate. + +## Helpers et réutilisation + +- Un helper répété dans plusieurs modules d'une même crate est déplacé dans un module commun explicite, généralement `helper.rs` ou un module spécialisé plus précis. +- Le nom `helpers.rs` ou `helper.rs` n'est utilisé que si aucune responsabilité métier plus précise ne convient. +- Un helper général réutilisable par plusieurs crates est déplacé dans une crate commune appropriée et exposé publiquement. +- Les types d'erreur généraux, identifiants de programme, primitives de validation et fonctions de sérialisation communes ne doivent pas être dupliqués entre crates. +- La mutualisation ne doit pas créer de dépendance cyclique ni déplacer un comportement métier spécifique dans une crate générique. + +## Gestion des erreurs et contrôle de flux + +- `unwrap`, `expect` et `panic` sont interdits dans le code de production. +- L'opérateur `?` est interdit dans les chemins de production ; utiliser des `match` explicites avec erreurs contextualisées. +- `anyhow` et `thiserror` ne sont pas utilisés par défaut. +- Les erreurs publiques sont typées lorsque leur contrat est stable ; les diagnostics dynamiques restent bornés. +- Les retours sont explicites conformément au lint `clippy::implicit_return`. + +## Sécurité et dépendances + +- Le code `unsafe` est interdit. +- Une dépendance n'est ajoutée que si elle est réellement utilisée dans le chemin de compilation concerné. +- Une dépendance utilisée uniquement dans les tests reste dans `[dev-dependencies]`. +- Les features sont minimales et explicites. + +## Contrôle avant livraison + +Chaque livraison Rust exécute au minimum, dans cet ordre : + +```bash +cargo fmt --all +python3 scripts/audit_rust_general_rules.py +python3 scripts/audit_khadhroony_workspace_rules.py +cargo test --workspace +cargo clippy --workspace --all-targets +``` + +Un projet peut utiliser une sélection de tests plus étroite pendant le développement, mais la fermeture d'une version exige le contrôle global. + +Le wrapper `python3 scripts/audit_rust_workspace_rules.py` exécute les deux audits sans fusionner leurs responsabilités. diff --git a/idls/001.README.md b/idls/001.README.md new file mode 100644 index 0000000..994a228 --- /dev/null +++ b/idls/001.README.md @@ -0,0 +1,8 @@ + + + +# IDL locales + +Ce répertoire contient les IDL locales utilisées comme sources de connaissance pour les décodeurs. + +Les fichiers JSON ne doivent pas être modifiés pour ajouter des commentaires, car le format JSON standard ne les supporte pas. diff --git a/idls/bonkswap.BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p.json b/idls/bonkswap.BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p.json new file mode 100644 index 0000000..80b7b11 --- /dev/null +++ b/idls/bonkswap.BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p.json @@ -0,0 +1 @@ +{"version":"0.1.1","name":"bonkswap","instructions":[{"name":"createPool","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":true},{"name":"poolYAccount","isMut":true,"isSigner":true},{"name":"adminXAccount","isMut":true,"isSigner":false},{"name":"adminYAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"projectOwner","isMut":false,"isSigner":false},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"lpFee","type":{"defined":"FixedPoint"}},{"name":"buybackFee","type":{"defined":"FixedPoint"}},{"name":"projectFee","type":{"defined":"FixedPoint"}},{"name":"mercantiFee","type":{"defined":"FixedPoint"}},{"name":"initialTokenX","type":{"defined":"Token"}},{"name":"initialTokenY","type":{"defined":"Token"}},{"name":"bump","type":"u8"}]},{"name":"createProvider","accounts":[{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"ownerXAccount","isMut":true,"isSigner":false},{"name":"ownerYAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"tokenXAmount","type":{"defined":"Token"}},{"name":"tokenYAmount","type":{"defined":"Token"}},{"name":"bump","type":"u8"}]},{"name":"createState","accounts":[{"name":"state","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"addTokens","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarco","isMut":true,"isSigner":false},{"name":"tokenProjectFirst","isMut":true,"isSigner":false},{"name":"tokenProjectSecond","isMut":true,"isSigner":false},{"name":"ownerXAccount","isMut":true,"isSigner":false},{"name":"ownerYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"ownerMarcoAccount","isMut":true,"isSigner":false},{"name":"ownerProjectFirstAccount","isMut":true,"isSigner":false},{"name":"ownerProjectSecondAccount","isMut":true,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"deltaX","type":{"defined":"Token"}},{"name":"deltaY","type":{"defined":"Token"}}]},{"name":"withdrawBuyback","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"buybackXAccount","isMut":true,"isSigner":false},{"name":"buybackYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"swap","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"swapperXAccount","isMut":true,"isSigner":false},{"name":"swapperYAccount","isMut":true,"isSigner":false},{"name":"swapper","isMut":true,"isSigner":true},{"name":"referrerXAccount","isMut":true,"isSigner":false},{"name":"referrerYAccount","isMut":true,"isSigner":false},{"name":"referrer","isMut":true,"isSigner":false},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"deltaIn","type":{"defined":"Token"}},{"name":"priceLimit","type":{"defined":"FixedPoint"}},{"name":"xToY","type":"bool"}]},{"name":"withdrawShares","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarco","isMut":true,"isSigner":false},{"name":"tokenProjectFirst","isMut":true,"isSigner":false},{"name":"tokenProjectSecond","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"ownerXAccount","isMut":true,"isSigner":false},{"name":"ownerYAccount","isMut":true,"isSigner":false},{"name":"ownerMarcoAccount","isMut":true,"isSigner":false},{"name":"ownerProjectFirstAccount","isMut":true,"isSigner":false},{"name":"ownerProjectSecondAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"shares","type":{"defined":"Token"}}]},{"name":"withdrawLpFee","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"ownerXAccount","isMut":true,"isSigner":false},{"name":"ownerYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"withdrawProjectFee","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"projectOwnerXAccount","isMut":true,"isSigner":false},{"name":"projectOwnerYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"projectOwner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"createFarm","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarco","isMut":false,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":true},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"supply","type":{"defined":"Token"}},{"name":"duration","type":"u64"},{"name":"bump","type":"u8"}]},{"name":"createDualFarm","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarco","isMut":false,"isSigner":false},{"name":"tokenProjectFirst","isMut":false,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":true},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":true},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"adminProjectFirstAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"supplyMarco","type":{"defined":"Token"}},{"name":"supplyProjectFirst","type":{"defined":"Token"}},{"name":"duration","type":"u64"},{"name":"bump","type":"u8"}]},{"name":"createTripleFarm","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarco","isMut":false,"isSigner":false},{"name":"tokenProjectFirst","isMut":false,"isSigner":false},{"name":"tokenProjectSecond","isMut":false,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":true},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":true},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":true},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"adminProjectFirstAccount","isMut":true,"isSigner":false},{"name":"adminProjectSecondAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"supplyMarco","type":{"defined":"Token"}},{"name":"supplyProjectFirst","type":{"defined":"Token"}},{"name":"supplyProjectSecond","type":{"defined":"Token"}},{"name":"duration","type":"u64"},{"name":"bump","type":"u8"}]},{"name":"withdrawRewards","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarco","isMut":true,"isSigner":false},{"name":"tokenProjectFirst","isMut":true,"isSigner":false},{"name":"tokenProjectSecond","isMut":true,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"ownerMarcoAccount","isMut":true,"isSigner":false},{"name":"ownerProjectFirstAccount","isMut":true,"isSigner":false},{"name":"ownerProjectSecondAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"closePool","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"buybackXAccount","isMut":true,"isSigner":false},{"name":"buybackYAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"withdrawMercantiFee","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"mercantiXAccount","isMut":true,"isSigner":false},{"name":"mercantiYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"addSupply","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"adminProjectFirstAccount","isMut":true,"isSigner":false},{"name":"adminProjectSecondAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"supplyMarco","type":{"defined":"Token"}},{"name":"supplyProjectFirst","type":{"defined":"Token"}},{"name":"supplyProjectSecond","type":{"defined":"Token"}},{"name":"duration","type":"u64"}]},{"name":"updateFees","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false}],"args":[{"name":"newBuybackFee","type":{"defined":"FixedPoint"}},{"name":"newProjectFee","type":{"defined":"FixedPoint"}},{"name":"newProviderFee","type":{"defined":"FixedPoint"}},{"name":"newMercantiFee","type":{"defined":"FixedPoint"}}]},{"name":"resetFarm","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarco","isMut":false,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"adminProjectFirstAccount","isMut":true,"isSigner":false},{"name":"adminProjectSecondAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"updateRewardTokens","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"tokenMarco","isMut":false,"isSigner":false},{"name":"newTokenMarcoAccount","isMut":true,"isSigner":true},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"Farm","type":{"kind":"struct","fields":[{"name":"pool","type":"publicKey"},{"name":"tokens","type":{"array":["publicKey",3]}},{"name":"tokenAccounts","type":{"array":["publicKey",3]}},{"name":"supply","type":{"array":[{"defined":"Token"},3]}},{"name":"supplyLeft","type":{"array":[{"defined":"Token"},3]}},{"name":"accumulatedSecondsPerShare","type":{"defined":"FixedPoint"}},{"name":"offsetSecondsPerShare","type":{"defined":"FixedPoint"}},{"name":"startTime","type":"u64"},{"name":"endTime","type":"u64"},{"name":"lastUpdate","type":"u64"},{"name":"bump","type":"u8"},{"name":"farmType","type":{"defined":"FarmType"}}]}},{"name":"PoolV2","type":{"kind":"struct","fields":[{"name":"tokenX","type":"publicKey"},{"name":"tokenY","type":"publicKey"},{"name":"poolXAccount","type":"publicKey"},{"name":"poolYAccount","type":"publicKey"},{"name":"admin","type":"publicKey"},{"name":"projectOwner","type":"publicKey"},{"name":"tokenXReserve","type":{"defined":"Token"}},{"name":"tokenYReserve","type":{"defined":"Token"}},{"name":"selfShares","type":{"defined":"Token"}},{"name":"allShares","type":{"defined":"Token"}},{"name":"buybackAmountX","type":{"defined":"Token"}},{"name":"buybackAmountY","type":{"defined":"Token"}},{"name":"projectAmountX","type":{"defined":"Token"}},{"name":"projectAmountY","type":{"defined":"Token"}},{"name":"mercantiAmountX","type":{"defined":"Token"}},{"name":"mercantiAmountY","type":{"defined":"Token"}},{"name":"lpAccumulatorX","type":{"defined":"FixedPoint"}},{"name":"lpAccumulatorY","type":{"defined":"FixedPoint"}},{"name":"constK","type":{"defined":"Product"}},{"name":"price","type":{"defined":"FixedPoint"}},{"name":"lpFee","type":{"defined":"FixedPoint"}},{"name":"buybackFee","type":{"defined":"FixedPoint"}},{"name":"projectFee","type":{"defined":"FixedPoint"}},{"name":"mercantiFee","type":{"defined":"FixedPoint"}},{"name":"farmCount","type":"u64"},{"name":"poolBump","type":"u8"},{"name":"lpToken","type":"publicKey"},{"name":"lpTokenMintBump","type":"u8"},{"name":"padding","type":{"array":["u64",8]}}]}},{"name":"Pool","type":{"kind":"struct","fields":[{"name":"tokenX","type":"publicKey"},{"name":"tokenY","type":"publicKey"},{"name":"poolXAccount","type":"publicKey"},{"name":"poolYAccount","type":"publicKey"},{"name":"admin","type":"publicKey"},{"name":"projectOwner","type":"publicKey"},{"name":"tokenXReserve","type":{"defined":"Token"}},{"name":"tokenYReserve","type":{"defined":"Token"}},{"name":"selfShares","type":{"defined":"Token"}},{"name":"allShares","type":{"defined":"Token"}},{"name":"buybackAmountX","type":{"defined":"Token"}},{"name":"buybackAmountY","type":{"defined":"Token"}},{"name":"projectAmountX","type":{"defined":"Token"}},{"name":"projectAmountY","type":{"defined":"Token"}},{"name":"mercantiAmountX","type":{"defined":"Token"}},{"name":"mercantiAmountY","type":{"defined":"Token"}},{"name":"lpAccumulatorX","type":{"defined":"FixedPoint"}},{"name":"lpAccumulatorY","type":{"defined":"FixedPoint"}},{"name":"constK","type":{"defined":"Product"}},{"name":"price","type":{"defined":"FixedPoint"}},{"name":"lpFee","type":{"defined":"FixedPoint"}},{"name":"buybackFee","type":{"defined":"FixedPoint"}},{"name":"projectFee","type":{"defined":"FixedPoint"}},{"name":"mercantiFee","type":{"defined":"FixedPoint"}},{"name":"farmCount","type":"u64"},{"name":"bump","type":"u8"}]}},{"name":"Provider","type":{"kind":"struct","fields":[{"name":"tokenX","type":"publicKey"},{"name":"tokenY","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"shares","type":{"defined":"Token"}},{"name":"lastFeeAccumulatorX","type":{"defined":"FixedPoint"}},{"name":"lastFeeAccumulatorY","type":{"defined":"FixedPoint"}},{"name":"lastSecondsPerShare","type":{"defined":"FixedPoint"}},{"name":"lastWithdrawTime","type":"u64"},{"name":"tokensOwedX","type":{"defined":"Token"}},{"name":"tokensOwedY","type":{"defined":"Token"}},{"name":"currentFarmCount","type":"u64"},{"name":"bump","type":"u8"}]}},{"name":"State","type":{"kind":"struct","fields":[{"name":"admin","type":"publicKey"},{"name":"programAuthority","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"nonce","type":"u8"}]}}],"types":[{"name":"FixedPoint","type":{"kind":"struct","fields":[{"name":"v","type":"u128"}]}},{"name":"Token","type":{"kind":"struct","fields":[{"name":"v","type":"u64"}]}},{"name":"Product","type":{"kind":"struct","fields":[{"name":"v","type":"u128"}]}},{"name":"FarmType","type":{"kind":"enum","variants":[{"name":"Single"},{"name":"Dual"},{"name":"Triple"}]}}],"errors":[{"code":6000,"name":"DeltaTooBig","msg":"Delta greater than provider's tokens"},{"code":6001,"name":"TokenUnderflow","msg":"Token amount underflow"},{"code":6002,"name":"WrongRatio","msg":"Wrong tokens ratio"},{"code":6003,"name":"TooMuchShares","msg":"Too much shares provided"},{"code":6004,"name":"SwapToBig","msg":"Swap too big"},{"code":6005,"name":"FeeExceeded","msg":"Fee exceeded 100%"},{"code":6007,"name":"ScalesNotEqual","msg":"Scales have to be equal"},{"code":6008,"name":"FeeExceededDeltaOut","msg":"Fees exceeded delta_out"},{"code":6009,"name":"PriceLimitExceeded","msg":"Price limit exceeded"},{"code":6010,"name":"MintMismatch","msg":"Mint mismatch"},{"code":6011,"name":"TokensAreTheSame","msg":"Tokens are the same"},{"code":6012,"name":"WrongFarm","msg":"Cannot add supply to wrong farm"},{"code":6013,"name":"RewardsExceedingSupply","msg":"Cannot withdraw rewards exceeding supply left"},{"code":6014,"name":"FarmNotEnded","msg":"Farm has not ended, cannot add additional rewards"},{"code":6015,"name":"ZeroAmount","msg":"Must provide a nonzero amount"},{"code":6016,"name":"InvariantDecreased","msg":"Invariant has changed"}]} \ No newline at end of file diff --git a/idls/boop_fun.boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4.json b/idls/boop_fun.boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4.json new file mode 100644 index 0000000..e130eea --- /dev/null +++ b/idls/boop_fun.boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4.json @@ -0,0 +1 @@ +{"address":"boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4","metadata":{"name":"boop","version":"0.3.0","spec":"0.1.0","description":"Boop is a token launcher with fee sharing"},"instructions":[{"name":"add_operators","discriminator":[165,199,62,214,81,54,4,150],"accounts":[{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"authority","writable":true,"signer":true,"relations":["config"]},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"operators","type":{"vec":"pubkey"}}]},{"name":"buy_token","discriminator":[138,127,14,91,38,87,115,105],"accounts":[{"name":"mint"},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"trading_fees_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,114,97,100,105,110,103,95,102,101,101,115,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_sol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"recipient_token_account"},{"name":"buyer","writable":true,"signer":true},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"wsol","address":"So11111111111111111111111111111111111111112"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"buy_amount","type":"u64"},{"name":"amount_out_min","type":"u64"}]},{"name":"cancel_authority_transfer","discriminator":[94,131,125,184,183,24,125,229],"accounts":[{"name":"authority","signer":true,"relations":["config"]},{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"close_bonding_curve_vault","discriminator":[189,71,189,239,113,66,59,189],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"operator","writable":true,"signer":true},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"mint"},{"name":"recipient_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"recipient"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"recipient","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","docs":["System program for creating associated token if needed"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[]},{"name":"collect_meteora_trading_fees","discriminator":[249,95,126,91,81,162,83,250],"accounts":[{"name":"operator","writable":true,"signer":true},{"name":"protocol_fee_recipient","relations":["config"]},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"pool_authority"},{"name":"pool"},{"name":"position","writable":true},{"name":"token_a_account","docs":["The token account to receive token a"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"protocol_fee_recipient"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_a_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_b_account","docs":["The token account to receive token b"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,114,97,100,105,110,103,95,102,101,101,115,95,118,97,117,108,116]},{"kind":"account","path":"token_a_mint"}]}},{"name":"token_a_vault","writable":true},{"name":"token_b_vault","writable":true},{"name":"token_a_mint"},{"name":"token_b_mint"},{"name":"position_nft_account","writable":true},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority"},{"name":"cp_amm_program","address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"}],"args":[]},{"name":"collect_meteora_trading_fees_v2","discriminator":[96,39,109,46,5,161,15,57],"accounts":[{"name":"operator","writable":true,"signer":true},{"name":"protocol_fee_recipient","relations":["config"]},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"pool_authority"},{"name":"pool"},{"name":"position","writable":true},{"name":"token_a_account","docs":["The token account to receive token a"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"protocol_fee_recipient"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_a_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_b_account","docs":["The token account to receive token b"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,116,95,103,114,97,100,95,116,114,97,100,105,110,103,95,102,101,101,115,95,118,97,117,108,116]},{"kind":"account","path":"token_a_mint"}]}},{"name":"token_a_vault","writable":true},{"name":"token_b_vault","writable":true},{"name":"token_a_mint"},{"name":"token_b_mint"},{"name":"position_nft_account","writable":true},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority"},{"name":"cp_amm_program","address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"collect_trading_fees","discriminator":[189,38,205,234,81,77,25,1],"accounts":[{"name":"operator","writable":true,"signer":true},{"name":"protocol_fee_recipient","relations":["config"]},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"lock_program","address":"LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE"},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[108,111,99,107,95,99,112,95,97,117,116,104,111,114,105,116,121,95,115,101,101,100]}],"program":{"kind":"account","path":"lock_program"}}},{"name":"fee_nft_account","docs":["Fee token account"]},{"name":"locked_liquidity","docs":["Store the locked the information of liquidity"],"writable":true},{"name":"cpmm_program","address":"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"},{"name":"cp_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"pool_state","writable":true},{"name":"lp_mint","docs":["lp mint","address = pool_state.lp_mint"],"writable":true},{"name":"recipient_token_0_account","docs":["The token account for receive token_0"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"protocol_fee_recipient"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"vault_0_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"recipient_token_1_account","docs":["The token account for receive token_1"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,114,97,100,105,110,103,95,102,101,101,115,95,118,97,117,108,116]},{"kind":"account","path":"token_0_vault.mint"}]}},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"locked_lp_vault","docs":["locked lp token account"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"authority"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"system_program","docs":["System program"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["Associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program","docs":["token Program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[]},{"name":"collect_trading_fees_v2","discriminator":[180,138,160,155,243,88,168,7],"accounts":[{"name":"operator","writable":true,"signer":true},{"name":"protocol_fee_recipient","relations":["config"]},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"lock_program","address":"LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE"},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[108,111,99,107,95,99,112,95,97,117,116,104,111,114,105,116,121,95,115,101,101,100]}],"program":{"kind":"account","path":"lock_program"}}},{"name":"fee_nft_account","docs":["Fee token account"]},{"name":"locked_liquidity","docs":["Store the locked the information of liquidity"],"writable":true},{"name":"cpmm_program","address":"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"},{"name":"cp_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"pool_state","writable":true},{"name":"lp_mint","docs":["lp mint","address = pool_state.lp_mint"],"writable":true},{"name":"recipient_token_0_account","docs":["The token account for receive token_0"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"protocol_fee_recipient"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"vault_0_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"recipient_token_1_account","docs":["The token account for receive token_1"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,116,95,103,114,97,100,95,116,114,97,100,105,110,103,95,102,101,101,115,95,118,97,117,108,116]},{"kind":"account","path":"token_0_vault.mint"}]}},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"locked_lp_vault","docs":["locked lp token account"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"authority"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"system_program","docs":["System program"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["Associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program","docs":["token Program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[]},{"name":"complete_authority_transfer","discriminator":[81,233,91,132,175,31,151,141],"accounts":[{"name":"pending_authority","signer":true,"relations":["config"]},{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_meteora_pool","discriminator":[246,254,33,37,225,176,41,232],"accounts":[{"name":"operator","writable":true,"signer":true},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"cp_amm_config"},{"name":"pool_authority"},{"name":"pool","writable":true},{"name":"position","writable":true},{"name":"position_nft_mint","writable":true,"signer":true},{"name":"position_nft_account","writable":true},{"name":"token_a_mint"},{"name":"token_b_mint","address":"So11111111111111111111111111111111111111112"},{"name":"token_a_vault","writable":true},{"name":"token_b_vault","writable":true},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"token_a_mint"}]}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"token_a_mint"}]}},{"name":"bonding_curve_wsol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,119,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"token_a_mint"}]}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority"},{"name":"cp_amm_program","address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"}],"args":[]},{"name":"create_raydium_pool","discriminator":[65,45,119,77,204,178,84,2],"accounts":[{"name":"cpmm_program","address":"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"pool_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"token_0_mint"},{"kind":"account","path":"token_1_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"token_0_mint","writable":true},{"name":"token_1_mint","docs":["Token_1 mint, the key must be greater than token_0 mint."],"address":"So11111111111111111111111111111111111111112"},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,108,112,95,109,105,110,116]},{"kind":"account","path":"pool_state"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"token_0_mint"}]}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"token_0_mint"}]}},{"name":"bonding_curve_wsol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,119,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"token_0_mint"}]}},{"name":"creator_lp_token","writable":true},{"name":"token_0_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_0_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"token_1_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_1_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"create_pool_fee","docs":["create pool fee account"],"writable":true,"address":"DNXgeM9EiiaAbaWvwjHj9fQQLAX5ZsfHyvmYUNRAdNC8"},{"name":"observation_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"operator","writable":true,"signer":true},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[]},{"name":"create_raydium_random_pool","discriminator":[78,44,173,29,132,180,4,172],"accounts":[{"name":"cpmm_program","address":"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"pool_state","writable":true,"signer":true},{"name":"token_0_mint","writable":true},{"name":"token_1_mint","docs":["Token_1 mint, the key must be greater than token_0 mint."],"address":"So11111111111111111111111111111111111111112"},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,108,112,95,109,105,110,116]},{"kind":"account","path":"pool_state"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"token_0_mint"}]}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"token_0_mint"}]}},{"name":"bonding_curve_wsol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,119,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"token_0_mint"}]}},{"name":"creator_lp_token","writable":true},{"name":"token_0_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_0_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"token_1_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_1_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"create_pool_fee","docs":["create pool fee account"],"writable":true,"address":"DNXgeM9EiiaAbaWvwjHj9fQQLAX5ZsfHyvmYUNRAdNC8"},{"name":"observation_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"operator","writable":true,"signer":true},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[]},{"name":"create_token","discriminator":[84,52,204,228,24,140,234,75],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"metadata","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]}}},{"name":"mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,105,110,116]},{"kind":"account","path":"payer"},{"kind":"arg","path":"salt"}]}},{"name":"payer","writable":true,"signer":true},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"}],"args":[{"name":"salt","type":"u64"},{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"create_token_fallback","discriminator":[253,184,126,199,235,232,172,162],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"metadata","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]}}},{"name":"mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,105,110,116]},{"kind":"arg","path":"salt"}]}},{"name":"payer","writable":true,"signer":true},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"}],"args":[{"name":"salt","type":"u64"},{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"deploy_bonding_curve","discriminator":[180,89,199,76,168,236,217,138],"accounts":[{"name":"mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,105,110,116]},{"kind":"account","path":"payer"},{"kind":"arg","path":"salt"}]}},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_sol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"creator","type":"pubkey"},{"name":"salt","type":"u64"}]},{"name":"deploy_bonding_curve_fallback","discriminator":[53,230,172,84,77,174,22,61],"accounts":[{"name":"mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,105,110,116]},{"kind":"arg","path":"salt"}]}},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_sol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"creator","type":"pubkey"},{"name":"salt","type":"u64"}]},{"name":"deposit_into_raydium","discriminator":[168,89,99,30,117,49,88,224],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"operator","writable":true,"signer":true},{"name":"operator_wsol_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"operator"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_1_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"pool_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"token_0_mint"},{"kind":"account","path":"token_1_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"token_0_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_0_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"token_1_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_1_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"token_0_mint"}]}},{"name":"bonding_curve_wsol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,119,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"token_0_mint"}]}},{"name":"token_program","docs":["token Program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","docs":["System program"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["Program to create an ATA for the operator to recoup any potential remaining WSOL"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,108,112,95,109,105,110,116]},{"kind":"account","path":"pool_state"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"cpmm_program","address":"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"},{"name":"owner_lp_token","writable":true},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"token_0_mint"}]}},{"name":"token_0_mint","writable":true},{"name":"token_1_mint","docs":["token_1 mint, the key must greater than token_0 mint"],"address":"So11111111111111111111111111111111111111112"}],"args":[{"name":"lp_token_amount","type":"u64"},{"name":"maximum_token_0_amount","type":"u64"},{"name":"maximum_token_1_amount","type":"u64"}]},{"name":"graduate","discriminator":[45,235,225,181,17,218,64,130],"accounts":[{"name":"mint"},{"name":"wsol","address":"So11111111111111111111111111111111111111112"},{"name":"protocol_fee_recipient","writable":true},{"name":"token_distributor"},{"name":"token_distributor_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"token_distributor"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve_sol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_wsol_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,119,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"operator","writable":true,"signer":true},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[]},{"name":"initialize","discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"authority","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"protocol_fee_recipient","type":"pubkey"},{"name":"token_distributor","type":"pubkey"}]},{"name":"initiate_authority_transfer","discriminator":[210,43,101,215,119,140,106,218],"accounts":[{"name":"authority","signer":true,"relations":["config"]},{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"new_authority","type":"pubkey"}]},{"name":"lock_raydium_liquidity","discriminator":[173,255,148,6,122,99,140,22],"accounts":[{"name":"lock_program","address":"LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE"},{"name":"vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[108,111,99,107,95,99,112,95,97,117,116,104,111,114,105,116,121,95,115,101,101,100]}],"program":{"kind":"account","path":"lock_program"}}},{"name":"fee_nft_owner"},{"name":"fee_nft_mint","writable":true,"signer":true},{"name":"fee_nft_account","writable":true},{"name":"pool_state","writable":true},{"name":"locked_liquidity","docs":["Store the locked information of liquidity"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,111,99,107,101,100,95,108,105,113,117,105,100,105,116,121]},{"kind":"account","path":"fee_nft_mint"}],"program":{"kind":"account","path":"lock_program"}}},{"name":"lp_mint","docs":["The mint of liquidity token"],"writable":true},{"name":"liquidity_owner_lp","docs":["liquidity owner lp token account"],"writable":true},{"name":"locked_lp_vault","writable":true},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"operator","writable":true,"signer":true},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"token_0_vault.mint"}]}},{"name":"metadata_account","docs":["To store metaplex metadata"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]},{"kind":"account","path":"fee_nft_mint"}],"program":{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]}}},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the new account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to create/transfer mint/token account"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving fee NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_program","docs":["Program to create NFT metadata accunt"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"}],"args":[]},{"name":"remove_operators","discriminator":[42,20,89,83,222,37,4,109],"accounts":[{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"authority","writable":true,"signer":true,"relations":["config"]},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"operators","type":{"vec":"pubkey"}}]},{"name":"sell_token","discriminator":[109,61,40,187,230,176,135,174],"accounts":[{"name":"mint"},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"trading_fees_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,114,97,100,105,110,103,95,102,101,101,115,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_sol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"seller_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"seller"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"seller","writable":true,"signer":true},{"name":"recipient","writable":true},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"sell_amount","type":"u64"},{"name":"amount_out_min","type":"u64"}]},{"name":"split_post_graduation_trading_fees","discriminator":[241,178,177,69,38,187,58,176],"accounts":[{"name":"operator","writable":true,"signer":true},{"name":"mint"},{"name":"wsol","address":"So11111111111111111111111111111111111111112"},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"trading_fees_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,116,95,103,114,97,100,95,116,114,97,100,105,110,103,95,102,101,101,115,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"fee_splitter_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"fee_splitter_config"},{"name":"fee_splitter_creator_vault","writable":true},{"name":"fee_splitter_vault_authority","writable":true},{"name":"fee_splitter_creator_vault_authority","writable":true},{"name":"fee_splitter_staking_mint"},{"name":"fee_splitter_wsol_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_splitter_vault_authority"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"wsol"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"fee_splitter_creator_vault_authority_wsol_vault","writable":true},{"name":"fee_splitter_treasury_wsol_vault","writable":true},{"name":"fee_splitter_team_wsol_vault","writable":true},{"name":"fee_splitter_reward_pool","writable":true},{"name":"fee_splitter_reward_pool_staking_vault","writable":true},{"name":"fee_splitter_reward_pool_reward_vault","writable":true},{"name":"fee_splitter_reward_pool_program"}],"args":[]},{"name":"split_trading_fees","discriminator":[96,126,225,47,185,213,50,58],"accounts":[{"name":"operator","writable":true,"signer":true},{"name":"mint"},{"name":"wsol","address":"So11111111111111111111111111111111111111112"},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"trading_fees_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,114,97,100,105,110,103,95,102,101,101,115,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"fee_splitter_program","address":"boopEYztaEYSnajfMtjcRysyzyRcchgKsPboRZEbnJi"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"fee_splitter_config"},{"name":"fee_splitter_creator_vault","writable":true},{"name":"fee_splitter_vault_authority","writable":true},{"name":"fee_splitter_creator_vault_authority","writable":true},{"name":"fee_splitter_staking_mint"},{"name":"fee_splitter_wsol_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_splitter_vault_authority"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"wsol"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"fee_splitter_creator_vault_authority_wsol_vault","writable":true},{"name":"fee_splitter_treasury_wsol_vault","writable":true},{"name":"fee_splitter_team_wsol_vault","writable":true},{"name":"fee_splitter_reward_pool","writable":true},{"name":"fee_splitter_reward_pool_staking_vault","writable":true},{"name":"fee_splitter_reward_pool_reward_vault","writable":true},{"name":"fee_splitter_reward_pool_program"}],"args":[]},{"name":"swap_sol_for_tokens_on_raydium","discriminator":[107,248,131,239,152,234,54,35],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"output_token_mint"}]}},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"operator","writable":true,"signer":true},{"name":"vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"pool_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"output_token_mint"},{"kind":"account","path":"input_token_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"input_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"input_token_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"output_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"output_token_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"output_token_mint"}]}},{"name":"bonding_curve_wsol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,119,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"output_token_mint"}]}},{"name":"output_token_mint","docs":["token_0 mint, the key must smaller than token_1 mint"]},{"name":"input_token_mint","docs":["token_1 mint, the key must greater than token_0 mint"],"address":"So11111111111111111111111111111111111111112"},{"name":"token_program","docs":["input_token_mint and output_token_mint have the same token program"]},{"name":"cp_swap_program","address":"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"},{"name":"observation_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}],"program":{"kind":"account","path":"cp_swap_program"}}}],"args":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"}]},{"name":"swap_tokens_for_sol_on_raydium","discriminator":[216,172,130,148,34,98,215,163],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"input_token_mint"}]}},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"operator","writable":true,"signer":true},{"name":"vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"pool_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"input_token_mint"},{"kind":"account","path":"output_token_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"input_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"input_token_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"output_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"output_token_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"input_token_mint"}]}},{"name":"bonding_curve_wsol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,119,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"input_token_mint"}]}},{"name":"input_token_mint","docs":["token_0 mint, the key must smaller than token_1 mint"]},{"name":"output_token_mint","docs":["token_1 mint, the key must greater than token_0 mint"],"address":"So11111111111111111111111111111111111111112"},{"name":"token_program","docs":["input_token_mint and output_token_mint have the same token program"]},{"name":"cp_swap_program","address":"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"},{"name":"observation_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}],"program":{"kind":"account","path":"cp_swap_program"}}}],"args":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"}]},{"name":"toggle_paused","discriminator":[54,83,147,198,123,97,218,72],"accounts":[{"name":"authority","writable":true,"signer":true,"relations":["config"]},{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}}],"args":[]},{"name":"update_config","discriminator":[29,158,252,191,10,83,219,99],"accounts":[{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"authority","signer":true,"relations":["config"]},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"new_protocol_fee_recipient","type":"pubkey"},{"name":"new_virtual_sol_reserves","type":"u64"},{"name":"new_virtual_token_reserves","type":"u64"},{"name":"new_graduation_target","type":"u64"},{"name":"new_graduation_fee","type":"u64"},{"name":"new_damping_term","type":"u8"},{"name":"new_swap_fee_basis_points","type":"u8"},{"name":"new_token_for_stakers_basis_points","type":"u16"},{"name":"new_token_amount_for_raydium_liquidity","type":"u64"},{"name":"new_max_graduation_price_deviation_basis_points","type":"u16"},{"name":"new_max_swap_amount_for_pool_price_correction_basis_points","type":"u16"}]}],"accounts":[{"name":"AmmConfig","discriminator":[218,244,33,104,203,203,43,111]},{"name":"BondingCurve","discriminator":[23,183,248,55,96,216,172,96]},{"name":"Config","discriminator":[155,12,170,224,30,250,204,130]},{"name":"LockedCpLiquidityState","discriminator":[25,10,238,197,207,234,73,22]}],"events":[{"name":"AuthorityTransferCancelledEvent","discriminator":[192,121,140,224,229,96,13,143]},{"name":"AuthorityTransferCompletedEvent","discriminator":[163,132,217,128,243,92,90,249]},{"name":"AuthorityTransferInitiatedEvent","discriminator":[121,246,95,155,229,109,148,205]},{"name":"BondingCurveDeployedEvent","discriminator":[225,80,178,34,217,39,184,148]},{"name":"BondingCurveDeployedFallbackEvent","discriminator":[106,252,243,115,199,159,247,31]},{"name":"BondingCurveVaultClosedEvent","discriminator":[185,36,156,82,189,164,207,79]},{"name":"ConfigUpdatedEvent","discriminator":[245,158,129,99,60,100,214,220]},{"name":"LiquidityDepositedIntoRaydiumEvent","discriminator":[236,50,97,27,198,101,248,20]},{"name":"OperatorsAddedEvent","discriminator":[247,58,112,56,203,186,112,152]},{"name":"OperatorsRemovedEvent","discriminator":[44,72,75,70,151,42,53,89]},{"name":"PausedToggledEvent","discriminator":[143,222,228,224,6,230,64,176]},{"name":"PostGraduationTradingFeesSplitEvent","discriminator":[34,231,16,81,36,203,158,196]},{"name":"RaydiumLiquidityLockedEvent","discriminator":[172,189,8,241,137,175,59,100]},{"name":"RaydiumPoolCreatedEvent","discriminator":[170,178,21,215,84,222,34,101]},{"name":"RaydiumRandomPoolCreatedEvent","discriminator":[152,251,128,152,158,235,83,53]},{"name":"SwapSolForTokensOnRaydiumEvent","discriminator":[247,1,8,166,221,116,113,98]},{"name":"SwapTokensForSolOnRaydiumEvent","discriminator":[76,249,221,162,65,70,118,32]},{"name":"TokenBoughtEvent","discriminator":[71,89,222,124,215,192,230,138]},{"name":"TokenCreatedEvent","discriminator":[96,122,113,138,50,227,149,57]},{"name":"TokenCreatedFallbackEvent","discriminator":[157,202,35,92,165,163,39,56]},{"name":"TokenGraduatedEvent","discriminator":[73,116,111,26,92,217,146,141]},{"name":"TokenSoldEvent","discriminator":[204,239,182,77,241,51,77,66]},{"name":"TradingFeesCollectedEvent","discriminator":[225,63,26,55,134,243,210,203]},{"name":"TradingFeesCollectedV2Event","discriminator":[23,246,130,250,11,49,240,179]},{"name":"TradingFeesSplitEvent","discriminator":[113,60,159,17,253,174,135,122]}],"errors":[{"code":6000,"name":"AmountInTooLow","msg":"Amount in too low"},{"code":6001,"name":"AmountOutTooLow","msg":"Amount out too low"},{"code":6002,"name":"AmountZero","msg":"Amount zero"},{"code":6003,"name":"ConfigNotInitialized","msg":"Config not initialized"},{"code":6004,"name":"CreatorIsNotProvided","msg":"Creator is not provided"},{"code":6005,"name":"DampingTermTooLow","msg":"Damping term too low"},{"code":6006,"name":"FirstBuyMustBeAtMost50PercentOfTotalSupply","msg":"First buy must be at most 50% of the total supply"},{"code":6007,"name":"GraduationFeeRelativeToTargetIsTooHigh","msg":"Graduation fee relative to graduation target is too high"},{"code":6008,"name":"InsufficientTokensOut","msg":"Insufficient tokens to transfer out of the bonding curve"},{"code":6009,"name":"InvalidBondingCurveStatus","msg":"Invalid bonding curve status"},{"code":6010,"name":"InvalidDampingTerm","msg":"Invalid damping term"},{"code":6011,"name":"InvalidMint","msg":"Invalid mint"},{"code":6012,"name":"InvalidProtocolFeeRecipient","msg":"Invalid protocol fee recipient"},{"code":6013,"name":"InvalidSqrtPrice","msg":"Invalid sqrt price"},{"code":6014,"name":"MathOverflow","msg":"Math overflow"},{"code":6015,"name":"MaxBasisPointsOffGraduationPriceTooHigh","msg":"Max basis points off graduation price too high"},{"code":6016,"name":"MaxGraduationPriceDeviationBasisPointsTooHigh","msg":"Max graduation price deviation basis points too high"},{"code":6017,"name":"MaxSwapAmountForPoolPriceCorrectionBasisPointsTooHigh","msg":"Max swap amount for pool price correction basis points too high"},{"code":6018,"name":"MintIsLargerThanOrEqualToNativeMint","msg":"Mint is larger than or equal to native mint"},{"code":6019,"name":"NoAuthorityTransferInProgress","msg":"No authority transfer in progress"},{"code":6020,"name":"NothingToDeposit","msg":"LP Token amount is too low"},{"code":6021,"name":"NothingToSplit","msg":"Nothing to split"},{"code":6022,"name":"NothingToLock","msg":"Nothing to lock"},{"code":6023,"name":"OperatorAlreadyAdded","msg":"Operator already added"},{"code":6024,"name":"OperatorDoesNotExist","msg":"Operator does not exist"},{"code":6025,"name":"Paused","msg":"Paused"},{"code":6026,"name":"PoolPriceOutOfRange","msg":"Pool is already created and has a price out of range when attempting to deposit liquidity"},{"code":6027,"name":"SwapFeeBasisPointsTooHigh","msg":"Swap fee basis points too high"},{"code":6028,"name":"SwapAmountTooHigh","msg":"Swap amount exceeds the reasonable limit to leave as liquidity"},{"code":6029,"name":"TokenAmountForRaydiumLiquidityTooHigh","msg":"Token amount for Raydium liquidity too high"},{"code":6030,"name":"TokenForStakersBasisPointsTooHigh","msg":"Token for stakers basis points too high"},{"code":6031,"name":"TokenGraduated","msg":"Token graduated"},{"code":6032,"name":"TokenNameTooLong","msg":"Token name too long"},{"code":6033,"name":"TokenNameTooShort","msg":"Token name too short"},{"code":6034,"name":"TokenSymbolTooLong","msg":"Token symbol too long"},{"code":6035,"name":"TokenSymbolTooShort","msg":"Token symbol too short"},{"code":6036,"name":"Unauthorized","msg":"Unauthorized"}],"types":[{"name":"AmmConfig","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"disable_create_pool","docs":["Status to control if new pool can be create"],"type":"bool"},{"name":"index","docs":["Config index"],"type":"u16"},{"name":"trade_fee_rate","docs":["The trade fee, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"protocol_fee_rate","docs":["The protocol fee"],"type":"u64"},{"name":"fund_fee_rate","docs":["The fund fee, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"create_pool_fee","docs":["Fee for create a new pool"],"type":"u64"},{"name":"protocol_owner","docs":["Address of the protocol fee owner"],"type":"pubkey"},{"name":"fund_owner","docs":["Address of the fund fee owner"],"type":"pubkey"},{"name":"padding","docs":["padding"],"type":{"array":["u64",16]}}]}},{"name":"AuthorityTransferCancelledEvent","type":{"kind":"struct","fields":[]}},{"name":"AuthorityTransferCompletedEvent","type":{"kind":"struct","fields":[{"name":"old_authority","type":"pubkey"},{"name":"new_authority","type":"pubkey"}]}},{"name":"AuthorityTransferInitiatedEvent","type":{"kind":"struct","fields":[{"name":"old_authority","type":"pubkey"},{"name":"new_authority","type":"pubkey"}]}},{"name":"BondingCurve","type":{"kind":"struct","fields":[{"name":"creator","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"virtual_sol_reserves","type":"u64"},{"name":"virtual_token_reserves","docs":["virtual token reserves is deprecated, we now use the xyk formula instead","and it only requires virtual sol reserves","to maintain backwards compatibility, if damping term is 30, we use the old formula","and we still need virtual_token_reserves to be set correctly"],"type":"u64"},{"name":"graduation_target","type":"u64"},{"name":"graduation_fee","type":"u64"},{"name":"sol_reserves","type":"u64"},{"name":"token_reserves","type":"u64"},{"name":"damping_term","docs":["In reality, this is now more like a bonding_curve_selector"],"type":"u8"},{"name":"swap_fee_basis_points","type":"u8"},{"name":"token_for_stakers_basis_points","type":"u16"},{"name":"status","type":{"defined":{"name":"BondingCurveStatus"}}}]}},{"name":"BondingCurveDeployedEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"creator","type":"pubkey"}]}},{"name":"BondingCurveDeployedFallbackEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"creator","type":"pubkey"}]}},{"name":"BondingCurveStatus","type":{"kind":"enum","variants":[{"name":"Trading"},{"name":"Graduated"},{"name":"PoolPriceCorrected"},{"name":"LiquidityProvisioned"},{"name":"LiquidityLocked"}]}},{"name":"BondingCurveVaultClosedEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"recipient","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"Config","type":{"kind":"struct","fields":[{"name":"is_paused","type":"bool"},{"name":"authority","type":"pubkey"},{"name":"pending_authority","type":"pubkey"},{"name":"operators","type":{"vec":"pubkey"}},{"name":"protocol_fee_recipient","type":"pubkey"},{"name":"token_distributor","type":"pubkey"},{"name":"virtual_sol_reserves","type":"u64"},{"name":"virtual_token_reserves","type":"u64"},{"name":"graduation_target","type":"u64"},{"name":"graduation_fee","type":"u64"},{"name":"damping_term","type":"u8"},{"name":"token_for_stakers_basis_points","type":"u16"},{"name":"swap_fee_basis_points","type":"u8"},{"name":"token_amount_for_raydium_liquidity","type":"u64"},{"name":"max_graduation_price_deviation_basis_points","type":"u16"},{"name":"max_swap_amount_for_pool_price_correction_basis_points","type":"u16"}]}},{"name":"ConfigUpdatedEvent","type":{"kind":"struct","fields":[{"name":"protocol_fee_recipient","type":"pubkey"},{"name":"virtual_sol_reserves","type":"u64"},{"name":"virtual_token_reserves","type":"u64"},{"name":"graduation_target","type":"u64"},{"name":"graduation_fee","type":"u64"},{"name":"damping_term","type":"u8"},{"name":"swap_fee_basis_points","type":"u8"},{"name":"token_for_stakers_basis_points","type":"u16"},{"name":"token_amount_for_raydium_liquidity","type":"u64"},{"name":"max_graduation_price_deviation_basis_points","type":"u16"},{"name":"max_swap_amount_for_pool_price_correction_basis_points","type":"u16"}]}},{"name":"LiquidityDepositedIntoRaydiumEvent","type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"lp_token_amount","type":"u64"},{"name":"tokens_deposited","type":"u64"},{"name":"wsol_deposited","type":"u64"}]}},{"name":"LockedCpLiquidityState","type":{"kind":"struct","fields":[{"name":"locked_lp_amount","docs":["The Locked liquidity amount without claimed lp fee"],"type":"u64"},{"name":"claimed_lp_amount","docs":["Claimed lp fee amount"],"type":"u64"},{"name":"unclaimed_lp_amount","docs":["Unclaimed lp fee amount"],"type":"u64"},{"name":"last_lp","docs":["Last updated cp pool lp total supply"],"type":"u64"},{"name":"last_k","docs":["Last updated cp pool k"],"type":"u128"},{"name":"recent_epoch","docs":["Account update recent epoch"],"type":"u64"},{"name":"pool_id","docs":["The ID of the pool with which this record is connected"],"type":"pubkey"},{"name":"fee_nft_mint","docs":["nft mint to check who has authority to collect fee"],"type":"pubkey"},{"name":"locked_owner","docs":["The owner who has locked liquidity"],"type":"pubkey"},{"name":"locked_lp_mint","docs":["The mint of locked lp token"],"type":"pubkey"},{"name":"padding","docs":["Unused bytes for future upgrades."],"type":{"array":["u64",8]}}]}},{"name":"OperatorsAddedEvent","type":{"kind":"struct","fields":[{"name":"operators","type":{"vec":"pubkey"}}]}},{"name":"OperatorsRemovedEvent","type":{"kind":"struct","fields":[{"name":"operators","type":{"vec":"pubkey"}}]}},{"name":"PausedToggledEvent","type":{"kind":"struct","fields":[{"name":"is_paused","type":"bool"}]}},{"name":"PostGraduationTradingFeesSplitEvent","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"creator","type":"pubkey"}]}},{"name":"RaydiumLiquidityLockedEvent","type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"lp_amount","type":"u64"}]}},{"name":"RaydiumPoolCreatedEvent","type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"mint","type":"pubkey"}]}},{"name":"RaydiumRandomPoolCreatedEvent","type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"mint","type":"pubkey"}]}},{"name":"SwapSolForTokensOnRaydiumEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"}]}},{"name":"SwapTokensForSolOnRaydiumEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"}]}},{"name":"TokenBoughtEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"},{"name":"swap_fee","type":"u64"},{"name":"buyer","type":"pubkey"},{"name":"recipient","type":"pubkey"}]}},{"name":"TokenCreatedEvent","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]}},{"name":"TokenCreatedFallbackEvent","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]}},{"name":"TokenGraduatedEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"sol_for_liquidity","type":"u64"},{"name":"graduation_fee","type":"u64"},{"name":"token_for_distributor","type":"u64"}]}},{"name":"TokenSoldEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"},{"name":"swap_fee","type":"u64"},{"name":"seller","type":"pubkey"},{"name":"recipient","type":"pubkey"}]}},{"name":"TradingFeesCollectedEvent","type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"mint","type":"pubkey"}]}},{"name":"TradingFeesCollectedV2Event","type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"mint","type":"pubkey"}]}},{"name":"TradingFeesSplitEvent","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"creator","type":"pubkey"}]}}]} \ No newline at end of file diff --git a/idls/bubblegum.BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY.json b/idls/bubblegum.BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY.json new file mode 100644 index 0000000..8131dde --- /dev/null +++ b/idls/bubblegum.BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY.json @@ -0,0 +1 @@ +{"version":"0.12.0","name":"bubblegum","instructions":[{"name":"burn","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"}]},{"name":"cancelRedeem","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":true,"isSigner":true},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"voucher","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}}]},{"name":"createTree","accounts":[{"name":"treeAuthority","isMut":true,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"treeCreator","isMut":false,"isSigner":true},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxDepth","type":"u32"},{"name":"maxBufferSize","type":"u32"},{"name":"public","type":{"option":"bool"}}]},{"name":"decompressV1","accounts":[{"name":"voucher","isMut":true,"isSigner":false},{"name":"leafOwner","isMut":true,"isSigner":true},{"name":"tokenAccount","isMut":true,"isSigner":false},{"name":"mint","isMut":true,"isSigner":false},{"name":"mintAuthority","isMut":true,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":false},{"name":"masterEdition","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"sysvarRent","isMut":false,"isSigner":false},{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false}],"args":[{"name":"metadata","type":{"defined":"MetadataArgs"}}]},{"name":"delegate","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":true},{"name":"previousLeafDelegate","isMut":false,"isSigner":false},{"name":"newLeafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"}]},{"name":"mintToCollectionV1","accounts":[{"name":"treeAuthority","isMut":true,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"treeDelegate","isMut":false,"isSigner":true},{"name":"collectionAuthority","isMut":false,"isSigner":true},{"name":"collectionAuthorityRecordPda","isMut":false,"isSigner":false},{"name":"collectionMint","isMut":false,"isSigner":false},{"name":"collectionMetadata","isMut":true,"isSigner":false},{"name":"editionAccount","isMut":false,"isSigner":false},{"name":"bubblegumSigner","isMut":false,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"metadataArgs","type":{"defined":"MetadataArgs"}}]},{"name":"mintV1","accounts":[{"name":"treeAuthority","isMut":true,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"treeDelegate","isMut":false,"isSigner":true},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"message","type":{"defined":"MetadataArgs"}}]},{"name":"redeem","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":true,"isSigner":true},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"voucher","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"}]},{"name":"setAndVerifyCollection","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"treeDelegate","isMut":false,"isSigner":false},{"name":"collectionAuthority","isMut":false,"isSigner":true},{"name":"collectionAuthorityRecordPda","isMut":false,"isSigner":false},{"name":"collectionMint","isMut":false,"isSigner":false},{"name":"collectionMetadata","isMut":true,"isSigner":false},{"name":"editionAccount","isMut":false,"isSigner":false},{"name":"bubblegumSigner","isMut":false,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"message","type":{"defined":"MetadataArgs"}},{"name":"collection","type":"publicKey"}]},{"name":"setDecompressibleState","accounts":[{"name":"treeAuthority","isMut":true,"isSigner":false},{"name":"treeCreator","isMut":false,"isSigner":true}],"args":[{"name":"decompressableState","type":{"defined":"DecompressibleState"}}]},{"name":"setTreeDelegate","accounts":[{"name":"treeAuthority","isMut":true,"isSigner":false},{"name":"treeCreator","isMut":false,"isSigner":true},{"name":"newTreeDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"transfer","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"newLeafOwner","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"}]},{"name":"unverifyCollection","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"treeDelegate","isMut":false,"isSigner":false},{"name":"collectionAuthority","isMut":false,"isSigner":true},{"name":"collectionAuthorityRecordPda","isMut":false,"isSigner":false},{"name":"collectionMint","isMut":false,"isSigner":false},{"name":"collectionMetadata","isMut":true,"isSigner":false},{"name":"editionAccount","isMut":false,"isSigner":false},{"name":"bubblegumSigner","isMut":false,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"message","type":{"defined":"MetadataArgs"}}]},{"name":"unverifyCreator","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"creator","isMut":false,"isSigner":true},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"message","type":{"defined":"MetadataArgs"}}]},{"name":"verifyCollection","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"treeDelegate","isMut":false,"isSigner":false},{"name":"collectionAuthority","isMut":false,"isSigner":true},{"name":"collectionAuthorityRecordPda","isMut":false,"isSigner":false},{"name":"collectionMint","isMut":false,"isSigner":false},{"name":"collectionMetadata","isMut":true,"isSigner":false},{"name":"editionAccount","isMut":false,"isSigner":false},{"name":"bubblegumSigner","isMut":false,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"message","type":{"defined":"MetadataArgs"}}]},{"name":"verifyCreator","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"creator","isMut":false,"isSigner":true},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"message","type":{"defined":"MetadataArgs"}}]},{"name":"updateMetadata","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"collectionMint","isMut":false,"isSigner":false,"isOptional":true},{"name":"collectionMetadata","isMut":false,"isSigner":false,"isOptional":true},{"name":"collectionAuthorityRecordPda","isMut":false,"isSigner":false,"isOptional":true},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"currentMetadata","type":{"defined":"MetadataArgs"}},{"name":"updateArgs","type":{"defined":"UpdateArgs"}}]}],"accounts":[{"name":"TreeConfig","type":{"kind":"struct","fields":[{"name":"treeCreator","type":"publicKey"},{"name":"treeDelegate","type":"publicKey"},{"name":"totalMintCapacity","type":"u64"},{"name":"numMinted","type":"u64"},{"name":"isPublic","type":"bool"},{"name":"isDecompressible","type":{"defined":"DecompressibleState"}}]}},{"name":"Voucher","type":{"kind":"struct","fields":[{"name":"leafSchema","type":{"defined":"LeafSchema"}},{"name":"index","type":"u32"},{"name":"merkleTree","type":"publicKey"}]}}],"types":[{"name":"Creator","type":{"kind":"struct","fields":[{"name":"address","type":"publicKey"},{"name":"verified","type":"bool"},{"name":"share","type":"u8"}]}},{"name":"Uses","type":{"kind":"struct","fields":[{"name":"useMethod","type":{"defined":"UseMethod"}},{"name":"remaining","type":"u64"},{"name":"total","type":"u64"}]}},{"name":"Collection","type":{"kind":"struct","fields":[{"name":"verified","type":"bool"},{"name":"key","type":"publicKey"}]}},{"name":"MetadataArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"sellerFeeBasisPoints","type":"u16"},{"name":"primarySaleHappened","type":"bool"},{"name":"isMutable","type":"bool"},{"name":"editionNonce","type":{"option":"u8"}},{"name":"tokenStandard","type":{"option":{"defined":"TokenStandard"}}},{"name":"collection","type":{"option":{"defined":"Collection"}}},{"name":"uses","type":{"option":{"defined":"Uses"}}},{"name":"tokenProgramVersion","type":{"defined":"TokenProgramVersion"}},{"name":"creators","type":{"vec":{"defined":"Creator"}}}]}},{"name":"UpdateArgs","type":{"kind":"struct","fields":[{"name":"name","type":{"option":"string"}},{"name":"symbol","type":{"option":"string"}},{"name":"uri","type":{"option":"string"}},{"name":"creators","type":{"option":{"vec":{"defined":"Creator"}}}},{"name":"sellerFeeBasisPoints","type":{"option":"u16"}},{"name":"primarySaleHappened","type":{"option":"bool"}},{"name":"isMutable","type":{"option":"bool"}}]}},{"name":"Version","type":{"kind":"enum","variants":[{"name":"V1"}]}},{"name":"LeafSchema","type":{"kind":"enum","variants":[{"name":"V1","fields":[{"name":"id","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"delegate","type":"publicKey"},{"name":"nonce","type":"u64"},{"name":"data_hash","type":{"array":["u8",32]}},{"name":"creator_hash","type":{"array":["u8",32]}}]}]}},{"name":"TokenProgramVersion","type":{"kind":"enum","variants":[{"name":"Original"},{"name":"Token2022"}]}},{"name":"TokenStandard","type":{"kind":"enum","variants":[{"name":"NonFungible"},{"name":"FungibleAsset"},{"name":"Fungible"},{"name":"NonFungibleEdition"}]}},{"name":"UseMethod","type":{"kind":"enum","variants":[{"name":"Burn"},{"name":"Multiple"},{"name":"Single"}]}},{"name":"BubblegumEventType","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"LeafSchemaEvent"}]}},{"name":"DecompressibleState","type":{"kind":"enum","variants":[{"name":"Enabled"},{"name":"Disabled"}]}}],"errors":[{"code":6000,"name":"AssetOwnerMismatch","msg":""},{"code":6001,"name":"PublicKeyMismatch","msg":""},{"code":6002,"name":"HashingMismatch","msg":""},{"code":6003,"name":"UnsupportedSchemaVersion","msg":""},{"code":6004,"name":"CreatorShareTotalMustBe100","msg":""},{"code":6005,"name":"DuplicateCreatorAddress","msg":""},{"code":6006,"name":"CreatorDidNotVerify","msg":"Creator did not verify the metadata"},{"code":6007,"name":"CreatorNotFound","msg":""},{"code":6008,"name":"NoCreatorsPresent","msg":"No creators in creator Vec"},{"code":6009,"name":"CreatorHashMismatch","msg":"Provided creator Vec must result in provided creator hash"},{"code":6010,"name":"DataHashMismatch","msg":"Provided metadata must result in provided data hash"},{"code":6011,"name":"CreatorsTooLong","msg":"Creators list too long"},{"code":6012,"name":"MetadataNameTooLong","msg":""},{"code":6013,"name":"MetadataSymbolTooLong","msg":""},{"code":6014,"name":"MetadataUriTooLong","msg":""},{"code":6015,"name":"MetadataBasisPointsTooHigh","msg":"Basis points cannot exceed 10000"},{"code":6016,"name":"TreeAuthorityIncorrect","msg":"Tree creator or tree delegate must sign."},{"code":6017,"name":"InsufficientMintCapacity","msg":"Not enough unapproved mints left"},{"code":6018,"name":"NumericalOverflowError","msg":""},{"code":6019,"name":"IncorrectOwner","msg":"r"},{"code":6020,"name":"CollectionCannotBeVerifiedInThisInstruction","msg":""},{"code":6021,"name":"CollectionNotFound","msg":"Collection Not Found on Metadata"},{"code":6022,"name":"AlreadyVerified","msg":""},{"code":6023,"name":"AlreadyUnverified","msg":""},{"code":6024,"name":"UpdateAuthorityIncorrect","msg":"Incorrect leaf metadata update authority."},{"code":6025,"name":"LeafAuthorityMustSign","msg":"Tx must be signed by leaf owner or leaf delegate"},{"code":6026,"name":"CollectionMustBeSized","msg":""},{"code":6027,"name":"MetadataMintMismatch","msg":"Metadata mint does not match collection mint"},{"code":6028,"name":"InvalidCollectionAuthority","msg":""},{"code":6029,"name":"InvalidDelegateRecord","msg":""},{"code":6030,"name":"CollectionMasterEditionAccountInvalid","msg":"Edition account doesnt match collection"},{"code":6031,"name":"CollectionMustBeAUniqueMasterEdition","msg":"Collection Must Be a Unique Master Edition v2"},{"code":6032,"name":"UnknownExternalError","msg":"Could not convert to BubblegumError"},{"code":6033,"name":"DecompressionDisabled","msg":"Decompression disabled for this tree."},{"code":6034,"name":"MissingCollectionMintAccount","msg":"Missing collection mint account"},{"code":6035,"name":"MissingCollectionMetadataAccount","msg":"Missing collection metadata account"},{"code":6036,"name":"CollectionMismatch","msg":"Collection mismatch"},{"code":6037,"name":"MetadataImmutable","msg":"Metadata not mutable"},{"code":6038,"name":"PrimarySaleCanOnlyBeFlippedToTrue","msg":"Can only update primary sale to true"},{"code":6039,"name":"CreatorDidNotUnverify","msg":"Creator did not unverify the metadata"}]} \ No newline at end of file diff --git a/idls/byreal_clmm.REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2.json b/idls/byreal_clmm.REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2.json new file mode 100644 index 0000000..777071d --- /dev/null +++ b/idls/byreal_clmm.REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2.json @@ -0,0 +1 @@ +{"address":"REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2","metadata":{"name":"byreal_clmm","version":"0.1.0","spec":"0.1.0","description":"Anchor client and source for Byreal concentrated liquidity AMM"},"instructions":[{"name":"claim_offchain_reward","docs":["claim offchain reward from the pool"],"discriminator":[195,87,221,149,141,195,146,19],"accounts":[{"name":"claimer","docs":["the address who claim the offchain reward."],"writable":true,"signer":true},{"name":"authority","docs":["The authority make decision that who can claim the offchain reward."],"signer":true},{"name":"admin_group","docs":["Initialize amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_id","docs":["the pool id, which is the pool state account."],"relations":["reward_config"]},{"name":"token_mint"},{"name":"claimer_token_account","docs":[""],"writable":true},{"name":"reward_vault_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"reward_config"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"reward_config","docs":["The offchain reward config account, it also is the reward vault account."],"pda":{"seeds":[{"kind":"const","value":[111,102,102,99,104,97,105,110,95,114,101,119,97,114,100]},{"kind":"account","path":"pool_id"}]}},{"name":"token_program","docs":["Spl token program or token program 2022"]},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"amount","type":"u64"}]},{"name":"close_position","docs":["Close the user's position and NFT account. If the NFT mint belongs to token2022, it will also be closed and the funds returned to the NFT owner.","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[123,134,81,0,49,68,98,98],"accounts":[{"name":"nft_owner","docs":["The position nft owner"],"writable":true,"signer":true},{"name":"position_nft_mint","docs":["Mint address bound to the personal position."],"writable":true},{"name":"position_nft_account","docs":["User token account where position NFT be minted to"],"writable":true},{"name":"personal_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"system_program","docs":["System program to close the position state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Token/Token2022 program to close token/mint account"]}],"args":[]},{"name":"close_protocol_position","docs":["close the protocol position, send the remaining fund fee to admin fee keeper","this instruction will be delete after all protocol position is closed"],"discriminator":[201,117,152,144,85,85,108,178],"accounts":[{"name":"admin_group","docs":["amm admin group account to store admin permissions.","anyone can collect fee, but only fee-manager in admin group can receive fee"],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"fee_keeper","writable":true},{"name":"protocol_position","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"collect_fund_fee","docs":["Collect the fund fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[167,138,78,149,223,194,6,126],"accounts":[{"name":"admin_group","docs":["amm admin group account to store admin permissions.","anyone can collect fee, but only fee-manager in admin group can receive fee"],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_protocol_fee","docs":["Collect the protocol fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[136,136,252,221,194,66,126,89],"accounts":[{"name":"admin_group","docs":["amm admin group account to store admin permissions.","anyone can collect fee, but only fee-manager in admin group can receive fee"],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_remaining_rewards","docs":["Collect remaining reward token for reward founder","","# Arguments","","* `ctx`- The context of accounts","* `reward_index` - the index to reward info",""],"discriminator":[18,237,166,197,34,16,213,144],"accounts":[{"name":"reward_funder","docs":["The founder who init reward info previously"],"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"funder_token_account","docs":["The funder's reward token account"],"writable":true},{"name":"pool_state","docs":["Set reward for this pool"],"writable":true},{"name":"reward_token_vault","docs":["Reward vault transfer remaining token to founder token account"]},{"name":"reward_vault_mint","docs":["The mint of reward token vault"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"create_amm_config","docs":["# Arguments","","* `ctx`- The accounts needed by instruction.","* `index` - The index of amm config, there may be multiple config.","* `tick_spacing` - The tickspacing binding with config, cannot be changed.","* `trade_fee_rate` - Trade fee rate, can be changed.","* `protocol_fee_rate` - The rate of protocol fee within trade fee.","* `fund_fee_rate` - The rate of fund fee within trade fee.",""],"discriminator":[137,52,237,212,215,117,108,104],"accounts":[{"name":"owner","docs":["Address to be set as normal manager in admin group."],"writable":true,"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"amm_config","docs":["Initialize config state account to store protocol owner address and fee rates."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,109,109,95,99,111,110,102,105,103]},{"kind":"arg","path":"index"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"u16"},{"name":"tick_spacing","type":"u16"},{"name":"trade_fee_rate","type":"u32"},{"name":"protocol_fee_rate","type":"u32"},{"name":"fund_fee_rate","type":"u32"}]},{"name":"create_operation_account","docs":["Creates an operation account for the program","","# Arguments","","* `ctx`- The context of accounts",""],"discriminator":[63,87,148,33,109,35,8,104],"accounts":[{"name":"owner","docs":["Address to be set as operation account owner."],"writable":true,"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"operation_state","docs":["Initialize operation state account to store operation owner address and white list mint."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_pool","docs":["Creates a pool for the given token pair and the initial price","","# Arguments","","* `ctx`- The context of accounts","* `sqrt_price_x64` - the initial sqrt price (amount_token_1 / amount_token_0) of the pool as a Q64.64","Note: The open_time must be smaller than the current block_timestamp on chain."],"discriminator":[233,146,209,142,207,104,64,188],"accounts":[{"name":"pool_creator","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"pool_manager","docs":["with pool_manager permission, the pool creator can create a pool."],"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"pool_state","docs":["Initialize an account to store the pool state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"token_mint_0"},{"kind":"account","path":"token_mint_1"}]}},{"name":"offchain_reward_config","docs":["Initialize an account to store the off-chain reward config"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,102,102,99,104,97,105,110,95,114,101,119,97,114,100]},{"kind":"account","path":"pool_state"}]}},{"name":"token_mint_0","docs":["Token_0 mint, the key must be smaller then token_1 mint."]},{"name":"token_mint_1","docs":["Token_1 mint"]},{"name":"token_vault_0","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_0"}]}},{"name":"token_vault_1","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_1"}]}},{"name":"observation_state","docs":["Initialize an account to store oracle observations"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"tick_array_bitmap","docs":["Initialize an account to store if a tick array is initialized."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,116,105,99,107,95,97,114,114,97,121,95,98,105,116,109,97,112,95,101,120,116,101,110,115,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"token_program_0","docs":["Spl token program or token program 2022"]},{"name":"token_program_1","docs":["Spl token program or token program 2022"]},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"sqrt_price_x64","type":"u128"},{"name":"open_time","type":"u64"}]},{"name":"create_pool_decay_fee","docs":["Creates a pool for the given token pair and the initial price with decay fee","# Arguments","","* `ctx`- The context of accounts","* `params` - The parameters for creating the pool with decay fee"],"discriminator":[252,154,210,191,22,217,136,252],"accounts":[{"name":"pool_creator","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"pool_manager","docs":["with pool_manager permission, the pool creator can create a pool."],"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"pool_state","docs":["Initialize an account to store the pool state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"token_mint_0"},{"kind":"account","path":"token_mint_1"}]}},{"name":"offchain_reward_config","docs":["Initialize an account to store the off-chain reward config"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,102,102,99,104,97,105,110,95,114,101,119,97,114,100]},{"kind":"account","path":"pool_state"}]}},{"name":"token_mint_0","docs":["Token_0 mint, the key must be smaller then token_1 mint."]},{"name":"token_mint_1","docs":["Token_1 mint"]},{"name":"token_vault_0","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_0"}]}},{"name":"token_vault_1","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_1"}]}},{"name":"observation_state","docs":["Initialize an account to store oracle observations"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"tick_array_bitmap","docs":["Initialize an account to store if a tick array is initialized."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,116,105,99,107,95,97,114,114,97,121,95,98,105,116,109,97,112,95,101,120,116,101,110,115,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"token_program_0","docs":["Spl token program or token program 2022"]},{"name":"token_program_1","docs":["Spl token program or token program 2022"]},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"CreatePoolDecayFeeParams"}}}]},{"name":"create_support_mint_associated","docs":["Create support token22 mint account which can create pool and send rewards with ignoring the not support extensions."],"discriminator":[17,251,65,92,136,242,14,169],"accounts":[{"name":"owner","docs":["Address to be set as protocol owner."],"writable":true,"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"token_mint","docs":["Support token mint"]},{"name":"support_mint_associated","docs":["Initialize support mint state account to store support mint address and bump."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,117,112,112,111,114,116,95,109,105,110,116]},{"kind":"account","path":"token_mint"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"decrease_liquidity","docs":["#[deprecated(note = \"Use `decrease_liquidity_v2` instead.\")]","Decreases liquidity for an existing position","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The amount by which liquidity will be decreased","* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity","* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity",""],"discriminator":[160,38,208,111,104,91,44,1],"accounts":[{"name":"nft_owner","docs":["The position owner or delegated authority"],"signer":true},{"name":"nft_account","docs":["The token account for the tokenized position"]},{"name":"personal_position","docs":["Decrease liquidity for this position"],"writable":true},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"token_vault_0","docs":["Token_0 vault"],"writable":true},{"name":"token_vault_1","docs":["Token_1 vault"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick","constraint = tick_array_lower.load()?.pool_id == pool_state.key()"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick","constraint = tick_array_upper.load()?.pool_id == pool_state.key()"],"writable":true},{"name":"recipient_token_account_0","docs":["The destination token account for receive amount_0"],"writable":true},{"name":"recipient_token_account_1","docs":["The destination token account for receive amount_1"],"writable":true},{"name":"token_program","docs":["SPL program to transfer out tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_min","type":"u64"},{"name":"amount_1_min","type":"u64"}]},{"name":"decrease_liquidity_v2","docs":["Decreases liquidity for an existing position, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The amount by which liquidity will be decreased","* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity","* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity",""],"discriminator":[58,127,188,62,79,82,196,96],"accounts":[{"name":"nft_owner","docs":["The position owner or delegated authority"],"signer":true},{"name":"nft_account","docs":["The token account for the tokenized position"]},{"name":"personal_position","docs":["Decrease liquidity for this position"],"writable":true},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"token_vault_0","docs":["Token_0 vault"],"writable":true},{"name":"token_vault_1","docs":["Token_1 vault"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick","constraint = tick_array_lower.load()?.pool_id == pool_state.key()"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick","constraint = tick_array_upper.load()?.pool_id == pool_state.key()"],"writable":true},{"name":"recipient_token_account_0","docs":["The destination token account for receive amount_0"],"writable":true},{"name":"recipient_token_account_1","docs":["The destination token account for receive amount_1"],"writable":true},{"name":"token_program","docs":["SPL program to transfer out tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_min","type":"u64"},{"name":"amount_1_min","type":"u64"}]},{"name":"deposit_offchain_reward","docs":["deposit offchain reward into the pool"],"discriminator":[97,125,48,169,92,241,44,142],"accounts":[{"name":"payer","docs":["the address paying to deposit the offchain reward."],"writable":true,"signer":true},{"name":"authority","docs":["The authority make decision that who can deposit the offchain reward."],"signer":true},{"name":"admin_group","docs":["Initialize amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_id","docs":["the pool id, which is the pool state account."],"relations":["reward_config"]},{"name":"token_mint"},{"name":"payer_token_account","docs":[""],"writable":true},{"name":"reward_vault_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"reward_config"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"reward_config","docs":["The offchain reward config account, it also is the reward vault account."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,102,102,99,104,97,105,110,95,114,101,119,97,114,100]},{"kind":"account","path":"pool_id"}]}},{"name":"token_program","docs":["Spl token program or token program 2022"]},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"}]},{"name":"increase_liquidity","docs":["#[deprecated(note = \"Use `increase_liquidity_v2` instead.\")]","Increases liquidity for an existing position, with amount paid by `payer`","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The desired liquidity to be added, can't be zero","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check",""],"discriminator":[46,156,243,118,13,205,251,178],"accounts":[{"name":"nft_owner","docs":["Pays to mint the position"],"signer":true},{"name":"nft_account","docs":["The token account for nft"]},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"personal_position","docs":["Increase liquidity for this position"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick","constraint = tick_array_lower.load()?.pool_id == pool_state.key()"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick","constraint = tick_array_upper.load()?.pool_id == pool_state.key()"],"writable":true},{"name":"token_account_0","docs":["The payer's token account for token_0"],"writable":true},{"name":"token_account_1","docs":["The token account spending token_1 to mint the position"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"}]},{"name":"increase_liquidity_v2","docs":["Increases liquidity for an existing position, with amount paid by `payer`, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The desired liquidity to be added, if zero, calculate liquidity base amount_0 or amount_1 according base_flag","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `base_flag` - Now, we don't use base_flag, it can be None;","We directly calculate liquidity from amount_0_max or amount_1_max according to the current price in pool.",""],"discriminator":[133,29,89,223,69,238,176,10],"accounts":[{"name":"nft_owner","docs":["Pays to mint the position"],"signer":true},{"name":"nft_account","docs":["The token account for nft"]},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"personal_position","docs":["Increase liquidity for this position"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick","constraint = tick_array_lower.load()?.pool_id == pool_state.key()"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick","constraint = tick_array_upper.load()?.pool_id == pool_state.key()"],"writable":true},{"name":"token_account_0","docs":["The payer's token account for token_0"],"writable":true},{"name":"token_account_1","docs":["The token account spending token_1 to mint the position"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"init_amm_admin_group","docs":["Initialize the AMM admin group account, which is used to manage the AMM protocol."],"discriminator":[209,108,32,246,157,214,237,86],"accounts":[{"name":"payer","docs":["only super admin can create admin group"],"writable":true,"signer":true,"address":"AY196f8U5EvM999PVnvLmyvaUnzL4GLiFaGKUgnJXN6o"},{"name":"admin_group","docs":["Initialize amm admin group account to store admin permissions."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"InitAdminGroupParams"}}}]},{"name":"initialize_reward","docs":["Initialize a reward info for a given pool and reward index","","# Arguments","","* `ctx`- The context of accounts","* `reward_index` - the index to reward info","* `open_time` - reward open timestamp","* `end_time` - reward end timestamp","* `emissions_per_second_x64` - Token reward per second are earned per unit of liquidity.",""],"discriminator":[95,135,192,196,242,129,230,68],"accounts":[{"name":"reward_funder","docs":["The founder deposit reward token to vault"],"writable":true,"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"funder_token_account","writable":true},{"name":"amm_config","docs":["For check the reward_funder authority"]},{"name":"pool_state","docs":["Set reward for this pool"],"writable":true},{"name":"operation_state","docs":["load info from the account to judge reward permission"],"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"reward_token_mint","docs":["Reward mint"]},{"name":"reward_token_vault","docs":["A pda, reward vault"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,114,101,119,97,114,100,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"reward_token_mint"}]}},{"name":"reward_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"param","type":{"defined":{"name":"InitializeRewardParam"}}}]},{"name":"open_position","docs":["#[deprecated(note = \"Use `open_position_with_token22_nft` instead.\")]","Creates a new position wrapped in a NFT","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which include tick low","* `tick_array_upper_start_index` - The start index of tick array which include tick upper","* `liquidity` - The liquidity to be added","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check",""],"discriminator":[135,128,47,77,15,152,240,49],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["Token account where position NFT will be minted","This account created in the contract by cpi to avoid large stack variables"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"position_nft_owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_nft_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","docs":["To store metaplex metadata"],"writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position"},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_program","docs":["Program to create NFT metadata"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"}]},{"name":"open_position_v2","docs":["#[deprecated(note = \"Use `open_position_with_token22_nft` instead.\")]","Creates a new position wrapped in a NFT, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which include tick low","* `tick_array_upper_start_index` - The start index of tick array which include tick upper","* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `with_metadata` - The flag indicating whether to create NFT mint metadata","* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[77,184,74,214,112,86,241,199],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["Token account where position NFT will be minted"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"position_nft_owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_nft_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","docs":["To store metaplex metadata"],"writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position"},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_program","docs":["Program to create NFT metadata"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"token_program_2022","docs":["Program to create mint account and mint tokens"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"with_metadata","type":"bool"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"open_position_with_token22_nft","docs":["Creates a new position wrapped in a Token2022 NFT without relying on metadata_program and metadata_account, reduce the cost for user to create a personal position.","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which include tick low","* `tick_array_upper_start_index` - The start index of tick array which include tick upper","* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `with_metadata` - The flag indicating whether to create NFT mint metadata","* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[77,255,174,82,125,29,201,46],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address, initialize in contract"],"writable":true,"signer":true},{"name":"position_nft_account","writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position"},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to transfer for token account"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program_2022","docs":["Program to create NFT mint/token account and transfer for token22 account"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"with_metadata","type":"bool"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"set_pool_quote_flag","docs":["设置 pool 的 quote token 标识"],"discriminator":[250,38,116,122,249,128,108,105],"accounts":[{"name":"authority","docs":["必须是 pool_manager 权限"],"signer":true},{"name":"admin_group","pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_state","writable":true}],"args":[{"name":"token1_as_quote","type":"bool"}]},{"name":"set_pool_trade_fee_rate","docs":["设置 pool 专属的 trade_fee_rate"],"discriminator":[109,117,170,34,87,202,50,20],"accounts":[{"name":"authority","docs":["必须是 pool_manager 权限(与 create_pool 一致)"],"signer":true},{"name":"admin_group","pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_state","writable":true}],"args":[{"name":"trade_fee_rate","type":"u32"}]},{"name":"set_reward_params","docs":["Reset reward param, start a new reward cycle or extend the current cycle.","","# Arguments","","* `ctx` - The context of accounts","* `reward_index` - The index of reward token in the pool.","* `emissions_per_second_x64` - The per second emission reward, when extend the current cycle,","new value can't be less than old value","* `open_time` - reward open timestamp, must be set when starting a new cycle","* `end_time` - reward end timestamp",""],"discriminator":[112,52,167,75,32,201,211,137],"accounts":[{"name":"authority","docs":["Address to be set as protocol owner. It pays to create factory state account."],"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"amm_config"},{"name":"pool_state","writable":true},{"name":"operation_state","docs":["load info from the account to judge reward permission"],"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"token_program","docs":["Token program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"reward_index","type":"u8"},{"name":"emissions_per_second_x64","type":"u128"},{"name":"open_time","type":"u64"},{"name":"end_time","type":"u64"}]},{"name":"set_swap_dynamic_fee_params","docs":["设置 swap-dynamic-fee 参数"],"discriminator":[169,23,133,74,115,251,233,52],"accounts":[{"name":"authority","docs":["必须是 pool_manager 权限 (与 create_pool 一致)"],"signer":true},{"name":"admin_group","pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_state","writable":true}],"args":[{"name":"params","type":{"defined":{"name":"SetSwapDynamicFeeParamsInput"}}}]},{"name":"swap","docs":["#[deprecated(note = \"Use `swap_v2` instead.\")]","Swaps one token for as much as possible of another token across a single pool","","# Arguments","","* `ctx` - The context of accounts","* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)","* `other_amount_threshold` - For slippage check","* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot","* `is_base_input` - swap base input or swap base output",""],"discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"tick_array","writable":true}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit_x64","type":"u128"},{"name":"is_base_input","type":"bool"}]},{"name":"swap_v2","docs":["Swaps one token for as much as possible of another token across a single pool, support token program 2022","","# Arguments","","* `ctx` - The context of accounts","* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)","* `other_amount_threshold` - For slippage check","* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot","* `is_base_input` - swap base input or swap base output",""],"discriminator":[43,4,237,11,26,201,30,98],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["SPL program 2022 for token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["Memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"input_vault_mint","docs":["The mint of token vault 0"]},{"name":"output_vault_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit_x64","type":"u128"},{"name":"is_base_input","type":"bool"}]},{"name":"swap_v3_dyn","docs":["swap_v3_dyn: 支持动态费率的 swap 指令","如果pool不支持 swap-dynamic-fee,则行为与 swap_v2 相同"],"discriminator":[229,46,213,132,105,40,40,228],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["SPL program 2022 for token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["Memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"input_vault_mint","docs":["The mint of token vault 0"]},{"name":"output_vault_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit_x64","type":"u128"},{"name":"is_base_input","type":"bool"}]},{"name":"transfer_reward_owner","docs":["Transfer reward owner","","# Arguments","","* `ctx`- The context of accounts","* `new_owner`- new owner pubkey",""],"discriminator":[7,22,12,83,242,43,48,121],"accounts":[{"name":"authority","docs":["Address to be set as operation account owner."],"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_state","writable":true}],"args":[{"name":"new_owner","type":"pubkey"}]},{"name":"update_amm_admin_group","docs":["Update the AMM admin group account, which is used to manage the AMM protocol."],"discriminator":[61,183,185,188,82,81,141,197],"accounts":[{"name":"payer","docs":["only super admin can create admin group"],"writable":true,"signer":true,"address":"AY196f8U5EvM999PVnvLmyvaUnzL4GLiFaGKUgnJXN6o"},{"name":"admin_group","docs":["update amm admin group account to store admin permissions."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}}],"args":[{"name":"params","type":{"defined":{"name":"UpdateAdminGroupParams"}}}]},{"name":"update_amm_config","docs":["Updates the owner of the amm config","Must be called by the current owner or admin","","# Arguments","","* `ctx`- The context of accounts","* `trade_fee_rate`- The new trade fee rate of amm config, be set when `param` is 0","* `protocol_fee_rate`- The new protocol fee rate of amm config, be set when `param` is 1","* `fund_fee_rate`- The new fund fee rate of amm config, be set when `param` is 2","* `new_owner`- The config's new owner, be set when `param` is 3","* `new_fund_owner`- The config's new fund owner, be set when `param` is 4","* `param`- The value can be 0 | 1 | 2 | 3 | 4, otherwise will report a error",""],"discriminator":[49,60,174,136,154,28,116,200],"accounts":[{"name":"owner","docs":["The amm config owner or admin"],"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"amm_config","docs":["Amm config account to be changed"],"writable":true}],"args":[{"name":"param","type":"u8"},{"name":"value","type":"u32"}]},{"name":"update_operation_account","docs":["Update the operation account","","# Arguments","","* `ctx`- The context of accounts","* `param`- The value can be 0 | 1 | 2 | 3, otherwise will report a error","* `keys`- update operation owner when the `param` is 0","remove operation owner when the `param` is 1","update whitelist mint when the `param` is 2","remove whitelist mint when the `param` is 3",""],"discriminator":[127,70,119,40,188,227,61,7],"accounts":[{"name":"owner","docs":["Address to be set as operation account owner."],"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"operation_state","docs":["Initialize operation state account to store operation owner address and white list mint."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"param","type":"u8"},{"name":"keys","type":{"vec":"pubkey"}}]},{"name":"update_pool_status","docs":["Update pool status for given value","","# Arguments","","* `ctx`- The context of accounts","* `status` - The value of status",""],"discriminator":[130,87,108,6,46,224,117,123],"accounts":[{"name":"authority","signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_state","writable":true}],"args":[{"name":"status","type":"u8"}]},{"name":"update_reward_infos","docs":["Update rewards info of the given pool, can be called for everyone","","# Arguments","","* `ctx`- The context of accounts",""],"discriminator":[163,172,224,52,11,154,106,223],"accounts":[{"name":"pool_state","docs":["The liquidity pool for which reward info to update"],"writable":true}],"args":[]},{"name":"withdraw_offchain_reward","docs":["withdraw offchain reward from the pool"],"discriminator":[86,53,59,76,217,38,71,213],"accounts":[{"name":"authority","docs":["The authority make decision that who can withdraw the offchain reward."],"signer":true},{"name":"admin_group","docs":["Initialize amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_id","docs":["the pool id, which is the pool state account."],"relations":["reward_config"]},{"name":"token_mint"},{"name":"receiver_token_account","docs":["the address who receive the withdrawn offchain reward."],"writable":true},{"name":"reward_vault_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"reward_config"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"reward_config","docs":["The offchain reward config account, it also is the reward vault account."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,102,102,99,104,97,105,110,95,114,101,119,97,114,100]},{"kind":"account","path":"pool_id"}]}},{"name":"token_program","docs":["Spl token program or token program 2022"]},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"amount","type":"u64"}]}],"accounts":[{"name":"AmmAdminGroup","discriminator":[128,128,234,30,61,172,188,123]},{"name":"AmmConfig","discriminator":[218,244,33,104,203,203,43,111]},{"name":"ObservationState","discriminator":[122,174,197,53,129,9,165,132]},{"name":"OffchainRewardConfig","discriminator":[118,52,115,150,99,69,164,76]},{"name":"OperationState","discriminator":[19,236,58,237,81,222,183,252]},{"name":"PersonalPositionState","discriminator":[70,111,150,126,230,15,25,117]},{"name":"PoolState","discriminator":[247,237,227,245,215,195,222,70]},{"name":"ProtocolPositionState","discriminator":[100,226,145,99,146,218,160,106]},{"name":"SupportMintAssociated","discriminator":[134,40,183,79,12,112,162,53]},{"name":"TickArrayBitmapExtension","discriminator":[60,150,36,219,97,128,139,153]}],"events":[{"name":"CollectPersonalFeeEvent","discriminator":[166,174,105,192,81,161,83,105]},{"name":"CollectProtocolFeeEvent","discriminator":[206,87,17,79,45,41,213,61]},{"name":"ConfigChangeEvent","discriminator":[247,189,7,119,106,112,95,151]},{"name":"CreatePersonalPositionEvent","discriminator":[100,30,87,249,196,223,154,206]},{"name":"DecreaseLiquidityEvent","discriminator":[58,222,86,58,68,50,85,56]},{"name":"IncreaseLiquidityEvent","discriminator":[49,79,105,212,32,34,30,84]},{"name":"LiquidityCalculateEvent","discriminator":[237,112,148,230,57,84,180,162]},{"name":"LiquidityChangeEvent","discriminator":[126,240,175,206,158,88,153,107]},{"name":"ModifyAmmAdminGroupEvent","discriminator":[218,118,87,81,53,80,18,235]},{"name":"PoolCreatedEvent","discriminator":[25,94,75,47,112,99,53,63]},{"name":"SwapEvent","discriminator":[64,198,205,232,38,8,113,226]},{"name":"UpdateRewardInfosEvent","discriminator":[109,127,186,78,114,65,37,236]}],"errors":[{"code":6000,"name":"LOK","msg":"LOK"},{"code":6001,"name":"NotApproved","msg":"Not approved"},{"code":6002,"name":"InvalidUpdateConfigFlag","msg":"invalid update amm config flag"},{"code":6003,"name":"AccountLack","msg":"Account lack"},{"code":6004,"name":"ClosePositionErr","msg":"Remove liquitity, collect fees owed and reward then you can close position account"},{"code":6005,"name":"ZeroMintAmount","msg":"Minting amount should be greater than 0"},{"code":6006,"name":"InvalidTickIndex","msg":"Tick out of range"},{"code":6007,"name":"TickInvalidOrder","msg":"The lower tick must be below the upper tick"},{"code":6008,"name":"TickLowerOverflow","msg":"The tick must be greater, or equal to the minimum tick(-443636)"},{"code":6009,"name":"TickUpperOverflow","msg":"The tick must be lesser than, or equal to the maximum tick(443636)"},{"code":6010,"name":"TickAndSpacingNotMatch","msg":"tick % tick_spacing must be zero"},{"code":6011,"name":"InvalidTickArray","msg":"Invalid tick array account"},{"code":6012,"name":"InvalidTickArrayBoundary","msg":"Invalid tick array boundary"},{"code":6013,"name":"SqrtPriceLimitOverflow","msg":"Square root price limit overflow"},{"code":6014,"name":"SqrtPriceX64","msg":"sqrt_price_x64 out of range"},{"code":6015,"name":"LiquiditySubValueErr","msg":"Liquidity sub delta L must be smaller than before"},{"code":6016,"name":"LiquidityAddValueErr","msg":"Liquidity add delta L must be greater, or equal to before"},{"code":6017,"name":"InvalidLiquidity","msg":"Invalid liquidity when update position"},{"code":6018,"name":"ForbidBothZeroForSupplyLiquidity","msg":"Both token amount must not be zero while supply liquidity"},{"code":6019,"name":"LiquidityInsufficient","msg":"Liquidity insufficient"},{"code":6020,"name":"TransactionTooOld","msg":"Transaction too old"},{"code":6021,"name":"PriceSlippageCheck","msg":"Price slippage check"},{"code":6022,"name":"TooLittleOutputReceived","msg":"Too little output received"},{"code":6023,"name":"TooMuchInputPaid","msg":"Too much input paid"},{"code":6024,"name":"ZeroAmountSpecified","msg":"Swap special amount can not be zero"},{"code":6025,"name":"InvalidInputPoolVault","msg":"Input pool vault is invalid"},{"code":6026,"name":"TooSmallInputOrOutputAmount","msg":"Swap input or output amount is too small"},{"code":6027,"name":"NotEnoughTickArrayAccount","msg":"Not enought tick array account"},{"code":6028,"name":"InvalidFirstTickArrayAccount","msg":"Invalid first tick array account"},{"code":6029,"name":"InvalidRewardIndex","msg":"Invalid reward index"},{"code":6030,"name":"FullRewardInfo","msg":"The init reward token reach to the max"},{"code":6031,"name":"RewardTokenAlreadyInUse","msg":"The init reward token already in use"},{"code":6032,"name":"ExceptRewardMint","msg":"The reward tokens must contain one of pool vault mint except the last reward"},{"code":6033,"name":"InvalidRewardInitParam","msg":"Invalid reward init param"},{"code":6034,"name":"InvalidRewardDesiredAmount","msg":"Invalid collect reward desired amount"},{"code":6035,"name":"InvalidRewardInputAccountNumber","msg":"Invalid collect reward input account number"},{"code":6036,"name":"InvalidRewardPeriod","msg":"Invalid reward period"},{"code":6037,"name":"NotApproveUpdateRewardEmissiones","msg":"Modification of emissiones is allowed within 72 hours from the end of the previous cycle"},{"code":6038,"name":"UnInitializedRewardInfo","msg":"uninitialized reward info"},{"code":6039,"name":"NotSupportMint","msg":"Not support token_2022 mint extension"},{"code":6040,"name":"MissingTickArrayBitmapExtensionAccount","msg":"Missing tickarray bitmap extension account"},{"code":6041,"name":"InsufficientLiquidityForDirection","msg":"Insufficient liquidity for this direction"},{"code":6042,"name":"MaxTokenOverflow","msg":"Max token overflow"},{"code":6043,"name":"CalculateOverflow","msg":"Calculate overflow"},{"code":6044,"name":"TransferFeeCalculateNotMatch","msg":"TransferFee calculate not match"},{"code":6045,"name":"IllegalAccountOwner","msg":"invalid account owner"},{"code":6046,"name":"InvalidAccount","msg":"Invalid account"},{"code":6047,"name":"DecayFeeNeitherOnSellMint0NorMint1","msg":"Invalid decay fee params"},{"code":6048,"name":"SwapDynamicFeeEnabled","msg":"Swap dynamic fee is enabled, must use swap_v3_dyn instruction"},{"code":6049,"name":"InvalidPythOracleAccount","msg":"Invalid pyth oracle account"},{"code":6050,"name":"PythPriceStale","msg":"Pyth price is stale"},{"code":6051,"name":"InvalidSwapDynamicFeeParams","msg":"Invalid swap dynamic fee params"}],"types":[{"name":"AmmAdminGroup","docs":["Holds the admin group information."],"type":{"kind":"struct","fields":[{"name":"fee_keeper","docs":["the address who can hold the fee,","anyone can trigger the fee collection action,"],"type":"pubkey"},{"name":"reward_config_manager","docs":["the address who can config the reward(config, deposit, withdraw),","deposit reward, set the account who can deposit the reward, withdraw the remaining reward(withdraw)"],"type":"pubkey"},{"name":"reward_claim_manager","docs":["the address who can manage the offchain reward claim"],"type":"pubkey"},{"name":"pool_manager","docs":["the address who can manage the pool create action,","without this account's permission, no one can create a pool"],"type":"pubkey"},{"name":"emergency_manager","docs":["the address who can manage the emergency action,","emergency action includes stop/resume the pool, stop/resume withdraw lp"],"type":"pubkey"},{"name":"normal_manager","docs":["normal action manager,","such as create amm config, update amm config"],"type":"pubkey"},{"name":"pad","docs":["The space required for the account. may be used for future extensions."],"type":{"array":["pubkey",6]}}]}},{"name":"AmmConfig","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"index","type":"u16"},{"name":"owner","docs":["Address of the protocol owner"],"type":"pubkey"},{"name":"protocol_fee_rate","docs":["The protocol fee"],"type":"u32"},{"name":"trade_fee_rate","docs":["The trade fee, denominated in hundredths of a bip (10^-6)"],"type":"u32"},{"name":"tick_spacing","docs":["The tick spacing"],"type":"u16"},{"name":"fund_fee_rate","docs":["The fund fee, denominated in hundredths of a bip (10^-6)"],"type":"u32"},{"name":"padding_u32","type":"u32"},{"name":"fund_owner","type":"pubkey"},{"name":"padding","type":{"array":["u64",3]}}]}},{"name":"CollectPersonalFeeEvent","docs":["Emitted when tokens are collected for a position"],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which underlying tokens were collected"],"type":"pubkey"},{"name":"recipient_token_account_0","docs":["The token account that received the collected token_0 tokens"],"type":"pubkey"},{"name":"recipient_token_account_1","docs":["The token account that received the collected token_1 tokens"],"type":"pubkey"},{"name":"amount_0","docs":["The amount of token_0 owed to the position that was collected"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_1 owed to the position that was collected"],"type":"u64"}]}},{"name":"CollectProtocolFeeEvent","docs":["Emitted when the collected protocol fees are withdrawn by the factory owner"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool whose protocol fee is collected"],"type":"pubkey"},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"type":"pubkey"},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"type":"pubkey"},{"name":"amount_0","docs":["The amount of token_0 protocol fees that is withdrawn"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_0 protocol fees that is withdrawn"],"type":"u64"}]}},{"name":"ConfigChangeEvent","docs":["Emitted when create or update a config"],"type":{"kind":"struct","fields":[{"name":"index","type":"u16"},{"name":"owner","type":"pubkey"},{"name":"protocol_fee_rate","type":"u32"},{"name":"trade_fee_rate","type":"u32"},{"name":"tick_spacing","type":"u16"},{"name":"fund_fee_rate","type":"u32"},{"name":"fund_owner","type":"pubkey"}]}},{"name":"CreatePersonalPositionEvent","docs":["Emitted when create a new position"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for which liquidity was added"],"type":"pubkey"},{"name":"minter","docs":["The address that create the position"],"type":"pubkey"},{"name":"nft_owner","docs":["The owner of the position and recipient of any minted liquidity"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity minted to the position range"],"type":"u128"},{"name":"deposit_amount_0","docs":["The amount of token_0 was deposit for the liquidity"],"type":"u64"},{"name":"deposit_amount_1","docs":["The amount of token_1 was deposit for the liquidity"],"type":"u64"},{"name":"deposit_amount_0_transfer_fee","docs":["The token transfer fee for deposit_amount_0"],"type":"u64"},{"name":"deposit_amount_1_transfer_fee","docs":["The token transfer fee for deposit_amount_1"],"type":"u64"}]}},{"name":"CreatePoolDecayFeeParams","type":{"kind":"struct","fields":[{"name":"sqrt_price_x64","docs":["The initial sqrt price of the pool, in x64 format."],"type":"u128"},{"name":"open_time","docs":["The open time of the pool, can be set to a future time; it should be None or future time.","If None, the pool will be opened immediately."],"type":{"option":"u64"}},{"name":"use_decay_fee","docs":["Whether to use decay fee for the pool.","If true, the pool will use decay fee."],"type":"bool"},{"name":"decay_fee_on_sell_mint0","docs":["Whether to use decay fee on sell for mint0 and mint1.","If true, the pool will use decay fee on sell for mint0 and mint1","these two fields are only used when use_decay_fee is true, and at least one of them must be true."],"type":"bool"},{"name":"decay_fee_on_sell_mint1","type":"bool"},{"name":"init_decay_fee_rate","docs":["The initial decay fee rate for the pool, in percentage.(1=1%)"],"type":"u8"},{"name":"decay_fee_decrease_rate","docs":["decrease rate for the decay fee, in percentage.(1=1%)"],"type":"u8"},{"name":"decay_fee_decrease_interval","docs":["The interval for decreasing the decay fee, in seconds.","how to calculate the current decay fee:","interval_count = (current_time - open_time) / decay_fee_decrease_interval","current_decay_fee = init_decay_fee_rate*((1-decay_fee_decrease_rate/100)^interval_count)"],"type":"u8"}]}},{"name":"DecreaseLiquidityEvent","docs":["Emitted when liquidity is decreased."],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which liquidity was decreased"],"type":"pubkey"},{"name":"liquidity","docs":["The amount by which liquidity for the position was decreased"],"type":"u128"},{"name":"decrease_amount_0","docs":["The amount of token_0 that was paid for the decrease in liquidity"],"type":"u64"},{"name":"decrease_amount_1","docs":["The amount of token_1 that was paid for the decrease in liquidity"],"type":"u64"},{"name":"fee_amount_0","type":"u64"},{"name":"fee_amount_1","docs":["The amount of token_1 fee"],"type":"u64"},{"name":"reward_amounts","docs":["The amount of rewards"],"type":{"array":["u64",3]}},{"name":"transfer_fee_0","docs":["The amount of token_0 transfer fee"],"type":"u64"},{"name":"transfer_fee_1","docs":["The amount of token_1 transfer fee"],"type":"u64"}]}},{"name":"IncreaseLiquidityEvent","docs":["Emitted when liquidity is increased."],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which liquidity was increased"],"type":"pubkey"},{"name":"liquidity","docs":["The amount by which liquidity for the NFT position was increased"],"type":"u128"},{"name":"amount_0","docs":["The amount of token_0 that was paid for the increase in liquidity"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_1 that was paid for the increase in liquidity"],"type":"u64"},{"name":"amount_0_transfer_fee","docs":["The token transfer fee for amount_0"],"type":"u64"},{"name":"amount_1_transfer_fee","docs":["The token transfer fee for amount_1"],"type":"u64"}]}},{"name":"InitAdminGroupParams","type":{"kind":"struct","fields":[{"name":"fee_keeper","docs":["the address who can hold the fee,","anyone can trigger the fee collection action,"],"type":"pubkey"},{"name":"reward_config_manager","docs":["the address who can config the reward(config, deposit, withdraw),","deposit reward, set the account who can deposit the reward, withdraw the remaining reward(withdraw)"],"type":"pubkey"},{"name":"reward_claim_manager","docs":["the address who can manage the offchain reward claim"],"type":"pubkey"},{"name":"pool_manager","docs":["the address who can manage the pool create action,","without this account's permission, no one can create a pool"],"type":"pubkey"},{"name":"emergency_manager","docs":["the address who can manage the emergency action,","emergency action includes stop/resume the pool, stop/resume withdraw lp"],"type":"pubkey"},{"name":"normal_manager","docs":["normal action manager,","such as create amm config, update amm config"],"type":"pubkey"}]}},{"name":"InitializeRewardParam","type":{"kind":"struct","fields":[{"name":"open_time","docs":["Reward open time"],"type":"u64"},{"name":"end_time","docs":["Reward end time"],"type":"u64"},{"name":"emissions_per_second_x64","docs":["Token reward per second are earned per unit of liquidity"],"type":"u128"}]}},{"name":"LiquidityCalculateEvent","docs":["Emitted when liquidity decreased or increase."],"type":{"kind":"struct","fields":[{"name":"pool_liquidity","docs":["The pool liquidity before decrease or increase"],"type":"u128"},{"name":"pool_sqrt_price_x64","docs":["The pool price when decrease or increase in liquidity"],"type":"u128"},{"name":"pool_tick","docs":["The pool tick when decrease or increase in liquidity"],"type":"i32"},{"name":"calc_amount_0","docs":["The amount of token_0 that was calculated for the decrease or increase in liquidity"],"type":"u64"},{"name":"calc_amount_1","docs":["The amount of token_1 that was calculated for the decrease or increase in liquidity"],"type":"u64"},{"name":"trade_fee_owed_0","type":"u64"},{"name":"trade_fee_owed_1","docs":["The amount of token_1 fee"],"type":"u64"},{"name":"transfer_fee_0","docs":["The amount of token_0 transfer fee without trade_fee_amount_0"],"type":"u64"},{"name":"transfer_fee_1","docs":["The amount of token_1 transfer fee without trade_fee_amount_0"],"type":"u64"}]}},{"name":"LiquidityChangeEvent","docs":["Emitted pool liquidity change when increase and decrease liquidity"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for swap"],"type":"pubkey"},{"name":"tick","docs":["The tick of the pool"],"type":"i32"},{"name":"tick_lower","docs":["The tick lower of position"],"type":"i32"},{"name":"tick_upper","docs":["The tick lower of position"],"type":"i32"},{"name":"liquidity_before","docs":["The liquidity of the pool before liquidity change"],"type":"u128"},{"name":"liquidity_after","docs":["The liquidity of the pool after liquidity change"],"type":"u128"}]}},{"name":"ModifyAmmAdminGroupEvent","type":{"kind":"struct","fields":[{"name":"fee_keeper","type":"pubkey"},{"name":"reward_config_manager","type":"pubkey"},{"name":"reward_claim_manager","type":"pubkey"},{"name":"pool_manager","type":"pubkey"},{"name":"emergency_manager","type":"pubkey"},{"name":"normal_manager","type":"pubkey"}]}},{"name":"Observation","docs":["The element of observations in ObservationState"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"block_timestamp","docs":["The block timestamp of the observation"],"type":"u32"},{"name":"tick_cumulative","docs":["the cumulative of tick during the duration time"],"type":"i64"},{"name":"padding","docs":["padding for feature update"],"type":{"array":["u64",4]}}]}},{"name":"ObservationState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"initialized","docs":["Whether the ObservationState is initialized"],"type":"bool"},{"name":"recent_epoch","docs":["recent update epoch"],"type":"u64"},{"name":"observation_index","docs":["the most-recently updated index of the observations array"],"type":"u16"},{"name":"pool_id","docs":["belongs to which pool"],"type":"pubkey"},{"name":"observations","docs":["observation array"],"type":{"array":[{"defined":{"name":"Observation"}},100]}},{"name":"padding","docs":["padding for feature update"],"type":{"array":["u64",4]}}]}},{"name":"OffchainRewardConfig","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"pool_id","docs":["the pool state address"],"type":"pubkey"},{"name":"reward_vault","docs":["the vault to hold the reward","vault address is this config address"],"type":"pubkey"},{"name":"vault_bump","docs":["Bump to identify vault PDA"],"type":{"array":["u8",1]}},{"name":"reward_mint_vec","docs":["reward token mint address list","reward token account is ATA(reward_vault, reward_mint)"],"type":{"vec":"pubkey"}}]}},{"name":"OperationState","docs":["Holds the current owner of the factory"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"operation_owners","docs":["Address of the operation owner"],"type":{"array":["pubkey",10]}},{"name":"whitelist_mints","docs":["The mint address of whitelist to emit reward"],"type":{"array":["pubkey",100]}}]}},{"name":"PersonalPositionState","type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":{"array":["u8",1]}},{"name":"nft_mint","docs":["Mint address of the tokenized position"],"type":"pubkey"},{"name":"pool_id","docs":["The ID of the pool with which this token is connected"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"fee_growth_inside_0_last_x64","docs":["The token_0 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"fee_growth_inside_1_last_x64","docs":["The token_1 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"token_fees_owed_0","docs":["The fees owed to the position owner in token_0, as of the last computation"],"type":"u64"},{"name":"token_fees_owed_1","docs":["The fees owed to the position owner in token_1, as of the last computation"],"type":"u64"},{"name":"reward_infos","type":{"array":[{"defined":{"name":"PositionRewardInfo"}},3]}},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u64",7]}}]}},{"name":"PoolCreatedEvent","docs":["Emitted when a pool is created and initialized with a starting price",""],"type":{"kind":"struct","fields":[{"name":"token_mint_0","docs":["The first token of the pool by address sort order"],"type":"pubkey"},{"name":"token_mint_1","docs":["The second token of the pool by address sort order"],"type":"pubkey"},{"name":"tick_spacing","docs":["The minimum number of ticks between initialized ticks"],"type":"u16"},{"name":"pool_state","docs":["The address of the created pool"],"type":"pubkey"},{"name":"sqrt_price_x64","docs":["The initial sqrt price of the pool, as a Q64.64"],"type":"u128"},{"name":"tick","docs":["The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool"],"type":"i32"},{"name":"token_vault_0","docs":["Vault of token_0"],"type":"pubkey"},{"name":"token_vault_1","docs":["Vault of token_1"],"type":"pubkey"}]}},{"name":"PoolState","docs":["The pool state","","PDA of `[POOL_SEED, config, token_mint_0, token_mint_1]`",""],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":{"array":["u8",1]}},{"name":"amm_config","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"token_mint_0","docs":["Token pair of the pool, where token_mint_0 address < token_mint_1 address"],"type":"pubkey"},{"name":"token_mint_1","type":"pubkey"},{"name":"token_vault_0","docs":["Token pair vault"],"type":"pubkey"},{"name":"token_vault_1","type":"pubkey"},{"name":"observation_key","docs":["observation account key"],"type":"pubkey"},{"name":"mint_decimals_0","docs":["mint0 and mint1 decimals"],"type":"u8"},{"name":"mint_decimals_1","type":"u8"},{"name":"tick_spacing","docs":["The minimum number of ticks between initialized ticks"],"type":"u16"},{"name":"liquidity","docs":["The currently in range liquidity available to the pool."],"type":"u128"},{"name":"sqrt_price_x64","docs":["The current price of the pool as a sqrt(token_1/token_0) Q64.64 value"],"type":"u128"},{"name":"tick_current","docs":["The current tick of the pool, i.e. according to the last tick transition that was run."],"type":"i32"},{"name":"padding3","type":"u16"},{"name":"padding4","type":"u16"},{"name":"fee_growth_global_0_x64","docs":["The fee growth as a Q64.64 number, i.e. fees of token_0 and token_1 collected per","unit of liquidity for the entire life of the pool."],"type":"u128"},{"name":"fee_growth_global_1_x64","type":"u128"},{"name":"protocol_fees_token_0","docs":["The amounts of token_0 and token_1 that are owed to the protocol."],"type":"u64"},{"name":"protocol_fees_token_1","type":"u64"},{"name":"swap_in_amount_token_0","docs":["The amounts in and out of swap token_0 and token_1"],"type":"u128"},{"name":"swap_out_amount_token_1","type":"u128"},{"name":"swap_in_amount_token_1","type":"u128"},{"name":"swap_out_amount_token_0","type":"u128"},{"name":"status","docs":["Bitwise representation of the state of the pool","bit0, 1: disable open position and increase liquidity, 0: normal","bit1, 1: disable decrease liquidity, 0: normal","bit2, 1: disable collect fee, 0: normal","bit3, 1: disable collect reward, 0: normal","bit4, 1: disable swap, 0: normal"],"type":"u8"},{"name":"padding","docs":["Leave blank for future use"],"type":{"array":["u8",3]}},{"name":"trade_fee_rate","docs":["如果该值不为0, 则会覆盖 amm config 中的 trade_fee_rate 参数,作为实际收取的 trade fee rate","The trade fee, denominated in hundredths of a bip (10^-6)"],"type":"u32"},{"name":"reward_infos","type":{"array":[{"defined":{"name":"RewardInfo"}},3]}},{"name":"tick_array_bitmap","docs":["Packed initialized tick array state"],"type":{"array":["u64",16]}},{"name":"total_fees_token_0","docs":["except protocol_fee and fund_fee"],"type":"u64"},{"name":"total_fees_claimed_token_0","docs":["except protocol_fee and fund_fee"],"type":"u64"},{"name":"total_fees_token_1","type":"u64"},{"name":"total_fees_claimed_token_1","type":"u64"},{"name":"fund_fees_token_0","type":"u64"},{"name":"fund_fees_token_1","type":"u64"},{"name":"open_time","type":"u64"},{"name":"recent_epoch","type":"u64"},{"name":"decay_fee_flag","docs":["--- decay fee flag","bit0, 1: use decay fee, 0: not use decay fee","bit1, 1: use decay fee on sell for mint0, 0: not use decay fee on sell for mint0","bit2, 1: use decay fee on sell for mint1, 0: not use decay fee on sell for mint1","--- swap-dynamic-fee-flag","bit3, 1: token1 as quote token; token0 as quote token","bit4, 1: 启用 swap-dynamic-fee; 0: 不启用 swap-dynamic-fee"],"type":"u8"},{"name":"decay_fee_init_fee_rate","docs":["The initial decay fee rate for the pool, in percentage.(1=1%)"],"type":"u8"},{"name":"decay_fee_decrease_rate","docs":["decrease rate for the decay fee, in percentage.(1=1%)"],"type":"u8"},{"name":"decay_fee_decrease_interval","docs":["The interval for decreasing the decay fee, in seconds."],"type":"u8"},{"name":"arbitrage_fee_buffer_ppm","docs":["arbitrage_fee: 手工设置的一个容忍度,价差小于该值,不收取 arbitrageFee;单位取 10^-6(percent-per-million);"],"type":"u16"},{"name":"trade_slippage_fee_base","docs":["trade_slippage_fee: 公式中的b值,单位取 1/1000"],"type":"u8"},{"name":"trade_slippage_fee_trade_size_threshold","docs":["归一化(按quote数量计算)的swap-size值; 单位取 100"],"type":"u8"},{"name":"imbalance_fee_base","docs":["公式中的b,单位取1/10"],"type":"u8"},{"name":"imbalance_fee_x","docs":["公式中的x;单位取 1/100"],"type":"u8"},{"name":"padding1_1","type":{"array":["u8",6]}},{"name":"token0_pyth_feed_id","docs":["Pyth feed id for base token."],"type":{"array":["u8",32]}},{"name":"token1_pyth_feed_id","docs":["Pyth feed id for quote token."],"type":{"array":["u8",32]}},{"name":"padding1","type":{"array":["u64",14]}},{"name":"padding2","type":{"array":["u64",32]}}]}},{"name":"PositionRewardInfo","type":{"kind":"struct","fields":[{"name":"growth_inside_last_x64","type":"u128"},{"name":"reward_amount_owed","type":"u64"}]}},{"name":"ProtocolPositionState","docs":["Info stored for each user's position"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"pool_id","docs":["The ID of the pool with which this token is connected"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"fee_growth_inside_0_last_x64","docs":["The token_0 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"fee_growth_inside_1_last_x64","docs":["The token_1 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"token_fees_owed_0","docs":["The fees owed to the position owner in token_0"],"type":"u64"},{"name":"token_fees_owed_1","docs":["The fees owed to the position owner in token_1"],"type":"u64"},{"name":"reward_growth_inside","docs":["The reward growth per unit of liquidity as of the last update to liquidity"],"type":{"array":["u128",3]}},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u64",7]}}]}},{"name":"RewardInfo","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"reward_state","docs":["Reward state"],"type":"u8"},{"name":"open_time","docs":["Reward open time"],"type":"u64"},{"name":"end_time","docs":["Reward end time"],"type":"u64"},{"name":"last_update_time","docs":["Reward last update time"],"type":"u64"},{"name":"emissions_per_second_x64","docs":["Q64.64 number indicates how many tokens per second are earned per unit of liquidity."],"type":"u128"},{"name":"reward_total_emissioned","docs":["The total amount of reward emissioned"],"type":"u64"},{"name":"reward_claimed","docs":["The total amount of claimed reward"],"type":"u64"},{"name":"token_mint","docs":["Reward token mint."],"type":"pubkey"},{"name":"token_vault","docs":["Reward vault token account."],"type":"pubkey"},{"name":"authority","docs":["The owner that has permission to set reward param"],"type":"pubkey"},{"name":"reward_growth_global_x64","docs":["Q64.64 number that tracks the total tokens earned per unit of liquidity since the reward","emissions were turned on."],"type":"u128"}]}},{"name":"SetSwapDynamicFeeParamsInput","type":{"kind":"struct","fields":[{"name":"enabled","docs":["是否启用 swap-dynamic-fee"],"type":"bool"},{"name":"arbitrage_fee_buffer_ppm","docs":["arbitrage_fee 缓冲值,单位 ppm (10^-6)"],"type":{"option":"u16"}},{"name":"trade_slippage_fee_base","docs":["trade_slippage_fee 基数,单位 1/1000"],"type":{"option":"u8"}},{"name":"trade_slippage_fee_trade_size_threshold","docs":["trade_slippage_fee 阈值,单位 100"],"type":{"option":"u8"}},{"name":"imbalance_fee_base","docs":["imbalance_fee 基数,单位 1/10"],"type":{"option":"u8"}},{"name":"imbalance_fee_x","docs":["imbalance_fee 阈值,单位 1/100"],"type":{"option":"u8"}},{"name":"token0_pyth_feed_id","docs":["token0 的 Pyth feed id"],"type":{"option":{"array":["u8",32]}}},{"name":"token1_pyth_feed_id","docs":["token1 的 Pyth feed id"],"type":{"option":{"array":["u8",32]}}}]}},{"name":"SupportMintAssociated","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"mint","docs":["Address of the supported token22 mint"],"type":"pubkey"},{"name":"padding","type":{"array":["u64",8]}}]}},{"name":"SwapEvent","docs":["Emitted by when a swap is performed for a pool"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for which token_0 and token_1 were swapped"],"type":"pubkey"},{"name":"sender","docs":["The address that initiated the swap call, and that received the callback"],"type":"pubkey"},{"name":"token_account_0","docs":["The payer token account in zero for one swaps, or the recipient token account","in one for zero swaps"],"type":"pubkey"},{"name":"token_account_1","docs":["The payer token account in one for zero swaps, or the recipient token account","in zero for one swaps"],"type":"pubkey"},{"name":"amount_0","docs":["The real delta amount of the token_0 of the pool or user"],"type":"u64"},{"name":"transfer_fee_0","docs":["The transfer fee charged by the withheld_amount of the token_0"],"type":"u64"},{"name":"amount_1","docs":["The real delta of the token_1 of the pool or user"],"type":"u64"},{"name":"transfer_fee_1","docs":["The transfer fee charged by the withheld_amount of the token_1"],"type":"u64"},{"name":"zero_for_one","docs":["if true, amount_0 is negtive and amount_1 is positive"],"type":"bool"},{"name":"sqrt_price_x64","docs":["The sqrt(price) of the pool after the swap, as a Q64.64"],"type":"u128"},{"name":"liquidity","docs":["The liquidity of the pool after the swap"],"type":"u128"},{"name":"tick","docs":["The log base 1.0001 of price of the pool after the swap"],"type":"i32"}]}},{"name":"TickArrayBitmapExtension","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"positive_tick_array_bitmap","docs":["Packed initialized tick array state for start_tick_index is positive"],"type":{"array":[{"array":["u64",8]},14]}},{"name":"negative_tick_array_bitmap","docs":["Packed initialized tick array state for start_tick_index is negitive"],"type":{"array":[{"array":["u64",8]},14]}}]}},{"name":"UpdateAdminGroupParams","type":{"kind":"struct","fields":[{"name":"fee_keeper","docs":["the address who can hold the fee,","anyone can trigger the fee collection action,"],"type":{"option":"pubkey"}},{"name":"reward_config_manager","docs":["the address who can config the reward(config, deposit, withdraw),","deposit reward, set the account who can deposit the reward, withdraw the remaining reward(withdraw)"],"type":{"option":"pubkey"}},{"name":"reward_claim_manager","docs":["the address who can manage the offchain reward claim"],"type":{"option":"pubkey"}},{"name":"pool_manager","docs":["the address who can manage the pool create action,","without this account's permission, no one can create a pool"],"type":{"option":"pubkey"}},{"name":"emergency_manager","docs":["the address who can manage the emergency action,","emergency action includes stop/resume the pool, stop/resume withdraw lp"],"type":{"option":"pubkey"}},{"name":"normal_manager","docs":["normal action manager,","such as create amm config, update amm config"],"type":{"option":"pubkey"}}]}},{"name":"UpdateRewardInfosEvent","docs":["Emitted when Reward are updated for a pool"],"type":{"kind":"struct","fields":[{"name":"reward_growth_global_x64","docs":["Reward info"],"type":{"array":["u128",3]}}]}}]} \ No newline at end of file diff --git a/idls/carrot.CarrotwivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s.json b/idls/carrot.CarrotwivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s.json new file mode 100644 index 0000000..f3bbb6d --- /dev/null +++ b/idls/carrot.CarrotwivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"carrot","instructions":[{"name":"initVault","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"shares","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"InitVaultArgs"}}]},{"name":"transferVaultAuthority","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"newAuthority","isMut":false,"isSigner":false},{"name":"oldAuthority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"pauseVault","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true}],"args":[{"name":"args","type":{"defined":"PauseVaultArgs"}}]},{"name":"migrateVaultAccount","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"distributeVaultFees","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"shares","isMut":true,"isSigner":false},{"name":"sharesDestination","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateFee","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdateFeeArgs"}}]},{"name":"issue","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"shares","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"asset","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"userAssetAta","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"assetTokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"IssueArgs"}}]},{"name":"redeem","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"shares","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"asset","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"userAssetAta","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"assetTokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"RedeemArgs"}}]},{"name":"addAsset","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"assetOracle","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateAsset","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdateAssetArgs"}}]},{"name":"removeAsset","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"asset","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"removeStrategy","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateStrategy","accounts":[{"name":"vault","isMut":false,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"UpdateStrategyArgs"}}]},{"name":"marginfiSupplyStrategyInit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"marginfiGroup","isMut":false,"isSigner":false},{"name":"marginfiAccount","isMut":true,"isSigner":true},{"name":"marginfiBank","isMut":false,"isSigner":false},{"name":"marginfiBankLiquidityVault","isMut":false,"isSigner":false},{"name":"marginfiBankLiquidityVaultAuthority","isMut":false,"isSigner":false},{"name":"marginfiOracle","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"marginfiProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MarginfiSupplyStrategyInitArgs"}}]},{"name":"marginfiSupplyStrategyDeposit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"marginfiGroup","isMut":false,"isSigner":false},{"name":"marginfiAccount","isMut":true,"isSigner":false},{"name":"marginfiBank","isMut":true,"isSigner":false},{"name":"marginfiBankLiquidityVault","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"marginfiProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MarginfiSupplyStrategyDepositArgs"}}]},{"name":"marginfiSupplyStrategyWithdraw","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"marginfiGroup","isMut":false,"isSigner":false},{"name":"marginfiAccount","isMut":true,"isSigner":false},{"name":"marginfiBank","isMut":true,"isSigner":false},{"name":"marginfiBankLiquidityVault","isMut":true,"isSigner":false},{"name":"marginfiBankLiquidityVaultAuthority","isMut":true,"isSigner":false},{"name":"marginfiOracle","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"marginfiProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MarginfiSupplyStrategyWithdrawArgs"}}]},{"name":"marginfiSupplyStrategyClaimEmissions","accounts":[{"name":"vault","isMut":false,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"marginfiGroup","isMut":false,"isSigner":false},{"name":"marginfiAccount","isMut":true,"isSigner":false},{"name":"marginfiBank","isMut":true,"isSigner":false},{"name":"marginfiEmissionsVault","isMut":true,"isSigner":false},{"name":"marginfiEmissionsAuth","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"marginfiProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"klendSupplyStrategyInit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"assetPythOracle","isMut":true,"isSigner":false},{"name":"scopePrices","isMut":true,"isSigner":false},{"name":"userMetadata","isMut":true,"isSigner":false},{"name":"obligation","isMut":true,"isSigner":false},{"name":"obligationFarm","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":true,"isSigner":false},{"name":"lendingMarketAuthority","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":false,"isSigner":false},{"name":"reserveDestinationDepositCollateral","isMut":false,"isSigner":false},{"name":"reserveFarmState","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"kfarmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"KlendSupplyStrategyInitArgs"}}]},{"name":"klendSupplyStrategyDeposit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":true,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveDestinationDepositCollateral","isMut":true,"isSigner":false},{"name":"vaultCollateralAta","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false},{"name":"obligation","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"instructionsSysvar","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"assetTokenProgram","isMut":false,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"kfarmsProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"KlendSupplyStrategyDepositArgs"}}]},{"name":"klendSupplyStrategyWithdraw","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":true,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveDestinationDepositCollateral","isMut":true,"isSigner":false},{"name":"vaultCollateralAta","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false},{"name":"obligation","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"instructionsSysvar","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"assetTokenProgram","isMut":false,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"kfarmsProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"KlendSupplyStrategyWithdrawArgs"}}]},{"name":"klendSupplyStrategyClaimEmissions","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false},{"name":"userState","isMut":true,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false},{"name":"kfarmsGlobalConfig","isMut":false,"isSigner":false},{"name":"kfarmsVaultsAuthority","isMut":false,"isSigner":false},{"name":"kfarmsRewardsVault","isMut":true,"isSigner":false},{"name":"kfarmsRewardsTreasuryVault","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"kfarmsProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"solendSupplyStrategyInit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"assetPythOracle","isMut":false,"isSigner":false},{"name":"assetSwitchboardOracle","isMut":false,"isSigner":false},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":false,"isSigner":false},{"name":"reserveCollateralMint","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":false,"isSigner":false},{"name":"reserveDepositCollateralAta","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"solendProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"SolendSupplyStrategyInitArgs"}}]},{"name":"solendSupplyStrategyDeposit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"assetPythOracle","isMut":false,"isSigner":false},{"name":"assetSwitchboardOracle","isMut":false,"isSigner":false},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":true,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"vaultCollateralAta","isMut":true,"isSigner":false},{"name":"depositCollateralAta","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"solendProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"SolendSupplyStrategyDepositArgs"}}]},{"name":"solendSupplyStrategyWithdraw","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"assetPythOracle","isMut":false,"isSigner":false},{"name":"assetSwitchboardOracle","isMut":false,"isSigner":false},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":true,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"vaultCollateralAta","isMut":true,"isSigner":false},{"name":"withdrawCollateralAta","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"solendProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"SolendSupplyStrategyWithdrawArgs"}}]},{"name":"mangoSupplyStrategyInit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"mangoGroup","isMut":false,"isSigner":false},{"name":"mangoAccount","isMut":true,"isSigner":false},{"name":"mangoBank","isMut":false,"isSigner":false},{"name":"mangoVault","isMut":false,"isSigner":false},{"name":"pythOracle","isMut":false,"isSigner":false},{"name":"switchboardOracle","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"mangoProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MangoSupplyStrategyInitArgs"}}]},{"name":"mangoSupplyStrategyDeposit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"mangoGroup","isMut":false,"isSigner":false},{"name":"mangoAccount","isMut":true,"isSigner":false},{"name":"mangoBank","isMut":true,"isSigner":false},{"name":"mangoVault","isMut":true,"isSigner":false},{"name":"pythOracle","isMut":false,"isSigner":false},{"name":"switchboardOracle","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"mangoProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MangoSupplyStrategyDepositArgs"}}]},{"name":"mangoSupplyStrategyWithdraw","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"mangoGroup","isMut":false,"isSigner":false},{"name":"mangoAccount","isMut":true,"isSigner":false},{"name":"mangoBank","isMut":true,"isSigner":false},{"name":"mangoVault","isMut":true,"isSigner":false},{"name":"pythOracle","isMut":false,"isSigner":false},{"name":"switchboardOracle","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"mangoProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MangoSupplyStrategyWithdrawArgs"}}]},{"name":"driftSupplyStrategyInit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"driftUser","isMut":true,"isSigner":false},{"name":"driftUserStats","isMut":true,"isSigner":false},{"name":"driftState","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"driftSpotMarket","isMut":false,"isSigner":false},{"name":"driftPerpMarket","isMut":false,"isSigner":false},{"name":"driftSpotMarketVault","isMut":false,"isSigner":false},{"name":"spotPythOracle","isMut":false,"isSigner":false},{"name":"perpPythOracle","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"driftProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"DriftSupplyStrategyInitArgs"}}]},{"name":"driftSupplyStrategyDeposit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"driftUser","isMut":true,"isSigner":false},{"name":"driftUserStats","isMut":true,"isSigner":false},{"name":"driftState","isMut":true,"isSigner":false},{"name":"driftSpotMarket","isMut":true,"isSigner":false},{"name":"driftSpotMarketVault","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"driftProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"DriftSupplyStrategyDepositArgs"}}]},{"name":"driftSupplyStrategyWithdraw","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"driftUser","isMut":true,"isSigner":false},{"name":"driftUserStats","isMut":true,"isSigner":false},{"name":"driftState","isMut":true,"isSigner":false},{"name":"driftSpotMarketVault","isMut":true,"isSigner":false},{"name":"driftSpotMarket","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"driftProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"DriftSupplyStrategyWithdrawArgs"}}]},{"name":"driftInsuranceFundStrategyInit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"driftUserStats","isMut":true,"isSigner":false},{"name":"driftSpotMarket","isMut":true,"isSigner":false},{"name":"driftSpotMarketVault","isMut":false,"isSigner":false},{"name":"driftState","isMut":true,"isSigner":false},{"name":"driftInsuranceFundStake","isMut":true,"isSigner":false},{"name":"driftInsuranceFundVault","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"DriftInsuranceFundStrategyInitArgs"}}]},{"name":"driftInsuranceFundStrategyStake","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"driftUserStats","isMut":true,"isSigner":false},{"name":"driftSpotMarket","isMut":true,"isSigner":false},{"name":"driftSpotMarketVault","isMut":true,"isSigner":false},{"name":"driftState","isMut":false,"isSigner":false},{"name":"driftInsuranceFundStake","isMut":true,"isSigner":false},{"name":"driftInsuranceFundVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"DriftInsuranceFundStrategyStakeArgs"}}]},{"name":"driftInsuranceFundStrategyUnstake","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"driftUserStats","isMut":true,"isSigner":false},{"name":"driftSpotMarket","isMut":true,"isSigner":false},{"name":"driftInsuranceFundStake","isMut":true,"isSigner":false},{"name":"driftInsuranceFundVault","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"driftProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"DriftInsuranceFundStrategyUnstakeArgs"}}]},{"name":"driftInsuranceFundStrategyWithdraw","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"driftUserStats","isMut":true,"isSigner":false},{"name":"driftSpotMarket","isMut":true,"isSigner":false},{"name":"driftState","isMut":false,"isSigner":false},{"name":"driftInsuranceFundStake","isMut":true,"isSigner":false},{"name":"driftInsuranceFundVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"clendSupplyStrategyInit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"clendGroup","isMut":false,"isSigner":false},{"name":"clendAccount","isMut":true,"isSigner":true},{"name":"clendBank","isMut":false,"isSigner":false},{"name":"clendBankLiquidityVault","isMut":false,"isSigner":false},{"name":"clendBankLiquidityVaultAuthority","isMut":false,"isSigner":false},{"name":"clendOracle","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"clendProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ClendSupplyStrategyInitArgs"}}]},{"name":"clendSupplyStrategyDeposit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"clendGroup","isMut":false,"isSigner":false},{"name":"clendAccount","isMut":true,"isSigner":false},{"name":"clendBank","isMut":true,"isSigner":false},{"name":"clendBankLiquidityVault","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"clendProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ClendSupplyStrategyDepositArgs"}}]},{"name":"clendSupplyStrategyWithdraw","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"clendGroup","isMut":false,"isSigner":false},{"name":"clendAccount","isMut":true,"isSigner":false},{"name":"clendBank","isMut":true,"isSigner":false},{"name":"clendBankLiquidityVault","isMut":true,"isSigner":false},{"name":"clendBankLiquidityVaultAuthority","isMut":true,"isSigner":false},{"name":"clendOracle","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"clendProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ClendSupplyStrategyWithdrawArgs"}}]},{"name":"chestStrategyInit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"assetTokenAccount","isMut":false,"isSigner":false},{"name":"chest","isMut":false,"isSigner":false},{"name":"coinTokenAccount","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ChestStrategyInitArgs"}}]},{"name":"chestStrategyDeposit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"chest","isMut":true,"isSigner":false},{"name":"coin","isMut":true,"isSigner":false},{"name":"chestAssetReserve","isMut":true,"isSigner":false},{"name":"driftVault","isMut":true,"isSigner":false},{"name":"driftVaultDepositor","isMut":true,"isSigner":false},{"name":"driftVaultTokenAccount","isMut":true,"isSigner":false},{"name":"driftUser","isMut":true,"isSigner":false},{"name":"driftUserStats","isMut":true,"isSigner":false},{"name":"driftSpotMarket","isMut":true,"isSigner":false},{"name":"driftSpotMarketVault","isMut":true,"isSigner":false},{"name":"driftState","isMut":true,"isSigner":false},{"name":"vaultAssetTokenAccount","isMut":true,"isSigner":false},{"name":"vaultCoinTokenAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"chestProgram","isMut":false,"isSigner":false},{"name":"driftProgram","isMut":false,"isSigner":false},{"name":"driftVaultsProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ChestStrategyDepositArgs"}}]},{"name":"chestStrategyRequestWithdraw","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"chest","isMut":true,"isSigner":false},{"name":"coin","isMut":true,"isSigner":false},{"name":"chestCoinReserve","isMut":true,"isSigner":false},{"name":"redemptionRequest","isMut":true,"isSigner":false},{"name":"driftVault","isMut":true,"isSigner":false},{"name":"driftVaultDepositor","isMut":true,"isSigner":false},{"name":"driftUser","isMut":true,"isSigner":false},{"name":"assetDestination","isMut":false,"isSigner":false},{"name":"vaultCoinTokenAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"chestProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ChestStrategyRequestWithdrawArgs"}}]},{"name":"chestStrategyWithdraw","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategyAssetTokenAccount","isMut":true,"isSigner":false},{"name":"chest","isMut":true,"isSigner":false},{"name":"chestRedemptionRequest","isMut":false,"isSigner":false},{"name":"coin","isMut":true,"isSigner":false},{"name":"vaultCoinTokenAccount","isMut":true,"isSigner":false},{"name":"driftVault","isMut":true,"isSigner":false},{"name":"driftVaultDepositor","isMut":true,"isSigner":false},{"name":"driftUser","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"chestProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ChestStrategyWithdrawArgs"}}]},{"name":"updateSwitchboardOraclePrice","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"shares","isMut":false,"isSigner":false},{"name":"feed","isMut":true,"isSigner":false},{"name":"queue","isMut":false,"isSigner":false},{"name":"programState","isMut":false,"isSigner":false},{"name":"recentSlothashes","isMut":false,"isSigner":false},{"name":"rewardVault","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"switchboardOnDemandProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"Vault","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"shares","type":"publicKey"},{"name":"fee","type":{"defined":"Fee"}},{"name":"paused","type":"bool"},{"name":"assetIndex","type":"u16"},{"name":"strategyIndex","type":"u16"},{"name":"assets","type":{"vec":{"defined":"Asset"}}},{"name":"strategies","type":{"vec":{"defined":"StrategyRecord"}}}]}},{"name":"Strategy","type":{"kind":"struct","fields":[{"name":"metadata","type":{"defined":"StrategyMetadata"}},{"name":"strategyType","type":{"defined":"StrategyType"}}]}}],"types":[{"name":"ChestStrategyDepositArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"failOnLowBalance","type":"bool"},{"name":"preserveRecordedBalance","type":"bool"}]}},{"name":"ChestStrategyInitArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"}]}},{"name":"ChestStrategyRequestWithdrawArgs","type":{"kind":"struct","fields":[{"name":"assetAmount","type":"u64"}]}},{"name":"ChestStrategyWithdrawArgs","type":{"kind":"struct","fields":[{"name":"failOnLowBalance","type":"bool"},{"name":"preserveRecordedBalance","type":"bool"}]}},{"name":"ClendSupplyStrategyDepositArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"ClendSupplyStrategyInitArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"}]}},{"name":"ClendSupplyStrategyWithdrawArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"DriftInsuranceFundStrategyInitArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"driftMarketIndex","type":"u16"}]}},{"name":"DriftInsuranceFundStrategyStakeArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"DriftInsuranceFundStrategyUnstakeArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"DriftSupplyStrategyDepositArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"DriftSupplyStrategyInitArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"driftSubAccountId","type":"u16"},{"name":"driftMarketIndex","type":"u16"}]}},{"name":"DriftSupplyStrategyWithdrawArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"InitVaultArgs","type":{"kind":"struct","fields":[{"name":"redemptionFeeBps","type":"u16"},{"name":"managementFeeBps","type":"u16"},{"name":"performanceFeeBps","type":"u16"}]}},{"name":"IssueArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"KlendSupplyStrategyDepositArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"KlendSupplyStrategyInitArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"}]}},{"name":"KlendSupplyStrategyWithdrawArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"MangoSupplyStrategyDepositArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"MangoSupplyStrategyInitArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"}]}},{"name":"MangoSupplyStrategyWithdrawArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"MarginfiSupplyStrategyDepositArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"MarginfiSupplyStrategyInitArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"}]}},{"name":"MarginfiSupplyStrategyWithdrawArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"PauseVaultArgs","type":{"kind":"struct","fields":[{"name":"paused","type":"bool"}]}},{"name":"RedeemArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"SolendSupplyStrategyDepositArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"SolendSupplyStrategyInitArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"}]}},{"name":"SolendSupplyStrategyWithdrawArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"UpdateAssetArgs","type":{"kind":"struct","fields":[{"name":"assetId","type":"u16"},{"name":"assetNewOracle","type":"publicKey"}]}},{"name":"UpdateFeeArgs","type":{"kind":"struct","fields":[{"name":"newRedemptionFeeBps","type":{"option":"u16"}},{"name":"newManagementFeeBps","type":{"option":"u16"}},{"name":"newPerformanceFeeBps","type":{"option":"u16"}}]}},{"name":"UpdateStrategyArgs","type":{"kind":"struct","fields":[{"name":"newStrategyType","type":{"defined":"StrategyType"}}]}},{"name":"Asset","type":{"kind":"struct","fields":[{"name":"assetId","type":"u16"},{"name":"mint","type":"publicKey"},{"name":"decimals","type":"u8"},{"name":"ata","type":"publicKey"},{"name":"oracle","type":"publicKey"}]}},{"name":"StrategyRecord","type":{"kind":"struct","fields":[{"name":"strategyId","type":"u16"},{"name":"assetId","type":"u16"},{"name":"balance","type":"u64"},{"name":"netEarnings","type":"i64"}]}},{"name":"StrategyMetadata","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"strategyId","type":"u16"},{"name":"assetMint","type":"publicKey"},{"name":"vault","type":"publicKey"}]}},{"name":"Fee","type":{"kind":"struct","fields":[{"name":"redemptionFeeBps","type":"u16"},{"name":"redemptionFeeAccumulated","type":"u64"},{"name":"managementFeeBps","type":"u16"},{"name":"managementFeeLastUpdate","type":"i64"},{"name":"managementFeeAccumulated","type":"u64"},{"name":"performanceFeeBps","type":"u16"}]}},{"name":"IssueEvent","type":{"kind":"struct","fields":[{"name":"depositor","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"amount","type":"u64"},{"name":"sharesMinted","type":"u64"},{"name":"managementFee","type":"u64"},{"name":"tvl","type":"u128"}]}},{"name":"RedeemEvent","type":{"kind":"struct","fields":[{"name":"withdrawer","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"amount","type":"u64"},{"name":"sharesBurned","type":"u64"},{"name":"redemptionFee","type":"u64"},{"name":"managementFee","type":"u64"},{"name":"tvl","type":"u128"}]}},{"name":"StrategyDepositEvent","type":{"kind":"struct","fields":[{"name":"strategy","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"depositAmount","type":"u64"},{"name":"netEarnings","type":"i64"},{"name":"strategyBalance","type":"u64"}]}},{"name":"StrategyWithdrawEvent","type":{"kind":"struct","fields":[{"name":"strategy","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"withdrawAmount","type":"u64"},{"name":"netEarnings","type":"i64"},{"name":"strategyBalance","type":"u64"}]}},{"name":"StrategyClaimEmissionsEvent","type":{"kind":"struct","fields":[{"name":"strategy","type":"publicKey"},{"name":"mint","type":"publicKey"}]}},{"name":"DistributeVaultFeesEvent","type":{"kind":"struct","fields":[{"name":"managementFeeAmountDistributed","type":"u64"},{"name":"performanceFeeAmountDistributed","type":"u64"},{"name":"redemptionFeeAmountDistributed","type":"u64"}]}},{"name":"UpdateSwitchboardOraclePriceEvent","type":{"kind":"struct","fields":[{"name":"vaultNav","type":"i128"},{"name":"vaultTvl","type":"u128"},{"name":"sharesSupply","type":"u64"}]}},{"name":"StrategyType","type":{"kind":"enum","variants":[{"name":"MarginfiSupply","fields":[{"name":"account","type":"publicKey"},{"name":"group","type":"publicKey"},{"name":"bank","type":"publicKey"},{"name":"bankLiquidityVault","type":"publicKey"},{"name":"bankLiquidityVaultAuthority","type":"publicKey"},{"name":"oracle","type":"publicKey"}]},{"name":"KlendSupply","fields":[{"name":"reserve","type":"publicKey"},{"name":"reserveCollateralMint","type":"publicKey"},{"name":"reserveLiquiditySupply","type":"publicKey"},{"name":"reserveDestinationDepositCollateral","type":"publicKey"},{"name":"reserveFarmState","type":"publicKey"},{"name":"lendingMarket","type":"publicKey"},{"name":"oracle","type":"publicKey"},{"name":"scopePrices","type":"publicKey"}]},{"name":"SolendSupply","fields":[{"name":"reserve","type":"publicKey"},{"name":"reserveCollateralMint","type":"publicKey"},{"name":"reserveLiquiditySupply","type":"publicKey"},{"name":"depositCollateralAta","type":"publicKey"},{"name":"lendingMarket","type":"publicKey"},{"name":"lendingMarketAuthority","type":"publicKey"},{"name":"pythOracle","type":"publicKey"},{"name":"switchboardOracle","type":"publicKey"}]},{"name":"MangoSupply","fields":[{"name":"group","type":"publicKey"},{"name":"account","type":"publicKey"},{"name":"bank","type":"publicKey"},{"name":"vault","type":"publicKey"},{"name":"pythOracle","type":"publicKey"},{"name":"switchboardOracle","type":"publicKey"}]},{"name":"DriftSupply","fields":[{"name":"state","type":"publicKey"},{"name":"signer","type":"publicKey"},{"name":"spotMarket","type":"publicKey"},{"name":"spotMarketVault","type":"publicKey"},{"name":"perpMarket","type":"publicKey"},{"name":"spotPythOracle","type":"publicKey"},{"name":"perpPythOracle","type":"publicKey"},{"name":"subAccountId","type":"u16"},{"name":"marketIndex","type":"u16"}]},{"name":"DriftInsuranceFund","fields":[{"name":"state","type":"publicKey"},{"name":"spotMarket","type":"publicKey"},{"name":"spotMarketVault","type":"publicKey"},{"name":"marketIndex","type":"u16"}]},{"name":"Chest","fields":[{"name":"chest","type":"publicKey"},{"name":"coin","type":"publicKey"},{"name":"driftVault","type":"publicKey"},{"name":"coinTokenAccount","type":"publicKey"},{"name":"assetDestination","type":"publicKey"},{"name":"redemptionRequest","type":{"option":"publicKey"}},{"name":"spotMarketIndex","type":"u16"}]},{"name":"ClendSupply","fields":[{"name":"account","type":"publicKey"},{"name":"group","type":"publicKey"},{"name":"bank","type":"publicKey"},{"name":"bankLiquidityVault","type":"publicKey"},{"name":"bankLiquidityVaultAuthority","type":"publicKey"},{"name":"oracle","type":"publicKey"}]}]}},{"name":"StrategyTypeSelection","type":{"kind":"enum","variants":[{"name":"MarginfiSupply"},{"name":"KlendSupply"},{"name":"SolendSupply"},{"name":"MangoSupply"},{"name":"DriftSupply"},{"name":"DriftInsuranceFund"},{"name":"Chest"},{"name":"ClendSupply"}]}},{"name":"CarrotEvent","type":{"kind":"enum","variants":[{"name":"Issue","fields":[{"defined":"IssueEvent"}]},{"name":"Redeem","fields":[{"defined":"RedeemEvent"}]},{"name":"StrategyDeposit","fields":[{"defined":"StrategyDepositEvent"}]},{"name":"StrategyWithdraw","fields":[{"defined":"StrategyWithdrawEvent"}]},{"name":"StrategyClaimEmissions","fields":[{"defined":"StrategyClaimEmissionsEvent"}]},{"name":"DistributeVaultFees","fields":[{"defined":"DistributeVaultFeesEvent"}]},{"name":"UpdateSwitchboardOraclePrice","fields":[{"defined":"UpdateSwitchboardOraclePriceEvent"}]}]}},{"name":"RoundingMode","type":{"kind":"enum","variants":[{"name":"RoundUp"},{"name":"RoundDown"},{"name":"Avg"}]}}],"errors":[{"code":6000,"name":"AssetAlreadyExists","msg":"Asset Already Exists"},{"code":6001,"name":"AssetNotFound","msg":"Asset Not Found"},{"code":6002,"name":"OracleNotFound","msg":"Oracle Not Found"},{"code":6003,"name":"OracleStalePrice","msg":"Oracle Returned Stale Price"},{"code":6004,"name":"StrategyNotFound","msg":"Strategy Not Found"},{"code":6005,"name":"AtaNotFound","msg":"Ata Not Found"},{"code":6006,"name":"StrategyAlreadyExists","msg":"Strategy Already Exists"},{"code":6007,"name":"InvalidStrategyType","msg":"Invalid Strategy Type"},{"code":6008,"name":"StrategyBalanceCalculationError","msg":"Strategy Balance Calculation Error"},{"code":6009,"name":"VaultIsPaused","msg":"Vault is Paused"},{"code":6010,"name":"InvalidVaultAuthority","msg":"Invalid Vault Authority"},{"code":6011,"name":"MigrationError","msg":"Account Migration Error"},{"code":6012,"name":"InvalidPriceConf","msg":"Invalid Price Confidence Interval"},{"code":6013,"name":"InvalidStrategyAccounts","msg":"Invalid Strategy Accounts"},{"code":6014,"name":"StrategyNotEmpty","msg":"Strategy Not Empty"},{"code":6015,"name":"AssetNotEmpty","msg":"Asset Not Empty"},{"code":6016,"name":"InvalidFeeBps","msg":"Invalid Fee Bps"},{"code":6017,"name":"InvalidReturnData","msg":"Invalid Return Data"},{"code":6018,"name":"InvalidCoinSupply","msg":"Invalid Coin Supply"},{"code":6019,"name":"InvalidNav","msg":"Invalid NAV"},{"code":6020,"name":"MathError","msg":"Math Error"},{"code":6021,"name":"ChestLowBalance","msg":"Chest Low Balance"}]} \ No newline at end of file diff --git a/idls/cctp_v1.CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3.json b/idls/cctp_v1.CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3.json new file mode 100644 index 0000000..1c7342c --- /dev/null +++ b/idls/cctp_v1.CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"token_messenger_minter","instructions":[{"name":"initialize","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"upgradeAuthority","isMut":false,"isSigner":true},{"name":"authorityPda","isMut":false,"isSigner":false},{"name":"tokenMessenger","isMut":true,"isSigner":false},{"name":"tokenMinter","isMut":true,"isSigner":false},{"name":"tokenMessengerMinterProgramData","isMut":false,"isSigner":false},{"name":"tokenMessengerMinterProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitializeParams"}}]},{"name":"transferOwnership","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"tokenMessenger","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"TransferOwnershipParams"}}]},{"name":"acceptOwnership","accounts":[{"name":"pendingOwner","isMut":false,"isSigner":true},{"name":"tokenMessenger","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"AcceptOwnershipParams"}}]},{"name":"addRemoteTokenMessenger","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"owner","isMut":false,"isSigner":true},{"name":"tokenMessenger","isMut":false,"isSigner":false},{"name":"remoteTokenMessenger","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"AddRemoteTokenMessengerParams"}}]},{"name":"removeRemoteTokenMessenger","accounts":[{"name":"payee","isMut":true,"isSigner":true},{"name":"owner","isMut":false,"isSigner":true},{"name":"tokenMessenger","isMut":false,"isSigner":false},{"name":"remoteTokenMessenger","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"RemoveRemoteTokenMessengerParams"}}]},{"name":"depositForBurn","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"eventRentPayer","isMut":true,"isSigner":true},{"name":"senderAuthorityPda","isMut":false,"isSigner":false},{"name":"burnTokenAccount","isMut":true,"isSigner":false},{"name":"messageTransmitter","isMut":true,"isSigner":false},{"name":"tokenMessenger","isMut":false,"isSigner":false},{"name":"remoteTokenMessenger","isMut":false,"isSigner":false},{"name":"tokenMinter","isMut":false,"isSigner":false},{"name":"localToken","isMut":true,"isSigner":false},{"name":"burnTokenMint","isMut":true,"isSigner":false},{"name":"messageSentEventData","isMut":true,"isSigner":true},{"name":"messageTransmitterProgram","isMut":false,"isSigner":false},{"name":"tokenMessengerMinterProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"DepositForBurnParams"}}],"returns":"u64"},{"name":"depositForBurnWithCaller","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"eventRentPayer","isMut":true,"isSigner":true},{"name":"senderAuthorityPda","isMut":false,"isSigner":false},{"name":"burnTokenAccount","isMut":true,"isSigner":false},{"name":"messageTransmitter","isMut":true,"isSigner":false},{"name":"tokenMessenger","isMut":false,"isSigner":false},{"name":"remoteTokenMessenger","isMut":false,"isSigner":false},{"name":"tokenMinter","isMut":false,"isSigner":false},{"name":"localToken","isMut":true,"isSigner":false},{"name":"burnTokenMint","isMut":true,"isSigner":false},{"name":"messageSentEventData","isMut":true,"isSigner":true},{"name":"messageTransmitterProgram","isMut":false,"isSigner":false},{"name":"tokenMessengerMinterProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"DepositForBurnWithCallerParams"}}],"returns":"u64"},{"name":"replaceDepositForBurn","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"eventRentPayer","isMut":true,"isSigner":true},{"name":"senderAuthorityPda","isMut":false,"isSigner":false},{"name":"messageTransmitter","isMut":true,"isSigner":false},{"name":"tokenMessenger","isMut":false,"isSigner":false},{"name":"messageSentEventData","isMut":true,"isSigner":true},{"name":"messageTransmitterProgram","isMut":false,"isSigner":false},{"name":"tokenMessengerMinterProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"ReplaceDepositForBurnParams"}}],"returns":"u64"},{"name":"handleReceiveMessage","accounts":[{"name":"authorityPda","isMut":false,"isSigner":true},{"name":"tokenMessenger","isMut":false,"isSigner":false},{"name":"remoteTokenMessenger","isMut":false,"isSigner":false},{"name":"tokenMinter","isMut":false,"isSigner":false},{"name":"localToken","isMut":true,"isSigner":false},{"name":"tokenPair","isMut":false,"isSigner":false},{"name":"recipientTokenAccount","isMut":true,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"HandleReceiveMessageParams"}}]},{"name":"setTokenController","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"tokenMessenger","isMut":false,"isSigner":false},{"name":"tokenMinter","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetTokenControllerParams"}}]},{"name":"pause","accounts":[{"name":"pauser","isMut":false,"isSigner":true},{"name":"tokenMinter","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"PauseParams"}}]},{"name":"unpause","accounts":[{"name":"pauser","isMut":false,"isSigner":true},{"name":"tokenMinter","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"UnpauseParams"}}]},{"name":"updatePauser","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"tokenMessenger","isMut":false,"isSigner":false},{"name":"tokenMinter","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"UpdatePauserParams"}}]},{"name":"setMaxBurnAmountPerMessage","accounts":[{"name":"tokenController","isMut":false,"isSigner":true},{"name":"tokenMinter","isMut":false,"isSigner":false},{"name":"localToken","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetMaxBurnAmountPerMessageParams"}}]},{"name":"addLocalToken","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"tokenController","isMut":false,"isSigner":true},{"name":"tokenMinter","isMut":false,"isSigner":false},{"name":"localToken","isMut":true,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"localTokenMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"AddLocalTokenParams"}}]},{"name":"removeLocalToken","accounts":[{"name":"payee","isMut":true,"isSigner":true},{"name":"tokenController","isMut":false,"isSigner":true},{"name":"tokenMinter","isMut":false,"isSigner":false},{"name":"localToken","isMut":true,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"RemoveLocalTokenParams"}}]},{"name":"linkTokenPair","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"tokenController","isMut":false,"isSigner":true},{"name":"tokenMinter","isMut":false,"isSigner":false},{"name":"tokenPair","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"LinkTokenPairParams"}}]},{"name":"unlinkTokenPair","accounts":[{"name":"payee","isMut":true,"isSigner":true},{"name":"tokenController","isMut":false,"isSigner":true},{"name":"tokenMinter","isMut":false,"isSigner":false},{"name":"tokenPair","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"UninkTokenPairParams"}}]},{"name":"burnTokenCustody","accounts":[{"name":"payee","isMut":true,"isSigner":true},{"name":"tokenController","isMut":false,"isSigner":true},{"name":"tokenMinter","isMut":false,"isSigner":false},{"name":"localToken","isMut":false,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"custodyTokenMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"BurnTokenCustodyParams"}}]}],"accounts":[{"name":"TokenMessenger","type":{"kind":"struct","fields":[{"name":"owner","type":"publicKey"},{"name":"pendingOwner","type":"publicKey"},{"name":"localMessageTransmitter","type":"publicKey"},{"name":"messageBodyVersion","type":"u32"},{"name":"authorityBump","type":"u8"}]}},{"name":"RemoteTokenMessenger","type":{"kind":"struct","fields":[{"name":"domain","type":"u32"},{"name":"tokenMessenger","type":"publicKey"}]}},{"name":"TokenMinter","type":{"kind":"struct","fields":[{"name":"tokenController","type":"publicKey"},{"name":"pauser","type":"publicKey"},{"name":"paused","type":"bool"},{"name":"bump","type":"u8"}]}},{"name":"TokenPair","type":{"kind":"struct","fields":[{"name":"remoteDomain","type":"u32"},{"name":"remoteToken","type":"publicKey"},{"name":"localToken","type":"publicKey"},{"name":"bump","type":"u8"}]}},{"name":"LocalToken","type":{"kind":"struct","fields":[{"name":"custody","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"burnLimitPerMessage","type":"u64"},{"name":"messagesSent","type":"u64"},{"name":"messagesReceived","type":"u64"},{"name":"amountSent","type":"u128"},{"name":"amountReceived","type":"u128"},{"name":"bump","type":"u8"},{"name":"custodyBump","type":"u8"}]}}],"types":[{"name":"AcceptOwnershipParams","type":{"kind":"struct","fields":[]}},{"name":"AddRemoteTokenMessengerParams","type":{"kind":"struct","fields":[{"name":"domain","type":"u32"},{"name":"tokenMessenger","type":"publicKey"}]}},{"name":"DepositForBurnWithCallerParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"destinationDomain","type":"u32"},{"name":"mintRecipient","type":"publicKey"},{"name":"destinationCaller","type":"publicKey"}]}},{"name":"DepositForBurnParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"destinationDomain","type":"u32"},{"name":"mintRecipient","type":"publicKey"}]}},{"name":"HandleReceiveMessageParams","type":{"kind":"struct","fields":[{"name":"remoteDomain","type":"u32"},{"name":"sender","type":"publicKey"},{"name":"messageBody","type":"bytes"},{"name":"authorityBump","type":"u8"}]}},{"name":"InitializeParams","type":{"kind":"struct","fields":[{"name":"tokenController","type":"publicKey"},{"name":"localMessageTransmitter","type":"publicKey"},{"name":"messageBodyVersion","type":"u32"}]}},{"name":"RemoveRemoteTokenMessengerParams","type":{"kind":"struct","fields":[]}},{"name":"ReplaceDepositForBurnParams","type":{"kind":"struct","fields":[{"name":"originalMessage","type":"bytes"},{"name":"originalAttestation","type":"bytes"},{"name":"newDestinationCaller","type":"publicKey"},{"name":"newMintRecipient","type":"publicKey"}]}},{"name":"TransferOwnershipParams","type":{"kind":"struct","fields":[{"name":"newOwner","type":"publicKey"}]}},{"name":"AddLocalTokenParams","type":{"kind":"struct","fields":[]}},{"name":"BurnTokenCustodyParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"LinkTokenPairParams","type":{"kind":"struct","fields":[{"name":"localToken","type":"publicKey"},{"name":"remoteDomain","type":"u32"},{"name":"remoteToken","type":"publicKey"}]}},{"name":"PauseParams","type":{"kind":"struct","fields":[]}},{"name":"RemoveLocalTokenParams","type":{"kind":"struct","fields":[]}},{"name":"SetMaxBurnAmountPerMessageParams","type":{"kind":"struct","fields":[{"name":"burnLimitPerMessage","type":"u64"}]}},{"name":"SetTokenControllerParams","type":{"kind":"struct","fields":[{"name":"tokenController","type":"publicKey"}]}},{"name":"UninkTokenPairParams","type":{"kind":"struct","fields":[]}},{"name":"UnpauseParams","type":{"kind":"struct","fields":[]}},{"name":"UpdatePauserParams","type":{"kind":"struct","fields":[{"name":"newPauser","type":"publicKey"}]}},{"name":"TokenMinterError","type":{"kind":"enum","variants":[{"name":"InvalidAuthority"},{"name":"InvalidTokenMinterState"},{"name":"ProgramPaused"},{"name":"InvalidTokenPairState"},{"name":"InvalidLocalTokenState"},{"name":"InvalidPauser"},{"name":"InvalidTokenController"},{"name":"BurnAmountExceeded"},{"name":"InvalidAmount"}]}}],"events":[{"name":"OwnershipTransferStarted","fields":[{"name":"previousOwner","type":"publicKey","index":false},{"name":"newOwner","type":"publicKey","index":false}]},{"name":"OwnershipTransferred","fields":[{"name":"previousOwner","type":"publicKey","index":false},{"name":"newOwner","type":"publicKey","index":false}]},{"name":"DepositForBurn","fields":[{"name":"nonce","type":"u64","index":false},{"name":"burnToken","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"depositor","type":"publicKey","index":false},{"name":"mintRecipient","type":"publicKey","index":false},{"name":"destinationDomain","type":"u32","index":false},{"name":"destinationTokenMessenger","type":"publicKey","index":false},{"name":"destinationCaller","type":"publicKey","index":false}]},{"name":"MintAndWithdraw","fields":[{"name":"mintRecipient","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"mintToken","type":"publicKey","index":false}]},{"name":"RemoteTokenMessengerAdded","fields":[{"name":"domain","type":"u32","index":false},{"name":"tokenMessenger","type":"publicKey","index":false}]},{"name":"RemoteTokenMessengerRemoved","fields":[{"name":"domain","type":"u32","index":false},{"name":"tokenMessenger","type":"publicKey","index":false}]},{"name":"SetTokenController","fields":[{"name":"tokenController","type":"publicKey","index":false}]},{"name":"PauserChanged","fields":[{"name":"newAddress","type":"publicKey","index":false}]},{"name":"SetBurnLimitPerMessage","fields":[{"name":"token","type":"publicKey","index":false},{"name":"burnLimitPerMessage","type":"u64","index":false}]},{"name":"LocalTokenAdded","fields":[{"name":"custody","type":"publicKey","index":false},{"name":"mint","type":"publicKey","index":false}]},{"name":"LocalTokenRemoved","fields":[{"name":"custody","type":"publicKey","index":false},{"name":"mint","type":"publicKey","index":false}]},{"name":"TokenPairLinked","fields":[{"name":"localToken","type":"publicKey","index":false},{"name":"remoteDomain","type":"u32","index":false},{"name":"remoteToken","type":"publicKey","index":false}]},{"name":"TokenPairUnlinked","fields":[{"name":"localToken","type":"publicKey","index":false},{"name":"remoteDomain","type":"u32","index":false},{"name":"remoteToken","type":"publicKey","index":false}]},{"name":"Pause","fields":[]},{"name":"Unpause","fields":[]},{"name":"TokenCustodyBurned","fields":[{"name":"custodyTokenAccount","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"InvalidAuthority","msg":"Invalid authority"},{"code":6001,"name":"InvalidTokenMessengerState","msg":"Invalid token messenger state"},{"code":6002,"name":"InvalidTokenMessenger","msg":"Invalid token messenger"},{"code":6003,"name":"InvalidOwner","msg":"Invalid owner"},{"code":6004,"name":"MalformedMessage","msg":"Malformed message"},{"code":6005,"name":"InvalidMessageBodyVersion","msg":"Invalid message body version"},{"code":6006,"name":"InvalidAmount","msg":"Invalid amount"},{"code":6007,"name":"InvalidDestinationDomain","msg":"Invalid destination domain"},{"code":6008,"name":"InvalidDestinationCaller","msg":"Invalid destination caller"},{"code":6009,"name":"InvalidMintRecipient","msg":"Invalid mint recipient"},{"code":6010,"name":"InvalidSender","msg":"Invalid sender"},{"code":6011,"name":"InvalidTokenPair","msg":"Invalid token pair"},{"code":6012,"name":"InvalidTokenMint","msg":"Invalid token mint"}]} \ No newline at end of file diff --git a/idls/cctp_v2.CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe.json b/idls/cctp_v2.CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe.json new file mode 100644 index 0000000..168bde5 --- /dev/null +++ b/idls/cctp_v2.CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe.json @@ -0,0 +1 @@ +{"address":"CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe","metadata":{"name":"token_messenger_minter_v2","version":"0.2.0","spec":"0.1.0","description":"Token Messenger and Minter for Cross-Chain Transfer Protocol V2","repository":"https://github.com/circlefin/solana-cctp-contracts"},"instructions":[{"name":"accept_ownership","discriminator":[172,23,43,13,238,213,85,150],"accounts":[{"name":"pending_owner","signer":true,"relations":["token_messenger"]},{"name":"token_messenger","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"AcceptOwnershipParams"}}}]},{"name":"add_local_token","discriminator":[213,199,205,18,98,124,73,198],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"token_controller","signer":true,"relations":["token_minter"]},{"name":"token_minter"},{"name":"local_token","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,111,99,97,108,95,116,111,107,101,110]},{"kind":"account","path":"local_token_mint"}]}},{"name":"custody_token_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,117,115,116,111,100,121]},{"kind":"account","path":"local_token_mint"}]}},{"name":"local_token_mint"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"AddLocalTokenParams"}}}]},{"name":"add_remote_token_messenger","discriminator":[12,149,172,165,111,202,24,33],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"owner","signer":true,"relations":["token_messenger"]},{"name":"token_messenger"},{"name":"remote_token_messenger","writable":true,"pda":{"seeds":[{"kind":"const","value":[114,101,109,111,116,101,95,116,111,107,101,110,95,109,101,115,115,101,110,103,101,114]},{"kind":"arg","path":"params.domain"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"AddRemoteTokenMessengerParams"}}}]},{"name":"burn_token_custody","discriminator":[233,136,180,175,112,41,62,71],"accounts":[{"name":"payee","writable":true,"signer":true},{"name":"token_controller","signer":true,"relations":["token_minter"]},{"name":"token_minter"},{"name":"local_token","pda":{"seeds":[{"kind":"const","value":[108,111,99,97,108,95,116,111,107,101,110]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}]}},{"name":"custody_token_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,117,115,116,111,100,121]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}]}},{"name":"custody_token_mint","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"BurnTokenCustodyParams"}}}]},{"name":"denylist_account","discriminator":[101,116,197,112,81,249,75,194],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"denylister","signer":true,"relations":["token_messenger"]},{"name":"token_messenger"},{"name":"denylist_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,110,121,108,105,115,116,95,97,99,99,111,117,110,116]},{"kind":"arg","path":"params.account"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"DenylistParams"}}}]},{"name":"deposit_for_burn","discriminator":[215,60,61,46,114,55,128,176],"accounts":[{"name":"owner","signer":true,"relations":["burn_token_account"]},{"name":"event_rent_payer","writable":true,"signer":true},{"name":"sender_authority_pda","pda":{"seeds":[{"kind":"const","value":[115,101,110,100,101,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"burn_token_account","writable":true},{"name":"denylist_account","docs":["Account is denylisted if the account exists at the expected PDA."],"pda":{"seeds":[{"kind":"const","value":[100,101,110,121,108,105,115,116,95,97,99,99,111,117,110,116]},{"kind":"account","path":"owner"}]}},{"name":"message_transmitter","writable":true},{"name":"token_messenger"},{"name":"remote_token_messenger"},{"name":"token_minter"},{"name":"local_token","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,111,99,97,108,95,116,111,107,101,110]},{"kind":"account","path":"burn_token_mint"}]}},{"name":"burn_token_mint","writable":true},{"name":"message_sent_event_data","writable":true,"signer":true},{"name":"message_transmitter_program","address":"CCTPV2Sm4AdWt5296sk4P66VBZ7bEhcARwFaaS9YPbeC"},{"name":"token_messenger_minter_program","address":"CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"DepositForBurnParams"}}}]},{"name":"deposit_for_burn_with_hook","discriminator":[111,245,62,131,204,108,223,155],"accounts":[{"name":"owner","signer":true,"relations":["burn_token_account"]},{"name":"event_rent_payer","writable":true,"signer":true},{"name":"sender_authority_pda","pda":{"seeds":[{"kind":"const","value":[115,101,110,100,101,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"burn_token_account","writable":true},{"name":"denylist_account","docs":["Account is denylisted if the account exists at the expected PDA."],"pda":{"seeds":[{"kind":"const","value":[100,101,110,121,108,105,115,116,95,97,99,99,111,117,110,116]},{"kind":"account","path":"owner"}]}},{"name":"message_transmitter","writable":true},{"name":"token_messenger"},{"name":"remote_token_messenger"},{"name":"token_minter"},{"name":"local_token","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,111,99,97,108,95,116,111,107,101,110]},{"kind":"account","path":"burn_token_mint"}]}},{"name":"burn_token_mint","writable":true},{"name":"message_sent_event_data","writable":true,"signer":true},{"name":"message_transmitter_program","address":"CCTPV2Sm4AdWt5296sk4P66VBZ7bEhcARwFaaS9YPbeC"},{"name":"token_messenger_minter_program","address":"CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"DepositForBurnWithHookParams"}}}]},{"name":"handle_receive_finalized_message","discriminator":[186,252,239,70,86,180,110,95],"accounts":[{"name":"authority_pda","signer":true,"pda":{"seeds":[{"kind":"const","value":[109,101,115,115,97,103,101,95,116,114,97,110,115,109,105,116,116,101,114,95,97,117,116,104,111,114,105,116,121]},{"kind":"const","value":[166,95,200,29,15,239,168,134,12,179,184,63,8,155,2,36,190,138,102,135,183,174,73,245,148,192,185,180,215,233,56,147]}],"program":{"kind":"const","value":[166,95,200,28,225,158,220,210,210,195,64,176,47,166,27,225,213,186,221,225,89,40,51,221,249,32,9,216,207,104,84,85]}}},{"name":"token_messenger"},{"name":"remote_token_messenger"},{"name":"token_minter"},{"name":"local_token","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,111,99,97,108,95,116,111,107,101,110]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}]}},{"name":"token_pair"},{"name":"fee_recipient_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"token_messenger.fee_recipient","account":"TokenMessenger"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"recipient_token_account","writable":true},{"name":"custody_token_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,117,115,116,111,100,121]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}]}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"HandleReceiveMessageParams"}}}]},{"name":"handle_receive_unfinalized_message","discriminator":[200,169,175,20,200,58,182,61],"accounts":[{"name":"authority_pda","signer":true,"pda":{"seeds":[{"kind":"const","value":[109,101,115,115,97,103,101,95,116,114,97,110,115,109,105,116,116,101,114,95,97,117,116,104,111,114,105,116,121]},{"kind":"const","value":[166,95,200,29,15,239,168,134,12,179,184,63,8,155,2,36,190,138,102,135,183,174,73,245,148,192,185,180,215,233,56,147]}],"program":{"kind":"const","value":[166,95,200,28,225,158,220,210,210,195,64,176,47,166,27,225,213,186,221,225,89,40,51,221,249,32,9,216,207,104,84,85]}}},{"name":"token_messenger"},{"name":"remote_token_messenger"},{"name":"token_minter"},{"name":"local_token","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,111,99,97,108,95,116,111,107,101,110]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}]}},{"name":"token_pair"},{"name":"fee_recipient_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"token_messenger.fee_recipient","account":"TokenMessenger"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"recipient_token_account","writable":true},{"name":"custody_token_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,117,115,116,111,100,121]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}]}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"HandleReceiveMessageParams"}}}]},{"name":"initialize","discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"upgrade_authority","signer":true},{"name":"authority_pda","pda":{"seeds":[{"kind":"const","value":[115,101,110,100,101,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"token_messenger","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,109,101,115,115,101,110,103,101,114]}]}},{"name":"token_minter","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,109,105,110,116,101,114]}]}},{"name":"token_messenger_minter_program_data"},{"name":"token_messenger_minter_program","address":"CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"InitializeParams"}}}]},{"name":"link_token_pair","discriminator":[68,162,24,104,125,46,130,12],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"token_controller","signer":true,"relations":["token_minter"]},{"name":"token_minter"},{"name":"token_pair","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,112,97,105,114]},{"kind":"arg","path":"params.remote_domain"},{"kind":"arg","path":"params.remote_token"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"LinkTokenPairParams"}}}]},{"name":"pause","discriminator":[211,22,221,251,74,121,193,47],"accounts":[{"name":"pauser","signer":true,"relations":["token_minter"]},{"name":"token_minter","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"PauseParams"}}}]},{"name":"remove_local_token","discriminator":[27,43,66,170,188,44,109,97],"accounts":[{"name":"payee","writable":true,"signer":true},{"name":"token_controller","signer":true,"relations":["token_minter"]},{"name":"token_minter"},{"name":"local_token","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,111,99,97,108,95,116,111,107,101,110]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}]}},{"name":"custody_token_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,117,115,116,111,100,121]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}]}},{"name":"custody_token_mint","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"RemoveLocalTokenParams"}}}]},{"name":"remove_remote_token_messenger","discriminator":[65,114,66,85,169,98,177,146],"accounts":[{"name":"payee","writable":true,"signer":true},{"name":"owner","signer":true,"relations":["token_messenger"]},{"name":"token_messenger"},{"name":"remote_token_messenger","writable":true,"pda":{"seeds":[{"kind":"const","value":[114,101,109,111,116,101,95,116,111,107,101,110,95,109,101,115,115,101,110,103,101,114]},{"kind":"account","path":"remote_token_messenger.domain","account":"RemoteTokenMessenger"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"RemoveRemoteTokenMessengerParams"}}}]},{"name":"set_fee_recipient","discriminator":[227,18,215,42,237,246,151,66],"accounts":[{"name":"owner","signer":true,"relations":["token_messenger"]},{"name":"token_messenger","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SetFeeRecipientParams"}}}]},{"name":"set_max_burn_amount_per_message","discriminator":[30,128,145,240,70,237,109,207],"accounts":[{"name":"token_controller","signer":true,"relations":["token_minter"]},{"name":"token_minter"},{"name":"local_token","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,111,99,97,108,95,116,111,107,101,110]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SetMaxBurnAmountPerMessageParams"}}}]},{"name":"set_min_fee","discriminator":[114,198,35,3,41,196,194,246],"accounts":[{"name":"min_fee_controller","signer":true,"relations":["token_messenger"]},{"name":"token_messenger","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SetMinFeeParams"}}}]},{"name":"set_min_fee_controller","discriminator":[195,142,74,84,234,94,180,113],"accounts":[{"name":"owner","signer":true,"relations":["token_messenger"]},{"name":"token_messenger","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SetMinFeeControllerParams"}}}]},{"name":"set_token_controller","discriminator":[88,6,98,10,79,59,15,24],"accounts":[{"name":"owner","signer":true,"relations":["token_messenger"]},{"name":"token_messenger"},{"name":"token_minter","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SetTokenControllerParams"}}}]},{"name":"transfer_ownership","discriminator":[65,177,215,73,53,45,99,47],"accounts":[{"name":"owner","signer":true,"relations":["token_messenger"]},{"name":"token_messenger","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"TransferOwnershipParams"}}}]},{"name":"undenylist_account","discriminator":[57,36,43,168,62,172,33,39],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"denylister","signer":true,"relations":["token_messenger"]},{"name":"token_messenger"},{"name":"denylist_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,110,121,108,105,115,116,95,97,99,99,111,117,110,116]},{"kind":"arg","path":"params.account"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"UndenylistParams"}}}]},{"name":"unlink_token_pair","discriminator":[52,198,100,114,104,174,85,58],"accounts":[{"name":"payee","writable":true,"signer":true},{"name":"token_controller","signer":true,"relations":["token_minter"]},{"name":"token_minter"},{"name":"token_pair","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,112,97,105,114]},{"kind":"account","path":"token_pair.remote_domain","account":"TokenPair"},{"kind":"account","path":"token_pair.remote_token","account":"TokenPair"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"UninkTokenPairParams"}}}]},{"name":"unpause","discriminator":[169,144,4,38,10,141,188,255],"accounts":[{"name":"pauser","signer":true,"relations":["token_minter"]},{"name":"token_minter","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"UnpauseParams"}}}]},{"name":"update_denylister","discriminator":[193,66,198,201,84,57,14,222],"accounts":[{"name":"owner","signer":true,"relations":["token_messenger"]},{"name":"token_messenger","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"UpdateDenylisterParams"}}}]},{"name":"update_pauser","discriminator":[140,171,211,132,57,201,16,254],"accounts":[{"name":"owner","signer":true,"relations":["token_messenger"]},{"name":"token_messenger"},{"name":"token_minter","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"UpdatePauserParams"}}}]}],"accounts":[{"name":"DenylistedAccount","discriminator":[186,58,212,239,102,131,157,146]},{"name":"LocalToken","discriminator":[159,131,58,170,193,84,128,182]},{"name":"MessageTransmitter","discriminator":[71,40,180,142,19,203,35,252]},{"name":"RemoteTokenMessenger","discriminator":[105,115,174,34,95,233,138,252]},{"name":"TokenMessenger","discriminator":[162,4,242,52,147,243,221,96]},{"name":"TokenMinter","discriminator":[122,133,84,63,57,159,171,206]},{"name":"TokenPair","discriminator":[17,214,45,176,229,149,197,71]}],"events":[{"name":"Denylisted","discriminator":[20,145,173,200,182,17,234,154]},{"name":"DenylisterChanged","discriminator":[249,170,81,180,185,175,138,72]},{"name":"DepositForBurn","discriminator":[144,252,145,146,6,74,167,235]},{"name":"FeeRecipientSet","discriminator":[99,140,80,35,245,176,179,110]},{"name":"LocalTokenAdded","discriminator":[146,8,224,150,122,173,23,39]},{"name":"LocalTokenRemoved","discriminator":[181,204,1,95,2,50,66,210]},{"name":"MinFeeControllerSet","discriminator":[239,12,122,105,231,114,13,196]},{"name":"MinFeeSet","discriminator":[60,127,101,230,216,129,188,98]},{"name":"MintAndWithdraw","discriminator":[75,67,229,70,162,126,0,71]},{"name":"OwnershipTransferStarted","discriminator":[183,253,239,246,140,179,133,105]},{"name":"OwnershipTransferred","discriminator":[172,61,205,183,250,50,38,98]},{"name":"Pause","discriminator":[194,251,232,196,118,95,111,219]},{"name":"PauserChanged","discriminator":[142,157,158,87,127,8,119,55]},{"name":"RemoteTokenMessengerAdded","discriminator":[251,29,63,244,48,114,210,175]},{"name":"RemoteTokenMessengerRemoved","discriminator":[255,121,137,39,230,125,11,30]},{"name":"SetBurnLimitPerMessage","discriminator":[98,152,88,191,245,30,27,209]},{"name":"SetTokenController","discriminator":[193,44,243,83,230,72,120,216]},{"name":"TokenCustodyBurned","discriminator":[219,143,107,226,67,75,178,46]},{"name":"TokenPairLinked","discriminator":[2,14,177,64,155,93,196,141]},{"name":"TokenPairUnlinked","discriminator":[78,232,230,208,180,212,246,72]},{"name":"UnDenylisted","discriminator":[150,39,227,20,162,180,5,242]},{"name":"Unpause","discriminator":[241,149,104,90,199,136,219,146]}],"errors":[{"code":6000,"name":"InvalidAuthority","msg":"Invalid authority"},{"code":6001,"name":"InvalidTokenMinterState","msg":"Invalid token minter state"},{"code":6002,"name":"ProgramPaused","msg":"Instruction is not allowed at this time"},{"code":6003,"name":"InvalidTokenPairState","msg":"Invalid token pair state"},{"code":6004,"name":"InvalidLocalTokenState","msg":"Invalid local token state"},{"code":6005,"name":"InvalidPauser","msg":"Invalid pauser"},{"code":6006,"name":"InvalidTokenController","msg":"Invalid token controller"},{"code":6007,"name":"BurnAmountExceeded","msg":"Burn amount exceeded"},{"code":6008,"name":"InvalidAmount","msg":"Invalid amount"}],"types":[{"name":"AcceptOwnershipParams","type":{"kind":"struct","fields":[]}},{"name":"AddLocalTokenParams","type":{"kind":"struct","fields":[]}},{"name":"AddRemoteTokenMessengerParams","type":{"kind":"struct","fields":[{"name":"domain","type":"u32"},{"name":"token_messenger","type":"pubkey"}]}},{"name":"BurnTokenCustodyParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"DenylistParams","type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"}]}},{"name":"Denylisted","type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"}]}},{"name":"DenylistedAccount","type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"}]}},{"name":"DenylisterChanged","type":{"kind":"struct","fields":[{"name":"old_denylister","type":"pubkey"},{"name":"new_denylister","type":"pubkey"}]}},{"name":"DepositForBurn","type":{"kind":"struct","fields":[{"name":"burn_token","type":"pubkey"},{"name":"amount","type":"u64"},{"name":"depositor","type":"pubkey"},{"name":"mint_recipient","type":"pubkey"},{"name":"destination_domain","type":"u32"},{"name":"destination_token_messenger","type":"pubkey"},{"name":"destination_caller","type":"pubkey"},{"name":"max_fee","type":"u64"},{"name":"min_finality_threshold","type":"u32"},{"name":"hook_data","type":"bytes"}]}},{"name":"DepositForBurnParams","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"destination_domain","type":"u32"},{"name":"mint_recipient","type":"pubkey"},{"name":"destination_caller","type":"pubkey"},{"name":"max_fee","type":"u64"},{"name":"min_finality_threshold","type":"u32"}]}},{"name":"DepositForBurnWithHookParams","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"destination_domain","type":"u32"},{"name":"mint_recipient","type":"pubkey"},{"name":"destination_caller","type":"pubkey"},{"name":"max_fee","type":"u64"},{"name":"min_finality_threshold","type":"u32"},{"name":"hook_data","type":"bytes"}]}},{"name":"FeeRecipientSet","type":{"kind":"struct","fields":[{"name":"new_fee_recipient","type":"pubkey"}]}},{"name":"HandleReceiveMessageParams","type":{"kind":"struct","fields":[{"name":"remote_domain","type":"u32"},{"name":"sender","type":"pubkey"},{"name":"finality_threshold_executed","type":"u32"},{"name":"message_body","type":"bytes"},{"name":"authority_bump","type":"u8"}]}},{"name":"InitializeParams","type":{"kind":"struct","fields":[{"name":"token_controller","type":"pubkey"},{"name":"denylister","type":"pubkey"},{"name":"fee_recipient","type":"pubkey"},{"name":"min_fee_controller","type":"pubkey"},{"name":"min_fee","type":"u32"},{"name":"message_body_version","type":"u32"}]}},{"name":"LinkTokenPairParams","type":{"kind":"struct","fields":[{"name":"local_token","type":"pubkey"},{"name":"remote_domain","type":"u32"},{"name":"remote_token","type":"pubkey"}]}},{"name":"LocalToken","type":{"kind":"struct","fields":[{"name":"custody","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"burn_limit_per_message","type":"u64"},{"name":"messages_sent","type":"u64"},{"name":"messages_received","type":"u64"},{"name":"amount_sent","type":"u128"},{"name":"amount_received","type":"u128"},{"name":"bump","type":"u8"},{"name":"custody_bump","type":"u8"}]}},{"name":"LocalTokenAdded","type":{"kind":"struct","fields":[{"name":"custody","type":"pubkey"},{"name":"mint","type":"pubkey"}]}},{"name":"LocalTokenRemoved","type":{"kind":"struct","fields":[{"name":"custody","type":"pubkey"},{"name":"mint","type":"pubkey"}]}},{"name":"MessageTransmitter","docs":["Main state of the MessageTransmitter program"],"type":{"kind":"struct","fields":[{"name":"owner","type":"pubkey"},{"name":"pending_owner","type":"pubkey"},{"name":"attester_manager","type":"pubkey"},{"name":"pauser","type":"pubkey"},{"name":"paused","type":"bool"},{"name":"local_domain","type":"u32"},{"name":"version","type":"u32"},{"name":"signature_threshold","type":"u32"},{"name":"enabled_attesters","type":{"vec":"pubkey"}},{"name":"max_message_body_size","type":"u64"}]}},{"name":"MinFeeControllerSet","type":{"kind":"struct","fields":[{"name":"new_min_fee_controller","type":"pubkey"}]}},{"name":"MinFeeSet","type":{"kind":"struct","fields":[{"name":"new_min_fee","type":"u32"}]}},{"name":"MintAndWithdraw","type":{"kind":"struct","fields":[{"name":"mint_recipient","type":"pubkey"},{"name":"amount","type":"u64"},{"name":"mint_token","type":"pubkey"},{"name":"fee_collected","type":"u64"}]}},{"name":"OwnershipTransferStarted","type":{"kind":"struct","fields":[{"name":"previous_owner","type":"pubkey"},{"name":"new_owner","type":"pubkey"}]}},{"name":"OwnershipTransferred","type":{"kind":"struct","fields":[{"name":"previous_owner","type":"pubkey"},{"name":"new_owner","type":"pubkey"}]}},{"name":"Pause","type":{"kind":"struct","fields":[]}},{"name":"PauseParams","type":{"kind":"struct","fields":[]}},{"name":"PauserChanged","type":{"kind":"struct","fields":[{"name":"new_address","type":"pubkey"}]}},{"name":"RemoteTokenMessenger","type":{"kind":"struct","fields":[{"name":"domain","type":"u32"},{"name":"token_messenger","type":"pubkey"}]}},{"name":"RemoteTokenMessengerAdded","type":{"kind":"struct","fields":[{"name":"domain","type":"u32"},{"name":"token_messenger","type":"pubkey"}]}},{"name":"RemoteTokenMessengerRemoved","type":{"kind":"struct","fields":[{"name":"domain","type":"u32"},{"name":"token_messenger","type":"pubkey"}]}},{"name":"RemoveLocalTokenParams","type":{"kind":"struct","fields":[]}},{"name":"RemoveRemoteTokenMessengerParams","type":{"kind":"struct","fields":[]}},{"name":"SetBurnLimitPerMessage","type":{"kind":"struct","fields":[{"name":"token","type":"pubkey"},{"name":"burn_limit_per_message","type":"u64"}]}},{"name":"SetFeeRecipientParams","type":{"kind":"struct","fields":[{"name":"new_fee_recipient","type":"pubkey"}]}},{"name":"SetMaxBurnAmountPerMessageParams","type":{"kind":"struct","fields":[{"name":"burn_limit_per_message","type":"u64"}]}},{"name":"SetMinFeeControllerParams","type":{"kind":"struct","fields":[{"name":"new_min_fee_controller","type":"pubkey"}]}},{"name":"SetMinFeeParams","type":{"kind":"struct","fields":[{"name":"new_min_fee","type":"u32"}]}},{"name":"SetTokenController","type":{"kind":"struct","fields":[{"name":"token_controller","type":"pubkey"}]}},{"name":"SetTokenControllerParams","type":{"kind":"struct","fields":[{"name":"token_controller","type":"pubkey"}]}},{"name":"TokenCustodyBurned","type":{"kind":"struct","fields":[{"name":"custody_token_account","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"TokenMessenger","type":{"kind":"struct","fields":[{"name":"denylister","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"pending_owner","type":"pubkey"},{"name":"message_body_version","type":"u32"},{"name":"authority_bump","type":"u8"},{"name":"fee_recipient","type":"pubkey"},{"name":"min_fee_controller","type":"pubkey"},{"name":"min_fee","type":"u32"}]}},{"name":"TokenMinter","type":{"kind":"struct","fields":[{"name":"token_controller","type":"pubkey"},{"name":"pauser","type":"pubkey"},{"name":"paused","type":"bool"},{"name":"bump","type":"u8"}]}},{"name":"TokenPair","type":{"kind":"struct","fields":[{"name":"remote_domain","type":"u32"},{"name":"remote_token","type":"pubkey"},{"name":"local_token","type":"pubkey"},{"name":"bump","type":"u8"}]}},{"name":"TokenPairLinked","type":{"kind":"struct","fields":[{"name":"local_token","type":"pubkey"},{"name":"remote_domain","type":"u32"},{"name":"remote_token","type":"pubkey"}]}},{"name":"TokenPairUnlinked","type":{"kind":"struct","fields":[{"name":"local_token","type":"pubkey"},{"name":"remote_domain","type":"u32"},{"name":"remote_token","type":"pubkey"}]}},{"name":"TransferOwnershipParams","type":{"kind":"struct","fields":[{"name":"new_owner","type":"pubkey"}]}},{"name":"UnDenylisted","type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"}]}},{"name":"UndenylistParams","type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"}]}},{"name":"UninkTokenPairParams","type":{"kind":"struct","fields":[]}},{"name":"Unpause","type":{"kind":"struct","fields":[]}},{"name":"UnpauseParams","type":{"kind":"struct","fields":[]}},{"name":"UpdateDenylisterParams","type":{"kind":"struct","fields":[{"name":"new_denylister","type":"pubkey"}]}},{"name":"UpdatePauserParams","type":{"kind":"struct","fields":[{"name":"new_pauser","type":"pubkey"}]}}]} \ No newline at end of file diff --git a/idls/clone.C1onEW2kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr.json b/idls/clone.C1onEW2kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr.json new file mode 100644 index 0000000..9f57bed --- /dev/null +++ b/idls/clone.C1onEW2kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"clone","instructions":[{"name":"initializeClone","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"clone","isMut":true,"isSigner":false},{"name":"collateralMint","isMut":false,"isSigner":false},{"name":"collateralVault","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"cometCollateralIldLiquidatorFeeBps","type":"u16"},{"name":"cometOnassetIldLiquidatorFeeBps","type":"u16"},{"name":"borrowLiquidatorFeeBps","type":"u16"},{"name":"treasuryAddress","type":"publicKey"},{"name":"collateralOracleIndex","type":"u8"},{"name":"collateralizationRatio","type":"u8"}]},{"name":"initializePools","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"clone","isMut":false,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeOracles","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"clone","isMut":false,"isSigner":false},{"name":"oracles","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateCloneParameters","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"clone","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"CloneParameters"}}]},{"name":"updatePoolParameters","accounts":[{"name":"auth","isMut":false,"isSigner":true},{"name":"clone","isMut":false,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false}],"args":[{"name":"index","type":"u8"},{"name":"params","type":{"defined":"PoolParameters"}}]},{"name":"updateOracles","accounts":[{"name":"auth","isMut":false,"isSigner":true},{"name":"clone","isMut":false,"isSigner":false},{"name":"oracles","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"UpdateOracleParameters"}}]},{"name":"initializeUser","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"authority","type":"publicKey"}]},{"name":"addPool","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"clone","isMut":false,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":false,"isSigner":false},{"name":"onassetTokenAccount","isMut":false,"isSigner":false},{"name":"underlyingAssetMint","isMut":false,"isSigner":false},{"name":"underlyingAssetTokenAccount","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"minOvercollateralRatio","type":"u16"},{"name":"maxLiquidationOvercollateralRatio","type":"u16"},{"name":"liquidityTradingFeeBps","type":"u16"},{"name":"treasuryTradingFeeBps","type":"u16"},{"name":"ilHealthScoreCoefficient","type":"u16"},{"name":"positionHealthScoreCoefficient","type":"u16"},{"name":"oracleInfoIndex","type":"u8"}]},{"name":"updatePrices","accounts":[{"name":"oracles","isMut":true,"isSigner":false}],"args":[{"name":"oracleIndices","type":"bytes"}]},{"name":"initializeBorrowPosition","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"oracles","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"userCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"userOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"poolIndex","type":"u8"},{"name":"onassetAmount","type":"u64"},{"name":"collateralAmount","type":"u64"}]},{"name":"addCollateralToBorrow","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":false,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"userCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"borrowIndex","type":"u8"},{"name":"amount","type":"u64"}]},{"name":"withdrawCollateralFromBorrow","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":false,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"oracles","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"userCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"borrowIndex","type":"u8"},{"name":"amount","type":"u64"}]},{"name":"payBorrowDebt","accounts":[{"name":"payer","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":false,"isSigner":false},{"name":"payerOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"user","type":"publicKey"},{"name":"borrowIndex","type":"u8"},{"name":"amount","type":"u64"}]},{"name":"borrowMore","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":false,"isSigner":false},{"name":"oracles","isMut":false,"isSigner":false},{"name":"userOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"borrowIndex","type":"u8"},{"name":"amount","type":"u64"}]},{"name":"addCollateralToComet","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"userCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"}]},{"name":"withdrawCollateralFromComet","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"oracles","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"userCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"}]},{"name":"addLiquidityToComet","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"oracles","isMut":true,"isSigner":false}],"args":[{"name":"poolIndex","type":"u8"},{"name":"collateralAmount","type":"u64"}]},{"name":"withdrawLiquidityFromComet","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"oracles","isMut":true,"isSigner":false}],"args":[{"name":"cometPositionIndex","type":"u8"},{"name":"amount","type":"u64"}]},{"name":"liquidateCometCollateralIld","accounts":[{"name":"liquidator","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":false,"isSigner":false},{"name":"oracles","isMut":false,"isSigner":false},{"name":"collateralMint","isMut":true,"isSigner":false},{"name":"liquidatorCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"user","type":"publicKey"},{"name":"cometPositionIndex","type":"u8"}]},{"name":"liquidateCometOnassetIld","accounts":[{"name":"liquidator","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":false,"isSigner":false},{"name":"oracles","isMut":false,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"liquidatorOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"liquidatorCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"user","type":"publicKey"},{"name":"cometPositionIndex","type":"u8"},{"name":"amount","type":"u64"}]},{"name":"liquidateBorrowPosition","accounts":[{"name":"liquidator","isMut":false,"isSigner":true},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"oracles","isMut":true,"isSigner":false},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"liquidatorCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"liquidatorOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"user","type":"publicKey"},{"name":"borrowIndex","type":"u8"},{"name":"amount","type":"u64"}]},{"name":"collectLpRewards","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":false,"isSigner":false},{"name":"collateralVault","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"userCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"userOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"cometPositionIndex","type":"u8"}]},{"name":"payImpermanentLossDebt","accounts":[{"name":"payer","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":false,"isSigner":false},{"name":"collateralMint","isMut":false,"isSigner":false},{"name":"collateralVault","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"payerCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"payerOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"user","type":"publicKey"},{"name":"cometPositionIndex","type":"u8"},{"name":"amount","type":"u64"},{"name":"paymentType","type":{"defined":"PaymentType"}}]},{"name":"closeUserAccount","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"destination","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"wrapAsset","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"clone","isMut":false,"isSigner":false},{"name":"pools","isMut":false,"isSigner":false},{"name":"underlyingAssetTokenAccount","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"userAssetTokenAccount","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"userOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"poolIndex","type":"u8"}]},{"name":"unwrapOnasset","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"clone","isMut":false,"isSigner":false},{"name":"pools","isMut":false,"isSigner":false},{"name":"underlyingAssetTokenAccount","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"userAssetTokenAccount","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"userOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"poolIndex","type":"u8"}]},{"name":"removeCometPosition","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false}],"args":[{"name":"cometPositionIndex","type":"u8"}]},{"name":"swap","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"oracles","isMut":true,"isSigner":false},{"name":"userCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"userOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"collateralMint","isMut":true,"isSigner":false},{"name":"collateralVault","isMut":true,"isSigner":false},{"name":"treasuryOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"treasuryCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"cloneStaking","isMut":false,"isSigner":false,"isOptional":true},{"name":"userStakingAccount","isMut":false,"isSigner":false,"isOptional":true},{"name":"cloneStakingProgram","isMut":false,"isSigner":false,"isOptional":true}],"args":[{"name":"poolIndex","type":"u8"},{"name":"quantity","type":"u64"},{"name":"quantityIsInput","type":"bool"},{"name":"quantityIsCollateral","type":"bool"},{"name":"resultThreshold","type":"u64"}]},{"name":"createTokenMetadata","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"clone","isMut":false,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"metaplexProgram","isMut":false,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"metadataArgs","type":{"defined":"MetadataArgs"}}]},{"name":"removePool","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"clone","isMut":false,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"underlyingAssetMint","isMut":false,"isSigner":false},{"name":"underlyingAssetTokenAccount","isMut":true,"isSigner":false},{"name":"treasuryAssetTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"poolIndex","type":"u8"}]}],"accounts":[{"name":"Clone","type":{"kind":"struct","fields":[{"name":"admin","type":"publicKey"},{"name":"auth","type":{"array":["publicKey",10]}},{"name":"bump","type":"u8"},{"name":"collateral","type":{"defined":"Collateral"}},{"name":"cometCollateralIldLiquidatorFeeBps","type":"u16"},{"name":"cometOnassetIldLiquidatorFeeBps","type":"u16"},{"name":"borrowLiquidatorFeeBps","type":"u16"},{"name":"treasuryAddress","type":"publicKey"},{"name":"eventCounter","type":"u64"},{"name":"nonAuthLiquidationsEnabled","type":"bool"}]}},{"name":"Pools","type":{"kind":"struct","fields":[{"name":"pools","type":{"vec":{"defined":"Pool"}}}]}},{"name":"Oracles","type":{"kind":"struct","fields":[{"name":"oracles","type":{"vec":{"defined":"OracleInfo"}}}]}},{"name":"User","type":{"kind":"struct","fields":[{"name":"borrows","type":{"vec":{"defined":"Borrow"}}},{"name":"comet","type":{"defined":"Comet"}}]}}],"types":[{"name":"MetadataArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]}},{"name":"AssetInfo","type":{"kind":"struct","fields":[{"name":"onassetMint","type":"publicKey"},{"name":"oracleInfoIndex","type":"u8"},{"name":"ilHealthScoreCoefficient","type":"u16"},{"name":"positionHealthScoreCoefficient","type":"u16"},{"name":"minOvercollateralRatio","type":"u16"},{"name":"maxLiquidationOvercollateralRatio","type":"u16"}]}},{"name":"OracleInfo","type":{"kind":"struct","fields":[{"name":"source","type":{"defined":"OracleSource"}},{"name":"address","type":"publicKey"},{"name":"price","type":"i64"},{"name":"expo","type":"u8"},{"name":"status","type":{"defined":"Status"}},{"name":"lastUpdateSlot","type":"u64"},{"name":"rescaleFactor","type":"u8"}]}},{"name":"Pool","type":{"kind":"struct","fields":[{"name":"underlyingAssetTokenAccount","type":"publicKey"},{"name":"committedCollateralLiquidity","type":"u64"},{"name":"collateralIld","type":"i64"},{"name":"onassetIld","type":"i64"},{"name":"treasuryTradingFeeBps","type":"u16"},{"name":"liquidityTradingFeeBps","type":"u16"},{"name":"assetInfo","type":{"defined":"AssetInfo"}},{"name":"status","type":{"defined":"Status"}}]}},{"name":"Collateral","type":{"kind":"struct","fields":[{"name":"oracleInfoIndex","type":"u8"},{"name":"mint","type":"publicKey"},{"name":"vault","type":"publicKey"},{"name":"collateralizationRatio","type":"u8"},{"name":"scale","type":"u8"}]}},{"name":"Comet","type":{"kind":"struct","fields":[{"name":"collateralAmount","type":"u64"},{"name":"positions","type":{"vec":{"defined":"LiquidityPosition"}}}]}},{"name":"LiquidityPosition","type":{"kind":"struct","fields":[{"name":"poolIndex","type":"u8"},{"name":"committedCollateralLiquidity","type":"u64"},{"name":"collateralIldRebate","type":"i64"},{"name":"onassetIldRebate","type":"i64"}]}},{"name":"Borrow","type":{"kind":"struct","fields":[{"name":"poolIndex","type":"u8"},{"name":"borrowedOnasset","type":"u64"},{"name":"collateralAmount","type":"u64"}]}},{"name":"PaymentType","type":{"kind":"enum","variants":[{"name":"Onasset"},{"name":"Collateral"},{"name":"CollateralFromWallet"}]}},{"name":"CloneParameters","type":{"kind":"enum","variants":[{"name":"AddAuth","fields":[{"name":"address","type":"publicKey"}]},{"name":"RemoveAuth","fields":[{"name":"address","type":"publicKey"}]},{"name":"CometCollateralLiquidationFee","fields":[{"name":"value","type":"u16"}]},{"name":"CometOnassetLiquidationFee","fields":[{"name":"value","type":"u16"}]},{"name":"BorrowLiquidationFee","fields":[{"name":"value","type":"u16"}]},{"name":"TreasuryAddress","fields":[{"name":"address","type":"publicKey"}]},{"name":"CollateralizationRatio","fields":[{"name":"value","type":"u8"}]},{"name":"NonAuthLiquidationsEnabled","fields":[{"name":"value","type":"bool"}]}]}},{"name":"UpdateOracleParameters","type":{"kind":"enum","variants":[{"name":"Add","fields":[{"name":"address","type":"publicKey"},{"name":"source","type":{"defined":"OracleSource"}},{"name":"rescale_factor","type":{"option":"u8"}}]},{"name":"Remove","fields":[{"name":"index","type":"u8"}]},{"name":"Modify","fields":[{"name":"index","type":"u8"},{"name":"address","type":{"option":"publicKey"}},{"name":"source","type":{"option":{"defined":"OracleSource"}}},{"name":"status","type":{"option":{"defined":"Status"}}}]}]}},{"name":"PoolParameters","type":{"kind":"enum","variants":[{"name":"Status","fields":[{"name":"value","type":{"defined":"Status"}}]},{"name":"TreasuryTradingFee","fields":[{"name":"value","type":"u16"}]},{"name":"LiquidityTradingFee","fields":[{"name":"value","type":"u16"}]},{"name":"OracleInfoIndex","fields":[{"name":"value","type":"u8"}]},{"name":"MinOvercollateralRatio","fields":[{"name":"value","type":"u16"}]},{"name":"MaxLiquidationOvercollateralRatio","fields":[{"name":"value","type":"u16"}]},{"name":"IlHealthScoreCoefficient","fields":[{"name":"value","type":"u16"}]},{"name":"PositionHealthScoreCoefficient","fields":[{"name":"value","type":"u16"}]}]}},{"name":"Status","type":{"kind":"enum","variants":[{"name":"Active"},{"name":"Frozen"},{"name":"Extraction"},{"name":"Liquidation"},{"name":"Deprecation"}]}},{"name":"OracleSource","type":{"kind":"enum","variants":[{"name":"PYTH"},{"name":"SWITCHBOARD"}]}}],"events":[{"name":"SwapEvent","fields":[{"name":"eventId","type":"u64","index":false},{"name":"userAddress","type":"publicKey","index":false},{"name":"poolIndex","type":"u8","index":false},{"name":"inputIsCollateral","type":"bool","index":false},{"name":"input","type":"u64","index":false},{"name":"output","type":"u64","index":false},{"name":"tradingFee","type":"u64","index":false},{"name":"treasuryFee","type":"u64","index":false}]},{"name":"LiquidityDelta","fields":[{"name":"eventId","type":"u64","index":false},{"name":"userAddress","type":"publicKey","index":false},{"name":"poolIndex","type":"u8","index":false},{"name":"committedCollateralDelta","type":"i64","index":false},{"name":"collateralIldDelta","type":"i64","index":false},{"name":"onassetIldDelta","type":"i64","index":false}]},{"name":"PoolState","fields":[{"name":"eventId","type":"u64","index":false},{"name":"poolIndex","type":"u8","index":false},{"name":"onassetIld","type":"i64","index":false},{"name":"collateralIld","type":"i64","index":false},{"name":"committedCollateralLiquidity","type":"u64","index":false},{"name":"poolPrice","type":"u64","index":false},{"name":"poolScale","type":"u32","index":false}]},{"name":"BorrowUpdate","fields":[{"name":"eventId","type":"u64","index":false},{"name":"poolIndex","type":"u8","index":false},{"name":"userAddress","type":"publicKey","index":false},{"name":"isLiquidation","type":"bool","index":false},{"name":"collateralSupplied","type":"u64","index":false},{"name":"collateralDelta","type":"i64","index":false},{"name":"borrowedAmount","type":"u64","index":false},{"name":"borrowedDelta","type":"i64","index":false}]},{"name":"CometCollateralUpdate","fields":[{"name":"eventId","type":"u64","index":false},{"name":"userAddress","type":"publicKey","index":false},{"name":"collateralSupplied","type":"u64","index":false},{"name":"collateralDelta","type":"i64","index":false}]}],"errors":[{"code":6000,"name":"Unauthorized","msg":"Unauthorized"},{"code":6001,"name":"InvalidMintCollateralRatio","msg":"Invalid Mint Collateral Ratio"},{"code":6002,"name":"IntTypeConversionError","msg":"Integer Type Conversion Error"},{"code":6003,"name":"PoolNotFound","msg":"Pool Not Found"},{"code":6004,"name":"BumpNotFound","msg":"Bump not found"},{"code":6005,"name":"InvalidTokenAmount","msg":"Invalid Token Amount"},{"code":6006,"name":"ExpectedAccountNotFound","msg":"Expected Account Not Found"},{"code":6007,"name":"OutdatedOracle","msg":"Outdated Oracle"},{"code":6008,"name":"CheckedMathError","msg":"Checked Math Error"},{"code":6009,"name":"BorrowPositionUnableToLiquidate","msg":"Mint Position Unable to Liquidate"},{"code":6010,"name":"HealthScoreTooLow","msg":"Health Score Too Low"},{"code":6011,"name":"InvalidInputCollateralAccount","msg":"Invalid input collateral account"},{"code":6012,"name":"InvalidAccountLoaderOwner","msg":"Invalid Account loader owner"},{"code":6013,"name":"InvalidInputPositionIndex","msg":"Invalid input position index"},{"code":6014,"name":"InvalidTokenAccountBalance","msg":"Invalid token account balance"},{"code":6015,"name":"InequalityComparisonViolated","msg":"Inequality comparison violated"},{"code":6016,"name":"CometNotEmpty","msg":"Comet Not Empty"},{"code":6017,"name":"NotSubjectToLiquidation","msg":"Not Subject to Liquidation"},{"code":6018,"name":"LiquidationAmountTooLarge","msg":"Liquidation amount too large"},{"code":6019,"name":"NoRemainingAccountsSupplied","msg":"No remaining accounts supplied"},{"code":6020,"name":"InvalidOvercollateralizationRatios","msg":"Invalid over-collateralization ratios"},{"code":6021,"name":"IncorrectOracleAddress","msg":"Incorrect oracle address provided"},{"code":6022,"name":"InvalidValueRange","msg":"Value is in an incorrect range"},{"code":6023,"name":"InvalidAssetStability","msg":"Asset stable requirement violated"},{"code":6024,"name":"SlippageToleranceExceeded","msg":"Slippage tolerance exceeded"},{"code":6025,"name":"RequireOnlyonUSDCollateral","msg":"Collateral must be all in onUSD"},{"code":6026,"name":"RequireAllPositionsClosed","msg":"Positions must be all closed"},{"code":6027,"name":"FailedToLoadPyth","msg":"Failed to Load Pyth Price Feed"},{"code":6028,"name":"StatusPreventsAction","msg":"Status Prevents Action"},{"code":6029,"name":"PoolEmpty","msg":"Pool is empty"},{"code":6030,"name":"NoLiquidityToWithdraw","msg":"No liquidity to withdraw"},{"code":6031,"name":"InvalidStatus","msg":"Invalid Status"},{"code":6032,"name":"AuthArrayFull","msg":"Auth Array Full"},{"code":6033,"name":"AuthNotFound","msg":"Auth Not Found"},{"code":6034,"name":"InvalidOracleIndex","msg":"Invalid oracle index"},{"code":6035,"name":"InvalidPaymentType","msg":"Invalid Payment Type"},{"code":6036,"name":"InvalidConversion","msg":"Invalid Conversion"},{"code":6037,"name":"AuthAlreadyExists","msg":"Auth Already Exists"}]} \ No newline at end of file diff --git a/idls/dflow_v4.DF1ow4tspfHX9JwWJsAb9epbkA8hmpSEAtxXy1V27QBH.json b/idls/dflow_v4.DF1ow4tspfHX9JwWJsAb9epbkA8hmpSEAtxXy1V27QBH.json new file mode 100644 index 0000000..d0a0765 --- /dev/null +++ b/idls/dflow_v4.DF1ow4tspfHX9JwWJsAb9epbkA8hmpSEAtxXy1V27QBH.json @@ -0,0 +1 @@ +{"address":"DF1ow4tspfHX9JwWJsAb9epbkA8hmpSEAtxXy1V27QBH","metadata":{"name":"swap_orchestrator","version":"0.1.0","spec":"0.1.0","description":"DFlow Aggregator program"},"instructions":[{"name":"close_empty_token_account","docs":["Closes a token account if it has a zero balance"],"discriminator":[232,75,140,136,250,78,224,188],"accounts":[{"name":"token_account","docs":["The token account to close (supports both spl_token and spl_token_2022)"],"writable":true},{"name":"authority","docs":["The authority of the token account. Must sign."],"signer":true},{"name":"destination","docs":["The account to receive the rent from closing the token account."],"writable":true},{"name":"token_program","docs":["The token program (either spl_token or spl_token_2022)"]}],"args":[]},{"name":"close_order","docs":["Closes an order, returning tokens from the order vault"],"discriminator":[90,103,209,28,7,63,168,4],"accounts":[{"name":"order","writable":true},{"name":"order_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,100,101,114,95,118,97,117,108,116]},{"kind":"account","path":"order"}]}},{"name":"return_input_token_account","docs":["Account to which leftover tokens in the order vault should be returned."],"writable":true,"relations":["order"]},{"name":"return_rent_to","docs":["Account to which rent for the order account and vault should be returned."],"writable":true,"relations":["order"]},{"name":"closer","docs":["Account that is authorized to close the order"],"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_referral_token_account_idempotent","docs":["Create a referral token account if it doesn't already exist"],"discriminator":[46,232,41,144,85,37,170,175],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"project"},{"name":"referral_account"},{"name":"referral_token_account","docs":["The referral token account to create. This is a PDA of the Referral program whose seeds are","[b\"referral_ata\", referral_account, mint]."],"writable":true},{"name":"mint"},{"name":"system_program"},{"name":"token_program"},{"name":"referral_program"}],"args":[]},{"name":"fill_order","docs":["Fills an order, closing the order after filling it"],"discriminator":[232,122,115,25,199,143,136,162],"accounts":[{"name":"order","writable":true},{"name":"order_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,100,101,114,95,118,97,117,108,116]},{"kind":"account","path":"order"}]}},{"name":"output_token_account","docs":["Account to which output from the fill should be sent."],"writable":true,"relations":["order"]},{"name":"return_input_token_account","docs":["Account to which leftover tokens in the order vault should be returned."],"writable":true,"relations":["order"]},{"name":"return_rent_to","docs":["Account to which rent for the order account and vault should be returned."],"writable":true,"relations":["order"]},{"name":"filler_input_token_account","writable":true},{"name":"input_mint","docs":["Input mint for the swap. Must be specified if the order inputs a mint for which the filler's","associated token account doesn't exist."],"optional":true},{"name":"filler_output_token_account","writable":true},{"name":"output_mint","docs":["Output mint for the swap. Must be specified if the order outputs native SOL or a mint for","which the filler's associated token account doesn't exist."],"optional":true},{"name":"filler","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"FillOrderParams"}}}]},{"name":"init_market_ledger_idempotent","docs":["Initialize a market ledger if it doesn't already exist"],"discriminator":[183,43,139,79,125,225,35,159],"accounts":[{"name":"predictions_program"},{"name":"market_ledger"},{"name":"market_ledger_redemption_vault"},{"name":"market_ledger_order_vault"},{"name":"market_ledger_reduce_yes_vault"},{"name":"market_ledger_reduce_no_vault"},{"name":"settlement_escrow"},{"name":"escrow_mint"},{"name":"market_yes_outcome_mint"},{"name":"market_no_outcome_mint"},{"name":"payer"},{"name":"outcome_token_program"},{"name":"escrow_token_program"},{"name":"system_program"}],"args":[{"name":"market_id","type":{"array":["u8",32]}}]},{"name":"open_order","docs":["Opens an order, escrowing the input tokens in the order vault"],"discriminator":[206,88,88,143,38,136,50,224],"accounts":[{"name":"order","docs":["Order account that the instruction will create to store the order details"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,100,101,114]},{"kind":"account","path":"return_input_token_account"},{"kind":"arg","path":"params.order_account_id"}]}},{"name":"order_vault","docs":["Token account that the instruction will create to escrow the input tokens for the order"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,100,101,114,95,118,97,117,108,116]},{"kind":"account","path":"order"}]}},{"name":"input_token_account","docs":["Token account from which input tokens should be withdrawn to fund the order"],"writable":true},{"name":"output_token_account","docs":["Account to which output from the fill should be sent. If the order specifies that it should","output native SOL, then this should be the user's wallet, although any account is allowed.","If the order doesn't specify that it should output native SOL, then this must be an SPL","token account, and the token account's mint determines the order's output mint."]},{"name":"return_input_token_account","docs":["Account to which leftover tokens in the order vault should be returned when the order is","filled or closed. If the input is native SOL, this should be the user's wallet, although any","account is allowed. If the input is wrapped SOL or an SPL token, this must be an SPL token","account for the input mint."]},{"name":"input_mint"},{"name":"user_token_authority","docs":["Authority of the input token account"],"signer":true},{"name":"fee_payer","docs":["Account that pays the fee budget for the order"],"writable":true,"signer":true},{"name":"fee_receiver","docs":["Account that receives the fee budget funds"],"writable":true},{"name":"rent_depositor","docs":["Depositor for the order account and vault account rent"],"writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"OpenOrderParams"}}}]},{"name":"swap","docs":["Executes a token swap"],"discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"user_token_authority","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SwapParams"}}}]},{"name":"swap2","docs":["Executes a token swap with a slippage fee"],"discriminator":[65,75,63,76,235,91,91,136],"accounts":[{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"user_token_authority","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"Swap2Params"}}}]},{"name":"swap2_with_destination","docs":["Executes a token swap with a slippage fee. Outputs to the specified destination account."],"discriminator":[95,123,213,246,122,1,86,231],"accounts":[{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"user_token_authority","signer":true},{"name":"destination_token_account","writable":true},{"name":"destination_token_authority"},{"name":"destination_mint"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"Swap2Params"}}}]},{"name":"swap2_with_destination_native","docs":["Executes a token swap with a slippage fee. Outputs native SOL to the specified destination","account by unwrapping WSOL."],"discriminator":[222,100,184,146,186,196,105,165],"accounts":[{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"user_token_authority","signer":true},{"name":"destination_account","docs":["The destination account that will receive the native SOL"],"writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"Swap2Params"}}}]},{"name":"swap_with_destination","docs":["Executes a token swap. Outputs to the specified destination account."],"discriminator":[168,172,24,77,197,156,135,101],"accounts":[{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"user_token_authority","signer":true},{"name":"destination_token_account","writable":true},{"name":"destination_token_authority"},{"name":"destination_mint"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SwapParams"}}}]},{"name":"swap_with_destination_native","docs":["Executes a token swap. Outputs native SOL to the specified destination account by unwrapping","WSOL."],"discriminator":[205,77,127,108,241,32,196,195],"accounts":[{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"user_token_authority","signer":true},{"name":"destination_account","docs":["The destination account that will receive the native SOL"],"writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SwapParams"}}}]},{"name":"transfer_fee","docs":["Transfer an SPL token fee if the receiving account can receive it"],"discriminator":[129,164,196,21,177,48,180,162],"accounts":[{"name":"from"},{"name":"to"},{"name":"authority"},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount","type":"u64"}]},{"name":"transfer_sol","docs":["Transfer native SOL from one account to another"],"discriminator":[78,10,236,247,109,117,21,76],"accounts":[{"name":"from"},{"name":"to"},{"name":"system_program"}],"args":[{"name":"lamports","type":"u64"}]},{"name":"transfer_to_sponsor","docs":["Transfer an SPL token to a sponsor's token account, creating the sponsor's token account if","necessary."],"discriminator":[155,179,130,151,196,139,253,163],"accounts":[{"name":"user_token_authority"},{"name":"user_token_account"},{"name":"sponsor"},{"name":"sponsor_token_account"},{"name":"mint"},{"name":"token_program"},{"name":"associated_token_program"},{"name":"system_program"}],"args":[{"name":"amount","type":"u64"}]},{"name":"unwrap_sol","docs":["Unwrap all SOL in the provided wrapped SOL token account"],"discriminator":[99,40,14,105,45,107,172,201],"accounts":[{"name":"owner"},{"name":"wrapped_sol_associated_token_account"},{"name":"token_program"}],"args":[]},{"name":"wrap_sol","docs":["Wrap SOL, creating a wrapped SOL associated token account if necessary"],"discriminator":[47,62,155,172,131,205,37,201],"accounts":[{"name":"from"},{"name":"wrapped_sol_associated_token_account"},{"name":"native_mint"},{"name":"token_program"},{"name":"associated_token_program"},{"name":"system_program"}],"args":[{"name":"lamports","type":"u64"}]}],"accounts":[{"name":"Order","discriminator":[134,173,223,185,77,86,28,51]}],"events":[{"name":"FeeEvent","discriminator":[73,79,78,127,184,213,13,220]},{"name":"SwapEvent","discriminator":[64,198,205,232,38,8,113,226]}],"errors":[{"code":15000,"name":"SwapCpiFailed","msg":"Swap CPI failed"},{"code":15001,"name":"SlippageLimitExceeded","msg":"Slippage limit exceeded"},{"code":15002,"name":"FeeAccountNotSpecified","msg":"Fee account must be specified when platform_fee_bps is nonzero"},{"code":15003,"name":"InvalidFeeAccountOwner","msg":"Invalid fee account owner"},{"code":15004,"name":"InputAmountIsZero","msg":"input_amount must be greater than 0"},{"code":15005,"name":"OutputTokenAccountMismatch","msg":"Swap output token account did not match the order's output_token_account"},{"code":15006,"name":"OrderNotFillableAfterLastFillableSlot","msg":"Order's last_fillable_slot has passed"},{"code":15007,"name":"FillOrderNotAuthorized","msg":"filler is not authorized to fill the order"},{"code":15008,"name":"MintAccountNotSpecified","msg":"Instruction must specify mint account"},{"code":15009,"name":"ReturnInputTokenAccountMismatch","msg":"return_input_token_account did not match the order's return_input_token_account"},{"code":15010,"name":"ReturnRentToMismatch","msg":"return_rent_to did not match the order's return_rent_to"},{"code":15011,"name":"CloseOrderNotAuthorized","msg":"closer is not authorized to close the order"},{"code":15012,"name":"InvalidReturnInputTokenAccount","msg":"Invalid return_input_token_account"},{"code":15013,"name":"InvalidOutputTokenAccount","msg":"Invalid output_token_account"},{"code":15014,"name":"CouldNotReturnInput","msg":"Couldn't return input from vault"},{"code":15015,"name":"LegInputOverconsumption","msg":"Leg consumed more than allowed input amount"},{"code":15016,"name":"RouteInputUnderconsumption","msg":"Route consumed less than minimum allowed amount of input"},{"code":15017,"name":"LegConsumedOutput","msg":"Leg consumed output"},{"code":15018,"name":"LegConsumedLamports","msg":"Leg consumed lamports"},{"code":15019,"name":"PumpFunAmmCreatedFeeAta","msg":"Pump.fun AMM leg created fee/buyback ATA"},{"code":15020,"name":"SkippedLeg","msg":"A leg in the route was skipped"},{"code":15021,"name":"DestinationTokenAccountDoesNotExist","msg":"Destination token account does not exist"},{"code":15022,"name":"Token2022ProgramNotProvided","msg":"Token 2022 Program account not provided"},{"code":15023,"name":"InvalidLegInputTokenAccount","msg":"Invalid leg input token account"},{"code":15024,"name":"RaydiumLaunchlabCreatedFeeAta","msg":"Raydium Launchlab leg created fee ATA"},{"code":15025,"name":"LegUnderproducedOutput","msg":"Leg produced less output than expected"},{"code":15026,"name":"NoSettlementTokensReceived","msg":"No settlement tokens were received during the swap"},{"code":15027,"name":"InsufficientAccountsForOpenPredictionsOrder","msg":"Insufficient accounts provided for OpenPredictionsOrder action"},{"code":15028,"name":"InvalidTokenAccount","msg":"Invalid token account. Must be either spl_token or spl_token_2022 account"},{"code":15029,"name":"InvalidCloseAuthorityForNativeMint","msg":"InitAtaIdempotent cannot set a close authority on a WSOL ATA"},{"code":15998,"name":"InvalidLegAction"},{"code":15999,"name":"InvalidInstructionData"}],"types":[{"name":"A","type":{"kind":"enum","variants":[{"name":"B","fields":[{"defined":{"name":"C"}}]},{"name":"J","fields":[{"defined":{"name":"J"}}]}]}},{"name":"Action","type":{"kind":"enum","variants":[{"name":"WhirlpoolsSwap","fields":[{"defined":{"name":"WhirlpoolsSwapOptions"}}]},{"name":"ClearpoolsSwap","fields":[{"defined":{"name":"ClearpoolsSwapOptions"}}]},{"name":"RaydiumAmmSwap","fields":[{"defined":{"name":"RaydiumAmmSwapOptions"}}]},{"name":"LifinityV2Swap","fields":[{"defined":{"name":"LifinityV2SwapOptions"}}]},{"name":"MeteoraDlmmSwap","fields":[{"defined":{"name":"MeteoraDlmmSwapOptions"}}]},{"name":"RaydiumClmmSwap","fields":[{"defined":{"name":"RaydiumClmmSwapOptions"}}]},{"name":"RaydiumClmmSwapV2","fields":[{"defined":{"name":"RaydiumClmmSwapV2Options"}}]},{"name":"PhoenixSwap","fields":[{"defined":{"name":"PhoenixSwapOptions"}}]},{"name":"PumpFunBuy","fields":[{"defined":{"name":"PumpFunBuyOptions"}}]},{"name":"PumpFunSell","fields":[{"defined":{"name":"PumpFunSellOptions"}}]},{"name":"GammaSwap","fields":[{"defined":{"name":"GammaSwapOptions"}}]},{"name":"ObricV2Swap","fields":[{"defined":{"name":"ObricV2SwapOptions"}}]},{"name":"PumpFunAmmBuy","fields":[{"defined":{"name":"PumpFunAmmBuyOptions"}}]},{"name":"PumpFunAmmSell","fields":[{"defined":{"name":"PumpFunAmmSellOptions"}}]},{"name":"SolFiSwap","fields":[{"defined":{"name":"SolFiSwapOptions"}}]},{"name":"RubiconSwap","fields":[{"defined":{"name":"RubiconSwapOptions"}}]},{"name":"MeteoraDammV1Swap","fields":[{"defined":{"name":"MeteoraDammV1SwapOptions"}}]},{"name":"RaydiumCpSwap","fields":[{"defined":{"name":"RaydiumCpSwapOptions"}}]},{"name":"StabbleStableSwap","fields":[{"defined":{"name":"StabbleStableSwapOptions"}}]},{"name":"TesseraVSwap","fields":[{"defined":{"name":"TesseraVSwapOptions"}}]},{"name":"MeteoraDammV2Swap","fields":[{"defined":{"name":"MeteoraDammV2SwapOptions"}}]},{"name":"RaydiumLaunchlabSwap","fields":[{"defined":{"name":"RaydiumLaunchlabSwapOptions"}}]},{"name":"MeteoraDbcSwap","fields":[{"defined":{"name":"MeteoraDbcSwapOptions"}}]},{"name":"HumidiFiSwap","fields":[{"defined":{"name":"HumidiFiSwapOptions"}}]},{"name":"WhirlpoolsSwapV2","fields":[{"defined":{"name":"WhirlpoolsSwapV2Options"}}]},{"name":"MeteoraDlmmSwapV2","fields":[{"defined":{"name":"MeteoraDlmmSwapV2Options"}}]},{"name":"ZeroFiSwap","fields":[{"defined":{"name":"ZeroFiSwapOptions"}}]},{"name":"AlphaQSwap","fields":[{"defined":{"name":"AlphaQSwapOptions"}}]},{"name":"TokenSwap","fields":[{"defined":{"name":"TokenSwapOptions"}}]},{"name":"SolFiV2Swap","fields":[{"defined":{"name":"SolFiV2SwapOptions"}}]},{"name":"MozartSwap","fields":[{"defined":{"name":"MozartSwapOptions"}}]},{"name":"DFlowDynamicRouteV1","fields":[{"defined":{"name":"DFlowDynamicRouteV1Options"}}]},{"name":"HeavenSwap","fields":[{"defined":{"name":"HeavenSwapOptions"}}]},{"name":"NexusSwap","fields":[{"defined":{"name":"NexusSwapOptions"}}]},{"name":"SarosDlmmSwap","fields":[{"defined":{"name":"SarosDlmmSwapOptions"}}]},{"name":"TransferFee","fields":[{"defined":{"name":"TransferFeeOptions"}}]},{"name":"TransferFeeWithMint","fields":[{"defined":{"name":"TransferFeeOptions"}}]},{"name":"RecordId","fields":[{"defined":{"name":"RecordIdOptions"}}]},{"name":"RecordId2","fields":[{"defined":{"name":"RecordId2Options"}}]},{"name":"ManifestSwap","fields":[{"defined":{"name":"ManifestSwapOptions"}}]},{"name":"BisonFiSwap","fields":[{"defined":{"name":"BisonFiSwapOptions"}}]},{"name":"SanctumInfinitySwap","fields":[{"defined":{"name":"SanctumInfinitySwapOptions"}}]},{"name":"SanctumInfinityLiquidity","fields":[{"defined":{"name":"SanctumInfinityLiquidityOptions"}}]},{"name":"OpenPredictionsOrder","fields":[{"defined":{"name":"OpenPredictionsOrderOptions"}}]},{"name":"ScorchSwap","fields":[{"defined":{"name":"ScorchSwapOptions"}}]},{"name":"IncludeAccount"},{"name":"StabbleWeightedSwap","fields":[{"defined":{"name":"StabbleWeightedSwapOptions"}}]},{"name":"VertigoSwap","fields":[{"defined":{"name":"VertigoSwapOptions"}}]},{"name":"SetMinimumLegOutputs","fields":[{"defined":{"name":"SetMinimumLegOutputsOptions"}}]},{"name":"SetMinimumLegPrices","fields":[{"defined":{"name":"SetMinimumLegPricesOptions"}}]},{"name":"SetSponsor"},{"name":"WrapSol","fields":[{"defined":{"name":"WrapSolOptions"}}]},{"name":"UnwrapSol"},{"name":"KDEXSwap","fields":[{"defined":{"name":"KDEXSwapOptions"}}]},{"name":"DeriverseSwap","fields":[{"defined":{"name":"DeriverseSwapOptions"}}]},{"name":"VaultSwap","fields":[{"defined":{"name":"VaultSwapOptions"}}]},{"name":"MetaDaoSwap","fields":[{"defined":{"name":"MetaDaoSwapOptions"}}]},{"name":"D","fields":[{"defined":{"name":"O"}}]},{"name":"XoCashExchangeSwap","fields":[{"defined":{"name":"XoCashExchangeSwapOptions"}}]},{"name":"LemmingsFiSwap","fields":[{"defined":{"name":"LemmingsFiSwapOptions"}}]},{"name":"InitAtaIdempotent"},{"name":"SetMaxUnderconsumptionBps","fields":[{"defined":{"name":"SetMaxUnderconsumptionBpsOptions"}}]},{"name":"GhostSwap","fields":[{"defined":{"name":"GhostSwapOptions"}}]},{"name":"TransferFeeV2","fields":[{"defined":{"name":"TransferFeeOptions"}}]},{"name":"TransferFeeWithMintV2","fields":[{"defined":{"name":"TransferFeeOptions"}}]},{"name":"PumpFunBuyV2","fields":[{"defined":{"name":"PumpFunBuyV2Options"}}]},{"name":"PumpFunSellV2","fields":[{"defined":{"name":"PumpFunSellV2Options"}}]}]}},{"name":"AlphaQDynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"AlphaQSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"BisonFiDynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"BisonFiSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"C","type":{"kind":"struct","fields":[{"name":"_padding","type":"u8"}]}},{"name":"ClearpoolsSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"a_to_b","type":"bool"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"DFlowDynamicRouteV1Options","type":{"kind":"struct","fields":[{"name":"candidate_actions","type":{"vec":{"defined":{"name":"DynamicRouteV1CandidateAction"}}}},{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"DeriverseSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"DynamicRouteV1CandidateAction","type":{"kind":"enum","variants":[{"name":"SolFi","fields":[{"defined":{"name":"SolFiDynamicRouteV1Options"}}]},{"name":"Rubicon","fields":[{"defined":{"name":"RubiconDynamicRouteV1Options"}}]},{"name":"TesseraV","fields":[{"defined":{"name":"TesseraVDynamicRouteV1Options"}}]},{"name":"HumidiFi","fields":[{"defined":{"name":"HumidiFiDynamicRouteV1Options"}}]},{"name":"SolFiV2","fields":[{"defined":{"name":"SolFiV2DynamicRouteV1Options"}}]},{"name":"Mozart","fields":[{"defined":{"name":"MozartDynamicRouteV1Options"}}]},{"name":"ObricV2","fields":[{"defined":{"name":"ObricV2DynamicRouteV1Options"}}]},{"name":"Nexus","fields":[{"defined":{"name":"NexusDynamicRouteV1Options"}}]},{"name":"BisonFi","fields":[{"defined":{"name":"BisonFiDynamicRouteV1Options"}}]},{"name":"ZeroFi","fields":[{"defined":{"name":"ZeroFiDynamicRouteV1Options"}}]},{"name":"Manifest","fields":[{"defined":{"name":"ManifestDynamicRouteV1Options"}}]},{"name":"AlphaQ","fields":[{"defined":{"name":"AlphaQDynamicRouteV1Options"}}]},{"name":"LemmingsFi","fields":[{"defined":{"name":"LemmingsFiDynamicRouteV1Options"}}]}]}},{"name":"FeeEvent","type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"FillOrderParams","type":{"kind":"struct","fields":[{"name":"swap_actions","type":{"vec":{"defined":{"name":"Action"}}}},{"name":"platform_fee_ubps","type":"u32"}]}},{"name":"GammaSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"endorsed","type":"bool"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"GhostSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"HeavenSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"HumidiFiDynamicRouteV1Options","type":{"kind":"struct","fields":[{"name":"swap_id","type":"u64"}]}},{"name":"HumidiFiSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"swap_id","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"J","type":{"kind":"struct","fields":[{"name":"_padding","type":"u8"}]}},{"name":"KDEXSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"LegPrice","type":{"kind":"struct","fields":[{"name":"output_atoms_per_input_token","type":"u64"},{"name":"input_mint_decimals","type":"u8"}]}},{"name":"LemmingsFiDynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"LemmingsFiSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"LifinityV2SwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"ManifestDynamicRouteV1Options","type":{"kind":"struct","fields":[{"name":"include_global","type":"bool"}]}},{"name":"ManifestSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"MetaDaoSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"MeteoraDammV1SwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"MeteoraDammV2SwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"MeteoraDbcSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"is_rate_limiter_applied","type":"bool"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"MeteoraDlmmSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"num_bin_arrays","type":"u8"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"MeteoraDlmmSwapV2Options","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"num_bin_arrays","type":"u8"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"MozartDynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"MozartSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"NexusDynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"NexusSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"O","type":{"kind":"struct","fields":[{"name":"a","type":{"vec":{"defined":{"name":"A"}}}},{"name":"amount","type":"u64"},{"name":"orchestrator_flags","type":{"defined":{"name":"OrchestratorFlags"}}},{"name":"pfs","type":"u16"},{"name":"dfs","type":"u16"}]}},{"name":"ObricV2DynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"ObricV2SwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"OpenOrderParams","type":{"kind":"struct","fields":[{"name":"input_amount","type":"u64"},{"name":"quoted_out_amount","docs":["Quoted output amount after platform fee"],"type":"u64"},{"name":"fee_budget","docs":["Maximum amount in lamports that the fee payer will pay to have the order filled or closed"],"type":"u64"},{"name":"order_account_id","type":"u64"},{"name":"fillable_for_slots","docs":["Number of slots after the slot in which the order is opened for which the order is fillable.","If zero, the order is only fillable in the slot in which the open transaction landed."],"type":"u32"},{"name":"slippage_bps","docs":["Max allowed slippage in basis points. Along with `quoted_out_amount`, this is used to","calculate the minimum allowed output amount."],"type":"u16"},{"name":"closer","docs":["Account that is authorized to close the order"],"type":"pubkey"},{"name":"flags","docs":["Flags for the order"],"type":"u8"}]}},{"name":"OpenPredictionsOrderOptions","type":{"kind":"struct","fields":[{"name":"nonce","type":"u64"},{"name":"order_outcome","type":"u8"},{"name":"quoted_out_amount","docs":["Quoted output amount for the predictions order"],"type":"u64"},{"name":"slippage_bps","docs":["Slippage for the predictions order (separate from swap slippage)"],"type":"u16"},{"name":"platform_fee_recipient_vault","type":"pubkey"},{"name":"platform_fee_scale","type":"u16"}]}},{"name":"OrchestratorFlags","type":{"kind":"struct","fields":[{"name":"flags","type":"u8"}]}},{"name":"Order","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"closer","docs":["Account that is authorized to close the order"],"type":"pubkey"},{"name":"output_token_account","docs":["Account to which output from the fill should be sent. If the order specifies that it should","output native SOL, then this should be the user's wallet, although any account is allowed.","If the order doesn't specify that it should output native SOL, then this must be an SPL","token account, and the token account's mint determines the order's output mint."],"type":"pubkey"},{"name":"return_input_token_account","docs":["Account to which leftover tokens in the order vault should be returned when the order is","filled or closed. If the input is native SOL, this should be the user's wallet, although any","account is allowed. If the input is wrapped SOL or an SPL token, this must be an SPL token","account for the input mint."],"type":"pubkey"},{"name":"return_rent_to","docs":["Account to which rent for the account should be returned when the account is closed"],"type":"pubkey"},{"name":"id","docs":["ID used to produce a unique PDA for the order account"],"type":"u64"},{"name":"quoted_out_amount","type":"u64"},{"name":"last_fillable_slot","docs":["Highest slot at which the order can be filled"],"type":"u64"},{"name":"slippage_bps","docs":["Max allowed slippage in basis points. Along with `quoted_out_amount`, this is used to","calculate the minimum allowed output amount for the order."],"type":"u16"},{"name":"bump","type":"u8"},{"name":"vault_bump","docs":["Bump seed for the vault token account associated with the order"],"type":"u8"},{"name":"flags","docs":["Flags for the order"],"type":"u8"},{"name":"_padding1","type":"u8"},{"name":"_padding2","type":"u8"},{"name":"_padding3","type":"u8"}]}},{"name":"PhoenixSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"side","type":{"defined":{"name":"Side"}}},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"PumpFunAmmBuyOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"PumpFunAmmSellOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"PumpFunBuyOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"PumpFunBuyV2Options","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"PumpFunSellOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"PumpFunSellV2Options","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"RaydiumAmmSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"RaydiumClmmSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"num_remaining_accounts","type":"u8"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"RaydiumClmmSwapV2Options","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"num_remaining_accounts","type":"u8"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"RaydiumCpSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"RaydiumLaunchlabSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"RecordId2Options","type":{"kind":"struct","fields":[{"name":"id","type":{"array":["u8",4]}}]}},{"name":"RecordIdOptions","type":{"kind":"struct","fields":[{"name":"id","type":{"array":["u8",76]}}]}},{"name":"RubiconDynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"RubiconSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"SanctumInfinityLiquidityOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"lst_value_calc_accs","type":"u8"},{"name":"lst_index","type":"u32"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"SanctumInfinitySwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"src_lst_value_calc_accs","type":"u8"},{"name":"dst_lst_value_calc_accs","type":"u8"},{"name":"src_lst_index","type":"u32"},{"name":"dst_lst_index","type":"u32"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"SarosDlmmSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"ScorchSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"id","type":"u128"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"SetMaxUnderconsumptionBpsOptions","type":{"kind":"struct","fields":[{"name":"max_underconsumption_bps","type":"u16"}]}},{"name":"SetMinimumLegOutputsOptions","type":{"kind":"struct","fields":[{"name":"minimum_outputs","docs":["Minimum allowed output amount for each swap leg"],"type":{"vec":"u64"}}]}},{"name":"SetMinimumLegPricesOptions","type":{"kind":"struct","fields":[{"name":"minimum_prices","docs":["Minimum allowed price for each swap leg"],"type":{"vec":{"defined":{"name":"LegPrice"}}}}]}},{"name":"Side","type":{"kind":"enum","variants":[{"name":"Bid"},{"name":"Ask"}]}},{"name":"SolFiDynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"SolFiSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"SolFiV2DynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"SolFiV2SwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"StabbleStableSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"StabbleWeightedSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"Swap2Params","type":{"kind":"struct","fields":[{"name":"actions","type":{"vec":{"defined":{"name":"Action"}}}},{"name":"quoted_out_amount","docs":["Quoted output amount for the swap after platform fee"],"type":"u64"},{"name":"slippage_bps","docs":["Max allowed slippage in basis points. Along with `quoted_out_amount`, this is used to","calculate the minimum allowed output amount for the swap."],"type":"u16"},{"name":"platform_fee_bps","docs":["Platform fee in basis points. If nonzero, this percentage of the output amount will be","transferred to the platform fee account, and the instruction must include the platform fee","account as the last account. The platform fee account must be a valid token account for the","output mint, and it must specify the public key of the Referral program's DFlow project as","its owner."],"type":"u16"},{"name":"positive_slippage_fee_limit_pct","docs":["Limit on the positive slippage fee in percent. The positive slippage fee is limited to the","lesser of (1) the excess actual out amount above the quoted amount and (2) this percentage","of the actual out amount after platform fee."],"type":"u8"}]}},{"name":"SwapEvent","type":{"kind":"struct","fields":[{"name":"amm","type":"pubkey"},{"name":"input_mint","type":"pubkey"},{"name":"input_amount","type":"u64"},{"name":"output_mint","type":"pubkey"},{"name":"output_amount","type":"u64"}]}},{"name":"SwapParams","type":{"kind":"struct","fields":[{"name":"actions","type":{"vec":{"defined":{"name":"Action"}}}},{"name":"quoted_out_amount","docs":["Quoted output amount for the swap after platform fee"],"type":"u64"},{"name":"slippage_bps","docs":["Max allowed slippage in basis points. Along with `quoted_out_amount`, this is used to","calculate the minimum allowed output amount for the swap."],"type":"u16"},{"name":"platform_fee_bps","docs":["Platform fee in basis points. If nonzero, this percentage of the output amount will be","transferred to the platform fee account, and the instruction must include the platform fee","account as the last account. The platform fee account must be a valid token account for the","output mint, and it must specify the public key of the Referral program's DFlow project as","its owner."],"type":"u16"}]}},{"name":"TesseraVDynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"TesseraVSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"TokenSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"TransferFeeOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"VaultSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"VertigoSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"WhirlpoolsSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"a_to_b","type":"bool"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"WhirlpoolsSwapV2Options","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"a_to_b","type":"bool"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"WrapSolOptions","type":{"kind":"struct","fields":[{"name":"lamports","type":"u64"}]}},{"name":"XoCashExchangeSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"ZeroFiDynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"ZeroFiSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}}]} \ No newline at end of file diff --git a/idls/drift_v2.dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH.json b/idls/drift_v2.dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH.json new file mode 100644 index 0000000..ce5f81e --- /dev/null +++ b/idls/drift_v2.dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH.json @@ -0,0 +1 @@ +{"version":"2.150.0","name":"drift","instructions":[{"name":"initializeUser","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"subAccountId","type":"u16"},{"name":"name","type":{"array":["u8",32]}}]},{"name":"initializeUserStats","accounts":[{"name":"userStats","isMut":true,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeSignedMsgUserOrders","accounts":[{"name":"signedMsgUserOrders","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"numOrders","type":"u16"}]},{"name":"resizeSignedMsgUserOrders","accounts":[{"name":"signedMsgUserOrders","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"user","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"numOrders","type":"u16"}]},{"name":"initializeSignedMsgWsDelegates","accounts":[{"name":"signedMsgWsDelegates","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"delegates","type":{"vec":"publicKey"}}]},{"name":"changeSignedMsgWsDelegateStatus","accounts":[{"name":"signedMsgWsDelegates","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"delegate","type":"publicKey"},{"name":"add","type":"bool"}]},{"name":"initializeFuelOverflow","accounts":[{"name":"fuelOverflow","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"sweepFuel","accounts":[{"name":"fuelOverflow","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"signer","isMut":false,"isSigner":true}],"args":[]},{"name":"resetFuelSeason","accounts":[{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[]},{"name":"initializeReferrerName","accounts":[{"name":"referrerName","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"name","type":{"array":["u8",32]}}]},{"name":"deposit","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"amount","type":"u64"},{"name":"reduceOnly","type":"bool"}]},{"name":"withdraw","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"amount","type":"u64"},{"name":"reduceOnly","type":"bool"}]},{"name":"transferDeposit","accounts":[{"name":"fromUser","isMut":true,"isSigner":false},{"name":"toUser","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarketVault","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"amount","type":"u64"}]},{"name":"transferPools","accounts":[{"name":"fromUser","isMut":true,"isSigner":false},{"name":"toUser","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"depositFromSpotMarketVault","isMut":true,"isSigner":false},{"name":"depositToSpotMarketVault","isMut":true,"isSigner":false},{"name":"borrowFromSpotMarketVault","isMut":true,"isSigner":false},{"name":"borrowToSpotMarketVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false}],"args":[{"name":"depositFromMarketIndex","type":"u16"},{"name":"depositToMarketIndex","type":"u16"},{"name":"borrowFromMarketIndex","type":"u16"},{"name":"borrowToMarketIndex","type":"u16"},{"name":"depositAmount","type":{"option":"u64"}},{"name":"borrowAmount","type":{"option":"u64"}}]},{"name":"transferPerpPosition","accounts":[{"name":"fromUser","isMut":true,"isSigner":false},{"name":"toUser","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"amount","type":{"option":"i64"}}]},{"name":"placePerpOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"OrderParams"}}]},{"name":"cancelOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"orderId","type":{"option":"u32"}}]},{"name":"cancelOrderByUserId","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"userOrderId","type":"u8"}]},{"name":"cancelOrders","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"marketType","type":{"option":{"defined":"MarketType"}}},{"name":"marketIndex","type":{"option":"u16"}},{"name":"direction","type":{"option":{"defined":"PositionDirection"}}}]},{"name":"cancelOrdersByIds","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"orderIds","type":{"vec":"u32"}}]},{"name":"modifyOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"orderId","type":{"option":"u32"}},{"name":"modifyOrderParams","type":{"defined":"ModifyOrderParams"}}]},{"name":"modifyOrderByUserId","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"userOrderId","type":"u8"},{"name":"modifyOrderParams","type":{"defined":"ModifyOrderParams"}}]},{"name":"placeAndTakePerpOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"OrderParams"}},{"name":"successCondition","type":{"option":"u32"}}]},{"name":"placeAndMakePerpOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"taker","isMut":true,"isSigner":false},{"name":"takerStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"OrderParams"}},{"name":"takerOrderId","type":"u32"}]},{"name":"placeAndMakeSignedMsgPerpOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"taker","isMut":true,"isSigner":false},{"name":"takerStats","isMut":true,"isSigner":false},{"name":"takerSignedMsgUserOrders","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"OrderParams"}},{"name":"signedMsgOrderUuid","type":{"array":["u8",8]}}]},{"name":"placeSignedMsgTakerOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"signedMsgUserOrders","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"ixSysvar","isMut":false,"isSigner":false,"docs":["the supplied Sysvar could be anything else.","The Instruction Sysvar has not been implemented","in the Anchor framework yet, so this is the safe approach."]}],"args":[{"name":"signedMsgOrderParamsMessageBytes","type":"bytes"},{"name":"isDelegateSigner","type":"bool"}]},{"name":"placeSpotOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"OrderParams"}}]},{"name":"placeAndTakeSpotOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"OrderParams"}},{"name":"fulfillmentType","type":{"option":{"defined":"SpotFulfillmentType"}}},{"name":"makerOrderId","type":{"option":"u32"}}]},{"name":"placeAndMakeSpotOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"taker","isMut":true,"isSigner":false},{"name":"takerStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"OrderParams"}},{"name":"takerOrderId","type":"u32"},{"name":"fulfillmentType","type":{"option":{"defined":"SpotFulfillmentType"}}}]},{"name":"placeOrders","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"vec":{"defined":"OrderParams"}}}]},{"name":"beginSwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"outSpotMarketVault","isMut":true,"isSigner":false},{"name":"inSpotMarketVault","isMut":true,"isSigner":false},{"name":"outTokenAccount","isMut":true,"isSigner":false},{"name":"inTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"instructions","isMut":false,"isSigner":false,"docs":["Instructions Sysvar for instruction introspection"]}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"outMarketIndex","type":"u16"},{"name":"amountIn","type":"u64"}]},{"name":"endSwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"outSpotMarketVault","isMut":true,"isSigner":false},{"name":"inSpotMarketVault","isMut":true,"isSigner":false},{"name":"outTokenAccount","isMut":true,"isSigner":false},{"name":"inTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"instructions","isMut":false,"isSigner":false,"docs":["Instructions Sysvar for instruction introspection"]}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"outMarketIndex","type":"u16"},{"name":"limitPrice","type":{"option":"u64"}},{"name":"reduceOnly","type":{"option":{"defined":"SwapReduceOnly"}}}]},{"name":"updateUserName","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"subAccountId","type":"u16"},{"name":"name","type":{"array":["u8",32]}}]},{"name":"updateUserCustomMarginRatio","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"subAccountId","type":"u16"},{"name":"marginRatio","type":"u32"}]},{"name":"updateUserPerpPositionCustomMarginRatio","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"subAccountId","type":"u16"},{"name":"perpMarketIndex","type":"u16"},{"name":"marginRatio","type":"u16"}]},{"name":"updateUserMarginTradingEnabled","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"subAccountId","type":"u16"},{"name":"marginTradingEnabled","type":"bool"}]},{"name":"updateUserPoolId","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"subAccountId","type":"u16"},{"name":"poolId","type":"u8"}]},{"name":"updateUserDelegate","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"subAccountId","type":"u16"},{"name":"delegate","type":"publicKey"}]},{"name":"updateUserReduceOnly","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"subAccountId","type":"u16"},{"name":"reduceOnly","type":"bool"}]},{"name":"updateUserProtectedMakerOrders","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"protectedMakerModeConfig","isMut":true,"isSigner":false}],"args":[{"name":"subAccountId","type":"u16"},{"name":"protectedMakerOrders","type":"bool"}]},{"name":"deleteUser","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true}],"args":[]},{"name":"forceDeleteUser","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":false},{"name":"keeper","isMut":true,"isSigner":true},{"name":"driftSigner","isMut":false,"isSigner":false}],"args":[]},{"name":"deleteSignedMsgUserOrders","accounts":[{"name":"signedMsgUserOrders","isMut":true,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[]},{"name":"reclaimRent","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"enableUserHighLeverageMode","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"highLeverageModeConfig","isMut":true,"isSigner":false}],"args":[{"name":"subAccountId","type":"u16"}]},{"name":"fillPerpOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"filler","isMut":true,"isSigner":false},{"name":"fillerStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false}],"args":[{"name":"orderId","type":{"option":"u32"}},{"name":"makerOrderId","type":{"option":"u32"}}]},{"name":"revertFill","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"filler","isMut":true,"isSigner":false},{"name":"fillerStats","isMut":true,"isSigner":false}],"args":[]},{"name":"fillSpotOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"filler","isMut":true,"isSigner":false},{"name":"fillerStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false}],"args":[{"name":"orderId","type":{"option":"u32"}},{"name":"fulfillmentType","type":{"option":{"defined":"SpotFulfillmentType"}}},{"name":"makerOrderId","type":{"option":"u32"}}]},{"name":"triggerOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"filler","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false}],"args":[{"name":"orderId","type":"u32"}]},{"name":"forceCancelOrders","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"filler","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false}],"args":[]},{"name":"updateUserIdle","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"filler","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false}],"args":[]},{"name":"logUserBalances","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"user","isMut":true,"isSigner":false}],"args":[]},{"name":"disableUserHighLeverageMode","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"user","isMut":true,"isSigner":false},{"name":"highLeverageModeConfig","isMut":true,"isSigner":false}],"args":[{"name":"disableMaintenance","type":"bool"}]},{"name":"updateUserStatsReferrerStatus","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"userStats","isMut":true,"isSigner":false}],"args":[]},{"name":"adminUpdateUserStatsPausedOperations","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false}],"args":[{"name":"pausedOperations","type":"u8"}]},{"name":"settlePnl","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"spotMarketVault","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"settleMultiplePnls","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"spotMarketVault","isMut":false,"isSigner":false}],"args":[{"name":"marketIndexes","type":{"vec":"u16"}},{"name":"mode","type":{"defined":"SettlePnlMode"}}]},{"name":"settleFundingPayment","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false}],"args":[]},{"name":"settleExpiredMarket","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"liquidatePerp","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"liquidator","isMut":true,"isSigner":false},{"name":"liquidatorStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"liquidatorMaxBaseAssetAmount","type":"u64"},{"name":"limitPrice","type":{"option":"u64"}}]},{"name":"liquidatePerpWithFill","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"liquidator","isMut":true,"isSigner":false},{"name":"liquidatorStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"liquidateSpot","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"liquidator","isMut":true,"isSigner":false},{"name":"liquidatorStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false}],"args":[{"name":"assetMarketIndex","type":"u16"},{"name":"liabilityMarketIndex","type":"u16"},{"name":"liquidatorMaxLiabilityTransfer","type":"u128"},{"name":"limitPrice","type":{"option":"u64"}}]},{"name":"liquidateSpotWithSwapBegin","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"liquidator","isMut":true,"isSigner":false},{"name":"liquidatorStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"liabilitySpotMarketVault","isMut":true,"isSigner":false},{"name":"assetSpotMarketVault","isMut":true,"isSigner":false},{"name":"liabilityTokenAccount","isMut":true,"isSigner":false},{"name":"assetTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"instructions","isMut":false,"isSigner":false,"docs":["Instructions Sysvar for instruction introspection"]}],"args":[{"name":"assetMarketIndex","type":"u16"},{"name":"liabilityMarketIndex","type":"u16"},{"name":"swapAmount","type":"u64"}]},{"name":"liquidateSpotWithSwapEnd","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"liquidator","isMut":true,"isSigner":false},{"name":"liquidatorStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"liabilitySpotMarketVault","isMut":true,"isSigner":false},{"name":"assetSpotMarketVault","isMut":true,"isSigner":false},{"name":"liabilityTokenAccount","isMut":true,"isSigner":false},{"name":"assetTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"instructions","isMut":false,"isSigner":false,"docs":["Instructions Sysvar for instruction introspection"]}],"args":[{"name":"assetMarketIndex","type":"u16"},{"name":"liabilityMarketIndex","type":"u16"}]},{"name":"liquidateBorrowForPerpPnl","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"liquidator","isMut":true,"isSigner":false},{"name":"liquidatorStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false}],"args":[{"name":"perpMarketIndex","type":"u16"},{"name":"spotMarketIndex","type":"u16"},{"name":"liquidatorMaxLiabilityTransfer","type":"u128"},{"name":"limitPrice","type":{"option":"u64"}}]},{"name":"liquidatePerpPnlForDeposit","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"liquidator","isMut":true,"isSigner":false},{"name":"liquidatorStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false}],"args":[{"name":"perpMarketIndex","type":"u16"},{"name":"spotMarketIndex","type":"u16"},{"name":"liquidatorMaxPnlTransfer","type":"u128"},{"name":"limitPrice","type":{"option":"u64"}}]},{"name":"setUserStatusToBeingLiquidated","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[]},{"name":"resolvePerpPnlDeficit","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"spotMarketIndex","type":"u16"},{"name":"perpMarketIndex","type":"u16"}]},{"name":"resolvePerpBankruptcy","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"liquidator","isMut":true,"isSigner":false},{"name":"liquidatorStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"quoteSpotMarketIndex","type":"u16"},{"name":"marketIndex","type":"u16"}]},{"name":"resolveSpotBankruptcy","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"liquidator","isMut":true,"isSigner":false},{"name":"liquidatorStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"settleRevenueToInsuranceFund","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"spotMarketIndex","type":"u16"}]},{"name":"updateFundingRate","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"updatePrelaunchOracle","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":false,"isSigner":false},{"name":"oracle","isMut":true,"isSigner":false}],"args":[]},{"name":"updatePerpBidAskTwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"keeperStats","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[]},{"name":"updateSpotMarketCumulativeInterest","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"spotMarketVault","isMut":false,"isSigner":false}],"args":[]},{"name":"updateAmms","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"marketIndexes","type":{"vec":"u16"}}]},{"name":"updateSpotMarketExpiry","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"expiryTs","type":"i64"}]},{"name":"updateUserQuoteAssetInsuranceStake","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"insuranceFundStake","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"signer","isMut":false,"isSigner":true},{"name":"insuranceFundVault","isMut":true,"isSigner":false}],"args":[]},{"name":"updateUserGovTokenInsuranceStake","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"insuranceFundStake","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"signer","isMut":false,"isSigner":true},{"name":"insuranceFundVault","isMut":true,"isSigner":false}],"args":[]},{"name":"updateDelegateUserGovTokenInsuranceStake","accounts":[{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"insuranceFundStake","isMut":false,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeInsuranceFundStake","accounts":[{"name":"spotMarket","isMut":false,"isSigner":false},{"name":"insuranceFundStake","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"addInsuranceFundStake","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"insuranceFundStake","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"amount","type":"u64"}]},{"name":"requestRemoveInsuranceFundStake","accounts":[{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"insuranceFundStake","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"insuranceFundVault","isMut":true,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"amount","type":"u64"}]},{"name":"cancelRequestRemoveInsuranceFundStake","accounts":[{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"insuranceFundStake","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"insuranceFundVault","isMut":true,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"removeInsuranceFundStake","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"insuranceFundStake","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"beginInsuranceFundSwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"outInsuranceFundVault","isMut":true,"isSigner":false},{"name":"inInsuranceFundVault","isMut":true,"isSigner":false},{"name":"outTokenAccount","isMut":true,"isSigner":false},{"name":"inTokenAccount","isMut":true,"isSigner":false},{"name":"ifRebalanceConfig","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"instructions","isMut":false,"isSigner":false,"docs":["Instructions Sysvar for instruction introspection"]}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"outMarketIndex","type":"u16"},{"name":"amountIn","type":"u64"}]},{"name":"endInsuranceFundSwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"outInsuranceFundVault","isMut":true,"isSigner":false},{"name":"inInsuranceFundVault","isMut":true,"isSigner":false},{"name":"outTokenAccount","isMut":true,"isSigner":false},{"name":"inTokenAccount","isMut":true,"isSigner":false},{"name":"ifRebalanceConfig","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"instructions","isMut":false,"isSigner":false,"docs":["Instructions Sysvar for instruction introspection"]}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"outMarketIndex","type":"u16"}]},{"name":"transferProtocolIfSharesToRevenuePool","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"ifRebalanceConfig","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"amount","type":"u64"}]},{"name":"depositIntoInsuranceFundStake","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"insuranceFundStake","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"amount","type":"u64"}]},{"name":"updatePythPullOracle","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"pythSolanaReceiver","isMut":false,"isSigner":false},{"name":"encodedVaa","isMut":false,"isSigner":false},{"name":"priceFeed","isMut":true,"isSigner":false}],"args":[{"name":"feedId","type":{"array":["u8",32]}},{"name":"params","type":"bytes"}]},{"name":"postPythPullOracleUpdateAtomic","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"pythSolanaReceiver","isMut":false,"isSigner":false},{"name":"guardianSet","isMut":false,"isSigner":false},{"name":"priceFeed","isMut":true,"isSigner":false}],"args":[{"name":"feedId","type":{"array":["u8",32]}},{"name":"params","type":"bytes"}]},{"name":"postMultiPythPullOracleUpdatesAtomic","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"pythSolanaReceiver","isMut":false,"isSigner":false},{"name":"guardianSet","isMut":false,"isSigner":false}],"args":[{"name":"params","type":"bytes"}]},{"name":"pauseSpotMarketDepositWithdraw","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"keeper","isMut":false,"isSigner":true},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":false,"isSigner":false}],"args":[]},{"name":"initialize","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":true,"isSigner":false},{"name":"quoteAssetMint","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeSpotMarket","accounts":[{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"spotMarketMint","isMut":false,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"optimalUtilization","type":"u32"},{"name":"optimalBorrowRate","type":"u32"},{"name":"maxBorrowRate","type":"u32"},{"name":"oracleSource","type":{"defined":"OracleSource"}},{"name":"initialAssetWeight","type":"u32"},{"name":"maintenanceAssetWeight","type":"u32"},{"name":"initialLiabilityWeight","type":"u32"},{"name":"maintenanceLiabilityWeight","type":"u32"},{"name":"imfFactor","type":"u32"},{"name":"liquidatorFee","type":"u32"},{"name":"ifLiquidationFee","type":"u32"},{"name":"activeStatus","type":"bool"},{"name":"assetTier","type":{"defined":"AssetTier"}},{"name":"scaleInitialAssetWeightStart","type":"u64"},{"name":"withdrawGuardThreshold","type":"u64"},{"name":"orderTickSize","type":"u64"},{"name":"orderStepSize","type":"u64"},{"name":"ifTotalFactor","type":"u32"},{"name":"name","type":{"array":["u8",32]}}]},{"name":"deleteInitializedSpotMarket","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":true,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"initializeSerumFulfillmentConfig","accounts":[{"name":"baseSpotMarket","isMut":false,"isSigner":false},{"name":"quoteSpotMarket","isMut":false,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"serumProgram","isMut":false,"isSigner":false},{"name":"serumMarket","isMut":false,"isSigner":false},{"name":"serumOpenOrders","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"serumFulfillmentConfig","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"updateSerumFulfillmentConfigStatus","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"serumFulfillmentConfig","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true}],"args":[{"name":"status","type":{"defined":"SpotFulfillmentConfigStatus"}}]},{"name":"initializeOpenbookV2FulfillmentConfig","accounts":[{"name":"baseSpotMarket","isMut":false,"isSigner":false},{"name":"quoteSpotMarket","isMut":false,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"openbookV2Program","isMut":false,"isSigner":false},{"name":"openbookV2Market","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"openbookV2FulfillmentConfig","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"openbookV2FulfillmentConfigStatus","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"openbookV2FulfillmentConfig","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true}],"args":[{"name":"status","type":{"defined":"SpotFulfillmentConfigStatus"}}]},{"name":"initializePhoenixFulfillmentConfig","accounts":[{"name":"baseSpotMarket","isMut":false,"isSigner":false},{"name":"quoteSpotMarket","isMut":false,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"phoenixProgram","isMut":false,"isSigner":false},{"name":"phoenixMarket","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"phoenixFulfillmentConfig","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"phoenixFulfillmentConfigStatus","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"phoenixFulfillmentConfig","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true}],"args":[{"name":"status","type":{"defined":"SpotFulfillmentConfigStatus"}}]},{"name":"initializePerpMarket","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":true,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"ammBaseAssetReserve","type":"u128"},{"name":"ammQuoteAssetReserve","type":"u128"},{"name":"ammPeriodicity","type":"i64"},{"name":"ammPegMultiplier","type":"u128"},{"name":"oracleSource","type":{"defined":"OracleSource"}},{"name":"contractTier","type":{"defined":"ContractTier"}},{"name":"marginRatioInitial","type":"u32"},{"name":"marginRatioMaintenance","type":"u32"},{"name":"liquidatorFee","type":"u32"},{"name":"ifLiquidationFee","type":"u32"},{"name":"imfFactor","type":"u32"},{"name":"activeStatus","type":"bool"},{"name":"baseSpread","type":"u32"},{"name":"maxSpread","type":"u32"},{"name":"maxOpenInterest","type":"u128"},{"name":"maxRevenueWithdrawPerPeriod","type":"u64"},{"name":"quoteMaxInsurance","type":"u64"},{"name":"orderStepSize","type":"u64"},{"name":"orderTickSize","type":"u64"},{"name":"minOrderSize","type":"u64"},{"name":"concentrationCoefScale","type":"u128"},{"name":"curveUpdateIntensity","type":"u8"},{"name":"ammJitIntensity","type":"u8"},{"name":"name","type":{"array":["u8",32]}},{"name":"lpPoolId","type":"u8"}]},{"name":"initializeAmmCache","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"ammCache","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"addMarketToAmmCache","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"ammCache","isMut":true,"isSigner":false},{"name":"perpMarket","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"deleteAmmCache","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"ammCache","isMut":true,"isSigner":false}],"args":[]},{"name":"updateInitialAmmCacheInfo","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"ammCache","isMut":true,"isSigner":false}],"args":[]},{"name":"initializePredictionMarket","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[]},{"name":"deleteInitializedPerpMarket","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":true,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"moveAmmPrice","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"baseAssetReserve","type":"u128"},{"name":"quoteAssetReserve","type":"u128"},{"name":"sqrtK","type":"u128"}]},{"name":"recenterPerpMarketAmm","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"pegMultiplier","type":"u128"},{"name":"sqrtK","type":"u128"}]},{"name":"recenterPerpMarketAmmCrank","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"spotMarket","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false}],"args":[{"name":"depth","type":{"option":"u128"}}]},{"name":"updatePerpMarketAmmSummaryStats","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"spotMarket","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"UpdatePerpMarketSummaryStatsParams"}}]},{"name":"updatePerpMarketExpiry","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"expiryTs","type":"i64"}]},{"name":"updatePerpMarketLpPoolPausedOperations","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"lpPausedOperations","type":"u8"}]},{"name":"updatePerpMarketLpPoolStatus","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"ammCache","isMut":true,"isSigner":false}],"args":[{"name":"lpStatus","type":"u8"}]},{"name":"updatePerpMarketLpPoolFeeTransferScalar","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"optionalLpFeeTransferScalar","type":{"option":"u8"}},{"name":"optionalLpNetPnlTransferScalar","type":{"option":"u8"}}]},{"name":"settleExpiredMarketPoolsToRevenuePool","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[]},{"name":"depositIntoPerpMarketFeePool","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"sourceVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"quoteSpotMarket","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"updatePerpMarketPnlPool","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"depositIntoSpotMarketVault","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"sourceVault","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"depositIntoSpotMarketRevenuePool","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"repegAmmCurve","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"newPegCandidate","type":"u128"}]},{"name":"updatePerpMarketAmmOracleTwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[]},{"name":"resetPerpMarketAmmOracleTwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[]},{"name":"updateK","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false}],"args":[{"name":"sqrtK","type":"u128"}]},{"name":"updatePerpMarketMarginRatio","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"marginRatioInitial","type":"u32"},{"name":"marginRatioMaintenance","type":"u32"}]},{"name":"updatePerpMarketHighLeverageMarginRatio","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"marginRatioInitial","type":"u16"},{"name":"marginRatioMaintenance","type":"u16"}]},{"name":"updatePerpMarketFundingPeriod","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"fundingPeriod","type":"i64"}]},{"name":"updatePerpMarketMaxImbalances","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"unrealizedMaxImbalance","type":"u64"},{"name":"maxRevenueWithdrawPerPeriod","type":"u64"},{"name":"quoteMaxInsurance","type":"u64"}]},{"name":"updatePerpMarketLiquidationFee","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"liquidatorFee","type":"u32"},{"name":"ifLiquidationFee","type":"u32"}]},{"name":"updatePerpMarketLpPoolId","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"lpPoolId","type":"u8"}]},{"name":"updateInsuranceFundUnstakingPeriod","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"insuranceFundUnstakingPeriod","type":"i64"}]},{"name":"updateSpotMarketPoolId","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"poolId","type":"u8"}]},{"name":"updateSpotMarketLiquidationFee","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"liquidatorFee","type":"u32"},{"name":"ifLiquidationFee","type":"u32"}]},{"name":"updateWithdrawGuardThreshold","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"withdrawGuardThreshold","type":"u64"}]},{"name":"updateSpotMarketIfFactor","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"spotMarketIndex","type":"u16"},{"name":"userIfFactor","type":"u32"},{"name":"totalIfFactor","type":"u32"}]},{"name":"updateSpotMarketRevenueSettlePeriod","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"revenueSettlePeriod","type":"i64"}]},{"name":"updateSpotMarketStatus","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"status","type":{"defined":"MarketStatus"}}]},{"name":"updateSpotMarketPausedOperations","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"pausedOperations","type":"u8"}]},{"name":"updateSpotMarketAssetTier","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"assetTier","type":{"defined":"AssetTier"}}]},{"name":"updateSpotMarketMarginWeights","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"initialAssetWeight","type":"u32"},{"name":"maintenanceAssetWeight","type":"u32"},{"name":"initialLiabilityWeight","type":"u32"},{"name":"maintenanceLiabilityWeight","type":"u32"},{"name":"imfFactor","type":"u32"}]},{"name":"updateSpotMarketBorrowRate","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"optimalUtilization","type":"u32"},{"name":"optimalBorrowRate","type":"u32"},{"name":"maxBorrowRate","type":"u32"},{"name":"minBorrowRate","type":{"option":"u8"}}]},{"name":"updateSpotMarketMaxTokenDeposits","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"maxTokenDeposits","type":"u64"}]},{"name":"updateSpotMarketMaxTokenBorrows","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"maxTokenBorrowsFraction","type":"u16"}]},{"name":"updateSpotMarketScaleInitialAssetWeightStart","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"scaleInitialAssetWeightStart","type":"u64"}]},{"name":"updateSpotMarketOracle","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oldOracle","isMut":false,"isSigner":false}],"args":[{"name":"oracle","type":"publicKey"},{"name":"oracleSource","type":{"defined":"OracleSource"}},{"name":"skipInvariantCheck","type":"bool"}]},{"name":"updateSpotMarketStepSizeAndTickSize","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"stepSize","type":"u64"},{"name":"tickSize","type":"u64"}]},{"name":"updateSpotMarketMinOrderSize","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"orderSize","type":"u64"}]},{"name":"updateSpotMarketOrdersEnabled","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"ordersEnabled","type":"bool"}]},{"name":"updateSpotMarketIfPausedOperations","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"pausedOperations","type":"u8"}]},{"name":"updateSpotMarketName","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"name","type":{"array":["u8",32]}}]},{"name":"updatePerpMarketStatus","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"status","type":{"defined":"MarketStatus"}}]},{"name":"updatePerpMarketPausedOperations","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"pausedOperations","type":"u8"}]},{"name":"updatePerpMarketContractTier","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"contractTier","type":{"defined":"ContractTier"}}]},{"name":"updatePerpMarketImfFactor","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"imfFactor","type":"u32"},{"name":"unrealizedPnlImfFactor","type":"u32"}]},{"name":"updatePerpMarketUnrealizedAssetWeight","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"unrealizedInitialAssetWeight","type":"u32"},{"name":"unrealizedMaintenanceAssetWeight","type":"u32"}]},{"name":"updatePerpMarketConcentrationCoef","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"concentrationScale","type":"u128"}]},{"name":"updatePerpMarketCurveUpdateIntensity","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"curveUpdateIntensity","type":"u8"}]},{"name":"updatePerpMarketReferencePriceOffsetDeadbandPct","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"referencePriceOffsetDeadbandPct","type":"u8"}]},{"name":"updatePerpFeeStructure","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"feeStructure","type":{"defined":"FeeStructure"}}]},{"name":"updateSpotFeeStructure","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"feeStructure","type":{"defined":"FeeStructure"}}]},{"name":"updateInitialPctToLiquidate","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"initialPctToLiquidate","type":"u16"}]},{"name":"updateLiquidationDuration","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"liquidationDuration","type":"u8"}]},{"name":"updateLiquidationMarginBufferRatio","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"liquidationMarginBufferRatio","type":"u32"}]},{"name":"updateOracleGuardRails","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"oracleGuardRails","type":{"defined":"OracleGuardRails"}}]},{"name":"updateStateSettlementDuration","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"settlementDuration","type":"u16"}]},{"name":"updateStateMaxNumberOfSubAccounts","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"maxNumberOfSubAccounts","type":"u16"}]},{"name":"updateStateMaxInitializeUserFee","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"maxInitializeUserFee","type":"u16"}]},{"name":"updatePerpMarketOracle","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oldOracle","isMut":false,"isSigner":false},{"name":"ammCache","isMut":true,"isSigner":false}],"args":[{"name":"oracle","type":"publicKey"},{"name":"oracleSource","type":{"defined":"OracleSource"}},{"name":"skipInvariantCheck","type":"bool"}]},{"name":"updatePerpMarketBaseSpread","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"baseSpread","type":"u32"}]},{"name":"updateAmmJitIntensity","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"ammJitIntensity","type":"u8"}]},{"name":"updatePerpMarketMaxSpread","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"maxSpread","type":"u32"}]},{"name":"updatePerpMarketStepSizeAndTickSize","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"stepSize","type":"u64"},{"name":"tickSize","type":"u64"}]},{"name":"updatePerpMarketName","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"name","type":{"array":["u8",32]}}]},{"name":"updatePerpMarketMinOrderSize","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"orderSize","type":"u64"}]},{"name":"updatePerpMarketMaxSlippageRatio","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"maxSlippageRatio","type":"u16"}]},{"name":"updatePerpMarketMaxFillReserveFraction","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"maxFillReserveFraction","type":"u16"}]},{"name":"updatePerpMarketMaxOpenInterest","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"maxOpenInterest","type":"u128"}]},{"name":"updatePerpMarketNumberOfUsers","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"numberOfUsers","type":{"option":"u32"}},{"name":"numberOfUsersWithBase","type":{"option":"u32"}}]},{"name":"updatePerpMarketFeeAdjustment","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"feeAdjustment","type":"i16"}]},{"name":"updateSpotMarketFeeAdjustment","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"feeAdjustment","type":"i16"}]},{"name":"updatePerpMarketProtectedMakerParams","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"protectedMakerLimitPriceDivisor","type":{"option":"u8"}},{"name":"protectedMakerDynamicDivisor","type":{"option":"u8"}}]},{"name":"updatePerpMarketOracleLowRiskSlotDelayOverride","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"oracleLowRiskSlotDelayOverride","type":"i8"}]},{"name":"updatePerpMarketAmmSpreadAdjustment","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"ammSpreadAdjustment","type":"i8"},{"name":"ammInventorySpreadAdjustment","type":"i8"},{"name":"referencePriceOffset","type":"i32"}]},{"name":"updatePerpMarketOracleSlotDelayOverride","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"oracleSlotDelayOverride","type":"i8"}]},{"name":"updateAdmin","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"admin","type":"publicKey"}]},{"name":"updateDiscountMint","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"discountMint","type":"publicKey"}]},{"name":"updateExchangeStatus","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"exchangeStatus","type":"u8"}]},{"name":"updatePerpAuctionDuration","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"minPerpAuctionDuration","type":"u8"}]},{"name":"updateSpotAuctionDuration","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"defaultSpotAuctionDuration","type":"u8"}]},{"name":"initializePrelaunchOracle","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"prelaunchOracle","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"PrelaunchOracleParams"}}]},{"name":"updatePrelaunchOracleParams","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"prelaunchOracle","isMut":true,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"PrelaunchOracleParams"}}]},{"name":"deletePrelaunchOracle","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"prelaunchOracle","isMut":true,"isSigner":false},{"name":"perpMarket","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"perpMarketIndex","type":"u16"}]},{"name":"initializePythPullOracle","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"pythSolanaReceiver","isMut":false,"isSigner":false},{"name":"priceFeed","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"feedId","type":{"array":["u8",32]}}]},{"name":"initializePythLazerOracle","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"lazerOracle","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"feedId","type":"u32"}]},{"name":"postPythLazerOracleUpdate","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"pythLazerStorage","isMut":false,"isSigner":false},{"name":"ixSysvar","isMut":false,"isSigner":false}],"args":[{"name":"pythMessage","type":"bytes"}]},{"name":"initializeHighLeverageModeConfig","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"highLeverageModeConfig","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxUsers","type":"u32"}]},{"name":"updateHighLeverageModeConfig","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"highLeverageModeConfig","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"maxUsers","type":"u32"},{"name":"reduceOnly","type":"bool"},{"name":"currentUsers","type":{"option":"u32"}}]},{"name":"initializeProtectedMakerModeConfig","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"protectedMakerModeConfig","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxUsers","type":"u32"}]},{"name":"updateProtectedMakerModeConfig","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"protectedMakerModeConfig","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"maxUsers","type":"u32"},{"name":"reduceOnly","type":"bool"},{"name":"currentUsers","type":{"option":"u32"}}]},{"name":"adminDeposit","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"adminTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"amount","type":"u64"}]},{"name":"initializeIfRebalanceConfig","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"ifRebalanceConfig","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"IfRebalanceConfigParams"}}]},{"name":"updateIfRebalanceConfig","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"ifRebalanceConfig","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"IfRebalanceConfigParams"}}]},{"name":"updateFeatureBitFlagsMmOracle","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"enable","type":"bool"}]},{"name":"zeroMmOracleFields","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[]},{"name":"updateFeatureBitFlagsMedianTriggerPrice","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"enable","type":"bool"}]},{"name":"updateFeatureBitFlagsBuilderCodes","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"enable","type":"bool"}]},{"name":"initializeRevenueShare","accounts":[{"name":"revenueShare","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeRevenueShareEscrow","accounts":[{"name":"escrow","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"numOrders","type":"u16"}]},{"name":"resizeRevenueShareEscrowOrders","accounts":[{"name":"escrow","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"numOrders","type":"u16"}]},{"name":"changeApprovedBuilder","accounts":[{"name":"escrow","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"builder","type":"publicKey"},{"name":"maxFeeBps","type":"u16"},{"name":"add","type":"bool"}]},{"name":"initializeLpPool","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"lpPool","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"lpPoolTokenVault","isMut":true,"isSigner":false},{"name":"ammConstituentMapping","isMut":true,"isSigner":false},{"name":"constituentTargetBase","isMut":true,"isSigner":false},{"name":"constituentCorrelations","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"lpPoolId","type":"u8"},{"name":"minMintFee","type":"i64"},{"name":"maxAum","type":"u128"},{"name":"maxSettleQuoteAmountPerMarket","type":"u64"},{"name":"whitelistMint","type":"publicKey"}]},{"name":"updateFeatureBitFlagsSettleLpPool","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"enable","type":"bool"}]},{"name":"updateFeatureBitFlagsSwapLpPool","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"enable","type":"bool"}]},{"name":"updateFeatureBitFlagsMintRedeemLpPool","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"enable","type":"bool"}]},{"name":"initializeConstituent","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"lpPool","isMut":true,"isSigner":false},{"name":"constituentTargetBase","isMut":true,"isSigner":false},{"name":"constituentCorrelations","isMut":true,"isSigner":false},{"name":"constituent","isMut":true,"isSigner":false},{"name":"spotMarket","isMut":false,"isSigner":false},{"name":"spotMarketMint","isMut":false,"isSigner":false},{"name":"constituentVault","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"spotMarketIndex","type":"u16"},{"name":"decimals","type":"u8"},{"name":"maxWeightDeviation","type":"i64"},{"name":"swapFeeMin","type":"i64"},{"name":"swapFeeMax","type":"i64"},{"name":"maxBorrowTokenAmount","type":"u64"},{"name":"oracleStalenessThreshold","type":"u64"},{"name":"costToTrade","type":"i32"},{"name":"constituentDerivativeIndex","type":{"option":"i16"}},{"name":"constituentDerivativeDepegThreshold","type":"u64"},{"name":"derivativeWeight","type":"u64"},{"name":"volatility","type":"u64"},{"name":"gammaExecution","type":"u8"},{"name":"gammaInventory","type":"u8"},{"name":"xi","type":"u8"},{"name":"newConstituentCorrelations","type":{"vec":"i64"}}]},{"name":"updateConstituentStatus","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"constituent","isMut":true,"isSigner":false}],"args":[{"name":"newStatus","type":"u8"}]},{"name":"updateConstituentPausedOperations","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"constituent","isMut":true,"isSigner":false}],"args":[{"name":"pausedOperations","type":"u8"}]},{"name":"updateConstituentParams","accounts":[{"name":"lpPool","isMut":false,"isSigner":false},{"name":"constituentTargetBase","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"constituent","isMut":true,"isSigner":false}],"args":[{"name":"constituentParams","type":{"defined":"ConstituentParams"}}]},{"name":"updateLpPoolParams","accounts":[{"name":"lpPool","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"lpPoolParams","type":{"defined":"LpPoolParams"}}]},{"name":"addAmmConstituentMappingData","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"ammConstituentMapping","isMut":true,"isSigner":false},{"name":"constituentTargetBase","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"ammConstituentMappingData","type":{"vec":{"defined":"AddAmmConstituentMappingDatum"}}}]},{"name":"updateAmmConstituentMappingData","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"ammConstituentMapping","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"ammConstituentMappingData","type":{"vec":{"defined":"AddAmmConstituentMappingDatum"}}}]},{"name":"removeAmmConstituentMappingData","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"ammConstituentMapping","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"perpMarketIndex","type":"u16"},{"name":"constituentIndex","type":"u16"}]},{"name":"updateConstituentCorrelationData","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"constituentCorrelations","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"index1","type":"u16"},{"name":"index2","type":"u16"},{"name":"correlation","type":"i64"}]},{"name":"updateLpConstituentTargetBase","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"keeper","isMut":true,"isSigner":true},{"name":"ammConstituentMapping","isMut":false,"isSigner":false},{"name":"constituentTargetBase","isMut":true,"isSigner":false},{"name":"ammCache","isMut":false,"isSigner":false},{"name":"lpPool","isMut":false,"isSigner":false}],"args":[]},{"name":"updateLpPoolAum","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"keeper","isMut":true,"isSigner":true},{"name":"lpPool","isMut":true,"isSigner":false},{"name":"constituentTargetBase","isMut":true,"isSigner":false},{"name":"ammCache","isMut":true,"isSigner":false}],"args":[]},{"name":"updateAmmCache","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"ammCache","isMut":true,"isSigner":false},{"name":"quoteMarket","isMut":false,"isSigner":false}],"args":[]},{"name":"overrideAmmCacheInfo","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"ammCache","isMut":true,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"overrideParams","type":{"defined":"OverrideAmmCacheParams"}}]},{"name":"lpPoolSwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"constituentTargetBase","isMut":false,"isSigner":false},{"name":"constituentCorrelations","isMut":false,"isSigner":false},{"name":"constituentInTokenAccount","isMut":true,"isSigner":false},{"name":"constituentOutTokenAccount","isMut":true,"isSigner":false},{"name":"userInTokenAccount","isMut":true,"isSigner":false},{"name":"userOutTokenAccount","isMut":true,"isSigner":false},{"name":"inConstituent","isMut":true,"isSigner":false},{"name":"outConstituent","isMut":true,"isSigner":false},{"name":"inMarketMint","isMut":false,"isSigner":false},{"name":"outMarketMint","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"outMarketIndex","type":"u16"},{"name":"inAmount","type":"u64"},{"name":"minOutAmount","type":"u64"}]},{"name":"viewLpPoolSwapFees","accounts":[{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"constituentTargetBase","isMut":false,"isSigner":false},{"name":"constituentCorrelations","isMut":false,"isSigner":false},{"name":"constituentInTokenAccount","isMut":true,"isSigner":false},{"name":"constituentOutTokenAccount","isMut":true,"isSigner":false},{"name":"inConstituent","isMut":true,"isSigner":false},{"name":"outConstituent","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"outMarketIndex","type":"u16"},{"name":"inAmount","type":"u64"},{"name":"inTargetWeight","type":"i64"},{"name":"outTargetWeight","type":"i64"}]},{"name":"lpPoolAddLiquidity","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"lpPool","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"inMarketMint","isMut":false,"isSigner":false},{"name":"inConstituent","isMut":true,"isSigner":false},{"name":"userInTokenAccount","isMut":true,"isSigner":false},{"name":"constituentInTokenAccount","isMut":true,"isSigner":false},{"name":"userLpTokenAccount","isMut":true,"isSigner":false},{"name":"lpMint","isMut":true,"isSigner":false},{"name":"constituentTargetBase","isMut":false,"isSigner":false},{"name":"lpPoolTokenVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"inAmount","type":"u128"},{"name":"minMintAmount","type":"u64"}]},{"name":"lpPoolRemoveLiquidity","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"lpPool","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"outMarketMint","isMut":false,"isSigner":false},{"name":"outConstituent","isMut":true,"isSigner":false},{"name":"userOutTokenAccount","isMut":true,"isSigner":false},{"name":"constituentOutTokenAccount","isMut":true,"isSigner":false},{"name":"userLpTokenAccount","isMut":true,"isSigner":false},{"name":"spotMarketTokenAccount","isMut":true,"isSigner":false},{"name":"lpMint","isMut":true,"isSigner":false},{"name":"constituentTargetBase","isMut":false,"isSigner":false},{"name":"lpPoolTokenVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"ammCache","isMut":false,"isSigner":false}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"inAmount","type":"u64"},{"name":"minOutAmount","type":"u128"}]},{"name":"viewLpPoolAddLiquidityFees","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"inMarketMint","isMut":false,"isSigner":false},{"name":"inConstituent","isMut":false,"isSigner":false},{"name":"lpMint","isMut":false,"isSigner":false},{"name":"constituentTargetBase","isMut":false,"isSigner":false}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"inAmount","type":"u128"}]},{"name":"viewLpPoolRemoveLiquidityFees","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"outMarketMint","isMut":false,"isSigner":false},{"name":"outConstituent","isMut":false,"isSigner":false},{"name":"lpMint","isMut":false,"isSigner":false},{"name":"constituentTargetBase","isMut":false,"isSigner":false}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"inAmount","type":"u64"}]},{"name":"beginLpSwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"signerOutTokenAccount","isMut":true,"isSigner":false,"docs":["Signer token accounts"]},{"name":"signerInTokenAccount","isMut":true,"isSigner":false},{"name":"constituentOutTokenAccount","isMut":true,"isSigner":false,"docs":["Constituent token accounts"]},{"name":"constituentInTokenAccount","isMut":true,"isSigner":false},{"name":"outConstituent","isMut":true,"isSigner":false,"docs":["Constituents"]},{"name":"inConstituent","isMut":true,"isSigner":false},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"instructions","isMut":false,"isSigner":false,"docs":["Instructions Sysvar for instruction introspection"]},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"outMarketIndex","type":"u16"},{"name":"amountIn","type":"u64"}]},{"name":"endLpSwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"signerOutTokenAccount","isMut":true,"isSigner":false,"docs":["Signer token accounts"]},{"name":"signerInTokenAccount","isMut":true,"isSigner":false},{"name":"constituentOutTokenAccount","isMut":true,"isSigner":false,"docs":["Constituent token accounts"]},{"name":"constituentInTokenAccount","isMut":true,"isSigner":false},{"name":"outConstituent","isMut":true,"isSigner":false,"docs":["Constituents"]},{"name":"inConstituent","isMut":true,"isSigner":false},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"instructions","isMut":false,"isSigner":false,"docs":["Instructions Sysvar for instruction introspection"]},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"outMarketIndex","type":"u16"}]},{"name":"updateConstituentOracleInfo","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"keeper","isMut":true,"isSigner":true},{"name":"constituent","isMut":true,"isSigner":false},{"name":"spotMarket","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false}],"args":[]},{"name":"depositToProgramVault","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"constituent","isMut":true,"isSigner":false},{"name":"constituentTokenAccount","isMut":true,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdrawFromProgramVault","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"constituent","isMut":true,"isSigner":false},{"name":"constituentTokenAccount","isMut":true,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"settlePerpToLpPool","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"lpPool","isMut":true,"isSigner":false},{"name":"keeper","isMut":true,"isSigner":true},{"name":"ammCache","isMut":true,"isSigner":false},{"name":"quoteMarket","isMut":true,"isSigner":false},{"name":"constituent","isMut":true,"isSigner":false},{"name":"constituentQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"quoteTokenVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"AmmCache","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"padding","type":{"array":["u8",3]}},{"name":"cache","type":{"vec":{"defined":"CacheInfo"}}}]}},{"name":"OpenbookV2FulfillmentConfig","type":{"kind":"struct","fields":[{"name":"pubkey","type":"publicKey"},{"name":"openbookV2ProgramId","type":"publicKey"},{"name":"openbookV2Market","type":"publicKey"},{"name":"openbookV2MarketAuthority","type":"publicKey"},{"name":"openbookV2EventHeap","type":"publicKey"},{"name":"openbookV2Bids","type":"publicKey"},{"name":"openbookV2Asks","type":"publicKey"},{"name":"openbookV2BaseVault","type":"publicKey"},{"name":"openbookV2QuoteVault","type":"publicKey"},{"name":"marketIndex","type":"u16"},{"name":"fulfillmentType","type":{"defined":"SpotFulfillmentType"}},{"name":"status","type":{"defined":"SpotFulfillmentConfigStatus"}},{"name":"padding","type":{"array":["u8",4]}}]}},{"name":"PhoenixV1FulfillmentConfig","type":{"kind":"struct","fields":[{"name":"pubkey","type":"publicKey"},{"name":"phoenixProgramId","type":"publicKey"},{"name":"phoenixLogAuthority","type":"publicKey"},{"name":"phoenixMarket","type":"publicKey"},{"name":"phoenixBaseVault","type":"publicKey"},{"name":"phoenixQuoteVault","type":"publicKey"},{"name":"marketIndex","type":"u16"},{"name":"fulfillmentType","type":{"defined":"SpotFulfillmentType"}},{"name":"status","type":{"defined":"SpotFulfillmentConfigStatus"}},{"name":"padding","type":{"array":["u8",4]}}]}},{"name":"SerumV3FulfillmentConfig","type":{"kind":"struct","fields":[{"name":"pubkey","type":"publicKey"},{"name":"serumProgramId","type":"publicKey"},{"name":"serumMarket","type":"publicKey"},{"name":"serumRequestQueue","type":"publicKey"},{"name":"serumEventQueue","type":"publicKey"},{"name":"serumBids","type":"publicKey"},{"name":"serumAsks","type":"publicKey"},{"name":"serumBaseVault","type":"publicKey"},{"name":"serumQuoteVault","type":"publicKey"},{"name":"serumOpenOrders","type":"publicKey"},{"name":"serumSignerNonce","type":"u64"},{"name":"marketIndex","type":"u16"},{"name":"fulfillmentType","type":{"defined":"SpotFulfillmentType"}},{"name":"status","type":{"defined":"SpotFulfillmentConfigStatus"}},{"name":"padding","type":{"array":["u8",4]}}]}},{"name":"HighLeverageModeConfig","type":{"kind":"struct","fields":[{"name":"maxUsers","type":"u32"},{"name":"currentUsers","type":"u32"},{"name":"reduceOnly","type":"u8"},{"name":"padding1","type":{"array":["u8",3]}},{"name":"currentMaintenanceUsers","type":"u32"},{"name":"padding2","type":{"array":["u8",24]}}]}},{"name":"IfRebalanceConfig","type":{"kind":"struct","fields":[{"name":"pubkey","type":"publicKey"},{"name":"totalInAmount","docs":["total amount to be sold"],"type":"u64"},{"name":"currentInAmount","docs":["amount already sold"],"type":"u64"},{"name":"currentOutAmount","docs":["amount already bought"],"type":"u64"},{"name":"currentOutAmountTransferred","docs":["amount already transferred to revenue pool"],"type":"u64"},{"name":"currentInAmountSinceLastTransfer","docs":["amount already bought in epoch"],"type":"u64"},{"name":"epochStartTs","docs":["start time of epoch"],"type":"i64"},{"name":"epochInAmount","docs":["amount already bought in epoch"],"type":"u64"},{"name":"epochMaxInAmount","docs":["max amount to swap in epoch"],"type":"u64"},{"name":"epochDuration","docs":["duration of epoch"],"type":"i64"},{"name":"outMarketIndex","docs":["market index to sell"],"type":"u16"},{"name":"inMarketIndex","docs":["market index to buy"],"type":"u16"},{"name":"maxSlippageBps","type":"u16"},{"name":"swapMode","type":"u8"},{"name":"status","type":"u8"},{"name":"padding2","type":{"array":["u8",32]}}]}},{"name":"InsuranceFundStake","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"ifShares","type":"u128"},{"name":"lastWithdrawRequestShares","type":"u128"},{"name":"ifBase","type":"u128"},{"name":"lastValidTs","type":"i64"},{"name":"lastWithdrawRequestValue","type":"u64"},{"name":"lastWithdrawRequestTs","type":"i64"},{"name":"costBasis","type":"i64"},{"name":"marketIndex","type":"u16"},{"name":"padding","type":{"array":["u8",14]}}]}},{"name":"ProtocolIfSharesTransferConfig","type":{"kind":"struct","fields":[{"name":"whitelistedSigners","type":{"array":["publicKey",4]}},{"name":"maxTransferPerEpoch","type":"u128"},{"name":"currentEpochTransfer","type":"u128"},{"name":"nextEpochTs","type":"i64"},{"name":"padding","type":{"array":["u128",8]}}]}},{"name":"LPPool","type":{"kind":"struct","fields":[{"name":"pubkey","docs":["address of the vault."],"type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"whitelistMint","type":"publicKey"},{"name":"constituentTargetBase","type":"publicKey"},{"name":"constituentCorrelations","type":"publicKey"},{"name":"maxAum","docs":["The current number of VaultConstituents in the vault, each constituent is pda(LPPool.address, constituent_index)","which constituent is the quote, receives revenue pool distributions. (maybe this should just be implied idx 0)","pub quote_constituent_index: u16,","QUOTE_PRECISION: Max AUM, Prohibit minting new DLP beyond this"],"type":"u128"},{"name":"lastAum","docs":["QUOTE_PRECISION: AUM of the vault in USD, updated lazily"],"type":"u128"},{"name":"cumulativeQuoteSentToPerpMarkets","docs":["QUOTE PRECISION: Cumulative quotes from settles"],"type":"u128"},{"name":"cumulativeQuoteReceivedFromPerpMarkets","type":"u128"},{"name":"totalMintRedeemFeesPaid","docs":["QUOTE_PRECISION: Total fees paid for minting and redeeming LP tokens"],"type":"i128"},{"name":"lastAumSlot","docs":["timestamp of last AUM slot"],"type":"u64"},{"name":"maxSettleQuoteAmount","type":"u64"},{"name":"padding","docs":["timestamp of last vAMM revenue rebalance"],"type":"u64"},{"name":"mintRedeemId","docs":["Every mint/redeem has a monotonically increasing id. This is the next id to use"],"type":"u64"},{"name":"settleId","type":"u64"},{"name":"minMintFee","docs":["PERCENTAGE_PRECISION"],"type":"i64"},{"name":"tokenSupply","type":"u64"},{"name":"volatility","type":"u64"},{"name":"constituents","type":"u16"},{"name":"quoteConsituentIndex","type":"u16"},{"name":"bump","type":"u8"},{"name":"gammaExecution","type":"u8"},{"name":"xi","type":"u8"},{"name":"targetOracleDelayFeeBpsPer10Slots","type":"u8"},{"name":"targetPositionDelayFeeBpsPer10Slots","type":"u8"},{"name":"lpPoolId","type":"u8"},{"name":"padding","type":{"array":["u8",174]}}]}},{"name":"Constituent","type":{"kind":"struct","fields":[{"name":"pubkey","docs":["address of the constituent"],"type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"lpPool","type":"publicKey"},{"name":"vault","type":"publicKey"},{"name":"totalSwapFees","docs":["total fees received by the constituent. Positive = fees received, Negative = fees paid"],"type":"i128"},{"name":"spotBalance","docs":["spot borrow-lend balance for constituent"],"type":{"defined":"ConstituentSpotBalance"}},{"name":"lastSpotBalanceTokenAmount","type":"i64"},{"name":"cumulativeSpotInterestAccruedTokenAmount","type":"i64"},{"name":"maxWeightDeviation","docs":["max deviation from target_weight allowed for the constituent","precision: PERCENTAGE_PRECISION"],"type":"i64"},{"name":"swapFeeMin","docs":["min fee charged on swaps to/from this constituent","precision: PERCENTAGE_PRECISION"],"type":"i64"},{"name":"swapFeeMax","docs":["max fee charged on swaps to/from this constituent","precision: PERCENTAGE_PRECISION"],"type":"i64"},{"name":"maxBorrowTokenAmount","docs":["Max Borrow amount:","precision: token precision"],"type":"u64"},{"name":"vaultTokenBalance","docs":["ata token balance in token precision"],"type":"u64"},{"name":"lastOraclePrice","type":"i64"},{"name":"lastOracleSlot","type":"u64"},{"name":"oracleStalenessThreshold","docs":["Delay allowed for valid AUM calculation"],"type":"u64"},{"name":"flashLoanInitialTokenAmount","type":"u64"},{"name":"nextSwapId","docs":["Every swap to/from this constituent has a monotonically increasing id. This is the next id to use"],"type":"u64"},{"name":"derivativeWeight","docs":["percentable of derivatve weight to go to this specific derivative PERCENTAGE_PRECISION. Zero if no derivative weight"],"type":"u64"},{"name":"volatility","type":"u64"},{"name":"constituentDerivativeDepegThreshold","type":"u64"},{"name":"constituentDerivativeIndex","docs":["The `constituent_index` of the parent constituent. -1 if it is a parent index","Example: if in a pool with SOL (parent) and dSOL (derivative),","SOL.constituent_index = 1, SOL.constituent_derivative_index = -1,","dSOL.constituent_index = 2, dSOL.constituent_derivative_index = 1"],"type":"i16"},{"name":"spotMarketIndex","type":"u16"},{"name":"constituentIndex","type":"u16"},{"name":"decimals","type":"u8"},{"name":"bump","type":"u8"},{"name":"vaultBump","type":"u8"},{"name":"gammaInventory","type":"u8"},{"name":"gammaExecution","type":"u8"},{"name":"xi","type":"u8"},{"name":"status","type":"u8"},{"name":"pausedOperations","type":"u8"},{"name":"padding","type":{"array":["u8",162]}}]}},{"name":"AmmConstituentMapping","type":{"kind":"struct","fields":[{"name":"lpPool","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"padding","type":{"array":["u8",3]}},{"name":"weights","type":{"vec":{"defined":"AmmConstituentDatum"}}}]}},{"name":"ConstituentTargetBase","type":{"kind":"struct","fields":[{"name":"lpPool","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"padding","type":{"array":["u8",3]}},{"name":"targets","type":{"vec":{"defined":"TargetsDatum"}}}]}},{"name":"ConstituentCorrelations","type":{"kind":"struct","fields":[{"name":"lpPool","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"padding","type":{"array":["u8",3]}},{"name":"correlations","type":{"vec":"i64"}}]}},{"name":"PrelaunchOracle","type":{"kind":"struct","fields":[{"name":"price","type":"i64"},{"name":"maxPrice","type":"i64"},{"name":"confidence","type":"u64"},{"name":"lastUpdateSlot","type":"u64"},{"name":"ammLastUpdateSlot","type":"u64"},{"name":"perpMarketIndex","type":"u16"},{"name":"padding","type":{"array":["u8",70]}}]}},{"name":"PerpMarket","type":{"kind":"struct","fields":[{"name":"pubkey","docs":["The perp market's address. It is a pda of the market index"],"type":"publicKey"},{"name":"amm","docs":["The automated market maker"],"type":{"defined":"AMM"}},{"name":"pnlPool","docs":["The market's pnl pool. When users settle negative pnl, the balance increases.","When users settle positive pnl, the balance decreases. Can not go negative."],"type":{"defined":"PoolBalance"}},{"name":"name","docs":["Encoded display name for the perp market e.g. SOL-PERP"],"type":{"array":["u8",32]}},{"name":"insuranceClaim","docs":["The perp market's claim on the insurance fund"],"type":{"defined":"InsuranceClaim"}},{"name":"unrealizedPnlMaxImbalance","docs":["The max pnl imbalance before positive pnl asset weight is discounted","pnl imbalance is the difference between long and short pnl. When it's greater than 0,","the amm has negative pnl and the initial asset weight for positive pnl is discounted","precision = QUOTE_PRECISION"],"type":"u64"},{"name":"expiryTs","docs":["The ts when the market will be expired. Only set if market is in reduce only mode"],"type":"i64"},{"name":"expiryPrice","docs":["The price at which positions will be settled. Only set if market is expired","precision = PRICE_PRECISION"],"type":"i64"},{"name":"nextFillRecordId","docs":["Every trade has a fill record id. This is the next id to be used"],"type":"u64"},{"name":"nextFundingRateRecordId","docs":["Every funding rate update has a record id. This is the next id to be used"],"type":"u64"},{"name":"nextCurveRecordId","docs":["Every amm k updated has a record id. This is the next id to be used"],"type":"u64"},{"name":"imfFactor","docs":["The initial margin fraction factor. Used to increase margin ratio for large positions","precision: MARGIN_PRECISION"],"type":"u32"},{"name":"unrealizedPnlImfFactor","docs":["The imf factor for unrealized pnl. Used to discount asset weight for large positive pnl","precision: MARGIN_PRECISION"],"type":"u32"},{"name":"liquidatorFee","docs":["The fee the liquidator is paid for taking over perp position","precision: LIQUIDATOR_FEE_PRECISION"],"type":"u32"},{"name":"ifLiquidationFee","docs":["The fee the insurance fund receives from liquidation","precision: LIQUIDATOR_FEE_PRECISION"],"type":"u32"},{"name":"marginRatioInitial","docs":["The margin ratio which determines how much collateral is required to open a position","e.g. margin ratio of .1 means a user must have $100 of total collateral to open a $1000 position","precision: MARGIN_PRECISION"],"type":"u32"},{"name":"marginRatioMaintenance","docs":["The margin ratio which determines when a user will be liquidated","e.g. margin ratio of .05 means a user must have $50 of total collateral to maintain a $1000 position","else they will be liquidated","precision: MARGIN_PRECISION"],"type":"u32"},{"name":"unrealizedPnlInitialAssetWeight","docs":["The initial asset weight for positive pnl. Negative pnl always has an asset weight of 1","precision: SPOT_WEIGHT_PRECISION"],"type":"u32"},{"name":"unrealizedPnlMaintenanceAssetWeight","docs":["The maintenance asset weight for positive pnl. Negative pnl always has an asset weight of 1","precision: SPOT_WEIGHT_PRECISION"],"type":"u32"},{"name":"numberOfUsersWithBase","docs":["number of users in a position (base)"],"type":"u32"},{"name":"numberOfUsers","docs":["number of users in a position (pnl) or pnl (quote)"],"type":"u32"},{"name":"marketIndex","type":"u16"},{"name":"status","docs":["Whether a market is active, reduce only, expired, etc","Affects whether users can open/close positions"],"type":{"defined":"MarketStatus"}},{"name":"contractType","docs":["Currently only Perpetual markets are supported"],"type":{"defined":"ContractType"}},{"name":"contractTier","docs":["The contract tier determines how much insurance a market can receive, with more speculative markets receiving less insurance","It also influences the order perp markets can be liquidated, with less speculative markets being liquidated first"],"type":{"defined":"ContractTier"}},{"name":"pausedOperations","type":"u8"},{"name":"quoteSpotMarketIndex","docs":["The spot market that pnl is settled in"],"type":"u16"},{"name":"feeAdjustment","docs":["Between -100 and 100, represents what % to increase/decrease the fee by","E.g. if this is -50 and the fee is 5bps, the new fee will be 2.5bps","if this is 50 and the fee is 5bps, the new fee will be 7.5bps"],"type":"i16"},{"name":"fuelBoostPosition","docs":["fuel multiplier for perp funding","precision: 10"],"type":"u8"},{"name":"fuelBoostTaker","docs":["fuel multiplier for perp taker","precision: 10"],"type":"u8"},{"name":"fuelBoostMaker","docs":["fuel multiplier for perp maker","precision: 10"],"type":"u8"},{"name":"poolId","type":"u8"},{"name":"highLeverageMarginRatioInitial","type":"u16"},{"name":"highLeverageMarginRatioMaintenance","type":"u16"},{"name":"protectedMakerLimitPriceDivisor","type":"u8"},{"name":"protectedMakerDynamicDivisor","type":"u8"},{"name":"lpFeeTransferScalar","type":"u8"},{"name":"lpStatus","type":"u8"},{"name":"lpPausedOperations","type":"u8"},{"name":"lpExchangeFeeExcluscionScalar","type":"u8"},{"name":"lastFillPrice","type":"u64"},{"name":"lpPoolId","type":"u8"},{"name":"padding","type":{"array":["u8",23]}}]}},{"name":"ProtectedMakerModeConfig","type":{"kind":"struct","fields":[{"name":"maxUsers","type":"u32"},{"name":"currentUsers","type":"u32"},{"name":"reduceOnly","type":"u8"},{"name":"padding","type":{"array":["u8",31]}}]}},{"name":"PythLazerOracle","type":{"kind":"struct","fields":[{"name":"price","type":"i64"},{"name":"publishTime","type":"u64"},{"name":"postedSlot","type":"u64"},{"name":"exponent","type":"i32"},{"name":"padding","type":{"array":["u8",4]}},{"name":"conf","type":"u64"}]}},{"name":"RevenueShare","type":{"kind":"struct","fields":[{"name":"authority","docs":["the owner of this account, a builder or referrer"],"type":"publicKey"},{"name":"totalReferrerRewards","type":"u64"},{"name":"totalBuilderRewards","type":"u64"},{"name":"padding","type":{"array":["u8",18]}}]}},{"name":"RevenueShareEscrow","type":{"kind":"struct","fields":[{"name":"authority","docs":["the owner of this account, a user"],"type":"publicKey"},{"name":"referrer","type":"publicKey"},{"name":"referrerBoostExpireTs","type":"u32"},{"name":"referrerRewardOffset","type":"i8"},{"name":"refereeFeeNumeratorOffset","type":"i8"},{"name":"referrerBoostNumerator","type":"i8"},{"name":"reservedFixed","type":{"array":["u8",17]}},{"name":"padding0","type":"u32"},{"name":"orders","type":{"vec":{"defined":"RevenueShareOrder"}}},{"name":"padding1","type":"u32"},{"name":"approvedBuilders","type":{"vec":{"defined":"BuilderInfo"}}}]}},{"name":"SignedMsgUserOrders","docs":["* This struct is a duplicate of SignedMsgUserOrdersZeroCopy\n * It is used to give anchor an struct to generate the idl for clients\n * The struct SignedMsgUserOrdersZeroCopy is used to load the data in efficiently"],"type":{"kind":"struct","fields":[{"name":"authorityPubkey","type":"publicKey"},{"name":"padding","type":"u32"},{"name":"signedMsgOrderData","type":{"vec":{"defined":"SignedMsgOrderId"}}}]}},{"name":"SignedMsgWsDelegates","docs":["* Used to store authenticated delegates for swift-like ws connections"],"type":{"kind":"struct","fields":[{"name":"delegates","type":{"vec":"publicKey"}}]}},{"name":"SpotMarket","type":{"kind":"struct","fields":[{"name":"pubkey","docs":["The address of the spot market. It is a pda of the market index"],"type":"publicKey"},{"name":"oracle","docs":["The oracle used to price the markets deposits/borrows"],"type":"publicKey"},{"name":"mint","docs":["The token mint of the market"],"type":"publicKey"},{"name":"vault","docs":["The vault used to store the market's deposits","The amount in the vault should be equal to or greater than deposits - borrows"],"type":"publicKey"},{"name":"name","docs":["The encoded display name for the market e.g. SOL"],"type":{"array":["u8",32]}},{"name":"historicalOracleData","type":{"defined":"HistoricalOracleData"}},{"name":"historicalIndexData","type":{"defined":"HistoricalIndexData"}},{"name":"revenuePool","docs":["Revenue the protocol has collected in this markets token","e.g. for SOL-PERP, funds can be settled in usdc and will flow into the USDC revenue pool"],"type":{"defined":"PoolBalance"}},{"name":"spotFeePool","docs":["The fees collected from swaps between this market and the quote market","Is settled to the quote markets revenue pool"],"type":{"defined":"PoolBalance"}},{"name":"insuranceFund","docs":["Details on the insurance fund covering bankruptcies in this markets token","Covers bankruptcies for borrows with this markets token and perps settling in this markets token"],"type":{"defined":"InsuranceFund"}},{"name":"totalSpotFee","docs":["The total spot fees collected for this market","precision: QUOTE_PRECISION"],"type":"u128"},{"name":"depositBalance","docs":["The sum of the scaled balances for deposits across users and pool balances","To convert to the deposit token amount, multiply by the cumulative deposit interest","precision: SPOT_BALANCE_PRECISION"],"type":"u128"},{"name":"borrowBalance","docs":["The sum of the scaled balances for borrows across users and pool balances","To convert to the borrow token amount, multiply by the cumulative borrow interest","precision: SPOT_BALANCE_PRECISION"],"type":"u128"},{"name":"cumulativeDepositInterest","docs":["The cumulative interest earned by depositors","Used to calculate the deposit token amount from the deposit balance","precision: SPOT_CUMULATIVE_INTEREST_PRECISION"],"type":"u128"},{"name":"cumulativeBorrowInterest","docs":["The cumulative interest earned by borrowers","Used to calculate the borrow token amount from the borrow balance","precision: SPOT_CUMULATIVE_INTEREST_PRECISION"],"type":"u128"},{"name":"totalSocialLoss","docs":["The total socialized loss from borrows, in the mint's token","precision: token mint precision"],"type":"u128"},{"name":"totalQuoteSocialLoss","docs":["The total socialized loss from borrows, in the quote market's token","preicision: QUOTE_PRECISION"],"type":"u128"},{"name":"withdrawGuardThreshold","docs":["no withdraw limits/guards when deposits below this threshold","precision: token mint precision"],"type":"u64"},{"name":"maxTokenDeposits","docs":["The max amount of token deposits in this market","0 if there is no limit","precision: token mint precision"],"type":"u64"},{"name":"depositTokenTwap","docs":["24hr average of deposit token amount","precision: token mint precision"],"type":"u64"},{"name":"borrowTokenTwap","docs":["24hr average of borrow token amount","precision: token mint precision"],"type":"u64"},{"name":"utilizationTwap","docs":["24hr average of utilization","which is borrow amount over token amount","precision: SPOT_UTILIZATION_PRECISION"],"type":"u64"},{"name":"lastInterestTs","docs":["Last time the cumulative deposit and borrow interest was updated"],"type":"u64"},{"name":"lastTwapTs","docs":["Last time the deposit/borrow/utilization averages were updated"],"type":"u64"},{"name":"expiryTs","docs":["The time the market is set to expire. Only set if market is in reduce only mode"],"type":"i64"},{"name":"orderStepSize","docs":["Spot orders must be a multiple of the step size","precision: token mint precision"],"type":"u64"},{"name":"orderTickSize","docs":["Spot orders must be a multiple of the tick size","precision: PRICE_PRECISION"],"type":"u64"},{"name":"minOrderSize","docs":["The minimum order size","precision: token mint precision"],"type":"u64"},{"name":"maxPositionSize","docs":["The maximum spot position size","if the limit is 0, there is no limit","precision: token mint precision"],"type":"u64"},{"name":"nextFillRecordId","docs":["Every spot trade has a fill record id. This is the next id to use"],"type":"u64"},{"name":"nextDepositRecordId","docs":["Every deposit has a deposit record id. This is the next id to use"],"type":"u64"},{"name":"initialAssetWeight","docs":["The initial asset weight used to calculate a deposits contribution to a users initial total collateral","e.g. if the asset weight is .8, $100 of deposits contributes $80 to the users initial total collateral","precision: SPOT_WEIGHT_PRECISION"],"type":"u32"},{"name":"maintenanceAssetWeight","docs":["The maintenance asset weight used to calculate a deposits contribution to a users maintenance total collateral","e.g. if the asset weight is .9, $100 of deposits contributes $90 to the users maintenance total collateral","precision: SPOT_WEIGHT_PRECISION"],"type":"u32"},{"name":"initialLiabilityWeight","docs":["The initial liability weight used to calculate a borrows contribution to a users initial margin requirement","e.g. if the liability weight is .9, $100 of borrows contributes $90 to the users initial margin requirement","precision: SPOT_WEIGHT_PRECISION"],"type":"u32"},{"name":"maintenanceLiabilityWeight","docs":["The maintenance liability weight used to calculate a borrows contribution to a users maintenance margin requirement","e.g. if the liability weight is .8, $100 of borrows contributes $80 to the users maintenance margin requirement","precision: SPOT_WEIGHT_PRECISION"],"type":"u32"},{"name":"imfFactor","docs":["The initial margin fraction factor. Used to increase liability weight/decrease asset weight for large positions","precision: MARGIN_PRECISION"],"type":"u32"},{"name":"liquidatorFee","docs":["The fee the liquidator is paid for taking over borrow/deposit","precision: LIQUIDATOR_FEE_PRECISION"],"type":"u32"},{"name":"ifLiquidationFee","docs":["The fee the insurance fund receives from liquidation","precision: LIQUIDATOR_FEE_PRECISION"],"type":"u32"},{"name":"optimalUtilization","docs":["The optimal utilization rate for this market.","Used to determine the markets borrow rate","precision: SPOT_UTILIZATION_PRECISION"],"type":"u32"},{"name":"optimalBorrowRate","docs":["The borrow rate for this market when the market has optimal utilization","precision: SPOT_RATE_PRECISION"],"type":"u32"},{"name":"maxBorrowRate","docs":["The borrow rate for this market when the market has 1000 utilization","precision: SPOT_RATE_PRECISION"],"type":"u32"},{"name":"decimals","docs":["The market's token mint's decimals. To from decimals to a precision, 10^decimals"],"type":"u32"},{"name":"marketIndex","type":"u16"},{"name":"ordersEnabled","docs":["Whether or not spot trading is enabled"],"type":"bool"},{"name":"oracleSource","type":{"defined":"OracleSource"}},{"name":"status","type":{"defined":"MarketStatus"}},{"name":"assetTier","docs":["The asset tier affects how a deposit can be used as collateral and the priority for a borrow being liquidated"],"type":{"defined":"AssetTier"}},{"name":"pausedOperations","type":"u8"},{"name":"ifPausedOperations","type":"u8"},{"name":"feeAdjustment","type":"i16"},{"name":"maxTokenBorrowsFraction","docs":["What fraction of max_token_deposits","disabled when 0, 1 => 1/10000 => .01% of max_token_deposits","precision: X/10000"],"type":"u16"},{"name":"flashLoanAmount","docs":["For swaps, the amount of token loaned out in the begin_swap ix","precision: token mint precision"],"type":"u64"},{"name":"flashLoanInitialTokenAmount","docs":["For swaps, the amount in the users token account in the begin_swap ix","Used to calculate how much of the token left the system in end_swap ix","precision: token mint precision"],"type":"u64"},{"name":"totalSwapFee","docs":["The total fees received from swaps","precision: token mint precision"],"type":"u64"},{"name":"scaleInitialAssetWeightStart","docs":["When to begin scaling down the initial asset weight","disabled when 0","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"minBorrowRate","docs":["The min borrow rate for this market when the market regardless of utilization","1 => 1/200 => .5%","precision: X/200"],"type":"u8"},{"name":"fuelBoostDeposits","docs":["fuel multiplier for spot deposits","precision: 10"],"type":"u8"},{"name":"fuelBoostBorrows","docs":["fuel multiplier for spot borrows","precision: 10"],"type":"u8"},{"name":"fuelBoostTaker","docs":["fuel multiplier for spot taker","precision: 10"],"type":"u8"},{"name":"fuelBoostMaker","docs":["fuel multiplier for spot maker","precision: 10"],"type":"u8"},{"name":"fuelBoostInsurance","docs":["fuel multiplier for spot insurance stake","precision: 10"],"type":"u8"},{"name":"tokenProgramFlag","type":"u8"},{"name":"poolId","type":"u8"},{"name":"padding","type":{"array":["u8",40]}}]}},{"name":"State","type":{"kind":"struct","fields":[{"name":"admin","type":"publicKey"},{"name":"whitelistMint","type":"publicKey"},{"name":"discountMint","type":"publicKey"},{"name":"signer","type":"publicKey"},{"name":"srmVault","type":"publicKey"},{"name":"perpFeeStructure","type":{"defined":"FeeStructure"}},{"name":"spotFeeStructure","type":{"defined":"FeeStructure"}},{"name":"oracleGuardRails","type":{"defined":"OracleGuardRails"}},{"name":"numberOfAuthorities","type":"u64"},{"name":"numberOfSubAccounts","type":"u64"},{"name":"lpCooldownTime","type":"u64"},{"name":"liquidationMarginBufferRatio","type":"u32"},{"name":"settlementDuration","type":"u16"},{"name":"numberOfMarkets","type":"u16"},{"name":"numberOfSpotMarkets","type":"u16"},{"name":"signerNonce","type":"u8"},{"name":"minPerpAuctionDuration","type":"u8"},{"name":"defaultMarketOrderTimeInForce","type":"u8"},{"name":"defaultSpotAuctionDuration","type":"u8"},{"name":"exchangeStatus","type":"u8"},{"name":"liquidationDuration","type":"u8"},{"name":"initialPctToLiquidate","type":"u16"},{"name":"maxNumberOfSubAccounts","type":"u16"},{"name":"maxInitializeUserFee","type":"u16"},{"name":"featureBitFlags","type":"u8"},{"name":"lpPoolFeatureBitFlags","type":"u8"},{"name":"padding","type":{"array":["u8",8]}}]}},{"name":"User","type":{"kind":"struct","fields":[{"name":"authority","docs":["The owner/authority of the account"],"type":"publicKey"},{"name":"delegate","docs":["An addresses that can control the account on the authority's behalf. Has limited power, cant withdraw"],"type":"publicKey"},{"name":"name","docs":["Encoded display name e.g. \"toly\""],"type":{"array":["u8",32]}},{"name":"spotPositions","docs":["The user's spot positions"],"type":{"array":[{"defined":"SpotPosition"},8]}},{"name":"perpPositions","docs":["The user's perp positions"],"type":{"array":[{"defined":"PerpPosition"},8]}},{"name":"orders","docs":["The user's orders"],"type":{"array":[{"defined":"Order"},32]}},{"name":"lastAddPerpLpSharesTs","docs":["The last time the user added perp lp positions"],"type":"i64"},{"name":"totalDeposits","docs":["The total values of deposits the user has made","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"totalWithdraws","docs":["The total values of withdrawals the user has made","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"totalSocialLoss","docs":["The total socialized loss the users has incurred upon the protocol","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"settledPerpPnl","docs":["Fees (taker fees, maker rebate, referrer reward, filler reward) and pnl for perps","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"cumulativeSpotFees","docs":["Fees (taker fees, maker rebate, filler reward) for spot","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"cumulativePerpFunding","docs":["Cumulative funding paid/received for perps","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"liquidationMarginFreed","docs":["The amount of margin freed during liquidation. Used to force the liquidation to occur over a period of time","Defaults to zero when not being liquidated","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"lastActiveSlot","docs":["The last slot a user was active. Used to determine if a user is idle"],"type":"u64"},{"name":"nextOrderId","docs":["Every user order has an order id. This is the next order id to be used"],"type":"u32"},{"name":"maxMarginRatio","docs":["Custom max initial margin ratio for the user"],"type":"u32"},{"name":"nextLiquidationId","docs":["The next liquidation id to be used for user"],"type":"u16"},{"name":"subAccountId","docs":["The sub account id for this user"],"type":"u16"},{"name":"status","docs":["Whether the user is active, being liquidated or bankrupt"],"type":"u8"},{"name":"isMarginTradingEnabled","docs":["Whether the user has enabled margin trading"],"type":"bool"},{"name":"idle","docs":["User is idle if they haven't interacted with the protocol in 1 week and they have no orders, perp positions or borrows","Off-chain keeper bots can ignore users that are idle"],"type":"bool"},{"name":"openOrders","docs":["number of open orders"],"type":"u8"},{"name":"hasOpenOrder","docs":["Whether or not user has open order"],"type":"bool"},{"name":"openAuctions","docs":["number of open orders with auction"],"type":"u8"},{"name":"hasOpenAuction","docs":["Whether or not user has open order with auction"],"type":"bool"},{"name":"marginMode","type":{"defined":"MarginMode"}},{"name":"poolId","type":"u8"},{"name":"padding1","type":{"array":["u8",3]}},{"name":"lastFuelBonusUpdateTs","type":"u32"},{"name":"padding","type":{"array":["u8",12]}}]}},{"name":"UserStats","type":{"kind":"struct","fields":[{"name":"authority","docs":["The authority for all of a users sub accounts"],"type":"publicKey"},{"name":"referrer","docs":["The address that referred this user"],"type":"publicKey"},{"name":"fees","docs":["Stats on the fees paid by the user"],"type":{"defined":"UserFees"}},{"name":"nextEpochTs","docs":["The timestamp of the next epoch","Epoch is used to limit referrer rewards earned in single epoch"],"type":"i64"},{"name":"makerVolume30d","docs":["Rolling 30day maker volume for user","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"takerVolume30d","docs":["Rolling 30day taker volume for user","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"fillerVolume30d","docs":["Rolling 30day filler volume for user","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"lastMakerVolume30dTs","docs":["last time the maker volume was updated"],"type":"i64"},{"name":"lastTakerVolume30dTs","docs":["last time the taker volume was updated"],"type":"i64"},{"name":"lastFillerVolume30dTs","docs":["last time the filler volume was updated"],"type":"i64"},{"name":"ifStakedQuoteAssetAmount","docs":["The amount of tokens staked in the quote spot markets if"],"type":"u64"},{"name":"numberOfSubAccounts","docs":["The current number of sub accounts"],"type":"u16"},{"name":"numberOfSubAccountsCreated","docs":["The number of sub accounts created. Can be greater than the number of sub accounts if user","has deleted sub accounts"],"type":"u16"},{"name":"referrerStatus","docs":["Flags for referrer status:","First bit (LSB): 1 if user is a referrer, 0 otherwise","Second bit: 1 if user was referred, 0 otherwise"],"type":"u8"},{"name":"disableUpdatePerpBidAskTwap","type":"bool"},{"name":"pausedOperations","type":"u8"},{"name":"fuelOverflowStatus","docs":["whether the user has a FuelOverflow account"],"type":"u8"},{"name":"fuelInsurance","docs":["accumulated fuel for token amounts of insurance"],"type":"u32"},{"name":"fuelDeposits","docs":["accumulated fuel for notional of deposits"],"type":"u32"},{"name":"fuelBorrows","docs":["accumulate fuel bonus for notional of borrows"],"type":"u32"},{"name":"fuelPositions","docs":["accumulated fuel for perp open interest"],"type":"u32"},{"name":"fuelTaker","docs":["accumulate fuel bonus for taker volume"],"type":"u32"},{"name":"fuelMaker","docs":["accumulate fuel bonus for maker volume"],"type":"u32"},{"name":"ifStakedGovTokenAmount","docs":["The amount of tokens staked in the governance spot markets if"],"type":"u64"},{"name":"lastFuelIfBonusUpdateTs","docs":["last unix ts user stats data was used to update if fuel (u32 to save space)"],"type":"u32"},{"name":"padding","type":{"array":["u8",12]}}]}},{"name":"ReferrerName","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"user","type":"publicKey"},{"name":"userStats","type":"publicKey"},{"name":"name","type":{"array":["u8",32]}}]}},{"name":"FuelOverflow","type":{"kind":"struct","fields":[{"name":"authority","docs":["The authority of this overflow account"],"type":"publicKey"},{"name":"fuelInsurance","type":"u128"},{"name":"fuelDeposits","type":"u128"},{"name":"fuelBorrows","type":"u128"},{"name":"fuelPositions","type":"u128"},{"name":"fuelTaker","type":"u128"},{"name":"fuelMaker","type":"u128"},{"name":"lastFuelSweepTs","type":"u32"},{"name":"lastResetTs","type":"u32"},{"name":"padding","type":{"array":["u128",6]}}]}}],"types":[{"name":"UpdatePerpMarketSummaryStatsParams","type":{"kind":"struct","fields":[{"name":"quoteAssetAmountWithUnsettledLp","type":{"option":"i64"}},{"name":"netUnsettledFundingPnl","type":{"option":"i64"}},{"name":"updateAmmSummaryStats","type":{"option":"bool"}},{"name":"excludeTotalLiqFee","type":{"option":"bool"}}]}},{"name":"ConstituentParams","type":{"kind":"struct","fields":[{"name":"maxWeightDeviation","type":{"option":"i64"}},{"name":"swapFeeMin","type":{"option":"i64"}},{"name":"swapFeeMax","type":{"option":"i64"}},{"name":"maxBorrowTokenAmount","type":{"option":"u64"}},{"name":"oracleStalenessThreshold","type":{"option":"u64"}},{"name":"costToTradeBps","type":{"option":"i32"}},{"name":"constituentDerivativeIndex","type":{"option":"i16"}},{"name":"derivativeWeight","type":{"option":"u64"}},{"name":"volatility","type":{"option":"u64"}},{"name":"gammaExecution","type":{"option":"u8"}},{"name":"gammaInventory","type":{"option":"u8"}},{"name":"xi","type":{"option":"u8"}}]}},{"name":"LpPoolParams","type":{"kind":"struct","fields":[{"name":"maxSettleQuoteAmount","type":{"option":"u64"}},{"name":"volatility","type":{"option":"u64"}},{"name":"gammaExecution","type":{"option":"u8"}},{"name":"xi","type":{"option":"u8"}},{"name":"maxAum","type":{"option":"u128"}},{"name":"whitelistMint","type":{"option":"publicKey"}}]}},{"name":"OverrideAmmCacheParams","type":{"kind":"struct","fields":[{"name":"quoteOwedFromLpPool","type":{"option":"i64"}},{"name":"lastSettleSlot","type":{"option":"u64"}},{"name":"lastFeePoolTokenAmount","type":{"option":"u128"}},{"name":"lastNetPnlPoolTokenAmount","type":{"option":"i128"}},{"name":"ammPositionScalar","type":{"option":"u8"}},{"name":"ammInventoryLimit","type":{"option":"i64"}}]}},{"name":"AddAmmConstituentMappingDatum","type":{"kind":"struct","fields":[{"name":"constituentIndex","type":"u16"},{"name":"perpMarketIndex","type":"u16"},{"name":"weight","type":"i64"}]}},{"name":"CacheInfo","type":{"kind":"struct","fields":[{"name":"oracle","type":"publicKey"},{"name":"lastFeePoolTokenAmount","type":"u128"},{"name":"lastNetPnlPoolTokenAmount","type":"i128"},{"name":"lastExchangeFees","type":"u128"},{"name":"lastSettleAmmExFees","type":"u128"},{"name":"lastSettleAmmPnl","type":"i128"},{"name":"position","docs":["BASE PRECISION"],"type":"i64"},{"name":"slot","type":"u64"},{"name":"lastSettleAmount","type":"u64"},{"name":"lastSettleSlot","type":"u64"},{"name":"lastSettleTs","type":"i64"},{"name":"quoteOwedFromLpPool","type":"i64"},{"name":"ammInventoryLimit","type":"i64"},{"name":"oraclePrice","type":"i64"},{"name":"oracleSlot","type":"u64"},{"name":"marketIndex","type":"u16"},{"name":"oracleSource","type":"u8"},{"name":"oracleValidity","type":"u8"},{"name":"lpStatusForPerpMarket","type":"u8"},{"name":"ammPositionScalar","type":"u8"},{"name":"padding","type":{"array":["u8",34]}}]}},{"name":"AmmCacheFixed","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"pad","type":{"array":["u8",3]}},{"name":"len","type":"u32"}]}},{"name":"LiquidatePerpRecord","type":{"kind":"struct","fields":[{"name":"marketIndex","type":"u16"},{"name":"oraclePrice","type":"i64"},{"name":"baseAssetAmount","type":"i64"},{"name":"quoteAssetAmount","type":"i64"},{"name":"lpShares","docs":["precision: AMM_RESERVE_PRECISION"],"type":"u64"},{"name":"fillRecordId","type":"u64"},{"name":"userOrderId","type":"u32"},{"name":"liquidatorOrderId","type":"u32"},{"name":"liquidatorFee","docs":["precision: QUOTE_PRECISION"],"type":"u64"},{"name":"ifFee","docs":["precision: QUOTE_PRECISION"],"type":"u64"}]}},{"name":"LiquidateSpotRecord","type":{"kind":"struct","fields":[{"name":"assetMarketIndex","type":"u16"},{"name":"assetPrice","type":"i64"},{"name":"assetTransfer","type":"u128"},{"name":"liabilityMarketIndex","type":"u16"},{"name":"liabilityPrice","type":"i64"},{"name":"liabilityTransfer","docs":["precision: token mint precision"],"type":"u128"},{"name":"ifFee","docs":["precision: token mint precision"],"type":"u64"}]}},{"name":"LiquidateBorrowForPerpPnlRecord","type":{"kind":"struct","fields":[{"name":"perpMarketIndex","type":"u16"},{"name":"marketOraclePrice","type":"i64"},{"name":"pnlTransfer","type":"u128"},{"name":"liabilityMarketIndex","type":"u16"},{"name":"liabilityPrice","type":"i64"},{"name":"liabilityTransfer","type":"u128"}]}},{"name":"LiquidatePerpPnlForDepositRecord","type":{"kind":"struct","fields":[{"name":"perpMarketIndex","type":"u16"},{"name":"marketOraclePrice","type":"i64"},{"name":"pnlTransfer","type":"u128"},{"name":"assetMarketIndex","type":"u16"},{"name":"assetPrice","type":"i64"},{"name":"assetTransfer","type":"u128"}]}},{"name":"PerpBankruptcyRecord","type":{"kind":"struct","fields":[{"name":"marketIndex","type":"u16"},{"name":"pnl","type":"i128"},{"name":"ifPayment","type":"u128"},{"name":"clawbackUser","type":{"option":"publicKey"}},{"name":"clawbackUserPayment","type":{"option":"u128"}},{"name":"cumulativeFundingRateDelta","type":"i128"}]}},{"name":"SpotBankruptcyRecord","type":{"kind":"struct","fields":[{"name":"marketIndex","type":"u16"},{"name":"borrowAmount","type":"u128"},{"name":"ifPayment","type":"u128"},{"name":"cumulativeDepositInterestDelta","type":"u128"}]}},{"name":"IfRebalanceConfigParams","type":{"kind":"struct","fields":[{"name":"totalInAmount","type":"u64"},{"name":"epochMaxInAmount","type":"u64"},{"name":"epochDuration","type":"i64"},{"name":"outMarketIndex","type":"u16"},{"name":"inMarketIndex","type":"u16"},{"name":"maxSlippageBps","type":"u16"},{"name":"swapMode","type":"u8"},{"name":"status","type":"u8"}]}},{"name":"ConstituentSpotBalance","type":{"kind":"struct","fields":[{"name":"scaledBalance","docs":["The scaled balance of the position. To get the token amount, multiply by the cumulative deposit/borrow","interest of corresponding market.","precision: token precision"],"type":"u128"},{"name":"cumulativeDeposits","docs":["The cumulative deposits/borrows a user has made into a market","precision: token mint precision"],"type":"i64"},{"name":"marketIndex","docs":["The market index of the corresponding spot market"],"type":"u16"},{"name":"balanceType","docs":["Whether the position is deposit or borrow"],"type":{"defined":"SpotBalanceType"}},{"name":"padding","type":{"array":["u8",5]}}]}},{"name":"AmmConstituentDatum","type":{"kind":"struct","fields":[{"name":"perpMarketIndex","type":"u16"},{"name":"constituentIndex","type":"u16"},{"name":"padding","type":{"array":["u8",4]}},{"name":"lastSlot","type":"u64"},{"name":"weight","docs":["PERCENTAGE_PRECISION. The weight this constituent has on the perp market"],"type":"i64"}]}},{"name":"AmmConstituentMappingFixed","type":{"kind":"struct","fields":[{"name":"lpPool","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"pad","type":{"array":["u8",3]}},{"name":"len","type":"u32"}]}},{"name":"TargetsDatum","type":{"kind":"struct","fields":[{"name":"costToTradeBps","type":"i32"},{"name":"padding","type":{"array":["u8",4]}},{"name":"targetBase","type":"i64"},{"name":"lastOracleSlot","type":"u64"},{"name":"lastPositionSlot","type":"u64"}]}},{"name":"ConstituentTargetBaseFixed","type":{"kind":"struct","fields":[{"name":"lpPool","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"pad","type":{"array":["u8",3]}},{"name":"len","docs":["total elements in the flattened `data` vec"],"type":"u32"}]}},{"name":"ConstituentCorrelationsFixed","type":{"kind":"struct","fields":[{"name":"lpPool","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"pad","type":{"array":["u8",3]}},{"name":"len","docs":["total elements in the flattened `data` vec"],"type":"u32"}]}},{"name":"MarketIdentifier","type":{"kind":"struct","fields":[{"name":"marketType","type":{"defined":"MarketType"}},{"name":"marketIndex","type":"u16"}]}},{"name":"HistoricalOracleData","type":{"kind":"struct","fields":[{"name":"lastOraclePrice","docs":["precision: PRICE_PRECISION"],"type":"i64"},{"name":"lastOracleConf","docs":["precision: PRICE_PRECISION"],"type":"u64"},{"name":"lastOracleDelay","docs":["number of slots since last update"],"type":"i64"},{"name":"lastOraclePriceTwap","docs":["precision: PRICE_PRECISION"],"type":"i64"},{"name":"lastOraclePriceTwap5min","docs":["precision: PRICE_PRECISION"],"type":"i64"},{"name":"lastOraclePriceTwapTs","docs":["unix_timestamp of last snapshot"],"type":"i64"}]}},{"name":"HistoricalIndexData","type":{"kind":"struct","fields":[{"name":"lastIndexBidPrice","docs":["precision: PRICE_PRECISION"],"type":"u64"},{"name":"lastIndexAskPrice","docs":["precision: PRICE_PRECISION"],"type":"u64"},{"name":"lastIndexPriceTwap","docs":["precision: PRICE_PRECISION"],"type":"u64"},{"name":"lastIndexPriceTwap5min","docs":["precision: PRICE_PRECISION"],"type":"u64"},{"name":"lastIndexPriceTwapTs","docs":["unix_timestamp of last snapshot"],"type":"i64"}]}},{"name":"PrelaunchOracleParams","type":{"kind":"struct","fields":[{"name":"perpMarketIndex","type":"u16"},{"name":"price","type":{"option":"i64"}},{"name":"maxPrice","type":{"option":"i64"}}]}},{"name":"OrderParams","type":{"kind":"struct","fields":[{"name":"orderType","type":{"defined":"OrderType"}},{"name":"marketType","type":{"defined":"MarketType"}},{"name":"direction","type":{"defined":"PositionDirection"}},{"name":"userOrderId","type":"u8"},{"name":"baseAssetAmount","type":"u64"},{"name":"price","type":"u64"},{"name":"marketIndex","type":"u16"},{"name":"reduceOnly","type":"bool"},{"name":"postOnly","type":{"defined":"PostOnlyParam"}},{"name":"bitFlags","type":"u8"},{"name":"maxTs","type":{"option":"i64"}},{"name":"triggerPrice","type":{"option":"u64"}},{"name":"triggerCondition","type":{"defined":"OrderTriggerCondition"}},{"name":"oraclePriceOffset","type":{"option":"i32"}},{"name":"auctionDuration","type":{"option":"u8"}},{"name":"auctionStartPrice","type":{"option":"i64"}},{"name":"auctionEndPrice","type":{"option":"i64"}}]}},{"name":"SignedMsgOrderParamsMessage","type":{"kind":"struct","fields":[{"name":"signedMsgOrderParams","type":{"defined":"OrderParams"}},{"name":"subAccountId","type":"u16"},{"name":"slot","type":"u64"},{"name":"uuid","type":{"array":["u8",8]}},{"name":"takeProfitOrderParams","type":{"option":{"defined":"SignedMsgTriggerOrderParams"}}},{"name":"stopLossOrderParams","type":{"option":{"defined":"SignedMsgTriggerOrderParams"}}},{"name":"maxMarginRatio","type":{"option":"u16"}},{"name":"builderIdx","type":{"option":"u8"}},{"name":"builderFeeTenthBps","type":{"option":"u16"}},{"name":"isolatedPositionDeposit","type":{"option":"u64"}}]}},{"name":"SignedMsgOrderParamsDelegateMessage","type":{"kind":"struct","fields":[{"name":"signedMsgOrderParams","type":{"defined":"OrderParams"}},{"name":"takerPubkey","type":"publicKey"},{"name":"slot","type":"u64"},{"name":"uuid","type":{"array":["u8",8]}},{"name":"takeProfitOrderParams","type":{"option":{"defined":"SignedMsgTriggerOrderParams"}}},{"name":"stopLossOrderParams","type":{"option":{"defined":"SignedMsgTriggerOrderParams"}}},{"name":"maxMarginRatio","type":{"option":"u16"}},{"name":"builderIdx","type":{"option":"u8"}},{"name":"builderFeeTenthBps","type":{"option":"u16"}},{"name":"isolatedPositionDeposit","type":{"option":"u64"}}]}},{"name":"SignedMsgTriggerOrderParams","type":{"kind":"struct","fields":[{"name":"triggerPrice","type":"u64"},{"name":"baseAssetAmount","type":"u64"}]}},{"name":"ModifyOrderParams","type":{"kind":"struct","fields":[{"name":"direction","type":{"option":{"defined":"PositionDirection"}}},{"name":"baseAssetAmount","type":{"option":"u64"}},{"name":"price","type":{"option":"u64"}},{"name":"reduceOnly","type":{"option":"bool"}},{"name":"postOnly","type":{"option":{"defined":"PostOnlyParam"}}},{"name":"bitFlags","type":{"option":"u8"}},{"name":"maxTs","type":{"option":"i64"}},{"name":"triggerPrice","type":{"option":"u64"}},{"name":"triggerCondition","type":{"option":{"defined":"OrderTriggerCondition"}}},{"name":"oraclePriceOffset","type":{"option":"i32"}},{"name":"auctionDuration","type":{"option":"u8"}},{"name":"auctionStartPrice","type":{"option":"i64"}},{"name":"auctionEndPrice","type":{"option":"i64"}},{"name":"policy","type":{"option":"u8"}}]}},{"name":"InsuranceClaim","type":{"kind":"struct","fields":[{"name":"revenueWithdrawSinceLastSettle","docs":["The amount of revenue last settled","Positive if funds left the perp market,","negative if funds were pulled into the perp market","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"maxRevenueWithdrawPerPeriod","docs":["The max amount of revenue that can be withdrawn per period","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"quoteMaxInsurance","docs":["The max amount of insurance that perp market can use to resolve bankruptcy and pnl deficits","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"quoteSettledInsurance","docs":["The amount of insurance that has been used to resolve bankruptcy and pnl deficits","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"lastRevenueWithdrawTs","docs":["The last time revenue was settled in/out of market"],"type":"i64"}]}},{"name":"PoolBalance","type":{"kind":"struct","fields":[{"name":"scaledBalance","docs":["To get the pool's token amount, you must multiply the scaled balance by the market's cumulative","deposit interest","precision: SPOT_BALANCE_PRECISION"],"type":"u128"},{"name":"marketIndex","docs":["The spot market the pool is for"],"type":"u16"},{"name":"padding","type":{"array":["u8",6]}}]}},{"name":"AMM","type":{"kind":"struct","fields":[{"name":"oracle","docs":["oracle price data public key"],"type":"publicKey"},{"name":"historicalOracleData","docs":["stores historically witnessed oracle data"],"type":{"defined":"HistoricalOracleData"}},{"name":"baseAssetAmountPerLp","docs":["accumulated base asset amount since inception per lp share","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"quoteAssetAmountPerLp","docs":["accumulated quote asset amount since inception per lp share","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"feePool","docs":["partition of fees from perp market trading moved from pnl settlements"],"type":{"defined":"PoolBalance"}},{"name":"baseAssetReserve","docs":["`x` reserves for constant product mm formula (x * y = k)","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"quoteAssetReserve","docs":["`y` reserves for constant product mm formula (x * y = k)","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"concentrationCoef","docs":["determines how close the min/max base asset reserve sit vs base reserves","allow for decreasing slippage without increasing liquidity and v.v.","precision: PERCENTAGE_PRECISION"],"type":"u128"},{"name":"minBaseAssetReserve","docs":["minimum base_asset_reserve allowed before AMM is unavailable","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"maxBaseAssetReserve","docs":["maximum base_asset_reserve allowed before AMM is unavailable","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"sqrtK","docs":["`sqrt(k)` in constant product mm formula (x * y = k). stored to avoid drift caused by integer math issues","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"pegMultiplier","docs":["normalizing numerical factor for y, its use offers lowest slippage in cp-curve when market is balanced","precision: PEG_PRECISION"],"type":"u128"},{"name":"terminalQuoteAssetReserve","docs":["y when market is balanced. stored to save computation","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"baseAssetAmountLong","docs":["always non-negative. tracks number of total longs in market (regardless of counterparty)","precision: BASE_PRECISION"],"type":"i128"},{"name":"baseAssetAmountShort","docs":["always non-positive. tracks number of total shorts in market (regardless of counterparty)","precision: BASE_PRECISION"],"type":"i128"},{"name":"baseAssetAmountWithAmm","docs":["tracks net position (longs-shorts) in market with AMM as counterparty","precision: BASE_PRECISION"],"type":"i128"},{"name":"baseAssetAmountWithUnsettledLp","docs":["tracks net position (longs-shorts) in market with LPs as counterparty","precision: BASE_PRECISION"],"type":"i128"},{"name":"maxOpenInterest","docs":["max allowed open interest, blocks trades that breach this value","precision: BASE_PRECISION"],"type":"u128"},{"name":"quoteAssetAmount","docs":["sum of all user's perp quote_asset_amount in market","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"quoteEntryAmountLong","docs":["sum of all long user's quote_entry_amount in market","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"quoteEntryAmountShort","docs":["sum of all short user's quote_entry_amount in market","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"quoteBreakEvenAmountLong","docs":["sum of all long user's quote_break_even_amount in market","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"quoteBreakEvenAmountShort","docs":["sum of all short user's quote_break_even_amount in market","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"userLpShares","docs":["total user lp shares of sqrt_k (protocol owned liquidity = sqrt_k - last_funding_rate)","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"lastFundingRate","docs":["last funding rate in this perp market (unit is quote per base)","precision: FUNDING_RATE_PRECISION"],"type":"i64"},{"name":"lastFundingRateLong","docs":["last funding rate for longs in this perp market (unit is quote per base)","precision: FUNDING_RATE_PRECISION"],"type":"i64"},{"name":"lastFundingRateShort","docs":["last funding rate for shorts in this perp market (unit is quote per base)","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"last24hAvgFundingRate","docs":["estimate of last 24h of funding rate perp market (unit is quote per base)","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"totalFee","docs":["total fees collected by this perp market","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"totalMmFee","docs":["total fees collected by the vAMM's bid/ask spread","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"totalExchangeFee","docs":["total fees collected by exchange fee schedule","precision: QUOTE_PRECISION"],"type":"u128"},{"name":"totalFeeMinusDistributions","docs":["total fees minus any recognized upnl and pool withdraws","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"totalFeeWithdrawn","docs":["sum of all fees from fee pool withdrawn to revenue pool","precision: QUOTE_PRECISION"],"type":"u128"},{"name":"totalLiquidationFee","docs":["all fees collected by market for liquidations","precision: QUOTE_PRECISION"],"type":"u128"},{"name":"cumulativeFundingRateLong","docs":["accumulated funding rate for longs since inception in market"],"type":"i128"},{"name":"cumulativeFundingRateShort","docs":["accumulated funding rate for shorts since inception in market"],"type":"i128"},{"name":"totalSocialLoss","docs":["accumulated social loss paid by users since inception in market"],"type":"u128"},{"name":"askBaseAssetReserve","docs":["transformed base_asset_reserve for users going long","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"askQuoteAssetReserve","docs":["transformed quote_asset_reserve for users going long","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"bidBaseAssetReserve","docs":["transformed base_asset_reserve for users going short","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"bidQuoteAssetReserve","docs":["transformed quote_asset_reserve for users going short","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"lastOracleNormalisedPrice","docs":["the last seen oracle price partially shrunk toward the amm reserve price","precision: PRICE_PRECISION"],"type":"i64"},{"name":"lastOracleReservePriceSpreadPct","docs":["the gap between the oracle price and the reserve price = y * peg_multiplier / x"],"type":"i64"},{"name":"lastBidPriceTwap","docs":["average estimate of bid price over funding_period","precision: PRICE_PRECISION"],"type":"u64"},{"name":"lastAskPriceTwap","docs":["average estimate of ask price over funding_period","precision: PRICE_PRECISION"],"type":"u64"},{"name":"lastMarkPriceTwap","docs":["average estimate of (bid+ask)/2 price over funding_period","precision: PRICE_PRECISION"],"type":"u64"},{"name":"lastMarkPriceTwap5min","docs":["average estimate of (bid+ask)/2 price over FIVE_MINUTES"],"type":"u64"},{"name":"lastUpdateSlot","docs":["the last blockchain slot the amm was updated"],"type":"u64"},{"name":"lastOracleConfPct","docs":["the pct size of the oracle confidence interval","precision: PERCENTAGE_PRECISION"],"type":"u64"},{"name":"netRevenueSinceLastFunding","docs":["the total_fee_minus_distribution change since the last funding update","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"lastFundingRateTs","docs":["the last funding rate update unix_timestamp"],"type":"i64"},{"name":"fundingPeriod","docs":["the peridocity of the funding rate updates"],"type":"i64"},{"name":"orderStepSize","docs":["the base step size (increment) of orders","precision: BASE_PRECISION"],"type":"u64"},{"name":"orderTickSize","docs":["the price tick size of orders","precision: PRICE_PRECISION"],"type":"u64"},{"name":"minOrderSize","docs":["the minimum base size of an order","precision: BASE_PRECISION"],"type":"u64"},{"name":"mmOracleSlot","docs":["the max base size a single user can have","precision: BASE_PRECISION"],"type":"u64"},{"name":"volume24h","docs":["estimated total of volume in market","QUOTE_PRECISION"],"type":"u64"},{"name":"longIntensityVolume","docs":["the volume intensity of long fills against AMM"],"type":"u64"},{"name":"shortIntensityVolume","docs":["the volume intensity of short fills against AMM"],"type":"u64"},{"name":"lastTradeTs","docs":["the blockchain unix timestamp at the time of the last trade"],"type":"i64"},{"name":"markStd","docs":["estimate of standard deviation of the fill (mark) prices","precision: PRICE_PRECISION"],"type":"u64"},{"name":"oracleStd","docs":["estimate of standard deviation of the oracle price at each update","precision: PRICE_PRECISION"],"type":"u64"},{"name":"lastMarkPriceTwapTs","docs":["the last unix_timestamp the mark twap was updated"],"type":"i64"},{"name":"baseSpread","docs":["the minimum spread the AMM can quote. also used as step size for some spread logic increases."],"type":"u32"},{"name":"maxSpread","docs":["the maximum spread the AMM can quote"],"type":"u32"},{"name":"longSpread","docs":["the spread for asks vs the reserve price"],"type":"u32"},{"name":"shortSpread","docs":["the spread for bids vs the reserve price"],"type":"u32"},{"name":"mmOraclePrice","docs":["MM oracle price"],"type":"i64"},{"name":"maxFillReserveFraction","docs":["the fraction of total available liquidity a single fill on the AMM can consume"],"type":"u16"},{"name":"maxSlippageRatio","docs":["the maximum slippage a single fill on the AMM can push"],"type":"u16"},{"name":"curveUpdateIntensity","docs":["the update intensity of AMM formulaic updates (adjusting k). 0-100"],"type":"u8"},{"name":"ammJitIntensity","docs":["the jit intensity of AMM. larger intensity means larger participation in jit. 0 means no jit participation.","(0, 100] is intensity for protocol-owned AMM. (100, 200] is intensity for user LP-owned AMM."],"type":"u8"},{"name":"oracleSource","docs":["the oracle provider information. used to decode/scale the oracle public key"],"type":{"defined":"OracleSource"}},{"name":"lastOracleValid","docs":["tracks whether the oracle was considered valid at the last AMM update"],"type":"bool"},{"name":"targetBaseAssetAmountPerLp","docs":["the target value for `base_asset_amount_per_lp`, used during AMM JIT with LP split","precision: BASE_PRECISION"],"type":"i32"},{"name":"perLpBase","docs":["expo for unit of per_lp, base 10 (if per_lp_base=X, then per_lp unit is 10^X)"],"type":"i8"},{"name":"oracleLowRiskSlotDelayOverride","docs":["the override for the state.min_perp_auction_duration","0 is no override, -1 is disable speed bump, 1-100 is literal speed bump"],"type":"i8"},{"name":"ammSpreadAdjustment","docs":["signed scale amm_spread similar to fee_adjustment logic (-100 = 0, 100 = double)"],"type":"i8"},{"name":"oracleSlotDelayOverride","type":"i8"},{"name":"mmOracleSequenceId","type":"u64"},{"name":"netUnsettledFundingPnl","type":"i64"},{"name":"quoteAssetAmountWithUnsettledLp","type":"i64"},{"name":"referencePriceOffset","type":"i32"},{"name":"ammInventorySpreadAdjustment","docs":["signed scale amm_spread similar to fee_adjustment logic (-100 = 0, 100 = double)"],"type":"i8"},{"name":"referencePriceOffsetDeadbandPct","type":"u8"},{"name":"padding","type":{"array":["u8",2]}},{"name":"lastFundingOracleTwap","type":"i64"}]}},{"name":"RevenueShareOrder","type":{"kind":"struct","fields":[{"name":"feesAccrued","docs":["fees accrued so far for this order slot. This is not exclusively fees from this order_id","and may include fees from other orders in the same market. This may be swept to the","builder's SpotPosition during settle_pnl."],"type":"u64"},{"name":"orderId","docs":["the order_id of the current active order in this slot. It's only relevant while bit_flag = Open"],"type":"u32"},{"name":"feeTenthBps","docs":["the builder fee on this order, in tenths of a bps, e.g. 100 = 0.01%"],"type":"u16"},{"name":"marketIndex","type":"u16"},{"name":"subAccountId","docs":["the subaccount_id of the user who created this order. It's only relevant while bit_flag = Open"],"type":"u16"},{"name":"builderIdx","docs":["the index of the RevenueShareEscrow.approved_builders list, that this order's fee will settle to. Ignored","if bit_flag = Referral."],"type":"u8"},{"name":"bitFlags","docs":["bitflags that describe the state of the order.","[`RevenueShareOrderBitFlag::Init`]: this order slot is available for use.","[`RevenueShareOrderBitFlag::Open`]: this order slot is occupied, `order_id` is the `sub_account_id`'s active order.","[`RevenueShareOrderBitFlag::Completed`]: this order has been filled or canceled, and is waiting to be settled into.","the builder's account order_id and sub_account_id are no longer relevant, it may be merged with other orders.","[`RevenueShareOrderBitFlag::Referral`]: this order stores referral rewards waiting to be settled for this market.","If it is set, no other bitflag should be set."],"type":"u8"},{"name":"userOrderIndex","docs":["the index into the User's orders list when this RevenueShareOrder was created, make sure to verify that order_id matches."],"type":"u8"},{"name":"marketType","type":{"defined":"MarketType"}},{"name":"padding","type":{"array":["u8",10]}}]}},{"name":"BuilderInfo","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"maxFeeTenthBps","type":"u16"},{"name":"padding","type":{"array":["u8",6]}}]}},{"name":"RevenueShareEscrowFixed","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"referrer","type":"publicKey"},{"name":"referrerBoostExpireTs","type":"u32"},{"name":"referrerRewardOffset","type":"i8"},{"name":"refereeFeeNumeratorOffset","type":"i8"},{"name":"referrerBoostNumerator","type":"i8"},{"name":"reservedFixed","type":{"array":["u8",17]}}]}},{"name":"SignedMsgOrderId","type":{"kind":"struct","fields":[{"name":"uuid","type":{"array":["u8",8]}},{"name":"maxSlot","type":"u64"},{"name":"orderId","type":"u32"},{"name":"padding","type":"u32"}]}},{"name":"SignedMsgUserOrdersFixed","type":{"kind":"struct","fields":[{"name":"userPubkey","type":"publicKey"},{"name":"padding","type":"u32"},{"name":"len","type":"u32"}]}},{"name":"InsuranceFund","type":{"kind":"struct","fields":[{"name":"vault","type":"publicKey"},{"name":"totalShares","type":"u128"},{"name":"userShares","type":"u128"},{"name":"sharesBase","type":"u128"},{"name":"unstakingPeriod","type":"i64"},{"name":"lastRevenueSettleTs","type":"i64"},{"name":"revenueSettlePeriod","type":"i64"},{"name":"totalFactor","type":"u32"},{"name":"userFactor","type":"u32"}]}},{"name":"OracleGuardRails","type":{"kind":"struct","fields":[{"name":"priceDivergence","type":{"defined":"PriceDivergenceGuardRails"}},{"name":"validity","type":{"defined":"ValidityGuardRails"}}]}},{"name":"PriceDivergenceGuardRails","type":{"kind":"struct","fields":[{"name":"markOraclePercentDivergence","type":"u64"},{"name":"oracleTwap5minPercentDivergence","type":"u64"}]}},{"name":"ValidityGuardRails","type":{"kind":"struct","fields":[{"name":"slotsBeforeStaleForAmm","type":"i64"},{"name":"slotsBeforeStaleForMargin","type":"i64"},{"name":"confidenceIntervalMaxSize","type":"u64"},{"name":"tooVolatileRatio","type":"i64"}]}},{"name":"FeeStructure","type":{"kind":"struct","fields":[{"name":"feeTiers","type":{"array":[{"defined":"FeeTier"},10]}},{"name":"fillerRewardStructure","type":{"defined":"OrderFillerRewardStructure"}},{"name":"referrerRewardEpochUpperBound","type":"u64"},{"name":"flatFillerFee","type":"u64"}]}},{"name":"FeeTier","type":{"kind":"struct","fields":[{"name":"feeNumerator","type":"u32"},{"name":"feeDenominator","type":"u32"},{"name":"makerRebateNumerator","type":"u32"},{"name":"makerRebateDenominator","type":"u32"},{"name":"referrerRewardNumerator","type":"u32"},{"name":"referrerRewardDenominator","type":"u32"},{"name":"refereeFeeNumerator","type":"u32"},{"name":"refereeFeeDenominator","type":"u32"}]}},{"name":"OrderFillerRewardStructure","type":{"kind":"struct","fields":[{"name":"rewardNumerator","type":"u32"},{"name":"rewardDenominator","type":"u32"},{"name":"timeBasedRewardLowerBound","type":"u128"}]}},{"name":"UserFees","type":{"kind":"struct","fields":[{"name":"totalFeePaid","docs":["Total taker fee paid","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"totalFeeRebate","docs":["Total maker fee rebate","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"totalTokenDiscount","docs":["Total discount from holding token","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"totalRefereeDiscount","docs":["Total discount from being referred","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"totalReferrerReward","docs":["Total reward to referrer","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"currentEpochReferrerReward","docs":["Total reward to referrer this epoch","precision: QUOTE_PRECISION"],"type":"u64"}]}},{"name":"SpotPosition","type":{"kind":"struct","fields":[{"name":"scaledBalance","docs":["The scaled balance of the position. To get the token amount, multiply by the cumulative deposit/borrow","interest of corresponding market.","precision: SPOT_BALANCE_PRECISION"],"type":"u64"},{"name":"openBids","docs":["How many spot non reduce only trigger orders the user has open","precision: token mint precision"],"type":"i64"},{"name":"openAsks","docs":["How many spot non reduce only trigger orders the user has open","precision: token mint precision"],"type":"i64"},{"name":"cumulativeDeposits","docs":["The cumulative deposits/borrows a user has made into a market","precision: token mint precision"],"type":"i64"},{"name":"marketIndex","docs":["The market index of the corresponding spot market"],"type":"u16"},{"name":"balanceType","docs":["Whether the position is deposit or borrow"],"type":{"defined":"SpotBalanceType"}},{"name":"openOrders","docs":["Number of open orders"],"type":"u8"},{"name":"padding","type":{"array":["u8",4]}}]}},{"name":"PerpPosition","type":{"kind":"struct","fields":[{"name":"lastCumulativeFundingRate","docs":["The perp market's last cumulative funding rate. Used to calculate the funding payment owed to user","precision: FUNDING_RATE_PRECISION"],"type":"i64"},{"name":"baseAssetAmount","docs":["the size of the users perp position","precision: BASE_PRECISION"],"type":"i64"},{"name":"quoteAssetAmount","docs":["Used to calculate the users pnl. Upon entry, is equal to base_asset_amount * avg entry price - fees","Updated when the user open/closes position or settles pnl. Includes fees/funding","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"quoteBreakEvenAmount","docs":["The amount of quote the user would need to exit their position at to break even","Updated when the user open/closes position or settles pnl. Includes fees/funding","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"quoteEntryAmount","docs":["The amount quote the user entered the position with. Equal to base asset amount * avg entry price","Updated when the user open/closes position. Excludes fees/funding","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"openBids","docs":["The amount of non reduce only trigger orders the user has open","precision: BASE_PRECISION"],"type":"i64"},{"name":"openAsks","docs":["The amount of non reduce only trigger orders the user has open","precision: BASE_PRECISION"],"type":"i64"},{"name":"settledPnl","docs":["The amount of pnl settled in this market since opening the position","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"lpShares","docs":["The number of lp (liquidity provider) shares the user has in this perp market","LP shares allow users to provide liquidity via the AMM","precision: BASE_PRECISION"],"type":"u64"},{"name":"lastBaseAssetAmountPerLp","docs":["The last base asset amount per lp the amm had","Used to settle the users lp position","precision: BASE_PRECISION"],"type":"i64"},{"name":"lastQuoteAssetAmountPerLp","docs":["The last quote asset amount per lp the amm had","Used to settle the users lp position","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"padding","type":{"array":["u8",2]}},{"name":"maxMarginRatio","type":"u16"},{"name":"marketIndex","docs":["The market index for the perp market"],"type":"u16"},{"name":"openOrders","docs":["The number of open orders"],"type":"u8"},{"name":"perLpBase","type":"i8"}]}},{"name":"Order","type":{"kind":"struct","fields":[{"name":"slot","docs":["The slot the order was placed"],"type":"u64"},{"name":"price","docs":["The limit price for the order (can be 0 for market orders)","For orders with an auction, this price isn't used until the auction is complete","precision: PRICE_PRECISION"],"type":"u64"},{"name":"baseAssetAmount","docs":["The size of the order","precision for perps: BASE_PRECISION","precision for spot: token mint precision"],"type":"u64"},{"name":"baseAssetAmountFilled","docs":["The amount of the order filled","precision for perps: BASE_PRECISION","precision for spot: token mint precision"],"type":"u64"},{"name":"quoteAssetAmountFilled","docs":["The amount of quote filled for the order","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"triggerPrice","docs":["At what price the order will be triggered. Only relevant for trigger orders","precision: PRICE_PRECISION"],"type":"u64"},{"name":"auctionStartPrice","docs":["The start price for the auction. Only relevant for market/oracle orders","precision: PRICE_PRECISION"],"type":"i64"},{"name":"auctionEndPrice","docs":["The end price for the auction. Only relevant for market/oracle orders","precision: PRICE_PRECISION"],"type":"i64"},{"name":"maxTs","docs":["The time when the order will expire"],"type":"i64"},{"name":"oraclePriceOffset","docs":["If set, the order limit price is the oracle price + this offset","precision: PRICE_PRECISION"],"type":"i32"},{"name":"orderId","docs":["The id for the order. Each users has their own order id space"],"type":"u32"},{"name":"marketIndex","docs":["The perp/spot market index"],"type":"u16"},{"name":"status","docs":["Whether the order is open or unused"],"type":{"defined":"OrderStatus"}},{"name":"orderType","docs":["The type of order"],"type":{"defined":"OrderType"}},{"name":"marketType","docs":["Whether market is spot or perp"],"type":{"defined":"MarketType"}},{"name":"userOrderId","docs":["User generated order id. Can make it easier to place/cancel orders"],"type":"u8"},{"name":"existingPositionDirection","docs":["What the users position was when the order was placed"],"type":{"defined":"PositionDirection"}},{"name":"direction","docs":["Whether the user is going long or short. LONG = bid, SHORT = ask"],"type":{"defined":"PositionDirection"}},{"name":"reduceOnly","docs":["Whether the order is allowed to only reduce position size"],"type":"bool"},{"name":"postOnly","docs":["Whether the order must be a maker"],"type":"bool"},{"name":"immediateOrCancel","docs":["Whether the order must be canceled the same slot it is placed"],"type":"bool"},{"name":"triggerCondition","docs":["Whether the order is triggered above or below the trigger price. Only relevant for trigger orders"],"type":{"defined":"OrderTriggerCondition"}},{"name":"auctionDuration","docs":["How many slots the auction lasts"],"type":"u8"},{"name":"postedSlotTail","docs":["Last 8 bits of the slot the order was posted on-chain (not order slot for signed msg orders)"],"type":"u8"},{"name":"bitFlags","docs":["Bitflags for further classification","0: is_signed_message"],"type":"u8"},{"name":"padding","type":{"array":["u8",1]}}]}},{"name":"SwapDirection","type":{"kind":"enum","variants":[{"name":"Add"},{"name":"Remove"}]}},{"name":"ModifyOrderId","type":{"kind":"enum","variants":[{"name":"UserOrderId","fields":["u8"]},{"name":"OrderId","fields":["u32"]}]}},{"name":"PositionDirection","type":{"kind":"enum","variants":[{"name":"Long"},{"name":"Short"}]}},{"name":"SpotFulfillmentType","type":{"kind":"enum","variants":[{"name":"SerumV3"},{"name":"Match"},{"name":"PhoenixV1"},{"name":"OpenbookV2"}]}},{"name":"SwapReduceOnly","type":{"kind":"enum","variants":[{"name":"In"},{"name":"Out"}]}},{"name":"TwapPeriod","type":{"kind":"enum","variants":[{"name":"FundingPeriod"},{"name":"FiveMin"}]}},{"name":"LiquidationMultiplierType","type":{"kind":"enum","variants":[{"name":"Discount"},{"name":"Premium"}]}},{"name":"SettlementDirection","type":{"kind":"enum","variants":[{"name":"ToLpPool"},{"name":"FromLpPool"},{"name":"None"}]}},{"name":"MarginRequirementType","type":{"kind":"enum","variants":[{"name":"Initial"},{"name":"Fill"},{"name":"Maintenance"}]}},{"name":"OracleValidity","type":{"kind":"enum","variants":[{"name":"NonPositive"},{"name":"TooVolatile"},{"name":"TooUncertain"},{"name":"StaleForMargin"},{"name":"InsufficientDataPoints"},{"name":"StaleForAMM","fields":[{"name":"immediate","type":"bool"},{"name":"lowRisk","type":"bool"}]},{"name":"Valid"}]}},{"name":"DriftAction","type":{"kind":"enum","variants":[{"name":"UpdateFunding"},{"name":"SettlePnl"},{"name":"TriggerOrder"},{"name":"FillOrderMatch"},{"name":"FillOrderAmmLowRisk"},{"name":"FillOrderAmmImmediate"},{"name":"Liquidate"},{"name":"MarginCalc"},{"name":"UpdateTwap"},{"name":"UpdateAMMCurve"},{"name":"OracleOrderPrice"},{"name":"UseMMOraclePrice"},{"name":"UpdateAmmCache"},{"name":"UpdateLpPoolAum"},{"name":"LpPoolSwap"}]}},{"name":"LogMode","type":{"kind":"enum","variants":[{"name":"None"},{"name":"ExchangeOracle"},{"name":"MMOracle"},{"name":"SafeMMOracle"},{"name":"Margin"}]}},{"name":"PositionUpdateType","type":{"kind":"enum","variants":[{"name":"Open"},{"name":"Increase"},{"name":"Reduce"},{"name":"Close"},{"name":"Flip"}]}},{"name":"DepositExplanation","type":{"kind":"enum","variants":[{"name":"None"},{"name":"Transfer"},{"name":"Borrow"},{"name":"RepayBorrow"},{"name":"Reward"}]}},{"name":"DepositDirection","type":{"kind":"enum","variants":[{"name":"Deposit"},{"name":"Withdraw"}]}},{"name":"OrderAction","type":{"kind":"enum","variants":[{"name":"Place"},{"name":"Cancel"},{"name":"Fill"},{"name":"Trigger"},{"name":"Expire"}]}},{"name":"OrderActionExplanation","type":{"kind":"enum","variants":[{"name":"None"},{"name":"InsufficientFreeCollateral"},{"name":"OraclePriceBreachedLimitPrice"},{"name":"MarketOrderFilledToLimitPrice"},{"name":"OrderExpired"},{"name":"Liquidation"},{"name":"OrderFilledWithAMM"},{"name":"OrderFilledWithAMMJit"},{"name":"OrderFilledWithMatch"},{"name":"OrderFilledWithMatchJit"},{"name":"MarketExpired"},{"name":"RiskingIncreasingOrder"},{"name":"ReduceOnlyOrderIncreasedPosition"},{"name":"OrderFillWithSerum"},{"name":"NoBorrowLiquidity"},{"name":"OrderFillWithPhoenix"},{"name":"OrderFilledWithAMMJitLPSplit"},{"name":"OrderFilledWithLPJit"},{"name":"DeriskLp"},{"name":"OrderFilledWithOpenbookV2"},{"name":"TransferPerpPosition"}]}},{"name":"LPAction","type":{"kind":"enum","variants":[{"name":"AddLiquidity"},{"name":"RemoveLiquidity"},{"name":"SettleLiquidity"},{"name":"RemoveLiquidityDerisk"}]}},{"name":"LiquidationType","type":{"kind":"enum","variants":[{"name":"LiquidatePerp"},{"name":"LiquidateSpot"},{"name":"LiquidateBorrowForPerpPnl"},{"name":"LiquidatePerpPnlForDeposit"},{"name":"PerpBankruptcy"},{"name":"SpotBankruptcy"}]}},{"name":"SettlePnlExplanation","type":{"kind":"enum","variants":[{"name":"None"},{"name":"ExpiredPosition"}]}},{"name":"StakeAction","type":{"kind":"enum","variants":[{"name":"Stake"},{"name":"UnstakeRequest"},{"name":"UnstakeCancelRequest"},{"name":"Unstake"},{"name":"UnstakeTransfer"},{"name":"StakeTransfer"},{"name":"AdminDeposit"}]}},{"name":"FillMode","type":{"kind":"enum","variants":[{"name":"Fill"},{"name":"PlaceAndMake"},{"name":"PlaceAndTake","fields":["bool","u8"]},{"name":"Liquidation"}]}},{"name":"PerpFulfillmentMethod","type":{"kind":"enum","variants":[{"name":"AMM","fields":[{"option":"u64"}]},{"name":"Match","fields":["publicKey","u16","u64"]}]}},{"name":"SpotFulfillmentMethod","type":{"kind":"enum","variants":[{"name":"ExternalMarket"},{"name":"Match","fields":["publicKey","u16"]}]}},{"name":"ConstituentStatus","type":{"kind":"enum","variants":[{"name":"ReduceOnly"},{"name":"Decommissioned"}]}},{"name":"MarginCalculationMode","type":{"kind":"enum","variants":[{"name":"Standard","fields":[{"name":"trackOpenOrdersFraction","type":"bool"}]},{"name":"Liquidation","fields":[{"name":"marketToTrackMarginRequirement","type":{"option":{"defined":"MarketIdentifier"}}}]}]}},{"name":"OracleSource","type":{"kind":"enum","variants":[{"name":"Pyth"},{"name":"Switchboard"},{"name":"QuoteAsset"},{"name":"Pyth1K"},{"name":"Pyth1M"},{"name":"PythStableCoin"},{"name":"Prelaunch"},{"name":"PythPull"},{"name":"Pyth1KPull"},{"name":"Pyth1MPull"},{"name":"PythStableCoinPull"},{"name":"SwitchboardOnDemand"},{"name":"PythLazer"},{"name":"PythLazer1K"},{"name":"PythLazer1M"},{"name":"PythLazerStableCoin"}]}},{"name":"OrderParamsBitFlag","type":{"kind":"enum","variants":[{"name":"ImmediateOrCancel"},{"name":"UpdateHighLeverageMode"}]}},{"name":"PostOnlyParam","type":{"kind":"enum","variants":[{"name":"None"},{"name":"MustPostOnly"},{"name":"TryPostOnly"},{"name":"Slide"}]}},{"name":"ModifyOrderPolicy","type":{"kind":"enum","variants":[{"name":"MustModify"},{"name":"ExcludePreviousFill"}]}},{"name":"PlaceAndTakeOrderSuccessCondition","type":{"kind":"enum","variants":[{"name":"PartialFill"},{"name":"FullFill"}]}},{"name":"PerpOperation","type":{"kind":"enum","variants":[{"name":"UpdateFunding"},{"name":"AmmFill"},{"name":"Fill"},{"name":"SettlePnl"},{"name":"SettlePnlWithPosition"},{"name":"Liquidation"},{"name":"AmmImmediateFill"},{"name":"SettleRevPool"}]}},{"name":"SpotOperation","type":{"kind":"enum","variants":[{"name":"UpdateCumulativeInterest"},{"name":"Fill"},{"name":"Deposit"},{"name":"Withdraw"},{"name":"Liquidation"}]}},{"name":"InsuranceFundOperation","type":{"kind":"enum","variants":[{"name":"Init"},{"name":"Add"},{"name":"RequestRemove"},{"name":"Remove"}]}},{"name":"PerpLpOperation","type":{"kind":"enum","variants":[{"name":"TrackAmmRevenue"},{"name":"SettleQuoteOwed"}]}},{"name":"ConstituentLpOperation","type":{"kind":"enum","variants":[{"name":"Swap"},{"name":"Deposit"},{"name":"Withdraw"}]}},{"name":"MarketStatus","type":{"kind":"enum","variants":[{"name":"Initialized"},{"name":"Active"},{"name":"FundingPaused"},{"name":"AmmPaused"},{"name":"FillPaused"},{"name":"WithdrawPaused"},{"name":"ReduceOnly"},{"name":"Settlement"},{"name":"Delisted"}]}},{"name":"LpStatus","type":{"kind":"enum","variants":[{"name":"Uncollateralized"},{"name":"Active"},{"name":"Decommissioning"}]}},{"name":"ContractType","type":{"kind":"enum","variants":[{"name":"Perpetual"},{"name":"Future"},{"name":"Prediction"}]}},{"name":"ContractTier","type":{"kind":"enum","variants":[{"name":"A"},{"name":"B"},{"name":"C"},{"name":"Speculative"},{"name":"HighlySpeculative"},{"name":"Isolated"}]}},{"name":"RevenueShareOrderBitFlag","type":{"kind":"enum","variants":[{"name":"Init"},{"name":"Open"},{"name":"Completed"},{"name":"Referral"}]}},{"name":"SettlePnlMode","type":{"kind":"enum","variants":[{"name":"MustSettle"},{"name":"TrySettle"}]}},{"name":"SpotBalanceType","type":{"kind":"enum","variants":[{"name":"Deposit"},{"name":"Borrow"}]}},{"name":"SpotFulfillmentConfigStatus","type":{"kind":"enum","variants":[{"name":"Enabled"},{"name":"Disabled"}]}},{"name":"AssetTier","type":{"kind":"enum","variants":[{"name":"Collateral"},{"name":"Protected"},{"name":"Cross"},{"name":"Isolated"},{"name":"Unlisted"}]}},{"name":"TokenProgramFlag","type":{"kind":"enum","variants":[{"name":"Token2022"},{"name":"TransferHook"}]}},{"name":"ExchangeStatus","type":{"kind":"enum","variants":[{"name":"DepositPaused"},{"name":"WithdrawPaused"},{"name":"AmmPaused"},{"name":"FillPaused"},{"name":"LiqPaused"},{"name":"FundingPaused"},{"name":"SettlePnlPaused"},{"name":"AmmImmediateFillPaused"}]}},{"name":"FeatureBitFlags","type":{"kind":"enum","variants":[{"name":"MmOracleUpdate"},{"name":"MedianTriggerPrice"},{"name":"BuilderCodes"},{"name":"BuilderReferral"}]}},{"name":"LpPoolFeatureBitFlags","type":{"kind":"enum","variants":[{"name":"SettleLpPool"},{"name":"SwapLpPool"},{"name":"MintRedeemLpPool"}]}},{"name":"UserStatus","type":{"kind":"enum","variants":[{"name":"BeingLiquidated"},{"name":"Bankrupt"},{"name":"ReduceOnly"},{"name":"AdvancedLp"},{"name":"ProtectedMakerOrders"}]}},{"name":"AssetType","type":{"kind":"enum","variants":[{"name":"Base"},{"name":"Quote"}]}},{"name":"OrderStatus","type":{"kind":"enum","variants":[{"name":"Init"},{"name":"Open"},{"name":"Filled"},{"name":"Canceled"}]}},{"name":"OrderType","type":{"kind":"enum","variants":[{"name":"Market"},{"name":"Limit"},{"name":"TriggerMarket"},{"name":"TriggerLimit"},{"name":"Oracle"}]}},{"name":"OrderTriggerCondition","type":{"kind":"enum","variants":[{"name":"Above"},{"name":"Below"},{"name":"TriggeredAbove"},{"name":"TriggeredBelow"}]}},{"name":"MarketType","type":{"kind":"enum","variants":[{"name":"Spot"},{"name":"Perp"}]}},{"name":"OrderBitFlag","type":{"kind":"enum","variants":[{"name":"SignedMessage"},{"name":"OracleTriggerMarket"},{"name":"SafeTriggerOrder"},{"name":"NewTriggerReduceOnly"},{"name":"HasBuilder"}]}},{"name":"ReferrerStatus","type":{"kind":"enum","variants":[{"name":"IsReferrer"},{"name":"IsReferred"},{"name":"BuilderReferral"}]}},{"name":"MarginMode","type":{"kind":"enum","variants":[{"name":"Default"},{"name":"HighLeverage"},{"name":"HighLeverageMaintenance"}]}},{"name":"FuelOverflowStatus","type":{"kind":"enum","variants":[{"name":"Exists"}]}},{"name":"UserStatsPausedOperations","type":{"kind":"enum","variants":[{"name":"UpdateBidAskTwap"},{"name":"AmmAtomicFill"},{"name":"AmmAtomicRiskIncreasingFill"}]}},{"name":"SignatureVerificationError","type":{"kind":"enum","variants":[{"name":"InvalidEd25519InstructionProgramId"},{"name":"InvalidEd25519InstructionDataLength"},{"name":"InvalidSignatureIndex"},{"name":"InvalidSignatureOffset"},{"name":"InvalidPublicKeyOffset"},{"name":"InvalidMessageOffset"},{"name":"InvalidMessageDataSize"},{"name":"InvalidInstructionIndex"},{"name":"MessageOffsetOverflow"},{"name":"InvalidMessageHex"},{"name":"InvalidMessageData"},{"name":"LoadInstructionAtFailed"}]}}],"events":[{"name":"NewUserRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"userAuthority","type":"publicKey","index":false},{"name":"user","type":"publicKey","index":false},{"name":"subAccountId","type":"u16","index":false},{"name":"name","type":{"array":["u8",32]},"index":false},{"name":"referrer","type":"publicKey","index":false}]},{"name":"DepositRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"userAuthority","type":"publicKey","index":false},{"name":"user","type":"publicKey","index":false},{"name":"direction","type":{"defined":"DepositDirection"},"index":false},{"name":"depositRecordId","type":"u64","index":false},{"name":"amount","type":"u64","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"oraclePrice","type":"i64","index":false},{"name":"marketDepositBalance","type":"u128","index":false},{"name":"marketWithdrawBalance","type":"u128","index":false},{"name":"marketCumulativeDepositInterest","type":"u128","index":false},{"name":"marketCumulativeBorrowInterest","type":"u128","index":false},{"name":"totalDepositsAfter","type":"u64","index":false},{"name":"totalWithdrawsAfter","type":"u64","index":false},{"name":"explanation","type":{"defined":"DepositExplanation"},"index":false},{"name":"transferUser","type":{"option":"publicKey"},"index":false},{"name":"signer","type":{"option":"publicKey"},"index":false},{"name":"userTokenAmountAfter","type":"i128","index":false}]},{"name":"SpotInterestRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"depositBalance","type":"u128","index":false},{"name":"cumulativeDepositInterest","type":"u128","index":false},{"name":"borrowBalance","type":"u128","index":false},{"name":"cumulativeBorrowInterest","type":"u128","index":false},{"name":"optimalUtilization","type":"u32","index":false},{"name":"optimalBorrowRate","type":"u32","index":false},{"name":"maxBorrowRate","type":"u32","index":false}]},{"name":"FundingPaymentRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"userAuthority","type":"publicKey","index":false},{"name":"user","type":"publicKey","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"fundingPayment","type":"i64","index":false},{"name":"baseAssetAmount","type":"i64","index":false},{"name":"userLastCumulativeFunding","type":"i64","index":false},{"name":"ammCumulativeFundingLong","type":"i128","index":false},{"name":"ammCumulativeFundingShort","type":"i128","index":false}]},{"name":"FundingRateRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"recordId","type":"u64","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"fundingRate","type":"i64","index":false},{"name":"fundingRateLong","type":"i128","index":false},{"name":"fundingRateShort","type":"i128","index":false},{"name":"cumulativeFundingRateLong","type":"i128","index":false},{"name":"cumulativeFundingRateShort","type":"i128","index":false},{"name":"oraclePriceTwap","type":"i64","index":false},{"name":"markPriceTwap","type":"u64","index":false},{"name":"periodRevenue","type":"i64","index":false},{"name":"baseAssetAmountWithAmm","type":"i128","index":false},{"name":"baseAssetAmountWithUnsettledLp","type":"i128","index":false}]},{"name":"CurveRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"recordId","type":"u64","index":false},{"name":"pegMultiplierBefore","type":"u128","index":false},{"name":"baseAssetReserveBefore","type":"u128","index":false},{"name":"quoteAssetReserveBefore","type":"u128","index":false},{"name":"sqrtKBefore","type":"u128","index":false},{"name":"pegMultiplierAfter","type":"u128","index":false},{"name":"baseAssetReserveAfter","type":"u128","index":false},{"name":"quoteAssetReserveAfter","type":"u128","index":false},{"name":"sqrtKAfter","type":"u128","index":false},{"name":"baseAssetAmountLong","type":"u128","index":false},{"name":"baseAssetAmountShort","type":"u128","index":false},{"name":"baseAssetAmountWithAmm","type":"i128","index":false},{"name":"totalFee","type":"i128","index":false},{"name":"totalFeeMinusDistributions","type":"i128","index":false},{"name":"adjustmentCost","type":"i128","index":false},{"name":"oraclePrice","type":"i64","index":false},{"name":"fillRecord","type":"u128","index":false},{"name":"numberOfUsers","type":"u32","index":false},{"name":"marketIndex","type":"u16","index":false}]},{"name":"SignedMsgOrderRecord","fields":[{"name":"user","type":"publicKey","index":false},{"name":"hash","type":"string","index":false},{"name":"matchingOrderParams","type":{"defined":"OrderParams"},"index":false},{"name":"userOrderId","type":"u32","index":false},{"name":"signedMsgOrderMaxSlot","type":"u64","index":false},{"name":"signedMsgOrderUuid","type":{"array":["u8",8]},"index":false},{"name":"ts","type":"i64","index":false}]},{"name":"OrderRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"user","type":"publicKey","index":false},{"name":"order","type":{"defined":"Order"},"index":false}]},{"name":"OrderActionRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"action","type":{"defined":"OrderAction"},"index":false},{"name":"actionExplanation","type":{"defined":"OrderActionExplanation"},"index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"marketType","type":{"defined":"MarketType"},"index":false},{"name":"filler","type":{"option":"publicKey"},"index":false},{"name":"fillerReward","type":{"option":"u64"},"index":false},{"name":"fillRecordId","type":{"option":"u64"},"index":false},{"name":"baseAssetAmountFilled","type":{"option":"u64"},"index":false},{"name":"quoteAssetAmountFilled","type":{"option":"u64"},"index":false},{"name":"takerFee","type":{"option":"u64"},"index":false},{"name":"makerFee","type":{"option":"i64"},"index":false},{"name":"referrerReward","type":{"option":"u32"},"index":false},{"name":"quoteAssetAmountSurplus","type":{"option":"i64"},"index":false},{"name":"spotFulfillmentMethodFee","type":{"option":"u64"},"index":false},{"name":"taker","type":{"option":"publicKey"},"index":false},{"name":"takerOrderId","type":{"option":"u32"},"index":false},{"name":"takerOrderDirection","type":{"option":{"defined":"PositionDirection"}},"index":false},{"name":"takerOrderBaseAssetAmount","type":{"option":"u64"},"index":false},{"name":"takerOrderCumulativeBaseAssetAmountFilled","type":{"option":"u64"},"index":false},{"name":"takerOrderCumulativeQuoteAssetAmountFilled","type":{"option":"u64"},"index":false},{"name":"maker","type":{"option":"publicKey"},"index":false},{"name":"makerOrderId","type":{"option":"u32"},"index":false},{"name":"makerOrderDirection","type":{"option":{"defined":"PositionDirection"}},"index":false},{"name":"makerOrderBaseAssetAmount","type":{"option":"u64"},"index":false},{"name":"makerOrderCumulativeBaseAssetAmountFilled","type":{"option":"u64"},"index":false},{"name":"makerOrderCumulativeQuoteAssetAmountFilled","type":{"option":"u64"},"index":false},{"name":"oraclePrice","type":"i64","index":false},{"name":"bitFlags","type":"u8","index":false},{"name":"takerExistingQuoteEntryAmount","type":{"option":"u64"},"index":false},{"name":"takerExistingBaseAssetAmount","type":{"option":"u64"},"index":false},{"name":"makerExistingQuoteEntryAmount","type":{"option":"u64"},"index":false},{"name":"makerExistingBaseAssetAmount","type":{"option":"u64"},"index":false},{"name":"triggerPrice","type":{"option":"u64"},"index":false},{"name":"builderIdx","type":{"option":"u8"},"index":false},{"name":"builderFee","type":{"option":"u64"},"index":false}]},{"name":"LPRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"user","type":"publicKey","index":false},{"name":"action","type":{"defined":"LPAction"},"index":false},{"name":"nShares","type":"u64","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"deltaBaseAssetAmount","type":"i64","index":false},{"name":"deltaQuoteAssetAmount","type":"i64","index":false},{"name":"pnl","type":"i64","index":false}]},{"name":"LiquidationRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"liquidationType","type":{"defined":"LiquidationType"},"index":false},{"name":"user","type":"publicKey","index":false},{"name":"liquidator","type":"publicKey","index":false},{"name":"marginRequirement","type":"u128","index":false},{"name":"totalCollateral","type":"i128","index":false},{"name":"marginFreed","type":"u64","index":false},{"name":"liquidationId","type":"u16","index":false},{"name":"bankrupt","type":"bool","index":false},{"name":"canceledOrderIds","type":{"vec":"u32"},"index":false},{"name":"liquidatePerp","type":{"defined":"LiquidatePerpRecord"},"index":false},{"name":"liquidateSpot","type":{"defined":"LiquidateSpotRecord"},"index":false},{"name":"liquidateBorrowForPerpPnl","type":{"defined":"LiquidateBorrowForPerpPnlRecord"},"index":false},{"name":"liquidatePerpPnlForDeposit","type":{"defined":"LiquidatePerpPnlForDepositRecord"},"index":false},{"name":"perpBankruptcy","type":{"defined":"PerpBankruptcyRecord"},"index":false},{"name":"spotBankruptcy","type":{"defined":"SpotBankruptcyRecord"},"index":false}]},{"name":"SettlePnlRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"user","type":"publicKey","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"pnl","type":"i128","index":false},{"name":"baseAssetAmount","type":"i64","index":false},{"name":"quoteAssetAmountAfter","type":"i64","index":false},{"name":"quoteEntryAmount","type":"i64","index":false},{"name":"settlePrice","type":"i64","index":false},{"name":"explanation","type":{"defined":"SettlePnlExplanation"},"index":false}]},{"name":"InsuranceFundRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"spotMarketIndex","type":"u16","index":false},{"name":"perpMarketIndex","type":"u16","index":false},{"name":"userIfFactor","type":"u32","index":false},{"name":"totalIfFactor","type":"u32","index":false},{"name":"vaultAmountBefore","type":"u64","index":false},{"name":"insuranceVaultAmountBefore","type":"u64","index":false},{"name":"totalIfSharesBefore","type":"u128","index":false},{"name":"totalIfSharesAfter","type":"u128","index":false},{"name":"amount","type":"i64","index":false}]},{"name":"InsuranceFundStakeRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"userAuthority","type":"publicKey","index":false},{"name":"action","type":{"defined":"StakeAction"},"index":false},{"name":"amount","type":"u64","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"insuranceVaultAmountBefore","type":"u64","index":false},{"name":"ifSharesBefore","type":"u128","index":false},{"name":"userIfSharesBefore","type":"u128","index":false},{"name":"totalIfSharesBefore","type":"u128","index":false},{"name":"ifSharesAfter","type":"u128","index":false},{"name":"userIfSharesAfter","type":"u128","index":false},{"name":"totalIfSharesAfter","type":"u128","index":false}]},{"name":"InsuranceFundSwapRecord","fields":[{"name":"rebalanceConfig","type":"publicKey","index":false},{"name":"inIfTotalSharesBefore","type":"u128","index":false},{"name":"outIfTotalSharesBefore","type":"u128","index":false},{"name":"inIfUserSharesBefore","type":"u128","index":false},{"name":"outIfUserSharesBefore","type":"u128","index":false},{"name":"inIfTotalSharesAfter","type":"u128","index":false},{"name":"outIfTotalSharesAfter","type":"u128","index":false},{"name":"inIfUserSharesAfter","type":"u128","index":false},{"name":"outIfUserSharesAfter","type":"u128","index":false},{"name":"ts","type":"i64","index":false},{"name":"inAmount","type":"u64","index":false},{"name":"outAmount","type":"u64","index":false},{"name":"outOraclePrice","type":"u64","index":false},{"name":"outOraclePriceTwap","type":"i64","index":false},{"name":"inVaultAmountBefore","type":"u64","index":false},{"name":"outVaultAmountBefore","type":"u64","index":false},{"name":"inFundVaultAmountAfter","type":"u64","index":false},{"name":"outFundVaultAmountAfter","type":"u64","index":false},{"name":"inMarketIndex","type":"u16","index":false},{"name":"outMarketIndex","type":"u16","index":false}]},{"name":"TransferProtocolIfSharesToRevenuePoolRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"amount","type":"u64","index":false},{"name":"shares","type":"u128","index":false},{"name":"ifVaultAmountBefore","type":"u64","index":false},{"name":"protocolSharesBefore","type":"u128","index":false},{"name":"transferAmount","type":"u64","index":false}]},{"name":"SwapRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"user","type":"publicKey","index":false},{"name":"amountOut","type":"u64","index":false},{"name":"amountIn","type":"u64","index":false},{"name":"outMarketIndex","type":"u16","index":false},{"name":"inMarketIndex","type":"u16","index":false},{"name":"outOraclePrice","type":"i64","index":false},{"name":"inOraclePrice","type":"i64","index":false},{"name":"fee","type":"u64","index":false}]},{"name":"SpotMarketVaultDepositRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"depositBalance","type":"u128","index":false},{"name":"cumulativeDepositInterestBefore","type":"u128","index":false},{"name":"cumulativeDepositInterestAfter","type":"u128","index":false},{"name":"depositTokenAmountBefore","type":"u64","index":false},{"name":"amount","type":"u64","index":false}]},{"name":"DeleteUserRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"userAuthority","type":"publicKey","index":false},{"name":"user","type":"publicKey","index":false},{"name":"subAccountId","type":"u16","index":false},{"name":"keeper","type":{"option":"publicKey"},"index":false}]},{"name":"FuelSweepRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"userStatsFuelInsurance","type":"u32","index":false},{"name":"userStatsFuelDeposits","type":"u32","index":false},{"name":"userStatsFuelBorrows","type":"u32","index":false},{"name":"userStatsFuelPositions","type":"u32","index":false},{"name":"userStatsFuelTaker","type":"u32","index":false},{"name":"userStatsFuelMaker","type":"u32","index":false},{"name":"fuelOverflowFuelInsurance","type":"u128","index":false},{"name":"fuelOverflowFuelDeposits","type":"u128","index":false},{"name":"fuelOverflowFuelBorrows","type":"u128","index":false},{"name":"fuelOverflowFuelPositions","type":"u128","index":false},{"name":"fuelOverflowFuelTaker","type":"u128","index":false},{"name":"fuelOverflowFuelMaker","type":"u128","index":false}]},{"name":"FuelSeasonRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"fuelInsurance","type":"u128","index":false},{"name":"fuelDeposits","type":"u128","index":false},{"name":"fuelBorrows","type":"u128","index":false},{"name":"fuelPositions","type":"u128","index":false},{"name":"fuelTaker","type":"u128","index":false},{"name":"fuelMaker","type":"u128","index":false},{"name":"fuelTotal","type":"u128","index":false}]},{"name":"RevenueShareSettleRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"builder","type":{"option":"publicKey"},"index":false},{"name":"referrer","type":{"option":"publicKey"},"index":false},{"name":"feeSettled","type":"u64","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"marketType","type":{"defined":"MarketType"},"index":false},{"name":"builderSubAccountId","type":"u16","index":false},{"name":"builderTotalReferrerRewards","type":"u64","index":false},{"name":"builderTotalBuilderRewards","type":"u64","index":false}]},{"name":"LPSettleRecord","fields":[{"name":"recordId","type":"u64","index":false},{"name":"lastTs","type":"i64","index":false},{"name":"lastSlot","type":"u64","index":false},{"name":"ts","type":"i64","index":false},{"name":"slot","type":"u64","index":false},{"name":"perpMarketIndex","type":"u16","index":false},{"name":"settleToLpAmount","type":"i64","index":false},{"name":"perpAmmPnlDelta","type":"i64","index":false},{"name":"perpAmmExFeeDelta","type":"i64","index":false},{"name":"lpAum","type":"u128","index":false},{"name":"lpPrice","type":"u128","index":false},{"name":"lpPool","type":"publicKey","index":false}]},{"name":"LPSwapRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"slot","type":"u64","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"outAmount","type":"u128","index":false},{"name":"inAmount","type":"u128","index":false},{"name":"outFee","type":"i128","index":false},{"name":"inFee","type":"i128","index":false},{"name":"outSpotMarketIndex","type":"u16","index":false},{"name":"inSpotMarketIndex","type":"u16","index":false},{"name":"outConstituentIndex","type":"u16","index":false},{"name":"inConstituentIndex","type":"u16","index":false},{"name":"outOraclePrice","type":"i64","index":false},{"name":"inOraclePrice","type":"i64","index":false},{"name":"lastAum","type":"u128","index":false},{"name":"lastAumSlot","type":"u64","index":false},{"name":"inMarketCurrentWeight","type":"i64","index":false},{"name":"outMarketCurrentWeight","type":"i64","index":false},{"name":"inMarketTargetWeight","type":"i64","index":false},{"name":"outMarketTargetWeight","type":"i64","index":false},{"name":"inSwapId","type":"u64","index":false},{"name":"outSwapId","type":"u64","index":false},{"name":"lpPool","type":"publicKey","index":false}]},{"name":"LPMintRedeemRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"slot","type":"u64","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"description","type":"u8","index":false},{"name":"amount","type":"u128","index":false},{"name":"fee","type":"i128","index":false},{"name":"spotMarketIndex","type":"u16","index":false},{"name":"constituentIndex","type":"u16","index":false},{"name":"oraclePrice","type":"i64","index":false},{"name":"mint","type":"publicKey","index":false},{"name":"lpAmount","type":"u64","index":false},{"name":"lpFee","type":"i64","index":false},{"name":"lpPrice","type":"u128","index":false},{"name":"mintRedeemId","type":"u64","index":false},{"name":"lastAum","type":"u128","index":false},{"name":"lastAumSlot","type":"u64","index":false},{"name":"inMarketCurrentWeight","type":"i64","index":false},{"name":"inMarketTargetWeight","type":"i64","index":false},{"name":"lpPool","type":"publicKey","index":false}]},{"name":"LPBorrowLendDepositRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"slot","type":"u64","index":false},{"name":"spotMarketIndex","type":"u16","index":false},{"name":"constituentIndex","type":"u16","index":false},{"name":"direction","type":{"defined":"DepositDirection"},"index":false},{"name":"tokenBalance","type":"i64","index":false},{"name":"lastTokenBalance","type":"i64","index":false},{"name":"interestAccruedTokenAmount","type":"i64","index":false},{"name":"amountDepositWithdraw","type":"u64","index":false},{"name":"lpPool","type":"publicKey","index":false}]}],"errors":[{"code":6000,"name":"InvalidSpotMarketAuthority","msg":"Invalid Spot Market Authority"},{"code":6001,"name":"InvalidInsuranceFundAuthority","msg":"Clearing house not insurance fund authority"},{"code":6002,"name":"InsufficientDeposit","msg":"Insufficient deposit"},{"code":6003,"name":"InsufficientCollateral","msg":"Insufficient collateral"},{"code":6004,"name":"SufficientCollateral","msg":"Sufficient collateral"},{"code":6005,"name":"MaxNumberOfPositions","msg":"Max number of positions taken"},{"code":6006,"name":"AdminControlsPricesDisabled","msg":"Admin Controls Prices Disabled"},{"code":6007,"name":"MarketDelisted","msg":"Market Delisted"},{"code":6008,"name":"MarketIndexAlreadyInitialized","msg":"Market Index Already Initialized"},{"code":6009,"name":"UserAccountAndUserPositionsAccountMismatch","msg":"User Account And User Positions Account Mismatch"},{"code":6010,"name":"UserHasNoPositionInMarket","msg":"User Has No Position In Market"},{"code":6011,"name":"InvalidInitialPeg","msg":"Invalid Initial Peg"},{"code":6012,"name":"InvalidRepegRedundant","msg":"AMM repeg already configured with amt given"},{"code":6013,"name":"InvalidRepegDirection","msg":"AMM repeg incorrect repeg direction"},{"code":6014,"name":"InvalidRepegProfitability","msg":"AMM repeg out of bounds pnl"},{"code":6015,"name":"SlippageOutsideLimit","msg":"Slippage Outside Limit Price"},{"code":6016,"name":"OrderSizeTooSmall","msg":"Order Size Too Small"},{"code":6017,"name":"InvalidUpdateK","msg":"Price change too large when updating K"},{"code":6018,"name":"AdminWithdrawTooLarge","msg":"Admin tried to withdraw amount larger than fees collected"},{"code":6019,"name":"MathError","msg":"Math Error"},{"code":6020,"name":"BnConversionError","msg":"Conversion to u128/u64 failed with an overflow or underflow"},{"code":6021,"name":"ClockUnavailable","msg":"Clock unavailable"},{"code":6022,"name":"UnableToLoadOracle","msg":"Unable To Load Oracles"},{"code":6023,"name":"PriceBandsBreached","msg":"Price Bands Breached"},{"code":6024,"name":"ExchangePaused","msg":"Exchange is paused"},{"code":6025,"name":"InvalidWhitelistToken","msg":"Invalid whitelist token"},{"code":6026,"name":"WhitelistTokenNotFound","msg":"Whitelist token not found"},{"code":6027,"name":"InvalidDiscountToken","msg":"Invalid discount token"},{"code":6028,"name":"DiscountTokenNotFound","msg":"Discount token not found"},{"code":6029,"name":"ReferrerNotFound","msg":"Referrer not found"},{"code":6030,"name":"ReferrerStatsNotFound","msg":"ReferrerNotFound"},{"code":6031,"name":"ReferrerMustBeWritable","msg":"ReferrerMustBeWritable"},{"code":6032,"name":"ReferrerStatsMustBeWritable","msg":"ReferrerMustBeWritable"},{"code":6033,"name":"ReferrerAndReferrerStatsAuthorityUnequal","msg":"ReferrerAndReferrerStatsAuthorityUnequal"},{"code":6034,"name":"InvalidReferrer","msg":"InvalidReferrer"},{"code":6035,"name":"InvalidOracle","msg":"InvalidOracle"},{"code":6036,"name":"OracleNotFound","msg":"OracleNotFound"},{"code":6037,"name":"LiquidationsBlockedByOracle","msg":"Liquidations Blocked By Oracle"},{"code":6038,"name":"MaxDeposit","msg":"Can not deposit more than max deposit"},{"code":6039,"name":"CantDeleteUserWithCollateral","msg":"Can not delete user that still has collateral"},{"code":6040,"name":"InvalidFundingProfitability","msg":"AMM funding out of bounds pnl"},{"code":6041,"name":"CastingFailure","msg":"Casting Failure"},{"code":6042,"name":"InvalidOrder","msg":"InvalidOrder"},{"code":6043,"name":"InvalidOrderMaxTs","msg":"InvalidOrderMaxTs"},{"code":6044,"name":"InvalidOrderMarketType","msg":"InvalidOrderMarketType"},{"code":6045,"name":"InvalidOrderForInitialMarginReq","msg":"InvalidOrderForInitialMarginReq"},{"code":6046,"name":"InvalidOrderNotRiskReducing","msg":"InvalidOrderNotRiskReducing"},{"code":6047,"name":"InvalidOrderSizeTooSmall","msg":"InvalidOrderSizeTooSmall"},{"code":6048,"name":"InvalidOrderNotStepSizeMultiple","msg":"InvalidOrderNotStepSizeMultiple"},{"code":6049,"name":"InvalidOrderBaseQuoteAsset","msg":"InvalidOrderBaseQuoteAsset"},{"code":6050,"name":"InvalidOrderIOC","msg":"InvalidOrderIOC"},{"code":6051,"name":"InvalidOrderPostOnly","msg":"InvalidOrderPostOnly"},{"code":6052,"name":"InvalidOrderIOCPostOnly","msg":"InvalidOrderIOCPostOnly"},{"code":6053,"name":"InvalidOrderTrigger","msg":"InvalidOrderTrigger"},{"code":6054,"name":"InvalidOrderAuction","msg":"InvalidOrderAuction"},{"code":6055,"name":"InvalidOrderOracleOffset","msg":"InvalidOrderOracleOffset"},{"code":6056,"name":"InvalidOrderMinOrderSize","msg":"InvalidOrderMinOrderSize"},{"code":6057,"name":"PlacePostOnlyLimitFailure","msg":"Failed to Place Post-Only Limit Order"},{"code":6058,"name":"UserHasNoOrder","msg":"User has no order"},{"code":6059,"name":"OrderAmountTooSmall","msg":"Order Amount Too Small"},{"code":6060,"name":"MaxNumberOfOrders","msg":"Max number of orders taken"},{"code":6061,"name":"OrderDoesNotExist","msg":"Order does not exist"},{"code":6062,"name":"OrderNotOpen","msg":"Order not open"},{"code":6063,"name":"FillOrderDidNotUpdateState","msg":"FillOrderDidNotUpdateState"},{"code":6064,"name":"ReduceOnlyOrderIncreasedRisk","msg":"Reduce only order increased risk"},{"code":6065,"name":"UnableToLoadAccountLoader","msg":"Unable to load AccountLoader"},{"code":6066,"name":"TradeSizeTooLarge","msg":"Trade Size Too Large"},{"code":6067,"name":"UserCantReferThemselves","msg":"User cant refer themselves"},{"code":6068,"name":"DidNotReceiveExpectedReferrer","msg":"Did not receive expected referrer"},{"code":6069,"name":"CouldNotDeserializeReferrer","msg":"Could not deserialize referrer"},{"code":6070,"name":"CouldNotDeserializeReferrerStats","msg":"Could not deserialize referrer stats"},{"code":6071,"name":"UserOrderIdAlreadyInUse","msg":"User Order Id Already In Use"},{"code":6072,"name":"NoPositionsLiquidatable","msg":"No positions liquidatable"},{"code":6073,"name":"InvalidMarginRatio","msg":"Invalid Margin Ratio"},{"code":6074,"name":"CantCancelPostOnlyOrder","msg":"Cant Cancel Post Only Order"},{"code":6075,"name":"InvalidOracleOffset","msg":"InvalidOracleOffset"},{"code":6076,"name":"CantExpireOrders","msg":"CantExpireOrders"},{"code":6077,"name":"CouldNotLoadMarketData","msg":"CouldNotLoadMarketData"},{"code":6078,"name":"PerpMarketNotFound","msg":"PerpMarketNotFound"},{"code":6079,"name":"InvalidMarketAccount","msg":"InvalidMarketAccount"},{"code":6080,"name":"UnableToLoadPerpMarketAccount","msg":"UnableToLoadMarketAccount"},{"code":6081,"name":"MarketWrongMutability","msg":"MarketWrongMutability"},{"code":6082,"name":"UnableToCastUnixTime","msg":"UnableToCastUnixTime"},{"code":6083,"name":"CouldNotFindSpotPosition","msg":"CouldNotFindSpotPosition"},{"code":6084,"name":"NoSpotPositionAvailable","msg":"NoSpotPositionAvailable"},{"code":6085,"name":"InvalidSpotMarketInitialization","msg":"InvalidSpotMarketInitialization"},{"code":6086,"name":"CouldNotLoadSpotMarketData","msg":"CouldNotLoadSpotMarketData"},{"code":6087,"name":"SpotMarketNotFound","msg":"SpotMarketNotFound"},{"code":6088,"name":"InvalidSpotMarketAccount","msg":"InvalidSpotMarketAccount"},{"code":6089,"name":"UnableToLoadSpotMarketAccount","msg":"UnableToLoadSpotMarketAccount"},{"code":6090,"name":"SpotMarketWrongMutability","msg":"SpotMarketWrongMutability"},{"code":6091,"name":"SpotMarketInterestNotUpToDate","msg":"SpotInterestNotUpToDate"},{"code":6092,"name":"SpotMarketInsufficientDeposits","msg":"SpotMarketInsufficientDeposits"},{"code":6093,"name":"UserMustSettleTheirOwnPositiveUnsettledPNL","msg":"UserMustSettleTheirOwnPositiveUnsettledPNL"},{"code":6094,"name":"CantUpdatePoolBalanceType","msg":"CantUpdatePoolBalanceType"},{"code":6095,"name":"InsufficientCollateralForSettlingPNL","msg":"InsufficientCollateralForSettlingPNL"},{"code":6096,"name":"AMMNotUpdatedInSameSlot","msg":"AMMNotUpdatedInSameSlot"},{"code":6097,"name":"AuctionNotComplete","msg":"AuctionNotComplete"},{"code":6098,"name":"MakerNotFound","msg":"MakerNotFound"},{"code":6099,"name":"MakerStatsNotFound","msg":"MakerNotFound"},{"code":6100,"name":"MakerMustBeWritable","msg":"MakerMustBeWritable"},{"code":6101,"name":"MakerStatsMustBeWritable","msg":"MakerMustBeWritable"},{"code":6102,"name":"MakerOrderNotFound","msg":"MakerOrderNotFound"},{"code":6103,"name":"CouldNotDeserializeMaker","msg":"CouldNotDeserializeMaker"},{"code":6104,"name":"CouldNotDeserializeMakerStats","msg":"CouldNotDeserializeMaker"},{"code":6105,"name":"AuctionPriceDoesNotSatisfyMaker","msg":"AuctionPriceDoesNotSatisfyMaker"},{"code":6106,"name":"MakerCantFulfillOwnOrder","msg":"MakerCantFulfillOwnOrder"},{"code":6107,"name":"MakerOrderMustBePostOnly","msg":"MakerOrderMustBePostOnly"},{"code":6108,"name":"CantMatchTwoPostOnlys","msg":"CantMatchTwoPostOnlys"},{"code":6109,"name":"OrderBreachesOraclePriceLimits","msg":"OrderBreachesOraclePriceLimits"},{"code":6110,"name":"OrderMustBeTriggeredFirst","msg":"OrderMustBeTriggeredFirst"},{"code":6111,"name":"OrderNotTriggerable","msg":"OrderNotTriggerable"},{"code":6112,"name":"OrderDidNotSatisfyTriggerCondition","msg":"OrderDidNotSatisfyTriggerCondition"},{"code":6113,"name":"PositionAlreadyBeingLiquidated","msg":"PositionAlreadyBeingLiquidated"},{"code":6114,"name":"PositionDoesntHaveOpenPositionOrOrders","msg":"PositionDoesntHaveOpenPositionOrOrders"},{"code":6115,"name":"AllOrdersAreAlreadyLiquidations","msg":"AllOrdersAreAlreadyLiquidations"},{"code":6116,"name":"CantCancelLiquidationOrder","msg":"CantCancelLiquidationOrder"},{"code":6117,"name":"UserIsBeingLiquidated","msg":"UserIsBeingLiquidated"},{"code":6118,"name":"LiquidationsOngoing","msg":"LiquidationsOngoing"},{"code":6119,"name":"WrongSpotBalanceType","msg":"WrongSpotBalanceType"},{"code":6120,"name":"UserCantLiquidateThemself","msg":"UserCantLiquidateThemself"},{"code":6121,"name":"InvalidPerpPositionToLiquidate","msg":"InvalidPerpPositionToLiquidate"},{"code":6122,"name":"InvalidBaseAssetAmountForLiquidatePerp","msg":"InvalidBaseAssetAmountForLiquidatePerp"},{"code":6123,"name":"InvalidPositionLastFundingRate","msg":"InvalidPositionLastFundingRate"},{"code":6124,"name":"InvalidPositionDelta","msg":"InvalidPositionDelta"},{"code":6125,"name":"UserBankrupt","msg":"UserBankrupt"},{"code":6126,"name":"UserNotBankrupt","msg":"UserNotBankrupt"},{"code":6127,"name":"UserHasInvalidBorrow","msg":"UserHasInvalidBorrow"},{"code":6128,"name":"DailyWithdrawLimit","msg":"DailyWithdrawLimit"},{"code":6129,"name":"DefaultError","msg":"DefaultError"},{"code":6130,"name":"InsufficientLPTokens","msg":"Insufficient LP tokens"},{"code":6131,"name":"CantLPWithPerpPosition","msg":"Cant LP with a market position"},{"code":6132,"name":"UnableToBurnLPTokens","msg":"Unable to burn LP tokens"},{"code":6133,"name":"TryingToRemoveLiquidityTooFast","msg":"Trying to remove liqudity too fast after adding it"},{"code":6134,"name":"InvalidSpotMarketVault","msg":"Invalid Spot Market Vault"},{"code":6135,"name":"InvalidSpotMarketState","msg":"Invalid Spot Market State"},{"code":6136,"name":"InvalidSerumProgram","msg":"InvalidSerumProgram"},{"code":6137,"name":"InvalidSerumMarket","msg":"InvalidSerumMarket"},{"code":6138,"name":"InvalidSerumBids","msg":"InvalidSerumBids"},{"code":6139,"name":"InvalidSerumAsks","msg":"InvalidSerumAsks"},{"code":6140,"name":"InvalidSerumOpenOrders","msg":"InvalidSerumOpenOrders"},{"code":6141,"name":"FailedSerumCPI","msg":"FailedSerumCPI"},{"code":6142,"name":"FailedToFillOnExternalMarket","msg":"FailedToFillOnExternalMarket"},{"code":6143,"name":"InvalidFulfillmentConfig","msg":"InvalidFulfillmentConfig"},{"code":6144,"name":"InvalidFeeStructure","msg":"InvalidFeeStructure"},{"code":6145,"name":"InsufficientIFShares","msg":"Insufficient IF shares"},{"code":6146,"name":"MarketActionPaused","msg":"the Market has paused this action"},{"code":6147,"name":"MarketPlaceOrderPaused","msg":"the Market status doesnt allow placing orders"},{"code":6148,"name":"MarketFillOrderPaused","msg":"the Market status doesnt allow filling orders"},{"code":6149,"name":"MarketWithdrawPaused","msg":"the Market status doesnt allow withdraws"},{"code":6150,"name":"ProtectedAssetTierViolation","msg":"Action violates the Protected Asset Tier rules"},{"code":6151,"name":"IsolatedAssetTierViolation","msg":"Action violates the Isolated Asset Tier rules"},{"code":6152,"name":"UserCantBeDeleted","msg":"User Cant Be Deleted"},{"code":6153,"name":"ReduceOnlyWithdrawIncreasedRisk","msg":"Reduce Only Withdraw Increased Risk"},{"code":6154,"name":"MaxOpenInterest","msg":"Max Open Interest"},{"code":6155,"name":"CantResolvePerpBankruptcy","msg":"Cant Resolve Perp Bankruptcy"},{"code":6156,"name":"LiquidationDoesntSatisfyLimitPrice","msg":"Liquidation Doesnt Satisfy Limit Price"},{"code":6157,"name":"MarginTradingDisabled","msg":"Margin Trading Disabled"},{"code":6158,"name":"InvalidMarketStatusToSettlePnl","msg":"Invalid Market Status to Settle Perp Pnl"},{"code":6159,"name":"PerpMarketNotInSettlement","msg":"PerpMarketNotInSettlement"},{"code":6160,"name":"PerpMarketNotInReduceOnly","msg":"PerpMarketNotInReduceOnly"},{"code":6161,"name":"PerpMarketSettlementBufferNotReached","msg":"PerpMarketSettlementBufferNotReached"},{"code":6162,"name":"PerpMarketSettlementUserHasOpenOrders","msg":"PerpMarketSettlementUserHasOpenOrders"},{"code":6163,"name":"PerpMarketSettlementUserHasActiveLP","msg":"PerpMarketSettlementUserHasActiveLP"},{"code":6164,"name":"UnableToSettleExpiredUserPosition","msg":"UnableToSettleExpiredUserPosition"},{"code":6165,"name":"UnequalMarketIndexForSpotTransfer","msg":"UnequalMarketIndexForSpotTransfer"},{"code":6166,"name":"InvalidPerpPositionDetected","msg":"InvalidPerpPositionDetected"},{"code":6167,"name":"InvalidSpotPositionDetected","msg":"InvalidSpotPositionDetected"},{"code":6168,"name":"InvalidAmmDetected","msg":"InvalidAmmDetected"},{"code":6169,"name":"InvalidAmmForFillDetected","msg":"InvalidAmmForFillDetected"},{"code":6170,"name":"InvalidAmmLimitPriceOverride","msg":"InvalidAmmLimitPriceOverride"},{"code":6171,"name":"InvalidOrderFillPrice","msg":"InvalidOrderFillPrice"},{"code":6172,"name":"SpotMarketBalanceInvariantViolated","msg":"SpotMarketBalanceInvariantViolated"},{"code":6173,"name":"SpotMarketVaultInvariantViolated","msg":"SpotMarketVaultInvariantViolated"},{"code":6174,"name":"InvalidPDA","msg":"InvalidPDA"},{"code":6175,"name":"InvalidPDASigner","msg":"InvalidPDASigner"},{"code":6176,"name":"RevenueSettingsCannotSettleToIF","msg":"RevenueSettingsCannotSettleToIF"},{"code":6177,"name":"NoRevenueToSettleToIF","msg":"NoRevenueToSettleToIF"},{"code":6178,"name":"NoAmmPerpPnlDeficit","msg":"NoAmmPerpPnlDeficit"},{"code":6179,"name":"SufficientPerpPnlPool","msg":"SufficientPerpPnlPool"},{"code":6180,"name":"InsufficientPerpPnlPool","msg":"InsufficientPerpPnlPool"},{"code":6181,"name":"PerpPnlDeficitBelowThreshold","msg":"PerpPnlDeficitBelowThreshold"},{"code":6182,"name":"MaxRevenueWithdrawPerPeriodReached","msg":"MaxRevenueWithdrawPerPeriodReached"},{"code":6183,"name":"MaxIFWithdrawReached","msg":"InvalidSpotPositionDetected"},{"code":6184,"name":"NoIFWithdrawAvailable","msg":"NoIFWithdrawAvailable"},{"code":6185,"name":"InvalidIFUnstake","msg":"InvalidIFUnstake"},{"code":6186,"name":"InvalidIFUnstakeSize","msg":"InvalidIFUnstakeSize"},{"code":6187,"name":"InvalidIFUnstakeCancel","msg":"InvalidIFUnstakeCancel"},{"code":6188,"name":"InvalidIFForNewStakes","msg":"InvalidIFForNewStakes"},{"code":6189,"name":"InvalidIFRebase","msg":"InvalidIFRebase"},{"code":6190,"name":"InvalidInsuranceUnstakeSize","msg":"InvalidInsuranceUnstakeSize"},{"code":6191,"name":"InvalidOrderLimitPrice","msg":"InvalidOrderLimitPrice"},{"code":6192,"name":"InvalidIFDetected","msg":"InvalidIFDetected"},{"code":6193,"name":"InvalidAmmMaxSpreadDetected","msg":"InvalidAmmMaxSpreadDetected"},{"code":6194,"name":"InvalidConcentrationCoef","msg":"InvalidConcentrationCoef"},{"code":6195,"name":"InvalidSrmVault","msg":"InvalidSrmVault"},{"code":6196,"name":"InvalidVaultOwner","msg":"InvalidVaultOwner"},{"code":6197,"name":"InvalidMarketStatusForFills","msg":"InvalidMarketStatusForFills"},{"code":6198,"name":"IFWithdrawRequestInProgress","msg":"IFWithdrawRequestInProgress"},{"code":6199,"name":"NoIFWithdrawRequestInProgress","msg":"NoIFWithdrawRequestInProgress"},{"code":6200,"name":"IFWithdrawRequestTooSmall","msg":"IFWithdrawRequestTooSmall"},{"code":6201,"name":"IncorrectSpotMarketAccountPassed","msg":"IncorrectSpotMarketAccountPassed"},{"code":6202,"name":"BlockchainClockInconsistency","msg":"BlockchainClockInconsistency"},{"code":6203,"name":"InvalidIFSharesDetected","msg":"InvalidIFSharesDetected"},{"code":6204,"name":"NewLPSizeTooSmall","msg":"NewLPSizeTooSmall"},{"code":6205,"name":"MarketStatusInvalidForNewLP","msg":"MarketStatusInvalidForNewLP"},{"code":6206,"name":"InvalidMarkTwapUpdateDetected","msg":"InvalidMarkTwapUpdateDetected"},{"code":6207,"name":"MarketSettlementAttemptOnActiveMarket","msg":"MarketSettlementAttemptOnActiveMarket"},{"code":6208,"name":"MarketSettlementRequiresSettledLP","msg":"MarketSettlementRequiresSettledLP"},{"code":6209,"name":"MarketSettlementAttemptTooEarly","msg":"MarketSettlementAttemptTooEarly"},{"code":6210,"name":"MarketSettlementTargetPriceInvalid","msg":"MarketSettlementTargetPriceInvalid"},{"code":6211,"name":"UnsupportedSpotMarket","msg":"UnsupportedSpotMarket"},{"code":6212,"name":"SpotOrdersDisabled","msg":"SpotOrdersDisabled"},{"code":6213,"name":"MarketBeingInitialized","msg":"Market Being Initialized"},{"code":6214,"name":"InvalidUserSubAccountId","msg":"Invalid Sub Account Id"},{"code":6215,"name":"InvalidTriggerOrderCondition","msg":"Invalid Trigger Order Condition"},{"code":6216,"name":"InvalidSpotPosition","msg":"Invalid Spot Position"},{"code":6217,"name":"CantTransferBetweenSameUserAccount","msg":"Cant transfer between same user account"},{"code":6218,"name":"InvalidPerpPosition","msg":"Invalid Perp Position"},{"code":6219,"name":"UnableToGetLimitPrice","msg":"Unable To Get Limit Price"},{"code":6220,"name":"InvalidLiquidation","msg":"Invalid Liquidation"},{"code":6221,"name":"SpotFulfillmentConfigDisabled","msg":"Spot Fulfillment Config Disabled"},{"code":6222,"name":"InvalidMaker","msg":"Invalid Maker"},{"code":6223,"name":"FailedUnwrap","msg":"Failed Unwrap"},{"code":6224,"name":"MaxNumberOfUsers","msg":"Max Number Of Users"},{"code":6225,"name":"InvalidOracleForSettlePnl","msg":"InvalidOracleForSettlePnl"},{"code":6226,"name":"MarginOrdersOpen","msg":"MarginOrdersOpen"},{"code":6227,"name":"TierViolationLiquidatingPerpPnl","msg":"TierViolationLiquidatingPerpPnl"},{"code":6228,"name":"CouldNotLoadUserData","msg":"CouldNotLoadUserData"},{"code":6229,"name":"UserWrongMutability","msg":"UserWrongMutability"},{"code":6230,"name":"InvalidUserAccount","msg":"InvalidUserAccount"},{"code":6231,"name":"CouldNotLoadUserStatsData","msg":"CouldNotLoadUserData"},{"code":6232,"name":"UserStatsWrongMutability","msg":"UserWrongMutability"},{"code":6233,"name":"InvalidUserStatsAccount","msg":"InvalidUserAccount"},{"code":6234,"name":"UserNotFound","msg":"UserNotFound"},{"code":6235,"name":"UnableToLoadUserAccount","msg":"UnableToLoadUserAccount"},{"code":6236,"name":"UserStatsNotFound","msg":"UserStatsNotFound"},{"code":6237,"name":"UnableToLoadUserStatsAccount","msg":"UnableToLoadUserStatsAccount"},{"code":6238,"name":"UserNotInactive","msg":"User Not Inactive"},{"code":6239,"name":"RevertFill","msg":"RevertFill"},{"code":6240,"name":"InvalidMarketAccountforDeletion","msg":"Invalid MarketAccount for Deletion"},{"code":6241,"name":"InvalidSpotFulfillmentParams","msg":"Invalid Spot Fulfillment Params"},{"code":6242,"name":"FailedToGetMint","msg":"Failed to Get Mint"},{"code":6243,"name":"FailedPhoenixCPI","msg":"FailedPhoenixCPI"},{"code":6244,"name":"FailedToDeserializePhoenixMarket","msg":"FailedToDeserializePhoenixMarket"},{"code":6245,"name":"InvalidPricePrecision","msg":"InvalidPricePrecision"},{"code":6246,"name":"InvalidPhoenixProgram","msg":"InvalidPhoenixProgram"},{"code":6247,"name":"InvalidPhoenixMarket","msg":"InvalidPhoenixMarket"},{"code":6248,"name":"InvalidSwap","msg":"InvalidSwap"},{"code":6249,"name":"SwapLimitPriceBreached","msg":"SwapLimitPriceBreached"},{"code":6250,"name":"SpotMarketReduceOnly","msg":"SpotMarketReduceOnly"},{"code":6251,"name":"FundingWasNotUpdated","msg":"FundingWasNotUpdated"},{"code":6252,"name":"ImpossibleFill","msg":"ImpossibleFill"},{"code":6253,"name":"CantUpdatePerpBidAskTwap","msg":"CantUpdatePerpBidAskTwap"},{"code":6254,"name":"UserReduceOnly","msg":"UserReduceOnly"},{"code":6255,"name":"InvalidMarginCalculation","msg":"InvalidMarginCalculation"},{"code":6256,"name":"CantPayUserInitFee","msg":"CantPayUserInitFee"},{"code":6257,"name":"CantReclaimRent","msg":"CantReclaimRent"},{"code":6258,"name":"InsuranceFundOperationPaused","msg":"InsuranceFundOperationPaused"},{"code":6259,"name":"NoUnsettledPnl","msg":"NoUnsettledPnl"},{"code":6260,"name":"PnlPoolCantSettleUser","msg":"PnlPoolCantSettleUser"},{"code":6261,"name":"OracleNonPositive","msg":"OracleInvalid"},{"code":6262,"name":"OracleTooVolatile","msg":"OracleTooVolatile"},{"code":6263,"name":"OracleTooUncertain","msg":"OracleTooUncertain"},{"code":6264,"name":"OracleStaleForMargin","msg":"OracleStaleForMargin"},{"code":6265,"name":"OracleInsufficientDataPoints","msg":"OracleInsufficientDataPoints"},{"code":6266,"name":"OracleStaleForAMM","msg":"OracleStaleForAMM"},{"code":6267,"name":"UnableToParsePullOracleMessage","msg":"Unable to parse pull oracle message"},{"code":6268,"name":"MaxBorrows","msg":"Can not borow more than max borrows"},{"code":6269,"name":"OracleUpdatesNotMonotonic","msg":"Updates must be monotonically increasing"},{"code":6270,"name":"OraclePriceFeedMessageMismatch","msg":"Trying to update price feed with the wrong feed id"},{"code":6271,"name":"OracleUnsupportedMessageType","msg":"The message in the update must be a PriceFeedMessage"},{"code":6272,"name":"OracleDeserializeMessageFailed","msg":"Could not deserialize the message in the update"},{"code":6273,"name":"OracleWrongGuardianSetOwner","msg":"Wrong guardian set owner in update price atomic"},{"code":6274,"name":"OracleWrongWriteAuthority","msg":"Oracle post update atomic price feed account must be drift program"},{"code":6275,"name":"OracleWrongVaaOwner","msg":"Oracle vaa owner must be wormhole program"},{"code":6276,"name":"OracleTooManyPriceAccountUpdates","msg":"Multi updates must have 2 or fewer accounts passed in remaining accounts"},{"code":6277,"name":"OracleMismatchedVaaAndPriceUpdates","msg":"Don't have the same remaining accounts number and pyth updates left"},{"code":6278,"name":"OracleBadRemainingAccountPublicKey","msg":"Remaining account passed does not match oracle update derived pda"},{"code":6279,"name":"FailedOpenbookV2CPI","msg":"FailedOpenbookV2CPI"},{"code":6280,"name":"InvalidOpenbookV2Program","msg":"InvalidOpenbookV2Program"},{"code":6281,"name":"InvalidOpenbookV2Market","msg":"InvalidOpenbookV2Market"},{"code":6282,"name":"NonZeroTransferFee","msg":"Non zero transfer fee"},{"code":6283,"name":"LiquidationOrderFailedToFill","msg":"Liquidation order failed to fill"},{"code":6284,"name":"InvalidPredictionMarketOrder","msg":"Invalid prediction market order"},{"code":6285,"name":"InvalidVerificationIxIndex","msg":"Ed25519 Ix must be before place and make SignedMsg order ix"},{"code":6286,"name":"SigVerificationFailed","msg":"SignedMsg message verificaiton failed"},{"code":6287,"name":"MismatchedSignedMsgOrderParamsMarketIndex","msg":"Market index mismatched b/w taker and maker SignedMsg order params"},{"code":6288,"name":"InvalidSignedMsgOrderParam","msg":"Invalid SignedMsg order param"},{"code":6289,"name":"PlaceAndTakeOrderSuccessConditionFailed","msg":"Place and take order success condition failed"},{"code":6290,"name":"InvalidHighLeverageModeConfig","msg":"Invalid High Leverage Mode Config"},{"code":6291,"name":"InvalidRFQUserAccount","msg":"Invalid RFQ User Account"},{"code":6292,"name":"RFQUserAccountWrongMutability","msg":"RFQUserAccount should be mutable"},{"code":6293,"name":"RFQUserAccountFull","msg":"RFQUserAccount has too many active RFQs"},{"code":6294,"name":"RFQOrderNotFilled","msg":"RFQ order not filled as expected"},{"code":6295,"name":"InvalidRFQOrder","msg":"RFQ orders must be jit makers"},{"code":6296,"name":"InvalidRFQMatch","msg":"RFQ matches must be valid"},{"code":6297,"name":"InvalidSignedMsgUserAccount","msg":"Invalid SignedMsg user account"},{"code":6298,"name":"SignedMsgUserAccountWrongMutability","msg":"SignedMsg account wrong mutability"},{"code":6299,"name":"SignedMsgUserOrdersAccountFull","msg":"SignedMsgUserAccount has too many active orders"},{"code":6300,"name":"SignedMsgOrderDoesNotExist","msg":"Order with SignedMsg uuid does not exist"},{"code":6301,"name":"InvalidSignedMsgOrderId","msg":"SignedMsg order id cannot be 0s"},{"code":6302,"name":"InvalidPoolId","msg":"Invalid pool id"},{"code":6303,"name":"InvalidProtectedMakerModeConfig","msg":"Invalid Protected Maker Mode Config"},{"code":6304,"name":"InvalidPythLazerStorageOwner","msg":"Invalid pyth lazer storage owner"},{"code":6305,"name":"UnverifiedPythLazerMessage","msg":"Verification of pyth lazer message failed"},{"code":6306,"name":"InvalidPythLazerMessage","msg":"Invalid pyth lazer message"},{"code":6307,"name":"PythLazerMessagePriceFeedMismatch","msg":"Pyth lazer message does not correspond to correct fed id"},{"code":6308,"name":"InvalidLiquidateSpotWithSwap","msg":"InvalidLiquidateSpotWithSwap"},{"code":6309,"name":"SignedMsgUserContextUserMismatch","msg":"User in SignedMsg message does not match user in ix context"},{"code":6310,"name":"UserFuelOverflowThresholdNotMet","msg":"User fuel overflow threshold not met"},{"code":6311,"name":"FuelOverflowAccountNotFound","msg":"FuelOverflow account not found"},{"code":6312,"name":"InvalidTransferPerpPosition","msg":"Invalid Transfer Perp Position"},{"code":6313,"name":"InvalidSignedMsgUserOrdersResize","msg":"Invalid SignedMsgUserOrders resize"},{"code":6314,"name":"CouldNotDeserializeHighLeverageModeConfig","msg":"Could not deserialize high leverage mode config"},{"code":6315,"name":"InvalidIfRebalanceConfig","msg":"Invalid If Rebalance Config"},{"code":6316,"name":"InvalidIfRebalanceSwap","msg":"Invalid If Rebalance Swap"},{"code":6317,"name":"InvalidRevenueShareResize","msg":"Invalid RevenueShare resize"},{"code":6318,"name":"BuilderRevoked","msg":"Builder has been revoked"},{"code":6319,"name":"InvalidBuilderFee","msg":"Builder fee is greater than max fee bps"},{"code":6320,"name":"RevenueShareEscrowAuthorityMismatch","msg":"RevenueShareEscrow authority mismatch"},{"code":6321,"name":"RevenueShareEscrowOrdersAccountFull","msg":"RevenueShareEscrow has too many active orders"},{"code":6322,"name":"InvalidRevenueShareAccount","msg":"Invalid RevenueShareAccount"},{"code":6323,"name":"CannotRevokeBuilderWithOpenOrders","msg":"Cannot revoke builder with open orders"},{"code":6324,"name":"UnableToLoadRevenueShareAccount","msg":"Unable to load builder account"},{"code":6325,"name":"InvalidConstituent","msg":"Invalid Constituent"},{"code":6326,"name":"InvalidAmmConstituentMappingArgument","msg":"Invalid Amm Constituent Mapping argument"},{"code":6327,"name":"ConstituentNotFound","msg":"Constituent not found"},{"code":6328,"name":"ConstituentCouldNotLoad","msg":"Constituent could not load"},{"code":6329,"name":"ConstituentWrongMutability","msg":"Constituent wrong mutability"},{"code":6330,"name":"WrongNumberOfConstituents","msg":"Wrong number of constituents passed to instruction"},{"code":6331,"name":"InsufficientConstituentTokenBalance","msg":"Insufficient constituent token balance"},{"code":6332,"name":"AMMCacheStale","msg":"Amm Cache data too stale"},{"code":6333,"name":"LpPoolAumDelayed","msg":"LP Pool AUM not updated recently"},{"code":6334,"name":"ConstituentOracleStale","msg":"Constituent oracle is stale"},{"code":6335,"name":"LpInvariantFailed","msg":"LP Invariant failed"},{"code":6336,"name":"InvalidConstituentDerivativeWeights","msg":"Invalid constituent derivative weights"},{"code":6337,"name":"MaxDlpAumBreached","msg":"Max DLP AUM Breached"},{"code":6338,"name":"SettleLpPoolDisabled","msg":"Settle Lp Pool Disabled"},{"code":6339,"name":"MintRedeemLpPoolDisabled","msg":"Mint/Redeem Lp Pool Disabled"},{"code":6340,"name":"LpPoolSettleInvariantBreached","msg":"Settlement amount exceeded"},{"code":6341,"name":"InvalidConstituentOperation","msg":"Invalid constituent operation"},{"code":6342,"name":"Unauthorized","msg":"Unauthorized for operation"},{"code":6343,"name":"InvalidLpPoolId","msg":"Invalid Lp Pool Id for Operation"},{"code":6344,"name":"MarketIndexNotFoundAmmCache","msg":"MarketIndexNotFoundAmmCache"}]} \ No newline at end of file diff --git a/idls/fusion_amm.fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9.json b/idls/fusion_amm.fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9.json new file mode 100644 index 0000000..9991644 --- /dev/null +++ b/idls/fusion_amm.fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9.json @@ -0,0 +1 @@ +{"address":"fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9","metadata":{"name":"fusionamm","version":"1.1.4","spec":"0.1.0"},"instructions":[{"name":"close_bundled_position","docs":["Close a bundled position in a FusionPool.","","### Authority","- `position_bundle_authority` - authority that owns the token corresponding to this desired position bundle.","","### Parameters","- `bundle_index` - The bundle index that we'd like to close.","","#### Special Errors","- `InvalidBundleIndex` - If the provided bundle index is out of bounds.","- `ClosePositionNotEmpty` - The provided position account is not empty."],"discriminator":[41,36,216,245,27,85,103,67],"accounts":[{"name":"bundled_position","writable":true},{"name":"position_bundle","writable":true},{"name":"position_bundle_token_account"},{"name":"position_bundle_authority","signer":true},{"name":"receiver","writable":true}],"args":[{"name":"bundle_index","type":"u16"}]},{"name":"close_limit_order","docs":["Close a limit order in a FusionPool. Burns the limit order token in the owner's wallet.","Mint and TokenAccount are based on Token-2022. And Mint account will be also closed.","","### Authority","- \"limit_order_authority\" - The authority that owns the limit order token.","","#### Special Errors","- `CloseLimitOrderNotEmpty` - The provided limit order account is not empty."],"discriminator":[76,124,128,15,213,87,37,250],"accounts":[{"name":"limit_order_authority","signer":true},{"name":"receiver","writable":true},{"name":"limit_order","writable":true},{"name":"limit_order_mint","writable":true},{"name":"limit_order_token_account","writable":true},{"name":"token_2022_program"}],"args":[]},{"name":"close_position","docs":["Close a position in a FusionPool. Burns the position token in the owner's wallet.","Mint and TokenAccount are based on Token-2022. And Mint account will be also closed.","","### Authority","- \"position_authority\" - The authority that owns the position token.","","#### Special Errors","- `ClosePositionNotEmpty` - The provided position account is not empty."],"discriminator":[123,134,81,0,49,68,98,98],"accounts":[{"name":"position_authority","signer":true},{"name":"receiver","writable":true},{"name":"position","writable":true},{"name":"position_mint","writable":true},{"name":"position_token_account","writable":true},{"name":"token_2022_program"}],"args":[]},{"name":"collect_fees","docs":["Collect fees accrued for this position.","","### Authority","- `position_authority` - authority that owns the token corresponding to this desired position."],"discriminator":[164,152,207,99,30,186,19,182],"accounts":[{"name":"fusion_pool"},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program"}],"args":[{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"collect_protocol_fees","docs":["Collect the protocol fees accrued in this FusionPool","","### Authority","- `collect_protocol_fees_authority` - assigned authority in the FusionPoolConfig that can collect protocol fees"],"discriminator":[22,67,23,98,150,178,70,220],"accounts":[{"name":"fusion_pools_config"},{"name":"fusion_pool","writable":true},{"name":"collect_protocol_fees_authority","signer":true},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"token_destination_a","writable":true},{"name":"token_destination_b","writable":true},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program"}],"args":[{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"decrease_limit_order","docs":["Remove tokens from a limit order in the FusionPool.","","### Parameters","- `amount` - The total amount of tokens the user is willing to withdraw."],"discriminator":[117,157,60,103,66,49,163,0],"accounts":[{"name":"limit_order_authority","signer":true},{"name":"fusion_pool","writable":true},{"name":"limit_order","writable":true},{"name":"limit_order_token_account"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array","writable":true},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program"}],"args":[{"name":"amount","type":"u64"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"decrease_liquidity","docs":["Withdraw liquidity from a position in the FusionPool. This call also updates the position's accrued fees.","","### Authority","- `position_authority` - authority that owns the token corresponding to this desired position.","","### Parameters","- `liquidity_amount` - The total amount of Liquidity the user desires to withdraw.","- `token_min_a` - The minimum amount of tokenA the user is willing to withdraw.","- `token_min_b` - The minimum amount of tokenB the user is willing to withdraw.","","#### Special Errors","- `LiquidityZero` - Provided liquidity amount is zero.","- `LiquidityTooHigh` - Provided liquidity exceeds u128::max.","- `TokenMinSubceeded` - The required token to perform this operation subceeds the user defined amount."],"discriminator":[160,38,208,111,104,91,44,1],"accounts":[{"name":"fusion_pool","writable":true},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program"},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_lower","writable":true},{"name":"tick_array_upper","writable":true}],"args":[{"name":"liquidity_amount","type":"u128"},{"name":"token_min_a","type":"u64"},{"name":"token_min_b","type":"u64"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"delete_position_bundle","docs":["Delete a PositionBundle account. Burns the position bundle token in the owner's wallet.","","### Authority","- `position_bundle_owner` - The owner that owns the position bundle token.","","### Special Errors","- `PositionBundleNotDeletable` - The provided position bundle has open positions."],"discriminator":[100,25,99,2,217,239,124,173],"accounts":[{"name":"position_bundle","writable":true},{"name":"position_bundle_mint","writable":true},{"name":"position_bundle_token_account","writable":true},{"name":"position_bundle_owner","signer":true},{"name":"receiver","writable":true},{"name":"token_program"}],"args":[]},{"name":"delete_token_badge","discriminator":[53,146,68,8,18,117,17,185],"accounts":[{"name":"fusion_pools_config"},{"name":"token_badge_authority","signer":true},{"name":"token_mint"},{"name":"token_badge","writable":true},{"name":"receiver","writable":true}],"args":[]},{"name":"idl_include","discriminator":[223,253,121,121,60,193,129,31],"accounts":[{"name":"tick_array"},{"name":"system_program"}],"args":[]},{"name":"increase_limit_order","docs":["Add tokens to a limit order in the FusionPool.","","### Parameters","- `amount` - The total amount of tokens the user is willing to deposit."],"discriminator":[177,144,89,236,250,186,125,99],"accounts":[{"name":"limit_order_authority","signer":true},{"name":"fusion_pool","writable":true},{"name":"limit_order","writable":true},{"name":"limit_order_token_account"},{"name":"token_mint"},{"name":"token_owner_account","writable":true},{"name":"token_vault","writable":true},{"name":"tick_array","writable":true},{"name":"token_program"},{"name":"memo_program"}],"args":[{"name":"amount","type":"u64"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"increase_liquidity","docs":["Add liquidity to a position in the FusionPool. This call also updates the position's accrued fees.","","### Authority","- `position_authority` - authority that owns the token corresponding to this desired position.","","### Parameters","- `liquidity_amount` - The total amount of Liquidity the user is willing to deposit.","- `token_max_a` - The maximum amount of tokenA the user is willing to deposit.","- `token_max_b` - The maximum amount of tokenB the user is willing to deposit.","","#### Special Errors","- `LiquidityZero` - Provided liquidity amount is zero.","- `LiquidityTooHigh` - Provided liquidity exceeds u128::max.","- `TokenMaxExceeded` - The required token to perform this operation exceeds the user defined amount."],"discriminator":[46,156,243,118,13,205,251,178],"accounts":[{"name":"fusion_pool","writable":true},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program"},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_lower","writable":true},{"name":"tick_array_upper","writable":true}],"args":[{"name":"liquidity_amount","type":"u128"},{"name":"token_max_a","type":"u64"},{"name":"token_max_b","type":"u64"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"initialize_config","docs":["Initializes a FusionPoolsConfig account that hosts info & authorities","required to govern a set of FusionPools.","","### Parameters","- `fee_authority` - Authority authorized to initialize fee-tiers and set customs fees.","- `collect_protocol_fees_authority` - Authority authorized to collect protocol fees.","- `token_badge_authority`","- `default_protocol_fee_rate`","- `default_order_protocol_fee_rate`"],"discriminator":[208,127,21,1,194,190,196,70],"accounts":[{"name":"fusion_pools_config","writable":true},{"name":"funder","writable":true,"signer":true},{"name":"system_program"}],"args":[{"name":"fee_authority","type":"pubkey"},{"name":"collect_protocol_fees_authority","type":"pubkey"},{"name":"token_badge_authority","type":"pubkey"},{"name":"default_protocol_fee_rate","type":"u16"}]},{"name":"initialize_pool","docs":["Initializes a FusionPool account.","","### Parameters","- `tick_spacing` - The desired tick spacing for this pool.","- `fee_rate` - The desired fee rate for this pool.","- `initial_sqrt_price` - The desired initial sqrt-price for this pool","","#### Special Errors","`InvalidTokenMintOrder` - The order of mints have to be ordered by","`SqrtPriceOutOfBounds` - provided initial_sqrt_price is not between 2^-64 to 2^64",""],"discriminator":[95,180,10,172,84,174,232,40],"accounts":[{"name":"fusion_pools_config"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_badge_a"},{"name":"token_badge_b"},{"name":"funder","writable":true,"signer":true},{"name":"fusion_pool","writable":true},{"name":"token_vault_a","writable":true,"signer":true},{"name":"token_vault_b","writable":true,"signer":true},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"system_program"},{"name":"rent"}],"args":[{"name":"tick_spacing","type":"u16"},{"name":"fee_rate","type":"u16"},{"name":"initial_sqrt_price","type":"u128"}]},{"name":"initialize_position_bundle","docs":["Initializes a PositionBundle account that bundles several positions.","A unique token will be minted to represent the position bundle in the users wallet."],"discriminator":[117,45,241,149,24,18,194,65],"accounts":[{"name":"position_bundle","writable":true},{"name":"position_bundle_mint","writable":true,"signer":true},{"name":"position_bundle_token_account","writable":true},{"name":"position_bundle_owner"},{"name":"funder","writable":true,"signer":true},{"name":"token_program"},{"name":"system_program"},{"name":"rent"},{"name":"associated_token_program"}],"args":[]},{"name":"initialize_position_bundle_with_metadata","docs":["Initializes a PositionBundle account that bundles several positions.","A unique token will be minted to represent the position bundle in the users wallet.","Additional Metaplex metadata is appended to identify the token."],"discriminator":[93,124,16,179,249,131,115,245],"accounts":[{"name":"position_bundle","writable":true},{"name":"position_bundle_mint","writable":true,"signer":true},{"name":"position_bundle_metadata","docs":["https://github.com/metaplex-foundation/metaplex-program-library/blob/773a574c4b34e5b9f248a81306ec24db064e255f/token-metadata/program/src/utils/metadata.rs#L100"],"writable":true},{"name":"position_bundle_token_account","writable":true},{"name":"position_bundle_owner"},{"name":"funder","writable":true,"signer":true},{"name":"metadata_update_auth"},{"name":"token_program"},{"name":"system_program"},{"name":"rent"},{"name":"associated_token_program"},{"name":"metadata_program"}],"args":[]},{"name":"initialize_tick_array","docs":["Initializes a tick_array account to represent a tick-range in a FusionPool.","","### Parameters","- `start_tick_index` - The starting tick index for this tick-array.","Has to be a multiple of TickArray size & the tick spacing of this pool.","","#### Special Errors","- `InvalidStartTick` - if the provided start tick is out of bounds or is not a multiple of","TICK_ARRAY_SIZE * tick spacing."],"discriminator":[11,188,193,214,141,91,149,184],"accounts":[{"name":"fusion_pool"},{"name":"funder","writable":true,"signer":true},{"name":"tick_array","writable":true},{"name":"system_program"}],"args":[{"name":"start_tick_index","type":"i32"}]},{"name":"initialize_token_badge","discriminator":[253,77,205,95,27,224,89,223],"accounts":[{"name":"fusion_pools_config"},{"name":"token_badge_authority","signer":true},{"name":"token_mint"},{"name":"token_badge","writable":true},{"name":"funder","writable":true,"signer":true},{"name":"system_program"}],"args":[]},{"name":"lock_position","docs":["Lock the position to prevent liquidity decrease.","","### Authority","- `position_authority` - The authority that owns the position token.","","#### Special Errors","- `PositionAlreadyLocked` - The provided position is already locked.","- `PositionNotLockable` - The provided position is not lockable (e.g. An empty position)."],"discriminator":[227,62,2,252,247,10,171,185],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"position_authority","signer":true},{"name":"position"},{"name":"position_mint"},{"name":"position_token_account","writable":true},{"name":"position_lock","writable":true},{"name":"fusion_pool"},{"name":"token_2022_program"},{"name":"system_program"}],"args":[{"name":"lock_type","type":{"defined":{"name":"PositionLockType"}}}]},{"name":"open_bundled_position","docs":["Open a bundled position in a FusionPool. No new tokens are issued","because the owner of the position bundle becomes the owner of the position.","The position will start off with 0 liquidity.","","### Authority","- `position_bundle_authority` - authority that owns the token corresponding to this desired position bundle.","","### Parameters","- `bundle_index` - The bundle index that we'd like to open.","- `tick_lower_index` - The tick specifying the lower end of the position range.","- `tick_upper_index` - The tick specifying the upper end of the position range.","","#### Special Errors","- `InvalidBundleIndex` - If the provided bundle index is out of bounds.","- `InvalidTickIndex` - If a provided tick is out of bounds, out of order or not a multiple of","the tick-spacing in this pool."],"discriminator":[169,113,126,171,213,172,212,49],"accounts":[{"name":"bundled_position","writable":true},{"name":"position_bundle","writable":true},{"name":"position_bundle_token_account"},{"name":"position_bundle_authority","signer":true},{"name":"fusion_pool"},{"name":"funder","writable":true,"signer":true},{"name":"system_program"},{"name":"rent"}],"args":[{"name":"bundle_index","type":"u16"},{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"}]},{"name":"open_limit_order","docs":["Open a limit order in a FusionPool. A unique token will be minted to represent the limit order","in the users wallet. The limit order will start off with 0 amount.","","### Parameters","- `tick_index` - The tick specifying the limit order execution price.","- `a_to_b` - The limit order swap direction.","- `with_token_metadata_extension` - If true, the token metadata extension will be initialized."],"discriminator":[157,32,218,183,71,29,18,147],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"owner"},{"name":"limit_order","writable":true},{"name":"limit_order_mint","writable":true,"signer":true},{"name":"limit_order_token_account","writable":true},{"name":"fusion_pool"},{"name":"token_2022_program"},{"name":"system_program"},{"name":"associated_token_program"},{"name":"metadata_update_auth"}],"args":[{"name":"tick_index","type":"i32"},{"name":"a_to_b","type":"bool"},{"name":"with_token_metadata_extension","type":"bool"}]},{"name":"open_position","docs":["Open a position in a FusionPool. A unique token will be minted to represent the position","in the users wallet. Additional TokenMetadata extension is initialized to identify the token.","Mint and TokenAccount are based on Token-2022.","The position will start off with 0 liquidity.","","### Parameters","- `tick_lower_index` - The tick specifying the lower end of the position range.","- `tick_upper_index` - The tick specifying the upper end of the position range.","- `with_token_metadata_extension` - If true, the token metadata extension will be initialized.","","#### Special Errors","- `InvalidTickIndex` - If a provided tick is out of bounds, out of order or not a multiple of","the tick-spacing in this pool."],"discriminator":[135,128,47,77,15,152,240,49],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"owner"},{"name":"position","writable":true},{"name":"position_mint","writable":true,"signer":true},{"name":"position_token_account","writable":true},{"name":"fusion_pool"},{"name":"token_2022_program"},{"name":"system_program"},{"name":"associated_token_program"},{"name":"metadata_update_auth"}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"with_token_metadata_extension","type":"bool"}]},{"name":"reset_pool_price","docs":["Resets the pool price to the provided value if the pool is empty.","","### Authority","- \"fee_authority\" - Set authority that can modify pool fees in the FusionPoolConfig","","### Parameters","- `sqrt_price` - The new sqrt_price of the pool.","","#### Special Errors","- `ResetPriceForNonEmptyPool` - Thrown if the pool is not empty."],"discriminator":[93,158,158,189,131,42,15,22],"accounts":[{"name":"fee_authority","signer":true},{"name":"fusion_pools_config"},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"fusion_pool","writable":true}],"args":[{"name":"sqrt_price","type":"u128"}]},{"name":"set_collect_protocol_fees_authority","docs":["Sets the fee authority to collect protocol fees for a FusionPoolConfig.","Only the current collect protocol fee authority has permission to invoke this instruction.","","### Authority","- \"fee_authority\" - Set authority that can collect protocol fees in the FusionPoolConfig"],"discriminator":[34,150,93,244,139,225,233,67],"accounts":[{"name":"fusion_pools_config","writable":true},{"name":"collect_protocol_fees_authority","signer":true},{"name":"new_collect_protocol_fees_authority"}],"args":[]},{"name":"set_default_protocol_fee_rate","docs":["Sets the default protocol fee rate for a FusionPoolConfig","Protocol fee rate is represented as a basis point.","Only the current fee authority has permission to invoke this instruction.","","### Authority","- \"fee_authority\" - Set authority that can modify pool fees in the FusionPoolConfig","","### Parameters","- `default_protocol_fee_rate` - Rate that is referenced during the initialization of a FusionPool using this config.","","#### Special Errors","- `ProtocolFeeRateMaxExceeded` - If the provided default_protocol_fee_rate exceeds MAX_PROTOCOL_FEE_RATE."],"discriminator":[107,205,249,226,151,35,86,0],"accounts":[{"name":"fusion_pools_config","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"default_protocol_fee_rate","type":"u16"}]},{"name":"set_fee_authority","docs":["Sets the fee authority for a FusionPoolConfig.","The fee authority can set the fee & protocol fee rate for individual pools or","set the default fee rate for newly minted pools.","Only the current fee authority has permission to invoke this instruction.","","### Authority","- \"fee_authority\" - Set authority that can modify pool fees in the FusionPoolConfig"],"discriminator":[31,1,50,87,237,101,97,132],"accounts":[{"name":"fusion_pools_config","writable":true},{"name":"fee_authority","signer":true},{"name":"new_fee_authority"}],"args":[]},{"name":"set_fee_rate","docs":["Sets the fee rate for a FusionPool.","Fee rate is represented as hundredths of a basis point.","Only the current fee authority has permission to invoke this instruction.","","### Authority","- \"fee_authority\" - Set authority that can modify pool fees in the FusionPoolConfig","","### Parameters","- `fee_rate` - The rate that the pool will use to calculate fees going onwards.","","#### Special Errors","- `FeeRateMaxExceeded` - If the provided fee_rate exceeds MAX_FEE_RATE."],"discriminator":[53,243,137,65,8,140,158,6],"accounts":[{"name":"fusion_pools_config"},{"name":"fusion_pool","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"fee_rate","type":"u16"}]},{"name":"set_position_range","docs":["Set the position range to a new one.","","### Authority","- `position_authority` - The authority that owns the position token.","","### Parameters","- `tick_lower_index` - The tick specifying the lower index of the position.","- `tick_upper_index` - The tick specifying the upper index of the position."],"discriminator":[192,22,176,176,155,49,153,96],"accounts":[{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account","writable":true},{"name":"fusion_pool"}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"}]},{"name":"set_protocol_fee_rate","docs":["Sets the protocol fee rate for a FusionPool.","Protocol fee rate is represented as a basis point.","Only the current fee authority has permission to invoke this instruction.","","### Authority","- \"fee_authority\" - Set authority that can modify pool fees in the FusionPoolConfig","","### Parameters","- `protocol_fee_rate` - The rate that the pool will use to calculate protocol fees going onwards.","","#### Special Errors","- `ProtocolFeeRateMaxExceeded` - If the provided default_protocol_fee_rate exceeds MAX_PROTOCOL_FEE_RATE."],"discriminator":[95,7,4,50,154,79,156,131],"accounts":[{"name":"fusion_pools_config"},{"name":"fusion_pool","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"protocol_fee_rate","type":"u16"}]},{"name":"set_token_badge_authority","discriminator":[207,202,4,32,205,79,13,178],"accounts":[{"name":"fusion_pools_config","writable":true},{"name":"fee_authority","signer":true},{"name":"new_token_badge_authority"}],"args":[]},{"name":"swap","docs":["Perform a swap in this FusionPool","","### Authority","- \"token_authority\" - The authority to withdraw tokens from the input token account.","","### Parameters","- `amount` - The amount of input or output token to swap from (depending on amount_specified_is_input).","- `other_amount_threshold` - The maximum/minimum of input/output token to swap into (depending on amount_specified_is_input).","- `sqrt_price_limit` - The maximum/minimum price the swap will swap to.","- `amount_specified_is_input` - Specifies the token the parameter `amount`represents. If true, the amount represents the input token of the swap.","- `a_to_b` - The direction of the swap. True if swapping from A to B. False if swapping from B to A.","","#### Special Errors","- `ZeroTradableAmount` - User provided parameter `amount` is 0.","- `InvalidSqrtPriceLimitDirection` - User provided parameter `sqrt_price_limit` does not match the direction of the trade.","- `SqrtPriceOutOfBounds` - User provided parameter `sqrt_price_limit` is over Whirlppool's max/min bounds for sqrt-price.","- `InvalidTickArraySequence` - User provided tick-arrays are not in sequential order required to proceed in this trade direction.","- `TickArraySequenceInvalidIndex` - The swap loop attempted to access an invalid array index during the query of the next initialized tick.","- `TickArrayIndexOutofBounds` - The swap loop attempted to access an invalid array index during tick crossing.","- `LiquidityOverflow` - Liquidity value overflowed 128bits during tick crossing.","- `InvalidTickSpacing` - The swap pool was initialized with tick-spacing of 0."],"discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program"},{"name":"token_authority","signer":true},{"name":"fusion_pool","writable":true},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_0","writable":true},{"name":"tick_array_1","writable":true},{"name":"tick_array_2","writable":true}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit","type":"u128"},{"name":"amount_specified_is_input","type":"bool"},{"name":"a_to_b","type":"bool"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"two_hop_swap","docs":["Perform a two-hop swap in this FusionPool","","### Authority","- \"token_authority\" - The authority to withdraw tokens from the input token account.","","### Parameters","- `amount` - The amount of input or output token to swap from (depending on amount_specified_is_input).","- `other_amount_threshold` - The maximum/minimum of input/output token to swap into (depending on amount_specified_is_input).","- `amount_specified_is_input` - Specifies the token the parameter `amount`represents. If true, the amount represents the input token of the swap.","- `a_to_b_one` - The direction of the swap of hop one. True if swapping from A to B. False if swapping from B to A.","- `a_to_b_two` - The direction of the swap of hop two. True if swapping from A to B. False if swapping from B to A.","- `sqrt_price_limit_one` - The maximum/minimum price the swap will swap to in the first hop.","- `sqrt_price_limit_two` - The maximum/minimum price the swap will swap to in the second hop.","","#### Special Errors","- `ZeroTradableAmount` - User provided parameter `amount` is 0.","- `InvalidSqrtPriceLimitDirection` - User provided parameter `sqrt_price_limit` does not match the direction of the trade.","- `SqrtPriceOutOfBounds` - User provided parameter `sqrt_price_limit` is over Whirlppool's max/min bounds for sqrt-price.","- `InvalidTickArraySequence` - User provided tick-arrays are not in sequential order required to proceed in this trade direction.","- `TickArraySequenceInvalidIndex` - The swap loop attempted to access an invalid array index during the query of the next initialized tick.","- `TickArrayIndexOutofBounds` - The swap loop attempted to access an invalid array index during tick crossing.","- `LiquidityOverflow` - Liquidity value overflowed 128bits during tick crossing.","- `InvalidTickSpacing` - The swap pool was initialized with tick-spacing of 0.","- `InvalidIntermediaryMint` - Error if the intermediary mint between hop one and two do not equal.","- `DuplicateTwoHopPool` - Error if fusion_pool one & two are the same pool."],"discriminator":[195,96,237,108,68,162,219,230],"accounts":[{"name":"fusion_pool_one","writable":true},{"name":"fusion_pool_two","writable":true},{"name":"token_mint_input"},{"name":"token_mint_intermediate"},{"name":"token_mint_output"},{"name":"token_program_input"},{"name":"token_program_intermediate"},{"name":"token_program_output"},{"name":"token_owner_account_input","writable":true},{"name":"token_vault_one_input","writable":true},{"name":"token_vault_one_intermediate","writable":true},{"name":"token_vault_two_intermediate","writable":true},{"name":"token_vault_two_output","writable":true},{"name":"token_owner_account_output","writable":true},{"name":"token_authority","signer":true},{"name":"tick_array_one_0","writable":true},{"name":"tick_array_one_1","writable":true},{"name":"tick_array_one_2","writable":true},{"name":"tick_array_two_0","writable":true},{"name":"tick_array_two_1","writable":true},{"name":"tick_array_two_2","writable":true},{"name":"memo_program"}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"amount_specified_is_input","type":"bool"},{"name":"a_to_b_one","type":"bool"},{"name":"a_to_b_two","type":"bool"},{"name":"sqrt_price_limit_one","type":"u128"},{"name":"sqrt_price_limit_two","type":"u128"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"update_fees","docs":["Update the accrued fees for a position.","","#### Special Errors","- `TickNotFound` - Provided tick array account does not contain the tick for this position.","- `LiquidityZero` - Position has zero liquidity and therefore already has the most updated fees values."],"discriminator":[225,27,13,6,69,84,172,191],"accounts":[{"name":"fusion_pool","writable":true},{"name":"position","writable":true},{"name":"tick_array_lower"},{"name":"tick_array_upper"}],"args":[]}],"accounts":[{"name":"FusionPool","discriminator":[254,204,207,98,25,181,29,67]},{"name":"FusionPoolsConfig","discriminator":[191,199,19,11,75,86,239,169]},{"name":"LimitOrder","discriminator":[137,183,212,91,115,29,141,227]},{"name":"Position","discriminator":[170,188,143,228,122,64,247,208]},{"name":"PositionBundle","discriminator":[129,169,175,65,185,95,32,100]},{"name":"PositionLock","discriminator":[163,137,166,27,112,14,172,118]},{"name":"TickArray","discriminator":[85,1,199,2,188,97,101,139]},{"name":"TokenBadge","discriminator":[116,219,204,229,249,116,255,150]}],"errors":[{"code":6000,"name":"InvalidEnum","msg":"Enum value could not be converted"},{"code":6001,"name":"InvalidStartTick","msg":"Invalid start tick index provided."},{"code":6002,"name":"TickArrayExistInPool","msg":"Tick-array already exists in this fusion_pool"},{"code":6003,"name":"TickArrayIndexOutofBounds","msg":"Attempt to search for a tick-array failed"},{"code":6004,"name":"InvalidTickSpacing","msg":"Tick-spacing is not supported"},{"code":6005,"name":"ClosePositionNotEmpty","msg":"Position is not empty It cannot be closed"},{"code":6006,"name":"DivideByZero","msg":"Unable to divide by zero"},{"code":6007,"name":"NumberCastError","msg":"Unable to cast number into BigInt"},{"code":6008,"name":"NumberDownCastError","msg":"Unable to down cast number"},{"code":6009,"name":"TickNotFound","msg":"Tick not found within tick array"},{"code":6010,"name":"InvalidTickIndex","msg":"Provided tick index is either out of bounds or uninitializable"},{"code":6011,"name":"SqrtPriceOutOfBounds","msg":"Provided sqrt price out of bounds"},{"code":6012,"name":"LiquidityZero","msg":"Liquidity amount must be greater than zero"},{"code":6013,"name":"LiquidityTooHigh","msg":"Liquidity amount must be less than i64::MAX"},{"code":6014,"name":"LiquidityOverflow","msg":"Liquidity overflow"},{"code":6015,"name":"LiquidityUnderflow","msg":"Liquidity underflow"},{"code":6016,"name":"LiquidityNetError","msg":"Tick liquidity net underflowed or overflowed"},{"code":6017,"name":"TokenMaxExceeded","msg":"Exceeded token max"},{"code":6018,"name":"TokenMinSubceeded","msg":"Did not meet token min"},{"code":6019,"name":"MissingOrInvalidDelegate","msg":"Position token account has a missing or invalid delegate"},{"code":6020,"name":"InvalidPositionTokenAmount","msg":"Position token amount must be 1"},{"code":6021,"name":"InvalidTimestampConversion","msg":"Timestamp should be convertible from i64 to u64"},{"code":6022,"name":"InvalidTimestamp","msg":"Timestamp should be greater than the last updated timestamp"},{"code":6023,"name":"InvalidTickArraySequence","msg":"Invalid tick array sequence provided for instruction."},{"code":6024,"name":"InvalidTokenMintOrder","msg":"Token Mint in wrong order"},{"code":6025,"name":"SetRangeForNonEmptyPosition","msg":"Position is not empty, range can't be set."},{"code":6026,"name":"Unused","msg":"Unused"},{"code":6027,"name":"RewardVaultAmountInsufficient","msg":"Reward vault requires amount to support emissions for at least one day"},{"code":6028,"name":"FeeRateMaxExceeded","msg":"Exceeded max fee rate"},{"code":6029,"name":"ProtocolFeeRateMaxExceeded","msg":"Exceeded max protocol fee rate"},{"code":6030,"name":"MultiplicationShiftRightOverflow","msg":"Multiplication with shift right overflow"},{"code":6031,"name":"MulDivOverflow","msg":"Muldiv overflow"},{"code":6032,"name":"MulDivInvalidInput","msg":"Invalid div_u256 input"},{"code":6033,"name":"MultiplicationOverflow","msg":"Multiplication overflow"},{"code":6034,"name":"InvalidSqrtPriceLimitDirection","msg":"Provided SqrtPriceLimit not in the same direction as the swap."},{"code":6035,"name":"ZeroTradableAmount","msg":"There are no tradable amount to swap."},{"code":6036,"name":"AmountOutBelowMinimum","msg":"Amount out below minimum threshold"},{"code":6037,"name":"AmountInAboveMaximum","msg":"Amount in above maximum threshold"},{"code":6038,"name":"TickArraySequenceInvalidIndex","msg":"Invalid index for tick array sequence"},{"code":6039,"name":"AmountCalcOverflow","msg":"Amount calculated overflows"},{"code":6040,"name":"AmountRemainingOverflow","msg":"Amount remaining overflows"},{"code":6041,"name":"InvalidIntermediaryMint","msg":"Invalid intermediary mint"},{"code":6042,"name":"DuplicateTwoHopPool","msg":"Duplicate two hop pool"},{"code":6043,"name":"InvalidBundleIndex","msg":"Bundle index is out of bounds"},{"code":6044,"name":"BundledPositionAlreadyOpened","msg":"Position has already been opened"},{"code":6045,"name":"BundledPositionAlreadyClosed","msg":"Position has already been closed"},{"code":6046,"name":"PositionBundleNotDeletable","msg":"Unable to delete PositionBundle with open positions"},{"code":6047,"name":"UnsupportedTokenMint","msg":"Token mint has unsupported attributes"},{"code":6048,"name":"RemainingAccountsInvalidSlice","msg":"Invalid remaining accounts"},{"code":6049,"name":"RemainingAccountsInsufficient","msg":"Insufficient remaining accounts"},{"code":6050,"name":"NoExtraAccountsForTransferHook","msg":"Unable to call transfer hook without extra accounts"},{"code":6051,"name":"IntermediateTokenAmountMismatch","msg":"Output and input amount mismatch"},{"code":6052,"name":"TransferFeeCalculationError","msg":"Transfer fee calculation failed"},{"code":6053,"name":"RemainingAccountsDuplicatedAccountsType","msg":"Same accounts type is provided more than once"},{"code":6054,"name":"FullRangeOnlyPool","msg":"This fusion_pool only supports full-range positions"},{"code":6055,"name":"TooManySupplementalTickArrays","msg":"Too many supplemental tick arrays provided"},{"code":6056,"name":"DifferentFusionPoolTickArrayAccount","msg":"TickArray account for different fusion_pool provided"},{"code":6057,"name":"PartialFillError","msg":"Trade resulted in partial fill"},{"code":6058,"name":"TakerOrderNotSupported","msg":"The limit order tick index is incorrect relative to the current market tick (taker order)"},{"code":6059,"name":"LimitOrderAmountExceeded","msg":"Exceeded the limit order deposited input amount"},{"code":6060,"name":"LimitOrderNotEmpty","msg":"Limit order is not empty and cannot be closed"},{"code":6061,"name":"LimitOrderIsFilled","msg":"Unable to increase a limit order because it's partially or fully filled"},{"code":6062,"name":"ZeroAmount","msg":"Zero amount"},{"code":6063,"name":"ResetPriceForNonEmptyPool","msg":"Can't reset the price because the pool is not empty"},{"code":6064,"name":"PositionLocked","msg":"Position locked"},{"code":6065,"name":"PositionNotLockable","msg":"Position is not lockable"},{"code":6066,"name":"RentCalculationError","msg":"Rent calculation error"}],"types":[{"name":"AccountsType","type":{"kind":"enum","variants":[{"name":"TransferHookA"},{"name":"TransferHookB"},{"name":"TransferHookInput"},{"name":"TransferHookIntermediate"},{"name":"TransferHookOutput"},{"name":"SupplementalTickArrays"},{"name":"SupplementalTickArraysOne"},{"name":"SupplementalTickArraysTwo"}]}},{"name":"FusionPool","type":{"kind":"struct","fields":[{"name":"bump","type":{"array":["u8",1]}},{"name":"version","type":"u16"},{"name":"token_mint_a","type":"pubkey"},{"name":"token_mint_b","type":"pubkey"},{"name":"token_vault_a","type":"pubkey"},{"name":"token_vault_b","type":"pubkey"},{"name":"tick_spacing","type":"u16"},{"name":"tick_spacing_seed","type":{"array":["u8",2]}},{"name":"fee_rate","type":"u16"},{"name":"protocol_fee_rate","type":"u16"},{"name":"unused_0","type":"u32"},{"name":"liquidity","type":"u128"},{"name":"sqrt_price","type":"u128"},{"name":"tick_current_index","type":"i32"},{"name":"protocol_fee_owed_a","type":"u64"},{"name":"protocol_fee_owed_b","type":"u64"},{"name":"fee_growth_global_a","type":"u128"},{"name":"fee_growth_global_b","type":"u128"},{"name":"orders_total_amount_a","type":"u64"},{"name":"orders_total_amount_b","type":"u64"},{"name":"orders_filled_amount_a","type":"u64"},{"name":"orders_filled_amount_b","type":"u64"},{"name":"olp_fee_owed_a","type":"u64"},{"name":"olp_fee_owed_b","type":"u64"},{"name":"ma_sqrt_price","type":"u128"},{"name":"last_swap_timestamp","type":"u64"},{"name":"reserved","type":{"array":["u8",116]}}]}},{"name":"FusionPoolsConfig","type":{"kind":"struct","fields":[{"name":"version","type":"u16"},{"name":"fee_authority","type":"pubkey"},{"name":"collect_protocol_fees_authority","type":"pubkey"},{"name":"token_badge_authority","type":"pubkey"},{"name":"default_protocol_fee_rate","type":"u16"},{"name":"unused_0","type":"u16"},{"name":"unused_1","type":"u16"},{"name":"reserved","type":{"array":["u8",170]}}]}},{"name":"LimitOrder","type":{"kind":"struct","fields":[{"name":"version","type":"u16"},{"name":"fusion_pool","type":"pubkey"},{"name":"limit_order_mint","type":"pubkey"},{"name":"tick_index","type":"i32"},{"name":"amount","type":"u64"},{"name":"a_to_b","type":"bool"},{"name":"age","type":"u64"},{"name":"reserved","type":{"array":["u8",128]}}]}},{"name":"MaybeTick","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Initialized","fields":[{"defined":{"name":"TickData"}}]}]}},{"name":"Position","type":{"kind":"struct","fields":[{"name":"version","type":"u16"},{"name":"fusion_pool","type":"pubkey"},{"name":"position_mint","type":"pubkey"},{"name":"liquidity","type":"u128"},{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"fee_growth_checkpoint_a","type":"u128"},{"name":"fee_owed_a","type":"u64"},{"name":"fee_growth_checkpoint_b","type":"u128"},{"name":"fee_owed_b","type":"u64"},{"name":"reserved","type":{"array":["u8",128]}}]}},{"name":"PositionBundle","type":{"kind":"struct","fields":[{"name":"position_bundle_mint","type":"pubkey"},{"name":"position_bitmap","type":{"array":["u8",32]}}]}},{"name":"PositionLock","type":{"kind":"struct","fields":[{"name":"position_mint","type":"pubkey"},{"name":"position_owner","type":"pubkey"},{"name":"fusion_pool","type":"pubkey"},{"name":"locked_timestamp","type":"u64"},{"name":"lock_type","type":{"defined":{"name":"PositionLockType"}}},{"name":"reserved","type":{"array":["u8",128]}}]}},{"name":"PositionLockType","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Permanent"}]}},{"name":"RemainingAccountsInfo","type":{"kind":"struct","fields":[{"name":"slices","type":{"vec":{"defined":{"name":"RemainingAccountsSlice"}}}}]}},{"name":"RemainingAccountsSlice","type":{"kind":"struct","fields":[{"name":"accounts_type","type":{"defined":{"name":"AccountsType"}}},{"name":"length","type":"u8"}]}},{"name":"TickArray","type":{"kind":"struct","fields":[{"name":"start_tick_index","type":"i32"},{"name":"fusion_pool","type":"pubkey"},{"name":"ticks","type":{"array":[{"defined":{"name":"MaybeTick"}},88]}}]}},{"name":"TickData","type":{"kind":"struct","fields":[{"name":"liquidity_net","type":"i128"},{"name":"liquidity_gross","type":"u128"},{"name":"fee_growth_outside_a","type":"u128"},{"name":"fee_growth_outside_b","type":"u128"},{"name":"age","type":"u64"},{"name":"open_orders_input","type":"u64"},{"name":"part_filled_orders_input","type":"u64"},{"name":"part_filled_orders_remaining_input","type":"u64"},{"name":"fulfilled_a_to_b_orders_input","type":"u64"},{"name":"fulfilled_b_to_a_orders_input","type":"u64"}]}},{"name":"TokenBadge","type":{"kind":"struct","fields":[{"name":"token_mint","type":"pubkey"},{"name":"reserved","type":{"array":["u8",128]}}]}}]} \ No newline at end of file diff --git a/idls/goosefx_gamma.GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT.json b/idls/goosefx_gamma.GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT.json new file mode 100644 index 0000000..6eadf43 --- /dev/null +++ b/idls/goosefx_gamma.GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT.json @@ -0,0 +1 @@ +{"address":"GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT","metadata":{"name":"gamma","version":"0.2.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"add_or_update_actor","discriminator":[113,136,228,230,107,185,128,227],"accounts":[{"name":"owner","docs":["Only admin(multisig) can add or update actor"],"writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"actor","writable":true,"pda":{"seeds":[{"kind":"const","value":[97,99,116,111,114]},{"kind":"account","path":"authority"}]}},{"name":"authority","docs":["Account for which the actor is being created.","This is the account that will be able to update the pool."]}],"args":[{"name":"is_admin","type":"bool"}]},{"name":"add_partner","docs":["Admin-permissioned instruction to add a new partner to a pool","","* `ctx` - The context of accounts",""],"discriminator":[180,111,45,157,241,187,234,88],"accounts":[{"name":"authority","signer":true},{"name":"amm_config"},{"name":"pool_state","writable":true,"relations":["partner"]},{"name":"pool_partners","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,97,114,116,110,101,114,95,105,110,102,111,115]},{"kind":"account","path":"pool_state"}]}},{"name":"partner"}],"args":[]},{"name":"calculate_rewards","docs":["Calculate rewards for the user","","* `ctx` - The context of accounts",""],"discriminator":[199,115,201,124,71,81,143,252],"accounts":[{"name":"signer","writable":true,"signer":true},{"name":"user","docs":["User for which we are calculating rewards"]},{"name":"pool_state"},{"name":"reward_info","writable":true,"pda":{"seeds":[{"kind":"const","value":[114,101,119,97,114,100,95,105,110,102,111,95,115,101,101,100]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"reward_info.start_at","account":"RewardInfo"},{"kind":"account","path":"reward_info.mint","account":"RewardInfo"}]}},{"name":"user_reward_info","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,114,101,119,97,114,100,95,105,110,102,111,95,115,101,101,100]},{"kind":"account","path":"reward_info"},{"kind":"account","path":"user"}]}},{"name":"user_pool_liquidity","docs":["User pool liquidity account"],"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,45,112,111,111,108,45,108,105,113,117,105,100,105,116,121]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"user"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"claim_partner_fees","docs":["Claim fees for a particular partner","","* `ctx` - The context of accounts",""],"discriminator":[114,71,103,57,160,205,242,185],"accounts":[{"name":"partner"},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state"},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"pool_partners","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,97,114,116,110,101,114,95,105,110,102,111,115]},{"kind":"account","path":"pool_state"}]}},{"name":"token_0_token_account","writable":true,"relations":["partner"]},{"name":"token_1_token_account","writable":true,"relations":["partner"]},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[]},{"name":"claim_rewards","docs":["Claim rewards for the user","Transfers the amount of tokens calculated for the user to their reward token account","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[4,144,132,71,116,23,151,80],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state"},{"name":"reward_info","pda":{"seeds":[{"kind":"const","value":[114,101,119,97,114,100,95,105,110,102,111,95,115,101,101,100]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"reward_info.start_at","account":"RewardInfo"},{"kind":"account","path":"reward_info.mint","account":"RewardInfo"}]}},{"name":"reward_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[114,101,119,97,114,100,95,118,97,117,108,116,95,115,101,101,100]},{"kind":"account","path":"reward_info"}]}},{"name":"user_token_account","writable":true},{"name":"user_reward_info","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,114,101,119,97,114,100,95,105,110,102,111,95,115,101,101,100]},{"kind":"account","path":"reward_info"},{"kind":"account","path":"user"}]}},{"name":"reward_mint","writable":true},{"name":"token_program","docs":["token Program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"collect_fund_fee","docs":["Collect the fund fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[167,138,78,149,223,194,6,126],"accounts":[{"name":"owner","docs":["Only admin or fund_owner can collect fee now"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores fund_owner"]},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"recipient_token_0_account","docs":["The address that receives the collected token_0 fund fees"],"writable":true},{"name":"recipient_token_1_account","docs":["The address that receives the collected token_1 fund fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_protocol_fee","docs":["Collect the protocol fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[136,136,252,221,194,66,126,89],"accounts":[{"name":"owner","docs":["Only admin or owner can collect fee now"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores owner"]},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"recipient_token_0_account","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_1_account","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"create_amm_config","docs":["The configuation of AMM protocol, include trade fee and protocol fee","# Arguments","","* `ctx`- The accounts needed by instruction.","* `index` - The index of amm config, there may be multiple config.","* `trade_fee_rate` - Trade fee rate, can be changed.","* `protocol_fee_rate` - The rate of protocol fee within tarde fee.","* `fund_fee_rate` - The rate of fund fee within tarde fee.",""],"discriminator":[137,52,237,212,215,117,108,104],"accounts":[{"name":"owner","docs":["Address to be set as protocol owner."],"writable":true,"signer":true,"address":"9QcHinaHcJFdzSHeiF1yGchcuQk3qPFNV13q6dZJbAny"},{"name":"amm_config","docs":["Initialize AmmConfig state account to store protocol owner address and fee rates"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,109,109,95,99,111,110,102,105,103]},{"kind":"arg","path":"index"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"u16"},{"name":"trade_fee_rate","type":"u64"},{"name":"protocol_fee_rate","type":"u64"},{"name":"fund_fee_rate","type":"u64"},{"name":"create_pool_fee","type":"u64"},{"name":"max_open_time","type":"u64"}]},{"name":"create_rewards","docs":["Create rewards for the pool","Initializes a new reward info account and a reward vault account","Transfers the rewards to the reward vault","","# Arguments","","* `ctx` - The context of accounts","* `start_time` - The start time of the reward","* `end_time` - The end time of the reward","* `reward_amount` - The amount of the reward",""],"discriminator":[124,251,145,232,5,173,159,223],"accounts":[{"name":"reward_provider","writable":true,"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state"},{"name":"reward_info","writable":true,"pda":{"seeds":[{"kind":"const","value":[114,101,119,97,114,100,95,105,110,102,111,95,115,101,101,100]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"start_time"},{"kind":"account","path":"reward_mint"}]}},{"name":"reward_providers_token_account","writable":true},{"name":"reward_vault","docs":["For reward to deposit into."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[114,101,119,97,114,100,95,118,97,117,108,116,95,115,101,101,100]},{"kind":"account","path":"reward_info"}]}},{"name":"reward_mint","writable":true},{"name":"token_program","docs":["token Program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"start_time","type":"u64"},{"name":"end_time","type":"u64"},{"name":"reward_amount","type":"u64"}]},{"name":"create_swap_referral","docs":["Initialize swap referrals for an existing AMM config","# Arguments","","* `ctx` - The accounts needed by the instruction, including those used by cpi to the referral program","* `name` - The project name, passed to the referral program. Must be less than 50 chars in length","* `default_share_bps` - Percentage share of fees to referrers. Must be less than 10_000"],"discriminator":[67,131,93,236,56,6,40,77],"accounts":[{"name":"admin","docs":["Admin signer for this operation"],"signer":true},{"name":"owner","address":"9QcHinaHcJFdzSHeiF1yGchcuQk3qPFNV13q6dZJbAny"},{"name":"payer","writable":true,"signer":true},{"name":"amm_config","docs":["The config acts as the base for its referral project"],"writable":true},{"name":"project","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"referral_program","address":"REFER4ZgmyYx9c6He5XfaTMiGfdLwRnkV4RPp9t9iF3"}],"args":[{"name":"name","type":"string"},{"name":"default_share_bps","type":"u16"}]},{"name":"deposit","docs":["Creates a pool for the given token pair and the initial price","","# Arguments","","* `ctx`- The context of accounts","* `lp_token_amount` - Pool token amount to transfer. token_a and token_b amount are set by the current exchange rate and size of the pool","* `maximum_token_0_amount` - Maximum token 0 amount to deposit, prevents excessive slippage","* `maximum_token_1_amount` - Maximum token 1 amount to deposit, prevents excessive slippage",""],"discriminator":[242,35,198,137,82,225,242,182],"accounts":[{"name":"owner","docs":["Owner of the liquidity provided"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state the owner is depositing into"],"writable":true},{"name":"user_pool_liquidity","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,45,112,111,111,108,45,108,105,113,117,105,100,105,116,121]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"owner"}]}},{"name":"token_0_account","docs":["The payer's token account to deposit token_0"],"writable":true},{"name":"token_1_account","docs":["The payer's token account to deposit token_1"],"writable":true},{"name":"token_0_vault","docs":["Pool vault for token_0 to deposit into","The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["Pool vault for token_1 to deposit into","The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["token Program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"pool_partners","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,97,114,116,110,101,114,95,105,110,102,111,115]},{"kind":"account","path":"pool_state"}]}}],"args":[{"name":"lp_token_amount","type":"u64"},{"name":"maximum_token_0_amount","type":"u64"},{"name":"maximum_token_1_amount","type":"u64"}]},{"name":"init_user_pool_liquidity","discriminator":[227,221,200,212,36,107,149,36],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"pool_state"},{"name":"user_pool_liquidity","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,45,112,111,111,108,45,108,105,113,117,105,100,105,116,121]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"user"}]}},{"name":"pool_partners","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,97,114,116,110,101,114,95,105,110,102,111,115]},{"kind":"account","path":"pool_state"}]}},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"}],"args":[{"name":"partner","type":{"option":"pubkey"}}]},{"name":"initialize","docs":["Creates a pool for the given token pair and the initial price","","# Arguments","","* `ctx`- The context of accounts","* `init_amount_0` - the initial amount_0 to deposit","* `init_amount_1` - the initial amount_1 to deposit","* `open_time` - the timestamp allowed for swap","* `max_trade_fee_rate` - The maximum trade fee that can be charged on swaps","* `volatility_factor` - The volatility factor of the pool to determine the trade fee",""],"discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"creator","docs":["Address paying to create the pool. It can be anyone."],"writable":true,"signer":true},{"name":"amm_config","docs":["Which amm config the pool belongs to"]},{"name":"authority","docs":["sign transactions on behalf of the pool","for vault and lp_mint"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Initialize an account to store the pool state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"token_0_mint"},{"kind":"account","path":"token_1_mint"}]}},{"name":"user_pool_liquidity","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,45,112,111,111,108,45,108,105,113,117,105,100,105,116,121]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"creator"}]}},{"name":"token_0_mint","docs":["Token_0 mint, the key must smaller than token_1 mint."]},{"name":"token_1_mint","docs":["Token_1 mint, the key must greater than token_0 mint."]},{"name":"creator_token_0","docs":["creator token 0 account"],"writable":true},{"name":"creator_token_1","docs":["creator token 1 account"],"writable":true},{"name":"token_0_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_0_mint"}]}},{"name":"token_1_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_1_mint"}]}},{"name":"create_pool_fee","docs":["create pool fee account"],"writable":true,"address":"8PhehuioLjhJ35A5eavazJSwoXcA4J7WwzgoWDBDFSuY"},{"name":"observation_state","docs":["an account to store oracle observations"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_0_program","docs":["Spl token program or token program 2022"]},{"name":"token_1_program","docs":["Spl token program or token program 2022"]},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"init_amount_0","type":"u64"},{"name":"init_amount_1","type":"u64"},{"name":"open_time","type":"u64"},{"name":"max_trade_fee_rate","type":"u64"},{"name":"volatility_factor","type":"u64"}]},{"name":"initialize_partner","docs":["Creates a new `Partner` account associated with a certain pool","","* `ctx` - The context of accounts","* `name` - The name associated with the partner","* `token_0_token_account` - Token account for receiving token0 tokens","* `token_1_token_account` - Token account for receiving token1 tokens",""],"discriminator":[165,62,179,112,129,50,173,144],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"authority","signer":true},{"name":"pool_state"},{"name":"pool_partners","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,97,114,116,110,101,114,95,105,110,102,111,115]},{"kind":"account","path":"pool_state"}]}},{"name":"partner","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"name","type":"string"},{"name":"token_0_token_account","type":"pubkey"},{"name":"token_1_token_account","type":"pubkey"}]},{"name":"initialize_pool_partners","docs":["Initializes a new `PoolPartnerInfos` account unique to a particular pool","","* `ctx` - The context of accounts","* `partner_share_rate` - A fraction representing the partner's share of protocol fees",""],"discriminator":[99,11,108,186,1,126,209,251],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"pool_state"},{"name":"pool_partners","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,97,114,116,110,101,114,95,105,110,102,111,115]},{"kind":"account","path":"pool_state"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"oracle_based_swap_base_input","docs":["Swap the tokens in the pool base input amount, using oracle price and Curve calculator combined.","","# Arguments","","* `ctx`- The context of accounts","* `amount_in` - input amount to transfer, output to DESTINATION is based on the exchange rate","* `minimum_amount_out` - Minimum amount of output token, prevents excessive slippage",""],"discriminator":[239,82,192,187,160,26,223,223],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"input_token_program","docs":["SPL program for input token transfers"]},{"name":"output_token_program","docs":["SPL program for output token transfers"]},{"name":"input_token_mint","docs":["The mint of input token"]},{"name":"output_token_mint","docs":["The mint of output token"]},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true}],"args":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"}]},{"name":"pool_config_update","discriminator":[196,212,99,63,59,117,248,223],"accounts":[{"name":"authority","signer":true},{"name":"pool_state","writable":true},{"name":"amm_config"}],"args":[{"name":"h1","type":"u128"}]},{"name":"pool_config_update_v2","discriminator":[228,161,199,222,53,172,121,238],"accounts":[{"name":"authority","signer":true},{"name":"pool_state","writable":true},{"name":"amm_config"}],"args":[{"name":"h1","type":"u128"},{"name":"price_fetched_at","type":"u64"}]},{"name":"pool_config_update_v3","discriminator":[123,135,23,15,174,100,120,152],"accounts":[{"name":"authority","signer":true},{"name":"pool_state","writable":true},{"name":"amm_config"},{"name":"observation_state","writable":true},{"name":"actor","pda":{"seeds":[{"kind":"const","value":[97,99,116,111,114]},{"kind":"account","path":"authority"}]}}],"args":[{"name":"h1","type":"u128"},{"name":"h2","type":"u128"},{"name":"price_fetched_at","type":"u64"},{"name":"min_trade_rate_at_oracle_price","type":{"option":"u32"}},{"name":"oracle_price_delay_fee_rate_per_second","type":{"option":"u32"}},{"name":"max_oracle_price_delay_fee","type":{"option":"u32"}},{"name":"drift_factor","type":{"option":"u32"}},{"name":"max_drift_factor","type":{"option":"u32"}},{"name":"volatility_factor","type":{"option":"u64"}}]},{"name":"rebalance_kamino","docs":["Migrate from Meteora Dlmm to Gamma"],"discriminator":[153,94,34,16,92,181,147,215],"accounts":[{"name":"signer","writable":true,"signer":true},{"name":"gamma_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"token_vault","docs":["The vault token account for token 0"],"writable":true},{"name":"token_mint","writable":true},{"name":"kamino_reserve","writable":true},{"name":"kamino_lending_market","writable":true,"address":"7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF"},{"name":"lending_market_authority"},{"name":"reserve_liquidity_supply","writable":true},{"name":"reserve_collateral_mint","writable":true},{"name":"gamma_pool_destination_collateral","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,107,97,109,105,110,111,95,100,101,112,111,115,105,116,115]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint"}]}},{"name":"instruction_sysvar_account","address":"Sysvar1nstructions1111111111111111111111111"},{"name":"liquidity_token_program"},{"name":"collateral_token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"kamino_program","address":"KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"swap_base_input","docs":["Swap the tokens in the pool base input amount","","# Arguments","","* `ctx`- The context of accounts","* `amount_in` - input amount to transfer, output to DESTINATION is based on the exchange rate","* `minimum_amount_out` - Minimum amount of output token, prevents excessive slippage",""],"discriminator":[143,190,90,218,196,30,51,222],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"input_token_program","docs":["SPL program for input token transfers"]},{"name":"output_token_program","docs":["SPL program for output token transfers"]},{"name":"input_token_mint","docs":["The mint of input token"]},{"name":"output_token_mint","docs":["The mint of output token"]},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true}],"args":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"}]},{"name":"swap_base_output","docs":["Swap the tokens in the pool base output amount","","# Arguments","","* `ctx`- The context of accounts","* `max_amount_in` - input amount prevents excessive slippage","* `amount_out` - amount of output token",""],"discriminator":[55,217,98,86,163,74,180,173],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"input_token_program","docs":["SPL program for input token transfers"]},{"name":"output_token_program","docs":["SPL program for output token transfers"]},{"name":"input_token_mint","docs":["The mint of input token"]},{"name":"output_token_mint","docs":["The mint of output token"]},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true}],"args":[{"name":"_max_amount_in","type":"u64"},{"name":"_amount_out","type":"u64"}]},{"name":"update_amm_config","docs":["Updates the owner of the amm config","Must be called by the current owner or admin","","# Arguments","","* `ctx`- The context of accounts","* `trade_fee_rate`- The new trade fee rate of amm config, be set when `param` is 0","* `protocol_fee_rate`- The new protocol fee rate of amm config, be set when `param` is 1","* `fund_fee_rate`- The new fund fee rate of amm config, be set when `param` is 2","* `new_owner`- The config's new owner, be set when `param` is 3","* `new_fund_owner`- The config's new fund owner, be set when `param` is 4","* `param`- The vaule can be 0 | 1 | 2 | 3 | 4, otherwise will report a error",""],"discriminator":[49,60,174,136,154,28,116,200],"accounts":[{"name":"owner","docs":["The amm config owner or admin"],"signer":true,"address":"9QcHinaHcJFdzSHeiF1yGchcuQk3qPFNV13q6dZJbAny"},{"name":"amm_config","docs":["The amm config account to update"],"writable":true}],"args":[{"name":"param","type":"u16"},{"name":"value","type":"u64"}]},{"name":"update_partner","docs":["Authority-permissioned instruction to update certain fields in a pool, e.g token-accounts","","* `ctx` - The context of accounts","* `token_account_0` - (optional) update for the token_0_token_account field","* `token_account_1` - (optional) update for the token_1_token_account field",""],"discriminator":[19,112,236,81,127,55,21,196],"accounts":[{"name":"authority","signer":true,"relations":["partner"]},{"name":"partner","writable":true}],"args":[{"name":"token_account_0","type":{"option":"pubkey"}},{"name":"token_account_1","type":{"option":"pubkey"}}]},{"name":"update_partner_fees","docs":["Updates claimable fee amounts for all `PartnerInfo`s in a `PoolPartnerInfos` account","","* `ctx` - The context of accounts",""],"discriminator":[154,142,122,49,239,161,232,29],"accounts":[{"name":"pool_state","writable":true},{"name":"pool_partners","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,97,114,116,110,101,114,95,105,110,102,111,115]},{"kind":"account","path":"pool_state"}]}}],"args":[]},{"name":"update_pool","docs":["Update pool status for given vaule","","# Arguments","","* `ctx`- The context of accounts","* `param`- The param of pool status","* `status` - The value",""],"discriminator":[239,214,170,78,36,35,30,34],"accounts":[{"name":"authority","signer":true},{"name":"pool_state","writable":true},{"name":"actor","pda":{"seeds":[{"kind":"const","value":[97,99,116,111,114]},{"kind":"account","path":"authority"}]}}],"args":[{"name":"param","type":"u32"},{"name":"value","type":"u64"}]},{"name":"withdraw","docs":["Withdraw lp for token0 ande token1","","# Arguments","","* `ctx`- The context of accounts","* `lp_token_amount` - Amount of pool tokens to burn. User receives an output of token a and b based on the percentage of the pool tokens that are returned.","* `minimum_token_0_amount` - Minimum amount of token 0 to receive, prevents excessive slippage","* `minimum_token_1_amount` - Minimum amount of token 1 to receive, prevents excessive slippage",""],"discriminator":[183,18,70,156,148,109,161,34],"accounts":[{"name":"owner","docs":["Owner of the liquidity provided"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state account"],"writable":true},{"name":"user_pool_liquidity","docs":["User pool liquidity account"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,45,112,111,111,108,45,108,105,113,117,105,100,105,116,121]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"owner"}]}},{"name":"token_0_account","docs":["The owner's token account for receive token_0"],"writable":true},{"name":"token_1_account","docs":["The owner's token account for receive token_1"],"writable":true},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["token Program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token_0 vault"],"writable":true},{"name":"vault_1_mint","docs":["The mint of token_1 vault"],"writable":true},{"name":"pool_partners","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,97,114,116,110,101,114,95,105,110,102,111,115]},{"kind":"account","path":"pool_state"}]}},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"kamino_program","address":"KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD"},{"name":"instruction_sysvar_account","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[{"name":"lp_token_amount","type":"u64"},{"name":"minimum_token_0_amount","type":"u64"},{"name":"minimum_token_1_amount","type":"u64"}]}],"accounts":[{"name":"Actor","discriminator":[46,77,47,204,204,54,34,88]},{"name":"AmmConfig","discriminator":[218,244,33,104,203,203,43,111]},{"name":"ObservationState","discriminator":[122,174,197,53,129,9,165,132]},{"name":"Partner","discriminator":[122,43,246,239,141,56,243,182]},{"name":"PoolPartnerInfos","discriminator":[130,98,226,231,158,49,44,231]},{"name":"PoolState","discriminator":[247,237,227,245,215,195,222,70]},{"name":"RewardInfo","discriminator":[39,7,129,22,241,96,83,133]},{"name":"UserPoolLiquidity","discriminator":[0,141,89,29,236,6,14,15]},{"name":"UserRewardInfo","discriminator":[110,57,251,139,250,236,213,178]}],"types":[{"name":"Actor","type":{"kind":"struct","fields":[{"name":"authority","type":"pubkey"},{"name":"is_admin","type":"bool"}]}},{"name":"AmmConfig","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"disable_create_pool","type":"bool"},{"name":"index","docs":["Config index"],"type":"u16"},{"name":"trade_fee_rate","docs":["The trade fee, denominated in hundredths of bip (10^-6)"],"type":"u64"},{"name":"protocol_fee_rate","docs":["The protocol fee"],"type":"u64"},{"name":"fund_fee_rate","docs":["The fund fee, denominated in hundredths of bip (10^-6)"],"type":"u64"},{"name":"create_pool_fee","docs":["Fee for creating a new pool"],"type":"u64"},{"name":"protocol_owner","docs":["Address of the protocol fee owner"],"type":"pubkey"},{"name":"fund_owner","docs":["Address of the fund fee owner"],"type":"pubkey"},{"name":"referral_project","docs":["Address of the referral project"],"type":"pubkey"},{"name":"max_open_time","docs":["Max open time for a pool in seconds"],"type":"u64"},{"name":"secondary_admin","type":"pubkey"},{"name":"padding","docs":["padding"],"type":{"array":["u64",7]}}]}},{"name":"LpChangeEvent","docs":["Emitted when deposit or withdraw"],"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"lp_amount_before","type":"u64"},{"name":"token_0_vault_before","type":"u64"},{"name":"token_1_vault_before","type":"u64"},{"name":"token_0_amount","type":"u64"},{"name":"token_1_amount","type":"u64"},{"name":"token_0_transfer_fee","type":"u64"},{"name":"token_1_transfer_fee","type":"u64"},{"name":"change_type","type":"u8"}]}},{"name":"MigrationEvent","docs":["Emitted when migration"],"type":{"kind":"struct","fields":[{"name":"from_pool","type":"pubkey"},{"name":"to_pool","type":"pubkey"},{"name":"token_0_amount_withdrawn","type":"u64"},{"name":"token_1_amount_withdrawn","type":"u64"},{"name":"lp_tokens_migrated","type":"u128"}]}},{"name":"Observation","docs":["The element of observations in ObservationState"],"serialization":"bytemuckunsafe","repr":{"kind":"rust","packed":true},"type":{"kind":"struct","fields":[{"name":"block_timestamp","docs":["The block timestamp of the observation"],"type":"u64"},{"name":"cumulative_token_0_price_x32","docs":["The cumulative of token0 price during the duration time, Q32.32, the remaining 64 bit for overflow"],"type":"u128"},{"name":"cumulative_token_1_price_x32","docs":["The cumulative of token1 price during the duration time, Q32.32, the remaining 64 bit for overflow"],"type":"u128"}]}},{"name":"ObservationState","serialization":"bytemuckunsafe","repr":{"kind":"rust","packed":true},"type":{"kind":"struct","fields":[{"name":"initialized","docs":["Whether the ObservationState is enabled"],"type":"bool"},{"name":"observation_index","docs":["The most recently updated index of the observations array"],"type":"u16"},{"name":"pool_id","type":"pubkey"},{"name":"observations","docs":["observation array"],"type":{"array":[{"defined":{"name":"Observation"}},100]}},{"name":"padding","docs":["padding"],"type":{"array":["u64",4]}}]}},{"name":"Partner","docs":["Account storing information for a protocol participating in the Gamma partner program"],"type":{"kind":"struct","fields":[{"name":"name","docs":["Protocol name"],"type":"string"},{"name":"authority","docs":["The authority for this account"],"type":"pubkey"},{"name":"pool_state","docs":["The pool which this partner account belongs to"],"type":"pubkey"},{"name":"token_0_token_account","docs":["The token-account that receives token0 tokens"],"type":"pubkey"},{"name":"token_1_token_account","docs":["The token-account that receives token1 tokens"],"type":"pubkey"}]}},{"name":"PartnerInfo","serialization":"bytemuckunsafe","repr":{"kind":"rust","packed":true},"type":{"kind":"struct","fields":[{"name":"partner","docs":["The address of the partner account."],"type":"pubkey"},{"name":"lp_token_linked_with_partner","docs":["This stores the LP tokens that are linked with the partner, i.e owned by customers of the partner."],"type":"u64"},{"name":"total_claimed_fee_amount_token_0","docs":["The total fee-amount token0 claimed by the partner"],"type":"u64"},{"name":"total_claimed_fee_amount_token_1","docs":["The total fee-amount token1 claimed by the partner"],"type":"u64"},{"name":"total_earned_fee_amount_token_0","docs":["The total fee-amount token0 calculated for the partner"],"type":"u64"},{"name":"total_earned_fee_amount_token_1","docs":["The total fee-amount token1 calculated for the partner"],"type":"u64"}]}},{"name":"PoolPartnerInfos","docs":["PDA storing all the information for valid pool partners"],"serialization":"bytemuckunsafe","repr":{"kind":"rust","packed":true},"type":{"kind":"struct","fields":[{"name":"last_observed_fee_amount_token_0","docs":["The observed fee-amount token0 as at the last infos update"],"type":"u64"},{"name":"last_observed_fee_amount_token_1","docs":["The observed fee-amount token1 as at the last infos update"],"type":"u64"},{"name":"infos","docs":["Partner infos"],"type":{"array":[{"defined":{"name":"PartnerInfo"}},5]}}]}},{"name":"PoolState","serialization":"bytemuckunsafe","repr":{"kind":"rust","packed":true},"type":{"kind":"struct","fields":[{"name":"amm_config","docs":["To which AmmConfig the pool belongs to"],"type":"pubkey"},{"name":"pool_creator","docs":["Pool Creator"],"type":"pubkey"},{"name":"token_0_vault","docs":["Vault to store Token A of the pool"],"type":"pubkey"},{"name":"token_1_vault","docs":["Vault to store Token B of the pool"],"type":"pubkey"},{"name":"h1","docs":["Scaled to 9 decimal places.","This means `token1/token0`"],"type":"u128"},{"name":"h2","type":"u64"},{"name":"h3","type":"u32"},{"name":"h4","type":"u32"},{"name":"token_0_mint","docs":["Mint info of Token A"],"type":"pubkey"},{"name":"token_1_mint","docs":["Mint info of Token B"],"type":"pubkey"},{"name":"token_0_program","docs":["token_0 program"],"type":"pubkey"},{"name":"token_1_program","docs":["token_1_program"],"type":"pubkey"},{"name":"observation_key","docs":["Observation account to store the oracle data"],"type":"pubkey"},{"name":"auth_bump","type":"u8"},{"name":"status","docs":["Bitwise represenation of the state of the pool","Bit0: 1 - Disable Deposit(value will be 1), 0 - Deposit can be done(normal)","Bit1: 1 - Disable Withdraw(value will be 2), 0 - Withdraw can be done(normal)","Bit2: 1 - Disable Swap(value will be 4), 0 - Swap can be done(normal)"],"type":"u8"},{"name":"_padding2","docs":["lp_mint decimals"],"type":"u8"},{"name":"mint_0_decimals","docs":["mint0 and mint1 decimals"],"type":"u8"},{"name":"mint_1_decimals","type":"u8"},{"name":"lp_supply","docs":["True circulating supply of lp_mint tokens without burns and lock-ups"],"type":"u64"},{"name":"protocol_fees_token_0","docs":["The amount of token_0 and token_1 owed to Liquidity Provider"],"type":"u64"},{"name":"protocol_fees_token_1","type":"u64"},{"name":"fund_fees_token_0","type":"u64"},{"name":"fund_fees_token_1","type":"u64"},{"name":"open_time","docs":["The timestamp allowed for swap in the pool"],"type":"u64"},{"name":"recent_epoch","docs":["recent epoch"],"type":"u64"},{"name":"cumulative_trade_fees_token_0","docs":["Trade fees of token_0 after every swap"],"type":"u128"},{"name":"cumulative_trade_fees_token_1","docs":["Trade fees of token_1 after every swap"],"type":"u128"},{"name":"cumulative_volume_token_0","docs":["Cummulative volume of token_0"],"type":"u128"},{"name":"cumulative_volume_token_1","docs":["Cummulative volume of token_1"],"type":"u128"},{"name":"latest_dynamic_fee_rate","docs":["latest dynamic fee rate"],"type":"u64"},{"name":"max_trade_fee_rate","type":"u64"},{"name":"volatility_factor","type":"u64"},{"name":"token_0_vault_amount","type":"u64"},{"name":"token_1_vault_amount","type":"u64"},{"name":"max_shared_token0","type":"u64"},{"name":"max_shared_token1","type":"u64"},{"name":"h16","type":"u32"},{"name":"h15","type":"u32"},{"name":"h14","type":"u32"},{"name":"h13","type":"u32"},{"name":"h12","type":"u32"},{"name":"h10","type":"u32"},{"name":"_padding3","type":{"array":["u8",8]}},{"name":"token_0_amount_in_kamino","type":"u64"},{"name":"token_1_amount_in_kamino","type":"u64"},{"name":"withdrawn_kamino_profit_token_0","type":"u64"},{"name":"withdrawn_kamino_profit_token_1","type":"u64"},{"name":"partner_share_rate","docs":["Fraction of protocol tokens shared with partners. Denominator is 1_000_000"],"type":"u64"},{"name":"partner_protocol_fees_token_0","docs":["amount of protocol fees owed to referral partners(token0)"],"type":"u64"},{"name":"partner_protocol_fees_token_1","docs":["amount of protocol fees owed to referral partners(token1)"],"type":"u64"},{"name":"min_swap_at_spot_price","docs":["We later in future want to allow any LP to come and create a Pool of sol-usdc, where they can configure all the params.","They also might want to define a max and min price for swaps, until which they want to allow swaps.","This can be thought of similar to the ranges in CLMM.","{token1/token0} with 9 Decimals"],"type":"u64"},{"name":"max_swap_at_spot_price","type":"u64"},{"name":"h20","type":"u128"},{"name":"padding","docs":["padding"],"type":{"array":["u64",1]}}]}},{"name":"RewardInfo","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"start_at","type":"u64"},{"name":"end_rewards_at","type":"u64"},{"name":"mint","type":"pubkey"},{"name":"total_to_disburse","type":"u64"},{"name":"rewarded_by","type":"pubkey"},{"name":"amount_disbursed","type":"u64"}]}},{"name":"SwapEvent","type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"input_vault_before","docs":["pool vault - trade_fees"],"type":"u64"},{"name":"output_vault_before","docs":["pool_vault - trade_fees"],"type":"u64"},{"name":"input_amount","docs":["calculate result without transfer fees"],"type":"u64"},{"name":"output_amount","docs":["calculate result without transfer fees"],"type":"u64"},{"name":"input_mint","docs":["input mint for the swap"],"type":"pubkey"},{"name":"output_mint","docs":["output mint for the swap"],"type":"pubkey"},{"name":"input_transfer_fee","docs":["transfer fees on input token using token extensions"],"type":"u64"},{"name":"output_transfer_fee","docs":["transfer fees on output token using token extensions"],"type":"u64"},{"name":"base_input","type":"bool"},{"name":"dynamic_fee","docs":["dynamic_fees after this swap"],"type":"u128"}]}},{"name":"UserPoolLiquidity","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"pool_state","type":"pubkey"},{"name":"token_0_deposited","type":"u128"},{"name":"token_1_deposited","type":"u128"},{"name":"token_0_withdrawn","type":"u128"},{"name":"token_1_withdrawn","type":"u128"},{"name":"lp_tokens_owned","type":"u128"},{"name":"_p1","type":"u64"},{"name":"_p2","type":"u8"},{"name":"first_investment_at","type":"u64"},{"name":"partner","type":{"option":"pubkey"}},{"name":"padding","type":{"array":["u8",15]}}]}},{"name":"UserRewardInfo","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"reward_info","type":"pubkey"},{"name":"pool_state","type":"pubkey"},{"name":"total_claimed","type":"u64"},{"name":"total_rewards","type":"u64"},{"name":"rewards_last_calculated_at","type":"u64"}]}}]} \ No newline at end of file diff --git a/idls/goosefx_v2.GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn.json b/idls/goosefx_v2.GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn.json new file mode 100644 index 0000000..da5ac1e --- /dev/null +++ b/idls/goosefx_v2.GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn.json @@ -0,0 +1 @@ +{"version":"0.3.0","name":"gfx_ssl_v2","instructions":[{"name":"crankPriceHistories","accounts":[{"name":"poolRegistry","isMut":false,"isSigner":false}],"args":[]},{"name":"internalSwap","accounts":[{"name":"poolRegistry","isMut":false,"isSigner":false},{"name":"pair","isMut":true,"isSigner":false},{"name":"sslAMainToken","isMut":true,"isSigner":false},{"name":"sslBMainToken","isMut":true,"isSigner":false},{"name":"sslASecondaryToken","isMut":true,"isSigner":false},{"name":"sslBSecondaryToken","isMut":true,"isSigner":false},{"name":"tokenAPriceHistory","isMut":true,"isSigner":false},{"name":"tokenAOracle","isMut":false,"isSigner":false},{"name":"tokenBPriceHistory","isMut":true,"isSigner":false},{"name":"tokenBOracle","isMut":false,"isSigner":false},{"name":"sslPoolASigner","isMut":false,"isSigner":false},{"name":"sslPoolBSigner","isMut":false,"isSigner":false},{"name":"eventEmitter","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"claimFees","accounts":[{"name":"poolRegistry","isMut":false,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"sslFeeVault","isMut":true,"isSigner":false},{"name":"ownerAta","isMut":true,"isSigner":false},{"name":"liquidityAccount","isMut":true,"isSigner":false},{"name":"eventEmitter","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"createLiquidityAccount","accounts":[{"name":"poolRegistry","isMut":false,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"liquidityAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"eventEmitter","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"closeLiquidityAccount","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"rentRecipient","isMut":true,"isSigner":false},{"name":"liquidityAccount","isMut":true,"isSigner":false},{"name":"eventEmitter","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"deposit","accounts":[{"name":"liquidityAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"userAta","isMut":true,"isSigner":false},{"name":"sslPoolSigner","isMut":false,"isSigner":false},{"name":"poolVault","isMut":true,"isSigner":false},{"name":"sslFeeVault","isMut":true,"isSigner":false},{"name":"poolRegistry","isMut":true,"isSigner":false},{"name":"eventEmitter","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdraw","accounts":[{"name":"liquidityAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"userAta","isMut":true,"isSigner":false},{"name":"sslPoolSigner","isMut":false,"isSigner":false},{"name":"poolVault","isMut":true,"isSigner":false},{"name":"sslFeeVault","isMut":true,"isSigner":false},{"name":"poolRegistry","isMut":true,"isSigner":false},{"name":"eventEmitter","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"swap","accounts":[{"name":"pair","isMut":true,"isSigner":false},{"name":"poolRegistry","isMut":true,"isSigner":false},{"name":"userWallet","isMut":false,"isSigner":false},{"name":"userWallet","isMut":false,"isSigner":true},{"name":"sslPoolInSigner","isMut":false,"isSigner":false},{"name":"sslPoolOutSigner","isMut":false,"isSigner":false},{"name":"userAtaIn","isMut":true,"isSigner":false},{"name":"userAtaOut","isMut":true,"isSigner":false},{"name":"sslOutMainVault","isMut":true,"isSigner":false},{"name":"sslOutSecondaryVault","isMut":true,"isSigner":false},{"name":"sslInMainVault","isMut":true,"isSigner":false},{"name":"sslInSecondaryVault","isMut":true,"isSigner":false},{"name":"sslOutFeeVault","isMut":true,"isSigner":false},{"name":"sslOutFeeDestination","isMut":true,"isSigner":false},{"name":"outputTokenPriceHistory","isMut":true,"isSigner":false},{"name":"outputTokenOracle","isMut":false,"isSigner":false},{"name":"inputTokenPriceHistory","isMut":true,"isSigner":false},{"name":"inputTokenOracle","isMut":false,"isSigner":false},{"name":"eventEmitter","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amountIn","type":"u64"},{"name":"minOut","type":"u64"}]}],"accounts":[{"name":"EventEmitter","type":{"kind":"struct","fields":[{"name":"eventId","type":"i64"}]}},{"name":"LiquidityAccount","type":{"kind":"struct","fields":[{"name":"poolRegistry","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"amountDeposited","type":"u64"},{"name":"lastObservedTap","type":"u64"},{"name":"lastClaimed","type":"i64"},{"name":"totalEarned","type":"u64"},{"name":"createdAt","type":"i64"},{"name":"lastDepositAt","type":"u64"},{"name":"lastObsRewardsPerShare","type":{"array":["u8",16]}},{"name":"space","type":{"array":["u8",104]}}]}},{"name":"HistoricalDecimal","type":{"kind":"struct","fields":[{"name":"num","type":"i64"},{"name":"scale","type":"u32"},{"name":"pad0","type":{"array":["u8",4]}}]}},{"name":"HistoricalPrice","type":{"kind":"struct","fields":[{"name":"price","type":{"defined":"HistoricalDecimal"}},{"name":"slot","type":"u64"}]}},{"name":"OraclePriceHistory","type":{"kind":"struct","fields":[{"name":"oracleType","type":"u8"},{"name":"minimumElapsedSlots","type":"u8"},{"name":"maxSlotPriceStaleness","type":"u8"},{"name":"backupOracleType","type":"u8"},{"name":"backupOracle2Type","type":"u8"},{"name":"pad0","type":{"array":["u8",3]}},{"name":"poolRegistry","type":"publicKey"},{"name":"oracleAddress","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"numUpdates","type":"u64"},{"name":"backupOracle","type":"publicKey"},{"name":"latestBackupOraclePrice","type":{"defined":"HistoricalPrice"}},{"name":"backupOracle2","type":"publicKey"},{"name":"latestBackupOracle2Price","type":{"defined":"HistoricalPrice"}},{"name":"space","type":{"array":["u8",16]}},{"name":"priceHistory","type":{"array":[{"defined":"HistoricalPrice"},256]}}]}},{"name":"Pair","type":{"kind":"struct","fields":[{"name":"poolRegistry","type":"publicKey"},{"name":"mints","type":{"defined":"(Pubkey,Pubkey)"}},{"name":"feeCollector","type":{"defined":"(Pubkey,Pubkey)"}},{"name":"normalRouteFeeRates","type":{"defined":"(u16,u16)"}},{"name":"totalFeesGeneratedNative","type":{"defined":"([u8;16],[u8;16])"}},{"name":"totalHistoricalVolume","type":{"array":["u8",16]}},{"name":"totalInternallySwapped","type":{"defined":"([u8;16],[u8;16])"}},{"name":"preferredRouteFeeRates","type":{"defined":"(u16,u16)"}},{"name":"space","type":{"array":["u8",124]}}]}},{"name":"SSLPool","type":{"kind":"struct","fields":[{"name":"status","type":"u8"},{"name":"assetType","type":"u8"},{"name":"pad0","type":{"array":["u8",6]}},{"name":"mint","type":"publicKey"},{"name":"mintDecimals","type":"u8"},{"name":"bump","type":"u8"},{"name":"pad1","type":{"array":["u8",6]}},{"name":"totalAccumulatedLpReward","type":"u64"},{"name":"totalLiquidityDeposits","type":"u64"},{"name":"oraclePriceHistories","type":{"array":["publicKey",3]}},{"name":"mathParams","type":{"defined":"SSLMathParams"}},{"name":"rewardsPerShare","type":{"array":["u8",16]}},{"name":"space","type":{"array":["u8",48]}}]}},{"name":"PoolRegistry","type":{"kind":"struct","fields":[{"name":"admin","type":"publicKey"},{"name":"seed","type":"publicKey"},{"name":"suspendAdmin","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"pricingAlgo","type":"u8"},{"name":"pad0","type":{"array":["u8",6]}},{"name":"numEntries","type":"u32"},{"name":"pad1","type":{"array":["u8",4]}},{"name":"categoricalPoolTokenRatios","type":{"array":["u16",16]}},{"name":"space","type":{"array":["u8",96]}},{"name":"entries","type":{"array":[{"defined":"SSLPool"},32]}}]}}],"types":[{"name":"ConfigPairParams","type":{"kind":"struct","fields":[{"name":"mintOneNormalRouteFeeRate","type":{"option":"u16"}},{"name":"mintTwoNormalRouteFeeRate","type":{"option":"u16"}},{"name":"mintOnePreferredRouteFeeRate","type":{"option":"u16"}},{"name":"mintTwoPreferredRouteFeeRate","type":{"option":"u16"}}]}},{"name":"SSLMathConfig","type":{"kind":"struct","fields":[{"name":"meanWindow","type":{"option":"u8"}},{"name":"stdWindow","type":{"option":"u8"}},{"name":"fixedPriceDistance","type":{"option":"u16"}},{"name":"minimumPriceDistance","type":{"option":"u16"}},{"name":"stdWeight","type":{"option":"u32"}},{"name":"latestPriceWeight","type":{"option":"u16"}},{"name":"checkPriceGapCount","type":{"option":"u16"}}]}},{"name":"SSLMathParams","type":{"kind":"struct","fields":[{"name":"meanWindow","type":"u8"},{"name":"stdWindow","type":"u8"},{"name":"fixedPriceDistance","type":"u16"},{"name":"minimumPriceDistance","type":"u16"},{"name":"deprecated0","type":"u16"},{"name":"latestPriceWeight","type":"u16"},{"name":"checkPriceGapCount","type":"u16"},{"name":"pad0","type":{"array":["u8",4]}},{"name":"stdWeight","type":"u32"},{"name":"pad1","type":{"array":["u8",4]}},{"name":"space","type":{"array":["u8",32]}}]}},{"name":"MaxPoolTokenRatio","type":{"kind":"struct","fields":[{"name":"inputToken","type":{"defined":"AssetType"}},{"name":"outputToken","type":{"defined":"AssetType"}},{"name":"poolTokenRatio","type":"u16"}]}},{"name":"PoolRegistryConfig","type":{"kind":"struct","fields":[{"name":"newAdmin","type":{"option":"publicKey"}},{"name":"newSuspendAdmin","type":{"option":"publicKey"}},{"name":"maxPoolTokenRatios","type":{"vec":{"defined":"MaxPoolTokenRatio"}}},{"name":"pricingAlgo","type":{"option":{"defined":"PricingAlgo"}}}]}},{"name":"OracleType","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Pyth"},{"name":"Switchboardv2"},{"name":"Invalid"}]}},{"name":"AssetType","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"BlueChip"},{"name":"Volatile"},{"name":"Stable"},{"name":"Invalid"}]}},{"name":"ConfigSSLPoolStatus","type":{"kind":"enum","variants":[{"name":"Active"},{"name":"Suspended"},{"name":"Locked"}]}},{"name":"PricingAlgo","type":{"kind":"enum","variants":[{"name":"Version1"},{"name":"Version2"},{"name":"Version3"},{"name":"Invalid"}]}},{"name":"SSLPoolStatus","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Active"},{"name":"Suspended"},{"name":"Locked"},{"name":"Invalid"}]}}]} \ No newline at end of file diff --git a/idls/guacswap.Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1.json b/idls/guacswap.Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1.json new file mode 100644 index 0000000..4f0b79b --- /dev/null +++ b/idls/guacswap.Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1.json @@ -0,0 +1 @@ +{"version":"0.1.1","name":"guacswap","instructions":[{"name":"createPool","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":true},{"name":"poolYAccount","isMut":true,"isSigner":true},{"name":"adminXAccount","isMut":true,"isSigner":false},{"name":"adminYAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"projectOwner","isMut":false,"isSigner":false},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"lpFee","type":{"defined":"FixedPoint"}},{"name":"buybackFee","type":{"defined":"FixedPoint"}},{"name":"projectFee","type":{"defined":"FixedPoint"}},{"name":"mercantiFee","type":{"defined":"FixedPoint"}},{"name":"initialTokenX","type":{"defined":"Token"}},{"name":"initialTokenY","type":{"defined":"Token"}},{"name":"bump","type":"u8"}]},{"name":"createProvider","accounts":[{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"ownerXAccount","isMut":true,"isSigner":false},{"name":"ownerYAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"tokenXAmount","type":{"defined":"Token"}},{"name":"tokenYAmount","type":{"defined":"Token"}},{"name":"bump","type":"u8"}]},{"name":"createState","accounts":[{"name":"state","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"addTokens","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"ownerXAccount","isMut":true,"isSigner":false},{"name":"ownerYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"ownerMarcoAccount","isMut":true,"isSigner":false},{"name":"ownerProjectFirstAccount","isMut":true,"isSigner":false},{"name":"ownerProjectSecondAccount","isMut":true,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"deltaX","type":{"defined":"Token"}},{"name":"deltaY","type":{"defined":"Token"}}]},{"name":"withdrawBuyback","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"buybackXAccount","isMut":true,"isSigner":false},{"name":"buybackYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"swap","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"swapperXAccount","isMut":true,"isSigner":false},{"name":"swapperYAccount","isMut":true,"isSigner":false},{"name":"swapper","isMut":true,"isSigner":true},{"name":"referrerXAccount","isMut":true,"isSigner":false},{"name":"referrerYAccount","isMut":true,"isSigner":false},{"name":"referrer","isMut":true,"isSigner":false},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"deltaIn","type":{"defined":"Token"}},{"name":"priceLimit","type":{"defined":"FixedPoint"}},{"name":"xToY","type":"bool"}]},{"name":"withdrawShares","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"ownerXAccount","isMut":true,"isSigner":false},{"name":"ownerYAccount","isMut":true,"isSigner":false},{"name":"ownerMarcoAccount","isMut":true,"isSigner":false},{"name":"ownerProjectFirstAccount","isMut":true,"isSigner":false},{"name":"ownerProjectSecondAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"shares","type":{"defined":"Token"}}]},{"name":"withdrawLpFee","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"ownerXAccount","isMut":true,"isSigner":false},{"name":"ownerYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"withdrawProjectFee","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"projectOwnerXAccount","isMut":true,"isSigner":false},{"name":"projectOwnerYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"projectOwner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"createFarm","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenMarco","isMut":false,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":true},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"supply","type":{"defined":"Token"}},{"name":"duration","type":"u64"},{"name":"bump","type":"u8"}]},{"name":"createDualFarm","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenMarco","isMut":false,"isSigner":false},{"name":"tokenProjectFirst","isMut":false,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":true},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":true},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"adminProjectFirstAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"supplyMarco","type":{"defined":"Token"}},{"name":"supplyProjectFirst","type":{"defined":"Token"}},{"name":"duration","type":"u64"},{"name":"bump","type":"u8"}]},{"name":"createTripleFarm","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarco","isMut":false,"isSigner":false},{"name":"tokenProjectFirst","isMut":false,"isSigner":false},{"name":"tokenProjectSecond","isMut":false,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":true},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":true},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":true},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"adminProjectFirstAccount","isMut":true,"isSigner":false},{"name":"adminProjectSecondAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"supplyMarco","type":{"defined":"Token"}},{"name":"supplyProjectFirst","type":{"defined":"Token"}},{"name":"supplyProjectSecond","type":{"defined":"Token"}},{"name":"duration","type":"u64"},{"name":"bump","type":"u8"}]},{"name":"withdrawRewards","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"ownerMarcoAccount","isMut":true,"isSigner":false},{"name":"ownerProjectFirstAccount","isMut":true,"isSigner":false},{"name":"ownerProjectSecondAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"closePool","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"buybackXAccount","isMut":true,"isSigner":false},{"name":"buybackYAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"withdrawMercantiFee","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"mercantiXAccount","isMut":true,"isSigner":false},{"name":"mercantiYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"addSupply","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"adminProjectFirstAccount","isMut":true,"isSigner":false},{"name":"adminProjectSecondAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"supplyMarco","type":{"defined":"Token"}},{"name":"supplyProjectFirst","type":{"defined":"Token"}},{"name":"supplyProjectSecond","type":{"defined":"Token"}},{"name":"duration","type":"u64"}]},{"name":"updateFees","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false}],"args":[{"name":"newBuybackFee","type":{"defined":"FixedPoint"}},{"name":"newProjectFee","type":{"defined":"FixedPoint"}},{"name":"newProviderFee","type":{"defined":"FixedPoint"}},{"name":"newMercantiFee","type":{"defined":"FixedPoint"}}]},{"name":"resetFarm","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"adminProjectFirstAccount","isMut":true,"isSigner":false},{"name":"adminProjectSecondAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateRewardTokens","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"tokenMarco","isMut":false,"isSigner":false},{"name":"newTokenMarcoAccount","isMut":true,"isSigner":true},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"forceWithdrawRewards","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","account":"Provider","path":"provider.owner"}]}},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"ownerMarcoAccount","isMut":true,"isSigner":false},{"name":"ownerProjectFirstAccount","isMut":true,"isSigner":false},{"name":"ownerProjectSecondAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"Farm","type":{"kind":"struct","fields":[{"name":"pool","type":"publicKey"},{"name":"tokens","type":{"array":["publicKey",3]}},{"name":"tokenAccounts","type":{"array":["publicKey",3]}},{"name":"supply","type":{"array":[{"defined":"Token"},3]}},{"name":"supplyLeft","type":{"array":[{"defined":"Token"},3]}},{"name":"accumulatedSecondsPerShare","type":{"defined":"FixedPoint"}},{"name":"offsetSecondsPerShare","type":{"defined":"FixedPoint"}},{"name":"startTime","type":"u64"},{"name":"endTime","type":"u64"},{"name":"lastUpdate","type":"u64"},{"name":"bump","type":"u8"},{"name":"farmType","type":{"defined":"FarmType"}}]}},{"name":"Pool","type":{"kind":"struct","fields":[{"name":"tokenX","type":"publicKey"},{"name":"tokenY","type":"publicKey"},{"name":"poolXAccount","type":"publicKey"},{"name":"poolYAccount","type":"publicKey"},{"name":"admin","type":"publicKey"},{"name":"projectOwner","type":"publicKey"},{"name":"tokenXReserve","type":{"defined":"Token"}},{"name":"tokenYReserve","type":{"defined":"Token"}},{"name":"selfShares","type":{"defined":"Token"}},{"name":"allShares","type":{"defined":"Token"}},{"name":"buybackAmountX","type":{"defined":"Token"}},{"name":"buybackAmountY","type":{"defined":"Token"}},{"name":"projectAmountX","type":{"defined":"Token"}},{"name":"projectAmountY","type":{"defined":"Token"}},{"name":"mercantiAmountX","type":{"defined":"Token"}},{"name":"mercantiAmountY","type":{"defined":"Token"}},{"name":"lpAccumulatorX","type":{"defined":"FixedPoint"}},{"name":"lpAccumulatorY","type":{"defined":"FixedPoint"}},{"name":"constK","type":{"defined":"Product"}},{"name":"price","type":{"defined":"FixedPoint"}},{"name":"lpFee","type":{"defined":"FixedPoint"}},{"name":"buybackFee","type":{"defined":"FixedPoint"}},{"name":"projectFee","type":{"defined":"FixedPoint"}},{"name":"mercantiFee","type":{"defined":"FixedPoint"}},{"name":"farmCount","type":"u64"},{"name":"bump","type":"u8"}]}},{"name":"Provider","type":{"kind":"struct","fields":[{"name":"tokenX","type":"publicKey"},{"name":"tokenY","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"shares","type":{"defined":"Token"}},{"name":"lastFeeAccumulatorX","type":{"defined":"FixedPoint"}},{"name":"lastFeeAccumulatorY","type":{"defined":"FixedPoint"}},{"name":"lastSecondsPerShare","type":{"defined":"FixedPoint"}},{"name":"lastWithdrawTime","type":"u64"},{"name":"tokensOwedX","type":{"defined":"Token"}},{"name":"tokensOwedY","type":{"defined":"Token"}},{"name":"currentFarmCount","type":"u64"},{"name":"bump","type":"u8"}]}},{"name":"State","type":{"kind":"struct","fields":[{"name":"admin","type":"publicKey"},{"name":"programAuthority","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"nonce","type":"u8"}]}}],"types":[{"name":"FixedPoint","type":{"kind":"struct","fields":[{"name":"v","type":"u128"}]}},{"name":"Token","type":{"kind":"struct","fields":[{"name":"v","type":"u64"}]}},{"name":"Product","type":{"kind":"struct","fields":[{"name":"v","type":"u128"}]}},{"name":"FarmType","type":{"kind":"enum","variants":[{"name":"Single"},{"name":"Dual"},{"name":"Triple"}]}}],"errors":[{"code":6000,"name":"DeltaTooBig","msg":"Delta greater than provider's tokens"},{"code":6001,"name":"TokenUnderflow","msg":"Token amount underflow"},{"code":6002,"name":"WrongRatio","msg":"Wrong tokens ratio"},{"code":6003,"name":"TooMuchShares","msg":"Too much shares provided"},{"code":6004,"name":"SwapToBig","msg":"Swap too big"},{"code":6005,"name":"FeeExceeded","msg":"Fee exceeded 100%"},{"code":6007,"name":"ScalesNotEqual","msg":"Scales have to be equal"},{"code":6008,"name":"FeeExceededDeltaOut","msg":"Fees exceeded delta_out"},{"code":6009,"name":"PriceLimitExceeded","msg":"Price limit exceeded"},{"code":6010,"name":"MintMismatch","msg":"Mint mismatch"},{"code":6011,"name":"TokensAreTheSame","msg":"Tokens are the same"},{"code":6012,"name":"WrongFarm","msg":"Cannot add supply to wrong farm"},{"code":6013,"name":"RewardsExceedingSupply","msg":"Cannot withdraw rewards exceeding supply left"},{"code":6014,"name":"FarmNotEnded","msg":"Farm has not ended, cannot add additional rewards"},{"code":6015,"name":"ZeroAmount","msg":"Must provide a nonzero amount"},{"code":6016,"name":"InvariantDecreased","msg":"Invariant has changed"}]} \ No newline at end of file diff --git a/idls/helium_treasury_management.treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5.json b/idls/helium_treasury_management.treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5.json new file mode 100644 index 0000000..ce12606 --- /dev/null +++ b/idls/helium_treasury_management.treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5.json @@ -0,0 +1 @@ +{"address":"treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5","metadata":{"name":"treasury_management","version":"0.2.3","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"initialize_treasury_management_v0","discriminator":[149,3,201,108,130,56,56,210],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"treasury_management","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,114,101,97,115,117,114,121,95,109,97,110,97,103,101,109,101,110,116]},{"kind":"account","path":"supply_mint"}]}},{"name":"treasury_mint"},{"name":"supply_mint"},{"name":"mint_authority","signer":true},{"name":"circuit_breaker","writable":true,"pda":{"seeds":[{"kind":"const","value":[97,99,99,111,117,110,116,95,119,105,110,100,111,119,101,100,95,98,114,101,97,107,101,114]},{"kind":"account","path":"treasury"}],"program":{"kind":"account","path":"circuit_breaker_program"}}},{"name":"treasury","writable":true,"pda":{"seeds":[{"kind":"account","path":"treasury_management"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"treasury_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"circuit_breaker_program","address":"circAbx64bbsscPbQzZAUvuXpHqrCe6fLMzc2uKXz9g"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"args","type":{"defined":{"name":"InitializeTreasuryManagementArgsV0"}}}]},{"name":"redeem_v0","discriminator":[235,127,171,139,119,77,235,118],"accounts":[{"name":"treasury_management","pda":{"seeds":[{"kind":"const","value":[116,114,101,97,115,117,114,121,95,109,97,110,97,103,101,109,101,110,116]},{"kind":"account","path":"supply_mint"}]}},{"name":"treasury_mint","relations":["treasury_management"]},{"name":"supply_mint","writable":true,"relations":["treasury_management"]},{"name":"treasury","writable":true,"relations":["treasury_management"]},{"name":"circuit_breaker","writable":true,"pda":{"seeds":[{"kind":"const","value":[97,99,99,111,117,110,116,95,119,105,110,100,111,119,101,100,95,98,114,101,97,107,101,114]},{"kind":"account","path":"treasury"}],"program":{"kind":"account","path":"circuit_breaker_program"}}},{"name":"from","writable":true},{"name":"to","writable":true},{"name":"owner","signer":true,"relations":["from"]},{"name":"circuit_breaker_program","address":"circAbx64bbsscPbQzZAUvuXpHqrCe6fLMzc2uKXz9g"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"args","type":{"defined":{"name":"RedeemArgsV0"}}}]},{"name":"update_treasury_management_v0","discriminator":[209,139,90,226,249,149,89,217],"accounts":[{"name":"treasury_management","writable":true},{"name":"authority","signer":true,"relations":["treasury_management"]}],"args":[{"name":"args","type":{"defined":{"name":"UpdateTreasuryManagementArgsV0"}}}]}],"accounts":[{"name":"AccountWindowedCircuitBreakerV0","discriminator":[134,11,69,100,90,132,174,187]},{"name":"TreasuryManagementV0","discriminator":[68,111,120,209,16,110,215,59]}],"errors":[{"code":6000,"name":"Frozen","msg":"Treasury management is currently frozen"},{"code":6001,"name":"ArithmeticError","msg":"Error in arithmetic"}],"types":[{"name":"AccountWindowedCircuitBreakerV0","type":{"kind":"struct","fields":[{"name":"token_account","type":"pubkey"},{"name":"authority","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"config","type":{"defined":{"name":"WindowedCircuitBreakerConfigV0"}}},{"name":"last_window","type":{"defined":{"name":"WindowV0"}}},{"name":"bump_seed","type":"u8"}]}},{"name":"Curve","type":{"kind":"enum","variants":[{"name":"ExponentialCurveV0","fields":[{"name":"k","type":"u128"}]}]}},{"name":"InitializeTreasuryManagementArgsV0","type":{"kind":"struct","fields":[{"name":"authority","type":"pubkey"},{"name":"curve","type":{"defined":{"name":"Curve"}}},{"name":"freeze_unix_time","type":"i64"},{"name":"window_config","type":{"defined":{"name":"WindowedCircuitBreakerConfigV0"}}}]}},{"name":"RedeemArgsV0","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"expected_output_amount","type":"u64"}]}},{"name":"ThresholdType","type":{"kind":"enum","variants":[{"name":"Percent"},{"name":"Absolute"}]}},{"name":"TreasuryManagementV0","type":{"kind":"struct","fields":[{"name":"treasury_mint","type":"pubkey"},{"name":"supply_mint","type":"pubkey"},{"name":"authority","type":"pubkey"},{"name":"treasury","type":"pubkey"},{"name":"curve","docs":["The bonding curve to use"],"type":{"defined":{"name":"Curve"}}},{"name":"freeze_unix_time","docs":["Freeze this curve at this time."],"type":"i64"},{"name":"bump_seed","type":"u8"}]}},{"name":"UpdateTreasuryManagementArgsV0","type":{"kind":"struct","fields":[{"name":"authority","type":"pubkey"},{"name":"curve","type":{"defined":{"name":"Curve"}}},{"name":"freeze_unix_time","type":"i64"}]}},{"name":"WindowV0","type":{"kind":"struct","fields":[{"name":"last_aggregated_value","type":"u64"},{"name":"last_unix_timestamp","type":"i64"}]}},{"name":"WindowedCircuitBreakerConfigV0","type":{"kind":"struct","fields":[{"name":"window_size_seconds","type":"u64"},{"name":"threshold_type","type":{"defined":{"name":"ThresholdType"}}},{"name":"threshold","type":"u64"}]}}]} \ No newline at end of file diff --git a/idls/hylo_exchange.HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn.json b/idls/hylo_exchange.HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn.json new file mode 100644 index 0000000..87e57a3 --- /dev/null +++ b/idls/hylo_exchange.HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn.json @@ -0,0 +1 @@ +{"address":"HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn","metadata":{"name":"hylo_exchange","version":"0.1.4","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"get_stats","discriminator":[241,65,112,185,230,140,139,177],"accounts":[{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"stablecoin_mint","pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}]},"relations":["hylo"]},{"name":"levercoin_mint","pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}]},"relations":["hylo"]},{"name":"sol_usd_pyth_feed"}],"args":[],"returns":{"defined":{"name":"ExchangeStats"}}},{"name":"harvest_yield","discriminator":[28,200,150,200,69,56,38,133],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"stablecoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}]},"relations":["hylo"]},{"name":"stablecoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}]}},{"name":"levercoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}]},"relations":["hylo"]},{"name":"levercoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"levercoin_mint"}]}},{"name":"stablecoin_fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}]}},{"name":"stablecoin_fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"stablecoin_fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"levercoin_fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"levercoin_mint"}]}},{"name":"levercoin_fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"levercoin_fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"stablecoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"levercoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool_auth","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,97,117,116,104]}],"program":{"kind":"account","path":"hylo_stability_pool"}}},{"name":"sol_usd_pyth_feed"},{"name":"hylo_stability_pool","address":"HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ"},{"name":"lst_registry","writable":true,"relations":["hylo"]},{"name":"lut_program","address":"AddressLookupTab1e1111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"initialize_lst_registry","discriminator":[148,180,49,29,133,6,9,59],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"registry_auth","pda":{"seeds":[{"kind":"const","value":[108,115,116,95,114,101,103,105,115,116,114,121,95,97,117,116,104]}]}},{"name":"lst_registry","writable":true},{"name":"lut_program","address":"AddressLookupTab1e1111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"slot","type":"u64"}]},{"name":"initialize_lst_registry_calculators","discriminator":[109,166,101,171,217,202,83,166],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"lst_registry_auth","pda":{"seeds":[{"kind":"const","value":[108,115,116,95,114,101,103,105,115,116,114,121,95,97,117,116,104]}]}},{"name":"lst_registry","writable":true,"relations":["hylo"]},{"name":"lut_program","address":"AddressLookupTab1e1111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"initialize_mints","discriminator":[189,84,85,142,177,200,57,22],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"stablecoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}]}},{"name":"levercoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"levercoin_mint"}]}},{"name":"stablecoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}]}},{"name":"levercoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}]}},{"name":"stablecoin_metadata","writable":true},{"name":"levercoin_metadata","writable":true},{"name":"metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"initialize_protocol","discriminator":[188,233,252,106,134,146,202,91],"accounts":[{"name":"admin","writable":true,"signer":true},{"name":"upgrade_authority","signer":true},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"treasury"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"program_data"},{"name":"hylo_exchange","address":"HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn"}],"args":[{"name":"oracle_interval_secs","type":"u64"},{"name":"stability_threshold_1","type":{"defined":{"name":"UFixValue64"}}},{"name":"stability_threshold_2","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_fees","type":{"defined":{"name":"StablecoinFees"}}},{"name":"levercoin_fees","type":{"defined":{"name":"LevercoinFees"}}},{"name":"yield_harvest_config","type":{"defined":{"name":"YieldHarvestConfig"}}}]},{"name":"mint_levercoin","discriminator":[91,156,221,157,151,186,223,231],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"vault_auth","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"levercoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"levercoin_mint"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"vault_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_header","pda":{"seeds":[{"kind":"const","value":[108,115,116,95,104,101,97,100,101,114]},{"kind":"account","path":"lst_mint"}]}},{"name":"user_lst_ta","writable":true},{"name":"user_levercoin_ta","writable":true},{"name":"lst_mint"},{"name":"levercoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}]},"relations":["hylo"]},{"name":"stablecoin_mint","pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}]}},{"name":"sol_usd_pyth_feed"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_lst_to_deposit","type":"u64"},{"name":"slippage_config","type":{"option":{"defined":{"name":"SlippageConfig"}}}}]},{"name":"mint_stablecoin","discriminator":[196,235,215,70,211,5,214,238],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"vault_auth","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"stablecoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"vault_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_header","pda":{"seeds":[{"kind":"const","value":[108,115,116,95,104,101,97,100,101,114]},{"kind":"account","path":"lst_mint"}]}},{"name":"user_lst_ta","writable":true},{"name":"user_stablecoin_ta","writable":true},{"name":"lst_mint"},{"name":"stablecoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}]},"relations":["hylo"]},{"name":"sol_usd_pyth_feed"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_lst_to_deposit","type":"u64"},{"name":"slippage_config","type":{"option":{"defined":{"name":"SlippageConfig"}}}}]},{"name":"redeem_levercoin","discriminator":[132,166,215,32,46,131,174,44],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"vault_auth","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"vault_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_header","pda":{"seeds":[{"kind":"const","value":[108,115,116,95,104,101,97,100,101,114]},{"kind":"account","path":"lst_mint"}]}},{"name":"user_levercoin_ta","writable":true},{"name":"user_lst_ta","writable":true},{"name":"levercoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}]},"relations":["hylo"]},{"name":"stablecoin_mint","pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}]},"relations":["hylo"]},{"name":"lst_mint"},{"name":"sol_usd_pyth_feed"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_to_redeem","type":"u64"},{"name":"slippage_config","type":{"option":{"defined":{"name":"SlippageConfig"}}}}]},{"name":"redeem_stablecoin","discriminator":[69,46,6,97,170,130,160,237],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"vault_auth","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"vault_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_header","pda":{"seeds":[{"kind":"const","value":[108,115,116,95,104,101,97,100,101,114]},{"kind":"account","path":"lst_mint"}]}},{"name":"user_stablecoin_ta","writable":true},{"name":"user_lst_ta","writable":true},{"name":"stablecoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}]},"relations":["hylo"]},{"name":"lst_mint"},{"name":"sol_usd_pyth_feed"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_to_redeem","type":"u64"},{"name":"slippage_config","type":{"option":{"defined":{"name":"SlippageConfig"}}}}]},{"name":"register_lst","discriminator":[167,114,254,24,190,221,82,107],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"lst_header","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,115,116,95,104,101,97,100,101,114]},{"kind":"account","path":"lst_mint"}]}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"vault_auth","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"registry_auth","pda":{"seeds":[{"kind":"const","value":[108,115,116,95,114,101,103,105,115,116,114,121,95,97,117,116,104]}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"vault_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_mint"},{"name":"lst_registry","writable":true,"relations":["hylo"]},{"name":"lst_stake_pool_state"},{"name":"sanctum_calculator_program"},{"name":"sanctum_calculator_state"},{"name":"stake_pool_program_data"},{"name":"stake_pool_program"},{"name":"lut_program","address":"AddressLookupTab1e1111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"swap_lever_to_stable","discriminator":[167,111,84,179,69,7,135,48],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"sol_usd_pyth_feed"},{"name":"stablecoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}]},"relations":["hylo"]},{"name":"stablecoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}]}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user_stablecoin_ta","writable":true},{"name":"levercoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}]},"relations":["hylo"]},{"name":"levercoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"levercoin_mint"}]}},{"name":"user_levercoin_ta","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_levercoin","type":"u64"},{"name":"slippage_config","type":{"option":{"defined":{"name":"SlippageConfig"}}}}],"returns":{"defined":{"name":"SwapLeverToStableEventV1"}}},{"name":"swap_lst","discriminator":[37,144,201,101,137,182,241,42],"accounts":[{"name":"user","signer":true},{"name":"hylo"},{"name":"lst_a_mint"},{"name":"lst_a_user_ta","writable":true},{"name":"lst_a_vault_auth","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"lst_a_mint"}]}},{"name":"lst_a_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"lst_a_vault_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_a_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_a_header","pda":{"seeds":[{"kind":"const","value":[108,115,116,95,104,101,97,100,101,114]},{"kind":"account","path":"lst_a_mint"}]}},{"name":"lst_b_mint"},{"name":"lst_b_user_ta","writable":true},{"name":"lst_b_vault_auth","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"lst_b_mint"}]}},{"name":"lst_b_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"lst_b_vault_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_b_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_b_header","pda":{"seeds":[{"kind":"const","value":[108,115,116,95,104,101,97,100,101,114]},{"kind":"account","path":"lst_b_mint"}]}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"lst_a_mint"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_a_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_lst_a","type":"u64"},{"name":"slippage_config","type":{"option":{"defined":{"name":"SlippageConfig"}}}}]},{"name":"swap_stable_to_lever","discriminator":[123,194,84,140,192,193,193,161],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"sol_usd_pyth_feed"},{"name":"stablecoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}]},"relations":["hylo"]},{"name":"stablecoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}]}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user_stablecoin_ta","writable":true},{"name":"levercoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}]},"relations":["hylo"]},{"name":"levercoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"levercoin_mint"}]}},{"name":"user_levercoin_ta","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_stablecoin","type":"u64"},{"name":"slippage_config","type":{"option":{"defined":{"name":"SlippageConfig"}}}}],"returns":{"defined":{"name":"SwapStableToLeverEventV1"}}},{"name":"update_admin","discriminator":[161,176,40,213,60,184,179,228],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"upgrade_authority","signer":true},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"program_data"},{"name":"hylo_exchange","address":"HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_admin","type":"pubkey"}]},{"name":"update_levercoin_fees","discriminator":[183,123,195,23,63,151,104,31],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_levercoin_fees","type":{"defined":{"name":"LevercoinFees"}}}]},{"name":"update_lst_prices","discriminator":[3,34,88,178,240,40,85,148],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"lst_registry","writable":true,"relations":["hylo"]},{"name":"lut_program","address":"AddressLookupTab1e1111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"update_lst_swap_fee","discriminator":[129,125,10,188,94,219,63,217],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_lst_swap_fee","type":{"defined":{"name":"UFixValue64"}}}]},{"name":"update_oracle_conf_tolerance","discriminator":[59,81,210,112,245,81,187,151],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_oracle_conf_tolerance","type":{"defined":{"name":"UFixValue64"}}}]},{"name":"update_oracle_interval","discriminator":[67,242,44,246,210,94,99,214],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_oracle_interval_secs","type":"u64"}]},{"name":"update_sol_usd_oracle","discriminator":[95,236,18,102,201,114,89,54],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_oracle","type":"pubkey"}]},{"name":"update_stability_pool","discriminator":[241,157,233,64,49,184,93,137],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_stability_pool","type":"pubkey"}]},{"name":"update_stability_thresholds","discriminator":[250,58,160,105,52,84,93,167],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_stability_threshold_1","type":{"defined":{"name":"UFixValue64"}}},{"name":"new_stability_threshold_2","type":{"defined":{"name":"UFixValue64"}}}]},{"name":"update_stablecoin_fees","discriminator":[49,74,146,60,3,38,96,4],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_stablecoin_fees","type":{"defined":{"name":"StablecoinFees"}}}]},{"name":"update_treasury","discriminator":[60,16,243,66,96,59,254,131],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_treasury","type":"pubkey"}]},{"name":"update_yield_harvest_config","discriminator":[140,91,46,213,44,179,201,32],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_yield_harvest_config","type":{"defined":{"name":"YieldHarvestConfig"}}}]},{"name":"withdraw_fees","discriminator":[198,212,171,109,144,215,174,89],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"treasury","writable":true,"relations":["hylo"]},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"fee_token_mint"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"fee_token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"treasury_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"treasury"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"fee_token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"fee_token_mint"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]}],"accounts":[{"name":"Hylo","discriminator":[114,161,169,210,204,175,149,174]},{"name":"LstHeader","discriminator":[125,135,217,151,122,202,138,59]},{"name":"PriceUpdateV2","discriminator":[34,241,35,99,157,126,244,205]}],"events":[{"name":"ExchangeStats","discriminator":[74,24,38,211,126,124,214,158]},{"name":"HarvestYieldEventV1","discriminator":[236,194,171,214,66,200,158,27]},{"name":"HarvestYieldEventV2","discriminator":[46,204,60,91,154,15,240,7]},{"name":"MintLevercoinEventV0","discriminator":[71,135,154,151,170,173,218,125]},{"name":"MintLevercoinEventV1","discriminator":[77,221,3,200,106,110,9,245]},{"name":"MintLevercoinEventV2","discriminator":[153,16,148,142,11,127,114,15]},{"name":"MintStablecoinEventV0","discriminator":[181,185,32,71,11,248,162,100]},{"name":"MintStablecoinEventV1","discriminator":[102,41,35,191,18,74,142,16]},{"name":"MintStablecoinEventV2","discriminator":[173,104,166,28,0,52,94,246]},{"name":"RedeemLevercoinEventV0","discriminator":[92,186,13,68,23,219,194,81]},{"name":"RedeemLevercoinEventV1","discriminator":[44,209,148,186,40,214,48,242]},{"name":"RedeemLevercoinEventV2","discriminator":[136,249,79,60,114,254,234,38]},{"name":"RedeemStablecoinEventV0","discriminator":[98,11,53,77,188,71,107,168]},{"name":"RedeemStablecoinEventV1","discriminator":[172,179,174,150,249,177,177,201]},{"name":"RedeemStablecoinEventV2","discriminator":[36,163,198,40,252,128,99,179]},{"name":"RegisterLstEvent","discriminator":[67,129,63,97,144,52,195,33]},{"name":"SwapLeverToStableEventV0","discriminator":[50,170,61,23,136,163,174,145]},{"name":"SwapLeverToStableEventV1","discriminator":[201,228,47,118,19,214,21,45]},{"name":"SwapLstEventV0","discriminator":[246,70,176,207,42,167,214,67]},{"name":"SwapStableToLeverEventV0","discriminator":[134,28,79,7,117,57,126,174]},{"name":"SwapStableToLeverEventV1","discriminator":[184,185,154,14,36,128,241,53]},{"name":"UpdateAdminEvent","discriminator":[225,152,171,87,246,63,66,234]},{"name":"UpdateLevercoinFeesEvent","discriminator":[84,176,60,195,204,115,242,169]},{"name":"UpdateLstPricesEvent","discriminator":[104,0,189,118,195,155,77,151]},{"name":"UpdateLstSwapFeeEvent","discriminator":[139,68,196,130,145,148,173,212]},{"name":"UpdateOracleAddressEvent","discriminator":[28,247,140,128,156,178,148,53]},{"name":"UpdateOracleConfEvent","discriminator":[190,89,82,192,231,31,206,91]},{"name":"UpdateOracleIntervalEvent","discriminator":[190,132,209,41,79,137,195,226]},{"name":"UpdateStabilityPoolEvent","discriminator":[220,82,162,130,152,212,135,63]},{"name":"UpdateStabilityThresholdsEvent","discriminator":[36,117,167,25,25,39,46,189]},{"name":"UpdateStablecoinFeesEvent","discriminator":[38,140,45,231,238,94,115,249]},{"name":"UpdateTreasuryEvent","discriminator":[104,210,213,227,230,150,146,19]},{"name":"UpdateYieldHarvestConfigEvent","discriminator":[67,168,96,20,78,117,245,206]},{"name":"WithdrawFeesEvent","discriminator":[236,118,138,90,139,173,177,89]}],"errors":[{"code":6000,"name":"LevercoinRedeemDisabled","msg":"Cannot redeem levercoin due to stability mode. NAV would be 0 or lower."},{"code":6001,"name":"LeverToStableDisabled","msg":"Levercoin to stablecoin swap disabled due to stability mode."},{"code":6002,"name":"StableToLeverDisabled","msg":"Stablecoin to levercoin swap disabled due to stability mode."},{"code":6003,"name":"SanctumCpi","msg":"Error during CPI to Sanctum LST/SOL calculator."},{"code":6004,"name":"LstRegistryAlreadyInitialized","msg":"LST registry cannot be initialized twice."},{"code":6005,"name":"LstRegistryEmpty","msg":"LST specific accounts found empty when attempting to load registry."},{"code":6006,"name":"LstRegistryPreamble","msg":"Sanctum calculator contexts in LST registry preamble are malformed."},{"code":6007,"name":"LstRegistryLookupTableDeser","msg":"Failed to deserialize registry lookup table."},{"code":6008,"name":"LstRegistryLookupTableInvalid","msg":"Contents of LST registry did not match remaining_accounts."},{"code":6009,"name":"LstBlockInvalid","msg":"Mint/vault/pool accounts in registry block do not match header."},{"code":6010,"name":"LstContextInvalid","msg":"Attempted to register an LST with invalid Sanctum context accounts."},{"code":6011,"name":"LstAdditionOverflow","msg":"Addition overflow while computing total SOL in LST registry."},{"code":6012,"name":"LstPriceOutdated","msg":"Cached LST price not from current epoch. Run pricing crank to update."},{"code":6013,"name":"LstPriceDelta","msg":"Failed to compute delta between current and previous LST prices."},{"code":6014,"name":"LstPriceEpochsInvalid","msg":"Found current epoch less than previous in LST header."},{"code":6015,"name":"LstSolAppreciation","msg":"Overflow while computing LST SOL appreciation."},{"code":6016,"name":"StablecoinMintDisabled","msg":"Stablecoin mint disabled. Protocol is in Mode2 or Depeg."},{"code":6017,"name":"LevercoinMintDisabled","msg":"Levercoin mint disabled. Protocol is in Depeg."},{"code":6018,"name":"YieldHarvestConfigValidation","msg":"Yield harvest configuration percentages failed validation."},{"code":6019,"name":"YieldHarvestDisabled","msg":"Yield harvest disabled due to protocol collateral ratio."},{"code":6020,"name":"YieldHarvestAllocation","msg":"Arithmetic error while computing yield harvest allocation."},{"code":6021,"name":"YieldHarvestEpoch","msg":"Yield harvest already occurred during this epoch."},{"code":6022,"name":"YieldHarvestHasNotRun","msg":"Yield harvest has not yet occurred during this epoch."},{"code":6023,"name":"ZeroRedeem","msg":"Cannot withdraw amount of 0 in a redemption action."},{"code":6024,"name":"ZeroMint","msg":"Cannot deposit amount of 0 in a minting action."},{"code":6025,"name":"ZeroSwap","msg":"Cannot swap amount of 0."},{"code":6026,"name":"IdentitySwap","msg":"Cannot swap from an asset to itself."}],"types":[{"name":"ExchangeStats","docs":["Stats"],"type":{"kind":"struct","fields":[{"name":"total_value_locked","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_supply","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_supply","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_ratio","type":{"defined":{"name":"UFixValue64"}}},{"name":"stability_mode","type":{"defined":{"name":"StabilityMode"}}},{"name":"yield_harvest_cache","type":{"defined":{"name":"YieldHarvestCache"}}}]}},{"name":"FeePair","docs":["Represents the spread of fees between mint and redeem for protocol tokens.","All fees must be in basis points to represent a fractional percentage","directly applicable to a token amount e.g. `0.XXXX` or `bips x 10^-4`."],"type":{"kind":"struct","fields":[{"name":"mint","type":{"defined":{"name":"UFixValue64"}}},{"name":"redeem","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"HarvestYieldEventV1","docs":["Cranks"],"type":{"kind":"struct","fields":[{"name":"harvestable_stablecoin","type":{"defined":{"name":"UFixValue64"}}},{"name":"fees_extracted","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_to_pool","type":{"defined":{"name":"UFixValue64"}}},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"HarvestYieldEventV2","type":{"kind":"struct","fields":[{"name":"harvest_token_mint","type":"pubkey"},{"name":"total_sol_harvested","type":{"defined":{"name":"UFixValue64"}}},{"name":"fees_extracted","type":{"defined":{"name":"UFixValue64"}}},{"name":"token_to_pool","type":{"defined":{"name":"UFixValue64"}}},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"Hylo","type":{"kind":"struct","fields":[{"name":"admin","type":"pubkey"},{"name":"treasury","type":"pubkey"},{"name":"lst_registry","type":"pubkey"},{"name":"stablecoin_mint","type":"pubkey"},{"name":"levercoin_mint","type":"pubkey"},{"name":"stability_pool","type":"pubkey"},{"name":"stablecoin_mint_bump","type":"u8"},{"name":"stablecoin_auth_bump","type":"u8"},{"name":"levercoin_mint_bump","type":"u8"},{"name":"levercoin_auth_bump","type":"u8"},{"name":"registry_auth_bump","type":"u8"},{"name":"total_sol_cache_bump","type":"u8"},{"name":"oracle_interval_secs","type":"u64"},{"name":"stablecoin_fees","type":{"defined":{"name":"StablecoinFees"}}},{"name":"levercoin_fees","type":{"defined":{"name":"LevercoinFees"}}},{"name":"total_sol_cache","type":{"defined":{"name":"TotalSolCache"}}},{"name":"yield_harvest_cache","type":{"defined":{"name":"YieldHarvestCache"}}},{"name":"yield_harvest_config","type":{"defined":{"name":"YieldHarvestConfig"}}},{"name":"stability_threshold_1","type":{"defined":{"name":"UFixValue64"}}},{"name":"stability_threshold_2","type":{"defined":{"name":"UFixValue64"}}},{"name":"oracle_conf_tolerance","type":{"defined":{"name":"UFixValue64"}}},{"name":"sol_usd_oracle","type":"pubkey"},{"name":"lst_swap_fee","type":{"defined":{"name":"UFixValue64"}}},{"name":"_reserved","type":{"array":["u8",78]}}]}},{"name":"LevercoinFees","type":{"kind":"struct","fields":[{"name":"normal","type":{"defined":{"name":"FeePair"}}},{"name":"mode_1","type":{"defined":{"name":"FeePair"}}},{"name":"mode_2","type":{"defined":{"name":"FeePair"}}}]}},{"name":"LstHeader","docs":["Header for a specific LST. Should be the first entry in the block of","accounts, and contains references to the rest of the accounts."],"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"vault","type":"pubkey"},{"name":"pool_state","type":"pubkey"},{"name":"stake_program","type":{"defined":{"name":"LstStakePoolProgram"}}},{"name":"prev_price_sol","type":{"defined":{"name":"LstSolPrice"}}},{"name":"price_sol","type":{"defined":{"name":"LstSolPrice"}}},{"name":"last_yield_harvest_epoch","type":"u64"},{"name":"_reserved","type":{"array":["u8",64]}}]}},{"name":"LstSolPrice","docs":["Captures the true LST price in SOL for the current epoch."],"type":{"kind":"struct","fields":[{"name":"price","type":{"defined":{"name":"UFixValue64"}}},{"name":"epoch","type":"u64"}]}},{"name":"LstStakePoolProgram","type":{"kind":"enum","variants":[{"name":"Spl"},{"name":"SanctumSpl"},{"name":"SanctumSplMulti"},{"name":"Marinade"}]}},{"name":"MintLevercoinEventV0","docs":["`MintLevercoin`"],"type":{"kind":"struct","fields":[{"name":"minted","type":{"defined":{"name":"UFixValue64"}}},{"name":"total_supply","type":"u64"},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_deposited","type":"u64"},{"name":"collateral_ratio","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"MintLevercoinEventV1","type":{"kind":"struct","fields":[{"name":"minted","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"MintLevercoinEventV2","type":{"kind":"struct","fields":[{"name":"minted","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"lst_mint","type":"pubkey"},{"name":"lst_sol_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_deposited","type":{"defined":{"name":"UFixValue64"}}},{"name":"fees_deposited","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"MintStablecoinEventV0","docs":["`MintStablecoin`"],"type":{"kind":"struct","fields":[{"name":"minted","type":{"defined":{"name":"UFixValue64"}}},{"name":"total_supply","type":"u64"},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_deposited","type":"u64"},{"name":"collateral_ratio","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"MintStablecoinEventV1","type":{"kind":"struct","fields":[{"name":"minted","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"MintStablecoinEventV2","type":{"kind":"struct","fields":[{"name":"minted","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"lst_mint","type":"pubkey"},{"name":"lst_sol_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_deposited","type":{"defined":{"name":"UFixValue64"}}},{"name":"fees_deposited","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"PriceFeedMessage","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"feed_id","docs":["`FeedId` but avoid the type alias because of compatibility issues with Anchor's `idl-build` feature."],"type":{"array":["u8",32]}},{"name":"price","type":"i64"},{"name":"conf","type":"u64"},{"name":"exponent","type":"i32"},{"name":"publish_time","docs":["The timestamp of this price update in seconds"],"type":"i64"},{"name":"prev_publish_time","docs":["The timestamp of the previous price update. This field is intended to allow users to","identify the single unique price update for any moment in time:","for any time t, the unique update is the one such that prev_publish_time < t <= publish_time.","","Note that there may not be such an update while we are migrating to the new message-sending logic,","as some price updates on pythnet may not be sent to other chains (because the message-sending","logic may not have triggered). We can solve this problem by making the message-sending mandatory","(which we can do once publishers have migrated over).","","Additionally, this field may be equal to publish_time if the message is sent on a slot where","where the aggregation was unsuccesful. This problem will go away once all publishers have","migrated over to a recent version of pyth-agent."],"type":"i64"},{"name":"ema_price","type":"i64"},{"name":"ema_conf","type":"u64"}]}},{"name":"PriceUpdateV2","docs":["A price update account. This account is used by the Pyth Receiver program to store a verified price update from a Pyth price feed.","It contains:","- `write_authority`: The write authority for this account. This authority can close this account to reclaim rent or update the account to contain a different price update.","- `verification_level`: The [`VerificationLevel`] of this price update. This represents how many Wormhole guardian signatures have been verified for this price update.","- `price_message`: The actual price update.","- `posted_slot`: The slot at which this price update was posted."],"type":{"kind":"struct","fields":[{"name":"write_authority","type":"pubkey"},{"name":"verification_level","type":{"defined":{"name":"VerificationLevel"}}},{"name":"price_message","type":{"defined":{"name":"PriceFeedMessage"}}},{"name":"posted_slot","type":"u64"}]}},{"name":"RedeemLevercoinEventV0","docs":["`RedeemLevercoin`"],"type":{"kind":"struct","fields":[{"name":"redeemed","type":"u64"},{"name":"total_supply","type":"u64"},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_withdrawn","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_ratio","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"RedeemLevercoinEventV1","type":{"kind":"struct","fields":[{"name":"redeemed","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"RedeemLevercoinEventV2","type":{"kind":"struct","fields":[{"name":"redeemed","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"lst_mint","type":"pubkey"},{"name":"lst_sol_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_withdrawn","type":{"defined":{"name":"UFixValue64"}}},{"name":"fees_deposited","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"RedeemStablecoinEventV0","docs":["`RedeemStablecoin`"],"type":{"kind":"struct","fields":[{"name":"redeemed","type":"u64"},{"name":"total_supply","type":"u64"},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_withdrawn","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_ratio","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"RedeemStablecoinEventV1","type":{"kind":"struct","fields":[{"name":"redeemed","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"RedeemStablecoinEventV2","type":{"kind":"struct","fields":[{"name":"redeemed","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"lst_mint","type":"pubkey"},{"name":"lst_sol_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_withdrawn","type":{"defined":{"name":"UFixValue64"}}},{"name":"fees_deposited","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"RegisterLstEvent","docs":["Admin"],"type":{"kind":"struct","fields":[{"name":"header","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"vault","type":"pubkey"},{"name":"pool_state","type":"pubkey"}]}},{"name":"SlippageConfig","docs":["Client specified slippage tolerance paired with expected token amount."],"type":{"kind":"struct","fields":[{"name":"expected_token_out","type":{"defined":{"name":"UFixValue64"}}},{"name":"slippage_tolerance","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"StabilityMode","docs":["Mode of operation based on the protocol's current collateral ratio.","See whitepaper for more."],"type":{"kind":"enum","variants":[{"name":"Normal"},{"name":"Mode1"},{"name":"Mode2"},{"name":"Depeg"}]}},{"name":"StablecoinFees","type":{"kind":"struct","fields":[{"name":"normal","type":{"defined":{"name":"FeePair"}}},{"name":"mode_1","type":{"defined":{"name":"FeePair"}}}]}},{"name":"SwapLeverToStableEventV0","type":{"kind":"struct","fields":[{"name":"levercoin_burned","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_minted","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"SwapLeverToStableEventV1","type":{"kind":"struct","fields":[{"name":"levercoin_burned","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_minted_user","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_minted_fees","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"SwapLstEventV0","type":{"kind":"struct","fields":[{"name":"lst_a_mint","type":"pubkey"},{"name":"lst_a_in","type":{"defined":{"name":"UFixValue64"}}},{"name":"lst_a_fees_extracted","type":{"defined":{"name":"UFixValue64"}}},{"name":"lst_b_mint","type":"pubkey"},{"name":"lst_b_out","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"SwapStableToLeverEventV0","docs":["Swaps"],"type":{"kind":"struct","fields":[{"name":"stablecoin_burned","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_minted","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"SwapStableToLeverEventV1","type":{"kind":"struct","fields":[{"name":"stablecoin_burned","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_fees","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_minted","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"TotalSolCache","type":{"kind":"struct","fields":[{"name":"current_update_epoch","type":"u64"},{"name":"total_sol","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"UFixValue64","docs":["A value-space `Fix` where base is always 10 and bits are a concrete type.","Intended for serialized storage in Solana accounts where generics won't work."],"type":{"kind":"struct","fields":[{"name":"bits","type":"u64"},{"name":"exp","type":"i8"}]}},{"name":"UpdateAdminEvent","type":{"kind":"struct","fields":[{"name":"old_admin","type":"pubkey"},{"name":"new_admin","type":"pubkey"}]}},{"name":"UpdateLevercoinFeesEvent","type":{"kind":"struct","fields":[{"name":"old_levercoin_fees","type":{"defined":{"name":"LevercoinFees"}}},{"name":"new_levercoin_fees","type":{"defined":{"name":"LevercoinFees"}}}]}},{"name":"UpdateLstPricesEvent","type":{"kind":"struct","fields":[{"name":"updated_mints","type":{"vec":"pubkey"}},{"name":"new_total_sol","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"UpdateLstSwapFeeEvent","type":{"kind":"struct","fields":[{"name":"old_lst_swap_fee","type":{"defined":{"name":"UFixValue64"}}},{"name":"new_lst_swap_fee","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"UpdateOracleAddressEvent","type":{"kind":"struct","fields":[{"name":"old_oracle","type":"pubkey"},{"name":"new_oracle","type":"pubkey"}]}},{"name":"UpdateOracleConfEvent","type":{"kind":"struct","fields":[{"name":"old_oracle_conf_tolerance","type":{"defined":{"name":"UFixValue64"}}},{"name":"new_oracle_conf_tolerance","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"UpdateOracleIntervalEvent","type":{"kind":"struct","fields":[{"name":"old_oracle_interval_secs","type":"u64"},{"name":"new_oracle_interval_secs","type":"u64"}]}},{"name":"UpdateStabilityPoolEvent","type":{"kind":"struct","fields":[{"name":"old_stability_pool","type":"pubkey"},{"name":"new_stability_pool","type":"pubkey"}]}},{"name":"UpdateStabilityThresholdsEvent","type":{"kind":"struct","fields":[{"name":"old_stability_threshold_1","type":{"defined":{"name":"UFixValue64"}}},{"name":"old_stability_threshold_2","type":{"defined":{"name":"UFixValue64"}}},{"name":"new_stability_threshold_1","type":{"defined":{"name":"UFixValue64"}}},{"name":"new_stability_threshold_2","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"UpdateStablecoinFeesEvent","type":{"kind":"struct","fields":[{"name":"old_stablecoin_fees","type":{"defined":{"name":"StablecoinFees"}}},{"name":"new_stablecoin_fees","type":{"defined":{"name":"StablecoinFees"}}}]}},{"name":"UpdateTreasuryEvent","type":{"kind":"struct","fields":[{"name":"old_treasury","type":"pubkey"},{"name":"new_treasury","type":"pubkey"}]}},{"name":"UpdateYieldHarvestConfigEvent","type":{"kind":"struct","fields":[{"name":"old_yield_harvest_config","type":{"defined":{"name":"YieldHarvestConfig"}}},{"name":"new_yield_harvest_config","type":{"defined":{"name":"YieldHarvestConfig"}}}]}},{"name":"VerificationLevel","docs":["Pyth price updates are bridged to all blockchains via Wormhole.","Using the price updates on another chain requires verifying the signatures of the Wormhole guardians.","The usual process is to check the signatures for two thirds of the total number of guardians, but this can be cumbersome on Solana because of the transaction size limits,","so we also allow for partial verification.","","This enum represents how much a price update has been verified:","- If `Full`, we have verified the signatures for two thirds of the current guardians.","- If `Partial`, only `num_signatures` guardian signatures have been checked.","","# Warning","Using partially verified price updates is dangerous, as it lowers the threshold of guardians that need to collude to produce a malicious price update."],"type":{"kind":"enum","variants":[{"name":"Partial","fields":[{"name":"num_signatures","type":"u8"}]},{"name":"Full"}]}},{"name":"WithdrawFeesEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"vault","type":"pubkey"},{"name":"treasury_ata","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"YieldHarvestCache","docs":["Records epoch yield harvest information for off-chain consumers."],"type":{"kind":"struct","fields":[{"name":"epoch","type":"u64"},{"name":"stability_pool_cap","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_yield_to_pool","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"YieldHarvestConfig","docs":["Captures yield harvest configuration as two basis point values:"],"type":{"kind":"struct","fields":[{"name":"allocation","type":{"defined":{"name":"UFixValue64"}}},{"name":"fee","type":{"defined":{"name":"UFixValue64"}}}]}}],"constants":[{"name":"FEE_AUTH","type":{"array":["u8",8]},"value":"[102, 101, 101, 95, 97, 117, 116, 104]"},{"name":"HYLO","type":{"array":["u8",4]},"value":"[104, 121, 108, 111]"},{"name":"HYUSD","type":{"array":["u8",5]},"value":"[104, 121, 85, 83, 68]"},{"name":"LST_HEADER","type":{"array":["u8",10]},"value":"[108, 115, 116, 95, 104, 101, 97, 100, 101, 114]"},{"name":"LST_REGISTRY_AUTH","type":{"array":["u8",17]},"value":"[108, 115, 116, 95, 114, 101, 103, 105, 115, 116, 114, 121, 95, 97, 117, 116, 104]"},{"name":"MINT_AUTH","type":{"array":["u8",9]},"value":"[109, 105, 110, 116, 95, 97, 117, 116, 104]"},{"name":"POOL_AUTH","type":{"array":["u8",9]},"value":"[112, 111, 111, 108, 95, 97, 117, 116, 104]"},{"name":"VAULT_AUTH","type":{"array":["u8",10]},"value":"[118, 97, 117, 108, 116, 95, 97, 117, 116, 104]"},{"name":"XSOL","type":{"array":["u8",4]},"value":"[120, 83, 79, 76]"}]} \ No newline at end of file diff --git a/idls/hylo_stability_pool.HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ.json b/idls/hylo_stability_pool.HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ.json new file mode 100644 index 0000000..8844cbd --- /dev/null +++ b/idls/hylo_stability_pool.HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ.json @@ -0,0 +1 @@ +{"address":"HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ","metadata":{"name":"hylo_stability_pool","version":"0.1.4","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"get_stats","discriminator":[241,65,112,185,230,140,139,177],"accounts":[{"name":"pool_config","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,99,111,110,102,105,103]}]}},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"stablecoin_mint","pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"levercoin_mint","pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"pool_auth","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,97,117,116,104]}]}},{"name":"stablecoin_pool","pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"levercoin_pool","pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lp_token_mint","pda":{"seeds":[{"kind":"const","value":[115,116,97,107,101,100,95,104,121,85,83,68]}]}},{"name":"sol_usd_pyth_feed"}],"args":[],"returns":{"defined":{"name":"StabilityPoolStats"}}},{"name":"initialize_lp_token_mint","discriminator":[188,32,168,40,166,152,159,156],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["pool_config"]},{"name":"pool_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,99,111,110,102,105,103]}]}},{"name":"lp_token_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"lp_token_mint"}]}},{"name":"lp_token_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,101,100,95,104,121,85,83,68]}]}},{"name":"lp_token_metadata","writable":true},{"name":"metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"initialize_stability_pool","discriminator":[220,103,253,62,133,245,74,222],"accounts":[{"name":"admin","writable":true,"signer":true},{"name":"upgrade_authority","signer":true},{"name":"pool_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,99,111,110,102,105,103]}]}},{"name":"pool_auth","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,97,117,116,104]}]}},{"name":"stablecoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"levercoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"stablecoin_mint","pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"levercoin_mint","pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"program_data"},{"name":"hylo_stability_pool","address":"HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ"}],"args":[]},{"name":"rebalance_lever_to_stable","discriminator":[194,252,113,42,31,248,184,211],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"pool_config","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,99,111,110,102,105,103]}]}},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"stablecoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"stablecoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool_auth","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,97,117,116,104]}]}},{"name":"levercoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"levercoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"sol_usd_pyth_feed"},{"name":"stablecoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"levercoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"hylo_event_authority"},{"name":"hylo_exchange_program","address":"HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"rebalance_stable_to_lever","discriminator":[210,59,29,161,20,10,3,68],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"pool_config","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,99,111,110,102,105,103]}]}},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"stablecoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"stablecoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool_auth","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,97,117,116,104]}]}},{"name":"levercoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"levercoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"sol_usd_pyth_feed"},{"name":"stablecoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"levercoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"hylo_event_authority"},{"name":"hylo_exchange_program","address":"HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"update_admin","discriminator":[161,176,40,213,60,184,179,228],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"upgrade_authority","signer":true},{"name":"pool_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,99,111,110,102,105,103]}]}},{"name":"program_data"},{"name":"hylo_stability_pool","address":"HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_admin","type":"pubkey"}]},{"name":"update_withdrawal_fee","discriminator":[165,235,231,25,127,99,244,133],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["pool_config"]},{"name":"pool_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,99,111,110,102,105,103]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_withdrawal_fee","type":{"defined":{"name":"UFixValue64"}}}]},{"name":"user_deposit","discriminator":[186,198,140,233,129,39,98,153],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"pool_config","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,99,111,110,102,105,103]}]}},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"stablecoin_mint","pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"levercoin_mint","pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"user_stablecoin_ta","writable":true},{"name":"user_lp_token_ta","writable":true},{"name":"pool_auth","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,97,117,116,104]}]}},{"name":"stablecoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"levercoin_pool","pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lp_token_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"lp_token_mint"}]}},{"name":"lp_token_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,101,100,95,104,121,85,83,68]}]}},{"name":"sol_usd_pyth_feed"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_stablecoin","type":"u64"}]},{"name":"user_withdraw","discriminator":[53,254,26,242,119,237,73,33],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"pool_config","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,99,111,110,102,105,103]}]}},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"stablecoin_mint","pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"user_stablecoin_ta","writable":true},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user_lp_token_ta","writable":true},{"name":"pool_auth","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,97,117,116,104]}]}},{"name":"stablecoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"levercoin_mint","pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"levercoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user_levercoin_ta","writable":true},{"name":"lp_token_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"lp_token_mint"}]}},{"name":"lp_token_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,101,100,95,104,121,85,83,68]}]}},{"name":"sol_usd_pyth_feed"},{"name":"hylo_event_authority"},{"name":"hylo_exchange_program","address":"HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_lp_token","type":"u64"}]}],"accounts":[{"name":"Hylo","discriminator":[114,161,169,210,204,175,149,174]},{"name":"PoolConfig","discriminator":[26,108,14,123,116,230,129,43]},{"name":"PriceUpdateV2","discriminator":[34,241,35,99,157,126,244,205]}],"events":[{"name":"RebalanceLeverToStableEvent","discriminator":[177,3,181,27,187,44,49,109]},{"name":"RebalanceStableToLeverEvent","discriminator":[117,99,97,34,247,186,34,198]},{"name":"StabilityPoolStats","discriminator":[146,92,252,198,221,148,242,182]},{"name":"UpdateAdminEvent","discriminator":[225,152,171,87,246,63,66,234]},{"name":"UpdateWithdrawalFeeEvent","discriminator":[204,60,32,187,54,211,107,115]},{"name":"UserDepositEvent","discriminator":[70,55,110,118,235,187,239,33]},{"name":"UserWithdrawEventV0","discriminator":[142,135,121,155,248,252,59,161]},{"name":"UserWithdrawEventV1","discriminator":[51,241,35,135,62,237,83,122]}],"errors":[{"code":6000,"name":"RebalanceDisabled","msg":"Rebalance is disabled due to stability or pool amount constraint not \\\n being met."},{"code":6001,"name":"DepositDisabled","msg":"Deposits to pool disabled due to active rebalancing."}],"types":[{"name":"FeePair","docs":["Represents the spread of fees between mint and redeem for protocol tokens.","All fees must be in basis points to represent a fractional percentage","directly applicable to a token amount e.g. `0.XXXX` or `bips x 10^-4`."],"type":{"kind":"struct","fields":[{"name":"mint","type":{"defined":{"name":"UFixValue64"}}},{"name":"redeem","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"Hylo","type":{"kind":"struct","fields":[{"name":"admin","type":"pubkey"},{"name":"treasury","type":"pubkey"},{"name":"lst_registry","type":"pubkey"},{"name":"stablecoin_mint","type":"pubkey"},{"name":"levercoin_mint","type":"pubkey"},{"name":"stability_pool","type":"pubkey"},{"name":"stablecoin_mint_bump","type":"u8"},{"name":"stablecoin_auth_bump","type":"u8"},{"name":"levercoin_mint_bump","type":"u8"},{"name":"levercoin_auth_bump","type":"u8"},{"name":"registry_auth_bump","type":"u8"},{"name":"total_sol_cache_bump","type":"u8"},{"name":"oracle_interval_secs","type":"u64"},{"name":"stablecoin_fees","type":{"defined":{"name":"StablecoinFees"}}},{"name":"levercoin_fees","type":{"defined":{"name":"LevercoinFees"}}},{"name":"total_sol_cache","type":{"defined":{"name":"TotalSolCache"}}},{"name":"yield_harvest_cache","type":{"defined":{"name":"YieldHarvestCache"}}},{"name":"yield_harvest_config","type":{"defined":{"name":"YieldHarvestConfig"}}},{"name":"stability_threshold_1","type":{"defined":{"name":"UFixValue64"}}},{"name":"stability_threshold_2","type":{"defined":{"name":"UFixValue64"}}},{"name":"oracle_conf_tolerance","type":{"defined":{"name":"UFixValue64"}}},{"name":"sol_usd_oracle","type":"pubkey"},{"name":"lst_swap_fee","type":{"defined":{"name":"UFixValue64"}}},{"name":"_reserved","type":{"array":["u8",78]}}]}},{"name":"LevercoinFees","type":{"kind":"struct","fields":[{"name":"normal","type":{"defined":{"name":"FeePair"}}},{"name":"mode_1","type":{"defined":{"name":"FeePair"}}},{"name":"mode_2","type":{"defined":{"name":"FeePair"}}}]}},{"name":"PoolConfig","type":{"kind":"struct","fields":[{"name":"admin","type":"pubkey"},{"name":"pool_auth_bump","type":"u8"},{"name":"lp_token_auth_bump","type":"u8"},{"name":"lp_token_mint_bump","type":"u8"},{"name":"withdrawal_fee","type":{"defined":{"name":"UFixValue64"}}},{"name":"_reserved","type":{"array":["u8",55]}}]}},{"name":"PriceFeedMessage","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"feed_id","docs":["`FeedId` but avoid the type alias because of compatibility issues with Anchor's `idl-build` feature."],"type":{"array":["u8",32]}},{"name":"price","type":"i64"},{"name":"conf","type":"u64"},{"name":"exponent","type":"i32"},{"name":"publish_time","docs":["The timestamp of this price update in seconds"],"type":"i64"},{"name":"prev_publish_time","docs":["The timestamp of the previous price update. This field is intended to allow users to","identify the single unique price update for any moment in time:","for any time t, the unique update is the one such that prev_publish_time < t <= publish_time.","","Note that there may not be such an update while we are migrating to the new message-sending logic,","as some price updates on pythnet may not be sent to other chains (because the message-sending","logic may not have triggered). We can solve this problem by making the message-sending mandatory","(which we can do once publishers have migrated over).","","Additionally, this field may be equal to publish_time if the message is sent on a slot where","where the aggregation was unsuccesful. This problem will go away once all publishers have","migrated over to a recent version of pyth-agent."],"type":"i64"},{"name":"ema_price","type":"i64"},{"name":"ema_conf","type":"u64"}]}},{"name":"PriceUpdateV2","docs":["A price update account. This account is used by the Pyth Receiver program to store a verified price update from a Pyth price feed.","It contains:","- `write_authority`: The write authority for this account. This authority can close this account to reclaim rent or update the account to contain a different price update.","- `verification_level`: The [`VerificationLevel`] of this price update. This represents how many Wormhole guardian signatures have been verified for this price update.","- `price_message`: The actual price update.","- `posted_slot`: The slot at which this price update was posted."],"type":{"kind":"struct","fields":[{"name":"write_authority","type":"pubkey"},{"name":"verification_level","type":{"defined":{"name":"VerificationLevel"}}},{"name":"price_message","type":{"defined":{"name":"PriceFeedMessage"}}},{"name":"posted_slot","type":"u64"}]}},{"name":"RebalanceLeverToStableEvent","type":{"kind":"struct","fields":[{"name":"levercoin_swapped","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"RebalanceStableToLeverEvent","type":{"kind":"struct","fields":[{"name":"stablecoin_swapped","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"StabilityPoolStats","type":{"kind":"struct","fields":[{"name":"lp_token_nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"lp_token_supply","type":{"defined":{"name":"UFixValue64"}}},{"name":"stability_pool_cap","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_in_pool","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_in_pool","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"StablecoinFees","type":{"kind":"struct","fields":[{"name":"normal","type":{"defined":{"name":"FeePair"}}},{"name":"mode_1","type":{"defined":{"name":"FeePair"}}}]}},{"name":"TotalSolCache","type":{"kind":"struct","fields":[{"name":"current_update_epoch","type":"u64"},{"name":"total_sol","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"UFixValue64","docs":["A value-space `Fix` where base is always 10 and bits are a concrete type.","Intended for serialized storage in Solana accounts where generics won't work."],"type":{"kind":"struct","fields":[{"name":"bits","type":"u64"},{"name":"exp","type":"i8"}]}},{"name":"UpdateAdminEvent","type":{"kind":"struct","fields":[{"name":"old_admin","type":"pubkey"},{"name":"new_admin","type":"pubkey"}]}},{"name":"UpdateWithdrawalFeeEvent","type":{"kind":"struct","fields":[{"name":"old_withdrawal_fee","type":{"defined":{"name":"UFixValue64"}}},{"name":"new_withdrawal_fee","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"UserDepositEvent","type":{"kind":"struct","fields":[{"name":"stablecoin_deposited","type":{"defined":{"name":"UFixValue64"}}},{"name":"lp_token_nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"lp_token_minted","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"UserWithdrawEventV0","type":{"kind":"struct","fields":[{"name":"lp_token_burned","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_withdrawn","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_withdrawn","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"UserWithdrawEventV1","type":{"kind":"struct","fields":[{"name":"lp_token_burned","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_withdrawn","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_fees","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_withdrawn","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"VerificationLevel","docs":["Pyth price updates are bridged to all blockchains via Wormhole.","Using the price updates on another chain requires verifying the signatures of the Wormhole guardians.","The usual process is to check the signatures for two thirds of the total number of guardians, but this can be cumbersome on Solana because of the transaction size limits,","so we also allow for partial verification.","","This enum represents how much a price update has been verified:","- If `Full`, we have verified the signatures for two thirds of the current guardians.","- If `Partial`, only `num_signatures` guardian signatures have been checked.","","# Warning","Using partially verified price updates is dangerous, as it lowers the threshold of guardians that need to collude to produce a malicious price update."],"type":{"kind":"enum","variants":[{"name":"Partial","fields":[{"name":"num_signatures","type":"u8"}]},{"name":"Full"}]}},{"name":"YieldHarvestCache","docs":["Records epoch yield harvest information for off-chain consumers."],"type":{"kind":"struct","fields":[{"name":"epoch","type":"u64"},{"name":"stability_pool_cap","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_yield_to_pool","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"YieldHarvestConfig","docs":["Captures yield harvest configuration as two basis point values:"],"type":{"kind":"struct","fields":[{"name":"allocation","type":{"defined":{"name":"UFixValue64"}}},{"name":"fee","type":{"defined":{"name":"UFixValue64"}}}]}}],"constants":[{"name":"POOL_AUTH","type":{"array":["u8",9]},"value":"[112, 111, 111, 108, 95, 97, 117, 116, 104]"},{"name":"POOL_CONFIG","type":{"array":["u8",11]},"value":"[112, 111, 111, 108, 95, 99, 111, 110, 102, 105, 103]"},{"name":"STAKED_HYUSD","type":{"array":["u8",12]},"value":"[115, 116, 97, 107, 101, 100, 95, 104, 121, 85, 83, 68]"}]} \ No newline at end of file diff --git a/idls/jito_tip_distribution.4R3gSG8BpU4t19KYj8CfnbtRpnT8gtk4dvTHxVRwc2r7..json b/idls/jito_tip_distribution.4R3gSG8BpU4t19KYj8CfnbtRpnT8gtk4dvTHxVRwc2r7..json new file mode 100644 index 0000000..ddfecc4 --- /dev/null +++ b/idls/jito_tip_distribution.4R3gSG8BpU4t19KYj8CfnbtRpnT8gtk4dvTHxVRwc2r7..json @@ -0,0 +1 @@ +{"address":"4R3gSG8BpU4t19KYj8CfnbtRpnT8gtk4dvTHxVRwc2r7","metadata":{"name":"jito_tip_distribution","version":"0.1.5","spec":"0.1.0","description":"Tip distribution program, responsible for distributing funds to entitled parties."},"instructions":[{"name":"claim","docs":["Claims tokens from the [TipDistributionAccount]."],"discriminator":[62,198,214,193,213,159,108,210],"accounts":[{"name":"config"},{"name":"tip_distribution_account","writable":true},{"name":"merkle_root_upload_authority","signer":true},{"name":"claim_status","docs":["Status of the claim. Used to prevent the same party from claiming multiple times."],"writable":true},{"name":"claimant","docs":["Receiver of the funds."],"writable":true},{"name":"payer","docs":["Who is paying for the claim."],"writable":true,"signer":true},{"name":"system_program"}],"args":[{"name":"bump","type":"u8"},{"name":"amount","type":"u64"},{"name":"proof","type":{"vec":{"array":["u8",32]}}}]},{"name":"close_claim_status","docs":["Anyone can invoke this only after the [TipDistributionAccount] has expired.","This instruction will return any rent back to `claimant` and close the account"],"discriminator":[163,214,191,165,245,188,17,185],"accounts":[{"name":"config"},{"name":"claim_status","writable":true},{"name":"claim_status_payer","docs":["Receiver of the funds."],"writable":true}],"args":[]},{"name":"close_tip_distribution_account","docs":["Anyone can invoke this only after the [TipDistributionAccount] has expired.","This instruction will send any unclaimed funds to the designated `expired_funds_account`","before closing and returning the rent exempt funds to the validator."],"discriminator":[47,136,208,190,125,243,74,227],"accounts":[{"name":"config"},{"name":"expired_funds_account","writable":true},{"name":"tip_distribution_account","writable":true},{"name":"validator_vote_account","writable":true},{"name":"signer","docs":["Anyone can crank this instruction."],"writable":true,"signer":true}],"args":[{"name":"_epoch","type":"u64"}]},{"name":"initialize","docs":["Initialize a singleton instance of the [Config] account."],"discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"config","writable":true},{"name":"system_program"},{"name":"initializer","writable":true,"signer":true}],"args":[{"name":"authority","type":"pubkey"},{"name":"expired_funds_account","type":"pubkey"},{"name":"num_epochs_valid","type":"u64"},{"name":"max_validator_commission_bps","type":"u16"},{"name":"bump","type":"u8"}]},{"name":"initialize_merkle_root_upload_config","discriminator":[232,87,72,14,89,40,40,27],"accounts":[{"name":"config","writable":true},{"name":"merkle_root_upload_config","writable":true},{"name":"authority","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program"}],"args":[{"name":"authority","type":"pubkey"},{"name":"original_authority","type":"pubkey"}]},{"name":"initialize_tip_distribution_account","docs":["Initialize a new [TipDistributionAccount] associated with the given validator vote key","and current epoch."],"discriminator":[120,191,25,182,111,49,179,55],"accounts":[{"name":"config"},{"name":"tip_distribution_account","writable":true},{"name":"validator_vote_account","docs":["The validator's vote account is used to check this transaction's signer is also the authorized withdrawer."]},{"name":"signer","docs":["Must be equal to the supplied validator vote account's authorized withdrawer."],"writable":true,"signer":true},{"name":"system_program"}],"args":[{"name":"merkle_root_upload_authority","type":"pubkey"},{"name":"validator_commission_bps","type":"u16"},{"name":"bump","type":"u8"}]},{"name":"migrate_tda_merkle_root_upload_authority","discriminator":[13,226,163,144,56,202,214,23],"accounts":[{"name":"tip_distribution_account","writable":true},{"name":"merkle_root_upload_config"}],"args":[]},{"name":"update_config","docs":["Update config fields. Only the [Config] authority can invoke this."],"discriminator":[29,158,252,191,10,83,219,99],"accounts":[{"name":"config","writable":true},{"name":"authority","writable":true,"signer":true}],"args":[{"name":"new_config","type":{"defined":{"name":"Config"}}}]},{"name":"update_merkle_root_upload_config","discriminator":[128,227,159,139,176,128,118,2],"accounts":[{"name":"config"},{"name":"merkle_root_upload_config","writable":true},{"name":"authority","signer":true},{"name":"system_program"}],"args":[{"name":"authority","type":"pubkey"},{"name":"original_authority","type":"pubkey"}]},{"name":"upload_merkle_root","docs":["Uploads a merkle root to the provided [TipDistributionAccount]. This instruction may be","invoked many times as long as the account is at least one epoch old and not expired; and","no funds have already been claimed. Only the `merkle_root_upload_authority` has the","authority to invoke."],"discriminator":[70,3,110,29,199,190,205,176],"accounts":[{"name":"config"},{"name":"tip_distribution_account","writable":true},{"name":"merkle_root_upload_authority","writable":true,"signer":true}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"max_total_claim","type":"u64"},{"name":"max_num_nodes","type":"u64"}]}],"accounts":[{"name":"ClaimStatus","discriminator":[22,183,249,157,247,95,150,96]},{"name":"Config","discriminator":[155,12,170,224,30,250,204,130]},{"name":"MerkleRootUploadConfig","discriminator":[213,125,30,192,25,121,87,33]},{"name":"TipDistributionAccount","discriminator":[85,64,113,198,234,94,120,123]}],"events":[{"name":"ClaimStatusClosedEvent","discriminator":[188,143,237,229,192,182,164,118]},{"name":"ClaimedEvent","discriminator":[144,172,209,86,144,87,84,115]},{"name":"ConfigUpdatedEvent","discriminator":[245,158,129,99,60,100,214,220]},{"name":"MerkleRootUploadAuthorityUpdatedEvent","discriminator":[83,157,58,165,200,171,8,106]},{"name":"MerkleRootUploadedEvent","discriminator":[94,233,236,49,52,224,181,167]},{"name":"TipDistributionAccountClosedEvent","discriminator":[246,152,49,154,9,79,25,58]},{"name":"TipDistributionAccountInitializedEvent","discriminator":[39,165,224,61,40,140,139,255]},{"name":"ValidatorCommissionBpsUpdatedEvent","discriminator":[4,34,92,25,228,88,51,206]}],"errors":[{"code":6000,"name":"AccountValidationFailure","msg":"Account failed validation."},{"code":6001,"name":"ArithmeticError","msg":"Encountered an arithmetic under/overflow error."},{"code":6002,"name":"ExceedsMaxClaim","msg":"The maximum number of funds to be claimed has been exceeded."},{"code":6003,"name":"ExceedsMaxNumNodes","msg":"The maximum number of claims has been exceeded."},{"code":6004,"name":"ExpiredTipDistributionAccount","msg":"The given TipDistributionAccount has expired."},{"code":6005,"name":"FundsAlreadyClaimed","msg":"The funds for the given index and TipDistributionAccount have already been claimed."},{"code":6006,"name":"InvalidParameters","msg":"Supplied invalid parameters."},{"code":6007,"name":"InvalidProof","msg":"The given proof is invalid."},{"code":6008,"name":"InvalidVoteAccountData","msg":"Failed to deserialize the supplied vote account data."},{"code":6009,"name":"MaxValidatorCommissionFeeBpsExceeded","msg":"Validator's commission basis points must be less than or equal to the Config account's max_validator_commission_bps."},{"code":6010,"name":"PrematureCloseTipDistributionAccount","msg":"The given TipDistributionAccount is not ready to be closed."},{"code":6011,"name":"PrematureCloseClaimStatus","msg":"The given ClaimStatus account is not ready to be closed."},{"code":6012,"name":"PrematureMerkleRootUpload","msg":"Must wait till at least one epoch after the tip distribution account was created to upload the merkle root."},{"code":6013,"name":"RootNotUploaded","msg":"No merkle root has been uploaded to the given TipDistributionAccount."},{"code":6014,"name":"Unauthorized","msg":"Unauthorized signer."},{"code":6015,"name":"InvalidTdaForMigration","msg":"TDA not valid for migration."}],"types":[{"name":"ClaimStatus","docs":["Gives us an audit trail of who and what was claimed; also enforces and only-once claim by any party."],"type":{"kind":"struct","fields":[{"name":"is_claimed","docs":["If true, the tokens have been claimed."],"type":"bool"},{"name":"claimant","docs":["Authority that claimed the tokens. Allows for delegated rewards claiming."],"type":"pubkey"},{"name":"claim_status_payer","docs":["The payer who created the claim."],"type":"pubkey"},{"name":"slot_claimed_at","docs":["When the funds were claimed."],"type":"u64"},{"name":"amount","docs":["Amount of funds claimed."],"type":"u64"},{"name":"expires_at","docs":["The epoch (upto and including) that tip funds can be claimed.","Copied since TDA can be closed, need to track to avoid making multiple claims"],"type":"u64"},{"name":"bump","docs":["The bump used to generate this account"],"type":"u8"}]}},{"name":"ClaimStatusClosedEvent","type":{"kind":"struct","fields":[{"name":"claim_status_payer","docs":["Account where funds were transferred to."],"type":"pubkey"},{"name":"claim_status_account","docs":["[ClaimStatus] account that was closed."],"type":"pubkey"}]}},{"name":"ClaimedEvent","type":{"kind":"struct","fields":[{"name":"tip_distribution_account","docs":["[TipDistributionAccount] claimed from."],"type":"pubkey"},{"name":"payer","docs":["User that paid for the claim, may or may not be the same as claimant."],"type":"pubkey"},{"name":"claimant","docs":["Account that received the funds."],"type":"pubkey"},{"name":"amount","docs":["Amount of funds to distribute."],"type":"u64"}]}},{"name":"Config","type":{"kind":"struct","fields":[{"name":"authority","docs":["Account with authority over this PDA."],"type":"pubkey"},{"name":"expired_funds_account","docs":["We want to expire funds after some time so that validators can be refunded the rent.","Expired funds will get transferred to this account."],"type":"pubkey"},{"name":"num_epochs_valid","docs":["Specifies the number of epochs a merkle root is valid for before expiring."],"type":"u64"},{"name":"max_validator_commission_bps","docs":["The maximum commission a validator can set on their distribution account."],"type":"u16"},{"name":"bump","docs":["The bump used to generate this account"],"type":"u8"}]}},{"name":"ConfigUpdatedEvent","type":{"kind":"struct","fields":[{"name":"authority","docs":["Who updated it."],"type":"pubkey"}]}},{"name":"MerkleRoot","type":{"kind":"struct","fields":[{"name":"root","docs":["The 256-bit merkle root."],"type":{"array":["u8",32]}},{"name":"max_total_claim","docs":["Maximum number of funds that can ever be claimed from this [MerkleRoot]."],"type":"u64"},{"name":"max_num_nodes","docs":["Maximum number of nodes that can ever be claimed from this [MerkleRoot]."],"type":"u64"},{"name":"total_funds_claimed","docs":["Total funds that have been claimed."],"type":"u64"},{"name":"num_nodes_claimed","docs":["Number of nodes that have been claimed."],"type":"u64"}]}},{"name":"MerkleRootUploadAuthorityUpdatedEvent","type":{"kind":"struct","fields":[{"name":"old_authority","type":"pubkey"},{"name":"new_authority","type":"pubkey"}]}},{"name":"MerkleRootUploadConfig","docs":["Singleton account that allows overriding TDA's merkle upload authority"],"type":{"kind":"struct","fields":[{"name":"override_authority","docs":["The authority that overrides the TipDistributionAccount merkle_root_upload_authority"],"type":"pubkey"},{"name":"original_upload_authority","docs":["The original merkle root upload authority that can be changed to the new overrided","authority. E.g. Jito Labs authority GZctHpWXmsZC1YHACTGGcHhYxjdRqQvTpYkb9LMvxDib"],"type":"pubkey"},{"name":"bump","docs":["The bump used to generate this account"],"type":"u8"}]}},{"name":"MerkleRootUploadedEvent","type":{"kind":"struct","fields":[{"name":"merkle_root_upload_authority","docs":["Who uploaded the root."],"type":"pubkey"},{"name":"tip_distribution_account","docs":["Where the root was uploaded to."],"type":"pubkey"}]}},{"name":"TipDistributionAccount","docs":["The account that validators register as **tip_receiver** with the tip-payment program."],"type":{"kind":"struct","fields":[{"name":"validator_vote_account","docs":["The validator's vote account, also the recipient of remaining lamports after","upon closing this account."],"type":"pubkey"},{"name":"merkle_root_upload_authority","docs":["The only account authorized to upload a merkle-root for this account."],"type":"pubkey"},{"name":"merkle_root","docs":["The merkle root used to verify user claims from this account."],"type":{"option":{"defined":{"name":"MerkleRoot"}}}},{"name":"epoch_created_at","docs":["Epoch for which this account was created."],"type":"u64"},{"name":"validator_commission_bps","docs":["The commission basis points this validator charges."],"type":"u16"},{"name":"expires_at","docs":["The epoch (upto and including) that tip funds can be claimed."],"type":"u64"},{"name":"bump","docs":["The bump used to generate this account"],"type":"u8"}]}},{"name":"TipDistributionAccountClosedEvent","type":{"kind":"struct","fields":[{"name":"expired_funds_account","docs":["Account where unclaimed funds were transferred to."],"type":"pubkey"},{"name":"tip_distribution_account","docs":["[TipDistributionAccount] closed."],"type":"pubkey"},{"name":"expired_amount","docs":["Unclaimed amount transferred."],"type":"u64"}]}},{"name":"TipDistributionAccountInitializedEvent","type":{"kind":"struct","fields":[{"name":"tip_distribution_account","type":"pubkey"}]}},{"name":"ValidatorCommissionBpsUpdatedEvent","type":{"kind":"struct","fields":[{"name":"tip_distribution_account","type":"pubkey"},{"name":"old_commission_bps","type":"u16"},{"name":"new_commission_bps","type":"u16"}]}}]} \ No newline at end of file diff --git a/idls/jupiter_aprepro_smart_wallet.JSW99DKmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw.json b/idls/jupiter_aprepro_smart_wallet.JSW99DKmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw.json new file mode 100644 index 0000000..7cc97ac --- /dev/null +++ b/idls/jupiter_aprepro_smart_wallet.JSW99DKmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"jupiter_smart_wallet","instructions":[{"name":"initialize","accounts":[{"name":"vault","isMut":false,"isSigner":false},{"name":"vaultInfo","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"userSigner","type":{"array":["u8",64]}},{"name":"label","type":"string"}]},{"name":"flashSwapApprove","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"wsolMint","isMut":false,"isSigner":false},{"name":"vaultWsol","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"vaultInfo","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"keeperInputAccount","isMut":true,"isSigner":false},{"name":"inputVault","isMut":true,"isSigner":false},{"name":"outputVault","isMut":true,"isSigner":false,"docs":["CHECKED: in implementation code"]},{"name":"inputTokenProgram","isMut":false,"isSigner":false},{"name":"outputTokenProgram","isMut":false,"isSigner":false},{"name":"instructionsSysvar","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"signature","type":{"array":["u8",64]}},{"name":"recoveryId","type":"u8"},{"name":"priorityFeeLamports","type":"u64"},{"name":"inputAmount","type":"u64"},{"name":"minOut","type":"u64"}]},{"name":"preFlashSwapApprove","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"vaultWsol","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"vaultInfo","isMut":true,"isSigner":false},{"name":"inputMint","isMut":true,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"keeperInputAccount","isMut":false,"isSigner":false},{"name":"referrer","isMut":true,"isSigner":false},{"name":"inputVault","isMut":true,"isSigner":false},{"name":"outputVault","isMut":true,"isSigner":false,"docs":["CHECKED: in implementation code"]},{"name":"inputTokenProgram","isMut":false,"isSigner":false},{"name":"outputTokenProgram","isMut":false,"isSigner":false},{"name":"instructionsSysvar","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"signature","type":{"array":["u8",64]}},{"name":"recoveryId","type":"u8"},{"name":"priorityFeeLamports","type":"u64"},{"name":"inputAmount","type":"u64"},{"name":"minOut","type":"u64"},{"name":"postSwapIxIndex","type":"u8"},{"name":"feeBps","type":"u16"},{"name":"referralShareBps","type":"u16"},{"name":"useDurableNonce","type":"bool"},{"name":"maxOut","type":"u64"}]},{"name":"postSwap","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"wsolMint","isMut":false,"isSigner":false},{"name":"vaultWsol","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"vaultInfo","isMut":true,"isSigner":false},{"name":"referrer","isMut":true,"isSigner":false},{"name":"outputVault","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"withdraw","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"vault","isMut":true,"isSigner":false},{"name":"vaultInfo","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"vaultTokenAccount","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"signature","type":{"array":["u8",64]}},{"name":"recoveryId","type":"u8"},{"name":"amount","type":"u64"}]},{"name":"withdrawToken","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"vault","isMut":true,"isSigner":false},{"name":"vaultInfo","isMut":true,"isSigner":false},{"name":"receiver","isMut":false,"isSigner":false},{"name":"receiverTokenAccount","isMut":true,"isSigner":false},{"name":"vaultTokenAccount","isMut":true,"isSigner":false},{"name":"mint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"signature","type":{"array":["u8",64]}},{"name":"recoveryId","type":"u8"},{"name":"amount","type":"u64"}]}],"accounts":[{"name":"VaultInfo","type":{"kind":"struct","fields":[{"name":"vault","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"createdAt","type":"i64"},{"name":"label","type":"string"},{"name":"transientNonces","type":{"array":["u64",32]}},{"name":"userSigner","type":{"array":["u8",64]}},{"name":"swapInProgress","type":"bool"},{"name":"preSwapAmount","type":"u64"},{"name":"minOut","type":"u64"},{"name":"feeBps","type":"u16"},{"name":"referralShareBps","type":"u16"},{"name":"maxOut","type":"u64"},{"name":"reserved","type":{"array":["u8",31]}},{"name":"durableNonces","type":{"vec":"u64"}}]}}],"types":[{"name":"SwapApproval","type":{"kind":"struct","fields":[{"name":"nonce","type":"u64"},{"name":"inputMint","type":"publicKey"},{"name":"outputMint","type":"publicKey"},{"name":"inputAmount","type":"u64"},{"name":"priorityFeeLamports","type":"u64"},{"name":"useDurableNonce","type":"bool"}]}},{"name":"WithdrawApproval","type":{"kind":"struct","fields":[{"name":"nonce","type":"u64"},{"name":"amount","type":"u64"},{"name":"withdrawalAddress","type":"publicKey"}]}},{"name":"WithdrawTokenApproval","type":{"kind":"struct","fields":[{"name":"nonce","type":"u64"},{"name":"amount","type":"u64"},{"name":"mint","type":"publicKey"},{"name":"withdrawalAddress","type":"publicKey"}]}},{"name":"BurnApproval","type":{"kind":"struct","fields":[{"name":"nonce","type":"u64"},{"name":"mint","type":"publicKey"}]}}],"events":[{"name":"Initialize","fields":[{"name":"vaultKey","type":"publicKey","index":false},{"name":"userKey","type":{"array":["u8",64]},"index":false}]},{"name":"Withdraw","fields":[{"name":"vaultKey","type":"publicKey","index":false},{"name":"receiverKey","type":"publicKey","index":false},{"name":"mint","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]},{"name":"Swap","fields":[{"name":"userKey","type":{"array":["u8",64]},"index":false},{"name":"vaultKey","type":"publicKey","index":false},{"name":"inputMint","type":"publicKey","index":false},{"name":"outputMint","type":"publicKey","index":false},{"name":"inputAmount","type":"u64","index":false},{"name":"baseFeeAmount","type":"u64","index":false}]},{"name":"Refer","fields":[{"name":"vaultKey","type":"publicKey","index":false},{"name":"referrerKey","type":"publicKey","index":false},{"name":"shareAmount","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"MathOverflow"},{"code":6001,"name":"AddressMismatch"},{"code":6002,"name":"Unauthorized"},{"code":6003,"name":"UnauthorizedKeeper"},{"code":6004,"name":"ProgramMismatch"},{"code":6005,"name":"UnexpectedSwapProgram"},{"code":6006,"name":"UnexpectedSwapInstruction"},{"code":6007,"name":"WrongSwapOutAccount"},{"code":6008,"name":"InvalidSwapMint"},{"code":6009,"name":"ExceedSlippageSetting"},{"code":6010,"name":"AmountMismatch"},{"code":6011,"name":"MissingSwapInstructions"},{"code":6012,"name":"InvalidPublicKey"},{"code":6013,"name":"InvalidApproval"},{"code":6014,"name":"ExpiredApproval"},{"code":6015,"name":"ExpiredOrder"},{"code":6016,"name":"InsufficientSOLForFees"},{"code":6017,"name":"InvalidNonce"},{"code":6018,"name":"ViolateMinOut"},{"code":6019,"name":"FeeTooHigh"},{"code":6020,"name":"NonceInvalid"},{"code":6021,"name":"NonceUsed"},{"code":6022,"name":"NonceLimit"},{"code":6023,"name":"UnableToVerify"},{"code":6024,"name":"MissingPreSwapIx"},{"code":6025,"name":"MissingPostSwapIx"},{"code":6026,"name":"InvalidBps"},{"code":6027,"name":"TooLargeFee"},{"code":6028,"name":"InvalidReceiverTokenAccount"},{"code":6029,"name":"Deprecated"},{"code":6030,"name":"ViolateMaxOut"}]} \ No newline at end of file diff --git a/idls/jupiter_dca.DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M.json b/idls/jupiter_dca.DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M.json new file mode 100644 index 0000000..bfcf77a --- /dev/null +++ b/idls/jupiter_dca.DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"dca","instructions":[{"name":"openDca","accounts":[{"name":"dca","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":true},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"userAta","isMut":true,"isSigner":false},{"name":"inAta","isMut":true,"isSigner":false},{"name":"outAta","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"applicationIdx","type":"u64"},{"name":"inAmount","type":"u64"},{"name":"inAmountPerCycle","type":"u64"},{"name":"cycleFrequency","type":"i64"},{"name":"minOutAmount","type":{"option":"u64"}},{"name":"maxOutAmount","type":{"option":"u64"}},{"name":"startAt","type":{"option":"i64"}},{"name":"closeWsolInAta","type":{"option":"bool"}}]},{"name":"openDcaV2","accounts":[{"name":"dca","isMut":true,"isSigner":false},{"name":"user","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"userAta","isMut":true,"isSigner":false},{"name":"inAta","isMut":true,"isSigner":false},{"name":"outAta","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"applicationIdx","type":"u64"},{"name":"inAmount","type":"u64"},{"name":"inAmountPerCycle","type":"u64"},{"name":"cycleFrequency","type":"i64"},{"name":"minOutAmount","type":{"option":"u64"}},{"name":"maxOutAmount","type":{"option":"u64"}},{"name":"startAt","type":{"option":"i64"}}]},{"name":"closeDca","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"dca","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"inAta","isMut":true,"isSigner":false},{"name":"outAta","isMut":true,"isSigner":false},{"name":"userInAta","isMut":true,"isSigner":false},{"name":"userOutAta","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"withdraw","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"dca","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"dcaAta","isMut":true,"isSigner":false},{"name":"userInAta","isMut":true,"isSigner":false,"isOptional":true},{"name":"userOutAta","isMut":true,"isSigner":false,"isOptional":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"withdrawParams","type":{"defined":"WithdrawParams"}}]},{"name":"deposit","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"dca","isMut":true,"isSigner":false},{"name":"inAta","isMut":true,"isSigner":false},{"name":"userInAta","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"depositIn","type":"u64"}]},{"name":"withdrawFees","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"mint","isMut":false,"isSigner":false},{"name":"feeAuthority","isMut":false,"isSigner":false,"docs":["CHECK"]},{"name":"programFeeAta","isMut":true,"isSigner":false},{"name":"adminFeeAta","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"initiateFlashFill","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"dca","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false,"docs":["The token to borrow"]},{"name":"keeperInAta","isMut":true,"isSigner":false,"docs":["The account to send borrowed tokens to"]},{"name":"inAta","isMut":true,"isSigner":false,"docs":["The account to borrow from"]},{"name":"outAta","isMut":false,"isSigner":false,"docs":["The account to repay to"]},{"name":"instructionsSysvar","isMut":false,"isSigner":false,"docs":["Solana Instructions Sysvar"]},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"fulfillFlashFill","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"dca","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"keeperInAta","isMut":false,"isSigner":false},{"name":"inAta","isMut":false,"isSigner":false},{"name":"outAta","isMut":false,"isSigner":false},{"name":"feeAuthority","isMut":false,"isSigner":false,"docs":["CHECK"]},{"name":"feeAta","isMut":true,"isSigner":false},{"name":"instructionsSysvar","isMut":false,"isSigner":false,"docs":["Solana Instructions Sysvar"]},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"repayAmount","type":"u64"}]},{"name":"initiateDlmmFill","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"dca","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false,"docs":["The token to borrow"]},{"name":"keeperInAta","isMut":true,"isSigner":false,"docs":["The account to send borrowed tokens to"]},{"name":"inAta","isMut":true,"isSigner":false,"docs":["The account to borrow from"]},{"name":"outAta","isMut":false,"isSigner":false,"docs":["The account to repay to"]},{"name":"instructionsSysvar","isMut":false,"isSigner":false,"docs":["Solana Instructions Sysvar"]},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"fulfillDlmmFill","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"dca","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"keeperInAta","isMut":false,"isSigner":false},{"name":"inAta","isMut":false,"isSigner":false},{"name":"outAta","isMut":false,"isSigner":false},{"name":"feeAuthority","isMut":false,"isSigner":false,"docs":["CHECK"]},{"name":"feeAta","isMut":true,"isSigner":false},{"name":"instructionsSysvar","isMut":false,"isSigner":false,"docs":["Solana Instructions Sysvar"]},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"repayAmount","type":"u64"}]},{"name":"transfer","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"dca","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"dcaOutAta","isMut":true,"isSigner":false},{"name":"userOutAta","isMut":true,"isSigner":false,"isOptional":true},{"name":"intermediateAccount","isMut":true,"isSigner":false,"isOptional":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"endAndClose","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"dca","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"inAta","isMut":true,"isSigner":false},{"name":"outAta","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userOutAta","isMut":true,"isSigner":false,"isOptional":true},{"name":"initUserOutAta","isMut":true,"isSigner":false,"isOptional":true},{"name":"intermediateAccount","isMut":true,"isSigner":false,"isOptional":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"Dca","type":{"kind":"struct","fields":[{"name":"user","type":"publicKey"},{"name":"inputMint","type":"publicKey"},{"name":"outputMint","type":"publicKey"},{"name":"idx","type":"u64"},{"name":"nextCycleAt","type":"i64"},{"name":"inDeposited","type":"u64"},{"name":"inWithdrawn","type":"u64"},{"name":"outWithdrawn","type":"u64"},{"name":"inUsed","type":"u64"},{"name":"outReceived","type":"u64"},{"name":"inAmountPerCycle","type":"u64"},{"name":"cycleFrequency","type":"i64"},{"name":"nextCycleAmountLeft","type":"u64"},{"name":"inAccount","type":"publicKey"},{"name":"outAccount","type":"publicKey"},{"name":"minOutAmount","type":"u64"},{"name":"maxOutAmount","type":"u64"},{"name":"keeperInBalanceBeforeBorrow","type":"u64"},{"name":"dcaOutBalanceBeforeSwap","type":"u64"},{"name":"createdAt","type":"i64"},{"name":"bump","type":"u8"}]}}],"types":[{"name":"WithdrawParams","type":{"kind":"struct","fields":[{"name":"withdrawAmount","type":"u64"},{"name":"withdrawal","type":{"defined":"Withdrawal"}}]}},{"name":"Withdrawal","type":{"kind":"enum","variants":[{"name":"In"},{"name":"Out"}]}}],"events":[{"name":"CollectedFee","fields":[{"name":"userKey","type":"publicKey","index":false},{"name":"dcaKey","type":"publicKey","index":false},{"name":"mint","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]},{"name":"Filled","fields":[{"name":"userKey","type":"publicKey","index":false},{"name":"dcaKey","type":"publicKey","index":false},{"name":"inputMint","type":"publicKey","index":false},{"name":"outputMint","type":"publicKey","index":false},{"name":"inAmount","type":"u64","index":false},{"name":"outAmount","type":"u64","index":false},{"name":"feeMint","type":"publicKey","index":false},{"name":"fee","type":"u64","index":false}]},{"name":"Opened","fields":[{"name":"userKey","type":"publicKey","index":false},{"name":"dcaKey","type":"publicKey","index":false},{"name":"inDeposited","type":"u64","index":false},{"name":"inputMint","type":"publicKey","index":false},{"name":"outputMint","type":"publicKey","index":false},{"name":"cycleFrequency","type":"i64","index":false},{"name":"inAmountPerCycle","type":"u64","index":false},{"name":"createdAt","type":"i64","index":false}]},{"name":"Closed","fields":[{"name":"userKey","type":"publicKey","index":false},{"name":"dcaKey","type":"publicKey","index":false},{"name":"inDeposited","type":"u64","index":false},{"name":"inputMint","type":"publicKey","index":false},{"name":"outputMint","type":"publicKey","index":false},{"name":"cycleFrequency","type":"i64","index":false},{"name":"inAmountPerCycle","type":"u64","index":false},{"name":"createdAt","type":"i64","index":false},{"name":"totalInWithdrawn","type":"u64","index":false},{"name":"totalOutWithdrawn","type":"u64","index":false},{"name":"unfilledAmount","type":"u64","index":false},{"name":"userClosed","type":"bool","index":false}]},{"name":"Withdraw","fields":[{"name":"dcaKey","type":"publicKey","index":false},{"name":"inAmount","type":"u64","index":false},{"name":"outAmount","type":"u64","index":false},{"name":"userWithdraw","type":"bool","index":false}]},{"name":"Deposit","fields":[{"name":"dcaKey","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"InvalidAmount","msg":"Invalid deposit amount"},{"code":6001,"name":"InvalidCycleAmount","msg":"Invalid deposit amount"},{"code":6002,"name":"InvalidPair","msg":"Invalid pair"},{"code":6003,"name":"TooFrequent","msg":"Too frequent DCA cycle"},{"code":6004,"name":"InvalidMinPrice","msg":"Minimum price constraint must be greater than 0"},{"code":6005,"name":"InvalidMaxPrice","msg":"Maximum price constraint must be greater than 0"},{"code":6006,"name":"InAmountInsufficient","msg":"In amount needs to be more than in amount per cycle"},{"code":6007,"name":"Unauthorized","msg":"Wrong user"},{"code":6008,"name":"NoInATA","msg":"inAta not passed in"},{"code":6009,"name":"NoUserInATA","msg":"userInAta not passed in"},{"code":6010,"name":"NoOutATA","msg":"outAta not passed in"},{"code":6011,"name":"NoUserOutATA","msg":"userOutAta not passed in"},{"code":6012,"name":"InsufficientBalanceInProgram","msg":"Trying to withdraw more than available"},{"code":6013,"name":"InvalidDepositAmount","msg":"Deposit should be more than 0"},{"code":6014,"name":"UserInsufficientBalance","msg":"User has insufficient balance"},{"code":6015,"name":"UnauthorizedKeeper","msg":"Unauthorized Keeper"},{"code":6016,"name":"UnrecognizedProgram","msg":"Unrecognized Program"},{"code":6017,"name":"MathErrors","msg":"Calculation errors"},{"code":6018,"name":"KeeperNotTimeToFill","msg":"Not time to fill"},{"code":6019,"name":"OrderFillAmountWrong","msg":"Order amount wrong"},{"code":6020,"name":"SwapOutAmountBelowMinimum","msg":"Out amount below expectations"},{"code":6021,"name":"WrongAdmin","msg":"Wrong admin"},{"code":6022,"name":"MathOverflow","msg":"Overflow in arithmetic operation"},{"code":6023,"name":"AddressMismatch","msg":"Address Mismatch"},{"code":6024,"name":"ProgramMismatch","msg":"Program Mismatch"},{"code":6025,"name":"IncorrectRepaymentAmount","msg":"Incorrect Repayment Amount"},{"code":6026,"name":"CannotBorrowBeforeRepay","msg":"Cannot Borrow Before Repay"},{"code":6027,"name":"NoRepaymentInstructionFound","msg":"No Repayment Found"},{"code":6028,"name":"MissingSwapInstructions","msg":"Missing Swap Instruction"},{"code":6029,"name":"UnexpectedSwapProgram","msg":"Expected Instruction to use Jupiter Swap Program"},{"code":6030,"name":"InvalidSwapMint","msg":"Invalid Swap Mint"},{"code":6031,"name":"UnknownInstruction","msg":"Unknown Instruction"},{"code":6032,"name":"MissingRepayInstructions","msg":"Missing Repay Instruction"},{"code":6033,"name":"KeeperShortchanged","msg":"Keeper Shortchanged"},{"code":6034,"name":"WrongSwapOutAccount","msg":"Jup Swap to Wrong Out Account"},{"code":6035,"name":"WrongTransferAmount","msg":"Transfer amount should be exactly account balance"},{"code":6036,"name":"InsufficientBalanceForRent","msg":"Insufficient balance for rent"},{"code":6037,"name":"UnexpectedSolBalance","msg":"Unexpected SOL amount in intermediate account"},{"code":6038,"name":"InsufficientWsolForTransfer","msg":"Too little WSOL to perform transfer"},{"code":6039,"name":"MissedInstruction","msg":"Did not call initiate_flash_fill"},{"code":6040,"name":"WrongProgram","msg":"Did not call this program's initiate_flash_fill"},{"code":6041,"name":"BalanceNotZero","msg":"Can't close account with balance"},{"code":6042,"name":"UnexpectedWSOLLeftover","msg":"Should not have WSOL leftover in DCA out-token account"},{"code":6043,"name":"IntermediateAccountNotSet","msg":"Should pass in a WSOL intermediate account when transferring SOL"},{"code":6044,"name":"UnexpectedSwapInstruction","msg":"Did not call jup swap"},{"code":6045,"name":"SwapOutLessThanUserMinimum","msg":"Expect more from swap"},{"code":6046,"name":"SwapOutMoreThanUserMaximum","msg":"Expect less from swap"}]} \ No newline at end of file diff --git a/idls/jupiter_limit_order.jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu.json b/idls/jupiter_limit_order.jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu.json new file mode 100644 index 0000000..1f67468 --- /dev/null +++ b/idls/jupiter_limit_order.jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"limit_order","instructions":[{"name":"initializeOrder","accounts":[{"name":"base","isMut":false,"isSigner":true},{"name":"maker","isMut":true,"isSigner":true},{"name":"order","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false,"docs":["CHECK"]},{"name":"makerInputAccount","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"makerOutputAccount","isMut":false,"isSigner":false},{"name":"referral","isMut":false,"isSigner":false,"isOptional":true},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"makingAmount","type":"u64"},{"name":"takingAmount","type":"u64"},{"name":"expiredAt","type":{"option":"i64"}}]},{"name":"fillOrder","accounts":[{"name":"order","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"maker","isMut":true,"isSigner":false},{"name":"taker","isMut":false,"isSigner":true},{"name":"takerOutputAccount","isMut":true,"isSigner":false},{"name":"makerOutputAccount","isMut":true,"isSigner":false},{"name":"takerInputAccount","isMut":true,"isSigner":false},{"name":"feeAuthority","isMut":false,"isSigner":false},{"name":"programFeeAccount","isMut":true,"isSigner":false},{"name":"referral","isMut":true,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"makingAmount","type":"u64"},{"name":"maxTakingAmount","type":"u64"}]},{"name":"preFlashFillOrder","accounts":[{"name":"order","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"taker","isMut":false,"isSigner":true},{"name":"takerOutputAccount","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"inputMintTokenProgram","isMut":false,"isSigner":false},{"name":"instruction","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"makingAmount","type":"u64"}]},{"name":"flashFillOrder","accounts":[{"name":"order","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"maker","isMut":true,"isSigner":false},{"name":"taker","isMut":false,"isSigner":true},{"name":"makerOutputAccount","isMut":true,"isSigner":false},{"name":"takerInputAccount","isMut":true,"isSigner":false},{"name":"feeAuthority","isMut":false,"isSigner":false},{"name":"programFeeAccount","isMut":true,"isSigner":false},{"name":"referral","isMut":true,"isSigner":false,"isOptional":true},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"inputMintTokenProgram","isMut":false,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"outputMintTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxTakingAmount","type":"u64"}]},{"name":"cancelOrder","accounts":[{"name":"order","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false,"docs":["CHECK"]},{"name":"maker","isMut":true,"isSigner":true},{"name":"makerInputAccount","isMut":true,"isSigner":false,"docs":["CHECK, it is not important if it is sol input mint"]},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false,"isOptional":true}],"args":[]},{"name":"cancelExpiredOrder","accounts":[{"name":"order","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false,"docs":["CHECK"]},{"name":"maker","isMut":true,"isSigner":false},{"name":"makerInputAccount","isMut":true,"isSigner":false,"docs":["CHECK, it is not important if it is sol input mint"]},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false,"isOptional":true}],"args":[]},{"name":"withdrawFee","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"feeAuthority","isMut":false,"isSigner":false,"docs":["CHECK"]},{"name":"programFeeAccount","isMut":true,"isSigner":false},{"name":"adminTokenAcocunt","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"initFee","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"feeAuthority","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"makerFee","type":"u64"},{"name":"makerStableFee","type":"u64"},{"name":"takerFee","type":"u64"},{"name":"takerStableFee","type":"u64"}]},{"name":"updateFee","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"feeAuthority","isMut":true,"isSigner":false}],"args":[{"name":"makerFee","type":"u64"},{"name":"makerStableFee","type":"u64"},{"name":"takerFee","type":"u64"},{"name":"takerStableFee","type":"u64"}]}],"accounts":[{"name":"Fee","type":{"kind":"struct","fields":[{"name":"makerFee","type":"u64"},{"name":"makerStableFee","type":"u64"},{"name":"takerFee","type":"u64"},{"name":"takerStableFee","type":"u64"}]}},{"name":"Order","type":{"kind":"struct","fields":[{"name":"maker","type":"publicKey"},{"name":"inputMint","type":"publicKey"},{"name":"outputMint","type":"publicKey"},{"name":"waiting","type":"bool"},{"name":"oriMakingAmount","type":"u64"},{"name":"oriTakingAmount","type":"u64"},{"name":"makingAmount","type":"u64"},{"name":"takingAmount","type":"u64"},{"name":"makerInputAccount","type":"publicKey"},{"name":"makerOutputAccount","type":"publicKey"},{"name":"reserve","type":"publicKey"},{"name":"borrowMakingAmount","type":"u64"},{"name":"expiredAt","type":{"option":"i64"}},{"name":"base","type":"publicKey"},{"name":"referral","type":{"option":"publicKey"}}]}}],"events":[{"name":"TradeEvent","fields":[{"name":"orderKey","type":"publicKey","index":false},{"name":"taker","type":"publicKey","index":false},{"name":"remainingInAmount","type":"u64","index":false},{"name":"remainingOutAmount","type":"u64","index":false},{"name":"inAmount","type":"u64","index":false},{"name":"outAmount","type":"u64","index":false}]},{"name":"CancelOrderEvent","fields":[{"name":"orderKey","type":"publicKey","index":false}]},{"name":"CreateOrderEvent","fields":[{"name":"orderKey","type":"publicKey","index":false},{"name":"maker","type":"publicKey","index":false},{"name":"inputMint","type":"publicKey","index":false},{"name":"outputMint","type":"publicKey","index":false},{"name":"inAmount","type":"u64","index":false},{"name":"outAmount","type":"u64","index":false},{"name":"expiredAt","type":{"option":"i64"},"index":false}]}],"errors":[{"code":6000,"name":"InvalidMakingAmount"},{"code":6001,"name":"InvalidTakingAmount"},{"code":6002,"name":"InvalidMaxTakingAmount"},{"code":6003,"name":"InvalidCalculation"},{"code":6004,"name":"InvalidInputAccount"},{"code":6005,"name":"InvalidOutputAccount"},{"code":6006,"name":"InvalidPair"},{"code":6007,"name":"MissingReferral"},{"code":6008,"name":"OrderExpired"},{"code":6009,"name":"OrderNotExpired"},{"code":6010,"name":"InvalidKeeper"},{"code":6011,"name":"MathOverflow"},{"code":6012,"name":"ProgramMismatch"},{"code":6013,"name":"UnknownInstruction"},{"code":6014,"name":"MissingRepayInstructions"},{"code":6015,"name":"InvalidOrder"},{"code":6016,"name":"InvalidBorrowMakingAmount"}]} \ No newline at end of file diff --git a/idls/jupiter_limit_order_v2.j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X.json b/idls/jupiter_limit_order_v2.j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X.json new file mode 100644 index 0000000..4d7c8c5 --- /dev/null +++ b/idls/jupiter_limit_order_v2.j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X.json @@ -0,0 +1 @@ +{"address":"j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X","metadata":{"name":"limit_order_2","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"cancel_dust_order","discriminator":[197,112,189,164,79,48,23,246],"accounts":[{"name":"signer","signer":true},{"name":"maker","writable":true,"relations":["order"]},{"name":"order","writable":true},{"name":"input_mint_reserve","writable":true},{"name":"maker_input_mint_account","writable":true},{"name":"input_mint"},{"name":"input_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"CancelDustOrderParams"}}}]},{"name":"cancel_order","discriminator":[95,129,237,240,8,49,223,132],"accounts":[{"name":"signer","signer":true},{"name":"maker","writable":true,"relations":["order"]},{"name":"order","writable":true},{"name":"input_mint_reserve","writable":true},{"name":"maker_input_mint_account","writable":true,"optional":true},{"name":"input_mint"},{"name":"input_token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"fill_order","discriminator":[232,122,115,25,199,143,136,162],"accounts":[{"name":"taker","writable":true,"signer":true},{"name":"maker","writable":true,"relations":["order"]},{"name":"order","writable":true},{"name":"taker_input_mint_account","writable":true},{"name":"taker_output_mint_account","writable":true},{"name":"maker_output_mint_account","writable":true,"optional":true},{"name":"fee_account","writable":true},{"name":"order_input_mint_account","writable":true},{"name":"input_mint"},{"name":"input_token_program"},{"name":"output_mint"},{"name":"output_token_program"},{"name":"jupiter_program","address":"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"FillOrderParams"}}}]},{"name":"flash_fill_order","discriminator":[252,104,18,134,164,78,18,140],"accounts":[{"name":"taker","writable":true,"signer":true},{"name":"maker","writable":true,"relations":["order"]},{"name":"order","writable":true},{"name":"input_mint_reserve","writable":true},{"name":"maker_output_mint_account","writable":true,"optional":true},{"name":"taker_output_mint_account","writable":true,"optional":true},{"name":"fee_account","writable":true},{"name":"input_token_program"},{"name":"output_mint"},{"name":"output_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"FlashFillOrderParams"}}}]},{"name":"initialize_order","discriminator":[133,110,74,175,112,159,245,159],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"maker","signer":true},{"name":"order","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,100,101,114]},{"kind":"account","path":"maker"},{"kind":"arg","path":"unique_id"}]}},{"name":"input_mint_reserve","writable":true,"pda":{"seeds":[{"kind":"account","path":"order"},{"kind":"account","path":"input_token_program"},{"kind":"account","path":"input_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"maker_input_mint_account","writable":true},{"name":"fee","pda":{"seeds":[{"kind":"const","value":[102,101,101]}]}},{"name":"referral","optional":true},{"name":"input_mint"},{"name":"output_mint"},{"name":"input_token_program"},{"name":"output_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"InitializeOrderParams"}}}]},{"name":"pre_flash_fill_order","discriminator":[240,47,153,68,13,190,225,42],"accounts":[{"name":"taker","signer":true},{"name":"order","writable":true},{"name":"input_mint_reserve","writable":true},{"name":"taker_input_mint_account","writable":true},{"name":"input_mint"},{"name":"input_token_program"},{"name":"instruction","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"PreFlashFillOrderParams"}}}]},{"name":"update_fee","discriminator":[232,253,195,247,148,212,73,222],"accounts":[{"name":"admin","writable":true,"signer":true},{"name":"fee_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"UpdateFeeParams"}}}]},{"name":"withdraw_fee","discriminator":[14,122,231,218,31,238,223,150],"accounts":[{"name":"admin","writable":true,"signer":true},{"name":"fee_authority","docs":["CHECK"],"pda":{"seeds":[{"kind":"const","value":[102,101,101]}]}},{"name":"program_fee_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"token_program"},{"name":"mint"}],"args":[]}],"accounts":[{"name":"Fee","discriminator":[24,55,150,250,168,27,101,178]},{"name":"Order","discriminator":[134,173,223,185,77,86,28,51]}],"events":[{"name":"CancelDustOrderEvent","discriminator":[124,190,74,28,177,40,200,220]},{"name":"CancelOrderEvent","discriminator":[174,66,141,17,4,224,162,77]},{"name":"CreateOrderEvent","discriminator":[49,142,72,166,230,29,84,84]},{"name":"TradeEvent","discriminator":[189,219,127,211,78,230,97,238]}],"errors":[{"code":6000,"name":"InvalidMakingAmount"},{"code":6001,"name":"InvalidTakingAmount"},{"code":6002,"name":"InvalidMaxTakingAmount"},{"code":6003,"name":"InvalidInputAccount"},{"code":6004,"name":"InvalidMakerOutputAccount"},{"code":6005,"name":"InvalidTakerOutputAccount"},{"code":6006,"name":"InvalidPair"},{"code":6007,"name":"OrderExpired"},{"code":6008,"name":"OrderNotExpired"},{"code":6009,"name":"InvalidAdmin"},{"code":6010,"name":"InvalidKeeper"},{"code":6011,"name":"MathOverflow"},{"code":6012,"name":"ProgramMismatch"},{"code":6013,"name":"UnknownInstruction"},{"code":6014,"name":"MissingRepayInstructions"},{"code":6015,"name":"InvalidOrder"},{"code":6016,"name":"InvalidBorrowMakingAmount"},{"code":6017,"name":"InvalidExpiredDate"},{"code":6018,"name":"FeeBpsTooLow"},{"code":6019,"name":"FeeBpsTooHigh"},{"code":6020,"name":"TransferTaxMint"},{"code":6021,"name":"InvalidOutputMint"},{"code":6022,"name":"InvalidMinTakingAmount"},{"code":6023,"name":"InvalidSwapData"},{"code":6024,"name":"InvalidSwapInputAmount"},{"code":6025,"name":"InvalidSwappedAmount"},{"code":6026,"name":"InvalidDustOrder"},{"code":6027,"name":"UnsupportedMint"}],"types":[{"name":"CancelDustOrderEvent","type":{"kind":"struct","fields":[{"name":"order_key","type":"pubkey"},{"name":"one_usd_input_mint_amount","type":"u64"}]}},{"name":"CancelDustOrderParams","type":{"kind":"struct","fields":[{"name":"one_usd_input_mint_amount","type":"u64"}]}},{"name":"CancelOrderEvent","type":{"kind":"struct","fields":[{"name":"order_key","type":"pubkey"}]}},{"name":"CreateOrderEvent","type":{"kind":"struct","fields":[{"name":"order_key","type":"pubkey"},{"name":"maker","type":"pubkey"},{"name":"input_mint","type":"pubkey"},{"name":"output_mint","type":"pubkey"},{"name":"input_token_program","type":"pubkey"},{"name":"output_token_program","type":"pubkey"},{"name":"making_amount","type":"u64"},{"name":"taking_amount","type":"u64"},{"name":"expired_at","type":{"option":"i64"}},{"name":"fee_bps","type":"u16"},{"name":"fee_account","type":"pubkey"},{"name":"slippage_bps","type":"u16"}]}},{"name":"Fee","type":{"kind":"struct","fields":[{"name":"normal_fee_bps","type":"u16"},{"name":"stable_fee_bps","type":"u16"}]}},{"name":"FillOrderParams","type":{"kind":"struct","fields":[{"name":"input_amount","type":"u64"},{"name":"swap_data","type":"bytes"}]}},{"name":"FlashFillOrderParams","type":{"kind":"struct","fields":[{"name":"max_taking_amount","type":"u64"}]}},{"name":"InitializeOrderParams","type":{"kind":"struct","fields":[{"name":"unique_id","type":"u64"},{"name":"making_amount","type":"u64"},{"name":"taking_amount","type":"u64"},{"name":"expired_at","type":{"option":"i64"}},{"name":"fee_bps","type":{"option":"u16"}},{"name":"slippage_bps","type":{"option":"u16"}}]}},{"name":"Order","type":{"kind":"struct","fields":[{"name":"maker","type":"pubkey"},{"name":"input_mint","type":"pubkey"},{"name":"output_mint","type":"pubkey"},{"name":"input_token_program","type":"pubkey"},{"name":"output_token_program","type":"pubkey"},{"name":"input_mint_reserve","type":"pubkey"},{"name":"unique_id","type":"u64"},{"name":"ori_making_amount","type":"u64"},{"name":"ori_taking_amount","type":"u64"},{"name":"making_amount","type":"u64"},{"name":"taking_amount","type":"u64"},{"name":"borrow_making_amount","type":"u64"},{"name":"expired_at","type":{"option":"i64"}},{"name":"fee_bps","type":"u16"},{"name":"fee_account","type":"pubkey"},{"name":"created_at","type":"i64"},{"name":"updated_at","type":"i64"},{"name":"bump","type":"u8"},{"name":"slippage_bps","type":"u16"}]}},{"name":"PreFlashFillOrderParams","type":{"kind":"struct","fields":[{"name":"making_amount","type":"u64"}]}},{"name":"TradeEvent","type":{"kind":"struct","fields":[{"name":"order_key","type":"pubkey"},{"name":"taker","type":"pubkey"},{"name":"remaining_making_amount","type":"u64"},{"name":"remaining_taking_amount","type":"u64"},{"name":"making_amount","type":"u64"},{"name":"taking_amount","type":"u64"}]}},{"name":"UpdateFeeParams","type":{"kind":"struct","fields":[{"name":"normal_fee_bps","type":"u16"},{"name":"stable_fee_bps","type":"u16"}]}}]} \ No newline at end of file diff --git a/idls/jupiter_locker.LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn.json b/idls/jupiter_locker.LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn.json new file mode 100644 index 0000000..fbbf423 --- /dev/null +++ b/idls/jupiter_locker.LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn.json @@ -0,0 +1 @@ +{"address":"LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn","metadata":{"name":"locker","version":"0.4.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"cancel_vesting_escrow","docs":["Cancel a vesting escrow.","- The claimable token will be transferred to recipient","- The remaining token will be transferred to the creator","This instruction supports both splToken and token2022","# Arguments","","* ctx - The accounts needed by instruction.","* remaining_accounts_info: additional accounts needed by instruction",""],"discriminator":[217,233,13,3,143,101,53,201],"accounts":[{"name":"escrow","docs":["Escrow."],"writable":true},{"name":"token_mint","docs":["Mint."],"writable":true,"relations":["escrow"]},{"name":"escrow_token","docs":["Escrow Token Account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"escrow"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"creator_token","docs":["Creator Token Account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"escrow"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"recipient_token","docs":["Receipient Token Account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"escrow"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"rent_receiver","docs":["CHECKED: The Token Account will receive the rent"],"writable":true},{"name":"signer","docs":["Signer."],"writable":true,"signer":true},{"name":"memo_program","docs":["Memo program."],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"token_program","docs":["Token program."]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"claim","docs":["Claim maximum amount from the vesting escrow","# Arguments","","* ctx - The accounts needed by instruction.","* max_amount - The maximum amount claimed by the recipient",""],"discriminator":[62,198,214,193,213,159,108,210],"accounts":[{"name":"escrow","docs":["Escrow."],"writable":true},{"name":"escrow_token","docs":["Escrow Token Account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"escrow"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"escrow"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"recipient","docs":["Recipient."],"writable":true,"signer":true,"relations":["escrow"]},{"name":"recipient_token","docs":["Recipient Token Account."],"writable":true},{"name":"token_program","docs":["Token program."],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"max_amount","type":"u64"}]},{"name":"claim_v2","docs":["Claim maximum amount from the vesting escrow","This instruction supports both splToken and token2022","# Arguments","","* ctx - The accounts needed by instruction.","* max_amount - The maximum amount claimed by the recipient","* remaining_accounts_info: additional accounts needed by instruction",""],"discriminator":[229,87,46,162,21,157,231,114],"accounts":[{"name":"escrow","docs":["Escrow."],"writable":true},{"name":"token_mint","docs":["Mint."],"relations":["escrow"]},{"name":"escrow_token","docs":["Escrow Token Account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"escrow"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"recipient","docs":["Recipient."],"writable":true,"signer":true,"relations":["escrow"]},{"name":"recipient_token","docs":["Recipient Token Account."],"writable":true},{"name":"memo_program","docs":["Memo program."],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"token_program","docs":["Token program."]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"max_amount","type":"u64"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"close_vesting_escrow","docs":["Close vesting escrow","- Close vesting escrow and escrow ATA and escrow metadata if recipient already claimed all tokens","- Rent receiver must be escrow's creator","This instruction supports both splToken and token2022","# Arguments","","* ctx - The accounts needed by instruction.","* remaining_accounts_info: additional accounts needed by instruction",""],"discriminator":[221,185,95,135,136,67,252,87],"accounts":[{"name":"escrow","docs":["Escrow."],"writable":true},{"name":"escrow_metadata","writable":true,"pda":{"seeds":[{"kind":"const","value":[101,115,99,114,111,119,95,109,101,116,97,100,97,116,97]},{"kind":"account","path":"escrow"}]}},{"name":"token_mint","docs":["Mint."],"writable":true},{"name":"escrow_token","writable":true},{"name":"creator_token","writable":true},{"name":"creator","docs":["Creator."],"writable":true,"signer":true,"relations":["escrow"]},{"name":"token_program","docs":["Token program."]},{"name":"memo_program","docs":["Memo program."],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"create_root_escrow","docs":["Create root escrow"],"discriminator":[116,212,12,188,77,226,32,201],"accounts":[{"name":"base","signer":true},{"name":"root_escrow","docs":["Root Escrow."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[114,111,111,116,95,101,115,99,114,111,119]},{"kind":"account","path":"base"},{"kind":"account","path":"token_mint"},{"kind":"arg","path":"params.version"}]}},{"name":"token_mint"},{"name":"payer","docs":["payer."],"writable":true,"signer":true},{"name":"creator"},{"name":"system_program","docs":["system program."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"CreateRootEscrowParameters"}}}]},{"name":"create_vesting_escrow","docs":["Create a vesting escrow for the given params","# Arguments","","* ctx - The accounts needed by instruction.","* params - The params needed by instruction.","* vesting_start_time - The creation time of this escrow","* cliff_time - Trade cliff time of the escrow","* frequency - How frequent the claimable amount will be updated","* cliff_unlock_amount - The amount unlocked after cliff time","* amount_per_period - The amount unlocked per vesting period","* number_of_period - The total number of vesting period","* update_recipient_mode - Decide who can update the recipient of the escrow","* cancel_mode - Decide who can cancel the the escrow",""],"discriminator":[23,100,197,94,222,153,38,90],"accounts":[{"name":"base","docs":["Base."],"writable":true,"signer":true},{"name":"escrow","docs":["Escrow."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[101,115,99,114,111,119]},{"kind":"account","path":"base"}]}},{"name":"escrow_token","docs":["Escrow Token Account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"escrow"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"sender_token.mint","account":"TokenAccount"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"sender","docs":["Sender."],"writable":true,"signer":true},{"name":"sender_token","docs":["Sender Token Account."],"writable":true},{"name":"recipient"},{"name":"token_program","docs":["Token program."],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","docs":["system program."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"CreateVestingEscrowParameters"}}}]},{"name":"create_vesting_escrow_from_root","docs":["Crate vesting escrow from root"],"discriminator":[6,238,161,108,252,114,246,91],"accounts":[{"name":"root_escrow","docs":["Root Escrow."],"writable":true},{"name":"base","pda":{"seeds":[{"kind":"const","value":[98,97,115,101]},{"kind":"account","path":"root_escrow"},{"kind":"account","path":"recipient"}]}},{"name":"escrow","docs":["Escrow."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[101,115,99,114,111,119]},{"kind":"account","path":"base"}]}},{"name":"escrow_token","docs":["Escrow Token Account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"escrow"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"root_escrow_token","writable":true,"pda":{"seeds":[{"kind":"account","path":"root_escrow"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_mint","docs":["Mint."],"relations":["root_escrow"]},{"name":"payer","docs":["Rent Payer"],"writable":true,"signer":true},{"name":"recipient"},{"name":"system_program","docs":["system program."],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Token program."]},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"CreateVestingEscrowFromRootParams"}}},{"name":"proof","type":{"vec":{"array":["u8",32]}}},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"create_vesting_escrow_metadata","docs":["Create vesting escrow metadata","# Arguments","","* ctx - The accounts needed by instruction.","* params - The params needed by instruction.","* name - The name of the vesting escrow","* description - The description of the vesting escrow","* creator_email - The email of the creator","* recipient_email - The email of the recipient",""],"discriminator":[93,78,33,103,173,125,70,0],"accounts":[{"name":"escrow","docs":["The [Escrow]."],"writable":true},{"name":"creator","docs":["Creator of the escrow."],"signer":true,"relations":["escrow"]},{"name":"escrow_metadata","docs":["The [ProposalMeta]."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[101,115,99,114,111,119,95,109,101,116,97,100,97,116,97]},{"kind":"account","path":"escrow"}]}},{"name":"payer","docs":["Payer of the [ProposalMeta]."],"writable":true,"signer":true},{"name":"system_program","docs":["system program."],"address":"11111111111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"CreateVestingEscrowMetadataParameters"}}}]},{"name":"create_vesting_escrow_v2","docs":["Create a vesting escrow for the given params","This instruction supports both splToken and token2022","# Arguments","","* ctx - The accounts needed by instruction.","* params - The params needed by instruction.","* vesting_start_time - The creation time of this escrow","* cliff_time - Trade cliff time of the escrow","* frequency - How frequent the claimable amount will be updated","* cliff_unlock_amount - The amount unlocked after cliff time","* amount_per_period - The amount unlocked per vesting period","* number_of_period - The total number of vesting period","* update_recipient_mode - Decide who can update the recipient of the escrow","* cancel_mode - Decide who can cancel the the escrow","* remaining_accounts_info: additional accounts needed by instruction",""],"discriminator":[181,155,104,183,182,128,35,47],"accounts":[{"name":"base","docs":["Base."],"writable":true,"signer":true},{"name":"escrow","docs":["Escrow."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[101,115,99,114,111,119]},{"kind":"account","path":"base"}]}},{"name":"token_mint"},{"name":"escrow_token","docs":["Escrow Token Account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"escrow"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"sender","docs":["Sender."],"writable":true,"signer":true},{"name":"sender_token","docs":["Sender Token Account."],"writable":true},{"name":"recipient"},{"name":"token_program","docs":["Token program."]},{"name":"system_program","docs":["system program."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"CreateVestingEscrowParameters"}}},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"fund_root_escrow","docs":["Fund root escrow"],"discriminator":[251,106,189,200,108,15,144,95],"accounts":[{"name":"root_escrow","docs":["Root Escrow."],"writable":true},{"name":"token_mint","relations":["root_escrow"]},{"name":"root_escrow_token","docs":["Escrow Token Account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"root_escrow"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"payer","docs":["Payer."],"writable":true,"signer":true},{"name":"payer_token","docs":["Payer Token Account."],"writable":true},{"name":"token_program","docs":["Token program."]},{"name":"system_program","docs":["system program."],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"max_amount","type":"u64"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"update_vesting_escrow_recipient","docs":["Update vesting escrow metadata","# Arguments","","* ctx - The accounts needed by instruction.","* new_recipient - The address of the new recipient","* new_recipient_email - The email of the new recipient",""],"discriminator":[26,242,127,255,237,109,47,206],"accounts":[{"name":"escrow","docs":["Escrow."],"writable":true},{"name":"escrow_metadata","docs":["Escrow metadata."],"writable":true,"optional":true},{"name":"signer","docs":["Signer."],"writable":true,"signer":true},{"name":"system_program","docs":["system program."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_recipient","type":"pubkey"},{"name":"new_recipient_email","type":{"option":"string"}}]}],"accounts":[{"name":"RootEscrow","discriminator":[253,209,220,107,206,191,71,158]},{"name":"VestingEscrow","discriminator":[244,119,183,4,73,116,135,195]},{"name":"VestingEscrowMetadata","discriminator":[24,204,166,104,87,158,76,13]}],"events":[{"name":"EventCancelVestingEscrow","discriminator":[113,2,117,173,195,39,101,155]},{"name":"EventCancelVestingEscrowV3","discriminator":[41,143,236,79,116,120,91,143]},{"name":"EventClaim","discriminator":[171,144,1,189,120,200,38,11]},{"name":"EventClaimV3","discriminator":[229,197,142,10,41,122,171,154]},{"name":"EventCloseClaimStatus","discriminator":[87,68,38,194,241,155,125,107]},{"name":"EventCloseVestingEscrow","discriminator":[45,141,253,209,196,133,21,204]},{"name":"EventCreateRootEscrow","discriminator":[105,216,97,182,27,224,199,228]},{"name":"EventCreateVestingEscrow","discriminator":[248,222,89,61,170,208,131,117]},{"name":"EventFundRootEscrow","discriminator":[74,8,68,181,198,235,138,81]},{"name":"EventUpdateVestingEscrowRecipient","discriminator":[206,218,33,65,133,237,131,57]}],"errors":[{"code":6000,"name":"MathOverflow","msg":"Math operation overflow"},{"code":6001,"name":"FrequencyIsZero","msg":"Frequency is zero"},{"code":6002,"name":"InvalidEscrowTokenAddress","msg":"Invalid escrow token address"},{"code":6003,"name":"InvalidUpdateRecipientMode","msg":"Invalid update recipient mode"},{"code":6004,"name":"InvalidCancelMode","msg":"Invalid cancel mode"},{"code":6005,"name":"NotPermitToDoThisAction","msg":"Not permit to do this action"},{"code":6006,"name":"InvalidRecipientTokenAccount","msg":"Invalid recipient token account"},{"code":6007,"name":"InvalidCreatorTokenAccount","msg":"Invalid creator token account"},{"code":6008,"name":"InvalidEscrowMetadata","msg":"Invalid escrow metadata"},{"code":6009,"name":"InvalidVestingStartTime","msg":"Invalid vesting start time"},{"code":6010,"name":"AlreadyCancelled","msg":"Already cancelled"},{"code":6011,"name":"CancelledAtIsZero","msg":"Cancelled timestamp is zero"},{"code":6012,"name":"IncorrectTokenProgramId","msg":"Invalid token program ID"},{"code":6013,"name":"TransferFeeCalculationFailure","msg":"Calculate transfer fee failure"},{"code":6014,"name":"UnsupportedMint","msg":"Unsupported mint"},{"code":6015,"name":"InvalidRemainingAccountSlice","msg":"Invalid remaining accounts"},{"code":6016,"name":"InsufficientRemainingAccounts","msg":"Insufficient remaining accounts"},{"code":6017,"name":"DuplicatedRemainingAccountTypes","msg":"Same accounts type is provided more than once"},{"code":6018,"name":"NoTransferHookProgram","msg":"Missing remaining accounts for transfer hook."},{"code":6019,"name":"ClaimingIsNotFinished","msg":"Claiming is not finished"},{"code":6020,"name":"InvalidMerkleProof","msg":"Invalid merkle proof"},{"code":6021,"name":"EscrowNotCancelled","msg":"Escrow is not cancelled"},{"code":6022,"name":"AmountIsZero","msg":"Amount is zero"},{"code":6023,"name":"InvalidParams","msg":"Invalid params"}],"types":[{"name":"AccountsType","type":{"kind":"enum","variants":[{"name":"TransferHookEscrow"}]}},{"name":"CreateRootEscrowParameters","type":{"kind":"struct","fields":[{"name":"max_claim_amount","type":"u64"},{"name":"max_escrow","type":"u64"},{"name":"version","type":"u64"},{"name":"root","type":{"array":["u8",32]}}]}},{"name":"CreateVestingEscrowFromRootParams","type":{"kind":"struct","fields":[{"name":"vesting_start_time","type":"u64"},{"name":"cliff_time","type":"u64"},{"name":"frequency","type":"u64"},{"name":"cliff_unlock_amount","type":"u64"},{"name":"amount_per_period","type":"u64"},{"name":"number_of_period","type":"u64"},{"name":"update_recipient_mode","type":"u8"},{"name":"cancel_mode","type":"u8"}]}},{"name":"CreateVestingEscrowMetadataParameters","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"description","type":"string"},{"name":"creator_email","type":"string"},{"name":"recipient_email","type":"string"}]}},{"name":"CreateVestingEscrowParameters","docs":["Accounts for [locker::create_vesting_escrow]."],"type":{"kind":"struct","fields":[{"name":"vesting_start_time","type":"u64"},{"name":"cliff_time","type":"u64"},{"name":"frequency","type":"u64"},{"name":"cliff_unlock_amount","type":"u64"},{"name":"amount_per_period","type":"u64"},{"name":"number_of_period","type":"u64"},{"name":"update_recipient_mode","type":"u8"},{"name":"cancel_mode","type":"u8"}]}},{"name":"EventCancelVestingEscrow","type":{"kind":"struct","fields":[{"name":"escrow","type":"pubkey"},{"name":"signer","type":"pubkey"},{"name":"claimable_amount","type":"u64"},{"name":"remaining_amount","type":"u64"},{"name":"cancelled_at","type":"u64"}]}},{"name":"EventCancelVestingEscrowV3","type":{"kind":"struct","fields":[{"name":"escrow","type":"pubkey"},{"name":"signer","type":"pubkey"},{"name":"remaining_amount","type":"u64"},{"name":"cancelled_at","type":"u64"}]}},{"name":"EventClaim","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"current_ts","type":"u64"},{"name":"escrow","type":"pubkey"}]}},{"name":"EventClaimV3","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"current_ts","type":"u64"},{"name":"escrow","type":"pubkey"},{"name":"vesting_start_time","type":"u64"},{"name":"cliff_time","type":"u64"},{"name":"frequency","type":"u64"},{"name":"cliff_unlock_amount","type":"u64"},{"name":"amount_per_period","type":"u64"},{"name":"number_of_period","type":"u64"},{"name":"recipient","type":"pubkey"}]}},{"name":"EventCloseClaimStatus","type":{"kind":"struct","fields":[{"name":"escrow","type":"pubkey"},{"name":"recipient","type":"pubkey"},{"name":"rent_receiver","type":"pubkey"}]}},{"name":"EventCloseVestingEscrow","type":{"kind":"struct","fields":[{"name":"escrow","type":"pubkey"}]}},{"name":"EventCreateRootEscrow","type":{"kind":"struct","fields":[{"name":"root_escrow","type":"pubkey"},{"name":"max_claim_amount","type":"u64"},{"name":"max_escrow","type":"u64"},{"name":"version","type":"u64"},{"name":"root","type":{"array":["u8",32]}}]}},{"name":"EventCreateVestingEscrow","type":{"kind":"struct","fields":[{"name":"vesting_start_time","type":"u64"},{"name":"cliff_time","type":"u64"},{"name":"frequency","type":"u64"},{"name":"cliff_unlock_amount","type":"u64"},{"name":"amount_per_period","type":"u64"},{"name":"number_of_period","type":"u64"},{"name":"update_recipient_mode","type":"u8"},{"name":"cancel_mode","type":"u8"},{"name":"recipient","type":"pubkey"},{"name":"escrow","type":"pubkey"}]}},{"name":"EventFundRootEscrow","type":{"kind":"struct","fields":[{"name":"root_escrow","type":"pubkey"},{"name":"funded_amount","type":"u64"}]}},{"name":"EventUpdateVestingEscrowRecipient","type":{"kind":"struct","fields":[{"name":"escrow","type":"pubkey"},{"name":"old_recipient","type":"pubkey"},{"name":"new_recipient","type":"pubkey"},{"name":"signer","type":"pubkey"}]}},{"name":"RemainingAccountsInfo","type":{"kind":"struct","fields":[{"name":"slices","type":{"vec":{"defined":{"name":"RemainingAccountsSlice"}}}}]}},{"name":"RemainingAccountsSlice","type":{"kind":"struct","fields":[{"name":"accounts_type","type":{"defined":{"name":"AccountsType"}}},{"name":"length","type":"u8"}]}},{"name":"RootEscrow","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"token_mint","docs":["token mint"],"type":"pubkey"},{"name":"creator","docs":["creator of the escrow"],"type":"pubkey"},{"name":"base","docs":["escrow base key"],"type":"pubkey"},{"name":"root","docs":["256 bit merkle root"],"type":{"array":["u8",32]}},{"name":"bump","docs":["bump"],"type":"u8"},{"name":"token_program_flag","docs":["token program flag"],"type":"u8"},{"name":"padding_0","docs":["padding"],"type":{"array":["u8",6]}},{"name":"max_claim_amount","docs":["max claim amount"],"type":"u64"},{"name":"max_escrow","docs":["max escrow"],"type":"u64"},{"name":"total_funded_amount","docs":["total funded amount"],"type":"u64"},{"name":"total_escrow_created","docs":["total escrow created"],"type":"u64"},{"name":"total_distribute_amount","docs":["total distributed amount"],"type":"u64"},{"name":"version","docs":["version"],"type":"u64"},{"name":"padding","docs":["padding"],"type":"u64"},{"name":"buffer","docs":["buffer"],"type":{"array":["u128",5]}}]}},{"name":"VestingEscrow","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"recipient","docs":["recipient address"],"type":"pubkey"},{"name":"token_mint","docs":["token mint"],"type":"pubkey"},{"name":"creator","docs":["creator of the escrow"],"type":"pubkey"},{"name":"base","docs":["escrow base key"],"type":"pubkey"},{"name":"escrow_bump","docs":["escrow bump"],"type":"u8"},{"name":"update_recipient_mode","docs":["update_recipient_mode"],"type":"u8"},{"name":"cancel_mode","docs":["cancel_mode"],"type":"u8"},{"name":"token_program_flag","docs":["token program flag"],"type":"u8"},{"name":"padding_0","docs":["padding"],"type":{"array":["u8",4]}},{"name":"cliff_time","docs":["cliff time"],"type":"u64"},{"name":"frequency","docs":["frequency"],"type":"u64"},{"name":"cliff_unlock_amount","docs":["cliff unlock amount"],"type":"u64"},{"name":"amount_per_period","docs":["amount per period"],"type":"u64"},{"name":"number_of_period","docs":["number of period"],"type":"u64"},{"name":"total_claimed_amount","docs":["total claimed amount"],"type":"u64"},{"name":"vesting_start_time","docs":["vesting start time"],"type":"u64"},{"name":"cancelled_at","docs":["cancelled_at"],"type":"u64"},{"name":"padding_1","docs":["buffer"],"type":"u64"},{"name":"buffer","docs":["buffer"],"type":{"array":["u128",5]}}]}},{"name":"VestingEscrowMetadata","docs":["Metadata about an escrow."],"type":{"kind":"struct","fields":[{"name":"escrow","docs":["The [Escrow]."],"type":"pubkey"},{"name":"name","docs":["Name of escrow."],"type":"string"},{"name":"description","docs":["Description of escrow."],"type":"string"},{"name":"creator_email","docs":["Email of creator"],"type":"string"},{"name":"recipient_email","docs":["Email of recipient"],"type":"string"}]}}]} \ No newline at end of file diff --git a/idls/jupiter_perpetuals.PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu.json b/idls/jupiter_perpetuals.PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu.json new file mode 100644 index 0000000..f6b69b0 --- /dev/null +++ b/idls/jupiter_perpetuals.PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"perpetuals","instructions":[{"name":"init","accounts":[{"name":"upgradeAuthority","isMut":true,"isSigner":true},{"name":"admin","isMut":false,"isSigner":false},{"name":"transferAuthority","isMut":true,"isSigner":false},{"name":"perpetuals","isMut":true,"isSigner":false},{"name":"perpetualsProgram","isMut":false,"isSigner":false},{"name":"perpetualsProgramData","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitParams"}}]},{"name":"addPool","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":true,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"AddPoolParams"}}]},{"name":"addCustody","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"custodyTokenMint","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"AddCustodyParams"}}]},{"name":"setCustodyConfig","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetCustodyConfigParams"}}]},{"name":"setPoolConfig","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetPoolConfigParams"}}]},{"name":"setPerpetualsConfig","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetPerpetualsConfigParams"}}]},{"name":"transferAdmin","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"newAdmin","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"TransferAdminParams"}}]},{"name":"withdrawFees2","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"receivingTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"WithdrawFees2Params"}}]},{"name":"createTokenMetadata","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":false,"isSigner":false},{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"CreateTokenMetadataParams"}}]},{"name":"createTokenLedger","accounts":[{"name":"tokenLedger","isMut":true,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"reallocCustody","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"custody","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"reallocPool","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"pool","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"createAndDelegateStakeAccount","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeInfo","isMut":true,"isSigner":false},{"name":"validatorVoteAccount","isMut":false,"isSigner":false},{"name":"stakeConfig","isMut":false,"isSigner":false},{"name":"wsolMint","isMut":false,"isSigner":false},{"name":"tempWsolAccount","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"stakeHistory","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"CreateAndDelegateStakeAccountParams"}}]},{"name":"unstake","accounts":[{"name":"operator","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeInfo","isMut":true,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"withdrawStake","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeInfo","isMut":true,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"stakeHistory","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"redeemStake","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":false,"isSigner":false},{"name":"stakeInfo","isMut":true,"isSigner":false}],"args":[]},{"name":"operatorSetCustodyConfig","accounts":[{"name":"operator","isMut":false,"isSigner":true},{"name":"custody","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"OperatorSetCustodyConfigParams"}}]},{"name":"operatorSetPoolConfig","accounts":[{"name":"operator","isMut":false,"isSigner":true},{"name":"pool","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"OperatorSetPoolConfigParams"}}]},{"name":"testInit","accounts":[{"name":"upgradeAuthority","isMut":true,"isSigner":true},{"name":"admin","isMut":false,"isSigner":false},{"name":"transferAuthority","isMut":true,"isSigner":false},{"name":"perpetuals","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"TestInitParams"}}]},{"name":"setTestTime","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetTestTimeParams"}}]},{"name":"setTokenLedger","accounts":[{"name":"tokenLedger","isMut":true,"isSigner":false},{"name":"tokenAccount","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"swap2","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"fundingAccount","isMut":true,"isSigner":false},{"name":"receivingAccount","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"receivingCustody","isMut":true,"isSigner":false},{"name":"receivingCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"receivingCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"receivingCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"dispensingCustody","isMut":true,"isSigner":false},{"name":"dispensingCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"dispensingCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"dispensingCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"Swap2Params"}}]},{"name":"swapWithTokenLedger","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"fundingAccount","isMut":true,"isSigner":false},{"name":"receivingAccount","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"receivingCustody","isMut":true,"isSigner":false},{"name":"receivingCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"receivingCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"dispensingCustody","isMut":true,"isSigner":false},{"name":"dispensingCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"dispensingCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenLedger","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instruction","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SwapWithTokenLedgerParams"}}]},{"name":"instantIncreasePositionPreSwap","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"fundingAccount","isMut":true,"isSigner":false},{"name":"receivingAccount","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"receivingCustody","isMut":true,"isSigner":false},{"name":"receivingCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"receivingCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"dispensingCustody","isMut":true,"isSigner":false},{"name":"dispensingCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"dispensingCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instruction","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InstantIncreasePositionPreSwapParams"}}]},{"name":"addLiquidity2","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"fundingAccount","isMut":true,"isSigner":false},{"name":"lpTokenAccount","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"AddLiquidity2Params"}}]},{"name":"removeLiquidity2","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"receivingAccount","isMut":true,"isSigner":false},{"name":"lpTokenAccount","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"RemoveLiquidity2Params"}}]},{"name":"createIncreasePositionMarketRequest","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"fundingAccount","isMut":true,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":false,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"referral","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"CreateIncreasePositionMarketRequestParams"}}]},{"name":"createDecreasePositionRequest2","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"receivingAccount","isMut":true,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":false,"isSigner":false},{"name":"desiredMint","isMut":false,"isSigner":false},{"name":"referral","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"CreateDecreasePositionRequest2Params"}}]},{"name":"createDecreasePositionMarketRequest","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"receivingAccount","isMut":true,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":false,"isSigner":false},{"name":"desiredMint","isMut":false,"isSigner":false},{"name":"referral","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"CreateDecreasePositionMarketRequestParams"}}]},{"name":"updateDecreasePositionRequest2","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"UpdateDecreasePositionRequest2Params"}}]},{"name":"closePositionRequest2","accounts":[{"name":"keeper","isMut":true,"isSigner":true,"isOptional":true},{"name":"owner","isMut":true,"isSigner":false},{"name":"ownerAta","isMut":true,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"closePositionRequest3","accounts":[{"name":"keeper","isMut":true,"isSigner":true,"isOptional":true},{"name":"owner","isMut":true,"isSigner":false},{"name":"ownerAta","isMut":true,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"increasePosition4","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"IncreasePosition4Params"}}]},{"name":"increasePositionPreSwap","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"keeperAta","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":false,"isSigner":false},{"name":"instruction","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"IncreasePositionPreSwapParams"}}]},{"name":"increasePositionWithInternalSwap","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"receivingCustody","isMut":true,"isSigner":false},{"name":"receivingCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"receivingCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"receivingCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"IncreasePositionWithInternalSwapParams"}}]},{"name":"decreasePosition4","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"owner","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"DecreasePosition4Params"}}]},{"name":"decreasePositionWithInternalSwap","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"owner","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"dispensingCustody","isMut":true,"isSigner":false},{"name":"dispensingCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"dispensingCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"dispensingCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"DecreasePositionWithInternalSwapParams"}}]},{"name":"decreasePositionWithTpsl","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"owner","isMut":false,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"DecreasePositionWithTpslParams"}}]},{"name":"decreasePositionWithTpslAndInternalSwap","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"owner","isMut":false,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"dispensingCustody","isMut":true,"isSigner":false},{"name":"dispensingCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"dispensingCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"DecreasePositionWithTpslAndInternalSwapParams"}}]},{"name":"liquidateFullPosition4","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"LiquidateFullPosition4Params"}}]},{"name":"refreshAssetsUnderManagement","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"RefreshAssetsUnderManagementParams"}}]},{"name":"setMaxGlobalSizes","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"custody","isMut":true,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetMaxGlobalSizesParams"}}]},{"name":"instantCreateTpsl","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"apiKeeper","isMut":false,"isSigner":true},{"name":"owner","isMut":true,"isSigner":true},{"name":"receivingAccount","isMut":true,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":false,"isSigner":false},{"name":"desiredMint","isMut":false,"isSigner":false},{"name":"referral","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InstantCreateTpslParams"}}]},{"name":"instantCreateLimitOrder","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"apiKeeper","isMut":false,"isSigner":true},{"name":"owner","isMut":true,"isSigner":true},{"name":"fundingAccount","isMut":true,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":false,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"referral","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InstantCreateLimitOrderParams"}}]},{"name":"instantIncreasePosition","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"apiKeeper","isMut":false,"isSigner":true},{"name":"owner","isMut":true,"isSigner":true},{"name":"fundingAccount","isMut":true,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenLedger","isMut":false,"isSigner":false,"isOptional":true},{"name":"referral","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InstantIncreasePositionParams"}}]},{"name":"instantDecreasePosition","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"apiKeeper","isMut":false,"isSigner":true},{"name":"owner","isMut":true,"isSigner":true},{"name":"receivingAccount","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"desiredMint","isMut":false,"isSigner":false},{"name":"referral","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InstantDecreasePositionParams"}}]},{"name":"instantDecreasePosition2","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"apiKeeper","isMut":false,"isSigner":true},{"name":"owner","isMut":true,"isSigner":true},{"name":"receivingAccount","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"desiredMint","isMut":false,"isSigner":false},{"name":"referral","isMut":false,"isSigner":false,"isOptional":true},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InstantDecreasePosition2Params"}}]},{"name":"instantUpdateLimitOrder","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"apiKeeper","isMut":false,"isSigner":true},{"name":"owner","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InstantUpdateLimitOrderParams"}}]},{"name":"instantUpdateTpsl","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"apiKeeper","isMut":false,"isSigner":true},{"name":"owner","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InstantUpdateTpslParams"}}]},{"name":"getAddLiquidityAmountAndFee2","accounts":[{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"lpTokenMint","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"GetAddLiquidityAmountAndFee2Params"}}],"returns":{"defined":"AmountAndFee"}},{"name":"getRemoveLiquidityAmountAndFee2","accounts":[{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"lpTokenMint","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"GetRemoveLiquidityAmountAndFee2Params"}}],"returns":{"defined":"AmountAndFee"}},{"name":"getAssetsUnderManagement2","accounts":[{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"GetAssetsUnderManagement2Params"}}],"returns":"u128"},{"name":"borrowFromCustody","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"borrowPosition","isMut":true,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"BorrowFromCustodyParams"}}]},{"name":"repayToCustody","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"borrowPosition","isMut":true,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"RepayToCustodyParams"}}]},{"name":"depositCollateralForBorrows","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"borrowPosition","isMut":true,"isSigner":false},{"name":"collateralTokenAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"DepositParams"}}]},{"name":"withdrawCollateralForBorrows","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"borrowPosition","isMut":true,"isSigner":false},{"name":"collateralTokenAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"WithdrawParams"}}]},{"name":"liquidateBorrowPosition","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"borrowPosition","isMut":true,"isSigner":false},{"name":"collateralTokenAccount","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"LiquidateBorrowPositionParams"}}]},{"name":"partialLiquidateBorrowPosition","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"borrowPosition","isMut":true,"isSigner":false},{"name":"collateralTokenAccount","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"PartialLiquidateBorrowPositionParams"}}]},{"name":"closeBorrowPosition","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"borrowPosition","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"CloseBorrowPositionParams"}}]}],"accounts":[{"name":"BorrowPosition","type":{"kind":"struct","fields":[{"name":"owner","type":"publicKey"},{"name":"pool","type":"publicKey"},{"name":"custody","type":"publicKey"},{"name":"openTime","type":"i64"},{"name":"updateTime","type":"i64"},{"name":"borrowSize","type":"u128"},{"name":"cumulativeCompoundedInterestSnapshot","type":"u128"},{"name":"lockedCollateral","type":"u64"},{"name":"bump","type":"u8"},{"name":"lastBorrowed","type":"i64"}]}},{"name":"Custody","type":{"kind":"struct","fields":[{"name":"pool","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"tokenAccount","type":"publicKey"},{"name":"decimals","type":"u8"},{"name":"isStable","type":"bool"},{"name":"oracle","type":{"defined":"OracleParams"}},{"name":"pricing","type":{"defined":"PricingParams"}},{"name":"permissions","type":{"defined":"Permissions"}},{"name":"targetRatioBps","type":"u64"},{"name":"assets","type":{"defined":"Assets"}},{"name":"fundingRateState","type":{"defined":"FundingRateState"}},{"name":"bump","type":"u8"},{"name":"tokenAccountBump","type":"u8"},{"name":"increasePositionBps","type":"u64"},{"name":"decreasePositionBps","type":"u64"},{"name":"maxPositionSizeUsd","type":"u64"},{"name":"dovesOracle","type":"publicKey"},{"name":"jumpRateState","type":{"defined":"JumpRateState"}},{"name":"dovesAgOracle","type":"publicKey"},{"name":"priceImpactBuffer","type":{"defined":"PriceImpactBuffer"}},{"name":"borrowLendParameters","type":{"defined":"BorrowLendParams"}},{"name":"borrowsFundingRateState","type":{"defined":"FundingRateState"}},{"name":"debt","type":"u128"},{"name":"borrowLendInterestsAccured","type":"u128"},{"name":"borrowLimitInTokenAmount","type":"u64"},{"name":"minInterestFeeBps","type":"u64"},{"name":"minInterestFeeGracePeriodSeconds","type":"u64"},{"name":"totalStakedAmountLamports","type":"u64"},{"name":"maxTotalStakedAmountLamports","type":"u64"},{"name":"externalSwapFeeMultiplierBps","type":"u64"},{"name":"disableClosePositionRequest","type":"bool"},{"name":"withdrawalLimitTokenAmount","type":"u64"},{"name":"withdrawalTokenAmountAccumulated","type":"u64"},{"name":"withdrawalLimitLastResetAt","type":"i64"},{"name":"withdrawalLimitIntervalSeconds","type":"u64"}]}},{"name":"Perpetuals","type":{"kind":"struct","fields":[{"name":"permissions","type":{"defined":"Permissions"}},{"name":"pools","type":{"vec":"publicKey"}},{"name":"admin","type":"publicKey"},{"name":"transferAuthorityBump","type":"u8"},{"name":"perpetualsBump","type":"u8"},{"name":"inceptionTime","type":"i64"}]}},{"name":"Pool","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"custodies","type":{"vec":"publicKey"}},{"name":"aumUsd","type":"u128"},{"name":"limit","type":{"defined":"Limit"}},{"name":"fees","type":{"defined":"Fees"}},{"name":"poolApr","type":{"defined":"PoolApr"}},{"name":"maxRequestExecutionSec","type":"i64"},{"name":"bump","type":"u8"},{"name":"lpTokenBump","type":"u8"},{"name":"inceptionTime","type":"i64"},{"name":"parameterUpdateOracle","type":{"defined":"Secp256k1Pubkey"}},{"name":"aumUsdUpdatedAt","type":"i64"},{"name":"maxTriggerPriceDiffBps","type":"u64"},{"name":"disableClosePositionRequest","type":"bool"},{"name":"maxLpTokenPriceChangeBps","type":"u64"},{"name":"aumUsdRefreshedAtSlot","type":"u64"}]}},{"name":"PositionRequest","type":{"kind":"struct","fields":[{"name":"owner","type":"publicKey"},{"name":"pool","type":"publicKey"},{"name":"custody","type":"publicKey"},{"name":"position","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"openTime","type":"i64"},{"name":"updateTime","type":"i64"},{"name":"sizeUsdDelta","type":"u64"},{"name":"collateralDelta","type":"u64"},{"name":"requestChange","type":{"defined":"RequestChange"}},{"name":"requestType","type":{"defined":"RequestType"}},{"name":"side","type":{"defined":"Side"}},{"name":"priceSlippage","type":{"option":"u64"}},{"name":"jupiterMinimumOut","type":{"option":"u64"}},{"name":"preSwapAmount","type":{"option":"u64"}},{"name":"triggerPrice","type":{"option":"u64"}},{"name":"triggerAboveThreshold","type":{"option":"bool"}},{"name":"entirePosition","type":{"option":"bool"}},{"name":"executed","type":"bool"},{"name":"counter","type":"u64"},{"name":"bump","type":"u8"},{"name":"referral","type":{"option":"publicKey"}}]}},{"name":"Position","type":{"kind":"struct","fields":[{"name":"owner","type":"publicKey"},{"name":"pool","type":"publicKey"},{"name":"custody","type":"publicKey"},{"name":"collateralCustody","type":"publicKey"},{"name":"openTime","type":"i64"},{"name":"updateTime","type":"i64"},{"name":"side","type":{"defined":"Side"}},{"name":"price","type":"u64"},{"name":"sizeUsd","type":"u64"},{"name":"collateralUsd","type":"u64"},{"name":"realisedPnlUsd","type":"i64"},{"name":"cumulativeInterestSnapshot","type":"u128"},{"name":"lockedAmount","type":"u64"},{"name":"bump","type":"u8"}]}},{"name":"StakeInfo","type":{"kind":"struct","fields":[{"name":"pool","type":"publicKey"},{"name":"stakeAccount","type":"publicKey"},{"name":"currentStakedAmountLamports","type":"u64"},{"name":"totalStakingRewardsLamports","type":"u64"},{"name":"lastUpdatedAt","type":"i64"},{"name":"deactivating","type":"bool"},{"name":"stakeAccountIndex","type":"u64"},{"name":"bump","type":"u8"}]}},{"name":"TokenLedger","type":{"kind":"struct","fields":[{"name":"tokenAccount","type":"publicKey"},{"name":"amount","type":"u64"}]}}],"types":[{"name":"AddCustodyParams","type":{"kind":"struct","fields":[{"name":"isStable","type":"bool"},{"name":"oracle","type":{"defined":"OracleParams"}},{"name":"pricing","type":{"defined":"PricingParams"}},{"name":"permissions","type":{"defined":"Permissions"}},{"name":"hourlyFundingDbps","type":"u64"},{"name":"targetRatioBps","type":"u64"},{"name":"increasePositionBps","type":"u64"},{"name":"decreasePositionBps","type":"u64"},{"name":"dovesOracle","type":"publicKey"},{"name":"maxPositionSizeUsd","type":"u64"},{"name":"jumpRate","type":{"defined":"JumpRateState"}},{"name":"priceImpactFeeFactor","type":"u64"},{"name":"priceImpactExponent","type":"f32"},{"name":"deltaImbalanceThresholdDecimal","type":"u64"},{"name":"maxFeeBps","type":"u64"},{"name":"dovesAgOracle","type":"publicKey"}]}},{"name":"AddLiquidity2Params","type":{"kind":"struct","fields":[{"name":"tokenAmountIn","type":"u64"},{"name":"minLpAmountOut","type":"u64"},{"name":"tokenAmountPreSwap","type":{"option":"u64"}}]}},{"name":"AddPoolParams","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"limit","type":{"defined":"Limit"}},{"name":"fees","type":{"defined":"Fees"}},{"name":"maxRequestExecutionSec","type":"i64"}]}},{"name":"BorrowFromCustodyParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"CloseBorrowPositionParams","type":{"kind":"struct","fields":[]}},{"name":"CreateAndDelegateStakeAccountParams","type":{"kind":"struct","fields":[{"name":"stakeAccountIndex","type":"u64"},{"name":"stakeAmountLamports","type":"u64"}]}},{"name":"CreateDecreasePositionMarketRequestParams","type":{"kind":"struct","fields":[{"name":"collateralUsdDelta","type":"u64"},{"name":"sizeUsdDelta","type":"u64"},{"name":"priceSlippage","type":"u64"},{"name":"jupiterMinimumOut","type":{"option":"u64"}},{"name":"entirePosition","type":{"option":"bool"}},{"name":"counter","type":"u64"}]}},{"name":"CreateDecreasePositionRequest2Params","type":{"kind":"struct","fields":[{"name":"collateralUsdDelta","type":"u64"},{"name":"sizeUsdDelta","type":"u64"},{"name":"requestType","type":{"defined":"RequestType"}},{"name":"priceSlippage","type":{"option":"u64"}},{"name":"jupiterMinimumOut","type":{"option":"u64"}},{"name":"triggerPrice","type":{"option":"u64"}},{"name":"triggerAboveThreshold","type":{"option":"bool"}},{"name":"entirePosition","type":{"option":"bool"}},{"name":"counter","type":"u64"}]}},{"name":"CreateIncreasePositionMarketRequestParams","type":{"kind":"struct","fields":[{"name":"sizeUsdDelta","type":"u64"},{"name":"collateralTokenDelta","type":"u64"},{"name":"side","type":{"defined":"Side"}},{"name":"priceSlippage","type":"u64"},{"name":"jupiterMinimumOut","type":{"option":"u64"}},{"name":"counter","type":"u64"}]}},{"name":"CreateTokenMetadataParams","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]}},{"name":"DecreasePosition4Params","type":{"kind":"struct","fields":[]}},{"name":"DecreasePositionWithInternalSwapParams","type":{"kind":"struct","fields":[]}},{"name":"DecreasePositionWithTpslAndInternalSwapParams","type":{"kind":"struct","fields":[]}},{"name":"DecreasePositionWithTpslParams","type":{"kind":"struct","fields":[]}},{"name":"DepositParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"GetAddLiquidityAmountAndFee2Params","type":{"kind":"struct","fields":[{"name":"tokenAmountIn","type":"u64"}]}},{"name":"GetAssetsUnderManagement2Params","type":{"kind":"struct","fields":[{"name":"mode","type":{"option":{"defined":"PriceCalcMode"}}}]}},{"name":"GetRemoveLiquidityAmountAndFee2Params","type":{"kind":"struct","fields":[{"name":"lpAmountIn","type":"u64"}]}},{"name":"IncreasePosition4Params","type":{"kind":"struct","fields":[]}},{"name":"IncreasePositionPreSwapParams","type":{"kind":"struct","fields":[]}},{"name":"IncreasePositionWithInternalSwapParams","type":{"kind":"struct","fields":[]}},{"name":"InitParams","type":{"kind":"struct","fields":[{"name":"allowSwap","type":"bool"},{"name":"allowAddLiquidity","type":"bool"},{"name":"allowRemoveLiquidity","type":"bool"},{"name":"allowIncreasePosition","type":"bool"},{"name":"allowDecreasePosition","type":"bool"},{"name":"allowCollateralWithdrawal","type":"bool"},{"name":"allowLiquidatePosition","type":"bool"}]}},{"name":"InstantCreateLimitOrderParams","type":{"kind":"struct","fields":[{"name":"sizeUsdDelta","type":"u64"},{"name":"collateralTokenDelta","type":"u64"},{"name":"side","type":{"defined":"Side"}},{"name":"triggerPrice","type":"u64"},{"name":"triggerAboveThreshold","type":"bool"},{"name":"counter","type":"u64"},{"name":"requestTime","type":"i64"}]}},{"name":"InstantCreateTpslParams","type":{"kind":"struct","fields":[{"name":"collateralUsdDelta","type":"u64"},{"name":"sizeUsdDelta","type":"u64"},{"name":"triggerPrice","type":"u64"},{"name":"triggerAboveThreshold","type":"bool"},{"name":"entirePosition","type":"bool"},{"name":"counter","type":"u64"},{"name":"requestTime","type":"i64"}]}},{"name":"InstantDecreasePosition2Params","type":{"kind":"struct","fields":[{"name":"collateralUsdDelta","type":"u64"},{"name":"sizeUsdDelta","type":"u64"},{"name":"priceSlippage","type":"u64"},{"name":"entirePosition","type":{"option":"bool"}},{"name":"requestTime","type":"i64"},{"name":"counter","type":"u64"}]}},{"name":"InstantDecreasePositionParams","type":{"kind":"struct","fields":[{"name":"collateralUsdDelta","type":"u64"},{"name":"sizeUsdDelta","type":"u64"},{"name":"priceSlippage","type":"u64"},{"name":"entirePosition","type":{"option":"bool"}},{"name":"requestTime","type":"i64"}]}},{"name":"InstantIncreasePositionPreSwapParams","type":{"kind":"struct","fields":[{"name":"amountIn","type":"u64"},{"name":"minAmountOut","type":"u64"}]}},{"name":"InstantIncreasePositionParams","type":{"kind":"struct","fields":[{"name":"sizeUsdDelta","type":"u64"},{"name":"collateralTokenDelta","type":{"option":"u64"}},{"name":"side","type":{"defined":"Side"}},{"name":"priceSlippage","type":"u64"},{"name":"requestTime","type":"i64"}]}},{"name":"InstantUpdateLimitOrderParams","type":{"kind":"struct","fields":[{"name":"sizeUsdDelta","type":"u64"},{"name":"triggerPrice","type":"u64"},{"name":"requestTime","type":"i64"}]}},{"name":"InstantUpdateTpslParams","type":{"kind":"struct","fields":[{"name":"sizeUsdDelta","type":"u64"},{"name":"triggerPrice","type":"u64"},{"name":"requestTime","type":"i64"}]}},{"name":"LiquidateBorrowPositionParams","type":{"kind":"struct","fields":[]}},{"name":"LiquidateFullPosition4Params","type":{"kind":"struct","fields":[]}},{"name":"OperatorSetCustodyConfigParams","type":{"kind":"struct","fields":[{"name":"pricing","type":{"defined":"PricingParams"}},{"name":"hourlyFundingDbps","type":"u64"},{"name":"targetRatioBps","type":"u64"},{"name":"increasePositionBps","type":"u64"},{"name":"decreasePositionBps","type":"u64"},{"name":"maxPositionSizeUsd","type":"u64"},{"name":"jumpRate","type":{"defined":"JumpRateState"}},{"name":"priceImpactFeeFactor","type":"u64"},{"name":"priceImpactExponent","type":"f32"},{"name":"deltaImbalanceThresholdDecimal","type":"u64"},{"name":"maxFeeBps","type":"u64"},{"name":"borrowLendParameters","type":{"defined":"BorrowLendParams"}},{"name":"borrowHourlyFundingDbps","type":"u64"},{"name":"borrowLimitInTokenAmount","type":"u64"},{"name":"minInterestFeeBps","type":"u64"},{"name":"minInterestFeeGracePeriodSeconds","type":"u64"},{"name":"maxTotalStakedAmountLamports","type":"u64"},{"name":"externalSwapFeeMultiplierBps","type":"u64"},{"name":"disableClosePositionRequest","type":"bool"},{"name":"withdrawalLimitTokenAmount","type":"u64"},{"name":"withdrawalLimitIntervalSeconds","type":"u64"}]}},{"name":"OperatorSetPoolConfigParams","type":{"kind":"struct","fields":[{"name":"fees","type":{"defined":"Fees"}},{"name":"limit","type":{"defined":"Limit"}},{"name":"maxRequestExecutionSec","type":"i64"},{"name":"maxTriggerPriceDiffBps","type":"u64"},{"name":"disableClosePositionRequest","type":"bool"},{"name":"maxLpTokenPriceChangeBps","type":"u64"}]}},{"name":"PartialLiquidateBorrowPositionParams","type":{"kind":"struct","fields":[]}},{"name":"RefreshAssetsUnderManagementParams","type":{"kind":"struct","fields":[]}},{"name":"RemoveLiquidity2Params","type":{"kind":"struct","fields":[{"name":"lpAmountIn","type":"u64"},{"name":"minAmountOut","type":"u64"}]}},{"name":"RepayToCustodyParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"SetCustodyConfigParams","type":{"kind":"struct","fields":[{"name":"oracle","type":{"defined":"OracleParams"}},{"name":"pricing","type":{"defined":"PricingParams"}},{"name":"permissions","type":{"defined":"Permissions"}},{"name":"hourlyFundingDbps","type":"u64"},{"name":"targetRatioBps","type":"u64"},{"name":"increasePositionBps","type":"u64"},{"name":"decreasePositionBps","type":"u64"},{"name":"dovesOracle","type":"publicKey"},{"name":"maxPositionSizeUsd","type":"u64"},{"name":"jumpRate","type":{"defined":"JumpRateState"}},{"name":"priceImpactFeeFactor","type":"u64"},{"name":"priceImpactExponent","type":"f32"},{"name":"deltaImbalanceThresholdDecimal","type":"u64"},{"name":"maxFeeBps","type":"u64"},{"name":"dovesAgOracle","type":"publicKey"},{"name":"borrowLendParameters","type":{"defined":"BorrowLendParams"}},{"name":"borrowHourlyFundingDbps","type":"u64"},{"name":"borrowLimitInTokenAmount","type":"u64"},{"name":"minInterestFeeBps","type":"u64"},{"name":"minInterestFeeGracePeriodSeconds","type":"u64"},{"name":"maxTotalStakedAmountLamports","type":"u64"},{"name":"externalSwapFeeMultiplierBps","type":"u64"},{"name":"disableClosePositionRequest","type":"bool"},{"name":"withdrawalLimitIntervalSeconds","type":"u64"},{"name":"withdrawalLimitTokenAmount","type":"u64"}]}},{"name":"SetMaxGlobalSizesParams","type":{"kind":"struct","fields":[{"name":"maxGlobalLongSize","type":"u64"},{"name":"maxGlobalShortSize","type":"u64"},{"name":"recoveryId","type":"u8"},{"name":"signature","type":{"array":["u8",64]}},{"name":"referenceId","type":{"array":["u8",16]}},{"name":"timestamp","type":"u64"}]}},{"name":"SetPerpetualsConfigParams","type":{"kind":"struct","fields":[{"name":"permissions","type":{"defined":"Permissions"}}]}},{"name":"SetPoolConfigParams","type":{"kind":"struct","fields":[{"name":"fees","type":{"defined":"Fees"}},{"name":"limit","type":{"defined":"Limit"}},{"name":"maxRequestExecutionSec","type":"i64"},{"name":"parameterUpdateOracle","type":{"defined":"Secp256k1Pubkey"}},{"name":"maxTriggerPriceDiffBps","type":"u64"},{"name":"disableClosePositionRequest","type":"bool"},{"name":"maxLpTokenPriceChangeBps","type":"u64"}]}},{"name":"SetTestTimeParams","type":{"kind":"struct","fields":[{"name":"time","type":"i64"}]}},{"name":"Swap2Params","type":{"kind":"struct","fields":[{"name":"amountIn","type":"u64"},{"name":"minAmountOut","type":"u64"}]}},{"name":"SwapWithTokenLedgerParams","type":{"kind":"struct","fields":[{"name":"minAmountOut","type":"u64"}]}},{"name":"TestInitParams","type":{"kind":"struct","fields":[{"name":"allowSwap","type":"bool"},{"name":"allowAddLiquidity","type":"bool"},{"name":"allowRemoveLiquidity","type":"bool"},{"name":"allowIncreasePosition","type":"bool"},{"name":"allowDecreasePosition","type":"bool"},{"name":"allowCollateralWithdrawal","type":"bool"},{"name":"allowLiquidatePosition","type":"bool"}]}},{"name":"TransferAdminParams","type":{"kind":"struct","fields":[]}},{"name":"UpdateDecreasePositionRequest2Params","type":{"kind":"struct","fields":[{"name":"sizeUsdDelta","type":"u64"},{"name":"triggerPrice","type":"u64"}]}},{"name":"WithdrawParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"WithdrawFees2Params","type":{"kind":"struct","fields":[]}},{"name":"PriceImpactBuffer","type":{"kind":"struct","fields":[{"name":"openInterest","type":{"array":["i64",60]}},{"name":"lastUpdated","type":"i64"},{"name":"feeFactor","type":"u64"},{"name":"exponent","type":"f32"},{"name":"deltaImbalanceThresholdDecimal","type":"u64"},{"name":"maxFeeBps","type":"u64"}]}},{"name":"Assets","type":{"kind":"struct","fields":[{"name":"feesReserves","type":"u64"},{"name":"owned","type":"u64"},{"name":"locked","type":"u64"},{"name":"guaranteedUsd","type":"u64"},{"name":"globalShortSizes","type":"u64"},{"name":"globalShortAveragePrices","type":"u64"}]}},{"name":"PricingParams","type":{"kind":"struct","fields":[{"name":"tradeImpactFeeScalar","type":"u64"},{"name":"buffer","type":"u64"},{"name":"swapSpread","type":"u64"},{"name":"maxLeverage","type":"u64"},{"name":"maxGlobalLongSizes","type":"u64"},{"name":"maxGlobalShortSizes","type":"u64"}]}},{"name":"FundingRateState","type":{"kind":"struct","fields":[{"name":"cumulativeInterestRate","type":"u128"},{"name":"lastUpdate","type":"i64"},{"name":"hourlyFundingDbps","type":"u64"}]}},{"name":"JumpRateState","type":{"kind":"struct","fields":[{"name":"minRateBps","type":"u64"},{"name":"maxRateBps","type":"u64"},{"name":"targetRateBps","type":"u64"},{"name":"targetUtilizationRate","type":"u64"}]}},{"name":"BorrowLendParams","type":{"kind":"struct","fields":[{"name":"borrowsLimitInBps","type":"u64"},{"name":"maintainanceMarginBps","type":"u64"},{"name":"protocolFeeBps","type":"u64"},{"name":"liquidationMargin","type":"u64"},{"name":"liquidationFeeBps","type":"u64"}]}},{"name":"OraclePrice","type":{"kind":"struct","fields":[{"name":"price","type":"u64"},{"name":"exponent","type":"i32"}]}},{"name":"Price","type":{"kind":"struct","fields":[{"name":"price","type":"u64"},{"name":"expo","type":"i32"},{"name":"publishTime","type":"i64"}]}},{"name":"OracleParams","type":{"kind":"struct","fields":[{"name":"oracleAccount","type":"publicKey"},{"name":"oracleType","type":{"defined":"OracleType"}},{"name":"buffer","type":"u64"},{"name":"maxPriceAgeSec","type":"u32"}]}},{"name":"AmountAndFee","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"fee","type":"u64"},{"name":"feeBps","type":"u64"}]}},{"name":"Permissions","type":{"kind":"struct","fields":[{"name":"allowSwap","type":"bool"},{"name":"allowAddLiquidity","type":"bool"},{"name":"allowRemoveLiquidity","type":"bool"},{"name":"allowIncreasePosition","type":"bool"},{"name":"allowDecreasePosition","type":"bool"},{"name":"allowCollateralWithdrawal","type":"bool"},{"name":"allowLiquidatePosition","type":"bool"}]}},{"name":"Fees","type":{"kind":"struct","fields":[{"name":"swapMultiplier","type":"u64"},{"name":"stableSwapMultiplier","type":"u64"},{"name":"addRemoveLiquidityBps","type":"u64"},{"name":"swapBps","type":"u64"},{"name":"taxBps","type":"u64"},{"name":"stableSwapBps","type":"u64"},{"name":"stableSwapTaxBps","type":"u64"},{"name":"liquidationRewardBps","type":"u64"},{"name":"protocolShareBps","type":"u64"}]}},{"name":"PoolApr","type":{"kind":"struct","fields":[{"name":"lastUpdated","type":"i64"},{"name":"feeAprBps","type":"u64"},{"name":"realizedFeeUsd","type":"u64"}]}},{"name":"Limit","type":{"kind":"struct","fields":[{"name":"maxAumUsd","type":"u128"},{"name":"tokenWeightageBufferBps","type":"u128"},{"name":"buffer","type":"u64"}]}},{"name":"Secp256k1Pubkey","type":{"kind":"struct","fields":[{"name":"prefix","type":"u8"},{"name":"key","type":{"array":["u8",32]}}]}},{"name":"PriceImpactMechanism","type":{"kind":"enum","variants":[{"name":"TradeSize"},{"name":"DeltaImbalance"}]}},{"name":"OracleType","type":{"kind":"enum","variants":[{"name":"None"},{"name":"Test"},{"name":"Pyth"}]}},{"name":"PriceCalcMode","type":{"kind":"enum","variants":[{"name":"Min"},{"name":"Max"},{"name":"Ignore"}]}},{"name":"PriceStaleTolerance","type":{"kind":"enum","variants":[{"name":"Strict"},{"name":"Loose"}]}},{"name":"TradePoolType","type":{"kind":"enum","variants":[{"name":"Increase"},{"name":"Decrease"}]}},{"name":"RequestType","type":{"kind":"enum","variants":[{"name":"Market"},{"name":"Trigger"}]}},{"name":"RequestChange","type":{"kind":"enum","variants":[{"name":"None"},{"name":"Increase"},{"name":"Decrease"}]}},{"name":"Side","type":{"kind":"enum","variants":[{"name":"None"},{"name":"Long"},{"name":"Short"}]}}],"events":[{"name":"CreatePositionRequestEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"positionKey","type":"publicKey","index":false},{"name":"positionSide","type":"u8","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionCollateralMint","type":"publicKey","index":false},{"name":"positionCollateralCustody","type":"publicKey","index":false},{"name":"positionRequestKey","type":"publicKey","index":false},{"name":"positionRequestMint","type":"publicKey","index":false},{"name":"sizeUsdDelta","type":"u64","index":false},{"name":"collateralDelta","type":"u64","index":false},{"name":"priceSlippage","type":{"option":"u64"},"index":false},{"name":"jupiterMinimumOut","type":{"option":"u64"},"index":false},{"name":"preSwapAmount","type":{"option":"u64"},"index":false},{"name":"requestChange","type":"u8","index":false},{"name":"openTime","type":"i64","index":false},{"name":"referral","type":{"option":"publicKey"},"index":false}]},{"name":"InstantCreateTpslEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"positionKey","type":"publicKey","index":false},{"name":"positionSide","type":"u8","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionCollateralCustody","type":"publicKey","index":false},{"name":"positionRequestKey","type":"publicKey","index":false},{"name":"positionRequestMint","type":"publicKey","index":false},{"name":"sizeUsdDelta","type":"u64","index":false},{"name":"collateralDelta","type":"u64","index":false},{"name":"entirePosition","type":"bool","index":false},{"name":"openTime","type":"i64","index":false}]},{"name":"InstantUpdateTpslEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"positionKey","type":"publicKey","index":false},{"name":"positionSide","type":"u8","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionCollateralCustody","type":"publicKey","index":false},{"name":"positionRequestKey","type":"publicKey","index":false},{"name":"positionRequestMint","type":"publicKey","index":false},{"name":"sizeUsdDelta","type":"u64","index":false},{"name":"collateralDelta","type":"u64","index":false},{"name":"entirePosition","type":"bool","index":false},{"name":"updateTime","type":"i64","index":false}]},{"name":"ClosePositionRequestEvent","fields":[{"name":"entirePosition","type":{"option":"bool"},"index":false},{"name":"executed","type":"bool","index":false},{"name":"requestChange","type":"u8","index":false},{"name":"requestType","type":"u8","index":false},{"name":"side","type":"u8","index":false},{"name":"positionRequestKey","type":"publicKey","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"mint","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"openTime","type":"i64","index":false}]},{"name":"IncreasePositionEvent","fields":[{"name":"positionKey","type":"publicKey","index":false},{"name":"positionSide","type":"u8","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionCollateralCustody","type":"publicKey","index":false},{"name":"positionSizeUsd","type":"u64","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionRequestKey","type":"publicKey","index":false},{"name":"positionRequestMint","type":"publicKey","index":false},{"name":"positionRequestChange","type":"u8","index":false},{"name":"positionRequestType","type":"u8","index":false},{"name":"positionRequestCollateralDelta","type":"u64","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"sizeUsdDelta","type":"u64","index":false},{"name":"collateralUsdDelta","type":"u64","index":false},{"name":"collateralTokenDelta","type":"u64","index":false},{"name":"price","type":"u64","index":false},{"name":"priceSlippage","type":{"option":"u64"},"index":false},{"name":"feeToken","type":"u64","index":false},{"name":"feeUsd","type":"u64","index":false},{"name":"openTime","type":"i64","index":false},{"name":"referral","type":{"option":"publicKey"},"index":false},{"name":"positionFeeUsd","type":"u64","index":false},{"name":"fundingFeeUsd","type":"u64","index":false},{"name":"priceImpactFeeUsd","type":"u64","index":false}]},{"name":"IncreasePositionPreSwapEvent","fields":[{"name":"positionRequestKey","type":"publicKey","index":false},{"name":"transferAmount","type":"u64","index":false},{"name":"collateralCustodyPreSwapAmount","type":"u64","index":false}]},{"name":"DecreasePositionEvent","fields":[{"name":"positionKey","type":"publicKey","index":false},{"name":"positionSide","type":"u8","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionCollateralCustody","type":"publicKey","index":false},{"name":"positionSizeUsd","type":"u64","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionRequestKey","type":"publicKey","index":false},{"name":"positionRequestMint","type":"publicKey","index":false},{"name":"positionRequestChange","type":"u8","index":false},{"name":"positionRequestType","type":"u8","index":false},{"name":"hasProfit","type":"bool","index":false},{"name":"pnlDelta","type":"u64","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"sizeUsdDelta","type":"u64","index":false},{"name":"transferAmountUsd","type":"u64","index":false},{"name":"transferToken","type":{"option":"u64"},"index":false},{"name":"price","type":"u64","index":false},{"name":"priceSlippage","type":{"option":"u64"},"index":false},{"name":"feeUsd","type":"u64","index":false},{"name":"openTime","type":"i64","index":false},{"name":"referral","type":{"option":"publicKey"},"index":false},{"name":"positionFeeUsd","type":"u64","index":false},{"name":"fundingFeeUsd","type":"u64","index":false},{"name":"priceImpactFeeUsd","type":"u64","index":false},{"name":"originalPositionCollateralUsd","type":"u64","index":false},{"name":"positionCollateralUsd","type":"u64","index":false},{"name":"positionOpenTime","type":"i64","index":false},{"name":"positionPrice","type":"u64","index":false}]},{"name":"DecreasePositionPostSwapEvent","fields":[{"name":"positionRequestKey","type":"publicKey","index":false},{"name":"swapAmount","type":"u64","index":false},{"name":"jupiterMinimumOut","type":{"option":"u64"},"index":false}]},{"name":"LiquidateFullPositionEvent","fields":[{"name":"positionKey","type":"publicKey","index":false},{"name":"positionSide","type":"u8","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionCollateralCustody","type":"publicKey","index":false},{"name":"positionCollateralMint","type":"publicKey","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionSizeUsd","type":"u64","index":false},{"name":"hasProfit","type":"bool","index":false},{"name":"pnlDelta","type":"u64","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"transferAmountUsd","type":"u64","index":false},{"name":"transferToken","type":"u64","index":false},{"name":"price","type":"u64","index":false},{"name":"feeUsd","type":"u64","index":false},{"name":"liquidationFeeUsd","type":"u64","index":false},{"name":"openTime","type":"i64","index":false},{"name":"positionFeeUsd","type":"u64","index":false},{"name":"fundingFeeUsd","type":"u64","index":false},{"name":"priceImpactFeeUsd","type":"u64","index":false},{"name":"originalPositionCollateralUsd","type":"u64","index":false},{"name":"positionCollateralUsd","type":"u64","index":false},{"name":"positionOpenTime","type":"i64","index":false},{"name":"positionPrice","type":"u64","index":false}]},{"name":"PoolSwapEvent","fields":[{"name":"receivingCustodyKey","type":"publicKey","index":false},{"name":"dispensingCustodyKey","type":"publicKey","index":false},{"name":"poolKey","type":"publicKey","index":false},{"name":"amountIn","type":"u64","index":false},{"name":"amountOut","type":"u64","index":false},{"name":"swapUsdAmount","type":"u64","index":false},{"name":"amountOutAfterFees","type":"u64","index":false},{"name":"feeBps","type":"u64","index":false},{"name":"ownerKey","type":"publicKey","index":false},{"name":"receivingAccountKey","type":"publicKey","index":false}]},{"name":"PoolSwapExactOutEvent","fields":[{"name":"receivingCustodyKey","type":"publicKey","index":false},{"name":"dispensingCustodyKey","type":"publicKey","index":false},{"name":"poolKey","type":"publicKey","index":false},{"name":"amountIn","type":"u64","index":false},{"name":"amountInAfterFees","type":"u64","index":false},{"name":"amountOut","type":"u64","index":false},{"name":"swapUsdAmount","type":"u64","index":false},{"name":"feeBps","type":"u64","index":false},{"name":"ownerKey","type":"publicKey","index":false},{"name":"receivingAccountKey","type":"publicKey","index":false}]},{"name":"AddLiquidityEvent","fields":[{"name":"custodyKey","type":"publicKey","index":false},{"name":"poolKey","type":"publicKey","index":false},{"name":"tokenAmountIn","type":"u64","index":false},{"name":"prePoolAmountUsd","type":"u128","index":false},{"name":"tokenAmountUsd","type":"u64","index":false},{"name":"feeBps","type":"u64","index":false},{"name":"tokenAmountAfterFee","type":"u64","index":false},{"name":"mintAmountUsd","type":"u64","index":false},{"name":"lpAmount","type":"u64","index":false},{"name":"postPoolAmountUsd","type":"u128","index":false}]},{"name":"RemoveLiquidityEvent","fields":[{"name":"custodyKey","type":"publicKey","index":false},{"name":"poolKey","type":"publicKey","index":false},{"name":"lpAmountIn","type":"u64","index":false},{"name":"removeAmountUsd","type":"u64","index":false},{"name":"feeBps","type":"u64","index":false},{"name":"removeTokenAmount","type":"u64","index":false},{"name":"tokenAmountAfterFee","type":"u64","index":false},{"name":"postPoolAmountUsd","type":"u128","index":false}]},{"name":"InstantCreateLimitOrderEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"positionKey","type":"publicKey","index":false},{"name":"positionSide","type":"u8","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionCollateralMint","type":"publicKey","index":false},{"name":"positionCollateralCustody","type":"publicKey","index":false},{"name":"positionRequestKey","type":"publicKey","index":false},{"name":"positionRequestMint","type":"publicKey","index":false},{"name":"sizeUsdDelta","type":"u64","index":false},{"name":"collateralDelta","type":"u64","index":false},{"name":"openTime","type":"i64","index":false}]},{"name":"InstantIncreasePositionEvent","fields":[{"name":"positionKey","type":"publicKey","index":false},{"name":"positionSide","type":"u8","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionCollateralCustody","type":"publicKey","index":false},{"name":"positionSizeUsd","type":"u64","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"sizeUsdDelta","type":"u64","index":false},{"name":"collateralUsdDelta","type":"u64","index":false},{"name":"collateralTokenDelta","type":"u64","index":false},{"name":"price","type":"u64","index":false},{"name":"priceSlippage","type":"u64","index":false},{"name":"feeToken","type":"u64","index":false},{"name":"feeUsd","type":"u64","index":false},{"name":"openTime","type":"i64","index":false},{"name":"referral","type":{"option":"publicKey"},"index":false},{"name":"positionFeeUsd","type":"u64","index":false},{"name":"fundingFeeUsd","type":"u64","index":false},{"name":"priceImpactFeeUsd","type":"u64","index":false}]},{"name":"InstantDecreasePositionEvent","fields":[{"name":"positionKey","type":"publicKey","index":false},{"name":"positionSide","type":"u8","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionCollateralCustody","type":"publicKey","index":false},{"name":"positionSizeUsd","type":"u64","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"desiredMint","type":"publicKey","index":false},{"name":"hasProfit","type":"bool","index":false},{"name":"pnlDelta","type":"u64","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"sizeUsdDelta","type":"u64","index":false},{"name":"transferAmountUsd","type":"u64","index":false},{"name":"transferToken","type":"u64","index":false},{"name":"price","type":"u64","index":false},{"name":"priceSlippage","type":"u64","index":false},{"name":"feeUsd","type":"u64","index":false},{"name":"openTime","type":"i64","index":false},{"name":"referral","type":{"option":"publicKey"},"index":false},{"name":"positionFeeUsd","type":"u64","index":false},{"name":"fundingFeeUsd","type":"u64","index":false},{"name":"originalPositionCollateralUsd","type":"u64","index":false},{"name":"positionCollateralUsd","type":"u64","index":false},{"name":"priceImpactFeeUsd","type":"u64","index":false},{"name":"positionOpenTime","type":"i64","index":false},{"name":"positionPrice","type":"u64","index":false},{"name":"positionRequest","type":{"option":"publicKey"},"index":false}]},{"name":"DepositCollateralEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"positionKey","type":"publicKey","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"depositAmount","type":"u64","index":false},{"name":"userTokenAccount","type":"publicKey","index":false},{"name":"time","type":"i64","index":false}]},{"name":"WithdrawCollateralEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"positionKey","type":"publicKey","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"withdrawAmount","type":"u64","index":false},{"name":"userTokenAccount","type":"publicKey","index":false},{"name":"custody","type":"publicKey","index":false},{"name":"previousCollateralAmount","type":"u64","index":false},{"name":"collateralAmount","type":"u64","index":false},{"name":"collateralAmountUsd","type":"u64","index":false},{"name":"marginUsd","type":"u64","index":false},{"name":"time","type":"i64","index":false}]},{"name":"BorrowFromCustodyEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"positionKey","type":"publicKey","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"sizeCustodyToken","type":"u64","index":false},{"name":"collateralAmount","type":"u64","index":false},{"name":"collateralAmountUsd","type":"u64","index":false},{"name":"marginUsd","type":"u64","index":false},{"name":"updateTime","type":"i64","index":false}]},{"name":"RepayToCustodyEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"positionKey","type":"publicKey","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"sizeCustodyToken","type":"u64","index":false},{"name":"updateTime","type":"i64","index":false},{"name":"interest","type":"u128","index":false}]},{"name":"LiquidateBorrowPositionEvent","fields":[{"name":"positionKey","type":"publicKey","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionSizeUsd","type":"u64","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"collateralLockedInUsd","type":"u64","index":false},{"name":"collateralLockedInLp","type":"u64","index":false},{"name":"remainingCollateralInLp","type":"u64","index":false},{"name":"custodyTokenPrice","type":"u64","index":false},{"name":"totalBorrowsInUsd","type":"u64","index":false},{"name":"liquidationFeeUsd","type":"u64","index":false},{"name":"liquidationTime","type":"i64","index":false}]},{"name":"RedeemStakeEvent","fields":[{"name":"custody","type":"publicKey","index":false},{"name":"stakeAccount","type":"publicKey","index":false},{"name":"stakeInfo","type":"publicKey","index":false},{"name":"stakeRewards","type":"u64","index":false},{"name":"custodyTotalStakedAmount","type":"u64","index":false},{"name":"currentStakedAmount","type":"u64","index":false},{"name":"totalStakingRewards","type":"u64","index":false},{"name":"redeemTime","type":"i64","index":false}]},{"name":"WithdrawStakeEvent","fields":[{"name":"custody","type":"publicKey","index":false},{"name":"stakeAccount","type":"publicKey","index":false},{"name":"stakeInfo","type":"publicKey","index":false},{"name":"stakeRewards","type":"u64","index":false},{"name":"custodyTotalStakedAmount","type":"u64","index":false},{"name":"totalStakingRewards","type":"u64","index":false},{"name":"withdrawAmount","type":"u64","index":false},{"name":"withdrawTime","type":"i64","index":false}]},{"name":"DelegateStakeEvent","fields":[{"name":"custody","type":"publicKey","index":false},{"name":"stakeAccount","type":"publicKey","index":false},{"name":"stakeInfo","type":"publicKey","index":false},{"name":"custodyTotalStakedAmount","type":"u64","index":false},{"name":"stakeAmount","type":"u64","index":false},{"name":"validatorVoteAccount","type":"publicKey","index":false},{"name":"delegateTime","type":"i64","index":false}]},{"name":"WithdrawFeesEvent","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"custody","type":"publicKey","index":false},{"name":"custodyMint","type":"publicKey","index":false},{"name":"receivingTokenAccount","type":"publicKey","index":false},{"name":"totalTradeSwapFees","type":"u64","index":false},{"name":"poolTradeSwapFees","type":"u64","index":false},{"name":"protocolTradeSwapFees","type":"u64","index":false},{"name":"totalBorrowLendingFees","type":"u64","index":false},{"name":"poolBorrowLendingFees","type":"u64","index":false},{"name":"protocolBorrowLendingFees","type":"u64","index":false},{"name":"poolTotalFeesUsd","type":"u64","index":false},{"name":"aprBpsBefore","type":"u64","index":false},{"name":"aprBpsAfter","type":"u64","index":false},{"name":"aprBpsUpdatedAt","type":"i64","index":false},{"name":"poolRealizedFeeUsdBefore","type":"u64","index":false},{"name":"poolRealizedFeeUsdAfter","type":"u64","index":false},{"name":"curtime","type":"i64","index":false}]}],"errors":[{"code":6000,"name":"MathOverflow","msg":"Overflow in arithmetic operation"},{"code":6001,"name":"UnsupportedOracle","msg":"Unsupported price oracle"},{"code":6002,"name":"InvalidOracleAccount","msg":"Invalid oracle account"},{"code":6003,"name":"StaleOraclePrice","msg":"Stale oracle price"},{"code":6004,"name":"InvalidOraclePrice","msg":"Invalid oracle price"},{"code":6005,"name":"InvalidEnvironment","msg":"Instruction is not allowed in production"},{"code":6006,"name":"InvalidCollateralAccount","msg":"Invalid collateral account"},{"code":6007,"name":"InvalidCollateralAmount","msg":"Invalid collateral amount"},{"code":6008,"name":"CollateralSlippage","msg":"Collateral slippage"},{"code":6009,"name":"InvalidPositionState","msg":"Invalid position state"},{"code":6010,"name":"InvalidPerpetualsConfig","msg":"Invalid perpetuals config"},{"code":6011,"name":"InvalidPoolConfig","msg":"Invalid pool config"},{"code":6012,"name":"InvalidInstruction","msg":"Invalid instruction"},{"code":6013,"name":"InvalidCustodyConfig","msg":"Invalid custody config"},{"code":6014,"name":"InvalidCustodyBalance","msg":"Invalid custody balance"},{"code":6015,"name":"InvalidArgument","msg":"Invalid argument"},{"code":6016,"name":"InvalidPositionRequest","msg":"Invalid position request"},{"code":6017,"name":"InvalidPositionRequestInputAta","msg":"Invalid position request input ata"},{"code":6018,"name":"InvalidMint","msg":"Invalid mint"},{"code":6019,"name":"InsufficientTokenAmount","msg":"Insufficient token amount"},{"code":6020,"name":"InsufficientAmountReturned","msg":"Insufficient token amount returned"},{"code":6021,"name":"MaxPriceSlippage","msg":"Price slippage limit exceeded"},{"code":6022,"name":"MaxLeverage","msg":"Position leverage limit exceeded"},{"code":6023,"name":"CustodyAmountLimit","msg":"Custody amount limit exceeded"},{"code":6024,"name":"PoolAmountLimit","msg":"Pool amount limit exceeded"},{"code":6025,"name":"PersonalPoolAmountLimit","msg":"Personal pool amount limit exceeded"},{"code":6026,"name":"UnsupportedToken","msg":"Token is not supported"},{"code":6027,"name":"InstructionNotAllowed","msg":"Instruction is not allowed at this time"},{"code":6028,"name":"JupiterProgramMismatch","msg":"Jupiter Program ID mismatch"},{"code":6029,"name":"ProgramMismatch","msg":"Program ID mismatch"},{"code":6030,"name":"AddressMismatch","msg":"Address mismatch"},{"code":6031,"name":"KeeperATAMissing","msg":"Missing keeper ATA"},{"code":6032,"name":"SwapAmountMismatch","msg":"Swap amount mismatch"},{"code":6033,"name":"CPINotAllowed","msg":"CPI not allowed"},{"code":6034,"name":"InvalidKeeper","msg":"Invalid Keeper"},{"code":6035,"name":"ExceedExecutionPeriod","msg":"Exceed execution period"},{"code":6036,"name":"InvalidRequestType","msg":"Invalid Request Type"},{"code":6037,"name":"InvalidTriggerPrice","msg":"Invalid Trigger Price"},{"code":6038,"name":"TriggerPriceSlippage","msg":"Trigger Price Slippage"},{"code":6039,"name":"MissingTriggerPrice","msg":"Missing Trigger Price"},{"code":6040,"name":"MissingPriceSlippage","msg":"Missing Price Slippage"},{"code":6041,"name":"InvalidPriceCalcMode","msg":"Invalid Price Calc Mode"},{"code":6042,"name":"RequestUpdatedTooRecent","msg":"Request Updated Too Recent"},{"code":6043,"name":"ExceedTokenWeightage","msg":"Exceed Token Weightage"},{"code":6044,"name":"OraclePublishTimeTooEarly","msg":"Oracle Publish Time Too Early"},{"code":6045,"name":"PullOraclePublishTimeTooEarly","msg":"Pull Oracle Publish Time Too Early"},{"code":6046,"name":"StalePullOraclePrice","msg":"Stale Pull Oracle Price"},{"code":6047,"name":"InvalidPullOraclePrice","msg":"Invalid Pull Oracle Price"},{"code":6048,"name":"PullOracleNotVerified","msg":"Pull Oracle Not Verified"},{"code":6049,"name":"PriceDiffTooLarge","msg":"Price Diff Between Pull and Push Oracle is Too Large"},{"code":6050,"name":"InvalidDovesOraclePrice","msg":"Invalid Doves Oracle Price"},{"code":6051,"name":"InvalidRequestTime","msg":"Invalid Request Time"},{"code":6052,"name":"PositionUpdatedTooRecent","msg":"Position Updated Too Recent"},{"code":6053,"name":"LedgerTokenAccountDoesNotMatch","msg":"Ledger token account does not match"},{"code":6054,"name":"InvalidTokenLedger","msg":"Invalid token ledger"},{"code":6055,"name":"OraclePriceDifferenceTooLarge","msg":"Oracle Price Difference Too Large"},{"code":6056,"name":"InvalidOracleSigner","msg":"Invalid Oracle Signer"},{"code":6057,"name":"InvalidOracleTimestamp","msg":"Invalid Oracle Timestamp"},{"code":6058,"name":"InvalidMaxGlobalLongSize","msg":"New Max Global Long Size Too Low"},{"code":6059,"name":"InvalidMaxGlobalShortSize","msg":"New Max Global Short Size Too Low"},{"code":6060,"name":"BorrowsDisabled","msg":"Borrows disabled for this custody"},{"code":6061,"name":"BorrowLimitsExceeded","msg":"Borrows limit exceeded for this custody"},{"code":6062,"name":"WithdrawExceedsMarginLimits","msg":"Withdraw exceeds margin of the custody"},{"code":6063,"name":"CannotLiquidate","msg":"Cannot liquidate"},{"code":6064,"name":"CannotDelegateStake","msg":"Cannot delegate stake to deactivated account"},{"code":6065,"name":"ExceededMaxTotalStakedAmount","msg":"Max total staked amount exceeded"},{"code":6066,"name":"LpTokenPriceChangeLimitExceeded","msg":"LP token price change exceeds limit"}]} \ No newline at end of file diff --git a/idls/jupiter_stable.JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr.json b/idls/jupiter_stable.JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr.json new file mode 100644 index 0000000..106f2b7 --- /dev/null +++ b/idls/jupiter_stable.JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr.json @@ -0,0 +1 @@ +{"address":"JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr","metadata":{"name":"jup_stable","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"create_benefactor","discriminator":[184,241,45,0,53,40,201,54],"accounts":[{"name":"operator_authority","signer":true,"relations":["operator"]},{"name":"operator"},{"name":"payer","writable":true,"signer":true},{"name":"benefactor_authority"},{"name":"benefactor","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,101,110,101,102,97,99,116,111,114]},{"kind":"account","path":"benefactor_authority"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"mint_fee_rate","type":"u16"},{"name":"redeem_fee_rate","type":"u16"}]},{"name":"create_operator","discriminator":[145,40,238,75,181,252,59,11],"accounts":[{"name":"operator_authority","signer":true,"relations":["operator"]},{"name":"payer","writable":true,"signer":true},{"name":"operator"},{"name":"new_operator_authority"},{"name":"new_operator","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,111,114]},{"kind":"account","path":"new_operator_authority"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"role","type":{"defined":{"name":"OperatorRole"}}}]},{"name":"create_vault","discriminator":[29,237,247,208,193,82,54,135],"accounts":[{"name":"operator_authority","signer":true,"relations":["operator"]},{"name":"operator"},{"name":"payer","writable":true,"signer":true},{"name":"mint"},{"name":"config"},{"name":"authority","relations":["config"]},{"name":"vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"authority"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"delete_benefactor","discriminator":[216,227,84,147,79,177,152,147],"accounts":[{"name":"operator_authority","writable":true,"signer":true,"relations":["operator"]},{"name":"operator"},{"name":"receiver","writable":true},{"name":"benefactor","writable":true}],"args":[]},{"name":"delete_operator","discriminator":[208,84,168,116,138,201,98,16],"accounts":[{"name":"operator_authority","signer":true,"relations":["operator"]},{"name":"payer","writable":true,"signer":true},{"name":"operator"},{"name":"deleted_operator","writable":true}],"args":[]},{"name":"init","discriminator":[220,59,207,236,108,250,47,100],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"upgrade_authority","signer":true},{"name":"operator","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,111,114]},{"kind":"account","path":"upgrade_authority"}]}},{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[97,117,116,104,111,114,105,116,121]}]}},{"name":"mint","writable":true,"signer":true},{"name":"metadata","writable":true},{"name":"program_data"},{"name":"program","address":"JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr"},{"name":"metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"decimals","type":"u8"},{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"manage_benefactor","discriminator":[22,231,128,62,115,219,149,14],"accounts":[{"name":"operator_authority","writable":true,"signer":true,"relations":["operator"]},{"name":"operator"},{"name":"benefactor","writable":true}],"args":[{"name":"action","type":{"defined":{"name":"BenefactorManagementAction"}}}]},{"name":"manage_config","discriminator":[119,51,144,55,24,242,232,231],"accounts":[{"name":"operator_authority","writable":true,"signer":true,"relations":["operator"]},{"name":"operator"},{"name":"config","writable":true}],"args":[{"name":"action","type":{"defined":{"name":"ConfigManagementAction"}}}]},{"name":"manage_operator","discriminator":[82,172,106,235,147,246,96,85],"accounts":[{"name":"operator_authority","signer":true,"relations":["operator"]},{"name":"operator"},{"name":"managed_operator","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"action","type":{"defined":{"name":"OperatorManagementAction"}}}]},{"name":"manage_vault","discriminator":[165,7,106,242,73,193,195,128],"accounts":[{"name":"operator_authority","signer":true,"relations":["operator"]},{"name":"operator"},{"name":"vault","writable":true}],"args":[{"name":"action","type":{"defined":{"name":"VaultManagementAction"}}}]},{"name":"mint","discriminator":[51,57,225,47,182,146,137,166],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"user_collateral_token_account","writable":true},{"name":"user_lp_token_account","writable":true},{"name":"config","writable":true},{"name":"authority"},{"name":"lp_mint","writable":true},{"name":"vault","writable":true},{"name":"vault_mint"},{"name":"custodian"},{"name":"custodian_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"custodian"},{"kind":"account","path":"vault_token_program"},{"kind":"account","path":"vault_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"benefactor","writable":true},{"name":"lp_token_program"},{"name":"vault_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount","type":"u64"},{"name":"min_amount_out","type":"u64"}]},{"name":"redeem","discriminator":[184,12,86,149,70,196,97,225],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"user_lp_token_account","writable":true},{"name":"user_collateral_token_account","writable":true},{"name":"config","writable":true},{"name":"authority"},{"name":"lp_mint","writable":true},{"name":"vault","writable":true},{"name":"vault_token_account","writable":true},{"name":"vault_mint"},{"name":"benefactor","writable":true},{"name":"lp_token_program"},{"name":"vault_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount","type":"u64"},{"name":"min_amount_out","type":"u64"}]},{"name":"withdraw","discriminator":[183,18,70,156,148,109,161,34],"accounts":[{"name":"operator_authority","signer":true,"relations":["operator"]},{"name":"operator"},{"name":"custodian"},{"name":"custodian_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"custodian"},{"kind":"account","path":"token_program"},{"kind":"account","path":"vault_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"config"},{"name":"authority"},{"name":"vault","writable":true},{"name":"vault_token_account","writable":true},{"name":"vault_mint"},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"}]}],"accounts":[{"name":"Benefactor","discriminator":[98,159,41,233,19,232,104,12]},{"name":"Config","discriminator":[155,12,170,224,30,250,204,130]},{"name":"Operator","discriminator":[219,31,188,145,69,139,204,117]},{"name":"Vault","discriminator":[211,8,232,43,2,152,117,119]}],"events":[{"name":"MintV0Event","discriminator":[217,98,231,213,105,77,68,88]},{"name":"MintV1Event","discriminator":[63,113,88,48,163,187,48,83]},{"name":"RedeemV0Event","discriminator":[50,202,68,30,122,77,84,153]},{"name":"RedeemV1Event","discriminator":[67,51,207,92,219,144,44,2]}],"errors":[{"code":6000,"name":"SomeError","msg":""},{"code":6001,"name":"AdminArrayFull","msg":"Admin Array Full"},{"code":6002,"name":"NotAuthorized","msg":"Not Authorized"},{"code":6003,"name":"BadInput","msg":"Bad Input"},{"code":6004,"name":"BenefactorDisabled","msg":"Benefactor Disabled"},{"code":6005,"name":"BenefactorActive","msg":"Benefactor Active"},{"code":6006,"name":"VaultNotActive","msg":"Vault Not Active"},{"code":6007,"name":"InsufficientAmount","msg":"Insufficient Amount"},{"code":6008,"name":"InvalidFeeRate","msg":"Invalid Fee Rate"},{"code":6009,"name":"MintLimitExceeded","msg":"Mint Limit Exceeded"},{"code":6010,"name":"RedeemLimitExceeded","msg":"Redeem Limit Exceeded"},{"code":6011,"name":"SlippageToleranceExceeded","msg":"Slippage Tolerance Exceeded"},{"code":6012,"name":"MathOverflow","msg":"Math Overflow"},{"code":6013,"name":"InvalidLPMint","msg":"Invalid LP Mint"},{"code":6014,"name":"InvalidVaultMint","msg":"Invalid Vault Mint"},{"code":6015,"name":"InvalidAuthority","msg":"Invalid Authority"},{"code":6016,"name":"InvalidVaultTokenAccount","msg":"Invalid Vault Token Account"},{"code":6017,"name":"InvalidTokenProgram","msg":"Invalid Token Program"},{"code":6018,"name":"InvalidVaultFeeTokenAccount","msg":"Invalid Vault Fee Token Account"},{"code":6019,"name":"BadOracle","msg":"Bad Oracle"},{"code":6020,"name":"NoValidPrice","msg":"No Valid Price"},{"code":6021,"name":"InvalidBenefactor","msg":"Invalid Benefactor"},{"code":6022,"name":"InvalidCustodian","msg":"Invalid Custodian"},{"code":6023,"name":"InvalidPeriodLimit","msg":"Invalid Rate Limit Window"},{"code":6024,"name":"MissingOracleAccounts","msg":"Missing Oracle Accounts"},{"code":6025,"name":"NoOraclesFound","msg":"No Oracles Found"},{"code":6026,"name":"ZeroAmount","msg":"Zero Amount"},{"code":6027,"name":"ProtocolPaused","msg":"Protocol Paused"},{"code":6028,"name":"OperatorDisabled","msg":"Operator Disabled"},{"code":6029,"name":"VaultDisabled","msg":"Vault Disabled"},{"code":6030,"name":"VaultEnabled","msg":"Vault Enabled"},{"code":6031,"name":"VaultIsDry","msg":"Vault Is Dry"},{"code":6032,"name":"InvalidPegPriceUSD","msg":"Invalid Peg Price USD"},{"code":6033,"name":"NoValidOracle","msg":"No Valid Oracle"},{"code":6034,"name":"PriceConfidenceTooWide","msg":"Price Confidence Too Wide"},{"code":6035,"name":"OperatorCannotDeleteItself","msg":"Operator Cannot Delete Itself"}],"types":[{"name":"Benefactor","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"authority","type":"pubkey"},{"name":"status","type":{"defined":{"name":"BenefactorStatus"}}},{"name":"_padding0","type":{"array":["u8",7]}},{"name":"mint_fee_rate","type":"u16"},{"name":"redeem_fee_rate","type":"u16"},{"name":"_padding1","type":{"array":["u8",4]}},{"name":"period_limits","type":{"array":[{"defined":{"name":"PeriodLimit"}},4]}},{"name":"total_minted","type":{"array":["u8",16]}},{"name":"total_redeemed","type":{"array":["u8",16]}},{"name":"fee_overrides","type":{"array":[{"defined":{"name":"FeeOverride"}},4]}},{"name":"reserved","type":{"array":["u8",96]}}]}},{"name":"BenefactorManagementAction","type":{"kind":"enum","variants":[{"name":"Disable"},{"name":"SetStatus","fields":[{"name":"status","type":{"defined":{"name":"BenefactorStatus"}}}]},{"name":"UpdateFeeRates","fields":[{"name":"mint_fee_rate","type":"u16"},{"name":"redeem_fee_rate","type":"u16"}]},{"name":"UpdatePeriodLimit","fields":[{"name":"index","type":"u8"},{"name":"duration_seconds","type":"u64"},{"name":"max_mint_amount","type":"u64"},{"name":"max_redeem_amount","type":"u64"}]},{"name":"ResetPeriodLimit","fields":[{"name":"index","type":"u8"}]},{"name":"UpdateFeeOverride","fields":[{"name":"index","type":"u8"},{"name":"mint","type":"pubkey"},{"name":"mint_fee_rate","type":"u16"},{"name":"redeem_fee_rate","type":"u16"}]},{"name":"DeleteFeeOverride","fields":[{"name":"index","type":"u8"}]}]}},{"name":"BenefactorStatus","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Active"},{"name":"Disabled"}]}},{"name":"Config","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"authority","type":"pubkey"},{"name":"token_program","type":"pubkey"},{"name":"period_limits","type":{"array":[{"defined":{"name":"PeriodLimit"}},4]}},{"name":"peg_price_usd","type":"u64"},{"name":"decimals","type":"u8"},{"name":"is_mint_redeem_enabled","type":"u8"},{"name":"authority_bump","type":"u8"},{"name":"config_bump","type":"u8"},{"name":"_padding","type":{"array":["u8",4]}},{"name":"reserved","type":{"array":["u8",192]}}]}},{"name":"ConfigManagementAction","type":{"kind":"enum","variants":[{"name":"Pause"},{"name":"UpdatePauseFlag","fields":[{"name":"is_mint_redeem_enabled","type":"bool"}]},{"name":"UpdatePeriodLimit","fields":[{"name":"index","type":"u8"},{"name":"duration_seconds","type":"u64"},{"name":"max_mint_amount","type":"u64"},{"name":"max_redeem_amount","type":"u64"}]},{"name":"ResetPeriodLimit","fields":[{"name":"index","type":"u8"}]},{"name":"SetPegPriceUSD","fields":[{"name":"peg_price_usd","type":"u64"}]}]}},{"name":"DovesOracle","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"},{"name":"reserved1","type":{"array":["u8",32]}},{"name":"reserved2","type":{"array":["u8",32]}},{"name":"reserved3","type":{"array":["u8",24]}}]}},{"name":"EmptyOracle","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"reserved","type":{"array":["u8",32]}},{"name":"reserved1","type":{"array":["u8",32]}},{"name":"reserved2","type":{"array":["u8",32]}},{"name":"reserved3","type":{"array":["u8",24]}}]}},{"name":"FeeOverride","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"mint_fee_rate","type":"u16"},{"name":"redeem_fee_rate","type":"u16"},{"name":"_padding","type":{"array":["u8",4]}}]}},{"name":"MintV0Event","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"net_amount","type":"u64"},{"name":"oracle_price","type":"u64"},{"name":"one_to_one_amount","type":"u64"},{"name":"oracle_amount","type":"u64"},{"name":"mint_amount","type":"u64"}]}},{"name":"MintV1Event","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"net_amount","type":"u64"},{"name":"oracle_price","type":"u64"},{"name":"one_to_one_amount","type":"u64"},{"name":"oracle_amount","type":"u64"},{"name":"mint_amount","type":"u64"},{"name":"collateral","type":"pubkey"},{"name":"benefactor","type":"pubkey"}]}},{"name":"Operator","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"operator_authority","type":"pubkey"},{"name":"role","type":"u64"},{"name":"status","type":{"defined":{"name":"OperatorStatus"}}},{"name":"_padding0","type":{"array":["u8",7]}},{"name":"reserved","type":{"array":["u8",128]}}]}},{"name":"OperatorManagementAction","type":{"kind":"enum","variants":[{"name":"SetStatus","fields":[{"name":"status","type":{"defined":{"name":"OperatorStatus"}}}]},{"name":"SetRole","fields":[{"name":"role","type":{"defined":{"name":"OperatorRole"}}}]},{"name":"ClearRole","fields":[{"name":"role","type":{"defined":{"name":"OperatorRole"}}}]}]}},{"name":"OperatorRole","type":{"kind":"enum","variants":[{"name":"Admin"},{"name":"PeriodManager"},{"name":"GlobalDisabler"},{"name":"VaultManager"},{"name":"VaultDisabler"},{"name":"BenefactorManager"},{"name":"BenefactorDisabler"},{"name":"PegManager"},{"name":"CollateralManager"}]}},{"name":"OperatorStatus","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Enabled"},{"name":"Disabled"}]}},{"name":"OracleConfig","type":{"kind":"enum","variants":[{"name":"None"},{"name":"Pyth","fields":[{"array":["u8",32]},"pubkey"]},{"name":"SwitchboardOnDemand","fields":["pubkey"]},{"name":"Doves","fields":["pubkey"]}]}},{"name":"OracleType","repr":{"kind":"c"},"type":{"kind":"enum","variants":[{"name":"Empty","fields":[{"defined":{"name":"EmptyOracle"}}]},{"name":"Pyth","fields":[{"defined":{"name":"PythV2Oracle"}}]},{"name":"Doves","fields":[{"defined":{"name":"DovesOracle"}}]},{"name":"SwitchboardOnDemand","fields":[{"defined":{"name":"SwitchboardOnDemandOracle"}}]}]}},{"name":"PeriodLimit","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"duration_seconds","docs":["Window duration in seconds (0 = disabled)"],"type":"u64"},{"name":"max_mint_amount","docs":["Maximum mint amount in this window"],"type":"u64"},{"name":"max_redeem_amount","docs":["Maximum redeem amount in this window"],"type":"u64"},{"name":"minted_amount","docs":["Amount minted in current window"],"type":"u64"},{"name":"redeemed_amount","docs":["Amount redeemed in current window"],"type":"u64"},{"name":"window_start","docs":["Window start timestamp"],"type":"i64"}]}},{"name":"PythV2Oracle","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"feed_id","type":{"array":["u8",32]}},{"name":"account","type":"pubkey"},{"name":"reserved1","type":{"array":["u8",32]}},{"name":"reserved2","type":{"array":["u8",24]}}]}},{"name":"RedeemV0Event","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"net_amount","type":"u64"},{"name":"oracle_price","type":"u64"},{"name":"one_to_one_amount","type":"u64"},{"name":"oracle_amount","type":"u64"},{"name":"redeem_amount","type":"u64"}]}},{"name":"RedeemV1Event","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"net_amount","type":"u64"},{"name":"oracle_price","type":"u64"},{"name":"one_to_one_amount","type":"u64"},{"name":"oracle_amount","type":"u64"},{"name":"redeem_amount","type":"u64"},{"name":"collateral","type":"pubkey"},{"name":"benefactor","type":"pubkey"}]}},{"name":"SwitchboardOnDemandOracle","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"},{"name":"reserved","type":{"array":["u8",32]}},{"name":"reserved1","type":{"array":["u8",32]}},{"name":"reserved2","type":{"array":["u8",24]}}]}},{"name":"Vault","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"custodian","type":"pubkey"},{"name":"token_account","type":"pubkey"},{"name":"token_program","type":"pubkey"},{"name":"stalesness_threshold","type":"u64"},{"name":"min_oracle_price_usd","type":"u64"},{"name":"max_oracle_price_usd","type":"u64"},{"name":"status","type":{"defined":{"name":"VaultStatus"}}},{"name":"_padding1","type":{"array":["u8",7]}},{"name":"bump","type":"u8"},{"name":"decimals","type":"u8"},{"name":"_padding2","type":{"array":["u8",6]}},{"name":"oracles","type":{"array":[{"defined":{"name":"OracleType"}},5]}},{"name":"_padding3","type":{"array":["u8",3]}},{"name":"period_limits","type":{"array":[{"defined":{"name":"PeriodLimit"}},4]}},{"name":"reserved1","type":{"array":["u8",32]}},{"name":"total_minted","type":{"array":["u8",16]}},{"name":"total_redeemed","type":{"array":["u8",16]}},{"name":"reserved","type":{"array":["u8",256]}}]}},{"name":"VaultManagementAction","type":{"kind":"enum","variants":[{"name":"Disable"},{"name":"SetStatus","fields":[{"name":"status","type":{"defined":{"name":"VaultStatus"}}}]},{"name":"UpdateOracle","fields":[{"name":"index","type":"u8"},{"name":"oracle","type":{"defined":{"name":"OracleConfig"}}}]},{"name":"UpdatePeriodLimit","fields":[{"name":"index","type":"u8"},{"name":"duration_seconds","type":"u64"},{"name":"max_mint_amount","type":"u64"},{"name":"max_redeem_amount","type":"u64"}]},{"name":"ResetPeriodLimit","fields":[{"name":"index","type":"u8"}]},{"name":"SetCustodian","fields":[{"name":"new_custodian","type":"pubkey"}]},{"name":"SetStalesnessThreshold","fields":[{"name":"stalesness_threshold","type":"u64"}]},{"name":"SetMinOraclePrice","fields":[{"name":"min_oracle_price_usd","type":"u64"}]},{"name":"SetMaxOraclePrice","fields":[{"name":"max_oracle_price_usd","type":"u64"}]}]}},{"name":"VaultStatus","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Enabled"},{"name":"Disabled"}]}}]} \ No newline at end of file diff --git a/idls/jupiter_v4.JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB.json b/idls/jupiter_v4.JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB.json new file mode 100644 index 0000000..cd8a53a --- /dev/null +++ b/idls/jupiter_v4.JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"jupiter","instructions":[{"name":"route","accounts":[{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"destinationTokenAccount","isMut":false,"isSigner":false}],"args":[{"name":"swapLeg","type":{"defined":"SwapLeg"}},{"name":"inAmount","type":"u64"},{"name":"quotedOutAmount","type":"u64"},{"name":"slippageBps","type":"u16"},{"name":"platformFeeBps","type":"u8"}]},{"name":"whirlpoolSwapExactOutput","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenAuthority","isMut":false,"isSigner":true},{"name":"whirlpool","isMut":true,"isSigner":false},{"name":"tokenOwnerAccountA","isMut":true,"isSigner":false},{"name":"tokenVaultA","isMut":true,"isSigner":false},{"name":"tokenOwnerAccountB","isMut":true,"isSigner":false},{"name":"tokenVaultB","isMut":true,"isSigner":false},{"name":"tickArray0","isMut":true,"isSigner":false},{"name":"tickArray1","isMut":true,"isSigner":false},{"name":"tickArray2","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false,"docs":["Oracle is currently unused and will be enabled on subsequent updates"]}],"args":[{"name":"outAmount","type":"u64"},{"name":"inAmountWithSlippage","type":{"defined":"AmountWithSlippage"}},{"name":"aToB","type":"bool"},{"name":"platformFeeBps","type":"u8"}]},{"name":"raydiumSwapExactOutput","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"ammId","isMut":true,"isSigner":false},{"name":"ammAuthority","isMut":false,"isSigner":false},{"name":"ammOpenOrders","isMut":true,"isSigner":false},{"name":"poolCoinTokenAccount","isMut":true,"isSigner":false},{"name":"poolPcTokenAccount","isMut":true,"isSigner":false},{"name":"serumProgramId","isMut":false,"isSigner":false},{"name":"serumMarket","isMut":true,"isSigner":false},{"name":"serumBids","isMut":true,"isSigner":false},{"name":"serumAsks","isMut":true,"isSigner":false},{"name":"serumEventQueue","isMut":true,"isSigner":false},{"name":"serumCoinVaultAccount","isMut":true,"isSigner":false},{"name":"serumPcVaultAccount","isMut":true,"isSigner":false},{"name":"serumVaultSigner","isMut":false,"isSigner":false},{"name":"userSourceTokenAccount","isMut":true,"isSigner":false},{"name":"userDestinationTokenAccount","isMut":true,"isSigner":false},{"name":"userSourceOwner","isMut":false,"isSigner":true}],"args":[{"name":"outAmount","type":"u64"},{"name":"inAmountWithSlippage","type":{"defined":"AmountWithSlippage"}},{"name":"platformFeeBps","type":"u8"}]},{"name":"raydiumClmmSwapExactOutput","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"ammConfig","isMut":false,"isSigner":false},{"name":"poolState","isMut":true,"isSigner":false},{"name":"inputTokenAccount","isMut":true,"isSigner":false},{"name":"outputTokenAccount","isMut":true,"isSigner":false},{"name":"inputVault","isMut":true,"isSigner":false},{"name":"outputVault","isMut":true,"isSigner":false},{"name":"observationState","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tickArray","isMut":true,"isSigner":false}],"args":[{"name":"outAmount","type":"u64"},{"name":"inAmountWithSlippage","type":{"defined":"AmountWithSlippage"}},{"name":"platformFeeBps","type":"u8"}]},{"name":"createOpenOrders","accounts":[{"name":"openOrders","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"market","isMut":false,"isSigner":false}],"args":[]},{"name":"createOpenOrderV2","accounts":[{"name":"openOrders","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"market","isMut":false,"isSigner":false}],"args":[]},{"name":"mercurialSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"swapState","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"poolAuthority","isMut":false,"isSigner":false},{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"sourceTokenAccount","isMut":true,"isSigner":false},{"name":"destinationTokenAccount","isMut":true,"isSigner":false}],"args":[]},{"name":"cykuraSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"signer","isMut":false,"isSigner":true},{"name":"factoryState","isMut":false,"isSigner":false},{"name":"poolState","isMut":true,"isSigner":false},{"name":"inputTokenAccount","isMut":true,"isSigner":false},{"name":"outputTokenAccount","isMut":true,"isSigner":false},{"name":"inputVault","isMut":true,"isSigner":false},{"name":"outputVault","isMut":true,"isSigner":false},{"name":"lastObservationState","isMut":true,"isSigner":false},{"name":"coreProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"serumSwap","accounts":[{"name":"market","accounts":[{"name":"market","isMut":true,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"requestQueue","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"coinVault","isMut":true,"isSigner":false},{"name":"pcVault","isMut":true,"isSigner":false},{"name":"vaultSigner","isMut":false,"isSigner":false}]},{"name":"authority","isMut":false,"isSigner":true},{"name":"orderPayerTokenAccount","isMut":true,"isSigner":false},{"name":"coinWallet","isMut":true,"isSigner":false},{"name":"pcWallet","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"saberSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swap","isMut":false,"isSigner":false},{"name":"swapAuthority","isMut":false,"isSigner":false},{"name":"userAuthority","isMut":false,"isSigner":false},{"name":"inputUserAccount","isMut":true,"isSigner":false},{"name":"inputTokenAccount","isMut":true,"isSigner":false},{"name":"outputUserAccount","isMut":true,"isSigner":false},{"name":"outputTokenAccount","isMut":true,"isSigner":false},{"name":"feesTokenAccount","isMut":true,"isSigner":false}],"args":[]},{"name":"saberAddDecimals","accounts":[{"name":"addDecimalsProgram","isMut":false,"isSigner":false},{"name":"wrapper","isMut":false,"isSigner":false},{"name":"wrapperMint","isMut":true,"isSigner":false},{"name":"wrapperUnderlyingTokens","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"userUnderlyingTokens","isMut":true,"isSigner":false},{"name":"userWrappedTokens","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"tokenSwap","accounts":[{"name":"tokenSwapProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swap","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"source","isMut":true,"isSigner":false},{"name":"swapSource","isMut":true,"isSigner":false},{"name":"swapDestination","isMut":true,"isSigner":false},{"name":"destination","isMut":true,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"poolFee","isMut":true,"isSigner":false}],"args":[]},{"name":"senchaSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swap","isMut":true,"isSigner":false},{"name":"userAuthority","isMut":false,"isSigner":false},{"name":"inputUserAccount","isMut":true,"isSigner":false},{"name":"inputTokenAccount","isMut":true,"isSigner":false},{"name":"inputFeesAccount","isMut":true,"isSigner":false},{"name":"outputUserAccount","isMut":true,"isSigner":false},{"name":"outputTokenAccount","isMut":true,"isSigner":false},{"name":"outputFeesAccount","isMut":true,"isSigner":false}],"args":[]},{"name":"stepSwap","accounts":[{"name":"tokenSwapProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swap","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"source","isMut":true,"isSigner":false},{"name":"swapSource","isMut":true,"isSigner":false},{"name":"swapDestination","isMut":true,"isSigner":false},{"name":"destination","isMut":true,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"poolFee","isMut":true,"isSigner":false}],"args":[]},{"name":"cropperSwap","accounts":[{"name":"tokenSwapProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swap","isMut":false,"isSigner":false},{"name":"swapState","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"source","isMut":true,"isSigner":false},{"name":"swapSource","isMut":true,"isSigner":false},{"name":"swapDestination","isMut":true,"isSigner":false},{"name":"destination","isMut":true,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"poolFee","isMut":true,"isSigner":false}],"args":[]},{"name":"raydiumSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"ammId","isMut":true,"isSigner":false},{"name":"ammAuthority","isMut":false,"isSigner":false},{"name":"ammOpenOrders","isMut":true,"isSigner":false},{"name":"poolCoinTokenAccount","isMut":true,"isSigner":false},{"name":"poolPcTokenAccount","isMut":true,"isSigner":false},{"name":"serumProgramId","isMut":false,"isSigner":false},{"name":"serumMarket","isMut":true,"isSigner":false},{"name":"serumBids","isMut":true,"isSigner":false},{"name":"serumAsks","isMut":true,"isSigner":false},{"name":"serumEventQueue","isMut":true,"isSigner":false},{"name":"serumCoinVaultAccount","isMut":true,"isSigner":false},{"name":"serumPcVaultAccount","isMut":true,"isSigner":false},{"name":"serumVaultSigner","isMut":false,"isSigner":false},{"name":"userSourceTokenAccount","isMut":true,"isSigner":false},{"name":"userDestinationTokenAccount","isMut":true,"isSigner":false},{"name":"userSourceOwner","isMut":false,"isSigner":true}],"args":[]},{"name":"cremaSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"clmmConfig","isMut":false,"isSigner":false},{"name":"clmmpool","isMut":true,"isSigner":false},{"name":"tokenA","isMut":false,"isSigner":false},{"name":"tokenB","isMut":false,"isSigner":false},{"name":"accountA","isMut":true,"isSigner":false},{"name":"accountB","isMut":true,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"tickArrayMap","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"partner","isMut":false,"isSigner":false},{"name":"partnerAtaA","isMut":true,"isSigner":false},{"name":"partnerAtaB","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"lifinitySwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"amm","isMut":false,"isSigner":false},{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"sourceInfo","isMut":true,"isSigner":false},{"name":"destinationInfo","isMut":true,"isSigner":false},{"name":"swapSource","isMut":true,"isSigner":false},{"name":"swapDestination","isMut":true,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"feeAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"pythAccount","isMut":false,"isSigner":false},{"name":"pythPcAccount","isMut":false,"isSigner":false},{"name":"configAccount","isMut":true,"isSigner":false}],"args":[]},{"name":"marinadeDeposit","accounts":[{"name":"marinadeFinanceProgram","isMut":false,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"msolMint","isMut":true,"isSigner":false},{"name":"liqPoolSolLegPda","isMut":true,"isSigner":false},{"name":"liqPoolMsolLeg","isMut":true,"isSigner":false},{"name":"liqPoolMsolLegAuthority","isMut":false,"isSigner":false},{"name":"reservePda","isMut":true,"isSigner":false},{"name":"transferFrom","isMut":true,"isSigner":false},{"name":"mintTo","isMut":true,"isSigner":false},{"name":"msolMintAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"userWsolTokenAccount","isMut":true,"isSigner":false},{"name":"tempWsolTokenAccount","isMut":true,"isSigner":false},{"name":"userTransferAuthority","isMut":true,"isSigner":true},{"name":"wsolMint","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"marinadeUnstake","accounts":[{"name":"marinadeFinanceProgram","isMut":false,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"msolMint","isMut":true,"isSigner":false},{"name":"liqPoolSolLegPda","isMut":true,"isSigner":false},{"name":"liqPoolMsolLeg","isMut":true,"isSigner":false},{"name":"treasuryMsolAccount","isMut":true,"isSigner":false},{"name":"getMsolFrom","isMut":true,"isSigner":false},{"name":"getMsolFromAuthority","isMut":false,"isSigner":true},{"name":"transferSolTo","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"userWsolTokenAccount","isMut":true,"isSigner":false}],"args":[]},{"name":"aldrinSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"poolSigner","isMut":false,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"baseTokenVault","isMut":true,"isSigner":false},{"name":"quoteTokenVault","isMut":true,"isSigner":false},{"name":"feePoolTokenAccount","isMut":true,"isSigner":false},{"name":"walletAuthority","isMut":false,"isSigner":true},{"name":"userBaseTokenAccount","isMut":true,"isSigner":false},{"name":"userQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"aldrinV2Swap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"poolSigner","isMut":false,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"baseTokenVault","isMut":true,"isSigner":false},{"name":"quoteTokenVault","isMut":true,"isSigner":false},{"name":"feePoolTokenAccount","isMut":true,"isSigner":false},{"name":"walletAuthority","isMut":false,"isSigner":true},{"name":"userBaseTokenAccount","isMut":true,"isSigner":false},{"name":"userQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"curve","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"whirlpoolSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenAuthority","isMut":false,"isSigner":true},{"name":"whirlpool","isMut":true,"isSigner":false},{"name":"tokenOwnerAccountA","isMut":true,"isSigner":false},{"name":"tokenVaultA","isMut":true,"isSigner":false},{"name":"tokenOwnerAccountB","isMut":true,"isSigner":false},{"name":"tokenVaultB","isMut":true,"isSigner":false},{"name":"tickArray0","isMut":true,"isSigner":false},{"name":"tickArray1","isMut":true,"isSigner":false},{"name":"tickArray2","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false,"docs":["Oracle is currently unused and will be enabled on subsequent updates"]}],"args":[]},{"name":"invariantSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"tickmap","isMut":true,"isSigner":false},{"name":"accountX","isMut":true,"isSigner":false},{"name":"accountY","isMut":true,"isSigner":false},{"name":"reserveX","isMut":true,"isSigner":false},{"name":"reserveY","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"meteoraSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"userSourceToken","isMut":true,"isSigner":false},{"name":"userDestinationToken","isMut":true,"isSigner":false},{"name":"aVault","isMut":true,"isSigner":false},{"name":"bVault","isMut":true,"isSigner":false},{"name":"aTokenVault","isMut":true,"isSigner":false},{"name":"bTokenVault","isMut":true,"isSigner":false},{"name":"aVaultLpMint","isMut":true,"isSigner":false},{"name":"bVaultLpMint","isMut":true,"isSigner":false},{"name":"aVaultLp","isMut":true,"isSigner":false},{"name":"bVaultLp","isMut":true,"isSigner":false},{"name":"adminTokenFee","isMut":true,"isSigner":false},{"name":"user","isMut":false,"isSigner":true},{"name":"vaultProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"goosefxSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"controller","isMut":false,"isSigner":false},{"name":"pair","isMut":true,"isSigner":false},{"name":"sslIn","isMut":true,"isSigner":false},{"name":"sslOut","isMut":true,"isSigner":false},{"name":"liabilityVaultIn","isMut":true,"isSigner":false},{"name":"swappedLiabilityVaultIn","isMut":true,"isSigner":false},{"name":"liabilityVaultOut","isMut":true,"isSigner":false},{"name":"swappedLiabilityVaultOut","isMut":true,"isSigner":false},{"name":"userInAta","isMut":true,"isSigner":false},{"name":"userOutAta","isMut":true,"isSigner":false},{"name":"feeCollectorAta","isMut":true,"isSigner":false},{"name":"userWallet","isMut":false,"isSigner":true},{"name":"feeCollector","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"deltafiSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"marketConfig","isMut":false,"isSigner":false},{"name":"swapInfo","isMut":true,"isSigner":false},{"name":"userSourceToken","isMut":true,"isSigner":false},{"name":"userDestinationToken","isMut":true,"isSigner":false},{"name":"swapSourceToken","isMut":true,"isSigner":false},{"name":"swapDestinationToken","isMut":true,"isSigner":false},{"name":"deltafiUser","isMut":true,"isSigner":false},{"name":"adminDestinationToken","isMut":true,"isSigner":false},{"name":"pythPriceBase","isMut":false,"isSigner":false},{"name":"pythPriceQuote","isMut":false,"isSigner":false},{"name":"userAuthority","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"balansolSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"pool","isMut":true,"isSigner":false},{"name":"taxMan","isMut":true,"isSigner":false},{"name":"bidMint","isMut":false,"isSigner":false},{"name":"treasurer","isMut":false,"isSigner":false},{"name":"srcTreasury","isMut":true,"isSigner":false},{"name":"srcAssociatedTokenAccount","isMut":true,"isSigner":false},{"name":"askMint","isMut":false,"isSigner":false},{"name":"dstTreasury","isMut":true,"isSigner":false},{"name":"dstAssociatedTokenAccount","isMut":true,"isSigner":false},{"name":"dstTokenAccountTaxman","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"marcoPoloSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"swapperXAccount","isMut":true,"isSigner":false},{"name":"swapperYAccount","isMut":true,"isSigner":false},{"name":"swapper","isMut":true,"isSigner":true},{"name":"referrerXAccount","isMut":true,"isSigner":false},{"name":"referrerYAccount","isMut":true,"isSigner":false},{"name":"referrer","isMut":true,"isSigner":false},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"dradexSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"pair","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"dexUser","isMut":false,"isSigner":false},{"name":"marketUser","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"t0Vault","isMut":true,"isSigner":false},{"name":"t1Vault","isMut":true,"isSigner":false},{"name":"t0User","isMut":true,"isSigner":false},{"name":"t1User","isMut":true,"isSigner":false},{"name":"master","isMut":false,"isSigner":false},{"name":"signer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"logger","isMut":false,"isSigner":false}],"args":[]},{"name":"lifinityV2Swap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"amm","isMut":true,"isSigner":false},{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"sourceInfo","isMut":true,"isSigner":false},{"name":"destinationInfo","isMut":true,"isSigner":false},{"name":"swapSource","isMut":true,"isSigner":false},{"name":"swapDestination","isMut":true,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"feeAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"oracleMainAccount","isMut":false,"isSigner":false},{"name":"oracleSubAccount","isMut":false,"isSigner":false},{"name":"oraclePcAccount","isMut":false,"isSigner":false}],"args":[]},{"name":"raydiumClmmSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"ammConfig","isMut":false,"isSigner":false},{"name":"poolState","isMut":true,"isSigner":false},{"name":"inputTokenAccount","isMut":true,"isSigner":false},{"name":"outputTokenAccount","isMut":true,"isSigner":false},{"name":"inputVault","isMut":true,"isSigner":false},{"name":"outputVault","isMut":true,"isSigner":false},{"name":"observationState","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tickArray","isMut":true,"isSigner":false}],"args":[]},{"name":"phoenixSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"logAuthority","isMut":false,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"trader","isMut":false,"isSigner":true},{"name":"baseAccount","isMut":true,"isSigner":false},{"name":"quoteAccount","isMut":true,"isSigner":false},{"name":"baseVault","isMut":true,"isSigner":false},{"name":"quoteVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"claimBonk","accounts":[{"name":"openOrders","isMut":false,"isSigner":false},{"name":"bonkMint","isMut":false,"isSigner":false},{"name":"openOrdersBonkTokenAccount","isMut":true,"isSigner":false},{"name":"market","isMut":false,"isSigner":false},{"name":"openOrdersOwner","isMut":true,"isSigner":false},{"name":"claimerTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]}],"types":[{"name":"AmountWithSlippage","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"slippageBps","type":"u16"}]}},{"name":"SplitLegDeeper","type":{"kind":"struct","fields":[{"name":"percent","type":"u8"},{"name":"swapLeg","type":{"defined":"SwapLegSwap"}}]}},{"name":"SplitLeg","type":{"kind":"struct","fields":[{"name":"percent","type":"u8"},{"name":"swapLeg","type":{"defined":"SwapLegDeeper"}}]}},{"name":"Side","type":{"kind":"enum","variants":[{"name":"Bid"},{"name":"Ask"}]}},{"name":"SwapLegSwap","type":{"kind":"enum","variants":[{"name":"PlaceholderOne"},{"name":"PlaceholderTwo"},{"name":"Swap","fields":[{"name":"swap","type":{"defined":"Swap"}}]}]}},{"name":"SwapLegDeeper","type":{"kind":"enum","variants":[{"name":"Chain","fields":[{"name":"swap_legs","type":{"vec":{"defined":"SwapLegSwap"}}}]},{"name":"Split","fields":[{"name":"split_legs","type":{"vec":{"defined":"SplitLegDeeper"}}}]},{"name":"Swap","fields":[{"name":"swap","type":{"defined":"Swap"}}]}]}},{"name":"SwapLeg","type":{"kind":"enum","variants":[{"name":"Chain","fields":[{"name":"swap_legs","type":{"vec":{"defined":"SwapLegDeeper"}}}]},{"name":"Split","fields":[{"name":"split_legs","type":{"vec":{"defined":"SplitLeg"}}}]},{"name":"Swap","fields":[{"name":"swap","type":{"defined":"Swap"}}]}]}},{"name":"Swap","type":{"kind":"enum","variants":[{"name":"Saber"},{"name":"SaberAddDecimalsDeposit"},{"name":"SaberAddDecimalsWithdraw"},{"name":"TokenSwap"},{"name":"Sencha"},{"name":"Step"},{"name":"Cropper"},{"name":"Raydium"},{"name":"Crema","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"Lifinity"},{"name":"Mercurial"},{"name":"Cykura"},{"name":"Serum","fields":[{"name":"side","type":{"defined":"Side"}}]},{"name":"MarinadeDeposit"},{"name":"MarinadeUnstake"},{"name":"Aldrin","fields":[{"name":"side","type":{"defined":"Side"}}]},{"name":"AldrinV2","fields":[{"name":"side","type":{"defined":"Side"}}]},{"name":"Whirlpool","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"Invariant","fields":[{"name":"x_to_y","type":"bool"}]},{"name":"Meteora"},{"name":"GooseFX"},{"name":"DeltaFi","fields":[{"name":"stable","type":"bool"}]},{"name":"Balansol"},{"name":"MarcoPolo","fields":[{"name":"x_to_y","type":"bool"}]},{"name":"Dradex","fields":[{"name":"side","type":{"defined":"Side"}}]},{"name":"LifinityV2"},{"name":"RaydiumClmm"},{"name":"Openbook","fields":[{"name":"side","type":{"defined":"Side"}}]},{"name":"Phoenix","fields":[{"name":"side","type":{"defined":"Side"}}]}]}}],"events":[{"name":"Swap","fields":[{"name":"amm","type":"publicKey","index":false},{"name":"inputMint","type":"publicKey","index":false},{"name":"inputAmount","type":"u64","index":false},{"name":"outputMint","type":"publicKey","index":false},{"name":"outputAmount","type":"u64","index":false}]},{"name":"Fee","fields":[{"name":"account","type":"publicKey","index":false},{"name":"mint","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"EmptyRoute","msg":"Empty route"},{"code":6001,"name":"SlippageToleranceExceeded","msg":"Slippage tolerance exceeded"},{"code":6002,"name":"InvalidCalculation","msg":"Invalid calculation"},{"code":6003,"name":"MissingPlatformFeeAccount","msg":"Missing platform fee account"},{"code":6004,"name":"InvalidSlippage","msg":"Invalid slippage"},{"code":6005,"name":"NotEnoughPercent","msg":"Not enough percent to 100"},{"code":6006,"name":"InAmountsStackIsEmpty","msg":"In amounts stack is empty"},{"code":6007,"name":"OutAmountsStackIsEmpty","msg":"Out amounts stack is empty"},{"code":6008,"name":"NotEnoughAccountKeys","msg":"Not Enough Account keys"},{"code":6009,"name":"NonZeroMinimumOutAmountNotSupported","msg":"Non zero minimum out amount not supported"}]} \ No newline at end of file diff --git a/idls/jupiter_v6.JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4.json b/idls/jupiter_v6.JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4.json new file mode 100644 index 0000000..f385542 --- /dev/null +++ b/idls/jupiter_v6.JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4.json @@ -0,0 +1 @@ +{"address":"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4","metadata":{"name":"jupiter","version":"0.1.0","spec":"0.1.0","description":"Jupiter aggregator program"},"instructions":[{"name":"claim","discriminator":[62,198,214,193,213,159,108,210],"accounts":[{"name":"wallet","writable":true,"address":"7JQeyNK55fkUPUmEotupBFpiBGpgEQYLe8Ht1VdSfxcP"},{"name":"program_authority","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"id","type":"u8"}],"returns":"u64"},{"name":"claim_token","discriminator":[116,206,27,191,166,19,0,73],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"wallet","address":"7JQeyNK55fkUPUmEotupBFpiBGpgEQYLe8Ht1VdSfxcP"},{"name":"program_authority"},{"name":"program_token_account","writable":true},{"name":"destination_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"wallet"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"mint"},{"name":"token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"id","type":"u8"}],"returns":"u64"},{"name":"close_token","discriminator":[26,74,236,151,104,64,183,249],"accounts":[{"name":"operator","signer":true,"address":"9RAufBfjGQjDfrwxeyKmZWPADHSb8HcoqCdrmpqvCr1g"},{"name":"wallet","writable":true,"address":"7JQeyNK55fkUPUmEotupBFpiBGpgEQYLe8Ht1VdSfxcP"},{"name":"program_authority"},{"name":"program_token_account","writable":true},{"name":"mint","writable":true},{"name":"token_program"}],"args":[{"name":"id","type":"u8"},{"name":"burn_all","type":"bool"}]},{"name":"create_token_ledger","discriminator":[232,242,197,253,240,143,129,52],"accounts":[{"name":"token_ledger","writable":true,"signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_token_account","discriminator":[147,241,123,100,244,132,174,118],"accounts":[{"name":"token_account","writable":true},{"name":"user","writable":true,"signer":true},{"name":"mint"},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bump","type":"u8"}]},{"name":"close_wsol_token_account","discriminator":[203,129,103,133,197,125,107,86],"accounts":[{"name":"token_account","writable":true},{"name":"user","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"exact_out_route","discriminator":[208,51,239,151,123,43,237,92],"accounts":[{"name":"token_program"},{"name":"user_transfer_authority","signer":true},{"name":"user_source_token_account","writable":true},{"name":"user_destination_token_account","writable":true},{"name":"destination_token_account","writable":true,"optional":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"token_2022_program","optional":true},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStep"}}}},{"name":"out_amount","type":"u64"},{"name":"quoted_in_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u8"}],"returns":"u64"},{"name":"route","discriminator":[229,23,203,151,122,227,173,42],"accounts":[{"name":"token_program"},{"name":"user_transfer_authority","signer":true},{"name":"user_source_token_account","writable":true},{"name":"user_destination_token_account","writable":true},{"name":"destination_token_account","writable":true,"optional":true},{"name":"destination_mint"},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStep"}}}},{"name":"in_amount","type":"u64"},{"name":"quoted_out_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u8"}],"returns":"u64"},{"name":"route_with_token_ledger","discriminator":[150,86,71,116,167,93,14,104],"accounts":[{"name":"token_program"},{"name":"user_transfer_authority","signer":true},{"name":"user_source_token_account","writable":true},{"name":"user_destination_token_account","writable":true},{"name":"destination_token_account","writable":true,"optional":true},{"name":"destination_mint"},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"token_ledger"},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStep"}}}},{"name":"quoted_out_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u8"}],"returns":"u64"},{"name":"set_token_ledger","discriminator":[228,85,185,112,78,79,77,2],"accounts":[{"name":"token_ledger","writable":true},{"name":"token_account"}],"args":[]},{"name":"shared_accounts_exact_out_route","discriminator":[176,209,105,168,154,125,69,62],"accounts":[{"name":"token_program"},{"name":"program_authority"},{"name":"user_transfer_authority","signer":true},{"name":"source_token_account","writable":true},{"name":"program_source_token_account","writable":true},{"name":"program_destination_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"token_2022_program","optional":true},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"id","type":"u8"},{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStep"}}}},{"name":"out_amount","type":"u64"},{"name":"quoted_in_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u8"}],"returns":"u64"},{"name":"shared_accounts_route","discriminator":[193,32,155,51,65,214,156,129],"accounts":[{"name":"token_program"},{"name":"program_authority"},{"name":"user_transfer_authority","signer":true},{"name":"source_token_account","writable":true},{"name":"program_source_token_account","writable":true},{"name":"program_destination_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"token_2022_program","optional":true},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"id","type":"u8"},{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStep"}}}},{"name":"in_amount","type":"u64"},{"name":"quoted_out_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u8"}],"returns":"u64"},{"name":"shared_accounts_route_with_token_ledger","discriminator":[230,121,143,80,119,159,106,170],"accounts":[{"name":"token_program"},{"name":"program_authority"},{"name":"user_transfer_authority","signer":true},{"name":"source_token_account","writable":true},{"name":"program_source_token_account","writable":true},{"name":"program_destination_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"token_2022_program","optional":true},{"name":"token_ledger"},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"id","type":"u8"},{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStep"}}}},{"name":"quoted_out_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u8"}],"returns":"u64"},{"name":"exact_out_route_v2","discriminator":[157,138,184,82,21,244,243,36],"accounts":[{"name":"user_transfer_authority","signer":true},{"name":"user_source_token_account","writable":true},{"name":"user_destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"source_token_program"},{"name":"destination_token_program"},{"name":"destination_token_account","writable":true,"optional":true},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"out_amount","type":"u64"},{"name":"quoted_in_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u16"},{"name":"positive_slippage_bps","type":"u16"},{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStepV2"}}}}],"returns":"u64"},{"name":"route_v2","discriminator":[187,100,250,204,49,196,175,20],"accounts":[{"name":"user_transfer_authority","signer":true},{"name":"user_source_token_account","writable":true},{"name":"user_destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"source_token_program"},{"name":"destination_token_program"},{"name":"destination_token_account","writable":true,"optional":true},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"in_amount","type":"u64"},{"name":"quoted_out_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u16"},{"name":"positive_slippage_bps","type":"u16"},{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStepV2"}}}}],"returns":"u64"},{"name":"shared_accounts_exact_out_route_v2","discriminator":[53,96,229,202,216,187,250,24],"accounts":[{"name":"program_authority"},{"name":"user_transfer_authority","signer":true},{"name":"source_token_account","writable":true},{"name":"program_source_token_account","writable":true},{"name":"program_destination_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"source_token_program"},{"name":"destination_token_program"},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"id","type":"u8"},{"name":"out_amount","type":"u64"},{"name":"quoted_in_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u16"},{"name":"positive_slippage_bps","type":"u16"},{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStepV2"}}}}],"returns":"u64"},{"name":"shared_accounts_route_v2","discriminator":[209,152,83,147,124,254,216,233],"accounts":[{"name":"program_authority"},{"name":"user_transfer_authority","signer":true},{"name":"source_token_account","writable":true},{"name":"program_source_token_account","writable":true},{"name":"program_destination_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"source_token_program"},{"name":"destination_token_program"},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"id","type":"u8"},{"name":"in_amount","type":"u64"},{"name":"quoted_out_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u16"},{"name":"positive_slippage_bps","type":"u16"},{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStepV2"}}}}],"returns":"u64"}],"accounts":[{"name":"TokenLedger","discriminator":[156,247,9,188,54,108,85,77]}],"events":[{"name":"FeeEvent","discriminator":[73,79,78,127,184,213,13,220]},{"name":"SwapEvent","discriminator":[64,198,205,232,38,8,113,226]},{"name":"SwapsEvent","discriminator":[152,47,78,235,192,96,110,106]},{"name":"CandidateSwapResults","discriminator":[45,9,244,30,229,52,168,123]},{"name":"CandidateSwapQuoteError","discriminator":[248,134,37,55,145,177,114,79]},{"name":"BestSwapOutAmountViolation","discriminator":[124,66,196,51,218,173,46,93]}],"errors":[{"code":6000,"name":"EmptyRoute","msg":"Empty route"},{"code":6001,"name":"SlippageToleranceExceeded","msg":"Slippage tolerance exceeded"},{"code":6002,"name":"InvalidCalculation","msg":"Invalid calculation"},{"code":6003,"name":"MissingPlatformFeeAccount","msg":"Missing platform fee account"},{"code":6004,"name":"InvalidSlippage","msg":"Invalid slippage"},{"code":6005,"name":"NotEnoughPercent","msg":"Not enough percent to 100"},{"code":6006,"name":"InvalidInputIndex","msg":"Token input index is invalid"},{"code":6007,"name":"InvalidOutputIndex","msg":"Token output index is invalid"},{"code":6008,"name":"NotEnoughAccountKeys","msg":"Not Enough Account keys"},{"code":6009,"name":"NonZeroMinimumOutAmountNotSupported","msg":"Non zero minimum out amount not supported"},{"code":6010,"name":"InvalidRoutePlan","msg":"Invalid route plan"},{"code":6011,"name":"InvalidReferralAuthority","msg":"Invalid referral authority"},{"code":6012,"name":"LedgerTokenAccountDoesNotMatch","msg":"Token account doesn't match the ledger"},{"code":6013,"name":"InvalidTokenLedger","msg":"Invalid token ledger"},{"code":6014,"name":"IncorrectTokenProgramID","msg":"Token program ID is invalid"},{"code":6015,"name":"TokenProgramNotProvided","msg":"Token program not provided"},{"code":6016,"name":"SwapNotSupported","msg":"Swap not supported"},{"code":6017,"name":"ExactOutAmountNotMatched","msg":"Exact out amount doesn't match"},{"code":6018,"name":"SourceAndDestinationMintCannotBeTheSame","msg":"Source mint and destination mint cannot the same"},{"code":6019,"name":"InvalidMint","msg":"Invalid mint"},{"code":6020,"name":"InvalidProgramAuthority","msg":"Invalid program authority"},{"code":6021,"name":"InvalidOutputTokenAccount","msg":"Invalid output token account"},{"code":6022,"name":"InvalidFeeWallet","msg":"Invalid fee wallet"},{"code":6023,"name":"InvalidAuthority","msg":"Invalid authority"},{"code":6024,"name":"InsufficientFunds","msg":"Insufficient funds"},{"code":6025,"name":"InvalidTokenAccount","msg":"Invalid token account"},{"code":6026,"name":"BondingCurveAlreadyCompleted","msg":"Bonding curve already completed"}],"types":[{"name":"FeeEvent","type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"RemainingAccountsInfo","type":{"kind":"struct","fields":[{"name":"slices","type":{"vec":{"defined":{"name":"RemainingAccountsSlice"}}}}]}},{"name":"RemainingAccountsSlice","type":{"kind":"struct","fields":[{"name":"accounts_type","type":"u8"},{"name":"length","type":"u8"}]}},{"name":"AccountsType","type":{"kind":"enum","variants":[{"name":"TransferHookA"},{"name":"TransferHookB"},{"name":"TransferHookReward"},{"name":"TransferHookInput"},{"name":"TransferHookIntermediate"},{"name":"TransferHookOutput"},{"name":"SupplementalTickArrays"},{"name":"SupplementalTickArraysOne"},{"name":"SupplementalTickArraysTwo"}]}},{"name":"DefiTunaAccountsType","type":{"kind":"enum","variants":[{"name":"TransferHookA"},{"name":"TransferHookB"},{"name":"TransferHookInput"},{"name":"TransferHookIntermediate"},{"name":"TransferHookOutput"},{"name":"SupplementalTickArrays"},{"name":"SupplementalTickArraysOne"},{"name":"SupplementalTickArraysTwo"}]}},{"name":"RoutePlanStep","type":{"kind":"struct","fields":[{"name":"swap","type":{"defined":{"name":"Swap"}}},{"name":"percent","type":"u8"},{"name":"input_index","type":"u8"},{"name":"output_index","type":"u8"}]}},{"name":"RoutePlanStepV2","type":{"kind":"struct","fields":[{"name":"swap","type":{"defined":{"name":"Swap"}}},{"name":"bps","type":"u16"},{"name":"input_index","type":"u8"},{"name":"output_index","type":"u8"}]}},{"name":"Side","type":{"kind":"enum","variants":[{"name":"Bid"},{"name":"Ask"}]}},{"name":"BisonFiPredictSide","type":{"kind":"enum","variants":[{"name":"Yes"},{"name":"No"}]}},{"name":"Swap","type":{"kind":"enum","variants":[{"name":"Saber"},{"name":"SaberAddDecimalsDeposit"},{"name":"SaberAddDecimalsWithdraw"},{"name":"TokenSwap"},{"name":"Sencha"},{"name":"Step"},{"name":"Cropper"},{"name":"Raydium"},{"name":"Crema","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"Lifinity"},{"name":"Mercurial"},{"name":"Cykura"},{"name":"Serum","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"MarinadeDeposit"},{"name":"MarinadeUnstake"},{"name":"Aldrin","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"AldrinV2","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"Whirlpool","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"Invariant","fields":[{"name":"x_to_y","type":"bool"}]},{"name":"Meteora"},{"name":"GooseFX"},{"name":"DeltaFi","fields":[{"name":"stable","type":"bool"}]},{"name":"Balansol"},{"name":"MarcoPolo","fields":[{"name":"x_to_y","type":"bool"}]},{"name":"Dradex","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"LifinityV2"},{"name":"RaydiumClmm"},{"name":"Openbook","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"Phoenix","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"Symmetry","fields":[{"name":"from_token_id","type":"u64"},{"name":"to_token_id","type":"u64"}]},{"name":"TokenSwapV2"},{"name":"HeliumTreasuryManagementRedeemV0"},{"name":"StakeDexStakeWrappedSol"},{"name":"StakeDexSwapViaStake","fields":[{"name":"bridge_stake_seed","type":"u32"}]},{"name":"GooseFXV2"},{"name":"Perps"},{"name":"PerpsAddLiquidity"},{"name":"PerpsRemoveLiquidity"},{"name":"MeteoraDlmm"},{"name":"OpenBookV2","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"RaydiumClmmV2"},{"name":"StakeDexPrefundWithdrawStakeAndDepositStake","fields":[{"name":"bridge_stake_seed","type":"u32"}]},{"name":"Clone","fields":[{"name":"pool_index","type":"u8"},{"name":"quantity_is_input","type":"bool"},{"name":"quantity_is_collateral","type":"bool"}]},{"name":"SanctumS","fields":[{"name":"src_lst_value_calc_accs","type":"u8"},{"name":"dst_lst_value_calc_accs","type":"u8"},{"name":"src_lst_index","type":"u32"},{"name":"dst_lst_index","type":"u32"}]},{"name":"SanctumSAddLiquidity","fields":[{"name":"lst_value_calc_accs","type":"u8"},{"name":"lst_index","type":"u32"}]},{"name":"SanctumSRemoveLiquidity","fields":[{"name":"lst_value_calc_accs","type":"u8"},{"name":"lst_index","type":"u32"}]},{"name":"RaydiumCP"},{"name":"WhirlpoolSwapV2","fields":[{"name":"a_to_b","type":"bool"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"OneIntro"},{"name":"PumpWrappedBuy"},{"name":"PumpWrappedSell"},{"name":"PerpsV2"},{"name":"PerpsV2AddLiquidity"},{"name":"PerpsV2RemoveLiquidity"},{"name":"MoonshotWrappedBuy"},{"name":"MoonshotWrappedSell"},{"name":"StabbleStableSwap"},{"name":"StabbleWeightedSwap"},{"name":"Obric","fields":[{"name":"x_to_y","type":"bool"}]},{"name":"FoxBuyFromEstimatedCost"},{"name":"FoxClaimPartial","fields":[{"name":"is_y","type":"bool"}]},{"name":"SolFi","fields":[{"name":"is_quote_to_base","type":"bool"}]},{"name":"SolayerDelegateNoInit"},{"name":"SolayerUndelegateNoInit"},{"name":"TokenMill","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"DaosFunBuy"},{"name":"DaosFunSell"},{"name":"ZeroFi"},{"name":"StakeDexWithdrawWrappedSol"},{"name":"VirtualsBuy"},{"name":"VirtualsSell"},{"name":"Perena","fields":[{"name":"in_index","type":"u8"},{"name":"out_index","type":"u8"}]},{"name":"PumpSwapBuy"},{"name":"PumpSwapSell"},{"name":"Gamma"},{"name":"MeteoraDlmmSwapV2","fields":[{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"Woofi"},{"name":"MeteoraDammV2"},{"name":"MeteoraDynamicBondingCurveSwap"},{"name":"StabbleStableSwapV2"},{"name":"StabbleWeightedSwapV2"},{"name":"RaydiumLaunchlabBuy","fields":[{"name":"share_fee_rate","type":"u64"}]},{"name":"RaydiumLaunchlabSell","fields":[{"name":"share_fee_rate","type":"u64"}]},{"name":"BoopdotfunWrappedBuy"},{"name":"BoopdotfunWrappedSell"},{"name":"Plasma","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"GoonFi","fields":[{"name":"is_bid","type":"bool"},{"name":"blacklist_bump","type":"u8"}]},{"name":"HumidiFi","fields":[{"name":"swap_id","type":"u64"},{"name":"is_base_to_quote","type":"bool"}]},{"name":"MeteoraDynamicBondingCurveSwapWithRemainingAccounts"},{"name":"TesseraV","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"PumpWrappedBuyV2"},{"name":"PumpWrappedSellV2"},{"name":"PumpSwapBuyV2"},{"name":"PumpSwapSellV2"},{"name":"Heaven","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"SolFiV2","fields":[{"name":"is_quote_to_base","type":"bool"}]},{"name":"Aquifer"},{"name":"PumpWrappedBuyV3"},{"name":"PumpWrappedSellV3"},{"name":"PumpSwapBuyV3"},{"name":"PumpSwapSellV3"},{"name":"JupiterLendDeposit"},{"name":"JupiterLendRedeem"},{"name":"DefiTuna","fields":[{"name":"a_to_b","type":"bool"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"AlphaQ","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"RaydiumV2"},{"name":"SarosDlmm","fields":[{"name":"swap_for_y","type":"bool"}]},{"name":"Futarchy","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"MeteoraDammV2WithRemainingAccounts"},{"name":"Obsidian"},{"name":"WhaleStreet","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"DynamicV1","fields":[{"name":"candidate_swaps","type":{"vec":{"defined":{"name":"CandidateSwap"}}}},{"name":"best_position","type":{"option":"u8"}}]},{"name":"PumpWrappedBuyV4"},{"name":"PumpWrappedSellV4"},{"name":"CarrotIssue"},{"name":"CarrotRedeem"},{"name":"Manifest","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"BisonFi","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"HumidiFiV2","fields":[{"name":"swap_id","type":"u64"},{"name":"is_base_to_quote","type":"bool"}]},{"name":"PerenaStar","fields":[{"name":"is_mint","type":"bool"}]},{"name":"JupiterRfqV2","fields":[{"name":"side","type":{"defined":{"name":"Side"}}},{"name":"fill_data","type":"bytes"}]},{"name":"GoonFiV2","fields":[{"name":"is_bid","type":"bool"}]},{"name":"Scorch","fields":[{"name":"swap_id","type":"u128"}]},{"name":"VaultLiquidUnstake","fields":[{"name":"lst_amounts","type":{"array":["u64",5]}},{"name":"seed","type":"u64"}]},{"name":"XOrca"},{"name":"Quantum","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"WhaleStreetV2","fields":[{"name":"side","type":{"defined":{"name":"Side"}}},{"name":"auth_amount_in","type":"u64"},{"name":"auth","type":"u64"}]},{"name":"Riptide","fields":[{"name":"amount_is_token_a","type":"bool"}]},{"name":"RunnerRodeo"},{"name":"TaurusFi","fields":[{"name":"is_base_in","type":"bool"}]},{"name":"Omnipair"},{"name":"MSwap"},{"name":"Hylo","fields":[{"name":"swap_type","type":{"defined":{"name":"HyloSwapType"}}}]},{"name":"VoltrDeposit"},{"name":"VoltrWithdraw"},{"name":"SanctumSV2","fields":[{"name":"src_lst_value_calc_accs","type":"u8"},{"name":"dst_lst_value_calc_accs","type":"u8"},{"name":"src_lst_index","type":"u32"},{"name":"dst_lst_index","type":"u32"}]},{"name":"LemmingsFi","fields":[{"name":"is_base_in","type":"bool"}]},{"name":"ScaleVmmBuy"},{"name":"ScaleVmmSell"},{"name":"ScaleAmmBuy"},{"name":"ScaleAmmSell"},{"name":"BisonFiV2","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"Trends"},{"name":"HumaDeposit"},{"name":"HumaInstantWithdraw"},{"name":"Kipseli","fields":[{"name":"is_base_to_quote","type":"bool"}]},{"name":"DynamicV2","fields":[{"name":"candidate_swaps","type":{"vec":{"defined":{"name":"CandidateSwapWithBps"}}}},{"name":"max_split_quote_calls","type":"u8"},{"name":"max_split_candidates","type":"u8"}]},{"name":"PumpSwapBuyV3WithCashbackClaim"},{"name":"PumpSwapSellV3WithCashbackClaim"},{"name":"PumpWrappedBuyV4WithCashbackClaim"},{"name":"PumpWrappedSellV4WithCashbackClaim"},{"name":"GoonFiV3","fields":[{"name":"is_bid","type":"bool"}]},{"name":"PumpWrappedBuyV5","fields":[{"name":"claim_cashback","type":"bool"}]},{"name":"PumpWrappedSellV5","fields":[{"name":"claim_cashback","type":"bool"}]},{"name":"ZeroFiSwapV2"},{"name":"BisonFiPredict","fields":[{"name":"side","type":{"defined":{"name":"BisonFiPredictSide"}}},{"name":"is_buy","type":"bool"}]},{"name":"ByrealDynamicV3"},{"name":"Flux","fields":[{"name":"swap_id","type":"u64"},{"name":"base_to_quote","type":"bool"}]}]}},{"name":"CandidateSwapWithBps","type":{"kind":"struct","fields":[{"name":"candidate_swap","type":{"defined":{"name":"CandidateSwap"}}},{"name":"bps","type":"u32"}]}},{"name":"CandidateSwap","type":{"kind":"enum","variants":[{"name":"HumidiFi","fields":[{"name":"swap_id","type":"u64"},{"name":"is_base_to_quote","type":"bool"}]},{"name":"TesseraV","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"HumidiFiV2","fields":[{"name":"swap_id","type":"u64"},{"name":"is_base_to_quote","type":"bool"}]},{"name":"RaydiumV2"},{"name":"RaydiumClmm"},{"name":"Whirlpool","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"ZeroFi"},{"name":"BisonFiV2","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"GoonFiV2","fields":[{"name":"is_bid","type":"bool"}]},{"name":"GoonFiV3","fields":[{"name":"is_bid","type":"bool"}]},{"name":"WhirlpoolV2","fields":[{"name":"a_to_b","type":"bool"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"ZeroFiSwapV2"}]}},{"name":"HyloSwapType","type":{"kind":"enum","variants":[{"name":"MintStable"},{"name":"RedeemStable"},{"name":"MintLever"},{"name":"RedeemLever"},{"name":"SwapStableToLever"},{"name":"SwapLeverToStable"},{"name":"StabilityPoolDeposit"},{"name":"StabilityPoolWithdraw"}]}},{"name":"SwapEvent","type":{"kind":"struct","fields":[{"name":"amm","type":"pubkey"},{"name":"input_mint","type":"pubkey"},{"name":"input_amount","type":"u64"},{"name":"output_mint","type":"pubkey"},{"name":"output_amount","type":"u64"}]}},{"name":"SwapEventV2","type":{"kind":"struct","fields":[{"name":"input_mint","type":"pubkey"},{"name":"input_amount","type":"u64"},{"name":"output_mint","type":"pubkey"},{"name":"output_amount","type":"u64"}]}},{"name":"SwapsEvent","type":{"kind":"struct","fields":[{"name":"swap_events","type":{"vec":{"defined":{"name":"SwapEventV2"}}}}]}},{"name":"TokenLedger","type":{"kind":"struct","fields":[{"name":"token_account","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"BestSwapOutAmountViolation","type":{"kind":"struct","fields":[{"name":"expected_out_amount","type":"u64"},{"name":"out_amount","type":"u64"}]}},{"name":"CandidateSwapResult","type":{"kind":"enum","variants":[{"name":"OutAmount","fields":["u64"]},{"name":"ProgramError","fields":["u64"]}]}},{"name":"CandidateSwapResults","type":{"kind":"struct","fields":[{"name":"results","type":{"vec":{"defined":{"name":"CandidateSwapResult"}}}}]}},{"name":"CandidateSwapQuoteError","type":{"kind":"struct","fields":[{"name":"candidate_index","type":"u64"},{"name":"in_amount","type":"u64"},{"name":"error_code","type":"u64"}]}}]} \ No newline at end of file diff --git a/idls/kamino.6LtLpnUFNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc.json b/idls/kamino.6LtLpnUFNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc.json new file mode 100644 index 0000000..eb8500e --- /dev/null +++ b/idls/kamino.6LtLpnUFNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"yvaults","instructions":[{"name":"initializeStrategy","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false,"docs":["Program owner also checked."]},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"sharesMintAuthority","isMut":true,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false}],"args":[{"name":"strategyType","type":"u64"},{"name":"tokenACollateralId","type":"u64"},{"name":"tokenBCollateralId","type":"u64"}]},{"name":"initializeKaminoReward","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"rewardMint","isMut":false,"isSigner":false},{"name":"rewardVault","isMut":true,"isSigner":true},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"kaminoRewardIndex","type":"u64"},{"name":"collateralToken","type":"u64"}]},{"name":"addKaminoRewards","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"rewardMint","isMut":false,"isSigner":false},{"name":"rewardVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"rewardAta","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"kaminoRewardIndex","type":"u64"},{"name":"amount","type":"u64"}]},{"name":"initializeGlobalConfig","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeCollateralInfo","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false},{"name":"collInfo","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateCollateralInfo","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":true,"isSigner":false}],"args":[{"name":"index","type":"u64"},{"name":"mode","type":"u64"},{"name":"value","type":{"array":["u8",32]}}]},{"name":"insertCollateralInfo","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":true,"isSigner":false}],"args":[{"name":"index","type":"u64"},{"name":"params","type":{"defined":"CollateralInfoParams"}}]},{"name":"initializeSharesMetadata","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":false,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"sharesMint","isMut":false,"isSigner":false},{"name":"sharesMetadata","isMut":true,"isSigner":false},{"name":"sharesMintAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"updateSharesMetadata","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":false,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"sharesMint","isMut":false,"isSigner":false},{"name":"sharesMetadata","isMut":true,"isSigner":false},{"name":"sharesMintAuthority","isMut":false,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"updateGlobalConfig","accounts":[{"name":"adminAuthority","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"key","type":"u16"},{"name":"index","type":"u16"},{"name":"value","type":{"array":["u8",32]}}]},{"name":"updateTreasuryFeeVault","accounts":[{"name":"signer","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"feeMint","isMut":false,"isSigner":false},{"name":"treasuryFeeVault","isMut":true,"isSigner":false},{"name":"treasuryFeeVaultAuthority","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"collateralId","type":"u16"}]},{"name":"updateStrategyConfig","accounts":[{"name":"adminAuthority","isMut":false,"isSigner":true},{"name":"newAccount","isMut":false,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"mode","type":"u16"},{"name":"value","type":{"array":["u8",128]}}]},{"name":"updateRewardMapping","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"rewardMint","isMut":false,"isSigner":false},{"name":"rewardVault","isMut":true,"isSigner":true},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"rewardIndex","type":"u8"},{"name":"collateralToken","type":"u8"}]},{"name":"openLiquidityPosition","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"tickArrayLower","isMut":true,"isSigner":false},{"name":"tickArrayUpper","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"positionMint","isMut":true,"isSigner":false},{"name":"positionMetadataAccount","isMut":true,"isSigner":false},{"name":"positionTokenAccount","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"system","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram2022","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"memoProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"poolProgram","isMut":false,"isSigner":false},{"name":"oldTickArrayLowerOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"oldTickArrayUpperOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"oldPositionOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"oldPositionMintOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"oldPositionTokenAccountOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"poolTokenVaultA","isMut":true,"isSigner":false},{"name":"poolTokenVaultB","isMut":true,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false,"isOptional":true},{"name":"consensusAccount","isMut":false,"isSigner":false}],"args":[{"name":"tickLowerIndex","type":"i64"},{"name":"tickUpperIndex","type":"i64"},{"name":"bump","type":"u8"}]},{"name":"closeStrategy","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"oldPositionOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"oldPositionMintOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"oldPositionTokenAccountOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"oldTickArrayLowerOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"oldTickArrayUpperOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"userTokenAAta","isMut":true,"isSigner":false},{"name":"userTokenBAta","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":true,"isSigner":false},{"name":"tokenBMint","isMut":true,"isSigner":false},{"name":"reward0Vault","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"reward1Vault","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"reward2Vault","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"kaminoReward0Vault","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"kaminoReward1Vault","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"kaminoReward2Vault","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"userReward0Ata","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"userReward1Ata","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"userReward2Ata","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"userKaminoReward0Ata","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"userKaminoReward1Ata","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"userKaminoReward2Ata","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"poolProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"system","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false,"isOptional":true}],"args":[]},{"name":"deposit","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"tickArrayLower","isMut":false,"isSigner":false},{"name":"tickArrayUpper","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"tokenAAta","isMut":true,"isSigner":false},{"name":"tokenBAta","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"sharesMintAuthority","isMut":false,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"tokenMaxA","type":"u64"},{"name":"tokenMaxB","type":"u64"}]},{"name":"invest","accounts":[{"name":"actionsAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"memoProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram2022","isMut":false,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"raydiumProtocolPositionOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"positionTokenAccount","isMut":true,"isSigner":false},{"name":"poolTokenVaultA","isMut":true,"isSigner":false},{"name":"poolTokenVaultB","isMut":true,"isSigner":false},{"name":"tickArrayLower","isMut":true,"isSigner":false},{"name":"tickArrayUpper","isMut":true,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"poolProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false,"isOptional":true}],"args":[{"name":"referencePriceTick","type":"i32"}]},{"name":"depositAndInvest","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false,"docs":["check that the pool is owned either by orca or by raydium"]},{"name":"position","isMut":true,"isSigner":false},{"name":"raydiumProtocolPositionOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"positionTokenAccount","isMut":true,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"poolTokenVaultA","isMut":true,"isSigner":false},{"name":"poolTokenVaultB","isMut":true,"isSigner":false},{"name":"tickArrayLower","isMut":true,"isSigner":false},{"name":"tickArrayUpper","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"tokenAAta","isMut":true,"isSigner":false},{"name":"tokenBAta","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"sharesMintAuthority","isMut":false,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram2022","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"memoProgram","isMut":false,"isSigner":false},{"name":"poolProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false,"isOptional":true}],"args":[{"name":"tokenMaxA","type":"u64"},{"name":"tokenMaxB","type":"u64"}]},{"name":"withdraw","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"tickArrayLower","isMut":true,"isSigner":false},{"name":"tickArrayUpper","isMut":true,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"poolTokenVaultA","isMut":true,"isSigner":false},{"name":"poolTokenVaultB","isMut":true,"isSigner":false},{"name":"tokenAAta","isMut":true,"isSigner":false},{"name":"tokenBAta","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"treasuryFeeTokenAVault","isMut":true,"isSigner":false},{"name":"treasuryFeeTokenBVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram2022","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"memoProgram","isMut":false,"isSigner":false},{"name":"positionTokenAccount","isMut":true,"isSigner":false},{"name":"poolProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false,"isOptional":true}],"args":[{"name":"sharesAmount","type":"u64"}]},{"name":"executiveWithdraw","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"raydiumProtocolPositionOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"positionTokenAccount","isMut":false,"isSigner":false},{"name":"tickArrayLower","isMut":true,"isSigner":false},{"name":"tickArrayUpper","isMut":true,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"poolTokenVaultA","isMut":true,"isSigner":false},{"name":"poolTokenVaultB","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"memoProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram2022","isMut":false,"isSigner":false},{"name":"poolProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false,"isOptional":true}],"args":[{"name":"action","type":"u8"}]},{"name":"collectFeesAndRewards","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"tickArrayLower","isMut":true,"isSigner":false},{"name":"tickArrayUpper","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"raydiumProtocolPositionOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"positionTokenAccount","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"poolTokenVaultA","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"poolTokenVaultB","isMut":true,"isSigner":false},{"name":"treasuryFeeTokenAVault","isMut":true,"isSigner":false},{"name":"treasuryFeeTokenBVault","isMut":true,"isSigner":false},{"name":"treasuryFeeVaultAuthority","isMut":false,"isSigner":false},{"name":"reward0Vault","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"reward1Vault","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"reward2Vault","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"poolRewardVault0","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"poolRewardVault1","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"poolRewardVault2","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"memoProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram2022","isMut":false,"isSigner":false},{"name":"poolProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false,"isOptional":true}],"args":[]},{"name":"swapRewards","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"rewardVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"treasuryFeeTokenAVault","isMut":true,"isSigner":false},{"name":"treasuryFeeTokenBVault","isMut":true,"isSigner":false},{"name":"treasuryFeeVaultAuthority","isMut":false,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"rewardMint","isMut":false,"isSigner":false},{"name":"userTokenAAta","isMut":true,"isSigner":false},{"name":"userTokenBAta","isMut":true,"isSigner":false},{"name":"userRewardTokenAccount","isMut":true,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"scopePricesReward","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"rewardTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"tokenAIn","type":"u64"},{"name":"tokenBIn","type":"u64"},{"name":"rewardIndex","type":"u64"},{"name":"rewardCollateralId","type":"u64"},{"name":"minCollateralTokenOut","type":"u64"}]},{"name":"checkExpectedVaultsBalances","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"tokenAAta","isMut":false,"isSigner":false},{"name":"tokenBAta","isMut":false,"isSigner":false}],"args":[{"name":"tokenAAtaBalance","type":"u64"},{"name":"tokenBAtaBalance","type":"u64"}]},{"name":"singleTokenDepositAndInvestWithMin","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false,"docs":["check that the pool is owned either by orca or by raydium"]},{"name":"position","isMut":true,"isSigner":false},{"name":"raydiumProtocolPositionOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"positionTokenAccount","isMut":true,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"poolTokenVaultA","isMut":true,"isSigner":false},{"name":"poolTokenVaultB","isMut":true,"isSigner":false},{"name":"tickArrayLower","isMut":true,"isSigner":false},{"name":"tickArrayUpper","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"tokenAAta","isMut":true,"isSigner":false},{"name":"tokenBAta","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"sharesMintAuthority","isMut":false,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram2022","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"memoProgram","isMut":false,"isSigner":false},{"name":"poolProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false,"isOptional":true}],"args":[{"name":"tokenAMinPostDepositBalance","type":"u64"},{"name":"tokenBMinPostDepositBalance","type":"u64"}]},{"name":"singleTokenDepositWithMin","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"tickArrayLower","isMut":false,"isSigner":false},{"name":"tickArrayUpper","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"tokenAAta","isMut":true,"isSigner":false},{"name":"tokenBAta","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"sharesMintAuthority","isMut":false,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"tokenAMinPostDepositBalance","type":"u64"},{"name":"tokenBMinPostDepositBalance","type":"u64"}]},{"name":"flashSwapUnevenVaultsStart","docs":["Start of a Flash swap uneven vaults.","","This needs to be the first instruction of the transaction or preceded only by a","ComputeBudget.","","This ix has to be paired with a `flash_swap_uneven_vaults_end` (`FlashSwapUnevenVaultsEnd`)","as the last instruction of the transaction. No other instruction targeted the program is","allowed.","The instructions between the start and end instructions are expected to perform the swap."],"accounts":[{"name":"swapper","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"tokenAAta","isMut":true,"isSigner":false},{"name":"tokenBAta","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"tickArrayLower","isMut":false,"isSigner":false},{"name":"tickArrayUpper","isMut":false,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false},{"name":"consensusAccount","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"aToB","type":"bool"}]},{"name":"flashSwapUnevenVaultsEnd","docs":["End of Flash swap uneven vaults.","","See [`flash_swap_uneven_vaults_start`] for details.","","Warning: This instruction is allowed to be used independently from","`FlashSwapUnevenVaultsStart` and shall not perform any operation","that can be exploited when used alone."],"accounts":[{"name":"swapper","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"tokenAAta","isMut":true,"isSigner":false},{"name":"tokenBAta","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"tickArrayLower","isMut":false,"isSigner":false},{"name":"tickArrayUpper","isMut":false,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false},{"name":"consensusAccount","isMut":false,"isSigner":false}],"args":[{"name":"minRepayAmount","type":"u64"},{"name":"amountToLeaveToUser","type":"u64"},{"name":"aToB","type":"bool"}]},{"name":"emergencySwap","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"poolTokenVaultA","isMut":true,"isSigner":false},{"name":"poolTokenVaultB","isMut":true,"isSigner":false},{"name":"tickArray0","isMut":true,"isSigner":false,"docs":["Payer must send this correctly."]},{"name":"tickArray1","isMut":true,"isSigner":false,"docs":["Payer must send this correctly."]},{"name":"tickArray2","isMut":true,"isSigner":false,"docs":["Payer must send this correctly."]},{"name":"oracle","isMut":true,"isSigner":false},{"name":"poolProgram","isMut":false,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"memoProgram","isMut":false,"isSigner":false}],"args":[{"name":"aToB","type":"bool"},{"name":"targetLimitBps","type":"u64"}]},{"name":"withdrawFromTreasury","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"treasuryFeeVault","isMut":true,"isSigner":false},{"name":"treasuryFeeVaultAuthority","isMut":true,"isSigner":false},{"name":"tokenAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"permisionlessWithdrawFromTreasury","accounts":[{"name":"signer","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"treasuryFeeVault","isMut":true,"isSigner":false},{"name":"treasuryFeeVaultAuthority","isMut":true,"isSigner":false},{"name":"tokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"withdrawFromTopup","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"topupVault","isMut":true,"isSigner":false},{"name":"system","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"changePool","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"oldPosition","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"newPool","isMut":false,"isSigner":false},{"name":"strategyRewardVault0OrBaseVaultAuthority","isMut":false,"isSigner":false},{"name":"strategyRewardVault1OrBaseVaultAuthority","isMut":false,"isSigner":false},{"name":"strategyRewardVault2OrBaseVaultAuthority","isMut":false,"isSigner":false}],"args":[]},{"name":"closeProgramAccount","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"program","isMut":false,"isSigner":false},{"name":"programData","isMut":false,"isSigner":false},{"name":"closingAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"orcaSwap","accounts":[{"name":"funder","isMut":true,"isSigner":true},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"memoProgram","isMut":false,"isSigner":false},{"name":"tokenAuthority","isMut":false,"isSigner":false},{"name":"whirlpool","isMut":false,"isSigner":false},{"name":"tokenOwnerAccountA","isMut":false,"isSigner":false},{"name":"tokenVaultA","isMut":false,"isSigner":false},{"name":"tokenOwnerAccountB","isMut":false,"isSigner":false},{"name":"tokenVaultB","isMut":false,"isSigner":false},{"name":"tokenMintA","isMut":false,"isSigner":false},{"name":"tokenMintB","isMut":false,"isSigner":false},{"name":"tickArray0","isMut":false,"isSigner":false},{"name":"tickArray1","isMut":false,"isSigner":false},{"name":"tickArray2","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"whirlpoolProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"otherAmountThreshold","type":"u64"},{"name":"sqrtPriceLimit","type":"u128"},{"name":"amountSpecifiedIsInput","type":"bool"},{"name":"aToB","type":"bool"}]},{"name":"signTerms","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"ownerSignatureState","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"signature","type":{"array":["u8",64]}}]},{"name":"updateStrategyAdmin","accounts":[{"name":"pendingAdmin","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false}],"args":[]},{"name":"resizeTokenInfos","accounts":[{"name":"signer","isMut":true,"isSigner":true},{"name":"tokenInfos","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"deprecateCollateralInfo","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":true,"isSigner":false}],"args":[{"name":"index","type":"u64"}]}],"accounts":[{"name":"Whirlpool","docs":["External types"],"type":{"kind":"struct","fields":[{"name":"whirlpoolsConfig","type":"publicKey"},{"name":"whirlpoolBump","type":{"array":["u8",1]}},{"name":"tickSpacing","type":"u16"},{"name":"tickSpacingSeed","type":{"array":["u8",2]}},{"name":"feeRate","type":"u16"},{"name":"protocolFeeRate","type":"u16"},{"name":"liquidity","type":"u128"},{"name":"sqrtPrice","type":"u128"},{"name":"tickCurrentIndex","type":"i32"},{"name":"protocolFeeOwedA","type":"u64"},{"name":"protocolFeeOwedB","type":"u64"},{"name":"tokenMintA","type":"publicKey"},{"name":"tokenVaultA","type":"publicKey"},{"name":"feeGrowthGlobalA","type":"u128"},{"name":"tokenMintB","type":"publicKey"},{"name":"tokenVaultB","type":"publicKey"},{"name":"feeGrowthGlobalB","type":"u128"},{"name":"rewardLastUpdatedTimestamp","type":"u64"},{"name":"rewardInfos","type":{"array":[{"defined":"WhirlpoolRewardInfo"},3]}}]}},{"name":"Position","type":{"kind":"struct","fields":[{"name":"whirlpool","type":"publicKey"},{"name":"positionMint","type":"publicKey"},{"name":"liquidity","type":"u128"},{"name":"tickLowerIndex","type":"i32"},{"name":"tickUpperIndex","type":"i32"},{"name":"feeGrowthCheckpointA","type":"u128"},{"name":"feeOwedA","type":"u64"},{"name":"feeGrowthCheckpointB","type":"u128"},{"name":"feeOwedB","type":"u64"},{"name":"rewardInfos","type":{"array":[{"defined":"PositionRewardInfo"},3]}}]}},{"name":"PoolState","type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"ammConfig","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"tokenMint0","docs":["Token pair of the pool, where token_mint_0 address < token_mint_1 address"],"type":"publicKey"},{"name":"tokenMint1","type":"publicKey"},{"name":"tokenVault0","docs":["Token pair vault"],"type":"publicKey"},{"name":"tokenVault1","type":"publicKey"},{"name":"observationKey","docs":["observation account key"],"type":"publicKey"},{"name":"mintDecimals0","docs":["mint0 and mint1 decimals"],"type":"u8"},{"name":"mintDecimals1","type":"u8"},{"name":"tickSpacing","docs":["The minimum number of ticks between initialized ticks"],"type":"u16"},{"name":"liquidity","docs":["The currently in range liquidity available to the pool."],"type":"u128"},{"name":"sqrtPriceX64","docs":["The current price of the pool as a sqrt(token_1/token_0) Q64.64 value"],"type":"u128"},{"name":"tickCurrent","docs":["The current tick of the pool, i.e. according to the last tick transition that was run."],"type":"i32"},{"name":"observationIndex","docs":["the most-recently updated index of the observations array"],"type":"u16"},{"name":"observationUpdateDuration","type":"u16"},{"name":"feeGrowthGlobal0X64","docs":["The fee growth as a Q64.64 number, i.e. fees of token_0 and token_1 collected per","unit of liquidity for the entire life of the pool."],"type":"u128"},{"name":"feeGrowthGlobal1X64","type":"u128"},{"name":"protocolFeesToken0","docs":["The amounts of token_0 and token_1 that are owed to the protocol."],"type":"u64"},{"name":"protocolFeesToken1","type":"u64"},{"name":"swapInAmountToken0","docs":["The amounts in and out of swap token_0 and token_1"],"type":"u128"},{"name":"swapOutAmountToken1","type":"u128"},{"name":"swapInAmountToken1","type":"u128"},{"name":"swapOutAmountToken0","type":"u128"},{"name":"status","docs":["Bitwise representation of the state of the pool","bit0, 1: disable open position and increase liquidity, 0: normal","bit1, 1: disable decrease liquidity, 0: normal","bit2, 1: disable collect fee, 0: normal","bit3, 1: disable collect reward, 0: normal","bit4, 1: disable swap, 0: normal"],"type":"u8"},{"name":"padding","docs":["Leave blank for future use"],"type":{"array":["u8",7]}},{"name":"rewardInfos","type":{"array":[{"defined":"RewardInfo"},3]}},{"name":"tickArrayBitmap","docs":["Packed initialized tick array state"],"type":{"array":["u64",16]}},{"name":"totalFeesToken0","docs":["except protocol_fee and fund_fee"],"type":"u64"},{"name":"totalFeesClaimedToken0","docs":["except protocol_fee and fund_fee"],"type":"u64"},{"name":"totalFeesToken1","type":"u64"},{"name":"totalFeesClaimedToken1","type":"u64"},{"name":"fundFeesToken0","type":"u64"},{"name":"fundFeesToken1","type":"u64"},{"name":"openTime","type":"u64"},{"name":"padding1","type":{"array":["u64",25]}},{"name":"padding2","type":{"array":["u64",32]}}]}},{"name":"PersonalPositionState","type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"nftMint","docs":["Mint address of the tokenized position"],"type":"publicKey"},{"name":"poolId","docs":["The ID of the pool with which this token is connected"],"type":"publicKey"},{"name":"tickLowerIndex","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tickUpperIndex","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"feeGrowthInside0LastX64","docs":["The token_0 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"feeGrowthInside1LastX64","docs":["The token_1 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"tokenFeesOwed0","docs":["The fees owed to the position owner in token_0, as of the last computation"],"type":"u64"},{"name":"tokenFeesOwed1","docs":["The fees owed to the position owner in token_1, as of the last computation"],"type":"u64"},{"name":"rewardInfos","type":{"array":[{"defined":"PositionRewardInfo"},3]}},{"name":"padding","type":{"array":["u64",8]}}]}},{"name":"ProtocolPositionState","type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"poolId","docs":["The ID of the pool with which this token is connected"],"type":"publicKey"},{"name":"tickLowerIndex","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tickUpperIndex","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"feeGrowthInside0LastX64","docs":["The token_0 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"feeGrowthInside1LastX64","docs":["The token_1 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"tokenFeesOwed0","docs":["The fees owed to the position owner in token_0"],"type":"u64"},{"name":"tokenFeesOwed1","docs":["The fees owed to the position owner in token_1"],"type":"u64"},{"name":"rewardGrowthInside","docs":["The reward growth per unit of liquidity as of the last update to liquidity"],"type":{"array":["u128",3]}},{"name":"padding","type":{"array":["u64",8]}}]}},{"name":"WhirlpoolStrategy","type":{"kind":"struct","fields":[{"name":"adminAuthority","type":"publicKey"},{"name":"globalConfig","type":"publicKey"},{"name":"baseVaultAuthority","type":"publicKey"},{"name":"baseVaultAuthorityBump","type":"u64"},{"name":"pool","type":"publicKey"},{"name":"poolTokenVaultA","type":"publicKey"},{"name":"poolTokenVaultB","type":"publicKey"},{"name":"tickArrayLower","type":"publicKey"},{"name":"tickArrayUpper","type":"publicKey"},{"name":"position","type":"publicKey"},{"name":"positionMint","type":"publicKey"},{"name":"positionMetadata","type":"publicKey"},{"name":"positionTokenAccount","type":"publicKey"},{"name":"tokenAVault","type":"publicKey"},{"name":"tokenBVault","type":"publicKey"},{"name":"deprecated0","type":{"array":["publicKey",2]}},{"name":"deprecated1","type":{"array":["u64",2]}},{"name":"tokenAMint","type":"publicKey"},{"name":"tokenBMint","type":"publicKey"},{"name":"tokenAMintDecimals","type":"u64"},{"name":"tokenBMintDecimals","type":"u64"},{"name":"tokenAAmounts","type":"u64"},{"name":"tokenBAmounts","type":"u64"},{"name":"tokenACollateralId","type":"u64"},{"name":"tokenBCollateralId","type":"u64"},{"name":"deprecated2","type":"publicKey"},{"name":"deprecated3","type":"publicKey"},{"name":"sharesMint","type":"publicKey"},{"name":"sharesMintDecimals","type":"u64"},{"name":"sharesMintAuthority","type":"publicKey"},{"name":"sharesMintAuthorityBump","type":"u64"},{"name":"sharesIssued","type":"u64"},{"name":"status","type":"u64"},{"name":"reward0Amount","type":"u64"},{"name":"reward0Vault","type":"publicKey"},{"name":"reward0CollateralId","type":"u64"},{"name":"reward0Decimals","type":"u64"},{"name":"reward1Amount","type":"u64"},{"name":"reward1Vault","type":"publicKey"},{"name":"reward1CollateralId","type":"u64"},{"name":"reward1Decimals","type":"u64"},{"name":"reward2Amount","type":"u64"},{"name":"reward2Vault","type":"publicKey"},{"name":"reward2CollateralId","type":"u64"},{"name":"reward2Decimals","type":"u64"},{"name":"depositCapUsd","type":"u64"},{"name":"feesACumulative","type":"u64"},{"name":"feesBCumulative","type":"u64"},{"name":"reward0AmountCumulative","type":"u64"},{"name":"reward1AmountCumulative","type":"u64"},{"name":"reward2AmountCumulative","type":"u64"},{"name":"depositCapUsdPerIxn","type":"u64"},{"name":"withdrawalCapA","type":{"defined":"WithdrawalCaps"}},{"name":"withdrawalCapB","type":{"defined":"WithdrawalCaps"}},{"name":"maxPriceDeviationBps","type":"u64"},{"name":"swapVaultMaxSlippageBps","type":"u32"},{"name":"swapVaultMaxSlippageFromReferenceBps","type":"u32"},{"name":"strategyType","type":"u64"},{"name":"padding0","type":"u64"},{"name":"withdrawFee","type":"u64"},{"name":"feesFee","type":"u64"},{"name":"reward0Fee","type":"u64"},{"name":"reward1Fee","type":"u64"},{"name":"reward2Fee","type":"u64"},{"name":"positionTimestamp","type":"u64"},{"name":"kaminoRewards","type":{"array":[{"defined":"KaminoRewardInfo"},3]}},{"name":"strategyDex","type":"u64"},{"name":"raydiumProtocolPositionOrBaseVaultAuthority","type":"publicKey"},{"name":"allowDepositWithoutInvest","type":"u64"},{"name":"raydiumPoolConfigOrBaseVaultAuthority","type":"publicKey"},{"name":"depositBlocked","type":"u8"},{"name":"creationStatus","type":"u8"},{"name":"investBlocked","type":"u8"},{"name":"shareCalculationMethod","docs":["share_calculation_method can be either DOLAR_BASED=0 or PROPORTION_BASED=1"],"type":"u8"},{"name":"withdrawBlocked","type":"u8"},{"name":"reservedFlag2","type":"u8"},{"name":"localAdminBlocked","type":"u8"},{"name":"flashVaultSwapAllowed","type":"u8"},{"name":"referenceSwapPriceA","type":{"defined":"Price"}},{"name":"referenceSwapPriceB","type":{"defined":"Price"}},{"name":"isCommunity","type":"u8"},{"name":"rebalanceType","type":"u8"},{"name":"flashSwapInProgress","type":"u8"},{"name":"padding1","type":{"array":["u8",5]}},{"name":"rebalanceRaw","type":{"defined":"RebalanceRaw"}},{"name":"padding2","type":{"array":["u8",7]}},{"name":"tokenAFeesFromRewardsCumulative","type":"u64"},{"name":"tokenBFeesFromRewardsCumulative","type":"u64"},{"name":"strategyLookupTable","type":"publicKey"},{"name":"lastSwapUnevenStepTimestamp","type":"u64"},{"name":"farm","type":"publicKey"},{"name":"rebalancesCap","type":{"defined":"WithdrawalCaps"}},{"name":"padding3NonZeroed","type":{"array":["u64",4]}},{"name":"tokenATokenProgram","type":"publicKey"},{"name":"tokenBTokenProgram","type":"publicKey"},{"name":"pendingAdmin","type":"publicKey"},{"name":"maxDeviationFromRefPriceOnInvestBps","type":"u32"},{"name":"padding3","type":"u32"},{"name":"lastInvestSlot","type":"u64"},{"name":"padding4","type":"u64"},{"name":"padding5","type":{"array":["u128",12]}},{"name":"padding6","type":{"array":["u128",32]}},{"name":"padding7","type":{"array":["u128",32]}},{"name":"padding8","type":{"array":["u128",32]}}]}},{"name":"GlobalConfig","type":{"kind":"struct","fields":[{"name":"emergencyMode","type":"u64"},{"name":"blockDeposit","type":"u64"},{"name":"blockInvest","type":"u64"},{"name":"blockWithdraw","type":"u64"},{"name":"blockCollectFees","type":"u64"},{"name":"blockCollectRewards","type":"u64"},{"name":"blockSwapRewards","type":"u64"},{"name":"blockSwapUnevenVaults","type":"u32"},{"name":"blockEmergencySwap","type":"u32"},{"name":"minWithdrawalFeeBps","type":"u64"},{"name":"scopeProgramId","type":"publicKey"},{"name":"deprecated","type":"publicKey"},{"name":"padding0NonZeroed","type":{"array":["u64",256]}},{"name":"actionsAuthority","type":"publicKey"},{"name":"adminAuthority","type":"publicKey"},{"name":"treasuryFeeVaults","type":{"array":["publicKey",256]}},{"name":"tokenInfos","type":"publicKey"},{"name":"blockLocalAdmin","type":"u64"},{"name":"minPerformanceFeeBps","type":"u64"},{"name":"minSwapUnevenSlippageToleranceBps","type":"u64"},{"name":"minReferencePriceSlippageToleranceBps","type":"u64"},{"name":"actionsAfterRebalanceDelaySeconds","type":"u64"},{"name":"treasuryFeeVaultReceiver","type":"publicKey"},{"name":"scopePriceIds","type":{"array":["publicKey",16]}},{"name":"maxDeviationFromRefPriceOnInvestBps","type":"u32"},{"name":"padding1","type":"u32"},{"name":"investCooldownSlots","type":"u64"},{"name":"minInvestTriggerValueUsd","type":"u64"},{"name":"padding","type":{"array":["u64",1968]}}]}},{"name":"CollateralInfos","type":{"kind":"struct","fields":[{"name":"infos","type":{"array":[{"defined":"CollateralInfo"},303]}}]}},{"name":"ScopeChainAccount","type":{"kind":"struct","fields":[{"name":"chainArray","type":{"array":[{"array":["u16",4]},512]}}]}},{"name":"TermsSignature","type":{"kind":"struct","fields":[{"name":"signature","type":{"array":["u8",64]}}]}}],"types":[{"name":"PositionRewardInfo","type":{"kind":"struct","fields":[{"name":"growthInsideCheckpoint","type":"u128"},{"name":"amountOwed","type":"u64"}]}},{"name":"WhirlpoolRewardInfo","type":{"kind":"struct","fields":[{"name":"mint","docs":["Reward token mint."],"type":"publicKey"},{"name":"vault","docs":["Reward vault token account."],"type":"publicKey"},{"name":"authority","docs":["Authority account that has permission to initialize the reward and set emissions."],"type":"publicKey"},{"name":"emissionsPerSecondX64","docs":["Q64.64 number that indicates how many tokens per second are earned per unit of liquidity."],"type":"u128"},{"name":"growthGlobalX64","docs":["Q64.64 number that tracks the total tokens earned per unit of liquidity since the reward","emissions were turned on."],"type":"u128"}]}},{"name":"RewardInfo","type":{"kind":"struct","fields":[{"name":"rewardState","docs":["Reward state"],"type":"u8"},{"name":"openTime","docs":["Reward open time"],"type":"u64"},{"name":"endTime","docs":["Reward end time"],"type":"u64"},{"name":"lastUpdateTime","docs":["Reward last update time"],"type":"u64"},{"name":"emissionsPerSecondX64","docs":["Q64.64 number indicates how many tokens per second are earned per unit of liquidity."],"type":"u128"},{"name":"rewardTotalEmissioned","docs":["The total amount of reward emissioned"],"type":"u64"},{"name":"rewardClaimed","docs":["The total amount of claimed reward"],"type":"u64"},{"name":"tokenMint","docs":["Reward token mint."],"type":"publicKey"},{"name":"tokenVault","docs":["Reward vault token account."],"type":"publicKey"},{"name":"authority","docs":["The owner that has permission to set reward param"],"type":"publicKey"},{"name":"rewardGrowthGlobalX64","docs":["Q64.64 number that tracks the total tokens earned per unit of liquidity since the reward","emissions were turned on."],"type":"u128"}]}},{"name":"RebalanceRaw","type":{"kind":"struct","fields":[{"name":"params","type":{"array":["u8",128]}},{"name":"state","type":{"array":["u8",256]}},{"name":"referencePriceType","type":"u8"}]}},{"name":"CollateralInfo","type":{"kind":"struct","fields":[{"name":"mint","type":"publicKey"},{"name":"lowerHeuristic","type":"u64"},{"name":"upperHeuristic","type":"u64"},{"name":"expHeuristic","type":"u64"},{"name":"maxTwapDivergenceBps","type":"u64"},{"name":"scopeTwapPriceChain","type":{"array":["u16",4]}},{"name":"scopePriceChain","type":{"array":["u16",4]}},{"name":"name","type":{"array":["u8",32]}},{"name":"maxAgePriceSeconds","type":"u64"},{"name":"maxAgeTwapSeconds","type":"u64"},{"name":"maxIgnorableAmountAsReward","type":"u64"},{"name":"disabled","type":"u8"},{"name":"padding0","type":{"array":["u8",7]}},{"name":"scopeStakingRateChain","type":{"array":["u16",4]}},{"name":"scopeFeed","type":"publicKey"},{"name":"padding","type":{"array":["u64",4]}}]}},{"name":"CollateralInfoParams","type":{"kind":"struct","fields":[{"name":"mint","type":"publicKey"},{"name":"lowerHeuristic","type":"u64"},{"name":"upperHeuristic","type":"u64"},{"name":"expHeuristic","type":"u64"},{"name":"maxTwapDivergenceBps","type":"u64"},{"name":"scopeTwapPriceChain","type":{"array":["u16",4]}},{"name":"scopePriceChain","type":{"array":["u16",4]}},{"name":"name","type":{"array":["u8",32]}},{"name":"maxAgePriceSeconds","type":"u64"},{"name":"maxAgeTwapSeconds","type":"u64"},{"name":"maxIgnorableAmountAsReward","type":"u64"},{"name":"disabled","type":"u8"},{"name":"scopeStakingRateChain","type":{"array":["u16",4]}},{"name":"scopeFeed","type":"publicKey"}]}},{"name":"KaminoRewardInfo","type":{"kind":"struct","fields":[{"name":"decimals","type":"u64"},{"name":"rewardVault","type":"publicKey"},{"name":"rewardMint","type":"publicKey"},{"name":"rewardCollateralId","type":"u64"},{"name":"lastIssuanceTs","type":"u64"},{"name":"rewardPerSecond","type":"u64"},{"name":"amountUncollected","type":"u64"},{"name":"amountIssuedCumulative","type":"u64"},{"name":"amountAvailable","type":"u64"}]}},{"name":"WithdrawalCaps","type":{"kind":"struct","fields":[{"name":"configCapacity","type":"i64"},{"name":"currentTotal","type":"i64"},{"name":"lastIntervalStartTimestamp","type":"u64"},{"name":"configIntervalLengthSeconds","type":"u64"}]}},{"name":"Price","type":{"kind":"struct","fields":[{"name":"value","type":"u64"},{"name":"exp","type":"u64"}]}},{"name":"RebalanceAutodriftParams","type":{"kind":"struct","fields":[{"name":"initDriftTicksPerEpoch","type":"u32"},{"name":"ticksBelowMid","type":"i32"},{"name":"ticksAboveMid","type":"i32"},{"name":"frontrunMultiplierBps","type":"u16"},{"name":"stakingRateASource","type":{"defined":"StakingRateSource"}},{"name":"stakingRateBSource","type":{"defined":"StakingRateSource"}},{"name":"initDriftDirection","type":{"defined":"DriftDirection"}}]}},{"name":"RebalanceAutodriftWindow","type":{"kind":"struct","fields":[{"name":"stakingRateA","type":{"option":{"defined":"Price"}}},{"name":"stakingRateB","type":{"option":{"defined":"Price"}}},{"name":"epoch","type":"u64"},{"name":"theoreticalTick","type":"i32"},{"name":"stratMidTick","type":"i32"}]}},{"name":"RebalanceAutodriftState","type":{"kind":"struct","fields":[{"name":"lastWindow","type":{"defined":"RebalanceAutodriftWindow"}},{"name":"currentWindow","type":{"defined":"RebalanceAutodriftWindow"}},{"name":"step","type":{"defined":"RebalanceAutodriftStep"}}]}},{"name":"RebalanceDriftParams","type":{"kind":"struct","fields":[{"name":"startMidTick","type":"i32"},{"name":"ticksBelowMid","type":"i32"},{"name":"ticksAboveMid","type":"i32"},{"name":"secondsPerTick","type":"u64"},{"name":"direction","type":{"defined":"DriftDirection"}}]}},{"name":"RebalanceDriftState","type":{"kind":"struct","fields":[{"name":"step","type":{"defined":"RebalanceDriftStep"}},{"name":"lastDriftTimestamp","type":"u64"},{"name":"lastMidTick","type":"i32"}]}},{"name":"RebalanceExpanderState","type":{"kind":"struct","fields":[{"name":"initialPoolPrice","type":"u128"},{"name":"expansionCount","type":"u16"}]}},{"name":"RebalanceManualState","type":{"kind":"struct","fields":[]}},{"name":"PeriodicRebalanceState","type":{"kind":"struct","fields":[{"name":"lastRebalanceTimestamp","type":"u64"}]}},{"name":"RebalancePricePercentageWithResetState","type":{"kind":"struct","fields":[{"name":"lastRebalanceLowerResetPoolPrice","type":"u128"},{"name":"lastRebalanceUpperResetPoolPrice","type":"u128"}]}},{"name":"RebalancePricePercentageState","type":{"kind":"struct","fields":[{"name":"lastRebalanceLowerPoolPrice","type":"u128"},{"name":"lastRebalanceUpperPoolPrice","type":"u128"}]}},{"name":"RebalanceTakeProfitState","type":{"kind":"struct","fields":[{"name":"step","type":{"defined":"RebalanceTakeProfitStep"}}]}},{"name":"BinAddLiquidityStrategy","type":{"kind":"enum","variants":[{"name":"Uniform","fields":[{"name":"currentBinIndex","type":"i32"},{"name":"lowerBinIndex","type":"i32"},{"name":"upperBinIndex","type":"i32"},{"name":"amountXToDeposit","type":"u64"},{"name":"amountYToDeposit","type":"u64"},{"name":"xCurrentBin","type":"u64"},{"name":"yCurrentBin","type":"u64"}]},{"name":"CurrentTick","fields":["i32"]}]}},{"name":"SimulationPrice","type":{"kind":"enum","variants":[{"name":"PoolPrice"},{"name":"SqrtPrice","fields":["u128"]},{"name":"TickIndex","fields":["i32"]}]}},{"name":"DexSpecificPrice","type":{"kind":"enum","variants":[{"name":"SqrtPrice","fields":["u128"]},{"name":"Q64_64","fields":["u128"]}]}},{"name":"RemoveLiquidityMode","type":{"kind":"enum","variants":[{"name":"Liquidity","fields":["u128"]},{"name":"Bps","fields":["u16"]},{"name":"All"}]}},{"name":"WithdrawalCapAccumulatorAction","type":{"kind":"enum","variants":[{"name":"KeepAccumulator"},{"name":"ResetAccumulator"}]}},{"name":"RebalanceEffects","type":{"kind":"enum","variants":[{"name":"NewRange","fields":["i32","i32"]},{"name":"WithdrawAndFreeze"}]}},{"name":"SwapLimit","type":{"kind":"enum","variants":[{"name":"Bps","fields":["u64"]},{"name":"Absolute","fields":[{"name":"srcAmountToSwap","docs":["Amount of src token expected by the user to perform the swap"],"type":"u64"},{"name":"dstAmountToVault","docs":["Amount of dst token the user provides in exchange"],"type":"u64"},{"name":"aToB","type":"bool"}]}]}},{"name":"MintingMethod","type":{"kind":"enum","variants":[{"name":"PriceBased"},{"name":"Proportional"}]}},{"name":"GlobalConfigOption","type":{"kind":"enum","variants":[{"name":"EmergencyMode"},{"name":"BlockDeposit"},{"name":"BlockInvest"},{"name":"BlockWithdraw"},{"name":"BlockCollectFees"},{"name":"BlockCollectRewards"},{"name":"BlockSwapRewards"},{"name":"BlockSwapUnevenVaults"},{"name":"WithdrawalFeeBps"},{"name":"DeprecatedSwapDiscountBps"},{"name":"ActionsAuthority"},{"name":"DeprecatedTreasuryFeeVaults"},{"name":"AdminAuthority"},{"name":"BlockEmergencySwap"},{"name":"BlockLocalAdmin"},{"name":"UpdateTokenInfos"},{"name":"ScopeProgramId"},{"name":"UpdateScopePriceId"},{"name":"MinPerformanceFeeBps"},{"name":"MinSwapUnevenSlippageToleranceBps"},{"name":"MinReferencePriceSlippageToleranceBps"},{"name":"ActionsAfterRebalanceDelaySeconds"},{"name":"TreasuryFeeVaultReceiver"},{"name":"AddScopePriceId"},{"name":"MaxDeviationFromRefPriceOnInvestBps"},{"name":"InvestCooldownSlots"},{"name":"MinInvestTriggerValueUsd"}]}},{"name":"StrategyConfigOption","type":{"kind":"enum","variants":[{"name":"UpdateDepositCap"},{"name":"UpdateDepositCapIxn"},{"name":"UpdateWithdrawalCapACapacity"},{"name":"UpdateWithdrawalCapAInterval"},{"name":"UpdateWithdrawalCapACurrentTotal"},{"name":"UpdateWithdrawalCapBCapacity"},{"name":"UpdateWithdrawalCapBInterval"},{"name":"UpdateWithdrawalCapBCurrentTotal"},{"name":"UpdateMaxDeviationBps"},{"name":"UpdateSwapVaultMaxSlippage"},{"name":"UpdateStrategyType"},{"name":"UpdateDepositFee"},{"name":"UpdateWithdrawFee"},{"name":"UpdateCollectFeesFee"},{"name":"UpdateReward0Fee"},{"name":"UpdateReward1Fee"},{"name":"UpdateReward2Fee"},{"name":"UpdateAdminAuthority"},{"name":"KaminoRewardIndex0TS"},{"name":"KaminoRewardIndex1TS"},{"name":"KaminoRewardIndex2TS"},{"name":"KaminoRewardIndex0RewardPerSecond"},{"name":"KaminoRewardIndex1RewardPerSecond"},{"name":"KaminoRewardIndex2RewardPerSecond"},{"name":"UpdateDepositBlocked"},{"name":"UpdateRaydiumProtocolPositionOrBaseVaultAuthority"},{"name":"UpdateRaydiumPoolConfigOrBaseVaultAuthority"},{"name":"UpdateInvestBlocked"},{"name":"UpdateWithdrawBlocked"},{"name":"UpdateLocalAdminBlocked"},{"name":"DeprecatedUpdateCollateralIdA"},{"name":"DeprecatedUpdateCollateralIdB"},{"name":"UpdateFlashVaultSwap"},{"name":"AllowDepositWithoutInvest"},{"name":"UpdateSwapVaultMaxSlippageFromRef"},{"name":"ResetReferencePrices"},{"name":"UpdateStrategyCreationState"},{"name":"UpdateIsCommunity"},{"name":"UpdateRebalanceType"},{"name":"UpdateRebalanceParams"},{"name":"UpdateDepositMintingMethod"},{"name":"UpdateLookupTable"},{"name":"UpdateReferencePriceType"},{"name":"UpdateReward0Amount"},{"name":"UpdateReward1Amount"},{"name":"UpdateReward2Amount"},{"name":"UpdateFarm"},{"name":"UpdateRebalancesCapCapacity"},{"name":"UpdateRebalancesCapInterval"},{"name":"UpdateRebalancesCapCurrentTotal"},{"name":"DeprecatedUpdateSwapUnevenAuthority"},{"name":"UpdatePendingStrategyAdmin"},{"name":"UpdateMaxDeviationFromRefPriceOnInvestBps"}]}},{"name":"StrategyStatus","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Active"},{"name":"Frozen"},{"name":"Rebalancing"},{"name":"NoPosition"}]}},{"name":"StrategyType","type":{"kind":"enum","variants":[{"name":"Stable"},{"name":"Pegged"},{"name":"Volatile"}]}},{"name":"CreationStatus","type":{"kind":"enum","variants":[{"name":"IGNORED"},{"name":"SHADOW"},{"name":"LIVE"},{"name":"DEPRECATED"},{"name":"STAGING"}]}},{"name":"ExecutiveWithdrawAction","type":{"kind":"enum","variants":[{"name":"Freeze"},{"name":"Unfreeze"},{"name":"Rebalance"}]}},{"name":"ReferencePriceType","type":{"kind":"enum","variants":[{"name":"POOL"},{"name":"TWAP"}]}},{"name":"LiquidityCalculationMode","type":{"kind":"enum","variants":[{"name":"Deposit"},{"name":"Withdraw"}]}},{"name":"UpdateCollateralInfoMode","type":{"kind":"enum","variants":[{"name":"CollateralId"},{"name":"LowerHeuristic"},{"name":"UpperHeuristic"},{"name":"ExpHeuristic"},{"name":"TwapDivergence"},{"name":"UpdateScopeTwap"},{"name":"UpdateScopeChain"},{"name":"UpdateName"},{"name":"UpdatePriceMaxAge"},{"name":"UpdateTwapMaxAge"},{"name":"UpdateDisabled"},{"name":"UpdateStakingRateChain"},{"name":"UpdateMaxIgnorableAmountAsReward"},{"name":"UpdateScopeFeed"}]}},{"name":"BalanceStatus","type":{"kind":"enum","variants":[{"name":"Balanced"},{"name":"Unbalanced"}]}},{"name":"RebalanceAutodriftStep","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Autodrifting"}]}},{"name":"StakingRateSource","type":{"kind":"enum","variants":[{"name":"Constant"},{"name":"Scope"}]}},{"name":"DriftDirection","type":{"kind":"enum","variants":[{"name":"Increasing"},{"name":"Decreasing"}]}},{"name":"RebalanceDriftStep","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Drifting"}]}},{"name":"ExpanderStep","type":{"kind":"enum","variants":[{"name":"ExpandOrContract","fields":["u16"]},{"name":"Recenter"}]}},{"name":"RebalanceTakeProfitToken","type":{"kind":"enum","variants":[{"name":"A"},{"name":"B"}]}},{"name":"RebalanceTakeProfitStep","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"TakingProfit"},{"name":"Finished"}]}},{"name":"RebalanceAction","type":{"kind":"enum","variants":[{"name":"NewPriceRange","fields":[{"defined":"DexSpecificPrice"},{"defined":"DexSpecificPrice"}]},{"name":"NewTickRange","fields":["i32","i32"]},{"name":"WithdrawAndFreeze"}]}},{"name":"RebalanceType","type":{"kind":"enum","variants":[{"name":"Manual"},{"name":"PricePercentage"},{"name":"PricePercentageWithReset"},{"name":"Drift"},{"name":"TakeProfit"},{"name":"PeriodicRebalance"},{"name":"Expander"},{"name":"Autodrift"}]}},{"name":"CollateralTestToken","type":{"kind":"enum","variants":[{"name":"USDC"},{"name":"USDH"},{"name":"SOL"},{"name":"ETH"},{"name":"BTC"},{"name":"MSOL"},{"name":"STSOL"},{"name":"USDT"},{"name":"ORCA"},{"name":"MNDE"},{"name":"HBB"},{"name":"JSOL"},{"name":"USH"},{"name":"DAI"},{"name":"LDO"},{"name":"SCNSOL"},{"name":"UXD"},{"name":"HDG"},{"name":"DUST"},{"name":"USDR"},{"name":"RATIO"},{"name":"UXP"},{"name":"JITOSOL"},{"name":"RAY"},{"name":"BONK"},{"name":"SAMO"},{"name":"LaineSOL"},{"name":"BSOL"}]}},{"name":"ScopePriceIdTest","type":{"kind":"enum","variants":[{"name":"SOL"},{"name":"ETH"},{"name":"BTC"},{"name":"SRM"},{"name":"RAY"},{"name":"FTT"},{"name":"MSOL"},{"name":"scnSOL_SOL"},{"name":"BNB"},{"name":"AVAX"},{"name":"DaoSOL_SOL"},{"name":"SaberMSOL_SOL"},{"name":"USDH"},{"name":"StSOL"},{"name":"CSOL_SOL"},{"name":"CETH_ETH"},{"name":"CBTC_BTC"},{"name":"CMSOL_SOL"},{"name":"wstETH"},{"name":"LDO"},{"name":"USDC"},{"name":"CUSDC_USDC"},{"name":"USDT"},{"name":"ORCA"},{"name":"MNDE"},{"name":"HBB"},{"name":"CORCA_ORCA"},{"name":"CSLND_SLND"},{"name":"CSRM_SRM"},{"name":"CRAY_RAY"},{"name":"CFTT_FTT"},{"name":"CSTSOL_STSOL"},{"name":"SLND"},{"name":"DAI"},{"name":"JSOL_SOL"},{"name":"USH"},{"name":"UXD"},{"name":"USDH_TWAP"},{"name":"USH_TWAP"},{"name":"UXD_TWAP"},{"name":"HDG"},{"name":"DUST"},{"name":"USDR"},{"name":"USDR_TWAP"},{"name":"RATIO"},{"name":"UXP"},{"name":"KUXDUSDCORCA"},{"name":"JITOSOL_SOL"},{"name":"SOL_EMA"},{"name":"ETH_EMA"},{"name":"BTC_EMA"},{"name":"SRM_EMA"},{"name":"RAY_EMA"},{"name":"FTT_EMA"},{"name":"MSOL_EMA"},{"name":"BNB_EMA"},{"name":"AVAX_EMA"},{"name":"STSOL_EMA"},{"name":"USDC_EMA"},{"name":"USDT_EMA"},{"name":"SLND_EMA"},{"name":"DAI_EMA"},{"name":"wstETH_TWAP"},{"name":"DUST_TWAP"},{"name":"BONK"},{"name":"BONK_TWAP"},{"name":"SAMO"},{"name":"SAMO_TWAP"},{"name":"BSOL"},{"name":"LaineSOL"}]}},{"name":"DEX","type":{"kind":"enum","variants":[{"name":"Orca"},{"name":"Raydium"},{"name":"Meteora"}]}}],"errors":[{"code":6000,"name":"IntegerOverflow","msg":"Integer overflow"},{"code":6001,"name":"OperationForbidden","msg":"Operation Forbidden"},{"code":6002,"name":"ZeroAmount","msg":"[DEPRECATED] Zero amount"},{"code":6003,"name":"UnableToDeserializeAccount","msg":"Unable to deserialize account"},{"code":6004,"name":"VaultBalanceDoesNotMatchTokenA","msg":"[DEPRECATED] Vault balance does not match for token A"},{"code":6005,"name":"VaultBalanceDoesNotMatchTokenB","msg":"[DEPRECATED] Vault balance does not match for token B"},{"code":6006,"name":"SharesIssuedAmountDoesNotMatch","msg":"[DEPRECATED] Shares issued amount does not match"},{"code":6007,"name":"GlobalConfigKeyError","msg":"Key is not present in global config"},{"code":6008,"name":"SystemInEmergencyMode","msg":"[DEPRECATED] System is in emergency mode"},{"code":6009,"name":"GlobalDepositBlocked","msg":"Global deposit is currently blocked"},{"code":6010,"name":"GlobalWithdrawBlocked","msg":"Global withdraw is currently blocked"},{"code":6011,"name":"GlobalInvestBlocked","msg":"Global invest is currently blocked"},{"code":6012,"name":"OutOfRangeIntegralConversion","msg":"Out of range integral conversion attempted"},{"code":6013,"name":"MathOverflow","msg":"[DEPRECATED] Mathematical operation with overflow"},{"code":6014,"name":"TooMuchLiquidityToWithdraw","msg":"Unable to withdraw more liquidity than available in position"},{"code":6015,"name":"DepositAmountsZero","msg":"Deposit amounts must be greater than zero"},{"code":6016,"name":"SharesZero","msg":"Number of shares to withdraw must be greater than zero"},{"code":6017,"name":"StrategyNotActive","msg":"Strategy not active"},{"code":6018,"name":"UnharvestedAmounts","msg":"There are unharvested gains"},{"code":6019,"name":"InvalidRewardMapping","msg":"Reward mapping incorrect"},{"code":6020,"name":"InvalidRewardIndex","msg":"Reward index incorrect"},{"code":6021,"name":"OwnRewardUninitialized","msg":"Cannot use uninitialized reward vault"},{"code":6022,"name":"PriceNotValid","msg":"Price is not valid"},{"code":6023,"name":"SwapRewardImbalanced","msg":"Must provide almost equal amounts of tokens"},{"code":6024,"name":"SwapRewardTooSmall","msg":"Swap reward is zero or less than requested"},{"code":6025,"name":"SwapRewardLessThanRequested","msg":"Swap reward is less than what user requested as minimum"},{"code":6026,"name":"SwapRewardLessThanMinimum","msg":"Swap reward is less than minimum acceptable"},{"code":6027,"name":"WrongDiscriminator","msg":"Wrong discriminator"},{"code":6028,"name":"WrongMint","msg":"Wrong mint"},{"code":6029,"name":"WrongVault","msg":"Wrong vault"},{"code":6030,"name":"SwapAmountsZero","msg":"Swap amounts must be greater than zero"},{"code":6031,"name":"PriceTooOld","msg":"Price too old"},{"code":6032,"name":"CannotInvestZeroAmount","msg":"Cannot invest zero amount"},{"code":6033,"name":"MaxInvestableZero","msg":"Cannot have zero investable amount"},{"code":6034,"name":"CollectFeesBlocked","msg":"Collect fees is blocked"},{"code":6035,"name":"CollectRewardsBlocked","msg":"Collect rewards is blocked"},{"code":6036,"name":"SwapRewardsBlocked","msg":"Swap rewards is blocked"},{"code":6037,"name":"WrongRewardCollateralID","msg":"Reward collateral ID is incorrect for strategy"},{"code":6038,"name":"InvalidPositionAccount","msg":"Position account doesn't match internal records"},{"code":6039,"name":"CouldNotDeserializeScope","msg":"Scope account could not be deserialized"},{"code":6040,"name":"WrongCollateralID","msg":"[DEPRECATED] Collateral ID invalid for strategy"},{"code":6041,"name":"CollateralTokensExceedDepositCap","msg":"Collaterals exceed deposit cap"},{"code":6042,"name":"SwapUnevenVaultsBlocked","msg":"Swap uneven vaults is blocked"},{"code":6043,"name":"VaultsAreAlreadyBalanced","msg":"Cannot swap as vaults are already balanced"},{"code":6044,"name":"CannotSwapUnevenOutOfRange","msg":"Cannot swap uneven vaults when position is out of range"},{"code":6045,"name":"DivideByZero","msg":"Cannot divide by zero"},{"code":6046,"name":"DeltaATooLarge","msg":"[DEPRECATED] Delta A too large"},{"code":6047,"name":"DeltaBTooLarge","msg":"[DEPRECATED] Delta B too large"},{"code":6048,"name":"CannotExecutiveWithdrawZeroAmount","msg":"[DEPRECATED] Cannot executive withdraw zero amount"},{"code":6049,"name":"CannotWithdrawZeroAmount","msg":"Cannot withdraw zero amount"},{"code":6050,"name":"CannotCollectFeesOnZeroLiquidityPosition","msg":"[DEPRECATED] Cannot collect fees on zero liquidity position"},{"code":6051,"name":"StrategyNotActiveWhenDepositing","msg":"Cannot deposit inactive position"},{"code":6052,"name":"CannotOpenPositionWithExistingPosition","msg":"Cannot open position with existing opened position"},{"code":6053,"name":"CollateralTokensExceedDepositCapPerIxn","msg":"Collaterals exceed deposit ixn cap"},{"code":6054,"name":"CannotDepositOutOfRange","msg":"Cannot deposit when strategy out of range"},{"code":6055,"name":"CannotInvestOutOfRange","msg":"Cannot invest when strategy out of range"},{"code":6056,"name":"WithdrawalCapReached","msg":"Withdrawal cap is reached"},{"code":6057,"name":"TimestampDecrease","msg":"Timestamp decrease"},{"code":6058,"name":"CPINotAllowed","msg":"CPI not allowed"},{"code":6059,"name":"OrcaPriceTooDifferentFromScope","msg":"Cannot use orca price as it is too different from scope price"},{"code":6060,"name":"LowerTickLargerThanUpperTick","msg":"Lower tick larger than upper tick"},{"code":6061,"name":"LowerTickTooLow","msg":"Lower tick is lower than the minimal supported low tick"},{"code":6062,"name":"UpperTickTooLarge","msg":"Upper tick is larger than the maximum supported tick"},{"code":6063,"name":"LowerTickNotMultipleOfTickSpacing","msg":"Lower tick is not a multiple of tick spacing"},{"code":6064,"name":"UpperTickNotMultipleOfTickSpacing","msg":"Upper tick is not a multiple of tick spacing"},{"code":6065,"name":"CannotChangeAdminAuthority","msg":"Cannot change admin authority"},{"code":6066,"name":"CannotResizeAccount","msg":"Cannot resize with smaller new size"},{"code":6067,"name":"ScopeChainUpdateFailed","msg":"Scope chain update failed"},{"code":6068,"name":"PriceTooDivergentFromTwap","msg":"Price too divergent from twap"},{"code":6069,"name":"ExistingRewardOverride","msg":"[DEPRECATED] Can not override the existing reward"},{"code":6070,"name":"WrongKaminoRewardId","msg":"Kamino reward id exceeds the available slots"},{"code":6071,"name":"KaminoRewardNotExist","msg":"Kamino reward is not initialized"},{"code":6072,"name":"KaminoRewardAlreadyExists","msg":"Kamino reward is already initialized"},{"code":6073,"name":"KaminoCollateralNotValid","msg":"Kamino collateral is not valid"},{"code":6074,"name":"KaminoRewardExceedsAvailableAmount","msg":"[DEPRECATED] Expected kamino reward is bigger then the available amount within the vault"},{"code":6075,"name":"SwapUnevenVaultsOvershoot","msg":"Swap uneven vaults result in the opposite unbalance of the vaults"},{"code":6076,"name":"BpsNotInRange","msg":"Bps parameter passed to instruction is not in range"},{"code":6077,"name":"EmergencySwapBlocked","msg":"Emergency Swap is blocked"},{"code":6078,"name":"StrategyNotFrozen","msg":"Strategy is expected to be frozen for this operation"},{"code":6079,"name":"UnexpectedTokenAmountsPostSwap","msg":"Token left in vault post swap are lower than expected"},{"code":6080,"name":"AccountNotBelongToDEX","msg":"Account doesn't belong to the DEX"},{"code":6081,"name":"WrongDEXProgramID","msg":"Wrong DEX program ID"},{"code":6082,"name":"OrcaRewardUninitialized","msg":"Cannot use uninitialized orca reward vault"},{"code":6083,"name":"InvalidAdminAuthority","msg":"Invalid admin authority"},{"code":6084,"name":"PriceIsBiggerThanHeuristic","msg":"Token price is bigger than heuristic"},{"code":6085,"name":"PriceIsLowerThanHeuristic","msg":"Token price is lower than heuristic"},{"code":6086,"name":"AccountDifferentThanExpected","msg":"Account different than expected"},{"code":6087,"name":"SwapAmountsTooSmall","msg":"Swap amount below the minimum value"},{"code":6088,"name":"InvalidDexProgramId","msg":"Invalid dex program id"},{"code":6089,"name":"StrategyDepositBlocked","msg":"Strategy deposit is currently blocked"},{"code":6090,"name":"StrategyInvestBlocked","msg":"Strategy invest is currently blocked"},{"code":6091,"name":"StrategyWithdrawBlocked","msg":"Strategy withdraw is currently blocked"},{"code":6092,"name":"WrongSwapVaultDirection","msg":"Vault swap can't be performed in the required direction"},{"code":6093,"name":"SwapVaultsTooBig","msg":"Provided amount for vault swap is over the limit"},{"code":6094,"name":"SwapVaultsCashOutputBelowMinimum","msg":"Token out for cash based vault swap is below minimum expected"},{"code":6095,"name":"FlashIxsNotEnded","msg":"Flash ixs initiated without the closing ix in the transaction"},{"code":6096,"name":"FlashTxWithUnexpectedIxs","msg":"Some unexpected instructions are present in the tx. Either before or after the flash ixs, or some ix target the same program between"},{"code":6097,"name":"FlashIxsAccountMismatch","msg":"Some accounts differ between the two flash ixs"},{"code":6098,"name":"FlashIxsIncludeScope","msg":"A scope ix is present in a flash tx"},{"code":6099,"name":"FlashVaultSwapBlocked","msg":"Flash vault swap is blocked on this strategy"},{"code":6100,"name":"FlashVaultSwapWrongAmountToLeave","msg":"Unexpected amount of tokens in ata prior flash vault swap (wrong amount_to_leave_to_user)"},{"code":6101,"name":"DepositLessThanMinimum","msg":"Deposit amount less than minimal allowed"},{"code":6102,"name":"DepositWithoutInvestDisallowed","msg":"Cannot deposit without invest"},{"code":6103,"name":"InvalidScopeChain","msg":"Invalid Scope Chain"},{"code":6104,"name":"InvalidScopeTWAPChain","msg":"Invalid Scope TWAP Chain"},{"code":6105,"name":"PositionHasRemainingLiquidity","msg":"Existent position has liquidity, new position creation is forbidden"},{"code":6106,"name":"PoolRebalancing","msg":"Deposit is not allowed as pool is rebalancing"},{"code":6107,"name":"PermissionlessRebalancingDisabled","msg":"Permissionless rebalancing is disabled"},{"code":6108,"name":"ManualRebalanceInvalidOwner","msg":"Only the owner of the strategy can manually rebalance it"},{"code":6109,"name":"InvalidRebalanceType","msg":"Invalid rebalance type for the strategy"},{"code":6110,"name":"NoRebalanceNecessary","msg":"No rebalance necessary based on current rebalance type/parameters"},{"code":6111,"name":"TickArraysDoNotMatchRebalance","msg":"The given tick arrays do not match the rebalance result"},{"code":6112,"name":"StrategyPositionNotValid","msg":"Expected strategy position to be initialized"},{"code":6113,"name":"CouldNotDeserializeRebalanceState","msg":"Rebalance state could not be deserialized"},{"code":6114,"name":"CouldNotSerializeRebalanceState","msg":"Rebalance state could not be serialized"},{"code":6115,"name":"CouldNotDeserializeRebalanceParams","msg":"Rebalance params could not be deserialized"},{"code":6116,"name":"NotEnoughTokensForRatio","msg":"Deposit is not allowed as token amounts are not enough to match our holdings ratio"},{"code":6117,"name":"AmountsRepresentZeroShares","msg":"The provided amounts are too small"},{"code":6118,"name":"MaxLossExceeded","msg":"Rouding errors exceed the maximal loss tolerance"},{"code":6119,"name":"RewardNotStrategyToken","msg":"Reward does not match strategy token"},{"code":6120,"name":"DecimalToU64ConversionFailed","msg":"Decimal to u64 conversion failed"},{"code":6121,"name":"DecimalOperationFailed","msg":"Decimal operation failed"},{"code":6122,"name":"VaultBalancesCausesWrongSharesIssuance","msg":"Deposit is not allowed as the strategy is not fully invested in the pool "},{"code":6123,"name":"TokenDisabled","msg":"Token cannot be used in strategy creation"},{"code":6124,"name":"InvalidReferencePriceType","msg":"Invalid reference price type"},{"code":6125,"name":"TokenToSwapNotEnough","msg":"Token amount to be swapped is not enough"},{"code":6126,"name":"TokenAccountBalanceMismatch","msg":"Token amount in ata is different than the expected amount"},{"code":6127,"name":"UnexpectedProgramIdForPrerequisiteIx","msg":"Unexpected programID for prerequisite ix"},{"code":6128,"name":"ComputeFeesAndRewardsUpdateError","msg":"Got an error from the dex specific function while computing the fees/rewards update"},{"code":6129,"name":"SharesNotZero","msg":"There must be no shares issued when closing a strategy"},{"code":6130,"name":"InvalidScopeStakingRateChain","msg":"Invalid Scope staking rate Chain"},{"code":6131,"name":"StakingRateNotValid","msg":"Staking rate (provided by Scope) is not valid"},{"code":6132,"name":"DecimalToU128ConversionFailed","msg":"Decimal to u128 conversion failed"},{"code":6133,"name":"DecimalNegativeSqrtRoot","msg":"Decimal sqrt on negative number"},{"code":6134,"name":"DriftingOppositeDirection","msg":"Drifting strategy is moving in the opposite direction"},{"code":6135,"name":"WrongRewardCollateralId","msg":"Wrong reward collateral_id"},{"code":6136,"name":"CollateralInfoAlreadyExists","msg":"Collateral info already exists for given index"},{"code":6137,"name":"InvestTooEarly","msg":"Invest is too early after the position was opened"},{"code":6138,"name":"SwapUnevenTooEarly","msg":"Swap uneven is too early after the position was opened"},{"code":6139,"name":"FlashSwapTooEarly","msg":"Flash swap is too early after the position was opened"},{"code":6140,"name":"RebalancesCapReached","msg":"Rebalance caps reached, no rebalances are allowed until the end of the current interval"},{"code":6141,"name":"SwapUnevenInvalidAuthority","msg":"Cannot swap uneven because authority is set and the given signer does not correspond"},{"code":6142,"name":"InvalidTick","msg":"Invalid tick requested"},{"code":6143,"name":"MeteoraMathOverflow","msg":"Meteora math overflowed"},{"code":6144,"name":"StrategyTickArrayNotValid","msg":"Expected strategy tick arrays to be initialized"},{"code":6145,"name":"WrongEventAuthority","msg":"Wrong event authority"},{"code":6146,"name":"StrategyFieldUpdateNotAllowed","msg":"Strategy field update is not allowed"},{"code":6147,"name":"UnsupportedDex","msg":"DEX is not supported for this operation"},{"code":6148,"name":"InvalidBPSValue","msg":"Invalid BPS value provided"},{"code":6149,"name":"RewardVaultOverrideNotAllowed","msg":"Reward vault override not allowed"},{"code":6150,"name":"ComputeFeesAndRewardsInvalidReward","msg":"Got invalid reward from the dex specific function while computing the fees/rewards update"},{"code":6151,"name":"EmptyTreasury","msg":"No tokens to withdraw from treasury fee vault"},{"code":6152,"name":"ChangingPoolRewardMintMismatch","msg":"New pool reward mint does not match the old pool reward mint"},{"code":6153,"name":"ProvidedRewardVaultMismatch","msg":"The provided reward vault does not match the strategy state"},{"code":6154,"name":"RepeatedMint","msg":"The provided reward vault does not match the strategy state"},{"code":6155,"name":"UnsupportedTokenExtension","msg":"The token extension is not supported by the program"},{"code":6156,"name":"UnsupportedDexForToken22","msg":"Cannot initialize strategy with this dex while having a mint with token22"},{"code":6157,"name":"GlobalConfigInvalidScopePriceIndex","msg":"Scope price index is not present in global config"},{"code":6158,"name":"GlobalConfigScopePricesIdsFull","msg":"Scope prices ids array in global config is full"},{"code":6159,"name":"GlobalConfigAlreadyContainsScopePriceId","msg":"Scope prices ids array already contains given scope price id"},{"code":6160,"name":"GlobalConfigExpectedScopePricesAccount","msg":"Expected scope prices account being passed in when adding or updating a scope prices feed"},{"code":6161,"name":"RewardScopePriceAccountNotPresent","msg":"A reward scope price account should have been passed in, but it is missing"},{"code":6162,"name":"IncorrectScopePricesAccountID","msg":"Incorrect scope prices account id passed in"},{"code":6163,"name":"CouldNotCalculatePriceTwap","msg":"Could not calculate get_price_usd_unchecked()"},{"code":6164,"name":"ReferencePriceTooFarFromPoolPrice","msg":"Reference price is too far from the current pool price"},{"code":6165,"name":"InvestCooldownNotElapsed","msg":"Invest cooldown has not elapsed"},{"code":6166,"name":"InvestAmountBelowMinimum","msg":"Uninvested value is below the minimum invest trigger"},{"code":6167,"name":"DepositAndInvestDisabled","msg":"Deposit and invest is disabled on mainnet"}]} \ No newline at end of file diff --git a/idls/kamino_farms.FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr.json b/idls/kamino_farms.FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr.json new file mode 100644 index 0000000..730916d --- /dev/null +++ b/idls/kamino_farms.FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr.json @@ -0,0 +1 @@ +{"version":"1.6.5","name":"farms","instructions":[{"name":"initializeGlobalConfig","accounts":[{"name":"globalAdmin","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false},{"name":"treasuryVaultsAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateGlobalConfig","accounts":[{"name":"globalAdmin","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false}],"args":[{"name":"mode","type":"u8"},{"name":"value","type":{"array":["u8",32]}}]},{"name":"initializeFarm","accounts":[{"name":"farmAdmin","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"farmVault","isMut":true,"isSigner":false},{"name":"farmVaultsAuthority","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeFarmDelegated","accounts":[{"name":"farmAdmin","isMut":true,"isSigner":true},{"name":"farmDelegate","isMut":false,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"farmVaultsAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeReward","accounts":[{"name":"farmAdmin","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"rewardMint","isMut":false,"isSigner":false},{"name":"rewardVault","isMut":true,"isSigner":false},{"name":"rewardTreasuryVault","isMut":true,"isSigner":false},{"name":"farmVaultsAuthority","isMut":false,"isSigner":false},{"name":"treasuryVaultsAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"addRewards","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"rewardMint","isMut":false,"isSigner":false},{"name":"rewardVault","isMut":true,"isSigner":false},{"name":"farmVaultsAuthority","isMut":false,"isSigner":false},{"name":"payerRewardTokenAta","isMut":true,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"rewardIndex","type":"u64"}]},{"name":"updateFarmConfig","accounts":[{"name":"signer","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true}],"args":[{"name":"mode","type":"u16"},{"name":"data","type":"bytes"}]},{"name":"initializeUser","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"owner","isMut":false,"isSigner":false},{"name":"delegatee","isMut":false,"isSigner":false},{"name":"userState","isMut":true,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"transferOwnership","accounts":[{"name":"oldOwner","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"newOwner","isMut":false,"isSigner":false},{"name":"oldUserState","isMut":true,"isSigner":false},{"name":"newUserState","isMut":true,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"rewardUserOnce","accounts":[{"name":"delegateAuthority","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"userState","isMut":true,"isSigner":false}],"args":[{"name":"rewardIndex","type":"u64"},{"name":"amount","type":"u64"},{"name":"expectedRewardIssuedUnclaimed","type":"u64"}]},{"name":"refreshFarm","accounts":[{"name":"farmState","isMut":true,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true}],"args":[]},{"name":"stake","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"userState","isMut":true,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false},{"name":"farmVault","isMut":true,"isSigner":false},{"name":"userAta","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"setStakeDelegated","accounts":[{"name":"delegateAuthority","isMut":false,"isSigner":true},{"name":"userState","isMut":true,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false}],"args":[{"name":"newAmount","type":"u64"}]},{"name":"harvestReward","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"userState","isMut":true,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"rewardMint","isMut":false,"isSigner":false},{"name":"userRewardTokenAccount","isMut":true,"isSigner":false},{"name":"rewardsVault","isMut":true,"isSigner":false},{"name":"rewardsTreasuryVault","isMut":true,"isSigner":false},{"name":"farmVaultsAuthority","isMut":false,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"rewardIndex","type":"u64"}]},{"name":"unstake","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"userState","isMut":true,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true}],"args":[{"name":"stakeSharesScaled","type":"u128"}]},{"name":"refreshUserState","accounts":[{"name":"userState","isMut":true,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true}],"args":[]},{"name":"withdrawUnstakedDeposits","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"userState","isMut":true,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false},{"name":"userAta","isMut":true,"isSigner":false},{"name":"farmVault","isMut":true,"isSigner":false},{"name":"farmVaultsAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"withdrawTreasury","accounts":[{"name":"globalAdmin","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"rewardMint","isMut":false,"isSigner":false},{"name":"rewardTreasuryVault","isMut":true,"isSigner":false},{"name":"treasuryVaultAuthority","isMut":false,"isSigner":false},{"name":"withdrawDestinationTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"depositToFarmVault","accounts":[{"name":"depositor","isMut":false,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"farmVault","isMut":true,"isSigner":false},{"name":"depositorAta","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdrawFromFarmVault","accounts":[{"name":"withdrawAuthority","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"withdrawerTokenAccount","isMut":true,"isSigner":false},{"name":"farmVault","isMut":true,"isSigner":false},{"name":"farmVaultsAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdrawSlashedAmount","accounts":[{"name":"crank","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"slashedAmountSpillAddress","isMut":true,"isSigner":false},{"name":"farmVault","isMut":true,"isSigner":false},{"name":"farmVaultsAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateFarmAdmin","accounts":[{"name":"pendingFarmAdmin","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false}],"args":[]},{"name":"updateGlobalConfigAdmin","accounts":[{"name":"pendingGlobalAdmin","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false}],"args":[]},{"name":"withdrawReward","accounts":[{"name":"farmAdmin","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"rewardMint","isMut":false,"isSigner":false},{"name":"rewardVault","isMut":true,"isSigner":false},{"name":"farmVaultsAuthority","isMut":false,"isSigner":false},{"name":"adminRewardTokenAta","isMut":true,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"rewardIndex","type":"u64"}]},{"name":"updateSecondDelegatedAuthority","accounts":[{"name":"globalAdmin","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"newSecondDelegatedAuthority","isMut":false,"isSigner":false}],"args":[]},{"name":"idlMissingTypes","accounts":[{"name":"globalAdmin","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false}],"args":[{"name":"globalConfigOptionKind","type":{"defined":"GlobalConfigOption"}},{"name":"farmConfigOptionKind","type":{"defined":"FarmConfigOption"}},{"name":"timeUnit","type":{"defined":"TimeUnit"}},{"name":"lockingMode","type":{"defined":"LockingMode"}},{"name":"rewardType","type":{"defined":"RewardType"}}]}],"accounts":[{"name":"FarmState","type":{"kind":"struct","fields":[{"name":"farmAdmin","type":"publicKey"},{"name":"globalConfig","type":"publicKey"},{"name":"token","type":{"defined":"TokenInfo"}},{"name":"rewardInfos","type":{"array":[{"defined":"RewardInfo"},10]}},{"name":"numRewardTokens","type":"u64"},{"name":"numUsers","docs":["Data used to calculate the rewards of the user"],"type":"u64"},{"name":"totalStakedAmount","docs":["The number of token in the `farm_vault` staked (getting rewards and fees)","Set such as `farm_vault.amount = total_staked_amount + total_pending_amount`"],"type":"u64"},{"name":"farmVault","type":"publicKey"},{"name":"farmVaultsAuthority","type":"publicKey"},{"name":"farmVaultsAuthorityBump","type":"u64"},{"name":"delegateAuthority","docs":["Only used for delegate farms","Set to `default()` otherwise"],"type":"publicKey"},{"name":"timeUnit","docs":["Raw representation of a `TimeUnit`","Seconds = 0, Slots = 1"],"type":"u8"},{"name":"isFarmFrozen","docs":["Automatically set to true in case of a full authority withdrawal","If true, the farm is frozen and no more deposits are allowed"],"type":"u8"},{"name":"isFarmDelegated","docs":["Indicates if the farm is a delegate farm","If true, the farm is a delegate farm and the `delegate_authority` is set*"],"type":"u8"},{"name":"isRewardUserOnceEnabled","docs":["If set to 1, indicates that the \"reward user once\" feature is enabled"],"type":"u8"},{"name":"isHarvestingPermissionless","type":"u8"},{"name":"padding0","type":{"array":["u8",3]}},{"name":"withdrawAuthority","docs":["Withdraw authority for the farm, allowed to lock deposited funds and withdraw them","Set to `default()` if unused (only the depositors can withdraw their funds)"],"type":"publicKey"},{"name":"depositWarmupPeriod","docs":["Delay between a user deposit and the moment it is considered as staked","0 if unused"],"type":"u32"},{"name":"withdrawalCooldownPeriod","docs":["Delay between a user unstake and the ability to withdraw his deposit."],"type":"u32"},{"name":"totalActiveStakeScaled","docs":["Total active stake of tokens in the farm (scaled from `Decimal` representation)."],"type":"u128"},{"name":"totalPendingStakeScaled","docs":["Total pending stake of tokens in the farm (scaled from `Decimal` representation).","(can be used by `withdraw_authority` but don't get rewards or fees)"],"type":"u128"},{"name":"totalPendingAmount","docs":["Total pending amount of tokens in the farm"],"type":"u64"},{"name":"slashedAmountCurrent","docs":["Slashed amounts from early withdrawal"],"type":"u64"},{"name":"slashedAmountCumulative","type":"u64"},{"name":"slashedAmountSpillAddress","type":"publicKey"},{"name":"lockingMode","docs":["Locking stake"],"type":"u64"},{"name":"lockingStartTimestamp","type":"u64"},{"name":"lockingDuration","type":"u64"},{"name":"lockingEarlyWithdrawalPenaltyBps","type":"u64"},{"name":"depositCapAmount","type":"u64"},{"name":"scopePrices","type":"publicKey"},{"name":"scopeOraclePriceId","type":"u64"},{"name":"scopeOracleMaxAge","type":"u64"},{"name":"pendingFarmAdmin","type":"publicKey"},{"name":"strategyId","type":"publicKey"},{"name":"delegatedRpsAdmin","type":"publicKey"},{"name":"vaultId","type":"publicKey"},{"name":"secondDelegatedAuthority","type":"publicKey"},{"name":"padding","type":{"array":["u64",74]}}]}},{"name":"GlobalConfig","type":{"kind":"struct","fields":[{"name":"globalAdmin","type":"publicKey"},{"name":"treasuryFeeBps","type":"u64"},{"name":"treasuryVaultsAuthority","type":"publicKey"},{"name":"treasuryVaultsAuthorityBump","type":"u64"},{"name":"pendingGlobalAdmin","type":"publicKey"},{"name":"padding1","type":{"array":["u128",126]}}]}},{"name":"UserState","type":{"kind":"struct","fields":[{"name":"userId","type":"u64"},{"name":"farmState","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"isFarmDelegated","docs":["Indicate if this user state is part of a delegated farm"],"type":"u8"},{"name":"padding0","type":{"array":["u8",7]}},{"name":"rewardsTallyScaled","docs":["Rewards tally used for computation of gained rewards","(scaled from `Decimal` representation)."],"type":{"array":["u128",10]}},{"name":"rewardsIssuedUnclaimed","docs":["Number of reward tokens ready for claim"],"type":{"array":["u64",10]}},{"name":"lastClaimTs","type":{"array":["u64",10]}},{"name":"activeStakeScaled","docs":["User stake deposited and usable, generating rewards and fees.","(scaled from `Decimal` representation)."],"type":"u128"},{"name":"pendingDepositStakeScaled","docs":["User stake deposited but not usable and not generating rewards yet.","(scaled from `Decimal` representation)."],"type":"u128"},{"name":"pendingDepositStakeTs","docs":["After this timestamp, pending user stake can be moved to user stake","Initialized to now() + delayed user stake period"],"type":"u64"},{"name":"pendingWithdrawalUnstakeScaled","docs":["User deposits unstaked, pending for withdrawal, not usable and not generating rewards.","(scaled from `Decimal` representation)."],"type":"u128"},{"name":"pendingWithdrawalUnstakeTs","docs":["After this timestamp, user can withdraw their deposit."],"type":"u64"},{"name":"bump","docs":["User bump used for account address validation"],"type":"u64"},{"name":"delegatee","docs":["Delegatee used for initialisation - useful to check against"],"type":"publicKey"},{"name":"lastStakeTs","type":"u64"},{"name":"rewardsIssuedCumulative","docs":["Cumulative rewards issued to the user - ONLY used for stats/analytics","DO NOT USE IN ANY CALCULATIONS","Old userStates will have this field populated only from the point of release","not reflecting any historical data before this was released"],"type":{"array":["u64",10]}},{"name":"padding1","type":{"array":["u64",40]}}]}},{"name":"OraclePrices","type":{"kind":"struct","fields":[{"name":"oracleMappings","type":"publicKey"},{"name":"prices","type":{"array":[{"defined":"DatedPrice"},512]}}]}}],"types":[{"name":"FarmConfigOption","type":{"kind":"enum","variants":[{"name":"UpdateRewardRps"},{"name":"UpdateRewardMinClaimDuration"},{"name":"WithdrawAuthority"},{"name":"DepositWarmupPeriod"},{"name":"WithdrawCooldownPeriod"},{"name":"RewardType"},{"name":"RpsDecimals"},{"name":"LockingMode"},{"name":"LockingStartTimestamp"},{"name":"LockingDuration"},{"name":"LockingEarlyWithdrawalPenaltyBps"},{"name":"DepositCapAmount"},{"name":"SlashedAmountSpillAddress"},{"name":"ScopePricesAccount"},{"name":"ScopeOraclePriceId"},{"name":"ScopeOracleMaxAge"},{"name":"UpdateRewardScheduleCurvePoints"},{"name":"UpdatePendingFarmAdmin"},{"name":"UpdateStrategyId"},{"name":"UpdateDelegatedRpsAdmin"},{"name":"UpdateVaultId"},{"name":"UpdateExtraDelegatedAuthority"},{"name":"UpdateIsRewardUserOnceEnabled"},{"name":"UpdateDelegatedAuthority"},{"name":"UpdateIsHarvestingPermissionless"}]}},{"name":"GlobalConfigOption","type":{"kind":"enum","variants":[{"name":"SetPendingGlobalAdmin"},{"name":"SetTreasuryFeeBps"}]}},{"name":"LockingMode","type":{"kind":"enum","variants":[{"name":"None"},{"name":"Continuous"},{"name":"WithExpiry"}]}},{"name":"RewardInfo","type":{"kind":"struct","fields":[{"name":"token","type":{"defined":"TokenInfo"}},{"name":"rewardsVault","type":"publicKey"},{"name":"rewardsAvailable","type":"u64"},{"name":"rewardScheduleCurve","type":{"defined":"RewardScheduleCurve"}},{"name":"minClaimDurationSeconds","type":"u64"},{"name":"lastIssuanceTs","type":"u64"},{"name":"rewardsIssuedUnclaimed","type":"u64"},{"name":"rewardsIssuedCumulative","type":"u64"},{"name":"rewardPerShareScaled","type":"u128"},{"name":"placeholder0","type":"u64"},{"name":"rewardType","type":"u8"},{"name":"rewardsPerSecondDecimals","type":"u8"},{"name":"padding0","type":{"array":["u8",6]}},{"name":"padding1","type":{"array":["u64",20]}}]}},{"name":"RewardPerTimeUnitPoint","type":{"kind":"struct","fields":[{"name":"tsStart","type":"u64"},{"name":"rewardPerTimeUnit","type":"u64"}]}},{"name":"RewardScheduleCurve","type":{"kind":"struct","fields":[{"name":"points","docs":["This is a stepwise function, meaning that each point represents","how many rewards are issued per time unit since the beginning","of that point until the beginning of the next point.","This is not a linear curve, there is no interpolation going on.","A curve can be [[t0, 100], [t1, 50], [t2, 0]]","meaning that from t0 to t1, 100 rewards are issued per time unit,","from t1 to t2, 50 rewards are issued per time unit, and after t2 it stops","Another curve, can be [[t0, 100], [u64::max, 0]]","meaning that from t0 to u64::max, 100 rewards are issued per time unit"],"type":{"array":[{"defined":"RewardPerTimeUnitPoint"},20]}}]}},{"name":"RewardType","type":{"kind":"enum","variants":[{"name":"Proportional"},{"name":"Constant"}]}},{"name":"TimeUnit","type":{"kind":"enum","variants":[{"name":"Seconds"},{"name":"Slots"}]}},{"name":"TokenInfo","type":{"kind":"struct","fields":[{"name":"mint","type":"publicKey"},{"name":"decimals","type":"u64"},{"name":"tokenProgram","type":"publicKey"},{"name":"padding","type":{"array":["u64",6]}}]}},{"name":"DatedPrice","type":{"kind":"struct","fields":[{"name":"price","type":{"defined":"Price"}},{"name":"lastUpdatedSlot","type":"u64"},{"name":"unixTimestamp","type":"u64"},{"name":"reserved","type":{"array":["u64",2]}},{"name":"reserved2","type":{"array":["u16",3]}},{"name":"index","type":"u16"}]}},{"name":"Price","type":{"kind":"struct","fields":[{"name":"value","type":"u64"},{"name":"exp","type":"u64"}]}}],"errors":[{"code":6000,"name":"StakeZero","msg":"Cannot stake 0 amount"},{"code":6001,"name":"UnstakeZero","msg":"Cannot unstake 0 amount"},{"code":6002,"name":"NothingToUnstake","msg":"Nothing to unstake"},{"code":6003,"name":"NoRewardToHarvest","msg":"No reward to harvest"},{"code":6004,"name":"NoRewardInList","msg":"Reward not present in reward list"},{"code":6005,"name":"RewardAlreadyInitialized","msg":"Reward already initialized"},{"code":6006,"name":"MaxRewardNumberReached","msg":"Max number of reward tokens reached"},{"code":6007,"name":"RewardDoesNotExist","msg":"Reward does not exist"},{"code":6008,"name":"WrongRewardVaultAccount","msg":"Reward vault exists but the account is wrong"},{"code":6009,"name":"RewardVaultMismatch","msg":"Reward vault pubkey does not match staking pool vault"},{"code":6010,"name":"RewardVaultAuthorityMismatch","msg":"Reward vault authority pubkey does not match staking pool vault"},{"code":6011,"name":"NothingStaked","msg":"Nothing staked, cannot collect any rewards"},{"code":6012,"name":"IntegerOverflow","msg":"Integer overflow"},{"code":6013,"name":"ConversionFailure","msg":"Conversion failure"},{"code":6014,"name":"UnexpectedAccount","msg":"Unexpected account in instruction"},{"code":6015,"name":"OperationForbidden","msg":"Operation forbidden"},{"code":6016,"name":"MathOverflow","msg":"Mathematical operation with overflow"},{"code":6017,"name":"MinClaimDurationNotReached","msg":"Minimum claim duration has not been reached"},{"code":6018,"name":"RewardsVaultHasDelegate","msg":"Reward vault has a delegate"},{"code":6019,"name":"RewardsVaultHasCloseAuthority","msg":"Reward vault has a close authority"},{"code":6020,"name":"FarmVaultHasDelegate","msg":"Farm vault has a delegate"},{"code":6021,"name":"FarmVaultHasCloseAuthority","msg":"Farm vault has a close authority"},{"code":6022,"name":"RewardsTreasuryVaultHasDelegate","msg":"Reward vault has a delegate"},{"code":6023,"name":"RewardsTreasuryVaultHasCloseAuthority","msg":"Reward vault has a close authority"},{"code":6024,"name":"UserAtaRewardVaultMintMissmatch","msg":"User ata and reward vault have different mints"},{"code":6025,"name":"UserAtaFarmTokenMintMissmatch","msg":"User ata and farm token have different mints"},{"code":6026,"name":"TokenFarmTokenMintMissmatch","msg":"Token mint and farm token have different mints"},{"code":6027,"name":"RewardAtaRewardMintMissmatch","msg":"Reward ata mint is different than reward mint"},{"code":6028,"name":"RewardAtaOwnerNotPayer","msg":"Reward ata owner is different than payer"},{"code":6029,"name":"InvalidGlobalConfigMode","msg":"Mode to update global_config is invalid"},{"code":6030,"name":"RewardIndexOutOfRange","msg":"Reward Index is higher than number of rewards"},{"code":6031,"name":"NothingToWithdraw","msg":"No tokens available to withdraw"},{"code":6032,"name":"UserDelegatedFarmNonDelegatedMissmatch","msg":"user, user_ref, authority and payer must match for non-delegated farm"},{"code":6033,"name":"AuthorityFarmDelegateMissmatch","msg":"Authority must match farm delegate authority"},{"code":6034,"name":"FarmNotDelegated","msg":"Farm not delegated, can not complete operation"},{"code":6035,"name":"FarmDelegated","msg":"Operation not allowed for delegated farm"},{"code":6036,"name":"UnstakeNotElapsed","msg":"Unstake lockup period is not elapsed. Deposit is locked until end of unstake period"},{"code":6037,"name":"PendingWithdrawalNotWithdrawnYet","msg":"Pending withdrawal already exist and not withdrawn yet"},{"code":6038,"name":"DepositZero","msg":"Cannot deposit zero amount directly to farm vault"},{"code":6039,"name":"InvalidConfigValue","msg":"Invalid config value"},{"code":6040,"name":"InvalidPenaltyPercentage","msg":"Invalid penalty percentage"},{"code":6041,"name":"EarlyWithdrawalNotAllowed","msg":"Early withdrawal not allowed"},{"code":6042,"name":"InvalidLockingTimestamps","msg":"Invalid locking timestamps"},{"code":6043,"name":"InvalidRpsCurvePoint","msg":"Invalid reward rate curve point"},{"code":6044,"name":"InvalidTimestamp","msg":"Invalid timestamp"},{"code":6045,"name":"DepositCapReached","msg":"Deposit cap reached"},{"code":6046,"name":"MissingScopePrices","msg":"Missing Scope Prices"},{"code":6047,"name":"ScopeOraclePriceTooOld","msg":"Scope Oracle Price Too Old"},{"code":6048,"name":"InvalidOracleConfig","msg":"Invalid Oracle Config"},{"code":6049,"name":"CouldNotDeserializeScope","msg":"Could not deserialize scope"},{"code":6050,"name":"RewardAtaOwnerNotAdmin","msg":"Reward ata owner is different than farm admin"},{"code":6051,"name":"WithdrawRewardZeroAvailable","msg":"Cannot withdraw reward as available amount is zero"},{"code":6052,"name":"RewardScheduleCurveSet","msg":"Cannot withdraw reward as reward schedule is set"},{"code":6053,"name":"UnsupportedTokenExtension","msg":"Cannot initialize farm while having a mint with token22 and requested extensions"},{"code":6054,"name":"InvalidFarmConfigUpdateAuthority","msg":"Invalid authority for updating farm config"},{"code":6055,"name":"InvalidTransferOwnershipOldOwner","msg":"Invalid authority for transfer ownersip new user state initialization"},{"code":6056,"name":"InvalidTransferOwnershipFarmState","msg":"Invalid farm state for transfer ownership new user state initialization"},{"code":6057,"name":"InvalidTransferOwnershipUserStateOwnerDelegatee","msg":"Invalid user state for transfer ownership, owner must match delegatee"},{"code":6058,"name":"InvalidTransferOwnershipFarmStateLockingMode","msg":"Invalid farm state locking mode for transfer ownership, must be 0"},{"code":6059,"name":"InvalidTransferOwnershipFarmStateWithdrawCooldownPeriod","msg":"Invalid farm state withdrawal cooldown period for transfer ownership, must be 0"},{"code":6060,"name":"InvalidTransferOwnershipStakeAmount","msg":"Invalid transfer ownership stake amount, must be equal to unstaked deposits"},{"code":6061,"name":"InvalidTransferOwnershipNewOwner","msg":"Invalid authority for transfer ownersip new user state initialization"},{"code":6062,"name":"InvalidTransferOwnershipFarmStateDepositWarmupPeriod","msg":"Invalid farm state deposit warmup period for transfer ownership, must be 0 if old user has stake"},{"code":6063,"name":"RewardUserOnceFeatureDisabled","msg":"Reward User Once feature is disabled"},{"code":6064,"name":"InvalidDelegatedAuthorityUpdate","msg":"Can not set delegate_authority to default pubkey - farm is delegated"},{"code":6065,"name":"UserTokenAccountOwnerMismatch","msg":"User token account owner does not match user state owner"},{"code":6066,"name":"HarvestingNotPermissionlessPayerMismatch","msg":"Harvesting is not permissionless, payer does not match user state owner"},{"code":6067,"name":"CurrentRewardIssuedUnclaimedMismatch","msg":"Current reward issued unclaimed does not match expected value"}]} \ No newline at end of file diff --git a/idls/kamino_lending.KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD.json b/idls/kamino_lending.KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD.json new file mode 100644 index 0000000..566e666 --- /dev/null +++ b/idls/kamino_lending.KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD.json @@ -0,0 +1 @@ +{"version":"1.23.0","name":"kamino_lending","instructions":[{"name":"initLendingMarket","accounts":[{"name":"lendingMarketOwner","isMut":true,"isSigner":true},{"name":"lendingMarket","isMut":true,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"quoteCurrency","type":{"array":["u8",32]}}]},{"name":"updateLendingMarket","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"lendingMarket","isMut":true,"isSigner":false}],"args":[{"name":"mode","type":"u64"},{"name":"value","type":{"array":["u8",72]}}]},{"name":"updateLendingMarketOwner","accounts":[{"name":"lendingMarketOwnerCached","isMut":false,"isSigner":true},{"name":"lendingMarket","isMut":true,"isSigner":false}],"args":[]},{"name":"initReserve","accounts":[{"name":"signer","isMut":true,"isSigner":true},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"feeReceiver","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveCollateralSupply","isMut":true,"isSigner":false},{"name":"initialLiquiditySource","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"cloneReserveConfig","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"targetLendingMarket","isMut":false,"isSigner":false,"docs":["The lending market of the [Self::target_reserve].","","The [Self::source_reserve] can come from a different market."]},{"name":"sourceReserve","isMut":false,"isSigner":false,"docs":["The reserve to be used as source for the configuration clone."]},{"name":"targetReserve","isMut":true,"isSigner":false,"docs":["The reserve to have its configuration replaced by a clone."]}],"args":[{"name":"customizations","type":{"defined":"ReserveConfigCustomizationArgs"}}]},{"name":"initFarmsForReserve","accounts":[{"name":"lendingMarketOwner","isMut":true,"isSigner":true},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"farmsProgram","isMut":false,"isSigner":false},{"name":"farmsGlobalConfig","isMut":false,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false},{"name":"farmsVaultAuthority","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"mode","type":"u8"}]},{"name":"updateReserveConfig","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false}],"args":[{"name":"mode","type":{"defined":"UpdateConfigMode"}},{"name":"value","type":"bytes"},{"name":"skipConfigIntegrityValidation","type":"bool"}]},{"name":"redeemFees","accounts":[{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveLiquidityFeeReceiver","isMut":true,"isSigner":false},{"name":"reserveSupplyLiquidity","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"withdrawProtocolFee","accounts":[{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":false,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"feeVault","isMut":true,"isSigner":false},{"name":"feeCollectorAta","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"seedDepositOnInitReserve","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"initialLiquiditySource","isMut":true,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"topupReserveRewards","docs":["Sponsor a reserve's rewards distribution by topping up its `rewards_amount_available` budget."],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"sourceLiquidity","isMut":true,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"socializeLoss","accounts":[{"name":"lendingMarketOwner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"socializeLossV2","accounts":[{"name":"socializeLossAccounts","accounts":[{"name":"lendingMarketOwner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"farmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"markObligationForDeleveraging","accounts":[{"name":"lendingMarketOwner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false}],"args":[{"name":"autodeleverageTargetLtvPct","type":"u8"}]},{"name":"refreshReserve","accounts":[{"name":"reserve","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"pythOracle","isMut":false,"isSigner":false,"isOptional":true},{"name":"switchboardPriceOracle","isMut":false,"isSigner":false,"isOptional":true},{"name":"switchboardTwapOracle","isMut":false,"isSigner":false,"isOptional":true},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true}],"args":[]},{"name":"refreshReservesBatch","accounts":[],"args":[{"name":"skipPriceUpdates","type":"bool"}]},{"name":"depositReserveLiquidity","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"reserve","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"userDestinationCollateral","isMut":true,"isSigner":false},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"redeemReserveCollateral","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"userSourceCollateral","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"}]},{"name":"initObligation","accounts":[{"name":"obligationOwner","isMut":false,"isSigner":true},{"name":"feePayer","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"seed1Account","isMut":false,"isSigner":false},{"name":"seed2Account","isMut":false,"isSigner":false},{"name":"ownerUserMetadata","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"InitObligationArgs"}}]},{"name":"initObligationFarmsForReserve","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"owner","isMut":false,"isSigner":false},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveFarmState","isMut":true,"isSigner":false},{"name":"obligationFarm","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"farmsProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"mode","type":"u8"}]},{"name":"refreshObligationFarmsForReserve","accounts":[{"name":"crank","isMut":false,"isSigner":true},{"name":"baseAccounts","accounts":[{"name":"obligation","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":false,"isSigner":false},{"name":"reserveFarmState","isMut":true,"isSigner":false,"docs":["`obligation_farm_user_state`'s own `farm_state` field, in the handler.","(Cannot live as an Anchor constraint because the handler is also reached","via `cpi_refresh_farms` from v2 host instructions, which bypass Anchor.)"]},{"name":"obligationFarmUserState","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false}]},{"name":"farmsProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"mode","type":"u8"}]},{"name":"refreshObligation","accounts":[{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"obligation","isMut":true,"isSigner":false}],"args":[]},{"name":"depositObligationCollateral","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"depositReserve","isMut":true,"isSigner":false},{"name":"reserveDestinationCollateral","isMut":true,"isSigner":false},{"name":"userSourceCollateral","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"}]},{"name":"depositObligationCollateralV2","accounts":[{"name":"depositAccounts","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"depositReserve","isMut":true,"isSigner":false},{"name":"reserveDestinationCollateral","isMut":true,"isSigner":false},{"name":"userSourceCollateral","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"farmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"}]},{"name":"withdrawObligationCollateral","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"withdrawReserve","isMut":true,"isSigner":false},{"name":"reserveSourceCollateral","isMut":true,"isSigner":false},{"name":"userDestinationCollateral","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"}]},{"name":"withdrawObligationCollateralV2","accounts":[{"name":"withdrawAccounts","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"withdrawReserve","isMut":true,"isSigner":false},{"name":"reserveSourceCollateral","isMut":true,"isSigner":false},{"name":"userDestinationCollateral","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"farmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"}]},{"name":"borrowObligationLiquidity","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"borrowReserve","isMut":true,"isSigner":false},{"name":"borrowReserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveSourceLiquidity","isMut":true,"isSigner":false},{"name":"borrowReserveLiquidityFeeReceiver","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"referrerTokenState","isMut":true,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"borrowObligationLiquidityV2","accounts":[{"name":"borrowAccounts","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"borrowReserve","isMut":true,"isSigner":false},{"name":"borrowReserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveSourceLiquidity","isMut":true,"isSigner":false},{"name":"borrowReserveLiquidityFeeReceiver","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"referrerTokenState","isMut":true,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"farmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"repayObligationLiquidity","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"repayReserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveDestinationLiquidity","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"repayObligationLiquidityV2","accounts":[{"name":"repayAccounts","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"repayReserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveDestinationLiquidity","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"farmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"repayAndWithdrawAndRedeem","accounts":[{"name":"repayAccounts","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"repayReserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveDestinationLiquidity","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"withdrawAccounts","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"withdrawReserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveSourceCollateral","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"placeholderUserDestinationCollateral","isMut":false,"isSigner":false,"isOptional":true},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"collateralFarmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"debtFarmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"repayAmount","type":"u64"},{"name":"withdrawCollateralAmount","type":"u64"}]},{"name":"depositAndWithdraw","accounts":[{"name":"depositAccounts","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveDestinationDepositCollateral","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"placeholderUserDestinationCollateral","isMut":false,"isSigner":false,"isOptional":true},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"withdrawAccounts","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"withdrawReserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveSourceCollateral","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"placeholderUserDestinationCollateral","isMut":false,"isSigner":false,"isOptional":true},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"depositFarmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"withdrawFarmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"},{"name":"withdrawCollateralAmount","type":"u64"}]},{"name":"depositReserveLiquidityAndObligationCollateral","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveDestinationDepositCollateral","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"placeholderUserDestinationCollateral","isMut":false,"isSigner":false,"isOptional":true},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"depositReserveLiquidityAndObligationCollateralV2","accounts":[{"name":"depositAccounts","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveDestinationDepositCollateral","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"placeholderUserDestinationCollateral","isMut":false,"isSigner":false,"isOptional":true},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"farmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"withdrawObligationCollateralAndRedeemReserveCollateral","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"withdrawReserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveSourceCollateral","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"placeholderUserDestinationCollateral","isMut":false,"isSigner":false,"isOptional":true},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"}]},{"name":"withdrawObligationCollateralAndRedeemReserveCollateralV2","accounts":[{"name":"withdrawAccounts","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"withdrawReserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveSourceCollateral","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"placeholderUserDestinationCollateral","isMut":false,"isSigner":false,"isOptional":true},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"farmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"}]},{"name":"liquidateObligationAndRedeemReserveCollateral","accounts":[{"name":"liquidator","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"repayReserve","isMut":true,"isSigner":false},{"name":"repayReserveLiquidityMint","isMut":false,"isSigner":false},{"name":"repayReserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"withdrawReserve","isMut":true,"isSigner":false},{"name":"withdrawReserveLiquidityMint","isMut":false,"isSigner":false},{"name":"withdrawReserveCollateralMint","isMut":true,"isSigner":false},{"name":"withdrawReserveCollateralSupply","isMut":true,"isSigner":false},{"name":"withdrawReserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"withdrawReserveLiquidityFeeReceiver","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"userDestinationCollateral","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"repayLiquidityTokenProgram","isMut":false,"isSigner":false},{"name":"withdrawLiquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"},{"name":"minAcceptableReceivedLiquidityAmount","type":"u64"},{"name":"maxAllowedLtvOverridePercent","type":"u64"}]},{"name":"liquidateObligationAndRedeemReserveCollateralV2","accounts":[{"name":"liquidationAccounts","accounts":[{"name":"liquidator","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"repayReserve","isMut":true,"isSigner":false},{"name":"repayReserveLiquidityMint","isMut":false,"isSigner":false},{"name":"repayReserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"withdrawReserve","isMut":true,"isSigner":false},{"name":"withdrawReserveLiquidityMint","isMut":false,"isSigner":false},{"name":"withdrawReserveCollateralMint","isMut":true,"isSigner":false},{"name":"withdrawReserveCollateralSupply","isMut":true,"isSigner":false},{"name":"withdrawReserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"withdrawReserveLiquidityFeeReceiver","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"userDestinationCollateral","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"repayLiquidityTokenProgram","isMut":false,"isSigner":false},{"name":"withdrawLiquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"collateralFarmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"debtFarmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"},{"name":"minAcceptableReceivedLiquidityAmount","type":"u64"},{"name":"maxAllowedLtvOverridePercent","type":"u64"}]},{"name":"flashRepayReserveLiquidity","accounts":[{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveDestinationLiquidity","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"reserveLiquidityFeeReceiver","isMut":true,"isSigner":false},{"name":"referrerTokenState","isMut":true,"isSigner":false,"isOptional":true},{"name":"referrerAccount","isMut":true,"isSigner":false,"isOptional":true},{"name":"sysvarInfo","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"},{"name":"borrowInstructionIndex","type":"u8"}]},{"name":"flashBorrowReserveLiquidity","accounts":[{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveSourceLiquidity","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"reserveLiquidityFeeReceiver","isMut":true,"isSigner":false},{"name":"referrerTokenState","isMut":true,"isSigner":false,"isOptional":true},{"name":"referrerAccount","isMut":true,"isSigner":false,"isOptional":true},{"name":"sysvarInfo","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"requestElevationGroup","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false}],"args":[{"name":"elevationGroup","type":"u8"}]},{"name":"initReferrerTokenState","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":false,"isSigner":false},{"name":"referrer","isMut":false,"isSigner":false},{"name":"referrerTokenState","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"initUserMetadata","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"feePayer","isMut":true,"isSigner":true},{"name":"userMetadata","isMut":true,"isSigner":false},{"name":"referrerUserMetadata","isMut":false,"isSigner":false,"isOptional":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"userLookupTable","type":"publicKey"}]},{"name":"withdrawReferrerFees","accounts":[{"name":"referrer","isMut":true,"isSigner":true},{"name":"referrerTokenState","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveSupplyLiquidity","isMut":true,"isSigner":false},{"name":"referrerTokenAccount","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"initReferrerStateAndShortUrl","accounts":[{"name":"referrer","isMut":true,"isSigner":true},{"name":"referrerState","isMut":true,"isSigner":false},{"name":"referrerShortUrl","isMut":true,"isSigner":false},{"name":"referrerUserMetadata","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"shortUrl","type":"string"}]},{"name":"deleteReferrerStateAndShortUrl","accounts":[{"name":"referrer","isMut":true,"isSigner":true},{"name":"referrerState","isMut":true,"isSigner":false},{"name":"shortUrl","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"setObligationOrder","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false}],"args":[{"name":"index","type":"u8"},{"name":"order","type":{"defined":"ObligationOrder"}}]},{"name":"setBorrowOrder","accounts":[{"name":"owner","isMut":false,"isSigner":true,"docs":["The [Self::obligation]'s owner."]},{"name":"obligation","isMut":true,"isSigner":false,"docs":["The obligation to set the [BorrowOrder] on."]},{"name":"lendingMarket","isMut":false,"isSigner":false,"docs":["The [Self::obligation]'s market - needed to validate feature flags and minimum order value."]},{"name":"reserve","isMut":false,"isSigner":false,"docs":["The reserve matching the [Self::debt_liquidity_mint] - needed for the minimum order value","check. On cancellation, this account is ignored (but still must belong to the same market)."]},{"name":"filledDebtDestination","isMut":false,"isSigner":false,"docs":["The [BorrowOrder::filled_debt_destination] to set on order creation. Not editable on order","updates.","Ignored when cancelling the order."]},{"name":"debtLiquidityMint","isMut":false,"isSigner":false,"docs":["The [BorrowOrder::debt_liquidity_mint] to set on order creation. Not editable on order","updates.","Ignored when cancelling the order."]},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"orderConfig","type":{"defined":"BorrowOrderConfigArgs"}},{"name":"minExpectedCurrentRemainingDebtAmount","type":"u64"}]},{"name":"updateObligationConfig","accounts":[{"name":"owner","isMut":false,"isSigner":true,"docs":["Obligation's owner (signer - only the owner can modify the obligation's configuration)."]},{"name":"obligation","isMut":true,"isSigner":false,"docs":["The obligation to update."]},{"name":"borrowReserve","isMut":false,"isSigner":false,"isOptional":true,"docs":["The reserve of a selected one among the [Obligation::borrows].","","Required only if the config update affects a single borrow (for validating its new config)."]},{"name":"depositReserve","isMut":false,"isSigner":false,"isOptional":true,"docs":["The reserve of a selected one among the [Obligation::deposits].","","Required only if the config update affects a single deposit (for validating its new config)."]},{"name":"lendingMarket","isMut":false,"isSigner":false,"docs":["The lending market (for validating the obligation's new config)."]}],"args":[{"name":"mode","type":{"defined":"UpdateObligationConfigMode"}},{"name":"value","type":"bytes"}]},{"name":"rolloverFixedTermBorrow","accounts":[{"name":"rolloverAccounts","accounts":[{"name":"payer","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false,"docs":["The obligation with a borrow to be rolled over (or migrated)."]},{"name":"lendingMarket","isMut":false,"isSigner":false,"docs":["The [Self::obligation]'s market, needed for validations against the market-wide","configuration."]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false,"docs":["The [Self::lending_market]'s authority, needed to transfer the newly-borrowed funds out of","the [Self::reserve_source_liquidity]."]},{"name":"sourceBorrowReserve","isMut":true,"isSigner":false,"docs":["The reserve used by the existing [Self::obligation]'s borrow that needs to rollover from."]},{"name":"targetBorrowReserve","isMut":true,"isSigner":false,"docs":["The reserve to rollover the borrow into - possibly (but not necessarily) the same as","[Self::source_borrow_reserve]'s (but definitely of the same liquidity mint)."]},{"name":"liquidityMint","isMut":false,"isSigner":false,"docs":["The mint of [Self::source_borrow_reserve] and [Self::target_borrow_reserve], needed to","transfer funds between them."]},{"name":"sourceBorrowReserveLiquidity","isMut":true,"isSigner":false,"docs":["The vault of [Self::source_borrow_reserve], from which the funds are transferred."]},{"name":"targetBorrowReserveLiquidity","isMut":true,"isSigner":false,"docs":["The vault of [Self::target_borrow_reserve], from which the funds are transferred."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["The token program of [Self::liquidity_mint] - needed to execute the transfer."]}]},{"name":"sourceFarmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"targetFarmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"fillBorrowOrder","accounts":[{"name":"borrowAccounts","accounts":[{"name":"payer","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false,"docs":["The obligation with a [BorrowOrder]."]},{"name":"lendingMarket","isMut":false,"isSigner":false,"docs":["The [Self::obligation]'s market - needed for borrowing-related configuration."]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false,"docs":["The [Self::lending_market]'s authority, needed to transfer the newly-borrowed funds out of","the [Self::reserve_source_liquidity]."]},{"name":"borrowReserve","isMut":true,"isSigner":false,"docs":["The reserve to borrow from.","","Its mint must match the asset requested by the [BorrowOrder::debt_liquidity_mint]."]},{"name":"borrowReserveLiquidityMint","isMut":false,"isSigner":false,"docs":["The mint of [Self::borrow_reserve] - needed to execute the transfer."]},{"name":"reserveSourceLiquidity","isMut":true,"isSigner":false,"docs":["The vault of [Self::borrow_reserve], from which the funds are transferred."]},{"name":"borrowReserveLiquidityFeeReceiver","isMut":true,"isSigner":false,"docs":["The fee vault of [Self::borrow_reserve], to which the fees are transferred."]},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false,"docs":["The destination token account that should receive the newly borrowed funds.","","It must match [BorrowOrder::filled_debt_destination], owner and mint.","","**Warning:** An altered destination account will prevent an order from being filled."]},{"name":"referrerTokenState","isMut":true,"isSigner":false,"isOptional":true,"docs":["The referrer's account, for accumulating fees - needed if the [Obligation::has_referrer]."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["The token program of [Self::borrow_reserve] - needed to execute the transfer."]},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"farmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"initiateObligationOwnershipTransfer","accounts":[{"name":"owner","isMut":false,"isSigner":true,"docs":["Current owner must sign this transaction"]},{"name":"obligation","isMut":true,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"newOwner","type":"publicKey"}]},{"name":"approveObligationOwnershipTransfer","accounts":[{"name":"globalAdmin","isMut":false,"isSigner":true,"docs":["Global admin must sign this transaction"]},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"obligation","isMut":true,"isSigner":false},{"name":"pendingOwner","isMut":false,"isSigner":false,"docs":["The pending owner that will receive ownership after acceptance.","Must match the pending_owner stored in the obligation."]}],"args":[]},{"name":"acceptObligationOwnership","accounts":[{"name":"pendingOwner","isMut":false,"isSigner":true,"docs":["Pending owner must sign this transaction"]},{"name":"obligation","isMut":true,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[]},{"name":"abortObligationOwnershipTransfer","accounts":[{"name":"owner","isMut":false,"isSigner":true,"docs":["Current owner must sign this transaction"]},{"name":"obligation","isMut":true,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[]},{"name":"enqueueToWithdraw","accounts":[{"name":"owner","isMut":true,"isSigner":true,"docs":["The depositor holding ctokens."]},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false,"docs":["The market's authority, needed to initialize the [Self::owner_queued_collateral_vault]."]},{"name":"reserve","isMut":true,"isSigner":false},{"name":"userSourceCollateralTa","isMut":true,"isSigner":false,"docs":["The source of collateral to be enqueued."]},{"name":"userDestinationLiquidityTa","isMut":false,"isSigner":false,"docs":["The account to which the liquidity should be finally transferred later (to be recorded in","the ticket)."]},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveCollateralMint","isMut":false,"isSigner":false},{"name":"collateralTokenProgram","isMut":false,"isSigner":false,"docs":["The collateral's program - needed for invoking the transfer to the vault *and* (implicitly)","for handling the `init_if_needed`."]},{"name":"withdrawTicket","isMut":true,"isSigner":false,"docs":["The new account to be initialized with the issued ticket's data."]},{"name":"ownerQueuedCollateralVault","isMut":true,"isSigner":false,"docs":["The per-owner \"this reserve's queued collateral\" vault (in which the collateral will be","locked)."]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["The System program - needed only for `init` / `init_if_needed` of the accounts above."]},{"name":"progressCallbackCustomAccount0","isMut":false,"isSigner":false,"isOptional":true,"docs":["The first one out of maximum 2 custom accounts that may be required if the optional \"ticket","progress callback\" is configured for this ticket.","","The expected custom accounts are validated based on the `progress_callback_type` instruction","argument. The currently supported ones are:","- for [ProgressCallbackType::None] no custom accounts are expected (this is the default","behavior, when the instruction data is zeroed or absent).","- for [ProgressCallbackType::KlendQueueAccountingHandlerOnKvault], only the `_0` custom","account is required:","- the `_0` custom account must represent the `VaultState` which disinvests from the","reserve (readonly; needed to be later passed as an input to the Kvault's handler);","- the `_1` custom account is ignored;","- the `owner` account (i.e. the signer) must be a Kvault-owned PDA base authority","associated with the vault indicated by `_0` (needed to prove that Kvault is CPI'ing this","handler)."]},{"name":"progressCallbackCustomAccount1","isMut":false,"isSigner":false,"isOptional":true,"docs":["The second possible account (see the `progress_callback_custom_account_0` above)."]},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"},{"name":"progressCallbackType","type":{"defined":"ProgressCallbackType"}}]},{"name":"withdrawQueuedLiquidity","accounts":[{"name":"payer","isMut":true,"isSigner":true,"docs":["The executor of the permissionless tx (not necessarily the ticket owner)."]},{"name":"lendingMarket","isMut":false,"isSigner":false,"docs":["The lending market."]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false,"docs":["The market's authority, needed for burning the collateral (from","[Self::owner_queued_collateral_vault]) and transferring the liquidity (from","[Self::reserve_liquidity_supply])."]},{"name":"reserve","isMut":true,"isSigner":false,"docs":["The reserve."]},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false,"docs":["The liquidity mint, needed to invoke the transfer."]},{"name":"reserveCollateralMint","isMut":true,"isSigner":false,"docs":["The collateral mint, needed to burn (`mut`!) the queued collateral."]},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false,"docs":["The liquidity supply vault (to withdraw the liquidity from)."]},{"name":"ownerQueuedCollateralVault","isMut":true,"isSigner":false,"docs":["The per-owner \"this reserve's queued collateral\" vault (from which the collateral will be","burnt)."]},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false,"docs":["The token account to which the liquidity should be transferred (the one recorded in the","ticket)."]},{"name":"collateralTokenProgram","isMut":false,"isSigner":false,"docs":["The program of [Self::reserve_collateral_mint], needed for transfer."]},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false,"docs":["The program of [Self::reserve_liquidity_mint], needed for transfer."]},{"name":"withdrawTicket","isMut":true,"isSigner":false,"docs":["The ticket's data itself.","","Note: in case of complete withdrawal, this account will be closed. In case of partial","withdrawal, its [WithdrawTicket::queued_collateral_amount] will simply be reduced, and the","ticket will maintain its position in the queue."]},{"name":"withdrawTicketOwner","isMut":true,"isSigner":false,"docs":["The owner of the [Self::withdraw_ticket]; needed only to return the rent of the","[WithdrawTicket] account (if it is getting fully-consumed and closed here)."]},{"name":"associatedTokenProgram","isMut":false,"isSigner":false,"docs":["The ATA program - needed for potential destination ATA creation."]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["The System program - needed for potential destination ATA creation."]},{"name":"progressCallbackProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["The progress callback program (if configured by the withdraw ticket)."]},{"name":"progressCallbackCustomAccount0","isMut":false,"isSigner":false,"isOptional":true,"docs":["The first one out of maximum 2 custom accounts that may be required if the withdraw ticket","defines a callback.","Please note that the constraints defined here do not mention `mut`, but a specific","[WithdrawTicket::progress_callback_type] may require it."]},{"name":"progressCallbackCustomAccount1","isMut":false,"isSigner":false,"isOptional":true,"docs":["The second possibly-required account (see the `progress_callback_custom_account_0` above)."]},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[],"returns":"bool"},{"name":"recoverInvalidTicketCollateral","accounts":[{"name":"payer","isMut":false,"isSigner":true,"docs":["The transaction executor.","","This instruction is, in principle, permissionless. However, only the ticket owner can use","arbitrary token account as destination for recovered collateral. Other signers can only","transfer the collateral to the ticket owner's ATA (see [Self::user_source_collateral])."]},{"name":"lendingMarket","isMut":false,"isSigner":false,"docs":["The lending market."]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false,"docs":["The market's authority, needed for transferring the collateral (from","[Self::owner_queued_collateral_vault])."]},{"name":"reserve","isMut":false,"isSigner":false,"docs":["The reserve, needed only to validate the other accounts."]},{"name":"reserveCollateralMint","isMut":false,"isSigner":false,"docs":["The collateral mint, needed to invoke the transfer."]},{"name":"ownerQueuedCollateralVault","isMut":true,"isSigner":false,"docs":["The per-owner \"this reserve's queued collateral\" vault (from which the collateral will be","recovered)."]},{"name":"userSourceCollateral","isMut":true,"isSigner":false,"docs":["The ticket's owner token account to which the ticket-locked collateral should be returned.","","Only the ticket's owner can indicate an arbitrary token account here. Permissionless","executors must indicate the ticket's owner ATA."]},{"name":"collateralTokenProgram","isMut":false,"isSigner":false,"docs":["The program of [Self::reserve_collateral_mint], needed for transfer."]},{"name":"withdrawTicket","isMut":true,"isSigner":false,"docs":["The ticket's account, necessarily marked as [WithdrawTicket::invalid] first (by the","`handler_withdraw_queued_liquidity`)."]},{"name":"withdrawTicketOwner","isMut":true,"isSigner":false,"docs":["The owner of the [Self::withdraw_ticket]; needed only to return the rent of the","[WithdrawTicket] account."]},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"ticketSequenceNumber","type":"u64"}]},{"name":"cancelWithdrawTicket","accounts":[{"name":"owner","isMut":false,"isSigner":true,"docs":["The ticket owner (i.e. the only allowed to cancel their ticket)."]},{"name":"lendingMarket","isMut":false,"isSigner":false,"docs":["The lending market."]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false,"docs":["The market's authority, needed for transferring the collateral (from","[Self::owner_queued_collateral_vault])."]},{"name":"reserve","isMut":true,"isSigner":false,"docs":["The reserve."]},{"name":"reserveCollateralMint","isMut":false,"isSigner":false,"docs":["The collateral mint, needed to invoke the transfer."]},{"name":"ownerQueuedCollateralVault","isMut":true,"isSigner":false,"docs":["The per-owner \"this reserve's queued collateral\" vault (from which the collateral will be","transferred back to the owner)."]},{"name":"userDestinationCollateral","isMut":true,"isSigner":false,"docs":["Destination for returned ctokens (owner's token account)."]},{"name":"collateralTokenProgram","isMut":false,"isSigner":false,"docs":["The program of [Self::reserve_collateral_mint], needed for transfer."]},{"name":"withdrawTicket","isMut":true,"isSigner":false,"docs":["Ticket to cancel - remains as tombstone until queue processes it."]}],"args":[{"name":"ticketSequenceNumber","type":"u64"},{"name":"collateralAmountToCancel","type":"u64"}]},{"name":"initGlobalConfig","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false},{"name":"programData","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"updateGlobalConfig","accounts":[{"name":"globalAdmin","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false}],"args":[{"name":"mode","type":{"defined":"UpdateGlobalConfigMode"}},{"name":"value","type":"bytes"}]},{"name":"updateGlobalConfigAdmin","accounts":[{"name":"pendingAdmin","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false}],"args":[]},{"name":"idlMissingTypes","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false}],"args":[{"name":"reserveFarmKind","type":{"defined":"ReserveFarmKind"}},{"name":"feeCalculation","type":{"defined":"FeeCalculation"}},{"name":"reserveStatus","type":{"defined":"ReserveStatus"}},{"name":"updateConfigMode","type":{"defined":"UpdateConfigMode"}},{"name":"updateLendingMarketConfigValue","type":{"defined":"UpdateLendingMarketConfigValue"}},{"name":"updateLendingMarketConfigMode","type":{"defined":"UpdateLendingMarketMode"}}]}],"accounts":[{"name":"UserState","type":{"kind":"struct","fields":[{"name":"userId","type":"u64"},{"name":"farmState","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"isFarmDelegated","type":"u8"},{"name":"padding0","type":{"array":["u8",7]}},{"name":"rewardsTallyScaled","type":{"array":["u128",10]}},{"name":"rewardsIssuedUnclaimed","type":{"array":["u64",10]}},{"name":"lastClaimTs","type":{"array":["u64",10]}},{"name":"activeStakeScaled","type":"u128"},{"name":"pendingDepositStakeScaled","type":"u128"},{"name":"pendingDepositStakeTs","type":"u64"},{"name":"pendingWithdrawalUnstakeScaled","type":"u128"},{"name":"pendingWithdrawalUnstakeTs","type":"u64"},{"name":"bump","type":"u64"},{"name":"delegatee","type":"publicKey"},{"name":"lastStakeTs","type":"u64"},{"name":"padding1","type":{"array":["u64",50]}}]}},{"name":"GlobalConfig","type":{"kind":"struct","fields":[{"name":"globalAdmin","docs":["Global admin of the program"],"type":"publicKey"},{"name":"pendingAdmin","docs":["Pending admin must sign a specific transaction to become the global admin"],"type":"publicKey"},{"name":"feeCollector","docs":["Fee collector is the only allowed owner of token accounts receiving protocol fees"],"type":"publicKey"},{"name":"padding","docs":["Padding to make the struct size 1024 bytes"],"type":{"array":["u8",928]}}]}},{"name":"LendingMarket","type":{"kind":"struct","fields":[{"name":"version","docs":["Version of lending market"],"type":"u64"},{"name":"bumpSeed","docs":["Bump seed for derived authority address"],"type":"u64"},{"name":"lendingMarketOwner","docs":["Owner authority which can add new reserves"],"type":"publicKey"},{"name":"lendingMarketOwnerCached","docs":["Temporary cache of the lending market owner, used in update_lending_market_owner"],"type":"publicKey"},{"name":"quoteCurrency","docs":["Currency market prices are quoted in","e.g. \"USD\" null padded (`*b\"USD\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"`) or a SPL token mint pubkey"],"type":{"array":["u8",32]}},{"name":"referralFeeBps","docs":["Referral fee for the lending market, as bps out of the total protocol fee"],"type":"u16"},{"name":"emergencyMode","type":"u8"},{"name":"autodeleverageEnabled","docs":["Whether the obligations on this market should be subject to auto-deleveraging after deposit","or borrow limit is crossed.","Besides this flag, the particular reserve's flag also needs to be enabled (logical `AND`).","**NOTE:** this also affects the individual \"target LTV\" deleveraging."],"type":"u8"},{"name":"borrowDisabled","type":"u8"},{"name":"priceRefreshTriggerToMaxAgePct","docs":["Refresh price from oracle only if it's older than this percentage of the price max age.","e.g. if the max age is set to 100s and this is set to 80%, the price will be refreshed if it's older than 80s.","Price is always refreshed if this set to 0."],"type":"u8"},{"name":"liquidationMaxDebtCloseFactorPct","docs":["Percentage of the total borrowed value in an obligation available for liquidation"],"type":"u8"},{"name":"insolvencyRiskUnhealthyLtvPct","docs":["Minimum acceptable unhealthy LTV before max_debt_close_factor_pct becomes 100%"],"type":"u8"},{"name":"minFullLiquidationValueThreshold","docs":["Minimum liquidation value threshold triggering full liquidation for an obligation, in full","units of the quote currency (e.g. `2` means \"$2\", not \"2 lamports of USDC\")."],"type":"u64"},{"name":"maxLiquidatableDebtMarketValueAtOnce","docs":["Max allowed liquidation value in one ix call"],"type":"u64"},{"name":"reserved0","docs":["[DEPRECATED] Global maximum unhealthy borrow value allowed for any obligation"],"type":{"array":["u8",8]}},{"name":"globalAllowedBorrowValue","docs":["Global maximum allowed borrow value allowed for any obligation"],"type":"u64"},{"name":"emergencyCouncil","docs":["The address of the emergency council, in charge of taking emergency actions on the market (e.g., enabling emergency mode)"],"type":"publicKey"},{"name":"reserved1","docs":["[DEPRECATED] Reward points multiplier per obligation type"],"type":{"array":["u8",8]}},{"name":"elevationGroups","docs":["Elevation groups are used to group together reserves that have the same risk parameters and can bump the ltv and liquidation threshold"],"type":{"array":[{"defined":"ElevationGroup"},32]}},{"name":"elevationGroupPadding","type":{"array":["u64",90]}},{"name":"minNetValueInObligationSf","docs":["Min net value accepted to be found in a position after any lending action in an obligation (scaled by quote currency decimals)"],"type":"u128"},{"name":"minValueSkipLiquidationLtvChecks","docs":["Minimum value to enforce smallest ltv priority checks on the collateral reserves on liquidation"],"type":"u64"},{"name":"name","docs":["Market name, zero-padded."],"type":{"array":["u8",32]}},{"name":"minValueSkipLiquidationBfChecks","docs":["Minimum value to enforce highest borrow factor priority checks on the debt reserves on liquidation"],"type":"u64"},{"name":"individualAutodeleverageMarginCallPeriodSecs","docs":["Time (in seconds) that must pass before liquidation is allowed on an obligation that has","been individually marked for auto-deleveraging."],"type":"u64"},{"name":"minInitialDepositAmount","docs":["Minimum amount of deposit at creation of a reserve to prevent artificial inflation","Note: this amount cannot be recovered, the ctoken associated are never minted"],"type":"u64"},{"name":"obligationOrderExecutionEnabled","docs":["Whether the obligation orders should be evaluated during liquidations."],"type":"u8"},{"name":"immutable","docs":["Whether the lending market is set as immutable."],"type":"u8"},{"name":"obligationOrderCreationEnabled","docs":["Whether new obligation orders can be created.","Note: updating or cancelling existing orders is *not* affected by this flag."],"type":"u8"},{"name":"priceTriggeredLiquidationDisabled","docs":["Whether the liquidation operations that are triggered by price changes should be disabled.","This includes regular liquidation (i.e. LTV exceeding the unhealthy threshold) and some","obligation orders' execution.","","*Caution:* this flag is *disabling* the liquidations when `1` - contrary to all the other","liquidation-driving flags (see e.g. [Self::autodeleverage_enabled])."],"type":"u8"},{"name":"matureReserveDebtLiquidationEnabled","docs":["Whether the debts that reached their reserve's [ReserveConfig::debt_maturity_timestamp] can","be liquidated."],"type":"u8"},{"name":"obligationBorrowDebtTermLiquidationEnabled","docs":["Whether the [Obligation::borrows] that reached their [ReserveConfig::debt_term_seconds] can","be liquidated."],"type":"u8"},{"name":"borrowOrderCreationEnabled","docs":["Whether new borrow orders can be created.","Note: updating or cancelling existing orders is *not* affected by this flag."],"type":"u8"},{"name":"borrowOrderExecutionEnabled","docs":["Whether the existing borrow orders can be filled."],"type":"u8"},{"name":"proposerAuthority","docs":["Authority that can propose creating of new reserves but cannot enable them."],"type":"publicKey"},{"name":"minBorrowOrderFillValue","docs":["Minimum value that can be filled in a single `fill_borrow_order()` call, in full units of","the quote currency (e.g. `2` means \"$2\", not \"2 lamports of USDC\")."],"type":"u64"},{"name":"withdrawTicketIssuanceEnabled","docs":["Whether any new withdraw tickets can be issued (i.e. whether new requests can enter the","withdraw queue)."],"type":"u8"},{"name":"withdrawTicketRedemptionEnabled","docs":["Whether the existing withdraw tickets can be redeemed (i.e. whether the tickets can be used","to transfer accumulated pending liquidity to destination accounts)."],"type":"u8"},{"name":"obligationBorrowRolloverConfigurationEnabled","docs":["Whether the owners can enable the borrow rollover/migration on their obligations.","","*Note 1:* the actual execution of (different kinds of) rollovers are enabled/disabled by:","- [Self::fixed_term_rollover_window_duration_seconds],","- [Self::open_term_rollover_window_duration_seconds],","- [Self::obligation_borrow_migration_to_fixed_execution_enabled].","","*Note 2:* when this configuration is disabled, the obligation owners can still disable their","rollover (i.e. set the obligation's flags to zeroes)."],"type":"u8"},{"name":"obligationBorrowMigrationToFixedExecutionEnabled","docs":["Whether the actual execution of a \"migration to fixed\" rollover flavor is allowed.","","See [FixedTermBorrowRolloverConfig::migration_to_fixed_enabled]."],"type":"u8"},{"name":"withdrawTicketCancellationEnabled","docs":["Whether the ticket owners can cancel their withdraw tickets (i.e. recover ctokens from the","queued collateral vault back to their wallet)."],"type":"u8"},{"name":"padding2","type":{"array":["u8",1]}},{"name":"reserveRewardsMaxAprBps","docs":["Maximum APR (in basis points; `FULL_BPS = 10_000` = 100%) at which reserves on this market","may distribute their `rewards_amount_per_slot`. `0` disables rewards on this market","entirely (`topup_reserve_rewards` is rejected). Bounded by `FULL_BPS` (100% APR) when set.","See [ReserveConfig::rewards_amount_per_slot] for the depositor-cap interaction."],"type":"u16"},{"name":"minWithdrawQueuedLiquidityValue","docs":["Minimum value that can be withdrawn in a single `withdraw_queued_liquidity()` call, in full","units of the quote currency (e.g. `2` means \"$2\", not \"2 lamports of USDC\")."],"type":"u64"},{"name":"fixedTermRolloverWindowDurationSeconds","docs":["A configurable time window (right before the end of a fixed debt term) during which an","auto-rollover into another *fixed* rate/term can happen.","","When zeroed, this rollover mode is effectively disabled.","Can only be enabled when [Self::min_partial_rollover_value] is configured.","","See [FixedTermBorrowRolloverConfig]."],"type":"u64"},{"name":"openTermRolloverWindowDurationSeconds","docs":["A configurable time window (right before the end of a fixed debt term) during which an","auto-rollover into a *variable* (indefinite) rate/term can happen.","","When zeroed, this rollover mode is effectively disabled.","Can only be enabled when [Self::min_partial_rollover_value] is configured.","","This will typically be shorter than [Self::fixed_term_rollover_window_duration_seconds],","acting as a fallback if a fixed reserve liquidity remains unavailable for considerable time."],"type":"u64"},{"name":"minPartialRolloverValue","docs":["Minimum dollar value for a partial rollover into a different reserve.","When the achievable rollover amount is below this threshold (and it's not a full rollover),","the rollover is rejected.","","In full units of the quote currency (e.g. `2` means \"$2\")."],"type":"u64"},{"name":"termBasedFullLiquidationDurationSecs","docs":["The time that must pass before an entire expired debt becomes liquidatable.","","For example:","Let's assume this duration is configured as 100 seconds; then:","- right after fixed-term debt expiration, effectively no debt can be liquidated.","- 30 seconds after expiration, we allow to 30% of the expired debt to be liquidated","- to be specific: at this point in time, we \"protect\" from liquidation 70% of the","[ObligationLiquidity::borrowed_amount_at_expiration] (regardless of how much interest","was accrued or how much debt was repaid while expired).","- 100 seconds after expiration we allow the entire debt to be liquidated.","","Only effective when [Self::obligation_borrow_debt_term_liquidation_enabled].","","Motivation note: this throttling feature gives an opportunity to execute a configured","auto-rollover (after a partial liquidation brings the debt size down so that there is enough","available liquidity in some compatible reserve).","","When zeroed, an entire expired debt can be liquidated right after expiration (i.e. no","throttling)."],"type":"u64"},{"name":"permissioningAuthority","docs":["If not NULL, operations encoded in permissioned_ops require a signature from this authority"],"type":"publicKey"},{"name":"permissionedOps","docs":["Bitmap of operations that require permissioning authority signature"],"type":"u64"},{"name":"padding1","type":{"array":["u64",153]}}]}},{"name":"Obligation","docs":["Lending market obligation state"],"type":{"kind":"struct","fields":[{"name":"tag","docs":["Version of the struct"],"type":"u64"},{"name":"lastUpdate","docs":["Last update to collateral, liquidity, or their market values"],"type":{"defined":"LastUpdate"}},{"name":"lendingMarket","docs":["Lending market address"],"type":"publicKey"},{"name":"owner","docs":["Owner authority which can borrow liquidity"],"type":"publicKey"},{"name":"deposits","docs":["Deposited collateral for the obligation, unique by deposit reserve address"],"type":{"array":[{"defined":"ObligationCollateral"},8]}},{"name":"lowestReserveDepositLiquidationLtv","docs":["Worst LTV for the collaterals backing the loan, represented as a percentage"],"type":"u64"},{"name":"depositedValueSf","docs":["Market value of deposits (scaled fraction)"],"type":"u128"},{"name":"borrows","docs":["Borrowed liquidity for the obligation, unique by borrow reserve address"],"type":{"array":[{"defined":"ObligationLiquidity"},5]}},{"name":"borrowFactorAdjustedDebtValueSf","docs":["Risk adjusted market value of borrows/debt (sum of price * borrowed_amount * borrow_factor) (scaled fraction)"],"type":"u128"},{"name":"borrowedAssetsMarketValueSf","docs":["Market value of borrows - used for max_liquidatable_borrowed_amount (scaled fraction)"],"type":"u128"},{"name":"allowedBorrowValueSf","docs":["The maximum borrow value at the weighted average loan to value ratio (scaled fraction)"],"type":"u128"},{"name":"unhealthyBorrowValueSf","docs":["The dangerous borrow value at the weighted average liquidation threshold (scaled fraction)"],"type":"u128"},{"name":"paddingDeprecatedAssetTiers","docs":["The asset tier of the deposits"],"type":{"array":["u8",13]}},{"name":"elevationGroup","docs":["The elevation group id the obligation opted into."],"type":"u8"},{"name":"numOfObsoleteDepositReserves","docs":["The number of obsolete reserves the obligation has a deposit in"],"type":"u8"},{"name":"hasDebt","docs":["Marked = 1 if borrows array is not empty, 0 = borrows empty"],"type":"u8"},{"name":"referrer","docs":["Wallet address of the referrer"],"type":"publicKey"},{"name":"borrowingDisabled","docs":["Marked = 1 if borrowing disabled, 0 = borrowing enabled"],"type":"u8"},{"name":"autodeleverageTargetLtvPct","docs":["A target LTV set by the market owner when marking this obligation for deleveraging.","Only effective when `deleveraging_margin_call_started_slot != 0`."],"type":"u8"},{"name":"lowestReserveDepositMaxLtvPct","docs":["The lowest max LTV found amongst the collateral deposits"],"type":"u8"},{"name":"numOfObsoleteBorrowReserves","docs":["The number of obsolete reserves the obligation has a borrow in"],"type":"u8"},{"name":"ownershipTransferState","docs":["State of ownership transfer, see [OwnershipTransferState]"],"type":"u8"},{"name":"reserved","type":{"array":["u8",3]}},{"name":"highestBorrowFactorPct","type":"u64"},{"name":"autodeleverageMarginCallStartedTimestamp","docs":["A timestamp at which the market owner most-recently marked this obligation for deleveraging.","Zero if not currently subject to deleveraging."],"type":"u64"},{"name":"obligationOrders","docs":["Owner-defined, permissionlessly-executed repay orders.","Typical use-cases would be a stop-loss and a take-profit (possibly co-existing)."],"type":{"array":[{"defined":"ObligationOrder"},2]}},{"name":"borrowOrder","docs":["Owner-defined, permissionlessly-executed borrow order applicable to this obligation.","Non-zeroed only on a newly-initialized fixed-rate, fixed-term obligation."],"type":{"defined":"BorrowOrder"}},{"name":"pendingOwner","docs":["Pending owner during ownership transfer process.","Pubkey::default() means no pending owner (similar to Option::None)"],"type":"publicKey"},{"name":"padding3","type":{"array":["u64",69]}}]}},{"name":"ReferrerState","type":{"kind":"struct","fields":[{"name":"shortUrl","type":"publicKey"},{"name":"owner","type":"publicKey"}]}},{"name":"ReferrerTokenState","docs":["Referrer account -> each owner can have multiple accounts for specific reserves"],"type":{"kind":"struct","fields":[{"name":"referrer","docs":["Pubkey of the referrer/owner"],"type":"publicKey"},{"name":"mint","docs":["Token mint for the account"],"type":"publicKey"},{"name":"amountUnclaimedSf","docs":["Amount that has been accumulated and not claimed yet -> available to claim (scaled fraction)"],"type":"u128"},{"name":"amountCumulativeSf","docs":["Amount that has been accumulated in total -> both already claimed and unclaimed (scaled fraction)"],"type":"u128"},{"name":"bump","docs":["Referrer token state bump, used for address validation"],"type":"u64"},{"name":"padding","type":{"array":["u64",31]}}]}},{"name":"ShortUrl","type":{"kind":"struct","fields":[{"name":"referrer","type":"publicKey"},{"name":"shortUrl","type":"string"}]}},{"name":"UserMetadata","docs":["Referrer account -> each owner can have multiple accounts for specific reserves"],"type":{"kind":"struct","fields":[{"name":"referrer","docs":["Pubkey of the referrer/owner - pubkey::default if no referrer"],"type":"publicKey"},{"name":"bump","docs":["Bump used for validation of account address"],"type":"u64"},{"name":"userLookupTable","docs":["User lookup table - used to store all user accounts - atas for each reserve mint, each obligation PDA, UserMetadata itself and all referrer_token_states if there is a referrer"],"type":"publicKey"},{"name":"owner","docs":["User metadata account owner"],"type":"publicKey"},{"name":"padding1","type":{"array":["u64",51]}},{"name":"padding2","type":{"array":["u64",64]}}]}},{"name":"Reserve","type":{"kind":"struct","fields":[{"name":"version","docs":["Version of the reserve"],"type":"u64"},{"name":"lastUpdate","docs":["Last slot when supply and rates updated"],"type":{"defined":"LastUpdate"}},{"name":"lendingMarket","docs":["Lending market address"],"type":"publicKey"},{"name":"farmCollateral","type":"publicKey"},{"name":"farmDebt","type":"publicKey"},{"name":"liquidity","docs":["Reserve liquidity"],"type":{"defined":"ReserveLiquidity"}},{"name":"reserveLiquidityPadding","type":{"array":["u64",150]}},{"name":"collateral","docs":["Reserve collateral"],"type":{"defined":"ReserveCollateral"}},{"name":"reserveCollateralPadding","type":{"array":["u64",150]}},{"name":"config","docs":["Reserve configuration values"],"type":{"defined":"ReserveConfig"}},{"name":"configPadding","type":{"array":["u64",112]}},{"name":"borrowedAmountOutsideElevationGroup","type":"u64"},{"name":"borrowedAmountsAgainstThisReserveInElevationGroups","docs":["Amount of token borrowed in lamport of debt asset in the given","elevation group when this reserve is part of the collaterals."],"type":{"array":["u64",32]}},{"name":"withdrawQueue","docs":["The tracker of ticket-based withdrawals."],"type":{"defined":"WithdrawQueue"}},{"name":"padding","type":{"array":["u64",204]}}]}},{"name":"WithdrawTicket","docs":["A finite-lifecycle account representing a specific depositor's place in the withdraw queue of","a specific reserve.","","The lifecycle:","1. The depositor holding ctokens wants to withdraw funds from the reserve, and finds out that","the required amount is not available (due to high utilization).","2. The depositor calls the `enqueue_to_withdraw` handler.","3. The handler transfers the depositor's ctokens to the reserve's internal \"pending\" vault.","4. The handler initializes a new [WithdrawTicket] account, with the next available sequence","number.","5. The depositor waits until his ticket is the next expected one for actual withdraw, and until","the reserve has enough liquidity.","6. Anyone (the depositor or a bot) calls the permissionless `withdraw_queued_liquidity`","handler. If the ticket became invalid (e.g. destination account no longer exists), then the","depositor can call the `recover_invalid_ticket_collateral` handler instead.","7. The handler transfers the liquidity amount according to the current exchange rate.","8. The handler closes the ticket account."],"type":{"kind":"struct","fields":[{"name":"sequenceNumber","docs":["This ticket's place in the queue; the same as used for PDA derivation."],"type":"u64"},{"name":"owner","docs":["The funds' owner (the user who called the `enqueue_to_withdraw` handler)."],"type":"publicKey"},{"name":"reserve","docs":["The reserve to withdraw from."],"type":"publicKey"},{"name":"userDestinationLiquidityTa","docs":["The token account to which the finally-available liquidity should be transferred (by the","`withdraw_queued_liquidity` handler)."],"type":"publicKey"},{"name":"queuedCollateralAmount","docs":["The amount of collateral still waiting to be withdrawn using this ticket."],"type":"u64"},{"name":"createdAtTimestamp","docs":["The timestamp at which the queue was entered.","","This is currently only a piece of metadata, not used by the logic."],"type":"u64"},{"name":"invalid","docs":["Whether the ticket has been found to be invalid (e.g. the [Self::user_destination_liquidity]","has been repurposed) by the `withdraw_queued_liquidity` handler.","To be specific: valid = `0`, invalid = `1`.","","An invalid ticket cannot be made valid again, and can only be passed to the","`recover_invalid_ticket_collateral` handler."],"type":"u8"},{"name":"progressCallbackType","docs":["One of the valid [ProgressCallbackType] representations."],"type":"u8"},{"name":"alignmentPadding","docs":["Inner padding, for alignment."],"type":{"array":["u8",6]}},{"name":"progressCallbackCustomAccounts","docs":["The (optional) accounts to be used by [Self::progress_callback_type]s."],"type":{"array":["publicKey",2]}},{"name":"endPadding","docs":["Trailing padding, for future developments."],"type":{"array":["u64",40]}}]}}],"types":[{"name":"ReserveConfigCustomizationArgs","docs":["A definition of optional customizations that should be applied after cloning the config."],"type":{"kind":"struct","fields":[{"name":"overrideFixedRateBps","docs":["A gate for [Self::fixed_borrow_rate_bps]."],"type":"u8"},{"name":"fixedBorrowRateBps","docs":["If [Self::override_fixed_rate_bps] is non-zero, this borrow rate will be used to override","the [ReserveConfig::borrow_rate_curve] with a fixed one."],"type":"u32"},{"name":"overrideDebtTermSeconds","docs":["A gate for [Self::debt_term_seconds]."],"type":"u8"},{"name":"debtTermSeconds","docs":["If [Self::override_debt_term_seconds] is non-zero, this value will be used to override the","[ReserveConfig::debt_term_seconds]."],"type":"u64"},{"name":"clearElevationGroups","docs":["Whether the target reserve should have zeroed [ReserveConfig::elevation_groups] (i.e. not","cloned from source).","","This customization is mandatory when cloning a reserve (with some elevation groups) into a","different market (where those elevation group indices would have different meaning)."],"type":"u8"}]}},{"name":"BorrowOrderConfigArgs","docs":["A subset of [BorrowOrderConfig] excluding the accounts passed via [SetBorrowOrder]."],"type":{"kind":"struct","fields":[{"name":"remainingDebtAmount","type":"u64"},{"name":"maxBorrowRateBps","type":"u32"},{"name":"minDebtTermSeconds","type":"u64"},{"name":"fillableUntilTimestamp","type":"u64"},{"name":"enableAutoRolloverOnFilledBorrows","type":"bool"}]}},{"name":"UpdateConfigMode","type":{"kind":"enum","variants":[{"name":"UpdateLoanToValuePct"},{"name":"UpdateMaxLiquidationBonusBps"},{"name":"UpdateLiquidationThresholdPct"},{"name":"UpdateProtocolLiquidationFee"},{"name":"UpdateProtocolTakeRate"},{"name":"UpdateFeesOriginationFee"},{"name":"UpdateFeesFlashLoanFee"},{"name":"DeprecatedUpdateFeesReferralFeeBps"},{"name":"UpdateDepositLimit"},{"name":"UpdateBorrowLimit"},{"name":"UpdateTokenInfoLowerHeuristic"},{"name":"UpdateTokenInfoUpperHeuristic"},{"name":"UpdateTokenInfoExpHeuristic"},{"name":"UpdateTokenInfoTwapDivergence"},{"name":"UpdateTokenInfoScopeTwap"},{"name":"UpdateTokenInfoScopeChain"},{"name":"UpdateTokenInfoName"},{"name":"UpdateTokenInfoPriceMaxAge"},{"name":"UpdateTokenInfoTwapMaxAge"},{"name":"UpdateScopePriceFeed"},{"name":"UpdatePythPrice"},{"name":"UpdateSwitchboardFeed"},{"name":"UpdateSwitchboardTwapFeed"},{"name":"UpdateBorrowRateCurve"},{"name":"DeprecatedUpdateEntireReserveConfig"},{"name":"UpdateDebtWithdrawalCap"},{"name":"UpdateDepositWithdrawalCap"},{"name":"DeprecatedUpdateDebtWithdrawalCapCurrentTotal"},{"name":"DeprecatedUpdateDepositWithdrawalCapCurrentTotal"},{"name":"UpdateBadDebtLiquidationBonusBps"},{"name":"UpdateMinLiquidationBonusBps"},{"name":"UpdateDeleveragingMarginCallPeriod"},{"name":"UpdateBorrowFactor"},{"name":"DeprecatedUpdateAssetTier"},{"name":"UpdateElevationGroup"},{"name":"UpdateDeleveragingThresholdDecreaseBpsPerDay"},{"name":"DeprecatedUpdateMultiplierSideBoost"},{"name":"DeprecatedUpdateMultiplierTagBoost"},{"name":"UpdateReserveStatus"},{"name":"UpdateFarmCollateral"},{"name":"UpdateFarmDebt"},{"name":"UpdateDisableUsageAsCollateralOutsideEmode"},{"name":"UpdateBlockBorrowingAboveUtilizationPct"},{"name":"UpdateBlockPriceUsage"},{"name":"UpdateBorrowLimitOutsideElevationGroup"},{"name":"UpdateBorrowLimitsInElevationGroupAgainstThisReserve"},{"name":"UpdateHostFixedInterestRateBps"},{"name":"UpdateAutodeleverageEnabled"},{"name":"UpdateDeleveragingBonusIncreaseBpsPerDay"},{"name":"UpdateProtocolOrderExecutionFee"},{"name":"UpdateProposerAuthorityLock"},{"name":"UpdateMinDeleveragingBonusBps"},{"name":"UpdateBlockCTokenUsage"},{"name":"UpdateDebtMaturityTimestamp"},{"name":"UpdateDebtTermSeconds"},{"name":"UpdateEarlyRepayRemainingInterestPct"},{"name":"UpdateReserveEmergencyMode"},{"name":"UpdateRewardsAmountPerSlot"},{"name":"UpdateReservePermissionedOps"}]}},{"name":"UpdateLendingMarketConfigValue","type":{"kind":"enum","variants":[{"name":"Bool","fields":["bool"]},{"name":"U8","fields":["u8"]},{"name":"U8Array","fields":[{"array":["u8",8]}]},{"name":"U16","fields":["u16"]},{"name":"U64","fields":["u64"]},{"name":"U128","fields":["u128"]},{"name":"Pubkey","fields":["publicKey"]},{"name":"ElevationGroup","fields":[{"defined":"ElevationGroup"}]},{"name":"Name","fields":[{"array":["u8",32]}]}]}},{"name":"UpdateLendingMarketMode","type":{"kind":"enum","variants":[{"name":"UpdateOwner"},{"name":"UpdateEmergencyMode"},{"name":"UpdateLiquidationCloseFactor"},{"name":"UpdateLiquidationMaxValue"},{"name":"DeprecatedUpdateGlobalUnhealthyBorrow"},{"name":"UpdateGlobalAllowedBorrow"},{"name":"UpdateEmergencyCouncil"},{"name":"UpdateMinFullLiquidationThreshold"},{"name":"UpdateInsolvencyRiskLtv"},{"name":"UpdateElevationGroup"},{"name":"UpdateReferralFeeBps"},{"name":"DeprecatedUpdateMultiplierPoints"},{"name":"UpdatePriceRefreshTriggerToMaxAgePct"},{"name":"UpdateAutodeleverageEnabled"},{"name":"UpdateBorrowingDisabled"},{"name":"UpdateMinNetValueObligationPostAction"},{"name":"UpdateMinValueLtvSkipPriorityLiqCheck"},{"name":"UpdateMinValueBfSkipPriorityLiqCheck"},{"name":"UpdatePaddingFields"},{"name":"UpdateName"},{"name":"UpdateIndividualAutodeleverageMarginCallPeriodSecs"},{"name":"UpdateInitialDepositAmount"},{"name":"UpdateObligationOrderExecutionEnabled"},{"name":"UpdateImmutableFlag"},{"name":"UpdateObligationOrderCreationEnabled"},{"name":"UpdateProposerAuthority"},{"name":"UpdatePriceTriggeredLiquidationDisabled"},{"name":"UpdateMatureReserveDebtLiquidationEnabled"},{"name":"UpdateObligationBorrowDebtTermLiquidationEnabled"},{"name":"UpdateBorrowOrderCreationEnabled"},{"name":"UpdateBorrowOrderExecutionEnabled"},{"name":"UpdateMinBorrowOrderFillValue"},{"name":"UpdateWithdrawTicketIssuanceEnabled"},{"name":"UpdateWithdrawTicketRedemptionEnabled"},{"name":"UpdateMinWithdrawQueuedLiquidityValue"},{"name":"UpdateFixedTermRolloverWindowDurationSeconds"},{"name":"UpdateOpenTermRolloverWindowDurationSeconds"},{"name":"UpdateObligationBorrowRolloverConfigurationEnabled"},{"name":"UpdateTermBasedFullLiquidationDurationSecs"},{"name":"UpdateObligationBorrowMigrationToFixedExecutionEnabled"},{"name":"UpdateMinPartialRolloverValue"},{"name":"UpdateWithdrawTicketCancellationEnabled"},{"name":"UpdatePermissioningAuthority"},{"name":"UpdatePermissionedOps"},{"name":"DeprecatedUpdateReserveRewardsMaxAprPct"},{"name":"UpdateReserveRewardsMaxAprBps"}]}},{"name":"UpdateGlobalConfigMode","type":{"kind":"enum","variants":[{"name":"PendingAdmin"},{"name":"FeeCollector"}]}},{"name":"LastUpdate","docs":["Last update state"],"type":{"kind":"struct","fields":[{"name":"slot","docs":["Last slot when updated"],"type":"u64"},{"name":"stale","docs":["True when marked stale, false when slot updated"],"type":"u8"},{"name":"priceStatus","docs":["Status of the prices used to calculate the last update"],"type":"u8"},{"name":"placeholder","type":{"array":["u8",6]}}]}},{"name":"ElevationGroup","type":{"kind":"struct","fields":[{"name":"maxLiquidationBonusBps","type":"u16"},{"name":"id","type":"u8"},{"name":"ltvPct","type":"u8"},{"name":"liquidationThresholdPct","type":"u8"},{"name":"allowNewLoans","type":"u8"},{"name":"maxReservesAsCollateral","type":"u8"},{"name":"padding0","type":"u8"},{"name":"debtReserve","docs":["Mandatory debt reserve for this elevation group"],"type":"publicKey"},{"name":"padding1","type":{"array":["u64",4]}}]}},{"name":"BorrowOrder","docs":["A borrow order.","","When the [Obligation::borrow_order] is populated (i.e. non-zeroed) on an Obligation, then the","permissionless \"fill\" operations may borrow liquidity to the owner according to this","specification."],"type":{"kind":"struct","fields":[{"name":"debtLiquidityMint","docs":["The asset to be borrowed.","The reserves used for [Obligation::borrows] *must* all provide exactly this asset."],"type":"publicKey"},{"name":"remainingDebtAmount","docs":["The amount of debt that still needs to be filled, in lamports."],"type":"u64"},{"name":"filledDebtDestination","docs":["The token account owned by the [Obligation::owner] and holding [Self::debt_liquidity_mint],","where the filled funds should be transferred to."],"type":"publicKey"},{"name":"minDebtTermSeconds","docs":["The minimum allowed debt term that the obligation owner agrees to.","The reserves used to fill this order *cannot* define their debt term *lower* than this.","","If zeroed, then only open-term reserves may be used."],"type":"u64"},{"name":"fillableUntilTimestamp","docs":["The time until which the borrow order can still be filled."],"type":"u64"},{"name":"placedAtTimestamp","docs":["The time at which this order was placed.","Currently, this is only a piece of metadata."],"type":"u64"},{"name":"lastUpdatedAtTimestamp","docs":["The time at which this order was most-recently updated (including: created).","Currently, this is only a piece of metadata."],"type":"u64"},{"name":"requestedDebtAmount","docs":["The amount of debt that was originally requested when this order was most-recently updated.","In other words: this field holds a value of [Self::remaining_debt_amount] captured at","[Self::last_updated_at_timestamp].","Currently, this is only a piece of metadata."],"type":"u64"},{"name":"maxBorrowRateBps","docs":["The maximum borrow rate that the obligation owner agrees to.","The reserves used for [Obligation::borrows] *cannot* define their maximum borrow rate","*higher* than this."],"type":"u32"},{"name":"active","docs":["Whether the [Self::remaining_debt_amount] is non-zero.","","This field is *not* used by smart contract logic (which prefers to treat the above","[Self::remaining_debt_amount]-based definition as the single source of truth). However, it","is useful for off-chain bots (order-searchers) to efficiently list (i.e. `memcmp` filter)","just the obligations that have active borrow orders."],"type":"u8"},{"name":"enableAutoRolloverOnFilledBorrows","docs":["When `1`, all [Obligation::borrows] that get filled by this order will have their","[FixedTermBorrowRolloverConfig::auto_rollover_enabled] flag set.","","Additionally, their rollover customizations:","- will exactly match this order's constraints regarding [Self::min_debt_term_seconds] and","[Self::max_borrow_rate_bps];","- will use the [FixedTermBorrowRolloverConfig::open_term_allowed] fallback.","","See [BorrowOrder::get_rollover_config_for_filled_borrow()].","","Clarification note: when `0`, this setting has no effect on any borrow (i.e. if an existing","borrow was independently marked for auto-rollover, it will *not* be unmarked when filled by","this order).","","Feature flag note: when [LendingMarket::obligation_borrow_rollover_configuration_enabled] is","disabled, this setting has no effect on any borrow (i.e. the fill will be successful, but","the borrow will not be marked for auto-rollover."],"type":"u8"},{"name":"padding1","docs":["Alignment padding."],"type":{"array":["u8",2]}},{"name":"endPadding","docs":["End padding."],"type":{"array":["u64",5]}}]}},{"name":"FixedTermBorrowRolloverConfig","docs":["Settings driving the auto-rollover (or migration) of an [ObligationLiquidity]'s borrow.","","This covers three flavors:","- *fixed-to-fixed*: a fixed-term borrow rolling into another fixed-term reserve,","- *fixed-to-open*: a fixed-term borrow rolling into an open-term reserve,","- *open-to-fixed*: an open-term borrow migrating into a fixed-term reserve.","","By its nature (not a special case), the zeroed struct means \"no auto-rollover/migration\"."],"type":{"kind":"struct","fields":[{"name":"autoRolloverEnabled","docs":["Whether this *fixed-term* borrow can be permissionlessly prolonged. The funds used to roll","over can come:","- either from a *fixed-term* reserve (same or a different one):","- This can only happen within [LendingMarket::fixed_term_rollover_window_duration_seconds].","- The target reserve must meet all the criteria defined in this config (see","[Self::max_borrow_rate_bps] and [Self::min_debt_term_seconds]).","- Note: not possible when [Self::min_debt_term_seconds] is `0` (open-term only).","- or from an *open-term* reserve:","- This can only happen within [LendingMarket::open_term_rollover_window_duration_seconds].","- The user must explicitly set [Self::open_term_allowed] here.","","This setting is not effective when the borrow is currently using an *open-term* reserve."],"type":"u8"},{"name":"openTermAllowed","docs":["When `1`, then [Self::auto_rollover_enabled] is allowed to roll this borrow over into any","open-term reserve.","","Please note that if such rollover actually happens, then [Self::max_borrow_rate_bps]","condition does not apply - technically, it could be evaluated, but open-term reserves","typically use float-rate (utilization-driven borrow rate curve) which has very high maximum","(when at 100% utilization) that would not meet any practical criteria here."],"type":"u8"},{"name":"migrationToFixedEnabled","docs":["Whether this *open-term* borrow can be permissionlessly migrated into a fixed-term reserve:","- This can happen at any moment (as soon as liquidity becomes available).","- The target fixed-term reserve must meet all the criteria defined in this config (see","[Self::max_borrow_rate_bps] and [Self::min_debt_term_seconds]).","","This setting is not effective when the borrow is currently using a *fixed-term* reserve.","","Cannot be enabled when [Self::min_debt_term_seconds] is `0` (open-term only), because","migrating into a fixed-term reserve contradicts the open-term-only intent."],"type":"u8"},{"name":"alignmentPadding","docs":["Internal alignment padding (free to reuse)."],"type":{"array":["u8",1]}},{"name":"maxBorrowRateBps","docs":["A maximum allowed borrow rate of a reserve that can be used for a rollover/migration.","","Note: this must be set (i.e. non-zero) when enabling any rollover/migration flavor, but is","of course not effective when rollover/migration is not enabled."],"type":"u32"},{"name":"minDebtTermSeconds","docs":["A minimum debt term (in seconds) of a fixed-term reserve that can be used for a","rollover/migration.","","When `0`, the owner only accepts open-term reserves as rollover targets — i.e. rolling over","(or migrating) into a fixed-term reserve is not allowed. This is consistent with the","semantics of [BorrowOrder::min_debt_term_seconds].","","This means that `0` is incompatible with [Self::migration_to_fixed_enabled] (which requires","a fixed-term target) — this combination is rejected at configuration time."],"type":"u64"}]}},{"name":"InitObligationArgs","type":{"kind":"struct","fields":[{"name":"tag","type":"u8"},{"name":"id","type":"u8"}]}},{"name":"ObligationCollateral","docs":["Obligation collateral state"],"type":{"kind":"struct","fields":[{"name":"depositReserve","docs":["Reserve collateral is deposited to"],"type":"publicKey"},{"name":"depositedAmount","docs":["Amount of collateral deposited"],"type":"u64"},{"name":"marketValueSf","docs":["Collateral market value in quote currency (scaled fraction)"],"type":"u128"},{"name":"borrowedAmountAgainstThisCollateralInElevationGroup","docs":["Debt amount (lamport) taken against this collateral.","(only meaningful if this obligation is part of an elevation group, otherwise 0)","This is only indicative of the debt computed on the last refresh obligation.","If the obligation have multiple collateral this value is the same for all of them."],"type":"u64"},{"name":"padding","type":{"array":["u64",9]}}]}},{"name":"ObligationLiquidity","docs":["Obligation liquidity state"],"type":{"kind":"struct","fields":[{"name":"borrowReserve","docs":["Reserve liquidity is borrowed from"],"type":"publicKey"},{"name":"cumulativeBorrowRateBsf","docs":["Borrow rate used for calculating interest (big scaled fraction)"],"type":{"defined":"BigFractionBytes"}},{"name":"lastBorrowedAtTimestamp","docs":["The timestamp at which this debt was taken.","","Conceptually, every borrow can be interpreted as \"closing the previous loan and starting a","new one\" (which would make a plain ` borrowed_at ` an even better name). But in terms of","implementation, this fields records when the *last* borrow operation from this reserve","happened (i.e. adding debt of the same reserve *does* move this timestamp).","","Note: this field is *not* only metadata: it is used in the logic, e.g. for enforcing the","fixed-term borrows (i.e. those induced by [ReserveConfig::debt_term_seconds])."],"type":"u64"},{"name":"borrowedAmountSf","docs":["Amount of liquidity borrowed plus interest (scaled fraction)"],"type":"u128"},{"name":"marketValueSf","docs":["Liquidity market value in quote currency (scaled fraction)"],"type":"u128"},{"name":"borrowFactorAdjustedMarketValueSf","docs":["Risk adjusted liquidity market value in quote currency - DEBUG ONLY - use market_value instead"],"type":"u128"},{"name":"borrowedAmountOutsideElevationGroups","docs":["Amount of liquidity borrowed outside of an elevation group"],"type":"u64"},{"name":"fixedTermBorrowRolloverConfig","docs":["The user's auto-rollover/migration opt-ins. Some settings are effective only for fixed-term","borrows, while others only for open-term borrows - see individual field docs."],"type":{"defined":"FixedTermBorrowRolloverConfig"}},{"name":"borrowedAmountAtExpiration","docs":["An amount of liquidity that was borrowed when this fixed-term borrow expired (i.e. zeroed if","this borrow is not fixed-term, or if it did not yet expire).","","Needed to honor the [LendingMarket::term_based_full_liquidation_duration_secs].","","This value is captured by [Self::capture_borrowed_amount_at_expiration] during obligation's","refresh - please see the method's docs for gotchas.","","Note on precision: we use a `u64` field, since the remaining space within this struct is","rather scarce, and we do not need sub-lamport precision for the liquidation throttling rate."],"type":"u64"},{"name":"padding2","type":{"array":["u64",4]}}]}},{"name":"ObligationOrder","docs":["A single obligation order.","See [Obligation::obligation_orders]."],"type":{"kind":"struct","fields":[{"name":"conditionThresholdSf","docs":["A threshold value used by the condition (scaled [Fraction]).","The exact meaning depends on the specific [Self::condition_type].","","Examples:","- when `condition_type == 2 (UserLtvBelow)`:","then a value of `0.455` here means that the order is active only when the obligation's","user LTV is less than `0.455` (i.e. < 45.5%).","- when `condition_type == 3 (DebtCollPriceRatioAbove)`:","assuming the obligation uses BTC collateral for SOL debt, then a value of `491.3` here","means that the order is active only when the BTC-SOL price is greater than `491.3` (i.e.","> 491.3 SOL per BTC)."],"type":"u128"},{"name":"opportunityParameterSf","docs":["A configuration parameter used by the opportunity (scaled [Fraction]).","The exact meaning depends on the specific [Self::opportunity_type].","","Examples:","- when `opportunity_type == 0 (DeleverageSingleDebtAmount)`:","Assuming the obligation uses BTC collateral for SOL debt, then a value of `1_234_000_000`","here means that a liquidator may repay up to 1234000000 lamports (i.e. 1.234 SOL) on this","obligation.","Note: the special value of [Fraction::MAX] is *not* allowed in this case.","- when `opportunity_type == 1 (DeleverageAllDebtAmount)`:","The only allowed value in this case is [Fraction::MAX] (to emphasize that *all* debt","should be repaid)."],"type":"u128"},{"name":"minExecutionBonusBps","docs":["A *minimum* additional fraction of collateral transferred to the liquidator, in bps.","","The minimum bonus is applied exactly when the [Self::condition_threshold_sf] is met, and","grows linearly towards the [Self::max_execution_bonus_bps].","","Example: a value of `50` here means 50bps == 0.5% bonus for an \"LTV > 65%\" order, when","executed precisely at the moment LTV exceeds 65%."],"type":"u16"},{"name":"maxExecutionBonusBps","docs":["A *maximum* additional fraction of collateral transferred to the liquidator, in bps.","","The maximum bonus is applied at the relevant \"extreme\" state of the obligation, i.e.:","- for a stop-loss condition, it is a point at which the obligation becomes liquidatable;","- for a take-profit condition, it is a point at which obligation has 0% LTV.","","In non-extreme states, the actual bonus value is interpolated linearly, starting from","[Self::min_execution_bonus_bps] (at the point specified by the order's condition).","","Example: a value of `300` here means 300bps == 3.0% bonus for a \"debt/coll price > 140\"","order, when executed at a higher price = 200, at which the obligation's LTV happens to","be equal to its liquidation LTV."],"type":"u16"},{"name":"conditionType","docs":["Serialized [ConditionType].","The entire order is void when this is zeroed (i.e. representing [ConditionType::Never]).","","Example: a value of `2` here denotes `UserLtvBelow` condition type. Of course, to","interpret this condition, we also need to take the [Self::condition_threshold_sf] into","account."],"type":"u8"},{"name":"opportunityType","docs":["Serialized [OpportunityType].","","Example: a value of `0` here denotes `DeleverageSingleDebtAmount` opportunity. Of course, to","interpret this opportunity, we also need to take the [Self::opportunity_parameter_sf] into","account."],"type":"u8"},{"name":"padding1","docs":["Alignment padding.","The fields above take up 2+2+1+1 bytes = 48 bits, which means we need 80 bits = 10 bytes to","align with `u128`s."],"type":{"array":["u8",10]}},{"name":"padding2","docs":["End padding.","The total size of a single instance is 8*u128 = 128 bytes."],"type":{"array":["u128",5]}}]}},{"name":"UpdateObligationConfigMode","docs":["A discriminator of a user-configurable piece of [Obligation].","","Implementation note: due to TS-side codegen quirks (and a \"convention\" currently seen e.g.","within reserve and market update operations), this is not a true Rust enum. The new value of","a config item is provided in a separate handler argument (borsh-serialized), and its expected","type is defined by each discriminator here. Additionally, each update mode acts on a specific","[ObligationConfigUpdateSubject] (e.g. the auto-rollover of fixed-term borrows is configured on","a per-borrow basis), which is also specified by separate handler arguments."],"type":{"kind":"enum","variants":[{"name":"FixedTermRolloverEnabled"},{"name":"FixedTermRolloverMaxBorrowRateBps"},{"name":"FixedTermRolloverMinDebtTermSeconds"},{"name":"FixedTermRolloverOpenTermAllowed"},{"name":"MigrationToFixedEnabled"}]}},{"name":"BigFractionBytes","type":{"kind":"struct","fields":[{"name":"value","type":{"array":["u64",4]}},{"name":"padding","type":{"array":["u64",2]}}]}},{"name":"FeeCalculation","docs":["Calculate fees exlusive or inclusive of an amount"],"type":{"kind":"enum","variants":[{"name":"Exclusive"},{"name":"Inclusive"}]}},{"name":"ReserveCollateral","docs":["Reserve collateral"],"type":{"kind":"struct","fields":[{"name":"mintPubkey","docs":["Reserve collateral mint address"],"type":"publicKey"},{"name":"mintTotalSupply","docs":["Reserve collateral mint supply, used for exchange rate"],"type":"u64"},{"name":"supplyVault","docs":["Reserve collateral supply address"],"type":"publicKey"},{"name":"padding1","type":{"array":["u128",32]}},{"name":"padding2","type":{"array":["u128",32]}}]}},{"name":"ReserveConfig","docs":["Reserve configuration values"],"type":{"kind":"struct","fields":[{"name":"status","docs":["Status of the reserve Active/Obsolete/Hidden"],"type":"u8"},{"name":"paddingDeprecatedAssetTier","docs":["Asset tier -> 0 - regular (collateral & debt), 1 - isolated collateral, 2 - isolated debt"],"type":"u8"},{"name":"hostFixedInterestRateBps","docs":["Flat rate that goes to the host"],"type":"u16"},{"name":"minDeleveragingBonusBps","docs":["Starting bonus for deleveraging-related liquidations, in bps."],"type":"u16"},{"name":"blockCtokenUsage","docs":["Boolean flag to block minting/redeeming of ctokens","Blocks usage of ctokens (minting or withdrawing from obligation)","Effectively blocks deposit_reserve_liquidity and withdraw_obligation_collateral"],"type":"u8"},{"name":"earlyRepayRemainingInterestPct","docs":["The percentage of remaining interest over the debt term that is charged as early repay penalty.","Only meaningful when `debt_term_seconds > 0`."],"type":"u8"},{"name":"emergencyMode","docs":["Whether the reserve is in emergency mode.","Blocks most user operations involving this reserve, similar to [LendingMarket::emergency_mode]","but scoped to a single reserve. Also cascades to obligations using this reserve as","collateral or debt, blocking borrows and withdrawals on other reserves but still","allowing repays and deposits."],"type":"u8"},{"name":"reserved1","docs":["Past reserved space - feel free to reuse."],"type":{"array":["u8",4]}},{"name":"protocolOrderExecutionFeePct","docs":["Cut of the order execution bonus that the protocol receives, as a percentage"],"type":"u8"},{"name":"protocolTakeRatePct","docs":["Protocol take rate is the amount borrowed interest protocol receives, as a percentage"],"type":"u8"},{"name":"protocolLiquidationFeePct","docs":["Cut of the liquidation bonus that the protocol receives, as a percentage"],"type":"u8"},{"name":"loanToValuePct","docs":["Target ratio of the value of borrows to deposits, as a percentage","0 if use as collateral is disabled"],"type":"u8"},{"name":"liquidationThresholdPct","docs":["Loan to value ratio at which an obligation can be liquidated, as percentage"],"type":"u8"},{"name":"minLiquidationBonusBps","docs":["Minimum bonus a liquidator receives when repaying part of an unhealthy obligation, as bps"],"type":"u16"},{"name":"maxLiquidationBonusBps","docs":["Maximum bonus a liquidator receives when repaying part of an unhealthy obligation, as bps"],"type":"u16"},{"name":"badDebtLiquidationBonusBps","docs":["Bad debt liquidation bonus for an undercollateralized obligation, as bps"],"type":"u16"},{"name":"deleveragingMarginCallPeriodSecs","docs":["Time in seconds that must pass before redemptions are enabled after the deposit limit is","crossed.","Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case."],"type":"u64"},{"name":"deleveragingThresholdDecreaseBpsPerDay","docs":["The rate at which the deleveraging threshold decreases, in bps per day.","Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case."],"type":"u64"},{"name":"fees","docs":["Program owner fees assessed, separate from gains due to interest accrual"],"type":{"defined":"ReserveFees"}},{"name":"borrowRateCurve","docs":["Borrow rate curve based on utilization"],"type":{"defined":"BorrowRateCurve"}},{"name":"borrowFactorPct","docs":["Borrow factor in percentage - used for risk adjustment"],"type":"u64"},{"name":"depositLimit","docs":["Maximum deposit limit of liquidity in native units, u64::MAX for inf"],"type":"u64"},{"name":"borrowLimit","docs":["Maximum amount borrowed, u64::MAX for inf, 0 to disable borrows (protected deposits)"],"type":"u64"},{"name":"tokenInfo","docs":["Token id from TokenInfos struct"],"type":{"defined":"TokenInfo"}},{"name":"depositWithdrawalCap","docs":["Deposit withdrawal caps - deposit & redeem"],"type":{"defined":"WithdrawalCaps"}},{"name":"debtWithdrawalCap","docs":["Debt withdrawal caps - borrow & repay"],"type":{"defined":"WithdrawalCaps"}},{"name":"elevationGroups","type":{"array":["u8",20]}},{"name":"disableUsageAsCollOutsideEmode","type":"u8"},{"name":"utilizationLimitBlockBorrowingAbovePct","docs":["Utilization (in percentage) above which borrowing is blocked. 0 to disable."],"type":"u8"},{"name":"autodeleverageEnabled","docs":["Whether this reserve should be subject to auto-deleveraging after deposit or borrow limit is","crossed.","Besides this flag, the lending market's flag also needs to be enabled (logical `AND`).","**NOTE:** the manual \"target LTV\" deleveraging is NOT affected by this flag."],"type":"u8"},{"name":"proposerAuthorityLocked","docs":["Boolean flag indicating whether the reserve is locked for the proposer authority.","","Once the proposer have finished preparing the reserve, it must be locked to prevent","further changes to the reserve configuration allowing review and voting on the proposal","without alteration during the voting period."],"type":"u8"},{"name":"borrowLimitOutsideElevationGroup","docs":["Maximum amount liquidity of this reserve borrowed outside all elevation groups","- u64::MAX for inf","- 0 to disable borrows outside elevation groups"],"type":"u64"},{"name":"borrowLimitAgainstThisCollateralInElevationGroup","docs":["Defines the maximum amount (in lamports of elevation group debt asset)","that can be borrowed when this reserve is used as collateral.","- u64::MAX for inf","- 0 to disable borrows in this elevation group (expected value for the debt asset)"],"type":{"array":["u64",32]}},{"name":"deleveragingBonusIncreaseBpsPerDay","docs":["The rate at which the deleveraging-related liquidation bonus increases, in bps per day.","Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case."],"type":"u64"},{"name":"debtMaturityTimestamp","docs":["The timestamp at which all [Obligation::borrows] using this reserve become liquidatable","(on the same terms as reserve-wide deleveraging).","Inactive when zeroed (i.e. debt never matures).","","Note: this feature is independent of [Self::debt_term_seconds] - the liquidation mechanism","is based directly on the timestamp defined here, on Reserve's level."],"type":"u64"},{"name":"debtTermSeconds","docs":["The duration after which any debt coming from this Reserve must be repaid.","Inactive when zeroed (i.e. funds can be borrowed indefinitely).","","Note: this feature is independent of [Self::debt_maturity_timestamp] - the liquidation","mechanism is based on the [ObligationLiquidity::last_borrowed_at_timestamp]."],"type":"u64"},{"name":"rewardsAmountPerSlot","docs":["Rewards distributed per slot to depositors. Drained from","[ReserveLiquidity::rewards_amount_available] into","[ReserveLiquidity::total_available_amount] at each refresh, capped by the","market-level [LendingMarket::reserve_rewards_max_apr_bps]. `0` disables.","","**Note:** because rewards inflate `total_available_amount`, a non-zero RPS on a","reserve with [Self::autodeleverage_enabled] and a finite [Self::deposit_limit]","will eventually cross the cap and arm the autodeleverage countdown. Size","`deposit_limit` and RPS together."],"type":"u64"},{"name":"permissionedOps","docs":["Bitmask of [PermissionedOp]s gated by the parent market's `permissioning_authority`","when this reserve is the operation's target. `0` = no operation is restricted at the","reserve level. Use [Reserve::get_permissioned_ops] for a typed view."],"type":"u64"}]}},{"name":"ReserveFarmKind","type":{"kind":"enum","variants":[{"name":"Collateral"},{"name":"Debt"}]}},{"name":"ReserveFees","docs":["Additional fee information on a reserve","","These exist separately from interest accrual fees, and are specifically for the program owner","and referral fee. The fees are paid out as a percentage of liquidity token amounts during","repayments and liquidations."],"type":{"kind":"struct","fields":[{"name":"originationFeeSf","docs":["Fee assessed on `BorrowObligationLiquidity`, as scaled fraction (60 bits fractional part)","Must be between `0` and `2^60`, such that `2^60 = 1`. A few examples for","clarity:","1% = (1 << 60) / 100 = 11529215046068470","0.01% (1 basis point) = 115292150460685","0.00001% (Aave origination fee) = 115292150461"],"type":"u64"},{"name":"flashLoanFeeSf","docs":["Fee for flash loan, expressed as scaled fraction.","0.3% (Aave flash loan fee) = 0.003 * 2^60 = 3458764513820541"],"type":"u64"},{"name":"padding","docs":["Used for allignment"],"type":{"array":["u8",8]}}]}},{"name":"ReserveLiquidity","docs":["Reserve liquidity"],"type":{"kind":"struct","fields":[{"name":"mintPubkey","docs":["Reserve liquidity mint address"],"type":"publicKey"},{"name":"supplyVault","docs":["Reserve liquidity supply address"],"type":"publicKey"},{"name":"feeVault","docs":["Reserve liquidity fee collection address"],"type":"publicKey"},{"name":"totalAvailableAmount","docs":["Total reserve liquidity available.","","Note: not all of this liquidity can be freely used for any purpose. Production code should","use the specialized getters - see e.g. [Reserve::total_available_liquidity_amount()],","[Reserve::freely_available_liquidity_amount()]."],"type":"u64"},{"name":"borrowedAmountSf","docs":["Reserve liquidity borrowed (scaled fraction)"],"type":"u128"},{"name":"marketPriceSf","docs":["Reserve liquidity market price in quote currency (scaled fraction)"],"type":"u128"},{"name":"marketPriceLastUpdatedTs","docs":["Unix timestamp of the market price (from the oracle)"],"type":"u64"},{"name":"mintDecimals","docs":["Reserve liquidity mint decimals"],"type":"u64"},{"name":"depositLimitCrossedTimestamp","docs":["Timestamp when the last refresh reserve detected that the liquidity amount is above the deposit cap. When this threshold is crossed, then redemptions (auto-deleverage) are enabled.","If the threshold is not crossed, then the timestamp is set to 0"],"type":"u64"},{"name":"borrowLimitCrossedTimestamp","docs":["Timestamp when the last refresh reserve detected that the borrowed amount is above the borrow cap. When this threshold is crossed, then redemptions (auto-deleverage) are enabled.","If the threshold is not crossed, then the timestamp is set to 0"],"type":"u64"},{"name":"cumulativeBorrowRateBsf","docs":["Reserve liquidity cumulative borrow rate (scaled fraction)"],"type":{"defined":"BigFractionBytes"}},{"name":"accumulatedProtocolFeesSf","docs":["Reserve cumulative protocol fees (scaled fraction)"],"type":"u128"},{"name":"accumulatedReferrerFeesSf","docs":["Reserve cumulative referrer fees (scaled fraction)"],"type":"u128"},{"name":"pendingReferrerFeesSf","docs":["Reserve pending referrer fees, to be claimed in refresh_obligation by referrer or protocol (scaled fraction)"],"type":"u128"},{"name":"absoluteReferralRateSf","docs":["Reserve referrer fee absolute rate calculated at each refresh_reserve operation (scaled fraction)"],"type":"u128"},{"name":"tokenProgram","docs":["Token program of the liquidity mint"],"type":"publicKey"},{"name":"rewardsAmountAvailable","docs":["Reserve rewards budget remaining for distribution.","","Tokens are deposited via `topup_reserve_rewards` and increase this counter (without","touching [Self::total_available_amount]). On every `refresh_reserve`, up to","`rewards_amount_per_slot * slots_elapsed` tokens are moved from this counter into","[Self::total_available_amount], inflating the cToken exchange rate, capped by the","market-level `reserve_rewards_max_apr_bps` cap."],"type":"u64"},{"name":"padding2","type":{"array":["u64",50]}},{"name":"padding3","type":{"array":["u128",32]}}]}},{"name":"ReserveStatus","type":{"kind":"enum","variants":[{"name":"Active"},{"name":"Obsolete"},{"name":"Hidden"}]}},{"name":"WithdrawQueue","docs":["A tracker of ticket-based withdrawals."],"type":{"kind":"struct","fields":[{"name":"queuedCollateralAmount","docs":["The part of [ReserveLiquidity::total_available_amount] locked for ticketed withdrawals."],"type":"u64"},{"name":"nextIssuedTicketSequenceNumber","docs":["The sequence number of the next ticket to be issued when enqueueing to withdraw.","Note: it is also a number of tickets issued so far."],"type":"u64"},{"name":"nextWithdrawableTicketSequenceNumber","docs":["The sequence number of the next ticket to be used for actually transferring the withdrawn","liquidity (assuming it is available in the reserve).","Note: it is also a number of fully-consumed tickets so far."],"type":"u64"}]}},{"name":"WithdrawalCaps","docs":["Reserve Withdrawal Caps State"],"type":{"kind":"struct","fields":[{"name":"configCapacity","type":"i64"},{"name":"currentTotal","type":"i64"},{"name":"lastIntervalStartTimestamp","type":"u64"},{"name":"configIntervalLengthSeconds","type":"u64"}]}},{"name":"PriceHeuristic","type":{"kind":"struct","fields":[{"name":"lower","docs":["Lower value of acceptable price"],"type":"u64"},{"name":"upper","docs":["Upper value of acceptable price"],"type":"u64"},{"name":"exp","docs":["Number of decimals of the previously defined values"],"type":"u64"}]}},{"name":"PythConfiguration","type":{"kind":"struct","fields":[{"name":"price","docs":["Pubkey of the base price feed (disabled if `null` or `default`)"],"type":"publicKey"}]}},{"name":"ScopeConfiguration","type":{"kind":"struct","fields":[{"name":"priceFeed","docs":["Pubkey of the scope price feed (disabled if `null` or `default`)"],"type":"publicKey"},{"name":"priceChain","docs":["This is the scope_id price chain that results in a price for the token"],"type":{"array":["u16",4]}},{"name":"twapChain","docs":["This is the scope_id price chain for the twap"],"type":{"array":["u16",4]}}]}},{"name":"SwitchboardConfiguration","type":{"kind":"struct","fields":[{"name":"priceAggregator","docs":["Pubkey of the base price feed (disabled if `null` or `default`)"],"type":"publicKey"},{"name":"twapAggregator","type":"publicKey"}]}},{"name":"TokenInfo","type":{"kind":"struct","fields":[{"name":"name","docs":["UTF-8 encoded name of the token (null-terminated)"],"type":{"array":["u8",32]}},{"name":"heuristic","docs":["Heuristics limits of acceptable price"],"type":{"defined":"PriceHeuristic"}},{"name":"maxTwapDivergenceBps","docs":["Max divergence between twap and price in bps"],"type":"u64"},{"name":"maxAgePriceSeconds","type":"u64"},{"name":"maxAgeTwapSeconds","type":"u64"},{"name":"scopeConfiguration","docs":["Scope price configuration"],"type":{"defined":"ScopeConfiguration"}},{"name":"switchboardConfiguration","docs":["Switchboard configuration"],"type":{"defined":"SwitchboardConfiguration"}},{"name":"pythConfiguration","docs":["Pyth configuration"],"type":{"defined":"PythConfiguration"}},{"name":"blockPriceUsage","type":"u8"},{"name":"reserved","type":{"array":["u8",7]}},{"name":"padding","type":{"array":["u64",19]}}]}},{"name":"ProgressCallbackType","docs":["A callback to be notified when the ticket is being processed.","","## Why an enum?","","Only reliable programs may be used for callbacks (since any error or panic returned from a CPI","aborts an entire transaction, which would stall the queue progress). Hence, we need a whitelist,","and the simplest initial implementation is a hardcoded enum. If we want to be able to add new","whitelist items without SC updates, we can implement such support using a special enum value","(e.g. `SPECIFIED_BY_PDA = 255`)."],"type":{"kind":"enum","variants":[{"name":"None"},{"name":"KlendQueueAccountingHandlerOnKvault"}]}},{"name":"BorrowRateCurve","type":{"kind":"struct","fields":[{"name":"points","type":{"array":[{"defined":"CurvePoint"},11]}}]}},{"name":"CurvePoint","type":{"kind":"struct","fields":[{"name":"utilizationRateBps","type":"u32"},{"name":"borrowRateBps","type":"u32"}]}}],"events":[{"name":"BorrowOrderCancelEvent","fields":[{"name":"before","type":{"defined":"BorrowOrder"},"index":false}]},{"name":"BorrowOrderFullFillEvent","fields":[{"name":"before","type":{"defined":"BorrowOrder"},"index":false}]},{"name":"BorrowOrderPartialFillEvent","fields":[{"name":"before","type":{"defined":"BorrowOrder"},"index":false},{"name":"after","type":{"defined":"BorrowOrder"},"index":false}]},{"name":"BorrowOrderPlaceEvent","fields":[{"name":"after","type":{"defined":"BorrowOrder"},"index":false}]},{"name":"BorrowOrderUpdateEvent","fields":[{"name":"before","type":{"defined":"BorrowOrder"},"index":false},{"name":"after","type":{"defined":"BorrowOrder"},"index":false}]}],"errors":[{"code":6000,"name":"InvalidMarketAuthority","msg":"Market authority is invalid"},{"code":6001,"name":"InvalidMarketOwner","msg":"Market owner is invalid"},{"code":6002,"name":"InvalidAccountOwner","msg":"Input account owner is not the program address"},{"code":6003,"name":"InvalidAmount","msg":"Input amount is invalid"},{"code":6004,"name":"InvalidConfig","msg":"Input config value is invalid"},{"code":6005,"name":"InvalidSigner","msg":"Signer is not allowed to perform this action"},{"code":6006,"name":"InvalidAccountInput","msg":"Invalid account input"},{"code":6007,"name":"MathOverflow","msg":"Math operation overflow"},{"code":6008,"name":"InsufficientLiquidity","msg":"Insufficient liquidity available"},{"code":6009,"name":"ReserveStale","msg":"Reserve state needs to be refreshed"},{"code":6010,"name":"WithdrawTooSmall","msg":"Withdraw amount too small"},{"code":6011,"name":"WithdrawTooLarge","msg":"Withdraw amount too large"},{"code":6012,"name":"BorrowTooSmall","msg":"Borrow amount too small to receive liquidity after fees"},{"code":6013,"name":"BorrowTooLarge","msg":"Borrow amount too large for deposited collateral"},{"code":6014,"name":"RepayTooSmall","msg":"Repay amount too small to transfer liquidity"},{"code":6015,"name":"LiquidationTooSmall","msg":"Liquidation amount too small to receive collateral"},{"code":6016,"name":"ObligationHealthy","msg":"Cannot liquidate healthy obligations"},{"code":6017,"name":"ObligationStale","msg":"Obligation state needs to be refreshed"},{"code":6018,"name":"ObligationReserveLimit","msg":"Obligation reserve limit exceeded"},{"code":6019,"name":"InvalidObligationOwner","msg":"Obligation owner is invalid"},{"code":6020,"name":"ObligationDepositsEmpty","msg":"Obligation deposits are empty"},{"code":6021,"name":"ObligationBorrowsEmpty","msg":"Obligation borrows are empty"},{"code":6022,"name":"ObligationDepositsZero","msg":"Obligation deposits have zero value"},{"code":6023,"name":"ObligationBorrowsZero","msg":"Obligation borrows have zero value"},{"code":6024,"name":"InvalidObligationCollateral","msg":"Invalid obligation collateral"},{"code":6025,"name":"InvalidObligationLiquidity","msg":"Invalid obligation liquidity"},{"code":6026,"name":"ObligationCollateralEmpty","msg":"Obligation collateral is empty"},{"code":6027,"name":"ObligationLiquidityEmpty","msg":"Obligation liquidity is empty"},{"code":6028,"name":"NegativeInterestRate","msg":"Interest rate is negative"},{"code":6029,"name":"InvalidOracleConfig","msg":"Input oracle config is invalid"},{"code":6030,"name":"InsufficientProtocolFeesToRedeem","msg":"Insufficient protocol fees to claim or no liquidity available"},{"code":6031,"name":"FlashBorrowCpi","msg":"No cpi flash borrows allowed"},{"code":6032,"name":"NoFlashRepayFound","msg":"No corresponding repay found for flash borrow"},{"code":6033,"name":"InvalidFlashRepay","msg":"Invalid repay found"},{"code":6034,"name":"FlashRepayCpi","msg":"No cpi flash repays allowed"},{"code":6035,"name":"MultipleFlashBorrows","msg":"Multiple flash borrows not allowed in the same transaction"},{"code":6036,"name":"FlashLoansDisabled","msg":"Flash loans are disabled for this reserve"},{"code":6037,"name":"SwitchboardV2Error","msg":"Switchboard error"},{"code":6038,"name":"CouldNotDeserializeScope","msg":"Cannot deserialize the scope price account"},{"code":6039,"name":"PriceTooOld","msg":"Price too old"},{"code":6040,"name":"PriceTooDivergentFromTwap","msg":"Price too divergent from twap"},{"code":6041,"name":"InvalidTwapPrice","msg":"Invalid twap price"},{"code":6042,"name":"GlobalEmergencyMode","msg":"Emergency mode is enabled"},{"code":6043,"name":"InvalidFlag","msg":"Invalid lending market config"},{"code":6044,"name":"PriceNotValid","msg":"Price is not valid"},{"code":6045,"name":"PriceIsBiggerThanHeuristic","msg":"Price is bigger than allowed by heuristic"},{"code":6046,"name":"PriceIsLowerThanHeuristic","msg":"Price lower than allowed by heuristic"},{"code":6047,"name":"PriceIsZero","msg":"Price is zero"},{"code":6048,"name":"PriceConfidenceTooWide","msg":"Price confidence too wide"},{"code":6049,"name":"IntegerOverflow","msg":"Conversion between integers failed"},{"code":6050,"name":"NoFarmForReserve","msg":"This reserve does not have a farm"},{"code":6051,"name":"IncorrectInstructionInPosition","msg":"Wrong instruction at expected position"},{"code":6052,"name":"NoPriceFound","msg":"No price found"},{"code":6053,"name":"InvalidTwapConfig","msg":"Invalid Twap configuration: Twap is enabled but one of the enabled price doesn't have a twap"},{"code":6054,"name":"InvalidPythPriceAccount","msg":"Pyth price account does not match configuration"},{"code":6055,"name":"InvalidSwitchboardAccount","msg":"Switchboard account(s) do not match configuration"},{"code":6056,"name":"InvalidScopePriceAccount","msg":"Scope price account does not match configuration"},{"code":6057,"name":"ObligationCollateralLtvZero","msg":"The obligation has one collateral with an LTV set to 0. Withdraw it before withdrawing other collaterals"},{"code":6058,"name":"InvalidObligationSeedsValue","msg":"Seeds must be default pubkeys for tag 0, and mint addresses for tag 1 or 2"},{"code":6059,"name":"DeprecatedInvalidObligationId","msg":"[DEPRECATED] Obligation id must be 0"},{"code":6060,"name":"InvalidBorrowRateCurvePoint","msg":"Invalid borrow rate curve point"},{"code":6061,"name":"InvalidUtilizationRate","msg":"Invalid utilization rate"},{"code":6062,"name":"CannotSocializeObligationWithCollateral","msg":"Obligation hasn't been fully liquidated and debt cannot be socialized."},{"code":6063,"name":"ObligationEmpty","msg":"Obligation has no borrows or deposits."},{"code":6064,"name":"WithdrawalCapReached","msg":"Withdrawal cap is reached"},{"code":6065,"name":"LastTimestampGreaterThanCurrent","msg":"The last interval start timestamp is greater than the current timestamp"},{"code":6066,"name":"LiquidationRewardTooSmall","msg":"The reward amount is less than the minimum acceptable received liquidity"},{"code":6067,"name":"IsolatedAssetTierViolation","msg":"Isolated Asset Tier Violation"},{"code":6068,"name":"InconsistentElevationGroup","msg":"The obligation's elevation group and the reserve's are not the same"},{"code":6069,"name":"InvalidElevationGroup","msg":"The elevation group chosen for the reserve does not exist in the lending market"},{"code":6070,"name":"InvalidElevationGroupConfig","msg":"The elevation group updated has wrong parameters set"},{"code":6071,"name":"UnhealthyElevationGroupLtv","msg":"The current obligation must have most or all its debt repaid before changing the elevation group"},{"code":6072,"name":"ElevationGroupNewLoansDisabled","msg":"Elevation group does not accept any new loans or any new borrows/withdrawals"},{"code":6073,"name":"ReserveDeprecated","msg":"Reserve was deprecated, no longer usable"},{"code":6074,"name":"ReferrerAccountNotInitialized","msg":"Referrer account not initialized"},{"code":6075,"name":"ReferrerAccountMintMissmatch","msg":"Referrer account mint does not match the operation reserve mint"},{"code":6076,"name":"ReferrerAccountWrongAddress","msg":"Referrer account address is not a valid program address"},{"code":6077,"name":"ReferrerAccountReferrerMissmatch","msg":"Referrer account referrer does not match the owner referrer"},{"code":6078,"name":"ReferrerAccountMissing","msg":"Referrer account missing for obligation with referrer"},{"code":6079,"name":"InsufficientReferralFeesToRedeem","msg":"Insufficient referral fees to claim or no liquidity available"},{"code":6080,"name":"CpiDisabled","msg":"CPI disabled for this instruction"},{"code":6081,"name":"ShortUrlNotAsciiAlphanumeric","msg":"Referrer short_url is not ascii alphanumeric"},{"code":6082,"name":"ReserveObsolete","msg":"Reserve is marked as obsolete"},{"code":6083,"name":"ElevationGroupAlreadyActivated","msg":"Obligation already part of the same elevation group"},{"code":6084,"name":"ObligationInObsoleteReserve","msg":"Obligation has a deposit or borrow in an obsolete reserve"},{"code":6085,"name":"ReferrerStateOwnerMismatch","msg":"Referrer state owner does not match the given signer"},{"code":6086,"name":"UserMetadataOwnerAlreadySet","msg":"User metadata owner is already set"},{"code":6087,"name":"CollateralNonLiquidatable","msg":"This collateral cannot be liquidated (LTV set to 0)"},{"code":6088,"name":"BorrowingDisabled","msg":"Borrowing is disabled"},{"code":6089,"name":"BorrowLimitExceeded","msg":"Cannot borrow above borrow limit"},{"code":6090,"name":"DepositLimitExceeded","msg":"Cannot deposit above deposit limit"},{"code":6091,"name":"BorrowingDisabledOutsideElevationGroup","msg":"Reserve does not accept any new borrows outside elevation group"},{"code":6092,"name":"NetValueRemainingTooSmall","msg":"Net value remaining too small"},{"code":6093,"name":"WorseLtvBlocked","msg":"Cannot get the obligation in a worse position"},{"code":6094,"name":"LiabilitiesBiggerThanAssets","msg":"Cannot have more liabilities than assets in a position"},{"code":6095,"name":"ReserveTokenBalanceMismatch","msg":"Reserve state and token account cannot drift"},{"code":6096,"name":"ReserveVaultBalanceMismatch","msg":"Reserve token account has been unexpectedly modified"},{"code":6097,"name":"ReserveAccountingMismatch","msg":"Reserve internal state accounting has been unexpectedly modified"},{"code":6098,"name":"BorrowingAboveUtilizationRateDisabled","msg":"Borrowing above set utilization rate is disabled"},{"code":6099,"name":"LiquidationBorrowFactorPriority","msg":"Liquidation must prioritize the debt with the highest borrow factor"},{"code":6100,"name":"LiquidationLowestLiquidationLtvPriority","msg":"Liquidation must prioritize the collateral with the lowest liquidation LTV"},{"code":6101,"name":"ElevationGroupBorrowLimitExceeded","msg":"Elevation group borrow limit exceeded"},{"code":6102,"name":"ElevationGroupWithoutDebtReserve","msg":"The elevation group does not have a debt reserve defined"},{"code":6103,"name":"ElevationGroupMaxCollateralReserveZero","msg":"The elevation group does not allow any collateral reserves"},{"code":6104,"name":"ElevationGroupHasAnotherDebtReserve","msg":"In elevation group attempt to borrow from a reserve that is not the debt reserve"},{"code":6105,"name":"ElevationGroupDebtReserveAsCollateral","msg":"The elevation group's debt reserve cannot be used as a collateral reserve"},{"code":6106,"name":"ObligationCollateralExceedsElevationGroupLimit","msg":"Obligation have more collateral than the maximum allowed by the elevation group"},{"code":6107,"name":"ObligationElevationGroupMultipleDebtReserve","msg":"Obligation is an elevation group but have more than one debt reserve"},{"code":6108,"name":"UnsupportedTokenExtension","msg":"Mint has a token (2022) extension that is not supported"},{"code":6109,"name":"InvalidTokenAccount","msg":"Can't have an spl token mint with a t22 account"},{"code":6110,"name":"DepositDisabledOutsideElevationGroup","msg":"Can't deposit into this reserve outside elevation group"},{"code":6111,"name":"CannotCalculateReferralAmountDueToSlotsMismatch","msg":"Cannot calculate referral amount due to slots mismatch"},{"code":6112,"name":"ObligationOwnersMustMatch","msg":"Obligation owners must match"},{"code":6113,"name":"ObligationsMustMatch","msg":"Obligations must match"},{"code":6114,"name":"LendingMarketsMustMatch","msg":"Lending markets must match"},{"code":6115,"name":"ObligationCurrentlyMarkedForDeleveraging","msg":"Obligation is already marked for deleveraging"},{"code":6116,"name":"MaximumWithdrawValueZero","msg":"Maximum withdrawable value of this collateral is zero, LTV needs improved"},{"code":6117,"name":"ZeroMaxLtvAssetsInDeposits","msg":"No max LTV 0 assets allowed in deposits for repay and withdraw"},{"code":6118,"name":"LowestLtvAssetsPriority","msg":"Withdrawing must prioritize the collateral with the lowest reserve max-LTV"},{"code":6119,"name":"WorseLtvThanUnhealthyLtv","msg":"Cannot get the obligation liquidatable"},{"code":6120,"name":"FarmAccountsMissing","msg":"Farm accounts to refresh are missing"},{"code":6121,"name":"RepayTooSmallForFullLiquidation","msg":"Repay amount is too small to satisfy the mandatory full liquidation"},{"code":6122,"name":"InsufficientRepayAmount","msg":"Liquidator provided repay amount lower than required by liquidation rules"},{"code":6123,"name":"OrderIndexOutOfBounds","msg":"Obligation order of the given index cannot exist"},{"code":6124,"name":"InvalidOrderConfiguration","msg":"Given order configuration has wrong parameters"},{"code":6125,"name":"OrderConfigurationNotSupportedByObligation","msg":"Given order configuration cannot be used with the current state of the obligation"},{"code":6126,"name":"OperationNotPermittedWithCurrentObligationOrders","msg":"Single debt, single collateral obligation orders have to be cancelled before changing the deposit/borrow count"},{"code":6127,"name":"OperationNotPermittedMarketImmutable","msg":"Cannot update lending market because it is set as immutable"},{"code":6128,"name":"OrderCreationDisabled","msg":"Creation of new orders is disabled"},{"code":6129,"name":"NoUpgradeAuthority","msg":"Cannot initialize global config because there is no upgrade authority to the program"},{"code":6130,"name":"InitialAdminDepositExecuted","msg":"Initial admin deposit in reserve already executed"},{"code":6131,"name":"ReserveHasNotReceivedInitialDeposit","msg":"Reserve has not received the initial deposit, cannot update config"},{"code":6132,"name":"CTokenUsageBlocked","msg":"CToken minting/redeeming is blocked for this reserve"},{"code":6133,"name":"CannotUseSameReserve","msg":"Cannot call ix with same reserve"},{"code":6134,"name":"TransactionIncludesRestrictedPrograms","msg":"Transaction includes restricted programs"},{"code":6135,"name":"BorrowOrderDebtLiquidityMintMismatch","msg":"There is no borrow order requesting debt in the given asset"},{"code":6136,"name":"BorrowOrderMaxBorrowRateExceeded","msg":"Reserve used for fill exceeds the maximum borrow rate specified by the order"},{"code":6137,"name":"BorrowOrderMinDebtTermInsufficient","msg":"Reserve used for fill defines a debt term shorter than specified by the order"},{"code":6138,"name":"BorrowOrderFillTimeLimitExceeded","msg":"Borrow order can no longer be filled"},{"code":6139,"name":"ReserveDebtMaturityReached","msg":"Cannot borrow from a reserve that reached its debt maturity timestamp"},{"code":6140,"name":"NonUpdatableOrderConfiguration","msg":"Some piece of the order's configuration cannot be updated (the order should be cancelled and placed again)"},{"code":6141,"name":"BorrowOrderExecutionDisabled","msg":"Execution of borrow orders is disabled"},{"code":6142,"name":"DebtReachedReserveDebtTerm","msg":"Cannot increase the debt that has reached its end of term configured by the reserve"},{"code":6143,"name":"ExpectationNotMet","msg":"The on-chain state does not meet expectation specified by the caller, so the operation must be aborted (to avoid race conditions)"},{"code":6144,"name":"BorrowOrderFillValueTooSmall","msg":"Available liquidity could not satisfy the minimum required borrow order fill value"},{"code":6145,"name":"WithdrawTicketIssuanceDisabled","msg":"Issuing new withdraw tickets is disabled by the market"},{"code":6146,"name":"WithdrawTicketRedemptionDisabled","msg":"Redeeming withdraw tickets is disabled by the market"},{"code":6147,"name":"WithdrawTicketStillValid","msg":"Recovering collateral is only available after the withdraw ticket has been marked invalid"},{"code":6148,"name":"WithdrawTicketRequiresFullRedemption","msg":"The withdraw ticket's current state requires that it is fully redeemed (e.g. due to owner ATA creation), but there is not enough liquidity"},{"code":6149,"name":"UserTokenBalanceMismatch","msg":"The user's token account has changed its balance in an unexpected way"},{"code":6150,"name":"WithdrawQueuedLiquidityValueTooSmall","msg":"Available liquidity could not satisfy the minimum required ticketed withdrawal value"},{"code":6151,"name":"InvalidTokenAccountState","msg":"Token account is in a state preventing the handler's operation (e.g. frozen or delegate)"},{"code":6152,"name":"WithdrawTicketInvalid","msg":"Cannot use ticket that was already marked invalid"},{"code":6153,"name":"BorrowOrderValueTooSmall","msg":"Borrow order's value would be below the market-configured minimum"},{"code":6154,"name":"WithdrawTicketValueTooSmall","msg":"Withdraw ticket's value would be below the market-configured minimum"},{"code":6155,"name":"InvalidWithdrawTicketProgressCallbackConfig","msg":"Invalid configuration or required custom accounts for the requested withdraw ticket callback type"},{"code":6156,"name":"WithdrawTicketProgressCallbackAccountsMissing","msg":"One or more accounts required by the ticket's configured progress callback are missing"},{"code":6157,"name":"BorrowRolloverConfigurationDisabled","msg":"Configuring auto-rollover on loans is disabled by market owner"},{"code":6158,"name":"InvalidObligationConfigUpdateSubject","msg":"Invalid specification of the Obligation's part to be configured"},{"code":6159,"name":"BorrowRolloverLiquidityMintMismatch","msg":"Auto-rollover must use a target reserve of the same token"},{"code":6160,"name":"ObligationBorrowRolloverNotApplicable","msg":"The given borrow is not fixed-term and does not require rolling over"},{"code":6161,"name":"ObligationBorrowOutsideRolloverWindow","msg":"The given borrow is outside the corresponding market-configured rollover window"},{"code":6162,"name":"ObligationBorrowRolloverNotEnabledByOwner","msg":"Obligation's owner did not opt-in for auto-rollover of the given borrow"},{"code":6163,"name":"ObligationBorrowRolloverTargetReserveMismatch","msg":"Obligation's owner did not allow to roll over into terms offered by the given reserve"},{"code":6164,"name":"BorrowRolloverExecutionDisabled","msg":"Executing auto-rollover is disabled by market owner"},{"code":6165,"name":"ObligationAccountingMismatch","msg":"Obligation internal state accounting has been unexpectedly modified"},{"code":6166,"name":"PartialRolloverValueTooSmall","msg":"Partial rollover amount is below the market-configured minimum value"},{"code":6167,"name":"ObligationBorrowRolloverConfigMismatch","msg":"Pre-existing rollover configuration of the loan cannot be overwritten by the operation"},{"code":6168,"name":"ObligationBorrowRolloverMustProlongDebtTerm","msg":"Rollover into existing borrow must prolong the remaining debt term"},{"code":6169,"name":"RolloverNotSupportedInElevationGroup","msg":"Rollover is not supported for obligations in an elevation group"},{"code":6170,"name":"WithdrawTicketCancellationDisabled","msg":"Cancelling withdraw tickets is disabled by the market"},{"code":6171,"name":"WithdrawTicketFullyCancelled","msg":"Cannot use ticket that was already fully-cancelled"},{"code":6172,"name":"CloneSourceReserveDisabled","msg":"Cannot clone config from a reserve that is disabled"},{"code":6173,"name":"CloneTargetReserveAlreadyInUse","msg":"Cannot clone config into a reserve that has been in use"},{"code":6174,"name":"ClonedReserveLiquidityMintMismatch","msg":"Cannot clone config between reserves of different mints"},{"code":6175,"name":"ReserveEmergencyMode","msg":"Reserve emergency mode is enabled"},{"code":6176,"name":"ObligationOwnershipTransferInProgress","msg":"Obligation ownership transfer is in progress"},{"code":6177,"name":"ObligationOwnershipTransferNotInInitiatedState","msg":"Obligation ownership transfer is not in initiated state"},{"code":6178,"name":"ObligationPendingOwnerNotSet","msg":"Obligation pending owner not set"},{"code":6179,"name":"ObligationInvalidPendingOwner","msg":"Invalid pending owner address"},{"code":6180,"name":"ObligationOwnershipTransferNotApproved","msg":"Obligation ownership transfer not approved by admin"},{"code":6181,"name":"ObligationHasActiveBorrowOrders","msg":"Obligation has active borrow orders"},{"code":6182,"name":"OnlyComputeBudgetCompanionIxsAllowed","msg":"Only ComputeBudget instructions may accompany this instruction"},{"code":6183,"name":"MissingPermissioner","msg":"Required permissioning account is missing"},{"code":6184,"name":"ReserveRewardsDisabled","msg":"Reserve rewards are disabled on this market (reserve_rewards_max_apr_bps is 0)"}]} \ No newline at end of file diff --git a/idls/kamino_vault.kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr.json b/idls/kamino_vault.kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr.json new file mode 100644 index 0000000..231f87f --- /dev/null +++ b/idls/kamino_vault.kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"kamino_vault","instructions":[{"name":"initVault","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"baseTokenMint","isMut":false,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"adminTokenAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateReserveAllocation","accounts":[{"name":"signer","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserve","isMut":false,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"weight","type":"u64"},{"name":"cap","type":"u64"}]},{"name":"deposit","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"userTokenAta","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxAmount","type":"u64"}]},{"name":"withdraw","accounts":[{"name":"withdrawFromAvailable","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"userTokenAta","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false}]},{"name":"withdrawFromReserveAccounts","accounts":[{"name":"vaultState","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]}],"args":[{"name":"sharesAmount","type":"u64"}]},{"name":"invest","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"payerTokenAccount","isMut":true,"isSigner":false},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false,"docs":["CPI accounts"]},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[]},{"name":"updateVaultConfig","accounts":[{"name":"vaultAdminAuthority","isMut":false,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false}],"args":[{"name":"entry","type":{"defined":"VaultConfigField"}},{"name":"data","type":"bytes"}]},{"name":"withdrawPendingFees","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"tokenAta","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false,"docs":["CPI accounts"]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[]},{"name":"updateAdmin","accounts":[{"name":"pendingAdmin","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false}],"args":[]},{"name":"giveUpPendingFees","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxAmountToGiveUp","type":"u64"}]},{"name":"initializeSharesMetadata","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":false,"isSigner":false},{"name":"sharesMint","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"sharesMetadata","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"updateSharesMetadata","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"sharesMetadata","isMut":true,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"withdrawFromAvailable","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"userTokenAta","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false}],"args":[{"name":"sharesAmount","type":"u64"}]}],"accounts":[{"name":"Reserve","type":{"kind":"struct","fields":[{"name":"version","docs":["Version of the reserve"],"type":"u64"},{"name":"lastUpdate","docs":["Last slot when supply and rates updated"],"type":{"defined":"LastUpdate"}},{"name":"lendingMarket","docs":["Lending market address"],"type":"publicKey"},{"name":"farmCollateral","type":"publicKey"},{"name":"farmDebt","type":"publicKey"},{"name":"liquidity","docs":["Reserve liquidity"],"type":{"defined":"ReserveLiquidity"}},{"name":"reserveLiquidityPadding","type":{"array":["u64",150]}},{"name":"collateral","docs":["Reserve collateral"],"type":{"defined":"ReserveCollateral"}},{"name":"reserveCollateralPadding","type":{"array":["u64",150]}},{"name":"config","docs":["Reserve configuration values"],"type":{"defined":"ReserveConfig"}},{"name":"configPadding","type":{"array":["u64",116]}},{"name":"borrowedAmountOutsideElevationGroup","type":"u64"},{"name":"borrowedAmountsAgainstThisReserveInElevationGroups","docs":["Amount of token borrowed in lamport of debt asset in the given","elevation group when this reserve is part of the collaterals."],"type":{"array":["u64",32]}},{"name":"padding","type":{"array":["u64",207]}}]}},{"name":"VaultState","type":{"kind":"struct","fields":[{"name":"vaultAdminAuthority","type":"publicKey"},{"name":"baseVaultAuthority","type":"publicKey"},{"name":"baseVaultAuthorityBump","type":"u64"},{"name":"tokenMint","type":"publicKey"},{"name":"tokenMintDecimals","type":"u64"},{"name":"tokenVault","type":"publicKey"},{"name":"tokenProgram","type":"publicKey"},{"name":"sharesMint","type":"publicKey"},{"name":"sharesMintDecimals","type":"u64"},{"name":"tokenAvailable","type":"u64"},{"name":"sharesIssued","type":"u64"},{"name":"availableCrankFunds","type":"u64"},{"name":"padding0","type":"u64"},{"name":"performanceFeeBps","type":"u64"},{"name":"managementFeeBps","type":"u64"},{"name":"lastFeeChargeTimestamp","type":"u64"},{"name":"prevAumSf","type":"u128"},{"name":"pendingFeesSf","type":"u128"},{"name":"vaultAllocationStrategy","type":{"array":[{"defined":"VaultAllocation"},10]}},{"name":"minDepositAmount","type":"u64"},{"name":"minWithdrawAmount","type":"u64"},{"name":"minInvestAmount","type":"u64"},{"name":"minInvestDelaySlots","type":"u64"},{"name":"crankFundFeePerReserve","type":"u64"},{"name":"pendingAdmin","type":"publicKey"},{"name":"cumulativeEarnedInterestSf","type":"u128"},{"name":"cumulativeMgmtFeesSf","type":"u128"},{"name":"cumulativePerfFeesSf","type":"u128"},{"name":"name","type":{"array":["u8",40]}},{"name":"vaultLookupTable","type":"publicKey"},{"name":"vaultFarm","type":"publicKey"},{"name":"creationTimestamp","type":"u64"},{"name":"padding1","type":"u64"},{"name":"allocationAdmin","type":"publicKey"},{"name":"padding2","type":{"array":["u128",242]}}]}}],"types":[{"name":"LastUpdate","docs":["Last update state"],"type":{"kind":"struct","fields":[{"name":"slot","docs":["Last slot when updated"],"type":"u64"},{"name":"stale","docs":["True when marked stale, false when slot updated"],"type":"u8"},{"name":"priceStatus","docs":["Status of the prices used to calculate the last update"],"type":"u8"},{"name":"placeholder","type":{"array":["u8",6]}}]}},{"name":"BigFractionBytes","type":{"kind":"struct","fields":[{"name":"value","type":{"array":["u64",4]}},{"name":"padding","type":{"array":["u64",2]}}]}},{"name":"ReserveCollateral","docs":["Reserve collateral"],"type":{"kind":"struct","fields":[{"name":"mintPubkey","docs":["Reserve collateral mint address"],"type":"publicKey"},{"name":"mintTotalSupply","docs":["Reserve collateral mint supply, used for exchange rate"],"type":"u64"},{"name":"supplyVault","docs":["Reserve collateral supply address"],"type":"publicKey"},{"name":"padding1","type":{"array":["u128",32]}},{"name":"padding2","type":{"array":["u128",32]}}]}},{"name":"ReserveConfig","docs":["Reserve configuration values"],"type":{"kind":"struct","fields":[{"name":"status","docs":["Status of the reserve Active/Obsolete/Hidden"],"type":"u8"},{"name":"assetTier","docs":["Asset tier -> 0 - regular (collateral & debt), 1 - isolated collateral, 2 - isolated debt"],"type":"u8"},{"name":"hostFixedInterestRateBps","docs":["Flat rate that goes to the host"],"type":"u16"},{"name":"reserved2","docs":["[DEPRECATED] Boost for side (debt or collateral)"],"type":{"array":["u8",2]}},{"name":"reserved3","docs":["[DEPRECATED] Reward points multiplier per obligation type"],"type":{"array":["u8",8]}},{"name":"protocolTakeRatePct","docs":["Protocol take rate is the amount borrowed interest protocol receives, as a percentage"],"type":"u8"},{"name":"protocolLiquidationFeePct","docs":["Cut of the liquidation bonus that the protocol receives, as a percentage"],"type":"u8"},{"name":"loanToValuePct","docs":["Target ratio of the value of borrows to deposits, as a percentage","0 if use as collateral is disabled"],"type":"u8"},{"name":"liquidationThresholdPct","docs":["Loan to value ratio at which an obligation can be liquidated, as percentage"],"type":"u8"},{"name":"minLiquidationBonusBps","docs":["Minimum bonus a liquidator receives when repaying part of an unhealthy obligation, as bps"],"type":"u16"},{"name":"maxLiquidationBonusBps","docs":["Maximum bonus a liquidator receives when repaying part of an unhealthy obligation, as bps"],"type":"u16"},{"name":"badDebtLiquidationBonusBps","docs":["Bad debt liquidation bonus for an undercollateralized obligation, as bps"],"type":"u16"},{"name":"deleveragingMarginCallPeriodSecs","docs":["Time in seconds that must pass before redemptions are enabled after the deposit limit is","crossed.","Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case."],"type":"u64"},{"name":"deleveragingThresholdDecreaseBpsPerDay","docs":["The rate at which the deleveraging threshold decreases, in bps per day.","Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case."],"type":"u64"},{"name":"fees","docs":["Program owner fees assessed, separate from gains due to interest accrual"],"type":{"defined":"ReserveFees"}},{"name":"borrowRateCurve","docs":["Borrow rate curve based on utilization"],"type":{"defined":"BorrowRateCurve"}},{"name":"borrowFactorPct","docs":["Borrow factor in percentage - used for risk adjustment"],"type":"u64"},{"name":"depositLimit","docs":["Maximum deposit limit of liquidity in native units, u64::MAX for inf"],"type":"u64"},{"name":"borrowLimit","docs":["Maximum amount borrowed, u64::MAX for inf, 0 to disable borrows (protected deposits)"],"type":"u64"},{"name":"tokenInfo","docs":["Token id from TokenInfos struct"],"type":{"defined":"TokenInfo"}},{"name":"depositWithdrawalCap","docs":["Deposit withdrawal caps - deposit & redeem"],"type":{"defined":"WithdrawalCaps"}},{"name":"debtWithdrawalCap","docs":["Debt withdrawal caps - borrow & repay"],"type":{"defined":"WithdrawalCaps"}},{"name":"elevationGroups","type":{"array":["u8",20]}},{"name":"disableUsageAsCollOutsideEmode","type":"u8"},{"name":"utilizationLimitBlockBorrowingAbovePct","docs":["Utilization (in percentage) above which borrowing is blocked. 0 to disable."],"type":"u8"},{"name":"autodeleverageEnabled","docs":["Whether this reserve should be subject to auto-deleveraging after deposit or borrow limit is","crossed.","Besides this flag, the lending market's flag also needs to be enabled (logical `AND`).","**NOTE:** the manual \"target LTV\" deleveraging (enabled by the risk council for individual","obligations) is NOT affected by this flag."],"type":"u8"},{"name":"reserved1","type":{"array":["u8",1]}},{"name":"borrowLimitOutsideElevationGroup","docs":["Maximum amount liquidity of this reserve borrowed outside all elevation groups","- u64::MAX for inf","- 0 to disable borrows outside elevation groups"],"type":"u64"},{"name":"borrowLimitAgainstThisCollateralInElevationGroup","docs":["Defines the maximum amount (in lamports of elevation group debt asset)","that can be borrowed when this reserve is used as collateral.","- u64::MAX for inf","- 0 to disable borrows in this elevation group (expected value for the debt asset)"],"type":{"array":["u64",32]}},{"name":"deleveragingBonusIncreaseBpsPerDay","docs":["The rate at which the deleveraging-related liquidation bonus increases, in bps per day.","Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case."],"type":"u64"}]}},{"name":"ReserveFees","docs":["Additional fee information on a reserve","","These exist separately from interest accrual fees, and are specifically for the program owner","and referral fee. The fees are paid out as a percentage of liquidity token amounts during","repayments and liquidations."],"type":{"kind":"struct","fields":[{"name":"borrowFeeSf","docs":["Fee assessed on `BorrowObligationLiquidity`, as scaled fraction (60 bits fractional part)","Must be between `0` and `2^60`, such that `2^60 = 1`. A few examples for","clarity:","1% = (1 << 60) / 100 = 11529215046068470","0.01% (1 basis point) = 115292150460685","0.00001% (Aave borrow fee) = 115292150461"],"type":"u64"},{"name":"flashLoanFeeSf","docs":["Fee for flash loan, expressed as scaled fraction.","0.3% (Aave flash loan fee) = 0.003 * 2^60 = 3458764513820541"],"type":"u64"},{"name":"padding","docs":["Used for allignment"],"type":{"array":["u8",8]}}]}},{"name":"ReserveLiquidity","docs":["Reserve liquidity"],"type":{"kind":"struct","fields":[{"name":"mintPubkey","docs":["Reserve liquidity mint address"],"type":"publicKey"},{"name":"supplyVault","docs":["Reserve liquidity supply address"],"type":"publicKey"},{"name":"feeVault","docs":["Reserve liquidity fee collection address"],"type":"publicKey"},{"name":"availableAmount","docs":["Reserve liquidity available"],"type":"u64"},{"name":"borrowedAmountSf","docs":["Reserve liquidity borrowed (scaled fraction)"],"type":"u128"},{"name":"marketPriceSf","docs":["Reserve liquidity market price in quote currency (scaled fraction)"],"type":"u128"},{"name":"marketPriceLastUpdatedTs","docs":["Unix timestamp of the market price (from the oracle)"],"type":"u64"},{"name":"mintDecimals","docs":["Reserve liquidity mint decimals"],"type":"u64"},{"name":"depositLimitCrossedTimestamp","docs":["Timestamp when the last refresh reserve detected that the liquidity amount is above the deposit cap. When this threshold is crossed, then redemptions (auto-deleverage) are enabled.","If the threshold is not crossed, then the timestamp is set to 0"],"type":"u64"},{"name":"borrowLimitCrossedTimestamp","docs":["Timestamp when the last refresh reserve detected that the borrowed amount is above the borrow cap. When this threshold is crossed, then redemptions (auto-deleverage) are enabled.","If the threshold is not crossed, then the timestamp is set to 0"],"type":"u64"},{"name":"cumulativeBorrowRateBsf","docs":["Reserve liquidity cumulative borrow rate (scaled fraction)"],"type":{"defined":"BigFractionBytes"}},{"name":"accumulatedProtocolFeesSf","docs":["Reserve cumulative protocol fees (scaled fraction)"],"type":"u128"},{"name":"accumulatedReferrerFeesSf","docs":["Reserve cumulative referrer fees (scaled fraction)"],"type":"u128"},{"name":"pendingReferrerFeesSf","docs":["Reserve pending referrer fees, to be claimed in refresh_obligation by referrer or protocol (scaled fraction)"],"type":"u128"},{"name":"absoluteReferralRateSf","docs":["Reserve referrer fee absolute rate calculated at each refresh_reserve operation (scaled fraction)"],"type":"u128"},{"name":"tokenProgram","docs":["Token program of the liquidity mint"],"type":"publicKey"},{"name":"padding2","type":{"array":["u64",51]}},{"name":"padding3","type":{"array":["u128",32]}}]}},{"name":"WithdrawalCaps","docs":["Reserve Withdrawal Caps State"],"type":{"kind":"struct","fields":[{"name":"configCapacity","type":"i64"},{"name":"currentTotal","type":"i64"},{"name":"lastIntervalStartTimestamp","type":"u64"},{"name":"configIntervalLengthSeconds","type":"u64"}]}},{"name":"PriceHeuristic","type":{"kind":"struct","fields":[{"name":"lower","docs":["Lower value of acceptable price"],"type":"u64"},{"name":"upper","docs":["Upper value of acceptable price"],"type":"u64"},{"name":"exp","docs":["Number of decimals of the previously defined values"],"type":"u64"}]}},{"name":"PythConfiguration","type":{"kind":"struct","fields":[{"name":"price","docs":["Pubkey of the base price feed (disabled if `null` or `default`)"],"type":"publicKey"}]}},{"name":"ScopeConfiguration","type":{"kind":"struct","fields":[{"name":"priceFeed","docs":["Pubkey of the scope price feed (disabled if `null` or `default`)"],"type":"publicKey"},{"name":"priceChain","docs":["This is the scope_id price chain that results in a price for the token"],"type":{"array":["u16",4]}},{"name":"twapChain","docs":["This is the scope_id price chain for the twap"],"type":{"array":["u16",4]}}]}},{"name":"SwitchboardConfiguration","type":{"kind":"struct","fields":[{"name":"priceAggregator","docs":["Pubkey of the base price feed (disabled if `null` or `default`)"],"type":"publicKey"},{"name":"twapAggregator","type":"publicKey"}]}},{"name":"TokenInfo","type":{"kind":"struct","fields":[{"name":"name","docs":["UTF-8 encoded name of the token (null-terminated)"],"type":{"array":["u8",32]}},{"name":"heuristic","docs":["Heuristics limits of acceptable price"],"type":{"defined":"PriceHeuristic"}},{"name":"maxTwapDivergenceBps","docs":["Max divergence between twap and price in bps"],"type":"u64"},{"name":"maxAgePriceSeconds","type":"u64"},{"name":"maxAgeTwapSeconds","type":"u64"},{"name":"scopeConfiguration","docs":["Scope price configuration"],"type":{"defined":"ScopeConfiguration"}},{"name":"switchboardConfiguration","docs":["Switchboard configuration"],"type":{"defined":"SwitchboardConfiguration"}},{"name":"pythConfiguration","docs":["Pyth configuration"],"type":{"defined":"PythConfiguration"}},{"name":"blockPriceUsage","type":"u8"},{"name":"reserved","type":{"array":["u8",7]}},{"name":"padding","type":{"array":["u64",19]}}]}},{"name":"BorrowRateCurve","type":{"kind":"struct","fields":[{"name":"points","type":{"array":[{"defined":"CurvePoint"},11]}}]}},{"name":"CurvePoint","type":{"kind":"struct","fields":[{"name":"utilizationRateBps","type":"u32"},{"name":"borrowRateBps","type":"u32"}]}},{"name":"VaultConfigField","type":{"kind":"enum","variants":[{"name":"PerformanceFeeBps"},{"name":"ManagementFeeBps"},{"name":"MinDepositAmount"},{"name":"MinWithdrawAmount"},{"name":"MinInvestAmount"},{"name":"MinInvestDelaySlots"},{"name":"CrankFundFeePerReserve"},{"name":"PendingVaultAdmin"},{"name":"Name"},{"name":"LookupTable"},{"name":"Farm"},{"name":"AllocationAdmin"}]}},{"name":"VaultAllocation","type":{"kind":"struct","fields":[{"name":"reserve","type":"publicKey"},{"name":"ctokenVault","type":"publicKey"},{"name":"targetAllocationWeight","type":"u64"},{"name":"tokenAllocationCap","docs":["Maximum token invested in this reserve"],"type":"u64"},{"name":"ctokenVaultBump","type":"u64"},{"name":"configPadding","type":{"array":["u64",127]}},{"name":"ctokenAllocation","type":"u64"},{"name":"lastInvestSlot","type":"u64"},{"name":"tokenTargetAllocationSf","type":"u128"},{"name":"statePadding","type":{"array":["u64",128]}}]}}],"errors":[{"code":7000,"name":"DepositAmountsZero","msg":"DepositAmountsZero"},{"code":7001,"name":"SharesIssuedAmountDoesNotMatch","msg":"SharesIssuedAmountDoesNotMatch"},{"code":7002,"name":"MathOverflow","msg":"MathOverflow"},{"code":7003,"name":"IntegerOverflow","msg":"IntegerOverflow"},{"code":7004,"name":"WithdrawAmountBelowMinimum","msg":"Withdrawn amount is below minimum"},{"code":7005,"name":"TooMuchLiquidityToWithdraw","msg":"TooMuchLiquidityToWithdraw"},{"code":7006,"name":"ReserveAlreadyExists","msg":"ReserveAlreadyExists"},{"code":7007,"name":"ReserveNotPartOfAllocations","msg":"ReserveNotPartOfAllocations"},{"code":7008,"name":"CouldNotDeserializeAccountAsReserve","msg":"CouldNotDeserializeAccountAsReserve"},{"code":7009,"name":"ReserveNotProvidedInTheAccounts","msg":"ReserveNotProvidedInTheAccounts"},{"code":7010,"name":"ReserveAccountAndKeyMismatch","msg":"ReserveAccountAndKeyMismatch"},{"code":7011,"name":"OutOfRangeOfReserveIndex","msg":"OutOfRangeOfReserveIndex"},{"code":7012,"name":"CannotFindReserveInAllocations","msg":"OutOfRangeOfReserveIndex"},{"code":7013,"name":"InvestAmountBelowMinimum","msg":"Invested amount is below minimum"},{"code":7014,"name":"AdminAuthorityIncorrect","msg":"AdminAuthorityIncorrect"},{"code":7015,"name":"BaseVaultAuthorityIncorrect","msg":"BaseVaultAuthorityIncorrect"},{"code":7016,"name":"BaseVaultAuthorityBumpIncorrect","msg":"BaseVaultAuthorityBumpIncorrect"},{"code":7017,"name":"TokenMintIncorrect","msg":"TokenMintIncorrect"},{"code":7018,"name":"TokenMintDecimalsIncorrect","msg":"TokenMintDecimalsIncorrect"},{"code":7019,"name":"TokenVaultIncorrect","msg":"TokenVaultIncorrect"},{"code":7020,"name":"SharesMintDecimalsIncorrect","msg":"SharesMintDecimalsIncorrect"},{"code":7021,"name":"SharesMintIncorrect","msg":"SharesMintIncorrect"},{"code":7022,"name":"InitialAccountingIncorrect","msg":"InitialAccountingIncorrect"},{"code":7023,"name":"ReserveIsStale","msg":"Reserve is stale and must be refreshed before any operation"},{"code":7024,"name":"NotEnoughLiquidityDisinvestedToSendToUser","msg":"Not enough liquidity disinvested to send to user"},{"code":7025,"name":"BPSValueTooBig","msg":"BPS value is greater than 10000"},{"code":7026,"name":"DepositAmountBelowMinimum","msg":"Deposited amount is below minimum"},{"code":7027,"name":"ReserveSpaceExhausted","msg":"Vault have no space for new reserves"},{"code":7028,"name":"CannotWithdrawFromEmptyVault","msg":"Cannot withdraw from empty vault"},{"code":7029,"name":"TokensDepositedAmountDoesNotMatch","msg":"TokensDepositedAmountDoesNotMatch"},{"code":7030,"name":"AmountToWithdrawDoesNotMatch","msg":"Amount to withdraw does not match"},{"code":7031,"name":"LiquidityToWithdrawDoesNotMatch","msg":"Liquidity to withdraw does not match"},{"code":7032,"name":"UserReceivedAmountDoesNotMatch","msg":"User received amount does not match"},{"code":7033,"name":"SharesBurnedAmountDoesNotMatch","msg":"Shares burned amount does not match"},{"code":7034,"name":"DisinvestedLiquidityAmountDoesNotMatch","msg":"Disinvested liquidity amount does not match"},{"code":7035,"name":"SharesMintedAmountDoesNotMatch","msg":"SharesMintedAmountDoesNotMatch"},{"code":7036,"name":"AUMDecreasedAfterInvest","msg":"AUM decreased after invest"},{"code":7037,"name":"AUMBelowPendingFees","msg":"AUM is below pending fees"},{"code":7038,"name":"DepositAmountsZeroShares","msg":"Deposit amount results in 0 shares"},{"code":7039,"name":"WithdrawResultsInZeroShares","msg":"Withdraw amount results in 0 shares"},{"code":7040,"name":"CannotWithdrawZeroShares","msg":"Cannot withdraw zero shares"},{"code":7041,"name":"ManagementFeeGreaterThanMaxAllowed","msg":"Management fee is greater than maximum allowed"},{"code":7042,"name":"VaultAUMZero","msg":"Vault assets under management are empty"},{"code":7043,"name":"MissingReserveForBatchRefresh","msg":"Missing reserve for batch refresh"},{"code":7044,"name":"MinWithdrawAmountTooBig","msg":"Min withdraw amount is too big"},{"code":7045,"name":"InvestTooSoon","msg":"Invest is called too soon after last invest"},{"code":7046,"name":"WrongAdminOrAllocationAdmin","msg":"Wrong admin or allocation admin"}]} \ No newline at end of file diff --git a/idls/kamino_vault_v2.KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd.json b/idls/kamino_vault_v2.KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd.json new file mode 100644 index 0000000..b4bf03e --- /dev/null +++ b/idls/kamino_vault_v2.KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd.json @@ -0,0 +1 @@ +{"version":"2.1.1","name":"kamino_vault","instructions":[{"name":"initVault","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"baseTokenMint","isMut":false,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"adminTokenAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateReserveAllocation","accounts":[{"name":"signer","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserve","isMut":false,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"reserveWhitelistEntry","isMut":false,"isSigner":false,"isOptional":true},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"weight","type":"u64"},{"name":"cap","type":"u64"}]},{"name":"deposit","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"userTokenAta","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"maxAmount","type":"u64"}]},{"name":"buy","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"userTokenAta","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"maxAmount","type":"u64"}]},{"name":"withdraw","accounts":[{"name":"withdrawFromAvailable","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"userTokenAta","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}]},{"name":"withdrawFromReserveAccounts","accounts":[{"name":"vaultState","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"sharesAmount","type":"u64"}]},{"name":"sell","accounts":[{"name":"withdrawFromAvailable","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"userTokenAta","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}]},{"name":"withdrawFromReserveAccounts","accounts":[{"name":"vaultState","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"sharesAmount","type":"u64"}]},{"name":"invest","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"payerTokenAccount","isMut":true,"isSigner":false},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false,"docs":["CPI accounts"]},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveWhitelistEntry","isMut":false,"isSigner":false,"isOptional":true},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[]},{"name":"updateVaultConfig","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false}],"args":[{"name":"entry","type":{"defined":"VaultConfigField"}},{"name":"data","type":"bytes"}]},{"name":"withdrawPendingFees","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"tokenAta","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false,"docs":["CPI accounts"]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[]},{"name":"updateAdmin","accounts":[{"name":"pendingAdmin","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false}],"args":[]},{"name":"giveUpPendingFees","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxAmountToGiveUp","type":"u64"}]},{"name":"initializeSharesMetadata","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":false,"isSigner":false},{"name":"sharesMint","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"sharesMetadata","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"updateSharesMetadata","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"sharesMetadata","isMut":true,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"withdrawFromAvailable","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"userTokenAta","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"sharesAmount","type":"u64"}]},{"name":"removeAllocation","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"reserve","isMut":false,"isSigner":false}],"args":[]},{"name":"initGlobalConfig","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false},{"name":"programData","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"updateGlobalConfig","accounts":[{"name":"globalAdmin","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false}],"args":[{"name":"update","type":{"defined":"UpdateGlobalConfigMode"}}]},{"name":"updateGlobalConfigAdmin","accounts":[{"name":"pendingAdmin","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false}],"args":[]},{"name":"addUpdateWhitelistedReserve","accounts":[{"name":"globalAdmin","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"reserve","isMut":false,"isSigner":false},{"name":"reserveWhitelistEntry","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"update","type":{"defined":"UpdateReserveWhitelistMode"}}]},{"name":"topupRewards","accounts":[{"name":"payer","isMut":false,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"payerTokenTa","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdrawRewards","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"withdrawTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"redeemInKind","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"userCtokenTa","isMut":true,"isSigner":false},{"name":"ctokenMint","isMut":true,"isSigner":false},{"name":"userSharesTa","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"sharesAmount","type":"u64"}]}],"accounts":[{"name":"Reserve","type":{"kind":"struct","fields":[{"name":"version","docs":["Version of the reserve"],"type":"u64"},{"name":"lastUpdate","docs":["Last slot when supply and rates updated"],"type":{"defined":"LastUpdate"}},{"name":"lendingMarket","docs":["Lending market address"],"type":"publicKey"},{"name":"farmCollateral","type":"publicKey"},{"name":"farmDebt","type":"publicKey"},{"name":"liquidity","docs":["Reserve liquidity"],"type":{"defined":"ReserveLiquidity"}},{"name":"reserveLiquidityPadding","type":{"array":["u64",150]}},{"name":"collateral","docs":["Reserve collateral"],"type":{"defined":"ReserveCollateral"}},{"name":"reserveCollateralPadding","type":{"array":["u64",150]}},{"name":"config","docs":["Reserve configuration values"],"type":{"defined":"ReserveConfig"}},{"name":"configPadding","type":{"array":["u64",116]}},{"name":"borrowedAmountOutsideElevationGroup","type":"u64"},{"name":"borrowedAmountsAgainstThisReserveInElevationGroups","docs":["Amount of token borrowed in lamport of debt asset in the given","elevation group when this reserve is part of the collaterals."],"type":{"array":["u64",32]}},{"name":"padding","type":{"array":["u64",207]}}]}},{"name":"GlobalConfig","type":{"kind":"struct","fields":[{"name":"globalAdmin","type":"publicKey"},{"name":"pendingAdmin","type":"publicKey"},{"name":"withdrawalPenaltyLamports","type":"u64"},{"name":"withdrawalPenaltyBps","type":"u64"},{"name":"padding","type":{"array":["u8",944]}}]}},{"name":"ReserveWhitelistEntry","type":{"kind":"struct","fields":[{"name":"tokenMint","docs":["The token mint is stored to solve the problem of finding all the whitelisted reserves for a particular token mint:","when storing the token mint inside the PDA, finding all the whitelisted reserves becomes a `getProgramAccounts` with","a filter on discriminator + the mint field","The reserve pubkey, as seed of the reserve whitelist PDA account, it stored so you can link back the PDA to its seeds","(for instance, in the operation above we easily find the reserve corresponding to the PDA)"],"type":"publicKey"},{"name":"reserve","type":"publicKey"},{"name":"whitelistAddAllocation","type":"u8"},{"name":"whitelistInvest","type":"u8"},{"name":"padding","type":{"array":["u8",62]}}]}},{"name":"VaultState","type":{"kind":"struct","fields":[{"name":"vaultAdminAuthority","type":"publicKey"},{"name":"baseVaultAuthority","type":"publicKey"},{"name":"baseVaultAuthorityBump","type":"u64"},{"name":"tokenMint","type":"publicKey"},{"name":"tokenMintDecimals","type":"u64"},{"name":"tokenVault","type":"publicKey"},{"name":"tokenProgram","type":"publicKey"},{"name":"sharesMint","type":"publicKey"},{"name":"sharesMintDecimals","type":"u64"},{"name":"tokenAvailable","type":"u64"},{"name":"sharesIssued","type":"u64"},{"name":"availableCrankFunds","type":"u64"},{"name":"unallocatedWeight","type":"u64"},{"name":"performanceFeeBps","type":"u64"},{"name":"managementFeeBps","type":"u64"},{"name":"lastFeeChargeTimestamp","type":"u64"},{"name":"prevAumSf","type":"u128"},{"name":"pendingFeesSf","type":"u128"},{"name":"vaultAllocationStrategy","type":{"array":[{"defined":"VaultAllocation"},25]}},{"name":"padding1","type":{"array":["u128",256]}},{"name":"minDepositAmount","type":"u64"},{"name":"minWithdrawAmount","type":"u64"},{"name":"minInvestAmount","type":"u64"},{"name":"minInvestDelaySlots","type":"u64"},{"name":"crankFundFeePerReserve","type":"u64"},{"name":"pendingAdmin","type":"publicKey"},{"name":"cumulativeEarnedInterestSf","type":"u128"},{"name":"cumulativeMgmtFeesSf","type":"u128"},{"name":"cumulativePerfFeesSf","type":"u128"},{"name":"name","type":{"array":["u8",40]}},{"name":"vaultLookupTable","type":"publicKey"},{"name":"vaultFarm","type":"publicKey"},{"name":"creationTimestamp","type":"u64"},{"name":"unallocatedTokensCap","type":"u64"},{"name":"allocationAdmin","type":"publicKey"},{"name":"withdrawalPenaltyLamports","type":"u64"},{"name":"withdrawalPenaltyBps","type":"u64"},{"name":"firstLossCapitalFarm","type":"publicKey"},{"name":"allowAllocationsInWhitelistedReservesOnly","type":"u8"},{"name":"allowInvestInWhitelistedReservesOnly","type":"u8"},{"name":"padding2","type":{"array":["u8",14]}},{"name":"rewardInfo","type":{"defined":"VaultRewardInfo"}},{"name":"padding3","type":{"array":["u128",232]}}]}}],"types":[{"name":"LastUpdate","docs":["Last update state"],"type":{"kind":"struct","fields":[{"name":"slot","docs":["Last slot when updated"],"type":"u64"},{"name":"stale","docs":["True when marked stale, false when slot updated"],"type":"u8"},{"name":"priceStatus","docs":["Status of the prices used to calculate the last update"],"type":"u8"},{"name":"placeholder","type":{"array":["u8",6]}}]}},{"name":"BigFractionBytes","type":{"kind":"struct","fields":[{"name":"value","type":{"array":["u64",4]}},{"name":"padding","type":{"array":["u64",2]}}]}},{"name":"ReserveCollateral","docs":["Reserve collateral"],"type":{"kind":"struct","fields":[{"name":"mintPubkey","docs":["Reserve collateral mint address"],"type":"publicKey"},{"name":"mintTotalSupply","docs":["Reserve collateral mint supply, used for exchange rate"],"type":"u64"},{"name":"supplyVault","docs":["Reserve collateral supply address"],"type":"publicKey"},{"name":"padding1","type":{"array":["u128",32]}},{"name":"padding2","type":{"array":["u128",32]}}]}},{"name":"ReserveConfig","docs":["Reserve configuration values"],"type":{"kind":"struct","fields":[{"name":"status","docs":["Status of the reserve Active/Obsolete/Hidden"],"type":"u8"},{"name":"assetTier","docs":["Asset tier -> 0 - regular (collateral & debt), 1 - isolated collateral, 2 - isolated debt"],"type":"u8"},{"name":"hostFixedInterestRateBps","docs":["Flat rate that goes to the host"],"type":"u16"},{"name":"minDeleveragingBonusBps","docs":["Starting bonus for deleveraging-related liquidations, in bps."],"type":"u16"},{"name":"blockCtokenUsage","docs":["Boolean flag to block minting/redeeming of ctokens","Blocks usage of ctokens (minting or withdrawing from obligation)","Effectively blocks deposit_reserve_liquidity and withdraw_obligation_collateral"],"type":"u8"},{"name":"reserved1","docs":["Past reserved space - feel free to reuse."],"type":{"array":["u8",6]}},{"name":"protocolOrderExecutionFeePct","docs":["Cut of the order execution bonus that the protocol receives, as a percentage"],"type":"u8"},{"name":"protocolTakeRatePct","docs":["Protocol take rate is the amount borrowed interest protocol receives, as a percentage"],"type":"u8"},{"name":"protocolLiquidationFeePct","docs":["Cut of the liquidation bonus that the protocol receives, as a percentage"],"type":"u8"},{"name":"loanToValuePct","docs":["Target ratio of the value of borrows to deposits, as a percentage","0 if use as collateral is disabled"],"type":"u8"},{"name":"liquidationThresholdPct","docs":["Loan to value ratio at which an obligation can be liquidated, as percentage"],"type":"u8"},{"name":"minLiquidationBonusBps","docs":["Minimum bonus a liquidator receives when repaying part of an unhealthy obligation, as bps"],"type":"u16"},{"name":"maxLiquidationBonusBps","docs":["Maximum bonus a liquidator receives when repaying part of an unhealthy obligation, as bps"],"type":"u16"},{"name":"badDebtLiquidationBonusBps","docs":["Bad debt liquidation bonus for an undercollateralized obligation, as bps"],"type":"u16"},{"name":"deleveragingMarginCallPeriodSecs","docs":["Time in seconds that must pass before redemptions are enabled after the deposit limit is","crossed.","Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case."],"type":"u64"},{"name":"deleveragingThresholdDecreaseBpsPerDay","docs":["The rate at which the deleveraging threshold decreases, in bps per day.","Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case."],"type":"u64"},{"name":"fees","docs":["Program owner fees assessed, separate from gains due to interest accrual"],"type":{"defined":"ReserveFees"}},{"name":"borrowRateCurve","docs":["Borrow rate curve based on utilization"],"type":{"defined":"BorrowRateCurve"}},{"name":"borrowFactorPct","docs":["Borrow factor in percentage - used for risk adjustment"],"type":"u64"},{"name":"depositLimit","docs":["Maximum deposit limit of liquidity in native units, u64::MAX for inf"],"type":"u64"},{"name":"borrowLimit","docs":["Maximum amount borrowed, u64::MAX for inf, 0 to disable borrows (protected deposits)"],"type":"u64"},{"name":"tokenInfo","docs":["Token id from TokenInfos struct"],"type":{"defined":"TokenInfo"}},{"name":"depositWithdrawalCap","docs":["Deposit withdrawal caps - deposit & redeem"],"type":{"defined":"WithdrawalCaps"}},{"name":"debtWithdrawalCap","docs":["Debt withdrawal caps - borrow & repay"],"type":{"defined":"WithdrawalCaps"}},{"name":"elevationGroups","type":{"array":["u8",20]}},{"name":"disableUsageAsCollOutsideEmode","type":"u8"},{"name":"utilizationLimitBlockBorrowingAbovePct","docs":["Utilization (in percentage) above which borrowing is blocked. 0 to disable."],"type":"u8"},{"name":"autodeleverageEnabled","docs":["Whether this reserve should be subject to auto-deleveraging after deposit or borrow limit is","crossed.","Besides this flag, the lending market's flag also needs to be enabled (logical `AND`).","**NOTE:** the manual \"target LTV\" deleveraging (enabled by the risk council for individual","obligations) is NOT affected by this flag."],"type":"u8"},{"name":"proposerAuthorityLocked","docs":["Boolean flag indicating whether the reserve is locked for the proposer authority.","","Once the proposer have finished preparing the reserve, it must be locked to prevent","further changes to the reserve configuration allowing review and voting on the proposal","without alteration during the voting period."],"type":"u8"},{"name":"borrowLimitOutsideElevationGroup","docs":["Maximum amount liquidity of this reserve borrowed outside all elevation groups","- u64::MAX for inf","- 0 to disable borrows outside elevation groups"],"type":"u64"},{"name":"borrowLimitAgainstThisCollateralInElevationGroup","docs":["Defines the maximum amount (in lamports of elevation group debt asset)","that can be borrowed when this reserve is used as collateral.","- u64::MAX for inf","- 0 to disable borrows in this elevation group (expected value for the debt asset)"],"type":{"array":["u64",32]}},{"name":"deleveragingBonusIncreaseBpsPerDay","docs":["The rate at which the deleveraging-related liquidation bonus increases, in bps per day.","Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case."],"type":"u64"}]}},{"name":"ReserveFees","docs":["Additional fee information on a reserve","","These exist separately from interest accrual fees, and are specifically for the program owner","and referral fee. The fees are paid out as a percentage of liquidity token amounts during","repayments and liquidations."],"type":{"kind":"struct","fields":[{"name":"originationFeeSf","docs":["Fee assessed on `BorrowObligationLiquidity`, as scaled fraction (60 bits fractional part)","Must be between `0` and `2^60`, such that `2^60 = 1`. A few examples for","clarity:","1% = (1 << 60) / 100 = 11529215046068470","0.01% (1 basis point) = 115292150460685","0.00001% (Aave origination fee) = 115292150461"],"type":"u64"},{"name":"flashLoanFeeSf","docs":["Fee for flash loan, expressed as scaled fraction.","0.3% (Aave flash loan fee) = 0.003 * 2^60 = 3458764513820541"],"type":"u64"},{"name":"padding","docs":["Used for allignment"],"type":{"array":["u8",8]}}]}},{"name":"ReserveLiquidity","docs":["Reserve liquidity"],"type":{"kind":"struct","fields":[{"name":"mintPubkey","docs":["Reserve liquidity mint address"],"type":"publicKey"},{"name":"supplyVault","docs":["Reserve liquidity supply address"],"type":"publicKey"},{"name":"feeVault","docs":["Reserve liquidity fee collection address"],"type":"publicKey"},{"name":"availableAmount","docs":["Reserve liquidity available"],"type":"u64"},{"name":"borrowedAmountSf","docs":["Reserve liquidity borrowed (scaled fraction)"],"type":"u128"},{"name":"marketPriceSf","docs":["Reserve liquidity market price in quote currency (scaled fraction)"],"type":"u128"},{"name":"marketPriceLastUpdatedTs","docs":["Unix timestamp of the market price (from the oracle)"],"type":"u64"},{"name":"mintDecimals","docs":["Reserve liquidity mint decimals"],"type":"u64"},{"name":"depositLimitCrossedTimestamp","docs":["Timestamp when the last refresh reserve detected that the liquidity amount is above the deposit cap. When this threshold is crossed, then redemptions (auto-deleverage) are enabled.","If the threshold is not crossed, then the timestamp is set to 0"],"type":"u64"},{"name":"borrowLimitCrossedTimestamp","docs":["Timestamp when the last refresh reserve detected that the borrowed amount is above the borrow cap. When this threshold is crossed, then redemptions (auto-deleverage) are enabled.","If the threshold is not crossed, then the timestamp is set to 0"],"type":"u64"},{"name":"cumulativeBorrowRateBsf","docs":["Reserve liquidity cumulative borrow rate (scaled fraction)"],"type":{"defined":"BigFractionBytes"}},{"name":"accumulatedProtocolFeesSf","docs":["Reserve cumulative protocol fees (scaled fraction)"],"type":"u128"},{"name":"accumulatedReferrerFeesSf","docs":["Reserve cumulative referrer fees (scaled fraction)"],"type":"u128"},{"name":"pendingReferrerFeesSf","docs":["Reserve pending referrer fees, to be claimed in refresh_obligation by referrer or protocol (scaled fraction)"],"type":"u128"},{"name":"absoluteReferralRateSf","docs":["Reserve referrer fee absolute rate calculated at each refresh_reserve operation (scaled fraction)"],"type":"u128"},{"name":"tokenProgram","docs":["Token program of the liquidity mint"],"type":"publicKey"},{"name":"padding2","type":{"array":["u64",51]}},{"name":"padding3","type":{"array":["u128",32]}}]}},{"name":"WithdrawalCaps","docs":["Reserve Withdrawal Caps State"],"type":{"kind":"struct","fields":[{"name":"configCapacity","type":"i64"},{"name":"currentTotal","type":"i64"},{"name":"lastIntervalStartTimestamp","type":"u64"},{"name":"configIntervalLengthSeconds","type":"u64"}]}},{"name":"PriceHeuristic","type":{"kind":"struct","fields":[{"name":"lower","docs":["Lower value of acceptable price"],"type":"u64"},{"name":"upper","docs":["Upper value of acceptable price"],"type":"u64"},{"name":"exp","docs":["Number of decimals of the previously defined values"],"type":"u64"}]}},{"name":"PythConfiguration","type":{"kind":"struct","fields":[{"name":"price","docs":["Pubkey of the base price feed (disabled if `null` or `default`)"],"type":"publicKey"}]}},{"name":"ScopeConfiguration","type":{"kind":"struct","fields":[{"name":"priceFeed","docs":["Pubkey of the scope price feed (disabled if `null` or `default`)"],"type":"publicKey"},{"name":"priceChain","docs":["This is the scope_id price chain that results in a price for the token"],"type":{"array":["u16",4]}},{"name":"twapChain","docs":["This is the scope_id price chain for the twap"],"type":{"array":["u16",4]}}]}},{"name":"SwitchboardConfiguration","type":{"kind":"struct","fields":[{"name":"priceAggregator","docs":["Pubkey of the base price feed (disabled if `null` or `default`)"],"type":"publicKey"},{"name":"twapAggregator","type":"publicKey"}]}},{"name":"TokenInfo","type":{"kind":"struct","fields":[{"name":"name","docs":["UTF-8 encoded name of the token (null-terminated)"],"type":{"array":["u8",32]}},{"name":"heuristic","docs":["Heuristics limits of acceptable price"],"type":{"defined":"PriceHeuristic"}},{"name":"maxTwapDivergenceBps","docs":["Max divergence between twap and price in bps"],"type":"u64"},{"name":"maxAgePriceSeconds","type":"u64"},{"name":"maxAgeTwapSeconds","type":"u64"},{"name":"scopeConfiguration","docs":["Scope price configuration"],"type":{"defined":"ScopeConfiguration"}},{"name":"switchboardConfiguration","docs":["Switchboard configuration"],"type":{"defined":"SwitchboardConfiguration"}},{"name":"pythConfiguration","docs":["Pyth configuration"],"type":{"defined":"PythConfiguration"}},{"name":"blockPriceUsage","type":"u8"},{"name":"reserved","type":{"array":["u8",7]}},{"name":"padding","type":{"array":["u64",19]}}]}},{"name":"BorrowRateCurve","type":{"kind":"struct","fields":[{"name":"points","type":{"array":[{"defined":"CurvePoint"},11]}}]}},{"name":"CurvePoint","type":{"kind":"struct","fields":[{"name":"utilizationRateBps","type":"u32"},{"name":"borrowRateBps","type":"u32"}]}},{"name":"UpdateReserveWhitelistMode","type":{"kind":"enum","variants":[{"name":"Invest","fields":["u8"]},{"name":"AddAllocation","fields":["u8"]}]}},{"name":"VaultConfigField","type":{"kind":"enum","variants":[{"name":"PerformanceFeeBps"},{"name":"ManagementFeeBps"},{"name":"MinDepositAmount"},{"name":"MinWithdrawAmount"},{"name":"MinInvestAmount"},{"name":"MinInvestDelaySlots"},{"name":"CrankFundFeePerReserve"},{"name":"PendingVaultAdmin"},{"name":"Name"},{"name":"LookupTable"},{"name":"Farm"},{"name":"AllocationAdmin"},{"name":"UnallocatedWeight"},{"name":"UnallocatedTokensCap"},{"name":"WithdrawalPenaltyLamports"},{"name":"WithdrawalPenaltyBps"},{"name":"FirstLossCapitalFarm"},{"name":"AllowAllocationsInWhitelistedReservesOnly"},{"name":"AllowInvestInWhitelistedReservesOnly"},{"name":"RewardPerSecond"}]}},{"name":"VaultAllocation","type":{"kind":"struct","fields":[{"name":"reserve","type":"publicKey"},{"name":"ctokenVault","type":"publicKey"},{"name":"targetAllocationWeight","type":"u64"},{"name":"tokenAllocationCap","docs":["Maximum token invested in this reserve"],"type":"u64"},{"name":"ctokenVaultBump","type":"u64"},{"name":"configPadding","type":{"array":["u64",127]}},{"name":"ctokenAllocation","type":"u64"},{"name":"lastInvestSlot","type":"u64"},{"name":"tokenTargetAllocationSf","type":"u128"},{"name":"statePadding","type":{"array":["u64",128]}}]}},{"name":"VaultRewardInfo","type":{"kind":"struct","fields":[{"name":"rewardPerSecond","type":"u64"},{"name":"lastIssuanceTs","type":"u64"},{"name":"rewardsAvailable","docs":["Rewards available to distribute (topped up but not yet moved to vault.token_available)"],"type":"u64"},{"name":"cumulativeRewardsDistributedAnalytics","type":"u64"},{"name":"padding","type":{"array":["u64",8]}}]}},{"name":"UpdateGlobalConfigMode","type":{"kind":"enum","variants":[{"name":"PendingAdmin","fields":["publicKey"]},{"name":"MinWithdrawalPenaltyLamports","fields":["u64"]},{"name":"MinWithdrawalPenaltyBPS","fields":["u64"]}]}}],"events":[{"name":"DepositResultEvent","fields":[{"name":"sharesToMint","type":"u64","index":false},{"name":"tokenToDeposit","type":"u64","index":false},{"name":"crankFundsToDeposit","type":"u64","index":false}]},{"name":"DepositUserAtaBalanceEvent","fields":[{"name":"userAtaBalance","type":"u64","index":false}]},{"name":"RedeemInKindResultEvent","fields":[{"name":"sharesToBurn","type":"u64","index":false},{"name":"ctokensToSendToUser","type":"u64","index":false}]},{"name":"SharesToWithdrawEvent","fields":[{"name":"sharesAmount","type":"u64","index":false},{"name":"userSharesBefore","type":"u64","index":false}]},{"name":"WithdrawResultEvent","fields":[{"name":"sharesToBurn","type":"u64","index":false},{"name":"availableToSendToUser","type":"u64","index":false},{"name":"investedToDisinvestCtokens","type":"u64","index":false},{"name":"investedLiquidityToSendToUser","type":"u64","index":false}]}],"errors":[{"code":7000,"name":"DepositAmountsZero","msg":"Cannot deposit zero tokens"},{"code":7001,"name":"SharesIssuedAmountDoesNotMatch","msg":"Post check failed on share issued"},{"code":7002,"name":"MathOverflow","msg":"Math operation overflowed"},{"code":7003,"name":"IntegerOverflow","msg":"Integer conversion overflowed"},{"code":7004,"name":"WithdrawAmountBelowMinimum","msg":"Withdrawn amount is below minimum"},{"code":7005,"name":"TooMuchLiquidityToWithdraw","msg":"TooMuchLiquidityToWithdraw"},{"code":7006,"name":"ReserveAlreadyExists","msg":"ReserveAlreadyExists"},{"code":7007,"name":"ReserveNotPartOfAllocations","msg":"ReserveNotPartOfAllocations"},{"code":7008,"name":"CouldNotDeserializeAccountAsReserve","msg":"CouldNotDeserializeAccountAsReserve"},{"code":7009,"name":"ReserveNotProvidedInTheAccounts","msg":"ReserveNotProvidedInTheAccounts"},{"code":7010,"name":"ReserveAccountAndKeyMismatch","msg":"ReserveAccountAndKeyMismatch"},{"code":7011,"name":"OutOfRangeOfReserveIndex","msg":"OutOfRangeOfReserveIndex"},{"code":7012,"name":"CannotFindReserveInAllocations","msg":"OutOfRangeOfReserveIndex"},{"code":7013,"name":"InvestAmountBelowMinimum","msg":"Invested amount is below minimum"},{"code":7014,"name":"AdminAuthorityIncorrect","msg":"AdminAuthorityIncorrect"},{"code":7015,"name":"BaseVaultAuthorityIncorrect","msg":"BaseVaultAuthorityIncorrect"},{"code":7016,"name":"BaseVaultAuthorityBumpIncorrect","msg":"BaseVaultAuthorityBumpIncorrect"},{"code":7017,"name":"TokenMintIncorrect","msg":"TokenMintIncorrect"},{"code":7018,"name":"TokenMintDecimalsIncorrect","msg":"TokenMintDecimalsIncorrect"},{"code":7019,"name":"TokenVaultIncorrect","msg":"TokenVaultIncorrect"},{"code":7020,"name":"SharesMintDecimalsIncorrect","msg":"SharesMintDecimalsIncorrect"},{"code":7021,"name":"SharesMintIncorrect","msg":"SharesMintIncorrect"},{"code":7022,"name":"InitialAccountingIncorrect","msg":"InitialAccountingIncorrect"},{"code":7023,"name":"ReserveIsStale","msg":"Reserve is stale and must be refreshed before any operation"},{"code":7024,"name":"NotEnoughLiquidityDisinvestedToSendToUser","msg":"Not enough liquidity disinvested to send to user"},{"code":7025,"name":"BPSValueTooBig","msg":"BPS value is greater than 10000"},{"code":7026,"name":"DepositAmountBelowMinimum","msg":"Deposited amount is below minimum"},{"code":7027,"name":"ReserveSpaceExhausted","msg":"Vault have no space for new reserves"},{"code":7028,"name":"CannotWithdrawFromEmptyVault","msg":"Cannot withdraw from empty vault"},{"code":7029,"name":"TokensDepositedAmountDoesNotMatch","msg":"TokensDepositedAmountDoesNotMatch"},{"code":7030,"name":"AmountToWithdrawDoesNotMatch","msg":"Amount to withdraw does not match"},{"code":7031,"name":"LiquidityToWithdrawDoesNotMatch","msg":"Liquidity to withdraw does not match"},{"code":7032,"name":"UserReceivedAmountDoesNotMatch","msg":"User received amount does not match"},{"code":7033,"name":"SharesBurnedAmountDoesNotMatch","msg":"Shares burned amount does not match"},{"code":7034,"name":"DisinvestedLiquidityAmountDoesNotMatch","msg":"Disinvested liquidity amount does not match"},{"code":7035,"name":"SharesMintedAmountDoesNotMatch","msg":"SharesMintedAmountDoesNotMatch"},{"code":7036,"name":"AUMDecreasedAfterInvest","msg":"AUM decreased after invest"},{"code":7037,"name":"AUMBelowPendingFees","msg":"AUM is below pending fees"},{"code":7038,"name":"DepositAmountsZeroShares","msg":"Deposit amount results in 0 shares"},{"code":7039,"name":"WithdrawResultsInZeroShares","msg":"Withdraw amount results in 0 shares"},{"code":7040,"name":"CannotWithdrawZeroShares","msg":"Cannot withdraw zero shares"},{"code":7041,"name":"ManagementFeeGreaterThanMaxAllowed","msg":"Management fee is greater than maximum allowed"},{"code":7042,"name":"VaultAUMZero","msg":"Vault assets under management are empty"},{"code":7043,"name":"MissingReserveForBatchRefresh","msg":"Missing reserve for batch refresh"},{"code":7044,"name":"MinWithdrawAmountTooBig","msg":"Min withdraw amount is too big"},{"code":7045,"name":"InvestTooSoon","msg":"Invest is called too soon after last invest"},{"code":7046,"name":"WrongAdminOrAllocationAdmin","msg":"Wrong admin or allocation admin"},{"code":7047,"name":"ReserveHasNonZeroAllocationOrCTokens","msg":"Reserve has non-zero allocation or ctokens so cannot be removed"},{"code":7048,"name":"DepositAmountGreaterThanRequestedAmount","msg":"Deposit amount is greater than requested amount"},{"code":7049,"name":"WithdrawAmountLessThanWithdrawalPenalty","msg":"Withdraw amount is less than withdrawal penalty"},{"code":7050,"name":"CannotWithdrawZeroLamports","msg":"Cannot withdraw 0 lamports"},{"code":7051,"name":"NoUpgradeAuthority","msg":"Cannot initialize global config because there is no upgrade authority to the program"},{"code":7052,"name":"WithdrawalFeeBPSGreaterThanMaxAllowed","msg":"Withdrawal fee BPS is greater than maximum allowed"},{"code":7053,"name":"WithdrawalFeeLamportsGreaterThanMaxAllowed","msg":"Withdrawal fee lamports is greater than maximum allowed"},{"code":7054,"name":"ReserveNotWhitelisted","msg":"Reserve is not whitelisted"},{"code":7055,"name":"InvalidBoolLikeValue","msg":"Invalid bool-like value passed in (should be 0 or 1)"},{"code":7056,"name":"AUMDecreasedMoreThanExpected","msg":"AUM decreased more than expected during redeem in kind"},{"code":7057,"name":"RewardTopupAmountZero","msg":"Reward topup amount is zero"},{"code":7058,"name":"RewardTopupAmountNotExpected","msg":"Reward topup is not as expected after transfer"},{"code":7059,"name":"RewardWithdrawAmountZero","msg":"Reward withdraw amount is zero"},{"code":7060,"name":"RewardWithdrawAmountNotExpected","msg":"Reward withdraw is not as expected after transfer"},{"code":7061,"name":"RewardsStaleForFeeUpdate","msg":"Rewards are stale - must be refreshed before updating fees"}]} \ No newline at end of file diff --git a/idls/layerzero_endpoint.76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6.json b/idls/layerzero_endpoint.76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6.json new file mode 100644 index 0000000..acbb23a --- /dev/null +++ b/idls/layerzero_endpoint.76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"endpoint","instructions":[{"name":"initEndpoint","docs":["--------------------------- Admin Instructions ---------------------------"],"accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"endpoint","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitEndpointParams"}}]},{"name":"transferAdmin","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"endpoint","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"TransferAdminParams"}}]},{"name":"setLzToken","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"endpoint","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetLzTokenParams"}}]},{"name":"registerLibrary","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"endpoint","isMut":false,"isSigner":false},{"name":"messageLibInfo","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"RegisterLibraryParams"}}]},{"name":"initDefaultSendLibrary","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"endpoint","isMut":false,"isSigner":false},{"name":"defaultSendLibraryConfig","isMut":true,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitDefaultSendLibraryParams"}}]},{"name":"setDefaultSendLibrary","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"endpoint","isMut":false,"isSigner":false},{"name":"defaultSendLibraryConfig","isMut":true,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetDefaultSendLibraryParams"}}]},{"name":"initDefaultReceiveLibrary","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"endpoint","isMut":false,"isSigner":false},{"name":"defaultReceiveLibraryConfig","isMut":true,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitDefaultReceiveLibraryParams"}}]},{"name":"setDefaultReceiveLibrary","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"endpoint","isMut":false,"isSigner":false},{"name":"defaultReceiveLibraryConfig","isMut":true,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetDefaultReceiveLibraryParams"}}]},{"name":"setDefaultReceiveLibraryTimeout","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"endpoint","isMut":false,"isSigner":false},{"name":"defaultReceiveLibraryConfig","isMut":true,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetDefaultReceiveLibraryTimeoutParams"}}]},{"name":"withdrawRent","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"endpoint","isMut":true,"isSigner":false},{"name":"receiver","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"WithdrawRentParams"}}]},{"name":"registerOapp","docs":["--------------------------- OApp Instructions ---------------------------"],"accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"oapp","isMut":false,"isSigner":true,"docs":["The PDA of the OApp"]},{"name":"oappRegistry","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"RegisterOAppParams"}}]},{"name":"initNonce","accounts":[{"name":"delegate","isMut":true,"isSigner":true,"docs":["only the delegate can initialize the nonce accounts"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"nonce","isMut":true,"isSigner":false},{"name":"pendingInboundNonce","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitNonceParams"}}]},{"name":"initSendLibrary","accounts":[{"name":"delegate","isMut":true,"isSigner":true,"docs":["only the delegate can initialize the send_library_config"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"sendLibraryConfig","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitSendLibraryParams"}}]},{"name":"setSendLibrary","accounts":[{"name":"signer","isMut":false,"isSigner":true,"docs":["The PDA of the OApp or delegate"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"sendLibraryConfig","isMut":true,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false,"isOptional":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetSendLibraryParams"}}]},{"name":"initReceiveLibrary","accounts":[{"name":"delegate","isMut":true,"isSigner":true,"docs":["only the delegate can initialize the send_library_config"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"receiveLibraryConfig","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitReceiveLibraryParams"}}]},{"name":"setReceiveLibrary","accounts":[{"name":"signer","isMut":false,"isSigner":true,"docs":["The PDA of the OApp or delegate"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"receiveLibraryConfig","isMut":true,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false,"isOptional":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetReceiveLibraryParams"}}]},{"name":"setReceiveLibraryTimeout","accounts":[{"name":"signer","isMut":false,"isSigner":true,"docs":["The PDA of the OApp or delegate"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"receiveLibraryConfig","isMut":true,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetReceiveLibraryTimeoutParams"}}]},{"name":"initConfig","accounts":[{"name":"delegate","isMut":false,"isSigner":true,"docs":["only the delegate can initialize the config accounts"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false,"docs":["The PDA signer to the message lib when the endpoint calls the message lib program."]},{"name":"messageLib","isMut":false,"isSigner":false,"docs":["the pda of the message_lib_program"]},{"name":"messageLibProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitConfigParams"}}]},{"name":"setConfig","accounts":[{"name":"signer","isMut":false,"isSigner":true,"docs":["The PDA of the OApp or delegate"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false,"docs":["The PDA signer to the message lib when the endpoint calls the message lib program"]},{"name":"messageLib","isMut":false,"isSigner":false,"docs":["the pda of the message_lib_program"]},{"name":"messageLibProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetConfigParams"}}]},{"name":"quote","accounts":[{"name":"sendLibraryProgram","isMut":false,"isSigner":false},{"name":"sendLibraryConfig","isMut":false,"isSigner":false},{"name":"defaultSendLibraryConfig","isMut":false,"isSigner":false},{"name":"sendLibraryInfo","isMut":false,"isSigner":false,"docs":["The PDA signer to the send library when the endpoint calls the send library."]},{"name":"endpoint","isMut":false,"isSigner":false},{"name":"nonce","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"QuoteParams"}}],"returns":{"defined":"MessagingFee"}},{"name":"send","accounts":[{"name":"sender","isMut":false,"isSigner":true},{"name":"sendLibraryProgram","isMut":false,"isSigner":false},{"name":"sendLibraryConfig","isMut":false,"isSigner":false},{"name":"defaultSendLibraryConfig","isMut":false,"isSigner":false},{"name":"sendLibraryInfo","isMut":false,"isSigner":false,"docs":["The PDA signer to the send library when the endpoint calls the send library."]},{"name":"endpoint","isMut":false,"isSigner":false},{"name":"nonce","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SendParams"}}],"returns":{"defined":"MessagingReceipt"}},{"name":"initVerify","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"nonce","isMut":false,"isSigner":false},{"name":"payloadHash","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitVerifyParams"}}]},{"name":"verify","accounts":[{"name":"receiveLibrary","isMut":false,"isSigner":true,"docs":["The PDA of the receive library."]},{"name":"receiveLibraryConfig","isMut":false,"isSigner":false},{"name":"defaultReceiveLibraryConfig","isMut":false,"isSigner":false},{"name":"nonce","isMut":true,"isSigner":false},{"name":"pendingInboundNonce","isMut":true,"isSigner":false},{"name":"payloadHash","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"VerifyParams"}}]},{"name":"skip","accounts":[{"name":"signer","isMut":false,"isSigner":true,"docs":["The PDA of the OApp or delegate"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"nonce","isMut":true,"isSigner":false},{"name":"pendingInboundNonce","isMut":true,"isSigner":false},{"name":"payloadHash","isMut":true,"isSigner":false,"docs":["the payload hash needs to be initialized before it can be skipped and closed, in order to prevent someone","from skipping a payload hash that has been initialized and can be re-verified and executed after skipping"]},{"name":"endpoint","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SkipParams"}}]},{"name":"burn","accounts":[{"name":"signer","isMut":false,"isSigner":true,"docs":["The PDA of the OApp or delegate"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"nonce","isMut":false,"isSigner":false},{"name":"payloadHash","isMut":true,"isSigner":false,"docs":["close the account and return the lamports to endpoint settings account"]},{"name":"endpoint","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"BurnParams"}}]},{"name":"nilify","accounts":[{"name":"signer","isMut":false,"isSigner":true,"docs":["The PDA of the OApp or delegate"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"nonce","isMut":true,"isSigner":false},{"name":"pendingInboundNonce","isMut":true,"isSigner":false},{"name":"payloadHash","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"NilifyParams"}}]},{"name":"clear","accounts":[{"name":"signer","isMut":false,"isSigner":true,"docs":["The PDA of the OApp or delegate"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"nonce","isMut":false,"isSigner":false},{"name":"payloadHash","isMut":true,"isSigner":false,"docs":["close the account and return the lamports to endpoint settings account"]},{"name":"endpoint","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"ClearParams"}}],"returns":{"array":["u8",32]}},{"name":"sendCompose","accounts":[{"name":"from","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"composeMessage","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SendComposeParams"}}]},{"name":"clearCompose","accounts":[{"name":"to","isMut":false,"isSigner":true},{"name":"composeMessage","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"ClearComposeParams"}}]},{"name":"setDelegate","accounts":[{"name":"oapp","isMut":false,"isSigner":true,"docs":["The PDA of the OApp"]},{"name":"oappRegistry","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetDelegateParams"}}]},{"name":"lzReceiveAlert","accounts":[{"name":"executor","isMut":false,"isSigner":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"LzReceiveAlertParams"}}]},{"name":"lzComposeAlert","accounts":[{"name":"executor","isMut":false,"isSigner":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"LzComposeAlertParams"}}]}],"accounts":[{"name":"ComposeMessageState","type":{"kind":"struct","fields":[{"name":"received","type":"bool"},{"name":"bump","type":"u8"}]}},{"name":"EndpointSettings","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"bump","type":"u8"},{"name":"admin","type":"publicKey"},{"name":"lzTokenMint","type":{"option":"publicKey"}}]}},{"name":"OAppRegistry","type":{"kind":"struct","fields":[{"name":"delegate","type":"publicKey"},{"name":"bump","type":"u8"}]}},{"name":"MessageLibInfo","type":{"kind":"struct","fields":[{"name":"messageLibType","type":{"defined":"MessageLibType"}},{"name":"bump","type":"u8"},{"name":"messageLibBump","type":"u8"}]}},{"name":"ReceiveLibraryConfig","type":{"kind":"struct","fields":[{"name":"messageLib","type":"publicKey"},{"name":"timeout","type":{"option":{"defined":"ReceiveLibraryTimeout"}}},{"name":"bump","type":"u8"}]}},{"name":"SendLibraryConfig","docs":["the reason for not using Option::None to indicate default is to respect the spec on evm"],"type":{"kind":"struct","fields":[{"name":"messageLib","type":"publicKey"},{"name":"bump","type":"u8"}]}},{"name":"Nonce","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"outboundNonce","type":"u64"},{"name":"inboundNonce","type":"u64"}]}},{"name":"PayloadHash","type":{"kind":"struct","fields":[{"name":"hash","type":{"array":["u8",32]}},{"name":"bump","type":"u8"}]}},{"name":"PendingInboundNonce","type":{"kind":"struct","fields":[{"name":"nonces","type":{"vec":"u64"}},{"name":"bump","type":"u8"}]}}],"types":[{"name":"InitDefaultReceiveLibraryParams","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"}]}},{"name":"InitDefaultSendLibraryParams","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"}]}},{"name":"InitEndpointParams","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"admin","type":"publicKey"}]}},{"name":"RegisterLibraryParams","type":{"kind":"struct","fields":[{"name":"libProgram","type":"publicKey"},{"name":"libType","type":{"defined":"MessageLibType"}}]}},{"name":"SetDefaultReceiveLibraryParams","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"},{"name":"gracePeriod","type":"u64"}]}},{"name":"SetDefaultReceiveLibraryTimeoutParams","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"lib","type":"publicKey"},{"name":"expiry","type":"u64"}]}},{"name":"SetDefaultSendLibraryParams","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"}]}},{"name":"SetLzTokenParams","type":{"kind":"struct","fields":[{"name":"lzToken","type":{"option":"publicKey"}}]}},{"name":"TransferAdminParams","type":{"kind":"struct","fields":[{"name":"admin","type":"publicKey"}]}},{"name":"WithdrawRentParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"InitVerifyParams","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"receiver","type":"publicKey"},{"name":"nonce","type":"u64"}]}},{"name":"LzComposeAlertParams","type":{"kind":"struct","fields":[{"name":"from","type":"publicKey"},{"name":"to","type":"publicKey"},{"name":"guid","type":{"array":["u8",32]}},{"name":"index","type":"u16"},{"name":"computeUnits","type":"u64"},{"name":"value","type":"u64"},{"name":"message","type":"bytes"},{"name":"extraData","type":"bytes"},{"name":"reason","type":"bytes"}]}},{"name":"LzReceiveAlertParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"guid","type":{"array":["u8",32]}},{"name":"computeUnits","type":"u64"},{"name":"value","type":"u64"},{"name":"message","type":"bytes"},{"name":"extraData","type":"bytes"},{"name":"reason","type":"bytes"}]}},{"name":"BurnParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"payloadHash","type":{"array":["u8",32]}}]}},{"name":"ClearParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"guid","type":{"array":["u8",32]}},{"name":"message","type":"bytes"}]}},{"name":"ClearComposeParams","type":{"kind":"struct","fields":[{"name":"from","type":"publicKey"},{"name":"guid","type":{"array":["u8",32]}},{"name":"index","type":"u16"},{"name":"message","type":"bytes"}]}},{"name":"InitNonceParams","type":{"kind":"struct","fields":[{"name":"localOapp","type":"publicKey"},{"name":"remoteEid","type":"u32"},{"name":"remoteOapp","type":{"array":["u8",32]}}]}},{"name":"InitReceiveLibraryParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"eid","type":"u32"}]}},{"name":"InitSendLibraryParams","type":{"kind":"struct","fields":[{"name":"sender","type":"publicKey"},{"name":"eid","type":"u32"}]}},{"name":"NilifyParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"payloadHash","type":{"array":["u8",32]}}]}},{"name":"QuoteParams","type":{"kind":"struct","fields":[{"name":"sender","type":"publicKey"},{"name":"dstEid","type":"u32"},{"name":"receiver","type":{"array":["u8",32]}},{"name":"message","type":"bytes"},{"name":"options","type":"bytes"},{"name":"payInLzToken","type":"bool"}]}},{"name":"RegisterOAppParams","type":{"kind":"struct","fields":[{"name":"delegate","type":"publicKey"}]}},{"name":"SendParams","type":{"kind":"struct","fields":[{"name":"dstEid","type":"u32"},{"name":"receiver","type":{"array":["u8",32]}},{"name":"message","type":"bytes"},{"name":"options","type":"bytes"},{"name":"nativeFee","type":"u64"},{"name":"lzTokenFee","type":"u64"}]}},{"name":"SendComposeParams","type":{"kind":"struct","fields":[{"name":"to","type":"publicKey"},{"name":"guid","type":{"array":["u8",32]}},{"name":"index","type":"u16"},{"name":"message","type":"bytes"}]}},{"name":"SetDelegateParams","type":{"kind":"struct","fields":[{"name":"delegate","type":"publicKey"}]}},{"name":"SetReceiveLibraryParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"},{"name":"gracePeriod","type":"u64"}]}},{"name":"SetReceiveLibraryTimeoutParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"eid","type":"u32"},{"name":"lib","type":"publicKey"},{"name":"expiry","type":"u64"}]}},{"name":"SetSendLibraryParams","type":{"kind":"struct","fields":[{"name":"sender","type":"publicKey"},{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"}]}},{"name":"SkipParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"}]}},{"name":"VerifyParams","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"receiver","type":"publicKey"},{"name":"nonce","type":"u64"},{"name":"payloadHash","type":{"array":["u8",32]}}]}},{"name":"ReceiveLibraryTimeout","type":{"kind":"struct","fields":[{"name":"messageLib","type":"publicKey"},{"name":"expiry","type":"u64"}]}},{"name":"InitConfigParams","type":{"kind":"struct","fields":[{"name":"oapp","type":"publicKey"},{"name":"eid","type":"u32"}]}},{"name":"MessageLibType","type":{"kind":"enum","variants":[{"name":"Send"},{"name":"Receive"},{"name":"SendAndReceive"}]}},{"name":"MessagingFee","type":{"kind":"struct","fields":[{"name":"nativeFee","type":"u64"},{"name":"lzTokenFee","type":"u64"}]}},{"name":"MessagingReceipt","type":{"kind":"struct","fields":[{"name":"guid","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"fee","type":{"defined":"MessagingFee"}}]}},{"name":"SetConfigParams","type":{"kind":"struct","fields":[{"name":"oapp","type":"publicKey"},{"name":"eid","type":"u32"},{"name":"configType","type":"u32"},{"name":"config","type":"bytes"}]}},{"name":"AdminTransferredEvent","type":{"kind":"struct","fields":[{"name":"newAdmin","type":"publicKey"}]}},{"name":"ComposeDeliveredEvent","type":{"kind":"struct","fields":[{"name":"from","type":"publicKey"},{"name":"to","type":"publicKey"},{"name":"guid","type":{"array":["u8",32]}},{"name":"index","type":"u16"}]}},{"name":"ComposeSentEvent","type":{"kind":"struct","fields":[{"name":"from","type":"publicKey"},{"name":"to","type":"publicKey"},{"name":"guid","type":{"array":["u8",32]}},{"name":"index","type":"u16"},{"name":"message","type":"bytes"}]}},{"name":"DefaultReceiveLibrarySetEvent","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"}]}},{"name":"DefaultReceiveLibraryTimeoutSetEvent","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"timeout","type":{"option":{"defined":"ReceiveLibraryTimeout"}}}]}},{"name":"DefaultSendLibrarySetEvent","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"}]}},{"name":"DelegateSetEvent","type":{"kind":"struct","fields":[{"name":"newDelegate","type":"publicKey"}]}},{"name":"InboundNonceSkippedEvent","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"receiver","type":"publicKey"},{"name":"nonce","type":"u64"}]}},{"name":"LibraryRegisteredEvent","type":{"kind":"struct","fields":[{"name":"newLib","type":"publicKey"},{"name":"newLibProgram","type":"publicKey"}]}},{"name":"LzComposeAlertEvent","type":{"kind":"struct","fields":[{"name":"executor","type":"publicKey"},{"name":"from","type":"publicKey"},{"name":"to","type":"publicKey"},{"name":"guid","type":{"array":["u8",32]}},{"name":"index","type":"u16"},{"name":"computeUnits","type":"u64"},{"name":"value","type":"u64"},{"name":"message","type":"bytes"},{"name":"extraData","type":"bytes"},{"name":"reason","type":"bytes"}]}},{"name":"LzReceiveAlertEvent","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"executor","type":"publicKey"},{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"guid","type":{"array":["u8",32]}},{"name":"computeUnits","type":"u64"},{"name":"value","type":"u64"},{"name":"message","type":"bytes"},{"name":"extraData","type":"bytes"},{"name":"reason","type":"bytes"}]}},{"name":"LzTokenSetEvent","type":{"kind":"struct","fields":[{"name":"token","type":{"option":"publicKey"}}]}},{"name":"OAppRegisteredEvent","type":{"kind":"struct","fields":[{"name":"oapp","type":"publicKey"},{"name":"delegate","type":"publicKey"}]}},{"name":"PacketBurntEvent","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"receiver","type":"publicKey"},{"name":"nonce","type":"u64"},{"name":"payloadHash","type":{"array":["u8",32]}}]}},{"name":"PacketDeliveredEvent","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"receiver","type":"publicKey"},{"name":"nonce","type":"u64"}]}},{"name":"PacketNilifiedEvent","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"receiver","type":"publicKey"},{"name":"nonce","type":"u64"},{"name":"payloadHash","type":{"array":["u8",32]}}]}},{"name":"PacketSentEvent","type":{"kind":"struct","fields":[{"name":"encodedPacket","type":"bytes"},{"name":"options","type":"bytes"},{"name":"sendLibrary","type":"publicKey"}]}},{"name":"PacketVerifiedEvent","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"receiver","type":"publicKey"},{"name":"nonce","type":"u64"},{"name":"payloadHash","type":{"array":["u8",32]}}]}},{"name":"ReceiveLibrarySetEvent","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"}]}},{"name":"ReceiveLibraryTimeoutSetEvent","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"eid","type":"u32"},{"name":"timeout","type":{"option":{"defined":"ReceiveLibraryTimeout"}}}]}},{"name":"RentWithdrawnEvent","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"amount","type":"u64"}]}},{"name":"SendLibrarySetEvent","type":{"kind":"struct","fields":[{"name":"sender","type":"publicKey"},{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"}]}}],"events":[{"name":"AdminTransferredEvent","fields":[{"name":"newAdmin","type":"publicKey","index":false}]},{"name":"ComposeDeliveredEvent","fields":[{"name":"from","type":"publicKey","index":false},{"name":"to","type":"publicKey","index":false},{"name":"guid","type":{"array":["u8",32]},"index":false},{"name":"index","type":"u16","index":false}]},{"name":"ComposeSentEvent","fields":[{"name":"from","type":"publicKey","index":false},{"name":"to","type":"publicKey","index":false},{"name":"guid","type":{"array":["u8",32]},"index":false},{"name":"index","type":"u16","index":false},{"name":"message","type":"bytes","index":false}]},{"name":"DefaultReceiveLibrarySetEvent","fields":[{"name":"eid","type":"u32","index":false},{"name":"newLib","type":"publicKey","index":false}]},{"name":"DefaultReceiveLibraryTimeoutSetEvent","fields":[{"name":"eid","type":"u32","index":false},{"name":"timeout","type":{"option":{"defined":"ReceiveLibraryTimeout"}},"index":false}]},{"name":"DefaultSendLibrarySetEvent","fields":[{"name":"eid","type":"u32","index":false},{"name":"newLib","type":"publicKey","index":false}]},{"name":"DelegateSetEvent","fields":[{"name":"newDelegate","type":"publicKey","index":false}]},{"name":"InboundNonceSkippedEvent","fields":[{"name":"srcEid","type":"u32","index":false},{"name":"sender","type":{"array":["u8",32]},"index":false},{"name":"receiver","type":"publicKey","index":false},{"name":"nonce","type":"u64","index":false}]},{"name":"LibraryRegisteredEvent","fields":[{"name":"newLib","type":"publicKey","index":false},{"name":"newLibProgram","type":"publicKey","index":false}]},{"name":"LzComposeAlertEvent","fields":[{"name":"executor","type":"publicKey","index":false},{"name":"from","type":"publicKey","index":false},{"name":"to","type":"publicKey","index":false},{"name":"guid","type":{"array":["u8",32]},"index":false},{"name":"index","type":"u16","index":false},{"name":"computeUnits","type":"u64","index":false},{"name":"value","type":"u64","index":false},{"name":"message","type":"bytes","index":false},{"name":"extraData","type":"bytes","index":false},{"name":"reason","type":"bytes","index":false}]},{"name":"LzReceiveAlertEvent","fields":[{"name":"receiver","type":"publicKey","index":false},{"name":"executor","type":"publicKey","index":false},{"name":"srcEid","type":"u32","index":false},{"name":"sender","type":{"array":["u8",32]},"index":false},{"name":"nonce","type":"u64","index":false},{"name":"guid","type":{"array":["u8",32]},"index":false},{"name":"computeUnits","type":"u64","index":false},{"name":"value","type":"u64","index":false},{"name":"message","type":"bytes","index":false},{"name":"extraData","type":"bytes","index":false},{"name":"reason","type":"bytes","index":false}]},{"name":"LzTokenSetEvent","fields":[{"name":"token","type":{"option":"publicKey"},"index":false}]},{"name":"OAppRegisteredEvent","fields":[{"name":"oapp","type":"publicKey","index":false},{"name":"delegate","type":"publicKey","index":false}]},{"name":"PacketBurntEvent","fields":[{"name":"srcEid","type":"u32","index":false},{"name":"sender","type":{"array":["u8",32]},"index":false},{"name":"receiver","type":"publicKey","index":false},{"name":"nonce","type":"u64","index":false},{"name":"payloadHash","type":{"array":["u8",32]},"index":false}]},{"name":"PacketDeliveredEvent","fields":[{"name":"srcEid","type":"u32","index":false},{"name":"sender","type":{"array":["u8",32]},"index":false},{"name":"receiver","type":"publicKey","index":false},{"name":"nonce","type":"u64","index":false}]},{"name":"PacketNilifiedEvent","fields":[{"name":"srcEid","type":"u32","index":false},{"name":"sender","type":{"array":["u8",32]},"index":false},{"name":"receiver","type":"publicKey","index":false},{"name":"nonce","type":"u64","index":false},{"name":"payloadHash","type":{"array":["u8",32]},"index":false}]},{"name":"PacketSentEvent","fields":[{"name":"encodedPacket","type":"bytes","index":false},{"name":"options","type":"bytes","index":false},{"name":"sendLibrary","type":"publicKey","index":false}]},{"name":"PacketVerifiedEvent","fields":[{"name":"srcEid","type":"u32","index":false},{"name":"sender","type":{"array":["u8",32]},"index":false},{"name":"receiver","type":"publicKey","index":false},{"name":"nonce","type":"u64","index":false},{"name":"payloadHash","type":{"array":["u8",32]},"index":false}]},{"name":"ReceiveLibrarySetEvent","fields":[{"name":"receiver","type":"publicKey","index":false},{"name":"eid","type":"u32","index":false},{"name":"newLib","type":"publicKey","index":false}]},{"name":"ReceiveLibraryTimeoutSetEvent","fields":[{"name":"receiver","type":"publicKey","index":false},{"name":"eid","type":"u32","index":false},{"name":"timeout","type":{"option":{"defined":"ReceiveLibraryTimeout"}},"index":false}]},{"name":"RentWithdrawnEvent","fields":[{"name":"receiver","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]},{"name":"SendLibrarySetEvent","fields":[{"name":"sender","type":"publicKey","index":false},{"name":"eid","type":"u32","index":false},{"name":"newLib","type":"publicKey","index":false}]}],"errors":[{"code":6000,"name":"InvalidSendLibrary"},{"code":6001,"name":"InvalidReceiveLibrary"},{"code":6002,"name":"SameValue"},{"code":6003,"name":"AccountNotFound"},{"code":6004,"name":"OnlySendLib"},{"code":6005,"name":"OnlyReceiveLib"},{"code":6006,"name":"InvalidExpiry"},{"code":6007,"name":"OnlyNonDefaultLib"},{"code":6008,"name":"InvalidAmount"},{"code":6009,"name":"InvalidNonce"},{"code":6010,"name":"Unauthorized"},{"code":6011,"name":"PayloadHashNotFound"},{"code":6012,"name":"ComposeNotFound"},{"code":6013,"name":"InvalidPayloadHash"},{"code":6014,"name":"LzTokenUnavailable"},{"code":6015,"name":"ReadOnlyAccount"},{"code":6016,"name":"InvalidMessageLib"},{"code":6017,"name":"WritableAccountNotAllowed"}]} \ No newline at end of file diff --git a/idls/layerzero_executor.6doghB248px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn.json b/idls/layerzero_executor.6doghB248px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn.json new file mode 100644 index 0000000..02f889a --- /dev/null +++ b/idls/layerzero_executor.6doghB248px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"executor","instructions":[{"name":"initExecutor","docs":["--------------------------- Owner Instructions ---------------------------"],"accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"config","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitExecutorParams"}}]},{"name":"ownerSetConfig","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"config","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"OwnerSetConfigParams"}}]},{"name":"adminSetConfig","docs":["--------------------------- Admin Instructions ---------------------------"],"accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"config","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"AdminSetConfigParams"}}]},{"name":"nativeDrop","accounts":[{"name":"executor","isMut":true,"isSigner":true},{"name":"config","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["For native drop transfer"]},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"NativeDropParams"}}]},{"name":"execute","accounts":[{"name":"executor","isMut":true,"isSigner":true},{"name":"config","isMut":false,"isSigner":false},{"name":"endpointProgram","isMut":false,"isSigner":false},{"name":"endpointEventAuthority","isMut":false,"isSigner":false,"docs":["The authority for the endpoint program to emit events"]},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"ExecuteParams"}}]},{"name":"compose","accounts":[{"name":"executor","isMut":true,"isSigner":true},{"name":"config","isMut":false,"isSigner":false},{"name":"endpointProgram","isMut":false,"isSigner":false},{"name":"endpointEventAuthority","isMut":false,"isSigner":false,"docs":["The authority for the endpoint program to emit events"]},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"ComposeParams"}}]},{"name":"quoteExecutor","docs":["--------------------------- MsgLib Instructions ---------------------------"],"accounts":[{"name":"executorConfig","isMut":false,"isSigner":false},{"name":"priceFeedProgram","isMut":false,"isSigner":false},{"name":"priceFeedConfig","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"QuoteExecutorParams"}}],"returns":"u64"},{"name":"executable","accounts":[{"name":"nonce","isMut":false,"isSigner":false},{"name":"payloadHash","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"ExecutableParams"}}],"returns":{"defined":"ExecutionState"}}],"accounts":[{"name":"Nonce","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"outboundNonce","type":"u64"},{"name":"inboundNonce","type":"u64"}]}},{"name":"ExecutorConfig","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"owner","type":"publicKey"},{"name":"acl","type":{"defined":"Acl"}},{"name":"admins","type":{"vec":"publicKey"}},{"name":"executors","type":{"vec":"publicKey"}},{"name":"msglibs","type":{"vec":"publicKey"}},{"name":"paused","type":"bool"},{"name":"defaultMultiplierBps","type":"u16"},{"name":"priceFeed","type":"publicKey"},{"name":"dstConfigs","type":{"vec":{"defined":"DstConfig"}}}]}}],"types":[{"name":"AdminSetConfigParams","type":{"kind":"enum","variants":[{"name":"PriceFeed","fields":["publicKey"]},{"name":"DefaultMultiplierBps","fields":["u16"]},{"name":"DstConfigs","fields":[{"vec":{"defined":"DstConfig"}}]}]}},{"name":"ComposeParams","type":{"kind":"struct","fields":[{"name":"lzCompose","type":{"defined":"LzComposeParams"}},{"name":"computeUnits","type":"u64"},{"name":"value","type":"u64"}]}},{"name":"ExecutableParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"}]}},{"name":"ExecutionState","type":{"kind":"enum","variants":[{"name":"NotExecutable"},{"name":"VerifiedButNotExecutable"},{"name":"Executable"},{"name":"Executed"}]}},{"name":"ExecuteParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"lzReceive","type":{"defined":"LzReceiveParams"}},{"name":"value","type":"u64"},{"name":"computeUnits","type":"u64"}]}},{"name":"InitExecutorParams","type":{"kind":"struct","fields":[{"name":"owner","type":"publicKey"},{"name":"admins","type":{"vec":"publicKey"}},{"name":"executors","type":{"vec":"publicKey"}},{"name":"msglibs","type":{"vec":"publicKey"}},{"name":"priceFeed","type":"publicKey"}]}},{"name":"NativeDropParams","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"dstEid","type":"u32"},{"name":"oapp","type":"publicKey"},{"name":"nativeDropRequests","type":{"vec":{"defined":"NativeDropRequest"}}}]}},{"name":"NativeDropRequest","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"amount","type":"u64"}]}},{"name":"OwnerSetConfigParams","type":{"kind":"enum","variants":[{"name":"Admins","fields":[{"vec":"publicKey"}]},{"name":"Executors","fields":[{"vec":"publicKey"}]},{"name":"Msglibs","fields":[{"vec":"publicKey"}]},{"name":"Owner","fields":["publicKey"]},{"name":"Paused","fields":["bool"]},{"name":"Allowlist","fields":[{"vec":"publicKey"}]},{"name":"Denylist","fields":[{"vec":"publicKey"}]}]}},{"name":"DstConfig","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"lzReceiveBaseGas","type":"u32"},{"name":"lzComposeBaseGas","type":"u32"},{"name":"multiplierBps","type":{"option":"u16"}},{"name":"floorMarginUsd","type":{"option":"u128"}},{"name":"nativeDropCap","type":"u128"}]}},{"name":"LzComposeParams","type":{"kind":"struct","fields":[{"name":"from","type":"publicKey"},{"name":"to","type":"publicKey"},{"name":"guid","type":{"array":["u8",32]}},{"name":"index","type":"u16"},{"name":"message","type":"bytes"},{"name":"extraData","type":"bytes"}]}},{"name":"LzReceiveParams","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"guid","type":{"array":["u8",32]}},{"name":"message","type":"bytes"},{"name":"extraData","type":"bytes"}]}},{"name":"LzOption","type":{"kind":"struct","fields":[{"name":"optionType","type":"u8"},{"name":"params","type":"bytes"}]}},{"name":"QuoteExecutorParams","type":{"kind":"struct","fields":[{"name":"msglib","type":"publicKey"},{"name":"dstEid","type":"u32"},{"name":"sender","type":"publicKey"},{"name":"calldataSize","type":"u64"},{"name":"options","type":{"vec":{"defined":"LzOption"}}}]}},{"name":"Acl","type":{"kind":"struct","fields":[{"name":"allowList","type":{"vec":"publicKey"}},{"name":"denyList","type":{"vec":"publicKey"}}]}},{"name":"NativeDropAppliedEvent","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"dstEid","type":"u32"},{"name":"oapp","type":"publicKey"},{"name":"nativeDropRequests","type":{"vec":{"defined":"NativeDropRequest"}}},{"name":"successes","type":{"vec":"bool"}}]}}],"events":[{"name":"NativeDropAppliedEvent","fields":[{"name":"srcEid","type":"u32","index":false},{"name":"sender","type":{"array":["u8",32]},"index":false},{"name":"nonce","type":"u64","index":false},{"name":"dstEid","type":"u32","index":false},{"name":"oapp","type":"publicKey","index":false},{"name":"nativeDropRequests","type":{"vec":{"defined":"NativeDropRequest"}},"index":false},{"name":"successes","type":{"vec":"bool"},"index":false}]}],"errors":[{"code":6000,"name":"InvalidSize"},{"code":6001,"name":"Paused"},{"code":6002,"name":"UnsupportedOptionType"},{"code":6003,"name":"ZeroLzComposeGasProvided"},{"code":6004,"name":"ZeroLzReceiveGasProvided"},{"code":6005,"name":"NativeAmountExceedsCap"},{"code":6006,"name":"NotAdmin"},{"code":6007,"name":"NotExecutor"},{"code":6008,"name":"MsgLibNotAllowed"},{"code":6009,"name":"TooManyAdmins"},{"code":6010,"name":"TooManyExecutors"},{"code":6011,"name":"TooManyOptionTypes"},{"code":6012,"name":"InvalidNativeDropRequestsLength"},{"code":6013,"name":"InvalidNativeDropReceiver"},{"code":6014,"name":"InsufficientBalance"},{"code":6015,"name":"EidNotSupported"},{"code":6016,"name":"ExecutorIsAdmin"},{"code":6017,"name":"InvalidOwner"}]} \ No newline at end of file diff --git a/idls/lifinity_amm_v2.2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c.json b/idls/lifinity_amm_v2.2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c.json new file mode 100644 index 0000000..9d181e7 --- /dev/null +++ b/idls/lifinity_amm_v2.2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c.json @@ -0,0 +1 @@ +{"version":"0.1.1","name":"lifinity_amm_v2","instructions":[{"name":"swap","accounts":[{"name":"authority","isMut":false,"isSigner":false},{"name":"amm","isMut":true,"isSigner":false},{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"sourceInfo","isMut":true,"isSigner":false},{"name":"destinationInfo","isMut":true,"isSigner":false},{"name":"swapSource","isMut":true,"isSigner":false},{"name":"swapDestination","isMut":true,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"feeAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"oracleMainAccount","isMut":false,"isSigner":false},{"name":"oracleSubAccount","isMut":false,"isSigner":false},{"name":"oraclePcAccount","isMut":false,"isSigner":false}],"args":[{"name":"amountIn","type":"u64"},{"name":"minimumAmountOut","type":"u64"}]},{"name":"depositAllTokenTypes","accounts":[{"name":"amm","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"userTransferAuthorityInfo","isMut":false,"isSigner":true},{"name":"sourceAInfo","isMut":true,"isSigner":false},{"name":"sourceBInfo","isMut":true,"isSigner":false},{"name":"tokenA","isMut":true,"isSigner":false},{"name":"tokenB","isMut":true,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"destination","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"poolTokenAmount","type":"u64"},{"name":"maximumTokenAAmount","type":"u64"},{"name":"maximumTokenBAmount","type":"u64"}]},{"name":"withdrawAllTokenTypes","accounts":[{"name":"amm","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"userTransferAuthorityInfo","isMut":false,"isSigner":true},{"name":"sourceInfo","isMut":true,"isSigner":false},{"name":"tokenA","isMut":true,"isSigner":false},{"name":"tokenB","isMut":true,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"destTokenAInfo","isMut":true,"isSigner":false},{"name":"destTokenBInfo","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"poolTokenAmount","type":"u64"},{"name":"minimumTokenAAmount","type":"u64"},{"name":"minimumTokenBAmount","type":"u64"}]}],"accounts":[{"name":"Amm","type":{"kind":"struct","fields":[{"name":"initializerKey","type":"publicKey"},{"name":"initializerDepositTokenAccount","type":"publicKey"},{"name":"initializerReceiveTokenAccount","type":"publicKey"},{"name":"initializerAmount","type":"u64"},{"name":"takerAmount","type":"u64"},{"name":"isInitialized","docs":["Is the swap initialized, with data written to it"],"type":"bool"},{"name":"bumpSeed","docs":["Bump seed used to generate the program address / authority"],"type":"u8"},{"name":"freezeTrade","docs":["Freeze swap"],"type":"u8"},{"name":"freezeDeposit","docs":["Freeze deposit"],"type":"u8"},{"name":"freezeWithdraw","docs":["Freeze withdraw"],"type":"u8"},{"name":"baseDecimals","docs":["Base decimals account"],"type":"u8"},{"name":"tokenProgramId","docs":["Token program ID associated with the swap"],"type":"publicKey"},{"name":"tokenAAccount","docs":["Address of token A liquidity account"],"type":"publicKey"},{"name":"tokenBAccount","docs":["Address of token B liquidity account"],"type":"publicKey"},{"name":"poolMint","docs":["Address of pool token mint"],"type":"publicKey"},{"name":"tokenAMint","docs":["Address of token A mint"],"type":"publicKey"},{"name":"tokenBMint","docs":["Address of token B mint"],"type":"publicKey"},{"name":"feeAccount","docs":["Address of pool fee account"],"type":"publicKey"},{"name":"oracleMainAccount","docs":["Oracle Main account"],"type":"publicKey"},{"name":"oracleSubAccount","docs":["Oracle Sub account"],"type":"publicKey"},{"name":"oraclePcAccount","docs":["oracle Pc account"],"type":"publicKey"},{"name":"fees","docs":["Fees associated with swap"],"type":{"defined":"AmmFees"}},{"name":"curve","docs":["Curve associated with swap"],"type":{"defined":"AmmCurve"}},{"name":"config","docs":["Curve associated with swap"],"type":{"defined":"AmmConfig"}},{"name":"ammPTemp1","docs":["Temp area"],"type":"publicKey"},{"name":"ammPTemp2","docs":["Temp area"],"type":"publicKey"},{"name":"ammPTemp3","docs":["Temp area"],"type":"publicKey"},{"name":"ammPTemp4","docs":["Temp area"],"type":"publicKey"},{"name":"ammPTemp5","docs":["Temp area"],"type":"publicKey"}]}}],"types":[{"name":"AmmFees","docs":["Encapsulates all fee information and calculations for swap operations"],"type":{"kind":"struct","fields":[{"name":"tradeFeeNumerator","type":"u64"},{"name":"tradeFeeDenominator","type":"u64"},{"name":"ownerTradeFeeNumerator","type":"u64"},{"name":"ownerTradeFeeDenominator","type":"u64"},{"name":"ownerWithdrawFeeNumerator","type":"u64"},{"name":"ownerWithdrawFeeDenominator","type":"u64"},{"name":"hostFeeNumerator","type":"u64"},{"name":"hostFeeDenominator","type":"u64"}]}},{"name":"AmmCurve","type":{"kind":"struct","fields":[{"name":"curveType","type":"u8"},{"name":"curveParameters","type":"u64"}]}},{"name":"AmmConfig","type":{"kind":"struct","fields":[{"name":"lastPrice","type":"u64"},{"name":"lastBalancedPrice","type":"u64"},{"name":"configDenominator","type":"u64"},{"name":"volumeX","type":"u64"},{"name":"volumeY","type":"u64"},{"name":"volumeXInY","type":"u64"},{"name":"depositCap","type":"u64"},{"name":"regressionTarget","type":"u64"},{"name":"oracleType","type":"u64"},{"name":"oracleStatus","type":"u64"},{"name":"oracleMainSlotLimit","type":"u64"},{"name":"oracleSubConfidenceLimit","type":"u64"},{"name":"oracleSubSlotLimit","type":"u64"},{"name":"oraclePcConfidenceLimit","type":"u64"},{"name":"oraclePcSlotLimit","type":"u64"},{"name":"stdSpread","type":"u64"},{"name":"stdSpreadBuffer","type":"u64"},{"name":"spreadCoefficient","type":"u64"},{"name":"priceBufferCoin","type":"i64"},{"name":"priceBufferPc","type":"i64"},{"name":"rebalanceRatio","type":"u64"},{"name":"feeTrade","type":"u64"},{"name":"feePlatform","type":"u64"},{"name":"oracleMainSlotBuffer","type":"u64"},{"name":"configTemp4","type":"u64"},{"name":"configTemp5","type":"u64"},{"name":"configTemp6","type":"u64"},{"name":"configTemp7","type":"u64"},{"name":"configTemp8","type":"u64"}]}},{"name":"CurveType","docs":["Curve types supported by the token-swap program."],"type":{"kind":"enum","variants":[{"name":"Standard"},{"name":"ConstantProduct"}]}}],"errors":[{"code":6000,"name":"AlreadyInUse","msg":"Swap account already in use"},{"code":6001,"name":"InvalidProgramAddress","msg":"Invalid program address generated from bump seed and key"},{"code":6002,"name":"InvalidOwner","msg":"Input account owner is not the program address"},{"code":6003,"name":"InvalidOutputOwner","msg":"Output pool account owner cannot be the program address"},{"code":6004,"name":"ExpectedMint","msg":"Deserialized account is not an SPL Token mint"},{"code":6005,"name":"ExpectedAccount","msg":"Deserialized account is not an SPL Token account"},{"code":6006,"name":"EmptySupply","msg":"Input token account empty"},{"code":6007,"name":"InvalidSupply","msg":"Pool token mint has a non-zero supply"},{"code":6008,"name":"InvalidDelegate","msg":"Token account has a delegate"},{"code":6009,"name":"InvalidInput","msg":"InvalidInput"},{"code":6010,"name":"IncorrectSwapAccount","msg":"Address of the provided swap token account is incorrect"},{"code":6011,"name":"IncorrectPoolMint","msg":"Address of the provided pool token mint is incorrect"},{"code":6012,"name":"InvalidOutput","msg":"InvalidOutput"},{"code":6013,"name":"CalculationFailure","msg":"General calculation failure due to overflow or underflow"},{"code":6014,"name":"InvalidInstruction","msg":"Invalid instruction"},{"code":6015,"name":"RepeatedMint","msg":"Swap input token accounts have the same mint"},{"code":6016,"name":"ExceededSlippage","msg":"Swap instruction exceeds desired slippage limit"},{"code":6017,"name":"InvalidCloseAuthority","msg":"Token account has a close authority"},{"code":6018,"name":"InvalidFreezeAuthority","msg":"Pool token mint has a freeze authority"},{"code":6019,"name":"IncorrectFeeAccount","msg":"Pool fee token account incorrect"},{"code":6020,"name":"ZeroTradingTokens","msg":"Given pool token amount results in zero trading tokens"},{"code":6021,"name":"FeeCalculationFailure","msg":"Fee calculation failed due to overflow, underflow, or unexpected 0"},{"code":6022,"name":"ConversionFailure","msg":"Conversion to u64 failed with an overflow or underflow"},{"code":6023,"name":"InvalidFee","msg":"The provided fee does not match the program owner's constraints"},{"code":6024,"name":"IncorrectTokenProgramId","msg":"The provided token program does not match the token program expected by the swap"},{"code":6025,"name":"IncorrectOracleAccount","msg":"Address of the provided oracle account is incorrect"},{"code":6026,"name":"IncorrectConfigAccount","msg":"Address of the provided config account is incorrect"},{"code":6027,"name":"UnsupportedCurveType","msg":"The provided curve type is not supported by the program owner"},{"code":6028,"name":"InvalidCurve","msg":"The provided curve parameters are invalid"},{"code":6029,"name":"UnsupportedCurveOperation","msg":"The operation cannot be performed on the given curve"},{"code":6030,"name":"InvalidPythStatus","msg":"Pyth oracle status is not 'trading'"},{"code":6031,"name":"InvalidPythPrice","msg":"Could not retrieve updated price feed from the Pyth oracle"},{"code":6032,"name":"IncorrectSigner","msg":"Address of the provided signer account is incorrect"},{"code":6033,"name":"ExceedPoolBalance","msg":"Swap amount exceeds pool balance"},{"code":6034,"name":"ProgramIsFrozen","msg":"Program is frozen"},{"code":6035,"name":"OracleConfidence","msg":"Oracle confidence is too high"},{"code":6036,"name":"OverCapAmount","msg":"Over Pool Cap Amount"},{"code":6037,"name":"InvalidUpdateAccount","msg":"Invalid update wallet address"},{"code":6038,"name":"InvalidUpdateParam","msg":"Invalid update param"},{"code":6039,"name":"InvalidInnerSwapAccount","msg":"Invalid inner swap account"},{"code":6040,"name":"ExceptionErr","msg":"Exception err"}]} \ No newline at end of file diff --git a/idls/marginfi_v2.MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA.json b/idls/marginfi_v2.MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA.json new file mode 100644 index 0000000..c829799 --- /dev/null +++ b/idls/marginfi_v2.MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA.json @@ -0,0 +1 @@ +{"address":"MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA","metadata":{"name":"marginfi","version":"0.1.8","spec":"0.1.0","description":"Borrow Lending Prime Broker"},"instructions":[{"name":"config_group_fee","docs":["(global fee admin only) Enable or disable program fees for any group. Does not require the","group admin to sign: the global fee state admin can turn program fees on or off for any","group"],"discriminator":[231,205,66,242,220,87,145,38],"accounts":[{"name":"marginfi_group","writable":true},{"name":"global_fee_admin","docs":["`global_fee_admin` of the FeeState"],"signer":true,"relations":["fee_state"]},{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}}],"args":[{"name":"enable_program_fee","type":"bool"}]},{"name":"configure_bank_rate_limits","docs":["(admin or delegate_limit_admin) Configure bank-level rate limits for withdraw/borrow.","Rate limits track net outflow in native tokens. Deposits offset withdraws.","Set to 0 to disable. Hourly and daily windows are independent."],"discriminator":[175,84,85,221,206,220,110,174],"accounts":[{"name":"group","relations":["bank"]},{"name":"admin","signer":true},{"name":"bank","writable":true}],"args":[{"name":"hourly_max_outflow","type":{"option":"u64"}},{"name":"daily_max_outflow","type":{"option":"u64"}}]},{"name":"configure_deleverage_withdrawal_limit","docs":["(admin or delegate_limit_admin) Set the daily withdrawal limit for deleverages per group."],"discriminator":[28,132,205,158,67,77,177,63],"accounts":[{"name":"marginfi_group","writable":true},{"name":"admin","signer":true}],"args":[{"name":"limit","type":"u32"}]},{"name":"configure_group_rate_limits","docs":["(admin or delegate_limit_admin) Configure group-level rate limits for withdraw/borrow.","Rate limits track aggregate net outflow in USD.","Example: $10M = 10_000_000. Set to 0 to disable."],"discriminator":[111,47,213,142,158,51,226,102],"accounts":[{"name":"marginfi_group","writable":true},{"name":"admin","signer":true}],"args":[{"name":"hourly_max_outflow_usd","type":{"option":"u64"}},{"name":"daily_max_outflow_usd","type":{"option":"u64"}}]},{"name":"drift_deposit","docs":["(user) Deposit into a Drift spot market through a marginfi account","* amount - in the underlying token (e.g., USDC), in native decimals"],"discriminator":[252,63,250,201,98,55,130,12],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"drift_oracle","docs":["The oracle account for the asset (not needed if using oracle type QuoteAsset)"],"optional":true},{"name":"liquidity_vault_authority","docs":["The bank's liquidity vault authority, which owns the Drift user account"],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["Used as an intermediary to deposit tokens into Drift"],"writable":true,"relations":["bank"]},{"name":"signer_token_account","docs":["Owned by authority, the source account for the token deposit"],"writable":true},{"name":"drift_state","docs":["The Drift state account"]},{"name":"integration_acc_2","docs":["The Drift user account owned by liquidity_vault_authority"],"writable":true,"relations":["bank"]},{"name":"integration_acc_3","docs":["The Drift user stats account owned by liquidity_vault_authority"],"writable":true,"relations":["bank"]},{"name":"integration_acc_1","docs":["The Drift spot market for this asset"],"writable":true,"relations":["bank"]},{"name":"drift_spot_market_vault","docs":["The Drift spot market vault that will receive tokens"],"writable":true},{"name":"mint","docs":["Bank's liquidity token mint"],"relations":["bank"]},{"name":"drift_program","address":"dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH"},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"}]},{"name":"drift_harvest_reward","docs":["(permissionless) Harvest rewards from admin deposits in Drift spot markets.","Rewards are always sent to the global fee wallet's canonical ATA.","The harvest spot market must be different from the bank's main drift spot market."],"discriminator":[167,161,240,194,138,54,87,189],"accounts":[{"name":"bank"},{"name":"fee_state","docs":["Global fee state that contains the global_fee_wallet"],"pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"liquidity_vault_authority","docs":["The bank's liquidity vault authority"],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"intermediary_token_account","docs":["To create this manually just send some of the reward token","to the liquidity vault authority address before claiming"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"liquidity_vault_authority"},{"kind":"account","path":"token_program"},{"kind":"account","path":"reward_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"destination_token_account","docs":["Destination token account must be owned by the global fee wallet"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_state"},{"kind":"account","path":"token_program"},{"kind":"account","path":"reward_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"drift_state","docs":["Drift accounts"]},{"name":"integration_acc_2","writable":true,"relations":["bank"]},{"name":"integration_acc_3","writable":true,"relations":["bank"]},{"name":"harvest_drift_spot_market","docs":["The harvest spot market - MUST be different from bank's Drift spot market (integration_acc_1)","This is the market that contains admin deposits to harvest"],"writable":true},{"name":"harvest_drift_spot_market_vault","docs":["The harvest spot market vault - derived from harvest_drift_spot_market"],"writable":true},{"name":"drift_signer","docs":["The Drift signer PDA"]},{"name":"reward_mint"},{"name":"drift_program","address":"dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH"},{"name":"token_program"}],"args":[]},{"name":"drift_init_user","docs":["(permissionless) Initialize a Drift user and user stats for a marginfi bank","Creates user with sub_account_id = 0 and empty name","Requires a minimum deposit to ensure the account remains active","* amount - minimum deposit amount (at least 10 units) in native decimals"],"discriminator":[29,18,236,190,29,254,114,169],"accounts":[{"name":"fee_payer","docs":["Pays to init the drift user and user stats accounts and provides initial deposit"],"writable":true,"signer":true},{"name":"signer_token_account","docs":["The fee payer must provide a nominal amount of bank tokens so the account is not empty.","This amount is irrecoverable and will prevent the account from being closed."],"writable":true},{"name":"bank"},{"name":"liquidity_vault_authority","docs":["The liquidity vault authority (PDA that will own the Drift user)"],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["Used as an intermediary to deposit a nominal amount of token into Drift."],"writable":true,"relations":["bank"]},{"name":"mint","docs":["Bank's liquidity token mint (e.g., USDC)"],"writable":true,"relations":["bank"]},{"name":"integration_acc_3","docs":["The user stats account to be created"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,115,116,97,116,115]},{"kind":"account","path":"liquidity_vault_authority"}],"program":{"kind":"const","value":[9,84,219,190,158,201,96,201,138,122,41,63,226,19,54,150,111,225,128,209,81,174,75,129,121,86,31,137,133,74,83,246]}},"relations":["bank"]},{"name":"integration_acc_2","docs":["The user account to be created (sub_account_id = 0)"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114]},{"kind":"account","path":"liquidity_vault_authority"},{"kind":"const","value":[0,0]}],"program":{"kind":"const","value":[9,84,219,190,158,201,96,201,138,122,41,63,226,19,54,150,111,225,128,209,81,174,75,129,121,86,31,137,133,74,83,246]}},"relations":["bank"]},{"name":"drift_state","writable":true},{"name":"integration_acc_1","writable":true,"relations":["bank"]},{"name":"drift_spot_market_vault","docs":["The Drift spot market vault where tokens will be deposited"],"writable":true},{"name":"drift_oracle","docs":["Oracle for the asset (can be null for USDC/market 0)"],"optional":true},{"name":"drift_program","address":"dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH"},{"name":"token_program"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"}]},{"name":"drift_withdraw","docs":["(user) Withdraw from a Drift spot market through a marginfi account","* amount - in the underlying token (e.g., USDC), in native decimals","* withdraw_all - if true, withdraws entire position"],"discriminator":[86,59,186,123,183,181,234,137],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"drift_oracle","docs":["The oracle account for the asset (not needed if using oracle type QuoteAsset)"],"optional":true},{"name":"liquidity_vault_authority","docs":["The bank's liquidity vault authority, which owns the Drift user account"],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["Receives tokens from Drift withdrawal"],"writable":true,"relations":["bank"]},{"name":"destination_token_account","docs":["Token account that will receive the withdrawn tokens"],"writable":true},{"name":"drift_state","docs":["The Drift state account"]},{"name":"integration_acc_2","docs":["The Drift user account owned by liquidity_vault_authority"],"writable":true,"relations":["bank"]},{"name":"integration_acc_3","docs":["The Drift user stats account owned by liquidity_vault_authority"],"writable":true,"relations":["bank"]},{"name":"integration_acc_1","docs":["The Drift spot market for this asset"],"writable":true,"relations":["bank"]},{"name":"drift_spot_market_vault","docs":["The Drift spot market vault that holds tokens"],"writable":true},{"name":"drift_reward_oracle","docs":["Optional: Oracle for first reward asset (only needed if rewards exist)"],"optional":true},{"name":"drift_reward_spot_market","docs":["Optional: Spot market for first reward asset (only needed if rewards exist)"],"optional":true},{"name":"drift_reward_mint","docs":["Optional: Mint for first reward asset (only needed if rewards exist)"],"optional":true},{"name":"drift_reward_oracle_2","docs":["Optional: Oracle for second reward asset (backup in case multiple rewards)"],"optional":true},{"name":"drift_reward_spot_market_2","docs":["Optional: Spot market for second reward asset (backup in case multiple rewards)"],"optional":true},{"name":"drift_reward_mint_2","docs":["Optional: Mint for second reward asset (backup in case multiple rewards)"],"optional":true},{"name":"drift_signer","docs":["The Drift signer PDA"]},{"name":"mint","docs":["Bank's liquidity token mint"],"relations":["bank"]},{"name":"drift_program","address":"dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH"},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"},{"name":"withdraw_all","type":{"option":"bool"}}]},{"name":"edit_global_fee_state","docs":["(global fee admin only) Adjust fees, admin, or the destination wallet"],"discriminator":[52,62,35,129,93,69,165,202],"accounts":[{"name":"global_fee_admin","docs":["Admin of the global FeeState"],"signer":true,"relations":["fee_state"]},{"name":"fee_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}}],"args":[{"name":"admin","type":"pubkey"},{"name":"fee_wallet","type":"pubkey"},{"name":"bank_init_flat_sol_fee","type":"u32"},{"name":"liquidation_flat_sol_fee","type":"u32"},{"name":"order_init_flat_sol_fee","type":"u32"},{"name":"program_fee_fixed","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"program_fee_rate","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liquidation_max_fee","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"order_execution_max_fee","type":{"defined":{"name":"WrappedI80F48"}}}]},{"name":"edit_staked_settings","docs":["(admin only) Edit the staked collateral settings for the group."],"discriminator":[11,108,215,87,240,9,66,241],"accounts":[{"name":"marginfi_group","relations":["staked_settings"]},{"name":"admin","signer":true,"relations":["marginfi_group"]},{"name":"staked_settings","writable":true}],"args":[{"name":"settings","type":{"defined":{"name":"StakedSettingsEditConfig"}}}]},{"name":"end_deleverage","docs":["(risk_admin only) End forced deleverage. Validates health did not worsen."],"discriminator":[114,14,250,143,252,104,214,209],"accounts":[{"name":"marginfi_account","writable":true},{"name":"liquidation_record","writable":true,"relations":["marginfi_account"]},{"name":"group","relations":["marginfi_account"]},{"name":"risk_admin","signer":true,"relations":["group"]}],"args":[]},{"name":"end_liquidation","docs":["(liquidation_receiver, set in start_liquidation) End receivership liquidation. Validates","health improved and seized assets are within fee limits. Charges a flat SOL fee."],"discriminator":[110,11,244,54,229,181,22,184],"accounts":[{"name":"marginfi_account","docs":["Account under liquidation"],"writable":true},{"name":"liquidation_record","docs":["The associated liquidation record PDA for the given `marginfi_account`"],"writable":true,"relations":["marginfi_account"]},{"name":"liquidation_receiver","writable":true,"signer":true,"relations":["liquidation_record"]},{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"global_fee_wallet","writable":true,"relations":["fee_state"]},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"init_bank_metadata","docs":["(permissionless) pay the rent to open a bank's metadata."],"discriminator":[94,239,50,136,137,204,254,213],"accounts":[{"name":"bank"},{"name":"fee_payer","docs":["Pays the init fee"],"writable":true,"signer":true},{"name":"metadata","docs":["Note: unique per-bank."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"account","path":"bank"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"init_global_fee_state","docs":["(Runs once per program) Configures the fee state account, where the global admin sets fees","that are assessed to the protocol"],"discriminator":[82,48,247,59,220,109,231,44],"accounts":[{"name":"payer","docs":["Pays the init fee"],"writable":true,"signer":true},{"name":"fee_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"admin","type":"pubkey"},{"name":"fee_wallet","type":"pubkey"},{"name":"bank_init_flat_sol_fee","type":"u32"},{"name":"liquidation_flat_sol_fee","type":"u32"},{"name":"order_init_flat_sol_fee","type":"u32"},{"name":"program_fee_fixed","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"program_fee_rate","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liquidation_max_fee","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"order_execution_max_fee","type":{"defined":{"name":"WrappedI80F48"}}}]},{"name":"init_staked_settings","docs":["(group admin only) Init the Staked Settings account, which is used to create staked","collateral banks, and must run before any staked collateral bank can be created with","`add_pool_permissionless`. Running this ix effectively opts the group into the staked","collateral feature."],"discriminator":[52,35,149,44,69,86,69,80],"accounts":[{"name":"marginfi_group"},{"name":"admin","signer":true,"relations":["marginfi_group"]},{"name":"fee_payer","docs":["Pays the init fee"],"writable":true,"signer":true},{"name":"staked_settings","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,101,100,95,115,101,116,116,105,110,103,115]},{"kind":"account","path":"marginfi_group"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"settings","type":{"defined":{"name":"StakedSettingsConfig"}}}]},{"name":"juplend_deposit","docs":["(user) Deposit into a JupLend lending pool through a marginfi account.","* amount - in the underlying token (e.g., USDC), in native decimals"],"discriminator":[114,11,218,81,183,165,143,255],"accounts":[{"name":"group","writable":true,"relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"signer_token_account","docs":["Owned by authority, the source account for the token deposit."],"writable":true},{"name":"liquidity_vault_authority","docs":["The bank's liquidity vault authority PDA (acts as signer for JupLend CPIs).","NOTE: JupLend marks the signer as writable in their deposit instruction."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["Bank liquidity vault (holds underlying mint and is used as depositor_token_account)."],"writable":true,"relations":["bank"]},{"name":"mint","docs":["Underlying mint."],"relations":["bank"]},{"name":"integration_acc_1","docs":["JupLend lending state account."],"writable":true,"relations":["bank"]},{"name":"f_token_mint","docs":["JupLend fToken mint."],"writable":true,"relations":["integration_acc_1"]},{"name":"integration_acc_2","docs":["Bank's fToken vault (validated via has_one on bank)."],"writable":true,"relations":["bank"]},{"name":"lending_admin"},{"name":"supply_token_reserves_liquidity","writable":true},{"name":"lending_supply_position_on_liquidity","writable":true},{"name":"rate_model"},{"name":"vault","writable":true},{"name":"liquidity","writable":true},{"name":"liquidity_program"},{"name":"rewards_rate_model"},{"name":"juplend_program","address":"jup3YeL8QhtSx1e253b2FDvsMNC87fDrgQZivbrndc9"},{"name":"token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"}]},{"name":"juplend_init_position","docs":["(permissionless) Initialize the bank-level JupLend position.","","This creates the bank's fToken ATA (owned by the bank liquidity vault authority) and","performs a nominal seed deposit into JupLend, then flips the bank from `Paused` to","`Operational`."],"discriminator":[176,255,151,106,5,207,74,215],"accounts":[{"name":"fee_payer","docs":["Provides a nominal deposit amount."],"writable":true,"signer":true},{"name":"signer_token_account","docs":["Token account owned by the fee payer holding the underlying mint."],"writable":true},{"name":"bank","writable":true},{"name":"liquidity_vault_authority","docs":["The bank's liquidity vault authority PDA (acts as signer for JupLend CPIs)."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["Bank liquidity vault (holds underlying mint and is used as depositor_token_account)."],"writable":true,"relations":["bank"]},{"name":"mint","docs":["Underlying mint (must match bank mint and JupLend lending state mint)."],"relations":["bank"]},{"name":"integration_acc_1","docs":["JupLend lending state account."],"writable":true,"relations":["bank"]},{"name":"f_token_mint","docs":["JupLend fToken mint."],"writable":true,"relations":["integration_acc_1"]},{"name":"integration_acc_2","docs":["Bank's fToken vault (validated via has_one on bank)."],"writable":true,"relations":["bank"]},{"name":"lending_admin"},{"name":"supply_token_reserves_liquidity","writable":true},{"name":"lending_supply_position_on_liquidity","writable":true},{"name":"rate_model"},{"name":"vault","writable":true},{"name":"liquidity","writable":true},{"name":"liquidity_program"},{"name":"rewards_rate_model"},{"name":"juplend_program","address":"jup3YeL8QhtSx1e253b2FDvsMNC87fDrgQZivbrndc9"},{"name":"token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"}]},{"name":"juplend_withdraw","docs":["(user) Withdraw from a JupLend lending pool through a marginfi account.","* amount - in the underlying token (e.g., USDC), in native decimals"],"discriminator":[245,164,253,202,53,77,251,221],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"destination_token_account","docs":["Token account that will receive the underlying withdrawal.","WARN: Completely unchecked!"],"writable":true},{"name":"liquidity_vault_authority","docs":["The bank's liquidity vault authority PDA (acts as signer for JupLend CPIs).","NOTE: JupLend marks the signer as writable in their withdraw instruction."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"mint","docs":["Underlying mint."],"relations":["bank"]},{"name":"integration_acc_1","docs":["JupLend lending state account."],"writable":true,"relations":["bank"]},{"name":"f_token_mint","docs":["JupLend fToken mint."],"writable":true,"relations":["integration_acc_1"]},{"name":"integration_acc_2","docs":["Bank's fToken vault (validated via has_one on bank)."],"writable":true,"relations":["bank"]},{"name":"integration_acc_3","docs":["Withdraw intermediary ATA (authority = liquidity_vault_authority).","This must be an ATA to satisfy JupLend's withdraw constraints."],"writable":true,"relations":["bank"]},{"name":"lending_admin"},{"name":"supply_token_reserves_liquidity","writable":true},{"name":"lending_supply_position_on_liquidity","writable":true},{"name":"rate_model"},{"name":"vault","writable":true},{"name":"claim_account","docs":["JupLend claim account for liquidity_vault_authority.","TEMPORARY: Mainnet currently requires this account (passing None causes ConstraintMut errors),","but an upcoming upgrade is expected to make it truly optional. The account is never actually","validated or used - you can pass any mutable account. We create the canonical PDA for consistency.","Seeds: [\"user_claim\", liquidity_vault_authority, mint] on Liquidity program."],"writable":true},{"name":"liquidity","writable":true},{"name":"liquidity_program"},{"name":"rewards_rate_model"},{"name":"juplend_program","address":"jup3YeL8QhtSx1e253b2FDvsMNC87fDrgQZivbrndc9"},{"name":"token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"},{"name":"withdraw_all","type":{"option":"bool"}}]},{"name":"kamino_deposit","docs":["(user) Deposit into a Kamino pool through a marginfi account","* amount - in the liquidity token (e.g. if there is a Kamino USDC bank, pass the amount of","USDC desired), in native decimals."],"discriminator":[237,8,188,187,115,99,49,85],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"signer_token_account","docs":["Owned by authority, the source account for the token deposit."],"writable":true},{"name":"liquidity_vault_authority","docs":["The bank's liquidity vault authority, which owns the Kamino obligation. Note: Kamino needs","this to be mut because `deposit` might return the rent here"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["Used as an intermediary to deposit token into Kamino"],"writable":true,"relations":["bank"]},{"name":"integration_acc_2","writable":true,"relations":["bank"]},{"name":"lending_market"},{"name":"lending_market_authority"},{"name":"integration_acc_1","docs":["The Kamino reserve that holds liquidity"],"writable":true,"relations":["bank"]},{"name":"mint","docs":["Bank's liquidity token mint (e.g., USDC). Kamino calls this the `reserve_liquidity_mint`"],"relations":["bank"]},{"name":"reserve_liquidity_supply","writable":true},{"name":"reserve_collateral_mint","docs":["The reserve's mint for tokenized representations of Kamino deposits."],"writable":true},{"name":"reserve_destination_deposit_collateral","docs":["The reserve's destination for tokenized representations of deposits. Note: the","`reserve_collateral_mint` will mint tokens directly to this account."],"writable":true},{"name":"obligation_farm_user_state","docs":["Required if the Kamino reserve has an active farm."],"writable":true,"optional":true},{"name":"reserve_farm_state","docs":["Required if the Kamino reserve has an active farm."],"writable":true,"optional":true},{"name":"kamino_program","address":"KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD"},{"name":"farms_program","docs":["Farms program for Kamino staking functionality"],"address":"FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr"},{"name":"collateral_token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"liquidity_token_program"},{"name":"instruction_sysvar_account","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[{"name":"amount","type":"u64"}]},{"name":"kamino_harvest_reward","docs":["(permissionless) Harvest the specified reward index from the Kamino Farm attached to this","bank. Rewards are always sent to the global fee wallet's canonical ATA.","","* `reward_index` — index of the reward token in the Kamino Farm's reward list"],"discriminator":[163,202,248,141,106,20,116,5],"accounts":[{"name":"bank"},{"name":"fee_state","docs":["Global fee state that contains the global_fee_admin"],"pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"destination_token_account","docs":["Destination token account must be owned by the global fee admin"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_state"},{"kind":"account","path":"token_program"},{"kind":"account","path":"reward_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"liquidity_vault_authority","docs":["The bank's liquidity vault authority, which owns the Kamino obligation."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"user_state","writable":true},{"name":"farm_state","writable":true},{"name":"global_config"},{"name":"reward_mint"},{"name":"user_reward_ata","docs":["An initialized ATA of type reward mint owned by liquidity vault"],"writable":true},{"name":"rewards_vault","writable":true},{"name":"rewards_treasury_vault","writable":true},{"name":"farm_vaults_authority"},{"name":"scope_prices","optional":true},{"name":"farms_program","address":"FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr"},{"name":"token_program"}],"args":[{"name":"reward_index","type":"u64"}]},{"name":"kamino_init_obligation","docs":["(permissionless) Initialize a Kamino obligation for a marginfi bank","* amount - In token, in native decimals. Must be >10 (i.e. 10 lamports, not 10 tokens). Lost","forever. Generally, try to make this the equivalent of around $1, in case Kamino ever","rounds small balances down to zero."],"discriminator":[253,177,160,225,70,156,217,109],"accounts":[{"name":"fee_payer","docs":["Pays to init the obligation and pays a nominal amount to ensure the obligation has a","non-zero balance."],"writable":true,"signer":true},{"name":"bank"},{"name":"signer_token_account","docs":["The fee payer must provide a nominal amount of bank tokens so the obligation is not empty.","This amount is irrecoverable and and will prevent the obligation from ever being closed,","even if the bank is otherwise empty (Kamino normally closes empty obligations automatically)"],"writable":true},{"name":"liquidity_vault_authority","docs":["The liquidity vault authority (PDA that will own the Kamino obligation). Note: Kamino needs","this to be mut because `deposit` might return the rent here"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["Used as an intermediary to deposit a nominal amount of token into the obligation."],"writable":true,"relations":["bank"]},{"name":"integration_acc_2","docs":["The obligation account to be created. Note that the key was already derived when","initializing the bank, and this must match the obligation recorded at that time."],"writable":true,"relations":["bank"]},{"name":"user_metadata","writable":true},{"name":"lending_market"},{"name":"lending_market_authority"},{"name":"integration_acc_1","writable":true,"relations":["bank"]},{"name":"mint","docs":["Bank's liquidity token mint (e.g., USDC). Kamino calls this the `reserve_liquidity_mint`"],"writable":true,"relations":["bank"]},{"name":"reserve_liquidity_supply","writable":true},{"name":"reserve_collateral_mint","docs":["The reserve's mint for tokenized representations of Kamino deposits."],"writable":true},{"name":"reserve_destination_deposit_collateral","docs":["The reserve's destination for tokenized representations of deposits. Note: the","`reserve_collateral_mint` will mint tokens directly to this account."],"writable":true},{"name":"pyth_oracle","optional":true},{"name":"switchboard_price_oracle","optional":true},{"name":"switchboard_twap_oracle","optional":true},{"name":"scope_prices","optional":true},{"name":"obligation_farm_user_state","docs":["Required if the Kamino reserve has an active farm."],"writable":true,"optional":true},{"name":"reserve_farm_state","docs":["Required if the Kamino reserve has an active farm."],"writable":true,"optional":true},{"name":"kamino_program","address":"KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD"},{"name":"farms_program","docs":["Farms program for Kamino staking functionality"],"address":"FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr"},{"name":"collateral_token_program","docs":["Note: the collateral token always uses Token classic, never Token22."],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"liquidity_token_program","docs":["Note: Kamino does not have full Token22 support, certain Token22 features are disallowed.","Expect this to update over time. Check with the Kamino source."]},{"name":"instruction_sysvar_account","address":"Sysvar1nstructions1111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"}]},{"name":"kamino_withdraw","docs":["(user) Withdraw from a Kamino pool through a marginfi account","* amount - in the collateral token (NOT liquidity token), in native decimals. Must convert","from collateral to liquidity token amounts using the current exchange rate.","* withdraw_all - if true, withdraw the entire mrgn balance (Note: due to rounding down, a","deposit and withdraw back to back may result in several lamports less)"],"discriminator":[199,101,41,45,213,98,224,200],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"destination_token_account","docs":["Token account that will receive the withdrawn tokens. Mint/owner are validated by the","SPL transfer; the caller controls the destination."],"writable":true},{"name":"liquidity_vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]},"relations":["bank"]},{"name":"integration_acc_2","writable":true,"relations":["bank"]},{"name":"lending_market","docs":["The Kamino lending market"]},{"name":"lending_market_authority","docs":["The Kamino lending market authority"]},{"name":"integration_acc_1","docs":["The Kamino reserve that holds liquidity"],"writable":true,"relations":["bank"]},{"name":"mint","docs":["The liquidity token mint (e.g., USDC)","Needs serde to get the mint decimals for transfer checked","TODO: rename to just 'mint' to make use of has_one and to be consistent with deposit"],"writable":true,"relations":["bank"]},{"name":"reserve_liquidity_supply","docs":["The reserve's liquidity supply account"],"writable":true},{"name":"reserve_collateral_mint","docs":["The reserve's collateral mint"],"writable":true},{"name":"reserve_source_collateral","docs":["The reserve's source for collateral tokens"],"writable":true},{"name":"obligation_farm_user_state","docs":["Optional farms accounts for Kamino staking functionality"],"writable":true,"optional":true},{"name":"reserve_farm_state","writable":true,"optional":true},{"name":"kamino_program","address":"KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD"},{"name":"farms_program","docs":["Farms program for Kamino staking functionality"],"address":"FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr"},{"name":"collateral_token_program","docs":["The token program for the collateral token"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"liquidity_token_program","docs":["The token program for the liquidity token"]},{"name":"instruction_sysvar_account","docs":["Used by kamino validate CPI calls"],"address":"Sysvar1nstructions1111111111111111111111111"}],"args":[{"name":"amount","type":"u64"},{"name":"withdraw_all","type":{"option":"bool"}}]},{"name":"lending_account_borrow","docs":["(account authority) Borrow assets from a bank. Accrues interest, records liability, applies","origination fee, transfers tokens, and runs a health check."],"discriminator":[4,126,116,53,48,5,212,31],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"destination_token_account","writable":true},{"name":"bank_liquidity_vault_authority","pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"relations":["bank"]},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"}]},{"name":"lending_account_clear_emissions","docs":["(permissionless) Zero out `emissions_outstanding` on a balance after emissions are disabled","on the bank."],"discriminator":[239,4,221,98,45,167,201,244],"accounts":[{"name":"marginfi_account","writable":true},{"name":"bank"}],"args":[]},{"name":"lending_account_close_balance","docs":["(account authority) Close a balance position with dust-level amounts."],"discriminator":[245,54,41,4,243,202,31,17],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true}],"args":[]},{"name":"lending_account_deposit","docs":["(account authority) Deposit assets into a bank. Accrues interest, records deposit, and","transfers tokens from the signer's token account to the bank's liquidity vault."],"discriminator":[171,94,235,103,82,64,212,140],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"signer_token_account","writable":true},{"name":"liquidity_vault","writable":true,"relations":["bank"]},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"},{"name":"deposit_up_to_limit","type":{"option":"bool"}}]},{"name":"lending_account_end_flashloan","docs":["(account authority) End a flash loan and run the health check."],"discriminator":[105,124,201,106,153,2,8,156],"accounts":[{"name":"marginfi_account","writable":true},{"name":"authority","signer":true,"relations":["marginfi_account"]}],"args":[]},{"name":"lending_account_liquidate","docs":["(permissionless) Liquidate a lending account balance of an unhealthy marginfi account.","The liquidator takes on the liability and receives discounted collateral (2.5% liquidator","fee + 2.5% insurance fee).","* `asset_amount` - amount of collateral to liquidate","* `liquidatee_accounts` - number of remaining accounts for the liquidatee","* `liquidator_accounts` - number of remaining accounts for the liquidator"],"discriminator":[214,169,151,213,251,167,86,219],"accounts":[{"name":"group","relations":["asset_bank","liab_bank","liquidator_marginfi_account","liquidatee_marginfi_account"]},{"name":"asset_bank","writable":true},{"name":"liab_bank","writable":true},{"name":"liquidator_marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"liquidatee_marginfi_account","writable":true},{"name":"bank_liquidity_vault_authority","pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"liab_bank"}]}},{"name":"bank_liquidity_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"liab_bank"}]}},{"name":"bank_insurance_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"liab_bank"}]}},{"name":"token_program"}],"args":[{"name":"asset_amount","type":"u64"},{"name":"liquidatee_accounts","type":"u8"},{"name":"liquidator_accounts","type":"u8"}]},{"name":"lending_account_pulse_health","docs":["(Permissionless) Refresh the internal risk engine health cache. Useful for liquidators and","other consumers that want to see the internal risk state of a user account. This cache is","read-only and serves no purpose except being populated by this ix.","* remaining accounts expected in the same order as borrow, etc. I.e., for each balance the","user has, pass bank and oracle: "],"discriminator":[186,52,117,97,34,74,39,253],"accounts":[{"name":"marginfi_account","writable":true}],"args":[]},{"name":"lending_account_repay","docs":["(account authority, or any signer during receivership) Repay borrowed assets. Accrues","interest, records repayment, and transfers tokens to the bank's liquidity vault."],"discriminator":[79,209,172,177,222,51,173,151],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","docs":["Must be marginfi_account's authority, unless in liquidation/deleverage receivership or order execution","","Note: during receivership and order execution, there are no signer checks whatsoever: any key can repay as","long as the invariants checked at the end of execution are met."],"signer":true},{"name":"bank","writable":true},{"name":"signer_token_account","writable":true},{"name":"liquidity_vault","writable":true,"relations":["bank"]},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"},{"name":"repay_all","type":{"option":"bool"}}]},{"name":"lending_account_start_flashloan","docs":["(account authority) Start a flash loan. Must have a corresponding `end_flashloan` ix in the","same tx. Health checks are skipped until the flash loan ends."],"discriminator":[14,131,33,220,81,186,180,107],"accounts":[{"name":"marginfi_account","writable":true},{"name":"authority","signer":true,"relations":["marginfi_account"]},{"name":"ixs_sysvar","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[{"name":"end_index","type":"u64"}]},{"name":"lending_account_withdraw","docs":["(account authority, or any signer during receivership) Withdraw assets from a bank. Accrues","interest, records withdrawal, transfers tokens, and runs a health check (skipped during","receivership)."],"discriminator":[36,72,74,19,210,210,192,192],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","docs":["Must be marginfi_account's authority, unless in liquidation/deleverage receivership or order execution","","Note: during receivership and order execution, there are no signer checks whatsoever: any key can repay as","long as the invariants checked at the end of execution are met."],"signer":true},{"name":"bank","writable":true},{"name":"destination_token_account","writable":true},{"name":"bank_liquidity_vault_authority","pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"relations":["bank"]},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"},{"name":"withdraw_all","type":{"option":"bool"}}]},{"name":"lending_pool_accrue_bank_interest","docs":["(permissionless) Accrue interest on a bank, updating share values and collecting fees."],"discriminator":[108,201,30,87,47,65,97,188],"accounts":[{"name":"group","relations":["bank"]},{"name":"bank","writable":true}],"args":[]},{"name":"lending_pool_add_bank","docs":["(admin only) Add a new bank to the lending pool"],"discriminator":[215,68,72,78,208,218,103,182],"accounts":[{"name":"marginfi_group","writable":true},{"name":"admin","signer":true,"relations":["marginfi_group"]},{"name":"fee_payer","docs":["Pays to init accounts and pays `fee_state.bank_init_flat_sol_fee` lamports to the protocol"],"writable":true,"signer":true},{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"global_fee_wallet","writable":true,"relations":["fee_state"]},{"name":"bank_mint"},{"name":"bank","writable":true,"signer":true},{"name":"liquidity_vault_authority","pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bank_config","type":{"defined":{"name":"BankConfigCompact"}}}]},{"name":"lending_pool_add_bank_drift","docs":["(group admin only) Add a Drift bank to the group."],"discriminator":[62,63,49,48,76,55,108,155],"accounts":[{"name":"group","writable":true},{"name":"admin","signer":true,"relations":["group"]},{"name":"fee_payer","writable":true,"signer":true},{"name":"bank_mint","docs":["Must match the mint used by `integration_acc_1`"]},{"name":"bank","writable":true,"pda":{"seeds":[{"kind":"account","path":"group"},{"kind":"account","path":"bank_mint"},{"kind":"arg","path":"bank_seed"}]}},{"name":"integration_acc_1","docs":["Drift spot market account that must match the bank mint"]},{"name":"integration_acc_2","docs":["Drift user account for the marginfi program (derived from liquidity_vault_authority)"],"pda":{"seeds":[{"kind":"const","value":[117,115,101,114]},{"kind":"account","path":"liquidity_vault_authority"},{"kind":"const","value":[0,0]}],"program":{"kind":"const","value":[9,84,219,190,158,201,96,201,138,122,41,63,226,19,54,150,111,225,128,209,81,174,75,129,121,86,31,137,133,74,83,246]}}},{"name":"integration_acc_3","docs":["Drift user stats account for the marginfi program (derived from liquidity_vault_authority)"],"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,115,116,97,116,115]},{"kind":"account","path":"liquidity_vault_authority"}],"program":{"kind":"const","value":[9,84,219,190,158,201,96,201,138,122,41,63,226,19,54,150,111,225,128,209,81,174,75,129,121,86,31,137,133,74,83,246]}}},{"name":"liquidity_vault_authority","docs":["Will be authority of the bank's liquidity vault. Used as intermediary for deposits/withdraws"],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["For Drift banks, the `liquidity_vault` never holds assets, but is instead used as an","intermediary when depositing/withdrawing, e.g., withdrawn funds move from Drift -> here ->","the user's token account."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","docs":["Note: Currently does nothing."],"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","docs":["Note: Currently does nothing."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","docs":["Note: Currently does nothing."],"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault","docs":["Note: Currently does nothing."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bank_config","type":{"defined":{"name":"DriftConfigCompact"}}},{"name":"bank_seed","type":"u64"}]},{"name":"lending_pool_add_bank_juplend","docs":["(admin) Add a JupLend bank to the marginfi group.","","Remaining accounts (for oracle validation):","0. underlying oracle feed (pyth push or switchboard pull)","1. JupLend `Lending` state"],"discriminator":[18,208,117,90,53,111,195,41],"accounts":[{"name":"group","writable":true},{"name":"admin","signer":true,"relations":["group"]},{"name":"fee_payer","writable":true,"signer":true},{"name":"bank_mint","docs":["Must match the mint used by the JupLend lending state."]},{"name":"bank","writable":true,"pda":{"seeds":[{"kind":"account","path":"group"},{"kind":"account","path":"bank_mint"},{"kind":"arg","path":"bank_seed"}]}},{"name":"integration_acc_1","docs":["JupLend lending state account that must match the bank mint."]},{"name":"liquidity_vault_authority","docs":["Will be authority of the bank's liquidity vault. Used as intermediary for deposits/withdraws."],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["For JupLend banks, the `liquidity_vault` is used as an intermediary when depositing/","withdrawing, e.g., withdrawn funds move from JupLend -> here -> the user's token account."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","docs":["Note: Currently does nothing."],"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","docs":["Note: Currently does nothing."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"f_token_mint","relations":["integration_acc_1"]},{"name":"integration_acc_2","docs":["The bank's fToken vault holds the fTokens received when depositing into JupLend.",""],"writable":true,"pda":{"seeds":[{"kind":"const","value":[102,95,116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"token_program","docs":["Token program for both underlying mint and fToken mint (SPL Token or Token-2022).","JupLend creates fToken mints using the same token program as the underlying."]},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bank_config","type":{"defined":{"name":"JuplendConfigCompact"}}},{"name":"bank_seed","type":"u64"}]},{"name":"lending_pool_add_bank_kamino","docs":["(group admin only) Add a Kamino bank to the group. Pass the oracle and reserve in remaining","accounts 0 and 1 respectively."],"discriminator":[118,53,16,243,255,245,149,241],"accounts":[{"name":"group","writable":true},{"name":"admin","signer":true,"relations":["group"]},{"name":"fee_payer","writable":true,"signer":true},{"name":"bank_mint","docs":["Must match the mint used by the Kamino reserve (integration_acc_1), Kamino calls this the","`reserve_liquidity_mint` aka `liquidity.mint_pubkey`"]},{"name":"bank","writable":true,"pda":{"seeds":[{"kind":"account","path":"group"},{"kind":"account","path":"bank_mint"},{"kind":"arg","path":"bank_seed"}]}},{"name":"integration_acc_1"},{"name":"integration_acc_2","docs":["Note: not yet initialized in this instruction, run `init_obligation` after."]},{"name":"liquidity_vault_authority","docs":["Will be authority of the bank's Kamino obligation (integration_acc_2). Note: When","depositing/withdrawing Kamino assets, the source/destination must also be owned by the","obligation authority.","Kamino assets, the source/destination must also be owned by the obligation authority. This","account owns the `liquidity_vault`, and thus acts as intermediary for deposits/withdraws"],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["For Kamino banks, the `liquidity_vault` never holds assets, but is instead used as an","intermediary when depositing/withdrawing, e.g., withdrawn funds move from Kamino -> here ->","the user's token account."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","docs":["Note: Currently does nothing."],"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","docs":["Note: Currently does nothing."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","docs":["Note: Currently does nothing."],"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault","docs":["Note: Currently does nothing."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bank_config","type":{"defined":{"name":"KaminoConfigCompact"}}},{"name":"bank_seed","type":"u64"}]},{"name":"lending_pool_add_bank_permissionless","docs":["(permissionless) Add a staked collateral bank. Requires a valid SPL single-pool LST mint."],"discriminator":[127,187,121,34,187,167,238,102],"accounts":[{"name":"marginfi_group","writable":true,"relations":["staked_settings"]},{"name":"staked_settings"},{"name":"fee_payer","writable":true,"signer":true},{"name":"bank_mint","docs":["Mint of the spl-single-pool LST (a PDA derived from `stake_pool`)","","because the sol_pool and stake_pool will not derive to a valid PDA which is also owned by","the staking program and spl-single-pool program."]},{"name":"sol_pool"},{"name":"stake_pool","docs":["this key.","","If derives the same `bank_mint`, then this must be the correct stake pool for that mint, and","we can subsequently use it to validate the `sol_pool`"]},{"name":"bank","writable":true,"pda":{"seeds":[{"kind":"account","path":"marginfi_group"},{"kind":"account","path":"bank_mint"},{"kind":"arg","path":"bank_seed"}]}},{"name":"liquidity_vault_authority","pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bank_seed","type":"u64"}]},{"name":"lending_pool_add_bank_solend","docs":["(admin) Add a Solend bank to the marginfi group"],"discriminator":[81,233,203,199,47,226,0,68],"accounts":[{"name":"group","writable":true},{"name":"admin","signer":true,"relations":["group"]},{"name":"fee_payer","writable":true,"signer":true},{"name":"bank_mint","docs":["Must match the mint used by `integration_acc_1`, Solend calls this the `liquidity.mint_pubkey`"]},{"name":"bank","writable":true,"pda":{"seeds":[{"kind":"account","path":"group"},{"kind":"account","path":"bank_mint"},{"kind":"arg","path":"bank_seed"}]}},{"name":"integration_acc_1","docs":["Solend reserve account that must match the bank mint"]},{"name":"integration_acc_2","docs":["Obligation PDA for this bank in Solend","Will be initialized and transferred to Solend in init_obligation instruction"],"pda":{"seeds":[{"kind":"const","value":[115,111,108,101,110,100,95,111,98,108,105,103,97,116,105,111,110]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault_authority","docs":["Will be authority of the bank's liquidity vault. Used as intermediary for deposits/withdraws"],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["For Solend banks, the `liquidity_vault` never holds assets, but is instead used as an","intermediary when depositing/withdrawing, e.g., withdrawn funds move from Solend -> here ->","the user's token account."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","docs":["Note: Currently does nothing."],"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","docs":["Note: Currently does nothing."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","docs":["Note: Currently does nothing."],"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault","docs":["Note: Currently does nothing."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bank_config","type":{"defined":{"name":"SolendConfigCompact"}}},{"name":"bank_seed","type":"u64"}]},{"name":"lending_pool_add_bank_with_seed","docs":["(admin only) A copy of lending_pool_add_bank with an additional bank seed.","This seed is used to create a PDA for the bank's signature.","lending_pool_add_bank is preserved for backwards compatibility."],"discriminator":[76,211,213,171,117,78,158,76],"accounts":[{"name":"marginfi_group","writable":true},{"name":"admin","signer":true,"relations":["marginfi_group"]},{"name":"fee_payer","docs":["Pays to init accounts and pays `fee_state.bank_init_flat_sol_fee` lamports to the protocol"],"writable":true,"signer":true},{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"global_fee_wallet","writable":true,"relations":["fee_state"]},{"name":"bank_mint"},{"name":"bank","writable":true,"pda":{"seeds":[{"kind":"account","path":"marginfi_group"},{"kind":"account","path":"bank_mint"},{"kind":"arg","path":"bank_seed"}]}},{"name":"liquidity_vault_authority","pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bank_config","type":{"defined":{"name":"BankConfigCompact"}}},{"name":"bank_seed","type":"u64"}]},{"name":"lending_pool_clone_bank","docs":["(admin only) Staging or localnet only, panics on mainnet","This instruction is used to clone a bank to a new PDA."],"discriminator":[214,93,17,236,177,228,78,17],"accounts":[{"name":"marginfi_group","writable":true},{"name":"admin","writable":true,"signer":true,"relations":["marginfi_group"]},{"name":"fee_payer","writable":true,"signer":true},{"name":"bank_mint"},{"name":"source_bank","docs":["Source bank to clone from mainnet program",""]},{"name":"bank","writable":true,"pda":{"seeds":[{"kind":"account","path":"marginfi_group"},{"kind":"account","path":"bank_mint"},{"kind":"arg","path":"bank_seed"}]}},{"name":"liquidity_vault_authority","pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bank_seed","type":"u64"}]},{"name":"lending_pool_clone_emode","docs":["(admin or emode_admin) Copies emode settings from one bank to another. Useful when applying","emode settings from e.g. one LST to another."],"discriminator":[146,167,94,106,184,202,15,10],"accounts":[{"name":"group","relations":["copy_from_bank","copy_to_bank"]},{"name":"signer","signer":true},{"name":"copy_from_bank"},{"name":"copy_to_bank","writable":true}],"args":[]},{"name":"lending_pool_close_bank","docs":["(admin only) Close a bank. Requires CLOSE_ENABLED_FLAG and zero positions/shares."],"discriminator":[22,115,7,130,227,85,0,47],"accounts":[{"name":"group","writable":true,"relations":["bank"]},{"name":"bank","writable":true},{"name":"admin","writable":true,"signer":true,"relations":["group"]}],"args":[]},{"name":"lending_pool_collect_bank_fees","docs":["(permissionless) Transfer accrued fees from the liquidity vault to insurance/fee/program","vaults."],"discriminator":[201,5,215,116,230,92,75,150],"accounts":[{"name":"group","relations":["bank"]},{"name":"bank","writable":true},{"name":"liquidity_vault_authority","pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"fee_ata","docs":["(validated in handler). Must already exist, may require initializing the ATA if it does not","already exist prior to this ix."],"writable":true},{"name":"token_program"}],"args":[]},{"name":"lending_pool_configure_bank","docs":["(admin only) Configure bank parameters. If the bank has `FREEZE_SETTINGS`, only","deposit/borrow limits are updated and all other config changes are silently ignored."],"discriminator":[121,173,156,40,93,148,56,237],"accounts":[{"name":"group","relations":["bank"]},{"name":"admin","signer":true,"relations":["group"]},{"name":"bank","writable":true}],"args":[{"name":"bank_config_opt","type":{"defined":{"name":"BankConfigOpt"}}}]},{"name":"lending_pool_configure_bank_emode","docs":["(emode_admin only)"],"discriminator":[17,175,91,57,239,86,49,71],"accounts":[{"name":"group","relations":["bank"]},{"name":"emode_admin","signer":true,"relations":["group"]},{"name":"bank","writable":true}],"args":[{"name":"emode_tag","type":"u16"},{"name":"entries","type":{"array":[{"defined":{"name":"EmodeEntry"}},10]}}]},{"name":"lending_pool_configure_bank_interest_only","docs":["(delegate_curve_admin only) Update interest rate config. Does nothing if bank has","`FREEZE_SETTINGS`."],"discriminator":[245,107,83,38,103,219,163,241],"accounts":[{"name":"group","relations":["bank"]},{"name":"delegate_curve_admin","signer":true,"relations":["group"]},{"name":"bank","writable":true}],"args":[{"name":"interest_rate_config","type":{"defined":{"name":"InterestRateConfigOpt"}}}]},{"name":"lending_pool_configure_bank_limits_only","docs":["(delegate_limit_admin only) Update deposit/borrow/init limits only."],"discriminator":[157,196,221,200,202,62,84,21],"accounts":[{"name":"group","relations":["bank"]},{"name":"delegate_limit_admin","signer":true,"relations":["group"]},{"name":"bank","writable":true}],"args":[{"name":"deposit_limit","type":{"option":"u64"}},{"name":"borrow_limit","type":{"option":"u64"}},{"name":"total_asset_value_init_limit","type":{"option":"u64"}}]},{"name":"lending_pool_configure_bank_oracle","docs":["(admin only)"],"discriminator":[209,82,255,171,124,21,71,81],"accounts":[{"name":"group","relations":["bank"]},{"name":"admin","signer":true,"relations":["group"]},{"name":"bank","writable":true}],"args":[{"name":"setup","type":"u8"},{"name":"oracle","type":"pubkey"}]},{"name":"lending_pool_force_tokenless_repay_complete","docs":["(risk_admin only) - Signals all of a bank's liability have been deleveraged. Used if a bank","still has liability dust after the risk admin has completed deleveraging all debts. The","risk admin is trusted not to execute this until all non-dust debts have been deleveraged."],"discriminator":[15,203,147,232,199,14,231,37],"accounts":[{"name":"group","relations":["bank"]},{"name":"risk_admin","signer":true,"relations":["group"]},{"name":"bank","writable":true}],"args":[]},{"name":"lending_pool_handle_bankruptcy","docs":["(risk_admin or admin, unless `PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG` is set on the bank)","Handle bad debt of a bankrupt marginfi account for a given bank. Covers bad debt from the","insurance fund and socializes any remainder among depositors."],"discriminator":[162,11,56,139,90,128,70,173],"accounts":[{"name":"group","relations":["bank","marginfi_account"]},{"name":"signer","docs":["Must be risk_admin or admin, unless the bank has PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG","set, in which case any signer is accepted."],"signer":true},{"name":"bank","writable":true},{"name":"marginfi_account","writable":true},{"name":"liquidity_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"token_program"}],"args":[]},{"name":"lending_pool_pulse_bank_price_cache","docs":["(Permissionless) Refresh the cached oracle price for a bank."],"discriminator":[192,19,201,135,105,203,32,222],"accounts":[{"name":"group","relations":["bank"]},{"name":"bank","writable":true}],"args":[]},{"name":"lending_pool_reclaim_emissions_vault","docs":["(permissionless) Reclaim all remaining tokens from the emissions vault","to the global fee wallet ATA, and disable emissions on the bank."],"discriminator":[206,67,186,225,41,30,95,216],"accounts":[{"name":"group","relations":["bank"]},{"name":"bank","writable":true},{"name":"emissions_mint"},{"name":"emissions_auth","pda":{"seeds":[{"kind":"const","value":[101,109,105,115,115,105,111,110,115,95,97,117,116,104,95,115,101,101,100]},{"kind":"account","path":"bank"},{"kind":"account","path":"emissions_mint"}]}},{"name":"emissions_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[101,109,105,115,115,105,111,110,115,95,116,111,107,101,110,95,97,99,99,111,117,110,116,95,115,101,101,100]},{"kind":"account","path":"bank"},{"kind":"account","path":"emissions_mint"}]}},{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"destination_account","docs":["emissions mint (validated in handler)."],"writable":true},{"name":"token_program"}],"args":[]},{"name":"lending_pool_set_fixed_oracle_price","docs":["(admin only)"],"discriminator":[28,126,127,127,60,37,211,125],"accounts":[{"name":"group","relations":["bank"]},{"name":"admin","signer":true,"relations":["group"]},{"name":"bank","writable":true}],"args":[{"name":"price","type":{"defined":{"name":"WrappedI80F48"}}}]},{"name":"lending_pool_update_fees_destination_account","docs":["(admin only) Set the destination wallet for permissionless fee withdrawals."],"discriminator":[102,4,121,243,237,110,95,13],"accounts":[{"name":"group","relations":["bank"]},{"name":"bank","writable":true},{"name":"admin","signer":true,"relations":["group"]},{"name":"destination_account","docs":["Bank fees will be sent to this account which must be an ATA of the bank's mint."]}],"args":[]},{"name":"lending_pool_withdraw_fees","docs":["(admin only) Withdraw collected group fees from the fee vault."],"discriminator":[92,140,215,254,170,0,83,174],"accounts":[{"name":"group","relations":["bank"]},{"name":"bank"},{"name":"admin","signer":true,"relations":["group"]},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"dst_token_account","writable":true},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"}]},{"name":"lending_pool_withdraw_fees_permissionless","docs":["(permissionless) Withdraw group fees to the pre-configured `fees_destination_account`."],"discriminator":[57,245,1,208,130,18,145,113],"accounts":[{"name":"group","relations":["bank"]},{"name":"bank"},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"fees_destination_account","writable":true,"relations":["bank"]},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"}]},{"name":"lending_pool_withdraw_insurance","docs":["(admin only) Withdraw from the insurance vault."],"discriminator":[108,60,60,246,104,79,159,243],"accounts":[{"name":"group","relations":["bank"]},{"name":"bank"},{"name":"admin","signer":true,"relations":["group"]},{"name":"insurance_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"dst_token_account","writable":true},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"}]},{"name":"marginfi_account_close","docs":["(account authority) Close a marginfi account. Requires all balances to be empty and no","active flags (disabled, flashloan, receivership)."],"discriminator":[186,221,93,34,50,97,194,241],"accounts":[{"name":"marginfi_account","writable":true},{"name":"authority","signer":true,"relations":["marginfi_account"]},{"name":"fee_payer","writable":true,"signer":true}],"args":[]},{"name":"marginfi_account_close_order","docs":["(user) Close an existing Order, returning rent to the user"],"discriminator":[212,223,79,182,172,183,205,237],"accounts":[{"name":"marginfi_account","writable":true,"relations":["order"]},{"name":"authority","signer":true,"relations":["marginfi_account"]},{"name":"order","writable":true},{"name":"fee_recipient","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"marginfi_account_end_execute_order","docs":["(permissionless keeper) End Order execution","* Closes the Order (keeper keeps the rent)","* Closes the borrow position involved in the Order, the lending position remains open","* User health must be \"unchanged\" (within Order requirements i.e. minus slippage). Keeper","may keep any slippage in excess of what was needed to complete the Order as profit.","* `StartExecuteOrder` must appear earlier in the tx","* Must appear last in the tx","* CPI is forbidden","* Returns rent for ephemeral accounts created during `StartExecuteOrder`"],"discriminator":[115,42,20,93,121,84,178,83],"accounts":[{"name":"group","relations":["marginfi_account"]},{"name":"marginfi_account","docs":["The account owning the order"],"writable":true,"relations":["order"]},{"name":"executor","docs":["The executioner ☠️"],"signer":true,"relations":["execute_record"]},{"name":"fee_recipient","writable":true},{"name":"order","writable":true,"relations":["execute_record"]},{"name":"execute_record","docs":["This keeps track of the relevant state to be checked at the end of execution."],"writable":true},{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}}],"args":[]},{"name":"marginfi_account_init_liq_record","docs":["(permissionless) Initialize a liquidation record PDA for a marginfi account. The fee_payer","pays rent; the record is required for receivership liquidation."],"discriminator":[236,213,238,126,147,251,164,8],"accounts":[{"name":"marginfi_account","writable":true},{"name":"fee_payer","writable":true,"signer":true},{"name":"liquidation_record","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,95,114,101,99,111,114,100]},{"kind":"account","path":"marginfi_account"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"marginfi_account_initialize","docs":["Initialize a marginfi account for a given group. The account is a fresh keypair, and must","sign. If you are a CPI caller, consider using `marginfi_account_initialize_pda` instead, or","create the account manually and use `transfer_to_new_account` to gift it to the owner you","wish."],"discriminator":[43,78,61,255,148,52,249,154],"accounts":[{"name":"marginfi_group"},{"name":"marginfi_account","writable":true,"signer":true},{"name":"authority","signer":true},{"name":"fee_payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"marginfi_account_initialize_pda","docs":["The same as `marginfi_account_initialize`, except the created marginfi account uses a PDA","(Program Derived Address)","","seeds:","- marginfi_group","- authority: The account authority (owner)","- account_index: A u16 value to allow multiple accounts per authority","- third_party_id: Optional u16 for third-party tagging. Seeds < PDA_FREE_THRESHOLD can be","used freely. For a dedicated seed used by just your program (via CPI), contact us."],"discriminator":[87,177,91,80,218,119,245,31],"accounts":[{"name":"marginfi_group"},{"name":"marginfi_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,97,114,103,105,110,102,105,95,97,99,99,111,117,110,116]},{"kind":"account","path":"marginfi_group"},{"kind":"account","path":"authority"},{"kind":"arg","path":"account_index"},{"kind":"arg","path":"third_party_id.unwrap_or(0)"}]}},{"name":"authority","signer":true},{"name":"fee_payer","writable":true,"signer":true},{"name":"instructions_sysvar","docs":["Instructions sysvar for CPI validation",""],"address":"Sysvar1nstructions1111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"account_index","type":"u16"},{"name":"third_party_id","type":{"option":"u16"}}]},{"name":"marginfi_account_keeper_close_order","docs":["(permissionless keeper) Close an existing Order after the user account was closed, or it no","longer has the associated positions, or the user has executed","`marginfi_account_set_keeper_close_flags`. Keeper keeps the rent."],"discriminator":[128,114,71,46,194,71,186,106],"accounts":[{"name":"marginfi_account","docs":["marginfi account was closed.","The ownership check is checked in the handler or/and type checks are made in the handler."],"relations":["order"]},{"name":"fee_recipient","writable":true},{"name":"order","writable":true}],"args":[]},{"name":"marginfi_account_place_order","docs":["(user) Create a new Order.","* bank_keys - Currently only two keys: the lending position and borrowing position in the","users's Balances for which the order is being placed","* trigger - the type of order (stop loss, take profit, or both), and the threshold at which","to trigger the order, in dollars"],"discriminator":[244,112,75,138,143,108,7,186],"accounts":[{"name":"group","relations":["marginfi_account"]},{"name":"marginfi_account","writable":true},{"name":"fee_payer","writable":true,"signer":true},{"name":"authority","signer":true,"relations":["marginfi_account"]},{"name":"order","writable":true},{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"global_fee_wallet","writable":true,"relations":["fee_state"]},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bank_keys","type":{"vec":"pubkey"}},{"name":"trigger","type":{"defined":{"name":"OrderTrigger"}}}]},{"name":"marginfi_account_set_freeze","docs":["(admin only) Freeze or unfreeze a marginfi account. Frozen accounts can only be operated on","by the group admin."],"discriminator":[199,179,231,30,138,247,110,227],"accounts":[{"name":"group","relations":["marginfi_account"]},{"name":"marginfi_account","writable":true},{"name":"admin","signer":true}],"args":[{"name":"frozen","type":"bool"}]},{"name":"marginfi_account_set_keeper_close_flags","docs":["(user) Purge flags from some balances, enabling a Keeper to call","`marginfi_account_keeper_close_order` on associated Orders. Typically, use","`marginfi_account_close_order` instead if trying to close an Order."],"discriminator":[82,163,165,222,212,255,33,210],"accounts":[{"name":"marginfi_account","writable":true},{"name":"authority","signer":true,"relations":["marginfi_account"]}],"args":[{"name":"bank_keys_opt","type":{"option":{"vec":"pubkey"}}}]},{"name":"marginfi_account_start_execute_order","docs":["(permissionless keeper) Begin Order execution","* Enables the Keeper to withdraw/repay associated positions until the end of the tx","* Only one `StartExecuteOrder` is allowed per tx","* Must appear before `EndExecuteOrder` in the tx, and before any instructions except certain","allowed ones (compute budget, kamino refresh, etc)","* `EndExecuteOrder` must also appear in the tx","* CPI is forbidden","* Costs a small amount of rent, which is returned at the end of the tx, make sure you have","enough SOL to start the tx."],"discriminator":[1,70,140,134,183,29,208,224],"accounts":[{"name":"group","relations":["marginfi_account"]},{"name":"marginfi_account","docs":["The account owning the order"],"writable":true,"relations":["order"]},{"name":"fee_payer","writable":true,"signer":true},{"name":"executor","docs":["This account will have the authority to withdraw/repay as if they are the user authority","until the end of the tx.",""]},{"name":"order","writable":true},{"name":"execute_record","docs":["This keeps track of the relevant state to be checked at the end of execution."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[101,120,101,99,117,116,101,95,111,114,100,101,114]},{"kind":"account","path":"order"}]}},{"name":"instruction_sysvar","address":"Sysvar1nstructions1111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"marginfi_account_update_emissions_destination_account","docs":["(account authority) Set the wallet whose canonical ATA will receive off-chain emissions."],"discriminator":[73,185,162,201,111,24,116,185],"accounts":[{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"destination_account","docs":["the canonical ATA for each emissions mint."]}],"args":[]},{"name":"marginfi_group_configure","docs":["(admin only) Configure group admin keys and emode leverage caps. All admin keys must be","provided on every call. Emode leverage caps are set if provided, otherwise the existing","(non-zero) values are kept. Pass `Some(value)` to update, `None` to leave unchanged."],"discriminator":[62,199,81,78,33,13,236,61],"accounts":[{"name":"marginfi_group","writable":true},{"name":"admin","signer":true,"relations":["marginfi_group"]}],"args":[{"name":"new_admin","type":{"option":"pubkey"}},{"name":"new_emode_admin","type":{"option":"pubkey"}},{"name":"new_curve_admin","type":{"option":"pubkey"}},{"name":"new_limit_admin","type":{"option":"pubkey"}},{"name":"new_flow_admin","type":{"option":"pubkey"}},{"name":"new_emissions_admin","type":{"option":"pubkey"}},{"name":"new_metadata_admin","type":{"option":"pubkey"}},{"name":"new_risk_admin","type":{"option":"pubkey"}},{"name":"emode_max_init_leverage","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"emode_max_maint_leverage","type":{"option":{"defined":{"name":"WrappedI80F48"}}}}]},{"name":"marginfi_group_initialize","docs":["(admin only) Initialize a new marginfi group. The signer becomes the group admin."],"discriminator":[255,67,67,26,94,31,34,20],"accounts":[{"name":"marginfi_group","writable":true,"signer":true},{"name":"admin","writable":true,"signer":true},{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"migrate_curve","docs":["(Permissionless) Convert a bank from the legacy curve setup to the new setup, with no effect","on how interest accrues."],"discriminator":[151,254,50,13,112,235,152,72],"accounts":[{"name":"bank","writable":true}],"args":[]},{"name":"panic_pause","docs":["(global_fee_admin only) Pause the protocol. Auto-expires after 30 minutes. Limited to 3","pauses per day and 2 consecutive pauses."],"discriminator":[76,164,123,25,4,43,79,165],"accounts":[{"name":"global_fee_admin","docs":["Admin of the global FeeState (can trigger panic pause)"],"signer":true,"relations":["fee_state"]},{"name":"fee_state","docs":["Global fee state account containing the panic state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}}],"args":[]},{"name":"panic_unpause","docs":["(global_fee_admin only) Unpause the protocol before the auto-expiry."],"discriminator":[236,107,194,242,99,51,121,128],"accounts":[{"name":"global_fee_admin","docs":["Admin of the global FeeState (can manually unpause)"],"writable":true,"signer":true,"relations":["fee_state"]},{"name":"fee_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}}],"args":[]},{"name":"panic_unpause_permissionless","docs":["(permissionless) Unpause the protocol when pause time has expired"],"discriminator":[245,139,50,159,213,62,91,248],"accounts":[{"name":"fee_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}}],"args":[]},{"name":"propagate_fee_state","docs":["(Permissionless) Force any group to adopt the current FeeState settings"],"discriminator":[64,3,166,194,129,21,101,155],"accounts":[{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"marginfi_group","docs":["Any group, this ix is permisionless and can propagate the fee to any group"],"writable":true}],"args":[]},{"name":"propagate_staked_settings","docs":["(permissionless) Propagate updated staked settings to a staked collateral bank."],"discriminator":[210,30,152,69,130,99,222,170],"accounts":[{"name":"marginfi_group","relations":["staked_settings"]},{"name":"staked_settings"},{"name":"bank","writable":true}],"args":[]},{"name":"purge_deleverage_balance","docs":["(risk admin only) Purge a user's lending balance without withdrawing anything. Only usable","after all the debt has been settled on a bank in deleveraging mode, e.g. when","`TOKENLESS_REPAYMENTS_ALLOWED` and `TOKENLESS_REPAYMENTS_COMPLETE`. used to purge remaining","lending assets in a now-worthless bank before it is fully sunset."],"discriminator":[132,187,25,149,181,59,253,136],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"risk_admin","signer":true,"relations":["group"]},{"name":"bank","writable":true}],"args":[]},{"name":"solend_deposit","docs":["(user) Deposit into a Solend reserve through a marginfi account","* amount - in the underlying token (e.g., USDC), in native decimals"],"discriminator":[56,127,176,148,12,25,3,24],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"signer_token_account","docs":["Owned by authority, the source account for the token deposit."],"writable":true},{"name":"liquidity_vault_authority","docs":["The bank's liquidity vault authority, which owns the Solend obligation"],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["Used as an intermediary to deposit tokens into Solend"],"writable":true,"relations":["bank"]},{"name":"integration_acc_2","docs":["The Solend obligation account"],"writable":true,"relations":["bank"]},{"name":"lending_market"},{"name":"lending_market_authority","docs":["Derived from the lending market"]},{"name":"integration_acc_1","docs":["The Solend reserve that holds liquidity"],"writable":true,"relations":["bank"]},{"name":"mint","docs":["Bank's liquidity token mint (e.g., USDC)"],"relations":["bank"]},{"name":"reserve_liquidity_supply","docs":["Reserve's liquidity supply SPL Token account"],"writable":true},{"name":"reserve_collateral_mint","docs":["The reserve's mint for cTokens"],"writable":true},{"name":"reserve_collateral_supply","docs":["The reserve's collateral supply account (where cTokens are stored)"],"writable":true},{"name":"user_collateral","docs":["The user's destination for cTokens (collateral). This is a temporary account owned by","liquidity_vault_authority that will hold cTokens between deposit and obligation update."],"writable":true},{"name":"pyth_price","docs":["Oracle accounts - required by Solend even if not actively used"]},{"name":"switchboard_feed"},{"name":"solend_program","address":"So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo"},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"}]},{"name":"solend_init_obligation","docs":["(permissionless) Initialize a Solend obligation for a marginfi bank","Requires a minimum deposit to ensure the obligation remains active","* amount - minimum deposit amount (at least 10 units) in native decimals"],"discriminator":[81,96,123,149,218,116,235,196],"accounts":[{"name":"fee_payer","docs":["Pays to init the obligation and pays a nominal amount to ensure the obligation has a","non-zero balance."],"writable":true,"signer":true},{"name":"bank"},{"name":"signer_token_account","docs":["The fee payer must provide a nominal amount of bank tokens so the obligation is not empty.","This amount is irrecoverable and will prevent the obligation from ever being closed."],"writable":true},{"name":"liquidity_vault_authority","docs":["The liquidity vault authority (PDA that will own the Solend obligation)"],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["Used as an intermediary to deposit a nominal amount of token into the obligation."],"writable":true,"relations":["bank"]},{"name":"integration_acc_2","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,111,108,101,110,100,95,111,98,108,105,103,97,116,105,111,110]},{"kind":"account","path":"bank"}]},"relations":["bank"]},{"name":"lending_market"},{"name":"lending_market_authority","docs":["Derived from the lending market"]},{"name":"integration_acc_1","writable":true,"relations":["bank"]},{"name":"mint","docs":["Bank's liquidity token mint (e.g., USDC)"],"writable":true,"relations":["bank"]},{"name":"reserve_liquidity_supply","writable":true},{"name":"reserve_collateral_mint","docs":["The reserve's mint for cTokens"],"writable":true},{"name":"reserve_collateral_supply","docs":["The reserve's collateral supply account (where cTokens are stored)"],"writable":true},{"name":"user_collateral","docs":["The user's destination for cTokens (collateral). This is a temporary account owned by","liquidity_vault_authority that will hold cTokens between deposit and obligation update."],"writable":true},{"name":"pyth_price","docs":["Oracle accounts - required by Solend even if not actively used"]},{"name":"switchboard_feed"},{"name":"solend_program","address":"So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo"},{"name":"token_program"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"}]},{"name":"solend_withdraw","docs":["(user) Withdraw from a Solend reserve through a marginfi account","* amount - in collateral tokens (cTokens), in native decimals","* withdraw_all - withdraw entire position if true"],"discriminator":[238,144,170,199,21,72,155,36],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"destination_token_account","docs":["Token account that will receive the withdrawn tokens. Mint/owner are validated by the","SPL transfer; the caller controls the destination."],"writable":true},{"name":"liquidity_vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"relations":["bank"]},{"name":"integration_acc_2","docs":["The Solend obligation account"],"writable":true,"relations":["bank"]},{"name":"lending_market","writable":true},{"name":"lending_market_authority","docs":["Derived from the lending market"]},{"name":"integration_acc_1","docs":["The Solend reserve that holds liquidity"],"writable":true,"relations":["bank"]},{"name":"mint","docs":["Bank's liquidity token mint (e.g., USDC)"],"relations":["bank"]},{"name":"reserve_liquidity_supply","docs":["Reserve's liquidity supply SPL Token account"],"writable":true},{"name":"reserve_collateral_mint","docs":["The reserve's mint for cTokens"],"writable":true},{"name":"reserve_collateral_supply","docs":["The reserve's collateral supply account (where cTokens are stored)"],"writable":true},{"name":"user_collateral","docs":["The user's destination for cTokens (collateral). This is a temporary account owned by","liquidity_vault_authority that holds cTokens."],"writable":true},{"name":"solend_program","address":"So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo"},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"},{"name":"withdraw_all","type":{"option":"bool"}}]},{"name":"start_deleverage","docs":["(risk_admin only) Begin forced deleverage on an account. Similar to start_liquidation but","does not require the account to be unhealthy."],"discriminator":[10,138,10,57,40,232,182,193],"accounts":[{"name":"marginfi_account","docs":["Account to deleverage"],"writable":true},{"name":"liquidation_record","docs":["The associated liquidation record PDA for the given `marginfi_account`"],"writable":true,"relations":["marginfi_account"]},{"name":"group","relations":["marginfi_account"]},{"name":"risk_admin","docs":["The risk admin will have the authority to withdraw/repay as if they are the user authority","until the end of the tx."],"signer":true,"relations":["group"]},{"name":"instruction_sysvar","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[]},{"name":"start_liquidation","docs":["(permissionless) Begin receivership liquidation on an unhealthy account. Snapshots health","and marks the account in receivership. Must have `end_liquidation` as the last ix in the tx."],"discriminator":[244,93,90,214,192,166,191,21],"accounts":[{"name":"marginfi_account","docs":["Account under liquidation"],"writable":true},{"name":"liquidation_record","docs":["The associated liquidation record PDA for the given `marginfi_account`"],"writable":true,"relations":["marginfi_account"]},{"name":"liquidation_receiver","docs":["This account will have the authority to withdraw/repay as if they are the user authority","until the end of the tx.",""]},{"name":"instruction_sysvar","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[]},{"name":"super_admin_deposit","docs":["(primary admin only) Deposit directly into a bank liquidity vault and raise","`asset_share_value` proportionally. No marginfi account is involved."],"discriminator":[241,189,199,17,207,225,64,75],"accounts":[{"name":"group","relations":["bank"]},{"name":"admin","signer":true,"relations":["group"]},{"name":"bank","writable":true},{"name":"admin_token_account","writable":true},{"name":"liquidity_vault","writable":true,"relations":["bank"]},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"}]},{"name":"super_admin_withdraw","docs":["(primary admin only) Withdraw directly from a bank liquidity vault and lower","`asset_share_value` proportionally. No marginfi account is involved."],"discriminator":[202,67,85,126,104,138,79,197],"accounts":[{"name":"group","relations":["bank"]},{"name":"admin","signer":true,"relations":["group"]},{"name":"bank","writable":true},{"name":"destination_token_account","writable":true},{"name":"liquidity_vault_authority","pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"relations":["bank"]},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"}]},{"name":"transfer_to_new_account","docs":["(account authority) Transfer all positions to a new account under a new authority. The old","account is disabled. Pays a flat SOL fee to the protocol."],"discriminator":[28,79,129,231,169,69,69,65],"accounts":[{"name":"group","relations":["old_marginfi_account"]},{"name":"old_marginfi_account","writable":true},{"name":"new_marginfi_account","writable":true,"signer":true},{"name":"authority","signer":true},{"name":"fee_payer","writable":true,"signer":true},{"name":"new_authority"},{"name":"global_fee_wallet","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"transfer_to_new_account_pda","docs":["(account authority) Same as `transfer_to_new_account` except the resulting account is a PDA","","seeds:","- marginfi_group","- authority: The account authority (owner)","- account_index: A u16 value to allow multiple accounts per authority","- third_party_id: Optional u16 for third-party tagging. Seeds < PDA_FREE_THRESHOLD can be","used freely. For a dedicated seed used by just your program (via CPI), contact us."],"discriminator":[172,210,224,220,146,212,253,49],"accounts":[{"name":"group","relations":["old_marginfi_account"]},{"name":"old_marginfi_account","writable":true},{"name":"new_marginfi_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,97,114,103,105,110,102,105,95,97,99,99,111,117,110,116]},{"kind":"account","path":"group"},{"kind":"account","path":"new_authority"},{"kind":"arg","path":"account_index"},{"kind":"arg","path":"third_party_id.unwrap_or(0)"}]}},{"name":"authority","signer":true},{"name":"fee_payer","writable":true,"signer":true},{"name":"new_authority"},{"name":"global_fee_wallet","writable":true},{"name":"instructions_sysvar","docs":["Instructions sysvar for CPI validation"],"address":"Sysvar1nstructions1111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"account_index","type":"u16"},{"name":"third_party_id","type":{"option":"u16"}}]},{"name":"update_deleverage_withdrawals","docs":["(delegate_flow_admin only) Update the deleverage daily withdraw outflow with","aggregated data. The delegate flow admin aggregates","`DeleverageWithdrawFlowEvent` events off-chain and calls this instruction at intervals."],"discriminator":[56,3,181,118,27,247,207,227],"accounts":[{"name":"marginfi_group","writable":true},{"name":"delegate_flow_admin","signer":true,"relations":["marginfi_group"]}],"args":[{"name":"outflow_usd","type":"u32"},{"name":"update_seq","type":"u64"},{"name":"event_start_slot","type":"u64"},{"name":"event_end_slot","type":"u64"}]},{"name":"update_group_rate_limiter","docs":["(delegate_flow_admin only) Update the group rate limiter with aggregated","inflow/outflow. The delegate flow admin aggregates","`RateLimitFlowEvent` events off-chain, converts to USD, and calls this instruction at","intervals to update group rate limiter state."],"discriminator":[23,78,60,139,187,44,129,37],"accounts":[{"name":"marginfi_group","writable":true},{"name":"delegate_flow_admin","signer":true,"relations":["marginfi_group"]}],"args":[{"name":"outflow_usd","type":{"option":"u64"}},{"name":"inflow_usd","type":{"option":"u64"}},{"name":"update_seq","type":"u64"},{"name":"event_start_slot","type":"u64"},{"name":"event_end_slot","type":"u64"}]},{"name":"write_bank_metadata","docs":["(metadata admin only) Write ticker/description information for a bank on-chain. Optional, not","all Banks are guaranteed to have metadata."],"discriminator":[147,78,81,133,129,138,233,59],"accounts":[{"name":"group","relations":["bank"]},{"name":"bank","relations":["metadata"]},{"name":"metadata_admin","writable":true,"signer":true,"relations":["group"]},{"name":"metadata","writable":true}],"args":[{"name":"ticker","type":{"option":"bytes"}},{"name":"description","type":{"option":"bytes"}}]}],"accounts":[{"name":"Bank","discriminator":[142,49,166,242,50,66,97,188]},{"name":"BankMetadata","discriminator":[49,207,31,34,67,225,169,186]},{"name":"ExecuteOrderRecord","discriminator":[6,100,107,60,164,226,56,97]},{"name":"FeeState","discriminator":[63,224,16,85,193,36,235,220]},{"name":"Lending","discriminator":[135,199,82,16,249,131,182,241]},{"name":"LiquidationRecord","discriminator":[95,116,23,132,89,210,245,162]},{"name":"MarginfiAccount","discriminator":[67,178,130,109,126,114,28,42]},{"name":"MarginfiGroup","discriminator":[182,23,173,240,151,206,182,67]},{"name":"MinimalObligation","discriminator":[168,206,141,106,88,76,172,167]},{"name":"MinimalReserve","discriminator":[43,242,204,202,26,247,59,127]},{"name":"MinimalSpotMarket","discriminator":[100,177,8,107,168,65,65,39]},{"name":"MinimalUser","discriminator":[159,117,95,227,239,151,58,236]},{"name":"Order","discriminator":[134,173,223,185,77,86,28,51]},{"name":"SolendMinimalReserve","discriminator":[1]},{"name":"StakedSettings","discriminator":[157,140,6,77,89,173,173,125]}],"events":[{"name":"DeleverageEvent","discriminator":[161,8,108,204,209,198,12,30]},{"name":"DeleverageWithdrawFlowEvent","discriminator":[109,90,139,200,10,204,84,176]},{"name":"EditStakedSettingsEvent","discriminator":[29,58,155,191,75,220,145,206]},{"name":"HealthPulseEvent","discriminator":[183,159,218,110,61,220,65,1]},{"name":"KeeperCloseOrderEvent","discriminator":[46,152,11,174,92,157,77,64]},{"name":"LendingAccountBorrowEvent","discriminator":[223,96,81,10,156,99,26,59]},{"name":"LendingAccountDepositEvent","discriminator":[161,54,237,217,105,248,122,151]},{"name":"LendingAccountLiquidateEvent","discriminator":[166,160,249,154,183,39,23,242]},{"name":"LendingAccountRepayEvent","discriminator":[16,220,55,111,7,80,16,25]},{"name":"LendingAccountWithdrawEvent","discriminator":[3,220,148,243,33,249,54,88]},{"name":"LendingPoolBankAccrueInterestEvent","discriminator":[104,117,187,156,111,154,106,186]},{"name":"LendingPoolBankCollectFeesEvent","discriminator":[101,119,97,250,169,175,156,253]},{"name":"LendingPoolBankConfigureEvent","discriminator":[246,35,233,110,93,152,235,40]},{"name":"LendingPoolBankConfigureFrozenEvent","discriminator":[24,10,55,18,49,150,157,179]},{"name":"LendingPoolBankConfigureOracleEvent","discriminator":[119,140,110,253,150,64,210,62]},{"name":"LendingPoolBankCreateEvent","discriminator":[236,220,201,63,239,126,136,249]},{"name":"LendingPoolBankHandleBankruptcyEvent","discriminator":[166,77,41,140,36,94,10,57]},{"name":"LendingPoolBankSetFixedOraclePriceEvent","discriminator":[65,72,8,85,229,20,90,26]},{"name":"LendingPoolSuperAdminDepositEvent","discriminator":[99,152,211,30,58,165,210,71]},{"name":"LendingPoolSuperAdminWithdrawEvent","discriminator":[107,168,232,181,144,161,252,37]},{"name":"LiquidationReceiverEvent","discriminator":[40,131,224,220,151,83,24,230]},{"name":"MarginfiAccountCloseOrderEvent","discriminator":[158,34,122,98,23,146,229,212]},{"name":"MarginfiAccountCreateEvent","discriminator":[183,5,117,104,122,199,68,51]},{"name":"MarginfiAccountFreezeEvent","discriminator":[219,219,57,178,75,86,146,122]},{"name":"MarginfiAccountPlaceOrderEvent","discriminator":[1,105,79,28,142,242,99,145]},{"name":"MarginfiAccountTransferToNewAccount","discriminator":[59,105,171,110,223,136,80,89]},{"name":"MarginfiGroupConfigureEvent","discriminator":[241,104,172,167,41,195,199,170]},{"name":"MarginfiGroupCreateEvent","discriminator":[233,125,61,14,98,240,136,253]},{"name":"RateLimitFlowEvent","discriminator":[229,5,73,200,0,107,105,109]},{"name":"SetKeeperCloseFlagsEvent","discriminator":[193,230,93,128,117,87,96,21]}],"errors":[{"code":6000,"name":"InternalLogicError","msg":"Internal Marginfi logic error"},{"code":6001,"name":"BankNotFound","msg":"Invalid bank index"},{"code":6002,"name":"LendingAccountBalanceNotFound","msg":"Lending account balance not found"},{"code":6003,"name":"BankAssetCapacityExceeded","msg":"Bank deposit capacity exceeded"},{"code":6004,"name":"InvalidTransfer","msg":"Invalid transfer"},{"code":6005,"name":"MissingPythOrBankAccount","msg":"Missing Oracle, Bank, LST mint, or Sol Pool"},{"code":6006,"name":"MissingPythAccount","msg":"Missing Pyth account"},{"code":6007,"name":"MissingBankAccount","msg":"Missing Bank account"},{"code":6008,"name":"InvalidBankAccount","msg":"Invalid Bank account"},{"code":6009,"name":"RiskEngineInitRejected","msg":"RiskEngine rejected due to either bad health or stale oracles"},{"code":6010,"name":"LendingAccountBalanceSlotsFull","msg":"Lending account balance slots are full"},{"code":6011,"name":"BankAlreadyExists","msg":"Bank already exists"},{"code":6012,"name":"ZeroLiquidationAmount","msg":"Amount to liquidate must be positive"},{"code":6013,"name":"AccountNotBankrupt","msg":"Account is not bankrupt"},{"code":6014,"name":"BalanceNotBadDebt","msg":"Account balance is not bad debt"},{"code":6015,"name":"InvalidConfig","msg":"Invalid group config"},{"code":6016,"name":"BankPaused","msg":"Bank paused"},{"code":6017,"name":"BankReduceOnly","msg":"Bank is ReduceOnly mode"},{"code":6018,"name":"BankAccountNotFound","msg":"Bank is missing"},{"code":6019,"name":"OperationDepositOnly","msg":"Operation is deposit-only"},{"code":6020,"name":"OperationWithdrawOnly","msg":"Operation is withdraw-only"},{"code":6021,"name":"OperationBorrowOnly","msg":"Operation is borrow-only"},{"code":6022,"name":"OperationRepayOnly","msg":"Operation is repay-only"},{"code":6023,"name":"NoAssetFound","msg":"No asset found"},{"code":6024,"name":"NoLiabilityFound","msg":"No liability found"},{"code":6025,"name":"InvalidOracleSetup","msg":"Invalid oracle setup"},{"code":6026,"name":"IllegalUtilizationRatio","msg":"Invalid bank utilization ratio"},{"code":6027,"name":"BankLiabilityCapacityExceeded","msg":"Bank borrow cap exceeded"},{"code":6028,"name":"InvalidPrice","msg":"Invalid Price"},{"code":6029,"name":"IsolatedAccountIllegalState","msg":"Account can have only one liability when account is under isolated risk"},{"code":6030,"name":"EmissionsAlreadySetup","msg":"Emissions already setup"},{"code":6031,"name":"OracleNotSetup","msg":"Oracle is not set"},{"code":6032,"name":"InvalidSwitchboardDecimalConversion","msg":"Invalid switchboard decimal conversion"},{"code":6033,"name":"CannotCloseOutstandingEmissions","msg":"Cannot close balance because of outstanding emissions"},{"code":6034,"name":"EmissionsUpdateError","msg":"Update emissions error"},{"code":6035,"name":"AccountDisabled","msg":"Account disabled"},{"code":6036,"name":"AccountTempActiveBalanceLimitExceeded","msg":"Account can't temporarily open 3 balances, please close a balance first"},{"code":6037,"name":"AccountInFlashloan","msg":"Illegal action during flashloan"},{"code":6038,"name":"IllegalFlashloan","msg":"Illegal flashloan"},{"code":6039,"name":"IllegalFlag","msg":"Illegal flag"},{"code":6040,"name":"IllegalBalanceState","msg":"Illegal balance state"},{"code":6041,"name":"IllegalAccountAuthorityTransfer","msg":"Illegal account authority transfer"},{"code":6042,"name":"Unauthorized","msg":"Unauthorized"},{"code":6043,"name":"IllegalAction","msg":"Invalid account authority"},{"code":6044,"name":"T22MintRequired","msg":"Token22 Banks require mint account as first remaining account"},{"code":6045,"name":"InvalidFeeAta","msg":"Invalid ATA for global fee account"},{"code":6046,"name":"AddedStakedPoolManually","msg":"Use add pool permissionless instead"},{"code":6047,"name":"AssetTagMismatch","msg":"Staked SOL accounts can only deposit staked assets and borrow SOL"},{"code":6048,"name":"StakePoolValidationFailed","msg":"Stake pool validation failed: check the stake pool, mint, or sol pool"},{"code":6049,"name":"SwitchboardStalePrice","msg":"Switchboard oracle: stale price"},{"code":6050,"name":"PythPushStalePrice","msg":"Pyth Push oracle: stale price"},{"code":6051,"name":"WrongNumberOfOracleAccounts","msg":"Oracle error: wrong number of accounts"},{"code":6052,"name":"WrongOracleAccountKeys","msg":"Oracle error: wrong account keys"},{"code":6053,"name":"Vacated2","msg":"Vacated2"},{"code":6054,"name":"Vacated3","msg":"Vacated3"},{"code":6055,"name":"OracleMaxConfidenceExceeded","msg":"Oracle max confidence exceeded: try again later"},{"code":6056,"name":"PythPushInsufficientVerificationLevel","msg":"Pyth Push oracle: insufficient verification level"},{"code":6057,"name":"ZeroAssetPrice","msg":"Zero asset price"},{"code":6058,"name":"ZeroLiabilityPrice","msg":"Zero liability price"},{"code":6059,"name":"SwitchboardWrongAccountOwner","msg":"Switchboard oracle: wrong account owner"},{"code":6060,"name":"PythPushInvalidAccount","msg":"Pyth Push oracle: invalid account"},{"code":6061,"name":"SwitchboardInvalidAccount","msg":"Switchboard oracle: invalid account"},{"code":6062,"name":"MathError","msg":"Math error"},{"code":6063,"name":"InvalidEmissionsDestinationAccount","msg":"Invalid emissions destination account"},{"code":6064,"name":"SameAssetAndLiabilityBanks","msg":"Asset and liability bank cannot be the same"},{"code":6065,"name":"OverliquidationAttempt","msg":"Trying to withdraw more assets than available"},{"code":6066,"name":"NoLiabilitiesInLiabilityBank","msg":"Liability bank has no liabilities"},{"code":6067,"name":"AssetsInLiabilityBank","msg":"Liability bank has assets"},{"code":6068,"name":"HealthyAccount","msg":"Account is healthy and cannot be liquidated"},{"code":6069,"name":"ExhaustedLiability","msg":"Liability payoff too severe, exhausted liability"},{"code":6070,"name":"TooSeverePayoff","msg":"Liability payoff too severe, liability balance has assets"},{"code":6071,"name":"TooSevereLiquidation","msg":"Liquidation too severe, account above maintenance requirement"},{"code":6072,"name":"WorseHealthPostLiquidation","msg":"Liquidation would worsen account health"},{"code":6073,"name":"IntegrationPositionLimitExceeded","msg":"Exceeded the maximum allowed integration positions"},{"code":6074,"name":"MaxInitLeverageExceeded","msg":"Maximum initial leverage exceeded"},{"code":6075,"name":"BadEmodeConfig","msg":"The Emode config was invalid"},{"code":6076,"name":"PythPushInvalidWindowSize","msg":"TWAP window size does not match expected duration"},{"code":6077,"name":"InvalidFeesDestinationAccount","msg":"Invalid fees destination account"},{"code":6078,"name":"BankCannotClose","msg":"Banks cannot close when they have open positions or emissions outstanding"},{"code":6079,"name":"AccountAlreadyMigrated","msg":"Account already migrated"},{"code":6080,"name":"ProtocolPaused","msg":"Protocol is paused"},{"code":6081,"name":"MetadataTooLong","msg":"Metadata is too long"},{"code":6082,"name":"PauseLimitExceeded","msg":"Pause limit exceeded"},{"code":6083,"name":"ProtocolNotPaused","msg":"Protocol is not paused"},{"code":6084,"name":"BankKilledByBankruptcy","msg":"Bank killed by bankruptcy: bank shutdown and value of all holdings is zero"},{"code":6085,"name":"UnexpectedLiquidationState","msg":"Liquidation state issue. Check start before end, end last, and both unique"},{"code":6086,"name":"StartNotFirst","msg":"Liquidation start must be first instruction (other than compute program ixes)"},{"code":6087,"name":"StartRepeats","msg":"Only one liquidation event allowed per tx"},{"code":6088,"name":"EndNotLast","msg":"The end instruction must be the last ix in the tx"},{"code":6089,"name":"ForbiddenIx","msg":"Tried to call an instruction that is forbidden during liquidation"},{"code":6090,"name":"LiquidationPremiumTooHigh","msg":"Seized too much of the asset relative to liability repaid"},{"code":6091,"name":"NotAllowedInCPI","msg":"Start and end liquidation and flashloan must be top-level instructions"},{"code":6092,"name":"ZeroSupplyInStakePool","msg":"Stake pool supply is zero: cannot compute price"},{"code":6093,"name":"InvalidGroup","msg":"Invalid group: account constraint violated"},{"code":6094,"name":"InvalidLiquidityVault","msg":"Invalid liquidity vault: account constraint violated"},{"code":6095,"name":"InvalidLiquidationRecord","msg":"Invalid liquidation record: account constraint violated"},{"code":6096,"name":"InvalidLiquidationReceiver","msg":"Invalid liquidation receiver: account constraint violated"},{"code":6097,"name":"InvalidEmissionsMint","msg":"Invalid emissions mint: account constraint violated"},{"code":6098,"name":"InvalidMint","msg":"Invalid mint: account constraint violated"},{"code":6099,"name":"InvalidFeeWallet","msg":"Invalid fee wallet: account constraint violated"},{"code":6100,"name":"FixedOraclePriceNegative","msg":"Fixed oracle price must be zero or greater"},{"code":6101,"name":"DailyWithdrawalLimitExceeded","msg":"Daily withdrawal limit exceeded: try again later"},{"code":6102,"name":"ZeroWithdrawalLimit","msg":"Cannot set daily withdrawal limit to zero"},{"code":6103,"name":"AccountFrozen","msg":"Account is frozen by the group admin"},{"code":6104,"name":"DuplicateBalance","msg":"Cannot reference duplicate balances"},{"code":6105,"name":"InvalidBalanceCount","msg":"Invalid amount of balances referenced"},{"code":6106,"name":"LiquidatorOrderCloseNotAllowed","msg":"Liquidator not allowed to close order"},{"code":6107,"name":"OrderTriggerNotMet","msg":"Order trigger is yet to be met"},{"code":6108,"name":"UnexpectedOrderExecutionState","msg":"Order execution state issue. Check the necessary invariants i.e not in flashloan or disabled e.t.c"},{"code":6109,"name":"OrderLiabilityNotClosed","msg":"Order liability not closed"},{"code":6110,"name":"InvalidAssetOrLiabilitiesCount","msg":"Invalid asset or liabilities count"},{"code":6111,"name":"WorseHealthPostExecution","msg":"Account health can only worsen if account is healthy"},{"code":6112,"name":"InvalidOrderTakeProfitOrStopLoss","msg":"TP must be > 0, SL must be > 0 and TP > SL if both are set"},{"code":6113,"name":"InvalidSlippage","msg":"Max slippage must be less than 100%"},{"code":6114,"name":"OrderExecutionOverWithdrawal","msg":"Executor withdrew too much: slippage or max fee constraint violated"},{"code":6115,"name":"BankHourlyRateLimitExceeded","msg":"Bank hourly rate limit exceeded: try again later"},{"code":6116,"name":"BankDailyRateLimitExceeded","msg":"Bank daily rate limit exceeded: try again later"},{"code":6117,"name":"GroupHourlyRateLimitExceeded","msg":"Group hourly rate limit exceeded: try again later"},{"code":6118,"name":"GroupDailyRateLimitExceeded","msg":"Group daily rate limit exceeded: try again later"},{"code":6119,"name":"InvalidRateLimitPrice","msg":"Invalid rate limit price: pass oracle or pre-crank cache"},{"code":6120,"name":"GroupRateLimiterUpdateEmpty","msg":"Group rate limiter admin update must include inflow and/or outflow"},{"code":6121,"name":"GroupRateLimiterUpdateInvalidSlotRange","msg":"Group rate limiter admin update slot range is invalid"},{"code":6122,"name":"GroupRateLimiterUpdateFutureSlot","msg":"Group rate limiter admin update cannot reference future slots"},{"code":6123,"name":"GroupRateLimiterUpdateStale","msg":"Group rate limiter admin update is too stale"},{"code":6124,"name":"GroupRateLimiterUpdateOutOfOrderSlot","msg":"Group rate limiter admin update slot progression is out of order"},{"code":6125,"name":"GroupRateLimiterUpdateOutOfOrderSeq","msg":"Group rate limiter admin update sequence is out of order"},{"code":6126,"name":"DeleverageWithdrawalUpdateEmpty","msg":"Deleverage withdrawal admin update must include outflow"},{"code":6127,"name":"DeleverageWithdrawalUpdateInvalidSlotRange","msg":"Deleverage withdrawal admin update slot range is invalid"},{"code":6128,"name":"DeleverageWithdrawalUpdateFutureSlot","msg":"Deleverage withdrawal admin update cannot reference future slots"},{"code":6129,"name":"DeleverageWithdrawalUpdateStale","msg":"Deleverage withdrawal admin update is too stale"},{"code":6130,"name":"DeleverageWithdrawalUpdateOutOfOrderSlot","msg":"Deleverage withdrawal admin update slot progression is out of order"},{"code":6131,"name":"DeleverageWithdrawalUpdateOutOfOrderSeq","msg":"Deleverage withdrawal admin update sequence is out of order"},{"code":6200,"name":"WrongAssetTagForStandardInstructions","msg":"Wrong asset tag for standard instructions, expected DEFAULT, SOL, or STAKED asset tag"},{"code":6201,"name":"WrongAssetTagForKaminoInstructions","msg":"Wrong asset tag for Kamino instructions, expected KAMINO asset tag"},{"code":6202,"name":"CantAddPool","msg":"Cannot create a kamino bank with this instruction, use add_bank_kamino"},{"code":6203,"name":"KaminoReserveMintAddressMismatch","msg":"Kamino reserve mint address doesn't match the bank mint address"},{"code":6204,"name":"KaminoDepositFailed","msg":"Deposit failed: obligation deposit amount increase did not match the expected increase, left - actual, right - expected"},{"code":6205,"name":"KaminoWithdrawFailed","msg":"Withdraw failed: token vault increase did not match the expected increase, left - actual, right - expected"},{"code":6206,"name":"ReserveStale","msg":"Kamino Reserve data is stale - run refresh_reserve on kamino program first"},{"code":6207,"name":"InvalidObligationDepositCount","msg":"Kamino obligation must have exactly one active deposit, at index 0"},{"code":6208,"name":"ObligationDepositReserveMismatch","msg":"Kamino obligation deposit doesn't match the expected reserve"},{"code":6209,"name":"ObligationInitDepositInsufficient","msg":"Failed to meet minimum deposit amount requirement for init obligation"},{"code":6210,"name":"KaminoReserveValidationFailed","msg":"Kamino reserve validation failed"},{"code":6211,"name":"KaminoInvalidOracleSetup","msg":"Invalid oracle setup: only KaminoPythPush and KaminoSwitchboardPull are supported"},{"code":6212,"name":"MaxMaintLeverageExceeded","msg":"Maximum Maintenance leverage exceeded"},{"code":6213,"name":"InvalidKaminoReserve","msg":"Invalid Kamino reserve: account constraint violated"},{"code":6214,"name":"InvalidKaminoObligation","msg":"Invalid Kamino obligation: account constraint violated"},{"code":6300,"name":"DriftInvalidOracleSetup","msg":"Invalid oracle setup: only DriftPythPull and DriftSwitchboardPull are supported"},{"code":6301,"name":"DriftSpotMarketMintMismatch","msg":"Drift spot market mint does not match bank mint"},{"code":6302,"name":"WrongBankAssetTagForDriftOperation","msg":"Wrong bank asset tag for Drift operation"},{"code":6303,"name":"CantUseStandardOperationsOnDriftAssets","msg":"Cannot use standard operations on Drift assets"},{"code":6304,"name":"DriftSpotMarketValidationFailed","msg":"Drift spot market validation failed"},{"code":6305,"name":"DriftInvalidSpotPositions","msg":"Drift user has invalid spot positions (only first position can have balance)"},{"code":6306,"name":"DriftSpotPositionMarketMismatch","msg":"Drift spot position market does not match bank's configured market"},{"code":6307,"name":"DriftInvalidPositionType","msg":"Drift position has invalid balance type (must be deposit)"},{"code":6308,"name":"DriftScaledBalanceMismatch","msg":"Drift scaled balance change does not match expected amount"},{"code":6309,"name":"DriftWithdrawFailed","msg":"Drift withdrawal failed - token amount mismatch"},{"code":6310,"name":"DriftUserInitDepositInsufficient","msg":"Drift user initial deposit insufficient (minimum 10 units required)"},{"code":6311,"name":"InvalidDriftAccount","msg":"Invalid drift account"},{"code":6312,"name":"DriftAuthorityMismatch","msg":"Drift authority mismatch"},{"code":6313,"name":"DriftInvalidHarvestPositionIndex","msg":"Invalid harvest position index - must be between 2 and 7"},{"code":6314,"name":"DriftPositionEmpty","msg":"Drift position is empty"},{"code":6315,"name":"DriftInvalidBalanceType","msg":"Drift position has invalid balance type"},{"code":6316,"name":"DriftNoAdminDeposit","msg":"No admin deposits found in Drift positions 2-7 for this market"},{"code":6317,"name":"DriftHarvestSameMarket","msg":"Cannot harvest from the same market as the bank's main drift spot market"},{"code":6318,"name":"DriftBrickedAccount","msg":"Drift account bricked: too many active deposits from admin operations"},{"code":6319,"name":"DriftMissingRewardOracle","msg":"Drift reward oracle required when 2+ active deposits exist"},{"code":6320,"name":"DriftMissingRewardSpotMarket","msg":"Drift reward spot market required when 2+ active deposits exist"},{"code":6321,"name":"DriftMissingRewardAccounts","msg":"Drift account has admin deposits that require reward accounts to be provided"},{"code":6322,"name":"DriftSpotMarketStale","msg":"Drift spot market is stale, interest needs to be updated"},{"code":6323,"name":"InvalidDriftSpotMarket","msg":"Invalid Drift spot market: account constraint violated"},{"code":6324,"name":"InvalidDriftUser","msg":"Invalid Drift user: account constraint violated"},{"code":6325,"name":"InvalidDriftUserStats","msg":"Invalid Drift user stats: account constraint violated"},{"code":6326,"name":"DriftUnsupportedTokenDecimals","msg":"Drift cannot support tokens with more than 19 decimals"},{"code":6400,"name":"SolendInvalidOracleSetup","msg":"Invalid oracle setup: only SolendPythPull and SolendSwitchboardPull are supported"},{"code":6401,"name":"SolendReserveValidationFailed","msg":"Solend reserve validation failed"},{"code":6402,"name":"SolendObligationOwnerMismatch","msg":"Solend obligation owner mismatch"},{"code":6403,"name":"WrongBankAssetTagForSolendOperation","msg":"Wrong bank asset tag for Solend operation"},{"code":6404,"name":"CantUseStandardOperationsOnSolendAssets","msg":"Cannot use standard operations on Solend assets"},{"code":6405,"name":"SolendReserveMismatch","msg":"Solend reserve mismatch"},{"code":6406,"name":"SolendReserveMintMismatch","msg":"Solend reserve mint mismatch"},{"code":6407,"name":"SolendInvalidDepositPositions","msg":"Solend obligation has invalid deposits (only first position can have balance)"},{"code":6408,"name":"SolendDepositPositionReserveMismatch","msg":"Solend deposit position reserve does not match bank's configured reserve"},{"code":6409,"name":"SolendCTokenBalanceMismatch","msg":"Solend cToken balance change does not match expected amount"},{"code":6410,"name":"SolendWithdrawFailed","msg":"Solend withdrawal failed - token amount mismatch"},{"code":6411,"name":"SolendReserveStale","msg":"Solend reserve is stale"},{"code":6412,"name":"SolendDepositFailed","msg":"Solend deposit failed - collateral amount mismatch"},{"code":6413,"name":"InvalidSolendAccount","msg":"Invalid Solend account owner"},{"code":6414,"name":"InvalidSolendAccountVersion","msg":"Invalid Solend account version"},{"code":6415,"name":"InvalidSolendReserve","msg":"Invalid Solend reserve: account constraint violated"},{"code":6416,"name":"InvalidSolendObligation","msg":"Invalid Solend obligation: account constraint violated"},{"code":6500,"name":"JuplendInvalidOracleSetup","msg":"Invalid oracle setup: only JuplendPythPull and JuplendSwitchboardPull are supported"},{"code":6501,"name":"JuplendLendingValidationFailed","msg":"Juplend lending state validation failed"},{"code":6502,"name":"WrongBankAssetTagForJuplendOperation","msg":"Wrong bank asset tag for Juplend operation"},{"code":6503,"name":"CantUseStandardOperationsOnJuplendAssets","msg":"Cannot use standard operations on Juplend assets"},{"code":6504,"name":"JuplendLendingStale","msg":"Juplend lending state is stale"},{"code":6505,"name":"InvalidJuplendLending","msg":"Invalid Juplend lending: account constraint violated"},{"code":6506,"name":"JuplendLendingMintMismatch","msg":"Juplend lending mint mismatch"},{"code":6507,"name":"JuplendBankAlreadyActivated","msg":"Juplend bank is already activated"},{"code":6508,"name":"InvalidJuplendFTokenVault","msg":"Invalid Juplend fToken vault"},{"code":6509,"name":"JuplendDepositFailed","msg":"Juplend deposit failed"},{"code":6510,"name":"JuplendWithdrawFailed","msg":"Juplend withdraw failed"},{"code":6511,"name":"JuplendInitPositionDepositInsufficient","msg":"Juplend init position deposit insufficient"},{"code":6512,"name":"InvalidJuplendWithdrawIntermediaryAta","msg":"Invalid Juplend withdraw intermediary ATA"}],"types":[{"name":"AccountEventHeader","type":{"kind":"struct","fields":[{"name":"signer","type":{"option":"pubkey"}},{"name":"marginfi_account","type":"pubkey"},{"name":"marginfi_account_authority","type":"pubkey"},{"name":"marginfi_group","type":"pubkey"}]}},{"name":"Balance","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"active","docs":["Whether this balance slot is in use (nonzero = active)"],"type":"u8"},{"name":"bank_pk","docs":["The bank this balance corresponds to"],"type":"pubkey"},{"name":"bank_asset_tag","docs":["Inherited from the bank when the position is first created and CANNOT BE CHANGED after that.","Note that all balances created before the addition of this feature use `ASSET_TAG_DEFAULT`"],"type":"u8"},{"name":"tag","docs":["Tag used by orders to reference this balance (0 means unused/unassigned).","A tag may also have a non-zero value while having no orders."],"type":"u16"},{"name":"_pad0","type":{"array":["u8",4]}},{"name":"asset_shares","docs":["The user's asset (deposit) shares in the bank. Multiply by `bank.asset_share_value` for","the token amount."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_shares","docs":["The user's liability (borrow) shares in the bank. Multiply by `bank.liability_share_value`","for the token amount."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"emissions_outstanding","docs":["Unclaimed emissions rewards for this position"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"last_update","docs":["Unix timestamp (u64) of the last emissions calculation for this position"],"type":"u64"},{"name":"_padding","docs":["Reserved for future use"],"type":{"array":["u64",1]}}]}},{"name":"Bank","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"mint","docs":["The SPL token mint this bank manages"],"type":"pubkey"},{"name":"mint_decimals","docs":["Number of decimals of the `mint`. Must be < 24."],"type":"u8"},{"name":"group","docs":["The `MarginfiGroup` this bank belongs to"],"type":"pubkey"},{"name":"_pad0","type":{"array":["u8",7]}},{"name":"asset_share_value","docs":["Monotonically increases as interest rate accumulates. For typical banks, a user's asset","value in token = (number of shares the user has * asset_share_value).","* A float (arbitrary decimals)","* Initially 1"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_share_value","docs":["Monotonically increases as interest rate accumulates. For typical banks, a user's liabilty","value in token = (number of shares the user has * liability_share_value)","* A float (arbitrary decimals)","* Initially 1"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liquidity_vault","docs":["The SPL token account holding deposited liquidity"],"type":"pubkey"},{"name":"liquidity_vault_bump","docs":["PDA bump for the liquidity vault"],"type":"u8"},{"name":"liquidity_vault_authority_bump","docs":["PDA bump for the liquidity vault authority"],"type":"u8"},{"name":"insurance_vault","docs":["The SPL token account holding insurance fund tokens"],"type":"pubkey"},{"name":"insurance_vault_bump","docs":["PDA bump for the insurance vault"],"type":"u8"},{"name":"insurance_vault_authority_bump","docs":["PDA bump for the insurance vault authority"],"type":"u8"},{"name":"_pad1","type":{"array":["u8",4]}},{"name":"collected_insurance_fees_outstanding","docs":["Fees collected and pending withdraw for the `insurance_vault`"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"fee_vault","docs":["The SPL token account holding collected group fees"],"type":"pubkey"},{"name":"fee_vault_bump","docs":["PDA bump for the fee vault"],"type":"u8"},{"name":"fee_vault_authority_bump","docs":["PDA bump for the fee vault authority"],"type":"u8"},{"name":"_pad2","type":{"array":["u8",6]}},{"name":"collected_group_fees_outstanding","docs":["Fees collected and pending withdraw for the `fee_vault`"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"total_liability_shares","docs":["Sum of all liability shares held by all borrowers in this bank.","Multiply by `liability_share_value` to get the total liability amount in native token units."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"total_asset_shares","docs":["Sum of all asset shares held by all depositors in this bank.","Multiply by `asset_share_value` to get the total asset amount in native token units.","* For Kamino banks, this is the quantity of collateral tokens (NOT liquidity tokens) in the","bank, and also uses `mint_decimals`, though the mint itself will always show (6) decimals","exactly (i.e Kamino ignores this and treats it as if it was using `mint_decimals`)"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"last_update","docs":["Unix timestamp (i64) of the last interest accrual"],"type":"i64"},{"name":"config","docs":["The bank's configuration parameters (weights, limits, oracle setup, interest rate config)"],"type":{"defined":{"name":"BankConfig"}}},{"name":"flags","docs":["Bank flags bitfield (u64).","","- Bit 0 (1): `EMISSIONS_FLAG_BORROW_ACTIVE` — borrow-side emissions are active","- Bit 1 (2): `EMISSIONS_FLAG_LENDING_ACTIVE` — lending-side emissions are active","- Bit 2 (4): `PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG` — anyone can settle bad debt","- Bit 3 (8): `FREEZE_SETTINGS` — bank configuration is frozen (only limits can change)","- Bit 4 (16): `CLOSE_ENABLED_FLAG` — bank can be closed (set at creation for banks >= 0.1.4)","- Bit 5 (32): `TOKENLESS_REPAYMENTS_ALLOWED` — risk admin can repay debt without tokens","- Bit 6 (64): `TOKENLESS_REPAYMENTS_COMPLETE` — all debt cleared, lender purge enabled"],"type":"u64"},{"name":"emissions_rate","docs":["Emissions APR. Number of emitted tokens (emissions_mint) per 1e(bank.mint_decimal) tokens","(bank mint) (native amount) per 1 YEAR."],"type":"u64"},{"name":"emissions_remaining","docs":["Remaining emissions tokens available for distribution"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"emissions_mint","docs":["The SPL token mint used for emissions rewards"],"type":"pubkey"},{"name":"collected_program_fees_outstanding","docs":["Fees collected and pending withdraw for the `FeeState.global_fee_wallet`'s canonical ATA for `mint`"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"emode","docs":["Controls this bank's emode configuration, which enables some banks to treat the assets of","certain other banks more preferentially as collateral."],"type":{"defined":{"name":"EmodeSettings"}}},{"name":"fees_destination_account","docs":["Set with `update_fees_destination_account`. Fees can be withdrawn to the canonical ATA of","this wallet without the admin's input (withdraw_fees_permissionless). If pubkey default, the","bank doesn't support this feature, and the fees must be collected manually (withdraw_fees)."],"type":"pubkey"},{"name":"cache","docs":["Cached bank metrics (interest rates, oracle price, etc.)"],"type":{"defined":{"name":"BankCache"}}},{"name":"lending_position_count","docs":["Number of user lending positions currently open in this bank","* For banks created prior to 0.1.4, this is the number of positions opened/closed after","0.1.4 goes live, and may be negative.","* For banks created in 0.1.4 or later, this is the number of positions open in total, and","the bank may safely be closed if this is zero. Will never go negative."],"type":"i32"},{"name":"borrowing_position_count","docs":["Number of user borrowing positions currently open in this bank","* For banks created prior to 0.1.4, this is the number of positions opened/closed after","0.1.4 goes live, and may be negative.","* For banks created in 0.1.4 or later, this is the number of positions open in total, and","the bank may safely be closed if this is zero. Will never go negative."],"type":"i32"},{"name":"_padding_0","docs":["Reserved for future use"],"type":{"array":["u8",16]}},{"name":"integration_acc_1","docs":["Integration account slot 1 (default Pubkey for non-integrations).","- Kamino: reserve","- Drift: spot market","- Solend: reserve","- JupLend: lending state"],"type":"pubkey"},{"name":"integration_acc_2","docs":["Integration account slot 2 (default Pubkey for non-integrations).","- Kamino: obligation","- Drift: user","- Solend: obligation","- JupLend: fToken vault"],"type":"pubkey"},{"name":"integration_acc_3","docs":["Integration account slot 3 (default Pubkey for non-integrations).","- Drift: user stats","- JupLend: withdraw intermediary ATA (ATA of liquidity_vault_authority for bank mint)"],"type":"pubkey"},{"name":"rate_limiter","docs":["Rate limiter for controlling withdraw/borrow outflow.","Tracks net outflow (outflows - inflows) in native tokens."],"type":{"defined":{"name":"BankRateLimiter"}}},{"name":"_pad_0","type":{"array":["u8",16]}},{"name":"_padding_1","type":{"array":[{"array":["u64",2]},7]}}]}},{"name":"BankCache","docs":["A read-only cache of the bank's key metrics, e.g. spot interest/fee rates."],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"base_rate","docs":["Actual (spot) interest/fee rates of the bank, based on utilization","* APR (annual percentage rate) values","* From 0-1000%, as u32, e.g. u32::MAX = 1000%, u32::MAX/2 = 500%, etc"],"type":"u32"},{"name":"lending_rate","docs":["Equivalent to `base_rate` * utilization","* From 0-1000%, as u32, e.g. u32::MAX = 1000%, u32::MAX/2 = 500%, etc"],"type":"u32"},{"name":"borrowing_rate","docs":["Equivalent to `base_rate` * (1 + ir_fees) + fixed_fees","* From 0-1000%, as u32, e.g. u32::MAX = 1000%, u32::MAX/2 = 500%, etc"],"type":"u32"},{"name":"interest_accumulated_for","docs":["* in seconds"],"type":"u32"},{"name":"accumulated_since_last_update","docs":["equivalent to (share value increase in the last `interest_accumulated_for` seconds *","shares), i.e. the delta in `asset_share_value`, in token.","* Note: if the tx that triggered this cache update increased or decreased the net shares,","this value still reports using the PRE-CHANGE share amount, since interest is always","earned on that amount.","* in token, in native decimals, as I80F48"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"last_oracle_price","docs":["Oracle price used in the last instruction that consumed an oracle price","* Only updated when instruction uses an oracle price, not updated for operations that don't","require prices (e.g., deposit, repay)","* Price in USD, with no price bias","* Zero if never updated"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"last_oracle_price_timestamp","docs":["Unix timestamp (seconds) when last_oracle_price was last updated","* Used to determine staleness of cached price","* Zero if never updated"],"type":"i64"},{"name":"last_oracle_price_confidence","docs":["Confidence interval reported by the oracle when last_oracle_price was fetched","* Always non-negative","* Zero if never updated","* Note: this value is the confidence reported by oracles, multiplied by `STD_DEV_MULTIPLE`"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liq_cache_flags","docs":["Liquidation cache flags, set during receivership flow.","* 1 (LIQ_CACHE_LOCKED_FLAG) - We \"lock\" the liquidation cache when writing to it in Start","Liquidate as an additional safeguard, if the liquidation prices stored here were to be","edited between start and end, it would completely break the risk engine. End validates that","the lock is set, panics if not, and removes it - which prevents footguns if the cache was","e.g. accidently set to default. The lock is also removed when a Balance is closed via","withdraw_all, repay_all, or close_balance, but only when the account has","ACCOUNT_IN_RECEIVERSHIP set, so that operations on unrelated accounts sharing the same","bank do not interfere with an in-progress liquidation."],"type":"u8"},{"name":"_padding","type":{"array":["u8",23]}},{"name":"liquidation_price_rt","docs":["Cached real-time price for receivership liquidation."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liquidation_price_rt_confidence","docs":["Cached real-time price confidence for receivership liquidation."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liquidation_price_twap","docs":["Cached TWAP price for receivership liquidation."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liquidation_price_twap_confidence","docs":["Cached TWAP price confidence for receivership liquidation."],"type":{"defined":{"name":"WrappedI80F48"}}}]}},{"name":"BankConfig","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"asset_weight_init","docs":["Discount factor for asset values in initial margin calculation (0 to 1).","E.g., 0.8 means assets count as 80% of their value for borrowing purposes."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_weight_maint","docs":["Discount factor for asset values in maintenance margin calculation (0 to 2).","Used for liquidation eligibility. Generally >= asset_weight_init."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_weight_init","docs":["Premium factor for liability values in initial margin calculation (>= 1).","E.g., 1.2 means liabilities count as 120% of their value for borrowing purposes."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_weight_maint","docs":["Premium factor for liability values in maintenance margin calculation (>= 1).","Used for liquidation eligibility. Generally <= liability_weight_init."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"deposit_limit","docs":["Maximum total deposits allowed in this bank, in native token units (0 = no limit)"],"type":"u64"},{"name":"interest_rate_config","docs":["Interest rate model configuration"],"type":{"defined":{"name":"InterestRateConfig"}}},{"name":"operational_state","docs":["Current operational state of the bank (Paused, Operational, ReduceOnly, KilledByBankruptcy)"],"type":{"defined":{"name":"BankOperationalState"}}},{"name":"oracle_setup","docs":["Oracle type used for price feeds"],"type":{"defined":{"name":"OracleSetup"}}},{"name":"oracle_keys","docs":["Oracle account keys (usage depends on oracle_setup type)"],"type":{"array":["pubkey",5]}},{"name":"_pad0","type":{"array":["u8",6]}},{"name":"borrow_limit","docs":["Maximum total borrows allowed in this bank, in native token units (0 = no limit)"],"type":"u64"},{"name":"risk_tier","docs":["Risk tier for this bank (Collateral or Isolated)"],"type":{"defined":{"name":"RiskTier"}}},{"name":"asset_tag","docs":["Determines what kinds of assets users of this bank can interact with. Options:","* `ASSET_TAG_DEFAULT` (0) - A regular asset that can be comingled with any other regular","asset or with `ASSET_TAG_SOL`","* `ASSET_TAG_SOL` (1) - Accounts with a SOL position can comingle with **either**","`ASSET_TAG_DEFAULT` or `ASSET_TAG_STAKED` positions, but not both","* `ASSET_TAG_STAKED` (2) - Staked SOL assets. Accounts with a STAKED position can only","deposit other STAKED assets or SOL (`ASSET_TAG_SOL`) and can only borrow SOL","* `ASSET_TAG_KAMINO` (3) - Treated the same as `ASSET_TAG_DEFAULT`","* `ASSET_TAG_DRIFT` (4) - Treated the same as `ASSET_TAG_DEFAULT`","* `ASSET_TAG_SOLEND` (5) - Treated the same as `ASSET_TAG_DEFAULT`"],"type":"u8"},{"name":"config_flags","docs":["Flags for various config options","* 1 - Always set if bank created in 0.1.4 or later, or if migrated to the new pyth oracle","setup from a prior version. Not set in 0.1.3 or earlier banks using pyth that have not yet","migrated. Does nothing for banks that use switchboard.","* 2, 4, 8, 16, etc - reserved for future use."],"type":"u8"},{"name":"_pad1","type":{"array":["u8",5]}},{"name":"total_asset_value_init_limit","docs":["USD denominated limit for calculating asset value for initialization margin requirements.","Example, if total SOL deposits are equal to $1M and the limit it set to $500K, then SOL","assets will be discounted by 50%.","","In other words the max value of liabilities that can be backed by the asset is $500K. This","is useful for limiting the damage of oracle attacks.","","Value is UI USD value, for example value 100 -> $100"],"type":"u64"},{"name":"oracle_max_age","docs":["Time window in seconds for the oracle price feed to be considered live."],"type":"u16"},{"name":"_padding0","type":{"array":["u8",2]}},{"name":"oracle_max_confidence","docs":["From 0-100%, if the confidence exceeds this value, the oracle is considered invalid. Note:","the confidence adjustment is capped at 5% regardless of this value.","* 0 falls back to using the default 10% instead, i.e., U32_MAX_DIV_10","* A %, as u32, e.g. 100% = u32::MAX, 50% = u32::MAX/2, etc."],"type":"u32"},{"name":"fixed_price","docs":["Stored oracle price for `OracleSetup::Fixed`, otherwise does nothing"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"_padding1","type":{"array":["u8",16]}}]}},{"name":"BankConfigCompact","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"asset_weight_init","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_weight_maint","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_weight_init","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_weight_maint","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"deposit_limit","type":"u64"},{"name":"interest_rate_config","type":{"defined":{"name":"InterestRateConfigCompact"}}},{"name":"operational_state","type":{"defined":{"name":"BankOperationalState"}}},{"name":"borrow_limit","type":"u64"},{"name":"risk_tier","type":{"defined":{"name":"RiskTier"}}},{"name":"asset_tag","docs":["Determines what kinds of assets users of this bank can interact with. Options:","* `ASSET_TAG_DEFAULT` (0) - A regular asset that can be comingled with any other regular","asset or with `ASSET_TAG_SOL`","* `ASSET_TAG_SOL` (1) - Accounts with a SOL position can comingle with **either**","`ASSET_TAG_DEFAULT` or `ASSET_TAG_STAKED` positions, but not both","* `ASSET_TAG_STAKED` (2) - Staked SOL assets. Accounts with a STAKED position can only","deposit other STAKED assets or SOL (`ASSET_TAG_SOL`) and can only borrow SOL","* `ASSET_TAG_KAMINO` (3) - Treated the same as `ASSET_TAG_DEFAULT`","* `ASSET_TAG_DRIFT` (4) - Treated the same as `ASSET_TAG_DEFAULT`","* `ASSET_TAG_SOLEND` (5) - Treated the same as `ASSET_TAG_DEFAULT`"],"type":"u8"},{"name":"config_flags","docs":["Flags for various config options","* 1 - Always set if bank created in 0.1.4 or later, or if migrated to the new oracle setup","from a prior version. Not set in 0.1.3 or earlier banks that have not yet migrated.","* 2, 4, 8, 16, etc - reserved for future use."],"type":"u8"},{"name":"_pad0","type":{"array":["u8",5]}},{"name":"total_asset_value_init_limit","docs":["USD denominated limit for calculating asset value for initialization margin requirements.","Example, if total SOL deposits are equal to $1M and the limit it set to $500K, then SOL","assets will be discounted by 50%.","","In other words the max value of liabilities that can be backed by the asset is $500K. This","is useful for limiting the damage of oracle attacks.","","Value is UI USD value, for example value 100 -> $100"],"type":"u64"},{"name":"oracle_max_age","docs":["Time window in seconds for the oracle price feed to be considered live."],"type":"u16"},{"name":"oracle_max_confidence","docs":["From 0-100%, if the confidence exceeds this value, the oracle is considered invalid. Note:","the confidence adjustment is capped at 5% regardless of this value.","* 0% = use the default (10%)","* A %, as u32, e.g. 100% = u32::MAX, 50% = u32::MAX/2, etc."],"type":"u32"}]}},{"name":"BankConfigOpt","type":{"kind":"struct","fields":[{"name":"asset_weight_init","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"asset_weight_maint","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"liability_weight_init","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"liability_weight_maint","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"deposit_limit","type":{"option":"u64"}},{"name":"borrow_limit","type":{"option":"u64"}},{"name":"operational_state","type":{"option":{"defined":{"name":"BankOperationalState"}}}},{"name":"interest_rate_config","type":{"option":{"defined":{"name":"InterestRateConfigOpt"}}}},{"name":"risk_tier","type":{"option":{"defined":{"name":"RiskTier"}}}},{"name":"asset_tag","type":{"option":"u8"}},{"name":"total_asset_value_init_limit","type":{"option":"u64"}},{"name":"oracle_max_confidence","type":{"option":"u32"}},{"name":"oracle_max_age","type":{"option":"u16"}},{"name":"permissionless_bad_debt_settlement","type":{"option":"bool"}},{"name":"freeze_settings","type":{"option":"bool"}},{"name":"tokenless_repayments_allowed","type":{"option":"bool"}}]}},{"name":"BankMetadata","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"bank","docs":["Bank this metadata corresponds to"],"type":"pubkey"},{"name":"placeholder","type":"u64"},{"name":"ticker","docs":["The token's ticker name, e.g. USDC","* utf-8"],"type":{"array":["u8",64]}},{"name":"description","docs":["The token's plain english description, e.g US Dollar Coin","* utf-8"],"type":{"array":["u8",128]}},{"name":"data_blob","docs":["Reserved for future use. Room for a very small icon or something else cool"],"type":{"array":["u8",256]}},{"name":"end_description_byte","docs":["The last data byte in description (padding follows)"],"type":"u16"},{"name":"end_data_blob","docs":["The last data byte in data_blob (padding follows)"],"type":"u16"},{"name":"end_ticker_byte","docs":["The last data byte in ticker (padding follows)"],"type":"u8"},{"name":"bump","type":"u8"},{"name":"_pad0","type":{"array":["u8",2]}}]}},{"name":"BankOperationalState","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Paused"},{"name":"Operational"},{"name":"ReduceOnly"},{"name":"KilledByBankruptcy"}]}},{"name":"BankRateLimiter","docs":["Per-bank rate limiting configuration and state.","Tracks net outflow in native tokens."],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"hourly","docs":["Hourly window rate limiter (native tokens)."],"type":{"defined":{"name":"RateLimitWindow"}}},{"name":"daily","docs":["Daily window rate limiter (native tokens)."],"type":{"defined":{"name":"RateLimitWindow"}}}]}},{"name":"DeleverageEvent","type":{"kind":"struct","fields":[{"name":"marginfi_account","type":"pubkey"},{"name":"risk_admin","type":"pubkey"},{"name":"deleveragee_assets_seized","type":"f64"},{"name":"deleveragee_liability_repaid","type":"f64"}]}},{"name":"DeleverageWithdrawFlowEvent","docs":["Emitted for deleverage-only withdraw outflows.","The delegate flow admin aggregates these off-chain and","updates the deleverage daily withdraws via `update_deleverage_withdrawals`."],"type":{"kind":"struct","fields":[{"name":"group","type":"pubkey"},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"outflow_usd","docs":["Equity-denominated outflow value in USD, rounded to integer."],"type":"u32"},{"name":"current_timestamp","docs":["Unix timestamp when the flow was recorded"],"type":"i64"}]}},{"name":"DriftConfigCompact","docs":["Used to configure Drift banks. A simplified version of `BankConfigCompact` which omits most","values related to interest since Drift banks cannot earn interest or be borrowed against."],"type":{"kind":"struct","fields":[{"name":"oracle","type":"pubkey"},{"name":"asset_weight_init","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_weight_maint","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"deposit_limit","type":"u64"},{"name":"oracle_setup","docs":["Either `DriftPythPull` or `DriftSwitchboardPull`"],"type":{"defined":{"name":"OracleSetup"}}},{"name":"operational_state","docs":["Bank operational state - allows starting banks in paused state"],"type":{"defined":{"name":"BankOperationalState"}}},{"name":"risk_tier","docs":["Risk tier - determines if assets can be borrowed in isolation"],"type":{"defined":{"name":"RiskTier"}}},{"name":"config_flags","docs":["Config flags for future-proofing"],"type":"u8"},{"name":"total_asset_value_init_limit","type":"u64"},{"name":"oracle_max_age","type":"u16"},{"name":"oracle_max_confidence","docs":["Oracle confidence threshold (0 = use default 10%)"],"type":"u32"}]}},{"name":"EditStakedSettingsEvent","type":{"kind":"struct","fields":[{"name":"group","type":"pubkey"},{"name":"settings","type":{"defined":{"name":"StakedSettingsEditConfig"}}}]}},{"name":"EmodeConfig","docs":["An emode configuration. Each bank has one such configuration, but this may also be the","intersection of many configurations (see `reconcile_emode_configs`). For example, the risk","engine creates such an intersection from all the emode config of all banks the user is borrowing","from."],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"entries","type":{"array":[{"defined":{"name":"EmodeEntry"}},10]}}]}},{"name":"EmodeEntry","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"collateral_bank_emode_tag","docs":["emode_tag of the bank(s) whose collateral you wish to treat preferentially."],"type":"u16"},{"name":"flags","docs":["* APPLIES_TO_ISOLATED (1) - (NOT YET IMPLEMENTED) if set, isolated banks with this tag","also benefit. If not set, isolated banks continue to offer zero collateral, even if they","use this tag.","* 2, 4, 8, 16, 32, etc - reserved for future use"],"type":"u8"},{"name":"pad0","type":{"array":["u8",5]}},{"name":"asset_weight_init","docs":["Note: If set below the collateral bank's weight, does nothing."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_weight_maint","docs":["Note: If set below the collateral bank's weight, does nothing."],"type":{"defined":{"name":"WrappedI80F48"}}}]}},{"name":"EmodeSettings","docs":["Controls the bank's e-mode configuration, allowing certain collateral sources to be treated more","favorably as collateral when used to borrow from this bank."],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"emode_tag","docs":["This bank's NON-unique id that other banks will use to determine what emode rate to use when","this bank is offered as collateral.","","For example, all stablecoin banks might share the same emode_tag, and in their entries, each","such stablecoin bank will recognize that collateral sources with this \"stable\" tag get","preferential weights. When a new stablecoin is added that is considered riskier, it may get","a new, less favorable emode tag, and eventually get upgraded to the same one as the other","stables","","* 0 is in an invalid tag and will do nothing."],"type":"u16"},{"name":"pad0","type":{"array":["u8",6]}},{"name":"timestamp","docs":["Unix timestamp from the system clock when emode state was last updated"],"type":"i64"},{"name":"flags","docs":["EMODE_ON (1) - If set, at least one entry is configured","2, 4, 8, etc, Reserved for future use"],"type":"u64"},{"name":"emode_config","type":{"defined":{"name":"EmodeConfig"}}}]}},{"name":"ExecuteOrderBalanceRecord","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"bank","type":"pubkey"},{"name":"is_asset","type":"u8"},{"name":"_pad0","type":{"array":["u8",5]}},{"name":"tag","type":"u16"},{"name":"shares","type":{"defined":{"name":"WrappedI80F48"}}}]}},{"name":"ExecuteOrderRecord","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"order","type":"pubkey"},{"name":"executor","type":"pubkey"},{"name":"balance_states","type":{"array":[{"defined":{"name":"ExecuteOrderBalanceRecord"}},14]}},{"name":"active_balance_count","type":"u8"},{"name":"inactive_balance_count","type":"u8"},{"name":"_reserved0","type":{"array":["u8",6]}},{"name":"order_start_health","type":{"defined":{"name":"WrappedI80F48"}}}]}},{"name":"FeeState","docs":["Unique per-program. The Program Owner uses this account to administrate fees collected by the protocol"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"key","docs":["The fee state's own key. A PDA derived from just `b\"feestate\"`"],"type":"pubkey"},{"name":"global_fee_admin","docs":["Can modify fees"],"type":"pubkey"},{"name":"global_fee_wallet","docs":["The base wallet for all protocol fees. All SOL fees go to this wallet. All non-SOL fees go","to the cannonical ATA of this wallet for that asset."],"type":"pubkey"},{"name":"placeholder0","type":"u64"},{"name":"bank_init_flat_sol_fee","docs":["Flat fee assessed when a new bank is initialized, in lamports.","* In SOL, in native decimals."],"type":"u32"},{"name":"bump_seed","type":"u8"},{"name":"_padding0","type":{"array":["u8",3]}},{"name":"liquidation_max_fee","docs":["Liquidators can claim at this premium, when liquidating an asset in receivership","liquidation, e.g. (1 + this) * amount repaid >= asset seized","* A percentage"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"program_fee_fixed","docs":["Fee collected by the program owner from all groups","* A percentage"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"program_fee_rate","docs":["Fee collected by the program owner from all groups","* A percentage"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"panic_state","docs":["When the global admin pauses the protocol in the event of an emergency, information about","the pause duration will be stored here and propagated to groups."],"type":{"defined":{"name":"PanicState"}}},{"name":"placeholder1","type":"u64"},{"name":"liquidation_flat_sol_fee","docs":["Flat fee assessed for insurance/program use when a liquidation is executed","* In SOL, in native decimals."],"type":"u32"},{"name":"order_init_flat_sol_fee","docs":["Flat fee assessed for preventing spam use when creating an order","* In SOL, in native decimals."],"type":"u32"},{"name":"order_execution_max_fee","docs":["Take-profit Orders can be executed at this premium, which Keepers are allowed to keep (no","pun intended) e.g. (1 + this) * amount repaid >= asset seized","* A percentage"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"_reserved1","type":{"array":["u8",32]}}]}},{"name":"FeeStateCache","docs":["Cached fee configuration propagated from the global FeeState"],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"global_fee_wallet","docs":["The wallet that receives program-level fees"],"type":"pubkey"},{"name":"program_fee_fixed","docs":["Fixed fee APR charged to borrowers (program-level)"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"program_fee_rate","docs":["Proportional fee rate on interest (program-level)"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"last_update","docs":["Unix timestamp of the last fee state propagation"],"type":"i64"}]}},{"name":"GroupEventHeader","type":{"kind":"struct","fields":[{"name":"signer","type":{"option":"pubkey"}},{"name":"marginfi_group","type":"pubkey"}]}},{"name":"GroupRateLimiter","docs":["Per-group rate limiting configuration and state.","Tracks aggregate net outflow in USD."],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"hourly","docs":["Hourly window rate limiter (USD)."],"type":{"defined":{"name":"RateLimitWindow"}}},{"name":"daily","docs":["Daily window rate limiter (USD)."],"type":{"defined":{"name":"RateLimitWindow"}}}]}},{"name":"HealthCache","docs":["A read-only cache of the internal risk engine's information. Only valid in borrow/withdraw if","the tx does not fail. To see the state in any context, e.g. to figure out if the risk engine is","failing due to some bad price information, use `pulse_health`."],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"asset_value","docs":["Internal risk engine asset value, using initial weight (e.g. what is used for borrowing","purposes), with all confidence adjustments, and other discounts on price.","* Uses EMA price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_value","docs":["Internal risk engine liability value, using initial weight (e.g. what is used for borrowing","purposes), with all confidence adjustments, and other discounts on price.","* Uses EMA price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_value_maint","docs":["Internal risk engine asset value, using maintenance weight (e.g. what is used for","liquidation purposes), with all confidence adjustments.","* Zero if the risk engine failed to load","* Uses SPOT price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_value_maint","docs":["Internal risk engine liability value, using maintenance weight (e.g. what is used for","liquidation purposes), with all confidence adjustments.","* Zero if the risk engine failed to load","* Uses SPOT price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_value_equity","docs":["The \"true\" value of assets without any confidence or weight adjustments. Internally, used","only for bankruptcies.","* Zero if the risk engine failed to load","* Uses EMA price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_value_equity","docs":["The \"true\" value of liabilities without any confidence or weight adjustments.","Internally, used only for bankruptcies.","* Zero if the risk engine failed to load","* Uses EMA price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"timestamp","docs":["Unix timestamp from the system clock when this cache was last updated"],"type":"i64"},{"name":"flags","docs":["The flags that indicate the state of the health cache. This is a u32 bitfield, where each","bit represents a flag.","","* HEALTHY = 1 - If set, the account cannot be liquidated. If 0, the account is unhealthy and","can be liquidated.","* ENGINE STATUS = 2 - If set, the engine did not error during the last health pulse. If 0,","the engine would have errored and this cache is likely invalid. `RiskEngineInitRejected`","is ignored and will allow the flag to be set anyways.","* ORACLE OK = 4 - If set, the engine did not error due to an oracle issue. If 0, engine was","passed a bad bank or oracle account, or an oracle was stale. Check the order in which","accounts were passed and ensure each balance has the correct banks/oracles, and that","oracle cranks ran recently enough. Check `internal_err` and `err_index` for more details","in some circumstances. Invalid if generated after borrow/withdraw (these instructions will","ignore oracle issues if health is still satisfactory with some balance zeroed out).","* 8, 16, 32, 64, 128, etc - reserved for future use"],"type":"u32"},{"name":"mrgn_err","docs":["If the engine errored, look here for the error code. If the engine returns ok, you may also","check here to see if the risk engine rejected this tx (3009)."],"type":"u32"},{"name":"prices","docs":["Each price corresponds to that index of Balances in the LendingAccount. Useful for debugging","or liquidator consumption, to determine how a user's position is priced internally.","* An f64 stored as bytes"],"type":{"array":[{"array":["u8",8]},16]}},{"name":"internal_err","docs":["Errors in asset oracles are ignored (with prices treated as zero). If you see a zero price","and the `ORACLE_OK` flag is not set, check here to see what error was ignored internally."],"type":"u32"},{"name":"err_index","docs":["Index in `balances` where `internal_err` appeared"],"type":"u8"},{"name":"program_version","docs":["Since 0.1.3, the version will be encoded here. See PROGRAM_VERSION."],"type":"u8"},{"name":"pad0","type":{"array":["u8",2]}},{"name":"internal_liq_err","docs":["Error code from the liquidation health check during the last health pulse (0 if none)"],"type":"u32"},{"name":"internal_bankruptcy_err","docs":["Error code from the bankruptcy check during the last health pulse (0 if none)"],"type":"u32"},{"name":"reserved0","type":{"array":["u8",32]}},{"name":"reserved1","type":{"array":["u8",16]}}]}},{"name":"HealthPulseEvent","type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"},{"name":"health_cache","type":{"defined":{"name":"HealthCache"}}}]}},{"name":"InterestRateConfig","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"optimal_utilization_rate","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"plateau_interest_rate","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"max_interest_rate","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"insurance_fee_fixed_apr","docs":["Goes to insurance, funds `collected_insurance_fees_outstanding`"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"insurance_ir_fee","docs":["Goes to insurance, funds `collected_insurance_fees_outstanding`"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"protocol_fixed_fee_apr","docs":["Earned by the group, goes to `collected_group_fees_outstanding`"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"protocol_ir_fee","docs":["Earned by the group, goes to `collected_group_fees_outstanding`"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"protocol_origination_fee","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"zero_util_rate","docs":["The base rate at utilization = 0","* a %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX"],"type":"u32"},{"name":"hundred_util_rate","docs":["The base rate at utilization = 100","* a %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX"],"type":"u32"},{"name":"points","docs":["The base rate at various points between 0 and 100%, exclusive. Essentially a piece-wise","linear curve.","* always in ascending order, e.g. points[0] = first kink point, points[1] = second kink","point, and so forth.","* points where util = 0 are unused"],"type":{"array":[{"defined":{"name":"RatePoint"}},5]}},{"name":"curve_type","docs":["Determines which interest rate curve implementation is active. 0 (INTEREST_CURVE_LEGACY) =","legacy three point curve, 1 (INTEREST_CURVE_SEVEN_POINT) = multi-point curve."],"type":"u8"},{"name":"_pad0","type":{"array":["u8",7]}},{"name":"_padding1","type":{"array":["u8",32]}},{"name":"_padding2","type":{"array":["u8",16]}},{"name":"_padding3","type":{"array":["u8",8]}}]}},{"name":"InterestRateConfigCompact","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"insurance_fee_fixed_apr","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"insurance_ir_fee","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"protocol_fixed_fee_apr","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"protocol_ir_fee","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"protocol_origination_fee","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"zero_util_rate","docs":["The base rate at utilization = 0","* a %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX"],"type":"u32"},{"name":"hundred_util_rate","docs":["The base rate at utilization = 100","* a %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX"],"type":"u32"},{"name":"points","docs":["The base rate at various points between 0 and 100%, exclusive. Essentially a piece-wise","linear curve.","* always in ascending order, e.g. points[0] = first kink point, points[1] = second kink","point, and so forth.","* points where util = 0 are unused"],"type":{"array":[{"defined":{"name":"RatePoint"}},5]}}]}},{"name":"InterestRateConfigOpt","type":{"kind":"struct","fields":[{"name":"insurance_fee_fixed_apr","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"insurance_ir_fee","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"protocol_fixed_fee_apr","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"protocol_ir_fee","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"protocol_origination_fee","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"zero_util_rate","docs":["The base rate at utilization = 0","* a %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX"],"type":{"option":"u32"}},{"name":"hundred_util_rate","docs":["The base rate at utilization = 100","* a %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX"],"type":{"option":"u32"}},{"name":"points","docs":["The base rate at various points between 0 and 100%, exclusive. Essentially a piece-wise","linear curve.","* always in ascending order, e.g. points[0] = first kink point, points[1] = second kink","point, and so forth.","* points where util = 0 are unused"],"type":{"option":{"array":[{"defined":{"name":"RatePoint"}},5]}}}]}},{"name":"JuplendConfigCompact","docs":["Used to configure JupLend banks. A simplified version of `BankConfigCompact` which omits most","values related to interest since JupLend banks cannot earn interest or be borrowed against.","","Note: JupLend banks do not take an Operational State, they always start in `Paused` state and","are set to `Operational` via `juplend_init_position` (seed deposit + protocol fToken vault)."],"type":{"kind":"struct","fields":[{"name":"oracle","type":"pubkey"},{"name":"asset_weight_init","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_weight_maint","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"deposit_limit","type":"u64"},{"name":"oracle_setup","docs":["Either `JuplendPythPull` or `JuplendSwitchboardPull`"],"type":{"defined":{"name":"OracleSetup"}}},{"name":"risk_tier","docs":["Isolated or Collateral"],"type":{"defined":{"name":"RiskTier"}}},{"name":"config_flags","docs":["Config flags for future-proofing, currently ignored"],"type":"u8"},{"name":"total_asset_value_init_limit","docs":["In $"],"type":"u64"},{"name":"oracle_max_age","docs":["In seconds"],"type":"u16"},{"name":"oracle_max_confidence","docs":["Oracle confidence threshold (0 = use default 10%)"],"type":"u32"}]}},{"name":"KaminoConfigCompact","docs":["Used to configure Kamino banks. A simplified version of `BankConfigCompact` which omits most","values related to interest since Kamino banks cannot earn interest or be borrowed against."],"type":{"kind":"struct","fields":[{"name":"oracle","type":"pubkey"},{"name":"asset_weight_init","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_weight_maint","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"deposit_limit","type":"u64"},{"name":"oracle_setup","docs":["Either `KaminoPythPush` or `KaminoSwitchboardPull`"],"type":{"defined":{"name":"OracleSetup"}}},{"name":"operational_state","docs":["Bank operational state - allows starting banks in paused state"],"type":{"defined":{"name":"BankOperationalState"}}},{"name":"risk_tier","docs":["Risk tier - determines if assets can be borrowed in isolation"],"type":{"defined":{"name":"RiskTier"}}},{"name":"config_flags","docs":["Config flags for future-proofing"],"type":"u8"},{"name":"total_asset_value_init_limit","type":"u64"},{"name":"oracle_max_age","docs":["Currently unused: Kamino's oracle age applies to kamino banks."],"type":"u16"},{"name":"oracle_max_confidence","docs":["Oracle confidence threshold (0 = use default 10%)"],"type":"u32"}]}},{"name":"KeeperCloseOrderEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"order","type":"pubkey"}]}},{"name":"Lending","docs":["Minimal representation of the on-chain JupLend `Lending` account.","","Notes:","- We intentionally use a **zero-copy** layout here to match how other integrations load large","external accounts (and to avoid paying Borsh (de)serialization cost on every access).","- `repr(C, packed)` keeps the byte layout identical to a field-by-field serialization","(i.e. no implicit padding). This is important because `Pubkey` has alignment=1 while `u64`","has alignment=8; using plain `repr(C)` would insert padding before the first `u64`."],"serialization":"bytemuck","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"f_token_mint","type":"pubkey"},{"name":"lending_id","type":"u16"},{"name":"decimals","docs":["number of decimals for the fToken, same as underlying mint"],"type":"u8"},{"name":"rewards_rate_model","docs":["PDA of rewards rate model (LRRM)"],"type":"pubkey"},{"name":"liquidity_exchange_price","docs":["exchange price in the liquidity layer (no rewards)"],"type":"u64"},{"name":"token_exchange_price","docs":["exchange price between fToken and underlying (with rewards)"],"type":"u64"},{"name":"last_update_timestamp","docs":["unix timestamp when exchange prices were updated last"],"type":"u64"},{"name":"token_reserves_liquidity","type":"pubkey"},{"name":"supply_position_on_liquidity","type":"pubkey"},{"name":"bump","type":"u8"}]}},{"name":"LendingAccount","docs":["The lending account holds up to 16 balance positions for a user."],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"balances","docs":["Array of balance positions (max 16). Sorted in descending order by bank_pk."],"type":{"array":[{"defined":{"name":"Balance"}},16]}},{"name":"last_tag_used","docs":["Last allocated balance tag (u16), used to find the next unused tag."],"type":"u16"},{"name":"_pad1","docs":["Reserved for future use"],"type":{"array":["u8",6]}},{"name":"_padding","docs":["Reserved for future use"],"type":{"array":["u64",7]}}]}},{"name":"LendingAccountBorrowEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"LendingAccountDepositEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"LendingAccountLiquidateEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"liquidatee_marginfi_account","type":"pubkey"},{"name":"liquidatee_marginfi_account_authority","type":"pubkey"},{"name":"asset_bank","type":"pubkey"},{"name":"asset_mint","type":"pubkey"},{"name":"liability_bank","type":"pubkey"},{"name":"liability_mint","type":"pubkey"},{"name":"liquidatee_pre_health","type":"f64"},{"name":"liquidatee_post_health","type":"f64"},{"name":"pre_balances","type":{"defined":{"name":"LiquidationBalances"}}},{"name":"post_balances","type":{"defined":{"name":"LiquidationBalances"}}}]}},{"name":"LendingAccountRepayEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"amount","type":"u64"},{"name":"close_balance","type":"bool"}]}},{"name":"LendingAccountWithdrawEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"amount","type":"u64"},{"name":"close_balance","type":"bool"}]}},{"name":"LendingPoolBankAccrueInterestEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"delta","type":"u64"},{"name":"fees_collected","type":"f64"},{"name":"insurance_collected","type":"f64"}]}},{"name":"LendingPoolBankCollectFeesEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"group_fees_collected","type":"f64"},{"name":"group_fees_outstanding","type":"f64"},{"name":"insurance_fees_collected","type":"f64"},{"name":"insurance_fees_outstanding","type":"f64"}]}},{"name":"LendingPoolBankConfigureEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"config","type":{"defined":{"name":"BankConfigOpt"}}}]}},{"name":"LendingPoolBankConfigureFrozenEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"deposit_limit","type":"u64"},{"name":"borrow_limit","type":"u64"}]}},{"name":"LendingPoolBankConfigureOracleEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"oracle_setup","type":"u8"},{"name":"oracle","type":"pubkey"}]}},{"name":"LendingPoolBankCreateEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"}]}},{"name":"LendingPoolBankHandleBankruptcyEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"bad_debt","type":"f64"},{"name":"covered_amount","type":"f64"},{"name":"socialized_amount","type":"f64"}]}},{"name":"LendingPoolBankSetFixedOraclePriceEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"price","type":{"defined":{"name":"WrappedI80F48"}}}]}},{"name":"LendingPoolSuperAdminDepositEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"transfer_amount","docs":["Amount requested in SPL transfer instruction."],"type":"u64"},{"name":"vault_inflow_amount","docs":["Assumed vault inflow. Token-2022 transfer fees are not handled by this instruction path."],"type":"u64"}]}},{"name":"LendingPoolSuperAdminWithdrawEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"vault_outflow_amount","type":"u64"}]}},{"name":"LiquidationBalances","type":{"kind":"struct","fields":[{"name":"liquidatee_asset_balance","type":"f64"},{"name":"liquidatee_liability_balance","type":"f64"},{"name":"liquidator_asset_balance","type":"f64"},{"name":"liquidator_liability_balance","type":"f64"}]}},{"name":"LiquidationCache","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"asset_value_maint","docs":["Internal risk engine asset value snapshot taken when liquidation begins, using maintenance","weight with all confidence adjustments.","* Uses SPOT price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_value_maint","docs":["Internal risk engine liability value snapshot taken when liquidation begins, using","maintenance weight with all confidence adjustments.","* Uses SPOT price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_value_equity","docs":["Actual cash value of assets pre-liquidation (inclusive of price adjustment for oracle","confidence, but without any weights)","* Liquidator is allowed to seize up to `liability_value_equity` - this amount","* Uses EMA price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_value_equity","docs":["Actual cash value of liabilities pre-liquidation (inclusive of price adjustment for oracle","confidence, but without any weights)","* Liquidator is allowed to seize up to this amount - `asset_value_equity`","* Uses EMA price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"_placeholder","type":"u64"},{"name":"_reserved0","type":{"array":["u8",32]}}]}},{"name":"LiquidationEntry","docs":["Used to record key details of the last few liquidation events on the account"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"asset_amount_seized","docs":["Dollar amount seized","* An f64 stored as bytes"],"type":{"array":["u8",8]}},{"name":"liab_amount_repaid","docs":["Dollar amount repaid","* An f64 stored as bytes"],"type":{"array":["u8",8]}},{"name":"placeholder0","type":"u64"},{"name":"timestamp","type":"i64"},{"name":"_reserved0","type":{"array":["u8",16]}}]}},{"name":"LiquidationReceiverEvent","type":{"kind":"struct","fields":[{"name":"marginfi_account","type":"pubkey"},{"name":"liquidation_receiver","type":"pubkey"},{"name":"liquidatee_assets_seized","type":"f64"},{"name":"liquidatee_liability_repaid","type":"f64"},{"name":"lamps_fee_paid","type":"u32"}]}},{"name":"LiquidationRecord","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"key","docs":["This account's own key. A PDA derived from `marginfi_account`"],"type":"pubkey"},{"name":"marginfi_account","docs":["Account this record tracks"],"type":"pubkey"},{"name":"record_payer","docs":["The key that paid to create this account. At some point, we may allow this wallet to reclaim","the rent paid to open a record."],"type":"pubkey"},{"name":"liquidation_receiver","docs":["The liquidator taking receivership of the `marginfi_account` to complete a liquidation. Pays","the liquidation fee.","* Always pubkey default unless actively within a liquidation event."],"type":"pubkey"},{"name":"entries","docs":["Basic historical data for the last few liquidation events on this account"],"type":{"array":[{"defined":{"name":"LiquidationEntry"}},4]}},{"name":"cache","type":{"defined":{"name":"LiquidationCache"}}},{"name":"_reserved0","type":{"array":["u8",64]}},{"name":"_reserved2","type":{"array":["u8",16]}},{"name":"_reserved3","type":{"array":["u8",8]}}]}},{"name":"MarginfiAccount","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"group","type":"pubkey"},{"name":"authority","type":"pubkey"},{"name":"lending_account","type":{"defined":{"name":"LendingAccount"}}},{"name":"account_flags","docs":["The flags that indicate the state of the account. This is u64 bitfield, where each bit","represents a flag.","","Flags:MarginfiAccount","- 1: `ACCOUNT_DISABLED` - Indicates that the account is disabled and no further actions can","be taken on it.","- 2: `ACCOUNT_IN_FLASHLOAN` - Only set when an account is within a flash loan, e.g. when","start_flashloan is called, then unset when the flashloan ends.","- 4: `ACCOUNT_FLAG_DEPRECATED` - Deprecated, available for future use","- 8: `ACCOUNT_TRANSFER_AUTHORITY_DEPRECATED` - the admin has flagged with account to be","moved, original owner can now call `set_account_transfer_authority`","- 16: `ACCOUNT_IN_RECEIVERSHIP` - the account is eligible to be liquidated and has entered","receivership, a liquidator is able to control borrows and withdraws until the end of the","tx. This flag will only appear within a tx.","- 32: `ACCOUNT_IN_DELEVERAGE - the account is being deleveraged by the risk admin","- 64: `ACCOUNT_FROZEN` - the admin has frozen the account; only the group admin may perform","actions until unfrozen."],"type":"u64"},{"name":"emissions_destination_account","docs":["Wallet whose canonical ATA receives off-chain emissions distributions."],"type":"pubkey"},{"name":"health_cache","type":{"defined":{"name":"HealthCache"}}},{"name":"migrated_from","docs":["If this account was migrated from another one, store the original account key"],"type":"pubkey"},{"name":"migrated_to","docs":["If this account has been migrated to another one, store the destination account key"],"type":"pubkey"},{"name":"last_update","docs":["Unix timestamp (u64) of the last account interaction. Note: Bank.last_update uses i64."],"type":"u64"},{"name":"account_index","docs":["If a PDA-based account, the account index, a seed used to derive the PDA that can be chosen","arbitrarily (0.1.5 or later). Otherwise, does nothing."],"type":"u16"},{"name":"third_party_index","docs":["If a PDA-based account (0.1.5 or later), a \"vendor specific\" id. Values < PDA_FREE_THRESHOLD","can be used by anyone with no restrictions. Values >= PDA_FREE_THRESHOLD can only be used by","a particular program via CPI. These values require being added to a list, contact us for","more details. For legacy non-pda accounts, does nothing.","","Note: use a unique seed to tag accounts related to some particular program or campaign so","you can easily fetch them all later."],"type":"u16"},{"name":"bump","docs":["This account's bump, if a PDA-based account (0.1.5 or later). Otherwise, does nothing."],"type":"u8"},{"name":"_pad0","type":{"array":["u8",3]}},{"name":"liquidation_record","docs":["Stores information related to liquidations made against this account. A pda of this","account's key, and \"liq_record\"","* Typically pubkey default if this account has never been liquidated or close to liquidation","* Opening this account is permissionless. Typically the liquidator pays, but e.g. we may","also charge the user if they are opening a risky position on the front end."],"type":"pubkey"},{"name":"_padding0","type":{"array":["u64",7]}}]}},{"name":"MarginfiAccountCloseOrderEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"order","type":"pubkey"}]}},{"name":"MarginfiAccountCreateEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}}]}},{"name":"MarginfiAccountFreezeEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"frozen","type":"bool"}]}},{"name":"MarginfiAccountPlaceOrderEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"order","type":"pubkey"},{"name":"trigger","type":{"defined":{"name":"OrderTriggerType"}}},{"name":"stop_loss","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"take_profit","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"tags","type":{"array":["u16",2]}}]}},{"name":"MarginfiAccountTransferToNewAccount","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"old_account","type":"pubkey"},{"name":"old_account_authority","type":"pubkey"},{"name":"new_account_authority","type":"pubkey"}]}},{"name":"MarginfiGroup","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"admin","docs":["Broadly able to modify anything, and can set/remove other admins at will."],"type":"pubkey"},{"name":"group_flags","docs":["Bitmask for group settings flags.","* Bit 0 (1): `PROGRAM_FEES_ENABLED` — If set, program-level fees are enabled.","* Bits 1-63: Reserved for future use."],"type":"u64"},{"name":"fee_state_cache","docs":["Caches information from the global `FeeState` so the FeeState can be omitted on certain ixes"],"type":{"defined":{"name":"FeeStateCache"}}},{"name":"banks","docs":["For groups initialized in versions 0.1.2 or greater, this is an authoritative count","of the number of banks under this group. For groups initialized prior to 0.1.2,","a non-authoritative count of the number of banks initiated after 0.1.2 went live."],"type":"u16"},{"name":"pad0","type":{"array":["u8",6]}},{"name":"emode_admin","docs":["This admin can configure collateral ratios above (but not below) the collateral ratio of","certain banks, e.g. allow SOL to count as 90% collateral when borrowing an LST instead of","the default rate."],"type":"pubkey"},{"name":"delegate_curve_admin","docs":["Can modify the fields in `config.interest_rate_config` but nothing else, for every bank","under this group"],"type":"pubkey"},{"name":"delegate_limit_admin","docs":["Can modify the `deposit_limit`, `borrow_limit`, `total_asset_value_init_limit` but nothing","else, for every bank under this group"],"type":"pubkey"},{"name":"delegate_emissions_admin","docs":["Can modify the emissions `flags`, `emissions_rate` and `emissions_mint`, but nothing else,","for every bank under this group"],"type":"pubkey"},{"name":"panic_state_cache","docs":["When program keeper temporarily puts the program into panic mode, information about the","duration of the lockup will be available here."],"type":{"defined":{"name":"PanicStateCache"}}},{"name":"deleverage_withdraw_window_cache","docs":["Keeps track of the liquidity withdrawn from the group over the day as a result of","deleverages. Used as a protection mechanism against too big (and unwanted) withdrawals (e.g.","when the risk admin is compromised)."],"type":{"defined":{"name":"WithdrawWindowCache"}}},{"name":"risk_admin","docs":["Can run bankruptcy and forced deleverage ixes to e.g. sunset risky/illiquid assets"],"type":"pubkey"},{"name":"metadata_admin","docs":["Can modify a Bank's metadata, and nothing else."],"type":"pubkey"},{"name":"emode_max_init_leverage","docs":["Maximum leverage allowed for emode positions (initial margin), stored as u32 basis.","Use `u32_to_basis` to convert to I80F48. Range: 1-100."],"type":"u32"},{"name":"emode_max_maint_leverage","docs":["Maximum leverage allowed for emode positions (maintenance margin), stored as u32 basis.","Must be > emode_max_init_leverage. Range: 1-100."],"type":"u32"},{"name":"_padding","docs":["Reserved for future use"],"type":{"array":["u8",8]}},{"name":"rate_limiter","docs":["Rate limiter for controlling aggregate withdraw/borrow outflow across all banks.","Tracks net outflow in USD."],"type":{"defined":{"name":"GroupRateLimiter"}}},{"name":"rate_limiter_last_admin_update_slot","docs":["Last slot covered by an admin group rate limiter aggregation update."],"type":"u64"},{"name":"rate_limiter_last_admin_update_seq","docs":["Monotonic sequence number for admin group rate limiter updates.","This is used to enforce strict ordering and prevent duplicate/replayed batches","when slot ranges overlap or multiple updates happen in the same slot."],"type":"u64"},{"name":"deleverage_withdraw_last_admin_update_slot","docs":["Last slot covered by an admin deleverage withdraw-limit aggregation update."],"type":"u64"},{"name":"deleverage_withdraw_last_admin_update_seq","docs":["Monotonic sequence number for admin deleverage withdraw-limit updates."],"type":"u64"},{"name":"delegate_flow_admin","docs":["Can modify flow-control status for the group, i.e. update the withdraw caches with flow","information from banks. Typically this is a hot wallet that lives in e.g. some cron job. If","compromised, flow control can be effectively disabled until the admin is restored, which","does not itself compromise any funds, and is merely annoying."],"type":"pubkey"},{"name":"_padding_0","type":{"array":[{"array":["u64",2]},2]}},{"name":"_padding_1","type":{"array":[{"array":["u64",2]},32]}}]}},{"name":"MarginfiGroupConfigureEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"admin","type":{"option":"pubkey"}},{"name":"flags","type":"u64"}]}},{"name":"MarginfiGroupCreateEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}}]}},{"name":"MinimalObligation","docs":["A minimal copy of Kamino's Obligation for zero-copy deserialization"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"tag","type":"u64"},{"name":"last_update_slot","docs":["Kamino obligations are only good for one slot, e.g. `refresh_obligation` must have run within the","same slot as any ix that needs a non-stale obligation e.g. withdraw."],"type":"u64"},{"name":"last_update_stale","docs":["True if the obligation is stale, which will cause various ixes like withdraw to fail. Typically","set to true in any tx that modifies obligation balance, and set to false at the end of a","successful `refresh_obligation`","* 0 = false, 1 = true"],"type":"u8"},{"name":"last_update_price_status","docs":["Each bit represents a passed check in price status.","* 63 = all checks passed","","Otherwise:","* PRICE_LOADED = 0b_0000_0001; // 1","* PRICE_AGE_CHECKED = 0b_0000_0010; // 2","* TWAP_CHECKED = 0b_0000_0100; // 4","* TWAP_AGE_CHECKED = 0b_0000_1000; // 8","* HEURISTIC_CHECKED = 0b_0001_0000; // 16","* PRICE_USAGE_ALLOWED = 0b_0010_0000; // 32"],"type":"u8"},{"name":"last_update_placeholder","type":{"array":["u8",6]}},{"name":"lending_market","type":"pubkey"},{"name":"owner","docs":["For mrgn banks, the bank's Liquidity Vault Authority (a pda which can be derived if the bank","key is known)"],"type":"pubkey"},{"name":"deposits","type":{"array":[{"defined":{"name":"MinimalObligationCollateral"}},8]}},{"name":"lowest_reserve_deposit_liquidation_ltv","type":"u64"},{"name":"deposited_value_sf","type":{"array":["u8",16]}},{"name":"padding_part1","type":{"array":["u8",512]}},{"name":"padding_part2","type":{"array":["u8",512]}},{"name":"padding_part3","type":{"array":["u8",512]}},{"name":"padding_part4","type":{"array":["u8",512]}},{"name":"padding_part5a","type":{"array":["u8",64]}},{"name":"padding_part5c","type":{"array":["u8",24]}}]}},{"name":"MinimalObligationCollateral","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"deposit_reserve","type":"pubkey"},{"name":"deposited_amount","docs":["In collateral token (NOT liquidity token), use `collateral_to_liquidity` to convert back to","liquidity token!","* Always 6 decimals"],"type":"u64"},{"name":"market_value_sf","docs":["* In dollars, based on last oracle price update","* Actually an I68F60, stored as a u128 (i.e. BN) in Kamino.","* A float (arbitrary decimals)"],"type":{"array":["u8",16]}},{"name":"borrowed_amount_against_this_collateral_in_elevation_group","type":"u64"},{"name":"padding","type":{"array":["u64",9]}}]}},{"name":"MinimalReserve","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"version","type":"u64"},{"name":"slot","docs":["Kamino reserves are only good for one slot, e.g. `refresh_reserve` must have run within the","same slot as any ix that needs a non-stale reserve e.g. withdraw."],"type":"u64"},{"name":"stale","docs":["True if the reserve is stale, which will cause various ixes like withdraw to fail. Typically","set to true in any tx that modifies reserve balance, and set to false at the end of a","successful `refresh_reserve`","* 0 = false, 1 = true"],"type":"u8"},{"name":"price_status","docs":["Each bit represents a passed check in price status.","* 63 = all checks passed","","Otherwise:","* PRICE_LOADED = 0b_0000_0001; // 1","* PRICE_AGE_CHECKED = 0b_0000_0010; // 2","* TWAP_CHECKED = 0b_0000_0100; // 4","* TWAP_AGE_CHECKED = 0b_0000_1000; // 8","* HEURISTIC_CHECKED = 0b_0001_0000; // 16","* PRICE_USAGE_ALLOWED = 0b_0010_0000; // 32"],"type":"u8"},{"name":"placeholder","type":{"array":["u8",6]}},{"name":"lending_market","type":"pubkey"},{"name":"farm_collateral","type":"pubkey"},{"name":"farm_debt","type":"pubkey"},{"name":"mint_pubkey","type":"pubkey"},{"name":"supply_vault","docs":["* A PDA"],"type":"pubkey"},{"name":"fee_vault","docs":["* A PDA"],"type":"pubkey"},{"name":"available_amount","docs":["In simple terms: (amount in supply vault - outstanding borrows)","* In token, with `mint_decimals`"],"type":"u64"},{"name":"borrowed_amount_sf","docs":["* In token, with `mint_decimals`","* Actually an I68F60, stored as a u128 (i.e. BN) in Kamino."],"type":{"array":["u8",16]}},{"name":"market_price_sf","docs":["* Actually an I68F60, stored as a u128 (i.e. BN) in Kamino."],"type":{"array":["u8",16]}},{"name":"market_price_last_updated_ts","type":"u64"},{"name":"mint_decimals","type":"u64"},{"name":"deposit_limit_crossed_timestamp","type":"u64"},{"name":"borrow_limit_crossed_timestamp","type":"u64"},{"name":"cumulative_borrow_rate_bsf","type":{"array":["u8",48]}},{"name":"accumulated_protocol_fees_sf","docs":["* In token, with `mint_decimals`","* Actually an I68F60, stored as a u128 (i.e. BN) in Kamino."],"type":{"array":["u8",16]}},{"name":"accumulated_referrer_fees_sf","docs":["* In token, with `mint_decimals`","* Actually an I68F60, stored as a u128 (i.e. BN) in Kamino."],"type":{"array":["u8",16]}},{"name":"pending_referrer_fees_sf","docs":["* In token, with `mint_decimals`","* Actually an I68F60, stored as a u128 (i.e. BN) in Kamino."],"type":{"array":["u8",16]}},{"name":"absolute_referral_rate_sf","docs":["* In token, with `mint_decimals`","* Actually an I68F60, stored as a u128 (i.e. BN) in Kamino."],"type":{"array":["u8",16]}},{"name":"token_program","docs":["Token or Token22. If token22, note that Kamino does not support all Token22 extensions."],"type":"pubkey"},{"name":"padding2_part1","type":{"array":["u8",256]}},{"name":"padding2_part2","type":{"array":["u8",128]}},{"name":"padding2_part3","type":{"array":["u8",24]}},{"name":"padding3","type":{"array":["u8",512]}},{"name":"padding_part1","type":{"array":["u8",512]}},{"name":"padding_part2","type":{"array":["u8",512]}},{"name":"padding_part3","type":{"array":["u8",128]}},{"name":"padding_part4","type":{"array":["u8",48]}},{"name":"collateral_mint_pubkey","docs":["Mints collateral tokens","* A PDA","* technically 6 decimals, but uses `mint_decimals` regardless for all purposes","* authority = lending_market_authority"],"type":"pubkey"},{"name":"mint_total_supply","docs":["Total number of collateral tokens","* uses `mint_decimals`, even though it's technically 6 decimals under the hood"],"type":"u64"},{"name":"collateral_supply_vault","docs":["* A PDA"],"type":"pubkey"},{"name":"padding1_reserve_collateral","type":{"array":["u8",512]}},{"name":"padding2_reserve_collateral","type":{"array":["u8",512]}},{"name":"padding4_part1","type":{"array":["u8",4096]}},{"name":"padding4_part2","type":{"array":["u8",512]}},{"name":"padding4_part3","type":{"array":["u8",256]}},{"name":"padding4_part4","type":{"array":["u8",64]}},{"name":"padding4_part5","type":{"array":["u8",32]}},{"name":"padding4_part6","type":{"array":["u8",8]}}]}},{"name":"MinimalSpotMarket","docs":["Minimal representation of Drift's SpotMarket account","Only includes the fields we actually need for marginfi integration","https://github.com/drift-labs/protocol-v2/tree/master/programs/drift/src/state/spot_market.rs#L35"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"pubkey","docs":["The address of the spot market. It is a pda of the market index"],"type":"pubkey"},{"name":"oracle","docs":["The oracle used to price the markets deposits/borrows"],"type":"pubkey"},{"name":"mint","docs":["The token mint of the market"],"type":"pubkey"},{"name":"vault","docs":["The vault used to store the market's deposits"],"type":"pubkey"},{"name":"_padding1","type":{"array":[{"array":["u64",4]},9]}},{"name":"_padding2","type":{"array":["u8",8]}},{"name":"deposit_balance","docs":["All the fields we need for testing (stored as raw bytes for simplicity)"],"type":{"array":["u8",16]}},{"name":"borrow_balance","type":{"array":["u8",16]}},{"name":"cumulative_deposit_interest","type":{"array":["u8",16]}},{"name":"cumulative_borrow_interest","type":{"array":["u8",16]}},{"name":"_padding3","type":{"array":["u64",9]}},{"name":"last_interest_ts","docs":["Last time the cumulative deposit and borrow interest was updated","Offset: 568 bytes from start of struct (including discriminator)"],"type":"u64"},{"name":"_padding4","type":{"array":["u64",13]}},{"name":"decimals","type":"u32"},{"name":"market_index","type":"u16"},{"name":"_padding5","type":{"array":["u16",24]}},{"name":"_padding6","type":{"array":["u8",1]}},{"name":"pool_id","type":"u8"},{"name":"_padding7","docs":["Padding to reach 776 bytes total (including discriminator)"],"type":{"array":["u64",5]}}]}},{"name":"MinimalUser","docs":["Minimal representation of Drift's User account","Only includes the fields we actually need"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"authority","docs":["The owner/authority of the account"],"type":"pubkey"},{"name":"delegate","docs":["An addresses that can control the account on the authority's behalf"],"type":"pubkey"},{"name":"name","docs":["Encoded display name for the account"],"type":{"array":["u8",32]}},{"name":"spot_positions","docs":["The user's spot positions (8 positions)"],"type":{"array":[{"defined":{"name":"SpotPosition"}},8]}},{"name":"_padding1","docs":["Skip to the fields we need at the end"],"type":{"array":["u64",256]}},{"name":"_padding2","type":{"array":["u64",128]}},{"name":"_padding3","type":{"array":["u64",64]}},{"name":"_padding4","type":{"array":["u64",32]}},{"name":"_padding5","type":{"array":["u64",8]}},{"name":"_padding6","type":{"array":["u64",2]}},{"name":"_padding7","type":{"array":["u16",1]}},{"name":"sub_account_id","docs":["Sub account id for this user account"],"type":"u16"},{"name":"status","type":{"defined":{"name":"UserStatus"}}},{"name":"_padding8","type":{"array":["u8",27]}}]}},{"name":"OracleSetup","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"None"},{"name":"PythLegacy"},{"name":"SwitchboardV2"},{"name":"PythPushOracle"},{"name":"SwitchboardPull"},{"name":"StakedWithPythPush"},{"name":"KaminoPythPush"},{"name":"KaminoSwitchboardPull"},{"name":"Fixed"},{"name":"DriftPythPull"},{"name":"DriftSwitchboardPull"},{"name":"SolendPythPull"},{"name":"SolendSwitchboardPull"},{"name":"FixedKamino"},{"name":"FixedDrift"},{"name":"JuplendPythPull"},{"name":"JuplendSwitchboardPull"},{"name":"FixedJuplend"}]}},{"name":"Order","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"marginfi_account","type":"pubkey"},{"name":"stop_loss","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"take_profit","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"placeholder","docs":["Reserved for future use"],"type":"u64"},{"name":"max_slippage","docs":["* a %, as u32, out of 100%, e.g. 50% = .5 * u32::MAX"],"type":"u32"},{"name":"pad0","type":{"array":["u8",4]}},{"name":"tags","docs":["Active tags (currently 2). Remaining capacity is stored in padding for layout compatibility.","Padding byte `ORDER_TAG_PADDING - 1` stores the tag count for forward compatibility. (u16 *","2 = 4 bytes)"],"type":{"array":["u16",2]}},{"name":"pad1","type":{"array":["u8",4]}},{"name":"_tags_padding","type":{"array":["u8",32]}},{"name":"trigger","docs":["Stop Loss (0), Take Profit (1), or Both (2)"],"type":{"defined":{"name":"OrderTriggerType"}}},{"name":"bump","docs":["Bump to derive this pda"],"type":"u8"},{"name":"pad2","type":{"array":["u8",6]}},{"name":"_reserved1","type":{"array":[{"array":["u8",32]},4]}}]}},{"name":"OrderTrigger","repr":{"kind":"c"},"type":{"kind":"enum","variants":[{"name":"StopLoss","fields":[{"name":"threshold","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"max_slippage","type":"u32"}]},{"name":"TakeProfit","fields":[{"name":"threshold","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"max_slippage","type":"u32"}]},{"name":"Both","fields":[{"name":"stop_loss","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"take_profit","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"max_slippage","type":"u32"}]}]}},{"name":"OrderTriggerType","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"StopLoss"},{"name":"TakeProfit"},{"name":"Both"}]}},{"name":"PanicState","docs":["Panic state for emergency protocol pausing"],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"pause_flags","docs":["Whether the protocol is currently paused (1 = paused, 0 = not paused)"],"type":"u8"},{"name":"daily_pause_count","docs":["Number of times paused today (resets every 24 hours)"],"type":"u8"},{"name":"consecutive_pause_count","docs":["Number of consecutive pauses (resets when unpause happens)"],"type":"u8"},{"name":"_reserved","type":{"array":["u8",5]}},{"name":"pause_start_timestamp","docs":["Timestamp when the current pause started (0 if not paused)","* When a pause is extended before expiring, this could be in the future."],"type":"i64"},{"name":"last_daily_reset_timestamp","docs":["Timestamp of the last daily reset (for tracking daily pause count)"],"type":"i64"},{"name":"_reserved_space","docs":["Reserved for future use (making total struct 32 bytes)"],"type":{"array":["u8",8]}}]}},{"name":"PanicStateCache","docs":["Cached panic state information for fast checking during user operations"],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"pause_flags","docs":["Whether the protocol is currently paused (1 = paused, 0 = not paused)"],"type":"u8"},{"name":"_reserved","type":{"array":["u8",7]}},{"name":"pause_start_timestamp","docs":["Timestamp when the current pause started (0 if not paused)"],"type":"i64"},{"name":"last_cache_update","docs":["Timestamp when this cache was last updated"],"type":"i64"}]}},{"name":"RateLimitFlowEvent","docs":["Emitted when a bank-level inflow or outflow is recorded.","The delegate flow admin aggregates these off-chain and","updates the group rate limiter via `update_group_rate_limiter`."],"type":{"kind":"struct","fields":[{"name":"group","type":"pubkey"},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"flow_direction","docs":["0 = outflow (withdraw/borrow), 1 = inflow (deposit/repay)"],"type":"u8"},{"name":"native_amount","docs":["Amount in native tokens"],"type":"u64"},{"name":"mint_decimals","type":"u8"},{"name":"current_timestamp","docs":["Unix timestamp when the flow was recorded"],"type":"i64"}]}},{"name":"RateLimitWindow","docs":["A sliding window rate limiter that tracks net outflow over a time window.","Uses weighted blend of previous and current windows for smooth transitions.","","Net outflow = (withdraws + borrows) - (deposits + repays).","A negative net outflow increases remaining capacity for subsequent outflows."],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"max_outflow","docs":["Maximum net outflow allowed per window (0 = disabled).","For bank-level: denominated in native tokens.","For group-level: denominated in USD."],"type":"u64"},{"name":"window_duration","docs":["Window duration in seconds (e.g., 3600 for hourly, 86400 for daily)."],"type":"u64"},{"name":"window_start","docs":["Unix timestamp when the current window started."],"type":"i64"},{"name":"prev_window_outflow","docs":["Net outflow accumulated in the previous window.","Signed to allow tracking when inflows exceed outflows."],"type":"i64"},{"name":"cur_window_outflow","docs":["Net outflow accumulated in the current window.","Signed to allow tracking when inflows exceed outflows."],"type":"i64"}]}},{"name":"RatePoint","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"util","docs":["The utilization rate where `rate` applies","* a %, as u32, out of 100%, e.g. 50% = .5 * u32::MAX"],"type":"u32"},{"name":"rate","docs":["The base rate that applies","* a %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX"],"type":"u32"}]}},{"name":"RiskTier","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Collateral"},{"name":"Isolated"}]}},{"name":"SetKeeperCloseFlagsEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"bank_keys","type":{"option":{"vec":"pubkey"}}}]}},{"name":"SolendConfigCompact","docs":["Used to configure Solend banks. A simplified version of `BankConfigCompact` which omits most","values related to interest since Solend banks cannot earn interest or be borrowed against."],"type":{"kind":"struct","fields":[{"name":"oracle","type":"pubkey"},{"name":"asset_weight_init","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_weight_maint","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"deposit_limit","type":"u64"},{"name":"oracle_setup","docs":["Either `SolendPythPull` or `SolendSwitchboardPull`"],"type":{"defined":{"name":"OracleSetup"}}},{"name":"operational_state","docs":["Bank operational state - allows starting banks in paused state"],"type":{"defined":{"name":"BankOperationalState"}}},{"name":"risk_tier","docs":["Risk tier - determines if assets can be borrowed in isolation"],"type":{"defined":{"name":"RiskTier"}}},{"name":"config_flags","docs":["Config flags for future-proofing"],"type":"u8"},{"name":"total_asset_value_init_limit","type":"u64"},{"name":"oracle_max_age","type":"u16"},{"name":"oracle_max_confidence","docs":["Oracle confidence threshold (0 = use default 10%)"],"type":"u32"}]}},{"name":"SolendMinimalReserve","serialization":"bytemuck","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"last_update_slot","docs":["Last slot when supply and rates updated"],"type":"u64"},{"name":"last_update_stale","docs":["True when marked stale"],"type":"u8"},{"name":"lending_market","docs":["Lending market address"],"type":"pubkey"},{"name":"liquidity_mint_pubkey","type":"pubkey"},{"name":"liquidity_mint_decimals","type":"u8"},{"name":"liquidity_supply_pubkey","type":"pubkey"},{"name":"liquidity_pyth_oracle_pubkey","type":"pubkey"},{"name":"liquidity_switchboard_oracle_pubkey","type":"pubkey"},{"name":"liquidity_available_amount","type":"u64"},{"name":"liquidity_borrowed_amount_wads","type":{"array":["u8",16]}},{"name":"liquidity_cumulative_borrow_rate_wads","type":{"array":["u8",16]}},{"name":"liquidity_market_price","type":{"array":["u8",16]}},{"name":"collateral_mint_pubkey","type":"pubkey"},{"name":"collateral_mint_total_supply","type":"u64"},{"name":"collateral_supply_pubkey","type":"pubkey"},{"name":"config_optimal_utilization_rate","type":"u8"},{"name":"config_loan_to_value_ratio","type":"u8"},{"name":"config_liquidation_bonus","type":"u8"},{"name":"config_liquidation_threshold","type":"u8"},{"name":"_padding_to_fees_64","type":{"array":["u8",64]}},{"name":"_padding_to_fees_6","type":{"array":["u8",6]}},{"name":"liquidity_accumulated_protocol_fees_wads","type":{"array":["u8",16]}},{"name":"_padding_final_128","type":{"array":["u8",128]}},{"name":"_padding_final_64","type":{"array":["u8",64]}},{"name":"_padding_final_32","type":{"array":["u8",32]}},{"name":"_padding_final_6","type":{"array":["u8",6]}}]}},{"name":"SpotBalanceType","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Deposit"},{"name":"Borrow"}]}},{"name":"SpotPosition","docs":["Minimal representation of a spot position within a User account"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"scaled_balance","docs":["The scaled balance of the position.","* Precision: SPOT_BALANCE_PRECISION"],"type":"u64"},{"name":"open_bids","docs":["How many spot bids the user has open","* Precision: token mint precision"],"type":"i64"},{"name":"open_asks","docs":["How many spot asks the user has open","* Precision: token mint precision"],"type":"i64"},{"name":"cumulative_deposits","docs":["The cumulative deposits/borrows a user has made","* Precision: token mint precision"],"type":"i64"},{"name":"market_index","docs":["The market index of the corresponding spot market"],"type":"u16"},{"name":"balance_type","docs":["Whether the position is deposit or borrow"],"type":{"defined":{"name":"SpotBalanceType"}}},{"name":"open_orders","docs":["Number of open orders"],"type":"u8"},{"name":"padding","docs":["Padding"],"type":{"array":["u8",4]}}]}},{"name":"StakedSettings","docs":["Unique per-group. Staked Collateral banks created under a group automatically use these","settings. Groups that have not created this struct cannot create staked collateral banks. When","this struct updates, changes must be permissionlessly propagated to staked collateral banks.","Administrators can also edit the bank manually, i.e. with configure_bank, to temporarily make","changes such as raising the deposit limit for a single bank."],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"key","docs":["This account's own key. A PDA derived from `marginfi_group` and `STAKED_SETTINGS_SEED`"],"type":"pubkey"},{"name":"marginfi_group","docs":["Group for which these settings apply"],"type":"pubkey"},{"name":"oracle","docs":["Generally, the Pyth push oracle for SOL"],"type":"pubkey"},{"name":"asset_weight_init","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_weight_maint","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"deposit_limit","type":"u64"},{"name":"total_asset_value_init_limit","type":"u64"},{"name":"oracle_max_age","type":"u16"},{"name":"risk_tier","type":{"defined":{"name":"RiskTier"}}},{"name":"_pad0","type":{"array":["u8",5]}},{"name":"_reserved0","docs":["The following values are irrelevant because staked collateral positions do not support","borrowing."],"type":{"array":["u8",8]}},{"name":"_reserved1","type":{"array":["u8",32]}},{"name":"_reserved2","type":{"array":["u8",64]}}]}},{"name":"StakedSettingsConfig","type":{"kind":"struct","fields":[{"name":"oracle","type":"pubkey"},{"name":"asset_weight_init","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_weight_maint","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"deposit_limit","type":"u64"},{"name":"total_asset_value_init_limit","type":"u64"},{"name":"oracle_max_age","type":"u16"},{"name":"risk_tier","docs":["WARN: You almost certainly want \"Collateral\", using Isolated risk tier makes the asset","worthless as collateral, and is generally useful only when creating a staked collateral pool","for rewards purposes only."],"type":{"defined":{"name":"RiskTier"}}}]}},{"name":"StakedSettingsEditConfig","type":{"kind":"struct","fields":[{"name":"oracle","type":{"option":"pubkey"}},{"name":"asset_weight_init","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"asset_weight_maint","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"deposit_limit","type":{"option":"u64"}},{"name":"total_asset_value_init_limit","type":{"option":"u64"}},{"name":"oracle_max_age","type":{"option":"u16"}},{"name":"risk_tier","docs":["WARN: You almost certainly want \"Collateral\", using Isolated risk tier makes the asset","worthless as collateral, making all outstanding accounts eligible to be liquidated, and is","generally useful only when creating a staked collateral pool for rewards purposes only."],"type":{"option":{"defined":{"name":"RiskTier"}}}}]}},{"name":"UserStatus","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Active"},{"name":"BeingLiquidated"},{"name":"Bankrupt"},{"name":"ReduceOnly"},{"name":"AdvancedLp"},{"name":"ProtectedMakerOrders"}]}},{"name":"WithdrawWindowCache","docs":["Tracks deleverage withdrawal limits to protect against compromised risk admin"],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"daily_limit","docs":["Maximum USD value that can be withdrawn per day via deleverage (0 = no limit)"],"type":"u32"},{"name":"withdrawn_today","docs":["USD value withdrawn today via deleverage (approximate, rounded)"],"type":"u32"},{"name":"last_daily_reset_timestamp","docs":["Unix timestamp of the last daily counter reset"],"type":"i64"}]}},{"name":"WrappedI80F48","serialization":"bytemuck","repr":{"kind":"c","align":8},"type":{"kind":"struct","fields":[{"name":"value","type":{"array":["u8",16]}}]}}]} \ No newline at end of file diff --git a/idls/marinade_finance.MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD.json b/idls/marinade_finance.MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD.json new file mode 100644 index 0000000..8b5674a --- /dev/null +++ b/idls/marinade_finance.MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"marinade_finance","instructions":[{"name":"initialize","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"reservePda","isMut":false,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"msolMint","isMut":false,"isSigner":false},{"name":"operationalSolAccount","isMut":false,"isSigner":false},{"name":"liqPool","accounts":[{"name":"lpMint","isMut":false,"isSigner":false},{"name":"solLegPda","isMut":false,"isSigner":false},{"name":"msolLeg","isMut":false,"isSigner":false}]},{"name":"treasuryMsolAccount","isMut":false,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"data","type":{"defined":"InitializeData"}}]},{"name":"changeAuthority","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"adminAuthority","isMut":false,"isSigner":true}],"args":[{"name":"data","type":{"defined":"ChangeAuthorityData"}}]},{"name":"addValidator","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"managerAuthority","isMut":false,"isSigner":true},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"validatorVote","isMut":false,"isSigner":false},{"name":"duplicationFlag","isMut":true,"isSigner":false,"docs":["by initializing this account we mark the validator as added"]},{"name":"rentPayer","isMut":true,"isSigner":true},{"name":"clock","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"score","type":"u32"}]},{"name":"removeValidator","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"managerAuthority","isMut":false,"isSigner":true},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"duplicationFlag","isMut":true,"isSigner":false},{"name":"operationalSolAccount","isMut":true,"isSigner":false}],"args":[{"name":"index","type":"u32"},{"name":"validatorVote","type":"publicKey"}]},{"name":"setValidatorScore","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"managerAuthority","isMut":false,"isSigner":true},{"name":"validatorList","isMut":true,"isSigner":false}],"args":[{"name":"index","type":"u32"},{"name":"validatorVote","type":"publicKey"},{"name":"score","type":"u32"}]},{"name":"configValidatorSystem","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"managerAuthority","isMut":false,"isSigner":true}],"args":[{"name":"extraRuns","type":"u32"}]},{"name":"deposit","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"msolMint","isMut":true,"isSigner":false},{"name":"liqPoolSolLegPda","isMut":true,"isSigner":false},{"name":"liqPoolMsolLeg","isMut":true,"isSigner":false},{"name":"liqPoolMsolLegAuthority","isMut":false,"isSigner":false},{"name":"reservePda","isMut":true,"isSigner":false},{"name":"transferFrom","isMut":true,"isSigner":true},{"name":"mintTo","isMut":true,"isSigner":false,"docs":["user mSOL Token account to send the mSOL"]},{"name":"msolMintAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"lamports","type":"u64"}]},{"name":"depositStakeAccount","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeAuthority","isMut":false,"isSigner":true},{"name":"duplicationFlag","isMut":true,"isSigner":false},{"name":"rentPayer","isMut":true,"isSigner":true},{"name":"msolMint","isMut":true,"isSigner":false},{"name":"mintTo","isMut":true,"isSigner":false,"docs":["user mSOL Token account to send the mSOL"]},{"name":"msolMintAuthority","isMut":false,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false}],"args":[{"name":"validatorIndex","type":"u32"}]},{"name":"liquidUnstake","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"msolMint","isMut":true,"isSigner":false},{"name":"liqPoolSolLegPda","isMut":true,"isSigner":false},{"name":"liqPoolMsolLeg","isMut":true,"isSigner":false},{"name":"treasuryMsolAccount","isMut":true,"isSigner":false},{"name":"getMsolFrom","isMut":true,"isSigner":false},{"name":"getMsolFromAuthority","isMut":false,"isSigner":true},{"name":"transferSolTo","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"msolAmount","type":"u64"}]},{"name":"addLiquidity","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"lpMint","isMut":true,"isSigner":false},{"name":"lpMintAuthority","isMut":false,"isSigner":false},{"name":"liqPoolMsolLeg","isMut":false,"isSigner":false},{"name":"liqPoolSolLegPda","isMut":true,"isSigner":false},{"name":"transferFrom","isMut":true,"isSigner":true},{"name":"mintTo","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"lamports","type":"u64"}]},{"name":"removeLiquidity","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"lpMint","isMut":true,"isSigner":false},{"name":"burnFrom","isMut":true,"isSigner":false},{"name":"burnFromAuthority","isMut":false,"isSigner":true},{"name":"transferSolTo","isMut":true,"isSigner":false},{"name":"transferMsolTo","isMut":true,"isSigner":false},{"name":"liqPoolSolLegPda","isMut":true,"isSigner":false},{"name":"liqPoolMsolLeg","isMut":true,"isSigner":false},{"name":"liqPoolMsolLegAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"tokens","type":"u64"}]},{"name":"configLp","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"adminAuthority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"ConfigLpParams"}}]},{"name":"configMarinade","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"adminAuthority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"ConfigMarinadeParams"}}]},{"name":"orderUnstake","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"msolMint","isMut":true,"isSigner":false},{"name":"burnMsolFrom","isMut":true,"isSigner":false},{"name":"burnMsolAuthority","isMut":false,"isSigner":true},{"name":"newTicketAccount","isMut":true,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"msolAmount","type":"u64"}]},{"name":"claim","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"reservePda","isMut":true,"isSigner":false},{"name":"ticketAccount","isMut":true,"isSigner":false},{"name":"transferSolTo","isMut":true,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"stakeReserve","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"validatorVote","isMut":true,"isSigner":false},{"name":"reservePda","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":true},{"name":"stakeDepositAuthority","isMut":false,"isSigner":false},{"name":"rentPayer","isMut":true,"isSigner":true},{"name":"clock","isMut":false,"isSigner":false},{"name":"epochSchedule","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"stakeHistory","isMut":false,"isSigner":false},{"name":"stakeConfig","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false}],"args":[{"name":"validatorIndex","type":"u32"}]},{"name":"updateActive","accounts":[{"name":"common","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeWithdrawAuthority","isMut":false,"isSigner":false},{"name":"reservePda","isMut":true,"isSigner":false},{"name":"msolMint","isMut":true,"isSigner":false},{"name":"msolMintAuthority","isMut":false,"isSigner":false},{"name":"treasuryMsolAccount","isMut":true,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"stakeHistory","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}]},{"name":"validatorList","isMut":true,"isSigner":false}],"args":[{"name":"stakeIndex","type":"u32"},{"name":"validatorIndex","type":"u32"}]},{"name":"updateDeactivated","accounts":[{"name":"common","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeWithdrawAuthority","isMut":false,"isSigner":false},{"name":"reservePda","isMut":true,"isSigner":false},{"name":"msolMint","isMut":true,"isSigner":false},{"name":"msolMintAuthority","isMut":false,"isSigner":false},{"name":"treasuryMsolAccount","isMut":true,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"stakeHistory","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}]},{"name":"operationalSolAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"stakeIndex","type":"u32"}]},{"name":"deactivateStake","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"reservePda","isMut":false,"isSigner":false},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeDepositAuthority","isMut":false,"isSigner":false},{"name":"splitStakeAccount","isMut":true,"isSigner":true},{"name":"splitStakeRentPayer","isMut":true,"isSigner":true},{"name":"clock","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"epochSchedule","isMut":false,"isSigner":false},{"name":"stakeHistory","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false}],"args":[{"name":"stakeIndex","type":"u32"},{"name":"validatorIndex","type":"u32"}]},{"name":"emergencyUnstake","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"validatorManagerAuthority","isMut":false,"isSigner":true},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeDepositAuthority","isMut":false,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false}],"args":[{"name":"stakeIndex","type":"u32"},{"name":"validatorIndex","type":"u32"}]},{"name":"partialUnstake","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"validatorManagerAuthority","isMut":false,"isSigner":true},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeDepositAuthority","isMut":false,"isSigner":false},{"name":"reservePda","isMut":false,"isSigner":false},{"name":"splitStakeAccount","isMut":true,"isSigner":true},{"name":"splitStakeRentPayer","isMut":true,"isSigner":true},{"name":"clock","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"stakeHistory","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false}],"args":[{"name":"stakeIndex","type":"u32"},{"name":"validatorIndex","type":"u32"},{"name":"desiredUnstakeAmount","type":"u64"}]},{"name":"mergeStakes","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"destinationStake","isMut":true,"isSigner":false},{"name":"sourceStake","isMut":true,"isSigner":false},{"name":"stakeDepositAuthority","isMut":false,"isSigner":false},{"name":"stakeWithdrawAuthority","isMut":false,"isSigner":false},{"name":"operationalSolAccount","isMut":true,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"stakeHistory","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false}],"args":[{"name":"destinationStakeIndex","type":"u32"},{"name":"sourceStakeIndex","type":"u32"},{"name":"validatorIndex","type":"u32"}]},{"name":"redelegate","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeDepositAuthority","isMut":false,"isSigner":false},{"name":"reservePda","isMut":false,"isSigner":false},{"name":"splitStakeAccount","isMut":true,"isSigner":true},{"name":"splitStakeRentPayer","isMut":true,"isSigner":true},{"name":"destValidatorAccount","isMut":false,"isSigner":false},{"name":"redelegateStakeAccount","isMut":true,"isSigner":true},{"name":"clock","isMut":false,"isSigner":false},{"name":"stakeHistory","isMut":false,"isSigner":false},{"name":"stakeConfig","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false}],"args":[{"name":"stakeIndex","type":"u32"},{"name":"sourceValidatorIndex","type":"u32"},{"name":"destValidatorIndex","type":"u32"}]},{"name":"pause","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"pauseAuthority","isMut":false,"isSigner":true}],"args":[]},{"name":"resume","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"pauseAuthority","isMut":false,"isSigner":true}],"args":[]},{"name":"withdrawStakeAccount","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"msolMint","isMut":true,"isSigner":false},{"name":"burnMsolFrom","isMut":true,"isSigner":false},{"name":"burnMsolAuthority","isMut":true,"isSigner":true},{"name":"treasuryMsolAccount","isMut":true,"isSigner":false},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"stakeWithdrawAuthority","isMut":false,"isSigner":false},{"name":"stakeDepositAuthority","isMut":false,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"splitStakeAccount","isMut":true,"isSigner":true},{"name":"splitStakeRentPayer","isMut":true,"isSigner":true},{"name":"clock","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false}],"args":[{"name":"stakeIndex","type":"u32"},{"name":"validatorIndex","type":"u32"},{"name":"msolAmount","type":"u64"},{"name":"beneficiary","type":"publicKey"}]},{"name":"reallocValidatorList","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"adminAuthority","isMut":false,"isSigner":true},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"rentFunds","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"capacity","type":"u32"}]},{"name":"reallocStakeList","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"adminAuthority","isMut":false,"isSigner":true},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"rentFunds","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"capacity","type":"u32"}]}],"accounts":[{"name":"TicketAccountData","type":{"kind":"struct","fields":[{"name":"stateAddress","type":"publicKey"},{"name":"beneficiary","type":"publicKey"},{"name":"lamportsAmount","type":"u64"},{"name":"createdEpoch","type":"u64"}]}},{"name":"State","type":{"kind":"struct","fields":[{"name":"msolMint","type":"publicKey"},{"name":"adminAuthority","type":"publicKey"},{"name":"operationalSolAccount","type":"publicKey"},{"name":"treasuryMsolAccount","type":"publicKey"},{"name":"reserveBumpSeed","type":"u8"},{"name":"msolMintAuthorityBumpSeed","type":"u8"},{"name":"rentExemptForTokenAcc","type":"u64"},{"name":"rewardFee","type":{"defined":"Fee"}},{"name":"stakeSystem","type":{"defined":"StakeSystem"}},{"name":"validatorSystem","type":{"defined":"ValidatorSystem"}},{"name":"liqPool","type":{"defined":"LiqPool"}},{"name":"availableReserveBalance","type":"u64"},{"name":"msolSupply","type":"u64"},{"name":"msolPrice","type":"u64"},{"name":"circulatingTicketCount","docs":["count tickets for delayed-unstake"],"type":"u64"},{"name":"circulatingTicketBalance","docs":["total lamports amount of generated and not claimed yet tickets"],"type":"u64"},{"name":"lentFromReserve","type":"u64"},{"name":"minDeposit","type":"u64"},{"name":"minWithdraw","type":"u64"},{"name":"stakingSolCap","type":"u64"},{"name":"emergencyCoolingDown","type":"u64"},{"name":"pauseAuthority","docs":["emergency pause"],"type":"publicKey"},{"name":"paused","type":"bool"},{"name":"delayedUnstakeFee","type":{"defined":"FeeCents"}},{"name":"withdrawStakeAccountFee","type":{"defined":"FeeCents"}},{"name":"withdrawStakeAccountEnabled","type":"bool"},{"name":"lastStakeMoveEpoch","type":"u64"},{"name":"stakeMoved","type":"u64"},{"name":"maxStakeMovedPerEpoch","type":{"defined":"Fee"}}]}}],"types":[{"name":"SplitStakeAccountInfo","type":{"kind":"struct","fields":[{"name":"account","type":"publicKey"},{"name":"index","type":"u32"}]}},{"name":"U64ValueChange","type":{"kind":"struct","fields":[{"name":"old","type":"u64"},{"name":"new","type":"u64"}]}},{"name":"U32ValueChange","type":{"kind":"struct","fields":[{"name":"old","type":"u32"},{"name":"new","type":"u32"}]}},{"name":"FeeValueChange","type":{"kind":"struct","fields":[{"name":"old","type":{"defined":"Fee"}},{"name":"new","type":{"defined":"Fee"}}]}},{"name":"FeeCentsValueChange","type":{"kind":"struct","fields":[{"name":"old","type":{"defined":"FeeCents"}},{"name":"new","type":{"defined":"FeeCents"}}]}},{"name":"PubkeyValueChange","type":{"kind":"struct","fields":[{"name":"old","type":"publicKey"},{"name":"new","type":"publicKey"}]}},{"name":"BoolValueChange","type":{"kind":"struct","fields":[{"name":"old","type":"bool"},{"name":"new","type":"bool"}]}},{"name":"ChangeAuthorityData","type":{"kind":"struct","fields":[{"name":"admin","type":{"option":"publicKey"}},{"name":"validatorManager","type":{"option":"publicKey"}},{"name":"operationalSolAccount","type":{"option":"publicKey"}},{"name":"treasuryMsolAccount","type":{"option":"publicKey"}},{"name":"pauseAuthority","type":{"option":"publicKey"}}]}},{"name":"ConfigLpParams","type":{"kind":"struct","fields":[{"name":"minFee","type":{"option":{"defined":"Fee"}}},{"name":"maxFee","type":{"option":{"defined":"Fee"}}},{"name":"liquidityTarget","type":{"option":"u64"}},{"name":"treasuryCut","type":{"option":{"defined":"Fee"}}}]}},{"name":"ConfigMarinadeParams","type":{"kind":"struct","fields":[{"name":"rewardsFee","type":{"option":{"defined":"Fee"}}},{"name":"slotsForStakeDelta","type":{"option":"u64"}},{"name":"minStake","type":{"option":"u64"}},{"name":"minDeposit","type":{"option":"u64"}},{"name":"minWithdraw","type":{"option":"u64"}},{"name":"stakingSolCap","type":{"option":"u64"}},{"name":"liquiditySolCap","type":{"option":"u64"}},{"name":"withdrawStakeAccountEnabled","type":{"option":"bool"}},{"name":"delayedUnstakeFee","type":{"option":{"defined":"FeeCents"}}},{"name":"withdrawStakeAccountFee","type":{"option":{"defined":"FeeCents"}}},{"name":"maxStakeMovedPerEpoch","type":{"option":{"defined":"Fee"}}}]}},{"name":"InitializeData","type":{"kind":"struct","fields":[{"name":"adminAuthority","type":"publicKey"},{"name":"validatorManagerAuthority","type":"publicKey"},{"name":"minStake","type":"u64"},{"name":"rewardsFee","type":{"defined":"Fee"}},{"name":"liqPool","type":{"defined":"LiqPoolInitializeData"}},{"name":"additionalStakeRecordSpace","type":"u32"},{"name":"additionalValidatorRecordSpace","type":"u32"},{"name":"slotsForStakeDelta","type":"u64"},{"name":"pauseAuthority","type":"publicKey"}]}},{"name":"LiqPoolInitializeData","type":{"kind":"struct","fields":[{"name":"lpLiquidityTarget","type":"u64"},{"name":"lpMaxFee","type":{"defined":"Fee"}},{"name":"lpMinFee","type":{"defined":"Fee"}},{"name":"lpTreasuryCut","type":{"defined":"Fee"}}]}},{"name":"Fee","type":{"kind":"struct","fields":[{"name":"basisPoints","type":"u32"}]}},{"name":"FeeCents","docs":["FeeCents, same as Fee but / 1_000_000 instead of 10_000","1 FeeCent = 0.0001%, 10_000 FeeCent = 1%, 1_000_000 FeeCent = 100%"],"type":{"kind":"struct","fields":[{"name":"bpCents","type":"u32"}]}},{"name":"LiqPool","type":{"kind":"struct","fields":[{"name":"lpMint","type":"publicKey"},{"name":"lpMintAuthorityBumpSeed","type":"u8"},{"name":"solLegBumpSeed","type":"u8"},{"name":"msolLegAuthorityBumpSeed","type":"u8"},{"name":"msolLeg","type":"publicKey"},{"name":"lpLiquidityTarget","docs":["Liquidity target. If the Liquidity reach this amount, the fee reaches lp_min_discount_fee"],"type":"u64"},{"name":"lpMaxFee","docs":["Liquidity pool max fee"],"type":{"defined":"Fee"}},{"name":"lpMinFee","docs":["SOL/mSOL Liquidity pool min fee"],"type":{"defined":"Fee"}},{"name":"treasuryCut","docs":["Treasury cut"],"type":{"defined":"Fee"}},{"name":"lpSupply","type":"u64"},{"name":"lentFromSolLeg","type":"u64"},{"name":"liquiditySolCap","type":"u64"}]}},{"name":"List","type":{"kind":"struct","fields":[{"name":"account","type":"publicKey"},{"name":"itemSize","type":"u32"},{"name":"count","type":"u32"},{"name":"reserved1","type":"publicKey"},{"name":"reserved2","type":"u32"}]}},{"name":"StakeRecord","type":{"kind":"struct","fields":[{"name":"stakeAccount","type":"publicKey"},{"name":"lastUpdateDelegatedLamports","type":"u64"},{"name":"lastUpdateEpoch","type":"u64"},{"name":"isEmergencyUnstaking","type":"u8"}]}},{"name":"StakeList","type":{"kind":"struct","fields":[]}},{"name":"StakeSystem","type":{"kind":"struct","fields":[{"name":"stakeList","type":{"defined":"List"}},{"name":"delayedUnstakeCoolingDown","type":"u64"},{"name":"stakeDepositBumpSeed","type":"u8"},{"name":"stakeWithdrawBumpSeed","type":"u8"},{"name":"slotsForStakeDelta","docs":["set by admin, how much slots before the end of the epoch, stake-delta can start"],"type":"u64"},{"name":"lastStakeDeltaEpoch","docs":["Marks the start of stake-delta operations, meaning that if somebody starts a delayed-unstake ticket","after this var is set with epoch_num the ticket will have epoch_created = current_epoch+1","(the user must wait one more epoch, because their unstake-delta will be execute in this epoch)"],"type":"u64"},{"name":"minStake","type":"u64"},{"name":"extraStakeDeltaRuns","docs":["can be set by validator-manager-auth to allow a second run of stake-delta to stake late stakers in the last minute of the epoch","so we maximize user's rewards"],"type":"u32"}]}},{"name":"ValidatorRecord","type":{"kind":"struct","fields":[{"name":"validatorAccount","docs":["Validator vote pubkey"],"type":"publicKey"},{"name":"activeBalance","docs":["Validator total balance in lamports"],"type":"u64"},{"name":"score","type":"u32"},{"name":"lastStakeDeltaEpoch","type":"u64"},{"name":"duplicationFlagBumpSeed","type":"u8"}]}},{"name":"ValidatorList","type":{"kind":"struct","fields":[]}},{"name":"ValidatorSystem","type":{"kind":"struct","fields":[{"name":"validatorList","type":{"defined":"List"}},{"name":"managerAuthority","type":"publicKey"},{"name":"totalValidatorScore","type":"u32"},{"name":"totalActiveBalance","docs":["sum of all active lamports staked"],"type":"u64"},{"name":"autoAddValidatorEnabled","docs":["DEPRECATED, no longer used"],"type":"u8"}]}}],"events":[{"name":"ChangeAuthorityEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"adminChange","type":{"option":{"defined":"PubkeyValueChange"}},"index":false},{"name":"validatorManagerChange","type":{"option":{"defined":"PubkeyValueChange"}},"index":false},{"name":"operationalSolAccountChange","type":{"option":{"defined":"PubkeyValueChange"}},"index":false},{"name":"treasuryMsolAccountChange","type":{"option":{"defined":"PubkeyValueChange"}},"index":false},{"name":"pauseAuthorityChange","type":{"option":{"defined":"PubkeyValueChange"}},"index":false}]},{"name":"ConfigLpEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"minFeeChange","type":{"option":{"defined":"FeeValueChange"}},"index":false},{"name":"maxFeeChange","type":{"option":{"defined":"FeeValueChange"}},"index":false},{"name":"liquidityTargetChange","type":{"option":{"defined":"U64ValueChange"}},"index":false},{"name":"treasuryCutChange","type":{"option":{"defined":"FeeValueChange"}},"index":false}]},{"name":"ConfigMarinadeEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"rewardsFeeChange","type":{"option":{"defined":"FeeValueChange"}},"index":false},{"name":"slotsForStakeDeltaChange","type":{"option":{"defined":"U64ValueChange"}},"index":false},{"name":"minStakeChange","type":{"option":{"defined":"U64ValueChange"}},"index":false},{"name":"minDepositChange","type":{"option":{"defined":"U64ValueChange"}},"index":false},{"name":"minWithdrawChange","type":{"option":{"defined":"U64ValueChange"}},"index":false},{"name":"stakingSolCapChange","type":{"option":{"defined":"U64ValueChange"}},"index":false},{"name":"liquiditySolCapChange","type":{"option":{"defined":"U64ValueChange"}},"index":false},{"name":"withdrawStakeAccountEnabledChange","type":{"option":{"defined":"BoolValueChange"}},"index":false},{"name":"delayedUnstakeFeeChange","type":{"option":{"defined":"FeeCentsValueChange"}},"index":false},{"name":"withdrawStakeAccountFeeChange","type":{"option":{"defined":"FeeCentsValueChange"}},"index":false},{"name":"maxStakeMovedPerEpochChange","type":{"option":{"defined":"FeeValueChange"}},"index":false}]},{"name":"InitializeEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"params","type":{"defined":"InitializeData"},"index":false},{"name":"stakeList","type":"publicKey","index":false},{"name":"validatorList","type":"publicKey","index":false},{"name":"msolMint","type":"publicKey","index":false},{"name":"operationalSolAccount","type":"publicKey","index":false},{"name":"lpMint","type":"publicKey","index":false},{"name":"lpMsolLeg","type":"publicKey","index":false},{"name":"treasuryMsolAccount","type":"publicKey","index":false}]},{"name":"EmergencyPauseEvent","fields":[{"name":"state","type":"publicKey","index":false}]},{"name":"ResumeEvent","fields":[{"name":"state","type":"publicKey","index":false}]},{"name":"ReallocValidatorListEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"count","type":"u32","index":false},{"name":"newCapacity","type":"u32","index":false}]},{"name":"ReallocStakeListEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"count","type":"u32","index":false},{"name":"newCapacity","type":"u32","index":false}]},{"name":"DeactivateStakeEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"epoch","type":"u64","index":false},{"name":"stakeIndex","type":"u32","index":false},{"name":"stakeAccount","type":"publicKey","index":false},{"name":"lastUpdateStakeDelegation","type":"u64","index":false},{"name":"splitStakeAccount","type":{"option":{"defined":"SplitStakeAccountInfo"}},"index":false},{"name":"validatorIndex","type":"u32","index":false},{"name":"validatorVote","type":"publicKey","index":false},{"name":"totalStakeTarget","type":"u64","index":false},{"name":"validatorStakeTarget","type":"u64","index":false},{"name":"totalActiveBalance","type":"u64","index":false},{"name":"delayedUnstakeCoolingDown","type":"u64","index":false},{"name":"validatorActiveBalance","type":"u64","index":false},{"name":"totalUnstakeDelta","type":"u64","index":false},{"name":"unstakedAmount","type":"u64","index":false}]},{"name":"MergeStakesEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"epoch","type":"u64","index":false},{"name":"destinationStakeIndex","type":"u32","index":false},{"name":"destinationStakeAccount","type":"publicKey","index":false},{"name":"lastUpdateDestinationStakeDelegation","type":"u64","index":false},{"name":"sourceStakeIndex","type":"u32","index":false},{"name":"sourceStakeAccount","type":"publicKey","index":false},{"name":"lastUpdateSourceStakeDelegation","type":"u64","index":false},{"name":"validatorIndex","type":"u32","index":false},{"name":"validatorVote","type":"publicKey","index":false},{"name":"extraDelegated","type":"u64","index":false},{"name":"returnedStakeRent","type":"u64","index":false},{"name":"validatorActiveBalance","type":"u64","index":false},{"name":"totalActiveBalance","type":"u64","index":false},{"name":"operationalSolBalance","type":"u64","index":false}]},{"name":"RedelegateEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"epoch","type":"u64","index":false},{"name":"stakeIndex","type":"u32","index":false},{"name":"stakeAccount","type":"publicKey","index":false},{"name":"lastUpdateDelegation","type":"u64","index":false},{"name":"sourceValidatorIndex","type":"u32","index":false},{"name":"sourceValidatorVote","type":"publicKey","index":false},{"name":"sourceValidatorScore","type":"u32","index":false},{"name":"sourceValidatorBalance","type":"u64","index":false},{"name":"sourceValidatorStakeTarget","type":"u64","index":false},{"name":"destValidatorIndex","type":"u32","index":false},{"name":"destValidatorVote","type":"publicKey","index":false},{"name":"destValidatorScore","type":"u32","index":false},{"name":"destValidatorBalance","type":"u64","index":false},{"name":"destValidatorStakeTarget","type":"u64","index":false},{"name":"redelegateAmount","type":"u64","index":false},{"name":"splitStakeAccount","type":{"option":{"defined":"SplitStakeAccountInfo"}},"index":false},{"name":"redelegateStakeIndex","type":"u32","index":false},{"name":"redelegateStakeAccount","type":"publicKey","index":false}]},{"name":"StakeReserveEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"epoch","type":"u64","index":false},{"name":"stakeIndex","type":"u32","index":false},{"name":"stakeAccount","type":"publicKey","index":false},{"name":"validatorIndex","type":"u32","index":false},{"name":"validatorVote","type":"publicKey","index":false},{"name":"totalStakeTarget","type":"u64","index":false},{"name":"validatorStakeTarget","type":"u64","index":false},{"name":"reserveBalance","type":"u64","index":false},{"name":"totalActiveBalance","type":"u64","index":false},{"name":"validatorActiveBalance","type":"u64","index":false},{"name":"totalStakeDelta","type":"u64","index":false},{"name":"amount","type":"u64","index":false}]},{"name":"UpdateActiveEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"epoch","type":"u64","index":false},{"name":"stakeIndex","type":"u32","index":false},{"name":"stakeAccount","type":"publicKey","index":false},{"name":"validatorIndex","type":"u32","index":false},{"name":"validatorVote","type":"publicKey","index":false},{"name":"delegationChange","type":{"defined":"U64ValueChange"},"index":false},{"name":"delegationGrowthMsolFees","type":{"option":"u64"},"index":false},{"name":"extraLamports","type":"u64","index":false},{"name":"extraMsolFees","type":{"option":"u64"},"index":false},{"name":"validatorActiveBalance","type":"u64","index":false},{"name":"totalActiveBalance","type":"u64","index":false},{"name":"msolPriceChange","type":{"defined":"U64ValueChange"},"index":false},{"name":"rewardFeeUsed","type":{"defined":"Fee"},"index":false},{"name":"totalVirtualStakedLamports","type":"u64","index":false},{"name":"msolSupply","type":"u64","index":false}]},{"name":"UpdateDeactivatedEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"epoch","type":"u64","index":false},{"name":"stakeIndex","type":"u32","index":false},{"name":"stakeAccount","type":"publicKey","index":false},{"name":"balanceWithoutRentExempt","type":"u64","index":false},{"name":"lastUpdateDelegatedLamports","type":"u64","index":false},{"name":"msolFees","type":{"option":"u64"},"index":false},{"name":"msolPriceChange","type":{"defined":"U64ValueChange"},"index":false},{"name":"rewardFeeUsed","type":{"defined":"Fee"},"index":false},{"name":"operationalSolBalance","type":"u64","index":false},{"name":"totalVirtualStakedLamports","type":"u64","index":false},{"name":"msolSupply","type":"u64","index":false}]},{"name":"ClaimEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"epoch","type":"u64","index":false},{"name":"ticket","type":"publicKey","index":false},{"name":"beneficiary","type":"publicKey","index":false},{"name":"circulatingTicketBalance","type":"u64","index":false},{"name":"circulatingTicketCount","type":"u64","index":false},{"name":"reserveBalance","type":"u64","index":false},{"name":"userBalance","type":"u64","index":false},{"name":"amount","type":"u64","index":false}]},{"name":"OrderUnstakeEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"ticketEpoch","type":"u64","index":false},{"name":"ticket","type":"publicKey","index":false},{"name":"beneficiary","type":"publicKey","index":false},{"name":"circulatingTicketBalance","type":"u64","index":false},{"name":"circulatingTicketCount","type":"u64","index":false},{"name":"userMsolBalance","type":"u64","index":false},{"name":"burnedMsolAmount","type":"u64","index":false},{"name":"solAmount","type":"u64","index":false},{"name":"feeBpCents","type":"u32","index":false},{"name":"totalVirtualStakedLamports","type":"u64","index":false},{"name":"msolSupply","type":"u64","index":false}]},{"name":"AddLiquidityEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"solOwner","type":"publicKey","index":false},{"name":"userSolBalance","type":"u64","index":false},{"name":"userLpBalance","type":"u64","index":false},{"name":"solLegBalance","type":"u64","index":false},{"name":"lpSupply","type":"u64","index":false},{"name":"solAddedAmount","type":"u64","index":false},{"name":"lpMinted","type":"u64","index":false},{"name":"totalVirtualStakedLamports","type":"u64","index":false},{"name":"msolSupply","type":"u64","index":false}]},{"name":"LiquidUnstakeEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"msolOwner","type":"publicKey","index":false},{"name":"liqPoolSolBalance","type":"u64","index":false},{"name":"liqPoolMsolBalance","type":"u64","index":false},{"name":"treasuryMsolBalance","type":{"option":"u64"},"index":false},{"name":"userMsolBalance","type":"u64","index":false},{"name":"userSolBalance","type":"u64","index":false},{"name":"msolAmount","type":"u64","index":false},{"name":"msolFee","type":"u64","index":false},{"name":"treasuryMsolCut","type":"u64","index":false},{"name":"solAmount","type":"u64","index":false},{"name":"lpLiquidityTarget","type":"u64","index":false},{"name":"lpMaxFee","type":{"defined":"Fee"},"index":false},{"name":"lpMinFee","type":{"defined":"Fee"},"index":false},{"name":"treasuryCut","type":{"defined":"Fee"},"index":false}]},{"name":"RemoveLiquidityEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"solLegBalance","type":"u64","index":false},{"name":"msolLegBalance","type":"u64","index":false},{"name":"userLpBalance","type":"u64","index":false},{"name":"userSolBalance","type":"u64","index":false},{"name":"userMsolBalance","type":"u64","index":false},{"name":"lpMintSupply","type":"u64","index":false},{"name":"lpBurned","type":"u64","index":false},{"name":"solOutAmount","type":"u64","index":false},{"name":"msolOutAmount","type":"u64","index":false}]},{"name":"AddValidatorEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"validator","type":"publicKey","index":false},{"name":"index","type":"u32","index":false},{"name":"score","type":"u32","index":false}]},{"name":"RemoveValidatorEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"validator","type":"publicKey","index":false},{"name":"index","type":"u32","index":false},{"name":"operationalSolBalance","type":"u64","index":false}]},{"name":"SetValidatorScoreEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"validator","type":"publicKey","index":false},{"name":"index","type":"u32","index":false},{"name":"scoreChange","type":{"defined":"U32ValueChange"},"index":false}]},{"name":"DepositStakeAccountEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"stake","type":"publicKey","index":false},{"name":"delegated","type":"u64","index":false},{"name":"withdrawer","type":"publicKey","index":false},{"name":"stakeIndex","type":"u32","index":false},{"name":"validator","type":"publicKey","index":false},{"name":"validatorIndex","type":"u32","index":false},{"name":"validatorActiveBalance","type":"u64","index":false},{"name":"totalActiveBalance","type":"u64","index":false},{"name":"userMsolBalance","type":"u64","index":false},{"name":"msolMinted","type":"u64","index":false},{"name":"totalVirtualStakedLamports","type":"u64","index":false},{"name":"msolSupply","type":"u64","index":false}]},{"name":"DepositEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"solOwner","type":"publicKey","index":false},{"name":"userSolBalance","type":"u64","index":false},{"name":"userMsolBalance","type":"u64","index":false},{"name":"solLegBalance","type":"u64","index":false},{"name":"msolLegBalance","type":"u64","index":false},{"name":"reserveBalance","type":"u64","index":false},{"name":"solSwapped","type":"u64","index":false},{"name":"msolSwapped","type":"u64","index":false},{"name":"solDeposited","type":"u64","index":false},{"name":"msolMinted","type":"u64","index":false},{"name":"totalVirtualStakedLamports","type":"u64","index":false},{"name":"msolSupply","type":"u64","index":false}]},{"name":"WithdrawStakeAccountEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"epoch","type":"u64","index":false},{"name":"stake","type":"publicKey","index":false},{"name":"lastUpdateStakeDelegation","type":"u64","index":false},{"name":"stakeIndex","type":"u32","index":false},{"name":"validator","type":"publicKey","index":false},{"name":"validatorIndex","type":"u32","index":false},{"name":"userMsolBalance","type":"u64","index":false},{"name":"userMsolAuth","type":"publicKey","index":false},{"name":"msolBurned","type":"u64","index":false},{"name":"msolFees","type":"u64","index":false},{"name":"splitStake","type":"publicKey","index":false},{"name":"beneficiary","type":"publicKey","index":false},{"name":"splitLamports","type":"u64","index":false},{"name":"feeBpCents","type":"u32","index":false},{"name":"totalVirtualStakedLamports","type":"u64","index":false},{"name":"msolSupply","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"WrongReserveOwner","msg":"Wrong reserve owner. Must be a system account"},{"code":6001,"name":"NonEmptyReserveData","msg":"Reserve must have no data, but has data"},{"code":6002,"name":"InvalidInitialReserveLamports","msg":"Invalid initial reserve lamports"},{"code":6003,"name":"ZeroValidatorChunkSize","msg":"Zero validator chunk size"},{"code":6004,"name":"TooBigValidatorChunkSize","msg":"Too big validator chunk size"},{"code":6005,"name":"ZeroCreditChunkSize","msg":"Zero credit chunk size"},{"code":6006,"name":"TooBigCreditChunkSize","msg":"Too big credit chunk size"},{"code":6007,"name":"TooLowCreditFee","msg":"Too low credit fee"},{"code":6008,"name":"InvalidMintAuthority","msg":"Invalid mint authority"},{"code":6009,"name":"MintHasInitialSupply","msg":"Non empty initial mint supply"},{"code":6010,"name":"InvalidOwnerFeeState","msg":"Invalid owner fee state"},{"code":6011,"name":"InvalidProgramId","msg":"Invalid program id. For using program from another account please update id in the code"},{"code":6012,"name":"UnexpectedAccount","msg":"Unexpected account"},{"code":6013,"name":"CalculationFailure","msg":"Calculation failure"},{"code":6014,"name":"StakeAccountWithLockup","msg":"You can't deposit a stake-account with lockup"},{"code":6015,"name":"MinStakeIsTooLow","msg":"Min stake is too low"},{"code":6016,"name":"LpMaxFeeIsTooHigh","msg":"Lp max fee is too high"},{"code":6017,"name":"BasisPointsOverflow","msg":"Basis points overflow"},{"code":6018,"name":"LpFeesAreWrongWayRound","msg":"LP min fee > LP max fee"},{"code":6019,"name":"LiquidityTargetTooLow","msg":"Liquidity target too low"},{"code":6020,"name":"TicketNotDue","msg":"Ticket not due. Wait more epochs"},{"code":6021,"name":"TicketNotReady","msg":"Ticket not ready. Wait a few hours and try again"},{"code":6022,"name":"WrongBeneficiary","msg":"Wrong Ticket Beneficiary"},{"code":6023,"name":"StakeAccountNotUpdatedYet","msg":"Stake Account not updated yet"},{"code":6024,"name":"StakeNotDelegated","msg":"Stake Account not delegated"},{"code":6025,"name":"StakeAccountIsEmergencyUnstaking","msg":"Stake Account is emergency unstaking"},{"code":6026,"name":"InsufficientLiquidity","msg":"Insufficient Liquidity in the Liquidity Pool"},{"code":6027,"name":"NotUsed6027"},{"code":6028,"name":"InvalidAdminAuthority","msg":"Invalid admin authority"},{"code":6029,"name":"InvalidValidatorManager","msg":"Invalid validator system manager"},{"code":6030,"name":"InvalidStakeListDiscriminator","msg":"Invalid stake list account discriminator"},{"code":6031,"name":"InvalidValidatorListDiscriminator","msg":"Invalid validator list account discriminator"},{"code":6032,"name":"TreasuryCutIsTooHigh","msg":"Treasury cut is too high"},{"code":6033,"name":"RewardsFeeIsTooHigh","msg":"Reward fee is too high"},{"code":6034,"name":"StakingIsCapped","msg":"Staking is capped"},{"code":6035,"name":"LiquidityIsCapped","msg":"Liquidity is capped"},{"code":6036,"name":"UpdateWindowIsTooLow","msg":"Update window is too low"},{"code":6037,"name":"MinWithdrawIsTooHigh","msg":"Min withdraw is too high"},{"code":6038,"name":"WithdrawAmountIsTooLow","msg":"Withdraw amount is too low"},{"code":6039,"name":"DepositAmountIsTooLow","msg":"Deposit amount is too low"},{"code":6040,"name":"NotEnoughUserFunds","msg":"Not enough user funds"},{"code":6041,"name":"WrongTokenOwnerOrDelegate","msg":"Wrong token owner or delegate"},{"code":6042,"name":"TooEarlyForStakeDelta","msg":"Too early for stake delta"},{"code":6043,"name":"RequiredDelegatedStake","msg":"Required delegated stake"},{"code":6044,"name":"RequiredActiveStake","msg":"Required active stake"},{"code":6045,"name":"RequiredDeactivatingStake","msg":"Required deactivating stake"},{"code":6046,"name":"DepositingNotActivatedStake","msg":"Depositing not activated stake"},{"code":6047,"name":"TooLowDelegationInDepositingStake","msg":"Too low delegation in the depositing stake"},{"code":6048,"name":"WrongStakeBalance","msg":"Wrong deposited stake balance"},{"code":6049,"name":"WrongValidatorAccountOrIndex","msg":"Wrong validator account or index"},{"code":6050,"name":"WrongStakeAccountOrIndex","msg":"Wrong stake account or index"},{"code":6051,"name":"UnstakingOnPositiveDelta","msg":"Delta stake is positive so we must stake instead of unstake"},{"code":6052,"name":"StakingOnNegativeDelta","msg":"Delta stake is negative so we must unstake instead of stake"},{"code":6053,"name":"MovingStakeIsCapped","msg":"Moving stake during an epoch is capped"},{"code":6054,"name":"StakeMustBeUninitialized","msg":"Stake must be uninitialized"},{"code":6055,"name":"DestinationStakeMustBeDelegated","msg":"Destination stake must be delegated"},{"code":6056,"name":"DestinationStakeMustNotBeDeactivating","msg":"Destination stake must not be deactivating"},{"code":6057,"name":"DestinationStakeMustBeUpdated","msg":"Destination stake must be updated"},{"code":6058,"name":"InvalidDestinationStakeDelegation","msg":"Invalid destination stake delegation"},{"code":6059,"name":"SourceStakeMustBeDelegated","msg":"Source stake must be delegated"},{"code":6060,"name":"SourceStakeMustNotBeDeactivating","msg":"Source stake must not be deactivating"},{"code":6061,"name":"SourceStakeMustBeUpdated","msg":"Source stake must be updated"},{"code":6062,"name":"InvalidSourceStakeDelegation","msg":"Invalid source stake delegation"},{"code":6063,"name":"InvalidDelayedUnstakeTicket","msg":"Invalid delayed unstake ticket"},{"code":6064,"name":"ReusingDelayedUnstakeTicket","msg":"Reusing delayed unstake ticket"},{"code":6065,"name":"EmergencyUnstakingFromNonZeroScoredValidator","msg":"Emergency unstaking from non zero scored validator"},{"code":6066,"name":"WrongValidatorDuplicationFlag","msg":"Wrong validator duplication flag"},{"code":6067,"name":"RedepositingMarinadeStake","msg":"Redepositing marinade stake"},{"code":6068,"name":"RemovingValidatorWithBalance","msg":"Removing validator with balance"},{"code":6069,"name":"RedelegateOverTarget","msg":"Redelegate will put validator over stake target"},{"code":6070,"name":"SourceAndDestValidatorsAreTheSame","msg":"Source and Dest Validators are the same"},{"code":6071,"name":"UnregisteredMsolMinted","msg":"Some mSOL tokens was minted outside of marinade contract"},{"code":6072,"name":"UnregisteredLPMinted","msg":"Some LP tokens was minted outside of marinade contract"},{"code":6073,"name":"ListIndexOutOfBounds","msg":"List index out of bounds"},{"code":6074,"name":"ListOverflow","msg":"List overflow"},{"code":6075,"name":"AlreadyPaused","msg":"Requested pause and already Paused"},{"code":6076,"name":"NotPaused","msg":"Requested resume, but not Paused"},{"code":6077,"name":"ProgramIsPaused","msg":"Emergency Pause is Active"},{"code":6078,"name":"InvalidPauseAuthority","msg":"Invalid pause authority"},{"code":6079,"name":"SelectedStakeAccountHasNotEnoughFunds","msg":"Selected Stake account has not enough funds"},{"code":6080,"name":"BasisPointCentsOverflow","msg":"Basis point CENTS overflow"},{"code":6081,"name":"WithdrawStakeAccountIsNotEnabled","msg":"Withdraw stake account is not enabled"},{"code":6082,"name":"WithdrawStakeAccountFeeIsTooHigh","msg":"Withdraw stake account fee is too high"},{"code":6083,"name":"DelayedUnstakeFeeIsTooHigh","msg":"Delayed unstake fee is too high"},{"code":6084,"name":"WithdrawStakeLamportsIsTooLow","msg":"Withdraw stake account value is too low"},{"code":6085,"name":"StakeAccountRemainderTooLow","msg":"Stake account remainder too low"},{"code":6086,"name":"ShrinkingListWithDeletingContents","msg":"Capacity of the list must be not less than it's current size"}]} \ No newline at end of file diff --git a/idls/metadao_amm_v0.5.AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ.json b/idls/metadao_amm_v0.5.AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ.json new file mode 100644 index 0000000..1a3998f --- /dev/null +++ b/idls/metadao_amm_v0.5.AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ.json @@ -0,0 +1 @@ +{"version":"0.5.0","name":"amm","instructions":[{"name":"createAmm","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"amm","isMut":true,"isSigner":false},{"name":"lpMint","isMut":true,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"vaultAtaBase","isMut":true,"isSigner":false},{"name":"vaultAtaQuote","isMut":true,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"CreateAmmArgs"}}]},{"name":"addLiquidity","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"amm","isMut":true,"isSigner":false},{"name":"lpMint","isMut":true,"isSigner":false},{"name":"userLpAccount","isMut":true,"isSigner":false},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"vaultAtaBase","isMut":true,"isSigner":false},{"name":"vaultAtaQuote","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"AddLiquidityArgs"}}]},{"name":"removeLiquidity","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"amm","isMut":true,"isSigner":false},{"name":"lpMint","isMut":true,"isSigner":false},{"name":"userLpAccount","isMut":true,"isSigner":false},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"vaultAtaBase","isMut":true,"isSigner":false},{"name":"vaultAtaQuote","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"RemoveLiquidityArgs"}}]},{"name":"swap","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"amm","isMut":true,"isSigner":false},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"vaultAtaBase","isMut":true,"isSigner":false},{"name":"vaultAtaQuote","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"SwapArgs"}}]},{"name":"crankThatTwap","accounts":[{"name":"amm","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"Amm","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"createdAtSlot","type":"u64"},{"name":"lpMint","type":"publicKey"},{"name":"baseMint","type":"publicKey"},{"name":"quoteMint","type":"publicKey"},{"name":"baseMintDecimals","type":"u8"},{"name":"quoteMintDecimals","type":"u8"},{"name":"baseAmount","type":"u64"},{"name":"quoteAmount","type":"u64"},{"name":"oracle","type":{"defined":"TwapOracle"}},{"name":"seqNum","type":"u64"},{"name":"vaultAtaBase","type":"publicKey"},{"name":"vaultAtaQuote","type":"publicKey"}]}}],"types":[{"name":"CommonFields","type":{"kind":"struct","fields":[{"name":"slot","type":"u64"},{"name":"unixTimestamp","type":"i64"},{"name":"user","type":"publicKey"},{"name":"amm","type":"publicKey"},{"name":"postBaseReserves","type":"u64"},{"name":"postQuoteReserves","type":"u64"},{"name":"oracleLastPrice","type":"u128"},{"name":"oracleLastObservation","type":"u128"},{"name":"oracleAggregator","type":"u128"},{"name":"seqNum","type":"u64"}]}},{"name":"AddLiquidityArgs","type":{"kind":"struct","fields":[{"name":"quoteAmount","docs":["How much quote token you will deposit to the pool"],"type":"u64"},{"name":"maxBaseAmount","docs":["The maximum base token you will deposit to the pool"],"type":"u64"},{"name":"minLpTokens","docs":["The minimum LP token you will get back"],"type":"u64"}]}},{"name":"CreateAmmArgs","type":{"kind":"struct","fields":[{"name":"twapInitialObservation","type":"u128"},{"name":"twapMaxObservationChangePerUpdate","type":"u128"},{"name":"twapStartDelaySlots","type":"u64"}]}},{"name":"RemoveLiquidityArgs","type":{"kind":"struct","fields":[{"name":"lpTokensToBurn","type":"u64"},{"name":"minQuoteAmount","type":"u64"},{"name":"minBaseAmount","type":"u64"}]}},{"name":"SwapArgs","type":{"kind":"struct","fields":[{"name":"swapType","type":{"defined":"SwapType"}},{"name":"inputAmount","type":"u64"},{"name":"outputAmountMin","type":"u64"}]}},{"name":"TwapOracle","type":{"kind":"struct","fields":[{"name":"lastUpdatedSlot","type":"u64"},{"name":"lastPrice","docs":["A price is the number of quote units per base unit multiplied by 1e12.","You cannot simply divide by 1e12 to get a price you can display in the UI","because the base and quote decimals may be different. Instead, do:","ui_price = (price * (10**(base_decimals - quote_decimals))) / 1e12"],"type":"u128"},{"name":"lastObservation","docs":["If we did a raw TWAP over prices, someone could push the TWAP heavily with","a few extremely large outliers. So we use observations, which can only move","by `max_observation_change_per_update` per update."],"type":"u128"},{"name":"aggregator","docs":["Running sum of slots_per_last_update * last_observation.","","Assuming latest observations are as big as possible (u64::MAX * 1e12),","we can store 18 million slots worth of observations, which turns out to","be ~85 days worth of slots.","","Assuming that latest observations are 100x smaller than they could theoretically","be, we can store 8500 days (23 years) worth of them. Even this is a very","very conservative assumption - META/USDC prices should be between 1e9 and","1e15, which would overflow after 1e15 years worth of slots.","","So in the case of an overflow, the aggregator rolls back to 0. It's the","client's responsibility to sanity check the assets or to handle an","aggregator at T2 being smaller than an aggregator at T1."],"type":"u128"},{"name":"maxObservationChangePerUpdate","docs":["The most that an observation can change per update."],"type":"u128"},{"name":"initialObservation","docs":["What the initial `latest_observation` is set to."],"type":"u128"},{"name":"startDelaySlots","docs":["Number of slots after amm.created_at_slot to start recording TWAP"],"type":"u64"}]}},{"name":"SwapType","type":{"kind":"enum","variants":[{"name":"Buy"},{"name":"Sell"}]}}],"events":[{"name":"SwapEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"inputAmount","type":"u64","index":false},{"name":"outputAmount","type":"u64","index":false},{"name":"swapType","type":{"defined":"SwapType"},"index":false}]},{"name":"AddLiquidityEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"quoteAmount","type":"u64","index":false},{"name":"maxBaseAmount","type":"u64","index":false},{"name":"minLpTokens","type":"u64","index":false},{"name":"baseAmount","type":"u64","index":false},{"name":"lpTokensMinted","type":"u64","index":false}]},{"name":"RemoveLiquidityEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"lpTokensBurned","type":"u64","index":false},{"name":"minQuoteAmount","type":"u64","index":false},{"name":"minBaseAmount","type":"u64","index":false},{"name":"baseAmount","type":"u64","index":false},{"name":"quoteAmount","type":"u64","index":false}]},{"name":"CreateAmmEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"twapInitialObservation","type":"u128","index":false},{"name":"twapMaxObservationChangePerUpdate","type":"u128","index":false},{"name":"lpMint","type":"publicKey","index":false},{"name":"baseMint","type":"publicKey","index":false},{"name":"quoteMint","type":"publicKey","index":false},{"name":"vaultAtaBase","type":"publicKey","index":false},{"name":"vaultAtaQuote","type":"publicKey","index":false}]},{"name":"CrankThatTwapEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false}]}],"errors":[{"code":6000,"name":"AssertFailed","msg":"An assertion failed"},{"code":6001,"name":"NoSlotsPassed","msg":"Can't get a TWAP before some observations have been stored"},{"code":6002,"name":"NoReserves","msg":"Can't swap through a pool without token reserves on either side"},{"code":6003,"name":"InputAmountOverflow","msg":"Input token amount is too large for a swap, causes overflow"},{"code":6004,"name":"AddLiquidityCalculationError","msg":"Add liquidity calculation error"},{"code":6005,"name":"DecimalScaleError","msg":"Error in decimal scale conversion"},{"code":6006,"name":"SameTokenMints","msg":"You can't create an AMM pool where the token mints are the same"},{"code":6007,"name":"SwapSlippageExceeded","msg":"A user wouldn't have gotten back their `output_amount_min`, reverting"},{"code":6008,"name":"InsufficientBalance","msg":"The user had insufficient balance to do this"},{"code":6009,"name":"ZeroLiquidityRemove","msg":"Must remove a non-zero amount of liquidity"},{"code":6010,"name":"ZeroLiquidityToAdd","msg":"Cannot add liquidity with 0 tokens on either side"},{"code":6011,"name":"ZeroMinLpTokens","msg":"Must specify a non-zero `min_lp_tokens` when adding to an existing pool"},{"code":6012,"name":"AddLiquiditySlippageExceeded","msg":"LP wouldn't have gotten back `lp_token_min`"},{"code":6013,"name":"AddLiquidityMaxBaseExceeded","msg":"LP would have spent more than `max_base_amount`"},{"code":6014,"name":"InsufficientQuoteAmount","msg":"`quote_amount` must be greater than 100000000 when initializing a pool"},{"code":6015,"name":"ZeroSwapAmount","msg":"Users must swap a non-zero amount"},{"code":6016,"name":"ConstantProductInvariantFailed","msg":"K should always be increasing"},{"code":6017,"name":"CastingOverflow","msg":"Casting has caused an overflow"}]} \ No newline at end of file diff --git a/idls/metadao_futarchy.FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq.json b/idls/metadao_futarchy.FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq.json new file mode 100644 index 0000000..6d88d2c --- /dev/null +++ b/idls/metadao_futarchy.FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq.json @@ -0,0 +1 @@ +{"version":"0.6.1","name":"futarchy","instructions":[{"name":"initializeDao","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"daoCreator","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"squadsMultisig","isMut":true,"isSigner":false},{"name":"squadsMultisigVault","isMut":false,"isSigner":false},{"name":"squadsProgram","isMut":false,"isSigner":false},{"name":"squadsProgramConfig","isMut":false,"isSigner":false},{"name":"squadsProgramConfigTreasury","isMut":true,"isSigner":false},{"name":"spendingLimit","isMut":true,"isSigner":false},{"name":"futarchyAmmBaseVault","isMut":true,"isSigner":false},{"name":"futarchyAmmQuoteVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitializeDaoParams"}}]},{"name":"initializeProposal","accounts":[{"name":"proposal","isMut":true,"isSigner":false},{"name":"squadsProposal","isMut":false,"isSigner":false},{"name":"squadsMultisig","isMut":false,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"question","isMut":false,"isSigner":false},{"name":"quoteVault","isMut":false,"isSigner":false},{"name":"baseVault","isMut":false,"isSigner":false},{"name":"proposer","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"stakeToProposal","accounts":[{"name":"proposal","isMut":true,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"stakerBaseAccount","isMut":true,"isSigner":false},{"name":"proposalBaseAccount","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"staker","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"StakeToProposalParams"}}]},{"name":"unstakeFromProposal","accounts":[{"name":"proposal","isMut":true,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"stakerBaseAccount","isMut":true,"isSigner":false},{"name":"proposalBaseAccount","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"staker","isMut":true,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"UnstakeFromProposalParams"}}]},{"name":"launchProposal","accounts":[{"name":"proposal","isMut":true,"isSigner":false},{"name":"baseVault","isMut":false,"isSigner":false},{"name":"quoteVault","isMut":false,"isSigner":false},{"name":"passBaseMint","isMut":false,"isSigner":false},{"name":"passQuoteMint","isMut":false,"isSigner":false},{"name":"failBaseMint","isMut":false,"isSigner":false},{"name":"failQuoteMint","isMut":false,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"ammPassBaseVault","isMut":true,"isSigner":false},{"name":"ammPassQuoteVault","isMut":true,"isSigner":false},{"name":"ammFailBaseVault","isMut":true,"isSigner":false},{"name":"ammFailQuoteVault","isMut":true,"isSigner":false},{"name":"squadsMultisig","isMut":false,"isSigner":false},{"name":"squadsProposal","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"finalizeProposal","accounts":[{"name":"proposal","isMut":true,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"question","isMut":true,"isSigner":false},{"name":"squadsProposal","isMut":true,"isSigner":false},{"name":"squadsMultisig","isMut":false,"isSigner":false},{"name":"squadsMultisigProgram","isMut":false,"isSigner":false},{"name":"ammPassBaseVault","isMut":true,"isSigner":false},{"name":"ammPassQuoteVault","isMut":true,"isSigner":false},{"name":"ammFailBaseVault","isMut":true,"isSigner":false},{"name":"ammFailQuoteVault","isMut":true,"isSigner":false},{"name":"ammBaseVault","isMut":true,"isSigner":false},{"name":"ammQuoteVault","isMut":true,"isSigner":false},{"name":"vaultProgram","isMut":false,"isSigner":false},{"name":"vaultEventAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"quoteVault","isMut":true,"isSigner":false},{"name":"quoteVaultUnderlyingTokenAccount","isMut":true,"isSigner":false},{"name":"passQuoteMint","isMut":true,"isSigner":false},{"name":"failQuoteMint","isMut":true,"isSigner":false},{"name":"passBaseMint","isMut":true,"isSigner":false},{"name":"failBaseMint","isMut":true,"isSigner":false},{"name":"baseVault","isMut":true,"isSigner":false},{"name":"baseVaultUnderlyingTokenAccount","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"updateDao","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"squadsMultisigVault","isMut":false,"isSigner":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"daoParams","type":{"defined":"UpdateDaoParams"}}]},{"name":"resizeDao","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"spotSwap","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"ammBaseVault","isMut":true,"isSigner":false},{"name":"ammQuoteVault","isMut":true,"isSigner":false},{"name":"user","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SpotSwapParams"}}]},{"name":"conditionalSwap","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"ammBaseVault","isMut":true,"isSigner":false},{"name":"ammQuoteVault","isMut":true,"isSigner":false},{"name":"proposal","isMut":false,"isSigner":false},{"name":"ammPassBaseVault","isMut":true,"isSigner":false},{"name":"ammPassQuoteVault","isMut":true,"isSigner":false},{"name":"ammFailBaseVault","isMut":true,"isSigner":false},{"name":"ammFailQuoteVault","isMut":true,"isSigner":false},{"name":"trader","isMut":false,"isSigner":true},{"name":"userInputAccount","isMut":true,"isSigner":false},{"name":"userOutputAccount","isMut":true,"isSigner":false},{"name":"baseVault","isMut":true,"isSigner":false},{"name":"baseVaultUnderlyingTokenAccount","isMut":true,"isSigner":false},{"name":"quoteVault","isMut":true,"isSigner":false},{"name":"quoteVaultUnderlyingTokenAccount","isMut":true,"isSigner":false},{"name":"passBaseMint","isMut":true,"isSigner":false},{"name":"failBaseMint","isMut":true,"isSigner":false},{"name":"passQuoteMint","isMut":true,"isSigner":false},{"name":"failQuoteMint","isMut":true,"isSigner":false},{"name":"conditionalVaultProgram","isMut":false,"isSigner":false},{"name":"vaultEventAuthority","isMut":false,"isSigner":false},{"name":"question","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"ConditionalSwapParams"}}]},{"name":"provideLiquidity","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"liquidityProvider","isMut":false,"isSigner":true},{"name":"liquidityProviderBaseAccount","isMut":true,"isSigner":false},{"name":"liquidityProviderQuoteAccount","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"ammBaseVault","isMut":true,"isSigner":false},{"name":"ammQuoteVault","isMut":true,"isSigner":false},{"name":"ammPosition","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"ProvideLiquidityParams"}}]},{"name":"withdrawLiquidity","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"positionAuthority","isMut":false,"isSigner":true},{"name":"liquidityProviderBaseAccount","isMut":true,"isSigner":false},{"name":"liquidityProviderQuoteAccount","isMut":true,"isSigner":false},{"name":"ammBaseVault","isMut":true,"isSigner":false},{"name":"ammQuoteVault","isMut":true,"isSigner":false},{"name":"ammPosition","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"WithdrawLiquidityParams"}}]},{"name":"collectFees","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"baseTokenAccount","isMut":true,"isSigner":false},{"name":"quoteTokenAccount","isMut":true,"isSigner":false},{"name":"ammBaseVault","isMut":true,"isSigner":false},{"name":"ammQuoteVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"executeSpendingLimitChange","accounts":[{"name":"proposal","isMut":true,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"squadsProposal","isMut":true,"isSigner":false},{"name":"squadsMultisig","isMut":false,"isSigner":false},{"name":"squadsMultisigProgram","isMut":false,"isSigner":false},{"name":"vaultTransaction","isMut":false,"isSigner":false}],"args":[]},{"name":"sponsorProposal","accounts":[{"name":"proposal","isMut":true,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"teamAddress","isMut":false,"isSigner":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"collectMeteoraDammFees","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"squadsMultisig","isMut":true,"isSigner":false},{"name":"squadsMultisigVault","isMut":true,"isSigner":false},{"name":"squadsMultisigVaultTransaction","isMut":true,"isSigner":false},{"name":"squadsMultisigProposal","isMut":true,"isSigner":false},{"name":"squadsMultisigPermissionlessAccount","isMut":false,"isSigner":true},{"name":"meteoraClaimPositionFeesAccounts","accounts":[{"name":"dammV2Program","isMut":false,"isSigner":false},{"name":"dammV2EventAuthority","isMut":false,"isSigner":false},{"name":"poolAuthority","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"tokenAAccount","isMut":true,"isSigner":false,"docs":["Token account of base tokens recipient"]},{"name":"tokenBAccount","isMut":true,"isSigner":false,"docs":["Token account of quote tokens recipient"]},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"positionNftAccount","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":false},{"name":"tokenAProgram","isMut":false,"isSigner":false},{"name":"tokenBProgram","isMut":false,"isSigner":false}]},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"squadsProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"initiateVaultSpendOptimisticProposal","accounts":[{"name":"squadsMultisig","isMut":false,"isSigner":false},{"name":"squadsMultisigVault","isMut":false,"isSigner":false},{"name":"squadsSpendingLimit","isMut":false,"isSigner":false},{"name":"squadsProposal","isMut":false,"isSigner":false},{"name":"squadsVaultTransaction","isMut":false,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"daoQuoteVaultAccount","isMut":false,"isSigner":false},{"name":"proposer","isMut":false,"isSigner":true},{"name":"recipient","isMut":false,"isSigner":false},{"name":"recipientQuoteAccount","isMut":false,"isSigner":false},{"name":"squadsProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitiateVaultSpendOptimisticProposalParams"}}]},{"name":"finalizeOptimisticProposal","accounts":[{"name":"squadsMultisig","isMut":true,"isSigner":false},{"name":"squadsProposal","isMut":true,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"squadsProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"adminEnqueueMultisigProposalApproval","accounts":[{"name":"dao","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"squadsMultisig","isMut":false,"isSigner":false},{"name":"squadsMultisigProposal","isMut":false,"isSigner":false},{"name":"enqueuedApproval","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"AdminEnqueueMultisigProposalApprovalArgs"}}]},{"name":"executeMultisigProposalApproval","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"rentReceiver","isMut":true,"isSigner":true},{"name":"squadsMultisig","isMut":true,"isSigner":false},{"name":"squadsMultisigProposal","isMut":true,"isSigner":false},{"name":"enqueuedApproval","isMut":true,"isSigner":false},{"name":"squadsMultisigProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"adminExecuteMultisigProposal","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"squadsMultisig","isMut":true,"isSigner":false},{"name":"squadsMultisigProposal","isMut":true,"isSigner":false},{"name":"squadsMultisigVaultTransaction","isMut":true,"isSigner":false},{"name":"squadsMultisigProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"adminCancelProposal","accounts":[{"name":"proposal","isMut":true,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"question","isMut":true,"isSigner":false},{"name":"squadsProposal","isMut":true,"isSigner":false},{"name":"squadsMultisig","isMut":false,"isSigner":false},{"name":"squadsMultisigProgram","isMut":false,"isSigner":false},{"name":"ammPassBaseVault","isMut":true,"isSigner":false},{"name":"ammPassQuoteVault","isMut":true,"isSigner":false},{"name":"ammFailBaseVault","isMut":true,"isSigner":false},{"name":"ammFailQuoteVault","isMut":true,"isSigner":false},{"name":"ammBaseVault","isMut":true,"isSigner":false},{"name":"ammQuoteVault","isMut":true,"isSigner":false},{"name":"vaultProgram","isMut":false,"isSigner":false},{"name":"vaultEventAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"quoteVault","isMut":true,"isSigner":false},{"name":"quoteVaultUnderlyingTokenAccount","isMut":true,"isSigner":false},{"name":"passQuoteMint","isMut":true,"isSigner":false},{"name":"failQuoteMint","isMut":true,"isSigner":false},{"name":"passBaseMint","isMut":true,"isSigner":false},{"name":"failBaseMint","isMut":true,"isSigner":false},{"name":"baseVault","isMut":true,"isSigner":false},{"name":"baseVaultUnderlyingTokenAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"adminRemoveProposal","accounts":[{"name":"proposal","isMut":true,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"AmmPosition","type":{"kind":"struct","fields":[{"name":"dao","type":"publicKey"},{"name":"positionAuthority","type":"publicKey"},{"name":"liquidity","type":"u128"}]}},{"name":"Dao","type":{"kind":"struct","fields":[{"name":"amm","docs":["Embedded FutarchyAmm - 1:1 relationship"],"type":{"defined":"FutarchyAmm"}},{"name":"nonce","docs":["`nonce` + `dao_creator` are PDA seeds"],"type":"u64"},{"name":"daoCreator","type":"publicKey"},{"name":"pdaBump","type":"u8"},{"name":"squadsMultisig","type":"publicKey"},{"name":"squadsMultisigVault","type":"publicKey"},{"name":"baseMint","type":"publicKey"},{"name":"quoteMint","type":"publicKey"},{"name":"proposalCount","type":"u32"},{"name":"passThresholdBps","type":"u16"},{"name":"secondsPerProposal","type":"u32"},{"name":"twapInitialObservation","docs":["For manipulation-resistance the TWAP is a time-weighted average observation,","where observation tries to approximate price but can only move by","`twap_max_observation_change_per_update` per update. Because it can only move","a little bit per update, you need to check that it has a good initial observation.","Otherwise, an attacker could create a very high initial observation in the pass","market and a very low one in the fail market to force the proposal to pass.","","We recommend setting an initial observation around the spot price of the token,","and max observation change per update around 2% the spot price of the token.","For example, if the spot price of META is $400, we'd recommend setting an initial","observation of 400 (converted into the AMM prices) and a max observation change per","update of 8 (also converted into the AMM prices). Observations can be updated once","a minute, so 2% allows the proposal market to reach double the spot price or 0","in 50 minutes."],"type":"u128"},{"name":"twapMaxObservationChangePerUpdate","type":"u128"},{"name":"twapStartDelaySeconds","docs":["Forces TWAP calculation to start after `twap_start_delay_seconds` seconds"],"type":"u32"},{"name":"minQuoteFutarchicLiquidity","docs":["As an anti-spam measure and to help liquidity, you need to lock up some liquidity","in both futarchic markets in order to create a proposal.","","For example, for META, we can use a `min_quote_futarchic_liquidity` of","5000 * 1_000_000 (5000 USDC) and a `min_base_futarchic_liquidity` of","10 * 1_000_000_000 (10 META)."],"type":"u64"},{"name":"minBaseFutarchicLiquidity","type":"u64"},{"name":"baseToStake","docs":["Minimum amount of base tokens that must be staked to launch a proposal"],"type":"u64"},{"name":"seqNum","type":"u64"},{"name":"initialSpendingLimit","type":{"option":{"defined":"InitialSpendingLimit"}}},{"name":"teamSponsoredPassThresholdBps","docs":["The percentage, in basis points, the pass price needs to be above the","fail price in order for the proposal to pass for team-sponsored proposals.","","Can be negative to allow for team-sponsored proposals to pass by default."],"type":"i16"},{"name":"teamAddress","type":"publicKey"},{"name":"optimisticProposal","type":{"option":{"defined":"OptimisticProposal"}}},{"name":"isOptimisticGovernanceEnabled","type":"bool"}]}},{"name":"OldDao","type":{"kind":"struct","fields":[{"name":"amm","docs":["Embedded FutarchyAmm - 1:1 relationship"],"type":{"defined":"FutarchyAmm"}},{"name":"nonce","docs":["`nonce` + `dao_creator` are PDA seeds"],"type":"u64"},{"name":"daoCreator","type":"publicKey"},{"name":"pdaBump","type":"u8"},{"name":"squadsMultisig","type":"publicKey"},{"name":"squadsMultisigVault","type":"publicKey"},{"name":"baseMint","type":"publicKey"},{"name":"quoteMint","type":"publicKey"},{"name":"proposalCount","type":"u32"},{"name":"passThresholdBps","type":"u16"},{"name":"secondsPerProposal","type":"u32"},{"name":"twapInitialObservation","docs":["For manipulation-resistance the TWAP is a time-weighted average observation,","where observation tries to approximate price but can only move by","`twap_max_observation_change_per_update` per update. Because it can only move","a little bit per update, you need to check that it has a good initial observation.","Otherwise, an attacker could create a very high initial observation in the pass","market and a very low one in the fail market to force the proposal to pass.","","We recommend setting an initial observation around the spot price of the token,","and max observation change per update around 2% the spot price of the token.","For example, if the spot price of META is $400, we'd recommend setting an initial","observation of 400 (converted into the AMM prices) and a max observation change per","update of 8 (also converted into the AMM prices). Observations can be updated once","a minute, so 2% allows the proposal market to reach double the spot price or 0","in 50 minutes."],"type":"u128"},{"name":"twapMaxObservationChangePerUpdate","type":"u128"},{"name":"twapStartDelaySeconds","docs":["Forces TWAP calculation to start after `twap_start_delay_seconds` seconds"],"type":"u32"},{"name":"minQuoteFutarchicLiquidity","docs":["As an anti-spam measure and to help liquidity, you need to lock up some liquidity","in both futarchic markets in order to create a proposal.","","For example, for META, we can use a `min_quote_futarchic_liquidity` of","5000 * 1_000_000 (5000 USDC) and a `min_base_futarchic_liquidity` of","10 * 1_000_000_000 (10 META)."],"type":"u64"},{"name":"minBaseFutarchicLiquidity","type":"u64"},{"name":"baseToStake","docs":["Minimum amount of base tokens that must be staked to launch a proposal"],"type":"u64"},{"name":"seqNum","type":"u64"},{"name":"initialSpendingLimit","type":{"option":{"defined":"InitialSpendingLimit"}}},{"name":"teamSponsoredPassThresholdBps","docs":["The percentage, in basis points, the pass price needs to be above the","fail price in order for the proposal to pass for team-sponsored proposals.","","Can be negative to allow for team-sponsored proposals to pass by default."],"type":"i16"},{"name":"teamAddress","type":"publicKey"}]}},{"name":"EnqueuedMultisigProposalApproval","type":{"kind":"struct","fields":[{"name":"dao","type":"publicKey"},{"name":"transactionIndex","type":"u64"},{"name":"pdaBump","type":"u8"}]}},{"name":"Proposal","type":{"kind":"struct","fields":[{"name":"number","type":"u32"},{"name":"proposer","type":"publicKey"},{"name":"timestampEnqueued","type":"i64"},{"name":"state","type":{"defined":"ProposalState"}},{"name":"baseVault","type":"publicKey"},{"name":"quoteVault","type":"publicKey"},{"name":"dao","type":"publicKey"},{"name":"pdaBump","type":"u8"},{"name":"question","type":"publicKey"},{"name":"durationInSeconds","type":"u32"},{"name":"squadsProposal","type":"publicKey"},{"name":"passBaseMint","type":"publicKey"},{"name":"passQuoteMint","type":"publicKey"},{"name":"failBaseMint","type":"publicKey"},{"name":"failQuoteMint","type":"publicKey"},{"name":"isTeamSponsored","type":"bool"}]}},{"name":"StakeAccount","type":{"kind":"struct","fields":[{"name":"proposal","type":"publicKey"},{"name":"staker","type":"publicKey"},{"name":"amount","type":"u64"},{"name":"bump","type":"u8"}]}}],"types":[{"name":"CommonFields","type":{"kind":"struct","fields":[{"name":"slot","type":"u64"},{"name":"unixTimestamp","type":"i64"},{"name":"daoSeqNum","type":"u64"}]}},{"name":"AdminEnqueueMultisigProposalApprovalArgs","type":{"kind":"struct","fields":[{"name":"transactionIndex","type":"u64"}]}},{"name":"ConditionalSwapParams","type":{"kind":"struct","fields":[{"name":"market","type":{"defined":"Market"}},{"name":"swapType","type":{"defined":"SwapType"}},{"name":"inputAmount","type":"u64"},{"name":"minOutputAmount","type":"u64"}]}},{"name":"InitializeDaoParams","type":{"kind":"struct","fields":[{"name":"twapInitialObservation","type":"u128"},{"name":"twapMaxObservationChangePerUpdate","type":"u128"},{"name":"twapStartDelaySeconds","type":"u32"},{"name":"minQuoteFutarchicLiquidity","type":"u64"},{"name":"minBaseFutarchicLiquidity","type":"u64"},{"name":"baseToStake","type":"u64"},{"name":"passThresholdBps","type":"u16"},{"name":"secondsPerProposal","type":"u32"},{"name":"nonce","type":"u64"},{"name":"initialSpendingLimit","type":{"option":{"defined":"InitialSpendingLimit"}}},{"name":"teamSponsoredPassThresholdBps","type":"i16"},{"name":"teamAddress","type":"publicKey"}]}},{"name":"InitiateVaultSpendOptimisticProposalParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"ProvideLiquidityParams","type":{"kind":"struct","fields":[{"name":"quoteAmount","docs":["How much quote token you will deposit to the pool"],"type":"u64"},{"name":"maxBaseAmount","docs":["The maximum base token you will deposit to the pool"],"type":"u64"},{"name":"minLiquidity","docs":["The minimum liquidity you will be assigned"],"type":"u128"},{"name":"positionAuthority","docs":["The account that will own the LP position, usually the same as the","liquidity provider"],"type":"publicKey"}]}},{"name":"SpotSwapParams","type":{"kind":"struct","fields":[{"name":"inputAmount","type":"u64"},{"name":"swapType","type":{"defined":"SwapType"}},{"name":"minOutputAmount","type":"u64"}]}},{"name":"StakeToProposalParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"UnstakeFromProposalParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"UpdateDaoParams","type":{"kind":"struct","fields":[{"name":"passThresholdBps","type":{"option":"u16"}},{"name":"secondsPerProposal","type":{"option":"u32"}},{"name":"twapInitialObservation","type":{"option":"u128"}},{"name":"twapMaxObservationChangePerUpdate","type":{"option":"u128"}},{"name":"twapStartDelaySeconds","type":{"option":"u32"}},{"name":"minQuoteFutarchicLiquidity","type":{"option":"u64"}},{"name":"minBaseFutarchicLiquidity","type":{"option":"u64"}},{"name":"baseToStake","type":{"option":"u64"}},{"name":"teamSponsoredPassThresholdBps","type":{"option":"i16"}},{"name":"teamAddress","type":{"option":"publicKey"}},{"name":"isOptimisticGovernanceEnabled","type":{"option":"bool"}}]}},{"name":"WithdrawLiquidityParams","type":{"kind":"struct","fields":[{"name":"liquidityToWithdraw","docs":["How much liquidity to withdraw"],"type":"u128"},{"name":"minBaseAmount","docs":["Minimum base tokens to receive"],"type":"u64"},{"name":"minQuoteAmount","docs":["Minimum quote tokens to receive"],"type":"u64"}]}},{"name":"OptimisticProposal","type":{"kind":"struct","fields":[{"name":"squadsProposal","docs":["The squads proposal currently enqueued for execution if not challenged by a new proposal."],"type":"publicKey"},{"name":"enqueuedTimestamp","docs":["The timestamp when the active optimistic squads proposal was enqueued."],"type":"i64"}]}},{"name":"InitialSpendingLimit","type":{"kind":"struct","fields":[{"name":"amountPerMonth","type":"u64"},{"name":"members","type":{"vec":"publicKey"}}]}},{"name":"FutarchyAmm","type":{"kind":"struct","fields":[{"name":"state","type":{"defined":"PoolState"}},{"name":"totalLiquidity","type":"u128"},{"name":"baseMint","type":"publicKey"},{"name":"quoteMint","type":"publicKey"},{"name":"ammBaseVault","type":"publicKey"},{"name":"ammQuoteVault","type":"publicKey"}]}},{"name":"TwapOracle","type":{"kind":"struct","fields":[{"name":"aggregator","docs":["Running sum of seconds_since_last_update * last_observation.","","Assuming latest observations are as big as possible (u64::MAX * 1e12),","we can store 18 million seconds worth of observations, which turns out to","be ~213 days.","","Assuming that latest observations are 100x smaller than they could theoretically","be, we can store ~57 years worth of them. Even this is a very","very conservative assumption - META/USDC prices should be between 1e9 and","1e15, which would overflow after 1e15 years.","","So in the case of an overflow, the aggregator rolls back to 0. It's the","client's responsibility to sanity check the assets or to handle an","aggregator at T2 being smaller than an aggregator at T1."],"type":"u128"},{"name":"lastUpdatedTimestamp","type":"i64"},{"name":"createdAtTimestamp","type":"i64"},{"name":"lastPrice","docs":["A price is the number of quote units per base unit multiplied by 1e12.","You cannot simply divide by 1e12 to get a price you can display in the UI","because the base and quote decimals may be different. Instead, do:","ui_price = (price * (10**(base_decimals - quote_decimals))) / 1e12"],"type":"u128"},{"name":"lastObservation","docs":["If we did a raw TWAP over prices, someone could push the TWAP heavily with","a few extremely large outliers. So we use observations, which can only move","by `max_observation_change_per_update` per update."],"type":"u128"},{"name":"maxObservationChangePerUpdate","docs":["The most that an observation can change per update."],"type":"u128"},{"name":"initialObservation","docs":["What the initial `latest_observation` is set to."],"type":"u128"},{"name":"startDelaySeconds","docs":["Number of seconds after amm.created_at_slot to start recording TWAP"],"type":"u32"}]}},{"name":"Pool","type":{"kind":"struct","fields":[{"name":"oracle","type":{"defined":"TwapOracle"}},{"name":"quoteReserves","type":"u64"},{"name":"baseReserves","type":"u64"},{"name":"quoteProtocolFeeBalance","type":"u64"},{"name":"baseProtocolFeeBalance","type":"u64"}]}},{"name":"PoolState","type":{"kind":"enum","variants":[{"name":"Spot","fields":[{"name":"spot","type":{"defined":"Pool"}}]},{"name":"Futarchy","fields":[{"name":"spot","type":{"defined":"Pool"}},{"name":"pass","type":{"defined":"Pool"}},{"name":"fail","type":{"defined":"Pool"}}]}]}},{"name":"Market","type":{"kind":"enum","variants":[{"name":"Spot"},{"name":"Pass"},{"name":"Fail"}]}},{"name":"SwapType","type":{"kind":"enum","variants":[{"name":"Buy"},{"name":"Sell"}]}},{"name":"Token","type":{"kind":"enum","variants":[{"name":"Base"},{"name":"Quote"}]}},{"name":"ProposalState","type":{"kind":"enum","variants":[{"name":"Draft","fields":[{"name":"amountStaked","type":"u64"}]},{"name":"Pending"},{"name":"Passed"},{"name":"Failed"},{"name":"Removed"}]}}],"events":[{"name":"CollectFeesEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"baseTokenAccount","type":"publicKey","index":false},{"name":"quoteTokenAccount","type":"publicKey","index":false},{"name":"ammBaseVault","type":"publicKey","index":false},{"name":"ammQuoteVault","type":"publicKey","index":false},{"name":"quoteMint","type":"publicKey","index":false},{"name":"baseMint","type":"publicKey","index":false},{"name":"quoteFeesCollected","type":"u64","index":false},{"name":"baseFeesCollected","type":"u64","index":false},{"name":"postAmmState","type":{"defined":"FutarchyAmm"},"index":false}]},{"name":"InitializeDaoEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"baseMint","type":"publicKey","index":false},{"name":"quoteMint","type":"publicKey","index":false},{"name":"passThresholdBps","type":"u16","index":false},{"name":"secondsPerProposal","type":"u32","index":false},{"name":"twapInitialObservation","type":"u128","index":false},{"name":"twapMaxObservationChangePerUpdate","type":"u128","index":false},{"name":"twapStartDelaySeconds","type":"u32","index":false},{"name":"minQuoteFutarchicLiquidity","type":"u64","index":false},{"name":"minBaseFutarchicLiquidity","type":"u64","index":false},{"name":"baseToStake","type":"u64","index":false},{"name":"initialSpendingLimit","type":{"option":{"defined":"InitialSpendingLimit"}},"index":false},{"name":"squadsMultisig","type":"publicKey","index":false},{"name":"squadsMultisigVault","type":"publicKey","index":false},{"name":"teamSponsoredPassThresholdBps","type":"i16","index":false},{"name":"teamAddress","type":"publicKey","index":false}]},{"name":"UpdateDaoEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"passThresholdBps","type":"u16","index":false},{"name":"secondsPerProposal","type":"u32","index":false},{"name":"twapInitialObservation","type":"u128","index":false},{"name":"twapMaxObservationChangePerUpdate","type":"u128","index":false},{"name":"twapStartDelaySeconds","type":"u32","index":false},{"name":"minQuoteFutarchicLiquidity","type":"u64","index":false},{"name":"minBaseFutarchicLiquidity","type":"u64","index":false},{"name":"baseToStake","type":"u64","index":false},{"name":"teamSponsoredPassThresholdBps","type":"i16","index":false},{"name":"teamAddress","type":"publicKey","index":false},{"name":"isOptimisticGovernanceEnabled","type":"bool","index":false}]},{"name":"InitializeProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"dao","type":"publicKey","index":false},{"name":"question","type":"publicKey","index":false},{"name":"quoteVault","type":"publicKey","index":false},{"name":"baseVault","type":"publicKey","index":false},{"name":"proposer","type":"publicKey","index":false},{"name":"number","type":"u32","index":false},{"name":"pdaBump","type":"u8","index":false},{"name":"durationInSeconds","type":"u32","index":false},{"name":"squadsProposal","type":"publicKey","index":false},{"name":"squadsMultisig","type":"publicKey","index":false},{"name":"squadsMultisigVault","type":"publicKey","index":false}]},{"name":"StakeToProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"staker","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"totalStaked","type":"u64","index":false}]},{"name":"UnstakeFromProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"staker","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"totalStaked","type":"u64","index":false}]},{"name":"LaunchProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"dao","type":"publicKey","index":false},{"name":"timestampEnqueued","type":"i64","index":false},{"name":"totalStaked","type":"u64","index":false},{"name":"postAmmState","type":{"defined":"FutarchyAmm"},"index":false}]},{"name":"FinalizeProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"dao","type":"publicKey","index":false},{"name":"passMarketTwap","type":"u128","index":false},{"name":"failMarketTwap","type":"u128","index":false},{"name":"threshold","type":"u128","index":false},{"name":"state","type":{"defined":"ProposalState"},"index":false},{"name":"squadsProposal","type":"publicKey","index":false},{"name":"squadsMultisig","type":"publicKey","index":false},{"name":"postAmmState","type":{"defined":"FutarchyAmm"},"index":false},{"name":"isTeamSponsored","type":"bool","index":false}]},{"name":"SpotSwapEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"user","type":"publicKey","index":false},{"name":"swapType","type":{"defined":"SwapType"},"index":false},{"name":"inputAmount","type":"u64","index":false},{"name":"outputAmount","type":"u64","index":false},{"name":"minOutputAmount","type":"u64","index":false},{"name":"postAmmState","type":{"defined":"FutarchyAmm"},"index":false}]},{"name":"ConditionalSwapEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"trader","type":"publicKey","index":false},{"name":"market","type":{"defined":"Market"},"index":false},{"name":"swapType","type":{"defined":"SwapType"},"index":false},{"name":"inputAmount","type":"u64","index":false},{"name":"outputAmount","type":"u64","index":false},{"name":"minOutputAmount","type":"u64","index":false},{"name":"postAmmState","type":{"defined":"FutarchyAmm"},"index":false}]},{"name":"ProvideLiquidityEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"liquidityProvider","type":"publicKey","index":false},{"name":"positionAuthority","type":"publicKey","index":false},{"name":"quoteAmount","type":"u64","index":false},{"name":"baseAmount","type":"u64","index":false},{"name":"liquidityMinted","type":"u128","index":false},{"name":"minLiquidity","type":"u128","index":false},{"name":"postAmmState","type":{"defined":"FutarchyAmm"},"index":false}]},{"name":"WithdrawLiquidityEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"liquidityProvider","type":"publicKey","index":false},{"name":"liquidityWithdrawn","type":"u128","index":false},{"name":"minBaseAmount","type":"u64","index":false},{"name":"minQuoteAmount","type":"u64","index":false},{"name":"baseAmount","type":"u64","index":false},{"name":"quoteAmount","type":"u64","index":false},{"name":"postAmmState","type":{"defined":"FutarchyAmm"},"index":false}]},{"name":"SponsorProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"dao","type":"publicKey","index":false},{"name":"teamAddress","type":"publicKey","index":false}]},{"name":"RemoveProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"dao","type":"publicKey","index":false},{"name":"admin","type":"publicKey","index":false}]},{"name":"AdminCancelProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"dao","type":"publicKey","index":false},{"name":"admin","type":"publicKey","index":false},{"name":"postAmmState","type":{"defined":"FutarchyAmm"},"index":false}]},{"name":"CollectMeteoraDammFeesEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"baseTokenAccount","type":"publicKey","index":false},{"name":"quoteTokenAccount","type":"publicKey","index":false},{"name":"quoteMint","type":"publicKey","index":false},{"name":"baseMint","type":"publicKey","index":false},{"name":"quoteFeesCollected","type":"u64","index":false},{"name":"baseFeesCollected","type":"u64","index":false}]},{"name":"AdminFixPositionAuthorityEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"admin","type":"publicKey","index":false},{"name":"ammPosition","type":"publicKey","index":false},{"name":"oldAuthority","type":"publicKey","index":false},{"name":"newAuthority","type":"publicKey","index":false}]},{"name":"InitiateVaultSpendOptimisticProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"proposer","type":"publicKey","index":false},{"name":"squadsProposal","type":"publicKey","index":false},{"name":"squadsMultisig","type":"publicKey","index":false},{"name":"squadsMultisigVault","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"recipient","type":"publicKey","index":false},{"name":"daoQuoteVaultAccount","type":"publicKey","index":false},{"name":"recipientQuoteAccount","type":"publicKey","index":false},{"name":"enqueuedTimestamp","type":"i64","index":false}]},{"name":"FinalizeOptimisticProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"squadsProposal","type":"publicKey","index":false}]}],"errors":[{"code":6000,"name":"AmmTooOld","msg":"Amms must have been created within 5 minutes (counted in slots) of proposal initialization"},{"code":6001,"name":"InvalidInitialObservation","msg":"An amm has an `initial_observation` that doesn't match the `dao`'s config"},{"code":6002,"name":"InvalidMaxObservationChange","msg":"An amm has a `max_observation_change_per_update` that doesn't match the `dao`'s config"},{"code":6003,"name":"InvalidStartDelaySlots","msg":"An amm has a `start_delay_slots` that doesn't match the `dao`'s config"},{"code":6004,"name":"InvalidSettlementAuthority","msg":"One of the vaults has an invalid `settlement_authority`"},{"code":6005,"name":"ProposalTooYoung","msg":"Proposal is too young to be executed or rejected"},{"code":6006,"name":"MarketsTooYoung","msg":"Markets too young for proposal to be finalized. TWAP might need to be cranked"},{"code":6007,"name":"ProposalAlreadyFinalized","msg":"This proposal has already been finalized"},{"code":6008,"name":"InvalidVaultNonce","msg":"A conditional vault has an invalid nonce. A nonce should encode the proposal number"},{"code":6009,"name":"ProposalNotPassed","msg":"This proposal can't be executed because it isn't in the passed state"},{"code":6010,"name":"InsufficientLiquidity","msg":"More liquidity needs to be in the AMM to launch this proposal"},{"code":6011,"name":"ProposalDurationTooShort","msg":"Proposal duration must be longer 1 day and longer than 2 times the TWAP start delay"},{"code":6012,"name":"PassThresholdTooHigh","msg":"Pass threshold must be less than 10%"},{"code":6013,"name":"QuestionMustBeBinary","msg":"Question must have exactly 2 outcomes for binary futarchy"},{"code":6014,"name":"InvalidSquadsProposalStatus","msg":"Squads proposal must be in Active status"},{"code":6015,"name":"CastingOverflow","msg":"Casting overflow. If you're seeing this, please report this"},{"code":6016,"name":"InsufficientBalance","msg":"Insufficient balance"},{"code":6017,"name":"ZeroLiquidityRemove","msg":"Cannot remove zero liquidity"},{"code":6018,"name":"SwapSlippageExceeded","msg":"Swap slippage exceeded"},{"code":6019,"name":"AssertFailed","msg":"Assert failed"},{"code":6020,"name":"InvalidAdmin","msg":"Invalid admin"},{"code":6021,"name":"ProposalNotInDraftState","msg":"Proposal is not in draft state"},{"code":6022,"name":"InsufficientTokenBalance","msg":"Insufficient token balance"},{"code":6023,"name":"InvalidAmount","msg":"Invalid amount"},{"code":6024,"name":"InsufficientStakeToLaunch","msg":"Insufficient stake to launch proposal"},{"code":6025,"name":"StakerNotFound","msg":"Staker not found in proposal"},{"code":6026,"name":"PoolNotInSpotState","msg":"Pool must be in spot state"},{"code":6027,"name":"InvalidDaoCreateLiquidity","msg":"If you're providing liquidity, you must provide both base and quote token accounts"},{"code":6028,"name":"InvalidStakeAccount","msg":"Invalid stake account"},{"code":6029,"name":"InvariantViolated","msg":"An invariant was violated. You should get in contact with the MetaDAO team if you see this"},{"code":6030,"name":"ProposalNotActive","msg":"Proposal needs to be active to perform a conditional swap"},{"code":6031,"name":"InvalidTransaction","msg":"This Squads transaction should only contain calls to update spending limits"},{"code":6032,"name":"ProposalAlreadySponsored","msg":"Proposal has already been sponsored"},{"code":6033,"name":"InvalidTeamSponsoredPassThreshold","msg":"Team sponsored pass threshold must be between -10% and 10%"},{"code":6034,"name":"InvalidTargetK","msg":"Target K must be greater than the current K"},{"code":6035,"name":"InvalidTransactionMessage","msg":"Failed to compile transaction message for Squads vault transaction"},{"code":6036,"name":"InvalidMint","msg":"Base mint and quote mint must be different"},{"code":6037,"name":"ProposalNotReadyToUnstake","msg":"Proposal is not ready to be unstaked"},{"code":6038,"name":"OptimisticGovernanceDisabled","msg":"Optimistic governance is disabled"},{"code":6039,"name":"ActiveOptimisticProposalAlreadyEnqueued","msg":"An active optimistic proposal is already enqueued"},{"code":6040,"name":"OptimisticProposalAlreadyPassed","msg":"Optimistic proposal has already passed"},{"code":6041,"name":"InvalidSpendingLimitMint","msg":"Invalid spending limit mint. Must be the same as the DAO's quote mint"},{"code":6042,"name":"NoActiveOptimisticProposal","msg":"No active optimistic proposal"}]} \ No newline at end of file diff --git a/idls/metadao_launchpad.moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM.json b/idls/metadao_launchpad.moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM.json new file mode 100644 index 0000000..0bf7c0c --- /dev/null +++ b/idls/metadao_launchpad.moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM.json @@ -0,0 +1 @@ +{"version":"0.7.0","name":"launchpad_v7","instructions":[{"name":"initializeLaunch","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"baseMint","isMut":true,"isSigner":false},{"name":"tokenMetadata","isMut":true,"isSigner":false},{"name":"launchSigner","isMut":false,"isSigner":false},{"name":"quoteVault","isMut":true,"isSigner":false},{"name":"baseVault","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"launchAuthority","isMut":false,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"additionalTokensRecipient","isMut":false,"isSigner":false,"isOptional":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"InitializeLaunchArgs"}}]},{"name":"startLaunch","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"launchAuthority","isMut":false,"isSigner":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"fund","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"fundingRecord","isMut":true,"isSigner":false},{"name":"launchQuoteVault","isMut":true,"isSigner":false},{"name":"funder","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"funderQuoteAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"setFundingRecordApproval","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"fundingRecord","isMut":true,"isSigner":false},{"name":"launchAuthority","isMut":false,"isSigner":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"approvedAmount","type":"u64"}]},{"name":"completeLaunch","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"launchAuthority","isMut":false,"isSigner":true,"isOptional":true},{"name":"tokenMetadata","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"launchSigner","isMut":true,"isSigner":false},{"name":"launchQuoteVault","isMut":true,"isSigner":false},{"name":"launchBaseVault","isMut":true,"isSigner":false},{"name":"treasuryQuoteAccount","isMut":true,"isSigner":false},{"name":"baseMint","isMut":true,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"daoOwnedLpPosition","isMut":true,"isSigner":false},{"name":"futarchyAmmBaseVault","isMut":true,"isSigner":false},{"name":"futarchyAmmQuoteVault","isMut":true,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"squadsMultisig","isMut":true,"isSigner":false},{"name":"squadsMultisigVault","isMut":false,"isSigner":false},{"name":"spendingLimit","isMut":true,"isSigner":false},{"name":"bidWall","isMut":true,"isSigner":false},{"name":"bidWallQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"feeRecipient","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"staticAccounts","accounts":[{"name":"futarchyProgram","isMut":false,"isSigner":false},{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"futarchyEventAuthority","isMut":false,"isSigner":false},{"name":"squadsProgram","isMut":false,"isSigner":false},{"name":"squadsProgramConfig","isMut":false,"isSigner":false},{"name":"squadsProgramConfigTreasury","isMut":true,"isSigner":false},{"name":"bidWallProgram","isMut":false,"isSigner":false},{"name":"bidWallEventAuthority","isMut":false,"isSigner":false}]},{"name":"meteoraAccounts","accounts":[{"name":"dammV2Program","isMut":false,"isSigner":false},{"name":"config","isMut":false,"isSigner":false},{"name":"token2022Program","isMut":false,"isSigner":false},{"name":"positionNftAccount","isMut":true,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"positionNftMint","isMut":true,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"poolCreatorAuthority","isMut":false,"isSigner":false},{"name":"poolAuthority","isMut":false,"isSigner":false},{"name":"dammV2EventAuthority","isMut":false,"isSigner":false}]},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"refund","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"fundingRecord","isMut":true,"isSigner":false},{"name":"launchQuoteVault","isMut":true,"isSigner":false},{"name":"launchSigner","isMut":false,"isSigner":false},{"name":"funder","isMut":false,"isSigner":false},{"name":"funderQuoteAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"claim","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"fundingRecord","isMut":true,"isSigner":false},{"name":"launchSigner","isMut":false,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"launchBaseVault","isMut":true,"isSigner":false},{"name":"funder","isMut":false,"isSigner":false},{"name":"funderTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"closeLaunch","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"claimAdditionalTokenAllocation","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"launchSigner","isMut":false,"isSigner":false},{"name":"launchBaseVault","isMut":true,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"additionalTokensRecipient","isMut":false,"isSigner":false},{"name":"additionalTokensRecipientTokenAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"initializePerformancePackage","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"launchSigner","isMut":false,"isSigner":false},{"name":"launchBaseVault","isMut":true,"isSigner":false},{"name":"baseMint","isMut":true,"isSigner":false},{"name":"dao","isMut":false,"isSigner":false},{"name":"squadsMultisig","isMut":false,"isSigner":false},{"name":"squadsMultisigVault","isMut":false,"isSigner":false},{"name":"performancePackage","isMut":true,"isSigner":false},{"name":"performancePackageTokenAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"squadsProgram","isMut":false,"isSigner":false},{"name":"priceBasedPerformancePackageProgram","isMut":false,"isSigner":false},{"name":"priceBasedPerformancePackageEventAuthority","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"resizeFundingRecord","accounts":[{"name":"fundingRecord","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"resizeLaunch","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"extendLaunch","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ExtendLaunchArgs"}}]}],"accounts":[{"name":"FundingRecord","type":{"kind":"struct","fields":[{"name":"pdaBump","docs":["The PDA bump."],"type":"u8"},{"name":"funder","docs":["The funder."],"type":"publicKey"},{"name":"launch","docs":["The launch."],"type":"publicKey"},{"name":"committedAmount","docs":["The amount of USDC that has been committed by the funder."],"type":"u64"},{"name":"isTokensClaimed","docs":["Whether the tokens have been claimed."],"type":"bool"},{"name":"isUsdcRefunded","docs":["Whether the USDC has been refunded."],"type":"bool"},{"name":"approvedAmount","docs":["The amount of USDC that the launch authority has approved for the funder.","If zero, the funder has not been approved for any amount."],"type":"u64"},{"name":"committedAmountAccumulator","docs":["Running integral of committed_amount over time (committed_amount * seconds)."],"type":"u128"},{"name":"lastAccumulatorUpdate","docs":["Unix timestamp of the last accumulator update."],"type":"i64"}]}},{"name":"OldFundingRecord","type":{"kind":"struct","fields":[{"name":"pdaBump","docs":["The PDA bump."],"type":"u8"},{"name":"funder","docs":["The funder."],"type":"publicKey"},{"name":"launch","docs":["The launch."],"type":"publicKey"},{"name":"committedAmount","docs":["The amount of USDC that has been committed by the funder."],"type":"u64"},{"name":"isTokensClaimed","docs":["Whether the tokens have been claimed."],"type":"bool"},{"name":"isUsdcRefunded","docs":["Whether the USDC has been refunded."],"type":"bool"},{"name":"approvedAmount","docs":["The amount of USDC that the launch authority has approved for the funder.","If zero, the funder has not been approved for any amount."],"type":"u64"}]}},{"name":"Launch","type":{"kind":"struct","fields":[{"name":"pdaBump","docs":["The PDA bump."],"type":"u8"},{"name":"minimumRaiseAmount","docs":["The minimum amount of USDC that must be raised, otherwise","everyone can get their USDC back."],"type":"u64"},{"name":"monthlySpendingLimitAmount","docs":["The monthly spending limit the DAO allocates to the team. Must be","less than 1/6th of the minimum raise amount (so 6 months of burn)."],"type":"u64"},{"name":"monthlySpendingLimitMembers","docs":["The wallets that have access to the monthly spending limit."],"type":{"vec":"publicKey"}},{"name":"launchAuthority","docs":["The account that can start the launch."],"type":"publicKey"},{"name":"launchSigner","docs":["The launch signer address. Needed because Raydium pools need a SOL payer and this PDA can't hold SOL."],"type":"publicKey"},{"name":"launchSignerPdaBump","docs":["The PDA bump for the launch signer."],"type":"u8"},{"name":"launchQuoteVault","docs":["The USDC vault that will hold the USDC raised until the launch is over."],"type":"publicKey"},{"name":"launchBaseVault","docs":["The token vault, used to send tokens to Raydium."],"type":"publicKey"},{"name":"baseMint","docs":["The token that will be minted to funders and that will control the DAO."],"type":"publicKey"},{"name":"quoteMint","docs":["The USDC mint."],"type":"publicKey"},{"name":"unixTimestampStarted","docs":["The unix timestamp when the launch was started."],"type":{"option":"i64"}},{"name":"unixTimestampClosed","docs":["The unix timestamp when the launch stopped taking new contributions."],"type":{"option":"i64"}},{"name":"totalCommittedAmount","docs":["The amount of USDC that has been committed by the users."],"type":"u64"},{"name":"state","docs":["The state of the launch."],"type":{"defined":"LaunchState"}},{"name":"seqNum","docs":["The sequence number of this launch. Useful for sorting events."],"type":"u64"},{"name":"secondsForLaunch","docs":["The number of seconds that the launch will be live for."],"type":"u32"},{"name":"dao","docs":["The DAO, if the launch is complete."],"type":{"option":"publicKey"}},{"name":"daoVault","docs":["The DAO treasury that USDC / LP is sent to, if the launch is complete."],"type":{"option":"publicKey"}},{"name":"performancePackageGrantee","docs":["The address that will receive the performance package tokens."],"type":"publicKey"},{"name":"performancePackageTokenAmount","docs":["The amount of tokens to be granted to the performance package grantee."],"type":"u64"},{"name":"monthsUntilInsidersCanUnlock","docs":["The number of months that insiders must wait before unlocking their tokens."],"type":"u8"},{"name":"teamAddress","docs":["The initial address used to sponsor team proposals."],"type":"publicKey"},{"name":"totalApprovedAmount","docs":["The amount of USDC that the launch authority has approved across all funders."],"type":"u64"},{"name":"additionalTokensAmount","docs":["The amount of additional tokens to be minted on a successful launch."],"type":"u64"},{"name":"additionalTokensRecipient","docs":["The token account that will receive the additional tokens."],"type":{"option":"publicKey"}},{"name":"additionalTokensClaimed","docs":["Are the additional tokens claimed"],"type":"bool"},{"name":"unixTimestampCompleted","docs":["The unix timestamp when the launch was completed."],"type":{"option":"i64"}},{"name":"isPerformancePackageInitialized","docs":["Whether the performance package has been initialized."],"type":"bool"},{"name":"accumulatorActivationDelaySeconds","docs":["Number of seconds after launch start before the funding accumulator","begins tracking."],"type":"u32"},{"name":"hasBidWall","docs":["Whether the launch has a bid wall."],"type":"bool"}]}},{"name":"OldLaunch","type":{"kind":"struct","fields":[{"name":"pdaBump","docs":["The PDA bump."],"type":"u8"},{"name":"minimumRaiseAmount","docs":["The minimum amount of USDC that must be raised, otherwise","everyone can get their USDC back."],"type":"u64"},{"name":"monthlySpendingLimitAmount","docs":["The monthly spending limit the DAO allocates to the team. Must be","less than 1/6th of the minimum raise amount (so 6 months of burn)."],"type":"u64"},{"name":"monthlySpendingLimitMembers","docs":["The wallets that have access to the monthly spending limit."],"type":{"vec":"publicKey"}},{"name":"launchAuthority","docs":["The account that can start the launch."],"type":"publicKey"},{"name":"launchSigner","docs":["The launch signer address. Needed because Raydium pools need a SOL payer and this PDA can't hold SOL."],"type":"publicKey"},{"name":"launchSignerPdaBump","docs":["The PDA bump for the launch signer."],"type":"u8"},{"name":"launchQuoteVault","docs":["The USDC vault that will hold the USDC raised until the launch is over."],"type":"publicKey"},{"name":"launchBaseVault","docs":["The token vault, used to send tokens to Raydium."],"type":"publicKey"},{"name":"baseMint","docs":["The token that will be minted to funders and that will control the DAO."],"type":"publicKey"},{"name":"quoteMint","docs":["The USDC mint."],"type":"publicKey"},{"name":"unixTimestampStarted","docs":["The unix timestamp when the launch was started."],"type":{"option":"i64"}},{"name":"unixTimestampClosed","docs":["The unix timestamp when the launch stopped taking new contributions."],"type":{"option":"i64"}},{"name":"totalCommittedAmount","docs":["The amount of USDC that has been committed by the users."],"type":"u64"},{"name":"state","docs":["The state of the launch."],"type":{"defined":"LaunchState"}},{"name":"seqNum","docs":["The sequence number of this launch. Useful for sorting events."],"type":"u64"},{"name":"secondsForLaunch","docs":["The number of seconds that the launch will be live for."],"type":"u32"},{"name":"dao","docs":["The DAO, if the launch is complete."],"type":{"option":"publicKey"}},{"name":"daoVault","docs":["The DAO treasury that USDC / LP is sent to, if the launch is complete."],"type":{"option":"publicKey"}},{"name":"performancePackageGrantee","docs":["The address that will receive the performance package tokens."],"type":"publicKey"},{"name":"performancePackageTokenAmount","docs":["The amount of tokens to be granted to the performance package grantee."],"type":"u64"},{"name":"monthsUntilInsidersCanUnlock","docs":["The number of months that insiders must wait before unlocking their tokens."],"type":"u8"},{"name":"teamAddress","docs":["The initial address used to sponsor team proposals."],"type":"publicKey"},{"name":"totalApprovedAmount","docs":["The amount of USDC that the launch authority has approved across all funders."],"type":"u64"},{"name":"additionalTokensAmount","docs":["The amount of additional tokens to be minted on a successful launch."],"type":"u64"},{"name":"additionalTokensRecipient","docs":["The token account that will receive the additional tokens."],"type":{"option":"publicKey"}},{"name":"additionalTokensClaimed","docs":["Are the additional tokens claimed"],"type":"bool"},{"name":"unixTimestampCompleted","docs":["The unix timestamp when the launch was completed."],"type":{"option":"i64"}},{"name":"isPerformancePackageInitialized","type":"bool"},{"name":"accumulatorActivationDelaySeconds","docs":["Number of seconds after launch start before the funding accumulator","begins tracking."],"type":"u32"}]}}],"types":[{"name":"CommonFields","type":{"kind":"struct","fields":[{"name":"slot","type":"u64"},{"name":"unixTimestamp","type":"i64"},{"name":"launchSeqNum","type":"u64"}]}},{"name":"ExtendLaunchArgs","type":{"kind":"struct","fields":[{"name":"durationSeconds","type":"u32"}]}},{"name":"InitializeLaunchArgs","type":{"kind":"struct","fields":[{"name":"minimumRaiseAmount","type":"u64"},{"name":"monthlySpendingLimitAmount","type":"u64"},{"name":"monthlySpendingLimitMembers","type":{"vec":"publicKey"}},{"name":"secondsForLaunch","type":"u32"},{"name":"tokenName","type":"string"},{"name":"tokenSymbol","type":"string"},{"name":"tokenUri","type":"string"},{"name":"performancePackageGrantee","type":"publicKey"},{"name":"performancePackageTokenAmount","type":"u64"},{"name":"monthsUntilInsidersCanUnlock","type":"u8"},{"name":"teamAddress","type":"publicKey"},{"name":"additionalTokensAmount","type":"u64"},{"name":"accumulatorActivationDelaySeconds","type":"u32"},{"name":"hasBidWall","type":"bool"}]}},{"name":"LaunchState","type":{"kind":"enum","variants":[{"name":"Initialized"},{"name":"Live"},{"name":"Closed"},{"name":"Complete"},{"name":"Refunding"}]}}],"events":[{"name":"LaunchInitializedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"minimumRaiseAmount","type":"u64","index":false},{"name":"launchAuthority","type":"publicKey","index":false},{"name":"launchSigner","type":"publicKey","index":false},{"name":"launchSignerPdaBump","type":"u8","index":false},{"name":"launchUsdcVault","type":"publicKey","index":false},{"name":"launchTokenVault","type":"publicKey","index":false},{"name":"performancePackageGrantee","type":"publicKey","index":false},{"name":"performancePackageTokenAmount","type":"u64","index":false},{"name":"monthsUntilInsidersCanUnlock","type":"u8","index":false},{"name":"monthlySpendingLimitAmount","type":"u64","index":false},{"name":"monthlySpendingLimitMembers","type":{"vec":"publicKey"},"index":false},{"name":"baseMint","type":"publicKey","index":false},{"name":"quoteMint","type":"publicKey","index":false},{"name":"pdaBump","type":"u8","index":false},{"name":"secondsForLaunch","type":"u32","index":false},{"name":"additionalTokensAmount","type":"u64","index":false},{"name":"additionalTokensRecipient","type":{"option":"publicKey"},"index":false},{"name":"accumulatorActivationDelaySeconds","type":"u32","index":false},{"name":"hasBidWall","type":"bool","index":false}]},{"name":"LaunchStartedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"launchAuthority","type":"publicKey","index":false},{"name":"slotStarted","type":"u64","index":false}]},{"name":"LaunchFundedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"fundingRecord","type":"publicKey","index":false},{"name":"launch","type":"publicKey","index":false},{"name":"funder","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"totalCommittedByFunder","type":"u64","index":false},{"name":"totalCommitted","type":"u64","index":false},{"name":"committedAmountAccumulator","type":"u128","index":false}]},{"name":"FundingRecordApprovalSetEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"fundingRecord","type":"publicKey","index":false},{"name":"funder","type":"publicKey","index":false},{"name":"approvedAmount","type":"u64","index":false},{"name":"totalApproved","type":"u64","index":false}]},{"name":"LaunchCompletedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"finalState","type":{"defined":"LaunchState"},"index":false},{"name":"totalCommitted","type":"u64","index":false},{"name":"dao","type":{"option":"publicKey"},"index":false},{"name":"daoTreasury","type":{"option":"publicKey"},"index":false},{"name":"totalApprovedAmount","type":"u64","index":false},{"name":"bidWall","type":{"option":"publicKey"},"index":false},{"name":"bidWallAmount","type":"u64","index":false}]},{"name":"LaunchRefundedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"funder","type":"publicKey","index":false},{"name":"usdcRefunded","type":"u64","index":false},{"name":"fundingRecord","type":"publicKey","index":false}]},{"name":"LaunchClaimEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"funder","type":"publicKey","index":false},{"name":"tokensClaimed","type":"u64","index":false},{"name":"fundingRecord","type":"publicKey","index":false}]},{"name":"LaunchCloseEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"newState","type":{"defined":"LaunchState"},"index":false}]},{"name":"LaunchClaimAdditionalTokenAllocationEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"additionalTokensAmount","type":"u64","index":false},{"name":"additionalTokensRecipient","type":"publicKey","index":false}]},{"name":"LaunchPerformancePackageInitializedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"performancePackage","type":"publicKey","index":false}]},{"name":"LaunchExtendedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"oldSecondsForLaunch","type":"u32","index":false},{"name":"newSecondsForLaunch","type":"u32","index":false}]}],"errors":[{"code":6000,"name":"InvalidAmount","msg":"Invalid amount"},{"code":6001,"name":"SupplyNonZero","msg":"Supply must be zero"},{"code":6002,"name":"InvalidSecondsForLaunch","msg":"Launch period must be between 1 hour and 2 weeks"},{"code":6003,"name":"InsufficientFunds","msg":"Insufficient funds"},{"code":6004,"name":"InvalidTokenKey","msg":"Token mint key must end in 'meta'"},{"code":6005,"name":"InvalidLaunchState","msg":"Invalid launch state"},{"code":6006,"name":"LaunchPeriodNotOver","msg":"Launch period not over"},{"code":6007,"name":"LaunchExpired","msg":"Launch is complete, no more funding allowed"},{"code":6008,"name":"LaunchNotRefunding","msg":"For you to get a refund, either the launch needs to be in a refunding state or the launch must have been over-committed"},{"code":6009,"name":"LaunchNotInitialized","msg":"Launch must be initialized to be started"},{"code":6010,"name":"FreezeAuthoritySet","msg":"Freeze authority can't be set on launchpad tokens"},{"code":6011,"name":"InvalidMonthlySpendingLimit","msg":"Monthly spending limit must be less than 1/6th of the minimum raise amount and cannot be 0"},{"code":6012,"name":"InvalidMonthlySpendingLimitMembers","msg":"There can only be at most 10 monthly spending limit members"},{"code":6013,"name":"InvalidPriceBasedPremineAmount","msg":"Cannot do more than a 50% premine, minimum is 10 atoms of token"},{"code":6014,"name":"InvalidPerformancePackageMinUnlockTime","msg":"Insiders must be forced to wait at least 18 months before unlocking their tokens"},{"code":6015,"name":"LaunchAuthorityNotSet","msg":"Launch authority must be set to complete the launch until 2 days after closing"},{"code":6016,"name":"FinalRaiseAmountTooLow","msg":"The final amount raised must be greater than or equal to the minimum raise amount"},{"code":6017,"name":"TokensAlreadyClaimed","msg":"Tokens already claimed"},{"code":6018,"name":"MoneyAlreadyRefunded","msg":"Money already refunded"},{"code":6019,"name":"InvariantViolated","msg":"An invariant was violated. You should get in contact with the MetaDAO team if you see this"},{"code":6020,"name":"LaunchNotLive","msg":"Launch must be live to be closed"},{"code":6021,"name":"InvalidMinimumRaiseAmount","msg":"Minimum raise amount must be greater than or equal to $0.5 so that there's enough liquidity for the launch"},{"code":6022,"name":"FinalRaiseAmountAlreadySet","msg":"The final raise amount has already been set"},{"code":6023,"name":"TotalApprovedAmountTooLow","msg":"Total approved amount must be greater than or equal to the minimum raise amount"},{"code":6024,"name":"InvalidAdditionalTokensRecipient","msg":"Invalid additional tokens recipient - should be set if additional tokens amount is greater than 0"},{"code":6025,"name":"NoAdditionalTokensRecipientSet","msg":"No additional tokens recipient set"},{"code":6026,"name":"AdditionalTokensAlreadyClaimed","msg":"Additional tokens already claimed"},{"code":6027,"name":"FundingRecordApprovalPeriodOver","msg":"Funding record approval period is over"},{"code":6028,"name":"PerformancePackageAlreadyInitialized","msg":"Performance package already initialized"},{"code":6029,"name":"InvalidDao","msg":"Invalid DAO"},{"code":6030,"name":"InvalidAccumulatorActivationDelaySeconds","msg":"Accumulator activation delay must be less than the launch duration"},{"code":6031,"name":"ExtendDurationExceedsMax","msg":"The extend duration would exceed the maximum allowed launch duration"}]} \ No newline at end of file diff --git a/idls/metdao_bid_wall.WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx.json b/idls/metdao_bid_wall.WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx.json new file mode 100644 index 0000000..45aa0a6 --- /dev/null +++ b/idls/metdao_bid_wall.WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx.json @@ -0,0 +1 @@ +{"version":"0.7.0","name":"bid_wall","instructions":[{"name":"initializeBidWall","accounts":[{"name":"bidWall","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"feeRecipient","isMut":false,"isSigner":false},{"name":"creator","isMut":false,"isSigner":true},{"name":"authority","isMut":false,"isSigner":false},{"name":"bidWallQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"creatorQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"daoTreasury","isMut":false,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"InitializeBidWallArgs"}}]},{"name":"closeBidWall","accounts":[{"name":"bidWall","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":false},{"name":"feeRecipient","isMut":false,"isSigner":false},{"name":"bidWallQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"authorityQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"feeRecipientQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"sellTokens","accounts":[{"name":"bidWall","isMut":true,"isSigner":false},{"name":"user","isMut":false,"isSigner":true},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"userQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"bidWallQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"daoTreasury","isMut":false,"isSigner":false},{"name":"daoTreasuryQuoteTokenAccount","isMut":false,"isSigner":false},{"name":"baseMint","isMut":true,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"SellTokensArgs"}}]},{"name":"collectFees","accounts":[{"name":"bidWall","isMut":true,"isSigner":false},{"name":"cranker","isMut":false,"isSigner":true},{"name":"bidWallQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"feeRecipientQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"cancelBidWall","accounts":[{"name":"bidWall","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"authority","isMut":false,"isSigner":true},{"name":"feeRecipient","isMut":false,"isSigner":false},{"name":"bidWallQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"authorityQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"feeRecipientQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"BidWall","type":{"kind":"struct","fields":[{"name":"nonce","docs":["The nonce of the bid wall."],"type":"u64"},{"name":"createdTimestamp","docs":["When the bid wall was created."],"type":"i64"},{"name":"initialAmmQuoteReserves","docs":["The initial quote (USDC) reserves of the Futarchy AMM."],"type":"u64"},{"name":"quoteAmount","docs":["The current amount of quote tokens assigned to the bid wall.","This is different from the amount in the bid wall quote token account,","because anyone can transfer quote tokens to the bid wall, and we don't want that to affect the bid wall's NAV calculation."],"type":"u64"},{"name":"feesCollected","docs":["The fees collected by the bid wall."],"type":"u64"},{"name":"baseBoughtAmount","docs":["The amount of base tokens bought up by the bid wall."],"type":"u64"},{"name":"seqNum","docs":["The event sequence number of the bid wall."],"type":"u64"},{"name":"creator","docs":["The authority of the bid wall."],"type":"publicKey"},{"name":"authority","docs":["The authority of the bid wall."],"type":"publicKey"},{"name":"daoTreasury","docs":["The DAO treasury address."],"type":"publicKey"},{"name":"baseMint","docs":["The mint of the token being sold into the bid wall."],"type":"publicKey"},{"name":"feeRecipient","docs":["The recipient of the fees collected by the bid wall."],"type":"publicKey"},{"name":"durationSeconds","docs":["The minimum duration in seconds before the bid wall can be closed."],"type":"u32"},{"name":"pdaBump","docs":["The PDA bump."],"type":"u8"}]}}],"types":[{"name":"CommonFields","type":{"kind":"struct","fields":[{"name":"slot","type":"u64"},{"name":"unixTimestamp","type":"i64"},{"name":"bidWallSeqNum","type":"u64"}]}},{"name":"InitializeBidWallArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"nonce","type":"u64"},{"name":"initialAmmQuoteReserves","type":"u64"},{"name":"durationSeconds","type":"u32"}]}},{"name":"SellTokensArgs","type":{"kind":"struct","fields":[{"name":"amountIn","type":"u64"},{"name":"minAmountOut","type":"u64"}]}}],"events":[{"name":"BidWallInitializedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"bidWall","type":"publicKey","index":false},{"name":"nonce","type":"u64","index":false},{"name":"initialAmmQuoteReserves","type":"u64","index":false},{"name":"amount","type":"u64","index":false},{"name":"creator","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"daoTreasury","type":"publicKey","index":false},{"name":"baseMint","type":"publicKey","index":false},{"name":"feeRecipient","type":"publicKey","index":false},{"name":"durationSeconds","type":"u32","index":false},{"name":"pdaBump","type":"u8","index":false}]},{"name":"BidWallTokensSoldEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"bidWall","type":"publicKey","index":false},{"name":"amountIn","type":"u64","index":false},{"name":"amountOut","type":"u64","index":false},{"name":"fee","type":"u64","index":false},{"name":"postBidWallQuoteTokenAccountAmount","type":"u64","index":false},{"name":"postBidWallBaseBoughtAmount","type":"u64","index":false},{"name":"user","type":"publicKey","index":false}]},{"name":"BidWallFeesCollectedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"bidWall","type":"publicKey","index":false},{"name":"feesCollected","type":"u64","index":false},{"name":"postBidWallQuoteTokenAccountAmount","type":"u64","index":false}]},{"name":"BidWallClosedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"bidWall","type":"publicKey","index":false}]},{"name":"BidWallCanceledEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"bidWall","type":"publicKey","index":false}]}],"errors":[{"code":6000,"name":"BidWallExpired","msg":"Bid wall expired"},{"code":6001,"name":"BidWallNotExpired","msg":"Bid wall not expired"},{"code":6002,"name":"FeeRecipientMismatch","msg":"Fee recipient mismatch"},{"code":6003,"name":"InsufficientQuoteReserves","msg":"Insufficient quote reserves"},{"code":6004,"name":"BidWallDepleted","msg":"Bid wall depleted"},{"code":6005,"name":"InvalidInputAmount","msg":"Invalid input amount"},{"code":6006,"name":"InvalidCrankAddress","msg":"Invalid crank address"},{"code":6007,"name":"InsufficientOutputAmount","msg":"Insufficient output amount"}]} \ No newline at end of file diff --git a/idls/meteora_damm_v2.cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG.json b/idls/meteora_damm_v2.cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG.json new file mode 100644 index 0000000..cef0305 --- /dev/null +++ b/idls/meteora_damm_v2.cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG.json @@ -0,0 +1 @@ +{"address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG","metadata":{"name":"cp_amm","version":"0.2.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"add_liquidity","discriminator":[181,157,89,67,143,182,52,72],"accounts":[{"name":"pool","writable":true,"relations":["position"]},{"name":"position","writable":true},{"name":"token_a_account","docs":["The user token a account"],"writable":true},{"name":"token_b_account","docs":["The user token b account"],"writable":true},{"name":"token_a_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"token_b_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"token_a_mint","docs":["The mint of token a"],"relations":["pool"]},{"name":"token_b_mint","docs":["The mint of token b"],"relations":["pool"]},{"name":"position_nft_account","docs":["The token account for nft"]},{"name":"owner","docs":["owner of position"],"signer":true},{"name":"token_a_program","docs":["Token a program"]},{"name":"token_b_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"AddLiquidityParameters"}}}]},{"name":"claim_position_fee","discriminator":[180,38,154,17,133,33,162,211],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","relations":["position"]},{"name":"position","writable":true},{"name":"token_a_account","docs":["The user token a account"],"writable":true},{"name":"token_b_account","docs":["The user token b account"],"writable":true},{"name":"token_a_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"token_b_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"token_a_mint","docs":["The mint of token a"],"relations":["pool"]},{"name":"token_b_mint","docs":["The mint of token b"],"relations":["pool"]},{"name":"position_nft_account","docs":["The token account for nft"]},{"name":"owner","docs":["owner of position"],"signer":true},{"name":"token_a_program","docs":["Token a program"]},{"name":"token_b_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"claim_protocol_fee","discriminator":[165,228,133,48,99,249,255,33],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","writable":true},{"name":"token_a_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"token_b_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"token_a_mint","docs":["The mint of token a"],"relations":["pool"]},{"name":"token_b_mint","docs":["The mint of token b"],"relations":["pool"]},{"name":"token_a_account","writable":true},{"name":"token_b_account","writable":true},{"name":"operator","docs":["Claim fee operator"]},{"name":"signer","docs":["Operator"],"signer":true},{"name":"token_a_program","docs":["Token a program"]},{"name":"token_b_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"max_amount_a","type":"u64"},{"name":"max_amount_b","type":"u64"}]},{"name":"claim_reward","discriminator":[149,95,181,242,94,90,158,162],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","writable":true,"relations":["position"]},{"name":"position","writable":true},{"name":"reward_vault","docs":["The vault token account for reward token"],"writable":true},{"name":"reward_mint"},{"name":"user_token_account","writable":true},{"name":"position_nft_account","docs":["The token account for nft"]},{"name":"owner","docs":["owner of position"],"signer":true},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u8"},{"name":"skip_reward","type":"u8"}]},{"name":"close_config","discriminator":[145,9,72,157,95,125,61,85],"accounts":[{"name":"config","writable":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"rent_receiver","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"close_operator_account","discriminator":[171,9,213,74,120,23,3,29],"accounts":[{"name":"operator","writable":true},{"name":"signer","signer":true},{"name":"rent_receiver","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"close_position","discriminator":[123,134,81,0,49,68,98,98],"accounts":[{"name":"position_nft_mint","docs":["position_nft_mint"],"writable":true},{"name":"position_nft_account","docs":["The token account for nft"],"writable":true},{"name":"pool","writable":true,"relations":["position"]},{"name":"position","writable":true},{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"rent_receiver","writable":true},{"name":"owner","docs":["Owner of position"],"signer":true},{"name":"token_program","docs":["Program to create NFT mint/token account and transfer for token22 account"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"close_token_badge","discriminator":[108,146,86,110,179,254,10,104],"accounts":[{"name":"token_badge","writable":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"rent_receiver","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"create_config","docs":["OPERATOR FUNCTIONS /////"],"discriminator":[201,207,243,114,75,111,47,189],"accounts":[{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]},{"kind":"arg","path":"index"}]}},{"name":"operator"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"index","type":"u64"},{"name":"config_parameters","type":{"defined":{"name":"StaticConfigParameters"}}}]},{"name":"create_dynamic_config","discriminator":[81,251,122,78,66,57,208,82],"accounts":[{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]},{"kind":"arg","path":"index"}]}},{"name":"operator"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"index","type":"u64"},{"name":"config_parameters","type":{"defined":{"name":"DynamicConfigParameters"}}}]},{"name":"create_operator_account","docs":["ADMIN FUNCTIONS /////"],"discriminator":[221,64,246,149,240,153,229,163],"accounts":[{"name":"operator","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,111,114]},{"kind":"account","path":"whitelisted_address"}]}},{"name":"whitelisted_address"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"permission","type":"u128"}]},{"name":"create_position","discriminator":[48,215,197,153,96,203,180,133],"accounts":[{"name":"owner"},{"name":"position_nft_mint","docs":["position_nft_mint"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["position nft account"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110,95,110,102,116,95,97,99,99,111,117,110,116]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"pool","writable":true},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"payer","docs":["Address paying to create the position. Can be anyone"],"writable":true,"signer":true},{"name":"token_program","docs":["Program to create NFT mint/token account and transfer for token22 account"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"create_token_badge","discriminator":[88,206,0,91,60,175,151,118],"accounts":[{"name":"token_badge","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,98,97,100,103,101]},{"kind":"account","path":"token_mint"}]}},{"name":"token_mint"},{"name":"operator"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"dummy_ix","discriminator":[234,95,176,185,7,42,35,159],"accounts":[{"name":"pod_aligned_fee_time_scheduler"},{"name":"pod_aligned_fee_rate_limiter"},{"name":"pod_aligned_fee_market_cap_scheduler"}],"args":[{"name":"_ixs","type":{"defined":{"name":"DummyParams"}}}]},{"name":"fix_config_fee_params","discriminator":[38,30,216,81,250,177,243,254],"accounts":[{"name":"config","writable":true},{"name":"operator"},{"name":"signer","signer":true}],"args":[{"name":"params","type":{"defined":{"name":"BaseFeeParameters"}}}]},{"name":"fix_pool_fee_params","discriminator":[132,98,81,196,44,58,120,193],"accounts":[{"name":"pool","writable":true},{"name":"operator"},{"name":"signer","signer":true}],"args":[{"name":"params","type":{"defined":{"name":"BaseFeeParameters"}}}]},{"name":"fix_pool_layout_version","discriminator":[166,158,69,35,81,167,200,215],"accounts":[{"name":"pool","writable":true},{"name":"operator"},{"name":"signer","signer":true}],"args":[]},{"name":"fund_reward","discriminator":[188,50,249,165,93,151,38,63],"accounts":[{"name":"pool","writable":true},{"name":"reward_vault","writable":true},{"name":"reward_mint"},{"name":"funder_token_account","writable":true},{"name":"funder","signer":true},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u8"},{"name":"amount","type":"u64"},{"name":"carry_forward","type":"bool"}]},{"name":"initialize_customizable_pool","discriminator":[20,161,241,24,189,221,180,2],"accounts":[{"name":"creator"},{"name":"position_nft_mint","docs":["position_nft_mint"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["position nft account"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110,95,110,102,116,95,97,99,99,111,117,110,116]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"payer","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","docs":["Initialize an account to store the pool state"],"writable":true},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_a_mint","docs":["Token a mint"]},{"name":"token_b_mint","docs":["Token b mint"]},{"name":"token_a_vault","docs":["Token a vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"token_a_mint"},{"kind":"account","path":"pool"}]}},{"name":"token_b_vault","docs":["Token b vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"token_b_mint"},{"kind":"account","path":"pool"}]}},{"name":"payer_token_a","docs":["payer token a account"],"writable":true},{"name":"payer_token_b","docs":["creator token b account"],"writable":true},{"name":"token_a_program","docs":["Program to create mint account and mint tokens"]},{"name":"token_b_program","docs":["Program to create mint account and mint tokens"]},{"name":"token_2022_program","docs":["Program to create NFT mint/token account and transfer for token22 account"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"InitializeCustomizablePoolParameters"}}}]},{"name":"initialize_pool","docs":["USER FUNCTIONS ////"],"discriminator":[95,180,10,172,84,174,232,40],"accounts":[{"name":"creator"},{"name":"position_nft_mint","docs":["position_nft_mint"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["position nft account"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110,95,110,102,116,95,97,99,99,111,117,110,116]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"payer","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"config","docs":["Which config the pool belongs to."]},{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","docs":["Initialize an account to store the pool state"],"writable":true},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_a_mint","docs":["Token a mint"]},{"name":"token_b_mint","docs":["Token b mint"]},{"name":"token_a_vault","docs":["Token a vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"token_a_mint"},{"kind":"account","path":"pool"}]}},{"name":"token_b_vault","docs":["Token b vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"token_b_mint"},{"kind":"account","path":"pool"}]}},{"name":"payer_token_a","docs":["payer token a account"],"writable":true},{"name":"payer_token_b","docs":["creator token b account"],"writable":true},{"name":"token_a_program","docs":["Program to create mint account and mint tokens"]},{"name":"token_b_program","docs":["Program to create mint account and mint tokens"]},{"name":"token_2022_program","docs":["Program to create NFT mint/token account and transfer for token22 account"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"InitializePoolParameters"}}}]},{"name":"initialize_pool_with_dynamic_config","discriminator":[149,82,72,197,253,252,68,15],"accounts":[{"name":"creator"},{"name":"position_nft_mint","docs":["position_nft_mint"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["position nft account"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110,95,110,102,116,95,97,99,99,111,117,110,116]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"payer","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"pool_creator_authority","signer":true,"relations":["config"]},{"name":"config","docs":["Which config the pool belongs to."]},{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","docs":["Initialize an account to store the pool state"],"writable":true},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_a_mint","docs":["Token a mint"]},{"name":"token_b_mint","docs":["Token b mint"]},{"name":"token_a_vault","docs":["Token a vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"token_a_mint"},{"kind":"account","path":"pool"}]}},{"name":"token_b_vault","docs":["Token b vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"token_b_mint"},{"kind":"account","path":"pool"}]}},{"name":"payer_token_a","docs":["payer token a account"],"writable":true},{"name":"payer_token_b","docs":["creator token b account"],"writable":true},{"name":"token_a_program","docs":["Program to create mint account and mint tokens"]},{"name":"token_b_program","docs":["Program to create mint account and mint tokens"]},{"name":"token_2022_program","docs":["Program to create NFT mint/token account and transfer for token22 account"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"InitializeCustomizablePoolParameters"}}}]},{"name":"initialize_reward","discriminator":[95,135,192,196,242,129,230,68],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","writable":true},{"name":"reward_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[114,101,119,97,114,100,95,118,97,117,108,116]},{"kind":"account","path":"pool"},{"kind":"arg","path":"reward_index"}]}},{"name":"reward_mint"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u8"},{"name":"reward_duration","type":"u64"},{"name":"funder","type":"pubkey"}]},{"name":"lock_inner_position","discriminator":[72,19,49,204,18,122,23,90],"accounts":[{"name":"pool","relations":["position"]},{"name":"position","writable":true},{"name":"position_nft_account","docs":["The token account for nft"]},{"name":"owner","docs":["owner of position"],"signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"VestingParameters"}}}]},{"name":"lock_position","discriminator":[227,62,2,252,247,10,171,185],"accounts":[{"name":"pool","relations":["position"]},{"name":"position","writable":true},{"name":"vesting","writable":true,"signer":true},{"name":"position_nft_account","docs":["The token account for nft"]},{"name":"owner","docs":["owner of position"],"signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"VestingParameters"}}}]},{"name":"permanent_lock_position","discriminator":[165,176,125,6,231,171,186,213],"accounts":[{"name":"pool","writable":true,"relations":["position"]},{"name":"position","writable":true},{"name":"position_nft_account","docs":["The token account for nft"]},{"name":"owner","docs":["owner of position"],"signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"permanent_lock_liquidity","type":"u128"}]},{"name":"refresh_vesting","discriminator":[9,94,216,14,116,204,247,0],"accounts":[{"name":"pool","relations":["position"]},{"name":"position","writable":true},{"name":"position_nft_account","docs":["The token account for nft"]},{"name":"owner"}],"args":[]},{"name":"remove_all_liquidity","discriminator":[10,51,61,35,112,105,24,85],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","writable":true,"relations":["position"]},{"name":"position","writable":true},{"name":"token_a_account","docs":["The user token a account"],"writable":true},{"name":"token_b_account","docs":["The user token b account"],"writable":true},{"name":"token_a_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"token_b_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"token_a_mint","docs":["The mint of token a"],"relations":["pool"]},{"name":"token_b_mint","docs":["The mint of token b"],"relations":["pool"]},{"name":"position_nft_account","docs":["The token account for nft"]},{"name":"owner","docs":["owner of position"],"signer":true},{"name":"token_a_program","docs":["Token a program"]},{"name":"token_b_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"token_a_amount_threshold","type":"u64"},{"name":"token_b_amount_threshold","type":"u64"}]},{"name":"remove_liquidity","discriminator":[80,85,209,72,24,206,177,108],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","writable":true,"relations":["position"]},{"name":"position","writable":true},{"name":"token_a_account","docs":["The user token a account"],"writable":true},{"name":"token_b_account","docs":["The user token b account"],"writable":true},{"name":"token_a_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"token_b_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"token_a_mint","docs":["The mint of token a"],"relations":["pool"]},{"name":"token_b_mint","docs":["The mint of token b"],"relations":["pool"]},{"name":"position_nft_account","docs":["The token account for nft"]},{"name":"owner","docs":["owner of position"],"signer":true},{"name":"token_a_program","docs":["Token a program"]},{"name":"token_b_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"RemoveLiquidityParameters"}}}]},{"name":"set_pool_status","discriminator":[112,87,135,223,83,204,132,53],"accounts":[{"name":"pool","writable":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"status","type":"u8"}]},{"name":"split_position","discriminator":[172,241,221,138,161,29,253,42],"accounts":[{"name":"pool","writable":true,"relations":["first_position","second_position"]},{"name":"first_position","docs":["The first position"],"writable":true},{"name":"first_position_nft_account","docs":["The token account for position nft"]},{"name":"second_position","docs":["The second position"],"writable":true},{"name":"second_position_nft_account","docs":["The token account for position nft"]},{"name":"first_owner","docs":["Owner of first position"],"signer":true},{"name":"second_owner","docs":["Owner of second position"],"signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SplitPositionParameters"}}}]},{"name":"split_position2","discriminator":[221,147,228,207,140,212,17,119],"accounts":[{"name":"pool","writable":true,"relations":["first_position","second_position"]},{"name":"first_position","docs":["The first position"],"writable":true},{"name":"first_position_nft_account","docs":["The token account for position nft"]},{"name":"second_position","docs":["The second position"],"writable":true},{"name":"second_position_nft_account","docs":["The token account for position nft"]},{"name":"first_owner","docs":["Owner of first position"],"signer":true},{"name":"second_owner","docs":["Owner of second position"],"signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"numerator","type":"u32"}]},{"name":"swap","discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","docs":["Pool account"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"token_a_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"token_b_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"token_a_mint","docs":["The mint of token a"]},{"name":"token_b_mint","docs":["The mint of token b"]},{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"token_a_program","docs":["Token a program"]},{"name":"token_b_program","docs":["Token b program"]},{"name":"referral_token_account","docs":["referral token account"],"writable":true,"optional":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"_params","type":{"defined":{"name":"SwapParameters"}}}]},{"name":"swap2","discriminator":[65,75,63,76,235,91,91,136],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","docs":["Pool account"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"token_a_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"token_b_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"token_a_mint","docs":["The mint of token a"]},{"name":"token_b_mint","docs":["The mint of token b"]},{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"token_a_program","docs":["Token a program"]},{"name":"token_b_program","docs":["Token b program"]},{"name":"referral_token_account","docs":["referral token account"],"writable":true,"optional":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"_params","type":{"defined":{"name":"SwapParameters2"}}}]},{"name":"update_pool_fees","discriminator":[118,217,203,179,60,8,70,89],"accounts":[{"name":"pool","writable":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"UpdatePoolFeesParameters"}}}]},{"name":"update_reward_duration","discriminator":[138,174,196,169,213,235,254,107],"accounts":[{"name":"pool","writable":true},{"name":"signer","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u8"},{"name":"new_duration","type":"u64"}]},{"name":"update_reward_funder","discriminator":[211,28,48,32,215,160,35,23],"accounts":[{"name":"pool","writable":true},{"name":"signer","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u8"},{"name":"new_funder","type":"pubkey"}]},{"name":"withdraw_ineligible_reward","discriminator":[148,206,42,195,247,49,103,8],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","writable":true},{"name":"reward_vault","writable":true},{"name":"reward_mint"},{"name":"funder_token_account","writable":true},{"name":"funder","signer":true},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"zap_protocol_fee","discriminator":[213,155,187,34,56,182,91,240],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","writable":true},{"name":"token_vault","writable":true},{"name":"token_mint"},{"name":"receiver_token","writable":true},{"name":"operator","docs":["zap claim fee operator"]},{"name":"signer","docs":["Operator"],"signer":true},{"name":"token_program","docs":["Token program"]},{"name":"sysvar_instructions","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[{"name":"max_amount","type":"u64"}]}],"accounts":[{"name":"Config","discriminator":[155,12,170,224,30,250,204,130]},{"name":"Operator","discriminator":[219,31,188,145,69,139,204,117]},{"name":"PodAlignedFeeMarketCapScheduler","discriminator":[251,130,208,253,245,27,145,203]},{"name":"PodAlignedFeeRateLimiter","discriminator":[160,219,8,251,179,7,16,117]},{"name":"PodAlignedFeeTimeScheduler","discriminator":[239,132,138,213,67,154,130,70]},{"name":"Pool","discriminator":[241,154,109,4,17,177,109,188]},{"name":"Position","discriminator":[170,188,143,228,122,64,247,208]},{"name":"TokenBadge","discriminator":[116,219,204,229,249,116,255,150]},{"name":"Vesting","discriminator":[100,149,66,138,95,200,128,241]}],"events":[{"name":"EvtClaimPositionFee","discriminator":[198,182,183,52,97,12,49,56]},{"name":"EvtClaimProtocolFee","discriminator":[186,244,75,251,188,13,25,33]},{"name":"EvtClaimReward","discriminator":[218,86,147,200,235,188,215,231]},{"name":"EvtCloseConfig","discriminator":[36,30,239,45,58,132,14,5]},{"name":"EvtClosePosition","discriminator":[20,145,144,68,143,142,214,178]},{"name":"EvtCreateConfig","discriminator":[131,207,180,174,180,73,165,54]},{"name":"EvtCreateDynamicConfig","discriminator":[231,197,13,164,248,213,133,152]},{"name":"EvtCreatePosition","discriminator":[156,15,119,198,29,181,221,55]},{"name":"EvtCreateTokenBadge","discriminator":[141,120,134,116,34,28,114,160]},{"name":"EvtFundReward","discriminator":[104,233,237,122,199,191,121,85]},{"name":"EvtInitializePool","discriminator":[228,50,246,85,203,66,134,37]},{"name":"EvtInitializeReward","discriminator":[129,91,188,3,246,52,185,249]},{"name":"EvtLiquidityChange","discriminator":[197,171,78,127,224,211,87,13]},{"name":"EvtLockPosition","discriminator":[168,63,108,83,219,82,2,200]},{"name":"EvtPermanentLockPosition","discriminator":[145,143,162,218,218,80,67,11]},{"name":"EvtSetPoolStatus","discriminator":[100,213,74,3,95,91,228,146]},{"name":"EvtSplitPosition2","discriminator":[165,32,203,174,72,100,233,103]},{"name":"EvtSplitPosition3","discriminator":[232,117,190,218,85,162,207,78]},{"name":"EvtSwap2","discriminator":[189,66,51,168,38,80,117,153]},{"name":"EvtUpdatePoolFees","discriminator":[76,165,246,102,102,217,156,44]},{"name":"EvtUpdateRewardDuration","discriminator":[149,135,65,231,129,153,65,57]},{"name":"EvtUpdateRewardFunder","discriminator":[76,154,208,13,40,115,246,146]},{"name":"EvtWithdrawIneligibleReward","discriminator":[248,215,184,78,31,180,179,168]}],"errors":[{"code":6000,"name":"MathOverflow","msg":"Math operation overflow"},{"code":6001,"name":"InvalidFee","msg":"Invalid fee setup"},{"code":6002,"name":"ExceededSlippage","msg":"Exceeded slippage tolerance"},{"code":6003,"name":"PoolDisabled","msg":"Pool disabled"},{"code":6004,"name":"ExceedMaxFeeBps","msg":"Exceeded max fee bps"},{"code":6005,"name":"InvalidAdmin","msg":"Invalid admin"},{"code":6006,"name":"AmountIsZero","msg":"Amount is zero"},{"code":6007,"name":"TypeCastFailed","msg":"Type cast error"},{"code":6008,"name":"UnableToModifyActivationPoint","msg":"Unable to modify activation point"},{"code":6009,"name":"InvalidAuthorityToCreateThePool","msg":"Invalid authority to create the pool"},{"code":6010,"name":"InvalidActivationType","msg":"Invalid activation type"},{"code":6011,"name":"InvalidActivationPoint","msg":"Invalid activation point"},{"code":6012,"name":"InvalidQuoteMint","msg":"Quote token must be SOL,USDC"},{"code":6013,"name":"InvalidFeeCurve","msg":"Invalid fee curve"},{"code":6014,"name":"InvalidPriceRange","msg":"Invalid Price Range"},{"code":6015,"name":"PriceRangeViolation","msg":"Trade is over price range"},{"code":6016,"name":"InvalidParameters","msg":"Invalid parameters"},{"code":6017,"name":"InvalidCollectFeeMode","msg":"Invalid collect fee mode"},{"code":6018,"name":"InvalidInput","msg":"Invalid input"},{"code":6019,"name":"CannotCreateTokenBadgeOnSupportedMint","msg":"Cannot create token badge on supported mint"},{"code":6020,"name":"InvalidTokenBadge","msg":"Invalid token badge"},{"code":6021,"name":"InvalidMinimumLiquidity","msg":"Invalid minimum liquidity"},{"code":6022,"name":"InvalidVestingInfo","msg":"Invalid vesting information"},{"code":6023,"name":"InsufficientLiquidity","msg":"Insufficient liquidity"},{"code":6024,"name":"InvalidVestingAccount","msg":"Invalid vesting account"},{"code":6025,"name":"InvalidPoolStatus","msg":"Invalid pool status"},{"code":6026,"name":"UnsupportNativeMintToken2022","msg":"Unsupported native mint token2022"},{"code":6027,"name":"InvalidRewardIndex","msg":"Invalid reward index"},{"code":6028,"name":"InvalidRewardDuration","msg":"Invalid reward duration"},{"code":6029,"name":"RewardInitialized","msg":"Reward already initialized"},{"code":6030,"name":"RewardUninitialized","msg":"Reward not initialized"},{"code":6031,"name":"InvalidRewardVault","msg":"Invalid reward vault"},{"code":6032,"name":"MustWithdrawnIneligibleReward","msg":"Must withdraw ineligible reward"},{"code":6033,"name":"IdenticalRewardDuration","msg":"Reward duration is the same"},{"code":6034,"name":"RewardCampaignInProgress","msg":"Reward campaign in progress"},{"code":6035,"name":"IdenticalFunder","msg":"Identical funder"},{"code":6036,"name":"InvalidFunder","msg":"Invalid funder"},{"code":6037,"name":"RewardNotEnded","msg":"Reward not ended"},{"code":6038,"name":"FeeInverseIsIncorrect","msg":"Fee inverse is incorrect"},{"code":6039,"name":"PositionIsNotEmpty","msg":"Position is not empty"},{"code":6040,"name":"InvalidPoolCreatorAuthority","msg":"Invalid pool creator authority"},{"code":6041,"name":"InvalidConfigType","msg":"Invalid config type"},{"code":6042,"name":"InvalidPoolCreator","msg":"Invalid pool creator"},{"code":6043,"name":"RewardVaultFrozenSkipRequired","msg":"Reward vault is frozen, must skip reward to proceed"},{"code":6044,"name":"InvalidSplitPositionParameters","msg":"Invalid parameters for split position"},{"code":6045,"name":"UnsupportPositionHasVestingLock","msg":"Unsupported split position has vesting lock"},{"code":6046,"name":"SamePosition","msg":"Same position"},{"code":6047,"name":"InvalidBaseFeeMode","msg":"Invalid base fee mode"},{"code":6048,"name":"InvalidFeeRateLimiter","msg":"Invalid fee rate limiter"},{"code":6049,"name":"FailToValidateSingleSwapInstruction","msg":"Fail to validate single swap instruction in rate limiter"},{"code":6050,"name":"InvalidFeeTimeScheduler","msg":"Invalid fee scheduler"},{"code":6051,"name":"UndeterminedError","msg":"Undetermined error"},{"code":6052,"name":"InvalidPoolVersion","msg":"Invalid pool version"},{"code":6053,"name":"InvalidAuthority","msg":"Invalid authority to do that action"},{"code":6054,"name":"InvalidPermission","msg":"Invalid permission"},{"code":6055,"name":"InvalidFeeMarketCapScheduler","msg":"Invalid fee market cap scheduler"},{"code":6056,"name":"CannotUpdateBaseFee","msg":"Cannot update base fee"},{"code":6057,"name":"InvalidDynamicFeeParameters","msg":"Invalid dynamic fee parameters"},{"code":6058,"name":"InvalidUpdatePoolFeesParameters","msg":"Invalid update pool fees parameters"},{"code":6059,"name":"MissingOperatorAccount","msg":"Missing operator account"},{"code":6060,"name":"IncorrectATA","msg":"Incorrect ATA"},{"code":6061,"name":"InvalidZapOutParameters","msg":"Invalid zap out parameters"},{"code":6062,"name":"InvalidWithdrawProtocolFeeZapAccounts","msg":"Invalid withdraw protocol fee zap accounts"},{"code":6063,"name":"MintRestrictedFromZap","msg":"SOL,USDC protocol fee cannot be withdrawn via zap"},{"code":6064,"name":"CpiDisabled","msg":"CPI disabled"},{"code":6065,"name":"MissingZapOutInstruction","msg":"Missing zap out instruction"},{"code":6066,"name":"InvalidZapAccounts","msg":"Invalid zap accounts"},{"code":6067,"name":"InvalidCompoundingFeeBps","msg":"Invalid compounding fee bps"}],"types":[{"name":"AddLiquidityParameters","type":{"kind":"struct","fields":[{"name":"liquidity_delta","docs":["delta liquidity"],"type":"u128"},{"name":"token_a_amount_threshold","docs":["maximum token a amount"],"type":"u64"},{"name":"token_b_amount_threshold","docs":["maximum token b amount"],"type":"u64"}]}},{"name":"BaseFeeInfo","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"data","type":{"array":["u8",32]}}]}},{"name":"BaseFeeParameters","type":{"kind":"struct","fields":[{"name":"data","type":{"array":["u8",27]}}]}},{"name":"BaseFeeStruct","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"base_fee_info","type":{"defined":{"name":"BaseFeeInfo"}}},{"name":"padding_1","type":"u64"}]}},{"name":"BorshFeeMarketCapScheduler","type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","type":"u64"},{"name":"number_of_period","type":"u16"},{"name":"sqrt_price_step_bps","type":"u32"},{"name":"scheduler_expiration_duration","type":"u32"},{"name":"reduction_factor","type":"u64"},{"name":"base_fee_mode","type":"u8"}]}},{"name":"BorshFeeRateLimiter","docs":["we denote reference_amount = x0, cliff_fee_numerator = c, fee_increment = i","if input_amount <= x0, then fee = input_amount * c","","if input_amount > x0, then input_amount = x0 + (a * x0 + b)","if a < max_index","then fee = x0 * c + x0 * (c + i) + .... + x0 * (c + i*a) + b * (c + i * (a+1))","then fee = x0 * (c + c*a + i*a*(a+1)/2) + b * (c + i * (a+1))","","if a >= max_index","if a = max_index + d, input_amount = x0 + max_index * x0 + (d * x0 + b)","then fee = x0 * (c + c*max_index + i*max_index*(max_index+1)/2) + (d * x0 + b) * MAX_FEE"],"type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","type":"u64"},{"name":"fee_increment_bps","type":"u16"},{"name":"max_limiter_duration","type":"u32"},{"name":"max_fee_bps","type":"u32"},{"name":"reference_amount","type":"u64"},{"name":"base_fee_mode","type":"u8"}]}},{"name":"BorshFeeTimeScheduler","type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","type":"u64"},{"name":"number_of_period","type":"u16"},{"name":"period_frequency","type":"u64"},{"name":"reduction_factor","type":"u64"},{"name":"base_fee_mode","type":"u8"}]}},{"name":"Config","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"vault_config_key","docs":["Vault config key"],"type":"pubkey"},{"name":"pool_creator_authority","docs":["Only pool_creator_authority can use the current config to initialize new pool. When it's Pubkey::default, it's a public config."],"type":"pubkey"},{"name":"pool_fees","docs":["Pool fee"],"type":{"defined":{"name":"PoolFeesConfig"}}},{"name":"activation_type","docs":["Activation type"],"type":"u8"},{"name":"collect_fee_mode","docs":["Collect fee mode"],"type":"u8"},{"name":"config_type","docs":["Config type mode, 0 for static, 1 for dynamic"],"type":"u8"},{"name":"_padding_0","docs":["padding 0"],"type":{"array":["u8",5]}},{"name":"index","docs":["config index"],"type":"u64"},{"name":"sqrt_min_price","docs":["sqrt min price"],"type":"u128"},{"name":"sqrt_max_price","docs":["sqrt max price"],"type":"u128"},{"name":"_padding_1","docs":["Fee curve point","Padding for further use"],"type":{"array":["u64",10]}}]}},{"name":"DummyParams","type":{"kind":"struct","fields":[{"name":"borsh_fee_time_scheduler_params","type":{"defined":{"name":"BorshFeeTimeScheduler"}}},{"name":"borsh_fee_rate_limiter_params","type":{"defined":{"name":"BorshFeeRateLimiter"}}},{"name":"borsh_fee_market_cap_scheduler_params","type":{"defined":{"name":"BorshFeeMarketCapScheduler"}}}]}},{"name":"DynamicConfigParameters","type":{"kind":"struct","fields":[{"name":"pool_creator_authority","type":"pubkey"}]}},{"name":"DynamicFeeConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"initialized","type":"u8"},{"name":"padding","type":{"array":["u8",7]}},{"name":"max_volatility_accumulator","type":"u32"},{"name":"variable_fee_control","type":"u32"},{"name":"bin_step","type":"u16"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"padding_1","type":{"array":["u8",8]}},{"name":"bin_step_u128","type":"u128"}]}},{"name":"DynamicFeeParameters","type":{"kind":"struct","fields":[{"name":"bin_step","type":"u16"},{"name":"bin_step_u128","type":"u128"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"max_volatility_accumulator","type":"u32"},{"name":"variable_fee_control","type":"u32"}]}},{"name":"DynamicFeeStruct","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"initialized","type":"u8"},{"name":"padding","type":{"array":["u8",7]}},{"name":"max_volatility_accumulator","type":"u32"},{"name":"variable_fee_control","type":"u32"},{"name":"bin_step","type":"u16"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"last_update_timestamp","type":"u64"},{"name":"bin_step_u128","type":"u128"},{"name":"sqrt_price_reference","type":"u128"},{"name":"volatility_accumulator","type":"u128"},{"name":"volatility_reference","type":"u128"}]}},{"name":"EvtClaimPositionFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"fee_a_claimed","type":"u64"},{"name":"fee_b_claimed","type":"u64"}]}},{"name":"EvtClaimProtocolFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"token_a_amount","type":"u64"},{"name":"token_b_amount","type":"u64"}]}},{"name":"EvtClaimReward","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"mint_reward","type":"pubkey"},{"name":"reward_index","type":"u8"},{"name":"total_reward","type":"u64"}]}},{"name":"EvtCloseConfig","docs":["Close config"],"type":{"kind":"struct","fields":[{"name":"config","docs":["Config pubkey"],"type":"pubkey"},{"name":"admin","docs":["admin pk"],"type":"pubkey"}]}},{"name":"EvtClosePosition","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"position_nft_mint","type":"pubkey"}]}},{"name":"EvtCreateConfig","docs":["Create static config"],"type":{"kind":"struct","fields":[{"name":"pool_fees","type":{"defined":{"name":"PoolFeeParameters"}}},{"name":"vault_config_key","type":"pubkey"},{"name":"pool_creator_authority","type":"pubkey"},{"name":"activation_type","type":"u8"},{"name":"sqrt_min_price","type":"u128"},{"name":"sqrt_max_price","type":"u128"},{"name":"collect_fee_mode","type":"u8"},{"name":"index","type":"u64"},{"name":"config","type":"pubkey"}]}},{"name":"EvtCreateDynamicConfig","docs":["Create dynamic config"],"type":{"kind":"struct","fields":[{"name":"config","type":"pubkey"},{"name":"pool_creator_authority","type":"pubkey"},{"name":"index","type":"u64"}]}},{"name":"EvtCreatePosition","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"position_nft_mint","type":"pubkey"}]}},{"name":"EvtCreateTokenBadge","docs":["Create token badge"],"type":{"kind":"struct","fields":[{"name":"token_mint","type":"pubkey"}]}},{"name":"EvtFundReward","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"funder","type":"pubkey"},{"name":"mint_reward","type":"pubkey"},{"name":"reward_index","type":"u8"},{"name":"amount","type":"u64"},{"name":"transfer_fee_excluded_amount_in","type":"u64"},{"name":"reward_duration_end","type":"u64"},{"name":"pre_reward_rate","type":"u128"},{"name":"post_reward_rate","type":"u128"}]}},{"name":"EvtInitializePool","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"token_a_mint","type":"pubkey"},{"name":"token_b_mint","type":"pubkey"},{"name":"creator","type":"pubkey"},{"name":"payer","type":"pubkey"},{"name":"alpha_vault","type":"pubkey"},{"name":"pool_fees","type":{"defined":{"name":"PoolFeeParameters"}}},{"name":"sqrt_min_price","type":"u128"},{"name":"sqrt_max_price","type":"u128"},{"name":"activation_type","type":"u8"},{"name":"collect_fee_mode","type":"u8"},{"name":"liquidity","type":"u128"},{"name":"sqrt_price","type":"u128"},{"name":"activation_point","type":"u64"},{"name":"token_a_flag","type":"u8"},{"name":"token_b_flag","type":"u8"},{"name":"token_a_amount","type":"u64"},{"name":"token_b_amount","type":"u64"},{"name":"total_amount_a","type":"u64"},{"name":"total_amount_b","type":"u64"},{"name":"pool_type","type":"u8"}]}},{"name":"EvtInitializeReward","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"reward_mint","type":"pubkey"},{"name":"funder","type":"pubkey"},{"name":"creator","type":"pubkey"},{"name":"reward_index","type":"u8"},{"name":"reward_duration","type":"u64"}]}},{"name":"EvtLiquidityChange","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"token_a_amount","type":"u64"},{"name":"token_b_amount","type":"u64"},{"name":"transfer_fee_included_token_a_amount","type":"u64"},{"name":"transfer_fee_included_token_b_amount","type":"u64"},{"name":"reserve_a_amount","type":"u64"},{"name":"reserve_b_amount","type":"u64"},{"name":"liquidity_delta","type":"u128"},{"name":"token_a_amount_threshold","type":"u64"},{"name":"token_b_amount_threshold","type":"u64"},{"name":"change_type","type":"u8"}]}},{"name":"EvtLockPosition","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"vesting","type":"pubkey"},{"name":"cliff_point","type":"u64"},{"name":"period_frequency","type":"u64"},{"name":"cliff_unlock_liquidity","type":"u128"},{"name":"liquidity_per_period","type":"u128"},{"name":"number_of_period","type":"u16"}]}},{"name":"EvtPermanentLockPosition","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"lock_liquidity_amount","type":"u128"},{"name":"total_permanent_locked_liquidity","type":"u128"}]}},{"name":"EvtSetPoolStatus","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"status","type":"u8"}]}},{"name":"EvtSplitPosition2","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"first_owner","type":"pubkey"},{"name":"second_owner","type":"pubkey"},{"name":"first_position","type":"pubkey"},{"name":"second_position","type":"pubkey"},{"name":"current_sqrt_price","type":"u128"},{"name":"amount_splits","type":{"defined":{"name":"SplitAmountInfo"}}},{"name":"first_position_info","type":{"defined":{"name":"SplitPositionInfo"}}},{"name":"second_position_info","type":{"defined":{"name":"SplitPositionInfo"}}},{"name":"split_position_parameters","type":{"defined":{"name":"SplitPositionParameters2"}}}]}},{"name":"EvtSplitPosition3","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"first_owner","type":"pubkey"},{"name":"second_owner","type":"pubkey"},{"name":"first_position","type":"pubkey"},{"name":"second_position","type":"pubkey"},{"name":"current_sqrt_price","type":"u128"},{"name":"amount_splits","type":{"defined":{"name":"SplitAmountInfo2"}}},{"name":"first_position_info","type":{"defined":{"name":"SplitPositionInfo2"}}},{"name":"second_position_info","type":{"defined":{"name":"SplitPositionInfo2"}}},{"name":"split_position_parameters","type":{"defined":{"name":"SplitPositionParameters3"}}}]}},{"name":"EvtSwap2","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"trade_direction","type":"u8"},{"name":"collect_fee_mode","type":"u8"},{"name":"has_referral","type":"bool"},{"name":"params","type":{"defined":{"name":"SwapParameters2"}}},{"name":"swap_result","type":{"defined":{"name":"SwapResult2"}}},{"name":"included_transfer_fee_amount_in","type":"u64"},{"name":"included_transfer_fee_amount_out","type":"u64"},{"name":"excluded_transfer_fee_amount_out","type":"u64"},{"name":"current_timestamp","type":"u64"},{"name":"reserve_a_amount","type":"u64"},{"name":"reserve_b_amount","type":"u64"}]}},{"name":"EvtUpdatePoolFees","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"operator","type":"pubkey"},{"name":"params","type":{"defined":{"name":"UpdatePoolFeesParameters"}}}]}},{"name":"EvtUpdateRewardDuration","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"reward_index","type":"u8"},{"name":"old_reward_duration","type":"u64"},{"name":"new_reward_duration","type":"u64"}]}},{"name":"EvtUpdateRewardFunder","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"reward_index","type":"u8"},{"name":"old_funder","type":"pubkey"},{"name":"new_funder","type":"pubkey"}]}},{"name":"EvtWithdrawIneligibleReward","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"reward_mint","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"InitializeCustomizablePoolParameters","type":{"kind":"struct","fields":[{"name":"pool_fees","docs":["pool fees"],"type":{"defined":{"name":"PoolFeeParameters"}}},{"name":"sqrt_min_price","docs":["sqrt min price"],"type":"u128"},{"name":"sqrt_max_price","docs":["sqrt max price"],"type":"u128"},{"name":"has_alpha_vault","docs":["has alpha vault"],"type":"bool"},{"name":"liquidity","docs":["initialize liquidity"],"type":"u128"},{"name":"sqrt_price","docs":["The init price of the pool as a sqrt(token_b/token_a) Q64.64 value. Market cap fee scheduler minimum price will be derived from this value"],"type":"u128"},{"name":"activation_type","docs":["activation type"],"type":"u8"},{"name":"collect_fee_mode","docs":["collect fee mode"],"type":"u8"},{"name":"activation_point","docs":["activation point"],"type":{"option":"u64"}}]}},{"name":"InitializePoolParameters","type":{"kind":"struct","fields":[{"name":"liquidity","docs":["initialize liquidity"],"type":"u128"},{"name":"sqrt_price","docs":["The init price of the pool as a sqrt(token_b/token_a) Q64.64 value"],"type":"u128"},{"name":"activation_point","docs":["activation point"],"type":{"option":"u64"}}]}},{"name":"InnerVesting","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"cliff_point","type":"u64"},{"name":"period_frequency","type":"u64"},{"name":"cliff_unlock_liquidity","type":"u128"},{"name":"liquidity_per_period","type":"u128"},{"name":"total_released_liquidity","type":"u128"},{"name":"number_of_period","type":"u16"},{"name":"padding","type":{"array":["u8",14]}}]}},{"name":"Operator","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"whitelisted_address","type":"pubkey"},{"name":"permission","type":"u128"},{"name":"padding","type":{"array":["u64",2]}}]}},{"name":"PodAlignedFeeMarketCapScheduler","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","type":"u64"},{"name":"base_fee_mode","type":"u8"},{"name":"padding","type":{"array":["u8",5]}},{"name":"number_of_period","type":"u16"},{"name":"sqrt_price_step_bps","type":"u32"},{"name":"scheduler_expiration_duration","type":"u32"},{"name":"reduction_factor","type":"u64"}]}},{"name":"PodAlignedFeeRateLimiter","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","type":"u64"},{"name":"base_fee_mode","type":"u8"},{"name":"padding","type":{"array":["u8",5]}},{"name":"fee_increment_bps","type":"u16"},{"name":"max_limiter_duration","type":"u32"},{"name":"max_fee_bps","type":"u32"},{"name":"reference_amount","type":"u64"}]}},{"name":"PodAlignedFeeTimeScheduler","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","type":"u64"},{"name":"base_fee_mode","type":"u8"},{"name":"padding","type":{"array":["u8",5]}},{"name":"number_of_period","type":"u16"},{"name":"period_frequency","type":"u64"},{"name":"reduction_factor","type":"u64"}]}},{"name":"Pool","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"pool_fees","docs":["Pool fee"],"type":{"defined":{"name":"PoolFeesStruct"}}},{"name":"token_a_mint","docs":["token a mint"],"type":"pubkey"},{"name":"token_b_mint","docs":["token b mint"],"type":"pubkey"},{"name":"token_a_vault","docs":["token a vault"],"type":"pubkey"},{"name":"token_b_vault","docs":["token b vault"],"type":"pubkey"},{"name":"whitelisted_vault","docs":["Whitelisted vault to be able to buy pool before activation_point"],"type":"pubkey"},{"name":"padding_0","docs":["padding, previously partner pubkey, be careful when using this field"],"type":{"array":["u8",32]}},{"name":"liquidity","docs":["liquidity share"],"type":"u128"},{"name":"padding_1","docs":["padding, previous reserve amount, be careful to use that field"],"type":"u128"},{"name":"protocol_a_fee","docs":["protocol a fee"],"type":"u64"},{"name":"protocol_b_fee","docs":["protocol b fee"],"type":"u64"},{"name":"padding_2","type":"u128"},{"name":"sqrt_min_price","docs":["min price"],"type":"u128"},{"name":"sqrt_max_price","docs":["max price"],"type":"u128"},{"name":"sqrt_price","docs":["current price"],"type":"u128"},{"name":"activation_point","docs":["Activation point, can be slot or timestamp"],"type":"u64"},{"name":"activation_type","docs":["Activation type, 0 means by slot, 1 means by timestamp"],"type":"u8"},{"name":"pool_status","docs":["pool status, 0: enable, 1 disable"],"type":"u8"},{"name":"token_a_flag","docs":["token a flag"],"type":"u8"},{"name":"token_b_flag","docs":["token b flag"],"type":"u8"},{"name":"collect_fee_mode","docs":["0 is collect fee in both token, 1 only collect fee only in token b"],"type":"u8"},{"name":"pool_type","docs":["pool type"],"type":"u8"},{"name":"fee_version","docs":["pool fee version, 0: max_fee is still capped at 50%, 1: max_fee is capped at 99%"],"type":"u8"},{"name":"padding_3","docs":["padding"],"type":"u8"},{"name":"fee_a_per_liquidity","docs":["cumulative"],"type":{"array":["u8",32]}},{"name":"fee_b_per_liquidity","docs":["cumulative"],"type":{"array":["u8",32]}},{"name":"permanent_lock_liquidity","type":"u128"},{"name":"metrics","docs":["metrics"],"type":{"defined":{"name":"PoolMetrics"}}},{"name":"creator","docs":["pool creator"],"type":"pubkey"},{"name":"token_a_amount","docs":["token a amount"],"type":"u64"},{"name":"token_b_amount","docs":["token b amount"],"type":"u64"},{"name":"layout_version","docs":["layout version: version 0: haven't track token_a_amount and token_b_amount, version 1: track token_a_amount and token_b_amount"],"type":"u8"},{"name":"padding_4","docs":["Padding for further use"],"type":{"array":["u8",7]}},{"name":"padding_5","docs":["Padding for further use"],"type":{"array":["u64",3]}},{"name":"reward_infos","docs":["Farming reward information"],"type":{"array":[{"defined":{"name":"RewardInfo"}},2]}}]}},{"name":"PoolFeeParameters","docs":["Information regarding fee charges"],"type":{"kind":"struct","fields":[{"name":"base_fee","docs":["Base fee"],"type":{"defined":{"name":"BaseFeeParameters"}}},{"name":"compounding_fee_bps","docs":["compounding fee bps, only have value if CollectFeeMode::Compounding"],"type":"u16"},{"name":"padding","docs":["padding for future use"],"type":"u8"},{"name":"dynamic_fee","docs":["dynamic fee"],"type":{"option":{"defined":{"name":"DynamicFeeParameters"}}}}]}},{"name":"PoolFeesConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"base_fee","type":{"defined":{"name":"BaseFeeInfo"}}},{"name":"dynamic_fee","type":{"defined":{"name":"DynamicFeeConfig"}}},{"name":"protocol_fee_percent","type":"u8"},{"name":"padding_0","type":"u8"},{"name":"referral_fee_percent","type":"u8"},{"name":"padding_1","type":{"array":["u8",3]}},{"name":"compounding_fee_bps","docs":["Compounding fee bps, only non-zero if collect_fee_mode is compounding"],"type":"u16"},{"name":"padding_2","type":{"array":["u64",5]}}]}},{"name":"PoolFeesStruct","docs":["Information regarding fee charges","trading_fee = amount * trade_fee_numerator / denominator","protocol_fee = trading_fee * protocol_fee_percentage / 100","referral_fee = protocol_fee * referral_percentage / 100"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"base_fee","docs":["Trade fees are extra token amounts that are held inside the token","accounts during a trade, making the value of liquidity tokens rise.","Trade fee numerator"],"type":{"defined":{"name":"BaseFeeStruct"}}},{"name":"protocol_fee_percent","docs":["Protocol trading fees are extra token amounts that are held inside the token","accounts during a trade, with the equivalent in pool tokens minted to","the protocol of the program.","Protocol trade fee numerator"],"type":"u8"},{"name":"padding_0","docs":["padding for future use"],"type":"u8"},{"name":"referral_fee_percent","docs":["referral fee"],"type":"u8"},{"name":"padding_1","docs":["padding"],"type":{"array":["u8",3]}},{"name":"compounding_fee_bps","docs":["compounding fee bps, only non-zero in CollectFeeMode::Compounding"],"type":"u16"},{"name":"dynamic_fee","docs":["dynamic fee"],"type":{"defined":{"name":"DynamicFeeStruct"}}},{"name":"init_sqrt_price","type":"u128"}]}},{"name":"PoolMetrics","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"total_lp_a_fee","type":"u128"},{"name":"total_lp_b_fee","type":"u128"},{"name":"total_protocol_a_fee","type":"u64"},{"name":"total_protocol_b_fee","type":"u64"},{"name":"padding_0","type":{"array":["u64",2]}},{"name":"total_position","type":"u64"},{"name":"padding","type":"u64"}]}},{"name":"Position","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"nft_mint","docs":["nft mint"],"type":"pubkey"},{"name":"fee_a_per_token_checkpoint","docs":["fee a checkpoint"],"type":{"array":["u8",32]}},{"name":"fee_b_per_token_checkpoint","docs":["fee b checkpoint"],"type":{"array":["u8",32]}},{"name":"fee_a_pending","docs":["fee a pending"],"type":"u64"},{"name":"fee_b_pending","docs":["fee b pending"],"type":"u64"},{"name":"unlocked_liquidity","docs":["unlock liquidity"],"type":"u128"},{"name":"vested_liquidity","docs":["vesting liquidity"],"type":"u128"},{"name":"permanent_locked_liquidity","docs":["permanent locked liquidity"],"type":"u128"},{"name":"metrics","docs":["metrics"],"type":{"defined":{"name":"PositionMetrics"}}},{"name":"reward_infos","docs":["Farming reward information"],"type":{"array":[{"defined":{"name":"UserRewardInfo"}},2]}},{"name":"inner_vesting","docs":["inner vesting info"],"type":{"defined":{"name":"InnerVesting"}}},{"name":"padding","docs":["padding for future usage"],"type":"u128"}]}},{"name":"PositionMetrics","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"total_claimed_a_fee","type":"u64"},{"name":"total_claimed_b_fee","type":"u64"}]}},{"name":"RemoveLiquidityParameters","type":{"kind":"struct","fields":[{"name":"liquidity_delta","docs":["delta liquidity"],"type":"u128"},{"name":"token_a_amount_threshold","docs":["minimum token a amount"],"type":"u64"},{"name":"token_b_amount_threshold","docs":["minimum token b amount"],"type":"u64"}]}},{"name":"RewardInfo","docs":["Stores the state relevant for tracking liquidity mining rewards"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"initialized","docs":["Indicates if the reward has been initialized"],"type":"u8"},{"name":"reward_token_flag","docs":["reward token flag"],"type":"u8"},{"name":"_padding_0","docs":["padding"],"type":{"array":["u8",6]}},{"name":"_padding_1","docs":["Padding to ensure `reward_rate: u128` is 16-byte aligned"],"type":{"array":["u8",8]}},{"name":"mint","docs":["Reward token mint."],"type":"pubkey"},{"name":"vault","docs":["Reward vault token account."],"type":"pubkey"},{"name":"funder","docs":["Authority account that allows to fund rewards"],"type":"pubkey"},{"name":"reward_duration","docs":["reward duration"],"type":"u64"},{"name":"reward_duration_end","docs":["reward duration end"],"type":"u64"},{"name":"reward_rate","docs":["reward rate"],"type":"u128"},{"name":"reward_per_token_stored","docs":["Reward per token stored"],"type":{"array":["u8",32]}},{"name":"last_update_time","docs":["The last time reward states were updated."],"type":"u64"},{"name":"cumulative_seconds_with_empty_liquidity_reward","docs":["Accumulated seconds when the farm distributed rewards but the bin was empty.","These rewards will be carried over to the next reward time window."],"type":"u64"}]}},{"name":"SplitAmountInfo","type":{"kind":"struct","fields":[{"name":"permanent_locked_liquidity","type":"u128"},{"name":"unlocked_liquidity","type":"u128"},{"name":"fee_a","type":"u64"},{"name":"fee_b","type":"u64"},{"name":"reward_0","type":"u64"},{"name":"reward_1","type":"u64"}]}},{"name":"SplitAmountInfo2","type":{"kind":"struct","fields":[{"name":"permanent_locked_liquidity","type":"u128"},{"name":"unlocked_liquidity","type":"u128"},{"name":"vested_liquidity","type":"u128"},{"name":"fee_a","type":"u64"},{"name":"fee_b","type":"u64"},{"name":"reward_0","type":"u64"},{"name":"reward_1","type":"u64"}]}},{"name":"SplitPositionInfo","type":{"kind":"struct","fields":[{"name":"liquidity","type":"u128"},{"name":"fee_a","type":"u64"},{"name":"fee_b","type":"u64"},{"name":"reward_0","type":"u64"},{"name":"reward_1","type":"u64"}]}},{"name":"SplitPositionInfo2","type":{"kind":"struct","fields":[{"name":"unlocked_liquidity","type":"u128"},{"name":"permanent_locked_liquidity","type":"u128"},{"name":"vested_liquidity","type":"u128"},{"name":"fee_a","type":"u64"},{"name":"fee_b","type":"u64"},{"name":"reward_0","type":"u64"},{"name":"reward_1","type":"u64"}]}},{"name":"SplitPositionParameters","type":{"kind":"struct","fields":[{"name":"unlocked_liquidity_percentage","docs":["Percentage of unlocked liquidity to split to the second position"],"type":"u8"},{"name":"permanent_locked_liquidity_percentage","docs":["Percentage of permanent locked liquidity to split to the second position"],"type":"u8"},{"name":"fee_a_percentage","docs":["Percentage of fee A pending to split to the second position"],"type":"u8"},{"name":"fee_b_percentage","docs":["Percentage of fee B pending to split to the second position"],"type":"u8"},{"name":"reward_0_percentage","docs":["Percentage of reward 0 pending to split to the second position"],"type":"u8"},{"name":"reward_1_percentage","docs":["Percentage of reward 1 pending to split to the second position"],"type":"u8"},{"name":"inner_vesting_liquidity_percentage","docs":["Percentage of inner vesting liquidity"],"type":"u8"},{"name":"padding","docs":["padding for future"],"type":{"array":["u8",15]}}]}},{"name":"SplitPositionParameters2","type":{"kind":"struct","fields":[{"name":"unlocked_liquidity_numerator","type":"u32"},{"name":"permanent_locked_liquidity_numerator","type":"u32"},{"name":"fee_a_numerator","type":"u32"},{"name":"fee_b_numerator","type":"u32"},{"name":"reward_0_numerator","type":"u32"},{"name":"reward_1_numerator","type":"u32"}]}},{"name":"SplitPositionParameters3","type":{"kind":"struct","fields":[{"name":"unlocked_liquidity_numerator","type":"u32"},{"name":"permanent_locked_liquidity_numerator","type":"u32"},{"name":"fee_a_numerator","type":"u32"},{"name":"fee_b_numerator","type":"u32"},{"name":"reward_0_numerator","type":"u32"},{"name":"reward_1_numerator","type":"u32"},{"name":"inner_vesting_liquidity_numerator","type":"u32"}]}},{"name":"StaticConfigParameters","type":{"kind":"struct","fields":[{"name":"pool_fees","type":{"defined":{"name":"PoolFeeParameters"}}},{"name":"sqrt_min_price","type":"u128"},{"name":"sqrt_max_price","type":"u128"},{"name":"vault_config_key","type":"pubkey"},{"name":"pool_creator_authority","type":"pubkey"},{"name":"activation_type","type":"u8"},{"name":"collect_fee_mode","type":"u8"}]}},{"name":"SwapParameters","type":{"kind":"struct","fields":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"}]}},{"name":"SwapParameters2","type":{"kind":"struct","fields":[{"name":"amount_0","docs":["When it's exact in, partial fill, this will be amount_in. When it's exact out, this will be amount_out"],"type":"u64"},{"name":"amount_1","docs":["When it's exact in, partial fill, this will be minimum_amount_out. When it's exact out, this will be maximum_amount_in"],"type":"u64"},{"name":"swap_mode","docs":["Swap mode, refer [SwapMode]"],"type":"u8"}]}},{"name":"SwapResult2","type":{"kind":"struct","fields":[{"name":"included_fee_input_amount","type":"u64"},{"name":"excluded_fee_input_amount","type":"u64"},{"name":"amount_left","type":"u64"},{"name":"output_amount","type":"u64"},{"name":"next_sqrt_price","type":"u128"},{"name":"claiming_fee","type":"u64"},{"name":"protocol_fee","type":"u64"},{"name":"compounding_fee","type":"u64"},{"name":"referral_fee","type":"u64"}]}},{"name":"TokenBadge","docs":["Parameter that set by the protocol"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"token_mint","docs":["token mint"],"type":"pubkey"},{"name":"_padding","docs":["Reserve"],"type":{"array":["u8",128]}}]}},{"name":"UpdatePoolFeesParameters","type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","docs":["Base fee update mode:","- None: skip base fee update","- Some: update new cliff_fee_numerator if base fee is static"],"type":{"option":"u64"}},{"name":"dynamic_fee","docs":["Dynamic fee update mode:","- None: skip dynamic fee update","- Some(with default value): disable dynamic fee","- Some(with non default value): enable dynamic fee if disabled or update dynamic fee if enabled"],"type":{"option":{"defined":{"name":"DynamicFeeParameters"}}}}]}},{"name":"UserRewardInfo","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"reward_per_token_checkpoint","docs":["The latest update reward checkpoint"],"type":{"array":["u8",32]}},{"name":"reward_pendings","docs":["Current pending rewards"],"type":"u64"},{"name":"total_claimed_rewards","docs":["Total claimed rewards"],"type":"u64"}]}},{"name":"Vesting","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"position","type":"pubkey"},{"name":"inner_vesting","type":{"defined":{"name":"InnerVesting"}}},{"name":"padding2","type":{"array":["u128",4]}}]}},{"name":"VestingParameters","type":{"kind":"struct","fields":[{"name":"cliff_point","type":{"option":"u64"}},{"name":"period_frequency","type":"u64"},{"name":"cliff_unlock_liquidity","type":"u128"},{"name":"liquidity_per_period","type":"u128"},{"name":"number_of_period","type":"u16"}]}}],"constants":[{"name":"BIN_STEP_BPS_DEFAULT","type":"u16","value":"1"},{"name":"BIN_STEP_U128_DEFAULT_LE_BYTES","type":{"array":["u8",16]},"value":"[203, 16, 199, 186, 184, 141, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0]"},{"name":"CURRENT_POOL_VERSION","type":"u8","value":"1"},{"name":"CUSTOMIZABLE_POOL_PREFIX","type":"bytes","value":"[99, 112, 111, 111, 108]"},{"name":"FEE_DENOMINATOR","docs":["Default fee denominator. DO NOT simply update it as it will break logic that depends on it as default value."],"type":"u64","value":"1000000000"},{"name":"MAX_BASIS_POINT","docs":["Max basis point. 100% in pct"],"type":"u16","value":"10000"},{"name":"MAX_FEE_NUMERATOR_V0","type":"u64","value":"500000000"},{"name":"MAX_FEE_NUMERATOR_V1","type":"u64","value":"990000000"},{"name":"MAX_SQRT_PRICE_LE_BYTES","type":{"array":["u8",16]},"value":"[155, 87, 105, 78, 169, 26, 92, 132, 177, 196, 254, 255, 0, 0, 0, 0]"},{"name":"MIN_FEE_NUMERATOR","type":"u64","value":"100000"},{"name":"MIN_SQRT_PRICE_LE_BYTES","type":{"array":["u8",16]},"value":"[80, 59, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]"},{"name":"POOL_AUTHORITY_PREFIX","type":"bytes","value":"[112, 111, 111, 108, 95, 97, 117, 116, 104, 111, 114, 105, 116, 121]"},{"name":"POOL_PREFIX","type":"bytes","value":"[112, 111, 111, 108]"},{"name":"POSITION_NFT_ACCOUNT_PREFIX","type":"bytes","value":"[112, 111, 115, 105, 116, 105, 111, 110, 95, 110, 102, 116, 95, 97, 99, 99, 111, 117, 110, 116]"},{"name":"POSITION_PREFIX","type":"bytes","value":"[112, 111, 115, 105, 116, 105, 111, 110]"},{"name":"SPLIT_POSITION_DENOMINATOR","type":"u32","value":"1000000000"},{"name":"TOKEN_VAULT_PREFIX","type":"bytes","value":"[116, 111, 107, 101, 110, 95, 118, 97, 117, 108, 116]"}]} \ No newline at end of file diff --git a/idls/meteora_dbc.dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN.json b/idls/meteora_dbc.dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN.json new file mode 100644 index 0000000..98ed38b --- /dev/null +++ b/idls/meteora_dbc.dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN.json @@ -0,0 +1 @@ +{"address":"dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN","metadata":{"name":"dynamic_bonding_curve","version":"0.1.10","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"claim_creator_trading_fee","discriminator":[82,220,250,189,3,85,107,45],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"pool","writable":true},{"name":"token_a_account","docs":["The treasury token a account"],"writable":true},{"name":"token_b_account","docs":["The treasury token b account"],"writable":true},{"name":"base_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"quote_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"base_mint","docs":["The mint of token a"],"relations":["pool"]},{"name":"quote_mint","docs":["The mint of token b"]},{"name":"creator","signer":true},{"name":"token_base_program","docs":["Token a program"]},{"name":"token_quote_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"max_base_amount","type":"u64"},{"name":"max_quote_amount","type":"u64"}]},{"name":"claim_partner_pool_creation_fee","discriminator":[250,238,26,4,139,10,101,248],"accounts":[{"name":"config","relations":["pool"]},{"name":"pool","writable":true},{"name":"fee_claimer","signer":true},{"name":"fee_receiver","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"claim_protocol_fee","discriminator":[165,228,133,48,99,249,255,33],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"config","relations":["pool"]},{"name":"pool","writable":true},{"name":"base_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"quote_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"base_mint","docs":["The mint of token a"],"relations":["pool"]},{"name":"quote_mint","docs":["The mint of token b"],"relations":["config"]},{"name":"token_base_account","writable":true},{"name":"token_quote_account","writable":true},{"name":"operator"},{"name":"signer","docs":["Signer"],"signer":true},{"name":"token_base_program","docs":["Token a program"]},{"name":"token_quote_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"max_base_amount","type":"u64"},{"name":"max_quote_amount","type":"u64"}]},{"name":"claim_protocol_pool_creation_fee","discriminator":[114,205,83,188,240,153,25,54],"accounts":[{"name":"config","relations":["pool"]},{"name":"pool","writable":true},{"name":"operator"},{"name":"signer","docs":["Operator"],"signer":true},{"name":"treasury","writable":true,"address":"6aYhxiNGmG8AyU25rh2R7iFu4pBrqnQHpNUGhmsEXRcm"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"claim_trading_fee","discriminator":[8,236,89,49,152,125,177,81],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"config","relations":["pool"]},{"name":"pool","writable":true},{"name":"token_a_account","docs":["The treasury token a account"],"writable":true},{"name":"token_b_account","docs":["The treasury token b account"],"writable":true},{"name":"base_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"quote_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"base_mint","docs":["The mint of token a"],"relations":["pool"]},{"name":"quote_mint","docs":["The mint of token b"],"relations":["config"]},{"name":"fee_claimer","signer":true},{"name":"token_base_program","docs":["Token a program"]},{"name":"token_quote_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"max_amount_a","type":"u64"},{"name":"max_amount_b","type":"u64"}]},{"name":"close_claim_protocol_fee_operator","discriminator":[8,41,87,35,80,48,121,26],"accounts":[{"name":"claim_fee_operator","writable":true},{"name":"rent_receiver","writable":true},{"name":"signer","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"close_operator_account","discriminator":[171,9,213,74,120,23,3,29],"accounts":[{"name":"operator","writable":true},{"name":"signer","signer":true},{"name":"rent_receiver","writable":true}],"args":[]},{"name":"create_config","discriminator":[201,207,243,114,75,111,47,189],"accounts":[{"name":"config","writable":true,"signer":true},{"name":"fee_claimer"},{"name":"leftover_receiver"},{"name":"quote_mint","docs":["quote mint"]},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"config_parameters","type":{"defined":{"name":"ConfigParameters"}}}]},{"name":"create_locker","docs":["PERMISSIONLESS FUNCTIONS ///","create locker"],"discriminator":[167,90,137,154,75,47,17,84],"accounts":[{"name":"virtual_pool","docs":["Virtual pool"],"writable":true},{"name":"config","docs":["Config"],"relations":["virtual_pool"]},{"name":"pool_authority","writable":true,"address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"base_vault","writable":true,"relations":["virtual_pool"]},{"name":"base_mint","writable":true,"relations":["virtual_pool"]},{"name":"base","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,97,115,101,95,108,111,99,107,101,114]},{"kind":"account","path":"virtual_pool"}]}},{"name":"creator","relations":["virtual_pool"]},{"name":"escrow","writable":true},{"name":"escrow_token","writable":true},{"name":"payer","writable":true,"signer":true},{"name":"token_program"},{"name":"locker_program","address":"LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn"},{"name":"locker_event_authority"},{"name":"system_program","docs":["System program."],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_operator_account","discriminator":[221,64,246,149,240,153,229,163],"accounts":[{"name":"operator","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,111,114]},{"kind":"account","path":"whitelisted_address"}]}},{"name":"whitelisted_address"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"permission","type":"u128"}]},{"name":"create_partner_metadata","docs":["PARTNER FUNCTIONS ///"],"discriminator":[192,168,234,191,188,226,227,255],"accounts":[{"name":"partner_metadata","docs":["Partner metadata"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,97,114,116,110,101,114,95,109,101,116,97,100,97,116,97]},{"kind":"account","path":"fee_claimer"}]}},{"name":"payer","docs":["Payer of the partner metadata."],"writable":true,"signer":true},{"name":"fee_claimer","docs":["Fee claimer for partner"],"signer":true},{"name":"system_program","docs":["System program."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"metadata","type":{"defined":{"name":"CreatePartnerMetadataParameters"}}}]},{"name":"create_virtual_pool_metadata","discriminator":[45,97,187,103,254,109,124,134],"accounts":[{"name":"virtual_pool","writable":true},{"name":"virtual_pool_metadata","docs":["Virtual pool metadata"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[118,105,114,116,117,97,108,95,112,111,111,108,95,109,101,116,97,100,97,116,97]},{"kind":"account","path":"virtual_pool"}]}},{"name":"creator","signer":true},{"name":"payer","docs":["Payer of the virtual pool metadata."],"writable":true,"signer":true},{"name":"system_program","docs":["System program."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"metadata","type":{"defined":{"name":"CreateVirtualPoolMetadataParameters"}}}]},{"name":"creator_withdraw_surplus","discriminator":[165,3,137,7,28,134,76,80],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"config","relations":["virtual_pool"]},{"name":"virtual_pool","writable":true},{"name":"token_quote_account","docs":["The receiver token account"],"writable":true},{"name":"quote_vault","docs":["The vault token account for output token"],"writable":true,"relations":["virtual_pool"]},{"name":"quote_mint","docs":["The mint of quote token"],"relations":["config"]},{"name":"creator","signer":true},{"name":"token_quote_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"initialize_virtual_pool_with_spl_token","docs":["POOL CREATOR FUNCTIONS ////"],"discriminator":[140,85,215,176,102,54,104,79],"accounts":[{"name":"config","docs":["Which config the pool belongs to."]},{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"creator","signer":true},{"name":"base_mint","writable":true,"signer":true},{"name":"quote_mint","relations":["config"]},{"name":"pool","docs":["Initialize an account to store the pool state"],"writable":true},{"name":"base_vault","docs":["Token a vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"base_mint"},{"kind":"account","path":"pool"}]}},{"name":"quote_vault","docs":["Token b vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"quote_mint"},{"kind":"account","path":"pool"}]}},{"name":"mint_metadata","writable":true},{"name":"metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"payer","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"token_quote_program","docs":["Program to create mint account and mint tokens"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"InitializePoolParameters"}}}]},{"name":"initialize_virtual_pool_with_token2022","discriminator":[169,118,51,78,145,110,220,155],"accounts":[{"name":"config","docs":["Which config the pool belongs to."]},{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"creator","signer":true},{"name":"base_mint","docs":["Unique token mint address, initialize in contract"],"writable":true,"signer":true},{"name":"quote_mint","relations":["config"]},{"name":"pool","docs":["Initialize an account to store the pool state"],"writable":true},{"name":"base_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"base_mint"},{"kind":"account","path":"pool"}]}},{"name":"quote_vault","docs":["Token quote vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"quote_mint"},{"kind":"account","path":"pool"}]}},{"name":"payer","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"token_quote_program","docs":["Program to create mint account and mint tokens"]},{"name":"token_program","docs":["token program for base mint"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"InitializePoolParameters"}}}]},{"name":"migrate_meteora_damm","discriminator":[27,1,48,22,180,63,118,217],"accounts":[{"name":"virtual_pool","docs":["virtual pool"],"writable":true,"relations":["migration_metadata"]},{"name":"migration_metadata","writable":true},{"name":"config","relations":["virtual_pool"]},{"name":"pool_authority","writable":true,"address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"pool","writable":true},{"name":"damm_config","docs":["pool config"]},{"name":"lp_mint","writable":true},{"name":"token_a_mint","writable":true},{"name":"token_b_mint"},{"name":"a_vault","writable":true},{"name":"b_vault","writable":true},{"name":"a_token_vault","writable":true},{"name":"b_token_vault","writable":true},{"name":"a_vault_lp_mint","writable":true},{"name":"b_vault_lp_mint","writable":true},{"name":"a_vault_lp","writable":true},{"name":"b_vault_lp","writable":true},{"name":"base_vault","writable":true,"relations":["virtual_pool"]},{"name":"quote_vault","writable":true,"relations":["virtual_pool"]},{"name":"virtual_pool_lp","writable":true},{"name":"protocol_token_a_fee","writable":true},{"name":"protocol_token_b_fee","writable":true},{"name":"payer","writable":true,"signer":true},{"name":"rent"},{"name":"mint_metadata","writable":true},{"name":"metadata_program"},{"name":"amm_program","address":"Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB"},{"name":"vault_program"},{"name":"token_program","docs":["token_program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program"},{"name":"system_program","docs":["System program."],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"migrate_meteora_damm_claim_lp_token","discriminator":[139,133,2,30,91,145,127,154],"accounts":[{"name":"virtual_pool","relations":["migration_metadata"]},{"name":"migration_metadata","docs":["migration metadata"],"writable":true},{"name":"pool_authority","writable":true,"address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"lp_mint","relations":["migration_metadata"]},{"name":"source_token","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_authority"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"migration_metadata"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"destination_token","writable":true,"pda":{"seeds":[{"kind":"account","path":"owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"migration_metadata"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"owner"},{"name":"sender","signer":true},{"name":"token_program","docs":["token_program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[]},{"name":"migrate_meteora_damm_lock_lp_token","discriminator":[177,55,238,157,251,88,165,42],"accounts":[{"name":"virtual_pool","relations":["migration_metadata"]},{"name":"migration_metadata","docs":["migration_metadata"],"writable":true},{"name":"pool_authority","writable":true,"address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"pool","writable":true,"relations":["lock_escrow"]},{"name":"lp_mint","relations":["migration_metadata"]},{"name":"lock_escrow","writable":true},{"name":"owner","relations":["lock_escrow"]},{"name":"source_tokens","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_authority"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"migration_metadata"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"escrow_vault","writable":true},{"name":"amm_program","address":"Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB"},{"name":"a_vault"},{"name":"b_vault"},{"name":"a_vault_lp"},{"name":"b_vault_lp"},{"name":"a_vault_lp_mint"},{"name":"b_vault_lp_mint"},{"name":"token_program","docs":["token_program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[]},{"name":"migration_damm_v2","discriminator":[156,169,230,103,53,228,80,64],"accounts":[{"name":"virtual_pool","docs":["virtual pool"],"writable":true},{"name":"migration_metadata"},{"name":"config","docs":["virtual pool config key"],"relations":["virtual_pool"]},{"name":"pool_authority","writable":true,"address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"pool","writable":true},{"name":"first_position_nft_mint","writable":true,"signer":true},{"name":"first_position_nft_account","writable":true},{"name":"first_position","writable":true},{"name":"second_position_nft_mint","writable":true,"signer":true,"optional":true},{"name":"second_position_nft_account","writable":true,"optional":true},{"name":"second_position","writable":true,"optional":true},{"name":"damm_pool_authority"},{"name":"amm_program","address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"},{"name":"base_mint","writable":true},{"name":"quote_mint","writable":true},{"name":"token_a_vault","writable":true},{"name":"token_b_vault","writable":true},{"name":"base_vault","writable":true,"relations":["virtual_pool"]},{"name":"quote_vault","writable":true,"relations":["virtual_pool"]},{"name":"payer","writable":true,"signer":true},{"name":"token_base_program"},{"name":"token_quote_program"},{"name":"token_2022_program"},{"name":"damm_event_authority"},{"name":"system_program","docs":["System program."],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"migration_damm_v2_create_metadata","discriminator":[109,189,19,36,195,183,222,82],"accounts":[{"name":"virtual_pool"},{"name":"config"},{"name":"migration_metadata"},{"name":"payer"},{"name":"system_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"migration_meteora_damm_create_metadata","docs":["migrate damm v1"],"discriminator":[47,94,126,115,221,226,194,133],"accounts":[{"name":"virtual_pool"},{"name":"config","relations":["virtual_pool"]},{"name":"migration_metadata","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,101,111,114,97]},{"kind":"account","path":"virtual_pool"}]}},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"partner_withdraw_surplus","discriminator":[168,173,72,100,201,98,38,92],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"config","relations":["virtual_pool"]},{"name":"virtual_pool","writable":true},{"name":"token_quote_account","docs":["The receiver token account"],"writable":true},{"name":"quote_vault","docs":["The vault token account for output token"],"writable":true,"relations":["virtual_pool"]},{"name":"quote_mint","docs":["The mint of quote token"],"relations":["config"]},{"name":"fee_claimer","signer":true},{"name":"token_quote_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"swap","docs":["TRADING BOTS FUNCTIONS ////"],"discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"config","docs":["config key"],"relations":["pool"]},{"name":"pool","docs":["Pool account"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"base_vault","docs":["The vault token account for base token"],"writable":true,"relations":["pool"]},{"name":"quote_vault","docs":["The vault token account for quote token"],"writable":true,"relations":["pool"]},{"name":"base_mint","docs":["The mint of base token"]},{"name":"quote_mint","docs":["The mint of quote token"]},{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"token_base_program","docs":["Token base program"]},{"name":"token_quote_program","docs":["Token quote program"]},{"name":"referral_token_account","docs":["referral token account"],"writable":true,"optional":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SwapParameters"}}}]},{"name":"swap2","discriminator":[65,75,63,76,235,91,91,136],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"config","docs":["config key"],"relations":["pool"]},{"name":"pool","docs":["Pool account"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"base_vault","docs":["The vault token account for base token"],"writable":true,"relations":["pool"]},{"name":"quote_vault","docs":["The vault token account for quote token"],"writable":true,"relations":["pool"]},{"name":"base_mint","docs":["The mint of base token"]},{"name":"quote_mint","docs":["The mint of quote token"]},{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"token_base_program","docs":["Token base program"]},{"name":"token_quote_program","docs":["Token quote program"]},{"name":"referral_token_account","docs":["referral token account"],"writable":true,"optional":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SwapParameters2"}}}]},{"name":"transfer_pool_creator","discriminator":[20,7,169,33,58,147,166,33],"accounts":[{"name":"virtual_pool","writable":true},{"name":"config","relations":["virtual_pool"]},{"name":"creator","signer":true},{"name":"new_creator"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"withdraw_leftover","discriminator":[20,198,202,237,235,243,183,66],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"config","relations":["virtual_pool"]},{"name":"virtual_pool","writable":true},{"name":"token_base_account","docs":["The receiver token account, withdraw to ATA"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"leftover_receiver"},{"kind":"account","path":"token_base_program"},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"base_vault","docs":["The vault token account for output token"],"writable":true,"relations":["virtual_pool"]},{"name":"base_mint","docs":["The mint of quote token"],"relations":["virtual_pool"]},{"name":"leftover_receiver","relations":["config"]},{"name":"token_base_program","docs":["Token base program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"withdraw_migration_fee","docs":["BOTH partner and creator FUNCTIONS ///"],"discriminator":[237,142,45,23,129,6,222,162],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"config","relations":["virtual_pool"]},{"name":"virtual_pool","writable":true},{"name":"token_quote_account","docs":["The receiver token account"],"writable":true},{"name":"quote_vault","docs":["The vault token account for output token"],"writable":true,"relations":["virtual_pool"]},{"name":"quote_mint","docs":["The mint of quote token"],"relations":["config"]},{"name":"sender","signer":true},{"name":"token_quote_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"flag","type":"u8"}]},{"name":"zap_protocol_fee","discriminator":[213,155,187,34,56,182,91,240],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"config","relations":["pool"]},{"name":"pool","writable":true},{"name":"token_vault","writable":true},{"name":"token_mint"},{"name":"receiver_token","writable":true},{"name":"operator","docs":["zap claim fee operator"]},{"name":"signer","docs":["Operator"],"signer":true},{"name":"token_program","docs":["Token program"]},{"name":"sysvar_instructions","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[{"name":"max_amount","type":"u64"}]}],"accounts":[{"name":"ClaimFeeOperator","discriminator":[166,48,134,86,34,200,188,150]},{"name":"Config","discriminator":[155,12,170,224,30,250,204,130]},{"name":"LockEscrow","discriminator":[190,106,121,6,200,182,21,75]},{"name":"MeteoraDammMigrationMetadata","discriminator":[17,155,141,215,207,4,133,156]},{"name":"Operator","discriminator":[219,31,188,145,69,139,204,117]},{"name":"PartnerMetadata","discriminator":[68,68,130,19,16,209,98,156]},{"name":"PoolConfig","discriminator":[26,108,14,123,116,230,129,43]},{"name":"VirtualPool","discriminator":[213,224,5,209,98,69,119,92]},{"name":"VirtualPoolMetadata","discriminator":[217,37,82,250,43,47,228,254]}],"events":[{"name":"EvtClaimCreatorTradingFee","discriminator":[154,228,215,202,133,155,214,138]},{"name":"EvtClaimPoolCreationFee","discriminator":[149,111,149,44,136,64,175,62]},{"name":"EvtClaimProtocolFee","discriminator":[186,244,75,251,188,13,25,33]},{"name":"EvtClaimProtocolLiquidityMigrationFee","discriminator":[81,168,116,31,161,86,27,35]},{"name":"EvtClaimTradingFee","discriminator":[26,83,117,240,92,202,112,254]},{"name":"EvtCloseClaimFeeOperator","discriminator":[111,39,37,55,110,216,194,23]},{"name":"EvtCreateClaimFeeOperator","discriminator":[21,6,153,120,68,116,28,177]},{"name":"EvtCreateConfig","discriminator":[131,207,180,174,180,73,165,54]},{"name":"EvtCreateConfigV2","discriminator":[163,74,66,187,119,195,26,144]},{"name":"EvtCreateMeteoraMigrationMetadata","discriminator":[99,167,133,63,214,143,175,139]},{"name":"EvtCreatorWithdrawSurplus","discriminator":[152,73,21,15,66,87,53,157]},{"name":"EvtCurveComplete","discriminator":[229,231,86,84,156,134,75,24]},{"name":"EvtInitializePool","discriminator":[228,50,246,85,203,66,134,37]},{"name":"EvtPartnerClaimPoolCreationFee","discriminator":[174,223,44,150,145,98,89,195]},{"name":"EvtPartnerMetadata","discriminator":[200,127,6,55,13,32,8,150]},{"name":"EvtPartnerWithdrawMigrationFee","discriminator":[181,105,127,67,8,187,120,57]},{"name":"EvtPartnerWithdrawSurplus","discriminator":[195,56,152,9,232,72,35,22]},{"name":"EvtSwap","discriminator":[27,60,21,213,138,170,187,147]},{"name":"EvtSwap2","discriminator":[189,66,51,168,38,80,117,153]},{"name":"EvtUpdatePoolCreator","discriminator":[107,225,165,237,91,158,213,220]},{"name":"EvtVirtualPoolMetadata","discriminator":[188,18,72,76,195,91,38,74]},{"name":"EvtWithdrawLeftover","discriminator":[191,189,104,143,111,156,94,229]},{"name":"EvtWithdrawMigrationFee","discriminator":[26,203,84,85,161,23,100,214]}],"errors":[{"code":6000,"name":"MathOverflow","msg":"Math operation overflow"},{"code":6001,"name":"InvalidFee","msg":"Invalid fee setup"},{"code":6002,"name":"ExceededSlippage","msg":"Exceeded slippage tolerance"},{"code":6003,"name":"ExceedMaxFeeBps","msg":"Exceeded max fee bps"},{"code":6004,"name":"InvalidAdmin","msg":"Invalid admin"},{"code":6005,"name":"AmountIsZero","msg":"Amount is zero"},{"code":6006,"name":"TypeCastFailed","msg":"Type cast error"},{"code":6007,"name":"InvalidActivationType","msg":"Invalid activation type"},{"code":6008,"name":"InvalidQuoteMint","msg":"Invalid quote mint"},{"code":6009,"name":"InvalidCollectFeeMode","msg":"Invalid collect fee mode"},{"code":6010,"name":"InvalidMigrationFeeOption","msg":"Invalid migration fee option"},{"code":6011,"name":"InvalidInput","msg":"Invalid input"},{"code":6012,"name":"NotEnoughLiquidity","msg":"Not enough liquidity"},{"code":6013,"name":"PoolIsCompleted","msg":"Pool is completed"},{"code":6014,"name":"PoolIsIncompleted","msg":"Pool is incompleted"},{"code":6015,"name":"InvalidMigrationOption","msg":"Invalid migration option"},{"code":6016,"name":"InvalidTokenDecimals","msg":"Invalid token decimals"},{"code":6017,"name":"InvalidTokenType","msg":"Invalid token type"},{"code":6018,"name":"InvalidFeePercentage","msg":"Invalid fee percentage"},{"code":6019,"name":"InvalidQuoteThreshold","msg":"Invalid quote threshold"},{"code":6020,"name":"InvalidTokenSupply","msg":"Invalid token supply"},{"code":6021,"name":"InvalidCurve","msg":"Invalid curve"},{"code":6022,"name":"NotPermitToDoThisAction","msg":"Not permit to do this action"},{"code":6023,"name":"InvalidOwnerAccount","msg":"Invalid owner account"},{"code":6024,"name":"InvalidConfigAccount","msg":"Invalid config account"},{"code":6025,"name":"SurplusHasBeenWithdraw","msg":"Surplus has been withdraw"},{"code":6026,"name":"LeftoverHasBeenWithdraw","msg":"Leftover has been withdraw"},{"code":6027,"name":"TotalBaseTokenExceedMaxSupply","msg":"Total base token is exceeded max supply"},{"code":6028,"name":"UnsupportNativeMintToken2022","msg":"Unsupport native mint token 2022"},{"code":6029,"name":"InsufficientLiquidityForMigration","msg":"Insufficient liquidity for migration"},{"code":6030,"name":"MissingPoolConfigInRemainingAccount","msg":"Missing pool config in remaining account"},{"code":6031,"name":"InvalidVestingParameters","msg":"Invalid vesting parameters"},{"code":6032,"name":"InvalidLeftoverAddress","msg":"Invalid leftover address"},{"code":6033,"name":"InsufficientLiquidity","msg":"Liquidity in bonding curve is insufficient"},{"code":6034,"name":"InvalidFeeScheduler","msg":"Invalid fee scheduler"},{"code":6035,"name":"InvalidCreatorTradingFeePercentage","msg":"Invalid creator trading fee percentage"},{"code":6036,"name":"InvalidNewCreator","msg":"Invalid new creator"},{"code":6037,"name":"InvalidTokenAuthorityOption","msg":"Invalid token authority option"},{"code":6038,"name":"InvalidAccount","msg":"Invalid account for the instruction"},{"code":6039,"name":"InvalidMigratorFeePercentage","msg":"Invalid migrator fee percentage"},{"code":6040,"name":"MigrationFeeHasBeenWithdraw","msg":"Migration fee has been withdraw"},{"code":6041,"name":"InvalidBaseFeeMode","msg":"Invalid base fee mode"},{"code":6042,"name":"InvalidFeeRateLimiter","msg":"Invalid fee rate limiter"},{"code":6043,"name":"FailToValidateSingleSwapInstruction","msg":"Fail to validate single swap instruction in rate limiter"},{"code":6044,"name":"InvalidMigratedPoolFee","msg":"Invalid migrated pool fee params"},{"code":6045,"name":"UndeterminedError","msg":"Undertermined error"},{"code":6046,"name":"RateLimiterNotSupported","msg":"Rate limiter not supported"},{"code":6047,"name":"AmountLeftIsNotZero","msg":"Amount left is not zero"},{"code":6048,"name":"NextSqrtPriceIsSmallerThanStartSqrtPrice","msg":"Next sqrt price is smaller than start sqrt price"},{"code":6049,"name":"InvalidMinBaseFee","msg":"Invalid min base fee"},{"code":6050,"name":"AccountInvariantViolation","msg":"Account invariant violation"},{"code":6051,"name":"InvalidPoolCreationFee","msg":"Invalid pool creation fee"},{"code":6052,"name":"PoolCreationFeeHasBeenClaimed","msg":"Pool creation fee has been claimed"},{"code":6053,"name":"Unauthorized","msg":"Not permit to do this action"},{"code":6054,"name":"ZeroPoolCreationFee","msg":"Pool creation fee is zero"},{"code":6055,"name":"InvalidMigrationLockedLiquidity","msg":"Invalid migration locked liquidity"},{"code":6056,"name":"InvalidFeeMarketCapScheduler","msg":"Invalid fee market cap scheduler"},{"code":6057,"name":"FirstSwapValidationFailed","msg":"Fail to validate first swap with minimum fee"},{"code":6058,"name":"IncorrectATA","msg":"Incorrect ATA"},{"code":6059,"name":"InsufficientPoolLamports","msg":"Pool has insufficient lamports to perform the operation"},{"code":6060,"name":"InvalidPermission","msg":"Invalid permission"},{"code":6061,"name":"InvalidWithdrawProtocolFeeZapAccounts","msg":"Invalid withdraw protocol fee zap accounts"},{"code":6062,"name":"MintRestrictedFromZap","msg":"SOL,USDC protocol fee cannot be withdrawn via zap"},{"code":6063,"name":"InvalidZapOutParameters","msg":"Invalid zap out parameters"},{"code":6064,"name":"CpiDisabled","msg":"CPI disabled"},{"code":6065,"name":"MissingZapOutInstruction","msg":"Missing zap out instruction"},{"code":6066,"name":"InvalidZapAccounts","msg":"Invalid zap accounts"},{"code":6067,"name":"InvalidCompoundingParameters","msg":"Invalid compounding parameters"}],"types":[{"name":"BaseFeeConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","type":"u64"},{"name":"second_factor","type":"u64"},{"name":"third_factor","type":"u64"},{"name":"first_factor","type":"u16"},{"name":"base_fee_mode","type":"u8"},{"name":"padding_0","type":{"array":["u8",5]}}]}},{"name":"BaseFeeParameters","type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","type":"u64"},{"name":"first_factor","type":"u16"},{"name":"second_factor","type":"u64"},{"name":"third_factor","type":"u64"},{"name":"base_fee_mode","type":"u8"}]}},{"name":"ClaimFeeOperator","docs":["Parameter that set by the protocol"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"operator","docs":["operator"],"type":"pubkey"},{"name":"_padding","docs":["Reserve"],"type":{"array":["u8",128]}}]}},{"name":"Config","type":{"kind":"struct","fields":[{"name":"pool_fees","type":{"defined":{"name":"PoolFees"}}},{"name":"activation_duration","type":"u64"},{"name":"vault_config_key","type":"pubkey"},{"name":"pool_creator_authority","type":"pubkey"},{"name":"activation_type","type":"u8"},{"name":"partner_fee_numerator","type":"u64"},{"name":"padding","type":{"array":["u8",219]}}]}},{"name":"ConfigParameters","type":{"kind":"struct","fields":[{"name":"pool_fees","type":{"defined":{"name":"PoolFeeParameters"}}},{"name":"collect_fee_mode","type":"u8"},{"name":"migration_option","type":"u8"},{"name":"activation_type","type":"u8"},{"name":"token_type","type":"u8"},{"name":"token_decimal","type":"u8"},{"name":"partner_liquidity_percentage","type":"u8"},{"name":"partner_permanent_locked_liquidity_percentage","type":"u8"},{"name":"creator_liquidity_percentage","type":"u8"},{"name":"creator_permanent_locked_liquidity_percentage","type":"u8"},{"name":"migration_quote_threshold","type":"u64"},{"name":"sqrt_start_price","type":"u128"},{"name":"locked_vesting","type":{"defined":{"name":"LockedVestingParams"}}},{"name":"migration_fee_option","type":"u8"},{"name":"token_supply","type":{"option":{"defined":{"name":"TokenSupplyParams"}}}},{"name":"creator_trading_fee_percentage","type":"u8"},{"name":"token_update_authority","type":"u8"},{"name":"migration_fee","type":{"defined":{"name":"MigrationFee"}}},{"name":"migrated_pool_fee","type":{"defined":{"name":"MigratedPoolFee"}}},{"name":"pool_creation_fee","docs":["pool creation fee in SOL lamports value"],"type":"u64"},{"name":"partner_liquidity_vesting_info","type":{"defined":{"name":"LiquidityVestingInfoParams"}}},{"name":"creator_liquidity_vesting_info","type":{"defined":{"name":"LiquidityVestingInfoParams"}}},{"name":"migrated_pool_base_fee_mode","type":"u8"},{"name":"migrated_pool_market_cap_fee_scheduler_params","type":{"defined":{"name":"MigratedPoolMarketCapFeeSchedulerParams"}}},{"name":"enable_first_swap_with_min_fee","type":"bool"},{"name":"compounding_fee_bps","type":"u16"},{"name":"padding","docs":["padding for future use"],"type":{"array":["u8",2]}},{"name":"curve","type":{"vec":{"defined":{"name":"LiquidityDistributionParameters"}}}}]}},{"name":"CreatePartnerMetadataParameters","type":{"kind":"struct","fields":[{"name":"padding","type":{"array":["u8",96]}},{"name":"name","type":"string"},{"name":"website","type":"string"},{"name":"logo","type":"string"}]}},{"name":"CreateVirtualPoolMetadataParameters","type":{"kind":"struct","fields":[{"name":"padding","type":{"array":["u8",96]}},{"name":"name","type":"string"},{"name":"website","type":"string"},{"name":"logo","type":"string"}]}},{"name":"DynamicFeeConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"initialized","type":"u8"},{"name":"padding","type":{"array":["u8",7]}},{"name":"max_volatility_accumulator","type":"u32"},{"name":"variable_fee_control","type":"u32"},{"name":"bin_step","type":"u16"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"padding2","type":{"array":["u8",8]}},{"name":"bin_step_u128","type":"u128"}]}},{"name":"DynamicFeeParameters","type":{"kind":"struct","fields":[{"name":"bin_step","type":"u16"},{"name":"bin_step_u128","type":"u128"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"max_volatility_accumulator","type":"u32"},{"name":"variable_fee_control","type":"u32"}]}},{"name":"EvtClaimCreatorTradingFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"token_base_amount","type":"u64"},{"name":"token_quote_amount","type":"u64"}]}},{"name":"EvtClaimPoolCreationFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"receiver","type":"pubkey"},{"name":"creation_fee","type":"u64"}]}},{"name":"EvtClaimProtocolFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"token_base_amount","type":"u64"},{"name":"token_quote_amount","type":"u64"}]}},{"name":"EvtClaimProtocolLiquidityMigrationFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"token_base_amount","type":"u64"},{"name":"token_quote_amount","type":"u64"}]}},{"name":"EvtClaimTradingFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"token_base_amount","type":"u64"},{"name":"token_quote_amount","type":"u64"}]}},{"name":"EvtCloseClaimFeeOperator","docs":["Close claim fee operator"],"type":{"kind":"struct","fields":[{"name":"claim_fee_operator","type":"pubkey"},{"name":"operator","type":"pubkey"}]}},{"name":"EvtCreateClaimFeeOperator","docs":["Create claim fee operator"],"type":{"kind":"struct","fields":[{"name":"operator","type":"pubkey"}]}},{"name":"EvtCreateConfig","docs":["Create config"],"type":{"kind":"struct","fields":[{"name":"config","type":"pubkey"},{"name":"quote_mint","type":"pubkey"},{"name":"fee_claimer","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"pool_fees","type":{"defined":{"name":"PoolFeeParameters"}}},{"name":"collect_fee_mode","type":"u8"},{"name":"migration_option","type":"u8"},{"name":"activation_type","type":"u8"},{"name":"token_decimal","type":"u8"},{"name":"token_type","type":"u8"},{"name":"partner_permanent_locked_liquidity_percentage","type":"u8"},{"name":"partner_liquidity_percentage","type":"u8"},{"name":"creator_permanent_locked_liquidity_percentage","type":"u8"},{"name":"creator_liquidity_percentage","type":"u8"},{"name":"swap_base_amount","type":"u64"},{"name":"migration_quote_threshold","type":"u64"},{"name":"migration_base_amount","type":"u64"},{"name":"sqrt_start_price","type":"u128"},{"name":"locked_vesting","type":{"defined":{"name":"LockedVestingParams"}}},{"name":"migration_fee_option","type":"u8"},{"name":"fixed_token_supply_flag","type":"u8"},{"name":"pre_migration_token_supply","type":"u64"},{"name":"post_migration_token_supply","type":"u64"},{"name":"curve","type":{"vec":{"defined":{"name":"LiquidityDistributionParameters"}}}}]}},{"name":"EvtCreateConfigV2","type":{"kind":"struct","fields":[{"name":"config","type":"pubkey"},{"name":"quote_mint","type":"pubkey"},{"name":"fee_claimer","type":"pubkey"},{"name":"leftover_receiver","type":"pubkey"},{"name":"config_parameters","type":{"defined":{"name":"ConfigParameters"}}}]}},{"name":"EvtCreateMeteoraMigrationMetadata","type":{"kind":"struct","fields":[{"name":"virtual_pool","type":"pubkey"}]}},{"name":"EvtCreatorWithdrawSurplus","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"surplus_amount","type":"u64"}]}},{"name":"EvtCurveComplete","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"config","type":"pubkey"},{"name":"base_reserve","type":"u64"},{"name":"quote_reserve","type":"u64"}]}},{"name":"EvtInitializePool","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"config","type":"pubkey"},{"name":"creator","type":"pubkey"},{"name":"base_mint","type":"pubkey"},{"name":"pool_type","type":"u8"},{"name":"activation_point","type":"u64"}]}},{"name":"EvtPartnerClaimPoolCreationFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"partner","type":"pubkey"},{"name":"creation_fee","type":"u64"},{"name":"fee_receiver","type":"pubkey"}]}},{"name":"EvtPartnerMetadata","docs":["Create partner metadata"],"type":{"kind":"struct","fields":[{"name":"partner_metadata","type":"pubkey"},{"name":"fee_claimer","type":"pubkey"}]}},{"name":"EvtPartnerWithdrawMigrationFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"fee","type":"u64"}]}},{"name":"EvtPartnerWithdrawSurplus","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"surplus_amount","type":"u64"}]}},{"name":"EvtSwap","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"config","type":"pubkey"},{"name":"trade_direction","type":"u8"},{"name":"has_referral","type":"bool"},{"name":"params","type":{"defined":{"name":"SwapParameters"}}},{"name":"swap_result","type":{"defined":{"name":"SwapResult"}}},{"name":"amount_in","type":"u64"},{"name":"current_timestamp","type":"u64"}]}},{"name":"EvtSwap2","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"config","type":"pubkey"},{"name":"trade_direction","type":"u8"},{"name":"has_referral","type":"bool"},{"name":"swap_parameters","type":{"defined":{"name":"SwapParameters2"}}},{"name":"swap_result","type":{"defined":{"name":"SwapResult2"}}},{"name":"quote_reserve_amount","type":"u64"},{"name":"migration_threshold","type":"u64"},{"name":"current_timestamp","type":"u64"}]}},{"name":"EvtUpdatePoolCreator","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"creator","type":"pubkey"},{"name":"new_creator","type":"pubkey"}]}},{"name":"EvtVirtualPoolMetadata","docs":["Create virtual pool metadata"],"type":{"kind":"struct","fields":[{"name":"virtual_pool_metadata","type":"pubkey"},{"name":"virtual_pool","type":"pubkey"}]}},{"name":"EvtWithdrawLeftover","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"leftover_receiver","type":"pubkey"},{"name":"leftover_amount","type":"u64"}]}},{"name":"EvtWithdrawMigrationFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"fee","type":"u64"},{"name":"flag","type":"u8"}]}},{"name":"InitializePoolParameters","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]}},{"name":"LiquidityDistributionConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"sqrt_price","type":"u128"},{"name":"liquidity","type":"u128"}]}},{"name":"LiquidityDistributionParameters","type":{"kind":"struct","fields":[{"name":"sqrt_price","type":"u128"},{"name":"liquidity","type":"u128"}]}},{"name":"LiquidityVestingInfo","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"is_initialized","type":"u8"},{"name":"vesting_percentage","type":"u8"},{"name":"_padding","type":{"array":["u8",2]}},{"name":"bps_per_period","type":"u16"},{"name":"number_of_periods","type":"u16"},{"name":"frequency","type":"u32"},{"name":"cliff_duration_from_migration_time","type":"u32"}]}},{"name":"LiquidityVestingInfoParams","type":{"kind":"struct","fields":[{"name":"vesting_percentage","type":"u8"},{"name":"bps_per_period","type":"u16"},{"name":"number_of_periods","type":"u16"},{"name":"cliff_duration_from_migration_time","type":"u32"},{"name":"frequency","type":"u32"}]}},{"name":"LockEscrow","docs":["State of lock escrow account"],"type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"escrow_vault","type":"pubkey"},{"name":"bump","type":"u8"},{"name":"total_locked_amount","type":"u64"},{"name":"lp_per_token","type":"u128"},{"name":"unclaimed_fee_pending","type":"u64"},{"name":"a_fee","type":"u64"},{"name":"b_fee","type":"u64"}]}},{"name":"LockedVestingConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"amount_per_period","type":"u64"},{"name":"cliff_duration_from_migration_time","type":"u64"},{"name":"frequency","type":"u64"},{"name":"number_of_period","type":"u64"},{"name":"cliff_unlock_amount","type":"u64"},{"name":"_padding","type":"u64"}]}},{"name":"LockedVestingParams","type":{"kind":"struct","fields":[{"name":"amount_per_period","type":"u64"},{"name":"cliff_duration_from_migration_time","type":"u64"},{"name":"frequency","type":"u64"},{"name":"number_of_period","type":"u64"},{"name":"cliff_unlock_amount","type":"u64"}]}},{"name":"MeteoraDammMigrationMetadata","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"virtual_pool","docs":["pool"],"type":"pubkey"},{"name":"padding_0","docs":["!!! BE CAREFUL to use tombstone field, previous is pool creator"],"type":{"array":["u8",32]}},{"name":"partner","docs":["partner"],"type":"pubkey"},{"name":"lp_mint","docs":["lp mint"],"type":"pubkey"},{"name":"partner_locked_liquidity","docs":["partner locked liquidity"],"type":"u64"},{"name":"partner_liquidity","docs":["partner liquidity"],"type":"u64"},{"name":"creator_locked_liquidity","docs":["creator locked liquidity"],"type":"u64"},{"name":"creator_liquidity","docs":["creator liquidity"],"type":"u64"},{"name":"_padding_0","docs":["padding"],"type":"u8"},{"name":"creator_locked_status","docs":["flag to check whether liquidity token is locked for creator"],"type":"u8"},{"name":"partner_locked_status","docs":["flag to check whether liquidity token is locked for partner"],"type":"u8"},{"name":"creator_claim_status","docs":["flag to check whether creator has claimed liquidity token"],"type":"u8"},{"name":"partner_claim_status","docs":["flag to check whether partner has claimed liquidity token"],"type":"u8"},{"name":"_padding","docs":["Reserve"],"type":{"array":["u8",107]}}]}},{"name":"MigratedPoolFee","type":{"kind":"struct","fields":[{"name":"collect_fee_mode","type":"u8"},{"name":"dynamic_fee","type":"u8"},{"name":"pool_fee_bps","type":"u16"}]}},{"name":"MigratedPoolMarketCapFeeSchedulerParams","type":{"kind":"struct","fields":[{"name":"number_of_period","type":"u16"},{"name":"sqrt_price_step_bps","type":"u16"},{"name":"scheduler_expiration_duration","type":"u32"},{"name":"reduction_factor","type":"u64"}]}},{"name":"MigrationFee","type":{"kind":"struct","fields":[{"name":"fee_percentage","type":"u8"},{"name":"creator_fee_percentage","type":"u8"}]}},{"name":"Operator","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"whitelisted_address","type":"pubkey"},{"name":"permission","type":"u128"},{"name":"padding","type":{"array":["u64",2]}}]}},{"name":"PartnerMetadata","docs":["Metadata for a partner."],"type":{"kind":"struct","fields":[{"name":"fee_claimer","docs":["fee claimer"],"type":"pubkey"},{"name":"padding","docs":["padding for future use"],"type":{"array":["u128",6]}},{"name":"name","docs":["Name of partner."],"type":"string"},{"name":"website","docs":["Website of partner."],"type":"string"},{"name":"logo","docs":["Logo of partner"],"type":"string"}]}},{"name":"PoolConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"quote_mint","docs":["quote mint"],"type":"pubkey"},{"name":"fee_claimer","docs":["Address to get the fee"],"type":"pubkey"},{"name":"leftover_receiver","docs":["Address to receive extra base token after migration, in case token is fixed supply"],"type":"pubkey"},{"name":"pool_fees","docs":["Pool fee"],"type":{"defined":{"name":"PoolFeesConfig"}}},{"name":"partner_liquidity_vesting_info","type":{"defined":{"name":"LiquidityVestingInfo"}}},{"name":"creator_liquidity_vesting_info","type":{"defined":{"name":"LiquidityVestingInfo"}}},{"name":"padding_0","docs":["Padding for future use"],"type":{"array":["u8",14]}},{"name":"padding_1","docs":["Previously was protocol and referral fee percent. Beware of tombstone."],"type":"u16"},{"name":"collect_fee_mode","docs":["Collect fee mode"],"type":"u8"},{"name":"migration_option","docs":["migration option"],"type":"u8"},{"name":"activation_type","docs":["whether mode slot or timestamp"],"type":"u8"},{"name":"token_decimal","docs":["token decimals"],"type":"u8"},{"name":"version","docs":["version"],"type":"u8"},{"name":"token_type","docs":["token type of base token"],"type":"u8"},{"name":"quote_token_flag","docs":["quote token flag"],"type":"u8"},{"name":"partner_permanent_locked_liquidity_percentage","docs":["partner locked liquidity percentage"],"type":"u8"},{"name":"partner_liquidity_percentage","docs":["partner liquidity percentage"],"type":"u8"},{"name":"creator_permanent_locked_liquidity_percentage","docs":["creator post migration fee percentage"],"type":"u8"},{"name":"creator_liquidity_percentage","docs":["creator liquidity percentage"],"type":"u8"},{"name":"migration_fee_option","docs":["migration fee option"],"type":"u8"},{"name":"fixed_token_supply_flag","docs":["flag to indicate whether token is dynamic supply (0) or fixed supply (1)"],"type":"u8"},{"name":"creator_trading_fee_percentage","docs":["creator trading fee percentage"],"type":"u8"},{"name":"token_update_authority","docs":["token update authority"],"type":"u8"},{"name":"migration_fee_percentage","docs":["migration fee percentage"],"type":"u8"},{"name":"creator_migration_fee_percentage","docs":["creator migration fee percentage"],"type":"u8"},{"name":"padding_2","type":{"array":["u8",7]}},{"name":"swap_base_amount","docs":["swap base amount"],"type":"u64"},{"name":"migration_quote_threshold","docs":["migration quote threshold (in quote token)"],"type":"u64"},{"name":"migration_base_threshold","docs":["migration base threshold (in base token)"],"type":"u64"},{"name":"migration_sqrt_price","docs":["migration sqrt price"],"type":"u128"},{"name":"locked_vesting_config","docs":["locked vesting config"],"type":{"defined":{"name":"LockedVestingConfig"}}},{"name":"pre_migration_token_supply","docs":["pre migration token supply"],"type":"u64"},{"name":"post_migration_token_supply","docs":["post migration token supply"],"type":"u64"},{"name":"migrated_collect_fee_mode","docs":["migrated pool collect fee mode"],"type":"u8"},{"name":"migrated_dynamic_fee","docs":["migrated dynamic fee option."],"type":"u8"},{"name":"migrated_pool_fee_bps","docs":["migrated pool fee in bps"],"type":"u16"},{"name":"migrated_pool_base_fee_mode","type":"u8"},{"name":"enable_first_swap_with_min_fee","type":"u8"},{"name":"migrated_compounding_fee_bps","docs":["compounding fee bps for migrated DAMM v2 pool, should only be non-zero if migrated_collect_fee_mode is 2 (Compounding)"],"type":"u16"},{"name":"pool_creation_fee","docs":["pool creation fee in lamports value"],"type":"u64"},{"name":"migrated_pool_base_fee_bytes","docs":["serialized MigratedPoolMarketCapFeeSchedulerParams, only used when migrated_pool_base_fee_mode is market cap scheduler"],"type":{"array":["u8",16]}},{"name":"sqrt_start_price","docs":["minimum price"],"type":"u128"},{"name":"curve","docs":["curve, only use 20 point firstly, we can extend that latter"],"type":{"array":[{"defined":{"name":"LiquidityDistributionConfig"}},20]}}]}},{"name":"PoolFeeParameters","docs":["Information regarding fee charges"],"type":{"kind":"struct","fields":[{"name":"base_fee","docs":["Base fee"],"type":{"defined":{"name":"BaseFeeParameters"}}},{"name":"dynamic_fee","docs":["dynamic fee"],"type":{"option":{"defined":{"name":"DynamicFeeParameters"}}}}]}},{"name":"PoolFees","docs":["Information regarding fee charges"],"type":{"kind":"struct","fields":[{"name":"trade_fee_numerator","type":"u64"},{"name":"trade_fee_denominator","type":"u64"},{"name":"protocol_trade_fee_numerator","type":"u64"},{"name":"protocol_trade_fee_denominator","type":"u64"}]}},{"name":"PoolFeesConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"base_fee","type":{"defined":{"name":"BaseFeeConfig"}}},{"name":"dynamic_fee","type":{"defined":{"name":"DynamicFeeConfig"}}}]}},{"name":"PoolMetrics","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"total_protocol_base_fee","type":"u64"},{"name":"total_protocol_quote_fee","type":"u64"},{"name":"total_trading_base_fee","type":"u64"},{"name":"total_trading_quote_fee","type":"u64"}]}},{"name":"SwapParameters","type":{"kind":"struct","fields":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"}]}},{"name":"SwapParameters2","type":{"kind":"struct","fields":[{"name":"amount_0","docs":["When it's exact in, partial fill, this will be amount_in. When it's exact out, this will be amount_out"],"type":"u64"},{"name":"amount_1","docs":["When it's exact in, partial fill, this will be minimum_amount_out. When it's exact out, this will be maximum_amount_in"],"type":"u64"},{"name":"swap_mode","docs":["Swap mode, refer [SwapMode]"],"type":"u8"}]}},{"name":"SwapResult","docs":["Encodes all results of swapping"],"type":{"kind":"struct","fields":[{"name":"actual_input_amount","type":"u64"},{"name":"output_amount","type":"u64"},{"name":"next_sqrt_price","type":"u128"},{"name":"trading_fee","type":"u64"},{"name":"protocol_fee","type":"u64"},{"name":"referral_fee","type":"u64"}]}},{"name":"SwapResult2","type":{"kind":"struct","fields":[{"name":"included_fee_input_amount","type":"u64"},{"name":"excluded_fee_input_amount","type":"u64"},{"name":"amount_left","type":"u64"},{"name":"output_amount","type":"u64"},{"name":"next_sqrt_price","type":"u128"},{"name":"trading_fee","type":"u64"},{"name":"protocol_fee","type":"u64"},{"name":"referral_fee","type":"u64"}]}},{"name":"TokenSupplyParams","type":{"kind":"struct","fields":[{"name":"pre_migration_token_supply","docs":["pre migration token supply"],"type":"u64"},{"name":"post_migration_token_supply","docs":["post migration token supply","because DBC allow user to swap over the migration quote threshold, so in extreme case user may swap more than allowed buffer on curve","that result the total supply in post migration may be increased a bit (between pre_migration_token_supply and post_migration_token_supply)"],"type":"u64"}]}},{"name":"VirtualPool","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"volatility_tracker","docs":["volatility tracker"],"type":{"defined":{"name":"VolatilityTracker"}}},{"name":"config","docs":["config key"],"type":"pubkey"},{"name":"creator","docs":["creator"],"type":"pubkey"},{"name":"base_mint","docs":["base mint"],"type":"pubkey"},{"name":"base_vault","docs":["base vault"],"type":"pubkey"},{"name":"quote_vault","docs":["quote vault"],"type":"pubkey"},{"name":"base_reserve","docs":["base reserve"],"type":"u64"},{"name":"quote_reserve","docs":["quote reserve"],"type":"u64"},{"name":"protocol_base_fee","docs":["protocol base fee"],"type":"u64"},{"name":"protocol_quote_fee","docs":["protocol quote fee"],"type":"u64"},{"name":"partner_base_fee","docs":["partner base fee"],"type":"u64"},{"name":"partner_quote_fee","docs":["trading quote fee"],"type":"u64"},{"name":"sqrt_price","docs":["current price"],"type":"u128"},{"name":"activation_point","docs":["Activation point"],"type":"u64"},{"name":"pool_type","docs":["pool type, spl token or token2022"],"type":"u8"},{"name":"is_migrated","docs":["is migrated"],"type":"u8"},{"name":"is_partner_withdraw_surplus","docs":["is partner withdraw surplus"],"type":"u8"},{"name":"is_protocol_withdraw_surplus","docs":["is protocol withdraw surplus"],"type":"u8"},{"name":"migration_progress","docs":["migration progress"],"type":"u8"},{"name":"is_withdraw_leftover","docs":["is withdraw leftover"],"type":"u8"},{"name":"is_creator_withdraw_surplus","docs":["is creator withdraw surplus"],"type":"u8"},{"name":"migration_fee_withdraw_status","docs":["migration fee withdraw status","bit 1 (0b010) creator","bit 2 (0b100) partner"],"type":"u8"},{"name":"metrics","docs":["pool metrics"],"type":{"defined":{"name":"PoolMetrics"}}},{"name":"finish_curve_timestamp","docs":["The time curve is finished"],"type":"u64"},{"name":"creator_base_fee","docs":["creator base fee"],"type":"u64"},{"name":"creator_quote_fee","docs":["creator quote fee"],"type":"u64"},{"name":"legacy_creation_fee_bits","docs":["legacy creation fee bits, we dont use this now"],"type":"u8"},{"name":"creation_fee_bits","docs":["pool creation fee claim status"],"type":"u8"},{"name":"has_swap","docs":["Cached flag"],"type":"u8"},{"name":"_padding_0","docs":["Padding for further use"],"type":{"array":["u8",5]}},{"name":"protocol_liquidity_migration_fee_bps","type":"u16"},{"name":"_padding_1","type":{"array":["u8",6]}},{"name":"protocol_migration_base_fee_amount","type":"u64"},{"name":"protocol_migration_quote_fee_amount","type":"u64"},{"name":"_padding_2","docs":["Padding for further use"],"type":{"array":["u64",3]}}]}},{"name":"VirtualPoolMetadata","docs":["Metadata for a virtual pool."],"type":{"kind":"struct","fields":[{"name":"virtual_pool","docs":["virtual pool"],"type":"pubkey"},{"name":"padding","docs":["padding for future use"],"type":{"array":["u128",6]}},{"name":"name","docs":["Name of project."],"type":"string"},{"name":"website","docs":["Website of project."],"type":"string"},{"name":"logo","docs":["Logo of project"],"type":"string"}]}},{"name":"VolatilityTracker","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"last_update_timestamp","type":"u64"},{"name":"padding","type":{"array":["u8",8]}},{"name":"sqrt_price_reference","type":"u128"},{"name":"volatility_accumulator","type":"u128"},{"name":"volatility_reference","type":"u128"}]}}]} \ No newline at end of file diff --git a/idls/meteora_dlmm.LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo.json b/idls/meteora_dlmm.LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo.json new file mode 100644 index 0000000..31fdbda --- /dev/null +++ b/idls/meteora_dlmm.LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo.json @@ -0,0 +1 @@ +{"address":"LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo","metadata":{"name":"lb_clmm","version":"0.12.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"add_liquidity","discriminator":[181,157,89,67,143,182,52,72],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension","bin_array_lower","bin_array_upper"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"token_x_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_y_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"liquidity_parameter","type":{"defined":{"name":"LiquidityParameter"}}}]},{"name":"add_liquidity2","discriminator":[228,162,78,28,70,219,116,115],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"sender","signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"liquidity_parameter","type":{"defined":{"name":"LiquidityParameter"}}},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"add_liquidity_by_strategy","discriminator":[7,3,150,127,148,40,61,200],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension","bin_array_lower","bin_array_upper"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"token_x_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_y_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"liquidity_parameter","type":{"defined":{"name":"LiquidityParameterByStrategy"}}}]},{"name":"add_liquidity_by_strategy2","discriminator":[3,221,149,218,111,141,118,213],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"sender","signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"liquidity_parameter","type":{"defined":{"name":"LiquidityParameterByStrategy"}}},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"add_liquidity_by_strategy_one_side","discriminator":[41,5,238,175,100,225,6,205],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension","bin_array_lower","bin_array_upper"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token","writable":true},{"name":"reserve","writable":true},{"name":"token_mint"},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"liquidity_parameter","type":{"defined":{"name":"LiquidityParameterByStrategyOneSide"}}}]},{"name":"add_liquidity_by_weight","discriminator":[28,140,238,99,231,162,21,149],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension","bin_array_lower","bin_array_upper"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"token_x_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_y_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"liquidity_parameter","type":{"defined":{"name":"LiquidityParameterByWeight"}}}]},{"name":"add_liquidity_by_weight2","discriminator":[209,59,63,91,111,200,153,228],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"sender","signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"liquidity_parameter","type":{"defined":{"name":"LiquidityParameterByWeight"}}},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"add_liquidity_one_side","discriminator":[94,155,103,151,70,95,220,165],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension","bin_array_lower","bin_array_upper"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token","writable":true},{"name":"reserve","writable":true},{"name":"token_mint"},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"liquidity_parameter","type":{"defined":{"name":"LiquidityOneSideParameter"}}}]},{"name":"add_liquidity_one_side_precise","discriminator":[161,194,103,84,171,71,250,154],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension","bin_array_lower","bin_array_upper"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token","writable":true},{"name":"reserve","writable":true},{"name":"token_mint"},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"parameter","type":{"defined":{"name":"AddLiquiditySingleSidePreciseParameter"}}}]},{"name":"add_liquidity_one_side_precise2","discriminator":[33,51,163,201,117,98,125,231],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token","writable":true},{"name":"reserve","writable":true},{"name":"token_mint"},{"name":"sender","signer":true},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"liquidity_parameter","type":{"defined":{"name":"AddLiquiditySingleSidePreciseParameter2"}}},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"cancel_limit_order","discriminator":[132,156,132,31,67,40,232,97],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array_bitmap_extension","limit_order"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"limit_order","writable":true},{"name":"owner_token_x","docs":["When dont set it tokenAccount to prevent unnecessary create token account (rent fee) when user only withdraw token y"],"writable":true},{"name":"owner_token_y","writable":true},{"name":"owner","signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"bins","type":{"vec":"i32"}},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"claim_fee","discriminator":[169,32,79,137,136,232,70,137],"accounts":[{"name":"lb_pair","writable":true,"relations":["position","bin_array_lower","bin_array_upper"]},{"name":"position","writable":true},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"claim_fee2","discriminator":[112,191,101,171,28,144,127,187],"accounts":[{"name":"lb_pair","writable":true,"relations":["position"]},{"name":"position","writable":true},{"name":"sender","signer":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"token_program_x"},{"name":"token_program_y"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"min_bin_id","type":"i32"},{"name":"max_bin_id","type":"i32"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"claim_reward","discriminator":[149,95,181,242,94,90,158,162],"accounts":[{"name":"lb_pair","writable":true,"relations":["position","bin_array_lower","bin_array_upper"]},{"name":"position","writable":true},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"reward_vault","writable":true},{"name":"reward_mint"},{"name":"user_token_account","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u64"}]},{"name":"claim_reward2","discriminator":[190,3,127,119,178,87,157,183],"accounts":[{"name":"lb_pair","writable":true,"relations":["position"]},{"name":"position","writable":true},{"name":"sender","signer":true},{"name":"reward_vault","writable":true},{"name":"reward_mint"},{"name":"user_token_account","writable":true},{"name":"token_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u64"},{"name":"min_bin_id","type":"i32"},{"name":"max_bin_id","type":"i32"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"close_bin_array","discriminator":[68,174,88,80,181,204,19,224],"accounts":[{"name":"lb_pair","relations":["bin_array"]},{"name":"bin_array","writable":true},{"name":"rent_receiver","writable":true,"signer":true},{"name":"signer","signer":true}],"args":[]},{"name":"close_claim_fee_operator_account","discriminator":[184,213,88,31,179,101,130,36],"accounts":[{"name":"claim_fee_operator","writable":true},{"name":"rent_receiver","writable":true},{"name":"signer","signer":true}],"args":[]},{"name":"close_limit_order_if_empty","discriminator":[57,124,36,155,126,249,93,171],"accounts":[{"name":"limit_order","writable":true},{"name":"owner","signer":true},{"name":"rent_receiver","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"close_operator_account","discriminator":[171,9,213,74,120,23,3,29],"accounts":[{"name":"operator","writable":true},{"name":"signer","signer":true},{"name":"rent_receiver","writable":true}],"args":[]},{"name":"close_position","discriminator":[123,134,81,0,49,68,98,98],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"rent_receiver","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"close_position2","discriminator":[174,90,35,115,186,40,147,226],"accounts":[{"name":"position","writable":true},{"name":"sender","signer":true},{"name":"rent_receiver","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"close_position_if_empty","discriminator":[59,124,212,118,91,152,110,157],"accounts":[{"name":"position","writable":true},{"name":"sender","signer":true},{"name":"rent_receiver","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"close_preset_parameter","discriminator":[4,148,145,100,134,26,181,61],"accounts":[{"name":"preset_parameter","writable":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"rent_receiver","writable":true}],"args":[]},{"name":"close_preset_parameter2","discriminator":[39,25,95,107,116,17,115,28],"accounts":[{"name":"preset_parameter","writable":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"rent_receiver","writable":true}],"args":[]},{"name":"close_token_badge","discriminator":[108,146,86,110,179,254,10,104],"accounts":[{"name":"token_badge","writable":true},{"name":"rent_receiver","writable":true},{"name":"operator"},{"name":"signer","signer":true}],"args":[]},{"name":"create_operator_account","discriminator":[221,64,246,149,240,153,229,163],"accounts":[{"name":"operator","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,111,114]},{"kind":"account","path":"whitelisted_signer"}]}},{"name":"whitelisted_signer"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"permission","type":"u128"}]},{"name":"decrease_position_length","discriminator":[194,219,136,32,25,96,105,37],"accounts":[{"name":"rent_receiver","writable":true},{"name":"position","writable":true},{"name":"owner","signer":true,"relations":["position"]},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"length_to_remove","type":"u16"},{"name":"side","type":"u8"}]},{"name":"for_idl_type_generation_do_not_call","discriminator":[180,105,69,80,95,50,73,108],"accounts":[{"name":"dummy_zc_account"}],"args":[{"name":"_ix","type":{"defined":{"name":"DummyIx"}}}]},{"name":"fund_reward","discriminator":[188,50,249,165,93,151,38,63],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array"]},{"name":"reward_vault","writable":true},{"name":"reward_mint"},{"name":"funder_token_account","writable":true},{"name":"funder","signer":true},{"name":"bin_array","writable":true},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u64"},{"name":"amount","type":"u64"},{"name":"carry_forward","type":"bool"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"go_to_a_bin","discriminator":[146,72,174,224,40,253,84,174],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array_bitmap_extension","from_bin_array","to_bin_array"]},{"name":"bin_array_bitmap_extension","optional":true},{"name":"from_bin_array","optional":true},{"name":"to_bin_array","optional":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"bin_id","type":"i32"}]},{"name":"increase_oracle_length","discriminator":[190,61,125,87,103,79,158,173],"accounts":[{"name":"oracle","writable":true},{"name":"funder","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"length_to_add","type":"u64"}]},{"name":"increase_position_length","discriminator":[80,83,117,211,66,13,33,149],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"lb_pair","relations":["position"]},{"name":"position","writable":true},{"name":"owner","signer":true,"relations":["position"]},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"length_to_add","type":"u16"},{"name":"side","type":"u8"}]},{"name":"increase_position_length2","discriminator":[255,210,204,71,115,137,225,113],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"lb_pair","relations":["position"]},{"name":"position","writable":true},{"name":"owner","signer":true,"relations":["position"]},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"minimum_upper_bin_id","type":"i32"}]},{"name":"initialize_bin_array","discriminator":[35,86,19,185,78,212,75,211],"accounts":[{"name":"lb_pair"},{"name":"bin_array","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,105,110,95,97,114,114,97,121]},{"kind":"account","path":"lb_pair"},{"kind":"arg","path":"index"}]}},{"name":"funder","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"i64"}]},{"name":"initialize_bin_array_bitmap_extension","discriminator":[47,157,226,180,12,240,33,71],"accounts":[{"name":"lb_pair"},{"name":"bin_array_bitmap_extension","docs":["Initialize an account to store if a bin array is initialized."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[98,105,116,109,97,112]},{"kind":"account","path":"lb_pair"}]}},{"name":"funder","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent"}],"args":[]},{"name":"initialize_customizable_permissionless_lb_pair","discriminator":[46,39,41,135,111,183,200,64],"accounts":[{"name":"lb_pair","writable":true},{"name":"bin_array_bitmap_extension","writable":true,"optional":true,"pda":{"seeds":[{"kind":"const","value":[98,105,116,109,97,112]},{"kind":"account","path":"lb_pair"}]}},{"name":"token_mint_x"},{"name":"token_mint_y"},{"name":"reserve_x","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_x"}]}},{"name":"reserve_y","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_y"}]}},{"name":"oracle","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"lb_pair"}]}},{"name":"user_token_x"},{"name":"funder","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"user_token_y"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"CustomizableParams"}}}]},{"name":"initialize_customizable_permissionless_lb_pair2","discriminator":[243,73,129,126,51,19,241,107],"accounts":[{"name":"lb_pair","writable":true},{"name":"bin_array_bitmap_extension","writable":true,"optional":true,"pda":{"seeds":[{"kind":"const","value":[98,105,116,109,97,112]},{"kind":"account","path":"lb_pair"}]}},{"name":"token_mint_x"},{"name":"token_mint_y"},{"name":"reserve_x","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_x"}]}},{"name":"reserve_y","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_y"}]}},{"name":"oracle","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"lb_pair"}]}},{"name":"user_token_x"},{"name":"funder","writable":true,"signer":true},{"name":"token_badge_x","optional":true},{"name":"token_badge_y","optional":true},{"name":"token_program_x"},{"name":"token_program_y"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"user_token_y"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"CustomizableParams"}}}]},{"name":"initialize_lb_pair","discriminator":[45,154,237,210,221,15,166,92],"accounts":[{"name":"lb_pair","writable":true},{"name":"bin_array_bitmap_extension","writable":true,"optional":true,"pda":{"seeds":[{"kind":"const","value":[98,105,116,109,97,112]},{"kind":"account","path":"lb_pair"}]}},{"name":"token_mint_x"},{"name":"token_mint_y"},{"name":"reserve_x","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_x"}]}},{"name":"reserve_y","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_y"}]}},{"name":"oracle","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"lb_pair"}]}},{"name":"preset_parameter"},{"name":"funder","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"active_id","type":"i32"},{"name":"bin_step","type":"u16"}]},{"name":"initialize_lb_pair2","discriminator":[73,59,36,120,237,83,108,198],"accounts":[{"name":"lb_pair","writable":true},{"name":"bin_array_bitmap_extension","writable":true,"optional":true,"pda":{"seeds":[{"kind":"const","value":[98,105,116,109,97,112]},{"kind":"account","path":"lb_pair"}]}},{"name":"token_mint_x"},{"name":"token_mint_y"},{"name":"reserve_x","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_x"}]}},{"name":"reserve_y","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_y"}]}},{"name":"oracle","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"lb_pair"}]}},{"name":"preset_parameter"},{"name":"funder","writable":true,"signer":true},{"name":"token_badge_x","optional":true},{"name":"token_badge_y","optional":true},{"name":"token_program_x"},{"name":"token_program_y"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"InitializeLbPair2Params"}}}]},{"name":"initialize_permission_lb_pair","discriminator":[108,102,213,85,251,3,53,21],"accounts":[{"name":"base","signer":true},{"name":"lb_pair","writable":true},{"name":"bin_array_bitmap_extension","writable":true,"optional":true,"pda":{"seeds":[{"kind":"const","value":[98,105,116,109,97,112]},{"kind":"account","path":"lb_pair"}]}},{"name":"token_mint_x"},{"name":"token_mint_y"},{"name":"reserve_x","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_x"}]}},{"name":"reserve_y","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_y"}]}},{"name":"oracle","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"lb_pair"}]}},{"name":"payer","writable":true,"signer":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"token_badge_x","optional":true},{"name":"token_badge_y","optional":true},{"name":"token_program_x"},{"name":"token_program_y"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"ix_data","type":{"defined":{"name":"InitPermissionPairIx"}}}]},{"name":"initialize_position","discriminator":[219,192,234,71,190,191,102,80],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"position","writable":true,"signer":true},{"name":"lb_pair"},{"name":"owner","signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"lower_bin_id","type":"i32"},{"name":"width","type":"i32"}]},{"name":"initialize_position2","discriminator":[143,19,242,145,213,15,104,115],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"position","writable":true,"signer":true},{"name":"lb_pair"},{"name":"owner","signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"lower_bin_id","type":"i32"},{"name":"width","type":"i32"}]},{"name":"initialize_position_by_operator","discriminator":[251,189,190,244,117,254,35,148],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"base","signer":true},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"lb_pair"},{"kind":"account","path":"base"},{"kind":"arg","path":"lower_bin_id"},{"kind":"arg","path":"width"}]}},{"name":"lb_pair"},{"name":"owner"},{"name":"operator","docs":["operator"],"signer":true},{"name":"operator_token_x"},{"name":"owner_token_x"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"lower_bin_id","type":"i32"},{"name":"width","type":"i32"},{"name":"fee_owner","type":"pubkey"},{"name":"lock_release_point","type":"u64"}]},{"name":"initialize_position_pda","discriminator":[46,82,125,146,85,141,228,153],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"base","signer":true},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"lb_pair"},{"kind":"account","path":"base"},{"kind":"arg","path":"lower_bin_id"},{"kind":"arg","path":"width"}]}},{"name":"lb_pair"},{"name":"owner","docs":["owner"],"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"lower_bin_id","type":"i32"},{"name":"width","type":"i32"}]},{"name":"initialize_preset_parameter","discriminator":[66,188,71,211,98,109,14,186],"accounts":[{"name":"preset_parameter","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,101,115,101,116,95,112,97,114,97,109,101,116,101,114,50]},{"kind":"arg","path":"ix.index"}]}},{"name":"operator"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"ix","type":{"defined":{"name":"InitPresetParametersIx"}}}]},{"name":"initialize_reward","discriminator":[95,135,192,196,242,129,230,68],"accounts":[{"name":"lb_pair","writable":true},{"name":"reward_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"arg","path":"reward_index"}]}},{"name":"reward_mint"},{"name":"token_badge","optional":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u64"},{"name":"reward_duration","type":"u64"},{"name":"funder","type":"pubkey"}]},{"name":"initialize_token_badge","discriminator":[253,77,205,95,27,224,89,223],"accounts":[{"name":"token_mint"},{"name":"token_badge","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,98,97,100,103,101]},{"kind":"account","path":"token_mint"}]}},{"name":"operator"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"place_limit_order","discriminator":[108,176,33,186,146,229,1,197],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"reserve","writable":true},{"name":"token_mint"},{"name":"limit_order","writable":true,"signer":true},{"name":"payer","writable":true,"signer":true},{"name":"owner"},{"name":"user_token","writable":true},{"name":"sender","signer":true},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"PlaceLimitOrderParams"}}},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"rebalance_liquidity","discriminator":[92,4,176,193,119,185,83,9],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"owner","signer":true,"relations":["position"]},{"name":"rent_payer","writable":true,"signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"RebalanceLiquidityParams"}}},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"remove_all_liquidity","discriminator":[10,51,61,35,112,105,24,85],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension","bin_array_lower","bin_array_upper"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"token_x_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_y_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"remove_liquidity","discriminator":[80,85,209,72,24,206,177,108],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension","bin_array_lower","bin_array_upper"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"token_x_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_y_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"bin_liquidity_removal","type":{"vec":{"defined":{"name":"BinLiquidityReduction"}}}}]},{"name":"remove_liquidity2","discriminator":[230,215,82,127,241,101,227,146],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"sender","signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"bin_liquidity_removal","type":{"vec":{"defined":{"name":"BinLiquidityReduction"}}}},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"remove_liquidity_by_range","discriminator":[26,82,102,152,240,74,105,26],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension","bin_array_lower","bin_array_upper"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"token_x_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_y_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"from_bin_id","type":"i32"},{"name":"to_bin_id","type":"i32"},{"name":"bps_to_remove","type":"u16"}]},{"name":"remove_liquidity_by_range2","discriminator":[204,2,195,145,53,145,145,205],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"sender","signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"from_bin_id","type":"i32"},{"name":"to_bin_id","type":"i32"},{"name":"bps_to_remove","type":"u16"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"set_activation_point","discriminator":[91,249,15,165,26,129,254,125],"accounts":[{"name":"lb_pair","writable":true},{"name":"signer","signer":true}],"args":[{"name":"activation_point","type":"u64"}]},{"name":"set_pair_status","discriminator":[67,248,231,137,154,149,217,174],"accounts":[{"name":"lb_pair","writable":true},{"name":"operator"},{"name":"signer","signer":true}],"args":[{"name":"status","type":"u8"}]},{"name":"set_pair_status_permissionless","discriminator":[78,59,152,211,70,183,46,208],"accounts":[{"name":"lb_pair","writable":true},{"name":"signer","signer":true}],"args":[{"name":"status","type":"u8"}]},{"name":"set_permissionless_operation_bits","discriminator":[84,58,203,139,163,81,190,186],"accounts":[{"name":"position","writable":true},{"name":"owner","signer":true,"relations":["position"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"bits","type":"u8"}]},{"name":"set_pre_activation_duration","discriminator":[165,61,201,244,130,159,22,100],"accounts":[{"name":"lb_pair","writable":true},{"name":"signer","signer":true}],"args":[{"name":"pre_activation_duration","type":"u64"}]},{"name":"set_pre_activation_swap_address","discriminator":[57,139,47,123,216,80,223,10],"accounts":[{"name":"lb_pair","writable":true},{"name":"signer","signer":true}],"args":[{"name":"pre_activation_swap_address","type":"pubkey"}]},{"name":"swap","discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"oracle","writable":true,"relations":["lb_pair"]},{"name":"host_fee_in","writable":true,"optional":true},{"name":"user","signer":true},{"name":"token_x_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_y_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_in","type":"u64"},{"name":"min_amount_out","type":"u64"}]},{"name":"swap2","discriminator":[65,75,63,76,235,91,91,136],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"oracle","writable":true,"relations":["lb_pair"]},{"name":"host_fee_in","writable":true,"optional":true},{"name":"user","signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_in","type":"u64"},{"name":"min_amount_out","type":"u64"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"swap_exact_out","discriminator":[250,73,101,33,38,207,75,184],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"oracle","writable":true,"relations":["lb_pair"]},{"name":"host_fee_in","writable":true,"optional":true},{"name":"user","signer":true},{"name":"token_x_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_y_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"max_in_amount","type":"u64"},{"name":"out_amount","type":"u64"}]},{"name":"swap_exact_out2","discriminator":[43,215,247,132,137,60,243,81],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"oracle","writable":true,"relations":["lb_pair"]},{"name":"host_fee_in","writable":true,"optional":true},{"name":"user","signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"max_in_amount","type":"u64"},{"name":"out_amount","type":"u64"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"swap_with_price_impact","discriminator":[56,173,230,208,173,228,156,205],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"oracle","writable":true,"relations":["lb_pair"]},{"name":"host_fee_in","writable":true,"optional":true},{"name":"user","signer":true},{"name":"token_x_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_y_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_in","type":"u64"},{"name":"active_id","type":{"option":"i32"}},{"name":"max_price_impact_bps","type":"u16"}]},{"name":"swap_with_price_impact2","discriminator":[74,98,192,214,177,51,75,51],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"oracle","writable":true,"relations":["lb_pair"]},{"name":"host_fee_in","writable":true,"optional":true},{"name":"user","signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_in","type":"u64"},{"name":"active_id","type":{"option":"i32"}},{"name":"max_price_impact_bps","type":"u16"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"update_base_fee_parameters","discriminator":[75,168,223,161,16,195,3,47],"accounts":[{"name":"lb_pair","writable":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"fee_parameter","type":{"defined":{"name":"BaseFeeParameter"}}}]},{"name":"update_dynamic_fee_parameters","discriminator":[92,161,46,246,255,189,22,22],"accounts":[{"name":"lb_pair","writable":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"fee_parameter","type":{"defined":{"name":"DynamicFeeParameter"}}}]},{"name":"update_fees_and_reward2","discriminator":[32,142,184,154,103,65,184,88],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position"]},{"name":"owner","signer":true}],"args":[{"name":"min_bin_id","type":"i32"},{"name":"max_bin_id","type":"i32"}]},{"name":"update_fees_and_rewards","discriminator":[154,230,250,13,236,209,75,223],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_lower","bin_array_upper"]},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"owner","signer":true}],"args":[]},{"name":"update_position_operator","discriminator":[202,184,103,143,180,191,116,217],"accounts":[{"name":"position","writable":true},{"name":"owner","signer":true,"relations":["position"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"operator","type":"pubkey"}]},{"name":"update_reward_duration","discriminator":[138,174,196,169,213,235,254,107],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array"]},{"name":"operator"},{"name":"signer","signer":true},{"name":"bin_array","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u64"},{"name":"new_duration","type":"u64"}]},{"name":"update_reward_funder","discriminator":[211,28,48,32,215,160,35,23],"accounts":[{"name":"lb_pair","writable":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u64"},{"name":"new_funder","type":"pubkey"}]},{"name":"withdraw_ineligible_reward","discriminator":[148,206,42,195,247,49,103,8],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array"]},{"name":"reward_vault","writable":true},{"name":"reward_mint"},{"name":"funder_token_account","writable":true},{"name":"funder","signer":true},{"name":"bin_array","writable":true},{"name":"token_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u64"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"withdraw_protocol_fee","discriminator":[158,201,158,189,33,93,162,103],"accounts":[{"name":"lb_pair","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"receiver_token_x","writable":true},{"name":"receiver_token_y","writable":true},{"name":"operator"},{"name":"signer","docs":["operator"],"signer":true},{"name":"token_x_program"},{"name":"token_y_program"}],"args":[{"name":"max_amount_x","type":"u64"},{"name":"max_amount_y","type":"u64"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"zap_protocol_fee","discriminator":[213,155,187,34,56,182,91,240],"accounts":[{"name":"lb_pair","writable":true},{"name":"reserve","writable":true},{"name":"token_mint"},{"name":"receiver_token","writable":true},{"name":"operator"},{"name":"signer","docs":["operator"],"signer":true},{"name":"token_program"},{"name":"sysvar_instructions","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[{"name":"max_amount","type":"u64"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]}],"accounts":[{"name":"BinArray","discriminator":[92,142,92,220,5,148,70,181]},{"name":"BinArrayBitmapExtension","discriminator":[80,111,124,113,55,237,18,5]},{"name":"ClaimFeeOperator","discriminator":[166,48,134,86,34,200,188,150]},{"name":"DummyZcAccount","discriminator":[94,107,238,80,208,48,180,8]},{"name":"LbPair","discriminator":[33,11,49,98,181,101,177,13]},{"name":"LimitOrder","discriminator":[137,183,212,91,115,29,141,227]},{"name":"Operator","discriminator":[219,31,188,145,69,139,204,117]},{"name":"Oracle","discriminator":[139,194,131,179,140,179,229,244]},{"name":"PositionV2","discriminator":[117,176,212,199,245,180,133,182]},{"name":"PresetParameter","discriminator":[242,62,244,34,181,112,58,170]},{"name":"PresetParameter2","discriminator":[171,236,148,115,162,113,222,174]},{"name":"TokenBadge","discriminator":[116,219,204,229,249,116,255,150]}],"events":[{"name":"AddLiquidity","discriminator":[31,94,125,90,227,52,61,186]},{"name":"CancelLimitOrderEvt","discriminator":[131,234,194,133,9,14,189,209]},{"name":"ClaimFee","discriminator":[75,122,154,48,140,74,123,163]},{"name":"ClaimFee2","discriminator":[232,171,242,97,58,77,35,45]},{"name":"ClaimReward","discriminator":[148,116,134,204,22,171,85,95]},{"name":"ClaimReward2","discriminator":[27,143,244,33,80,43,110,146]},{"name":"CloseLimitOrderEvt","discriminator":[142,135,8,76,92,63,118,83]},{"name":"CompositionFee","discriminator":[128,151,123,106,17,102,113,142]},{"name":"DecreasePositionLength","discriminator":[52,118,235,85,172,169,15,128]},{"name":"DynamicFeeParameterUpdate","discriminator":[88,88,178,135,194,146,91,243]},{"name":"FeeParameterUpdate","discriminator":[48,76,241,117,144,215,242,44]},{"name":"FundReward","discriminator":[246,228,58,130,145,170,79,204]},{"name":"GoToABin","discriminator":[59,138,76,68,138,131,176,67]},{"name":"IncreaseObservation","discriminator":[99,249,17,121,166,156,207,215]},{"name":"IncreasePositionLength","discriminator":[157,239,42,204,30,56,223,46]},{"name":"InitializeReward","discriminator":[211,153,88,62,149,60,177,70]},{"name":"LbPairCreate","discriminator":[185,74,252,125,27,215,188,111]},{"name":"PlaceLimitOrderEvt","discriminator":[43,79,27,169,244,28,225,63]},{"name":"PositionClose","discriminator":[255,196,16,107,28,202,53,128]},{"name":"PositionCreate","discriminator":[144,142,252,84,157,53,37,121]},{"name":"Rebalancing","discriminator":[0,109,117,179,61,91,199,200]},{"name":"RemoveLiquidity","discriminator":[116,244,97,232,103,31,152,58]},{"name":"SetPositionPermissionlessOperationBitsEvt","discriminator":[195,229,147,245,29,125,48,168]},{"name":"Swap","discriminator":[81,108,227,190,205,208,10,196]},{"name":"Swap2Evt","discriminator":[46,116,82,215,148,27,84,77]},{"name":"UpdatePositionLockReleasePoint","discriminator":[133,214,66,224,64,12,7,191]},{"name":"UpdatePositionOperator","discriminator":[39,115,48,204,246,47,66,57]},{"name":"UpdateRewardDuration","discriminator":[223,245,224,153,49,29,163,172]},{"name":"UpdateRewardFunder","discriminator":[224,178,174,74,252,165,85,180]},{"name":"WithdrawIneligibleReward","discriminator":[231,189,65,149,102,215,154,244]}],"errors":[{"code":6000,"name":"InvalidStartBinIndex","msg":"Invalid start bin index"},{"code":6001,"name":"InvalidBinId","msg":"Invalid bin id"},{"code":6002,"name":"InvalidInput","msg":"Invalid input data"},{"code":6003,"name":"ExceededAmountSlippageTolerance","msg":"Exceeded amount slippage tolerance"},{"code":6004,"name":"ExceededBinSlippageTolerance","msg":"Exceeded bin slippage tolerance"},{"code":6005,"name":"CompositionFactorFlawed","msg":"Composition factor flawed"},{"code":6006,"name":"NonPresetBinStep","msg":"Non preset bin step"},{"code":6007,"name":"ZeroLiquidity","msg":"Zero liquidity"},{"code":6008,"name":"InvalidPosition","msg":"Invalid position"},{"code":6009,"name":"BinArrayNotFound","msg":"Bin array not found"},{"code":6010,"name":"InvalidTokenMint","msg":"Invalid token mint"},{"code":6011,"name":"InvalidAccountForSingleDeposit","msg":"Invalid account for single deposit"},{"code":6012,"name":"PairInsufficientLiquidity","msg":"Pair insufficient liquidity"},{"code":6013,"name":"InvalidFeeOwner","msg":"Invalid fee owner"},{"code":6014,"name":"InvalidFeeWithdrawAmount","msg":"Invalid fee withdraw amount"},{"code":6015,"name":"InvalidAdmin","msg":"Invalid admin"},{"code":6016,"name":"IdenticalFeeOwner","msg":"Identical fee owner"},{"code":6017,"name":"InvalidBps","msg":"Invalid basis point"},{"code":6018,"name":"MathOverflow","msg":"Math operation overflow"},{"code":6019,"name":"TypeCastFailed","msg":"Type cast error"},{"code":6020,"name":"InvalidRewardIndex","msg":"Invalid reward index"},{"code":6021,"name":"InvalidRewardDuration","msg":"Invalid reward duration"},{"code":6022,"name":"RewardInitialized","msg":"Reward already initialized"},{"code":6023,"name":"RewardUninitialized","msg":"Reward not initialized"},{"code":6024,"name":"IdenticalFunder","msg":"Identical funder"},{"code":6025,"name":"RewardCampaignInProgress","msg":"Reward campaign in progress"},{"code":6026,"name":"IdenticalRewardDuration","msg":"Reward duration is the same"},{"code":6027,"name":"InvalidBinArray","msg":"Invalid bin array"},{"code":6028,"name":"NonContinuousBinArrays","msg":"Bin arrays must be continuous"},{"code":6029,"name":"InvalidRewardVault","msg":"Invalid reward vault"},{"code":6030,"name":"NonEmptyPosition","msg":"Position is not empty"},{"code":6031,"name":"UnauthorizedAccess","msg":"Unauthorized access"},{"code":6032,"name":"InvalidFeeParameter","msg":"Invalid fee parameter"},{"code":6033,"name":"MissingOracle","msg":"Missing oracle account"},{"code":6034,"name":"InsufficientSample","msg":"Insufficient observation sample"},{"code":6035,"name":"InvalidLookupTimestamp","msg":"Invalid lookup timestamp"},{"code":6036,"name":"BitmapExtensionAccountIsNotProvided","msg":"Bitmap extension account is not provided"},{"code":6037,"name":"CannotFindNonZeroLiquidityBinArrayId","msg":"Cannot find non-zero liquidity binArrayId"},{"code":6038,"name":"BinIdOutOfBound","msg":"Bin id out of bound"},{"code":6039,"name":"InsufficientOutAmount","msg":"Insufficient amount in for minimum out"},{"code":6040,"name":"InvalidPositionWidth","msg":"Invalid position width"},{"code":6041,"name":"ExcessiveFeeUpdate","msg":"Excessive fee update"},{"code":6042,"name":"PoolDisabled","msg":"Pool disabled"},{"code":6043,"name":"InvalidPoolType","msg":"Invalid pool type"},{"code":6044,"name":"ExceedMaxWhitelist","msg":"Whitelist for wallet is full"},{"code":6045,"name":"InvalidIndex","msg":"Invalid index"},{"code":6046,"name":"RewardNotEnded","msg":"Reward not ended"},{"code":6047,"name":"MustWithdrawnIneligibleReward","msg":"Must withdraw ineligible reward"},{"code":6048,"name":"UnauthorizedAddress","msg":"Unauthorized address"},{"code":6049,"name":"OperatorsAreTheSame","msg":"Cannot update because operators are the same"},{"code":6050,"name":"WithdrawToWrongTokenAccount","msg":"Withdraw to wrong token account"},{"code":6051,"name":"WrongRentReceiver","msg":"Wrong rent receiver"},{"code":6052,"name":"AlreadyPassActivationPoint","msg":"Already activated"},{"code":6053,"name":"ExceedMaxSwappedAmount","msg":"Swapped amount is exceeded max swapped amount"},{"code":6054,"name":"InvalidStrategyParameters","msg":"Invalid strategy parameters"},{"code":6055,"name":"LiquidityLocked","msg":"Liquidity locked"},{"code":6056,"name":"BinRangeIsNotEmpty","msg":"Bin range is not empty"},{"code":6057,"name":"NotExactAmountOut","msg":"Amount out is not matched with exact amount out"},{"code":6058,"name":"InvalidActivationType","msg":"Invalid activation type"},{"code":6059,"name":"InvalidActivationDuration","msg":"Invalid activation duration"},{"code":6060,"name":"MissingTokenAmountAsTokenLaunchProof","msg":"Missing token amount as token launch owner proof"},{"code":6061,"name":"InvalidQuoteToken","msg":"Quote token must be SOL or USDC"},{"code":6062,"name":"InvalidBinStep","msg":"Invalid bin step"},{"code":6063,"name":"InvalidBaseFee","msg":"Invalid base fee"},{"code":6064,"name":"InvalidPreActivationDuration","msg":"Invalid pre-activation duration"},{"code":6065,"name":"AlreadyPassPreActivationSwapPoint","msg":"Already pass pre-activation swap point"},{"code":6066,"name":"InvalidStatus","msg":"Invalid status"},{"code":6067,"name":"ExceededMaxOracleLength","msg":"Exceed max oracle length"},{"code":6068,"name":"InvalidMinimumLiquidity","msg":"Invalid minimum liquidity"},{"code":6069,"name":"NotSupportMint","msg":"Not support token_2022 mint extension"},{"code":6070,"name":"UnsupportedMintExtension","msg":"Unsupported mint extension"},{"code":6071,"name":"UnsupportNativeMintToken2022","msg":"Unsupported native mint token2022"},{"code":6072,"name":"UnmatchTokenMint","msg":"Unmatch token mint"},{"code":6073,"name":"UnsupportedTokenMint","msg":"Unsupported token mint"},{"code":6074,"name":"InsufficientRemainingAccounts","msg":"Insufficient remaining accounts"},{"code":6075,"name":"InvalidRemainingAccountSlice","msg":"Invalid remaining account slice"},{"code":6076,"name":"DuplicatedRemainingAccountTypes","msg":"Duplicated remaining account types"},{"code":6077,"name":"MissingRemainingAccountForTransferHook","msg":"Missing remaining account for transfer hook"},{"code":6078,"name":"NoTransferHookProgram","msg":"Remaining account was passed for transfer hook but there's no hook program"},{"code":6079,"name":"ZeroFundedAmount","msg":"Zero funded amount"},{"code":6080,"name":"InvalidSide","msg":"Invalid side"},{"code":6081,"name":"InvalidResizeLength","msg":"Invalid resize length"},{"code":6082,"name":"NotSupportAtTheMoment","msg":"Not support at the moment"},{"code":6083,"name":"InvalidRebalanceParameters","msg":"Invalid rebalance parameters"},{"code":6084,"name":"InvalidRewardAccounts","msg":"Invalid reward accounts"},{"code":6085,"name":"UndeterminedError","msg":"Undetermined error"},{"code":6086,"name":"ReallocExceedMaxLengthPerInstruction","msg":"Realloc exceed max length per instruction"},{"code":6087,"name":"InvalidBaseFeeMantissa","msg":"Mantissa cannot more than two significant digits"},{"code":6088,"name":"InvalidPositionOwner","msg":"Invalid position owner"},{"code":6089,"name":"InvalidPoolAddress","msg":"Invalid pool address"},{"code":6090,"name":"InvalidTokenBadgeType","msg":"Invalid token badge type"},{"code":6091,"name":"InvalidTransferHookAuthority","msg":"Invalid transfer hook authority"},{"code":6092,"name":"AmountXIsNegative","msg":"Amount x is negative"},{"code":6093,"name":"AmountYIsNegative","msg":"Amount y is negative"},{"code":6094,"name":"InvalidPoolCreator","msg":"Invalid pool creator"},{"code":6095,"name":"InvalidFunctionType","msg":"Invalid function type"},{"code":6096,"name":"InvalidPermission","msg":"Invalid permission"},{"code":6097,"name":"IncorrectATA","msg":"Incorrect ATA"},{"code":6098,"name":"InvalidWithdrawProtocolFeeZapAccounts","msg":"Invalid withdraw protocol fee zap accounts"},{"code":6099,"name":"MintRestrictedFromZap","msg":"SOL,USDC protocol fee cannot be withdrawn via zap"},{"code":6100,"name":"CpiDisabled","msg":"CPI disabled"},{"code":6101,"name":"MissingZapOutInstruction","msg":"Missing zap out instruction"},{"code":6102,"name":"InvalidZapAccounts","msg":"Invalid zap accounts"},{"code":6103,"name":"InvalidZapOutParameters","msg":"Invalid zap out parameters"},{"code":6104,"name":"InsufficientInAmount","msg":"Insufficient in amount"},{"code":6105,"name":"InvalidPlaceLimitOrderParameters","msg":"Invalid place limit order parameters"},{"code":6106,"name":"InvalidLimitOrderOwner","msg":"Invalid limit order owner"},{"code":6107,"name":"InvalidCancelLimitOrderParameters","msg":"Invalid cancel limit order parameters"},{"code":6108,"name":"CannotFindLimitOrderByBinId","msg":"Cannot find limit order by bin id"},{"code":6109,"name":"CancelNonEmptyLimitOrder","msg":"Cannot cancel non-empty limit order"},{"code":6110,"name":"InvalidCollectFeeMode","msg":"Invalid collect fee mode"}],"types":[{"name":"AccountsType","type":{"kind":"enum","variants":[{"name":"TransferHookX"},{"name":"TransferHookY"},{"name":"TransferHookReward"},{"name":"TransferHookMultiReward","fields":["u8"]},{"name":"TransferHookReferral"}]}},{"name":"ActivationType","docs":["Type of the activation"],"repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Slot"},{"name":"Timestamp"}]}},{"name":"AddLiquidity","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"from","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"amounts","type":{"array":["u64",2]}},{"name":"active_bin_id","type":"i32"}]}},{"name":"AddLiquidityParams","type":{"kind":"struct","fields":[{"name":"min_delta_id","type":"i32"},{"name":"max_delta_id","type":"i32"},{"name":"x0","type":"u64"},{"name":"y0","type":"u64"},{"name":"delta_x","type":"u64"},{"name":"delta_y","type":"u64"},{"name":"bit_flag","type":"u8"},{"name":"favor_x_in_active_id","type":"bool"},{"name":"padding","type":{"array":["u8",16]}}]}},{"name":"AddLiquiditySingleSidePreciseParameter","type":{"kind":"struct","fields":[{"name":"bins","type":{"vec":{"defined":{"name":"CompressedBinDepositAmount"}}}},{"name":"decompress_multiplier","type":"u64"}]}},{"name":"AddLiquiditySingleSidePreciseParameter2","type":{"kind":"struct","fields":[{"name":"bins","type":{"vec":{"defined":{"name":"CompressedBinDepositAmount"}}}},{"name":"decompress_multiplier","type":"u64"},{"name":"max_amount","type":"u64"}]}},{"name":"BaseFeeParameter","type":{"kind":"struct","fields":[{"name":"protocol_share","docs":["Portion of swap fees retained by the protocol by controlling protocol_share parameter. protocol_swap_fee = protocol_share * total_swap_fee"],"type":"u16"},{"name":"base_factor","docs":["Base factor for base fee rate"],"type":"u16"},{"name":"base_fee_power_factor","docs":["Base fee power factor"],"type":"u8"}]}},{"name":"Bin","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"amount_x","docs":["Amount of token X in the bin for market making. This already excluded protocol fees."],"type":"u64"},{"name":"amount_y","docs":["Amount of token Y in the bin for market making. This already excluded protocol fees."],"type":"u64"},{"name":"price","docs":["Bin price"],"type":"u128"},{"name":"liquidity_supply","docs":["Bin MM liquidity supply."],"type":"u128"},{"name":"fulfilled_order_amount_x","docs":["Total fulfilled order amount x"],"type":"u64"},{"name":"fulfilled_order_amount_y","docs":["Total fulfilled order amount y"],"type":"u64"},{"name":"limit_order_fee_ask_side","docs":["Limit order fee collected by ask side orders"],"type":"u64"},{"name":"limit_order_fee_bid_side","docs":["Limit order fee collected by bid side orders"],"type":"u64"},{"name":"fee_amount_x_per_token_stored","docs":["Swap fee amount of token X per liquidity deposited."],"type":"u128"},{"name":"fee_amount_y_per_token_stored","docs":["Swap fee amount of token Y per liquidity deposited."],"type":"u128"},{"name":"open_order_amount","docs":["Pending open limit orders amount in the bin."],"type":"u64"},{"name":"total_processing_order_amount","docs":["Total processing order amount"],"type":"u64"},{"name":"processed_order_remaining_amount","docs":["Remaining in processing open limit orders amount in the bin."],"type":"u64"},{"name":"order_age","docs":["Age"],"type":"u32"},{"name":"limit_order_ask_side","docs":["limit order flag"],"type":"u8"},{"name":"_padding_1","docs":["padding"],"type":{"array":["u8",3]}}]}},{"name":"BinArray","docs":["An account to contain a range of bin. For example: Bin 100 <-> 200.","For example:","BinArray index: 0 contains bin 0 <-> 599","index: 2 contains bin 600 <-> 1199, ..."],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"index","type":"i64"},{"name":"version","type":"u8"},{"name":"_padding_1","type":{"array":["u8",7]}},{"name":"lb_pair","type":"pubkey"},{"name":"bins","type":{"array":[{"defined":{"name":"Bin"}},70]}}]}},{"name":"BinArrayBitmapExtension","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"positive_bin_array_bitmap","docs":["Packed initialized bin array state for start_bin_index is positive"],"type":{"array":[{"array":["u64",8]},12]}},{"name":"negative_bin_array_bitmap","docs":["Packed initialized bin array state for start_bin_index is negative"],"type":{"array":[{"array":["u64",8]},12]}}]}},{"name":"BinLimitOrderAmount","type":{"kind":"struct","fields":[{"name":"id","type":"i32"},{"name":"amount","type":"u64"}]}},{"name":"BinLiquidityDistribution","type":{"kind":"struct","fields":[{"name":"bin_id","docs":["Define the bin ID wish to deposit to."],"type":"i32"},{"name":"distribution_x","docs":["DistributionX (or distributionY) is the percentages of amountX (or amountY) you want to add to each bin."],"type":"u16"},{"name":"distribution_y","docs":["DistributionX (or distributionY) is the percentages of amountX (or amountY) you want to add to each bin."],"type":"u16"}]}},{"name":"BinLiquidityDistributionByWeight","type":{"kind":"struct","fields":[{"name":"bin_id","docs":["Define the bin ID wish to deposit to."],"type":"i32"},{"name":"weight","docs":["weight of liquidity distributed for this bin id"],"type":"u16"}]}},{"name":"BinLiquidityReduction","type":{"kind":"struct","fields":[{"name":"bin_id","type":"i32"},{"name":"bps_to_remove","type":"u16"}]}},{"name":"CancelLimitOrderEvt","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"from","type":"pubkey"},{"name":"limit_order","type":"pubkey"},{"name":"amounts","type":{"array":["u64",2]}},{"name":"active_id","type":"i32"},{"name":"bins","type":{"vec":"i32"}}]}},{"name":"ClaimFee","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"fee_x","type":"u64"},{"name":"fee_y","type":"u64"}]}},{"name":"ClaimFee2","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"fee_x","type":"u64"},{"name":"fee_y","type":"u64"},{"name":"active_bin_id","type":"i32"}]}},{"name":"ClaimFeeOperator","docs":["Parameter that set by the protocol"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"operator","docs":["operator"],"type":"pubkey"},{"name":"_padding","docs":["Reserve"],"type":{"array":["u8",128]}}]}},{"name":"ClaimReward","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"reward_index","type":"u64"},{"name":"total_reward","type":"u64"}]}},{"name":"ClaimReward2","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"reward_index","type":"u64"},{"name":"total_reward","type":"u64"},{"name":"active_bin_id","type":"i32"}]}},{"name":"CloseLimitOrderEvt","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"limit_order","type":"pubkey"}]}},{"name":"CompositionFee","type":{"kind":"struct","fields":[{"name":"from","type":"pubkey"},{"name":"bin_id","type":"i16"},{"name":"token_x_fee_amount","type":"u64"},{"name":"token_y_fee_amount","type":"u64"},{"name":"protocol_token_x_fee_amount","type":"u64"},{"name":"protocol_token_y_fee_amount","type":"u64"}]}},{"name":"CompressedBinDepositAmount","type":{"kind":"struct","fields":[{"name":"bin_id","type":"i32"},{"name":"amount","type":"u32"}]}},{"name":"CustomizableParams","type":{"kind":"struct","fields":[{"name":"active_id","docs":["Pool price"],"type":"i32"},{"name":"bin_step","docs":["Bin step"],"type":"u16"},{"name":"base_factor","docs":["Base factor"],"type":"u16"},{"name":"activation_type","docs":["Activation type. 0 = Slot, 1 = Time. Check ActivationType enum"],"type":"u8"},{"name":"has_alpha_vault","docs":["Whether the pool has an alpha vault"],"type":"bool"},{"name":"activation_point","docs":["Decide when does the pool start trade. None = Now"],"type":{"option":"u64"}},{"name":"creator_pool_on_off_control","docs":["Pool creator have permission to enable/disable pool with restricted program validation. Only applicable for customizable permissionless pool."],"type":"bool"},{"name":"base_fee_power_factor","docs":["Base fee power factor"],"type":"u8"},{"name":"concrete_function_type","docs":["Concrete function type"],"type":"u8"},{"name":"collect_fee_mode","docs":["Collect fee mode"],"type":"u8"},{"name":"padding","docs":["Padding, for future use"],"type":{"array":["u8",60]}}]}},{"name":"DecreasePositionLength","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"length_to_remove","type":"u16"},{"name":"side","type":"u8"}]}},{"name":"DummyIx","type":{"kind":"struct","fields":[{"name":"_pair_status","type":{"defined":{"name":"PairStatus"}}},{"name":"_pair_type","type":{"defined":{"name":"PairType"}}},{"name":"_activation_type","type":{"defined":{"name":"ActivationType"}}},{"name":"_token_program_flag","type":{"defined":{"name":"TokenProgramFlags"}}},{"name":"_resize_side","type":{"defined":{"name":"ResizeSide"}}},{"name":"_rounding","type":{"defined":{"name":"Rounding"}}}]}},{"name":"DummyZcAccount","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"position_bin_data","type":{"defined":{"name":"PositionBinData"}}}]}},{"name":"DynamicFeeParameter","type":{"kind":"struct","fields":[{"name":"filter_period","docs":["Filter period determine high frequency trading time window."],"type":"u16"},{"name":"decay_period","docs":["Decay period determine when the volatile fee start decay / decrease."],"type":"u16"},{"name":"reduction_factor","docs":["Reduction factor controls the volatile fee rate decrement rate."],"type":"u16"},{"name":"variable_fee_control","docs":["Used to scale the variable fee component depending on the dynamic of the market"],"type":"u32"},{"name":"max_volatility_accumulator","docs":["Maximum number of bin crossed can be accumulated. Used to cap volatile fee rate."],"type":"u32"}]}},{"name":"DynamicFeeParameterUpdate","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"filter_period","docs":["Filter period determine high frequency trading time window."],"type":"u16"},{"name":"decay_period","docs":["Decay period determine when the volatile fee start decay / decrease."],"type":"u16"},{"name":"reduction_factor","docs":["Reduction factor controls the volatile fee rate decrement rate."],"type":"u16"},{"name":"variable_fee_control","docs":["Used to scale the variable fee component depending on the dynamic of the market"],"type":"u32"},{"name":"max_volatility_accumulator","docs":["Maximum number of bin crossed can be accumulated. Used to cap volatile fee rate."],"type":"u32"}]}},{"name":"FeeInfo","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"fee_x_per_token_complete","type":"u128"},{"name":"fee_y_per_token_complete","type":"u128"},{"name":"fee_x_pending","type":"u64"},{"name":"fee_y_pending","type":"u64"}]}},{"name":"FeeParameterUpdate","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"protocol_share","type":"u16"},{"name":"base_factor","type":"u16"},{"name":"base_fee_power_factor","type":"u8"}]}},{"name":"FundReward","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"funder","type":"pubkey"},{"name":"reward_index","type":"u64"},{"name":"amount","type":"u64"}]}},{"name":"GoToABin","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"from_bin_id","type":"i32"},{"name":"to_bin_id","type":"i32"}]}},{"name":"IncreaseObservation","type":{"kind":"struct","fields":[{"name":"oracle","type":"pubkey"},{"name":"new_observation_length","type":"u64"}]}},{"name":"IncreasePositionLength","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"length_to_add","type":"u16"},{"name":"side","type":"u8"}]}},{"name":"InitPermissionPairIx","type":{"kind":"struct","fields":[{"name":"active_id","type":"i32"},{"name":"bin_step","type":"u16"},{"name":"base_factor","type":"u16"},{"name":"base_fee_power_factor","type":"u8"},{"name":"activation_type","type":"u8"},{"name":"padding0","type":"u16"},{"name":"concrete_function_type","type":"u8"},{"name":"collect_fee_mode","type":"u8"}]}},{"name":"InitPresetParametersIx","type":{"kind":"struct","fields":[{"name":"index","type":"u16"},{"name":"bin_step","docs":["Bin step. Represent the price increment / decrement."],"type":"u16"},{"name":"base_factor","docs":["Used for base fee calculation. base_fee_rate = base_factor * bin_step * 10 * 10^base_fee_power_factor"],"type":"u16"},{"name":"filter_period","docs":["Filter period determine high frequency trading time window."],"type":"u16"},{"name":"decay_period","docs":["Decay period determine when the volatile fee start decay / decrease."],"type":"u16"},{"name":"reduction_factor","docs":["Reduction factor controls the volatile fee rate decrement rate."],"type":"u16"},{"name":"variable_fee_control","docs":["Used to scale the variable fee component depending on the dynamic of the market"],"type":"u32"},{"name":"max_volatility_accumulator","docs":["Maximum number of bin crossed can be accumulated. Used to cap volatile fee rate."],"type":"u32"},{"name":"protocol_share","docs":["Portion of swap fees retained by the protocol by controlling protocol_share parameter. protocol_swap_fee = protocol_share * total_swap_fee"],"type":"u16"},{"name":"base_fee_power_factor","docs":["Base fee power factor"],"type":"u8"},{"name":"concrete_function_type","docs":["function type"],"type":"u8"},{"name":"collect_fee_mode","docs":["collect fee mode"],"type":"u8"}]}},{"name":"InitializeLbPair2Params","type":{"kind":"struct","fields":[{"name":"active_id","docs":["Pool price"],"type":"i32"},{"name":"padding","docs":["Padding, for future use"],"type":{"array":["u8",96]}}]}},{"name":"InitializeReward","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"reward_mint","type":"pubkey"},{"name":"funder","type":"pubkey"},{"name":"reward_index","type":"u64"},{"name":"reward_duration","type":"u64"}]}},{"name":"LbPair","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"parameters","type":{"defined":{"name":"StaticParameters"}}},{"name":"v_parameters","type":{"defined":{"name":"VariableParameters"}}},{"name":"bump_seed","type":{"array":["u8",1]}},{"name":"bin_step_seed","docs":["Bin step signer seed"],"type":{"array":["u8",2]}},{"name":"pair_type","docs":["Type of the pair"],"type":"u8"},{"name":"active_id","docs":["Active bin id"],"type":"i32"},{"name":"bin_step","docs":["Bin step. Represent the price increment / decrement."],"type":"u16"},{"name":"status","docs":["Status of the pair. Check PairStatus enum."],"type":"u8"},{"name":"require_base_factor_seed","docs":["Require base factor seed"],"type":"u8"},{"name":"base_factor_seed","docs":["Base factor seed"],"type":{"array":["u8",2]}},{"name":"activation_type","docs":["Activation type"],"type":"u8"},{"name":"creator_pool_on_off_control","docs":["Allow pool creator to enable/disable pool with restricted validation. Only applicable for customizable permissionless pair type."],"type":"u8"},{"name":"token_x_mint","docs":["Token X mint"],"type":"pubkey"},{"name":"token_y_mint","docs":["Token Y mint"],"type":"pubkey"},{"name":"reserve_x","docs":["LB token X vault"],"type":"pubkey"},{"name":"reserve_y","docs":["LB token Y vault"],"type":"pubkey"},{"name":"protocol_fee","docs":["Uncollected protocol fee"],"type":{"defined":{"name":"ProtocolFee"}}},{"name":"_padding_1","docs":["padding 1"],"type":{"array":["u8",32]}},{"name":"reward_infos","docs":["Farming reward information"],"type":{"array":[{"defined":{"name":"RewardInfo"}},2]}},{"name":"oracle","docs":["Oracle pubkey"],"type":"pubkey"},{"name":"bin_array_bitmap","docs":["Packed initialized bin array state"],"type":{"array":["u64",16]}},{"name":"last_updated_at","docs":["Last time the pool fee parameter was updated"],"type":"i64"},{"name":"_padding_2","docs":["_padding_2"],"type":{"array":["u8",32]}},{"name":"pre_activation_swap_address","docs":["Address allowed to swap when the current point is greater than or equal to the pre-activation point. The pre-activation point is calculated as `activation_point - pre_activation_duration`."],"type":"pubkey"},{"name":"base_key","docs":["Base keypair. Only required for permission pair"],"type":"pubkey"},{"name":"activation_point","docs":["Time point to enable the pair. Only applicable for permission pair."],"type":"u64"},{"name":"pre_activation_duration","docs":["Duration before activation activation_point. Used to calculate pre-activation time point for pre_activation_swap_address"],"type":"u64"},{"name":"_padding_3","docs":["_padding 3"],"type":{"array":["u8",8]}},{"name":"_padding_4","docs":["_padding_4"],"type":"u64"},{"name":"creator","docs":["Pool creator"],"type":"pubkey"},{"name":"token_mint_x_program_flag","docs":["token_mint_x_program_flag"],"type":"u8"},{"name":"token_mint_y_program_flag","docs":["token_mint_y_program_flag"],"type":"u8"},{"name":"version","docs":["version to know whether we have reset tombstone fields"],"type":"u8"},{"name":"_reserved","docs":["Reserved space for future use"],"type":{"array":["u8",21]}}]}},{"name":"LbPairCreate","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"bin_step","type":"u16"},{"name":"token_x","type":"pubkey"},{"name":"token_y","type":"pubkey"}]}},{"name":"LimitOrder","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"lb_pair","docs":["The LB pair of this LO"],"type":"pubkey"},{"name":"owner","docs":["Owner of the LO. Client rely on this to to fetch their LOs."],"type":"pubkey"},{"name":"bin_count","docs":["Bin count"],"type":"u16"},{"name":"_padding_0","docs":["Padding"],"type":{"array":["u8",14]}},{"name":"padding_1","docs":["Reserved space for future use"],"type":{"array":["u64",4]}}]}},{"name":"LiquidityOneSideParameter","type":{"kind":"struct","fields":[{"name":"amount","docs":["Amount of X token or Y token to deposit"],"type":"u64"},{"name":"active_id","docs":["Active bin that integrator observe off-chain"],"type":"i32"},{"name":"max_active_bin_slippage","docs":["max active bin slippage allowed"],"type":"i32"},{"name":"bin_liquidity_dist","docs":["Liquidity distribution to each bins"],"type":{"vec":{"defined":{"name":"BinLiquidityDistributionByWeight"}}}}]}},{"name":"LiquidityParameter","type":{"kind":"struct","fields":[{"name":"amount_x","docs":["Amount of X token to deposit"],"type":"u64"},{"name":"amount_y","docs":["Amount of Y token to deposit"],"type":"u64"},{"name":"bin_liquidity_dist","docs":["Liquidity distribution to each bins"],"type":{"vec":{"defined":{"name":"BinLiquidityDistribution"}}}}]}},{"name":"LiquidityParameterByStrategy","type":{"kind":"struct","fields":[{"name":"amount_x","docs":["Amount of X token to deposit"],"type":"u64"},{"name":"amount_y","docs":["Amount of Y token to deposit"],"type":"u64"},{"name":"active_id","docs":["Active bin that integrator observe off-chain"],"type":"i32"},{"name":"max_active_bin_slippage","docs":["max active bin slippage allowed"],"type":"i32"},{"name":"strategy_parameters","docs":["strategy parameters"],"type":{"defined":{"name":"StrategyParameters"}}}]}},{"name":"LiquidityParameterByStrategyOneSide","type":{"kind":"struct","fields":[{"name":"amount","docs":["Amount of X token or Y token to deposit"],"type":"u64"},{"name":"active_id","docs":["Active bin that integrator observe off-chain"],"type":"i32"},{"name":"max_active_bin_slippage","docs":["max active bin slippage allowed"],"type":"i32"},{"name":"strategy_parameters","docs":["strategy parameters"],"type":{"defined":{"name":"StrategyParameters"}}}]}},{"name":"LiquidityParameterByWeight","type":{"kind":"struct","fields":[{"name":"amount_x","docs":["Amount of X token to deposit"],"type":"u64"},{"name":"amount_y","docs":["Amount of Y token to deposit"],"type":"u64"},{"name":"active_id","docs":["Active bin that integrator observe off-chain"],"type":"i32"},{"name":"max_active_bin_slippage","docs":["max active bin slippage allowed"],"type":"i32"},{"name":"bin_liquidity_dist","docs":["Liquidity distribution to each bins"],"type":{"vec":{"defined":{"name":"BinLiquidityDistributionByWeight"}}}}]}},{"name":"Operator","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"signer","type":"pubkey"},{"name":"permission","type":"u128"},{"name":"padding","type":{"array":["u64",2]}}]}},{"name":"Oracle","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"idx","docs":["Index of latest observation"],"type":"u64"},{"name":"active_size","docs":["Size of active sample. Active sample is initialized observation."],"type":"u64"},{"name":"length","docs":["Number of observations"],"type":"u64"}]}},{"name":"PairStatus","docs":["Pair status. 0 = Enabled, 1 = Disabled. Putting 0 as enabled for backward compatibility."],"repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Enabled"},{"name":"Disabled"}]}},{"name":"PairType","docs":["Type of the Pair. 0 = Permissionless, 1 = Permission, 2 = CustomizablePermissionless. Putting 0 as permissionless for backward compatibility."],"repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Permissionless"},{"name":"Permission"},{"name":"CustomizablePermissionless"},{"name":"PermissionlessV2"}]}},{"name":"PlaceLimitOrderEvt","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"sender","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"limit_order","type":"pubkey"},{"name":"active_id","type":"i32"},{"name":"params","type":{"defined":{"name":"PlaceLimitOrderParams"}}}]}},{"name":"PlaceLimitOrderParams","type":{"kind":"struct","fields":[{"name":"is_ask_side","type":"bool"},{"name":"padding","type":{"array":["u8",16]}},{"name":"relative_bin","type":{"option":{"defined":{"name":"RelativeBin"}}}},{"name":"bins","type":{"vec":{"defined":{"name":"BinLimitOrderAmount"}}}}]}},{"name":"PositionBinData","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"liquidity_share","type":"u128"},{"name":"reward_info","type":{"defined":{"name":"UserRewardInfo"}}},{"name":"fee_info","type":{"defined":{"name":"FeeInfo"}}}]}},{"name":"PositionClose","type":{"kind":"struct","fields":[{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"}]}},{"name":"PositionCreate","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"}]}},{"name":"PositionV2","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"lb_pair","docs":["The LB pair of this position"],"type":"pubkey"},{"name":"owner","docs":["Owner of the position. Client rely on this to to fetch their positions."],"type":"pubkey"},{"name":"liquidity_shares","docs":["Liquidity shares of this position in bins (lower_bin_id <-> upper_bin_id). This is the same as LP concept."],"type":{"array":["u128",70]}},{"name":"reward_infos","docs":["Farming reward information"],"type":{"array":[{"defined":{"name":"UserRewardInfo"}},70]}},{"name":"fee_infos","docs":["Swap fee to claim information"],"type":{"array":[{"defined":{"name":"FeeInfo"}},70]}},{"name":"lower_bin_id","docs":["Lower bin ID"],"type":"i32"},{"name":"upper_bin_id","docs":["Upper bin ID"],"type":"i32"},{"name":"last_updated_at","docs":["Last updated timestamp"],"type":"i64"},{"name":"total_claimed_fee_x_amount","docs":["Total claimed token fee X"],"type":"u64"},{"name":"total_claimed_fee_y_amount","docs":["Total claimed token fee Y"],"type":"u64"},{"name":"total_claimed_rewards","docs":["Total claimed rewards"],"type":{"array":["u64",2]}},{"name":"operator","docs":["Operator of position"],"type":"pubkey"},{"name":"lock_release_point","docs":["Time point which the locked liquidity can be withdraw"],"type":"u64"},{"name":"_padding_0","docs":["_padding_0, previous subjected_to_bootstrap_liquidity_locking, BE CAREFUL FOR TOMBSTONE WHEN REUSE !!"],"type":"u8"},{"name":"fee_owner","docs":["Address is able to claim fee in this position, only valid for bootstrap_liquidity_position"],"type":"pubkey"},{"name":"version","docs":["version to know whether we have reset tombstone fields"],"type":"u8"},{"name":"permissionless_operation_bits","type":"u8"},{"name":"_reserved","docs":["Reserved space for future use"],"type":{"array":["u8",85]}}]}},{"name":"PresetParameter","type":{"kind":"struct","fields":[{"name":"bin_step","docs":["Bin step. Represent the price increment / decrement."],"type":"u16"},{"name":"base_factor","docs":["Used for base fee calculation. base_fee_rate = base_factor * bin_step * 10 * 10^base_fee_power_factor"],"type":"u16"},{"name":"filter_period","docs":["Filter period determine high frequency trading time window."],"type":"u16"},{"name":"decay_period","docs":["Decay period determine when the volatile fee start decay / decrease."],"type":"u16"},{"name":"reduction_factor","docs":["Reduction factor controls the volatile fee rate decrement rate."],"type":"u16"},{"name":"variable_fee_control","docs":["Used to scale the variable fee component depending on the dynamic of the market"],"type":"u32"},{"name":"max_volatility_accumulator","docs":["Maximum number of bin crossed can be accumulated. Used to cap volatile fee rate."],"type":"u32"},{"name":"min_bin_id","docs":["Min bin id supported by the pool based on the configured bin step."],"type":"i32"},{"name":"max_bin_id","docs":["Max bin id supported by the pool based on the configured bin step."],"type":"i32"},{"name":"protocol_share","docs":["Portion of swap fees retained by the protocol by controlling protocol_share parameter. protocol_swap_fee = protocol_share * total_swap_fee"],"type":"u16"}]}},{"name":"PresetParameter2","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"bin_step","docs":["Bin step. Represent the price increment / decrement."],"type":"u16"},{"name":"base_factor","docs":["Used for base fee calculation. base_fee_rate = base_factor * bin_step * 10 * 10^base_fee_power_factor"],"type":"u16"},{"name":"filter_period","docs":["Filter period determine high frequency trading time window."],"type":"u16"},{"name":"decay_period","docs":["Decay period determine when the volatile fee start decay / decrease."],"type":"u16"},{"name":"variable_fee_control","docs":["Used to scale the variable fee component depending on the dynamic of the market"],"type":"u32"},{"name":"max_volatility_accumulator","docs":["Maximum number of bin crossed can be accumulated. Used to cap volatile fee rate."],"type":"u32"},{"name":"reduction_factor","docs":["Reduction factor controls the volatile fee rate decrement rate."],"type":"u16"},{"name":"protocol_share","docs":["Portion of swap fees retained by the protocol by controlling protocol_share parameter. protocol_swap_fee = protocol_share * total_swap_fee"],"type":"u16"},{"name":"index","docs":["index"],"type":"u16"},{"name":"base_fee_power_factor","docs":["Base fee power factor"],"type":"u8"},{"name":"concrete_function_type","docs":["function type, to check whether the pool should have LM farming or other functions in the future, refer ConcreteFunctionType"],"type":"u8"},{"name":"collect_fee_mode","docs":["collect fee mode"],"type":"u8"},{"name":"padding_0","type":{"array":["u8",7]}},{"name":"padding_1","docs":["Padding 1 for future use"],"type":{"array":["u64",19]}}]}},{"name":"ProtocolFee","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"amount_x","type":"u64"},{"name":"amount_y","type":"u64"}]}},{"name":"RebalanceLiquidityParams","type":{"kind":"struct","fields":[{"name":"active_id","docs":["active id"],"type":"i32"},{"name":"max_active_bin_slippage","docs":["max active bin slippage allowed"],"type":"u16"},{"name":"should_claim_fee","docs":["a flag to indicate that whether fee should be harvested"],"type":"bool"},{"name":"should_claim_reward","docs":["a flag to indicate that whether rewards should be harvested"],"type":"bool"},{"name":"min_withdraw_x_amount","docs":["threshold for withdraw token x"],"type":"u64"},{"name":"max_deposit_x_amount","docs":["threshold for deposit token x"],"type":"u64"},{"name":"min_withdraw_y_amount","docs":["threshold for withdraw token y"],"type":"u64"},{"name":"max_deposit_y_amount","docs":["threshold for deposit token y"],"type":"u64"},{"name":"shrink_mode","docs":["shrink mode"],"type":"u8"},{"name":"padding","docs":["padding 32 bytes for future usage"],"type":{"array":["u8",31]}},{"name":"removes","docs":["removes"],"type":{"vec":{"defined":{"name":"RemoveLiquidityParams"}}}},{"name":"adds","docs":["adds"],"type":{"vec":{"defined":{"name":"AddLiquidityParams"}}}}]}},{"name":"Rebalancing","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"active_bin_id","type":"i32"},{"name":"x_withdrawn_amount","type":"u64"},{"name":"x_added_amount","type":"u64"},{"name":"y_withdrawn_amount","type":"u64"},{"name":"y_added_amount","type":"u64"},{"name":"x_fee_amount","type":"u64"},{"name":"y_fee_amount","type":"u64"},{"name":"old_min_id","type":"i32"},{"name":"old_max_id","type":"i32"},{"name":"new_min_id","type":"i32"},{"name":"new_max_id","type":"i32"},{"name":"rewards","type":{"array":["u64",2]}}]}},{"name":"RelativeBin","type":{"kind":"struct","fields":[{"name":"active_id","docs":["Active bin that integrator observe off-chain"],"type":"i32"},{"name":"max_active_bin_slippage","docs":["max active bin slippage allowed"],"type":"i32"}]}},{"name":"RemainingAccountsInfo","type":{"kind":"struct","fields":[{"name":"slices","type":{"vec":{"defined":{"name":"RemainingAccountsSlice"}}}}]}},{"name":"RemainingAccountsSlice","type":{"kind":"struct","fields":[{"name":"accounts_type","type":{"defined":{"name":"AccountsType"}}},{"name":"length","type":"u8"}]}},{"name":"RemoveLiquidity","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"from","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"amounts","type":{"array":["u64",2]}},{"name":"active_bin_id","type":"i32"}]}},{"name":"RemoveLiquidityParams","type":{"kind":"struct","fields":[{"name":"min_bin_id","type":{"option":"i32"}},{"name":"max_bin_id","type":{"option":"i32"}},{"name":"bps","type":"u16"},{"name":"padding","type":{"array":["u8",16]}}]}},{"name":"ResizeSide","docs":["Side of resize, 0 for lower and 1 for upper"],"repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Lower"},{"name":"Upper"}]}},{"name":"RewardInfo","docs":["Stores the state relevant for tracking liquidity mining rewards"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"mint","docs":["Reward token mint."],"type":"pubkey"},{"name":"vault","docs":["Reward vault token account."],"type":"pubkey"},{"name":"funder","docs":["Authority account that allows to fund rewards"],"type":"pubkey"},{"name":"reward_duration","docs":["LM reward duration in seconds."],"type":"u64"},{"name":"reward_duration_end","docs":["LM reward duration end time."],"type":"u64"},{"name":"reward_rate","docs":["LM reward rate"],"type":"u128"},{"name":"last_update_time","docs":["The last time reward states were updated."],"type":"u64"},{"name":"cumulative_seconds_with_empty_liquidity_reward","docs":["Accumulated seconds where when farm distribute rewards, but the bin is empty. The reward will be accumulated for next reward time window."],"type":"u64"}]}},{"name":"Rounding","type":{"kind":"enum","variants":[{"name":"Up"},{"name":"Down"}]}},{"name":"SetPositionPermissionlessOperationBitsEvt","type":{"kind":"struct","fields":[{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"old_bits","type":"u8"},{"name":"new_bits","type":"u8"}]}},{"name":"StaticParameters","docs":["Parameter that set by the protocol"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"base_factor","docs":["Used for base fee calculation. base_fee_rate = base_factor * bin_step * 10 * 10^base_fee_power_factor"],"type":"u16"},{"name":"filter_period","docs":["Filter period determine high frequency trading time window."],"type":"u16"},{"name":"decay_period","docs":["Decay period determine when the volatile fee start decay / decrease."],"type":"u16"},{"name":"reduction_factor","docs":["Reduction factor controls the volatile fee rate decrement rate."],"type":"u16"},{"name":"variable_fee_control","docs":["Used to scale the variable fee component depending on the dynamic of the market"],"type":"u32"},{"name":"max_volatility_accumulator","docs":["Maximum number of bin crossed can be accumulated. Used to cap volatile fee rate."],"type":"u32"},{"name":"min_bin_id","docs":["Min bin id supported by the pool based on the configured bin step."],"type":"i32"},{"name":"max_bin_id","docs":["Max bin id supported by the pool based on the configured bin step."],"type":"i32"},{"name":"protocol_share","docs":["Portion of swap fees retained by the protocol by controlling protocol_share parameter. protocol_swap_fee = protocol_share * total_swap_fee"],"type":"u16"},{"name":"base_fee_power_factor","docs":["Base fee power factor"],"type":"u8"},{"name":"function_type","docs":["function type"],"type":"u8"},{"name":"collect_fee_mode","docs":["Collect fee mode"],"type":"u8"},{"name":"_padding","docs":["Padding for bytemuck safe alignment"],"type":{"array":["u8",3]}}]}},{"name":"StrategyParameters","type":{"kind":"struct","fields":[{"name":"min_bin_id","docs":["min bin id"],"type":"i32"},{"name":"max_bin_id","docs":["max bin id"],"type":"i32"},{"name":"strategy_type","docs":["strategy type"],"type":{"defined":{"name":"StrategyType"}}},{"name":"parameteres","docs":["parameters"],"type":{"array":["u8",64]}}]}},{"name":"StrategyType","type":{"kind":"enum","variants":[{"name":"SpotOneSide"},{"name":"CurveOneSide"},{"name":"BidAskOneSide"},{"name":"SpotBalanced"},{"name":"CurveBalanced"},{"name":"BidAskBalanced"},{"name":"SpotImBalanced"},{"name":"CurveImBalanced"},{"name":"BidAskImBalanced"}]}},{"name":"Swap","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"from","type":"pubkey"},{"name":"start_bin_id","type":"i32"},{"name":"end_bin_id","type":"i32"},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"},{"name":"swap_for_y","type":"bool"},{"name":"fee","type":"u64"},{"name":"protocol_fee","type":"u64"},{"name":"fee_bps","type":"u128"},{"name":"host_fee","type":"u64"}]}},{"name":"Swap2Evt","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"from","type":"pubkey"},{"name":"start_bin_id","type":"i32"},{"name":"end_bin_id","type":"i32"},{"name":"swap_for_y","type":"bool"},{"name":"fee_bps","type":"u128"},{"name":"amount_in","docs":["Total amount, user transfer out"],"type":"u64"},{"name":"amount_left","docs":["Leftover amount"],"type":"u64"},{"name":"amount_out","docs":["Total amount transfer to user, including transfer fee"],"type":"u64"},{"name":"mm_fee","docs":["Market maker fee"],"type":"u64"},{"name":"protocol_fee","docs":["Total protocol fee"],"type":"u64"},{"name":"limit_order_fee","docs":["Total limit order fee"],"type":"u64"},{"name":"host_fee","docs":["Total host fee"],"type":"u64"},{"name":"fees_on_input","docs":["check if fees on input"],"type":"bool"},{"name":"fees_on_token_x","docs":["check if fees is on token x"],"type":"bool"}]}},{"name":"TokenBadge","docs":["Parameter that set by the protocol"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"token_mint","docs":["token mint"],"type":"pubkey"},{"name":"_padding","docs":["Reserve"],"type":{"array":["u8",128]}}]}},{"name":"TokenProgramFlags","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"TokenProgram"},{"name":"TokenProgram2022"}]}},{"name":"UpdatePositionLockReleasePoint","type":{"kind":"struct","fields":[{"name":"position","type":"pubkey"},{"name":"current_point","type":"u64"},{"name":"new_lock_release_point","type":"u64"},{"name":"old_lock_release_point","type":"u64"},{"name":"sender","type":"pubkey"}]}},{"name":"UpdatePositionOperator","type":{"kind":"struct","fields":[{"name":"position","type":"pubkey"},{"name":"old_operator","type":"pubkey"},{"name":"new_operator","type":"pubkey"}]}},{"name":"UpdateRewardDuration","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"reward_index","type":"u64"},{"name":"old_reward_duration","type":"u64"},{"name":"new_reward_duration","type":"u64"}]}},{"name":"UpdateRewardFunder","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"reward_index","type":"u64"},{"name":"old_funder","type":"pubkey"},{"name":"new_funder","type":"pubkey"}]}},{"name":"UserRewardInfo","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"reward_per_token_completes","type":{"array":["u128",2]}},{"name":"reward_pendings","type":{"array":["u64",2]}}]}},{"name":"VariableParameters","docs":["Parameters that changes based on dynamic of the market"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"volatility_accumulator","docs":["Volatility accumulator measure the number of bin crossed since reference bin ID. Normally (without filter period taken into consideration), reference bin ID is the active bin of last swap.","It affects the variable fee rate"],"type":"u32"},{"name":"volatility_reference","docs":["Volatility reference is decayed volatility accumulator. It is always <= volatility_accumulator"],"type":"u32"},{"name":"index_reference","docs":["Active bin id of last swap."],"type":"i32"},{"name":"_padding","docs":["Padding for bytemuck safe alignment"],"type":{"array":["u8",4]}},{"name":"last_update_timestamp","docs":["Last timestamp the variable parameters was updated"],"type":"i64"},{"name":"_padding_1","docs":["Padding for bytemuck safe alignment"],"type":{"array":["u8",8]}}]}},{"name":"WithdrawIneligibleReward","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"reward_mint","type":"pubkey"},{"name":"amount","type":"u64"}]}}],"constants":[{"name":"BASIS_POINT_MAX","type":"u16","value":"10000"},{"name":"BIN_ARRAY","type":"bytes","value":"[98, 105, 110, 95, 97, 114, 114, 97, 121]"},{"name":"BIN_ARRAY_BITMAP_SEED","type":"bytes","value":"[98, 105, 116, 109, 97, 112]"},{"name":"BIN_ARRAY_BITMAP_SIZE","type":"i32","value":"512"},{"name":"CLAIM_PROTOCOL_FEE_OPERATOR","type":"bytes","value":"[99, 102, 95, 111, 112, 101, 114, 97, 116, 111, 114]"},{"name":"DEFAULT_BIN_PER_POSITION","type":"u64","value":"70"},{"name":"EXTENSION_BINARRAY_BITMAP_SIZE","type":"u64","value":"12"},{"name":"FEE_DENOMINATOR","type":"u64","value":"1000000000"},{"name":"HOST_FEE_BPS","docs":["Host fee. 20%"],"type":"u16","value":"2000"},{"name":"ILM_PROTOCOL_SHARE","type":"u16","value":"2000"},{"name":"LIMIT_ORDER_FEE_SHARE","type":"u16","value":"5000"},{"name":"MAX_BASE_FEE","docs":["Maximum base fee, base_fee / 10^9 = fee_in_percentage"],"type":"u128","value":"100000000"},{"name":"MAX_BIN_ID_PER_BIN_STEP","docs":["Maximum bin ID per bin step. Computed based on 1 bps. Used for bin id bound estimation."],"type":"i32","value":"351639"},{"name":"MAX_BIN_PER_ARRAY","type":"u64","value":"70"},{"name":"MAX_BIN_PER_LIMIT_ORDER","type":"u64","value":"50"},{"name":"MAX_BIN_STEP","docs":["Maximum bin step"],"type":"u16","value":"400"},{"name":"MAX_FEE_RATE","docs":["Maximum fee rate. 10%"],"type":"u64","value":"100000000"},{"name":"MAX_PROTOCOL_SHARE","docs":["Maximum protocol share of the fee. 25%"],"type":"u16","value":"2500"},{"name":"MAX_RESIZE_LENGTH","type":"u64","value":"91"},{"name":"MAX_REWARD_BIN_SPLIT","type":"u64","value":"15"},{"name":"MAX_REWARD_DURATION","type":"u64","value":"31536000"},{"name":"MINIMUM_LIQUIDITY","type":"u128","value":"1000000"},{"name":"MIN_BASE_FEE","docs":["Minimum base fee"],"type":"u128","value":"100000"},{"name":"MIN_REWARD_DURATION","type":"u64","value":"1"},{"name":"NUM_REWARDS","type":"u64","value":"2"},{"name":"OPERATOR_PREFIX","type":"bytes","value":"[111, 112, 101, 114, 97, 116, 111, 114]"},{"name":"ORACLE","type":"bytes","value":"[111, 114, 97, 99, 108, 101]"},{"name":"POSITION","type":"bytes","value":"[112, 111, 115, 105, 116, 105, 111, 110]"},{"name":"POSITION_MAX_LENGTH","type":"u64","value":"1400"},{"name":"PRESET_PARAMETER","type":"bytes","value":"[112, 114, 101, 115, 101, 116, 95, 112, 97, 114, 97, 109, 101, 116, 101, 114]"},{"name":"PRESET_PARAMETER2","type":"bytes","value":"[112, 114, 101, 115, 101, 116, 95, 112, 97, 114, 97, 109, 101, 116, 101, 114, 50]"},{"name":"PROTOCOL_SHARE","type":"u16","value":"500"}]} \ No newline at end of file diff --git a/idls/meteora_pools_amm.Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB.json b/idls/meteora_pools_amm.Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB.json new file mode 100644 index 0000000..3c1bd2d --- /dev/null +++ b/idls/meteora_pools_amm.Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB.json @@ -0,0 +1 @@ +{"version":"0.5.2","name":"amm","docs":["Program for AMM"],"instructions":[{"name":"initializePermissionedPool","docs":["Initialize a new permissioned pool."],"accounts":[{"name":"pool","isMut":true,"isSigner":true,"docs":["Pool account (arbitrary address)"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"tokenAMint","isMut":false,"isSigner":false,"docs":["Token A mint of the pool. Eg: USDT"]},{"name":"tokenBMint","isMut":false,"isSigner":false,"docs":["Token B mint of the pool. Eg: USDC"]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token A. Token A of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token B. Token B of the pool will be deposit / withdraw from this vault account."]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault A"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault B"]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn vault LP upon deposit/withdraw from the vault."]},{"name":"adminTokenA","isMut":true,"isSigner":false,"docs":["Admin token account for pool token A mint. Used to bootstrap the pool with initial liquidity."]},{"name":"adminTokenB","isMut":true,"isSigner":false,"docs":["Admin token account for pool token B mint. Used to bootstrap the pool with initial liquidity."]},{"name":"adminPoolLp","isMut":true,"isSigner":false,"docs":["Admin pool LP token account. Used to receive LP during first deposit (initialize pool)","Admin pool LP token account. Used to receive LP during first deposit (initialize pool)"]},{"name":"protocolTokenAFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token A. Used to receive trading fee."]},{"name":"protocolTokenBFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token B. Used to receive trading fee."]},{"name":"admin","isMut":true,"isSigner":true,"docs":["Admin account. This account will be the admin of the pool, and the payer for PDA during initialize pool."]},{"name":"feeOwner","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false,"docs":["Rent account."]},{"name":"mintMetadata","isMut":true,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. The pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]},{"name":"associatedTokenProgram","isMut":false,"isSigner":false,"docs":["Associated token program."]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program."]}],"args":[{"name":"curveType","type":{"defined":"CurveType"}}]},{"name":"initializePermissionlessPool","docs":["Initialize a new permissionless pool."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA address)"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"tokenAMint","isMut":false,"isSigner":false,"docs":["Token A mint of the pool. Eg: USDT"]},{"name":"tokenBMint","isMut":false,"isSigner":false,"docs":["Token B mint of the pool. Eg: USDC"]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token A. Token A of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token B. Token B of the pool will be deposit / withdraw from this vault account."]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault A"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault B"]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn vault LP upon deposit/withdraw from the vault."]},{"name":"payerTokenA","isMut":true,"isSigner":false,"docs":["Payer token account for pool token A mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerTokenB","isMut":true,"isSigner":false,"docs":["Admin token account for pool token B mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerPoolLp","isMut":true,"isSigner":false},{"name":"protocolTokenAFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token A. Used to receive trading fee."]},{"name":"protocolTokenBFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token B. Used to receive trading fee."]},{"name":"payer","isMut":true,"isSigner":true,"docs":["Admin account. This account will be the admin of the pool, and the payer for PDA during initialize pool."]},{"name":"feeOwner","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false,"docs":["Rent account."]},{"name":"mintMetadata","isMut":true,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. The pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]},{"name":"associatedTokenProgram","isMut":false,"isSigner":false,"docs":["Associated token program."]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program."]}],"args":[{"name":"curveType","type":{"defined":"CurveType"}},{"name":"tokenAAmount","type":"u64"},{"name":"tokenBAmount","type":"u64"}]},{"name":"initializePermissionlessPoolWithFeeTier","docs":["Initialize a new permissionless pool with customized fee tier"],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA address)"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"tokenAMint","isMut":false,"isSigner":false,"docs":["Token A mint of the pool. Eg: USDT"]},{"name":"tokenBMint","isMut":false,"isSigner":false,"docs":["Token B mint of the pool. Eg: USDC"]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token A. Token A of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token B. Token B of the pool will be deposit / withdraw from this vault account."]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault A"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault B"]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn vault LP upon deposit/withdraw from the vault."]},{"name":"payerTokenA","isMut":true,"isSigner":false,"docs":["Payer token account for pool token A mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerTokenB","isMut":true,"isSigner":false,"docs":["Admin token account for pool token B mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerPoolLp","isMut":true,"isSigner":false},{"name":"protocolTokenAFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token A. Used to receive trading fee."]},{"name":"protocolTokenBFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token B. Used to receive trading fee."]},{"name":"payer","isMut":true,"isSigner":true,"docs":["Admin account. This account will be the admin of the pool, and the payer for PDA during initialize pool."]},{"name":"feeOwner","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false,"docs":["Rent account."]},{"name":"mintMetadata","isMut":true,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. The pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]},{"name":"associatedTokenProgram","isMut":false,"isSigner":false,"docs":["Associated token program."]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program."]}],"args":[{"name":"curveType","type":{"defined":"CurveType"}},{"name":"tradeFeeBps","type":"u64"},{"name":"tokenAAmount","type":"u64"},{"name":"tokenBAmount","type":"u64"}]},{"name":"enableOrDisablePool","docs":["Enable or disable a pool. A disabled pool allow only remove balanced liquidity operation."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"admin","isMut":false,"isSigner":true,"docs":["Admin account. Must be owner of the pool."]}],"args":[{"name":"enable","type":"bool"}]},{"name":"swap","docs":["Swap token A to B, or vice versa. An amount of trading fee will be charged for liquidity provider, and the admin of the pool."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"userSourceToken","isMut":true,"isSigner":false,"docs":["User token account. Token from this account will be transfer into the vault by the pool in exchange for another token of the pool."]},{"name":"userDestinationToken","isMut":true,"isSigner":false,"docs":["User token account. The exchanged token will be transfer into this account from the pool."]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token a. token a of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token b. token b of the pool will be deposit / withdraw from this vault account."]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["Lp token mint of vault a"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["Lp token mint of vault b"]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"protocolTokenFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account. Used to receive trading fee. It's mint field must matched with user_source_token mint field."]},{"name":"user","isMut":false,"isSigner":true,"docs":["User account. Must be owner of user_source_token."]},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. the pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]}],"args":[{"name":"inAmount","type":"u64"},{"name":"minimumOutAmount","type":"u64"}]},{"name":"removeLiquiditySingleSide","docs":["Withdraw only single token from the pool. Only supported by pool with stable swap curve."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"userPoolLp","isMut":true,"isSigner":false,"docs":["User pool lp token account. LP will be burned from this account upon success liquidity removal."]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token A. Token A of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token B. Token B of the pool will be deposit / withdraw from this vault account."]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault A"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault B"]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"userDestinationToken","isMut":true,"isSigner":false,"docs":["User token account to receive token upon success liquidity removal."]},{"name":"user","isMut":false,"isSigner":true,"docs":["User account. Must be owner of the user_pool_lp account."]},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. The pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]}],"args":[{"name":"poolTokenAmount","type":"u64"},{"name":"minimumOutAmount","type":"u64"}]},{"name":"addImbalanceLiquidity","docs":["Deposit tokens to the pool in an imbalance ratio. Only supported by pool with stable swap curve."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"userPoolLp","isMut":true,"isSigner":false,"docs":["user pool lp token account. lp will be burned from this account upon success liquidity removal."]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token a. token a of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token b. token b of the pool will be deposit / withdraw from this vault account."]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault a"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault b"]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"userAToken","isMut":true,"isSigner":false,"docs":["User token A account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"userBToken","isMut":true,"isSigner":false,"docs":["User token B account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"user","isMut":false,"isSigner":true,"docs":["User account. Must be owner of user_a_token, and user_b_token."]},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. the pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]}],"args":[{"name":"minimumPoolTokenAmount","type":"u64"},{"name":"tokenAAmount","type":"u64"},{"name":"tokenBAmount","type":"u64"}]},{"name":"removeBalanceLiquidity","docs":["Withdraw tokens from the pool in a balanced ratio. User will still able to withdraw from pool even the pool is disabled. This allow user to exit their liquidity when there's some unforeseen event happen."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"userPoolLp","isMut":true,"isSigner":false,"docs":["user pool lp token account. lp will be burned from this account upon success liquidity removal."]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token a. token a of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token b. token b of the pool will be deposit / withdraw from this vault account."]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault a"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault b"]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"userAToken","isMut":true,"isSigner":false,"docs":["User token A account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"userBToken","isMut":true,"isSigner":false,"docs":["User token B account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"user","isMut":false,"isSigner":true,"docs":["User account. Must be owner of user_a_token, and user_b_token."]},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. the pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]}],"args":[{"name":"poolTokenAmount","type":"u64"},{"name":"minimumATokenOut","type":"u64"},{"name":"minimumBTokenOut","type":"u64"}]},{"name":"addBalanceLiquidity","docs":["Deposit tokens to the pool in a balanced ratio."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"userPoolLp","isMut":true,"isSigner":false,"docs":["user pool lp token account. lp will be burned from this account upon success liquidity removal."]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token a. token a of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token b. token b of the pool will be deposit / withdraw from this vault account."]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault a"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault b"]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"userAToken","isMut":true,"isSigner":false,"docs":["User token A account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"userBToken","isMut":true,"isSigner":false,"docs":["User token B account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"user","isMut":false,"isSigner":true,"docs":["User account. Must be owner of user_a_token, and user_b_token."]},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. the pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]}],"args":[{"name":"poolTokenAmount","type":"u64"},{"name":"maximumTokenAAmount","type":"u64"},{"name":"maximumTokenBAmount","type":"u64"}]},{"name":"setPoolFees","docs":["Update trading fee charged for liquidity provider, and admin."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"feeOperator","isMut":false,"isSigner":true,"docs":["Fee operator account"]}],"args":[{"name":"fees","type":{"defined":"PoolFees"}},{"name":"newPartnerFeeNumerator","type":"u64"}]},{"name":"overrideCurveParam","docs":["Update swap curve parameters. This function do not allow update of curve type. For example: stable swap curve to constant product curve. Only supported by pool with stable swap curve.","Only amp is allowed to be override. The other attributes of stable swap curve will be ignored."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"admin","isMut":false,"isSigner":true,"docs":["Admin account."]}],"args":[{"name":"curveType","type":{"defined":"CurveType"}}]},{"name":"getPoolInfo","docs":["Get the general information of the pool."],"accounts":[{"name":"pool","isMut":false,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"lpMint","isMut":false,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"aVaultLp","isMut":false,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":false,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"aVault","isMut":false,"isSigner":false,"docs":["Vault account for token a. token a of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":false,"isSigner":false,"docs":["Vault account for token b. token b of the pool will be deposit / withdraw from this vault account."]},{"name":"aVaultLpMint","isMut":false,"isSigner":false,"docs":["LP token mint of vault a"]},{"name":"bVaultLpMint","isMut":false,"isSigner":false,"docs":["LP token mint of vault b"]}],"args":[]},{"name":"bootstrapLiquidity","docs":["Bootstrap the pool when liquidity is depleted."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"userPoolLp","isMut":true,"isSigner":false,"docs":["user pool lp token account. lp will be burned from this account upon success liquidity removal."]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token a. token a of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token b. token b of the pool will be deposit / withdraw from this vault account."]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault a"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault b"]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"userAToken","isMut":true,"isSigner":false,"docs":["User token A account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"userBToken","isMut":true,"isSigner":false,"docs":["User token B account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"user","isMut":false,"isSigner":true,"docs":["User account. Must be owner of user_a_token, and user_b_token."]},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. the pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]}],"args":[{"name":"tokenAAmount","type":"u64"},{"name":"tokenBAmount","type":"u64"}]},{"name":"createMintMetadata","docs":["Create mint metadata account for old pools"],"accounts":[{"name":"pool","isMut":false,"isSigner":false,"docs":["Pool account"]},{"name":"lpMint","isMut":false,"isSigner":false,"docs":["LP mint account of the pool"]},{"name":"aVaultLp","isMut":false,"isSigner":false,"docs":["Vault A LP account of the pool"]},{"name":"mintMetadata","isMut":true,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program."]},{"name":"payer","isMut":true,"isSigner":true,"docs":["Payer"]}],"args":[]},{"name":"createLockEscrow","docs":["Create lock account"],"accounts":[{"name":"pool","isMut":false,"isSigner":false,"docs":["Pool account"]},{"name":"lockEscrow","isMut":true,"isSigner":false,"docs":["Lock account"]},{"name":"owner","isMut":false,"isSigner":false},{"name":"lpMint","isMut":false,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"payer","isMut":true,"isSigner":true,"docs":["Payer account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program."]}],"args":[]},{"name":"lock","docs":["Lock Lp token"],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account"]},{"name":"lpMint","isMut":false,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"lockEscrow","isMut":true,"isSigner":false,"docs":["Lock account"]},{"name":"owner","isMut":true,"isSigner":true,"docs":["Can be anyone"]},{"name":"sourceTokens","isMut":true,"isSigner":false,"docs":["owner lp token account"]},{"name":"escrowVault","isMut":true,"isSigner":false,"docs":["Escrow vault"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]},{"name":"aVault","isMut":false,"isSigner":false,"docs":["Vault account for token a. token a of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":false,"isSigner":false,"docs":["Vault account for token b. token b of the pool will be deposit / withdraw from this vault account."]},{"name":"aVaultLp","isMut":false,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":false,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"aVaultLpMint","isMut":false,"isSigner":false,"docs":["LP token mint of vault a"]},{"name":"bVaultLpMint","isMut":false,"isSigner":false,"docs":["LP token mint of vault b"]}],"args":[{"name":"maxAmount","type":"u64"}]},{"name":"claimFee","docs":["Claim fee"],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"lockEscrow","isMut":true,"isSigner":false,"docs":["Lock account"]},{"name":"owner","isMut":true,"isSigner":true,"docs":["Owner of lock account"]},{"name":"sourceTokens","isMut":true,"isSigner":false,"docs":["owner lp token account"]},{"name":"escrowVault","isMut":true,"isSigner":false,"docs":["Escrow vault"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token a. token a of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token b. token b of the pool will be deposit / withdraw from this vault account."]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault a"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault b"]},{"name":"userAToken","isMut":true,"isSigner":false,"docs":["User token A account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"userBToken","isMut":true,"isSigner":false,"docs":["User token B account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. the pool will deposit/withdraw liquidity from the vault."]}],"args":[{"name":"maxAmount","type":"u64"}]},{"name":"createConfig","docs":["Create config"],"accounts":[{"name":"config","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"configParameters","type":{"defined":"ConfigParameters"}}]},{"name":"closeConfig","docs":["Close config"],"accounts":[{"name":"config","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"rentReceiver","isMut":true,"isSigner":false}],"args":[]},{"name":"initializePermissionlessConstantProductPoolWithConfig","docs":["Initialize permissionless pool with config"],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA address)"]},{"name":"config","isMut":false,"isSigner":false},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"tokenAMint","isMut":false,"isSigner":false,"docs":["Token A mint of the pool. Eg: USDT"]},{"name":"tokenBMint","isMut":false,"isSigner":false,"docs":["Token B mint of the pool. Eg: USDC"]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token A. Token A of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token B. Token B of the pool will be deposit / withdraw from this vault account."]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault A"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault B"]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn vault LP upon deposit/withdraw from the vault."]},{"name":"payerTokenA","isMut":true,"isSigner":false,"docs":["Payer token account for pool token A mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerTokenB","isMut":true,"isSigner":false,"docs":["Admin token account for pool token B mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerPoolLp","isMut":true,"isSigner":false},{"name":"protocolTokenAFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token A. Used to receive trading fee."]},{"name":"protocolTokenBFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token B. Used to receive trading fee."]},{"name":"payer","isMut":true,"isSigner":true,"docs":["Admin account. This account will be the admin of the pool, and the payer for PDA during initialize pool."]},{"name":"rent","isMut":false,"isSigner":false,"docs":["Rent account."]},{"name":"mintMetadata","isMut":true,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. The pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]},{"name":"associatedTokenProgram","isMut":false,"isSigner":false,"docs":["Associated token program."]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program."]}],"args":[{"name":"tokenAAmount","type":"u64"},{"name":"tokenBAmount","type":"u64"}]},{"name":"initializePermissionlessConstantProductPoolWithConfig2","docs":["Initialize permissionless pool with config 2"],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA address)"]},{"name":"config","isMut":false,"isSigner":false},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"tokenAMint","isMut":false,"isSigner":false,"docs":["Token A mint of the pool. Eg: USDT"]},{"name":"tokenBMint","isMut":false,"isSigner":false,"docs":["Token B mint of the pool. Eg: USDC"]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token A. Token A of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token B. Token B of the pool will be deposit / withdraw from this vault account."]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault A"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault B"]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn vault LP upon deposit/withdraw from the vault."]},{"name":"payerTokenA","isMut":true,"isSigner":false,"docs":["Payer token account for pool token A mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerTokenB","isMut":true,"isSigner":false,"docs":["Admin token account for pool token B mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerPoolLp","isMut":true,"isSigner":false},{"name":"protocolTokenAFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token A. Used to receive trading fee."]},{"name":"protocolTokenBFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token B. Used to receive trading fee."]},{"name":"payer","isMut":true,"isSigner":true,"docs":["Admin account. This account will be the admin of the pool, and the payer for PDA during initialize pool."]},{"name":"rent","isMut":false,"isSigner":false,"docs":["Rent account."]},{"name":"mintMetadata","isMut":true,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. The pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]},{"name":"associatedTokenProgram","isMut":false,"isSigner":false,"docs":["Associated token program."]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program."]}],"args":[{"name":"tokenAAmount","type":"u64"},{"name":"tokenBAmount","type":"u64"},{"name":"activationPoint","type":{"option":"u64"}}]},{"name":"initializeCustomizablePermissionlessConstantProductPool","docs":["Initialize permissionless pool with customizable params"],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA address)"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"tokenAMint","isMut":false,"isSigner":false,"docs":["Token A mint of the pool. Eg: USDT"]},{"name":"tokenBMint","isMut":false,"isSigner":false,"docs":["Token B mint of the pool. Eg: USDC"]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token A. Token A of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token B. Token B of the pool will be deposit / withdraw from this vault account."]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault A"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault B"]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn vault LP upon deposit/withdraw from the vault."]},{"name":"payerTokenA","isMut":true,"isSigner":false,"docs":["Payer token account for pool token A mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerTokenB","isMut":true,"isSigner":false,"docs":["Admin token account for pool token B mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerPoolLp","isMut":true,"isSigner":false},{"name":"protocolTokenAFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token A. Used to receive trading fee."]},{"name":"protocolTokenBFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token B. Used to receive trading fee."]},{"name":"payer","isMut":true,"isSigner":true,"docs":["Admin account. This account will be the admin of the pool, and the payer for PDA during initialize pool."]},{"name":"rent","isMut":false,"isSigner":false,"docs":["Rent account."]},{"name":"mintMetadata","isMut":true,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. The pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]},{"name":"associatedTokenProgram","isMut":false,"isSigner":false,"docs":["Associated token program."]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program."]}],"args":[{"name":"tokenAAmount","type":"u64"},{"name":"tokenBAmount","type":"u64"},{"name":"params","type":{"defined":"CustomizableParams"}}]},{"name":"updateActivationPoint","docs":["Update activation slot"],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"admin","isMut":false,"isSigner":true,"docs":["Admin account."]}],"args":[{"name":"newActivationPoint","type":"u64"}]},{"name":"withdrawProtocolFees","docs":["Withdraw protocol fee"],"accounts":[{"name":"pool","isMut":false,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"aVaultLp","isMut":false,"isSigner":false},{"name":"protocolTokenAFee","isMut":true,"isSigner":false},{"name":"protocolTokenBFee","isMut":true,"isSigner":false},{"name":"treasuryTokenA","isMut":true,"isSigner":false},{"name":"treasuryTokenB","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"setWhitelistedVault","docs":["Set whitelisted vault"],"accounts":[{"name":"pool","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"whitelistedVault","type":"publicKey"}]},{"name":"partnerClaimFee","docs":["Partner claim fee"],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"aVaultLp","isMut":false,"isSigner":false},{"name":"protocolTokenAFee","isMut":true,"isSigner":false},{"name":"protocolTokenBFee","isMut":true,"isSigner":false},{"name":"partnerTokenA","isMut":true,"isSigner":false},{"name":"partnerTokenB","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"partnerAuthority","isMut":false,"isSigner":true}],"args":[{"name":"maxAmountA","type":"u64"},{"name":"maxAmountB","type":"u64"}]}],"accounts":[{"name":"Config","type":{"kind":"struct","fields":[{"name":"poolFees","type":{"defined":"PoolFees"}},{"name":"activationDuration","type":"u64"},{"name":"vaultConfigKey","type":"publicKey"},{"name":"poolCreatorAuthority","docs":["Only pool_creator_authority can use the current config to initialize new pool. When it's Pubkey::default, it's a public config."],"type":"publicKey"},{"name":"activationType","docs":["Activation type"],"type":"u8"},{"name":"partnerFeeNumerator","type":"u64"},{"name":"padding","type":{"array":["u8",219]}}]}},{"name":"LockEscrow","docs":["State of lock escrow account"],"type":{"kind":"struct","fields":[{"name":"pool","docs":["Pool address"],"type":"publicKey"},{"name":"owner","docs":["Owner address"],"type":"publicKey"},{"name":"escrowVault","docs":["Vault address, store the lock user lock"],"type":"publicKey"},{"name":"bump","docs":["bump, used to sign"],"type":"u8"},{"name":"totalLockedAmount","docs":["Total locked amount"],"type":"u64"},{"name":"lpPerToken","docs":["Lp per token, virtual price of lp token"],"type":"u128"},{"name":"unclaimedFeePending","docs":["Unclaimed fee pending"],"type":"u64"},{"name":"aFee","docs":["Total a fee claimed so far"],"type":"u64"},{"name":"bFee","docs":["Total b fee claimed so far"],"type":"u64"}]}},{"name":"Pool","docs":["State of pool account"],"type":{"kind":"struct","fields":[{"name":"lpMint","docs":["LP token mint of the pool"],"type":"publicKey"},{"name":"tokenAMint","docs":["Token A mint of the pool. Eg: USDT"],"type":"publicKey"},{"name":"tokenBMint","docs":["Token B mint of the pool. Eg: USDC"],"type":"publicKey"},{"name":"aVault","docs":["Vault account for token A. Token A of the pool will be deposit / withdraw from this vault account."],"type":"publicKey"},{"name":"bVault","docs":["Vault account for token B. Token B of the pool will be deposit / withdraw from this vault account."],"type":"publicKey"},{"name":"aVaultLp","docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."],"type":"publicKey"},{"name":"bVaultLp","docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."],"type":"publicKey"},{"name":"aVaultLpBump","docs":["\"A\" vault lp bump. Used to create signer seeds."],"type":"u8"},{"name":"enabled","docs":["Flag to determine whether the pool is enabled, or disabled."],"type":"bool"},{"name":"protocolTokenAFee","docs":["Protocol fee token account for token A. Used to receive trading fee."],"type":"publicKey"},{"name":"protocolTokenBFee","docs":["Protocol fee token account for token B. Used to receive trading fee."],"type":"publicKey"},{"name":"feeLastUpdatedAt","docs":["Fee last updated timestamp"],"type":"u64"},{"name":"padding0","type":{"array":["u8",24]}},{"name":"fees","docs":["Store the fee charges setting."],"type":{"defined":"PoolFees"}},{"name":"poolType","docs":["Pool type"],"type":{"defined":"PoolType"}},{"name":"stake","docs":["Stake pubkey of SPL stake pool"],"type":"publicKey"},{"name":"totalLockedLp","docs":["Total locked lp token"],"type":"u64"},{"name":"bootstrapping","docs":["bootstrapping config"],"type":{"defined":"Bootstrapping"}},{"name":"partnerInfo","type":{"defined":"PartnerInfo"}},{"name":"padding","docs":["Padding for future pool field"],"type":{"defined":"Padding"}},{"name":"curveType","docs":["The type of the swap curve supported by the pool."],"type":{"defined":"CurveType"}}]}}],"types":[{"name":"TokenMultiplier","docs":["Multiplier for the pool token. Used to normalized token with different decimal into the same precision."],"type":{"kind":"struct","fields":[{"name":"tokenAMultiplier","docs":["Multiplier for token A of the pool."],"type":"u64"},{"name":"tokenBMultiplier","docs":["Multiplier for token B of the pool."],"type":"u64"},{"name":"precisionFactor","docs":["Record the highest token decimal in the pool. For example, Token A is 6 decimal, token B is 9 decimal. This will save value of 9."],"type":"u8"}]}},{"name":"PoolFees","docs":["Information regarding fee charges"],"type":{"kind":"struct","fields":[{"name":"tradeFeeNumerator","docs":["Trade fees are extra token amounts that are held inside the token","accounts during a trade, making the value of liquidity tokens rise.","Trade fee numerator"],"type":"u64"},{"name":"tradeFeeDenominator","docs":["Trade fee denominator"],"type":"u64"},{"name":"protocolTradeFeeNumerator","docs":["Protocol trading fees are extra token amounts that are held inside the token","accounts during a trade, with the equivalent in pool tokens minted to","the protocol of the program.","Protocol trade fee numerator"],"type":"u64"},{"name":"protocolTradeFeeDenominator","docs":["Protocol trade fee denominator"],"type":"u64"}]}},{"name":"Depeg","docs":["Contains information for depeg pool"],"type":{"kind":"struct","fields":[{"name":"baseVirtualPrice","docs":["The virtual price of staking / interest bearing token"],"type":"u64"},{"name":"baseCacheUpdated","docs":["The last time base_virtual_price is updated"],"type":"u64"},{"name":"depegType","docs":["Type of the depeg pool"],"type":{"defined":"DepegType"}}]}},{"name":"ConfigParameters","type":{"kind":"struct","fields":[{"name":"tradeFeeNumerator","type":"u64"},{"name":"protocolTradeFeeNumerator","type":"u64"},{"name":"activationDuration","type":"u64"},{"name":"vaultConfigKey","type":"publicKey"},{"name":"poolCreatorAuthority","type":"publicKey"},{"name":"activationType","type":"u8"},{"name":"index","type":"u64"},{"name":"partnerFeeNumerator","type":"u64"}]}},{"name":"CustomizableParams","type":{"kind":"struct","fields":[{"name":"tradeFeeNumerator","docs":["Trading fee."],"type":"u32"},{"name":"activationPoint","docs":["The pool start trading."],"type":{"option":"u64"}},{"name":"hasAlphaVault","docs":["Whether the pool support alpha vault"],"type":"bool"},{"name":"activationType","docs":["Activation type"],"type":"u8"},{"name":"padding","docs":["Padding"],"type":{"array":["u8",90]}}]}},{"name":"Padding","docs":["Padding for future pool fields"],"type":{"kind":"struct","fields":[{"name":"padding0","docs":["Padding 0"],"type":{"array":["u8",6]}},{"name":"padding1","docs":["Padding 1"],"type":{"array":["u64",21]}},{"name":"padding2","docs":["Padding 2"],"type":{"array":["u64",21]}}]}},{"name":"PartnerInfo","type":{"kind":"struct","fields":[{"name":"feeNumerator","type":"u64"},{"name":"partnerAuthority","type":"publicKey"},{"name":"pendingFeeA","type":"u64"},{"name":"pendingFeeB","type":"u64"}]}},{"name":"Bootstrapping","type":{"kind":"struct","fields":[{"name":"activationPoint","docs":["Activation point, can be slot or timestamp"],"type":"u64"},{"name":"whitelistedVault","docs":["Whitelisted vault to be able to buy pool before activation_point"],"type":"publicKey"},{"name":"poolCreator","docs":["Need to store pool creator in lauch pool, so they can modify liquidity before activation_point"],"type":"publicKey"},{"name":"activationType","docs":["Activation type, 0 means by slot, 1 means by timestamp"],"type":"u8"}]}},{"name":"ActivationType","docs":["Type of the activation"],"type":{"kind":"enum","variants":[{"name":"Slot"},{"name":"Timestamp"}]}},{"name":"RoundDirection","docs":["Rounding direction"],"type":{"kind":"enum","variants":[{"name":"Floor"},{"name":"Ceiling"}]}},{"name":"TradeDirection","docs":["Trade (swap) direction"],"type":{"kind":"enum","variants":[{"name":"AtoB"},{"name":"BtoA"}]}},{"name":"NewCurveType","docs":["Type of the swap curve"],"type":{"kind":"enum","variants":[{"name":"ConstantProduct"},{"name":"Stable","fields":[{"name":"amp","docs":["Amplification coefficient"],"type":"u64"},{"name":"token_multiplier","docs":["Multiplier for the pool token. Used to normalized token with different decimal into the same precision."],"type":{"defined":"TokenMultiplier"}},{"name":"depeg","docs":["Depeg pool information. Contains functions to allow token amount to be repeg using stake / interest bearing token virtual price"],"type":{"defined":"Depeg"}},{"name":"last_amp_updated_timestamp","docs":["The last amp updated timestamp. Used to prevent update_curve_info called infinitely many times within a short period"],"type":"u64"}]},{"name":"NewCurve","fields":[{"name":"field_one","type":"u64"},{"name":"field_two","type":"u64"}]}]}},{"name":"CurveType","docs":["Type of the swap curve"],"type":{"kind":"enum","variants":[{"name":"ConstantProduct"},{"name":"Stable","fields":[{"name":"amp","docs":["Amplification coefficient"],"type":"u64"},{"name":"token_multiplier","docs":["Multiplier for the pool token. Used to normalized token with different decimal into the same precision."],"type":{"defined":"TokenMultiplier"}},{"name":"depeg","docs":["Depeg pool information. Contains functions to allow token amount to be repeg using stake / interest bearing token virtual price"],"type":{"defined":"Depeg"}},{"name":"last_amp_updated_timestamp","docs":["The last amp updated timestamp. Used to prevent update_curve_info called infinitely many times within a short period"],"type":"u64"}]}]}},{"name":"DepegType","docs":["Type of depeg pool"],"type":{"kind":"enum","variants":[{"name":"None"},{"name":"Marinade"},{"name":"Lido"},{"name":"SplStake"}]}},{"name":"Rounding","docs":["Round up, down"],"type":{"kind":"enum","variants":[{"name":"Up"},{"name":"Down"}]}},{"name":"PoolType","docs":["Pool type"],"type":{"kind":"enum","variants":[{"name":"Permissioned"},{"name":"Permissionless"}]}}],"events":[{"name":"AddLiquidity","fields":[{"name":"lpMintAmount","type":"u64","index":false},{"name":"tokenAAmount","type":"u64","index":false},{"name":"tokenBAmount","type":"u64","index":false}]},{"name":"RemoveLiquidity","fields":[{"name":"lpUnmintAmount","type":"u64","index":false},{"name":"tokenAOutAmount","type":"u64","index":false},{"name":"tokenBOutAmount","type":"u64","index":false}]},{"name":"BootstrapLiquidity","fields":[{"name":"lpMintAmount","type":"u64","index":false},{"name":"tokenAAmount","type":"u64","index":false},{"name":"tokenBAmount","type":"u64","index":false},{"name":"pool","type":"publicKey","index":false}]},{"name":"Swap","fields":[{"name":"inAmount","type":"u64","index":false},{"name":"outAmount","type":"u64","index":false},{"name":"tradeFee","type":"u64","index":false},{"name":"protocolFee","type":"u64","index":false},{"name":"hostFee","type":"u64","index":false}]},{"name":"SetPoolFees","fields":[{"name":"tradeFeeNumerator","type":"u64","index":false},{"name":"tradeFeeDenominator","type":"u64","index":false},{"name":"protocolTradeFeeNumerator","type":"u64","index":false},{"name":"protocolTradeFeeDenominator","type":"u64","index":false},{"name":"pool","type":"publicKey","index":false}]},{"name":"PoolInfo","fields":[{"name":"tokenAAmount","type":"u64","index":false},{"name":"tokenBAmount","type":"u64","index":false},{"name":"virtualPrice","type":"f64","index":false},{"name":"currentTimestamp","type":"u64","index":false}]},{"name":"TransferAdmin","fields":[{"name":"admin","type":"publicKey","index":false},{"name":"newAdmin","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false}]},{"name":"OverrideCurveParam","fields":[{"name":"newAmp","type":"u64","index":false},{"name":"updatedTimestamp","type":"u64","index":false},{"name":"pool","type":"publicKey","index":false}]},{"name":"PoolCreated","fields":[{"name":"lpMint","type":"publicKey","index":false},{"name":"tokenAMint","type":"publicKey","index":false},{"name":"tokenBMint","type":"publicKey","index":false},{"name":"poolType","type":{"defined":"PoolType"},"index":false},{"name":"pool","type":"publicKey","index":false}]},{"name":"PoolEnabled","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"enabled","type":"bool","index":false}]},{"name":"MigrateFeeAccount","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"newAdminTokenAFee","type":"publicKey","index":false},{"name":"newAdminTokenBFee","type":"publicKey","index":false},{"name":"tokenAAmount","type":"u64","index":false},{"name":"tokenBAmount","type":"u64","index":false}]},{"name":"CreateLockEscrow","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"owner","type":"publicKey","index":false}]},{"name":"Lock","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]},{"name":"ClaimFee","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"aFee","type":"u64","index":false},{"name":"bFee","type":"u64","index":false}]},{"name":"CreateConfig","fields":[{"name":"tradeFeeNumerator","type":"u64","index":false},{"name":"protocolTradeFeeNumerator","type":"u64","index":false},{"name":"config","type":"publicKey","index":false}]},{"name":"CloseConfig","fields":[{"name":"config","type":"publicKey","index":false}]},{"name":"WithdrawProtocolFees","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"protocolAFee","type":"u64","index":false},{"name":"protocolBFee","type":"u64","index":false},{"name":"protocolAFeeOwner","type":"publicKey","index":false},{"name":"protocolBFeeOwner","type":"publicKey","index":false}]},{"name":"PartnerClaimFees","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"feeA","type":"u64","index":false},{"name":"feeB","type":"u64","index":false},{"name":"partner","type":"publicKey","index":false}]}],"errors":[{"code":6000,"name":"MathOverflow","msg":"Math operation overflow"},{"code":6001,"name":"InvalidFee","msg":"Invalid fee setup"},{"code":6002,"name":"InvalidInvariant","msg":"Invalid invariant d"},{"code":6003,"name":"FeeCalculationFailure","msg":"Fee calculation failure"},{"code":6004,"name":"ExceededSlippage","msg":"Exceeded slippage tolerance"},{"code":6005,"name":"InvalidCalculation","msg":"Invalid curve calculation"},{"code":6006,"name":"ZeroTradingTokens","msg":"Given pool token amount results in zero trading tokens"},{"code":6007,"name":"ConversionError","msg":"Math conversion overflow"},{"code":6008,"name":"FaultyLpMint","msg":"LP mint authority must be 'A' vault lp, without freeze authority, and 0 supply"},{"code":6009,"name":"MismatchedTokenMint","msg":"Token mint mismatched"},{"code":6010,"name":"MismatchedLpMint","msg":"LP mint mismatched"},{"code":6011,"name":"MismatchedOwner","msg":"Invalid lp token owner"},{"code":6012,"name":"InvalidVaultAccount","msg":"Invalid vault account"},{"code":6013,"name":"InvalidVaultLpAccount","msg":"Invalid vault lp account"},{"code":6014,"name":"InvalidPoolLpMintAccount","msg":"Invalid pool lp mint account"},{"code":6015,"name":"PoolDisabled","msg":"Pool disabled"},{"code":6016,"name":"InvalidAdminAccount","msg":"Invalid admin account"},{"code":6017,"name":"InvalidProtocolFeeAccount","msg":"Invalid protocol fee account"},{"code":6018,"name":"SameAdminAccount","msg":"Same admin account"},{"code":6019,"name":"IdenticalSourceDestination","msg":"Identical user source and destination token account"},{"code":6020,"name":"ApyCalculationError","msg":"Apy calculation error"},{"code":6021,"name":"InsufficientSnapshot","msg":"Insufficient virtual price snapshot"},{"code":6022,"name":"NonUpdatableCurve","msg":"Current curve is non-updatable"},{"code":6023,"name":"MisMatchedCurve","msg":"New curve is mismatched with old curve"},{"code":6024,"name":"InvalidAmplification","msg":"Amplification is invalid"},{"code":6025,"name":"UnsupportedOperation","msg":"Operation is not supported"},{"code":6026,"name":"ExceedMaxAChanges","msg":"Exceed max amplification changes"},{"code":6027,"name":"InvalidRemainingAccountsLen","msg":"Invalid remaining accounts length"},{"code":6028,"name":"InvalidRemainingAccounts","msg":"Invalid remaining account"},{"code":6029,"name":"MismatchedDepegMint","msg":"Token mint B doesn't matches depeg type token mint"},{"code":6030,"name":"InvalidApyAccount","msg":"Invalid APY account"},{"code":6031,"name":"InvalidTokenMultiplier","msg":"Invalid token multiplier"},{"code":6032,"name":"InvalidDepegInformation","msg":"Invalid depeg information"},{"code":6033,"name":"UpdateTimeConstraint","msg":"Update time constraint violated"},{"code":6034,"name":"ExceedMaxFeeBps","msg":"Exceeded max fee bps"},{"code":6035,"name":"InvalidAdmin","msg":"Invalid admin"},{"code":6036,"name":"PoolIsNotPermissioned","msg":"Pool is not permissioned"},{"code":6037,"name":"InvalidDepositAmount","msg":"Invalid deposit amount"},{"code":6038,"name":"InvalidFeeOwner","msg":"Invalid fee owner"},{"code":6039,"name":"NonDepletedPool","msg":"Pool is not depleted"},{"code":6040,"name":"AmountNotPeg","msg":"Token amount is not 1:1"},{"code":6041,"name":"AmountIsZero","msg":"Amount is zero"},{"code":6042,"name":"TypeCastFailed","msg":"Type cast error"},{"code":6043,"name":"AmountIsNotEnough","msg":"Amount is not enough"},{"code":6044,"name":"InvalidActivationDuration","msg":"Invalid activation duration"},{"code":6045,"name":"PoolIsNotLaunchPool","msg":"Pool is not launch pool"},{"code":6046,"name":"UnableToModifyActivationPoint","msg":"Unable to modify activation point"},{"code":6047,"name":"InvalidAuthorityToCreateThePool","msg":"Invalid authority to create the pool"},{"code":6048,"name":"InvalidActivationType","msg":"Invalid activation type"},{"code":6049,"name":"InvalidActivationPoint","msg":"Invalid activation point"},{"code":6050,"name":"PreActivationSwapStarted","msg":"Pre activation swap window started"},{"code":6051,"name":"InvalidPoolType","msg":"Invalid pool type"},{"code":6052,"name":"InvalidQuoteMint","msg":"Quote token must be SOL,USDC"}]} \ No newline at end of file diff --git a/idls/meteora_vault.24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi.json b/idls/meteora_vault.24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi.json new file mode 100644 index 0000000..61034fa --- /dev/null +++ b/idls/meteora_vault.24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi.json @@ -0,0 +1 @@ +{"version":"0.7.2","name":"vault","docs":["Program for vault"],"instructions":[{"name":"initialize","docs":["initialize new vault"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["This is base account for all vault","No need base key now because we only allow 1 vault per token now","Vault account"]},{"name":"payer","isMut":true,"isSigner":true,"docs":["Payer can be anyone"]},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["Token vault account"]},{"name":"tokenMint","isMut":false,"isSigner":false,"docs":["Token mint account"]},{"name":"lpMint","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false,"docs":["rent"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["system_program"]}],"args":[]},{"name":"enableVault","docs":["enable vault"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["Vault account"]},{"name":"admin","isMut":false,"isSigner":true,"docs":["Admin account"]}],"args":[{"name":"enabled","type":"u8"}]},{"name":"setOperator","docs":["set new operator"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["Vault account"]},{"name":"operator","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true,"docs":["admin"]}],"args":[]},{"name":"initializeStrategy","docs":["Initialize a strategy and add strategy to vault.strategies index"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["Vault account"]},{"name":"strategyProgram","isMut":false,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false,"docs":["Strategy account"]},{"name":"reserve","isMut":true,"isSigner":false},{"name":"collateralVault","isMut":true,"isSigner":false,"docs":["Collateral vault account"]},{"name":"collateralMint","isMut":false,"isSigner":false,"docs":["Collateral mint account"]},{"name":"admin","isMut":true,"isSigner":true,"docs":["Admin account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program account"]},{"name":"rent","isMut":false,"isSigner":false,"docs":["Rent account"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program account"]}],"args":[{"name":"bumps","type":{"defined":"StrategyBumps"}},{"name":"strategyType","type":{"defined":"StrategyType"}}]},{"name":"removeStrategy","docs":["remove a strategy"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["Vault account"]},{"name":"strategy","isMut":true,"isSigner":false,"docs":["Strategy account"]},{"name":"strategyProgram","isMut":false,"isSigner":false},{"name":"collateralVault","isMut":true,"isSigner":false,"docs":["Collateral vault account"]},{"name":"reserve","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["token_vault"]},{"name":"feeVault","isMut":true,"isSigner":false,"docs":["fee_vault"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["lp_mint"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]},{"name":"admin","isMut":false,"isSigner":true,"docs":["admin"]}],"args":[]},{"name":"removeStrategy2","docs":["remove a strategy by advance payment"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["Vault account"]},{"name":"strategy","isMut":true,"isSigner":false,"docs":["Strategy account"]},{"name":"strategyProgram","isMut":false,"isSigner":false},{"name":"collateralVault","isMut":true,"isSigner":false,"docs":["Collateral vault account"]},{"name":"reserve","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["token_vault"]},{"name":"tokenAdminAdvancePayment","isMut":true,"isSigner":false,"docs":["token_advance_payemnt","the owner of token_advance_payment must be admin"]},{"name":"tokenVaultAdvancePayment","isMut":true,"isSigner":false,"docs":["token_vault_advance_payment","the account must be different from token_vault","the owner of token_advance_payment must be vault"]},{"name":"feeVault","isMut":true,"isSigner":false,"docs":["fee_vault"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["lp_mint"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]},{"name":"admin","isMut":false,"isSigner":true,"docs":["admin"]}],"args":[{"name":"maxAdminPayAmount","type":"u64"}]},{"name":"collectDust","docs":["collect token, that someone send wrongly","also help in case Mango reimbursement"],"accounts":[{"name":"vault","isMut":false,"isSigner":false,"docs":["vault"]},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["Token vault, must be different from vault.token_vault"]},{"name":"tokenAdmin","isMut":true,"isSigner":false,"docs":["token admin, enforce owner is admin to avoid mistake"]},{"name":"admin","isMut":false,"isSigner":true,"docs":["admin"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]}],"args":[]},{"name":"addStrategy","docs":["add a strategy"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["vault"]},{"name":"strategy","isMut":false,"isSigner":false,"docs":["strategy"]},{"name":"admin","isMut":false,"isSigner":true,"docs":["admin"]}],"args":[]},{"name":"depositStrategy","docs":["deposit liquidity to a strategy"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["vault"]},{"name":"strategy","isMut":true,"isSigner":false,"docs":["strategy"]},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["token_vault"]},{"name":"feeVault","isMut":true,"isSigner":false,"docs":["fee_vault"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["lp_mint"]},{"name":"strategyProgram","isMut":false,"isSigner":false},{"name":"collateralVault","isMut":true,"isSigner":false,"docs":["collateral_vault"]},{"name":"reserve","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]},{"name":"operator","isMut":false,"isSigner":true,"docs":["operator"]}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdrawStrategy","docs":["withdraw liquidity from a strategy"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["vault"]},{"name":"strategy","isMut":true,"isSigner":false,"docs":["strategy"]},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["token_vault"]},{"name":"feeVault","isMut":true,"isSigner":false,"docs":["fee_vault"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["lp_mint"]},{"name":"strategyProgram","isMut":false,"isSigner":false},{"name":"collateralVault","isMut":true,"isSigner":false,"docs":["collateral_vault"]},{"name":"reserve","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]},{"name":"operator","isMut":false,"isSigner":true,"docs":["operator"]}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdraw2","docs":["Withdraw v2. Withdraw from token vault if no remaining accounts are available. Else, it will attempt to withdraw from strategy and token vault. This method just proxy between 2 methods. Protocol integration should be using withdraw instead of this function."],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["vault"]},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["token_vault"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["lp_mint"]},{"name":"userToken","isMut":true,"isSigner":false,"docs":["user_token"]},{"name":"userLp","isMut":true,"isSigner":false,"docs":["user_lp"]},{"name":"user","isMut":false,"isSigner":true,"docs":["user"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]}],"args":[{"name":"unmintAmount","type":"u64"},{"name":"minOutAmount","type":"u64"}]},{"name":"deposit","docs":["user deposit liquidity to vault"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["vault"]},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["token_vault"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["lp_mint"]},{"name":"userToken","isMut":true,"isSigner":false,"docs":["user_token"]},{"name":"userLp","isMut":true,"isSigner":false,"docs":["user_lp"]},{"name":"user","isMut":false,"isSigner":true,"docs":["user"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]}],"args":[{"name":"tokenAmount","type":"u64"},{"name":"minimumLpTokenAmount","type":"u64"}]},{"name":"withdraw","docs":["user withdraw liquidity from vault"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["vault"]},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["token_vault"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["lp_mint"]},{"name":"userToken","isMut":true,"isSigner":false,"docs":["user_token"]},{"name":"userLp","isMut":true,"isSigner":false,"docs":["user_lp"]},{"name":"user","isMut":false,"isSigner":true,"docs":["user"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]}],"args":[{"name":"unmintAmount","type":"u64"},{"name":"minOutAmount","type":"u64"}]},{"name":"withdrawDirectlyFromStrategy","docs":["user withdraw liquidity from vault, if vault reserve doesn't have enough liquidity, it will withdraw from the strategy firstly"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["vault"]},{"name":"strategy","isMut":true,"isSigner":false,"docs":["strategy"]},{"name":"reserve","isMut":true,"isSigner":false},{"name":"strategyProgram","isMut":false,"isSigner":false},{"name":"collateralVault","isMut":true,"isSigner":false,"docs":["collateral_vault"]},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["token_vault"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["lp_mint"]},{"name":"feeVault","isMut":true,"isSigner":false,"docs":["fee_vault"]},{"name":"userToken","isMut":true,"isSigner":false,"docs":["user_token"]},{"name":"userLp","isMut":true,"isSigner":false,"docs":["user_lp"]},{"name":"user","isMut":false,"isSigner":true,"docs":["user"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]}],"args":[{"name":"unmintAmount","type":"u64"},{"name":"minOutAmount","type":"u64"}]}],"accounts":[{"name":"Vault","docs":["Vault struct"],"type":{"kind":"struct","fields":[{"name":"enabled","docs":["The flag, if admin set enable = false, then the user can only withdraw and cannot deposit in the vault."],"type":"u8"},{"name":"bumps","docs":["Vault nonce, to create vault seeds"],"type":{"defined":"VaultBumps"}},{"name":"totalAmount","docs":["The total liquidity of the vault, including remaining tokens in token_vault and the liquidity in all strategies."],"type":"u64"},{"name":"tokenVault","docs":["Token account, hold liquidity in vault reserve"],"type":"publicKey"},{"name":"feeVault","docs":["Hold lp token of vault, each time rebalance crank is called, vault calculate performance fee and mint corresponding lp token amount to fee_vault. fee_vault is owned by treasury address"],"type":"publicKey"},{"name":"tokenMint","docs":["Token mint that vault supports"],"type":"publicKey"},{"name":"lpMint","docs":["Lp mint of vault"],"type":"publicKey"},{"name":"strategies","docs":["The list of strategy addresses that vault supports, vault can support up to MAX_STRATEGY strategies at the same time."],"type":{"array":["publicKey",30]}},{"name":"base","docs":["The base address to create vault seeds"],"type":"publicKey"},{"name":"admin","docs":["Admin of vault"],"type":"publicKey"},{"name":"operator","docs":["Person who can send the crank. Operator can only send liquidity to strategies that admin defined, and claim reward to account of treasury address"],"type":"publicKey"},{"name":"lockedProfitTracker","docs":["Stores information for locked profit."],"type":{"defined":"LockedProfitTracker"}}]}},{"name":"Strategy","docs":["Strategy struct"],"type":{"kind":"struct","fields":[{"name":"reserve","docs":["Lending pool address, that the strategy will deposit/withdraw balance"],"type":"publicKey"},{"name":"collateralVault","docs":["The token account, that holds the collateral token"],"type":"publicKey"},{"name":"strategyType","docs":["Specify type of strategy"],"type":{"defined":"StrategyType"}},{"name":"currentLiquidity","docs":["The liquidity in strategy at the time vault deposit/withdraw from a lending protocol"],"type":"u64"},{"name":"bumps","docs":["Hold some bumps, in case the strategy needs to use other seeds to sign a CPI call."],"type":{"array":["u8",10]}},{"name":"vault","docs":["Vault address, that the strategy belongs"],"type":"publicKey"},{"name":"isDisable","docs":["If we remove strategy by remove_strategy2 endpoint, this account will be never added again"],"type":"u8"}]}}],"types":[{"name":"VaultBumps","docs":["Vault bumps struct"],"type":{"kind":"struct","fields":[{"name":"vaultBump","docs":["vault_bump"],"type":"u8"},{"name":"tokenVaultBump","docs":["token_vault_bump"],"type":"u8"}]}},{"name":"StrategyBumps","docs":["Strategy bumps struct"],"type":{"kind":"struct","fields":[{"name":"strategyIndex","docs":["strategy_index"],"type":"u8"},{"name":"otherBumps","docs":["Bumps of PDAs for the integrated protocol."],"type":{"array":["u8",10]}}]}},{"name":"LockedProfitTracker","docs":["LockedProfitTracker struct"],"type":{"kind":"struct","fields":[{"name":"lastUpdatedLockedProfit","docs":["The total locked profit from the last report"],"type":"u64"},{"name":"lastReport","docs":["The last timestamp (in seconds) rebalancing"],"type":"u64"},{"name":"lockedProfitDegradation","docs":["Rate per second of degradation"],"type":"u64"}]}},{"name":"StrategyType","docs":["StrategyType struct"],"type":{"kind":"enum","variants":[{"name":"PortFinanceWithoutLM"},{"name":"PortFinanceWithLM"},{"name":"SolendWithoutLM"},{"name":"Mango"},{"name":"SolendWithLM"},{"name":"ApricotWithoutLM"},{"name":"Francium"},{"name":"Tulip"},{"name":"Vault"},{"name":"Drift"},{"name":"Frakt"},{"name":"Marginfi"}]}}],"events":[{"name":"AddLiquidity","fields":[{"name":"lpMintAmount","type":"u64","index":false},{"name":"tokenAmount","type":"u64","index":false}]},{"name":"RemoveLiquidity","fields":[{"name":"lpUnmintAmount","type":"u64","index":false},{"name":"tokenAmount","type":"u64","index":false}]},{"name":"StrategyDeposit","fields":[{"name":"strategyType","type":{"defined":"StrategyType"},"index":false},{"name":"tokenAmount","type":"u64","index":false}]},{"name":"StrategyWithdraw","fields":[{"name":"strategyType","type":{"defined":"StrategyType"},"index":false},{"name":"collateralAmount","type":"u64","index":false},{"name":"estimatedTokenAmount","type":"u64","index":false}]},{"name":"ClaimReward","fields":[{"name":"strategyType","type":{"defined":"StrategyType"},"index":false},{"name":"tokenAmount","type":"u64","index":false},{"name":"mintAccount","type":"publicKey","index":false}]},{"name":"PerformanceFee","fields":[{"name":"lpMintMore","type":"u64","index":false}]},{"name":"ReportLoss","fields":[{"name":"strategy","type":"publicKey","index":false},{"name":"loss","type":"u64","index":false}]},{"name":"TotalAmount","fields":[{"name":"totalAmount","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"VaultIsDisabled","msg":"Vault is disabled"},{"code":6001,"name":"ExceededSlippage","msg":"Exceeded slippage tolerance"},{"code":6002,"name":"StrategyIsNotExisted","msg":"Strategy is not existed"},{"code":6003,"name":"UnAuthorized","msg":"UnAuthorized"},{"code":6004,"name":"MathOverflow","msg":"Math operation overflow"},{"code":6005,"name":"ProtocolIsNotSupported","msg":"Protocol is not supported"},{"code":6006,"name":"UnMatchReserve","msg":"Reserve does not support token mint"},{"code":6007,"name":"InvalidLockedProfitDegradation","msg":"lockedProfitDegradation is invalid"},{"code":6008,"name":"MaxStrategyReached","msg":"Maximum number of strategies have been reached"},{"code":6009,"name":"StrategyExisted","msg":"Strategy existed"},{"code":6010,"name":"InvalidUnmintAmount","msg":"Invalid unmint amount"},{"code":6011,"name":"InvalidAccountsForStrategy","msg":"Invalid accounts for strategy"},{"code":6012,"name":"InvalidBump","msg":"Invalid bump"},{"code":6013,"name":"AmountMustGreaterThanZero","msg":"Amount must be greater than 0"},{"code":6014,"name":"MangoIsNotSupportedAnymore","msg":"Mango is not supported anymore"},{"code":6015,"name":"StrategyIsNotSupported","msg":"Strategy is not supported"},{"code":6016,"name":"PayAmountIsExeeced","msg":"Pay amount is exceeded"},{"code":6017,"name":"FeeVaultIsNotSet","msg":"Fee vault is not set"},{"code":6018,"name":"LendingAssertionViolation","msg":"deposit amount in lending is not matched"},{"code":6019,"name":"HaveMoneyInLending","msg":"Cannot remove strategy becase we have some in lending"}]} \ No newline at end of file diff --git a/idls/moonit.MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG.json b/idls/moonit.MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG.json new file mode 100644 index 0000000..8f66db1 --- /dev/null +++ b/idls/moonit.MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"token_launchpad","instructions":[{"name":"tokenMint","accounts":[{"name":"sender","isMut":true,"isSigner":true},{"name":"backendAuthority","isMut":false,"isSigner":true},{"name":"curveAccount","isMut":true,"isSigner":false},{"name":"mint","isMut":true,"isSigner":true},{"name":"mintMetadata","isMut":true,"isSigner":false,"docs":["Type validating that the account is owned by the System Program = uninitialized","seeds should ensure that the address is correct"]},{"name":"curveTokenAccount","isMut":true,"isSigner":false},{"name":"configAccount","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"mplTokenMetadata","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"mintParams","type":{"defined":"TokenMintParams"}}]},{"name":"buy","accounts":[{"name":"sender","isMut":true,"isSigner":true},{"name":"senderTokenAccount","isMut":true,"isSigner":false},{"name":"curveAccount","isMut":true,"isSigner":false},{"name":"curveTokenAccount","isMut":true,"isSigner":false},{"name":"dexFee","isMut":true,"isSigner":false},{"name":"helioFee","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"configAccount","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"data","type":{"defined":"TradeParams"}}]},{"name":"sell","accounts":[{"name":"sender","isMut":true,"isSigner":true},{"name":"senderTokenAccount","isMut":true,"isSigner":false},{"name":"curveAccount","isMut":true,"isSigner":false},{"name":"curveTokenAccount","isMut":true,"isSigner":false},{"name":"dexFee","isMut":true,"isSigner":false},{"name":"helioFee","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"configAccount","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"data","type":{"defined":"TradeParams"}}]},{"name":"migrateFunds","accounts":[{"name":"backendAuthority","isMut":false,"isSigner":true,"docs":["BE Authority"]},{"name":"migrationAuthority","isMut":true,"isSigner":true,"docs":["Migration Authority","Owner and Payer over Token Accounts, needs to be mutable"]},{"name":"curveAccount","isMut":true,"isSigner":false,"docs":["Curve Account","The account is closed after this instruction"]},{"name":"curveTokenAccount","isMut":true,"isSigner":false,"docs":["Curve Token Account","The account is closed after this instruction"]},{"name":"migrationAuthorityTokenAccount","isMut":true,"isSigner":false,"docs":["Authority token Account","Init on demand"]},{"name":"mint","isMut":true,"isSigner":false,"docs":["InterfaceAccount: checks program ownership + deserialize into Mint"]},{"name":"dexFeeAccount","isMut":true,"isSigner":false},{"name":"helioFeeAccount","isMut":true,"isSigner":false},{"name":"configAccount","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"configInit","accounts":[{"name":"configAuthority","isMut":true,"isSigner":true},{"name":"configAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"data","type":{"defined":"ConfigParams"}}]},{"name":"configUpdate","accounts":[{"name":"configAuthority","isMut":false,"isSigner":true},{"name":"configAccount","isMut":true,"isSigner":false}],"args":[{"name":"data","type":{"defined":"ConfigParams"}}]}],"accounts":[{"name":"ConfigAccount","type":{"kind":"struct","fields":[{"name":"migrationAuthority","type":"publicKey"},{"name":"backendAuthority","type":"publicKey"},{"name":"configAuthority","type":"publicKey"},{"name":"helioFee","type":"publicKey"},{"name":"dexFee","type":"publicKey"},{"name":"feeBps","type":"u16"},{"name":"dexFeeShare","type":"u8"},{"name":"migrationFee","type":"u64"},{"name":"marketcapThreshold","type":"u64"},{"name":"marketcapCurrency","type":{"defined":"Currency"}},{"name":"minSupportedDecimalPlaces","type":"u8"},{"name":"maxSupportedDecimalPlaces","type":"u8"},{"name":"minSupportedTokenSupply","type":"u64"},{"name":"maxSupportedTokenSupply","type":"u64"},{"name":"bump","type":"u8"},{"name":"coefB","type":"u32"}]}},{"name":"CurveAccount","type":{"kind":"struct","fields":[{"name":"totalSupply","type":"u64"},{"name":"curveAmount","type":"u64"},{"name":"mint","type":"publicKey"},{"name":"decimals","type":"u8"},{"name":"collateralCurrency","type":{"defined":"Currency"}},{"name":"curveType","type":{"defined":"CurveType"}},{"name":"marketcapThreshold","type":"u64"},{"name":"marketcapCurrency","type":{"defined":"Currency"}},{"name":"migrationFee","type":"u64"},{"name":"coefB","type":"u32"},{"name":"bump","type":"u8"},{"name":"migrationTarget","type":{"defined":"MigrationTarget"}}]}}],"types":[{"name":"TokenMintParams","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"decimals","type":"u8"},{"name":"collateralCurrency","type":"u8"},{"name":"amount","type":"u64"},{"name":"curveType","type":"u8"},{"name":"migrationTarget","type":"u8"}]}},{"name":"TradeParams","type":{"kind":"struct","fields":[{"name":"tokenAmount","type":"u64"},{"name":"collateralAmount","type":"u64"},{"name":"fixedSide","type":"u8"},{"name":"slippageBps","type":"u64"}]}},{"name":"ConfigParams","type":{"kind":"struct","fields":[{"name":"migrationAuthority","type":{"option":"publicKey"}},{"name":"backendAuthority","type":{"option":"publicKey"}},{"name":"configAuthority","type":{"option":"publicKey"}},{"name":"helioFee","type":{"option":"publicKey"}},{"name":"dexFee","type":{"option":"publicKey"}},{"name":"feeBps","type":{"option":"u16"}},{"name":"dexFeeShare","type":{"option":"u8"}},{"name":"migrationFee","type":{"option":"u64"}},{"name":"marketcapThreshold","type":{"option":"u64"}},{"name":"marketcapCurrency","type":{"option":"u8"}},{"name":"minSupportedDecimalPlaces","type":{"option":"u8"}},{"name":"maxSupportedDecimalPlaces","type":{"option":"u8"}},{"name":"minSupportedTokenSupply","type":{"option":"u64"}},{"name":"maxSupportedTokenSupply","type":{"option":"u64"}},{"name":"coefB","type":{"option":"u32"}}]}},{"name":"Currency","type":{"kind":"enum","variants":[{"name":"Sol"}]}},{"name":"CurveType","type":{"kind":"enum","variants":[{"name":"LinearV1"},{"name":"ConstantProductV1"}]}},{"name":"MigrationTarget","type":{"kind":"enum","variants":[{"name":"Raydium"},{"name":"Meteora"}]}},{"name":"TradeType","type":{"kind":"enum","variants":[{"name":"Buy"},{"name":"Sell"}]}},{"name":"FixedSide","type":{"kind":"enum","variants":[{"name":"ExactIn"},{"name":"ExactOut"}]}}],"events":[{"name":"TradeEvent","fields":[{"name":"amount","type":"u64","index":false},{"name":"collateralAmount","type":"u64","index":false},{"name":"dexFee","type":"u64","index":false},{"name":"helioFee","type":"u64","index":false},{"name":"allocation","type":"u64","index":false},{"name":"curve","type":"publicKey","index":false},{"name":"costToken","type":"publicKey","index":false},{"name":"sender","type":"publicKey","index":false},{"name":"type","type":{"defined":"TradeType"},"index":false},{"name":"label","type":"string","index":true}]},{"name":"MigrationEvent","fields":[{"name":"tokensMigrated","type":"u64","index":false},{"name":"tokensBurned","type":"u64","index":false},{"name":"collateralMigrated","type":"u64","index":false},{"name":"fee","type":"u64","index":false},{"name":"label","type":"string","index":true}]}],"errors":[{"code":6000,"name":"InsufficientBalance","msg":"Insufficient SOL to pay for the transaction."},{"code":6001,"name":"InvalidAmount","msg":"The amount must be available in the curve ."},{"code":6002,"name":"InvalidSlippage","msg":"The slippage must be under 100 percent."},{"code":6003,"name":"SlippageOverflow","msg":"The cost amount is not in the allowed slippage interval."},{"code":6004,"name":"ThresholdReached","msg":"Threshold limit exceeded."},{"code":6005,"name":"InvalidTokenAccount","msg":"Trade disabled, market cap threshold reached."},{"code":6006,"name":"InvalidCurveAccount","msg":"Invalid curve account."},{"code":6007,"name":"InvalidFeeAccount","msg":"Invalid fee account address."},{"code":6008,"name":"CurveLimit","msg":"Curve limit exceeded."},{"code":6009,"name":"InvalidCurveType","msg":"Invalid curve type."},{"code":6010,"name":"InvalidCurrency","msg":"Invalid currency."},{"code":6011,"name":"Arithmetics","msg":"Artithmetics error"},{"code":6012,"name":"ThresholdNotHit","msg":"Market Cap threshold not hit, cannot migrate funds yet"},{"code":6013,"name":"InvalidAuthority","msg":"Invalid Authority provided."},{"code":6014,"name":"TradeAmountTooLow","msg":"Trade amount too low , resulting in 0 costs"},{"code":6015,"name":"ConfigFieldMissing","msg":"Config field needs to be present during initialization"},{"code":6016,"name":"DifferentCurrencies","msg":"Unsupported different currency types"},{"code":6017,"name":"BasisPointTooHigh","msg":"Basis points too high"},{"code":6018,"name":"FeeShareTooHigh","msg":"Fee share too High"},{"code":6019,"name":"TokenDecimalsOutOfRange","msg":"Token decimals are not within the supported range"},{"code":6020,"name":"TokenNameTooLong","msg":"Token Name too long, max supported length is 32 bytes"},{"code":6021,"name":"TokenSymbolTooLong","msg":"Token Symbol too long, max supported length is 10 bytes"},{"code":6022,"name":"TokenURITooLong","msg":"Token URI too long, max supported length is 200 bytes"},{"code":6023,"name":"IncorrectDecimalPlacesBounds","msg":"Minimum Decimal Places cannot be lower than Maximum Decimal Places"},{"code":6024,"name":"IncorrectTokenSupplyBounds","msg":"Minimum Token Supply cannot be lower than Maximum Token Supply"},{"code":6025,"name":"TotalSupplyOutOfBounds","msg":"Token Total Supply out of bounds"},{"code":6026,"name":"FinalCollateralTooLow","msg":"This setup will produce final collateral amount less than the migration fee"},{"code":6027,"name":"CoefficientZero","msg":"One of the Coefficients is equal to ZERO"},{"code":6028,"name":"MarketCapThresholdTooLow","msg":"Market cap Threshold under the Hard lower bound limits"},{"code":6029,"name":"CoefBOutofBounds","msg":"Default coef_b set out of hard limit bounds"},{"code":6030,"name":"IncorrectMarketCap","msg":"For Constant Product the Market Cap threshold cannot be higher than 325 SOL"},{"code":6031,"name":"IncorrectDecimals","msg":"For Constant Product the Decimal places cannot be other than 9"},{"code":6032,"name":"IncorrectMaxSupply","msg":"For Constant Product the Maximal Token Supply cannot be other than 1_000_000_000"},{"code":6033,"name":"MarketCapTooHigh","msg":"Market Cap Threshold set too high, will not be hit even if Curve Hard Limit reached"},{"code":6034,"name":"InvalidMigrationTarget","msg":"This Migration Target is not supported!"},{"code":6035,"name":"General","msg":"General error"}]} \ No newline at end of file diff --git a/idls/numeraire.NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P.json b/idls/numeraire.NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P.json new file mode 100644 index 0000000..fb67676 --- /dev/null +++ b/idls/numeraire.NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P.json @@ -0,0 +1 @@ +{"address":"NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P","metadata":{"name":"numeraire","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"add_liquidity","docs":["Pool Owner/Whitelist only functions"],"discriminator":[181,157,89,67,143,182,52,72],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"const","value":[108,105,113,117,105,100,105,116,121]}]}},{"name":"payer_lp_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"payer"},{"kind":"account","path":"token_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"data","type":{"defined":{"name":"AddLiquidityData"}}}],"returns":"u64"},{"name":"compound","discriminator":[165,208,251,78,242,160,141,47],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"const","value":[108,105,113,117,105,100,105,116,121]}]}},{"name":"payer_lp_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"payer"},{"kind":"account","path":"token_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[]},{"name":"create_pool","discriminator":[233,146,209,142,207,104,64,188],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"arg","path":"data.pool_seed"}]}},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"const","value":[108,105,113,117,105,100,105,116,121]}]}},{"name":"creator_lp_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"payer"},{"kind":"account","path":"token_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","writable":true,"signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"data","type":{"defined":{"name":"CreateStablePoolData"}}}]},{"name":"init_virtual_stable_pair","discriminator":[228,18,163,161,101,204,106,172],"accounts":[{"name":"x_mint"},{"name":"pair","writable":true,"pda":{"seeds":[{"kind":"account","path":"x_mint"},{"kind":"arg","path":"data.pair_seed"}]}},{"name":"pair_authority","pda":{"seeds":[{"kind":"account","path":"pair"},{"kind":"const","value":[97,117,116,104,111,114,105,116,121]}]}},{"name":"x_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"pair"},{"kind":"const","value":[108,105,113,117,105,100,105,116,121]}]}},{"name":"x_adder","writable":true,"pda":{"seeds":[{"kind":"account","path":"payer"},{"kind":"account","path":"token_program"},{"kind":"account","path":"x_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","writable":true,"signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"]},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"data","type":{"defined":{"name":"InitVirtualStablePairData"}}}]},{"name":"remove_all_liquidity","discriminator":[10,51,61,35,112,105,24,85],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"const","value":[108,105,113,117,105,100,105,116,121]}]}},{"name":"payer_lp_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"payer"},{"kind":"account","path":"token_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[]},{"name":"remove_liquidity","discriminator":[80,85,209,72,24,206,177,108],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"const","value":[108,105,113,117,105,100,105,116,121]}]}},{"name":"payer_lp_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"payer"},{"kind":"account","path":"token_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"data","type":{"defined":{"name":"RemoveLiquidityData"}}}],"returns":"u64"},{"name":"set_fee","discriminator":[18,154,24,18,237,214,19,80],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"payer","signer":true}],"args":[{"name":"data","type":{"defined":{"name":"SetFeeData"}}}]},{"name":"set_inv_t_max","discriminator":[218,209,244,237,211,236,98,58],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"payer","signer":true}],"args":[{"name":"data","type":{"defined":{"name":"SetInvTMaxData"}}}]},{"name":"set_lp_token_metadata","discriminator":[71,73,56,155,202,142,100,150],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"const","value":[108,105,113,117,105,100,105,116,121]}]}},{"name":"metadata_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"account","path":"token_metadata_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"account","path":"token_metadata_program"}}},{"name":"payer","signer":true},{"name":"token_metadata_program","docs":["Solana ecosystem accounts"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"instructions","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[{"name":"data","type":{"defined":{"name":"SetMetadataData"}}}]},{"name":"set_numeraire_owner","discriminator":[6,199,177,104,86,61,93,253],"accounts":[{"name":"numeraire_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"pair_mint","optional":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","docs":["Solana ecosystem accounts"],"address":"11111111111111111111111111111111"}],"args":[{"name":"data","type":{"defined":{"name":"SetOwnerData"}}}]},{"name":"set_numeraire_status","discriminator":[10,17,5,71,204,171,126,173],"accounts":[{"name":"numeraire_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"pair_mint","optional":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","docs":["Solana ecosystem accounts"],"address":"11111111111111111111111111111111"}],"args":[{"name":"data","type":{"defined":{"name":"SetStatusData"}}}]},{"name":"set_numeraire_whitelisted_pool_creator","discriminator":[147,51,31,255,111,2,189,173],"accounts":[{"name":"numeraire_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"pair_mint","optional":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","docs":["Solana ecosystem accounts"],"address":"11111111111111111111111111111111"}],"args":[{"name":"data","type":{"defined":{"name":"SetWhilelistedAddrData"}}}]},{"name":"set_owner","discriminator":[72,202,120,52,77,128,96,197],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"payer","signer":true}],"args":[{"name":"data","type":{"defined":{"name":"SetOwnerData"}}}]},{"name":"set_rate","discriminator":[99,58,170,238,160,120,74,11],"accounts":[{"name":"numeraire_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"pair_mint","optional":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","docs":["Solana ecosystem accounts"],"address":"11111111111111111111111111111111"}],"args":[{"name":"data","type":{"defined":{"name":"SetRateData"}}}]},{"name":"set_status","docs":["Pool Owner only functions"],"discriminator":[181,184,224,203,193,29,177,224],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"payer","signer":true}],"args":[{"name":"data","type":{"defined":{"name":"SetStatusData"}}}]},{"name":"set_whitelisted_adder","discriminator":[74,38,140,129,228,73,236,105],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"payer","signer":true}],"args":[{"name":"data","type":{"defined":{"name":"SetWhilelistedAddrData"}}}]},{"name":"skim","discriminator":[238,120,221,138,82,60,100,218],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"const","value":[108,105,113,117,105,100,105,116,121]}]}},{"name":"payer_lp_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"payer"},{"kind":"account","path":"token_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[]},{"name":"swap_exact_in","discriminator":[104,104,131,86,161,189,180,216],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"in_mint","writable":true},{"name":"out_mint","writable":true},{"name":"in_trader","writable":true},{"name":"out_trader","writable":true},{"name":"in_vault","writable":true,"optional":true},{"name":"out_vault","writable":true,"optional":true},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","docs":["The trader account (executing the swap, paying for all rents)"],"signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"data","type":{"defined":{"name":"SwapExactInHintlessData"}}}],"returns":"u64"},{"name":"swap_exact_in_hinted","discriminator":[98,239,244,233,16,236,40,49],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"in_mint","writable":true},{"name":"out_mint","writable":true},{"name":"in_trader","writable":true},{"name":"out_trader","writable":true},{"name":"in_vault","writable":true,"optional":true},{"name":"out_vault","writable":true,"optional":true},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","docs":["The trader account (executing the swap, paying for all rents)"],"signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"data","type":{"defined":{"name":"SwapExactInData"}}}],"returns":"u64"},{"name":"swap_exact_in_quote","discriminator":[68,209,177,170,185,100,29,191],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"in_mint","writable":true},{"name":"out_mint","writable":true},{"name":"in_trader","writable":true},{"name":"out_trader","writable":true},{"name":"in_vault","writable":true,"optional":true},{"name":"out_vault","writable":true,"optional":true},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","docs":["The trader account (executing the swap, paying for all rents)"],"signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"data","type":{"defined":{"name":"SwapExactInData"}}}],"returns":"u64"},{"name":"swap_exact_out","discriminator":[250,73,101,33,38,207,75,184],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"in_mint","writable":true},{"name":"out_mint","writable":true},{"name":"in_trader","writable":true},{"name":"out_trader","writable":true},{"name":"in_vault","writable":true,"optional":true},{"name":"out_vault","writable":true,"optional":true},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","docs":["The trader account (executing the swap, paying for all rents)"],"signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"data","type":{"defined":{"name":"SwapExactOutHintlessData"}}}],"returns":"u64"},{"name":"swap_exact_out_hinted","discriminator":[153,208,206,70,62,234,98,182],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"in_mint","writable":true},{"name":"out_mint","writable":true},{"name":"in_trader","writable":true},{"name":"out_trader","writable":true},{"name":"in_vault","writable":true,"optional":true},{"name":"out_vault","writable":true,"optional":true},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","docs":["The trader account (executing the swap, paying for all rents)"],"signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"data","type":{"defined":{"name":"SwapExactOutData"}}}],"returns":"u64"}],"accounts":[{"name":"NumeraireConfig","discriminator":[230,62,124,43,102,101,88,63]},{"name":"StablePool","discriminator":[239,91,93,162,171,14,42,66]},{"name":"VirtualStablePair","discriminator":[112,153,135,223,53,247,129,101]}],"events":[{"name":"AddLiquidity","discriminator":[31,94,125,90,227,52,61,186]},{"name":"Quote","discriminator":[133,244,92,134,193,24,187,158]},{"name":"RemoveLiquidity","discriminator":[116,244,97,232,103,31,152,58]},{"name":"SwapExactIn","discriminator":[147,136,213,11,150,23,141,152]},{"name":"SwapExactOut","discriminator":[71,66,127,123,231,29,227,92]}],"errors":[{"code":6000,"name":"OnlyOwner","msg":"Attempt to call an owner only function not by the owner"},{"code":6001,"name":"InvalidTokenAccountData","msg":"Token account data is not as expected"},{"code":6002,"name":"InvalidAccountData","msg":"Account data is not as expected"},{"code":6003,"name":"DecimalsMismatch","msg":"Token pair decimals mismatch"},{"code":6004,"name":"InvalidTokenExtension","msg":"Token should not have this extension"},{"code":6005,"name":"IncorrectAccounts","msg":"Wrong account(s) passed as input"},{"code":6006,"name":"IncorrectAuthority","msg":"Wrong authority passed as input"},{"code":6007,"name":"InvalidDelegate","msg":"Delegate not allowed"},{"code":6008,"name":"UnsupportedFeature","msg":"This feature is currently unsupported or unenabled"},{"code":6009,"name":"FunctionPaused","msg":"This function is currently paused"},{"code":6010,"name":"InvalidFee","msg":"Fee must be less than 100 basis points"},{"code":6011,"name":"InvalidPoolCreate","msg":"Token mints must be ordered by pubkey"},{"code":6012,"name":"InvalidCurveParams","msg":"Curve params must be positive"},{"code":6013,"name":"InsufficientLiquidity","msg":"Pool has too little liquidity for action"},{"code":6014,"name":"LiquidityAddUnbalanced","msg":"Liquidity add does not make pool balanced (or curve params are asymmetric)"},{"code":6015,"name":"InsufficientBalance","msg":"Input is more than trader balance"},{"code":6016,"name":"InputTooSmall","msg":"Input is below the minimum expected"},{"code":6017,"name":"InputTooBig","msg":"Input is more than available liquidity"},{"code":6018,"name":"OutputTooSmall","msg":"Output is below the minimum expected"},{"code":6019,"name":"InvariantOverflow","msg":"Invariant computation overflowed"},{"code":6020,"name":"InvariantViolated","msg":"Invariant does not hold"},{"code":6021,"name":"LiquidityAddTooSmall","msg":"Depositing too little liquidity"},{"code":6022,"name":"UnsupportedTokenProgram","msg":"Only Token Program 2022 and/or Token Program are supported"},{"code":6023,"name":"InvalidPoolWeights","msg":"Some pool weights are zero or nonzero and shouldn't be"},{"code":6024,"name":"SwapOverflowError","msg":"A swap math operation overflowed"},{"code":6025,"name":"LiquidityMathOverflow","msg":"An add/remove liquidity math operation overflowed"},{"code":6026,"name":"InvalidBalanceDeltas","msg":"Some add/remove balance deltas are nonzero and shouldn't be"},{"code":6027,"name":"InvalidPoolParams","msg":"The provided pool params were not well formed"},{"code":6028,"name":"InvalidHints","msg":"The hints provided do not bound the swap amounts"},{"code":6029,"name":"FeeError","msg":"Unexpected fee result"},{"code":6030,"name":"VaultOverflowError","msg":"A vault math operation overflowed"},{"code":6031,"name":"OutputTooBig","msg":"Output is above the maximum expected"},{"code":6032,"name":"Unreachable","msg":"This cannot happen"},{"code":6033,"name":"InvalidAction","msg":"This action is not allowed"}],"types":[{"name":"AddLiquidity","type":{"kind":"struct","fields":[{"name":"lp_token_mint_amount","type":"u64"},{"name":"x_reserve_deltas","type":{"array":["u64",10]}},{"name":"y_reserve_deltas","type":{"array":["u64",10]}},{"name":"inv_L_deltas","type":{"array":["u64",10]}},{"name":"min_lp_token_mint_amount","type":"u64"},{"name":"trader","type":"pubkey"},{"name":"pool","type":"pubkey"}]}},{"name":"AddLiquidityData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"max_amounts_in","type":{"array":["u64",10]}},{"name":"min_lp_token_mint_amount","type":"u64"},{"name":"take_swaps","type":"u8"},{"name":"swap_paths","type":{"array":["u8",10]}},{"name":"swap_amounts","type":{"array":["u64",10]}}]}},{"name":"CreateStablePoolData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"decimals","type":"u8"},{"name":"fee_num","type":"u32"},{"name":"fee_denom","type":"u32"},{"name":"pool_seed","type":"pubkey"},{"name":"weights","type":{"array":["u32",10]}},{"name":"inv_T","type":"u64"},{"name":"inv_T_max","type":"u64"}]}},{"name":"InitVirtualStablePairData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"decimals","type":"u8"},{"name":"init_amount","type":"u64"},{"name":"curve_Amp","type":"u128"},{"name":"curve_a","type":"u128"},{"name":"curve_b","type":"u128"},{"name":"curve_alpha","type":"u64"},{"name":"curve_beta","type":"u64"},{"name":"pair_seed","type":"pubkey"}]}},{"name":"NumeraireConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"owner","type":"pubkey"},{"name":"status","type":"u32"},{"name":"rate_mints","type":{"array":["pubkey",10]}},{"name":"rate_nums","type":{"array":["u32",10]}},{"name":"rate_denoms","type":{"array":["u32",10]}},{"name":"_padding","type":{"array":["u8",12]}},{"name":"padding","type":{"array":["u8",1024]}}]}},{"name":"Quote","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"RemoveLiquidity","type":{"kind":"struct","fields":[{"name":"lp_token_redeem_amount","type":"u64"},{"name":"x_reserve_deltas","type":{"array":["u64",10]}},{"name":"y_reserve_deltas","type":{"array":["u64",10]}},{"name":"inv_L_deltas","type":{"array":["u64",10]}},{"name":"min_amounts_out","type":{"array":["u64",10]}},{"name":"trader","type":"pubkey"},{"name":"pool","type":"pubkey"}]}},{"name":"RemoveLiquidityData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"lp_token_redeem_amount","type":"u64"},{"name":"min_amounts_out","type":{"array":["u64",10]}},{"name":"out_index","type":"u8"}]}},{"name":"SetFeeData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"fee_num","type":"u32"},{"name":"fee_denom","type":"u32"}]}},{"name":"SetInvTMaxData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"inv_T_max","type":"u64"}]}},{"name":"SetMetadataData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]}},{"name":"SetOwnerData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"owner","type":"pubkey"}]}},{"name":"SetRateData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"rate_mint","type":"pubkey"},{"name":"rate_num","type":"u32"},{"name":"rate_denom","type":"u32"}]}},{"name":"SetStatusData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"status","type":"u32"}]}},{"name":"SetWhilelistedAddrData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"whitelisted_addr","type":"pubkey"}]}},{"name":"StablePool","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"pool_seed","type":"pubkey"},{"name":"lp_mint","type":"pubkey"},{"name":"whitelisted_adder","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"inv_T","type":"u64"},{"name":"inv_T_max","type":"u64"},{"name":"pairs","type":{"array":[{"defined":{"name":"VirtualStablePair"}},10]}},{"name":"weights","type":{"array":["u32",10]}},{"name":"total_weight","type":"u64"},{"name":"status","type":"u32"},{"name":"fee_num","type":"u32"},{"name":"fee_denom","type":"u32"},{"name":"decimals","type":"u8"},{"name":"num_stables","type":"u8"},{"name":"_padding","type":{"array":["u8",2]}},{"name":"padding","type":{"array":["u8",128]}}]}},{"name":"SwapExactIn","type":{"kind":"struct","fields":[{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"},{"name":"min_amount_out","type":"u64"},{"name":"trader","type":"pubkey"},{"name":"in_index","type":"u8"},{"name":"out_index","type":"u8"},{"name":"pool","type":"pubkey"}]}},{"name":"SwapExactInData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"in_index","type":"u8"},{"name":"out_index","type":"u8"},{"name":"exact_amount_in","type":"u64"},{"name":"min_amount_out","type":"u64"},{"name":"hints","type":{"array":["u64",10]}},{"name":"path_hints","type":{"array":["u8",10]}}]}},{"name":"SwapExactInHintlessData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"in_index","type":"u8"},{"name":"out_index","type":"u8"},{"name":"exact_amount_in","type":"u64"},{"name":"min_amount_out","type":"u64"}]}},{"name":"SwapExactOut","type":{"kind":"struct","fields":[{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"},{"name":"max_amount_in","type":"u64"},{"name":"trader","type":"pubkey"},{"name":"in_index","type":"u8"},{"name":"out_index","type":"u8"},{"name":"pool","type":"pubkey"}]}},{"name":"SwapExactOutData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"in_index","type":"u8"},{"name":"out_index","type":"u8"},{"name":"exact_amount_out","type":"u64"},{"name":"max_amount_in","type":"u64"},{"name":"hints","type":{"array":["u64",10]}},{"name":"path_hints","type":{"array":["u8",10]}}]}},{"name":"SwapExactOutHintlessData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"in_index","type":"u8"},{"name":"out_index","type":"u8"},{"name":"exact_amount_out","type":"u64"},{"name":"max_amount_in","type":"u64"}]}},{"name":"VirtualStablePair","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"pair_authority","type":"pubkey"},{"name":"x_reserve_amount","type":"u64"},{"name":"y_reserve","type":"u64"},{"name":"curve_Amp","docs":["Curve params. Define bonding curve shape: x + y + a + b - A / (x + a) - A / (y + b) - D = 0"],"type":"u128"},{"name":"curve_a","type":"u128"},{"name":"curve_b","type":"u128"},{"name":"inv_L","docs":["Invariant constant. For invariant: x/L + y/L + a + b - A / (x/L + a) - A / (y/L + b) - D = 0"],"type":"u128"},{"name":"owner","type":"pubkey"},{"name":"x_mint","type":"pubkey"},{"name":"x_vault","type":"pubkey"},{"name":"curve_alpha","type":"u64"},{"name":"curve_beta","type":"u64"},{"name":"newest_rate_num","type":"u32"},{"name":"newest_rate_denom","type":"u32"},{"name":"decimals","type":"u8"},{"name":"pair_index","type":"u8"},{"name":"x_is_2022","type":"u8"},{"name":"_padding","type":{"array":["u8",5]}},{"name":"padding","type":{"array":["u8",128]}}]}}],"constants":[{"name":"AUTHORITY_SEED","type":"bytes","value":"[97, 117, 116, 104, 111, 114, 105, 116, 121]"},{"name":"CONFIG_SEED","type":"bytes","value":"[99, 111, 110, 102, 105, 103]"},{"name":"LIQUIDITY_SEED","type":"bytes","value":"[108, 105, 113, 117, 105, 100, 105, 116, 121]"},{"name":"LP_TOKEN_PROGRAM","type":"pubkey","value":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"MAX_STABLES_PER_POOL","type":"u8","value":"10"},{"name":"NORMALIZED_VALUE_DECIMALS","type":"u8","value":"6"}]} \ No newline at end of file diff --git a/idls/okx_dex_router.proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u.json b/idls/okx_dex_router.proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u.json new file mode 100644 index 0000000..0366a0a --- /dev/null +++ b/idls/okx_dex_router.proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u.json @@ -0,0 +1 @@ +{"address":"proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u","metadata":{"name":"OKX: DEX Router","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"claim","discriminator":[62,198,214,193,213,159,108,210],"accounts":[{"name":"signer","writable":true,"signer":true,"address":"CjoV5B96reuCfPh2rRK11G1QptG97jZdyZArTn3EN1Mj"},{"name":"receiver","writable":true},{"name":"source_token_account","writable":true,"optional":true},{"name":"destination_token_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"address":"ARu4n5mFdZogZAravu7CcizaojWnS6oqka37gdLT5SZn"},{"name":"token_mint","optional":true},{"name":"token_program","optional":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[]},{"name":"claim_cashback_pumpfun","discriminator":[83,1,245,125,201,207,209,249],"accounts":[{"name":"signer","signer":true,"address":"CjoV5B96reuCfPh2rRK11G1QptG97jZdyZArTn3EN1Mj"},{"name":"sa_authority","docs":["PumpFun transfers claimed SOL directly to this account (account[0] in the CPI)."],"writable":true,"address":"ARu4n5mFdZogZAravu7CcizaojWnS6oqka37gdLT5SZn"},{"name":"user_volume_accumulator","docs":["Holds native SOL accumulated as cashback; emptied by PumpFun during claim."],"writable":true},{"name":"receiver","docs":["after PumpFun deposits it there."],"writable":true},{"name":"event_authority","address":"Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"pumpfun_program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"}],"args":[]},{"name":"claim_cashback_pumpswap","discriminator":[253,242,176,173,217,78,100,25],"accounts":[{"name":"signer","signer":true,"address":"CjoV5B96reuCfPh2rRK11G1QptG97jZdyZArTn3EN1Mj"},{"name":"sa_authority","writable":true,"address":"ARu4n5mFdZogZAravu7CcizaojWnS6oqka37gdLT5SZn"},{"name":"user_volume_accumulator","writable":true},{"name":"wsol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"spl_token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"uva_wsol_ata","writable":true},{"name":"sa_wsol_ata","docs":["PumpSwap requires account[5] to be a token account owned by authority_pda","(same owner as user_volume_accumulator). WSOL is deposited here by PumpSwap,","then forwarded to receiver_wsol_ata via a second SPL transfer."],"writable":true},{"name":"receiver_wsol_ata","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","address":"GS4CU59F31iL7aR2Q8zVS8DRrcRnXX1yjQ66TqNVQnaR"},{"name":"pumpswap_program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"}],"args":[]},{"name":"create_token_account","discriminator":[147,241,123,100,244,132,174,118],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"owner"},{"name":"token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"token_mint"},{"kind":"account","path":"owner"}]}},{"name":"token_mint","address":"So11111111111111111111111111111111111111112"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bump","type":"u8"}]},{"name":"create_token_account_with_seed","discriminator":[125,191,239,140,66,8,9,228],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"owner"},{"name":"token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"token_mint"},{"kind":"account","path":"owner"},{"kind":"arg","path":"seed"}]}},{"name":"token_mint","address":"So11111111111111111111111111111111111111112"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bump","type":"u8"},{"name":"seed","type":"u32"}]},{"name":"init_token_ledger","discriminator":[157,116,66,59,137,180,49,157],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"token_ledger","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"u8"}]},{"name":"proxy_swap","discriminator":[19,44,130,148,72,56,44,238],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"sa_authority","optional":true,"pda":{"seeds":[{"kind":"const","value":[111,107,120,95,115,97]}]}},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}}]},{"name":"set_token_ledger","discriminator":[228,85,185,112,78,79,77,2],"accounts":[{"name":"token_ledger","writable":true},{"name":"token_account","docs":["SPL / Token-2022 token account to snapshot."]}],"args":[]},{"name":"swap","discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"payer","signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}}]},{"name":"swap_tob","discriminator":[170,41,85,177,132,80,31,53],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"trim_rate","type":"u8"}]},{"name":"swap_tob_enhanced","discriminator":[190,156,169,176,149,154,161,108],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"trim_rate","type":"u8"},{"name":"charge_rate","type":"u16"}]},{"name":"swap_tob_v2","docs":["Used to support commission distribution between parent and child nodes in TOB business"],"discriminator":[72,1,215,242,8,75,54,216],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"parent_commission_account","writable":true},{"name":"child_commission_account","writable":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"total_commission_info","type":"u32"},{"name":"parent_commission_rate","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"trim_rate","type":"u8"}]},{"name":"swap_tob_with_receiver","discriminator":[223,170,216,234,204,6,241,25],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"sol_receiver","docs":["Optional SOL receiver account","- None: normal swap or SOL stays with payer","- Some: SOL receiver when converting wSOL -> SOL"],"writable":true,"optional":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"trim_rate","type":"u8"}]},{"name":"swap_tob_with_receiver_token_ledger","discriminator":[239,93,10,202,161,134,127,130],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"sol_receiver","docs":["Optional SOL receiver account","- None: normal swap or SOL stays with payer","- Some: SOL receiver when converting wSOL -> SOL"],"writable":true,"optional":true},{"name":"token_ledger"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgsTokenLedger"}}},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"trim_rate","type":"u8"}]},{"name":"swap_tob_with_token_ledger","discriminator":[36,92,147,219,26,176,159,90],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"token_ledger"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgsTokenLedger"}}},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"trim_rate","type":"u8"}]},{"name":"swap_toc","discriminator":[187,201,212,51,16,155,236,60],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"}]},{"name":"swap_toc_v2","docs":["Used to support commission distribution between parent and child nodes in TOC business"],"discriminator":[127,214,107,189,23,90,47,104],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"parent_commission_account","writable":true},{"name":"child_commission_account","writable":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"total_commission_info","type":"u32"},{"name":"parent_commission_rate","type":"u32"},{"name":"platform_fee_rate","type":"u16"}]},{"name":"wrap_unwrap","discriminator":[220,101,139,249,41,190,118,199],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"payer_wsol_account","writable":true},{"name":"wsol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"temp_wsol_account","writable":true,"optional":true,"pda":{"seeds":[{"kind":"const","value":[116,101,109,112,95,119,115,111,108]},{"kind":"account","path":"payer"}]}},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"authority_pda","docs":["Used for signing fee transfers from authority_pda (SOL) or wsol_sa (WSOL)"],"writable":true,"optional":true},{"name":"wsol_sa","docs":["This is the authority_pda's associated token account for WSOL"],"writable":true,"optional":true},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"PlatformFeeWrapUnwrapArgs"}}}]},{"name":"wrap_unwrap_with_receiver","docs":["Wrap/Unwrap with optional specified receiver","- Wrap (SOL -> WSOL): receiver is WSOL token account (ATA)","- Unwrap (WSOL -> SOL): receiver is system account (EOA)","Transfer amount:","- From fee: amount_in","- To fee: amount_in - fees"],"discriminator":[123,25,47,134,233,167,171,170],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"payer_wsol_account","writable":true},{"name":"wsol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"temp_wsol_account","writable":true,"optional":true,"pda":{"seeds":[{"kind":"const","value":[116,101,109,112,95,119,115,111,108]},{"kind":"account","path":"payer"}]}},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"authority_pda","docs":["Used for signing fee transfers from authority_pda (SOL) or wsol_sa (WSOL)"],"writable":true,"optional":true},{"name":"wsol_sa","docs":["This is the authority_pda's associated token account for WSOL"],"writable":true,"optional":true},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"receiver","docs":["- Wrap: WSOL token account (ATA) to receive WSOL","- Unwrap: System account (EOA) to receive SOL"],"writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"PlatformFeeWrapUnwrapArgs"}}}]}],"accounts":[{"name":"TokenLedger","discriminator":[156,247,9,188,54,108,85,77]}],"events":[{"name":"SwapCpiEvent2","discriminator":[21,94,224,53,220,232,193,94]},{"name":"SwapEvent","discriminator":[64,198,205,232,38,8,113,226]},{"name":"SwapTobV2CpiEvent2","discriminator":[244,105,184,90,60,165,36,33]},{"name":"SwapTocV2CpiEvent2","discriminator":[102,16,181,62,201,71,212,41]},{"name":"SwapWithFeesCpiEvent2","discriminator":[12,134,38,93,167,151,42,69]},{"name":"SwapWithFeesCpiEventEnhanced2","discriminator":[84,79,219,247,218,156,219,177]}],"errors":[{"code":6000,"name":"RoutesCannotBeEmpty","msg":"Routes cannot be empty"},{"code":6001,"name":"TooManyRoutes","msg":"Too many routes"},{"code":6002,"name":"MultipleOrNoOutputNode","msg":"Multiple or no output node"},{"code":6003,"name":"MultipleOrNoInputNode","msg":"Multiple or no input node"},{"code":6004,"name":"NotAllNodesProcessed","msg":"Not all nodes are processed"},{"code":6005,"name":"InputIndexShouldStartFromZero","msg":"Input index should start from zero"},{"code":6006,"name":"CircularDependency","msg":"Circular dependency detected in DAG"},{"code":6007,"name":"InvalidWeight","msg":"Invalid weight"},{"code":6008,"name":"InvalidNodeOrder","msg":"Invalid node order"},{"code":6009,"name":"TargetNodeNotFound","msg":"Target node not found"},{"code":6010,"name":"MinReturnNotReached","msg":"Min return not reached"},{"code":6011,"name":"AmountInMustBeGreaterThanZero","msg":"amount_in must be greater than 0"},{"code":6012,"name":"SlippageTooHigh","msg":"slippage must be less than or equal to 100%"},{"code":6013,"name":"InvalidExpectAmountOut","msg":"invalid expect amount out"},{"code":6014,"name":"WeightsMustSumTo100","msg":"weights must sum to 100%"},{"code":6015,"name":"InvalidShareAmount","msg":"Invalid share amount"},{"code":6016,"name":"InvalidCommissionRate","msg":"Invalid commission rate"},{"code":6017,"name":"InvalidParentCommissionRate","msg":"Invalid parent commission rate"},{"code":6018,"name":"InvalidTrimRate","msg":"Invalid trim rate"},{"code":6019,"name":"InvalidChargeRate","msg":"Invalid charge rate"},{"code":6020,"name":"InvalidCommissionTemporaryTokenAccount","msg":"Invalid commission temporary token account"},{"code":6021,"name":"InvalidAccountsLength","msg":"Invalid accounts length"},{"code":6022,"name":"SwapAuthorityIsNotSigner","msg":"Swap authority is not signer"},{"code":6023,"name":"InvalidAuthorityPda","msg":"Invalid authority pda"},{"code":6024,"name":"InvalidSwapAuthority","msg":"Invalid swap authority"},{"code":6025,"name":"InvalidProgramId","msg":"Invalid program id"},{"code":6026,"name":"InvalidPool","msg":"Invalid pool"},{"code":6027,"name":"InvalidTokenMint","msg":"Invalid token mint"},{"code":6028,"name":"CalculationError","msg":"Calculation error"},{"code":6029,"name":"InvalidSanctumLstStateListData","msg":"Invalid sanctum lst state list data"},{"code":6030,"name":"InvalidSanctumLstStateListIndex","msg":"Invalid sanctum lst state list index"},{"code":6031,"name":"InvalidSanctumSwapAccounts","msg":"Invalid sanctum swap accounts"},{"code":6032,"name":"InvalidSwapAuthorityAccount","msg":"Invalid swap authority account"},{"code":6033,"name":"InvalidBridgeSeed","msg":"Bridge Seed Error"},{"code":6034,"name":"InvalidBundleInput","msg":"Invalid accounts and instruction length"},{"code":6035,"name":"InvalidPlatformFeeRate","msg":"Invalid platform fee rate"},{"code":6036,"name":"AmountOutMustBeGreaterThanZero","msg":"Amount out must be greater than 0"},{"code":6037,"name":"InvalidDampingTerm","msg":"Invalid DampingTerm"},{"code":6038,"name":"InvalidMint","msg":"Invalid mint"},{"code":6039,"name":"InvalidPlatformFeeAccount","msg":"Invalid platform fee account"},{"code":6040,"name":"InvalidTrimAccount","msg":"Invalid trim account"},{"code":6041,"name":"InvalidChargeAccount","msg":"Invalid charge account"},{"code":6042,"name":"InvalidPlatformFeeAmount","msg":"Invalid platform fee amount"},{"code":6043,"name":"InvalidFeeTokenAccount","msg":"Invalid fee token account"},{"code":6044,"name":"InvalidSaAuthority","msg":"Invalid sa authority"},{"code":6045,"name":"InvalidNodeFromAccounts","msg":"Invalid node from accounts"},{"code":6046,"name":"InvalidNodeToAccounts","msg":"Invalid node to accounts"},{"code":6047,"name":"InvalidSourceTokenAccount","msg":"Invalid source token account"},{"code":6048,"name":"InvalidTokenAccount","msg":"Invalid token account"},{"code":6049,"name":"InvalidDestinationTokenAccount","msg":"Invalid destination token account"},{"code":6050,"name":"CommissionAccountIsNone","msg":"Commission account is none"},{"code":6051,"name":"PlatformFeeAccountIsNone","msg":"Platform fee account is none"},{"code":6052,"name":"TrimAccountIsNone","msg":"Trim account is none"},{"code":6053,"name":"ChargeAccountIsNone","msg":"Charge account is none"},{"code":6054,"name":"InvalidFeeAccount","msg":"Invalid fee account"},{"code":6055,"name":"InvalidTokenOwner","msg":"Invalid token owner"},{"code":6056,"name":"SaAuthorityIsNone","msg":"Sa authority is none"},{"code":6057,"name":"SourceTokenSaIsNone","msg":"Source token sa is none"},{"code":6058,"name":"SourceTokenProgramIsNone","msg":"Source token program is none"},{"code":6059,"name":"DestinationTokenSaIsNone","msg":"Destination token sa is none"},{"code":6060,"name":"DestinationTokenProgramIsNone","msg":"Destination token program is none"},{"code":6061,"name":"ResultMustBeGreaterThanZero","msg":"Calculation result must be greater than zero"},{"code":6062,"name":"InvalidAccountData","msg":"Invalid account data"},{"code":6063,"name":"InvalidRfqParameters","msg":"Invalid RFQ parameters"},{"code":6064,"name":"TobAuthorityPdaRequired","msg":"TOB mode requires authority PDA"},{"code":6065,"name":"TobWsolSaRequired","msg":"TOB mode with WSOL fees requires wsol_sa account"},{"code":6066,"name":"InvalidWsolSa","msg":"Invalid WSOL SA account"},{"code":6067,"name":"InvalidCommissionAccount","msg":"Invalid commission account"},{"code":6068,"name":"InvalidActualAmountIn","msg":"Invalid actual amount in"},{"code":6069,"name":"UnexpectedSaTokenAccount","msg":"Unexpected SA token account in CPI"},{"code":6070,"name":"InvalidSourceTokenSaMint","msg":"Invalid source token sa mint"},{"code":6071,"name":"InvalidDestinationTokenSaMint","msg":"Invalid destination token sa mint"},{"code":6072,"name":"InsufficientFunds","msg":"Insufficient funds"},{"code":6073,"name":"SaAuthorityLamportsDecreased","msg":"Sa authority lamports decreased"},{"code":6074,"name":"InvalidTokenProgram","msg":"Invalid token program"},{"code":6075,"name":"InvalidSigner","msg":"Invalid signer"},{"code":6076,"name":"InvalidAssociatedTokenProgram","msg":"Invalid associated token program"},{"code":6077,"name":"TokenProgramIsNone","msg":"Token program is none"},{"code":6078,"name":"AssociatedTokenProgramIsNone","msg":"Associated token program is none"},{"code":6079,"name":"SystemProgramIsNone","msg":"System program is none"},{"code":6080,"name":"InsufficientBalance","msg":"Insufficient balance for transfer"},{"code":6081,"name":"SolReceiverMustBeSystemAccount","msg":"SOL receiver must be a system account"},{"code":6082,"name":"SolReceiverRequiresAccCloseFlag","msg":"SOL receiver requires acc_close_flag to be true"},{"code":6083,"name":"DestinationMustBeWsolForSolReceiver","msg":"Destination must be wSOL when sol_receiver is specified"},{"code":6084,"name":"InvalidGoonfiParameters","msg":"Invalid goonfi parameters"},{"code":6085,"name":"AdapterAbort","msg":"Adapter abort"},{"code":6086,"name":"InconsistentFeeAccountTypes","msg":"Inconsistent fee account types"},{"code":6087,"name":"ReceiverMustBeTokenAccount","msg":"Receiver must be a token account"},{"code":6088,"name":"ReceiverMustBeWsolAccount","msg":"Receiver must be a WSOL token account"},{"code":6089,"name":"InconsistentCommissionAndTrimAccountTypes","msg":"The commission fee account and trim account are different types."},{"code":6090,"name":"NoAvailableNodeIndexForReturnToStart","msg":"No available node index to support returning to START_INDEX"},{"code":6091,"name":"InvalidTokenLedgerTokenAccount","msg":"Invalid token ledger token account"},{"code":6092,"name":"TokenLedgerDeltaCalculationFailure","msg":"Token ledger delta calculation failed"},{"code":6093,"name":"InvalidTokenAccountOwner","msg":"Token account wallet owner mismatch"},{"code":6094,"name":"CashbackFlagMismatch","msg":"Cashback flag mismatch"}],"types":[{"name":"Dex","type":{"kind":"enum","variants":[{"name":"SplTokenSwap"},{"name":"StableSwap"},{"name":"Whirlpool"},{"name":"MeteoraDynamicpool"},{"name":"RaydiumSwap"},{"name":"RaydiumStableSwap"},{"name":"RaydiumClmmSwap"},{"name":"AldrinExchangeV1"},{"name":"AldrinExchangeV2"},{"name":"LifinityV1"},{"name":"LifinityV2"},{"name":"RaydiumClmmSwapV2"},{"name":"FluxBeam"},{"name":"MeteoraDlmm"},{"name":"RaydiumCpmmSwap"},{"name":"OpenBookV2"},{"name":"WhirlpoolV2"},{"name":"Phoenix"},{"name":"ObricV2"},{"name":"SanctumAddLiq"},{"name":"SanctumRemoveLiq"},{"name":"SanctumNonWsolSwap"},{"name":"SanctumWsolSwap"},{"name":"PumpfunBuy","fields":[{"name":"is_cash_back","type":"bool"}]},{"name":"PumpfunSell","fields":[{"name":"is_cash_back","type":"bool"}]},{"name":"StabbleSwap"},{"name":"SanctumRouter"},{"name":"MeteoraVaultDeposit"},{"name":"MeteoraVaultWithdraw"},{"name":"Saros"},{"name":"MeteoraLst"},{"name":"Solfi"},{"name":"QualiaSwap"},{"name":"Zerofi"},{"name":"PumpfunammBuy","fields":[{"name":"is_cash_back","type":"bool"}]},{"name":"PumpfunammSell","fields":[{"name":"is_cash_back","type":"bool"}]},{"name":"Virtuals"},{"name":"VertigoBuy"},{"name":"VertigoSell"},{"name":"PerpetualsAddLiq"},{"name":"PerpetualsRemoveLiq"},{"name":"PerpetualsSwap"},{"name":"RaydiumLaunchpad"},{"name":"LetsBonkFun"},{"name":"Woofi"},{"name":"MeteoraDbc"},{"name":"MeteoraDlmmSwap2"},{"name":"MeteoraDAMMV2"},{"name":"Gavel"},{"name":"BoopfunBuy"},{"name":"BoopfunSell"},{"name":"MeteoraDbc2"},{"name":"GooseFX"},{"name":"Dooar"},{"name":"Numeraire"},{"name":"SaberDecimalWrapperDeposit"},{"name":"SaberDecimalWrapperWithdraw"},{"name":"SarosDlmm"},{"name":"OneDexSwap"},{"name":"Manifest"},{"name":"ByrealClmm"},{"name":"PancakeSwapV3Swap"},{"name":"PancakeSwapV3SwapV2"},{"name":"Tessera"},{"name":"SolRfq","fields":[{"name":"rfq_id","type":"u64"},{"name":"expected_maker_amount","type":"u64"},{"name":"expected_taker_amount","type":"u64"},{"name":"maker_send_amount","type":"u64"},{"name":"taker_send_amount","type":"u64"},{"name":"expiry","type":"u64"},{"name":"maker_use_native_sol","type":"bool"},{"name":"taker_use_native_sol","type":"bool"}]},{"name":"Humidifi"},{"name":"HeavenBuy"},{"name":"HeavenSell"},{"name":"SolfiV2"},{"name":"Goonfi"},{"name":"MoonitBuy"},{"name":"MoonitSell"},{"name":"RaydiumSwapV2"},{"name":"Whalestreet"},{"name":"SugarMoneyBuy","fields":[{"name":"bonding_curve_bump","type":"u8"},{"name":"bonding_curve_sol_associated_account_bump","type":"u8"}]},{"name":"SugarMoneySell","fields":[{"name":"bonding_curve_bump","type":"u8"},{"name":"bonding_curve_sol_associated_account_bump","type":"u8"}]},{"name":"MeteoraDAMMV2Swap2"},{"name":"AlphaQ"},{"name":"FutarchyAmm"},{"name":"PumpfunBuyV2"},{"name":"PumpfunSellV2"},{"name":"HumidifiSwap2","fields":[{"name":"swap_id","type":"u64"}]},{"name":"Scorch","fields":[{"name":"id","type":"u128"}]},{"name":"JupiterLendDeposit"},{"name":"JupiterLendRedeem"},{"name":"TaurusFi"},{"name":"BisonFi"},{"name":"GoonfiV2"},{"name":"Quantum"},{"name":"BoopfunBuy2"},{"name":"BoopfunSell2"},{"name":"ByrealClmm2"},{"name":"Dooar2"},{"name":"HeavenBuy2"},{"name":"HeavenSell2"},{"name":"MoonitBuy2"},{"name":"MoonitSell2"},{"name":"SaberDecimalWrapperDeposit2"},{"name":"SaberDecimalWrapperWithdraw2"},{"name":"ByrealPropAmm"},{"name":"SanctumPrefundSwapViaStake","fields":[{"name":"swap_id","type":"u64"}]},{"name":"AbyssAmm"},{"name":"Aquifer"},{"name":"WhalestreetV2","fields":[{"name":"auth_amount_in","type":"u64"},{"name":"auth","type":"u64"}]},{"name":"SolfiV2WithSig","fields":[{"name":"unix_timestamp","type":"u64"},{"name":"msg_amount_in","type":"u64"},{"name":"expect_amount_out","type":"u64"},{"name":"slippage","type":"u16"},{"name":"user_request_slot","type":"u64"},{"name":"signature","type":{"array":["u8",64]}},{"name":"recovery_id","type":"u8"}]},{"name":"FusionAmm"},{"name":"ScaleAmmBuy"},{"name":"ScaleAmmSell"},{"name":"ScaleVmmBuy"},{"name":"ScaleVmmSell"},{"name":"PumpfunBuy3"},{"name":"PumpfunSell3"},{"name":"PumpfunammBuy2"},{"name":"PumpfunammSell2"},{"name":"Riptide"},{"name":"ZerofiSwapV2"},{"name":"TaurusFiV2"},{"name":"SolRfqV2","fields":[{"name":"taker_side","type":{"defined":{"name":"RfqSide"}}},{"name":"rfq_id","type":"u64"},{"name":"expire_at","type":"i64"},{"name":"levels","type":{"vec":{"defined":{"name":"RfqLevel"}}}}]}]}},{"name":"PlatformFeeWrapUnwrapArgs","type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"amount_in","type":"u64"},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"tob","type":"bool"}]}},{"name":"RfqLevel","type":{"kind":"struct","fields":[{"name":"base_atoms","type":"u64"},{"name":"quote_atoms","type":"u64"}]}},{"name":"RfqSide","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Bid"},{"name":"Ask"}]}},{"name":"Route","type":{"kind":"struct","fields":[{"name":"dex","type":{"defined":{"name":"Dex"}}},{"name":"weight","type":"u16"},{"name":"index","type":"u8"}]}},{"name":"SwapArgs","type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"amount_in","type":"u64"},{"name":"expect_amount_out","type":"u64"},{"name":"slippage","type":"u16"},{"name":"routes","type":{"vec":{"defined":{"name":"Route"}}}}]}},{"name":"SwapArgsTokenLedger","docs":["Swap args for token-ledger swaps: no `amount_in` parameter.","`amount_in` is derived on-chain from `token_ledger` delta."],"type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"expect_amount_out","type":"u64"},{"name":"slippage","type":"u16"},{"name":"routes","type":{"vec":{"defined":{"name":"Route"}}}}]}},{"name":"SwapCpiEvent2","type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"source_mint","type":"pubkey"},{"name":"destination_mint","type":"pubkey"},{"name":"source_token_account_owner","type":"pubkey"},{"name":"destination_token_account_owner","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"source_token_change","type":"u64"},{"name":"destination_token_change","type":"u64"}]}},{"name":"SwapEvent","type":{"kind":"struct","fields":[{"name":"dex","type":{"defined":{"name":"Dex"}}},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"}]}},{"name":"SwapTobV2CpiEvent2","type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"source_mint","type":"pubkey"},{"name":"destination_mint","type":"pubkey"},{"name":"source_token_account_owner","type":"pubkey"},{"name":"destination_token_account_owner","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"source_token_change","type":"u64"},{"name":"destination_token_change","type":"u64"},{"name":"commission_direction","type":"bool"},{"name":"total_commission_rate","type":"u32"},{"name":"parent_commission_rate","type":"u32"},{"name":"parent_commission_amount","type":"u64"},{"name":"parent_commission_account","type":"pubkey"},{"name":"child_commission_rate","type":"u32"},{"name":"child_commission_amount","type":"u64"},{"name":"child_commission_account","type":"pubkey"},{"name":"platform_fee_rate","type":"u16"},{"name":"platform_fee_amount","type":"u64"},{"name":"platform_fee_account","type":"pubkey"},{"name":"trim_rate","type":"u8"},{"name":"trim_amount","type":"u64"},{"name":"trim_account","type":"pubkey"}]}},{"name":"SwapTocV2CpiEvent2","type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"source_mint","type":"pubkey"},{"name":"destination_mint","type":"pubkey"},{"name":"source_token_account_owner","type":"pubkey"},{"name":"destination_token_account_owner","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"source_token_change","type":"u64"},{"name":"destination_token_change","type":"u64"},{"name":"commission_direction","type":"bool"},{"name":"total_commission_rate","type":"u32"},{"name":"parent_commission_rate","type":"u32"},{"name":"parent_commission_amount","type":"u64"},{"name":"parent_commission_account","type":"pubkey"},{"name":"child_commission_rate","type":"u32"},{"name":"child_commission_amount","type":"u64"},{"name":"child_commission_account","type":"pubkey"},{"name":"platform_fee_rate","type":"u16"},{"name":"platform_fee_amount","type":"u64"},{"name":"platform_fee_account","type":"pubkey"}]}},{"name":"SwapWithFeesCpiEvent2","type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"source_mint","type":"pubkey"},{"name":"destination_mint","type":"pubkey"},{"name":"source_token_account_owner","type":"pubkey"},{"name":"destination_token_account_owner","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"source_token_change","type":"u64"},{"name":"destination_token_change","type":"u64"},{"name":"commission_direction","type":"bool"},{"name":"commission_rate","type":"u32"},{"name":"commission_amount","type":"u64"},{"name":"commission_account","type":"pubkey"},{"name":"platform_fee_rate","type":"u16"},{"name":"platform_fee_amount","type":"u64"},{"name":"platform_fee_account","type":"pubkey"},{"name":"trim_rate","type":"u8"},{"name":"trim_amount","type":"u64"},{"name":"trim_account","type":"pubkey"}]}},{"name":"SwapWithFeesCpiEventEnhanced2","type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"source_mint","type":"pubkey"},{"name":"destination_mint","type":"pubkey"},{"name":"source_token_account_owner","type":"pubkey"},{"name":"destination_token_account_owner","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"source_token_change","type":"u64"},{"name":"destination_token_change","type":"u64"},{"name":"commission_direction","type":"bool"},{"name":"commission_rate","type":"u32"},{"name":"commission_amount","type":"u64"},{"name":"commission_account","type":"pubkey"},{"name":"platform_fee_rate","type":"u16"},{"name":"platform_fee_amount","type":"u64"},{"name":"platform_fee_account","type":"pubkey"},{"name":"trim_rate","type":"u8"},{"name":"trim_amount","type":"u64"},{"name":"trim_account","type":"pubkey"},{"name":"charge_rate","type":"u16"},{"name":"charge_amount","type":"u64"},{"name":"charge_account","type":"pubkey"}]}},{"name":"TokenLedger","docs":["Token ledger PDA used to snapshot a token account balance within a transaction.","","Seeds: `[b\"token_ledger\", [index]]`, where `index = 0..=255` (u8)."],"type":{"kind":"struct","fields":[{"name":"token_account","type":"pubkey"},{"name":"amount","type":"u64"}]}}],"constants":[{"name":"SEED_SA","type":"bytes","value":"[111, 107, 120, 95, 115, 97]"}]} \ No newline at end of file diff --git a/idls/okx_lab_v1.6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma.json b/idls/okx_lab_v1.6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma.json new file mode 100644 index 0000000..723685a --- /dev/null +++ b/idls/okx_lab_v1.6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma.json @@ -0,0 +1 @@ +{"address":"6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma","metadata":{"name":"dex_solana","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"claim","discriminator":[62,198,214,193,213,159,108,210],"accounts":[{"name":"signer","writable":true,"signer":true,"address":"CjoV5B96reuCfPh2rRK11G1QptG97jZdyZArTn3EN1Mj"},{"name":"receiver","writable":true},{"name":"source_token_account","writable":true,"optional":true},{"name":"destination_token_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"address":"HV1KXxWFaSeriyFvXyx48FqG9BoFbfinB8njCJonqP7K"},{"name":"token_mint","optional":true},{"name":"token_program","optional":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[]},{"name":"claim_cashback_pumpfun","discriminator":[83,1,245,125,201,207,209,249],"accounts":[{"name":"signer","signer":true,"address":"CjoV5B96reuCfPh2rRK11G1QptG97jZdyZArTn3EN1Mj"},{"name":"sa_authority","docs":["PumpFun transfers claimed SOL directly to this account (account[0] in the CPI)."],"writable":true,"address":"HV1KXxWFaSeriyFvXyx48FqG9BoFbfinB8njCJonqP7K"},{"name":"user_volume_accumulator","docs":["Holds native SOL accumulated as cashback; emptied by PumpFun during claim."],"writable":true},{"name":"receiver","docs":["after PumpFun deposits it there."],"writable":true},{"name":"event_authority","address":"Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"pumpfun_program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"}],"args":[]},{"name":"claim_cashback_pumpswap","discriminator":[253,242,176,173,217,78,100,25],"accounts":[{"name":"signer","signer":true,"address":"CjoV5B96reuCfPh2rRK11G1QptG97jZdyZArTn3EN1Mj"},{"name":"sa_authority","writable":true,"address":"HV1KXxWFaSeriyFvXyx48FqG9BoFbfinB8njCJonqP7K"},{"name":"user_volume_accumulator","writable":true},{"name":"wsol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"spl_token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"uva_wsol_ata","writable":true},{"name":"sa_wsol_ata","docs":["PumpSwap requires account[5] to be a token account owned by authority_pda","(same owner as user_volume_accumulator). WSOL is deposited here by PumpSwap,","then forwarded to receiver_wsol_ata via a second SPL transfer."],"writable":true},{"name":"receiver_wsol_ata","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","address":"GS4CU59F31iL7aR2Q8zVS8DRrcRnXX1yjQ66TqNVQnaR"},{"name":"pumpswap_program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"}],"args":[]},{"name":"create_token_account","discriminator":[147,241,123,100,244,132,174,118],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"owner"},{"name":"token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"token_mint"},{"kind":"account","path":"owner"}]}},{"name":"token_mint","address":"So11111111111111111111111111111111111111112"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bump","type":"u8"}]},{"name":"create_token_account_with_seed","discriminator":[125,191,239,140,66,8,9,228],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"owner"},{"name":"token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"token_mint"},{"kind":"account","path":"owner"},{"kind":"arg","path":"seed"}]}},{"name":"token_mint","address":"So11111111111111111111111111111111111111112"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bump","type":"u8"},{"name":"seed","type":"u32"}]},{"name":"proxy_swap","discriminator":[19,44,130,148,72,56,44,238],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"sa_authority","optional":true,"pda":{"seeds":[{"kind":"const","value":[111,107,120,95,115,97]}]}},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"}],"args":[{"name":"data","type":{"defined":{"name":"SwapArgs"}}},{"name":"order_id","type":"u64"}]},{"name":"swap","discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"payer","signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"}],"args":[{"name":"data","type":{"defined":{"name":"SwapArgs"}}},{"name":"order_id","type":"u64"}]},{"name":"swap_tob_v3","discriminator":[14,191,44,246,142,225,224,157],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"commission_info","type":"u32"},{"name":"trim_rate","type":"u8"},{"name":"platform_fee_rate","type":"u16"},{"name":"order_id","type":"u64"}]},{"name":"swap_tob_v3_enhanced","discriminator":[236,71,155,68,198,98,14,118],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"commission_info","type":"u32"},{"name":"trim_rate","type":"u8"},{"name":"charge_rate","type":"u16"},{"name":"platform_fee_rate","type":"u16"},{"name":"order_id","type":"u64"}]},{"name":"swap_tob_v3_with_receiver","docs":["Swap ToB with optional specified receiver","- For normal token swaps: sol_receiver should be None","- For swap to SOL with custom receiver: sol_receiver should be Some and acc_close_flag must be true"],"discriminator":[63,114,246,131,51,2,247,29],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"sol_receiver","docs":["Optional SOL receiver account","- None: normal swap or SOL stays with payer","- Some: SOL receiver when converting wSOL -> SOL"],"writable":true,"optional":true}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"commission_info","type":"u32"},{"name":"trim_rate","type":"u8"},{"name":"platform_fee_rate","type":"u16"},{"name":"order_id","type":"u64"}]},{"name":"swap_v3","discriminator":[240,224,38,33,176,31,241,175],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"order_id","type":"u64"}]},{"name":"swap_v3_with_cpi_event","discriminator":[184,104,79,156,107,182,120,138],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"order_id","type":"u64"}]},{"name":"wrap_unwrap_v3","discriminator":[180,178,191,54,70,8,13,224],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"payer_wsol_account","writable":true},{"name":"wsol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"temp_wsol_account","writable":true,"optional":true,"pda":{"seeds":[{"kind":"const","value":[116,101,109,112,95,119,115,111,108]},{"kind":"account","path":"payer"}]}},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"authority_pda","docs":["Used for signing fee transfers from authority_pda (SOL) or wsol_sa (WSOL)"],"writable":true,"optional":true},{"name":"wsol_sa","docs":["This is the authority_pda's associated token account for WSOL"],"writable":true,"optional":true},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"args","type":{"defined":{"name":"PlatformFeeWrapUnwrapArgs"}}}]},{"name":"wrap_unwrap_v3_with_receiver","docs":["Wrap/Unwrap with optional specified receiver","- Wrap (SOL -> WSOL): receiver is WSOL token account (ATA)","- Unwrap (WSOL -> SOL): receiver is system account (EOA)","Transfer amount:","- From fee: amount_in","- To fee: amount_in - fees"],"discriminator":[70,211,190,165,47,40,213,95],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"payer_wsol_account","writable":true},{"name":"wsol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"temp_wsol_account","writable":true,"optional":true,"pda":{"seeds":[{"kind":"const","value":[116,101,109,112,95,119,115,111,108]},{"kind":"account","path":"payer"}]}},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"authority_pda","docs":["Used for signing fee transfers from authority_pda (SOL) or wsol_sa (WSOL)"],"writable":true,"optional":true},{"name":"wsol_sa","docs":["This is the authority_pda's associated token account for WSOL"],"writable":true,"optional":true},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"receiver","docs":["- Wrap: WSOL token account (ATA) to receive WSOL","- Unwrap: System account (EOA) to receive SOL"],"writable":true}],"args":[{"name":"args","type":{"defined":{"name":"PlatformFeeWrapUnwrapArgs"}}}]}],"events":[{"name":"SwapEvent","discriminator":[64,198,205,232,38,8,113,226]},{"name":"SwapWithFeesCpiEvent","discriminator":[189,97,67,12,37,209,247,29]}],"errors":[{"code":6000,"name":"TooManyHops","msg":"Too many hops"},{"code":6001,"name":"MinReturnNotReached","msg":"Min return not reached"},{"code":6002,"name":"AmountInMustBeGreaterThanZero","msg":"amount_in must be greater than 0"},{"code":6003,"name":"InvalidExpectAmountOut","msg":"invalid expect amount out"},{"code":6004,"name":"AmountsAndRoutesMustHaveTheSameLength","msg":"amounts and routes must have the same length"},{"code":6005,"name":"TotalAmountsMustBeEqualToAmountIn","msg":"total_amounts must be equal to amount_in"},{"code":6006,"name":"DexesAndWeightsMustHaveTheSameLength","msg":"dexes and weights must have the same length"},{"code":6007,"name":"WeightsMustSumTo100","msg":"weights must sum to 100"},{"code":6008,"name":"InvalidSourceTokenAccount","msg":"Invalid source token account"},{"code":6009,"name":"InvalidDestinationTokenAccount","msg":"Invalid destination token account"},{"code":6010,"name":"InvalidTokenAccount","msg":"Invalid token account"},{"code":6011,"name":"InvalidCommissionRate","msg":"Invalid commission rate"},{"code":6012,"name":"InvalidTrimRate","msg":"Invalid trim rate"},{"code":6013,"name":"InvalidChargeRate","msg":"Invalid charge rate"},{"code":6014,"name":"InvalidCommissionTokenAccount","msg":"Invalid commission token account"},{"code":6015,"name":"InvalidCommissionTemporaryTokenAccount","msg":"Invalid commission temporary token account"},{"code":6016,"name":"InvalidAccountsLength","msg":"Invalid accounts length"},{"code":6017,"name":"InvalidHopAccounts","msg":"Invalid hop accounts"},{"code":6018,"name":"InvalidHopFromAccount","msg":"Invalid hop from account"},{"code":6019,"name":"SwapAuthorityIsNotSigner","msg":"Swap authority is not signer"},{"code":6020,"name":"InvalidAuthorityPda","msg":"Invalid authority pda"},{"code":6021,"name":"InvalidSwapAuthority","msg":"Invalid swap authority"},{"code":6022,"name":"InvalidProgramId","msg":"Invalid program id"},{"code":6023,"name":"InvalidPool","msg":"Invalid pool"},{"code":6024,"name":"InvalidTokenMint","msg":"Invalid token mint"},{"code":6025,"name":"CalculationError","msg":"Calculation error"},{"code":6026,"name":"InvalidSanctumLstStateListData","msg":"Invalid sanctum lst state list data"},{"code":6027,"name":"InvalidSanctumLstStateListIndex","msg":"Invalid sanctum lst state list index"},{"code":6028,"name":"InvalidSanctumSwapAccounts","msg":"Invalid sanctum swap accounts"},{"code":6029,"name":"InvalidSwapAuthorityAccounts","msg":"Invalid swap authority account"},{"code":6030,"name":"InvalidBridgeSeed","msg":"Bridge Seed Error"},{"code":6031,"name":"InvalidBundleInput","msg":"Invalid accounts and instruction length"},{"code":6032,"name":"MissingSaAccount","msg":"SA is required"},{"code":6033,"name":"InvalidPlatformFeeRate","msg":"Invalid platform fee rate"},{"code":6034,"name":"AmountOutMustBeGreaterThanZero","msg":"Amount out must be greater than 0"},{"code":6035,"name":"InvalidDampingTerm","msg":"Invalid DampingTerm"},{"code":6036,"name":"InvalidMint","msg":"Invalid mint"},{"code":6037,"name":"InvalidPlatformFeeAmount","msg":"Invalid platform fee amount"},{"code":6038,"name":"InvalidFeeTokenAccount","msg":"Invalid fee token account"},{"code":6039,"name":"InvalidSaAuthority","msg":"Invalid sa authority"},{"code":6040,"name":"CommissionAccountIsNone","msg":"Commission account is none"},{"code":6041,"name":"PlatformFeeAccountIsNone","msg":"Platform fee account is none"},{"code":6042,"name":"TrimAccountIsNone","msg":"Trim account is none"},{"code":6043,"name":"ChargeAccountIsNone","msg":"Charge account is none"},{"code":6044,"name":"InvalidFeeAccount","msg":"Invalid fee account"},{"code":6045,"name":"InvalidSourceTokenSa","msg":"Invalid source token sa"},{"code":6046,"name":"SaAuthorityIsNone","msg":"Sa authority is none"},{"code":6047,"name":"SourceTokenSaIsNone","msg":"Source token sa is none"},{"code":6048,"name":"SourceTokenProgramIsNone","msg":"Source token program is none"},{"code":6049,"name":"DestinationTokenSaIsNone","msg":"Destination token sa is none"},{"code":6050,"name":"DestinationTokenProgramIsNone","msg":"Destination token program is none"},{"code":6051,"name":"ResultMustBeGreaterThanZero","msg":"Calculation result must be greater than zero"},{"code":6052,"name":"InvalidAccountData","msg":"Invalid account data"},{"code":6053,"name":"InvalidRfqParameters","msg":"Invalid RFQ parameters"},{"code":6054,"name":"TobAuthorityPdaRequired","msg":"TOB mode requires authority PDA"},{"code":6055,"name":"TobWsolSaRequired","msg":"TOB mode with WSOL fees requires wsol_sa account"},{"code":6056,"name":"InvalidWsolSa","msg":"Invalid WSOL SA account"},{"code":6057,"name":"InvalidTrimAccount","msg":"Invalid trim account"},{"code":6058,"name":"InvalidChargeAccount","msg":"Invalid charge account"},{"code":6059,"name":"InvalidCommissionAccount","msg":"Invalid commission account"},{"code":6060,"name":"InvalidPlatformFeeAccount","msg":"Invalid platform fee account"},{"code":6061,"name":"InvalidActualAmountIn","msg":"Invalid actual amount in"},{"code":6062,"name":"UnexpectedSaTokenAccount","msg":"Unexpected SA token account in CPI"},{"code":6063,"name":"InvalidSourceTokenSaMint","msg":"Invalid source token sa mint"},{"code":6064,"name":"InvalidDestinationTokenSaMint","msg":"Invalid destination token sa mint"},{"code":6065,"name":"AdapterAbort","msg":"Adapter abort"},{"code":6066,"name":"InsufficientFunds","msg":"Insufficient funds"},{"code":6067,"name":"InvalidDiffLamports","msg":"Invalid diff lamports"},{"code":6068,"name":"InvalidTokenProgram","msg":"Invalid token program"},{"code":6069,"name":"InvalidSigner","msg":"Invalid signer"},{"code":6070,"name":"InvalidAssociatedTokenProgram","msg":"Invalid associated token program"},{"code":6071,"name":"SolReceiverMustBeSystemAccount","msg":"SOL receiver must be a system account"},{"code":6072,"name":"InsufficientBalance","msg":"Insufficient balance for transfer"},{"code":6073,"name":"SolReceiverRequiresAccCloseFlag","msg":"SOL receiver requires acc_close_flag to be true"},{"code":6074,"name":"DestinationMustBeWsolForSolReceiver","msg":"Destination must be wSOL when sol_receiver is specified"},{"code":6075,"name":"InvalidGoonfiParameters","msg":"Invalid Goonfi parameters"},{"code":6076,"name":"InvalidTrimAmount","msg":"Invalid trim amount"},{"code":6077,"name":"ReceiverMustBeTokenAccount","msg":"Receiver must be a token account for wrap operation"},{"code":6078,"name":"ReceiverMustBeWsolAccount","msg":"Receiver must be a WSOL token account for wrap operation"},{"code":6079,"name":"TokenProgramIsNone","msg":"Token program is none"},{"code":6080,"name":"AssociatedTokenProgramIsNone","msg":"Associated token program is none"},{"code":6081,"name":"SystemProgramIsNone","msg":"System program is none"}],"types":[{"name":"Dex","type":{"kind":"enum","variants":[{"name":"SplTokenSwap"},{"name":"StableSwap"},{"name":"Whirlpool"},{"name":"MeteoraDynamicpool"},{"name":"RaydiumSwap"},{"name":"RaydiumStableSwap"},{"name":"RaydiumClmmSwap"},{"name":"AldrinExchangeV1"},{"name":"AldrinExchangeV2"},{"name":"LifinityV1"},{"name":"LifinityV2"},{"name":"RaydiumClmmSwapV2"},{"name":"FluxBeam"},{"name":"MeteoraDlmm"},{"name":"RaydiumCpmmSwap"},{"name":"OpenBookV2"},{"name":"WhirlpoolV2"},{"name":"Phoenix"},{"name":"ObricV2"},{"name":"SanctumAddLiq"},{"name":"SanctumRemoveLiq"},{"name":"SanctumNonWsolSwap"},{"name":"SanctumWsolSwap"},{"name":"PumpfunBuy"},{"name":"PumpfunSell"},{"name":"StabbleSwap"},{"name":"SanctumRouter"},{"name":"MeteoraVaultDeposit"},{"name":"MeteoraVaultWithdraw"},{"name":"Saros"},{"name":"MeteoraLst"},{"name":"Solfi"},{"name":"QualiaSwap"},{"name":"Zerofi"},{"name":"PumpfunammBuy"},{"name":"PumpfunammSell"},{"name":"Virtuals"},{"name":"VertigoBuy"},{"name":"VertigoSell"},{"name":"PerpetualsAddLiq"},{"name":"PerpetualsRemoveLiq"},{"name":"PerpetualsSwap"},{"name":"RaydiumLaunchpad"},{"name":"LetsBonkFun"},{"name":"Woofi"},{"name":"MeteoraDbc"},{"name":"MeteoraDlmmSwap2"},{"name":"MeteoraDAMMV2"},{"name":"Gavel"},{"name":"BoopfunBuy"},{"name":"BoopfunSell"},{"name":"MeteoraDbc2"},{"name":"GooseFX"},{"name":"Dooar"},{"name":"Numeraire"},{"name":"SaberDecimalWrapperDeposit"},{"name":"SaberDecimalWrapperWithdraw"},{"name":"SarosDlmm"},{"name":"OneDexSwap"},{"name":"Manifest"},{"name":"ByrealClmm"},{"name":"PancakeSwapV3Swap"},{"name":"PancakeSwapV3SwapV2"},{"name":"Tessera"},{"name":"SolRfq","fields":[{"name":"rfq_id","type":"u64"},{"name":"expected_maker_amount","type":"u64"},{"name":"expected_taker_amount","type":"u64"},{"name":"maker_send_amount","type":"u64"},{"name":"taker_send_amount","type":"u64"},{"name":"expiry","type":"u64"},{"name":"maker_use_native_sol","type":"bool"},{"name":"taker_use_native_sol","type":"bool"}]},{"name":"PumpfunBuy2"},{"name":"PumpfunammBuy2"},{"name":"Humidifi"},{"name":"HeavenBuy"},{"name":"HeavenSell"},{"name":"SolfiV2"},{"name":"PumpfunBuy3"},{"name":"PumpfunSell3"},{"name":"PumpfunammBuy3"},{"name":"PumpfunammSell3"},{"name":"Goonfi"},{"name":"MoonitBuy"},{"name":"MoonitSell"},{"name":"RaydiumSwapV2"},{"name":"Whalestreet"},{"name":"SugarMoneyBuy","fields":[{"name":"bonding_curve_bump","type":"u8"},{"name":"bonding_curve_sol_associated_account_bump","type":"u8"}]},{"name":"SugarMoneySell","fields":[{"name":"bonding_curve_bump","type":"u8"},{"name":"bonding_curve_sol_associated_account_bump","type":"u8"}]},{"name":"MeteoraDAMMV2Swap2"},{"name":"AlphaQ"},{"name":"FutarchyAmm"},{"name":"PumpfunSell2"},{"name":"HumidifiSwap2","fields":[{"name":"swap_id","type":"u64"}]},{"name":"Scorch","fields":[{"name":"id","type":"u128"}]},{"name":"JupiterLendDeposit"},{"name":"JupiterLendRedeem"},{"name":"TaurusFi"},{"name":"BisonFi"},{"name":"GoonfiV2"},{"name":"Quantum"},{"name":"BoopfunBuy2"},{"name":"BoopfunSell2"},{"name":"ByrealClmm2"},{"name":"Dooar2"},{"name":"HeavenBuy2"},{"name":"HeavenSell2"},{"name":"MoonitBuy2"},{"name":"MoonitSell2"},{"name":"SaberDecimalWrapperDeposit2"},{"name":"SaberDecimalWrapperWithdraw2"},{"name":"ByrealPropAmm"},{"name":"SanctumPrefundSwapViaStake","fields":[{"name":"swap_id","type":"u64"}]},{"name":"AbyssAmm"},{"name":"Aquifer"},{"name":"WhalestreetV2","fields":[{"name":"auth_amount_in","type":"u64"},{"name":"auth","type":"u64"}]},{"name":"SolfiV2WithSig","fields":[{"name":"unix_timestamp","type":"u64"},{"name":"msg_amount_in","type":"u64"},{"name":"expect_amount_out","type":"u64"},{"name":"slippage","type":"u16"},{"name":"user_request_slot","type":"u64"},{"name":"signature","type":{"array":["u8",64]}},{"name":"recovery_id","type":"u8"}]}]}},{"name":"PlatformFeeWrapUnwrapArgs","type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"amount_in","type":"u64"},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"tob","type":"bool"}]}},{"name":"Route","type":{"kind":"struct","fields":[{"name":"dexes","type":{"vec":{"defined":{"name":"Dex"}}}},{"name":"weights","type":"bytes"}]}},{"name":"SwapArgs","type":{"kind":"struct","fields":[{"name":"amount_in","type":"u64"},{"name":"expect_amount_out","type":"u64"},{"name":"min_return","type":"u64"},{"name":"amounts","type":{"vec":"u64"}},{"name":"routes","type":{"vec":{"vec":{"defined":{"name":"Route"}}}}}]}},{"name":"SwapEvent","type":{"kind":"struct","fields":[{"name":"dex","type":{"defined":{"name":"Dex"}}},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"}]}},{"name":"SwapWithFeesCpiEvent","type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"source_mint","type":"pubkey"},{"name":"destination_mint","type":"pubkey"},{"name":"source_token_account_owner","type":"pubkey"},{"name":"destination_token_account_owner","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"source_token_change","type":"u64"},{"name":"destination_token_change","type":"u64"},{"name":"commission_direction","type":"bool"},{"name":"commission_rate","type":"u32"},{"name":"commission_amount","type":"u64"},{"name":"commission_account","type":"pubkey"},{"name":"platform_fee_rate","type":"u16"},{"name":"platform_fee_amount","type":"u64"},{"name":"platform_fee_account","type":"pubkey"},{"name":"trim_rate","type":"u8"},{"name":"trim_amount","type":"u64"},{"name":"trim_account","type":"pubkey"}]}}],"constants":[{"name":"SEED_SA","type":"bytes","value":"[111, 107, 120, 95, 115, 97]"}]} \ No newline at end of file diff --git a/idls/ondo_gm.XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm.json b/idls/ondo_gm.XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm.json new file mode 100644 index 0000000..c58d355 --- /dev/null +++ b/idls/ondo_gm.XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm.json @@ -0,0 +1 @@ +{"address":"XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm","metadata":{"name":"ondo_gm","version":"0.1.0","spec":"0.1.0","description":"Ondo Finance Global Markets"},"instructions":[{"name":"add_to_whitelist","docs":["Add an address to the whitelist","Signer must have the ADMIN_ROLE_WHITELIST role"],"discriminator":[157,211,52,54,144,81,5,55],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account that has the authority to add an address to the whitelist"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_WHITELIST` role","# PDA Seeds","- ADMIN_ROLE_WHITELIST","- The authority's address",""],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,87,104,105,116,101,108,105,115,116]},{"kind":"account","path":"authority"}]}},{"name":"whitelist","docs":["The Whitelist account being created","# PDA Seeds","- WHITELIST_SEED","- Address being whitelisted"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[119,104,105,116,101,108,105,115,116]},{"kind":"arg","path":"address_to_whitelist"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"address_to_whitelist","type":"pubkey"}]},{"name":"batch_close_attestation_accounts","docs":["Batch close attestation accounts","","Accounts to close are passed via remaining_accounts, constraints:","1. Accounts must be marked writable","2. No other accounts should present in `remaining_accounts`","3. Each attestation account must be created by the recipient","4. Each attestation must be older than 30 seconds","Unpermissioned"],"discriminator":[73,167,240,82,80,48,205,207],"accounts":[{"name":"closer","docs":["The user closing the attestation accounts"],"signer":true},{"name":"recipient","docs":["The recipient of the lamports from closed attestation accounts","Must be the creator of each attestation"],"writable":true},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"burn_usdon","docs":["Burn USDon tokens (admin function)","Signer must have the BURNER_ROLE_USDON role"],"discriminator":[173,55,240,198,34,223,191,82],"accounts":[{"name":"authority","docs":["The account with the authority to burn USDon tokens"],"writable":true,"signer":true},{"name":"permanent_delegate","docs":["The permanent delegate PDA (also the mint authority)","# PDA Seeds","- MINT_AUTHORITY_SEED",""],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"usdon_manager_state","docs":["The USDonManagerState account containing USDon configuration","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has either the `BURNER_ROLE_USDON` role","or the `ADMIN_ROLE_USDON` role","# PDA Seeds","- Role seed (from the account's role field)","- The authority's address"],"pda":{"seeds":[{"kind":"account","path":"authority_role_account.role","account":"Roles"},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The USDon mint"],"writable":true},{"name":"token_program","docs":["The token program (Token-2022)"]},{"name":"destination","docs":["The token account to burn tokens from"],"writable":true}],"args":[{"name":"amount","type":"u64"}]},{"name":"close_attestation_account","docs":["Close a single attestation account","","The attestation account must be older than 30 seconds to be closed.","The rent from the closed account is returned to the recipient (original creator).","Unpermissioned"],"discriminator":[201,34,214,89,249,220,97,101],"accounts":[{"name":"closer","docs":["The user closing the attestation account"],"signer":true},{"name":"attestation","docs":["The attestation account to close","# PDA Seeds","- ATTESTATION_ID_SEED","- _attestation_id"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,116,116,101,115,116,97,116,105,111,110,95,105,100]},{"kind":"arg","path":"_attestation_id"}]}},{"name":"recipient","docs":["The recipient of the lamports from the closed attestation account","Must be the creator of the attestation",""],"writable":true},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"_attestation_id","type":{"array":["u8",16]}}]},{"name":"enable_oracle_price","docs":["Enable or disable oracle price for USDon","Signer must have the ADMIN_ROLE_USDON_MANAGER role"],"discriminator":[31,98,115,216,241,185,41,109],"accounts":[{"name":"authority","docs":["The account with the authority to execute the operation"],"signer":true},{"name":"usdon_manager_state","docs":["The USDonManagerState account to be modified","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `ADMIN_ROLE_USDON_MANAGER` role","# PDA Seeds","- ADMIN_ROLE_USDON_MANAGER","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,85,83,68,111,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}}],"args":[{"name":"is_enabled","type":"bool"}]},{"name":"grant_gmtoken_factory_role","docs":["Grants the specified GM Token Factory role to the user","Signer must have the ADMIN_ROLE_GMTOKEN_FACTORY role"],"discriminator":[219,108,46,79,46,104,74,179],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to grant a GM Token Factory role"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_FACTORY` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_FACTORY`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,70,97,99,116,111,114,121]},{"kind":"account","path":"authority"}]}},{"name":"role_to_grant","docs":["The new `Roles` account being created for the user","# PDA Seeds","- Role seed (from RoleType)","- User's address"],"writable":true,"pda":{"seeds":[{"kind":"arg","path":"role"},{"kind":"arg","path":"user"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"user","type":"pubkey"}]},{"name":"grant_gmtoken_manager_role","docs":["Grant a GM Token Manager role","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[245,248,145,72,159,93,154,176],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to grant GM Token Manager roles"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- ADMIN_ROLE_GMTOKEN_MANAGER","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"role_to_grant","docs":["The new `Roles` account being created for the user","# PDA Seeds","- Role seed (from RoleType)","- User's address"],"writable":true,"pda":{"seeds":[{"kind":"arg","path":"role"},{"kind":"arg","path":"user"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"user","type":"pubkey"}]},{"name":"grant_gmtoken_role","docs":["Grants the specified GMToken role to the user","Signer must have the ADMIN_ROLE_GMTOKEN role"],"discriminator":[80,114,198,5,12,2,84,105],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to grant GM Token roles"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN` role","# PDA Seeds","- ADMIN_ROLE_GMTOKEN","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110]},{"kind":"account","path":"authority"}]}},{"name":"role_to_grant","docs":["The new Roles account being created for the user","# PDA Seeds","- Role seed (from RoleType)","- User's address"],"writable":true,"pda":{"seeds":[{"kind":"arg","path":"role"},{"kind":"arg","path":"user"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"user","type":"pubkey"}]},{"name":"grant_role","docs":["Grants the specified role to a user","The signer must be the upgrade authority of the program"],"discriminator":[218,234,128,15,82,33,236,253],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to grant roles, must be the program upgrade authority"],"signer":true},{"name":"role_to_grant","docs":["The Roles account to be initialized","# PDA Seeds","- The role seed (from RoleType)","- The user's address"],"writable":true,"pda":{"seeds":[{"kind":"arg","path":"role"},{"kind":"arg","path":"user"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"},{"name":"program","docs":["The Ondo Global Markets program"],"address":"XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm"},{"name":"program_data","docs":["The ProgramData account of the Ondo Global Markets program"]}],"args":[{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"user","type":"pubkey"}]},{"name":"grant_sanity_configurer_role","docs":["Grant a configurer role for sanity check","Signer must have the ADMIN_ROLE_ONDO_SANITY_CHECK role"],"discriminator":[22,112,48,145,46,77,61,33],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to grant the configurer role"],"signer":true},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `ADMIN_ROLE_ONDO_SANITY_CHECK` role","# PDA Seeds","- `ADMIN_ROLE_ONDO_SANITY_CHECK`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,79,110,100,111,83,97,110,105,116,121,67,104,101,99,107]},{"kind":"account","path":"authority"}]}},{"name":"role_to_grant","docs":["The new `Roles` account being created for the user","# PDA Seeds","- Role seed (from RoleType)","- User's address"],"writable":true,"pda":{"seeds":[{"kind":"arg","path":"role"},{"kind":"arg","path":"user"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"user","type":"pubkey"}]},{"name":"grant_sanity_setter_role","docs":["Grant a setter role for sanity check","Signer must have the ADMIN_ROLE_ONDO_SANITY_CHECK role"],"discriminator":[209,154,110,150,18,113,63,227],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to grant the setter role"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_ONDO_SANITY_CHECK` role","# PDA Seeds","- `RoleType::ADMIN_ROLE_ONDO_SANITY_CHECK`","- `authority` (the authority's address)"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,79,110,100,111,83,97,110,105,116,121,67,104,101,99,107]},{"kind":"account","path":"authority"}]}},{"name":"role_to_grant","docs":["The role account to be initialized for the user","# PDA Seeds","- `role.seed()` (the seed for the setter role)","- `user` (the user's address)"],"writable":true,"pda":{"seeds":[{"kind":"arg","path":"role"},{"kind":"arg","path":"user"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"user","type":"pubkey"}]},{"name":"grant_usdon_role","docs":["Grants the specified USDon role to a user","Signer must have the GUARDIAN_USDON role"],"discriminator":[107,112,6,81,156,182,208,9],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to grant the role"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `GUARDIAN_USDON` role","# PDA Seeds","- GUARDIAN_USDON","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[71,117,97,114,100,105,97,110,85,83,68,111,110]},{"kind":"account","path":"authority"}]}},{"name":"role_to_grant","docs":["The new `Roles` account being created for the user","# PDA Seeds","- Role seed (from RoleType)","- User's address"],"writable":true,"pda":{"seeds":[{"kind":"arg","path":"role"},{"kind":"arg","path":"user"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"user","type":"pubkey"}]},{"name":"init_mint","docs":["Initialize a new GM token mint (without permanent delegate)","Signer must have the DEPLOYER_ROLE_GMTOKEN_FACTORY role"],"discriminator":[126,176,233,16,66,117,209,125],"accounts":[{"name":"payer","docs":["The payer account funding the mint account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to deploy new GM tokens"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the user has `DEPLOYER_ROLE_GMTOKEN_FACTORY`","# PDA Seeds","- `DEPLOYER_ROLE_GMTOKEN_FACTORY`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[68,101,112,108,111,121,101,114,82,111,108,101,71,77,84,111,107,101,110,70,97,99,116,111,114,121]},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The new mint account to be initialized",""],"writable":true,"signer":true},{"name":"mint_authority","docs":["The mint authority PDA that will control the mint","# PDA Seeds","- `MINT_AUTHORITY_SEED`",""],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["The token program (Token-2022)"]},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account containing factory configuration","# PDA Seeds","- GMTOKEN_MANAGER_STATE_SEED"],"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"freeze_authority","type":"pubkey"}]},{"name":"init_mint_delegate","docs":["Initialize a new token mint with permanent delegate (for USDon)","Signer must have the DEPLOYER_ROLE_GMTOKEN_FACTORY role"],"discriminator":[66,23,77,148,164,53,165,83],"accounts":[{"name":"payer","docs":["The payer account funding the mint account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to deploy new tokens"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `DEPLOYER_ROLE_GMTOKEN_FACTORY` role","# PDA Seeds","- `DEPLOYER_ROLE_GMTOKEN_FACTORY`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[68,101,112,108,111,121,101,114,82,111,108,101,71,77,84,111,107,101,110,70,97,99,116,111,114,121]},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The new mint account to be initialized with permanent delegate",""],"writable":true,"signer":true},{"name":"mint_authority","docs":["The mint authority PDA that will control the mint and act as permanent delegate","# PDA Seeds","- `MINT_AUTHORITY_SEED`",""],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["The token program (Token-2022)"]},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account containing factory configuration","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"freeze_authority","type":"pubkey"}]},{"name":"initialize_gmtoken_manager","docs":["Initialize the GM token manager state","","Sets up the manager with pause states for factory, redemptions, and minting,","and configures the secp256k1 attestation signer address.","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[118,57,145,134,233,31,145,47],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to initialize the GM Token Manager"],"signer":true},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be initialized","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"factory_paused","type":"bool"},{"name":"redemptions_paused","type":"bool"},{"name":"minting_paused","type":"bool"},{"name":"attestation_signer_secp","type":{"array":["u8",20]}},{"name":"trading_hours_offset","type":"i64"}]},{"name":"initialize_sanity_check","docs":["Initialize sanity check parameters for a token","","Sets up price deviation and time delay checks to ensure safe trading.","Signer must have the ADMIN_ROLE_ONDO_SANITY_CHECK role"],"discriminator":[99,169,10,74,68,74,22,72],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to initialize the sanity check"],"signer":true},{"name":"sanity_check","docs":["The sanity check account to be initialized","# PDA Seeds","- `ORACLE_SANITY_CHECK_SEED`","- `mint` (the mint address of the GM Token for which the sanity check is being initialized)"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,97,110,105,116,121,95,99,104,101,99,107]},{"kind":"account","path":"mint"}]}},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `ADMIN_ROLE_ONDO_SANITY_CHECK` role","# PDA Seeds","- `RoleType::ADMIN_ROLE_ONDO_SANITY_CHECK`","- `authority` (the authority's address)"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,79,110,100,111,83,97,110,105,116,121,67,104,101,99,107]},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The GM Token mint for which the sanity check is being initialized"]},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"last_price","type":"u64"},{"name":"allowed_deviation_bps","type":"u64"},{"name":"max_time_delay","type":"i64"}]},{"name":"initialize_token_limit","docs":["Initialize token-level rate limits","","Sets rate limits for the token and default limits for users trading this token.","Signer must have the DEPLOYER_ROLE_GMTOKEN_FACTORY role"],"discriminator":[187,218,162,146,4,49,163,240],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to initialize token limit accounts"],"signer":true},{"name":"mint","docs":["The GM Token or USDon mint"]},{"name":"token_limit","docs":["The `TokenLimit` account to be initialized","# PDA Seeds","- `TOKEN_LIMIT_ACCOUNT_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"mint"}]}},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `DEPLOYER_ROLE_GMTOKEN_FACTORY` role","# PDA Seeds","- `DEPLOYER_ROLE_GMTOKEN_FACTORY`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[68,101,112,108,111,121,101,114,82,111,108,101,71,77,84,111,107,101,110,70,97,99,116,111,114,121]},{"kind":"account","path":"authority"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"rate_limit","type":{"option":"u64"}},{"name":"limit_window","type":{"option":"u64"}},{"name":"default_user_rate_limit","type":{"option":"u64"}},{"name":"default_limit_window","type":{"option":"u64"}}]},{"name":"initialize_usdon_manager","docs":["Initialize the USDon manager state","","Sets up the manager with the USDon mint, initial price, oracle configuration,","and vault addresses for USDC and USDon tokens.","Signer must have the GUARDIAN_USDON role"],"discriminator":[9,32,232,105,2,166,13,47],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to initialize the USDon Manager"],"signer":true},{"name":"mint_authority","docs":["The mint authority PDA","# PDA Seeds","- MINT_AUTHORITY_SEED","","Does not need to be checked for correctness as it is uninitialized."],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"usdon_mint","docs":["The USDon mint account","Must be initialized with mint authority as `mint_authority`","and use the token-2022 program"]},{"name":"usdon_vault","docs":["The USDon vault token account","Must be the ATA for `usdon_mint` owned by `usdon_manager_state`"],"pda":{"seeds":[{"kind":"account","path":"usdon_manager_state"},{"kind":"const","value":[6,221,246,225,238,117,143,222,24,66,93,188,228,108,205,218,182,26,252,77,131,185,13,39,254,189,249,40,216,161,139,252]},{"kind":"account","path":"usdon_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"usdc_vault","docs":["The USDC vault token account","Must be the ATA for USDC mint owned by `usdon_manager_state`"],"pda":{"seeds":[{"kind":"account","path":"usdon_manager_state"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"const","value":[198,250,122,243,190,219,173,58,61,101,243,106,171,201,116,49,177,187,228,194,210,246,224,228,124,166,2,3,69,47,93,97]}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"usdon_manager_state","docs":["The USDonManagerState account to be initialized","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `GUARDIAN_USDON` role","# PDA Seeds","- GUARDIAN_USDON","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[71,117,97,114,100,105,97,110,85,83,68,111,110]},{"kind":"account","path":"authority"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"oracle_price_enabled","type":"bool"},{"name":"oracle_price_max_age","type":"u64"},{"name":"usdc_price_update_address","type":"pubkey"}]},{"name":"initialize_user","docs":["Initialize a user account with optional rate limits"],"discriminator":[111,17,185,250,60,122,38,254],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to initialize an `OndoUser` account"],"signer":true},{"name":"user","docs":["The user for whom the OndoUser account is being initialized"]},{"name":"mint","docs":["The GM Token mint associated with the OndoUser account"]},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"ondo_user","docs":["The `OndoUser` account to be initialized","# PDA seeds:","- `ONDO_USER_SEED`","- User's address,","- The GM Token's mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[79,110,100,111,85,115,101,114]},{"kind":"account","path":"user"},{"kind":"account","path":"mint"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"rate_limit","type":{"option":"u64"}},{"name":"limit_window","type":{"option":"u64"}}]},{"name":"mint_gm","docs":["Mint GM tokens directly (admin function)","Signer must have the MINTER_ROLE_GMTOKEN role"],"discriminator":[117,223,58,111,44,36,16,43],"accounts":[{"name":"payer","docs":["Pays for destination account if needed"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to mint GM Tokens,"],"signer":true},{"name":"user","docs":["The user receiving the minted tokens"]},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `MINTER_ROLE_GMTOKEN` role","# PDA Seeds","- `MINTER_ROLE_GMTOKEN`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[77,105,110,116,101,114,82,111,108,101,71,77,84,111,107,101,110]},{"kind":"account","path":"authority"}]}},{"name":"oracle_sanity_check","docs":["The `OracleSanityCheck` account validating oracle price updates","# PDA Seeds","- `ORACLE_SANITY_CHECK_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,97,110,105,116,121,95,99,104,101,99,107]},{"kind":"account","path":"mint"}]}},{"name":"mint_authority","docs":["The mint authority PDA","# PDA Seeds","- `MINT_AUTHORITY_SEED`",""],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"mint","docs":["The GM Token mint to mint from"],"writable":true,"relations":["oracle_sanity_check"]},{"name":"destination","docs":["The destination token account to mint tokens to"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"usdon_manager_state","docs":["The `USDonManagerState` account for validation","# PDA Seeds","- `USDON_MANAGER_STATE_SEED`"],"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"token_program","docs":["The token program (Token-2022)"]},{"name":"associated_token_program","docs":["The associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"}]},{"name":"mint_usdon","docs":["Mint USDon tokens (admin function)","Signer must have the MINTER_ROLE_USDON role"],"discriminator":[178,178,234,133,225,144,48,129],"accounts":[{"name":"authority","docs":["The account with the authority to mint USDon tokens"],"writable":true,"signer":true},{"name":"mint_authority","docs":["The mint authority PDA","# PDA Seeds","- MINT_AUTHORITY_SEED",""],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"usdon_manager_state","docs":["The USDonManagerState account containing USDon configuration","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"authority_role_account","docs":["The Roles account verifying the authority has either the `MINTER_ROLE_USDON`","or `ADMIN_ROLE_USDON` role","# PDA Seeds","- Role seed (from the account's role field)","- The authority's address"],"pda":{"seeds":[{"kind":"account","path":"authority_role_account.role","account":"Roles"},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The USDon mint"],"writable":true},{"name":"token_program","docs":["The token program (Token-2022)"]},{"name":"destination","docs":["The destination token account to mint tokens to"],"writable":true}],"args":[{"name":"amount","type":"u64"}]},{"name":"mint_with_usdc","docs":["Mint GM tokens by paying with USDC","","Requires a valid attestation with price, amount, and expiration."],"discriminator":[128,28,133,173,71,142,185,206],"accounts":[{"name":"user","docs":["The user performing the USDC swap, pays for account creation if needed"],"writable":true,"signer":true},{"name":"mint","docs":["The GM Token mint involved in the swap"],"writable":true},{"name":"mint_authority","docs":["The mint authority PDA","# PDA Seeds","- MINT_AUTHORITY_SEED","Does not need to be checked for correctness as it is uninitialized."],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"ondo_user","docs":["The OndoUser account tracking user-specific state for this mint","# PDA Seeds","- ONDO_USER_SEED","- User's address","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[79,110,100,111,85,115,101,114]},{"kind":"account","path":"user"},{"kind":"account","path":"mint"}]}},{"name":"token_limit_account","docs":["The TokenLimit account enforcing mint/burn limits for the GM Token","# PDA Seeds","- TOKEN_LIMIT_ACCOUNT_SEED","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"mint"}]}},{"name":"sanity_check_account","docs":["The OracleSanityCheck account validating oracle price updates","# PDA Seeds","- ORACLE_SANITY_CHECK_SEED","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,97,110,105,116,121,95,99,104,101,99,107]},{"kind":"account","path":"mint"}]}},{"name":"user_token_account","docs":["The user's associated token account for the GM Token"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"attestation_id_account","docs":["The attestation ID account preventing attestation reuse","# PDA Seeds","- ATTESTATION_ID_SEED","- Attestation ID (16-byte array)","Existence means the attestation has been used."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,116,116,101,115,116,97,116,105,111,110,95,105,100]},{"kind":"arg","path":"attestation_id"}]}},{"name":"whitelist","docs":["The Whitelist account verifying the user is authorized","# PDA Seeds","- WHITELIST_SEED","- User's address","Validated in instruction handler - returns UserNotWhitelisted if not initialized."],"pda":{"seeds":[{"kind":"const","value":[119,104,105,116,101,108,105,115,116]},{"kind":"account","path":"user"}]}},{"name":"token_program","docs":["The token program (Token-2022)"]},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["The associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"spl_token_program","docs":["The legacy SPL Token program","Required when performing swaps between USDC and USDon tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"usdc_price_update","docs":["The oracle price update account for USDC/USD price feed","Provides real-time price data for USDC token","Used in swap operations to calculate exchange rates","Must be recent (within oracle_price_max_age) to ensure price accuracy","`has_one` ensures that this account matches the expected oracle account stored in the `usdon_manager_state`."],"relations":["usdon_manager_state"]},{"name":"usdc_vault","docs":["The USDC vault storing USDC tokens received from users during swaps"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"usdon_manager_state"},{"kind":"account","path":"spl_token_program"},{"kind":"account","path":"usdc_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"usdon_vault","docs":["The USDon vault storing USDon tokens received from users during swaps"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"usdon_manager_state"},{"kind":"account","path":"token_program"},{"kind":"account","path":"usdon_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"usdc_mint"},{"name":"user_usdc_token_account","docs":["The user's USDC token account"],"writable":true},{"name":"usdon_mint","docs":["The USDon mint (Token-2022)"],"writable":true},{"name":"user_usdon_token_account","docs":["The user's USDon token account"],"writable":true},{"name":"usdon_manager_state","docs":["The USDonManagerState account used as authority for vault operations","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"gmtoken_manager_state","docs":["The GmTokenManagerState account managing GM Token operations","- Stores protocol parameters like factory, redemption, and minting paused.","# PDA Seeds","- GMTOKEN_MANAGER_STATE_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}},{"name":"instructions","address":"Sysvar1nstructions1111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"attestation_id","type":{"array":["u8",16]}},{"name":"price","type":"u64"},{"name":"amount","type":"u64"},{"name":"expiration","type":"i64"}]},{"name":"mint_with_usdon","docs":["Mint GM tokens by paying with USDon","","Requires a valid attestation with price, amount, and expiration."],"discriminator":[25,116,92,45,43,188,95,58],"accounts":[{"name":"user","docs":["The user performing the USDon swap, pays for account creation if needed"],"writable":true,"signer":true},{"name":"mint","docs":["The GM Token mint involved in the swap"],"writable":true},{"name":"mint_authority","docs":["The mint authority PDA","# PDA Seeds","- MINT_AUTHORITY_SEED","Does not need to be checked for correctness as it is uninitialized."],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"ondo_user","docs":["The OndoUser account tracking user-specific state for this mint","# PDA Seeds","- ONDO_USER_SEED","- User's address","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[79,110,100,111,85,115,101,114]},{"kind":"account","path":"user"},{"kind":"account","path":"mint"}]}},{"name":"token_limit_account","docs":["The TokenLimit account enforcing mint/burn limits for the GM Token","# PDA Seeds","- TOKEN_LIMIT_ACCOUNT_SEED","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"mint"}]}},{"name":"sanity_check_account","docs":["The OracleSanityCheck account validating oracle price updates","# PDA Seeds","- ORACLE_SANITY_CHECK_SEED","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,97,110,105,116,121,95,99,104,101,99,107]},{"kind":"account","path":"mint"}]}},{"name":"user_token_account","docs":["The user's associated token account for the GM Token"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"attestation_id_account","docs":["The attestation ID account preventing attestation reuse","# PDA Seeds","- ATTESTATION_ID_SEED","- Attestation ID (16-byte array)","Existence means the attestation has been used."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,116,116,101,115,116,97,116,105,111,110,95,105,100]},{"kind":"arg","path":"attestation_id"}]}},{"name":"whitelist","docs":["The Whitelist account verifying the user is authorized","# PDA Seeds","- WHITELIST_SEED","- User's address","Validated in instruction handler - returns UserNotWhitelisted if not initialized."],"pda":{"seeds":[{"kind":"const","value":[119,104,105,116,101,108,105,115,116]},{"kind":"account","path":"user"}]}},{"name":"token_program","docs":["The token program (Token-2022)"]},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["The associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"usdon_vault","docs":["The USDon vault storing USDon tokens received from users during swaps"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"usdon_manager_state"},{"kind":"account","path":"token_program"},{"kind":"account","path":"usdon_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"usdon_mint","docs":["The USDon mint (Token-2022)"],"writable":true},{"name":"user_usdon_token_account","docs":["The user's USDon token account"],"writable":true},{"name":"usdon_manager_state","docs":["The USDonManagerState account used as authority for vault operations","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"gmtoken_manager_state","docs":["The GmTokenManagerState account managing GM Token operations","- Stores protocol parameters like factory, redemption, and minting paused.","# PDA Seeds","- GMTOKEN_MANAGER_STATE_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}},{"name":"instructions","address":"Sysvar1nstructions1111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"attestation_id","type":{"array":["u8",16]}},{"name":"price","type":"u64"},{"name":"amount","type":"u64"},{"name":"expiration","type":"i64"}]},{"name":"pause_global_minting","docs":["Pause all mints globally","Signer must have the PAUSER_ROLE_GMTOKEN_MANAGER role"],"discriminator":[191,70,149,85,20,141,242,239],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `PAUSER_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `PAUSER_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[80,97,117,115,101,114,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[]},{"name":"pause_global_minting_admin","docs":["Pause minting globally (admin function)","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[20,43,34,201,214,148,151,75],"accounts":[{"name":"authority","docs":["The account with the authority to execute the unpause/configuration operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[]},{"name":"pause_global_redemption","docs":["Pause all redemption globally","Signer must have the PAUSER_ROLE_GMTOKEN_MANAGER role"],"discriminator":[169,5,1,19,183,71,133,147],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `PAUSER_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `PAUSER_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[80,97,117,115,101,114,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[]},{"name":"pause_global_redemption_admin","docs":["Pause redemption globally (admin function)","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[77,80,68,48,7,94,111,183],"accounts":[{"name":"authority","docs":["The account with the authority to execute the unpause/configuration operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[]},{"name":"pause_token","docs":["Pause all transfers for a GM token by invoking the pausable token extension","Signer must have the PAUSER_ROLE_GMTOKEN role"],"discriminator":[226,150,72,211,159,51,226,39],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause operation"],"writable":true,"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `PAUSER_ROLE_GMTOKEN` role","# PDA Seeds","- `PAUSER_ROLE_GMTOKEN`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[80,97,117,115,101,114,82,111,108,101,71,77,84,111,107,101,110]},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The GM Token mint to pause"],"writable":true},{"name":"mint_authority","docs":["The mint authority PDA that has pausable authority","# PDA Seeds","- MINT_AUTHORITY_SEED",""],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"token_program","docs":["The token program (Token-2022)"]}],"args":[]},{"name":"pause_token_factory","docs":["Pause the GM token factory","Signer must have the PAUSER_ROLE_GMTOKEN_FACTORY role"],"discriminator":[79,227,221,11,182,89,193,150],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `PAUSER_ROLE_GMTOKEN_FACTORY` role","# PDA Seeds","- `PAUSER_ROLE_GMTOKEN_FACTORY`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[80,97,117,115,101,114,82,111,108,101,71,77,84,111,107,101,110,70,97,99,116,111,114,121]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[]},{"name":"pause_token_factory_admin","docs":["Pause the GM token factory (admin version)","Signer must have the ADMIN_ROLE_GMTOKEN_FACTORY role"],"discriminator":[244,155,26,17,39,152,51,18],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause/unpause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has `ADMIN_ROLE_GMTOKEN_FACTORY` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_FACTORY`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,70,97,99,116,111,114,121]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[]},{"name":"pause_token_minting","docs":["Pause minting for a specific token","Signer must have the PAUSER_ROLE_GMTOKEN_MANAGER role"],"discriminator":[105,219,64,183,163,148,17,126],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `PAUSER_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `PAUSER_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[80,97,117,115,101,114,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"token_limit_account","docs":["The `TokenLimit` account for the specific GM Token to pause","# PDA Seeds","- `TOKEN_LIMIT_ACCOUNT_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"token_limit_account.mint","account":"TokenLimit"}]}}],"args":[]},{"name":"pause_token_minting_admin","docs":["Pause mints for a specific token (admin function)","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[8,3,163,16,201,93,94,145],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause/unpause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"token_limit_account","docs":["The `TokenLimit` account for the specific GM Token to pause/unpause","# PDA Seeds","- `TOKEN_LIMIT_ACCOUNT_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"token_limit_account.mint","account":"TokenLimit"}]}}],"args":[]},{"name":"pause_token_redemption","docs":["Pause redemptions for a specific token","Signer must have the PAUSER_ROLE_GMTOKEN_MANAGER role"],"discriminator":[232,134,32,8,70,234,54,216],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `PAUSER_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `PAUSER_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[80,97,117,115,101,114,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"token_limit_account","docs":["The `TokenLimit` account for the specific GM Token to pause","# PDA Seeds","- `TOKEN_LIMIT_ACCOUNT_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"token_limit_account.mint","account":"TokenLimit"}]}}],"args":[]},{"name":"pause_token_redemption_admin","docs":["Pause redemptions for a specific token (admin function)","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[62,162,192,223,22,143,110,191],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause/unpause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"token_limit_account","docs":["The `TokenLimit` account for the specific GM Token to pause/unpause","# PDA Seeds","- `TOKEN_LIMIT_ACCOUNT_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"token_limit_account.mint","account":"TokenLimit"}]}}],"args":[]},{"name":"redeem_for_usdc","docs":["Redeem GM tokens for USDC","","Requires a valid attestation with price, amount, and expiration."],"discriminator":[150,9,215,220,255,157,74,78],"accounts":[{"name":"user","docs":["The user performing the USDC swap, pays for account creation if needed"],"writable":true,"signer":true},{"name":"mint","docs":["The GM Token mint involved in the swap"],"writable":true},{"name":"mint_authority","docs":["The mint authority PDA","# PDA Seeds","- MINT_AUTHORITY_SEED","Does not need to be checked for correctness as it is uninitialized."],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"ondo_user","docs":["The OndoUser account tracking user-specific state for this mint","# PDA Seeds","- ONDO_USER_SEED","- User's address","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[79,110,100,111,85,115,101,114]},{"kind":"account","path":"user"},{"kind":"account","path":"mint"}]}},{"name":"token_limit_account","docs":["The TokenLimit account enforcing mint/burn limits for the GM Token","# PDA Seeds","- TOKEN_LIMIT_ACCOUNT_SEED","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"mint"}]}},{"name":"sanity_check_account","docs":["The OracleSanityCheck account validating oracle price updates","# PDA Seeds","- ORACLE_SANITY_CHECK_SEED","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,97,110,105,116,121,95,99,104,101,99,107]},{"kind":"account","path":"mint"}]}},{"name":"user_token_account","docs":["The user's associated token account for the GM Token"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"attestation_id_account","docs":["The attestation ID account preventing attestation reuse","# PDA Seeds","- ATTESTATION_ID_SEED","- Attestation ID (16-byte array)","Existence means the attestation has been used."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,116,116,101,115,116,97,116,105,111,110,95,105,100]},{"kind":"arg","path":"attestation_id"}]}},{"name":"whitelist","docs":["The Whitelist account verifying the user is authorized","# PDA Seeds","- WHITELIST_SEED","- User's address","Validated in instruction handler - returns UserNotWhitelisted if not initialized."],"pda":{"seeds":[{"kind":"const","value":[119,104,105,116,101,108,105,115,116]},{"kind":"account","path":"user"}]}},{"name":"token_program","docs":["The token program (Token-2022)"]},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["The associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"spl_token_program","docs":["The legacy SPL Token program","Required when performing swaps between USDC and USDon tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"usdc_price_update","docs":["The oracle price update account for USDC/USD price feed","Provides real-time price data for USDC token","Used in swap operations to calculate exchange rates","Must be recent (within oracle_price_max_age) to ensure price accuracy","`has_one` ensures that this account matches the expected oracle account stored in the `usdon_manager_state`."],"relations":["usdon_manager_state"]},{"name":"usdc_vault","docs":["The USDC vault storing USDC tokens received from users during swaps"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"usdon_manager_state"},{"kind":"account","path":"spl_token_program"},{"kind":"account","path":"usdc_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"usdon_vault","docs":["The USDon vault storing USDon tokens received from users during swaps"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"usdon_manager_state"},{"kind":"account","path":"token_program"},{"kind":"account","path":"usdon_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"usdc_mint"},{"name":"user_usdc_token_account","docs":["The user's USDC token account"],"writable":true},{"name":"usdon_mint","docs":["The USDon mint (Token-2022)"],"writable":true},{"name":"user_usdon_token_account","docs":["The user's USDon token account"],"writable":true},{"name":"usdon_manager_state","docs":["The USDonManagerState account used as authority for vault operations","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"gmtoken_manager_state","docs":["The GmTokenManagerState account managing GM Token operations","- Stores protocol parameters like factory, redemption, and minting paused.","# PDA Seeds","- GMTOKEN_MANAGER_STATE_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}},{"name":"instructions","address":"Sysvar1nstructions1111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"attestation_id","type":{"array":["u8",16]}},{"name":"price","type":"u64"},{"name":"amount","type":"u64"},{"name":"expiration","type":"i64"}]},{"name":"redeem_for_usdon","docs":["Redeem GM tokens for USDon","","Requires a valid attestation with price, amount, and expiration."],"discriminator":[231,121,93,33,143,252,82,13],"accounts":[{"name":"user","docs":["The user performing the USDon swap, pays for account creation if needed"],"writable":true,"signer":true},{"name":"mint","docs":["The GM Token mint involved in the swap"],"writable":true},{"name":"mint_authority","docs":["The mint authority PDA","# PDA Seeds","- MINT_AUTHORITY_SEED","Does not need to be checked for correctness as it is uninitialized."],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"ondo_user","docs":["The OndoUser account tracking user-specific state for this mint","# PDA Seeds","- ONDO_USER_SEED","- User's address","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[79,110,100,111,85,115,101,114]},{"kind":"account","path":"user"},{"kind":"account","path":"mint"}]}},{"name":"token_limit_account","docs":["The TokenLimit account enforcing mint/burn limits for the GM Token","# PDA Seeds","- TOKEN_LIMIT_ACCOUNT_SEED","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"mint"}]}},{"name":"sanity_check_account","docs":["The OracleSanityCheck account validating oracle price updates","# PDA Seeds","- ORACLE_SANITY_CHECK_SEED","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,97,110,105,116,121,95,99,104,101,99,107]},{"kind":"account","path":"mint"}]}},{"name":"user_token_account","docs":["The user's associated token account for the GM Token"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"attestation_id_account","docs":["The attestation ID account preventing attestation reuse","# PDA Seeds","- ATTESTATION_ID_SEED","- Attestation ID (16-byte array)","Existence means the attestation has been used."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,116,116,101,115,116,97,116,105,111,110,95,105,100]},{"kind":"arg","path":"attestation_id"}]}},{"name":"whitelist","docs":["The Whitelist account verifying the user is authorized","# PDA Seeds","- WHITELIST_SEED","- User's address","Validated in instruction handler - returns UserNotWhitelisted if not initialized."],"pda":{"seeds":[{"kind":"const","value":[119,104,105,116,101,108,105,115,116]},{"kind":"account","path":"user"}]}},{"name":"token_program","docs":["The token program (Token-2022)"]},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["The associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"usdon_vault","docs":["The USDon vault storing USDon tokens received from users during swaps"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"usdon_manager_state"},{"kind":"account","path":"token_program"},{"kind":"account","path":"usdon_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"usdon_mint","docs":["The USDon mint (Token-2022)"],"writable":true},{"name":"user_usdon_token_account","docs":["The user's USDon token account"],"writable":true},{"name":"usdon_manager_state","docs":["The USDonManagerState account used as authority for vault operations","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"gmtoken_manager_state","docs":["The GmTokenManagerState account managing GM Token operations","- Stores protocol parameters like factory, redemption, and minting paused.","# PDA Seeds","- GMTOKEN_MANAGER_STATE_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}},{"name":"instructions","address":"Sysvar1nstructions1111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"attestation_id","type":{"array":["u8",16]}},{"name":"price","type":"u64"},{"name":"amount","type":"u64"},{"name":"expiration","type":"i64"}]},{"name":"remove_from_whitelist","docs":["Remove an address from the whitelist","Signer must have the ADMIN_ROLE_WHITELIST role"],"discriminator":[7,144,216,239,243,236,193,235],"accounts":[{"name":"authority","docs":["The account with the authority to remove an address from the whitelist"],"signer":true},{"name":"recipient","docs":["Receives the lamports from closing the Whitelist account"],"writable":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_WHITELIST` role","# PDA Seeds","- `ADMIN_ROLE_WHITELIST`","- The authority's address",""],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,87,104,105,116,101,108,105,115,116]},{"kind":"account","path":"authority"}]}},{"name":"whitelist","docs":["The Whitelist account being closed","# PDA Seeds","- `WHITELIST_SEED`","- Address being removed from whitelist"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[119,104,105,116,101,108,105,115,116]},{"kind":"arg","path":"address_to_remove"}]}}],"args":[{"name":"address_to_remove","type":"pubkey"}]},{"name":"resume_global_minting","docs":["Resume all mints globally","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[18,40,173,140,250,6,10,207],"accounts":[{"name":"authority","docs":["The account with the authority to execute the unpause/configuration operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[]},{"name":"resume_global_redemption","docs":["Resume all redemption globally","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[80,47,24,168,44,185,215,97],"accounts":[{"name":"authority","docs":["The account with the authority to execute the unpause/configuration operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[]},{"name":"resume_token","docs":["Resume all transfers for a GM token by invoking the pausable token extension to remove a pause.","Signer must have the UNPAUSER_ROLE_GMTOKEN role"],"discriminator":[173,116,46,190,81,191,249,83],"accounts":[{"name":"authority","docs":["The account with the authority to execute the resume operation"],"writable":true,"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `UNPAUSER_ROLE_GMTOKEN` role","# PDA Seeds","- `UNPAUSER_ROLE_GMTOKEN`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[85,110,112,97,117,115,101,114,82,111,108,101,71,77,84,111,107,101,110]},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The GM Token mint to resume"],"writable":true},{"name":"mint_authority","docs":["The mint authority PDA that has pausable authority","# PDA Seeds","- `MINT_AUTHORITY_SEED`",""],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"token_program","docs":["The token program (Token-2022)"]}],"args":[]},{"name":"resume_token_factory","docs":["Resume the GM token factory","Signer must have the ADMIN_ROLE_GMTOKEN_FACTORY role"],"discriminator":[235,37,239,145,244,212,31,37],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause/unpause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has `ADMIN_ROLE_GMTOKEN_FACTORY` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_FACTORY`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,70,97,99,116,111,114,121]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[]},{"name":"resume_token_minting","docs":["Resume mints for a token (admin function)","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[188,102,226,249,194,29,81,248],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause/unpause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"token_limit_account","docs":["The `TokenLimit` account for the specific GM Token to pause/unpause","# PDA Seeds","- `TOKEN_LIMIT_ACCOUNT_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"token_limit_account.mint","account":"TokenLimit"}]}}],"args":[]},{"name":"resume_token_redemption","docs":["Resume redemptions for a token (admin function)","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[152,249,213,178,85,141,122,45],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause/unpause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"token_limit_account","docs":["The `TokenLimit` account for the specific GM Token to pause/unpause","# PDA Seeds","- `TOKEN_LIMIT_ACCOUNT_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"token_limit_account.mint","account":"TokenLimit"}]}}],"args":[]},{"name":"retrieve_tokens","docs":["Retrieve (withdraw) tokens from a vault controlled by the USDon manager","","Allows admins to withdraw any tokens (USDC, USDon, etc.) from vaults","owned by the usdon_manager_state PDA.","Signer must have the ADMIN_ROLE_USDON_MANAGER role"],"discriminator":[208,194,68,55,183,22,93,135],"accounts":[{"name":"authority","docs":["The account with the authority to execute the retrieval operation"],"signer":true},{"name":"usdon_manager_state","docs":["The USDonManagerState account used as authority for vault operations","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `ADMIN_ROLE_USDON_MANAGER` role","# PDA Seeds","- ADMIN_ROLE_USDON_MANAGER","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,85,83,68,111,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"token_mint","docs":["The mint of the token being retrieved"]},{"name":"source_vault","docs":["The source vault token account (must be owned by usdon_manager_state)"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"usdon_manager_state"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"destination","docs":["The destination token account to receive the tokens"],"writable":true},{"name":"token_program","docs":["The token program (SPL Token or Token-2022)"]}],"args":[{"name":"amount","type":"u64"}]},{"name":"revoke_gmtoken_factory_role","docs":["Revokes the specified GM Token Factory role from the user","Signer must have the ADMIN_ROLE_GMTOKEN_FACTORY role"],"discriminator":[214,48,33,96,99,36,94,198],"accounts":[{"name":"authority","docs":["The account with the authority to revoke a GM Token Factory role"],"signer":true},{"name":"recipient","docs":["Receives the lamports from closing the account"],"writable":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_FACTORY` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_FACTORY`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,70,97,99,116,111,114,121]},{"kind":"account","path":"authority"}]}},{"name":"role_to_revoke","docs":["The Roles account being closed","# PDA Seeds","- `role_to_revoke.role.seed()` (the seed for the role)","- `role_to_revoke.address` (the user's address)"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"role_to_revoke.role","account":"Roles"},{"kind":"account","path":"role_to_revoke.address","account":"Roles"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"revoke_gmtoken_manager_role","docs":["Revoke a role from the GM token manager","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[202,63,132,81,219,242,100,90],"accounts":[{"name":"authority","docs":["The account with the authority to revoke GM Token Manager roles"],"signer":true},{"name":"recipient","docs":["Receives the lamports from closing the Roles account"],"writable":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"role_to_revoke","docs":["The Roles account being closed","# PDA Seeds","- `role_to_revoke.role.seed()` (the seed for the role)","- `role_to_revoke.address` (the user's address)"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"role_to_revoke.role","account":"Roles"},{"kind":"account","path":"role_to_revoke.address","account":"Roles"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"revoke_gmtoken_role","docs":["Revokes the specified GMToken role from the user","Signer must have the ADMIN_ROLE_GMTOKEN role"],"discriminator":[108,55,0,39,40,39,103,144],"accounts":[{"name":"recipient","docs":["The recipient of the closed account lamports"],"writable":true},{"name":"authority","docs":["The account with the authority to revoke GM Token roles"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN` role","# PDA Seeds","- ADMIN_ROLE_GMTOKEN","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110]},{"kind":"account","path":"authority"}]}},{"name":"role_to_revoke","docs":["The Roles account being closed","# PDA Seeds","- `role_to_revoke.role.seed()` (the seed for the role)","- `role_to_revoke.address` (the user's address)"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"role_to_revoke.role","account":"Roles"},{"kind":"account","path":"role_to_revoke.address","account":"Roles"}]}}],"args":[]},{"name":"revoke_role","docs":["Revoke a role by closing the Roles account and reclaim rent","Signer must be the upgrade authority of the program"],"discriminator":[179,232,2,180,48,227,82,7],"accounts":[{"name":"recipient","docs":["Receives funds from account closure"],"writable":true},{"name":"authority","docs":["The account with the authority to revoke roles, must be the program upgrade authority"],"signer":true},{"name":"role_to_revoke","docs":["The Roles account to be closed","# PDA Seeds","- `role_to_revoke.role.seed()` (the seed for the role)","- `role_to_revoke.address` (the user's address)"],"writable":true,"pda":{"seeds":[{"kind":"arg","path":"_role"},{"kind":"account","path":"role_to_revoke.address","account":"Roles"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"},{"name":"program","docs":["The Ondo Global Markets program"],"address":"XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm"},{"name":"program_data","docs":["The ProgramData account of the Ondo Global Markets program"]}],"args":[{"name":"_role","type":{"defined":{"name":"RoleType"}}}]},{"name":"revoke_sanity_configurer_role","docs":["Revoke a configurer role for sanity check","Signer must have the ADMIN_ROLE_ONDO_SANITY_CHECK role"],"discriminator":[170,122,0,190,220,165,67,86],"accounts":[{"name":"recipient","docs":["Receives the lamports from closing the Roles account"],"writable":true},{"name":"authority","docs":["The account with the authority to revoke the configurer role"],"signer":true},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `ADMIN_ROLE_ONDO_SANITY_CHECK` role","# PDA Seeds","- `ADMIN_ROLE_ONDO_SANITY_CHECK`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,79,110,100,111,83,97,110,105,116,121,67,104,101,99,107]},{"kind":"account","path":"authority"}]}},{"name":"role_to_revoke","docs":["The Roles account being closed","# PDA Seeds","- `role_to_revoke.role.seed()` (the seed for the configurer role)","- `role_to_revoke.address` (the user's address)"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"role_to_revoke.role","account":"Roles"},{"kind":"account","path":"role_to_revoke.address","account":"Roles"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"revoke_sanity_setter_role","docs":["Revoke a setter role for sanity check","Signer must have the ADMIN_ROLE_ONDO_SANITY_CHECK role"],"discriminator":[224,165,35,213,84,53,9,229],"accounts":[{"name":"recipient","docs":["Receives the lamports from closing the Roles account"],"writable":true},{"name":"authority","docs":["The account that has the authority to revoke the setter role"],"signer":true},{"name":"authority_role_account","docs":["The role account of the admin","# PDA Seeds","- `RoleType::ADMIN_ROLE_ONDO_SANITY_CHECK`","- `authority` (the authority's address)"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,79,110,100,111,83,97,110,105,116,121,67,104,101,99,107]},{"kind":"account","path":"authority"}]}},{"name":"role_to_revoke","docs":["The role account to be closed","# PDA Seeds","- `role_to_revoke.role.seed()` (the seed for the setter role)","- `role_to_revoke.address` (the user's address)"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"role_to_revoke.role","account":"Roles"},{"kind":"account","path":"role_to_revoke.address","account":"Roles"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"revoke_usdon_role","docs":["Revokes the specified USDon role from a user","Signer must have the GUARDIAN_USDON role"],"discriminator":[231,35,217,203,112,165,182,69],"accounts":[{"name":"authority","docs":["The account with the authority to revoke a role"],"signer":true},{"name":"recipient","docs":["Receives the lamports from closing the `Roles` account"],"writable":true},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `GUARDIAN_USDON` role","# PDA Seeds","- GUARDIAN_USDON","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[71,117,97,114,100,105,97,110,85,83,68,111,110]},{"kind":"account","path":"authority"}]}},{"name":"role_to_revoke","docs":["The Roles account being closed","# PDA Seeds","- Role seed (from RoleType)","- User's address"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"role_to_revoke.role","account":"Roles"},{"kind":"account","path":"role_to_revoke.address","account":"Roles"}]}}],"args":[]},{"name":"set_allowed_deviation_bps","docs":["Set allowed price deviation in basis points","Signer must have the CONFIGURER_ROLE_ONDO_SANITY_CHECK role"],"discriminator":[46,36,29,85,194,180,86,124],"accounts":[{"name":"authority","docs":["The account with the authority to configure the sanity check"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `CONFIGURER_ROLE_ONDO_SANITY_CHECK` role","# PDA Seeds","- `CONFIGURER_ROLE_ONDO_SANITY_CHECK`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[67,111,110,102,105,103,117,114,101,114,82,111,108,101,79,110,100,111,83,97,110,105,116,121,67,104,101,99,107]},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The GM Token mint"]},{"name":"sanity_check_account","docs":["The OracleSanityCheck account to be configured","# PDA Seeds","- `ORACLE_SANITY_CHECK_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,97,110,105,116,121,95,99,104,101,99,107]},{"kind":"account","path":"mint"}]}}],"args":[{"name":"allowed_deviation_bps","type":"u64"}]},{"name":"set_attestation_signer_secp","docs":["Update the secp256k1 attestation signer address","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[121,157,129,65,181,51,55,0],"accounts":[{"name":"authority","docs":["The account with the authority to execute the unpause/configuration operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[{"name":"attestation_signer_secp","type":{"array":["u8",20]}}]},{"name":"set_last_price","docs":["Update the last price in sanity check","Signer must have the SETTER_ROLE_ONDO_SANITY_CHECK role"],"discriminator":[58,56,189,177,199,145,145,43],"accounts":[{"name":"authority","docs":["The account with the authority to set the last price"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `SETTER_ROLE_ONDO_SANITY_CHECK` role","# PDA Seeds","- `SETTER_ROLE_ONDO_SANITY_CHECK`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[83,101,116,116,101,114,82,111,108,101,79,110,100,111,83,97,110,105,116,121,67,104,101,99,107]},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The GM Token mint"]},{"name":"sanity_check_account","docs":["The `OracleSanityCheck` account to be updated","# PDA Seeds","- `ORACLE_SANITY_CHECK_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,97,110,105,116,121,95,99,104,101,99,107]},{"kind":"account","path":"mint"}]}}],"args":[{"name":"last_price","type":"u64"}]},{"name":"set_max_time_delay","docs":["Set maximum time delay for sanity check","Signer must have the CONFIGURER_ROLE_ONDO_SANITY_CHECK role"],"discriminator":[145,127,145,218,239,173,252,9],"accounts":[{"name":"authority","docs":["The account with the authority to configure the sanity check"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `CONFIGURER_ROLE_ONDO_SANITY_CHECK` role","# PDA Seeds","- `CONFIGURER_ROLE_ONDO_SANITY_CHECK`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[67,111,110,102,105,103,117,114,101,114,82,111,108,101,79,110,100,111,83,97,110,105,116,121,67,104,101,99,107]},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The GM Token mint"]},{"name":"sanity_check_account","docs":["The OracleSanityCheck account to be configured","# PDA Seeds","- `ORACLE_SANITY_CHECK_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,97,110,105,116,121,95,99,104,101,99,107]},{"kind":"account","path":"mint"}]}}],"args":[{"name":"max_time_delay","type":"i64"}]},{"name":"set_ondo_user_limits","docs":["Set rate limit for a user","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[86,133,101,215,245,143,28,159],"accounts":[{"name":"authority","docs":["The account with the authority to set user limits"],"signer":true},{"name":"mint","docs":["The GM Token mint"],"relations":["token_limit"]},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"ondo_user","docs":["The `OndoUser` account to update","# PDA Seeds","- `ONDO_USER_SEED`","- User's owner address","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[79,110,100,111,85,115,101,114]},{"kind":"account","path":"ondo_user.owner","account":"OndoUser"},{"kind":"account","path":"mint"}]}},{"name":"token_limit","docs":["The TokenLimit account","# PDA Seeds","- TOKEN_LIMIT_ACCOUNT_SEED","- Mint address"],"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"mint"}]}}],"args":[{"name":"rate_limit","type":"u64"},{"name":"limit_window","type":"u64"}]},{"name":"set_oracle_price_max_age","docs":["Set the maximum age for oracle price data. When USDC oracle prices are more stale than `oracle_price_max_age`","swaps will halt.","Signer must have the ADMIN_ROLE_USDON_MANAGER role"],"discriminator":[198,173,23,10,148,48,248,61],"accounts":[{"name":"authority","docs":["The account with the authority to execute the operation"],"signer":true},{"name":"usdon_manager_state","docs":["The USDonManagerState account to be modified","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `ADMIN_ROLE_USDON_MANAGER` role","# PDA Seeds","- ADMIN_ROLE_USDON_MANAGER","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,85,83,68,111,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}}],"args":[{"name":"oracle_price_max_age","type":"u64"}]},{"name":"set_token_limit","docs":["Update token-level rate limits","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[216,80,182,245,223,87,77,53],"accounts":[{"name":"authority","docs":["The account with the authority to update token limits"],"writable":true,"signer":true},{"name":"mint","docs":["The GM Token or USDon mint"]},{"name":"token_limit","docs":["The `TokenLimit` account to be updated","# PDA Seeds","- `TOKEN_LIMIT_ACCOUNT_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"mint"}]}},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}}],"args":[{"name":"rate_limit","type":{"option":"u64"}},{"name":"limit_window","type":{"option":"u64"}},{"name":"default_user_rate_limit","type":{"option":"u64"}},{"name":"default_user_limit_window","type":{"option":"u64"}}]},{"name":"set_trading_hours_offset","docs":["Set the trading hours offset","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER or ISSUANCE_HOURS_ROLE role"],"discriminator":[65,114,20,50,234,67,255,126],"accounts":[{"name":"authority","docs":["The account with the authority to set the trading hours offset"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER` or `ISSUANCE_HOURS_ROLE`","- The authority's address"],"pda":{"seeds":[{"kind":"account","path":"authority_role_account.role","account":"Roles"},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The GmTokenManagerState account to be modified"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[{"name":"new_trading_hours_offset","type":"i64"}]},{"name":"set_usdc_price_update_address","docs":["Set the USDC price update address","Signer must have the ADMIN_ROLE_USDON_MANAGER role"],"discriminator":[12,142,252,201,224,148,254,32],"accounts":[{"name":"authority","docs":["The account with the authority to execute the operation"],"signer":true},{"name":"usdon_manager_state","docs":["The USDonManagerState account to be modified","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `ADMIN_ROLE_USDON_MANAGER` role","# PDA Seeds","- ADMIN_ROLE_USDON_MANAGER","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,85,83,68,111,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}}],"args":[{"name":"new_price_update_address","type":"pubkey"}]},{"name":"update_scaled_ui_multiplier","docs":["Update the UI multiplier for token display","Signer must have the UPDATE_MULTIPLIER_ROLE role"],"discriminator":[96,212,189,168,220,27,240,70],"accounts":[{"name":"authority","docs":["The account with the authority to update the multiplier"],"writable":true,"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `UPDATE_MULTIPLIER_ROLE` role"],"pda":{"seeds":[{"kind":"const","value":[85,112,100,97,116,101,77,117,108,116,105,112,108,105,101,114,82,111,108,101]},{"kind":"account","path":"authority"}]}},{"name":"mint_authority","docs":["Does not need to be checked for correctness as it is uninitialized."],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"mint","docs":["The mint whose scaled UI multiplier is being updated"],"writable":true},{"name":"usdon_manager_state","docs":["The USDon manager state account, validates that the mint is not the USDon mint"],"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"token_program","docs":["The token program (should be the spl_token_2022 program)"]}],"args":[{"name":"new_multiplier","type":"f64"},{"name":"timestamp","type":"i64"}]},{"name":"update_token_metadata","docs":["Update a token's metadata (name, symbol, URI)","Signer must have the UPDATE_METADATA_ROLE role"],"discriminator":[243,6,8,23,126,181,251,158],"accounts":[{"name":"payer","docs":["Pays for fees if needed"],"writable":true,"signer":true},{"name":"authority","docs":["The operator updating the metadata, if multisig then this is different to the payer","Otherwise, the operator is the same as the payer"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has `UPDATE_METADATA_ROLE` role"],"pda":{"seeds":[{"kind":"const","value":[85,112,100,97,116,101,77,101,116,97,100,97,116,97,82,111,108,101]},{"kind":"account","path":"authority"}]}},{"name":"mint_authority","docs":["Does not need to be checked for correctness as it is uninitialized."],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"mint","docs":["The mint whose metadata is being updated"],"writable":true},{"name":"token_program","docs":["The token program (should be the spl_token_2022 program)"]},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"new_name","type":{"option":"string"}},{"name":"new_symbol","type":{"option":"string"}},{"name":"new_uri","type":{"option":"string"}}]}],"accounts":[{"name":"Attestation","discriminator":[152,125,183,86,36,146,121,73]},{"name":"GMTokenManagerState","discriminator":[120,3,104,163,207,32,156,24]},{"name":"OndoUser","discriminator":[20,5,255,14,176,93,189,142]},{"name":"OracleSanityCheck","discriminator":[27,186,33,40,113,34,206,76]},{"name":"Roles","discriminator":[177,37,17,201,242,158,212,65]},{"name":"TokenLimit","discriminator":[130,254,128,120,255,3,217,51]},{"name":"USDonManagerState","discriminator":[4,39,95,41,51,43,174,91]},{"name":"Whitelist","discriminator":[204,176,52,79,146,121,54,247]}],"events":[{"name":"GMTokenDeployed","discriminator":[155,46,81,223,209,203,89,46]},{"name":"GMTokenMintingPaused","discriminator":[215,17,82,34,76,211,151,22]},{"name":"GMTokenPaused","discriminator":[147,68,57,240,27,46,44,201]},{"name":"GMTokenRedemptionPaused","discriminator":[246,249,230,73,152,65,61,214]},{"name":"RateLimitTokenSet","discriminator":[133,18,77,154,53,236,197,235]},{"name":"RateLimitUserSet","discriminator":[175,49,222,84,192,105,171,129]},{"name":"RoleGranted","discriminator":[220,183,89,228,143,63,246,58]},{"name":"RoleRevoked","discriminator":[167,183,52,229,126,206,62,61]},{"name":"SanityCheckSet","discriminator":[9,207,117,155,44,153,31,234]},{"name":"SanityCheckUpdated","discriminator":[173,97,103,183,67,230,249,1]},{"name":"SetTradingHoursOffset","discriminator":[122,8,3,44,200,105,217,208]},{"name":"TokenFactoryPaused","discriminator":[24,185,192,62,218,128,226,103]},{"name":"TokenManagerMintingPaused","discriminator":[96,11,118,59,8,78,247,254]},{"name":"TokenManagerRedemptionPaused","discriminator":[66,222,165,60,32,59,135,161]},{"name":"TokensRetrieved","discriminator":[217,35,1,89,38,122,145,187]},{"name":"TradeExecuted","discriminator":[41,110,64,129,60,79,179,80]},{"name":"UserAddedToWhitelist","discriminator":[206,139,181,72,116,111,42,42]},{"name":"UserRemovedFromWhitelist","discriminator":[106,223,245,11,213,150,2,15]}],"errors":[{"code":6000,"name":"MissingOrMismatchedSecpIx","msg":"Missing or mismatched secp256k1 verification instruction"},{"code":6001,"name":"MalformedSecpIx","msg":"Malformed secp256k1 instruction"},{"code":6002,"name":"WrongSigCount","msg":"Wrong signature count"},{"code":6003,"name":"WrongDigestLen","msg":"Expected 32-byte hash"},{"code":6004,"name":"DigestMismatch","msg":"Digest mismatch"},{"code":6005,"name":"AddressMismatch","msg":"Recovered address mismatch"}],"types":[{"name":"Attestation","docs":["Attestation account to track consumed attestations","each consumed attestation is stored in its own account"],"type":{"kind":"struct","fields":[{"name":"attestation_id","type":{"array":["u8",16]}},{"name":"creator","type":"pubkey"},{"name":"created_at","type":"i64"},{"name":"bump","type":"u8"}]}},{"name":"GMTokenDeployed","docs":["Event emitted when a GM Token is deployed","Fields:","- gm_token: The public key of the deployed GM Token"],"type":{"kind":"struct","fields":[{"name":"gm_token","type":"pubkey"}]}},{"name":"GMTokenManagerState","docs":["GM Token Manager State account - tracks global state for GM Token operations"],"type":{"kind":"struct","fields":[{"name":"execution_id","type":{"option":"u128"}},{"name":"factory_paused","type":"bool"},{"name":"redemption_paused","type":"bool"},{"name":"minting_paused","type":"bool"},{"name":"bump","type":"u8"},{"name":"attestation_signer_secp","docs":["Ethereum address (20 bytes) for secp256k1 signature verification","Used to verify attestation signatures for buy/sell operations","All zeros ([0u8; 20]) means not set"],"type":{"array":["u8",20]}},{"name":"trading_hours_offset","docs":["Trading hours offset from UTC in seconds","Positive values are east of UTC, negative values are west of UTC"],"type":"i64"}]}},{"name":"GMTokenMintingPaused","docs":["Event emitted when minting for a GM Token is paused or unpaused","Fields:","- is_paused: Boolean indicating if minting/redemptions are paused","- token: The address of the GM Token","- pauser: The address of the operator who performed the pause/unpause action"],"type":{"kind":"struct","fields":[{"name":"is_paused","type":"bool"},{"name":"token","type":"pubkey"},{"name":"pauser","type":"pubkey"}]}},{"name":"GMTokenPaused","docs":["Event emitted when a GM Token is paused/unpaused at the mint-level","Fields:","- is_paused: Boolean indicating whether the token is paused","- token: The address of the GM Token","- pauser: The address of the operator who performed the pause/unpaused action"],"type":{"kind":"struct","fields":[{"name":"is_paused","type":"bool"},{"name":"token","type":"pubkey"},{"name":"pauser","type":"pubkey"}]}},{"name":"GMTokenRedemptionPaused","docs":["Event emitted redemptions for a GM Token are paused or unpaused","Fields:","- is_paused: Boolean indicating if redemptions are paused","- token: The address of the GM Token","- pauser: The address of the operator who performed the pause/unpause action"],"type":{"kind":"struct","fields":[{"name":"is_paused","type":"bool"},{"name":"token","type":"pubkey"},{"name":"pauser","type":"pubkey"}]}},{"name":"OndoUser","docs":["OndoUser state account - tracks user-specific data for a GM Token"],"type":{"kind":"struct","fields":[{"name":"owner","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"rate_limit","type":{"option":"u64"}},{"name":"limit_window","type":{"option":"u64"}},{"name":"mint_capacity_used","type":{"option":"u64"}},{"name":"mint_last_updated","type":{"option":"i64"}},{"name":"redeem_capacity_used","type":{"option":"u64"}},{"name":"redeem_last_updated","type":{"option":"i64"}},{"name":"bump","type":"u8"}]}},{"name":"OracleSanityCheck","docs":["OracleSanityCheck state account - tracks sanity check parameters for a specific mint"],"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"last_price","type":"u64"},{"name":"allowed_deviation_bps","type":"u64"},{"name":"max_time_delay","type":"i64"},{"name":"price_last_updated","type":"i64"},{"name":"bump","type":"u8"}]}},{"name":"RateLimitTokenSet","docs":["Event emitted when a rate limit is set for a token","Fields:","- token: The public key of the token for which the rate limit is set","- limit: The rate limit value","- limit_window: The time window for the rate limit"],"type":{"kind":"struct","fields":[{"name":"token","type":"pubkey"},{"name":"limit","type":{"option":"u64"}},{"name":"limit_window","type":{"option":"u64"}}]}},{"name":"RateLimitUserSet","docs":["Event emitted when a rate limit is set for a user","Fields:","- user: The public key of the user for whom the rate limit is set","- limit: The rate limit value"],"type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"limit","type":"u64"}]}},{"name":"RoleGranted","docs":["Event emitted when a role is granted to a user","Fields:","- role: The role that was granted","- grantee: The public key of the user who was granted the role","- granter: The public key of the user who granted the role"],"type":{"kind":"struct","fields":[{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"grantee","type":"pubkey"},{"name":"granter","type":"pubkey"}]}},{"name":"RoleRevoked","docs":["Event emitted when a role is revoked from a user","Fields:","- role: The role that was revoked","- grantee: The public key of the user who had the role revoked","- revoker: The public key of the user who revoked the role"],"type":{"kind":"struct","fields":[{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"grantee","type":"pubkey"},{"name":"revoker","type":"pubkey"}]}},{"name":"RoleType","type":{"kind":"enum","variants":[{"name":"MinterRoleUSDon"},{"name":"BurnerRoleUSDon"},{"name":"AdminRoleUSDon"},{"name":"AdminRoleUSDonManager"},{"name":"GuardianUSDon"},{"name":"DeployerRoleGMTokenFactory"},{"name":"PauserRoleGMTokenFactory"},{"name":"AdminRoleGMTokenFactory"},{"name":"MinterRoleGMToken"},{"name":"AdminRoleGMToken"},{"name":"PauserRoleGMTokenManager"},{"name":"PauserRoleGMToken"},{"name":"UnpauserRoleGMToken"},{"name":"AdminRoleGMTokenManager"},{"name":"IssuanceHoursRole"},{"name":"SetterRoleOndoSanityCheck"},{"name":"ConfigurerRoleOndoSanityCheck"},{"name":"AdminRoleOndoSanityCheck"},{"name":"AdminRoleWhitelist"},{"name":"UpdateMultiplierRole"},{"name":"UpdateMetadataRole"}]}},{"name":"Roles","docs":["Roles state account - tracks role assignments for addresses"],"type":{"kind":"struct","fields":[{"name":"address","type":"pubkey"},{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"bump","type":"u8"}]}},{"name":"SanityCheckSet","docs":["Event emitted when a sanity check is set for a mint","Fields:","- mint: The public key of the mint for which the sanity check is set","- allowed_deviation_bps: The allowed deviation in basis points","- max_time_delay: The maximum time delay for the sanity check"],"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"allowed_deviation_bps","type":"u64"},{"name":"max_time_delay","type":"i64"}]}},{"name":"SanityCheckUpdated","docs":["Event emitted when a sanity check is updated for a mint","Fields:","- mint: The public key of the mint for which the sanity check is updated","- last_price: The last recorded price (optional)","- allowed_deviation_bps: The allowed deviation in basis points (optional)","- max_time_delay: The maximum time delay for the sanity check (optional)"],"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"last_price","type":{"option":"u64"}},{"name":"allowed_deviation_bps","type":{"option":"u64"}},{"name":"max_time_delay","type":{"option":"i64"}}]}},{"name":"SetTradingHoursOffset","docs":["Event emitted when the trading hours offset is set","Fields:","- prev_trading_hours_offset: The previous trading hours offset","- new_trading_hours_offset: The new trading hours offset"],"type":{"kind":"struct","fields":[{"name":"prev_trading_hours_offset","type":"i64"},{"name":"new_trading_hours_offset","type":"i64"}]}},{"name":"TokenFactoryPaused","docs":["Event emitted when the Token Factory is paused or unpaused","Fields:","- is_paused: Boolean indicating if the Token Factory is paused","- pauser: The address of the operator who performed the pause/unpause action"],"type":{"kind":"struct","fields":[{"name":"is_paused","type":"bool"},{"name":"pauser","type":"pubkey"}]}},{"name":"TokenLimit","docs":["TokenLimit state account - tracks global token limit parameters for a specific GM Token"],"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"rate_limit","type":{"option":"u64"}},{"name":"limit_window","type":{"option":"u64"}},{"name":"mint_capacity_used","type":{"option":"u64"}},{"name":"mint_last_updated","type":{"option":"i64"}},{"name":"redeem_capacity_used","type":{"option":"u64"}},{"name":"redeem_last_updated","type":{"option":"i64"}},{"name":"redemption_paused","type":"bool"},{"name":"minting_paused","type":"bool"},{"name":"default_user_rate_limit","type":{"option":"u64"}},{"name":"default_user_limit_window","type":{"option":"u64"}},{"name":"bump","type":"u8"}]}},{"name":"TokenManagerMintingPaused","docs":["Event emitted when GM Token minting is globally paused or unpaused","Fields:","- is_paused: Boolean indicating if the minting is globally paused","- pauser: The address of the user who performed the pause/unpause action"],"type":{"kind":"struct","fields":[{"name":"is_paused","type":"bool"},{"name":"pauser","type":"pubkey"}]}},{"name":"TokenManagerRedemptionPaused","docs":["Event emitted when GM Token redemptions are globally paused or unpaused","Fields:","- is_paused: Boolean indicating if redemptions are globally paused","- pauser: The address of the operator who performed the pause/unpause action"],"type":{"kind":"struct","fields":[{"name":"is_paused","type":"bool"},{"name":"pauser","type":"pubkey"}]}},{"name":"TokensRetrieved","docs":["Event emitted when tokens are retrieved (withdrawn) from a vault by an admin","Fields:","- token: The public key of the token mint being withdrawn","- to: The destination address receiving the tokens","- amount: The amount of tokens withdrawn","- authority: The public key of the admin who executed the withdrawal"],"type":{"kind":"struct","fields":[{"name":"token","type":"pubkey"},{"name":"to","type":"pubkey"},{"name":"amount","type":"u64"},{"name":"authority","type":"pubkey"}]}},{"name":"TradeExecuted","docs":["Event emitted when a trade is executed","Fields:","- execution_id: The unique identifier of the trade execution"],"type":{"kind":"struct","fields":[{"name":"execution_id","type":"u128"}]}},{"name":"USDonManagerState","docs":["USDonManagerState state account - tracks global configuration for the USDon stablecoin system"],"type":{"kind":"struct","fields":[{"name":"owner","type":"pubkey"},{"name":"usdon_mint","type":"pubkey"},{"name":"oracle_price_enabled","type":"bool"},{"name":"oracle_price_max_age","type":"u64"},{"name":"usdc_price_update","type":"pubkey"},{"name":"usdc_vault","type":"pubkey"},{"name":"usdon_vault","type":"pubkey"},{"name":"bump","type":"u8"}]}},{"name":"UserAddedToWhitelist","docs":["Event emitted when a user is added to the whitelist","Fields:","- user: The public key of the user being added to the whitelist","- added_by: The public key of the admin who added the user to the whitelist"],"type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"added_by","type":"pubkey"}]}},{"name":"UserRemovedFromWhitelist","docs":["Event emitted when a user is removed from the whitelist","Fields:","- user: The public key of the user being removed from the whitelist","- removed_by: The public key of the admin who removed the user from the whitelist"],"type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"removed_by","type":"pubkey"}]}},{"name":"Whitelist","docs":["Whitelist account - tracks whitelisted addresses.","","Used as a marker account - presence of the account indicates whitelisting."],"type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"}]}}]} \ No newline at end of file diff --git a/idls/openbook_v2.opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb.json b/idls/openbook_v2.opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb.json new file mode 100644 index 0000000..4869fc9 --- /dev/null +++ b/idls/openbook_v2.opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"openbook_v2","instructions":[{"name":"createMarket","docs":["Create a [`Market`](crate::state::Market) for a given token pair."],"accounts":[{"name":"market","isMut":true,"isSigner":true},{"name":"marketAuthority","isMut":false,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false,"docs":["Accounts are initialized by client,","anchor discriminator is set first when ix exits,"]},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"marketBaseVault","isMut":true,"isSigner":false},{"name":"marketQuoteVault","isMut":true,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"oracleA","isMut":false,"isSigner":false,"isOptional":true},{"name":"oracleB","isMut":false,"isSigner":false,"isOptional":true},{"name":"collectFeeAdmin","isMut":false,"isSigner":false},{"name":"openOrdersAdmin","isMut":false,"isSigner":false,"isOptional":true},{"name":"consumeEventsAdmin","isMut":false,"isSigner":false,"isOptional":true},{"name":"closeMarketAdmin","isMut":false,"isSigner":false,"isOptional":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"name","type":"string"},{"name":"oracleConfig","type":{"defined":"OracleConfigParams"}},{"name":"quoteLotSize","type":"i64"},{"name":"baseLotSize","type":"i64"},{"name":"makerFee","type":"i64"},{"name":"takerFee","type":"i64"},{"name":"timeExpiry","type":"i64"}]},{"name":"closeMarket","docs":["Close a [`Market`](crate::state::Market) (only","[`close_market_admin`](crate::state::Market::close_market_admin))."],"accounts":[{"name":"closeMarketAdmin","isMut":false,"isSigner":true},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false},{"name":"solDestination","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"createOpenOrdersIndexer","docs":["Create an [`OpenOrdersIndexer`](crate::state::OpenOrdersIndexer) account."],"accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"owner","isMut":false,"isSigner":true},{"name":"openOrdersIndexer","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"closeOpenOrdersIndexer","docs":["Close an [`OpenOrdersIndexer`](crate::state::OpenOrdersIndexer) account."],"accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"openOrdersIndexer","isMut":true,"isSigner":false},{"name":"solDestination","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"createOpenOrdersAccount","docs":["Create an [`OpenOrdersAccount`](crate::state::OpenOrdersAccount)."],"accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"owner","isMut":false,"isSigner":true},{"name":"delegateAccount","isMut":false,"isSigner":false,"isOptional":true},{"name":"openOrdersIndexer","isMut":true,"isSigner":false},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"market","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"name","type":"string"}]},{"name":"closeOpenOrdersAccount","docs":["Close an [`OpenOrdersAccount`](crate::state::OpenOrdersAccount)."],"accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"openOrdersIndexer","isMut":true,"isSigner":false},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"solDestination","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"placeOrder","docs":["Place an order.","","Different types of orders have different effects on the order book,","as described in [`PlaceOrderType`](crate::state::PlaceOrderType).","","`price_lots` refers to the price in lots: the number of quote lots","per base lot. It is ignored for `PlaceOrderType::Market` orders.","","`expiry_timestamp` is a unix timestamp for when this order should","expire. If 0 is passed in, the order will never expire. If the time","is in the past, the instruction is skipped. Timestamps in the future","are reduced to now + 65,535s.","","`limit` determines the maximum number of orders from the book to fill,","and can be used to limit CU spent. When the limit is reached, processing","stops and the instruction succeeds."],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"openOrdersAdmin","isMut":false,"isSigner":true,"isOptional":true},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false},{"name":"marketVault","isMut":true,"isSigner":false},{"name":"oracleA","isMut":false,"isSigner":false,"isOptional":true},{"name":"oracleB","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"PlaceOrderArgs"}}],"returns":{"option":"u128"}},{"name":"editOrder","docs":["Edit an order."],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"openOrdersAdmin","isMut":false,"isSigner":true,"isOptional":true},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false},{"name":"marketVault","isMut":true,"isSigner":false},{"name":"oracleA","isMut":false,"isSigner":false,"isOptional":true},{"name":"oracleB","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"clientOrderId","type":"u64"},{"name":"expectedCancelSize","type":"i64"},{"name":"placeOrder","type":{"defined":"PlaceOrderArgs"}}],"returns":{"option":"u128"}},{"name":"editOrderPegged","docs":["Edit an order pegged."],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"openOrdersAdmin","isMut":false,"isSigner":true,"isOptional":true},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false},{"name":"marketVault","isMut":true,"isSigner":false},{"name":"oracleA","isMut":false,"isSigner":false,"isOptional":true},{"name":"oracleB","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"clientOrderId","type":"u64"},{"name":"expectedCancelSize","type":"i64"},{"name":"placeOrder","type":{"defined":"PlaceOrderPeggedArgs"}}],"returns":{"option":"u128"}},{"name":"placeOrders","docs":["Place multiple orders"],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"openOrdersAdmin","isMut":false,"isSigner":true,"isOptional":true},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false},{"name":"marketQuoteVault","isMut":true,"isSigner":false},{"name":"marketBaseVault","isMut":true,"isSigner":false},{"name":"oracleA","isMut":false,"isSigner":false,"isOptional":true},{"name":"oracleB","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"ordersType","type":{"defined":"PlaceOrderType"}},{"name":"bids","type":{"vec":{"defined":"PlaceMultipleOrdersArgs"}}},{"name":"asks","type":{"vec":{"defined":"PlaceMultipleOrdersArgs"}}},{"name":"limit","type":"u8"}],"returns":{"vec":{"option":"u128"}}},{"name":"cancelAllAndPlaceOrders","docs":["Cancel orders and place multiple orders."],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"openOrdersAdmin","isMut":false,"isSigner":true,"isOptional":true},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false},{"name":"marketQuoteVault","isMut":true,"isSigner":false},{"name":"marketBaseVault","isMut":true,"isSigner":false},{"name":"oracleA","isMut":false,"isSigner":false,"isOptional":true},{"name":"oracleB","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"ordersType","type":{"defined":"PlaceOrderType"}},{"name":"bids","type":{"vec":{"defined":"PlaceMultipleOrdersArgs"}}},{"name":"asks","type":{"vec":{"defined":"PlaceMultipleOrdersArgs"}}},{"name":"limit","type":"u8"}],"returns":{"vec":{"option":"u128"}}},{"name":"placeOrderPegged","docs":["Place an oracle-peg order."],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"openOrdersAdmin","isMut":false,"isSigner":true,"isOptional":true},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false},{"name":"marketVault","isMut":true,"isSigner":false},{"name":"oracleA","isMut":false,"isSigner":false,"isOptional":true},{"name":"oracleB","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"PlaceOrderPeggedArgs"}}],"returns":{"option":"u128"}},{"name":"placeTakeOrder","docs":["Place an order that shall take existing liquidity off of the book, not","add a new order off the book.","","This type of order allows for instant token settlement for the taker."],"accounts":[{"name":"signer","isMut":true,"isSigner":true},{"name":"penaltyPayer","isMut":true,"isSigner":true},{"name":"market","isMut":true,"isSigner":false},{"name":"marketAuthority","isMut":false,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"marketBaseVault","isMut":true,"isSigner":false},{"name":"marketQuoteVault","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"oracleA","isMut":false,"isSigner":false,"isOptional":true},{"name":"oracleB","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"openOrdersAdmin","isMut":false,"isSigner":true,"isOptional":true}],"args":[{"name":"args","type":{"defined":"PlaceTakeOrderArgs"}}]},{"name":"consumeEvents","docs":["Process up to `limit` [events](crate::state::AnyEvent).","","When a user places a 'take' order, they do not know beforehand which","market maker will have placed the 'make' order that they get executed","against. This prevents them from passing in a market maker's","[`OpenOrdersAccount`](crate::state::OpenOrdersAccount), which is needed","to credit/debit the relevant tokens to/from the maker. As such, Openbook","uses a 'crank' system, where `place_order` only emits events, and","`consume_events` handles token settlement.","","Currently, there are two types of events: [`FillEvent`](crate::state::FillEvent)s","and [`OutEvent`](crate::state::OutEvent)s.","","A `FillEvent` is emitted when an order is filled, and it is handled by","debiting whatever the taker is selling from the taker and crediting","it to the maker, and debiting whatever the taker is buying from the","maker and crediting it to the taker. Note that *no tokens are moved*,","these are just debits and credits to each party's [`Position`](crate::state::Position).","","An `OutEvent` is emitted when a limit order needs to be removed from","the book during a `place_order` invocation, and it is handled by","crediting whatever the maker would have sold (quote token in a bid,","base token in an ask) back to the maker."],"accounts":[{"name":"consumeEventsAdmin","isMut":false,"isSigner":true,"isOptional":true},{"name":"market","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false}],"args":[{"name":"limit","type":"u64"}]},{"name":"consumeGivenEvents","docs":["Process the [events](crate::state::AnyEvent) at the given positions."],"accounts":[{"name":"consumeEventsAdmin","isMut":false,"isSigner":true,"isOptional":true},{"name":"market","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false}],"args":[{"name":"slots","type":{"vec":"u64"}}]},{"name":"cancelOrder","docs":["Cancel an order by its `order_id`.","","Note that this doesn't emit an [`OutEvent`](crate::state::OutEvent) because a","maker knows that they will be passing in their own [`OpenOrdersAccount`](crate::state::OpenOrdersAccount)."],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"market","isMut":false,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false}],"args":[{"name":"orderId","type":"u128"}]},{"name":"cancelOrderByClientOrderId","docs":["Cancel an order by its `client_order_id`.","","Note that this doesn't emit an [`OutEvent`](crate::state::OutEvent) because a","maker knows that they will be passing in their own [`OpenOrdersAccount`](crate::state::OpenOrdersAccount)."],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"market","isMut":false,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false}],"args":[{"name":"clientOrderId","type":"u64"}],"returns":"i64"},{"name":"cancelAllOrders","docs":["Cancel up to `limit` orders, optionally filtering by side"],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"market","isMut":false,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false}],"args":[{"name":"sideOption","type":{"option":{"defined":"Side"}}},{"name":"limit","type":"u8"}]},{"name":"deposit","docs":["Deposit a certain amount of `base` and `quote` lamports into one's","[`Position`](crate::state::Position).","","Makers might wish to `deposit`, rather than have actual tokens moved for","each trade, in order to reduce CUs."],"accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"marketBaseVault","isMut":true,"isSigner":false},{"name":"marketQuoteVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"baseAmount","type":"u64"},{"name":"quoteAmount","type":"u64"}]},{"name":"refill","docs":["Refill a certain amount of `base` and `quote` lamports. The amount being passed is the","total lamports that the [`Position`](crate::state::Position) will have.","","Makers might wish to `refill`, rather than have actual tokens moved for","each trade, in order to reduce CUs."],"accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"marketBaseVault","isMut":true,"isSigner":false},{"name":"marketQuoteVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"baseAmount","type":"u64"},{"name":"quoteAmount","type":"u64"}]},{"name":"settleFunds","docs":["Withdraw any available tokens."],"accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"penaltyPayer","isMut":true,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"marketAuthority","isMut":false,"isSigner":false},{"name":"marketBaseVault","isMut":true,"isSigner":false},{"name":"marketQuoteVault","isMut":true,"isSigner":false},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"referrerAccount","isMut":true,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"settleFundsExpired","docs":["Withdraw any available tokens when the market is expired (only","[`close_market_admin`](crate::state::Market::close_market_admin))."],"accounts":[{"name":"closeMarketAdmin","isMut":false,"isSigner":true},{"name":"owner","isMut":true,"isSigner":true},{"name":"penaltyPayer","isMut":true,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"marketAuthority","isMut":false,"isSigner":false},{"name":"marketBaseVault","isMut":true,"isSigner":false},{"name":"marketQuoteVault","isMut":true,"isSigner":false},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"referrerAccount","isMut":true,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"sweepFees","docs":["Sweep fees, as a [`Market`](crate::state::Market)'s admin."],"accounts":[{"name":"collectFeeAdmin","isMut":false,"isSigner":true},{"name":"market","isMut":true,"isSigner":false},{"name":"marketAuthority","isMut":false,"isSigner":false},{"name":"marketQuoteVault","isMut":true,"isSigner":false},{"name":"tokenReceiverAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"setDelegate","docs":["Update the [`delegate`](crate::state::OpenOrdersAccount::delegate) of an open orders account."],"accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"delegateAccount","isMut":false,"isSigner":false,"isOptional":true}],"args":[]},{"name":"setMarketExpired","docs":["Set market to expired before pruning orders and closing the market (only","[`close_market_admin`](crate::state::Market::close_market_admin))."],"accounts":[{"name":"closeMarketAdmin","isMut":false,"isSigner":true},{"name":"market","isMut":true,"isSigner":false}],"args":[]},{"name":"pruneOrders","docs":["Remove orders from the book when the market is expired (only","[`close_market_admin`](crate::state::Market::close_market_admin))."],"accounts":[{"name":"closeMarketAdmin","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"market","isMut":false,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false}],"args":[{"name":"limit","type":"u8"}]},{"name":"stubOracleCreate","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"owner","isMut":false,"isSigner":true},{"name":"oracle","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"price","type":"f64"}]},{"name":"stubOracleClose","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"oracle","isMut":true,"isSigner":false},{"name":"solDestination","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"stubOracleSet","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"oracle","isMut":true,"isSigner":false}],"args":[{"name":"price","type":"f64"}]}],"accounts":[{"name":"Market","type":{"kind":"struct","fields":[{"name":"bump","docs":["PDA bump"],"type":"u8"},{"name":"baseDecimals","docs":["Number of decimals used for the base token.","","Used to convert the oracle's price into a native/native price."],"type":"u8"},{"name":"quoteDecimals","type":"u8"},{"name":"padding1","type":{"array":["u8",5]}},{"name":"marketAuthority","type":"publicKey"},{"name":"timeExpiry","docs":["No expiry = 0. Market will expire and no trading allowed after time_expiry"],"type":"i64"},{"name":"collectFeeAdmin","docs":["Admin who can collect fees from the market"],"type":"publicKey"},{"name":"openOrdersAdmin","docs":["Admin who must sign off on all order creations"],"type":{"defined":"NonZeroPubkeyOption"}},{"name":"consumeEventsAdmin","docs":["Admin who must sign off on all event consumptions"],"type":{"defined":"NonZeroPubkeyOption"}},{"name":"closeMarketAdmin","docs":["Admin who can set market expired, prune orders and close the market"],"type":{"defined":"NonZeroPubkeyOption"}},{"name":"name","docs":["Name. Trailing zero bytes are ignored."],"type":{"array":["u8",16]}},{"name":"bids","docs":["Address of the BookSide account for bids"],"type":"publicKey"},{"name":"asks","docs":["Address of the BookSide account for asks"],"type":"publicKey"},{"name":"eventHeap","docs":["Address of the EventHeap account"],"type":"publicKey"},{"name":"oracleA","docs":["Oracles account address"],"type":{"defined":"NonZeroPubkeyOption"}},{"name":"oracleB","type":{"defined":"NonZeroPubkeyOption"}},{"name":"oracleConfig","docs":["Oracle configuration"],"type":{"defined":"OracleConfig"}},{"name":"quoteLotSize","docs":["Number of quote native in a quote lot. Must be a power of 10.","","Primarily useful for increasing the tick size on the market: A lot price","of 1 becomes a native price of quote_lot_size/base_lot_size becomes a","ui price of quote_lot_size*base_decimals/base_lot_size/quote_decimals."],"type":"i64"},{"name":"baseLotSize","docs":["Number of base native in a base lot. Must be a power of 10.","","Example: If base decimals for the underlying asset is 6, base lot size","is 100 and and base position lots is 10_000 then base position native is","1_000_000 and base position ui is 1."],"type":"i64"},{"name":"seqNum","docs":["Total number of orders seen"],"type":"u64"},{"name":"registrationTime","docs":["Timestamp in seconds that the market was registered at."],"type":"i64"},{"name":"makerFee","docs":["Fees","","Fee (in 10^-6) when matching maker orders.","maker_fee < 0 it means some of the taker_fees goes to the maker","maker_fee > 0, it means no taker_fee to the maker, and maker fee goes to the referral"],"type":"i64"},{"name":"takerFee","docs":["Fee (in 10^-6) for taker orders, always >= 0."],"type":"i64"},{"name":"feesAccrued","docs":["Total fees accrued in native quote"],"type":"u128"},{"name":"feesToReferrers","docs":["Total fees settled in native quote"],"type":"u128"},{"name":"referrerRebatesAccrued","docs":["Referrer rebates to be distributed"],"type":"u64"},{"name":"feesAvailable","docs":["Fees generated and available to withdraw via sweep_fees"],"type":"u64"},{"name":"makerVolume","docs":["Cumulative maker volume (same as taker volume) in quote native units"],"type":"u128"},{"name":"takerVolumeWoOo","docs":["Cumulative taker volume in quote native units due to place take orders"],"type":"u128"},{"name":"baseMint","type":"publicKey"},{"name":"quoteMint","type":"publicKey"},{"name":"marketBaseVault","type":"publicKey"},{"name":"baseDepositTotal","type":"u64"},{"name":"marketQuoteVault","type":"publicKey"},{"name":"quoteDepositTotal","type":"u64"},{"name":"reserved","type":{"array":["u8",128]}}]}},{"name":"OpenOrdersAccount","type":{"kind":"struct","fields":[{"name":"owner","type":"publicKey"},{"name":"market","type":"publicKey"},{"name":"name","type":{"array":["u8",32]}},{"name":"delegate","type":{"defined":"NonZeroPubkeyOption"}},{"name":"accountNum","type":"u32"},{"name":"bump","type":"u8"},{"name":"version","type":"u8"},{"name":"padding","type":{"array":["u8",2]}},{"name":"position","type":{"defined":"Position"}},{"name":"openOrders","type":{"array":[{"defined":"OpenOrder"},24]}}]}},{"name":"OpenOrdersIndexer","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"createdCounter","type":"u32"},{"name":"addresses","type":{"vec":"publicKey"}}]}},{"name":"StubOracle","type":{"kind":"struct","fields":[{"name":"owner","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"price","type":"f64"},{"name":"lastUpdateTs","type":"i64"},{"name":"lastUpdateSlot","type":"u64"},{"name":"deviation","type":"f64"},{"name":"reserved","type":{"array":["u8",104]}}]}},{"name":"BookSide","type":{"kind":"struct","fields":[{"name":"roots","type":{"array":[{"defined":"OrderTreeRoot"},2]}},{"name":"reservedRoots","type":{"array":[{"defined":"OrderTreeRoot"},4]}},{"name":"reserved","type":{"array":["u8",256]}},{"name":"nodes","type":{"defined":"OrderTreeNodes"}}]}},{"name":"EventHeap","docs":["Container for the different EventTypes.","","Events are stored in a fixed-array of nodes. Free nodes are connected by a single-linked list","starting at free_head while used nodes form a circular doubly-linked list starting at","used_head."],"type":{"kind":"struct","fields":[{"name":"header","type":{"defined":"EventHeapHeader"}},{"name":"nodes","type":{"array":[{"defined":"EventNode"},600]}},{"name":"reserved","type":{"array":["u8",64]}}]}}],"types":[{"name":"NonZeroPubkeyOption","docs":["Like `Option`, but implemented for `Pubkey` to be used with `zero_copy`"],"type":{"kind":"struct","fields":[{"name":"key","type":"publicKey"}]}},{"name":"Position","type":{"kind":"struct","fields":[{"name":"bidsBaseLots","docs":["Base lots in open bids"],"type":"i64"},{"name":"asksBaseLots","docs":["Base lots in open asks"],"type":"i64"},{"name":"baseFreeNative","type":"u64"},{"name":"quoteFreeNative","type":"u64"},{"name":"lockedMakerFees","type":"u64"},{"name":"referrerRebatesAvailable","type":"u64"},{"name":"penaltyHeapCount","docs":["Count of ixs when events are added to the heap","To avoid this, send remaining accounts in order to process the events"],"type":"u64"},{"name":"makerVolume","docs":["Cumulative maker volume in quote native units (display only)"],"type":"u128"},{"name":"takerVolume","docs":["Cumulative taker volume in quote native units (display only)"],"type":"u128"},{"name":"bidsQuoteLots","docs":["Quote lots in open bids"],"type":"i64"},{"name":"reserved","type":{"array":["u8",64]}}]}},{"name":"OpenOrder","type":{"kind":"struct","fields":[{"name":"id","type":"u128"},{"name":"clientId","type":"u64"},{"name":"lockedPrice","docs":["Price at which user's assets were locked"],"type":"i64"},{"name":"isFree","type":"u8"},{"name":"sideAndTree","type":"u8"},{"name":"padding","type":{"array":["u8",6]}}]}},{"name":"OracleConfig","type":{"kind":"struct","fields":[{"name":"confFilter","type":"f64"},{"name":"maxStalenessSlots","type":"i64"},{"name":"reserved","type":{"array":["u8",72]}}]}},{"name":"OracleConfigParams","type":{"kind":"struct","fields":[{"name":"confFilter","type":"f32"},{"name":"maxStalenessSlots","type":{"option":"u32"}}]}},{"name":"EventHeapHeader","type":{"kind":"struct","fields":[{"name":"freeHead","type":"u16"},{"name":"usedHead","type":"u16"},{"name":"count","type":"u16"},{"name":"padd","type":"u16"},{"name":"seqNum","type":"u64"}]}},{"name":"EventNode","type":{"kind":"struct","fields":[{"name":"next","type":"u16"},{"name":"prev","type":"u16"},{"name":"pad","type":{"array":["u8",4]}},{"name":"event","type":{"defined":"AnyEvent"}}]}},{"name":"AnyEvent","type":{"kind":"struct","fields":[{"name":"eventType","type":"u8"},{"name":"padding","type":{"array":["u8",143]}}]}},{"name":"FillEvent","type":{"kind":"struct","fields":[{"name":"eventType","type":"u8"},{"name":"takerSide","type":"u8"},{"name":"makerOut","type":"u8"},{"name":"makerSlot","type":"u8"},{"name":"padding","type":{"array":["u8",4]}},{"name":"timestamp","type":"u64"},{"name":"seqNum","type":"u64"},{"name":"maker","type":"publicKey"},{"name":"makerTimestamp","type":"u64"},{"name":"taker","type":"publicKey"},{"name":"takerClientOrderId","type":"u64"},{"name":"price","type":"i64"},{"name":"pegLimit","type":"i64"},{"name":"quantity","type":"i64"},{"name":"makerClientOrderId","type":"u64"},{"name":"reserved","type":{"array":["u8",8]}}]}},{"name":"OutEvent","type":{"kind":"struct","fields":[{"name":"eventType","type":"u8"},{"name":"side","type":"u8"},{"name":"ownerSlot","type":"u8"},{"name":"padding0","type":{"array":["u8",5]}},{"name":"timestamp","type":"u64"},{"name":"seqNum","type":"u64"},{"name":"owner","type":"publicKey"},{"name":"quantity","type":"i64"},{"name":"padding1","type":{"array":["u8",80]}}]}},{"name":"InnerNode","docs":["InnerNodes and LeafNodes compose the binary tree of orders.","","Each InnerNode has exactly two children, which are either InnerNodes themselves,","or LeafNodes. The children share the top `prefix_len` bits of `key`. The left","child has a 0 in the next bit, and the right a 1."],"type":{"kind":"struct","fields":[{"name":"tag","type":"u8"},{"name":"padding","type":{"array":["u8",3]}},{"name":"prefixLen","docs":["number of highest `key` bits that all children share","e.g. if it's 2, the two highest bits of `key` will be the same on all children"],"type":"u32"},{"name":"key","docs":["only the top `prefix_len` bits of `key` are relevant"],"type":"u128"},{"name":"children","docs":["indexes into `BookSide::nodes`"],"type":{"array":["u32",2]}},{"name":"childEarliestExpiry","docs":["The earliest expiry timestamp for the left and right subtrees.","","Needed to be able to find and remove expired orders without having to","iterate through the whole bookside."],"type":{"array":["u64",2]}},{"name":"reserved","type":{"array":["u8",40]}}]}},{"name":"LeafNode","docs":["LeafNodes represent an order in the binary tree"],"type":{"kind":"struct","fields":[{"name":"tag","docs":["NodeTag"],"type":"u8"},{"name":"ownerSlot","docs":["Index into the owning OpenOrdersAccount's OpenOrders"],"type":"u8"},{"name":"timeInForce","docs":["Time in seconds after `timestamp` at which the order expires.","A value of 0 means no expiry."],"type":"u16"},{"name":"padding","type":{"array":["u8",4]}},{"name":"key","docs":["The binary tree key, see new_node_key()"],"type":"u128"},{"name":"owner","docs":["Address of the owning OpenOrdersAccount"],"type":"publicKey"},{"name":"quantity","docs":["Number of base lots to buy or sell, always >=1"],"type":"i64"},{"name":"timestamp","docs":["The time the order was placed"],"type":"u64"},{"name":"pegLimit","docs":["If the effective price of an oracle pegged order exceeds this limit,","it will be considered invalid and may be removed.","","Only applicable in the oracle_pegged OrderTree"],"type":"i64"},{"name":"clientOrderId","docs":["User defined id for this order, used in FillEvents"],"type":"u64"}]}},{"name":"AnyNode","type":{"kind":"struct","fields":[{"name":"tag","type":"u8"},{"name":"data","type":{"array":["u8",87]}}]}},{"name":"OrderTreeRoot","type":{"kind":"struct","fields":[{"name":"maybeNode","type":"u32"},{"name":"leafCount","type":"u32"}]}},{"name":"OrderTreeNodes","docs":["A binary tree on AnyNode::key()","","The key encodes the price in the top 64 bits."],"type":{"kind":"struct","fields":[{"name":"orderTreeType","type":"u8"},{"name":"padding","type":{"array":["u8",3]}},{"name":"bumpIndex","type":"u32"},{"name":"freeListLen","type":"u32"},{"name":"freeListHead","type":"u32"},{"name":"reserved","type":{"array":["u8",512]}},{"name":"nodes","type":{"array":[{"defined":"AnyNode"},1024]}}]}},{"name":"I80F48","docs":["Nothing in Rust shall use these types. They only exist so that the Anchor IDL","knows about them and typescript can deserialize it."],"type":{"kind":"struct","fields":[{"name":"val","type":"i128"}]}},{"name":"PlaceOrderArgs","type":{"kind":"struct","fields":[{"name":"side","type":{"defined":"Side"}},{"name":"priceLots","type":"i64"},{"name":"maxBaseLots","type":"i64"},{"name":"maxQuoteLotsIncludingFees","type":"i64"},{"name":"clientOrderId","type":"u64"},{"name":"orderType","type":{"defined":"PlaceOrderType"}},{"name":"expiryTimestamp","type":"u64"},{"name":"selfTradeBehavior","type":{"defined":"SelfTradeBehavior"}},{"name":"limit","type":"u8"}]}},{"name":"PlaceMultipleOrdersArgs","type":{"kind":"struct","fields":[{"name":"priceLots","type":"i64"},{"name":"maxQuoteLotsIncludingFees","type":"i64"},{"name":"expiryTimestamp","type":"u64"}]}},{"name":"PlaceOrderPeggedArgs","type":{"kind":"struct","fields":[{"name":"side","type":{"defined":"Side"}},{"name":"priceOffsetLots","type":"i64"},{"name":"pegLimit","type":"i64"},{"name":"maxBaseLots","type":"i64"},{"name":"maxQuoteLotsIncludingFees","type":"i64"},{"name":"clientOrderId","type":"u64"},{"name":"orderType","type":{"defined":"PlaceOrderType"}},{"name":"expiryTimestamp","type":"u64"},{"name":"selfTradeBehavior","type":{"defined":"SelfTradeBehavior"}},{"name":"limit","type":"u8"}]}},{"name":"PlaceTakeOrderArgs","type":{"kind":"struct","fields":[{"name":"side","type":{"defined":"Side"}},{"name":"priceLots","type":"i64"},{"name":"maxBaseLots","type":"i64"},{"name":"maxQuoteLotsIncludingFees","type":"i64"},{"name":"orderType","type":{"defined":"PlaceOrderType"}},{"name":"limit","type":"u8"}]}},{"name":"OracleType","type":{"kind":"enum","variants":[{"name":"Pyth"},{"name":"Stub"},{"name":"SwitchboardV1"},{"name":"SwitchboardV2"},{"name":"RaydiumCLMM"}]}},{"name":"OrderState","type":{"kind":"enum","variants":[{"name":"Valid"},{"name":"Invalid"},{"name":"Skipped"}]}},{"name":"BookSideOrderTree","type":{"kind":"enum","variants":[{"name":"Fixed"},{"name":"OraclePegged"}]}},{"name":"EventType","type":{"kind":"enum","variants":[{"name":"Fill"},{"name":"Out"}]}},{"name":"NodeTag","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"InnerNode"},{"name":"LeafNode"},{"name":"FreeNode"},{"name":"LastFreeNode"}]}},{"name":"PlaceOrderType","type":{"kind":"enum","variants":[{"name":"Limit"},{"name":"ImmediateOrCancel"},{"name":"PostOnly"},{"name":"Market"},{"name":"PostOnlySlide"}]}},{"name":"PostOrderType","type":{"kind":"enum","variants":[{"name":"Limit"},{"name":"PostOnly"},{"name":"PostOnlySlide"}]}},{"name":"SelfTradeBehavior","docs":["Self trade behavior controls how taker orders interact with resting limit orders of the same account.","This setting has no influence on placing a resting or oracle pegged limit order that does not match","immediately, instead it's the responsibility of the user to correctly configure his taker orders."],"type":{"kind":"enum","variants":[{"name":"DecrementTake"},{"name":"CancelProvide"},{"name":"AbortTransaction"}]}},{"name":"Side","type":{"kind":"enum","variants":[{"name":"Bid"},{"name":"Ask"}]}},{"name":"SideAndOrderTree","docs":["SideAndOrderTree is a storage optimization, so we don't need two bytes for the data"],"type":{"kind":"enum","variants":[{"name":"BidFixed"},{"name":"AskFixed"},{"name":"BidOraclePegged"},{"name":"AskOraclePegged"}]}},{"name":"OrderParams","type":{"kind":"enum","variants":[{"name":"Market"},{"name":"ImmediateOrCancel","fields":[{"name":"price_lots","type":"i64"}]},{"name":"Fixed","fields":[{"name":"price_lots","type":"i64"},{"name":"order_type","type":{"defined":"PostOrderType"}}]},{"name":"OraclePegged","fields":[{"name":"price_offset_lots","type":"i64"},{"name":"order_type","type":{"defined":"PostOrderType"}},{"name":"peg_limit","type":"i64"}]}]}},{"name":"OrderTreeType","type":{"kind":"enum","variants":[{"name":"Bids"},{"name":"Asks"}]}}],"events":[{"name":"DepositLog","fields":[{"name":"openOrdersAccount","type":"publicKey","index":false},{"name":"signer","type":"publicKey","index":false},{"name":"baseAmount","type":"u64","index":false},{"name":"quoteAmount","type":"u64","index":false}]},{"name":"FillLog","fields":[{"name":"market","type":"publicKey","index":false},{"name":"takerSide","type":"u8","index":false},{"name":"makerSlot","type":"u8","index":false},{"name":"makerOut","type":"bool","index":false},{"name":"timestamp","type":"u64","index":false},{"name":"seqNum","type":"u64","index":false},{"name":"maker","type":"publicKey","index":false},{"name":"makerClientOrderId","type":"u64","index":false},{"name":"makerFee","type":"u64","index":false},{"name":"makerTimestamp","type":"u64","index":false},{"name":"taker","type":"publicKey","index":false},{"name":"takerClientOrderId","type":"u64","index":false},{"name":"takerFeeCeil","type":"u64","index":false},{"name":"price","type":"i64","index":false},{"name":"quantity","type":"i64","index":false}]},{"name":"MarketMetaDataLog","fields":[{"name":"market","type":"publicKey","index":false},{"name":"name","type":"string","index":false},{"name":"baseMint","type":"publicKey","index":false},{"name":"quoteMint","type":"publicKey","index":false},{"name":"baseDecimals","type":"u8","index":false},{"name":"quoteDecimals","type":"u8","index":false},{"name":"baseLotSize","type":"i64","index":false},{"name":"quoteLotSize","type":"i64","index":false}]},{"name":"TotalOrderFillEvent","fields":[{"name":"side","type":"u8","index":false},{"name":"taker","type":"publicKey","index":false},{"name":"totalQuantityPaid","type":"u64","index":false},{"name":"totalQuantityReceived","type":"u64","index":false},{"name":"fees","type":"u64","index":false}]},{"name":"SetDelegateLog","fields":[{"name":"openOrdersAccount","type":"publicKey","index":false},{"name":"delegate","type":{"option":"publicKey"},"index":false}]},{"name":"SettleFundsLog","fields":[{"name":"openOrdersAccount","type":"publicKey","index":false},{"name":"baseNative","type":"u64","index":false},{"name":"quoteNative","type":"u64","index":false},{"name":"referrerRebate","type":"u64","index":false},{"name":"referrer","type":{"option":"publicKey"},"index":false}]},{"name":"SweepFeesLog","fields":[{"name":"market","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"receiver","type":"publicKey","index":false}]},{"name":"OpenOrdersPositionLog","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"openOrdersAccountNum","type":"u32","index":false},{"name":"market","type":"publicKey","index":false},{"name":"bidsBaseLots","type":"i64","index":false},{"name":"bidsQuoteLots","type":"i64","index":false},{"name":"asksBaseLots","type":"i64","index":false},{"name":"baseFreeNative","type":"u64","index":false},{"name":"quoteFreeNative","type":"u64","index":false},{"name":"lockedMakerFees","type":"u64","index":false},{"name":"referrerRebatesAvailable","type":"u64","index":false},{"name":"makerVolume","type":"u128","index":false},{"name":"takerVolume","type":"u128","index":false}]}],"errors":[{"code":6000,"name":"SomeError","msg":""},{"code":6001,"name":"InvalidInputNameLength","msg":"Name lenght above limit"},{"code":6002,"name":"InvalidInputMarketExpired","msg":"Market cannot be created as expired"},{"code":6003,"name":"InvalidInputMarketFees","msg":"Taker fees should be positive and if maker fees are negative, greater or equal to their abs value"},{"code":6004,"name":"InvalidInputLots","msg":"Lots cannot be negative"},{"code":6005,"name":"InvalidInputLotsSize","msg":"Lots size above market limits"},{"code":6006,"name":"InvalidInputOrdersAmounts","msg":"Input amounts above limits"},{"code":6007,"name":"InvalidInputCancelSize","msg":"Price lots should be greater than zero"},{"code":6008,"name":"InvalidInputPriceLots","msg":"Expected cancel size should be greater than zero"},{"code":6009,"name":"InvalidInputPegLimit","msg":"Peg limit should be greater than zero"},{"code":6010,"name":"InvalidInputOrderType","msg":"The order type is invalid. A taker order must be Market or ImmediateOrCancel"},{"code":6011,"name":"InvalidInputOrderId","msg":"Order id cannot be zero"},{"code":6012,"name":"InvalidInputHeapSlots","msg":"Slot above heap limit"},{"code":6013,"name":"InvalidOracleTypes","msg":"Cannot combine two oracles of different providers"},{"code":6014,"name":"InvalidSecondOracle","msg":"Cannot configure secondary oracle without primary"},{"code":6015,"name":"NoCloseMarketAdmin","msg":"This market does not have a `close_market_admin` and thus cannot be closed."},{"code":6016,"name":"InvalidCloseMarketAdmin","msg":"The signer of this transaction is not this market's `close_market_admin`."},{"code":6017,"name":"InvalidOpenOrdersAdmin","msg":"The `open_orders_admin` required by this market to sign all instructions that creates orders is missing or is not valid"},{"code":6018,"name":"InvalidConsumeEventsAdmin","msg":"The `consume_events_admin` required by this market to sign all instructions that consume events is missing or is not valid"},{"code":6019,"name":"InvalidMarketVault","msg":"Provided `market_vault` is invalid"},{"code":6020,"name":"IndexerActiveOO","msg":"Cannot be closed due to the existence of open orders accounts"},{"code":6021,"name":"OraclePegInvalidOracleState","msg":"Cannot place a peg order due to invalid oracle state"},{"code":6022,"name":"UnknownOracleType","msg":"oracle type cannot be determined"},{"code":6023,"name":"OracleConfidence","msg":"an oracle does not reach the confidence threshold"},{"code":6024,"name":"OracleStale","msg":"an oracle is stale"},{"code":6025,"name":"OrderIdNotFound","msg":"Order id not found on the orderbook"},{"code":6026,"name":"EventHeapContainsElements","msg":"Event heap contains elements and market can't be closed"},{"code":6027,"name":"InvalidOrderPostIOC","msg":"ImmediateOrCancel is not a PostOrderType"},{"code":6028,"name":"InvalidOrderPostMarket","msg":"Market is not a PostOrderType"},{"code":6029,"name":"WouldSelfTrade","msg":"would self trade"},{"code":6030,"name":"MarketHasExpired","msg":"The Market has already expired."},{"code":6031,"name":"InvalidPriceLots","msg":"Price lots should be greater than zero"},{"code":6032,"name":"InvalidOraclePrice","msg":"Oracle price above market limits"},{"code":6033,"name":"MarketHasNotExpired","msg":"The Market has not expired yet."},{"code":6034,"name":"NoOwnerOrDelegate","msg":"No correct owner or delegate."},{"code":6035,"name":"NoOwner","msg":"No correct owner"},{"code":6036,"name":"OpenOrdersFull","msg":"No free order index in open orders account"},{"code":6037,"name":"BookContainsElements","msg":"Book contains elements"},{"code":6038,"name":"OpenOrdersOrderNotFound","msg":"Could not find order in user account"},{"code":6039,"name":"InvalidPostAmount","msg":"Amount to post above book limits"},{"code":6040,"name":"DisabledOraclePeg","msg":"Oracle peg orders are not enabled for this market"},{"code":6041,"name":"NonEmptyMarket","msg":"Cannot close a non-empty market"},{"code":6042,"name":"NonEmptyOpenOrdersPosition","msg":"Cannot close a non-empty open orders account"}]} \ No newline at end of file diff --git a/idls/orca_wavebreak.waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF.json b/idls/orca_wavebreak.waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF.json new file mode 100644 index 0000000..2c861b2 --- /dev/null +++ b/idls/orca_wavebreak.waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF.json @@ -0,0 +1 @@ +{"version":"1.1.5","name":"orca_wavebreak_program","instructions":[{"name":"PermissionConsumeTopLevel","accounts":[{"name":"consumer","isMut":true,"isSigner":true,"docs":["The consumer of the permission"]},{"name":"permissionConfig","isMut":false,"isSigner":false,"docs":["The permission config account"]},{"name":"consumedPermission","isMut":true,"isSigner":false,"docs":["The permission program account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"instructions","isMut":false,"isSigner":false,"docs":["Sysvar instructions"]}],"args":[{"name":"permissionMessage","type":{"defined":"PermissionMessage"}},{"name":"permissionSignature","type":{"defined":"PermissionSignature"}}],"discriminant":{"type":"u8","value":0}},{"name":"PermissionConsumeCpi","accounts":[{"name":"consumer","isMut":true,"isSigner":true,"docs":["The consumer of the permission"]},{"name":"consumerProgramAuthority","isMut":false,"isSigner":true,"docs":["Any pda of the consumer program"]},{"name":"permissionConfig","isMut":false,"isSigner":false,"docs":["The permission config account"]},{"name":"consumedPermission","isMut":true,"isSigner":false,"docs":["The permission program account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]}],"args":[{"name":"permissionMessage","type":{"defined":"PermissionMessage"}},{"name":"permissionSignature","type":{"defined":"PermissionSignature"}},{"name":"consumerProgramAuthoritySeeds","type":{"vec":"bytes"}}],"discriminant":{"type":"u8","value":1}},{"name":"PermissionConfigInitialize","accounts":[{"name":"authority","isMut":true,"isSigner":true,"docs":["The authority that is allowed to initialize a permission config"]},{"name":"permissionConfig","isMut":true,"isSigner":false,"docs":["The permission config account"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]}],"args":[{"name":"permissionAuthority","type":{"defined":"PermissionSigner"}},{"name":"consumerProgram","type":"publicKey"}],"discriminant":{"type":"u8","value":2}},{"name":"PermissionConfigUpdate","accounts":[{"name":"authority","isMut":true,"isSigner":true,"docs":["The authority that is allowed to update a permission config"]},{"name":"permissionConfig","isMut":true,"isSigner":false,"docs":["The permission config account"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]}],"args":[{"name":"update","type":{"defined":"PermissionConfigUpdateType"}}],"discriminant":{"type":"u8","value":3}},{"name":"PermissionConfigClose","accounts":[{"name":"authority","isMut":true,"isSigner":true,"docs":["The authority that is allowed to close a permission config"]},{"name":"permissionConfig","isMut":true,"isSigner":false,"docs":["The permission config account"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]}],"args":[],"discriminant":{"type":"u8","value":4}},{"name":"PermissionRevoke","accounts":[{"name":"funder","isMut":true,"isSigner":true,"docs":["The funder of the consumed permission PDA"]},{"name":"permissionConfig","isMut":false,"isSigner":false,"docs":["The permission config account"]},{"name":"consumedPermission","isMut":true,"isSigner":false,"docs":["The consumed permission account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]}],"args":[{"name":"permissionMessage","type":{"defined":"PermissionMessage"}},{"name":"permissionSignature","type":{"defined":"PermissionSignature"}}],"discriminant":{"type":"u8","value":5}},{"name":"PermissionRefund","accounts":[{"name":"consumedPermission","isMut":true,"isSigner":false,"docs":["The consumed permission account"]},{"name":"refundDestination","isMut":true,"isSigner":false,"docs":["The destination to refund the rent to"]}],"args":[],"discriminant":{"type":"u8","value":6}},{"name":"ReservedPermissionA","accounts":[],"args":[],"discriminant":{"type":"u8","value":7}},{"name":"TokenBuyExactIn","accounts":[{"name":"buyer","isMut":true,"isSigner":true,"docs":["The buyer of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"baseMint","isMut":true,"isSigner":false,"docs":["The base token mint account"]},{"name":"baseAta","isMut":true,"isSigner":false,"docs":["The base token ata for the buyer"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"quoteAta","isMut":true,"isSigner":false,"docs":["The quote token ata for the buyer"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the base token"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the quote token"]}],"args":[{"name":"amountIn","type":"u64"},{"name":"allowPartialFill","type":"bool"},{"name":"priceThreshold","type":{"option":{"tuple":["u64","u64"]}}}],"discriminant":{"type":"u8","value":8}},{"name":"TokenBuyExactOut","accounts":[{"name":"buyer","isMut":true,"isSigner":true,"docs":["The buyer of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"baseMint","isMut":true,"isSigner":false,"docs":["The base token mint account"]},{"name":"baseAta","isMut":true,"isSigner":false,"docs":["The base token ata for the buyer"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"quoteAta","isMut":true,"isSigner":false,"docs":["The quote token ata for the buyer"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the base token"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the quote token"]}],"args":[{"name":"amountOut","type":"u64"},{"name":"allowPartialFill","type":"bool"},{"name":"priceThreshold","type":{"option":{"tuple":["u64","u64"]}}}],"discriminant":{"type":"u8","value":9}},{"name":"TokenSellExactIn","accounts":[{"name":"seller","isMut":true,"isSigner":true,"docs":["The seller of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"baseMint","isMut":true,"isSigner":false,"docs":["The base token mint account"]},{"name":"baseAta","isMut":true,"isSigner":false,"docs":["The base token ata for the seller"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"quoteAta","isMut":true,"isSigner":false,"docs":["The quote token ata for the seller"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the base token"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the quote token"]}],"args":[{"name":"amountIn","type":"u64"},{"name":"allowPartialFill","type":"bool"},{"name":"priceThreshold","type":{"option":{"tuple":["u64","u64"]}}}],"discriminant":{"type":"u8","value":10}},{"name":"TokenSellExactOut","accounts":[{"name":"seller","isMut":true,"isSigner":true,"docs":["The seller of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"baseMint","isMut":true,"isSigner":false,"docs":["The base token mint account"]},{"name":"baseAta","isMut":true,"isSigner":false,"docs":["The base token ata for the seller"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"quoteAta","isMut":true,"isSigner":false,"docs":["The quote token ata for the seller"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the base token"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the quote token"]}],"args":[{"name":"amountOut","type":"u64"},{"name":"allowPartialFill","type":"bool"},{"name":"priceThreshold","type":{"option":{"tuple":["u64","u64"]}}}],"discriminant":{"type":"u8","value":11}},{"name":"TokenRefund","accounts":[{"name":"signer","isMut":true,"isSigner":true,"docs":["The signer of the transaction"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"signerQuoteAta","isMut":true,"isSigner":false,"docs":["The signer's quote token ata account"]},{"name":"baseMint","isMut":true,"isSigner":false,"docs":["The base token mint account"]},{"name":"signerBaseAta","isMut":true,"isSigner":false,"docs":["The signer's base token ata account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The base token program"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The quote token program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]}],"args":[],"discriminant":{"type":"u8","value":12}},{"name":"ReservedTokenY","accounts":[],"args":[],"discriminant":{"type":"u8","value":13}},{"name":"ReservedTokenZ","accounts":[],"args":[],"discriminant":{"type":"u8","value":14}},{"name":"ReservedTokenA","accounts":[],"args":[],"discriminant":{"type":"u8","value":15}},{"name":"AuthorityConfigInitialize","accounts":[{"name":"authority","isMut":true,"isSigner":true,"docs":["The authority of the config"]},{"name":"authorityConfig","isMut":true,"isSigner":false,"docs":["The authority config account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]}],"args":[],"discriminant":{"type":"u8","value":16}},{"name":"AuthorityConfigGrant","accounts":[{"name":"authority","isMut":false,"isSigner":true,"docs":["The authority that is allowed to update the config"]},{"name":"authorityConfig","isMut":true,"isSigner":false,"docs":["The authority config account"]}],"args":[{"name":"account","type":"publicKey"},{"name":"privileges","type":{"vec":{"defined":"Privilege"}}}],"discriminant":{"type":"u8","value":17}},{"name":"AuthorityConfigRevoke","accounts":[{"name":"authority","isMut":false,"isSigner":true,"docs":["The authority that is allowed to update the config"]},{"name":"authorityConfig","isMut":true,"isSigner":false,"docs":["The authority config account"]}],"args":[{"name":"account","type":"publicKey"},{"name":"privileges","type":{"vec":{"defined":"Privilege"}}}],"discriminant":{"type":"u8","value":18}},{"name":"ReservedAuthorityConfigY","accounts":[],"args":[],"discriminant":{"type":"u8","value":19}},{"name":"ReservedAuthorityConfigZ","accounts":[],"args":[],"discriminant":{"type":"u8","value":20}},{"name":"ReservedAuthorityConfigA","accounts":[],"args":[],"discriminant":{"type":"u8","value":21}},{"name":"ReservedAuthorityConfigB","accounts":[],"args":[],"discriminant":{"type":"u8","value":22}},{"name":"ReservedAuthorityConfigC","accounts":[],"args":[],"discriminant":{"type":"u8","value":23}},{"name":"MintConfigInitialize","accounts":[{"name":"authority","isMut":true,"isSigner":true,"docs":["The authority that is allowed to initialize a mint config"]},{"name":"mintConfig","isMut":true,"isSigner":false,"docs":["The mint config account"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]}],"args":[{"name":"tokenMint","type":"publicKey"},{"name":"instructionDiscriminator","type":"u8"}],"discriminant":{"type":"u8","value":24}},{"name":"MintConfigClose","accounts":[{"name":"authority","isMut":true,"isSigner":true,"docs":["The authority that is allowed to close a mint config"]},{"name":"mintConfig","isMut":true,"isSigner":false,"docs":["The mint config account"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]}],"args":[],"discriminant":{"type":"u8","value":25}},{"name":"MintConfigUpdate","accounts":[{"name":"authority","isMut":false,"isSigner":true,"docs":["The authority that is allowed to update a mint config"]},{"name":"mintConfig","isMut":true,"isSigner":false,"docs":["The mint config account"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]}],"args":[{"name":"update","type":{"defined":"MintConfigUpdateType"}}],"discriminant":{"type":"u8","value":26}},{"name":"ReservedMintConfigY","accounts":[],"args":[],"discriminant":{"type":"u8","value":27}},{"name":"ReservedMintConfigZ","accounts":[],"args":[],"discriminant":{"type":"u8","value":28}},{"name":"ReservedMintConfigA","accounts":[],"args":[],"discriminant":{"type":"u8","value":29}},{"name":"ReservedMintConfigB","accounts":[],"args":[],"discriminant":{"type":"u8","value":30}},{"name":"ReservedMintConfigC","accounts":[],"args":[],"discriminant":{"type":"u8","value":31}},{"name":"GraduateWhirlpool","accounts":[{"name":"signer","isMut":true,"isSigner":true,"docs":["The signer of the transaction"]},{"name":"lpAuthority","isMut":false,"isSigner":false,"docs":["The lp authority of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"signerQuoteAta","isMut":true,"isSigner":false,"docs":["The quote token ata account"]},{"name":"lpAuthorityQuoteAta","isMut":true,"isSigner":false,"docs":["The lp authority quote token ata account"]},{"name":"whirlpoolQuoteVault","isMut":true,"isSigner":false,"docs":["The whirlpool quote vault account"]},{"name":"baseMint","isMut":true,"isSigner":false,"docs":["The base token mint account"]},{"name":"baseVault","isMut":true,"isSigner":false,"docs":["The config base vault account"]},{"name":"lpAuthorityBaseAta","isMut":true,"isSigner":false,"docs":["The lp authority base token ata account"]},{"name":"whirlpoolBaseVault","isMut":true,"isSigner":false,"docs":["The whirlpool base vault account"]},{"name":"whirlpoolConfig","isMut":false,"isSigner":false,"docs":["The whirlpool config account"]},{"name":"feeTier","isMut":false,"isSigner":false,"docs":["The fee tier account"]},{"name":"whirlpool","isMut":true,"isSigner":false,"docs":["The whirlpool account"]},{"name":"oracle","isMut":true,"isSigner":false,"docs":["The whirlpool oracle account"]},{"name":"position","isMut":true,"isSigner":false,"docs":["The position account"]},{"name":"positionMint","isMut":true,"isSigner":false,"docs":["The position mint account"]},{"name":"positionTokenAccount","isMut":true,"isSigner":false,"docs":["The position token account for the bonding curve"]},{"name":"lpAuthorityTokenAccount","isMut":true,"isSigner":false,"docs":["The position token account for the lp authority"]},{"name":"lowerTickArray","isMut":true,"isSigner":false,"docs":["The lower tick array account"]},{"name":"upperTickArray","isMut":true,"isSigner":false,"docs":["The upper tick array account"]},{"name":"quoteTokenBadge","isMut":false,"isSigner":false,"docs":["The quote token badge account"]},{"name":"baseTokenBadge","isMut":false,"isSigner":false,"docs":["The base token badge account"]},{"name":"whirlpoolInitAuthority","isMut":false,"isSigner":false,"docs":["The whirlpool init authority account"]},{"name":"whirlpoolUpdateAuthority","isMut":false,"isSigner":false,"docs":["The whirlpool update authority account"]},{"name":"lockConfig","isMut":true,"isSigner":false,"docs":["The lock config account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["The system program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The ata program"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The quote token program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The base token program"]},{"name":"token22Program","isMut":false,"isSigner":false,"docs":["The token 22 program"]},{"name":"memoProgram","isMut":false,"isSigner":false,"docs":["The memo program"]},{"name":"whirlpoolProgram","isMut":false,"isSigner":false,"docs":["The whirlpool program"]},{"name":"rent","isMut":false,"isSigner":false,"docs":["The rent sysvar"]}],"args":[],"discriminant":{"type":"u8","value":32}},{"name":"GraduateManual","accounts":[{"name":"signer","isMut":true,"isSigner":true,"docs":["The signer of the transaction"]},{"name":"destination","isMut":false,"isSigner":false,"docs":["The destination of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote vault account"]},{"name":"signerQuoteAta","isMut":true,"isSigner":false,"docs":["The quote token ata account"]},{"name":"destinationQuoteAta","isMut":true,"isSigner":false,"docs":["The destination quote ata account"]},{"name":"baseMint","isMut":true,"isSigner":false,"docs":["The base mint account"]},{"name":"destinationBaseAta","isMut":true,"isSigner":false,"docs":["The destination base ata account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["The system program account"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The ata program account"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The quote token program account"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The base token program account"]}],"args":[],"discriminant":{"type":"u8","value":33}},{"name":"ReservedGraduateX","accounts":[],"args":[],"discriminant":{"type":"u8","value":34}},{"name":"ReservedGraduateY","accounts":[],"args":[],"discriminant":{"type":"u8","value":35}},{"name":"ReservedGraduateZ","accounts":[],"args":[],"discriminant":{"type":"u8","value":36}},{"name":"ReservedGraduateA","accounts":[],"args":[],"discriminant":{"type":"u8","value":37}},{"name":"ReservedGraduateB","accounts":[],"args":[],"discriminant":{"type":"u8","value":38}},{"name":"ReservedGraduateC","accounts":[],"args":[],"discriminant":{"type":"u8","value":39}},{"name":"CreateLockedlaunch","accounts":[{"name":"creator","isMut":true,"isSigner":true,"docs":["The creator of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"baseMint","isMut":true,"isSigner":true,"docs":["The token mint account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"mintConfig","isMut":false,"isSigner":false,"docs":["The base token mint config account"]},{"name":"metadata","isMut":true,"isSigner":false,"docs":["The metadata account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the new token"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the quote token"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]},{"name":"metaplexProgram","isMut":false,"isSigner":false,"docs":["The metaplex metadata program"]}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}],"discriminant":{"type":"u8","value":40}},{"name":"CreateLaunch","accounts":[{"name":"creator","isMut":true,"isSigner":true,"docs":["The creator of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"baseMint","isMut":true,"isSigner":true,"docs":["The token mint account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"mintConfig","isMut":false,"isSigner":false,"docs":["The base token mint config account"]},{"name":"metadata","isMut":true,"isSigner":false,"docs":["The metadata account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the new token"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the quote token"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]},{"name":"metaplexProgram","isMut":false,"isSigner":false,"docs":["The metaplex metadata program"]}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"startPrice","type":{"option":"u128"}},{"name":"endPrice","type":{"option":"u128"}},{"name":"controlPoints","type":{"option":{"array":["u16",4]}}},{"name":"graduationTarget","type":{"option":"u64"}},{"name":"graduationMethods","type":{"option":{"array":[{"defined":"GraduationMethod"},8]}}},{"name":"launchTime","type":{"option":"i64"}},{"name":"graduationTime","type":{"option":"i64"}},{"name":"baseAllocationBps","type":{"option":"u16"}}],"discriminant":{"type":"u8","value":41}},{"name":"CreatePresale","accounts":[{"name":"creator","isMut":true,"isSigner":true,"docs":["The creator of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"baseMint","isMut":true,"isSigner":true,"docs":["The token mint account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"mintConfig","isMut":false,"isSigner":false,"docs":["The base token mint config account"]},{"name":"metadata","isMut":true,"isSigner":false,"docs":["The metadata account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the new token"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the quote token"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]},{"name":"metaplexProgram","isMut":false,"isSigner":false,"docs":["The metaplex metadata program"]}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"tokenPrice","type":{"option":"u128"}},{"name":"graduationTarget","type":{"option":"u64"}},{"name":"graduationMethods","type":{"option":{"array":[{"defined":"GraduationMethod"},8]}}},{"name":"launchTime","type":{"option":"i64"}},{"name":"graduationTime","type":{"option":"i64"}},{"name":"minReserveBps","type":{"option":"u16"}},{"name":"baseAllocationBps","type":{"option":"u16"}}],"discriminant":{"type":"u8","value":42}},{"name":"ReservedCreateY","accounts":[],"args":[],"discriminant":{"type":"u8","value":43}},{"name":"ReservedCreateZ","accounts":[],"args":[],"discriminant":{"type":"u8","value":44}},{"name":"ReservedCreateA","accounts":[],"args":[],"discriminant":{"type":"u8","value":45}},{"name":"ReservedCreateB","accounts":[],"args":[],"discriminant":{"type":"u8","value":46}},{"name":"ReservedCreateC","accounts":[],"args":[],"discriminant":{"type":"u8","value":47}},{"name":"BondingCurveInitialize","accounts":[{"name":"authority","isMut":true,"isSigner":true,"docs":["The authority that is allowed to initialize a bonding curve"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"baseMint","isMut":false,"isSigner":false,"docs":["The token mint account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the new token"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the quote token"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]}],"args":[{"name":"startPrice","type":"u128"},{"name":"endPrice","type":"u128"},{"name":"controlPoints","type":{"array":["u16",4]}},{"name":"creator","type":"publicKey"},{"name":"graduationMethods","type":{"array":[{"defined":"GraduationMethod"},8]}},{"name":"swapFeeBps","type":"u16"},{"name":"quoteFeeBps","type":"u16"},{"name":"baseFeeBps","type":"u16"},{"name":"launchTime","type":"i64"},{"name":"creatorReward","type":"u64"},{"name":"graduationReward","type":"u64"},{"name":"graduationTarget","type":"u64"},{"name":"graduationTime","type":"i64"},{"name":"minReserveBps","type":"u16"},{"name":"buyRequiresPermission","type":"bool"},{"name":"buyPermissionBitmap","type":{"array":["u8",32]}},{"name":"sellRequiresPermission","type":"bool"},{"name":"sellPermissionBitmap","type":{"array":["u8",32]}},{"name":"maxBuyAmount","type":"u64"},{"name":"maxSellAmount","type":"u64"},{"name":"retainMintAuthority","type":"bool"},{"name":"baseAllocationBps","type":"u16"}],"discriminant":{"type":"u8","value":48}},{"name":"BondingCurveCollectFees","accounts":[{"name":"signer","isMut":true,"isSigner":true,"docs":["The signer of the transaction"]},{"name":"feeAuthority","isMut":false,"isSigner":false,"docs":["The fee authority of the program"]},{"name":"feeAuthorityAta","isMut":true,"isSigner":false,"docs":["The fee authority associated token account"]},{"name":"bondingCurve","isMut":false,"isSigner":false,"docs":["The bonding curve account"]},{"name":"baseMint","isMut":false,"isSigner":false,"docs":["The base token mint account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The quote token program"]}],"args":[],"discriminant":{"type":"u8","value":49}},{"name":"BondingCurveGraduate","accounts":[{"name":"signer","isMut":true,"isSigner":true,"docs":["The signer of the transaction"]},{"name":"creator","isMut":true,"isSigner":false,"docs":["The creator of the token"]},{"name":"feeAuthority","isMut":true,"isSigner":false,"docs":["The fee authority of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"signerQuoteAta","isMut":true,"isSigner":false,"docs":["The signer's quote token ata account"]},{"name":"creatorQuoteAta","isMut":true,"isSigner":false,"docs":["The creator's quote token ata account"]},{"name":"feeAuthorityQuoteAta","isMut":true,"isSigner":false,"docs":["The fee authority's quote token ata account"]},{"name":"baseMint","isMut":true,"isSigner":false,"docs":["The base token mint account"]},{"name":"creatorBaseAta","isMut":true,"isSigner":false,"docs":["The creator's base token ata account"]},{"name":"feeAuthorityBaseAta","isMut":true,"isSigner":false,"docs":["The fee authority's base token ata account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The quote token program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The base token program"]}],"args":[],"discriminant":{"type":"u8","value":50}},{"name":"BondingCurveClose","accounts":[{"name":"authority","isMut":false,"isSigner":true,"docs":["The authority that is allowed to close a bonding curve"]},{"name":"creator","isMut":true,"isSigner":false,"docs":["The creator of the bonding curve"]},{"name":"feeAuthority","isMut":false,"isSigner":false,"docs":["The fee authority of the bonding curve"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"feeAuthorityQuoteAta","isMut":true,"isSigner":false,"docs":["The fee authority's quote token ata account"]},{"name":"baseMint","isMut":false,"isSigner":false,"docs":["The base token mint account"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The quote token program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The base token program"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["The system program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]}],"args":[],"discriminant":{"type":"u8","value":51}},{"name":"ReservedBondingCurveX","accounts":[],"args":[],"discriminant":{"type":"u8","value":52}},{"name":"ReservedBondingCurveY","accounts":[],"args":[],"discriminant":{"type":"u8","value":53}},{"name":"ReservedBondingCurveZ","accounts":[],"args":[],"discriminant":{"type":"u8","value":54}},{"name":"ReservedBondingCurveA","accounts":[],"args":[],"discriminant":{"type":"u8","value":55}},{"name":"LpHarvest","accounts":[{"name":"lpAuthority","isMut":true,"isSigner":true,"docs":["The authority of the lp escrow account"]},{"name":"lpEscrow","isMut":false,"isSigner":false,"docs":["The LP escrow account"]},{"name":"whirlpool","isMut":true,"isSigner":false,"docs":["The whirlpool account"]},{"name":"position","isMut":true,"isSigner":false,"docs":["The position account"]},{"name":"positionTokenAccount","isMut":false,"isSigner":false,"docs":["The position token account"]},{"name":"lowerTickArray","isMut":false,"isSigner":false,"docs":["The lower tick array account"]},{"name":"upperTickArray","isMut":false,"isSigner":false,"docs":["The upper tick array account"]},{"name":"tokenAMint","isMut":true,"isSigner":false,"docs":["The token mint a account"]},{"name":"tokenBMint","isMut":true,"isSigner":false,"docs":["The token mint b account"]},{"name":"tokenAAta","isMut":true,"isSigner":false,"docs":["The token owner a account"]},{"name":"tokenBAta","isMut":true,"isSigner":false,"docs":["The token owner b account"]},{"name":"tokenAVault","isMut":true,"isSigner":false,"docs":["The token vault a account"]},{"name":"tokenBVault","isMut":true,"isSigner":false,"docs":["The token vault b account"]},{"name":"reward1Mint","isMut":false,"isSigner":false,"docs":["The reward mint 1 account"]},{"name":"reward2Mint","isMut":false,"isSigner":false,"docs":["The reward mint 2 account"]},{"name":"reward3Mint","isMut":false,"isSigner":false,"docs":["The reward mint 3 account"]},{"name":"reward1Ata","isMut":true,"isSigner":false,"docs":["The reward owner 1 account"]},{"name":"reward2Ata","isMut":true,"isSigner":false,"docs":["The reward owner 2 account"]},{"name":"reward3Ata","isMut":true,"isSigner":false,"docs":["The reward owner 3 account"]},{"name":"reward1Vault","isMut":true,"isSigner":false,"docs":["The reward vault 1 account"]},{"name":"reward2Vault","isMut":true,"isSigner":false,"docs":["The reward vault 2 account"]},{"name":"reward3Vault","isMut":true,"isSigner":false,"docs":["The reward vault 3 account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["The system program"]},{"name":"tokenAProgram","isMut":false,"isSigner":false,"docs":["The token a program"]},{"name":"tokenBProgram","isMut":false,"isSigner":false,"docs":["The token b program"]},{"name":"reward1TokenProgram","isMut":false,"isSigner":false,"docs":["The token reward 1 program"]},{"name":"reward2TokenProgram","isMut":false,"isSigner":false,"docs":["The token reward 2 program"]},{"name":"reward3TokenProgram","isMut":false,"isSigner":false,"docs":["The token reward 3 program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The ata program"]},{"name":"memoProgram","isMut":false,"isSigner":false,"docs":["The memo program"]},{"name":"whirlpoolProgram","isMut":false,"isSigner":false,"docs":["The whirlpool program"]}],"args":[],"discriminant":{"type":"u8","value":56}},{"name":"LpTransfer","accounts":[{"name":"lpAuthority","isMut":true,"isSigner":true,"docs":["The authority of the lp escrow account"]},{"name":"newLpAuthority","isMut":false,"isSigner":false,"docs":["The new authority of the lp escrow account"]},{"name":"lpEscrow","isMut":false,"isSigner":false,"docs":["The lp escrow account"]},{"name":"newLpEscrow","isMut":false,"isSigner":false,"docs":["The new lp escrow account"]},{"name":"positionMint","isMut":false,"isSigner":false,"docs":["The position mint account"]},{"name":"position","isMut":false,"isSigner":false,"docs":["The position account"]},{"name":"positionTokenAccount","isMut":true,"isSigner":false,"docs":["The position token account"]},{"name":"newPositionTokenAccount","isMut":true,"isSigner":false,"docs":["The new position token account"]},{"name":"lockConfig","isMut":true,"isSigner":false,"docs":["The lock config account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["The system program"]},{"name":"token22Program","isMut":false,"isSigner":false,"docs":["The token 22 program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The ata program"]},{"name":"whirlpoolProgram","isMut":false,"isSigner":false,"docs":["The whirlpool program"]}],"args":[],"discriminant":{"type":"u8","value":57}},{"name":"LpTakeover","accounts":[{"name":"authority","isMut":true,"isSigner":true,"docs":["The authority account"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]},{"name":"lpAuthority","isMut":false,"isSigner":false,"docs":["The authority of the lp escrow account"]},{"name":"newLpAuthority","isMut":false,"isSigner":false,"docs":["The new authority of the lp escrow account"]},{"name":"lpEscrow","isMut":false,"isSigner":false,"docs":["The lp escrow account"]},{"name":"newLpEscrow","isMut":false,"isSigner":false,"docs":["The new lp escrow account"]},{"name":"positionMint","isMut":false,"isSigner":false,"docs":["The position mint account"]},{"name":"position","isMut":false,"isSigner":false,"docs":["The position account"]},{"name":"positionTokenAccount","isMut":true,"isSigner":false,"docs":["The position token account"]},{"name":"newPositionTokenAccount","isMut":true,"isSigner":false,"docs":["The new position token account"]},{"name":"lockConfig","isMut":true,"isSigner":false,"docs":["The lock config account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["The system program"]},{"name":"token22Program","isMut":false,"isSigner":false,"docs":["The token 22 program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The ata program"]},{"name":"whirlpoolProgram","isMut":false,"isSigner":false,"docs":["The whirlpool program"]}],"args":[],"discriminant":{"type":"u8","value":58}},{"name":"ReservedLpX","accounts":[],"args":[],"discriminant":{"type":"u8","value":59}},{"name":"ReservedLpY","accounts":[],"args":[],"discriminant":{"type":"u8","value":60}},{"name":"ReservedLpZ","accounts":[],"args":[],"discriminant":{"type":"u8","value":61}},{"name":"ReservedLpA","accounts":[],"args":[],"discriminant":{"type":"u8","value":62}},{"name":"ReservedLpB","accounts":[],"args":[],"discriminant":{"type":"u8","value":63}}],"accounts":[{"name":"AuthorityConfig","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"defined":"AccountDiscriminator"}},{"name":"authorities","type":{"array":[{"defined":"ProgramAuthority"},64]}}]}},{"name":"BondingCurve","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"defined":"AccountDiscriminator"}},{"name":"baseMint","type":"publicKey"},{"name":"quoteMint","type":"publicKey"},{"name":"creator","type":"publicKey"},{"name":"retainMintAuthority","type":"bool"},{"name":"buyRequiresPermission","type":"bool"},{"name":"buyPermissionBitmap","type":{"array":["u8",32]}},{"name":"sellRequiresPermission","type":"bool"},{"name":"sellPermissionBitmap","type":{"array":["u8",32]}},{"name":"quoteFeeBps","type":"u16"},{"name":"baseFeeBps","type":"u16"},{"name":"controlPoints","type":{"array":["u16",4]}},{"name":"startPrice","type":"u128"},{"name":"endPrice","type":"u128"},{"name":"quoteAmount","type":"u64"},{"name":"baseAmount","type":"u64"},{"name":"launchTime","type":"i64"},{"name":"creatorReward","type":"u64"},{"name":"graduationTarget","type":"u64"},{"name":"graduationTime","type":"i64"},{"name":"graduationReward","type":"u64"},{"name":"maxBuyAmount","type":"u64"},{"name":"maxSellAmount","type":"u64"},{"name":"swapFeeBps","type":"u16"},{"name":"baseAllocationBps","type":"u16"},{"name":"graduationMethods","type":{"array":[{"defined":"GraduationMethodData"},8]}},{"name":"minReserveBps","type":"u16"},{"name":"padding1","type":{"array":["u8",2]}},{"name":"premintedSupply","type":"u64"},{"name":"padding2","type":{"array":["u8",728]}}]}},{"name":"MintConfig","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"defined":"AccountDiscriminator"}},{"name":"instructionDiscriminator","type":"u8"},{"name":"quoteMint","type":"publicKey"},{"name":"createRequiresPermission","type":"bool"},{"name":"createPermissionBitmap","type":{"array":["u8",32]}},{"name":"defaultBuyRequiresPermission","type":"bool"},{"name":"defaultBuyPermissionBitmap","type":{"array":["u8",32]}},{"name":"defaultSellRequiresPermission","type":"bool"},{"name":"defaultSellPermissionBitmap","type":{"array":["u8",32]}},{"name":"padding1","type":{"array":["u8",3]}},{"name":"defaultCreatorReward","type":"u64"},{"name":"defaultGraduationReward","type":"u64"},{"name":"defaultGraduationTarget","type":"u64"},{"name":"defaultMaxBuyAmount","type":"u64"},{"name":"defaultMaxSellAmount","type":"u64"},{"name":"defaultStartPrice","type":"u128"},{"name":"defaultEndPrice","type":"u128"},{"name":"defaultControlPoints","type":{"array":["u16",4]}},{"name":"defaultSwapFeeBps","type":"u16"},{"name":"defaultQuoteFeeBps","type":"u16"},{"name":"defaultBaseFeeBps","type":"u16"},{"name":"padding2","type":{"array":["u8",1826]}}]}},{"name":"PermissionConfig","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"defined":"AccountDiscriminator"}},{"name":"consumerProgram","type":"publicKey"},{"name":"allowedSigners","type":{"array":[{"defined":"PermissionSigner"},3]}},{"name":"padding","type":{"array":["u8",124]}}]}},{"name":"ConsumedPermission","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"defined":"AccountDiscriminator"}},{"name":"padding1","type":{"array":["u8",7]}},{"name":"safeToCloseSlot","type":"u64"},{"name":"refundDestination","type":"publicKey"},{"name":"padding2","type":{"array":["u8",16]}}]}}],"types":[{"name":"MetaplexMetadata","type":{"kind":"struct","fields":[{"name":"discriminator","type":"u8"},{"name":"updateAuthority","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"data","type":{"defined":"MetaplexData"}},{"name":"primarySaleHappened","type":"bool"},{"name":"mutable","type":"bool"},{"name":"editionNonce","type":{"option":"u8"}},{"name":"tokenStandard","type":{"option":{"defined":"MetaplexTokenStandard"}}},{"name":"collection","type":{"option":{"defined":"MetaplexCollection"}}},{"name":"uses","type":{"option":{"defined":"MetaplexUses"}}},{"name":"collectionDetails","type":{"option":{"defined":"MetaplexCollectionDetails"}}},{"name":"programmableConfig","type":{"option":{"defined":"MetaplexProgrammableConfig"}}}]}},{"name":"MetaplexData","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"sellerFeeBasisPoints","type":"u16"},{"name":"creators","type":{"option":{"vec":{"defined":"MetaplexCreator"}}}}]}},{"name":"MetaplexCreator","type":{"kind":"struct","fields":[{"name":"address","type":"publicKey"},{"name":"verified","type":"bool"},{"name":"share","type":"u8"}]}},{"name":"MetaplexCollection","type":{"kind":"struct","fields":[{"name":"verified","type":"bool"},{"name":"key","type":"publicKey"}]}},{"name":"MetaplexUses","type":{"kind":"struct","fields":[{"name":"useMethod","type":{"defined":"MetaplexUseMethod"}},{"name":"remaining","type":"u64"},{"name":"total","type":"u64"}]}},{"name":"TokenMint","type":{"kind":"struct","fields":[{"name":"mintAuthorityFlag","type":"u32"},{"name":"mintAuthority","type":"publicKey"},{"name":"supply","type":"u64"},{"name":"decimals","type":"u8"},{"name":"isInitialized","type":"bool"},{"name":"freezeAuthorityFlag","type":"u32"},{"name":"freezeAuthority","type":"publicKey"}]}},{"name":"TokenAccount","type":{"kind":"struct","fields":[{"name":"mint","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"amount","type":"u64"},{"name":"delegateFlag","type":"u32"},{"name":"delegate","type":"publicKey"},{"name":"state","type":{"defined":"TokenAccountState"}},{"name":"isNativeFlag","type":"u32"},{"name":"nativeAmount","type":"u64"},{"name":"delegateAmount","type":"u64"},{"name":"closeAuthorityFlag","type":"u32"},{"name":"closeAuthority","type":"publicKey"}]}},{"name":"TokenMetadata","type":{"kind":"struct","fields":[{"name":"updateAuthority","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"additionalMetadata","type":{"vec":{"tuple":["string","string"]}}}]}},{"name":"TokenMetadataPointer","type":{"kind":"struct","fields":[{"name":"updateAuthority","type":"publicKey"},{"name":"metadataAddress","type":"publicKey"}]}},{"name":"WhirlpoolFeeTier","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"array":["u8",8]}},{"name":"whirlpoolConfig","type":"publicKey"},{"name":"tickSpacing","type":"u16"},{"name":"defaultFeeRate","type":"u16"}]}},{"name":"WhirlpoolAdaptiveFeeTier","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"array":["u8",8]}},{"name":"whirlpoolConfig","type":"publicKey"},{"name":"feeTierIndex","type":"u16"},{"name":"tickSpacing","type":"u16"},{"name":"initializePoolAuthority","type":"publicKey"},{"name":"delegatedFeeAuthority","type":"publicKey"},{"name":"defaultBaseFeeRate","type":"u16"},{"name":"filterPeriod","type":"u16"},{"name":"decayPeriod","type":"u16"},{"name":"reductionFactor","type":"u16"},{"name":"adaptiveFeeControlFactor","type":"u32"},{"name":"maxVolatilityAccumulator","type":"u32"},{"name":"tickGroupSize","type":"u16"},{"name":"majorSwapThresholdTicks","type":"u16"}]}},{"name":"WhirlpoolConfig","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"array":["u8",8]}},{"name":"feeAuthority","type":"publicKey"},{"name":"collectProtocolFeesAuthority","type":"publicKey"},{"name":"rewardEmissionsSuperAuthority","type":"publicKey"},{"name":"defaultProtocolFeeRate","type":"u16"},{"name":"featureFlags","type":"u16"}]}},{"name":"Whirlpool","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"array":["u8",8]}},{"name":"whirlpoolConfig","type":"publicKey"},{"name":"whirlpoolBump","type":"u8"},{"name":"tickSpacing","type":"u16"},{"name":"feeTierIndex","type":"u16"},{"name":"feeRate","type":"u16"},{"name":"protocolFeeRate","type":"u16"},{"name":"liquidity","type":"u128"},{"name":"sqrtPrice","type":"u128"},{"name":"tickCurrentIndex","type":"i32"},{"name":"protocolFeeOwedA","type":"u64"},{"name":"protocolFeeOwedB","type":"u64"},{"name":"tokenAMint","type":"publicKey"},{"name":"tokenAVault","type":"publicKey"},{"name":"feeGrowthGlobalA","type":"u128"},{"name":"tokenBMint","type":"publicKey"},{"name":"tokenBVault","type":"publicKey"},{"name":"feeGrowthGlobalB","type":"u128"},{"name":"rewardLastUpdatedTimestamp","type":"u64"},{"name":"rewardInfos","type":{"array":[{"defined":"WhirlpoolRewardInfo"},3]}}]}},{"name":"WhirlpoolRewardInfo","type":{"kind":"struct","fields":[{"name":"mint","type":"publicKey"},{"name":"vault","type":"publicKey"},{"name":"authority","type":"publicKey"},{"name":"emissionsPerSecondX64","type":"u128"},{"name":"growthGlobalX64","type":"u128"}]}},{"name":"WhirlpoolLockConfig","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"array":["u8",8]}},{"name":"position","type":"publicKey"},{"name":"positionOwner","type":"publicKey"},{"name":"whirlpool","type":"publicKey"},{"name":"lockedTimestamp","type":"u64"},{"name":"lockType","type":{"defined":"LockTypeLabel"}}]}},{"name":"WhirlpoolPosition","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"array":["u8",8]}},{"name":"whirlpool","type":"publicKey"},{"name":"positionMint","type":"publicKey"},{"name":"liquidity","type":"u128"},{"name":"tickLowerIndex","type":"i32"},{"name":"tickUpperIndex","type":"i32"},{"name":"feeGrowthCheckpointA","type":"u128"},{"name":"feeOwedA","type":"u64"},{"name":"feeGrowthCheckpointB","type":"u128"},{"name":"feeOwedB","type":"u64"},{"name":"rewardInfos","type":{"array":[{"defined":"WhirlpoolPositionRewardInfo"},3]}}]}},{"name":"WhirlpoolPositionRewardInfo","type":{"kind":"struct","fields":[{"name":"growthInsideCheckpoint","type":"u128"},{"name":"amountOwed","type":"u64"}]}},{"name":"WhirlpoolTickArray","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"array":["u8",8]}},{"name":"startTickIndex","type":"i32"},{"name":"ticks","type":{"array":["u8",9944]}},{"name":"whirlpool","type":"publicKey"}]}},{"name":"ProgramAuthority","type":{"kind":"struct","fields":[{"name":"key","type":"publicKey"},{"name":"privileges","type":{"array":["u8",32]}}]}},{"name":"GraduationMethodData","type":{"kind":"struct","fields":[{"name":"label","type":{"defined":"GraduationMethodLabel"}},{"name":"graduated","type":"bool"},{"name":"feeTierIndex","type":"u16"},{"name":"splitBps","type":"u16"},{"name":"destination","type":"publicKey"},{"name":"unlocked","type":"bool"},{"name":"padding","type":{"array":["u8",89]}}]}},{"name":"PermissionSigner","type":{"kind":"struct","fields":[{"name":"bytes","type":{"array":["u8",33]}}]}},{"name":"PermissionSignature","type":{"kind":"struct","fields":[{"name":"recoveryId","type":"u8"},{"name":"bytes","type":{"array":["u8",64]}}]}},{"name":"MetaplexTokenStandard","type":{"kind":"enum","variants":[{"name":"NonFungible"},{"name":"FungibleAsset"},{"name":"Fungible"},{"name":"NonFungibleEdition"},{"name":"ProgrammableNonFungible"},{"name":"ProgrammableNonFungibleEdition"}]}},{"name":"MetaplexUseMethod","type":{"kind":"enum","variants":[{"name":"Burn"},{"name":"Multiple"},{"name":"Single"}]}},{"name":"MetaplexCollectionDetails","type":{"kind":"enum","variants":[{"name":"V1","fields":[{"name":"size","type":"u64"}]},{"name":"V2","fields":[{"name":"padding","type":{"array":["u8",8]}}]}]}},{"name":"MetaplexProgrammableConfig","type":{"kind":"enum","variants":[{"name":"V1","fields":[{"name":"rule_set","type":{"option":"publicKey"}}]}]}},{"name":"TokenAccountState","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Initialized"},{"name":"Frozen"}]}},{"name":"LockTypeLabel","type":{"kind":"enum","variants":[{"name":"Permanent"}]}},{"name":"BondingCurveCreationType","type":{"kind":"enum","variants":[{"name":"Manual"},{"name":"Launch"},{"name":"Lockedlaunch"},{"name":"Presale"}]}},{"name":"Event","type":{"kind":"enum","variants":[{"name":"BondingCurveCreated","fields":[{"name":"creation_type","type":{"defined":"BondingCurveCreationType"}},{"name":"base_mint","type":"publicKey"},{"name":"quote_mint","type":"publicKey"},{"name":"creator","type":"publicKey"},{"name":"start_price","type":"u128"},{"name":"end_price","type":"u128"},{"name":"control_points","type":{"array":["u16",4]}},{"name":"swap_fee_bps","type":"u16"},{"name":"quote_fee_bps","type":"u16"},{"name":"base_fee_bps","type":"u16"},{"name":"base_allocation_bps","type":"u16"},{"name":"launch_time","type":"i64"},{"name":"creator_reward","type":"u64"},{"name":"graduation_methods","type":{"array":[{"defined":"GraduationMethodData"},8]}},{"name":"graduation_target","type":"u64"},{"name":"graduation_time","type":"i64"},{"name":"graduation_reward","type":"u64"},{"name":"retain_mint_authority","type":"bool"},{"name":"min_reserve_bps","type":"u16"},{"name":"buy_requires_permission","type":"bool"},{"name":"buy_permission_bitmap","type":{"array":["u8",32]}},{"name":"max_buy_amount","type":"u64"},{"name":"sell_requires_permission","type":"bool"},{"name":"sell_permission_bitmap","type":{"array":["u8",32]}},{"name":"max_sell_amount","type":"u64"},{"name":"quote_token_program","type":"publicKey"},{"name":"base_token_program","type":"publicKey"},{"name":"quote_token_decimals","type":"u8"},{"name":"base_token_decimals","type":"u8"}]},{"name":"TokenBought","fields":[{"name":"buyer","type":"publicKey"},{"name":"base_mint","type":"publicKey"},{"name":"quote_mint","type":"publicKey"},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"},{"name":"price_before","type":"u128"},{"name":"price_after","type":"u128"},{"name":"quote_amount_before","type":"u64"},{"name":"quote_amount_after","type":"u64"},{"name":"base_amount_before","type":"u64"},{"name":"base_amount_after","type":"u64"},{"name":"protocol_fee","type":"u64"}]},{"name":"TokenSold","fields":[{"name":"seller","type":"publicKey"},{"name":"base_mint","type":"publicKey"},{"name":"quote_mint","type":"publicKey"},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"},{"name":"price_before","type":"u128"},{"name":"price_after","type":"u128"},{"name":"quote_amount_before","type":"u64"},{"name":"quote_amount_after","type":"u64"},{"name":"base_amount_before","type":"u64"},{"name":"base_amount_after","type":"u64"},{"name":"protocol_fee","type":"u64"}]},{"name":"TokenRefunded","fields":[{"name":"signer","type":"publicKey"},{"name":"base_mint","type":"publicKey"},{"name":"quote_mint","type":"publicKey"},{"name":"price","type":"u128"},{"name":"quote_amount","type":"u64"},{"name":"base_amount","type":"u64"}]},{"name":"BondingCurveGraduated","fields":[{"name":"graduator","type":"publicKey"},{"name":"base_mint","type":"publicKey"},{"name":"quote_mint","type":"publicKey"},{"name":"final_price","type":"u128"},{"name":"graduation_methods","type":{"array":[{"defined":"GraduationMethodData"},8]}}]},{"name":"BondingCurveClosed","fields":[{"name":"bonding_curve","type":"publicKey"}]}]}},{"name":"MintConfigUpdateType","type":{"kind":"enum","variants":[{"name":"DefaultCreatorReward","fields":["u64"]},{"name":"DefaultGraduationReward","fields":["u64"]},{"name":"DefaultGraduationTarget","fields":["u64"]},{"name":"DefaultMaxBuyAmount","fields":["u64"]},{"name":"DefaultMaxSellAmount","fields":["u64"]},{"name":"DefaultBuyPermissionBitmap","fields":[{"option":{"array":["u8",32]}}]},{"name":"DefaultSellPermissionBitmap","fields":[{"option":{"array":["u8",32]}}]},{"name":"CreatePermissionBitmap","fields":[{"option":{"array":["u8",32]}}]},{"name":"PriceCurve","fields":["u128","u128",{"array":["u16",4]}]},{"name":"DefaultSwapFeeBps","fields":["u16"]},{"name":"DefaultBaseFeeBps","fields":["u16"]},{"name":"DefaultQuoteFeeBps","fields":["u16"]}]}},{"name":"PermissionConfigUpdateType","type":{"kind":"enum","variants":[{"name":"AllowedSigner1","fields":[{"defined":"PermissionSigner"}]},{"name":"AllowedSigner2","fields":[{"defined":"PermissionSigner"}]},{"name":"AllowedSigner3","fields":[{"defined":"PermissionSigner"}]}]}},{"name":"AccountDiscriminator","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Closed"},{"name":"BondingCurve"},{"name":"AuthorityConfig"},{"name":"PermissionConfig"},{"name":"ConsumedPermission"},{"name":"MintConfig"}]}},{"name":"Privilege","type":{"kind":"enum","variants":[{"name":"AuthorityConfigUpdate"},{"name":"ReceiveProtocolFees"},{"name":"MintConfigInitialize"},{"name":"MintConfigUpdate"},{"name":"MintConfigClose"},{"name":"PermissionConfigInitialize"},{"name":"PermissionConfigUpdate"},{"name":"PermissionConfigClose"},{"name":"BondingCurveInitialize"},{"name":"BondingCurveUpdate"},{"name":"BondingCurveClose"},{"name":"ExecuteLpTakeover"}]}},{"name":"GraduationMethodLabel","type":{"kind":"enum","variants":[{"name":"None"},{"name":"Whirlpool"},{"name":"Manual"}]}},{"name":"GraduationMethod","type":{"kind":"enum","variants":[{"name":"None"},{"name":"Whirlpool","fields":[{"name":"split_bps","type":"u16"},{"name":"fee_tier_index","type":"u16"},{"name":"destination","type":"publicKey"},{"name":"unlocked","type":"bool"}]},{"name":"Manual","fields":[{"name":"split_bps","type":"u16"},{"name":"destination","type":"publicKey"}]}]}},{"name":"PermissionMessage","type":{"kind":"enum","variants":[{"name":"V1","fields":[{"name":"nonce","type":"u64"},{"name":"consumer_program","type":"publicKey"},{"name":"permission_signer","type":{"defined":"PermissionSigner"}},{"name":"permission_subject","type":"publicKey"},{"name":"valid_until","type":"u64"},{"name":"permission_type","type":"u8"},{"name":"instruction_discriminators","type":{"vec":"bytes"}}]}]}}],"errors":[{"code":6000,"name":"InvalidAccountRole","msg":"Invalid account role"},{"code":6001,"name":"IncorrectAccountAddress","msg":"Incorrect account address"},{"code":6002,"name":"PermissionActive","msg":"Permission still active"},{"code":6003,"name":"PermissionExpired","msg":"Permission expired"},{"code":6004,"name":"PermissionInvalidSignature","msg":"Permission invalid signature"},{"code":6005,"name":"WhirlpoolError","msg":"Error from whirlpool lib"},{"code":6006,"name":"UnknownInstructionDiscriminator","msg":"Unknown instruction discriminator"},{"code":6007,"name":"IncorrectProgramId","msg":"Incorrect program id"},{"code":6008,"name":"NotEnoughAccountKeys","msg":"Not enough account keys"},{"code":6009,"name":"ArithmeticError","msg":"Arithmetic error"},{"code":6010,"name":"QuoteError","msg":"Error calculating quote from math-lib"},{"code":6011,"name":"SlippageExceeded","msg":"Slippage exceeded"},{"code":6012,"name":"PriceError","msg":"Error calculating token price"},{"code":6013,"name":"GraduationConditionsNotMet","msg":"Token has not met graduation conditions"},{"code":6014,"name":"WhirlpoolAlreadyExists","msg":"Whirlpool already exists"},{"code":6015,"name":"IncorrectGraduationInstruction","msg":"Incorrect graduation instruction"},{"code":6016,"name":"BondingCurveNotExpired","msg":"Bonding curve is still active"},{"code":6017,"name":"InsufficientTokensForGraduation","msg":"Insufficient tokens for graduation"},{"code":6018,"name":"MissingPermissionConsumeInstruction","msg":"Missing permission consume instruction"},{"code":6019,"name":"BondingCurveNotActive","msg":"Bonding curve is either not launched yet or is ready to be graduated"},{"code":6020,"name":"InvalidPriceCurve","msg":"Invalid price curve"},{"code":6021,"name":"GraduationMethodNotCalled","msg":"Graduation method not called"},{"code":6022,"name":"InvalidAccountData","msg":"Invalid account data"},{"code":6023,"name":"InvalidSeeds","msg":"Invalid seeds"},{"code":6024,"name":"IncorrectOwner","msg":"Account is not owned by the right program"},{"code":6025,"name":"InvalidInstructionData","msg":"Invalid instruction data"},{"code":6026,"name":"ReservedX","msg":"Reserved for future use"},{"code":6027,"name":"MissingPrivileges","msg":"An account does not have the required privileges"},{"code":6028,"name":"NoMoreAuthoritySlots","msg":"No more authority slots"},{"code":6029,"name":"EmitEventError","msg":"Event serialization failed"}],"metadata":{"origin":"shank","address":"waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF"}} \ No newline at end of file diff --git a/idls/orca_whirlpool.whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.json b/idls/orca_whirlpool.whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.json new file mode 100644 index 0000000..7a4dee9 --- /dev/null +++ b/idls/orca_whirlpool.whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.json @@ -0,0 +1 @@ +{"address":"whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc","metadata":{"name":"whirlpool","version":"0.9.0","spec":"0.1.0"},"instructions":[{"name":"close_bundled_position","discriminator":[41,36,216,245,27,85,103,67],"accounts":[{"name":"bundled_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,117,110,100,108,101,100,95,112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_bundle.position_bundle_mint","account":"PositionBundle"},{"kind":"arg","path":"bundle_index"}]}},{"name":"position_bundle","writable":true},{"name":"position_bundle_token_account"},{"name":"position_bundle_authority","signer":true},{"name":"receiver","writable":true}],"args":[{"name":"bundle_index","type":"u16"}]},{"name":"close_position","discriminator":[123,134,81,0,49,68,98,98],"accounts":[{"name":"position_authority","signer":true},{"name":"receiver","writable":true},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_mint"}]}},{"name":"position_mint","writable":true},{"name":"position_token_account","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[]},{"name":"close_position_with_token_extensions","discriminator":[1,182,135,59,155,25,99,223],"accounts":[{"name":"position_authority","signer":true},{"name":"receiver","writable":true},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_mint"}]}},{"name":"position_mint","writable":true},{"name":"position_token_account","writable":true},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[]},{"name":"collect_fees","discriminator":[164,152,207,99,30,186,19,182],"accounts":[{"name":"whirlpool","relations":["position"]},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_owner_account_a","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_b","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[]},{"name":"collect_fees_v2","discriminator":[207,117,95,191,229,180,226,15],"accounts":[{"name":"whirlpool","relations":["position"]},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_b","writable":true},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"collect_protocol_fees","discriminator":[22,67,23,98,150,178,70,220],"accounts":[{"name":"whirlpools_config","relations":["whirlpool"]},{"name":"whirlpool","writable":true},{"name":"collect_protocol_fees_authority","signer":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"token_destination_a","writable":true},{"name":"token_destination_b","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[]},{"name":"collect_protocol_fees_v2","discriminator":[103,128,222,134,114,200,22,200],"accounts":[{"name":"whirlpools_config","relations":["whirlpool"]},{"name":"whirlpool","writable":true},{"name":"collect_protocol_fees_authority","signer":true},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"token_destination_a","writable":true},{"name":"token_destination_b","writable":true},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"collect_reward","discriminator":[70,5,132,87,86,235,177,34],"accounts":[{"name":"whirlpool","relations":["position"]},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"reward_owner_account","writable":true},{"name":"reward_vault","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"collect_reward_v2","discriminator":[177,107,37,180,160,19,49,209],"accounts":[{"name":"whirlpool","relations":["position"]},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"reward_owner_account","writable":true},{"name":"reward_mint"},{"name":"reward_vault","writable":true},{"name":"reward_token_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"reward_index","type":"u8"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"decrease_liquidity","discriminator":[160,38,208,111,104,91,44,1],"accounts":[{"name":"whirlpool","writable":true,"relations":["position"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_lower","writable":true},{"name":"tick_array_upper","writable":true}],"args":[{"name":"liquidity_amount","type":"u128"},{"name":"token_min_a","type":"u64"},{"name":"token_min_b","type":"u64"}]},{"name":"decrease_liquidity_v2","discriminator":[58,127,188,62,79,82,196,96],"accounts":[{"name":"whirlpool","writable":true,"relations":["position"]},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_lower","writable":true},{"name":"tick_array_upper","writable":true}],"args":[{"name":"liquidity_amount","type":"u128"},{"name":"token_min_a","type":"u64"},{"name":"token_min_b","type":"u64"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"delete_position_bundle","discriminator":[100,25,99,2,217,239,124,173],"accounts":[{"name":"position_bundle","writable":true},{"name":"position_bundle_mint","writable":true},{"name":"position_bundle_token_account","writable":true},{"name":"position_bundle_owner","signer":true},{"name":"receiver","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[]},{"name":"delete_token_badge","discriminator":[53,146,68,8,18,117,17,185],"accounts":[{"name":"whirlpools_config","relations":["whirlpools_config_extension","token_badge"]},{"name":"whirlpools_config_extension"},{"name":"token_badge_authority","signer":true},{"name":"token_mint"},{"name":"token_badge","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,98,97,100,103,101]},{"kind":"account","path":"whirlpools_config"},{"kind":"account","path":"token_mint"}]}},{"name":"receiver","writable":true}],"args":[]},{"name":"idl_include","discriminator":[223,253,121,121,60,193,129,31],"accounts":[{"name":"tick_array"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"increase_liquidity","discriminator":[46,156,243,118,13,205,251,178],"accounts":[{"name":"whirlpool","writable":true,"relations":["position"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_lower","writable":true},{"name":"tick_array_upper","writable":true}],"args":[{"name":"liquidity_amount","type":"u128"},{"name":"token_max_a","type":"u64"},{"name":"token_max_b","type":"u64"}]},{"name":"increase_liquidity_by_token_amounts_v2","discriminator":[239,251,9,124,210,198,53,43],"accounts":[{"name":"whirlpool","writable":true,"relations":["position"]},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_lower","writable":true},{"name":"tick_array_upper","writable":true}],"args":[{"name":"method","type":{"defined":{"name":"IncreaseLiquidityMethod"}}},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"increase_liquidity_v2","discriminator":[133,29,89,223,69,238,176,10],"accounts":[{"name":"whirlpool","writable":true,"relations":["position"]},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_lower","writable":true},{"name":"tick_array_upper","writable":true}],"args":[{"name":"liquidity_amount","type":"u128"},{"name":"token_max_a","type":"u64"},{"name":"token_max_b","type":"u64"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"initialize_adaptive_fee_tier","discriminator":[77,99,208,200,141,123,117,48],"accounts":[{"name":"whirlpools_config"},{"name":"adaptive_fee_tier","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,116,105,101,114]},{"kind":"account","path":"whirlpools_config"},{"kind":"arg","path":"fee_tier_index"}]}},{"name":"funder","writable":true,"signer":true},{"name":"fee_authority","signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"fee_tier_index","type":"u16"},{"name":"tick_spacing","type":"u16"},{"name":"initialize_pool_authority","type":"pubkey"},{"name":"delegated_fee_authority","type":"pubkey"},{"name":"default_base_fee_rate","type":"u16"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"adaptive_fee_control_factor","type":"u32"},{"name":"max_volatility_accumulator","type":"u32"},{"name":"tick_group_size","type":"u16"},{"name":"major_swap_threshold_ticks","type":"u16"}]},{"name":"initialize_config","discriminator":[208,127,21,1,194,190,196,70],"accounts":[{"name":"config","writable":true,"signer":true},{"name":"funder","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"fee_authority","type":"pubkey"},{"name":"collect_protocol_fees_authority","type":"pubkey"},{"name":"reward_emissions_super_authority","type":"pubkey"},{"name":"default_protocol_fee_rate","type":"u16"}]},{"name":"initialize_config_extension","discriminator":[55,9,53,9,114,57,209,52],"accounts":[{"name":"config"},{"name":"config_extension","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103,95,101,120,116,101,110,115,105,111,110]},{"kind":"account","path":"config"}]}},{"name":"funder","writable":true,"signer":true},{"name":"fee_authority","signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"initialize_dynamic_tick_array","discriminator":[41,33,165,200,120,231,142,50],"accounts":[{"name":"whirlpool"},{"name":"funder","writable":true,"signer":true},{"name":"tick_array","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"whirlpool"},{"kind":"arg","path":"start_tick_index"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"start_tick_index","type":"i32"},{"name":"idempotent","type":"bool"}]},{"name":"initialize_fee_tier","discriminator":[183,74,156,160,112,2,42,30],"accounts":[{"name":"config"},{"name":"fee_tier","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,116,105,101,114]},{"kind":"account","path":"config"},{"kind":"arg","path":"tick_spacing"}]}},{"name":"funder","writable":true,"signer":true},{"name":"fee_authority","signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"tick_spacing","type":"u16"},{"name":"default_fee_rate","type":"u16"}]},{"name":"initialize_pool","discriminator":[95,180,10,172,84,174,232,40],"accounts":[{"name":"whirlpools_config","relations":["fee_tier"]},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"funder","writable":true,"signer":true},{"name":"whirlpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[119,104,105,114,108,112,111,111,108]},{"kind":"account","path":"whirlpools_config"},{"kind":"account","path":"token_mint_a"},{"kind":"account","path":"token_mint_b"},{"kind":"arg","path":"tick_spacing"}]}},{"name":"token_vault_a","writable":true,"signer":true},{"name":"token_vault_b","writable":true,"signer":true},{"name":"fee_tier"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"bumps","type":{"defined":{"name":"WhirlpoolBumps"}}},{"name":"tick_spacing","type":"u16"},{"name":"initial_sqrt_price","type":"u128"}]},{"name":"initialize_pool_v2","discriminator":[207,45,87,242,27,63,204,67],"accounts":[{"name":"whirlpools_config","relations":["fee_tier"]},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_badge_a","pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,98,97,100,103,101]},{"kind":"account","path":"whirlpools_config"},{"kind":"account","path":"token_mint_a"}]}},{"name":"token_badge_b","pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,98,97,100,103,101]},{"kind":"account","path":"whirlpools_config"},{"kind":"account","path":"token_mint_b"}]}},{"name":"funder","writable":true,"signer":true},{"name":"whirlpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[119,104,105,114,108,112,111,111,108]},{"kind":"account","path":"whirlpools_config"},{"kind":"account","path":"token_mint_a"},{"kind":"account","path":"token_mint_b"},{"kind":"arg","path":"tick_spacing"}]}},{"name":"token_vault_a","writable":true,"signer":true},{"name":"token_vault_b","writable":true,"signer":true},{"name":"fee_tier"},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"tick_spacing","type":"u16"},{"name":"initial_sqrt_price","type":"u128"}]},{"name":"initialize_pool_with_adaptive_fee","discriminator":[143,94,96,76,172,124,119,199],"accounts":[{"name":"whirlpools_config","relations":["adaptive_fee_tier"]},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_badge_a","pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,98,97,100,103,101]},{"kind":"account","path":"whirlpools_config"},{"kind":"account","path":"token_mint_a"}]}},{"name":"token_badge_b","pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,98,97,100,103,101]},{"kind":"account","path":"whirlpools_config"},{"kind":"account","path":"token_mint_b"}]}},{"name":"funder","writable":true,"signer":true},{"name":"initialize_pool_authority","signer":true},{"name":"whirlpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[119,104,105,114,108,112,111,111,108]},{"kind":"account","path":"whirlpools_config"},{"kind":"account","path":"token_mint_a"},{"kind":"account","path":"token_mint_b"},{"kind":"account","path":"adaptive_fee_tier.fee_tier_index","account":"AdaptiveFeeTier"}]}},{"name":"oracle","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"whirlpool"}]}},{"name":"token_vault_a","writable":true,"signer":true},{"name":"token_vault_b","writable":true,"signer":true},{"name":"adaptive_fee_tier"},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"initial_sqrt_price","type":"u128"},{"name":"trade_enable_timestamp","type":{"option":"u64"}}]},{"name":"initialize_position_bundle","discriminator":[117,45,241,149,24,18,194,65],"accounts":[{"name":"position_bundle","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110,95,98,117,110,100,108,101]},{"kind":"account","path":"position_bundle_mint"}]}},{"name":"position_bundle_mint","writable":true,"signer":true},{"name":"position_bundle_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"position_bundle_owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_bundle_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"position_bundle_owner"},{"name":"funder","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[]},{"name":"initialize_position_bundle_with_metadata","discriminator":[93,124,16,179,249,131,115,245],"accounts":[{"name":"position_bundle","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110,95,98,117,110,100,108,101]},{"kind":"account","path":"position_bundle_mint"}]}},{"name":"position_bundle_mint","writable":true,"signer":true},{"name":"position_bundle_metadata","writable":true},{"name":"position_bundle_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"position_bundle_owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_bundle_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"position_bundle_owner"},{"name":"funder","writable":true,"signer":true},{"name":"metadata_update_auth","address":"3axbTs2z5GBy6usVbNVoqEgZMng3vZvMnAoX29BFfwhr"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"}],"args":[]},{"name":"initialize_reward","discriminator":[95,135,192,196,242,129,230,68],"accounts":[{"name":"reward_authority","signer":true},{"name":"funder","writable":true,"signer":true},{"name":"whirlpool","writable":true},{"name":"reward_mint"},{"name":"reward_vault","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"initialize_reward_v2","discriminator":[91,1,77,50,235,229,133,49],"accounts":[{"name":"reward_authority","signer":true},{"name":"funder","writable":true,"signer":true},{"name":"whirlpool","writable":true},{"name":"reward_mint"},{"name":"reward_token_badge","pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,98,97,100,103,101]},{"kind":"account","path":"whirlpool.whirlpools_config","account":"Whirlpool"},{"kind":"account","path":"reward_mint"}]}},{"name":"reward_vault","writable":true,"signer":true},{"name":"reward_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"initialize_tick_array","discriminator":[11,188,193,214,141,91,149,184],"accounts":[{"name":"whirlpool"},{"name":"funder","writable":true,"signer":true},{"name":"tick_array","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"whirlpool"},{"kind":"arg","path":"start_tick_index"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"start_tick_index","type":"i32"}]},{"name":"initialize_token_badge","discriminator":[253,77,205,95,27,224,89,223],"accounts":[{"name":"whirlpools_config","relations":["whirlpools_config_extension"]},{"name":"whirlpools_config_extension"},{"name":"token_badge_authority","signer":true},{"name":"token_mint"},{"name":"token_badge","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,98,97,100,103,101]},{"kind":"account","path":"whirlpools_config"},{"kind":"account","path":"token_mint"}]}},{"name":"funder","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"lock_position","discriminator":[227,62,2,252,247,10,171,185],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"position_authority","signer":true},{"name":"position","pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_mint"}]}},{"name":"position_mint"},{"name":"position_token_account","writable":true},{"name":"lock_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,111,99,107,95,99,111,110,102,105,103]},{"kind":"account","path":"position"}]}},{"name":"whirlpool","relations":["position"]},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"lock_type","type":{"defined":{"name":"LockType"}}}]},{"name":"migrate_repurpose_reward_authority_space","discriminator":[214,161,248,79,152,98,172,231],"accounts":[{"name":"whirlpool","writable":true}],"args":[]},{"name":"open_bundled_position","discriminator":[169,113,126,171,213,172,212,49],"accounts":[{"name":"bundled_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,117,110,100,108,101,100,95,112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_bundle.position_bundle_mint","account":"PositionBundle"},{"kind":"arg","path":"bundle_index"}]}},{"name":"position_bundle","writable":true},{"name":"position_bundle_token_account"},{"name":"position_bundle_authority","signer":true},{"name":"whirlpool"},{"name":"funder","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"bundle_index","type":"u16"},{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"}]},{"name":"open_position","discriminator":[135,128,47,77,15,152,240,49],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"owner"},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_mint"}]}},{"name":"position_mint","writable":true,"signer":true},{"name":"position_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"whirlpool"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"bumps","type":{"defined":{"name":"OpenPositionBumps"}}},{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"}]},{"name":"open_position_with_metadata","discriminator":[242,29,134,48,58,110,14,60],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"owner"},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_mint"}]}},{"name":"position_mint","writable":true,"signer":true},{"name":"position_metadata_account","writable":true},{"name":"position_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"whirlpool"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"metadata_update_auth","address":"3axbTs2z5GBy6usVbNVoqEgZMng3vZvMnAoX29BFfwhr"}],"args":[{"name":"bumps","type":{"defined":{"name":"OpenPositionWithMetadataBumps"}}},{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"}]},{"name":"open_position_with_token_extensions","discriminator":[212,47,95,92,114,102,131,250],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"owner"},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_mint"}]}},{"name":"position_mint","writable":true,"signer":true},{"name":"position_token_account","writable":true},{"name":"whirlpool"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_update_auth","address":"3axbTs2z5GBy6usVbNVoqEgZMng3vZvMnAoX29BFfwhr"}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"with_token_metadata_extension","type":"bool"}]},{"name":"reposition_liquidity_v2","discriminator":[191,169,224,11,131,19,158,253],"accounts":[{"name":"whirlpool","writable":true,"relations":["position"]},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"position_authority","signer":true},{"name":"funder","writable":true,"signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"existing_tick_array_lower","writable":true},{"name":"existing_tick_array_upper","writable":true},{"name":"new_tick_array_lower","writable":true},{"name":"new_tick_array_upper","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"new_tick_lower_index","type":"i32"},{"name":"new_tick_upper_index","type":"i32"},{"name":"method","type":{"defined":{"name":"RepositionLiquidityMethod"}}},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"reset_position_range","discriminator":[164,123,180,141,194,100,160,175],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"position_authority","signer":true},{"name":"whirlpool","relations":["position"]},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"new_tick_lower_index","type":"i32"},{"name":"new_tick_upper_index","type":"i32"}]},{"name":"set_adaptive_fee_constants","discriminator":[133,158,212,189,237,12,73,39],"accounts":[{"name":"whirlpool","relations":["oracle"]},{"name":"whirlpools_config","relations":["whirlpool"]},{"name":"oracle","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"filter_period","type":{"option":"u16"}},{"name":"decay_period","type":{"option":"u16"}},{"name":"reduction_factor","type":{"option":"u16"}},{"name":"adaptive_fee_control_factor","type":{"option":"u32"}},{"name":"max_volatility_accumulator","type":{"option":"u32"}},{"name":"tick_group_size","type":{"option":"u16"}},{"name":"major_swap_threshold_ticks","type":{"option":"u16"}}]},{"name":"set_collect_protocol_fees_authority","discriminator":[34,150,93,244,139,225,233,67],"accounts":[{"name":"whirlpools_config","writable":true},{"name":"collect_protocol_fees_authority","signer":true},{"name":"new_collect_protocol_fees_authority"}],"args":[]},{"name":"set_config_extension_authority","discriminator":[44,94,241,116,24,188,60,143],"accounts":[{"name":"whirlpools_config","relations":["whirlpools_config_extension"]},{"name":"whirlpools_config_extension","writable":true},{"name":"config_extension_authority","signer":true},{"name":"new_config_extension_authority"}],"args":[]},{"name":"set_config_feature_flag","discriminator":[71,173,228,18,67,247,210,57],"accounts":[{"name":"whirlpools_config","writable":true},{"name":"authority","signer":true}],"args":[{"name":"feature_flag","type":{"defined":{"name":"ConfigFeatureFlag"}}}]},{"name":"set_default_base_fee_rate","discriminator":[229,66,84,251,164,134,183,7],"accounts":[{"name":"whirlpools_config","relations":["adaptive_fee_tier"]},{"name":"adaptive_fee_tier","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"default_base_fee_rate","type":"u16"}]},{"name":"set_default_fee_rate","discriminator":[118,215,214,157,182,229,208,228],"accounts":[{"name":"whirlpools_config","relations":["fee_tier"]},{"name":"fee_tier","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"default_fee_rate","type":"u16"}]},{"name":"set_default_protocol_fee_rate","discriminator":[107,205,249,226,151,35,86,0],"accounts":[{"name":"whirlpools_config","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"default_protocol_fee_rate","type":"u16"}]},{"name":"set_delegated_fee_authority","discriminator":[193,234,231,147,138,57,3,122],"accounts":[{"name":"whirlpools_config","relations":["adaptive_fee_tier"]},{"name":"adaptive_fee_tier","writable":true},{"name":"fee_authority","signer":true},{"name":"new_delegated_fee_authority"}],"args":[]},{"name":"set_fee_authority","discriminator":[31,1,50,87,237,101,97,132],"accounts":[{"name":"whirlpools_config","writable":true},{"name":"fee_authority","signer":true},{"name":"new_fee_authority"}],"args":[]},{"name":"set_fee_rate","discriminator":[53,243,137,65,8,140,158,6],"accounts":[{"name":"whirlpools_config","relations":["whirlpool"]},{"name":"whirlpool","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"fee_rate","type":"u16"}]},{"name":"set_fee_rate_by_delegated_fee_authority","discriminator":[121,121,54,114,131,230,162,104],"accounts":[{"name":"whirlpool","writable":true},{"name":"adaptive_fee_tier"},{"name":"delegated_fee_authority","signer":true}],"args":[{"name":"fee_rate","type":"u16"}]},{"name":"set_initialize_pool_authority","discriminator":[125,43,127,235,149,26,106,236],"accounts":[{"name":"whirlpools_config","relations":["adaptive_fee_tier"]},{"name":"adaptive_fee_tier","writable":true},{"name":"fee_authority","signer":true},{"name":"new_initialize_pool_authority"}],"args":[]},{"name":"set_preset_adaptive_fee_constants","discriminator":[132,185,66,148,83,88,134,198],"accounts":[{"name":"whirlpools_config","relations":["adaptive_fee_tier"]},{"name":"adaptive_fee_tier","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"adaptive_fee_control_factor","type":"u32"},{"name":"max_volatility_accumulator","type":"u32"},{"name":"tick_group_size","type":"u16"},{"name":"major_swap_threshold_ticks","type":"u16"}]},{"name":"set_protocol_fee_rate","discriminator":[95,7,4,50,154,79,156,131],"accounts":[{"name":"whirlpools_config","relations":["whirlpool"]},{"name":"whirlpool","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"protocol_fee_rate","type":"u16"}]},{"name":"set_reward_authority","discriminator":[34,39,183,252,83,28,85,127],"accounts":[{"name":"whirlpool","writable":true},{"name":"reward_authority","signer":true},{"name":"new_reward_authority"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"set_reward_authority_by_super_authority","discriminator":[240,154,201,198,148,93,56,25],"accounts":[{"name":"whirlpools_config","relations":["whirlpool"]},{"name":"whirlpool","writable":true},{"name":"reward_emissions_super_authority","signer":true},{"name":"new_reward_authority"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"set_reward_emissions","discriminator":[13,197,86,168,109,176,27,244],"accounts":[{"name":"whirlpool","writable":true},{"name":"reward_authority","signer":true},{"name":"reward_vault"}],"args":[{"name":"reward_index","type":"u8"},{"name":"emissions_per_second_x64","type":"u128"}]},{"name":"set_reward_emissions_super_authority","discriminator":[207,5,200,209,122,56,82,183],"accounts":[{"name":"whirlpools_config","writable":true},{"name":"reward_emissions_super_authority","signer":true},{"name":"new_reward_emissions_super_authority"}],"args":[]},{"name":"set_reward_emissions_v2","discriminator":[114,228,72,32,193,48,160,102],"accounts":[{"name":"whirlpool","writable":true},{"name":"reward_authority","signer":true},{"name":"reward_vault"}],"args":[{"name":"reward_index","type":"u8"},{"name":"emissions_per_second_x64","type":"u128"}]},{"name":"set_token_badge_attribute","discriminator":[224,88,65,33,138,147,246,137],"accounts":[{"name":"whirlpools_config","relations":["whirlpools_config_extension","token_badge"]},{"name":"whirlpools_config_extension"},{"name":"token_badge_authority","signer":true},{"name":"token_mint","relations":["token_badge"]},{"name":"token_badge","writable":true}],"args":[{"name":"attribute","type":{"defined":{"name":"TokenBadgeAttribute"}}}]},{"name":"set_token_badge_authority","discriminator":[207,202,4,32,205,79,13,178],"accounts":[{"name":"whirlpools_config","relations":["whirlpools_config_extension"]},{"name":"whirlpools_config_extension","writable":true},{"name":"config_extension_authority","signer":true},{"name":"new_token_badge_authority"}],"args":[]},{"name":"swap","discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_authority","signer":true},{"name":"whirlpool","writable":true},{"name":"token_owner_account_a","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_0","writable":true},{"name":"tick_array_1","writable":true},{"name":"tick_array_2","writable":true},{"name":"oracle","pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"whirlpool"}]}}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit","type":"u128"},{"name":"amount_specified_is_input","type":"bool"},{"name":"a_to_b","type":"bool"}]},{"name":"swap_v2","discriminator":[43,4,237,11,26,201,30,98],"accounts":[{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"token_authority","signer":true},{"name":"whirlpool","writable":true},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_0","writable":true},{"name":"tick_array_1","writable":true},{"name":"tick_array_2","writable":true},{"name":"oracle","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"whirlpool"}]}}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit","type":"u128"},{"name":"amount_specified_is_input","type":"bool"},{"name":"a_to_b","type":"bool"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"transfer_locked_position","discriminator":[179,121,229,46,67,138,194,138],"accounts":[{"name":"position_authority","signer":true},{"name":"receiver","writable":true},{"name":"position","pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_mint"}]},"relations":["lock_config"]},{"name":"position_mint"},{"name":"position_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"lock_config","writable":true},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[]},{"name":"two_hop_swap","discriminator":[195,96,237,108,68,162,219,230],"accounts":[{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_authority","signer":true},{"name":"whirlpool_one","writable":true},{"name":"whirlpool_two","writable":true},{"name":"token_owner_account_one_a","writable":true},{"name":"token_vault_one_a","writable":true},{"name":"token_owner_account_one_b","writable":true},{"name":"token_vault_one_b","writable":true},{"name":"token_owner_account_two_a","writable":true},{"name":"token_vault_two_a","writable":true},{"name":"token_owner_account_two_b","writable":true},{"name":"token_vault_two_b","writable":true},{"name":"tick_array_one_0","writable":true},{"name":"tick_array_one_1","writable":true},{"name":"tick_array_one_2","writable":true},{"name":"tick_array_two_0","writable":true},{"name":"tick_array_two_1","writable":true},{"name":"tick_array_two_2","writable":true},{"name":"oracle_one","pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"whirlpool_one"}]}},{"name":"oracle_two","pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"whirlpool_two"}]}}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"amount_specified_is_input","type":"bool"},{"name":"a_to_b_one","type":"bool"},{"name":"a_to_b_two","type":"bool"},{"name":"sqrt_price_limit_one","type":"u128"},{"name":"sqrt_price_limit_two","type":"u128"}]},{"name":"two_hop_swap_v2","discriminator":[186,143,209,29,254,2,194,117],"accounts":[{"name":"whirlpool_one","writable":true},{"name":"whirlpool_two","writable":true},{"name":"token_mint_input"},{"name":"token_mint_intermediate"},{"name":"token_mint_output"},{"name":"token_program_input"},{"name":"token_program_intermediate"},{"name":"token_program_output"},{"name":"token_owner_account_input","writable":true},{"name":"token_vault_one_input","writable":true},{"name":"token_vault_one_intermediate","writable":true},{"name":"token_vault_two_intermediate","writable":true},{"name":"token_vault_two_output","writable":true},{"name":"token_owner_account_output","writable":true},{"name":"token_authority","signer":true},{"name":"tick_array_one_0","writable":true},{"name":"tick_array_one_1","writable":true},{"name":"tick_array_one_2","writable":true},{"name":"tick_array_two_0","writable":true},{"name":"tick_array_two_1","writable":true},{"name":"tick_array_two_2","writable":true},{"name":"oracle_one","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"whirlpool_one"}]}},{"name":"oracle_two","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"whirlpool_two"}]}},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"amount_specified_is_input","type":"bool"},{"name":"a_to_b_one","type":"bool"},{"name":"a_to_b_two","type":"bool"},{"name":"sqrt_price_limit_one","type":"u128"},{"name":"sqrt_price_limit_two","type":"u128"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"update_fees_and_rewards","discriminator":[154,230,250,13,236,209,75,223],"accounts":[{"name":"whirlpool","writable":true,"relations":["position"]},{"name":"position","writable":true},{"name":"tick_array_lower"},{"name":"tick_array_upper"}],"args":[]}],"accounts":[{"name":"AdaptiveFeeTier","discriminator":[147,16,144,116,47,146,149,46]},{"name":"DynamicTickArray","discriminator":[17,216,246,142,225,199,218,56]},{"name":"FeeTier","discriminator":[56,75,159,76,142,68,190,105]},{"name":"LockConfig","discriminator":[106,47,238,159,124,12,160,192]},{"name":"Oracle","discriminator":[139,194,131,179,140,179,229,244]},{"name":"Position","discriminator":[170,188,143,228,122,64,247,208]},{"name":"PositionBundle","discriminator":[129,169,175,65,185,95,32,100]},{"name":"TickArray","discriminator":[69,97,189,190,110,7,66,187]},{"name":"TokenBadge","discriminator":[116,219,204,229,249,116,255,150]},{"name":"Whirlpool","discriminator":[63,149,209,12,225,128,99,9]},{"name":"WhirlpoolsConfig","discriminator":[157,20,49,224,217,87,193,254]},{"name":"WhirlpoolsConfigExtension","discriminator":[2,99,215,163,240,26,153,58]}],"events":[{"name":"LiquidityDecreased","discriminator":[166,1,36,71,112,202,181,171]},{"name":"LiquidityIncreased","discriminator":[30,7,144,181,102,254,155,161]},{"name":"LiquidityRepositioned","discriminator":[95,130,181,132,251,50,195,38]},{"name":"PoolInitialized","discriminator":[100,118,173,87,12,198,254,229]},{"name":"PositionOpened","discriminator":[237,175,243,230,147,117,101,121]},{"name":"Traded","discriminator":[225,202,73,175,147,43,160,150]}],"errors":[{"code":6000,"name":"InvalidEnum","msg":"Enum value could not be converted"},{"code":6001,"name":"InvalidStartTick","msg":"Invalid start tick index provided."},{"code":6002,"name":"TickArrayExistInPool","msg":"Tick-array already exists in this whirlpool"},{"code":6003,"name":"TickArrayIndexOutofBounds","msg":"Attempt to search for a tick-array failed"},{"code":6004,"name":"InvalidTickSpacing","msg":"Tick-spacing is not supported"},{"code":6005,"name":"ClosePositionNotEmpty","msg":"Position is not empty It cannot be closed"},{"code":6006,"name":"DivideByZero","msg":"Unable to divide by zero"},{"code":6007,"name":"NumberCastError","msg":"Unable to cast number into BigInt"},{"code":6008,"name":"NumberDownCastError","msg":"Unable to down cast number"},{"code":6009,"name":"TickNotFound","msg":"Tick not found within tick array"},{"code":6010,"name":"InvalidTickIndex","msg":"Provided tick index is either out of bounds or uninitializable"},{"code":6011,"name":"SqrtPriceOutOfBounds","msg":"Provided sqrt price out of bounds"},{"code":6012,"name":"LiquidityZero","msg":"Liquidity amount must be greater than zero"},{"code":6013,"name":"LiquidityTooHigh","msg":"Liquidity amount must be less than i64::MAX"},{"code":6014,"name":"LiquidityOverflow","msg":"Liquidity overflow"},{"code":6015,"name":"LiquidityUnderflow","msg":"Liquidity underflow"},{"code":6016,"name":"LiquidityNetError","msg":"Tick liquidity net underflowed or overflowed"},{"code":6017,"name":"TokenMaxExceeded","msg":"Exceeded token max"},{"code":6018,"name":"TokenMinSubceeded","msg":"Did not meet token min"},{"code":6019,"name":"MissingOrInvalidDelegate","msg":"Position token account has a missing or invalid delegate"},{"code":6020,"name":"InvalidPositionTokenAmount","msg":"Position token amount must be 1"},{"code":6021,"name":"InvalidTimestampConversion","msg":"Timestamp should be convertible from i64 to u64"},{"code":6022,"name":"InvalidTimestamp","msg":"Timestamp should be greater than the last updated timestamp"},{"code":6023,"name":"InvalidTickArraySequence","msg":"Invalid tick array sequence provided for instruction."},{"code":6024,"name":"InvalidTokenMintOrder","msg":"Token Mint in wrong order"},{"code":6025,"name":"RewardNotInitialized","msg":"Reward not initialized"},{"code":6026,"name":"InvalidRewardIndex","msg":"Invalid reward index"},{"code":6027,"name":"RewardVaultAmountInsufficient","msg":"Reward vault requires amount to support emissions for at least one day"},{"code":6028,"name":"FeeRateMaxExceeded","msg":"Exceeded max fee rate"},{"code":6029,"name":"ProtocolFeeRateMaxExceeded","msg":"Exceeded max protocol fee rate"},{"code":6030,"name":"MultiplicationShiftRightOverflow","msg":"Multiplication with shift right overflow"},{"code":6031,"name":"MulDivOverflow","msg":"Muldiv overflow"},{"code":6032,"name":"MulDivInvalidInput","msg":"Invalid div_u256 input"},{"code":6033,"name":"MultiplicationOverflow","msg":"Multiplication overflow"},{"code":6034,"name":"InvalidSqrtPriceLimitDirection","msg":"Provided SqrtPriceLimit not in the same direction as the swap."},{"code":6035,"name":"ZeroTradableAmount","msg":"There are no tradable amount to swap."},{"code":6036,"name":"AmountOutBelowMinimum","msg":"Amount out below minimum threshold"},{"code":6037,"name":"AmountInAboveMaximum","msg":"Amount in above maximum threshold"},{"code":6038,"name":"TickArraySequenceInvalidIndex","msg":"Invalid index for tick array sequence"},{"code":6039,"name":"AmountCalcOverflow","msg":"Amount calculated overflows"},{"code":6040,"name":"AmountRemainingOverflow","msg":"Amount remaining overflows"},{"code":6041,"name":"InvalidIntermediaryMint","msg":"Invalid intermediary mint"},{"code":6042,"name":"DuplicateTwoHopPool","msg":"Duplicate two hop pool"},{"code":6043,"name":"InvalidBundleIndex","msg":"Bundle index is out of bounds"},{"code":6044,"name":"BundledPositionAlreadyOpened","msg":"Position has already been opened"},{"code":6045,"name":"BundledPositionAlreadyClosed","msg":"Position has already been closed"},{"code":6046,"name":"PositionBundleNotDeletable","msg":"Unable to delete PositionBundle with open positions"},{"code":6047,"name":"UnsupportedTokenMint","msg":"Token mint has unsupported attributes"},{"code":6048,"name":"RemainingAccountsInvalidSlice","msg":"Invalid remaining accounts"},{"code":6049,"name":"RemainingAccountsInsufficient","msg":"Insufficient remaining accounts"},{"code":6050,"name":"NoExtraAccountsForTransferHook","msg":"Unable to call transfer hook without extra accounts"},{"code":6051,"name":"IntermediateTokenAmountMismatch","msg":"Output and input amount mismatch"},{"code":6052,"name":"TransferFeeCalculationError","msg":"Transfer fee calculation failed"},{"code":6053,"name":"RemainingAccountsDuplicatedAccountsType","msg":"Same accounts type is provided more than once"},{"code":6054,"name":"FullRangeOnlyPool","msg":"This whirlpool only supports full-range positions"},{"code":6055,"name":"TooManySupplementalTickArrays","msg":"Too many supplemental tick arrays provided"},{"code":6056,"name":"DifferentWhirlpoolTickArrayAccount","msg":"TickArray account for different whirlpool provided"},{"code":6057,"name":"PartialFillError","msg":"Trade resulted in partial fill"},{"code":6058,"name":"PositionNotLockable","msg":"Position is not lockable"},{"code":6059,"name":"OperationNotAllowedOnLockedPosition","msg":"Operation not allowed on locked position"},{"code":6060,"name":"SameTickRangeNotAllowed","msg":"Cannot reset position range with same tick range"},{"code":6061,"name":"InvalidAdaptiveFeeConstants","msg":"Invalid adaptive fee constants"},{"code":6062,"name":"InvalidFeeTierIndex","msg":"Invalid fee tier index"},{"code":6063,"name":"InvalidTradeEnableTimestamp","msg":"Invalid trade enable timestamp"},{"code":6064,"name":"TradeIsNotEnabled","msg":"Trade is not enabled yet"},{"code":6065,"name":"RentCalculationError","msg":"Rent calculation error"},{"code":6066,"name":"FeatureIsNotEnabled","msg":"Feature is not enabled"},{"code":6067,"name":"PositionWithTokenExtensionsRequired","msg":"This whirlpool only supports open_position_with_token_extensions instruction"},{"code":6068,"name":"AdaptiveFeeConstantsUnchanged","msg":"Provided adaptive fee constants are unchanged"},{"code":6069,"name":"PriceSlippageOutOfBounds","msg":"Price outside slippage bounds"}],"types":[{"name":"AccountsType","type":{"kind":"enum","variants":[{"name":"TransferHookA"},{"name":"TransferHookB"},{"name":"TransferHookReward"},{"name":"TransferHookInput"},{"name":"TransferHookIntermediate"},{"name":"TransferHookOutput"},{"name":"SupplementalTickArrays"},{"name":"SupplementalTickArraysOne"},{"name":"SupplementalTickArraysTwo"},{"name":"TransferHookDepositA"},{"name":"TransferHookDepositB"},{"name":"TransferHookWithdrawalA"},{"name":"TransferHookWithdrawalB"}]}},{"name":"AdaptiveFeeConstants","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"adaptive_fee_control_factor","type":"u32"},{"name":"max_volatility_accumulator","type":"u32"},{"name":"tick_group_size","type":"u16"},{"name":"major_swap_threshold_ticks","type":"u16"},{"name":"reserved","type":{"array":["u8",16]}}]}},{"name":"AdaptiveFeeTier","type":{"kind":"struct","fields":[{"name":"whirlpools_config","type":"pubkey"},{"name":"fee_tier_index","type":"u16"},{"name":"tick_spacing","type":"u16"},{"name":"initialize_pool_authority","type":"pubkey"},{"name":"delegated_fee_authority","type":"pubkey"},{"name":"default_base_fee_rate","type":"u16"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"adaptive_fee_control_factor","type":"u32"},{"name":"max_volatility_accumulator","type":"u32"},{"name":"tick_group_size","type":"u16"},{"name":"major_swap_threshold_ticks","type":"u16"}]}},{"name":"AdaptiveFeeVariables","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"last_reference_update_timestamp","type":"u64"},{"name":"last_major_swap_timestamp","type":"u64"},{"name":"volatility_reference","type":"u32"},{"name":"tick_group_index_reference","type":"i32"},{"name":"volatility_accumulator","type":"u32"},{"name":"reserved","type":{"array":["u8",16]}}]}},{"name":"ConfigFeatureFlag","type":{"kind":"enum","variants":[{"name":"TokenBadge","fields":["bool"]}]}},{"name":"DynamicTick","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Initialized","fields":[{"defined":{"name":"DynamicTickData"}}]}]}},{"name":"DynamicTickArray","type":{"kind":"struct","fields":[{"name":"start_tick_index","type":"i32"},{"name":"whirlpool","type":"pubkey"},{"name":"tick_bitmap","type":"u128"},{"name":"ticks","type":{"array":[{"defined":{"name":"DynamicTick"}},88]}}]}},{"name":"DynamicTickData","type":{"kind":"struct","fields":[{"name":"liquidity_net","type":"i128"},{"name":"liquidity_gross","type":"u128"},{"name":"fee_growth_outside_a","type":"u128"},{"name":"fee_growth_outside_b","type":"u128"},{"name":"reward_growths_outside","type":{"array":["u128",3]}}]}},{"name":"FeeTier","type":{"kind":"struct","fields":[{"name":"whirlpools_config","type":"pubkey"},{"name":"tick_spacing","type":"u16"},{"name":"default_fee_rate","type":"u16"}]}},{"name":"IncreaseLiquidityMethod","type":{"kind":"enum","variants":[{"name":"ByTokenAmounts","fields":[{"name":"token_max_a","type":"u64"},{"name":"token_max_b","type":"u64"},{"name":"min_sqrt_price","type":"u128"},{"name":"max_sqrt_price","type":"u128"}]}]}},{"name":"LiquidityDecreased","type":{"kind":"struct","fields":[{"name":"whirlpool","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"token_a_amount","type":"u64"},{"name":"token_b_amount","type":"u64"},{"name":"token_a_transfer_fee","type":"u64"},{"name":"token_b_transfer_fee","type":"u64"}]}},{"name":"LiquidityIncreased","type":{"kind":"struct","fields":[{"name":"whirlpool","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"token_a_amount","type":"u64"},{"name":"token_b_amount","type":"u64"},{"name":"token_a_transfer_fee","type":"u64"},{"name":"token_b_transfer_fee","type":"u64"}]}},{"name":"LiquidityRepositioned","type":{"kind":"struct","fields":[{"name":"whirlpool","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"existing_range_tick_lower_index","type":"i32"},{"name":"existing_range_tick_upper_index","type":"i32"},{"name":"new_range_tick_lower_index","type":"i32"},{"name":"new_range_tick_upper_index","type":"i32"},{"name":"existing_range_liquidity","type":"u128"},{"name":"new_range_liquidity","type":"u128"},{"name":"existing_range_token_a_amount","type":"u64"},{"name":"existing_range_token_b_amount","type":"u64"},{"name":"new_range_token_a_amount","type":"u64"},{"name":"new_range_token_b_amount","type":"u64"},{"name":"token_a_transfer_amount","type":"u64"},{"name":"token_a_transfer_fee","type":"u64"},{"name":"is_token_a_transfer_from_owner","type":"bool"},{"name":"token_b_transfer_amount","type":"u64"},{"name":"token_b_transfer_fee","type":"u64"},{"name":"is_token_b_transfer_from_owner","type":"bool"}]}},{"name":"LockConfig","type":{"kind":"struct","fields":[{"name":"position","type":"pubkey"},{"name":"position_owner","type":"pubkey"},{"name":"whirlpool","type":"pubkey"},{"name":"locked_timestamp","type":"u64"},{"name":"lock_type","type":{"defined":{"name":"LockTypeLabel"}}}]}},{"name":"LockType","type":{"kind":"enum","variants":[{"name":"Permanent"}]}},{"name":"LockTypeLabel","type":{"kind":"enum","variants":[{"name":"Permanent"}]}},{"name":"OpenPositionBumps","type":{"kind":"struct","fields":[{"name":"position_bump","type":"u8"}]}},{"name":"OpenPositionWithMetadataBumps","type":{"kind":"struct","fields":[{"name":"position_bump","type":"u8"},{"name":"metadata_bump","type":"u8"}]}},{"name":"Oracle","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"whirlpool","type":"pubkey"},{"name":"trade_enable_timestamp","type":"u64"},{"name":"adaptive_fee_constants","type":{"defined":{"name":"AdaptiveFeeConstants"}}},{"name":"adaptive_fee_variables","type":{"defined":{"name":"AdaptiveFeeVariables"}}},{"name":"reserved","type":{"array":["u8",128]}}]}},{"name":"PoolInitialized","type":{"kind":"struct","fields":[{"name":"whirlpool","type":"pubkey"},{"name":"whirlpools_config","type":"pubkey"},{"name":"token_mint_a","type":"pubkey"},{"name":"token_mint_b","type":"pubkey"},{"name":"tick_spacing","type":"u16"},{"name":"token_program_a","type":"pubkey"},{"name":"token_program_b","type":"pubkey"},{"name":"decimals_a","type":"u8"},{"name":"decimals_b","type":"u8"},{"name":"initial_sqrt_price","type":"u128"}]}},{"name":"Position","type":{"kind":"struct","fields":[{"name":"whirlpool","type":"pubkey"},{"name":"position_mint","type":"pubkey"},{"name":"liquidity","type":"u128"},{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"fee_growth_checkpoint_a","type":"u128"},{"name":"fee_owed_a","type":"u64"},{"name":"fee_growth_checkpoint_b","type":"u128"},{"name":"fee_owed_b","type":"u64"},{"name":"reward_infos","type":{"array":[{"defined":{"name":"PositionRewardInfo"}},3]}}]}},{"name":"PositionBundle","type":{"kind":"struct","fields":[{"name":"position_bundle_mint","type":"pubkey"},{"name":"position_bitmap","type":{"array":["u8",32]}}]}},{"name":"PositionOpened","type":{"kind":"struct","fields":[{"name":"whirlpool","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"}]}},{"name":"PositionRewardInfo","type":{"kind":"struct","fields":[{"name":"growth_inside_checkpoint","type":"u128"},{"name":"amount_owed","type":"u64"}]}},{"name":"RemainingAccountsInfo","type":{"kind":"struct","fields":[{"name":"slices","type":{"vec":{"defined":{"name":"RemainingAccountsSlice"}}}}]}},{"name":"RemainingAccountsSlice","type":{"kind":"struct","fields":[{"name":"accounts_type","type":{"defined":{"name":"AccountsType"}}},{"name":"length","type":"u8"}]}},{"name":"RepositionLiquidityMethod","type":{"kind":"enum","variants":[{"name":"ByLiquidity","fields":[{"name":"new_liquidity_amount","type":"u128"},{"name":"existing_range_token_min_a","type":"u64"},{"name":"existing_range_token_min_b","type":"u64"},{"name":"new_range_token_max_a","type":"u64"},{"name":"new_range_token_max_b","type":"u64"}]}]}},{"name":"Tick","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"initialized","type":"bool"},{"name":"liquidity_net","type":"i128"},{"name":"liquidity_gross","type":"u128"},{"name":"fee_growth_outside_a","type":"u128"},{"name":"fee_growth_outside_b","type":"u128"},{"name":"reward_growths_outside","type":{"array":["u128",3]}}]}},{"name":"TickArray","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"start_tick_index","type":"i32"},{"name":"ticks","type":{"array":[{"defined":{"name":"Tick"}},88]}},{"name":"whirlpool","type":"pubkey"}]}},{"name":"TokenBadge","type":{"kind":"struct","fields":[{"name":"whirlpools_config","type":"pubkey"},{"name":"token_mint","type":"pubkey"},{"name":"attribute_require_non_transferable_position","type":"bool"}]}},{"name":"TokenBadgeAttribute","type":{"kind":"enum","variants":[{"name":"RequireNonTransferablePosition","fields":["bool"]}]}},{"name":"Traded","type":{"kind":"struct","fields":[{"name":"whirlpool","type":"pubkey"},{"name":"a_to_b","type":"bool"},{"name":"pre_sqrt_price","type":"u128"},{"name":"post_sqrt_price","type":"u128"},{"name":"input_amount","type":"u64"},{"name":"output_amount","type":"u64"},{"name":"input_transfer_fee","type":"u64"},{"name":"output_transfer_fee","type":"u64"},{"name":"lp_fee","type":"u64"},{"name":"protocol_fee","type":"u64"}]}},{"name":"Whirlpool","type":{"kind":"struct","fields":[{"name":"whirlpools_config","type":"pubkey"},{"name":"whirlpool_bump","type":{"array":["u8",1]}},{"name":"tick_spacing","type":"u16"},{"name":"fee_tier_index_seed","type":{"array":["u8",2]}},{"name":"fee_rate","type":"u16"},{"name":"protocol_fee_rate","type":"u16"},{"name":"liquidity","type":"u128"},{"name":"sqrt_price","type":"u128"},{"name":"tick_current_index","type":"i32"},{"name":"protocol_fee_owed_a","type":"u64"},{"name":"protocol_fee_owed_b","type":"u64"},{"name":"token_mint_a","type":"pubkey"},{"name":"token_vault_a","type":"pubkey"},{"name":"fee_growth_global_a","type":"u128"},{"name":"token_mint_b","type":"pubkey"},{"name":"token_vault_b","type":"pubkey"},{"name":"fee_growth_global_b","type":"u128"},{"name":"reward_last_updated_timestamp","type":"u64"},{"name":"reward_infos","type":{"array":[{"defined":{"name":"WhirlpoolRewardInfo"}},3]}}]}},{"name":"WhirlpoolBumps","type":{"kind":"struct","fields":[{"name":"whirlpool_bump","type":"u8"}]}},{"name":"WhirlpoolRewardInfo","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"vault","type":"pubkey"},{"name":"extension","type":{"array":["u8",32]}},{"name":"emissions_per_second_x64","type":"u128"},{"name":"growth_global_x64","type":"u128"}]}},{"name":"WhirlpoolsConfig","type":{"kind":"struct","fields":[{"name":"fee_authority","type":"pubkey"},{"name":"collect_protocol_fees_authority","type":"pubkey"},{"name":"reward_emissions_super_authority","type":"pubkey"},{"name":"default_protocol_fee_rate","type":"u16"},{"name":"feature_flags","type":"u16"}]}},{"name":"WhirlpoolsConfigExtension","type":{"kind":"struct","fields":[{"name":"whirlpools_config","type":"pubkey"},{"name":"config_extension_authority","type":"pubkey"},{"name":"token_badge_authority","type":"pubkey"}]}}]} \ No newline at end of file diff --git a/idls/pancakeswap.HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq.json b/idls/pancakeswap.HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq.json new file mode 100644 index 0000000..ca82772 --- /dev/null +++ b/idls/pancakeswap.HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq.json @@ -0,0 +1 @@ +{"address":"HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq","metadata":{"name":"amm_v3","version":"0.1.0","spec":"0.1.0","description":"Anchor client and source for Pancake concentrated liquidity AMM"},"instructions":[{"name":"close_position","docs":["Close the user's position and NFT account. If the NFT mint belongs to token2022, it will also be closed and the funds returned to the NFT owner.","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[123,134,81,0,49,68,98,98],"accounts":[{"name":"nft_owner","docs":["The position nft owner"],"writable":true,"signer":true},{"name":"position_nft_mint","docs":["Mint address bound to the personal position."],"writable":true},{"name":"position_nft_account","docs":["User token account where position NFT be minted to"],"writable":true},{"name":"personal_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"system_program","docs":["System program to close the position state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Token/Token2022 program to close token/mint account"]}],"args":[]},{"name":"collect_fund_fee","docs":["Collect the fund fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[167,138,78,149,223,194,6,126],"accounts":[{"name":"owner","docs":["Only admin or fund_owner can collect fee now"],"signer":true},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores fund_owner"]},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_protocol_fee","docs":["Collect the protocol fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[136,136,252,221,194,66,126,89],"accounts":[{"name":"owner","docs":["Only admin or config owner can collect fee now"],"signer":true},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores owner"]},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_remaining_rewards","docs":["Collect remaining reward token for reward founder","","# Arguments","","* `ctx`- The context of accounts","* `reward_index` - the index to reward info",""],"discriminator":[18,237,166,197,34,16,213,144],"accounts":[{"name":"reward_funder","docs":["The founder who init reward info previously"],"signer":true},{"name":"funder_token_account","docs":["The funder's reward token account"],"writable":true},{"name":"pool_state","docs":["Set reward for this pool"],"writable":true},{"name":"reward_token_vault","docs":["Reward vault transfer remaining token to founder token account"]},{"name":"reward_vault_mint","docs":["The mint of reward token vault"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"create_amm_config","docs":["# Arguments","","* `ctx`- The accounts needed by instruction.","* `index` - The index of amm config, there may be multiple config.","* `tick_spacing` - The tickspacing binding with config, cannot be changed.","* `trade_fee_rate` - Trade fee rate, can be changed.","* `protocol_fee_rate` - The rate of protocol fee within trade fee.","* `fund_fee_rate` - The rate of fund fee within trade fee.",""],"discriminator":[137,52,237,212,215,117,108,104],"accounts":[{"name":"owner","docs":["Address to be set as protocol owner."],"writable":true,"signer":true,"address":"DmwXqqK5Zuj619au6q2Jx3TMr9ZV1837uxJcEwyvXVtV"},{"name":"amm_config","docs":["Initialize config state account to store protocol owner address and fee rates."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,109,109,95,99,111,110,102,105,103]},{"kind":"arg","path":"index"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"u16"},{"name":"tick_spacing","type":"u16"},{"name":"trade_fee_rate","type":"u32"},{"name":"protocol_fee_rate","type":"u32"},{"name":"fund_fee_rate","type":"u32"}]},{"name":"create_operation_account","docs":["Creates an operation account for the program","","# Arguments","","* `ctx`- The context of accounts",""],"discriminator":[63,87,148,33,109,35,8,104],"accounts":[{"name":"owner","docs":["Address to be set as operation account owner."],"writable":true,"signer":true,"address":"DmwXqqK5Zuj619au6q2Jx3TMr9ZV1837uxJcEwyvXVtV"},{"name":"operation_state","docs":["Initialize operation state account to store operation owner address and white list mint."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_permissionless_farm_switch","docs":["Creates a permissionless farm switch account for the program","","# Arguments","","* `ctx`- The context of accounts",""],"discriminator":[88,242,198,70,241,6,196,251],"accounts":[{"name":"owner","writable":true,"signer":true,"address":"DmwXqqK5Zuj619au6q2Jx3TMr9ZV1837uxJcEwyvXVtV"},{"name":"permissionless_farm_switch","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,101,114,109,105,115,115,105,111,110,108,101,115,115,95,102,97,114,109,95,115,119,105,116,99,104]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_pool","docs":["Creates a pool for the given token pair and the initial price","","# Arguments","","* `ctx`- The context of accounts","* `sqrt_price_x64` - the initial sqrt price (amount_token_1 / amount_token_0) of the pool as a Q64.64","Note: The open_time must be smaller than the current block_timestamp on chain."],"discriminator":[233,146,209,142,207,104,64,188],"accounts":[{"name":"pool_creator","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"pool_state","docs":["Initialize an account to store the pool state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"token_mint_0"},{"kind":"account","path":"token_mint_1"}]}},{"name":"token_mint_0","docs":["Token_0 mint, the key must be smaller then token_1 mint."]},{"name":"token_mint_1","docs":["Token_1 mint"]},{"name":"token_vault_0","docs":["Token_0 vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_0"}]}},{"name":"token_vault_1","docs":["Token_1 vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_1"}]}},{"name":"observation_state","docs":["Initialize an account to store oracle observations"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"tick_array_bitmap","docs":["Initialize an account to store if a tick array is initialized."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,116,105,99,107,95,97,114,114,97,121,95,98,105,116,109,97,112,95,101,120,116,101,110,115,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"token_program_0","docs":["Spl token program or token program 2022"]},{"name":"token_program_1","docs":["Spl token program or token program 2022"]},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"sqrt_price_x64","type":"u128"},{"name":"open_time","type":"u64"}]},{"name":"create_support_mint_associated","docs":["Create support token22 mint account which can create pool and send rewards with ignoring the not support extensions."],"discriminator":[17,251,65,92,136,242,14,169],"accounts":[{"name":"owner","docs":["Address to be set as protocol owner."],"writable":true,"signer":true,"address":"DmwXqqK5Zuj619au6q2Jx3TMr9ZV1837uxJcEwyvXVtV"},{"name":"token_mint","docs":["Support token mint"]},{"name":"support_mint_associated","docs":["Initialize support mint state account to store support mint address and bump."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,117,112,112,111,114,116,95,109,105,110,116]},{"kind":"account","path":"token_mint"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"decrease_liquidity","docs":["#[deprecated(note = \"Use `decrease_liquidity_v2` instead.\")]","Decreases liquidity for an existing position","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The amount by which liquidity will be decreased","* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity","* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity",""],"discriminator":[160,38,208,111,104,91,44,1],"accounts":[{"name":"nft_owner","docs":["The position owner or delegated authority"],"signer":true},{"name":"nft_account","docs":["The token account for the tokenized position"]},{"name":"personal_position","docs":["Decrease liquidity for this position"],"writable":true},{"name":"pool_state","writable":true},{"name":"protocol_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"personal_position.tick_lower_index","account":"PersonalPositionState"},{"kind":"account","path":"personal_position.tick_upper_index","account":"PersonalPositionState"}]}},{"name":"token_vault_0","docs":["Token_0 vault"],"writable":true},{"name":"token_vault_1","docs":["Token_1 vault"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"recipient_token_account_0","docs":["The destination token account for receive amount_0"],"writable":true},{"name":"recipient_token_account_1","docs":["The destination token account for receive amount_1"],"writable":true},{"name":"token_program","docs":["SPL program to transfer out tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_min","type":"u64"},{"name":"amount_1_min","type":"u64"}]},{"name":"decrease_liquidity_v2","docs":["Decreases liquidity for an existing position, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The amount by which liquidity will be decreased","* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity","* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity",""],"discriminator":[58,127,188,62,79,82,196,96],"accounts":[{"name":"nft_owner","docs":["The position owner or delegated authority"],"signer":true},{"name":"nft_account","docs":["The token account for the tokenized position"]},{"name":"personal_position","docs":["Decrease liquidity for this position"],"writable":true},{"name":"pool_state","writable":true},{"name":"protocol_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"personal_position.tick_lower_index","account":"PersonalPositionState"},{"kind":"account","path":"personal_position.tick_upper_index","account":"PersonalPositionState"}]}},{"name":"token_vault_0","docs":["Token_0 vault"],"writable":true},{"name":"token_vault_1","docs":["Token_1 vault"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"recipient_token_account_0","docs":["The destination token account for receive amount_0"],"writable":true},{"name":"recipient_token_account_1","docs":["The destination token account for receive amount_1"],"writable":true},{"name":"token_program","docs":["SPL program to transfer out tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_min","type":"u64"},{"name":"amount_1_min","type":"u64"}]},{"name":"increase_liquidity","docs":["#[deprecated(note = \"Use `increase_liquidity_v2` instead.\")]","Increases liquidity for an existing position, with amount paid by `payer`","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The desired liquidity to be added, can't be zero","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check",""],"discriminator":[46,156,243,118,13,205,251,178],"accounts":[{"name":"nft_owner","docs":["Pays to mint the position"],"signer":true},{"name":"nft_account","docs":["The token account for nft"]},{"name":"pool_state","writable":true},{"name":"protocol_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"personal_position.tick_lower_index","account":"PersonalPositionState"},{"kind":"account","path":"personal_position.tick_upper_index","account":"PersonalPositionState"}]}},{"name":"personal_position","docs":["Increase liquidity for this position"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"token_account_0","docs":["The payer's token account for token_0"],"writable":true},{"name":"token_account_1","docs":["The token account spending token_1 to mint the position"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"}]},{"name":"increase_liquidity_v2","docs":["Increases liquidity for an existing position, with amount paid by `payer`, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The desired liquidity to be added, if zero, calculate liquidity base amount_0 or amount_1 according base_flag","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `base_flag` - must be specified if liquidity is zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[133,29,89,223,69,238,176,10],"accounts":[{"name":"nft_owner","docs":["Pays to mint the position"],"signer":true},{"name":"nft_account","docs":["The token account for nft"]},{"name":"pool_state","writable":true},{"name":"protocol_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"personal_position.tick_lower_index","account":"PersonalPositionState"},{"kind":"account","path":"personal_position.tick_upper_index","account":"PersonalPositionState"}]}},{"name":"personal_position","docs":["Increase liquidity for this position"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"token_account_0","docs":["The payer's token account for token_0"],"writable":true},{"name":"token_account_1","docs":["The token account spending token_1 to mint the position"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"initialize_reward","docs":["Initialize a reward info for a given pool and reward index","","# Arguments","","* `ctx`- The context of accounts","* `reward_index` - the index to reward info","* `open_time` - reward open timestamp","* `end_time` - reward end timestamp","* `emissions_per_second_x64` - Token reward per second are earned per unit of liquidity.",""],"discriminator":[95,135,192,196,242,129,230,68],"accounts":[{"name":"reward_funder","docs":["The founder deposit reward token to vault"],"writable":true,"signer":true},{"name":"funder_token_account","writable":true},{"name":"amm_config","docs":["For check the reward_funder authority"]},{"name":"pool_state","docs":["Set reward for this pool"],"writable":true},{"name":"operation_state","docs":["load info from the account to judge reward permission"],"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"permissionless_farm_switch","pda":{"seeds":[{"kind":"const","value":[112,101,114,109,105,115,115,105,111,110,108,101,115,115,95,102,97,114,109,95,115,119,105,116,99,104]}]}},{"name":"reward_token_mint","docs":["Reward mint"]},{"name":"reward_token_vault","docs":["A pda, reward vault"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,114,101,119,97,114,100,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"reward_token_mint"}]}},{"name":"reward_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"param","type":{"defined":{"name":"InitializeRewardParam"}}}]},{"name":"open_position","docs":["#[deprecated(note = \"Use `open_position_with_token22_nft` instead.\")]","Creates a new position wrapped in a NFT","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which include tick low","* `tick_array_upper_start_index` - The start index of tick array which include tick upper","* `liquidity` - The liquidity to be added","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check",""],"discriminator":[135,128,47,77,15,152,240,49],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["Token account where position NFT will be minted","This account created in the contract by cpi to avoid large stack variables"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"position_nft_owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_nft_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","docs":["To store metaplex metadata"],"writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position","docs":["Store the information of market marking in range"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_lower_index"},{"kind":"arg","path":"tick_upper_index"}]}},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_program","docs":["Program to create NFT metadata"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"}]},{"name":"open_position_v2","docs":["#[deprecated(note = \"Use `open_position_with_token22_nft` instead.\")]","Creates a new position wrapped in a NFT, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which include tick low","* `tick_array_upper_start_index` - The start index of tick array which include tick upper","* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `with_metadata` - The flag indicating whether to create NFT mint metadata","* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[77,184,74,214,112,86,241,199],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["Token account where position NFT will be minted"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"position_nft_owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_nft_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","docs":["To store metaplex metadata"],"writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position","docs":["Store the information of market marking in range"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_lower_index"},{"kind":"arg","path":"tick_upper_index"}]}},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_program","docs":["Program to create NFT metadata"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"token_program_2022","docs":["Program to create mint account and mint tokens"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"with_metadata","type":"bool"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"open_position_with_token22_nft","docs":["Creates a new position wrapped in a Token2022 NFT without relying on metadata_program and metadata_account, reduce the cost for user to create a personal position.","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which include tick low","* `tick_array_upper_start_index` - The start index of tick array which include tick upper","* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `with_metadata` - The flag indicating whether to create NFT mint metadata","* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[77,255,174,82,125,29,201,46],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address, initialize in contract"],"writable":true,"signer":true},{"name":"position_nft_account","writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position","docs":["Store the information of market marking in range"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_lower_index"},{"kind":"arg","path":"tick_upper_index"}]}},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to transfer for token account"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program_2022","docs":["Program to create NFT mint/token account and transfer for token22 account"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"with_metadata","type":"bool"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"set_reward_params","docs":["Reset reward param, start a new reward cycle or extend the current cycle.","","# Arguments","","* `ctx` - The context of accounts","* `reward_index` - The index of reward token in the pool.","* `emissions_per_second_x64` - The per second emission reward, when extend the current cycle,","new value can't be less than old value","* `open_time` - reward open timestamp, must be set when starting a new cycle","* `end_time` - reward end timestamp",""],"discriminator":[112,52,167,75,32,201,211,137],"accounts":[{"name":"authority","docs":["Address to be set as protocol owner. It pays to create factory state account."],"signer":true},{"name":"amm_config"},{"name":"pool_state","writable":true},{"name":"operation_state","docs":["load info from the account to judge reward permission"],"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"token_program","docs":["Token program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"reward_index","type":"u8"},{"name":"emissions_per_second_x64","type":"u128"},{"name":"open_time","type":"u64"},{"name":"end_time","type":"u64"}]},{"name":"swap","docs":["#[deprecated(note = \"Use `swap_v2` instead.\")]","Swaps one token for as much as possible of another token across a single pool","","# Arguments","","* `ctx` - The context of accounts","* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)","* `other_amount_threshold` - For slippage check","* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot","* `is_base_input` - swap base input or swap base output",""],"discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"tick_array","writable":true}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit_x64","type":"u128"},{"name":"is_base_input","type":"bool"}]},{"name":"swap_router_base_in","docs":["Swap token for as much as possible of another token across the path provided, base input","","# Arguments","","* `ctx` - The context of accounts","* `amount_in` - Token amount to be swapped in","* `amount_out_minimum` - Panic if output amount is below minimum amount. For slippage.",""],"discriminator":[69,125,115,218,245,186,242,196],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"input_token_account","docs":["The token account that pays input tokens for the swap"],"writable":true},{"name":"input_token_mint","docs":["The mint of input token"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["SPL program 2022 for token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["Memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"amount_in","type":"u64"},{"name":"amount_out_minimum","type":"u64"}]},{"name":"swap_v2","docs":["Swaps one token for as much as possible of another token across a single pool, support token program 2022","","# Arguments","","* `ctx` - The context of accounts","* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)","* `other_amount_threshold` - For slippage check","* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot","* `is_base_input` - swap base input or swap base output",""],"discriminator":[43,4,237,11,26,201,30,98],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["SPL program 2022 for token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["Memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"input_vault_mint","docs":["The mint of token vault 0"]},{"name":"output_vault_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit_x64","type":"u128"},{"name":"is_base_input","type":"bool"}]},{"name":"toggle_permissionless_farm_switch","docs":["Toggle the permissionless farm switch account","","# Arguments","","* `ctx`- The context of accounts","* `is_on`- Is the permissionless farm allowed or not",""],"discriminator":[150,112,54,233,238,161,7,86],"accounts":[{"name":"owner","writable":true,"signer":true,"address":"DmwXqqK5Zuj619au6q2Jx3TMr9ZV1837uxJcEwyvXVtV"},{"name":"permissionless_farm_switch","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,101,114,109,105,115,115,105,111,110,108,101,115,115,95,102,97,114,109,95,115,119,105,116,99,104]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"is_on","type":"bool"}]},{"name":"transfer_reward_owner","docs":["Transfer reward owner","","# Arguments","","* `ctx`- The context of accounts","* `new_owner`- new owner pubkey",""],"discriminator":[7,22,12,83,242,43,48,121],"accounts":[{"name":"authority","docs":["Address to be set as operation account owner."],"signer":true,"address":"DmwXqqK5Zuj619au6q2Jx3TMr9ZV1837uxJcEwyvXVtV"},{"name":"pool_state","writable":true}],"args":[{"name":"new_owner","type":"pubkey"}]},{"name":"update_amm_config","docs":["Updates the owner of the amm config","Must be called by the current owner or admin","","# Arguments","","* `ctx`- The context of accounts","* `trade_fee_rate`- The new trade fee rate of amm config, be set when `param` is 0","* `protocol_fee_rate`- The new protocol fee rate of amm config, be set when `param` is 1","* `fund_fee_rate`- The new fund fee rate of amm config, be set when `param` is 2","* `new_owner`- The config's new owner, be set when `param` is 3","* `new_fund_owner`- The config's new fund owner, be set when `param` is 4","* `param`- The value can be 0 | 1 | 2 | 3 | 4, otherwise will report a error",""],"discriminator":[49,60,174,136,154,28,116,200],"accounts":[{"name":"owner","docs":["The amm config owner or admin"],"signer":true,"address":"DmwXqqK5Zuj619au6q2Jx3TMr9ZV1837uxJcEwyvXVtV"},{"name":"amm_config","docs":["Amm config account to be changed"],"writable":true}],"args":[{"name":"param","type":"u8"},{"name":"value","type":"u32"}]},{"name":"update_operation_account","docs":["Update the operation account","","# Arguments","","* `ctx`- The context of accounts","* `param`- The value can be 0 | 1 | 2 | 3, otherwise will report a error","* `keys`- update operation owner when the `param` is 0","remove operation owner when the `param` is 1","update whitelist mint when the `param` is 2","remove whitelist mint when the `param` is 3",""],"discriminator":[127,70,119,40,188,227,61,7],"accounts":[{"name":"owner","docs":["Address to be set as operation account owner."],"signer":true,"address":"DmwXqqK5Zuj619au6q2Jx3TMr9ZV1837uxJcEwyvXVtV"},{"name":"operation_state","docs":["Initialize operation state account to store operation owner address and white list mint."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"param","type":"u8"},{"name":"keys","type":{"vec":"pubkey"}}]},{"name":"update_pool_status","docs":["Update pool status for given value","","# Arguments","","* `ctx`- The context of accounts","* `status` - The value of status",""],"discriminator":[130,87,108,6,46,224,117,123],"accounts":[{"name":"authority","signer":true,"address":"DmwXqqK5Zuj619au6q2Jx3TMr9ZV1837uxJcEwyvXVtV"},{"name":"pool_state","writable":true}],"args":[{"name":"status","type":"u8"}]},{"name":"update_reward_infos","docs":["Update rewards info of the given pool, can be called for everyone","","# Arguments","","* `ctx`- The context of accounts",""],"discriminator":[163,172,224,52,11,154,106,223],"accounts":[{"name":"pool_state","docs":["The liquidity pool for which reward info to update"],"writable":true}],"args":[]}],"accounts":[{"name":"AmmConfig","discriminator":[218,244,33,104,203,203,43,111]},{"name":"ObservationState","discriminator":[122,174,197,53,129,9,165,132]},{"name":"OperationState","discriminator":[19,236,58,237,81,222,183,252]},{"name":"PermissionlessFarmSwitch","discriminator":[37,92,147,78,250,100,147,137]},{"name":"PersonalPositionState","discriminator":[70,111,150,126,230,15,25,117]},{"name":"PoolState","discriminator":[247,237,227,245,215,195,222,70]},{"name":"ProtocolPositionState","discriminator":[100,226,145,99,146,218,160,106]},{"name":"SupportMintAssociated","discriminator":[134,40,183,79,12,112,162,53]},{"name":"TickArrayBitmapExtension","discriminator":[60,150,36,219,97,128,139,153]},{"name":"TickArrayState","discriminator":[192,155,85,205,49,249,129,42]}],"events":[{"name":"CollectPersonalFeeEvent","discriminator":[166,174,105,192,81,161,83,105]},{"name":"CollectProtocolFeeEvent","discriminator":[206,87,17,79,45,41,213,61]},{"name":"ConfigChangeEvent","discriminator":[247,189,7,119,106,112,95,151]},{"name":"CreatePersonalPositionEvent","discriminator":[100,30,87,249,196,223,154,206]},{"name":"DecreaseLiquidityEvent","discriminator":[58,222,86,58,68,50,85,56]},{"name":"IncreaseLiquidityEvent","discriminator":[49,79,105,212,32,34,30,84]},{"name":"LiquidityCalculateEvent","discriminator":[237,112,148,230,57,84,180,162]},{"name":"LiquidityChangeEvent","discriminator":[126,240,175,206,158,88,153,107]},{"name":"PoolCreatedEvent","discriminator":[25,94,75,47,112,99,53,63]},{"name":"SwapEvent","discriminator":[64,198,205,232,38,8,113,226]},{"name":"UpdateRewardInfosEvent","discriminator":[109,127,186,78,114,65,37,236]}],"errors":[{"code":6000,"name":"LOK","msg":"LOK"},{"code":6001,"name":"NotApproved","msg":"Not approved"},{"code":6002,"name":"InvalidUpdateConfigFlag","msg":"invalid update amm config flag"},{"code":6003,"name":"AccountLack","msg":"Account lack"},{"code":6004,"name":"ClosePositionErr","msg":"Remove liquitity, collect fees owed and reward then you can close position account"},{"code":6005,"name":"ZeroMintAmount","msg":"Minting amount should be greater than 0"},{"code":6006,"name":"InvalidTickIndex","msg":"Tick out of range"},{"code":6007,"name":"TickInvalidOrder","msg":"The lower tick must be below the upper tick"},{"code":6008,"name":"TickLowerOverflow","msg":"The tick must be greater, or equal to the minimum tick(-443636)"},{"code":6009,"name":"TickUpperOverflow","msg":"The tick must be lesser than, or equal to the maximum tick(443636)"},{"code":6010,"name":"TickAndSpacingNotMatch","msg":"tick % tick_spacing must be zero"},{"code":6011,"name":"InvalidTickArray","msg":"Invalid tick array account"},{"code":6012,"name":"InvalidTickArrayBoundary","msg":"Invalid tick array boundary"},{"code":6013,"name":"SqrtPriceLimitOverflow","msg":"Square root price limit overflow"},{"code":6014,"name":"SqrtPriceX64","msg":"sqrt_price_x64 out of range"},{"code":6015,"name":"LiquiditySubValueErr","msg":"Liquidity sub delta L must be smaller than before"},{"code":6016,"name":"LiquidityAddValueErr","msg":"Liquidity add delta L must be greater, or equal to before"},{"code":6017,"name":"InvalidLiquidity","msg":"Invalid liquidity when update position"},{"code":6018,"name":"ForbidBothZeroForSupplyLiquidity","msg":"Both token amount must not be zero while supply liquidity"},{"code":6019,"name":"LiquidityInsufficient","msg":"Liquidity insufficient"},{"code":6020,"name":"TransactionTooOld","msg":"Transaction too old"},{"code":6021,"name":"PriceSlippageCheck","msg":"Price slippage check"},{"code":6022,"name":"TooLittleOutputReceived","msg":"Too little output received"},{"code":6023,"name":"TooMuchInputPaid","msg":"Too much input paid"},{"code":6024,"name":"ZeroAmountSpecified","msg":"Swap special amount can not be zero"},{"code":6025,"name":"InvalidInputPoolVault","msg":"Input pool vault is invalid"},{"code":6026,"name":"TooSmallInputOrOutputAmount","msg":"Swap input or output amount is too small"},{"code":6027,"name":"NotEnoughTickArrayAccount","msg":"Not enought tick array account"},{"code":6028,"name":"InvalidFirstTickArrayAccount","msg":"Invalid first tick array account"},{"code":6029,"name":"InvalidRewardIndex","msg":"Invalid reward index"},{"code":6030,"name":"FullRewardInfo","msg":"The init reward token reach to the max"},{"code":6031,"name":"RewardTokenAlreadyInUse","msg":"The init reward token already in use"},{"code":6032,"name":"ExceptRewardMint","msg":"The reward tokens must contain one of pool vault mint except the last reward"},{"code":6033,"name":"InvalidRewardInitParam","msg":"Invalid reward init param"},{"code":6034,"name":"InvalidRewardDesiredAmount","msg":"Invalid collect reward desired amount"},{"code":6035,"name":"InvalidRewardInputAccountNumber","msg":"Invalid collect reward input account number"},{"code":6036,"name":"InvalidRewardPeriod","msg":"Invalid reward period"},{"code":6037,"name":"NotApproveUpdateRewardEmissiones","msg":"Modification of emissiones is allowed within 72 hours from the end of the previous cycle"},{"code":6038,"name":"UnInitializedRewardInfo","msg":"uninitialized reward info"},{"code":6039,"name":"NotSupportMint","msg":"Not support token_2022 mint extension"},{"code":6040,"name":"MissingTickArrayBitmapExtensionAccount","msg":"Missing tickarray bitmap extension account"},{"code":6041,"name":"InsufficientLiquidityForDirection","msg":"Insufficient liquidity for this direction"},{"code":6042,"name":"MaxTokenOverflow","msg":"Max token overflow"},{"code":6043,"name":"CalculateOverflow","msg":"Calculate overflow"},{"code":6044,"name":"TransferFeeCalculateNotMatch","msg":"TransferFee calculate not match"}],"types":[{"name":"AmmConfig","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"index","type":"u16"},{"name":"owner","docs":["Address of the protocol owner"],"type":"pubkey"},{"name":"protocol_fee_rate","docs":["The protocol fee"],"type":"u32"},{"name":"trade_fee_rate","docs":["The trade fee, denominated in hundredths of a bip (10^-6)"],"type":"u32"},{"name":"tick_spacing","docs":["The tick spacing"],"type":"u16"},{"name":"fund_fee_rate","docs":["The fund fee, denominated in hundredths of a bip (10^-6)"],"type":"u32"},{"name":"padding_u32","type":"u32"},{"name":"fund_owner","type":"pubkey"},{"name":"padding","type":{"array":["u64",3]}}]}},{"name":"CollectPersonalFeeEvent","docs":["Emitted when tokens are collected for a position"],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which underlying tokens were collected"],"type":"pubkey"},{"name":"recipient_token_account_0","docs":["The token account that received the collected token_0 tokens"],"type":"pubkey"},{"name":"recipient_token_account_1","docs":["The token account that received the collected token_1 tokens"],"type":"pubkey"},{"name":"amount_0","docs":["The amount of token_0 owed to the position that was collected"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_1 owed to the position that was collected"],"type":"u64"}]}},{"name":"CollectProtocolFeeEvent","docs":["Emitted when the collected protocol fees are withdrawn by the factory owner"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool whose protocol fee is collected"],"type":"pubkey"},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"type":"pubkey"},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"type":"pubkey"},{"name":"amount_0","docs":["The amount of token_0 protocol fees that is withdrawn"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_0 protocol fees that is withdrawn"],"type":"u64"}]}},{"name":"ConfigChangeEvent","docs":["Emitted when create or update a config"],"type":{"kind":"struct","fields":[{"name":"index","type":"u16"},{"name":"owner","type":"pubkey"},{"name":"protocol_fee_rate","type":"u32"},{"name":"trade_fee_rate","type":"u32"},{"name":"tick_spacing","type":"u16"},{"name":"fund_fee_rate","type":"u32"},{"name":"fund_owner","type":"pubkey"}]}},{"name":"CreatePersonalPositionEvent","docs":["Emitted when create a new position"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for which liquidity was added"],"type":"pubkey"},{"name":"minter","docs":["The address that create the position"],"type":"pubkey"},{"name":"nft_owner","docs":["The owner of the position and recipient of any minted liquidity"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity minted to the position range"],"type":"u128"},{"name":"deposit_amount_0","docs":["The amount of token_0 was deposit for the liquidity"],"type":"u64"},{"name":"deposit_amount_1","docs":["The amount of token_1 was deposit for the liquidity"],"type":"u64"},{"name":"deposit_amount_0_transfer_fee","docs":["The token transfer fee for deposit_amount_0"],"type":"u64"},{"name":"deposit_amount_1_transfer_fee","docs":["The token transfer fee for deposit_amount_1"],"type":"u64"}]}},{"name":"DecreaseLiquidityEvent","docs":["Emitted when liquidity is decreased."],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which liquidity was decreased"],"type":"pubkey"},{"name":"liquidity","docs":["The amount by which liquidity for the position was decreased"],"type":"u128"},{"name":"decrease_amount_0","docs":["The amount of token_0 that was paid for the decrease in liquidity"],"type":"u64"},{"name":"decrease_amount_1","docs":["The amount of token_1 that was paid for the decrease in liquidity"],"type":"u64"},{"name":"fee_amount_0","type":"u64"},{"name":"fee_amount_1","docs":["The amount of token_1 fee"],"type":"u64"},{"name":"reward_amounts","docs":["The amount of rewards"],"type":{"array":["u64",3]}},{"name":"transfer_fee_0","docs":["The amount of token_0 transfer fee"],"type":"u64"},{"name":"transfer_fee_1","docs":["The amount of token_1 transfer fee"],"type":"u64"}]}},{"name":"IncreaseLiquidityEvent","docs":["Emitted when liquidity is increased."],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which liquidity was increased"],"type":"pubkey"},{"name":"liquidity","docs":["The amount by which liquidity for the NFT position was increased"],"type":"u128"},{"name":"amount_0","docs":["The amount of token_0 that was paid for the increase in liquidity"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_1 that was paid for the increase in liquidity"],"type":"u64"},{"name":"amount_0_transfer_fee","docs":["The token transfer fee for amount_0"],"type":"u64"},{"name":"amount_1_transfer_fee","docs":["The token transfer fee for amount_1"],"type":"u64"}]}},{"name":"InitializeRewardParam","type":{"kind":"struct","fields":[{"name":"open_time","docs":["Reward open time"],"type":"u64"},{"name":"end_time","docs":["Reward end time"],"type":"u64"},{"name":"emissions_per_second_x64","docs":["Token reward per second are earned per unit of liquidity"],"type":"u128"}]}},{"name":"LiquidityCalculateEvent","docs":["Emitted when liquidity decreased or increase."],"type":{"kind":"struct","fields":[{"name":"pool_liquidity","docs":["The pool liquidity before decrease or increase"],"type":"u128"},{"name":"pool_sqrt_price_x64","docs":["The pool price when decrease or increase in liquidity"],"type":"u128"},{"name":"pool_tick","docs":["The pool tick when decrease or increase in liquidity"],"type":"i32"},{"name":"calc_amount_0","docs":["The amount of token_0 that was calculated for the decrease or increase in liquidity"],"type":"u64"},{"name":"calc_amount_1","docs":["The amount of token_1 that was calculated for the decrease or increase in liquidity"],"type":"u64"},{"name":"trade_fee_owed_0","type":"u64"},{"name":"trade_fee_owed_1","docs":["The amount of token_1 fee"],"type":"u64"},{"name":"transfer_fee_0","docs":["The amount of token_0 transfer fee without trade_fee_amount_0"],"type":"u64"},{"name":"transfer_fee_1","docs":["The amount of token_1 transfer fee without trade_fee_amount_0"],"type":"u64"}]}},{"name":"LiquidityChangeEvent","docs":["Emitted pool liquidity change when increase and decrease liquidity"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for swap"],"type":"pubkey"},{"name":"tick","docs":["The tick of the pool"],"type":"i32"},{"name":"tick_lower","docs":["The tick lower of position"],"type":"i32"},{"name":"tick_upper","docs":["The tick lower of position"],"type":"i32"},{"name":"liquidity_before","docs":["The liquidity of the pool before liquidity change"],"type":"u128"},{"name":"liquidity_after","docs":["The liquidity of the pool after liquidity change"],"type":"u128"}]}},{"name":"Observation","docs":["The element of observations in ObservationState"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"block_timestamp","docs":["The block timestamp of the observation"],"type":"u32"},{"name":"tick_cumulative","docs":["the cumulative of tick during the duration time"],"type":"i64"},{"name":"padding","docs":["padding for feature update"],"type":{"array":["u64",4]}}]}},{"name":"ObservationState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"initialized","docs":["Whether the ObservationState is initialized"],"type":"bool"},{"name":"recent_epoch","docs":["recent update epoch"],"type":"u64"},{"name":"observation_index","docs":["the most-recently updated index of the observations array"],"type":"u16"},{"name":"pool_id","docs":["belongs to which pool"],"type":"pubkey"},{"name":"observations","docs":["observation array"],"type":{"array":[{"defined":{"name":"Observation"}},100]}},{"name":"padding","docs":["padding for feature update"],"type":{"array":["u64",4]}}]}},{"name":"OperationState","docs":["Holds the current owner of the factory"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"operation_owners","docs":["Address of the operation owner"],"type":{"array":["pubkey",10]}},{"name":"whitelist_mints","docs":["The mint address of whitelist to emit reward"],"type":{"array":["pubkey",100]}}]}},{"name":"PermissionlessFarmSwitch","type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"is_on","docs":["Is the permissionless farm allowed or not"],"type":"bool"}]}},{"name":"PersonalPositionState","type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":{"array":["u8",1]}},{"name":"nft_mint","docs":["Mint address of the tokenized position"],"type":"pubkey"},{"name":"pool_id","docs":["The ID of the pool with which this token is connected"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"fee_growth_inside_0_last_x64","docs":["The token_0 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"fee_growth_inside_1_last_x64","docs":["The token_1 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"token_fees_owed_0","docs":["The fees owed to the position owner in token_0, as of the last computation"],"type":"u64"},{"name":"token_fees_owed_1","docs":["The fees owed to the position owner in token_1, as of the last computation"],"type":"u64"},{"name":"reward_infos","type":{"array":[{"defined":{"name":"PositionRewardInfo"}},3]}},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u64",7]}}]}},{"name":"PoolCreatedEvent","docs":["Emitted when a pool is created and initialized with a starting price",""],"type":{"kind":"struct","fields":[{"name":"token_mint_0","docs":["The first token of the pool by address sort order"],"type":"pubkey"},{"name":"token_mint_1","docs":["The second token of the pool by address sort order"],"type":"pubkey"},{"name":"tick_spacing","docs":["The minimum number of ticks between initialized ticks"],"type":"u16"},{"name":"pool_state","docs":["The address of the created pool"],"type":"pubkey"},{"name":"sqrt_price_x64","docs":["The initial sqrt price of the pool, as a Q64.64"],"type":"u128"},{"name":"tick","docs":["The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool"],"type":"i32"},{"name":"token_vault_0","docs":["Vault of token_0"],"type":"pubkey"},{"name":"token_vault_1","docs":["Vault of token_1"],"type":"pubkey"}]}},{"name":"PoolState","docs":["The pool state","","PDA of `[POOL_SEED, config, token_mint_0, token_mint_1]`",""],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":{"array":["u8",1]}},{"name":"amm_config","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"token_mint_0","docs":["Token pair of the pool, where token_mint_0 address < token_mint_1 address"],"type":"pubkey"},{"name":"token_mint_1","type":"pubkey"},{"name":"token_vault_0","docs":["Token pair vault"],"type":"pubkey"},{"name":"token_vault_1","type":"pubkey"},{"name":"observation_key","docs":["observation account key"],"type":"pubkey"},{"name":"mint_decimals_0","docs":["mint0 and mint1 decimals"],"type":"u8"},{"name":"mint_decimals_1","type":"u8"},{"name":"tick_spacing","docs":["The minimum number of ticks between initialized ticks"],"type":"u16"},{"name":"liquidity","docs":["The currently in range liquidity available to the pool."],"type":"u128"},{"name":"sqrt_price_x64","docs":["The current price of the pool as a sqrt(token_1/token_0) Q64.64 value"],"type":"u128"},{"name":"tick_current","docs":["The current tick of the pool, i.e. according to the last tick transition that was run."],"type":"i32"},{"name":"padding3","type":"u16"},{"name":"padding4","type":"u16"},{"name":"fee_growth_global_0_x64","docs":["The fee growth as a Q64.64 number, i.e. fees of token_0 and token_1 collected per","unit of liquidity for the entire life of the pool."],"type":"u128"},{"name":"fee_growth_global_1_x64","type":"u128"},{"name":"protocol_fees_token_0","docs":["The amounts of token_0 and token_1 that are owed to the protocol."],"type":"u64"},{"name":"protocol_fees_token_1","type":"u64"},{"name":"swap_in_amount_token_0","docs":["The amounts in and out of swap token_0 and token_1"],"type":"u128"},{"name":"swap_out_amount_token_1","type":"u128"},{"name":"swap_in_amount_token_1","type":"u128"},{"name":"swap_out_amount_token_0","type":"u128"},{"name":"status","docs":["Bitwise representation of the state of the pool","bit0, 1: disable open position and increase liquidity, 0: normal","bit1, 1: disable decrease liquidity, 0: normal","bit2, 1: disable collect fee, 0: normal","bit3, 1: disable collect reward, 0: normal","bit4, 1: disable swap, 0: normal"],"type":"u8"},{"name":"padding","docs":["Leave blank for future use"],"type":{"array":["u8",7]}},{"name":"reward_infos","type":{"array":[{"defined":{"name":"RewardInfo"}},3]}},{"name":"tick_array_bitmap","docs":["Packed initialized tick array state"],"type":{"array":["u64",16]}},{"name":"total_fees_token_0","docs":["except protocol_fee and fund_fee"],"type":"u64"},{"name":"total_fees_claimed_token_0","docs":["except protocol_fee and fund_fee"],"type":"u64"},{"name":"total_fees_token_1","type":"u64"},{"name":"total_fees_claimed_token_1","type":"u64"},{"name":"fund_fees_token_0","type":"u64"},{"name":"fund_fees_token_1","type":"u64"},{"name":"open_time","type":"u64"},{"name":"recent_epoch","type":"u64"},{"name":"padding1","type":{"array":["u64",24]}},{"name":"padding2","type":{"array":["u64",32]}}]}},{"name":"PositionRewardInfo","type":{"kind":"struct","fields":[{"name":"growth_inside_last_x64","type":"u128"},{"name":"reward_amount_owed","type":"u64"}]}},{"name":"ProtocolPositionState","docs":["Info stored for each user's position"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"pool_id","docs":["The ID of the pool with which this token is connected"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"fee_growth_inside_0_last_x64","docs":["The token_0 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"fee_growth_inside_1_last_x64","docs":["The token_1 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"token_fees_owed_0","docs":["The fees owed to the position owner in token_0"],"type":"u64"},{"name":"token_fees_owed_1","docs":["The fees owed to the position owner in token_1"],"type":"u64"},{"name":"reward_growth_inside","docs":["The reward growth per unit of liquidity as of the last update to liquidity"],"type":{"array":["u128",3]}},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u64",7]}}]}},{"name":"RewardInfo","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"reward_state","docs":["Reward state"],"type":"u8"},{"name":"open_time","docs":["Reward open time"],"type":"u64"},{"name":"end_time","docs":["Reward end time"],"type":"u64"},{"name":"last_update_time","docs":["Reward last update time"],"type":"u64"},{"name":"emissions_per_second_x64","docs":["Q64.64 number indicates how many tokens per second are earned per unit of liquidity."],"type":"u128"},{"name":"reward_total_emissioned","docs":["The total amount of reward emissioned"],"type":"u64"},{"name":"reward_claimed","docs":["The total amount of claimed reward"],"type":"u64"},{"name":"token_mint","docs":["Reward token mint."],"type":"pubkey"},{"name":"token_vault","docs":["Reward vault token account."],"type":"pubkey"},{"name":"authority","docs":["The owner that has permission to set reward param"],"type":"pubkey"},{"name":"reward_growth_global_x64","docs":["Q64.64 number that tracks the total tokens earned per unit of liquidity since the reward","emissions were turned on."],"type":"u128"}]}},{"name":"SupportMintAssociated","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"mint","docs":["Address of the supported token22 mint"],"type":"pubkey"},{"name":"padding","type":{"array":["u64",8]}}]}},{"name":"SwapEvent","docs":["Emitted by when a swap is performed for a pool"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for which token_0 and token_1 were swapped"],"type":"pubkey"},{"name":"sender","docs":["The address that initiated the swap call, and that received the callback"],"type":"pubkey"},{"name":"token_account_0","docs":["The payer token account in zero for one swaps, or the recipient token account","in one for zero swaps"],"type":"pubkey"},{"name":"token_account_1","docs":["The payer token account in one for zero swaps, or the recipient token account","in zero for one swaps"],"type":"pubkey"},{"name":"amount_0","docs":["The real delta amount of the token_0 of the pool or user"],"type":"u64"},{"name":"transfer_fee_0","docs":["The transfer fee charged by the withheld_amount of the token_0"],"type":"u64"},{"name":"amount_1","docs":["The real delta of the token_1 of the pool or user"],"type":"u64"},{"name":"transfer_fee_1","docs":["The transfer fee charged by the withheld_amount of the token_1"],"type":"u64"},{"name":"zero_for_one","docs":["if true, amount_0 is negtive and amount_1 is positive"],"type":"bool"},{"name":"sqrt_price_x64","docs":["The sqrt(price) of the pool after the swap, as a Q64.64"],"type":"u128"},{"name":"liquidity","docs":["The liquidity of the pool after the swap"],"type":"u128"},{"name":"tick","docs":["The log base 1.0001 of price of the pool after the swap"],"type":"i32"}]}},{"name":"TickArrayBitmapExtension","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"positive_tick_array_bitmap","docs":["Packed initialized tick array state for start_tick_index is positive"],"type":{"array":[{"array":["u64",8]},14]}},{"name":"negative_tick_array_bitmap","docs":["Packed initialized tick array state for start_tick_index is negitive"],"type":{"array":[{"array":["u64",8]},14]}}]}},{"name":"TickArrayState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"start_tick_index","type":"i32"},{"name":"ticks","type":{"array":[{"defined":{"name":"TickState"}},60]}},{"name":"initialized_tick_count","type":"u8"},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u8",107]}}]}},{"name":"TickState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"tick","type":"i32"},{"name":"liquidity_net","docs":["Amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left)"],"type":"i128"},{"name":"liquidity_gross","docs":["The total position liquidity that references this tick"],"type":"u128"},{"name":"fee_growth_outside_0_x64","docs":["Fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)","only has relative meaning, not absolute — the value depends on when the tick is initialized"],"type":"u128"},{"name":"fee_growth_outside_1_x64","type":"u128"},{"name":"reward_growths_outside_x64","type":{"array":["u128",3]}},{"name":"padding","type":{"array":["u32",13]}}]}},{"name":"UpdateRewardInfosEvent","docs":["Emitted when Reward are updated for a pool"],"type":{"kind":"struct","fields":[{"name":"reward_growth_global_x64","docs":["Reward info"],"type":{"array":["u128",3]}}]}}]} \ No newline at end of file diff --git a/idls/phoenix_eternal.EtrnLzgbS7nMMy5fbD42kXiUzGg8XQzJ972Xtk1cjWih.json b/idls/phoenix_eternal.EtrnLzgbS7nMMy5fbD42kXiUzGg8XQzJ972Xtk1cjWih.json new file mode 100644 index 0000000..e69de29 diff --git a/idls/printr.T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint.json b/idls/printr.T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint.json new file mode 100644 index 0000000..2899019 --- /dev/null +++ b/idls/printr.T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint.json @@ -0,0 +1 @@ +{"address":"T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint","metadata":{"name":"printr","version":"1.0.0","spec":"0.1.0","description":"Printr on Solana"},"instructions":[{"name":"authorized_spend_all_quote","discriminator":[55,106,238,14,152,246,186,129],"accounts":[{"name":"authorizor","docs":["This is a Printr PDA.","You can see the expected seed in the [Self::apply] static function."],"writable":true},{"name":"authorizor_wallet","writable":true,"optional":true},{"name":"swap","accounts":[{"name":"payer","docs":["The user performing the swap"],"writable":true,"signer":true},{"name":"printr_authority","docs":["This is the account that funds the graduation process.",""],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"input_wallet","writable":true},{"name":"output_wallet","docs":["The mint of this token account determines what's being sold, whether telecoin or quote."],"writable":true},{"name":"telecoin_mint","writable":true},{"name":"quote_mint","writable":true},{"name":"dbc_pool_authority","writable":true},{"name":"dbc_config"},{"name":"dbc_pool","writable":true},{"name":"dbc_telecoin_vault","writable":true},{"name":"dbc_quote_vault","writable":true},{"name":"dbc_event_authority"},{"name":"dbc_migration_metadata","docs":["DEPRECATED: Meteora no longer uses this one.",""],"writable":true},{"name":"damm_printr_partner_config"},{"name":"damm_pool","writable":true},{"name":"damm_pool_authority"},{"name":"damm_telecoin_vault","writable":true},{"name":"damm_quote_vault","writable":true},{"name":"damm_position_nft_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,100,97,109,109,95,112,111,115,105,116,105,111,110,95,110,102,116,95,109,105,110,116]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"damm_position_nft_account","writable":true},{"name":"damm_position","writable":true},{"name":"damm_event_authority"},{"name":"telecoin_token_program","docs":["We'll need this to init the mint and init the token account."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"dbc_program","docs":["The DBC program that abstracts the bonding curve logic."],"address":"dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN"},{"name":"damm_program","docs":["The DAMM V2 program that abstracts the AMM logic."],"address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"},{"name":"system_program","docs":["New accounts, native transfers, etc."],"address":"11111111111111111111111111111111"}]}],"args":[{"name":"max_price","type":{"defined":{"name":"SqrtBitshiftedPrice"}}},{"name":"extra_seed","type":"bytes"}]},{"name":"buyback_and_burn","discriminator":[121,156,154,165,194,86,180,130],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"printr_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"payer_quote_wallet","writable":true},{"name":"buyback_telecoin_wallet","docs":["Owned by printr_authority (PDA)."],"writable":true},{"name":"telecoin_mint","writable":true},{"name":"quote_mint","docs":["UncheckedAccount to avoid self-CPI exit handler corruption."],"writable":true},{"name":"dbc_pool_authority","writable":true},{"name":"dbc_config"},{"name":"dbc_pool","writable":true},{"name":"dbc_telecoin_vault","writable":true},{"name":"dbc_quote_vault","writable":true},{"name":"dbc_event_authority"},{"name":"dbc_migration_metadata","writable":true},{"name":"damm_printr_partner_config"},{"name":"damm_pool","writable":true},{"name":"damm_pool_authority"},{"name":"damm_telecoin_vault","writable":true},{"name":"damm_quote_vault","writable":true},{"name":"damm_position_nft_mint","writable":true},{"name":"damm_position_nft_account","writable":true},{"name":"damm_position","writable":true},{"name":"damm_event_authority"},{"name":"telecoin_token_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program"},{"name":"dbc_program","address":"dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN"},{"name":"damm_program","address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"printr_config","pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,99,111,110,102,105,103]}]}},{"name":"dev_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"quote_fee_vault","docs":["Stores all quote fees across all telecoins."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,115,95,102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"quote_mint"}]}},{"name":"printr_quote_fee_wallet","writable":true,"pda":{"seeds":[{"kind":"account","path":"printr_config.printr_fee_authority","account":"PrintrConfig"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"revshare1_quote_fee_wallet","docs":["If relevant then authority is checked via [CollectProtocolTransfers]"],"writable":true,"optional":true},{"name":"revshare2_quote_fee_wallet","docs":["If relevant then authority is checked via [CollectProtocolTransfers]"],"writable":true,"optional":true},{"name":"instructions_sysvar","address":"Sysvar1nstructions1111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[],"returns":{"defined":{"name":"FeesClaimed"}}},{"name":"cancel_authorized_spend_all_quote","discriminator":[167,1,184,69,242,171,240,207],"accounts":[{"name":"authorizor","docs":["This is a Printr PDA.","You can see the expected seed in the [Self::apply] static function."],"writable":true},{"name":"authorizor_wallet","docs":["The SPL token account associated with the authorizor."],"writable":true,"optional":true},{"name":"quote_mint","docs":["The quote mint that was meant to be swapped against the telecoin token."]},{"name":"recipient","docs":["The authorizor's recipient can cancel their own swap order."],"writable":true,"signer":true},{"name":"recipient_wallet","docs":["The recipient's wallet if applicable."],"writable":true,"optional":true},{"name":"quote_token_program","docs":["Allows us to swap."]},{"name":"system_program","docs":["We need to transfer."],"address":"11111111111111111111111111111111"}],"args":[{"name":"max_price","type":{"defined":{"name":"SqrtBitshiftedPrice"}}},{"name":"telecoin_mint","type":"pubkey"},{"name":"extra_seed","type":"bytes"}]},{"name":"claim_damm_fees","discriminator":[221,192,167,35,180,110,107,114],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"printr_authority","pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"printr_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,99,111,110,102,105,103]}]}},{"name":"telecoin_mint"},{"name":"quote_mint","docs":["The quote mint is the token that will be used to buy telecoin tokens."]},{"name":"telecoin_fee_vault_authority","docs":["Authority for the telecoin fee vault.","For staking telecoins: the staking pool PDA.","For non-staking telecoins: printr_config.staking_authority.",""]},{"name":"telecoin_fee_vault","docs":["Stores telecoin fees.","","For staking telecoins: staking pool's fee vault (authority = staking_pool).","","For non-staking telecoins: generic staking wallet (authority =","printr_config.staking_authority).",""],"writable":true},{"name":"dev_config","docs":["The dev config stores information about the dev and must match the damm pool.","","We know this is the right dev config because its seed matches the telecoin mint which in","turn matches the dammv2 pool."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"quote_fee_vault","docs":["Stores all quote fees across all telecoins."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,115,95,102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"quote_mint"}]}},{"name":"damm_position_nft_account"},{"name":"damm_position","writable":true},{"name":"damm_pool_authority"},{"name":"damm_pool","writable":true},{"name":"damm_base_vault","writable":true},{"name":"damm_quote_vault","writable":true},{"name":"damm_event_authority"},{"name":"telecoin_token_program","docs":["We'll need this to transfer."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","docs":["For the telecoin fees."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"system_program","docs":["Necessary for creating new accounts."],"address":"11111111111111111111111111111111"},{"name":"damm_v2_program","docs":["The DAMM program that abstracts the bonding curve logic."],"address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"}],"args":[],"returns":{"defined":{"name":"FeesClaimed"}}},{"name":"claim_dbc_fees","discriminator":[162,222,128,90,59,115,34,130],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"printr_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"printr_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,99,111,110,102,105,103]}]}},{"name":"telecoin_mint"},{"name":"quote_mint","docs":["The quote mint is the token that is be used to buy telecoin tokens."]},{"name":"any_telecoin_wallet","docs":["Therefore any old telecoin wallet will do, it will not be used."],"writable":true},{"name":"printr_intermediary_authority","pda":{"seeds":[{"kind":"const","value":[105,110,116,101,114,109,101,100,105,97,114,121,95,116,111,107,101,110,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"printr_intermediary_quote_wallet","docs":["Useful only for $wSOL quote token.","We will collect some fee amount to support smart graduation in swap ix."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"printr_intermediary_authority"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"dbc_pool_authority"},{"name":"dbc_pool","writable":true},{"name":"dbc_base_vault","writable":true},{"name":"dbc_quote_vault","writable":true},{"name":"dbc_event_authority"},{"name":"dev_config","docs":["The dev config stores information about the dev and must match the DBC pool.","","We know this is the right dev config because its seed matches the telecoin mint which in","turn matches the dbc pool."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"quote_fee_vault","docs":["Shared vault per quote mint. Per-telecoin accounting is tracked in DevConfig."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,115,95,102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"quote_mint"}]}},{"name":"telecoin_token_program","docs":["We'll need this to transfer."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"associated_token_program","docs":["Used to create dev's telecoin ATA."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["Necessary for creating new accounts."],"address":"11111111111111111111111111111111"},{"name":"dbc_program","docs":["The DBC program that abstracts the bonding curve logic."],"address":"dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN"}],"args":[],"returns":{"defined":{"name":"FeesClaimed"}}},{"name":"claim_printr_authority_lamports","discriminator":[52,14,140,114,41,105,0,48],"accounts":[{"name":"admin","writable":true,"signer":true},{"name":"printr_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"system_program","docs":["Necessary for creating new accounts."],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"claim_staking_rewards","discriminator":[229,141,170,69,111,94,6,72],"accounts":[{"name":"payer","docs":["Anyone can call this instruction."],"writable":true,"signer":true},{"name":"position_owner","writable":true},{"name":"printr_config","docs":["Printr config for authorizing transfers."],"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,99,111,110,102,105,103]}]}},{"name":"telecoin_mint","docs":["The telecoin mint."]},{"name":"quote_mint","docs":["The quote token mint."]},{"name":"staking_pool","docs":["The staking pool for this telecoin."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,105,110,103,95,112,111,111,108]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"stake_position","docs":["The stake position being claimed from."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,101,95,112,111,115,105,116,105,111,110]},{"kind":"account","path":"telecoin_mint"},{"kind":"account","path":"position_owner"},{"kind":"account","path":"stake_position.position_nonce","account":"StakePosition"}]}},{"name":"owner_quote_account","docs":["Owner's quote token account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"position_owner"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"owner_telecoin_account","docs":["Owner's telecoin token account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"position_owner"},{"kind":"account","path":"telecoin_token_program"},{"kind":"account","path":"telecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"quote_fee_vault","docs":["Stores all quote fees across Printr so be gentle with it."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,115,95,102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"quote_mint"}]}},{"name":"pool_telecoin_vault","docs":["Pool's telecoin vault.","Contains both trading fees and staked telecoin."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"staking_pool"},{"kind":"account","path":"telecoin_token_program"},{"kind":"account","path":"telecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"telecoin_token_program","docs":["Checks the account owner."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"associated_token_program","docs":["ATA boy"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["Needed because `owner_quote_account` and `owner_telecoin_account` are `init_if_needed`."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"close_stake_position","discriminator":[8,63,74,143,105,42,28,64],"accounts":[{"name":"position_owner","docs":["Only the position owner can close the position.","Receives the rent refund from the closed position account."],"writable":true,"signer":true},{"name":"telecoin_mint","docs":["The telecoin mint."]},{"name":"staking_pool","docs":["The staking pool for this telecoin."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,105,110,103,95,112,111,111,108]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"stake_position","docs":["The stake position being closed. Rent is refunded to `position_owner`."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,101,95,112,111,115,105,116,105,111,110]},{"kind":"account","path":"telecoin_mint"},{"kind":"account","path":"position_owner"},{"kind":"account","path":"stake_position.position_nonce","account":"StakePosition"}]}},{"name":"owner_telecoin_account","docs":["Owner's telecoin token account. Receives the staked telecoin back."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"position_owner"},{"kind":"account","path":"telecoin_token_program"},{"kind":"account","path":"telecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool_telecoin_vault","docs":["Pool's telecoin vault holding the staked telecoin."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"staking_pool"},{"kind":"account","path":"telecoin_token_program"},{"kind":"account","path":"telecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"telecoin_token_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["Needed for `init_if_needed` on `owner_telecoin_account` and for `close` on the position."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"create_printr_config","discriminator":[160,191,66,212,5,106,20,215],"accounts":[{"name":"admin","writable":true,"signer":true},{"name":"printr_fee_authority"},{"name":"questflow_ai_fee_authority"},{"name":"printr_staking_authority"},{"name":"printr_config","docs":["We use `init_if_needed` purposefully.","Call this ix again to update the config."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,99,111,110,102,105,103]}]}},{"name":"system_program","docs":["Necessary for creating new accounts."],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_stake_position","discriminator":[92,168,96,133,102,121,86,138],"accounts":[{"name":"payer","docs":["We can create positions on behalf of others."],"writable":true,"signer":true},{"name":"position_owner","writable":true},{"name":"printr_authority","pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"dev_config","docs":["Dev config must indicate this telecoin uses staking for fee distribution."],"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"staking_pool","docs":["The staking pool for this telecoin.","Will be lazily initialized if this is the first time anyone stakes this telecoin."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,105,110,103,95,112,111,111,108]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"stake_position","docs":["The stake position being created.","The ID can be reused, just cannot be used at the same time as another position."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,101,95,112,111,115,105,116,105,111,110]},{"kind":"account","path":"telecoin_mint"},{"kind":"account","path":"position_owner"},{"kind":"arg","path":"position_nonce"}]}},{"name":"payer_telecoin_account","docs":["Payer doesn't necessarily equal to the user but most likely will."],"writable":true},{"name":"pool_telecoin_vault","docs":["Staking pool's telecoin vault."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"staking_pool"},{"kind":"account","path":"telecoin_token_program"},{"kind":"account","path":"telecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"telecoin_mint","docs":["The telecoin mint."]},{"name":"telecoin_token_program","docs":["Checks the account owner."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","docs":["Related to the telecoin vault ATA."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"position_nonce","type":"u32"},{"name":"to_stake","type":{"defined":{"name":"TokenAmount"}}},{"name":"lock_period","type":{"defined":{"name":"LockPeriod"}}}]},{"name":"deploy_telecoin","discriminator":[4,248,20,1,125,63,15,0],"accounts":[{"name":"payer","docs":["Creating a launch is a permission-less operation.","This could be a Printr user or a BE."],"writable":true,"signer":true},{"name":"printr_authority","pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"dev_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"telecoin_mint","docs":["This is the telecoin mint which we initialize according to the telecoin params."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,116,101,108,101,99,111,105,110,95,109,105,110,116]},{"kind":"arg","path":"telecoin_params"}]}},{"name":"telecoin_token_program","docs":["We'll need this to init the mint."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","docs":["Necessary for creating new accounts."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"telecoin_params_compact","type":{"defined":{"name":"TelecoinParamsCompact"}}}]},{"name":"deploy_telecoin2","discriminator":[37,155,54,236,186,225,201,137],"accounts":[{"name":"payer","docs":["Creating a launch is a permission-less operation.","This could be a Printr user or a BE."],"writable":true,"signer":true},{"name":"printr_authority","pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"dev_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"telecoin_mint","docs":["This is the telecoin mint which we initialize according to the telecoin params."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,116,101,108,101,99,111,105,110,95,109,105,110,116]},{"kind":"arg","path":"telecoin_params"}]}},{"name":"telecoin_token_program","docs":["We'll need this to init the mint."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","docs":["Necessary for creating new accounts."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"telecoin_params","type":{"defined":{"name":"TelecoinParams"}}}]},{"name":"distribute_telecoin_quote_fees","discriminator":[31,197,88,239,123,165,197,208],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"printr_authority","pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"printr_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,99,111,110,102,105,103]}]}},{"name":"quote_mint","docs":["The quote mint is the token that is be used to buy telecoin tokens."]},{"name":"telecoin_mint"},{"name":"quote_fee_vault","docs":["Stores all quote fees across all telecoins.","How much can we claim now is tracked in the dev config."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,115,95,102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"quote_mint"}]}},{"name":"dev_config","docs":["The dev config stores information about how much a dev can claim.","The existence of this account implies that the telecoin mint was created by Printr."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"dev"},{"name":"dev_quote_wallet","writable":true,"pda":{"seeds":[{"kind":"account","path":"dev"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"printr_quote_fee_wallet","writable":true,"pda":{"seeds":[{"kind":"account","path":"printr_config.printr_fee_authority","account":"PrintrConfig"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"revshare1_quote_fee_wallet","docs":["If relevant then authority is checked via [CollectProtocolTransfers]"],"writable":true,"optional":true},{"name":"revshare2_quote_fee_wallet","docs":["If relevant then authority is checked via [CollectProtocolTransfers]"],"writable":true,"optional":true},{"name":"dbc_pool"},{"name":"dbc_config"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"associated_token_program","docs":["Used to create dev's quote ATA."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["Necessary for creating new accounts."],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"estimate_swap","discriminator":[219,196,183,82,59,37,166,59],"accounts":[{"name":"dbc_config"},{"name":"dbc_pool"},{"name":"damm_pool","docs":["Might be uninitialized if the pool is not yet graduated."]}],"args":[{"name":"params","type":{"defined":{"name":"SwapIntent"}}}],"returns":{"defined":{"name":"EstimateSwapOutput"}}},{"name":"estimate_teleport_with_lz","discriminator":[11,141,166,118,153,255,62,132],"accounts":[{"name":"peer"}],"args":[{"name":"params","type":{"defined":{"name":"EstimateTeleportWithLzParams"}}}],"returns":{"defined":{"name":"TokenAmount"}}},{"name":"lz_receive","discriminator":[8,179,120,109,33,118,189,80],"accounts":[{"name":"payer","docs":["The LZ executor."],"writable":true,"signer":true},{"name":"peer","docs":["Represents where did the message come from."]},{"name":"telecoin_teleport_config","docs":["If this account does not exist then the provided peer must be set as a default peer.","Otherwise we parse this account's configuration and assert it's been upheld."]},{"name":"printr_authority","docs":["Global Printr telecoin mint authority"],"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"telecoin_mint","docs":["We'll mint these tokens.",""],"writable":true},{"name":"recipient"},{"name":"recipient_destination_telecoin_wallet","writable":true,"pda":{"seeds":[{"kind":"account","path":"recipient"},{"kind":"account","path":"telecoin_token_program"},{"kind":"account","path":"telecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"telecoin_token_program","docs":["Telecoin mint is always token program 2022."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","docs":["In case we need to create the ATA"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["In case we need to create the ATA"],"address":"11111111111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"LzReceiveParams"}}}]},{"name":"lz_receive_types","discriminator":[221,17,246,159,248,128,31,96],"accounts":[],"args":[{"name":"params","type":{"defined":{"name":"LzReceiveParams"}}}],"returns":{"vec":{"defined":{"name":"LzAccount"}}}},{"name":"lz_set_peer","discriminator":[199,123,81,116,208,49,213,232],"accounts":[{"name":"admin","docs":["Printr staff."],"writable":true,"signer":true},{"name":"peer","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,101,101,114]},{"kind":"arg","path":"peer_address"}]}},{"name":"lz_oapp_registry","writable":true,"pda":{"seeds":[{"kind":"const","value":[79,65,112,112]},{"kind":"account","path":"peer"}],"program":{"kind":"account","path":"endpoint_program"}}},{"name":"endpoint_emit_cpi_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"account","path":"endpoint_program"}}},{"name":"endpoint_program","address":"76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"peer_address","type":{"array":["u8",32]}}]},{"name":"print_telecoin","discriminator":[214,30,4,102,207,73,108,35],"accounts":[{"name":"payer","docs":["Creating a launch is a permission-less operation.","This could be a Printr user or a BE."],"writable":true,"signer":true},{"name":"printr_authority","pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"dev_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"telecoin_mint","docs":["This is the telecoin mint which we initialize according to the telecoin params in the","Meteora CPI.",""],"writable":true},{"name":"quote_mint","docs":["The quote mint is the token that will be used to buy telecoin tokens."]},{"name":"initial_buy_authority","docs":["When a telecoin is printed we perform an initial buy on behalf of the dev.","","There are three options how the initial buy can happen:","1. No deposit PDA","2. Deposit PDA of native $SOL","3. Deposit PDA with an associated token account.","","See `DepositPda` enum for more details.",""],"writable":true},{"name":"initial_buy_source","writable":true},{"name":"dev_on_solana","writable":true},{"name":"dev_destination_telecoin_wallet","docs":["Associated token account cannot exist because we've just created the telecoin mint.","Will not be initialized if initial spend is zero.",""],"writable":true,"optional":true},{"name":"dbc_config","docs":["We'll init the config first.",""],"writable":true},{"name":"dbc_pool_authority"},{"name":"dbc_pool","docs":["The bonding curve state, initialized after the config.",""],"writable":true},{"name":"dbc_telecoin_vault","writable":true},{"name":"dbc_quote_vault","writable":true},{"name":"dbc_event_authority"},{"name":"dbc_migration_metadata","docs":["DEPRECATED: Meteora no longer uses this one.","","If provided then we create a migration metadata account for the DBC pool.",""],"writable":true,"optional":true},{"name":"telecoin_token_program","docs":["We'll need this to init the mint and init the token account."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"associated_token_program","docs":["Used to create dev's telecoin ATA."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["Necessary for creating new accounts."],"address":"11111111111111111111111111111111"},{"name":"dbc_program","docs":["The DBC program that abstracts the bonding curve logic."],"address":"dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"telecoin_params_compact","type":{"defined":{"name":"TelecoinParamsCompact"}}}]},{"name":"print_telecoin2","discriminator":[166,60,38,43,238,32,2,160],"accounts":[{"name":"payer","docs":["Creating a launch is a permission-less operation.","This could be a Printr user or a BE."],"writable":true,"signer":true},{"name":"printr_authority","pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"dev_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"telecoin_mint","docs":["This is the telecoin mint which we initialize according to the telecoin params in the","Meteora CPI.",""],"writable":true},{"name":"quote_mint","docs":["The quote mint is the token that will be used to buy telecoin tokens."]},{"name":"initial_buy_authority","docs":["When a telecoin is printed we perform an initial buy on behalf of the dev.","","There are three options how the initial buy can happen:","1. No deposit PDA","2. Deposit PDA of native $SOL","3. Deposit PDA with an associated token account.","","See `DepositPda` enum for more details.",""],"writable":true},{"name":"initial_buy_source","writable":true},{"name":"dev_on_solana","writable":true},{"name":"dev_destination_telecoin_wallet","docs":["Associated token account cannot exist because we've just created the telecoin mint.","Will not be initialized if initial spend is zero.",""],"writable":true,"optional":true},{"name":"dbc_config","docs":["We'll init the config first.",""],"writable":true},{"name":"dbc_pool_authority"},{"name":"dbc_pool","docs":["The bonding curve state, initialized after the config.",""],"writable":true},{"name":"dbc_telecoin_vault","writable":true},{"name":"dbc_quote_vault","writable":true},{"name":"dbc_event_authority"},{"name":"dbc_migration_metadata","docs":["DEPRECATED: Meteora no longer uses this one.","","If provided then we create a migration metadata account for the DBC pool.",""],"writable":true,"optional":true},{"name":"telecoin_token_program","docs":["We'll need this to init the mint and init the token account."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"associated_token_program","docs":["Used to create dev's telecoin ATA."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["Necessary for creating new accounts."],"address":"11111111111111111111111111111111"},{"name":"dbc_program","docs":["The DBC program that abstracts the bonding curve logic."],"address":"dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"telecoin_params","type":{"defined":{"name":"TelecoinParams"}}}]},{"name":"refresh_staking2","discriminator":[36,197,195,27,8,224,118,27],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"printr_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,99,111,110,102,105,103]}]}},{"name":"printr_quote_fee_wallet","writable":true,"pda":{"seeds":[{"kind":"account","path":"printr_config.printr_fee_authority","account":"PrintrConfig"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"quote_fee_vault","docs":["Stores all quote fees across Printr."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,115,95,102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"quote_mint"}]}},{"name":"revshare1_quote_fee_wallet","writable":true,"optional":true},{"name":"revshare2_quote_fee_wallet","writable":true,"optional":true},{"name":"staking_pool","docs":["The staking pool for this telecoin."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,105,110,103,95,112,111,111,108]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"dev_config","docs":["The dev config stores information about accumulated quote fees."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"dbc_config"},{"name":"dbc_pool","docs":["DBC config."]},{"name":"telecoin_mint","docs":["The telecoin mint."]},{"name":"quote_mint","docs":["The quote mint is the token that is be used to buy telecoin tokens."]},{"name":"pool_telecoin_vault","docs":["Telecoin vault that holds staked tokens and accumulated telecoin fees."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"staking_pool"},{"kind":"account","path":"telecoin_token_program"},{"kind":"account","path":"telecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"protocol_telecoin_wallet","docs":["Protocol's telecoin wallet. Receives the protocol's share of telecoin rewards.","ATA of `printr_config.staking_authority` for the telecoin mint."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"protocol_telecoin_authority"},{"kind":"account","path":"telecoin_token_program"},{"kind":"account","path":"telecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"protocol_telecoin_authority"},{"name":"telecoin_token_program","docs":["Checks the account owner."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"associated_token_program","docs":["Related to the telecoin vault ATA."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["Need to init the staking pool if not yet."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"set_dev_config_dev","discriminator":[175,111,205,104,111,167,15,28],"accounts":[{"name":"admin","signer":true},{"name":"telecoin_mint","docs":["The telecoin mint whose dev config we want to update."]},{"name":"dev_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_dev","type":"pubkey"}]},{"name":"set_dev_config_fees","discriminator":[75,184,89,133,143,57,183,121],"accounts":[{"name":"admin","signer":true},{"name":"telecoin_mint","docs":["The telecoin mint whose dev config we want to update."]},{"name":"dev_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}}],"args":[{"name":"params","type":{"defined":{"name":"SetDevConfigFeesParams"}}}]},{"name":"set_peer_conf","discriminator":[81,211,81,152,212,153,58,71],"accounts":[{"name":"admin","docs":["Printr staff."],"signer":true},{"name":"peer","writable":true}],"args":[{"name":"params","type":{"defined":{"name":"SetPeerConfParams"}}}]},{"name":"set_telecoin_teleport_conf","discriminator":[100,47,61,148,198,138,62,49],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"admin","docs":["Printr staff."],"signer":true},{"name":"telecoin_teleport_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,101,108,101,99,111,105,110,95,116,101,108,101,112,111,114,116,95,99,111,110,102,105,103]},{"kind":"arg","path":"telecoin_id"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"telecoin_id","type":{"defined":{"name":"TelecoinId"}}},{"name":"params","type":{"defined":{"name":"SetTelecoinTeleportConfParams"}}}]},{"name":"spend_all_quote_no_graduation","discriminator":[198,170,217,245,59,73,114,60],"accounts":[{"name":"payer","docs":["The entity performing the swap.","Owner of the input token account.","","If the quote token is $wSOL then we transfer everything from this account to the","intermediary token account."],"writable":true,"signer":true},{"name":"printr_intermediary_authority","pda":{"seeds":[{"kind":"const","value":[105,110,116,101,114,109,101,100,105,97,114,121,95,116,111,107,101,110,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"printr_intermediary_quote_wallet","docs":["To support post hooks with native $SOL we need to create an intermediate ATA for the quote","token.","All the quote tokens will be transferred to this account first before being swapped.","The account is not closed so that it can be reused in the future without paying rent."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"printr_intermediary_authority"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"input_quote_wallet","docs":["We will use everything that's in this token account to buy as much telecoin as possible.","","Can be none for $wSOL quote if wrapping is needed."],"writable":true,"optional":true},{"name":"recipient"},{"name":"recipient_quote_wallet","writable":true,"pda":{"seeds":[{"kind":"account","path":"recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"output_telecoin_wallet","docs":["The owner of the output account will not match the payer for a posthook ix.",""],"writable":true,"pda":{"seeds":[{"kind":"account","path":"recipient"},{"kind":"account","path":"telecoin_token_program"},{"kind":"account","path":"telecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"telecoin_mint","writable":true},{"name":"quote_mint","writable":true},{"name":"dbc_pool_authority","writable":true},{"name":"dbc_config"},{"name":"dbc_pool","writable":true},{"name":"dbc_telecoin_vault","writable":true},{"name":"dbc_quote_vault","writable":true},{"name":"dbc_event_authority"},{"name":"damm_pool","writable":true},{"name":"damm_pool_authority"},{"name":"damm_telecoin_vault","writable":true},{"name":"damm_quote_vault","writable":true},{"name":"damm_event_authority"},{"name":"telecoin_token_program","docs":["We'll need this to init the mint and init the token account."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"dbc_program","docs":["The DBC program that abstracts the bonding curve logic."],"address":"dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN"},{"name":"damm_program","docs":["The DAMM V2 program that abstracts the AMM logic."],"address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"},{"name":"associated_token_program","docs":["Used to create ATA if needed."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["We might need to refill the input token account with $wSOL."],"address":"11111111111111111111111111111111"}],"args":[{"name":"max_price","type":{"defined":{"name":"SqrtBitshiftedPrice"}}}]},{"name":"swap","discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"payer","docs":["The user performing the swap"],"writable":true,"signer":true},{"name":"printr_authority","docs":["This is the account that funds the graduation process.",""],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"input_wallet","writable":true},{"name":"output_wallet","docs":["The mint of this token account determines what's being sold, whether telecoin or quote."],"writable":true},{"name":"telecoin_mint","writable":true},{"name":"quote_mint","writable":true},{"name":"dbc_pool_authority","writable":true},{"name":"dbc_config"},{"name":"dbc_pool","writable":true},{"name":"dbc_telecoin_vault","writable":true},{"name":"dbc_quote_vault","writable":true},{"name":"dbc_event_authority"},{"name":"dbc_migration_metadata","docs":["DEPRECATED: Meteora no longer uses this one.",""],"writable":true},{"name":"damm_printr_partner_config"},{"name":"damm_pool","writable":true},{"name":"damm_pool_authority"},{"name":"damm_telecoin_vault","writable":true},{"name":"damm_quote_vault","writable":true},{"name":"damm_position_nft_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,100,97,109,109,95,112,111,115,105,116,105,111,110,95,110,102,116,95,109,105,110,116]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"damm_position_nft_account","writable":true},{"name":"damm_position","writable":true},{"name":"damm_event_authority"},{"name":"telecoin_token_program","docs":["We'll need this to init the mint and init the token account."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"dbc_program","docs":["The DBC program that abstracts the bonding curve logic."],"address":"dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN"},{"name":"damm_program","docs":["The DAMM V2 program that abstracts the AMM logic."],"address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"},{"name":"system_program","docs":["New accounts, native transfers, etc."],"address":"11111111111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"SwapIntent"}}}]},{"name":"teleport_with_lz","discriminator":[166,121,233,8,125,40,179,47],"accounts":[{"name":"payer","docs":["This is the user that wants to send telecoin tokens."],"writable":true,"signer":true},{"name":"telecoin_mint","docs":["The telecoin mint that we burn here."],"writable":true},{"name":"telecoin_teleport_config","docs":["If this account does not exist then the provided peer must be set as a default peer.","Otherwise we parse this account's configuration and assert it's been upheld."]},{"name":"peer"},{"name":"user_source_telecoin_wallet","docs":["Where to burn from."],"writable":true},{"name":"telecoin_token_program","docs":["Needed to burn telecoin tokens."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"TeleportWithLzParams"}}}]}],"accounts":[{"name":"DevConfig","discriminator":[92,158,1,106,83,222,4,81]},{"name":"Peer","discriminator":[50,8,19,55,40,253,37,58]},{"name":"PoolConfig","discriminator":[26,108,14,123,116,230,129,43]},{"name":"PrintrConfig","discriminator":[184,34,37,157,37,223,65,252]},{"name":"StakePosition","discriminator":[78,165,30,111,171,125,11,220]},{"name":"StakingPool","discriminator":[203,19,214,220,220,154,24,102]},{"name":"TelecoinTeleportConfig","discriminator":[146,117,46,78,137,112,71,57]},{"name":"VirtualPool","discriminator":[213,224,5,209,98,69,119,92]}],"events":[{"name":"BuybackAndBurnEvent","discriminator":[158,146,152,190,14,197,155,158]},{"name":"ClaimStakingRewardsEvent","discriminator":[109,78,167,39,50,229,74,85]},{"name":"CreateStakePositionEvent","discriminator":[47,51,210,240,178,138,154,198]},{"name":"DeployTelecoinEvent","discriminator":[133,217,193,229,51,195,248,98]},{"name":"PrintTelecoinEvent","discriminator":[202,7,212,34,205,71,111,139]},{"name":"RefreshStaking2Event","discriminator":[39,218,120,153,29,229,190,254]},{"name":"SetDevConfigDevEvent","discriminator":[89,201,9,41,16,123,16,57]},{"name":"TeleportWithLzEvent","discriminator":[166,170,61,125,224,166,227,98]}],"types":[{"name":"Accumulator","docs":["Accumulator value tracking cumulative rewards per unit of weighted stake.","","Used in the staking system where rewards are distributed proportionally across","all stakers. Values are scaled by 2^64 for precision:","","```text","accumulator += (new_rewards << 64) / total_weighted_stake","pending_rewards = weighted_stake * (current_acc - last_acc) >> 64","```"],"type":{"kind":"struct","fields":["u128"]}},{"name":"BaseFeeConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","type":"u64"},{"name":"second_factor","type":"u64"},{"name":"third_factor","type":"u64"},{"name":"first_factor","type":"u16"},{"name":"base_fee_mode","type":"u8"},{"name":"padding_0","type":{"array":["u8",5]}}]}},{"name":"BuybackAndBurnEvent","type":{"kind":"struct","fields":[{"name":"telecoin_id","type":{"defined":{"name":"TelecoinId"}}},{"name":"quote_spent","type":{"defined":{"name":"TokenAmount"}}},{"name":"telecoin_burned","type":{"defined":{"name":"TokenAmount"}}}]}},{"name":"ClaimStakingRewardsEvent","type":{"kind":"struct","fields":[{"name":"owner","docs":["Owner of the position."],"type":"pubkey"},{"name":"telecoin_mint","docs":["Telecoin mint."],"type":"pubkey"},{"name":"position_nonce","docs":["Position nonce."],"type":"u32"},{"name":"quote_rewards","docs":["Quote token rewards claimed."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"telecoin_rewards","docs":["Telecoin rewards claimed."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"position_closed","docs":["Whether the position was closed. Always false for `claim_staking_rewards`;","set to true only by `close_stake_position`."],"type":"bool"},{"name":"returned_stake","docs":["Amount returned to user if closed. Always zero for `claim_staking_rewards`;","the staked amount is only returned by `close_stake_position`."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"quote_token_accumulator","docs":["Position's quote token accumulator after claim."],"type":{"defined":{"name":"Accumulator"}}},{"name":"telecoin_accumulator","docs":["Position's telecoin accumulator after claim."],"type":{"defined":{"name":"Accumulator"}}},{"name":"telecoin_id","docs":["Telecoin ID."],"type":{"defined":{"name":"TelecoinId"}}}]}},{"name":"CreateStakePositionEvent","type":{"kind":"struct","fields":[{"name":"owner","docs":["User that will receive the staking rewards and the telecoins back."],"type":"pubkey"},{"name":"telecoin_id","docs":["Telecoin ID."],"type":{"defined":{"name":"TelecoinId"}}},{"name":"position_nonce","docs":["Position nonce for this user's stake."],"type":"u32"},{"name":"staked","docs":["Amount of telecoin staked."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"lock_period","docs":["Lock period selected."],"type":{"defined":{"name":"LockPeriod"}}},{"name":"staked_at","docs":["Timestamp when staking was created."],"type":"i64"},{"name":"quote_token_accumulator","docs":["Pool's quote token accumulator at position creation (position's initial checkpoint)."],"type":{"defined":{"name":"Accumulator"}}},{"name":"telecoin_accumulator","docs":["Pool's telecoin accumulator at position creation (position's initial checkpoint)."],"type":{"defined":{"name":"Accumulator"}}}]}},{"name":"DeployTelecoinEvent","docs":["Some Printr related details about the deploy."],"type":{"kind":"struct","fields":[{"name":"protocol_version","docs":["The protocol version of the graduating telecoin token."],"type":{"defined":{"name":"PrintrProtocolVersion"}}},{"name":"telecoin_id","docs":["The Printr Telecoin ID."],"type":{"defined":{"name":"TelecoinId"}}},{"name":"dev_on_solana","docs":["The dev's wallet on Solana.","","Will be default if this telecoin doesn't have a solana dev wallet."],"type":"pubkey"}]}},{"name":"DevConfig","docs":["Configuration for a dev of a telecoin token."],"type":{"kind":"struct","fields":[{"name":"fee_sink","docs":["Used to identify the dev for fee distribution.","","Will be the default key if this telecoin doesn't have a solana dev wallet.","That can happen if the telecoin was not printed on Solana and only deployed here","additionally without a bonding curve."],"type":{"defined":{"name":"FeeSink"}}},{"name":"protocol_version","docs":["Can be used to distinguish between breaking changes in the future."],"type":{"defined":{"name":"PrintrProtocolVersion"}}},{"name":"telecoin_id","docs":["What's the canonical telecoin ID at the time of creation.","We try not to break how telecoin IDs are derived but we neither make guarantees they won't","change."],"type":{"defined":{"name":"TelecoinId"}}},{"name":"fee_sink_quote_fee","docs":["When claiming fees we accumulate them in this counter.","When distributing the fees we reset this to zero and transfer the amount.","","# Migration","","After `PrintrProtocolVersion(3)` inclusive this field collects _all_ quote fees, both dev","fees and protocol fees."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"fees","docs":["Custom fee configuration set at print/deploy time."],"type":{"defined":{"name":"DevConfigFees"}}},{"name":"flags","docs":["Bit flags for fee distribution lifecycle."],"type":{"defined":{"name":"DevConfigFlags"}}},{"name":"_padding","type":"u8"}]}},{"name":"DevConfigFees","docs":["Custom fee overrides stored in [DevConfig].","","# Migration","","Until `PrintrProtocolVersion(3)` exclusive we used to store 8 padding bytes."],"type":{"kind":"struct","fields":[{"name":"dbc_protocol_share_of_trading_fee_pct","docs":["Protocol's share of the bonding curve trading fee as a percentage (0-100).","","For example, if the total DBC trading fee is 2% and this value is 15,","then 0.3% goes to the protocol and 1.7% goes to the dev.","","0 means not set.","This is going to be the case for telecoins printed before this struct was introduced.","In that case we use the defaults to keep the behavior consistent with what it was before."],"type":"u8"},{"name":"amm_protocol_share_of_trading_fee_pct","docs":["Protocol's share of the AMM trading fee as a percentage (0-100).","","Same semantics as [Self::dbc_protocol_share_of_trading_fee_pct] but for AMM trades."],"type":"u8"},{"name":"rev_shares","docs":["Up to 2 revshare entries for protocol fee distribution."],"type":{"array":[{"defined":{"name":"RevShareSlot"}},2]}}]}},{"name":"DevConfigFlags","docs":["Anchor-serializable wrapper around [printr_sdk::dev_config::DevConfigFlags]."],"type":{"kind":"struct","fields":["u8"]}},{"name":"DynamicFeeConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"initialized","type":"u8"},{"name":"padding","type":{"array":["u8",7]}},{"name":"max_volatility_accumulator","type":"u32"},{"name":"variable_fee_control","type":"u32"},{"name":"bin_step","type":"u16"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"padding2","type":{"array":["u8",8]}},{"name":"bin_step_u128","type":"u128"}]}},{"name":"EstimateSwapOutput","docs":["This is the returned value from this ix."],"type":{"kind":"struct","fields":[{"name":"estimate","docs":["The swap estimate."],"type":{"defined":{"name":"SwapEstimate"}}},{"name":"which_remaining_accounts_are_owned_by_system_program","docs":["The remaining accounts are mapped onto this list.","The index will be `true` if the account is owned by the system program."],"type":{"vec":"bool"}}]}},{"name":"EstimateTeleportWithLzParams","docs":["Parameters for the [TeleportWithLz] instruction."],"type":{"kind":"struct","fields":[{"name":"message_len","docs":["How long is the message to send.","","See [omnichain_codec::OutgoingMessage] for the message format."],"type":"u16"},{"name":"lz_options","docs":[""],"type":"bytes"},{"name":"lz_destination_endpoint_id","docs":["The chain ID of the destination chain.","",""],"type":"u32"}]}},{"name":"FeeSink","type":{"kind":"type","alias":"pubkey"}},{"name":"FeesClaimed","docs":["Fee claiming endpoints return how much fees were claimed - in total - for each token."],"type":{"kind":"struct","fields":[{"name":"in_telecoin","type":{"defined":{"name":"TokenAmount"}}},{"name":"in_quote","type":{"defined":{"name":"TokenAmount"}}}]}},{"name":"LiquidityDistributionConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"sqrt_price","type":"u128"},{"name":"liquidity","type":"u128"}]}},{"name":"LiquidityVestingInfo","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"is_initialized","type":"u8"},{"name":"vesting_percentage","type":"u8"},{"name":"_padding","type":{"array":["u8",2]}},{"name":"bps_per_period","type":"u16"},{"name":"number_of_periods","type":"u16"},{"name":"frequency","type":"u32"},{"name":"cliff_duration_from_migration_time","type":"u32"}]}},{"name":"LockPeriod","docs":["Lock periods for staking, each with its own reward multiplier."],"type":{"kind":"enum","variants":[{"name":"SevenDays"},{"name":"FourteenDays"},{"name":"ThirtyDays"},{"name":"SixtyDays"},{"name":"NinetyDays"},{"name":"OneHundredEightyDays"},{"name":"TenSeconds"}]}},{"name":"LockedVestingConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"amount_per_period","type":"u64"},{"name":"cliff_duration_from_migration_time","type":"u64"},{"name":"frequency","type":"u64"},{"name":"number_of_period","type":"u64"},{"name":"cliff_unlock_amount","type":"u64"},{"name":"_padding","type":"u64"}]}},{"name":"LzAccount","docs":["same to anchor_lang::prelude::AccountMeta"],"type":{"kind":"struct","fields":[{"name":"pubkey","type":"pubkey"},{"name":"is_signer","type":"bool"},{"name":"is_writable","type":"bool"}]}},{"name":"LzReceiveParams","type":{"kind":"struct","fields":[{"name":"src_eid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"guid","type":{"array":["u8",32]}},{"name":"message","type":"bytes"},{"name":"extra_data","type":"bytes"}]}},{"name":"Peer","docs":["A peer in omnichain network is a connection living on another chain,","typically a smart contract.","","A peer is authorized by the Printr staff to send and receive messages","authoritatively.","This suggests a reciprocal relationship between the two peers.","","To trust a message we must validate that it was sent by a peer.","Validation is done by relaying networks such as Axelar or LayerZero.","Acts as a connection to another chain."],"type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"address","docs":["The address of the peer on the other chain.","Is part of the seed"],"type":{"array":["u8",32]}},{"name":"is_lz_enabled","docs":["Admin can enable and disable the LZ communication channel to the peer.","","Multiple peers can be enabled."],"type":"bool"},{"name":"is_lz_default","docs":["Use this peer as the default for LZ teleports.","","Multiple peers can be set as default in which case they are equivalent from the POV of the","security of the protocol."],"type":"bool"}]}},{"name":"PoolConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"quote_mint","docs":["quote mint"],"type":"pubkey"},{"name":"fee_claimer","docs":["Address to get the fee"],"type":"pubkey"},{"name":"leftover_receiver","docs":["Address to receive extra base token after migration, in case token is fixed supply"],"type":"pubkey"},{"name":"pool_fees","docs":["Pool fee"],"type":{"defined":{"name":"PoolFeesConfig"}}},{"name":"partner_liquidity_vesting_info","type":{"defined":{"name":"LiquidityVestingInfo"}}},{"name":"creator_liquidity_vesting_info","type":{"defined":{"name":"LiquidityVestingInfo"}}},{"name":"padding_0","docs":["Padding for future use"],"type":{"array":["u8",14]}},{"name":"padding_1","docs":["Previously was protocol and referral fee percent. Beware of tombstone."],"type":"u16"},{"name":"collect_fee_mode","docs":["Collect fee mode"],"type":"u8"},{"name":"migration_option","docs":["migration option"],"type":"u8"},{"name":"activation_type","docs":["whether mode slot or timestamp"],"type":"u8"},{"name":"token_decimal","docs":["token decimals"],"type":"u8"},{"name":"version","docs":["version"],"type":"u8"},{"name":"token_type","docs":["token type of base token"],"type":"u8"},{"name":"quote_token_flag","docs":["quote token flag"],"type":"u8"},{"name":"partner_permanent_locked_liquidity_percentage","docs":["partner locked liquidity percentage"],"type":"u8"},{"name":"partner_liquidity_percentage","docs":["partner liquidity percentage"],"type":"u8"},{"name":"creator_permanent_locked_liquidity_percentage","docs":["creator post migration fee percentage"],"type":"u8"},{"name":"creator_liquidity_percentage","docs":["creator liquidity percentage"],"type":"u8"},{"name":"migration_fee_option","docs":["migration fee option"],"type":"u8"},{"name":"fixed_token_supply_flag","docs":["flag to indicate whether token is dynamic supply (0) or fixed supply (1)"],"type":"u8"},{"name":"creator_trading_fee_percentage","docs":["creator trading fee percentage"],"type":"u8"},{"name":"token_update_authority","docs":["token update authority"],"type":"u8"},{"name":"migration_fee_percentage","docs":["migration fee percentage"],"type":"u8"},{"name":"creator_migration_fee_percentage","docs":["creator migration fee percentage"],"type":"u8"},{"name":"padding_2","type":{"array":["u8",7]}},{"name":"swap_base_amount","docs":["swap base amount"],"type":"u64"},{"name":"migration_quote_threshold","docs":["migration quote threshold (in quote token)"],"type":"u64"},{"name":"migration_base_threshold","docs":["migration base threshold (in base token)"],"type":"u64"},{"name":"migration_sqrt_price","docs":["migration sqrt price"],"type":"u128"},{"name":"locked_vesting_config","docs":["locked vesting config"],"type":{"defined":{"name":"LockedVestingConfig"}}},{"name":"pre_migration_token_supply","docs":["pre migration token supply"],"type":"u64"},{"name":"post_migration_token_supply","docs":["post migration token supply"],"type":"u64"},{"name":"migrated_collect_fee_mode","docs":["migrated pool collect fee mode"],"type":"u8"},{"name":"migrated_dynamic_fee","docs":["migrated dynamic fee option."],"type":"u8"},{"name":"migrated_pool_fee_bps","docs":["migrated pool fee in bps"],"type":"u16"},{"name":"migrated_pool_base_fee_mode","type":"u8"},{"name":"enable_first_swap_with_min_fee","type":"u8"},{"name":"migrated_compounding_fee_bps","docs":["compounding fee bps for migrated DAMM v2 pool, should only be non-zero if migrated_collect_fee_mode is 2 (Compounding)"],"type":"u16"},{"name":"pool_creation_fee","docs":["pool creation fee in lamports value"],"type":"u64"},{"name":"migrated_pool_base_fee_bytes","docs":["serialized MigratedPoolMarketCapFeeSchedulerParams, only used when migrated_pool_base_fee_mode is market cap scheduler"],"type":{"array":["u8",16]}},{"name":"sqrt_start_price","docs":["minimum price"],"type":"u128"},{"name":"curve","docs":["curve, only use 20 point firstly, we can extend that latter"],"type":{"array":[{"defined":{"name":"LiquidityDistributionConfig"}},20]}}]}},{"name":"PoolFeesConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"base_fee","type":{"defined":{"name":"BaseFeeConfig"}}},{"name":"dynamic_fee","type":{"defined":{"name":"DynamicFeeConfig"}}}]}},{"name":"PoolMetrics","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"total_protocol_base_fee","type":"u64"},{"name":"total_protocol_quote_fee","type":"u64"},{"name":"total_trading_base_fee","type":"u64"},{"name":"total_trading_quote_fee","type":"u64"}]}},{"name":"PrintTelecoinEvent","docs":["Some Printr related details about the launch on top of meteora events."],"type":{"kind":"struct","fields":[{"name":"protocol_version","docs":["The protocol version of the graduating telecoin token."],"type":{"defined":{"name":"PrintrProtocolVersion"}}},{"name":"telecoin_id","docs":["The Printr Telecoin ID."],"type":{"defined":{"name":"TelecoinId"}}},{"name":"quote_mint","docs":["Which mint is used as a quote."],"type":"pubkey"},{"name":"dev_on_solana","docs":["The dev's wallet on Solana."],"type":"pubkey"}]}},{"name":"PrintrConfig","docs":["Configuration struct holding the public keys for various Printr program wallets.","","# Fields","- `printr_fee_authority`: Receives Printr's portion of the protocol fees.","- `questflow_ai_fee_authority`: Receives QuestFlow AI's portion of the protocol fees.","- `staking_authority`: Receives all fees that are in telecoin.","","# Authority","","This account is an authority over several token account vaults that hold protocol and dev fees."],"type":{"kind":"struct","fields":[{"name":"printr_fee_authority","docs":["This account receives Printr's portion of the protocol fees."],"type":"pubkey"},{"name":"questflow_ai_fee_authority","docs":["This account receives QuestFlow AI's portion of the protocol fees."],"type":"pubkey"},{"name":"_padding_1","docs":["# Migration","","See [Self::migrate_if_necessary]."],"type":{"array":["u8",32]}},{"name":"staking_authority","docs":["Receives all fees that are in telecoin."],"type":"pubkey"},{"name":"_padding_2","docs":["# Migration","","See [Self::migrate_if_necessary]."],"type":{"array":["u8",2]}},{"name":"_padding_3","docs":["# Migration","","See [Self::migrate_if_necessary]."],"type":{"array":["u8",2]}},{"name":"_padding_4","docs":["# Migration","","See [Self::migrate_if_necessary]."],"type":{"array":["u8",2]}},{"name":"_padding_5","docs":["# Migration","","See [Self::migrate_if_necessary]."],"type":{"array":["u8",2]}},{"name":"_padding_6","docs":["# Migration","","See [Self::migrate_if_necessary]."],"type":{"array":["u8",1]}},{"name":"last_update","docs":["# Migration","","This field was originally padding.","It was therefore set to 0.","We repurposed it to [PrintrProtocolVersion] to be able to track migrations of this struct."],"type":{"defined":{"name":"PrintrProtocolVersion"}}}]}},{"name":"PrintrProtocolVersion","docs":["Stores the protocol version that the telecoin was created with."],"type":{"kind":"struct","fields":["u8"]}},{"name":"ProtocolFeeSink","docs":["Lookup table for protocol fee sinks.","","Main raison d'etre for this is to avoid storage cost and migrations on Solana.","We agreed with the business side of Printr that the list will be rather static and won't require","additions in near future.","So we buy ourselves time by skipping the need for migrations this way."],"repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"None"},{"name":"QuestflowAi"}]}},{"name":"RefreshStaking2Event","type":{"kind":"struct","fields":[{"name":"telecoin_id","docs":["Telecoin ID, derives the mint."],"type":{"defined":{"name":"TelecoinId"}}},{"name":"quote_rewards","docs":["Quote token rewards distributed to stakers."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"protocol_quote_fees","docs":["Quote token fees sent to protocol + revshare wallets."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"telecoin_rewards","docs":["Telecoin rewards added."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"quote_token_accumulator","docs":["New quote token accumulator value."],"type":{"defined":{"name":"Accumulator"}}},{"name":"telecoin_accumulator","docs":["New telecoin accumulator value."],"type":{"defined":{"name":"Accumulator"}}},{"name":"protocol_telecoin_fees","docs":["Telecoin fees sent to the protocol staking wallet."],"type":{"defined":{"name":"TokenAmount"}}}]}},{"name":"RevShareSlot","type":{"kind":"struct","fields":[{"name":"sink","type":{"defined":{"name":"ProtocolFeeSink"}}},{"name":"share_of_protocol_fee_pct","docs":["This partner's share of the protocol fee as a percentage (0-100).","","The fee hierarchy is:","1. A trade incurs a **trading fee** (e.g. 1% of trade volume).","2. The protocol takes a share of that trading fee","([DevConfigFees::dbc_protocol_share_of_trading_fee_pct] or","[DevConfigFees::amm_protocol_share_of_trading_fee_pct], e.g. 75%).","3. This field is a percentage of *that* protocol share (e.g. 10%).","","Concrete example: 1% trading fee, 75% protocol share, 10% revshare →","effective revshare = 1% × 75% × 10% = 0.075% of trade volume."],"type":"u8"}]}},{"name":"SetDevConfigDevEvent","docs":["Emitted whenever an admin updates the fee sink on a dev config."],"type":{"kind":"struct","fields":[{"name":"telecoin_id","docs":["The Printr Telecoin ID whose dev config was updated."],"type":{"defined":{"name":"TelecoinId"}}},{"name":"telecoin_mint","docs":["The telecoin mint whose dev config was updated."],"type":"pubkey"},{"name":"previous_fee_sink","docs":["The fee sink before the update."],"type":{"defined":{"name":"FeeSink"}}},{"name":"new_fee_sink","docs":["The fee sink after the update."],"type":{"defined":{"name":"FeeSink"}}}]}},{"name":"SetDevConfigFeesParams","type":{"kind":"struct","fields":[{"name":"dbc_protocol_share_of_trading_fee_pct","type":"u8"},{"name":"amm_protocol_share_of_trading_fee_pct","type":"u8"}]}},{"name":"SetPeerConfParams","docs":["Parameters for the [SetPeerConf] instruction."],"type":{"kind":"struct","fields":[{"name":"enable_lz","type":{"option":"bool"}},{"name":"set_as_default","type":{"option":"bool"}}]}},{"name":"SetTelecoinTeleportConfParams","docs":["Parameters for the [SetTelecoinTeleportConf] instruction."],"type":{"kind":"struct","fields":[{"name":"set_lz_peer_config","type":{"option":{"defined":{"name":"TeleportPathConfig"}}}}]}},{"name":"SqrtBitshiftedPrice","docs":["This is a sqrt and bitshifted price ready to be compared to Meteora prices."],"type":{"kind":"struct","fields":[{"name":"price","docs":["The inner raw value."],"type":"u128"}]}},{"name":"StakePosition","docs":["Individual user's stake position in a staking pool.","","PDA seeds:","```ignore","[","SEED_PREFIX_FOR_STAKE_POSITION,","telecoin_mint.as_ref(),","owner.as_ref(),","position_nonce.to_le_bytes().as_ref(),","]","```","","The position_nonce allows users to have multiple stake positions per telecoin."],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump seed for PDA derivation."],"type":"u8"},{"name":"position_nonce","docs":["Position nonce - allows multiple positions per user per telecoin.","","Stored as u32 and used in PDA derivation as little-endian bytes (to_le_bytes()).","There can be only one position per (telecoin, owner, position_nonce) tuple at the time.","However, position nonces can be reused after a position is closed (unlocked and fully","claimed)."],"type":"u32"},{"name":"staked","docs":["Amount of telecoin staked."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"lock_period","docs":["Lock period selected by user."],"type":{"defined":{"name":"LockPeriod"}}},{"name":"staked_at_clock_timestamp","docs":["Timestamp from Clock sysvar when the stake was created."],"type":"i64"},{"name":"quote_token_last_accumulator","docs":["Quote token accumulator value when user last claimed or staked."],"type":{"defined":{"name":"Accumulator"}}},{"name":"telecoin_last_accumulator","docs":["Telecoin accumulator value when user last claimed or staked."],"type":{"defined":{"name":"Accumulator"}}}]}},{"name":"StakingPool","docs":["One pool per telecoin. Users stake the telecoin and earn rewards in multiple tokens:","","- Quote token trading fees","- Telecoin trading fees","","PDA seeds: `[b\"staking_pool\", telecoin_mint.as_ref()]`","","Note: Mint addresses can be derived from telecoin_id, so they're not stored here."],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump seed for PDA derivation."],"type":"u8"},{"name":"refreshed_at_clock_slot","docs":["Last slot when the pool was refreshed."],"type":"u64"},{"name":"telecoin_id","docs":["Telecoin ID this pool is associated with."],"type":{"defined":{"name":"TelecoinId"}}},{"name":"total_weighted_stake","docs":["Total weighted stake across all users.","","weighted_stake = sum(user_staked_amount × lock_multiplier_bps / 10000)"],"type":"u128"},{"name":"quote_token_accumulator","docs":["Accumulator for quote token rewards."],"type":{"defined":{"name":"Accumulator"}}},{"name":"telecoin_accumulator","docs":["Accumulator for telecoin rewards."],"type":{"defined":{"name":"Accumulator"}}},{"name":"last_telecoin_vault_balance","docs":["Last observed telecoin vault balance for calculating new rewards."],"type":{"defined":{"name":"TokenAmount"}}}]}},{"name":"SwapEstimate","type":{"kind":"struct","fields":[{"name":"actual_input","docs":["What goes into a pool (either bonding curve or AMM).","Does not include the fees."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"input_fee","docs":["What's spent on fees. (Input)"],"type":{"defined":{"name":"TokenAmount"}}},{"name":"output_fee","docs":["What's spent on fees. (Output)"],"type":{"defined":{"name":"TokenAmount"}}},{"name":"output","docs":["What comes out of the pool.","Does not include the fees."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"next_price","docs":["The sqrt price after the swap."],"type":{"defined":{"name":"SqrtBitshiftedPrice"}}},{"name":"current_price","docs":["The sqrt price after the swap."],"type":{"defined":{"name":"SqrtBitshiftedPrice"}}}]}},{"name":"SwapIntent","docs":["Represents user's intent to swap tokens in Printr ecosystem.","We translate this intent to specific Meteora CPIs."],"type":{"kind":"enum","variants":[{"name":"SpendQuote","fields":[{"name":"sell","docs":["Quote amount"],"type":{"defined":{"name":"TokenAmount"}}},{"name":"max_price","docs":["In Printr we apply slippage to price."],"type":{"defined":{"name":"SqrtBitshiftedPrice"}}}]},{"name":"SellTelecoin","fields":[{"name":"sell","docs":["telecoin amount"],"type":{"defined":{"name":"TokenAmount"}}},{"name":"min_price","docs":["In Printr we apply slippage to price."],"type":{"defined":{"name":"SqrtBitshiftedPrice"}}}]},{"name":"SpendAllQuote","fields":[{"name":"special_value","docs":["Must be equal to [printr_sdk::consts::SPEND_ALL_SPECIAL_VALUE]."],"type":"u64"},{"name":"max_price","docs":["In Printr we apply slippage to price."],"type":{"defined":{"name":"SqrtBitshiftedPrice"}}}]}]}},{"name":"TelecoinId","docs":["Printr's Token ID is a hash of the ABI-encoded telecoin params."],"type":{"kind":"struct","fields":[{"array":["u8",32]}]}},{"name":"TelecoinParams","docs":["The core primitive of the Printr protocol.","","These telecoin params uniquely identify a token launched with Printr."],"type":{"kind":"struct","fields":[{"name":"salt","type":{"array":["u8",32]}},{"name":"dev_addresses","docs":["# Note","","At this point, `dev_addresses` has become a misnomer.","It contains more information than that."],"type":"bytes"},{"name":"name","type":{"array":["u8",32]}},{"name":"symbol","type":{"array":["u8",32]}},{"name":"packed_params","type":{"array":["u8",32]}},{"name":"chains","type":{"vec":{"array":["u8",32]}}},{"name":"quote_pairs","type":{"vec":{"array":["u8",32]}}},{"name":"quote_prices","type":"bytes"}]}},{"name":"TelecoinParamsCompact","docs":["On Solana we're dealing with txs size limits.","Being able to compress the telecoin params into a smaller structure helps us submit smaller txs.","","You can always convert this back to [TelecoinParams]."],"type":{"kind":"struct","fields":[{"name":"version","docs":["It's nice to version this in case we need to further compact it or do other changes."],"type":"u8"},{"name":"data","docs":["The compact representation of the telecoin params."],"type":"bytes"}]}},{"name":"TelecoinTeleportConfig","docs":["Overrides default configuration for a specific telecoin with respect to omnichain transfers."],"type":{"kind":"struct","fields":[{"name":"lz_peer","docs":["If set then only the specified peer can be used to teleport this telecoin."],"type":{"defined":{"name":"TeleportPathConfig"}}}]}},{"name":"TeleportPathConfig","docs":["Defines how teleports should be handled."],"type":{"kind":"enum","variants":[{"name":"Default"},{"name":"Disabled"},{"name":"Peers","fields":[{"name":"teleport_any_direction1","docs":["This peer can be used for teleports in any direction: mint and burn."],"type":"pubkey"},{"name":"teleport_any_direction2","docs":["This peer can be used for teleports in any direction: mint and burn."],"type":"pubkey"},{"name":"teleport_in_only","docs":["This peer can be used for teleports in only: mint"],"type":"pubkey"}]}]}},{"name":"TeleportWithLzEvent","docs":["Event emitted by the [TeleportWithLz] instruction."],"type":{"kind":"struct","fields":[{"name":"guid","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"lz_destination_endpoint_id","type":"u32"},{"name":"telecoin_id","type":{"defined":{"name":"TelecoinId"}}},{"name":"destination_recipient_address","type":{"array":["u8",32]}},{"name":"destination_peer_address","type":{"array":["u8",32]}},{"name":"source_user_address","type":"pubkey"},{"name":"teleporting","type":{"defined":{"name":"TokenAmount"}}}]}},{"name":"TeleportWithLzParams","docs":["Parameters for the [TeleportWithLz] instruction.","Used to construct [OutgoingMessage]."],"type":{"kind":"struct","fields":[{"name":"to_send","docs":["Burns this much telecoin tokens and mints them again on the other chain."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"destination_recipient_address","docs":["Who to mint the telecoin tokens to."],"type":{"array":["u8",32]}},{"name":"telecoin_id","docs":["Printr Telecoin ID derives the telecoin mint."],"type":{"defined":{"name":"TelecoinId"}}},{"name":"lamports_gas","docs":["How much to pay LZ for the transaction."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"lz_options","docs":[""],"type":"bytes"},{"name":"lz_destination_endpoint_id","docs":["The chain ID of the destination chain.","",""],"type":"u32"}]}},{"name":"TokenAmount","docs":["A token amount in its smallest denomination as seen in a token account."],"type":{"kind":"struct","fields":[{"name":"amount","docs":["Wraps around this value to give it a meaning."],"type":"u64"}]}},{"name":"VirtualPool","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"volatility_tracker","docs":["volatility tracker"],"type":{"defined":{"name":"VolatilityTracker"}}},{"name":"config","docs":["config key"],"type":"pubkey"},{"name":"creator","docs":["creator"],"type":"pubkey"},{"name":"base_mint","docs":["base mint"],"type":"pubkey"},{"name":"base_vault","docs":["base vault"],"type":"pubkey"},{"name":"quote_vault","docs":["quote vault"],"type":"pubkey"},{"name":"base_reserve","docs":["base reserve"],"type":"u64"},{"name":"quote_reserve","docs":["quote reserve"],"type":"u64"},{"name":"protocol_base_fee","docs":["protocol base fee"],"type":"u64"},{"name":"protocol_quote_fee","docs":["protocol quote fee"],"type":"u64"},{"name":"partner_base_fee","docs":["partner base fee"],"type":"u64"},{"name":"partner_quote_fee","docs":["trading quote fee"],"type":"u64"},{"name":"sqrt_price","docs":["current price"],"type":"u128"},{"name":"activation_point","docs":["Activation point"],"type":"u64"},{"name":"pool_type","docs":["pool type, spl token or token2022"],"type":"u8"},{"name":"is_migrated","docs":["is migrated"],"type":"u8"},{"name":"is_partner_withdraw_surplus","docs":["is partner withdraw surplus"],"type":"u8"},{"name":"is_protocol_withdraw_surplus","docs":["is protocol withdraw surplus"],"type":"u8"},{"name":"migration_progress","docs":["migration progress"],"type":"u8"},{"name":"is_withdraw_leftover","docs":["is withdraw leftover"],"type":"u8"},{"name":"is_creator_withdraw_surplus","docs":["is creator withdraw surplus"],"type":"u8"},{"name":"migration_fee_withdraw_status","docs":["migration fee withdraw status","bit 1 (0b010) creator","bit 2 (0b100) partner"],"type":"u8"},{"name":"metrics","docs":["pool metrics"],"type":{"defined":{"name":"PoolMetrics"}}},{"name":"finish_curve_timestamp","docs":["The time curve is finished"],"type":"u64"},{"name":"creator_base_fee","docs":["creator base fee"],"type":"u64"},{"name":"creator_quote_fee","docs":["creator quote fee"],"type":"u64"},{"name":"legacy_creation_fee_bits","docs":["legacy creation fee bits, we dont use this now"],"type":"u8"},{"name":"creation_fee_bits","docs":["pool creation fee claim status"],"type":"u8"},{"name":"has_swap","docs":["Cached flag"],"type":"u8"},{"name":"_padding_0","docs":["Padding for further use"],"type":{"array":["u8",5]}},{"name":"protocol_liquidity_migration_fee_bps","type":"u16"},{"name":"_padding_1","type":{"array":["u8",6]}},{"name":"protocol_migration_base_fee_amount","type":"u64"},{"name":"protocol_migration_quote_fee_amount","type":"u64"},{"name":"_padding_2","docs":["Padding for further use"],"type":{"array":["u64",3]}}]}},{"name":"VolatilityTracker","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"last_update_timestamp","type":"u64"},{"name":"padding","type":{"array":["u8",8]}},{"name":"sqrt_price_reference","type":"u128"},{"name":"volatility_accumulator","type":"u128"},{"name":"volatility_reference","type":"u128"}]}}]} \ No newline at end of file diff --git a/idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json b/idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json new file mode 100644 index 0000000..d2804b5 --- /dev/null +++ b/idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json @@ -0,0 +1 @@ +{"address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ","metadata":{"name":"pump_fees","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"claim_social_fee_pda","discriminator":[225,21,251,133,161,30,199,226],"accounts":[{"name":"recipient","writable":true},{"name":"social_fee_pda","writable":true},{"name":"fee_program_global","pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"social_claim_authority","signer":true,"relations":["fee_program_global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"user_id","type":"string"},{"name":"platform","type":"u8"}],"returns":{"option":{"defined":{"name":"SocialFeePdaClaimed"}}}},{"name":"claim_social_fee_pda_v2","discriminator":[17,77,240,134,58,188,53,149],"accounts":[{"name":"recipient","writable":true},{"name":"social_fee_pda","writable":true},{"name":"quote_mint","docs":["Quote mint for claim"],"writable":true},{"name":"associated_social_fee_pda","writable":true,"pda":{"seeds":[{"kind":"account","path":"social_fee_pda"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"associated_recipient","writable":true,"pda":{"seeds":[{"kind":"account","path":"recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"quote_token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"fee_program_global","pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"social_claim_authority","signer":true,"relations":["fee_program_global"]},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"user_id","type":"string"},{"name":"platform","type":"u8"}],"returns":{"option":{"defined":{"name":"SocialFeePdaClaimed"}}}},{"name":"crank_donation_fee_pda","discriminator":[220,10,189,167,169,17,25,69],"accounts":[{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"payer","docs":["Pays rent when [`temp_wsol_token_account`] is created (`init_if_needed`); receives rent when it is closed after the relay CPI."],"writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"fee_program_global","pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"donation_fee_pda","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,111,110,97,116,105,111,110,45,102,101,101,45,112,100,97]},{"kind":"account","path":"donation_fee_pda.base_mint","account":"DonationFeePda"},{"kind":"account","path":"donation_fee_pda.config_id","account":"DonationFeePda"}]}},{"name":"quote_mint","docs":["Quote mint from donation fee pda."],"writable":true},{"name":"donation_fee_pda_ata","docs":["WSOL ATA owned by `donation_fee_pda`."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"donation_fee_pda"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"donation_relay_program","address":"RLAYHr9TRFcKB2ubYQhspcnXiaGpaVzNQvHytt47RZu"},{"name":"donation_relay_event_authority"},{"name":"mint_whitelist"},{"name":"epoch_tracker","writable":true},{"name":"debouncer","writable":true},{"name":"debouncer_ata","writable":true}],"args":[]},{"name":"create_donation_fee_pda","discriminator":[244,139,16,88,14,255,122,26],"accounts":[{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"fee_program_global","pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"donation_fee_pda","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,111,110,97,116,105,111,110,45,102,101,101,45,112,100,97]},{"kind":"account","path":"base_mint"},{"kind":"account","path":"config_id"}]}},{"name":"config_id","docs":["stored on the PDA, so distinct `config_id`s for the same `base_mint` derive distinct addresses."]},{"name":"base_mint"},{"name":"bonding_curve","pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pool"},{"name":"sharing_config","docs":["(derived from `[SHARING_CONFIG_SEED, base_mint]`)"],"pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"base_mint"}]}}],"args":[]},{"name":"create_fee_sharing_config","docs":["Create Fee Sharing Config"],"discriminator":[195,78,86,76,111,52,251,213],"accounts":[{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"payer","writable":true,"signer":true},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"mint"},{"name":"sharing_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"mint"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"pump_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pool","writable":true,"optional":true},{"name":"pump_amm_program","optional":true,"address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"pump_amm_event_authority","optional":true,"pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}}}],"args":[]},{"name":"create_social_fee_pda","discriminator":[144,224,59,211,78,248,202,220],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"social_fee_pda","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"fee_program_global","pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"user_id","type":"string"},{"name":"platform","type":"u8"}]},{"name":"extend_fee_config","docs":["Realloc the fee_config PDA to [`FeeConfig::CURRENT_SIZE`] (signer pays rent delta)."],"discriminator":[68,179,244,90,173,56,17,217],"accounts":[{"name":"fee_config","writable":true},{"name":"user","signer":true},{"name":"config_program_id"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"get_fees","docs":["Get Fees"],"discriminator":[231,37,126,85,207,91,63,52],"accounts":[{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"account","path":"config_program_id"}]}},{"name":"config_program_id"}],"args":[{"name":"is_pump_pool","type":"bool"},{"name":"market_cap_lamports","type":"u128"},{"name":"trade_size_lamports","type":"u64"},{"name":"is_new_quote_mint","type":{"defined":{"name":"OptionBool"}}}],"returns":{"defined":{"name":"Fees"}}},{"name":"initialize_buyback","discriminator":[250,129,236,160,227,36,103,134],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"buyback_vault","writable":true},{"name":"buyback_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"buyback_vault"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"mint"},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"index","type":"u8"}]},{"name":"initialize_fee_config","docs":["Initialize FeeConfig admin"],"discriminator":[62,162,20,133,121,65,145,27],"accounts":[{"name":"admin","writable":true,"signer":true,"address":"8LWu7QM2dGR1G8nKDHthckea57bkCzXyBTAKPJUBDHo8"},{"name":"fee_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"account","path":"config_program_id"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"config_program_id"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"initialize_fee_program_global","discriminator":[35,215,130,84,233,56,124,167],"accounts":[{"name":"authority","writable":true,"signer":true,"relations":["pump_global"]},{"name":"pump_global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"fee_program_global","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"social_claim_authority","type":"pubkey"},{"name":"disable_flags","type":"u8"},{"name":"claim_rate_limit","type":"u64"}]},{"name":"reset_fee_sharing_config","docs":["Reset Fee Sharing Config and distribute pending fees first"],"discriminator":[10,2,182,95,16,127,129,186],"accounts":[{"name":"new_admin"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"authority","signer":true},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"mint","relations":["sharing_config"]},{"name":"sharing_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve","pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"sharing_config"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"pump_program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"pump_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_amm_program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"amm_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}}},{"name":"wsol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"coin_creator_vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"sharing_config"}],"program":{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}}},{"name":"coin_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"coin_creator_vault_authority"},{"kind":"account","path":"token_program"},{"kind":"account","path":"wsol_mint"}],"program":{"kind":"account","path":"associated_token_program"}}}],"args":[]},{"name":"reset_fee_sharing_config_v2","docs":["Reset Fee Sharing Config and distribute pending fees first"],"discriminator":[169,245,17,209,94,91,248,128],"accounts":[{"name":"new_admin"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"authority","writable":true,"signer":true},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"mint","relations":["sharing_config"]},{"name":"sharing_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve","pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"sharing_config"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"pump_creator_vault"},{"kind":"account","path":"token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"pump_program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"pump_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_amm_program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"amm_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}}},{"name":"quote_mint"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"coin_creator_vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"sharing_config"}],"program":{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}}},{"name":"coin_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"coin_creator_vault_authority"},{"kind":"account","path":"token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}}],"args":[]},{"name":"revoke_fee_sharing_authority","docs":["Revoke Fee Sharing Authority"],"discriminator":[18,233,158,39,185,207,58,104],"accounts":[],"args":[]},{"name":"set_authority","discriminator":[133,250,37,21,110,163,26,121],"accounts":[{"name":"authority","writable":true,"signer":true,"relations":["fee_program_global"]},{"name":"fee_program_global","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_authority","type":"pubkey"}]},{"name":"set_claim_rate_limit","discriminator":[185,211,159,174,212,49,88,4],"accounts":[{"name":"authority","writable":true,"signer":true,"relations":["fee_program_global"]},{"name":"fee_program_global","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"claim_rate_limit","type":"u64"}]},{"name":"set_disable_flags","discriminator":[194,217,112,35,114,222,51,190],"accounts":[{"name":"authority","writable":true,"signer":true,"relations":["fee_program_global"]},{"name":"fee_program_global","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"disable_flags","type":"u8"}]},{"name":"set_social_claim_authority","discriminator":[147,54,184,154,136,237,185,153],"accounts":[{"name":"authority","writable":true,"signer":true,"relations":["fee_program_global"]},{"name":"fee_program_global","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"social_claim_authority","type":"pubkey"}]},{"name":"sweep_buyback","discriminator":[138,33,204,38,207,161,159,226],"accounts":[{"name":"destination","writable":true},{"name":"authority","writable":true,"signer":true,"relations":["buyback_vault"]},{"name":"buyback_vault","writable":true},{"name":"buyback_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"buyback_vault"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"destination_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"destination"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"mint"},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"index","type":"u8"}]},{"name":"transfer_fee_sharing_authority","docs":["Transfer Fee Sharing Authority"],"discriminator":[202,10,75,200,164,34,210,96],"accounts":[],"args":[]},{"name":"update_admin","docs":["Update admin (only callable by admin)"],"discriminator":[161,176,40,213,60,184,179,228],"accounts":[{"name":"admin","signer":true,"relations":["fee_config"]},{"name":"fee_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"account","path":"config_program_id"}]}},{"name":"new_admin"},{"name":"config_program_id"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"update_buyback_authority","discriminator":[66,98,113,202,121,37,219,107],"accounts":[{"name":"authority","writable":true,"signer":true,"relations":["fee_program_global"]},{"name":"fee_program_global","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"buyback_vault","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"index","type":"u8"},{"name":"new_authority","type":"pubkey"}]},{"name":"update_buyback_claim_rate_limit","discriminator":[186,95,135,190,255,199,137,170],"accounts":[{"name":"authority","writable":true,"signer":true,"relations":["fee_program_global"]},{"name":"fee_program_global","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"buyback_vault","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"index","type":"u8"},{"name":"claim_rate_limit","type":"i64"}]},{"name":"update_fee_config","docs":["Set/Replace fee parameters entirely (only callable by admin)"],"discriminator":[104,184,103,242,88,151,107,20],"accounts":[{"name":"fee_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"account","path":"config_program_id"}]}},{"name":"admin","signer":true,"relations":["fee_config"]},{"name":"config_program_id"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"flat_fees","type":{"defined":{"name":"Fees"}}}]},{"name":"update_fee_shares","docs":["Update Fee Shares, make sure to distribute all the fees before calling this"],"discriminator":[189,13,136,99,187,164,237,35],"accounts":[{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"authority","signer":true},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"mint","relations":["sharing_config"]},{"name":"sharing_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve","pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"sharing_config"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"pump_program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"pump_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_amm_program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"amm_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}}},{"name":"wsol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"coin_creator_vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"sharing_config"}],"program":{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}}},{"name":"coin_creator_vault_ata","writable":true}],"args":[{"name":"shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}}]},{"name":"update_fee_shares_v2","docs":["Update Fee Shares, make sure to distribute all the fees before calling this"],"discriminator":[111,251,49,6,78,78,106,18],"accounts":[{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"authority","writable":true,"signer":true},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"mint","relations":["sharing_config"]},{"name":"sharing_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve","pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"sharing_config"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"pump_creator_vault"},{"kind":"account","path":"token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"pump_program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"pump_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_amm_program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"amm_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}}},{"name":"quote_mint"},{"name":"token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"coin_creator_vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"sharing_config"}],"program":{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}}},{"name":"coin_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"coin_creator_vault_authority"},{"kind":"account","path":"token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}}],"args":[{"name":"shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}}]},{"name":"update_stable_fee_config","docs":["Set/Replace fee parameters entirely (only callable by admin)"],"discriminator":[107,169,100,179,134,155,146,221],"accounts":[{"name":"fee_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"account","path":"config_program_id"}]}},{"name":"admin","signer":true,"relations":["fee_config"]},{"name":"config_program_id"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}}]},{"name":"upsert_fee_tiers","docs":["Update or expand fee tiers (only callable by admin)"],"discriminator":[227,23,150,12,77,86,94,4],"accounts":[{"name":"fee_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"account","path":"config_program_id"}]}},{"name":"admin","signer":true,"relations":["fee_config"]},{"name":"config_program_id"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"offset","type":"u8"}]},{"name":"upsert_stable_fee_tiers","docs":["Update or expand fee tiers (only callable by admin)"],"discriminator":[181,160,162,252,74,76,224,221],"accounts":[{"name":"fee_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"account","path":"config_program_id"}]}},{"name":"admin","signer":true,"relations":["fee_config"]},{"name":"config_program_id"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"offset","type":"u8"}]}],"accounts":[{"name":"BondingCurve","discriminator":[23,183,248,55,96,216,172,96]},{"name":"BuybackVault","discriminator":[153,166,71,144,179,189,137,251]},{"name":"DonationFeePda","discriminator":[246,197,96,9,193,30,93,115]},{"name":"FeeConfig","discriminator":[143,52,146,187,219,123,76,155]},{"name":"FeeProgramGlobal","discriminator":[162,165,245,49,29,37,55,242]},{"name":"Global","discriminator":[167,232,232,177,200,108,114,127]},{"name":"Pool","discriminator":[241,154,109,4,17,177,109,188]},{"name":"SharingConfig","discriminator":[216,74,9,0,56,140,93,75]},{"name":"SocialFeePda","discriminator":[139,96,53,17,42,169,206,150]}],"events":[{"name":"CreateFeeSharingConfigEvent","discriminator":[133,105,170,200,184,116,251,88]},{"name":"DonationFeePdaCranked","discriminator":[30,208,107,93,177,0,223,78]},{"name":"DonationFeePdaCreated","discriminator":[94,20,137,239,35,77,225,235]},{"name":"ExtendFeeConfigEvent","discriminator":[226,203,224,35,153,10,88,51]},{"name":"InitializeFeeConfigEvent","discriminator":[89,138,244,230,10,56,226,126]},{"name":"InitializeFeeProgramGlobalEvent","discriminator":[40,233,156,78,95,0,8,199]},{"name":"ResetFeeSharingConfigEvent","discriminator":[203,204,151,226,120,55,214,243]},{"name":"SetAuthorityEvent","discriminator":[18,175,132,66,208,201,87,242]},{"name":"SetClaimRateLimitEvent","discriminator":[13,143,143,235,181,19,51,40]},{"name":"SetDisableFlagsEvent","discriminator":[5,8,179,65,49,55,145,126]},{"name":"SetSocialClaimAuthorityEvent","discriminator":[60,118,127,132,239,52,254,14]},{"name":"SocialFeePdaClaimed","discriminator":[50,18,193,65,237,210,234,236]},{"name":"SocialFeePdaCreated","discriminator":[183,183,218,147,24,124,137,169]},{"name":"SweepBuybackEvent","discriminator":[43,56,42,214,153,57,166,137]},{"name":"UpdateAdminEvent","discriminator":[225,152,171,87,246,63,66,234]},{"name":"UpdateFeeConfigEvent","discriminator":[90,23,65,35,62,244,188,208]},{"name":"UpdateFeeSharesEvent","discriminator":[21,186,196,184,91,228,225,203]},{"name":"UpdateStableFeeConfigEvent","discriminator":[94,5,43,237,103,147,232,245]},{"name":"UpsertFeeTiersEvent","discriminator":[171,89,169,187,122,186,33,204]},{"name":"UpsertStableFeeTiersEvent","discriminator":[232,237,237,52,98,146,73,243]}],"errors":[{"code":6000,"name":"UnauthorizedProgram","msg":"Only Pump and PumpSwap programs can call this instruction"},{"code":6001,"name":"InvalidAdmin","msg":"Invalid admin"},{"code":6002,"name":"NoFeeTiers","msg":"No fee tiers provided"},{"code":6003,"name":"TooManyFeeTiers","msg":"format"},{"code":6004,"name":"OffsetNotContinuous","msg":"The offset should be <= fee_config.fee_tiers.len()"},{"code":6005,"name":"FeeTiersNotSorted","msg":"Fee tiers must be sorted by market cap threshold (ascending)"},{"code":6006,"name":"InvalidFeeTotal","msg":"Fee total must not exceed 10_000bps"},{"code":6007,"name":"InvalidSharingConfig","msg":"Invalid Sharing Config"},{"code":6008,"name":"InvalidPool","msg":"Invalid Pool"},{"code":6009,"name":"SharingConfigAdminRevoked","msg":"Sharing config authority has been revoked - sharing config can only be updated once"},{"code":6010,"name":"NoShareholders","msg":"No shareholders provided"},{"code":6011,"name":"TooManyShareholders","msg":"format"},{"code":6012,"name":"DuplicateShareholder","msg":"Duplicate shareholder address"},{"code":6013,"name":"NotEnoughRemainingAccounts","msg":"Not enough remaining accounts"},{"code":6014,"name":"InvalidShareTotal","msg":"Invalid share total - must equal 10_000 basis points"},{"code":6015,"name":"ShareCalculationOverflow","msg":"Share calculation overflow"},{"code":6016,"name":"NotAuthorized","msg":"The given account is not authorized to execute this instruction."},{"code":6017,"name":"ZeroShareNotAllowed","msg":"Shareholder cannot have zero share"},{"code":6018,"name":"SharingConfigNotActive","msg":"Fee sharing config is not active"},{"code":6019,"name":"AmmAccountsRequiredForGraduatedCoin","msg":"AMM accounts are required for graduated coins"},{"code":6020,"name":"ShareholderAccountMismatch","msg":"Remaining account key doesn't match shareholder address"},{"code":6021,"name":"FeatureDeactivated","msg":"Feature is currently deactivated"},{"code":6022,"name":"UserIdTooLong","msg":"User ID exceeds maximum length"},{"code":6023,"name":"DeprecatedInstruction","msg":"Instruction is deprecated"},{"code":6024,"name":"FeeSharesAlreadyUpdated","msg":"Reward split can only be updated once"},{"code":6025,"name":"MathOverflow","msg":"Math overflow"},{"code":6026,"name":"InvalidBuybackIndex","msg":"Invalid buybackindex"},{"code":6027,"name":"ClaimRateLimitExceeded","msg":"Claim rate limit exceeded"},{"code":6028,"name":"InvalidFeeConfigAccount","msg":"Account is not a valid FeeConfig for this instruction"},{"code":6029,"name":"AccountTypeNotSupported","msg":"Account type not supported"},{"code":6030,"name":"InvalidMint","msg":"Mint does not match quote mint"}],"types":[{"name":"BondingCurve","type":{"kind":"struct","fields":[{"name":"virtual_token_reserves","type":"u64"},{"name":"virtual_quote_reserves","type":"u64"},{"name":"real_token_reserves","type":"u64"},{"name":"real_quote_reserves","type":"u64"},{"name":"token_total_supply","type":"u64"},{"name":"complete","type":"bool"},{"name":"creator","type":"pubkey"},{"name":"is_mayhem_mode","type":"bool"}]}},{"name":"BuybackVault","type":{"kind":"struct","fields":[{"name":"authority","type":"pubkey"},{"name":"total_claimed","type":"u64"},{"name":"total_claimed_token1","type":"u64"},{"name":"total_claimed_token2","type":"u64"},{"name":"last_claimed","type":"i64"},{"name":"claim_rate_limit","type":"i64"},{"name":"_reserved","type":{"array":["u8",128]}}]}},{"name":"ConfigStatus","type":{"kind":"enum","variants":[{"name":"Paused"},{"name":"Active"}]}},{"name":"CreateFeeSharingConfigEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"bonding_curve","type":"pubkey"},{"name":"pool","type":{"option":"pubkey"}},{"name":"sharing_config","type":"pubkey"},{"name":"admin","type":"pubkey"},{"name":"initial_shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}},{"name":"status","type":{"defined":{"name":"ConfigStatus"}}}]}},{"name":"DonationFeePda","docs":["Escrow PDA for donation relay: one per (mint, donation campaign `config_id`)."],"type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"version","type":"u8"},{"name":"config_id","type":"pubkey"},{"name":"base_mint","type":"pubkey"},{"name":"quote_mint","type":"pubkey"},{"name":"creator","type":"pubkey"},{"name":"total_donated","type":"u64"},{"name":"last_crank_ts","type":"i64"},{"name":"_reserved","type":{"array":["u8",64]}}]}},{"name":"DonationFeePdaCranked","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"signer","type":"pubkey"},{"name":"donation_fee_pda","type":"pubkey"},{"name":"config_id","type":"pubkey"},{"name":"base_mint","type":"pubkey"},{"name":"quote_mint","type":"pubkey"},{"name":"creator","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"DonationFeePdaCreated","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"created_by","type":"pubkey"},{"name":"donation_fee_pda","type":"pubkey"},{"name":"config_id","type":"pubkey"},{"name":"base_mint","type":"pubkey"},{"name":"quote_mint","type":"pubkey"},{"name":"creator","type":"pubkey"}]}},{"name":"ExtendFeeConfigEvent","type":{"kind":"struct","fields":[{"name":"fee_config","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"current_size","type":"u64"},{"name":"new_size","type":"u64"},{"name":"timestamp","type":"i64"}]}},{"name":"FeeConfig","type":{"kind":"struct","fields":[{"name":"bump","docs":["The bump for the PDA"],"type":"u8"},{"name":"admin","docs":["The admin account that can update the fee config"],"type":"pubkey"},{"name":"flat_fees","docs":["The flat fees for non-pump pools"],"type":{"defined":{"name":"Fees"}}},{"name":"fee_tiers","docs":["The fee tiers"],"type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"stable_fee_tiers","docs":["The fee tiers"],"type":{"vec":{"defined":{"name":"FeeTier"}}}}]}},{"name":"FeeProgramGlobal","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"authority","type":"pubkey"},{"name":"disable_flags","type":"u8"},{"name":"social_claim_authority","type":"pubkey"},{"name":"claim_rate_limit","type":"u64"},{"name":"_reserved","type":{"array":["u8",256]}}]}},{"name":"FeeTier","type":{"kind":"struct","fields":[{"name":"market_cap_lamports_threshold","type":"u128"},{"name":"fees","type":{"defined":{"name":"Fees"}}}]}},{"name":"Fees","type":{"kind":"struct","fields":[{"name":"lp_fee_bps","type":"u64"},{"name":"protocol_fee_bps","type":"u64"},{"name":"creator_fee_bps","type":"u64"}]}},{"name":"Global","type":{"kind":"struct","fields":[{"name":"initialized","type":"bool"},{"name":"authority","type":"pubkey"},{"name":"fee_recipient","type":"pubkey"},{"name":"initial_virtual_token_reserves","type":"u64"},{"name":"initial_virtual_sol_reserves","type":"u64"},{"name":"initial_real_token_reserves","type":"u64"},{"name":"token_total_supply","type":"u64"},{"name":"fee_basis_points","type":"u64"},{"name":"withdraw_authority","type":"pubkey"},{"name":"enable_migrate","type":"bool"},{"name":"pool_migration_fee","type":"u64"},{"name":"creator_fee_basis_points","type":"u64"},{"name":"fee_recipients","type":{"array":["pubkey",7]}},{"name":"set_creator_authority","type":"pubkey"},{"name":"admin_set_creator_authority","type":"pubkey"},{"name":"create_v2_enabled","type":"bool"},{"name":"whitelist_pda","type":"pubkey"},{"name":"reserved_fee_recipient","type":"pubkey"},{"name":"mayhem_mode_enabled","type":"bool"},{"name":"reserved_fee_recipients","type":{"array":["pubkey",7]}}]}},{"name":"InitializeFeeConfigEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin","type":"pubkey"},{"name":"fee_config","type":"pubkey"}]}},{"name":"InitializeFeeProgramGlobalEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"authority","type":"pubkey"},{"name":"social_claim_authority","type":"pubkey"},{"name":"disable_flags","type":"u8"},{"name":"claim_rate_limit","type":"u64"}]}},{"name":"OptionBool","type":{"kind":"struct","fields":["bool"]}},{"name":"Pool","type":{"kind":"struct","fields":[{"name":"pool_bump","type":"u8"},{"name":"index","type":"u16"},{"name":"creator","type":"pubkey"},{"name":"base_mint","type":"pubkey"},{"name":"quote_mint","type":"pubkey"},{"name":"lp_mint","type":"pubkey"},{"name":"pool_base_token_account","type":"pubkey"},{"name":"pool_quote_token_account","type":"pubkey"},{"name":"lp_supply","type":"u64"},{"name":"coin_creator","type":"pubkey"},{"name":"is_mayhem_mode","type":"bool"}]}},{"name":"ResetFeeSharingConfigEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"sharing_config","type":"pubkey"},{"name":"old_admin","type":"pubkey"},{"name":"old_shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}},{"name":"new_admin","type":"pubkey"},{"name":"new_shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}},{"name":"old_version","type":"u8"},{"name":"new_version","type":"u8"}]}},{"name":"SetAuthorityEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"old_authority","type":"pubkey"},{"name":"new_authority","type":"pubkey"}]}},{"name":"SetClaimRateLimitEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"claim_rate_limit","type":"u64"}]}},{"name":"SetDisableFlagsEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"disable_flags","type":"u8"}]}},{"name":"SetSocialClaimAuthorityEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"social_claim_authority","type":"pubkey"}]}},{"name":"Shareholder","type":{"kind":"struct","fields":[{"name":"address","type":"pubkey"},{"name":"share_bps","type":"u16"}]}},{"name":"SharingConfig","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"version","type":"u8"},{"name":"status","type":{"defined":{"name":"ConfigStatus"}}},{"name":"mint","type":"pubkey"},{"name":"admin","type":"pubkey"},{"name":"admin_revoked","type":"bool"},{"name":"shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}}]}},{"name":"SocialFeePda","docs":["Platform identifier: 0=pump, 1=twitter, etc."],"type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"version","type":"u8"},{"name":"user_id","docs":["Max 20 characters to fit u64::MAX (18,446,744,073,709,551,615) as a string.","Actual storage: 4 bytes (length prefix) + 20 bytes (content) = 24 bytes."],"type":"string"},{"name":"platform","type":"u8"},{"name":"total_claimed","type":"u64"},{"name":"last_claimed","type":"u64"},{"name":"_reserved","type":{"array":["u8",128]}}]}},{"name":"SocialFeePdaClaimed","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"user_id","type":"string"},{"name":"platform","type":"u8"},{"name":"social_fee_pda","type":"pubkey"},{"name":"recipient","type":"pubkey"},{"name":"social_claim_authority","type":"pubkey"},{"name":"amount_claimed","type":"u64"},{"name":"claimable_before","type":"u64"},{"name":"lifetime_claimed","type":"u64"},{"name":"recipient_balance_before","type":"u64"},{"name":"recipient_balance_after","type":"u64"},{"name":"quote_mint","type":"pubkey"}]}},{"name":"SocialFeePdaCreated","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"user_id","type":"string"},{"name":"platform","type":"u8"},{"name":"social_fee_pda","type":"pubkey"},{"name":"created_by","type":"pubkey"}]}},{"name":"SweepBuybackEvent","type":{"kind":"struct","fields":[{"name":"index","type":"u8"},{"name":"sol_amount","type":"u64"},{"name":"token_amount","type":"u64"},{"name":"destination","type":"pubkey"},{"name":"buyback_vault","type":"pubkey"},{"name":"mint","type":"pubkey"}]}},{"name":"UpdateAdminEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"old_admin","type":"pubkey"},{"name":"new_admin","type":"pubkey"}]}},{"name":"UpdateFeeConfigEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin","type":"pubkey"},{"name":"fee_config","type":"pubkey"},{"name":"fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"flat_fees","type":{"defined":{"name":"Fees"}}}]}},{"name":"UpdateFeeSharesEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"sharing_config","type":"pubkey"},{"name":"admin","type":"pubkey"},{"name":"new_shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}},{"name":"version","type":"u8"}]}},{"name":"UpdateStableFeeConfigEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin","type":"pubkey"},{"name":"fee_config","type":"pubkey"},{"name":"stable_fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"flat_fees","type":{"defined":{"name":"Fees"}}}]}},{"name":"UpsertFeeTiersEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin","type":"pubkey"},{"name":"fee_config","type":"pubkey"},{"name":"fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"offset","type":"u8"}]}},{"name":"UpsertStableFeeTiersEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin","type":"pubkey"},{"name":"fee_config","type":"pubkey"},{"name":"stable_fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"offset","type":"u8"}]}}],"constants":[{"name":"AMM_CREATOR_VAULT_AUTHORITY_SEED","type":{"array":["u8",13]},"value":"[99, 114, 101, 97, 116, 111, 114, 95, 118, 97, 117, 108, 116]"},{"name":"BUYBACK_VAULT_SEED","type":{"array":["u8",13]},"value":"[98, 117, 121, 98, 97, 99, 107, 45, 118, 97, 117, 108, 116]"},{"name":"DEBOUNCER_V1","type":"bytes","value":"[100, 101, 98, 111, 117, 110, 99, 101, 114, 95, 118, 49]"},{"name":"DONATION_FEE_PDA_SEED","type":{"array":["u8",16]},"value":"[100, 111, 110, 97, 116, 105, 111, 110, 45, 102, 101, 101, 45, 112, 100, 97]"},{"name":"EPOCH_TRACKER_V1","type":"bytes","value":"[101, 112, 111, 99, 104, 95, 116, 114, 97, 99, 107, 101, 114, 95, 118, 49]"},{"name":"FEE_CONFIG_SEED","type":"bytes","value":"[102, 101, 101, 95, 99, 111, 110, 102, 105, 103]"},{"name":"FEE_PROGRAM_GLOBAL_SEED","type":{"array":["u8",18]},"value":"[102, 101, 101, 45, 112, 114, 111, 103, 114, 97, 109, 45, 103, 108, 111, 98, 97, 108]"},{"name":"IX_DONATE_PUBKEY_CONFIG_ID_WITH_PAYER_V1","type":{"array":["u8",8]},"value":"[120, 217, 57, 241, 135, 104, 139, 184]"},{"name":"MAX_BUYBACK_INDEX","type":"u8","value":"8"},{"name":"MINT_WHITELIST_V1","type":"bytes","value":"[109, 105, 110, 116, 95, 119, 104, 105, 116, 101, 108, 105, 115, 116, 95, 118, 49]"},{"name":"PUMP_CREATOR_VAULT_SEED","type":{"array":["u8",13]},"value":"[99, 114, 101, 97, 116, 111, 114, 45, 118, 97, 117, 108, 116]"},{"name":"PUMP_GLOBAL_SEED","docs":["Bonding Curve Program Global Seed"],"type":{"array":["u8",6]},"value":"[103, 108, 111, 98, 97, 108]"},{"name":"SHARING_CONFIG_SEED","type":{"array":["u8",14]},"value":"[115, 104, 97, 114, 105, 110, 103, 45, 99, 111, 110, 102, 105, 103]"},{"name":"SOCIAL_FEE_PDA_SEED","type":{"array":["u8",14]},"value":"[115, 111, 99, 105, 97, 108, 45, 102, 101, 101, 45, 112, 100, 97]"}]} \ No newline at end of file diff --git a/idls/pump_fun.6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P.json b/idls/pump_fun.6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P.json new file mode 100644 index 0000000..670461a --- /dev/null +++ b/idls/pump_fun.6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P.json @@ -0,0 +1 @@ +{"address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P","metadata":{"name":"pump","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"add_quote_mint","discriminator":[111,121,21,56,40,24,94,209],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","writable":true,"signer":true,"relations":["global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"quote_mint","type":"pubkey"}]},{"name":"admin_set_creator","docs":["Allows Global::admin_set_creator_authority to override the bonding curve creator"],"discriminator":[69,25,171,142,57,239,13,4],"accounts":[{"name":"admin_set_creator_authority","signer":true,"relations":["global"]},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"mint"},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"creator","type":"pubkey"}]},{"name":"admin_set_idl_authority","discriminator":[8,217,96,231,144,104,192,5],"accounts":[{"name":"authority","signer":true,"relations":["global"]},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"idl_account","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"program_signer","pda":{"seeds":[]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"idl_authority","type":"pubkey"}]},{"name":"admin_update_token_incentives","discriminator":[209,11,115,87,213,23,124,204],"accounts":[{"name":"authority","writable":true,"signer":true,"relations":["global"]},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"global_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"mint"},{"name":"global_incentive_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"global_volume_accumulator"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"start_time","type":"i64"},{"name":"end_time","type":"i64"},{"name":"seconds_in_a_day","type":"i64"},{"name":"day_number","type":"u64"},{"name":"pump_token_supply_per_day","type":"u64"}]},{"name":"buy","docs":["Buys tokens from a bonding curve."],"discriminator":[102,6,61,18,1,218,235,234],"accounts":[{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"fee_recipient","writable":true},{"name":"mint"},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"associated_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"associated_user","writable":true},{"name":"user","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program"},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"bonding_curve.creator","account":"BondingCurve"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}],"program":{"kind":"account","path":"fee_program"}}},{"name":"fee_program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"}],"args":[{"name":"amount","type":"u64"},{"name":"max_sol_cost","type":"u64"},{"name":"track_volume","type":{"defined":{"name":"OptionBool"}}}]},{"name":"buy_exact_quote_in_v2","discriminator":[194,171,28,70,104,77,91,47],"accounts":[{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"base_mint"},{"name":"quote_mint"},{"name":"base_token_program"},{"name":"quote_token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"fee_recipient","writable":true},{"name":"associated_quote_fee_recipient","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"buyback_fee_recipient","writable":true},{"name":"associated_quote_buyback_fee_recipient","writable":true,"pda":{"seeds":[{"kind":"account","path":"buyback_fee_recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"base_mint"}]}},{"name":"associated_base_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"base_token_program"},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"associated_quote_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user","writable":true,"signer":true},{"name":"associated_base_user","writable":true},{"name":"associated_quote_user","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"bonding_curve.creator","account":"BondingCurve"}]}},{"name":"associated_creator_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"creator_vault"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"sharing_config","docs":["seeds; the account is intentionally not deserialized here because it may be uninitialized","for mints that have not created a fee sharing config. Handlers must check","`data_is_empty()` / owner before reading."],"pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[12,53,255,169,5,90,142,86,141,168,247,188,7,86,21,39,76,241,201,44,164,31,64,0,156,81,106,164,20,194,124,112]}}},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"associated_user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"account","path":"user_volume_accumulator"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}],"program":{"kind":"account","path":"fee_program"}}},{"name":"fee_program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"}],"args":[{"name":"spendable_quote_in","type":"u64"},{"name":"min_tokens_out","type":"u64"}]},{"name":"buy_exact_sol_in","docs":["Given a budget of spendable SOL, buy at least min_tokens_out tokens.","Fees are deducted from spendable_sol_in.","","# Quote formulas","Where:","- total_fee_bps = protocol_fee_bps + creator_fee_bps (creator_fee_bps is 0 if no creator)","- floor(a/b) = a / b (integer division)","- ceil(a/b) = (a + b - 1) / b","","SOL → tokens quote","To calculate tokens_out for a given spendable_sol_in:","1. net_sol = floor(spendable_sol_in * 10_000 / (10_000 + total_fee_bps))","2. fees = ceil(net_sol * protocol_fee_bps / 10_000) + ceil(net_sol * creator_fee_bps / 10_000) (creator_fee_bps is 0 if no creator)","3. if net_sol + fees > spendable_sol_in: net_sol = net_sol - (net_sol + fees - spendable_sol_in)","4. tokens_out = floor((net_sol - 1) * virtual_token_reserves / (virtual_sol_reserves + net_sol - 1))","","Reverse quote (tokens → SOL)","To calculate spendable_sol_in for a desired number of tokens:","1. net_sol = ceil(tokens * virtual_sol_reserves / (virtual_token_reserves - tokens)) + 1","2. spendable_sol_in = ceil(net_sol * (10_000 + total_fee_bps) / 10_000)","","Rent","Separately make sure the instruction's payer has enough SOL to cover rent for:","- creator_vault: rent.minimum_balance(0)","- user_volume_accumulator: rent.minimum_balance(UserVolumeAccumulator::LEN)"],"discriminator":[56,252,116,8,158,223,205,95],"accounts":[{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"fee_recipient","writable":true},{"name":"mint"},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"associated_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"associated_user","writable":true},{"name":"user","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program"},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"bonding_curve.creator","account":"BondingCurve"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}],"program":{"kind":"account","path":"fee_program"}}},{"name":"fee_program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"}],"args":[{"name":"spendable_sol_in","type":"u64"},{"name":"min_tokens_out","type":"u64"},{"name":"track_volume","type":{"defined":{"name":"OptionBool"}}}]},{"name":"buy_v2","discriminator":[184,23,238,97,103,197,211,61],"accounts":[{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"base_mint"},{"name":"quote_mint"},{"name":"base_token_program"},{"name":"quote_token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"fee_recipient","writable":true},{"name":"associated_quote_fee_recipient","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"buyback_fee_recipient","writable":true},{"name":"associated_quote_buyback_fee_recipient","writable":true,"pda":{"seeds":[{"kind":"account","path":"buyback_fee_recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"base_mint"}]}},{"name":"associated_base_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"base_token_program"},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"associated_quote_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user","writable":true,"signer":true},{"name":"associated_base_user","writable":true},{"name":"associated_quote_user","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"bonding_curve.creator","account":"BondingCurve"}]}},{"name":"associated_creator_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"creator_vault"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"sharing_config","docs":["seeds; the account is intentionally not deserialized here because it may be uninitialized","for mints that have not created a fee sharing config. Handlers must check","`data_is_empty()` / owner before reading."],"pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[12,53,255,169,5,90,142,86,141,168,247,188,7,86,21,39,76,241,201,44,164,31,64,0,156,81,106,164,20,194,124,112]}}},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"associated_user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"account","path":"user_volume_accumulator"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}],"program":{"kind":"account","path":"fee_program"}}},{"name":"fee_program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"}],"args":[{"name":"amount","type":"u64"},{"name":"max_sol_cost","type":"u64"}]},{"name":"claim_cashback","discriminator":[37,58,35,126,190,53,228,197],"accounts":[{"name":"user","writable":true},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"}],"args":[]},{"name":"claim_cashback_v2","discriminator":[122,243,204,65,94,116,29,55],"accounts":[{"name":"user","writable":true},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"quote_mint"},{"name":"quote_token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"associated_user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"account","path":"user_volume_accumulator"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"associated_quote_user","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"}],"args":[]},{"name":"claim_token_incentives","discriminator":[16,4,71,28,204,1,40,27],"accounts":[{"name":"user"},{"name":"user_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"global_incentive_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"global_volume_accumulator"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"mint","relations":["global_volume_accumulator"]},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"payer","writable":true,"signer":true}],"args":[]},{"name":"close_user_volume_accumulator","discriminator":[249,69,164,218,150,103,84,138],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"collect_creator_fee","docs":["Collects creator_fee from creator_vault to the coin creator account"],"discriminator":[20,22,86,123,198,28,219,132],"accounts":[{"name":"creator","writable":true},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"creator"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"collect_creator_fee_v2","docs":["Collects creator_fee from creator_vault to the coin creator account"],"discriminator":[207,17,138,242,4,34,19,56],"accounts":[{"name":"creator","writable":true},{"name":"creator_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"creator"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"creator"}]}},{"name":"creator_vault_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"creator_vault"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"quote_mint"},{"name":"quote_token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"create","docs":["Creates a new coin and bonding curve."],"discriminator":[24,30,200,40,5,28,7,119],"accounts":[{"name":"mint","writable":true,"signer":true},{"name":"mint_authority","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,45,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"associated_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"mpl_token_metadata","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"metadata","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]},{"kind":"account","path":"mint"}],"program":{"kind":"account","path":"mpl_token_metadata"}}},{"name":"user","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"creator","type":"pubkey"}]},{"name":"create_v2","docs":["Creates a new spl-22 coin and bonding curve."],"discriminator":[214,144,76,236,95,139,49,180],"accounts":[{"name":"mint","writable":true,"signer":true},{"name":"mint_authority","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,45,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"associated_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"user","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"mayhem_program_id","writable":true,"address":"MAyhSmzXzV1pTf7LsNkrNwkWKTo4ougAJ1PPg47MD4e"},{"name":"global_params","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,45,112,97,114,97,109,115]}],"program":{"kind":"const","value":[5,42,229,215,167,218,167,36,166,234,176,167,41,84,145,133,90,212,160,103,22,96,103,76,78,3,69,89,128,61,101,163]}}},{"name":"sol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,111,108,45,118,97,117,108,116]}],"program":{"kind":"const","value":[5,42,229,215,167,218,167,36,166,234,176,167,41,84,145,133,90,212,160,103,22,96,103,76,78,3,69,89,128,61,101,163]}}},{"name":"mayhem_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,97,121,104,101,109,45,115,116,97,116,101]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[5,42,229,215,167,218,167,36,166,234,176,167,41,84,145,133,90,212,160,103,22,96,103,76,78,3,69,89,128,61,101,163]}}},{"name":"mayhem_token_vault","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"creator","type":"pubkey"},{"name":"is_mayhem_mode","type":"bool"},{"name":"is_cashback_enabled","type":{"defined":{"name":"OptionBool"}}}]},{"name":"distribute_creator_fees","docs":["Distributes creator fees to shareholders based on their share percentages","The creator vault needs to have at least the minimum distributable amount to distribute fees","This can be checked with the get_minimum_distributable_fee instruction"],"discriminator":[165,114,103,0,121,206,247,81],"accounts":[{"name":"mint","relations":["sharing_config"]},{"name":"bonding_curve","pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"sharing_config","pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[12,53,255,169,5,90,142,86,141,168,247,188,7,86,21,39,76,241,201,44,164,31,64,0,156,81,106,164,20,194,124,112]}}},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"bonding_curve.creator","account":"BondingCurve"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"}],"args":[],"returns":{"defined":{"name":"DistributeCreatorFeesEvent"}}},{"name":"distribute_creator_fees_v2","discriminator":[255,203,19,79,244,68,8,159],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"mint","relations":["sharing_config"]},{"name":"bonding_curve","pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"sharing_config","pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[12,53,255,169,5,90,142,86,141,168,247,188,7,86,21,39,76,241,201,44,164,31,64,0,156,81,106,164,20,194,124,112]}}},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"bonding_curve.creator","account":"BondingCurve"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"creator_vault_quote_token_account","docs":["Deserialized manually in the handler for non-legacy quote mints."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"creator_vault"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"quote_mint"},{"name":"quote_token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"initialize_ata","type":"bool"}],"returns":{"defined":{"name":"DistributeCreatorFeesEvent"}}},{"name":"extend_account","docs":["Extends the size of program-owned accounts"],"discriminator":[234,102,194,203,150,72,62,229],"accounts":[{"name":"account","writable":true},{"name":"user","signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"get_minimum_distributable_fee","docs":["Permissionless instruction to check the minimum required fees for distribution","Returns the minimum required balance from the creator_vault and whether distribution can proceed"],"discriminator":[117,225,127,202,134,95,68,35],"accounts":[{"name":"mint","relations":["sharing_config"]},{"name":"bonding_curve","pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"sharing_config","pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[12,53,255,169,5,90,142,86,141,168,247,188,7,86,21,39,76,241,201,44,164,31,64,0,156,81,106,164,20,194,124,112]}}},{"name":"creator_vault","pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"bonding_curve.creator","account":"BondingCurve"}]}}],"args":[],"returns":{"defined":{"name":"MinimumDistributableFeeEvent"}}},{"name":"init_user_volume_accumulator","discriminator":[94,6,202,115,255,96,232,183],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"user"},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"initialize","docs":["Creates the global state."],"discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"user","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"migrate","docs":["Migrates liquidity to pump_amm if the bonding curve is complete"],"discriminator":[155,234,231,146,236,158,162,30],"accounts":[{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"withdraw_authority","writable":true,"relations":["global"]},{"name":"mint"},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"associated_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"mint"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user","signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"pump_amm","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"const","value":[0,0]},{"kind":"account","path":"pool_authority"},{"kind":"account","path":"mint"},{"kind":"account","path":"wsol_mint"}],"program":{"kind":"account","path":"pump_amm"}}},{"name":"pool_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,45,97,117,116,104,111,114,105,116,121]},{"kind":"account","path":"mint"}]}},{"name":"pool_authority_mint_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_authority"},{"kind":"account","path":"mint"},{"kind":"account","path":"mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"pool_authority_wsol_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_authority"},{"kind":"account","path":"token_program"},{"kind":"account","path":"wsol_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"amm_global_config","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,99,111,110,102,105,103]}],"program":{"kind":"account","path":"pump_amm"}}},{"name":"wsol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,108,112,95,109,105,110,116]},{"kind":"account","path":"pool"}],"program":{"kind":"account","path":"pump_amm"}}},{"name":"user_pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_authority"},{"kind":"account","path":"token_2022_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"pool_base_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"mint"},{"kind":"account","path":"mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"pool_quote_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"token_program"},{"kind":"account","path":"wsol_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"pump_amm_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"account","path":"pump_amm"}}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[]},{"name":"migrate_bonding_curve_creator","discriminator":[87,124,52,191,52,38,214,232],"accounts":[{"name":"mint","relations":["sharing_config"]},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"sharing_config","pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[12,53,255,169,5,90,142,86,141,168,247,188,7,86,21,39,76,241,201,44,164,31,64,0,156,81,106,164,20,194,124,112]}}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"migrate_v2","docs":["Migrates liquidity to pump_amm if the bonding curve is complete"],"discriminator":[187,203,18,31,206,237,254,41],"accounts":[{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"withdraw_authority","writable":true,"relations":["global"]},{"name":"base_mint"},{"name":"quote_mint"},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"base_mint"}]}},{"name":"associated_base_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"base_token_program"},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"associated_quote_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"user","signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"pump_amm","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"const","value":[0,0]},{"kind":"account","path":"pool_authority"},{"kind":"account","path":"base_mint"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"pump_amm"}}},{"name":"pool_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,45,97,117,116,104,111,114,105,116,121]},{"kind":"account","path":"base_mint"}]}},{"name":"pool_authority_mint_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_authority"},{"kind":"account","path":"base_token_program"},{"kind":"account","path":"base_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"pool_authority_quote_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_authority"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"amm_global_config","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,99,111,110,102,105,103]}],"program":{"kind":"account","path":"pump_amm"}}},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,108,112,95,109,105,110,116]},{"kind":"account","path":"pool"}],"program":{"kind":"account","path":"pump_amm"}}},{"name":"user_pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_authority"},{"kind":"account","path":"token_2022_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"pool_base_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"base_token_program"},{"kind":"account","path":"base_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"pool_quote_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"base_token_program"},{"name":"quote_token_program"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"pump_amm_event_authority"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"remove_quote_mint","discriminator":[177,65,223,38,88,209,158,155],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","writable":true,"signer":true,"relations":["global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"quote_mint","type":"pubkey"}]},{"name":"sell","docs":["Sells tokens into a bonding curve.","For cashback coins, pass as remaining_accounts: [0] user_volume_accumulator,","[1] bonding_curve_v2. If provided and valid, creator_fee goes to user_volume_accumulator.","Otherwise, falls back to transferring creator_fee to creator_vault."],"discriminator":[51,230,133,164,1,127,131,173],"accounts":[{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"fee_recipient","writable":true},{"name":"mint"},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"associated_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"associated_user","writable":true},{"name":"user","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"bonding_curve.creator","account":"BondingCurve"}]}},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}],"program":{"kind":"account","path":"fee_program"}}},{"name":"fee_program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"}],"args":[{"name":"amount","type":"u64"},{"name":"min_sol_output","type":"u64"}]},{"name":"sell_v2","discriminator":[93,246,130,60,231,233,64,178],"accounts":[{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"base_mint"},{"name":"quote_mint"},{"name":"base_token_program"},{"name":"quote_token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"fee_recipient","writable":true},{"name":"associated_quote_fee_recipient","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"buyback_fee_recipient","writable":true},{"name":"associated_quote_buyback_fee_recipient","writable":true,"pda":{"seeds":[{"kind":"account","path":"buyback_fee_recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"base_mint"}]}},{"name":"associated_base_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"base_token_program"},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"associated_quote_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user","writable":true,"signer":true},{"name":"associated_base_user","writable":true},{"name":"associated_quote_user","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"bonding_curve.creator","account":"BondingCurve"}]}},{"name":"associated_creator_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"creator_vault"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"sharing_config","docs":["seeds; the account is intentionally not deserialized here because it may be uninitialized","for mints that have not created a fee sharing config. Handlers must check","`data_is_empty()` / owner before reading."],"pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[12,53,255,169,5,90,142,86,141,168,247,188,7,86,21,39,76,241,201,44,164,31,64,0,156,81,106,164,20,194,124,112]}}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"associated_user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"account","path":"user_volume_accumulator"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}],"program":{"kind":"account","path":"fee_program"}}},{"name":"fee_program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"}],"args":[{"name":"amount","type":"u64"},{"name":"min_sol_output","type":"u64"}]},{"name":"set_creator","docs":["Allows Global::set_creator_authority to set the bonding curve creator from Metaplex metadata or input argument"],"discriminator":[254,148,255,112,207,142,170,165],"accounts":[{"name":"set_creator_authority","signer":true,"relations":["global"]},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"mint"},{"name":"metadata","pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]}}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"creator","type":"pubkey"}]},{"name":"set_mayhem_virtual_params","discriminator":[61,169,188,191,153,149,42,97],"accounts":[{"name":"sol_vault_authority","writable":true,"signer":true,"pda":{"seeds":[{"kind":"const","value":[115,111,108,45,118,97,117,108,116]}],"program":{"kind":"const","value":[5,42,229,215,167,218,167,36,166,234,176,167,41,84,145,133,90,212,160,103,22,96,103,76,78,3,69,89,128,61,101,163]}}},{"name":"mayhem_token_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"sol_vault_authority"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"mint"},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"token_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"set_metaplex_creator","docs":["Syncs the bonding curve creator with the Metaplex metadata creator if it exists"],"discriminator":[138,96,174,217,48,85,197,246],"accounts":[{"name":"mint"},{"name":"metadata","pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]}}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"set_params","docs":["Sets the global state parameters."],"discriminator":[27,234,178,52,147,2,187,141],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","writable":true,"signer":true,"relations":["global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"initial_virtual_token_reserves","type":"u64"},{"name":"initial_virtual_sol_reserves","type":"u64"},{"name":"initial_real_token_reserves","type":"u64"},{"name":"token_total_supply","type":"u64"},{"name":"fee_basis_points","type":"u64"},{"name":"withdraw_authority","type":"pubkey"},{"name":"enable_migrate","type":"bool"},{"name":"pool_migration_fee","type":"u64"},{"name":"creator_fee_basis_points","type":"u64"},{"name":"set_creator_authority","type":"pubkey"},{"name":"admin_set_creator_authority","type":"pubkey"}]},{"name":"set_reserved_fee_recipients","discriminator":[111,172,162,232,114,89,213,142],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","signer":true,"relations":["global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"whitelist_pda","type":"pubkey"}]},{"name":"set_virtual_quote_reserves","discriminator":[101,135,191,104,9,88,20,96],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","writable":true,"signer":true,"relations":["global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"initial_virtual_quote_reserves","type":"u64"}]},{"name":"sync_user_volume_accumulator","discriminator":[86,31,192,87,163,87,79,238],"accounts":[{"name":"user"},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"toggle_cashback_enabled","discriminator":[115,103,224,255,189,89,86,195],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","writable":true,"signer":true,"relations":["global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"enabled","type":"bool"}]},{"name":"toggle_create_v2","discriminator":[28,255,230,240,172,107,203,171],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","writable":true,"signer":true,"relations":["global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"enabled","type":"bool"}]},{"name":"toggle_mayhem_mode","discriminator":[1,9,111,208,100,31,255,163],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","writable":true,"signer":true,"relations":["global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"enabled","type":"bool"}]},{"name":"update_buyback_config","discriminator":[251,224,171,146,160,26,113,233],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","writable":true,"signer":true,"relations":["global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"buyback_basis_points","type":{"option":"u64"}}]},{"name":"update_global_authority","discriminator":[227,181,74,196,208,21,97,213],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","signer":true,"relations":["global"]},{"name":"new_authority"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]}],"accounts":[{"name":"BondingCurve","discriminator":[23,183,248,55,96,216,172,96]},{"name":"FeeConfig","discriminator":[143,52,146,187,219,123,76,155]},{"name":"Global","discriminator":[167,232,232,177,200,108,114,127]},{"name":"GlobalVolumeAccumulator","discriminator":[202,42,246,43,142,190,30,255]},{"name":"SharingConfig","discriminator":[216,74,9,0,56,140,93,75]},{"name":"UserVolumeAccumulator","discriminator":[86,255,112,14,102,53,154,250]}],"events":[{"name":"AdminSetCreatorEvent","discriminator":[64,69,192,104,29,30,25,107]},{"name":"AdminSetIdlAuthorityEvent","discriminator":[245,59,70,34,75,185,109,92]},{"name":"AdminUpdateTokenIncentivesEvent","discriminator":[147,250,108,120,247,29,67,222]},{"name":"ClaimCashbackEvent","discriminator":[226,214,246,33,7,242,147,229]},{"name":"ClaimTokenIncentivesEvent","discriminator":[79,172,246,49,205,91,206,232]},{"name":"CloseUserVolumeAccumulatorEvent","discriminator":[146,159,189,172,146,88,56,244]},{"name":"CollectCreatorFeeEvent","discriminator":[122,2,127,1,14,191,12,175]},{"name":"CompleteEvent","discriminator":[95,114,97,156,212,46,152,8]},{"name":"CompletePumpAmmMigrationEvent","discriminator":[189,233,93,185,92,148,234,148]},{"name":"CreateEvent","discriminator":[27,114,169,77,222,235,99,118]},{"name":"DistributeCreatorFeesEvent","discriminator":[165,55,129,112,4,179,202,40]},{"name":"ExtendAccountEvent","discriminator":[97,97,215,144,93,146,22,124]},{"name":"InitUserVolumeAccumulatorEvent","discriminator":[134,36,13,72,232,101,130,216]},{"name":"MigrateBondingCurveCreatorEvent","discriminator":[155,167,104,220,213,108,243,3]},{"name":"MinimumDistributableFeeEvent","discriminator":[168,216,132,239,235,182,49,52]},{"name":"ReservedFeeRecipientsEvent","discriminator":[43,188,250,18,221,75,187,95]},{"name":"SetCreatorEvent","discriminator":[237,52,123,37,245,251,72,210]},{"name":"SetMetaplexCreatorEvent","discriminator":[142,203,6,32,127,105,191,162]},{"name":"SetParamsEvent","discriminator":[223,195,159,246,62,48,143,131]},{"name":"SyncUserVolumeAccumulatorEvent","discriminator":[197,122,167,124,116,81,91,255]},{"name":"TradeEvent","discriminator":[189,219,127,211,78,230,97,238]},{"name":"UpdateGlobalAuthorityEvent","discriminator":[182,195,137,42,35,206,207,247]},{"name":"UpdateMayhemVirtualParamsEvent","discriminator":[117,123,228,182,161,168,220,214]}],"errors":[{"code":6000,"name":"NotAuthorized","msg":"The given account is not authorized to execute this instruction."},{"code":6001,"name":"AlreadyInitialized","msg":"The program is already initialized."},{"code":6002,"name":"TooMuchSolRequired","msg":"slippage: Too much SOL required to buy the given amount of tokens."},{"code":6003,"name":"TooLittleSolReceived","msg":"slippage: Too little SOL received to sell the given amount of tokens."},{"code":6004,"name":"MintDoesNotMatchBondingCurve","msg":"The mint does not match the bonding curve."},{"code":6005,"name":"BondingCurveComplete","msg":"The bonding curve has completed and liquidity migrated to raydium."},{"code":6006,"name":"BondingCurveNotComplete","msg":"The bonding curve has not completed."},{"code":6007,"name":"NotInitialized","msg":"The program is not initialized."},{"code":6008,"name":"WithdrawTooFrequent","msg":"Withdraw too frequent"},{"code":6009,"name":"NewSizeShouldBeGreaterThanCurrentSize","msg":"new_size should be > current_size"},{"code":6010,"name":"AccountTypeNotSupported","msg":"Account type not supported"},{"code":6011,"name":"InitialRealTokenReservesShouldBeLessThanTokenTotalSupply","msg":"initial_real_token_reserves should be less than token_total_supply"},{"code":6012,"name":"InitialVirtualTokenReservesShouldBeGreaterThanInitialRealTokenReserves","msg":"initial_virtual_token_reserves should be greater than initial_real_token_reserves"},{"code":6013,"name":"FeeBasisPointsGreaterThanMaximum","msg":"fee_basis_points greater than maximum"},{"code":6014,"name":"AllZerosWithdrawAuthority","msg":"Withdraw authority cannot be set to System Program ID"},{"code":6015,"name":"PoolMigrationFeeShouldBeLessThanFinalRealSolReserves","msg":"pool_migration_fee should be less than final_real_sol_reserves"},{"code":6016,"name":"PoolMigrationFeeShouldBeGreaterThanCreatorFeePlusMaxMigrateFees","msg":"pool_migration_fee should be greater than creator_fee + MAX_MIGRATE_FEES"},{"code":6017,"name":"DisabledWithdraw","msg":"Migrate instruction is disabled"},{"code":6018,"name":"DisabledMigrate","msg":"Migrate instruction is disabled"},{"code":6019,"name":"InvalidCreator","msg":"Invalid creator pubkey"},{"code":6020,"name":"BuyZeroAmount","msg":"Buy zero amount"},{"code":6021,"name":"NotEnoughTokensToBuy","msg":"Not enough tokens to buy"},{"code":6022,"name":"SellZeroAmount","msg":"Sell zero amount"},{"code":6023,"name":"NotEnoughTokensToSell","msg":"Not enough tokens to sell"},{"code":6024,"name":"Overflow","msg":"Overflow"},{"code":6025,"name":"Truncation","msg":"Truncation"},{"code":6026,"name":"DivisionByZero","msg":"Division by zero"},{"code":6027,"name":"NotEnoughRemainingAccounts","msg":"Not enough remaining accounts"},{"code":6028,"name":"AllFeeRecipientsShouldBeNonZero","msg":"All fee recipients should be non-zero"},{"code":6029,"name":"UnsortedNotUniqueFeeRecipients","msg":"Unsorted or not unique fee recipients"},{"code":6030,"name":"CreatorShouldNotBeZero","msg":"Creator should not be zero"},{"code":6031,"name":"StartTimeInThePast"},{"code":6032,"name":"EndTimeInThePast"},{"code":6033,"name":"EndTimeBeforeStartTime"},{"code":6034,"name":"TimeRangeTooLarge"},{"code":6035,"name":"EndTimeBeforeCurrentDay"},{"code":6036,"name":"SupplyUpdateForFinishedRange"},{"code":6037,"name":"DayIndexAfterEndIndex"},{"code":6038,"name":"DayInActiveRange"},{"code":6039,"name":"InvalidIncentiveMint"},{"code":6040,"name":"BuyNotEnoughSolToCoverRent","msg":"Buy: Not enough SOL to cover for rent exemption."},{"code":6041,"name":"BuyNotEnoughSolToCoverFees","msg":"Buy: Not enough SOL to cover for fees."},{"code":6042,"name":"BuySlippageBelowMinTokensOut","msg":"Slippage: Would buy less tokens than expected min_tokens_out"},{"code":6043,"name":"NameTooLong"},{"code":6044,"name":"SymbolTooLong"},{"code":6045,"name":"UriTooLong"},{"code":6046,"name":"CreateV2Disabled"},{"code":6047,"name":"CpitializeMayhemFailed"},{"code":6048,"name":"MayhemModeDisabled"},{"code":6049,"name":"CreatorMigratedToSharingConfig","msg":"creator has been migrated to sharing config, use pump_fees::reset_fee_sharing_config instead"},{"code":6050,"name":"UnableToDistributeCreatorVaultMigratedToSharingConfig","msg":"creator_vault has been migrated to sharing config, use pump:distribute_creator_fees instead"},{"code":6051,"name":"SharingConfigNotActive","msg":"Sharing config is not active"},{"code":6052,"name":"UnableToDistributeCreatorFeesToExecutableRecipient","msg":"The recipient account is executable, so it cannot receive lamports, remove it from the team first"},{"code":6053,"name":"BondingCurveAndSharingConfigCreatorMismatch","msg":"Bonding curve creator does not match sharing config"},{"code":6054,"name":"ShareholdersAndRemainingAccountsMismatch","msg":"Remaining accounts do not match shareholders, make sure to pass exactly the same pubkeys in the same order"},{"code":6055,"name":"InvalidShareBps","msg":"Share bps must be greater than 0"},{"code":6056,"name":"CashbackNotEnabled","msg":"Cashback is not enabled"},{"code":6057,"name":"BuybackFeeRecipientNotAuthorized","msg":"Buyback fee recipient not authorized"},{"code":6058,"name":"AllBuybackFeeRecipientsShouldBeNonZero"},{"code":6059,"name":"NotUniqueBuybackFeeRecipients"},{"code":6060,"name":"BuybackBasisPointsOutOfRange","msg":"buyback_basis_points must be <= 10_000"},{"code":6061,"name":"WrongBuybackFeeRecipientsCount","msg":"buyback fee recipients require exactly 8 remaining accounts (or none)"},{"code":6062,"name":"BuybackFeeRecipientMissing"},{"code":6063,"name":"UnsupportedQuoteMint","msg":"Unsupported quote mint"},{"code":6064,"name":"InvalidQuoteTokenProgram","msg":"Create v2: quote token program must be legacy SPL Token"},{"code":6065,"name":"InvalidAssociatedQuoteBondingCurve","msg":"Create v2: associated quote bonding curve address does not match derivation"},{"code":6066,"name":"QuoteMintWhitelistFull","msg":"Quote mint whitelist is full"},{"code":6067,"name":"QuoteMintAlreadyWhitelisted","msg":"Quote mint is already whitelisted"},{"code":6068,"name":"QuoteMintNotWhitelisted","msg":"Quote mint is not in the whitelist"},{"code":6069,"name":"QuoteMintNotEligibleForWhitelist","msg":"Quote mint cannot be added or removed via whitelist (default or native SOL mint)"},{"code":6070,"name":"UnableToDistributeCreatorFeesToUninitializedAccount","msg":"Unable to distribute creator fees to uninitialized account"},{"code":6071,"name":"MayhemModeQuoteMintNotAllowed","msg":"Mayhem mode quote mint not allowed"}],"types":[{"name":"AdminSetCreatorEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin_set_creator_authority","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"bonding_curve","type":"pubkey"},{"name":"old_creator","type":"pubkey"},{"name":"new_creator","type":"pubkey"}]}},{"name":"AdminSetIdlAuthorityEvent","type":{"kind":"struct","fields":[{"name":"idl_authority","type":"pubkey"}]}},{"name":"AdminUpdateTokenIncentivesEvent","type":{"kind":"struct","fields":[{"name":"start_time","type":"i64"},{"name":"end_time","type":"i64"},{"name":"day_number","type":"u64"},{"name":"token_supply_per_day","type":"u64"},{"name":"mint","type":"pubkey"},{"name":"seconds_in_a_day","type":"i64"},{"name":"timestamp","type":"i64"}]}},{"name":"BondingCurve","type":{"kind":"struct","fields":[{"name":"virtual_token_reserves","type":"u64"},{"name":"virtual_quote_reserves","type":"u64"},{"name":"real_token_reserves","type":"u64"},{"name":"real_quote_reserves","type":"u64"},{"name":"token_total_supply","type":"u64"},{"name":"complete","type":"bool"},{"name":"creator","type":"pubkey"},{"name":"is_mayhem_mode","type":"bool"},{"name":"is_cashback_coin","type":"bool"},{"name":"quote_mint","type":"pubkey"}]}},{"name":"ClaimCashbackEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"amount","type":"u64"},{"name":"timestamp","type":"i64"},{"name":"total_claimed","type":"u64"},{"name":"total_cashback_earned","type":"u64"}]}},{"name":"ClaimTokenIncentivesEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"amount","type":"u64"},{"name":"timestamp","type":"i64"},{"name":"total_claimed_tokens","type":"u64"},{"name":"current_sol_volume","type":"u64"}]}},{"name":"CloseUserVolumeAccumulatorEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"timestamp","type":"i64"},{"name":"total_unclaimed_tokens","type":"u64"},{"name":"total_claimed_tokens","type":"u64"},{"name":"current_sol_volume","type":"u64"},{"name":"last_update_timestamp","type":"i64"}]}},{"name":"CollectCreatorFeeEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"creator","type":"pubkey"},{"name":"creator_fee","type":"u64"},{"name":"quote_mint","type":"pubkey"}]}},{"name":"CompleteEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"bonding_curve","type":"pubkey"},{"name":"timestamp","type":"i64"},{"name":"quote_mint","type":"pubkey"}]}},{"name":"CompletePumpAmmMigrationEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"mint_amount","type":"u64"},{"name":"sol_amount","type":"u64"},{"name":"pool_migration_fee","type":"u64"},{"name":"bonding_curve","type":"pubkey"},{"name":"timestamp","type":"i64"},{"name":"pool","type":"pubkey"},{"name":"quote_mint","type":"pubkey"}]}},{"name":"ConfigStatus","type":{"kind":"enum","variants":[{"name":"Paused"},{"name":"Active"}]}},{"name":"CreateEvent","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"mint","type":"pubkey"},{"name":"bonding_curve","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"creator","type":"pubkey"},{"name":"timestamp","type":"i64"},{"name":"virtual_token_reserves","type":"u64"},{"name":"virtual_sol_reserves","type":"u64"},{"name":"real_token_reserves","type":"u64"},{"name":"token_total_supply","type":"u64"},{"name":"token_program","type":"pubkey"},{"name":"is_mayhem_mode","type":"bool"},{"name":"is_cashback_enabled","type":"bool"},{"name":"quote_mint","type":"pubkey"},{"name":"virtual_quote_reserves","type":"u64"}]}},{"name":"DistributeCreatorFeesEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"bonding_curve","type":"pubkey"},{"name":"sharing_config","type":"pubkey"},{"name":"admin","type":"pubkey"},{"name":"shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}},{"name":"distributed","type":"u64"},{"name":"quote_mint","type":"pubkey"}]}},{"name":"ExtendAccountEvent","type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"current_size","type":"u64"},{"name":"new_size","type":"u64"},{"name":"timestamp","type":"i64"}]}},{"name":"FeeConfig","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"admin","type":"pubkey"},{"name":"flat_fees","type":{"defined":{"name":"Fees"}}},{"name":"fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"stable_fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}}]}},{"name":"FeeTier","type":{"kind":"struct","fields":[{"name":"market_cap_lamports_threshold","type":"u128"},{"name":"fees","type":{"defined":{"name":"Fees"}}}]}},{"name":"Fees","type":{"kind":"struct","fields":[{"name":"lp_fee_bps","type":"u64"},{"name":"protocol_fee_bps","type":"u64"},{"name":"creator_fee_bps","type":"u64"}]}},{"name":"Global","type":{"kind":"struct","fields":[{"name":"initialized","docs":["Unused"],"type":"bool"},{"name":"authority","type":"pubkey"},{"name":"fee_recipient","type":"pubkey"},{"name":"initial_virtual_token_reserves","type":"u64"},{"name":"initial_virtual_sol_reserves","type":"u64"},{"name":"initial_real_token_reserves","type":"u64"},{"name":"token_total_supply","type":"u64"},{"name":"fee_basis_points","type":"u64"},{"name":"withdraw_authority","type":"pubkey"},{"name":"enable_migrate","docs":["Unused"],"type":"bool"},{"name":"pool_migration_fee","type":"u64"},{"name":"creator_fee_basis_points","type":"u64"},{"name":"fee_recipients","type":{"array":["pubkey",7]}},{"name":"set_creator_authority","type":"pubkey"},{"name":"admin_set_creator_authority","type":"pubkey"},{"name":"create_v2_enabled","type":"bool"},{"name":"whitelist_pda","type":"pubkey"},{"name":"reserved_fee_recipient","type":"pubkey"},{"name":"mayhem_mode_enabled","type":"bool"},{"name":"reserved_fee_recipients","type":{"array":["pubkey",7]}},{"name":"is_cashback_enabled","type":"bool"},{"name":"buyback_fee_recipients","type":{"array":["pubkey",8]}},{"name":"buyback_basis_points","type":"u64"},{"name":"initial_virtual_quote_reserves","type":"u64"},{"name":"whitelisted_quote_mints","type":{"array":["pubkey",1]}}]}},{"name":"GlobalVolumeAccumulator","type":{"kind":"struct","fields":[{"name":"start_time","type":"i64"},{"name":"end_time","type":"i64"},{"name":"seconds_in_a_day","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"total_token_supply","type":{"array":["u64",30]}},{"name":"sol_volumes","type":{"array":["u64",30]}}]}},{"name":"InitUserVolumeAccumulatorEvent","type":{"kind":"struct","fields":[{"name":"payer","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"timestamp","type":"i64"}]}},{"name":"MigrateBondingCurveCreatorEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"bonding_curve","type":"pubkey"},{"name":"sharing_config","type":"pubkey"},{"name":"old_creator","type":"pubkey"},{"name":"new_creator","type":"pubkey"}]}},{"name":"MinimumDistributableFeeEvent","type":{"kind":"struct","fields":[{"name":"minimum_required","type":"u64"},{"name":"distributable_fees","type":"u64"},{"name":"can_distribute","type":"bool"}]}},{"name":"OptionBool","type":{"kind":"struct","fields":["bool"]}},{"name":"ReservedFeeRecipientsEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"reserved_fee_recipient","type":"pubkey"},{"name":"reserved_fee_recipients","type":{"array":["pubkey",7]}}]}},{"name":"SetCreatorEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"bonding_curve","type":"pubkey"},{"name":"creator","type":"pubkey"}]}},{"name":"SetMetaplexCreatorEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"bonding_curve","type":"pubkey"},{"name":"metadata","type":"pubkey"},{"name":"creator","type":"pubkey"}]}},{"name":"SetParamsEvent","type":{"kind":"struct","fields":[{"name":"initial_virtual_token_reserves","type":"u64"},{"name":"initial_virtual_sol_reserves","type":"u64"},{"name":"initial_real_token_reserves","type":"u64"},{"name":"final_real_sol_reserves","type":"u64"},{"name":"token_total_supply","type":"u64"},{"name":"fee_basis_points","type":"u64"},{"name":"withdraw_authority","type":"pubkey"},{"name":"enable_migrate","type":"bool"},{"name":"pool_migration_fee","type":"u64"},{"name":"creator_fee_basis_points","type":"u64"},{"name":"fee_recipients","type":{"array":["pubkey",8]}},{"name":"timestamp","type":"i64"},{"name":"set_creator_authority","type":"pubkey"},{"name":"admin_set_creator_authority","type":"pubkey"}]}},{"name":"Shareholder","type":{"kind":"struct","fields":[{"name":"address","type":"pubkey"},{"name":"share_bps","type":"u16"}]}},{"name":"SharingConfig","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"version","type":"u8"},{"name":"status","type":{"defined":{"name":"ConfigStatus"}}},{"name":"mint","type":"pubkey"},{"name":"admin","type":"pubkey"},{"name":"admin_revoked","type":"bool"},{"name":"shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}}]}},{"name":"SyncUserVolumeAccumulatorEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"total_claimed_tokens_before","type":"u64"},{"name":"total_claimed_tokens_after","type":"u64"},{"name":"timestamp","type":"i64"}]}},{"name":"TradeEvent","docs":["ix_name: \"buy\" | \"sell\" | \"buy_exact_sol_in\""],"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"sol_amount","type":"u64"},{"name":"token_amount","type":"u64"},{"name":"is_buy","type":"bool"},{"name":"user","type":"pubkey"},{"name":"timestamp","type":"i64"},{"name":"virtual_sol_reserves","type":"u64"},{"name":"virtual_token_reserves","type":"u64"},{"name":"real_sol_reserves","type":"u64"},{"name":"real_token_reserves","type":"u64"},{"name":"fee_recipient","type":"pubkey"},{"name":"fee_basis_points","type":"u64"},{"name":"fee","type":"u64"},{"name":"creator","type":"pubkey"},{"name":"creator_fee_basis_points","type":"u64"},{"name":"creator_fee","type":"u64"},{"name":"track_volume","type":"bool"},{"name":"total_unclaimed_tokens","type":"u64"},{"name":"total_claimed_tokens","type":"u64"},{"name":"current_sol_volume","type":"u64"},{"name":"last_update_timestamp","type":"i64"},{"name":"ix_name","type":"string"},{"name":"mayhem_mode","type":"bool"},{"name":"cashback_fee_basis_points","type":"u64"},{"name":"cashback","type":"u64"},{"name":"buyback_fee_basis_points","type":"u64"},{"name":"buyback_fee","type":"u64"},{"name":"shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}},{"name":"quote_mint","type":"pubkey"},{"name":"quote_amount","type":"u64"},{"name":"virtual_quote_reserves","type":"u64"},{"name":"real_quote_reserves","type":"u64"}]}},{"name":"UpdateGlobalAuthorityEvent","type":{"kind":"struct","fields":[{"name":"global","type":"pubkey"},{"name":"authority","type":"pubkey"},{"name":"new_authority","type":"pubkey"},{"name":"timestamp","type":"i64"}]}},{"name":"UpdateMayhemVirtualParamsEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"virtual_token_reserves","type":"u64"},{"name":"virtual_sol_reserves","type":"u64"},{"name":"new_virtual_token_reserves","type":"u64"},{"name":"new_virtual_sol_reserves","type":"u64"},{"name":"real_token_reserves","type":"u64"},{"name":"real_sol_reserves","type":"u64"}]}},{"name":"UserVolumeAccumulator","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"needs_claim","type":"bool"},{"name":"total_unclaimed_tokens","type":"u64"},{"name":"total_claimed_tokens","type":"u64"},{"name":"current_sol_volume","type":"u64"},{"name":"last_update_timestamp","type":"i64"},{"name":"has_total_claimed_tokens","type":"bool"},{"name":"cashback_earned","type":"u64"},{"name":"total_cashback_claimed","type":"u64"}]}}]} \ No newline at end of file diff --git a/idls/pump_swap.pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA.json b/idls/pump_swap.pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA.json new file mode 100644 index 0000000..daf2337 --- /dev/null +++ b/idls/pump_swap.pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA.json @@ -0,0 +1 @@ +{"address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA","metadata":{"name":"pump_amm","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"admin_set_coin_creator","docs":["Overrides the coin creator for a canonical pump pool"],"discriminator":[242,40,117,145,73,96,105,104],"accounts":[{"name":"admin_set_coin_creator_authority","signer":true,"relations":["global_config"]},{"name":"global_config"},{"name":"pool","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"coin_creator","type":"pubkey"}]},{"name":"admin_update_token_incentives","discriminator":[209,11,115,87,213,23,124,204],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["global_config"]},{"name":"global_config","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,99,111,110,102,105,103]}]}},{"name":"global_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"mint"},{"name":"global_incentive_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"global_volume_accumulator"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"start_time","type":"i64"},{"name":"end_time","type":"i64"},{"name":"seconds_in_a_day","type":"i64"},{"name":"day_number","type":"u64"},{"name":"token_supply_per_day","type":"u64"}]},{"name":"buy","docs":["For cashback coins, optionally pass user_volume_accumulator_wsol_ata as remaining_accounts[0].","If provided and valid, the ATA will be initialized if needed."],"discriminator":[102,6,61,18,1,218,235,234],"accounts":[{"name":"pool","writable":true},{"name":"user","writable":true,"signer":true},{"name":"global_config"},{"name":"base_mint","relations":["pool"]},{"name":"quote_mint","relations":["pool"]},{"name":"user_base_token_account","writable":true},{"name":"user_quote_token_account","writable":true},{"name":"pool_base_token_account","writable":true,"relations":["pool"]},{"name":"pool_quote_token_account","writable":true,"relations":["pool"]},{"name":"protocol_fee_recipient"},{"name":"protocol_fee_recipient_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"protocol_fee_recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"base_token_program"},{"name":"quote_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"coin_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"coin_creator_vault_authority"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"coin_creator_vault_authority","pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"pool.coin_creator","account":"Pool"}]}},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}],"program":{"kind":"account","path":"fee_program"}}},{"name":"fee_program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"}],"args":[{"name":"base_amount_out","type":"u64"},{"name":"max_quote_amount_in","type":"u64"},{"name":"track_volume","type":{"defined":{"name":"OptionBool"}}}]},{"name":"buy_exact_quote_in","docs":["Given a budget of spendable_quote_in, buy at least min_base_amount_out","Fees will be deducted from spendable_quote_in","","f(quote) = tokens, where tokens >= min_base_amount_out","","Make sure the payer has enough SOL to cover creation of the following accounts (unless already created):","- protocol_fee_recipient_token_account: rent.minimum_balance(TokenAccount::LEN)","- coin_creator_vault_ata: rent.minimum_balance(TokenAccount::LEN)","- user_volume_accumulator: rent.minimum_balance(UserVolumeAccumulator::LEN)","","For cashback coins, optionally pass user_volume_accumulator_wsol_ata as remaining_accounts[0].","If provided and valid, the ATA will be initialized if needed."],"discriminator":[198,46,21,82,180,217,232,112],"accounts":[{"name":"pool","writable":true},{"name":"user","writable":true,"signer":true},{"name":"global_config"},{"name":"base_mint","relations":["pool"]},{"name":"quote_mint","relations":["pool"]},{"name":"user_base_token_account","writable":true},{"name":"user_quote_token_account","writable":true},{"name":"pool_base_token_account","writable":true,"relations":["pool"]},{"name":"pool_quote_token_account","writable":true,"relations":["pool"]},{"name":"protocol_fee_recipient"},{"name":"protocol_fee_recipient_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"protocol_fee_recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"base_token_program"},{"name":"quote_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"coin_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"coin_creator_vault_authority"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"coin_creator_vault_authority","pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"pool.coin_creator","account":"Pool"}]}},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}],"program":{"kind":"account","path":"fee_program"}}},{"name":"fee_program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"}],"args":[{"name":"spendable_quote_in","type":"u64"},{"name":"min_base_amount_out","type":"u64"},{"name":"track_volume","type":{"defined":{"name":"OptionBool"}}}]},{"name":"claim_cashback","discriminator":[37,58,35,126,190,53,228,197],"accounts":[{"name":"user","writable":true},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"quote_mint"},{"name":"quote_token_program"},{"name":"user_volume_accumulator_wsol_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"user_volume_accumulator"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user_wsol_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"}],"args":[]},{"name":"claim_token_incentives","discriminator":[16,4,71,28,204,1,40,27],"accounts":[{"name":"user"},{"name":"user_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"global_incentive_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"global_volume_accumulator"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"mint","relations":["global_volume_accumulator"]},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"payer","writable":true,"signer":true}],"args":[]},{"name":"close_user_volume_accumulator","discriminator":[249,69,164,218,150,103,84,138],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"collect_coin_creator_fee","discriminator":[160,57,89,42,181,139,43,66],"accounts":[{"name":"quote_mint"},{"name":"quote_token_program"},{"name":"coin_creator"},{"name":"coin_creator_vault_authority","pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"coin_creator"}]}},{"name":"coin_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"coin_creator_vault_authority"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"coin_creator_token_account","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"create_config","discriminator":[201,207,243,114,75,111,47,189],"accounts":[{"name":"admin","writable":true,"signer":true,"address":"8LWu7QM2dGR1G8nKDHthckea57bkCzXyBTAKPJUBDHo8"},{"name":"global_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,99,111,110,102,105,103]}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"lp_fee_basis_points","type":"u64"},{"name":"protocol_fee_basis_points","type":"u64"},{"name":"protocol_fee_recipients","type":{"array":["pubkey",8]}},{"name":"coin_creator_fee_basis_points","type":"u64"},{"name":"admin_set_coin_creator_authority","type":"pubkey"}]},{"name":"create_pool","discriminator":[233,146,209,142,207,104,64,188],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"arg","path":"index"},{"kind":"account","path":"creator"},{"kind":"account","path":"base_mint"},{"kind":"account","path":"quote_mint"}]}},{"name":"global_config"},{"name":"creator","writable":true,"signer":true},{"name":"base_mint"},{"name":"quote_mint"},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,108,112,95,109,105,110,116]},{"kind":"account","path":"pool"}]}},{"name":"user_base_token_account","writable":true},{"name":"user_quote_token_account","writable":true},{"name":"user_pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"creator"},{"kind":"account","path":"token_2022_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool_base_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"base_token_program"},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool_quote_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"base_token_program"},{"name":"quote_token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"index","type":"u16"},{"name":"base_amount_in","type":"u64"},{"name":"quote_amount_in","type":"u64"},{"name":"coin_creator","type":"pubkey"},{"name":"is_mayhem_mode","type":"bool"},{"name":"is_cashback_coin","type":{"defined":{"name":"OptionBool"}}}]},{"name":"deposit","discriminator":[242,35,198,137,82,225,242,182],"accounts":[{"name":"pool","writable":true},{"name":"global_config"},{"name":"user","signer":true},{"name":"base_mint","relations":["pool"]},{"name":"quote_mint","relations":["pool"]},{"name":"lp_mint","writable":true,"relations":["pool"]},{"name":"user_base_token_account","writable":true},{"name":"user_quote_token_account","writable":true},{"name":"user_pool_token_account","writable":true},{"name":"pool_base_token_account","writable":true,"relations":["pool"]},{"name":"pool_quote_token_account","writable":true,"relations":["pool"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"lp_token_amount_out","type":"u64"},{"name":"max_base_amount_in","type":"u64"},{"name":"max_quote_amount_in","type":"u64"}]},{"name":"disable","discriminator":[185,173,187,90,216,15,238,233],"accounts":[{"name":"admin","signer":true,"relations":["global_config"]},{"name":"global_config","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"disable_create_pool","type":"bool"},{"name":"disable_deposit","type":"bool"},{"name":"disable_withdraw","type":"bool"},{"name":"disable_buy","type":"bool"},{"name":"disable_sell","type":"bool"}]},{"name":"extend_account","discriminator":[234,102,194,203,150,72,62,229],"accounts":[{"name":"account","writable":true},{"name":"user","signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"init_user_volume_accumulator","discriminator":[94,6,202,115,255,96,232,183],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"user"},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"migrate_pool_coin_creator","docs":["Migrate Pool Coin Creator to Sharing Config"],"discriminator":[208,8,159,4,74,175,16,58],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"pool.index","account":"Pool"},{"kind":"account","path":"pool.creator","account":"Pool"},{"kind":"account","path":"pool.base_mint","account":"Pool"},{"kind":"account","path":"pool.quote_mint","account":"Pool"}]}},{"name":"sharing_config","pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"pool.base_mint","account":"Pool"}],"program":{"kind":"const","value":[12,53,255,169,5,90,142,86,141,168,247,188,7,86,21,39,76,241,201,44,164,31,64,0,156,81,106,164,20,194,124,112]}}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"sell","discriminator":[51,230,133,164,1,127,131,173],"accounts":[{"name":"pool","writable":true},{"name":"user","writable":true,"signer":true},{"name":"global_config"},{"name":"base_mint","relations":["pool"]},{"name":"quote_mint","relations":["pool"]},{"name":"user_base_token_account","writable":true},{"name":"user_quote_token_account","writable":true},{"name":"pool_base_token_account","writable":true,"relations":["pool"]},{"name":"pool_quote_token_account","writable":true,"relations":["pool"]},{"name":"protocol_fee_recipient"},{"name":"protocol_fee_recipient_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"protocol_fee_recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"base_token_program"},{"name":"quote_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"coin_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"coin_creator_vault_authority"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"coin_creator_vault_authority","pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"pool.coin_creator","account":"Pool"}]}},{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}],"program":{"kind":"account","path":"fee_program"}}},{"name":"fee_program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"}],"args":[{"name":"base_amount_in","type":"u64"},{"name":"min_quote_amount_out","type":"u64"}]},{"name":"set_coin_creator","docs":["Sets Pool::coin_creator from Metaplex metadata creator or BondingCurve::creator"],"discriminator":[210,149,128,45,188,58,78,175],"accounts":[{"name":"pool","writable":true},{"name":"metadata","pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]},{"kind":"account","path":"pool.base_mint","account":"Pool"}],"program":{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]}}},{"name":"bonding_curve","pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"pool.base_mint","account":"Pool"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"set_reserved_fee_recipients","discriminator":[111,172,162,232,114,89,213,142],"accounts":[{"name":"global_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,99,111,110,102,105,103]}]}},{"name":"admin","signer":true,"relations":["global_config"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"whitelist_pda","type":"pubkey"}]},{"name":"sync_user_volume_accumulator","discriminator":[86,31,192,87,163,87,79,238],"accounts":[{"name":"user"},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"toggle_cashback_enabled","discriminator":[115,103,224,255,189,89,86,195],"accounts":[{"name":"admin","signer":true,"relations":["global_config"]},{"name":"global_config","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"enabled","type":"bool"}]},{"name":"toggle_mayhem_mode","discriminator":[1,9,111,208,100,31,255,163],"accounts":[{"name":"admin","signer":true,"relations":["global_config"]},{"name":"global_config","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"enabled","type":"bool"}]},{"name":"transfer_creator_fees_to_pump","docs":["Transfer creator fees to pump creator vault","If coin creator fees are currently below rent.minimum_balance(TokenAccount::LEN)","The transfer will be skipped"],"discriminator":[139,52,134,85,228,229,108,241],"accounts":[{"name":"wsol_mint","docs":["Pump Canonical Pool are quoted in wSOL"]},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"coin_creator"},{"name":"coin_creator_vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"coin_creator"}]}},{"name":"coin_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"coin_creator_vault_authority"},{"kind":"account","path":"token_program"},{"kind":"account","path":"wsol_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pump_creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"coin_creator"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"transfer_creator_fees_to_pump_v2","discriminator":[1,33,78,185,33,67,44,92],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"quote_mint"},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"coin_creator"},{"name":"coin_creator_vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"coin_creator"}]}},{"name":"coin_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"coin_creator_vault_authority"},{"kind":"account","path":"token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pump_creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"coin_creator"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"pump_creator_vault"},{"kind":"account","path":"token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"update_admin","discriminator":[161,176,40,213,60,184,179,228],"accounts":[{"name":"admin","signer":true,"relations":["global_config"]},{"name":"global_config","writable":true},{"name":"new_admin"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"update_buyback_config","discriminator":[251,224,171,146,160,26,113,233],"accounts":[{"name":"admin","signer":true,"relations":["global_config"]},{"name":"global_config","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"buyback_basis_points","type":{"option":"u64"}}]},{"name":"update_fee_config","discriminator":[104,184,103,242,88,151,107,20],"accounts":[{"name":"admin","signer":true,"relations":["global_config"]},{"name":"global_config","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"lp_fee_basis_points","type":"u64"},{"name":"protocol_fee_basis_points","type":"u64"},{"name":"protocol_fee_recipients","type":{"array":["pubkey",8]}},{"name":"coin_creator_fee_basis_points","type":"u64"},{"name":"admin_set_coin_creator_authority","type":"pubkey"}]},{"name":"withdraw","discriminator":[183,18,70,156,148,109,161,34],"accounts":[{"name":"pool","writable":true},{"name":"global_config"},{"name":"user","signer":true},{"name":"base_mint","relations":["pool"]},{"name":"quote_mint","relations":["pool"]},{"name":"lp_mint","writable":true,"relations":["pool"]},{"name":"user_base_token_account","writable":true},{"name":"user_quote_token_account","writable":true},{"name":"user_pool_token_account","writable":true},{"name":"pool_base_token_account","writable":true,"relations":["pool"]},{"name":"pool_quote_token_account","writable":true,"relations":["pool"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"lp_token_amount_in","type":"u64"},{"name":"min_base_amount_out","type":"u64"},{"name":"min_quote_amount_out","type":"u64"}]}],"accounts":[{"name":"BondingCurve","discriminator":[23,183,248,55,96,216,172,96]},{"name":"FeeConfig","discriminator":[143,52,146,187,219,123,76,155]},{"name":"GlobalConfig","discriminator":[149,8,156,202,160,252,176,217]},{"name":"GlobalVolumeAccumulator","discriminator":[202,42,246,43,142,190,30,255]},{"name":"Pool","discriminator":[241,154,109,4,17,177,109,188]},{"name":"SharingConfig","discriminator":[216,74,9,0,56,140,93,75]},{"name":"UserVolumeAccumulator","discriminator":[86,255,112,14,102,53,154,250]}],"events":[{"name":"AdminSetCoinCreatorEvent","discriminator":[45,220,93,24,25,97,172,104]},{"name":"AdminUpdateTokenIncentivesEvent","discriminator":[147,250,108,120,247,29,67,222]},{"name":"BuyEvent","discriminator":[103,244,82,31,44,245,119,119]},{"name":"ClaimCashbackEvent","discriminator":[226,214,246,33,7,242,147,229]},{"name":"ClaimTokenIncentivesEvent","discriminator":[79,172,246,49,205,91,206,232]},{"name":"CloseUserVolumeAccumulatorEvent","discriminator":[146,159,189,172,146,88,56,244]},{"name":"CollectCoinCreatorFeeEvent","discriminator":[232,245,194,238,234,218,58,89]},{"name":"CreateConfigEvent","discriminator":[107,52,89,129,55,226,81,22]},{"name":"CreatePoolEvent","discriminator":[177,49,12,210,160,118,167,116]},{"name":"DepositEvent","discriminator":[120,248,61,83,31,142,107,144]},{"name":"DisableEvent","discriminator":[107,253,193,76,228,202,27,104]},{"name":"ExtendAccountEvent","discriminator":[97,97,215,144,93,146,22,124]},{"name":"InitUserVolumeAccumulatorEvent","discriminator":[134,36,13,72,232,101,130,216]},{"name":"MigratePoolCoinCreatorEvent","discriminator":[170,221,82,199,147,165,247,46]},{"name":"ReservedFeeRecipientsEvent","discriminator":[43,188,250,18,221,75,187,95]},{"name":"SellEvent","discriminator":[62,47,55,10,165,3,220,42]},{"name":"SetBondingCurveCoinCreatorEvent","discriminator":[242,231,235,102,65,99,189,211]},{"name":"SetMetaplexCoinCreatorEvent","discriminator":[150,107,199,123,124,207,102,228]},{"name":"SyncUserVolumeAccumulatorEvent","discriminator":[197,122,167,124,116,81,91,255]},{"name":"UpdateAdminEvent","discriminator":[225,152,171,87,246,63,66,234]},{"name":"UpdateFeeConfigEvent","discriminator":[90,23,65,35,62,244,188,208]},{"name":"WithdrawEvent","discriminator":[22,9,133,26,160,44,71,192]}],"errors":[{"code":6000,"name":"FeeBasisPointsExceedsMaximum"},{"code":6001,"name":"ZeroBaseAmount"},{"code":6002,"name":"ZeroQuoteAmount"},{"code":6003,"name":"TooLittlePoolTokenLiquidity"},{"code":6004,"name":"ExceededSlippage"},{"code":6005,"name":"InvalidAdmin"},{"code":6006,"name":"UnsupportedBaseMint"},{"code":6007,"name":"UnsupportedQuoteMint"},{"code":6008,"name":"InvalidBaseMint"},{"code":6009,"name":"InvalidQuoteMint"},{"code":6010,"name":"InvalidLpMint"},{"code":6011,"name":"AllProtocolFeeRecipientsShouldBeNonZero"},{"code":6012,"name":"UnsortedNotUniqueProtocolFeeRecipients"},{"code":6013,"name":"InvalidProtocolFeeRecipient"},{"code":6014,"name":"InvalidPoolBaseTokenAccount"},{"code":6015,"name":"InvalidPoolQuoteTokenAccount"},{"code":6016,"name":"BuyMoreBaseAmountThanPoolReserves"},{"code":6017,"name":"DisabledCreatePool"},{"code":6018,"name":"DisabledDeposit"},{"code":6019,"name":"DisabledWithdraw"},{"code":6020,"name":"DisabledBuy"},{"code":6021,"name":"DisabledSell"},{"code":6022,"name":"SameMint"},{"code":6023,"name":"Overflow"},{"code":6024,"name":"Truncation"},{"code":6025,"name":"DivisionByZero"},{"code":6026,"name":"NewSizeLessThanCurrentSize"},{"code":6027,"name":"AccountTypeNotSupported"},{"code":6028,"name":"OnlyCanonicalPumpPoolsCanHaveCoinCreator"},{"code":6029,"name":"InvalidAdminSetCoinCreatorAuthority"},{"code":6030,"name":"StartTimeInThePast"},{"code":6031,"name":"EndTimeInThePast"},{"code":6032,"name":"EndTimeBeforeStartTime"},{"code":6033,"name":"TimeRangeTooLarge"},{"code":6034,"name":"EndTimeBeforeCurrentDay"},{"code":6035,"name":"SupplyUpdateForFinishedRange"},{"code":6036,"name":"DayIndexAfterEndIndex"},{"code":6037,"name":"DayInActiveRange"},{"code":6038,"name":"InvalidIncentiveMint"},{"code":6039,"name":"BuyNotEnoughQuoteTokensToCoverFees","msg":"buy: Not enough quote tokens to cover for fees."},{"code":6040,"name":"BuySlippageBelowMinBaseAmountOut","msg":"buy: slippage - would buy less tokens than expected min_base_amount_out"},{"code":6041,"name":"MayhemModeDisabled"},{"code":6042,"name":"OnlyPumpPoolsMayhemMode"},{"code":6043,"name":"MayhemModeInDesiredState"},{"code":6044,"name":"NotEnoughRemainingAccounts"},{"code":6045,"name":"InvalidSharingConfigBaseMint"},{"code":6046,"name":"InvalidSharingConfigCoinCreator"},{"code":6047,"name":"CoinCreatorMigratedToSharingConfig","msg":"coin creator has been migrated to sharing config, use pump_fees::reset_fee_sharing_config instead"},{"code":6048,"name":"CreatorVaultMigratedToSharingConfig","msg":"creator_vault has been migrated to sharing config, use pump:distribute_creator_fees instead"},{"code":6049,"name":"CashbackNotEnabled","msg":"Cashback is disabled"},{"code":6050,"name":"OnlyPumpPoolsCashback"},{"code":6051,"name":"CashbackNotInDesiredState"},{"code":6052,"name":"TokensInVaultLessThanCashbackEarned"},{"code":6053,"name":"BuybackFeeRecipientNotAuthorized","msg":"Buyback fee recipient not authorized"},{"code":6054,"name":"AllBuybackFeeRecipientsShouldBeNonZero"},{"code":6055,"name":"NotUniqueBuybackFeeRecipients"},{"code":6056,"name":"BuybackBasisPointsOutOfRange","msg":"buyback_basis_points must be <= 10_000"},{"code":6057,"name":"WrongBuybackFeeRecipientsCount","msg":"buyback fee recipients require exactly 8 remaining accounts (or none)"},{"code":6058,"name":"BuybackFeeRecipientMissing"}],"types":[{"name":"AdminSetCoinCreatorEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin_set_coin_creator_authority","type":"pubkey"},{"name":"base_mint","type":"pubkey"},{"name":"pool","type":"pubkey"},{"name":"old_coin_creator","type":"pubkey"},{"name":"new_coin_creator","type":"pubkey"}]}},{"name":"AdminUpdateTokenIncentivesEvent","type":{"kind":"struct","fields":[{"name":"start_time","type":"i64"},{"name":"end_time","type":"i64"},{"name":"day_number","type":"u64"},{"name":"token_supply_per_day","type":"u64"},{"name":"mint","type":"pubkey"},{"name":"seconds_in_a_day","type":"i64"},{"name":"timestamp","type":"i64"}]}},{"name":"BondingCurve","type":{"kind":"struct","fields":[{"name":"virtual_token_reserves","type":"u64"},{"name":"virtual_sol_reserves","type":"u64"},{"name":"real_token_reserves","type":"u64"},{"name":"real_sol_reserves","type":"u64"},{"name":"token_total_supply","type":"u64"},{"name":"complete","type":"bool"},{"name":"creator","type":"pubkey"},{"name":"is_mayhem_mode","type":"bool"},{"name":"is_cashback_coin","type":"bool"}]}},{"name":"BuyEvent","docs":["ix_name: \"buy\" | \"buy_exact_quote_in\""],"type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"base_amount_out","type":"u64"},{"name":"max_quote_amount_in","type":"u64"},{"name":"user_base_token_reserves","type":"u64"},{"name":"user_quote_token_reserves","type":"u64"},{"name":"pool_base_token_reserves","type":"u64"},{"name":"pool_quote_token_reserves","type":"u64"},{"name":"quote_amount_in","type":"u64"},{"name":"lp_fee_basis_points","type":"u64"},{"name":"lp_fee","type":"u64"},{"name":"protocol_fee_basis_points","type":"u64"},{"name":"protocol_fee","type":"u64"},{"name":"quote_amount_in_with_lp_fee","type":"u64"},{"name":"user_quote_amount_in","type":"u64"},{"name":"pool","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"user_base_token_account","type":"pubkey"},{"name":"user_quote_token_account","type":"pubkey"},{"name":"protocol_fee_recipient","type":"pubkey"},{"name":"protocol_fee_recipient_token_account","type":"pubkey"},{"name":"coin_creator","type":"pubkey"},{"name":"coin_creator_fee_basis_points","type":"u64"},{"name":"coin_creator_fee","type":"u64"},{"name":"track_volume","type":"bool"},{"name":"total_unclaimed_tokens","type":"u64"},{"name":"total_claimed_tokens","type":"u64"},{"name":"current_sol_volume","type":"u64"},{"name":"last_update_timestamp","type":"i64"},{"name":"min_base_amount_out","type":"u64"},{"name":"ix_name","type":"string"},{"name":"cashback_fee_basis_points","type":"u64"},{"name":"cashback","type":"u64"},{"name":"buyback_fee_basis_points","type":"u64"},{"name":"buyback_fee","type":"u64"}]}},{"name":"ClaimCashbackEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"amount","type":"u64"},{"name":"timestamp","type":"i64"},{"name":"total_claimed","type":"u64"},{"name":"total_cashback_earned","type":"u64"}]}},{"name":"ClaimTokenIncentivesEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"amount","type":"u64"},{"name":"timestamp","type":"i64"},{"name":"total_claimed_tokens","type":"u64"},{"name":"current_sol_volume","type":"u64"}]}},{"name":"CloseUserVolumeAccumulatorEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"timestamp","type":"i64"},{"name":"total_unclaimed_tokens","type":"u64"},{"name":"total_claimed_tokens","type":"u64"},{"name":"current_sol_volume","type":"u64"},{"name":"last_update_timestamp","type":"i64"}]}},{"name":"CollectCoinCreatorFeeEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"coin_creator","type":"pubkey"},{"name":"coin_creator_fee","type":"u64"},{"name":"coin_creator_vault_ata","type":"pubkey"},{"name":"coin_creator_token_account","type":"pubkey"}]}},{"name":"ConfigStatus","type":{"kind":"enum","variants":[{"name":"Paused"},{"name":"Active"}]}},{"name":"CreateConfigEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin","type":"pubkey"},{"name":"lp_fee_basis_points","type":"u64"},{"name":"protocol_fee_basis_points","type":"u64"},{"name":"protocol_fee_recipients","type":{"array":["pubkey",8]}},{"name":"coin_creator_fee_basis_points","type":"u64"},{"name":"admin_set_coin_creator_authority","type":"pubkey"}]}},{"name":"CreatePoolEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"index","type":"u16"},{"name":"creator","type":"pubkey"},{"name":"base_mint","type":"pubkey"},{"name":"quote_mint","type":"pubkey"},{"name":"base_mint_decimals","type":"u8"},{"name":"quote_mint_decimals","type":"u8"},{"name":"base_amount_in","type":"u64"},{"name":"quote_amount_in","type":"u64"},{"name":"pool_base_amount","type":"u64"},{"name":"pool_quote_amount","type":"u64"},{"name":"minimum_liquidity","type":"u64"},{"name":"initial_liquidity","type":"u64"},{"name":"lp_token_amount_out","type":"u64"},{"name":"pool_bump","type":"u8"},{"name":"pool","type":"pubkey"},{"name":"lp_mint","type":"pubkey"},{"name":"user_base_token_account","type":"pubkey"},{"name":"user_quote_token_account","type":"pubkey"},{"name":"coin_creator","type":"pubkey"},{"name":"is_mayhem_mode","type":"bool"}]}},{"name":"DepositEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"lp_token_amount_out","type":"u64"},{"name":"max_base_amount_in","type":"u64"},{"name":"max_quote_amount_in","type":"u64"},{"name":"user_base_token_reserves","type":"u64"},{"name":"user_quote_token_reserves","type":"u64"},{"name":"pool_base_token_reserves","type":"u64"},{"name":"pool_quote_token_reserves","type":"u64"},{"name":"base_amount_in","type":"u64"},{"name":"quote_amount_in","type":"u64"},{"name":"lp_mint_supply","type":"u64"},{"name":"pool","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"user_base_token_account","type":"pubkey"},{"name":"user_quote_token_account","type":"pubkey"},{"name":"user_pool_token_account","type":"pubkey"}]}},{"name":"DisableEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin","type":"pubkey"},{"name":"disable_create_pool","type":"bool"},{"name":"disable_deposit","type":"bool"},{"name":"disable_withdraw","type":"bool"},{"name":"disable_buy","type":"bool"},{"name":"disable_sell","type":"bool"}]}},{"name":"ExtendAccountEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"account","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"current_size","type":"u64"},{"name":"new_size","type":"u64"}]}},{"name":"FeeConfig","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"admin","type":"pubkey"},{"name":"flat_fees","type":{"defined":{"name":"Fees"}}},{"name":"fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"stable_fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}}]}},{"name":"FeeTier","type":{"kind":"struct","fields":[{"name":"market_cap_lamports_threshold","type":"u128"},{"name":"fees","type":{"defined":{"name":"Fees"}}}]}},{"name":"Fees","type":{"kind":"struct","fields":[{"name":"lp_fee_bps","type":"u64"},{"name":"protocol_fee_bps","type":"u64"},{"name":"creator_fee_bps","type":"u64"}]}},{"name":"GlobalConfig","type":{"kind":"struct","fields":[{"name":"admin","docs":["The admin pubkey"],"type":"pubkey"},{"name":"lp_fee_basis_points","type":"u64"},{"name":"protocol_fee_basis_points","type":"u64"},{"name":"disable_flags","docs":["Flags to disable certain functionality","bit 0 - Disable create pool","bit 1 - Disable deposit","bit 2 - Disable withdraw","bit 3 - Disable buy","bit 4 - Disable sell"],"type":"u8"},{"name":"protocol_fee_recipients","docs":["Addresses of the protocol fee recipients"],"type":{"array":["pubkey",8]}},{"name":"coin_creator_fee_basis_points","type":"u64"},{"name":"admin_set_coin_creator_authority","docs":["The admin authority for setting coin creators"],"type":"pubkey"},{"name":"whitelist_pda","type":"pubkey"},{"name":"reserved_fee_recipient","type":"pubkey"},{"name":"mayhem_mode_enabled","type":"bool"},{"name":"reserved_fee_recipients","type":{"array":["pubkey",7]}},{"name":"is_cashback_enabled","type":"bool"},{"name":"buyback_fee_recipients","type":{"array":["pubkey",8]}},{"name":"buyback_basis_points","type":"u64"}]}},{"name":"GlobalVolumeAccumulator","type":{"kind":"struct","fields":[{"name":"start_time","type":"i64"},{"name":"end_time","type":"i64"},{"name":"seconds_in_a_day","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"total_token_supply","type":{"array":["u64",30]}},{"name":"sol_volumes","type":{"array":["u64",30]}}]}},{"name":"InitUserVolumeAccumulatorEvent","type":{"kind":"struct","fields":[{"name":"payer","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"timestamp","type":"i64"}]}},{"name":"MigratePoolCoinCreatorEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"base_mint","type":"pubkey"},{"name":"pool","type":"pubkey"},{"name":"sharing_config","type":"pubkey"},{"name":"old_coin_creator","type":"pubkey"},{"name":"new_coin_creator","type":"pubkey"}]}},{"name":"OptionBool","type":{"kind":"struct","fields":["bool"]}},{"name":"Pool","type":{"kind":"struct","fields":[{"name":"pool_bump","type":"u8"},{"name":"index","type":"u16"},{"name":"creator","type":"pubkey"},{"name":"base_mint","type":"pubkey"},{"name":"quote_mint","type":"pubkey"},{"name":"lp_mint","type":"pubkey"},{"name":"pool_base_token_account","type":"pubkey"},{"name":"pool_quote_token_account","type":"pubkey"},{"name":"lp_supply","docs":["True circulating supply without burns and lock-ups"],"type":"u64"},{"name":"coin_creator","type":"pubkey"},{"name":"is_mayhem_mode","type":"bool"},{"name":"is_cashback_coin","type":"bool"}]}},{"name":"ReservedFeeRecipientsEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"reserved_fee_recipient","type":"pubkey"},{"name":"reserved_fee_recipients","type":{"array":["pubkey",7]}}]}},{"name":"SellEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"base_amount_in","type":"u64"},{"name":"min_quote_amount_out","type":"u64"},{"name":"user_base_token_reserves","type":"u64"},{"name":"user_quote_token_reserves","type":"u64"},{"name":"pool_base_token_reserves","type":"u64"},{"name":"pool_quote_token_reserves","type":"u64"},{"name":"quote_amount_out","type":"u64"},{"name":"lp_fee_basis_points","type":"u64"},{"name":"lp_fee","type":"u64"},{"name":"protocol_fee_basis_points","type":"u64"},{"name":"protocol_fee","type":"u64"},{"name":"quote_amount_out_without_lp_fee","type":"u64"},{"name":"user_quote_amount_out","type":"u64"},{"name":"pool","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"user_base_token_account","type":"pubkey"},{"name":"user_quote_token_account","type":"pubkey"},{"name":"protocol_fee_recipient","type":"pubkey"},{"name":"protocol_fee_recipient_token_account","type":"pubkey"},{"name":"coin_creator","type":"pubkey"},{"name":"coin_creator_fee_basis_points","type":"u64"},{"name":"coin_creator_fee","type":"u64"},{"name":"cashback_fee_basis_points","type":"u64"},{"name":"cashback","type":"u64"},{"name":"buyback_fee_basis_points","type":"u64"},{"name":"buyback_fee","type":"u64"}]}},{"name":"SetBondingCurveCoinCreatorEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"base_mint","type":"pubkey"},{"name":"pool","type":"pubkey"},{"name":"bonding_curve","type":"pubkey"},{"name":"coin_creator","type":"pubkey"}]}},{"name":"SetMetaplexCoinCreatorEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"base_mint","type":"pubkey"},{"name":"pool","type":"pubkey"},{"name":"metadata","type":"pubkey"},{"name":"coin_creator","type":"pubkey"}]}},{"name":"Shareholder","type":{"kind":"struct","fields":[{"name":"address","type":"pubkey"},{"name":"share_bps","type":"u16"}]}},{"name":"SharingConfig","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"version","type":"u8"},{"name":"status","type":{"defined":{"name":"ConfigStatus"}}},{"name":"mint","type":"pubkey"},{"name":"admin","type":"pubkey"},{"name":"admin_revoked","type":"bool"},{"name":"shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}}]}},{"name":"SyncUserVolumeAccumulatorEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"total_claimed_tokens_before","type":"u64"},{"name":"total_claimed_tokens_after","type":"u64"},{"name":"timestamp","type":"i64"}]}},{"name":"UpdateAdminEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin","type":"pubkey"},{"name":"new_admin","type":"pubkey"}]}},{"name":"UpdateFeeConfigEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin","type":"pubkey"},{"name":"lp_fee_basis_points","type":"u64"},{"name":"protocol_fee_basis_points","type":"u64"},{"name":"protocol_fee_recipients","type":{"array":["pubkey",8]}},{"name":"coin_creator_fee_basis_points","type":"u64"},{"name":"admin_set_coin_creator_authority","type":"pubkey"}]}},{"name":"UserVolumeAccumulator","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"needs_claim","type":"bool"},{"name":"total_unclaimed_tokens","type":"u64"},{"name":"total_claimed_tokens","type":"u64"},{"name":"current_sol_volume","type":"u64"},{"name":"last_update_timestamp","type":"i64"},{"name":"has_total_claimed_tokens","type":"bool"},{"name":"cashback_earned","type":"u64"},{"name":"total_cashback_claimed","type":"u64"}]}},{"name":"WithdrawEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"lp_token_amount_in","type":"u64"},{"name":"min_base_amount_out","type":"u64"},{"name":"min_quote_amount_out","type":"u64"},{"name":"user_base_token_reserves","type":"u64"},{"name":"user_quote_token_reserves","type":"u64"},{"name":"pool_base_token_reserves","type":"u64"},{"name":"pool_quote_token_reserves","type":"u64"},{"name":"base_amount_out","type":"u64"},{"name":"quote_amount_out","type":"u64"},{"name":"lp_mint_supply","type":"u64"},{"name":"pool","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"user_base_token_account","type":"pubkey"},{"name":"user_quote_token_account","type":"pubkey"},{"name":"user_pool_token_account","type":"pubkey"}]}}]} \ No newline at end of file diff --git a/idls/pumpup.PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd.json b/idls/pumpup.PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd.json new file mode 100644 index 0000000..3230b1d --- /dev/null +++ b/idls/pumpup.PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd.json @@ -0,0 +1 @@ +{"address":"PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd","metadata":{"name":"pumpup","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"add_liquidity","discriminator":[181,157,89,67,143,182,52,72],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,112,111,111,108]},{"kind":"account","path":"pool.token_a_mint","account":"Pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"token_a_vault","docs":["CHECK"],"writable":true},{"name":"token_b_vault","docs":["CHECK"],"writable":true},{"name":"lp_mint","docs":["CHECK"],"writable":true},{"name":"user_token_a","writable":true},{"name":"user_token_b","writable":true},{"name":"user_lp_token","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"token_a_amount","type":"u64"},{"name":"token_b_amount","type":"u64"},{"name":"minimum_lp_tokens","type":"u64"}]},{"name":"buy","discriminator":[102,6,61,18,1,218,235,234],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,99,111,110,102,105,103]}]}},{"name":"mint"},{"name":"pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_sol_account"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool_sol_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,112,111,111,108]},{"kind":"account","path":"mint"}]}},{"name":"ai_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"config"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pumpup_fee","writable":true},{"name":"user_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"token_amount","type":"u64"},{"name":"max_sol_amount","type":"u64"}]},{"name":"create","discriminator":[24,30,200,40,5,28,7,119],"accounts":[{"name":"creator","writable":true,"signer":true},{"name":"mint","writable":true,"signer":true},{"name":"mint_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"pool_sol_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,112,111,111,108]},{"kind":"account","path":"mint"}]}},{"name":"pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_sol_account"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","writable":true},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"create_leverage","discriminator":[71,62,173,225,174,65,205,227],"accounts":[{"name":"creator","writable":true,"signer":true},{"name":"mint","writable":true,"signer":true},{"name":"mint_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"pool_sol_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,112,111,111,108]},{"kind":"account","path":"mint"}]}},{"name":"pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_sol_account"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","writable":true},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"feed","discriminator":[46,213,237,176,190,113,182,94],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,99,111,110,102,105,103]}]}},{"name":"mint"},{"name":"original","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,111,114,105,103,105,110,97,108,46,100,97,116,97]},{"kind":"account","path":"mint"}]}},{"name":"migration_address","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"datas","type":{"array":["f64",33]}}]},{"name":"inference","discriminator":[1,249,84,211,187,82,82,51],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,99,111,110,102,105,103]}]}},{"name":"mint"},{"name":"original","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,111,114,105,103,105,110,97,108,46,100,97,116,97]},{"kind":"account","path":"mint"}]}},{"name":"pool_sol_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,112,111,111,108]},{"kind":"account","path":"mint"}]}},{"name":"migration_address","writable":true,"signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"period","type":"u64"}]},{"name":"init_ai_token","discriminator":[195,26,226,86,175,143,32,58],"accounts":[{"name":"creator","writable":true,"signer":true},{"name":"config","pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,99,111,110,102,105,103]}]}},{"name":"mint"},{"name":"ai_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"config"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"initialize","discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,99,111,110,102,105,103]}]}},{"name":"fee_address","docs":["CHECK"],"writable":true},{"name":"migration_address","docs":["CHECK"],"writable":true},{"name":"mint_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"authority_address","writable":true,"signer":true},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"fee_rate","type":"u64"}]},{"name":"initialize_divide","discriminator":[82,21,35,146,161,99,102,28],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,112,111,111,108]},{"kind":"account","path":"pool.token_a_mint","account":"Pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"token_b_mint"},{"name":"fee_address"},{"name":"fee_recipient","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_address"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_b_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"payer","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"fee_rate","type":"u16"}]},{"name":"initialize_lp_token","discriminator":[124,21,221,232,250,37,120,35],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,112,111,111,108]},{"kind":"account","path":"pool.token_a_mint","account":"Pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"lp_mint","docs":["LP代币Mint账户 - 用于铸造流动性提供者代币"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,108,112,95,109,105,110,116]},{"kind":"account","path":"pool"}]}},{"name":"payer","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[]},{"name":"initialize_pool","discriminator":[95,180,10,172,84,174,232,40],"accounts":[{"name":"pool","docs":["新的流动性池账户"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,112,111,111,108]},{"kind":"account","path":"token_a_mint"},{"kind":"account","path":"token_b_mint"}]}},{"name":"token_a_mint","docs":["代币A的Mint账户"]},{"name":"token_b_mint","docs":["代币B的Mint账户(USDT)"]},{"name":"config","pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,99,111,110,102,105,103]}]}},{"name":"fee_address"},{"name":"fee_recipient","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_address"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_b_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"payer","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"initialize_tokena","discriminator":[209,97,45,4,27,32,11,205],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,112,111,111,108]},{"kind":"account","path":"pool.token_a_mint","account":"Pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"token_a_mint","docs":["代币Mint账户"]},{"name":"token_a_vault","docs":["代币的保险库账户 - 存储池中的对应代币A"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,118,97,117,108,116]},{"kind":"account","path":"pool"},{"kind":"account","path":"token_a_mint"}]}},{"name":"payer","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[]},{"name":"initialize_tokenb","discriminator":[218,101,38,193,111,170,244,254],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,112,111,111,108]},{"kind":"account","path":"pool.token_a_mint","account":"Pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"token_b_mint","docs":["代币B的Mint账户(USDT)"]},{"name":"token_b_vault","docs":["代币的保险库账户 - 存储池中的对应代币A"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,118,97,117,108,116]},{"kind":"account","path":"pool"},{"kind":"account","path":"token_b_mint"}]}},{"name":"payer","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[]},{"name":"migrate_pool","discriminator":[55,170,171,123,210,69,39,172],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,112,111,111,108]},{"kind":"account","path":"pool.token_a_mint","account":"Pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"token_b_mint"},{"name":"fee_address2"},{"name":"fee_recipient2","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_address2"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_b_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"payer","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[]},{"name":"quick_create","discriminator":[127,6,145,127,36,205,4,225],"accounts":[{"name":"creator","writable":true,"signer":true},{"name":"mint","writable":true,"signer":true},{"name":"mint_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"pool_sol_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,112,111,111,108]},{"kind":"account","path":"mint"}]}},{"name":"pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_sol_account"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","writable":true},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"quick_create_leverage","discriminator":[24,2,81,246,97,77,131,127],"accounts":[{"name":"creator","writable":true,"signer":true},{"name":"mint","writable":true,"signer":true},{"name":"mint_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"pool_sol_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,112,111,111,108]},{"kind":"account","path":"mint"}]}},{"name":"pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_sol_account"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","writable":true},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"remove_liquidity","docs":["移除流动性"],"discriminator":[80,85,209,72,24,206,177,108],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,112,111,111,108]},{"kind":"account","path":"pool.token_a_mint","account":"Pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"token_a_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,118,97,117,108,116]},{"kind":"account","path":"pool"},{"kind":"account","path":"pool.token_a_mint","account":"Pool"}]}},{"name":"token_b_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,118,97,117,108,116]},{"kind":"account","path":"pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,108,112,95,109,105,110,116]},{"kind":"account","path":"pool"}]}},{"name":"user_token_a","writable":true},{"name":"user_token_b","writable":true},{"name":"user_lp_token","writable":true},{"name":"user","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"lp_token_amount","type":"u64"},{"name":"minimum_token_a","type":"u64"},{"name":"minimum_token_b","type":"u64"}]},{"name":"sell","discriminator":[51,230,133,164,1,127,131,173],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,99,111,110,102,105,103]}]}},{"name":"mint"},{"name":"pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_sol_account"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool_sol_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,112,111,111,108]},{"kind":"account","path":"mint"}]}},{"name":"ai_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"config"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pumpup_fee","writable":true},{"name":"user_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"token_amount","type":"u64"},{"name":"min_sol_amount","type":"u64"}]},{"name":"swap","discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,112,111,111,108]},{"kind":"account","path":"pool.token_a_mint","account":"Pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"token_a_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,118,97,117,108,116]},{"kind":"account","path":"pool"},{"kind":"account","path":"pool.token_a_mint","account":"Pool"}]}},{"name":"token_b_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,118,97,117,108,116]},{"kind":"account","path":"pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"fee_recipient_token","writable":true},{"name":"fee_recipient_token2","writable":true},{"name":"user","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"}]},{"name":"withdraw","discriminator":[183,18,70,156,148,109,161,34],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,99,111,110,102,105,103]}]}},{"name":"mint","writable":true},{"name":"pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_sol_account"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool_sol_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,112,111,111,108]},{"kind":"account","path":"mint"}]}},{"name":"ai_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"config"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pumpup_fee","writable":true},{"name":"to_sol_account","writable":true,"signer":true},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"launch_account","writable":true},{"name":"launch_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"launch_account"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"pumpup_migration_amount","type":"u64"}]}],"accounts":[{"name":"AIOriginalDataAccount","discriminator":[125,158,65,141,197,244,39,160]},{"name":"BondingCurve","discriminator":[23,183,248,55,96,216,172,96]},{"name":"Pool","discriminator":[241,154,109,4,17,177,109,188]},{"name":"PumpupConfiguration","discriminator":[36,175,125,65,27,3,84,217]}],"events":[{"name":"CreateMintEvent","discriminator":[239,193,191,44,156,194,85,141]},{"name":"FeedEvent","discriminator":[250,174,105,83,244,245,23,11]},{"name":"MintLeverageEvent","discriminator":[64,149,175,150,7,170,9,69]},{"name":"RWAInitEvent","discriminator":[171,102,39,251,156,173,88,8]},{"name":"RWATradeEvent","discriminator":[163,38,91,101,120,148,151,90]},{"name":"TradeEvent","discriminator":[189,219,127,211,78,230,97,238]}],"errors":[{"code":6000,"name":"InvalidAmount","msg":"Invalid amount provided"},{"code":6001,"name":"InvalidFeeRecipient","msg":"Invalid fee recipient"},{"code":6002,"name":"InsufficientLiquidity","msg":"Insufficient liquidity in the pool"},{"code":6003,"name":"SlippageExceeded","msg":"Slippage tolerance exceeded"},{"code":6004,"name":"InvalidTokenMint","msg":"Invalid token mint"},{"code":6005,"name":"DuplicateTokens","msg":"Duplicate tokens"},{"code":6006,"name":"InvalidTokenAVault","msg":"Invalid token_a_vault account"},{"code":6007,"name":"InvalidTokenBVault","msg":"Invalid token_b_vault account"},{"code":6008,"name":"InvalidLpMint","msg":"Invalid lp_mint account"},{"code":6009,"name":"PoolPaused","msg":"Pool is paused"},{"code":6010,"name":"Unauthorized","msg":"Unauthorized access"},{"code":6011,"name":"InvalidFeeRate","msg":"Invalid fee rate"},{"code":6012,"name":"PoolAlreadyExists","msg":"Pool already exists"},{"code":6013,"name":"MathOverflow","msg":"Mathematical overflow"},{"code":6014,"name":"InvalidLpTokenAmount","msg":"Invalid LP token amount"},{"code":6015,"name":"MinimumLiquidityNotMet","msg":"Minimum liquidity not met"},{"code":6016,"name":"TokenBMustBeUsdt","msg":"Token B must be USDT"},{"code":6017,"name":"InvalidPayer","msg":"Invalid payer"}],"types":[{"name":"AIOriginalDataAccount","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"period","type":"u64"},{"name":"period_done","type":"bool"},{"name":"regulated","type":"bool"},{"name":"sol","type":"f64"},{"name":"datas","type":{"array":["f64",42]}}]}},{"name":"BondingCurve","type":{"kind":"struct","fields":[{"name":"launch_token_surplus","type":"u64"},{"name":"virtual_sol","type":"u64"},{"name":"real_sol","type":"u64"},{"name":"pool_sol_reserves","type":"u64"},{"name":"pool_token_reserves","type":"u64"},{"name":"current_leverage_index","type":"u8"},{"name":"leverage","type":{"vec":{"array":["u64",3]}}}]}},{"name":"CreateMintEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"pool_sol_address","type":"pubkey"},{"name":"pool_token_address","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"timestamp","type":"i64"},{"name":"launch_token_surplus","type":"u64"},{"name":"pool_sol_reserves","type":"u64"},{"name":"pool_token_reserves","type":"u64"},{"name":"is_leverage","type":"bool"}]}},{"name":"FeedEvent","type":{"kind":"struct","fields":[{"name":"period","type":"u64"}]}},{"name":"InitProgress","type":{"kind":"enum","variants":[{"name":"START"},{"name":"COMPLETED"}]}},{"name":"MintLeverageEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"period","type":"u64"},{"name":"regulated","type":"bool"},{"name":"timestamp","type":"i64"},{"name":"launch_token_surplus","type":"u64"},{"name":"pool_sol_reserves","type":"u64"},{"name":"pool_token_reserves","type":"u64"},{"name":"leverage","type":{"vec":{"array":["u64",3]}}}]}},{"name":"Pool","type":{"kind":"struct","fields":[{"name":"token_a_mint","docs":["代币A的mint地址"],"type":"pubkey"},{"name":"token_b_mint","docs":["代币B的mint地址(USDT)"],"type":"pubkey"},{"name":"token_a_vault","docs":["代币A金库账户"],"type":"pubkey"},{"name":"token_b_vault","docs":["代币B金库账户"],"type":"pubkey"},{"name":"lp_mint","docs":["LP代币mint"],"type":"pubkey"},{"name":"fee_recipient","docs":["手续费接收地址"],"type":"pubkey"},{"name":"token_a_reserve","docs":["代币A储备量"],"type":"u64"},{"name":"token_b_reserve","docs":["代币B储备量"],"type":"u64"},{"name":"lp_token_supply","docs":["LP代币总供应量"],"type":"u64"},{"name":"fee_rate","docs":["手续费率(基点,100=1%)"],"type":"u16"},{"name":"bump","docs":["PDA bump"],"type":"u8"},{"name":"fee_recipient2","type":"pubkey"},{"name":"fee_rate2","docs":["分润比(基点,100=1%)"],"type":"u16"}]}},{"name":"PumpupConfiguration","serialization":"bytemuckunsafe","repr":{"kind":"rust","packed":true},"type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"fee_rate","type":"u64"},{"name":"authority_address","type":"pubkey"},{"name":"fee_address","type":"pubkey"},{"name":"migration_address","type":"pubkey"}]}},{"name":"RWAInitEvent","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"init_progress","type":{"defined":{"name":"InitProgress"}}},{"name":"token_a_mint","type":"pubkey"},{"name":"token_b_mint","type":"pubkey"},{"name":"token_a_vault","type":"pubkey"},{"name":"token_b_vault","type":"pubkey"},{"name":"timestamp","type":"i64"},{"name":"lp_mint","type":"pubkey"},{"name":"fee_recipient","type":"pubkey"},{"name":"token_a_reserve","type":"u64"},{"name":"token_b_reserve","type":"u64"},{"name":"lp_token_supply","type":"u64"},{"name":"fee_rate","type":"u16"}]}},{"name":"RWATradeEvent","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"event_type","type":{"defined":{"name":"TradeEventType"}}},{"name":"token_a_mint","type":"pubkey"},{"name":"token_a_amount","type":"u64"},{"name":"token_b_mint","type":"pubkey"},{"name":"token_b_amount","type":"u64"},{"name":"fee_amount","type":"u64"},{"name":"user","type":"pubkey"},{"name":"pool_tokena_reserves","type":"u64"},{"name":"pool_tokenb_reserves","type":"u64"},{"name":"timestamp","type":"i64"}]}},{"name":"TradeEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"sol_amount","type":"u64"},{"name":"token_amount","type":"u64"},{"name":"ai_token_amount","type":"u64"},{"name":"event_type","type":{"defined":{"name":"TradeEventType"}}},{"name":"user","type":"pubkey"},{"name":"timestamp","type":"i64"},{"name":"pumpup_fee","type":"u64"},{"name":"pool_real_sol_amount","type":"u64"},{"name":"pool_sol_reserves","type":"u64"},{"name":"pool_token_reserves","type":"u64"}]}},{"name":"TradeEventType","type":{"kind":"enum","variants":[{"name":"BUY"},{"name":"SELL"},{"name":"WITHDRAW"},{"name":"RwaBuy"},{"name":"RwaSell"}]}}]} \ No newline at end of file diff --git a/idls/raydium_clmm.CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK.json b/idls/raydium_clmm.CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK.json new file mode 100644 index 0000000..de07bf6 --- /dev/null +++ b/idls/raydium_clmm.CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK.json @@ -0,0 +1 @@ +{"address":"CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK","metadata":{"name":"raydium_clmm","version":"0.1.0","spec":"0.1.0","description":"Anchor client and source for Raydium concentrated liquidity AMM"},"instructions":[{"name":"close_limit_order","docs":["Close a limit order","Closes the limit order account when unfilled amount is zero","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[76,124,128,15,213,87,37,250],"accounts":[{"name":"signer","signer":true},{"name":"rent_receiver","writable":true},{"name":"limit_order","writable":true}],"args":[]},{"name":"close_position","docs":["Close the user's position and NFT account. If the NFT mint belongs to token2022, it will also be closed and the funds returned to the NFT owner.","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[123,134,81,0,49,68,98,98],"accounts":[{"name":"nft_owner","docs":["The position nft owner"],"writable":true,"signer":true},{"name":"position_nft_mint","docs":["Mint address bound to the personal position."],"writable":true},{"name":"position_nft_account","docs":["User token account where position NFT be minted to"],"writable":true},{"name":"personal_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"system_program","docs":["System program to close the position state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Token/Token2022 program to close token/mint account"]}],"args":[]},{"name":"close_protocol_position","docs":["The CLMM protocol decides to discard the protocol position account, which can reduce users' opening costs.","After the original protocol position account is closed, the gas fee will be refunded to the user who created it.","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[201,117,152,144,85,85,108,178],"accounts":[{"name":"admin","writable":true,"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"protocol_position","writable":true}],"args":[]},{"name":"collect_fund_fee","docs":["Collect the fund fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[167,138,78,149,223,194,6,126],"accounts":[{"name":"owner","docs":["Only admin or fund_owner can collect fee now"],"signer":true},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores fund_owner"]},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_protocol_fee","docs":["Collect the protocol fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[136,136,252,221,194,66,126,89],"accounts":[{"name":"owner","docs":["Only admin or config owner can collect fee now"],"signer":true},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores owner"]},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_remaining_rewards","docs":["Collect remaining reward token for reward founder","","# Arguments","","* `ctx` - The context of accounts","* `reward_index` - the index to reward info",""],"discriminator":[18,237,166,197,34,16,213,144],"accounts":[{"name":"reward_funder","docs":["The founder who init reward info previously"],"signer":true},{"name":"funder_token_account","docs":["The funder's reward token account"],"writable":true},{"name":"pool_state","docs":["Set reward for this pool"],"writable":true},{"name":"reward_token_vault","docs":["Reward vault transfer remaining token to founder token account"],"writable":true},{"name":"reward_vault_mint","docs":["The mint of reward token vault"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"create_amm_config","docs":["# Arguments","","* `ctx` - The accounts needed by instruction.","* `index` - The index of amm config, there may be multiple config.","* `tick_spacing` - The tickspacing binding with config, cannot be changed.","* `trade_fee_rate` - Trade fee rate, can be changed.","* `protocol_fee_rate` - The rate of protocol fee within trade fee.","* `fund_fee_rate` - The rate of fund fee within trade fee.","* `limit_fee_rate` - The rate of limit order fee.",""],"discriminator":[137,52,237,212,215,117,108,104],"accounts":[{"name":"owner","docs":["Address to be set as protocol owner."],"writable":true,"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"amm_config","docs":["Initialize config state account to store protocol owner address and fee rates."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,109,109,95,99,111,110,102,105,103]},{"kind":"arg","path":"index"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"u16"},{"name":"tick_spacing","type":"u16"},{"name":"trade_fee_rate","type":"u32"},{"name":"protocol_fee_rate","type":"u32"},{"name":"fund_fee_rate","type":"u32"}]},{"name":"create_customizable_pool","docs":["Creates a pool for the given token pair with customizable parameters","","# Arguments","","* `ctx` - The context of accounts","* `customizable_params` - the customizable parameters",""],"discriminator":[43,68,212,167,89,47,164,1],"accounts":[{"name":"pool_creator","docs":["Address paying to create the pool"],"writable":true,"signer":true},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"pool_state","docs":["Initialize an account to store the pool state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"token_mint_0"},{"kind":"account","path":"token_mint_1"}]}},{"name":"token_mint_0","docs":["Token_0 mint, the key must be smaller then token_1 mint."]},{"name":"token_mint_1","docs":["Token_1 mint"]},{"name":"token_vault_0","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_0"}]}},{"name":"token_vault_1","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_1"}]}},{"name":"observation_state","docs":["Initialize an account to store oracle observations"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"tick_array_bitmap","docs":["Initialize an account to store if a tick array is initialized."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,116,105,99,107,95,97,114,114,97,121,95,98,105,116,109,97,112,95,101,120,116,101,110,115,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"token_program_0","docs":["Spl token program or token program 2022"]},{"name":"token_program_1","docs":["Spl token program or token program 2022"]},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"customizable_params","type":{"defined":{"name":"CreateCustomizableParams"}}}]},{"name":"create_dynamic_fee_config","docs":["Create dynamic fee config","","# Arguments","","* `ctx` - The context of accounts","* `index` - The index of dynamic fee config","* `filter_period` - The period of filter","* `decay_period` - The period of decay","* `reduction_factor` - The reduction factor","* `adaptive_fee_control_factor` - The adaptive fee control factor","* `max_volatility_accumulator` - The max volatility accumulator","* `tick_group_size` - The tick group size"],"discriminator":[189,14,181,120,85,118,227,62],"accounts":[{"name":"owner","writable":true,"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"dynamic_fee_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,121,110,97,109,105,99,95,102,101,101,95,99,111,110,102,105,103]},{"kind":"arg","path":"index"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"u16"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"dynamic_fee_control","type":"u32"},{"name":"max_volatility_accumulator","type":"u32"}]},{"name":"create_operation_account","docs":["Creates an operation account for the program","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[63,87,148,33,109,35,8,104],"accounts":[{"name":"owner","docs":["Address to be set as operation account owner."],"writable":true,"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"operation_state","docs":["Initialize operation state account to store operation owner address and white list mint."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_pool","docs":["Creates a pool for the given token pair and the initial price","","# Arguments","","* `ctx` - The context of accounts","* `sqrt_price_x64` - the initial sqrt price (amount_token_1 / amount_token_0) of the pool as a Q64.64","Note: The open_time must be smaller than the current block_timestamp on chain."],"discriminator":[233,146,209,142,207,104,64,188],"accounts":[{"name":"pool_creator","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"pool_state","docs":["Initialize an account to store the pool state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"token_mint_0"},{"kind":"account","path":"token_mint_1"}]}},{"name":"token_mint_0","docs":["Token_0 mint, the key must be smaller then token_1 mint."]},{"name":"token_mint_1","docs":["Token_1 mint"]},{"name":"token_vault_0","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_0"}]}},{"name":"token_vault_1","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_1"}]}},{"name":"observation_state","docs":["Initialize an account to store oracle observations"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"tick_array_bitmap","docs":["Initialize an account to store if a tick array is initialized."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,116,105,99,107,95,97,114,114,97,121,95,98,105,116,109,97,112,95,101,120,116,101,110,115,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"token_program_0","docs":["Spl token program or token program 2022"]},{"name":"token_program_1","docs":["Spl token program or token program 2022"]},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"sqrt_price_x64","type":"u128"},{"name":"open_time","type":"u64"}]},{"name":"create_support_mint_associated","docs":["Create support token22 mint account which can create pool and send rewards while ignoring unsupported extensions."],"discriminator":[17,251,65,92,136,242,14,169],"accounts":[{"name":"owner","docs":["Address to be set as protocol owner."],"writable":true,"signer":true},{"name":"token_mint","docs":["Support token mint"]},{"name":"support_mint_associated","docs":["Initialize support mint state account to store support mint address and bump."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,117,112,112,111,114,116,95,109,105,110,116]},{"kind":"account","path":"token_mint"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"decrease_limit_order","docs":["Decrease/Cancel a limit order","First settle the filled portion of the order; only the remaining unfilled amount can be decreased.","Even if the specified amount is very large, the decrease will not exceed the remaining unfilled order amount.","It is recommended to call the `settle_limit_order` instruction before invoking this instruction to settle any filled portions of the order.","Make sure to check your order's remaining unfilled amount.","# Arguments","","* `ctx` - The context of accounts","* `amount` - The decrease amount of the order,","* `amount_min` - The minimum amount of the order, which serves as a slippage check",""],"discriminator":[117,157,60,103,66,49,163,0],"accounts":[{"name":"owner","signer":true},{"name":"pool_state","writable":true},{"name":"tick_array","writable":true},{"name":"limit_order","writable":true},{"name":"input_token_account","docs":["The payer's limit order token account"],"writable":true},{"name":"output_token_account","docs":["The owner's output token account (opposite direction from input)"],"writable":true},{"name":"input_vault","docs":["The address that holds limit order token"],"writable":true},{"name":"output_vault","docs":["The address that holds output tokens"],"writable":true},{"name":"input_vault_mint","docs":["The mint of token vault"]},{"name":"output_vault_mint","docs":["The mint of output vault"]},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["SPL program 2022 for token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount","type":"u64"},{"name":"amount_min","type":"u64"}]},{"name":"decrease_liquidity","docs":["#[deprecated(note = \"Use `decrease_liquidity_v2` instead.\")]","Decreases liquidity for an existing position","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The amount by which liquidity will be decreased","* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity","* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity",""],"discriminator":[160,38,208,111,104,91,44,1],"accounts":[{"name":"nft_owner","docs":["The position owner or delegated authority"],"signer":true},{"name":"nft_account","docs":["The token account for the tokenized position"]},{"name":"personal_position","docs":["Decrease liquidity for this position"],"writable":true},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"token_vault_0","docs":["Token_0 vault"],"writable":true},{"name":"token_vault_1","docs":["Token_1 vault"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"recipient_token_account_0","docs":["The destination token account for receive amount_0"],"writable":true},{"name":"recipient_token_account_1","docs":["The destination token account for receive amount_1"],"writable":true},{"name":"token_program","docs":["SPL program to transfer out tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_min","type":"u64"},{"name":"amount_1_min","type":"u64"}]},{"name":"decrease_liquidity_v2","docs":["Decreases liquidity for an existing position, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The amount by which liquidity will be decreased","* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity","* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity",""],"discriminator":[58,127,188,62,79,82,196,96],"accounts":[{"name":"nft_owner","docs":["The position owner or delegated authority"],"signer":true},{"name":"nft_account","docs":["The token account for the tokenized position"]},{"name":"personal_position","docs":["Decrease liquidity for this position"],"writable":true},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"token_vault_0","docs":["Token_0 vault"],"writable":true},{"name":"token_vault_1","docs":["Token_1 vault"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"recipient_token_account_0","docs":["The destination token account for receive amount_0"],"writable":true},{"name":"recipient_token_account_1","docs":["The destination token account for receive amount_1"],"writable":true},{"name":"token_program","docs":["SPL program to transfer out tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_min","type":"u64"},{"name":"amount_1_min","type":"u64"}]},{"name":"increase_limit_order","docs":["Increase a limit order","","# Arguments","","* `ctx` - The context of accounts","* `amount` - The increase amount of the order","* `is_increase` - The direction of the order, true: increase, false: decrease",""],"discriminator":[177,144,89,236,250,186,125,99],"accounts":[{"name":"owner","signer":true},{"name":"pool_state"},{"name":"tick_array","writable":true},{"name":"limit_order","writable":true},{"name":"input_token_account","docs":["The payer's limit order token account"],"writable":true},{"name":"input_vault","docs":["The address that holds limit order token"],"writable":true},{"name":"input_vault_mint","docs":["The mint of token vault"]},{"name":"input_token_program","docs":["SPL-TOKEN / SPL-TOKEN2022 program for token transfers"]}],"args":[{"name":"amount","type":"u64"}]},{"name":"increase_liquidity","docs":["#[deprecated(note = \"Use `increase_liquidity_v2` instead.\")]","Increases liquidity for an existing position, with amount paid by `payer`","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The desired liquidity to be added, can't be zero","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check",""],"discriminator":[46,156,243,118,13,205,251,178],"accounts":[{"name":"nft_owner","docs":["Pays to mint the position"],"signer":true},{"name":"nft_account","docs":["The token account for nft"]},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"personal_position","docs":["Increase liquidity for this position"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"token_account_0","docs":["The payer's token account for token_0"],"writable":true},{"name":"token_account_1","docs":["The token account spending token_1 to mint the position"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"}]},{"name":"increase_liquidity_v2","docs":["Increases liquidity for an existing position, with amount paid by `payer`, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The desired liquidity to be added, if zero, calculate liquidity base amount_0 or amount_1 according base_flag","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `base_flag` - must be specified if liquidity is zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[133,29,89,223,69,238,176,10],"accounts":[{"name":"nft_owner","docs":["Pays to mint the position"],"signer":true},{"name":"nft_account","docs":["The token account for nft"]},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"personal_position","docs":["Increase liquidity for this position"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"token_account_0","docs":["The payer's token account for token_0"],"writable":true},{"name":"token_account_1","docs":["The token account spending token_1 to mint the position"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"initialize_reward","docs":["Initialize a reward info for a given pool and reward index","","# Arguments","","* `ctx` - The context of accounts","* `reward_index` - the index to reward info","* `open_time` - reward open timestamp","* `end_time` - reward end timestamp","* `emissions_per_second_x64` - Token reward per second are earned per unit of liquidity.",""],"discriminator":[95,135,192,196,242,129,230,68],"accounts":[{"name":"reward_funder","docs":["The founder deposit reward token to vault"],"writable":true,"signer":true},{"name":"funder_token_account","writable":true},{"name":"amm_config","docs":["For check the reward_funder authority"]},{"name":"pool_state","docs":["Set reward for this pool"],"writable":true},{"name":"operation_state","docs":["load info from the account to judge reward permission"],"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"reward_token_mint","docs":["Reward mint"]},{"name":"reward_token_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,114,101,119,97,114,100,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"reward_token_mint"}]}},{"name":"reward_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"param","type":{"defined":{"name":"InitializeRewardParam"}}}]},{"name":"open_limit_order","docs":["Place a limit order","# Arguments","","* `ctx` - The context of accounts","* `nonce_index` - The limit order nonce account index, used to create user's order nonce PDA account.","* `zero_for_one` - The direction of the order, true: zero for one, false: one for zero","* `tick_index` - The index of the tick","* `amount` - The amount of the order","* `with_metadata` - The flag indicating whether to create NFT mint metadata",""],"discriminator":[157,32,218,183,71,29,18,147],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"pool_state","docs":["The pool"],"writable":true},{"name":"tick_array","writable":true},{"name":"limit_order_nonce","docs":["The order nonce account PDA"],"writable":true},{"name":"limit_order","docs":["The order account PDA"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"payer"},{"kind":"account","path":"limit_order_nonce"},{"kind":"account","path":"limit_order_nonce.order_nonce","account":"LimitOrderNonce"}]}},{"name":"input_token_account","docs":["The payer's limit order token account"],"writable":true},{"name":"input_vault","docs":["The address that holds limit order token"],"writable":true},{"name":"input_vault_mint","docs":["The mint of token vault"]},{"name":"input_token_program","docs":["SPL-TOKEN / SPL-TOKEN2022 program for input token transfers"]},{"name":"system_program","docs":["SPL program for system operations"],"address":"11111111111111111111111111111111"}],"args":[{"name":"nonce_index","type":"u8"},{"name":"zero_for_one","type":"bool"},{"name":"tick_index","type":"i32"},{"name":"amount","type":"u64"}]},{"name":"open_position","docs":["#[deprecated(note = \"Use `open_position_with_token22_nft` instead.\")]","Creates a new position wrapped in a NFT","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which includes tick low","* `tick_array_upper_start_index` - The start index of tick array which includes tick upper","* `liquidity` - The liquidity to be added","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check",""],"discriminator":[135,128,47,77,15,152,240,49],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["Token account where position NFT will be minted","This account created in the contract by cpi to avoid large stack variables"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"position_nft_owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_nft_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","docs":["To store metaplex metadata"],"writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position"},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_program","docs":["Program to create NFT metadata"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"}]},{"name":"open_position_v2","docs":["#[deprecated(note = \"Use `open_position_with_token22_nft` instead.\")]","Creates a new position wrapped in a NFT, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which includes tick low","* `tick_array_upper_start_index` - The start index of tick array which includes tick upper","* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `with_metadata` - The flag indicating whether to create NFT mint metadata","* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[77,184,74,214,112,86,241,199],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["Token account where position NFT will be minted"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"position_nft_owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_nft_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","docs":["To store metaplex metadata"],"writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position"},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_program","docs":["Program to create NFT metadata"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"token_program_2022","docs":["Program to create mint account and mint tokens"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"with_metadata","type":"bool"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"open_position_with_token22_nft","docs":["Creates a new position wrapped in a Token2022 NFT without relying on metadata_program and metadata_account, reduce the cost for user to create a personal position.","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which includes tick low","* `tick_array_upper_start_index` - The start index of tick array which includes tick upper","* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `with_metadata` - The flag indicating whether to create NFT mint metadata","* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[77,255,174,82,125,29,201,46],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address, initialize in contract"],"writable":true,"signer":true},{"name":"position_nft_account","writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position"},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to transfer for token account"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program_2022","docs":["Program to create NFT mint/token account and transfer for token22 account"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"with_metadata","type":"bool"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"set_reward_params","docs":["Reset reward param, start a new reward cycle or extend the current cycle.","","# Arguments","","* `ctx` - The context of accounts","* `reward_index` - The index of reward token in the pool.","* `emissions_per_second_x64` - The per second emission reward, when extend the current cycle,","new value can't be less than old value","* `open_time` - reward open timestamp, must be set when starting a new cycle","* `end_time` - reward end timestamp",""],"discriminator":[112,52,167,75,32,201,211,137],"accounts":[{"name":"authority","docs":["Address to be set as protocol owner. It pays to create factory state account."],"signer":true},{"name":"amm_config"},{"name":"pool_state","writable":true},{"name":"operation_state","docs":["load info from the account to judge reward permission"],"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"token_program","docs":["Token program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"reward_index","type":"u8"},{"name":"emissions_per_second_x64","type":"u128"},{"name":"open_time","type":"u64"},{"name":"end_time","type":"u64"}]},{"name":"settle_limit_order","docs":["Settle a limit order","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[205,78,116,33,92,105,26,96],"accounts":[{"name":"signer","signer":true},{"name":"pool_state"},{"name":"tick_array"},{"name":"limit_order","writable":true},{"name":"output_token_account","docs":["The owner's output token account (opposite direction from input)"],"writable":true},{"name":"output_vault","docs":["The address that holds output tokens"],"writable":true},{"name":"output_vault_mint","docs":["The mint of output vault"]},{"name":"output_token_program","docs":["SPL-TOKEN / SPL-TOKEN2022 program for token transfers"]}],"args":[]},{"name":"swap","docs":["#[deprecated(note = \"Use `swap_v2` instead.\")]","Swaps one token for as much as possible of another token across a single pool","","# Arguments","","* `ctx` - The context of accounts","* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)","* `other_amount_threshold` - For slippage check","* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot","* `is_base_input` - swap base input or swap base output",""],"discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"tick_array","writable":true}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit_x64","type":"u128"},{"name":"is_base_input","type":"bool"}]},{"name":"swap_router_base_in","docs":["Swap token for as much as possible of another token across the path provided, base input","","# Arguments","","* `ctx` - The context of accounts","* `amount_in` - Token amount to be swapped in","* `amount_out_minimum` - Panic if output amount is below minimum amount. For slippage.",""],"discriminator":[69,125,115,218,245,186,242,196],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"input_token_account","docs":["The token account that pays input tokens for the swap"],"writable":true},{"name":"input_token_mint","docs":["The mint of input token"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["SPL program 2022 for token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["Memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"amount_in","type":"u64"},{"name":"amount_out_minimum","type":"u64"}]},{"name":"swap_v2","docs":["Swaps one token for as much as possible of another token across a single pool, support token program 2022","","# Arguments","","* `ctx` - The context of accounts","* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)","* `other_amount_threshold` - For slippage check","* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot","* `is_base_input` - swap base input or swap base output",""],"discriminator":[43,4,237,11,26,201,30,98],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["SPL program 2022 for token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["Memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"input_vault_mint","docs":["The mint of token vault 0"]},{"name":"output_vault_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit_x64","type":"u128"},{"name":"is_base_input","type":"bool"}]},{"name":"transfer_reward_owner","docs":["Transfer reward owner","","# Arguments","","* `ctx` - The context of accounts","* `new_owner` - new owner pubkey",""],"discriminator":[7,22,12,83,242,43,48,121],"accounts":[{"name":"authority","docs":["Address to be set as operation account owner."],"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"pool_state","writable":true}],"args":[{"name":"new_owner","type":"pubkey"}]},{"name":"update_amm_config","docs":["Updates the owner of the amm config","Must be called by the current owner or admin","","# Arguments","","* `ctx` - The context of accounts","* `trade_fee_rate` - The new trade fee rate of amm config, be set when `param` is 0","* `protocol_fee_rate` - The new protocol fee rate of amm config, be set when `param` is 1","* `fund_fee_rate` - The new fund fee rate of amm config, be set when `param` is 2","* `new_owner` - The config's new owner, be set when `param` is 3","* `new_fund_owner` - The config's new fund owner, be set when `param` is 4","* `limit_protocol_fee_rate` - The new limit protocol fee rate of amm config, be set when `param` is 5","* `limit_fund_fee_rate` - The new limit fund fee rate of amm config, be set when `param` is 6","* `param` - The value can be 0 | 1 | 2 | 3 | 4 | 5 | 6, otherwise will report an error",""],"discriminator":[49,60,174,136,154,28,116,200],"accounts":[{"name":"owner","docs":["The amm config owner or admin"],"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"amm_config","docs":["Amm config account to be changed"],"writable":true}],"args":[{"name":"param","type":"u8"},{"name":"value","type":"u32"}]},{"name":"update_dynamic_fee_config","docs":["Updates the dynamic fee configuration constants.","","This function allows updating the parameters that control dynamic fee calculation,","such as filter period, decay period, reduction factor, fee control factor, etc.","Only the admin or the amm config owner can update these settings.","","# Arguments","","* `ctx` - The context of accounts","* `filter_period` - Period that determines the high frequency trading time window (in seconds)","* `decay_period` - Period that determines when the dynamic fee starts to decrease (in seconds)","* `reduction_factor` - Dynamic fee rate decrement rate, used for volatility reference decay","* `dynamic_fee_control` - Factor used to scale the dynamic fee component","* `max_volatility_accumulator` - Maximum value for the volatility accumulator","* `create_pool_authority` - The new authority pubkey that can use this dynamic fee config to create pools"],"discriminator":[7,7,80,8,2,199,132,240],"accounts":[{"name":"owner","docs":["The admin has permission to update the config"],"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"dynamic_fee_config","docs":["The dynamic fee config account to be updated"],"writable":true}],"args":[{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"dynamic_fee_control","type":"u32"},{"name":"max_volatility_accumulator","type":"u32"}]},{"name":"update_operation_account","docs":["Update the operation account","","# Arguments","","* `ctx` - The context of accounts","* `param` - The value can be 0 | 1 | 2 | 3, otherwise will report an error","* `keys`- update operation owner when the `param` is 0","remove operation owner when the `param` is 1","update whitelist mint when the `param` is 2","remove whitelist mint when the `param` is 3",""],"discriminator":[127,70,119,40,188,227,61,7],"accounts":[{"name":"owner","docs":["Address to be set as operation account owner."],"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"operation_state","docs":["Initialize operation state account to store operation owner address and white list mint."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"param","type":"u8"},{"name":"keys","type":{"vec":"pubkey"}}]},{"name":"update_pool_status","docs":["Update pool status for given value","","# Arguments","","* `ctx` - The context of accounts","* `status` - The value of status",""],"discriminator":[130,87,108,6,46,224,117,123],"accounts":[{"name":"authority","signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"pool_state","writable":true}],"args":[{"name":"status","type":"u8"}]},{"name":"update_reward_infos","docs":["Update rewards info of the given pool, can be called for everyone","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[163,172,224,52,11,154,106,223],"accounts":[{"name":"pool_state","docs":["The liquidity pool for which reward info to update"],"writable":true}],"args":[]}],"accounts":[{"name":"AmmConfig","discriminator":[218,244,33,104,203,203,43,111]},{"name":"DynamicFeeConfig","discriminator":[244,226,88,82,14,59,246,220]},{"name":"LimitOrderNonce","discriminator":[17,153,236,57,25,199,231,248]},{"name":"LimitOrderState","discriminator":[1,238,5,142,207,62,44,228]},{"name":"ObservationState","discriminator":[122,174,197,53,129,9,165,132]},{"name":"OperationState","discriminator":[19,236,58,237,81,222,183,252]},{"name":"PersonalPositionState","discriminator":[70,111,150,126,230,15,25,117]},{"name":"PoolState","discriminator":[247,237,227,245,215,195,222,70]},{"name":"ProtocolPositionState","discriminator":[100,226,145,99,146,218,160,106]},{"name":"SupportMintAssociated","discriminator":[134,40,183,79,12,112,162,53]},{"name":"TickArrayBitmapExtension","discriminator":[60,150,36,219,97,128,139,153]},{"name":"TickArrayState","discriminator":[192,155,85,205,49,249,129,42]}],"events":[{"name":"CollectPersonalFeeEvent","discriminator":[166,174,105,192,81,161,83,105]},{"name":"CollectProtocolFeeEvent","discriminator":[206,87,17,79,45,41,213,61]},{"name":"ConfigChangeEvent","discriminator":[247,189,7,119,106,112,95,151]},{"name":"CreatePersonalPositionEvent","discriminator":[100,30,87,249,196,223,154,206]},{"name":"DecreaseLimitOrderEvent","discriminator":[70,48,40,221,219,237,212,163]},{"name":"DecreaseLiquidityEvent","discriminator":[58,222,86,58,68,50,85,56]},{"name":"IncreaseLimitOrderEvent","discriminator":[11,120,13,204,199,87,19,200]},{"name":"IncreaseLiquidityEvent","discriminator":[49,79,105,212,32,34,30,84]},{"name":"LiquidityCalculateEvent","discriminator":[237,112,148,230,57,84,180,162]},{"name":"LiquidityChangeEvent","discriminator":[126,240,175,206,158,88,153,107]},{"name":"OpenLimitOrderEvent","discriminator":[106,24,71,85,57,169,158,216]},{"name":"PoolCreatedEvent","discriminator":[25,94,75,47,112,99,53,63]},{"name":"SettleLimitOrderEvent","discriminator":[88,119,77,164,125,124,10,194]},{"name":"SwapEvent","discriminator":[64,198,205,232,38,8,113,226]},{"name":"UpdateRewardInfosEvent","discriminator":[109,127,186,78,114,65,37,236]}],"errors":[{"code":6000,"name":"NotApproved","msg":"Not approved"},{"code":6001,"name":"InvalidUpdateConfigFlag","msg":"invalid update amm config flag"},{"code":6002,"name":"AccountLack","msg":"Account lack"},{"code":6003,"name":"ClosePositionErr","msg":"Remove liquidity, collect fees owed and reward then you can close position account"},{"code":6004,"name":"InvalidTickIndex","msg":"Tick out of range"},{"code":6005,"name":"TickInvalidOrder","msg":"The lower tick must be below the upper tick"},{"code":6006,"name":"TickLowerOverflow","msg":"The tick must be greater, or equal to the minimum tick(-443636)"},{"code":6007,"name":"TickUpperOverflow","msg":"The tick must be lesser than, or equal to the maximum tick(443636)"},{"code":6008,"name":"TickAndSpacingNotMatch","msg":"tick % tick_spacing must be zero"},{"code":6009,"name":"InvalidTickArray","msg":"Invalid tick array account"},{"code":6010,"name":"InvalidTickArrayBoundary","msg":"Invalid tick array boundary"},{"code":6011,"name":"SqrtPriceLimitOverflow","msg":"Square root price limit overflow"},{"code":6012,"name":"SqrtPriceX64","msg":"sqrt_price_x64 out of range"},{"code":6013,"name":"LiquiditySubValueErr","msg":"Liquidity sub delta L must be smaller than before"},{"code":6014,"name":"LiquidityAddValueErr","msg":"Liquidity add delta L must be greater, or equal to before"},{"code":6015,"name":"ForbidBothZeroForSupplyLiquidity","msg":"Both token amount must not be zero while supply liquidity"},{"code":6016,"name":"LiquidityInsufficient","msg":"Liquidity insufficient"},{"code":6017,"name":"PriceSlippageCheck","msg":"Price slippage check"},{"code":6018,"name":"TooLittleOutputReceived","msg":"Too little output received"},{"code":6019,"name":"TooMuchInputPaid","msg":"Too much input paid"},{"code":6020,"name":"ZeroAmountSpecified","msg":"Swap special amount can not be zero"},{"code":6021,"name":"InvalidInputPoolVault","msg":"Input pool vault is invalid"},{"code":6022,"name":"TooSmallInputOrOutputAmount","msg":"Swap input or output amount is too small"},{"code":6023,"name":"NotEnoughTickArrayAccount","msg":"Not enough tick array account"},{"code":6024,"name":"InvalidFirstTickArrayAccount","msg":"Invalid first tick array account"},{"code":6025,"name":"InvalidRewardIndex","msg":"Invalid reward index"},{"code":6026,"name":"FullRewardInfo","msg":"The init reward token reach to the max"},{"code":6027,"name":"RewardTokenAlreadyInUse","msg":"The init reward token already in use"},{"code":6028,"name":"ExceptRewardMint","msg":"The reward tokens must contain one of pool vault mint except the last reward"},{"code":6029,"name":"InvalidRewardInitParam","msg":"Invalid reward init param"},{"code":6030,"name":"InvalidRewardInputAccountNumber","msg":"Invalid collect reward input account number"},{"code":6031,"name":"InvalidRewardPeriod","msg":"Invalid reward period"},{"code":6032,"name":"NotApproveUpdateRewardEmissions","msg":"Modification of emissions is allowed within 72 hours from the end of the previous cycle"},{"code":6033,"name":"UnInitializedRewardInfo","msg":"uninitialized reward info"},{"code":6034,"name":"NotSupportMint","msg":"Not support token_2022 mint extension"},{"code":6035,"name":"MissingTickArrayBitmapExtensionAccount","msg":"Missing tickarray bitmap extension account"},{"code":6036,"name":"InsufficientLiquidityForDirection","msg":"Insufficient liquidity for this direction"},{"code":6037,"name":"MaxTokenOverflow","msg":"Max token overflow"},{"code":6038,"name":"CalculateOverflow","msg":"Calculate overflow"},{"code":6039,"name":"TransferFeeCalculateNotMatch","msg":"TransferFee calculate not match"},{"code":6040,"name":"OrderAlreadyFilled","msg":"Order already fully filled, cannot modify"},{"code":6041,"name":"InvalidOrderPhase","msg":"Invalid order phase"},{"code":6042,"name":"InvalidLimitOrderAmount","msg":"Invalid limit order amount"},{"code":6043,"name":"OrderPhaseSaturated","msg":"Tick order phase saturated"},{"code":6044,"name":"InvalidDynamicFeeConfigParams","msg":"Invalid dynamic fee config params"},{"code":6045,"name":"InvalidFeeOn","msg":"Invalid fee on which token (must be 0, 1, or 2)"},{"code":6046,"name":"ZeroSqrtPrice","msg":"sqrt_price_x64 must be greater than 0"},{"code":6047,"name":"ZeroLiquidity","msg":"liquidity must be greater than 0"},{"code":6048,"name":"MissingBaseFlag","msg":"base_flag is required when liquidity is zero"},{"code":6049,"name":"MissingMintAccount","msg":"Mint account is required but not provided"},{"code":6050,"name":"MissingTokenProgram2022","msg":"Token-2022 program is required but not provided"}],"types":[{"name":"AmmConfig","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"index","type":"u16"},{"name":"owner","docs":["Address of the protocol owner"],"type":"pubkey"},{"name":"protocol_fee_rate","docs":["The protocol fee"],"type":"u32"},{"name":"trade_fee_rate","docs":["The trade fee, denominated in hundredths of a bip (10^-6)"],"type":"u32"},{"name":"tick_spacing","docs":["The tick spacing"],"type":"u16"},{"name":"fund_fee_rate","docs":["The fund fee, denominated in hundredths of a bip (10^-6)"],"type":"u32"},{"name":"padding_u32","type":"u32"},{"name":"fund_owner","docs":["The fund owner"],"type":"pubkey"},{"name":"padding","type":{"array":["u64",3]}}]}},{"name":"CollectFeeOn","type":{"kind":"enum","variants":[{"name":"FromInput"},{"name":"Token0Only"},{"name":"Token1Only"}]}},{"name":"CollectPersonalFeeEvent","docs":["Emitted when tokens are collected for a position"],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which underlying tokens were collected"],"type":"pubkey"},{"name":"recipient_token_account_0","docs":["The token account that received the collected token_0 tokens"],"type":"pubkey"},{"name":"recipient_token_account_1","docs":["The token account that received the collected token_1 tokens"],"type":"pubkey"},{"name":"amount_0","docs":["The amount of token_0 owed to the position that was collected"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_1 owed to the position that was collected"],"type":"u64"}]}},{"name":"CollectProtocolFeeEvent","docs":["Emitted when the collected protocol fees are withdrawn by the factory owner"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool whose protocol fee is collected"],"type":"pubkey"},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"type":"pubkey"},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"type":"pubkey"},{"name":"amount_0","docs":["The amount of token_0 protocol fees that is withdrawn"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_0 protocol fees that is withdrawn"],"type":"u64"}]}},{"name":"ConfigChangeEvent","docs":["Emitted when create or update a config"],"type":{"kind":"struct","fields":[{"name":"index","type":"u16"},{"name":"owner","type":"pubkey"},{"name":"protocol_fee_rate","type":"u32"},{"name":"trade_fee_rate","type":"u32"},{"name":"tick_spacing","type":"u16"},{"name":"fund_fee_rate","type":"u32"},{"name":"fund_owner","type":"pubkey"}]}},{"name":"CreateCustomizableParams","type":{"kind":"struct","fields":[{"name":"sqrt_price_x64","type":"u128"},{"name":"collect_fee_on","type":{"defined":{"name":"CollectFeeOn"}}},{"name":"enable_dynamic_fee","type":"bool"}]}},{"name":"CreatePersonalPositionEvent","docs":["Emitted when create a new position"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for which liquidity was added"],"type":"pubkey"},{"name":"minter","docs":["The address that create the position"],"type":"pubkey"},{"name":"nft_owner","docs":["The owner of the position and recipient of any minted liquidity"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity minted to the position range"],"type":"u128"},{"name":"deposit_amount_0","docs":["The amount of token_0 was deposit for the liquidity"],"type":"u64"},{"name":"deposit_amount_1","docs":["The amount of token_1 was deposit for the liquidity"],"type":"u64"},{"name":"deposit_amount_0_transfer_fee","docs":["The token transfer fee for deposit_amount_0"],"type":"u64"},{"name":"deposit_amount_1_transfer_fee","docs":["The token transfer fee for deposit_amount_1"],"type":"u64"}]}},{"name":"DecreaseLimitOrderEvent","type":{"kind":"struct","fields":[{"name":"pool_id","docs":["The pool whose limit order was decreased"],"type":"pubkey"},{"name":"limit_order","docs":["The limit order account"],"type":"pubkey"},{"name":"zero_for_one","docs":["Direction of the limit order (true if zero_for_one)"],"type":"bool"},{"name":"tick_index","docs":["Tick index of the limit order"],"type":"i32"},{"name":"total_amount","docs":["Total amount of the limit order"],"type":"u64"},{"name":"filled_amount","docs":["Filled amount of the limit order"],"type":"u64"},{"name":"settled_output_amount","docs":["Amount of output tokens settled (excluding fees)"],"type":"u64"},{"name":"decreased_amount","docs":["Amount of input tokens returned to user"],"type":"u64"}]}},{"name":"DecreaseLiquidityEvent","docs":["Emitted when liquidity is decreased."],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which liquidity was decreased"],"type":"pubkey"},{"name":"liquidity","docs":["The amount by which liquidity for the position was decreased"],"type":"u128"},{"name":"decrease_amount_0","docs":["The amount of token_0 that was paid for the decrease in liquidity"],"type":"u64"},{"name":"decrease_amount_1","docs":["The amount of token_1 that was paid for the decrease in liquidity"],"type":"u64"},{"name":"fee_amount_0","type":"u64"},{"name":"fee_amount_1","docs":["The amount of token_1 fee"],"type":"u64"},{"name":"reward_amounts","docs":["The amount of rewards"],"type":{"array":["u64",3]}},{"name":"transfer_fee_0","docs":["The amount of token_0 transfer fee"],"type":"u64"},{"name":"transfer_fee_1","docs":["The amount of token_1 transfer fee"],"type":"u64"}]}},{"name":"DynamicFeeConfig","type":{"kind":"struct","fields":[{"name":"index","type":"u16"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"dynamic_fee_control","type":"u32"},{"name":"max_volatility_accumulator","type":"u32"},{"name":"padding","type":{"array":["u64",8]}}]}},{"name":"DynamicFeeInfo","docs":["Dynamic fee information for pool configuration"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"filter_period","docs":["Period that determines the high frequency trading time window (in seconds)."],"type":"u16"},{"name":"decay_period","docs":["Period that determines when the dynamic fee starts to decrease (in seconds)."],"type":"u16"},{"name":"reduction_factor","docs":["Dynamic fee rate decrement rate, used for volatility reference decay."],"type":"u16"},{"name":"dynamic_fee_control","docs":["Factor used to scale the dynamic fee component in the fee rate calculation."],"type":"u32"},{"name":"max_volatility_accumulator","docs":["Maximum value for the volatility accumulator, used to cap the dynamic fee rate."],"type":"u32"},{"name":"tick_spacing_index_reference","docs":["Active tick spacing index at the last reference update."],"type":"i32"},{"name":"volatility_reference","docs":["Volatility reference value, stores the decayed volatility accumulator."],"type":"u32"},{"name":"volatility_accumulator","docs":["Volatility accumulator, used to calculate the dynamic fee rate."],"type":"u32"},{"name":"last_update_timestamp","docs":["Last timestamp (block time) when `volatility_reference` and `tick_spacing_index_reference` were updated."],"type":"u64"},{"name":"padding","docs":["Reserved for future upgrades."],"type":{"array":["u8",46]}}]}},{"name":"IncreaseLimitOrderEvent","type":{"kind":"struct","fields":[{"name":"pool_id","docs":["The pool whose limit order was increased"],"type":"pubkey"},{"name":"limit_order","docs":["The limit order account"],"type":"pubkey"},{"name":"zero_for_one","docs":["Direction of the limit order (true if zero_for_one)"],"type":"bool"},{"name":"tick_index","docs":["Tick index of the limit order"],"type":"i32"},{"name":"total_amount","docs":["Total amount of the limit order"],"type":"u64"},{"name":"increased_amount","docs":["Increased amount of the limit order"],"type":"u64"},{"name":"transfer_fee","docs":["Transfer fee of the limit order"],"type":"u64"}]}},{"name":"IncreaseLiquidityEvent","docs":["Emitted when liquidity is increased."],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which liquidity was increased"],"type":"pubkey"},{"name":"liquidity","docs":["The amount by which liquidity for the NFT position was increased"],"type":"u128"},{"name":"amount_0","docs":["The amount of token_0 that was paid for the increase in liquidity"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_1 that was paid for the increase in liquidity"],"type":"u64"},{"name":"amount_0_transfer_fee","docs":["The token transfer fee for amount_0"],"type":"u64"},{"name":"amount_1_transfer_fee","docs":["The token transfer fee for amount_1"],"type":"u64"}]}},{"name":"InitializeRewardParam","type":{"kind":"struct","fields":[{"name":"open_time","docs":["Reward open time"],"type":"u64"},{"name":"end_time","docs":["Reward end time"],"type":"u64"},{"name":"emissions_per_second_x64","docs":["Token reward per second are earned per unit of liquidity"],"type":"u128"}]}},{"name":"LimitOrderNonce","type":{"kind":"struct","fields":[{"name":"user_wallet","type":"pubkey"},{"name":"nonce_index","type":"u8"},{"name":"order_nonce","type":"u64"},{"name":"padding","type":{"array":["u64",4]}}]}},{"name":"LimitOrderState","type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"owner","docs":["Owner of this limit order"],"type":"pubkey"},{"name":"tick_index","type":"i32"},{"name":"zero_for_one","type":"bool"},{"name":"order_phase","docs":["Order phase of this limit order, aligned with `TickState.order_phase` for FIFO semantics"],"type":"u64"},{"name":"total_amount","docs":["Total amount of the limit order"],"type":"u64"},{"name":"filled_amount","docs":["Filled amount of the limit order (informational, for events/display)"],"type":"u64"},{"name":"settle_base","docs":["The unfilled amount when the current computation segment started.","Set to total_amount on open, reset on decrease. Never updated during settle —","this avoids cascading floor-division error across multiple settles."],"type":"u64"},{"name":"settled_output","docs":["Cumulative output paid to user in the current segment. Reset on decrease.","Each settle computes total_output from the segment base and pays the diff","(total_output - settled_output), limiting dust deduction to once per segment."],"type":"u64"},{"name":"open_time","docs":["Order open time"],"type":"u64"},{"name":"unfilled_ratio_x64","docs":["Snapshot of `TickState.unfilled_ratio_x64` at the time this segment started (Q64.64).","Initialized to Q64 on open; reset to tick's current ratio on decrease."],"type":"u128"},{"name":"padding","type":{"array":["u64",4]}}]}},{"name":"LiquidityCalculateEvent","docs":["Emitted when liquidity decreased or increase."],"type":{"kind":"struct","fields":[{"name":"pool_liquidity","docs":["The pool liquidity before decrease or increase"],"type":"u128"},{"name":"pool_sqrt_price_x64","docs":["The pool price when decrease or increase in liquidity"],"type":"u128"},{"name":"pool_tick","docs":["The pool tick when decrease or increase in liquidity"],"type":"i32"},{"name":"calc_amount_0","docs":["The amount of token_0 that was calculated for the decrease or increase in liquidity"],"type":"u64"},{"name":"calc_amount_1","docs":["The amount of token_1 that was calculated for the decrease or increase in liquidity"],"type":"u64"},{"name":"trade_fee_owed_0","type":"u64"},{"name":"trade_fee_owed_1","docs":["The amount of token_1 fee"],"type":"u64"},{"name":"transfer_fee_0","docs":["The amount of token_0 transfer fee without trade_fee_amount_0"],"type":"u64"},{"name":"transfer_fee_1","docs":["The amount of token_1 transfer fee without trade_fee_amount_0"],"type":"u64"}]}},{"name":"LiquidityChangeEvent","docs":["Emitted pool liquidity change when increase and decrease liquidity"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for swap"],"type":"pubkey"},{"name":"tick","docs":["The tick of the pool"],"type":"i32"},{"name":"tick_lower","docs":["The tick lower of position"],"type":"i32"},{"name":"tick_upper","docs":["The tick lower of position"],"type":"i32"},{"name":"liquidity_before","docs":["The liquidity of the pool before liquidity change"],"type":"u128"},{"name":"liquidity_after","docs":["The liquidity of the pool after liquidity change"],"type":"u128"}]}},{"name":"Observation","docs":["The element of observations in ObservationState"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"block_timestamp","docs":["The block timestamp of the observation"],"type":"u32"},{"name":"tick_cumulative","docs":["the cumulative of tick during the duration time"],"type":"i64"},{"name":"padding","docs":["padding for feature update"],"type":{"array":["u64",4]}}]}},{"name":"ObservationState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"initialized","docs":["Whether the ObservationState is initialized"],"type":"bool"},{"name":"recent_epoch","docs":["recent update epoch"],"type":"u64"},{"name":"observation_index","docs":["the most-recently updated index of the observations array"],"type":"u16"},{"name":"pool_id","docs":["belongs to which pool"],"type":"pubkey"},{"name":"observations","docs":["observation array"],"type":{"array":[{"defined":{"name":"Observation"}},100]}},{"name":"padding","docs":["padding for feature update"],"type":{"array":["u64",4]}}]}},{"name":"OpenLimitOrderEvent","docs":["Emitted when a limit order is opened"],"type":{"kind":"struct","fields":[{"name":"pool_id","docs":["The pool whose limit order was opened"],"type":"pubkey"},{"name":"limit_order","docs":["The limit order account"],"type":"pubkey"},{"name":"zero_for_one","docs":["Direction of the limit order (true if zero_for_one)"],"type":"bool"},{"name":"tick_index","docs":["Tick index of the limit order"],"type":"i32"},{"name":"total_amount","docs":["Total amount of the limit order"],"type":"u64"},{"name":"transfer_fee","docs":["Transfer fee of the limit order"],"type":"u64"}]}},{"name":"OperationState","docs":["Holds the current owner of the factory"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"operation_owners","docs":["Address of the operation owner"],"type":{"array":["pubkey",10]}},{"name":"whitelist_mints","docs":["The mint address of whitelist to emit reward"],"type":{"array":["pubkey",100]}}]}},{"name":"PersonalPositionState","type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":{"array":["u8",1]}},{"name":"nft_mint","docs":["Mint address of the tokenized position"],"type":"pubkey"},{"name":"pool_id","docs":["The ID of the pool with which this token is connected"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"fee_growth_inside_0_last_x64","docs":["The token_0 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"fee_growth_inside_1_last_x64","docs":["The token_1 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"token_fees_owed_0","docs":["The fees owed to the position owner in token_0, as of the last computation"],"type":"u64"},{"name":"token_fees_owed_1","docs":["The fees owed to the position owner in token_1, as of the last computation"],"type":"u64"},{"name":"reward_infos","type":{"array":[{"defined":{"name":"PositionRewardInfo"}},3]}},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u64",7]}}]}},{"name":"PoolCreatedEvent","docs":["Emitted when a pool is created and initialized with a starting price",""],"type":{"kind":"struct","fields":[{"name":"token_mint_0","docs":["The first token of the pool by address sort order"],"type":"pubkey"},{"name":"token_mint_1","docs":["The second token of the pool by address sort order"],"type":"pubkey"},{"name":"tick_spacing","docs":["The minimum number of ticks between initialized ticks"],"type":"u16"},{"name":"pool_state","docs":["The address of the created pool"],"type":"pubkey"},{"name":"sqrt_price_x64","docs":["The initial sqrt price of the pool, as a Q64.64"],"type":"u128"},{"name":"tick","docs":["The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool"],"type":"i32"},{"name":"token_vault_0","docs":["Vault of token_0"],"type":"pubkey"},{"name":"token_vault_1","docs":["Vault of token_1"],"type":"pubkey"}]}},{"name":"PoolState","docs":["The pool state","","PDA of `[POOL_SEED, config, token_mint_0, token_mint_1]`",""],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":{"array":["u8",1]}},{"name":"amm_config","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"token_mint_0","docs":["Token pair of the pool, where token_mint_0 address < token_mint_1 address"],"type":"pubkey"},{"name":"token_mint_1","type":"pubkey"},{"name":"token_vault_0","docs":["Token pair vault"],"type":"pubkey"},{"name":"token_vault_1","type":"pubkey"},{"name":"observation_key","docs":["observation account key"],"type":"pubkey"},{"name":"mint_decimals_0","docs":["mint0 and mint1 decimals"],"type":"u8"},{"name":"mint_decimals_1","type":"u8"},{"name":"tick_spacing","docs":["The minimum number of ticks between initialized ticks"],"type":"u16"},{"name":"liquidity","docs":["The currently in range liquidity available to the pool."],"type":"u128"},{"name":"sqrt_price_x64","docs":["The current price of the pool as a sqrt(token_1/token_0) Q64.64 value"],"type":"u128"},{"name":"tick_current","docs":["The current tick of the pool, i.e. according to the last tick transition that was run."],"type":"i32"},{"name":"padding3","type":"u16"},{"name":"padding4","type":"u16"},{"name":"fee_growth_global_0_x64","docs":["The fee growth as a Q64.64 number, i.e. fees of token_0 and token_1 collected per","unit of liquidity for the entire life of the pool."],"type":"u128"},{"name":"fee_growth_global_1_x64","type":"u128"},{"name":"protocol_fees_token_0","docs":["The amounts of token_0 and token_1 that are owed to the protocol."],"type":"u64"},{"name":"protocol_fees_token_1","type":"u64"},{"name":"padding5","type":{"array":["u128",4]}},{"name":"status","docs":["Bitwise representation of the state of the pool","bit0, 1: disable open position and increase liquidity, 0: normal","bit1, 1: disable decrease liquidity, 0: normal","bit2, 1: disable collect fee, 0: normal","bit3, 1: disable collect reward, 0: normal","bit4, 1: disable swap, 0: normal","bit5, 1: disable limit order, 0: normal"],"type":"u8"},{"name":"fee_on","docs":["Fee on which token (0 = FromInput, 1 = Token0Only, 2 = Token1Only)"],"type":"u8"},{"name":"padding","docs":["Leave blank for future use"],"type":{"array":["u8",6]}},{"name":"reward_infos","type":{"array":[{"defined":{"name":"RewardInfo"}},3]}},{"name":"tick_array_bitmap","docs":["Packed initialized tick array state"],"type":{"array":["u64",16]}},{"name":"padding6","type":{"array":["u64",4]}},{"name":"fund_fees_token_0","type":"u64"},{"name":"fund_fees_token_1","type":"u64"},{"name":"open_time","type":"u64"},{"name":"recent_epoch","type":"u64"},{"name":"dynamic_fee_info","docs":["Dynamic fee info"],"type":{"defined":{"name":"DynamicFeeInfo"}}},{"name":"padding1","type":{"array":["u64",14]}},{"name":"padding2","type":{"array":["u64",32]}}]}},{"name":"PositionRewardInfo","type":{"kind":"struct","fields":[{"name":"growth_inside_last_x64","type":"u128"},{"name":"reward_amount_owed","type":"u64"}]}},{"name":"ProtocolPositionState","docs":["Info stored for each user's position"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"pool_id","docs":["The ID of the pool with which this token is connected"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"fee_growth_inside_0_last_x64","docs":["The token_0 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"fee_growth_inside_1_last_x64","docs":["The token_1 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"token_fees_owed_0","docs":["The fees owed to the position owner in token_0"],"type":"u64"},{"name":"token_fees_owed_1","docs":["The fees owed to the position owner in token_1"],"type":"u64"},{"name":"reward_growth_inside","docs":["The reward growth per unit of liquidity as of the last update to liquidity"],"type":{"array":["u128",3]}},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u64",7]}}]}},{"name":"RewardInfo","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"reward_state","docs":["Reward state"],"type":"u8"},{"name":"open_time","docs":["Reward open time"],"type":"u64"},{"name":"end_time","docs":["Reward end time"],"type":"u64"},{"name":"last_update_time","docs":["Reward last update time"],"type":"u64"},{"name":"emissions_per_second_x64","docs":["Q64.64 number indicates how many tokens per second are earned per unit of liquidity."],"type":"u128"},{"name":"reward_total_emitted","docs":["The total amount of reward emitted"],"type":"u64"},{"name":"reward_claimed","docs":["The total amount of claimed reward"],"type":"u64"},{"name":"token_mint","docs":["Reward token mint."],"type":"pubkey"},{"name":"token_vault","docs":["Reward vault token account."],"type":"pubkey"},{"name":"authority","docs":["The owner that has permission to set reward param"],"type":"pubkey"},{"name":"reward_growth_global_x64","docs":["Q64.64 number that tracks the total tokens earned per unit of liquidity since the reward","emissions were turned on."],"type":"u128"}]}},{"name":"SettleLimitOrderEvent","docs":["Emitted when a limit order is settled and proceeds are transferred"],"type":{"kind":"struct","fields":[{"name":"pool_id","docs":["The pool whose limit order was settled"],"type":"pubkey"},{"name":"limit_order","docs":["The limit order account"],"type":"pubkey"},{"name":"zero_for_one","docs":["Direction of the limit order (true if zero_for_one)"],"type":"bool"},{"name":"tick_index","docs":["Tick index of the limit order"],"type":"i32"},{"name":"total_amount","docs":["Total amount of the limit order"],"type":"u64"},{"name":"filled_amount","docs":["Filled amount of the limit order"],"type":"u64"},{"name":"settled_amount_out","docs":["Amount of output tokens transferred (excluding fees)"],"type":"u64"}]}},{"name":"SupportMintAssociated","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"mint","docs":["Address of the supported token22 mint"],"type":"pubkey"},{"name":"padding","type":{"array":["u64",8]}}]}},{"name":"SwapEvent","docs":["Emitted by when a swap is performed for a pool"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for which token_0 and token_1 were swapped"],"type":"pubkey"},{"name":"sender","docs":["The address that initiated the swap call, and that received the callback"],"type":"pubkey"},{"name":"token_account_0","docs":["The payer token account in zero for one swaps, or the recipient token account","in one for zero swaps"],"type":"pubkey"},{"name":"token_account_1","docs":["The payer token account in one for zero swaps, or the recipient token account","in zero for one swaps"],"type":"pubkey"},{"name":"amount_0","docs":["The real delta amount of the token_0 of the pool or user"],"type":"u64"},{"name":"transfer_fee_0","docs":["The transfer fee charged by the withheld_amount of the token_0"],"type":"u64"},{"name":"amount_1","docs":["The real delta of the token_1 of the pool or user"],"type":"u64"},{"name":"transfer_fee_1","docs":["The transfer fee charged by the withheld_amount of the token_1"],"type":"u64"},{"name":"zero_for_one","docs":["if true, amount_0 is negtive and amount_1 is positive"],"type":"bool"},{"name":"sqrt_price_x64","docs":["The sqrt(price) of the pool after the swap, as a Q64.64"],"type":"u128"},{"name":"liquidity","docs":["The liquidity of the pool after the swap"],"type":"u128"},{"name":"tick","docs":["The log base 1.0001 of price of the pool after the swap"],"type":"i32"},{"name":"trade_fee_0","docs":["Total AMM trade fee (lp + protocol + fund) charged in token_0 during this swap"],"type":"u64"},{"name":"trade_fee_1","docs":["Total AMM trade fee (lp + protocol + fund) charged in token_1 during this swap"],"type":"u64"}]}},{"name":"TickArrayBitmapExtension","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"positive_tick_array_bitmap","docs":["Packed initialized tick array state for start_tick_index is positive"],"type":{"array":[{"array":["u64",8]},14]}},{"name":"negative_tick_array_bitmap","docs":["Packed initialized tick array state for start_tick_index is negitive"],"type":{"array":[{"array":["u64",8]},14]}}]}},{"name":"TickArrayState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"start_tick_index","type":"i32"},{"name":"ticks","type":{"array":[{"defined":{"name":"TickState"}},60]}},{"name":"initialized_tick_count","type":"u8"},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u8",107]}}]}},{"name":"TickState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"tick","type":"i32"},{"name":"liquidity_net","docs":["Amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left)"],"type":"i128"},{"name":"liquidity_gross","docs":["The total position liquidity that references this tick"],"type":"u128"},{"name":"fee_growth_outside_0_x64","docs":["Fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)","only has relative meaning, not absolute — the value depends on when the tick is initialized"],"type":"u128"},{"name":"fee_growth_outside_1_x64","type":"u128"},{"name":"reward_growths_outside_x64","docs":["Reward growth per unit of liquidity like fee, array of Q64.64"],"type":{"array":["u128",3]}},{"name":"order_phase","docs":["Order phase of the tick, used as a FIFO cohort index for limit orders"],"type":"u64"},{"name":"orders_amount","docs":["The amount of limit orders that have never been matched,","only counts newly opened orders, not partially filled ones"],"type":"u64"},{"name":"part_filled_orders_remaining","docs":["Remaining part filled orders amount"],"type":"u64"},{"name":"unfilled_ratio_x64","docs":["Cumulative unfilled ratio for the current part-filled cohort (Q64.64 format).","Starts at Q64(1) when a new cohort forms, multiplied down as fills occur."],"type":"u128"},{"name":"padding","type":{"array":["u32",3]}}]}},{"name":"UpdateRewardInfosEvent","docs":["Emitted when Reward are updated for a pool"],"type":{"kind":"struct","fields":[{"name":"reward_growth_global_x64","docs":["Reward info"],"type":{"array":["u128",3]}}]}}]} \ No newline at end of file diff --git a/idls/raydium_cpmm.CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C.json b/idls/raydium_cpmm.CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C.json new file mode 100644 index 0000000..8499ebe --- /dev/null +++ b/idls/raydium_cpmm.CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C.json @@ -0,0 +1 @@ +{"address":"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C","metadata":{"name":"raydium_cp_swap","version":"0.2.0","spec":"0.1.0","description":"Raydium constant product AMM, supports Token2022 and without Openbook"},"instructions":[{"name":"close_permission_pda","docs":["Close a permission account","","# Arguments","","* `ctx`- The context of accounts",""],"discriminator":[156,84,32,118,69,135,70,123],"accounts":[{"name":"owner","writable":true,"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"permission_authority"},{"name":"permission","docs":["Initialize config state account to store protocol owner address and fee rates."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,101,114,109,105,115,115,105,111,110]},{"kind":"account","path":"permission_authority"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"collect_creator_fee","docs":["Collect the creator fee","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[20,22,86,123,198,28,219,132],"accounts":[{"name":"creator","docs":["Only pool creator can collect fee"],"writable":true,"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores fund_owner"]},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"creator_token_0","docs":["The address that receives the collected token_0 fund fees"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"creator"},{"kind":"account","path":"token_0_program"},{"kind":"account","path":"vault_0_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"creator_token_1","docs":["The address that receives the collected token_1 fund fees"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"creator"},{"kind":"account","path":"token_1_program"},{"kind":"account","path":"vault_1_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_0_program","docs":["Spl token program or token program 2022"]},{"name":"token_1_program","docs":["Spl token program or token program 2022"]},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"collect_fund_fee","docs":["Collect the fund fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[167,138,78,149,223,194,6,126],"accounts":[{"name":"owner","docs":["Only admin or fund_owner can collect fee now"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores fund_owner"]},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"recipient_token_0_account","docs":["The address that receives the collected token_0 fund fees"],"writable":true},{"name":"recipient_token_1_account","docs":["The address that receives the collected token_1 fund fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_protocol_fee","docs":["Collect the protocol fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[136,136,252,221,194,66,126,89],"accounts":[{"name":"owner","docs":["Only admin or owner can collect fee now"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores owner"]},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"recipient_token_0_account","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_1_account","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"create_amm_config","docs":["# Arguments","","* `ctx`- The accounts needed by instruction.","* `index` - The index of amm config, there may be multiple config.","* `trade_fee_rate` - Trade fee rate, can be changed.","* `protocol_fee_rate` - The rate of protocol fee within trade fee.","* `fund_fee_rate` - The rate of fund fee within trade fee.",""],"discriminator":[137,52,237,212,215,117,108,104],"accounts":[{"name":"owner","docs":["Address to be set as protocol owner."],"writable":true,"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"amm_config","docs":["Initialize config state account to store protocol owner address and fee rates."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,109,109,95,99,111,110,102,105,103]},{"kind":"arg","path":"index"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"u16"},{"name":"trade_fee_rate","type":"u64"},{"name":"protocol_fee_rate","type":"u64"},{"name":"fund_fee_rate","type":"u64"},{"name":"create_pool_fee","type":"u64"},{"name":"creator_fee_rate","type":"u64"}]},{"name":"create_permission_pda","docs":["Create a permission account","","# Arguments","","* `ctx`- The context of accounts",""],"discriminator":[135,136,2,216,137,169,181,202],"accounts":[{"name":"owner","writable":true,"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"permission_authority"},{"name":"permission","docs":["Initialize config state account to store protocol owner address and fee rates."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,101,114,109,105,115,115,105,111,110]},{"kind":"account","path":"permission_authority"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"deposit","docs":["Deposit lp token to the pool","","# Arguments","","* `ctx`- The context of accounts","* `lp_token_amount` - Increased number of LPs","* `maximum_token_0_amount` - Maximum token 0 amount to deposit, prevents excessive slippage","* `maximum_token_1_amount` - Maximum token 1 amount to deposit, prevents excessive slippage",""],"discriminator":[242,35,198,137,82,225,242,182],"accounts":[{"name":"owner","docs":["Pays to mint the position"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","writable":true},{"name":"owner_lp_token","docs":["Owner lp token account"],"writable":true},{"name":"token_0_account","docs":["The payer's token account for token_0"],"writable":true},{"name":"token_1_account","docs":["The payer's token account for token_1"],"writable":true},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["token Program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"lp_mint","docs":["Lp token mint"],"writable":true}],"args":[{"name":"lp_token_amount","type":"u64"},{"name":"maximum_token_0_amount","type":"u64"},{"name":"maximum_token_1_amount","type":"u64"}]},{"name":"initialize","docs":["Creates a pool for the given token pair and the initial price","","# Arguments","","* `ctx`- The context of accounts","* `init_amount_0` - the initial amount_0 to deposit","* `init_amount_1` - the initial amount_1 to deposit","* `open_time` - the timestamp allowed for swap",""],"discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"creator","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"authority","docs":["pool vault and lp mint authority"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["PDA account:","seeds = [","POOL_SEED.as_bytes(),","amm_config.key().as_ref(),","token_0_mint.key().as_ref(),","token_1_mint.key().as_ref(),","],","","Or random account: must be signed by cli"],"writable":true},{"name":"token_0_mint","docs":["Token_0 mint, the key must smaller than token_1 mint."]},{"name":"token_1_mint","docs":["Token_1 mint, the key must grater then token_0 mint."]},{"name":"lp_mint","docs":["pool lp mint"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,108,112,95,109,105,110,116]},{"kind":"account","path":"pool_state"}]}},{"name":"creator_token_0","docs":["payer token0 account"],"writable":true},{"name":"creator_token_1","docs":["creator token1 account"],"writable":true},{"name":"creator_lp_token","docs":["creator lp token account"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"creator"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_0_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_0_mint"}]}},{"name":"token_1_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_1_mint"}]}},{"name":"create_pool_fee","docs":["create pool fee account"],"writable":true,"address":"DNXgeM9EiiaAbaWvwjHj9fQQLAX5ZsfHyvmYUNRAdNC8"},{"name":"observation_state","docs":["an account to store oracle observations"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_0_program","docs":["Spl token program or token program 2022"]},{"name":"token_1_program","docs":["Spl token program or token program 2022"]},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"init_amount_0","type":"u64"},{"name":"init_amount_1","type":"u64"},{"name":"open_time","type":"u64"}]},{"name":"initialize_with_permission","docs":["Create a pool with permission","","# Arguments","","* `ctx`- The context of accounts","* `init_amount_0` - the initial amount_0 to deposit","* `init_amount_1` - the initial amount_1 to deposit","* `open_time` - the timestamp allowed for swap","* `creator_fee_on` - creator fee model, 0:both token0 and token1 (depends on the input), 1: only token0, 2: only token1",""],"discriminator":[63,55,254,65,49,178,89,121],"accounts":[{"name":"payer","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"creator"},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"authority","docs":["pool vault and lp mint authority"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["PDA account:","seeds = [","POOL_SEED.as_bytes(),","amm_config.key().as_ref(),","token_0_mint.key().as_ref(),","token_1_mint.key().as_ref(),","],","","Or random account: must be signed by cli"],"writable":true},{"name":"token_0_mint","docs":["Token_0 mint, the key must smaller than token_1 mint."]},{"name":"token_1_mint","docs":["Token_1 mint, the key must grater then token_0 mint."]},{"name":"lp_mint","docs":["pool lp mint"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,108,112,95,109,105,110,116]},{"kind":"account","path":"pool_state"}]}},{"name":"payer_token_0","docs":["payer token0 account"],"writable":true},{"name":"payer_token_1","docs":["payer token1 account"],"writable":true},{"name":"payer_lp_token","docs":["payer lp token account"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"payer"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_0_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_0_mint"}]}},{"name":"token_1_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_1_mint"}]}},{"name":"create_pool_fee","docs":["create pool fee account"],"writable":true,"address":"DNXgeM9EiiaAbaWvwjHj9fQQLAX5ZsfHyvmYUNRAdNC8"},{"name":"observation_state","docs":["an account to store oracle observations"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"permission","pda":{"seeds":[{"kind":"const","value":[112,101,114,109,105,115,115,105,111,110]},{"kind":"account","path":"payer"}]}},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_0_program","docs":["Spl token program or token program 2022"]},{"name":"token_1_program","docs":["Spl token program or token program 2022"]},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"}],"args":[{"name":"init_amount_0","type":"u64"},{"name":"init_amount_1","type":"u64"},{"name":"open_time","type":"u64"},{"name":"creator_fee_on","type":{"defined":{"name":"CreatorFeeOn"}}}]},{"name":"swap_base_input","docs":["Swap the tokens in the pool base input amount","","# Arguments","","* `ctx`- The context of accounts","* `amount_in` - input amount to transfer, output to DESTINATION is based on the exchange rate","* `minimum_amount_out` - Minimum amount of output token, prevents excessive slippage",""],"discriminator":[143,190,90,218,196,30,51,222],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"input_token_program","docs":["SPL program for input token transfers"]},{"name":"output_token_program","docs":["SPL program for output token transfers"]},{"name":"input_token_mint","docs":["The mint of input token"]},{"name":"output_token_mint","docs":["The mint of output token"]},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true}],"args":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"}]},{"name":"swap_base_output","docs":["Swap the tokens in the pool base output amount","","# Arguments","","* `ctx`- The context of accounts","* `max_amount_in` - input amount prevents excessive slippage","* `amount_out` - amount of output token",""],"discriminator":[55,217,98,86,163,74,180,173],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"input_token_program","docs":["SPL program for input token transfers"]},{"name":"output_token_program","docs":["SPL program for output token transfers"]},{"name":"input_token_mint","docs":["The mint of input token"]},{"name":"output_token_mint","docs":["The mint of output token"]},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true}],"args":[{"name":"max_amount_in","type":"u64"},{"name":"amount_out","type":"u64"}]},{"name":"update_amm_config","docs":["Updates the owner of the amm config","Must be called by the current owner or admin","","# Arguments","","* `ctx`- The context of accounts","* `trade_fee_rate`- The new trade fee rate of amm config, be set when `param` is 0","* `protocol_fee_rate`- The new protocol fee rate of amm config, be set when `param` is 1","* `fund_fee_rate`- The new fund fee rate of amm config, be set when `param` is 2","* `new_owner`- The config's new owner, be set when `param` is 3","* `new_fund_owner`- The config's new fund owner, be set when `param` is 4","* `param`- The value can be 0 | 1 | 2 | 3 | 4, otherwise will report a error",""],"discriminator":[49,60,174,136,154,28,116,200],"accounts":[{"name":"owner","docs":["The amm config owner or admin"],"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"amm_config","docs":["Amm config account to be changed"],"writable":true}],"args":[{"name":"param","type":"u8"},{"name":"value","type":"u64"}]},{"name":"update_pool_status","docs":["Update pool status for given value","","# Arguments","","* `ctx`- The context of accounts","* `status` - The value of status",""],"discriminator":[130,87,108,6,46,224,117,123],"accounts":[{"name":"authority","signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"pool_state","writable":true}],"args":[{"name":"status","type":"u8"}]},{"name":"withdraw","docs":["Withdraw lp for token0 and token1","","# Arguments","","* `ctx`- The context of accounts","* `lp_token_amount` - Amount of pool tokens to burn. User receives an output of token a and b based on the percentage of the pool tokens that are returned.","* `minimum_token_0_amount` - Minimum amount of token 0 to receive, prevents excessive slippage","* `minimum_token_1_amount` - Minimum amount of token 1 to receive, prevents excessive slippage",""],"discriminator":[183,18,70,156,148,109,161,34],"accounts":[{"name":"owner","docs":["Pays to mint the position"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state account"],"writable":true},{"name":"owner_lp_token","docs":["Owner lp token account"],"writable":true},{"name":"token_0_account","docs":["The token account for receive token_0,"],"writable":true},{"name":"token_1_account","docs":["The token account for receive token_1"],"writable":true},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["token Program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"lp_mint","docs":["Pool lp token mint"],"writable":true},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"lp_token_amount","type":"u64"},{"name":"minimum_token_0_amount","type":"u64"},{"name":"minimum_token_1_amount","type":"u64"}]}],"accounts":[{"name":"AmmConfig","discriminator":[218,244,33,104,203,203,43,111]},{"name":"ObservationState","discriminator":[122,174,197,53,129,9,165,132]},{"name":"Permission","discriminator":[224,83,28,79,10,253,161,28]},{"name":"PoolState","discriminator":[247,237,227,245,215,195,222,70]}],"events":[{"name":"LpChangeEvent","discriminator":[121,163,205,201,57,218,117,60]},{"name":"SwapEvent","discriminator":[64,198,205,232,38,8,113,226]}],"errors":[{"code":6000,"name":"NotApproved","msg":"Not approved"},{"code":6001,"name":"InvalidOwner","msg":"Input account owner is not the program address"},{"code":6002,"name":"EmptySupply","msg":"Input token account empty"},{"code":6003,"name":"InvalidInput","msg":"InvalidInput"},{"code":6004,"name":"IncorrectLpMint","msg":"Address of the provided lp token mint is incorrect"},{"code":6005,"name":"ExceededSlippage","msg":"Exceeds desired slippage limit"},{"code":6006,"name":"ZeroTradingTokens","msg":"Given pool token amount results in zero trading tokens"},{"code":6007,"name":"NotSupportMint","msg":"Not support token_2022 mint extension"},{"code":6008,"name":"InvalidVault","msg":"invaild vault"},{"code":6009,"name":"InitLpAmountTooLess","msg":"Init lp amount is too less(Because 100 amount lp will be locked)"},{"code":6010,"name":"TransferFeeCalculateNotMatch","msg":"TransferFee calculate not match"},{"code":6011,"name":"MathOverflow","msg":"Math overflow"},{"code":6012,"name":"InsufficientVault","msg":"Insufficient vault"},{"code":6013,"name":"InvalidFeeModel","msg":"Invalid fee model"},{"code":6014,"name":"NoFeeCollect","msg":"Fee is zero"}],"types":[{"name":"AmmConfig","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"disable_create_pool","docs":["Status to control if new pool can be create"],"type":"bool"},{"name":"index","docs":["Config index"],"type":"u16"},{"name":"trade_fee_rate","docs":["The trade fee, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"protocol_fee_rate","docs":["The protocol fee"],"type":"u64"},{"name":"fund_fee_rate","docs":["The fund fee, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"create_pool_fee","docs":["Fee for create a new pool"],"type":"u64"},{"name":"protocol_owner","docs":["Address of the protocol fee owner"],"type":"pubkey"},{"name":"fund_owner","docs":["Address of the fund fee owner"],"type":"pubkey"},{"name":"creator_fee_rate","docs":["The pool creator fee, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"padding","docs":["padding"],"type":{"array":["u64",15]}}]}},{"name":"CreatorFeeOn","type":{"kind":"enum","variants":[{"name":"BothToken"},{"name":"OnlyToken0"},{"name":"OnlyToken1"}]}},{"name":"LpChangeEvent","docs":["Emitted when deposit and withdraw"],"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"lp_amount_before","type":"u64"},{"name":"token_0_vault_before","docs":["pool vault sub trade fees"],"type":"u64"},{"name":"token_1_vault_before","docs":["pool vault sub trade fees"],"type":"u64"},{"name":"token_0_amount","docs":["calculate result without transfer fee"],"type":"u64"},{"name":"token_1_amount","docs":["calculate result without transfer fee"],"type":"u64"},{"name":"token_0_transfer_fee","type":"u64"},{"name":"token_1_transfer_fee","type":"u64"},{"name":"change_type","type":"u8"}]}},{"name":"Observation","docs":["The element of observations in ObservationState"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"block_timestamp","docs":["The block timestamp of the observation"],"type":"u64"},{"name":"cumulative_token_0_price_x32","docs":["the cumulative of token0 price during the duration time, Q32.32, the remaining 64 bit for overflow"],"type":"u128"},{"name":"cumulative_token_1_price_x32","docs":["the cumulative of token1 price during the duration time, Q32.32, the remaining 64 bit for overflow"],"type":"u128"}]}},{"name":"ObservationState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"initialized","docs":["Whether the ObservationState is initialized"],"type":"bool"},{"name":"observation_index","docs":["the most-recently updated index of the observations array"],"type":"u16"},{"name":"pool_id","type":"pubkey"},{"name":"observations","docs":["observation array"],"type":{"array":[{"defined":{"name":"Observation"}},100]}},{"name":"padding","docs":["padding for feature update"],"type":{"array":["u64",4]}}]}},{"name":"Permission","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"authority","docs":["authority"],"type":"pubkey"},{"name":"padding","docs":["padding"],"type":{"array":["u64",30]}}]}},{"name":"PoolState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"amm_config","docs":["Which config the pool belongs"],"type":"pubkey"},{"name":"pool_creator","docs":["pool creator"],"type":"pubkey"},{"name":"token_0_vault","docs":["Token A"],"type":"pubkey"},{"name":"token_1_vault","docs":["Token B"],"type":"pubkey"},{"name":"lp_mint","docs":["Pool tokens are issued when A or B tokens are deposited.","Pool tokens can be withdrawn back to the original A or B token."],"type":"pubkey"},{"name":"token_0_mint","docs":["Mint information for token A"],"type":"pubkey"},{"name":"token_1_mint","docs":["Mint information for token B"],"type":"pubkey"},{"name":"token_0_program","docs":["token_0 program"],"type":"pubkey"},{"name":"token_1_program","docs":["token_1 program"],"type":"pubkey"},{"name":"observation_key","docs":["observation account to store oracle data"],"type":"pubkey"},{"name":"auth_bump","type":"u8"},{"name":"status","docs":["Bitwise representation of the state of the pool","bit0, 1: disable deposit(value is 1), 0: normal","bit1, 1: disable withdraw(value is 2), 0: normal","bit2, 1: disable swap(value is 4), 0: normal"],"type":"u8"},{"name":"lp_mint_decimals","type":"u8"},{"name":"mint_0_decimals","docs":["mint0 and mint1 decimals"],"type":"u8"},{"name":"mint_1_decimals","type":"u8"},{"name":"lp_supply","docs":["True circulating supply without burns and lock ups"],"type":"u64"},{"name":"protocol_fees_token_0","docs":["The amounts of token_0 and token_1 that are owed to the liquidity provider."],"type":"u64"},{"name":"protocol_fees_token_1","type":"u64"},{"name":"fund_fees_token_0","type":"u64"},{"name":"fund_fees_token_1","type":"u64"},{"name":"open_time","docs":["The timestamp allowed for swap in the pool."],"type":"u64"},{"name":"recent_epoch","docs":["recent epoch"],"type":"u64"},{"name":"creator_fee_on","docs":["Creator fee collect mode","0: both token_0 and token_1 can be used as trade fees. It depends on what the input token is when swapping","1: only token_0 as trade fee","2: only token_1 as trade fee"],"type":"u8"},{"name":"enable_creator_fee","type":"bool"},{"name":"padding1","type":{"array":["u8",6]}},{"name":"creator_fees_token_0","type":"u64"},{"name":"creator_fees_token_1","type":"u64"},{"name":"padding","docs":["padding for future updates"],"type":{"array":["u64",28]}}]}},{"name":"SwapEvent","docs":["Emitted when swap"],"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"input_vault_before","docs":["pool vault sub trade fees"],"type":"u64"},{"name":"output_vault_before","docs":["pool vault sub trade fees"],"type":"u64"},{"name":"input_amount","docs":["calculate result without transfer fee"],"type":"u64"},{"name":"output_amount","docs":["calculate result without transfer fee"],"type":"u64"},{"name":"input_transfer_fee","type":"u64"},{"name":"output_transfer_fee","type":"u64"},{"name":"base_input","type":"bool"},{"name":"input_mint","type":"pubkey"},{"name":"output_mint","type":"pubkey"},{"name":"trade_fee","type":"u64"},{"name":"creator_fee","docs":["Amount of fee tokens going to creator"],"type":"u64"},{"name":"creator_fee_on_input","type":"bool"}]}}]} \ No newline at end of file diff --git a/idls/raydium_launchlab.LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj.json b/idls/raydium_launchlab.LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj.json new file mode 100644 index 0000000..52f4970 --- /dev/null +++ b/idls/raydium_launchlab.LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj.json @@ -0,0 +1 @@ +{"address":"LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj","metadata":{"name":"raydium_launchpad","version":"0.2.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"buy_exact_in","docs":["Use the given amount of quote tokens to purchase base tokens.","# Arguments","","* `ctx` - The context of accounts","* `amount_in` - Amount of quote token to purchase","* `minimum_amount_out` - Minimum amount of base token to receive (slippage protection)","* `share_fee_rate` - Fee rate for the share",""],"discriminator":[250,234,13,123,213,156,19,236],"accounts":[{"name":"payer","docs":["The user performing the swap operation","Must sign the transaction and pay for fees"],"writable":true,"signer":true},{"name":"authority","docs":["PDA that acts as the authority for pool vault operations","Generated using AUTH_SEED"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"global_config","docs":["Global configuration account containing protocol-wide settings","Used to read protocol fee rates and curve type"]},{"name":"platform_config","docs":["Platform configuration account containing platform-wide settings","Used to read platform fee rate"]},{"name":"pool_state","docs":["The pool state account where the swap will be performed","Contains current pool parameters and balances"],"writable":true},{"name":"user_base_token","docs":["The user's token account for base tokens (tokens being bought)","Will receive the output tokens after the swap"],"writable":true},{"name":"user_quote_token","docs":["The user's token account for quote tokens (tokens being sold)","Will be debited for the input amount"],"writable":true},{"name":"base_vault","docs":["The pool's vault for base tokens","Will be debited to send tokens to the user"],"writable":true},{"name":"quote_vault","docs":["The pool's vault for quote tokens","Will receive the input tokens from the user"],"writable":true},{"name":"base_token_mint","docs":["The mint of the base token","Used for transfer fee calculations if applicable"]},{"name":"quote_token_mint","docs":["The mint of the quote token"]},{"name":"base_token_program","docs":["SPL Token program for base token transfers"]},{"name":"quote_token_program","docs":["SPL Token program for quote token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"},{"name":"share_fee_rate","type":"u64"}]},{"name":"buy_exact_out","docs":["Use quote tokens to purchase the given amount of base tokens.","# Arguments","","* `ctx` - The context of accounts","* `amount_out` - Amount of base token to receive","* `maximum_amount_in` - Maximum amount of quote token to purchase (slippage protection)","* `share_fee_rate` - Fee rate for the share"],"discriminator":[24,211,116,40,105,3,153,56],"accounts":[{"name":"payer","docs":["The user performing the swap operation","Must sign the transaction and pay for fees"],"writable":true,"signer":true},{"name":"authority","docs":["PDA that acts as the authority for pool vault operations","Generated using AUTH_SEED"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"global_config","docs":["Global configuration account containing protocol-wide settings","Used to read protocol fee rates and curve type"]},{"name":"platform_config","docs":["Platform configuration account containing platform-wide settings","Used to read platform fee rate"]},{"name":"pool_state","docs":["The pool state account where the swap will be performed","Contains current pool parameters and balances"],"writable":true},{"name":"user_base_token","docs":["The user's token account for base tokens (tokens being bought)","Will receive the output tokens after the swap"],"writable":true},{"name":"user_quote_token","docs":["The user's token account for quote tokens (tokens being sold)","Will be debited for the input amount"],"writable":true},{"name":"base_vault","docs":["The pool's vault for base tokens","Will be debited to send tokens to the user"],"writable":true},{"name":"quote_vault","docs":["The pool's vault for quote tokens","Will receive the input tokens from the user"],"writable":true},{"name":"base_token_mint","docs":["The mint of the base token","Used for transfer fee calculations if applicable"]},{"name":"quote_token_mint","docs":["The mint of the quote token"]},{"name":"base_token_program","docs":["SPL Token program for base token transfers"]},{"name":"quote_token_program","docs":["SPL Token program for quote token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_out","type":"u64"},{"name":"maximum_amount_in","type":"u64"},{"name":"share_fee_rate","type":"u64"}]},{"name":"claim_creator_fee","docs":["Claim the fee from the exclusive creator fee vault.","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[26,97,138,203,132,171,141,252],"accounts":[{"name":"creator","docs":["The pool creator"],"writable":true,"signer":true},{"name":"fee_vault_authority","pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,102,101,101,95,118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"creator_fee_vault","docs":["The creator fee vault"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"creator"},{"kind":"account","path":"quote_mint"}]}},{"name":"recipient_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"creator"},{"kind":"account","path":"token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"quote_mint","docs":["The mint for the quote token"]},{"name":"token_program","docs":["SPL Token program for the quote token"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["Required for associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[]},{"name":"claim_platform_fee","docs":["Claim platform fee","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[156,39,208,135,76,237,61,72],"accounts":[{"name":"platform_fee_wallet","docs":["Only the wallet stored in platform_config can collect platform fees"],"writable":true,"signer":true},{"name":"authority","docs":["PDA that acts as the authority for pool vault and mint operations","Generated using AUTH_SEED"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Account that stores the pool's state and parameters","PDA generated using POOL_SEED and both token mints"],"writable":true},{"name":"platform_config","docs":["The platform config account"]},{"name":"quote_vault","writable":true},{"name":"recipient_token_account","docs":["The address that receives the collected quote token fees"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"platform_fee_wallet"},{"kind":"account","path":"token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"quote_mint","docs":["The mint of quote token vault"]},{"name":"token_program","docs":["SPL program for input token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["Required for associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[]},{"name":"claim_platform_fee_from_vault","docs":["Claim the fee from the exclusive platform fee vault.","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[117,241,198,168,248,218,80,29],"accounts":[{"name":"platform_fee_wallet","docs":["Only the wallet stored in platform_config can collect platform fees"],"writable":true,"signer":true},{"name":"fee_vault_authority","pda":{"seeds":[{"kind":"const","value":[112,108,97,116,102,111,114,109,95,102,101,101,95,118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"platform_config","docs":["The platform config account"]},{"name":"platform_fee_vault","docs":["The platform fee vault"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"platform_config"},{"kind":"account","path":"quote_mint"}]}},{"name":"recipient_token_account","docs":["The address that receives the collected quote token fees"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"platform_fee_wallet"},{"kind":"account","path":"token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"quote_mint","docs":["The mint of quote token vault"]},{"name":"token_program","docs":["SPL program for input token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["Required for associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[]},{"name":"claim_vested_token","docs":["Claim vested token","# Arguments"],"discriminator":[49,33,104,30,189,157,79,35],"accounts":[{"name":"beneficiary","docs":["The beneficiary of the vesting account"],"writable":true,"signer":true},{"name":"authority","docs":["PDA that acts as the authority for pool vault and mint operations","Generated using AUTH_SEED"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Account that stores the pool's state and parameters","PDA generated using POOL_SEED and both token mints"],"writable":true},{"name":"vesting_record","docs":["The vesting record account"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,101,115,116,105,110,103]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"beneficiary"}]}},{"name":"base_vault","docs":["The pool's vault for base tokens","Will be debited to send tokens to the user"],"writable":true},{"name":"user_base_token","writable":true,"pda":{"seeds":[{"kind":"account","path":"beneficiary"},{"kind":"account","path":"base_token_program"},{"kind":"account","path":"base_token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"base_token_mint","docs":["The mint for the base token (token being sold)","Created in this instruction with specified decimals"]},{"name":"base_token_program","docs":["SPL Token program for the base token","Must be the standard Token program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["Required for associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[]},{"name":"close_platform_global_access","docs":["Close a platform's permission to use a global config."],"discriminator":[123,180,184,129,111,185,187,59],"accounts":[{"name":"owner","signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"global_config"},{"name":"platform_config"},{"name":"platform_global_access","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,108,97,116,102,111,114,109,95,103,108,111,98,97,108,95,97,99,99,101,115,115]},{"kind":"account","path":"platform_config"},{"kind":"account","path":"global_config"}]}}],"args":[]},{"name":"collect_fee","docs":["Collects accumulated fees from the pool","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[60,173,247,103,4,93,130,48],"accounts":[{"name":"owner","docs":["Only protocol_fee_owner saved in global_config can collect protocol fee now"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"global_config","docs":["Global config account stores owner"]},{"name":"quote_vault","docs":["The address that holds pool tokens for quote token"],"writable":true},{"name":"quote_mint","docs":["The mint of quote token vault"]},{"name":"recipient_token_account","docs":["The address that receives the collected quote token fees"],"writable":true},{"name":"token_program","docs":["SPL program for input token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[]},{"name":"collect_migrate_fee","docs":["Collects migrate fees from the pool","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[255,186,150,223,235,118,201,186],"accounts":[{"name":"owner","docs":["Only migrate_fee_owner saved in global_config can collect migrate fee now"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"global_config","docs":["Global config account stores owner"]},{"name":"quote_vault","docs":["The address that holds pool tokens for quote token"],"writable":true},{"name":"quote_mint","docs":["The mint of quote token vault"]},{"name":"recipient_token_account","docs":["The address that receives the collected quote token fees"],"writable":true},{"name":"token_program","docs":["SPL program for input token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[]},{"name":"create_config","docs":["Creates a new configuration","# Arguments","","* `ctx` - The accounts needed by instruction","* `curve_type` - The type of bonding curve (0: ConstantProduct)","* `index` - The index of config, there may be multiple config with the same curve type.","* `trade_fee_rate` - Trade fee rate, must be less than RATE_DENOMINATOR_VALUE",""],"discriminator":[201,207,243,114,75,111,47,189],"accounts":[{"name":"owner","docs":["The protocol owner/admin account","Must match the predefined admin address","Has authority to create and modify protocol configurations"],"writable":true,"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"global_config","docs":["Global configuration account that stores protocol-wide settings","PDA generated using GLOBAL_CONFIG_SEED, quote token mint, and curve type","Stores fee rates and protocol parameters"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,99,111,110,102,105,103]},{"kind":"account","path":"quote_token_mint"},{"kind":"arg","path":"curve_type"},{"kind":"arg","path":"index"}]}},{"name":"quote_token_mint","docs":["The mint address of the quote token (token used for buying)","This will be the standard token used for all pools with this config"]},{"name":"protocol_fee_owner","docs":["Account that will receive protocol fees"]},{"name":"migrate_fee_owner","docs":["Account that will receive migrate fees"]},{"name":"migrate_to_amm_wallet","docs":["The control wallet address for migrating to amm"]},{"name":"migrate_to_cpswap_wallet","docs":["The control wallet address for migrating to cpswap"]},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"}],"args":[{"name":"curve_type","type":"u8"},{"name":"index","type":"u16"},{"name":"migrate_fee","type":"u64"},{"name":"trade_fee_rate","type":"u64"}]},{"name":"create_platform_config","docs":["Create platform config account","# Arguments","","* `ctx` - The context of accounts","# Fields","* `fee_rate` - Fee rate of the platform","* `name` - Name of the platform","* `web` - Website of the platform","* `img` - Image link of the platform",""],"discriminator":[176,90,196,175,253,113,220,20],"accounts":[{"name":"platform_admin","docs":["The account paying for the initialization costs"],"writable":true,"signer":true},{"name":"platform_fee_wallet"},{"name":"platform_nft_wallet"},{"name":"platform_config","docs":["The platform config account"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,108,97,116,102,111,114,109,95,99,111,110,102,105,103]},{"kind":"account","path":"platform_admin"}]}},{"name":"cpswap_config"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"transfer_fee_extension_authority"},{"name":"platform_vesting_wallet"}],"args":[{"name":"platform_params","type":{"defined":{"name":"PlatformParams"}}}]},{"name":"create_platform_global_access","docs":["Create a platform permission to use a global config when authorization is enabled."],"discriminator":[162,91,146,199,93,133,234,237],"accounts":[{"name":"owner","writable":true,"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"global_config"},{"name":"platform_config"},{"name":"platform_global_access","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,108,97,116,102,111,114,109,95,103,108,111,98,97,108,95,97,99,99,101,115,115]},{"kind":"account","path":"platform_config"},{"kind":"account","path":"global_config"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_platform_vesting_account","docs":["Create vesting account","# Arguments","","* `ctx` - The context of accounts","* `share` - The share amount of base token to be vested",""],"discriminator":[146,71,173,69,98,19,15,106],"accounts":[{"name":"platform_vesting_wallet","docs":["The account paying for the initialization costs","This can be any account with sufficient SOL to cover the transaction"],"writable":true,"signer":true},{"name":"beneficiary","docs":["The beneficiary is used to receive the allocated linear release of tokens.","Once this account is set, it cannot be modified, so please ensure the validity of this account,","otherwise, the unlocked tokens will not be claimable."],"writable":true},{"name":"platform_config","docs":["Platform config account to be changed"]},{"name":"pool_state","docs":["The pool state account"],"writable":true},{"name":"platform_vesting_record","docs":["The vesting record account"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,101,115,116,105,110,103]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"beneficiary"}]}},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_vesting_account","docs":["Create vesting account","# Arguments","","* `ctx` - The context of accounts","* `share` - The share amount of base token to be vested",""],"discriminator":[129,178,2,13,217,172,230,218],"accounts":[{"name":"creator","docs":["The account paying for the initialization costs","This can be any account with sufficient SOL to cover the transaction"],"writable":true,"signer":true},{"name":"beneficiary","docs":["The beneficiary is used to receive the allocated linear release of tokens.","Once this account is set, it cannot be modified, so please ensure the validity of this account,","otherwise, the unlocked tokens will not be claimable."],"writable":true},{"name":"pool_state","docs":["The pool state account"],"writable":true},{"name":"vesting_record","docs":["The vesting record account"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,101,115,116,105,110,103]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"beneficiary"}]}},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"}],"args":[{"name":"share_amount","type":"u64"}]},{"name":"initialize","docs":["Initializes a new trading pool","# Arguments","","* `ctx` - The context of accounts containing pool and token information",""],"discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"payer","docs":["The account paying for the initialization costs","This can be any account with sufficient SOL to cover the transaction"],"writable":true,"signer":true},{"name":"creator"},{"name":"global_config","docs":["Global configuration account containing protocol-wide settings","Includes settings like quote token mint and fee parameters"]},{"name":"platform_config","docs":["Platform configuration account containing platform info","Includes settings like the fee_rate, name, web, img of the platform"]},{"name":"authority","docs":["PDA that acts as the authority for pool vault and mint operations","Generated using AUTH_SEED"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Account that stores the pool's state and parameters","PDA generated using POOL_SEED and both token mints"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"base_mint"},{"kind":"account","path":"quote_mint"}]}},{"name":"base_mint","docs":["The mint for the base token (token being sold)","Created in this instruction with specified decimals"],"writable":true,"signer":true},{"name":"quote_mint","docs":["The mint for the quote token (token used to buy)","Must match the quote_mint specified in global config"]},{"name":"base_vault","docs":["Token account that holds the pool's base tokens","PDA generated using POOL_VAULT_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"base_mint"}]}},{"name":"quote_vault","docs":["Token account that holds the pool's quote tokens","PDA generated using POOL_VAULT_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"quote_mint"}]}},{"name":"metadata_account","docs":["Account to store the base token's metadata","Created using Metaplex metadata program"],"writable":true},{"name":"base_token_program","docs":["SPL Token program for the base token","Must be the standard Token program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"quote_token_program","docs":["SPL Token program for the quote token"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"metadata_program","docs":["Metaplex Token Metadata program","Used to create metadata for the base token"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"rent_program","docs":["Required for rent exempt calculations"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"base_mint_param","type":{"defined":{"name":"MintParams"}}},{"name":"curve_param","type":{"defined":{"name":"CurveParams"}}},{"name":"vesting_param","type":{"defined":{"name":"VestingParams"}}}]},{"name":"initialize_v2","docs":["Initializes a new trading pool","# Arguments","","* `ctx` - The context of accounts containing pool and token information",""],"discriminator":[67,153,175,39,218,16,38,32],"accounts":[{"name":"payer","docs":["The account paying for the initialization costs","This can be any account with sufficient SOL to cover the transaction"],"writable":true,"signer":true},{"name":"creator"},{"name":"global_config","docs":["Global configuration account containing protocol-wide settings","Includes settings like quote token mint and fee parameters"]},{"name":"platform_config","docs":["Platform configuration account containing platform info","Includes settings like the fee_rate, name, web, img of the platform"]},{"name":"authority","docs":["PDA that acts as the authority for pool vault and mint operations","Generated using AUTH_SEED"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Account that stores the pool's state and parameters","PDA generated using POOL_SEED and both token mints"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"base_mint"},{"kind":"account","path":"quote_mint"}]}},{"name":"base_mint","docs":["The mint for the base token (token being sold)","Created in this instruction with specified decimals"],"writable":true,"signer":true},{"name":"quote_mint","docs":["The mint for the quote token (token used to buy)","Must match the quote_mint specified in global config"]},{"name":"base_vault","docs":["Token account that holds the pool's base tokens","PDA generated using POOL_VAULT_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"base_mint"}]}},{"name":"quote_vault","docs":["Token account that holds the pool's quote tokens","PDA generated using POOL_VAULT_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"quote_mint"}]}},{"name":"metadata_account","docs":["Account to store the base token's metadata","Created using Metaplex metadata program"],"writable":true},{"name":"base_token_program","docs":["SPL Token program for the base token","Must be the standard Token program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"quote_token_program","docs":["SPL Token program for the quote token"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"metadata_program","docs":["Metaplex Token Metadata program","Used to create metadata for the base token"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"rent_program","docs":["Required for rent exempt calculations"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"base_mint_param","type":{"defined":{"name":"MintParams"}}},{"name":"curve_param","type":{"defined":{"name":"CurveParams"}}},{"name":"vesting_param","type":{"defined":{"name":"VestingParams"}}},{"name":"amm_fee_on","type":{"defined":{"name":"AmmCreatorFeeOn"}}}]},{"name":"initialize_with_token_2022","docs":["Initializes a new trading pool with base token belongs to spl-token-2022,","pool created by this instruction must be migrated to cpswap after fundraising ends, i.e., curve_param.migrate_type = 1","# Arguments","","* `ctx` - The context of accounts containing pool and token information",""],"discriminator":[37,190,126,222,44,154,171,17],"accounts":[{"name":"payer","docs":["The account paying for the initialization costs","This can be any account with sufficient SOL to cover the transaction"],"writable":true,"signer":true},{"name":"creator"},{"name":"global_config","docs":["Global configuration account containing protocol-wide settings","Includes settings like quote token mint and fee parameters"]},{"name":"platform_config","docs":["Platform configuration account containing platform info","Includes settings like the fee_rate, name, web, img of the platform"]},{"name":"authority","docs":["PDA that acts as the authority for pool vault and mint operations","Generated using AUTH_SEED"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Account that stores the pool's state and parameters","PDA generated using POOL_SEED and both token mints"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"base_mint"},{"kind":"account","path":"quote_mint"}]}},{"name":"base_mint","docs":["The mint for the base token (token being sold)","Created in this instruction with specified decimals"],"writable":true,"signer":true},{"name":"quote_mint","docs":["The mint for the quote token (token used to buy)","Must match the quote_mint specified in global config"]},{"name":"base_vault","docs":["Token account that holds the pool's base tokens","PDA generated using POOL_VAULT_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"base_mint"}]}},{"name":"quote_vault","docs":["Token account that holds the pool's quote tokens","PDA generated using POOL_VAULT_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"quote_mint"}]}},{"name":"base_token_program","docs":["SPL Token program for the base token"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program","docs":["SPL Token program for the quote token"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"base_mint_param","type":{"defined":{"name":"MintParams"}}},{"name":"curve_param","type":{"defined":{"name":"CurveParams"}}},{"name":"vesting_param","type":{"defined":{"name":"VestingParams"}}},{"name":"amm_fee_on","type":{"defined":{"name":"AmmCreatorFeeOn"}}},{"name":"transfer_fee_extension_param","type":{"option":{"defined":{"name":"TransferFeeExtensionParams"}}}}]},{"name":"migrate_to_amm","docs":["# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[207,82,192,145,254,207,145,223],"accounts":[{"name":"payer","docs":["Only migrate_to_amm_wallet can migrate to cpswap pool","This signer must match the migrate_to_amm_wallet saved in global_config"],"writable":true,"signer":true},{"name":"base_mint","docs":["The mint for the base token (token being sold)"]},{"name":"quote_mint","docs":["The mint for the quote token (token used to buy)"]},{"name":"openbook_program","address":"srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX"},{"name":"market","docs":["Account created and asigned to openbook_program but not been initialized"],"writable":true},{"name":"request_queue","docs":["Account created and asigned to openbook_program but not been initialized"],"writable":true},{"name":"event_queue","docs":["Account created and asigned to openbook_program but not been initialized"],"writable":true},{"name":"bids","docs":["Account created and asigned to openbook_program but not been initialized"],"writable":true},{"name":"asks","docs":["Account created and asigned to openbook_program but not been initialized"],"writable":true},{"name":"market_vault_signer"},{"name":"market_base_vault","docs":["Token account that holds the market's base tokens"],"writable":true},{"name":"market_quote_vault","docs":["Token account that holds the market's quote tokens"],"writable":true},{"name":"amm_program","address":"675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"},{"name":"amm_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"amm_program"},{"kind":"account","path":"market"},{"kind":"const","value":[97,109,109,95,97,115,115,111,99,105,97,116,101,100,95,115,101,101,100]}],"program":{"kind":"account","path":"amm_program"}}},{"name":"amm_authority","pda":{"seeds":[{"kind":"const","value":[97,109,109,32,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"account","path":"amm_program"}}},{"name":"amm_open_orders","writable":true,"pda":{"seeds":[{"kind":"account","path":"amm_program"},{"kind":"account","path":"market"},{"kind":"const","value":[111,112,101,110,95,111,114,100,101,114,95,97,115,115,111,99,105,97,116,101,100,95,115,101,101,100]}],"program":{"kind":"account","path":"amm_program"}}},{"name":"amm_lp_mint","writable":true,"pda":{"seeds":[{"kind":"account","path":"amm_program"},{"kind":"account","path":"market"},{"kind":"const","value":[108,112,95,109,105,110,116,95,97,115,115,111,99,105,97,116,101,100,95,115,101,101,100]}],"program":{"kind":"account","path":"amm_program"}}},{"name":"amm_base_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"amm_program"},{"kind":"account","path":"market"},{"kind":"const","value":[99,111,105,110,95,118,97,117,108,116,95,97,115,115,111,99,105,97,116,101,100,95,115,101,101,100]}],"program":{"kind":"account","path":"amm_program"}}},{"name":"amm_quote_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"amm_program"},{"kind":"account","path":"market"},{"kind":"const","value":[112,99,95,118,97,117,108,116,95,97,115,115,111,99,105,97,116,101,100,95,115,101,101,100]}],"program":{"kind":"account","path":"amm_program"}}},{"name":"amm_target_orders","writable":true,"pda":{"seeds":[{"kind":"account","path":"amm_program"},{"kind":"account","path":"market"},{"kind":"const","value":[116,97,114,103,101,116,95,97,115,115,111,99,105,97,116,101,100,95,115,101,101,100]}],"program":{"kind":"account","path":"amm_program"}}},{"name":"amm_config","pda":{"seeds":[{"kind":"const","value":[97,109,109,95,99,111,110,102,105,103,95,97,99,99,111,117,110,116,95,115,101,101,100]}],"program":{"kind":"account","path":"amm_program"}}},{"name":"amm_create_fee_destination","writable":true},{"name":"authority","docs":["PDA that acts as the authority for pool vault operations","Generated using AUTH_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Account that stores the pool's state and parameters","PDA generated using POOL_SEED and both token mints"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"base_mint"},{"kind":"account","path":"quote_mint"}]}},{"name":"global_config","docs":["Global config account stores owner"]},{"name":"base_vault","docs":["The pool's vault for base tokens","Will be fully drained during migration"],"writable":true},{"name":"quote_vault","docs":["The pool's vault for quote tokens","Will be fully drained during migration"],"writable":true},{"name":"pool_lp_token","writable":true},{"name":"spl_token_program","docs":["SPL Token program for the base token","Must be the standard Token program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving fee NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"rent_program","docs":["Required for rent exempt calculations"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"base_lot_size","type":"u64"},{"name":"quote_lot_size","type":"u64"},{"name":"market_vault_signer_nonce","type":"u8"}]},{"name":"migrate_to_cpswap","docs":["# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[136,92,200,103,28,218,144,140],"accounts":[{"name":"payer","docs":["Only migrate_to_cpswap_wallet can migrate to cpswap pool","This signer must match the migrate_to_cpswap_wallet saved in global_config"],"writable":true,"signer":true},{"name":"base_mint","docs":["The mint for the base token (token being sold)"],"writable":true},{"name":"quote_mint","docs":["The mint for the quote token (token used to buy)"]},{"name":"platform_config","docs":["Platform configuration account containing platform-wide settings","Used to read platform fee rate"]},{"name":"cpswap_program","address":"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"},{"name":"cpswap_pool","docs":["PDA account:","seeds = [","b\"pool\",","cpswap_config.key().as_ref(),","token_0_mint.key().as_ref(),","token_1_mint.key().as_ref(),","],","seeds::program = cpswap_program,","","Or random account: must be signed by cli"],"writable":true},{"name":"cpswap_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}],"program":{"kind":"account","path":"cpswap_program"}}},{"name":"cpswap_lp_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,108,112,95,109,105,110,116]},{"kind":"account","path":"cpswap_pool"}],"program":{"kind":"account","path":"cpswap_program"}}},{"name":"cpswap_base_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"cpswap_pool"},{"kind":"account","path":"base_mint"}],"program":{"kind":"account","path":"cpswap_program"}}},{"name":"cpswap_quote_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"cpswap_pool"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"cpswap_program"}}},{"name":"cpswap_config"},{"name":"cpswap_create_pool_fee","writable":true},{"name":"cpswap_observation","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"cpswap_pool"}],"program":{"kind":"account","path":"cpswap_program"}}},{"name":"lock_program","address":"LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE"},{"name":"lock_authority","pda":{"seeds":[{"kind":"const","value":[108,111,99,107,95,99,112,95,97,117,116,104,111,114,105,116,121,95,115,101,101,100]}],"program":{"kind":"account","path":"lock_program"}}},{"name":"lock_lp_vault","writable":true},{"name":"authority","docs":["PDA that acts as the authority for pool vault operations","Generated using AUTH_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Account that stores the pool's state and parameters","PDA generated using POOL_SEED and both token mints"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"base_mint"},{"kind":"account","path":"quote_mint"}]}},{"name":"global_config","docs":["Global config account stores owner"]},{"name":"base_vault","docs":["The pool's vault for base tokens","Will be fully drained during migration"],"writable":true},{"name":"quote_vault","docs":["The pool's vault for quote tokens","Will be fully drained during migration"],"writable":true},{"name":"pool_lp_token","writable":true},{"name":"base_token_program","docs":["SPL Token program for the base token","Must be the standard Token program"]},{"name":"quote_token_program","docs":["SPL Token program for the quote token"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving fee NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"rent_program","docs":["Required for rent exempt calculations"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"metadata_program","docs":["Program to create NFT metadata accunt"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"}],"args":[]},{"name":"remove_platform_curve_param","docs":["Remove platform launch param","# Arguments","","* `ctx` - The context of accounts","* `index` - The index of the curve param to remove",""],"discriminator":[27,30,62,169,93,224,24,145],"accounts":[{"name":"platform_admin","docs":["The account paying for the initialization costs"],"signer":true},{"name":"platform_config","docs":["Platform config account to be changed"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,108,97,116,102,111,114,109,95,99,111,110,102,105,103]},{"kind":"account","path":"platform_admin"}]}}],"args":[{"name":"index","type":"u8"}]},{"name":"sell_exact_in","docs":["Use the given amount of base tokens to sell for quote tokens.","# Arguments","","* `ctx` - The context of accounts","* `amount_in` - Amount of base token to sell","* `minimum_amount_out` - Minimum amount of quote token to receive (slippage protection)","* `share_fee_rate` - Fee rate for the share",""],"discriminator":[149,39,222,155,211,124,152,26],"accounts":[{"name":"payer","docs":["The user performing the swap operation","Must sign the transaction and pay for fees"],"writable":true,"signer":true},{"name":"authority","docs":["PDA that acts as the authority for pool vault operations","Generated using AUTH_SEED"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"global_config","docs":["Global configuration account containing protocol-wide settings","Used to read protocol fee rates and curve type"]},{"name":"platform_config","docs":["Platform configuration account containing platform-wide settings","Used to read platform fee rate"]},{"name":"pool_state","docs":["The pool state account where the swap will be performed","Contains current pool parameters and balances"],"writable":true},{"name":"user_base_token","docs":["The user's token account for base tokens (tokens being bought)","Will receive the output tokens after the swap"],"writable":true},{"name":"user_quote_token","docs":["The user's token account for quote tokens (tokens being sold)","Will be debited for the input amount"],"writable":true},{"name":"base_vault","docs":["The pool's vault for base tokens","Will be debited to send tokens to the user"],"writable":true},{"name":"quote_vault","docs":["The pool's vault for quote tokens","Will receive the input tokens from the user"],"writable":true},{"name":"base_token_mint","docs":["The mint of the base token","Used for transfer fee calculations if applicable"]},{"name":"quote_token_mint","docs":["The mint of the quote token"]},{"name":"base_token_program","docs":["SPL Token program for base token transfers"]},{"name":"quote_token_program","docs":["SPL Token program for quote token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"},{"name":"share_fee_rate","type":"u64"}]},{"name":"sell_exact_out","docs":["Sell base tokens for the given amount of quote tokens.","# Arguments","","* `ctx` - The context of accounts","* `amount_out` - Amount of quote token to receive","* `maximum_amount_in` - Maximum amount of base token to purchase (slippage protection)","* `share_fee_rate` - Fee rate for the share",""],"discriminator":[95,200,71,34,8,9,11,166],"accounts":[{"name":"payer","docs":["The user performing the swap operation","Must sign the transaction and pay for fees"],"writable":true,"signer":true},{"name":"authority","docs":["PDA that acts as the authority for pool vault operations","Generated using AUTH_SEED"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"global_config","docs":["Global configuration account containing protocol-wide settings","Used to read protocol fee rates and curve type"]},{"name":"platform_config","docs":["Platform configuration account containing platform-wide settings","Used to read platform fee rate"]},{"name":"pool_state","docs":["The pool state account where the swap will be performed","Contains current pool parameters and balances"],"writable":true},{"name":"user_base_token","docs":["The user's token account for base tokens (tokens being bought)","Will receive the output tokens after the swap"],"writable":true},{"name":"user_quote_token","docs":["The user's token account for quote tokens (tokens being sold)","Will be debited for the input amount"],"writable":true},{"name":"base_vault","docs":["The pool's vault for base tokens","Will be debited to send tokens to the user"],"writable":true},{"name":"quote_vault","docs":["The pool's vault for quote tokens","Will receive the input tokens from the user"],"writable":true},{"name":"base_token_mint","docs":["The mint of the base token","Used for transfer fee calculations if applicable"]},{"name":"quote_token_mint","docs":["The mint of the quote token"]},{"name":"base_token_program","docs":["SPL Token program for base token transfers"]},{"name":"quote_token_program","docs":["SPL Token program for quote token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_out","type":"u64"},{"name":"maximum_amount_in","type":"u64"},{"name":"share_fee_rate","type":"u64"}]},{"name":"update_config","docs":["Updates configuration parameters","# Arguments","","* `ctx` - The context of accounts","* `param` - Parameter to update:","- 0: Update trade_fee_rate","- 1: Update fee owner","- 12: Toggle whether explicit platform authorization is required","* `value` - New value for the selected parameter",""],"discriminator":[29,158,252,191,10,83,219,99],"accounts":[{"name":"owner","docs":["The global config owner or admin"],"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"global_config","docs":["Global config account to be changed"],"writable":true}],"args":[{"name":"param","type":"u8"},{"name":"value","type":"u64"}]},{"name":"update_platform_config","docs":["Update platform config","# Arguments","","* `ctx` - The context of accounts","* `param` - Parameter to update",""],"discriminator":[195,60,76,129,146,45,67,143],"accounts":[{"name":"platform_admin","docs":["The account paying for the initialization costs"],"signer":true},{"name":"platform_config","docs":["Platform config account to be changed"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,108,97,116,102,111,114,109,95,99,111,110,102,105,103]},{"kind":"account","path":"platform_admin"}]}}],"args":[{"name":"param","type":{"defined":{"name":"PlatformConfigParam"}}}]},{"name":"update_platform_curve_param","docs":["Update platform launch param","# Arguments","","* `ctx` - The context of accounts","* `bonding_curve_param` - Parameter to update",""],"discriminator":[138,144,138,250,220,128,4,57],"accounts":[{"name":"platform_admin","docs":["The account paying for the initialization costs"],"writable":true,"signer":true},{"name":"platform_config","docs":["Platform config account to be changed"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,108,97,116,102,111,114,109,95,99,111,110,102,105,103]},{"kind":"account","path":"platform_admin"}]}},{"name":"global_config","docs":["Global configuration account containing protocol-wide settings","Includes settings like quote token mint and fee parameters"]},{"name":"system_program","docs":["System program for lamport transfers"],"address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"u8"},{"name":"bonding_curve_param","type":{"defined":{"name":"BondingCurveParam"}}}]}],"accounts":[{"name":"GlobalConfig","discriminator":[149,8,156,202,160,252,176,217]},{"name":"PlatformConfig","discriminator":[160,78,128,0,248,83,230,160]},{"name":"PlatformGlobalAccess","discriminator":[174,147,132,240,137,219,243,16]},{"name":"PoolState","discriminator":[247,237,227,245,215,195,222,70]},{"name":"VestingRecord","discriminator":[106,243,221,205,230,126,85,83]}],"events":[{"name":"ClaimVestedEvent","discriminator":[21,194,114,87,120,211,226,32]},{"name":"CreateVestingEvent","discriminator":[150,152,11,179,52,210,191,125]},{"name":"PoolCreateEvent","discriminator":[151,215,226,9,118,161,115,174]},{"name":"TradeEvent","discriminator":[189,219,127,211,78,230,97,238]}],"errors":[{"code":6000,"name":"NotApproved","msg":"Not approved"},{"code":6001,"name":"InvalidOwner","msg":"Input account owner is not the program address"},{"code":6002,"name":"InvalidInput","msg":"InvalidInput"},{"code":6003,"name":"InputNotMatchCurveConfig","msg":"The input params are not match with curve type in config"},{"code":6004,"name":"ExceededSlippage","msg":"Exceeds desired slippage limit"},{"code":6005,"name":"PoolFunding","msg":"Pool funding"},{"code":6006,"name":"PoolMigrated","msg":"Pool migrated"},{"code":6007,"name":"MigrateTypeNotMatch","msg":"Migrate type not match"},{"code":6008,"name":"MathOverflow","msg":"Math overflow"},{"code":6009,"name":"NoAssetsToCollect","msg":"No assets to collect"},{"code":6010,"name":"VestingRatioTooHigh","msg":"Vesting ratio too high"},{"code":6011,"name":"VestingSettingEnded","msg":"Vesting setting ended"},{"code":6012,"name":"VestingNotStarted","msg":"Vesting not started"},{"code":6013,"name":"NoVestingSchedule","msg":"No vesting schedule"},{"code":6014,"name":"InvalidPlatformInfo","msg":"The platform info input is invalid"},{"code":6015,"name":"PoolNotMigrated","msg":"Pool not migrated"},{"code":6016,"name":"InvalidCpSwapConfig","msg":"The input cp swap config account is invalid"},{"code":6017,"name":"NoSupportExtension","msg":"No support extension"},{"code":6018,"name":"NotEnoughRemainingAccounts","msg":"Not enough remaining accounts"},{"code":6019,"name":"TransferFeeCalculateNotMatch","msg":"TransferFee calculate not match"},{"code":6020,"name":"CurveParamIsNotExist","msg":"Curve param is not exist"},{"code":6021,"name":"InvalidTotalLockedAmount","msg":"Total locked amount must great or equal to the platform vesting share amount"},{"code":6022,"name":"PlatformGlobalAccessDenied","msg":"Platform is not authorized to use this global config"},{"code":6023,"name":"InvalidPlatformGlobalAccess","msg":"Invalid platform-global access account"}],"types":[{"name":"AmmCreatorFeeOn","docs":["migrate to cpmm, creator fee on quote token or both token"],"type":{"kind":"enum","variants":[{"name":"QuoteToken"},{"name":"BothToken"}]}},{"name":"BondingCurveParam","type":{"kind":"struct","fields":[{"name":"migrate_type","docs":["Migrate to AMM or CpSwap, 0: amm, 1: cpswap,","Neither 0 nor 1: invalid"],"type":"u8"},{"name":"migrate_cpmm_fee_on","docs":["The migrate fee on, 0 means fee on the quote token, 1 means fee on both token","Neither 0 nor 1: invalid"],"type":"u8"},{"name":"supply","docs":["The supply of the token,","0: invalid"],"type":"u64"},{"name":"total_base_sell","docs":["The total base sell of the token","0: invalid"],"type":"u64"},{"name":"total_quote_fund_raising","docs":["The total quote fund raising of the token","0: invalid"],"type":"u64"},{"name":"total_locked_amount","docs":["total amount of tokens to be unlocked","u64::MAX: invalid"],"type":"u64"},{"name":"cliff_period","docs":["Waiting time in seconds before unlocking after fundraising ends","u64::MAX: invalid"],"type":"u64"},{"name":"unlock_period","docs":["Unlocking period in seconds","u64::MAX: invalid"],"type":"u64"}]}},{"name":"ClaimVestedEvent","docs":["Emitted when vesting token claimed by beneficiary"],"type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"beneficiary","type":"pubkey"},{"name":"claim_amount","type":"u64"}]}},{"name":"ConstantCurve","type":{"kind":"struct","fields":[{"name":"supply","type":"u64"},{"name":"total_base_sell","type":"u64"},{"name":"total_quote_fund_raising","type":"u64"},{"name":"migrate_type","type":"u8"}]}},{"name":"CreateVestingEvent","docs":["Emitted when vest_account created"],"type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"beneficiary","type":"pubkey"},{"name":"share_amount","type":"u64"}]}},{"name":"CurveParams","type":{"kind":"enum","variants":[{"name":"Constant","fields":[{"name":"data","type":{"defined":{"name":"ConstantCurve"}}}]},{"name":"Fixed","fields":[{"name":"data","type":{"defined":{"name":"FixedCurve"}}}]},{"name":"Linear","fields":[{"name":"data","type":{"defined":{"name":"LinearCurve"}}}]}]}},{"name":"FixedCurve","type":{"kind":"struct","fields":[{"name":"supply","type":"u64"},{"name":"total_quote_fund_raising","type":"u64"},{"name":"migrate_type","type":"u8"}]}},{"name":"GlobalConfig","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"epoch","docs":["Account update epoch"],"type":"u64"},{"name":"curve_type","docs":["0: Constant Product Curve","1: Fixed Price Curve","2: Linear Price Curve"],"type":"u8"},{"name":"index","docs":["Config index"],"type":"u16"},{"name":"migrate_fee","docs":["The fee of migrate to amm"],"type":"u64"},{"name":"trade_fee_rate","docs":["The trade fee rate, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"max_share_fee_rate","docs":["The maximum share fee rate, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"min_base_supply","docs":["The minimum base supply, the value without decimals"],"type":"u64"},{"name":"max_lock_rate","docs":["The maximum lock rate, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"min_base_sell_rate","docs":["The minimum base sell rate, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"min_base_migrate_rate","docs":["The minimum base migrate rate, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"min_quote_fund_raising","docs":["The minimum quote fund raising, the value with decimals"],"type":"u64"},{"name":"quote_mint","docs":["Mint information for quote token"],"type":"pubkey"},{"name":"protocol_fee_owner","docs":["Protocol Fee owner"],"type":"pubkey"},{"name":"migrate_fee_owner","docs":["Migrate Fee owner"],"type":"pubkey"},{"name":"migrate_to_amm_wallet","docs":["Migrate to amm control wallet"],"type":"pubkey"},{"name":"migrate_to_cpswap_wallet","docs":["Migrate to cpswap wallet"],"type":"pubkey"},{"name":"requires_platform_auth","docs":["Whether a platform must be explicitly authorized before using this global config"],"type":"u8"},{"name":"padding_alignment","docs":["padding alignment"],"type":{"array":["u8",7]}},{"name":"padding","docs":["padding for future updates"],"type":{"array":["u64",15]}}]}},{"name":"LinearCurve","type":{"kind":"struct","fields":[{"name":"supply","type":"u64"},{"name":"total_quote_fund_raising","type":"u64"},{"name":"migrate_type","type":"u8"}]}},{"name":"MigrateNftInfo","docs":["Represents the parameters for initializing a platform config account(Only support MigrateType::CPSWAP)","# Fields","* `platform_scale` - Scale of the platform liquidity quantity rights will be converted into NFT","* `creator_scale` - Scale of the token creator liquidity quantity rights will be converted into NFT","* `burn_scale` - Scale of liquidity directly to burn","","* platform_scale + creator_scale + burn_scale = RATE_DENOMINATOR_VALUE"],"type":{"kind":"struct","fields":[{"name":"platform_scale","type":"u64"},{"name":"creator_scale","type":"u64"},{"name":"burn_scale","type":"u64"}]}},{"name":"MintParams","docs":["Represents the parameters for initializing a new token mint","# Fields","* `decimals` - Number of decimal places for the token","* `name` - Name of the token","* `symbol` - Symbol/ticker of the token","* `uri` - URI pointing to token metadata"],"type":{"kind":"struct","fields":[{"name":"decimals","type":"u8"},{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]}},{"name":"PlatformConfig","type":{"kind":"struct","fields":[{"name":"epoch","docs":["The epoch for update interval"],"type":"u64"},{"name":"platform_fee_wallet","docs":["The platform fee wallet"],"type":"pubkey"},{"name":"platform_nft_wallet","docs":["The platform nft wallet to receive the platform NFT after migration if platform_scale is not 0(Only support MigrateType::CPSWAP)"],"type":"pubkey"},{"name":"platform_scale","docs":["Scale of the platform liquidity quantity rights will be converted into NFT(Only support MigrateType::CPSWAP)"],"type":"u64"},{"name":"creator_scale","docs":["Scale of the token creator liquidity quantity rights will be converted into NFT(Only support MigrateType::CPSWAP)"],"type":"u64"},{"name":"burn_scale","docs":["Scale of liquidity directly to burn"],"type":"u64"},{"name":"fee_rate","docs":["The platform fee rate"],"type":"u64"},{"name":"name","docs":["The platform name"],"type":{"array":["u8",64]}},{"name":"web","docs":["The platform website"],"type":{"array":["u8",256]}},{"name":"img","docs":["The platform img link"],"type":{"array":["u8",256]}},{"name":"cpswap_config","docs":["The platform specifies the trade fee rate after migration to cp swap"],"type":"pubkey"},{"name":"creator_fee_rate","docs":["Creator fee rate"],"type":"u64"},{"name":"transfer_fee_extension_auth","docs":["If the base token belongs to token2022, then you can choose to support the transferfeeConfig extension, which includes permissions such as `transfer_fee_config_authority`` and `withdraw_withheld_authority`.","When initializing mint, `withdraw_withheld_authority` and `transfer_fee_config_authority` both belongs to the contract.","Once the token is migrated to AMM, the authorities will be reset to this value"],"type":"pubkey"},{"name":"platform_vesting_wallet","type":"pubkey"},{"name":"platform_vesting_scale","type":"u64"},{"name":"platform_cp_creator","docs":["If a valid platform_cp_creator is configured for the platform,","it will be used as the creator for the AMM pool during migration to cpswap pool."],"type":"pubkey"},{"name":"padding","docs":["padding for future updates"],"type":{"array":["u8",108]}},{"name":"curve_params","docs":["The parameters for launching the pool"],"type":{"vec":{"defined":{"name":"PlatformCurveParam"}}}}]}},{"name":"PlatformConfigInfo","type":{"kind":"struct","fields":[{"name":"fee_wallet","type":"pubkey"},{"name":"nft_wallet","type":"pubkey"},{"name":"migrate_nft_info","type":{"defined":{"name":"MigrateNftInfo"}}},{"name":"fee_rate","type":"u64"},{"name":"name","type":"string"},{"name":"web","type":"string"},{"name":"img","type":"string"},{"name":"transfer_fee_extension_auth","type":"pubkey"},{"name":"creator_fee_rate","type":"u64"},{"name":"platform_vesting_scale","type":"u64"},{"name":"vesting_wallet","type":"pubkey"}]}},{"name":"PlatformConfigParam","type":{"kind":"enum","variants":[{"name":"FeeWallet","fields":["pubkey"]},{"name":"NFTWallet","fields":["pubkey"]},{"name":"MigrateNftInfo","fields":[{"defined":{"name":"MigrateNftInfo"}}]},{"name":"FeeRate","fields":["u64"]},{"name":"Name","fields":["string"]},{"name":"Web","fields":["string"]},{"name":"Img","fields":["string"]},{"name":"CpSwapConfig"},{"name":"AllInfo","fields":[{"defined":{"name":"PlatformConfigInfo"}}]},{"name":"VestingWallet","fields":["pubkey"]},{"name":"PlatformVestingScale","fields":["u64"]},{"name":"PlatformCPCreator","fields":["pubkey"]}]}},{"name":"PlatformCurveParam","type":{"kind":"struct","fields":[{"name":"epoch","docs":["The epoch for update interval, 0 means not update"],"type":"u64"},{"name":"index","docs":["The curve params index"],"type":"u8"},{"name":"global_config","docs":["The global config address"],"type":"pubkey"},{"name":"bonding_curve_param","docs":["bonding curve param"],"type":{"defined":{"name":"BondingCurveParam"}}},{"name":"padding","docs":["padding for future updates"],"type":{"array":["u64",50]}}]}},{"name":"PlatformGlobalAccess","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"global_config","type":"pubkey"},{"name":"platform_config","type":"pubkey"},{"name":"padding","docs":["padding for future updates"],"type":{"array":["u64",8]}}]}},{"name":"PlatformParams","docs":["Represents the parameters for initializing a platform config account","# Fields","* `migrate_nft_info` - The platform configures liquidity info during migration(Only support MigrateType::CPSWAP)","* `fee_rate` - Fee rate of the platform","* `name` - Name of the platform","* `web` - Website of the platform","* `img` - Image link of the platform","/// * `creator_fee_rate` - The fee rate charged by the creator for each transaction."],"type":{"kind":"struct","fields":[{"name":"migrate_nft_info","type":{"defined":{"name":"MigrateNftInfo"}}},{"name":"fee_rate","type":"u64"},{"name":"name","type":"string"},{"name":"web","type":"string"},{"name":"img","type":"string"},{"name":"creator_fee_rate","type":"u64"},{"name":"platform_vesting_scale","type":"u64"}]}},{"name":"PoolCreateEvent","docs":["Emitted when pool created"],"type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"creator","type":"pubkey"},{"name":"config","type":"pubkey"},{"name":"base_mint_param","type":{"defined":{"name":"MintParams"}}},{"name":"curve_param","type":{"defined":{"name":"CurveParams"}}},{"name":"vesting_param","type":{"defined":{"name":"VestingParams"}}},{"name":"amm_fee_on","type":{"defined":{"name":"AmmCreatorFeeOn"}}}]}},{"name":"PoolState","docs":["Represents the state of a trading pool in the protocol","Stores all essential information about pool balances, fees, and configuration"],"type":{"kind":"struct","fields":[{"name":"epoch","docs":["Account update epoch"],"type":"u64"},{"name":"auth_bump","docs":["Bump seed used for PDA address derivation"],"type":"u8"},{"name":"status","docs":["Current status of the pool","* 0: Pool is funding","* 1: Pool funding is end, waiting for migration","* 2: Pool migration is done"],"type":"u8"},{"name":"base_decimals","docs":["Decimals of the pool base token"],"type":"u8"},{"name":"quote_decimals","docs":["Decimals of the pool quote token"],"type":"u8"},{"name":"migrate_type","docs":["Migrate to AMM or CpSwap, 0: amm, 1: cpswap"],"type":"u8"},{"name":"supply","docs":["Supply of the pool base token"],"type":"u64"},{"name":"total_base_sell","docs":["Total sell amount of the base token"],"type":"u64"},{"name":"virtual_base","docs":["For different curves, virtual_base and virtual_quote have different meanings","For constant product curve, virtual_base and virtual_quote are virtual liquidity, virtual_quote/virtual_base is the initial price","For linear price curve, virtual_base is the price slope parameter a, virtual_quote has no effect","For fixed price curve, virtual_quote/virtual_base is the initial price"],"type":"u64"},{"name":"virtual_quote","type":"u64"},{"name":"real_base","docs":["Actual base token amount in the pool","Represents the real tokens available for trading"],"type":"u64"},{"name":"real_quote","docs":["Actual quote token amount in the pool","Represents the real tokens available for trading"],"type":"u64"},{"name":"total_quote_fund_raising","docs":["The total quote fund raising of the pool"],"type":"u64"},{"name":"quote_protocol_fee","docs":["Accumulated trading fees in quote tokens","Can be collected by the protocol fee owner"],"type":"u64"},{"name":"platform_fee","docs":["Accumulated platform fees in quote tokens","Can be collected by the platform wallet stored in platform config"],"type":"u64"},{"name":"migrate_fee","docs":["The fee of migrate to amm"],"type":"u64"},{"name":"vesting_schedule","docs":["Vesting schedule for the base token"],"type":{"defined":{"name":"VestingSchedule"}}},{"name":"global_config","docs":["Public key of the global configuration account","Contains protocol-wide settings this pool adheres to"],"type":"pubkey"},{"name":"platform_config","docs":["Public key of the platform configuration account","Contains platform-wide settings this pool adheres to"],"type":"pubkey"},{"name":"base_mint","docs":["Public key of the base mint address"],"type":"pubkey"},{"name":"quote_mint","docs":["Public key of the quote mint address"],"type":"pubkey"},{"name":"base_vault","docs":["Public key of the base token vault","Holds the actual base tokens owned by the pool"],"type":"pubkey"},{"name":"quote_vault","docs":["Public key of the quote token vault","Holds the actual quote tokens owned by the pool"],"type":"pubkey"},{"name":"creator","docs":["The creator of base token"],"type":"pubkey"},{"name":"token_program_flag","docs":["token program bits","bit0: base token program flag","0: spl_token_program","1: token_program_2022","","bit1: quote token program flag","0: spl_token_program","1: token_program_2022"],"type":"u8"},{"name":"amm_creator_fee_on","docs":["migrate to cpmm, creator fee on quote token or both token"],"type":{"defined":{"name":"AmmCreatorFeeOn"}}},{"name":"platform_vesting_share","docs":["platform vesting token amount"],"type":"u64"},{"name":"padding","docs":["padding for future updates"],"type":{"array":["u8",54]}}]}},{"name":"PoolStatus","docs":["Represents the different states a pool can be in","* Fund - Initial state where pool is accepting funds","* Migrate - Pool funding has ended and waiting for migration","* Trade - Pool migration is complete and amm trading is enabled"],"type":{"kind":"enum","variants":[{"name":"Fund"},{"name":"Migrate"},{"name":"Trade"}]}},{"name":"TradeDirection","docs":["Specifies the direction of a trade in the bonding curve","This is important because curves can treat tokens differently through weights or offsets"],"type":{"kind":"enum","variants":[{"name":"Buy"},{"name":"Sell"}]}},{"name":"TradeEvent","docs":["Emitted when trade process"],"type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"total_base_sell","type":"u64"},{"name":"virtual_base","type":"u64"},{"name":"virtual_quote","type":"u64"},{"name":"real_base_before","type":"u64"},{"name":"real_quote_before","type":"u64"},{"name":"real_base_after","type":"u64"},{"name":"real_quote_after","type":"u64"},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"},{"name":"protocol_fee","type":"u64"},{"name":"platform_fee","type":"u64"},{"name":"creator_fee","type":"u64"},{"name":"share_fee","type":"u64"},{"name":"trade_direction","type":{"defined":{"name":"TradeDirection"}}},{"name":"pool_status","type":{"defined":{"name":"PoolStatus"}}},{"name":"exact_in","type":"bool"}]}},{"name":"TransferFeeExtensionParams","type":{"kind":"struct","fields":[{"name":"transfer_fee_basis_points","docs":["denominator is 10000, currently, this value cannot exceed 5%, which is 500."],"type":"u16"},{"name":"maximum_fee","docs":["Maximum fee on each transfers, the value must exceed supply * transfer_fee_basis_points / 10000"],"type":"u64"}]}},{"name":"VestingParams","type":{"kind":"struct","fields":[{"name":"total_locked_amount","type":"u64"},{"name":"cliff_period","type":"u64"},{"name":"unlock_period","type":"u64"}]}},{"name":"VestingRecord","type":{"kind":"struct","fields":[{"name":"epoch","docs":["Account update epoch"],"type":"u64"},{"name":"pool","docs":["The pool state account"],"type":"pubkey"},{"name":"beneficiary","docs":["The beneficiary of the vesting account"],"type":"pubkey"},{"name":"claimed_amount","docs":["The amount of tokens claimed"],"type":"u64"},{"name":"token_share_amount","docs":["The share amount of the token to be vested"],"type":"u64"},{"name":"padding","docs":["padding for future updates"],"type":{"array":["u64",8]}}]}},{"name":"VestingSchedule","type":{"kind":"struct","fields":[{"name":"total_locked_amount","type":"u64"},{"name":"cliff_period","type":"u64"},{"name":"unlock_period","type":"u64"},{"name":"start_time","type":"u64"},{"name":"allocated_share_amount","docs":["Total allocated share amount of the base token, not greater than total_locked_amount"],"type":"u64"}]}}]} \ No newline at end of file diff --git a/idls/raydium_lock.LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE.json b/idls/raydium_lock.LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE.json new file mode 100644 index 0000000..eeb48fa --- /dev/null +++ b/idls/raydium_lock.LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"raydium_liquidity_locking","instructions":[{"name":"lockClmmPosition","docs":["Lock an existing clmm's position","","# Arguments","","* `ctx` - The context of accounts","* `with_metadata` - Create NFT with metadata or not",""],"accounts":[{"name":"authority","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true,"docs":["Pay to create account lamports"]},{"name":"positionNftOwner","isMut":false,"isSigner":true,"docs":["The position NFT owner"]},{"name":"feeNftOwner","isMut":false,"isSigner":false},{"name":"positionNftAccount","isMut":true,"isSigner":false,"docs":["The token account for the tokenized position"]},{"name":"personalPosition","isMut":false,"isSigner":false,"docs":["Lock personal position associated with position_nft_account's mint"]},{"name":"positionNftMint","isMut":false,"isSigner":false,"docs":["Position NFT mint to create a ATA `locked_nft_account` belongs to program"]},{"name":"lockedNftAccount","isMut":true,"isSigner":false,"docs":["Program's token account where `position_nft_account` will transfer to"]},{"name":"lockedPosition","isMut":true,"isSigner":false,"docs":["Store the locked information of the personal position"]},{"name":"feeNftMint","isMut":true,"isSigner":true,"docs":["Create a unique fee nft mint"]},{"name":"feeNftAccount","isMut":true,"isSigner":false,"docs":["Token account where fee nft will be minted to"]},{"name":"metadataAccount","isMut":true,"isSigner":false,"docs":["To store metaplex metadata"]},{"name":"metadataProgram","isMut":false,"isSigner":false,"docs":["Program to create NFT metadata account"]},{"name":"associatedTokenProgram","isMut":false,"isSigner":false,"docs":["Program to create an ATA for receiving fee NFT"]},{"name":"rent","isMut":false,"isSigner":false,"docs":["Sysvar for token mint and ATA creation"]},{"name":"feeNftTokenProgram","isMut":false,"isSigner":false,"docs":["Token program fee NFT mint/account belongs to"]},{"name":"lockedNftTokenProgram","isMut":false,"isSigner":false,"docs":["Token program position NFT mint/account belongs to"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["Program to create new account"]}],"args":[{"name":"withMetadata","type":"bool"}]},{"name":"collectClmmFeesAndRewards","docs":["collect clmm locked postion fees and rewards","# Arguments","","* `ctx` - The context of accounts",""],"accounts":[{"name":"authority","isMut":false,"isSigner":false},{"name":"feeNftOwner","isMut":false,"isSigner":true,"docs":["The owner who has fee nft account"]},{"name":"feeNftAccount","isMut":false,"isSigner":false,"docs":["Fee token account"]},{"name":"lockedPosition","isMut":false,"isSigner":false,"docs":["Store the locked the information of position"]},{"name":"clmmProgram","isMut":false,"isSigner":false,"docs":["clmm program"]},{"name":"lockedNftAccount","isMut":true,"isSigner":false,"docs":["Program ATA locked NFT account or user ATA position NFT account"]},{"name":"personalPosition","isMut":true,"isSigner":false,"docs":["Decrease liquidity for this position"]},{"name":"poolState","isMut":true,"isSigner":false},{"name":"protocolPosition","isMut":true,"isSigner":false},{"name":"token0Vault","isMut":true,"isSigner":false,"docs":["The address that holds pool tokens for token_0"]},{"name":"token1Vault","isMut":true,"isSigner":false,"docs":["The address that holds pool tokens for token_1"]},{"name":"tickArrayLower","isMut":true,"isSigner":false,"docs":["Stores init state for the lower tick"]},{"name":"tickArrayUpper","isMut":true,"isSigner":false,"docs":["Stores init state for the upper tick"]},{"name":"recipientToken0Account","isMut":true,"isSigner":false,"docs":["The destination token account for receive amount_0"]},{"name":"recipientToken1Account","isMut":true,"isSigner":false,"docs":["The destination token account for receive amount_1"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["SPL program to transfer out tokens"]},{"name":"tokenProgram2022","isMut":false,"isSigner":false,"docs":["Token program 2022"]},{"name":"memoProgram","isMut":false,"isSigner":false,"docs":["memo program"]},{"name":"vault0Mint","isMut":false,"isSigner":false,"docs":["The mint of token vault 0"]},{"name":"vault1Mint","isMut":false,"isSigner":false,"docs":["The mint of token vault 1"]}],"args":[]},{"name":"lockCpLiquidity","docs":["Lock cpSwap liquidity and mint a nft to collect locked liquidity's fee.","","# Arguments","","* `ctx` - The context of accounts","* `lp_amount` - The lp amount to lock","* `with_metadata` - Create NFT with metadata or not",""],"accounts":[{"name":"authority","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true,"docs":["Pay to create account lamports"]},{"name":"liquidityOwner","isMut":false,"isSigner":true,"docs":["who want to lock liquidity"]},{"name":"feeNftOwner","isMut":false,"isSigner":false},{"name":"feeNftMint","isMut":true,"isSigner":true,"docs":["Create a unique fee nft mint"]},{"name":"feeNftAccount","isMut":true,"isSigner":false,"docs":["Token account where fee nft will be minted to"]},{"name":"poolState","isMut":false,"isSigner":false,"docs":["Indicates which pool the locked liquidity belong to"]},{"name":"lockedLiquidity","isMut":true,"isSigner":false,"docs":["Store the locked information of liquidity"]},{"name":"lpMint","isMut":false,"isSigner":false,"docs":["The mint of liquidity token"]},{"name":"liquidityOwnerLp","isMut":true,"isSigner":false,"docs":["liquidity owner lp token account"]},{"name":"lockedLpVault","isMut":true,"isSigner":false,"docs":["Locked lp token deposit to"]},{"name":"token0Vault","isMut":true,"isSigner":false,"docs":["The address that holds pool tokens for token_0"]},{"name":"token1Vault","isMut":true,"isSigner":false,"docs":["The address that holds pool tokens for token_1"]},{"name":"metadataAccount","isMut":true,"isSigner":false,"docs":["To store metaplex metadata"]},{"name":"rent","isMut":false,"isSigner":false,"docs":["Sysvar for token mint and ATA creation"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["Program to create the new account"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Program to create/transfer mint/token account"]},{"name":"associatedTokenProgram","isMut":false,"isSigner":false,"docs":["Program to create an ATA for receiving fee NFT"]},{"name":"metadataProgram","isMut":false,"isSigner":false,"docs":["Program to create NFT metadata accunt"]}],"args":[{"name":"lpAmount","type":"u64"},{"name":"withMetadata","type":"bool"}]},{"name":"collectCpFees","docs":["collect cpSwap locked liquidity's fees","# Arguments","","* `ctx` - The context of accounts","* `fee_lp_amount` - The amount lp want to claim",""],"accounts":[{"name":"authority","isMut":false,"isSigner":false},{"name":"feeNftOwner","isMut":false,"isSigner":true,"docs":["Fee nft owner who is allowed to receive fees"]},{"name":"feeNftAccount","isMut":false,"isSigner":false,"docs":["Fee token account"]},{"name":"lockedLiquidity","isMut":true,"isSigner":false,"docs":["Store the locked the information of liquidity"]},{"name":"cpSwapProgram","isMut":false,"isSigner":false,"docs":["cpswap program"]},{"name":"cpAuthority","isMut":false,"isSigner":false},{"name":"poolState","isMut":true,"isSigner":false,"docs":["Pool state account"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["lp mint"]},{"name":"recipientToken0Account","isMut":true,"isSigner":false,"docs":["The token account for receive token_0"]},{"name":"recipientToken1Account","isMut":true,"isSigner":false,"docs":["The token account for receive token_1"]},{"name":"token0Vault","isMut":true,"isSigner":false,"docs":["The address that holds pool tokens for token_0"]},{"name":"token1Vault","isMut":true,"isSigner":false,"docs":["The address that holds pool tokens for token_1"]},{"name":"vault0Mint","isMut":false,"isSigner":false,"docs":["The mint of token_0 vault"]},{"name":"vault1Mint","isMut":false,"isSigner":false,"docs":["The mint of token_1 vault"]},{"name":"lockedLpVault","isMut":true,"isSigner":false,"docs":["locked lp token account"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token Program"]},{"name":"tokenProgram2022","isMut":false,"isSigner":false,"docs":["Token program 2022"]},{"name":"memoProgram","isMut":false,"isSigner":false,"docs":["memo program"]}],"args":[{"name":"feeLpAmount","type":"u64"}]}],"accounts":[{"name":"LockedCpLiquidityState","type":{"kind":"struct","fields":[{"name":"lockedLpAmount","docs":["The Locked liquidity amount without claimed lp fee"],"type":"u64"},{"name":"claimedLpAmount","docs":["Claimed lp fee amount"],"type":"u64"},{"name":"unclaimedLpAmount","docs":["Unclaimed lp fee amount"],"type":"u64"},{"name":"lastLp","docs":["Last updated cp pool lp total supply"],"type":"u64"},{"name":"lastK","docs":["Last updated cp pool k"],"type":"u128"},{"name":"recentEpoch","docs":["Account update recent epoch"],"type":"u64"},{"name":"poolId","docs":["The ID of the pool with which this record is connected"],"type":"publicKey"},{"name":"feeNftMint","docs":["nft mint to check who has authority to collect fee"],"type":"publicKey"},{"name":"lockedOwner","docs":["The owner who has locked liquidity"],"type":"publicKey"},{"name":"lockedLpMint","docs":["The mint of locked lp token"],"type":"publicKey"},{"name":"padding","docs":["Unused bytes for future upgrades."],"type":{"array":["u64",8]}}]}},{"name":"LockedClmmPositionState","type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":{"array":["u8",1]}},{"name":"positionOwner","docs":["The owner who has locked clmm NFT"],"type":"publicKey"},{"name":"poolId","docs":["The ID of the pool with which this record is connected"],"type":"publicKey"},{"name":"positionId","docs":["The ID of the position with which this record is connected"],"type":"publicKey"},{"name":"lockedNftAccount","docs":["Program ATA locked NFT account or user ATA position NFT account"],"type":"publicKey"},{"name":"feeNftMint","docs":["nft mint to check who has authority to collect fee"],"type":"publicKey"},{"name":"recentEpoch","docs":["account update recent epoch"],"type":"u64"},{"name":"padding","docs":["Unused bytes for future upgrades."],"type":{"array":["u64",8]}}]}}],"events":[{"name":"SettleCpFeeEvent","fields":[{"name":"deltaAmount","type":"u64","index":false},{"name":"unclaimedAmount","type":"u64","index":false},{"name":"lockedAmount","type":"u64","index":false},{"name":"currPoolLp","type":"u64","index":false},{"name":"lastPoolLp","type":"u64","index":false},{"name":"currK","type":"u128","index":false},{"name":"lastK","type":"u128","index":false}]}]} \ No newline at end of file diff --git a/idls/saber_decimal.DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB.json b/idls/saber_decimal.DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB.json new file mode 100644 index 0000000..e827bc7 --- /dev/null +++ b/idls/saber_decimal.DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB.json @@ -0,0 +1 @@ +{"version":"0.0.0","name":"add_decimals","instructions":[{"name":"initializeWrapper","accounts":[{"name":"wrapper","isMut":true,"isSigner":false},{"name":"wrapperUnderlyingTokens","isMut":false,"isSigner":false},{"name":"underlyingMint","isMut":false,"isSigner":false},{"name":"wrapperMint","isMut":false,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"deposit","accounts":[{"name":"wrapper","isMut":false,"isSigner":false},{"name":"wrapperMint","isMut":true,"isSigner":false},{"name":"wrapperUnderlyingTokens","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"userUnderlyingTokens","isMut":true,"isSigner":false},{"name":"userWrappedTokens","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"depositAmount","type":"u64"}]},{"name":"withdraw","accounts":[{"name":"wrapper","isMut":false,"isSigner":false},{"name":"wrapperMint","isMut":true,"isSigner":false},{"name":"wrapperUnderlyingTokens","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"userUnderlyingTokens","isMut":true,"isSigner":false},{"name":"userWrappedTokens","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxBurnAmount","type":"u64"}]}],"accounts":[{"name":"WrappedToken","type":{"kind":"struct","fields":[{"name":"decimals","type":"u8"},{"name":"multiplier","type":"u64"},{"name":"wrapperUnderlyingMint","type":"publicKey"},{"name":"wrapperUnderlyingTokens","type":"publicKey"},{"name":"wrapperMint","type":"publicKey"}]}}],"events":[{"name":"InitEvent","fields":[{"name":"payer","type":"publicKey","index":false},{"name":"decimals","type":"u8","index":false},{"name":"multiplier","type":"u64","index":false},{"name":"wrapperUnderlyingMint","type":"publicKey","index":false},{"name":"wrapperUnderlyingTokens","type":"publicKey","index":false},{"name":"wrapperMint","type":"publicKey","index":false}]},{"name":"DepositEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"underlyingMint","type":"publicKey","index":false},{"name":"wrappedMint","type":"publicKey","index":false},{"name":"depositAmount","type":"u64","index":false},{"name":"mintAmount","type":"u64","index":false}]},{"name":"WithdrawEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"underlyingMint","type":"publicKey","index":false},{"name":"wrappedMint","type":"publicKey","index":false},{"name":"withdrawAmount","type":"u64","index":false},{"name":"burnAmount","type":"u64","index":false},{"name":"dustAmount","type":"u64","index":false}]}],"errors":[{"code":300,"name":"InitNonEmptyAccount","msg":"Wrapper underlying tokens account must be empty."},{"code":301,"name":"InitWrapperSupplyNonZero","msg":"Supply of the wrapper mint is non-zero"},{"code":302,"name":"InitWrapperUnderlyingOwnerMismatch","msg":"Owner of the wrapper underlying tokens account must be the wrapper"},{"code":303,"name":"InitWrapperUnderlyingMintMismatch","msg":"Underlying mint does not match underlying tokens account mint"},{"code":304,"name":"InitMintAuthorityMismatch","msg":"Mint authority mismatch"},{"code":305,"name":"InitMultiplierOverflow","msg":"Initial decimals too high"},{"code":306,"name":"InitWrapperDecimalsTooLow","msg":"The number of target decimals must be greater than or equal to the underlying asset's decimals."},{"code":307,"name":"MintAmountOverflow","msg":"Mint amount overflow. This error happens when the token cannot support this many decimals added to the token."},{"code":308,"name":"InvalidBurnAmount","msg":"Failed to convert burn amount from withdraw amount."},{"code":309,"name":"InvalidWithdrawAmount","msg":"Failed to convert withdraw amount from wrapped amount."},{"code":310,"name":"InsufficientUnderlyingBalance","msg":"User does not have enough underlying tokens"},{"code":311,"name":"InsufficientWrappedBalance","msg":"User does not have enough wrapped tokens"},{"code":312,"name":"ZeroAmount","msg":"Cannot send zero tokens"},{"code":313,"name":"UnknownAction","msg":"Unknown router action"}]} \ No newline at end of file diff --git a/idls/squads.SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf.json b/idls/squads.SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf.json new file mode 100644 index 0000000..92bcbac --- /dev/null +++ b/idls/squads.SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf.json @@ -0,0 +1 @@ +{"version":"2.0.0","name":"squads_multisig_program","instructions":[{"name":"programConfigInit","docs":["Initialize the program config."],"accounts":[{"name":"programConfig","isMut":true,"isSigner":false},{"name":"initializer","isMut":true,"isSigner":true,"docs":["The hard-coded account that is used to initialize the program config once."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ProgramConfigInitArgs"}}]},{"name":"programConfigSetAuthority","docs":["Set the `authority` parameter of the program config."],"accounts":[{"name":"programConfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"ProgramConfigSetAuthorityArgs"}}]},{"name":"programConfigSetMultisigCreationFee","docs":["Set the `multisig_creation_fee` parameter of the program config."],"accounts":[{"name":"programConfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"ProgramConfigSetMultisigCreationFeeArgs"}}]},{"name":"programConfigSetTreasury","docs":["Set the `treasury` parameter of the program config."],"accounts":[{"name":"programConfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"ProgramConfigSetTreasuryArgs"}}]},{"name":"multisigCreate","docs":["Create a multisig."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"createKey","isMut":false,"isSigner":true,"docs":["An ephemeral signer that is used as a seed for the Multisig PDA.","Must be a signer to prevent front-running attack by someone else but the original creator."]},{"name":"creator","isMut":true,"isSigner":true,"docs":["The creator of the multisig."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MultisigCreateArgs"}}]},{"name":"multisigCreateV2","docs":["Create a multisig."],"accounts":[{"name":"programConfig","isMut":false,"isSigner":false,"docs":["Global program config account."]},{"name":"treasury","isMut":true,"isSigner":false,"docs":["The treasury where the creation fee is transferred to."]},{"name":"multisig","isMut":true,"isSigner":false},{"name":"createKey","isMut":false,"isSigner":true,"docs":["An ephemeral signer that is used as a seed for the Multisig PDA.","Must be a signer to prevent front-running attack by someone else but the original creator."]},{"name":"creator","isMut":true,"isSigner":true,"docs":["The creator of the multisig."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MultisigCreateArgsV2"}}]},{"name":"multisigAddMember","docs":["Add a new member to the controlled multisig."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged or credited in case the multisig account needs to reallocate space,","for example when adding a new member or a spending limit.","This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[{"name":"args","type":{"defined":"MultisigAddMemberArgs"}}]},{"name":"multisigRemoveMember","docs":["Remove a member/key from the controlled multisig."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged or credited in case the multisig account needs to reallocate space,","for example when adding a new member or a spending limit.","This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[{"name":"args","type":{"defined":"MultisigRemoveMemberArgs"}}]},{"name":"multisigSetTimeLock","docs":["Set the `time_lock` config parameter for the controlled multisig."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged or credited in case the multisig account needs to reallocate space,","for example when adding a new member or a spending limit.","This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[{"name":"args","type":{"defined":"MultisigSetTimeLockArgs"}}]},{"name":"multisigChangeThreshold","docs":["Set the `threshold` config parameter for the controlled multisig."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged or credited in case the multisig account needs to reallocate space,","for example when adding a new member or a spending limit.","This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[{"name":"args","type":{"defined":"MultisigChangeThresholdArgs"}}]},{"name":"multisigSetConfigAuthority","docs":["Set the multisig `config_authority`."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged or credited in case the multisig account needs to reallocate space,","for example when adding a new member or a spending limit.","This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[{"name":"args","type":{"defined":"MultisigSetConfigAuthorityArgs"}}]},{"name":"multisigSetRentCollector","docs":["Set the multisig `rent_collector`."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged or credited in case the multisig account needs to reallocate space,","for example when adding a new member or a spending limit.","This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[{"name":"args","type":{"defined":"MultisigSetRentCollectorArgs"}}]},{"name":"multisigAddSpendingLimit","docs":["Create a new spending limit for the controlled multisig."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"spendingLimit","isMut":true,"isSigner":false},{"name":"rentPayer","isMut":true,"isSigner":true,"docs":["This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MultisigAddSpendingLimitArgs"}}]},{"name":"multisigRemoveSpendingLimit","docs":["Remove the spending limit from the controlled multisig."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"spendingLimit","isMut":true,"isSigner":false},{"name":"rentCollector","isMut":true,"isSigner":false,"docs":["This is usually the same as `config_authority`, but can be a different account if needed."]}],"args":[{"name":"args","type":{"defined":"MultisigRemoveSpendingLimitArgs"}}]},{"name":"configTransactionCreate","docs":["Create a new config transaction."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false},{"name":"creator","isMut":false,"isSigner":true,"docs":["The member of the multisig that is creating the transaction."]},{"name":"rentPayer","isMut":true,"isSigner":true,"docs":["The payer for the transaction account rent."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ConfigTransactionCreateArgs"}}]},{"name":"configTransactionExecute","docs":["Execute a config transaction.","The transaction must be `Approved`."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false,"docs":["The multisig account that owns the transaction."]},{"name":"member","isMut":false,"isSigner":true,"docs":["One of the multisig members with `Execute` permission."]},{"name":"proposal","isMut":true,"isSigner":false,"docs":["The proposal account associated with the transaction."]},{"name":"transaction","isMut":false,"isSigner":false,"docs":["The transaction to execute."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged/credited in case the config transaction causes space reallocation,","for example when adding a new member, adding or removing a spending limit.","This is usually the same as `member`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[]},{"name":"vaultTransactionCreate","docs":["Create a new vault transaction."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false},{"name":"creator","isMut":false,"isSigner":true,"docs":["The member of the multisig that is creating the transaction."]},{"name":"rentPayer","isMut":true,"isSigner":true,"docs":["The payer for the transaction account rent."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"VaultTransactionCreateArgs"}}]},{"name":"vaultTransactionExecute","docs":["Execute a vault transaction.","The transaction must be `Approved`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false,"docs":["The proposal account associated with the transaction."]},{"name":"transaction","isMut":false,"isSigner":false,"docs":["The transaction to execute."]},{"name":"member","isMut":false,"isSigner":true}],"args":[]},{"name":"batchCreate","docs":["Create a new batch."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"batch","isMut":true,"isSigner":false},{"name":"creator","isMut":false,"isSigner":true,"docs":["The member of the multisig that is creating the batch."]},{"name":"rentPayer","isMut":true,"isSigner":true,"docs":["The payer for the batch account rent."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"BatchCreateArgs"}}]},{"name":"batchAddTransaction","docs":["Add a transaction to the batch."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false,"docs":["Multisig account this batch belongs to."]},{"name":"proposal","isMut":false,"isSigner":false,"docs":["The proposal account associated with the batch."]},{"name":"batch","isMut":true,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false,"docs":["`VaultBatchTransaction` account to initialize and add to the `batch`."]},{"name":"member","isMut":false,"isSigner":true,"docs":["Member of the multisig."]},{"name":"rentPayer","isMut":true,"isSigner":true,"docs":["The payer for the batch transaction account rent."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"BatchAddTransactionArgs"}}]},{"name":"batchExecuteTransaction","docs":["Execute a transaction from the batch."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false,"docs":["Multisig account this batch belongs to."]},{"name":"member","isMut":false,"isSigner":true,"docs":["Member of the multisig."]},{"name":"proposal","isMut":true,"isSigner":false,"docs":["The proposal account associated with the batch.","If `transaction` is the last in the batch, the `proposal` status will be set to `Executed`."]},{"name":"batch","isMut":true,"isSigner":false},{"name":"transaction","isMut":false,"isSigner":false,"docs":["Batch transaction to execute."]}],"args":[]},{"name":"proposalCreate","docs":["Create a new multisig proposal."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false},{"name":"creator","isMut":false,"isSigner":true,"docs":["The member of the multisig that is creating the proposal."]},{"name":"rentPayer","isMut":true,"isSigner":true,"docs":["The payer for the proposal account rent."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ProposalCreateArgs"}}]},{"name":"proposalActivate","docs":["Update status of a multisig proposal from `Draft` to `Active`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"member","isMut":true,"isSigner":true},{"name":"proposal","isMut":true,"isSigner":false}],"args":[]},{"name":"proposalApprove","docs":["Approve a multisig proposal on behalf of the `member`.","The proposal must be `Active`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"member","isMut":true,"isSigner":true},{"name":"proposal","isMut":true,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ProposalVoteArgs"}}]},{"name":"proposalReject","docs":["Reject a multisig proposal on behalf of the `member`.","The proposal must be `Active`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"member","isMut":true,"isSigner":true},{"name":"proposal","isMut":true,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ProposalVoteArgs"}}]},{"name":"proposalCancel","docs":["Cancel a multisig proposal on behalf of the `member`.","The proposal must be `Approved`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"member","isMut":true,"isSigner":true},{"name":"proposal","isMut":true,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ProposalVoteArgs"}}]},{"name":"spendingLimitUse","docs":["Use a spending limit to transfer tokens from a multisig vault to a destination account."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false,"docs":["The multisig account the `spending_limit` is for."]},{"name":"member","isMut":false,"isSigner":true},{"name":"spendingLimit","isMut":true,"isSigner":false,"docs":["The SpendingLimit account to use."]},{"name":"vault","isMut":true,"isSigner":false,"docs":["Multisig vault account to transfer tokens from."]},{"name":"destination","isMut":true,"isSigner":false,"docs":["Destination account to transfer tokens to."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["In case `spending_limit.mint` is SOL."]},{"name":"mint","isMut":false,"isSigner":false,"isOptional":true,"docs":["The mint of the tokens to transfer in case `spending_limit.mint` is an SPL token."]},{"name":"vaultTokenAccount","isMut":true,"isSigner":false,"isOptional":true,"docs":["Multisig vault token account to transfer tokens from in case `spending_limit.mint` is an SPL token."]},{"name":"destinationTokenAccount","isMut":true,"isSigner":false,"isOptional":true,"docs":["Destination token account in case `spending_limit.mint` is an SPL token."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["In case `spending_limit.mint` is an SPL token."]}],"args":[{"name":"args","type":{"defined":"SpendingLimitUseArgs"}}]},{"name":"configTransactionAccountsClose","docs":["Closes a `ConfigTransaction` and the corresponding `Proposal`.","`transaction` can be closed if either:","- the `proposal` is in a terminal state: `Executed`, `Rejected`, or `Cancelled`.","- the `proposal` is stale."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false,"docs":["ConfigTransaction corresponding to the `proposal`."]},{"name":"rentCollector","isMut":true,"isSigner":false,"docs":["The rent collector."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"vaultTransactionAccountsClose","docs":["Closes a `VaultTransaction` and the corresponding `Proposal`.","`transaction` can be closed if either:","- the `proposal` is in a terminal state: `Executed`, `Rejected`, or `Cancelled`.","- the `proposal` is stale and not `Approved`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false,"docs":["VaultTransaction corresponding to the `proposal`."]},{"name":"rentCollector","isMut":true,"isSigner":false,"docs":["The rent collector."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"vaultBatchTransactionAccountClose","docs":["Closes a `VaultBatchTransaction` belonging to the `batch` and `proposal`.","`transaction` can be closed if either:","- it's marked as executed within the `batch`;","- the `proposal` is in a terminal state: `Executed`, `Rejected`, or `Cancelled`.","- the `proposal` is stale and not `Approved`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"proposal","isMut":false,"isSigner":false},{"name":"batch","isMut":true,"isSigner":false,"docs":["`Batch` corresponding to the `proposal`."]},{"name":"transaction","isMut":true,"isSigner":false,"docs":["`VaultBatchTransaction` account to close.","The transaction must be the current last one in the batch."]},{"name":"rentCollector","isMut":true,"isSigner":false,"docs":["The rent collector."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"batchAccountsClose","docs":["Closes Batch and the corresponding Proposal accounts for proposals in terminal states:","`Executed`, `Rejected`, or `Cancelled` or stale proposals that aren't `Approved`.","","This instruction is only allowed to be executed when all `VaultBatchTransaction` accounts","in the `batch` are already closed: `batch.size == 0`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false},{"name":"batch","isMut":true,"isSigner":false,"docs":["`Batch` corresponding to the `proposal`."]},{"name":"rentCollector","isMut":true,"isSigner":false,"docs":["The rent collector."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"Batch","docs":["Stores data required for serial execution of a batch of multisig vault transactions.","Vault transaction is a transaction that's executed on behalf of the multisig vault PDA","and wraps arbitrary Solana instructions, typically calling into other Solana programs.","The transactions themselves are stored in separate PDAs associated with the this account."],"type":{"kind":"struct","fields":[{"name":"multisig","docs":["The multisig this belongs to."],"type":"publicKey"},{"name":"creator","docs":["Member of the Multisig who submitted the batch."],"type":"publicKey"},{"name":"index","docs":["Index of this batch within the multisig transactions."],"type":"u64"},{"name":"bump","docs":["PDA bump."],"type":"u8"},{"name":"vaultIndex","docs":["Index of the vault this batch belongs to."],"type":"u8"},{"name":"vaultBump","docs":["Derivation bump of the vault PDA this batch belongs to."],"type":"u8"},{"name":"size","docs":["Number of transactions in the batch."],"type":"u32"},{"name":"executedTransactionIndex","docs":["Index of the last executed transaction within the batch.","0 means that no transactions have been executed yet."],"type":"u32"}]}},{"name":"VaultBatchTransaction","docs":["Stores data required for execution of one transaction from a batch."],"type":{"kind":"struct","fields":[{"name":"bump","docs":["PDA bump."],"type":"u8"},{"name":"ephemeralSignerBumps","docs":["Derivation bumps for additional signers.","Some transactions require multiple signers. Often these additional signers are \"ephemeral\" keypairs","that are generated on the client with a sole purpose of signing the transaction and be discarded immediately after.","When wrapping such transactions into multisig ones, we replace these \"ephemeral\" signing keypairs","with PDAs derived from the transaction's `transaction_index` and controlled by the Multisig Program;","during execution the program includes the seeds of these PDAs into the `invoke_signed` calls,","thus \"signing\" on behalf of these PDAs."],"type":"bytes"},{"name":"message","docs":["data required for executing the transaction."],"type":{"defined":"VaultTransactionMessage"}}]}},{"name":"ConfigTransaction","docs":["Stores data required for execution of a multisig configuration transaction.","Config transaction can perform a predefined set of actions on the Multisig PDA, such as adding/removing members,","changing the threshold, etc."],"type":{"kind":"struct","fields":[{"name":"multisig","docs":["The multisig this belongs to."],"type":"publicKey"},{"name":"creator","docs":["Member of the Multisig who submitted the transaction."],"type":"publicKey"},{"name":"index","docs":["Index of this transaction within the multisig."],"type":"u64"},{"name":"bump","docs":["bump for the transaction seeds."],"type":"u8"},{"name":"actions","docs":["Action to be performed on the multisig."],"type":{"vec":{"defined":"ConfigAction"}}}]}},{"name":"Multisig","type":{"kind":"struct","fields":[{"name":"createKey","docs":["Key that is used to seed the multisig PDA."],"type":"publicKey"},{"name":"configAuthority","docs":["The authority that can change the multisig config.","This is a very important parameter as this authority can change the members and threshold.","","The convention is to set this to `Pubkey::default()`.","In this case, the multisig becomes autonomous, so every config change goes through","the normal process of voting by the members.","","However, if this parameter is set to any other key, all the config changes for this multisig","will need to be signed by the `config_authority`. We call such a multisig a \"controlled multisig\"."],"type":"publicKey"},{"name":"threshold","docs":["Threshold for signatures."],"type":"u16"},{"name":"timeLock","docs":["How many seconds must pass between transaction voting settlement and execution."],"type":"u32"},{"name":"transactionIndex","docs":["Last transaction index. 0 means no transactions have been created."],"type":"u64"},{"name":"staleTransactionIndex","docs":["Last stale transaction index. All transactions up until this index are stale.","This index is updated when multisig config (members/threshold/time_lock) changes."],"type":"u64"},{"name":"rentCollector","docs":["The address where the rent for the accounts related to executed, rejected, or cancelled","transactions can be reclaimed. If set to `None`, the rent reclamation feature is turned off."],"type":{"option":"publicKey"}},{"name":"bump","docs":["Bump for the multisig PDA seed."],"type":"u8"},{"name":"members","docs":["Members of the multisig."],"type":{"vec":{"defined":"Member"}}}]}},{"name":"ProgramConfig","docs":["Global program configuration account."],"type":{"kind":"struct","fields":[{"name":"authority","docs":["The authority which can update the config."],"type":"publicKey"},{"name":"multisigCreationFee","docs":["The lamports amount charged for creating a new multisig account.","This fee is sent to the `treasury` account."],"type":"u64"},{"name":"treasury","docs":["The treasury account to send charged fees to."],"type":"publicKey"},{"name":"reserved","docs":["Reserved for future use."],"type":{"array":["u8",64]}}]}},{"name":"Proposal","docs":["Stores the data required for tracking the status of a multisig proposal.","Each `Proposal` has a 1:1 association with a transaction account, e.g. a `VaultTransaction` or a `ConfigTransaction`;","the latter can be executed only after the `Proposal` has been approved and its time lock is released."],"type":{"kind":"struct","fields":[{"name":"multisig","docs":["The multisig this belongs to."],"type":"publicKey"},{"name":"transactionIndex","docs":["Index of the multisig transaction this proposal is associated with."],"type":"u64"},{"name":"status","docs":["The status of the transaction."],"type":{"defined":"ProposalStatus"}},{"name":"bump","docs":["PDA bump."],"type":"u8"},{"name":"approved","docs":["Keys that have approved/signed."],"type":{"vec":"publicKey"}},{"name":"rejected","docs":["Keys that have rejected."],"type":{"vec":"publicKey"}},{"name":"cancelled","docs":["Keys that have cancelled (Approved only)."],"type":{"vec":"publicKey"}}]}},{"name":"SpendingLimit","type":{"kind":"struct","fields":[{"name":"multisig","docs":["The multisig this belongs to."],"type":"publicKey"},{"name":"createKey","docs":["Key that is used to seed the SpendingLimit PDA."],"type":"publicKey"},{"name":"vaultIndex","docs":["The index of the vault that the spending limit is for."],"type":"u8"},{"name":"mint","docs":["The token mint the spending limit is for.","Pubkey::default() means SOL.","use NATIVE_MINT for Wrapped SOL."],"type":"publicKey"},{"name":"amount","docs":["The amount of tokens that can be spent in a period.","This amount is in decimals of the mint,","so 1 SOL would be `1_000_000_000` and 1 USDC would be `1_000_000`."],"type":"u64"},{"name":"period","docs":["The reset period of the spending limit.","When it passes, the remaining amount is reset, unless it's `Period::OneTime`."],"type":{"defined":"Period"}},{"name":"remainingAmount","docs":["The remaining amount of tokens that can be spent in the current period.","When reaches 0, the spending limit cannot be used anymore until the period reset."],"type":"u64"},{"name":"lastReset","docs":["Unix timestamp marking the last time the spending limit was reset (or created)."],"type":"i64"},{"name":"bump","docs":["PDA bump."],"type":"u8"},{"name":"members","docs":["Members of the multisig that can use the spending limit.","In case a member is removed from the multisig, the spending limit will remain existent","(until explicitly deleted), but the removed member will not be able to use it anymore."],"type":{"vec":"publicKey"}},{"name":"destinations","docs":["The destination addresses the spending limit is allowed to sent funds to.","If empty, funds can be sent to any address."],"type":{"vec":"publicKey"}}]}},{"name":"VaultTransaction","docs":["Stores data required for tracking the voting and execution status of a vault transaction.","Vault transaction is a transaction that's executed on behalf of the multisig vault PDA","and wraps arbitrary Solana instructions, typically calling into other Solana programs."],"type":{"kind":"struct","fields":[{"name":"multisig","docs":["The multisig this belongs to."],"type":"publicKey"},{"name":"creator","docs":["Member of the Multisig who submitted the transaction."],"type":"publicKey"},{"name":"index","docs":["Index of this transaction within the multisig."],"type":"u64"},{"name":"bump","docs":["bump for the transaction seeds."],"type":"u8"},{"name":"vaultIndex","docs":["Index of the vault this transaction belongs to."],"type":"u8"},{"name":"vaultBump","docs":["Derivation bump of the vault PDA this transaction belongs to."],"type":"u8"},{"name":"ephemeralSignerBumps","docs":["Derivation bumps for additional signers.","Some transactions require multiple signers. Often these additional signers are \"ephemeral\" keypairs","that are generated on the client with a sole purpose of signing the transaction and be discarded immediately after.","When wrapping such transactions into multisig ones, we replace these \"ephemeral\" signing keypairs","with PDAs derived from the MultisigTransaction's `transaction_index` and controlled by the Multisig Program;","during execution the program includes the seeds of these PDAs into the `invoke_signed` calls,","thus \"signing\" on behalf of these PDAs."],"type":"bytes"},{"name":"message","docs":["data required for executing the transaction."],"type":{"defined":"VaultTransactionMessage"}}]}}],"types":[{"name":"BatchAddTransactionArgs","type":{"kind":"struct","fields":[{"name":"ephemeralSigners","docs":["Number of ephemeral signing PDAs required by the transaction."],"type":"u8"},{"name":"transactionMessage","type":"bytes"}]}},{"name":"BatchCreateArgs","type":{"kind":"struct","fields":[{"name":"vaultIndex","docs":["Index of the vault this transaction belongs to."],"type":"u8"},{"name":"memo","type":{"option":"string"}}]}},{"name":"ConfigTransactionCreateArgs","type":{"kind":"struct","fields":[{"name":"actions","type":{"vec":{"defined":"ConfigAction"}}},{"name":"memo","type":{"option":"string"}}]}},{"name":"MultisigAddSpendingLimitArgs","type":{"kind":"struct","fields":[{"name":"createKey","docs":["Key that is used to seed the SpendingLimit PDA."],"type":"publicKey"},{"name":"vaultIndex","docs":["The index of the vault that the spending limit is for."],"type":"u8"},{"name":"mint","docs":["The token mint the spending limit is for."],"type":"publicKey"},{"name":"amount","docs":["The amount of tokens that can be spent in a period.","This amount is in decimals of the mint,","so 1 SOL would be `1_000_000_000` and 1 USDC would be `1_000_000`."],"type":"u64"},{"name":"period","docs":["The reset period of the spending limit.","When it passes, the remaining amount is reset, unless it's `Period::OneTime`."],"type":{"defined":"Period"}},{"name":"members","docs":["Members of the multisig that can use the spending limit.","In case a member is removed from the multisig, the spending limit will remain existent","(until explicitly deleted), but the removed member will not be able to use it anymore."],"type":{"vec":"publicKey"}},{"name":"destinations","docs":["The destination addresses the spending limit is allowed to sent funds to.","If empty, funds can be sent to any address."],"type":{"vec":"publicKey"}},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigAddMemberArgs","type":{"kind":"struct","fields":[{"name":"newMember","type":{"defined":"Member"}},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigRemoveMemberArgs","type":{"kind":"struct","fields":[{"name":"oldMember","type":"publicKey"},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigChangeThresholdArgs","type":{"kind":"struct","fields":[{"name":"newThreshold","type":"u16"},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigSetTimeLockArgs","type":{"kind":"struct","fields":[{"name":"timeLock","type":"u32"},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigSetConfigAuthorityArgs","type":{"kind":"struct","fields":[{"name":"configAuthority","type":"publicKey"},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigSetRentCollectorArgs","type":{"kind":"struct","fields":[{"name":"rentCollector","type":{"option":"publicKey"}},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigCreateArgs","type":{"kind":"struct","fields":[{"name":"configAuthority","docs":["The authority that can configure the multisig: add/remove members, change the threshold, etc.","Should be set to `None` for autonomous multisigs."],"type":{"option":"publicKey"}},{"name":"threshold","docs":["The number of signatures required to execute a transaction."],"type":"u16"},{"name":"members","docs":["The members of the multisig."],"type":{"vec":{"defined":"Member"}}},{"name":"timeLock","docs":["How many seconds must pass between transaction voting, settlement, and execution."],"type":"u32"},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigCreateArgsV2","type":{"kind":"struct","fields":[{"name":"configAuthority","docs":["The authority that can configure the multisig: add/remove members, change the threshold, etc.","Should be set to `None` for autonomous multisigs."],"type":{"option":"publicKey"}},{"name":"threshold","docs":["The number of signatures required to execute a transaction."],"type":"u16"},{"name":"members","docs":["The members of the multisig."],"type":{"vec":{"defined":"Member"}}},{"name":"timeLock","docs":["How many seconds must pass between transaction voting, settlement, and execution."],"type":"u32"},{"name":"rentCollector","docs":["The address where the rent for the accounts related to executed, rejected, or cancelled","transactions can be reclaimed. If set to `None`, the rent reclamation feature is turned off."],"type":{"option":"publicKey"}},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigRemoveSpendingLimitArgs","type":{"kind":"struct","fields":[{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"ProgramConfigInitArgs","type":{"kind":"struct","fields":[{"name":"authority","docs":["The authority that can configure the program config: change the treasury, etc."],"type":"publicKey"},{"name":"multisigCreationFee","docs":["The fee that is charged for creating a new multisig."],"type":"u64"},{"name":"treasury","docs":["The treasury where the creation fee is transferred to."],"type":"publicKey"}]}},{"name":"ProgramConfigSetAuthorityArgs","type":{"kind":"struct","fields":[{"name":"newAuthority","type":"publicKey"}]}},{"name":"ProgramConfigSetMultisigCreationFeeArgs","type":{"kind":"struct","fields":[{"name":"newMultisigCreationFee","type":"u64"}]}},{"name":"ProgramConfigSetTreasuryArgs","type":{"kind":"struct","fields":[{"name":"newTreasury","type":"publicKey"}]}},{"name":"ProposalCreateArgs","type":{"kind":"struct","fields":[{"name":"transactionIndex","docs":["Index of the multisig transaction this proposal is associated with."],"type":"u64"},{"name":"draft","docs":["Whether the proposal should be initialized with status `Draft`."],"type":"bool"}]}},{"name":"ProposalVoteArgs","type":{"kind":"struct","fields":[{"name":"memo","type":{"option":"string"}}]}},{"name":"SpendingLimitUseArgs","type":{"kind":"struct","fields":[{"name":"amount","docs":["Amount of tokens to transfer."],"type":"u64"},{"name":"decimals","docs":["Decimals of the token mint. Used for double-checking against incorrect order of magnitude of `amount`."],"type":"u8"},{"name":"memo","docs":["Memo used for indexing."],"type":{"option":"string"}}]}},{"name":"VaultTransactionCreateArgs","type":{"kind":"struct","fields":[{"name":"vaultIndex","docs":["Index of the vault this transaction belongs to."],"type":"u8"},{"name":"ephemeralSigners","docs":["Number of ephemeral signing PDAs required by the transaction."],"type":"u8"},{"name":"transactionMessage","type":"bytes"},{"name":"memo","type":{"option":"string"}}]}},{"name":"TransactionMessage","docs":["Unvalidated instruction data, must be treated as untrusted."],"type":{"kind":"struct","fields":[{"name":"numSigners","docs":["The number of signer pubkeys in the account_keys vec."],"type":"u8"},{"name":"numWritableSigners","docs":["The number of writable signer pubkeys in the account_keys vec."],"type":"u8"},{"name":"numWritableNonSigners","docs":["The number of writable non-signer pubkeys in the account_keys vec."],"type":"u8"},{"name":"accountKeys","docs":["The list of unique account public keys (including program IDs) that will be used in the provided instructions."],"type":{"defined":"SmallVec"}},{"name":"instructions","docs":["The list of instructions to execute."],"type":{"defined":"SmallVec"}},{"name":"addressTableLookups","docs":["List of address table lookups used to load additional accounts","for this transaction."],"type":{"defined":"SmallVec"}}]}},{"name":"CompiledInstruction","type":{"kind":"struct","fields":[{"name":"programIdIndex","type":"u8"},{"name":"accountIndexes","docs":["Indices into the tx's `account_keys` list indicating which accounts to pass to the instruction."],"type":{"defined":"SmallVec"}},{"name":"data","docs":["Instruction data."],"type":{"defined":"SmallVec"}}]}},{"name":"MessageAddressTableLookup","docs":["Address table lookups describe an on-chain address lookup table to use","for loading more readonly and writable accounts in a single tx."],"type":{"kind":"struct","fields":[{"name":"accountKey","docs":["Address lookup table account key"],"type":"publicKey"},{"name":"writableIndexes","docs":["List of indexes used to load writable account addresses"],"type":{"defined":"SmallVec"}},{"name":"readonlyIndexes","docs":["List of indexes used to load readonly account addresses"],"type":{"defined":"SmallVec"}}]}},{"name":"Member","type":{"kind":"struct","fields":[{"name":"key","type":"publicKey"},{"name":"permissions","type":{"defined":"Permissions"}}]}},{"name":"Permissions","docs":["Bitmask for permissions."],"type":{"kind":"struct","fields":[{"name":"mask","type":"u8"}]}},{"name":"VaultTransactionMessage","type":{"kind":"struct","fields":[{"name":"numSigners","docs":["The number of signer pubkeys in the account_keys vec."],"type":"u8"},{"name":"numWritableSigners","docs":["The number of writable signer pubkeys in the account_keys vec."],"type":"u8"},{"name":"numWritableNonSigners","docs":["The number of writable non-signer pubkeys in the account_keys vec."],"type":"u8"},{"name":"accountKeys","docs":["Unique account pubkeys (including program IDs) required for execution of the tx.","The signer pubkeys appear at the beginning of the vec, with writable pubkeys first, and read-only pubkeys following.","The non-signer pubkeys follow with writable pubkeys first and read-only ones following.","Program IDs are also stored at the end of the vec along with other non-signer non-writable pubkeys:","","```plaintext","[pubkey1, pubkey2, pubkey3, pubkey4, pubkey5, pubkey6, pubkey7, pubkey8]","|---writable---| |---readonly---| |---writable---| |---readonly---|","|------------signers-------------| |----------non-singers-----------|","```"],"type":{"vec":"publicKey"}},{"name":"instructions","docs":["List of instructions making up the tx."],"type":{"vec":{"defined":"MultisigCompiledInstruction"}}},{"name":"addressTableLookups","docs":["List of address table lookups used to load additional accounts","for this transaction."],"type":{"vec":{"defined":"MultisigMessageAddressTableLookup"}}}]}},{"name":"MultisigCompiledInstruction","docs":["Concise serialization schema for instructions that make up a transaction.","Closely mimics the Solana transaction wire format."],"type":{"kind":"struct","fields":[{"name":"programIdIndex","type":"u8"},{"name":"accountIndexes","docs":["Indices into the tx's `account_keys` list indicating which accounts to pass to the instruction."],"type":"bytes"},{"name":"data","docs":["Instruction data."],"type":"bytes"}]}},{"name":"MultisigMessageAddressTableLookup","docs":["Address table lookups describe an on-chain address lookup table to use","for loading more readonly and writable accounts into a transaction."],"type":{"kind":"struct","fields":[{"name":"accountKey","docs":["Address lookup table account key."],"type":"publicKey"},{"name":"writableIndexes","docs":["List of indexes used to load writable accounts."],"type":"bytes"},{"name":"readonlyIndexes","docs":["List of indexes used to load readonly accounts."],"type":"bytes"}]}},{"name":"Vote","type":{"kind":"enum","variants":[{"name":"Approve"},{"name":"Reject"},{"name":"Cancel"}]}},{"name":"ConfigAction","type":{"kind":"enum","variants":[{"name":"AddMember","fields":[{"name":"newMember","type":{"defined":"Member"}}]},{"name":"RemoveMember","fields":[{"name":"oldMember","type":"publicKey"}]},{"name":"ChangeThreshold","fields":[{"name":"newThreshold","type":"u16"}]},{"name":"SetTimeLock","fields":[{"name":"newTimeLock","type":"u32"}]},{"name":"AddSpendingLimit","fields":[{"name":"createKey","docs":["Key that is used to seed the SpendingLimit PDA."],"type":"publicKey"},{"name":"vaultIndex","docs":["The index of the vault that the spending limit is for."],"type":"u8"},{"name":"mint","docs":["The token mint the spending limit is for."],"type":"publicKey"},{"name":"amount","docs":["The amount of tokens that can be spent in a period.","This amount is in decimals of the mint,","so 1 SOL would be `1_000_000_000` and 1 USDC would be `1_000_000`."],"type":"u64"},{"name":"period","docs":["The reset period of the spending limit.","When it passes, the remaining amount is reset, unless it's `Period::OneTime`."],"type":{"defined":"Period"}},{"name":"members","docs":["Members of the multisig that can use the spending limit.","In case a member is removed from the multisig, the spending limit will remain existent","(until explicitly deleted), but the removed member will not be able to use it anymore."],"type":{"vec":"publicKey"}},{"name":"destinations","docs":["The destination addresses the spending limit is allowed to sent funds to.","If empty, funds can be sent to any address."],"type":{"vec":"publicKey"}}]},{"name":"RemoveSpendingLimit","fields":[{"name":"spendingLimit","type":"publicKey"}]},{"name":"SetRentCollector","fields":[{"name":"newRentCollector","type":{"option":"publicKey"}}]}]}},{"name":"Permission","type":{"kind":"enum","variants":[{"name":"Initiate"},{"name":"Vote"},{"name":"Execute"}]}},{"name":"ProposalStatus","docs":["The status of a proposal.","Each variant wraps a timestamp of when the status was set."],"type":{"kind":"enum","variants":[{"name":"Draft","fields":[{"name":"timestamp","type":"i64"}]},{"name":"Active","fields":[{"name":"timestamp","type":"i64"}]},{"name":"Rejected","fields":[{"name":"timestamp","type":"i64"}]},{"name":"Approved","fields":[{"name":"timestamp","type":"i64"}]},{"name":"Executing"},{"name":"Executed","fields":[{"name":"timestamp","type":"i64"}]},{"name":"Cancelled","fields":[{"name":"timestamp","type":"i64"}]}]}},{"name":"Period","docs":["The reset period of the spending limit."],"type":{"kind":"enum","variants":[{"name":"OneTime"},{"name":"Day"},{"name":"Week"},{"name":"Month"}]}}],"errors":[{"code":6000,"name":"DuplicateMember","msg":"Found multiple members with the same pubkey"},{"code":6001,"name":"EmptyMembers","msg":"Members array is empty"},{"code":6002,"name":"TooManyMembers","msg":"Too many members, can be up to 65535"},{"code":6003,"name":"InvalidThreshold","msg":"Invalid threshold, must be between 1 and number of members with Vote permission"},{"code":6004,"name":"Unauthorized","msg":"Attempted to perform an unauthorized action"},{"code":6005,"name":"NotAMember","msg":"Provided pubkey is not a member of multisig"},{"code":6006,"name":"InvalidTransactionMessage","msg":"TransactionMessage is malformed."},{"code":6007,"name":"StaleProposal","msg":"Proposal is stale"},{"code":6008,"name":"InvalidProposalStatus","msg":"Invalid proposal status"},{"code":6009,"name":"InvalidTransactionIndex","msg":"Invalid transaction index"},{"code":6010,"name":"AlreadyApproved","msg":"Member already approved the transaction"},{"code":6011,"name":"AlreadyRejected","msg":"Member already rejected the transaction"},{"code":6012,"name":"AlreadyCancelled","msg":"Member already cancelled the transaction"},{"code":6013,"name":"InvalidNumberOfAccounts","msg":"Wrong number of accounts provided"},{"code":6014,"name":"InvalidAccount","msg":"Invalid account provided"},{"code":6015,"name":"RemoveLastMember","msg":"Cannot remove last member"},{"code":6016,"name":"NoVoters","msg":"Members don't include any voters"},{"code":6017,"name":"NoProposers","msg":"Members don't include any proposers"},{"code":6018,"name":"NoExecutors","msg":"Members don't include any executors"},{"code":6019,"name":"InvalidStaleTransactionIndex","msg":"`stale_transaction_index` must be <= `transaction_index`"},{"code":6020,"name":"NotSupportedForControlled","msg":"Instruction not supported for controlled multisig"},{"code":6021,"name":"TimeLockNotReleased","msg":"Proposal time lock has not been released"},{"code":6022,"name":"NoActions","msg":"Config transaction must have at least one action"},{"code":6023,"name":"MissingAccount","msg":"Missing account"},{"code":6024,"name":"InvalidMint","msg":"Invalid mint"},{"code":6025,"name":"InvalidDestination","msg":"Invalid destination"},{"code":6026,"name":"SpendingLimitExceeded","msg":"Spending limit exceeded"},{"code":6027,"name":"DecimalsMismatch","msg":"Decimals don't match the mint"},{"code":6028,"name":"UnknownPermission","msg":"Member has unknown permission"},{"code":6029,"name":"ProtectedAccount","msg":"Account is protected, it cannot be passed into a CPI as writable"},{"code":6030,"name":"TimeLockExceedsMaxAllowed","msg":"Time lock exceeds the maximum allowed (90 days)"},{"code":6031,"name":"IllegalAccountOwner","msg":"Account is not owned by Multisig program"},{"code":6032,"name":"RentReclamationDisabled","msg":"Rent reclamation is disabled for this multisig"},{"code":6033,"name":"InvalidRentCollector","msg":"Invalid rent collector address"},{"code":6034,"name":"ProposalForAnotherMultisig","msg":"Proposal is for another multisig"},{"code":6035,"name":"TransactionForAnotherMultisig","msg":"Transaction is for another multisig"},{"code":6036,"name":"TransactionNotMatchingProposal","msg":"Transaction doesn't match proposal"},{"code":6037,"name":"TransactionNotLastInBatch","msg":"Transaction is not last in batch"},{"code":6038,"name":"BatchNotEmpty","msg":"Batch is not empty"},{"code":6039,"name":"SpendingLimitInvalidAmount","msg":"Invalid SpendingLimit amount"}]} \ No newline at end of file diff --git a/idls/stabble_clmm.6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6.json b/idls/stabble_clmm.6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6.json new file mode 100644 index 0000000..24a105f --- /dev/null +++ b/idls/stabble_clmm.6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6.json @@ -0,0 +1 @@ +{"address":"6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6","metadata":{"name":"amm_v3","version":"0.1.0","spec":"0.1.0","description":"Anchor client and source for Raydium concentrated liquidity AMM"},"instructions":[{"name":"close_position","docs":["Close the user's position and NFT account. If the NFT mint belongs to token2022, it will also be closed and the funds returned to the NFT owner.","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[123,134,81,0,49,68,98,98],"accounts":[{"name":"nft_owner","docs":["The position nft owner"],"writable":true,"signer":true},{"name":"position_nft_mint","docs":["Mint address bound to the personal position."],"writable":true},{"name":"position_nft_account","docs":["User token account where position NFT be minted to"],"writable":true},{"name":"personal_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"system_program","docs":["System program to close the position state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Token/Token2022 program to close token/mint account"]}],"args":[]},{"name":"close_protocol_position","docs":["The CLMM protocol decides to discard the protocol position account, which can reduce users' opening costs.","After the original protocol position account is closed, the gas fee will be refunded to the user who created it.","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[201,117,152,144,85,85,108,178],"accounts":[{"name":"admin","writable":true,"signer":true,"address":"3kXrf8w8Z6EjLJU4S8dAkpRL2von8z7Eh3kJnFrmo7Z2"},{"name":"protocol_position","writable":true}],"args":[]},{"name":"collect_fund_fee","docs":["Collect the fund fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[167,138,78,149,223,194,6,126],"accounts":[{"name":"owner","docs":["Only admin or fund_owner can collect fee now"],"signer":true},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores fund_owner"]},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_protocol_fee","docs":["Collect the protocol fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[136,136,252,221,194,66,126,89],"accounts":[{"name":"owner","docs":["Only admin or config owner can collect fee now"],"signer":true},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores owner"]},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_remaining_rewards","docs":["Collect remaining reward token for reward founder","","# Arguments","","* `ctx`- The context of accounts","* `reward_index` - the index to reward info",""],"discriminator":[18,237,166,197,34,16,213,144],"accounts":[{"name":"reward_funder","docs":["The founder who init reward info previously"],"signer":true},{"name":"funder_token_account","docs":["The funder's reward token account"],"writable":true},{"name":"pool_state","docs":["Set reward for this pool"],"writable":true},{"name":"reward_token_vault","docs":["Reward vault transfer remaining token to founder token account"],"writable":true},{"name":"reward_vault_mint","docs":["The mint of reward token vault"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"create_amm_config","docs":["# Arguments","","* `ctx`- The accounts needed by instruction.","* `index` - The index of clmm config, there may be multiple config.","* `tick_spacing` - The tickspacing binding with config, cannot be changed.","* `trade_fee_rate` - Trade fee rate, can be changed.","* `protocol_fee_rate` - The rate of protocol fee within trade fee.","* `fund_fee_rate` - The rate of fund fee within trade fee.",""],"discriminator":[137,52,237,212,215,117,108,104],"accounts":[{"name":"owner","docs":["Address to be set as protocol owner."],"writable":true,"signer":true,"address":"3kXrf8w8Z6EjLJU4S8dAkpRL2von8z7Eh3kJnFrmo7Z2"},{"name":"amm_config","docs":["Initialize config state account to store protocol owner address and fee rates."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,109,109,95,99,111,110,102,105,103]},{"kind":"arg","path":"index"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"u16"},{"name":"tick_spacing","type":"u16"},{"name":"trade_fee_rate","type":"u32"},{"name":"protocol_fee_rate","type":"u32"},{"name":"fund_fee_rate","type":"u32"}]},{"name":"create_operation_account","docs":["Creates an operation account for the program","","# Arguments","","* `ctx`- The context of accounts",""],"discriminator":[63,87,148,33,109,35,8,104],"accounts":[{"name":"owner","docs":["Address to be set as operation account owner."],"writable":true,"signer":true,"address":"3kXrf8w8Z6EjLJU4S8dAkpRL2von8z7Eh3kJnFrmo7Z2"},{"name":"operation_state","docs":["Initialize operation state account to store operation owner address and white list mint."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_pool","docs":["Creates a pool for the given token pair and the initial price","","# Arguments","","* `ctx`- The context of accounts","* `sqrt_price_x64` - the initial sqrt price (amount_token_1 / amount_token_0) of the pool as a Q64.64","Note: The open_time must be smaller than the current block_timestamp on chain."],"discriminator":[233,146,209,142,207,104,64,188],"accounts":[{"name":"pool_creator","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"pool_state","docs":["Initialize an account to store the pool state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"token_mint_0"},{"kind":"account","path":"token_mint_1"}]}},{"name":"token_mint_0","docs":["Token_0 mint, the key must be smaller then token_1 mint."]},{"name":"token_mint_1","docs":["Token_1 mint"]},{"name":"token_vault_0","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_0"}]}},{"name":"token_vault_1","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_1"}]}},{"name":"observation_state","docs":["Initialize an account to store oracle observations"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"tick_array_bitmap","docs":["Initialize an account to store if a tick array is initialized."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,116,105,99,107,95,97,114,114,97,121,95,98,105,116,109,97,112,95,101,120,116,101,110,115,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"token_program_0","docs":["Spl token program or token program 2022"]},{"name":"token_program_1","docs":["Spl token program or token program 2022"]},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"sqrt_price_x64","type":"u128"},{"name":"open_time","type":"u64"}]},{"name":"create_support_mint_associated","docs":["Create support token22 mint account which can create pool and send rewards with ignoring the not support extensions."],"discriminator":[17,251,65,92,136,242,14,169],"accounts":[{"name":"owner","docs":["Address to be set as protocol owner."],"writable":true,"signer":true},{"name":"token_mint","docs":["Support token mint"]},{"name":"support_mint_associated","docs":["Initialize support mint state account to store support mint address and bump."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,117,112,112,111,114,116,95,109,105,110,116]},{"kind":"account","path":"token_mint"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"decrease_liquidity_v2","docs":["Decreases liquidity for an existing position, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The amount by which liquidity will be decreased","* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity","* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity",""],"discriminator":[58,127,188,62,79,82,196,96],"accounts":[{"name":"nft_owner","docs":["The position owner or delegated authority"],"signer":true},{"name":"nft_account","docs":["The token account for the tokenized position"]},{"name":"personal_position","docs":["Decrease liquidity for this position"],"writable":true},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"token_vault_0","docs":["Token_0 vault"],"writable":true},{"name":"token_vault_1","docs":["Token_1 vault"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"recipient_token_account_0","docs":["The destination token account for receive amount_0"],"writable":true},{"name":"recipient_token_account_1","docs":["The destination token account for receive amount_1"],"writable":true},{"name":"token_program","docs":["SPL program to transfer out tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_min","type":"u64"},{"name":"amount_1_min","type":"u64"}]},{"name":"increase_liquidity_v2","docs":["Increases liquidity for an existing position, with amount paid by `payer`, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The desired liquidity to be added, if zero, calculate liquidity base amount_0 or amount_1 according base_flag","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `base_flag` - must be specified if liquidity is zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[133,29,89,223,69,238,176,10],"accounts":[{"name":"nft_owner","docs":["Pays to mint the position"],"signer":true},{"name":"nft_account","docs":["The token account for nft"]},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"personal_position","docs":["Increase liquidity for this position"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"token_account_0","docs":["The payer's token account for token_0"],"writable":true},{"name":"token_account_1","docs":["The token account spending token_1 to mint the position"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"initialize_reward","docs":["Initialize a reward info for a given pool and reward index","","# Arguments","","* `ctx`- The context of accounts","* `reward_index` - the index to reward info","* `open_time` - reward open timestamp","* `end_time` - reward end timestamp","* `emissions_per_second_x64` - Token reward per second are earned per unit of liquidity.",""],"discriminator":[95,135,192,196,242,129,230,68],"accounts":[{"name":"reward_funder","docs":["The founder deposit reward token to vault"],"writable":true,"signer":true},{"name":"funder_token_account","writable":true},{"name":"amm_config","docs":["For check the reward_funder authority"]},{"name":"pool_state","docs":["Set reward for this pool"],"writable":true},{"name":"operation_state","docs":["load info from the account to judge reward permission"],"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"reward_token_mint","docs":["Reward mint"]},{"name":"reward_token_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,114,101,119,97,114,100,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"reward_token_mint"}]}},{"name":"reward_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"param","type":{"defined":{"name":"InitializeRewardParam"}}}]},{"name":"open_position_with_token22_nft","docs":["Creates a new position wrapped in a Token2022 NFT without relying on metadata_program and metadata_account, reduce the cost for user to create a personal position.","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which include tick low","* `tick_array_upper_start_index` - The start index of tick array which include tick upper","* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `with_metadata` - The flag indicating whether to create NFT mint metadata","* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[77,255,174,82,125,29,201,46],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address, initialize in contract"],"writable":true,"signer":true},{"name":"position_nft_account","writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position"},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to transfer for token account"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program_2022","docs":["Program to create NFT mint/token account and transfer for token22 account"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"with_metadata","type":"bool"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"set_reward_params","docs":["Reset reward param, start a new reward cycle or extend the current cycle.","","# Arguments","","* `ctx` - The context of accounts","* `reward_index` - The index of reward token in the pool.","* `emissions_per_second_x64` - The per second emission reward, when extend the current cycle,","new value can't be less than old value","* `open_time` - reward open timestamp, must be set when starting a new cycle","* `end_time` - reward end timestamp",""],"discriminator":[112,52,167,75,32,201,211,137],"accounts":[{"name":"authority","docs":["Address to be set as protocol owner. It pays to create factory state account."],"signer":true},{"name":"amm_config"},{"name":"pool_state","writable":true},{"name":"operation_state","docs":["load info from the account to judge reward permission"],"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"token_program","docs":["Token program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"reward_index","type":"u8"},{"name":"emissions_per_second_x64","type":"u128"},{"name":"open_time","type":"u64"},{"name":"end_time","type":"u64"}]},{"name":"swap_router_base_in","docs":["Swap token for as much as possible of another token across the path provided, base input","","# Arguments","","* `ctx` - The context of accounts","* `amount_in` - Token amount to be swapped in","* `amount_out_minimum` - Panic if output amount is below minimum amount. For slippage.",""],"discriminator":[69,125,115,218,245,186,242,196],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"input_token_account","docs":["The token account that pays input tokens for the swap"],"writable":true},{"name":"input_token_mint","docs":["The mint of input token"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["SPL program 2022 for token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["Memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"amount_in","type":"u64"},{"name":"amount_out_minimum","type":"u64"}]},{"name":"swap_v2","docs":["Swaps one token for as much as possible of another token across a single pool, support token program 2022","","# Arguments","","* `ctx` - The context of accounts","* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)","* `other_amount_threshold` - For slippage check","* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot","* `is_base_input` - swap base input or swap base output",""],"discriminator":[43,4,237,11,26,201,30,98],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["SPL program 2022 for token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["Memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"input_vault_mint","docs":["The mint of token vault 0"]},{"name":"output_vault_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit_x64","type":"u128"},{"name":"is_base_input","type":"bool"}]},{"name":"transfer_reward_owner","docs":["Transfer reward owner","","# Arguments","","* `ctx`- The context of accounts","* `new_owner`- new owner pubkey",""],"discriminator":[7,22,12,83,242,43,48,121],"accounts":[{"name":"authority","docs":["Address to be set as operation account owner."],"signer":true,"address":"3kXrf8w8Z6EjLJU4S8dAkpRL2von8z7Eh3kJnFrmo7Z2"},{"name":"pool_state","writable":true}],"args":[{"name":"new_owner","type":"pubkey"}]},{"name":"update_amm_config","docs":["Updates the owner of the clmm config","Must be called by the current owner or admin","","# Arguments","","* `ctx`- The context of accounts","* `trade_fee_rate`- The new trade fee rate of clmm config, be set when `param` is 0","* `protocol_fee_rate`- The new protocol fee rate of clmm config, be set when `param` is 1","* `fund_fee_rate`- The new fund fee rate of clmm config, be set when `param` is 2","* `new_owner`- The config's new owner, be set when `param` is 3","* `new_fund_owner`- The config's new fund owner, be set when `param` is 4","* `param`- The value can be 0 | 1 | 2 | 3 | 4, otherwise will report a error",""],"discriminator":[49,60,174,136,154,28,116,200],"accounts":[{"name":"owner","docs":["The clmm config owner or admin"],"signer":true,"address":"3kXrf8w8Z6EjLJU4S8dAkpRL2von8z7Eh3kJnFrmo7Z2"},{"name":"amm_config","docs":["Amm config account to be changed"],"writable":true}],"args":[{"name":"param","type":"u8"},{"name":"value","type":"u32"}]},{"name":"update_operation_account","docs":["Update the operation account","","# Arguments","","* `ctx`- The context of accounts","* `param`- The value can be 0 | 1 | 2 | 3, otherwise will report a error","* `keys`- update operation owner when the `param` is 0","remove operation owner when the `param` is 1","update whitelist mint when the `param` is 2","remove whitelist mint when the `param` is 3",""],"discriminator":[127,70,119,40,188,227,61,7],"accounts":[{"name":"owner","docs":["Address to be set as operation account owner."],"signer":true,"address":"3kXrf8w8Z6EjLJU4S8dAkpRL2von8z7Eh3kJnFrmo7Z2"},{"name":"operation_state","docs":["Initialize operation state account to store operation owner address and white list mint."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"param","type":"u8"},{"name":"keys","type":{"vec":"pubkey"}}]},{"name":"update_pool_status","docs":["Update pool status for given value","","# Arguments","","* `ctx`- The context of accounts","* `status` - The value of status",""],"discriminator":[130,87,108,6,46,224,117,123],"accounts":[{"name":"authority","signer":true,"address":"3kXrf8w8Z6EjLJU4S8dAkpRL2von8z7Eh3kJnFrmo7Z2"},{"name":"pool_state","writable":true}],"args":[{"name":"status","type":"u8"}]},{"name":"update_reward_infos","docs":["Update rewards info of the given pool, can be called for everyone","","# Arguments","","* `ctx`- The context of accounts",""],"discriminator":[163,172,224,52,11,154,106,223],"accounts":[{"name":"pool_state","docs":["The liquidity pool for which reward info to update"],"writable":true}],"args":[]}],"accounts":[{"name":"AmmConfig","discriminator":[218,244,33,104,203,203,43,111]},{"name":"ObservationState","discriminator":[122,174,197,53,129,9,165,132]},{"name":"OperationState","discriminator":[19,236,58,237,81,222,183,252]},{"name":"PersonalPositionState","discriminator":[70,111,150,126,230,15,25,117]},{"name":"PoolState","discriminator":[247,237,227,245,215,195,222,70]},{"name":"ProtocolPositionState","discriminator":[100,226,145,99,146,218,160,106]},{"name":"SupportMintAssociated","discriminator":[134,40,183,79,12,112,162,53]},{"name":"TickArrayBitmapExtension","discriminator":[60,150,36,219,97,128,139,153]},{"name":"TickArrayState","discriminator":[192,155,85,205,49,249,129,42]}],"events":[{"name":"CollectPersonalFeeEvent","discriminator":[166,174,105,192,81,161,83,105]},{"name":"CollectProtocolFeeEvent","discriminator":[206,87,17,79,45,41,213,61]},{"name":"ConfigChangeEvent","discriminator":[247,189,7,119,106,112,95,151]},{"name":"CreatePersonalPositionEvent","discriminator":[100,30,87,249,196,223,154,206]},{"name":"DecreaseLiquidityEvent","discriminator":[58,222,86,58,68,50,85,56]},{"name":"IncreaseLiquidityEvent","discriminator":[49,79,105,212,32,34,30,84]},{"name":"LiquidityCalculateEvent","discriminator":[237,112,148,230,57,84,180,162]},{"name":"LiquidityChangeEvent","discriminator":[126,240,175,206,158,88,153,107]},{"name":"PoolCreatedEvent","discriminator":[25,94,75,47,112,99,53,63]},{"name":"SwapEvent","discriminator":[64,198,205,232,38,8,113,226]},{"name":"UpdateRewardInfosEvent","discriminator":[109,127,186,78,114,65,37,236]}],"errors":[{"code":6000,"name":"LOK","msg":"LOK"},{"code":6001,"name":"NotApproved","msg":"Not approved"},{"code":6002,"name":"InvalidUpdateConfigFlag","msg":"invalid update clmm config flag"},{"code":6003,"name":"AccountLack","msg":"Account lack"},{"code":6004,"name":"ClosePositionErr","msg":"Remove liquitity, collect fees owed and reward then you can close position account"},{"code":6005,"name":"ZeroMintAmount","msg":"Minting amount should be greater than 0"},{"code":6006,"name":"InvalidTickIndex","msg":"Tick out of range"},{"code":6007,"name":"TickInvalidOrder","msg":"The lower tick must be below the upper tick"},{"code":6008,"name":"TickLowerOverflow","msg":"The tick must be greater, or equal to the minimum tick(-443636)"},{"code":6009,"name":"TickUpperOverflow","msg":"The tick must be lesser than, or equal to the maximum tick(443636)"},{"code":6010,"name":"TickAndSpacingNotMatch","msg":"tick % tick_spacing must be zero"},{"code":6011,"name":"InvalidTickArray","msg":"Invalid tick array account"},{"code":6012,"name":"InvalidTickArrayBoundary","msg":"Invalid tick array boundary"},{"code":6013,"name":"SqrtPriceLimitOverflow","msg":"Square root price limit overflow"},{"code":6014,"name":"SqrtPriceX64","msg":"sqrt_price_x64 out of range"},{"code":6015,"name":"LiquiditySubValueErr","msg":"Liquidity sub delta L must be smaller than before"},{"code":6016,"name":"LiquidityAddValueErr","msg":"Liquidity add delta L must be greater, or equal to before"},{"code":6017,"name":"InvalidLiquidity","msg":"Invalid liquidity when update position"},{"code":6018,"name":"ForbidBothZeroForSupplyLiquidity","msg":"Both token amount must not be zero while supply liquidity"},{"code":6019,"name":"LiquidityInsufficient","msg":"Liquidity insufficient"},{"code":6020,"name":"TransactionTooOld","msg":"Transaction too old"},{"code":6021,"name":"PriceSlippageCheck","msg":"Price slippage check"},{"code":6022,"name":"TooLittleOutputReceived","msg":"Too little output received"},{"code":6023,"name":"TooMuchInputPaid","msg":"Too much input paid"},{"code":6024,"name":"ZeroAmountSpecified","msg":"Swap special amount can not be zero"},{"code":6025,"name":"InvalidInputPoolVault","msg":"Input pool vault is invalid"},{"code":6026,"name":"TooSmallInputOrOutputAmount","msg":"Swap input or output amount is too small"},{"code":6027,"name":"NotEnoughTickArrayAccount","msg":"Not enought tick array account"},{"code":6028,"name":"InvalidFirstTickArrayAccount","msg":"Invalid first tick array account"},{"code":6029,"name":"InvalidRewardIndex","msg":"Invalid reward index"},{"code":6030,"name":"FullRewardInfo","msg":"The init reward token reach to the max"},{"code":6031,"name":"RewardTokenAlreadyInUse","msg":"The init reward token already in use"},{"code":6032,"name":"ExceptRewardMint","msg":"The reward tokens must contain one of pool vault mint except the last reward"},{"code":6033,"name":"InvalidRewardInitParam","msg":"Invalid reward init param"},{"code":6034,"name":"InvalidRewardDesiredAmount","msg":"Invalid collect reward desired amount"},{"code":6035,"name":"InvalidRewardInputAccountNumber","msg":"Invalid collect reward input account number"},{"code":6036,"name":"InvalidRewardPeriod","msg":"Invalid reward period"},{"code":6037,"name":"NotApproveUpdateRewardEmissiones","msg":"Modification of emissiones is allowed within 72 hours from the end of the previous cycle"},{"code":6038,"name":"UnInitializedRewardInfo","msg":"uninitialized reward info"},{"code":6039,"name":"NotSupportMint","msg":"Not support token_2022 mint extension"},{"code":6040,"name":"MissingTickArrayBitmapExtensionAccount","msg":"Missing tickarray bitmap extension account"},{"code":6041,"name":"InsufficientLiquidityForDirection","msg":"Insufficient liquidity for this direction"},{"code":6042,"name":"MaxTokenOverflow","msg":"Max token overflow"},{"code":6043,"name":"CalculateOverflow","msg":"Calculate overflow"},{"code":6044,"name":"TransferFeeCalculateNotMatch","msg":"TransferFee calculate not match"}],"types":[{"name":"AmmConfig","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"index","type":"u16"},{"name":"owner","docs":["Address of the protocol owner"],"type":"pubkey"},{"name":"protocol_fee_rate","docs":["The protocol fee"],"type":"u32"},{"name":"trade_fee_rate","docs":["The trade fee, denominated in hundredths of a bip (10^-6)"],"type":"u32"},{"name":"tick_spacing","docs":["The tick spacing"],"type":"u16"},{"name":"fund_fee_rate","docs":["The fund fee, denominated in hundredths of a bip (10^-6)"],"type":"u32"},{"name":"padding_u32","type":"u32"},{"name":"fund_owner","type":"pubkey"},{"name":"padding","type":{"array":["u64",3]}}]}},{"name":"CollectPersonalFeeEvent","docs":["Emitted when tokens are collected for a position"],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which underlying tokens were collected"],"type":"pubkey"},{"name":"recipient_token_account_0","docs":["The token account that received the collected token_0 tokens"],"type":"pubkey"},{"name":"recipient_token_account_1","docs":["The token account that received the collected token_1 tokens"],"type":"pubkey"},{"name":"amount_0","docs":["The amount of token_0 owed to the position that was collected"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_1 owed to the position that was collected"],"type":"u64"}]}},{"name":"CollectProtocolFeeEvent","docs":["Emitted when the collected protocol fees are withdrawn by the factory owner"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool whose protocol fee is collected"],"type":"pubkey"},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"type":"pubkey"},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"type":"pubkey"},{"name":"amount_0","docs":["The amount of token_0 protocol fees that is withdrawn"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_0 protocol fees that is withdrawn"],"type":"u64"}]}},{"name":"ConfigChangeEvent","docs":["Emitted when create or update a config"],"type":{"kind":"struct","fields":[{"name":"index","type":"u16"},{"name":"owner","type":"pubkey"},{"name":"protocol_fee_rate","type":"u32"},{"name":"trade_fee_rate","type":"u32"},{"name":"tick_spacing","type":"u16"},{"name":"fund_fee_rate","type":"u32"},{"name":"fund_owner","type":"pubkey"}]}},{"name":"CreatePersonalPositionEvent","docs":["Emitted when create a new position"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for which liquidity was added"],"type":"pubkey"},{"name":"personal_position","docs":["The personal position"],"type":"pubkey"},{"name":"nft_mint","docs":["The nft mint address for the position"],"type":"pubkey"},{"name":"minter","docs":["The address that create the position"],"type":"pubkey"},{"name":"nft_owner","docs":["The owner of the position and recipient of any minted liquidity"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity minted to the position range"],"type":"u128"},{"name":"deposit_amount_0","docs":["The amount of token_0 was deposit for the liquidity"],"type":"u64"},{"name":"deposit_amount_1","docs":["The amount of token_1 was deposit for the liquidity"],"type":"u64"},{"name":"deposit_amount_0_transfer_fee","docs":["The token transfer fee for deposit_amount_0"],"type":"u64"},{"name":"deposit_amount_1_transfer_fee","docs":["The token transfer fee for deposit_amount_1"],"type":"u64"}]}},{"name":"DecreaseLiquidityEvent","docs":["Emitted when liquidity is decreased."],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which liquidity was decreased"],"type":"pubkey"},{"name":"liquidity","docs":["The amount by which liquidity for the position was decreased"],"type":"u128"},{"name":"decrease_amount_0","docs":["The amount of token_0 that was paid for the decrease in liquidity"],"type":"u64"},{"name":"decrease_amount_1","docs":["The amount of token_1 that was paid for the decrease in liquidity"],"type":"u64"},{"name":"fee_amount_0","type":"u64"},{"name":"fee_amount_1","docs":["The amount of token_1 fee"],"type":"u64"},{"name":"reward_amounts","docs":["The amount of rewards"],"type":{"array":["u64",3]}},{"name":"transfer_fee_0","docs":["The amount of token_0 transfer fee"],"type":"u64"},{"name":"transfer_fee_1","docs":["The amount of token_1 transfer fee"],"type":"u64"}]}},{"name":"IncreaseLiquidityEvent","docs":["Emitted when liquidity is increased."],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which liquidity was increased"],"type":"pubkey"},{"name":"liquidity","docs":["The amount by which liquidity for the NFT position was increased"],"type":"u128"},{"name":"amount_0","docs":["The amount of token_0 that was paid for the increase in liquidity"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_1 that was paid for the increase in liquidity"],"type":"u64"},{"name":"amount_0_transfer_fee","docs":["The token transfer fee for amount_0"],"type":"u64"},{"name":"amount_1_transfer_fee","docs":["The token transfer fee for amount_1"],"type":"u64"}]}},{"name":"InitializeRewardParam","type":{"kind":"struct","fields":[{"name":"open_time","docs":["Reward open time"],"type":"u64"},{"name":"end_time","docs":["Reward end time"],"type":"u64"},{"name":"emissions_per_second_x64","docs":["Token reward per second are earned per unit of liquidity"],"type":"u128"}]}},{"name":"LiquidityCalculateEvent","docs":["Emitted when liquidity decreased or increase."],"type":{"kind":"struct","fields":[{"name":"pool_liquidity","docs":["The pool liquidity before decrease or increase"],"type":"u128"},{"name":"pool_sqrt_price_x64","docs":["The pool price when decrease or increase in liquidity"],"type":"u128"},{"name":"pool_tick","docs":["The pool tick when decrease or increase in liquidity"],"type":"i32"},{"name":"calc_amount_0","docs":["The amount of token_0 that was calculated for the decrease or increase in liquidity"],"type":"u64"},{"name":"calc_amount_1","docs":["The amount of token_1 that was calculated for the decrease or increase in liquidity"],"type":"u64"},{"name":"trade_fee_owed_0","type":"u64"},{"name":"trade_fee_owed_1","docs":["The amount of token_1 fee"],"type":"u64"},{"name":"transfer_fee_0","docs":["The amount of token_0 transfer fee without trade_fee_amount_0"],"type":"u64"},{"name":"transfer_fee_1","docs":["The amount of token_1 transfer fee without trade_fee_amount_0"],"type":"u64"}]}},{"name":"LiquidityChangeEvent","docs":["Emitted pool liquidity change when increase and decrease liquidity"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for swap"],"type":"pubkey"},{"name":"tick","docs":["The tick of the pool"],"type":"i32"},{"name":"tick_lower","docs":["The tick lower of position"],"type":"i32"},{"name":"tick_upper","docs":["The tick lower of position"],"type":"i32"},{"name":"liquidity_before","docs":["The liquidity of the pool before liquidity change"],"type":"u128"},{"name":"liquidity_after","docs":["The liquidity of the pool after liquidity change"],"type":"u128"}]}},{"name":"Observation","docs":["The element of observations in ObservationState"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"block_timestamp","docs":["The block timestamp of the observation"],"type":"u32"},{"name":"tick_cumulative","docs":["the cumulative of tick during the duration time"],"type":"i64"},{"name":"padding","docs":["padding for feature update"],"type":{"array":["u64",4]}}]}},{"name":"ObservationState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"initialized","docs":["Whether the ObservationState is initialized"],"type":"bool"},{"name":"recent_epoch","docs":["recent update epoch"],"type":"u64"},{"name":"observation_index","docs":["the most-recently updated index of the observations array"],"type":"u16"},{"name":"pool_id","docs":["belongs to which pool"],"type":"pubkey"},{"name":"observations","docs":["observation array"],"type":{"array":[{"defined":{"name":"Observation"}},100]}},{"name":"padding","docs":["padding for feature update"],"type":{"array":["u64",4]}}]}},{"name":"OperationState","docs":["Holds the current owner of the factory"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"operation_owners","docs":["Address of the operation owner"],"type":{"array":["pubkey",10]}},{"name":"whitelist_mints","docs":["The mint address of whitelist to emit reward"],"type":{"array":["pubkey",100]}}]}},{"name":"PersonalPositionState","type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":{"array":["u8",1]}},{"name":"nft_mint","docs":["Mint address of the tokenized position"],"type":"pubkey"},{"name":"pool_id","docs":["The ID of the pool with which this token is connected"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"fee_growth_inside_0_last_x64","docs":["The token_0 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"fee_growth_inside_1_last_x64","docs":["The token_1 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"token_fees_owed_0","docs":["The fees owed to the position owner in token_0, as of the last computation"],"type":"u64"},{"name":"token_fees_owed_1","docs":["The fees owed to the position owner in token_1, as of the last computation"],"type":"u64"},{"name":"reward_infos","type":{"array":[{"defined":{"name":"PositionRewardInfo"}},3]}},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u64",7]}}]}},{"name":"PoolCreatedEvent","docs":["Emitted when a pool is created and initialized with a starting price",""],"type":{"kind":"struct","fields":[{"name":"token_mint_0","docs":["The first token of the pool by address sort order"],"type":"pubkey"},{"name":"token_mint_1","docs":["The second token of the pool by address sort order"],"type":"pubkey"},{"name":"tick_spacing","docs":["The minimum number of ticks between initialized ticks"],"type":"u16"},{"name":"pool_state","docs":["The address of the created pool"],"type":"pubkey"},{"name":"sqrt_price_x64","docs":["The initial sqrt price of the pool, as a Q64.64"],"type":"u128"},{"name":"tick","docs":["The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool"],"type":"i32"},{"name":"token_vault_0","docs":["Vault of token_0"],"type":"pubkey"},{"name":"token_vault_1","docs":["Vault of token_1"],"type":"pubkey"},{"name":"amm_config","docs":["The PublicKey of the config account with fee rates etc"],"type":"pubkey"}]}},{"name":"PoolState","docs":["The pool state","","PDA of `[POOL_SEED, config, token_mint_0, token_mint_1]`",""],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":{"array":["u8",1]}},{"name":"amm_config","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"token_mint_0","docs":["Token pair of the pool, where token_mint_0 address < token_mint_1 address"],"type":"pubkey"},{"name":"token_mint_1","type":"pubkey"},{"name":"token_vault_0","docs":["Token pair vault"],"type":"pubkey"},{"name":"token_vault_1","type":"pubkey"},{"name":"observation_key","docs":["observation account key"],"type":"pubkey"},{"name":"mint_decimals_0","docs":["mint0 and mint1 decimals"],"type":"u8"},{"name":"mint_decimals_1","type":"u8"},{"name":"tick_spacing","docs":["The minimum number of ticks between initialized ticks"],"type":"u16"},{"name":"liquidity","docs":["The currently in range liquidity available to the pool."],"type":"u128"},{"name":"sqrt_price_x64","docs":["The current price of the pool as a sqrt(token_1/token_0) Q64.64 value"],"type":"u128"},{"name":"tick_current","docs":["The current tick of the pool, i.e. according to the last tick transition that was run."],"type":"i32"},{"name":"padding3","type":"u16"},{"name":"padding4","type":"u16"},{"name":"fee_growth_global_0_x64","docs":["The fee growth as a Q64.64 number, i.e. fees of token_0 and token_1 collected per","unit of liquidity for the entire life of the pool."],"type":"u128"},{"name":"fee_growth_global_1_x64","type":"u128"},{"name":"protocol_fees_token_0","docs":["The amounts of token_0 and token_1 that are owed to the protocol."],"type":"u64"},{"name":"protocol_fees_token_1","type":"u64"},{"name":"swap_in_amount_token_0","docs":["The amounts in and out of swap token_0 and token_1"],"type":"u128"},{"name":"swap_out_amount_token_1","type":"u128"},{"name":"swap_in_amount_token_1","type":"u128"},{"name":"swap_out_amount_token_0","type":"u128"},{"name":"status","docs":["Bitwise representation of the state of the pool","bit0, 1: disable open position and increase liquidity, 0: normal","bit1, 1: disable decrease liquidity, 0: normal","bit2, 1: disable collect fee, 0: normal","bit3, 1: disable collect reward, 0: normal","bit4, 1: disable swap, 0: normal"],"type":"u8"},{"name":"padding","docs":["Leave blank for future use"],"type":{"array":["u8",7]}},{"name":"reward_infos","type":{"array":[{"defined":{"name":"RewardInfo"}},3]}},{"name":"tick_array_bitmap","docs":["Packed initialized tick array state"],"type":{"array":["u64",16]}},{"name":"total_fees_token_0","docs":["except protocol_fee and fund_fee"],"type":"u64"},{"name":"total_fees_claimed_token_0","docs":["except protocol_fee and fund_fee"],"type":"u64"},{"name":"total_fees_token_1","type":"u64"},{"name":"total_fees_claimed_token_1","type":"u64"},{"name":"fund_fees_token_0","type":"u64"},{"name":"fund_fees_token_1","type":"u64"},{"name":"open_time","type":"u64"},{"name":"recent_epoch","type":"u64"},{"name":"padding1","type":{"array":["u64",24]}},{"name":"padding2","type":{"array":["u64",32]}}]}},{"name":"PositionRewardInfo","type":{"kind":"struct","fields":[{"name":"growth_inside_last_x64","type":"u128"},{"name":"reward_amount_owed","type":"u64"}]}},{"name":"ProtocolPositionState","docs":["Info stored for each user's position"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"pool_id","docs":["The ID of the pool with which this token is connected"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"fee_growth_inside_0_last_x64","docs":["The token_0 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"fee_growth_inside_1_last_x64","docs":["The token_1 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"token_fees_owed_0","docs":["The fees owed to the position owner in token_0"],"type":"u64"},{"name":"token_fees_owed_1","docs":["The fees owed to the position owner in token_1"],"type":"u64"},{"name":"reward_growth_inside","docs":["The reward growth per unit of liquidity as of the last update to liquidity"],"type":{"array":["u128",3]}},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u64",7]}}]}},{"name":"RewardInfo","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"reward_state","docs":["Reward state"],"type":"u8"},{"name":"open_time","docs":["Reward open time"],"type":"u64"},{"name":"end_time","docs":["Reward end time"],"type":"u64"},{"name":"last_update_time","docs":["Reward last update time"],"type":"u64"},{"name":"emissions_per_second_x64","docs":["Q64.64 number indicates how many tokens per second are earned per unit of liquidity."],"type":"u128"},{"name":"reward_total_emissioned","docs":["The total amount of reward emissioned"],"type":"u64"},{"name":"reward_claimed","docs":["The total amount of claimed reward"],"type":"u64"},{"name":"token_mint","docs":["Reward token mint."],"type":"pubkey"},{"name":"token_vault","docs":["Reward vault token account."],"type":"pubkey"},{"name":"authority","docs":["The owner that has permission to set reward param"],"type":"pubkey"},{"name":"reward_growth_global_x64","docs":["Q64.64 number that tracks the total tokens earned per unit of liquidity since the reward","emissions were turned on."],"type":"u128"}]}},{"name":"SupportMintAssociated","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"mint","docs":["Address of the supported token22 mint"],"type":"pubkey"},{"name":"padding","type":{"array":["u64",8]}}]}},{"name":"SwapEvent","docs":["Emitted by when a swap is performed for a pool"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for which token_0 and token_1 were swapped"],"type":"pubkey"},{"name":"sender","docs":["The address that initiated the swap call, and that received the callback"],"type":"pubkey"},{"name":"token_account_0","docs":["The payer token account in zero for one swaps, or the recipient token account","in one for zero swaps"],"type":"pubkey"},{"name":"token_account_1","docs":["The payer token account in one for zero swaps, or the recipient token account","in zero for one swaps"],"type":"pubkey"},{"name":"amount_0","docs":["The real delta amount of the token_0 of the pool or user"],"type":"u64"},{"name":"transfer_fee_0","docs":["The transfer fee charged by the withheld_amount of the token_0"],"type":"u64"},{"name":"amount_1","docs":["The real delta of the token_1 of the pool or user"],"type":"u64"},{"name":"transfer_fee_1","docs":["The transfer fee charged by the withheld_amount of the token_1"],"type":"u64"},{"name":"zero_for_one","docs":["if true, amount_0 is negtive and amount_1 is positive"],"type":"bool"},{"name":"sqrt_price_x64","docs":["The sqrt(price) of the pool after the swap, as a Q64.64"],"type":"u128"},{"name":"liquidity","docs":["The liquidity of the pool after the swap"],"type":"u128"},{"name":"tick","docs":["The log base 1.0001 of price of the pool after the swap"],"type":"i32"}]}},{"name":"TickArrayBitmapExtension","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"positive_tick_array_bitmap","docs":["Packed initialized tick array state for start_tick_index is positive"],"type":{"array":[{"array":["u64",8]},14]}},{"name":"negative_tick_array_bitmap","docs":["Packed initialized tick array state for start_tick_index is negitive"],"type":{"array":[{"array":["u64",8]},14]}}]}},{"name":"TickArrayState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"start_tick_index","type":"i32"},{"name":"ticks","type":{"array":[{"defined":{"name":"TickState"}},60]}},{"name":"initialized_tick_count","type":"u8"},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u8",107]}}]}},{"name":"TickState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"tick","type":"i32"},{"name":"liquidity_net","docs":["Amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left)"],"type":"i128"},{"name":"liquidity_gross","docs":["The total position liquidity that references this tick"],"type":"u128"},{"name":"fee_growth_outside_0_x64","docs":["Fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)","only has relative meaning, not absolute — the value depends on when the tick is initialized"],"type":"u128"},{"name":"fee_growth_outside_1_x64","type":"u128"},{"name":"reward_growths_outside_x64","type":{"array":["u128",3]}},{"name":"padding","type":{"array":["u32",13]}}]}},{"name":"UpdateRewardInfosEvent","docs":["Emitted when Reward are updated for a pool"],"type":{"kind":"struct","fields":[{"name":"reward_growth_global_x64","docs":["Reward info"],"type":{"array":["u128",3]}}]}}]} \ No newline at end of file diff --git a/idls/stabble_stable_swap.swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ.json b/idls/stabble_stable_swap.swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ.json new file mode 100644 index 0000000..77d460f --- /dev/null +++ b/idls/stabble_stable_swap.swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ.json @@ -0,0 +1 @@ +{"address":"swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ","metadata":{"name":"stable_swap","version":"1.5.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"accept_owner","discriminator":[176,23,41,28,23,111,8,4],"accounts":[{"name":"pending_owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"approve_strategy","discriminator":[7,141,162,60,71,115,26,146],"accounts":[{"name":"admin_only","accounts":[{"name":"pool","writable":true},{"name":"vault"},{"name":"admin","signer":true}]},{"name":"strategy","writable":true}],"args":[]},{"name":"change_amp_factor","discriminator":[56,238,189,35,200,157,42,66],"accounts":[{"name":"pool","writable":true},{"name":"vault"},{"name":"admin","signer":true}],"args":[{"name":"new_amp_factor","type":"u16"},{"name":"ramp_duration","type":"u32"}]},{"name":"change_max_supply","discriminator":[93,176,0,205,69,63,87,80],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[{"name":"new_max_supply","type":"u64"}]},{"name":"change_swap_fee","discriminator":[231,15,132,51,132,165,64,170],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[{"name":"new_swap_fee","type":"u64"}]},{"name":"change_swap_fee_privileged","discriminator":[155,200,14,91,105,38,220,119],"accounts":[{"name":"pool","writable":true},{"name":"vault"},{"name":"admin","signer":true}],"args":[{"name":"new_swap_fee","type":"u64"}]},{"name":"close_strategy","discriminator":[56,247,170,246,89,221,134,200],"accounts":[{"name":"owner_only","accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}]},{"name":"strategy","writable":true},{"name":"rent_collector","writable":true}],"args":[]},{"name":"create_strategy","discriminator":[152,160,107,148,245,190,127,224],"accounts":[{"name":"owner_only","accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}]},{"name":"strategy","writable":true}],"args":[{"name":"amp_min_factor","type":"u16"},{"name":"amp_max_factor","type":"u16"},{"name":"ramp_min_step","type":"u16"},{"name":"ramp_max_step","type":"u16"},{"name":"ramp_min_duration","type":"u32"},{"name":"ramp_max_duration","type":"u32"}]},{"name":"deposit","docs":["add liquidity"],"discriminator":[242,35,198,137,82,225,242,182],"accounts":[{"name":"user","signer":true},{"name":"user_pool_token","writable":true},{"name":"mint","writable":true},{"name":"pool","writable":true},{"name":"pool_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"token_program"},{"name":"token_program_2022"}],"args":[{"name":"amounts","type":{"vec":"u64"}},{"name":"minimum_amount_out","type":"u64"}]},{"name":"exec_strategy","discriminator":[249,46,55,57,31,38,61,27],"accounts":[{"name":"strategy"},{"name":"pool","writable":true}],"args":[{"name":"ramp_step","type":"u16"},{"name":"ramp_duration","type":"u32"}]},{"name":"initialize","docs":["initialize a pool"],"discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"owner","signer":true},{"name":"mint"},{"name":"pool","writable":true},{"name":"pool_authority"},{"name":"withdraw_authority"},{"name":"vault"}],"args":[{"name":"amp_factor","type":"u16"},{"name":"swap_fee","type":"u64"},{"name":"max_caps","type":{"vec":"u64"}}]},{"name":"pause","discriminator":[211,22,221,251,74,121,193,47],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"reject_owner","discriminator":[238,206,198,215,51,178,133,228],"accounts":[{"name":"pending_owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"shutdown","docs":["shutdown the zero-liquidity pool"],"discriminator":[146,204,241,213,86,21,253,211],"accounts":[{"name":"owner","writable":true},{"name":"pool","writable":true}],"args":[]},{"name":"swap","docs":["swap"],"discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"user","signer":true},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"vault_token_in","writable":true},{"name":"vault_token_out","writable":true},{"name":"beneficiary_token_out","writable":true},{"name":"pool","writable":true},{"name":"withdraw_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"vault_program"},{"name":"token_program"}],"args":[{"name":"amount_in","type":{"option":"u64"}},{"name":"minimum_amount_out","type":"u64"}]},{"name":"swap_v2","discriminator":[43,4,237,11,26,201,30,98],"accounts":[{"name":"user","signer":true},{"name":"mint_in"},{"name":"mint_out"},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"vault_token_in","writable":true},{"name":"vault_token_out","writable":true},{"name":"beneficiary_token_out","writable":true},{"name":"pool","writable":true},{"name":"withdraw_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"vault_program"},{"name":"token_program"},{"name":"token_2022_program"}],"args":[{"name":"amount_in","type":{"option":"u64"}},{"name":"minimum_amount_out","type":"u64"}]},{"name":"transfer_owner","discriminator":[245,25,221,175,106,229,225,45],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[{"name":"new_owner","type":"pubkey"}]},{"name":"unpause","discriminator":[169,144,4,38,10,141,188,255],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"withdraw","docs":["remove liquidity"],"discriminator":[183,18,70,156,148,109,161,34],"accounts":[{"name":"user","signer":true},{"name":"user_pool_token","writable":true},{"name":"mint","writable":true},{"name":"pool","writable":true},{"name":"withdraw_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"vault_program"},{"name":"token_program"},{"name":"token_program_2022"}],"args":[{"name":"amount","type":"u64"},{"name":"minimum_amounts_out","type":{"vec":"u64"}}]}],"accounts":[{"name":"Pool","discriminator":[241,154,109,4,17,177,109,188]},{"name":"Strategy","discriminator":[174,110,39,119,82,106,169,102]},{"name":"Vault","discriminator":[211,8,232,43,2,152,117,119]}],"events":[{"name":"PoolBalanceUpdatedEvent","discriminator":[172,82,114,207,27,103,211,4]},{"name":"PoolUpdatedEvent","discriminator":[128,39,94,221,230,222,127,141]}],"types":[{"name":"Pool","type":{"kind":"struct","fields":[{"name":"owner","type":"pubkey"},{"name":"vault","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"authority_bump","type":"u8"},{"name":"is_active","type":"bool"},{"name":"amp_initial_factor","type":"u16"},{"name":"amp_target_factor","type":"u16"},{"name":"ramp_start_ts","type":"i64"},{"name":"ramp_stop_ts","type":"i64"},{"name":"swap_fee","type":"u64"},{"name":"tokens","type":{"vec":{"defined":{"name":"PoolToken"}}}},{"name":"pending_owner","type":{"option":"pubkey"}},{"name":"max_supply","type":"u64"}]}},{"name":"PoolBalanceUpdatedData","type":{"kind":"struct","fields":[{"name":"balances","type":{"vec":"u64"}}]}},{"name":"PoolBalanceUpdatedEvent","type":{"kind":"struct","fields":[{"name":"pubkey","type":"pubkey"},{"name":"data","type":{"defined":{"name":"PoolBalanceUpdatedData"}}}]}},{"name":"PoolToken","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"decimals","type":"u8"},{"name":"scaling_up","type":"bool"},{"name":"scaling_factor","type":"u64"},{"name":"balance","type":"u64"}]}},{"name":"PoolUpdatedData","type":{"kind":"struct","fields":[{"name":"is_active","type":"bool"},{"name":"amp_initial_factor","type":"u16"},{"name":"amp_target_factor","type":"u16"},{"name":"ramp_start_ts","type":"i64"},{"name":"ramp_stop_ts","type":"i64"},{"name":"swap_fee","type":"u64"},{"name":"max_supply","type":"u64"}]}},{"name":"PoolUpdatedEvent","type":{"kind":"struct","fields":[{"name":"pubkey","type":"pubkey"},{"name":"data","type":{"defined":{"name":"PoolUpdatedData"}}}]}},{"name":"Strategy","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"is_active","type":"bool"},{"name":"amp_min_factor","type":"u16"},{"name":"amp_max_factor","type":"u16"},{"name":"ramp_min_step","type":"u16"},{"name":"ramp_max_step","type":"u16"},{"name":"ramp_min_duration","type":"u32"},{"name":"ramp_max_duration","type":"u32"}]}},{"name":"Vault","type":{"kind":"struct","fields":[{"name":"admin","type":"pubkey"},{"name":"withdraw_authority","docs":["PDA of pool programs seeded by vault address"],"type":"pubkey"},{"name":"withdraw_authority_bump","docs":["bump seed of withdraw_authority PDA"],"type":"u8"},{"name":"authority_bump","docs":["bump seed of vault_authority PDA"],"type":"u8"},{"name":"is_active","type":"bool"},{"name":"beneficiary","type":"pubkey"},{"name":"beneficiary_fee","type":"u64"},{"name":"pending_admin","type":{"option":"pubkey"}}]}}]} \ No newline at end of file diff --git a/idls/stabble_weighted_swap.swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW.json b/idls/stabble_weighted_swap.swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW.json new file mode 100644 index 0000000..d735135 --- /dev/null +++ b/idls/stabble_weighted_swap.swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW.json @@ -0,0 +1 @@ +{"address":"swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW","metadata":{"name":"weighted_swap","version":"1.5.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"accept_owner","discriminator":[176,23,41,28,23,111,8,4],"accounts":[{"name":"pending_owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"change_max_supply","discriminator":[93,176,0,205,69,63,87,80],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[{"name":"new_max_supply","type":"u64"}]},{"name":"change_swap_fee","discriminator":[231,15,132,51,132,165,64,170],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[{"name":"new_swap_fee","type":"u64"}]},{"name":"deposit","docs":["add liquidity"],"discriminator":[242,35,198,137,82,225,242,182],"accounts":[{"name":"user","signer":true},{"name":"user_pool_token","writable":true},{"name":"mint","writable":true},{"name":"pool","writable":true},{"name":"pool_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"token_program"},{"name":"token_program_2022"}],"args":[{"name":"amounts","type":{"vec":"u64"}},{"name":"minimum_amount_out","type":"u64"}]},{"name":"initialize","docs":["initialize a pool"],"discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"owner","signer":true},{"name":"mint"},{"name":"pool","writable":true},{"name":"pool_authority"},{"name":"withdraw_authority"},{"name":"vault"}],"args":[{"name":"swap_fee","type":"u64"},{"name":"weights","type":{"vec":"u64"}},{"name":"max_caps","type":{"vec":"u64"}}]},{"name":"pause","discriminator":[211,22,221,251,74,121,193,47],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"reject_owner","discriminator":[238,206,198,215,51,178,133,228],"accounts":[{"name":"pending_owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"shutdown","docs":["shutdown the zero-liquidity pool"],"discriminator":[146,204,241,213,86,21,253,211],"accounts":[{"name":"owner","writable":true},{"name":"pool","writable":true}],"args":[]},{"name":"swap","docs":["swap"],"discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"user","signer":true},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"vault_token_in","writable":true},{"name":"vault_token_out","writable":true},{"name":"beneficiary_token_out","writable":true},{"name":"pool","writable":true},{"name":"withdraw_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"vault_program"},{"name":"token_program"}],"args":[{"name":"amount_in","type":{"option":"u64"}},{"name":"minimum_amount_out","type":"u64"}]},{"name":"swap_v2","discriminator":[43,4,237,11,26,201,30,98],"accounts":[{"name":"user","signer":true},{"name":"mint_in"},{"name":"mint_out"},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"vault_token_in","writable":true},{"name":"vault_token_out","writable":true},{"name":"beneficiary_token_out","writable":true},{"name":"pool","writable":true},{"name":"withdraw_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"vault_program"},{"name":"token_program"},{"name":"token_2022_program"}],"args":[{"name":"amount_in","type":{"option":"u64"}},{"name":"minimum_amount_out","type":"u64"}]},{"name":"transfer_owner","discriminator":[245,25,221,175,106,229,225,45],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[{"name":"new_owner","type":"pubkey"}]},{"name":"unpause","discriminator":[169,144,4,38,10,141,188,255],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"withdraw","docs":["remove liquidity"],"discriminator":[183,18,70,156,148,109,161,34],"accounts":[{"name":"user","signer":true},{"name":"user_pool_token","writable":true},{"name":"mint","writable":true},{"name":"pool","writable":true},{"name":"withdraw_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"vault_program"},{"name":"token_program"},{"name":"token_program_2022"}],"args":[{"name":"amount","type":"u64"},{"name":"minimum_amounts_out","type":{"vec":"u64"}}]}],"accounts":[{"name":"Pool","discriminator":[241,154,109,4,17,177,109,188]},{"name":"Vault","discriminator":[211,8,232,43,2,152,117,119]}],"events":[{"name":"PoolBalanceUpdatedEvent","discriminator":[172,82,114,207,27,103,211,4]},{"name":"PoolUpdatedEvent","discriminator":[128,39,94,221,230,222,127,141]}],"types":[{"name":"Pool","type":{"kind":"struct","fields":[{"name":"owner","type":"pubkey"},{"name":"vault","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"authority_bump","type":"u8"},{"name":"is_active","type":"bool"},{"name":"invariant","type":"u64"},{"name":"swap_fee","type":"u64"},{"name":"tokens","type":{"vec":{"defined":{"name":"PoolToken"}}}},{"name":"pending_owner","type":{"option":"pubkey"}},{"name":"max_supply","type":"u64"}]}},{"name":"PoolBalanceUpdatedData","type":{"kind":"struct","fields":[{"name":"balances","type":{"vec":"u64"}}]}},{"name":"PoolBalanceUpdatedEvent","type":{"kind":"struct","fields":[{"name":"pubkey","type":"pubkey"},{"name":"data","type":{"defined":{"name":"PoolBalanceUpdatedData"}}}]}},{"name":"PoolToken","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"decimals","type":"u8"},{"name":"scaling_up","type":"bool"},{"name":"scaling_factor","type":"u64"},{"name":"balance","type":"u64"},{"name":"weight","type":"u64"}]}},{"name":"PoolUpdatedData","type":{"kind":"struct","fields":[{"name":"is_active","type":"bool"},{"name":"swap_fee","type":"u64"},{"name":"max_supply","type":"u64"}]}},{"name":"PoolUpdatedEvent","type":{"kind":"struct","fields":[{"name":"pubkey","type":"pubkey"},{"name":"data","type":{"defined":{"name":"PoolUpdatedData"}}}]}},{"name":"Vault","type":{"kind":"struct","fields":[{"name":"admin","type":"pubkey"},{"name":"withdraw_authority","docs":["PDA of pool programs seeded by vault address"],"type":"pubkey"},{"name":"withdraw_authority_bump","docs":["bump seed of withdraw_authority PDA"],"type":"u8"},{"name":"authority_bump","docs":["bump seed of vault_authority PDA"],"type":"u8"},{"name":"is_active","type":"bool"},{"name":"beneficiary","type":"pubkey"},{"name":"beneficiary_fee","type":"u64"},{"name":"pending_admin","type":{"option":"pubkey"}}]}}]} diff --git a/idls/streamflow.strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m.json b/idls/streamflow.strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m.json new file mode 100644 index 0000000..3068154 --- /dev/null +++ b/idls/streamflow.strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"timelock","instructions":[{"name":"create","accounts":[{"name":"sender","isMut":true,"isSigner":true},{"name":"senderTokens","isMut":true,"isSigner":false},{"name":"recipient","isMut":true,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":true},{"name":"escrowTokens","isMut":true,"isSigner":false},{"name":"recipientTokens","isMut":true,"isSigner":false},{"name":"streamflowTreasury","isMut":true,"isSigner":false},{"name":"streamflowTreasuryTokens","isMut":true,"isSigner":false},{"name":"withdrawor","isMut":true,"isSigner":false},{"name":"partner","isMut":true,"isSigner":false},{"name":"partnerTokens","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"feeOracle","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"timelockProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"startTime","type":"u64"},{"name":"netAmountDeposited","type":"u64"},{"name":"period","type":"u64"},{"name":"amountPerPeriod","type":"u64"},{"name":"cliff","type":"u64"},{"name":"cliffAmount","type":"u64"},{"name":"cancelableBySender","type":"bool"},{"name":"cancelableByRecipient","type":"bool"},{"name":"automaticWithdrawal","type":"bool"},{"name":"transferableBySender","type":"bool"},{"name":"transferableByRecipient","type":"bool"},{"name":"canTopup","type":"bool"},{"name":"streamName","type":{"array":["u8",64]}},{"name":"withdrawFrequency","type":"u64"},{"name":"pausable","type":{"option":"bool"}},{"name":"canUpdateRate","type":{"option":"bool"}}]},{"name":"createUnchecked","accounts":[{"name":"sender","isMut":true,"isSigner":true},{"name":"senderTokens","isMut":true,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":false},{"name":"escrowTokens","isMut":true,"isSigner":false},{"name":"withdrawor","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"feeOracle","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"timelockProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"startTime","type":"u64"},{"name":"netAmountDeposited","type":"u64"},{"name":"period","type":"u64"},{"name":"amountPerPeriod","type":"u64"},{"name":"cliff","type":"u64"},{"name":"cliffAmount","type":"u64"},{"name":"cancelableBySender","type":"bool"},{"name":"cancelableByRecipient","type":"bool"},{"name":"automaticWithdrawal","type":"bool"},{"name":"transferableBySender","type":"bool"},{"name":"transferableByRecipient","type":"bool"},{"name":"canTopup","type":"bool"},{"name":"streamName","type":{"array":["u8",64]}},{"name":"withdrawFrequency","type":"u64"},{"name":"recipient","type":"publicKey"},{"name":"partner","type":"publicKey"},{"name":"pausable","type":"bool"},{"name":"canUpdateRate","type":"bool"}]},{"name":"createUncheckedWithPayer","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"sender","isMut":true,"isSigner":true},{"name":"senderTokens","isMut":true,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":false},{"name":"escrowTokens","isMut":true,"isSigner":false},{"name":"withdrawor","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"feeOracle","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"timelockProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"startTime","type":"u64"},{"name":"netAmountDeposited","type":"u64"},{"name":"period","type":"u64"},{"name":"amountPerPeriod","type":"u64"},{"name":"cliff","type":"u64"},{"name":"cliffAmount","type":"u64"},{"name":"cancelableBySender","type":"bool"},{"name":"cancelableByRecipient","type":"bool"},{"name":"automaticWithdrawal","type":"bool"},{"name":"transferableBySender","type":"bool"},{"name":"transferableByRecipient","type":"bool"},{"name":"canTopup","type":"bool"},{"name":"streamName","type":{"array":["u8",64]}},{"name":"withdrawFrequency","type":"u64"},{"name":"recipient","type":"publicKey"},{"name":"partner","type":"publicKey"},{"name":"pausable","type":"bool"},{"name":"canUpdateRate","type":"bool"}]},{"name":"withdraw","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"recipient","isMut":true,"isSigner":false},{"name":"recipientTokens","isMut":true,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":false},{"name":"escrowTokens","isMut":true,"isSigner":false},{"name":"streamflowTreasury","isMut":true,"isSigner":false},{"name":"streamflowTreasuryTokens","isMut":true,"isSigner":false},{"name":"partner","isMut":true,"isSigner":false},{"name":"partnerTokens","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"cancel","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"sender","isMut":true,"isSigner":false},{"name":"senderTokens","isMut":true,"isSigner":false},{"name":"recipient","isMut":true,"isSigner":false},{"name":"recipientTokens","isMut":true,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":false},{"name":"escrowTokens","isMut":true,"isSigner":false},{"name":"streamflowTreasury","isMut":true,"isSigner":false},{"name":"streamflowTreasuryTokens","isMut":true,"isSigner":false},{"name":"partner","isMut":true,"isSigner":false},{"name":"partnerTokens","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"transferRecipient","accounts":[{"name":"authority","isMut":true,"isSigner":true},{"name":"newRecipient","isMut":true,"isSigner":false},{"name":"newRecipientTokens","isMut":true,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"topup","accounts":[{"name":"sender","isMut":true,"isSigner":true},{"name":"senderTokens","isMut":true,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":false},{"name":"escrowTokens","isMut":true,"isSigner":false},{"name":"streamflowTreasury","isMut":true,"isSigner":false},{"name":"streamflowTreasuryTokens","isMut":true,"isSigner":false},{"name":"withdrawor","isMut":true,"isSigner":false},{"name":"partner","isMut":true,"isSigner":false},{"name":"partnerTokens","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"pause","accounts":[{"name":"sender","isMut":false,"isSigner":true},{"name":"metadata","isMut":true,"isSigner":false}],"args":[]},{"name":"unpause","accounts":[{"name":"sender","isMut":false,"isSigner":true},{"name":"metadata","isMut":true,"isSigner":false}],"args":[]},{"name":"update","accounts":[{"name":"sender","isMut":true,"isSigner":true},{"name":"metadata","isMut":true,"isSigner":false},{"name":"withdrawor","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"enableAutomaticWithdrawal","type":{"option":"bool"}},{"name":"withdrawFrequency","type":{"option":"u64"}},{"name":"amountPerPeriod","type":{"option":"u64"}}]}]} \ No newline at end of file diff --git a/idls/tensor-cnft.TCMPhJdwDryooaGtiocG1u3xcYbRpiJzb283XfCZsDp.json b/idls/tensor-cnft.TCMPhJdwDryooaGtiocG1u3xcYbRpiJzb283XfCZsDp.json new file mode 100644 index 0000000..088d031 --- /dev/null +++ b/idls/tensor-cnft.TCMPhJdwDryooaGtiocG1u3xcYbRpiJzb283XfCZsDp.json @@ -0,0 +1 @@ +{"version":"2.0.1","name":"tcomp","constants":[{"name":"CURRENT_TCOMP_VERSION","type":"u8","value":"1"},{"name":"TCOMP_FEE_BPS","type":"u16","value":"150"},{"name":"MAX_EXPIRY_SEC","type":"i64","value":"31536000"},{"name":"MAKER_BROKER_PCT","type":"u16","value":"0"},{"name":"LIST_STATE_SIZE","type":{"defined":"usize"},"value":"8 + 1 + 1 + (32 * 2) + 8 + 33 + 8 + (33 * 2) + 128"},{"name":"BID_STATE_SIZE","type":{"defined":"usize"},"value":"8 + 1 + 1 + (32 * 2) + 1 + 32 + 2 + 33 + 4 * 2 + 8 + 33 + 8 + (33 * 3) + 128"}],"instructions":[{"name":"tcompNoop","accounts":[{"name":"tcompSigner","isMut":false,"isSigner":true}],"args":[{"name":"event","type":{"defined":"TcompEvent"}}]},{"name":"withdrawFees","accounts":[{"name":"tswap","isMut":true,"isSigner":false},{"name":"tcomp","isMut":true,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true,"docs":["We ask also for a signature just to make sure this wallet can actually sign things"]},{"name":"owner","isMut":true,"isSigner":true},{"name":"destination","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"buy","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"listState","isMut":true,"isSigner":false},{"name":"buyer","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"metaHash","type":{"array":["u8",32]}},{"name":"creatorShares","type":"bytes"},{"name":"creatorVerified","type":{"vec":"bool"}},{"name":"sellerFeeBasisPoints","type":"u16"},{"name":"maxAmount","type":"u64"},{"name":"optionalRoyaltyPct","type":{"option":"u16"}}]},{"name":"buySpl","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"tcompAta","isMut":true,"isSigner":false},{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"listState","isMut":true,"isSigner":false},{"name":"buyer","isMut":false,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"payerSource","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":false},{"name":"ownerDest","isMut":true,"isSigner":false},{"name":"currency","isMut":false,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"takerBrokerAta","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBrokerAta","isMut":true,"isSigner":false,"isOptional":true},{"name":"rentDest","isMut":true,"isSigner":false},{"name":"rentPayer","isMut":true,"isSigner":true}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"metaHash","type":{"array":["u8",32]}},{"name":"creatorShares","type":"bytes"},{"name":"creatorVerified","type":{"vec":"bool"}},{"name":"sellerFeeBasisPoints","type":"u16"},{"name":"maxAmount","type":"u64"},{"name":"optionalRoyaltyPct","type":{"option":"u16"}}]},{"name":"buyCore","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"listState","isMut":true,"isSigner":false},{"name":"asset","isMut":true,"isSigner":false},{"name":"collection","isMut":false,"isSigner":false,"isOptional":true},{"name":"buyer","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"rentDest","isMut":true,"isSigner":false},{"name":"mplCoreProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxAmount","type":"u64"}]},{"name":"list","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":false},{"name":"delegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"listState","isMut":true,"isSigner":false},{"name":"rentPayer","isMut":true,"isSigner":true}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"amount","type":"u64"},{"name":"expireInSec","type":{"option":"u64"}},{"name":"currency","type":{"option":"publicKey"}},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"makerBroker","type":{"option":"publicKey"}}]},{"name":"delist","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"listState","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}}]},{"name":"edit","accounts":[{"name":"listState","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"tcompProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"expireInSec","type":{"option":"u64"}},{"name":"currency","type":{"option":"publicKey"}},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"makerBroker","type":{"option":"publicKey"}}]},{"name":"listCore","accounts":[{"name":"asset","isMut":true,"isSigner":false},{"name":"collection","isMut":false,"isSigner":false,"isOptional":true},{"name":"listState","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"mplCoreProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true}],"args":[{"name":"amount","type":"u64"},{"name":"expireInSec","type":{"option":"u64"}},{"name":"currency","type":{"option":"publicKey"}},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"makerBroker","type":{"option":"publicKey"}}]},{"name":"delistCore","accounts":[{"name":"asset","isMut":true,"isSigner":false},{"name":"collection","isMut":false,"isSigner":false,"isOptional":true},{"name":"owner","isMut":true,"isSigner":true},{"name":"listState","isMut":true,"isSigner":false},{"name":"mplCoreProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rentDest","isMut":true,"isSigner":true}],"args":[]},{"name":"bid","accounts":[{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"rentPayer","isMut":true,"isSigner":true}],"args":[{"name":"bidId","type":"publicKey"},{"name":"target","type":{"defined":"Target"}},{"name":"targetId","type":"publicKey"},{"name":"field","type":{"option":{"defined":"Field"}}},{"name":"fieldId","type":{"option":"publicKey"}},{"name":"amount","type":"u64"},{"name":"quantity","type":"u32"},{"name":"expireInSec","type":{"option":"u64"}},{"name":"currency","type":{"option":"publicKey"}},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"makerBroker","type":{"option":"publicKey"}}]},{"name":"cancelBid","accounts":[{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[]},{"name":"closeExpiredBid","accounts":[{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[]},{"name":"closeExpiredListing","accounts":[{"name":"listState","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}}]},{"name":"closeExpiredListingCore","accounts":[{"name":"listState","isMut":true,"isSigner":false},{"name":"asset","isMut":true,"isSigner":false},{"name":"collection","isMut":false,"isSigner":false,"isOptional":true},{"name":"owner","isMut":false,"isSigner":false},{"name":"mplCoreProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[]},{"name":"takeBidMetaHash","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"seller","isMut":true,"isSigner":false},{"name":"delegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tensorswapProgram","isMut":false,"isSigner":false},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"whitelist","isMut":false,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"metaHash","type":{"array":["u8",32]}},{"name":"creatorShares","type":"bytes"},{"name":"creatorVerified","type":{"vec":"bool"}},{"name":"sellerFeeBasisPoints","type":"u16"},{"name":"minAmount","type":"u64"},{"name":"optionalRoyaltyPct","type":{"option":"u16"}}]},{"name":"takeBidFullMeta","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"seller","isMut":true,"isSigner":false},{"name":"delegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tensorswapProgram","isMut":false,"isSigner":false},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"whitelist","isMut":false,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"metaArgs","type":{"defined":"TMetadataArgs"}},{"name":"minAmount","type":"u64"},{"name":"optionalRoyaltyPct","type":{"option":"u16"}}]},{"name":"takeBidLegacy","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"seller","isMut":true,"isSigner":true},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"whitelist","isMut":false,"isSigner":false},{"name":"nftSellerAcc","isMut":true,"isSigner":false},{"name":"nftMint","isMut":false,"isSigner":false},{"name":"nftMetadata","isMut":true,"isSigner":false},{"name":"ownerAtaAcc","isMut":true,"isSigner":false},{"name":"nftEdition","isMut":false,"isSigner":false},{"name":"ownerTokenRecord","isMut":true,"isSigner":false},{"name":"destTokenRecord","isMut":true,"isSigner":false},{"name":"pnftShared","accounts":[{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"instructions","isMut":false,"isSigner":false},{"name":"authorizationRulesProgram","isMut":false,"isSigner":false}]},{"name":"nftEscrow","isMut":true,"isSigner":false,"docs":["Implicitly checked via transfer. Will fail if wrong account"]},{"name":"tempEscrowTokenRecord","isMut":true,"isSigner":false},{"name":"authRules","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tensorswapProgram","isMut":false,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"mintProof","isMut":false,"isSigner":false,"docs":["intentionally not deserializing, it would be dummy in the case of VOC/FVC based verification"]},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"minAmount","type":"u64"},{"name":"optionalRoyaltyPct","type":{"option":"u16"}},{"name":"rulesAccPresent","type":"bool"},{"name":"authorizationData","type":{"option":{"defined":"AuthorizationDataLocal"}}}]},{"name":"takeBidT22","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"seller","isMut":true,"isSigner":true},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"whitelist","isMut":false,"isSigner":false},{"name":"nftSellerAcc","isMut":true,"isSigner":false},{"name":"nftMint","isMut":false,"isSigner":false},{"name":"ownerAtaAcc","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tensorswapProgram","isMut":false,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"mintProof","isMut":false,"isSigner":false,"docs":["intentionally not deserializing, it would be dummy in the case of VOC/FVC based verification"]},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"minAmount","type":"u64"}]},{"name":"takeBidWns","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"seller","isMut":true,"isSigner":true},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"whitelist","isMut":false,"isSigner":false},{"name":"nftSellerAcc","isMut":true,"isSigner":false},{"name":"nftMint","isMut":false,"isSigner":false},{"name":"ownerAtaAcc","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tensorswapProgram","isMut":false,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"mintProof","isMut":false,"isSigner":false,"docs":["intentionally not deserializing, it would be dummy in the case of VOC/FVC based verification"]},{"name":"rentDest","isMut":true,"isSigner":false},{"name":"approveAccount","isMut":true,"isSigner":false},{"name":"distribution","isMut":true,"isSigner":false},{"name":"wnsProgram","isMut":false,"isSigner":false},{"name":"distributionProgram","isMut":false,"isSigner":false},{"name":"extraMetas","isMut":false,"isSigner":false}],"args":[{"name":"minAmount","type":"u64"}]},{"name":"takeBidCore","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"seller","isMut":true,"isSigner":true},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"whitelist","isMut":false,"isSigner":false},{"name":"asset","isMut":true,"isSigner":false},{"name":"collection","isMut":false,"isSigner":false,"isOptional":true},{"name":"mplCoreProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tensorswapProgram","isMut":false,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"mintProof","isMut":false,"isSigner":false,"docs":["intentionally not deserializing, it would be dummy in the case of VOC/FVC based verification"]},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"minAmount","type":"u64"}]}],"accounts":[{"name":"ListState","type":{"kind":"struct","fields":[{"name":"version","type":"u8"},{"name":"bump","type":{"array":["u8",1]}},{"name":"owner","type":"publicKey"},{"name":"assetId","type":"publicKey"},{"name":"amount","type":"u64"},{"name":"currency","type":{"option":"publicKey"}},{"name":"expiry","type":"i64"},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"makerBroker","type":{"option":"publicKey"}},{"name":"rentPayer","docs":["owner is the rent payer when this is PublicKey::default"],"type":"publicKey"},{"name":"reserved","type":{"array":["u8",32]}},{"name":"reserved1","type":{"array":["u8",64]}}]}},{"name":"BidState","type":{"kind":"struct","fields":[{"name":"version","type":"u8"},{"name":"bump","type":{"array":["u8",1]}},{"name":"owner","type":"publicKey"},{"name":"bidId","docs":["Randomly picked pubkey used in bid seeds. To avoid dangling bids can use assetId here."],"type":"publicKey"},{"name":"target","type":{"defined":"Target"}},{"name":"targetId","type":"publicKey"},{"name":"field","type":{"option":{"defined":"Field"}}},{"name":"fieldId","type":{"option":"publicKey"}},{"name":"quantity","type":"u32"},{"name":"filledQuantity","type":"u32"},{"name":"amount","type":"u64"},{"name":"currency","type":{"option":"publicKey"}},{"name":"expiry","type":"i64"},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"makerBroker","type":{"option":"publicKey"}},{"name":"margin","type":{"option":"publicKey"}},{"name":"updatedAt","type":"i64"},{"name":"cosigner","type":"publicKey"},{"name":"rentPayer","docs":["owner is the rent payer when this is PublicKey::default"],"type":"publicKey"},{"name":"reserved","type":{"array":["u8",8]}},{"name":"reserved1","type":{"array":["u8",16]}},{"name":"reserved2","type":{"array":["u8",32]}}]}}],"types":[{"name":"TUses","type":{"kind":"struct","fields":[{"name":"useMethod","type":{"defined":"TUseMethod"}},{"name":"remaining","type":"u64"},{"name":"total","type":"u64"}]}},{"name":"TCollection","type":{"kind":"struct","fields":[{"name":"verified","type":"bool"},{"name":"key","type":"publicKey"}]}},{"name":"TMetadataArgs","type":{"kind":"struct","fields":[{"name":"name","docs":["The name of the asset"],"type":"string"},{"name":"symbol","docs":["The symbol for the asset"],"type":"string"},{"name":"uri","docs":["URI pointing to JSON representing the asset"],"type":"string"},{"name":"sellerFeeBasisPoints","docs":["Royalty basis points that goes to creators in secondary sales (0-10000)"],"type":"u16"},{"name":"primarySaleHappened","type":"bool"},{"name":"isMutable","type":"bool"},{"name":"editionNonce","docs":["nonce for easy calculation of editions, if present"],"type":{"option":"u8"}},{"name":"tokenStandard","docs":["Since we cannot easily change Metadata, we add the new DataV2 fields here at the end."],"type":{"option":{"defined":"TTokenStandard"}}},{"name":"collection","docs":["Collection"],"type":{"option":{"defined":"TCollection"}}},{"name":"uses","docs":["Uses"],"type":{"option":{"defined":"TUses"}}},{"name":"tokenProgramVersion","type":{"defined":"TTokenProgramVersion"}},{"name":"creatorShares","type":"bytes"},{"name":"creatorVerified","type":{"vec":"bool"}}]}},{"name":"MakeEvent","type":{"kind":"struct","fields":[{"name":"maker","type":"publicKey"},{"name":"bidId","type":{"option":"publicKey"}},{"name":"target","type":{"defined":"Target"}},{"name":"targetId","type":"publicKey"},{"name":"field","type":{"option":{"defined":"Field"}}},{"name":"fieldId","type":{"option":"publicKey"}},{"name":"amount","type":"u64"},{"name":"quantity","type":"u32"},{"name":"currency","type":{"option":"publicKey"}},{"name":"expiry","type":"i64"},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"assetId","type":{"option":"publicKey"}}]}},{"name":"TakeEvent","type":{"kind":"struct","fields":[{"name":"taker","type":"publicKey"},{"name":"bidId","type":{"option":"publicKey"}},{"name":"target","type":{"defined":"Target"}},{"name":"targetId","type":"publicKey"},{"name":"field","type":{"option":{"defined":"Field"}}},{"name":"fieldId","type":{"option":"publicKey"}},{"name":"amount","type":"u64"},{"name":"quantity","type":"u32"},{"name":"tcompFee","type":"u64"},{"name":"takerBrokerFee","type":"u64"},{"name":"makerBrokerFee","type":"u64"},{"name":"creatorFee","type":"u64"},{"name":"currency","type":{"option":"publicKey"}},{"name":"assetId","type":{"option":"publicKey"}}]}},{"name":"AuthorizationDataLocal","type":{"kind":"struct","fields":[{"name":"payload","type":{"vec":{"defined":"TaggedPayload"}}}]}},{"name":"TaggedPayload","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"payload","type":{"defined":"PayloadTypeLocal"}}]}},{"name":"SeedsVecLocal","type":{"kind":"struct","fields":[{"name":"seeds","docs":["The vector of derivation seeds."],"type":{"vec":"bytes"}}]}},{"name":"ProofInfoLocal","type":{"kind":"struct","fields":[{"name":"proof","docs":["The merkle proof."],"type":{"vec":{"array":["u8",32]}}}]}},{"name":"TTokenProgramVersion","type":{"kind":"enum","variants":[{"name":"Original"},{"name":"Token2022"}]}},{"name":"TTokenStandard","type":{"kind":"enum","variants":[{"name":"NonFungible"},{"name":"FungibleAsset"},{"name":"Fungible"},{"name":"NonFungibleEdition"}]}},{"name":"TUseMethod","type":{"kind":"enum","variants":[{"name":"Burn"},{"name":"Multiple"},{"name":"Single"}]}},{"name":"TcompEvent","type":{"kind":"enum","variants":[{"name":"Maker","fields":[{"defined":"MakeEvent"}]},{"name":"Taker","fields":[{"defined":"TakeEvent"}]}]}},{"name":"PayloadTypeLocal","type":{"kind":"enum","variants":[{"name":"Pubkey","fields":["publicKey"]},{"name":"Seeds","fields":[{"defined":"SeedsVecLocal"}]},{"name":"MerkleProof","fields":[{"defined":"ProofInfoLocal"}]},{"name":"Number","fields":["u64"]}]}},{"name":"Target","type":{"kind":"enum","variants":[{"name":"AssetId"},{"name":"Whitelist"}]}},{"name":"Field","type":{"kind":"enum","variants":[{"name":"Name"}]}}],"errors":[{"code":6100,"name":"ArithmeticError","msg":"arithmetic error"},{"code":6101,"name":"ExpiryTooLarge","msg":"expiry too large"},{"code":6102,"name":"BadOwner","msg":"bad owner"},{"code":6103,"name":"BadListState","msg":"bad list state"},{"code":6104,"name":"BadRoyaltiesPct","msg":"royalties pct must be between 0 and 100"},{"code":6105,"name":"PriceMismatch","msg":"price mismatch"},{"code":6106,"name":"CreatorMismatch","msg":"creator mismatch"},{"code":6107,"name":"InsufficientBalance","msg":"insufficient balance"},{"code":6108,"name":"BidExpired","msg":"bid has expired"},{"code":6109,"name":"TakerNotAllowed","msg":"taker not allowed"},{"code":6110,"name":"BadBidField","msg":"cannot pass bid field"},{"code":6111,"name":"BidNotYetExpired","msg":"bid not yet expired"},{"code":6112,"name":"BadMargin","msg":"bad margin"},{"code":6113,"name":"WrongIxForBidTarget","msg":"wrong ix for bid target called"},{"code":6114,"name":"WrongTargetId","msg":"wrong target id"},{"code":6115,"name":"MissingFvc","msg":"creator array missing first verified creator"},{"code":6116,"name":"MissingCollection","msg":"metadata missing collection"},{"code":6117,"name":"CannotModifyTarget","msg":"cannot modify bid target, create a new bid"},{"code":6118,"name":"TargetIdMustEqualBidId","msg":"target id and bid id must be the same for single bids"},{"code":6119,"name":"CurrencyNotYetEnabled","msg":"currency not yet enabled"},{"code":6120,"name":"MakerBrokerNotYetEnabled","msg":"maker broker not yet enabled"},{"code":6121,"name":"OptionalRoyaltiesNotYetEnabled","msg":"optional royalties not yet enabled"},{"code":6122,"name":"WrongStateVersion","msg":"wrong state version"},{"code":6123,"name":"WrongBidFieldId","msg":"wrong field id"},{"code":6124,"name":"BrokerMismatch","msg":"broker mismatch"},{"code":6125,"name":"AssetIdMismatch","msg":"asset id mismatch"},{"code":6126,"name":"ListingExpired","msg":"listing has expired"},{"code":6127,"name":"ListingNotYetExpired","msg":"listing not yet expired"},{"code":6128,"name":"BadQuantity","msg":"bad quantity passed in"},{"code":6129,"name":"BidFullyFilled","msg":"bid fully filled"},{"code":6130,"name":"BadWhitelist","msg":"bad whitelist"},{"code":6131,"name":"ForbiddenCollection","msg":"forbidden collection"},{"code":6132,"name":"BadCosigner","msg":"bad cosigner"},{"code":6133,"name":"BadMintProof","msg":"bad mint proof"},{"code":6134,"name":"CurrencyMismatch","msg":"Currency mismatch"},{"code":6135,"name":"BidBalanceNotEmptied","msg":"The bid balance was not emptied"},{"code":6136,"name":"BadRentDest","msg":"Bad rent dest."},{"code":6137,"name":"CurrencyNotYetWhitelisted","msg":"currency not yet whitelisted"},{"code":6138,"name":"MakerBrokerNotYetWhitelisted","msg":"maker broker not yet whitelisted"},{"code":6139,"name":"WrongTokenRecordDerivation","msg":"token record derivation is wrong"}]} diff --git a/idls/vertigo.vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ.json b/idls/vertigo.vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ.json new file mode 100644 index 0000000..4998453 --- /dev/null +++ b/idls/vertigo.vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ.json @@ -0,0 +1 @@ +{"address":"vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ","metadata":{"name":"amm","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"buy","discriminator":[102,6,61,18,1,218,235,234],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"owner"},{"kind":"account","path":"mint_a"},{"kind":"account","path":"mint_b"}]}},{"name":"user","signer":true},{"name":"owner"},{"name":"mint_a"},{"name":"mint_b"},{"name":"user_ta_a","docs":["Can be any token account owned by the user for this mint"],"writable":true},{"name":"user_ta_b","docs":["Can be any token account owned by the user for this mint"],"writable":true},{"name":"vault_a","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"mint_a"}]}},{"name":"vault_b","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"mint_b"}]}},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"program","address":"vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ"}],"args":[{"name":"params","type":{"defined":{"name":"SwapParams"}}}]},{"name":"claim","discriminator":[62,198,214,193,213,159,108,210],"accounts":[{"name":"pool","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"claimer","signer":true},{"name":"mint_a","docs":["The token mint of the tokens used in the pool."]},{"name":"vault_a","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"mint_a"}]}},{"name":"receiver_ta_a","docs":["Can be any token account owned by the user for this mint"],"writable":true},{"name":"token_program_a"}],"args":[]},{"name":"create","discriminator":[24,30,200,40,5,28,7,119],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"owner","signer":true},{"name":"token_wallet_authority","signer":true},{"name":"mint_a"},{"name":"mint_b"},{"name":"token_wallet_b","docs":["Token wallet that funds the pool with token B."],"writable":true},{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"owner"},{"kind":"account","path":"mint_a"},{"kind":"account","path":"mint_b"}]}},{"name":"vault_a","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"mint_a"}]}},{"name":"vault_b","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"mint_b"}]}},{"name":"token_program_a","docs":["Required programs and sysvars"]},{"name":"token_program_b"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"CreateParams"}}}]},{"name":"quote_buy","discriminator":[83,9,231,110,146,31,40,12],"accounts":[{"name":"pool","pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"owner"},{"kind":"account","path":"mint_a"},{"kind":"account","path":"mint_b"}]}},{"name":"owner"},{"name":"user"},{"name":"mint_a"},{"name":"mint_b"},{"name":"program","address":"vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ"}],"args":[{"name":"params","type":{"defined":{"name":"SwapParams"}}}],"returns":{"defined":{"name":"SwapResult"}}},{"name":"quote_sell","discriminator":[5,178,49,206,140,231,131,145],"accounts":[{"name":"pool","pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"owner"},{"kind":"account","path":"mint_a"},{"kind":"account","path":"mint_b"}]}},{"name":"owner"},{"name":"user"},{"name":"mint_a"},{"name":"mint_b"},{"name":"program","address":"vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ"}],"args":[{"name":"params","type":{"defined":{"name":"SwapParams"}}}],"returns":{"defined":{"name":"SwapResult"}}},{"name":"sell","discriminator":[51,230,133,164,1,127,131,173],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"owner"},{"kind":"account","path":"mint_a"},{"kind":"account","path":"mint_b"}]}},{"name":"user","signer":true},{"name":"owner"},{"name":"mint_a"},{"name":"mint_b"},{"name":"user_ta_a","docs":["Can be any token account owned by the user for this mint"],"writable":true},{"name":"user_ta_b","docs":["Can be any token account owned by the user for this mint"],"writable":true},{"name":"vault_a","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"mint_a"}]}},{"name":"vault_b","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"mint_b"}]}},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"program","address":"vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ"}],"args":[{"name":"params","type":{"defined":{"name":"SwapParams"}}}]}],"accounts":[{"name":"Pool","discriminator":[241,154,109,4,17,177,109,188]}],"events":[{"name":"BuyEvent","discriminator":[103,244,82,31,44,245,119,119]},{"name":"PoolCreated","discriminator":[202,44,41,88,104,220,157,82]},{"name":"SellEvent","discriminator":[62,47,55,10,165,3,220,42]}],"errors":[{"code":6000,"name":"PoolDisabled","msg":"Pool Disabled"},{"code":6001,"name":"OwnerMustBeSystemAccount","msg":"Owner must be a system account (wallet), not a PDA"},{"code":6002,"name":"InvalidFeeDecay","msg":"Invalid Fee Decay"},{"code":6003,"name":"InvalidInitialTokenReserves","msg":"Initial token reserves must be greater than 0"},{"code":6004,"name":"InvalidMint","msg":"Invalid Mint"},{"code":6005,"name":"InvalidTokenAccount","msg":"Invalid Token Account"},{"code":6006,"name":"InsufficientFunds","msg":"Insufficient Funds"},{"code":6007,"name":"InvalidShift","msg":"Shift must be greater than 0"},{"code":6008,"name":"InvalidFees","msg":"Fees must be between 0 and 10000 basis points"},{"code":6009,"name":"MathOverflow","msg":"MathOverflow"},{"code":6010,"name":"InsufficientOutput","msg":"Insufficient output"},{"code":6011,"name":"InsufficientInput","msg":"Insufficient input"},{"code":6012,"name":"IllegalClaimant","msg":"Illegal Claimant"},{"code":6013,"name":"PoolEmpty","msg":"Pool Empty"}],"types":[{"name":"BuyEvent","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"amount_a","type":"u64"},{"name":"amount_b","type":"u64"},{"name":"new_reserves_a","type":"u128"},{"name":"new_reserves_b","type":"u128"},{"name":"fee_a","type":"u64"}]}},{"name":"CreateParams","type":{"kind":"struct","fields":[{"name":"shift","type":"u128"},{"name":"initial_token_b_reserves","type":"u64"},{"name":"fee_params","type":{"defined":{"name":"FeeParams"}}}]}},{"name":"FeeParams","type":{"kind":"struct","fields":[{"name":"normalization_period","docs":["The normalization period in slots.","During this period, the fee will decay from 100% to the base fee."],"type":"u64"},{"name":"decay","docs":["Normalization period fee decay rate.","Higher values decay faster."],"type":"f64"},{"name":"reference","docs":["Reference slot for the fee calculation."],"type":"u64"},{"name":"royalties_bps","docs":["Royalties in basis points."],"type":"u16"},{"name":"privileged_swapper","docs":["Number of fee exempt buys."],"type":{"option":"pubkey"}}]}},{"name":"Pool","type":{"kind":"struct","fields":[{"name":"enabled","type":"bool"},{"name":"owner","type":"pubkey"},{"name":"mint_a","type":"pubkey"},{"name":"mint_b","type":"pubkey"},{"name":"token_a_reserves","type":"u128"},{"name":"token_b_reserves","type":"u128"},{"name":"shift","type":"u128"},{"name":"royalties","type":"u64"},{"name":"vertigo_fees","type":"u64"},{"name":"bump","type":"u8"},{"name":"fee_params","type":{"defined":{"name":"FeeParams"}}}]}},{"name":"PoolCreated","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"mint_a","type":"pubkey"},{"name":"mint_b","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"initial_token_reserves","type":"u64"},{"name":"shift","type":"u128"},{"name":"fee_params","type":{"defined":{"name":"FeeParams"}}}]}},{"name":"SellEvent","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"amount_a","type":"u64"},{"name":"amount_b","type":"u64"},{"name":"new_reserves_a","type":"u128"},{"name":"new_reserves_b","type":"u128"},{"name":"fee_a","type":"u64"}]}},{"name":"SwapParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"limit","type":"u64"}]}},{"name":"SwapResult","type":{"kind":"struct","fields":[{"name":"new_reserves_a","type":"u128"},{"name":"new_reserves_b","type":"u128"},{"name":"amount_a","type":"u64"},{"name":"amount_b","type":"u64"},{"name":"fee_a","type":"u64"}]}}]} \ No newline at end of file diff --git a/idls/virtuals.5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki.json b/idls/virtuals.5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki.json new file mode 100644 index 0000000..c0c7861 --- /dev/null +++ b/idls/virtuals.5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki.json @@ -0,0 +1 @@ +{"address":"5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki","metadata":{"name":"virtuals_program","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"buy","discriminator":[102,6,61,18,1,218,235,234],"accounts":[{"name":"user","signer":true},{"name":"vpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,112,111,111,108]},{"kind":"account","path":"token_mint"}]}},{"name":"token_mint"},{"name":"user_virtuals_ata","writable":true},{"name":"user_token_ata","writable":true},{"name":"vpool_token_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"platform_prototype","writable":true,"address":"933jV351WDG23QTcHPqLFJxyYRrEPWRTR3qoPWi3jwEL"},{"name":"platform_prototype_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"platform_prototype"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"const","value":[40,82,144,230,223,30,203,194,218,8,182,83,3,69,230,99,143,96,63,54,49,170,42,186,128,143,177,157,236,50,113,76]}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vpool_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"const","value":[40,82,144,230,223,30,203,194,218,8,182,83,3,69,230,99,143,96,63,54,49,170,42,186,128,143,177,157,236,50,113,76]}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"amount","type":"u64"},{"name":"max_amount_out","type":"u64"}]},{"name":"claim_fees","discriminator":[82,251,233,156,12,52,184,202],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"vpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,112,111,111,108]},{"kind":"account","path":"token_mint"}]}},{"name":"virtuals_mint","address":"3iQL8BFS2vE7mww4ehAqQHAsbmRNCrPxizWAT2Zfyr9y"},{"name":"token_mint"},{"name":"vpool_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"virtuals_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vpool_token_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"platform","writable":true,"address":"FxKXqCKXdxPbfYau83ZkGUXgcZWhKBTfAjGg2KzYSopS"},{"name":"platform_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"platform"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"virtuals_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"platform_token_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"platform"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"creator_virtuals_ata","writable":true},{"name":"creator_token_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool.creator","account":"VirtualsPool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool","writable":true},{"name":"lp_mint","writable":true},{"name":"lock_escrow","writable":true},{"name":"escrow_vault","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"virtuals_vault","writable":true},{"name":"token_vault","writable":true},{"name":"virtuals_token_vault","writable":true},{"name":"token_token_vault","writable":true},{"name":"virtuals_vault_lp_mint","writable":true},{"name":"token_vault_lp_mint","writable":true},{"name":"virtuals_vault_lp","writable":true},{"name":"token_vault_lp","writable":true},{"name":"vault_program","address":"24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"dynamic_amm_program","address":"Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB"}],"args":[]},{"name":"create_meteora_pool","discriminator":[246,254,33,37,225,176,41,232],"accounts":[{"name":"vpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,112,111,111,108]},{"kind":"account","path":"token_mint"}]}},{"name":"meteora_deployer","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,101,111,114,97,95,100,101,112,108,111,121,101,114]}]}},{"name":"meteora_deployer_virtuals_ata","writable":true},{"name":"meteora_deployer_token_ata","writable":true},{"name":"vpool_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"virtuals_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vpool_token_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lock_escrow","writable":true},{"name":"escrow_vault","writable":true},{"name":"pool","writable":true},{"name":"config","address":"B6FT4AHhQJ7BttrC1i223km5bYRUA2nVwBR7k5TVwLtx"},{"name":"lp_mint","writable":true},{"name":"virtuals_mint","address":"3iQL8BFS2vE7mww4ehAqQHAsbmRNCrPxizWAT2Zfyr9y"},{"name":"token_mint"},{"name":"virtuals_vault","writable":true},{"name":"token_vault","writable":true},{"name":"virtuals_token_vault","writable":true},{"name":"token_token_vault","writable":true},{"name":"virtuals_vault_lp_mint","writable":true},{"name":"token_vault_lp_mint","writable":true},{"name":"virtuals_vault_lp","writable":true},{"name":"token_vault_lp","writable":true},{"name":"pool_virtuals_ata","writable":true},{"name":"pool_token_ata","writable":true},{"name":"meteora_deployer_pool_lp","writable":true},{"name":"protocol_virtuals_fee","writable":true},{"name":"protocol_token_fee","writable":true},{"name":"payer","writable":true,"signer":true},{"name":"token_metadata","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"account","path":"metadata_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"account","path":"metadata_program"}}},{"name":"rent","docs":["Rent account."],"address":"SysvarRent111111111111111111111111111111111"},{"name":"mint_metadata","writable":true},{"name":"metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"vault_program","address":"24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi"},{"name":"token_program","docs":["Token program."],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Associated token program."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["System program."],"address":"11111111111111111111111111111111"},{"name":"dynamic_amm_program","address":"Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB"}],"args":[]},{"name":"initialize","docs":["# Initialize Pool","","This is where Virtuals creates a new mint address and pool and mints the initial 1,000,000,000 tokens.","This enabels Virtuals to premine addresses and \\","sell\\\\","them to customers without any delays of mining the","address. While this instruction could be permissioned, it is currently permissionless. As such, we make","sure to handle all the necessary checks such as: address suffix, zero supply, 6 decimals, correct mint","authority."],"discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"virtuals_mint","address":"3iQL8BFS2vE7mww4ehAqQHAsbmRNCrPxizWAT2Zfyr9y"},{"name":"token_mint","writable":true},{"name":"vpool_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"virtuals_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vpool_token_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,112,111,111,108]},{"kind":"account","path":"token_mint"}]}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"initialize_meteora_accounts","discriminator":[53,12,118,158,253,239,185,214],"accounts":[{"name":"vpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,112,111,111,108]},{"kind":"account","path":"token_mint"}]}},{"name":"meteora_deployer","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,101,111,114,97,95,100,101,112,108,111,121,101,114]}]}},{"name":"meteora_deployer_virtuals_ata","writable":true},{"name":"meteora_deployer_token_ata","writable":true},{"name":"vpool_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"virtuals_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vpool_token_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lock_escrow","writable":true},{"name":"escrow_vault","writable":true},{"name":"pool","writable":true},{"name":"config","address":"B6FT4AHhQJ7BttrC1i223km5bYRUA2nVwBR7k5TVwLtx"},{"name":"lp_mint","writable":true},{"name":"virtuals_mint","address":"3iQL8BFS2vE7mww4ehAqQHAsbmRNCrPxizWAT2Zfyr9y"},{"name":"token_mint"},{"name":"virtuals_vault","writable":true},{"name":"token_vault","writable":true},{"name":"virtuals_token_vault","writable":true},{"name":"token_token_vault","writable":true},{"name":"virtuals_vault_lp_mint","writable":true},{"name":"token_vault_lp_mint","writable":true},{"name":"virtuals_vault_lp","writable":true},{"name":"token_vault_lp","writable":true},{"name":"pool_virtuals_ata","writable":true},{"name":"pool_token_ata","writable":true},{"name":"meteora_deployer_pool_lp","writable":true},{"name":"protocol_virtuals_fee","writable":true},{"name":"protocol_token_fee","writable":true},{"name":"payer","writable":true,"signer":true},{"name":"token_metadata","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"account","path":"metadata_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"account","path":"metadata_program"}}},{"name":"rent","docs":["Rent account."],"address":"SysvarRent111111111111111111111111111111111"},{"name":"mint_metadata","writable":true},{"name":"metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"vault_program","address":"24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi"},{"name":"token_program","docs":["Token program."],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Associated token program."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["System program."],"address":"11111111111111111111111111111111"},{"name":"dynamic_amm_program","address":"Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB"}],"args":[]},{"name":"launch","docs":["","This is where a user comes along and purchases a mint address and pool. The user will provide a name,","symbol and URI containing offchain metadata. The contract will initialize a new onchain metadata","account containing these values, then revoke the mint authority.",""],"discriminator":[153,241,93,225,22,69,74,61],"accounts":[{"name":"creator","writable":true,"signer":true},{"name":"creator_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"creator"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"const","value":[40,82,144,230,223,30,203,194,218,8,182,83,3,69,230,99,143,96,63,54,49,170,42,186,128,143,177,157,236,50,113,76]}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_mint","writable":true},{"name":"platform_prototype","writable":true,"address":"933jV351WDG23QTcHPqLFJxyYRrEPWRTR3qoPWi3jwEL"},{"name":"platform_prototype_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"platform_prototype"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"const","value":[40,82,144,230,223,30,203,194,218,8,182,83,3,69,230,99,143,96,63,54,49,170,42,186,128,143,177,157,236,50,113,76]}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,112,111,111,108]},{"kind":"account","path":"token_mint"}]}},{"name":"token_metadata","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"account","path":"metadata_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"account","path":"metadata_program"}}},{"name":"metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"symbol","type":"string"},{"name":"name","type":"string"},{"name":"uri","type":"string"}]},{"name":"sell","discriminator":[51,230,133,164,1,127,131,173],"accounts":[{"name":"user","signer":true},{"name":"vpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,112,111,111,108]},{"kind":"account","path":"token_mint"}]}},{"name":"token_mint"},{"name":"user_virtuals_ata","writable":true},{"name":"user_token_ata","writable":true},{"name":"vpool_token_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"platform_prototype","writable":true,"address":"933jV351WDG23QTcHPqLFJxyYRrEPWRTR3qoPWi3jwEL"},{"name":"platform_prototype_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"platform_prototype"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"const","value":[40,82,144,230,223,30,203,194,218,8,182,83,3,69,230,99,143,96,63,54,49,170,42,186,128,143,177,157,236,50,113,76]}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vpool_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"const","value":[40,82,144,230,223,30,203,194,218,8,182,83,3,69,230,99,143,96,63,54,49,170,42,186,128,143,177,157,236,50,113,76]}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"amount","type":"u64"},{"name":"min_amount_out","type":"u64"}]},{"name":"update_pool_creator","discriminator":[113,225,166,185,94,231,96,28],"accounts":[{"name":"creator","writable":true,"signer":true,"relations":["vpool"]},{"name":"new_creator"},{"name":"virtuals_mint","address":"3iQL8BFS2vE7mww4ehAqQHAsbmRNCrPxizWAT2Zfyr9y"},{"name":"token_mint"},{"name":"new_creator_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"new_creator"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"virtuals_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"new_creator_token_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"new_creator"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vpool","pda":{"seeds":[{"kind":"const","value":[118,112,111,111,108]},{"kind":"account","path":"token_mint"}]}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]}],"accounts":[{"name":"VirtualsPool","discriminator":[71,118,5,203,5,98,135,116]}],"events":[{"name":"BuyEvent","discriminator":[103,244,82,31,44,245,119,119]},{"name":"GraduationEvent","discriminator":[10,246,223,127,48,98,149,55]},{"name":"LaunchEvent","discriminator":[27,193,47,130,115,92,239,94]},{"name":"SellEvent","discriminator":[62,47,55,10,165,3,220,42]}],"errors":[{"code":6000,"name":"InvalidMintAddress","msg":"Invalid mint address"},{"code":6001,"name":"InvalidMintParams","msg":"Invalid mint params"},{"code":6002,"name":"CurveError","msg":"Curve error"},{"code":6003,"name":"InvalidFee","msg":"Fee cannot exceed 100%"},{"code":6004,"name":"NameTooLong","msg":"Name too long. Max length = 20"},{"code":6005,"name":"SymbolTooLong","msg":"Symbol too long. Max length = 10"},{"code":6006,"name":"URITooLong","msg":"URI too long. Max length = 200"},{"code":6007,"name":"InvalidAmount","msg":"Amount must be >0"},{"code":6008,"name":"SlippageExceeded","msg":"Slippage exceeded"},{"code":6009,"name":"InvalidVPoolState","msg":"Invalid vpool state"},{"code":6010,"name":"InvalidName","msg":"Invalid name"},{"code":6011,"name":"InvalidSymbol","msg":"Invalid symbol"},{"code":6012,"name":"InvalidURI","msg":"Invalid URI"}],"types":[{"name":"BuyEvent","type":{"kind":"struct","fields":[{"name":"buy_amount","type":"u64"},{"name":"virtuals_amount","type":"u64"}]}},{"name":"GraduationEvent","type":{"kind":"struct","fields":[{"name":"vpool","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"balance","type":"u64"}]}},{"name":"LaunchEvent","type":{"kind":"struct","fields":[{"name":"vpool","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"creator","type":"pubkey"}]}},{"name":"PoolState","type":{"kind":"enum","variants":[{"name":"Initialized"},{"name":"Active"},{"name":"Graduated"},{"name":"Migrated"}]}},{"name":"SellEvent","type":{"kind":"struct","fields":[{"name":"sell_amount","type":"u64"},{"name":"virtuals_amount","type":"u64"}]}},{"name":"VirtualsPool","type":{"kind":"struct","fields":[{"name":"creator","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"virtual_y","type":"u64"},{"name":"graduation_x","type":"u64"},{"name":"state","type":{"defined":{"name":"PoolState"}}},{"name":"bump","type":"u8"}]}}]} \ No newline at end of file diff --git a/idls/woofi.WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb.json b/idls/woofi.WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb.json new file mode 100644 index 0000000..dd5fa49 --- /dev/null +++ b/idls/woofi.WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"woofi","instructions":[{"name":"createConfig","accounts":[{"name":"authority","isMut":true,"isSigner":true},{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"createWooracle","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"feedAccount","isMut":false,"isSigner":false},{"name":"priceUpdate","isMut":false,"isSigner":false},{"name":"quoteTokenMint","isMut":false,"isSigner":false},{"name":"quoteFeedAccount","isMut":false,"isSigner":false},{"name":"quotePriceUpdate","isMut":false,"isSigner":false}],"args":[{"name":"maximumAge","type":"u64"}]},{"name":"setOracleMaximumAge","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"maximumAge","type":"u64"}]},{"name":"setStaleDuration","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"staleDuration","type":"i64"}]},{"name":"setWooBound","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"bound","type":"u64"}]},{"name":"setWooRange","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"rangeMin","type":"u128"},{"name":"rangeMax","type":"u128"}]},{"name":"setWooPrice","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"price","type":"u128"}]},{"name":"setWooCoeff","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"coeff","type":"u64"}]},{"name":"setWooSpread","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"spread","type":"u64"}]},{"name":"setWooAdmin","accounts":[{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"adminAuthority","type":{"vec":"publicKey"}}]},{"name":"setGuardianAdmin","accounts":[{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"guardianAuthority","type":{"vec":"publicKey"}}]},{"name":"setLendingManager","accounts":[{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"lendingManagers","type":{"vec":"publicKey"}}]},{"name":"setSuperchargerVaultWhitelist","accounts":[{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"superchargerVaults","type":{"vec":"publicKey"}}]},{"name":"setWooState","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"price","type":"u128"},{"name":"coeff","type":"u64"},{"name":"spread","type":"u64"}]},{"name":"getPrice","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"priceUpdate","isMut":false,"isSigner":false},{"name":"quotePriceUpdate","isMut":false,"isSigner":false}],"args":[],"returns":{"defined":"GetPriceResult"}},{"name":"createPool","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"quoteTokenMint","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"woopool","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":true},{"name":"wooracle","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"createWooAmmPool","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"wooammpool","isMut":true,"isSigner":false},{"name":"wooracleA","isMut":false,"isSigner":false},{"name":"woopoolA","isMut":false,"isSigner":false},{"name":"wooracleB","isMut":false,"isSigner":false},{"name":"woopoolB","isMut":false,"isSigner":false},{"name":"woopoolQuote","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"setPoolAdmin","accounts":[{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"adminAuthority","type":{"vec":"publicKey"}}]},{"name":"setFeeAdmin","accounts":[{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"feeAuthority","type":{"vec":"publicKey"}}]},{"name":"setPauseRole","accounts":[{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"pauseAuthority","type":{"vec":"publicKey"}}]},{"name":"pause","accounts":[{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[]},{"name":"unpause","accounts":[{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[]},{"name":"setPoolFeeRate","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"woopool","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"feeRate","type":"u16"}]},{"name":"setPoolMaxGamma","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"woopool","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"maxGamma","type":"u128"}]},{"name":"setPoolMaxNotionalSwap","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"woopool","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"maxNotionalSwap","type":"u128"}]},{"name":"setPoolCapBal","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"woopool","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"capBal","type":"u128"}]},{"name":"setPoolMinSwapAmount","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"woopool","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"minSwapAmount","type":"u128"}]},{"name":"tryQuery","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracleFrom","isMut":false,"isSigner":false},{"name":"woopoolFrom","isMut":false,"isSigner":false},{"name":"priceUpdateFrom","isMut":true,"isSigner":false},{"name":"wooracleTo","isMut":false,"isSigner":false},{"name":"woopoolTo","isMut":false,"isSigner":false},{"name":"priceUpdateTo","isMut":true,"isSigner":false},{"name":"quotePriceUpdate","isMut":false,"isSigner":false}],"args":[{"name":"fromAmount","type":"u128"}],"returns":{"defined":"QueryResult"}},{"name":"query","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracleFrom","isMut":false,"isSigner":false},{"name":"woopoolFrom","isMut":false,"isSigner":false},{"name":"tokenVaultFrom","isMut":true,"isSigner":false},{"name":"priceUpdateFrom","isMut":true,"isSigner":false},{"name":"wooracleTo","isMut":false,"isSigner":false},{"name":"woopoolTo","isMut":false,"isSigner":false},{"name":"tokenVaultTo","isMut":false,"isSigner":false},{"name":"priceUpdateTo","isMut":true,"isSigner":false},{"name":"woopoolQuote","isMut":false,"isSigner":false},{"name":"quotePriceUpdate","isMut":false,"isSigner":false},{"name":"quoteTokenVault","isMut":false,"isSigner":false}],"args":[{"name":"fromAmount","type":"u128"},{"name":"minToAmount","type":"u128"}],"returns":{"defined":"QueryResult"}},{"name":"swap","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"wooracleFrom","isMut":true,"isSigner":false},{"name":"woopoolFrom","isMut":true,"isSigner":false},{"name":"tokenOwnerAccountFrom","isMut":true,"isSigner":false},{"name":"tokenVaultFrom","isMut":true,"isSigner":false},{"name":"priceUpdateFrom","isMut":true,"isSigner":false},{"name":"wooracleTo","isMut":true,"isSigner":false},{"name":"woopoolTo","isMut":true,"isSigner":false},{"name":"tokenOwnerAccountTo","isMut":true,"isSigner":false},{"name":"tokenVaultTo","isMut":true,"isSigner":false},{"name":"priceUpdateTo","isMut":true,"isSigner":false},{"name":"woopoolQuote","isMut":true,"isSigner":false},{"name":"quotePriceUpdate","isMut":false,"isSigner":false},{"name":"quoteTokenVault","isMut":true,"isSigner":false},{"name":"rebateTo","isMut":false,"isSigner":false}],"args":[{"name":"fromAmount","type":"u128"},{"name":"minToAmount","type":"u128"}]},{"name":"deposit","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"tokenOwnerAccount","isMut":true,"isSigner":false},{"name":"woopool","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u128"}]},{"name":"withdraw","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"toTokenAccount","isMut":true,"isSigner":false},{"name":"woopool","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u128"}]},{"name":"claimFee","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"woopool","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"claimFeeToAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"claimFeeAmount","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"woopool","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"claimFeeToAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"claimAmount","type":"u128"}]},{"name":"incaseTokenGotStuck","accounts":[{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"toTokenAccount","isMut":true,"isSigner":false},{"name":"woopool","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u128"}]},{"name":"setWooconfigNewAuthority","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"newAuthority","isMut":false,"isSigner":false}],"args":[]},{"name":"claimWooconfigAuthority","accounts":[{"name":"newAuthority","isMut":false,"isSigner":true},{"name":"wooconfig","isMut":true,"isSigner":false}],"args":[]},{"name":"claimWooracleAuthority","accounts":[{"name":"newAuthority","isMut":false,"isSigner":true},{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false}],"args":[]},{"name":"claimWoopoolAuthority","accounts":[{"name":"newAuthority","isMut":false,"isSigner":true},{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"woopool","isMut":true,"isSigner":false}],"args":[]},{"name":"claimWooammpoolAuthority","accounts":[{"name":"newAuthority","isMut":false,"isSigner":true},{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooammpool","isMut":true,"isSigner":false}],"args":[]},{"name":"repayByLendingManager","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"woopool","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"superChargerVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"repayAmount","type":"u128"}]}],"accounts":[{"name":"WooAmmPool","type":{"kind":"struct","fields":[{"name":"wooconfig","type":"publicKey"},{"name":"wooammpoolBump","type":{"array":["u8",1]}},{"name":"authority","type":"publicKey"},{"name":"wooracleA","type":"publicKey"},{"name":"woopoolA","type":"publicKey"},{"name":"feedAccountA","type":"publicKey"},{"name":"priceUpdateA","type":"publicKey"},{"name":"tokenMintA","type":"publicKey"},{"name":"tokenVaultA","type":"publicKey"},{"name":"wooracleB","type":"publicKey"},{"name":"woopoolB","type":"publicKey"},{"name":"feedAccountB","type":"publicKey"},{"name":"priceUpdateB","type":"publicKey"},{"name":"tokenMintB","type":"publicKey"},{"name":"tokenVaultB","type":"publicKey"},{"name":"quoteTokenMint","type":"publicKey"},{"name":"quoteFeedAccount","type":"publicKey"},{"name":"quotePriceUpdate","type":"publicKey"},{"name":"quoteWoopool","type":"publicKey"},{"name":"quoteVault","type":"publicKey"}]}},{"name":"WooConfig","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"paused","type":"bool"},{"name":"woopoolAdminAuthority","type":{"vec":"publicKey"}},{"name":"wooracleAdminAuthority","type":{"vec":"publicKey"}},{"name":"feeAuthority","type":{"vec":"publicKey"}},{"name":"guardianAuthority","type":{"vec":"publicKey"}},{"name":"pauseAuthority","type":{"vec":"publicKey"}},{"name":"lendingManagerAuthority","type":{"vec":"publicKey"}},{"name":"superchargerVaultWhitelist","type":{"vec":"publicKey"}},{"name":"newAuthority","type":"publicKey"}]}},{"name":"WooPool","type":{"kind":"struct","fields":[{"name":"wooconfig","type":"publicKey"},{"name":"woopoolBump","type":{"array":["u8",1]}},{"name":"authority","type":"publicKey"},{"name":"wooracle","type":"publicKey"},{"name":"feeRate","type":"u16"},{"name":"maxGamma","type":"u128"},{"name":"maxNotionalSwap","type":"u128"},{"name":"capBal","type":"u128"},{"name":"minSwapAmount","type":"u128"},{"name":"unclaimedFee","type":"u128"},{"name":"tokenMint","type":"publicKey"},{"name":"tokenVault","type":"publicKey"},{"name":"quoteTokenMint","type":"publicKey"},{"name":"baseDecimals","type":"u8"}]}},{"name":"Wooracle","type":{"kind":"struct","fields":[{"name":"wooconfig","type":"publicKey"},{"name":"authority","type":"publicKey"},{"name":"tokenMint","type":"publicKey"},{"name":"feedAccount","type":"publicKey"},{"name":"priceUpdate","type":"publicKey"},{"name":"maximumAge","type":"u64"},{"name":"priceDecimals","type":"u8"},{"name":"quoteDecimals","type":"u8"},{"name":"baseDecimals","type":"u8"},{"name":"updatedAt","type":"i64"},{"name":"staleDuration","type":"i64"},{"name":"bound","type":"u64"},{"name":"price","type":"u128"},{"name":"coeff","type":"u64"},{"name":"spread","type":"u64"},{"name":"rangeMin","type":"u128"},{"name":"rangeMax","type":"u128"},{"name":"quoteTokenMint","type":"publicKey"},{"name":"quoteFeedAccount","type":"publicKey"},{"name":"quotePriceUpdate","type":"publicKey"}]}}],"types":[{"name":"GetPriceResult","type":{"kind":"struct","fields":[{"name":"priceOut","type":"u128"},{"name":"feasibleOut","type":"bool"}]}},{"name":"GetStateResult","type":{"kind":"struct","fields":[{"name":"priceOut","type":"u128"},{"name":"spread","type":"u64"},{"name":"coeff","type":"u64"},{"name":"feasibleOut","type":"bool"}]}},{"name":"QueryResult","type":{"kind":"struct","fields":[{"name":"toAmount","type":"u128"},{"name":"swapFee","type":"u128"}]}}],"events":[{"name":"PauseEvent","fields":[{"name":"wooconfig","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false}]},{"name":"UnPauseEvent","fields":[{"name":"wooconfig","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false}]},{"name":"DepositEvent","fields":[{"name":"tokenMint","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"depositAmount","type":"u128","index":false}]},{"name":"WithdrawEvent","fields":[{"name":"tokenMint","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"withdrawAmount","type":"u128","index":false}]},{"name":"RepayEvent","fields":[{"name":"tokenMint","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"repayAmount","type":"u128","index":false}]},{"name":"ClaimFeeEvent","fields":[{"name":"quoteTokenMint","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"claimFeeToAccount","type":"publicKey","index":false},{"name":"claimAmount","type":"u128","index":false}]},{"name":"WooPoolAdminUpdatedEvent","fields":[{"name":"wooconfig","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"admins","type":{"vec":"publicKey"},"index":false}]},{"name":"FeeAdminUpdatedEvent","fields":[{"name":"wooconfig","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"feeAdmins","type":{"vec":"publicKey"},"index":false}]},{"name":"PauseRoleUpdatedEvent","fields":[{"name":"wooconfig","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"pauseRoles","type":{"vec":"publicKey"},"index":false}]},{"name":"SwapEvent","fields":[{"name":"sender","type":"publicKey","index":false},{"name":"fromTokenMint","type":"publicKey","index":false},{"name":"toTokenMint","type":"publicKey","index":false},{"name":"fromAmount","type":"u128","index":false},{"name":"toAmount","type":"u128","index":false},{"name":"fromAccount","type":"publicKey","index":false},{"name":"toAccount","type":"publicKey","index":false},{"name":"rebateTo","type":"publicKey","index":false},{"name":"swapVol","type":"u128","index":false},{"name":"swapFee","type":"u128","index":false}]}],"errors":[{"code":6000,"name":"DivideByZero","msg":"Unable to divide by zero"},{"code":6001,"name":"NumberCastError","msg":"Unable to cast number into BigInt"},{"code":6002,"name":"FeeRateMaxExceeded","msg":"Exceeded max fee rate"},{"code":6003,"name":"MathOverflow","msg":"Mathematical operation with overflow"},{"code":6004,"name":"MulDivOverflow","msg":"Muldiv overflow"},{"code":6005,"name":"ProtocolFeeMaxExceeded","msg":"Exceeded max protocol fee"},{"code":6006,"name":"ProtocolFeeNotEnough","msg":"Protocol fee not enough"},{"code":6007,"name":"RebateFeeMaxExceeded","msg":"Exceeded max rebate fee"},{"code":6008,"name":"RebateFeeNotEnough","msg":"Rebate fee not enough"},{"code":6009,"name":"ReserveMaxExceeded","msg":"Exceeded max reserve"},{"code":6010,"name":"ReserveNotEnough","msg":"Reserve not enough"},{"code":6011,"name":"ReserveLessThanFee","msg":"Reserve less than fee"},{"code":6012,"name":"TooManyAuthorities","msg":"Too Many Authorities"},{"code":6013,"name":"WooOracleBoundLimit","msg":"Woo oracle bound exceed limit"},{"code":6014,"name":"WooOracleNotFeasible","msg":"Woo oracle is not feasible"},{"code":6015,"name":"WooOraclePriceNotValid","msg":"Woo oracle price is not valid"},{"code":6016,"name":"WooOraclePriceRangeMin","msg":"Woo oracle price below range MIN"},{"code":6017,"name":"WooOraclePriceRangeMax","msg":"Woo oracle price exceed range MAX"},{"code":6018,"name":"WooOracleSpreadExceed","msg":"Woo oracle spread exceed 1E18"},{"code":6019,"name":"WooPoolExceedMaxNotionalValue","msg":"Woo pp exceed max notional value"},{"code":6020,"name":"WooPoolExceedMaxGamma","msg":"Woo pp exceed max gamma"},{"code":6021,"name":"NotEnoughBalance","msg":"Src Balance < LP Deposit Amount."},{"code":6022,"name":"NotEnoughOut","msg":"Not enough out"},{"code":6023,"name":"AmountOutBelowMinimum","msg":"Amount out below minimum threshold"},{"code":6024,"name":"BalanceCapExceeds","msg":"Amount exceeds max balance cap"},{"code":6025,"name":"SwapPoolInvalid","msg":"Swap pool invalid"},{"code":6026,"name":"InvalidAuthority","msg":"invalid authority"}]} \ No newline at end of file diff --git a/idls/zeta.ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD.json b/idls/zeta.ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD.json new file mode 100644 index 0000000..789b291 --- /dev/null +++ b/idls/zeta.ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"zeta","instructions":[{"name":"initializeZetaPricing","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true}],"args":[{"name":"args","type":{"defined":"InitializeZetaPricingArgs"}}]},{"name":"updateZetaPricingPubkeys","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdateZetaPricingPubkeysArgs"}}]},{"name":"initializeZetaGroup","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"underlyingMint","isMut":false,"isSigner":false},{"name":"zetaProgram","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"greeks","isMut":true,"isSigner":false},{"name":"perpSyncQueue","isMut":true,"isSigner":false},{"name":"underlying","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"insuranceVault","isMut":true,"isSigner":false},{"name":"socializedLossAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"usdcMint","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"InitializeZetaGroupArgs"}}]},{"name":"overrideExpiry","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"zetaGroup","isMut":true,"isSigner":false}],"args":[{"name":"args","type":{"defined":"OverrideExpiryArgs"}}]},{"name":"migrateToCrossMarginAccount","accounts":[{"name":"crossMarginAccount","isMut":true,"isSigner":false},{"name":"pricing","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[]},{"name":"initializeCrossMarginAccountManager","accounts":[{"name":"crossMarginAccountManager","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"zetaProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeCrossMarginAccount","accounts":[{"name":"crossMarginAccount","isMut":true,"isSigner":false},{"name":"crossMarginAccountManager","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"zetaProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"subaccountIndex","type":"u8"}]},{"name":"initializeMarginAccount","accounts":[{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"zetaProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeSpreadAccount","accounts":[{"name":"spreadAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"zetaProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false}],"args":[]},{"name":"closeCrossMarginAccountManager","accounts":[{"name":"crossMarginAccountManager","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[]},{"name":"closeCrossMarginAccount","accounts":[{"name":"crossMarginAccount","isMut":true,"isSigner":false},{"name":"crossMarginAccountManager","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true}],"args":[{"name":"subaccountIndex","type":"u8"}]},{"name":"closeMarginAccount","accounts":[{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"zetaGroup","isMut":false,"isSigner":false}],"args":[]},{"name":"closeSpreadAccount","accounts":[{"name":"spreadAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"zetaGroup","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeUnderlying","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"zetaProgram","isMut":false,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"underlying","isMut":true,"isSigner":false},{"name":"underlyingMint","isMut":false,"isSigner":false}],"args":[{"name":"flexUnderlying","type":"bool"}]},{"name":"initializePerpSyncQueue","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"zetaProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"perpSyncQueue","isMut":true,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"initializeMarketIndexes","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marketIndexes","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"pricing","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"initializeMarketNode","accounts":[{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marketNode","isMut":true,"isSigner":false},{"name":"greeks","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"InitializeMarketNodeArgs"}}]},{"name":"halt","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"unhalt","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"updateHaltState","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"HaltStateArgs"}}]},{"name":"updateVolatility","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"greeks","isMut":true,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdateVolatilityArgs"}}]},{"name":"updateInterestRate","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"greeks","isMut":true,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdateInterestRateArgs"}}]},{"name":"addPerpMarketIndex","accounts":[{"name":"marketIndexes","isMut":true,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"addMarketIndexes","accounts":[{"name":"marketIndexes","isMut":true,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false}],"args":[]},{"name":"initializeZetaState","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"treasuryWallet","isMut":true,"isSigner":false},{"name":"referralsAdmin","isMut":false,"isSigner":false},{"name":"referralsRewardsWallet","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"usdcMint","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"secondaryAdmin","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"InitializeStateArgs"}}]},{"name":"initializeZetaTreasuryWallet","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"treasuryWallet","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"usdcMint","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true}],"args":[]},{"name":"initializeZetaReferralsRewardsWallet","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"referralsRewardsWallet","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"usdcMint","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true}],"args":[]},{"name":"updateAdmin","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"newAdmin","isMut":true,"isSigner":true}],"args":[]},{"name":"updateSecondaryAdmin","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"newAdmin","isMut":true,"isSigner":true}],"args":[]},{"name":"updateReferralsAdmin","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"newAdmin","isMut":true,"isSigner":true}],"args":[]},{"name":"updateZetaState","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdateStateArgs"}}]},{"name":"updateOracle","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"oracle","isMut":false,"isSigner":false}],"args":[]},{"name":"updateOracleBackupFeed","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"oracle","isMut":false,"isSigner":false}],"args":[]},{"name":"updatePricingParameters","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdatePricingParametersArgs"}}]},{"name":"updateMarginParameters","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdateMarginParametersArgs"}},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"updateZetaGroupMarginParameters","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdateMarginParametersArgs"}}]},{"name":"updatePerpParameters","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdatePerpParametersArgs"}},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"updateZetaGroupPerpParameters","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdatePerpParametersArgs"}}]},{"name":"updateZetaGroupExpiryParameters","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdateZetaGroupExpiryArgs"}}]},{"name":"toggleZetaGroupPerpsOnly","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[]},{"name":"cleanZetaMarkets","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false}],"args":[]},{"name":"cleanZetaMarketHalted","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"market","isMut":false,"isSigner":false},{"name":"bids","isMut":false,"isSigner":false},{"name":"asks","isMut":false,"isSigner":false}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"settlePositionsHalted","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"initializeMarketStrikes","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"expireSeriesOverride","accounts":[],"args":[{"name":"args","type":{"defined":"ExpireSeriesOverrideArgs"}}]},{"name":"expireSeries","accounts":[],"args":[{"name":"settlementNonce","type":"u8"}]},{"name":"initializeZetaMarket","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marketIndexes","isMut":false,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"market","isMut":true,"isSigner":false},{"name":"requestQueue","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"baseMint","isMut":true,"isSigner":false},{"name":"quoteMint","isMut":true,"isSigner":false},{"name":"zetaBaseVault","isMut":true,"isSigner":false},{"name":"zetaQuoteVault","isMut":true,"isSigner":false},{"name":"dexBaseVault","isMut":true,"isSigner":false},{"name":"dexQuoteVault","isMut":true,"isSigner":false},{"name":"vaultOwner","isMut":false,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"InitializeMarketArgs"}}]},{"name":"initializeMarketTifEpochCycle","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"market","isMut":true,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false}],"args":[{"name":"epochLength","type":"u16"}]},{"name":"updatePricingV2","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":false,"isSigner":false},{"name":"perpBids","isMut":false,"isSigner":false},{"name":"perpAsks","isMut":false,"isSigner":false}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"applyPerpFunding","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":false,"isSigner":false}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"deposit","accounts":[{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"socializedLossAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"depositV2","accounts":[{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"socializedLossAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"depositInsuranceVault","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"insuranceVault","isMut":true,"isSigner":false},{"name":"insuranceDepositAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"zetaVault","isMut":true,"isSigner":false},{"name":"socializedLossAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"depositInsuranceVaultV2","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"insuranceVault","isMut":true,"isSigner":false},{"name":"insuranceDepositAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"zetaVault","isMut":true,"isSigner":false},{"name":"socializedLossAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdraw","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"greeks","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"socializedLossAccount","isMut":true,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdrawV2","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"socializedLossAccount","isMut":true,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdrawInsuranceVault","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"insuranceVault","isMut":true,"isSigner":false},{"name":"insuranceDepositAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"percentageAmount","type":"u64"}]},{"name":"withdrawInsuranceVaultV2","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"insuranceVault","isMut":true,"isSigner":false},{"name":"insuranceDepositAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"percentageAmount","type":"u64"}]},{"name":"initializeOpenOrders","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"market","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrdersMap","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeOpenOrdersV2","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"market","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrdersMap","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeOpenOrdersV3","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"crossMarginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"market","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrdersMap","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"closeOpenOrders","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"market","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrdersMap","isMut":true,"isSigner":false}],"args":[{"name":"mapNonce","type":"u8"}]},{"name":"closeOpenOrdersV2","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"market","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrdersMap","isMut":true,"isSigner":false}],"args":[{"name":"mapNonce","type":"u8"}]},{"name":"closeOpenOrdersV3","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":false,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"crossMarginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"market","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrdersMap","isMut":true,"isSigner":false}],"args":[{"name":"mapNonce","type":"u8"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"initializeWhitelistDepositAccount","accounts":[{"name":"whitelistDepositAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"user","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"initializeWhitelistInsuranceAccount","accounts":[{"name":"whitelistInsuranceAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"user","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"initializeWhitelistTradingFeesAccount","accounts":[{"name":"whitelistTradingFeesAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"user","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"initializeInsuranceDepositAccount","accounts":[{"name":"insuranceDepositAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"whitelistInsuranceAccount","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"initializeCombinedInsuranceVault","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"insuranceVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"usdcMint","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"initializeCombinedVault","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"usdcMint","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"initializeCombinedSocializedLossAccount","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"socializedLossAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"usdcMint","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"placeOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"marketAccounts","accounts":[{"name":"market","isMut":true,"isSigner":false},{"name":"requestQueue","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"orderPayerTokenAccount","isMut":true,"isSigner":false},{"name":"coinVault","isMut":true,"isSigner":false},{"name":"pcVault","isMut":true,"isSigner":false},{"name":"coinWallet","isMut":true,"isSigner":false},{"name":"pcWallet","isMut":true,"isSigner":false}]},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"marketNode","isMut":true,"isSigner":false},{"name":"marketMint","isMut":true,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false}],"args":[{"name":"price","type":"u64"},{"name":"size","type":"u64"},{"name":"side","type":{"defined":"Side"}},{"name":"clientOrderId","type":{"option":"u64"}}]},{"name":"placeOrderV2","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"marketAccounts","accounts":[{"name":"market","isMut":true,"isSigner":false},{"name":"requestQueue","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"orderPayerTokenAccount","isMut":true,"isSigner":false},{"name":"coinVault","isMut":true,"isSigner":false},{"name":"pcVault","isMut":true,"isSigner":false},{"name":"coinWallet","isMut":true,"isSigner":false},{"name":"pcWallet","isMut":true,"isSigner":false}]},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"marketNode","isMut":true,"isSigner":false},{"name":"marketMint","isMut":true,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false}],"args":[{"name":"price","type":"u64"},{"name":"size","type":"u64"},{"name":"side","type":{"defined":"Side"}},{"name":"orderType","type":{"defined":"OrderType"}},{"name":"clientOrderId","type":{"option":"u64"}}]},{"name":"placeOrderV3","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"marketAccounts","accounts":[{"name":"market","isMut":true,"isSigner":false},{"name":"requestQueue","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"orderPayerTokenAccount","isMut":true,"isSigner":false},{"name":"coinVault","isMut":true,"isSigner":false},{"name":"pcVault","isMut":true,"isSigner":false},{"name":"coinWallet","isMut":true,"isSigner":false},{"name":"pcWallet","isMut":true,"isSigner":false}]},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"marketNode","isMut":true,"isSigner":false},{"name":"marketMint","isMut":true,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false}],"args":[{"name":"price","type":"u64"},{"name":"size","type":"u64"},{"name":"side","type":{"defined":"Side"}},{"name":"orderType","type":{"defined":"OrderType"}},{"name":"clientOrderId","type":{"option":"u64"}},{"name":"tag","type":{"option":"string"}}]},{"name":"placePerpOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"marketAccounts","accounts":[{"name":"market","isMut":true,"isSigner":false},{"name":"requestQueue","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"orderPayerTokenAccount","isMut":true,"isSigner":false},{"name":"coinVault","isMut":true,"isSigner":false},{"name":"pcVault","isMut":true,"isSigner":false},{"name":"coinWallet","isMut":true,"isSigner":false},{"name":"pcWallet","isMut":true,"isSigner":false}]},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"marketMint","isMut":true,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false},{"name":"perpSyncQueue","isMut":true,"isSigner":false}],"args":[{"name":"price","type":"u64"},{"name":"size","type":"u64"},{"name":"side","type":{"defined":"Side"}},{"name":"orderType","type":{"defined":"OrderType"}},{"name":"clientOrderId","type":{"option":"u64"}},{"name":"tag","type":{"option":"string"}}]},{"name":"placePerpOrderV2","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"marketAccounts","accounts":[{"name":"market","isMut":true,"isSigner":false},{"name":"requestQueue","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"orderPayerTokenAccount","isMut":true,"isSigner":false},{"name":"coinVault","isMut":true,"isSigner":false},{"name":"pcVault","isMut":true,"isSigner":false},{"name":"coinWallet","isMut":true,"isSigner":false},{"name":"pcWallet","isMut":true,"isSigner":false}]},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"marketMint","isMut":true,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false},{"name":"perpSyncQueue","isMut":true,"isSigner":false}],"args":[{"name":"price","type":"u64"},{"name":"size","type":"u64"},{"name":"side","type":{"defined":"Side"}},{"name":"orderType","type":{"defined":"OrderType"}},{"name":"clientOrderId","type":{"option":"u64"}},{"name":"tag","type":{"option":"string"}},{"name":"tifOffset","type":{"option":"u16"}}]},{"name":"placeOrderV4","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"marketAccounts","accounts":[{"name":"market","isMut":true,"isSigner":false},{"name":"requestQueue","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"orderPayerTokenAccount","isMut":true,"isSigner":false},{"name":"coinVault","isMut":true,"isSigner":false},{"name":"pcVault","isMut":true,"isSigner":false},{"name":"coinWallet","isMut":true,"isSigner":false},{"name":"pcWallet","isMut":true,"isSigner":false}]},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"marketNode","isMut":true,"isSigner":false},{"name":"marketMint","isMut":true,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false}],"args":[{"name":"price","type":"u64"},{"name":"size","type":"u64"},{"name":"side","type":{"defined":"Side"}},{"name":"orderType","type":{"defined":"OrderType"}},{"name":"clientOrderId","type":{"option":"u64"}},{"name":"tag","type":{"option":"string"}},{"name":"tifOffset","type":{"option":"u16"}}]},{"name":"placePerpOrderV3","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"marketAccounts","accounts":[{"name":"market","isMut":true,"isSigner":false},{"name":"requestQueue","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"orderPayerTokenAccount","isMut":true,"isSigner":false},{"name":"coinVault","isMut":true,"isSigner":false},{"name":"pcVault","isMut":true,"isSigner":false},{"name":"coinWallet","isMut":true,"isSigner":false},{"name":"pcWallet","isMut":true,"isSigner":false}]},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"marketMint","isMut":true,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false},{"name":"perpSyncQueue","isMut":true,"isSigner":false}],"args":[{"name":"price","type":"u64"},{"name":"size","type":"u64"},{"name":"side","type":{"defined":"Side"}},{"name":"orderType","type":{"defined":"OrderType"}},{"name":"clientOrderId","type":{"option":"u64"}},{"name":"tag","type":{"option":"string"}},{"name":"tifOffset","type":{"option":"u16"}},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"cancelOrder","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"side","type":{"defined":"Side"}},{"name":"orderId","type":"u128"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"cancelOrderNoError","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"side","type":{"defined":"Side"}},{"name":"orderId","type":"u128"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"cancelAllMarketOrders","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"cancelOrderHalted","accounts":[{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"side","type":{"defined":"Side"}},{"name":"orderId","type":"u128"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"cancelOrderByClientOrderId","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"clientOrderId","type":"u64"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"cancelOrderByClientOrderIdNoError","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"clientOrderId","type":"u64"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"pruneExpiredTifOrders","accounts":[{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}],"args":[]},{"name":"forceCancelOrderByOrderIdV2","accounts":[{"name":"pricing","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"side","type":{"defined":"Side"}},{"name":"orderId","type":"u128"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"forceCancelOrderByOrderId","accounts":[{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"side","type":{"defined":"Side"}},{"name":"orderId","type":"u128"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"forceCancelOrdersV2","accounts":[{"name":"pricing","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"forceCancelOrders","accounts":[{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"crankEventQueue","docs":["The only events that need to be cranked are maker fills."],"accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":false,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"perpSyncQueue","isMut":true,"isSigner":false}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"collectTreasuryFunds","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"treasuryWallet","isMut":true,"isSigner":false},{"name":"collectionTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"amount","type":"u64"}]},{"name":"treasuryMovement","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"insuranceVault","isMut":true,"isSigner":false},{"name":"treasuryWallet","isMut":true,"isSigner":false},{"name":"referralsRewardsWallet","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"treasuryMovementType","type":{"defined":"TreasuryMovementType"}},{"name":"amount","type":"u64"}]},{"name":"rebalanceInsuranceVault","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaVault","isMut":true,"isSigner":false},{"name":"insuranceVault","isMut":true,"isSigner":false},{"name":"treasuryWallet","isMut":true,"isSigner":false},{"name":"socializedLossAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"liquidateV2","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"liquidator","isMut":false,"isSigner":true},{"name":"liquidatorAccount","isMut":true,"isSigner":false},{"name":"pricing","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"market","isMut":false,"isSigner":false},{"name":"liquidatedAccount","isMut":true,"isSigner":false}],"args":[{"name":"size","type":"u64"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"liquidate","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"liquidator","isMut":false,"isSigner":true},{"name":"liquidatorMarginAccount","isMut":true,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"market","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"liquidatedMarginAccount","isMut":true,"isSigner":false}],"args":[{"name":"size","type":"u64"}]},{"name":"burnVaultTokens","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"mint","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"settleDexFunds","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"zetaBaseVault","isMut":true,"isSigner":false},{"name":"zetaQuoteVault","isMut":true,"isSigner":false},{"name":"dexBaseVault","isMut":true,"isSigner":false},{"name":"dexQuoteVault","isMut":true,"isSigner":false},{"name":"vaultOwner","isMut":false,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"positionMovement","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"spreadAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"greeks","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false}],"args":[{"name":"movementType","type":{"defined":"MovementType"}},{"name":"movements","type":{"vec":{"defined":"PositionMovementArg"}}}]},{"name":"transferExcessSpreadBalance","accounts":[{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"spreadAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[]},{"name":"toggleMarketMaker","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"marginAccount","isMut":true,"isSigner":false}],"args":[{"name":"isMarketMaker","type":"bool"}]},{"name":"initializeReferrerAccount","accounts":[{"name":"referrer","isMut":true,"isSigner":true},{"name":"referrerAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"referUser","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"referrerAccount","isMut":false,"isSigner":false},{"name":"referralAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeReferrerAlias","accounts":[{"name":"referrer","isMut":true,"isSigner":true},{"name":"referrerAlias","isMut":true,"isSigner":false},{"name":"referrerAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"alias","type":"string"}]},{"name":"setReferralsRewards","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"referralsAdmin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"vec":{"defined":"SetReferralsRewardsArgs"}}}]},{"name":"claimReferralsRewards","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"referralsRewardsWallet","isMut":true,"isSigner":false},{"name":"userReferralsAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"user","isMut":false,"isSigner":true}],"args":[]},{"name":"editDelegatedPubkey","accounts":[{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"newKey","type":"publicKey"}]},{"name":"resetNumFlexUnderlyings","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[]}],"accounts":[{"name":"Pricing","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"markPrices","type":{"array":["u64",5]}},{"name":"markPricesPadding","type":{"array":["u64",20]}},{"name":"updateTimestamps","type":{"array":["u64",5]}},{"name":"updateTimestampsPadding","type":{"array":["u64",20]}},{"name":"fundingDeltas","type":{"array":[{"defined":"AnchorDecimal"},5]}},{"name":"fundingDeltasPadding","type":{"array":[{"defined":"AnchorDecimal"},20]}},{"name":"latestFundingRates","type":{"array":[{"defined":"AnchorDecimal"},5]}},{"name":"latestFundingRatesPadding","type":{"array":[{"defined":"AnchorDecimal"},20]}},{"name":"latestMidpoints","type":{"array":["u64",5]}},{"name":"latestMidpointsPadding","type":{"array":["u64",20]}},{"name":"oracles","type":{"array":["publicKey",5]}},{"name":"oraclesPadding","type":{"array":["publicKey",20]}},{"name":"oracleBackupFeeds","type":{"array":["publicKey",5]}},{"name":"oracleBackupFeedsPadding","type":{"array":["publicKey",20]}},{"name":"markets","type":{"array":["publicKey",5]}},{"name":"marketsPadding","type":{"array":["publicKey",20]}},{"name":"perpSyncQueues","type":{"array":["publicKey",5]}},{"name":"perpSyncQueuesPadding","type":{"array":["publicKey",20]}},{"name":"perpParameters","type":{"array":[{"defined":"PerpParameters"},5]}},{"name":"perpParametersPadding","type":{"array":[{"defined":"PerpParameters"},20]}},{"name":"marginParameters","type":{"array":[{"defined":"MarginParameters"},5]}},{"name":"marginParametersPadding","type":{"array":[{"defined":"MarginParameters"},20]}},{"name":"products","type":{"array":[{"defined":"Product"},5]}},{"name":"productsPadding","type":{"array":[{"defined":"Product"},20]}},{"name":"zetaGroupKeys","type":{"array":["publicKey",5]}},{"name":"zetaGroupKeysPadding","type":{"array":["publicKey",20]}},{"name":"totalInsuranceVaultDeposits","type":"u64"},{"name":"lastWithdrawTimestamp","type":"u64"},{"name":"netOutflowSum","type":"i64"},{"name":"haltForcePricing","type":{"array":["bool",5]}},{"name":"haltForcePricingPadding","type":{"array":["bool",20]}},{"name":"padding","type":{"array":["u8",2707]}}]}},{"name":"Greeks","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"markPrices","type":{"array":["u64",46]}},{"name":"markPricesPadding","type":{"array":["u64",91]}},{"name":"perpMarkPrice","type":"u64"},{"name":"productGreeks","type":{"array":[{"defined":"ProductGreeks"},22]}},{"name":"productGreeksPadding","type":{"array":[{"defined":"ProductGreeks"},44]}},{"name":"updateTimestamp","type":{"array":["u64",2]}},{"name":"updateTimestampPadding","type":{"array":["u64",4]}},{"name":"retreatExpirationTimestamp","type":{"array":["u64",2]}},{"name":"retreatExpirationTimestampPadding","type":{"array":["u64",4]}},{"name":"interestRate","type":{"array":["i64",2]}},{"name":"interestRatePadding","type":{"array":["i64",4]}},{"name":"nodes","type":{"array":["u64",5]}},{"name":"volatility","type":{"array":["u64",10]}},{"name":"volatilityPadding","type":{"array":["u64",20]}},{"name":"nodeKeys","type":{"array":["publicKey",138]}},{"name":"haltForcePricing","type":{"array":["bool",6]}},{"name":"perpUpdateTimestamp","type":"u64"},{"name":"perpFundingDelta","type":{"defined":"AnchorDecimal"}},{"name":"perpLatestFundingRate","type":{"defined":"AnchorDecimal"}},{"name":"perpLatestMidpoint","type":"u64"},{"name":"padding","type":{"array":["u8",1593]}}]}},{"name":"MarketIndexes","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"initialized","type":"bool"},{"name":"indexes","type":{"array":["u8",138]}}]}},{"name":"OpenOrdersMap","type":{"kind":"struct","fields":[{"name":"userKey","type":"publicKey"}]}},{"name":"CrossOpenOrdersMap","type":{"kind":"struct","fields":[{"name":"userKey","type":"publicKey"},{"name":"subaccountIndex","type":"u8"}]}},{"name":"State","type":{"kind":"struct","fields":[{"name":"admin","type":"publicKey"},{"name":"stateNonce","type":"u8"},{"name":"serumNonce","type":"u8"},{"name":"mintAuthNonce","type":"u8"},{"name":"numUnderlyings","type":"u8"},{"name":"numFlexUnderlyings","type":"u8"},{"name":"null","type":{"array":["u8",7]}},{"name":"strikeInitializationThresholdSeconds","type":"u32"},{"name":"pricingFrequencySeconds","type":"u32"},{"name":"liquidatorLiquidationPercentage","type":"u32"},{"name":"insuranceVaultLiquidationPercentage","type":"u32"},{"name":"nativeD1TradeFeePercentage","type":"u64"},{"name":"nativeD1UnderlyingFeePercentage","type":"u64"},{"name":"nativeWhitelistUnderlyingFeePercentage","type":"u64"},{"name":"nativeDepositLimit","type":"u64"},{"name":"expirationThresholdSeconds","type":"u32"},{"name":"positionMovementFeeBps","type":"u8"},{"name":"marginConcessionPercentage","type":"u8"},{"name":"treasuryWalletNonce","type":"u8"},{"name":"nativeOptionTradeFeePercentage","type":"u64"},{"name":"nativeOptionUnderlyingFeePercentage","type":"u64"},{"name":"referralsAdmin","type":"publicKey"},{"name":"referralsRewardsWalletNonce","type":"u8"},{"name":"maxPerpDeltaAge","type":"u16"},{"name":"secondaryAdmin","type":"publicKey"},{"name":"vaultNonce","type":"u8"},{"name":"insuranceVaultNonce","type":"u8"},{"name":"deprecatedTotalInsuranceVaultDeposits","type":"u64"},{"name":"nativeWithdrawLimit","type":"u64"},{"name":"withdrawLimitEpochSeconds","type":"u32"},{"name":"nativeOpenInterestLimit","type":"u64"},{"name":"haltStates","type":{"array":[{"defined":"HaltStateV2"},5]}},{"name":"haltStatesPadding","type":{"array":[{"defined":"HaltStateV2"},20]}},{"name":"padding","type":{"array":["u8",338]}}]}},{"name":"Underlying","type":{"kind":"struct","fields":[{"name":"mint","type":"publicKey"}]}},{"name":"SettlementAccount","type":{"kind":"struct","fields":[{"name":"settlementPrice","type":"u64"},{"name":"strikes","type":{"array":["u64",23]}}]}},{"name":"PerpSyncQueue","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"head","type":"u16"},{"name":"length","type":"u16"},{"name":"queue","type":{"array":[{"defined":"AnchorDecimal"},600]}}]}},{"name":"ZetaGroup","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"noncePadding","type":{"array":["u8",2]}},{"name":"frontExpiryIndex","type":"u8"},{"name":"haltState","type":{"defined":"HaltState"}},{"name":"underlyingMint","type":"publicKey"},{"name":"oracle","type":"publicKey"},{"name":"greeks","type":"publicKey"},{"name":"pricingParameters","type":{"defined":"PricingParameters"}},{"name":"marginParameters","type":{"defined":"MarginParameters"}},{"name":"marginParametersPadding","type":{"array":["u8",104]}},{"name":"products","type":{"array":[{"defined":"Product"},46]}},{"name":"productsPadding","type":{"array":[{"defined":"Product"},91]}},{"name":"perp","type":{"defined":"Product"}},{"name":"expirySeries","type":{"array":[{"defined":"ExpirySeries"},2]}},{"name":"expirySeriesPadding","type":{"array":[{"defined":"ExpirySeries"},4]}},{"name":"deprecatedPadding","type":{"array":["u8",8]}},{"name":"asset","type":{"defined":"Asset"}},{"name":"expiryIntervalSeconds","type":"u32"},{"name":"newExpiryThresholdSeconds","type":"u32"},{"name":"perpParameters","type":{"defined":"PerpParameters"}},{"name":"perpSyncQueue","type":"publicKey"},{"name":"oracleBackupFeed","type":"publicKey"},{"name":"perpsOnly","type":"bool"},{"name":"flexUnderlying","type":"bool"},{"name":"padding","type":{"array":["u8",964]}}]}},{"name":"MarketNode","type":{"kind":"struct","fields":[{"name":"index","type":"u8"},{"name":"nonce","type":"u8"},{"name":"nodeUpdates","type":{"array":["i64",5]}},{"name":"interestUpdate","type":"i64"}]}},{"name":"SpreadAccount","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"nonce","type":"u8"},{"name":"balance","type":"u64"},{"name":"seriesExpiry","type":{"array":["u64",5]}},{"name":"seriesExpiryPadding","type":"u64"},{"name":"positions","type":{"array":[{"defined":"Position"},46]}},{"name":"positionsPadding","type":{"array":[{"defined":"Position"},92]}},{"name":"asset","type":{"defined":"Asset"}},{"name":"padding","type":{"array":["u8",262]}}]}},{"name":"CrossMarginAccountManager","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"authority","type":"publicKey"},{"name":"accounts","type":{"array":[{"defined":"CrossMarginAccountInfo"},25]}}]}},{"name":"CrossMarginAccount","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"delegatedPubkey","type":"publicKey"},{"name":"balance","type":"u64"},{"name":"subaccountIndex","type":"u8"},{"name":"nonce","type":"u8"},{"name":"forceCancelFlag","type":"bool"},{"name":"accountType","type":{"defined":"MarginAccountType"}},{"name":"openOrdersNonces","type":{"array":["u8",5]}},{"name":"openOrdersNoncesPadding","type":{"array":["u8",20]}},{"name":"rebalanceAmount","type":"i64"},{"name":"lastFundingDeltas","type":{"array":[{"defined":"AnchorDecimal"},5]}},{"name":"lastFundingDeltasPadding","type":{"array":[{"defined":"AnchorDecimal"},20]}},{"name":"productLedgers","type":{"array":[{"defined":"ProductLedger"},5]}},{"name":"productLedgersPadding","type":{"array":[{"defined":"ProductLedger"},20]}},{"name":"padding","type":{"array":["u8",2000]}}]}},{"name":"MarginAccount","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"nonce","type":"u8"},{"name":"balance","type":"u64"},{"name":"forceCancelFlag","type":"bool"},{"name":"openOrdersNonce","type":{"array":["u8",138]}},{"name":"seriesExpiry","type":{"array":["u64",5]}},{"name":"seriesExpiryPadding","type":"u64"},{"name":"productLedgers","type":{"array":[{"defined":"ProductLedger"},46]}},{"name":"productLedgersPadding","type":{"array":[{"defined":"ProductLedger"},91]}},{"name":"perpProductLedger","type":{"defined":"ProductLedger"}},{"name":"rebalanceAmount","type":"i64"},{"name":"asset","type":{"defined":"Asset"}},{"name":"accountType","type":{"defined":"MarginAccountType"}},{"name":"lastFundingDelta","type":{"defined":"AnchorDecimal"}},{"name":"delegatedPubkey","type":"publicKey"},{"name":"padding","type":{"array":["u8",338]}}]}},{"name":"SocializedLossAccount","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"overbankruptAmount","type":"u64"}]}},{"name":"WhitelistDepositAccount","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"userKey","type":"publicKey"}]}},{"name":"WhitelistInsuranceAccount","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"userKey","type":"publicKey"}]}},{"name":"InsuranceDepositAccount","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"amount","type":"u64"}]}},{"name":"WhitelistTradingFeesAccount","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"userKey","type":"publicKey"}]}},{"name":"ReferrerAccount","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"hasAlias","type":"bool"},{"name":"referrer","type":"publicKey"},{"name":"pendingRewards","type":"u64"},{"name":"claimedRewards","type":"u64"}]}},{"name":"ReferralAccount","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"referrer","type":"publicKey"},{"name":"user","type":"publicKey"},{"name":"timestamp","type":"u64"},{"name":"pendingRewards","type":"u64"},{"name":"claimedRewards","type":"u64"}]}},{"name":"ReferrerAlias","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"alias","type":{"array":["u8",15]}},{"name":"referrer","type":"publicKey"}]}}],"types":[{"name":"ProductGreeks","type":{"kind":"struct","fields":[{"name":"delta","type":"u64"},{"name":"vega","type":{"defined":"AnchorDecimal"}},{"name":"volatility","type":{"defined":"AnchorDecimal"}}]}},{"name":"AnchorDecimal","type":{"kind":"struct","fields":[{"name":"flags","type":"u32"},{"name":"hi","type":"u32"},{"name":"lo","type":"u32"},{"name":"mid","type":"u32"}]}},{"name":"HaltStateV2","type":{"kind":"struct","fields":[{"name":"halted","type":"bool"},{"name":"timestamp","type":"u64"},{"name":"spotPrice","type":"u64"},{"name":"marketCleaned","type":"bool"}]}},{"name":"HaltState","type":{"kind":"struct","fields":[{"name":"halted","type":"bool"},{"name":"spotPrice","type":"u64"},{"name":"timestamp","type":"u64"},{"name":"markPricesSet","type":{"array":["bool",2]}},{"name":"markPricesSetPadding","type":{"array":["bool",3]}},{"name":"perpMarkPriceSet","type":"bool"},{"name":"marketNodesCleaned","type":{"array":["bool",2]}},{"name":"marketNodesCleanedPadding","type":{"array":["bool",4]}},{"name":"marketCleaned","type":{"array":["bool",46]}},{"name":"marketCleanedPadding","type":{"array":["bool",91]}},{"name":"perpMarketCleaned","type":"bool"}]}},{"name":"PricingParameters","type":{"kind":"struct","fields":[{"name":"optionTradeNormalizer","type":{"defined":"AnchorDecimal"}},{"name":"futureTradeNormalizer","type":{"defined":"AnchorDecimal"}},{"name":"maxVolatilityRetreat","type":{"defined":"AnchorDecimal"}},{"name":"maxInterestRetreat","type":{"defined":"AnchorDecimal"}},{"name":"maxDelta","type":"u64"},{"name":"minDelta","type":"u64"},{"name":"minVolatility","type":"u64"},{"name":"maxVolatility","type":"u64"},{"name":"minInterestRate","type":"i64"},{"name":"maxInterestRate","type":"i64"}]}},{"name":"MarginParameters","type":{"kind":"struct","fields":[{"name":"futureMarginInitial","type":"u64"},{"name":"futureMarginMaintenance","type":"u64"}]}},{"name":"PerpParameters","type":{"kind":"struct","fields":[{"name":"minFundingRatePercent","type":"i64"},{"name":"maxFundingRatePercent","type":"i64"},{"name":"impactCashDelta","type":"u64"}]}},{"name":"ExpirySeries","type":{"kind":"struct","fields":[{"name":"activeTs","type":"u64"},{"name":"expiryTs","type":"u64"},{"name":"dirty","type":"bool"},{"name":"padding","type":{"array":["u8",15]}}]}},{"name":"Strike","type":{"kind":"struct","fields":[{"name":"isSet","type":"bool"},{"name":"value","type":"u64"}]}},{"name":"Product","type":{"kind":"struct","fields":[{"name":"market","type":"publicKey"},{"name":"strike","type":{"defined":"Strike"}},{"name":"dirty","type":"bool"},{"name":"kind","type":{"defined":"Kind"}}]}},{"name":"Position","type":{"kind":"struct","fields":[{"name":"size","type":"i64"},{"name":"costOfTrades","type":"u64"}]}},{"name":"OrderState","type":{"kind":"struct","fields":[{"name":"closingOrders","type":"u64"},{"name":"openingOrders","type":{"array":["u64",2]}}]}},{"name":"ProductLedger","type":{"kind":"struct","fields":[{"name":"position","type":{"defined":"Position"}},{"name":"orderState","type":{"defined":"OrderState"}}]}},{"name":"CrossMarginAccountInfo","type":{"kind":"struct","fields":[{"name":"initialized","type":"bool"},{"name":"name","type":{"array":["u8",10]}}]}},{"name":"HaltStateArgs","type":{"kind":"struct","fields":[{"name":"asset","type":{"defined":"Asset"}},{"name":"spotPrice","type":"u64"},{"name":"timestamp","type":"u64"}]}},{"name":"HaltArgs","type":{"kind":"struct","fields":[{"name":"spotPrices","type":{"array":["u64",5]}},{"name":"timestamp","type":"u64"}]}},{"name":"UpdateVolatilityArgs","type":{"kind":"struct","fields":[{"name":"expiryIndex","type":"u8"},{"name":"volatility","type":{"array":["u64",5]}}]}},{"name":"UpdateInterestRateArgs","type":{"kind":"struct","fields":[{"name":"expiryIndex","type":"u8"},{"name":"interestRate","type":"i64"}]}},{"name":"SetReferralsRewardsArgs","type":{"kind":"struct","fields":[{"name":"referralsAccountKey","type":"publicKey"},{"name":"pendingRewards","type":"u64"},{"name":"overwrite","type":"bool"}]}},{"name":"ExpireSeriesOverrideArgs","type":{"kind":"struct","fields":[{"name":"settlementNonce","type":"u8"},{"name":"settlementPrice","type":"u64"}]}},{"name":"InitializeMarketArgs","type":{"kind":"struct","fields":[{"name":"asset","type":{"defined":"Asset"}},{"name":"marketNonce","type":"u8"},{"name":"baseMintNonce","type":"u8"},{"name":"quoteMintNonce","type":"u8"},{"name":"zetaBaseVaultNonce","type":"u8"},{"name":"zetaQuoteVaultNonce","type":"u8"},{"name":"dexBaseVaultNonce","type":"u8"},{"name":"dexQuoteVaultNonce","type":"u8"},{"name":"vaultSignerNonce","type":"u64"}]}},{"name":"InitializeStateArgs","type":{"kind":"struct","fields":[{"name":"stateNonce","type":"u8"},{"name":"serumNonce","type":"u8"},{"name":"mintAuthNonce","type":"u8"},{"name":"strikeInitializationThresholdSeconds","type":"u32"},{"name":"pricingFrequencySeconds","type":"u32"},{"name":"liquidatorLiquidationPercentage","type":"u32"},{"name":"insuranceVaultLiquidationPercentage","type":"u32"},{"name":"nativeD1TradeFeePercentage","type":"u64"},{"name":"nativeD1UnderlyingFeePercentage","type":"u64"},{"name":"nativeOptionTradeFeePercentage","type":"u64"},{"name":"nativeOptionUnderlyingFeePercentage","type":"u64"},{"name":"nativeWhitelistUnderlyingFeePercentage","type":"u64"},{"name":"nativeDepositLimit","type":"u64"},{"name":"expirationThresholdSeconds","type":"u32"},{"name":"positionMovementFeeBps","type":"u8"},{"name":"marginConcessionPercentage","type":"u8"},{"name":"maxPerpDeltaAgeSeconds","type":"u16"},{"name":"nativeWithdrawLimit","type":"u64"},{"name":"withdrawLimitEpochSeconds","type":"u32"},{"name":"nativeOpenInterestLimit","type":"u64"}]}},{"name":"InitializeMarketNodeArgs","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"index","type":"u8"}]}},{"name":"OverrideExpiryArgs","type":{"kind":"struct","fields":[{"name":"expiryIndex","type":"u8"},{"name":"activeTs","type":"u64"},{"name":"expiryTs","type":"u64"}]}},{"name":"UpdateStateArgs","type":{"kind":"struct","fields":[{"name":"strikeInitializationThresholdSeconds","type":"u32"},{"name":"pricingFrequencySeconds","type":"u32"},{"name":"liquidatorLiquidationPercentage","type":"u32"},{"name":"insuranceVaultLiquidationPercentage","type":"u32"},{"name":"nativeD1TradeFeePercentage","type":"u64"},{"name":"nativeD1UnderlyingFeePercentage","type":"u64"},{"name":"nativeOptionTradeFeePercentage","type":"u64"},{"name":"nativeOptionUnderlyingFeePercentage","type":"u64"},{"name":"nativeWhitelistUnderlyingFeePercentage","type":"u64"},{"name":"nativeDepositLimit","type":"u64"},{"name":"expirationThresholdSeconds","type":"u32"},{"name":"positionMovementFeeBps","type":"u8"},{"name":"marginConcessionPercentage","type":"u8"},{"name":"maxPerpDeltaAgeSeconds","type":"u16"},{"name":"nativeWithdrawLimit","type":"u64"},{"name":"withdrawLimitEpochSeconds","type":"u32"},{"name":"nativeOpenInterestLimit","type":"u64"}]}},{"name":"UpdatePricingParametersArgs","type":{"kind":"struct","fields":[{"name":"optionTradeNormalizer","type":"u64"},{"name":"futureTradeNormalizer","type":"u64"},{"name":"maxVolatilityRetreat","type":"u64"},{"name":"maxInterestRetreat","type":"u64"},{"name":"minDelta","type":"u64"},{"name":"maxDelta","type":"u64"},{"name":"minInterestRate","type":"i64"},{"name":"maxInterestRate","type":"i64"},{"name":"minVolatility","type":"u64"},{"name":"maxVolatility","type":"u64"}]}},{"name":"UpdateMarginParametersArgs","type":{"kind":"struct","fields":[{"name":"futureMarginInitial","type":"u64"},{"name":"futureMarginMaintenance","type":"u64"}]}},{"name":"UpdatePerpParametersArgs","type":{"kind":"struct","fields":[{"name":"minFundingRatePercent","type":"i64"},{"name":"maxFundingRatePercent","type":"i64"},{"name":"perpImpactCashDelta","type":"u64"}]}},{"name":"InitializeZetaGroupArgs","type":{"kind":"struct","fields":[{"name":"perpsOnly","type":"bool"},{"name":"flexUnderlying","type":"bool"},{"name":"assetOverride","type":{"option":{"defined":"Asset"}}},{"name":"zetaGroupNonce","type":"u8"},{"name":"underlyingNonce","type":"u8"},{"name":"greeksNonce","type":"u8"},{"name":"vaultNonce","type":"u8"},{"name":"insuranceVaultNonce","type":"u8"},{"name":"socializedLossAccountNonce","type":"u8"},{"name":"perpSyncQueueNonce","type":"u8"},{"name":"interestRate","type":"i64"},{"name":"volatility","type":{"array":["u64",5]}},{"name":"optionTradeNormalizer","type":"u64"},{"name":"futureTradeNormalizer","type":"u64"},{"name":"maxVolatilityRetreat","type":"u64"},{"name":"maxInterestRetreat","type":"u64"},{"name":"maxDelta","type":"u64"},{"name":"minDelta","type":"u64"},{"name":"minInterestRate","type":"i64"},{"name":"maxInterestRate","type":"i64"},{"name":"minVolatility","type":"u64"},{"name":"maxVolatility","type":"u64"},{"name":"futureMarginInitial","type":"u64"},{"name":"futureMarginMaintenance","type":"u64"},{"name":"expiryIntervalSeconds","type":"u32"},{"name":"newExpiryThresholdSeconds","type":"u32"},{"name":"minFundingRatePercent","type":"i64"},{"name":"maxFundingRatePercent","type":"i64"},{"name":"perpImpactCashDelta","type":"u64"}]}},{"name":"UpdateZetaGroupExpiryArgs","type":{"kind":"struct","fields":[{"name":"expiryIntervalSeconds","type":"u32"},{"name":"newExpiryThresholdSeconds","type":"u32"}]}},{"name":"UpdateGreeksArgs","type":{"kind":"struct","fields":[{"name":"index","type":"u8"},{"name":"theo","type":"u64"},{"name":"delta","type":"u32"},{"name":"gamma","type":"u32"},{"name":"volatility","type":"u32"}]}},{"name":"PositionMovementArg","type":{"kind":"struct","fields":[{"name":"index","type":"u8"},{"name":"size","type":"i64"}]}},{"name":"UpdateZetaPricingPubkeysArgs","type":{"kind":"struct","fields":[{"name":"asset","type":{"defined":"Asset"}},{"name":"oracle","type":"publicKey"},{"name":"oracleBackupFeed","type":"publicKey"},{"name":"market","type":"publicKey"},{"name":"perpSyncQueue","type":"publicKey"},{"name":"zetaGroupKey","type":"publicKey"}]}},{"name":"InitializeZetaPricingArgs","type":{"kind":"struct","fields":[{"name":"minFundingRatePercent","type":"i64"},{"name":"maxFundingRatePercent","type":"i64"},{"name":"perpImpactCashDelta","type":"u64"},{"name":"marginInitial","type":"u64"},{"name":"marginMaintenance","type":"u64"},{"name":"pricingNonce","type":"u8"}]}},{"name":"ExpirySeriesStatus","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Initialized"},{"name":"Live"},{"name":"Expired"},{"name":"ExpiredDirty"}]}},{"name":"Kind","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Call"},{"name":"Put"},{"name":"Future"},{"name":"Perp"}]}},{"name":"OrderType","type":{"kind":"enum","variants":[{"name":"Limit"},{"name":"PostOnly"},{"name":"FillOrKill"},{"name":"ImmediateOrCancel"},{"name":"PostOnlySlide"}]}},{"name":"Side","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Bid"},{"name":"Ask"}]}},{"name":"Asset","type":{"kind":"enum","variants":[{"name":"SOL"},{"name":"BTC"},{"name":"ETH"},{"name":"APT"},{"name":"ARB"},{"name":"UNDEFINED"}]}},{"name":"MovementType","type":{"kind":"enum","variants":[{"name":"Undefined"},{"name":"Lock"},{"name":"Unlock"}]}},{"name":"TreasuryMovementType","type":{"kind":"enum","variants":[{"name":"Undefined"},{"name":"ToTreasuryFromInsurance"},{"name":"ToInsuranceFromTreasury"},{"name":"ToTreasuryFromReferralsRewards"},{"name":"ToReferralsRewardsFromTreasury"}]}},{"name":"OrderCompleteType","type":{"kind":"enum","variants":[{"name":"Cancel"},{"name":"Fill"},{"name":"Booted"}]}},{"name":"MarginRequirement","type":{"kind":"enum","variants":[{"name":"Initial"},{"name":"Maintenance"},{"name":"MaintenanceIncludingOrders"},{"name":"MarketMakerConcession"}]}},{"name":"MarginAccountType","type":{"kind":"enum","variants":[{"name":"Normal"},{"name":"MarketMaker"}]}},{"name":"PlaceOrderType","type":{"kind":"enum","variants":[{"name":"PlaceOrder"},{"name":"PlacePerpOrder"}]}},{"name":"ValidationType","type":{"kind":"enum","variants":[{"name":"Place"},{"name":"Cancel"},{"name":"OpenOrders"}]}},{"name":"TraitType","type":{"kind":"enum","variants":[{"name":"MarginAccount"},{"name":"CrossMarginAccount"}]}}],"events":[{"name":"TradeEvent","fields":[{"name":"marginAccount","type":"publicKey","index":false},{"name":"index","type":"u8","index":false},{"name":"size","type":"u64","index":false},{"name":"costOfTrades","type":"u64","index":false},{"name":"isBid","type":"bool","index":false},{"name":"clientOrderId","type":"u64","index":false},{"name":"orderId","type":"u128","index":false}]},{"name":"TradeEventV2","fields":[{"name":"marginAccount","type":"publicKey","index":false},{"name":"index","type":"u8","index":false},{"name":"size","type":"u64","index":false},{"name":"costOfTrades","type":"u64","index":false},{"name":"isBid","type":"bool","index":false},{"name":"clientOrderId","type":"u64","index":false},{"name":"orderId","type":"u128","index":false},{"name":"asset","type":"u8","index":false},{"name":"user","type":"publicKey","index":false},{"name":"isTaker","type":"bool","index":false},{"name":"sequenceNumber","type":"u64","index":false}]},{"name":"TradeEventV3","fields":[{"name":"marginAccount","type":"publicKey","index":false},{"name":"index","type":"u8","index":false},{"name":"size","type":"u64","index":false},{"name":"costOfTrades","type":"u64","index":false},{"name":"isBid","type":"bool","index":false},{"name":"clientOrderId","type":"u64","index":false},{"name":"orderId","type":"u128","index":false},{"name":"asset","type":{"defined":"Asset"},"index":false},{"name":"user","type":"publicKey","index":false},{"name":"isTaker","type":"bool","index":false},{"name":"sequenceNumber","type":"u64","index":false}]},{"name":"PositionMovementEvent","fields":[{"name":"netBalanceTransfer","type":"i64","index":false},{"name":"marginAccountBalance","type":"u64","index":false},{"name":"spreadAccountBalance","type":"u64","index":false},{"name":"movementFees","type":"u64","index":false}]},{"name":"PlaceOrderEvent","fields":[{"name":"fee","type":"u64","index":false},{"name":"oraclePrice","type":"u64","index":false},{"name":"orderId","type":"u128","index":false},{"name":"expiryTs","type":"u64","index":false}]},{"name":"LiquidationEvent","fields":[{"name":"liquidatorReward","type":"u64","index":false},{"name":"insuranceReward","type":"u64","index":false},{"name":"costOfTrades","type":"u64","index":false},{"name":"size","type":"i64","index":false},{"name":"remainingLiquidateeBalance","type":"u64","index":false},{"name":"remainingLiquidatorBalance","type":"u64","index":false},{"name":"markPrice","type":"u64","index":false},{"name":"underlyingPrice","type":"u64","index":false},{"name":"liquidatee","type":"publicKey","index":false},{"name":"liquidator","type":"publicKey","index":false},{"name":"asset","type":{"defined":"Asset"},"index":false}]},{"name":"OrderCompleteEvent","fields":[{"name":"marginAccount","type":"publicKey","index":false},{"name":"user","type":"publicKey","index":false},{"name":"asset","type":{"defined":"Asset"},"index":false},{"name":"marketIndex","type":"u8","index":false},{"name":"side","type":{"defined":"Side"},"index":false},{"name":"unfilledSize","type":"u64","index":false},{"name":"orderId","type":"u128","index":false},{"name":"clientOrderId","type":"u64","index":false},{"name":"orderCompleteType","type":{"defined":"OrderCompleteType"},"index":false}]},{"name":"ApplyFundingEvent","fields":[{"name":"marginAccount","type":"publicKey","index":false},{"name":"user","type":"publicKey","index":false},{"name":"asset","type":{"defined":"Asset"},"index":false},{"name":"balanceChange","type":"i64","index":false},{"name":"remainingBalance","type":"u64","index":false},{"name":"fundingRate","type":"i64","index":false},{"name":"oraclePrice","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"DepositOverflow","msg":"Deposit overflow"},{"code":6001,"name":"Unreachable","msg":"Unreachable"},{"code":6002,"name":"FailedInitialMarginRequirement","msg":"Failed initial margin requirement"},{"code":6003,"name":"LiquidatorFailedMarginRequirement","msg":"Liquidator failed margin requirement"},{"code":6004,"name":"CannotLiquidateOwnAccount","msg":"Cannot liquidate own account"},{"code":6005,"name":"CrankInvalidRemainingAccounts","msg":"Invalid cranking remaining accounts"},{"code":6006,"name":"IncorrectTickSize","msg":"Incorrect tick size"},{"code":6007,"name":"ZeroPrice","msg":"ZeroPrice"},{"code":6008,"name":"ZeroSize","msg":"ZeroSize"},{"code":6009,"name":"ZeroWithdrawableBalance","msg":"Zero withdrawable balance"},{"code":6010,"name":"DepositAmountExceeded","msg":"Deposit amount exceeds limit and user is not whitelisted"},{"code":6011,"name":"WithdrawalAmountExceedsWithdrawableBalance","msg":"Withdrawal amount exceeds withdrawable balance"},{"code":6012,"name":"AccountHasSufficientMarginPostCancels","msg":"Account has sufficient margin post cancels"},{"code":6013,"name":"OverBankrupt","msg":"Over bankrupt"},{"code":6014,"name":"AccountHasSufficientMargin","msg":"Account has sufficient margin"},{"code":6015,"name":"UserHasNoActiveOrders","msg":"User has no active orders"},{"code":6016,"name":"InvalidExpirationInterval","msg":"Invalid expiration interval"},{"code":6017,"name":"ProductMarketsAlreadyInitialized","msg":"Product markets already initialized"},{"code":6018,"name":"InvalidProductMarketKey","msg":"Invalid product market key"},{"code":6019,"name":"MarketNotLive","msg":"Market not live"},{"code":6020,"name":"MarketPricingNotReady","msg":"Market pricing not ready"},{"code":6021,"name":"UserHasRemainingOrdersOnExpiredMarket","msg":"User has remaining orders on expired market"},{"code":6022,"name":"InvalidSeriesExpiration","msg":"Invalid series expiration"},{"code":6023,"name":"InvalidExpiredOrderCancel","msg":"Invalid expired order cancel"},{"code":6024,"name":"NoMarketsToAdd","msg":"No markets to add"},{"code":6025,"name":"UserHasUnsettledPositions","msg":"User has unsettled positions"},{"code":6026,"name":"NoMarginAccountsToSettle","msg":"No margin accounts to settle"},{"code":6027,"name":"CannotSettleUserWithActiveOrders","msg":"Cannot settle users with active orders"},{"code":6028,"name":"OrderbookNotEmpty","msg":"Orderbook not empty"},{"code":6029,"name":"InvalidNumberOfAccounts","msg":"Invalid number of accounts"},{"code":6030,"name":"InvalidMarketAccounts","msg":"Bids or Asks don't match the Market"},{"code":6031,"name":"ProductStrikeUninitialized","msg":"Product strike uninitialized"},{"code":6032,"name":"PricingNotUpToDate","msg":"Pricing not up to date"},{"code":6033,"name":"RetreatsAreStale","msg":"Retreats are stale"},{"code":6034,"name":"ProductDirty","msg":"Product dirty"},{"code":6035,"name":"ProductStrikesInitialized","msg":"Product strikes initialized"},{"code":6036,"name":"StrikeInitializationNotReady","msg":"Strike initialization not ready"},{"code":6037,"name":"UnsupportedKind","msg":"Unsupported kind"},{"code":6038,"name":"InvalidZetaGroup","msg":"Invalid zeta group"},{"code":6039,"name":"InvalidMarginAccount","msg":"Invalid margin account"},{"code":6040,"name":"InvalidGreeksAccount","msg":"Invalid greeks account"},{"code":6041,"name":"InvalidSettlementAccount","msg":"Invalid settlement account"},{"code":6042,"name":"InvalidCancelAuthority","msg":"Invalid cancel authority"},{"code":6043,"name":"CannotUpdatePricingAfterExpiry","msg":"Cannot update pricing after expiry"},{"code":6044,"name":"LoadAccountDiscriminatorAlreadySet","msg":"Account discriminator already set"},{"code":6045,"name":"AccountAlreadyInitialized","msg":"Account already initialized"},{"code":6046,"name":"GreeksAccountSeedsMismatch","msg":"Greeks account seeds mismatch"},{"code":6047,"name":"ZetaGroupAccountSeedsMismatch","msg":"Zeta group account seeds mismatch"},{"code":6048,"name":"MarginAccountSeedsMismatch","msg":"Margin account seeds mismatch"},{"code":6049,"name":"OpenOrdersAccountSeedsMismatch","msg":"Open orders account seeds mismatch"},{"code":6050,"name":"MarketNodeAccountSeedsMismatch","msg":"Market node seeds mismatch"},{"code":6051,"name":"UserTradingFeeWhitelistAccountSeedsMismatch","msg":"User trading fee whitelist account seeds mismatch"},{"code":6052,"name":"UserDepositWhitelistAccountSeedsMismatch","msg":"User deposit whitelist account seeds mismatch"},{"code":6053,"name":"MarketIndexesUninitialized","msg":"Market indexes uninitialized"},{"code":6054,"name":"MarketIndexesAlreadyInitialized","msg":"Market indexes already initialized"},{"code":6055,"name":"CannotGetUnsetStrike","msg":"Cannot get unset strike"},{"code":6056,"name":"CannotSetInitializedStrike","msg":"Cannot set initialized strike"},{"code":6057,"name":"CannotResetUninitializedStrike","msg":"Cannot set initialized strike"},{"code":6058,"name":"CrankMarginAccountNotMutable","msg":"CrankMarginAccountNotMutable"},{"code":6059,"name":"InvalidAdminSigner","msg":"InvalidAdminSigner"},{"code":6060,"name":"UserHasActiveOrders","msg":"User still has active orders"},{"code":6061,"name":"UserForceCancelInProgress","msg":"User has a force cancel in progress"},{"code":6062,"name":"FailedPriceBandCheck","msg":"Failed price band check"},{"code":6063,"name":"UnsortedOpenOrdersAccounts","msg":"Unsorted open orders accounts"},{"code":6064,"name":"AccountNotMutable","msg":"Account not mutable"},{"code":6065,"name":"AccountDiscriminatorMismatch","msg":"Account discriminator mismatch"},{"code":6066,"name":"InvalidMarketNodeIndex","msg":"Invalid market node index"},{"code":6067,"name":"InvalidMarketNode","msg":"Invalid market node"},{"code":6068,"name":"LUTOutOfBounds","msg":"Lut out of bounds"},{"code":6069,"name":"RebalanceInsuranceInvalidRemainingAccounts","msg":"Rebalance insurance vault with no margin accounts"},{"code":6070,"name":"InvalidMintDecimals","msg":"Invalid mint decimals"},{"code":6071,"name":"InvalidZetaGroupOracle","msg":"Invalid oracle for this zeta group"},{"code":6072,"name":"InvalidZetaGroupDepositMint","msg":"Invalid zeta group deposit mint"},{"code":6073,"name":"InvalidZetaGroupRebalanceMint","msg":"Invalid zeta group rebalance insurance vault mint"},{"code":6074,"name":"InvalidDepositAmount","msg":"Invalid deposit amount"},{"code":6075,"name":"InvalidTokenAccountOwner","msg":"Invalid token account owner"},{"code":6076,"name":"InvalidWithdrawAmount","msg":"Invalid withdraw amount"},{"code":6077,"name":"InvalidDepositRemainingAccounts","msg":"Invalid number of remaining accounts in deposit"},{"code":6078,"name":"InvalidPlaceOrderRemainingAccounts","msg":"Invalid number of remaining accounts in place order"},{"code":6079,"name":"ClientOrderIdCannotBeZero","msg":"ClientOrderIdCannotBeZero"},{"code":6080,"name":"ZetaGroupHalted","msg":"Zeta group halted"},{"code":6081,"name":"ZetaGroupNotHalted","msg":"Zeta group not halted"},{"code":6082,"name":"HaltMarkPriceNotSet","msg":"Halt mark price not set"},{"code":6083,"name":"HaltMarketsNotCleaned","msg":"Halt markets not cleaned"},{"code":6084,"name":"HaltMarketNodesNotCleaned","msg":"Halt market nodes not cleaned"},{"code":6085,"name":"CannotExpireOptionsAfterExpirationThreshold","msg":"Cannot expire options after expiration threshold"},{"code":6086,"name":"PostOnlyInCross","msg":"Post only order in cross"},{"code":6087,"name":"FillOrKillNotFullSize","msg":"Fill or kill order was not filled for full size"},{"code":6088,"name":"InvalidOpenOrdersMapOwner","msg":"Invalid open orders map owner"},{"code":6089,"name":"AccountDidNotSerialize","msg":"Failed to serialize the account"},{"code":6090,"name":"OpenOrdersWithNonEmptyPositions","msg":"Cannot close open orders account with non empty positions"},{"code":6091,"name":"CannotCloseNonEmptyMarginAccount","msg":"Cannot close margin account that is not empty"},{"code":6092,"name":"InvalidTagLength","msg":"Invalid tag length"},{"code":6093,"name":"NakedShortCallIsNotAllowed","msg":"Naked short call is not allowed"},{"code":6094,"name":"InvalidSpreadAccount","msg":"Invalid spread account"},{"code":6095,"name":"CannotCloseNonEmptySpreadAccount","msg":"Cannot close non empty spread account"},{"code":6096,"name":"SpreadAccountSeedsMismatch","msg":"Spread account seeds mismatch"},{"code":6097,"name":"SpreadAccountHasUnsettledPositions","msg":"Spread account seeds mismatch"},{"code":6098,"name":"SpreadAccountInvalidExpirySeriesState","msg":"Spread account invalid expiry series state"},{"code":6099,"name":"InsufficientFundsToCollateralizeSpreadAccount","msg":"Insufficient funds to collateralize spread account"},{"code":6100,"name":"FailedMaintenanceMarginRequirement","msg":"Failed maintenance margin requirement"},{"code":6101,"name":"InvalidMovement","msg":"Invalid movement"},{"code":6102,"name":"MovementOnExpiredSeries","msg":"Movement on expired series"},{"code":6103,"name":"InvalidMovementSize","msg":"Invalid movement size"},{"code":6104,"name":"ExceededMaxPositionMovements","msg":"Exceeded max position movements"},{"code":6105,"name":"ExceededMaxSpreadAccountContracts","msg":"Exceeded max spread account contracts"},{"code":6106,"name":"OraclePriceIsInvalid","msg":"Fetched oracle price is invalid"},{"code":6107,"name":"InvalidUnderlyingMint","msg":"Provided underlying mint address is invalid"},{"code":6108,"name":"InvalidReferrerAlias","msg":"Invalid referrer alias - Invalid length"},{"code":6109,"name":"ReferrerAlreadyHasAlias","msg":"Referrer already has alias"},{"code":6110,"name":"InvalidTreasuryMovementAmount","msg":"Invalid treasury movement amount"},{"code":6111,"name":"InvalidReferralsAdminSigner","msg":"Invalid referrals admin signer"},{"code":6112,"name":"InvalidSetReferralsRewardsRemainingAccounts","msg":"Invalid set referrals rewards remaining accounts"},{"code":6113,"name":"SetReferralsRewardsAccountNotMutable","msg":"Referrals account not mutable"},{"code":6114,"name":"InvalidClaimReferralsRewardsAmount","msg":"Invalid claim referrals rewards: not enough in refererals rewards wallet"},{"code":6115,"name":"InvalidClaimReferralsRewardsAccount","msg":"Invalid claim referrals rewards: referrals account is not a referral or referrer account"},{"code":6116,"name":"ReferralAccountSeedsMismatch","msg":"Referral account seeds mismatch"},{"code":6117,"name":"ReferrerAccountSeedsMismatch","msg":"Referrer account seeds mismatch"},{"code":6118,"name":"ProtectedMmMarginAccount","msg":"Market maker accounts are protected from liquidation"},{"code":6119,"name":"CannotWithdrawWithOpenOrders","msg":"Cannot withdraw with open orders"},{"code":6120,"name":"FundingRateNotUpToDate","msg":"Perp funding rate not up to date"},{"code":6121,"name":"PerpSyncQueueFull","msg":"Perp taker/maker sync queue is full"},{"code":6122,"name":"PerpSyncQueueAccountSeedsMismatch","msg":"PerpSyncQueue account seeds mismatch"},{"code":6123,"name":"PerpSyncQueueEmpty","msg":"Program tried to pop from an empty perpSyncQueue"},{"code":6124,"name":"InvalidNonPerpMarket","msg":"Perp product index given in placeOrder, use placePerpOrder"},{"code":6125,"name":"InvalidPerpMarket","msg":"Non-perp product index given in placePerpOrder, use placeOrder"},{"code":6126,"name":"CannotInitializePerpMarketNode","msg":"Not allowed to initialize market node for a perp market"},{"code":6127,"name":"DeprecatedInstruction","msg":"Instruction is deprecated, please use the newer version"},{"code":6128,"name":"ForceCancelExpiredTIFOrdersOnly","msg":"Can only force cancel expired TIF orders"},{"code":6129,"name":"InvalidPlaceOrderAuthority","msg":"Invalid place order authority"},{"code":6130,"name":"InvalidOpenOrdersAuthority","msg":"Invalid open orders authority"},{"code":6131,"name":"InsuranceVaultSeedsMismatch","msg":"Insurance vault seeds mismatch"},{"code":6132,"name":"OpenInterestLimitBreach","msg":"Open interest limit breach, decrease your position"},{"code":6133,"name":"WithdrawLimitBreach","msg":"Withdraw limit breach, wait to withdraw more"},{"code":6134,"name":"InvalidPricingOracle","msg":"Invalid oracle for this pricing account"},{"code":6135,"name":"PricingAccountSeedsMismatch","msg":"Pricing account seeds mismatch"},{"code":6136,"name":"ZetaHalted","msg":"Zeta exchange is halted"},{"code":6137,"name":"ZetaNotHalted","msg":"Zeta exchange is not halted"},{"code":6138,"name":"NotFreshCrossMarginAccount","msg":"Cross margin account is not unused, close it and make a new one"},{"code":6139,"name":"CannotCloseNonEmptyMarginAccountManager","msg":"Cannot close margin account manager that is not empty"},{"code":6140,"name":"CannotMigrateWithOpenOrders","msg":"Cannot migrate to cross margin account with open orders, close all open orders"},{"code":6141,"name":"InvalidMarginAccountType","msg":"Invalid margin account type - account is not MarginAccount or CrossMarginAccount"},{"code":6142,"name":"MarginAccountAssetMismatch","msg":"Margin account asset mismatched with instruction argument asset"},{"code":6143,"name":"FeatureUnavailable","msg":"Feature is not available yet"},{"code":6144,"name":"MarginAccountCannotLiquidateCrossMarginAccount","msg":"MarginAccount cannot liquidate CrossMarginAccount"}]} \ No newline at end of file diff --git a/kb-app-demo/Cargo.toml b/kb-app-demo/Cargo.toml new file mode 100644 index 0000000..ea4c03c --- /dev/null +++ b/kb-app-demo/Cargo.toml @@ -0,0 +1,23 @@ +# file: kb-app-demo/Cargo.toml +# version: 1 + +[package] +name = "kb-app-demo" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb-config = { path = "../kb-config" } +kb-core = { path = "../kb-core" } +kb-lib = { path = "../kb-lib" } +kb-logging = { path = "../kb-logging" } +kb-pipeline = { path = "../kb-pipeline" } +kb-program-ids = { path = "../kb-program-ids" } +kb-rpc = { path = "../kb-rpc" } +kb-store = { path = "../kb-store" } +kb-wallet = { path = "../kb-wallet" } + +[lints] +workspace = true diff --git a/kb-app-demo/src/main.rs b/kb-app-demo/src/main.rs new file mode 100644 index 0000000..0a569df --- /dev/null +++ b/kb-app-demo/src/main.rs @@ -0,0 +1,14 @@ +// file: kb-app-demo/src/main.rs +// version: 1 + +#![forbid(unsafe_code)] +#![deny(unreachable_pub)] +#![warn(missing_docs)] + +//! Minimal migration smoke-test application. + +fn main() { + println!("khadhroony-bot3 workspace scaffold"); + println!("logging target: {}", kb_logging::tracing_target()); + println!("pipeline: {}", kb_pipeline::migration_status()); +} diff --git a/kb-config/Cargo.toml b/kb-config/Cargo.toml new file mode 100644 index 0000000..79c2377 --- /dev/null +++ b/kb-config/Cargo.toml @@ -0,0 +1,15 @@ +# file: kb-config/Cargo.toml +# version: 1 + +[package] +name = "kb-config" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb-core = { path = "../kb-core" } + +[lints] +workspace = true diff --git a/kb-config/src/lib.rs b/kb-config/src/lib.rs new file mode 100644 index 0000000..3a838fd --- /dev/null +++ b/kb-config/src/lib.rs @@ -0,0 +1,23 @@ +// file: kb-config/src/lib.rs +// version: 1 + +#![forbid(unsafe_code)] +#![deny(unreachable_pub)] +#![warn(missing_docs)] + +//! Configuration contracts for Khadhroony Bot3. + +/// Minimal application configuration used during the migration. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AppConfig { + /// Active profile name. + pub active_profile: std::string::String, +} + +impl std::default::Default for AppConfig { + fn default() -> Self { + return Self { + active_profile: std::string::String::from("development"), + }; + } +} diff --git a/kb-core/Cargo.toml b/kb-core/Cargo.toml new file mode 100644 index 0000000..2e41b7e --- /dev/null +++ b/kb-core/Cargo.toml @@ -0,0 +1,12 @@ +# file: kb-core/Cargo.toml +# version: 1 + +[package] +name = "kb-core" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[lints] +workspace = true diff --git a/kb-core/README.md b/kb-core/README.md new file mode 100644 index 0000000..5848d3c --- /dev/null +++ b/kb-core/README.md @@ -0,0 +1,23 @@ + + + +# kb-core + +Ce crate fournit les types minimaux partagés, les erreurs communes et l'identité des modules. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. + +## Erreurs communes + +`kb-core::Error` est l'enum d'erreur commune du workspace. Elle fournit des familles stables pour configuration, I/O, JSON, tracing, Tauri, HTTP, WebSocket, base de données, état invalide, client non connecté, fonctionnalité non implémentée et erreurs personnalisées. + +`kb-core::Error::new(code, message)` reste disponible comme compatibilité courte pendant que les domaines sont progressivement spécialisés. diff --git a/kb-core/src/error.rs b/kb-core/src/error.rs new file mode 100644 index 0000000..ce21e38 --- /dev/null +++ b/kb-core/src/error.rs @@ -0,0 +1,197 @@ +// file: kb-core/src/error.rs +// version: 6 + +//! Error and result primitives shared by the workspace. + +/// Workspace-wide result alias. +pub type Result = std::result::Result; + +/// Workspace-wide explicit error type. +/// +/// The project avoids generic catch-all error crates, so this enum centralizes +/// the first stable error families used by core, configuration, logging, +/// applications, RPC, stores and execution modules. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Error { + /// Configuration or profile validation error. + Config(std::string::String), + /// Filesystem or standard I/O error. + Io(std::string::String), + /// JSON serialization, deserialization or schema validation error. + Json(std::string::String), + /// Tracing initialization or logging runtime error. + Tracing(std::string::String), + /// Tauri application or WebView runtime error. + Tauri(std::string::String), + /// HTTP transport error. + Http(std::string::String), + /// WebSocket transport error. + Ws(std::string::String), + /// Database or storage backend error. + Db(std::string::String), + /// Invalid internal state error. + InvalidState(std::string::String), + /// Operation requested while a client or subsystem is not connected. + NotConnected(std::string::String), + /// Feature intentionally scheduled for a later version. + NotImplemented(std::string::String), + /// Stable custom error code used while domains are still being split. + Custom { + /// Stable custom error code. + code: std::string::String, + /// Human-readable custom error message. + message: std::string::String, + }, +} + +impl crate::Error { + /// Creates a custom error value with a stable code. + pub fn new( + code: impl std::convert::Into, + message: impl std::convert::Into, + ) -> Self { + return Self::Custom { + code: code.into(), + message: message.into(), + }; + } + + /// Creates a configuration error value. + pub fn config(message: impl std::convert::Into) -> Self { + return Self::Config(message.into()); + } + + /// Creates an I/O error value. + pub fn io(message: impl std::convert::Into) -> Self { + return Self::Io(message.into()); + } + + /// Creates a JSON error value. + pub fn json(message: impl std::convert::Into) -> Self { + return Self::Json(message.into()); + } + + /// Creates a tracing error value. + pub fn tracing(message: impl std::convert::Into) -> Self { + return Self::Tracing(message.into()); + } + + /// Creates a Tauri runtime error value. + pub fn tauri(message: impl std::convert::Into) -> Self { + return Self::Tauri(message.into()); + } + + /// Creates an HTTP transport error value. + pub fn http(message: impl std::convert::Into) -> Self { + return Self::Http(message.into()); + } + + /// Creates a WebSocket transport error value. + pub fn ws(message: impl std::convert::Into) -> Self { + return Self::Ws(message.into()); + } + + /// Creates a database error value. + pub fn db(message: impl std::convert::Into) -> Self { + return Self::Db(message.into()); + } + + /// Creates an invalid state error value. + pub fn invalid_state(message: impl std::convert::Into) -> Self { + return Self::InvalidState(message.into()); + } + + /// Creates a not-connected error value. + pub fn not_connected(message: impl std::convert::Into) -> Self { + return Self::NotConnected(message.into()); + } + + /// Creates a not-implemented error value. + pub fn not_implemented(message: impl std::convert::Into) -> Self { + return Self::NotImplemented(message.into()); + } + + /// Returns a stable code for logs and UI diagnostics. + pub fn code(&self) -> &str { + return match self { + Self::Config(_) => "config", + Self::Io(_) => "io", + Self::Json(_) => "json", + Self::Tracing(_) => "tracing", + Self::Tauri(_) => "tauri", + Self::Http(_) => "http", + Self::Ws(_) => "ws", + Self::Db(_) => "db", + Self::InvalidState(_) => "invalid_state", + Self::NotConnected(_) => "not_connected", + Self::NotImplemented(_) => "not_implemented", + Self::Custom { code, message: _ } => code.as_str(), + }; + } + + /// Returns the human-readable message without the family prefix. + pub fn message(&self) -> &str { + return match self { + Self::Config(message) => message, + Self::Io(message) => message, + Self::Json(message) => message, + Self::Tracing(message) => message, + Self::Tauri(message) => message, + Self::Http(message) => message, + Self::Ws(message) => message, + Self::Db(message) => message, + Self::InvalidState(message) => message, + Self::NotConnected(message) => message, + Self::NotImplemented(message) => message, + Self::Custom { code: _, message } => message, + }; + } +} + +impl std::fmt::Display for crate::Error { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + return match self { + Self::Config(message) => write!(formatter, "configuration error: {message}"), + Self::Io(message) => write!(formatter, "io error: {message}"), + Self::Json(message) => write!(formatter, "json error: {message}"), + Self::Tracing(message) => write!(formatter, "tracing error: {message}"), + Self::Tauri(message) => write!(formatter, "tauri error: {message}"), + Self::Http(message) => write!(formatter, "http error: {message}"), + Self::Ws(message) => write!(formatter, "websocket error: {message}"), + Self::Db(message) => write!(formatter, "database error: {message}"), + Self::InvalidState(message) => write!(formatter, "invalid state: {message}"), + Self::NotConnected(message) => write!(formatter, "not connected: {message}"), + Self::NotImplemented(message) => { + write!(formatter, "not implemented: {message}") + }, + Self::Custom { code, message } => write!(formatter, "{code}: {message}"), + }; + } +} + +impl std::error::Error for crate::Error {} + +impl std::convert::From for crate::Error { + fn from(error: std::io::Error) -> Self { + return Self::Io(error.to_string()); + } +} + +#[cfg(test)] +mod tests { + #[test] + fn custom_error_preserves_code_and_message() { + let error = super::Error::new("sample_code", "sample message"); + assert_eq!(error.code(), "sample_code"); + assert_eq!(error.message(), "sample message"); + assert_eq!(error.to_string(), "sample_code: sample message"); + } + + #[test] + fn family_error_formats_with_family_prefix() { + let error = super::Error::config("missing profile"); + assert_eq!(error.code(), "config"); + assert_eq!(error.message(), "missing profile"); + assert_eq!(error.to_string(), "configuration error: missing profile"); + } +} diff --git a/kb-core/src/lib.rs b/kb-core/src/lib.rs new file mode 100644 index 0000000..84fddc9 --- /dev/null +++ b/kb-core/src/lib.rs @@ -0,0 +1,21 @@ +// file: kb-core/src/lib.rs +// version: 2 + +//! Core primitives, module identity and shared errors for the workspace. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod error; +mod module; + +/// Workspace-wide explicit error type. +pub use crate::error::Error; +/// Workspace-wide result alias. +pub use crate::error::Result; +/// Processing module category. +pub use crate::module::ModuleKind; +/// Stable module name. +pub use crate::module::ModuleName; +/// Stable semantic module version. +pub use crate::module::ModuleVersion; diff --git a/kb-core/src/module.rs b/kb-core/src/module.rs new file mode 100644 index 0000000..0e66553 --- /dev/null +++ b/kb-core/src/module.rs @@ -0,0 +1,31 @@ +// file: kb-core/src/module.rs +// version: 1 + +//! Module identity and module-kind primitives. + +/// Stable module name. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ModuleName(pub std::string::String); + +/// Stable semantic module version. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ModuleVersion(pub std::string::String); + +/// Processing module category. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ModuleKind { + /// Raw transaction ingestion. + Ingestor, + /// Generic Solana extractor. + Extractor, + /// Program observation builder. + Observer, + /// Protocol decoder. + Decoder, + /// Business materializer. + Materializer, + /// Aggregation stage. + Aggregator, + /// Validation stage. + Validator, +} diff --git a/kb-lib/Cargo.toml b/kb-lib/Cargo.toml new file mode 100644 index 0000000..980e69c --- /dev/null +++ b/kb-lib/Cargo.toml @@ -0,0 +1,22 @@ +# file: kb-lib/Cargo.toml +# version: 2 + +[package] +name = "kb-lib" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb-core = { path = "../kb-core" } +kb-program-ids = { path = "../kb-program-ids" } +base64 = "0.22" +bs58 = "0.5" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +sha2 = "0.10" +ts-rs = { version = "11", features = ["serde-compat"] } + +[lints] +workspace = true diff --git a/kb-lib/src/decoder.rs b/kb-lib/src/decoder.rs new file mode 100644 index 0000000..3560c09 --- /dev/null +++ b/kb-lib/src/decoder.rs @@ -0,0 +1,34 @@ +// file: kb-lib/src/decoder.rs +// version: 1 + +//! Consolidated decoder modules. + +pub mod adapter; +pub mod admin; +pub mod amm; +pub mod anchor; +pub mod api; +pub mod bridge; +pub mod clmm; +pub mod cpmm; +pub mod dlmm; +pub mod fees; +pub mod governance; +pub mod launchpad; +pub mod lending; +pub mod lock; +pub mod metadata; +pub mod nft; +pub mod orderbook; +pub mod perpetuals; +pub mod router; +pub mod rwa; +pub mod solana; +pub mod spl; +pub mod stable; +pub mod staking; +pub mod treasury; +pub mod vault; +pub mod vesting; +pub mod wallet; +pub mod weighted; diff --git a/kb-lib/src/decoder/adapter.rs b/kb-lib/src/decoder/adapter.rs new file mode 100644 index 0000000..ca70eed --- /dev/null +++ b/kb-lib/src/decoder/adapter.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/decoder/adapter.rs +// version: 1 + +//! `adapter` decoder family. + +pub mod saber_decimal_wrapper; diff --git a/kb-lib/src/decoder/adapter/saber_decimal_wrapper.rs b/kb-lib/src/decoder/adapter/saber_decimal_wrapper.rs new file mode 100644 index 0000000..00d06f4 --- /dev/null +++ b/kb-lib/src/decoder/adapter/saber_decimal_wrapper.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/adapter/saber_decimal_wrapper.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_adapter_saber_decimal_wrapper`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_adapter_saber_decimal_wrapper"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/admin.rs b/kb-lib/src/decoder/admin.rs new file mode 100644 index 0000000..9ccca03 --- /dev/null +++ b/kb-lib/src/decoder/admin.rs @@ -0,0 +1,7 @@ +// file: kb-lib/src/decoder/admin.rs +// version: 1 + +//! `admin` decoder family. + +pub mod jupiter_lock; +pub mod pump_fees; diff --git a/kb-lib/src/decoder/admin/jupiter_lock.rs b/kb-lib/src/decoder/admin/jupiter_lock.rs new file mode 100644 index 0000000..8895fbf --- /dev/null +++ b/kb-lib/src/decoder/admin/jupiter_lock.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/admin/jupiter_lock.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_admin_jupiter_lock`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_admin_jupiter_lock"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/admin/pump_fees.rs b/kb-lib/src/decoder/admin/pump_fees.rs new file mode 100644 index 0000000..da344e1 --- /dev/null +++ b/kb-lib/src/decoder/admin/pump_fees.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/admin/pump_fees.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_admin_pump_fees`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_admin_pump_fees"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm.rs b/kb-lib/src/decoder/amm.rs new file mode 100644 index 0000000..74d2d79 --- /dev/null +++ b/kb-lib/src/decoder/amm.rs @@ -0,0 +1,31 @@ +// file: kb-lib/src/decoder/amm.rs +// version: 1 + +//! `amm` decoder family. + +pub mod aldrin_v1; +pub mod aldrin_v2; +pub mod alphaq; +pub mod believe; +pub mod bonk_swap; +pub mod fluxbeam; +pub mod goon_fi; +pub mod goosefx_gamma; +pub mod goosefx_v2; +pub mod guac_swap; +pub mod lifinity_swap_v2; +pub mod metadao_futarchy_amm; +pub mod metadao_v0_5; +pub mod meteora_damm_v1; +pub mod meteora_damm_v2; +pub mod obric_v2; +pub mod one_dex; +pub mod pump_swap; +pub mod raydium_lp_v4; +pub mod solfi; +pub mod solfi_v2; +pub mod vertigo; +pub mod virtuals; +pub mod woofi; +pub mod zero_fi; +pub mod zora; diff --git a/kb-lib/src/decoder/amm/aldrin_v1.rs b/kb-lib/src/decoder/amm/aldrin_v1.rs new file mode 100644 index 0000000..35b6c97 --- /dev/null +++ b/kb-lib/src/decoder/amm/aldrin_v1.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/aldrin_v1.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_aldrin_v1`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_aldrin_v1"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/aldrin_v2.rs b/kb-lib/src/decoder/amm/aldrin_v2.rs new file mode 100644 index 0000000..da05b00 --- /dev/null +++ b/kb-lib/src/decoder/amm/aldrin_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/aldrin_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_aldrin_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_aldrin_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/alphaq.rs b/kb-lib/src/decoder/amm/alphaq.rs new file mode 100644 index 0000000..3be8b6f --- /dev/null +++ b/kb-lib/src/decoder/amm/alphaq.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/alphaq.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_alphaq`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_alphaq"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/believe.rs b/kb-lib/src/decoder/amm/believe.rs new file mode 100644 index 0000000..4f0aef3 --- /dev/null +++ b/kb-lib/src/decoder/amm/believe.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/believe.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_believe`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_believe"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/bonk_swap.rs b/kb-lib/src/decoder/amm/bonk_swap.rs new file mode 100644 index 0000000..73cd749 --- /dev/null +++ b/kb-lib/src/decoder/amm/bonk_swap.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/bonk_swap.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_bonk_swap`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_bonk_swap"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/fluxbeam.rs b/kb-lib/src/decoder/amm/fluxbeam.rs new file mode 100644 index 0000000..c9e1561 --- /dev/null +++ b/kb-lib/src/decoder/amm/fluxbeam.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/fluxbeam.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_fluxbeam`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_fluxbeam"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/goon_fi.rs b/kb-lib/src/decoder/amm/goon_fi.rs new file mode 100644 index 0000000..de23f47 --- /dev/null +++ b/kb-lib/src/decoder/amm/goon_fi.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/goon_fi.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_goon_fi`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_goon_fi"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/goosefx_gamma.rs b/kb-lib/src/decoder/amm/goosefx_gamma.rs new file mode 100644 index 0000000..deee864 --- /dev/null +++ b/kb-lib/src/decoder/amm/goosefx_gamma.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/goosefx_gamma.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_goosefx_gamma`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_goosefx_gamma"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/goosefx_v2.rs b/kb-lib/src/decoder/amm/goosefx_v2.rs new file mode 100644 index 0000000..caf2c50 --- /dev/null +++ b/kb-lib/src/decoder/amm/goosefx_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/goosefx_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_goosefx_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_goosefx_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/guac_swap.rs b/kb-lib/src/decoder/amm/guac_swap.rs new file mode 100644 index 0000000..e312008 --- /dev/null +++ b/kb-lib/src/decoder/amm/guac_swap.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/guac_swap.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_guac_swap`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_guac_swap"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/lifinity_swap_v2.rs b/kb-lib/src/decoder/amm/lifinity_swap_v2.rs new file mode 100644 index 0000000..938cfe8 --- /dev/null +++ b/kb-lib/src/decoder/amm/lifinity_swap_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/lifinity_swap_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_lifinity_swap_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_lifinity_swap_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/metadao_futarchy_amm.rs b/kb-lib/src/decoder/amm/metadao_futarchy_amm.rs new file mode 100644 index 0000000..b859227 --- /dev/null +++ b/kb-lib/src/decoder/amm/metadao_futarchy_amm.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/metadao_futarchy_amm.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_metadao_futarchy_amm`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_metadao_futarchy_amm"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/metadao_v0_5.rs b/kb-lib/src/decoder/amm/metadao_v0_5.rs new file mode 100644 index 0000000..f1e0619 --- /dev/null +++ b/kb-lib/src/decoder/amm/metadao_v0_5.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/metadao_v0_5.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_metadao_v0_5`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_metadao_v0_5"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/meteora_damm_v1.rs b/kb-lib/src/decoder/amm/meteora_damm_v1.rs new file mode 100644 index 0000000..97a5f68 --- /dev/null +++ b/kb-lib/src/decoder/amm/meteora_damm_v1.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/meteora_damm_v1.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_meteora_damm_v1`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_meteora_damm_v1"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/meteora_damm_v2.rs b/kb-lib/src/decoder/amm/meteora_damm_v2.rs new file mode 100644 index 0000000..a451730 --- /dev/null +++ b/kb-lib/src/decoder/amm/meteora_damm_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/meteora_damm_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_meteora_damm_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_meteora_damm_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/obric_v2.rs b/kb-lib/src/decoder/amm/obric_v2.rs new file mode 100644 index 0000000..6af2996 --- /dev/null +++ b/kb-lib/src/decoder/amm/obric_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/obric_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_obric_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_obric_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/one_dex.rs b/kb-lib/src/decoder/amm/one_dex.rs new file mode 100644 index 0000000..c1007a4 --- /dev/null +++ b/kb-lib/src/decoder/amm/one_dex.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/one_dex.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_one_dex`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_one_dex"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/pump_swap.rs b/kb-lib/src/decoder/amm/pump_swap.rs new file mode 100644 index 0000000..a32b64c --- /dev/null +++ b/kb-lib/src/decoder/amm/pump_swap.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/pump_swap.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_pump_swap`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_pump_swap"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/raydium_lp_v4.rs b/kb-lib/src/decoder/amm/raydium_lp_v4.rs new file mode 100644 index 0000000..e0d50ee --- /dev/null +++ b/kb-lib/src/decoder/amm/raydium_lp_v4.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/raydium_lp_v4.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_raydium_lp_v4`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_raydium_lp_v4"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/solfi.rs b/kb-lib/src/decoder/amm/solfi.rs new file mode 100644 index 0000000..4e3fb65 --- /dev/null +++ b/kb-lib/src/decoder/amm/solfi.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/solfi.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_solfi`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_solfi"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/solfi_v2.rs b/kb-lib/src/decoder/amm/solfi_v2.rs new file mode 100644 index 0000000..2901585 --- /dev/null +++ b/kb-lib/src/decoder/amm/solfi_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/solfi_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_solfi_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_solfi_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/vertigo.rs b/kb-lib/src/decoder/amm/vertigo.rs new file mode 100644 index 0000000..20d79ab --- /dev/null +++ b/kb-lib/src/decoder/amm/vertigo.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/vertigo.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_vertigo`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_vertigo"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/virtuals.rs b/kb-lib/src/decoder/amm/virtuals.rs new file mode 100644 index 0000000..f7515c0 --- /dev/null +++ b/kb-lib/src/decoder/amm/virtuals.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/virtuals.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_virtuals`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_virtuals"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/woofi.rs b/kb-lib/src/decoder/amm/woofi.rs new file mode 100644 index 0000000..2e31857 --- /dev/null +++ b/kb-lib/src/decoder/amm/woofi.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/woofi.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_woofi`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_woofi"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/zero_fi.rs b/kb-lib/src/decoder/amm/zero_fi.rs new file mode 100644 index 0000000..031e0c1 --- /dev/null +++ b/kb-lib/src/decoder/amm/zero_fi.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/zero_fi.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_zero_fi`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_zero_fi"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/amm/zora.rs b/kb-lib/src/decoder/amm/zora.rs new file mode 100644 index 0000000..a204a63 --- /dev/null +++ b/kb-lib/src/decoder/amm/zora.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/amm/zora.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_amm_zora`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_amm_zora"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/anchor.rs b/kb-lib/src/decoder/anchor.rs new file mode 100644 index 0000000..e775a2c --- /dev/null +++ b/kb-lib/src/decoder/anchor.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/decoder/anchor.rs +// version: 1 + +//! `anchor` decoder family. + +pub mod core; diff --git a/kb-lib/src/decoder/anchor/core.rs b/kb-lib/src/decoder/anchor/core.rs new file mode 100644 index 0000000..4c2eaaa --- /dev/null +++ b/kb-lib/src/decoder/anchor/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/anchor/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_anchor`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_anchor"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/api.rs b/kb-lib/src/decoder/api.rs new file mode 100644 index 0000000..4588795 --- /dev/null +++ b/kb-lib/src/decoder/api.rs @@ -0,0 +1,50 @@ +// file: kb-lib/src/decoder/api.rs +// version: 3 + +//! Decoder contracts consolidated from `kb_decoder_api`. + +/// Decoder API constants. +pub mod constants; +pub mod contracts; +pub mod decoder; +/// Canonical tracing target for this crate. +/// Current contextual core instruction input contract version. +pub use crate::decoder::api::contracts::CORE_INSTRUCTION_INPUT_CONTRACT_VERSION; +/// Stable contextual decoded observation. +pub use crate::decoder::api::contracts::DecodedObservation; +/// Machine-readable decoder coverage declaration. +pub use crate::decoder::api::contracts::DecoderCoverageDeclaration; +/// Declared decoder coverage entry kind. +pub use crate::decoder::api::contracts::DecoderCoverageEntryKind; +/// Structured decoder diagnostic. +pub use crate::decoder::api::contracts::DecoderDiagnostic; +/// Complete decoder execution result. +pub use crate::decoder::api::contracts::DecoderExecutionResult; +/// Stable decoder identity. +pub use crate::decoder::api::contracts::DecoderIdentity; +/// Decoder terminal result status. +pub use crate::decoder::api::contracts::DecoderOutcomeStatus; +/// Explicit decoding proof. +pub use crate::decoder::api::contracts::DecoderProof; +/// Decoding proof category. +pub use crate::decoder::api::contracts::DecoderProofKind; +/// Deterministic decoder recognition result. +pub use crate::decoder::api::contracts::DecoderRecognition; +/// One statically declared decoder surface. +pub use crate::decoder::api::contracts::DecoderSurface; +/// Contextual instruction decoder contract. +pub use crate::decoder::api::contracts::InstructionDecoder; +/// Computes the deterministic contextual input hash. +pub use crate::decoder::api::contracts::contextual_input_hash; +/// Returns true when a decoder declares the exact program id. +pub use crate::decoder::api::contracts::decoder_handles_program_id; +/// Computes a deterministic lowercase SHA-256 hash for one JSON value. +pub use crate::decoder::api::contracts::deterministic_json_hash; +/// Extracts the first eight payload bytes as hexadecimal when available. +pub use crate::decoder::api::contracts::discriminator_8_hex; +/// Exposes the decoder support level. +pub use crate::decoder::api::decoder::DecoderSupport; +/// Initial decoder implementation placeholder for API-level tests. +pub use crate::decoder::api::decoder::InitialDecoder; +/// Exposes the common protocol decoder trait. +pub use crate::decoder::api::decoder::ProtocolDecoder; diff --git a/kb-lib/src/decoder/api/constants.rs b/kb-lib/src/decoder/api/constants.rs new file mode 100644 index 0000000..c0a6798 --- /dev/null +++ b/kb-lib/src/decoder/api/constants.rs @@ -0,0 +1,5 @@ +// file: kb-lib/src/decoder/api/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_api"; diff --git a/kb-lib/src/decoder/api/contracts.rs b/kb-lib/src/decoder/api/contracts.rs new file mode 100644 index 0000000..b7d4fb0 --- /dev/null +++ b/kb-lib/src/decoder/api/contracts.rs @@ -0,0 +1,576 @@ +// file: kb-lib/src/decoder/api/contracts.rs +// version: 7 + +//! Backend-neutral contextual instruction decoder contracts. + +use sha2::Digest; // rust-rules: trait-import + +/// Current contextual core instruction input contract version. +pub const CORE_INSTRUCTION_INPUT_CONTRACT_VERSION: u32 = crate::CORE_REPLAY_INPUT_CONTRACT_VERSION; + +/// Stable identity of one contextual instruction decoder. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecoderIdentity { + /// Stable lower snake case processor name. + pub name: std::string::String, + /// Semantic or deterministic implementation version. + pub version: std::string::String, +} + +impl DecoderIdentity { + /// Builds a validated decoder identity. + pub fn new( + name: impl std::convert::Into, + version: impl std::convert::Into, + ) -> kb_core::Result { + let value = Self { + name: name.into(), + version: version.into(), + }; + if value.name.trim().is_empty() || value.version.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "decoder identity fields must not be empty", + )); + } + return std::result::Result::Ok(value); + } +} + +/// One statically declared Solana surface supported by a decoder. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct DecoderSurface { + /// Exact Solana program identifier. + pub program_id: &'static str, + /// Stable surface code. + pub surface_code: &'static str, + /// Explicit dispatch priority, where larger values win. + pub priority: u16, +} + +/// Declared decoder coverage entry kind. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum DecoderCoverageEntryKind { + /// Program instruction entry. + Instruction, + /// Program event entry. + Event, + /// Raw discriminator entry. + Discriminator, +} + +/// Machine-readable decoder coverage declaration. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecoderCoverageDeclaration { + /// Solana program identifier. + pub program_id: std::string::String, + /// Optional stable surface code. + pub surface_code: std::option::Option, + /// Entry kind. + pub entry_kind: DecoderCoverageEntryKind, + /// Stable instruction, event or discriminator code. + pub entry_code: std::string::String, + /// Optional normalized lowercase hexadecimal discriminator. + pub discriminator_hex: std::option::Option, + /// Whether the entry is historical or deprecated but still supported. + pub historical: bool, +} + +impl DecoderCoverageDeclaration { + /// Builds a validated coverage declaration. + pub fn new( + program_id: impl std::convert::Into, + surface_code: std::option::Option, + entry_kind: DecoderCoverageEntryKind, + entry_code: impl std::convert::Into, + discriminator_hex: std::option::Option, + historical: bool, + ) -> kb_core::Result { + let value = Self { + program_id: program_id.into(), + surface_code, + entry_kind, + entry_code: entry_code.into(), + discriminator_hex, + historical, + }; + if value.program_id.trim().is_empty() || value.entry_code.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "decoder coverage program id and entry code must not be empty", + )); + } + if value + .surface_code + .as_deref() + .is_some_and(|surface| return surface.trim().is_empty()) + { + return std::result::Result::Err(kb_core::Error::invalid_state( + "decoder coverage surface code must not be empty when present", + )); + } + if value + .discriminator_hex + .as_deref() + .is_some_and(|discriminator| return discriminator.trim().is_empty()) + { + return std::result::Result::Err(kb_core::Error::invalid_state( + "decoder coverage discriminator must not be empty when present", + )); + } + return std::result::Result::Ok(value); + } +} + +/// Deterministic recognition result used by dispatch. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecoderRecognition { + /// Whether the decoder is compatible with the input. + pub compatible: bool, + /// Whether recognition is exact rather than program-only. + pub exact: bool, + /// Explicit priority used after program and surface matching. + pub priority: u16, + /// Optional recognized surface code. + pub surface_code: std::option::Option, + /// Optional recognized instruction or discriminator code. + pub entry_code: std::option::Option, + /// Optional normalized lowercase hexadecimal discriminator. + pub discriminator_hex: std::option::Option, +} + +impl DecoderRecognition { + /// Builds a non-compatible recognition result. + pub fn incompatible() -> Self { + return Self { + compatible: false, + exact: false, + priority: 0, + surface_code: std::option::Option::None, + entry_code: std::option::Option::None, + discriminator_hex: std::option::Option::None, + }; + } + + /// Builds a compatible recognition result. + pub fn compatible( + exact: bool, + priority: u16, + surface_code: std::option::Option, + entry_code: std::option::Option, + discriminator_hex: std::option::Option, + ) -> Self { + return Self { + compatible: true, + exact, + priority, + surface_code, + entry_code, + discriminator_hex, + }; + } +} + +/// Decoder terminal result status. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum DecoderOutcomeStatus { + /// At least one stable decoded observation was produced. + Decoded, + /// The decoder intentionally ignored a recognized input. + Ignored, + /// The input belongs to the program but its entry is unsupported. + Unsupported, + /// Decoding failed and diagnostics were produced. + Failed, +} + +/// Proof category attached to one decoded observation. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum DecoderProofKind { + /// Exact instruction discriminator match. + ExactDiscriminator, + /// Exact binary layout decode. + ExactLayout, + /// Exact IDL-based decode. + Idl, + /// Manually verified exact decode. + Manual, + /// Correlation with runtime logs. + LogCorrelation, + /// Balance delta evidence. + BalanceDelta, + /// Heuristic evidence that must not be treated as exact. + Heuristic, + /// Audit-only classification. + Audit, + /// Unknown or unclassified evidence. + Unknown, +} + +/// Explicit proof attached to one decoded observation. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecoderProof { + /// Proof category. + pub kind: DecoderProofKind, + /// Decoder confidence. + pub confidence: crate::DecoderConfidence, + /// Stable evidence strings, hashes or classifier codes. + pub evidence: std::vec::Vec, +} + +/// Structured decoder diagnostic without third-party error wrappers. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecoderDiagnostic { + /// Stable machine-readable diagnostic code. + pub code: std::string::String, + /// Human-readable diagnostic message. + pub message: std::string::String, + /// Whether retrying the same version and input may succeed. + pub retriable: bool, +} + +impl DecoderDiagnostic { + /// Builds a validated decoder diagnostic. + pub fn new( + code: impl std::convert::Into, + message: impl std::convert::Into, + retriable: bool, + ) -> kb_core::Result { + let value = Self { + code: code.into(), + message: message.into(), + retriable, + }; + if value.code.trim().is_empty() || value.message.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "decoder diagnostic code and message must not be empty", + )); + } + return std::result::Result::Ok(value); + } +} + +/// Stable contextual decoded observation persisted by the decode pipeline. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecodedObservation { + /// Stable processor-owned event key within one input. + pub event_key: std::string::String, + /// Shared typed protocol event identity. + pub event: crate::DecodedProtocolEvent, + /// Typed event payload serialized as deterministic JSON. + pub payload_json: serde_json::Value, + /// Whether the source transaction failed on-chain. + pub transaction_failed: bool, + /// Optional transaction error JSON. + pub transaction_error: std::option::Option, + /// Whether the observed state mutation was committed on-chain. + pub observation_committed: bool, + /// Explicit decoding proof. + pub proof: DecoderProof, +} + +impl DecodedObservation { + /// Validates failed transaction commit semantics and stable identity fields. + pub fn validate(&self) -> kb_core::Result<()> { + if self.event_key.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "decoded observation event key must not be empty", + )); + } + if self.transaction_failed && self.observation_committed { + return std::result::Result::Err(kb_core::Error::invalid_state( + "failed transaction observations must not be marked committed", + )); + } + if self.event.signature.0.trim().is_empty() + || self.event.instruction_path.0.trim().is_empty() + || self.event.program_id.0.trim().is_empty() + || self.event.event_code.0.trim().is_empty() + { + return std::result::Result::Err(kb_core::Error::invalid_state( + "decoded observation event identity fields must not be empty", + )); + } + return std::result::Result::Ok(()); + } +} + +/// Complete explicit result produced by one decoder invocation. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecoderExecutionResult { + /// Terminal decoder status. + pub status: DecoderOutcomeStatus, + /// Optional recognized instruction or discriminator code. + pub recognized_entry_code: std::option::Option, + /// Decoded observations. + pub observations: std::vec::Vec, + /// Structured diagnostics. + pub diagnostics: std::vec::Vec, +} + +impl DecoderExecutionResult { + /// Builds an unsupported result with no false decoded observation. + pub fn unsupported(entry_code: std::option::Option) -> Self { + return Self { + status: DecoderOutcomeStatus::Unsupported, + recognized_entry_code: entry_code, + observations: std::vec::Vec::new(), + diagnostics: std::vec::Vec::new(), + }; + } + + /// Builds an ignored result. + pub fn ignored(entry_code: std::option::Option) -> Self { + return Self { + status: DecoderOutcomeStatus::Ignored, + recognized_entry_code: entry_code, + observations: std::vec::Vec::new(), + diagnostics: std::vec::Vec::new(), + }; + } + + /// Validates status and observation consistency. + pub fn validate(&self) -> kb_core::Result<()> { + if self.status == DecoderOutcomeStatus::Decoded && self.observations.is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "decoded status requires at least one observation", + )); + } + if self.status != DecoderOutcomeStatus::Decoded && !self.observations.is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "non-decoded status must not contain observations", + )); + } + if self.status == DecoderOutcomeStatus::Failed && self.diagnostics.is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "failed decoder status requires at least one diagnostic", + )); + } + for observation in &self.observations { + let validation_result = observation.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + } + return std::result::Result::Ok(()); + } +} + +/// Contextual instruction decoder contract used by the common pipeline. +pub trait InstructionDecoder: std::marker::Send + std::marker::Sync { + /// Returns the stable decoder identity. + fn identity(&self) -> DecoderIdentity; + /// Returns every exact Solana surface supported by the decoder. + fn surfaces(&self) -> &'static [DecoderSurface]; + /// Returns the machine-readable declared coverage matrix. + fn coverage(&self) -> std::vec::Vec; + /// Recognizes one contextual core instruction for deterministic dispatch. + fn recognize(&self, input: &crate::CoreInstructionReplayInput) -> DecoderRecognition; + /// Decodes one recognized contextual core instruction. + fn decode(&self, input: &crate::CoreInstructionReplayInput) -> DecoderExecutionResult; +} + +/// Returns true when a decoder declares the exact program id. +pub fn decoder_handles_program_id(decoder: &dyn InstructionDecoder, program_id: &str) -> bool { + for surface in decoder.surfaces() { + if surface.program_id == program_id { + return true; + } + } + return false; +} + +/// Computes a deterministic lowercase SHA-256 hash for one contextual input. +pub fn contextual_input_hash( + input: &crate::CoreInstructionReplayInput, +) -> kb_core::Result { + let serialization_result = serde_json::to_value(input); + let mut value = match serialization_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot serialize contextual decoder input: {error}" + ))); + }, + }; + return crate::deterministic_json_hash(&mut value); +} + +/// Computes a deterministic lowercase SHA-256 hash for one JSON value. +pub fn deterministic_json_hash( + value: &mut serde_json::Value, +) -> kb_core::Result { + sort_json_value(value); + let bytes_result = serde_json::to_vec(value); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot serialize deterministic JSON bytes: {error}" + ))); + }, + }; + let digest = sha2::Sha256::digest(bytes); + let mut output = std::string::String::with_capacity(64); + for byte in digest { + output.push_str(format!("{byte:02x}").as_str()); + } + return std::result::Result::Ok(output); +} + +/// Extracts the first eight payload bytes as normalized lowercase hexadecimal when available. +pub fn discriminator_8_hex( + input: &crate::CoreInstructionReplayInput, +) -> std::option::Option { + let payload = match &input.instruction_payload_json { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::option::Option::None, + }; + let encoded = match payload.get("dataBase64").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::option::Option::None, + }; + let decoded_result = + base64::Engine::decode(&base64::engine::general_purpose::STANDARD, encoded.as_bytes()); + let decoded = match decoded_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => return std::option::Option::None, + }; + if decoded.len() < 8 { + return std::option::Option::None; + } + let mut output = std::string::String::with_capacity(16); + for byte in &decoded[..8] { + output.push_str(format!("{byte:02x}").as_str()); + } + return std::option::Option::Some(output); +} + +fn sort_json_value(value: &mut serde_json::Value) { + match value { + serde_json::Value::Object(map) => { + let mut entries = std::mem::take(map).into_iter().collect::>(); + entries.sort_by(|left, right| return left.0.cmp(&right.0)); + for (key, mut child) in entries { + sort_json_value(&mut child); + map.insert(key, child); + } + }, + serde_json::Value::Array(values) => { + for child in values { + sort_json_value(child); + } + }, + _ => (), + } +} + +#[cfg(test)] +mod tests { + fn replay_input() -> crate::CoreInstructionReplayInput { + let result = crate::CoreInstructionReplayInput::new( + "signature:0", + "signature", + 42, + "0", + "program", + false, + std::option::Option::None, + serde_json::json!([{"accountIndex": 0, "accountKey": "account"}]), + serde_json::json!([{"accountIndex": 0, "accountKey": "account"}]), + std::option::Option::Some(serde_json::json!({"dataBase64": "AQIDBAUGBwg="})), + std::option::Option::Some("payload-hash".to_string()), + serde_json::json!([{ + "instructionIndex": 0, + "instructionPath": "0", + "programId": "program", + "payloadJson": {"dataBase64": "AQIDBAUGBwg="}, + "payloadHash": "payload-hash" + }]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected replay input error: {error}"), + }; + } + + #[test] + fn contextual_input_hash_is_stable() { + let first = crate::contextual_input_hash(&replay_input()); + let second = crate::contextual_input_hash(&replay_input()); + assert!(first.is_ok()); + assert_eq!(first, second); + } + + #[test] + fn contextual_input_hash_changes_when_outer_context_changes() { + let first = replay_input(); + let mut second = replay_input(); + second.outer_instructions_json = serde_json::json!([{ + "instructionIndex": 0, + "instructionPath": "0", + "programId": "program", + "payloadJson": {"dataBase64": "CAcGBQQDAgE="}, + "payloadHash": "different-payload-hash" + }]); + let first_hash = crate::contextual_input_hash(&first); + let second_hash = crate::contextual_input_hash(&second); + assert!(first_hash.is_ok()); + assert!(second_hash.is_ok()); + assert_ne!(first_hash, second_hash); + } + + #[test] + fn contextual_input_serialization_is_deterministic_with_outer_context() { + let first_result = serde_json::to_string(&replay_input()); + let first = match first_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected serialization error: {error}"), + }; + let second_result = serde_json::to_string(&replay_input()); + let second = match second_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected serialization error: {error}"), + }; + assert_eq!(first, second); + } + + #[test] + fn discriminator_uses_first_eight_bytes() { + let value = crate::discriminator_8_hex(&replay_input()); + assert_eq!(value.as_deref(), std::option::Option::Some("0102030405060708")); + } + + #[test] + fn failed_observation_cannot_be_committed() { + let observation = crate::DecodedObservation { + event_key: "event".to_string(), + event: crate::DecodedProtocolEvent { + signature: crate::Signature("signature".to_string()), + slot: crate::Slot(42), + instruction_path: crate::InstructionPath("0".to_string()), + program_id: crate::ProgramId("program".to_string()), + protocol_code: crate::ProtocolCode("solana".to_string()), + surface_code: crate::SurfaceCode("system_program".to_string()), + event_code: crate::EventCode("system_program.attempt".to_string()), + event_name: crate::EventName("attempt".to_string()), + event_family: crate::EventFamily::Audit, + source_kind: crate::EventSourceKind::Instruction, + confidence: crate::DecoderConfidence::Exact, + }, + payload_json: serde_json::json!({}), + transaction_failed: true, + transaction_error: std::option::Option::Some( + serde_json::json!({"InstructionError": [0, "Custom"]}), + ), + observation_committed: true, + proof: crate::DecoderProof { + kind: crate::DecoderProofKind::ExactLayout, + confidence: crate::DecoderConfidence::Exact, + evidence: std::vec!["layout".to_string()], + }, + }; + assert!(observation.validate().is_err()); + } +} diff --git a/kb-lib/src/decoder/api/decoder.rs b/kb-lib/src/decoder/api/decoder.rs new file mode 100644 index 0000000..7a25a52 --- /dev/null +++ b/kb-lib/src/decoder/api/decoder.rs @@ -0,0 +1,74 @@ +// file: kb-lib/src/decoder/api/decoder.rs +// version: 4 + +//! Shared decoder contracts for protocol decoder crates. + +/// Decoder support level for a program observation. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum DecoderSupport { + /// The decoder does not support the observation. + No, + /// The decoder may support the observation after deeper inspection. + Maybe, + /// The decoder explicitly supports the observation. + Yes, +} + +/// Common trait implemented by every protocol decoder crate. +pub trait ProtocolDecoder { + /// Returns the stable decoder name. + fn decoder_name(&self) -> &'static str; + /// Returns the decoder implementation version. + fn decoder_version(&self) -> &'static str; + /// Returns the Solana program ids handled by this decoder. + fn program_ids(&self) -> &'static [&'static str]; + /// Checks whether the decoder handles a program id. + fn handles_program_id(&self, program_id: &crate::ProgramId) -> bool { + for candidate in self.program_ids() { + if program_id.0.as_str() == *candidate { + return true; + } + } + return false; + } + /// Checks whether the decoder supports a generic program observation. + fn supports_observation(&self, observation: &crate::ProgramObservation) -> DecoderSupport; + /// Decodes a generic program observation into protocol events. + fn decode_observation( + &self, + observation: &crate::ProgramObservation, + ) -> kb_core::Result>; +} + +/// Initial decoder implementation placeholder for API-level tests. +#[derive(Clone, Debug, Default)] +pub struct InitialDecoder; + +impl ProtocolDecoder for InitialDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_api"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[]; + } + + fn supports_observation(&self, observation: &crate::ProgramObservation) -> DecoderSupport { + if ProtocolDecoder::handles_program_id(self, &observation.program_id) { + return DecoderSupport::Maybe; + } + return DecoderSupport::No; + } + + fn decode_observation( + &self, + _observation: &crate::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/kb-lib/src/decoder/bridge.rs b/kb-lib/src/decoder/bridge.rs new file mode 100644 index 0000000..762d024 --- /dev/null +++ b/kb-lib/src/decoder/bridge.rs @@ -0,0 +1,9 @@ +// file: kb-lib/src/decoder/bridge.rs +// version: 1 + +//! `bridge` decoder family. + +pub mod circle_cctp_token_messenger_minter; +pub mod circle_cctp_token_messenger_minter_v2; +pub mod layer_zero_endpoint; +pub mod layer_zero_executor; diff --git a/kb-lib/src/decoder/bridge/circle_cctp_token_messenger_minter.rs b/kb-lib/src/decoder/bridge/circle_cctp_token_messenger_minter.rs new file mode 100644 index 0000000..3bd5c51 --- /dev/null +++ b/kb-lib/src/decoder/bridge/circle_cctp_token_messenger_minter.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/bridge/circle_cctp_token_messenger_minter.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_bridge_circle_cctp_token_messenger_minter`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_bridge_circle_cctp_token_messenger_minter"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/bridge/circle_cctp_token_messenger_minter_v2.rs b/kb-lib/src/decoder/bridge/circle_cctp_token_messenger_minter_v2.rs new file mode 100644 index 0000000..ad1880a --- /dev/null +++ b/kb-lib/src/decoder/bridge/circle_cctp_token_messenger_minter_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/bridge/circle_cctp_token_messenger_minter_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_bridge_circle_cctp_token_messenger_minter_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_bridge_circle_cctp_token_messenger_minter_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/bridge/layer_zero_endpoint.rs b/kb-lib/src/decoder/bridge/layer_zero_endpoint.rs new file mode 100644 index 0000000..9482c28 --- /dev/null +++ b/kb-lib/src/decoder/bridge/layer_zero_endpoint.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/bridge/layer_zero_endpoint.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_bridge_layer_zero_endpoint`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_bridge_layer_zero_endpoint"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/bridge/layer_zero_executor.rs b/kb-lib/src/decoder/bridge/layer_zero_executor.rs new file mode 100644 index 0000000..16a1b6c --- /dev/null +++ b/kb-lib/src/decoder/bridge/layer_zero_executor.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/bridge/layer_zero_executor.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_bridge_layer_zero_executor`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_bridge_layer_zero_executor"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/clmm.rs b/kb-lib/src/decoder/clmm.rs new file mode 100644 index 0000000..e9c1013 --- /dev/null +++ b/kb-lib/src/decoder/clmm.rs @@ -0,0 +1,11 @@ +// file: kb-lib/src/decoder/clmm.rs +// version: 1 + +//! `clmm` decoder family. + +pub mod byreal; +pub mod fusion; +pub mod orca_whirlpool; +pub mod pancake_swap; +pub mod raydium; +pub mod stabble; diff --git a/kb-lib/src/decoder/clmm/byreal.rs b/kb-lib/src/decoder/clmm/byreal.rs new file mode 100644 index 0000000..fcf0022 --- /dev/null +++ b/kb-lib/src/decoder/clmm/byreal.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/clmm/byreal.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_clmm_byreal`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_clmm_byreal"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/clmm/fusion.rs b/kb-lib/src/decoder/clmm/fusion.rs new file mode 100644 index 0000000..f0e0438 --- /dev/null +++ b/kb-lib/src/decoder/clmm/fusion.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/clmm/fusion.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_clmm_fusion`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_clmm_fusion"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/clmm/orca_whirlpool.rs b/kb-lib/src/decoder/clmm/orca_whirlpool.rs new file mode 100644 index 0000000..52ee4e9 --- /dev/null +++ b/kb-lib/src/decoder/clmm/orca_whirlpool.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/clmm/orca_whirlpool.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_clmm_orca_whirlpool`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_clmm_orca_whirlpool"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/clmm/pancake_swap.rs b/kb-lib/src/decoder/clmm/pancake_swap.rs new file mode 100644 index 0000000..1652ec2 --- /dev/null +++ b/kb-lib/src/decoder/clmm/pancake_swap.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/clmm/pancake_swap.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_clmm_pancake_swap`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_clmm_pancake_swap"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/clmm/raydium.rs b/kb-lib/src/decoder/clmm/raydium.rs new file mode 100644 index 0000000..a5063e6 --- /dev/null +++ b/kb-lib/src/decoder/clmm/raydium.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/clmm/raydium.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_clmm_raydium`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_clmm_raydium"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/clmm/stabble.rs b/kb-lib/src/decoder/clmm/stabble.rs new file mode 100644 index 0000000..3165488 --- /dev/null +++ b/kb-lib/src/decoder/clmm/stabble.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/clmm/stabble.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_clmm_stabble`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_clmm_stabble"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/cpmm.rs b/kb-lib/src/decoder/cpmm.rs new file mode 100644 index 0000000..1210730 --- /dev/null +++ b/kb-lib/src/decoder/cpmm.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/decoder/cpmm.rs +// version: 1 + +//! `cpmm` decoder family. + +pub mod raydium; diff --git a/kb-lib/src/decoder/cpmm/raydium.rs b/kb-lib/src/decoder/cpmm/raydium.rs new file mode 100644 index 0000000..d8b4bc5 --- /dev/null +++ b/kb-lib/src/decoder/cpmm/raydium.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/cpmm/raydium.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_cpmm_raydium`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_cpmm_raydium"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/dlmm.rs b/kb-lib/src/decoder/dlmm.rs new file mode 100644 index 0000000..5a5e9cf --- /dev/null +++ b/kb-lib/src/decoder/dlmm.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/decoder/dlmm.rs +// version: 1 + +//! `dlmm` decoder family. + +pub mod meteora; diff --git a/kb-lib/src/decoder/dlmm/meteora.rs b/kb-lib/src/decoder/dlmm/meteora.rs new file mode 100644 index 0000000..e9f251c --- /dev/null +++ b/kb-lib/src/decoder/dlmm/meteora.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/dlmm/meteora.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_dlmm_meteora`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_dlmm_meteora"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/fees.rs b/kb-lib/src/decoder/fees.rs new file mode 100644 index 0000000..a095265 --- /dev/null +++ b/kb-lib/src/decoder/fees.rs @@ -0,0 +1,7 @@ +// file: kb-lib/src/decoder/fees.rs +// version: 1 + +//! `fees` decoder family. + +pub mod bags_fee_share_v1; +pub mod bags_fee_share_v2; diff --git a/kb-lib/src/decoder/fees/bags_fee_share_v1.rs b/kb-lib/src/decoder/fees/bags_fee_share_v1.rs new file mode 100644 index 0000000..70ea157 --- /dev/null +++ b/kb-lib/src/decoder/fees/bags_fee_share_v1.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/fees/bags_fee_share_v1.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_fees_bags_fee_share_v1`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_fees_bags_fee_share_v1"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/fees/bags_fee_share_v2.rs b/kb-lib/src/decoder/fees/bags_fee_share_v2.rs new file mode 100644 index 0000000..eec53f8 --- /dev/null +++ b/kb-lib/src/decoder/fees/bags_fee_share_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/fees/bags_fee_share_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_fees_bags_fee_share_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_fees_bags_fee_share_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/governance.rs b/kb-lib/src/decoder/governance.rs new file mode 100644 index 0000000..efce434 --- /dev/null +++ b/kb-lib/src/decoder/governance.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/decoder/governance.rs +// version: 1 + +//! `governance` decoder family. + +pub mod metadao_bid_wall; diff --git a/kb-lib/src/decoder/governance/metadao_bid_wall.rs b/kb-lib/src/decoder/governance/metadao_bid_wall.rs new file mode 100644 index 0000000..b8eabe2 --- /dev/null +++ b/kb-lib/src/decoder/governance/metadao_bid_wall.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/governance/metadao_bid_wall.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_governance_metadao_bid_wall`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_governance_metadao_bid_wall"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/launchpad.rs b/kb-lib/src/decoder/launchpad.rs new file mode 100644 index 0000000..508b611 --- /dev/null +++ b/kb-lib/src/decoder/launchpad.rs @@ -0,0 +1,14 @@ +// file: kb-lib/src/decoder/launchpad.rs +// version: 1 + +//! `launchpad` decoder family. + +pub mod boop_fun; +pub mod metadao_ico; +pub mod meteora_dbc; +pub mod moonit; +pub mod orca_wavebreak; +pub mod printr; +pub mod pump_fun; +pub mod pump_pumpup_ai; +pub mod raydium_launchlab; diff --git a/kb-lib/src/decoder/launchpad/boop_fun.rs b/kb-lib/src/decoder/launchpad/boop_fun.rs new file mode 100644 index 0000000..ceb2683 --- /dev/null +++ b/kb-lib/src/decoder/launchpad/boop_fun.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/launchpad/boop_fun.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_launchpad_boop_fun`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_launchpad_boop_fun"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/launchpad/metadao_ico.rs b/kb-lib/src/decoder/launchpad/metadao_ico.rs new file mode 100644 index 0000000..edc4d37 --- /dev/null +++ b/kb-lib/src/decoder/launchpad/metadao_ico.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/launchpad/metadao_ico.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_launchpad_metadao_ico`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_launchpad_metadao_ico"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/launchpad/meteora_dbc.rs b/kb-lib/src/decoder/launchpad/meteora_dbc.rs new file mode 100644 index 0000000..920ad2d --- /dev/null +++ b/kb-lib/src/decoder/launchpad/meteora_dbc.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/launchpad/meteora_dbc.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_launchpad_meteora_dbc`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_launchpad_meteora_dbc"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/launchpad/moonit.rs b/kb-lib/src/decoder/launchpad/moonit.rs new file mode 100644 index 0000000..301a630 --- /dev/null +++ b/kb-lib/src/decoder/launchpad/moonit.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/launchpad/moonit.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_launchpad_moonit`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_launchpad_moonit"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/launchpad/orca_wavebreak.rs b/kb-lib/src/decoder/launchpad/orca_wavebreak.rs new file mode 100644 index 0000000..3daea74 --- /dev/null +++ b/kb-lib/src/decoder/launchpad/orca_wavebreak.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/launchpad/orca_wavebreak.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_launchpad_orca_wavebreak`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_launchpad_orca_wavebreak"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/launchpad/printr.rs b/kb-lib/src/decoder/launchpad/printr.rs new file mode 100644 index 0000000..00b719c --- /dev/null +++ b/kb-lib/src/decoder/launchpad/printr.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/launchpad/printr.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_launchpad_printr`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_launchpad_printr"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/launchpad/pump_fun.rs b/kb-lib/src/decoder/launchpad/pump_fun.rs new file mode 100644 index 0000000..51282e7 --- /dev/null +++ b/kb-lib/src/decoder/launchpad/pump_fun.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/launchpad/pump_fun.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_launchpad_pump_fun`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_launchpad_pump_fun"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/launchpad/pump_pumpup_ai.rs b/kb-lib/src/decoder/launchpad/pump_pumpup_ai.rs new file mode 100644 index 0000000..edf05fa --- /dev/null +++ b/kb-lib/src/decoder/launchpad/pump_pumpup_ai.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/launchpad/pump_pumpup_ai.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_launchpad_pump_pumpup_ai`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_launchpad_pump_pumpup_ai"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/launchpad/raydium_launchlab.rs b/kb-lib/src/decoder/launchpad/raydium_launchlab.rs new file mode 100644 index 0000000..39702b8 --- /dev/null +++ b/kb-lib/src/decoder/launchpad/raydium_launchlab.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/launchpad/raydium_launchlab.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_launchpad_raydium_launchlab`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_launchpad_raydium_launchlab"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/lending.rs b/kb-lib/src/decoder/lending.rs new file mode 100644 index 0000000..f943cd0 --- /dev/null +++ b/kb-lib/src/decoder/lending.rs @@ -0,0 +1,12 @@ +// file: kb-lib/src/decoder/lending.rs +// version: 1 + +//! `lending` decoder family. + +pub mod clone; +pub mod jupiter_lend_borrow; +pub mod jupiter_lend_earn; +pub mod jupiter_lend_flash_loan; +pub mod jupiter_lend_liquidity; +pub mod kamino; +pub mod marginfi_v2; diff --git a/kb-lib/src/decoder/lending/clone.rs b/kb-lib/src/decoder/lending/clone.rs new file mode 100644 index 0000000..88b796b --- /dev/null +++ b/kb-lib/src/decoder/lending/clone.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/lending/clone.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_lending_clone`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_lending_clone"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/lending/jupiter_lend_borrow.rs b/kb-lib/src/decoder/lending/jupiter_lend_borrow.rs new file mode 100644 index 0000000..58cf9b7 --- /dev/null +++ b/kb-lib/src/decoder/lending/jupiter_lend_borrow.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/lending/jupiter_lend_borrow.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_lending_jupiter_lend_borrow`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_lending_jupiter_lend_borrow"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/lending/jupiter_lend_earn.rs b/kb-lib/src/decoder/lending/jupiter_lend_earn.rs new file mode 100644 index 0000000..02d5aeb --- /dev/null +++ b/kb-lib/src/decoder/lending/jupiter_lend_earn.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/lending/jupiter_lend_earn.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_lending_jupiter_lend_earn`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_lending_jupiter_lend_earn"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/lending/jupiter_lend_flash_loan.rs b/kb-lib/src/decoder/lending/jupiter_lend_flash_loan.rs new file mode 100644 index 0000000..2cce758 --- /dev/null +++ b/kb-lib/src/decoder/lending/jupiter_lend_flash_loan.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/lending/jupiter_lend_flash_loan.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_lending_jupiter_lend_flash_loan`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_lending_jupiter_lend_flash_loan"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/lending/jupiter_lend_liquidity.rs b/kb-lib/src/decoder/lending/jupiter_lend_liquidity.rs new file mode 100644 index 0000000..56f13c6 --- /dev/null +++ b/kb-lib/src/decoder/lending/jupiter_lend_liquidity.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/lending/jupiter_lend_liquidity.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_lending_jupiter_lend_liquidity`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_lending_jupiter_lend_liquidity"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/lending/kamino.rs b/kb-lib/src/decoder/lending/kamino.rs new file mode 100644 index 0000000..cf79836 --- /dev/null +++ b/kb-lib/src/decoder/lending/kamino.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/lending/kamino.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_lending_kamino`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_lending_kamino"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/lending/marginfi_v2.rs b/kb-lib/src/decoder/lending/marginfi_v2.rs new file mode 100644 index 0000000..ef173b9 --- /dev/null +++ b/kb-lib/src/decoder/lending/marginfi_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/lending/marginfi_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_lending_marginfi_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_lending_marginfi_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/lock.rs b/kb-lib/src/decoder/lock.rs new file mode 100644 index 0000000..4e47726 --- /dev/null +++ b/kb-lib/src/decoder/lock.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/decoder/lock.rs +// version: 1 + +//! `lock` decoder family. + +pub mod raydium_lp; diff --git a/kb-lib/src/decoder/lock/raydium_lp.rs b/kb-lib/src/decoder/lock/raydium_lp.rs new file mode 100644 index 0000000..0b387d4 --- /dev/null +++ b/kb-lib/src/decoder/lock/raydium_lp.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/lock/raydium_lp.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_lock_raydium_lp`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_lock_raydium_lp"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/metadata.rs b/kb-lib/src/decoder/metadata.rs new file mode 100644 index 0000000..5d518c5 --- /dev/null +++ b/kb-lib/src/decoder/metadata.rs @@ -0,0 +1,7 @@ +// file: kb-lib/src/decoder/metadata.rs +// version: 1 + +//! `metadata` decoder family. + +pub mod metaplex_token_metadata; +pub mod spl_name_service; diff --git a/kb-lib/src/decoder/metadata/metaplex_token_metadata.rs b/kb-lib/src/decoder/metadata/metaplex_token_metadata.rs new file mode 100644 index 0000000..ce686ed --- /dev/null +++ b/kb-lib/src/decoder/metadata/metaplex_token_metadata.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/metadata/metaplex_token_metadata.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_metadata_metaplex_token_metadata`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_metadata_metaplex_token_metadata"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/metadata/spl_name_service.rs b/kb-lib/src/decoder/metadata/spl_name_service.rs new file mode 100644 index 0000000..108c6e9 --- /dev/null +++ b/kb-lib/src/decoder/metadata/spl_name_service.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/metadata/spl_name_service.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_metadata_spl_name_service`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_metadata_spl_name_service"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/nft.rs b/kb-lib/src/decoder/nft.rs new file mode 100644 index 0000000..bdf5c67 --- /dev/null +++ b/kb-lib/src/decoder/nft.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/decoder/nft.rs +// version: 1 + +//! `nft` decoder family. + +pub mod metaplex_bubblegum; diff --git a/kb-lib/src/decoder/nft/metaplex_bubblegum.rs b/kb-lib/src/decoder/nft/metaplex_bubblegum.rs new file mode 100644 index 0000000..18fc116 --- /dev/null +++ b/kb-lib/src/decoder/nft/metaplex_bubblegum.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/nft/metaplex_bubblegum.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_nft_metaplex_bubblegum`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_nft_metaplex_bubblegum"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/orderbook.rs b/kb-lib/src/decoder/orderbook.rs new file mode 100644 index 0000000..fd86e45 --- /dev/null +++ b/kb-lib/src/decoder/orderbook.rs @@ -0,0 +1,8 @@ +// file: kb-lib/src/decoder/orderbook.rs +// version: 1 + +//! `orderbook` decoder family. + +pub mod jupiter_limit_order; +pub mod jupiter_limit_order_v2; +pub mod openbook_v2; diff --git a/kb-lib/src/decoder/orderbook/jupiter_limit_order.rs b/kb-lib/src/decoder/orderbook/jupiter_limit_order.rs new file mode 100644 index 0000000..8d5c111 --- /dev/null +++ b/kb-lib/src/decoder/orderbook/jupiter_limit_order.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/orderbook/jupiter_limit_order.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_orderbook_jupiter_limit_order`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_orderbook_jupiter_limit_order"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/orderbook/jupiter_limit_order_v2.rs b/kb-lib/src/decoder/orderbook/jupiter_limit_order_v2.rs new file mode 100644 index 0000000..148be1d --- /dev/null +++ b/kb-lib/src/decoder/orderbook/jupiter_limit_order_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/orderbook/jupiter_limit_order_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_orderbook_jupiter_limit_order_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_orderbook_jupiter_limit_order_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/orderbook/openbook_v2.rs b/kb-lib/src/decoder/orderbook/openbook_v2.rs new file mode 100644 index 0000000..a01b635 --- /dev/null +++ b/kb-lib/src/decoder/orderbook/openbook_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/orderbook/openbook_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_orderbook_openbook_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_orderbook_openbook_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/perpetuals.rs b/kb-lib/src/decoder/perpetuals.rs new file mode 100644 index 0000000..7e2f027 --- /dev/null +++ b/kb-lib/src/decoder/perpetuals.rs @@ -0,0 +1,8 @@ +// file: kb-lib/src/decoder/perpetuals.rs +// version: 1 + +//! `perpetuals` decoder family. + +pub mod drift_v2; +pub mod jupiter; +pub mod zeta; diff --git a/kb-lib/src/decoder/perpetuals/drift_v2.rs b/kb-lib/src/decoder/perpetuals/drift_v2.rs new file mode 100644 index 0000000..f0c503a --- /dev/null +++ b/kb-lib/src/decoder/perpetuals/drift_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/perpetuals/drift_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_perpetuals_drift_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_perpetuals_drift_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/perpetuals/jupiter.rs b/kb-lib/src/decoder/perpetuals/jupiter.rs new file mode 100644 index 0000000..3fd691c --- /dev/null +++ b/kb-lib/src/decoder/perpetuals/jupiter.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/perpetuals/jupiter.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_perpetuals_jupiter`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_perpetuals_jupiter"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/perpetuals/zeta.rs b/kb-lib/src/decoder/perpetuals/zeta.rs new file mode 100644 index 0000000..04db283 --- /dev/null +++ b/kb-lib/src/decoder/perpetuals/zeta.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/perpetuals/zeta.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_perpetuals_zeta`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_perpetuals_zeta"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/router.rs b/kb-lib/src/decoder/router.rs new file mode 100644 index 0000000..da9fb10 --- /dev/null +++ b/kb-lib/src/decoder/router.rs @@ -0,0 +1,11 @@ +// file: kb-lib/src/decoder/router.rs +// version: 1 + +//! `router` decoder family. + +pub mod dflow_aggregator_v4; +pub mod jupiter_aggregator_v4; +pub mod jupiter_aggregator_v6; +pub mod jupiter_dca; +pub mod okx_labs_v1; +pub mod okx_labs_v2; diff --git a/kb-lib/src/decoder/router/dflow_aggregator_v4.rs b/kb-lib/src/decoder/router/dflow_aggregator_v4.rs new file mode 100644 index 0000000..7150dc0 --- /dev/null +++ b/kb-lib/src/decoder/router/dflow_aggregator_v4.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/router/dflow_aggregator_v4.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_router_dflow_aggregator_v4`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_router_dflow_aggregator_v4"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/router/jupiter_aggregator_v4.rs b/kb-lib/src/decoder/router/jupiter_aggregator_v4.rs new file mode 100644 index 0000000..cac6b3b --- /dev/null +++ b/kb-lib/src/decoder/router/jupiter_aggregator_v4.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/router/jupiter_aggregator_v4.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_router_jupiter_aggregator_v4`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_router_jupiter_aggregator_v4"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/router/jupiter_aggregator_v6.rs b/kb-lib/src/decoder/router/jupiter_aggregator_v6.rs new file mode 100644 index 0000000..d59ea39 --- /dev/null +++ b/kb-lib/src/decoder/router/jupiter_aggregator_v6.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/router/jupiter_aggregator_v6.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_router_jupiter_aggregator_v6`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_router_jupiter_aggregator_v6"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/router/jupiter_dca.rs b/kb-lib/src/decoder/router/jupiter_dca.rs new file mode 100644 index 0000000..4ca27be --- /dev/null +++ b/kb-lib/src/decoder/router/jupiter_dca.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/router/jupiter_dca.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_router_jupiter_dca`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_router_jupiter_dca"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/router/okx_labs_v1.rs b/kb-lib/src/decoder/router/okx_labs_v1.rs new file mode 100644 index 0000000..96e8576 --- /dev/null +++ b/kb-lib/src/decoder/router/okx_labs_v1.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/router/okx_labs_v1.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_router_okx_labs_v1`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_router_okx_labs_v1"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/router/okx_labs_v2.rs b/kb-lib/src/decoder/router/okx_labs_v2.rs new file mode 100644 index 0000000..cdd22a9 --- /dev/null +++ b/kb-lib/src/decoder/router/okx_labs_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/router/okx_labs_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_router_okx_labs_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_router_okx_labs_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/rwa.rs b/kb-lib/src/decoder/rwa.rs new file mode 100644 index 0000000..1099e82 --- /dev/null +++ b/kb-lib/src/decoder/rwa.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/decoder/rwa.rs +// version: 1 + +//! `rwa` decoder family. + +pub mod ondo_global_markets; diff --git a/kb-lib/src/decoder/rwa/ondo_global_markets.rs b/kb-lib/src/decoder/rwa/ondo_global_markets.rs new file mode 100644 index 0000000..68bfa69 --- /dev/null +++ b/kb-lib/src/decoder/rwa/ondo_global_markets.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/rwa/ondo_global_markets.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_rwa_ondo_global_markets`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_rwa_ondo_global_markets"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/solana.rs b/kb-lib/src/decoder/solana.rs new file mode 100644 index 0000000..9f5aad4 --- /dev/null +++ b/kb-lib/src/decoder/solana.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/decoder/solana.rs +// version: 1 + +//! `solana` decoder family. + +pub mod core; diff --git a/kb-lib/src/decoder/solana/core.rs b/kb-lib/src/decoder/solana/core.rs new file mode 100644 index 0000000..fb42582 --- /dev/null +++ b/kb-lib/src/decoder/solana/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/solana/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_solana_core`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_solana_core"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/spl.rs b/kb-lib/src/decoder/spl.rs new file mode 100644 index 0000000..c649953 --- /dev/null +++ b/kb-lib/src/decoder/spl.rs @@ -0,0 +1,14 @@ +// file: kb-lib/src/decoder/spl.rs +// version: 1 + +//! `spl` decoder family. + +pub mod account_compression; +pub mod associated_token_account; +pub mod elgamal_registry; +pub mod memo; +pub mod noop; +pub mod single_pool; +pub mod stake_pool; +pub mod token; +pub mod token_2022; diff --git a/kb-lib/src/decoder/spl/account_compression.rs b/kb-lib/src/decoder/spl/account_compression.rs new file mode 100644 index 0000000..2e63da5 --- /dev/null +++ b/kb-lib/src/decoder/spl/account_compression.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/spl/account_compression.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_spl_account_compression`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_spl_account_compression"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/spl/associated_token_account.rs b/kb-lib/src/decoder/spl/associated_token_account.rs new file mode 100644 index 0000000..372acf2 --- /dev/null +++ b/kb-lib/src/decoder/spl/associated_token_account.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/spl/associated_token_account.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_spl_associated_token_account`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_spl_associated_token_account"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/spl/elgamal_registry.rs b/kb-lib/src/decoder/spl/elgamal_registry.rs new file mode 100644 index 0000000..df4f465 --- /dev/null +++ b/kb-lib/src/decoder/spl/elgamal_registry.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/spl/elgamal_registry.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_spl_elgamal_registry`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_spl_elgamal_registry"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/spl/memo.rs b/kb-lib/src/decoder/spl/memo.rs new file mode 100644 index 0000000..6eba648 --- /dev/null +++ b/kb-lib/src/decoder/spl/memo.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/spl/memo.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_spl_memo`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_spl_memo"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/spl/noop.rs b/kb-lib/src/decoder/spl/noop.rs new file mode 100644 index 0000000..6730263 --- /dev/null +++ b/kb-lib/src/decoder/spl/noop.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/spl/noop.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_spl_noop`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_spl_noop"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/spl/single_pool.rs b/kb-lib/src/decoder/spl/single_pool.rs new file mode 100644 index 0000000..48cfd2a --- /dev/null +++ b/kb-lib/src/decoder/spl/single_pool.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/spl/single_pool.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_spl_single_pool`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_spl_single_pool"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/spl/stake_pool.rs b/kb-lib/src/decoder/spl/stake_pool.rs new file mode 100644 index 0000000..e0b82b3 --- /dev/null +++ b/kb-lib/src/decoder/spl/stake_pool.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/spl/stake_pool.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_spl_stake_pool`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_spl_stake_pool"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/spl/token.rs b/kb-lib/src/decoder/spl/token.rs new file mode 100644 index 0000000..6e93199 --- /dev/null +++ b/kb-lib/src/decoder/spl/token.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/spl/token.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_spl_token`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_spl_token"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/spl/token_2022.rs b/kb-lib/src/decoder/spl/token_2022.rs new file mode 100644 index 0000000..7cc08be --- /dev/null +++ b/kb-lib/src/decoder/spl/token_2022.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/spl/token_2022.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_spl_token_2022`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_spl_token_2022"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/stable.rs b/kb-lib/src/decoder/stable.rs new file mode 100644 index 0000000..1faa4ac --- /dev/null +++ b/kb-lib/src/decoder/stable.rs @@ -0,0 +1,9 @@ +// file: kb-lib/src/decoder/stable.rs +// version: 1 + +//! `stable` decoder family. + +pub mod swap_hylo_exchange; +pub mod swap_jupiter_stable; +pub mod swap_numeraire; +pub mod swap_stabble; diff --git a/kb-lib/src/decoder/stable/swap_hylo_exchange.rs b/kb-lib/src/decoder/stable/swap_hylo_exchange.rs new file mode 100644 index 0000000..668ecbc --- /dev/null +++ b/kb-lib/src/decoder/stable/swap_hylo_exchange.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/stable/swap_hylo_exchange.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_stable_swap_hylo_exchange`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_stable_swap_hylo_exchange"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/stable/swap_jupiter_stable.rs b/kb-lib/src/decoder/stable/swap_jupiter_stable.rs new file mode 100644 index 0000000..e07f3c0 --- /dev/null +++ b/kb-lib/src/decoder/stable/swap_jupiter_stable.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/stable/swap_jupiter_stable.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_stable_swap_jupiter_stable`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_stable_swap_jupiter_stable"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/stable/swap_numeraire.rs b/kb-lib/src/decoder/stable/swap_numeraire.rs new file mode 100644 index 0000000..bf10ea5 --- /dev/null +++ b/kb-lib/src/decoder/stable/swap_numeraire.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/stable/swap_numeraire.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_stable_swap_numeraire`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_stable_swap_numeraire"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/stable/swap_stabble.rs b/kb-lib/src/decoder/stable/swap_stabble.rs new file mode 100644 index 0000000..3680e6b --- /dev/null +++ b/kb-lib/src/decoder/stable/swap_stabble.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/stable/swap_stabble.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_stable_swap_stabble`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_stable_swap_stabble"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/staking.rs b/kb-lib/src/decoder/staking.rs new file mode 100644 index 0000000..e8cec74 --- /dev/null +++ b/kb-lib/src/decoder/staking.rs @@ -0,0 +1,8 @@ +// file: kb-lib/src/decoder/staking.rs +// version: 1 + +//! `staking` decoder family. + +pub mod kamino_farm; +pub mod marinade_finance; +pub mod solayer; diff --git a/kb-lib/src/decoder/staking/kamino_farm.rs b/kb-lib/src/decoder/staking/kamino_farm.rs new file mode 100644 index 0000000..cf3578e --- /dev/null +++ b/kb-lib/src/decoder/staking/kamino_farm.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/staking/kamino_farm.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_staking_kamino_farm`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_staking_kamino_farm"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/staking/marinade_finance.rs b/kb-lib/src/decoder/staking/marinade_finance.rs new file mode 100644 index 0000000..d78e821 --- /dev/null +++ b/kb-lib/src/decoder/staking/marinade_finance.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/staking/marinade_finance.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_staking_marinade_finance`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_staking_marinade_finance"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/staking/solayer.rs b/kb-lib/src/decoder/staking/solayer.rs new file mode 100644 index 0000000..24ce55e --- /dev/null +++ b/kb-lib/src/decoder/staking/solayer.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/staking/solayer.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_staking_solayer`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_staking_solayer"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/treasury.rs b/kb-lib/src/decoder/treasury.rs new file mode 100644 index 0000000..df134a1 --- /dev/null +++ b/kb-lib/src/decoder/treasury.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/decoder/treasury.rs +// version: 1 + +//! `treasury` decoder family. + +pub mod helium_treasury_management; diff --git a/kb-lib/src/decoder/treasury/helium_treasury_management.rs b/kb-lib/src/decoder/treasury/helium_treasury_management.rs new file mode 100644 index 0000000..0c7f343 --- /dev/null +++ b/kb-lib/src/decoder/treasury/helium_treasury_management.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/treasury/helium_treasury_management.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_treasury_helium_treasury_management`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_treasury_helium_treasury_management"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/vault.rs b/kb-lib/src/decoder/vault.rs new file mode 100644 index 0000000..e62911a --- /dev/null +++ b/kb-lib/src/decoder/vault.rs @@ -0,0 +1,11 @@ +// file: kb-lib/src/decoder/vault.rs +// version: 1 + +//! `vault` decoder family. + +pub mod carrot_defi; +pub mod hylo_stability_pool; +pub mod kamino; +pub mod kamino_v2; +pub mod kamino_yvaults; +pub mod meteora; diff --git a/kb-lib/src/decoder/vault/carrot_defi.rs b/kb-lib/src/decoder/vault/carrot_defi.rs new file mode 100644 index 0000000..ab3e985 --- /dev/null +++ b/kb-lib/src/decoder/vault/carrot_defi.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/vault/carrot_defi.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_vault_carrot_defi`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_vault_carrot_defi"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/vault/hylo_stability_pool.rs b/kb-lib/src/decoder/vault/hylo_stability_pool.rs new file mode 100644 index 0000000..3523575 --- /dev/null +++ b/kb-lib/src/decoder/vault/hylo_stability_pool.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/vault/hylo_stability_pool.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_vault_hylo_stability_pool`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_vault_hylo_stability_pool"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/vault/kamino.rs b/kb-lib/src/decoder/vault/kamino.rs new file mode 100644 index 0000000..ebc03f6 --- /dev/null +++ b/kb-lib/src/decoder/vault/kamino.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/vault/kamino.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_vault_kamino`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_vault_kamino"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/vault/kamino_v2.rs b/kb-lib/src/decoder/vault/kamino_v2.rs new file mode 100644 index 0000000..99bed69 --- /dev/null +++ b/kb-lib/src/decoder/vault/kamino_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/vault/kamino_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_vault_kamino_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_vault_kamino_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/vault/kamino_yvaults.rs b/kb-lib/src/decoder/vault/kamino_yvaults.rs new file mode 100644 index 0000000..fce5680 --- /dev/null +++ b/kb-lib/src/decoder/vault/kamino_yvaults.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/vault/kamino_yvaults.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_vault_kamino_yvaults`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_vault_kamino_yvaults"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/vault/meteora.rs b/kb-lib/src/decoder/vault/meteora.rs new file mode 100644 index 0000000..7c5732f --- /dev/null +++ b/kb-lib/src/decoder/vault/meteora.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/vault/meteora.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_vault_meteora`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_vault_meteora"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/vesting.rs b/kb-lib/src/decoder/vesting.rs new file mode 100644 index 0000000..5a0aee5 --- /dev/null +++ b/kb-lib/src/decoder/vesting.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/decoder/vesting.rs +// version: 1 + +//! `vesting` decoder family. + +pub mod streamflow; diff --git a/kb-lib/src/decoder/vesting/streamflow.rs b/kb-lib/src/decoder/vesting/streamflow.rs new file mode 100644 index 0000000..cfacc41 --- /dev/null +++ b/kb-lib/src/decoder/vesting/streamflow.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/vesting/streamflow.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_vesting_streamflow`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_vesting_streamflow"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/wallet.rs b/kb-lib/src/decoder/wallet.rs new file mode 100644 index 0000000..f260b88 --- /dev/null +++ b/kb-lib/src/decoder/wallet.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/decoder/wallet.rs +// version: 1 + +//! `wallet` decoder family. + +pub mod jupiter_apepro_smart_wallet; diff --git a/kb-lib/src/decoder/wallet/jupiter_apepro_smart_wallet.rs b/kb-lib/src/decoder/wallet/jupiter_apepro_smart_wallet.rs new file mode 100644 index 0000000..771454c --- /dev/null +++ b/kb-lib/src/decoder/wallet/jupiter_apepro_smart_wallet.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/wallet/jupiter_apepro_smart_wallet.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_wallet_jupiter_apepro_smart_wallet`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_wallet_jupiter_apepro_smart_wallet"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/decoder/weighted.rs b/kb-lib/src/decoder/weighted.rs new file mode 100644 index 0000000..206c266 --- /dev/null +++ b/kb-lib/src/decoder/weighted.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/decoder/weighted.rs +// version: 1 + +//! `weighted` decoder family. + +pub mod swap_stabble; diff --git a/kb-lib/src/decoder/weighted/swap_stabble.rs b/kb-lib/src/decoder/weighted/swap_stabble.rs new file mode 100644 index 0000000..60aed3e --- /dev/null +++ b/kb-lib/src/decoder/weighted/swap_stabble.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/decoder/weighted/swap_stabble.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_decoder_weighted_swap_stabble`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_decoder_weighted_swap_stabble"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor.rs b/kb-lib/src/executor.rs new file mode 100644 index 0000000..9e58a78 --- /dev/null +++ b/kb-lib/src/executor.rs @@ -0,0 +1,33 @@ +// file: kb-lib/src/executor.rs +// version: 1 + +//! Consolidated executor modules. + +pub mod adapter; +pub mod admin; +pub mod amm; +pub mod api; +pub mod bridge; +pub mod clmm; +pub mod cpmm; +pub mod dlmm; +pub mod fees; +pub mod governance; +pub mod launchpad; +pub mod lending; +pub mod lock; +pub mod metadata; +pub mod nft; +pub mod orderbook; +pub mod perpetuals; +pub mod router; +pub mod rwa; +pub mod solana; +pub mod spl; +pub mod stable; +pub mod staking; +pub mod treasury; +pub mod vault; +pub mod vesting; +pub mod wallet; +pub mod weighted; diff --git a/kb-lib/src/executor/adapter.rs b/kb-lib/src/executor/adapter.rs new file mode 100644 index 0000000..9b6e95e --- /dev/null +++ b/kb-lib/src/executor/adapter.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/executor/adapter.rs +// version: 1 + +//! `adapter` executor family. + +pub mod saber_decimal_wrapper; diff --git a/kb-lib/src/executor/adapter/saber_decimal_wrapper.rs b/kb-lib/src/executor/adapter/saber_decimal_wrapper.rs new file mode 100644 index 0000000..bfa640e --- /dev/null +++ b/kb-lib/src/executor/adapter/saber_decimal_wrapper.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/adapter/saber_decimal_wrapper.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_adapter_saber_decimal_wrapper`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_adapter_saber_decimal_wrapper"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/admin.rs b/kb-lib/src/executor/admin.rs new file mode 100644 index 0000000..85aa52c --- /dev/null +++ b/kb-lib/src/executor/admin.rs @@ -0,0 +1,7 @@ +// file: kb-lib/src/executor/admin.rs +// version: 1 + +//! `admin` executor family. + +pub mod jupiter_lock; +pub mod pump_fees; diff --git a/kb-lib/src/executor/admin/jupiter_lock.rs b/kb-lib/src/executor/admin/jupiter_lock.rs new file mode 100644 index 0000000..9b417e2 --- /dev/null +++ b/kb-lib/src/executor/admin/jupiter_lock.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/admin/jupiter_lock.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_admin_jupiter_lock`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_admin_jupiter_lock"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/admin/pump_fees.rs b/kb-lib/src/executor/admin/pump_fees.rs new file mode 100644 index 0000000..5fce2fa --- /dev/null +++ b/kb-lib/src/executor/admin/pump_fees.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/admin/pump_fees.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_admin_pump_fees`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_admin_pump_fees"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm.rs b/kb-lib/src/executor/amm.rs new file mode 100644 index 0000000..bb5c941 --- /dev/null +++ b/kb-lib/src/executor/amm.rs @@ -0,0 +1,31 @@ +// file: kb-lib/src/executor/amm.rs +// version: 1 + +//! `amm` executor family. + +pub mod aldrin_v1; +pub mod aldrin_v2; +pub mod alphaq; +pub mod believe; +pub mod bonk_swap; +pub mod fluxbeam; +pub mod goon_fi; +pub mod goosefx_gamma; +pub mod goosefx_v2; +pub mod guac_swap; +pub mod lifinity_swap_v2; +pub mod metadao_futarchy_amm; +pub mod metadao_v0_5; +pub mod meteora_damm_v1; +pub mod meteora_damm_v2; +pub mod obric_v2; +pub mod one_dex; +pub mod pump_swap; +pub mod raydium_lp_v4; +pub mod solfi; +pub mod solfi_v2; +pub mod vertigo; +pub mod virtuals; +pub mod woofi; +pub mod zero_fi; +pub mod zora; diff --git a/kb-lib/src/executor/amm/aldrin_v1.rs b/kb-lib/src/executor/amm/aldrin_v1.rs new file mode 100644 index 0000000..48e1c2e --- /dev/null +++ b/kb-lib/src/executor/amm/aldrin_v1.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/aldrin_v1.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_aldrin_v1`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_aldrin_v1"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/aldrin_v2.rs b/kb-lib/src/executor/amm/aldrin_v2.rs new file mode 100644 index 0000000..6688a49 --- /dev/null +++ b/kb-lib/src/executor/amm/aldrin_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/aldrin_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_aldrin_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_aldrin_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/alphaq.rs b/kb-lib/src/executor/amm/alphaq.rs new file mode 100644 index 0000000..8aa3790 --- /dev/null +++ b/kb-lib/src/executor/amm/alphaq.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/alphaq.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_alphaq`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_alphaq"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/believe.rs b/kb-lib/src/executor/amm/believe.rs new file mode 100644 index 0000000..97863ac --- /dev/null +++ b/kb-lib/src/executor/amm/believe.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/believe.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_believe`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_believe"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/bonk_swap.rs b/kb-lib/src/executor/amm/bonk_swap.rs new file mode 100644 index 0000000..cf8f42d --- /dev/null +++ b/kb-lib/src/executor/amm/bonk_swap.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/bonk_swap.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_bonk_swap`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_bonk_swap"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/fluxbeam.rs b/kb-lib/src/executor/amm/fluxbeam.rs new file mode 100644 index 0000000..eb41497 --- /dev/null +++ b/kb-lib/src/executor/amm/fluxbeam.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/fluxbeam.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_fluxbeam`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_fluxbeam"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/goon_fi.rs b/kb-lib/src/executor/amm/goon_fi.rs new file mode 100644 index 0000000..0ea86c4 --- /dev/null +++ b/kb-lib/src/executor/amm/goon_fi.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/goon_fi.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_goon_fi`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_goon_fi"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/goosefx_gamma.rs b/kb-lib/src/executor/amm/goosefx_gamma.rs new file mode 100644 index 0000000..7fdd2ac --- /dev/null +++ b/kb-lib/src/executor/amm/goosefx_gamma.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/goosefx_gamma.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_goosefx_gamma`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_goosefx_gamma"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/goosefx_v2.rs b/kb-lib/src/executor/amm/goosefx_v2.rs new file mode 100644 index 0000000..769761c --- /dev/null +++ b/kb-lib/src/executor/amm/goosefx_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/goosefx_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_goosefx_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_goosefx_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/guac_swap.rs b/kb-lib/src/executor/amm/guac_swap.rs new file mode 100644 index 0000000..941c19e --- /dev/null +++ b/kb-lib/src/executor/amm/guac_swap.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/guac_swap.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_guac_swap`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_guac_swap"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/lifinity_swap_v2.rs b/kb-lib/src/executor/amm/lifinity_swap_v2.rs new file mode 100644 index 0000000..b95aa3b --- /dev/null +++ b/kb-lib/src/executor/amm/lifinity_swap_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/lifinity_swap_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_lifinity_swap_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_lifinity_swap_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/metadao_futarchy_amm.rs b/kb-lib/src/executor/amm/metadao_futarchy_amm.rs new file mode 100644 index 0000000..1512038 --- /dev/null +++ b/kb-lib/src/executor/amm/metadao_futarchy_amm.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/metadao_futarchy_amm.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_metadao_futarchy_amm`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_metadao_futarchy_amm"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/metadao_v0_5.rs b/kb-lib/src/executor/amm/metadao_v0_5.rs new file mode 100644 index 0000000..ba3dc52 --- /dev/null +++ b/kb-lib/src/executor/amm/metadao_v0_5.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/metadao_v0_5.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_metadao_v0_5`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_metadao_v0_5"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/meteora_damm_v1.rs b/kb-lib/src/executor/amm/meteora_damm_v1.rs new file mode 100644 index 0000000..3333ec5 --- /dev/null +++ b/kb-lib/src/executor/amm/meteora_damm_v1.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/meteora_damm_v1.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_meteora_damm_v1`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_meteora_damm_v1"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/meteora_damm_v2.rs b/kb-lib/src/executor/amm/meteora_damm_v2.rs new file mode 100644 index 0000000..9cc61a8 --- /dev/null +++ b/kb-lib/src/executor/amm/meteora_damm_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/meteora_damm_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_meteora_damm_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_meteora_damm_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/obric_v2.rs b/kb-lib/src/executor/amm/obric_v2.rs new file mode 100644 index 0000000..60519ab --- /dev/null +++ b/kb-lib/src/executor/amm/obric_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/obric_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_obric_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_obric_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/one_dex.rs b/kb-lib/src/executor/amm/one_dex.rs new file mode 100644 index 0000000..538b549 --- /dev/null +++ b/kb-lib/src/executor/amm/one_dex.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/one_dex.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_one_dex`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_one_dex"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/pump_swap.rs b/kb-lib/src/executor/amm/pump_swap.rs new file mode 100644 index 0000000..511e8de --- /dev/null +++ b/kb-lib/src/executor/amm/pump_swap.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/pump_swap.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_pump_swap`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_pump_swap"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/raydium_lp_v4.rs b/kb-lib/src/executor/amm/raydium_lp_v4.rs new file mode 100644 index 0000000..1207165 --- /dev/null +++ b/kb-lib/src/executor/amm/raydium_lp_v4.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/raydium_lp_v4.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_raydium_lp_v4`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_raydium_lp_v4"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/solfi.rs b/kb-lib/src/executor/amm/solfi.rs new file mode 100644 index 0000000..8aa1097 --- /dev/null +++ b/kb-lib/src/executor/amm/solfi.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/solfi.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_solfi`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_solfi"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/solfi_v2.rs b/kb-lib/src/executor/amm/solfi_v2.rs new file mode 100644 index 0000000..1448257 --- /dev/null +++ b/kb-lib/src/executor/amm/solfi_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/solfi_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_solfi_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_solfi_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/vertigo.rs b/kb-lib/src/executor/amm/vertigo.rs new file mode 100644 index 0000000..022a429 --- /dev/null +++ b/kb-lib/src/executor/amm/vertigo.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/vertigo.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_vertigo`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_vertigo"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/virtuals.rs b/kb-lib/src/executor/amm/virtuals.rs new file mode 100644 index 0000000..6b4fdfc --- /dev/null +++ b/kb-lib/src/executor/amm/virtuals.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/virtuals.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_virtuals`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_virtuals"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/woofi.rs b/kb-lib/src/executor/amm/woofi.rs new file mode 100644 index 0000000..50fea7d --- /dev/null +++ b/kb-lib/src/executor/amm/woofi.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/woofi.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_woofi`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_woofi"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/zero_fi.rs b/kb-lib/src/executor/amm/zero_fi.rs new file mode 100644 index 0000000..168aaf1 --- /dev/null +++ b/kb-lib/src/executor/amm/zero_fi.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/zero_fi.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_zero_fi`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_zero_fi"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/amm/zora.rs b/kb-lib/src/executor/amm/zora.rs new file mode 100644 index 0000000..b36d341 --- /dev/null +++ b/kb-lib/src/executor/amm/zora.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/amm/zora.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_amm_zora`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_amm_zora"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/api.rs b/kb-lib/src/executor/api.rs new file mode 100644 index 0000000..c49a959 --- /dev/null +++ b/kb-lib/src/executor/api.rs @@ -0,0 +1,57 @@ +// file: kb-lib/src/executor/api.rs +// version: 2 + +//! Executor contracts consolidated from `kb_execution_api`. + +pub mod execution; +pub mod executor; +/// Exposes the blockhash policy kind. +pub use crate::executor::api::execution::ExecutionBlockhashKind; +/// Exposes the blockhash policy. +pub use crate::executor::api::execution::ExecutionBlockhashPolicy; +/// Exposes the exact execution capability result. +pub use crate::executor::api::execution::ExecutionCapability; +/// Exposes supported Solana clusters. +pub use crate::executor::api::execution::ExecutionCluster; +/// Exposes cluster restrictions. +pub use crate::executor::api::execution::ExecutionClusterPolicy; +/// Exposes the result of waiting for transaction confirmation. +pub use crate::executor::api::execution::ExecutionConfirmationResult; +/// Exposes transaction confirmation states. +pub use crate::executor::api::execution::ExecutionConfirmationStatus; +/// Exposes explicit execution cost ceilings. +pub use crate::executor::api::execution::ExecutionCostLimit; +/// Exposes the complete execution policy. +pub use crate::executor::api::execution::ExecutionPolicy; +/// Exposes the result of a transaction send adapter. +pub use crate::executor::api::execution::ExecutionSendResult; +/// Exposes the simulation policy. +pub use crate::executor::api::execution::ExecutionSimulationPolicy; +/// Exposes the simulation result contract. +pub use crate::executor::api::execution::ExecutionSimulationResult; +/// Exposes one planned instruction account. +pub use crate::executor::api::execution::PlannedAccount; +/// Exposes one encoded planned instruction. +pub use crate::executor::api::execution::PlannedInstruction; +/// Exposes post-execution replay diagnostics. +pub use crate::executor::api::execution::PostExecutionDiagnostic; +/// Exposes post-execution validation requirements. +pub use crate::executor::api::execution::PostExecutionValidationPolicy; +/// Exposes a typed execution plan. +pub use crate::executor::api::execution::PreparedExecutionPlan; +/// Exposes one required signer. +pub use crate::executor::api::execution::RequiredSigner; +/// Exposes the typed executor contract. +pub use crate::executor::api::execution::TypedInstructionExecutor; +/// Exposes the legacy common execution plan type. +pub use crate::executor::api::executor::ExecutionPlan; +/// Exposes the legacy common execution request type. +pub use crate::executor::api::executor::ExecutionRequest; +/// Exposes the legacy common execution support enum. +pub use crate::executor::api::executor::ExecutionSupport; +/// Exposes the legacy common instruction executor trait. +pub use crate::executor::api::executor::InstructionExecutor; +/// Exposes the compact JSON payload serializer. +pub use crate::executor::api::executor::serialize_payload_json; +/// Exposes the pretty JSON payload serializer. +pub use crate::executor::api::executor::serialize_payload_json_pretty; diff --git a/kb-lib/src/executor/api/execution.rs b/kb-lib/src/executor/api/execution.rs new file mode 100644 index 0000000..74b329a --- /dev/null +++ b/kb-lib/src/executor/api/execution.rs @@ -0,0 +1,496 @@ +// file: kb-lib/src/executor/api/execution.rs +// version: 7 + +//! Typed execution plans, policies and result contracts. + +use ts_rs::TS; // rust-rules: derive-import + +/// Exact capability returned for one program operation. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(tag = "status", rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionCapability.ts" +)] +pub enum ExecutionCapability { + /// The operation is implemented by the executor. + Supported { + /// Stable operation code implemented by the executor. + operation_code: std::string::String, + }, + /// The operation is not implemented or is rejected by design. + Unsupported { + /// Stable reason code. + reason_code: std::string::String, + /// Human-readable reason. + reason: std::string::String, + }, +} + +impl crate::ExecutionCapability { + /// Creates a supported capability value. + pub fn supported(operation_code: impl std::convert::Into) -> Self { + return Self::Supported { operation_code: operation_code.into() }; + } + + /// Creates an unsupported capability value. + pub fn unsupported( + reason_code: impl std::convert::Into, + reason: impl std::convert::Into, + ) -> Self { + return Self::Unsupported { + reason_code: reason_code.into(), + reason: reason.into(), + }; + } + + /// Returns whether the capability is explicitly supported. + pub fn is_supported(&self) -> bool { + return matches!(self, Self::Supported { operation_code: _ }); + } +} + +/// Solana cluster expected by an execution plan. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionCluster.ts" +)] +pub enum ExecutionCluster { + /// Local validator cluster. + Localnet, + /// Public development cluster. + Devnet, + /// Public test cluster. + Testnet, + /// Public production cluster. + /// + /// RPC URLs and CLI aliases may still use `mainnet-beta`. + Mainnet, +} + +#[derive(serde::Deserialize)] +#[serde(rename_all = "snake_case")] +enum ExecutionClusterWire { + Localnet, + Devnet, + Testnet, + #[serde(alias = "mainnet_beta")] + Mainnet, +} + +impl<'de> serde::Deserialize<'de> for crate::ExecutionCluster { + fn deserialize( + deserializer: Deserializer, + ) -> std::result::Result + where + Deserializer: serde::Deserializer<'de>, + { + let wire = match ::deserialize(deserializer) { + std::result::Result::Ok(wire) => wire, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(match wire { + ExecutionClusterWire::Localnet => Self::Localnet, + ExecutionClusterWire::Devnet => Self::Devnet, + ExecutionClusterWire::Testnet => Self::Testnet, + ExecutionClusterWire::Mainnet => Self::Mainnet, + }); + } +} + +/// Cluster restrictions attached to an execution plan. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionClusterPolicy.ts" +)] +pub struct ExecutionClusterPolicy { + /// Cluster that the RPC endpoint must report. + pub expected_cluster: crate::ExecutionCluster, + /// Whether production execution may proceed after all other checks. + pub allow_mainnet: bool, + /// Whether the operator explicitly confirmed this production execution. + pub mainnet_confirmation: bool, +} + +impl std::default::Default for crate::ExecutionClusterPolicy { + fn default() -> Self { + return Self { + expected_cluster: crate::ExecutionCluster::Devnet, + allow_mainnet: false, + mainnet_confirmation: false, + }; + } +} + +/// Simulation requirement attached to an execution plan. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionSimulationPolicy.ts" +)] +pub enum ExecutionSimulationPolicy { + /// Simulation must succeed before signing or sending. + Required, + /// Simulation is not required. Safety policy rejects this mode by default. + Optional, +} + +/// Blockhash source required when a transaction is assembled. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionBlockhashKind.ts" +)] +pub enum ExecutionBlockhashKind { + /// Fetch a recent blockhash and enforce a bounded age. + Latest, + /// Use a durable nonce account and authority. + DurableNonce, +} + +/// Blockhash or durable nonce policy attached to an execution plan. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionBlockhashPolicy.ts" +)] +pub struct ExecutionBlockhashPolicy { + /// Selected blockhash source. + pub kind: crate::ExecutionBlockhashKind, + /// Maximum accepted age for a recent blockhash, in slots. + pub max_age_slots: std::option::Option, + /// Durable nonce account when `kind` is `DurableNonce`. + pub nonce_account: std::option::Option, + /// Durable nonce authority when `kind` is `DurableNonce`. + pub nonce_authority: std::option::Option, +} + +impl std::default::Default for crate::ExecutionBlockhashPolicy { + fn default() -> Self { + return Self { + kind: crate::ExecutionBlockhashKind::Latest, + max_age_slots: std::option::Option::Some(150), + nonce_account: std::option::Option::None, + nonce_authority: std::option::Option::None, + }; + } +} + +/// Explicit cost ceilings attached to an execution plan. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionCostLimit.ts" +)] +pub struct ExecutionCostLimit { + /// Maximum lamports that operation instructions may transfer or lock. + pub max_spend_lamports: std::option::Option, + /// Maximum total transaction fee accepted after simulation. + pub max_fee_lamports: std::option::Option, + /// Maximum compute-unit price accepted, in micro-lamports per compute unit. + pub max_compute_unit_price_micro_lamports: std::option::Option, +} + +impl std::default::Default for crate::ExecutionCostLimit { + fn default() -> Self { + return Self { + max_spend_lamports: std::option::Option::None, + max_fee_lamports: std::option::Option::None, + max_compute_unit_price_micro_lamports: std::option::Option::None, + }; + } +} + +/// Post-execution replay validation policy. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/PostExecutionValidationPolicy.ts" +)] +pub struct PostExecutionValidationPolicy { + /// Whether the sent signature must be inserted into canonical storage. + pub canonical_insert_required: bool, + /// Whether core extraction must complete. + pub core_extraction_required: bool, + /// Whether contextual decode replay must complete. + pub decode_replay_required: bool, + /// Whether compatible materializers must run. + pub materialization_required: bool, +} + +impl std::default::Default for crate::PostExecutionValidationPolicy { + fn default() -> Self { + return Self { + canonical_insert_required: true, + core_extraction_required: true, + decode_replay_required: true, + materialization_required: false, + }; + } +} + +/// Conservative policy carried from intent creation to transaction assembly. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionPolicy.ts" +)] +pub struct ExecutionPolicy { + /// Cluster restrictions. + pub cluster: crate::ExecutionClusterPolicy, + /// Simulation requirement. + pub simulation: crate::ExecutionSimulationPolicy, + /// Blockhash source restrictions. + pub blockhash: crate::ExecutionBlockhashPolicy, + /// Explicit operation and fee ceilings. + pub cost_limit: crate::ExecutionCostLimit, + /// Public keys authorized to sign this plan. + pub authorized_signers: std::vec::Vec, + /// Whether the operation is restricted to plan construction and simulation. + pub dry_run: bool, + /// Required post-execution validation steps. + pub post_execution_validation: crate::PostExecutionValidationPolicy, +} + +impl std::default::Default for crate::ExecutionPolicy { + fn default() -> Self { + return Self { + cluster: crate::ExecutionClusterPolicy::default(), + simulation: crate::ExecutionSimulationPolicy::Required, + blockhash: crate::ExecutionBlockhashPolicy::default(), + cost_limit: crate::ExecutionCostLimit::default(), + authorized_signers: std::vec::Vec::new(), + dry_run: true, + post_execution_validation: crate::PostExecutionValidationPolicy::default(), + }; + } +} + +/// One account required by a planned instruction. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/PlannedAccount.ts" +)] +pub struct PlannedAccount { + /// Account public key. + pub pubkey: crate::Pubkey, + /// Whether the account must sign the transaction. + pub is_signer: bool, + /// Whether the account must be writable. + pub is_writable: bool, +} + +/// One fully encoded instruction in an execution plan. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/PlannedInstruction.ts" +)] +pub struct PlannedInstruction { + /// Target program id. + pub program_id: crate::ProgramId, + /// Stable operation code represented by the instruction. + pub operation_code: std::string::String, + /// Ordered account metadata. + pub accounts: std::vec::Vec, + /// Exact encoded instruction payload. + pub data: std::vec::Vec, +} + +/// Signer required before transaction assembly. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/RequiredSigner.ts" +)] +pub struct RequiredSigner { + /// Signer public key. + pub pubkey: crate::Pubkey, + /// Stable signer role such as `fee_payer`, `funding_account` or `new_account`. + pub role: std::string::String, +} + +/// Typed execution plan produced before transaction assembly. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/PreparedExecutionPlan.ts" +)] +pub struct PreparedExecutionPlan { + /// Stable executor crate name. + pub executor_name: std::string::String, + /// Executor package version. + pub executor_version: std::string::String, + /// Caller-provided intent identifier. + pub intent_id: std::string::String, + /// Stable operation code. + pub operation_code: std::string::String, + /// Transaction fee payer. + pub fee_payer: crate::Pubkey, + /// Ordered instructions to place in the transaction message. + pub instructions: std::vec::Vec, + /// Deduplicated signers required by the fee payer and instruction metas. + pub required_signers: std::vec::Vec, + /// Explicit policy to evaluate before simulation, signing and sending. + pub policy: crate::ExecutionPolicy, + /// Lamports explicitly transferred or locked by planned instructions. + pub requested_spend_lamports: u64, + /// Requested compute-unit price, when present. + pub requested_compute_unit_price_micro_lamports: std::option::Option, +} + +/// Result returned by a transaction simulation adapter. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionSimulationResult.ts" +)] +pub struct ExecutionSimulationResult { + /// Whether an RPC simulation call was actually performed. + pub simulated: bool, + /// Whether the simulated transaction completed successfully. + pub success: bool, + /// Cluster reported by the simulation adapter. + pub cluster: crate::ExecutionCluster, + /// Blockhash source used by the simulated transaction. + pub blockhash_kind: crate::ExecutionBlockhashKind, + /// Observed recent blockhash age, in slots. + pub blockhash_age_slots: std::option::Option, + /// Replacement blockhash returned by the node, if replacement was requested. + #[serde(default)] + pub replacement_blockhash: std::option::Option, + /// Last valid block height associated with the replacement blockhash. + #[serde(default)] + pub replacement_last_valid_block_height: std::option::Option, + /// Durable nonce account used by the simulated transaction. + pub nonce_account: std::option::Option, + /// Durable nonce authority used by the simulated transaction. + pub nonce_authority: std::option::Option, + /// Compute units consumed when reported by the RPC endpoint. + pub units_consumed: std::option::Option, + /// Estimated transaction fee in lamports when available. + pub estimated_fee_lamports: std::option::Option, + /// Simulation logs in runtime order. + pub logs: std::vec::Vec, + /// Stable or provider error text when simulation failed. + pub error: std::option::Option, +} + +/// Result returned after an adapter submits a signed transaction. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionSendResult.ts" +)] +pub struct ExecutionSendResult { + /// Cluster used for submission. + pub cluster: crate::ExecutionCluster, + /// Signature returned by the RPC endpoint. + pub signature: crate::Signature, + /// Whether submission was accepted by the RPC endpoint. + pub submitted: bool, +} + +/// Confirmation state returned by an execution confirmation adapter. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionConfirmationStatus.ts" +)] +pub enum ExecutionConfirmationStatus { + /// The transaction was observed at processed commitment. + Processed, + /// The transaction reached confirmed commitment. + Confirmed, + /// The transaction reached finalized commitment. + Finalized, + /// The transaction was confirmed with a runtime error. + Failed, + /// The transaction was not confirmed before its blockhash expired. + Expired, + /// The bounded confirmation poll ended before reaching the requested commitment. + TimedOut, +} + +/// Result returned after waiting for transaction confirmation. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionConfirmationResult.ts" +)] +pub struct ExecutionConfirmationResult { + /// Cluster queried for confirmation. + pub cluster: crate::ExecutionCluster, + /// Submitted transaction signature. + pub signature: crate::Signature, + /// Highest confirmation state reached. + pub status: crate::ExecutionConfirmationStatus, + /// Slot that reported the transaction when available. + pub slot: std::option::Option, + /// Number of status polling attempts performed. + pub attempts: u32, + /// Last block height observed while checking recent-blockhash expiration. + pub last_observed_block_height: std::option::Option, + /// Stable or provider error text when confirmation failed. + pub error: std::option::Option, +} + +/// Diagnostic produced by post-execution canonical replay validation. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/PostExecutionDiagnostic.ts" +)] +pub struct PostExecutionDiagnostic { + /// Executed transaction signature. + pub signature: crate::Signature, + /// Whether canonical insertion completed. + pub canonical_inserted: bool, + /// Whether core extraction completed. + pub core_extracted: bool, + /// Whether contextual decode replay completed. + pub decode_replayed: bool, + /// Whether requested materialization completed. + pub materialized: bool, + /// Diagnostic messages from orchestration layers. + pub diagnostics: std::vec::Vec, +} + +/// Contract implemented by executors that expose typed intents and exact plans. +pub trait TypedInstructionExecutor { + /// Protocol-specific typed intent. + type Intent; + + /// Returns the exact capability for one program and operation code. + fn capability( + &self, + program_id: &crate::ProgramId, + operation_code: &str, + ) -> crate::ExecutionCapability; + + /// Builds a typed execution plan without signing, sending or performing I/O. + fn build_prepared_plan( + &self, + intent: &Self::Intent, + ) -> kb_core::Result; +} + +#[cfg(test)] +mod tests { + #[test] + fn mainnet_cluster_serializes_with_current_name() { + let serialized = serde_json::to_string(&crate::ExecutionCluster::Mainnet) + .unwrap_or_else(|error| panic!("unexpected serialization error: {error}")); + assert_eq!(serialized, "\"mainnet\""); + let legacy = serde_json::from_str::("\"mainnet_beta\"") + .unwrap_or_else(|error| panic!("unexpected deserialization error: {error}")); + assert_eq!(legacy, crate::ExecutionCluster::Mainnet); + } +} diff --git a/kb-lib/src/executor/api/executor.rs b/kb-lib/src/executor/api/executor.rs new file mode 100644 index 0000000..499ce59 --- /dev/null +++ b/kb-lib/src/executor/api/executor.rs @@ -0,0 +1,104 @@ +// file: kb-lib/src/executor/api/executor.rs +// version: 3 + +//! Shared execution contract and neutral request models. + +use ts_rs::TS; // rust-rules: derive-import + +/// Support level returned by an executor for a requested operation. +#[derive(Clone, Copy, Debug, Eq, PartialEq, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/executor/ExecutionSupport.ts" +)] +pub enum ExecutionSupport { + /// The executor cannot handle the request. + No, + /// The executor may handle the request after account and payload validation. + Maybe, + /// The executor explicitly supports the request. + Yes, +} + +/// Generic request used before protocol-specific execution models are introduced. +#[derive(Clone, Debug, Eq, PartialEq, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/executor/ExecutionRequest.ts" +)] +pub struct ExecutionRequest { + /// Target program id. + pub program_id: crate::ProgramId, + /// Stable operation code such as buy, sell, transfer or route. + pub operation_code: std::string::String, + /// Serialized JSON payload reserved for protocol-specific arguments. + pub payload_json: std::string::String, +} + +/// Neutral execution plan placeholder produced by executor crates. +#[derive(Clone, Debug, Eq, PartialEq, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/executor/ExecutionPlan.ts" +)] +pub struct ExecutionPlan { + /// Stable executor crate name. + pub executor_name: std::string::String, + /// Number of instructions that would be produced by the final implementation. + pub instruction_count: usize, + /// Serialized JSON payload reserved for future instruction data and account metadata. + pub payload_json: std::string::String, +} + +/// Serializes a JSON payload to its compact string representation. +pub fn serialize_payload_json(value: &serde_json::Value) -> kb_core::Result { + return match serde_json::to_string(value) { + std::result::Result::Ok(serialized) => std::result::Result::Ok(serialized), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "execution_payload_json_serialize_failed", + error.to_string(), + )), + }; +} + +/// Serializes a JSON payload to its pretty string representation. +pub fn serialize_payload_json_pretty( + value: &serde_json::Value, +) -> kb_core::Result { + return match serde_json::to_string_pretty(value) { + std::result::Result::Ok(serialized) => std::result::Result::Ok(serialized), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "execution_payload_json_pretty_serialize_failed", + error.to_string(), + )), + }; +} + +/// Common contract implemented by all execution crates. +pub trait InstructionExecutor { + /// Returns the stable executor name. + fn executor_name(&self) -> &'static str; + + /// Returns the executor version. + fn executor_version(&self) -> &'static str; + + /// Returns the program ids handled by this executor. + fn program_ids(&self) -> &'static [&'static str]; + + /// Tests whether this executor owns a program id. + fn handles_program_id(&self, program_id: &crate::ProgramId) -> bool { + return self + .program_ids() + .iter() + .any(|candidate| return *candidate == program_id.0.as_str()); + } + + /// Tests whether this executor supports a request. + fn supports_request(&self, request: &crate::ExecutionRequest) -> crate::ExecutionSupport; + + /// Builds a neutral execution plan placeholder. + fn build_plan( + &self, + request: &crate::ExecutionRequest, + ) -> kb_core::Result; +} diff --git a/kb-lib/src/executor/bridge.rs b/kb-lib/src/executor/bridge.rs new file mode 100644 index 0000000..a7318eb --- /dev/null +++ b/kb-lib/src/executor/bridge.rs @@ -0,0 +1,9 @@ +// file: kb-lib/src/executor/bridge.rs +// version: 1 + +//! `bridge` executor family. + +pub mod circle_cctp_token_messenger_minter; +pub mod circle_cctp_token_messenger_minter_v2; +pub mod layer_zero_endpoint; +pub mod layer_zero_executor; diff --git a/kb-lib/src/executor/bridge/circle_cctp_token_messenger_minter.rs b/kb-lib/src/executor/bridge/circle_cctp_token_messenger_minter.rs new file mode 100644 index 0000000..fde20ef --- /dev/null +++ b/kb-lib/src/executor/bridge/circle_cctp_token_messenger_minter.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/bridge/circle_cctp_token_messenger_minter.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_bridge_circle_cctp_token_messenger_minter`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_bridge_circle_cctp_token_messenger_minter"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/bridge/circle_cctp_token_messenger_minter_v2.rs b/kb-lib/src/executor/bridge/circle_cctp_token_messenger_minter_v2.rs new file mode 100644 index 0000000..b30dcc7 --- /dev/null +++ b/kb-lib/src/executor/bridge/circle_cctp_token_messenger_minter_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/bridge/circle_cctp_token_messenger_minter_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_bridge_circle_cctp_token_messenger_minter_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_bridge_circle_cctp_token_messenger_minter_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/bridge/layer_zero_endpoint.rs b/kb-lib/src/executor/bridge/layer_zero_endpoint.rs new file mode 100644 index 0000000..d25f62f --- /dev/null +++ b/kb-lib/src/executor/bridge/layer_zero_endpoint.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/bridge/layer_zero_endpoint.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_bridge_layer_zero_endpoint`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_bridge_layer_zero_endpoint"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/bridge/layer_zero_executor.rs b/kb-lib/src/executor/bridge/layer_zero_executor.rs new file mode 100644 index 0000000..be521ae --- /dev/null +++ b/kb-lib/src/executor/bridge/layer_zero_executor.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/bridge/layer_zero_executor.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_bridge_layer_zero_executor`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_bridge_layer_zero_executor"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/clmm.rs b/kb-lib/src/executor/clmm.rs new file mode 100644 index 0000000..10e30be --- /dev/null +++ b/kb-lib/src/executor/clmm.rs @@ -0,0 +1,11 @@ +// file: kb-lib/src/executor/clmm.rs +// version: 1 + +//! `clmm` executor family. + +pub mod byreal; +pub mod fusion; +pub mod orca_whirlpool; +pub mod pancake_swap; +pub mod raydium; +pub mod stabble; diff --git a/kb-lib/src/executor/clmm/byreal.rs b/kb-lib/src/executor/clmm/byreal.rs new file mode 100644 index 0000000..474a272 --- /dev/null +++ b/kb-lib/src/executor/clmm/byreal.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/clmm/byreal.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_clmm_byreal`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_clmm_byreal"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/clmm/fusion.rs b/kb-lib/src/executor/clmm/fusion.rs new file mode 100644 index 0000000..b4372c9 --- /dev/null +++ b/kb-lib/src/executor/clmm/fusion.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/clmm/fusion.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_clmm_fusion`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_clmm_fusion"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/clmm/orca_whirlpool.rs b/kb-lib/src/executor/clmm/orca_whirlpool.rs new file mode 100644 index 0000000..e928020 --- /dev/null +++ b/kb-lib/src/executor/clmm/orca_whirlpool.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/clmm/orca_whirlpool.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_clmm_orca_whirlpool`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_clmm_orca_whirlpool"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/clmm/pancake_swap.rs b/kb-lib/src/executor/clmm/pancake_swap.rs new file mode 100644 index 0000000..6597e32 --- /dev/null +++ b/kb-lib/src/executor/clmm/pancake_swap.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/clmm/pancake_swap.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_clmm_pancake_swap`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_clmm_pancake_swap"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/clmm/raydium.rs b/kb-lib/src/executor/clmm/raydium.rs new file mode 100644 index 0000000..cbcafc3 --- /dev/null +++ b/kb-lib/src/executor/clmm/raydium.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/clmm/raydium.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_clmm_raydium`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_clmm_raydium"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/clmm/stabble.rs b/kb-lib/src/executor/clmm/stabble.rs new file mode 100644 index 0000000..9e7ef93 --- /dev/null +++ b/kb-lib/src/executor/clmm/stabble.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/clmm/stabble.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_clmm_stabble`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_clmm_stabble"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/cpmm.rs b/kb-lib/src/executor/cpmm.rs new file mode 100644 index 0000000..b478176 --- /dev/null +++ b/kb-lib/src/executor/cpmm.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/executor/cpmm.rs +// version: 1 + +//! `cpmm` executor family. + +pub mod raydium; diff --git a/kb-lib/src/executor/cpmm/raydium.rs b/kb-lib/src/executor/cpmm/raydium.rs new file mode 100644 index 0000000..ec33d26 --- /dev/null +++ b/kb-lib/src/executor/cpmm/raydium.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/cpmm/raydium.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_cpmm_raydium`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_cpmm_raydium"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/dlmm.rs b/kb-lib/src/executor/dlmm.rs new file mode 100644 index 0000000..be1c1b3 --- /dev/null +++ b/kb-lib/src/executor/dlmm.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/executor/dlmm.rs +// version: 1 + +//! `dlmm` executor family. + +pub mod meteora; diff --git a/kb-lib/src/executor/dlmm/meteora.rs b/kb-lib/src/executor/dlmm/meteora.rs new file mode 100644 index 0000000..f0ec784 --- /dev/null +++ b/kb-lib/src/executor/dlmm/meteora.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/dlmm/meteora.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_dlmm_meteora`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_dlmm_meteora"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/fees.rs b/kb-lib/src/executor/fees.rs new file mode 100644 index 0000000..52be93c --- /dev/null +++ b/kb-lib/src/executor/fees.rs @@ -0,0 +1,7 @@ +// file: kb-lib/src/executor/fees.rs +// version: 1 + +//! `fees` executor family. + +pub mod bags_fee_share_v1; +pub mod bags_fee_share_v2; diff --git a/kb-lib/src/executor/fees/bags_fee_share_v1.rs b/kb-lib/src/executor/fees/bags_fee_share_v1.rs new file mode 100644 index 0000000..0a4b259 --- /dev/null +++ b/kb-lib/src/executor/fees/bags_fee_share_v1.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/fees/bags_fee_share_v1.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_fees_bags_fee_share_v1`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_fees_bags_fee_share_v1"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/fees/bags_fee_share_v2.rs b/kb-lib/src/executor/fees/bags_fee_share_v2.rs new file mode 100644 index 0000000..1dbed37 --- /dev/null +++ b/kb-lib/src/executor/fees/bags_fee_share_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/fees/bags_fee_share_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_fees_bags_fee_share_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_fees_bags_fee_share_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/governance.rs b/kb-lib/src/executor/governance.rs new file mode 100644 index 0000000..f570307 --- /dev/null +++ b/kb-lib/src/executor/governance.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/executor/governance.rs +// version: 1 + +//! `governance` executor family. + +pub mod metadao_bid_wall; diff --git a/kb-lib/src/executor/governance/metadao_bid_wall.rs b/kb-lib/src/executor/governance/metadao_bid_wall.rs new file mode 100644 index 0000000..7ca7ecf --- /dev/null +++ b/kb-lib/src/executor/governance/metadao_bid_wall.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/governance/metadao_bid_wall.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_governance_metadao_bid_wall`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_governance_metadao_bid_wall"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/launchpad.rs b/kb-lib/src/executor/launchpad.rs new file mode 100644 index 0000000..bbc2ba3 --- /dev/null +++ b/kb-lib/src/executor/launchpad.rs @@ -0,0 +1,14 @@ +// file: kb-lib/src/executor/launchpad.rs +// version: 1 + +//! `launchpad` executor family. + +pub mod boop_fun; +pub mod metadao_ico; +pub mod meteora_dbc; +pub mod moonit; +pub mod orca_wavebreak; +pub mod printr; +pub mod pump_fun; +pub mod pump_pumpup_ai; +pub mod raydium_launchlab; diff --git a/kb-lib/src/executor/launchpad/boop_fun.rs b/kb-lib/src/executor/launchpad/boop_fun.rs new file mode 100644 index 0000000..6022143 --- /dev/null +++ b/kb-lib/src/executor/launchpad/boop_fun.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/launchpad/boop_fun.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_launchpad_boop_fun`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_launchpad_boop_fun"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/launchpad/metadao_ico.rs b/kb-lib/src/executor/launchpad/metadao_ico.rs new file mode 100644 index 0000000..1a8ef7e --- /dev/null +++ b/kb-lib/src/executor/launchpad/metadao_ico.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/launchpad/metadao_ico.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_launchpad_metadao_ico`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_launchpad_metadao_ico"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/launchpad/meteora_dbc.rs b/kb-lib/src/executor/launchpad/meteora_dbc.rs new file mode 100644 index 0000000..bb25599 --- /dev/null +++ b/kb-lib/src/executor/launchpad/meteora_dbc.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/launchpad/meteora_dbc.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_launchpad_meteora_dbc`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_launchpad_meteora_dbc"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/launchpad/moonit.rs b/kb-lib/src/executor/launchpad/moonit.rs new file mode 100644 index 0000000..e460dd6 --- /dev/null +++ b/kb-lib/src/executor/launchpad/moonit.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/launchpad/moonit.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_launchpad_moonit`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_launchpad_moonit"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/launchpad/orca_wavebreak.rs b/kb-lib/src/executor/launchpad/orca_wavebreak.rs new file mode 100644 index 0000000..c18acb7 --- /dev/null +++ b/kb-lib/src/executor/launchpad/orca_wavebreak.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/launchpad/orca_wavebreak.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_launchpad_orca_wavebreak`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_launchpad_orca_wavebreak"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/launchpad/printr.rs b/kb-lib/src/executor/launchpad/printr.rs new file mode 100644 index 0000000..58db109 --- /dev/null +++ b/kb-lib/src/executor/launchpad/printr.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/launchpad/printr.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_launchpad_printr`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_launchpad_printr"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/launchpad/pump_fun.rs b/kb-lib/src/executor/launchpad/pump_fun.rs new file mode 100644 index 0000000..39f1bab --- /dev/null +++ b/kb-lib/src/executor/launchpad/pump_fun.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/launchpad/pump_fun.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_launchpad_pump_fun`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_launchpad_pump_fun"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/launchpad/pump_pumpup_ai.rs b/kb-lib/src/executor/launchpad/pump_pumpup_ai.rs new file mode 100644 index 0000000..7e265b8 --- /dev/null +++ b/kb-lib/src/executor/launchpad/pump_pumpup_ai.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/launchpad/pump_pumpup_ai.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_launchpad_pump_pumpup_ai`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_launchpad_pump_pumpup_ai"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/launchpad/raydium_launchlab.rs b/kb-lib/src/executor/launchpad/raydium_launchlab.rs new file mode 100644 index 0000000..3ca77cf --- /dev/null +++ b/kb-lib/src/executor/launchpad/raydium_launchlab.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/launchpad/raydium_launchlab.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_launchpad_raydium_launchlab`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_launchpad_raydium_launchlab"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/lending.rs b/kb-lib/src/executor/lending.rs new file mode 100644 index 0000000..ef2039a --- /dev/null +++ b/kb-lib/src/executor/lending.rs @@ -0,0 +1,12 @@ +// file: kb-lib/src/executor/lending.rs +// version: 1 + +//! `lending` executor family. + +pub mod clone; +pub mod jupiter_lend_borrow; +pub mod jupiter_lend_earn; +pub mod jupiter_lend_flash_loan; +pub mod jupiter_lend_liquidity; +pub mod kamino; +pub mod marginfi_v2; diff --git a/kb-lib/src/executor/lending/clone.rs b/kb-lib/src/executor/lending/clone.rs new file mode 100644 index 0000000..c65ca42 --- /dev/null +++ b/kb-lib/src/executor/lending/clone.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/lending/clone.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_lending_clone`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_lending_clone"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/lending/jupiter_lend_borrow.rs b/kb-lib/src/executor/lending/jupiter_lend_borrow.rs new file mode 100644 index 0000000..88ae809 --- /dev/null +++ b/kb-lib/src/executor/lending/jupiter_lend_borrow.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/lending/jupiter_lend_borrow.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_lending_jupiter_lend_borrow`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_lending_jupiter_lend_borrow"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/lending/jupiter_lend_earn.rs b/kb-lib/src/executor/lending/jupiter_lend_earn.rs new file mode 100644 index 0000000..d617478 --- /dev/null +++ b/kb-lib/src/executor/lending/jupiter_lend_earn.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/lending/jupiter_lend_earn.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_lending_jupiter_lend_earn`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_lending_jupiter_lend_earn"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/lending/jupiter_lend_flash_loan.rs b/kb-lib/src/executor/lending/jupiter_lend_flash_loan.rs new file mode 100644 index 0000000..f63b6e0 --- /dev/null +++ b/kb-lib/src/executor/lending/jupiter_lend_flash_loan.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/lending/jupiter_lend_flash_loan.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_lending_jupiter_lend_flash_loan`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_lending_jupiter_lend_flash_loan"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/lending/jupiter_lend_liquidity.rs b/kb-lib/src/executor/lending/jupiter_lend_liquidity.rs new file mode 100644 index 0000000..718ebcb --- /dev/null +++ b/kb-lib/src/executor/lending/jupiter_lend_liquidity.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/lending/jupiter_lend_liquidity.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_lending_jupiter_lend_liquidity`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_lending_jupiter_lend_liquidity"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/lending/kamino.rs b/kb-lib/src/executor/lending/kamino.rs new file mode 100644 index 0000000..22e777a --- /dev/null +++ b/kb-lib/src/executor/lending/kamino.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/lending/kamino.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_lending_kamino`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_lending_kamino"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/lending/marginfi_v2.rs b/kb-lib/src/executor/lending/marginfi_v2.rs new file mode 100644 index 0000000..d50e56f --- /dev/null +++ b/kb-lib/src/executor/lending/marginfi_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/lending/marginfi_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_lending_marginfi_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_lending_marginfi_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/lock.rs b/kb-lib/src/executor/lock.rs new file mode 100644 index 0000000..6cd3b9c --- /dev/null +++ b/kb-lib/src/executor/lock.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/executor/lock.rs +// version: 1 + +//! `lock` executor family. + +pub mod raydium_lp; diff --git a/kb-lib/src/executor/lock/raydium_lp.rs b/kb-lib/src/executor/lock/raydium_lp.rs new file mode 100644 index 0000000..7819f0e --- /dev/null +++ b/kb-lib/src/executor/lock/raydium_lp.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/lock/raydium_lp.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_lock_raydium_lp`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_lock_raydium_lp"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/metadata.rs b/kb-lib/src/executor/metadata.rs new file mode 100644 index 0000000..a9c6301 --- /dev/null +++ b/kb-lib/src/executor/metadata.rs @@ -0,0 +1,7 @@ +// file: kb-lib/src/executor/metadata.rs +// version: 1 + +//! `metadata` executor family. + +pub mod metaplex_token_metadata; +pub mod spl_name_service; diff --git a/kb-lib/src/executor/metadata/metaplex_token_metadata.rs b/kb-lib/src/executor/metadata/metaplex_token_metadata.rs new file mode 100644 index 0000000..e6dbc89 --- /dev/null +++ b/kb-lib/src/executor/metadata/metaplex_token_metadata.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/metadata/metaplex_token_metadata.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_metadata_metaplex_token_metadata`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_metadata_metaplex_token_metadata"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/metadata/spl_name_service.rs b/kb-lib/src/executor/metadata/spl_name_service.rs new file mode 100644 index 0000000..b30e226 --- /dev/null +++ b/kb-lib/src/executor/metadata/spl_name_service.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/metadata/spl_name_service.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_metadata_spl_name_service`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_metadata_spl_name_service"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/nft.rs b/kb-lib/src/executor/nft.rs new file mode 100644 index 0000000..9c0ad96 --- /dev/null +++ b/kb-lib/src/executor/nft.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/executor/nft.rs +// version: 1 + +//! `nft` executor family. + +pub mod metaplex_bubblegum; diff --git a/kb-lib/src/executor/nft/metaplex_bubblegum.rs b/kb-lib/src/executor/nft/metaplex_bubblegum.rs new file mode 100644 index 0000000..c44f26b --- /dev/null +++ b/kb-lib/src/executor/nft/metaplex_bubblegum.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/nft/metaplex_bubblegum.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_nft_metaplex_bubblegum`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_nft_metaplex_bubblegum"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/orderbook.rs b/kb-lib/src/executor/orderbook.rs new file mode 100644 index 0000000..54fa55d --- /dev/null +++ b/kb-lib/src/executor/orderbook.rs @@ -0,0 +1,8 @@ +// file: kb-lib/src/executor/orderbook.rs +// version: 1 + +//! `orderbook` executor family. + +pub mod jupiter_limit_order; +pub mod jupiter_limit_order_v2; +pub mod openbook_v2; diff --git a/kb-lib/src/executor/orderbook/jupiter_limit_order.rs b/kb-lib/src/executor/orderbook/jupiter_limit_order.rs new file mode 100644 index 0000000..ab3f427 --- /dev/null +++ b/kb-lib/src/executor/orderbook/jupiter_limit_order.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/orderbook/jupiter_limit_order.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_orderbook_jupiter_limit_order`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_orderbook_jupiter_limit_order"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/orderbook/jupiter_limit_order_v2.rs b/kb-lib/src/executor/orderbook/jupiter_limit_order_v2.rs new file mode 100644 index 0000000..a7de10b --- /dev/null +++ b/kb-lib/src/executor/orderbook/jupiter_limit_order_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/orderbook/jupiter_limit_order_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_orderbook_jupiter_limit_order_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_orderbook_jupiter_limit_order_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/orderbook/openbook_v2.rs b/kb-lib/src/executor/orderbook/openbook_v2.rs new file mode 100644 index 0000000..c1ec67c --- /dev/null +++ b/kb-lib/src/executor/orderbook/openbook_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/orderbook/openbook_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_orderbook_openbook_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_orderbook_openbook_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/perpetuals.rs b/kb-lib/src/executor/perpetuals.rs new file mode 100644 index 0000000..5695fc7 --- /dev/null +++ b/kb-lib/src/executor/perpetuals.rs @@ -0,0 +1,8 @@ +// file: kb-lib/src/executor/perpetuals.rs +// version: 1 + +//! `perpetuals` executor family. + +pub mod drift_v2; +pub mod jupiter; +pub mod zeta; diff --git a/kb-lib/src/executor/perpetuals/drift_v2.rs b/kb-lib/src/executor/perpetuals/drift_v2.rs new file mode 100644 index 0000000..fd40831 --- /dev/null +++ b/kb-lib/src/executor/perpetuals/drift_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/perpetuals/drift_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_perpetuals_drift_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_perpetuals_drift_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/perpetuals/jupiter.rs b/kb-lib/src/executor/perpetuals/jupiter.rs new file mode 100644 index 0000000..d9933ff --- /dev/null +++ b/kb-lib/src/executor/perpetuals/jupiter.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/perpetuals/jupiter.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_perpetuals_jupiter`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_perpetuals_jupiter"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/perpetuals/zeta.rs b/kb-lib/src/executor/perpetuals/zeta.rs new file mode 100644 index 0000000..40ec751 --- /dev/null +++ b/kb-lib/src/executor/perpetuals/zeta.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/perpetuals/zeta.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_perpetuals_zeta`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_perpetuals_zeta"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/router.rs b/kb-lib/src/executor/router.rs new file mode 100644 index 0000000..7f7c883 --- /dev/null +++ b/kb-lib/src/executor/router.rs @@ -0,0 +1,11 @@ +// file: kb-lib/src/executor/router.rs +// version: 1 + +//! `router` executor family. + +pub mod dflow_aggregator_v4; +pub mod jupiter_aggregator_v4; +pub mod jupiter_aggregator_v6; +pub mod jupiter_dca; +pub mod okx_labs_v1; +pub mod okx_labs_v2; diff --git a/kb-lib/src/executor/router/dflow_aggregator_v4.rs b/kb-lib/src/executor/router/dflow_aggregator_v4.rs new file mode 100644 index 0000000..27f3bda --- /dev/null +++ b/kb-lib/src/executor/router/dflow_aggregator_v4.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/router/dflow_aggregator_v4.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_router_dflow_aggregator_v4`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_router_dflow_aggregator_v4"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/router/jupiter_aggregator_v4.rs b/kb-lib/src/executor/router/jupiter_aggregator_v4.rs new file mode 100644 index 0000000..d9cf3ec --- /dev/null +++ b/kb-lib/src/executor/router/jupiter_aggregator_v4.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/router/jupiter_aggregator_v4.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_router_jupiter_aggregator_v4`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_router_jupiter_aggregator_v4"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/router/jupiter_aggregator_v6.rs b/kb-lib/src/executor/router/jupiter_aggregator_v6.rs new file mode 100644 index 0000000..fc80545 --- /dev/null +++ b/kb-lib/src/executor/router/jupiter_aggregator_v6.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/router/jupiter_aggregator_v6.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_router_jupiter_aggregator_v6`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_router_jupiter_aggregator_v6"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/router/jupiter_dca.rs b/kb-lib/src/executor/router/jupiter_dca.rs new file mode 100644 index 0000000..fc08cfb --- /dev/null +++ b/kb-lib/src/executor/router/jupiter_dca.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/router/jupiter_dca.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_router_jupiter_dca`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_router_jupiter_dca"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/router/okx_labs_v1.rs b/kb-lib/src/executor/router/okx_labs_v1.rs new file mode 100644 index 0000000..dbe5ddf --- /dev/null +++ b/kb-lib/src/executor/router/okx_labs_v1.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/router/okx_labs_v1.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_router_okx_labs_v1`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_router_okx_labs_v1"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/router/okx_labs_v2.rs b/kb-lib/src/executor/router/okx_labs_v2.rs new file mode 100644 index 0000000..5151d15 --- /dev/null +++ b/kb-lib/src/executor/router/okx_labs_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/router/okx_labs_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_router_okx_labs_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_router_okx_labs_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/rwa.rs b/kb-lib/src/executor/rwa.rs new file mode 100644 index 0000000..b964b14 --- /dev/null +++ b/kb-lib/src/executor/rwa.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/executor/rwa.rs +// version: 1 + +//! `rwa` executor family. + +pub mod ondo_global_markets; diff --git a/kb-lib/src/executor/rwa/ondo_global_markets.rs b/kb-lib/src/executor/rwa/ondo_global_markets.rs new file mode 100644 index 0000000..b00f533 --- /dev/null +++ b/kb-lib/src/executor/rwa/ondo_global_markets.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/rwa/ondo_global_markets.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_rwa_ondo_global_markets`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_rwa_ondo_global_markets"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/solana.rs b/kb-lib/src/executor/solana.rs new file mode 100644 index 0000000..0b85a08 --- /dev/null +++ b/kb-lib/src/executor/solana.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/executor/solana.rs +// version: 1 + +//! `solana` executor family. + +pub mod core; diff --git a/kb-lib/src/executor/solana/core.rs b/kb-lib/src/executor/solana/core.rs new file mode 100644 index 0000000..ea6ba2a --- /dev/null +++ b/kb-lib/src/executor/solana/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/solana/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_solana_core`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_solana_core"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/spl.rs b/kb-lib/src/executor/spl.rs new file mode 100644 index 0000000..b331ab9 --- /dev/null +++ b/kb-lib/src/executor/spl.rs @@ -0,0 +1,14 @@ +// file: kb-lib/src/executor/spl.rs +// version: 1 + +//! `spl` executor family. + +pub mod account_compression; +pub mod associated_token_account; +pub mod elgamal_registry; +pub mod memo; +pub mod noop; +pub mod single_pool; +pub mod stake_pool; +pub mod token; +pub mod token_2022; diff --git a/kb-lib/src/executor/spl/account_compression.rs b/kb-lib/src/executor/spl/account_compression.rs new file mode 100644 index 0000000..7bb7f10 --- /dev/null +++ b/kb-lib/src/executor/spl/account_compression.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/spl/account_compression.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_spl_account_compression`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_spl_account_compression"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/spl/associated_token_account.rs b/kb-lib/src/executor/spl/associated_token_account.rs new file mode 100644 index 0000000..937f148 --- /dev/null +++ b/kb-lib/src/executor/spl/associated_token_account.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/spl/associated_token_account.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_spl_associated_token_account`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_spl_associated_token_account"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/spl/elgamal_registry.rs b/kb-lib/src/executor/spl/elgamal_registry.rs new file mode 100644 index 0000000..9f490ac --- /dev/null +++ b/kb-lib/src/executor/spl/elgamal_registry.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/spl/elgamal_registry.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_spl_elgamal_registry`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_spl_elgamal_registry"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/spl/memo.rs b/kb-lib/src/executor/spl/memo.rs new file mode 100644 index 0000000..50aab62 --- /dev/null +++ b/kb-lib/src/executor/spl/memo.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/spl/memo.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_spl_memo`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_spl_memo"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/spl/noop.rs b/kb-lib/src/executor/spl/noop.rs new file mode 100644 index 0000000..c162f17 --- /dev/null +++ b/kb-lib/src/executor/spl/noop.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/spl/noop.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_spl_noop`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_spl_noop"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/spl/single_pool.rs b/kb-lib/src/executor/spl/single_pool.rs new file mode 100644 index 0000000..0087f53 --- /dev/null +++ b/kb-lib/src/executor/spl/single_pool.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/spl/single_pool.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_spl_single_pool`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_spl_single_pool"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/spl/stake_pool.rs b/kb-lib/src/executor/spl/stake_pool.rs new file mode 100644 index 0000000..96a0e24 --- /dev/null +++ b/kb-lib/src/executor/spl/stake_pool.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/spl/stake_pool.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_spl_stake_pool`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_spl_stake_pool"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/spl/token.rs b/kb-lib/src/executor/spl/token.rs new file mode 100644 index 0000000..868e586 --- /dev/null +++ b/kb-lib/src/executor/spl/token.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/spl/token.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_spl_token`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_spl_token"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/spl/token_2022.rs b/kb-lib/src/executor/spl/token_2022.rs new file mode 100644 index 0000000..fc839bf --- /dev/null +++ b/kb-lib/src/executor/spl/token_2022.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/spl/token_2022.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_spl_token_2022`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_spl_token_2022"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/stable.rs b/kb-lib/src/executor/stable.rs new file mode 100644 index 0000000..f8254a0 --- /dev/null +++ b/kb-lib/src/executor/stable.rs @@ -0,0 +1,9 @@ +// file: kb-lib/src/executor/stable.rs +// version: 1 + +//! `stable` executor family. + +pub mod swap_hylo_exchange; +pub mod swap_jupiter_stable; +pub mod swap_numeraire; +pub mod swap_stabble; diff --git a/kb-lib/src/executor/stable/swap_hylo_exchange.rs b/kb-lib/src/executor/stable/swap_hylo_exchange.rs new file mode 100644 index 0000000..92f7706 --- /dev/null +++ b/kb-lib/src/executor/stable/swap_hylo_exchange.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/stable/swap_hylo_exchange.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_stable_swap_hylo_exchange`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_stable_swap_hylo_exchange"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/stable/swap_jupiter_stable.rs b/kb-lib/src/executor/stable/swap_jupiter_stable.rs new file mode 100644 index 0000000..81a1b69 --- /dev/null +++ b/kb-lib/src/executor/stable/swap_jupiter_stable.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/stable/swap_jupiter_stable.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_stable_swap_jupiter_stable`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_stable_swap_jupiter_stable"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/stable/swap_numeraire.rs b/kb-lib/src/executor/stable/swap_numeraire.rs new file mode 100644 index 0000000..968444e --- /dev/null +++ b/kb-lib/src/executor/stable/swap_numeraire.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/stable/swap_numeraire.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_stable_swap_numeraire`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_stable_swap_numeraire"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/stable/swap_stabble.rs b/kb-lib/src/executor/stable/swap_stabble.rs new file mode 100644 index 0000000..e0d6023 --- /dev/null +++ b/kb-lib/src/executor/stable/swap_stabble.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/stable/swap_stabble.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_stable_swap_stabble`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_stable_swap_stabble"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/staking.rs b/kb-lib/src/executor/staking.rs new file mode 100644 index 0000000..2531085 --- /dev/null +++ b/kb-lib/src/executor/staking.rs @@ -0,0 +1,8 @@ +// file: kb-lib/src/executor/staking.rs +// version: 1 + +//! `staking` executor family. + +pub mod kamino_farm; +pub mod marinade_finance; +pub mod solayer; diff --git a/kb-lib/src/executor/staking/kamino_farm.rs b/kb-lib/src/executor/staking/kamino_farm.rs new file mode 100644 index 0000000..d0984a4 --- /dev/null +++ b/kb-lib/src/executor/staking/kamino_farm.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/staking/kamino_farm.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_staking_kamino_farm`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_staking_kamino_farm"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/staking/marinade_finance.rs b/kb-lib/src/executor/staking/marinade_finance.rs new file mode 100644 index 0000000..7450d6e --- /dev/null +++ b/kb-lib/src/executor/staking/marinade_finance.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/staking/marinade_finance.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_staking_marinade_finance`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_staking_marinade_finance"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/staking/solayer.rs b/kb-lib/src/executor/staking/solayer.rs new file mode 100644 index 0000000..49e386e --- /dev/null +++ b/kb-lib/src/executor/staking/solayer.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/staking/solayer.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_staking_solayer`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_staking_solayer"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/treasury.rs b/kb-lib/src/executor/treasury.rs new file mode 100644 index 0000000..00f22e4 --- /dev/null +++ b/kb-lib/src/executor/treasury.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/executor/treasury.rs +// version: 1 + +//! `treasury` executor family. + +pub mod helium_treasury_management; diff --git a/kb-lib/src/executor/treasury/helium_treasury_management.rs b/kb-lib/src/executor/treasury/helium_treasury_management.rs new file mode 100644 index 0000000..75ef644 --- /dev/null +++ b/kb-lib/src/executor/treasury/helium_treasury_management.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/treasury/helium_treasury_management.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_treasury_helium_treasury_management`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_treasury_helium_treasury_management"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/vault.rs b/kb-lib/src/executor/vault.rs new file mode 100644 index 0000000..abba931 --- /dev/null +++ b/kb-lib/src/executor/vault.rs @@ -0,0 +1,11 @@ +// file: kb-lib/src/executor/vault.rs +// version: 1 + +//! `vault` executor family. + +pub mod carrot_defi; +pub mod hylo_stability_pool; +pub mod kamino; +pub mod kamino_v2; +pub mod kamino_yvaults; +pub mod meteora; diff --git a/kb-lib/src/executor/vault/carrot_defi.rs b/kb-lib/src/executor/vault/carrot_defi.rs new file mode 100644 index 0000000..070dd77 --- /dev/null +++ b/kb-lib/src/executor/vault/carrot_defi.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/vault/carrot_defi.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_vault_carrot_defi`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_vault_carrot_defi"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/vault/hylo_stability_pool.rs b/kb-lib/src/executor/vault/hylo_stability_pool.rs new file mode 100644 index 0000000..98ad890 --- /dev/null +++ b/kb-lib/src/executor/vault/hylo_stability_pool.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/vault/hylo_stability_pool.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_vault_hylo_stability_pool`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_vault_hylo_stability_pool"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/vault/kamino.rs b/kb-lib/src/executor/vault/kamino.rs new file mode 100644 index 0000000..c773b6b --- /dev/null +++ b/kb-lib/src/executor/vault/kamino.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/vault/kamino.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_vault_kamino`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_vault_kamino"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/vault/kamino_v2.rs b/kb-lib/src/executor/vault/kamino_v2.rs new file mode 100644 index 0000000..f8fb6cf --- /dev/null +++ b/kb-lib/src/executor/vault/kamino_v2.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/vault/kamino_v2.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_vault_kamino_v2`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_vault_kamino_v2"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/vault/kamino_yvaults.rs b/kb-lib/src/executor/vault/kamino_yvaults.rs new file mode 100644 index 0000000..5c1acd4 --- /dev/null +++ b/kb-lib/src/executor/vault/kamino_yvaults.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/vault/kamino_yvaults.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_vault_kamino_yvaults`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_vault_kamino_yvaults"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/vault/meteora.rs b/kb-lib/src/executor/vault/meteora.rs new file mode 100644 index 0000000..4510a87 --- /dev/null +++ b/kb-lib/src/executor/vault/meteora.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/vault/meteora.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_vault_meteora`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_vault_meteora"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/vesting.rs b/kb-lib/src/executor/vesting.rs new file mode 100644 index 0000000..c45954d --- /dev/null +++ b/kb-lib/src/executor/vesting.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/executor/vesting.rs +// version: 1 + +//! `vesting` executor family. + +pub mod streamflow; diff --git a/kb-lib/src/executor/vesting/streamflow.rs b/kb-lib/src/executor/vesting/streamflow.rs new file mode 100644 index 0000000..fd7e62f --- /dev/null +++ b/kb-lib/src/executor/vesting/streamflow.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/vesting/streamflow.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_vesting_streamflow`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_vesting_streamflow"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/wallet.rs b/kb-lib/src/executor/wallet.rs new file mode 100644 index 0000000..a2e9330 --- /dev/null +++ b/kb-lib/src/executor/wallet.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/executor/wallet.rs +// version: 1 + +//! `wallet` executor family. + +pub mod jupiter_apepro_smart_wallet; diff --git a/kb-lib/src/executor/wallet/jupiter_apepro_smart_wallet.rs b/kb-lib/src/executor/wallet/jupiter_apepro_smart_wallet.rs new file mode 100644 index 0000000..f64a725 --- /dev/null +++ b/kb-lib/src/executor/wallet/jupiter_apepro_smart_wallet.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/wallet/jupiter_apepro_smart_wallet.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_wallet_jupiter_apepro_smart_wallet`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_wallet_jupiter_apepro_smart_wallet"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/executor/weighted.rs b/kb-lib/src/executor/weighted.rs new file mode 100644 index 0000000..e061b49 --- /dev/null +++ b/kb-lib/src/executor/weighted.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/executor/weighted.rs +// version: 1 + +//! `weighted` executor family. + +pub mod swap_stabble; diff --git a/kb-lib/src/executor/weighted/swap_stabble.rs b/kb-lib/src/executor/weighted/swap_stabble.rs new file mode 100644 index 0000000..460e5a1 --- /dev/null +++ b/kb-lib/src/executor/weighted/swap_stabble.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/executor/weighted/swap_stabble.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_executor_weighted_swap_stabble`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_executor_weighted_swap_stabble"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/lib.rs b/kb-lib/src/lib.rs new file mode 100644 index 0000000..7ae8d44 --- /dev/null +++ b/kb-lib/src/lib.rs @@ -0,0 +1,198 @@ +// file: kb-lib/src/lib.rs +// version: 3 + +//! Consolidated decoder, executor, materializer and shared model library. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +pub mod decoder; +pub mod executor; +pub mod materializer; +pub mod model; + +/// Canonical tracing target for the consolidated library. +pub(crate) use crate::decoder::api::constants::TRACING_TARGET; + +/// Canonical tracing target for this crate. +/// Current contextual core instruction input contract version. +pub use crate::decoder::api::contracts::CORE_INSTRUCTION_INPUT_CONTRACT_VERSION; +/// Stable contextual decoded observation. +pub use crate::decoder::api::contracts::DecodedObservation; +/// Machine-readable decoder coverage declaration. +pub use crate::decoder::api::contracts::DecoderCoverageDeclaration; +/// Declared decoder coverage entry kind. +pub use crate::decoder::api::contracts::DecoderCoverageEntryKind; +/// Structured decoder diagnostic. +pub use crate::decoder::api::contracts::DecoderDiagnostic; +/// Complete decoder execution result. +pub use crate::decoder::api::contracts::DecoderExecutionResult; +/// Stable decoder identity. +pub use crate::decoder::api::contracts::DecoderIdentity; +/// Decoder terminal result status. +pub use crate::decoder::api::contracts::DecoderOutcomeStatus; +/// Explicit decoding proof. +pub use crate::decoder::api::contracts::DecoderProof; +/// Decoding proof category. +pub use crate::decoder::api::contracts::DecoderProofKind; +/// Deterministic decoder recognition result. +pub use crate::decoder::api::contracts::DecoderRecognition; +/// One statically declared decoder surface. +pub use crate::decoder::api::contracts::DecoderSurface; +/// Contextual instruction decoder contract. +pub use crate::decoder::api::contracts::InstructionDecoder; +/// Computes the deterministic contextual input hash. +pub use crate::decoder::api::contracts::contextual_input_hash; +/// Returns true when a decoder declares the exact program id. +pub use crate::decoder::api::contracts::decoder_handles_program_id; +/// Computes a deterministic lowercase SHA-256 hash for one JSON value. +pub use crate::decoder::api::contracts::deterministic_json_hash; +/// Extracts the first eight payload bytes as hexadecimal when available. +pub use crate::decoder::api::contracts::discriminator_8_hex; +/// Exposes the decoder support level. +pub use crate::decoder::api::decoder::DecoderSupport; +/// Initial decoder implementation placeholder for API-level tests. +pub use crate::decoder::api::decoder::InitialDecoder; +/// Exposes the common protocol decoder trait. +pub use crate::decoder::api::decoder::ProtocolDecoder; +/// Exposes the blockhash policy kind. +pub use crate::executor::api::execution::ExecutionBlockhashKind; +/// Exposes the blockhash policy. +pub use crate::executor::api::execution::ExecutionBlockhashPolicy; +/// Exposes the exact execution capability result. +pub use crate::executor::api::execution::ExecutionCapability; +/// Exposes supported Solana clusters. +pub use crate::executor::api::execution::ExecutionCluster; +/// Exposes cluster restrictions. +pub use crate::executor::api::execution::ExecutionClusterPolicy; +/// Exposes the result of waiting for transaction confirmation. +pub use crate::executor::api::execution::ExecutionConfirmationResult; +/// Exposes transaction confirmation states. +pub use crate::executor::api::execution::ExecutionConfirmationStatus; +/// Exposes explicit execution cost ceilings. +pub use crate::executor::api::execution::ExecutionCostLimit; +/// Exposes the complete execution policy. +pub use crate::executor::api::execution::ExecutionPolicy; +/// Exposes the result of a transaction send adapter. +pub use crate::executor::api::execution::ExecutionSendResult; +/// Exposes the simulation policy. +pub use crate::executor::api::execution::ExecutionSimulationPolicy; +/// Exposes the simulation result contract. +pub use crate::executor::api::execution::ExecutionSimulationResult; +/// Exposes one planned instruction account. +pub use crate::executor::api::execution::PlannedAccount; +/// Exposes one encoded planned instruction. +pub use crate::executor::api::execution::PlannedInstruction; +/// Exposes post-execution replay diagnostics. +pub use crate::executor::api::execution::PostExecutionDiagnostic; +/// Exposes post-execution validation requirements. +pub use crate::executor::api::execution::PostExecutionValidationPolicy; +/// Exposes a typed execution plan. +pub use crate::executor::api::execution::PreparedExecutionPlan; +/// Exposes one required signer. +pub use crate::executor::api::execution::RequiredSigner; +/// Exposes the typed executor contract. +pub use crate::executor::api::execution::TypedInstructionExecutor; +/// Exposes the legacy common execution plan type. +pub use crate::executor::api::executor::ExecutionPlan; +/// Exposes the legacy common execution request type. +pub use crate::executor::api::executor::ExecutionRequest; +/// Exposes the legacy common execution support enum. +pub use crate::executor::api::executor::ExecutionSupport; +/// Exposes the legacy common instruction executor trait. +pub use crate::executor::api::executor::InstructionExecutor; +/// Exposes the compact JSON payload serializer. +pub use crate::executor::api::executor::serialize_payload_json; +/// Exposes the pretty JSON payload serializer. +pub use crate::executor::api::executor::serialize_payload_json_pretty; +/// Stable decoded observation materializer contract used by the common pipeline. +pub use crate::materializer::api::contracts::EventMaterializer; +/// Explicit policy applied to successful and failed source transactions. +pub use crate::materializer::api::contracts::MaterializationTransactionPolicy; +/// Stable processor-owned business output derived from one decoded observation. +pub use crate::materializer::api::contracts::MaterializedOutput; +/// Structured materializer diagnostic without generic error wrappers. +pub use crate::materializer::api::contracts::MaterializerDiagnostic; +/// Complete explicit result produced by one materializer invocation. +pub use crate::materializer::api::contracts::MaterializerExecutionResult; +/// Stable identity of one decoded event materializer. +pub use crate::materializer::api::contracts::MaterializerIdentity; +/// Terminal materializer result status. +pub use crate::materializer::api::contracts::MaterializerOutcomeStatus; +/// Returns true when a materializer explicitly accepts one decoded family. +pub use crate::materializer::api::contracts::materializer_accepts_family; +/// Returns true when a materializer accepts one exact decoded observation. +pub use crate::materializer::api::contracts::materializer_accepts_observation; +/// Applies the mandatory source transaction policy before materialization. +pub use crate::materializer::api::contracts::validate_materialization_policy; +/// Rejects mutable business outputs derived from failed or uncommitted observations. +pub use crate::materializer::api::contracts::validate_materialized_output_policy; +/// Exposes the legacy common business materializer trait. +pub use crate::materializer::api::materializer::Materializer; +/// Current canonical transaction document version. +pub use crate::model::canonical_transaction::CANONICAL_TRANSACTION_FORMAT_VERSION; +/// Canonical address lookup table reference. +pub use crate::model::canonical_transaction::CanonicalAddressTableLookup; +/// Canonical compiled Solana instruction. +pub use crate::model::canonical_transaction::CanonicalCompiledInstruction; +/// Canonical group of inner instructions. +pub use crate::model::canonical_transaction::CanonicalInnerInstructionGroup; +/// Canonical runtime-loaded addresses. +pub use crate::model::canonical_transaction::CanonicalLoadedAddresses; +/// Canonical Solana message header. +pub use crate::model::canonical_transaction::CanonicalMessageHeader; +/// Canonical transaction return data. +pub use crate::model::canonical_transaction::CanonicalReturnData; +/// Canonical transaction reward. +pub use crate::model::canonical_transaction::CanonicalReward; +/// Canonical exact token balance. +pub use crate::model::canonical_transaction::CanonicalTokenBalance; +/// Source-independent canonical Solana transaction. +pub use crate::model::canonical_transaction::CanonicalTransaction; +/// Canonical Solana transaction message. +pub use crate::model::canonical_transaction::CanonicalTransactionMessage; +/// Canonical Solana transaction execution metadata. +pub use crate::model::canonical_transaction::CanonicalTransactionMetadata; +/// Canonical Solana transaction execution status. +pub use crate::model::canonical_transaction::CanonicalTransactionStatus; +/// Canonical Solana transaction version. +pub use crate::model::canonical_transaction::CanonicalTransactionVersion; +/// Protocol-level decoded event produced by a decoder. +pub use crate::model::decoded::DecodedProtocolEvent; +/// Confidence level for a decoded event. +pub use crate::model::decoded::DecoderConfidence; +/// Source from which a decoded event was derived. +pub use crate::model::decoded::EventSourceKind; +/// Business-level materialized event placeholder. +pub use crate::model::materialized::MaterializedEvent; +/// Materialized event family. +pub use crate::model::materialized::MaterializedEventFamily; +/// Canonical event code in `.` format. +pub use crate::model::nomenclature::EventCode; +/// High-level event family. +pub use crate::model::nomenclature::EventFamily; +/// Event name without surface prefix. +pub use crate::model::nomenclature::EventName; +/// Stable internal program code. +pub use crate::model::nomenclature::ProgramCode; +/// Protocol family code, for example `raydium` or `meteora`. +pub use crate::model::nomenclature::ProtocolCode; +/// Concrete protocol surface code, for example `raydium_amm_v4`. +pub use crate::model::nomenclature::SurfaceCode; +/// Generic program observation derived from a Solana instruction. +pub use crate::model::observation::ProgramObservation; +/// Source-neutral replay contracts. +/// Version of the normalized core replay contract. +pub use crate::model::replay::CORE_REPLAY_INPUT_CONTRACT_VERSION; +/// Decoder replay input containing one instruction plus extracted transaction context. +pub use crate::model::replay::CoreInstructionReplayInput; +/// Stable top-level or inner instruction path. +pub use crate::model::solana::InstructionPath; +/// Solana program id. +pub use crate::model::solana::ProgramId; +/// Solana public key. +pub use crate::model::solana::Pubkey; +/// Solana transaction signature. +pub use crate::model::solana::Signature; +/// Solana slot. +pub use crate::model::solana::Slot; diff --git a/kb-lib/src/materializer.rs b/kb-lib/src/materializer.rs new file mode 100644 index 0000000..e8a56b5 --- /dev/null +++ b/kb-lib/src/materializer.rs @@ -0,0 +1,28 @@ +// file: kb-lib/src/materializer.rs +// version: 1 + +//! Consolidated materializer modules. + +pub mod admin; +pub mod api; +pub mod bridge; +pub mod compliance; +pub mod fees; +pub mod governance; +pub mod lending; +pub mod lifecycle; +pub mod liquidity; +pub mod metadata; +pub mod nft; +pub mod oracle; +pub mod orderbook; +pub mod perpetuals; +pub mod pool; +pub mod rewards; +pub mod risk; +pub mod routing; +pub mod staking; +pub mod token; +pub mod trades; +pub mod transaction; +pub mod vault; diff --git a/kb-lib/src/materializer/admin.rs b/kb-lib/src/materializer/admin.rs new file mode 100644 index 0000000..a43e1d0 --- /dev/null +++ b/kb-lib/src/materializer/admin.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/admin.rs +// version: 1 + +//! `admin` materializer family. + +pub mod core; diff --git a/kb-lib/src/materializer/admin/core.rs b/kb-lib/src/materializer/admin/core.rs new file mode 100644 index 0000000..2eca785 --- /dev/null +++ b/kb-lib/src/materializer/admin/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/admin/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_admin`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_admin"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/api.rs b/kb-lib/src/materializer/api.rs new file mode 100644 index 0000000..8745290 --- /dev/null +++ b/kb-lib/src/materializer/api.rs @@ -0,0 +1,31 @@ +// file: kb-lib/src/materializer/api.rs +// version: 2 + +//! Materializer contracts consolidated from `kb_materializer_api`. + +pub mod contracts; +pub mod materializer; +/// Stable decoded observation materializer contract used by the common pipeline. +pub use crate::materializer::api::contracts::EventMaterializer; +/// Explicit policy applied to successful and failed source transactions. +pub use crate::materializer::api::contracts::MaterializationTransactionPolicy; +/// Stable processor-owned business output derived from one decoded observation. +pub use crate::materializer::api::contracts::MaterializedOutput; +/// Structured materializer diagnostic without generic error wrappers. +pub use crate::materializer::api::contracts::MaterializerDiagnostic; +/// Complete explicit result produced by one materializer invocation. +pub use crate::materializer::api::contracts::MaterializerExecutionResult; +/// Stable identity of one decoded event materializer. +pub use crate::materializer::api::contracts::MaterializerIdentity; +/// Terminal materializer result status. +pub use crate::materializer::api::contracts::MaterializerOutcomeStatus; +/// Returns true when a materializer explicitly accepts one decoded family. +pub use crate::materializer::api::contracts::materializer_accepts_family; +/// Returns true when a materializer accepts one exact decoded observation. +pub use crate::materializer::api::contracts::materializer_accepts_observation; +/// Applies the mandatory source transaction policy before materialization. +pub use crate::materializer::api::contracts::validate_materialization_policy; +/// Rejects mutable business outputs derived from failed or uncommitted observations. +pub use crate::materializer::api::contracts::validate_materialized_output_policy; +/// Exposes the legacy common business materializer trait. +pub use crate::materializer::api::materializer::Materializer; diff --git a/kb-lib/src/materializer/api/contracts.rs b/kb-lib/src/materializer/api/contracts.rs new file mode 100644 index 0000000..b449f61 --- /dev/null +++ b/kb-lib/src/materializer/api/contracts.rs @@ -0,0 +1,412 @@ +// file: kb-lib/src/materializer/api/contracts.rs +// version: 7 + +//! Backend-neutral decoded observation materialization contracts. + +/// Stable identity of one decoded event materializer. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct MaterializerIdentity { + /// Stable lower snake case processor name. + pub name: std::string::String, + /// Semantic or deterministic implementation version. + pub version: std::string::String, +} + +impl MaterializerIdentity { + /// Builds a validated materializer identity. + pub fn new( + name: impl std::convert::Into, + version: impl std::convert::Into, + ) -> kb_core::Result { + let value = Self { + name: name.into(), + version: version.into(), + }; + if value.name.trim().is_empty() || value.version.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "materializer identity fields must not be empty", + )); + } + return std::result::Result::Ok(value); + } +} + +/// Explicit policy applied to successful and failed source transactions. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum MaterializationTransactionPolicy { + /// Accept committed observations from successful transactions only. + SuccessfulCommittedOnly, + /// Accept successful observations and failed audit-only observations. + SuccessfulOrFailedAudit, + /// Accept failed observations for a specifically declared non-mutating family. + ExplicitFailedObservation, +} + +/// Terminal materializer result status. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum MaterializerOutcomeStatus { + /// One or more new stable outputs were inserted. + Inserted, + /// One or more processor-owned outputs were deterministically replaced. + Replaced, + /// The decoded observation was intentionally ignored. + Ignored, + /// The decoded observation was rejected by the declared policy. + Refused, + /// Materialization failed and diagnostics were produced. + Failed, +} + +/// Structured materializer diagnostic without generic error wrappers. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct MaterializerDiagnostic { + /// Stable machine-readable diagnostic code. + pub code: std::string::String, + /// Human-readable diagnostic message. + pub message: std::string::String, + /// Whether retrying the same version and input may succeed. + pub retriable: bool, +} + +impl MaterializerDiagnostic { + /// Builds a validated materializer diagnostic. + pub fn new( + code: impl std::convert::Into, + message: impl std::convert::Into, + retriable: bool, + ) -> kb_core::Result { + let value = Self { + code: code.into(), + message: message.into(), + retriable, + }; + if value.code.trim().is_empty() || value.message.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "materializer diagnostic code and message must not be empty", + )); + } + return std::result::Result::Ok(value); + } +} + +/// Stable processor-owned business output derived from one decoded observation. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct MaterializedOutput { + /// Stable processor-owned output key within one decoded observation. + pub output_key: std::string::String, + /// Business output family. + pub family: crate::MaterializedEventFamily, + /// Typed business payload serialized as deterministic JSON. + pub payload_json: serde_json::Value, +} + +impl MaterializedOutput { + /// Validates stable output identity. + pub fn validate(&self) -> kb_core::Result<()> { + if self.output_key.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "materialized output key must not be empty", + )); + } + return std::result::Result::Ok(()); + } +} + +/// Complete explicit result produced by one materializer invocation. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct MaterializerExecutionResult { + /// Terminal materializer status. + pub status: MaterializerOutcomeStatus, + /// Stable business outputs. + pub outputs: std::vec::Vec, + /// Structured diagnostics. + pub diagnostics: std::vec::Vec, +} + +impl MaterializerExecutionResult { + /// Builds a policy refusal result. + pub fn refused(code: &str, message: &str) -> Self { + return Self { + status: MaterializerOutcomeStatus::Refused, + outputs: std::vec::Vec::new(), + diagnostics: std::vec![MaterializerDiagnostic { + code: code.to_string(), + message: message.to_string(), + retriable: false, + }], + }; + } + + /// Builds an ignored result. + pub fn ignored() -> Self { + return Self { + status: MaterializerOutcomeStatus::Ignored, + outputs: std::vec::Vec::new(), + diagnostics: std::vec::Vec::new(), + }; + } + + /// Validates status and output consistency. + pub fn validate(&self) -> kb_core::Result<()> { + let output_status = self.status == MaterializerOutcomeStatus::Inserted + || self.status == MaterializerOutcomeStatus::Replaced; + if output_status && self.outputs.is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "inserted or replaced materializer status requires outputs", + )); + } + if !output_status && !self.outputs.is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "ignored, refused or failed materializer status must not contain outputs", + )); + } + if self.status == MaterializerOutcomeStatus::Failed && self.diagnostics.is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "failed materializer status requires at least one diagnostic", + )); + } + for output in &self.outputs { + let validation_result = output.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + } + return std::result::Result::Ok(()); + } +} + +/// Stable decoded observation materializer contract used by the common pipeline. +pub trait EventMaterializer: std::marker::Send + std::marker::Sync { + /// Returns the stable materializer identity. + fn identity(&self) -> MaterializerIdentity; + /// Returns every decoded event family explicitly accepted by this materializer. + fn accepted_families(&self) -> &'static [crate::EventFamily]; + /// Returns whether this materializer accepts one exact decoded observation. + fn accepts_observation(&self, observation: &crate::DecodedObservation) -> bool { + let family = observation.event.event_family; + return self.accepted_families().iter().any(|accepted| return *accepted == family); + } + /// Returns the failed/successful transaction policy for one accepted family. + fn transaction_policy(&self, family: crate::EventFamily) -> MaterializationTransactionPolicy; + /// Materializes one validated decoded observation. + fn materialize(&self, observation: &crate::DecodedObservation) -> MaterializerExecutionResult; +} + +/// Returns true when a materializer explicitly accepts one decoded family. +pub fn materializer_accepts_family( + materializer: &dyn EventMaterializer, + family: crate::EventFamily, +) -> bool { + return materializer + .accepted_families() + .iter() + .any(|accepted| return *accepted == family); +} + +/// Returns true when a materializer accepts one exact decoded observation. +pub fn materializer_accepts_observation( + materializer: &dyn EventMaterializer, + observation: &crate::DecodedObservation, +) -> bool { + return materializer.accepts_observation(observation); +} + +/// Applies the mandatory source transaction policy before materialization. +pub fn validate_materialization_policy( + materializer: &dyn EventMaterializer, + observation: &crate::DecodedObservation, +) -> std::result::Result<(), MaterializerExecutionResult> { + let family = observation.event.event_family; + if !crate::materializer_accepts_observation(materializer, observation) { + return std::result::Result::Err(MaterializerExecutionResult::refused( + "unsupported_decoded_observation", + "materializer does not accept the decoded observation surface and entry", + )); + } + let policy = materializer.transaction_policy(family); + if observation.transaction_failed || !observation.observation_committed { + if family == crate::EventFamily::Trade + || family == crate::EventFamily::Liquidity + || family == crate::EventFamily::Lifecycle + { + return std::result::Result::Err(MaterializerExecutionResult::refused( + "failed_transaction_mutation_refused", + "failed or uncommitted observations cannot create successful mutable business outputs", + )); + } + if policy == MaterializationTransactionPolicy::SuccessfulCommittedOnly { + return std::result::Result::Err(MaterializerExecutionResult::refused( + "failed_transaction_policy_refused", + "materializer accepts committed observations from successful transactions only", + )); + } + if policy == MaterializationTransactionPolicy::SuccessfulOrFailedAudit + && family != crate::EventFamily::Audit + && family != crate::EventFamily::ComplianceAudit + && family != crate::EventFamily::Risk + { + return std::result::Result::Err(MaterializerExecutionResult::refused( + "failed_transaction_non_audit_refused", + "failed transaction materialization is limited to declared audit or risk families", + )); + } + } + return std::result::Result::Ok(()); +} + +/// Rejects successful mutable business outputs derived from failed or uncommitted observations. +pub fn validate_materialized_output_policy( + observation: &crate::DecodedObservation, + result: &MaterializerExecutionResult, +) -> std::result::Result<(), MaterializerExecutionResult> { + if !observation.transaction_failed && observation.observation_committed { + return std::result::Result::Ok(()); + } + let forbidden_output = result.outputs.iter().any(|output| { + return output.family != crate::MaterializedEventFamily::ComplianceAudit + && output.family != crate::MaterializedEventFamily::TokenMetadataRisk + && output.family != crate::MaterializedEventFamily::Risk; + }); + if forbidden_output { + return std::result::Result::Err(MaterializerExecutionResult::refused( + "failed_transaction_output_refused", + "failed or uncommitted observations may only produce audit or risk outputs", + )); + } + return std::result::Result::Ok(()); +} + +#[cfg(test)] +mod tests { + struct TradeMaterializer; + + impl crate::EventMaterializer for TradeMaterializer { + fn identity(&self) -> crate::MaterializerIdentity { + return crate::MaterializerIdentity { + name: "trade_materializer".to_string(), + version: "1".to_string(), + }; + } + + fn accepted_families(&self) -> &'static [crate::EventFamily] { + return &[crate::EventFamily::Trade]; + } + + fn transaction_policy( + &self, + _family: crate::EventFamily, + ) -> crate::MaterializationTransactionPolicy { + return crate::MaterializationTransactionPolicy::SuccessfulCommittedOnly; + } + + fn materialize( + &self, + _observation: &crate::DecodedObservation, + ) -> crate::MaterializerExecutionResult { + return crate::MaterializerExecutionResult::ignored(); + } + } + + fn failed_trade_observation() -> crate::DecodedObservation { + return crate::DecodedObservation { + event_key: "trade".to_string(), + event: crate::DecodedProtocolEvent { + signature: crate::Signature("signature".to_string()), + slot: crate::Slot(42), + instruction_path: crate::InstructionPath("0".to_string()), + program_id: crate::ProgramId("program".to_string()), + protocol_code: crate::ProtocolCode("protocol".to_string()), + surface_code: crate::SurfaceCode("surface".to_string()), + event_code: crate::EventCode("trade".to_string()), + event_name: crate::EventName("trade".to_string()), + event_family: crate::EventFamily::Trade, + source_kind: crate::EventSourceKind::Instruction, + confidence: crate::DecoderConfidence::Exact, + }, + payload_json: serde_json::json!({}), + transaction_failed: true, + transaction_error: std::option::Option::Some( + serde_json::json!({"InstructionError": [0, "Custom"]}), + ), + observation_committed: false, + proof: crate::DecoderProof { + kind: crate::DecoderProofKind::ExactLayout, + confidence: crate::DecoderConfidence::Exact, + evidence: std::vec!["layout".to_string()], + }, + }; + } + + #[test] + fn failed_trade_is_refused_before_materialization() { + let result = + crate::validate_materialization_policy(&TradeMaterializer, &failed_trade_observation()); + assert!(result.is_err()); + let refusal = match result { + std::result::Result::Ok(()) => panic!("failed trade unexpectedly accepted"), + std::result::Result::Err(value) => value, + }; + assert_eq!(refusal.status, crate::MaterializerOutcomeStatus::Refused); + assert_eq!(refusal.diagnostics[0].code, "failed_transaction_mutation_refused"); + } + + struct FailedAuditToTradeMaterializer; + + impl crate::EventMaterializer for FailedAuditToTradeMaterializer { + fn identity(&self) -> crate::MaterializerIdentity { + return crate::MaterializerIdentity { + name: "failed_audit_to_trade".to_string(), + version: "1".to_string(), + }; + } + + fn accepted_families(&self) -> &'static [crate::EventFamily] { + return &[crate::EventFamily::Audit]; + } + + fn transaction_policy( + &self, + _family: crate::EventFamily, + ) -> crate::MaterializationTransactionPolicy { + return crate::MaterializationTransactionPolicy::SuccessfulOrFailedAudit; + } + + fn materialize( + &self, + _observation: &crate::DecodedObservation, + ) -> crate::MaterializerExecutionResult { + return crate::MaterializerExecutionResult { + status: crate::MaterializerOutcomeStatus::Inserted, + outputs: std::vec![crate::MaterializedOutput { + output_key: "trade".to_string(), + family: crate::MaterializedEventFamily::Trade, + payload_json: serde_json::json!({}), + }], + diagnostics: std::vec::Vec::new(), + }; + } + } + + fn failed_audit_observation() -> crate::DecodedObservation { + let mut observation = failed_trade_observation(); + observation.event.event_family = crate::EventFamily::Audit; + return observation; + } + + #[test] + fn failed_audit_cannot_emit_successful_trade_output() { + let materializer = FailedAuditToTradeMaterializer; + let observation = failed_audit_observation(); + let policy_result = crate::validate_materialization_policy(&materializer, &observation); + assert!(policy_result.is_ok()); + let materialized = crate::EventMaterializer::materialize(&materializer, &observation); + let output_result = crate::validate_materialized_output_policy(&observation, &materialized); + assert!(output_result.is_err()); + let refusal = match output_result { + std::result::Result::Ok(()) => panic!("failed audit unexpectedly emitted a trade"), + std::result::Result::Err(value) => value, + }; + assert_eq!(refusal.status, crate::MaterializerOutcomeStatus::Refused); + assert_eq!(refusal.diagnostics[0].code, "failed_transaction_output_refused"); + } +} diff --git a/kb-lib/src/materializer/api/materializer.rs b/kb-lib/src/materializer/api/materializer.rs new file mode 100644 index 0000000..71d6685 --- /dev/null +++ b/kb-lib/src/materializer/api/materializer.rs @@ -0,0 +1,22 @@ +// file: kb-lib/src/materializer/api/materializer.rs +// version: 2 + +//! Materializer API contract used by business materializer crates. + +/// Common contract implemented by all business materializer crates. +pub trait Materializer { + /// Returns the stable materializer name. + fn materializer_name(&self) -> &'static str; + + /// Returns the materializer version. + fn materializer_version(&self) -> &'static str; + + /// Tests whether this materializer accepts a decoded event. + fn accepts_event(&self, event: &crate::DecodedProtocolEvent) -> bool; + + /// Materializes a decoded event into zero or more business events. + fn materialize_event( + &self, + event: &crate::DecodedProtocolEvent, + ) -> kb_core::Result>; +} diff --git a/kb-lib/src/materializer/bridge.rs b/kb-lib/src/materializer/bridge.rs new file mode 100644 index 0000000..c9b771e --- /dev/null +++ b/kb-lib/src/materializer/bridge.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/bridge.rs +// version: 1 + +//! `bridge` materializer family. + +pub mod core; diff --git a/kb-lib/src/materializer/bridge/core.rs b/kb-lib/src/materializer/bridge/core.rs new file mode 100644 index 0000000..9516200 --- /dev/null +++ b/kb-lib/src/materializer/bridge/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/bridge/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_bridge`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_bridge"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/compliance.rs b/kb-lib/src/materializer/compliance.rs new file mode 100644 index 0000000..2891fd4 --- /dev/null +++ b/kb-lib/src/materializer/compliance.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/compliance.rs +// version: 1 + +//! `compliance` materializer family. + +pub mod audit; diff --git a/kb-lib/src/materializer/compliance/audit.rs b/kb-lib/src/materializer/compliance/audit.rs new file mode 100644 index 0000000..5cff13f --- /dev/null +++ b/kb-lib/src/materializer/compliance/audit.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/compliance/audit.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_compliance_audit`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_compliance_audit"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/fees.rs b/kb-lib/src/materializer/fees.rs new file mode 100644 index 0000000..0fd4295 --- /dev/null +++ b/kb-lib/src/materializer/fees.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/fees.rs +// version: 1 + +//! `fees` materializer family. + +pub mod core; diff --git a/kb-lib/src/materializer/fees/core.rs b/kb-lib/src/materializer/fees/core.rs new file mode 100644 index 0000000..e5cb3db --- /dev/null +++ b/kb-lib/src/materializer/fees/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/fees/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_fees`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_fees"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/governance.rs b/kb-lib/src/materializer/governance.rs new file mode 100644 index 0000000..fafad37 --- /dev/null +++ b/kb-lib/src/materializer/governance.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/governance.rs +// version: 1 + +//! `governance` materializer family. + +pub mod core; diff --git a/kb-lib/src/materializer/governance/core.rs b/kb-lib/src/materializer/governance/core.rs new file mode 100644 index 0000000..35736bd --- /dev/null +++ b/kb-lib/src/materializer/governance/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/governance/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_governance`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_governance"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/lending.rs b/kb-lib/src/materializer/lending.rs new file mode 100644 index 0000000..efee829 --- /dev/null +++ b/kb-lib/src/materializer/lending.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/lending.rs +// version: 1 + +//! `lending` materializer family. + +pub mod core; diff --git a/kb-lib/src/materializer/lending/core.rs b/kb-lib/src/materializer/lending/core.rs new file mode 100644 index 0000000..b481d6e --- /dev/null +++ b/kb-lib/src/materializer/lending/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/lending/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_lending`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_lending"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/lifecycle.rs b/kb-lib/src/materializer/lifecycle.rs new file mode 100644 index 0000000..842fbe7 --- /dev/null +++ b/kb-lib/src/materializer/lifecycle.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/lifecycle.rs +// version: 1 + +//! `lifecycle` materializer family. + +pub mod core; diff --git a/kb-lib/src/materializer/lifecycle/core.rs b/kb-lib/src/materializer/lifecycle/core.rs new file mode 100644 index 0000000..f2b8036 --- /dev/null +++ b/kb-lib/src/materializer/lifecycle/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/lifecycle/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_lifecycle`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_lifecycle"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/liquidity.rs b/kb-lib/src/materializer/liquidity.rs new file mode 100644 index 0000000..69479b5 --- /dev/null +++ b/kb-lib/src/materializer/liquidity.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/liquidity.rs +// version: 1 + +//! `liquidity` materializer family. + +pub mod core; diff --git a/kb-lib/src/materializer/liquidity/core.rs b/kb-lib/src/materializer/liquidity/core.rs new file mode 100644 index 0000000..03ec80a --- /dev/null +++ b/kb-lib/src/materializer/liquidity/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/liquidity/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_liquidity`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_liquidity"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/metadata.rs b/kb-lib/src/materializer/metadata.rs new file mode 100644 index 0000000..58d7ba9 --- /dev/null +++ b/kb-lib/src/materializer/metadata.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/metadata.rs +// version: 1 + +//! `metadata` materializer family. + +pub mod core; diff --git a/kb-lib/src/materializer/metadata/core.rs b/kb-lib/src/materializer/metadata/core.rs new file mode 100644 index 0000000..ea64b17 --- /dev/null +++ b/kb-lib/src/materializer/metadata/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/metadata/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_metadata`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_metadata"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/nft.rs b/kb-lib/src/materializer/nft.rs new file mode 100644 index 0000000..3220b11 --- /dev/null +++ b/kb-lib/src/materializer/nft.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/nft.rs +// version: 1 + +//! `nft` materializer family. + +pub mod core; diff --git a/kb-lib/src/materializer/nft/core.rs b/kb-lib/src/materializer/nft/core.rs new file mode 100644 index 0000000..1a7c28e --- /dev/null +++ b/kb-lib/src/materializer/nft/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/nft/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_nft`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_nft"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/oracle.rs b/kb-lib/src/materializer/oracle.rs new file mode 100644 index 0000000..5bc5504 --- /dev/null +++ b/kb-lib/src/materializer/oracle.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/oracle.rs +// version: 1 + +//! `oracle` materializer family. + +pub mod core; diff --git a/kb-lib/src/materializer/oracle/core.rs b/kb-lib/src/materializer/oracle/core.rs new file mode 100644 index 0000000..c5c37e2 --- /dev/null +++ b/kb-lib/src/materializer/oracle/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/oracle/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_oracle`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_oracle"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/orderbook.rs b/kb-lib/src/materializer/orderbook.rs new file mode 100644 index 0000000..8c52bad --- /dev/null +++ b/kb-lib/src/materializer/orderbook.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/orderbook.rs +// version: 1 + +//! `orderbook` materializer family. + +pub mod core; diff --git a/kb-lib/src/materializer/orderbook/core.rs b/kb-lib/src/materializer/orderbook/core.rs new file mode 100644 index 0000000..c9690b1 --- /dev/null +++ b/kb-lib/src/materializer/orderbook/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/orderbook/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_orderbook`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_orderbook"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/perpetuals.rs b/kb-lib/src/materializer/perpetuals.rs new file mode 100644 index 0000000..79ab60f --- /dev/null +++ b/kb-lib/src/materializer/perpetuals.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/perpetuals.rs +// version: 1 + +//! `perpetuals` materializer family. + +pub mod core; diff --git a/kb-lib/src/materializer/perpetuals/core.rs b/kb-lib/src/materializer/perpetuals/core.rs new file mode 100644 index 0000000..632c795 --- /dev/null +++ b/kb-lib/src/materializer/perpetuals/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/perpetuals/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_perpetuals`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_perpetuals"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/pool.rs b/kb-lib/src/materializer/pool.rs new file mode 100644 index 0000000..1802525 --- /dev/null +++ b/kb-lib/src/materializer/pool.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/pool.rs +// version: 1 + +//! `pool` materializer family. + +pub mod state; diff --git a/kb-lib/src/materializer/pool/state.rs b/kb-lib/src/materializer/pool/state.rs new file mode 100644 index 0000000..98c2472 --- /dev/null +++ b/kb-lib/src/materializer/pool/state.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/pool/state.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_pool_state`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_pool_state"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/rewards.rs b/kb-lib/src/materializer/rewards.rs new file mode 100644 index 0000000..757b67c --- /dev/null +++ b/kb-lib/src/materializer/rewards.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/rewards.rs +// version: 1 + +//! `rewards` materializer family. + +pub mod core; diff --git a/kb-lib/src/materializer/rewards/core.rs b/kb-lib/src/materializer/rewards/core.rs new file mode 100644 index 0000000..c2a32b5 --- /dev/null +++ b/kb-lib/src/materializer/rewards/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/rewards/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_rewards`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_rewards"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/risk.rs b/kb-lib/src/materializer/risk.rs new file mode 100644 index 0000000..5c5802c --- /dev/null +++ b/kb-lib/src/materializer/risk.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/risk.rs +// version: 1 + +//! `risk` materializer family. + +pub mod core; diff --git a/kb-lib/src/materializer/risk/core.rs b/kb-lib/src/materializer/risk/core.rs new file mode 100644 index 0000000..1e75035 --- /dev/null +++ b/kb-lib/src/materializer/risk/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/risk/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_risk`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_risk"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/routing.rs b/kb-lib/src/materializer/routing.rs new file mode 100644 index 0000000..7c72e13 --- /dev/null +++ b/kb-lib/src/materializer/routing.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/routing.rs +// version: 1 + +//! `routing` materializer family. + +pub mod core; diff --git a/kb-lib/src/materializer/routing/core.rs b/kb-lib/src/materializer/routing/core.rs new file mode 100644 index 0000000..832960f --- /dev/null +++ b/kb-lib/src/materializer/routing/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/routing/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_routing`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_routing"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/staking.rs b/kb-lib/src/materializer/staking.rs new file mode 100644 index 0000000..67e9381 --- /dev/null +++ b/kb-lib/src/materializer/staking.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/staking.rs +// version: 1 + +//! `staking` materializer family. + +pub mod core; diff --git a/kb-lib/src/materializer/staking/core.rs b/kb-lib/src/materializer/staking/core.rs new file mode 100644 index 0000000..880d855 --- /dev/null +++ b/kb-lib/src/materializer/staking/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/staking/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_staking`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_staking"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/token.rs b/kb-lib/src/materializer/token.rs new file mode 100644 index 0000000..f141bbb --- /dev/null +++ b/kb-lib/src/materializer/token.rs @@ -0,0 +1,7 @@ +// file: kb-lib/src/materializer/token.rs +// version: 1 + +//! `token` materializer family. + +pub mod accounts; +pub mod metadata_risk; diff --git a/kb-lib/src/materializer/token/accounts.rs b/kb-lib/src/materializer/token/accounts.rs new file mode 100644 index 0000000..9ce6749 --- /dev/null +++ b/kb-lib/src/materializer/token/accounts.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/token/accounts.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_token_accounts`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_token_accounts"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/token/metadata_risk.rs b/kb-lib/src/materializer/token/metadata_risk.rs new file mode 100644 index 0000000..39d4ccf --- /dev/null +++ b/kb-lib/src/materializer/token/metadata_risk.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/token/metadata_risk.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_token_metadata_risk`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_token_metadata_risk"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/trades.rs b/kb-lib/src/materializer/trades.rs new file mode 100644 index 0000000..6998d41 --- /dev/null +++ b/kb-lib/src/materializer/trades.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/trades.rs +// version: 1 + +//! `trades` materializer family. + +pub mod core; diff --git a/kb-lib/src/materializer/trades/core.rs b/kb-lib/src/materializer/trades/core.rs new file mode 100644 index 0000000..bfe58ad --- /dev/null +++ b/kb-lib/src/materializer/trades/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/trades/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_trades`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_trades"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/transaction.rs b/kb-lib/src/materializer/transaction.rs new file mode 100644 index 0000000..53e79e6 --- /dev/null +++ b/kb-lib/src/materializer/transaction.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/transaction.rs +// version: 1 + +//! `transaction` materializer family. + +pub mod annotations; diff --git a/kb-lib/src/materializer/transaction/annotations.rs b/kb-lib/src/materializer/transaction/annotations.rs new file mode 100644 index 0000000..5e66581 --- /dev/null +++ b/kb-lib/src/materializer/transaction/annotations.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/transaction/annotations.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_transaction_annotations`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_transaction_annotations"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/materializer/vault.rs b/kb-lib/src/materializer/vault.rs new file mode 100644 index 0000000..d95e9f1 --- /dev/null +++ b/kb-lib/src/materializer/vault.rs @@ -0,0 +1,6 @@ +// file: kb-lib/src/materializer/vault.rs +// version: 1 + +//! `vault` materializer family. + +pub mod core; diff --git a/kb-lib/src/materializer/vault/core.rs b/kb-lib/src/materializer/vault/core.rs new file mode 100644 index 0000000..d265c09 --- /dev/null +++ b/kb-lib/src/materializer/vault/core.rs @@ -0,0 +1,10 @@ +// file: kb-lib/src/materializer/vault/core.rs +// version: 1 + +//! Migration boundary for legacy crate `kb_materializer_vault`. + +/// Legacy crate name retained for migration and compatibility tracking. +pub const LEGACY_CRATE: &str = "kb_materializer_vault"; + +/// Current porting status. +pub const MIGRATION_STATUS: &str = "source-preserved-pending-port"; diff --git a/kb-lib/src/model.rs b/kb-lib/src/model.rs new file mode 100644 index 0000000..2e100c1 --- /dev/null +++ b/kb-lib/src/model.rs @@ -0,0 +1,81 @@ +// file: kb-lib/src/model.rs +// version: 1 + +//! Shared canonical models consolidated from `kb_model`. + +pub mod canonical_transaction; +pub mod decoded; +pub mod materialized; +pub mod nomenclature; +pub mod observation; +pub mod solana; + +/// Current canonical transaction document version. +pub use crate::model::canonical_transaction::CANONICAL_TRANSACTION_FORMAT_VERSION; +/// Canonical address lookup table reference. +pub use crate::model::canonical_transaction::CanonicalAddressTableLookup; +/// Canonical compiled Solana instruction. +pub use crate::model::canonical_transaction::CanonicalCompiledInstruction; +/// Canonical group of inner instructions. +pub use crate::model::canonical_transaction::CanonicalInnerInstructionGroup; +/// Canonical runtime-loaded addresses. +pub use crate::model::canonical_transaction::CanonicalLoadedAddresses; +/// Canonical Solana message header. +pub use crate::model::canonical_transaction::CanonicalMessageHeader; +/// Canonical transaction return data. +pub use crate::model::canonical_transaction::CanonicalReturnData; +/// Canonical transaction reward. +pub use crate::model::canonical_transaction::CanonicalReward; +/// Canonical exact token balance. +pub use crate::model::canonical_transaction::CanonicalTokenBalance; +/// Source-independent canonical Solana transaction. +pub use crate::model::canonical_transaction::CanonicalTransaction; +/// Canonical Solana transaction message. +pub use crate::model::canonical_transaction::CanonicalTransactionMessage; +/// Canonical Solana transaction execution metadata. +pub use crate::model::canonical_transaction::CanonicalTransactionMetadata; +/// Canonical Solana transaction execution status. +pub use crate::model::canonical_transaction::CanonicalTransactionStatus; +/// Canonical Solana transaction version. +pub use crate::model::canonical_transaction::CanonicalTransactionVersion; +/// Protocol-level decoded event produced by a decoder. +pub use crate::model::decoded::DecodedProtocolEvent; +/// Confidence level for a decoded event. +pub use crate::model::decoded::DecoderConfidence; +/// Source from which a decoded event was derived. +pub use crate::model::decoded::EventSourceKind; +/// Business-level materialized event placeholder. +pub use crate::model::materialized::MaterializedEvent; +/// Materialized event family. +pub use crate::model::materialized::MaterializedEventFamily; +/// Canonical event code in `.` format. +pub use crate::model::nomenclature::EventCode; +/// High-level event family. +pub use crate::model::nomenclature::EventFamily; +/// Event name without surface prefix. +pub use crate::model::nomenclature::EventName; +/// Stable internal program code. +pub use crate::model::nomenclature::ProgramCode; +/// Protocol family code, for example `raydium` or `meteora`. +pub use crate::model::nomenclature::ProtocolCode; +/// Concrete protocol surface code, for example `raydium_amm_v4`. +pub use crate::model::nomenclature::SurfaceCode; +/// Generic program observation derived from a Solana instruction. +pub use crate::model::observation::ProgramObservation; +/// Stable top-level or inner instruction path. +pub use crate::model::solana::InstructionPath; +/// Solana program id. +pub use crate::model::solana::ProgramId; +/// Solana public key. +pub use crate::model::solana::Pubkey; +/// Solana transaction signature. +pub use crate::model::solana::Signature; +/// Solana slot. +pub use crate::model::solana::Slot; + +/// Source-neutral replay contracts. +pub mod replay; +/// Version of the normalized core replay contract. +pub use crate::model::replay::CORE_REPLAY_INPUT_CONTRACT_VERSION; +/// Decoder replay input containing one instruction plus extracted transaction context. +pub use crate::model::replay::CoreInstructionReplayInput; diff --git a/kb-lib/src/model/canonical_transaction.rs b/kb-lib/src/model/canonical_transaction.rs new file mode 100644 index 0000000..9fbe464 --- /dev/null +++ b/kb-lib/src/model/canonical_transaction.rs @@ -0,0 +1,819 @@ +// file: kb-lib/src/model/canonical_transaction.rs +// version: 7 + +//! Source-independent canonical Solana transaction contract. + +use base64::Engine; // rust-rules: trait-import +use sha2::Digest; // rust-rules: trait-import + +/// Current canonical transaction document version. +pub const CANONICAL_TRANSACTION_FORMAT_VERSION: u32 = 1; + +/// Source-independent canonical Solana transaction. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalTransaction { + /// Canonical contract version included in the hashed document. + pub format_version: u32, + /// Primary transaction signature as base58 text. + pub primary_signature: std::string::String, + /// Slot in which the transaction was processed. + pub slot: u64, + /// Optional block timestamp as Unix seconds. + pub block_time: std::option::Option, + /// Transaction message version. + pub version: CanonicalTransactionVersion, + /// All transaction signatures in message order. + pub signatures: std::vec::Vec, + /// Canonical transaction message. + pub message: CanonicalTransactionMessage, + /// Optional execution metadata. + pub metadata: std::option::Option, +} + +impl CanonicalTransaction { + /// Validates the canonical transaction invariants. + pub fn validate(&self) -> kb_core::Result<()> { + if self.format_version != crate::CANONICAL_TRANSACTION_FORMAT_VERSION { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "unsupported canonical transaction format version: {}", + self.format_version + ))); + } + let primary_result = validate_signature_text( + self.primary_signature.as_str(), + "primary transaction signature", + ); + if let std::result::Result::Err(error) = primary_result { + return std::result::Result::Err(error); + } + if self.signatures.is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical transaction must contain at least one signature", + )); + } + if self.signatures[0] != self.primary_signature { + return std::result::Result::Err(kb_core::Error::invalid_state( + "primary transaction signature must equal the first signature", + )); + } + if self.signatures.len() != usize::from(self.message.header.num_required_signatures) { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical signature count must match the message header", + )); + } + for signature in &self.signatures { + let signature_result = + validate_signature_text(signature.as_str(), "transaction signature"); + if let std::result::Result::Err(error) = signature_result { + return std::result::Result::Err(error); + } + } + let message_result = self.message.validate(); + if let std::result::Result::Err(error) = message_result { + return std::result::Result::Err(error); + } + if self.version == CanonicalTransactionVersion::Legacy + && (!self.message.address_table_lookups.is_empty() + || !self.message.loaded_addresses.writable.is_empty() + || !self.message.loaded_addresses.readonly.is_empty()) + { + return std::result::Result::Err(kb_core::Error::invalid_state( + "legacy canonical transaction must not contain lookup table data", + )); + } + if let std::option::Option::Some(metadata) = &self.metadata { + let metadata_result = metadata + .validate(self.message.resolved_account_count(), self.message.instructions.len()); + if let std::result::Result::Err(error) = metadata_result { + return std::result::Result::Err(error); + } + } + return std::result::Result::Ok(()); + } + + /// Serializes the transaction into the canonical JSON value. + pub fn to_canonical_json(&self) -> kb_core::Result { + let validation_result = self.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let serialization_result = serde_json::to_value(self); + let mut value = match serialization_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot serialize canonical transaction: {error}" + ))); + }, + }; + sort_json_value(&mut value); + return std::result::Result::Ok(value); + } + + /// Serializes the transaction into deterministic compact canonical JSON bytes. + pub fn to_canonical_json_bytes(&self) -> kb_core::Result> { + let json_result = self.to_canonical_json(); + let json = match json_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let serialization_result = serde_json::to_vec(&json); + return match serialization_result { + std::result::Result::Ok(bytes) => std::result::Result::Ok(bytes), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::json( + format!("cannot serialize canonical transaction bytes: {error}"), + )), + }; + } + + /// Calculates the lowercase SHA-256 digest of the canonical JSON bytes. + pub fn canonical_json_hash(&self) -> kb_core::Result { + let bytes_result = self.to_canonical_json_bytes(); + let bytes = match bytes_result { + std::result::Result::Ok(bytes) => bytes, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let digest = sha2::Sha256::digest(bytes); + let mut text = std::string::String::with_capacity(64); + for byte in digest { + text.push_str(format!("{byte:02x}").as_str()); + } + return std::result::Result::Ok(text); + } +} + +/// Canonical Solana transaction version. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(tag = "kind", content = "number", rename_all = "snake_case")] +pub enum CanonicalTransactionVersion { + /// Legacy transaction message. + Legacy, + /// Numbered versioned transaction message. + Number(u8), +} + +/// Canonical Solana message header. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalMessageHeader { + /// Number of required transaction signatures. + pub num_required_signatures: u8, + /// Number of readonly signed accounts. + pub num_readonly_signed_accounts: u8, + /// Number of readonly unsigned accounts. + pub num_readonly_unsigned_accounts: u8, +} + +/// Canonical Solana transaction message. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalTransactionMessage { + /// Message header. + pub header: CanonicalMessageHeader, + /// Static account keys in message order. + pub static_account_keys: std::vec::Vec, + /// Recent blockhash as base58 text. + pub recent_blockhash: std::string::String, + /// Top-level compiled instructions. + pub instructions: std::vec::Vec, + /// Address lookup table references for versioned messages. + pub address_table_lookups: std::vec::Vec, + /// Loaded writable and readonly addresses resolved by the runtime. + pub loaded_addresses: CanonicalLoadedAddresses, +} + +impl CanonicalTransactionMessage { + /// Returns the resolved account count including loaded addresses. + pub fn resolved_account_count(&self) -> usize { + return self.static_account_keys.len() + + self.loaded_addresses.writable.len() + + self.loaded_addresses.readonly.len(); + } + + fn validate(&self) -> kb_core::Result<()> { + if self.static_account_keys.is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical transaction message must contain static account keys", + )); + } + for account_key in &self.static_account_keys { + let key_result = validate_pubkey_text(account_key.as_str(), "static account key"); + if let std::result::Result::Err(error) = key_result { + return std::result::Result::Err(error); + } + } + let blockhash_result = + validate_pubkey_text(self.recent_blockhash.as_str(), "recent blockhash"); + if let std::result::Result::Err(error) = blockhash_result { + return std::result::Result::Err(error); + } + let required_signature_count = usize::from(self.header.num_required_signatures); + if required_signature_count == 0 + || required_signature_count > self.static_account_keys.len() + { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical message required signature count is invalid", + )); + } + if usize::from(self.header.num_readonly_signed_accounts) > required_signature_count { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical readonly signed account count is invalid", + )); + } + let unsigned_account_count = self.static_account_keys.len() - required_signature_count; + if usize::from(self.header.num_readonly_unsigned_accounts) > unsigned_account_count { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical readonly unsigned account count is invalid", + )); + } + let resolved_account_count = self.resolved_account_count(); + for instruction in &self.instructions { + let instruction_result = instruction.validate(resolved_account_count); + if let std::result::Result::Err(error) = instruction_result { + return std::result::Result::Err(error); + } + } + for lookup in &self.address_table_lookups { + let lookup_result = + validate_pubkey_text(lookup.account_key.as_str(), "address lookup table key"); + if let std::result::Result::Err(error) = lookup_result { + return std::result::Result::Err(error); + } + } + for account_key in &self.loaded_addresses.writable { + let key_result = + validate_pubkey_text(account_key.as_str(), "loaded writable account key"); + if let std::result::Result::Err(error) = key_result { + return std::result::Result::Err(error); + } + } + for account_key in &self.loaded_addresses.readonly { + let key_result = + validate_pubkey_text(account_key.as_str(), "loaded readonly account key"); + if let std::result::Result::Err(error) = key_result { + return std::result::Result::Err(error); + } + } + return std::result::Result::Ok(()); + } +} + +/// Canonical compiled Solana instruction. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalCompiledInstruction { + /// Resolved account index of the invoked program. + pub program_id_index: u16, + /// Resolved account indexes consumed by the instruction. + pub account_indexes: std::vec::Vec, + /// Instruction data encoded as standard base64. + pub data_base64: std::string::String, + /// Optional runtime invocation stack height. + pub stack_height: std::option::Option, +} + +impl CanonicalCompiledInstruction { + fn validate(&self, resolved_account_count: usize) -> kb_core::Result<()> { + if usize::from(self.program_id_index) >= resolved_account_count { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical instruction program id index is outside resolved accounts", + )); + } + for account_index in &self.account_indexes { + if usize::from(*account_index) >= resolved_account_count { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical instruction account index is outside resolved accounts", + )); + } + } + let decode_result = + base64::engine::general_purpose::STANDARD.decode(self.data_base64.as_bytes()); + if let std::result::Result::Err(error) = decode_result { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "canonical instruction data is not valid base64: {error}" + ))); + } + return std::result::Result::Ok(()); + } +} + +/// Canonical address lookup table reference. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalAddressTableLookup { + /// Lookup table account key as base58 text. + pub account_key: std::string::String, + /// Writable lookup indexes. + pub writable_indexes: std::vec::Vec, + /// Readonly lookup indexes. + pub readonly_indexes: std::vec::Vec, +} + +/// Runtime-loaded addresses for a versioned transaction. +#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalLoadedAddresses { + /// Writable loaded addresses in runtime order. + pub writable: std::vec::Vec, + /// Readonly loaded addresses in runtime order. + pub readonly: std::vec::Vec, +} + +/// Canonical transaction execution status. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum CanonicalTransactionStatus { + /// Transaction execution succeeded. + Success, + /// Transaction execution failed. + Failed, +} + +/// Canonical transaction execution metadata. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalTransactionMetadata { + /// Canonical execution status. + pub status: CanonicalTransactionStatus, + /// Optional source-independent transaction error document. + pub error: std::option::Option, + /// Transaction fee in lamports. + pub fee: u64, + /// Account balances before execution in lamports. + pub pre_balances: std::vec::Vec, + /// Account balances after execution in lamports. + pub post_balances: std::vec::Vec, + /// Inner instruction groups indexed by parent top-level instruction. + pub inner_instructions: std::vec::Vec, + /// Runtime log messages in execution order. + pub log_messages: std::vec::Vec, + /// Token balances before execution. + pub pre_token_balances: std::vec::Vec, + /// Token balances after execution. + pub post_token_balances: std::vec::Vec, + /// Transaction rewards. + pub rewards: std::vec::Vec, + /// Optional transaction return data. + pub return_data: std::option::Option, + /// Optional consumed compute units. + pub compute_units_consumed: std::option::Option, + /// Optional runtime cost units. + pub cost_units: std::option::Option, +} + +impl CanonicalTransactionMetadata { + fn validate( + &self, + resolved_account_count: usize, + top_level_instruction_count: usize, + ) -> kb_core::Result<()> { + if self.pre_balances.len() != self.post_balances.len() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical pre and post balance arrays must have the same length", + )); + } + if self.pre_balances.len() != resolved_account_count { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical balance arrays must match the resolved account count", + )); + } + match self.status { + CanonicalTransactionStatus::Success => { + if self.error.is_some() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "successful canonical transaction must not contain an error", + )); + } + }, + CanonicalTransactionStatus::Failed => { + if self.error.is_none() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "failed canonical transaction must contain an error", + )); + } + }, + } + for inner_group in &self.inner_instructions { + if usize::from(inner_group.parent_instruction_index) >= top_level_instruction_count { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical inner instruction parent index is outside top-level instructions", + )); + } + for instruction in &inner_group.instructions { + let instruction_result = instruction.validate(resolved_account_count); + if let std::result::Result::Err(error) = instruction_result { + return std::result::Result::Err(error); + } + } + } + for balance in &self.pre_token_balances { + let balance_result = balance.validate(resolved_account_count); + if let std::result::Result::Err(error) = balance_result { + return std::result::Result::Err(error); + } + } + for balance in &self.post_token_balances { + let balance_result = balance.validate(resolved_account_count); + if let std::result::Result::Err(error) = balance_result { + return std::result::Result::Err(error); + } + } + for reward in &self.rewards { + let reward_result = validate_pubkey_text(reward.pubkey.as_str(), "reward public key"); + if let std::result::Result::Err(error) = reward_result { + return std::result::Result::Err(error); + } + if let std::option::Option::Some(commission) = reward.commission { + if commission > 100 { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical reward commission must not exceed 100 percent", + )); + } + } + } + if let std::option::Option::Some(return_data) = &self.return_data { + let program_result = + validate_pubkey_text(return_data.program_id.as_str(), "return data program id"); + if let std::result::Result::Err(error) = program_result { + return std::result::Result::Err(error); + } + let decode_result = base64::engine::general_purpose::STANDARD + .decode(return_data.data_base64.as_bytes()); + if let std::result::Result::Err(error) = decode_result { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "canonical return data is not valid base64: {error}" + ))); + } + } + return std::result::Result::Ok(()); + } +} + +/// Group of inner instructions attached to one top-level instruction. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalInnerInstructionGroup { + /// Parent top-level instruction index. + pub parent_instruction_index: u16, + /// Inner instructions in runtime order. + pub instructions: std::vec::Vec, +} + +/// Exact SPL token balance representation. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalTokenBalance { + /// Resolved account index. + pub account_index: u16, + /// Token mint public key. + pub mint: std::string::String, + /// Optional token account owner public key. + pub owner: std::option::Option, + /// Optional token program public key. + pub program_id: std::option::Option, + /// Exact raw token amount as an unsigned decimal string. + pub amount: std::string::String, + /// Mint decimals. + pub decimals: u8, + /// Exact fixed-scale decimal amount derived from `amount` and `decimals`. + pub decimal_amount: std::string::String, +} + +impl CanonicalTokenBalance { + /// Creates an exact canonical token balance without provider-specific UI formatting. + pub fn new( + account_index: u16, + mint: impl std::convert::Into, + owner: std::option::Option, + program_id: std::option::Option, + amount: impl std::convert::Into, + decimals: u8, + ) -> kb_core::Result { + let amount_value = amount.into(); + let normalized_result = normalize_unsigned_decimal(amount_value.as_str()); + let normalized_amount = match normalized_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let decimal_result = exact_decimal_amount(normalized_amount.as_str(), decimals); + let decimal_amount = match decimal_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(Self { + account_index, + mint: mint.into(), + owner, + program_id, + amount: normalized_amount, + decimals, + decimal_amount, + }); + } + + fn validate(&self, resolved_account_count: usize) -> kb_core::Result<()> { + if usize::from(self.account_index) >= resolved_account_count { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical token balance account index is outside resolved accounts", + )); + } + let mint_result = validate_pubkey_text(self.mint.as_str(), "token balance mint"); + if let std::result::Result::Err(error) = mint_result { + return std::result::Result::Err(error); + } + if let std::option::Option::Some(owner) = &self.owner { + let owner_result = validate_pubkey_text(owner.as_str(), "token balance owner"); + if let std::result::Result::Err(error) = owner_result { + return std::result::Result::Err(error); + } + } + if let std::option::Option::Some(program_id) = &self.program_id { + let program_result = + validate_pubkey_text(program_id.as_str(), "token balance program id"); + if let std::result::Result::Err(error) = program_result { + return std::result::Result::Err(error); + } + } + let decimal_result = exact_decimal_amount(self.amount.as_str(), self.decimals); + let expected_decimal_amount = match decimal_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if self.decimal_amount != expected_decimal_amount { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical token balance decimal amount does not match amount and decimals", + )); + } + return std::result::Result::Ok(()); + } +} + +/// Canonical transaction reward. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalReward { + /// Reward account public key. + pub pubkey: std::string::String, + /// Signed lamport delta. + pub lamports: i64, + /// Post-reward balance in lamports. + pub post_balance: u64, + /// Optional reward type string. + pub reward_type: std::option::Option, + /// Optional validator commission percentage. + pub commission: std::option::Option, +} + +/// Canonical transaction return data. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalReturnData { + /// Program that returned the data. + pub program_id: std::string::String, + /// Return bytes encoded as standard base64. + pub data_base64: std::string::String, +} + +fn normalize_unsigned_decimal(amount: &str) -> kb_core::Result { + if amount.is_empty() || !amount.chars().all(|character| return character.is_ascii_digit()) { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical token balance amount must be an unsigned decimal string", + )); + } + let trimmed = amount.trim_start_matches('0'); + if trimmed.is_empty() { + return std::result::Result::Ok("0".to_string()); + } + return std::result::Result::Ok(trimmed.to_string()); +} + +fn exact_decimal_amount(amount: &str, decimals: u8) -> kb_core::Result { + let normalized_result = normalize_unsigned_decimal(amount); + let normalized = match normalized_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let scale = usize::from(decimals); + if scale == 0 { + return std::result::Result::Ok(normalized.clone()); + } + if normalized.len() > scale { + let split_index = normalized.len() - scale; + return std::result::Result::Ok(format!( + "{}.{}", + &normalized[..split_index], + &normalized[split_index..] + )); + } + let zero_count = scale - normalized.len(); + return std::result::Result::Ok(format!("0.{}{}", "0".repeat(zero_count), normalized)); +} + +fn sort_json_value(value: &mut serde_json::Value) { + match value { + serde_json::Value::Object(object) => { + let mut keys = object.keys().cloned().collect::>(); + keys.sort(); + let mut sorted = serde_json::Map::new(); + for key in keys { + let removed = object.remove(key.as_str()); + if let std::option::Option::Some(mut child) = removed { + sort_json_value(&mut child); + sorted.insert(key, child); + } + } + *object = sorted; + }, + serde_json::Value::Array(items) => { + for item in items { + sort_json_value(item); + } + }, + _ => {}, + } +} + +fn validate_signature_text(value: &str, field_name: &str) -> kb_core::Result<()> { + return validate_base58_length(value, field_name, 64); +} + +fn validate_pubkey_text(value: &str, field_name: &str) -> kb_core::Result<()> { + return validate_base58_length(value, field_name, 32); +} + +fn validate_base58_length( + value: &str, + field_name: &str, + expected_length: usize, +) -> kb_core::Result<()> { + if value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "{field_name} must not be empty" + ))); + } + let decode_result = bs58::decode(value).into_vec(); + let decoded = match decode_result { + std::result::Result::Ok(bytes) => bytes, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "{field_name} is not valid base58: {error}" + ))); + }, + }; + if decoded.len() != expected_length { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "{field_name} must decode to {expected_length} bytes" + ))); + } + return std::result::Result::Ok(()); +} + +#[cfg(test)] +mod tests { + fn sample_transaction() -> crate::CanonicalTransaction { + return crate::CanonicalTransaction { + format_version: crate::CANONICAL_TRANSACTION_FORMAT_VERSION, + primary_signature: "2Ana1pUpv2ZbMVkwF5FXapYeBEjdxDatLn7nvJkhgTSXbs59SyZSx866bXirPgj8QQVB57uxHJBG1YFvkRbFj4T".to_string(), + slot: 42, + block_time: std::option::Option::Some(1_700_000_000), + version: crate::CanonicalTransactionVersion::Legacy, + signatures: std::vec![ + "2Ana1pUpv2ZbMVkwF5FXapYeBEjdxDatLn7nvJkhgTSXbs59SyZSx866bXirPgj8QQVB57uxHJBG1YFvkRbFj4T".to_string(), + ], + message: crate::CanonicalTransactionMessage { + header: crate::CanonicalMessageHeader { + num_required_signatures: 1, + num_readonly_signed_accounts: 0, + num_readonly_unsigned_accounts: 1, + }, + static_account_keys: std::vec![ + "11111111111111111111111111111111".to_string(), + "SysvarC1ock11111111111111111111111111111111".to_string(), + ], + recent_blockhash: "11111111111111111111111111111111".to_string(), + instructions: std::vec![crate::CanonicalCompiledInstruction { + program_id_index: 0, + account_indexes: std::vec![1], + data_base64: "AQ==".to_string(), + stack_height: std::option::Option::Some(1), + }], + address_table_lookups: std::vec::Vec::new(), + loaded_addresses: crate::CanonicalLoadedAddresses::default(), + }, + metadata: std::option::Option::Some(crate::CanonicalTransactionMetadata { + status: crate::CanonicalTransactionStatus::Success, + error: std::option::Option::None, + fee: 5000, + pre_balances: std::vec![10_000, 1], + post_balances: std::vec![5_000, 1], + inner_instructions: std::vec::Vec::new(), + log_messages: std::vec!["Program 11111111111111111111111111111111 success".to_string()], + pre_token_balances: std::vec::Vec::new(), + post_token_balances: std::vec::Vec::new(), + rewards: std::vec::Vec::new(), + return_data: std::option::Option::None, + compute_units_consumed: std::option::Option::Some(100), + cost_units: std::option::Option::Some(120), + }), + }; + } + + #[test] + fn canonical_hash_is_stable() { + let first = sample_transaction(); + let second = sample_transaction(); + let first_hash = match first.canonical_json_hash() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("first hash failed: {error}"), + }; + let second_hash = match second.canonical_json_hash() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("second hash failed: {error}"), + }; + assert_eq!(first_hash, second_hash); + assert_eq!(first_hash.len(), 64); + } + + #[test] + fn canonical_hash_sorts_nested_error_objects() { + let mut first = sample_transaction(); + let mut second = sample_transaction(); + if let std::option::Option::Some(metadata) = &mut first.metadata { + metadata.status = crate::CanonicalTransactionStatus::Failed; + metadata.error = std::option::Option::Some(serde_json::json!({ + "z": 3, + "nested": {"b": 2, "a": 1} + })); + } + if let std::option::Option::Some(metadata) = &mut second.metadata { + metadata.status = crate::CanonicalTransactionStatus::Failed; + let parse_result = + serde_json::from_str::(r#"{"nested":{"a":1,"b":2},"z":3}"#); + metadata.error = match parse_result { + std::result::Result::Ok(value) => std::option::Option::Some(value), + std::result::Result::Err(error) => panic!("error fixture parse failed: {error}"), + }; + } + let first_hash = match first.canonical_json_hash() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("first nested hash failed: {error}"), + }; + let second_hash = match second.canonical_json_hash() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("second nested hash failed: {error}"), + }; + assert_eq!(first_hash, second_hash); + } + + #[test] + fn validation_rejects_signature_with_public_key_length() { + let mut transaction = sample_transaction(); + transaction.primary_signature = "3Bxs4NN8M2Yn4TLb7gR6Xy7n2D1Q8VjQqWcnpX8C6pQw".to_string(); + transaction.signatures[0] = transaction.primary_signature.clone(); + let result = transaction.validate(); + assert!(result.is_err()); + } + + #[test] + fn token_decimal_amount_is_derived_without_source_ui_formatting() { + let result = crate::CanonicalTokenBalance::new( + 0, + "So11111111111111111111111111111111111111112", + std::option::Option::None, + std::option::Option::None, + "000123", + 5, + ); + let balance = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("token balance creation failed: {error}"), + }; + assert_eq!(balance.amount, "123"); + assert_eq!(balance.decimal_amount, "0.00123"); + } + + #[test] + fn canonical_json_contains_no_provider_fields() { + let transaction = sample_transaction(); + let json_result = transaction.to_canonical_json(); + let json = match json_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("canonical json failed: {error}"), + }; + let text = json.to_string(); + assert!(!text.contains("provider")); + assert!(!text.contains("endpoint")); + assert!(!text.contains("protocol")); + } + + #[test] + fn validation_rejects_inconsistent_status_and_error() { + let mut transaction = sample_transaction(); + if let std::option::Option::Some(metadata) = &mut transaction.metadata { + metadata.status = crate::CanonicalTransactionStatus::Failed; + } + let result = transaction.validate(); + assert!(result.is_err()); + } +} diff --git a/kb-lib/src/model/decoded.rs b/kb-lib/src/model/decoded.rs new file mode 100644 index 0000000..4b9443d --- /dev/null +++ b/kb-lib/src/model/decoded.rs @@ -0,0 +1,87 @@ +// file: kb-lib/src/model/decoded.rs +// version: 4 + +//! Shared decoded protocol event model. + +use ts_rs::TS; // rust-rules: derive-import + +/// Source from which a decoded event was derived. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/decoded/EventSourceKind.ts" +)] +pub enum EventSourceKind { + /// Top-level instruction. + Instruction, + /// Inner instruction. + InnerInstruction, + /// Program log line. + Log, + /// Anchor event. + AnchorEvent, + /// Anchor self-CPI event. + AnchorSelfCpiEvent, + /// Balance delta inference. + BalanceDelta, + /// Synthetic test or generated event. + Synthetic, + /// Inferred event. + Inferred, + /// Audit-only event. + Audit, +} + +/// Confidence level for a decoded event. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/decoded/DecoderConfidence.ts" +)] +pub enum DecoderConfidence { + /// Exact manual decode. + Exact, + /// Exact IDL-based decode. + IdlExact, + /// Exact manually verified decode. + ManualExact, + /// Inferred decode. + Inferred, + /// Unsafe decode. + Unsafe, + /// Audit-only decode. + AuditOnly, + /// Unknown confidence. + Unknown, +} + +/// Protocol-level decoded event produced by a decoder. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/decoded/DecodedProtocolEvent.ts" +)] +pub struct DecodedProtocolEvent { + /// Transaction signature. + pub signature: crate::Signature, + /// Transaction slot. + pub slot: crate::Slot, + /// Instruction path. + pub instruction_path: crate::InstructionPath, + /// Program id. + pub program_id: crate::ProgramId, + /// Protocol family. + pub protocol_code: crate::ProtocolCode, + /// Protocol surface. + pub surface_code: crate::SurfaceCode, + /// Canonical event code. + pub event_code: crate::EventCode, + /// Short event name. + pub event_name: crate::EventName, + /// Event family. + pub event_family: crate::EventFamily, + /// Event source kind. + pub source_kind: crate::EventSourceKind, + /// Decoder confidence. + pub confidence: crate::DecoderConfidence, +} diff --git a/kb-lib/src/model/materialized.rs b/kb-lib/src/model/materialized.rs new file mode 100644 index 0000000..7121220 --- /dev/null +++ b/kb-lib/src/model/materialized.rs @@ -0,0 +1,80 @@ +// file: kb-lib/src/model/materialized.rs +// version: 5 + +//! Shared materialized business event model. + +use ts_rs::TS; // rust-rules: derive-import + +/// Materialized event family. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/materialized/MaterializedEventFamily.ts" +)] +pub enum MaterializedEventFamily { + /// Trade materialization. + Trade, + /// Liquidity materialization. + Liquidity, + /// Lifecycle materialization. + Lifecycle, + /// Fee materialization. + Fee, + /// Admin materialization. + Admin, + /// Token-account materialization. + TokenAccount, + /// Pool-state materialization. + PoolState, + /// Orderbook materialization. + Orderbook, + /// Reward materialization. + Reward, + /// NFT materialization. + Nft, + /// Metadata materialization. + Metadata, + /// Oracle materialization. + Oracle, + /// Lending materialization. + Lending, + /// Staking materialization. + Staking, + /// Governance materialization. + Governance, + /// Bridge materialization. + Bridge, + /// Perpetuals materialization. + Perpetuals, + /// Vault materialization. + Vault, + /// Routing materialization. + Routing, + /// Compliance audit materialization. + ComplianceAudit, + /// Token metadata risk materialization. + TokenMetadataRisk, + /// Risk materialization. + Risk, + /// Transaction annotation materialization. + TransactionAnnotation, + /// Unknown materialization family. + Unknown, +} + +/// Business-level materialized event placeholder. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/materialized/MaterializedEvent.ts" +)] +pub struct MaterializedEvent { + /// Source transaction signature. + pub signature: crate::Signature, + /// Source transaction slot. + pub slot: crate::Slot, + /// Source decoded family. + pub decoded_family: crate::EventFamily, + /// Materialized family. + pub materialized_family: crate::MaterializedEventFamily, +} diff --git a/kb-lib/src/model/nomenclature.rs b/kb-lib/src/model/nomenclature.rs new file mode 100644 index 0000000..5ec0bd3 --- /dev/null +++ b/kb-lib/src/model/nomenclature.rs @@ -0,0 +1,99 @@ +// file: kb-lib/src/model/nomenclature.rs +// version: 3 + +//! Shared protocol, surface, and event nomenclature types. + +use ts_rs::TS; // rust-rules: derive-import + +/// Stable internal program code. +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/nomenclature/ProgramCode.ts" +)] +pub struct ProgramCode(pub std::string::String); + +/// Protocol family code, for example `raydium` or `meteora`. +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/nomenclature/ProtocolCode.ts" +)] +pub struct ProtocolCode(pub std::string::String); + +/// Concrete protocol surface code, for example `raydium_amm_v4`. +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/nomenclature/SurfaceCode.ts" +)] +pub struct SurfaceCode(pub std::string::String); + +/// Event name without surface prefix. +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_model/nomenclature/EventName.ts")] +pub struct EventName(pub std::string::String); + +/// Canonical event code in `.` format. +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_model/nomenclature/EventCode.ts")] +pub struct EventCode(pub std::string::String); + +/// High-level event family. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/nomenclature/EventFamily.ts" +)] +pub enum EventFamily { + /// Swap or buy/sell event. + Trade, + /// Liquidity deposit/withdraw or equivalent. + Liquidity, + /// Pool, market, curve, or config lifecycle event. + Lifecycle, + /// Fee claim, collection, sweep, or update. + Fee, + /// Admin or authority event. + Admin, + /// Reward event. + Reward, + /// Orderbook event. + Orderbook, + /// Token account event. + TokenAccount, + /// Token mint event. + TokenMint, + /// Token burn event. + TokenBurn, + /// NFT event. + Nft, + /// Token or NFT metadata event. + Metadata, + /// Oracle event. + Oracle, + /// Lending or borrowing event. + Lending, + /// Staking event. + Staking, + /// Governance event. + Governance, + /// Bridge event. + Bridge, + /// Perpetuals or derivatives event. + Perpetuals, + /// Vault event. + Vault, + /// Router or aggregator event. + Routing, + /// Compliance audit event. + ComplianceAudit, + /// Token metadata risk event. + TokenMetadataRisk, + /// Risk signal. + Risk, + /// Audit-only event. + Audit, + /// Unknown family. + Unknown, +} diff --git a/kb-lib/src/model/observation.rs b/kb-lib/src/model/observation.rs new file mode 100644 index 0000000..1bf5ed3 --- /dev/null +++ b/kb-lib/src/model/observation.rs @@ -0,0 +1,31 @@ +// file: kb-lib/src/model/observation.rs +// version: 4 + +//! Shared program observation model. + +use ts_rs::TS; // rust-rules: derive-import + +/// Generic program observation derived from a Solana instruction. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/observation/ProgramObservation.ts" +)] +pub struct ProgramObservation { + /// Transaction signature. + pub signature: crate::Signature, + /// Transaction slot. + pub slot: crate::Slot, + /// Instruction path. + pub instruction_path: crate::InstructionPath, + /// Program id. + pub program_id: crate::ProgramId, + /// Optional 8-byte discriminator in hexadecimal. + pub discriminator_8: std::option::Option, + /// Raw instruction data length. + pub data_len: usize, + /// Number of instruction accounts. + pub accounts_len: usize, + /// Whether the source transaction failed. + pub failed: bool, +} diff --git a/kb-lib/src/model/replay.rs b/kb-lib/src/model/replay.rs new file mode 100644 index 0000000..afe9be6 --- /dev/null +++ b/kb-lib/src/model/replay.rs @@ -0,0 +1,126 @@ +// file: kb-lib/src/model/replay.rs +// version: 2 + +//! Source-neutral instruction replay input shared by decoders and stores. + +/// Version of the normalized core replay contract. +pub const CORE_REPLAY_INPUT_CONTRACT_VERSION: u32 = 2; + +/// Decoder replay input containing one instruction plus extracted transaction context. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoreInstructionReplayInput { + /// Version of the normalized core replay contract. + pub core_contract_version: u32, + /// Stable deduplication key for this replay input. + pub replay_input_key: std::string::String, + /// Transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Transaction slot in Solana unsigned representation. + pub slot: u64, + /// Stable instruction path, for example `0` or `2/1`. + pub instruction_path: std::string::String, + /// Program id as non-empty base58 text. + pub program_id: std::string::String, + /// Optional surface hint supplied by an operator or upstream classifier. + pub surface_code_hint: std::option::Option, + /// Whether the parent transaction failed on-chain. + pub transaction_failed: bool, + /// Optional transaction error JSON. + pub transaction_err_json: std::option::Option, + /// Resolved account keys for the whole transaction. + pub account_keys_json: serde_json::Value, + /// Instruction accounts for the target instruction. + pub instruction_accounts_json: serde_json::Value, + /// Optional instruction payload retained by the store. + pub instruction_payload_json: std::option::Option, + /// Optional deterministic hash of the retained instruction payload. + pub instruction_payload_hash: std::option::Option, + /// Ordered outer instructions for the transaction. + pub outer_instructions_json: serde_json::Value, + /// Inner instruction tree or subtree relevant to the target instruction. + pub inner_instructions_json: serde_json::Value, + /// Ordered logs relevant to the transaction or target instruction. + pub logs_json: serde_json::Value, + /// Balance changes relevant to the transaction or target instruction. + pub balance_changes_json: serde_json::Value, +} + +impl CoreInstructionReplayInput { + /// Builds a decoder replay input after validating its stable identity and array context. + #[allow(clippy::too_many_arguments)] + pub fn new( + replay_input_key: impl std::convert::Into, + signature: impl std::convert::Into, + slot: u64, + instruction_path: impl std::convert::Into, + program_id: impl std::convert::Into, + transaction_failed: bool, + transaction_err_json: std::option::Option, + account_keys_json: serde_json::Value, + instruction_accounts_json: serde_json::Value, + instruction_payload_json: std::option::Option, + instruction_payload_hash: std::option::Option, + outer_instructions_json: serde_json::Value, + inner_instructions_json: serde_json::Value, + logs_json: serde_json::Value, + balance_changes_json: serde_json::Value, + ) -> kb_core::Result { + if !outer_instructions_json.is_array() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "core replay input outer instructions must be a JSON array", + )); + } + let value = Self { + core_contract_version: crate::CORE_REPLAY_INPUT_CONTRACT_VERSION, + replay_input_key: replay_input_key.into(), + signature: signature.into(), + slot, + instruction_path: instruction_path.into(), + program_id: program_id.into(), + surface_code_hint: std::option::Option::None, + transaction_failed, + transaction_err_json, + account_keys_json, + instruction_accounts_json, + instruction_payload_json, + instruction_payload_hash, + outer_instructions_json, + inner_instructions_json, + logs_json, + balance_changes_json, + }; + let validation_result = value.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(value); + } + + /// Validates the stable replay identity and contract version. + pub fn validate(&self) -> kb_core::Result<()> { + if self.core_contract_version != crate::CORE_REPLAY_INPUT_CONTRACT_VERSION { + return std::result::Result::Err(kb_core::Error::invalid_state( + "unsupported core replay input contract version", + )); + } + if self.replay_input_key.trim().is_empty() + || self.signature.trim().is_empty() + || self.instruction_path.trim().is_empty() + || self.program_id.trim().is_empty() + { + return std::result::Result::Err(kb_core::Error::invalid_state( + "core replay identity fields must not be empty", + )); + } + if self + .instruction_payload_hash + .as_deref() + .is_some_and(|value| return value.trim().is_empty()) + { + return std::result::Result::Err(kb_core::Error::invalid_state( + "core replay payload hash must not be empty when present", + )); + } + return std::result::Result::Ok(()); + } +} diff --git a/kb-lib/src/model/solana.rs b/kb-lib/src/model/solana.rs new file mode 100644 index 0000000..2e2a28b --- /dev/null +++ b/kb-lib/src/model/solana.rs @@ -0,0 +1,43 @@ +// file: kb-lib/src/model/solana.rs +// version: 3 + +//! Shared Solana primitive wrapper types. + +use ts_rs::TS; // rust-rules: derive-import + +/// Solana transaction signature. +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_model/solana/Signature.ts")] +pub struct Signature(pub std::string::String); + +/// Solana slot. +#[derive( + Clone, + Copy, + Debug, + Eq, + PartialEq, + Ord, + PartialOrd, + Hash, + serde::Deserialize, + serde::Serialize, + TS, +)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_model/solana/Slot.ts")] +pub struct Slot(pub u64); + +/// Solana program id. +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_model/solana/ProgramId.ts")] +pub struct ProgramId(pub std::string::String); + +/// Solana public key. +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_model/solana/Pubkey.ts")] +pub struct Pubkey(pub std::string::String); + +/// Stable top-level or inner instruction path. +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_model/solana/InstructionPath.ts")] +pub struct InstructionPath(pub std::string::String); diff --git a/kb-logging/Cargo.toml b/kb-logging/Cargo.toml new file mode 100644 index 0000000..6ecf759 --- /dev/null +++ b/kb-logging/Cargo.toml @@ -0,0 +1,12 @@ +# file: kb-logging/Cargo.toml +# version: 1 + +[package] +name = "kb-logging" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[lints] +workspace = true diff --git a/kb-logging/src/constants.rs b/kb-logging/src/constants.rs new file mode 100644 index 0000000..f122210 --- /dev/null +++ b/kb-logging/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb-logging/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb-logging"; diff --git a/kb-logging/src/lib.rs b/kb-logging/src/lib.rs new file mode 100644 index 0000000..29dce3e --- /dev/null +++ b/kb-logging/src/lib.rs @@ -0,0 +1,18 @@ +// file: kb-logging/src/lib.rs +// version: 1 + +#![forbid(unsafe_code)] +#![deny(unreachable_pub)] +#![warn(missing_docs)] + +//! Logging contracts for Khadhroony Bot3. + +mod constants; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Returns the canonical tracing target. +pub fn tracing_target() -> &'static str { + return crate::TRACING_TARGET; +} diff --git a/kb-pipeline/Cargo.toml b/kb-pipeline/Cargo.toml new file mode 100644 index 0000000..5959dfb --- /dev/null +++ b/kb-pipeline/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb-pipeline/Cargo.toml +# version: 1 + +[package] +name = "kb-pipeline" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb-core = { path = "../kb-core" } +kb-lib = { path = "../kb-lib" } +kb-store = { path = "../kb-store" } +kb-rpc = { path = "../kb-rpc" } +kb-wallet = { path = "../kb-wallet" } + +[lints] +workspace = true diff --git a/kb-pipeline/src/lib.rs b/kb-pipeline/src/lib.rs new file mode 100644 index 0000000..7873bfb --- /dev/null +++ b/kb-pipeline/src/lib.rs @@ -0,0 +1,13 @@ +// file: kb-pipeline/src/lib.rs +// version: 1 + +#![forbid(unsafe_code)] +#![deny(unreachable_pub)] +#![warn(missing_docs)] + +//! Pipeline orchestration boundary. + +/// Returns the pipeline migration status. +pub fn migration_status() -> &'static str { + return "scaffolded"; +} diff --git a/kb-program-ids/Cargo.toml b/kb-program-ids/Cargo.toml new file mode 100644 index 0000000..6254807 --- /dev/null +++ b/kb-program-ids/Cargo.toml @@ -0,0 +1,12 @@ +# file: kb-program-ids/Cargo.toml +# version: 1 + +[package] +name = "kb-program-ids" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[lints] +workspace = true diff --git a/kb-program-ids/src/lib.rs b/kb-program-ids/src/lib.rs new file mode 100644 index 0000000..bee5434 --- /dev/null +++ b/kb-program-ids/src/lib.rs @@ -0,0 +1,22 @@ +// file: kb-program-ids/src/lib.rs +// version: 1 + +#![forbid(unsafe_code)] +#![deny(unreachable_pub)] +#![warn(missing_docs)] + +//! Program identifier registry. + +/// One program identifier entry. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ProgramIdEntry { + /// Stable symbolic name. + pub name: &'static str, + /// Base58 program identifier. + pub address: &'static str, +} + +/// Returns the initial canonical program registry. +pub fn entries() -> &'static [crate::ProgramIdEntry] { + return &[]; +} diff --git a/kb-rpc/Cargo.toml b/kb-rpc/Cargo.toml new file mode 100644 index 0000000..fd2e266 --- /dev/null +++ b/kb-rpc/Cargo.toml @@ -0,0 +1,15 @@ +# file: kb-rpc/Cargo.toml +# version: 1 + +[package] +name = "kb-rpc" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb-core = { path = "../kb-core" } + +[lints] +workspace = true diff --git a/kb-rpc/src/lib.rs b/kb-rpc/src/lib.rs new file mode 100644 index 0000000..aa92b23 --- /dev/null +++ b/kb-rpc/src/lib.rs @@ -0,0 +1,13 @@ +// file: kb-rpc/src/lib.rs +// version: 1 + +#![forbid(unsafe_code)] +#![deny(unreachable_pub)] +#![warn(missing_docs)] + +//! RPC transport contracts. + +/// Returns the crate migration status. +pub fn migration_status() -> &'static str { + return "scaffolded"; +} diff --git a/kb-store/Cargo.toml b/kb-store/Cargo.toml new file mode 100644 index 0000000..e4bd3f4 --- /dev/null +++ b/kb-store/Cargo.toml @@ -0,0 +1,15 @@ +# file: kb-store/Cargo.toml +# version: 1 + +[package] +name = "kb-store" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb-core = { path = "../kb-core" } + +[lints] +workspace = true diff --git a/kb-store/src/core.rs b/kb-store/src/core.rs new file mode 100644 index 0000000..52a7aa2 --- /dev/null +++ b/kb-store/src/core.rs @@ -0,0 +1,11 @@ +// file: kb-store/src/core.rs +// version: 1 + +//! Store-neutral contracts. + +/// Store health summary. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct StoreHealth { + /// Whether the backend is available. + pub available: bool, +} diff --git a/kb-store/src/lib.rs b/kb-store/src/lib.rs new file mode 100644 index 0000000..fc917f9 --- /dev/null +++ b/kb-store/src/lib.rs @@ -0,0 +1,14 @@ +// file: kb-store/src/lib.rs +// version: 1 + +#![forbid(unsafe_code)] +#![deny(unreachable_pub)] +#![warn(missing_docs)] + +//! Consolidated storage crate. + +pub mod core; +pub mod postgres; + +/// Store health summary. +pub use crate::core::StoreHealth; diff --git a/kb-store/src/postgres.rs b/kb-store/src/postgres.rs new file mode 100644 index 0000000..4e9d400 --- /dev/null +++ b/kb-store/src/postgres.rs @@ -0,0 +1,9 @@ +// file: kb-store/src/postgres.rs +// version: 1 + +//! PostgreSQL backend boundary. + +/// PostgreSQL backend migration status. +pub fn migration_status() -> &'static str { + return "pending-source-port"; +} diff --git a/kb-wallet/Cargo.toml b/kb-wallet/Cargo.toml new file mode 100644 index 0000000..2f07557 --- /dev/null +++ b/kb-wallet/Cargo.toml @@ -0,0 +1,15 @@ +# file: kb-wallet/Cargo.toml +# version: 1 + +[package] +name = "kb-wallet" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb-core = { path = "../kb-core" } + +[lints] +workspace = true diff --git a/kb-wallet/src/lib.rs b/kb-wallet/src/lib.rs new file mode 100644 index 0000000..a2ef429 --- /dev/null +++ b/kb-wallet/src/lib.rs @@ -0,0 +1,13 @@ +// file: kb-wallet/src/lib.rs +// version: 1 + +#![forbid(unsafe_code)] +#![deny(unreachable_pub)] +#![warn(missing_docs)] + +//! Wallet contracts. + +/// Returns the crate migration status. +pub fn migration_status() -> &'static str { + return "scaffolded"; +} diff --git a/migration/README.md b/migration/README.md new file mode 100644 index 0000000..3f3d52d --- /dev/null +++ b/migration/README.md @@ -0,0 +1,7 @@ +# Migration depuis khadhroony-bot2 + +La copie `khadhroony-bot2-reference` est une référence non membre du workspace. Elle permet de porter chaque API sans perdre les sources précédentes. + +`module-map.json` associe chaque ancienne crate decoder/executor/materializer à son nouveau module `kb-lib`. + +Une entrée `MIGRATION_STATUS = "source-preserved-pending-port"` signifie que l’arborescence existe mais que l’implémentation doit encore être portée et validée. diff --git a/migration/khadhroony-bot2-reference/CHANGELOG.md b/migration/khadhroony-bot2-reference/CHANGELOG.md new file mode 100644 index 0000000..b08dcc6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/CHANGELOG.md @@ -0,0 +1,208 @@ + + + +# Changelog + +Ce fichier est réservé aux versions validées. Les changements non validés restent décrits dans `ROADMAP.md` ou `RULES.md`. + +## 0.0.1 — projet vide + +Initialisation du dépôt comme point de départ vide. Cette version sert uniquement de repère historique avant la création du squelette modulaire. + +## 0.0.2 — squelette stabilisé + +Validation du squelette initial de `khadhroony-bot2` : workspace Rust modulaire, règles de développement, nomenclature des surfaces, registres de programmes, configuration multi-profils, couche d'exécution réservée, décodeurs/exécuteurs/matérialisateurs réservés, documentation de cadrage, prompts de session et application Tauri de démonstration en squelette. Cette version devient la base propre avant le développement `0.1.x` consacré à `kb_logging` et `kb_config`. + +## 0.1.0 — `kb_logging` réel + +Validation du jalon `kb_logging` : API publique minimale, initialisation `tracing_subscriber`, routes console, fichier humain, fichier JSON et fichier erreurs, filtrage par target et niveau, nettoyage ANSI des fichiers pour les messages issus de WebView Tauri, support des globs `crate.*` et `crate::*`, et couverture unitaire renforcée. Validation locale effectuée : `cargo test -p kb_logging` avec 14 tests passés et `cargo clippy -p kb_logging --all-targets` sans erreur. `cargo fmt` n'est pas retenu comme validation obligatoire du jalon, car la configuration `rustfmt.toml` contient des options nightly non applicables sur stable et le formatage courant est géré depuis Eclipse. + +## 0.1.1 — `kb_config` typé et validé + +Validation du jalon `kb_config` : configuration JSON multi-profils avec un seul profil actif, schéma `config/schema.config.json` validé via `jsonschema`, désérialisation typée, validation métier des profils, endpoints HTTP JSON-RPC et WebSocket Solana standard, listeners standards, sorties de logging configurables, stores PostgreSQL/SQLite, wallet, exécution et sections de démonstration. Les placeholders de clés restent directement dans les URLs et les champs runtime hors périmètre avant `v1.x` sont refusés par le schéma. Les exports TS-rs suivent la convention `../frontend/ts/bindings/kb_config/settings/.ts`. Validation locale effectuée : `cargo test --tests -p kb_config` avec 35 tests passés, dont les tests générateurs TS-rs `export_bindings_*`, et `cargo clippy` annoncé sans erreur pour le jalon. + +## 0.1.2 — liaison `kb_logging`, `kb_config` et démo Tauri + +Validation du jalon d’intégration Tauri : `kb_app_demo` charge le profil actif via `kb_config`, initialise `kb_logging` depuis cette configuration, conserve un `AppState` global, garde `demo_config` comme fenêtre de démonstration isolée et réémet les logs frontend vers `tracing` avec des targets statiques. La fenêtre principale rend le README workspace en HTML via `markdown-it`, expose un dropdown de démos, et la fenêtre configuration affiche le profil actif, la configuration complète et le schéma JSON avec un viewer JSON interactif. Le verrou mono-instance est appliqué uniquement dans `kb_app_demo/src/main.rs` afin de préserver l’entrée mobile par `kb_app_demo_lib::run()`. Le provider Rustls par défaut est initialisé dans la librairie pour desktop et mobile. Le splashscreen est aligné sur le modèle de `khadhroony-bobobot/kb_demo_app`, avec fade-in/fade-out, durée minimale extensible et attente initiale avant fade-in pour éviter l’apparition instantanée. `kb_core` expose maintenant l’erreur et le résultat communs sous `kb_core::Error` et `kb_core::Result`. Validation locale effectuée : `cargo test -p kb_core` avec 2 tests passés, `cargo test -p kb_config` avec 35 tests passés, `cargo test -p kb_logging` avec 14 tests passés, `cargo test -p kb_app_demo` avec 12 tests passés, `cargo clippy --all-targets` sans erreur et validation visuelle via `cargo tauri dev -c kb_app_demo/tauri.conf.json`. + +## 0.1.3 — clients HTTP/WS Solana standard, rôles et pools + +Validation du jalon RPC initial : `kb_rpc` expose les contrats JSON-RPC 2.0 partagés, le routage par rôle et `request_kind`, les clients HTTP JSON-RPC et WebSocket Solana standard, ainsi que les pools HTTP/WS par rôle. `kb_app_demo` ajoute les fenêtres dédiées `demo_http` et `demo_ws`, les listes contrôlées de rôles et méthodes, le refresh explicite des pools, le profil `mainnet` Helius, la connexion WebSocket persistante conservée dans `AppState`, les subscriptions multiples sur une même socket, l’unsubscribe individuel, la déconnexion globale à l’arrêt de l’application et le throttling des notifications WebSocket vers la WebView pour éviter les freezes sur les programmes très bavards. Les transports gRPC, Helius enhanced WebSocket, Helius gRPC et LaserStream restent hors périmètre avant les versions ultérieures. Validation locale effectuée : `cargo test -p kb_rpc` avec 39 tests passés, `cargo test -p kb_app_demo` avec 28 tests passés, `cargo clippy --all-targets` sans erreur, et validation visuelle des démos HTTP/WS via `cargo tauri dev -c kb_app_demo/tauri.conf.json`. + +## 0.2.0 — conventions PostgreSQL et contrats DB + +Validation du jalon de cadrage stockage : conventions PostgreSQL définies autour du schéma courant/default du profil, généralement `public`, sans espaces applicatifs séparés, et convention de tables Solana `kb_sol__` avec les domaines `raw`, `core`, `obs`, `decode`, `mat`, `catalog`, `agg`, `ops` et `wallet` intégrés au nom de table. Les règles SQL de base sont documentées pour `id`, `created_at`, `updated_at`, `slot`, `signature`, `program_id`, `raw_json` et `payload_json`, avec index minimaux et contraintes strictes mais réversibles. Les responsabilités de `kb_store_core` et `kb_store_pg` sont clarifiées, avec séparation `entities/`, `dtos/`, `queries/` et `repositories/`, et interdiction de placer des structures métier ou DB dans les requêtes. Les migrations historiques de brouillon sont neutralisées afin de ne pas créer d'espace PostgreSQL applicatif ni de table qualifiée par espace logique. Validation locale effectuée : `cargo test -p kb_config` avec 35 tests passés, `cargo test -p kb_app_demo` avec 28 tests passés et `cargo clippy --all-targets` sans erreur. `kb_store_core` et `kb_store_pg` ne disposent pas encore de tests à ce stade documentaire. + +## 0.2.1 — contrats `kb_store_core` et replay instruction-level + +Validation du jalon de contrats storage : `kb_store_core` expose les types communs backend-agnostiques pour pagination, healthcheck, migrations, erreurs de stockage, DTOs applicatifs, entities proches SQL et traits repositories sans implémentation PostgreSQL. Les contrats couvrent le raw RPC et WebSocket, le cycle de vie raw `Full` / `Compacted` / `Archived` / `Purged`, les états de traitement raw, la signature optionnelle des notifications WebSocket, la déduplication par `notification_key`, le lien futur optionnel notification vers transaction canonique, ainsi que le replay par instruction. Le replay est défini comme un scheduling au niveau instruction, mais le décodage reçoit un contexte extrait complet via `CoreInstructionReplayInput` : instruction ciblée, account keys, logs, balance changes et contexte transactionnel minimal. Les repositories restent abstraits et ne créent encore ni pool PostgreSQL, ni migration SQL réelle. Validation locale effectuée : `cargo test -p kb_store_core` avec 28 tests passés et `cargo clippy --all-targets` sans avertissement. Les validations précédentes du jalon ont également confirmé `kb_store_pg` sans test actif, `kb_config` avec 35 tests passés et `kb_app_demo` avec 28 tests passés. + +## 0.2.2 — infrastructure PostgreSQL minimale + +Validation du jalon d'infrastructure PostgreSQL : `kb_store_pg` expose maintenant une première implémentation réelle avec options de connexion, pool `sqlx::PgPool`, construction depuis la configuration active, masquage du DSN pour les diagnostics, healthcheck `SELECT 1`, lecture du schema courant, lecture de la version PostgreSQL et snapshot de migrations non destructif. La stratégie de migrations reste volontairement cadrée sans créer les tables Solana lourdes, qui sont reportées à `0.2.3` pour le raw store minimal. Les validateurs de noms de tables imposent la convention `kb_sol__` et refusent les schemas explicites. Validation locale effectuée : `cargo test -p kb_store_pg` avec 11 tests passés, test PostgreSQL réel optionnel `optional_postgres_healthcheck_from_env` validé avec `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana`, `cargo test -p kb_store_core` avec 28 tests passés, `cargo test -p kb_config` avec 35 tests passés, `cargo test -p kb_app_demo` avec 28 tests passés et `cargo clippy --all-targets` sans avertissement après remplacement local de l'exception `allow` par `expect` sur l'incompatibilité connue `async_trait` / `clippy::implicit_return`. + +## 0.2.3 — raw store Solana minimal + +Validation du jalon raw store minimal : `kb_store_pg` crée et initialise les deux premières tables Solana réelles, `kb_sol_raw_rpc_transactions` et `kb_sol_raw_ws_notifications`, sans créer de schema PostgreSQL applicatif explicite. Le raw RPC est inséré de façon idempotente par signature, les notifications WebSocket sont dédupliquées par `notification_key`, la signature des notifications reste optionnelle et le lien vers la transaction RPC canonique est préparé pour les étapes suivantes. Les tables conservent les champs de cycle de vie `retention_state`, `processing_state`, `processed_at` et `processing_reason`, avec `raw_json` nullable côté SQL afin de permettre les futures politiques `compacted`, `archived` et `purged` sans migration cassante. Les requêtes et l'implémentation PostgreSQL de `RawTransactionStore` couvrent les insertions, lookup de signature, lookup de notification et marquage lifecycle. Validation locale effectuée : `cargo test -p kb_store_pg` avec 21 tests passés, incluant le test PostgreSQL réel optionnel `optional_postgres_raw_store_roundtrip_from_env` avec `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana`, `cargo test -p kb_store_core` avec 28 tests passés, `cargo test -p kb_config` avec 35 tests passés, `cargo test -p kb_app_demo` avec 28 tests passés et `cargo clippy --all-targets` sans avertissement. Une correction locale du test raw PostgreSQL a été intégrée pour appeler directement `test_signature()` depuis le module de test. + +## 0.2.4 — core Solana normalisé minimal + +Validation du jalon core store minimal : `kb_store_pg` crée et initialise maintenant les tables core `kb_sol_core_transactions`, `kb_sol_core_account_keys`, `kb_sol_core_instructions`, `kb_sol_core_inner_instructions`, `kb_sol_core_logs` et `kb_sol_core_balance_changes`, en complément du raw store `0.2.3`, sans schema PostgreSQL applicatif explicite. Les migrations et l'initializer utilisent des noms SQL préfixés et explicites pour les contraintes et index : `pk_`, `fk_`, `ck_`, `ux_` et `ix_`. L'initialisation raw/core est sérialisée par un verrou PostgreSQL `pg_advisory_xact_lock` afin d'éviter les courses concurrentes pendant les tests ou les démarrages parallèles. `CoreTransactionStore` dispose de l'implémentation PostgreSQL minimale pour insérer les transactions core, account keys, instructions, inner instructions, logs et deltas de balances, puis reconstruire `CoreInstructionReplayInput` pour les futurs décodeurs. Le script de maintenance `kb_store_pg/maintenance/drop_raw_core_store.sql` permet de réinitialiser explicitement les tables raw/core locales dans l'ordre inverse des dépendances. Validation locale effectuée : `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana cargo test -p kb_store_pg -- --nocapture` avec 30 tests passés, création vérifiée des 8 tables dans PostgreSQL, réinitialisation validée via le script de maintenance, puis `cargo clippy --all-targets` sans avertissement. Le worker d'extraction raw RPC vers core, les observations et le ledger ops sont conservés pour les jalons suivants. + +## 0.2.5 — diagnostics SQL PostgreSQL dans `kb_app_demo` + +Validation du jalon diagnostics SQL : `kb_app_demo` ajoute les fenêtres `demo_sql_diag`, `demo_sql_pg_raw` et `demo_sql_pg_core` pour inspecter en lecture seule l’état PostgreSQL courant. Le démarrage Tauri tente l’initialisation raw/core lorsque `database.postgres.auto_initialize_schema` est actif, journalise les tables créées ou déjà présentes via le splashscreen et `tracing`, puis les fenêtres exposent le profil actif, le backend, le DSN masqué, le schema courant, le healthcheck, l’état migrations et les statistiques de tables raw/core. Les payloads JSON des trois fenêtres utilisent le viewer interactif déjà utilisé par `demo_config`, sans ajouter de nouvelle dépendance npm. Le jalon ne lance ni extracteur raw RPC vers core, ni décodage, ni matérialisation ; ces traitements restent reportés à `0.3.x`. Validation locale effectuée : `cargo test -p kb_store_pg` avec 30 tests passés, `cargo test -p kb_config` avec 35 tests passés, `cargo test -p kb_app_demo` avec 32 tests passés, `cargo clippy --all-targets` sans avertissement, et validation visuelle des trois fenêtres via `cargo tauri dev -c kb_app_demo/tauri.conf.json`. + +## 0.3.0 — cadrage Agave local et sources temps réel + +Validation du jalon de cadrage `0.3.x` : la phase ingestion/backfill/extraction raw vers core est reportée à `0.4.x`, tandis que `0.3.x` devient une phase d’expérimentation Agave local non votant et de stratégie live source. Le jalon documente le profil manuel `agave_local_research`, la commande de départ `agave-validator`, les probes RPC/WS attendus, les mesures ledger/accounts/CPU/RAM/réseau/lag, les règles de fallback vers les providers externes et les critères de décision avant `0.4.x`. Le profil Agave reste désactivé par défaut afin de préserver `mainnet_research` comme base stable, et aucune ingestion de production, aucun backfill historique, aucun décodage DEX, aucune matérialisation et aucune opération de trading ne sont introduits dans ce jalon. Validation locale du delta `0.3.0-pre.001` confirmée par l’utilisateur avant mise à jour du changelog. + +## 0.3.1 — transaction canonique et observations d’acquisition + +Validation du jalon de stockage canonique : `kb_sol_raw_transactions` conserve désormais une transaction source-indépendante par signature avec `canonical_json`, `canonical_json_hash` et `canonical_format_version`, tandis que `kb_sol_obs_transaction_observations` conserve uniquement les informations de provenance, méthode, session, timestamps, taille, statut et erreur sans dupliquer le payload complet. La lignée core utilise `raw_transaction_id`; les DTOs, entities, repositories, requêtes et diagnostics Tauri ont été alignés. La migration réelle depuis le schéma `0.2.x` a été contrôlée sur PostgreSQL 17.10 : les anciennes tables `kb_sol_raw_rpc_transactions` et `kb_sol_raw_ws_notifications` sont absentes, les tables canoniques sont présentes et les colonnes actives portent les nouveaux noms. Après cette validation, les fichiers SQL ont été consolidés en une baseline propre ne contenant que `0001_canonical_transaction_store.sql` et `0002_core_store.sql`; les fichiers SQL actifs n’embarquent plus les anciennes définitions de tables. Validation locale effectuée : `cargo test -p kb_app_demo` avec 32 tests passés, `cargo test -p kb_store_core` avec 30 tests passés, `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana cargo test -p kb_store_pg -- --nocapture` avec 32 tests passés, `cargo clippy --all-targets` sans avertissement, démarrage Tauri validé et diagnostics des huit tables raw/obs/core confirmés. Les fixtures PostgreSQL de test ont été nettoyées manuellement après validation. + +## 0.3.2 — contrat canonique et adaptateur HTTP + +Validation du jalon transactionnel canonique : `kb_model` expose désormais un modèle source-indépendant couvrant les transactions legacy et version 0, le message, les comptes statiques et chargés par Address Lookup Tables, les instructions externes et internes, les logs, le statut et les erreurs, les frais, compute units et cost units, les balances SOL et SPL/Token-2022, les rewards, les return data et le block time lorsqu’ils sont disponibles. Les signatures et clés publiques restent en base58, les données d’instruction sont normalisées en base64, les montants SPL bruts sont canonicalisés sans perte et la sérialisation déterministe trie récursivement les objets JSON avant calcul du hash SHA-256. `kb_rpc` fournit l’adaptateur `getTransaction` JSON-RPC et l’interface commune préparant les futurs adaptateurs Helius WebSocket et Yellowstone gRPC dans la crate existante, tandis que `kb_store_core::RawTransactionInsert::from_canonical` relie le modèle au store canonique. Les fixtures offline couvrent legacy/v0, succès/échec, ALT, CPI, SPL Token et Token-2022, et les formes fournisseur équivalentes produisent le même hash. Validation locale effectuée : `cargo test -p kb_model` avec 23 tests passés, `cargo test -p kb_rpc` avec 48 tests passés, `cargo test -p kb_store_core` avec 31 tests passés, `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana cargo test -p kb_store_pg -- --nocapture` avec 32 tests passés, puis `cargo clippy --all-targets` sans avertissement après ajout des retours explicites imposés par `clippy::implicit_return`. Les fixtures PostgreSQL du roundtrip ont été nettoyées manuellement après validation. + +## 0.3.3 — backfill HTTP gratuit et `demo_backfill` + +Validation du jalon de backfill HTTP : `kb_rpc` expose désormais `getSignaturesForAddress` avec pagination `before` / `until`, tandis que `getTransaction` alimente le modèle canonique sans conserver durablement le payload fournisseur. `kb_pipeline` orchestre les campagnes de signatures explicites et les recherches avant ou après une signature pour un programme, un token ou un pool, avec déduplication, limites de pages, reprise exportable, concurrence, temporisation, retries bornés, arrêt coopératif et journalisation détaillée. Le parcours `after` utilise la borne RPC `until`, des pages pouvant atteindre 1 000 signatures et une sélection bornée en mémoire afin de ne conserver que les `X` signatures directement postérieures à l’ancre dans l’historique de l’adresse. `kb_app_demo` ajoute la fenêtre `demo_backfill`, organisée en accordéons Bootstrap 5, avec paramètres communs, textarea de signatures, formulaires programme/token/pool, journal, résumé et commande d’arrêt. Chaque transaction complète est insérée une seule fois dans `kb_sol_raw_transactions`; chaque tentative d’acquisition produit une observation légère dans `kb_sol_obs_transaction_observations`, tandis que les résultats `missing` et `failed` ne créent aucun faux payload canonique. + +La correction finale de l’arrêt coopératif remplace la création globale des futures par une file bornée à la concurrence effective, distingue les candidats sélectionnés, démarrés, terminés, annulés et non démarrés, et calcule `resume_before_signature` depuis le dernier préfixe contigu réellement terminé. Une campagne réelle de 500 candidats a validé l’arrêt avec 7 candidats démarrés, 3 terminés, 4 annulés et 493 non démarrés, sans journalisation artificielle des candidats restants. Validation locale effectuée : `cargo test -p kb_rpc` avec 54 tests passés, `cargo test -p kb_pipeline` avec 10 tests passés, `cargo test -p kb_store_core` avec 31 tests passés, `cargo test -p kb_store_pg` avec 32 tests passés, `cargo test -p kb_app_demo` avec 38 tests passés et `cargo clippy --all-targets` sans avertissement. Les campagnes Tauri réelles ont validé les signatures explicites, programme avant/après, token avant/après et pool avant/après ; le cas `program_after` a parcouru 5 516 signatures indexées avant de sélectionner les 5 signatures les plus proches de l’ancre. Les diagnostics PostgreSQL ont confirmé 62 transactions canoniques et 62 observations, sans alimentation prématurée des tables core, dont l’extraction reste prévue pour `0.3.4`. + +## 0.3.4 — extraction canonique vers core et clôture de la fondation `0.3.x` + +Validation du jalon d’extraction structurelle : `kb_pipeline` transforme les transactions canoniques en graphes core atomiques comprenant transactions, account keys statiques et ALT, instructions outer et inner à chemins stables, logs reliés prudemment et deltas SOL/SPL Token/Token-2022. `kb_store_core` et `kb_store_pg` exposent et implémentent les contrats de persistance, le ledger versionné `kb_sol_ops_processing_ledger`, le skip par version/hash, le force replay et le rollback complet. `kb_app_demo` ajoute `demo_core_extraction` et le navigateur read-only `demo_sql_replay_candidates` avec filtres, sélection, copie et exports CSV sous `data/exports_csv/`; `kb_program_ids` fournit un registre énumérable de 182 programmes. Les transactions Solana échouées restent extractibles et seront décodables comme observations d’intention ou d’échec, sans être matérialisées automatiquement comme mutations réussies. Validation réelle effectuée sur PostgreSQL 17.10 : 70 transactions extraites sans erreur, sept contrôles d’intégrité sans anomalie, replay normal de 14 signatures entièrement ignoré, force replay des mêmes 14 signatures sans duplication, second replay entièrement ignoré, cardinalité finale de 70 transactions core et ledger final de 70 succès pour 84 tentatives. Validation locale finale : `cargo test -p kb_pipeline` avec 24 tests passés, `KB_POSTGRES_TEST_URL=... cargo test -p kb_store_pg -- --nocapture` avec 39 tests passés dont le rollback/replay corrigé, `cargo test -p kb_app_demo` avec 60 tests passés, `cargo test -p kb_program_ids` avec 2 tests passés et `cargo clippy --all-targets` sans avertissement. L’ancien jalon `0.3.5` est absorbé dans cette version ; la suite active devient `0.4.0` pour l’infrastructure commune de décodage et de matérialisation. + +## 0.4.0 — infrastructure commune de décodage et de matérialisation + +Validation du socle commun de replay contextualisé : `kb_decoder_api` et `kb_materializer_api` travaillent sur les inputs core complets, `kb_pipeline` sélectionne uniquement les programmes compatibles avec les processors activés, effectue un dispatch déterministe par programme et chemin d’instruction, persiste atomiquement couverture, ledger, événements décodés et sorties matérialisées, et applique le skip par processor/version/hash. Le force replay est autorisé uniquement avec des signatures explicites ou avec l’autorisation opérateur « toutes les signatures » bornée par les filtres et la limite. La matérialisation est refusée lorsqu’aucun matérialiseur n’est enregistré. Le classifieur `solana_native_classifier` déclare les 18 surfaces natives/runtime/loaders/précompiles connues sans produire de faux décodage. + +`kb_app_demo` ajoute `demo_decode_replay`, ses diagnostics decode/ledger/couverture et les contrôles de replay normal, force replay ciblé, replay global explicitement autorisé, concurrence bornée et arrêt coopératif. Chaque campagne possède un `campaign_id` propagé par spans jusqu’au pipeline, au store PostgreSQL et au décodeur. Les migrations raw/core/decode sont appliquées une seule fois au démarrage Tauri ; les commandes de démo réutilisent ensuite le schéma initialisé. Les validations frontend du backfill bloquent localement les requêtes incomplètes sans produire de faux événements backend `ERROR`. + +Validation locale finale : `cargo test -p kb_config` avec 36 tests passés, `cargo test -p kb_pipeline` avec 41 tests passés, `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana_test cargo test -p kb_store_pg -- --nocapture` avec 44 tests passés, `cargo test -p kb_app_demo` avec 75 tests passés et `cargo clippy --all-targets` sans avertissement. Les campagnes Tauri réelles ont validé neuf skips version/hash, neuf force replays ciblés, un second skip cohérent, un replay global strictement limité à cinq inputs, l’annulation puis le redémarrage d’une campagne, et la corrélation complète des traces. Le correctif final d’annulation garantit `started + not_started = selected` et `completed = started`; une campagne réelle a terminé avec 382 inputs sélectionnés, 45 démarrés/terminés, 337 non démarrés, 45 dispatchs `unsupported`, aucun unmatched, aucun échec et aucun événement dans `errors.jsonl`. La couverture PostgreSQL confirme 18 déclarations natives, 382 observations Compute Budget reconnues sans erreur et 94 observations System reconnues sans erreur. `0.4.1` devient le jalon actif pour le décodage maximal des programmes Solana natifs, loaders et précompiles. + +## 0.4.1 — programmes Solana natifs, loaders, précompiles et matérialisations natives + +Validation du jalon `0.4.1` : `kb_decoder_solana_core` couvre désormais les programmes Solana natifs/runtime, historiques, loaders, précompiles et surfaces ZK explicitement incluses dans le périmètre. Le registre exécutable natif contient 18 surfaces après reclassification de `StakeConfig11111111111111111111111111111111` comme compte historique non exécutable et ajout du stateless Slashing Program `S1ashing11111111111111111111111111111111111` observé dans Agave `v4.1.1` derrière feature gate. Les surfaces décodées incluent System, Vote, Stake, Config, Compute Budget, Address Lookup Table, Feature, ZK ElGamal Proof, l’ancien ZK Token Proof historique/no-op, les loaders Native/BPF v1/v2/Upgradeable/v4, Ed25519, secp256k1, secp256r1 et Slashing. SPL Memo, SPL Token, Token-2022, Stake Pool, Single Pool, Account Compression, Noop et SPL Name Service restent réservés aux jalons suivants. + +Le jalon a étendu le contrat de replay instructionnel avec les instructions outer ordonnées afin de résoudre les références inter-instructions des précompiles et du profil Compute Budget. Les décodeurs bornent les offsets, tailles, compteurs, références d’instructions, comptes requis et payloads suffixes sans recalcul cryptographique pour les signatures ou preuves ZK. L’ancien `solana-zk-token-sdk` déprécié a été retiré ; le miroir ZK Token historique repose maintenant sur une table wire locale auditée, sans dépendance directe à `bincode`. + +Les matérialisations natives sont clôturées au niveau instructionnel stable. `kb_materializer_lifecycle` projette Address Lookup Table, lifecycle des loaders, révocation Feature, durable nonce, comptes System create/allocate, contextes ZK ElGamal et rapports Slashing. `kb_materializer_admin` projette assignations System, écritures Config opaques et changements d’autorité Loader. `kb_materializer_compliance_audit` projette écritures/copies de bytecode Loader et profils Compute Budget transactionnels agrégés. `kb_materializer_staking` projette les intentions commitées Stake/Vote : comptes, autorités, lockup, délégation, désactivation, split, merge, retraits, move stake/lamports, identité Vote, commission, vote state et dépôt de rewards. Les sorties ne prétendent pas reconstruire les snapshots finaux de comptes, les sysvars historiques, les crédits Vote cumulés, l’activation Stake par epoch, les unités compute consommées ou les frais finaux runtime. + +`kb_app_demo` enregistre les matérialiseurs natifs `solana_native_lifecycle`, `solana_native_admin`, `solana_native_compliance_audit` et `solana_native_staking`. Les profils de logging montent à 53 routes avec fichiers dédiés pour les crates opérationnelles ajoutées. `demo_backfill` accepte maintenant les modes `program_latest`, `token_latest` et `pool_latest` : une recherche « avant, plus anciennes » sans ancre commence sur les signatures les plus récentes, tandis que la direction « après, plus récentes » conserve une ancre obligatoire. + +Les validations utilisateur finales confirment : `kb_program_ids` 5 tests, `kb_decoder_solana_core` 114 tests, `kb_materializer_lifecycle` 16 tests, `kb_materializer_admin` 7 tests, `kb_materializer_compliance_audit` 7 tests, `kb_materializer_staking` 8 tests, `kb_config` 36 tests, `kb_pipeline` 45 tests, `kb_store_pg` 45 tests avec PostgreSQL réel, `kb_app_demo` 78 tests et `cargo clippy --all-targets` sans avertissement. Le correctif de sérialisation test-only des tests PostgreSQL réels élimine l’interblocage intermittent observé lors des tests optionnels concurrents. + +Les campagnes mainnet ciblées validées couvrent notamment : System 817 inputs décodés avec 258 sorties et 52 refus attendus sur transactions échouées ; Config 96/96 avec 96 sorties admin ; Stake 448/448 avec 447 sorties et un refus attendu ; Vote 600/600 avec 600 sorties ; Compute Budget 2 746/2 746 avec 1 603 profils transactionnels matérialisés ; ZK ElGamal 151/151 avec 139 sorties de contexte ; ALT 104/104 avec 104 sorties lifecycle ; Slashing sans corpus observable, validé par sources officielles et fixtures synthétiques. Tous ces replays se terminent avec `unmatched = 0`, `failedInputs = 0`, `unsupported = 0` et journaux `error.jsonl` vides dans les archives fournies. + +La suite active devient `0.4.2` : infrastructure d’exécution et `kb_executor_solana_core`, avec séparation stricte entre plan, simulation, signature, envoi et validation post-exécution. Les exécuteurs SPL suivront ensuite la politique décodeur maximal → matérialisateurs → exécuteur limité par opération. + +## 0.4.2 — infrastructure d’exécution native et RPC Solana standard + +Validation du jalon d’exécution native. `kb_execution_api`, `kb_execution_safety`, `kb_execution_solana`, `kb_wallet`, `kb_executor_solana_core`, `kb_rpc` et `kb_pipeline` séparent explicitement intent, plan, politique, lecture stateful, simulation, signature, envoi, confirmation et validation post-exécution. Le wallet temporaire de laboratoire reste confiné à Localnet/Devnet ; dry-run et simulation sont obligatoires par défaut, les plafonds de dépense/frais sont vérifiés et Mainnet reste désactivé sans politique et confirmation explicites. + +`kb_executor_solana_core` couvre 18 surfaces natives avec 109 opérations appelables : System, Compute Budget, Address Lookup Table, précompiles Ed25519/secp256k1/secp256r1, Config, Feature, Slashing, ZK ElGamal, Stake, Vote, Loader v3 et Loader v4. BPF Loader v1/v2, Native Loader et l’ancien ZK Token Proof sont classés sans builder client ; Stake `Redelegate` reste historique `decode-only`. Chaque builder est comparé à un encodeur officiel ou à un layout wire audité. La matrice `docs/NATIVE_SOLANA_EXECUTION_MATRIX.json` est vérifiée par un test Rust contre les 109 codes compilés. `kb_decoder_solana_core` conserve l’égalité exacte avec les 18 Program IDs natifs et 121 déclarations de couverture. + +Le préflight stateful de `kb_pipeline` inspecte ALT, Config, Feature, Slashing et les contextes ZK ElGamal sur Localnet/Devnet et retourne `NotRequired`, `Ready` ou `Blocked`. Les opérations administratives non validées réellement par un scénario cluster restent derrière ce garde-fou, hors de l’UI et sans activation Mainnet. Le parcours Devnet représentatif System transfer a été validé avec un wallet persistant préfinancé et 1 000 000 lamports : simulation exacte, signature, envoi, confirmation, `getTransaction`, insertion canonique, extraction core et decode replay ciblé ont réussi. + +`kb_rpc` expose des contrats propres et configurables pour les 52 méthodes HTTP standard et les neuf paires WebSocket standard. Les options sont composables sans exposer les DTO Agave. `WsSession` gère multiplexage, unsubscribe, timeout, reconnexion bornée et réabonnement. `blockSubscribe`, `slotsUpdatesSubscribe` et `voteSubscribe` sont activables par endpoint ; un rejet serveur indiquant une méthode absente ou non activée désactive automatiquement la capacité pour la session. `kb_app_demo` ne contient plus la boucle de transport WebSocket. + +La frontière Tauri/TS-rs a été corrigée pour ne plus transmettre de `bigint` dans les payloads JSON actifs. Les identifiants WebSocket sont contrôlés contre `Number.isSafeInteger` et les limites du navigateur SQL sont validées avant l’invocation backend. Les logs réels confirment les souscriptions/désabonnements WebSocket et le garde-fou de changement d’endpoint avec session active. + +Validation finale fournie par l’utilisateur : `kb_program_ids` 5 tests, `kb_decoder_solana_core` 116, `kb_executor_solana_core` 88, `kb_execution_api` 22, `kb_execution_safety` 15, `kb_execution_solana` 12, `kb_rpc` 113, `kb_pipeline` 56, `kb_config` 41, `kb_app_demo` 88 et `kb_store_pg` 45 avec PostgreSQL réel. Le test Devnet opt-in et le replay post-exécution sont réussis, `cargo clippy --all-targets` est propre et `cargo tauri dev` démarre avec Vite et initialise correctement PostgreSQL. Aucun jalon suivant n’est ouvert par cette clôture. + +## 0.4.3 — SPL Memo v1, v3 et v4 + +Validation complète de la surface SPL Memo. `kb_decoder_spl_memo` reconnaît exactement les Program IDs v1, v3 et v4, décode le payload UTF-8 brut, sa longueur et son SHA-256, conserve les comptes ordonnés et leurs flags, distingue les règles historiques de signataires, les chemins outer/inner, les transactions réussies ou échouées et les tentatives invalides sans produire de faux état commité. La matrice machine-readable `docs/SPL_MEMO_MATRIX.json` relie les contrats officiels, les fixtures synthétiques et le corpus Mainnet réel des trois générations. + +`kb_materializer_transaction_annotations` projette uniquement les Memo réussis et commités dans la famille `transaction_annotation`, avec texte borné, génération, Program ID, signataires vérifiés, provenance et clé d’idempotence. Les comptes complets et diagnostics d’audit restent dans le decode. La persistance réutilise `kb_sol_mat_events` et le ledger commun sans nouvelle table ; `demo_decode_replay` expose un journal typé et borné filtrable par signature. + +`kb_executor_spl_memo` fournit les intents typés v1/v3/v4 et construit les trois générations avec `spl-memo-interface 2.1.0`, payload exact et signataires readonly ordonnés. La bibliothèque reste universelle sur les clusters ; simulation obligatoire, dry-run par défaut, plafond de frais, autorisation opérateur et politiques Mainnet appartiennent à l’infrastructure commune. Le parcours Memo v4 Devnet réutilise wallet, RPC, safety, transaction Solana, confirmation, hydratation canonique, extraction core et decode replay. Trois transactions réelles ont chacune produit une annotation et un second replay idempotent `skipped`. + +La clôture utilisateur confirme : `kb_program_ids` 5 tests, `kb_decoder_spl_memo` 9, `kb_materializer_transaction_annotations` 7, `kb_executor_spl_memo` 15, `kb_execution_api` 22, `kb_execution_safety` 15, `kb_execution_solana` 12, `kb_pipeline` 61, `kb_rpc` 113, `kb_app_demo` 96 et `kb_store_pg` 46 avec PostgreSQL réel. Le test opt-in Devnet avec envoi est réussi et `cargo clippy --all-targets` est propre. La suite active devient `0.4.4 — SPL Token` selon `prompts/023_v0_4_4_spl_token.md`; l’acquisition historique différée reste hors ROADMAP actif. + +## 0.4.4 — SPL Token classique + +Validation complète du programme SPL Token classique `Tokenkeg...`. `kb_decoder_spl_token` +reconnaît exactement les 28 tags publiés par `spl-token-interface 3.0.0`, conserve le wire borné, +les comptes ordonnés, outer/inner paths, autorités simples ou multisig, montants bruts, decimals, +transactions échouées non commitées et diagnostics sans inventer mint ni état final. `Batch` +conserve son parent et ses enfants déterministes sans accepter les batchs imbriqués. La matrice +`docs/SPL_TOKEN_MATRIX.json` relie interface, processor, builders, corpus et preuves cluster. + +Les projections stables produisent des événements instructionnels `token_account`, `admin` et +`risk`, sans fee classique fictive, sans OHLC et sans snapshot final reconstruit. Le replay Mainnet +de 370 instructions a produit 371 sorties, huit refus correspondant exactement à huit transactions +échouées, puis un second replay idempotent sans nouvelle sortie. Le journal Tauri expose des filtres +bornés par signature, mint, compte, famille et opération, avec montants JSON sans perte. + +`kb_executor_spl_token` expose 24 opérations typées courantes ou récentes et laisse les quatre +initialisations Rent obsolètes en decode-only. Le préflight stateful Localnet/Devnet valide layouts, +relations mint/comptes, soldes, native reserve, délégations, autorités, multisig et rent hors du +décodeur et de l'exécuteur. Le parcours Devnet `TransferChecked` a réussi simulation, envoi, +confirmation, hydratation, extraction, replay, matérialisation et second replay idempotent. Un +lifecycle contrôlé sans ATA a ensuite initialisé mint et comptes, minté, transféré, approuvé, +révoqué, brûlé puis fermé les comptes avec récupération sûre après interruption RPC. + +Les tags récents `Batch` et `UnwrapLamports` ont été simulés avec succès sur le programme classique +Devnet, sans signature ni envoi, respectivement en 270 et 140 compute units. La clôture Tauri charge +77 routes de logs, réussit le préflight et la simulation `TransferChecked`, bloque l'envoi sans +confirmation, affiche la matérialisation commitée exacte et simule Memo v4 sans erreur runtime. + +La régression finale communiquée confirme notamment : `kb_program_ids` 5 tests, +`kb_decoder_spl_token` 8, `kb_executor_spl_token` 15, `kb_decoder_spl_memo` 9, +`kb_materializer_transaction_annotations` 7, `kb_execution_api` 22, `kb_execution_safety` 15, +`kb_execution_solana` 12, `kb_pipeline` 81, `kb_rpc` 113, `kb_config` 41, `kb_app_demo` 102 et +`kb_store_pg` 46 avec PostgreSQL réel. `cargo clippy --all-targets` est propre. La suite active +devient `0.4.5 — SPL Associated Token Account` selon +`prompts/024_v0_4_5_spl_associated_token_account.md`. + +## 0.4.5 — SPL Associated Token Account + +Validation complète du programme SPL Associated Token Account +`ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL`. Le décodeur consomme +`spl-associated-token-account-interface 2.0.0`, reconnaît exactement `Create`, +`CreateIdempotent`, `RecoverNested` et la forme historique vide de `Create`, puis conserve comptes +ordonnés, flags, doublons, chemins outer/inner, transactions échouées et diagnostics bornés. Les +adresses observées sont comparées aux PDA canoniques dérivés depuis wallet, Token Program ID et +mint ; une incohérence reste décodable et n'est jamais corrigée silencieusement. + +`kb_materializer_token_accounts` possède exclusivement le lifecycle de l'adresse associée : créée, +créée ou réutilisée idempotemment, ou récupérée depuis une imbrication. `kb_materializer_risk` +possède le constat distinct d'anti-pattern nested récupéré, sans score arbitraire. Les CPI SPL Token +restent propriétaires des initialisations, transferts et fermetures, et les matérialiseurs lifecycle +général, admin et fees ne produisent aucun doublon. Les transactions non commitées ou invalides sont +refusées comme mutations. + +`kb_executor_spl_associated_token_account` expose les trois variantes actuelles pour SPL Token +classique et Token-2022 avec les builders officiels, metas dans l'ordre exact, signataires uniques, +simulation obligatoire, dry-run par défaut et coûts rent/frais plafonnés. Le pipeline stateful +contrôle mints, owners, Program IDs, dérivations, comptes existants, rent, signataires et +postconditions avant et après l'exécution. La fenêtre Tauri Solana existante ajoute création ATA, +dérivation readonly et journal lifecycle borné ; `RecoverNested` reste volontairement hors UI. + +Les scénarios Devnet contrôlés du 16 juillet 2026 ont validé la création et la réutilisation +idempotente d'ATA classiques et Token-2022, puis un `RecoverNested` classique transférant +1 000 000 000 unités brutes vers l'ATA wallet et fermant le nested ATA. Chaque envoi a suivi +simulation, confirmation, hydratation canonique, extraction, decode, matérialisation et second +replay idempotent. L'ATA Token-2022 observé mesure 170 octets et expose `ImmutableOwner`, sans que le +jalon ne commence le décodage général de ses extensions. + +La clôture confirme 5 tests Program IDs, 8 tests décodeur Token classique, 15 tests exécuteur Token, +9 tests Memo, 7 tests annotations, 9 tests matérialiseur token accounts, 6 tests risk, 17 tests +lifecycle, 9 tests admin, 3 tests fees, 10 tests décodeur ATA, 10 tests exécuteur ATA, 22 tests API +d'exécution, 15 tests safety, 12 tests transaction Solana, 90 tests pipeline, 113 tests RPC, 41 tests +configuration, 111 tests démo et 46 tests PostgreSQL réels. `cargo clippy --all-targets` est propre +et `cargo tauri dev -c kb_app_demo/tauri.conf.json` a démarré Vite, Tauri et PostgreSQL. La suite +active devient `0.4.6 — Token-2022, extensions et registre ElGamal` selon +`prompts/025_v0_4_6_spl_token_2022.md`. + +## 0.4.6 — Token-2022, extensions et registre ElGamal + +Validation du jalon Token-2022 et ElGamal Registry avec frontières techniques séparées. Token-2022 couvre les instructions de base, les familles d’extensions identifiables, les états Mint/Account/Multisig et TLV, les metadata/group incorporés, les parcours confidentiels, les matérialisations propriétaires et les exécuteurs simulation-first avec préflights stateful et preuves inline/context-state. Huit opérations publiques Token-2022 ont été confirmées sur Devnet, hydratées, extraites, décodées, matérialisées et rejouées idempotemment. Le corpus Mainnet contient 55 instructions Token-2022 décodées sans échec. +Le registre ElGamal dispose de son décodeur, de sa matrice, de son parser d’état de 64 octets, de sa matérialisation administrative, de ses builders `CreateRegistry`/`UpdateRegistry`, de son PDA, de ses préflights et de son orchestration de preuves. La validation réseau publique reste indisponible sur Devnet/Mainnet au 20 juillet 2026 ; la validation synthétique/offline est conservée sans inventer de preuve cluster. +La validation Mainnet a corrigé le wire `Write` du loader immuable : longueur `u32` et données à l’offset 12, supprimant 68 diagnostics `loader_vector_length_mismatch`. Les 76 rejets restants sont tous issus de transactions Solana échouées et restent explicitement fail-closed ; quatre tags Loader v4 inconnus sont classés `Unsupported`. Un timeout ponctuel du pool PostgreSQL a été réparé par replay ciblé. +Le replay de `kb_app_demo` ajoute `incomplete_signatures`, qui sélectionne seulement les signatures contenant une instruction `pending`, `failed`, `replay_requested` ou `unsupported`, limite d’abord les signatures puis étend leurs instructions compatibles sans force replay global. Deux campagnes PostgreSQL réelles ont produit le même résultat : 81 entrées, 1 décodée, 4 unsupported, 76 failed, aucun unmatched et aucun doublon decode/mat. Validation finale : `kb_store_core` 41/41, `kb_store_pg` 46/46 avec PostgreSQL réel, `kb_pipeline` 124/124, `kb_app_demo` 118/118 et `cargo clippy --all-targets` propre. La suite active devient `0.4.7 — Metaplex Token Metadata` selon `prompts/026_v0_4_7_metaplex_token_metadata.md`. Anchor n’étant pas requis par les programmes SPL de `0.4.x`, son infrastructure commune est planifiée en `0.4.18`, immédiatement avant Meteora. + diff --git a/migration/khadhroony-bot2-reference/Cargo.toml b/migration/khadhroony-bot2-reference/Cargo.toml new file mode 100644 index 0000000..db1d14a --- /dev/null +++ b/migration/khadhroony-bot2-reference/Cargo.toml @@ -0,0 +1,388 @@ +# file: Cargo.toml +# version: 41 + +[workspace] +resolver = "3" +members = [ + # Foundation crates + "kb_core", + "kb_model", + "kb_program_ids", + "kb_logging", + "kb_config", + "kb_rpc", + "kb_wallet", + # Store crates + "kb_store_core", + "kb_store_pg", + "kb_store_sqlite", + # API, execution and pipeline crates + "kb_decoder_api", + "kb_materializer_api", + "kb_execution_api", + "kb_execution_safety", + "kb_execution_solana", + "kb_pipeline", + # Live demo and operator crates + "kb_app_demo", + "kb_app_cli", + "kb_worker", + # Anchor decoding toolbox + "kb_decoder_anchor", + # Decoder and executor crates ordered by development surface + "kb_decoder_solana_core", + "kb_executor_solana_core", + "kb_decoder_spl_token", + "kb_executor_spl_token", + "kb_decoder_spl_token_2022", + "kb_executor_spl_token_2022", + "kb_decoder_spl_elgamal_registry", + "kb_executor_spl_elgamal_registry", + "kb_decoder_spl_associated_token_account", + "kb_decoder_metadata_metaplex_token_metadata", + "kb_executor_metadata_metaplex_token_metadata", + "kb_executor_spl_associated_token_account", + "kb_decoder_spl_stake_pool", + "kb_executor_spl_stake_pool", + "kb_decoder_spl_memo", + "kb_executor_spl_memo", + "kb_decoder_spl_single_pool", + "kb_executor_spl_single_pool", + "kb_decoder_spl_account_compression", + "kb_executor_spl_account_compression", + "kb_decoder_spl_noop", + "kb_executor_spl_noop", + "kb_decoder_metadata_spl_name_service", + "kb_executor_metadata_spl_name_service", + "kb_decoder_amm_pump_swap", + "kb_executor_amm_pump_swap", + "kb_decoder_launchpad_pump_fun", + "kb_executor_launchpad_pump_fun", + "kb_decoder_admin_pump_fees", + "kb_executor_admin_pump_fees", + "kb_decoder_fees_bags_fee_share_v1", + "kb_executor_fees_bags_fee_share_v1", + "kb_decoder_fees_bags_fee_share_v2", + "kb_executor_fees_bags_fee_share_v2", + "kb_decoder_launchpad_pump_pumpup_ai", + "kb_executor_launchpad_pump_pumpup_ai", + "kb_decoder_launchpad_meteora_dbc", + "kb_executor_launchpad_meteora_dbc", + "kb_decoder_dlmm_meteora", + "kb_executor_dlmm_meteora", + "kb_decoder_amm_meteora_damm_v1", + "kb_executor_amm_meteora_damm_v1", + "kb_decoder_amm_meteora_damm_v2", + "kb_executor_amm_meteora_damm_v2", + "kb_decoder_vault_meteora", + "kb_executor_vault_meteora", + "kb_decoder_amm_raydium_lp_v4", + "kb_executor_amm_raydium_lp_v4", + "kb_decoder_cpmm_raydium", + "kb_executor_cpmm_raydium", + "kb_decoder_clmm_raydium", + "kb_executor_clmm_raydium", + "kb_decoder_launchpad_raydium_launchlab", + "kb_executor_launchpad_raydium_launchlab", + "kb_decoder_lock_raydium_lp", + "kb_executor_lock_raydium_lp", + "kb_decoder_clmm_orca_whirlpool", + "kb_executor_clmm_orca_whirlpool", + "kb_decoder_launchpad_orca_wavebreak", + "kb_executor_launchpad_orca_wavebreak", + "kb_decoder_router_dflow_aggregator_v4", + "kb_executor_router_dflow_aggregator_v4", + "kb_decoder_router_jupiter_aggregator_v4", + "kb_executor_router_jupiter_aggregator_v4", + "kb_decoder_router_jupiter_aggregator_v6", + "kb_executor_router_jupiter_aggregator_v6", + "kb_decoder_router_jupiter_dca", + "kb_executor_router_jupiter_dca", + "kb_decoder_router_okx_labs_v1", + "kb_executor_router_okx_labs_v1", + "kb_decoder_router_okx_labs_v2", + "kb_executor_router_okx_labs_v2", + "kb_decoder_orderbook_jupiter_limit_order", + "kb_executor_orderbook_jupiter_limit_order", + "kb_decoder_orderbook_jupiter_limit_order_v2", + "kb_executor_orderbook_jupiter_limit_order_v2", + "kb_decoder_orderbook_openbook_v2", + "kb_executor_orderbook_openbook_v2", + "kb_decoder_amm_aldrin_v1", + "kb_executor_amm_aldrin_v1", + "kb_decoder_amm_aldrin_v2", + "kb_executor_amm_aldrin_v2", + "kb_decoder_amm_alphaq", + "kb_executor_amm_alphaq", + "kb_decoder_amm_believe", + "kb_executor_amm_believe", + "kb_decoder_amm_bonk_swap", + "kb_executor_amm_bonk_swap", + "kb_decoder_amm_fluxbeam", + "kb_executor_amm_fluxbeam", + "kb_decoder_amm_goon_fi", + "kb_executor_amm_goon_fi", + "kb_decoder_amm_goosefx_gamma", + "kb_executor_amm_goosefx_gamma", + "kb_decoder_amm_goosefx_v2", + "kb_executor_amm_goosefx_v2", + "kb_decoder_amm_guac_swap", + "kb_executor_amm_guac_swap", + "kb_decoder_amm_lifinity_swap_v2", + "kb_executor_amm_lifinity_swap_v2", + "kb_decoder_amm_metadao_futarchy_amm", + "kb_executor_amm_metadao_futarchy_amm", + "kb_decoder_amm_metadao_v0_5", + "kb_executor_amm_metadao_v0_5", + "kb_decoder_amm_obric_v2", + "kb_executor_amm_obric_v2", + "kb_decoder_amm_one_dex", + "kb_executor_amm_one_dex", + "kb_decoder_amm_solfi", + "kb_executor_amm_solfi", + "kb_decoder_amm_solfi_v2", + "kb_executor_amm_solfi_v2", + "kb_decoder_amm_vertigo", + "kb_executor_amm_vertigo", + "kb_decoder_amm_virtuals", + "kb_executor_amm_virtuals", + "kb_decoder_amm_woofi", + "kb_executor_amm_woofi", + "kb_decoder_amm_zero_fi", + "kb_executor_amm_zero_fi", + "kb_decoder_amm_zora", + "kb_executor_amm_zora", + "kb_decoder_clmm_byreal", + "kb_executor_clmm_byreal", + "kb_decoder_clmm_fusion", + "kb_executor_clmm_fusion", + "kb_decoder_clmm_pancake_swap", + "kb_executor_clmm_pancake_swap", + "kb_decoder_clmm_stabble", + "kb_executor_clmm_stabble", + "kb_decoder_stable_swap_hylo_exchange", + "kb_executor_stable_swap_hylo_exchange", + "kb_decoder_stable_swap_jupiter_stable", + "kb_executor_stable_swap_jupiter_stable", + "kb_decoder_stable_swap_numeraire", + "kb_executor_stable_swap_numeraire", + "kb_decoder_stable_swap_stabble", + "kb_executor_stable_swap_stabble", + "kb_decoder_weighted_swap_stabble", + "kb_executor_weighted_swap_stabble", + "kb_decoder_launchpad_boop_fun", + "kb_executor_launchpad_boop_fun", + "kb_decoder_launchpad_metadao_ico", + "kb_executor_launchpad_metadao_ico", + "kb_decoder_launchpad_moonit", + "kb_executor_launchpad_moonit", + "kb_decoder_launchpad_printr", + "kb_executor_launchpad_printr", + "kb_decoder_adapter_saber_decimal_wrapper", + "kb_executor_adapter_saber_decimal_wrapper", + "kb_decoder_admin_jupiter_lock", + "kb_executor_admin_jupiter_lock", + "kb_decoder_governance_metadao_bid_wall", + "kb_executor_governance_metadao_bid_wall", + "kb_decoder_bridge_circle_cctp_token_messenger_minter", + "kb_executor_bridge_circle_cctp_token_messenger_minter", + "kb_decoder_bridge_circle_cctp_token_messenger_minter_v2", + "kb_executor_bridge_circle_cctp_token_messenger_minter_v2", + "kb_decoder_bridge_layer_zero_endpoint", + "kb_executor_bridge_layer_zero_endpoint", + "kb_decoder_bridge_layer_zero_executor", + "kb_executor_bridge_layer_zero_executor", + "kb_decoder_rwa_ondo_global_markets", + "kb_executor_rwa_ondo_global_markets", + "kb_decoder_lending_clone", + "kb_executor_lending_clone", + "kb_decoder_lending_jupiter_lend_borrow", + "kb_executor_lending_jupiter_lend_borrow", + "kb_decoder_lending_jupiter_lend_earn", + "kb_executor_lending_jupiter_lend_earn", + "kb_decoder_lending_jupiter_lend_flash_loan", + "kb_executor_lending_jupiter_lend_flash_loan", + "kb_decoder_lending_jupiter_lend_liquidity", + "kb_executor_lending_jupiter_lend_liquidity", + "kb_decoder_lending_kamino", + "kb_executor_lending_kamino", + "kb_decoder_lending_marginfi_v2", + "kb_executor_lending_marginfi_v2", + "kb_decoder_staking_kamino_farm", + "kb_executor_staking_kamino_farm", + "kb_decoder_staking_marinade_finance", + "kb_executor_staking_marinade_finance", + "kb_decoder_staking_solayer", + "kb_executor_staking_solayer", + "kb_decoder_vault_carrot_defi", + "kb_executor_vault_carrot_defi", + "kb_decoder_vault_hylo_stability_pool", + "kb_executor_vault_hylo_stability_pool", + "kb_decoder_vault_kamino", + "kb_executor_vault_kamino", + "kb_decoder_vault_kamino_v2", + "kb_executor_vault_kamino_v2", + "kb_decoder_vault_kamino_yvaults", + "kb_executor_vault_kamino_yvaults", + "kb_decoder_vesting_streamflow", + "kb_executor_vesting_streamflow", + "kb_decoder_nft_metaplex_bubblegum", + "kb_executor_nft_metaplex_bubblegum", + "kb_decoder_treasury_helium_treasury_management", + "kb_executor_treasury_helium_treasury_management", + "kb_decoder_wallet_jupiter_apepro_smart_wallet", + "kb_executor_wallet_jupiter_apepro_smart_wallet", + "kb_decoder_perpetuals_drift_v2", + "kb_executor_perpetuals_drift_v2", + "kb_decoder_perpetuals_jupiter", + "kb_executor_perpetuals_jupiter", + "kb_decoder_perpetuals_zeta", + "kb_executor_perpetuals_zeta", + # Application materializer crates + "kb_materializer_admin", + "kb_materializer_bridge", + "kb_materializer_compliance_audit", + "kb_materializer_fees", + "kb_materializer_governance", + "kb_materializer_lending", + "kb_materializer_lifecycle", + "kb_materializer_liquidity", + "kb_materializer_metadata", + "kb_materializer_nft", + "kb_materializer_oracle", + "kb_materializer_orderbook", + "kb_materializer_perpetuals", + "kb_materializer_pool_state", + "kb_materializer_rewards", + "kb_materializer_risk", + "kb_materializer_routing", + "kb_materializer_staking", + "kb_materializer_transaction_annotations", + "kb_materializer_token_accounts", + "kb_materializer_token_metadata_risk", + "kb_materializer_trades", + "kb_materializer_vault", +] + +[workspace.package] +version = "0.4.7" +edition = "2024" +license = "MIT" +repository = "https://git.sasedev.com/Sasedev/khadhroony-bot2" +authors = ["SinuS von SifriduS "] +publish = false + +[workspace.dependencies] +argon2 = { version = "^0.5", features = ["std", "zeroize"] } +async-trait = { version = "^0.1", features = [] } +base64 = { version = "^0.22", features = [] } +bytemuck = { version = "^1.25", features = ["derive"] } +borsh_0_10 = { package = "borsh", version = "^0.10" } +borsh = { version = "^1.7", features = ["ascii", "bson", "bytes", "default", "derive", "de_strict_order", "borsh-derive", "indexmap", "std", "rc"] } +bs58 = { version = "^0.5", features = ["cb58", "check", "default"] } +chacha20poly1305 = { version = "^0.11", features = ["std", "stream"] } +chrono = { version = "^0.4", features = ["serde"] } +fs2 = { version = "^0.4", features = [] } +futures-util = { version = "^0.3", features = ["default", "futures-sink"] } +jsonschema = { version = "^0.48", features = [] } +mpl-token-metadata = { version = "^5.1", features = ["serde"] } +rand = { version = "^0.10", features = ["chacha", "serde", "std", "sys_rng"] } +reqwest = { version = "^0.13", default-features = false, features = ["charset", "cookies", "deflate", "form", "gzip", "http2", "json", "multipart", "query", "rustls", "socks", "stream", "zstd"] } +rustls = { version = "^0.23", features = ["aws-lc-rs"] } +serde = { version = "^1.0", features = ["derive"] } +serde_json = { version = "^1.0", features = [] } +sha2 = { version = "^0.11", features = ["zeroize"] } +solana-account-decoder-client-types = { version = "^4.1", features = ["zstd"] } +solana-address-lookup-table-interface = { version = "^3.1", features = ["serde", "wincode"] } +solana-compute-budget-interface = { version = "^3.0", features = ["borsh", "serde"] } +solana-config-interface = { version = "^2.0", features = ["serde"] } +solana-ed25519-program = { version = "^3.0", features = [] } +solana-feature-gate-interface = { version = "^4.0", features = ["serde"] } +solana-feature-set-interface = { version = "^4.0", features = [] } +solana-hash = { version = "^4.5", features = ["atomic", "borsh", "copy", "decode", "default", "rand", "sanitize", "serde", "std", "wincode"] } +solana-instruction = { version = "^3.4", features = ["borsh", "default", "serde", "std", "syscalls", "wincode"] } +solana-keypair = { version = "^3.1", features = ["seed-derivable"] } +solana-loader-v2-interface = { version = "^3.0", features = ["serde"] } +solana-loader-v3-interface = { version = "^8.0", features = ["serde", "wincode"] } +solana-loader-v4-interface = { version = "^3.1", features = ["serde"] } +solana-message = { version = "^4.3", features = ["blake3", "default", "serde", "std", "wincode"] } +solana-nonce = { version = "^3.2", features = ["serde", "wincode"] } +solana-pubkey = { version = "^4.2", features = ["alloc", "borsh", "bytemuck", "curve25519", "default", "rand", "serde", "sha2", "std", "wincode"] } +solana-rpc-client-api = { version = "^4.1", features = [] } +solana-sdk = { version = "^4.0", features = ["full"] } +solana-sdk-ids = { version = "^3.1", features = [] } +solana-secp256k1-program = { version = "^3.0", default-features = false, features = ["serde"] } +solana-secp256r1-program = { version = "^3.0", features = [] } +solana-signer = { version = "^3.0", features = ["default", "std"] } +solana-stake-interface = { version = "^4.3", features = ["borsh", "codama", "serde", "sysvar", "wincode"] } +solana-system-interface = { version = "^3.2", features = ["alloc", "serde", "std", "solana-instruction", "wincode"] } +solana-sysvar = { version = "^4.1", features = ["serde", "wincode"] } +solana-transaction = { version = "^4.1", features = ["blake3", "default", "serde", "std", "verify", "wincode"] } +solana-transaction-status-client-types = { version = "^4.1", features = [] } +solana-vote-interface = { version = "^6.0", features = ["serde", "wincode"] } +solana-zk-elgamal-proof-interface = { version = "^0.1", features = [] } +solana-zk-sdk-pod = { version = "^0.1", features = ["serde"] } +spl-associated-token-account-interface = { version = "^2.0", features = ["borsh"] } +spl-elgamal-registry-interface = { version = "^0.2", features = [] } +spl-instruction-padding-interface = { version = "^1.0", features = [] } +spl-memo-interface = { version = "^2.1", features = [] } +spl-nonce-interface = { version = "^0.1", features = [] } +spl-token-confidential-transfer-proof-extraction = { version = "^0.6", features = [] } +spl-transfer-hook-interface = { version = "^2.1", features = [] } +spl-token-metadata-interface = { version = "^1.0", features = ["serde", "serde-traits"] } +spl-token-group-interface = { version = "^0.7", features = [] } +spl-token-interface = { version = "^3.0", features = [] } +spl-token-2022-interface = { version = "^3.1", features = ["serde"] } +sqlx = { version = "^0.9", features = ["bigdecimal", "chrono", "default", "derive", "json", "mysql", "postgres", "runtime-tokio", "sqlite", "tls-rustls", "uuid"] } +tauri = { version = "^2.11", features = ["default", "tray-icon"] } +tauri-build = { version = "^2.6", features = [] } +tauri-plugin-tracing = { version = "^0.3", default-features = false, features = [] } +tempfile = { version = "^3.27", features = [] } +tokio = { version = "^1.52", features = ["full"] } +tokio-stream = { version = "^0.1", features = ["full"] } +tokio-tungstenite = { version = "^0.30", default-features = false, features = ["connect", "handshake", "rustls-tls-webpki-roots", "stream", "url"] } +tracing = { version = "^0.1", features = [] } +tracing-appender = { version = "^0.2", features = [] } +tracing-subscriber = { version = "^0.3", features = ["ansi", "env-filter", "chrono", "serde", "json"] } +wincode = { version = "^0.5", features = ["alloc", "bytes", "derive", "indexmap", "smallvec", "uuid", "uuid-serde-compat"] } +ts-rs = { version = "^12.0", features = ["bigdecimal", "bson", "bytes", "chrono", "indexmap", "ordered-float", "serde_json", "tokio", "url", "uuid"] } +yellowstone-grpc-client = { version = "^13.1", features = [] } +yellowstone-grpc-proto = { version = "^12.5", features = [] } +uuid = { version = "^1.23", features = ["v4", "serde"] } +zeroize = { version = "^1.9", features = ["derive", "serde", "std"] } + +[workspace.lints.rust] +missing_docs = "warn" +unreachable_pub = "deny" +unsafe_code = "forbid" + +[workspace.lints.clippy] +manual_range_patterns = "allow" +unwrap_used = "deny" +expect_used = "deny" +implicit_return = "deny" +needless_return = "allow" +useless_vec = "deny" +question_mark = "deny" +question_mark_used = "deny" +needless_match = "allow" +manual_ok_err = "allow" +manual_unwrap_or = "allow" +manual_map = "allow" +match_like_matches_macro = "allow" +single_match = "allow" +manual_unwrap_or_default = "allow" +manual_find = "allow" +explicit_counter_loop = "allow" +get_first = "allow" +implicit_saturating_sub = "allow" + +[profile.dev] +incremental = true # Compile your binary in smaller steps. + +[profile.release] +codegen-units = 1 # Allows LLVM to perform better optimization. +lto = true # Enables link-time-optimizations. +opt-level = 3 # s Prioritizes small binary size. Use `3` if you prefer speed. +panic = "abort" # Higher performance by disabling panic handlers. +strip = true # Ensures debug symbols are removed. diff --git a/migration/khadhroony-bot2-reference/README.md b/migration/khadhroony-bot2-reference/README.md new file mode 100644 index 0000000..2b10c8e --- /dev/null +++ b/migration/khadhroony-bot2-reference/README.md @@ -0,0 +1,293 @@ + + + +# Khadhroony Bot2 + +Khadhroony Bot2 est un workspace Rust modulaire pour observer, décoder, matérialiser, exécuter et analyser des transactions Solana. + +Le projet poursuit deux objectifs complémentaires. À court terme, il doit fournir un trading assisté contrôlable depuis des démos live Tauri. À long terme, il doit devenir une bibliothèque d'indexation et d'analyse Solana capable de classifier un grand nombre de programmes et de surfaces. + +Le socle technique doit permettre de : + +- ingérer des transactions Solana depuis RPC HTTP, WebSocket ou futures sources spécialisées ; +- normaliser chaque source vers une transaction canonique source-indépendante ; +- conserver la transaction canonique comme source d’audit et de replay ; +- conserver séparément les observations légères de provenance et de timing ; +- extraire les instructions, inner instructions, logs, comptes résolus et deltas de balances ; +- détecter les programmes impliqués dans chaque transaction ; +- décoder les programmes Solana de base, les programmes SPL, les surfaces DEX, les orderbooks, les routers et les protocoles annexes ; +- matérialiser les événements décodés en tables métier exploitables ; +- rejouer uniquement les modules nécessaires grâce à un ledger de traitement par module et version ; +- alimenter des agrégations, diagnostics, signaux de risque et futures règles de stratégie ; +- construire des plans d'exécution séparés des décodeurs ; +- tester les fonctionnalités live via `kb_app_demo` avant automatisation. + +## Architecture générale + +Le workspace suit une chaîne de traitement en couches : + +```text +canonical transaction -> source observations -> Solana extraction -> decoding -> materialization -> aggregation -> strategy +``` + +Chaque couche doit être indépendante autant que possible. Les décodeurs ne dépendent pas du stockage, du RPC, de Tauri, du wallet ou des matérialisateurs. Les matérialisateurs travaillent sur les événements décodés et produisent des événements métier normalisés. + +## Rôle des dossiers principaux + +- `kb_core` contient les primitives partagées minimales. +- `kb_model` contient les types communs du modèle interne. +- `kb_config` définit le contrat JSON multi-profils, le schéma runtime, les validations typées et les exports TS-rs destinés aux applications Tauri. +- `kb_rpc` contient les protocoles et clients RPC Solana HTTP, WebSocket et futurs flux Helius/Yellowstone. +- `kb_execution_solana` assemble les plans communs en transactions Solana, lie la simulation au message exact et résout les signataires via l’interface Solana `Signer`; `kb_wallet` fournit actuellement le backend temporaire. +- Le projet différé d’acquisition historique gratuite est décrit dans `docs/HISTORICAL_DATA_ACQUISITION_PLAN.md`. Il reste hors du ROADMAP actif tant que les décodeurs, matérialisateurs et exécuteurs prioritaires ne sont pas suffisamment avancés. +- `kb_store_core` définit les traits de stockage. +- `kb_store_pg` fournit le backend PostgreSQL cible. +- `kb_store_sqlite` reste limité aux tests et imports locaux. +- `kb_decoder_api` définit le contrat des décodeurs. +- `kb_materializer_api` définit le contrat des matérialisateurs. +- `kb_materializer_*` contient les projections métier par famille : trades, liquidité, NFT, oracle, lending, routing, compliance audit, etc. +- `kb_pipeline` orchestre backfill, extraction canonique vers core, décodage, matérialisation, agrégation et validation. +- `kb_app_demo` sert de démonstrateur Tauri, sans logique métier lourde. Il charge le profil actif, initialise `kb_logging`, rend le README en HTML et expose une fenêtre dédiée à la configuration. +- Chaque crate opérationnelle doit émettre ses propres événements `tracing` avec une cible canonique égale à son nom Cargo ; `kb_app_demo` reste limité aux frontières UI/Tauri et aux résumés. +- Chaque profil écrit des fichiers globaux `debug.log`, `info.log`, `error.jsonl` et `app.log`, ainsi que `debug.log`, `info.log` et `error.jsonl` dans un répertoire par crate utilisant `tracing`. +- Les interfaces officielles Solana/SPL étroites sont privilégiées ; `wincode` puis Borsh sont préférés, et un parser local borné reste autorisé lorsque l’interface officielle n’expose que `bincode`. + + +## État `0.4.1` validé + +`0.4.1` clôt le décodage et les matérialisations instructionnelles stables des programmes Solana natifs, historiques, loaders, précompiles et surfaces ZK du périmètre core. + +`kb_decoder_solana_core` couvre maintenant 18 surfaces exécutables natives : System, Vote, Stake, Config, Compute Budget, Address Lookup Table, Feature, ZK ElGamal Proof, l’ancien ZK Token Proof historique/no-op, Native Loader, BPF Loader v1/v2/Upgradeable/v4, Ed25519, secp256k1, secp256r1 et Slashing. `StakeConfig11111111111111111111111111111111` est classé comme compte historique non exécutable, sans dispatch ni plan d’exécution. + +Les matérialisations natives actives sont réparties par famille : + +- `kb_materializer_lifecycle` : Address Lookup Table, lifecycle loaders, Feature revoke, durable nonce, comptes System create/allocate, contextes ZK ElGamal et rapports Slashing ; +- `kb_materializer_admin` : assignations System, écritures Config opaques, changements d’autorité Loader ; +- `kb_materializer_compliance_audit` : écritures/copies de bytecode Loader et profil transactionnel Compute Budget ; +- `kb_materializer_staking` : projections instructionnelles Stake/Vote commitées, sans prétendre reconstruire les snapshots finaux. + +Les précompiles de signature, les preuves ZK sans compte de contexte et l’ancien ZK Token Proof restent en decode/audit, sans duplication dans `kb_sol_mat_events` tant qu’aucun consommateur dédié ne l’exige. Les transactions échouées restent décodées comme intentions ou échecs, mais les matérialiseurs mutables appliquent `SuccessfulCommittedOnly`. + +Les validations finales de `0.4.1` couvrent les tests unitaires, PostgreSQL réel, Clippy et des replays mainnet ciblés : System, Config, Stake, Vote, Compute Budget, ALT, ZK ElGamal et Slashing synthétique. Les replays terminent sans `unmatched`, `failedInputs` ou `unsupported`; les archives de logs fournies ont des `error.jsonl` vides. + +## Couche d'exécution + +Le workspace développe une couche d'exécution séparée du décodage. Les crates `kb_executor_*` sont des bibliothèques universelles : elles construisent des plans pour les applications de trading, les CLI, les workers, les outils d'administration et de futurs exécutables indépendants. + +Les opérations dangereuses ne sont pas supprimées des bibliothèques. Elles doivent être encodées, testées et protégées par des politiques renforcées, tandis que `kb_app_demo` peut volontairement ne pas les exposer. Les exécuteurs ne dépendent ni du wallet, ni du RPC, ni des décodeurs ; l'orchestration assemble ensuite plan, sécurité, simulation, signature, envoi et validation post-exécution. + +## Démos live + +`kb_app_demo` reste le shell Tauri unique pour valider les capacités du workspace : configuration, logs, DB, RPC, wallet, listeners, décodage, matérialisation, exécution et validation post-transaction. + +## Documentation + +- `RUST_RULES.md` contient les règles Rust générales réutilisables ; `RULES.md` contient les règles spécifiques et normatives du workspace. +- `ROADMAP.md` contient les étapes futures et changements prévus. +- `CHANGELOG.md` ne reçoit une entrée qu'après validation d'une version. +- `docs/` contient les notes d'architecture et de conception, notamment `TRACING_CONTRACT.md`, `SOLANA_INTERFACE_DEPENDENCIES.md`, `NATIVE_SOLANA_CLOSURE_AUDIT.md`, `NATIVE_SOLANA_MATERIALIZATION_AUDIT.md` et `DEVNET_EXECUTION.md`. +- `validation_sql/` contient les contrôles SQL de validation. + +## Jalon validé `0.4.2` + +`0.4.2` valide l’infrastructure d’exécution et `kb_executor_solana_core`. L’exécuteur expose **109 opérations déterministes** : dix-sept System, quatre Compute Budget, cinq Address Lookup Table, six formes de précompile, deux Config, deux Feature, deux Slashing, trois ZK ElGamal, vingt-quatre Stake, vingt-quatre Vote, onze Loader v3 et neuf Loader v4. La surface Vote couvre les vingt variantes wire actuelles de `solana-vote-interface 6.0.3` ainsi que quatre plans composés de création legacy/V2, avec ou sans seed. Loader v3 consomme `solana-loader-v3-interface 8.x` et son schéma `wincode`; Loader v4 reproduit explicitement son wire officiel sans activer les helpers conditionnés par `bincode`. BPF Loader v1/v2 restent historiques `decode-only`, Native Loader ne possède pas de builder client et l’ancien ZK Token Proof reste historique. Les opérations nécessitant un état courant restent protégées par un préflight séparé et ne sont pas activées sur Mainnet par ce jalon. + +`kb_pipeline` fournit le préflight stateful Localnet/Devnet pour ALT, Config, Feature, Slashing et les contextes ZK ElGamal. Le rapport `Ready`/`Blocked`/`NotRequired` expose les contrôles et faits mesurés avant simulation. `docs/NATIVE_SOLANA_EXECUTION_MATRIX.json` inventorie les dix-huit surfaces et les 109 opérations appelables, avec égalité imposée par un test Rust. La matrice décodeur couvre les mêmes dix-huit surfaces et 121 déclarations de couverture. + +`kb_rpc` possède des contrats propres et configurables pour les 52 méthodes HTTP standard et les neuf paires WebSocket standard. Les options restent composables champ par champ et aucun DTO Agave n’est exposé par l’API publique. La session WebSocket persistante gère multiplexage, unsubscribe, timeout, reconnexion et réabonnement. `blockSubscribe`, `slotsUpdatesSubscribe` et `voteSubscribe` sont activables par endpoint ; lorsqu’un nœud les rejette comme indisponibles, la capacité concernée est désactivée automatiquement pour la session et les appels suivants sont refusés localement. + +`kb_wallet` fournit un wallet temporaire en mémoire ou persistant sous `wallets/temporary/`, avec fichiers non écrasés, permissions privées et secrets confinés côté Rust. `kb_execution_solana` assemble les transactions, lie la simulation au hash exact du message et signe seulement après autorisation de `kb_execution_safety`. `kb_pipeline` orchestre le parcours backend Devnet complet : contrôle du destinataire et du minimum rent-exempt, financement plafonné, System transfer, simulation exacte, signature, envoi, confirmation, hydratation canonique, extraction core et decode replay ciblé. Ce parcours a été validé réellement sur Devnet avec un transfert de 1 000 000 lamports et PostgreSQL réel. + +La frontière Tauri/TS-rs n’émet plus de `bigint` dans les payloads JSON actifs. Les identifiants WebSocket sont contrôlés contre `Number.isSafeInteger`, et les limites du navigateur SQL sont validées côté frontend avant l’appel backend. `cargo tauri dev` avec Vite constitue la validation frontend retenue ; aucun `npm run build` séparé n’est exigé pour cette clôture. + +## Jalon validé `0.4.3` + +Le prompt `prompts/022_v0_4_3_spl_memo.md` reste l’historique de travail du jalon. Le décodeur contextualisé distingue v1, v3 et v4 avec payload UTF‑8 ou tentative invalide, comptes ordonnés, signataires, outer/inner, hash et intentions non commitées. La projection dédiée `kb_materializer_transaction_annotations` transforme uniquement les Memo réussis et commités en annotations consultables et idempotentes ; les faits d’audit détaillés restent dans le decode. `demo_decode_replay` expose ces sorties comme un journal borné filtrable par signature, avec slot, instruction path, génération, texte, hash et signataires vérifiés. + +`kb_executor_spl_memo` expose maintenant un intent typé `AddMemo` et appelle le builder officiel `spl-memo-interface 2.1.0` avec l’ID explicite v1/v3/v4. Le wire reste le texte UTF‑8 brut et chaque signer est readonly, ordonné et visible avant signature. La bibliothèque construit les trois générations sur tout cluster ; la disponibilité effective du programme est vérifiée par la simulation obligatoire, et Mainnet reste régi par les garde-fous communs plutôt que désactivé dans l’exécuteur. La démo opérateur reste volontairement limitée à Memo v4 Devnet et réutilise la fenêtre Solana existante. Elle montre la simulation, l’envoi explicitement confirmé, l’hydratation, l’extraction, le replay, l’annotation et l’idempotence avec des DTO Tauri JSON sans `bigint` actif. + +Le parcours a été validé le 15 juillet 2026 avec une simulation seule puis trois transactions v4 Devnet confirmées. Chaque signature a produit une insertion canonique, une extraction core, un événement Memo, une annotation `transaction_annotation` et un second replay `skipped` sans nouvelle sortie. Le corpus Mainnet réel couvre séparément v1, v3 et v4 ; aucun envoi historique v1/v3 sur Devnet n’est revendiqué ni requis. + +La matrice `docs/SPL_MEMO_MATRIX.json` conserve les différences historiques, le corpus Mainnet réel, les preuves Devnet v4 et les limitations explicitement non revendiquées pour v1/v3. Le worker d’acquisition historique reste un projet différé indépendant, sans numéro de version attribué. + +La validation finale confirme 5 tests `kb_program_ids`, 9 tests décodeur Memo, 7 tests matérialiseur d’annotations, 15 tests exécuteur Memo, 22 tests API d’exécution, 15 tests safety, 12 tests Solana, 61 tests pipeline, 113 tests RPC, 96 tests démo et 46 tests PostgreSQL réels. Le test opt-in Devnet avec envoi et `cargo clippy --all-targets` sont propres. La version active devient `0.4.4 — SPL Token` selon `prompts/023_v0_4_4_spl_token.md`. + +## Jalon validé `0.4.4` + +Les premières tranches SPL Token établissent la matrice machine-readable des 28 tags publiés par +`spl-token-interface 3.0.0` et remplacent le scaffold du décodeur classique. Le wire local borné +conserve montants bruts, decimals explicites, autorités, comptes ordonnés, chemins outer/inner, +transactions échouées non commitées, suffixes et diagnostics. `Batch` produit un parent d'audit et +des enfants à identités dérivées stables sans accepter de batch imbriqué. + +Les matérialiseurs actifs produisent un journal instructionnel `token_account`, des faits admin et +des constats de risque sans score ; aucune fee classique n'est inventée. Le replay Mainnet validé de +370 instructions a produit 371 sorties, huit refus expliqués par huit transactions échouées et un +second replay idempotent sans nouvelle sélection. + +`kb_executor_spl_token` expose 24 opérations typées courantes ou récentes avec montants JSON en +chaînes, autorités simples/multisig et builders officiels. Quatre initialisations Rent obsolètes +restent decode-only. `UnwrapLamports` et `Batch` sont constructibles selon l'interface publiée ; leur +déploiement sur le programme classique Devnet a été observé le 16 juillet 2026 par deux simulations +réussies sans signature ni envoi. Localnet, Testnet et Mainnet restent non testés pour ces deux tags. + +`0.4.4-pre.005` ajoute le préflight stateful Localnet/Devnet dans `kb_pipeline`. Les lectures RPC +restent hors du décodeur et de l’exécuteur ; elles valident les layouts Mint/Account/Multisig, +l’état, les relations de mint, les soldes, les réserves wrapped SOL, les délégations, les autorités, +les seuils multisig et le rent avant simulation. Les tests standards sont offline et un test Devnet +read-only reste opt-in. La démo Token réutilise la fenêtre d’exécution Solana existante : son panneau +`TransferChecked` expose préflight, simulation, confirmation et validation post-exécution, tandis +qu’un journal matérialisé borné accepte des filtres typés par signature, mint, compte, famille et +opération sans créer une WebView par opération. + +Le correctif fonctionnel de `pre.005` ajoute l’orchestration Devnet générique : simulation autonome +après préflight, puis envoi explicitement confirmé lorsque le wallet du profil résout tous les +signataires. Une transaction envoyée est confirmée, hydratée, extraite, décodée et matérialisée, +avant un second replay idempotent. Cette couche ne restreint pas les builders universels aux seuls +clusters ou signataires disponibles dans la démo. + +Le parcours `TransferChecked` a été validé réellement sur Devnet le 15 juillet 2026 avec deux +comptes Token auxiliaires du même mint et un wallet isolé correspondant exactement à l’autorité. +Le test opt-in impose désormais la présence de la soumission et de la confirmation, l’absence +d’échec ou de refus au premier replay, au moins une matérialisation Token et un second replay sans +nouvelle sortie ; il imprime aussi la signature afin qu’elle puisse rejoindre le corpus audité. + +`0.4.4-pre.006` ajoute un lifecycle destructif explicitement autorisé pour trois comptes classiques +sans ATA. Son correctif crée d'abord les comptes bruts via trois instructions System officielles, +avec keypairs éphémères, rent et tailles 82/165/165 vérifiés, puis onze transactions initialisent le mint et deux comptes Token, mintent et +transfèrent un montant contrôlé, créent puis révoquent une délégation, brûlent exactement les deux +soldes et ferment les comptes Token. Chaque étape est simulée, confirmée, hydratée, décodée, +matérialisée et rejouée une seconde fois avant que l'étape suivante ne soit admise. + +Le correctif de reprise du lifecycle traite explicitement les confirmations RPC interrompues : une +signature prédécesseur confirmée est hydratée, extraite, décodée, rapprochée de l'opération attendue +et rejouée idempotemment avant de reprendre à l'index demandé. Un délai borné sépare ensuite les +étapes afin de réduire la pression sur les endpoints Devnet publics, sans masquer leurs erreurs. + +Le lifecycle Devnet réel a été validé le 15 juillet 2026 après une interruption du RPC public : la +reprise a récupéré l'initialisation déjà confirmée, puis achevé mint, transfer, approve/revoke, deux +burn et deux close. Chaque étape récupérée ou envoyée a produit exactement une matérialisation et un +second replay idempotent. La fenêtre opérateur `pre.007` permet désormais de rejouer le parcours +représentatif `TransferChecked` et de consulter ces faits `token_account`, `admin` et `risk` comme +événements chronologiques, sans inventer un état final de compte. + +L'audit `pre.008` reporte dans `docs/SPL_TOKEN_MATRIX.json` les preuves par instruction. Le corpus +Mainnet réel couvre `Transfer`, `Approve`, `CloseAccount`, `TransferChecked`, `BurnChecked` et +`SyncNative`; ses trois initialisations restent volontairement référencées sous leur nom matérialisé +normalisé sans inventer une variante wire. Le lifecycle Devnet prouve `InitializeAccount3`, +`MintToChecked`, `TransferChecked`, `ApproveChecked`, `Revoke`, `BurnChecked` et `CloseAccount`. +Localnet n'a pas été exécuté, et la publication p-token des tags `45`/`255` n'est pas assimilée à +leur déploiement sur un cluster public. + +La validation Tauri de `pre.007` a démarré Vite et l'application, réussi le préflight et la +simulation `TransferChecked`, puis refusé correctement l'envoi sans confirmation opérateur. Avec +confirmation, un second refus a identifié que le wallet du profil et l'autorité Token étaient deux +clés distinctes. La démo ne tente jamais de signer à la place d'une autorité externe ; pour envoyer, +les comptes Token doivent appartenir au wallet persistant sélectionné, comme dans le scénario +backend Devnet déjà confirmé et matérialisé. + +La clôture du 16 juillet 2026 confirme les régressions ciblées, PostgreSQL réel, Clippy et Tauri. +Les validations finales couvrent notamment 5 tests Program IDs, 8 tests décodeur Token, 15 tests +exécuteur Token, 9 tests décodeur Memo, 7 tests annotations, 22 tests API d'exécution, 15 tests +safety, 12 tests transaction Solana, 81 tests pipeline, 113 tests RPC, 41 tests configuration, +102 tests démo et 46 tests PostgreSQL réels. Tauri charge 77 routes de logs, simule +`TransferChecked`, bloque l'envoi non confirmé, restitue sa matérialisation commitée exacte et +simule Memo v4 sans erreur runtime. + +## Jalon validé `0.4.5` + +`0.4.5` clôt la surface SPL Associated Token Account avec +`spl-associated-token-account-interface 2.0.0`. Le décodeur reconnaît exactement `Create`, +`CreateIdempotent`, `RecoverNested` et la forme historique vide de `Create`; il conserve comptes +ordonnés, flags, doublons, outer/inner paths, transactions échouées non commitées et diagnostics +bornés. Les PDA sont dérivés et validés explicitement avec les seeds +`[wallet, Token Program ID, mint]`, ce qui distingue les ATA classiques des ATA Token-2022 sans +prétendre décoder les extensions de ce dernier. + +Le lifecycle ATA est possédé par `kb_materializer_token_accounts` : création normale, création ou +réutilisation idempotente non distinguable depuis le parent seul, et récupération nested. +`kb_materializer_risk` possède séparément le constat factuel d'anti-pattern nested récupéré. Les +matérialiseurs lifecycle général, admin et fees déclarent explicitement leur absence de projection, +et les CPI SPL Token conservent seules leurs initialisations, transferts et fermetures. Les +transactions échouées ou structurellement invalides ne produisent aucune mutation. + +`kb_executor_spl_associated_token_account` construit les trois variantes actuelles avec les +builders officiels, pour SPL Token classique ou Token-2022. Simulation et dry-run sont obligatoires +par défaut. Le préflight Localnet/Devnet contrôle cluster, mints, Program IDs, dérivations, comptes +existants, signataires, rent et plafonds ; la post-validation vérifie création/réutilisation ou les +conditions de fermeture/transfert de `RecoverNested`. + +Les preuves Devnet du 16 juillet 2026 couvrent deux parcours complets `CreateIdempotent` classiques, +la création puis la réutilisation d'un ATA Token-2022 avec `ImmutableOwner`, et un +`RecoverNested` classique transférant 1 000 000 000 unités brutes avant fermeture du nested ATA. +Chaque soumission a été simulée, confirmée, hydratée, extraite, décodée et matérialisée, puis rejouée +sans doublon. La fenêtre Tauri unique expose création ATA et journal lifecycle borné ; +`RecoverNested` reste couvert hors UI par un test opt-in contrôlé. + +La validation finale confirme les régressions Token/Memo, 10 tests décodeur ATA, 10 tests exécuteur +ATA, 90 tests pipeline, 113 tests RPC, 41 tests configuration, 111 tests démo, 46 tests PostgreSQL +réels et `cargo clippy --all-targets` propre. Les versions Rust, npm et Tauri sont synchronisées en +`0.4.5`. + +## Jalon validé `0.4.6` + +`0.4.6` clôt Token-2022 et le registre ElGamal comme surfaces séparées. Token-2022 couvre les instructions de base, les extensions identifiables, les états Mint/Account/Multisig et TLV, les matérialisations propriétaires et les exécuteurs simulation-first. Le registre ElGamal conserve son propre Program ID, sa matrice, son état de 64 octets, son PDA et ses opérations administratives ; il n’est pas fusionné avec Token-2022 ni avec le programme natif ZK ElGamal Proof. + +La validation publique Token-2022 comprend huit opérations Devnet confirmées et 55 instructions Mainnet décodées sans échec. La validation réseau ElGamal Registry reste indisponible sur les clusters publics au 20 juillet 2026, mais ses builders, parsers, préflights, preuves et scénarios stateful sont validés synthétiquement/offline. Le bilan détaillé est publié dans `docs/V0_4_6_VALIDATION.md`. + +Le replay contextualisé dispose désormais du mode `incomplete_signatures`. Il sélectionne uniquement les signatures contenant au moins une instruction `pending`, `failed`, `replay_requested` ou `unsupported`, applique la limite aux signatures avant expansion et réévalue leurs instructions compatibles sans activer le force replay global. Deux campagnes PostgreSQL réelles ont sélectionné 81 entrées chacune : 1 décodée, 4 unsupported et 76 rejets fail-closed appartenant tous à des transactions Solana échouées. + +La régression finale confirme `kb_store_core` 41/41, `kb_store_pg` 46/46 avec PostgreSQL réel, `kb_pipeline` 124/124, `kb_app_demo` 118/118 et `cargo clippy --all-targets` propre. + +## Jalon actif `0.4.7` + +La suite active est Metaplex Token Metadata selon `prompts/026_v0_4_7_metaplex_token_metadata.md`. La préversion `0.4.7-pre.001` ouvre l’audit avec les crates `kb_decoder_metadata_metaplex_token_metadata` et `kb_executor_metadata_metaplex_token_metadata`, ainsi que `docs/METAPLEX_TOKEN_METADATA_MATRIX.json`, sans activation runtime tant que le wire complet n’est pas prouvé. L’exécuteur existe comme frontière audit-only parce que le SDK officiel fournit des builders, mais il n’expose encore aucune construction de transaction. Ce programme indépendant complète les metadata externes des mints SPL Token classique et Token-2022 sans être absorbé par leurs décodeurs. La priorité porte sur la frontière exacte du Program ID, les PDA et layouts versionnés, les NFT/collections/programmable assets, la provenance des metadata et le replay PostgreSQL idempotent. + +Anchor n’est pas requis par les programmes SPL planifiés dans `0.4.x`. Son infrastructure commune est reportée à `0.4.18`, immédiatement avant Meteora, afin d’être conçue à partir des besoins réels des protocoles Anchor sans retarder Metaplex et les autres surfaces Core/SPL. + +## Backend de données + +PostgreSQL est le backend principal prévu. SQLite reste présent uniquement pour les tests, les imports ponctuels ou les petits corpus locaux. + +Les tables Solana PostgreSQL utilisent le schéma courant du profil, généralement `public`. Le projet ne crée pas de schémas applicatifs explicites comme `raw`, `core`, `obs`, `decode`, `mat`, `catalog`, `agg`, `ops` ou `wallet`. La séparation logique se fait dans le nom physique de table au format `kb_sol__`, par exemple `kb_sol_raw_transactions`. + +## Replay des signatures incomplètes + +La fenêtre de replay contextualisé de `kb_app_demo` expose un mode `signatures_incomplete`. Il sélectionne uniquement les signatures contenant au moins une instruction `pending`, `failed`, `replay_requested` ou un résultat `unsupported`, puis réévalue les instructions compatibles de ces signatures sans activer le force replay global. La limite porte sur le nombre de signatures incomplètes avant expansion. La matérialisation reste optionnelle et idempotente. + +Ce mode est destiné aux backfills croissants : une correction de décodeur peut reprendre seulement les signatures incomplètes au lieu de rejouer toutes les signatures déjà réussies. + +## Livraison par delta + +Après le squelette initial, les modifications doivent être livrées sous forme de zip delta contenant uniquement les fichiers ajoutés ou modifiés. Un delta multi-module ou racine utilise `khadhroony-bot2_vX.Y.Z-pre.abc-delta.zip`. Un delta limité à un module Rust utilise `kb_modulename_vX.Y.Z-pre.abc-delta.zip`. Un correctif conserve le même numéro `pre.abc` et ajoute `-delta-fix-001.zip`, puis `-fix-002.zip`, au lieu d’incrémenter la préversion. Chaque archive contient un `delta.md` non versionné listant les fichiers ajoutés, modifiés, à supprimer manuellement, ainsi que les validations exécutées ou non exécutées. + +## Surfaces réservées + +Le workspace réserve dès le squelette les crates de décodage pour les DEX, launchpads, orderbooks, routers, perps et surfaces candidates recensés dans `idls/` et dans la documentation de conception. Ces crates restent des points d'ancrage tant que les discriminants, corpus et règles de matérialisation ne sont pas validés. + + +## Registres de programmes + +Le workspace distingue deux registres : + +- `registry/program_registry_seed.toml` pour les surfaces applicatives et protocolaires ; +- `registry/core_program_id_seed.toml` pour les programmes primitifs, loaders, sysvars et comptes natifs Solana/SPL. + +Les comptes non exécutables observés pendant la recherche restent documentés dans `docs/ACCOUNT_ONLY_CANDIDATES.md`. Bags dispose maintenant aussi d'une note dédiée dans `docs/BAGS_FM.md` pour les programmes Fee Share V1/V2 confirmés. + +## Classification des surfaces + +La classification des IDL locales est suivie dans `docs/IDL_SURFACE_CLASSIFICATION.md`. Le registre machine principal reste `registry/program_registry_seed.toml`. + +## Configuration, endpoints et listeners + +Le workspace utilise `kb_config` pour charger plusieurs profils de configuration, avec un seul profil actif. Le fichier JSON est d'abord validé par `config/schema.config.json`, puis désérialisé et validé par les règles métier Rust. + +Le pipeline normalise toutes les sources vers une transaction canonique commune. HTTP JSON-RPC est utilisé d’abord pour les backfills gratuits. Helius `transactionSubscribe` et Yellowstone gRPC seront ajoutés plus tard dans `kb_rpc`, après les décodeurs prioritaires, sans créer de crate provider séparée. La fenêtre `demo_backfill` permet de lancer des campagnes bornées par liste de signatures, programme, token ou pool. En direction « avant, plus anciennes », une ancre vide commence sur la page la plus récente ; la direction « après, plus récentes » exige toujours une signature d’ancrage. + +Les listeners WebSocket doivent permettre la détection temps réel des créations de token, créations de pool, migrations, swaps, changements de liquidité et changements sur les tokens ou pools surveillés. Les exports TS-rs de `kb_config` servent uniquement à partager les types Rust/TypeScript avec les applications Tauri comme `kb_app_demo`, puis les futures applications wallet ou finales. Les payloads Tauri partagés, comme la page de configuration, doivent aussi être exportés par TS-rs depuis leur crate applicative. + +### Metaplex Token Metadata `0.4.7-pre.016` + +L’inventaire officiel complet des modules d’instruction a été réaudité avant le passage aux comptes on-chain. `FreezeDelegatedAccount` et `ThawDelegatedAccount` historiques sont désormais décodés avec leurs contrats exacts ; les autres surfaces encore absentes restent planifiées avant toute déclaration de couverture exhaustive. + diff --git a/migration/khadhroony-bot2-reference/ROADMAP.md b/migration/khadhroony-bot2-reference/ROADMAP.md new file mode 100644 index 0000000..f21cd46 --- /dev/null +++ b/migration/khadhroony-bot2-reference/ROADMAP.md @@ -0,0 +1,1439 @@ + + + +# Roadmap + +Ce fichier contient les changements futurs et le phasage prévu. Il ne remplace pas le changelog : le changelog ne reçoit que les versions validées. + +## Version 0.0.x — stabilisation du squelette + +- [x] Réserver les crates core, décodeurs, matérialisateurs, exécuteurs et applications. +- [x] Valider que le workspace compile après ajout de la couche d'exécution. +- [x] Conserver `CHANGELOG.md` pour les versions validées uniquement. +- [x] Réserver `prompts/` pour les futures sessions ChatGPT par version. +- [x] Documenter les objectifs court terme et long terme. +- [x] Ajouter `kb_config` et `config/example.config.json` comme base de configuration. +- [x] Ajouter les surfaces Bags Fee Share V1/V2 confirmées. +- [x] Définir le format obligatoire des zips delta ChatGPT. +- [x] Mettre à jour `CHANGELOG.md` pour `0.0.1` et `0.0.2`. +- [x] Exécuter `cargo build`. +- [x] Exécuter `cargo test --workspace`. +- [x] Exécuter `cargo clippy --workspace --all-targets`. +- [x] Faire le commit `v0.0.2` comme squelette stabilisé. + +## Version 0.1.x — logging et configuration + +### 0.1.0 — `kb_logging` + +- [x] Finaliser l'API publique minimale de `kb_logging`. +- [x] Définir les routes de logs console, fichier humain, fichier JSON et fichier erreurs. +- [x] Router les logs vers console, fichier humain, fichier JSON et fichier erreurs. +- [x] Gérer les niveaux par target et par sortie. +- [x] Conserver le nettoyage ANSI des fichiers via writer dédié pour les messages issus de WebView Tauri. +- [x] Ajouter la normalisation des globs `crate::*` en plus des globs `crate.*`. +- [x] Ajouter les tests unitaires de configuration des targets et expressions de filtres. +- [x] Ajouter les tests unitaires de sélection des routes console, fichier humain, fichier JSON et fichier erreurs. +- [x] Ajouter les tests unitaires du writer sans ANSI. +- [x] Valider localement `cargo test -p kb_logging` : 14 tests passés. +- [x] Valider localement `cargo clippy -p kb_logging --all-targets`. +- [x] Reporter `0.1.0` dans `CHANGELOG.md` après validation. + +### 0.1.1 — `kb_config` + +- [x] Finaliser les structures typées de configuration. +- [x] Normaliser les exports TS-rs avec `export_to = "../frontend/ts/bindings/kb_config/settings/*.ts"`. +- [x] Charger un fichier JSON avec plusieurs profils. +- [x] Garantir un seul profil actif. +- [x] Ajouter `config/schema.config.json`. +- [x] Valider le JSON brut via `jsonschema`. +- [x] Valider les profils absents, dupliqués ou incohérents. +- [x] Exclure du schéma les transports avancés hors périmètre avant `v1.x`. +- [x] Conserver les placeholders de clés directement dans les URLs. +- [x] Supprimer `ProfileConfig.enabled` : `active_profile` est l’unique sélection de profil. +- [x] Retirer `data.idls_directory` du contrat runtime : les IDLs restent des artefacts de développement. +- [x] Conserver `kb_core` comme dépendance anticipée pour les erreurs communes workspace. +- [x] Valider `config/example.config.json`. +- [x] Ajouter les tests de parsing, validation et sérialisation. +- [x] Valider localement `cargo test --tests -p kb_config` : 35 tests passés. +- [x] Valider localement `cargo clippy -p kb_config --all-targets`. +- [x] Reporter `0.1.1` dans `CHANGELOG.md` après validation. + +### 0.1.2 — liaison `kb_logging`, `kb_config` et démo Tauri + +- [x] Initialiser `kb_logging` depuis le profil actif. +- [x] Appliquer les niveaux, formats et chemins de fichiers depuis la configuration. +- [x] Vérifier la création des fichiers de logs configurés et conserver la rotation comme amélioration future. +- [x] Conserver `main.html` comme fenêtre d'accueil. +- [x] Rendre le README workspace en HTML dans la fenêtre `main` sans afficher les commentaires de métadonnées `file/version`. +- [x] Ajouter dans `main.html` un dropdown vers les fenêtres de démonstration. +- [x] Créer `kb_app_demo/src/demo_config.rs` comme fenêtre de démo uniquement. +- [x] Créer `kb_app_demo/frontend/demo_config.html`. +- [x] Créer `kb_app_demo/frontend/ts/demo_config.ts`. +- [x] Réutiliser le même format visuel que la fenêtre principale. +- [x] Afficher le profil actif, la configuration complète et le schéma JSON. +- [x] Ajouter un viewer JSON interactif pour le profil actif, la configuration complète et le schéma JSON. +- [x] Désactiver le toolbar natif du viewer JSON pour éviter les boutons d’indentation non configurables. +- [x] Exporter `DemoConfigPayload` via TS-rs et l'importer côté TypeScript. +- [x] Déclarer `demo_config` dans `capabilities/default.json` et `vite.config.ts`, puis créer la fenêtre à la demande depuis Rust pour éviter une fenêtre cachée bloquante. +- [x] Corriger la fermeture de l’application lorsque `demo_config` n’a jamais été ouverte. +- [x] Ajouter un pont `emit_frontend_log` pour réémettre les logs WebView avec des targets `tracing` statiques. +- [x] Router `kb_app_demo.frontend::*` dans la console et le fichier debug de `kb_app_demo`. +- [x] Ajouter `AppState` comme état applicatif global dans `kb_app_demo/src/app_state.rs`. +- [x] Retirer l’initialisation runtime globale de `demo_config`, qui reste une simple fenêtre de démo. +- [x] Ajouter un verrou mono-instance uniquement dans `kb_app_demo/src/main.rs`, non appliqué à `kb_app_demo_lib::run()` pour préserver l’entrée mobile. +- [x] Déplacer l’installation du provider Rustls par défaut dans `kb_app_demo_lib::run()` pour desktop et mobile. +- [x] Ajouter un splashscreen avec fade-in/fade-out et durée minimale extensible pour les vérifications longues futures. +- [x] Aligner le branchement du splashscreen dans `tauri_builder.setup` sur le modèle de `khadhroony-bobobot/kb_demo_app`. +- [x] Corriger `SplashOrder.duration_ms` en `u32` pour éviter un type BigInt côté TypeScript. +- [x] Corriger l’animation du splash avec `requestAnimationFrame`, une opacité initiale explicite et une attente initiale de `1500 ms` avant fade-in. +- [x] Valider visuellement le fade-in/fade-out dans `cargo tauri dev`. +- [x] Valider localement `cargo test -p kb_core` : 2 tests passés. +- [x] Valider localement `cargo test -p kb_config` : 35 tests passés. +- [x] Valider localement `cargo test -p kb_logging` : 14 tests passés. +- [x] Valider localement `cargo test -p kb_app_demo` : 12 tests passés. +- [x] Valider localement `cargo clippy --all-targets`. +- [x] Valider localement `cargo tauri dev -c kb_app_demo/tauri.conf.json` depuis la racine du workspace. +- [x] Reporter `0.1.2` dans `CHANGELOG.md` après validation. + +### 0.1.3 — clients HTTP/WS Solana standard, rôles et pools + +- [x] Explorer `khadhroony-bobobot/kb_lib` et les anciennes démos HTTP/WS comme source d'adaptation. +- [x] Remonter depuis `0.3.x` les points déjà nécessaires : réutilisation des pools HTTP/WS validés et finalisation initiale de `kb_rpc`. +- [x] Ajouter les contrats JSON-RPC 2.0 partagés entre HTTP et WebSocket. +- [x] Créer ou finaliser le client HTTP JSON-RPC Solana standard dans `kb_rpc`. +- [x] Créer ou finaliser le client WebSocket Solana standard court dans `kb_rpc`. +- [x] Ajouter la gestion de pool HTTP par rôle d'endpoint. +- [x] Ajouter la gestion de pool WebSocket par rôle d'endpoint. +- [x] Finaliser le premier modèle de rôles d'endpoints exploitable par les pools. +- [x] Associer chaque rôle à un ou plusieurs types de requêtes via `request_kinds`. +- [x] Router les requêtes HTTP et les requêtes WebSocket par rôle. +- [x] Préparer les contrats nécessaires aux futurs pools HTTP et WebSocket persistants. +- [x] Ajouter une fenêtre de démo HTTP JSON-RPC dédiée dans `kb_app_demo`. +- [x] Ajouter une fenêtre de démo WebSocket dédiée dans `kb_app_demo`. +- [x] Remplacer les champs libres `role`/`method` des démos HTTP/WS par des listes contrôlées issues de la config et des méthodes supportées. +- [x] Ajouter une connexion/déconnexion explicite dans `demo_ws`. +- [x] Réutiliser la même connexion WebSocket pour plusieurs souscriptions tant que l’endpoint sélectionné reste identique. +- [x] Conserver la connexion WebSocket dans `AppState` quand la fenêtre `demo_ws` est fermée puis recréée. +- [x] Ne plus déconnecter automatiquement `demo_ws` à la fermeture de la fenêtre ; réserver la fermeture au bouton déconnecter ou à l’arrêt global de l’application. +- [x] Remplacer l’affichage JSON échappé des démos par des zones texte readonly avec bouton de copie. +- [x] Ajouter le refresh explicite des pools HTTP/WS depuis les fenêtres de démo. +- [x] Ajouter un profil `mainnet` Helius avec les paramètres issus de l’ancien `config.json` fourni. +- [x] Élargir les listes de méthodes HTTP/WS de démonstration aux commandes Solana standard courantes. +- [x] Ajouter un champ `Params JSON complet` à la démo HTTP pour tester les méthodes multi-paramètres. +- [x] Ajouter l’unsubscribe explicite d’une subscription WebSocket sélectionnée sans fermer la socket persistante. +- [x] Ajouter une limitation côté Rust des notifications WebSocket envoyées à la WebView pour éviter le gel sur les subscriptions très bavardes. +- [x] Borner côté TypeScript l'historique affiché dans `demo_ws`. +- [x] Ajouter les tests unitaires offline de `kb_rpc` pour JSON-RPC, rôles, endpoints désactivés, snapshots, round-robin HTTP/WS et génération d’IDs. +- [x] Ajouter les liens correspondants dans le dropdown de la fenêtre principale. +- [x] Conserver les démos dans des fichiers Rust, HTML et TypeScript séparés comme dans `khadhroony-bobobot/kb_demo_app`. +- [x] Confirmer que gRPC, Helius enhanced WebSocket, Helius gRPC et LaserStream restent hors scope avant `v1.x` ou `v2.x`. +- [x] Reporter vers les versions RPC ultérieures les wrappers métier spécialisés HTTP/WS à partir des commandes RPC déjà couvertes dans `khadhroony-bobobot`. +- [x] Conserver les limites comme contrat de configuration et reporter vers `0.4.x` leur enforcement runtime par token bucket et pause 429. +- [x] Reporter vers `0.7.x` la généralisation des clients WebSocket persistants de production avec registry, unsubscribe contrôlé et relay d'événements durables. +- [x] Valider localement `cargo test -p kb_rpc` : 39 tests passés. +- [x] Valider localement `cargo test -p kb_app_demo` : 28 tests passés. +- [x] Valider localement `cargo clippy --all-targets`. +- [x] Valider visuellement `demo_http` dans `cargo tauri dev -c kb_app_demo/tauri.conf.json`. +- [x] Valider visuellement `demo_ws` dans `cargo tauri dev -c kb_app_demo/tauri.conf.json`. +- [x] Valider que le pool HTTP alterne entre endpoints compatibles avec le profil `mainnet_research`. +- [x] Valider que `programSubscribe` très bavard reste utilisable grâce au throttling UI et que l’unsubscribe fonctionne. +- [x] Reporter `0.1.3` dans `CHANGELOG.md` après validation. + +## Version 0.2.x — SQL, stockage et conventions DB + +### 0.2.0 — conventions storage, préfixes et contrats + +- [x] Auditer les crates existantes concernées par le stockage : `kb_store_core`, `kb_store_pg`, `kb_config`, `kb_core`, `kb_app_demo` et, si nécessaire seulement, `kb_rpc`. +- [x] Définir le rôle exact de chaque crate touchée avant d'ajouter des migrations ou des requêtes. +- [x] Valider que PostgreSQL utilise le schema courant/default, généralement `public`, sans schemas applicatifs explicites. +- [x] Valider le préfixe de table `kb_sol__` pour les tables Solana du nouveau projet. +- [x] Définir les domaines intégrés au nom de table : `raw`, `core`, `obs`, `decode`, `mat`, `catalog`, `agg`, `ops`, `wallet`. +- [x] Documenter les tables candidates initiales sans les implémenter toutes immédiatement. +- [x] Utiliser uniquement des noms de tables sans préfixe de schema explicite, par exemple `kb_sol_raw_rpc_transactions`, `kb_sol_core_transactions`, `kb_sol_obs_program_observations`, `kb_sol_decode_decoded_events`, `kb_sol_mat_trade_events`, `kb_sol_catalog_tokens` et `kb_sol_ops_processing_ledger`. +- [x] Définir la séparation stricte `entities/`, `dtos/`, `queries/`, `repositories/`. +- [x] Interdire les structures Rust dans `queries/` : les structures appartiennent à `entities/` ou `dtos/`. +- [x] Définir la convention `Entity` comme représentation proche d'une ligne SQL. +- [x] Définir la convention `Dto` comme contrat applicatif, Tauri ou repository. +- [x] Définir la convention `InsertDto` / `UpsertDto` pour les écritures. +- [x] Définir les règles de nommage SQL : `id`, `created_at`, `updated_at`, `slot`, `signature`, `program_id`, `raw_json`, `payload_json`. +- [x] Définir les contraintes minimales : clés primaires, index uniques, index par signature, slot, program id et temps d'insertion. +- [x] Définir les migrations comme artefacts PostgreSQL versionnés dans `kb_store_pg`. +- [x] Préparer la page Tauri future de diagnostic DB sans l'implémenter si le store n'est pas encore prêt. +- [x] Mettre à jour `kb_store_core/README.md` avec les conventions validées. +- [x] Mettre à jour `kb_store_pg/README.md` avec les conventions PostgreSQL validées. +- [x] Mettre à jour ou créer un document de prompt pour ouvrir la suite `0.2.x`. +- [x] Neutraliser les migrations héritées qui créaient des schemas applicatifs ou des tables qualifiées comme `raw DOT sol_transactions`. +- [x] Ne pas modifier `CHANGELOG.md` avant validation locale du jalon. + +### 0.2.1 — `kb_store_core` contrats et types communs + +- [x] Créer les modules publics minimaux de `kb_store_core` sans dépendre de PostgreSQL. +- [x] Ajouter les erreurs storage en s'appuyant sur `kb_core::Error` et `kb_core::Result`. +- [x] Ajouter les types communs : pagination, tri, limites, healthcheck, statut migration. +- [x] Ajouter les premiers DTO génériques : raw RPC transaction, raw WS notification, core transaction, core instruction. +- [x] Ajouter les contrats core pour account keys, logs et balance changes extraits. +- [x] Ajouter `CoreInstructionReplayInput` pour fournir aux décodeurs une instruction avec contexte extrait. +- [x] Ajouter les contrats de cycle de vie raw : état de rétention, état de traitement et mark lifecycle. +- [x] Ajouter les contrats de replay par instruction : état instruction, filtre de replay et lifecycle mark. +- [x] Ajouter les premiers traits repository sans implémentation SQL. +- [x] Étendre les repositories pour préparer la sélection des instructions non traitées. +- [x] Ajouter tests unitaires offline pour pagination, validation DTO et erreurs. +- [x] Documenter le cycle de vie raw et la future compaction/purge sans l'activer. +- [x] Documenter le split core des instructions, logs, account keys et balances avant les migrations. +- [x] Valider localement `cargo test -p kb_store_core` : 28 tests passés. +- [x] Valider localement `cargo clippy --all-targets` sans avertissement. +- [x] Reporter `0.2.1` dans `CHANGELOG.md` après validation locale. + +### 0.2.2 — `kb_store_pg` connexion PostgreSQL et migrations + +- [x] Ajouter la lecture effective de la configuration PostgreSQL depuis le profil actif. +- [x] Créer les options de connexion PostgreSQL minimales dans `kb_store_pg`. +- [x] Créer le pool PostgreSQL via `sqlx::PgPool`. +- [x] Ajouter le masquage DSN pour les diagnostics. +- [x] Ajouter un healthcheck PostgreSQL minimal basé sur `SELECT 1`. +- [x] Lire le schema courant PostgreSQL via `current_schema()`. +- [x] Lire la version PostgreSQL pour les diagnostics. +- [x] Ajouter une stratégie de migrations PostgreSQL explicite et non destructive. +- [x] Ne pas créer de schemas PostgreSQL applicatifs ; utiliser le schema par défaut du profil, généralement `public`. +- [x] Valider les noms de tables `kb_sol__` et refuser les schemas explicites. +- [x] Ajouter des tests offline pour options, masquage DSN et validation des noms de tables. +- [x] Ajouter un test PostgreSQL réel optionnel contrôlé par `KB_POSTGRES_TEST_URL`. +- [x] Valider localement `cargo test -p kb_store_pg` : 11 tests passés. +- [x] Valider localement le test PostgreSQL réel optionnel `optional_postgres_healthcheck_from_env` avec `postgres://solana:solana@localhost:5432/solana`. +- [x] Valider localement `cargo test -p kb_store_core` : 28 tests passés. +- [x] Valider localement `cargo test -p kb_config` : 35 tests passés. +- [x] Valider localement `cargo test -p kb_app_demo` : 28 tests passés. +- [x] Valider localement `cargo clippy --all-targets` sans avertissement après correction de l'exception `async_trait` / `clippy::implicit_return`. +- [x] Reporter `0.2.2` dans `CHANGELOG.md` après validation. + +### 0.2.3 — raw store Solana minimal + +- [x] Créer `kb_sol_raw_rpc_transactions` pour stocker le raw RPC immuable. +- [x] Créer `kb_sol_raw_ws_notifications` pour stocker les notifications WebSocket brutes utiles. +- [x] Prévoir `notification_key`, signature optionnelle et lien optionnel vers la transaction RPC canonique. +- [x] Créer les queries et repositories PostgreSQL associés aux DTOs `0.2.1`. +- [x] Ajouter insert idempotent par signature pour les transactions raw. +- [x] Ajouter insert append-only dédupliqué par `notification_key` pour les notifications WS raw. +- [x] Ajouter index minimaux par signature, slot, subscription kind, lien RPC et date d'insertion. +- [x] Ajouter une initialisation idempotente `PostgresStore::initialize_raw_store_schema()`. +- [x] Préparer `auto_initialize_schema` pour appliquer le schéma raw minimal après connexion quand le profil le demande. +- [x] Documenter que `0.2.3` ne purge pas encore physiquement `raw_json`. +- [x] Valider localement `cargo test -p kb_store_pg` : 21 tests passés. +- [x] Valider localement le test PostgreSQL réel optionnel `optional_postgres_raw_store_roundtrip_from_env` avec `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana`. +- [x] Valider localement `cargo test -p kb_store_core` : 28 tests passés. +- [x] Valider localement `cargo test -p kb_config` : 35 tests passés. +- [x] Valider localement `cargo test -p kb_app_demo` : 28 tests passés. +- [x] Valider localement `cargo clippy --all-targets` sans avertissement. +- [x] Reporter `0.2.3` dans `CHANGELOG.md` après validation. + +### 0.2.4 — core Solana normalisé minimal + +- [x] Créer les tables core minimales : `kb_sol_core_transactions`, `kb_sol_core_account_keys`, `kb_sol_core_instructions`, `kb_sol_core_inner_instructions`, `kb_sol_core_logs`, `kb_sol_core_balance_changes`. +- [x] Construire `CoreInstructionReplayInput` depuis les tables `core` pour les décodeurs. +- [x] Marquer les instructions insérées comme `Pending` pour permettre un replay instruction-level. +- [x] Préparer le replay partiel par état, slot et program id via `CoreInstructionReplayFilter`. +- [x] Ajouter une initialisation idempotente raw/core via `PostgresStore::initialize_store_schema()`. +- [x] Sérialiser l'initialisation raw/core par verrou PostgreSQL `pg_advisory_xact_lock`. +- [x] Normaliser les noms de contraintes et d'index PostgreSQL avec les préfixes `pk_`, `fk_`, `ck_`, `ux_` et `ix_`. +- [x] Ajouter un script SQL de maintenance pour supprimer les tables raw/core dans l'ordre inverse des dépendances. +- [x] Valider localement le script `kb_store_pg/maintenance/drop_raw_core_store.sql` sur la base PostgreSQL locale. +- [x] Valider localement `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana cargo test -p kb_store_pg -- --nocapture` : 30 tests passés. +- [x] Valider localement la création des 8 tables raw/core dans PostgreSQL. +- [x] Valider localement `cargo clippy --all-targets` sans avertissement. +- [x] Reporter le worker/extracteur raw RPC vers core vers les jalons d'ingestion/backfill, car `0.2.4` valide uniquement le stockage core et les inputs de replay. +- [x] Reporter les observations minimales et le ledger ops vers un jalon dédié après stabilisation de l'extraction. +- [x] Reporter l'extension du replay par module, version, signature, surface et discriminator après l'introduction des domaines `decode` et `ops`. +- [x] Reporter `0.2.4` dans `CHANGELOG.md` après validation. + +### 0.2.5 — diagnostics SQL dans `kb_app_demo` + +- [x] Ajouter `demo_sql_diag` pour le diagnostic PostgreSQL global : profil actif, backend, DSN masqué, schema courant, tables détectées, version migrations et healthcheck. +- [x] Ajouter `demo_sql_pg_raw` pour les tables `kb_sol_raw_rpc_transactions` et `kb_sol_raw_ws_notifications`. +- [x] Ajouter `demo_sql_pg_core` pour les tables core normalisées `kb_sol_core_*`. +- [x] Ajouter les boutons de refresh read-only dans chaque fenêtre. +- [x] Ajouter les liens dans le dropdown de la fenêtre principale. +- [x] Tenter l'initialisation raw/core au démarrage Tauri quand `database.postgres.auto_initialize_schema` est actif. +- [x] Afficher dans le splashscreen les tables créées, déjà présentes, manquantes ou en erreur, avec logs `tracing` associés. +- [x] Remplacer les payloads JSON textuels des démos SQL par le viewer JSON interactif déjà utilisé par `demo_config`. +- [x] Garantir que les démos SQL ne lancent ni extracteur, ni décodage, ni matérialisation. +- [x] Documenter à l’époque que l’extracteur raw RPC vers core était reporté à `0.4.x`; ce phasage historique est remplacé par le recadrage `0.3.x` ci-dessous. +- [x] Valider localement `cargo test -p kb_store_pg` : 30 tests passés. +- [x] Valider localement `cargo test -p kb_config` : 35 tests passés. +- [x] Valider localement `cargo test -p kb_app_demo` : 32 tests passés. +- [x] Valider localement `cargo clippy --all-targets` sans avertissement. +- [x] Valider visuellement `demo_sql_diag`, `demo_sql_pg_raw` et `demo_sql_pg_core` via `cargo tauri dev -c kb_app_demo/tauri.conf.json`. +- [x] Reporter `0.2.5` dans `CHANGELOG.md` après validation. + +## Version 0.3.x — fondation transactionnelle canonique et backfill gratuit + +`0.3.0` reste le jalon historique de cadrage Agave local validé. Les essais matériels ont ensuite montré qu’un nœud Agave mainnet complet n’était pas une source légère adaptée à cette phase. `0.3.1` réoriente donc la série vers une transaction canonique indépendante de la source, des observations d’acquisition légères, le backfill HTTP gratuit et l’extraction core. + +```text +sources HTTP / WebSocket / gRPC + -> adaptateur de source dans kb_rpc + -> transaction Solana canonique indépendante du provider + -> kb_sol_raw_transactions + -> kb_sol_obs_transaction_observations + -> extraction core + -> décodeurs et matérialiseurs +``` + +Après validation réelle de la transition `0.2.x -> 0.3.1`, les fichiers SQL ont été consolidés en une baseline propre avec `0001_canonical_transaction_store.sql` et `0002_core_store.sql`. `0.3.4` ajoute uniquement `0003_processing_ledger.sql`; l’historique des anciennes tables reste dans le changelog et la documentation, pas dans le DDL actif. + +### 0.3.0 — cadrage Agave local et sources temps réel — clôturé + +- [x] Documenter l’hypothèse d’un nœud Agave local non votant comme source temps réel. +- [x] Préparer le profil manuel `agave_local_research`, les probes RPC/WS et les mesures de ressources. +- [x] Conserver `mainnet_research` comme profil actif stable et ne lancer aucune ingestion de production. +- [x] Valider localement le delta `0.3.0-pre.001` avant mise à jour du changelog. +- [x] Mesurer l’impact réel du snapshot, d’AccountsDB, du ledger, de la RAM, du swap et du réseau. +- [x] Abandonner temporairement Agave local et `blockSubscribe` comme axe actif à cause du coût matériel et opérationnel. +- [x] Reporter les conclusions historiques dans `CHANGELOG.md`. + +### 0.3.1 — transaction canonique et observations d’acquisition — clôturé + +- [x] Remplacer la cible `kb_sol_raw_rpc_transactions` par `kb_sol_raw_transactions`. +- [x] Remplacer la cible `kb_sol_raw_ws_notifications` par `kb_sol_obs_transaction_observations`. +- [x] Renommer `raw_json` en `canonical_json` et `raw_json_hash` en `canonical_json_hash`. +- [x] Ajouter `canonical_format_version`. +- [x] Renommer `kb_sol_core_transactions.raw_rpc_transaction_id` en `raw_transaction_id`. +- [x] Stocker dans les observations uniquement la provenance, le protocole, la méthode, le commitment, la session, le filtre, les timestamps, la taille, les hashes techniques, les statuts et les erreurs. +- [x] Interdire la duplication durable du payload complet dans les observations. +- [x] Aligner DTOs, entities, repositories, requêtes, diagnostics SQL et démos Tauri. +- [x] Valider manuellement la migration réelle depuis le schéma `0.2.x` sur PostgreSQL 17.10. +- [x] Vérifier l’absence de `kb_sol_raw_rpc_transactions` et `kb_sol_raw_ws_notifications`. +- [x] Vérifier la présence de `kb_sol_raw_transactions`, `kb_sol_obs_transaction_observations`, `canonical_json`, `canonical_json_hash`, `canonical_format_version` et `raw_transaction_id`. +- [x] Consolider les migrations SQL en deux fichiers ne contenant que les tables actives. +- [x] Conserver temporairement dans le DDL runtime le chemin de compatibilité idempotent pour les workspaces encore en `pre.001`, sans recréer les anciennes tables. +- [x] Valider `cargo test -p kb_app_demo` : 32 tests passés. +- [x] Valider `cargo test -p kb_store_core` : 30 tests passés. +- [x] Valider `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana cargo test -p kb_store_pg -- --nocapture` : 32 tests passés. +- [x] Valider `cargo clippy --all-targets` sans avertissement. +- [x] Valider le démarrage Tauri et les diagnostics des huit tables raw/obs/core. +- [x] Nettoyer manuellement les fixtures PostgreSQL de test. +- [x] Vérifier et rationaliser les prompts `0.3.x`. +- [x] Reporter `0.3.1` dans `CHANGELOG.md`. + +### 0.3.2 — contrat canonique et adaptateur HTTP — clôturé + +- [x] Définir le type canonique dans `kb_model` et ses conversions explicites. +- [x] Conserver les signatures et public keys en base58, les payloads d’instruction en base64 et les valeurs numériques sans perte. +- [x] Inclure transaction legacy/v0, message header, static account keys, Address Lookup Tables, loaded addresses, outer instructions, inner instructions, logs, statut, erreurs, fee, compute units, cost units, balances SOL/SPL, rewards, return data et block time quand disponibles. +- [x] Garantir une sérialisation déterministe pour `canonical_json_hash` avec tri récursif des objets JSON et SHA-256. +- [x] Ajouter dans `kb_rpc` un adaptateur `getTransaction` JSON-RPC vers le modèle canonique. +- [x] Définir une interface interne commune pour les futurs adaptateurs Helius WebSocket et Yellowstone gRPC, sans créer de crate séparée. +- [x] Ajouter des fixtures offline legacy et v0, succès et échec, ALT, CPI, Token et Token-2022. +- [x] Tester l’idempotence : une même transaction provenant de plusieurs réponses HTTP doit produire le même hash canonique. +- [x] Valider `cargo test -p kb_model` : 23 tests passés. +- [x] Valider `cargo test -p kb_rpc` : 48 tests passés. +- [x] Valider `cargo test -p kb_store_core` : 31 tests passés. +- [x] Valider `KB_POSTGRES_TEST_URL=postgres://solana:solana@localhost:5432/solana cargo test -p kb_store_pg -- --nocapture` : 32 tests passés avec roundtrip PostgreSQL réel. +- [x] Ajouter les retours explicites requis dans `kb_model/src/canonical_transaction.rs`, `kb_rpc/src/get_transaction.rs` et `kb_store_core/src/dtos/raw_dtos.rs`. +- [x] Valider `cargo clippy --all-targets` sans avertissement. +- [x] Nettoyer manuellement les fixtures PostgreSQL après validation. +- [x] Reporter `0.3.2` dans `CHANGELOG.md` après validation. + +### 0.3.3 — backfill HTTP sur comptes gratuits — clôturé + +- [x] Ajouter `getSignaturesForAddress` paginé avec `before`, `until`, limite, curseur de reprise et borne `max_pages`. +- [x] Ajouter le backfill d’une liste explicite de signatures séparées par des retours à la ligne. +- [x] Ajouter les campagnes avant/après une signature pour un programme, un token et un pool. +- [x] Hydrater chaque signature avec `getTransaction` via les pools HTTP existants. +- [x] Appliquer le débit, la concurrence et la pause 429 depuis le rôle configuré, avec retries bornés. +- [x] Écrire une seule ligne canonique par signature dans `kb_sol_raw_transactions`. +- [x] Écrire une observation légère par tentative `getTransaction` dans `kb_sol_obs_transaction_observations`. +- [x] Conserver les statuts `missing` et `failed` sans créer de faux payload canonique. +- [x] Ignorer avant hydratation les signatures déjà présentes dans le store canonique. +- [x] Ajouter `demo_backfill` avec accordéons Bootstrap 5, journal, résumé et arrêt coopératif. +- [x] Corriger le parcours `after` avec la borne RPC `until`, la pagination `before`, des pages de 1 000 et une sélection bornée des signatures les plus proches de l’ancre. +- [x] Documenter le fonctionnement dans `docs/BACKFILL_HTTP.md`. +- [x] Valider `cargo test -p kb_rpc` : 54 tests passés. +- [x] Valider `cargo test -p kb_pipeline` : 10 tests passés après le correctif final d’arrêt/reprise. +- [x] Valider `cargo test -p kb_store_core` : 31 tests passés. +- [x] Valider `cargo test -p kb_store_pg` : 32 tests passés. +- [x] Valider `cargo test -p kb_app_demo` et les exports TS-rs : 38 tests passés. +- [x] Valider `cargo clippy --all-targets` sans avertissement. +- [x] Valider visuellement les modes signatures explicites, programme avant/après, token avant/après et pool avant/après dans Tauri. +- [x] Valider le parcours profond `program_after` : 5 516 signatures indexées parcourues pour sélectionner et hydrater les 5 signatures directement postérieures à l’ancre. +- [x] Valider la persistance PostgreSQL réelle : 62 transactions canoniques et 62 observations, sans ligne core avant `0.3.4`. +- [x] Détecter l’arrêt opérateur pendant une campagne de 500 candidats et confirmer l’interruption des nouveaux appels `getTransaction`. +- [x] Remplacer la création globale des futures par une file d’exécution bornée à la concurrence effective. +- [x] Séparer `candidates_started`, `candidates_completed`, `candidates_cancelled` et `candidates_not_started`. +- [x] Calculer le curseur de reprise `before` depuis le dernier préfixe contigu réellement terminé. +- [x] Valider localement le correctif `0.3.3-pre.004` avec `cargo test -p kb_pipeline`, `cargo test -p kb_app_demo` et `cargo clippy --all-targets`. +- [x] Rejouer l’arrêt d’une campagne de 500 candidats et vérifier qu’aucun candidat non démarré n’est journalisé comme traité : 7 démarrés, 3 terminés, 4 annulés et 493 non démarrés. +- [x] Reporter la validation finale de `0.3.3` dans `CHANGELOG.md`. + +### 0.3.4 — extraction transaction canonique vers core et clôture `0.3.x` — clôturé + +- [x] Ajouter le worker `kb_sol_raw_transactions -> kb_sol_core_*` dans `kb_pipeline`. +- [x] Résoudre les account keys statiques et les loaded addresses dans un ordre canonique unique. +- [x] Extraire outer instructions, inner instructions, logs, balances, statut et contexte transactionnel. +- [x] Conserver des hashes déterministes pour les payloads d’instruction et le texte des logs. +- [x] Garantir l’idempotence par signature, indices stables et instruction path. +- [x] Ajouter le ledger `kb_sol_ops_processing_ledger` pour l’extracteur et ses versions. +- [x] Persister le graphe core, l’état raw et le succès ledger dans une transaction PostgreSQL unique. +- [x] Permettre le replay par signature, lot pending, plage de slots et program id déjà indexé. +- [x] Ajouter le mode normal, le force replay, la concurrence bornée et l’arrêt coopératif. +- [x] Ajouter `demo_core_extraction` avec accordéons Bootstrap 5, journal et résumé JSON. +- [x] Ajouter `demo_sql_replay_candidates` avec cinq tableaux read-only : transactions, programmes, mints, owners et account keys. +- [x] Ajouter les filtres PostgreSQL bornés par raw, ledger, slots, programme outer/inner/logs, mint, owner et account key. +- [x] Ajouter sélection par checkbox, reset des filtres, copie individuelle/multiligne et export CSV natif. +- [x] Écrire les CSV dans `data/exports_csv/` avec BOM UTF-8, séparateur `;`, CRLF et nom non écrasant. +- [x] Valider l’écriture CSV native dans Tauri/Linux : `replay_transactions.csv` écrit dans le workspace. +- [x] Ajouter un registre énumérable de 182 program IDs dans `kb_program_ids` et l’exposer à l’interface. +- [x] Ajouter les requêtes SQL d’intégrité raw/core/ledger. +- [x] Valider une extraction pending réelle : 70 extraites, 0 skip, 0 échec. +- [x] Valider l’intégrité après extraction : aucun doublon, orphelin, hash incohérent, ledger incohérent ou chemin inner invalide. +- [x] Valider le skip réel sur 14 signatures : 0 extraction, 14 skips. +- [x] Valider le force replay réel sur les mêmes 14 signatures : 14 extractions, 0 skip, cardinalité core inchangée. +- [x] Valider un second skip après force replay : 0 extraction, 14 skips. +- [x] Confirmer le ledger réel : 70 lignes `succeeded`, 84 tentatives après le force replay. +- [x] Ajouter un test PostgreSQL optionnel qui provoque une erreur intermédiaire, vérifie le rollback complet puis rejoue le bundle corrigé. +- [x] Rendre annulables les appels `getSignaturesForAddress`, `getTransaction`, l’attente du pacer et les pauses de retry/429. +- [x] Ajouter les tests offline d’annulation d’une opération longue et d’une pause de retry déjà annulée. +- [x] Conserver les transactions Solana échouées dans core et les rendre éligibles au futur décodage comme observations d’intention/échec. +- [x] Interdire aux futurs matérialiseurs d’interpréter automatiquement les événements d’une transaction échouée comme mutations d’état réussies. +- [x] Fusionner l’ancien périmètre `0.3.5` dans `0.3.4`; les corpus exhaustifs sont reportés aux versions protocolaires concernées. +- [x] Considérer le sélecteur read-only, les requêtes d’intégrité et le replay core comme outils de clôture de la fondation. +- [x] Valider `cargo test -p kb_pipeline` après l’ajout des tests d’annulation : 24 tests passés. +- [x] Valider `KB_POSTGRES_TEST_URL=... cargo test -p kb_store_pg -- --nocapture` avec le test de rollback : 39 tests passés. +- [x] Valider `cargo clippy --all-targets` sans avertissement. +- [x] Reporter `0.3.4` dans `CHANGELOG.md` et basculer le jalon actif vers `0.4.0`. + +Les contrôles autrefois prévus dans `0.3.5` sont reclassés ainsi : la stabilité canonique entre formes fournisseur est déjà couverte par `0.3.2`; le coût et les limites du backfill sont documentés par `0.3.3`; les corpus exhaustifs Pump, Meteora, Raydium et Orca appartiennent à leurs versions protocolaires; le rejeu réel d’un curseur `before` reste un contrôle opératoire utile mais ne bloque pas l’infrastructure de décodage, car la frontière contiguë et l’arrêt réel ont déjà été validés. + +## Version 0.4.x — décodeurs et matérialisations Solana Core/SPL + +### 0.4.0 — infrastructure de décodage core — clôturé + +- [x] Utiliser `prompts/019_v0_4_0_decoder_infrastructure.md` comme contrat de session. +- [x] Stabiliser les API `kb_decoder_api` et `kb_materializer_api` sur les inputs core contextualisés. +- [x] Ajouter le dispatch déterministe par `program_id`, surface, instruction path, payload/discriminator et contexte inner/logs. +- [x] Ajouter le replay borné par signatures, slots, program IDs, instruction paths, processor/version/hash et force replay. +- [x] Ajouter la persistance atomique des observations décodées, sorties matérialisées, couvertures et ledger. +- [x] Ajouter la politique explicite pour les transactions on-chain échouées et le refus des mutations métier supposées réussies. +- [x] Ajouter la matrice machine-readable déclarée/observée/reconnue/décodée/matérialisée/erreur, séparée par succès ou échec on-chain. +- [x] Ajouter `demo_decode_replay` sans SQL libre, avec sélection, processors, concurrence, journal, arrêt et diagnostics. +- [x] Ajouter le classifieur initial de toutes les surfaces natives connues sans faux décodage. +- [x] Borner automatiquement la sélection SQL aux program IDs déclarés par les décodeurs activés et refuser les filtres incompatibles. +- [x] Permettre au force replay borné par signatures de resoumettre les inputs quel que soit leur lifecycle courant. +- [x] Ajouter l’autorisation explicite « toutes les signatures » bornée par les filtres et la limite. +- [x] Refuser tout force replay sans signatures ni autorisation « toutes les signatures ». +- [x] Désactiver/refuser la matérialisation lorsqu’aucun matérialiseur n’est enregistré. +- [x] Corréler les traces par `campaign_id`, signature, instruction path, program ID et processor/version. +- [x] Valider localement les champs obligatoires du backfill avant invocation Tauri. +- [x] Ne plus réappliquer les DDL à chaque commande Tauri et synchroniser précisément les snapshots de couverture. +- [x] Valider `cargo test -p kb_config` : 36 tests passés. +- [x] Valider `cargo test -p kb_pipeline` : 41 tests passés. +- [x] Valider PostgreSQL réel `cargo test -p kb_store_pg -- --nocapture` : 44 tests passés. +- [x] Valider `cargo test -p kb_app_demo` : 75 tests passés. +- [x] Valider `cargo clippy --all-targets` sans avertissement. +- [x] Valider le build frontend de production avec `(cd kb_app_demo && npm run build)`. +- [x] Valider dans Tauri le skip version/hash, le force replay ciblé, le second skip, le replay global limité, l’annulation et le redémarrage. +- [x] Corriger le résumé d’annulation afin que `completed = started` et `started + not_started = selected`. +- [x] Valider la campagne finale : 382 sélectionnés, 45 démarrés/terminés, 337 non démarrés, aucun unmatched ni échec. +- [x] Valider les diagnostics `0.4.0` : 18 déclarations alors exposées, 382 observations Compute Budget et 94 observations System reconnues sans erreur ; Stake Config a ensuite été reclassé, ramenant le registre exécutable à 17 surfaces dans `pre.008`. +- [x] Reporter `0.4.0` dans `CHANGELOG.md` et basculer le jalon actif vers `0.4.1`. + +### 0.4.1 — programmes Solana natifs, loaders et précompiles — clôturé + +- [x] Utiliser `prompts/020_v0_4_1_native_solana_programs.md` comme contrat de session. +- [x] Auditer l’état réel de `kb_decoder_solana_core`, des types decode, du store, de la couverture et de la démo avant modification. +- [x] Vérifier chaque format d’instruction depuis les sources officielles actuelles ; ne jamais inventer un discriminant, un ordre de champs ou une sémantique. +- [x] Définir une taxonomie stable des événements natifs et les types partagés nécessaires sans créer de dépendance vers PostgreSQL, RPC ou Tauri. +- [x] Décoder maximalement `system`, y compris nonce accounts, seeds, allocate/assign, transfers et `CreateAccountAllowPrefund`, depuis `solana-system-interface 3.2.0`. +- [x] Décoder maximalement `compute_budget`, y compris `RequestUnitsDeprecated`, le tag réservé actuel et les limites modernes de `solana-compute-budget-interface 3.0.0` ; `pre.011` reproduit aussi l’acceptation runtime des octets suffixes via Borsh unchecked et les conserve pour audit. +- [x] Décoder maximalement `address_lookup_table` : create, freeze, extend, deactivate et close depuis `solana-address-lookup-table-interface 3.1.0`. +- [x] Ajouter une matérialisation générique et idempotente du lifecycle ALT, limitée aux observations exactes, réussies et commitées. +- [x] Valider ALT sur corpus mainnet réel : 104 instructions core, 104 inputs décodés, 104 sorties lifecycle, zéro échec ou unsupported. +- [x] Décoder maximalement `vote` depuis `solana-vote-interface ^6.0` avec `serde` + `wincode` : 20 variantes, autorisations, identité, commissions, withdraw, votes/state updates, formes compactes/tower, V2/BLS et récompenses délégateurs ; les deux collecteurs V2 proviennent des comptes d’instruction et non de `VoteInitV2`. +- [x] Décoder maximalement `stake` dans `pre.013` : 18 tags officiels, initialize/authorize/delegate/split/merge/withdraw/deactivate/lockup, variantes checked/seed, minimum delegation, deactivate delinquent, move stake/lamports et `redelegate` historique désactivé. +- [x] Reclasser `StakeConfig11111111111111111111111111111111` comme compte natif historique non exécutable et le retirer du dispatch/domaine d’exécution. +- [x] Valider `pre.013` sur le corpus Stake mainnet : 2 601/2 601 inputs décodés, zéro `failed`, `unsupported` ou `unmatched`, dont 339 instructions Stake réparties sur neuf entry codes observés. +- [x] Décoder `config` comme une écriture générique `ConfigKeys` + payload opaque borné et `feature` comme `revoke_pending_activation` selon leurs interfaces officielles. +- [x] Valider `pre.008` par tests ciblés, PostgreSQL réel, Clippy et démarrage/replay Tauri : 476/476 décodés, aucun échec, `unsupported` ou `unmatched`. +- [x] Valider Config sur corpus réel : 95 instructions décodées, zéro échec ou unsupported. Conserver Feature en validation synthétique faute d’invocation core observable dans les campagnes adressées. +- [x] Valider Vote par tests ciblés et replay mainnet : 51 tests décodeur réussis, 500 instructions Vote réelles décodées, toutes `tower_sync`, zéro échec Vote ; les 20 variantes restent couvertes par fixtures `wincode` officielles. +- [x] Valider `pre.011` après correction Compute Budget : 52 tests natifs, Clippy propre et force replay mainnet de 1 265/1 265 inputs décodés, avec zéro `failed`, `unsupported` ou `unmatched`. +- [x] Stabiliser dans `pre.012` le tracing transversal avant de reprendre les décodeurs : target canonique égal au package Cargo, matrice globale et par crate, `error.jsonl` dédié, erreurs decode/materialize structurées et responsabilité laissée à la crate opérationnelle. +- [x] Auditer les crates utilisant `tracing.workspace = true` et couvrir les treize crates alors actives par un test de constante canonique et par les routes des trois profils. +- [x] Ajouter le tracing propriétaire manquant sur les clients/pools HTTP et WebSocket ainsi que sur le backfill ; promouvoir les inputs decode unmatched, statuts failed/unsupported et échecs de matérialisation/persistance au niveau `error`. +- [x] Formaliser dans `docs/SOLANA_INTERFACE_DEPENDENCIES.md` la politique d’interfaces officielles : dépendance seulement au point de consommation, priorité `wincode` puis Borsh, parser borné lorsque l’interface n’expose que `bincode`. +- [x] Passer `CoreInstructionReplayInput` au contrat `2` avec une liste stable et numériquement ordonnée des instructions outer, sans migration SQL, afin de résoudre les références inter-instructions des précompiles. +- [x] Décoder `zk_elgamal_proof` dans `pre.015` selon `solana-zk-elgamal-proof-interface ^0.1` et le runtime Agave : treize discriminants, preuves inline ou référencées par compte, contexte optionnel et fermeture du contexte, sans recalcul cryptographique. +- [x] Décoder dans `pre.016` l’ancien `zk_token_proof` comme compatibilité historique, puis retirer dans `pre.019` la dépendance dépréciée `solana-zk-token-sdk` au profit d’un miroir wire local borné des dix-sept discriminants et tailles auditées. +- [x] Décoder les loaders `native_loader`, `bpf_loader_deprecated`, `bpf_loader`, `bpf_loader_upgradeable` et `loader_v4` ; classer l’invocation Native Loader opaque comme `ignored` sans sémantique inventée. +- [x] Décoder dans `pre.014` les précompiles `ed25519`, `secp256k1` et `secp256r1`, y compris compteurs, tables d’offsets, références outer courantes/externes, recovery ID secp256k1 et payloads structurés bornés sans recalcul cryptographique. +- [x] Conserver Memo hors périmètre natif et réserver `kb_decoder_spl_memo` pour ses générations v1, v3 et v4. +- [x] Distinguer `recognized`, `decoded`, `ignored`, `unsupported` et `failed` sans convertir une reconnaissance partielle en succès de décodage ; l’audit `pre.017` confirme les statuts terminaux séparés du pipeline et des observations. +- [x] Conserver la politique des transactions échouées : événements d’intention/échec autorisés, mutations réussies interdites par défaut ; `SuccessfulCommittedOnly` protège toutes les projections lifecycle natives. +- [x] Matérialiser les projections natives génériques déjà stabilisées : ALT, lifecycle loaders, révocation Feature, durable nonce, contextes ZK ElGamal et rapports Slashing. Ne pas les présenter comme la totalité des matérialisations natives possibles. +- [x] Implémenter dans `pre.017` les projections natives manquantes réellement justifiées : lifecycle des durable nonce accounts System et lifecycle des comptes de contexte ZK ElGamal, sans matérialiser les preuves. +- [x] Concevoir dans `pre.017` le contrat de `kb_materializer_staking` pour les projections Stake/Vote nécessitant un état durable : état précédent/suivant, délégation, désactivation, retrait, autorités, lockup, identité/commission Vote et récompenses ; ne pas les ajouter au matérialiseur générique. +- [x] Ajouter dans `pre.022` un profil Compute Budget transactionnel agrégé ; conserver les précompiles de signature et les preuves ZK sans contexte en decode/audit tant qu’aucun consommateur n’exige une projection dédiée. +- [x] Documenter dans `pre.019` l’audit complet des projections actives, différées et volontairement absentes dans `docs/NATIVE_SOLANA_MATERIALIZATION_AUDIT.md`. +- [x] Couvrir chaque variante déclarée par des fixtures officielles, dérivées des encodeurs SDK ou explicitement alignées sur le wire runtime ; l’audit `pre.018` inclut aussi les deux instructions Slashing. +- [x] Clore l’inventaire corpus : ALT et Config validés réellement ; Feature, loaders v3/v4, Slashing et ZK Token Proof validés synthétiquement faute d’invocation observable ; campagne Slashing retournée proprement à zéro signature. +- [x] Couvrir les payloads tronqués, discriminants inconnus, comptes invalides, offsets précompile invalides et transactions on-chain échouées sur toutes les surfaces dont le runtime expose ces cas. +- [x] Ajouter dans `pre.014` les fixtures adversariales des trois précompiles : références courantes, externes et mixtes, signatures multiples, messages vides/volumineux, headers/tables tronqués, compteurs invalides, index ou offsets hors bornes, Base64 absent/invalide, débordement borné et transaction échouée non commitée. +- [x] Ajouter dans `pre.001`/`pre.002` les fixtures officielles System/Compute Budget, les limites numériques, payloads tronqués, tags inconnus, comptes manquants, déterminisme et transactions échouées. +- [x] Ajouter dans `pre.003` ALT exact, la projection lifecycle ALT, les IDs SPL vérifiés et les crates réservées manquantes. +- [x] Analyser le replay mainnet `pre.004` : 476 inputs, 475 décodés, un faux échec System dû à un compte additionnel et 11 faux refus de matérialisation hors surface ALT. +- [x] Corriger dans `pre.005` le décodage System afin de conserver les comptes additionnels validement résolus avec le rôle `additional_account`. +- [x] Ajouter dans `pre.005` une sélection de matérialiseur par observation exacte, avant ledger et politique de transaction. +- [x] Réserver les exécuteurs manquants pour Memo, Single Pool, Account Compression, Noop et SPL Name Service. +- [x] Formaliser puis rendre obligatoire le contrat de tracing par crate dans `docs/TRACING_CONTRACT.md`. +- [x] Valider `pre.005` : cinq exécuteurs SPL réservés, 42 tests pipeline, 22 tests natifs, 4 tests lifecycle, 76 tests démo et clippy propre. +- [x] Valider le force replay ciblé puis global : 3/3 et 476/476 décodés, zéro échec, unsupported, unmatched et refus de matérialisation. +- [x] Ajouter dans `pre.006` les loaders immuables v1/v2, le loader upgradeable, Loader v4, le classement Native Loader opaque et la projection lifecycle des loaders. +- [x] Valider `pre.006` : 31 tests natifs, 6 tests lifecycle, 42 tests pipeline, 76 tests démo, Clippy propre et replay global Tauri de 476/476 inputs sans échec, unsupported, unmatched ni refus de matérialisation. +- [x] Rejouer System, Compute Budget et ALT sur le corpus PostgreSQL réel : campagne globale `pre.005` de 476 inputs, 476 décodés, aucun failed, unsupported, unmatched ou faux refus de matérialisation. +- [x] Valider `pre.017` : 12 tests lifecycle, 42 tests pipeline, 45 tests PostgreSQL réel, 76 tests démo, Clippy propre, 484 instructions System décodées avec 27 sorties nonce et 27 refus attendus de transactions échouées, puis 151 instructions ZK ElGamal avec 139 sorties de contexte. +- [x] Auditer Agave `v4.1.1` et ajouter dans `pre.018` le stateless Slashing Program absent du registre : deux instructions exactes, dispatch sans fallback, projection de rapport et absence explicite de pénalité appliquée par le programme. +- [x] Ajouter des invariants automatiques garantissant une surface par ID natif, au moins une couverture par surface, des identités de couverture uniques et zéro fallback `unclassified_native_instruction`. +- [x] Ajouter dans `pre.019` les modes `program_latest`, `token_latest` et `pool_latest` : une recherche « avant, plus anciennes » sans ancre commence sur la page de signatures la plus récente ; la direction `after` conserve une ancre obligatoire. +- [x] Retirer dans `pre.019` `solana-zk-token-sdk` et sa dépendance transitive à `bincode`; conserver uniquement un miroir wire local audité des tags et tailles historiques. +- [x] `pre.020` : matérialiser System create/allocate/assign, les écritures Config, les changements d’autorité Loader et les mutations de bytecode Loader sans dupliquer les balances core ni les payloads complets. +- [x] Activer `kb_materializer_admin` pour `assign`/`assign_with_seed`, Config `store`, Loader v3 `set_authority`/`set_authority_checked` et Loader v4 `transfer_authority`, avec politique `SuccessfulCommittedOnly`. +- [x] Activer `kb_materializer_compliance_audit` pour les `write` Loader v1/v2/v3/v4 et `copy` Loader v4, en conservant uniquement comptes, offsets, tailles, SHA-256 et préfixes bornés. +- [x] Enregistrer les trois matérialiseurs natifs dans `demo_decode_replay` et ajouter les routes `debug`/`info`/`error.jsonl` dédiées aux deux crates nouvellement opérationnelles. +- [x] `pre.021` : activer `kb_materializer_staking` pour les intentions/transitions commitées Stake/Vote, avec limites explicites sur l'absence de snapshots de comptes, et sérialiser les tests PostgreSQL réels pour éviter les interblocages intermittents. +- [x] `pre.022` : produire le profil Compute Budget transactionnel agrégé via `kb_materializer_compliance_audit` et confirmer la politique d'absence de projection pour signatures/ZK sans contexte. +- [x] Clôturer `0.4.1` avec 18 surfaces natives, matérialisations instructionnelles stables, logs propres et prompt de reprise `0.4.2`. +- [x] `pre.023` : replay global ciblé, validations finales, documentation de clôture, prompt `0.4.2` et mise à jour du `CHANGELOG.md`. +- [x] Confirmer que les précompiles de signature, les preuves ZK sans contexte et l’ancien ZK Token Proof restent volontairement sans projection dédiée, hors contexte matérialisable. +- [x] Ne pas étendre `demo_decode_replay` au-delà des matérialiseurs natifs enregistrés : inspection suffisante via résumés, couverture et requêtes SQL de validation. +- [x] Valider les replays ciblés avec corrélation par `campaign_id` pour System, Config, Stake, Vote, Compute Budget, ALT, ZK ElGamal et les surfaces synthétiques sans corpus. +- [x] Exécuter les tests unitaires des crates touchées, PostgreSQL réel, Clippy et campagnes Tauri/réelles avant mise à jour du changelog. +- [x] Mettre à jour `CHANGELOG.md` uniquement après validation complète de `0.4.1`. + +### 0.4.2 — infrastructure d’exécution et `kb_executor_solana_core` — clôturée + +#### Principes de portée + +- [x] Les crates d’exécution sont des bibliothèques universelles réutilisables par `kb_app_demo`, les workers, les CLI et de futurs exécutables non liés au trading. +- [x] La dangerosité d’une opération ne justifie pas son absence du builder : elle doit être encodée, testée et protégée par une politique dédiée, puis éventuellement rester non exposée dans `kb_app_demo`. +- [x] L’objectif est de couvrir toutes les instructions natives officiellement appelables. `Unsupported(reason)` ne doit subsister que pour une surface non invocable par transaction, retirée du runtime, insuffisamment documentée officiellement ou encore temporairement non implémentée avec dette explicite au ROADMAP. +- [x] Les exécuteurs restent indépendants du wallet, du RPC, des décodeurs et de Tauri ; ils produisent des plans déterministes et laissent simulation, signature, envoi et validation à l’orchestration. + +#### Tranches validées + +- [x] `pre.001` : ajouter les contrats typés de capacité, politique, plan préparé, simulation contextualisée, envoi, confirmation et diagnostic post-exécution sans casser les exécuteurs réservés historiques. +- [x] `pre.001` : implémenter les builders officiels System transfer/create account/allocate/assign et Compute Budget set unit limit/set unit price. +- [x] `pre.001` : remplacer `Maybe` dans `kb_executor_solana_core` par `Yes/No` pour le pont historique et `Supported/Unsupported(reason)` pour la voie typée. +- [x] `pre.001` : ajouter les contrôles de plan et pré-envoi pour simulation obligatoire, dry-run, cluster, mainnet, blockhash/nonce, signataires et plafonds. +- [x] `pre.002` : aligner le registre de l’exécuteur sur les dix-huit surfaces natives en incluant Slashing et corriger les retours explicites Clippy. +- [x] Valider localement les fondations : `kb_execution_api` 21 tests, `kb_execution_safety` 13 tests, `kb_executor_solana_core` 10 tests, `kb_pipeline` 45 tests, `kb_app_demo` 78 tests, `kb_config` 36 tests et Clippy global propre. +- [x] `pre.003` : étendre la configuration wallet/exécution à localnet, devnet, testnet et mainnet, ajouter dry-run, plafonds de frais/priorité et âge maximal du blockhash. +- [x] `pre.003` : remplacer le placeholder `kb_wallet` par un wallet temporaire en mémoire ou persistant, stocké sous `wallets/`, créé sans écrasement, chargé de façon bornée et utilisable comme `Signer` sans exposer les octets privés. +- [x] `pre.003` : protéger les fichiers wallet par permissions Unix privées, effacement des buffers secrets sérialisés, `.gitignore` bloquant et interdiction de sélectionner le wallet temporaire dans un profil mainnet. +- [x] Valider localement `pre.003` : `kb_wallet` 6 tests, `kb_config` 40 tests, `kb_store_pg` 45 tests offline et PostgreSQL réel, `kb_app_demo` 78 tests et Clippy global propre. + +#### Suite d’orchestration + +- [x] `pre.004` : ajouter dans `kb_rpc` les adaptateurs typés `getGenesisHash`, `getLatestBlockhash`, `getFeeForMessage` et `simulateTransaction`, avec routage par rôle, classification des clusters publics, diagnostics runtime et conversion vers `ExecutionSimulationResult`, sans signature ni envoi. +- [x] `pre.004` : documenter dans `kb_executor_solana_core/README.md` chaque fonction publique, les six opérations constructibles, leurs paramètres, résultats, effets et frontières. +- [x] Valider localement `pre.004` : `kb_rpc` 63 tests, `kb_execution_api` 21 tests, `kb_execution_safety` 13 tests, `kb_executor_solana_core` 10 tests, `kb_config` 40 tests et Clippy global propre. +- [x] `pre.005` : ajouter `kb_execution_solana` pour assembler les instructions planifiées en transaction legacy, produire les payloads base64 de frais/simulation, lier la simulation au hash exact du message, résoudre exactement les signataires requis via l’interface Solana `Signer` et signer séparément hors Tauri. +- [x] `pre.005` : renommer la constante interne en `MAINNET_GENESIS_HASH` sans changer sa valeur ; conserver `mainnet-beta` seulement comme alias historique des endpoints/CLI et renommer le variant public en `Mainnet` sérialisé `mainnet`. +- [x] `pre.005` : distinguer la simulation exacte de la simulation avec remplacement de blockhash et interdire qu’un résultat `replaceRecentBlockhash=true` autorise la signature du message original. +- [x] Valider localement les tests ciblés de `pre.005`/`pre.006` : `kb_execution_solana` 7 tests, `kb_execution_api` 22 tests, `kb_execution_safety` 15 tests, `kb_rpc` 63 tests, `kb_executor_solana_core` 10 tests, `kb_wallet` 6 tests et `kb_config` 40 tests. +- [x] `pre.006` : corriger la compilation croisée de `pre.005`, supprimer l’avertissement TS-rs sur l’alias historique `mainnet_beta`, utiliser le trait public `TypedInstructionExecutor` dans les tests d’assemblage, resynchroniser le contrat de simulation RPC et rendre les erreurs de routage tracing explicites. +- [x] `pre.007` : supprimer le dernier opérateur `?` introduit dans un chemin de production de `kb_execution_api`, le remplacer par un `match` avec propagation explicite et auditer les fichiers d’exécution `0.4.2` contre `?`, `unwrap` et `expect` de production. +- [x] Valider localement `pre.007` : `kb_execution_api` 22 tests et Clippy global propre. +- [x] `pre.008` : ajouter les contrats RPC typés `getBalance`, `requestAirdrop`, `sendTransaction`, `getSignatureStatuses` et `getBlockHeight`, avec routage par rôle, préflight obligatoire, contrôle de la signature retournée et confirmation bornée jusqu’à succès, échec, expiration ou timeout. +- [x] `pre.008` : étendre la configuration avec retries d’envoi, intervalle/nombre maximal de polls et plafond d’airdrop devnet ; construire directement les politiques RPC depuis `ExecutionConfig`. +- [x] Valider localement `pre.008` : `kb_rpc` 69 tests, `kb_execution_api` 22 tests, `kb_config` 40 tests, `kb_execution_solana` 7 tests, `kb_execution_safety` 15 tests, `kb_wallet` 6 tests, `kb_executor_solana_core` 10 tests, `kb_store_pg` 45 tests avec PostgreSQL réel et Clippy global propre. +- [x] `pre.009` : orchestrer le premier parcours backend Devnet complet avec wallet persistant, airdrop plafonné, plan System transfer, simulation exacte, signature, envoi, confirmation, `getTransaction`, insertion canonique, extraction core et decode replay ciblé. +- [x] Valider `pre.009` offline : `kb_pipeline` 49 tests, `kb_rpc` 69 tests, `kb_execution_api` 22 tests, `kb_execution_safety` 15 tests, `kb_execution_solana` 7 tests, `kb_executor_solana_core` 10 tests, `kb_wallet` 6 tests, `kb_config` 40 tests, `kb_store_pg` 45 tests avec PostgreSQL réel et Clippy global propre. +- [x] Valider le parcours Devnet réel avec le wallet persistant préfinancé et un transfert de 1 000 000 lamports : simulation, signature, envoi, confirmation et replay post-exécution réussis. +- [x] `pre.010` : ajouter `getAccountInfo` et `getMinimumBalanceForRentExemption`, refuser avant simulation un transfert insuffisant vers une adresse inexistante et conserver l’erreur runtime ainsi que les logs lorsque la simulation échoue. +- [x] `pre.010` : ajouter `demo_execution_solana_core`, limité aux profils Devnet persistants, avec plan, simulation exacte, confirmation opérateur, envoi, progression et diagnostics post-exécution sans exposition de clé privée. +- [x] Valider localement `pre.010` : `kb_rpc` 70 tests, `kb_pipeline` 51 tests, `kb_app_demo` 87 tests, `kb_execution_api` 22 tests, `kb_execution_safety` 15 tests, `kb_execution_solana` 7 tests, `kb_executor_solana_core` 10 tests, `kb_wallet` 6 tests, `kb_config` 40 tests, `kb_store_pg` 45 tests avec PostgreSQL réel et Clippy global propre. +- [x] Valider visuellement `demo_execution_solana_core` dans `cargo tauri dev` : simulation dry-run, refus sans confirmation opérateur, envoi Devnet confirmé, insertion canonique 1, extraction core 1 et decode replay 1 sans échec. +- [x] `pre.011` : ajouter les builders System `create_account_with_seed`, `transfer_with_seed`, `allocate_with_seed`, `assign_with_seed` et `create_account_allow_prefund`, avec validation des dérivations et signataires exacts. +- [x] Valider localement `pre.011` : `kb_executor_solana_core` 14 tests, `kb_app_demo` 87 tests et Clippy global propre. Le build frontend séparé n’est pas requis lorsque Vite est déjà validé par le serveur de développement Tauri. +- [x] `pre.012` : ajouter `transfer_many`, les créations de compte nonce normale/seedée et les builders de lifecycle nonce initialize/advance/authorize/withdraw/upgrade, avec plans multi-instructions et politique recent-blockhash explicite. +- [x] Valider localement `pre.012` : `kb_executor_solana_core` 19 tests, `kb_execution_solana` 7 tests, `kb_execution_api` 22 tests et Clippy global propre. +- [x] `pre.013` : ajouter la lecture RPC complète et bornée d’un compte nonce, sa validation System Program/Current/Initialized via `solana-nonce` et `wincode`, l’assemblage officiel `Message::new_with_nonce` avec `AdvanceNonceAccount` en première instruction, la valeur nonce comme blockhash et la preuve de simulation liée au message et à cette valeur. +- [x] Valider localement `pre.013` : `kb_executor_solana_core` 20 tests, `kb_execution_solana` 12 tests, `kb_execution_api` 22 tests, `kb_rpc` 70 tests et Clippy global propre. +- [x] `pre.014` : compléter Compute Budget avec `RequestHeapFrame` et `SetLoadedAccountsDataSizeLimit`, synchroniser les capacités et exports TS-rs, rendre normative la documentation des APIs publiques dans les README et détailler le phasage des exécuteurs futurs. +- [x] Valider localement `pre.014` : `kb_executor_solana_core` 21 tests, `kb_execution_api` 22 tests, `kb_execution_safety` 15 tests, `kb_execution_solana` 12 tests, `kb_rpc` 70 tests et Clippy global propre. +- [x] `pre.015` : ajouter les cinq builders officiels Address Lookup Table create/extend/freeze/deactivate/close, avec contrats d’intent, capacités exactes, exports TS-rs, contrôles statiques et documentation de la frontière stateful. +- [x] Valider localement `pre.015` : `kb_executor_solana_core` 25 tests, `kb_execution_api` 22 tests, `kb_execution_safety` 15 tests, `kb_execution_solana` 12 tests, `kb_rpc` 70 tests et Clippy global propre. +- [x] `pre.016` : ajouter les builders déterministes Ed25519, secp256k1 et secp256r1 dans deux formes par précompile — inline et table d’offsets — avec layouts officiels, références externes, bornes, exports TS-rs et documentation des contraintes de positionnement. +- [x] Valider localement `pre.016` : `kb_executor_solana_core` 31 tests, `kb_execution_api` 22 tests, `kb_execution_safety` 15 tests, `kb_execution_solana` 12 tests, `kb_rpc` 70 tests et Clippy global propre. +- [x] `pre.017` : ajouter les builders Config create/store, Feature activate/revoke, Slashing close/duplicate-block proof atomique et ZK ElGamal inline/account/close ; classer l’ancien ZK Token Proof comme historique `decode-only`, sans `Unsupported` générique. +- [x] `pre.018` : corriger les quatre avertissements Clippy de tests de `pre.017`, ajouter les vingt-quatre opérations Stake actuelles avec miroir wire `wincode`, plans composés officiels, capacités exactes, exports TS-rs et documentation stateful. +- [x] Valider localement `pre.017` : `kb_executor_solana_core` 46 tests, `kb_execution_api` 22, `kb_execution_safety` 15, `kb_execution_solana` 12 et `kb_rpc` 70. Clippy ne signale que quatre `cloned_ref_to_slice_refs` de tests, corrigés dans `pre.018`. + +#### Exhaustivité des builders natifs + +- [x] Compléter System avec les variantes seed, transferts seed, `create_account_allow_prefund` et le helper multi-instructions `transfer_many`. +- [x] Implémenter les builders officiels de création et lifecycle durable nonce : create/create-with-seed, initialize, advance, authorize, withdraw et upgrade, exécutés avec un recent blockhash normal. +- [x] Ajouter dans `kb_execution_solana` l’assemblage d’une transaction utilisant réellement un nonce durable : lecture complète bornée, validation exacte de l’état courant initialisé, `AdvanceNonceAccount` en première instruction, valeur nonce comme blockhash et preuve de simulation liée au message exact. +- [x] `pre.014` : compléter Compute Budget avec les quatre variantes client actuelles `request_heap_frame`, `set_compute_unit_limit`, `set_compute_unit_price` et `set_loaded_accounts_data_size_limit`, comparées aux builders officiels et protégées par des limites explicites. Conserver `Unused` et `RequestUnitsDeprecated` comme compatibilité de décodage seulement. +- [x] `pre.014` : rendre normative la documentation des APIs publiques dans les README des crates opérationnelles, documenter les contrats `kb_execution_api`, `kb_execution_safety`, `kb_execution_solana`, `kb_executor_solana_core` et auditer l’appariement des 102 surfaces décodeur/exécuteur réservées. +- [x] Valider localement `pre.014` : `kb_executor_solana_core` 21 tests, `kb_execution_api` 22 tests, `kb_execution_safety` 15 tests, `kb_execution_solana` 12 tests, `kb_rpc` 70 tests et Clippy global propre. +- [x] Implémenter les builders stateless Address Lookup Table create/extend/freeze/deactivate/close contre l’interface officielle, avec signataires exacts, ordre conservé et bornes d’input. +- [x] Ajouter l’orchestration ALT stateful : lecture/décodage du compte, autorité et état courants, capacité restante, slot récent, rent top-up mesuré, cooldown de fermeture et lifecycle localnet/devnet. +- [x] Implémenter les précompiles Ed25519, secp256k1 et secp256r1 avec formes inline et offsets, layouts officiels déterministes, références inter-instructions, bornes de taille et aucune dépendance `bincode` directe. +- [x] Valider localement `pre.016` et confirmer les six builders par tests officiels, exports TS-rs et Clippy global. +- [x] Implémenter Config create/store, Feature activate/revoke, les deux instructions Slashing et les douze vérifications plus fermeture ZK ElGamal officiellement appelables, même lorsqu’elles ne seront pas proposées dans la démo opérateur. +- [x] Classer l’ancien ZK Token Proof comme programme historique `decode-only` : aucun builder moderne ne doit être inventé pour le stub retiré du runtime actif. +- [x] Ajouter les orchestrations stateful de `pre.022` : rent et état Config/Feature, compte de preuve et fenêtre temporelle Slashing, préallocation/owner/space des contextes ZK ElGamal. +- [x] Implémenter Stake maximalement pour les vingt-quatre helpers clients actuels : create/initialize, checked/seed, split/merge, délégation, autorités, lockup, withdraw, deactivate, delinquent et move. Classer `Redelegate`/`redelegate_with_seed` comme historiques désactivés `decode-only`. +- [x] Implémenter dans `pre.019` Vote maximalement : vingt variantes wire actuelles, quatre créations composées legacy/V2 avec ou sans seed, autorités Ed25519/BLS, votes, TowerSync, commissions, collecteurs, retrait et dépôt de récompenses, avec tests unitaires avant tout essai devnet. +- [x] Implémenter dans `pre.020` les opérations Loader réellement constructibles : onze plans Loader v3 via `solana-loader-v3-interface`/`wincode` et neuf plans Loader v4 au wire officiel reproduit sans activer ses helpers `bincode`; conserver BPF Loader v1/v2 historiques en `decode-only` et Native Loader sans builder client. +- [x] Traiter dans `pre.020` la dette post-`0.4.1` : migrer le parser Loader v3 upgradeable vers `solana-loader-v3-interface ^8.0` avec `wincode`, préserver la compatibilité du booléen optionnel historique et ne pas activer les helpers `bincode` de Config, Loader v2 ou Loader v4. +- [x] Consolider dans `pre.021` les helpers internes répétés de `kb_executor_solana_core` : neuf parsers de clés, cinq parsers d’ID natif, validations communes de comptes distincts, montants positifs, longueurs exactes, dérivations seedées, wrappers d’erreur Loader et appender d’offsets `u16`; conserver séparées les sérialisations et validations dont le contrat reste propre à une surface. +- [x] Ajouter une matrice machine-readable programme/opération/builder officiel/politique/tests unitaires/tests cluster/exposition UI, validée automatiquement contre les 109 constantes d’opération et les 18 surfaces natives. + +#### Phasage restant de l’exhaustivité native + +- [x] `pre.015` : builders stateless Address Lookup Table create/extend/freeze/deactivate/close, avec autorité, ordre de comptes, payer optionnel et contrats de lifecycle exacts ; conserver rent, capacité, slot et cooldown dans la tranche stateful `pre.022`. +- [x] `pre.016` : builders Ed25519, secp256k1 et secp256r1, avec formes inline/offsets, références d’instructions, comparaison aux constructeurs ou layouts officiels, low-S secp256r1 et position secp256k1 explicitement bornée. +- [x] Validation locale de `pre.016` avant passage à `pre.017`. +- [x] `pre.017` : Config, Feature, Slashing et surfaces ZK officiellement appelables ; classification explicite de l’ancien ZK Token Proof comme historique `decode-only`. +- [x] Validation locale de `pre.017` avant passage à `pre.018`, avec quatre avertissements Clippy de tests reportés et corrigés dans le lot suivant. +- [x] `pre.018` : Stake maximal — vingt-quatre helpers clients actuels, autorités, lockup, création/rent fourni, délégation, merge/split, retraits, move et variantes checked/seed ; `Redelegate` reste historique désactivé. +- [x] Valider localement `pre.018` : `kb_executor_solana_core` 57 tests, `kb_execution_api` 22, `kb_execution_safety` 15, `kb_execution_solana` 12, `kb_rpc` 70 et Clippy global propre. +- [x] `pre.019` : Vote maximal — vingt variantes wire, quatre créations composées, initialisation legacy/V2, vote/update, autorisations, commissions, collecteurs, identité, retrait, récompenses, compact update et TowerSync. +- [x] Valider localement `pre.019` : `kb_executor_solana_core` 70 tests, `kb_execution_api` 22, `kb_execution_safety` 15, `kb_execution_solana` 12, `kb_rpc` 70 et Clippy global propre. +- [x] `pre.020` : onze opérations Loader v3 et neuf opérations Loader v4 réellement constructibles, avec déploiement/upgrade/autorités/fermeture hors UI; BPF Loader v1/v2 restent historiques `decode-only` et Native Loader est classé sans instruction client. +- [x] Valider localement `pre.020` : `kb_executor_solana_core` 81 tests, `kb_decoder_solana_core` 115, `kb_execution_api` 22, `kb_execution_safety` 15, `kb_execution_solana` 12 et `kb_rpc` 70. +- [x] `pre.021` : centraliser dans un module privé les parsers `Pubkey`/program ID et les validations communes de longueur, montant positif, comptes distincts, dérivation seedée et erreurs partagées ; supprimer les implémentations de production identiques sans modifier les 109 opérations ni leur wire. +- [x] Valider localement `pre.021` après `fix.001` : `kb_executor_solana_core` 86 tests et `cargo clippy --all-targets` propre ; ce Clippy couvre également les changements Loader de `pre.020`. +- [x] `pre.022` : implémenter le préflight stateful Localnet/Devnet pour ALT, Config, Feature, Slashing et contextes ZK ElGamal ; ajouter `getEpochInfo` typé et la matrice machine-readable complète des 18 surfaces natives avec classification de toute opération non invocable. +- [x] Valider localement `pre.022` : `kb_executor_solana_core` 87 tests, `kb_pipeline` 56 tests, `kb_rpc` 71 tests, `kb_execution_api` 22 tests, `kb_execution_safety` 15 tests, `kb_execution_solana` 12 tests, validateur de matrice réussi et `cargo clippy --all-targets` propre. +- [x] `pre.023` : remplacer le validateur Python de la matrice d’exécution par un test Rust fondé sur `SOLANA_CORE_OPERATION_CODES`; ajouter une matrice décodeur exacte de 18 surfaces/121 déclarations et l’inventaire canonique des 52 méthodes HTTP et neuf paires WebSocket standard. +- [x] Valider localement `pre.023` : `kb_executor_solana_core` 88 tests, `kb_decoder_solana_core` 116 tests, `kb_rpc` 78 tests et `cargo clippy --all-targets` propre. +- [x] `pre.024` : ajouter les adaptateurs configurables propres à `kb_rpc` des 38 méthodes HTTP standard auparavant limitées au contrat JSON brut ; conserver toutes les options officielles sélectionnables indépendamment, ne pas exposer les DTO Agave et imposer `52 typed / 0 raw` dans la matrice RPC. +- [x] Valider localement `pre.024` : `kb_rpc` 98 tests après correction explicite du type de `Vec` dans un test, puis `cargo clippy --all-targets` propre. +- [x] `pre.025` : déplacer le runtime WebSocket persistant de `kb_app_demo` vers `kb_rpc`, typer paramètres et notifications des neuf paires standard et garder les trois surfaces instables derrière une capacité explicite. +- [x] Valider localement `pre.025` après `fix.001` : `kb_rpc` 113 tests, `kb_app_demo` 87 tests et `cargo clippy --all-targets` propre ; confirmer la désactivation dynamique des méthodes WebSocket instables rejetées par le nœud. +- [x] `pre.026` : auditer les logs Tauri de clôture et identifier l’unique erreur applicative réelle comme une incompatibilité TS-rs `u64 -> bigint` lors de `demo_ws_unsubscribe`; les avertissements de confirmation opérateur et de requête SQL lente restent attendus et non bloquants. + +- [x] `pre.027` : premières projections Transfer Fee publiques dans `kb_materializer_fees`; validation 4/4, Token-2022 50/50, ElGamal Registry 5/5 et Clippy propre. +- [x] `pre.028` : sept feuilles Confidential Transfer Fee classées dans `EventFamily::Fee` et projetées sans déchiffrement ; validation Token-2022 50/50, ElGamal Registry 5/5, fees 6/6 et Clippy propre. +- [ ] `pre.029` : projeter les snapshots bornés Mint/Account/Multisig Token-2022, leur inventaire TLV ordonné et les états confidentiels publics dans `kb_materializer_token_accounts`. +- [x] `pre.026` : aligner les payloads Tauri WebSocket, backfill, SQL et configuration sur des nombres JSON, supprimer la construction frontend de `BigInt`, borner l’identifiant d’unsubscribe à `Number.isSafeInteger` et ajouter des tests de non-régression sur les déclarations TS-rs. +- [x] Valider la correction TS-rs de `pre.026` : `kb_config` 41 tests, `kb_app_demo` 88 tests, `cargo clippy --all-targets` propre, démarrage Tauri/Vite réussi et souscriptions/désabonnements WebSocket réels sans erreur `BigInt`. +- [x] `pre.026`/`pre.027` : terminer les audits README/API, enregistrer les validations offline, PostgreSQL, Devnet et RPC réelles, conserver les opérations administratives stateful derrière leurs préflights et sans activation Mainnet, puis clôturer la documentation et le `CHANGELOG.md`. + +#### Critères de clôture de l’exécuteur natif + +- [x] Chaque surface native du registre possède soit tous ses builders client officiellement appelables, soit une classification documentée `non invocable`, `retirée` ou `historique decode-only` dans la matrice canonique. +- [x] La couverture bibliothèque est indépendante de l’exposition dans `kb_app_demo` ; masquer une opération dangereuse dans l’UI ne permet pas de la considérer comme hors périmètre. +- [x] Toute API publique utile des crates d’exécution est inventoriée dans le README de sa crate avec rôle, paramètres, résultat, effets, limites et exemple minimal ; l’audit final couvre `kb_execution_api`, `kb_execution_safety`, `kb_execution_solana`, `kb_executor_solana_core`, `kb_rpc`, `kb_pipeline` et `kb_app_demo`. +- [x] Chaque builder est comparé offline à un encodeur ou layout officiel. Les opérations stateful non exercées réellement restent bloquées par le préflight, hors UI dangereuse et sans activation Mainnet ; une validation Localnet/Devnet par opération reste obligatoire avant toute future activation mutable. +- [x] La matrice machine-readable ne contient aucune opération callable sans statut, politique, tests offline, validation cluster et décision d’exposition UI ; le test Rust impose l’égalité exacte avec les 109 codes compilés. + +#### Critères de clôture du décodeur natif et de `kb_rpc` + +- [x] Les 18 Program IDs natifs du registre partagé correspondent exactement aux 18 surfaces du décodeur. +- [x] Les 121 déclarations de couverture sont comptées par surface et reliées à un test d’enum officielle ou de layout wire audité. +- [x] Les 52 méthodes HTTP et neuf paires WebSocket standard sont inventoriées sans omission dans un registre compilé et une matrice JSON. +- [x] Les 52 méthodes HTTP possèdent un contrat typé dédié ; la voie JSON brute ne constitue plus le contrat principal d’une méthode standard. +- [x] Les neuf paires WebSocket possèdent paramètres, notifications et runtime persistant dans `kb_rpc`, sans logique de transport lourde dans `kb_app_demo`. + +#### Démo et clôture + +- [x] Ajouter `demo_execution_solana_core` après le premier parcours backend Devnet validé ; exposer le transfert contrôlé et masquer les opérations administratives dangereuses. +- [x] Afficher plan, comptes, signataires requis, plafonds, cluster réel, simulation, signature/envoi et validation post-exécution sans jamais transférer de clé privée à la WebView. +- [x] Conserver `0.4.2` limité à la démo System transfer validée ; les créations/allocation/assignation et le nonce ne sont volontairement pas ajoutés à l’UI sans orchestrateur stateful cluster dédié. +- [x] Valider les builders offline contre les encodeurs officiels, valider le parcours Devnet représentatif et conserver Mainnet désactivé par défaut ; les autres opérations stateful restent conditionnées à une preuve cluster ultérieure avant exposition. +- [x] Clôturer `0.4.2` par tests ciblés, PostgreSQL réel, Clippy, parcours Devnet avec replay post-exécution, smoke tests RPC/Tauri, documentation finale et mise à jour du `CHANGELOG.md`. + +#### Validation finale observée — 13 juillet 2026 + +- [x] `kb_program_ids` 5 tests, `kb_decoder_solana_core` 116, `kb_executor_solana_core` 88, `kb_execution_api` 22, `kb_execution_safety` 15 et `kb_execution_solana` 12. +- [x] `kb_rpc` 113 tests, `kb_pipeline` 56, `kb_config` 41 et `kb_app_demo` 88. +- [x] `kb_store_pg` 45 tests avec PostgreSQL réel, migrations et roundtrips optionnels réussis. +- [x] Parcours Devnet opt-in réussi avec wallet persistant préfinancé, transfert de 1 000 000 lamports, simulation, signature, envoi, confirmation, hydratation canonique, extraction core et decode replay. +- [x] `cargo clippy --all-targets` sans avertissement. +- [x] `cargo tauri dev -c kb_app_demo/tauri.conf.json` validé avec Vite, initialisation PostgreSQL et sessions WebSocket réelles. +- [x] Les logs de clôture confirment les unsubscribe `slot`, `root` et `program`. Le refus de changer d’endpoint avec une session active est un garde-fou attendu. La limite SQL supérieure à 5 000 est désormais refusée dans le frontend avant l’appel backend. + +La clôture de `0.4.2` n’active pas Mainnet et n’étend pas l’UI aux opérations administratives. La suite active reprend avec `0.4.3 — SPL Memo v1, v3 et v4`. Le projet d’acquisition historique gratuite est volontairement retiré du ROADMAP actif et conservé séparément dans `docs/HISTORICAL_DATA_ACQUISITION_PLAN.md`, sans version attribuée. + +### Politique commune de tous les programmes Solana + +Pour chaque programme Solana, natif, SPL, DEX, launchpad ou autre, l'ordre obligatoire est : décodeur maximal et corpus, décision/ajout des matérialisateurs concernés, puis exécuteur limité aux opérations explicitement supportées. Le décodeur conserve tout wire identifiable, historique, courant, récent ou expérimental. Les matérialisateurs projettent maximalement les faits stables sans dupliquer le core ni une autre projection. L'exécuteur exclut les opérations obsolètes ou purement historiques, mais peut construire les opérations courantes ou expérimentales officiellement encodées indépendamment de leur déploiement par cluster. Une absence de matérialisation ou d'exécution doit être une décision documentée, jamais un oubli. Chaque exécuteur est validé après le décodeur, avec builder officiel, simulation/dry-run, politiques d'autorisation et tests devnet avant mainnet. + +#### Audit rétroactif des programmes déjà clôturés + +- [x] Solana natif : le décodeur conserve les wires historiques ; les absences de projection des précompiles et preuves ZK sont documentées ; BPF Loader v1/v2, ancien ZK Token Proof et Stake Redelegate restent `decode-only`, tandis que l'exécuteur couvre les opérations officiellement appelables. +- [x] SPL Memo : le décodeur v1/v3/v4 et la matérialisation des annotations commitées respectent la politique maximale sans doublon. +- [x] Corriger le contrat de `kb_executor_spl_memo` : v1 et v3 historiques deviennent `Unsupported(reason)`/`decode-only`; v4 reste la seule génération courante constructible et exécutée par la démo. +- [ ] Valider par Cargo la matrice, les capacités compilées et la régression Memo/exécution avant de commencer le code des matérialisateurs SPL Token. + +### 0.4.3 — SPL Memo v1, v3 et v4 + +- [x] Réserver `kb_decoder_spl_memo` et `kb_executor_spl_memo` pour les trois générations sans produire de faux événement ni activer d’envoi. +- [x] Suivre `prompts/022_v0_4_3_spl_memo.md` et auditer les contrats officiels/historiques exacts des trois program IDs ; `docs/SPL_MEMO_MATRIX.json` prouve notamment que v1 ignore les comptes alors que v3/v4 exigent chaque compte comme signer. +- [x] Implémenter le décodeur maximal synthétique : génération, payload UTF-8 ou tentative invalide, longueur/hash, comptes signataires ordonnés, outer/inner, transaction réussie ou échouée et couverture machine-readable ; compilation, 9 tests et Clippy validés après `pre.001-delta-fix-001`. +- [x] Matérialiser les annotations Memo commitées dans un domaine dédié, sans détourner une projection métier incompatible ni produire de mutation réussie pour une transaction échouée ; `pre.002` validé par les tests ciblés, le store PostgreSQL réel et Clippy. +- [x] Remplacer l’exécuteur réservé par un contrat typé exact pour les générations réellement constructibles, avec builders/layouts officiels, signataires déclarés, simulation/dry-run et validation post-exécution Devnet. +- [x] Clôturer le jalon avec corpus synthétique et réel, replay idempotent, tests ciblés, PostgreSQL réel si le store est touché, Clippy et documentation des trois crates concernées. + +#### `0.4.3-pre.001` — audit et décodeur Memo contextualisé + +- [x] Remplacer `DecoderSupport::Maybe` par une reconnaissance exacte des trois IDs et déplacer le target tracing vers `src/constants.rs`. +- [x] Implémenter le wire brut borné, l’UTF‑8, le texte complet, longueur, SHA-256, préfixe diagnostic, comptes ordonnés, signataires et chemin outer/inner. +- [x] Séparer `add_memo`, `memo_intent` et `invalid_memo_attempt`, sans jamais marquer committed une transaction échouée. +- [x] Enregistrer `spl_memo` dans `demo_decode_replay` à côté du décodeur natif. +- [x] Valider la compilation et les tests sur une machine disposant de Cargo : 9 tests Memo, 41 tests config, 89 tests démo et Clippy propre après `delta-fix-001`; le corpus réel reste requis pour la clôture globale du jalon. + +#### `0.4.3-pre.002` — annotations de transaction Memo + +- [x] Créer `kb_materializer_transaction_annotations` et la famille explicite `TransactionAnnotation`, sans détourner les projections existantes. +- [x] Projeter uniquement `add_memo` réussi et commité avec identité, texte, longueur, hash, signataires vérifiés, provenance et clé d’idempotence. +- [x] Refuser les transactions échouées et observations non commitées ; laisser les comptes complets, writable, diagnostics et preuves uniquement dans le decode. +- [x] Réutiliser `kb_sol_mat_events` et le ledger commun sans migration SQL ni duplication core/decode. +- [x] Enregistrer le matérialiseur dans `demo_decode_replay` et déclarer son target tracing dans les trois profils. +- [x] Valider les tests ciblés, Clippy et PostgreSQL réel : modèle 23, Memo 9, matérialiseur 7, API matérialiseur 2, pipeline 56, config 41, démo 89, store PostgreSQL 45 et Clippy propre. +- [x] Valider `pre.002-delta-fix-001` côté compilation et persistance : store core 40, Memo 9, pipeline 57, démo 93 et store PostgreSQL réel 46 ; corriger dans `delta-fix-002` l’assertion de matrice du matérialiseur, la garde de mutex signalée par Clippy et la limite de 64 filtres qui empêchait le démarrage Tauri avec 65 routes. +- [x] Valider le parcours opérateur Mainnet de `pre.002-delta-fix-002` : trois backfills de 100 transactions, core extraction 300/300, replay v1 102/102 avec 69 annotations et 33 refus, v3 439/439 avec 422 annotations et 17 refus, v4 100/100 avec 100 annotations, zéro unmatched ou failed input ; second replay v3 non forcé sélectionnant zéro entrée. +- [x] Valider le démarrage Tauri avec 65 routes de logging sans dépassement de la limite de filtres ni erreur opérationnelle. +- [x] Valider le test logging de `pre.002-delta-fix-003` : 17 tests réussis, y compris la composition de plus de 64 routes ; Clippy a encore identifié la durée lexicale d’un `MutexGuard` dans le test pipeline malgré son `drop` explicite. +- [x] Valider `pre.002-delta-fix-004` : pipeline 57 tests et `cargo clippy --all-targets` propres après fermeture lexicale du `MutexGuard`. + +#### `0.4.3-pre.003` — contrat typé et wire de l’exécuteur Memo + +- [x] Remplacer le plan réservé par `SplMemoGeneration`, `SplMemoOperation::AddMemo`, `SplMemoExecutionIntent` et `SplMemoSigner`. +- [x] À la clôture initiale de `0.4.3`, construire v1/v3/v4 avec `spl_memo_interface::instruction::build_memo`; cette capacité historique est ensuite remplacée par l'audit transversal de `0.4.4`, qui conserve uniquement v4 exécutable. +- [x] Conserver les doublons dans les metas tout en dédupliquant les signataires transactionnels requis. +- [x] Imposer simulation, plafond de frais, dépense nulle hors frais et validation post-exécution complète sans lier la bibliothèque à un cluster. +- [x] Valider alors la séparation cluster/simulation/politique commune ; l'audit transversal ultérieur classe v1/v3 `decode-only` sans modifier le caractère universel du builder v4. +- [x] Valider `pre.003` : exécuteur Memo 14, API 22, safety 15, Solana 12 et `cargo clippy --all-targets` propres. +- [x] Valider `pre.003-delta-fix-001` après retrait des restrictions réseau placées à tort dans la bibliothèque universelle : exécuteur 15 tests et Clippy propre. +- [x] Ajouter un parcours opérateur Devnet v4 réutilisant simulation, confirmation, hydratation, core extraction, decode replay, projection et idempotence. +- [x] Réutiliser le journal générique des matérialisations et la fenêtre d’exécution Devnet pour Memo v4 avec DTO Tauri JSON sans `bigint` actif. + +#### `0.4.3-pre.004` — orchestration backend Memo v4 Devnet + +- [x] Ajouter un request simulation-first, un plan Memo v4 à dépense nulle hors frais et une autorisation d’envoi explicite. +- [x] Réutiliser le wallet persistant, la vérification du genesis Devnet, le blockhash, les frais, la simulation liée au message, la signature et la confirmation communs. +- [x] Hydrater la signature exacte, extraire le core, rejouer `spl_memo`, vérifier la projection `transaction_annotation` puis exécuter un second replay non forcé. +- [x] Valider `pre.004-delta-fix-001` : pipeline 61, exécuteur Memo 15, API 22, safety 15, Solana 12 et Clippy propres ; test opt-in Devnet réussi en simulation puis avec envoi. + +#### `0.4.3-pre.005` — démonstration Tauri Memo v4 Devnet + +- [x] Étendre la fenêtre Solana Devnet existante sans créer une nouvelle WebView ni déplacer de logique métier dans l’application. +- [x] Exposer message UTF-8, signer wallet readonly, simulation, confirmation opérateur et diagnostic complet sans clé privée ni `bigint` Tauri. +- [x] Afficher confirmation, validation canonique/core/decode, nombre d’annotations et preuve d’idempotence. +- [x] Valider 96 tests démo, 61 tests pipeline, Clippy propre et démarrage Tauri/Vite. +- [x] Exécuter trois Memo v4 Devnet réels : trois confirmations, trois insertions canoniques, trois extractions, trois annotations et trois seconds replays `skipped`, sans échec ni nouvelle matérialisation. + +#### `0.4.3-pre.006` — consolidation documentaire et corpus Devnet + +- [x] Reporter dans `docs/SPL_MEMO_MATRIX.json` les trois signatures v4 Devnet confirmées, leurs annotations de 34 octets et leurs seconds replays idempotents. +- [x] Synchroniser les README du décodeur, du matérialiseur, de l’exécuteur, du pipeline et de la démo avec les limites réellement validées. +- [x] Conserver comme non-revendications explicites l’envoi v1/v3 sur Devnet, le déploiement Localnet des binaires historiques et les coûts hors simulation/plafond de frais. + +#### `0.4.3-pre.007` — clôture et prompt SPL Token + +- [x] Enregistrer les validations finales fournies par l’utilisateur, y compris PostgreSQL réel, le parcours Devnet opt-in avec envoi et Clippy propre. +- [x] Clôturer `0.4.3` dans le ROADMAP et le `CHANGELOG.md` sans modifier le code ni le schéma PostgreSQL. +- [x] Ajouter `prompts/023_v0_4_4_spl_token.md` et rendre `0.4.4 — SPL Token` actif. + +#### Validation finale observée — 15 juillet 2026 + +- [x] `kb_program_ids` 5 tests, `kb_decoder_spl_memo` 9, `kb_materializer_transaction_annotations` 7 et `kb_executor_spl_memo` 15. +- [x] `kb_execution_api` 22 tests, `kb_execution_safety` 15 et `kb_execution_solana` 12. +- [x] `kb_pipeline` 61 tests, `kb_rpc` 113 et `kb_app_demo` 96. +- [x] `kb_store_pg` 46 tests avec PostgreSQL réel, migrations et requêtes de matérialisation typées réussies. +- [x] Test opt-in Devnet exécuté avec `KB_DEVNET_MEMO_EXECUTION_TEST=1` et `KB_DEVNET_MEMO_SUBMIT=1` : simulation, envoi, confirmation, hydratation, extraction, replay, annotation et idempotence réussis. +- [x] `cargo clippy --all-targets` sans avertissement. +- [x] Démo Tauri/Vite validée avec simulation Memo et trois envois v4 Devnet réels ; chaque transaction a produit une annotation et un second replay `skipped`. + +`0.4.3` est clôturé. La suite active devient `0.4.4 — SPL Token` selon `prompts/023_v0_4_4_spl_token.md`. Le worker d’acquisition historique reste différé et hors ROADMAP actif. + +### 0.4.4 — SPL Token + +- [x] `0.4.4-pre.001` — auditer les 28 tags publiés par `spl-token-interface 3.0.0` et implémenter le décodeur contextualisé classique : wire borné, comptes, autorités, multisig, inner instructions, transactions échouées, return data explicitement indisponible et `Batch` non imbriqué. +- [x] Valider `pre.001-delta-fix-001` le 15 juillet 2026 : 7 tests `kb_decoder_spl_token` réussis et `cargo clippy --all-targets` propre ; le correctif conserve le manifeste parent `Batch`, aligne la fixture multisig et autorise les listes explicites de tags via `workspace.lints.clippy.manual_range_patterns`. + +#### `0.4.4-pre.002` — contrat documentaire et politique d'exécution + +- [x] Rendre normative la couverture historique/courante/récente/expérimentale des décodeurs, la matérialisation maximale sans doublons et l'exclusion des opérations obsolètes dans les exécuteurs. +- [x] Détailler les responsabilités `token_accounts`, `admin`, `risk` et `fees`, ainsi que le futur journal métier et la démo opérateur. +- [x] Corriger la matrice et le test Memo : v1/v3 decode-only, v4 exécutable sur tout cluster sous gouvernance commune ; 16 tests Memo et Clippy global validés après `fix-002`. + +#### `0.4.4-pre.003` — matérialisations SPL Token classiques + +- [x] Activer `kb_materializer_token_accounts` pour les initialisations de mint, compte et multisig, transferts, approvals/revocations, mint, burn, freeze/thaw, close, sync native et unwrap, uniquement lorsqu'ils sont réussis et commités. +- [x] Produire un journal de mutations instructionnelles avec signature, slot, chemin stable, opération, comptes, mint explicitement connu ou `null`, montant brut en chaîne, decimals wire éventuels, autorité, provenance et clé d'idempotence ; ne pas prétendre reconstruire le snapshot final du compte. +- [x] Étendre `kb_materializer_admin` sans régresser ses projections natives, pour `set_authority` et les configurations multisig Token clairement administratives. +- [x] Activer `kb_materializer_risk` pour des constats factuels : délégation/allowance, revoke, freeze/thaw, révocation d'autorité et configuration multisig structurellement faible ; ne produire aucun score arbitraire. +- [x] Conserver `kb_materializer_fees` inactif pour SPL Token classique et tester/documenter explicitement `no projection`, car ni les transfer fees Token-2022 ni les frais réseau core n'appartiennent à cette surface. +- [x] Dédupliquer les sorties parent/enfants de `Batch` par leur chemin dérivé et leur `output_key`, refuser toute observation non commitée et réutiliser `kb_sol_mat_events` avec le ledger commun. +- [x] Enregistrer les matérialisateurs actifs dans le replay commun et valider leur idempotence, sans migration PostgreSQL si le contrat générique suffit. +- [x] Valider `pre.003` le 15 juillet 2026 : tests des quatre matérialiseurs, pipeline, config et démo réussis, Clippy global propre ; replay Mainnet de 370 instructions, 371 sorties, huit refus correspondant exactement à huit transactions échouées, puis second replay idempotent avec zéro sélection. + +#### `0.4.4-pre.004` — contrat typé et builders de `kb_executor_spl_token` + +- [x] Remplacer le scaffold par des intents typés pour les 24 opérations courantes et expérimentales officiellement constructibles ; classer les quatre initialisations historiques obsolètes en `Unsupported(reason)` au lieu de les construire. +- [x] Appeler les builders officiels 3.0.0, préserver l'ordre des metas et séparer autorité simple, multisig, signataires transactionnels, montants bruts en chaînes, decimals, rent, SOL transféré et frais réseau. +- [x] Construire `UnwrapLamports` et `Batch` après preuve exacte de leurs signatures et layouts officiels ; séparer explicitement constructibilité, déploiement par cluster et autorisation d'envoi. +- [x] Imposer simulation, dry-run par défaut, plafond de frais et validation post-exécution, sans dépendance vers RPC, wallet, Tauri ou le décodeur. +- [x] Valider `pre.004-delta-fix-001` le 15 juillet 2026 : 15 tests `kb_executor_spl_token`, 22 tests API, 15 tests safety, 12 tests Solana, 61 tests pipeline et `cargo clippy --all-targets` propres. + +#### `0.4.4-pre.005` — préflight stateful et orchestration + +- [x] Ajouter dans le pipeline les lectures bornées des comptes Mint/Account/Multisig nécessaires aux préconditions : owner, mint, decimals, état, solde, native reserve, delegate/allowance, autorités, seuil M/N et rent des comptes à initialiser. +- [x] Séparer le test réseau du test standard : layouts et bornes restent offline ; `KB_DEVNET_SPL_TOKEN_PREFLIGHT_TEST=1` active un contrôle réel de `TransferChecked` sur des comptes Devnet explicitement fournis, sans signature ni dépense. +- [x] Valider le socle `pre.005` le 15 juillet 2026 : 67 tests `kb_pipeline`, 15 tests `kb_executor_spl_token`, Clippy global propre et préflight `TransferChecked` Devnet réel `Ready` sur deux comptes auxiliaires contrôlés du même mint. +- [x] `pre.005-delta-fix-001` : réutiliser l'orchestration commune pour préflight obligatoire, simulation liée au message exact, signature/envoi autorisé, confirmation, hydratation canonique, extraction core, replay Token, vérification des projections et second replay idempotent. +- [x] Exposer séparément une API de simulation sans store ni signature et refuser l'envoi si les signataires requis ne sont pas tous résolus par le wallet persistant du profil. +- [x] Valider le test opt-in d'orchestration le 15 juillet 2026 sur les comptes Devnet contrôlés : simulation seule puis envoi réel de `TransferChecked`, avec wallet isolé explicitement aligné sur l'autorité Token, confirmation, hydratation canonique, extraction core, replay et matérialisation réussis ; le contrat renforcé a produit la signature `5Q71KeomfhHBueDsD92C7L21vHUpFGZnZzCFPGaCDAMHx6yV4Jy2fMDzCpze8mNr2c1GLSRT93mmBaYy2RGZ95ZY`, une matérialisation et un second replay sans échec, refus ni nouvelle sortie. + +#### `0.4.4-pre.006` — lifecycle contrôlé Devnet sans ATA + +- [x] Ajouter un orchestrateur séquentiel pour des comptes Mint/Account rent-exempt, non initialisés et possédés par le programme Token classique, sans création ATA implicite. +- [x] Enchaîner onze transactions simulation-first : `InitializeMint2`, deux `InitializeAccount3`, `MintToChecked`, `TransferChecked`, `ApproveChecked`, `Revoke`, deux `BurnChecked` puis deux `CloseAccount`. +- [x] Exiger la même autorité simple résolue par le wallet de profil, une confirmation destructive explicite, la conservation exacte des montants bruts et une validation canonique/idempotente complète avant de passer à l'étape suivante. +- [x] `pre.006-delta-fix-001` : remplacer la dépendance à l'ancienne commande CLI `solana create-account` par une préparation interne qui génère trois keypairs éphémères, mesure le rent, construit et simule trois `SystemCreateAccount`, exige les deux signatures, confirme puis vérifie owner Token, tailles 82/165/165 et données entièrement nulles. +- [x] `pre.006-delta-fix-002` : rendre une séquence partielle reprenable depuis un index explicite seulement après récupération canonique de la signature prédécesseur, correspondance de l'opération matérialisée, second replay idempotent, puis pacing borné et émission immédiate des signatures suivantes. +- [x] Valider le lifecycle réel Devnet le 15 juillet 2026 : préparation interne des comptes 82/165/165, reprise contrôlée après throttling du RPC public, puis `MintToChecked`, `TransferChecked`, `ApproveChecked`, `Revoke`, deux `BurnChecked` et deux `CloseAccount` confirmés ; chaque étape récupérée ou envoyée produit exactement une matérialisation et un second replay idempotent. Le scénario multisig reste candidat pour l'audit final si son coût demeure raisonnable. + +#### `0.4.4-pre.007` — journal métier et démo opérateur + +- [x] Étendre la fenêtre d'exécution Devnet existante, sans nouvelle WebView par opération, avec un panneau Token représentatif `TransferChecked`, simulation par défaut et confirmation explicite avant envoi ; le lifecycle complet reste exercé par son scénario opt-in dédié. +- [x] Conserver une seule fenêtre d'exécution : le panneau Token réutilise le profil, le wallet, le verrou d'exécution, la simulation et la confirmation déjà communs aux parcours System et Memo. +- [x] Ajouter une vue bornée du journal `kb_sol_mat_events`, filtrable par signature, mint, compte, famille et opération, avec détail JSON typé et montants bruts sans perte. +- [x] Visualiser les initialisations, transferts, délégations, mint/burn, freeze/thaw, changements d'autorité, close et opérations wrapped SOL comme événements chronologiques ; ne pas afficher d'OHLC ni de faux solde final. +- [x] Afficher séparément résultat de préflight, simulation, confirmation, extraction, décodage, matérialisations attendues et preuve d'idempotence. +- [x] Valider `pre.007` le 15 juillet 2026 : 102 tests `kb_app_demo`, 80 tests pipeline, 15 tests exécuteur Token et Clippy global propres ; `cargo tauri dev` a démarré Vite et Tauri, chargé le journal borné et simulé `TransferChecked`. Le refus sans confirmation puis le refus d'un envoi exigeant une autorité externe au wallet de profil prouvent les deux garde-fous attendus, sans invalider le parcours backend Devnet déjà envoyé et matérialisé. + +#### `0.4.4-pre.008` — corpus, audit cluster et clôture + +- [x] Documenter dans la matrice les signatures Mainnet représentatives et le lifecycle Devnet, avec statut séparé pour interface, processor, builder, simulation, soumission et déploiement ; conserver Localnet non testé et `UnwrapLamports`/`Batch` sans revendication de déploiement cluster. +- [x] `pre.008-delta-fix-001` : ajouter le probe Devnet simulation-only de `Batch` et `UnwrapLamports` ; valider 81 tests pipeline et Clippy global propre le 16 juillet 2026. +- [x] `pre.008-delta-fix-002` : enregistrer les deux simulations Devnet réussies, sans signature ni envoi : `Batch` avec un enfant `TransferChecked` de montant nul en 270 compute units et `UnwrapLamports` d'un lamport depuis un compte wrapped SOL auxiliaire classique en 140 compute units, sans erreur runtime. Devnet expose donc les deux tags récents et aucun binaire Localnet p-token n'est requis pour pallier une absence de déploiement. +- [x] `pre.008-delta-fix-003` : aligner le test de non-invention cluster sur la preuve Devnet réelle tout en exigeant que Localnet/Mainnet restent non testés et que les signatures réelles restent vides pour les deux simulations sans envoi ; validation ciblée 8/8 et Clippy global propre le 16 juillet 2026. +- [x] `pre.008-delta-fix-004` : aligner les assertions Memo sur `matrixVersion = 4` et compléter, dans les trois profils exemple, les routes console et fichier de `kb_executor_spl_token` ; validation 9/9 Memo, 7/7 annotations, 41/41 configuration, 102/102 démo et Clippy global propre le 16 juillet 2026. +- [x] Exécuter la régression ciblée, PostgreSQL réel, Clippy global et Tauri/Vite : 77 routes chargées, simulation `TransferChecked` réussie, refus sans confirmation vérifié, matérialisation commitée exacte consultée et simulation Memo v4 réussie ; synchroniser README/CHANGELOG et clôturer `0.4.4` avant ATA. +- [x] `pre.008-delta-fix-005` : publier la clôture documentaire `0.4.4`, activer `0.4.5 — SPL Associated Token Account` et fournir `prompts/024_v0_4_5_spl_associated_token_account.md` sans commencer son implémentation. + +### 0.4.5 — SPL Associated Token Account + +Règle de couverture du jalon : décoder maximalement toute variante officiellement identifiable, qu'elle soit historique, courante ou expérimentale ; matérialiser maximalement chaque fait stable et prouvé sans chevauchement entre propriétaires ; exécuter toute variante actuelle officiellement constructible, en excluant seulement les opérations historiques, obsolètes ou qui ne sont plus d'actualité. Un remaniement reste autorisé lorsqu'il est nécessaire pour respecter ces contrats, à condition d'être borné, justifié, documenté et couvert par les régressions. + +- [x] `pre.001` : auditer `spl-associated-token-account-interface 2.0.0`, les trois variantes Borsh publiées, les builders officiels, l'ordre et les flags des comptes, la dérivation PDA `[wallet, Token Program ID, mint]` et le processor de référence `program@v8.0.0` ; publier `docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json` et quatre tests d'égalité interface/matrice/builders/PDA. Résolution Cargo `2.0.0`, 4/4 tests et Clippy global validés le 16 juillet 2026 ; version workspace corrigée à `0.4.5` ; tests de matrice réintégrés dans `decoder.rs` pour conserver l'organisation homogène des décodeurs existants. Corpus canonique et égalité des binaires cluster encore non revendiqués. +- [x] `pre.002` : remplacer le scaffold par le décodeur ATA exact pour toutes les variantes officiellement identifiables de l'interface et du processor résolus, actuellement `Create`, `CreateIdempotent` et `RecoverNested` ; couvrir wire Borsh et `Create` historique vide, comptes ordonnés, flags, doublons, dérivations PDA, Token classique/Token-2022, outer/CPI, succès/échec, données inconnues/tronquées/suffixées et diagnostics bornés ; compléter le corpus offline et l'égalité matrice/couverture compilée sans commencer l'exécuteur. Validation opérateur du 16 juillet 2026 : ATA 10/10, configuration 41/41, pipeline 81/81, application 102/102 et Clippy global propre après `pre.002-delta-fix-001`. +- [x] `pre.003` : matérialiser maximalement les faits ATA commités avec propriétaires explicites : étendre `kb_materializer_token_accounts` pour `ata_created`, `ata_created_or_reused_idempotently` et `nested_ata_recovered`, puis étendre `kb_materializer_risk` pour le fait distinct `nested_ata_anti_pattern_recovered` sans score arbitraire. Documenter et tester l'absence de projection dans `kb_materializer_lifecycle` afin d'éviter le doublon de lifecycle, dans `kb_materializer_admin` car aucune autorité n'est modifiée et dans `kb_materializer_fees` car le wire ATA ne prouve aucun montant exact de rent ou de frais. Validation opérateur du 16 juillet 2026 : token_accounts 9/9, risk 6/6, lifecycle 17/17, admin 9/9, fees 3/3, décodeur ATA 10/10, application 103/103, pipeline 81/81 et Clippy global propre. Le replay PostgreSQL réel reste réservé à la validation cluster/finale. +- [x] `pre.004` : remplacer le scaffold exécuteur par les trois variantes actuelles officiellement constructibles `Create`, `CreateIdempotent` et `RecoverNested` ; comparer chaque plan au builder officiel et ajouter le préflight stateful pour dérivation, mint, compte existant, RecoverNested, rent, plafonds et signataires. `pre.004-delta-fix-001` ajoute les types `solana_pubkey::Pubkey` explicites après l'échec d'inférence `E0282`. Validation opérateur du 16 juillet 2026 : exécuteur ATA 10/10, pipeline 84/84 et Clippy global propre. +- [x] `pre.005` : valider l'orchestration Devnet simulation-first, la post-validation stateful adaptée aux créations et à RecoverNested, la reprise bornée après `429`, la confirmation, l'extraction, le replay ATA et CPI Token classique, la matérialisation et le second replay idempotent ; intégrer Create/CreateIdempotent et le journal lifecycle borné dans la fenêtre Solana existante. Validation opérateur du 16 juillet 2026 : décodeur ATA 10/10, exécuteur ATA 10/10, pipeline 90/90, configuration 41/41, application 111/111 et Clippy global propre ; `cargo tauri dev -c kb_app_demo/tauri.conf.json` démarre Vite, Tauri, les 83 routes de journalisation et le schéma PostgreSQL réel. Deux soumissions Devnet contrôlées `CreateIdempotent` classiques ont été simulées, confirmées, extraites, décodées et matérialisées exactement une fois, puis leur second replay n'a produit aucun doublon. Le mint Token-2022 contrôlé `3DxK…KTE` a ensuite créé puis réutilisé l'ATA `6WfC…C9Y` aux slots 476702448 et 476703343 ; les deux signatures ont réussi le parcours complet et leur second replay sans doublon. L'ATA indépendant mesure 170 octets, appartient à Token-2022, conserve le wallet/mint attendus et expose `ImmutableOwner`. +- [x] `pre.006` : valider le test opt-in Devnet `RecoverNested` hors UI avec fixture classique contrôlée, préflight des trois PDA, simulation, transfert de 1 000 000 000 unités brutes, fermeture du nested ATA, conservation de l'owner ATA, projections lifecycle/risk distinctes, deux matérialisations exactes et second replay idempotent sous la signature `37LG…HskM`. Les suites pipeline 90/90, décodeur ATA 10/10, exécuteur ATA 10/10 et Clippy global restent propres après `pre.006-delta-fix-001`. +- [x] `pre.006-delta-fix-003` : clôturer `0.4.5` après la régression complète. Validation opérateur du 16 juillet 2026 : Program IDs 5/5, Token classique decode 8/8 et exécuteur 15/15, Memo 9/9, annotations 7/7, token accounts 9/9, risk 6/6, lifecycle 17/17, admin 9/9, fees 3/3, API d'exécution 22/22, safety 15/15, transaction Solana 12/12, pipeline 90/90, RPC 113/113, configuration 41/41, application 111/111 et PostgreSQL réel 46/46 ; Clippy global propre. Les manifests Cargo héritent tous de `workspace.package.version = 0.4.5`, l'interface ATA résolue reste `2.0.0`, et les versions npm/Tauri de la démo sont synchronisées en `0.4.5`. README, matrice, dépendances d'interface, CHANGELOG et prompt `0.4.6` sont publiés seulement après ces preuves. +- [x] Décoder maximalement `Create`, `CreateIdempotent`, `RecoverNested` et la forme historique vide officiellement identifiable. +- [x] Couvrir les ATA ciblant SPL Token et Token-2022 sans commencer le décodage général Token-2022. +- [x] Matérialiser maximalement le lifecycle ATA et le fait de risque nested sans dupliquer les changements Token, puis développer l'exécuteur maximal des opérations actuelles après validation des projections. + +### 0.4.6 — Token-2022, extensions et registre ElGamal — clôturé + +`0.4.6` clôt la couverture Token-2022 et le registre ElGamal comme deux frontières techniques distinctes. Token-2022 conserve son Program ID, sa matrice, ses événements, ses états TLV, ses matérialisateurs et ses capacités d’exécution propres. Le registre ElGamal conserve séparément son Program ID, son état de 64 octets, son PDA, ses opérations administratives et ses preuves. Le programme natif ZK ElGamal Proof reste une troisième surface indépendante. + +- [x] Résoudre et documenter les interfaces réellement utilisées : `spl-token-2022-interface 3.1.1`, `spl-elgamal-registry-interface 0.2.1`, `solana-zk-elgamal-proof-interface 0.1.3`, Group `0.7.2`, Metadata `1.0.1` et TLV `0.9.1`. +- [x] Publier `docs/SPL_TOKEN_2022_MATRIX.json` et `docs/SPL_ELGAMAL_REGISTRY_MATRIX.json` avec frontières, wire, comptes, états, capacités et preuves séparés. +- [x] Décoder les instructions Token-2022 de base, les familles d’extensions identifiables, Token Metadata/Group incorporés, les parcours confidentiels et les instructions outer/CPI, sans fusionner les implémentations tierces. +- [x] Parser les préfixes Mint, Account et Multisig, le discriminant de type de compte et la région TLV bornée, y compris metadata variable, groupes et extensions publiques/confidentielles sans inventer de valeur déchiffrée. +- [x] Décoder et parser séparément le registre ElGamal, avec `create_registry`, `update_registry`, PDA officiel, owner, taille et état de 64 octets. +- [x] Attribuer chaque fait stable à un matérialiseur propriétaire unique : token accounts, metadata, admin, fees, risk/compliance ou annotations ; préserver la provenance et refuser les transactions échouées. +- [x] Exposer les exécuteurs typés Token-2022 et ElGamal Registry avec simulation obligatoire, dry-run par défaut, signataires exacts, préflights stateful, preuves inline/context-state, plafonds et postconditions explicites. +- [x] Valider sur Devnet huit opérations publiques Token-2022 : `MintToChecked`, `TransferChecked`, `ApproveChecked`, `Revoke`, `BurnChecked`, `FreezeAccount`, `ThawAccount` et `CloseAccount`, avec hydratation, extraction, replay, matérialisation et second replay idempotent. +- [x] Valider ElGamal Registry synthétiquement/offline : builders, exact-wire, PDA, état, lectures RPC bornées, preuves et orchestration. La validation réseau publique reste explicitement indisponible sur Devnet/Mainnet au 20 juillet 2026 ; aucune preuve cluster n’est inventée. +- [x] Valider le corpus Mainnet/PostgreSQL : 55 instructions Token-2022 décodées sans échec, aucune duplication decode/mat, et correction du wire `Write` du loader immuable sur 68 instructions Mainnet. +- [x] Qualifier les rejets natifs restants : 31 `immutable_loader_tag_truncated`, 44 `loader_v4_tag_truncated` et 1 `loader_accounts_invalid`, tous dans des transactions Solana échouées ; quatre variantes Loader v4 inconnues restent explicitement `Unsupported`. +- [x] Ajouter le mode `incomplete_signatures` dans le replay de `kb_app_demo` : sélection des signatures contenant au moins une instruction `pending`, `failed`, `replay_requested` ou `unsupported`, limite appliquée aux signatures, expansion aux instructions compatibles et réévaluation sans force replay global. +- [x] Valider deux campagnes PostgreSQL réelles `incomplete_signatures` identiques : 81 entrées sélectionnées, 1 décodée, 4 unsupported, 76 failed, aucun unmatched, aucun not-started et aucune duplication de matérialisation. +- [x] Valider la régression finale : `kb_store_core` 41/41, `kb_store_pg` 46/46 avec PostgreSQL réel, `kb_pipeline` 124/124, `kb_app_demo` 118/118 et `cargo clippy --all-targets` propre. +- [x] Publier le bilan final dans `docs/V0_4_6_VALIDATION.md` et préparer `prompts/026_v0_4_7_metaplex_token_metadata.md`. + +#### Modèle de couverture des tokens et actifs numériques + +- [x] Les tokens fongibles et les NFT basés sur un mint restent décodés au niveau des mouvements par `kb_decoder_spl_token` ou `kb_decoder_spl_token_2022`; il n’existe pas d’instruction Token distincte nommée « NFT ». +- [x] La classification fongible/NFT/SFT est une projection dérivée de la supply, des decimals, des authorities et des standards metadata observés; elle ne doit jamais remplacer l’adresse canonique du mint ou de l’actif. +- [x] Les NFT classiques et programmables liés à un mint sont couverts par le futur `kb_decoder_metadata_metaplex_token_metadata` (`0.4.7`). +- [x] Les actifs Metaplex Core, qui ne sont pas des mints SPL Token classiques, sont couverts séparément en `0.4.8`. +- [x] Les NFT compressés exigent Bubblegum en plus d’Account Compression et Noop; leur couverture complète est planifiée en `0.4.9`. +- [x] Les metadata Token-2022 incorporées, Metaplex externes et implémentations tierces de Token Metadata Interface conservent leur provenance et ne sont jamais fusionnées silencieusement. + +### 0.4.7 — Metaplex Token Metadata + +Ce jalon est prioritaire immédiatement après Token-2022. Metaplex Token Metadata n'est pas un programme SPL officiel : il s'agit d'un programme indépendant de la Metaplex Foundation, utilisé comme standard de metadata externe pour SPL Token classique et Token-2022. Son Program ID canonique est `metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s` et doit être revérifié depuis les sources officielles au début du jalon. + +### Audit et dépendances + +- [x] Lire `RULES.md`, les contrats decode/materialization/execution/replay, les matrices SPL Token/Token-2022, `docs/SOLANA_INTERFACE_DEPENDENCIES.md` et le prompt `026` avant tout code. +- [x] Ouvrir l’audit des crates et sources officielles Metaplex réellement disponibles : SDK Rust `mpl-token-metadata ^5.1`, Program ID, IDL `1.14.0`, inventaire généré des instructions et séparation maintenu/historique. Les comptes, builders et PDA détaillés restent à prouver par variante. +- [x] Créer `docs/METAPLEX_TOKEN_METADATA_MATRIX.json` comme matrice d’audit machine-readable avant le décodage maximal : sources officielles figées, inventaire SDK courant, frontière historique IDL, familles de PDA/comptes et statuts de preuve. L’enrichissement exhaustif par instruction et compte reste requis avant l’activation runtime. +- [x] Ne pas introduire Anchor dans ce jalon : Token Metadata utilise ses propres layouts Borsh et interfaces ; l'infrastructure Anchor reste en `0.4.18`, juste avant les protocoles qui en dépendent réellement. +- [ ] Conserver `wincode ^0.5` tant que les crates Solana utilisées exposent leurs implémentations `SchemaRead`/`SchemaWrite` contre `wincode 0.5.x`; ne pas migrer isolément le workspace vers `0.6`. + +### Journal de préversions + +- `0.4.7-pre.001` : ouverture de l’audit officiel, ajout de `mpl-token-metadata ^5.1`, création de la matrice initiale et des squelettes `kb_decoder_metadata_metaplex_token_metadata` et `kb_executor_metadata_metaplex_token_metadata`; aucun décodeur runtime ni builder/exécuteur actif. +- `0.4.7-pre.002` : activer le dispatcher contextualisé et le décodage Borsh exact du premier groupe création/mise à jour (`CreateMetadataAccountV3`, `UpdateMetadataAccountV2`), avec comptes ordonnés, outer/CPI, transactions échouées non committées, bornes et diagnostics fail-closed; garder l’enregistrement runtime, la matérialisation et l’exécution pour les groupes suivants. +- `0.4.7-pre.003` : ajouter le groupe de vérification de collection (`VerifyCollection`, `UnverifyCollection`, `SetAndVerifyCollection`) avec discriminateurs officiels, comptes ordonnés, autorité de collection, payer/update authority, `collection_authority_record` optionnel, outer/CPI, échecs non committés et diagnostics fail-closed; conserver matérialisation, exécution et registre runtime pour des préversions dédiées. +- `0.4.7-pre.004` : décodage du groupe sized collection item (`VerifySizedCollectionItem`, `UnverifySizedCollectionItem`, `SetAndVerifySizedCollectionItem`). +- `0.4.7-pre.005` : décoder l’administration des autorités de collection (`ApproveCollectionAuthority`, `RevokeCollectionAuthority`) avec records PDA, autorités distinctes, comptes système/rent et contrats signer/writable exacts. +- `0.4.7-pre.006` : décoder les usages et autorités d’usage (`ApproveUseAuthority`, `RevokeUseAuthority`, `Utilize`) avec compteurs `u64`, `use_authority_record`, burner, comptes Token/ATA, comptes système/rent, outer/CPI et échecs non committés. +- `0.4.7-pre.007` : décoder la signature de creator et la déclaration de première vente (`SignMetadata`, `UpdatePrimarySaleHappenedViaToken`) avec comptes ordonnés, signer/writable exacts, outer/CPI, échecs non committés et diagnostics fail-closed. +- `0.4.7-pre.008` : décoder la normalisation et la classification metadata (`PuffMetadata`, `RemoveCreatorVerification`, `SetTokenStandard`) avec edition optionnelle, comptes ordonnés et diagnostics fail-closed. +- `0.4.7-pre.009` : décoder `CreateMetadataAccountV2` historique (discriminant `16`) avec `DataV2`, mutabilité, comptes exacts et politique non exécutable. +- `0.4.7-pre.010` : décoder `CreateMetadataAccount` et `UpdateMetadataAccount` historiques (discriminants `0` et `1`) avec layouts `Data` V1 bornés, comptes exacts et politique non exécutable. +- `0.4.7-pre.011` : décoder `CreateMasterEditionV3` et la conversion historique `ConvertMasterEditionV1ToV2`, avec `max_supply`, comptes exacts et politique d’exécution distincte. +- `0.4.7-pre.012` : décoder le mint d’éditions imprimées via token et la variante historique vault proxy (`MintNewEditionFromMasterEditionViaToken`, `MintNewEditionFromMasterEditionViaVaultProxy`), avec numéro d’édition `u64`, edition marker et comptes exacts. +- `0.4.7-pre.013` : décoder `SetCollectionSize` et le bridge `BubblegumSetCollectionSize`, avec taille `u64`, autorités/records exacts et frontière Bubblegum explicite. +- `0.4.7-pre.014` : décoder `Delegate` et `Revoke` programmables, leurs enums Borsh, 14 comptes positionnels et placeholders optionnels. +- `0.4.7-pre.015` : décoder `Transfer`, `Lock`, `Unlock`, `Burn` et `CloseAccounts`, avec enums Borsh, montants `u64`, comptes programmables exacts et transactions échouées non committées. +- `0.4.7-pre.016` : réauditer exhaustivement l’index officiel des instructions Rust avant le décodage des comptes, puis décoder `FreezeDelegatedAccount` et `ThawDelegatedAccount` historiques avec discriminants `26`/`27`, cinq comptes exacts, outer/CPI, échecs non committés et politique decode-only. +- `0.4.7-pre.017` : décoder `BurnNft`, `BurnEditionNft` et `DeprecatedMintNewEditionFromMasterEditionViaPrintingToken` historiques avec discriminateurs `29`/`37`/`3`, comptes exacts, comptes optionnels de fin, outer/CPI, échecs non committés et politique decode-only. +- `0.4.7-pre.018` : décoder `CreateEscrowAccount`, `CloseEscrowAccount`, `TransferOutOfEscrow` et `Collect`, avec montant `u64`, authorities optionnelles, comptes exacts et diagnostics fail-closed. +- `0.4.7-pre.019` : décoder le wrapper moderne `Create` (`42`) avec `CreateArgs::V1`, neuf comptes positionnels et placeholders optionnels exacts. +- `0.4.7-pre.020` : décoder le wrapper moderne `Mint` (`43`) avec `MintArgs::V1`, montant `u64`, `AuthorizationData`, quinze positions et placeholders optionnels exacts. +- `0.4.7-pre.021` : décoder le wrapper moderne `Print` (`55`) avec `PrintArgs::V1`/`V2`, édition `u64`, dix-huit positions et Token Record optionnel exact. +- `0.4.7-pre.022` : décoder le wrapper moderne `Update` (`50`) et toutes les variantes `UpdateArgs` V1/V2, avec onze positions et placeholders optionnels exacts. +- `0.4.7-pre.023` : décoder les wrappers modernes `Use` (`51`), `Verify` (`52`) et `Unverify` (`53`) avec leurs enums Borsh, comptes positionnels, placeholders optionnels, outer/CPI et échecs non committés. +- `0.4.7-pre.024` : décoder `Resize` (`56`), `Migrate` (`48`) et les six discriminateurs historiques manquants `2`/`5`/`6`/`8`/`9`/`10`, puis prouver une couverture exhaustive des 58 discriminateurs `0..=57`. +- `0.4.7-pre.025` : décoder le compte `Metadata`, vérifier owner, discriminant `MetadataV1`, bornes officielles et PDA canonique `[metadata, program_id, mint]`, sans encore enregistrer le décodeur stateful dans le pipeline. +- `0.4.7-pre.026` : décoder `DeprecatedMasterEditionV1`, `MasterEditionV2` et `EditionV1`, vérifier owner, layouts bornés et PDA commun `[metadata, program_id, mint, edition]`, tout en conservant V1 comme état historique decode-only. +- `0.4.7-pre.027` : décoder `EditionMarker` et `EditionMarkerV2`, vérifier owner, bitmaps, groupes de 248 éditions, seeds PDA V1/V2 et longueurs Borsh exactes. +- `0.4.7-pre.028` : décoder `TokenRecord`, vérifier owner, layout fixe de 80 octets, PDA mint/token account, bump stocké, états programmables, delegate, rôle et locked transfer. +- `0.4.7-pre.029` : décoder `MetadataDelegateRecord`, `HolderDelegateRecord`, `CollectionAuthorityRecord` et `UseAuthorityRecord`, avec longueurs, rôles-seeds, PDA et bumps exacts. +- `0.4.7-pre.030` : décoder `TokenOwnedEscrow`, distinguer `TokenOwner`/`Creator`, vérifier les deux familles de seeds PDA, longueurs Borsh et bump stocké, puis documenter les transitions stateful escrow/migrate/resize sans les matérialiser prématurément. +- `0.4.7-pre.031` : décoder `ReservationListV1/V2` historiques, préserver compteurs, supply snapshot, caches et PDA dépendant de `resource`, avec limites officielles et politique decode-only. + +- `0.4.7-pre.032` : inventaire exhaustif des quinze variantes `Key`, liaison de chaque layout à son décodeur, sentinelle `Uninitialized` explicitement rejetée et audit fail-closed des futures variantes upstream. +- `0.4.7-pre.033` : ajouter l’enveloppe canonique des snapshots, l’identité stable `program_id:account`, la version de layout, les classes actif/historique/expérimental et la provenance live/replay/backfill/repair ; rendre explicite que les comptes historiques sont matérialisables mais jamais exécutables. +- `0.4.7-pre.034` : règles décodeur/matérialiseur/exécuteur renforcées ; projection canonique commitée de `MetadataV1`, snapshot owned autoritatif et absence de doublon d’état avec les observations d’instructions. +- `0.4.7-pre.035` : matérialiser les autres comptes Metaplex actifs, expérimentaux et historiques avec identité stable, lifecycle, provenance et propriétaire unique des faits. + +#### Audit canonique des comptes `0.4.7` + +- [x] Auditer les quinze variantes `Key` publiées (`0..=14`) et relier chaque compte réel à un décodeur borné. +- [x] Rejeter explicitement `Key::Uninitialized` et toute variante future inconnue sans projection partielle. +- [x] Vérifier par test les discriminateurs Borsh du SDK épinglé, l’unicité de l’inventaire et sa correspondance avec la matrice. +- [x] Définir l’identité stable canonique `program_id:account`, le nom de layout, le discriminant et la version canonique. +- [x] Distinguer explicitement lifecycle, politique de matérialisation et politique d’exécution. +- [x] Autoriser la matérialisation des comptes historiques comme état historique de backfill/replay, sans jamais les exposer à l’exécuteur. +- [x] Conserver source, slot, write version, signature, index outer/inner et statut committed dans la provenance canonique. + +#### Découpage prévisionnel restant de `0.4.7` + +- `pre.036` : intégration du décodeur de comptes dans `kb_pipeline`, routage owner/discriminant, replay, fermeture de comptes, ordering et déduplication. +- `pre.037` : corrélation instructions/comptes avant-après et vérification stateful des observations décodées. +- `pre.038` : exécuteur moderne de base (`Create`, `Update`, `Mint`, `Print`, `Verify`, `Unverify`, `Use`, `Resize`, `Migrate`) avec builders officiels et simulation-first. +- `pre.039` : exécuteur programmable (`Delegate`, `Revoke`, `Transfer`, `Lock`, `Unlock`, `Burn`) avec Token Records et Authorization Rules. +- `pre.040` : exécuteur collection/use/admin, en excluant formellement les opérations historiques. +- `pre.041` : preflight stateful complet, postconditions et replay de confirmation idempotent. +- `pre.042` : intégration `kb_app_demo`, bindings et scénarios PostgreSQL/replay/dry-run. +- `pre.043` : corpus Mainnet de signatures et comptes historiques/actifs. +- `pre.044` : validations Devnet et Localnet des opérations encore appelables. +- `pre.045` : audit de non-duplication et conflits de provenance Metaplex externe / Token-2022 incorporée / Metaplex Core. +- `pre.046` : audit final, documentation, changelog, prompt de session suivant et clôture de `0.4.7`. + +### Frontière du décodeur + +- [x] Créer le squelette canonique `kb_decoder_metadata_metaplex_token_metadata`, borné au Program ID Metaplex vérifié, sans enregistrement runtime ni prétention de décodage dans `0.4.7-pre.001`. +- [x] Créer le squelette canonique `kb_executor_metadata_metaplex_token_metadata`, audit-only et sans builder runtime avant validation exacte des comptes, autorités, coûts, confirmations et postconditions stateful. +- [ ] Réutiliser le Program ID déjà enregistré dans `kb_program_ids`, puis ajouter les capacités au registre runtime et aux options Tauri seulement après validation du premier corpus, sans heuristique fondée seulement sur un PDA ou un discriminant. +- [ ] Préserver signature, slot, outer/CPI, chemin d'instruction, statut de transaction, payload hash, comptes ordonnés, signers/writable et provenance de layout. +- [ ] Produire des diagnostics stables et bornés pour discriminant inconnu, payload tronqué/suffixé, comptes manquants/supplémentaires, PDA incohérent, owner incorrect, enum future et chaîne/vecteur hors limites. +- [ ] Classer les variantes futures ou non prouvées en `Unsupported`, et les payloads structurellement invalides en `Failed`, sans projection partielle trompeuse. + +### Décodage des instructions + +- [x] Inventorier et couvrir les générations historiques et actuelles publiées de création et mise à jour de metadata (`CreateMetadataAccount`, V2, V3, `UpdateMetadataAccount`, V2). +- [ ] Couvrir création de master edition, edition, edition marker et mint d'éditions, avec limites numériques et PDA exacts. + - [x] Décoder `CreateMasterEditionV3` et `ConvertMasterEditionV1ToV2` avec wire et comptes exacts. + - [x] Décoder le mint d'éditions via token et vault proxy, avec numéro d'édition `u64`, edition marker et comptes exacts. + - [ ] Décoder et valider les comptes edition/edition marker ainsi que leurs PDA dans la tranche de décodage d’état. +- [x] Couvrir vérification/dé-vérification de créateurs et collections, collection size/details et autorités de collection. + - [x] Décoder `SetCollectionSize` et `BubblegumSetCollectionSize` avec taille `u64`, flags exacts et record optionnel. + - [ ] Valider les PDA/owners des collection authority records lors du décodage des comptes on-chain. +- [x] Couvrir uses, approve/revoke use authority et consommation d'usage avec comptes exacts. +- [x] Couvrir délégations et révocations metadata/collection/use/sale/transfer/utility/staking/programmable lorsqu'elles sont officiellement définies. +- [x] Couvrir les opérations programmables actuelles : transfer, lock, unlock, burn, delegate, revoke et authorization data/rule set lorsque le wire exact est prouvé. La validation stateful des rule sets et PDA reste planifiée séparément. +- [x] Couvrir les migrations et instructions historiques encore observables sans les confondre avec les variantes actuelles. + - [x] Décoder `FreezeDelegatedAccount` et `ThawDelegatedAccount` historiques avec leurs cinq comptes exacts et les conserver decode-only. + - [x] Décoder les burns NFT historiques et le mint déprécié via printing token (`BurnNft`, `BurnEditionNft`, `DeprecatedMintNewEditionFromMasterEditionViaPrintingToken`). + - [x] Décoder les instructions escrow/collect (`CreateEscrowAccount`, `CloseEscrowAccount`, `TransferOutOfEscrow`, `Collect`) avec wire et comptes exacts. + - [x] Décoder le wrapper moderne `Create` (`42`) avec `CreateArgs::V1`, neuf comptes positionnels et placeholders optionnels exacts. + - [x] Décoder le wrapper moderne `Mint` (`43`) avec `MintArgs::V1`, quinze comptes positionnels et placeholders optionnels exacts. + - [x] Décoder le wrapper moderne `Print` (`55`) avec `PrintArgs::V1`/`V2`, dix-huit comptes positionnels et Token Record optionnel exact. + - [x] Décoder le wrapper moderne `Update` (`50`) et ses variantes `V1`/`As…V2` avec wire et comptes exacts. + - [x] Décoder les wrappers modernes `Use`, `Verify` et `Unverify` avec leurs variantes et contrats positionnels exacts. + - [x] Décoder `Resize` et `Migrate`, couvrir les six discriminateurs historiques manquants `2`/`5`/`6`/`8`/`9`/`10` et prouver l’inventaire exhaustif `0..=57`. +- [x] Préserver tous les champs wire significatifs : autorités, creators, seller fee basis points, collection, uses, token standard, collection details, programmable config et authorization data pour les instructions actuellement couvertes. + +### Intégration `kb_pipeline` et `kb_app_demo` + +- [ ] Enregistrer le décodeur dans le registre runtime de `kb_pipeline` après stabilisation de la matrice d’instructions et des comptes. +- [ ] Ajouter les tests `kb_pipeline` pour outer/CPI, transactions échouées, replay, déduplication et routage vers les matérialiseurs. +- [ ] Ajouter les tests `kb_pipeline` de matérialisation idempotente et d’orchestration d’exécution simulation-first. +- [ ] Enregistrer décodeur, matérialiseurs et exécuteur dans `kb_app_demo`, puis couvrir les campagnes replay et les scénarios PostgreSQL. +- [ ] Valider dans `kb_app_demo` les refus explicites des instructions historiques/non supportées et les commandes Tauri éventuelles. + +### Décodage des comptes et PDA + +- [ ] Parser et versionner `Metadata`, `MasterEditionV1/V2`, `Edition`, `EditionMarker`, `EditionMarkerV2`, `TokenRecord`, `MetadataDelegateRecord`, `CollectionAuthorityRecord`, `UseAuthorityRecord` et tout autre compte officiel confirmé. + - [x] `pre.025` : parser `MetadataV1`, conserver les champs descriptifs et administratifs, et produire une projection JSON bornée. + - [x] `pre.026` : parser `DeprecatedMasterEditionV1`, `MasterEditionV2` et `EditionV1`, conserver supply/max supply, printing mints historiques, parent et numéro d’édition. + - [x] `pre.027` : parser `EditionMarker` et `EditionMarkerV2`, conserver ledger, groupe V1, index d’octet, masque et état de l’édition demandée. + - [x] `pre.028` : parser `TokenRecord`, conserver bump, état programmable, révision du rule set, delegate, rôle et locked transfer. + - [x] `pre.029` : parser les records Metadata/Holder Delegate, Collection Authority et Use Authority avec leurs champs exacts. + - [x] `pre.030` : parser `TokenOwnedEscrow`, conserver base token, autorité TokenOwner/Creator et bump. + - [x] `pre.031` : parser `ReservationListV1/V2`, conserver Master Edition, supply snapshot, réservations et caches historiques en decode-only. +- [ ] Vérifier l'owner du compte avant tout parsing et refuser les comptes étrangers même si leur payload ressemble au layout attendu. + - [x] `pre.025` : appliquer cette frontière au compte `Metadata` avant toute désérialisation. + - [x] `pre.026` : appliquer la même frontière aux comptes edition/master edition avant toute désérialisation. + - [x] `pre.027` : appliquer cette frontière aux Edition Markers avant toute lecture du discriminant ou du ledger. + - [x] `pre.028` : appliquer cette frontière au Token Record avant toute lecture de l’état programmable. + - [x] `pre.029` : appliquer cette frontière aux quatre familles de records avant désérialisation. + - [x] `pre.030` : appliquer cette frontière à Token Owned Escrow avant lecture de l’autorité. + - [x] `pre.031` : appliquer cette frontière aux Reservation Lists avant lecture de leur génération historique. +- [ ] Vérifier les PDA canoniques et l'ordre exact des seeds pour metadata, edition/master edition, edition markers, token records, delegates, collection authorities et use authorities. + - [x] `pre.025` : valider le PDA Metadata exact `[b"metadata", program_id, mint]` et conserver le bump. + - [x] `pre.026` : valider le PDA edition/master edition exact `[b"metadata", program_id, mint, b"edition"]` et conserver le bump. + - [x] `pre.027` : valider le PDA V1 `[metadata, program_id, mint, edition, edition/248 décimal]` et le PDA V2 `[metadata, program_id, mint, edition, marker]`. + - [x] `pre.028` : valider le PDA Token Record `[metadata, program_id, mint, token_record, token_account]` et le bump stocké. + - [x] `pre.029` : valider les PDA exacts des records Metadata/Holder Delegate, Collection Authority et Use Authority. + - [x] `pre.030` : valider les PDA escrow TokenOwner `[metadata, program_id, base_token, 0, escrow]` et Creator `[metadata, program_id, base_token, 1, creator, escrow]`, ainsi que le bump stocké. + - [x] `pre.031` : valider le PDA Reservation List `[metadata, program_id, master_edition, reservation, resource]` avec contexte resource explicite. +- [ ] Borner strictement noms, symboles, URI, creators, options, enums et données d'autorisation ; distinguer taille minimale, taille exacte et compte extensible. + - [x] `pre.025` : borner Metadata à 1 024 octets, name à 32, symbol à 10, URI à 200 et creators à 5. + - [x] `pre.026` : borner edition/master edition à 128 octets et exiger exactement 41 octets pour `EditionV1`. + - [x] `pre.027` : exiger 32 octets pour V1, une longueur Borsh sans suffixe pour V2 et un ledger V2 borné à 1 048 576 octets. + - [x] `pre.028` : exiger exactement 80 octets pour Token Record et refuser troncature, suffixe, clé ou bump incohérents. + - [x] `pre.029` : exiger 98 octets pour les delegate records, 10 pour Use Authority et les deux tailles Borsh Collection Authority. + - [x] `pre.030` : exiger 35 octets pour TokenOwner et 67 pour Creator, sans suffixe. + - [x] `pre.031` : limiter les listes à 200 entrées, 6 949 octets pour V1 et 9 749 pour V2, avec padding nul uniquement. +- [ ] Corréler mint, token account, edition, collection et rule set uniquement lorsque toutes les identités sont prouvées. +- [ ] Conserver explicitement la provenance `metaplex_token_metadata`, distincte de `token_2022_embedded_metadata` et de `metaplex_core`. + +### Matérialisation + +- [ ] Étendre `kb_materializer_metadata` comme propriétaire unique des faits descriptifs : nom, symbole, URI, seller fee, creators, collection, uses, token standard, mutabilité, collection details, programmable config et provenance. + - [x] `pre.034` : matérialiser `MetadataV1` comme snapshot owned autoritatif, stable et commité, sans doublon avec les observations d’instructions. + - [x] `pre.035` : matérialiser Edition/Master Edition, Edition Marker et Reservation Lists historiques avec lifecycle et provenance explicites. +- [ ] Étendre `kb_materializer_admin` pour update authority, collection authority, delegates, rule set authority et changements d'autorité prouvés. + - [x] `pre.035` : projeter les Delegate Records et Authority Records avec `kb_materializer_admin` déclaré comme propriétaire unique du fait administratif. +- [ ] Étendre `kb_materializer_lifecycle` pour création, mise à jour, édition, verify/unverify, mint d'édition, burn, lock/unlock et transitions programmables prouvées. + - [x] `pre.035` : projeter Token Record et Token Owned Escrow avec `kb_materializer_lifecycle` déclaré comme propriétaire unique du fait de lifecycle. +- [ ] Étendre `kb_materializer_risk`/`compliance_audit` pour creators non vérifiés, metadata mutable, royalties, délégations sensibles, rule sets et conflits de provenance, sans dupliquer les faits metadata. +- [ ] Définir une identité canonique stable par mint/asset + source + compte metadata, avec `event_key`/`output_key` idempotents. + - [x] `pre.033–035` : utiliser `program_id:account` et un `output_key` stable par compte pour les snapshots actifs, expérimentaux et historiques. +- [ ] Conserver simultanément metadata Metaplex externe et metadata Token-2022 incorporée ; exposer source, autorité, slot, priorité de lecture et conflits sans fusion silencieuse. +- [ ] Refuser toute matérialisation engagée issue d'une transaction Solana en échec. + +### Exécuteur et préflight + +- [ ] Créer ou finaliser `kb_executor_metadata_metaplex_token_metadata` uniquement après audit des builders officiels et de leurs comptes exacts. +- [ ] Définir des intents typés pour les opérations officiellement constructibles ; retourner `Unsupported(reason)` pour les surfaces sans builder/wire suffisamment prouvé. +- [ ] Préserver l'ordre exact des metas, signers, writable, PDA, authorities, payer, token program et system/sysvar programs. +- [ ] Imposer simulation-first, dry-run par défaut, plafond de frais/dépense et confirmation opérateur dédiée pour burn, authority changes, verify/unverify, delegate/revoke, lock/unlock et opérations programmables. +- [ ] Ajouter les prélectures stateful : owner, layout/version, PDA, mint/token account, update authority, collection authority, delegate, token standard, rule set, état locked/burned et cohérence avec SPL Token/Token-2022. +- [ ] Définir des postconditions exactes et un replay de confirmation idempotent avant d'autoriser une opération comme réussie. +- [ ] Ne jamais télécharger le JSON externe référencé par URI dans le chemin critique de construction, simulation ou confirmation on-chain. + +### JSON externe et sécurité + +- [ ] Concevoir le fetch HTTP/IPFS/Arweave comme composant optionnel séparé du décodeur canonique on-chain. +- [ ] Borner timeout, taille, content type, redirections, nombre de tentatives, cache et concurrence ; conserver URL finale, hash, provenance et date d'observation. +- [ ] Bloquer SSRF, localhost, réseaux privés/link-local et schémas non autorisés ; ne jamais faire confiance aux champs du JSON distant. +- [ ] Ne jamais faire dépendre le statut `Decoded` on-chain de la disponibilité ou de la validité du contenu externe. +- [ ] Autoriser une première livraison sans fetch actif si l'isolation et les politiques réseau ne sont pas encore entièrement prouvées. + +### Corpus, replay et validation + +- [ ] Ajouter un corpus synthétique couvrant chaque instruction, compte, PDA et branche de diagnostic, avec valeurs limites et variantes historiques/currentes. +- [ ] Ajouter des signatures Mainnet réelles pour token fongible, NFT, SFT, collection sized/unsized, master edition, edition et programmable NFT. +- [ ] Couvrir outer et CPI, transactions réussies et échouées, comptes dupliqués/manquants, mauvais Program ID/owner/PDA, payload tronqué/suffixé et discriminant inconnu. +- [ ] Ajouter des cas de conflit entre metadata Metaplex externe et metadata Token-2022 incorporée, sans perte de provenance. +- [ ] Enregistrer le décodeur et les materializers dans `kb_pipeline` et `kb_app_demo`, avec démonstration de lecture des observations/projections. +- [ ] Vérifier le replay PostgreSQL idempotent, l'absence de doublons decode/materialization et le mode `incomplete_signatures` sans `forceReplay` global. +- [ ] Valider avec PostgreSQL réel, tests ciblés, tests pipeline/app demo, bindings Tauri si modifiés et `cargo clippy --all-targets` propre. + +### Documentation et clôture + +- [ ] Mettre à jour les README des crates touchées, `README.md`, `docs/SOLANA_INTERFACE_DEPENDENCIES.md`, la matrice dédiée et les contrats de provenance metadata. +- [ ] Documenter précisément les surfaces observées sur Mainnet, celles seulement synthétiques, les variantes `Unsupported` et les limites du fetch externe. +- [ ] Mettre à jour `CHANGELOG.md` et cocher ce jalon seulement après validations réelles. +- [ ] Préparer le prompt suivant `0.4.8 — Metaplex Core` sans mélanger les actifs Core avec les mints SPL/Token-2022. + +### 0.4.8 — Metaplex Core + +Metaplex Core est un standard d’actifs indépendant qui ne repose pas sur un mint SPL Token pour représenter chaque NFT. Il doit donc disposer de sa propre frontière de programme et ne pas être traité comme une simple variante de metadata SPL. + +- [ ] Créer `kb_decoder_metaplex_core` avec Program IDs, comptes Asset/Collection et plugins audités depuis les sources Metaplex officielles. +- [ ] Décoder les opérations et états des actifs Core, collections, authorities, royalties, attributes et plugins sans les fusionner avec Metaplex Token Metadata. +- [ ] Matérialiser une identité d’actif canonique avec provenance `metaplex_core`, distincte des mints SPL Token/Token-2022. +- [ ] Ajouter exécuteur, corpus, démo et replay seulement après validation exacte des comptes et autorités. + +### 0.4.9 — SPL Account Compression, Noop et Metaplex Bubblegum + +Ce jalon remonte avant les pools de stake : Bubblegum dépend directement d’Account Compression et de Noop, et complète la couverture NFT ouverte par Metaplex Token Metadata et Metaplex Core. + +- [x] Réserver `kb_executor_spl_account_compression`, `kb_executor_spl_noop` et `kb_executor_nft_metaplex_bubblegum` sans activer d’envoi. +- [ ] Implémenter `kb_decoder_spl_account_compression` et `kb_decoder_spl_noop`. +- [ ] Finaliser `kb_decoder_nft_metaplex_bubblegum` pour les instructions, arbres, feuilles et metadata des NFT compressés ; ne pas considérer Compression/Noop seuls comme un décodeur NFT complet. +- [ ] Corréler Bubblegum, Account Compression et Noop sans fusionner leurs frontières de programme ni dupliquer les événements. +- [ ] Matérialiser l’identité des actifs compressés avec arbre, leaf index, asset id, owner/delegate, collection et metadata prouvées. +- [ ] N’activer les exécuteurs qu’après validation des preuves Merkle, coûts et builders officiels. + +### 0.4.10 — SPL Stake Pool + +- [ ] Décoder maximalement `SPoo1...` dans `kb_decoder_spl_stake_pool`. +- [ ] Distinguer le pool SPL du Stake Program natif, adapter `staking`, `reward`, `fee`, `admin` et `lifecycle`, puis développer `kb_executor_spl_stake_pool` après validation du décodeur. + +### 0.4.11 — SPL Single Pool + +- [x] Réserver `kb_executor_spl_single_pool` sans activer d’envoi. +- [ ] Implémenter `kb_decoder_spl_single_pool` pour `SVSPx...`. +- [ ] Couvrir et matérialiser lifecycle, dépôts/retraits, mint LST et récompenses, puis activer `kb_executor_spl_single_pool` seulement après validation des projections. + +### 0.4.12 — SPL Token Wrap + +Token Wrap est un programme SPL actuel et audité, destiné au wrapping bidirectionnel entre SPL Token classique et Token-2022. Il ne doit pas être classé avec les programmes historiques. + +- [ ] Ajouter le Program ID canonique `TwRapQCDhWkZRrDaHfZGuHxkZ91gHDRkyuzNqeU5MgR` à `kb_program_ids` après vérification finale des sources officielles. +- [ ] Créer `kb_decoder_spl_token_wrap` et auditer instructions, comptes d’état, PDA, mints source/wrapped et contrats de conversion. +- [ ] Matérialiser le lien canonique entre mint source et mint wrapped sans fusionner leurs balances, supplies, authorities ou metadata. +- [ ] Créer `kb_executor_spl_token_wrap` uniquement après preuve des builders, coûts, decimals, ratios et conditions de fermeture/unwrapping. +- [ ] Ajouter corpus réel, démo, préflight et replay PostgreSQL idempotent. + +### 0.4.13 — SPL Token Swap + +SPL Token Swap reste dans la série `0.4.x` : il s’agit d’un programme SPL autonome avec Program ID propre, même si son domaine fonctionnel est un AMM. Les phases AMM ultérieures couvriront les protocoles tiers et réutiliseront les abstractions établies ici. + +- [ ] Vérifier et enregistrer le Program ID officiel `SwapsVeCiPHMUAtzQWZw7RjsKjgCjhwU55QGu4U1Szw` dans `kb_program_ids`. +- [ ] Créer `kb_decoder_spl_token_swap` et auditer instructions, courbes, fees, états de pool, autorités et compatibilité Token classique/Token-2022. +- [ ] Matérialiser pools, liquidité, swaps, fees et lifecycle sans dupliquer les mouvements Token sous-jacents. +- [ ] Créer `kb_executor_spl_token_swap` seulement après preuve des courbes, slippage, comptes ordonnés, builders et simulations. +- [ ] Ajouter corpus réel, démo, préflight et replay PostgreSQL idempotent. + +### 0.4.14 — SPL Token Lending + +SPL Token Lending reste également dans `0.4.x` : c’est un programme SPL autonome avec Program ID propre. Les phases Lending ultérieures couvriront Solend, Kamino et les autres protocoles dérivés ou indépendants. + +- [ ] Vérifier et enregistrer le Program ID officiel `6TvznH3B2e3p2mbhufNBpgSrLx6UkgvxtVQvopEZ2kuH` dans `kb_program_ids`. +- [ ] Créer `kb_decoder_spl_token_lending` et auditer markets, reserves, obligations, collateral, liquidations, flash loans, oracles et autorités. +- [ ] Matérialiser lending, borrowing, collateral, interest, liquidation, fees et risk sans dupliquer les transferts Token. +- [ ] Créer `kb_executor_spl_token_lending` uniquement après validation des calculs, oracles, slippage, health factor et simulation. +- [ ] Ajouter corpus réel, démo, préflight et replay PostgreSQL idempotent. + +### 0.4.15 — SPL Name Service + +- [x] Réserver `kb_executor_metadata_spl_name_service` sans activer d’envoi. +- [ ] Implémenter `kb_decoder_metadata_spl_name_service` pour `namesLPne...`. +- [ ] Décoder puis matérialiser metadata/admin/lifecycle des noms, puis activer `kb_executor_metadata_spl_name_service` après validation des autorités et coûts. + +### 0.4.16 — SPL Governance et programmes utilitaires déployés + +- [ ] Auditer les déploiements et versions encore pertinents de SPL Governance, Feature Proposal, Record, Shared Memory et Instruction Padding depuis leurs dépôts maintenus ou forks officiels identifiés. +- [ ] Créer des jalons internes séparés par Program ID ; ne pas regrouper leurs wire formats dans un décodeur générique « SPL utilities ». +- [ ] Prioriser Governance si des stratégies ou materializations de vote/DAO en dépendent ; conserver Feature Proposal, Record, Shared Memory et Instruction Padding comme surfaces d’infrastructure à priorité moindre. +- [ ] Distinguer strictement les programmes déployés, les interfaces sans Program ID universel et les implémentations tierces. + +### 0.4.17 — SPL historique ou archivé + +- [ ] Auditer Managed Token, Stateless Asks, Token Upgrade, Binary Option et Binary Oracle Pair avant toute réservation de crate ou Program ID. +- [ ] Auditer Token Collection et les anciennes implémentations Token Metadata/Token Group uniquement comme sources historiques ou compatibilité, sans les confondre avec les interfaces actuelles. +- [ ] Ne pas attribuer un Program ID universel aux interfaces Token Group, Token Metadata ou Transfer Hook ; dispatcher vers les implémentations observées. + +### 0.4.18 — infrastructure de décodage Anchor + +Anchor est un framework et un ensemble de conventions, pas un programme Solana unique. Les programmes SPL planifiés dans `0.4.x` utilisent leurs propres interfaces et n’exigent pas cette couche. Le jalon Anchor est donc placé immédiatement avant Meteora afin de fournir l’infrastructure commune requise par Meteora, Pump, Raydium et les autres programmes Anchor, sans remplacer leurs décodeurs protocolaires ni accepter un discriminant isolé comme preuve d’identité. + +- [ ] Auditer les versions Anchor réellement rencontrées, les discriminateurs d’instruction et de compte, les événements `Program data:`, les erreurs, les IDL historiques/courantes et les différences entre générations. +- [ ] Définir un registre versionné `Program ID + provenance IDL + hash/version + cluster + plage de slots` ; refuser tout décodage lorsqu’aucun descripteur vérifié ne correspond au programme et à sa version observée. +- [ ] Créer une couche commune pour les enveloppes, comptes, événements et erreurs Anchor, tout en laissant aux crates Meteora/Pump/Raydium leurs Program IDs, leur sémantique métier, leurs invariants, leurs matérialisations et leurs exécuteurs. +- [ ] Décoder outer et CPI avec chemins exacts, comptes ordonnés, discriminateurs vérifiés, arguments Borsh bornés et politique explicite pour suffixes, troncatures et versions inconnues. +- [ ] Extraire les événements Anchor depuis les logs sans les confondre avec des logs texte arbitraires, conserver l’ordre d’émission et rattacher chaque événement à l’instruction/CPI source lorsque la pile d’invocation le permet. +- [ ] Décoder les comptes Anchor uniquement avec owner/program ID, discriminant et layout IDL vérifiés ; conserver les comptes inconnus ou versions futures comme `Unsupported`, jamais comme état partiellement inventé. +- [ ] Définir les contrats de fallback pour les programmes sans IDL publique, les IDL obsolètes, les upgrades et les collisions de discriminateurs entre Program IDs. +- [ ] Ajouter une matrice machine-readable, des corpus synthétiques/Mainnet et des tests anti-collision prouvant qu’un même discriminant sous deux Program IDs ne partage jamais silencieusement le même sens. +- [ ] Raccorder `incomplete_signatures` afin qu’une nouvelle IDL ou version de descripteur puisse reprendre uniquement les signatures Anchor non complètement décodées. +- [ ] Préparer `0.5.x — Meteora` : les décodeurs Meteora doivent consommer cette infrastructure pour le wire commun, tout en conservant leurs frontières protocolaires propres. + +### 0.4.19 — clôture Solana Core/SPL et metadata adjacente + +- [ ] Exécuter le replay complet des corpus `0.3.x` et des corpus SPL dédiés. +- [ ] Vérifier couverture, matérialisations, idempotence, versions de processor et absence de régression. +- [ ] Produire la documentation des événements core/SPL disponibles pour les stratégies. + +## Version 0.5.x — Meteora + +Les surfaces AMM et de liquidité précèdent le launchpad DBC dans l’ordre de développement. + +### 0.5.0 — corpus et surfaces Meteora + +- [ ] Constituer les corpus dédiés DLMM, DAMM v1, DAMM v2, DBC et Vault. +- [ ] Vérifier tous les Program IDs dans `kb_program_ids`. +- [ ] Auditer IDL/interfaces, comptes, événements, autorités et frais. +- [ ] Établir la matrice complète instructions/events/materializations/executors. + +### 0.5.1 — Meteora DLMM + +- [ ] Décoder maximalement instructions, événements, bins, positions et lifecycle. +- [ ] Matérialiser pools, bins, positions, liquidité, swaps, fees et changements administratifs. +- [ ] Implémenter `kb_executor_dlmm_meteora` après validation du décodeur, avec slippage, limites de liquidité, autorités et coûts bornés. + +### 0.5.2 — Meteora DAMM v1 + +- [ ] Décoder maximalement instructions, événements et états de pool. +- [ ] Matérialiser pools, liquidité, swaps, fees et lifecycle sans dupliquer les mouvements Token. +- [ ] Implémenter `kb_executor_amm_meteora_damm_v1` pour les opérations client officiellement appelables. + +### 0.5.3 — Meteora DAMM v2 + +- [ ] Décoder maximalement instructions, événements et états de pool. +- [ ] Matérialiser pools, liquidité, swaps, fees et lifecycle. +- [ ] Implémenter `kb_executor_amm_meteora_damm_v2` avec quotes, slippage, plafonds et simulation. + +### 0.5.4 — Meteora DBC + +- [ ] Décoder maximalement le launchpad DBC après stabilisation des AMM et Vault. +- [ ] Matérialiser créations, courbes, migrations, liquidité, paramètres et risques observables. +- [ ] Implémenter `kb_executor_launchpad_meteora_dbc` pour les opérations client officiellement appelables. + +### 0.5.5 — Meteora Vault + +- [ ] Auditer et décoder les surfaces Vault et complémentaires réellement confirmées. +- [ ] Matérialiser dépôts, retraits, parts, stratégies, fees et lifecycle. +- [ ] Implémenter `kb_executor_vault_meteora` uniquement pour les opérations appelables validées. + +### 0.5.6 — régression Meteora + +- [ ] Fusionner les corpus Meteora et rejouer toutes les surfaces. +- [ ] Vérifier interactions, idempotence et régressions croisées. +- [ ] Valider les cinq exécuteurs offline et sur localnet/devnet, puis enregistrer coûts, autorités et exposition dans la matrice. + +## Version 0.6.x — Pump + +Les surfaces AMM Pump Swap sont traitées avant le launchpad Pump Fun. + +### 0.6.0 — corpus et surfaces Pump + +- [ ] Constituer des corpus dédiés Pump Swap, Pump Fun et Pump Fees. +- [ ] Vérifier tous les Program IDs dans `kb_program_ids`. +- [ ] Établir la matrice complète instructions/events/materializations. + +### 0.6.1 — Pump Swap + +- [ ] Décoder toutes les instructions et événements connus. +- [ ] Matérialiser trades, liquidité, lifecycle et changements administratifs. +- [ ] Implémenter `kb_executor_amm_pump_swap` maximalement pour les opérations client appelables, avec quotes/plafonds/slippage et comparaison au contrat officiel. + +### 0.6.2 — Pump Fun + +- [ ] Décoder toutes les instructions et événements connus, y compris les entrées non directement liées au trading. +- [ ] Matérialiser créations, migrations, trades, liquidité, paramètres et risques observables. +- [ ] Implémenter `kb_executor_launchpad_pump_fun` après stabilisation du décodeur : builders officiels/IDL validés, autorités, coûts, slippage, simulation et tests devnet ; l’exposition trading reste une décision séparée. + +### 0.6.3 — Pump Fees + +- [ ] Décoder toutes les instructions et événements connus. +- [ ] Matérialiser les signaux de frais, buyback, partage, donation et changements d’autorité utiles à la gestion du risque. +- [ ] Implémenter `kb_executor_admin_pump_fees` pour les opérations administratives officiellement appelables, avec politiques d’autorité renforcées et aucune exposition UI par défaut. + +### 0.6.4 — régression Pump + +- [ ] Fusionner les corpus Pump et rejouer toutes les surfaces. +- [ ] Vérifier les interactions et régressions croisées. +- [ ] Valider les trois exécuteurs Pump offline et sur devnet/localnet, puis enregistrer leur statut exact dans la matrice décodeur/matérialiseur/exécuteur. + +## Version 0.7.x — Raydium + +Les surfaces AMM précèdent le launchpad LaunchLab. + +### 0.7.0 — corpus et surfaces Raydium + +- [ ] Constituer les corpus AMM v4, CPMM, CLMM, Stable Swap et LaunchLab. +- [ ] Vérifier tous les Program IDs et versions de programme dans `kb_program_ids`. +- [ ] Auditer instructions, événements, états, autorités, fees et courbes. +- [ ] Établir la matrice complète instructions/events/materializations/executors. + +### 0.7.1 — Raydium AMM v4 + +- [ ] Décoder maximalement instructions, événements et comptes historiques/courants. +- [ ] Matérialiser pools, liquidité, swaps, fees et lifecycle. +- [ ] Implémenter `kb_executor_amm_raydium_lp_v4` après preuve des builders et limites. + +### 0.7.2 — Raydium CPMM + +- [ ] Décoder maximalement instructions, événements et états CPMM. +- [ ] Matérialiser pools, liquidité, swaps, fees et lifecycle. +- [ ] Implémenter `kb_executor_cpmm_raydium` avec quotes, slippage et plafonds. + +### 0.7.3 — Raydium CLMM + +- [ ] Décoder maximalement instructions, événements, ticks, positions et rewards. +- [ ] Matérialiser pools, ticks, positions, liquidité, swaps, fees et rewards. +- [ ] Implémenter `kb_executor_clmm_raydium` avec bornes de ticks, slippage et coûts. + +### 0.7.4 — Raydium Stable Swap + +- [ ] Auditer les surfaces actuelles et historiques confirmées sans inventer de programme générique. +- [ ] Décoder et matérialiser uniquement les contrats dont le Program ID et le wire sont prouvés. +- [ ] N’activer un exécuteur qu’après identification d’une interface ou crate canonique. + +### 0.7.5 — Raydium LaunchLab + +- [ ] Décoder maximalement LaunchLab après stabilisation des AMM. +- [ ] Matérialiser créations, migrations, liquidité, paramètres et risques observables. +- [ ] Implémenter `kb_executor_launchpad_raydium_launchlab` pour les opérations client appelables. + +### 0.7.6 — régression Raydium + +- [ ] Fusionner les corpus Raydium et rejouer toutes les surfaces. +- [ ] Vérifier interactions, idempotence et régressions croisées. +- [ ] Valider les exécuteurs confirmés offline/localnet/devnet et enregistrer coûts, slippage et autorités. + +## Version 0.8.x — Orca + +La surface AMM Whirlpool précède le launchpad Wavebreak. + +### 0.8.0 — corpus et surfaces Orca + +- [ ] Constituer les corpus Whirlpool, Wavebreak et surfaces complémentaires confirmées. +- [ ] Vérifier tous les Program IDs et versions dans `kb_program_ids`. +- [ ] Auditer instructions, événements, comptes, authorities, fees et matrice commune. + +### 0.8.1 — Orca Whirlpool + +- [ ] Décoder maximalement instructions, événements, ticks, positions et rewards. +- [ ] Matérialiser pools, ticks, positions, liquidité, swaps, fees et rewards. +- [ ] Implémenter `kb_executor_clmm_orca_whirlpool` avec slippage, bornes de ticks, positions, liquidité et autorités. + +### 0.8.2 — Orca Wavebreak + +- [ ] Décoder maximalement le launchpad après stabilisation de Whirlpool. +- [ ] Matérialiser créations, migrations, liquidité, paramètres et risques observables. +- [ ] Implémenter `kb_executor_launchpad_orca_wavebreak` pour les opérations client officiellement appelables. + +### 0.8.3 — régression Orca + +- [ ] Fusionner les corpus Orca et rejouer toutes les surfaces. +- [ ] Vérifier interactions, idempotence et régressions croisées. +- [ ] Valider les exécuteurs offline/localnet/devnet et enregistrer leur statut dans la matrice. + +## Version 0.9.x — Jupiter, routeurs et surfaces complémentaires + +- [ ] Décoder Jupiter Aggregator v4/v6 et les routes multi-hop, puis implémenter `kb_executor_router_jupiter_aggregator_v4` et `kb_executor_router_jupiter_aggregator_v6` après validation des contrats encore appelables. +- [ ] Décoder Jupiter DCA puis implémenter `kb_executor_router_jupiter_dca` pour les opérations officiellement appelables. +- [ ] Décoder Jupiter Limit Order v1/v2 puis implémenter `kb_executor_orderbook_jupiter_limit_order` et `kb_executor_orderbook_jupiter_limit_order_v2`. +- [ ] Décoder Jupiter Perpetuals puis implémenter `kb_executor_perpetuals_jupiter` avec politiques de marge, collateral, levier, prix et liquidation renforcées. +- [ ] Traiter les surfaces Jupiter lending/admin/wallet selon les crates réservées correspondantes ; chaque décodeur validé reçoit un statut d’exécuteur explicite, pas une promesse générique Jupiter. +- [ ] Décoder Jupiter même lorsque la source temps réel n’est pas directement abonnée au program id Jupiter. +- [ ] Ajouter OKX/Onchain Labs et DFlow après validation des program ids, puis leurs exécuteurs réservés `kb_executor_router_okx_labs_v1`, `kb_executor_router_okx_labs_v2` et `kb_executor_router_dflow_aggregator_v4`. +- [ ] Ajouter Bags Fee Share v1/v2 puis `kb_executor_fees_bags_fee_share_v1` et `kb_executor_fees_bags_fee_share_v2`, avec garde-fous administratifs et aucune exposition trading implicite. +- [ ] Mesurer quelles routes Jupiter sont déjà reçues via les DEX sous-jacents. +- [ ] Valider les exécuteurs routeur/orderbook/perpetuals offline puis localnet/devnet ; l’activation live dépend de quotes exactes, slippage, limites de dépense et validation post-exécution. + +## Version 0.10.x — démonstrations Devnet Solana Core + +Le scan de `kb_decoder_solana_core` et `kb_executor_solana_core` confirme que System, Compute Budget, Address Lookup Table, Stake et Vote appartiennent tous au domaine Solana Core. Les exécuteurs sont disponibles, mais `kb_pipeline` et `DemoExecutionSolanaCore` n’orchestrent actuellement qu’un transfert System de bout en bout. + +- [x] Confirmer les capacités exactes déjà compilées : quatre opérations Compute Budget, cinq opérations Address Lookup Table, vingt-quatre helpers Stake actuels et vingt-quatre opérations Vote actuelles. +- [ ] Ajouter un accordéon par famille seulement lorsque la famille dispose d’une démonstration utile ; conserver System affiché directement tant qu’il reste la seule orchestration réseau complète. +- [ ] Ajouter en premier une transaction System enrichie de `SetComputeUnitLimit` et `SetComputeUnitPrice`, avec plan, simulation, soumission, hydratation et replay. +- [ ] Ajouter ensuite le lifecycle Address Lookup Table contrôlé : create, extend, deactivate, attente du cooldown puis close ; freeze doit utiliser une table jetable distincte car il est irréversible. +- [ ] Ajouter un lifecycle Stake contrôlé sur compte temporaire : create/initialize, delegate vers un vote account Devnet existant, deactivate, attente d’epoch puis withdraw/close. +- [ ] Ajouter Vote en dernier : création d’un vote account temporaire et opérations administratives sûres ; ne pas tenter de produire artificiellement des votes de consensus sans validator contrôlé. +- [ ] Mutualiser l’orchestration réseau générique afin d’éviter une fonction pipeline spécifique par opération et conserver simulation-first, autorisation explicite, plafonds, postconditions et second replay. + +## Version 0.11.x — sources temps réel interchangeables + +Cette série commence seulement lorsque les transactions reçues peuvent être décodées et matérialisées immédiatement. + +### 0.11.0 — contrat de stream commun dans `kb_rpc` + +- [ ] Ajouter une interface interne de flux transactionnel source-indépendante dans `kb_rpc`. +- [ ] Normaliser toutes les sources vers le même modèle canonique `kb_model`. +- [ ] Garantir que les décodeurs et stores ne dépendent d’aucun provider. + +### 0.11.1 — extension Helius WebSocket + +- [ ] Ajouter `transactionSubscribe` et `transactionUnsubscribe` dans des modules internes Helius de `kb_rpc`. +- [ ] Ajouter `accountInclude`, `accountExclude`, `accountRequired`, `vote`, `failed` et options de transaction complète. +- [ ] Tester offline avant tout abonnement payant. +- [ ] Acheter Helius Developer seulement pour la campagne mainnet réelle. + +### 0.11.2 — Yellowstone gRPC + +- [ ] Ajouter le client Yellowstone gRPC dans `kb_rpc` avec `tonic` et Protobuf. +- [ ] Supporter Triton, Chainstack, Shyft et autres endpoints compatibles par configuration. +- [ ] Ajouter filtres transaction `account_include`, `account_exclude`, `account_required`, `vote` et `failed`. +- [ ] Normaliser `SubscribeUpdateTransaction` vers la transaction canonique. + +### 0.11.3 — fallback Solana standard + +- [ ] Ajouter `logsSubscribe` par mention et `logsSubscribe("all")` comme probes/fallbacks mesurés. +- [ ] Ajouter l’hydratation `getTransaction` bornée. +- [ ] Ne jamais stocker le payload complet des notifications quand la transaction canonique est disponible. + +### 0.11.4 — comparaison fournisseur et coût + +- [ ] Comparer Helius `transactionSubscribe`, Yellowstone gRPC et logs + hydration. +- [ ] Mesurer couverture, latence, octets, crédits/coût, doublons, pertes et reconnexions. +- [ ] Utiliser `kb_sol_obs_transaction_observations` pour les comparaisons de timing par signature. +- [ ] Commencer avec Meteora, Pump et Raydium ; ajouter Orca ensuite ; garder Jupiter hors filtre initial. +- [ ] Choisir la source principale, le fallback et le fournisseur selon les mesures. + +### 0.11.5 — ingestion live durable + +- [ ] Ajouter sessions persistantes, backpressure, reconnexion et réparation des trous. +- [ ] Désactiver le trading pendant toute perte de source ou phase de rattrapage. +- [ ] Marquer les origines `live`, `replayed`, `backfilled` et `repaired`. +- [ ] Interdire par défaut toute décision de trading depuis un événement non `live`. + +## Version 0.12.x — wallet, sécurité et démos devnet + +- [ ] Promouvoir `kb_wallet` du backend temporaire `0.4.2` vers des backends de production chiffrés, coffre système ou hardware wallet. +- [ ] Lire l’adresse wallet locale et les soldes SOL/SPL. +- [ ] Généraliser les parcours wallet et soldes SOL/SPL au-delà du laboratoire d’exécution native validé en `0.4.2`. +- [ ] Finaliser `kb_execution_safety` minimal. +- [ ] Ajouter simulation obligatoire, limites de dépense, slippage, dry-run et validation post-transaction. +- [ ] Ajouter les pages Tauri wallet, balances et transferts. + +## Version 0.13.x — listeners et trading assisté + +- [ ] Relier les transactions décodées live aux listeners métier. +- [ ] Notifier créations de tokens/pools, migrations, liquidité, swaps, frais et opérations administratives dangereuses. +- [ ] Ajouter des conditions manuelles d’achat/revente sans automatisation complète. +- [ ] Ajouter heartbeat opérateur et état global `TRADING_DISABLED` / `LIVE_READY`. +- [ ] Interdire tout envoi mainnet tant que les garde-fous ne sont pas validés. +- [ ] N’autoriser les listeners ou stratégies à demander que des capacités d’exécuteur explicitement activées ; une crate réservée ou un builder hors UI ne constitue pas une autorisation de trading. + +## Développement parallèle par surface + +Pour chaque surface prioritaire : + +- [ ] Décodeur maximal, y compris instructions obsolètes ou non directement liées au trading. +- [ ] Corpus de signatures dédié. +- [ ] Événements décodés stables. +- [ ] Matérialisation complète de tout ce qui est exploitable. +- [ ] Tests synthétiques. +- [ ] Replay et SQL anti-régression. +- [ ] Statut d’exécuteur obligatoire dans le même jalon : `réservé`, `en implémentation`, `complet`, ou `non applicable` avec justification officielle. +- [ ] Pour toute surface appelable, implémenter l’exécuteur après stabilisation du décodeur et des garde-fous ; couvrir maximalement les opérations client même si elles restent hors `kb_app_demo`. +- [ ] Synchroniser le README de l’exécuteur avec ses types, constantes, opérations, paramètres, effets, erreurs, exemple d’usage et limites d’exposition. + +## Objectif long terme — bibliothèque d’analyse Solana + +- [ ] Étendre les registres à toutes les surfaces observées. +- [ ] Ajouter classification automatique des programmes inconnus. +- [ ] Ajouter matérialisations NFT, metadata, oracle, lending, staking, governance, bridge, perpetuals, vault, routing, risk et compliance audit. +- [ ] Ajouter agrégations comparables à un explorateur : tokens, pools, pairs, volumes, états, risques, anomalies et historiques. +- [ ] Ajouter replay partiel par module, version, slot, signature, program id, surface et discriminator. + +## Transports futurs + +- [ ] Étudier shred delivery/RabbitStream uniquement après stabilisation des flux transactionnels complets. +- [ ] Ne pas confondre les transactions pré-exécution sans metadata avec la source canonique de décodage. diff --git a/migration/khadhroony-bot2-reference/RULES.md b/migration/khadhroony-bot2-reference/RULES.md new file mode 100644 index 0000000..e0aeec2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/RULES.md @@ -0,0 +1,231 @@ + + + +# Règles spécifiques à `khadhroony-bot2` + +Ce fichier contient uniquement les règles propres au projet et au workspace `khadhroony-bot2`. + +Les règles Rust générales et réutilisables dans tous les projets sont définies dans [`RUST_RULES.md`](RUST_RULES.md). Les deux fichiers sont normatifs et cumulatifs. En cas de conflit, la règle la plus stricte s'applique ; une règle spécifique au workspace ne peut jamais assouplir une règle générale sans exception explicitement documentée. + +Toute livraison doit exécuter l'audit `python3 scripts/audit_rust_workspace_rules.py`. Tant que l'audit global n'est pas propre, les écarts existants doivent être résorbés par lots de correctifs avant toute nouvelle prérelease. + +## Règles de nommage + +- Tous les noms de fichiers et de répertoires doivent être écrits en anglais. +- Les noms de fichiers et de répertoires ne doivent contenir aucun accent, espace ou caractère spécial inutile. +- Les noms internes doivent utiliser le format `snake_case` lorsque c'est applicable. +- Les crates Rust utilisent le préfixe `kb_`. +- Les crates de décodeur utilisent le format `kb_decoder__`. +- Les crates de matérialisation utilisent le format `kb_materializer_`. +- Le crate de journalisation s'appelle `kb_logging`. + +## Règles Tauri et TypeScript + +- Toute structure ou énumération Rust exposée au frontend TypeScript doit importer le trait `TS` avec `use ts_rs::TS;` puis dériver `TS`. +- Les types Rust exportés vers TypeScript doivent utiliser des noms stables et explicites. +- Les bindings générés doivent être produits dans un dossier dédié, généralement `../frontend/ts/bindings` ou `#[ts(export, export_to = "../frontend/ts/bindings/MyStruct.ts")]`. +- Les types purement internes au backend ne doivent pas être exportés vers TypeScript par défaut. +- Un payload de commande ou d’événement Tauri traverse une frontière JSON et ne doit jamais exiger un `bigint` JavaScript. Pour un entier Rust borné et représentable par l’UI, utiliser un override TS-rs `number`; si l’exactitude au-delà de `Number.MAX_SAFE_INTEGER` est nécessaire, sérialiser explicitement une chaîne décimale côté Rust et exporter `string`. Ne jamais construire un `BigInt` dans un objet passé à `invoke` ou `emit`. +- Corriger le type Rust/TS-rs source puis régénérer les bindings ; ne pas considérer une modification manuelle isolée d’un fichier généré comme un correctif durable. + +## Règles d'architecture + +- Les décodeurs ne dépendent pas de PostgreSQL, SQLite, RPC, Tauri, wallet, stratégie ou matérialisateurs. +- Les matérialisateurs ne dépendent pas du RPC, du wallet, de Tauri ou des décodeurs spécifiques. +- Pour chaque programme Solana, quelle que soit sa famille, un décodeur doit couvrir maximalement tout wire officiellement identifiable de sa surface, qu'il soit historique, courant, récent, expérimental ou publié avant son déploiement généralisé. Ces statuts doivent rester explicites et ne constituent pas un motif pour supprimer le décodage. +- Une observation lisible issue d'une transaction échouée peut conserver une intention non commitée ; elle ne doit jamais être présentée comme une mutation réussie. +- Pour chaque programme Solana, les matérialisateurs doivent projeter maximalement tous les faits métier stables et prouvés par le décodage, y compris les états historiques, obsolètes, dépréciés ou seulement rencontrables pendant un backfill. Le statut historique interdit l'exécution, mais ne constitue jamais à lui seul un motif de non-matérialisation. +- Toute projection historique doit conserver explicitement son lifecycle, sa version de layout, sa provenance, son slot ou ordre d'observation lorsqu'ils sont connus, et ne doit jamais écraser silencieusement un état actif plus récent. +- Une même réalité métier doit avoir une seule projection canonique. Les snapshots de comptes sont la source autoritative de l'état final lorsqu'ils sont disponibles ; les instructions et événements corrélés restent des observations de mutation et ne doivent pas produire un doublon concurrent du même état. +- Les matérialisateurs doivent refuser les transactions non commitées pour les mutations d'état, tout en pouvant conserver séparément une intention non commitée lorsque le modèle métier le prévoit explicitement. +- Toute absence volontaire de projection doit être documentée avec une justification technique précise. Un matérialisateur ne doit inventer ni état final, ni montant, ni autorité, ni frais, ni agrégation que l'observation ne prouve pas. +- Pour chaque programme Solana, les exécuteurs doivent couvrir maximalement les opérations officiellement appelables et les opérations expérimentales publiées lorsque leurs contrats exacts et garde-fous sont prouvés. Les opérations historiques, dépréciées ou obsolètes doivent rester décodables et matérialisables, mais ne doivent jamais être exposées à l'exécution ; cette exclusion doit être explicite dans la matrice et le README. +- Les stores exposent leurs comportements via les traits de `kb_store_core`. +- `kb_store_pg` est le store de production. +- `kb_store_sqlite` sert aux tests, imports et corpus locaux. +- `kb_wallet` reste isolé du reste du système. +- Les transactions raw sont immuables et restent la source d'audit. +- Les replays doivent être ciblés par module, version, programme, surface, discriminator, slot ou signatures. +- La documentation du projet ne doit pas référencer le nom de l'ancien workspace. + +## Règles de documentation projet + +- `README.md` décrit le projet, son rôle, ses objectifs et son organisation. +- `ROADMAP.md` contient les futures étapes, versions et changements prévus. +- `ROADMAP.md` conserve une structure documentaire normale par phases, cases cochées et journal de préversions ; les étiquettes conversationnelles de priorité ne doivent pas y être ajoutées. +- Les réponses de livraison doivent reprendre la liste exhaustive des tâches avec les rubriques conversationnelles **Validé**, **En cours**, **Next** et **Planifié**, sans réduire le suivi à un résumé ambigu. +- `CHANGELOG.md` n'est modifié qu'après validation d'une version, avec un paragraphe ajouté à la fin décrivant ce qui a été fait ou ajouté. +- Chaque crate Rust doit avoir un `README.md` ou `001.README.md` expliquant son rôle dans l'écosystème. +- Le README d'une crate opérationnelle doit inventorier ses types, traits, constantes et fonctions publics utiles, expliquer leurs paramètres, résultats, effets et frontières, puis fournir au moins un exemple d'utilisation lorsque l'API est destinée à être appelée directement. +- Une API publique ajoutée ou modifiée n'est pas considérée comme documentée tant que le README de sa crate n'a pas été synchronisé ; les bindings générés seuls ne remplacent pas cette documentation. + +## Nomenclature métier + +- `protocol_code` désigne la famille : `pump`, `raydium`, `meteora`, `orca`, `jupiter`, etc. +- `surface_code` désigne la surface concrète : `pump_swap`, `raydium_amm_v4`, `meteora_dlmm`, etc. +- `event_code` suit le format `.`. +- Les familles d'événements principales sont : `trade`, `liquidity`, `lifecycle`, `fee`, `admin`, `reward`, `orderbook`, `token_account`, `pool_state`, `routing`, `risk`, `audit`, `unknown`. + +## Base de données + +- Les schémas PostgreSQL cibles sont : `raw`, `core`, `obs`, `decode`, `mat`, `catalog`, `agg`, `ops`, `wallet`. +- Les colonnes JSONB doivent se terminer par `_jsonb`. +- Les montants bruts on-chain doivent se terminer par `_raw`. +- La transaction canonique raw reste la source rejouable et ne doit pas être supprimée après traitement sans politique de rétention explicite. + +## Nommage canonique des surfaces + +Les nouvelles surfaces de programmes doivent utiliser un nom canonique basé sur la fonction réelle du programme : + +```text +__[_vN] +``` + +Les crates de décodage doivent utiliser : + +```text +kb_decoder___[_vN] +``` + +Exemples : + +- `kb_decoder_amm_raydium_cpmm` ; +- `kb_decoder_clmm_raydium` ; +- `kb_decoder_dlmm_meteora` ; +- `kb_decoder_router_jupiter_aggregator_v6` ; +- `kb_decoder_orderbook_openbook_v2` ; +- `kb_decoder_metadata_metaplex_token_metadata` ; +- `kb_decoder_nft_metaplex_bubblegum`. + +Les noms historiques ou issus d'IDL doivent être conservés dans le registre, mais ne doivent pas créer de nouvelles crates si une crate canonique existe déjà. + +Aucun renommage massif de crates n'est autorisé sans étape de contrôle dédiée et sans `cargo build` validé. + +## Règles de nommage des surfaces Solana + +- Le nom canonique d'une surface doit commencer par une fonction réelle : `amm`, `clmm`, `dlmm`, `router`, `orderbook`, `launchpad`, `lending`, `vault`, `staking`, `bridge`, `perpetuals`, `oracle`, `nft`, `metadata`, `admin`, etc. +- Le préfixe `program_` est interdit pour les nouveaux noms canoniques. +- Les surfaces non classifiées doivent utiliser `unknown_*` et ne doivent pas avoir de crate cible tant que leur fonction n'est pas validée. +- Les programmes Solana/SPL primitifs doivent rester séparés des surfaces DEX/router dans la documentation de contrôle. +- `damm` ne doit pas être utilisé comme préfixe fonctionnel ; utiliser `amm_meteora_damm_v1` ou `amm_meteora_damm_v2`. + +## Registre des identifiants core + +- Les identifiants Solana/SPL primitifs doivent être tenus à jour dans `registry/core_program_id_seed.toml`. +- Les sysvars et comptes natifs connus ne doivent pas être classés comme surfaces DEX/router. +- `spl_token`, `spl_token_2022` et `associated_token_account` restent dans leurs crates spécialisées. +- `stake_pool` doit être réservé à un futur crate spécialisé plutôt que mélangé avec le programme `stake`. + +## Index court de programme + +- Les noms de crates ne doivent pas commencer par un index hexadécimal. +- Un champ `registry_code` optionnel peut être ajouté au registre pour l'UI, PostgreSQL ou les matrices. +- Le nom canonique reste la source principale : fonction + famille + identifiant + version. + +## Comptes non exécutables + +- Une adresse de compte, de pool, de PDA ou de vault ne doit pas générer une crate de décodeur. +- Le vrai `program_id` propriétaire doit être prouvé avant création d'une surface canonique. + +## Constantes Rust + +- Les fichiers `program_ids.rs` sont interdits dans les nouveaux crates. Utiliser `constants.rs`. +- Les `program_id` publics doivent être réexportés depuis `lib.rs`. +- Dans une crate, les modules internes doivent appeler les constantes réexportées via `crate::CONSTANT_NAME`. +- Les discriminants, sélecteurs, longueurs Borsh et constantes internes futures doivent être placés dans `constants.rs` et rester `pub(crate)` sauf besoin d'API publique explicite. + +## Règles de tracing + +- Une crate est opérationnelle lorsqu’elle effectue des I/O, orchestre un pipeline, décode, matérialise, exécute, applique une politique runtime ou prend une décision mutable observable. +- Toute crate opérationnelle ajoutée ou modifiée doit dépendre de `tracing` depuis le workspace et déclarer exactement un `pub(crate) const TRACING_TARGET` dans `src/constants.rs`. +- La valeur canonique de `TRACING_TARGET` est le nom exact du package Cargo. Les targets historiques `khbot.*`, les suffixes de module et les targets de fenêtre sont interdits dans les nouvelles modifications. +- Les macros `tracing` doivent utiliser `target: crate::TRACING_TARGET` et des champs structurés stables. La granularité interne passe par `action`, `stage`, `window`, `campaign_id`, `signature`, `instruction_path`, `program_id`, `processor_name`, `processor_version`, `status` et `error_code`. +- Les crates passives de types, contrats, DTO, API sans exécution, registres ou constantes restent sans dépendance `tracing`. `kb_config` reste une exception de bootstrap tant que sa validation précède l’installation du subscriber. +- Il est interdit d’ajouter `tracing` sans événement réel ou de conserver un faux target uniquement consommé par `let _target`. +- Une décision interne doit être journalisée par la crate responsable ; `kb_app_demo` ne journalise que les frontières Tauri/UI, les actions utilisateur et les résumés d’orchestration. +- Tout input sélectionné sans décodeur compatible, tout résultat de décodage `failed` ou `unsupported`, tout résultat de matérialisation `failed`, toute validation de résultat invalide et toute erreur de persistance doivent émettre un événement `error` avant le retour ou la persistance terminale. +- Une transaction Solana échouée mais correctement décodée n’est pas une erreur du logiciel. Une décision `ignored`, un refus de matérialisation conforme à la politique ou une annulation coopérative ne doivent pas être promus artificiellement au niveau `error`. +- Les erreurs de décodage et de matérialisation doivent conserver au minimum, lorsque disponibles : `campaign_id`, `signature`, `slot`, `instruction_path`, `program_id`, processor ou materializer avec version, `input_key`, `input_hash` ou hash du payload, statut, code et diagnostic borné. +- Les payloads complets, DSN non masqués, secrets, clés privées et données non bornées sont interdits dans les logs. +- Chaque profil doit router les événements vers les sorties globales `debug.log`, `info.log`, `error.jsonl` et `app.log`, puis vers `debug.log`, `info.log` et `error.jsonl` dans un répertoire propre à chaque crate utilisant `tracing`. +- L’ajout ou la suppression de `tracing.workspace = true` dans une crate impose la mise à jour simultanée de la matrice de routes, de ses tests et de `docs/TRACING_CONTRACT.md`. +- Le contrat détaillé est défini dans `docs/TRACING_CONTRACT.md`. +- L’audit mécanique spécifique au projet est exécuté par `python3 scripts/audit_khadhroony_workspace_rules.py`. Il couvre notamment `TRACING_TARGET` et l’usage obligatoire de `solana_pubkey::Pubkey` à la place de `solana_address::Address`. + +## Règles de réutilisation des interfaces Solana et SPL + +- Les dépendances déclarées dans `[workspace.dependencies]` forment un catalogue de versions et de features autorisées ; elles ne doivent être ajoutées à une crate consommatrice que lorsqu’un type, un encodeur, un décodeur ou un identifiant officiel est réellement utilisé. + +- Dans un `Cargo.toml`, placer dans `[dependencies]` toute crate référencée par le code de bibliothèque compilé en production. Réserver `[dev-dependencies]` aux références contenues exclusivement dans `#[cfg(test)]`, les tests d’intégration, benches ou exemples. Une dépendance de test vers un décodeur ou matérialiseur concret est légitime lorsqu’elle sert uniquement à éprouver une orchestration générique fondée sur les traits API ; elle ne prouve pas qu’une capacité de production manque. Toute promotion de `dev-dependencies` vers `dependencies` doit être motivée par un appel runtime réel, et toute dépendance runtime inutilisée doit être supprimée. +- Les registres de composition runtime des applications doivent énumérer explicitement chaque décodeur et matérialiseur concret activé. Toute nouvelle surface instructionnelle dotée d’un `EventMaterializer` doit être ajoutée au registre applicatif et couverte par un test d’inventaire ordonné ; une crate présente dans le workspace ou dans `kb_pipeline` n’est pas activée automatiquement. Les matérialiseurs exclusivement stateful qui n’implémentent pas `EventMaterializer` restent routés par leurs APIs de snapshots dédiées. +- Les corrélations instruction/état doivent produire une issue explicite (`confirmed`, `contradicted` ou `not_applicable`) et ne doivent jamais transformer automatiquement une configuration observée en violation, score ou conclusion métier. +- Une interface officielle Solana ou SPL étroite doit être préférée à `solana-sdk` lorsque son contrat suffit. +- L’ordre de préférence des formats est : schéma officiel `wincode`, schéma officiel Borsh, puis parseur local borné reproduisant exactement le runtime lorsque l’interface officielle n’expose que `bincode`. +- Aucun nouveau code ne doit dépendre directement de `bincode`. Une interface officielle uniquement disponible derrière une feature `bincode` ne justifie pas l’activation de cette feature ; le layout doit alors être prouvé depuis les sources officielles et implémenté localement avec des bornes et des tests. +- Les exécuteurs doivent utiliser les builders officiels disponibles, préserver l’ordre exact des metas, borner toute liste de comptes variable avant l’appel au builder et refuser les doublons lorsque leur répétition n’a pas de sémantique publiée. +- Les features des interfaces doivent rester minimales et explicites. Une feature `serde`, `wincode`, `borsh`, `std`, `alloc` ou équivalente n’est activée que si la crate consommatrice l’utilise réellement. +- Les crates applicatives ne doivent pas dépendre d’interfaces RPC ou transactionnelles uniquement pour relayer des types ; ces dépendances appartiennent à `kb_rpc`, aux modèles communs justifiés ou à la crate opérationnelle propriétaire. +- Toute exception et toute implémentation locale doivent être documentées dans `docs/SOLANA_INTERFACE_DEPENDENCIES.md` avec la raison, la source officielle et la stratégie de test. + +## Règles des exécuteurs + +- Les crates d'exécution utilisent le préfixe `kb_executor_`. +- Une surface classifiée peut avoir un décodeur `kb_decoder_` et un exécuteur `kb_executor_`. +- Les exécuteurs ne doivent pas dépendre des décodeurs. +- Les exécuteurs doivent passer par `kb_execution_api` pour leur contrat public. +- Les garde-fous communs doivent être placés dans `kb_execution_safety`. +- Aucun exécuteur ne doit envoyer de transaction sans simulation et validation explicite. +- Les surfaces non classifiées ne doivent pas avoir de crate exécuteur. +- Pour chaque programme Solana et contrairement aux décodeurs, les exécuteurs ne construisent pas les opérations obsolètes ou purement historiques. Ils couvrent uniquement les opérations courantes et expérimentales officiellement constructibles, avec un statut exact `Supported` ou `Unsupported(reason)`. +- Le statut expérimental, récent ou non encore déployé partout n'interdit pas un builder universel lorsqu'un wire officiel exact existe. Le déploiement, la simulation et l'autorisation d'envoi restent trois décisions séparées ; la bibliothèque ne doit pas imposer artificiellement un cluster. + + +- Les champs JSON exposés à TypeScript ne doivent pas utiliser directement `serde_json::Value` avec `TS-rs`; utiliser une chaîne JSON sérialisée (`std::string::String`) ou un type Rust typé exportable. +- Les APIs qui gardent des payloads dynamiques doivent fournir des helpers explicites basés sur `serde_json::to_string` et `serde_json::to_string_pretty`. + +## Règles de configuration + +- La configuration applicative commune doit passer par `kb_config`. +- Les fichiers JSON de configuration ne doivent pas contenir de commentaires. +- Les secrets ne doivent pas être écrits en clair dans le dépôt. +- Les valeurs sensibles doivent utiliser des variables d'environnement ou un stockage chiffré dédié. +- Les structures de configuration exposées à Tauri doivent dériver `TS`. + +## Ordre de développement cible + +- `kb_logging` doit être stabilisé avant les logs avancés des autres crates. +- `kb_config` doit être stabilisé avant les stores, RPC, wallet, applications et workers. +- Les contrats SQL et de matérialisation doivent être définis avant les gros décodeurs DEX. +- Les implémentations détaillées des matérialisateurs doivent suivre les sorties réelles des décodeurs correspondants. +- `kb_app_demo` doit fournir des validations live après chaque capacité majeure, sans créer une application Tauri séparée par crate. + +## Règles de livraison ChatGPT + +- Après le squelette initial, ChatGPT doit fournir uniquement des zips delta sauf demande explicite de zip complet. +- Un delta qui modifie la racine du workspace ou plusieurs modules doit être nommé `khadhroony-bot2_vX.Y.Z-pre.abc-delta.zip`. +- Un delta qui ne modifie qu'un seul module Rust doit être nommé `kb_modulename_vX.Y.Z-pre.abc-delta.zip`. +- Lorsqu'un delta `pre.abc` a déjà été livré et qu'un correctif est nécessaire, le numéro `abc` ne doit pas être incrémenté. Le correctif doit être nommé `khadhroony-bot2_vX.Y.Z-pre.abc-delta-fix-001.zip`, puis `-fix-002.zip`, etc. Pour un seul module, utiliser la même règle avec le préfixe `kb_modulename`. +- Un correctif doit indiquer dans `delta.md` le delta de base et les correctifs antérieurs à appliquer. Il ne doit pas réutiliser silencieusement le nom ou l'empreinte d'une archive déjà livrée. +- Le numéro `pre.abc` suivant est réservé à une nouvelle tranche fonctionnelle, pas à la réparation d'une archive existante. +- Chaque zip delta ou correctif doit contenir un fichier `delta.md` non versionné à la racine du zip. +- `delta.md` doit lister les fichiers ajoutés, les fichiers modifiés, les fichiers à supprimer manuellement, les validations exécutées et les validations non exécutées. +- Le zip delta ne doit pas contenir de fichiers inchangés. +- Le zip delta ne doit pas modifier `CHANGELOG.md` sauf validation explicite d'une version. +- Les suppressions de fichiers ou dossiers doivent être indiquées dans `delta.md`, car l'extraction d'un zip ne supprime pas automatiquement les anciens fichiers. +- Les prompts de session doivent rappeler ce format de livraison et la règle `delta-fix-NNN`. + +## Constantes des décodeurs + +- Chaque crate `kb_decoder_*` classifié avec un `program_id` doit avoir un fichier `src/constants.rs`. +- `src/constants.rs` contient les `PROGRAM_ID`, puis les discriminators, sélecteurs, opcodes, constantes Borsh et constantes de décodage quand elles seront connues. +- `src/lib.rs` doit réexporter les constantes nécessaires avec `pub use crate::constants::...`. +- `src/decoder.rs` doit utiliser les constantes réexportées par la crate, par exemple `crate::AMM_PUMP_SWAP_PROGRAM_ID`, et non `crate::constants::AMM_PUMP_SWAP_PROGRAM_ID`. +- Les literals de `program_id` ne doivent pas rester dans `program_ids()`, sauf dans un fichier `constants.rs`. + +## Identifiants de programmes + +Les `program_id` connus doivent être définis une seule fois dans `kb_program_ids`. Les crates de décodeur, d’exécuteur, de store, d’application ou d’outil doivent référencer directement `kb_program_ids::XXX_PROGRAM_ID`. Les fichiers `constants.rs` locaux ne doivent pas redéfinir ces chaînes ; ils restent réservés aux constantes internes du module, par exemple discriminators, opcodes, seeds, index de comptes ou layouts. + +## Validation frontend Tauri + +- Pour `kb_app_demo`, ne pas lancer `npm --prefix kb_app_demo run build` séparément : la validation frontend de développement est réalisée par `cargo tauri dev -c kb_app_demo/tauri.conf.json`, qui démarre et pilote le serveur Vite. diff --git a/migration/khadhroony-bot2-reference/RUST_RULES.md b/migration/khadhroony-bot2-reference/RUST_RULES.md new file mode 100644 index 0000000..c1262df --- /dev/null +++ b/migration/khadhroony-bot2-reference/RUST_RULES.md @@ -0,0 +1,134 @@ + + + +# Règles Rust générales + +Ce fichier contient les règles normatives applicables à tous les projets Rust. Elles sont indépendantes de `khadhroony-bot2` et doivent pouvoir être réutilisées telles quelles dans un autre workspace. + +## En-têtes et versions de fichiers + +- Tout fichier texte qui supporte des commentaires commence par une ligne indiquant son chemin relatif dans le projet, puis une ligne `version` entière. +- La version d'un fichier est incrémentée à chaque modification après validation de sa version précédente. +- Les fichiers Rust utilisent `// file: ...` et `// version: N`. +- Les fichiers Markdown utilisent `` et ``. +- Tous les fichiers texte se terminent par exactement une fin de ligne. + +## Langue et documentation + +- Les commentaires et rustdocs du code sont rédigés en anglais. +- Les documents Markdown du projet sont rédigés dans la langue documentaire choisie par le projet. +- Tout élément `pub` ou `pub(crate)` possède une rustdoc utile au point de déclaration. +- Toute réexportation `pub use` ou `pub(crate) use` dans `lib.rs` ou `main.rs` possède également sa propre rustdoc copiée ou reformulée de manière équivalente. La documentation du point d'entrée doit permettre de comprendre l'API sans ouvrir le module interne. + +## Édition et lints obligatoires + +- L'édition Rust cible est Rust 2024, sauf contrainte explicitement documentée. +- Chaque `lib.rs` et `main.rs` contient : + - `#![warn(missing_docs)]` ; + - `#![deny(unreachable_pub)]` ; + - `#![forbid(unsafe_code)]`. +- Les lints Clippy obligatoires sont déclarés au niveau workspace et hérités par toutes les crates. +- Les règles minimales sont : interdiction de `unwrap`, `expect`, `?`, retours implicites, code `unsafe`, APIs publiques inaccessibles et imports globaux non justifiés. +- Les tests peuvent disposer d'exceptions limitées pour `unwrap` et `expect` uniquement lorsqu'elles sont explicitement autorisées par la configuration Clippy. + +La configuration workspace doit au minimum déclarer : + +```toml +[workspace.lints.rust] +missing_docs = "warn" +unreachable_pub = "deny" +unsafe_code = "forbid" + +[workspace.lints.clippy] +unwrap_used = "deny" +expect_used = "deny" +implicit_return = "deny" +needless_return = "allow" +useless_vec = "deny" +question_mark = "deny" +question_mark_used = "deny" +needless_match = "allow" +manual_ok_err = "allow" +manual_unwrap_or = "allow" +manual_map = "allow" +match_like_matches_macro = "allow" +single_match = "allow" +manual_unwrap_or_default = "allow" +manual_find = "allow" +explicit_counter_loop = "allow" +get_first = "allow" +implicit_saturating_sub = "allow" +``` + +## Formatage + +- `cargo fmt --all` est exécuté après application de chaque delta ou correctif et avant les tests. +- Les fichiers Rust ne contiennent pas de lignes vides à l'intérieur d'une fonction, d'une structure, d'une énumération ou d'une implémentation courte. +- Les lignes vides séparent uniquement les fonctions, blocs `impl`, types et sections logiques. +- Les exports de `lib.rs` ou `main.rs` ne contiennent aucune ligne vide à l'intérieur d'une série homogène de `pub use` ou `pub(crate) use`. +- Les séries `pub use` et `pub(crate) use` forment deux groupes séparés lorsqu'elles coexistent. + +## Imports et chemins + +- `use` est interdit pour les constantes, fonctions, structures, énumérations, unions, alias de types, modules et macros. +- `use` est autorisé uniquement pour un trait lorsque la résolution de méthode, une macro de dérivation ou une contrainte de langage l'exige réellement. +- Un import de trait doit rester étroit et ne doit jamais importer simultanément des éléments non-traits par accolades. +- Les imports globaux, glob imports et imports groupés par accolades sont interdits. +- Pour un élément fourni par une crate externe au workspace, utiliser directement le chemin public le plus court exposé par cette crate. Exemple : utiliser `external_crate::Struct`, jamais `external_crate::module::Struct` si `external_crate::Struct` existe, et jamais `use external_crate::Struct`. +- Pour un élément `pub` déclaré dans le workspace : + - il est réexporté depuis le `lib.rs` ou `main.rs` de sa crate propriétaire ; + - depuis une autre crate, il est appelé via `owner_crate::Item` ; + - depuis sa propre crate, il est appelé via `crate::Item`, même depuis son module de déclaration. +- Pour un élément `pub(crate)` : + - il est réexporté depuis le `lib.rs` ou `main.rs` via `pub(crate) use` ; + - il est appelé via `crate::Item`, même depuis son module de déclaration. +- Un élément strictement privé à un module n'est pas réexporté et est appelé par son nom local uniquement. Il est interdit d'utiliser un chemin long comme `crate::module::helper` ou `owner_crate::module::helper` pour un helper privé du module courant. +- Dans un sous-module de tests, un élément privé du module parent est appelé via `super::Item`. Un élément `pub` ou `pub(crate)` continue d’être appelé via le point d’entrée de crate le plus court, par exemple `crate::Item`. +- Les exports ne sont jamais groupés avec des accolades. Un type, une fonction, une constante ou un alias correspond à une ligne de réexport distincte. + +## Visibilité et API de crate + +- Aucun `pub mod` n'est autorisé. Les modules restent privés et l'API est constituée par des réexports explicites. +- Un élément `pub` inaccessible depuis le point d'entrée de sa crate est une erreur de conception, pas un simple avertissement. +- Un élément `pub(crate)` utilisé hors de son module est réexporté au niveau du point d'entrée de la crate. +- Les chemins internes de modules ne font pas partie de l'API stable. +- Les méthodes inhérentes publiques restent appelées via le type réexporté ; les fonctions libres publiques sont appelées via le point d'entrée de crate. + +## Helpers et réutilisation + +- Un helper répété dans plusieurs modules d'une même crate est déplacé dans un module commun explicite, généralement `helper.rs` ou un module spécialisé plus précis. +- Le nom `helpers.rs` ou `helper.rs` n'est utilisé que si aucune responsabilité métier plus précise ne convient. +- Un helper général réutilisable par plusieurs crates est déplacé dans une crate commune appropriée et exposé publiquement. +- Les types d'erreur généraux, identifiants de programme, primitives de validation et fonctions de sérialisation communes ne doivent pas être dupliqués entre crates. +- La mutualisation ne doit pas créer de dépendance cyclique ni déplacer un comportement métier spécifique dans une crate générique. + +## Gestion des erreurs et contrôle de flux + +- `unwrap`, `expect` et `panic` sont interdits dans le code de production. +- L'opérateur `?` est interdit dans les chemins de production ; utiliser des `match` explicites avec erreurs contextualisées. +- `anyhow` et `thiserror` ne sont pas utilisés par défaut. +- Les erreurs publiques sont typées lorsque leur contrat est stable ; les diagnostics dynamiques restent bornés. +- Les retours sont explicites conformément au lint `clippy::implicit_return`. + +## Sécurité et dépendances + +- Le code `unsafe` est interdit. +- Une dépendance n'est ajoutée que si elle est réellement utilisée dans le chemin de compilation concerné. +- Une dépendance utilisée uniquement dans les tests reste dans `[dev-dependencies]`. +- Les features sont minimales et explicites. + +## Contrôle avant livraison + +Chaque livraison Rust exécute au minimum, dans cet ordre : + +```bash +cargo fmt --all +python3 scripts/audit_rust_general_rules.py +python3 scripts/audit_khadhroony_workspace_rules.py +cargo test --workspace +cargo clippy --workspace --all-targets +``` + +Un projet peut utiliser une sélection de tests plus étroite pendant le développement, mais la fermeture d'une version exige le contrôle global. + +Le wrapper `python3 scripts/audit_rust_workspace_rules.py` exécute les deux audits sans fusionner leurs responsabilités. diff --git a/migration/khadhroony-bot2-reference/clippy.toml b/migration/khadhroony-bot2-reference/clippy.toml new file mode 100644 index 0000000..1f35ef0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/clippy.toml @@ -0,0 +1,39 @@ +# file: clippy.toml +# version: 1 + +msrv = "1.85.0" + +# The project favors explicit control flow and visible intent. +# These settings complement the coding rules already enforced manually +# in code review: no `?`, no `unwrap`, no `expect`, explicit error paths. + +too-many-arguments-threshold = 16 +type-complexity-threshold = 250 +single-char-binding-names-threshold = 3 +trivial-copy-size-limit = 16 +pass-by-value-size-limit = 256 +stack-size-threshold = 512000 +vec-box-size-threshold = 4096 +max-fn-params-bools = 2 +max-include-file-size = 1048576 +cognitive-complexity-threshold = 25 +too-large-for-stack = 2048 +enum-variant-size-threshold = 200 +large-error-threshold = 128 +avoid-breaking-exported-api = true +allow-unwrap-in-tests = true +allow-expect-in-tests = true +allow-useless-vec-in-tests = true + +disallowed-macros = [] +disallowed-methods = [] +disallowed-names = ["foo", "bar", "baz", "tmp"] +disallowed-types = [] +allowed-idents-below-min-chars = [ + "id", + "tx", + "rx", + "ms", + "pcm", + "vad", +] diff --git a/migration/khadhroony-bot2-reference/config/README.md b/migration/khadhroony-bot2-reference/config/README.md new file mode 100644 index 0000000..6e80344 --- /dev/null +++ b/migration/khadhroony-bot2-reference/config/README.md @@ -0,0 +1,62 @@ + + + +# Configuration locale + +Ce dossier contient les exemples et le schéma de configuration JSON. + +## Règles + +- Le fichier versionné doit rester sans secret. +- Les placeholders de clés restent directement dans les URLs. +- Le champ `active_profile` sélectionne un seul profil actif. +- Un profil actif doit être présent et unique. +- Les profils non actifs peuvent rester dans le fichier pour les tests devnet, mainnet lecture seule, backfill ou future production. +- Le contrat courant couvre HTTP JSON-RPC Solana et WebSocket JSON-RPC générique. +- Helius `transactionSubscribe` et Yellowstone gRPC seront ajoutés plus tard dans `kb_rpc`, avec évolution explicite du schéma quand les transports seront réellement utilisés. +- Les IDLs restent des artefacts de développement et ne font pas partie de la configuration runtime. + +## Fichiers + +- `example.config.json` : exemple complet avec profils `local_devnet`, `mainnet_research` et `mainnet`. +- `schema.config.json` : JSON Schema de validation du fichier de configuration. + +## Phase `0.3.x` + +`0.3.x` utilise les endpoints HTTP des profils de recherche existants pour : + +```text +getSignaturesForAddress +getTransaction +getSignatureStatuses +``` + +Aucun profil Helius payant ou Yellowstone n’est activé à ce stade. + +Les sources futures devront déclarer séparément : + +- provider ; +- protocole ; +- rôle ; +- limites de streams et filtres ; +- authentification ; +- région éventuelle. + +## Évolution du schéma + +`schema.config.json` est le contrat de validation runtime. Il évolue seulement lorsqu’un champ est réellement utilisé par l’exécution. + +## Logging de développement + +L’exemple configure, pour chacun des trois profils, quatre fichiers globaux puis trois fichiers dédiés par crate opérationnelle utilisant `tracing` : `debug.log`, `info.log` et `error.jsonl`. + +`pre.020` rend `kb_materializer_admin` et `kb_materializer_compliance_audit` opérationnelles. `pre.021` active ensuite `kb_materializer_staking`. Elles rejoignent donc la console et disposent de leurs routes propres sous : + +```text +logs//kb_materializer_admin/ +logs//kb_materializer_compliance_audit/ +logs//kb_materializer_staking/ +``` + +Le test de configuration découvre dynamiquement toutes les crates déclarant `tracing.workspace = true` et vérifie leurs trois routes dans chaque profil. Les logs ne doivent contenir ni secret, ni DSN non masqué, ni payload Config ou bytecode complet. + diff --git a/migration/khadhroony-bot2-reference/config/example.config.json b/migration/khadhroony-bot2-reference/config/example.config.json new file mode 100644 index 0000000..267cae1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/config/example.config.json @@ -0,0 +1,5211 @@ +{ + "active_profile": "mainnet_research", + "profiles": [ + { + "name": "local_devnet", + "app": { + "name": "khadhroony-bot2", + "environment": "development", + "auto_reconnect_default": false + }, + "logging": { + "default_level": "warn", + "targets": [ + { + "name": "console_local_devnet_compact", + "enabled": true, + "sink": "console", + "level": "debug", + "path": "", + "rotation": "none", + "format": "compact", + "ansi": true, + "targets": [ + "kb_app_demo", + "kb_decoder_metadata_metaplex_token_metadata", + "kb_decoder_solana_core", + "kb_decoder_spl_associated_token_account", + "kb_decoder_spl_elgamal_registry", + "kb_decoder_spl_memo", + "kb_decoder_spl_token", + "kb_decoder_spl_token_2022", + "kb_execution_solana", + "kb_executor_metadata_metaplex_token_metadata", + "kb_executor_metadata_spl_name_service", + "kb_executor_solana_core", + "kb_executor_spl_account_compression", + "kb_executor_spl_associated_token_account", + "kb_executor_spl_elgamal_registry", + "kb_executor_spl_memo", + "kb_executor_spl_noop", + "kb_executor_spl_single_pool", + "kb_executor_spl_token", + "kb_executor_spl_token_2022", + "kb_logging", + "kb_materializer_admin", + "kb_materializer_compliance_audit", + "kb_materializer_lifecycle", + "kb_materializer_risk", + "kb_materializer_staking", + "kb_materializer_token_accounts", + "kb_materializer_transaction_annotations", + "kb_pipeline", + "kb_rpc", + "kb_store_pg", + "kb_wallet" + ] + }, + { + "name": "file_local_devnet_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "*" + ] + }, + { + "name": "file_local_devnet_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "*" + ] + }, + { + "name": "file_local_devnet_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "*" + ] + }, + { + "name": "file_local_devnet_app", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/app.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_app_demo_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_app_demo/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_app_demo_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_app_demo/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_app_demo_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_app_demo/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_decoder_solana_core_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_decoder_solana_core/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_solana_core" + ] + }, + { + "name": "file_kb_decoder_solana_core_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_decoder_solana_core/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_solana_core" + ] + }, + { + "name": "file_kb_decoder_solana_core_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_decoder_solana_core/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_solana_core" + ] + }, + { + "name": "file_kb_decoder_spl_memo_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_decoder_spl_memo/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_memo" + ] + }, + { + "name": "file_kb_decoder_spl_memo_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_decoder_spl_memo/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_memo" + ] + }, + { + "name": "file_kb_decoder_spl_memo_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_decoder_spl_memo/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_memo" + ] + }, + { + "name": "file_kb_decoder_spl_associated_token_account_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_decoder_spl_associated_token_account/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_associated_token_account" + ] + }, + { + "name": "file_kb_decoder_spl_associated_token_account_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_decoder_spl_associated_token_account/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_associated_token_account" + ] + }, + { + "name": "file_kb_decoder_spl_associated_token_account_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_decoder_spl_associated_token_account/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_associated_token_account" + ] + }, + { + "name": "file_kb_decoder_spl_token_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_decoder_spl_token/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token" + ] + }, + { + "name": "file_kb_decoder_spl_token_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_decoder_spl_token/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token" + ] + }, + { + "name": "file_kb_decoder_spl_token_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_decoder_spl_token/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_token" + ] + }, + { + "name": "file_kb_executor_metadata_spl_name_service_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_metadata_spl_name_service/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_spl_name_service" + ] + }, + { + "name": "file_kb_executor_metadata_spl_name_service_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_metadata_spl_name_service/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_spl_name_service" + ] + }, + { + "name": "file_kb_executor_metadata_spl_name_service_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_metadata_spl_name_service/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_metadata_spl_name_service" + ] + }, + { + "name": "file_kb_executor_solana_core_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_solana_core/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_solana_core" + ] + }, + { + "name": "file_kb_executor_solana_core_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_solana_core/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_solana_core" + ] + }, + { + "name": "file_kb_executor_solana_core_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_solana_core/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_solana_core" + ] + }, + { + "name": "file_kb_execution_solana_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_execution_solana/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_execution_solana" + ] + }, + { + "name": "file_kb_execution_solana_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_execution_solana/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_execution_solana" + ] + }, + { + "name": "file_kb_execution_solana_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_execution_solana/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_execution_solana" + ] + }, + { + "name": "file_kb_executor_spl_account_compression_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_spl_account_compression/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_account_compression" + ] + }, + { + "name": "file_kb_executor_spl_account_compression_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_spl_account_compression/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_account_compression" + ] + }, + { + "name": "file_kb_executor_spl_account_compression_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_spl_account_compression/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_account_compression" + ] + }, + { + "name": "file_kb_executor_spl_memo_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_spl_memo/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_memo" + ] + }, + { + "name": "file_kb_executor_spl_memo_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_spl_memo/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_memo" + ] + }, + { + "name": "file_kb_executor_spl_memo_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_spl_memo/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_memo" + ] + }, + { + "name": "file_kb_executor_spl_associated_token_account_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_spl_associated_token_account/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_associated_token_account" + ] + }, + { + "name": "file_kb_executor_spl_associated_token_account_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_spl_associated_token_account/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_associated_token_account" + ] + }, + { + "name": "file_kb_executor_spl_associated_token_account_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_spl_associated_token_account/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_associated_token_account" + ] + }, + { + "name": "file_kb_executor_spl_token_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_spl_token/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token" + ] + }, + { + "name": "file_kb_executor_spl_token_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_spl_token/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token" + ] + }, + { + "name": "file_kb_executor_spl_token_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_spl_token/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_token" + ] + }, + { + "name": "file_kb_executor_spl_noop_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_spl_noop/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_noop" + ] + }, + { + "name": "file_kb_executor_spl_noop_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_spl_noop/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_noop" + ] + }, + { + "name": "file_kb_executor_spl_noop_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_spl_noop/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_noop" + ] + }, + { + "name": "file_kb_executor_spl_single_pool_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_spl_single_pool/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_single_pool" + ] + }, + { + "name": "file_kb_executor_spl_single_pool_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_spl_single_pool/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_single_pool" + ] + }, + { + "name": "file_kb_executor_spl_single_pool_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_spl_single_pool/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_single_pool" + ] + }, + { + "name": "file_kb_logging_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_logging/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_logging" + ] + }, + { + "name": "file_kb_logging_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_logging/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_logging" + ] + }, + { + "name": "file_kb_logging_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_logging/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_logging" + ] + }, + { + "name": "file_kb_materializer_admin_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_materializer_admin/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_admin" + ] + }, + { + "name": "file_kb_materializer_admin_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_materializer_admin/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_admin" + ] + }, + { + "name": "file_kb_materializer_admin_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_materializer_admin/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_admin" + ] + }, + { + "name": "file_kb_materializer_compliance_audit_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_materializer_compliance_audit/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_compliance_audit" + ] + }, + { + "name": "file_kb_materializer_compliance_audit_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_materializer_compliance_audit/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_compliance_audit" + ] + }, + { + "name": "file_kb_materializer_compliance_audit_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_materializer_compliance_audit/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_compliance_audit" + ] + }, + { + "name": "file_kb_materializer_lifecycle_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_materializer_lifecycle/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_lifecycle" + ] + }, + { + "name": "file_kb_materializer_lifecycle_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_materializer_lifecycle/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_lifecycle" + ] + }, + { + "name": "file_kb_materializer_lifecycle_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_materializer_lifecycle/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_lifecycle" + ] + }, + { + "name": "file_kb_materializer_transaction_annotations_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_materializer_transaction_annotations/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_transaction_annotations" + ] + }, + { + "name": "file_kb_materializer_transaction_annotations_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_materializer_transaction_annotations/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_transaction_annotations" + ] + }, + { + "name": "file_kb_materializer_transaction_annotations_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_materializer_transaction_annotations/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_transaction_annotations" + ] + }, + { + "name": "file_kb_materializer_staking_debug", + "enabled": true, + "level": "debug", + "format": "human", + "path": "logs/devnet/kb_materializer_staking/debug.log", + "targets": [ + "kb_materializer_staking" + ], + "sink": "file", + "rotation": "daily", + "ansi": false + }, + { + "name": "file_kb_materializer_staking_info", + "enabled": true, + "level": "info", + "format": "human", + "path": "logs/devnet/kb_materializer_staking/info.log", + "targets": [ + "kb_materializer_staking" + ], + "sink": "file", + "rotation": "daily", + "ansi": false + }, + { + "name": "file_kb_materializer_staking_error", + "enabled": true, + "level": "error", + "format": "json", + "path": "logs/devnet/kb_materializer_staking/error.jsonl", + "targets": [ + "kb_materializer_staking" + ], + "sink": "file", + "rotation": "daily", + "ansi": false + }, + { + "name": "file_kb_pipeline_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_pipeline/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_pipeline" + ] + }, + { + "name": "file_kb_pipeline_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_pipeline/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_pipeline" + ] + }, + { + "name": "file_kb_pipeline_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_pipeline/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_pipeline" + ] + }, + { + "name": "file_kb_rpc_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_rpc/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_rpc" + ] + }, + { + "name": "file_kb_rpc_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_rpc/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_rpc" + ] + }, + { + "name": "file_kb_rpc_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_rpc/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_rpc" + ] + }, + { + "name": "file_kb_store_pg_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_store_pg/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_store_pg" + ] + }, + { + "name": "file_kb_store_pg_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_store_pg/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_store_pg" + ] + }, + { + "name": "file_kb_store_pg_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_store_pg/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_store_pg" + ] + }, + { + "name": "file_kb_wallet_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_wallet/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_wallet" + ] + }, + { + "name": "file_kb_wallet_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_wallet/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_wallet" + ] + }, + { + "name": "file_kb_wallet_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_wallet/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_wallet" + ] + }, + { + "name": "file_kb_materializer_token_accounts_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_materializer_token_accounts/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_token_accounts" + ] + }, + { + "name": "file_kb_materializer_token_accounts_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_materializer_token_accounts/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_token_accounts" + ] + }, + { + "name": "file_kb_materializer_token_accounts_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_materializer_token_accounts/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_token_accounts" + ] + }, + { + "name": "file_kb_materializer_risk_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_materializer_risk/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_risk" + ] + }, + { + "name": "file_kb_materializer_risk_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_materializer_risk/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_risk" + ] + }, + { + "name": "file_kb_materializer_risk_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_materializer_risk/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_risk" + ] + }, + { + "name": "file_kb_decoder_spl_token_2022_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_decoder_spl_token_2022/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token_2022" + ] + }, + { + "name": "file_kb_decoder_spl_token_2022_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_decoder_spl_token_2022/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token_2022" + ] + }, + { + "name": "file_kb_decoder_spl_token_2022_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_decoder_spl_token_2022/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_token_2022" + ] + }, + { + "name": "file_kb_decoder_metadata_metaplex_token_metadata_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_decoder_metadata_metaplex_token_metadata/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_decoder_metadata_metaplex_token_metadata_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_decoder_metadata_metaplex_token_metadata/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_decoder_metadata_metaplex_token_metadata_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_decoder_metadata_metaplex_token_metadata/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_decoder_spl_elgamal_registry_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_decoder_spl_elgamal_registry/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_decoder_spl_elgamal_registry_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_decoder_spl_elgamal_registry/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_decoder_spl_elgamal_registry_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_decoder_spl_elgamal_registry/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_metadata_metaplex_token_metadata_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_metadata_metaplex_token_metadata/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_executor_metadata_metaplex_token_metadata_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_metadata_metaplex_token_metadata/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_executor_metadata_metaplex_token_metadata_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_metadata_metaplex_token_metadata/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_executor_spl_elgamal_registry_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_spl_elgamal_registry/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_spl_elgamal_registry_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_spl_elgamal_registry/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_spl_elgamal_registry_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_spl_elgamal_registry/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_spl_token_2022_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/devnet/kb_executor_spl_token_2022/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token_2022" + ] + }, + { + "name": "file_kb_executor_spl_token_2022_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/devnet/kb_executor_spl_token_2022/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token_2022" + ] + }, + { + "name": "file_kb_executor_spl_token_2022_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/devnet/kb_executor_spl_token_2022/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_token_2022" + ] + } + ], + "target_filters": [ + { + "target": "hyper", + "level": "warn" + }, + { + "target": "reqwest", + "level": "warn" + }, + { + "target": "tao", + "level": "warn" + }, + { + "target": "wry", + "level": "warn" + } + ] + }, + "database": { + "enabled": true, + "backend": "postgres", + "postgres": { + "url": "postgres://solana:solana@localhost:5432/solana", + "max_connections": 8, + "connect_timeout_ms": 5000, + "auto_initialize_schema": true + }, + "sqlite": { + "path": "data/local.sqlite", + "create_if_missing": true, + "busy_timeout_ms": 5000, + "max_connections": 1, + "auto_initialize_schema": true, + "use_wal": true + } + }, + "data": { + "wallets_directory": "wallets", + "logs_directory": "logs" + }, + "solana": { + "http_endpoints": [ + { + "name": "devnet_public_http", + "enabled": true, + "provider": "solana-public", + "cluster": "devnet", + "url": "https://api.devnet.solana.com", + "connect_timeout_ms": 4000, + "request_timeout_ms": 10000, + "max_idle_connections_per_host": 4, + "roles": [ + { + "role": "http_queries", + "enabled": true, + "request_kinds": [ + "get_account_info", + "get_balance", + "get_block_commitment", + "get_block_height", + "get_block_production", + "get_blocks", + "get_blocks_with_limit", + "get_cluster_nodes", + "get_epoch_info", + "get_epoch_schedule", + "get_fee_for_message", + "get_first_available_block", + "get_genesis_hash", + "get_health", + "get_highest_snapshot_slot", + "get_identity", + "get_inflation_governor", + "get_inflation_rate", + "get_inflation_reward", + "get_largest_accounts", + "get_latest_blockhash", + "get_leader_schedule", + "get_max_retransmit_slot", + "get_max_shred_insert_slot", + "get_minimum_balance_for_rent_exemption", + "get_multiple_accounts", + "get_recent_performance_samples", + "get_recent_prioritization_fees", + "get_signature_statuses", + "get_slot", + "get_slot_leader", + "get_slot_leaders", + "get_stake_activation", + "get_stake_minimum_delegation", + "get_supply", + "get_token_account_balance", + "get_token_accounts_by_delegate", + "get_token_accounts_by_owner", + "get_token_largest_accounts", + "get_token_supply", + "get_transaction", + "get_transaction_count", + "get_version", + "get_vote_accounts", + "is_blockhash_valid", + "request_airdrop" + ], + "priority": 10, + "requests_per_second": 8, + "burst_capacity": 16, + "max_concurrent_requests": 8, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 1500 + }, + { + "role": "http_transactions", + "enabled": true, + "request_kinds": [ + "simulate_transaction", + "send_transaction", + "get_signature_statuses" + ], + "priority": 20, + "requests_per_second": 2, + "burst_capacity": 2, + "max_concurrent_requests": 2, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 3000 + } + ] + } + ], + "ws_endpoints": [ + { + "name": "devnet_public_ws", + "enabled": true, + "provider": "solana-public", + "cluster": "devnet", + "url": "wss://api.devnet.solana.com", + "connect_timeout_ms": 5000, + "request_timeout_ms": 10000, + "unsubscribe_timeout_ms": 1500, + "write_channel_capacity": 256, + "event_channel_capacity": 1024, + "auto_reconnect": false, + "roles": [ + { + "role": "slot_notifications", + "enabled": true, + "request_kinds": [ + "slot_subscribe", + "slots_updates_subscribe" + ], + "priority": 10, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 16, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "program_logs", + "enabled": true, + "request_kinds": [ + "logs_subscribe_mentions", + "program_subscribe", + "account_subscribe", + "signature_subscribe", + "block_subscribe", + "vote_subscribe" + ], + "priority": 20, + "requests_per_second": 2, + "burst_capacity": 4, + "max_concurrent_requests": 2, + "max_subscriptions": 64, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "root_notifications", + "enabled": true, + "request_kinds": [ + "root_subscribe" + ], + "priority": 15, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 16, + "pause_after_rate_limit_ms": 3000 + } + ] + } + ], + "listeners": { + "enabled": true, + "default_commitment": "confirmed", + "log_listeners": [ + { + "name": "pump_swap_logs", + "enabled": false, + "endpoint_role": "program_logs", + "program_id": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA", + "purpose": "Détecter les swaps, créations de pool et changements de liquidité Pump Swap." + } + ], + "program_listeners": [ + { + "name": "pump_fun_program_accounts", + "enabled": false, + "endpoint_role": "program_logs", + "program_id": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", + "purpose": "Détecter les changements de comptes utiles à la création ou migration de tokens Pump Fun." + } + ], + "account_listeners": [] + } + }, + "wallet": { + "wallet_dir": "wallets/temporary/local_devnet", + "cluster": "devnet", + "temporary_wallet_enabled": true, + "temporary_wallet_alias": "local-devnet-operator", + "temporary_wallet_persist": true, + "localnet_send_enabled": false, + "devnet_send_enabled": true, + "testnet_send_enabled": false, + "mainnet_send_enabled": false + }, + "execution": { + "dry_run_default": true, + "require_simulation": true, + "require_operator_confirmation": true, + "localnet_max_spend_lamports": 0, + "devnet_max_spend_lamports": 100000000, + "testnet_max_spend_lamports": 0, + "mainnet_max_spend_lamports": 0, + "max_fee_lamports": 1000000, + "max_compute_unit_price_micro_lamports": 100000, + "recent_blockhash_max_age_slots": 150, + "send_max_retries": 3, + "confirmation_poll_interval_ms": 500, + "confirmation_max_attempts": 120, + "devnet_airdrop_max_lamports": 2000000000 + }, + "demo": { + "live_demo_enabled": true, + "trading_demo_enabled": false + } + }, + { + "name": "mainnet_research", + "app": { + "name": "khadhroony-bot2", + "environment": "research", + "auto_reconnect_default": false + }, + "logging": { + "default_level": "warn", + "targets": [ + { + "name": "console_mainnet_research_compact", + "enabled": true, + "sink": "console", + "level": "debug", + "path": "", + "rotation": "none", + "format": "compact", + "ansi": true, + "targets": [ + "kb_app_demo", + "kb_decoder_metadata_metaplex_token_metadata", + "kb_decoder_solana_core", + "kb_decoder_spl_associated_token_account", + "kb_decoder_spl_elgamal_registry", + "kb_decoder_spl_memo", + "kb_decoder_spl_token", + "kb_decoder_spl_token_2022", + "kb_execution_solana", + "kb_executor_metadata_metaplex_token_metadata", + "kb_executor_metadata_spl_name_service", + "kb_executor_solana_core", + "kb_executor_spl_account_compression", + "kb_executor_spl_associated_token_account", + "kb_executor_spl_elgamal_registry", + "kb_executor_spl_memo", + "kb_executor_spl_noop", + "kb_executor_spl_single_pool", + "kb_executor_spl_token", + "kb_executor_spl_token_2022", + "kb_logging", + "kb_materializer_admin", + "kb_materializer_compliance_audit", + "kb_materializer_lifecycle", + "kb_materializer_risk", + "kb_materializer_staking", + "kb_materializer_token_accounts", + "kb_materializer_transaction_annotations", + "kb_pipeline", + "kb_rpc", + "kb_store_pg", + "kb_wallet" + ] + }, + { + "name": "file_mainnet_research_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "*" + ] + }, + { + "name": "file_mainnet_research_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "*" + ] + }, + { + "name": "file_mainnet_research_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "*" + ] + }, + { + "name": "file_mainnet_research_app", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/app.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_app_demo_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_app_demo/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_app_demo_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_app_demo/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_app_demo_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_app_demo/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_decoder_solana_core_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_decoder_solana_core/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_solana_core" + ] + }, + { + "name": "file_kb_decoder_solana_core_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_decoder_solana_core/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_solana_core" + ] + }, + { + "name": "file_kb_decoder_solana_core_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_decoder_solana_core/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_solana_core" + ] + }, + { + "name": "file_kb_decoder_spl_memo_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_decoder_spl_memo/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_memo" + ] + }, + { + "name": "file_kb_decoder_spl_memo_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_decoder_spl_memo/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_memo" + ] + }, + { + "name": "file_kb_decoder_spl_memo_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_decoder_spl_memo/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_memo" + ] + }, + { + "name": "file_kb_decoder_spl_associated_token_account_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_decoder_spl_associated_token_account/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_associated_token_account" + ] + }, + { + "name": "file_kb_decoder_spl_associated_token_account_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_decoder_spl_associated_token_account/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_associated_token_account" + ] + }, + { + "name": "file_kb_decoder_spl_associated_token_account_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_decoder_spl_associated_token_account/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_associated_token_account" + ] + }, + { + "name": "file_kb_decoder_spl_token_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_decoder_spl_token/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token" + ] + }, + { + "name": "file_kb_decoder_spl_token_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_decoder_spl_token/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token" + ] + }, + { + "name": "file_kb_decoder_spl_token_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_decoder_spl_token/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_token" + ] + }, + { + "name": "file_kb_executor_metadata_spl_name_service_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_metadata_spl_name_service/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_spl_name_service" + ] + }, + { + "name": "file_kb_executor_metadata_spl_name_service_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_metadata_spl_name_service/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_spl_name_service" + ] + }, + { + "name": "file_kb_executor_metadata_spl_name_service_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_metadata_spl_name_service/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_metadata_spl_name_service" + ] + }, + { + "name": "file_kb_executor_solana_core_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_solana_core/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_solana_core" + ] + }, + { + "name": "file_kb_executor_solana_core_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_solana_core/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_solana_core" + ] + }, + { + "name": "file_kb_executor_solana_core_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_solana_core/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_solana_core" + ] + }, + { + "name": "file_kb_execution_solana_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_execution_solana/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_execution_solana" + ] + }, + { + "name": "file_kb_execution_solana_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_execution_solana/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_execution_solana" + ] + }, + { + "name": "file_kb_execution_solana_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_execution_solana/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_execution_solana" + ] + }, + { + "name": "file_kb_executor_spl_account_compression_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_spl_account_compression/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_account_compression" + ] + }, + { + "name": "file_kb_executor_spl_account_compression_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_spl_account_compression/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_account_compression" + ] + }, + { + "name": "file_kb_executor_spl_account_compression_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_spl_account_compression/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_account_compression" + ] + }, + { + "name": "file_kb_executor_spl_memo_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_spl_memo/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_memo" + ] + }, + { + "name": "file_kb_executor_spl_memo_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_spl_memo/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_memo" + ] + }, + { + "name": "file_kb_executor_spl_memo_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_spl_memo/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_memo" + ] + }, + { + "name": "file_kb_executor_spl_associated_token_account_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_spl_associated_token_account/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_associated_token_account" + ] + }, + { + "name": "file_kb_executor_spl_associated_token_account_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_spl_associated_token_account/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_associated_token_account" + ] + }, + { + "name": "file_kb_executor_spl_associated_token_account_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_spl_associated_token_account/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_associated_token_account" + ] + }, + { + "name": "file_kb_executor_spl_token_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_spl_token/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token" + ] + }, + { + "name": "file_kb_executor_spl_token_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_spl_token/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token" + ] + }, + { + "name": "file_kb_executor_spl_token_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_spl_token/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_token" + ] + }, + { + "name": "file_kb_executor_spl_noop_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_spl_noop/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_noop" + ] + }, + { + "name": "file_kb_executor_spl_noop_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_spl_noop/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_noop" + ] + }, + { + "name": "file_kb_executor_spl_noop_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_spl_noop/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_noop" + ] + }, + { + "name": "file_kb_executor_spl_single_pool_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_spl_single_pool/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_single_pool" + ] + }, + { + "name": "file_kb_executor_spl_single_pool_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_spl_single_pool/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_single_pool" + ] + }, + { + "name": "file_kb_executor_spl_single_pool_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_spl_single_pool/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_single_pool" + ] + }, + { + "name": "file_kb_logging_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_logging/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_logging" + ] + }, + { + "name": "file_kb_logging_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_logging/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_logging" + ] + }, + { + "name": "file_kb_logging_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_logging/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_logging" + ] + }, + { + "name": "file_kb_materializer_admin_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_materializer_admin/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_admin" + ] + }, + { + "name": "file_kb_materializer_admin_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_materializer_admin/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_admin" + ] + }, + { + "name": "file_kb_materializer_admin_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_materializer_admin/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_admin" + ] + }, + { + "name": "file_kb_materializer_compliance_audit_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_materializer_compliance_audit/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_compliance_audit" + ] + }, + { + "name": "file_kb_materializer_compliance_audit_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_materializer_compliance_audit/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_compliance_audit" + ] + }, + { + "name": "file_kb_materializer_compliance_audit_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_materializer_compliance_audit/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_compliance_audit" + ] + }, + { + "name": "file_kb_materializer_lifecycle_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_materializer_lifecycle/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_lifecycle" + ] + }, + { + "name": "file_kb_materializer_lifecycle_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_materializer_lifecycle/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_lifecycle" + ] + }, + { + "name": "file_kb_materializer_lifecycle_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_materializer_lifecycle/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_lifecycle" + ] + }, + { + "name": "file_kb_materializer_transaction_annotations_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_materializer_transaction_annotations/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_transaction_annotations" + ] + }, + { + "name": "file_kb_materializer_transaction_annotations_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_materializer_transaction_annotations/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_transaction_annotations" + ] + }, + { + "name": "file_kb_materializer_transaction_annotations_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_materializer_transaction_annotations/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_transaction_annotations" + ] + }, + { + "name": "file_kb_materializer_staking_debug", + "enabled": true, + "level": "debug", + "format": "human", + "path": "logs/mainnet_research/kb_materializer_staking/debug.log", + "targets": [ + "kb_materializer_staking" + ], + "sink": "file", + "rotation": "daily", + "ansi": false + }, + { + "name": "file_kb_materializer_staking_info", + "enabled": true, + "level": "info", + "format": "human", + "path": "logs/mainnet_research/kb_materializer_staking/info.log", + "targets": [ + "kb_materializer_staking" + ], + "sink": "file", + "rotation": "daily", + "ansi": false + }, + { + "name": "file_kb_materializer_staking_error", + "enabled": true, + "level": "error", + "format": "json", + "path": "logs/mainnet_research/kb_materializer_staking/error.jsonl", + "targets": [ + "kb_materializer_staking" + ], + "sink": "file", + "rotation": "daily", + "ansi": false + }, + { + "name": "file_kb_pipeline_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_pipeline/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_pipeline" + ] + }, + { + "name": "file_kb_pipeline_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_pipeline/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_pipeline" + ] + }, + { + "name": "file_kb_pipeline_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_pipeline/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_pipeline" + ] + }, + { + "name": "file_kb_rpc_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_rpc/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_rpc" + ] + }, + { + "name": "file_kb_rpc_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_rpc/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_rpc" + ] + }, + { + "name": "file_kb_rpc_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_rpc/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_rpc" + ] + }, + { + "name": "file_kb_store_pg_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_store_pg/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_store_pg" + ] + }, + { + "name": "file_kb_store_pg_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_store_pg/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_store_pg" + ] + }, + { + "name": "file_kb_store_pg_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_store_pg/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_store_pg" + ] + }, + { + "name": "file_kb_wallet_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_wallet/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_wallet" + ] + }, + { + "name": "file_kb_wallet_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_wallet/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_wallet" + ] + }, + { + "name": "file_kb_wallet_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_wallet/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_wallet" + ] + }, + { + "name": "file_kb_materializer_token_accounts_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_materializer_token_accounts/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_token_accounts" + ] + }, + { + "name": "file_kb_materializer_token_accounts_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_materializer_token_accounts/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_token_accounts" + ] + }, + { + "name": "file_kb_materializer_token_accounts_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_materializer_token_accounts/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_token_accounts" + ] + }, + { + "name": "file_kb_materializer_risk_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_materializer_risk/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_risk" + ] + }, + { + "name": "file_kb_materializer_risk_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_materializer_risk/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_risk" + ] + }, + { + "name": "file_kb_materializer_risk_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_materializer_risk/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_risk" + ] + }, + { + "name": "file_kb_decoder_spl_token_2022_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_decoder_spl_token_2022/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token_2022" + ] + }, + { + "name": "file_kb_decoder_spl_token_2022_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_decoder_spl_token_2022/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token_2022" + ] + }, + { + "name": "file_kb_decoder_spl_token_2022_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_decoder_spl_token_2022/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_token_2022" + ] + }, + { + "name": "file_kb_decoder_metadata_metaplex_token_metadata_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_decoder_metadata_metaplex_token_metadata/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_decoder_metadata_metaplex_token_metadata_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_decoder_metadata_metaplex_token_metadata/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_decoder_metadata_metaplex_token_metadata_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_decoder_metadata_metaplex_token_metadata/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_decoder_spl_elgamal_registry_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_decoder_spl_elgamal_registry/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_decoder_spl_elgamal_registry_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_decoder_spl_elgamal_registry/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_decoder_spl_elgamal_registry_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_decoder_spl_elgamal_registry/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_metadata_metaplex_token_metadata_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_metadata_metaplex_token_metadata/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_executor_metadata_metaplex_token_metadata_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_metadata_metaplex_token_metadata/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_executor_metadata_metaplex_token_metadata_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_metadata_metaplex_token_metadata/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_executor_spl_elgamal_registry_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_spl_elgamal_registry/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_spl_elgamal_registry_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_spl_elgamal_registry/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_spl_elgamal_registry_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_spl_elgamal_registry/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_spl_token_2022_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet_research/kb_executor_spl_token_2022/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token_2022" + ] + }, + { + "name": "file_kb_executor_spl_token_2022_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet_research/kb_executor_spl_token_2022/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token_2022" + ] + }, + { + "name": "file_kb_executor_spl_token_2022_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet_research/kb_executor_spl_token_2022/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_token_2022" + ] + } + ], + "target_filters": [ + { + "target": "hyper", + "level": "warn" + }, + { + "target": "reqwest", + "level": "warn" + }, + { + "target": "tao", + "level": "warn" + }, + { + "target": "wry", + "level": "warn" + } + ] + }, + "database": { + "enabled": true, + "backend": "postgres", + "postgres": { + "url": "postgres://solana:solana@localhost:5432/solana", + "max_connections": 16, + "connect_timeout_ms": 5000, + "auto_initialize_schema": true + }, + "sqlite": { + "path": "data/mainnet-import.sqlite", + "create_if_missing": true, + "busy_timeout_ms": 5000, + "max_connections": 1, + "auto_initialize_schema": true, + "use_wal": true + } + }, + "data": { + "wallets_directory": "wallets", + "logs_directory": "logs" + }, + "solana": { + "http_endpoints": [ + { + "name": "mainnet_public_http", + "enabled": true, + "provider": "solana-public", + "cluster": "mainnet-beta", + "url": "https://api.mainnet.solana.com", + "connect_timeout_ms": 4000, + "request_timeout_ms": 10000, + "max_idle_connections_per_host": 4, + "roles": [ + { + "role": "http_queries", + "enabled": true, + "request_kinds": [ + "get_account_info", + "get_balance", + "get_block_commitment", + "get_block_height", + "get_block_production", + "get_blocks", + "get_blocks_with_limit", + "get_cluster_nodes", + "get_epoch_info", + "get_epoch_schedule", + "get_fee_for_message", + "get_first_available_block", + "get_genesis_hash", + "get_health", + "get_highest_snapshot_slot", + "get_identity", + "get_inflation_governor", + "get_inflation_rate", + "get_inflation_reward", + "get_largest_accounts", + "get_latest_blockhash", + "get_leader_schedule", + "get_max_retransmit_slot", + "get_max_shred_insert_slot", + "get_minimum_balance_for_rent_exemption", + "get_multiple_accounts", + "get_recent_performance_samples", + "get_recent_prioritization_fees", + "get_signature_statuses", + "get_slot", + "get_slot_leader", + "get_slot_leaders", + "get_stake_activation", + "get_stake_minimum_delegation", + "get_supply", + "get_token_account_balance", + "get_token_accounts_by_delegate", + "get_token_accounts_by_owner", + "get_token_largest_accounts", + "get_token_supply", + "get_transaction_count", + "get_version", + "get_vote_accounts", + "is_blockhash_valid", + "request_airdrop" + ], + "priority": 50, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "http_fallback", + "enabled": true, + "request_kinds": [ + "get_account_info", + "get_balance", + "get_block_commitment", + "get_block_height", + "get_block_production", + "get_blocks", + "get_blocks_with_limit", + "get_cluster_nodes", + "get_epoch_info", + "get_epoch_schedule", + "get_fee_for_message", + "get_first_available_block", + "get_genesis_hash", + "get_health", + "get_highest_snapshot_slot", + "get_identity", + "get_inflation_governor", + "get_inflation_rate", + "get_inflation_reward", + "get_largest_accounts", + "get_latest_blockhash", + "get_leader_schedule", + "get_max_retransmit_slot", + "get_max_shred_insert_slot", + "get_minimum_balance_for_rent_exemption", + "get_multiple_accounts", + "get_recent_performance_samples", + "get_recent_prioritization_fees", + "get_signature_statuses", + "get_slot", + "get_slot_leader", + "get_slot_leaders", + "get_stake_activation", + "get_stake_minimum_delegation", + "get_supply", + "get_token_account_balance", + "get_token_accounts_by_delegate", + "get_token_accounts_by_owner", + "get_token_largest_accounts", + "get_token_supply", + "get_transaction_count", + "get_version", + "get_vote_accounts", + "is_blockhash_valid", + "request_airdrop" + ], + "priority": 90, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 3000 + } + ] + }, + { + "name": "helius_primary_http", + "enabled": true, + "provider": "helius", + "cluster": "mainnet-beta", + "url": "https://mainnet.helius-rpc.com/?api-key=95e73621-b539-4a79-af39-3b0877a13857", + "connect_timeout_ms": 4000, + "request_timeout_ms": 10000, + "max_idle_connections_per_host": 8, + "roles": [ + { + "role": "http_queries", + "enabled": true, + "request_kinds": [ + "get_account_info", + "get_balance", + "get_block_commitment", + "get_block_height", + "get_block_production", + "get_blocks", + "get_blocks_with_limit", + "get_cluster_nodes", + "get_epoch_info", + "get_epoch_schedule", + "get_fee_for_message", + "get_first_available_block", + "get_genesis_hash", + "get_health", + "get_highest_snapshot_slot", + "get_identity", + "get_inflation_governor", + "get_inflation_rate", + "get_inflation_reward", + "get_largest_accounts", + "get_latest_blockhash", + "get_leader_schedule", + "get_max_retransmit_slot", + "get_max_shred_insert_slot", + "get_minimum_balance_for_rent_exemption", + "get_multiple_accounts", + "get_recent_performance_samples", + "get_recent_prioritization_fees", + "get_signature_statuses", + "get_slot", + "get_slot_leader", + "get_slot_leaders", + "get_stake_activation", + "get_stake_minimum_delegation", + "get_supply", + "get_token_account_balance", + "get_token_accounts_by_delegate", + "get_token_accounts_by_owner", + "get_token_largest_accounts", + "get_token_supply", + "get_transaction_count", + "get_version", + "get_vote_accounts", + "is_blockhash_valid", + "request_airdrop" + ], + "priority": 10, + "requests_per_second": 8, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 1500 + }, + { + "role": "http_heavy", + "enabled": true, + "request_kinds": [ + "get_block", + "get_program_accounts", + "get_signatures_for_address", + "get_transaction" + ], + "priority": 20, + "requests_per_second": 2, + "burst_capacity": 1, + "max_concurrent_requests": 2, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 1500 + }, + { + "role": "history_backfill", + "enabled": true, + "request_kinds": [ + "get_block", + "get_program_accounts", + "get_signatures_for_address", + "get_transaction" + ], + "priority": 30, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 1500 + }, + { + "role": "http_transactions", + "enabled": false, + "request_kinds": [ + "simulate_transaction", + "send_transaction", + "get_signature_statuses" + ], + "priority": 40, + "requests_per_second": 1, + "burst_capacity": 1, + "max_concurrent_requests": 1, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 3000 + } + ] + } + ], + "ws_endpoints": [ + { + "name": "mainnet_public_ws_slots", + "enabled": true, + "provider": "solana-public", + "cluster": "mainnet-beta", + "url": "wss://api.mainnet.solana.com", + "connect_timeout_ms": 5000, + "request_timeout_ms": 10000, + "unsubscribe_timeout_ms": 1500, + "write_channel_capacity": 256, + "event_channel_capacity": 1024, + "auto_reconnect": false, + "roles": [ + { + "role": "slot_notifications", + "enabled": true, + "request_kinds": [ + "slot_subscribe", + "slots_updates_subscribe" + ], + "priority": 10, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 16, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "root_notifications", + "enabled": true, + "request_kinds": [ + "root_subscribe" + ], + "priority": 20, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 16, + "pause_after_rate_limit_ms": 3000 + } + ] + }, + { + "name": "helius_primary_ws_programs", + "enabled": true, + "provider": "helius", + "cluster": "mainnet-beta", + "url": "wss://mainnet.helius-rpc.com/?api-key=95e73621-b539-4a79-af39-3b0877a13857", + "connect_timeout_ms": 5000, + "request_timeout_ms": 10000, + "unsubscribe_timeout_ms": 1500, + "write_channel_capacity": 512, + "event_channel_capacity": 4096, + "auto_reconnect": false, + "roles": [ + { + "role": "logs_subscribe", + "enabled": true, + "request_kinds": [ + "logs_subscribe_mentions" + ], + "priority": 10, + "requests_per_second": 2, + "burst_capacity": 4, + "max_concurrent_requests": 2, + "max_subscriptions": 128, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "program_subscribe", + "enabled": true, + "request_kinds": [ + "program_subscribe" + ], + "priority": 20, + "requests_per_second": 2, + "burst_capacity": 4, + "max_concurrent_requests": 2, + "max_subscriptions": 128, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "account_subscribe", + "enabled": true, + "request_kinds": [ + "account_subscribe" + ], + "priority": 30, + "requests_per_second": 2, + "burst_capacity": 4, + "max_concurrent_requests": 2, + "max_subscriptions": 128, + "pause_after_rate_limit_ms": 3000 + } + ] + } + ], + "listeners": { + "enabled": true, + "default_commitment": "confirmed", + "log_listeners": [ + { + "name": "pump_swap_logs", + "enabled": true, + "endpoint_role": "program_logs", + "program_id": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA", + "purpose": "Détecter les swaps, créations de pool et changements de liquidité Pump Swap." + }, + { + "name": "raydium_cpmm_logs", + "enabled": true, + "endpoint_role": "program_logs", + "program_id": "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C", + "purpose": "Détecter les créations de pools et swaps Raydium CPMM." + } + ], + "program_listeners": [ + { + "name": "pump_fun_program_accounts", + "enabled": true, + "endpoint_role": "program_logs", + "program_id": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", + "purpose": "Détecter les changements de comptes utiles à la création ou migration de tokens Pump Fun." + } + ], + "account_listeners": [] + } + }, + "wallet": { + "wallet_dir": "wallets/temporary/mainnet_research", + "cluster": "mainnet-beta", + "temporary_wallet_enabled": false, + "temporary_wallet_alias": "mainnet_research-disabled", + "temporary_wallet_persist": false, + "localnet_send_enabled": false, + "devnet_send_enabled": false, + "testnet_send_enabled": false, + "mainnet_send_enabled": false + }, + "execution": { + "dry_run_default": true, + "require_simulation": true, + "require_operator_confirmation": true, + "localnet_max_spend_lamports": 0, + "devnet_max_spend_lamports": 0, + "testnet_max_spend_lamports": 0, + "mainnet_max_spend_lamports": 0, + "max_fee_lamports": 1000000, + "max_compute_unit_price_micro_lamports": 100000, + "recent_blockhash_max_age_slots": 150, + "send_max_retries": 3, + "confirmation_poll_interval_ms": 500, + "confirmation_max_attempts": 120, + "devnet_airdrop_max_lamports": 0 + }, + "demo": { + "live_demo_enabled": true, + "trading_demo_enabled": false + } + }, + { + "name": "mainnet", + "app": { + "name": "khadhroony-bot2", + "environment": "mainnet", + "auto_reconnect_default": false + }, + "logging": { + "default_level": "warn", + "targets": [ + { + "name": "console_mainnet_compact", + "enabled": true, + "sink": "console", + "level": "debug", + "path": "", + "rotation": "none", + "format": "compact", + "ansi": true, + "targets": [ + "kb_app_demo", + "kb_decoder_metadata_metaplex_token_metadata", + "kb_decoder_solana_core", + "kb_decoder_spl_associated_token_account", + "kb_decoder_spl_elgamal_registry", + "kb_decoder_spl_memo", + "kb_decoder_spl_token", + "kb_decoder_spl_token_2022", + "kb_execution_solana", + "kb_executor_metadata_metaplex_token_metadata", + "kb_executor_metadata_spl_name_service", + "kb_executor_solana_core", + "kb_executor_spl_account_compression", + "kb_executor_spl_associated_token_account", + "kb_executor_spl_elgamal_registry", + "kb_executor_spl_memo", + "kb_executor_spl_noop", + "kb_executor_spl_single_pool", + "kb_executor_spl_token", + "kb_executor_spl_token_2022", + "kb_logging", + "kb_materializer_admin", + "kb_materializer_compliance_audit", + "kb_materializer_lifecycle", + "kb_materializer_risk", + "kb_materializer_staking", + "kb_materializer_token_accounts", + "kb_materializer_transaction_annotations", + "kb_pipeline", + "kb_rpc", + "kb_store_pg", + "kb_wallet" + ] + }, + { + "name": "file_mainnet_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "*" + ] + }, + { + "name": "file_mainnet_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "*" + ] + }, + { + "name": "file_mainnet_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "*" + ] + }, + { + "name": "file_mainnet_app", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/app.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_app_demo_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_app_demo/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_app_demo_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_app_demo/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_app_demo_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_app_demo/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_app_demo" + ] + }, + { + "name": "file_kb_decoder_solana_core_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_decoder_solana_core/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_solana_core" + ] + }, + { + "name": "file_kb_decoder_solana_core_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_decoder_solana_core/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_solana_core" + ] + }, + { + "name": "file_kb_decoder_solana_core_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_decoder_solana_core/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_solana_core" + ] + }, + { + "name": "file_kb_decoder_spl_memo_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_decoder_spl_memo/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_memo" + ] + }, + { + "name": "file_kb_decoder_spl_memo_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_decoder_spl_memo/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_memo" + ] + }, + { + "name": "file_kb_decoder_spl_memo_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_decoder_spl_memo/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_memo" + ] + }, + { + "name": "file_kb_decoder_spl_associated_token_account_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_decoder_spl_associated_token_account/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_associated_token_account" + ] + }, + { + "name": "file_kb_decoder_spl_associated_token_account_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_decoder_spl_associated_token_account/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_associated_token_account" + ] + }, + { + "name": "file_kb_decoder_spl_associated_token_account_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_decoder_spl_associated_token_account/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_associated_token_account" + ] + }, + { + "name": "file_kb_decoder_spl_token_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_decoder_spl_token/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token" + ] + }, + { + "name": "file_kb_decoder_spl_token_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_decoder_spl_token/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token" + ] + }, + { + "name": "file_kb_decoder_spl_token_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_decoder_spl_token/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_token" + ] + }, + { + "name": "file_kb_executor_metadata_spl_name_service_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_metadata_spl_name_service/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_spl_name_service" + ] + }, + { + "name": "file_kb_executor_metadata_spl_name_service_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_metadata_spl_name_service/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_spl_name_service" + ] + }, + { + "name": "file_kb_executor_metadata_spl_name_service_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_metadata_spl_name_service/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_metadata_spl_name_service" + ] + }, + { + "name": "file_kb_executor_solana_core_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_solana_core/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_solana_core" + ] + }, + { + "name": "file_kb_executor_solana_core_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_solana_core/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_solana_core" + ] + }, + { + "name": "file_kb_executor_solana_core_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_solana_core/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_solana_core" + ] + }, + { + "name": "file_kb_execution_solana_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_execution_solana/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_execution_solana" + ] + }, + { + "name": "file_kb_execution_solana_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_execution_solana/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_execution_solana" + ] + }, + { + "name": "file_kb_execution_solana_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_execution_solana/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_execution_solana" + ] + }, + { + "name": "file_kb_executor_spl_account_compression_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_spl_account_compression/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_account_compression" + ] + }, + { + "name": "file_kb_executor_spl_account_compression_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_spl_account_compression/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_account_compression" + ] + }, + { + "name": "file_kb_executor_spl_account_compression_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_spl_account_compression/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_account_compression" + ] + }, + { + "name": "file_kb_executor_spl_memo_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_spl_memo/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_memo" + ] + }, + { + "name": "file_kb_executor_spl_memo_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_spl_memo/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_memo" + ] + }, + { + "name": "file_kb_executor_spl_memo_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_spl_memo/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_memo" + ] + }, + { + "name": "file_kb_executor_spl_associated_token_account_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_spl_associated_token_account/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_associated_token_account" + ] + }, + { + "name": "file_kb_executor_spl_associated_token_account_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_spl_associated_token_account/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_associated_token_account" + ] + }, + { + "name": "file_kb_executor_spl_associated_token_account_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_spl_associated_token_account/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_associated_token_account" + ] + }, + { + "name": "file_kb_executor_spl_token_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_spl_token/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token" + ] + }, + { + "name": "file_kb_executor_spl_token_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_spl_token/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token" + ] + }, + { + "name": "file_kb_executor_spl_token_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_spl_token/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_token" + ] + }, + { + "name": "file_kb_executor_spl_noop_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_spl_noop/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_noop" + ] + }, + { + "name": "file_kb_executor_spl_noop_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_spl_noop/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_noop" + ] + }, + { + "name": "file_kb_executor_spl_noop_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_spl_noop/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_noop" + ] + }, + { + "name": "file_kb_executor_spl_single_pool_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_spl_single_pool/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_single_pool" + ] + }, + { + "name": "file_kb_executor_spl_single_pool_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_spl_single_pool/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_single_pool" + ] + }, + { + "name": "file_kb_executor_spl_single_pool_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_spl_single_pool/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_single_pool" + ] + }, + { + "name": "file_kb_logging_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_logging/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_logging" + ] + }, + { + "name": "file_kb_logging_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_logging/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_logging" + ] + }, + { + "name": "file_kb_logging_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_logging/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_logging" + ] + }, + { + "name": "file_kb_materializer_admin_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_materializer_admin/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_admin" + ] + }, + { + "name": "file_kb_materializer_admin_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_materializer_admin/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_admin" + ] + }, + { + "name": "file_kb_materializer_admin_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_materializer_admin/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_admin" + ] + }, + { + "name": "file_kb_materializer_compliance_audit_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_materializer_compliance_audit/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_compliance_audit" + ] + }, + { + "name": "file_kb_materializer_compliance_audit_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_materializer_compliance_audit/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_compliance_audit" + ] + }, + { + "name": "file_kb_materializer_compliance_audit_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_materializer_compliance_audit/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_compliance_audit" + ] + }, + { + "name": "file_kb_materializer_lifecycle_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_materializer_lifecycle/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_lifecycle" + ] + }, + { + "name": "file_kb_materializer_lifecycle_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_materializer_lifecycle/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_lifecycle" + ] + }, + { + "name": "file_kb_materializer_lifecycle_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_materializer_lifecycle/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_lifecycle" + ] + }, + { + "name": "file_kb_materializer_transaction_annotations_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_materializer_transaction_annotations/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_transaction_annotations" + ] + }, + { + "name": "file_kb_materializer_transaction_annotations_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_materializer_transaction_annotations/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_transaction_annotations" + ] + }, + { + "name": "file_kb_materializer_transaction_annotations_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_materializer_transaction_annotations/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_transaction_annotations" + ] + }, + { + "name": "file_kb_materializer_staking_debug", + "enabled": true, + "level": "debug", + "format": "human", + "path": "logs/mainnet/kb_materializer_staking/debug.log", + "targets": [ + "kb_materializer_staking" + ], + "sink": "file", + "rotation": "daily", + "ansi": false + }, + { + "name": "file_kb_materializer_staking_info", + "enabled": true, + "level": "info", + "format": "human", + "path": "logs/mainnet/kb_materializer_staking/info.log", + "targets": [ + "kb_materializer_staking" + ], + "sink": "file", + "rotation": "daily", + "ansi": false + }, + { + "name": "file_kb_materializer_staking_error", + "enabled": true, + "level": "error", + "format": "json", + "path": "logs/mainnet/kb_materializer_staking/error.jsonl", + "targets": [ + "kb_materializer_staking" + ], + "sink": "file", + "rotation": "daily", + "ansi": false + }, + { + "name": "file_kb_pipeline_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_pipeline/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_pipeline" + ] + }, + { + "name": "file_kb_pipeline_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_pipeline/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_pipeline" + ] + }, + { + "name": "file_kb_pipeline_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_pipeline/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_pipeline" + ] + }, + { + "name": "file_kb_rpc_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_rpc/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_rpc" + ] + }, + { + "name": "file_kb_rpc_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_rpc/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_rpc" + ] + }, + { + "name": "file_kb_rpc_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_rpc/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_rpc" + ] + }, + { + "name": "file_kb_store_pg_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_store_pg/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_store_pg" + ] + }, + { + "name": "file_kb_store_pg_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_store_pg/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_store_pg" + ] + }, + { + "name": "file_kb_store_pg_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_store_pg/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_store_pg" + ] + }, + { + "name": "file_kb_wallet_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_wallet/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_wallet" + ] + }, + { + "name": "file_kb_wallet_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_wallet/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_wallet" + ] + }, + { + "name": "file_kb_wallet_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_wallet/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_wallet" + ] + }, + { + "name": "file_kb_materializer_token_accounts_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_materializer_token_accounts/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_token_accounts" + ] + }, + { + "name": "file_kb_materializer_token_accounts_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_materializer_token_accounts/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_token_accounts" + ] + }, + { + "name": "file_kb_materializer_token_accounts_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_materializer_token_accounts/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_token_accounts" + ] + }, + { + "name": "file_kb_materializer_risk_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_materializer_risk/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_risk" + ] + }, + { + "name": "file_kb_materializer_risk_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_materializer_risk/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_materializer_risk" + ] + }, + { + "name": "file_kb_materializer_risk_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_materializer_risk/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_materializer_risk" + ] + }, + { + "name": "file_kb_decoder_spl_token_2022_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_decoder_spl_token_2022/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token_2022" + ] + }, + { + "name": "file_kb_decoder_spl_token_2022_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_decoder_spl_token_2022/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_token_2022" + ] + }, + { + "name": "file_kb_decoder_spl_token_2022_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_decoder_spl_token_2022/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_token_2022" + ] + }, + { + "name": "file_kb_decoder_metadata_metaplex_token_metadata_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_decoder_metadata_metaplex_token_metadata/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_decoder_metadata_metaplex_token_metadata_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_decoder_metadata_metaplex_token_metadata/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_decoder_metadata_metaplex_token_metadata_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_decoder_metadata_metaplex_token_metadata/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_decoder_spl_elgamal_registry_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_decoder_spl_elgamal_registry/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_decoder_spl_elgamal_registry_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_decoder_spl_elgamal_registry/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_decoder_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_decoder_spl_elgamal_registry_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_decoder_spl_elgamal_registry/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_decoder_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_metadata_metaplex_token_metadata_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_metadata_metaplex_token_metadata/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_executor_metadata_metaplex_token_metadata_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_metadata_metaplex_token_metadata/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_executor_metadata_metaplex_token_metadata_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_metadata_metaplex_token_metadata/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_metadata_metaplex_token_metadata" + ] + }, + { + "name": "file_kb_executor_spl_elgamal_registry_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_spl_elgamal_registry/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_spl_elgamal_registry_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_spl_elgamal_registry/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_spl_elgamal_registry_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_spl_elgamal_registry/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_elgamal_registry" + ] + }, + { + "name": "file_kb_executor_spl_token_2022_debug", + "enabled": true, + "sink": "file", + "level": "debug", + "path": "logs/mainnet/kb_executor_spl_token_2022/debug.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token_2022" + ] + }, + { + "name": "file_kb_executor_spl_token_2022_info", + "enabled": true, + "sink": "file", + "level": "info", + "path": "logs/mainnet/kb_executor_spl_token_2022/info.log", + "rotation": "daily", + "format": "human", + "ansi": false, + "targets": [ + "kb_executor_spl_token_2022" + ] + }, + { + "name": "file_kb_executor_spl_token_2022_error", + "enabled": true, + "sink": "file", + "level": "error", + "path": "logs/mainnet/kb_executor_spl_token_2022/error.jsonl", + "rotation": "daily", + "format": "json", + "ansi": false, + "targets": [ + "kb_executor_spl_token_2022" + ] + } + ], + "target_filters": [ + { + "target": "hyper", + "level": "warn" + }, + { + "target": "reqwest", + "level": "warn" + }, + { + "target": "tao", + "level": "warn" + }, + { + "target": "wry", + "level": "warn" + } + ] + }, + "database": { + "enabled": true, + "backend": "postgres", + "postgres": { + "url": "postgres://solana:solana@localhost:5432/solana", + "max_connections": 16, + "connect_timeout_ms": 5000, + "auto_initialize_schema": true + }, + "sqlite": { + "path": "data/mainnet.sqlite", + "create_if_missing": true, + "busy_timeout_ms": 5000, + "max_connections": 1, + "auto_initialize_schema": true, + "use_wal": true + } + }, + "data": { + "wallets_directory": "wallets", + "logs_directory": "logs" + }, + "solana": { + "http_endpoints": [ + { + "name": "mainnet_public_http", + "enabled": true, + "provider": "solana-public", + "cluster": "mainnet-beta", + "url": "https://api.mainnet.solana.com", + "connect_timeout_ms": 4000, + "request_timeout_ms": 10000, + "max_idle_connections_per_host": 4, + "roles": [ + { + "role": "http_queries", + "enabled": true, + "request_kinds": [ + "get_account_info", + "get_balance", + "get_block_commitment", + "get_block_height", + "get_block_production", + "get_blocks", + "get_blocks_with_limit", + "get_cluster_nodes", + "get_epoch_info", + "get_epoch_schedule", + "get_fee_for_message", + "get_first_available_block", + "get_genesis_hash", + "get_health", + "get_highest_snapshot_slot", + "get_identity", + "get_inflation_governor", + "get_inflation_rate", + "get_inflation_reward", + "get_largest_accounts", + "get_latest_blockhash", + "get_leader_schedule", + "get_max_retransmit_slot", + "get_max_shred_insert_slot", + "get_minimum_balance_for_rent_exemption", + "get_multiple_accounts", + "get_recent_performance_samples", + "get_recent_prioritization_fees", + "get_signature_statuses", + "get_slot", + "get_slot_leader", + "get_slot_leaders", + "get_stake_activation", + "get_stake_minimum_delegation", + "get_supply", + "get_token_account_balance", + "get_token_accounts_by_delegate", + "get_token_accounts_by_owner", + "get_token_largest_accounts", + "get_token_supply", + "get_transaction_count", + "get_version", + "get_vote_accounts", + "is_blockhash_valid", + "request_airdrop" + ], + "priority": 50, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "http_fallback", + "enabled": true, + "request_kinds": [ + "get_account_info", + "get_balance", + "get_block_commitment", + "get_block_height", + "get_block_production", + "get_blocks", + "get_blocks_with_limit", + "get_cluster_nodes", + "get_epoch_info", + "get_epoch_schedule", + "get_fee_for_message", + "get_first_available_block", + "get_genesis_hash", + "get_health", + "get_highest_snapshot_slot", + "get_identity", + "get_inflation_governor", + "get_inflation_rate", + "get_inflation_reward", + "get_largest_accounts", + "get_latest_blockhash", + "get_leader_schedule", + "get_max_retransmit_slot", + "get_max_shred_insert_slot", + "get_minimum_balance_for_rent_exemption", + "get_multiple_accounts", + "get_recent_performance_samples", + "get_recent_prioritization_fees", + "get_signature_statuses", + "get_slot", + "get_slot_leader", + "get_slot_leaders", + "get_stake_activation", + "get_stake_minimum_delegation", + "get_supply", + "get_token_account_balance", + "get_token_accounts_by_delegate", + "get_token_accounts_by_owner", + "get_token_largest_accounts", + "get_token_supply", + "get_transaction_count", + "get_version", + "get_vote_accounts", + "is_blockhash_valid", + "request_airdrop" + ], + "priority": 90, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 3000 + } + ] + }, + { + "name": "helius_primary_http", + "enabled": true, + "provider": "helius", + "cluster": "mainnet-beta", + "url": "https://mainnet.helius-rpc.com/?api-key=95e73621-b539-4a79-af39-3b0877a13857", + "connect_timeout_ms": 4000, + "request_timeout_ms": 10000, + "max_idle_connections_per_host": 8, + "roles": [ + { + "role": "http_queries", + "enabled": true, + "request_kinds": [ + "get_account_info", + "get_balance", + "get_block_commitment", + "get_block_height", + "get_block_production", + "get_blocks", + "get_blocks_with_limit", + "get_cluster_nodes", + "get_epoch_info", + "get_epoch_schedule", + "get_fee_for_message", + "get_first_available_block", + "get_genesis_hash", + "get_health", + "get_highest_snapshot_slot", + "get_identity", + "get_inflation_governor", + "get_inflation_rate", + "get_inflation_reward", + "get_largest_accounts", + "get_latest_blockhash", + "get_leader_schedule", + "get_max_retransmit_slot", + "get_max_shred_insert_slot", + "get_minimum_balance_for_rent_exemption", + "get_multiple_accounts", + "get_recent_performance_samples", + "get_recent_prioritization_fees", + "get_signature_statuses", + "get_slot", + "get_slot_leader", + "get_slot_leaders", + "get_stake_activation", + "get_stake_minimum_delegation", + "get_supply", + "get_token_account_balance", + "get_token_accounts_by_delegate", + "get_token_accounts_by_owner", + "get_token_largest_accounts", + "get_token_supply", + "get_transaction_count", + "get_version", + "get_vote_accounts", + "is_blockhash_valid", + "request_airdrop" + ], + "priority": 10, + "requests_per_second": 8, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 1500 + }, + { + "role": "http_heavy", + "enabled": true, + "request_kinds": [ + "get_block", + "get_program_accounts", + "get_signatures_for_address", + "get_transaction" + ], + "priority": 20, + "requests_per_second": 2, + "burst_capacity": 1, + "max_concurrent_requests": 2, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 1500 + }, + { + "role": "history_backfill", + "enabled": true, + "request_kinds": [ + "get_block", + "get_program_accounts", + "get_signatures_for_address", + "get_transaction" + ], + "priority": 30, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 1500 + }, + { + "role": "http_transactions", + "enabled": false, + "request_kinds": [ + "simulate_transaction", + "send_transaction", + "get_signature_statuses" + ], + "priority": 40, + "requests_per_second": 1, + "burst_capacity": 1, + "max_concurrent_requests": 1, + "max_subscriptions": 0, + "pause_after_rate_limit_ms": 3000 + } + ] + } + ], + "ws_endpoints": [ + { + "name": "mainnet_public_ws_slots", + "enabled": true, + "provider": "solana-public", + "cluster": "mainnet-beta", + "url": "wss://api.mainnet.solana.com", + "connect_timeout_ms": 5000, + "request_timeout_ms": 10000, + "unsubscribe_timeout_ms": 1500, + "write_channel_capacity": 256, + "event_channel_capacity": 1024, + "auto_reconnect": false, + "roles": [ + { + "role": "slot_notifications", + "enabled": true, + "request_kinds": [ + "slot_subscribe", + "slots_updates_subscribe" + ], + "priority": 10, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 16, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "root_notifications", + "enabled": true, + "request_kinds": [ + "root_subscribe" + ], + "priority": 20, + "requests_per_second": 4, + "burst_capacity": 8, + "max_concurrent_requests": 4, + "max_subscriptions": 16, + "pause_after_rate_limit_ms": 3000 + } + ] + }, + { + "name": "helius_primary_ws_programs", + "enabled": true, + "provider": "helius", + "cluster": "mainnet-beta", + "url": "wss://mainnet.helius-rpc.com/?api-key=95e73621-b539-4a79-af39-3b0877a13857", + "connect_timeout_ms": 5000, + "request_timeout_ms": 10000, + "unsubscribe_timeout_ms": 1500, + "write_channel_capacity": 512, + "event_channel_capacity": 4096, + "auto_reconnect": false, + "roles": [ + { + "role": "logs_subscribe", + "enabled": true, + "request_kinds": [ + "logs_subscribe_mentions" + ], + "priority": 10, + "requests_per_second": 2, + "burst_capacity": 4, + "max_concurrent_requests": 2, + "max_subscriptions": 128, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "program_subscribe", + "enabled": true, + "request_kinds": [ + "program_subscribe" + ], + "priority": 20, + "requests_per_second": 2, + "burst_capacity": 4, + "max_concurrent_requests": 2, + "max_subscriptions": 128, + "pause_after_rate_limit_ms": 3000 + }, + { + "role": "account_subscribe", + "enabled": true, + "request_kinds": [ + "account_subscribe" + ], + "priority": 30, + "requests_per_second": 2, + "burst_capacity": 4, + "max_concurrent_requests": 2, + "max_subscriptions": 128, + "pause_after_rate_limit_ms": 3000 + } + ] + } + ], + "listeners": { + "enabled": true, + "default_commitment": "confirmed", + "log_listeners": [ + { + "name": "pump_swap_logs", + "enabled": true, + "endpoint_role": "program_logs", + "program_id": "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA", + "purpose": "Détecter les swaps, créations de pool et changements de liquidité Pump Swap." + }, + { + "name": "raydium_cpmm_logs", + "enabled": true, + "endpoint_role": "program_logs", + "program_id": "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C", + "purpose": "Détecter les créations de pools et swaps Raydium CPMM." + } + ], + "program_listeners": [ + { + "name": "pump_fun_program_accounts", + "enabled": true, + "endpoint_role": "program_logs", + "program_id": "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P", + "purpose": "Détecter les changements de comptes utiles à la création ou migration de tokens Pump Fun." + } + ], + "account_listeners": [] + } + }, + "wallet": { + "wallet_dir": "wallets/temporary/mainnet", + "cluster": "mainnet-beta", + "temporary_wallet_enabled": false, + "temporary_wallet_alias": "mainnet-disabled", + "temporary_wallet_persist": false, + "localnet_send_enabled": false, + "devnet_send_enabled": false, + "testnet_send_enabled": false, + "mainnet_send_enabled": false + }, + "execution": { + "dry_run_default": true, + "require_simulation": true, + "require_operator_confirmation": true, + "localnet_max_spend_lamports": 0, + "devnet_max_spend_lamports": 0, + "testnet_max_spend_lamports": 0, + "mainnet_max_spend_lamports": 0, + "max_fee_lamports": 1000000, + "max_compute_unit_price_micro_lamports": 100000, + "recent_blockhash_max_age_slots": 150, + "send_max_retries": 3, + "confirmation_poll_interval_ms": 500, + "confirmation_max_attempts": 120, + "devnet_airdrop_max_lamports": 0 + }, + "demo": { + "live_demo_enabled": true, + "trading_demo_enabled": false + } + } + ] +} diff --git a/migration/khadhroony-bot2-reference/config/schema.config.json b/migration/khadhroony-bot2-reference/config/schema.config.json new file mode 100644 index 0000000..984b88b --- /dev/null +++ b/migration/khadhroony-bot2-reference/config/schema.config.json @@ -0,0 +1,774 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://khadhroony.local/schema/config.schema.json", + "title": "Khadhroony Bot2 configuration", + "type": "object", + "additionalProperties": false, + "required": [ + "active_profile", + "profiles" + ], + "properties": { + "active_profile": { + "$ref": "#/$defs/non_empty_string" + }, + "profiles": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/profile" + } + } + }, + "$defs": { + "non_empty_string": { + "type": "string", + "minLength": 1 + }, + "log_level": { + "type": "string", + "enum": [ + "trace", + "debug", + "info", + "warn", + "error", + "off" + ] + }, + "profile": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "app", + "logging", + "database", + "data", + "solana", + "wallet", + "execution", + "demo" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "app": { + "$ref": "#/$defs/app_section" + }, + "logging": { + "$ref": "#/$defs/logging" + }, + "database": { + "$ref": "#/$defs/database" + }, + "data": { + "$ref": "#/$defs/data" + }, + "solana": { + "$ref": "#/$defs/solana" + }, + "wallet": { + "$ref": "#/$defs/wallet" + }, + "execution": { + "$ref": "#/$defs/execution" + }, + "demo": { + "$ref": "#/$defs/demo" + } + } + }, + "app_section": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "environment", + "auto_reconnect_default" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "environment": { + "$ref": "#/$defs/non_empty_string" + }, + "auto_reconnect_default": { + "type": "boolean" + } + } + }, + "logging": { + "type": "object", + "additionalProperties": false, + "required": [ + "default_level", + "targets", + "target_filters" + ], + "properties": { + "default_level": { + "$ref": "#/$defs/log_level" + }, + "targets": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/log_target" + } + }, + "target_filters": { + "type": "array", + "items": { + "$ref": "#/$defs/log_target_filter" + } + } + } + }, + "log_target": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "enabled", + "sink", + "level", + "path", + "rotation", + "format", + "ansi", + "targets" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "enabled": { + "type": "boolean" + }, + "sink": { + "type": "string", + "enum": [ + "console", + "file" + ] + }, + "level": { + "$ref": "#/$defs/log_level" + }, + "path": { + "type": "string" + }, + "rotation": { + "type": "string", + "enum": [ + "none", + "never", + "daily", + "hourly" + ] + }, + "format": { + "type": "string", + "enum": [ + "human", + "compact", + "pretty", + "json" + ] + }, + "ansi": { + "type": "boolean" + }, + "targets": { + "type": "array", + "items": { + "$ref": "#/$defs/non_empty_string" + } + } + } + }, + "log_target_filter": { + "type": "object", + "additionalProperties": false, + "required": [ + "target", + "level" + ], + "properties": { + "target": { + "$ref": "#/$defs/non_empty_string" + }, + "level": { + "$ref": "#/$defs/log_level" + } + } + }, + "database": { + "type": "object", + "additionalProperties": false, + "required": [ + "enabled", + "backend", + "postgres", + "sqlite" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "backend": { + "type": "string", + "enum": [ + "postgres", + "sqlite" + ] + }, + "postgres": { + "$ref": "#/$defs/postgres" + }, + "sqlite": { + "$ref": "#/$defs/sqlite" + } + } + }, + "postgres": { + "type": "object", + "additionalProperties": false, + "required": [ + "url", + "max_connections", + "connect_timeout_ms", + "auto_initialize_schema" + ], + "properties": { + "url": { + "$ref": "#/$defs/non_empty_string" + }, + "max_connections": { + "type": "integer", + "minimum": 1 + }, + "connect_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "auto_initialize_schema": { + "type": "boolean" + } + } + }, + "sqlite": { + "type": "object", + "additionalProperties": false, + "required": [ + "path", + "create_if_missing", + "busy_timeout_ms", + "max_connections", + "auto_initialize_schema", + "use_wal" + ], + "properties": { + "path": { + "$ref": "#/$defs/non_empty_string" + }, + "create_if_missing": { + "type": "boolean" + }, + "busy_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "max_connections": { + "type": "integer", + "minimum": 1 + }, + "auto_initialize_schema": { + "type": "boolean" + }, + "use_wal": { + "type": "boolean" + } + } + }, + "data": { + "type": "object", + "additionalProperties": false, + "required": [ + "wallets_directory", + "logs_directory" + ], + "properties": { + "wallets_directory": { + "$ref": "#/$defs/non_empty_string" + }, + "logs_directory": { + "$ref": "#/$defs/non_empty_string" + } + } + }, + "solana": { + "type": "object", + "additionalProperties": false, + "required": [ + "http_endpoints", + "ws_endpoints", + "listeners" + ], + "properties": { + "http_endpoints": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/http_endpoint" + } + }, + "ws_endpoints": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/ws_endpoint" + } + }, + "listeners": { + "$ref": "#/$defs/listeners" + } + } + }, + "http_endpoint": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "enabled", + "provider", + "cluster", + "url", + "connect_timeout_ms", + "request_timeout_ms", + "max_idle_connections_per_host", + "roles" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "enabled": { + "type": "boolean" + }, + "provider": { + "$ref": "#/$defs/non_empty_string" + }, + "cluster": { + "$ref": "#/$defs/non_empty_string" + }, + "url": { + "type": "string", + "pattern": "^https?://" + }, + "connect_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "request_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "max_idle_connections_per_host": { + "type": "integer", + "minimum": 1 + }, + "roles": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/endpoint_role" + } + } + } + }, + "ws_endpoint": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "enabled", + "provider", + "cluster", + "url", + "connect_timeout_ms", + "request_timeout_ms", + "unsubscribe_timeout_ms", + "write_channel_capacity", + "event_channel_capacity", + "auto_reconnect", + "roles" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "enabled": { + "type": "boolean" + }, + "provider": { + "$ref": "#/$defs/non_empty_string" + }, + "cluster": { + "$ref": "#/$defs/non_empty_string" + }, + "url": { + "type": "string", + "pattern": "^wss?://" + }, + "connect_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "request_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "unsubscribe_timeout_ms": { + "type": "integer", + "minimum": 1 + }, + "write_channel_capacity": { + "type": "integer", + "minimum": 1 + }, + "event_channel_capacity": { + "type": "integer", + "minimum": 1 + }, + "auto_reconnect": { + "type": "boolean" + }, + "roles": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/endpoint_role" + } + } + } + }, + "endpoint_role": { + "type": "object", + "additionalProperties": false, + "required": [ + "role", + "enabled", + "request_kinds", + "priority", + "requests_per_second", + "burst_capacity", + "max_concurrent_requests", + "max_subscriptions", + "pause_after_rate_limit_ms" + ], + "properties": { + "role": { + "$ref": "#/$defs/non_empty_string" + }, + "enabled": { + "type": "boolean" + }, + "request_kinds": { + "type": "array", + "minItems": 1, + "items": { + "$ref": "#/$defs/non_empty_string" + } + }, + "priority": { + "type": "integer" + }, + "requests_per_second": { + "type": "integer", + "minimum": 1 + }, + "burst_capacity": { + "type": "integer", + "minimum": 1 + }, + "max_concurrent_requests": { + "type": "integer", + "minimum": 1 + }, + "max_subscriptions": { + "type": "integer" + }, + "pause_after_rate_limit_ms": { + "type": "integer", + "minimum": 1 + } + } + }, + "listeners": { + "type": "object", + "additionalProperties": false, + "required": [ + "enabled", + "default_commitment", + "log_listeners", + "program_listeners", + "account_listeners" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "default_commitment": { + "$ref": "#/$defs/non_empty_string" + }, + "log_listeners": { + "type": "array", + "items": { + "$ref": "#/$defs/log_listener" + } + }, + "program_listeners": { + "type": "array", + "items": { + "$ref": "#/$defs/program_listener" + } + }, + "account_listeners": { + "type": "array", + "items": { + "$ref": "#/$defs/account_listener" + } + } + } + }, + "log_listener": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "enabled", + "endpoint_role", + "program_id", + "purpose" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "enabled": { + "type": "boolean" + }, + "endpoint_role": { + "$ref": "#/$defs/non_empty_string" + }, + "program_id": { + "$ref": "#/$defs/non_empty_string" + }, + "purpose": { + "$ref": "#/$defs/non_empty_string" + } + } + }, + "program_listener": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "enabled", + "endpoint_role", + "program_id", + "purpose" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "enabled": { + "type": "boolean" + }, + "endpoint_role": { + "$ref": "#/$defs/non_empty_string" + }, + "program_id": { + "$ref": "#/$defs/non_empty_string" + }, + "purpose": { + "$ref": "#/$defs/non_empty_string" + } + } + }, + "account_listener": { + "type": "object", + "additionalProperties": false, + "required": [ + "name", + "enabled", + "endpoint_role", + "account_pubkey", + "purpose" + ], + "properties": { + "name": { + "$ref": "#/$defs/non_empty_string" + }, + "enabled": { + "type": "boolean" + }, + "endpoint_role": { + "$ref": "#/$defs/non_empty_string" + }, + "account_pubkey": { + "$ref": "#/$defs/non_empty_string" + }, + "purpose": { + "$ref": "#/$defs/non_empty_string" + } + } + }, + "wallet": { + "type": "object", + "additionalProperties": false, + "required": [ + "wallet_dir", + "cluster", + "temporary_wallet_enabled", + "temporary_wallet_alias", + "temporary_wallet_persist", + "localnet_send_enabled", + "devnet_send_enabled", + "testnet_send_enabled", + "mainnet_send_enabled" + ], + "properties": { + "wallet_dir": { + "$ref": "#/$defs/non_empty_string" + }, + "cluster": { + "$ref": "#/$defs/non_empty_string" + }, + "temporary_wallet_enabled": { + "type": "boolean" + }, + "temporary_wallet_alias": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "pattern": "^[A-Za-z0-9][A-Za-z0-9_-]*$" + }, + "temporary_wallet_persist": { + "type": "boolean" + }, + "localnet_send_enabled": { + "type": "boolean" + }, + "devnet_send_enabled": { + "type": "boolean" + }, + "testnet_send_enabled": { + "type": "boolean" + }, + "mainnet_send_enabled": { + "type": "boolean" + } + } + }, + "execution": { + "type": "object", + "additionalProperties": false, + "required": [ + "dry_run_default", + "require_simulation", + "require_operator_confirmation", + "localnet_max_spend_lamports", + "devnet_max_spend_lamports", + "testnet_max_spend_lamports", + "mainnet_max_spend_lamports", + "max_fee_lamports", + "max_compute_unit_price_micro_lamports", + "recent_blockhash_max_age_slots", + "send_max_retries", + "confirmation_poll_interval_ms", + "confirmation_max_attempts", + "devnet_airdrop_max_lamports" + ], + "properties": { + "dry_run_default": { + "type": "boolean" + }, + "require_simulation": { + "type": "boolean" + }, + "require_operator_confirmation": { + "type": "boolean" + }, + "localnet_max_spend_lamports": { + "type": "integer", + "minimum": 0 + }, + "devnet_max_spend_lamports": { + "type": "integer", + "minimum": 0 + }, + "testnet_max_spend_lamports": { + "type": "integer", + "minimum": 0 + }, + "mainnet_max_spend_lamports": { + "type": "integer", + "minimum": 0 + }, + "max_fee_lamports": { + "type": "integer", + "minimum": 1 + }, + "max_compute_unit_price_micro_lamports": { + "type": "integer", + "minimum": 0 + }, + "recent_blockhash_max_age_slots": { + "type": "integer", + "minimum": 1 + }, + "send_max_retries": { + "type": "integer", + "minimum": 0, + "maximum": 4294967295 + }, + "confirmation_poll_interval_ms": { + "type": "integer", + "minimum": 1, + "maximum": 60000 + }, + "confirmation_max_attempts": { + "type": "integer", + "minimum": 1, + "maximum": 10000 + }, + "devnet_airdrop_max_lamports": { + "type": "integer", + "minimum": 0 + } + } + }, + "demo": { + "type": "object", + "additionalProperties": false, + "required": [ + "live_demo_enabled", + "trading_demo_enabled" + ], + "properties": { + "live_demo_enabled": { + "type": "boolean" + }, + "trading_demo_enabled": { + "type": "boolean" + } + } + } + } +} diff --git a/migration/khadhroony-bot2-reference/docs/ACCOUNT_ONLY_CANDIDATES.md b/migration/khadhroony-bot2-reference/docs/ACCOUNT_ONLY_CANDIDATES.md new file mode 100644 index 0000000..b235af9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/ACCOUNT_ONLY_CANDIDATES.md @@ -0,0 +1,25 @@ + + + +# Candidats qui ne sont pas encore des program_id + +Certains identifiants vus dans les explorateurs peuvent être des comptes de configuration, de pool, d'autorité, de registre, de vault ou de PDA. Ils ne doivent pas devenir des crates de décodeur tant que leur owner executable n'est pas prouvé. + +## Bags + +| Identifiant | Source supposée | Statut | Décision | +|------------------------------------------------|-----------------|-----------------------|-----------------------------------------------------------------------------------------------------------| +| `BAGSB9TpGrZxQbEsrEznv5jXXdwyP6AXerN8aVRiAmcv` | Bags | `account_not_program` | Ne pas créer `kb_decoder_bags` comme surface canonique tant que le vrai `program_id` n'est pas identifié. | + +## Règle + +Avant de créer une crate de décodeur : + +1. vérifier que l'adresse est bien un programme exécutable ; +2. vérifier l'owner et les transactions qui l'invoquent ; +3. rattacher les comptes non exécutables à leur `program_id` propriétaire ; +4. documenter les comptes importants dans une matrice séparée du registre des programmes. + +## Bags Fee Share + +Les programmes Fee Share V1/V2 sont maintenant documentés dans `docs/BAGS_FM.md`. Le compte `BAGSB9TpGrZxQbEsrEznv5jXXdwyP6AXerN8aVRiAmcv` reste un compte observé, pas une surface exécutable canonique. diff --git a/migration/khadhroony-bot2-reference/docs/AGAVE_LOCAL_NODE.md b/migration/khadhroony-bot2-reference/docs/AGAVE_LOCAL_NODE.md new file mode 100644 index 0000000..e7b3361 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/AGAVE_LOCAL_NODE.md @@ -0,0 +1,210 @@ + + + +# Nœud Agave local expérimental + +Ce document cadre le jalon `0.3.x`. L'objectif est de mesurer si un nœud Agave local non votant peut devenir une source temps réel utile avant de construire l'ingestion, le backfill et l'extracteur raw vers core en `0.4.x`. + +## Positionnement + +`khadhroony-bot2` ne doit pas embarquer un validateur. Le validateur reste un process externe démarré manuellement ou par script de développement. + +```text +agave-validator local non votant + -> HTTP RPC local + -> WebSocket RPC local + -> demo_ws ou future démo Agave + -> mesures de capacité et de retard + -> décision pour l'ingestion 0.4.x +``` + +Le jalon `0.3.x` est une phase de recherche. Il ne doit pas écrire de raw de production, ne doit pas extraire vers core et ne doit pas déclencher d'achat/revente. + +## Hypothèse à tester + +Un nœud local Agave non votant avec ledger pruné pourrait permettre : + +- `blockSubscribe` local avec transactions complètes ; +- moins de `getTransaction` temps réel ; +- moins de listeners par programme ; +- filtrage local par program id, logs, instructions, balances et discriminators ; +- usage des providers externes surtout pour backfill historique, réparation et `sendTransaction`. + +Cette hypothèse doit être mesurée. Elle ne doit pas devenir une décision d'architecture sans chiffres locaux. + +## Commande de départ + +La commande exacte dépend de la version Agave installée, du snapshot disponible, des ports libres et des ressources locales. La commande de départ à documenter et ajuster est : + +```bash +agave-validator \ + --no-voting \ + --full-rpc-api \ + --enable-rpc-transaction-history \ + --rpc-pubsub-enable-block-subscription \ + --limit-ledger-size \ + --ledger ./data/agave-ledger \ + --accounts ./data/agave-accounts \ + --rpc-port 8899 \ + --dynamic-port-range 8000-8020 +``` + +`` est une limite de shreds, pas une durée exacte ni un nombre exact de blocks. La fenêtre réellement conservée dépend du débit réseau, de la purge, des snapshots, de la vitesse disque et de l'état local du nœud. + +## Préparation locale + +Créer les dossiers explicitement pour éviter de mélanger le ledger expérimental avec d'autres données : + +```bash +mkdir -p ./data/agave-ledger ./data/agave-accounts ./logs/agave_local_research +``` + +Avant chaque essai, noter : + +```text +version agave-validator +commande exacte +valeur N_SHREDS +heure de lancement +état initial du dossier ledger +état initial du dossier accounts +profil de configuration utilisé +endpoint externe de référence +``` + +Ne pas supprimer automatiquement `./data/agave-ledger` ou `./data/agave-accounts` dans le projet. Toute purge doit rester une action manuelle et documentée dans le compte rendu d'essai. + +## Profil de configuration + +`config/example.config.json` contient le profil `agave_local_research`, non actif par défaut. + +```text +HTTP local: http://127.0.0.1:8899 +WS local: ws://127.0.0.1:8900 +``` + +Pour l'utiliser, changer `active_profile` dans un fichier local non versionné ou lancer l'application avec `KB_CONFIG_PATH` pointant vers une copie locale. + +Le profil expose les rôles suivants : + +| Rôle | Usage | +|----------------------|-----------------------------------------------------------| +| `local_node_status` | santé, version et retard du nœud local | +| `http_queries` | lectures RPC légères locales | +| `http_heavy` | `getBlock`, `getTransaction`, `getProgramAccounts` locaux | +| `block_stream` | probe `blockSubscribe` local | +| `slot_notifications` | `slotSubscribe`, `slotsUpdatesSubscribe`, `rootSubscribe` | +| `logs_subscribe` | `logsSubscribe` par mention de programme | +| `program_subscribe` | `programSubscribe` par programme | +| `account_subscribe` | `accountSubscribe` par compte précis | + +Le fallback externe est volontairement séparé. Il ne doit pas masquer un échec local pendant les mesures. + +## Probes minimaux + +Les probes `0.3.2` devront couvrir au minimum : + +| Famille | Méthode | Résultat attendu | +|---------|-------------------------------------------------|--------------------------------------------------| +| HTTP | `getVersion` | version du nœud ou erreur explicite | +| HTTP | `getHealth` | état du nœud ou erreur explicite | +| HTTP | `getSlot` | slot local | +| HTTP | `getBlockHeight` | block height local | +| HTTP | `getMaxShredInsertSlot` | slot maximal de shred inséré | +| WS | `blockSubscribe` avec `transactionDetails=none` | `supported`, `unsupported`, `timeout` ou `error` | +| WS | `slotSubscribe` | subscription id puis notifications | +| WS | `slotsUpdatesSubscribe` | subscription id puis notifications | +| WS | `rootSubscribe` | subscription id puis notifications | +| WS | `logsSubscribe` `mentions` | subscription id ou refus provider | +| WS | `programSubscribe` | subscription id ou refus provider | +| WS | `accountSubscribe` | subscription id ou refus provider | + +Le probe minimal `blockSubscribe` ne doit pas laisser un flux ouvert : + +```text +1. ouvrir la WebSocket ; +2. envoyer blockSubscribe avec transactionDetails=none ; +3. attendre un subscription id ou une erreur ; +4. désabonner immédiatement ; +5. classer supported / unsupported / timeout / error ; +6. journaliser la réponse brute tronquée si nécessaire. +``` + +Paramètres de départ à tester dans `demo_ws` : + +```text +role: block_stream +method: blockSubscribe +filterJson: "all" +configJson: {"commitment":"confirmed","encoding":"json","transactionDetails":"none","showRewards":false} +``` + +Si `"all"` est refusé par la version RPC testée, documenter l'erreur et tester ensuite un filtre plus ciblé par programme dans `0.3.4`. + +## Mesures à collecter + +| Mesure | Commande ou source indicative | Rôle | +|-----------------------------|----------------------------------------------|-----------------------------------| +| taille ledger | `du -sh ./data/agave-ledger` | calibrer `--limit-ledger-size` | +| taille accounts | `du -sh ./data/agave-accounts` | vérifier le coût disque | +| CPU | `pidstat`, `top`, `htop` | mesurer le coût du nœud local | +| RAM | `pidstat`, `free`, `ps` | vérifier la faisabilité locale | +| réseau RX/TX | `nload`, `iftop`, `/proc/net/dev` | mesurer le coût mainnet | +| slot local | `getSlot` local | vérifier l'avancement | +| slot externe | `getSlot` provider de référence | calculer le retard | +| lag slots | slot externe moins slot local | détecter un décrochage | +| lag secondes | lag slots multiplié par estimation slot time | estimer l'impact trading | +| débit WebSocket | compte notifications par seconde | comparer les modes live | +| taille moyenne notification | taille texte JSON ou bytes WS | dimensionner raw_ws_notifications | +| reconnects | logs `kb_app_demo.demo_ws` et nœud | détecter l'instabilité | +| erreurs provider ou Agave | logs et réponses JSON-RPC | classer les limites | + +La mesure de lag secondes reste approximative tant que le slot time local n'est pas calculé sur fenêtre glissante. Le chiffre utile pour la décision reste le couple `lag slots` plus `lag secondes estimées`. + +## Erreurs à documenter sans les masquer + +| Catégorie | Exemples à noter | +|------------|---------------------------------------------------------------------------------------------| +| snapshot | téléchargement lent, snapshot incompatible, absence de snapshot | +| ledger | corruption, purge trop agressive, croissance disque excessive | +| accounts | croissance inattendue, I/O saturée, chemin invalide | +| ports | `8899` occupé, port WS indisponible, `dynamic-port-range` trop étroit | +| ressources | CPU saturé, RAM insuffisante, disque saturé, réseau insuffisant | +| retard | slot local qui n'avance plus, lag croissant, root très en retard | +| RPC | méthode non supportée, timeouts, erreurs JSON-RPC | +| WebSocket | connexion refusée, fermeture serveur, subscription refusée, notifications trop volumineuses | + +Un essai qui échoue est exploitable s'il indique précisément la commande, le contexte, l'erreur et la ressource bloquante. + +## Table de résultats à remplir + +| Date | Agave | N_SHREDS | durée | ledger | accounts | CPU moy/max | RAM moy/max | RX/TX | slot local | slot réf. | lag slots | lag sec. | blockSubscribe none | remarques | +|-----------|-----------|-----------|-----------|-----------|-----------|-------------|-------------|-----------|------------|-----------|-----------|-----------|---------------------|-----------| +| À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | À remplir | + +## Critères de décision pour `0.4.x` + +Le nœud local peut être retenu comme source live principale uniquement si les conditions suivantes sont mesurées : + +- le slot local suit l'endpoint externe avec un retard stable et acceptable ; +- le ledger et les accounts restent soutenables sur le disque local ; +- `blockSubscribe` est supporté et stable avec au moins `transactionDetails=none` ; +- le mode `full` ou `signatures` ne bloque pas la WebView ni le process Rust lorsqu'il sera testé ; +- les reconnects et timeouts restent rares ou récupérables ; +- un fallback externe reste disponible pour backfill, réparation et envoi de transaction. + +Si une de ces conditions échoue, `0.4.x` doit privilégier `slotSubscribe + getBlock`, les listeners ciblés ou une stratégie hybride, sans dépendance obligatoire à Agave local. + +## Sortie attendue de `0.3.x` + +La fin de `0.3.x` doit produire une décision documentée : + +```text +source temps réel principale retenue +fallback temps réel retenu +politique raw_ws_notifications +faisabilité du nœud local +limites ressources observées +prochain design de l'ingestion 0.4.x +``` + diff --git a/migration/khadhroony-bot2-reference/docs/APPLICATION_CRATES.md b/migration/khadhroony-bot2-reference/docs/APPLICATION_CRATES.md new file mode 100644 index 0000000..2a060af --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/APPLICATION_CRATES.md @@ -0,0 +1,28 @@ + + + +# Crates applicatifs + +Les crates applicatifs sont les points d'entrée exécutables ou Tauri du workspace. Ils orchestrent les crates internes mais ne portent pas la logique métier profonde. + +## Nomenclature + +- Les applications ponctuelles ou interactives utilisent le préfixe `kb_app_`. +- `kb_app_cli` contient les commandes de contrôle, import, replay et diagnostic. +- `kb_app_demo` contient la démonstration Tauri. +- `kb_worker` est l'exception autorisée, car il représente un processus de travail long-running. + +## Responsabilités autorisées + +- Lire la configuration. +- Initialiser `kb_logging`. +- Appeler les APIs publiques de `kb_pipeline`. +- Afficher ou exposer les résultats. +- Retourner des codes de sortie ou événements UI. + +## Responsabilités interdites + +- Décoder directement une instruction DEX. +- Écrire directement dans PostgreSQL sans passer par les traits de stockage. +- Réimplémenter les règles de matérialisation. +- Charger ou manipuler des secrets wallet hors `kb_wallet`. diff --git a/migration/khadhroony-bot2-reference/docs/ARCHITECTURE.md b/migration/khadhroony-bot2-reference/docs/ARCHITECTURE.md new file mode 100644 index 0000000..dcb96ec --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/ARCHITECTURE.md @@ -0,0 +1,108 @@ + + + +# Architecture + +L'architecture cible est organisée en couches strictes afin d'éviter de recréer un monolithe. Les noms `raw`, `core`, `obs`, `decode`, `mat`, `agg` et `ops` désignent des couches logiques, pas des schémas PostgreSQL applicatifs. + +## Chaîne principale + +```text +sources RPC HTTP / WebSocket / gRPC + -> kb_rpc + -> kb_model transaction canonique + -> raw + -> core + -> obs + -> decode + -> mat + -> agg + -> strategy + -> execution + +sources historiques déjà indexées + -> kb_external_sources + -> candidats de signatures + -> kb_pipeline + -> hydratation canonique via kb_rpc +``` + +## Responsabilités + +- `kb_rpc` gère les méthodes et flux RPC Solana : JSON-RPC HTTP, WebSocket, Helius/LaserStream et Yellowstone gRPC lorsque ces surfaces seront activées. +- `kb_external_sources` est une crate future réservée aux APIs REST, exports et imports historiques externes, par exemple Solscan Pro ou un CSV officiel. Elle ne produit jamais directement une transaction canonique et ne doit pas être confondue avec un futur index interne PostgreSQL. +- `kb_pipeline` combine la découverte de signatures par `kb_rpc` ou `kb_external_sources` avec l’hydratation canonique par `kb_rpc`. +- `kb_model` définit la transaction Solana canonique indépendante du fournisseur. +- `raw` conserve une transaction canonique unique et rejouable par signature. +- `obs` conserve les observations techniques de source, programme, instruction et discriminator. +- `core` expose les structures Solana normalisées pour les décodeurs. +- `decode` contient les événements protocolairement compris. +- `mat` contient les projections métier. +- `agg` contient les agrégations temporelles ou analytiques. +- `ops` trace les modules, versions et traitements. + +## Acquisition multi-source + +```text +getTransaction JSON-RPC +transactionSubscribe Helius +Yellowstone gRPC +logsSubscribe + hydration + | + v +transaction canonique identique +``` + +Les détails de fournisseur restent dans `kb_sol_obs_transaction_observations`. Ils ne contaminent pas les décodeurs ni les tables core. + +`kb_rpc` ne doit pas être renommée en `kb_com` ou `kb_transport` pour accueillir des sources historiques externes. Ces noms seraient trop génériques et mélangeraient RPC Solana, APIs REST indexées, imports CSV et orchestration métier. Le nom `kb_external_sources` évite la confusion avec l’indexation locale et décrit explicitement une frontière de fournisseurs externes plutôt qu’une simple action de récupération. Si des primitives HTTP réellement communes apparaissent plus tard, elles pourront être extraites dans une crate technique dédiée sans modifier la frontière fonctionnelle entre `kb_rpc` et `kb_external_sources`. + +## Convention DB associée + +Quand une couche logique devient une table Solana PostgreSQL, elle est encodée dans le nom de table : + +```text +kb_sol__ +``` + +Exemples : + +```text +kb_sol_raw_transactions +kb_sol_obs_transaction_observations +kb_sol_core_transactions +kb_sol_obs_program_observations +kb_sol_decode_decoded_events +kb_sol_mat_trade_events +kb_sol_ops_processing_ledger +``` + +Les formes qualifiées héritées, écrites ici avec `DOT` (`raw DOT sol_transactions`, `core DOT sol_instructions`, `obs DOT program_observations`), sont interdites. + +## Frontières interdites + +- Un décodeur ne dépend pas du store concret. +- Un décodeur ne dépend pas du fournisseur ou du transport d’acquisition. +- Un matérialisateur ne dépend pas du RPC. +- Le wallet ne dépend pas des décodeurs. +- L'application de démonstration ne doit pas contourner les APIs de pipeline. +- Une observation de source ne doit pas dupliquer le payload canonique complet. + +## Frontière extraction canonique vers core + +Depuis `0.3.4`, `kb_pipeline` contient un extracteur pur qui dépend de `kb_model` et des contrats `kb_store_core`, mais pas de PostgreSQL ni de Tauri. `kb_store_pg` implémente la transaction atomique et `kb_app_demo` ne fait qu’orchestrer la requête opérateur. + +```text +kb_model::CanonicalTransaction + | + v +kb_pipeline::core_extraction + | CoreExtractionBundle + v +kb_store_core::CoreExtractionStore + | + v +kb_store_pg::PostgresStore +``` + +Les futurs décodeurs consommeront les inputs core contextualisés ; ils ne reliront pas directement le JSON canonique pour reconstruire les comptes, CPI, logs ou balances. diff --git a/migration/khadhroony-bot2-reference/docs/BACKFILL_HTTP.md b/migration/khadhroony-bot2-reference/docs/BACKFILL_HTTP.md new file mode 100644 index 0000000..db6ce6f --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/BACKFILL_HTTP.md @@ -0,0 +1,145 @@ + + + +# Backfill HTTP transactionnel + +## Objectif + +Le jalon `0.3.3` fournit un moteur de backfill réutilisable qui transforme les réponses HTTP Solana standard en transactions canoniques indépendantes du fournisseur. + +```text +signatures explicites +ou getSignaturesForAddress + -> getTransaction + -> kb_model::CanonicalTransaction + -> kb_sol_raw_transactions + -> kb_sol_obs_transaction_observations +``` + +Aucun décodeur DEX, aucune extraction core et aucune source temps réel payante ne sont exécutés dans ce jalon. + +## Sources de signatures + +Le moteur accepte quatre catégories : + +- liste explicite de signatures, une signature par ligne ; +- historique d’un `program_id` ; +- historique d’un mint de token ; +- historique d’une adresse de pool. + +Les trois modes par adresse utilisent la même méthode standard `getSignaturesForAddress`. La catégorie est conservée dans le `filter_code` des observations afin de distinguer `program_before`, `program_after`, `program_latest`, et les équivalents token/pool. + +## Sens de pagination + +### Avant une signature + +Avec une ancre, le paramètre RPC `before` sélectionne les transactions plus anciennes que cette signature. Sans ancre, le mode `Before` omet `before` et commence sur la page la plus récente retournée par le RPC. Le moteur poursuit la pagination jusqu’à atteindre la limite demandée, une page incomplète, la limite de pages ou une demande d’arrêt. + +### Après une signature + +Le moteur transmet directement la signature d’ancrage dans le paramètre RPC `until`. Les pages suivantes combinent le même `until` avec un curseur `before` correspondant à la dernière signature de la page précédente. + +Le mode `after` utilise des pages RPC de 1 000 signatures afin que la limite par défaut de 20 pages puisse couvrir jusqu’à 20 000 signatures plus récentes que l’ancre. Le moteur ne conserve que les `X` candidates les plus proches de l’ancre, en plus de l’ensemble de déduplication borné par `max_pages × 1 000`. + +Une page incomplète signifie que la borne `until` a été atteinte. Si toutes les pages autorisées sont pleines, la campagne échoue avec le nombre de signatures inspectées et demande d’augmenter `max_pages`. Une erreur RPC de borne inconnue est propagée sans être transformée en résultat vide. + +## Limites et retries + +Le moteur combine : + +- les limites saisies par l’opérateur ; +- `requests_per_second` du rôle sélectionné ; +- `max_concurrent_requests` du rôle sélectionné ; +- `pause_after_rate_limit_ms` après une erreur 429 ; +- un backoff borné pour les autres erreurs temporaires. + +Le rôle HTTP doit supporter à la fois : + +```text +get_signatures_for_address +get_transaction +``` + +Le rôle recommandé reste `history_backfill`. + +## Persistance + +Une signature déjà présente dans `kb_sol_raw_transactions` est ignorée avant l’appel `getTransaction`. + +Chaque tentative d’hydratation produit une observation légère : + +- provider et endpoint ; +- protocole `solana_http_json_rpc` ; +- méthode `getTransaction` ; +- origine `backfill` ; +- commitment, session et filtre ; +- timestamps ; +- taille et hash du payload source lorsque disponibles ; +- statut `persisted`, `missing` ou `failed` ; +- erreur normalisée lorsque nécessaire. + +Le payload source complet n’est jamais dupliqué dans la table d’observations. + +## Démo Tauri et arrêt coopératif + +La fenêtre `demo_backfill` regroupe dans des accordéons Bootstrap 5 : + +1. paramètres communs ; +2. signatures explicites ; +3. programme ; +4. token ; +5. pool ; +6. journal et résumé JSON. + +Une seule campagne peut fonctionner à la fois. Le bouton `Arrêter` pose un drapeau d’annulation coopérative lu entre les pages, les candidats, le pacer et les retries. + +Depuis la préversion de clôture `0.3.4-pre.011`, l’annulation ne se limite plus aux frontières entre appels : les futures RPC `getSignaturesForAddress` et `getTransaction` sont mises en concurrence avec un observateur d’arrêt, l’attente du pacer est annulable et une pause de retry, y compris après un 429, est interrompue par sondage borné. L’abandon de la future HTTP empêche une campagne arrêtée d’attendre un timeout réseau complet. + +Le moteur maintient une file bornée par la concurrence effective et cesse d’admettre de nouveaux candidats dès que l’arrêt est demandé. Les candidats non démarrés ne sont ni journalisés comme traités, ni inclus dans `candidates_completed`. + +Le résumé distingue : + +- `candidates_selected` : candidats découverts ; +- `candidates_started` : candidats admis dans la file d’exécution ; +- `candidates_completed` : candidats arrivés à un résultat terminal ; +- `candidates_cancelled` : candidats démarrés puis interrompus avant un résultat terminal ; +- `candidates_not_started` : candidats jamais admis après la demande d’arrêt. + +Les invariants attendus sont : + +```text +candidates_selected = candidates_started + candidates_not_started +candidates_started = candidates_completed + candidates_cancelled +``` + +Pour une campagne `before`, `resume_before_signature` correspond à la dernière signature terminée dans un préfixe contigu de la liste ordonnée. Une transaction terminée hors ordre ne fait pas avancer seule ce curseur. Si aucun candidat n’a terminé, le curseur reste la signature d’ancrage initiale. Cette règle interdit de sauter les candidats non traités lors d’une reprise. + +Le calcul de cette frontière est couvert par les tests unitaires. Une campagne réelle de 500 candidats a déjà validé l’arrêt et la séparation entre terminés, annulés et non démarrés. Le rejeu manuel depuis `resume_before_signature` reste un contrôle opératoire recommandé, mais il ne constitue plus un jalon séparé ni un blocage pour `0.4.0`. + +## Validation finale `0.3.3` + +Validations locales : + +```text +cargo test -p kb_rpc : 54 tests passés +cargo test -p kb_pipeline : 10 tests passés +cargo test -p kb_store_core: 31 tests passés +cargo test -p kb_store_pg : 32 tests passés +cargo test -p kb_app_demo : 38 tests passés +cargo clippy --all-targets : validé +``` + +Les campagnes Tauri ont validé : + +- signatures explicites ; +- programme avant/après ; +- token avant/après ; +- pool avant/après ; +- parcours profond `program_after` avec 5 516 signatures indexées parcourues pour 5 transactions hydratées ; +- arrêt d’une campagne de 500 candidats avec 7 démarrés, 3 terminés, 4 annulés et 493 non démarrés. + +Les diagnostics PostgreSQL ont confirmé 62 transactions canoniques et 62 observations avant les derniers tests d’arrêt. Les tables core restent volontairement vides jusqu’à `0.3.4`. + +## Recherche latest sans ancre + +Pour programme, token et pool, une direction `before` avec ancre absente produit un filtre `*_latest`. La première page est demandée sans `before`, puis les pages suivantes utilisent normalement la dernière signature reçue comme curseur. Une direction `after` sans ancre est refusée, car la borne `until` ne peut pas être déterminée. En cas d’arrêt avant le premier candidat terminé, une campagne `*_latest` reprend depuis la page la plus récente et ne fabrique aucun curseur. diff --git a/migration/khadhroony-bot2-reference/docs/BAGS_FM.md b/migration/khadhroony-bot2-reference/docs/BAGS_FM.md new file mode 100644 index 0000000..221e47b --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/BAGS_FM.md @@ -0,0 +1,26 @@ + + + +# Bags.fm + +## Décision de classification + +`BAGSB9TpGrZxQbEsrEznv5jXXdwyP6AXerN8aVRiAmcv` reste classé comme compte observé et non comme `program_id` exécutable prouvé. + +En revanche, la documentation publique Bags liste deux programmes Fee Share : + +| Surface canonique | Program id | Statut | +|--------------------------|------------------------------------------------|---------| +| `fees_bags_fee_share_v1` | `FEEhPbKVKnco9EXnaY3i4R5rQVUx91wgVfu8qokixywi` | legacy | +| `fees_bags_fee_share_v2` | `FEE2tBhCKAt7shrod19QttSVREUYPiyMzoku1mL1gqVK` | current | + +## Crates réservés + +- `kb_decoder_fees_bags_fee_share_v1` +- `kb_executor_fees_bags_fee_share_v1` +- `kb_decoder_fees_bags_fee_share_v2` +- `kb_executor_fees_bags_fee_share_v2` + +## Rôle dans le trading assisté + +Ces surfaces ne sont pas des DEX directs. Elles peuvent cependant être utiles pour détecter des configurations de frais, claims, partenaires, vaults de frais ou comportements liés à des tokens lancés via Bags. diff --git a/migration/khadhroony-bot2-reference/docs/CODE_REUSE.md b/migration/khadhroony-bot2-reference/docs/CODE_REUSE.md new file mode 100644 index 0000000..2d04b62 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/CODE_REUSE.md @@ -0,0 +1,41 @@ + + + +# Réutilisation de code + +Ce document liste les composants de l'ancien workspace qui peuvent servir de référence pour le nouveau workspace. Le code ne doit pas être copié mécaniquement : chaque portion reprise doit être adaptée aux nouvelles frontières de crates, aux règles de nommage, aux règles `crate::` et au backend PostgreSQL. + +## Composants candidats + +| Composant | Cible dans le nouveau workspace | Décision provisoire | +|--------------------------------------------|-------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------| +| Client RPC HTTP | `kb_rpc` | Réutiliser les idées : retry, backoff, limites, traces, idempotence. Réécrire l'API selon les nouveaux modèles. | +| Pool RPC HTTP | `kb_rpc` | Reprendre le principe de sélection d'endpoint et de contrôle de débit. Isoler les métriques et les erreurs dans `kb_core`. | +| Client WebSocket | `kb_rpc` | Reprendre la gestion ping/pong, subscribe/unsubscribe, shutdown explicite et reconnexion contrôlée. | +| Pool WebSocket | `kb_rpc` | Garder l'idée de pool, mais éviter toute dépendance vers les décodeurs ou le stockage concret. | +| Ledger de replay | `kb_pipeline` et `kb_store_core` | Réécrire en versionné par module, version, scope et input hash. | +| Validation SQL | `validation_sql/` | Porter seulement les contrôles encore utiles : doublons de signatures, events failed, non-swap vers trade, coverage résiduelle. | +| Découvertes de discriminators | `docs/DECODER_SURFACE_AUDIT.md`, `idls/`, futurs catalogues | Conserver comme corpus de décision, pas comme support automatique. | +| Matérialisations trade/liquidity/lifecycle | `kb_materializer_*` | Reprendre les règles validées, mais séparer strictement decode et materialize. | + +## Interdictions + +- Ne pas importer directement l'ancien schéma SQLite dans les nouveaux décodeurs. +- Ne pas faire dépendre `kb_rpc` de `kb_store_pg`. +- Ne pas faire dépendre un décodeur de `kb_pipeline`. +- Ne pas conserver une fonction monolithique de replay global. + +## Critère d'acceptation + +Une portion reprise est acceptée seulement si elle respecte les règles suivantes : + +- responsabilité limitée à un crate cible ; +- documentation de code en anglais ; +- absence de `use` hors traits nécessaires ; +- erreurs typées via `kb_core` ; +- tracing target stable ; +- tests unitaires ou validation de build. + +## Interfaces officielles actuelles + +La réutilisation prioritaire ne concerne pas uniquement l’ancien code. Les enums, types, IDs et encodeurs publiés dans les interfaces officielles Solana/SPL doivent être utilisés lorsqu’ils exposent un contrat compatible avec les règles du workspace. La matrice, les exceptions `bincode` et l’ordre `wincode`/Borsh/parser borné sont maintenus dans `docs/SOLANA_INTERFACE_DEPENDENCIES.md`. diff --git a/migration/khadhroony-bot2-reference/docs/CONFIGURATION.md b/migration/khadhroony-bot2-reference/docs/CONFIGURATION.md new file mode 100644 index 0000000..e85e3bd --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/CONFIGURATION.md @@ -0,0 +1,114 @@ + + + +# Configuration + +La configuration sert de contrat commun entre les applications Tauri, les workers, les tests locaux, les stores, le RPC, le wallet et les exécuteurs. + +## Fichier principal + +```text +config/example.config.json +``` + +Ce fichier ne contient pas de commentaires, car JSON ne le permet pas. + +## Profils + +La configuration racine contient plusieurs profils et un seul profil actif : + +```json +{ + "active_profile": "local_devnet", + "profiles": [] +} +``` + +Les profils fournis sont `local_devnet`, `mainnet_research` et `mainnet`. + +## Logging + +Chaque sortie définit son nom, activation, sink, niveau, chemin, rotation, format, ANSI et liste de targets. Toute crate opérationnelle disposant d'un target `tracing` canonique doit recevoir ses fichiers `debug.log`, `info.log` et `error.jsonl` dédiés dans chaque profil. + +`kb_wallet` devient une crate opérationnelle en `0.4.2-pre.003`; ses logs ne contiennent jamais de secret ou d'octets de keypair. + +## Endpoints HTTP et WS + +Le modèle principal conserve les endpoints HTTP JSON-RPC et WebSocket Solana standards. Les rôles permettent de sélectionner un endpoint selon le type de requête et d'appliquer débit, burst, concurrence, subscriptions et pause 429. + +Les transports gRPC, Helius enrichi et LaserStream restent planifiés dans leurs jalons dédiés. + +## URL et clés API + +Une clé API doit rester sous forme de placeholder dans l'URL : + +```text +https://mainnet.helius-rpc.com/?api-key=${HELIUS_API_KEY} +``` + +Aucune clé réelle ne doit être committée. + +## Wallet + +Le bloc `wallet` contient : + +```text +wallet_dir +temporary_wallet_enabled +temporary_wallet_alias +temporary_wallet_persist +cluster +localnet_send_enabled +devnet_send_enabled +testnet_send_enabled +mainnet_send_enabled +``` + +Le profil local devnet persiste le wallet sous : + +```text +wallets/temporary/local_devnet/local-devnet-operator.json +``` + +Cette persistance est nécessaire pour conserver la même adresse entre plusieurs sessions, recevoir un airdrop et signer une séquence de tests. Le fichier contient un keypair Solana standard non chiffré : il est exclu du dépôt, protégé par permissions privées sur Unix et réservé au laboratoire local/devnet. + +La validation refuse : + +- un alias vide, trop long ou contenant un séparateur de chemin ; +- `temporary_wallet_persist = true` lorsque le wallet temporaire est désactivé ; +- un wallet temporaire actif sur `mainnet-beta`. + +## Exécution + +Le bloc `execution` contient : + +```text +dry_run_default +require_simulation +require_operator_confirmation +localnet_max_spend_lamports +devnet_max_spend_lamports +testnet_max_spend_lamports +mainnet_max_spend_lamports +max_fee_lamports +max_compute_unit_price_micro_lamports +recent_blockhash_max_age_slots +send_max_retries +confirmation_poll_interval_ms +confirmation_max_attempts +devnet_airdrop_max_lamports +``` + +Règles : + +- toute dépense configurée exige la simulation ; +- tout envoi activé exige un plafond de dépense positif pour le cluster correspondant ; +- mainnet exige la confirmation opérateur ; +- le plafond de frais et l'âge maximal du blockhash doivent être strictement positifs ; +- l’intervalle et le nombre de polls de confirmation sont strictement bornés ; +- le plafond d’airdrop Devnet doit rester nul dans les profils non Devnet ; +- `dry_run_default` reste actif dans tous les profils fournis. + +## Règle de sécurité + +Aucun secret ne doit être écrit dans le dépôt, les logs, PostgreSQL ou un payload Tauri. Les futurs backends chiffrés ou hardware wallet resteront confinés dans `kb_wallet` et exposeront uniquement une interface de signature. diff --git a/migration/khadhroony-bot2-reference/docs/CORE_EXTRACTION_CONTRACTS.md b/migration/khadhroony-bot2-reference/docs/CORE_EXTRACTION_CONTRACTS.md new file mode 100644 index 0000000..1a06582 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/CORE_EXTRACTION_CONTRACTS.md @@ -0,0 +1,148 @@ + + + +# Contrats d’extraction core Solana + +## État de clôture `0.3.4-pre.011` + +Le jalon transforme une transaction canonique déjà persistée en graphe core normalisé et rejouable : + +```text +kb_sol_raw_transactions.canonical_json + -> kb_model::CanonicalTransaction + -> validation version/signature/slot/hash + -> kb_pipeline::extract_raw_transaction_to_core + -> CoreExtractionBundle + -> transaction PostgreSQL atomique +``` + +Le payload fournisseur contenu à l’origine dans une réponse RPC n’est jamais relu depuis les observations. La seule source fonctionnelle de l’extracteur est le document canonique de `kb_sol_raw_transactions`. + +## Unité atomique + +Une signature produit un `CoreExtractionBundle` contenant : + +- une transaction core liée par `raw_transaction_id` ; +- les account keys statiques et chargées par ALT ; +- les instructions top-level ; +- les inner instructions ; +- les logs ordonnés ; +- les changements de balances natifs et token ; +- l’identité du processor dans le ledger. + +Le repository PostgreSQL supprime puis recrée le graphe de la signature dans une transaction unique. Un échec avant le commit laisse intact l’état précédemment validé. Le test PostgreSQL de clôture provoque volontairement une violation d’unicité après insertion de la transaction et d’une première account key, puis vérifie l’absence totale de graphe partiel, de changement raw et de succès ledger. + +## Validation de l’entrée + +L’extraction refuse explicitement : + +- un `canonical_json` absent ; +- un `canonical_json_hash` absent ; +- une version différente de `CANONICAL_TRANSACTION_FORMAT_VERSION` ; +- un JSON non désérialisable en `CanonicalTransaction` ; +- une signature ou un slot différent de la ligne raw ; +- un hash recalculé différent du hash stocké ; +- un indice de programme ou de compte hors de l’espace résolu. + +L’identité du ledger est : + +```text +stage = core_extraction +processor_name = canonical_to_core +processor_version = 1 +input_key = signature +input_hash = canonical_json_hash +``` + +## Account keys + +L’espace d’indices est construit strictement dans cet ordre : + +```text +static account keys +loaded writable addresses +loaded readonly addresses +``` + +Pour les comptes statiques, les flags `signer` et `writable` sont dérivés du header Solana. Les loaded writable sont non-signers et writable. Les loaded readonly sont non-signers et readonly. `executable` reste `NULL`, car cette information n’est pas contenue de manière fiable dans le contrat canonique actuel. + +## Instructions + +Les chemins sont déterministes : + +```text +0 +1 +2 +0/0 +0/1 +2/0 +``` + +`accounts_json` conserve, dans l’ordre original, chaque indice et la clé publique résolue. `payload_json` conserve `programIdIndex`, `dataBase64` et `stackHeight`. Un SHA-256 stable du payload JSON est persisté dans `payload_json_hash`. + +## Logs + +Chaque log conserve : + +- son `log_index` global ; +- son texte original ; +- un SHA-256 du texte ; +- un `program_id` et un `instruction_path` uniquement lorsque la pile `invoke/success/failed` permet un rattachement déterministe. + +Lorsque la reconstruction est ambiguë, le lien reste `NULL` plutôt que d’inventer une relation. + +## Balances + +Les changements natifs utilisent les valeurs entières en lamports et stockent pre, post et delta comme chaînes décimales dans JSONB afin d’éviter toute perte de précision. + +Les changements SPL et Token-2022 utilisent exclusivement `amount` et `decimals`. Les valeurs UI du fournisseur ne sont pas utilisées comme source de calcul. Pour un compte créé ou fermé pendant la transaction, le côté absent est représenté explicitement par un montant brut nul avec les mêmes décimales. + +Le `balance_change_index` est déterministe : balances natives par index de compte, puis balances token triées par `(account_index, mint, program_id)`. + +## Modes de sélection + +La campagne peut sélectionner des transactions raw par signatures explicites, état `received`, plage de slots ou programme déjà présent dans les instructions top-level core. Les filtres, limites et scénarios de validation sont détaillés dans `docs/CORE_EXTRACTION_SELECTION_AND_REPLAY.md`. + +Le mode par programme ne découvre pas un programme nouveau : il dépend des lignes déjà présentes dans `kb_sol_core_instructions` et ne couvre pas actuellement les occurrences exclusivement inner. + +## Idempotence et replay + +En mode normal, une entrée ledger `succeeded` ayant la même version et le même hash provoque un skip. + +Un changement de version ou de hash entraîne une nouvelle extraction. Le mode `force_replay` ignore le skip et remplace atomiquement le graphe existant de la signature. + +## Orchestration + +`kb_pipeline::execute_core_extraction` fournit : + +- sélection par signatures ; +- lot raw en état `received` ; +- plage de slots ; +- replay par program id déjà présent dans core ; +- limite et concurrence bornées ; +- arrêt coopératif avant admission de nouveaux candidats ; +- résumé `selected`, `started`, `completed`, `skipped`, `extracted`, `failed`, `cancelled_candidates` et `not_started`. + +La logique de transformation ne dépend ni de Tauri ni d’un décodeur protocolaire. + +## Transactions échouées + +Une transaction Solana ayant `failed = true` reste extraite intégralement lorsque ses métadonnées sont disponibles. Les instructions, inner instructions et logs exécutés avant l’erreur constituent des observations utiles pour les futurs décodeurs. + +Le futur pipeline doit distinguer : + +```text +observation décodée dans une transaction échouée +mutation d’état confirmée dans une transaction réussie +``` + +Une transaction échouée peut produire des observations d’intention, de cause d’échec, de compute budget ou de surface appelée. Elle ne doit pas produire automatiquement un trade, une modification de liquidité ou une candle présentés comme réussis. + +## Validation réelle + +Le skip et le force replay ont été validés sur 14 signatures : `14 skipped`, puis `14 extracted` avec force replay, puis `14 skipped`. Les tables core restent à 70 transactions et le ledger totalise 84 tentatives. Les requêtes `sql/validation/000_core_integrity.sql` ne retournent aucune anomalie. + +## Hors périmètre + +`0.3.4` n’effectue aucun décodage Solana Core/SPL, aucun décodage DEX, aucune matérialisation métier et aucune compaction du JSON canonique. diff --git a/migration/khadhroony-bot2-reference/docs/CORE_EXTRACTION_SELECTION_AND_REPLAY.md b/migration/khadhroony-bot2-reference/docs/CORE_EXTRACTION_SELECTION_AND_REPLAY.md new file mode 100644 index 0000000..34aee86 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/CORE_EXTRACTION_SELECTION_AND_REPLAY.md @@ -0,0 +1,456 @@ + + + +# Sélection et replay de l’extraction core + +## Objet + +Ce document décrit les modes disponibles dans `demo_core_extraction`, leur effet réel sur PostgreSQL et la différence entre : + +- acquisition d’une transaction depuis le réseau ; +- extraction du document canonique vers les tables core ; +- futur décodage protocolaire ; +- future matérialisation métier. + +Dans `0.3.4`, le mot « replay » désigne uniquement le rejeu de l’étape suivante : + +```text +kb_sol_raw_transactions.canonical_json + -> kb_model::CanonicalTransaction + -> CoreExtractionBundle + -> kb_sol_core_transactions + -> kb_sol_core_account_keys + -> kb_sol_core_instructions + -> kb_sol_core_inner_instructions + -> kb_sol_core_logs + -> kb_sol_core_balance_changes + -> kb_sol_ops_processing_ledger +``` + +Aucun appel RPC n’est effectué par cette étape. Aucun décodeur Solana Core, SPL, DEX ou autre protocole n’est exécuté. Aucune ligne métier n’est matérialisée. + +## Cycle complet d’une transaction + +Le cycle prévu est séparé en étapes rejouables : + +```text +réseau RPC + -> acquisition HTTP ou WebSocket + -> transaction canonique raw + -> extraction structurelle core + -> observation et classification + -> décodage protocolaire + -> matérialisation métier + -> agrégations et stratégies +``` + +`0.3.4` couvre uniquement la transition `raw canonique -> core structurel`. + +Les lignes de `kb_sol_core_instructions` sont créées avec `processing_state = pending`. Elles constituent l’entrée des futurs décodeurs prévus à partir de `0.4.x`. + +## Paramètres communs + +### Transactions maximales + +`limit` borne le nombre de transactions sélectionnées par PostgreSQL avant démarrage de la campagne. + +La sélection est ordonnée par : + +```text +slot ASC, signature ASC +``` + +Cette règle s’applique aussi au mode signatures explicites. L’ordre des lignes collées dans la zone de texte n’est donc pas l’ordre d’exécution garanti. + +### Concurrence + +`max_concurrent_extractions` borne le nombre d’extractions admises simultanément. + +Chaque signature reste une unité transactionnelle indépendante. Une transaction Solana produit son graphe core dans une transaction PostgreSQL atomique. + +### Force replay + +Sans `force_replay`, une signature est ignorée lorsque le ledger contient déjà un succès ayant exactement : + +```text +stage = core_extraction +processor_name = canonical_to_core +processor_version = version courante +input_key = signature +input_hash = canonical_json_hash courant +status = succeeded +``` + +Avec `force_replay`, ce skip est désactivé. Le graphe core existant de la signature est supprimé puis recréé dans la même transaction PostgreSQL. Les autres signatures ne sont pas touchées. + +Le force replay ne relance pas `getTransaction` et ne remplace pas le document canonique raw. Il reconstruit uniquement le core depuis le raw déjà présent. + +## Mode signatures explicites + +### Entrée + +La zone de texte accepte une signature par ligne. + +Avant l’appel au pipeline, l’interface : + +- supprime les espaces autour de chaque ligne ; +- ignore les lignes vides ; +- supprime les doublons en conservant la première occurrence ; +- valide chaque valeur comme signature Solana. + +### Sélection PostgreSQL + +Le mode sélectionne uniquement les signatures déjà présentes dans `kb_sol_raw_transactions`. + +Une signature absente de la table raw n’est pas téléchargée automatiquement. Elle n’apparaît simplement pas dans les candidats sélectionnés. Le compteur `selected` peut donc être inférieur au nombre de signatures saisies. + +Aucun filtre de `processing_state` n’est appliqué. Ce mode peut sélectionner une transaction raw : + +- `received` ; +- `core_extracted` ; +- `failed`. + +Le ledger décide ensuite entre skip, extraction ou retry. + +### Usages principaux + +- vérifier l’idempotence sur un échantillon connu ; +- forcer la reconstruction de quelques signatures ; +- retenter explicitement des transactions raw en échec ; +- comparer deux versions de l’extracteur ; +- préparer plus tard un corpus ciblé pour un décodeur. + +### Point d’attention + +Si le nombre de signatures existantes dépasse `limit`, seules les premières selon `slot ASC, signature ASC` sont retenues. + +## Mode transactions en attente + +### Filtre réel + +Le mode pending sélectionne : + +```text +kb_sol_raw_transactions.processing_state = received +``` + +Il ne sélectionne ni les lignes déjà `core_extracted`, ni les lignes `failed`. + +### Transition après succès + +Après commit du graphe core : + +```text +received -> core_extracted +``` + +La même ligne ne sera donc plus sélectionnée lors d’une campagne pending suivante. + +### Transition après échec + +Après un échec d’extraction persistable : + +```text +received -> failed +``` + +La raison est stockée dans `lifecycle_reason` et le ledger reçoit un statut `failed`. + +Une ligne `failed` doit être retentée par signature explicite ou par un futur mode dédié aux erreurs. Une nouvelle campagne pending ne la reprend pas. + +### Usages principaux + +- vider progressivement la file des transactions canoniques nouvellement acquises ; +- exécuter l’extraction courante après un backfill HTTP ; +- traiter un lot borné sans sélectionner manuellement les signatures. + +### Conséquence sur le corpus actuel + +Après la campagne réelle de 70 transactions : + +```text +selected = 70 +extracted = 70 +failed = 0 +``` + +les 70 lignes raw sont normalement passées à `core_extracted`. Une nouvelle campagne pending doit donc sélectionner zéro transaction tant qu’aucune nouvelle transaction raw `received` n’a été acquise. + +## Mode plage de slots + +### Filtre réel + +Le mode sélectionne toutes les transactions raw dont le slot se trouve dans l’intervalle inclusif : + +```text +min_slot <= slot <= max_slot +``` + +Aucun filtre de `processing_state` n’est appliqué. + +### Comportement normal + +Les transactions déjà à jour sont sélectionnées puis comptées comme `skipped` par le ledger. Les transactions reçues, en échec, ou dont la version/hash a changé peuvent être extraites. + +### Comportement avec force replay + +Toutes les transactions sélectionnées sont reconstruites dans la limite configurée. + +### Usages principaux + +- rejouer un intervalle temporel connu ; +- vérifier une régression sur un bloc ou une période ; +- reconstruire un corpus après changement de version de l’extracteur. + +## Mode programme déjà indexé dans core + +### Filtre réel actuel + +Le mode sélectionne les signatures raw pour lesquelles il existe déjà une ligne correspondante dans : + +```text +kb_sol_core_instructions +``` + +avec un `program_id` exactement égal à la valeur demandée. + +La requête actuelle ne recherche pas dans : + +- `kb_sol_core_inner_instructions` ; +- `kb_sol_core_logs` ; +- `kb_sol_core_account_keys` ; +- le document canonique raw. + +### Conséquences + +Ce mode ne peut pas découvrir un programme pour la première fois. Il fonctionne uniquement après une première extraction core ayant déjà résolu au moins une instruction top-level de ce programme. + +Une transaction où le programme apparaît exclusivement en inner instruction n’est pas sélectionnée par ce mode dans son état actuel. + +### Usages principaux + +- reconstruire les transactions d’un programme déjà indexé après changement de version ; +- vérifier une correction de résolution des comptes ou instructions ; +- préparer un sous-corpus structurel avant le futur replay d’un décodeur. + +### Évolution recommandée + +Le futur sélecteur de corpus devrait distinguer explicitement : + +- programme top-level ; +- programme inner ; +- programme observé dans les logs ; +- union de toutes les occurrences core fiables. + +Le mode d’extraction actuel peut rester strictement basé sur les instructions top-level, à condition que cette limite demeure visible dans l’interface. + +## Interprétation des compteurs + +| Compteur | Signification | +|-----------------------|-------------------------------------------------------------| +| `selected` | Candidats retournés par PostgreSQL après filtres et limite. | +| `started` | Candidats admis dans la file d’exécution bornée. | +| `completed` | Candidats arrivés à un résultat terminal. | +| `skipped` | Ledger déjà à jour pour la même version et le même hash. | +| `extracted` | Graphe core écrit et commit réussi. | +| `failed` | Extraction ou persistance terminée en erreur. | +| `cancelledCandidates` | Candidats admis mais annulés avant résultat terminal. | +| `notStarted` | Candidats sélectionnés mais jamais admis après arrêt. | +| `cancelled` | Une demande d’arrêt coopératif a été observée. | + +Pour une campagne terminée normalement : + +```text +completed = skipped + extracted + failed +selected = completed + cancelledCandidates + notStarted +``` + +## Scénarios de validation recommandés + +### Vérifier le skip + +1. Extraire 5 à 10 signatures déjà présentes dans core depuis un outil de sélection. +2. Les coller dans le mode signatures explicites. +3. Laisser `force_replay` désactivé. + +Résultat attendu : + +```text +selected = N +skipped = N +extracted = 0 +failed = 0 +``` + +### Vérifier le force replay + +1. Réutiliser exactement les mêmes signatures. +2. Activer `force_replay`. + +Résultat attendu : + +```text +selected = N +skipped = 0 +extracted = N +failed = 0 +``` + +Les cardinalités globales des tables core doivent rester stables pour ces signatures. Le `attempt_count` du ledger doit augmenter. + +### Vérifier un changement de version ou de hash + +Un changement réel de `processor_version` ou de `canonical_json_hash` doit provoquer une nouvelle extraction même sans `force_replay`. + +Cette vérification doit être effectuée dans une base de test dédiée. Le hash canonique d’une ligne de production ne doit pas être modifié manuellement. + +### Vérifier le rollback + +Le rollback atomique doit être validé par un test PostgreSQL avec injection d’une erreur entre les insertions du graphe et le commit. + +La méthode recommandée est un test d’intégration sur `solana_test`, et non une corruption volontaire de la base principale. + +## Sélecteur de corpus dans l’application + +La fenêtre read-only dédiée est : + +```text +demo_sql_replay_candidates +``` + +Elle est accessible depuis le menu principal et depuis l’en-tête de `demo_core_extraction`. `demo_sql_diag` reste limité à la connexion, au profil actif, aux migrations et aux cardinalités. + +Le sélecteur ne déclenche aucune acquisition RPC, extraction core, opération de décodage ou matérialisation. Il lit uniquement les tables PostgreSQL déjà alimentées. + +## Tableau des transactions et signatures + +Les filtres PostgreSQL disponibles sont : + +- fragment de signature ; +- slot minimum et maximum inclusifs ; +- état raw `received`, `core_extracted`, `decoded`, `materialized` ou `failed` ; +- statut ledger `not_started`, `running`, `succeeded` ou `failed` ; +- program ID exact ; +- portée du programme : `any`, `outer`, `inner` ou `logs` ; +- entité exacte : `mint`, `owner` ou `account_key` ; +- limite ; +- ordre par slot croissant ou décroissant. + +Chaque résultat affiche : + +- signature et slot ; +- état raw et rétention ; +- présence de la transaction core et éventuel échec Solana ; +- dernier statut du ledger `core_extraction` ; +- version et nombre de tentatives ; +- nombres d’instructions et de programmes outer/inner ; +- date de dernière mise à jour raw. + +Le filtre de programme inspecte les instructions outer, les inner instructions et les logs auxquels un `program_id` a pu être rattaché de manière prudente. Une occurrence dans les logs n’est donc visible que lorsque le rattachement core a produit un `program_id` non nul. + +## Tableau des programmes + +Le tableau agrège : + +```text +kb_sol_core_instructions.program_id +kb_sol_core_inner_instructions.program_id +kb_sol_core_logs.program_id +``` + +Pour chaque programme, il expose : + +- le `program_id` ; +- le nombre de transactions distinctes ; +- le nombre d’instructions outer ; +- le nombre d’instructions inner ; +- le nombre de logs reliés ; +- le slot minimum et maximum. + +La sélection d’un programme peut alimenter le filtre programme du tableau des transactions, puis charger les signatures correspondantes. + +## Tableau des entités + +Le tableau agrège trois familles : + +- `mint` depuis `kb_sol_core_balance_changes.mint` ; +- `owner` depuis `kb_sol_core_balance_changes.owner` ; +- `account_key` depuis `kb_sol_core_account_keys.account_key`. + +Chaque ligne indique le nombre de transactions distinctes, le nombre d’occurrences et la plage de slots. Une entité sélectionnée peut alimenter le filtre du tableau des transactions. + +Les account keys ne sont pas présentées comme wallet, pool, mint ou token account sans classification supplémentaire. + +## DataTables, copie et exports + +La fenêtre utilise cinq tableaux DataTables avec intégration Bootstrap 5 et extension Select : + +1. transactions et signatures ; +2. programmes ; +3. mints ; +4. owners ; +5. account keys. + +Chaque tableau affiche 10 lignes par défaut. Une checkbox par ligne et une checkbox de header permettent de sélectionner le jeu filtré. Les filtres possèdent un bouton de reset qui restaure les valeurs par défaut, efface la recherche locale, désélectionne les lignes et recharge PostgreSQL. + +Les signatures, program IDs, mints, owners et account keys sont tronqués visuellement sous la forme `préfixe…suffixe`. La valeur complète reste utilisée par le tri, la recherche, les tooltips, la copie et l’export. Un bouton de copie individuel évite les espaces parasites liés à une sélection manuelle. + +La règle d’export est la suivante : + +1. lorsqu’une ou plusieurs lignes sont sélectionnées, seules ces lignes sont exportées ; +2. sans sélection, toutes les lignes correspondant au filtre local DataTables sont exportées ; +3. la limite PostgreSQL reste la borne supérieure du corpus chargé. + +Le frontend construit un CSV UTF-8 avec BOM, séparateur `;` et fins de ligne CRLF. Une commande Rust l’écrit dans : + +```text +data/exports_csv/ +``` + +Le backend ajoute un suffixe numérique lorsque le nom existe déjà. Ce chemin a été validé sous Tauri/Linux et ne dépend pas du téléchargement HTML du WebView. + +La copie multiligne des signatures est directement compatible avec la zone `Signatures explicites` de `demo_core_extraction`. + +## Garanties et limites + +- commandes Tauri read-only ; +- requêtes SQL statiques et paramétrées ; +- aucune zone permettant d’exécuter du SQL libre ; +- limite obligatoire comprise entre `1` et `5 000` ; +- filtres exacts pour programme et entité afin d’éviter une sélection ambiguë ; +- recherche locale DataTables appliquée après le chargement serveur ; +- aucune découverte de transaction absente du raw ; +- aucun décodage de protocole ; +- aucun changement d’état raw, core ou ledger. + +Les futurs écrans de `0.4.x` pourront réutiliser le même principe pour les replays de décodeurs et de matérialiseurs. +## Utilisation du sélecteur SQL + +`demo_sql_replay_candidates` aide à constituer des corpus sans requête manuelle dans pgAdmin ou DBeaver. Il reste séparé du worker : il lit les tables raw/core/ledger, puis produit des signatures ou des filtres réutilisables. + +Dans l’onglet programmes, « Utiliser comme filtre transactions » prend exactement un programme coché. À défaut de sélection, une unique ligne restant après le filtre local est acceptée. L’action renseigne le program ID exact, choisit la portée `any`, ouvre l’onglet transactions et recharge la liste. + +Le sélecteur des programmes connus est alimenté par `kb_program_ids`, mais la saisie libre reste disponible pour les programmes observés qui ne sont pas encore enregistrés. + +Les exports CSV sont écrits dans `data/exports_csv/` par le backend Rust. Ils servent à l’audit et à la conservation d’un corpus ; pour rejouer immédiatement des signatures, la copie multiligne reste le chemin le plus direct. + +Les notions de pool et de paire ne sont pas encore disponibles à ce stade structurel. Une account key core ne suffit pas à prouver qu’un compte représente un pool ou une paire. Cette navigation sera ajoutée après matérialisation de catalogues sémantiques par les décodeurs. + + +## Validation réelle de l’idempotence + +Un échantillon de 14 signatures a produit les résultats suivants : + +```text +mode normal : extracted=0, skipped=14 +force replay: extracted=14, skipped=0 +mode normal : extracted=0, skipped=14 +``` + +Le nombre de transactions core est resté à 70. Le ledger contient 70 lignes `succeeded` et 84 tentatives, soit les 70 tentatives initiales plus les 14 remplacements forcés. Les requêtes d’intégrité n’ont détecté aucune anomalie. + +## Politique future pour les transactions échouées + +Les transactions on-chain échouées restent candidates au décodage. Elles conservent des informations sur les programmes appelés, les instructions exécutées avant l’erreur, les logs, le compute consommé et la cause d’échec. + +Les décodeurs devront produire des observations marquées par le statut de la transaction. Les matérialiseurs ne devront pas convertir ces observations en mutations d’état réussies, trades confirmés, changements de liquidité ou candles normales. diff --git a/migration/khadhroony-bot2-reference/docs/CORE_PROGRAM_IDS.md b/migration/khadhroony-bot2-reference/docs/CORE_PROGRAM_IDS.md new file mode 100644 index 0000000..7374b9b --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/CORE_PROGRAM_IDS.md @@ -0,0 +1,63 @@ + + + +# Identifiants core Solana et SPL + +Ce fichier sépare les identifiants primitifs Solana/SPL du registre des surfaces applicatives. Cette séparation évite de traiter `system`, `token`, `memo`, `sysvar` ou les loaders comme des DEX/routers. + +## Règle de classification + +- Les programmes runtime natifs, loaders et précompiles relèvent de `kb_decoder_solana_core`. +- Chaque programme SPL vérifié conserve une frontière spécialisée : Token, Token-2022, ATA, Memo, Stake Pool, Single Pool, Account Compression, Noop ou Name Service. +- Les sysvars sont des comptes connus à reconnaître pendant l'extraction, pas des surfaces DEX à décoder comme des programmes applicatifs. +- `stake_pool` et `single_pool` sont distincts du Stake Program natif et possèdent leurs propres crates. + +## Table de contrôle + +| Canonique | Program ID | Source | Type | Crate cible | Statut | +|--------------------------------------------|------------------------------------------------|-----------------------------------|----------------------|-------------------------------------------|-------------------------------------| +| `core_solana_address_lookup_table_v1` | `AddressLookupTab1e1111111111111111111111111` | `address_lookup_table` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_bpf_loader_deprecated_v1` | `BPFLoader1111111111111111111111111111111111` | `bpf_loader_deprecated` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_bpf_loader_v2` | `BPFLoader2111111111111111111111111111111111` | `bpf_loader` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_bpf_loader_upgradeable_v1` | `BPFLoaderUpgradeab1e11111111111111111111111` | `bpf_loader_upgradeable` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_compute_budget_v1` | `ComputeBudget111111111111111111111111111111` | `compute_budget` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_config_v1` | `Config1111111111111111111111111111111111111` | `config` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_ed25519_v1` | `Ed25519SigVerify111111111111111111111111111` | `ed25519` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_feature_v1` | `Feature111111111111111111111111111111111111` | `feature` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_incinerator_v1` | `1nc1nerator11111111111111111111111111111111` | `incinerator` | `well_known_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_loader_v4` | `LoaderV411111111111111111111111111111111111` | `loader_v4` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_native_loader_v1` | `NativeLoader1111111111111111111111111111111` | `native_loader` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_secp256k1_v1` | `KeccakSecp256k11111111111111111111111111111` | `secp256k1` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_secp256r1_v1` | `Secp256r1SigVerify1111111111111111111111111` | `secp256r1` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_slashing_v1` | `S1ashing11111111111111111111111111111111111` | `slashing` | `stateless_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_stake_config_v1` | `StakeConfig11111111111111111111111111111111` | `stake_config` | `well_known_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_stake_v1` | `Stake11111111111111111111111111111111111111` | `stake` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_system_v1` | `11111111111111111111111111111111` | `system` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_sysvar_clock_v1` | `SysvarC1ock11111111111111111111111111111111` | `sysvar_clock` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_epoch_rewards_v1` | `SysvarEpochRewards1111111111111111111111111` | `sysvar_epoch_rewards` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_epoch_schedule_v1` | `SysvarEpochSchedu1e111111111111111111111111` | `sysvar_epoch_schedule` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_fees_v1` | `SysvarFees111111111111111111111111111111111` | `sysvar_fees` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_instructions_v1` | `Sysvar1nstructions1111111111111111111111111` | `sysvar_instructions` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_last_restart_slot_v1` | `SysvarLastRestartS1ot1111111111111111111111` | `sysvar_last_restart_slot` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_recent_blockhashes_v1` | `SysvarRecentB1ockHashes11111111111111111111` | `sysvar_recent_blockhashes` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_rent_v1` | `SysvarRent111111111111111111111111111111111` | `sysvar_rent` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_rewards_v1` | `SysvarRewards111111111111111111111111111111` | `sysvar_rewards` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_slot_hashes_v1` | `SysvarS1otHashes111111111111111111111111111` | `sysvar_slot_hashes` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_slot_history_v1` | `SysvarS1otHistory11111111111111111111111111` | `sysvar_slot_history` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_stake_history_v1` | `SysvarStakeHistory1111111111111111111111111` | `sysvar_stake_history` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_v1` | `Sysvar1111111111111111111111111111111111111` | `sysvar` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_vote_v1` | `Vote111111111111111111111111111111111111111` | `vote` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_zk_elgamal_proof_v1` | `ZkE1Gama1Proof11111111111111111111111111111` | `zk_elgamal_proof` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_zk_token_proof_v1` | `ZkTokenProof1111111111111111111111111111111` | `zk_token_proof` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_spl_associated_token_account_v1` | `ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL` | `associated_token_account` | `program` | `kb_decoder_spl_associated_token_account` | `core_handled` | +| `core_spl_account_compression_v1` | `cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK` | `spl_account_compression` | `program` | `kb_decoder_spl_account_compression` | `core_specialized_reserved_current` | +| `core_spl_memo_v1` | `Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo` | `spl_memo_v1` | `program` | `kb_decoder_spl_memo` | `core_specialized_reserved_current` | +| `core_spl_memo_v3` | `MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr` | `spl_memo_v3` | `program` | `kb_decoder_spl_memo` | `core_specialized_reserved_current` | +| `core_spl_memo_v4` | `Memo4c2pN8afCj432Lb7RMVKi9PbQnnW7ewFFaV3oAH` | `spl_memo_v4` | `program` | `kb_decoder_spl_memo` | `core_specialized_reserved_current` | +| `core_spl_noop_v1` | `noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV` | `spl_noop` | `program` | `kb_decoder_spl_noop` | `core_specialized_reserved_current` | +| `core_spl_single_pool_v1` | `SVSPxpvHdN29nkVg9rPapPNDddN5DipNLRUFhyjFThE` | `spl_single_pool` | `program` | `kb_decoder_spl_single_pool` | `core_specialized_reserved_current` | +| `core_spl_name_service_v1` | `namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX` | `spl_name_service` | `program` | `kb_decoder_metadata_spl_name_service` | `core_specialized_reserved_current` | +| `core_spl_stake_pool_v1` | `SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy` | `stake_pool` | `program` | `kb_decoder_spl_stake_pool` | `core_specialized_reserved_current` | +| `core_spl_token_2022_v2022` | `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` | `token_2022` | `program` | `kb_decoder_spl_token_2022` | `core_handled` | +| `core_spl_token_2022_elgamal_registry_v1` | `regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg` | `spl_token_2022_elgamal_registry` | `program` | `kb_decoder_spl_token_2022` | `core_specialized_reserved_current` | +| `core_spl_token_v1` | `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA` | `token` | `program` | `kb_decoder_spl_token` | `core_handled` | diff --git a/migration/khadhroony-bot2-reference/docs/CORE_STORE.md b/migration/khadhroony-bot2-reference/docs/CORE_STORE.md new file mode 100644 index 0000000..6cf07fc --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/CORE_STORE.md @@ -0,0 +1,73 @@ + + + +# Store core Solana + +## Évolution `0.3.4` + +Le store core historique devient alimenté par un extracteur transactionnel à partir de `kb_sol_raw_transactions.canonical_json`. + +| Table | Rôle | +|----------------------------------|------------------------------------------------------------| +| `kb_sol_core_transactions` | Transaction normalisée, statut et lien vers la ligne raw. | +| `kb_sol_core_account_keys` | Espace résolu statique + ALT avec flags Solana. | +| `kb_sol_core_instructions` | Instructions top-level et payload canonique hashé. | +| `kb_sol_core_inner_instructions` | Inner instructions rattachées à un chemin parent. | +| `kb_sol_core_logs` | Logs ordonnés, texte hashé et lien d’invocation optionnel. | +| `kb_sol_core_balance_changes` | Deltas natifs, SPL Token et Token-2022. | +| `kb_sol_ops_processing_ledger` | Version, hash, statut et tentatives du processor. | + +## Écriture atomique + +Une extraction réussie exécute dans la même transaction PostgreSQL : + +1. validation du bundle ; +2. recherche d’un graphe core existant ; +3. suppression en cascade de ce graphe pour la signature ; +4. insertion de la transaction et de toutes ses lignes filles ; +5. passage de la ligne raw à `core_extracted` ; +6. upsert du ledger en `succeeded` ; +7. commit. + +Une erreur déclenche le rollback implicite de la transaction. L’enregistrement terminal d’un échec utilise une transaction dédiée et marque la ligne raw `failed` avec un diagnostic rejouable. + +## Clés d’idempotence + +Les contraintes actives restent : + +```text +core transaction : signature +account key : signature + account_index +instruction top-level : signature + instruction_path +inner instruction : signature + instruction_path +log : signature + log_index +balance change : signature + balance_change_index +ledger : stage + processor_name + processor_version + input_key +``` + +Le ledger ajoute `input_hash` à la décision de skip sans l’ajouter à la clé unique. Un nouvel hash met à jour la même identité de processor et augmente `attempt_count`. + +## Sélection raw + +Le contrat `CoreExtractionSelectionFilter` permet : + +- une liste de signatures ; +- une plage inclusive de slots ; +- un état raw ; +- un program id déjà indexé dans les instructions core ; +- une limite stricte. + +Le filtre par program id est un mode de replay. Il ne peut pas découvrir un programme dans une transaction qui n’a jamais encore été extraite. + +## Repositories + +`CoreExtractionStore` isole le pipeline du backend : + +```text +list_raw_transactions_for_core_extraction +is_core_extraction_current +persist_core_extraction +mark_core_extraction_failed +``` + +Les repositories historiques d’insertion unitaire restent disponibles, mais le worker `0.3.4` utilise exclusivement l’écriture atomique du bundle. diff --git a/migration/khadhroony-bot2-reference/docs/DATABASE.md b/migration/khadhroony-bot2-reference/docs/DATABASE.md new file mode 100644 index 0000000..df13aaa --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/DATABASE.md @@ -0,0 +1,155 @@ + + + +# Base de données + +PostgreSQL est le backend principal prévu pour `khadhroony-bot2`. `0.2.0` fixe uniquement les conventions de stockage : les migrations complètes, repositories SQL et diagnostics applicatifs sont reportés aux jalons suivants. + +## Décision PostgreSQL + +Le projet ne crée pas de schémas PostgreSQL applicatifs explicites. + +Le store utilise le schéma courant du profil PostgreSQL, généralement `public`. Le choix du schéma reste donc une responsabilité de configuration ou d'administration PostgreSQL, pas une responsabilité des migrations applicatives. + +Interdits, écrits avec `DOT` pour que les audits textuels simples ne confondent pas documentation et usage réel : + +```text +raw DOT kb_sol_rpc_transactions +core DOT kb_sol_transactions +obs DOT kb_sol_program_observations +decode DOT kb_sol_decoded_events +mat DOT kb_sol_trade_events +catalog DOT kb_sol_tokens +ops DOT kb_sol_processing_ledger +``` + +Autorisés : + +```text +kb_sol_raw_transactions +kb_sol_core_transactions +kb_sol_obs_program_observations +kb_sol_decode_decoded_events +kb_sol_mat_trade_events +kb_sol_catalog_tokens +kb_sol_ops_processing_ledger +``` + +## Format canonique des tables Solana + +Toutes les tables Solana applicatives suivent ce format : + +```text +kb_sol__ +``` + +Domaines autorisés au départ : + +| Domaine | Rôle logique | Exemple de table | +|-----------|-------------------------------------------------------------------------------------|---------------------------------------| +| `raw` | transactions Solana canoniques, source-indépendantes et rejouables | `kb_sol_raw_transactions` | +| `core` | extraction Solana générique normalisée | `kb_sol_core_instructions` | +| `obs` | observations techniques de source, programmes, instructions, logs et discriminators | `kb_sol_obs_transaction_observations` | +| `decode` | événements décodés par les décodeurs | `kb_sol_decode_decoded_events` | +| `mat` | projections métier matérialisées | `kb_sol_mat_trade_events` | +| `catalog` | tokens, pools, paires et références stables | `kb_sol_catalog_pairs` | +| `agg` | agrégations temporelles ou analytiques | `kb_sol_agg_pair_candles` | +| `ops` | ledger de traitement, migrations applicatives et diagnostics | `kb_sol_ops_processing_ledger` | +| `wallet` | métadonnées wallet non sensibles | `kb_sol_wallet_accounts` | + +Le domaine reste un préfixe de nom de table, jamais un schéma PostgreSQL. + + +## Décision de phasage à partir de `0.3.x` + +`0.3.x` corrige le modèle d’acquisition avant les décodeurs : transaction canonique source-indépendante, observations de source légères, backfill HTTP sur comptes gratuits et extraction vers `core`. + +Les flux payants Helius `transactionSubscribe` et Yellowstone gRPC sont reportés à `0.10.x`, après les décodeurs Core, Pump, Meteora, Raydium, Orca et Jupiter. Ils devront alimenter exactement le même contrat canonique que `getTransaction`. + +## Tables candidates initiales + +Ces tables sont candidates, pas toutes implémentées en `0.2.0`. + +| Table | Jalons pressentis | Rôle | +|---------------------------------------|--------------------|--------------------------------------------------------------------------------------------------------------------------| +| `kb_sol_raw_rpc_transactions` | `0.2.3` historique | Ancienne table validée puis remplacée pendant `0.3.1`; absente de la baseline SQL active. | +| `kb_sol_raw_transactions` | `0.3.1` | Stocker une transaction Solana canonique unique par signature, indépendante de la source. | +| `kb_sol_raw_ws_notifications` | `0.2.3` historique | Ancienne table supprimée pendant `0.3.1` après conversion des métadonnées utiles; absente de la baseline active. | +| `kb_sol_obs_transaction_observations` | `0.3.1` | Stocker source, protocole, méthode, timestamps, taille, latences et statuts sans dupliquer le payload transactionnel. | +| `kb_sol_core_transactions` | `0.2.4` | Ligne transaction normalisée liée à une signature. | +| `kb_sol_core_account_keys` | `0.2.4` | Comptes résolus, y compris loaded addresses. | +| `kb_sol_core_instructions` | `0.2.4` | Instructions top-level normalisées. | +| `kb_sol_core_inner_instructions` | `0.2.4` | Instructions internes normalisées. | +| `kb_sol_core_logs` | `0.2.4` | Logs de transaction et ordre d'apparition. | +| `kb_sol_core_balance_changes` | `0.2.4` | Deltas SOL/SPL dérivés du metadata RPC. | +| `kb_sol_obs_program_observations` | `0.3.4+` | Observations par programme invoqué, après stabilisation de l'ingestion et de l'extraction. | +| `kb_sol_obs_instruction_observations` | `0.3.4+` | Observations par instruction, discriminator et surface candidate, après stabilisation de l'ingestion et de l'extraction. | +| `kb_sol_decode_decoded_events` | `0.4.x+` | Événements décodés par surface et version de décodeur. | +| `kb_sol_mat_trade_events` | `0.5.x+` | Trades matérialisés. | +| `kb_sol_catalog_tokens` | `0.5.x+` | Catalogue de tokens observés. | +| `kb_sol_catalog_pools` | `0.5.x+` | Catalogue de pools observés. | +| `kb_sol_catalog_pairs` | `0.5.x+` | Catalogue de paires tradables. | +| `kb_sol_ops_processing_ledger` | `0.3.4+` | Ledger de traitements par module, version, input et statut, après stabilisation de l'ingestion. | + +## Règles SQL générales + +- `id` est la clé primaire technique, sauf exception explicitement documentée. +- `created_at` est le timestamp d'insertion. +- `updated_at` existe seulement si la table est mutable. +- `slot` est un `BIGINT` côté SQL ; les conversions Rust doivent être explicites. +- `signature` est un texte non vide quand applicable. +- `program_id` est un texte non vide quand applicable. +- `canonical_json` est un `JSONB` réservé à la représentation canonique source-indépendante de la transaction. +- `payload_json` est un `JSONB` réservé au payload décodé, enrichi ou matérialisé. +- Les index minimaux sont ajoutés selon l'usage : `signature`, `slot`, `program_id`, `created_at`. +- Les noms d'index utilisent un préfixe fonctionnel : `ux_` pour les index uniques et `ix_` pour les index non uniques, par exemple `ux_kb_sol_raw_transactions_signature` ou `ix_kb_sol_obs_transaction_observations_signature`. +- Les contraintes de clés nommées utilisent un préfixe fonctionnel : `pk_` pour les clés primaires et `fk_` pour les clés étrangères. Les contraintes de validation peuvent utiliser `ck_`. +- Les contraintes métier doivent rester strictes mais réversibles tant que les corpus ne sont pas stabilisés. + +## Principe raw et cycle de vie + +La couche `raw` conserve une seule représentation canonique de chaque transaction Solana. Elle ne conserve pas une copie complète par transport ou fournisseur. + +Les adaptateurs `kb_rpc` convertissent JSON-RPC, Helius WebSocket ou Yellowstone Protobuf vers le même contrat `kb_model`. Le payload canonique reste la source rejouable pour l’extraction `core`, les décodeurs et les matérialisateurs. + +La couche `obs` conserve les acquisitions successives : fournisseur, endpoint, protocole, méthode, commitment, timestamps, taille et statut. `kb_sol_obs_transaction_observations` ne contient pas de `raw_json` complet. + +Les états de rétention s’appliquent à la transaction canonique. Les observations techniques ont une politique de conservation séparée, car elles sont petites et utiles aux mesures de couverture et de latence. + +Le contrat détaillé est décrit dans `docs/TRANSACTION_ACQUISITION_MODEL.md`. Le cycle de vie est décrit dans `docs/RAW_STORAGE_LIFECYCLE.md`. Le découpage core destiné aux décodeurs est décrit dans `docs/CORE_EXTRACTION_CONTRACTS.md`. + +## Replay partiel + +Le replay ne doit pas être limité à la transaction complète. Après extraction `core`, l'unité de scheduling principale devient l'instruction normalisée. Cela permet de traiter uniquement les instructions `Pending`, `Failed` ou `ReplayRequested`, sans rejouer toute une transaction déjà extraite. + +Le décodage ne doit cependant pas être limité au payload de l'instruction. Les décodeurs doivent recevoir une entrée contextualisée : instruction ciblée, comptes résolus, inner instructions, logs, deltas de balance et statut de transaction. + +Les contrats de replay par instruction sont décrits dans `docs/INSTRUCTION_REPLAY_CONTRACTS.md`. Les contrats d'extraction core sont décrits dans `docs/CORE_EXTRACTION_CONTRACTS.md`. + +Chaque table dérivée doit pouvoir être reconstruite par au moins un des axes suivants quand l'information existe : + +- module et version ; +- signature ; +- plage de slots ; +- `program_id` ; +- surface candidate ; +- discriminator ; +- état dans `kb_sol_ops_processing_ledger`. + +## Frontière Rust store + +`kb_store_core` définit les contrats indépendants du backend : DTOs, entities, health, pagination, erreurs et traits repository. + +`kb_store_pg` implémente PostgreSQL : pool, migrations, requêtes SQL, repositories concrets et diagnostics du schéma courant. + +Les structures Rust ne doivent pas être placées dans `queries/`. Les requêtes SQL, les binds et l'exécution SQL restent dans `queries/`; les types proches des lignes SQL restent dans `entities/`; les contrats applicatifs restent dans `dtos/`; les APIs de stockage restent dans `repositories/`. + +## Statut `0.2.0` + +`0.2.0` est un jalon de cadrage. Il ne doit pas introduire un grand schéma SQL. Les migrations présentes avant cette convention doivent être neutralisées ou remplacées avant d'être exécutées sur une base réelle. + +## Ajout `0.3.4` — ledger de traitement + +La table `kb_sol_ops_processing_ledger` devient la source de vérité de l’idempotence par processor. Pour l’extraction core, elle mémorise la signature, le hash canonique, la version, le statut, le nombre de tentatives et le dernier diagnostic. + +L’écriture du succès ledger, le passage raw à `core_extracted` et le graphe core sont commités ensemble. Un statut `failed` reste rejouable et ne doit jamais être interprété comme une transaction canonique invalide de façon définitive. diff --git a/migration/khadhroony-bot2-reference/docs/DECODER_MATERIALIZATION_CONTRACTS.md b/migration/khadhroony-bot2-reference/docs/DECODER_MATERIALIZATION_CONTRACTS.md new file mode 100644 index 0000000..a50fc64 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/DECODER_MATERIALIZATION_CONTRACTS.md @@ -0,0 +1,179 @@ + + + +# Contrats communs de décodage et de matérialisation + +## Objet + +La version `0.4.0` introduit l’infrastructure backend-agnostique utilisée par les futurs décodeurs Solana Core, SPL et protocolaires. Elle ne cherche pas encore à décoder maximalement un protocole précis. + +Le flux commun est : + +```text +core instruction contextualisée + -> dispatch déterministe + -> observation décodée versionnée + -> persistance atomique decode + couverture + ledger + -> matérialisation optionnelle explicitement autorisée + -> persistance atomique mat + ledger +``` + +## Input contextualisé + +`CoreInstructionReplayInput` reste l’unique contrat d’entrée commun. Son contrat passe à la version `2` dans `0.4.1-pre.014`. Il contient la signature, le slot, le statut et l’erreur on-chain, le chemin stable de l’instruction, le program ID, les comptes résolus dans leur ordre original, le payload brut déterministe et son hash, toutes les instructions outer ordonnées par index numérique, les inner instructions descendantes, les logs reliés prudemment, les changements de balances et la version du contrat core. + +`outer_instructions_json` est un tableau stable dont chaque entrée contient `instructionIndex`, `instructionPath`, `programId`, `payloadJson` et `payloadHash`. L’instruction cible est incluse. Cette projection provient des tables core existantes et ne nécessite aucune migration SQL. + +Le hash de replay est calculé à partir de la sérialisation JSON canonique de cet input. Le skip exige la même étape, le même processor, la même version, la même clé d’entrée, le même hash et un statut ledger réussi. + +Le passage au contrat `2` modifie légitimement les hashes existants, car les payloads outer participent désormais à la sérialisation. Le pipeline reste en version `1` : la version du contrat core et le nouveau contexte suffisent à invalider les anciens skips. Un force replay est requis après mise à niveau. + +## Contrat de décodeur + +`InstructionDecoder` expose : + +- une identité stable `name/version` ; +- les programmes et surfaces supportés ; +- une matrice de couverture déclarée ; +- une reconnaissance déterministe ; +- un résultat terminal `decoded`, `ignored`, `unsupported` ou `failed` ; +- zéro ou plusieurs observations typées uniquement pour `decoded` ; +- une preuve, une confiance et des diagnostics structurés. + +Le dispatch est ordonné par compatibilité exacte, priorité déclarée, surface, code d’entrée et identité stable. Il ne dépend ni du nom de la crate ni de l’ordre d’enregistrement implicite. + +Avant la lecture du store, le pipeline calcule le périmètre effectif des programmes à partir de l’union des `program_id` déclarés par les décodeurs activés. Lorsque l’opérateur fournit un filtre explicite, ce filtre doit être un sous-ensemble exact de ces programmes ; une valeur incompatible est refusée avant toute sélection SQL. Cette règle empêche un décodeur natif de consommer par défaut des instructions SPL ou protocolaires et évite de rejouer indéfiniment le même lot `unmatched` sans rapport avec les processors choisis. + +Une instruction inconnue d’un programme reconnu doit être classée sans produire de faux événement. Un `unmatched` reste possible lorsque le program ID est supporté mais que la reconnaissance contextuelle refuse l’input ; il ne modifie pas globalement le lifecycle de l’instruction pour ne pas empêcher un autre décodeur futur de la traiter. + +## Transactions on-chain échouées + +Une transaction échouée reste décodable lorsqu’un input structurel existe. Toute observation conserve : + +```text +transaction_failed +transaction_error +observation_committed +``` + +`observation_committed` doit être faux lorsque la transaction a été annulée. Ces observations peuvent décrire une instruction tentée, un événement loggé avant l’erreur, une classification d’échec, une consommation de compute ou une surface appelée. + +Elles ne peuvent pas produire automatiquement un trade réussi, une modification de liquidité réussie, un changement confirmé de catalogue ou une candle normale. `EventMaterializer` applique une politique explicite par famille. Les familles `trade`, `liquidity` et `lifecycle` sont refusées avant l’appel au matérialiseur lorsque la source est échouée ou non commitée. Une seconde barrière valide ensuite les familles de sortie et n’autorise, dans ce contexte, que les matérialisations d’audit ou de risque. + +## Persistance + +Les tables actives sont : + +- `kb_sol_decode_events` ; +- `kb_sol_decode_coverage_declarations` ; +- `kb_sol_decode_coverage_observations` ; +- `kb_sol_mat_events` ; +- `kb_sol_ops_processing_ledger`. + +Aucun schéma PostgreSQL applicatif explicite n’est créé. Les payloads et preuves JSONB utilisent des colonnes suffixées par `_jsonb`. + +Le décodage persiste dans une transaction unique les observations, la couverture, l’état lifecycle de l’instruction et le ledger. La matérialisation persiste également ses sorties et son ledger dans une transaction unique. Un rollback à la dernière étape doit donc supprimer toutes les écritures précédentes de la même tentative. + +Toute exécution non skippée remplace atomiquement les sorties du même processor, de la même version et de la même clé d’entrée. Le force replay contourne uniquement le skip version/hash ; il ne supprime jamais les sorties d’un autre processor, d’une autre version ou d’une autre clé. Les versions antérieures restent disponibles pour l’audit. + +Lorsqu’un force replay contient une liste explicite de signatures, le pipeline retire le filtre de lifecycle pour cette sélection bornée. Le même contournement est autorisé sans signatures uniquement lorsque l’opérateur active explicitement le mode « toutes les signatures » ; la sélection reste alors limitée par les programmes compatibles, les instruction paths, les slots éventuels et la limite. Sans signatures ni cette autorisation explicite, la requête est refusée. + +Les déclarations de couverture sont synchronisées comme un snapshot du couple processor/version. Le résultat de persistance distingue les lignes réellement insérées, modifiées, supprimées du snapshot ou strictement inchangées ; une déclaration identique est comptée comme `skipped`, pas comme une nouvelle insertion. + +## Couverture + +La couverture compare les entrées déclarées et observées, puis agrège : + +- reconnaissance ; +- décodage ; +- matérialisation ; +- erreurs ; +- inconnues ; +- transactions réussies ; +- transactions échouées. + +Une surface ne peut pas être considérée comme clôturée tant que toutes les instructions, événements et discriminants connus, y compris historiques et non liés au trading, ne sont pas classifiés. + +## Traçabilité structurée + +Chaque campagne reçoit un `campaign_id` process-local stable, propagé de la démo jusqu’aux décisions de dispatch, de ledger, de décodage, de matérialisation et de persistance. Des spans imbriqués de campagne, input et processor transmettent aussi ce contexte aux événements émis par les décodeurs et les stores sans modifier leurs contrats. Elle doit permettre de reconstruire précisément les décisions prises sans requête SQL libre. Les événements `debug` utilisent un champ `action` stable et conservent au minimum, selon l’étape : + +- le nombre de signatures, un échantillon borné et la sélection normalisée ; +- les filtres demandés puis les `program_id` et états effectivement appliqués ; +- la signature, le slot, l’instruction path, le program ID et la clé d’input ; +- le processor et sa version ; +- le hash déterministe pertinent ; +- la décision de dispatch, y compris lorsque `recognize` n’est pas appelé à cause d’un program ID incompatible ; +- le résultat de reconnaissance et de décodage ; +- le contrôle du ledger, le motif exact du skip ou son contournement par force replay ; +- le début et la validation de la persistance PostgreSQL ; +- le statut terminal et les compteurs par processor. + +Les targets stables sont : + +```text +kb_app_demo.demo_decode_replay +kb_app_demo.frontend.demo_decode_replay +kb_pipeline.decode_replay +kb_store_pg.decode_pipeline +kb_decoder_solana_core +``` + +Les signatures, slots, paths et program IDs sont des identifiants publics on-chain. Les listes complètes de signatures ne sont pas répétées à chaque couche : les événements de campagne conservent un compteur et un petit échantillon, tandis que les événements par input conservent la signature exacte. Les traces ne doivent pas enregistrer de DSN, secret, clé privée ni payload brut complet. Le payload d’instruction est représenté par son hash déterministe. + +## Orchestration + +`kb_pipeline::execute_decode_replay` fournit : + +- sélection bornée par signatures, états, slots, program IDs et instruction paths ; +- zéro, un ou plusieurs décodeurs compatibles selon la politique choisie ; +- concurrence bornée ; +- arrêt coopératif ; +- skip version/hash ; +- force replay ; +- résumé par processor et statut ; +- matérialisation optionnelle après succès de la persistance decode. + +La fenêtre Tauri `demo_decode_replay` ne contient aucune logique SQL libre. Elle construit une requête typée, appelle le pipeline et affiche les diagnostics read-only. + +## Projections natives actives + +`kb_materializer_lifecycle::LifecycleMaterializer` est enregistré dans `demo_decode_replay` à partir de `0.4.1-pre.003`. Son applicabilité est filtrée par surface, entrée et paramètres avant toute consultation du ledger ou application de politique. `pre.017` étend ses familles acceptées à `Lifecycle`, `Admin` et `Audit`, mais uniquement pour des observations exactes qui produisent réellement une mutation lifecycle. `pre.018` ajoute l’initialisation/la fermeture des rapports Slashing sous `slashing_violation_report`, sans transformer le rapport en pénalité de stake. Il applique toujours `SuccessfulCommittedOnly`. + +Les sorties stables actives sont : + +- `address_lookup_table::0` pour les cinq mutations ALT ; +- `program_loader:::0` pour les mutations loader stables ; +- `feature_gate:revoke_pending_activation:0` pour la révocation Feature Gate ; +- `durable_nonce_account::0` pour initialize, advance, authorize, upgrade et withdraw du System Program ; +- `zk_proof_context::0` pour l’initialisation d’un contexte ZK ElGamal demandée par une vérification réussie et pour sa fermeture. + +`authorize_nonce_account` reste décodé en famille `Admin`, mais sa mutation d’autorité durable est promue en sortie `Lifecycle`. Les vérifications ZK ElGamal restent des événements `Audit`; elles ne sont acceptées par le matérialiseur que lorsque `contextStateRequested = true`. Une preuve sans contexte n’est jamais proposée au matérialiseur. + +Le retrait d’un nonce account conserve une sémantique conditionnelle. Le runtime détruit le compte lorsque la totalité du solde est retirée ; l’instruction seule ne suffit pas à reconstruire de manière certaine l’état final transactionnel du compte. La projection décrit donc l’opération commitée, conserve le montant demandé et indique explicitement que l’état final n’est pas capturé. Les deltas SOL ne sont pas dupliqués. + +La création d’un contexte ZK conserve le compte cible, son autorité et le type de preuve, sans matérialiser les octets de preuve. La fermeture conserve le compte, la destination des lamports et l’autorité, puis décrit le reset vers le System Program. L’ancien ZK Token Proof Program n’est jamais matérialisé : son runtime actuel est un stub sans effet et sa sémantique historique n’est pas attribuable à une transaction sans preuve de version. + +Le hash d’entrée matérialiseur reste dérivé de l’observation décodée complète. Le ledger conserve séparément les processors `solana_native_lifecycle`, `solana_native_admin` et `solana_native_compliance_audit`, leur version, la clé d’input et le hash. Une transaction échouée ou une observation non commitée est refusée avant toute sortie mutable. + +`pre.020` répartit les responsabilités : create/allocate System restent dans lifecycle ; assignations System, Config `store` et changements d’autorité Loader appartiennent à `kb_materializer_admin` ; writes/copies de bytecode Loader appartiennent à `kb_materializer_compliance_audit`. Les transferts SOL ne sont pas rematérialisés, car les balance changes core en sont la source canonique. + +## Projections natives restantes + +La couverture maximale d’un décodeur n’implique pas une matérialisation systématique. Une projection n’est ajoutée que lorsqu’elle possède une identité stable, un état cible explicite, une politique d’idempotence et suffisamment de contexte pour ne pas reconstruire une mutation fictive. + +Les prochaines projections sont réparties par propriétaire : + +- `kb_materializer_lifecycle` : créations et allocations System, sans dupliquer les deltas SOL ; +- `kb_materializer_admin` : assignations System, écritures Config opaques commitées et changements d’autorité Loader ; +- `kb_materializer_compliance_audit` : écritures et copies de bytecode Loader, avec hash et préfixe borné sans payload complet ; +- `kb_materializer_staking` : intentions/transitions Stake et Vote commitées. `pre.021` active des projections instructionnelles pour comptes Stake/Vote, autorités, lockup, vote state, retraits et rewards ; un snapshot final exige toujours l’état antérieur/suivant du compte, les crédits cumulés et les sysvars ; +- un matérialiseur transactionnel Compute Budget : profil fusionnant toutes les instructions Compute Budget du message. + +Les surfaces suivantes restent volontairement en decode/audit : + +- précompiles de signature, qui décrivent une vérification runtime sans état métier durable ; +- preuves ZK sans compte de contexte, qui n’ont pas de mutation persistante à projeter ; +- ancien ZK Token Proof Program, dont le runtime Agave `v4.1.1` est un stub sans effet. + +Chaque ajout futur doit indiquer le matérialiseur propriétaire, la source d’état et la politique de transaction, au lieu d’étendre automatiquement `solana_native_lifecycle`. diff --git a/migration/khadhroony-bot2-reference/docs/DECODER_SURFACE_AUDIT.md b/migration/khadhroony-bot2-reference/docs/DECODER_SURFACE_AUDIT.md new file mode 100644 index 0000000..f59f31b --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/DECODER_SURFACE_AUDIT.md @@ -0,0 +1,49 @@ + + + +# Audit des surfaces de décodage + +Ce document suit les noms de crates de décodeurs réservés, les aliases probables et les surfaces à consolider. Il s'appuie sur les IDL locales du dossier `idls/` et sur les matrices de conception historiques importées dans l'archive de travail. + +## Règle de décision + +Un nom de décodeur devient canonique seulement si au moins un des critères suivants est vérifié : + +- une IDL locale existe avec un `program_id` explicite ; +- le `program_id` est prouvé par corpus local ; +- le rôle de la surface est stable : DEX effectif, router, orderbook, launchpad, vault, lending, staking, etc. ; +- le nom respecte la nomenclature `_` sans alias marketing ambigu. + +Un nom historique sans `program_id` local reste en statut `alias_candidate` ou `watchlist`. Il ne doit pas recevoir de logique métier avant consolidation. + +## Aliases et doublons à traiter + +| Groupe | Canonique recommandé | Crates ou noms concurrents | Statut | Décision provisoire | +|------------------------|-----------------------------------------------------|-------------------------------------------------------|-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| OKX Labs | `kb_decoder_okx_lab_v1` | `kb_decoder_okx_v1`, `kb_decoder_onchain_labs_dex_v1` | `alias_candidate` | L'IDL locale existe pour `okx_lab_v1` avec le programme `6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma`. Les noms `okx_v1` et `onchain_labs_dex_v1` ne doivent pas être implémentés séparément sans preuve contraire. | +| OKX Router | `kb_decoder_okx_dex_router` | `kb_decoder_okx_v2`, `kb_decoder_onchain_labs_dex_v2` | `alias_candidate` | L'IDL locale existe pour `okx_dex_router` avec le programme `proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u`. Les noms `okx_v2` et `onchain_labs_dex_v2` restent des noms historiques à vérifier. | +| GooseFX | `kb_decoder_goosefx_v2`, `kb_decoder_goosefx_gamma` | `kb_decoder_goosefx_v1` | `watchlist` | `goosefx_v2` et `goosefx_gamma` ont des IDL locales et des programmes distincts. `goosefx_v1` reste une entrée historique sans IDL locale dans l'archive courante. | +| Fusion AMM | `kb_decoder_fusion_amm` | `kb_decoder_fusionamm` | `alias_candidate` | L'IDL locale se nomme `fusion_amm`, mais son metadata interne peut utiliser `fusionamm`. Le crate canonique doit rester lisible : `fusion_amm`. | +| PancakeSwap | `kb_decoder_pancakeswap` | `kb_decoder_pancake_swap` | `alias_candidate` | L'IDL locale se nomme `pancakeswap`. Le crate avec underscore supplémentaire doit rester non canonique. | +| Orca Wavebreak | `kb_decoder_orca_wavebreak` | `kb_decoder_wavebreak` | `alias_candidate` | L'IDL locale se nomme `orca_wavebreak`. Le nom sans protocole est trop ambigu. | +| Jupiter Limit Order v2 | `kb_decoder_jupiter_limit_order_v2` | `kb_decoder_jupiter_limit_order_2` | `alias_candidate` | L'IDL locale utilise `jupiter_limit_order_v2`. Le suffixe `_2` ne doit pas être utilisé. | +| dFlow | `kb_decoder_dflow_v4` | `kb_decoder_dflow_aggregator_v4` | `alias_candidate` | L'IDL locale se nomme `dflow_v4`. Le nom `dflow_aggregator_v4` peut rester comme alias historique mais ne doit pas porter une implémentation distincte sans `program_id` différent. | +| Meteora DAMM v1 | `kb_decoder_meteora_damm_v1` | `kb_decoder_meteora_pools_amm` | `alias_candidate` | Les matrices historiques indiquent que `meteora_pools` peut être un alias de DAMM v1. La consolidation doit être décidée par `program_id`, corpus et naming final. | +| Raydium LaunchLab | `kb_decoder_raydium_launchlab` | `kb_decoder_raydium_launchpad` | `naming_mismatch` | L'IDL locale se nomme `raydium_launchlab`. L'ancien nom `launchpad` décrit la famille métier, mais pas forcément la surface canonique. | +| Raydium Lock | `kb_decoder_raydium_lock` | `kb_decoder_raydium_liquidity_locking` | `naming_mismatch` | L'IDL locale se nomme `raydium_lock`. Le nom `liquidity_locking` est descriptif. La surface canonique doit être confirmée avant implémentation. | +| Raydium Pool v4 | `kb_decoder_raydium_amm_v4` | `kb_decoder_raydium_pool_v4` | `audit_only` | La note historique indique que `raydium_pool_v4` ne doit pas être promu comme décodeur autonome sans `program_id` distinct et corpus local. | + +## Actions recommandées + +- Ne pas supprimer immédiatement les crates aliases tant que le squelette sert de matrice de réservation. +- Ne pas implémenter deux décodeurs pour le même `program_id`. +- Ajouter dans chaque décodeur un mapping explicite `surface_code`, `program_code` et `program_id` dès que le corpus commence. +- Déplacer les aliases historiques vers une table de catalogue ou un document de watchlist quand le support réel commence. +- Supprimer ou fusionner les crates aliases seulement dans un delta dédié, avec validation `cargo build` et manifeste de suppression. + +## Surfaces à ajouter ou renommer plus tard + +- Ajouter `kb_decoder_raydium_launchlab` si la décision est de refléter strictement le nom de l'IDL locale. +- Vérifier si `kb_decoder_raydium_launchpad` doit devenir un alias documentaire ou rester une surface métier. +- Vérifier si `kb_decoder_meteora_pools_amm` doit être fusionné dans `kb_decoder_meteora_damm_v1`. +- Vérifier si les crates `okx_v1`, `okx_v2`, `onchain_labs_dex_v1` et `onchain_labs_dex_v2` doivent être supprimés après confirmation des deux programmes OKX canoniques. diff --git a/migration/khadhroony-bot2-reference/docs/DELTA_WORKFLOW.md b/migration/khadhroony-bot2-reference/docs/DELTA_WORKFLOW.md new file mode 100644 index 0000000..776aafc --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/DELTA_WORKFLOW.md @@ -0,0 +1,58 @@ + + + +# Workflow delta + +Après le squelette initial, les livraisons doivent être faites sous forme de zip delta. + +## Nommage des zips + +Un delta qui touche la racine du workspace ou plusieurs modules doit utiliser : + +```text +khadhroony-bot2_vX.Y.Z-pre.abc.zip +``` + +Un delta qui ne touche qu'un seul module Rust doit utiliser : + +```text +kb_modulename_vX.Y.Z-pre.abc.zip +``` + +Exemples : + +```text +khadhroony-bot2_v0.1.0-pre.001.zip +kb_logging_v0.1.0-pre.002.zip +``` + +## Fichier `delta.md` + +Chaque zip delta doit contenir un fichier `delta.md` non versionné à la racine du zip. + +`delta.md` doit indiquer : + +- les fichiers ajoutés ; +- les fichiers modifiés ; +- les fichiers à supprimer manuellement ; +- les validations exécutées ; +- les validations non exécutées ; +- les remarques de compatibilité ou d'application du delta. + +## Contenu d'un delta + +Un delta contient uniquement : + +- les fichiers ajoutés ; +- les fichiers modifiés ; +- `delta.md`. + +Il ne doit pas contenir de fichiers inchangés. + +## Règles + +- Ne pas renvoyer un zip complet sauf demande explicite. +- Ne pas modifier `CHANGELOG.md` avant validation d'une version. +- Placer les évolutions prévues dans `ROADMAP.md`. +- Garder `README.md` descriptif. +- Documenter les suppressions dans `delta.md`, car un zip ne supprime pas les anciens fichiers à l'extraction. diff --git a/migration/khadhroony-bot2-reference/docs/DEVELOPMENT_ORDER.md b/migration/khadhroony-bot2-reference/docs/DEVELOPMENT_ORDER.md new file mode 100644 index 0000000..c93464d --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/DEVELOPMENT_ORDER.md @@ -0,0 +1,91 @@ + + + +# Ordre de développement + +## Principe + +La priorité est de rendre chaque transaction exploitable avant de payer un flux temps réel enrichi. Le développement suit donc l’ordre : acquisition gratuite, transaction canonique, extraction core, décodeurs, matérialisations, puis sources live payantes. + +## Ordre cible après `0.2.x` + +- [x] `0.1.x` : logging, configuration, clients HTTP/WS standard, rôles et démos RPC. +- [x] `0.2.0` à `0.2.5` : conventions PostgreSQL, stores raw/core historiques et diagnostics SQL. +- [x] `0.3.0` : cadrage Agave local historique, ensuite abandonné comme axe actif après mesures réelles. +- [x] `0.3.1` : transaction canonique, observations légères, validation PostgreSQL réelle et consolidation de la baseline SQL. +- [x] `0.3.2` : modèle canonique et adaptateur HTTP `getTransaction`. +- [x] `0.3.3` : backfill gratuit par signatures, programme, token et pool, avec parcours avant/après et arrêt coopératif borné. +- [~] `0.3.4` : extraction core, navigateur de candidats, intégrité et replay validés ; derniers tests automatisés de rollback/annulation à exécuter. +- [ ] `0.4.0` : infrastructure commune de décodage et de matérialisation. +- [ ] `0.4.1+` : décodeurs et matérialisations Solana Core/SPL. +- [ ] `0.5.x` : Pump. +- [ ] `0.6.x` : Meteora. +- [ ] `0.7.x` : Raydium. +- [ ] `0.8.x` : Orca. +- [ ] `0.9.x` : Jupiter, routeurs et surfaces complémentaires. +- [ ] `0.10.x` : Helius `transactionSubscribe`, Yellowstone gRPC, fallback standard et comparaison fournisseur. +- [ ] `0.11.x` : wallet, sécurité et démos devnet. +- [ ] `0.12.x` : listeners métier et trading assisté. + +## Ordre technique de stockage + +1. Maintenir une baseline SQL courante ne contenant que les tables actives. +2. Normaliser chaque source vers un contrat canonique dans `kb_model`. +3. Écrire une transaction unique dans `kb_sol_raw_transactions`. +4. Écrire une observation légère dans `kb_sol_obs_transaction_observations`. +5. Extraire les tables `kb_sol_core_*` depuis la transaction canonique. +6. Ajouter les observations programme/instruction nécessaires aux décodeurs. +7. Ajouter les tables `kb_sol_decode_*` et `kb_sol_mat_*` par surface. +8. Ajouter les sources live payantes seulement lorsque les décodeurs peuvent exploiter immédiatement le flux. + +## Séquence par surface + +```text +program ids + -> corpus de signatures + -> backfill gratuit + -> decodeur maximal + -> événements stables + -> matérialisation complète + -> replay et anti-régression + -> listener live +``` + +## Démos + +`kb_app_demo` reste le shell unique pour : + +- diagnostics DB ; +- backfills ; +- inspection canonique/core ; +- couverture des décodeurs ; +- comparaison future des sources live ; +- wallet et sécurité. + +## Contraintes permanentes + +- Ne pas créer de schémas PostgreSQL applicatifs explicites. +- Utiliser le format `kb_sol__`. +- Ne pas placer de structures Rust dans `queries/`. +- Ne pas faire dépendre les décodeurs du store concret. +- Ne pas faire dépendre les décodeurs du fournisseur d’acquisition. +- Intégrer Helius et Yellowstone dans `kb_rpc`, sans crate provider séparée. +- Ne pas modifier `CHANGELOG.md` avant validation locale du jalon. + +## État de clôture `0.3.4-pre.011` + +L’ordre technique est concrétisé jusqu’à l’écriture core et aux outils de replay : + +```text +backfill HTTP +-> transaction canonique +-> sélection raw bornée +-> extraction core pure +-> commit PostgreSQL atomique +-> ledger version/hash +-> sélection et export de corpus +``` + +Les validations réelles couvrent 70 transactions core, l’intégrité SQL, l’écriture CSV, le skip et le force replay sur 14 signatures. `pre.011` automatise le dernier contrôle de rollback PostgreSQL et rend les attentes réseau/retry annulables. + +L’ancien jalon `0.3.5` est supprimé. Après validation de `pre.011` et mise à jour du changelog, la prochaine version active est directement `0.4.0`. diff --git a/migration/khadhroony-bot2-reference/docs/DEVNET_EXECUTION.md b/migration/khadhroony-bot2-reference/docs/DEVNET_EXECUTION.md new file mode 100644 index 0000000..ad48772 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/DEVNET_EXECUTION.md @@ -0,0 +1,119 @@ + + + +# Validation d’exécution Devnet + +Ce document décrit le premier parcours réseau réel de la couche d’exécution native et sa fenêtre Tauri Devnet. Le test backend reste opt-in et aucune activation Mainnet n’est autorisée. + +## Périmètre + +Le parcours exécute une instruction System transfer depuis le wallet temporaire persistant du profil `local_devnet` vers un destinataire éphémère créé uniquement pour le test. + +```text +wallet persistant + -> vérification du genesis hash Devnet + -> contrôle du destinataire et du minimum rent-exempt + -> lecture du solde + -> airdrop plafonné si nécessaire + -> plan System transfer + -> blockhash et estimation des frais + -> simulation exacte + -> signature locale + -> envoi avec preflight + -> confirmation bornée + -> getTransaction + -> insertion canonique + -> extraction core + -> decode replay System +``` + +Les clés privées restent dans `kb_wallet`. Elles ne sont ni sérialisées dans les résumés, ni envoyées à une WebView, ni écrites dans PostgreSQL ou les logs. + +## Préconditions + +- le profil `local_devnet` doit rester configuré sur le genesis hash Devnet officiel ; +- `temporary_wallet_enabled`, `temporary_wallet_persist` et `devnet_send_enabled` doivent être actifs ; +- la simulation et la confirmation opérateur doivent rester obligatoires ; +- PostgreSQL doit être disponible pour la validation canonique/core/decode ; +- le rôle `http_queries` doit accepter `getTransaction` et les lectures d’exécution ; +- le rôle `http_transactions` doit accepter simulation, envoi et lecture des statuts. + +Le wallet persistant par défaut est créé sous : + +```text +wallets/temporary/local_devnet/local-devnet-operator.json +``` + +Ce fichier n’est pas chiffré. Il est exclusivement destiné à Localnet/Devnet et ne doit jamais recevoir de fonds réels. + +## Exécution du test réel + +Depuis la racine du workspace : + +```bash +KB_DEVNET_EXECUTION_TEST=1 \ +KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \ +KB_DEVNET_WALLET_DIR='./wallets/temporary/local_devnet' \ +KB_DEVNET_AIRDROP_LAMPORTS=0 \ +KB_DEVNET_TRANSFER_LAMPORTS=1000000 \ +cargo test -p kb_pipeline optional_devnet_system_transfer_from_env -- --nocapture +``` + +Variables : + +- `KB_DEVNET_EXECUTION_TEST=1` autorise explicitement le test réseau ; +- `KB_POSTGRES_TEST_URL` sélectionne la base de validation ; +- `KB_DEVNET_WALLET_DIR` peut isoler le wallet utilisé par le test ; +- `KB_DEVNET_AIRDROP_LAMPORTS` fixe le montant maximal demandé au faucet lorsque le solde est insuffisant ; +- `KB_DEVNET_TRANSFER_LAMPORTS` fixe le transfert System réel. + +Les valeurs restent soumises aux plafonds de `ExecutionConfig`. Une valeur d’airdrop supérieure à `devnet_airdrop_max_lamports` ou un transfert supérieur à `devnet_max_spend_lamports` est refusé avant tout appel mutable. + +## Résultat attendu + +Pour un destinataire inexistant, le pipeline appelle `getAccountInfo`, puis `getMinimumBalanceForRentExemption` avec une longueur de données nulle. Le transfert est refusé avant simulation lorsqu’il est inférieur au minimum retourné. Lorsqu’une simulation échoue malgré ces contrôles, l’erreur remontée conserve l’erreur runtime structurée et les vingt premières lignes de logs. + +Le test doit confirmer : + +- cluster classifié `Devnet` ; +- simulation exacte réussie ; +- signature locale vérifiée ; +- signature RPC identique à la signature calculée ; +- statut `confirmed` ou `finalized` ; +- transaction canonique présente ou déjà existante ; +- extraction core terminée ou déjà courante ; +- decode replay System terminé sans input unmatched ou failed. + +L’airdrop peut être absent lorsque le wallet possède déjà un solde suffisant. Le destinataire reste éphémère et sa clé privée n’est pas persistée. Après signature, tout échec ultérieur conserve la signature dans le résumé avec un diagnostic de l’étape concernée. + +## Fenêtre Tauri + +`demo_execution_solana_core` est accessible depuis la fenêtre principale. Elle expose uniquement les profils Devnet utilisant un wallet temporaire persistant. Elle permet de générer une adresse destinataire jetable, de simuler exactement le message ou de signer/envoyer après confirmation opérateur. Le plan, les comptes, les signataires, la simulation, la confirmation et les résultats canonical/core/decode sont affichés en lecture seule. La clé privée du wallet source reste exclusivement dans le backend Rust. + +Un airdrop demandé par la fenêtre reste soumis au plafond du profil et peut échouer lorsque le faucet public est limité. Un wallet déjà financé peut être utilisé avec une valeur d’airdrop nulle. + +## Limites + +- le faucet Devnet peut appliquer des limites temporaires ; +- `getTransaction` peut devenir disponible après la confirmation de statut, d’où les retries bornés ; +- le parcours utilise actuellement une transaction legacy avec recent blockhash ; +- durable nonce, ALT et transactions v0 seront validés dans des tranches dédiées ; +- aucune opération Mainnet n’est autorisée par ce test. + +## Validation de clôture `0.4.2` + +Le 13 juillet 2026, la commande complète a été exécutée avec : + +```bash +KB_DEVNET_EXECUTION_TEST=1 \ +KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \ +KB_DEVNET_WALLET_DIR='./wallets/temporary/local_devnet' \ +KB_DEVNET_AIRDROP_LAMPORTS=0 \ +KB_DEVNET_TRANSFER_LAMPORTS=1000000 \ +cargo test -p kb_pipeline -- --nocapture +``` + +Les 56 tests de `kb_pipeline` ont réussi, y compris `optional_devnet_system_transfer_from_env`. Le wallet était déjà financé, aucun airdrop n’a été demandé et le parcours a confirmé la chaîne complète jusqu’au replay post-exécution. Les 45 tests de `kb_store_pg` ont également réussi avec PostgreSQL réel. + +Cette validation ne généralise pas l’autorisation à toutes les opérations administratives stateful. Elle prouve le parcours d’orchestration commun ; ALT, Config, Feature, Slashing, ZK, Stake, Vote et loaders restent soumis à leurs contrôles spécifiques avant toute future activation mutable. + diff --git a/migration/khadhroony-bot2-reference/docs/EXECUTION_MODEL.md b/migration/khadhroony-bot2-reference/docs/EXECUTION_MODEL.md new file mode 100644 index 0000000..95cc292 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/EXECUTION_MODEL.md @@ -0,0 +1,387 @@ + + + +# Modèle d'exécution + +Ce document décrit la couche d'exécution universelle du workspace. + +## Séparation des responsabilités + +Un décodeur lit une transaction existante. Un exécuteur prépare une opération future. Les deux couches peuvent partager une surface canonique, mais elles ne doivent pas dépendre l'une de l'autre. + +```text +kb_decoder_amm_pump_swap -> observation, décodage, audit +kb_executor_amm_pump_swap -> construction future d'instructions d'achat/vente +``` + +## Bibliothèques universelles + +Le trading est le premier consommateur du workspace, pas la limite de ses crates. Les exécuteurs doivent pouvoir être réutilisés par une CLI, un worker, un outil d'administration, une application de jeu, une application Tauri ou un service autonome. + +Cette universalité implique : + +- un intent typé indépendant de l'interface utilisateur ; +- un plan déterministe sans I/O ; +- des builders couvrant les opérations officiellement appelables, y compris administratives ou dangereuses ; +- des politiques de sécurité séparées du builder ; +- une exposition UI volontairement plus étroite que la capacité de la bibliothèque ; +- aucune dépendance vers les décodeurs pour construire ou signer une transaction. + +Une opération dangereuse ne doit pas être supprimée de l'exécuteur. Elle doit être implémentée, testée, classifiée et soumise à une politique renforcée. `kb_app_demo` peut ne jamais l'exposer. + +## Couches + +- `kb_execution_api` définit les contrats communs des exécuteurs. +- `kb_execution_safety` regroupe les validations avant simulation, signature ou envoi. +- `kb_execution_solana` assemble les plans en messages/transactions Solana et orchestre la signature sans RPC. +- `kb_executor_solana_core` construit les instructions des programmes natifs. +- `kb_wallet` isole les secrets et fournit des signataires. +- `kb_rpc` fournit simulation, envoi et confirmation. +- `kb_pipeline` peut orchestrer la validation post-exécution par réingestion et replay. +- `kb_app_demo` ne fait qu'orchestrer une sélection sûre de ces capacités. + +## Pipeline obligatoire + +```text +intent + -> execution plan + -> policy checks + -> transaction build + -> simulation / dry-run + -> signature + -> send + -> confirmation + -> post-execution decode replay validation +``` + +Construction, signature, envoi et validation ne doivent jamais être fusionnés dans une fonction opaque. + +## Politique de couverture + +`Supported` signifie que le builder et son contrat de comptes/signataires sont implémentés. `Unsupported(reason)` reste acceptable uniquement lorsqu'une surface n'est pas appelable par transaction, a été retirée du runtime, ne dispose pas encore d'une source officielle suffisante ou constitue une dette temporaire explicitement planifiée. + +L'objectif de clôture de `kb_executor_solana_core` est de ne laisser aucune instruction native officiellement appelable oubliée. Les variantes historiques uniquement décodables ne doivent pas être artificiellement réémises. + +## Adaptateurs RPC de simulation + +`0.4.2-pre.004` ajoute dans `kb_rpc` les premières lectures nécessaires avant signature : + +```text +getGenesisHash +getLatestBlockhash +getFeeForMessage +simulateTransaction +``` + +Le genesis hash sert à classifier les clusters publics connus. Le réseau de production est nommé Mainnet dans le contrat public du workspace ; le hash est inchangé et certains endpoints/CLI conservent encore l’alias historique `mainnet-beta`. Un hash inconnu reste explicite et n’est pas automatiquement assimilé à Devnet ou Mainnet. Le blockhash et sa dernière block height valide sont conservés séparément. L’estimation des frais accepte une valeur nulle lorsque le message référence un blockhash expiré. + +La simulation accepte une transaction base64 non signée lorsque `sigVerify = false`; `replaceRecentBlockhash = true` permet au nœud de remplacer le blockhash avant simulation. Les erreurs runtime restent un résultat de simulation typé et ne sont pas confondues avec une erreur HTTP ou JSON-RPC. + +`kb_rpc` ne fabrique pas le contexte de sécurité : cluster attendu, âge du blockhash, nonce account et nonce authority sont fournis explicitement lors de la conversion vers `ExecutionSimulationResult`. Depuis `pre.013`, `getAccountInfo` possède aussi un mode données complètes borné : le mode metadata-only conserve `dataSlice.length = 0`, tandis que `confirmed_with_data(max_data_bytes)` exige un tuple base64 complet dont la longueur décodée égale `space`. + +## Assemblage et signature Solana + +`0.4.2-pre.005` introduit `kb_execution_solana`, frontière commune entre les exécuteurs et les adaptateurs RPC. La crate convertit les `PlannedInstruction` en instructions SDK, compile le message avec son fee payer et sa source de blockhash, puis vérifie que les signataires réellement compilés correspondent exactement au contrat du plan. + +La transaction non signée fournit deux sorties distinctes : + +```text +message base64 -> getFeeForMessage +transaction non signée -> simulateTransaction(sigVerify=false, replaceRecentBlockhash=false) +``` + +Le résultat de simulation est lié au hash exact du message et à la valeur de blockhash ou nonce compilée. Une simulation avec remplacement de blockhash reste utile au diagnostic, mais ne peut pas autoriser la signature du message original. La signature refuse un résultat provenant d’un autre message ou d’une autre valeur nonce, réévalue `kb_execution_safety`, exige la décision `Allow`, puis résout tous les signataires via l’interface Solana `Signer`. Les signataires manquants, supplémentaires ou dupliqués sont refusés. La transaction signée reste en mémoire/backend et n’est ni envoyée ni exposée automatiquement à Tauri. + +La sérialisation transactionnelle utilise le schéma officiel `wincode` fourni par les types Solana et applique la limite réseau de 1 232 octets. Aucun appel direct à `bincode` n’est ajouté. + +### Chemin durable nonce réel — `pre.013` + +Le lifecycle d’un compte nonce et la consommation d’un durable nonce sont deux contrats distincts. Les opérations `initialize`, `advance`, `authorize`, `withdraw` et `upgrade` administrent le compte avec une politique `Latest`. Une transaction métier utilisant un nonce durable suit le chemin stateful suivant : + +```text +getAccountInfo complet et borné à la taille nonce officielle + -> validation owner System Program / non-executable / space exact + -> décodage wincode Versions::Current(State::Initialized) + -> concordance compte et autorité avec ExecutionBlockhashPolicy + -> Message::new_with_nonce + -> AdvanceNonceAccount en instruction 0 + -> valeur nonce comme recent_blockhash du message + -> simulation exacte sans remplacement + -> signature du même hash de message et de la même valeur nonce +``` + +`kb_executor_solana_core` ajoute l’autorité nonce aux signataires requis du plan métier. `kb_execution_solana` refuse les états legacy ou non initialisés, conserve le plan source immuable, expose un plan effectif avec l’avance injectée et exige que le SDK reconnaisse la transaction comme durable nonce. La lecture RPC, l’assemblage, la simulation et la signature restent des étapes séparées. + + +### Builders Address Lookup Table — `pre.015` + +`kb_executor_solana_core` couvre les cinq opérations client de l’interface Address Lookup Table : création, extension, gel, désactivation et fermeture. Le builder stateless garantit le program ID, le payload, l’ordre des comptes et les signataires exacts, mais ne prétend pas connaître l’état courant du compte ni le coût de rent calculé par le runtime. + +```text +intent ALT typé + -> builder officiel exact + -> plan avec autorité/payer explicites + -> lecture RPC stateful du compte et du slot + -> contrôle owner / autorité / état / capacité / cooldown + -> calcul du top-up rent-exempt et simulation exacte + -> politique de dépense + -> signature et envoi +``` + +La création dérive la table depuis l’autorité et un slot récent. L’extension conserve l’ordre des adresses, exige une liste non vide et borne l’input à la capacité maximale officielle ; la capacité restante et le top-up effectif doivent être contrôlés sur le compte réel. Le gel est irréversible. La fermeture n’est autorisée qu’après désactivation et expiration du cooldown lié aux slots. `requested_spend_lamports` reste nul dans le plan ALT parce qu’aucun montant de rent n’est encodé directement dans l’instruction ; l’orchestrateur doit calculer le top-up à partir du compte réel et du minimum rent-exempt, le comparer au plafond de dépense, puis confirmer l’instruction par simulation. + + +### Précompiles de signature — `pre.016` + +Les précompiles Ed25519, secp256k1 et secp256r1 sont des vérifications natives, pas des signatures de transaction. Leur plan ne déclare aucun compte applicatif ni signataire propre ; le fee payer signe uniquement la transaction. Le programme métier qui consomme la preuve doit inspecter l’instruction correspondante et appliquer lui-même son contrat d’autorisation. + +```text +message + signature + clé/adresse + -> builder inline ou table d’offsets officielle + -> instruction précompile sans comptes + -> positionnement transactionnel exact + -> programme consommateur qui inspecte le sysvar d’instructions + -> simulation exacte + -> signature Solana du fee payer et des signataires métier distincts +``` + +Les formes inline Ed25519 et secp256r1 utilisent `u16::MAX` comme référence à leurs propres données. secp256k1 encode des index d’instruction `u8` sans sentinelle ; la forme inline et les références locales du builder exigent donc que l’instruction secp256k1 soit à l’index transactionnel `0`. Un futur assembleur multi-plans doit refuser ou réécrire explicitement toute combinaison qui violerait cette position. + +Les tables avancées acceptent des références externes et un buffer local ajouté après les offsets. Le builder borne le nombre d’entrées à 255, la taille totale à 65 535 octets et les plages locales connues. Il ne lit pas les données des autres instructions : leur cohérence cryptographique est vérifiée par le runtime et leur signification métier par le programme consommateur. + +La signature secp256r1 est fournie au format compact `r || s`; le builder exige des composants non nuls et la forme low-S imposée par le runtime. Le builder secp256k1 reçoit la signature compacte, le recovery ID et l’adresse Ethereum déjà dérivée ; il ne manipule aucune clé privée et n’active aucun helper `bincode`. Le runtime secp256k1 ne garantit pas la canonicalité low-S : lorsqu’elle est requise, cette politique appartient au programme consommateur ou à une validation métier explicite. + + +### Config, Feature, Slashing et ZK ElGamal — `pre.017` + +Les opérations administratives natives restent des plans déterministes sans accès RPC implicite. + +```text +état/rent fourni par l’orchestrateur + -> builder exact + -> plan et signataires + -> lecture stateful de confirmation + -> simulation exacte +``` + +Config encode manuellement le contrat `ConfigKeys + données sérialisées` afin de ne pas activer le helper `bincode` de l’interface. Feature reproduit les séquences officielles d’activation et de révocation. Les montants de rent sont des entrées explicites comptabilisées dans le plafond de dépense. + +La preuve de bloc dupliqué Slashing est indivisible : transfert de préfinancement, instruction Ed25519 et instruction Slashing doivent conserver leurs positions. Le plan refuse donc durable nonce et exige un recent blockhash normal. Le compte de preuve, la fenêtre temporelle, le montant rent-exempt et la rétention avant fermeture relèvent de l’orchestrateur stateful. + +ZK ElGamal accepte une preuve inline ou une preuve dans un compte, avec création optionnelle d’un contexte déjà préalloué. La fermeture exige l’autorité comme signataire. Le builder vérifie discriminant, taille et comptes, mais ne crée pas le contexte et ne recalcule pas la preuve cryptographique. L’ancien ZK Token Proof est historique et reste `decode-only`. + +## Stake et Vote dans la bibliothèque + +`pre.018` ajoute les vingt-quatre helpers clients actuels de `solana-stake-interface 4.3.1` sous forme de plans déterministes. Les formes composées conservent l’ordre officiel : création System puis initialisation Stake, délégation ajoutée en dernier, allocation/assignation avant split. Les rôles staker/withdrawer sont typés par `StakeAuthorizationKind`. + +`pre.019` ajoute les vingt variantes wire Vote actuelles et quatre créations composées legacy/V2. Les autorités Ed25519/BLS, commissions, collecteurs, votes, switch proofs, compact updates et TowerSync sont encodés avec l’enum officielle `VoteInstruction` et `wincode`. Les créations, retraits et dépôts de récompenses déclarent leurs lamports dans le plafond de dépense. + +La construction reste stateless. Avant simulation puis envoi, l’orchestrateur doit charger et valider l’état des comptes Stake/Vote, les autorités, le lockup, le rent, le minimum de délégation, les epochs d’activation/désactivation, la compatibilité merge/split/move, la version Vote, les collecteurs et l’admissibilité runtime de la tour. `Redelegate` reste historique `decode-only`, car l’interface officielle le déclare déprécié et non activable. + +## Loaders dans la bibliothèque + +`pre.020` ajoute onze plans Loader v3 et neuf plans Loader v4. Loader v3 utilise les helpers de `solana-loader-v3-interface 8.x` et leur schéma `wincode` pour créer des buffers, écrire, déployer, upgrader, modifier les autorités, fermer et étendre. Loader v4 conserve le contrat officiel des sept discriminants, mais son wire est construit localement et explicitement parce que les helpers publiés sont conditionnés par `bincode` et qu’aucun schéma `wincode` n’est exposé. + +Les plans de création conservent l’ordre atomique System puis Loader. Les écritures sont bornées, les offsets contrôlés contre les dépassements, les comptes/signataires reproduisent l’interface officielle et les lamports explicitement transférés sont inclus dans `requested_spend_lamports`. Les loyers calculés dynamiquement par le runtime ne sont pas inventés par le builder. + +Avant simulation, l’orchestrateur stateful doit vérifier owner, état courant, autorité, taille/capacité, rent, existence des comptes Program/ProgramData/buffer, relations dérivées et admissibilité de fermeture ou d’extension. Les opérations Loader restent hors de la démo opérateur. BPF Loader v1/v2 sont historiques `decode-only`; Native Loader correspond au déploiement du logiciel validator et n’expose pas d’instruction client autonome. + + +## Préflight stateful natif — `pre.022` + +Les builders restent déterministes et sans I/O. `kb_pipeline::inspect_solana_core_stateful_readiness` ajoute une étape séparée avant la simulation pour les opérations dont l’admissibilité dépend de comptes ou de l’époque courante. + +```text +SolanaCoreOperation + -> plan stateless + -> préflight stateful Localnet/Devnet + -> rapport Ready / Blocked / NotRequired + -> simulation exacte +``` + +Le préflight vérifie le genesis hash attendu et utilise uniquement des RPC typés. Il couvre : + +- ALT : PDA de création, slot récent, owner, autorité, capacité, rent top-up, état actif/désactivé et cooldown de fermeture ; +- Config : absence/réutilisation contrôlée, allocation calculée, owner et espace disponible ; +- Feature : compte absent ou System réutilisable, rent mesuré et état pending avant révocation ; +- Slashing : compte de preuve complet et borné, deux shreds length-prefixed, fenêtre d’un epoch, rent du rapport, destination retenue et délai de fermeture ; +- ZK ElGamal : plage de preuve, owner/space/rent du contexte préalloué, état zéro avant écriture, puis autorité et discriminant avant fermeture. + +Le rapport contient des codes de contrôle stables, des faits mesurés et le plus haut slot de contexte observé. Il ne signe, ne simule et n’envoie aucune transaction. Testnet et Mainnet sont refusés dans cette couche tant que les parcours Localnet/Devnet ne sont pas validés. + +## Préflight stateful SPL Token classique — `0.4.4-pre.005` + +Le même découpage s’applique à SPL Token : `kb_executor_spl_token` construit un plan universel sans I/O, puis `kb_pipeline::inspect_spl_token_stateful_readiness` vérifie l’état Localnet ou Devnet avant simulation. Le décodeur ne réalise aucune lecture RPC. + +Le préflight lit des données complètes bornées et reproduit les layouts classiques Mint, Account et Multisig. Il contrôle notamment owner programme, initialisation, mint/decimals, état frozen, solde et réserve native, delegate/allowance, autorités spécialisées, seuil multisig et rent des comptes préparés. Les montants restent des entiers bruts exacts. Une précondition métier invalide produit `Blocked`; une erreur de transport ou de forme reste une erreur `kb_core`. + +La preuve réseau est séparée en deux niveaux. Le test opt-in de `pre.005` vérifie en lecture seule un `TransferChecked` sur des comptes Devnet fournis explicitement. Le scénario opérateur complet doit ensuite créer des comptes classiques sans ATA, simuler par défaut, et ne signer/envoyer qu’avec une autorisation distincte ; après confirmation, il doit réutiliser hydratation canonique, extraction core, replay, projections et second replay idempotent. + +`pre.005-delta-fix-001` relie ce préflight au chemin d’exécution commun. La simulation constitue une API autonome sans store et conserve le plan, le rapport stateful, le blockhash, les frais et le résultat runtime. La soumission est une seconde API : elle refuse tout signataire absent du wallet de profil, puis applique confirmation, hydratation, extraction, decode replay, requête matérialisée bornée et preuve d’idempotence. Cette restriction de signataire appartient à l’orchestrateur actuel, pas à l’exécuteur universel ni au wire multisig. + +Le 15 juillet 2026, ce parcours a réussi en simulation puis avec un envoi `TransferChecked` réel sur Devnet, entre deux comptes auxiliaires classiques créés sans dépendance ATA. `pre.005-delta-fix-002` durcit la preuve opt-in : résultat d’envoi présent, confirmation `confirmed` ou `finalized`, premier replay sans échec ni refus, matérialisation Token présente, second replay sans échec, refus ou nouvelle sortie, et signature imprimée pour l’audit. + +`0.4.4-pre.006` compose ce contrat sans le contourner. La préparation interne génère trois keypairs éphémères, mesure le rent exact puis construit trois `SystemCreateAccount` officiels avec le propriétaire Token classique et les tailles 82/165/165. Chaque création est simulée, signée par le payeur et le nouveau compte, confirmée, puis relue pour vérifier owner, rent, taille, absence d'executable et données entièrement nulles. Les clés de création peuvent ensuite disparaître : les initialisations Token n'en ont pas besoin. Le lifecycle n'utilise pas ATA. Après autorisation destructive explicite, il exécute onze transactions séparées : initialisation du mint, initialisation des deux comptes, mint checked, transfer checked, approve checked, revoke, burn checked des deux soldes puis fermeture des deux comptes. Une étape incomplète arrête la séquence ; la suivante n'est construite qu'après confirmation, hydratation, extraction, décodage, matérialisation et second replay idempotent de la précédente. + +Une confirmation RPC interrompue ne rend pas la séquence aveuglément rejouable : les initialisations Token ne sont pas idempotentes. La reprise exige donc l'index de la prochaine étape et la signature confirmée de son prédécesseur. Le pipeline hydrate cette signature, extrait le core, impose le decode Token commité, vérifie l'opération matérialisée attendue et un second replay sans sortie avant de construire l'étape suivante. Un délai borné entre étapes réduit les rafales vers le RPC public ; il ne transforme pas un statut inconnu en succès. + +Cette reprise a été exercée sur Devnet le 15 juillet 2026 après un throttling `429` puis une confirmation momentanément incomplète. L'initialisation du compte destination déjà finalisée a été récupérée depuis sa signature canonique ; les étapes 3 à 10 ont ensuite achevé `MintToChecked`, `TransferChecked`, `ApproveChecked`, `Revoke`, deux `BurnChecked` et deux `CloseAccount`. Les neuf étapes récupérées ou envoyées ont chacune validé une matérialisation et un second replay idempotent. + +`0.4.4-pre.007` n'ajoute aucune nouvelle couche d'exécution. La fenêtre Tauri existante construit un intent `TransferChecked` mince, puis appelle le même `execute_devnet_spl_token`. Le journal opérateur interroge au maximum 500 lignes via `MaterializedEventFilter`; les filtres exacts mint, compte et opération sont appliqués au payload typé en mémoire. La frontière UI conserve slots et montants bruts sous forme de chaînes et ne revendique aucun snapshot final ni agrégat OHLC. + +`docs/NATIVE_SOLANA_EXECUTION_MATRIX.json` constitue l’inventaire machine-readable de clôture. Un test Rust de `kb_executor_solana_core` charge cette matrice et impose dix-huit surfaces, 109 opérations appelables exactement égales à `SOLANA_CORE_OPERATION_CODES`, ainsi qu’une justification non vide pour chaque surface sans builder client. Le validateur Python provisoire a été supprimé. + +## Préflight stateful ATA — `0.4.5-pre.004` + +`kb_executor_spl_associated_token_account` construit exactement un builder officiel parmi +`Create`, `CreateIdempotent` et `RecoverNested`. Le Token Program classique ou Token-2022 fait +partie de l’intent et de la dérivation ; la bibliothèque ne lie toujours pas un plan à un endpoint +ou à un wallet concret. + +`kb_pipeline::inspect_spl_associated_token_account_stateful_readiness` est la frontière I/O +Localnet/Devnet. Elle vérifie le genesis, le Program ID ATA, la simulation obligatoire, les mints, +les PDA, l’absence stricte pour `Create`, la compatibilité absence/compte existant pour +`CreateIdempotent`, les trois comptes Token de `RecoverNested`, tous les signataires et le solde du +payer couvrant le plafond rent-plus-frais. + +Les comptes Token-2022 peuvent dépasser 165 octets. Le préflight valide leur préfixe Token Account +commun et laisse les extensions opaques ; il ne commence donc pas `0.4.6`. Le rent minimal du +compte de base et le plafond sont contrôlés avant simulation. La taille et le rent exacts imposés +par des extensions restent vérifiés par le runtime pendant la simulation obligatoire. + +Le rapport `Ready`/`Blocked` ne signe, ne simule et n’envoie rien. L’orchestration de soumission, +confirmation et replay reste une tranche séparée afin qu’un statut RPC inconnu ne provoque jamais +une recréation ATA aveugle. + +## Orchestration ATA Devnet — `0.4.5-pre.005` + +`execute_devnet_spl_associated_token_account` réutilise les frontières communes validées : wallet +persistant de profil, plan typé, préflight stateful, message exact, frais, simulation liée au hash, +signature après autorisation, envoi et confirmation. Après confirmation, la signature déjà connue +est hydratée par tentatives bornées ; chaque tentative RPC autorise deux reprises internes pour les +réponses transitoires telles que `429`, sans reconstruire ni renvoyer l’instruction ATA. + +Le replay sélectionne toujours le Program ID ATA. Pour une cible SPL Token classique, il sélectionne +aussi le Program ID Token afin de décoder les CPI internes, tout en laissant chaque matérialiseur +posséder uniquement ses faits. Pour Token-2022, il ne prétend pas décoder les instructions ou +extensions CPI générales réservées à `0.4.6`. La seconde passe inclut l’état matérialisé et doit ne +produire aucune nouvelle sortie. + +La bibliothèque d’exécution ATA reste indépendante du cluster et du wallet. L’orchestrateur Devnet +fourni ne peut signer qu’avec le wallet persistant du profil ; un `RecoverNested` exigeant une autre +clé est donc refusé comme signataire indisponible. La fenêtre Tauri existante expose seulement les +modes représentatifs `Create` et `CreateIdempotent`, la dérivation readonly et un journal ATA borné. + +Le 16 juillet 2026, le parcours classique `CreateIdempotent` a été confirmé deux fois sur le même +ATA Devnet. La première signature `2H9V…CPbP` et la seconde `3wWT…fv6q` ont chacune réussi +simulation, confirmation, hydratation canonique, extraction, décodage, projection lifecycle unique +et second replay sans nouvelle sortie. La seconde exécution prouve le chemin compte existant +compatible ; elle reste un fait transactionnel distinct et non un doublon de replay. + +Le garde-fou Token-2022 a également refusé correctement le Program ID `TokenzQd…` lorsqu’il était +fourni à la place d’un compte mint : owner, layout Mint et état initialisé ne correspondaient pas. +La preuve positive utilise ensuite le mint contrôlé `3DxK…KTE`, initialisé sur 82 octets et possédé +par Token-2022. `CreateIdempotent` a créé puis réutilisé l’ATA `6WfC…C9Y`, compte de 170 octets +initialisé pour `DwuA…g8B2` avec l’extension `ImmutableOwner`. Les signatures `bMMj…QAJ` au slot +476702448 et `4RZK…VSG` au slot 476703343 ont chacune été simulées, confirmées, hydratées, décodées +et matérialisées une fois, puis ignorées au second replay. L’état préalable observé établit la +réutilisation de la seconde transaction, tandis que la projection parent conserve correctement +`created_or_reused` car l’instruction seule ne distingue pas ces branches. + +`RecoverNested` reste volontairement hors UI. Son parcours contrôlé opt-in reçoit deux mints et une +fixture nested déjà préparée, puis réutilise le même orchestrateur afin de vérifier simulation, +confirmation, fermeture du nested ATA, destination cohérente, deux projections ATA/risk et second +replay idempotent sans introduire de décodeur Token-2022 général. + +La preuve Devnet classique du 16 juillet 2026 utilise `3tdX…FbF5`, ATA wrapped SOL du wallet, +comme owner du nested ATA `AAyv…gti5`. Celui-ci contenait exactement 1 token, soit +1 000 000 000 unités brutes du mint `3fd5…hC9A`, et la destination `69TP…fyWp` était vide. La +signature `37LG…HskM` a transféré le montant complet, fermé le nested ATA, conservé l’owner ATA et +la destination initialisés, produit exactement les deux sorties parent `nested_ata_recovered` et +`nested_ata_anti_pattern_recovered`, puis réussi le second replay sans doublon. Les mutations CPI +Token de transfert et fermeture ne sont pas recopiées dans les projections parent ATA. + +## Soumission et confirmation RPC + +`0.4.2-pre.008` complète la frontière réseau sans fusionner les étapes : + +```text +SignedSolanaTransaction + -> sendTransaction avec preflight obligatoire + -> contrôle de la signature primaire retournée + -> getSignatureStatuses borné + -> getBlockHeight pour détecter l’expiration + -> confirmed / finalized / failed / expired / timed_out +``` + +`sendTransaction` signifie uniquement que le nœud a accepté la transaction signée pour relay. La confirmation reste une opération séparée. La politique borne le nombre de polls et leur intervalle, conserve le dernier slot et la dernière block height observée, puis termine explicitement sur succès, erreur runtime, expiration du recent blockhash ou timeout. + +`requestAirdrop` et `getBalance` sont ajoutés comme primitives génériques de laboratoire. Le plafond d’airdrop Devnet appartient à la configuration et sera appliqué par l’orchestrateur du parcours réel ; la méthode RPC reste indépendante de Tauri et du wallet concret. + +## Wallet temporaire persistant + +`0.4.2-pre.003` introduit un signer de laboratoire dans `kb_wallet` : + +```text +wallets/temporary//.json +``` + +Il peut être généré en mémoire ou persisté afin de conserver la même adresse entre plusieurs démarrages, recevoir un airdrop devnet, créer des comptes et signer les transactions de validation. Le fichier utilise le format JSON Solana standard, reste exclu du dépôt et reçoit des permissions privées sur Unix. + +Ce backend n'est pas un wallet de production : il n'est pas chiffré et les profils fournis l'interdisent sur mainnet. Les futurs backends chiffrés, coffre système et hardware wallet devront conserver la même frontière de signature afin que les exécuteurs ne changent pas. + +## Garde-fous obligatoires + +- dry-run activé par défaut ; +- simulation RPC obligatoire avant envoi ; +- cluster réel comparé au cluster attendu ; +- contrôle du wallet source et des signataires requis ; +- plafond de dépense, de frais et de compute-unit price ; +- fraîcheur du blockhash ou état nonce durable complet, courant, initialisé et concordant ; +- confirmation explicite pour tout envoi mainnet ; +- journalisation séparée des plans et résultats ; +- validation post-exécution par transaction canonique, extraction core, décodage et matérialisation. + +## Exposition dans `kb_app_demo` + +La démo exposera uniquement un sous-ensemble opérationnel sûr : transfert, création/allocate/assign contrôlés, Compute Budget et durable nonce après ajout d’un orchestrateur stateful validé sur cluster. Stake, Vote, loaders, changement d'autorité, Feature, Slashing et opérations ZK peuvent être disponibles dans les crates sans apparaître dans la fenêtre Tauri. + + +## Orchestration Devnet et validation post-exécution + +`0.4.2-pre.009` ajoute dans `kb_pipeline` une orchestration backend bornée pour le premier parcours System transfer sur Devnet. La fonction publique ne remplace aucune couche : elle appelle successivement les contrats existants et conserve leurs résultats séparés. + +```text +classification du genesis hash + -> wallet temporaire persistant + -> solde / airdrop plafonné + -> PreparedExecutionPlan System transfer + -> getLatestBlockhash + -> getFeeForMessage + -> simulateTransaction exact + -> signature locale + -> sendTransaction + -> getSignatureStatuses + getBlockHeight + -> getTransaction + -> transaction canonique + -> extraction core + -> decode replay ciblé +``` + +Le mode par défaut reste une simulation seule. Il ne signe pas, n’envoie pas et ne fabrique pas de signature vide. Le parcours soumis exige simultanément l’autorisation `submit`, la confirmation opérateur lorsque le profil l’impose, l’activation Devnet du wallet, un plafond de dépense et une simulation exacte réussie. + +Le financement par faucet est une étape distincte, uniquement disponible sur Devnet et bornée par `devnet_airdrop_max_lamports`. Il est déclenché seulement lorsque le solde du wallet persistant ne couvre pas le transfert plus le plafond de frais. L’airdrop est lui-même confirmé avant de poursuivre. + +`0.4.2-pre.010` ajoute une précondition liée au destinataire. Le mode metadata-only de `getAccountInfo` détermine si l’adresse existe déjà. Lorsqu’elle est absente, `getMinimumBalanceForRentExemption(0)` fournit le minimum nécessaire à la création implicite d’un compte System sans données ; un transfert inférieur est refusé avant simulation. Les échecs de simulation conservent désormais l’erreur runtime et un extrait borné des logs. La fenêtre `demo_execution_solana_core` ne réimplémente aucune de ces règles : elle sélectionne un profil Devnet, construit la requête et relaie les événements du pipeline. + +Après une confirmation terminale, le pipeline hydrate exclusivement la signature envoyée. Une transaction on-chain échouée reste éligible à l’insertion canonique et au décodage comme intention échouée. Une expiration ou un timeout arrête la validation post-exécution sans prétendre que l’envoi a été confirmé. + +Une erreur avant signature reste un `Err`. Dès qu’une signature locale existe, l’orchestrateur conserve cette signature et transforme les erreurs d’envoi, de confirmation ou de replay en diagnostics dans le résumé. Cette règle évite qu’un appelant perde la référence d’une transaction potentiellement diffusée. + +L’orchestrateur est une API de bibliothèque et ne dépend pas de Tauri. La fenêtre `demo_execution_solana_core` expose uniquement une sélection Devnet réduite de ce backend ; les opérations administratives natives restent disponibles à terme dans les bibliothèques sans être nécessairement proposées dans l’interface. + +## État de clôture `0.4.2` + +Le parcours représentatif Devnet a validé la chaîne plan → simulation exacte → signature → envoi → confirmation → hydratation canonique → extraction core → decode replay. Les builders administratifs supplémentaires restent disponibles dans la bibliothèque, mais ne reçoivent aucune autorisation Mainnet implicite. + +La règle de clôture est donc la suivante : complétude du wire et des plans offline, politique de sécurité et préflight stateful présents, puis preuve cluster obligatoire avant toute exposition mutable supplémentaire. Un statut `future` dans la matrice désigne cette preuve d’activation, pas une instruction manquante. diff --git a/migration/khadhroony-bot2-reference/docs/EXECUTOR_SURFACE_MATRIX.md b/migration/khadhroony-bot2-reference/docs/EXECUTOR_SURFACE_MATRIX.md new file mode 100644 index 0000000..90c57e6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/EXECUTOR_SURFACE_MATRIX.md @@ -0,0 +1,130 @@ + + + +# Matrice des exécuteurs + +## Matrice canonique machine-readable — `pre.022` + +Le fichier `docs/NATIVE_SOLANA_EXECUTION_MATRIX.json` est désormais la source vérifiable pour la clôture de l’exécuteur natif. Il contient exactement : + +- 18 surfaces natives ; +- 14 surfaces `callable` ; +- 4 surfaces `non_invocable` ou historiques ; +- 109 opérations callable, chacune munie de sa source de construction, politique, préflight stateful, contrat de test, décision cluster et exposition UI. + +Les quatre classifications sans builder client sont BPF Loader v1, BPF Loader v2, Native Loader et l’ancien ZK Token Proof historique. Un test Rust charge la matrice et la compare à `SOLANA_CORE_OPERATION_CODES`; toute omission, duplication ou opération inconnue fait échouer `cargo test -p kb_executor_solana_core`. Le validateur Python provisoire a été supprimé. + +Le préflight stateful est actif pour Address Lookup Table, Config, Feature, Slashing et ZK ElGamal. Stake, Vote et Loaders restent marqués `future` dans la matrice pour leur preuve d’activation stateful approfondie ; cela ne retire aucun builder de la surface appelable, mais interdit toute exposition mutable sans validation cluster dédiée. + + +Cette matrice suit la symétrie entre surfaces de décodage et surfaces de construction d’instructions. Elle ne confond pas réservation d’une crate, disponibilité d’un builder, autorisation d’envoi et exposition dans une interface. + +## Socle d’exécution + +| Crate | Rôle | +|-----------------------|------------------------------------------------------------------------------| +| `kb_execution_api` | Contrats provider-neutral des capacités, politiques, plans et résultats. | +| `kb_execution_safety` | Garde-fous stateless avant simulation, signature ou envoi. | +| `kb_execution_solana` | Compilation Solana, blockhash/nonce, preuve de simulation et signature. | +| `kb_rpc` | Acquisition, simulation, envoi et confirmation JSON-RPC ; aucune clé privée. | +| `kb_wallet` | Résolution des signataires derrière un backend explicite. | + +## Règle mécanique + +Pour chaque décodeur de programme ou de protocole classifié : + +```text +kb_decoder_ -> kb_executor_ +``` + +Deux crates de décodage ne suivent volontairement pas cette règle : + +- `kb_decoder_api` est le contrat commun des décodeurs, pas un programme appelable ; +- `kb_decoder_anchor` est un helper technique partagé, pas une surface on-chain autonome. + +L’audit du workspace `0.4.2-pre.014` trouve : + +```text +104 crates kb_decoder_* +102 crates kb_executor_* +102 paires mécaniques exactes +2 exceptions techniques documentées +0 exécuteur orphelin +``` + +## Répartition des 102 surfaces appariées + +| Famille | Paires | +|---------------------------------------------------------------------------------------------------------------|----------:| +| AMM | 26 | +| Launchpad | 9 | +| SPL | 8 | +| Lending | 7 | +| CLMM | 6 | +| Router | 6 | +| Vault | 6 | +| Bridge | 4 | +| Stable | 4 | +| Orderbook | 3 | +| Perpetuals | 3 | +| Staking | 3 | +| Admin | 2 | +| Fees | 2 | +| Adapter, CPMM, DLMM, Governance, Lock, Metadata, NFT, RWA, Solana Core, Treasury, Vesting, Wallet et Weighted | 1 chacune | + +## Niveaux de maturité + +Une paire doit utiliser un statut explicite : + +| Statut | Signification | +|------------------|--------------------------------------------------------------------------------------------------| +| `reserved` | Crate et identité réservées ; aucun builder utilisable. | +| `implementing` | Contrat officiel et politiques en cours de validation. | +| `complete` | Toutes les opérations client officiellement appelables du périmètre sont construites et testées. | +| `not_applicable` | Surface non invocable, retirée ou purement technique, avec justification officielle. | + +`Unsupported(reason)` est acceptable pendant l’implémentation ou pour une impossibilité officielle précise. Il ne doit pas masquer une opération callable oubliée. + +## Politique de jalon + +Chaque version qui active un décodeur doit également : + +1. identifier la crate `kb_executor_` correspondante ; +2. attribuer un statut de maturité à l’exécuteur ; +3. lister les opérations client appelables et les opérations historiques decode-only ; +4. définir autorités, signataires, coûts, slippage ou autres garde-fous ; +5. comparer les payloads et comptes à un builder, une IDL ou un layout officiel ; +6. ajouter les tests offline puis localnet/devnet nécessaires ; +7. documenter l’API publique de la crate dans son README ; +8. décider séparément quelles opérations sont visibles dans `kb_app_demo` ou activables par une stratégie. + +Une opération dangereuse peut rester hors UI, mais elle reste dans le périmètre de la bibliothèque lorsqu’elle est officiellement appelable. + +## Programme Solana Core + +`kb_executor_solana_core` est la première surface passée de `reserved` à une implémentation opérationnelle. Après `pre.021`, elle expose toujours 109 opérations : 17 System, 4 Compute Budget, 5 Address Lookup Table, 6 précompiles de signature, 2 Config, 2 Feature, 2 Slashing, 3 ZK ElGamal, 24 Stake, 24 Vote, 11 Loader v3 et 9 Loader v4. + +| Surface native | Statut à la clôture `0.4.2` | Décision | +|-------------------------------|-----------------------------------------------------|-------------------------------------------------------------------------------------------------| +| System, Compute Budget | `complete` pour les builders client actuels | orchestration durable nonce séparée ; exposition mutable toujours soumise aux politiques | +| Address Lookup Table | `complete` stateless | autorité, rent, capacité et cooldown contrôlés par le préflight stateful | +| Ed25519, secp256k1, secp256r1 | `complete` | formes inline et offsets ; aucune autorisation métier implicite | +| Config | `complete` builder | wire local vérifié sans helper `bincode`; rent/état contrôlés avant envoi | +| Feature | `complete` builder | activation et révocation ; état/rent contrôlés avant envoi | +| Slashing | `complete` builder | close et plan atomique duplicate-block ; compte de preuve et fenêtres epoch stateful | +| ZK ElGamal Proof | `complete` builder | douze preuves inline/account et fermeture de contexte | +| ZK Token Proof historique | `not_applicable` | programme retiré/stub runtime, conservé uniquement pour décodage historique | +| Stake | `complete` pour les 24 helpers clients actuels | validations compte/rent/epoch exigées avant exposition ; Redelegate historique `decode-only` | +| Vote | `complete` pour les 20 variantes wire + 4 créations | validations account/rent/autorités/BLS/tour exigées avant exposition | +| Loader v3 upgradeable | `complete` pour 11 opérations client | interface officielle `wincode`; état, rent, owner et capacité restent stateful | +| Loader v4 | `complete` pour 9 plans client | wire officiel reproduit sans activer les helpers `bincode`; état/autorité/rent restent stateful | +| BPF Loader v1/v2 | `historical_decode_only` | builders dépréciés historiques non promus dans l’exécuteur moderne | +| Native Loader direct | `non_invocable_by_client_instruction` | déploiement lié au logiciel validator, aucun builder transactionnel inventé | + +`pre.021` ne change aucune capacité : elle centralise les validations communes `pub(crate)` et élimine les implémentations identiques détectées dans les modules System, ALT, Config/Feature, Slashing, ZK, Stake, Vote et Loader. + +La clôture `0.4.2` confirme la matrice machine-readable des dix-huit surfaces natives, avec pour chaque opération : builder/layout officiel, politique, tests offline, statut de validation cluster et décision d’exposition UI. + +## Versions futures + +Le `ROADMAP.md` associe désormais explicitement les exécuteurs aux séries Pump, Meteora, Raydium, Orca, Jupiter/routeurs et à chaque jalon ultérieur de décodeur. La symétrie de crates ne vaut pas activation : les listeners et stratégies ne peuvent demander que des capacités explicitement validées et autorisées. diff --git a/migration/khadhroony-bot2-reference/docs/FOUNDATION_CLOSURE.md b/migration/khadhroony-bot2-reference/docs/FOUNDATION_CLOSURE.md new file mode 100644 index 0000000..2c7d760 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/FOUNDATION_CLOSURE.md @@ -0,0 +1,78 @@ + + + +# Clôture de la fondation `0.3.x` + +## Décision de version + +Le jalon `0.3.5` n’est plus conservé comme version autonome. Ses outils d’intégrité, de sélection de corpus et de replay ont été réalisés pendant `0.3.4`. + +La séquence retenue est : + +```text +0.3.4 -> clôture raw/canonical/core/replay +0.4.0 -> infrastructure de décodage et matérialisation +``` + +## Validations réelles acquises + +| Contrôle | Résultat | +|-------------------------|--------------------------------------------------| +| Extraction pending | 70 extraites, 0 échec | +| Intégrité SQL | 7 contrôles sans ligne anormale | +| Ledger initial | 70 succès, 70 tentatives | +| Skip sur échantillon | 14 skips, 0 extraction | +| Force replay | 14 extractions, 0 skip | +| Skip après replay | 14 skips, 0 extraction | +| Cardinalité finale | 70 transactions core | +| Ledger final | 70 succès, 84 tentatives | +| CSV natif | écrit dans `data/exports_csv/` sous Tauri/Linux | +| Tests application | `kb_app_demo` 60 tests, `kb_program_ids` 2 tests | +| Tests pipeline finaux | `kb_pipeline` 24 tests | +| Tests PostgreSQL finaux | `kb_store_pg` 39 tests, rollback inclus | +| Clippy final | `cargo clippy --all-targets` sans avertissement | + +Ces résultats confirment que le force-replay remplace le graphe ciblé sans créer de transaction supplémentaire et que le ledger comptabilise la tentative additionnelle. + +## Contrôles automatisés finaux validés + +### Rollback PostgreSQL + +Le test `optional_postgres_core_extraction_rolls_back_partial_graph_from_env` injecte une violation d’unicité après le début de la persistance. Il vérifie : + +- absence de transaction core partielle ; +- absence d’account key partielle ; +- raw toujours `received` ; +- aucune entrée ledger réussie ; +- replay corrigé possible ; +- nettoyage des fixtures. + +### Annulation backfill + +Les appels réseau et attentes suivantes sont maintenant annulables : + +- `getSignaturesForAddress` en vol ; +- `getTransaction` en vol ; +- attente du pacer ; +- pause de retry standard ; +- pause configurée après 429. + +Les tests offline couvrent une attente déjà annulée et l’abandon coopératif d’une future longue. + +## Clôture acquise + +Les commandes finales ont été exécutées avec succès le 7 juillet 2026 : + +```text +kb_pipeline: 24 passed, 0 failed +kb_store_pg avec PostgreSQL réel: 39 passed, 0 failed +cargo clippy --all-targets: aucun avertissement +``` + +`0.3.4` est inscrite dans `CHANGELOG.md`, le jalon `0.3.x` est clôturé et `0.4.0` devient le jalon actif. La session suivante doit démarrer avec `prompts/019_v0_4_0_decoder_infrastructure.md`. + +## Éléments reportés sans blocage + +Les corpus exhaustifs de protocoles ne sont pas nécessaires pour prouver l’indépendance de l’extracteur core. Ils sont constitués dans leurs versions respectives : Pump `0.5.x`, Meteora `0.6.x`, Raydium `0.7.x` et Orca `0.8.x`. + +Le rejeu manuel d’un backfill `before` depuis `resume_before_signature` reste recommandé lors d’une prochaine campagne longue. La logique de frontière contiguë, les compteurs d’arrêt et une campagne réelle interrompue sont déjà validés. diff --git a/migration/khadhroony-bot2-reference/docs/HISTORICAL_DATA_ACQUISITION_PLAN.md b/migration/khadhroony-bot2-reference/docs/HISTORICAL_DATA_ACQUISITION_PLAN.md new file mode 100644 index 0000000..3a5378a --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/HISTORICAL_DATA_ACQUISITION_PLAN.md @@ -0,0 +1,355 @@ + + + +# Plan différé — acquisition historique gratuite et worker de campagnes + +## Statut + +Ce document conserve un projet futur volontairement retiré du `ROADMAP.md` actif. + +Aucune version n'est attribuée à ce chantier. Il ne doit pas retarder les décodeurs, matérialisateurs et exécuteurs Solana Core, SPL, AMM, launchpads, orderbooks, routers ou autres surfaces prioritaires. + +Le nom **W2** est provisoire. Il désigne ici un worker manuel d'acquisition historique, distinct du worker temps réel destiné au trading. Le nom final des crates et binaires sera décidé au moment de l'activation du chantier. + +## 1. Motivation + +Le projet aura besoin de deux voies d'acquisition complémentaires : + +```text +W1 — temps réel + -> fournisseurs fiables ou payants + -> faible latence + -> détection de créations de tokens, pools et pairs + -> changements de prix/liquidité + -> déclencheurs d'achat, vente et gestion du risque + +W2 — historique manuel + -> sources gratuites ou très économiques + -> campagnes lentes et reprenables + -> corpus vieux d'un an, deux ans ou davantage + -> données destinées à l'analyse, aux tests et à la détection de patterns + -> aucune consommation implicite du budget réservé au trading +``` + +W2 ne remplace ni `kb_rpc`, ni le pipeline canonique, ni les fournisseurs temps réel. Il sert à alimenter progressivement PostgreSQL avec des données historiques dont la latence n'est pas critique. + +## 2. Principes non négociables + +- `kb_rpc` reste la frontière des protocoles Solana : JSON-RPC HTTP/WS standard, extensions fournisseur officiellement prises en charge, Yellowstone/gRPC et transports similaires. +- Une source historique parlant JSON-RPC standard peut réutiliser `kb_rpc`, mais sa politique de campagne, son coût, sa provenance et ses fallbacks restent hors de `kb_rpc`. +- Les APIs REST indexées, exports, datasets analytiques, archives CAR ou outils externes appartiennent à des crates de sources historiques séparées. +- W2 fonctionne en `free_only` par défaut. Aucun fournisseur payant ne doit être interrogé sans autorisation explicite de la campagne. +- Les endpoints et crédits de W1 ne doivent jamais être utilisés comme fallback silencieux de W2. +- Toute donnée destinée au pipeline doit finir par être normalisée dans le contrat canonique existant. +- Une source externe peut découvrir une signature, un slot ou un candidat ; elle ne devient pas pour autant la source canonique de la transaction. +- Les campagnes doivent être bornées, annulables, reprenables, dédupliquées et auditées. +- Une page vide ou une réponse `null` provenant d'une source best-effort ne prouve pas nécessairement l'absence historique de la donnée. + +## 3. Séparation découverte / hydratation + +W2 doit séparer deux responsabilités. + +### 3.1 Découverte + +Trouver des candidats à partir de : + +- adresses ou Program IDs ; +- signatures explicites ; +- plages de slots ou périodes ; +- pools, vaults ou token accounts ; +- programmes DEX/launchpads ; +- datasets indexés capables de filtrer par comptes, instructions ou timestamps. + +### 3.2 Hydratation + +Récupérer la transaction ou le bloc brut correspondant, puis suivre le pipeline existant : + +```text +candidate + -> transaction ou bloc brut + -> insertion raw canonique + -> core extraction + -> decode replay + -> materialization + -> agrégations et corpus d'analyse +``` + +La source de découverte et la source d'hydratation peuvent être différentes. + +Exemple : + +```text +BigQuery découvre les signatures d'une période + -> Old Faithful hydrate les anciennes transactions + -> un RPC public hydrate les transactions encore disponibles + -> PostgreSQL ignore les signatures déjà présentes + -> aucun crédit Helius n'est consommé +``` + +## 4. Architecture candidate + +Les noms ci-dessous sont provisoires et ne constituent pas encore des réservations de crates : + +```text +kb_historical_api + -> capacités, campagnes, candidats, provenance, coût et complétude + +kb_historical_source_rpc + -> profils RPC publics/best-effort utilisant les contrats de kb_rpc + +kb_historical_source_old_faithful + -> intégration avec un processus faithful-cli externe ou une archive locale + +kb_historical_source_bigquery + -> découverte indexée par requêtes analytiques bornées + +kb_historical_source_solscan + -> API officielle optionnelle, avec clé et budget explicites + +kb_worker_historical + -> orchestration manuelle, reprise, quotas, déduplication et import canonique +``` + +Une alternative consiste à garder les contrats et l'orchestration dans des modules internes d'une crate plus compacte. Ce choix devra être tranché après les prototypes et mesures, pas avant. + +## 5. Sources candidates + +### 5.1 RPC utilisé par l'Explorer Solana + +L'Explorer officiel repose sur des appels JSON-RPC Solana standards. Une source `rpc_best_effort` peut donc utiliser des endpoints publics ou communautaires pour : + +- `getSignaturesForAddress` ; +- `getTransaction` ; +- `getBlocks` ; +- `getBlock` ; +- réparations ciblées par signature ou slot. + +Cette source convient aux campagnes étroites et lentes. Elle n'offre pas de SLA, d'index arbitraire ni de garantie de rétention complète. Le site HTML ne doit pas être scrapé. + +Références de recherche : + +- +- +- + +### 5.2 Old Faithful + +Old Faithful est la piste prioritaire pour l'histoire profonde. L'intégration initiale doit privilégier un processus externe `faithful-cli` ou un serveur JSON-RPC local, afin d'éviter un couplage prématuré au format CAR, à l'implémentation Go et à sa licence. + +Usages visés : + +- hydratation de transactions anciennes ; +- scans bornés par époque ou slots ; +- constitution progressive d'un corpus local durable ; +- fallback gratuit lorsque les RPC récents ont purgé les données. + +Références de recherche : + +- +- + +### 5.3 BigQuery ou dataset analytique équivalent + +Un dataset indexé peut servir de moteur de découverte et réduire massivement le nombre d'appels RPC. Il doit être évalué pour : + +- schéma et fraîcheur ; +- instructions outer/inner disponibles ; +- comptes et balances pré/post ; +- précision des filtres ; +- coût réel des requêtes ; +- capacité à exporter des signatures et slots de manière reprenable. + +Il ne remplace pas PostgreSQL ni le pipeline canonique. + +Référence de recherche : + +- + +### 5.4 Solscan + +Solscan fournit un index fonctionnellement intéressant, mais l'intégration durable doit utiliser uniquement son API officielle et respecter ses quotas et conditions. + +Cette source serait : + +- optionnelle ; +- désactivée par défaut ; +- configurée par clé ; +- classée `free_quota` ou `paid` selon le plan ; +- interdite dans une campagne `free_only` si elle entraîne un coût. + +Les endpoints privés du site, cookies, jetons internes et scraping HTML ne doivent pas être utilisés. + +Références de recherche : + +- +- + +### 5.5 Fournisseurs payants + +Helius, Shyft, Chainstack, Triton ou équivalents peuvent être ajoutés ultérieurement comme fallback explicitement autorisé. Ils ne doivent pas être activés dans la politique par défaut de W2 et ne doivent jamais consommer les crédits de W1 à l'insu de l'opérateur. + +## 6. Stablecoins et corpus de marché + +Une campagne naïve `getSignaturesForAddress(MINT)` ne suffit pas à reconstruire toute l'activité d'un stablecoin. Les transferts classiques peuvent référencer les token accounts sans inclure systématiquement le mint dans les comptes de l'instruction. + +Pour constituer des séries utiles à un analyseur de patterns, la stratégie prioritaire doit être : + +```text +stablecoin + -> pools et pairs pertinents + -> vaults et token accounts des pools + -> Program IDs des AMM/CLMM/DLMM/routers + -> transactions candidates + -> décodage des swaps et changements de liquidité + -> matérialisation trades/pools + -> bougies et séries temporelles +``` + +Les transferts génériques du token restent un corpus séparé, beaucoup plus volumineux et moins directement utile au prix. + +## 7. Contrats fonctionnels candidats + +### 7.1 Capacités + +```rust +pub struct HistoricalSourceCapabilities { + pub signatures_by_address: bool, + pub transactions_by_signature: bool, + pub blocks_by_slot: bool, + pub slot_ranges: bool, + pub time_ranges: bool, + pub program_filter: bool, + pub token_filter: bool, + pub instruction_filter: bool, + pub indexed_results: bool, + pub full_history: bool, +} +``` + +### 7.2 Budget + +```rust +pub enum HistoricalSourceCost { + Free, + FreeQuota, + Paid, +} + +pub enum HistoricalBudgetPolicy { + FreeOnly, + FreeThenPaid, + ExplicitSourcesOnly, +} +``` + +`FreeOnly` doit être la valeur de départ de toute campagne manuelle. + +### 7.3 Complétude et provenance + +```rust +pub enum HistoricalCompleteness { + Complete, + Partial, + Unknown, + Pruned, +} + +pub struct HistoricalProvenance { + pub source_name: String, + pub source_kind: String, + pub method: String, + pub fetched_at_unix_ms: i64, + pub paid: bool, +} +``` + +Les types définitifs devront respecter les conventions du workspace, les bornes PostgreSQL et les règles TS-rs si une UI est ajoutée. + +## 8. Campagnes et observabilité + +Une campagne doit conserver au minimum : + +- identifiant stable ; +- cible et filtres ; +- source de découverte et source d'hydratation ; +- politique de coût ; +- curseur et checkpoint de reprise ; +- plage temporelle ou de slots ; +- concurrence, rate limit et backoff ; +- candidats découverts ; +- signatures déjà présentes ; +- transactions hydratées, absentes, pruned ou invalides ; +- erreurs et fallbacks ; +- volume réseau et coût estimé ; +- état `running`, `paused`, `cancelled`, `completed` ou `failed`. + +Les événements `tracing` doivent être émis par les crates responsables. Les secrets, clés API, payloads complets non bornés et URLs contenant des credentials sont interdits dans les logs. + +## 9. Étude préalable obligatoire + +Avant toute activation dans le ROADMAP, exécuter un benchmark reproductible avec : + +- une transaction récente ; +- une transaction vieille d'environ un an ; +- une transaction vieille d'environ deux ans ; +- une adresse peu active ; +- un Program ID très actif ; +- un pool stablecoin ; +- une plage de slots bornée. + +Mesurer pour chaque source : + +- profondeur historique ; +- taux de réponse `null` ou pruned ; +- cohérence de pagination ; +- latence et débit soutenable ; +- limitations et `Retry-After` ; +- filtres disponibles ; +- complétude des transactions et métadonnées ; +- coût ; +- conditions d'utilisation ; +- capacité de reprise et stabilité du contrat. + +## 10. Phases futures sans numéro de version + +### Phase R0 — recherche + +- valider les sources, licences, conditions d'utilisation, coûts et corpus ; +- produire une matrice comparative et un prototype jetable ; +- choisir le nom final de W2. + +### Phase R1 — contrats et ledger de campagne + +- définir les capacités, budgets, candidats, provenance et checkpoints ; +- ajouter les migrations PostgreSQL seulement après stabilisation du contrat. + +### Phase R2 — source RPC gratuite + +- réutiliser `kb_rpc` pour les méthodes standards ; +- ajouter quotas, backoff, déduplication et frontières de complétude. + +### Phase R3 — archive profonde + +- intégrer `faithful-cli` comme processus externe ou service local ; +- valider les imports par époque/slots et la cohérence avec le canonique. + +### Phase R4 — découverte indexée + +- intégrer BigQuery ou une source analytique équivalente après benchmark ; +- exporter uniquement les candidats nécessaires. + +### Phase R5 — sources commerciales facultatives et UI + +- ajouter Solscan ou d'autres APIs officielles avec budget explicite ; +- ajouter une UI de campagne manuelle seulement lorsque les contrats backend sont stables. + +## 11. Conditions avant retour dans le ROADMAP + +Ce chantier ne revient dans le `ROADMAP.md` que lorsque : + +- les décodeurs/matérialisateurs/exécuteurs prioritaires ont suffisamment progressé ; +- au moins deux sources gratuites ont été testées réellement ; +- une stratégie de licence et de conditions d'utilisation est validée ; +- la frontière avec `kb_rpc`, `kb_pipeline` et PostgreSQL est décidée ; +- la politique `free_only` est testable et empêche réellement tout fallback payant ; +- un prompt de session dédié peut être écrit sans hypothèse majeure non vérifiée. diff --git a/migration/khadhroony-bot2-reference/docs/IDL_SURFACE_CLASSIFICATION.md b/migration/khadhroony-bot2-reference/docs/IDL_SURFACE_CLASSIFICATION.md new file mode 100644 index 0000000..eef15ea --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/IDL_SURFACE_CLASSIFICATION.md @@ -0,0 +1,107 @@ + + + +# Classification des surfaces IDL + +Ce fichier résume les reclassements déduits des fichiers présents dans `idls/`. Il ne remplace pas une validation on-chain ; il sert à stabiliser la nomenclature et les crates réservés. + +## Décisions principales + +- `program_ids.rs` est remplacé par `constants.rs` dans les crates core/SPL, afin de regrouper plus tard les discriminants, sélecteurs et constantes internes non publiques. +- Les constantes réexportées par le `lib.rs` d’une crate doivent être appelées via `crate::CONSTANT_NAME` depuis les modules internes de cette même crate. +- Les surfaces IDL classifiées reçoivent un nom canonique fonctionnel : `amm_*`, `clmm_*`, `router_*`, `vault_*`, `lending_*`, etc. +- Les comptes non prouvés comme `program_id`, comme `BAGSB...`, restent hors workspace de décodage. Les vrais programmes Bags Fee Share V1/V2 sont classés séparément dans `docs/BAGS_FM.md`. + +## Reclassements notables + +| Canonique | Ancien nom/source | Justification IDL | +|--------------------------------------|-------------------------------|----------------------------------------------------------------------------------------| +| `vault_carrot_defi` | `carrot_defi` | IDL orientée vault : `initVault`, `issue`, `redeem`, `addAsset`, `removeStrategy`. | +| `lending_clone` | `clone` | IDL orientée lending : borrow positions, collateral, pool parameters, oracle updates. | +| `clmm_fusion` | `fusion_amm` | IDL avec positions, liquidity, fees et limit orders ; plus proche CLMM que simple AMM. | +| `stable_swap_hylo_exchange` | `hylo_exchange` | IDL stable/levercoin : mint, redeem, swap stable/lever. | +| `vault_hylo_stability_pool` | `hylo_stability_pool` | IDL de stability pool : user deposit/withdraw et rebalances. | +| `wallet_jupiter_apepro_smart_wallet` | `jupiter_apepro_smart_wallet` | IDL smart wallet avec approvals, `postSwap` et withdraw. | +| `vault_kamino_yvaults` | `kamino` | IDL `yvaults`, stratégies, collateral info et shares metadata. | +| `governance_metadao_bid_wall` | `metadao_bid_wall` | Surface MetaDAO liée à la gouvernance/futarchy plutôt qu’à un AMM direct. | +| `stable_swap_numeraire` | `numeraire` | IDL avec pool stable virtuel, add/remove liquidity et metadata LP. | +| `rwa_ondo_global_markets` | `ondo_global_markets` | IDL RWA/roles/attestation/burn ; pas un DEX. | +| `clmm_pancake_swap` | `pancake_swap` | IDL `amm_v3` avec positions et liquidity : classé CLMM. | +| `adapter_saber_decimal_wrapper` | `sabre_decimal_wrapper` | IDL wrapper d’adaptation de décimales : initializeWrapper, deposit, withdraw. | + +## Table IDL triée par canonique + +| Canonique | Source | Program ID | IDL | Crate cible | Statut | +|------------------------------------------------|----------------------------------|------------------------------------------------|---------------------------------------------------------------------------------|-----------------------------------------------------------|---------------------| +| `adapter_saber_decimal_wrapper` | `sabre_decimal_wrapper` | `DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB` | `saber_decimal.DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB.json` | `kb_decoder_adapter_saber_decimal_wrapper` | `canonical_current` | +| `admin_jupiter_lock` | `jupiter_lock` | `LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn` | `jupiter_locker.LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn.json` | `kb_decoder_admin_jupiter_lock` | `canonical_current` | +| `admin_pump_fees` | `pump_fees` | `pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ` | `pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json` | `kb_decoder_admin_pump_fees` | `canonical_current` | +| `amm_bonk_swap` | `bonk_swap` | `BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p` | `bonkswap.BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p.json` | `kb_decoder_amm_bonk_swap` | `canonical_current` | +| `amm_goosefx_gamma` | `goose_fx_gamma` | `GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT` | `goosefx_gamma.GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT.json` | `kb_decoder_amm_goosefx_gamma` | `canonical_current` | +| `amm_goosefx_v2` | `goose_fx_v2` | `GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn` | `goosefx_v2.GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn.json` | `kb_decoder_amm_goosefx_v2` | `canonical_current` | +| `amm_guac_swap` | `guac_swap` | `Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1` | `guacswap.Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1.json` | `kb_decoder_amm_guac_swap` | `canonical_current` | +| `amm_lifinity_swap_v2` | `lifinity_swap_v2` | `2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c` | `lifinity_amm_v2.2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c.json` | `kb_decoder_amm_lifinity_swap_v2` | `canonical_current` | +| `amm_metadao_futarchy_amm` | `metadao_futarchy_amm` | `FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq` | `metadao_futarchy.FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq.json` | `kb_decoder_amm_metadao_futarchy_amm` | `canonical_current` | +| `amm_metadao_v0_5` | `metadao_amm_v0_5` | `AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ` | `metadao_amm_v0.5.AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ.json` | `kb_decoder_amm_metadao_v0_5` | `canonical_current` | +| `amm_meteora_damm_v1` | `meteora_damm_v1` | `Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB` | `meteora_pools_amm.Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB.json` | `kb_decoder_amm_meteora_damm_v1` | `canonical_current` | +| `amm_meteora_damm_v2` | `meteora_damm_v2` | `cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG` | `meteora_damm_v2.cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG.json` | `kb_decoder_amm_meteora_damm_v2` | `canonical_current` | +| `amm_pump_swap` | `pump_swap` | `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA` | `pump_swap.pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA.json` | `kb_decoder_amm_pump_swap` | `canonical_current` | +| `amm_vertigo` | `vertigo` | `vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ` | `vertigo.vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ.json` | `kb_decoder_amm_vertigo` | `canonical_current` | +| `amm_virtuals` | `virtuals` | `5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki` | `virtuals.5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki.json` | `kb_decoder_amm_virtuals` | `canonical_current` | +| `amm_woofi` | `woofi` | `WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb` | `woofi.WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb.json` | `kb_decoder_amm_woofi` | `canonical_current` | +| `bridge_circle_cctp_token_messenger_minter` | `cctp_token_messenger_minter` | `CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3` | `cctp_v1.CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3.json` | `kb_decoder_bridge_circle_cctp_token_messenger_minter` | `canonical_current` | +| `bridge_circle_cctp_token_messenger_minter_v2` | `cctp_token_messenger_minter_v2` | `CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe` | `cctp_v2.CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe.json` | `kb_decoder_bridge_circle_cctp_token_messenger_minter_v2` | `canonical_current` | +| `bridge_layer_zero_endpoint` | `layer_zero_endpoint` | `76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6` | `layerzero_endpoint.76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6.json` | `kb_decoder_bridge_layer_zero_endpoint` | `canonical_current` | +| `bridge_layer_zero_executor` | `layer_zero_executor` | `6doghB248px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn` | `layerzero_executor.6doghB248px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn.json` | `kb_decoder_bridge_layer_zero_executor` | `canonical_current` | +| `clmm_byreal` | `byreal_clmm` | `REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2` | `byreal_clmm.REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2.json` | `kb_decoder_clmm_byreal` | `canonical_current` | +| `clmm_fusion` | `fusion_amm` | `fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9` | `fusion_amm.fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9.json` | `kb_decoder_clmm_fusion` | `canonical_current` | +| `clmm_orca_whirlpool` | `orca_whirlpool` | `whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc` | `orca_whirlpool.whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.json` | `kb_decoder_clmm_orca_whirlpool` | `canonical_current` | +| `clmm_orca_whirlpool` | `orca_whirlpool` | `whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc` | `orca_whirlpool.whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.json` | `kb_decoder_clmm_orca_whirlpool` | `canonical_current` | +| `clmm_pancake_swap` | `pancake_swap` | `HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq` | `pancakeswap.HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq.json` | `kb_decoder_clmm_pancake_swap` | `canonical_current` | +| `clmm_raydium` | `raydium_clmm` | `CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK` | `raydium_clmm.CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK.json` | `kb_decoder_clmm_raydium` | `canonical_current` | +| `clmm_stabble` | `stabble_clmm` | `6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6` | `stabble_clmm.6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6.json` | `kb_decoder_clmm_stabble` | `canonical_current` | +| `cpmm_raydium` | `raydium_cpmm` | `CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C` | `raydium_cpmm.CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C.json` | `kb_decoder_cpmm_raydium` | `canonical_current` | +| `dlmm_meteora` | `meteora_dlmm` | `LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo` | `meteora_dlmm.LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo.json` | `kb_decoder_dlmm_meteora` | `canonical_current` | +| `governance_metadao_bid_wall` | `metadao_bid_wall` | `WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx` | `metdao_bid_wall.WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx.json` | `kb_decoder_governance_metadao_bid_wall` | `canonical_current` | +| `launchpad_boop_fun` | `boop_fun` | `boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4` | `boop_fun.boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4.json` | `kb_decoder_launchpad_boop_fun` | `canonical_current` | +| `launchpad_metadao_ico` | `metadao_ico` | `moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM` | `metadao_launchpad.moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM.json` | `kb_decoder_launchpad_metadao_ico` | `canonical_current` | +| `launchpad_meteora_dbc` | `meteora_dbc` | `dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN` | `meteora_dbc.dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN.json` | `kb_decoder_launchpad_meteora_dbc` | `canonical_current` | +| `launchpad_moonit` | `moonit` | `MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG` | `moonit.MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG.json` | `kb_decoder_launchpad_moonit` | `canonical_current` | +| `launchpad_orca_wavebreak` | `orca_wavebreak` | `waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF` | `orca_wavebreak.waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF.json` | `kb_decoder_launchpad_orca_wavebreak` | `canonical_current` | +| `launchpad_printr` | `printr` | `T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint` | `printr.T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint.json` | `kb_decoder_launchpad_printr` | `canonical_current` | +| `launchpad_pump_fun` | `pump_fun` | `6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P` | `pump_fun.6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P.json` | `kb_decoder_launchpad_pump_fun` | `canonical_current` | +| `launchpad_pump_pumpup_ai` | `pumpup_ai` | `PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd` | `pumpup.PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd.json` | `kb_decoder_launchpad_pump_pumpup_ai` | `canonical_current` | +| `launchpad_raydium_launchlab` | `raydium_launchlab` | `LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj` | `raydium_launchlab.LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj.json` | `kb_decoder_launchpad_raydium_launchlab` | `canonical_current` | +| `lending_clone` | `clone` | `C1onEW2kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr` | `clone.C1onEW2kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr.json` | `kb_decoder_lending_clone` | `canonical_current` | +| `lending_kamino` | `kamino_lending` | `KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD` | `kamino_lending.KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD.json` | `kb_decoder_lending_kamino` | `canonical_current` | +| `lending_marginfi_v2` | `marginfi_v2` | `MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA` | `marginfi_v2.MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA.json` | `kb_decoder_lending_marginfi_v2` | `canonical_current` | +| `lock_raydium_lp` | `raydium_lock_lp` | `LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE` | `raydium_lock.LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE.json` | `kb_decoder_lock_raydium_lp` | `canonical_current` | +| `nft_metaplex_bubblegum` | `bubblegum` | `BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY` | `bubblegum.BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY.json` | `kb_decoder_nft_metaplex_bubblegum` | `canonical_current` | +| `orderbook_jupiter_limit_order` | `jupiter_limit_order` | `jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu` | `jupiter_limit_order.jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu.json` | `kb_decoder_orderbook_jupiter_limit_order` | `canonical_current` | +| `orderbook_jupiter_limit_order_v2` | `jupiter_limit_order_v2` | `j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X` | `jupiter_limit_order_v2.j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X.json` | `kb_decoder_orderbook_jupiter_limit_order_v2` | `canonical_current` | +| `orderbook_openbook_v2` | `openbook_v2` | `opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb` | `openbook_v2.opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb.json` | `kb_decoder_orderbook_openbook_v2` | `canonical_current` | +| `perpetuals_drift_v2` | `drift_v2` | `dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH` | `drift_v2.dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH.json` | `kb_decoder_perpetuals_drift_v2` | `canonical_current` | +| `perpetuals_jupiter` | `jupiter_perpetuals` | `PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu` | `jupiter_perpetuals.PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu.json` | `kb_decoder_perpetuals_jupiter` | `canonical_current` | +| `perpetuals_zeta` | `zeta` | `ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD` | `zeta.ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD.json` | `kb_decoder_perpetuals_zeta` | `canonical_current` | +| `router_jupiter_aggregator_v4` | `jupiter_agregator_v4` | `JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB` | `jupiter_v4.JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB.json` | `kb_decoder_router_jupiter_aggregator_v4` | `canonical_current` | +| `router_jupiter_aggregator_v6` | `jupiter_agregator_v6` | `JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4` | `jupiter_v6.JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4.json` | `kb_decoder_router_jupiter_aggregator_v6` | `canonical_current` | +| `router_jupiter_dca` | `jupiter_dca` | `DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M` | `jupiter_dca.DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M.json` | `kb_decoder_router_jupiter_dca` | `canonical_current` | +| `router_okx_labs_v1` | `okx_labs_v1` | `6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma` | `okx_lab_v1.6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma.json` | `kb_decoder_router_okx_labs_v1` | `canonical_current` | +| `router_okx_labs_v2` | `okx_labs_v2` | `proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u` | `okx_dex_router.proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u.json` | `kb_decoder_router_okx_labs_v2` | `canonical_current` | +| `rwa_ondo_global_markets` | `ondo_global_markets` | `XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm` | `ondo_gm.XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm.json` | `kb_decoder_rwa_ondo_global_markets` | `canonical_current` | +| `stable_swap_hylo_exchange` | `hylo_exchange` | `HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn` | `hylo_exchange.HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn.json` | `kb_decoder_stable_swap_hylo_exchange` | `canonical_current` | +| `stable_swap_jupiter_stable` | `jupiter_stable` | `JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr` | `jupiter_stable.JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr.json` | `kb_decoder_stable_swap_jupiter_stable` | `canonical_current` | +| `stable_swap_numeraire` | `numeraire` | `NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P` | `numeraire.NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P.json` | `kb_decoder_stable_swap_numeraire` | `canonical_current` | +| `stable_swap_stabble` | `stabble_stable_swap` | `swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ` | `stabble_stable_swap.swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ.json` | `kb_decoder_stable_swap_stabble` | `canonical_current` | +| `staking_kamino_farm` | `kamino_farm` | `FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr` | `kamino_farms.FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr.json` | `kb_decoder_staking_kamino_farm` | `canonical_current` | +| `staking_marinade_finance` | `marinade_finance` | `MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD` | `marinade_finance.MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD.json` | `kb_decoder_staking_marinade_finance` | `canonical_current` | +| `treasury_helium_treasury_management` | `helium_treasury_management` | `treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5` | `helium_treasury_management.treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5.json` | `kb_decoder_treasury_helium_treasury_management` | `canonical_current` | +| `vault_carrot_defi` | `carrot_defi` | `CarrotwivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s` | `carrot.CarrotwivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s.json` | `kb_decoder_vault_carrot_defi` | `canonical_current` | +| `vault_hylo_stability_pool` | `hylo_stability_pool` | `HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ` | `hylo_stability_pool.HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ.json` | `kb_decoder_vault_hylo_stability_pool` | `canonical_current` | +| `vault_kamino` | `kamino_vault` | `kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr` | `kamino_vault.kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr.json` | `kb_decoder_vault_kamino` | `canonical_current` | +| `vault_kamino_v2` | `kamino_vault_v2` | `KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd` | `kamino_vault_v2.KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd.json` | `kb_decoder_vault_kamino_v2` | `canonical_current` | +| `vault_kamino_yvaults` | `kamino` | `6LtLpnUFNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc` | `kamino.6LtLpnUFNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc.json` | `kb_decoder_vault_kamino_yvaults` | `canonical_current` | +| `vault_meteora` | `meteora_vault` | `24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi` | `meteora_vault.24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi.json` | `kb_decoder_vault_meteora` | `canonical_current` | +| `vesting_streamflow` | `streamflow` | `strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m` | `streamflow.strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m.json` | `kb_decoder_vesting_streamflow` | `canonical_current` | +| `wallet_jupiter_apepro_smart_wallet` | `jupiter_apepro_smart_wallet` | `JSW99DKmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw` | `jupiter_aprepro_smart_wallet.JSW99DKmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw.json` | `kb_decoder_wallet_jupiter_apepro_smart_wallet` | `canonical_current` | +| `weighted_swap_stabble` | `stabble_weighted_swap` | `swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW` | `stabble_weighted_swap.swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW.json` | `kb_decoder_weighted_swap_stabble` | `canonical_current` | diff --git a/migration/khadhroony-bot2-reference/docs/INSTRUCTION_REPLAY_CONTRACTS.md b/migration/khadhroony-bot2-reference/docs/INSTRUCTION_REPLAY_CONTRACTS.md new file mode 100644 index 0000000..6204d21 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/INSTRUCTION_REPLAY_CONTRACTS.md @@ -0,0 +1,112 @@ + + + +# Contrats de replay par instruction + +Ce document complète le cadrage `0.2.1`. Il ne crée pas encore le schéma SQL complet, mais il verrouille l'idée suivante : le scheduling de replay doit cibler les instructions normalisées, tandis que le décodage doit recevoir une instruction avec son contexte Solana extrait. + +## Motivation + +Une transaction Solana peut contenir plusieurs instructions top-level et plusieurs inner instructions. Certaines instructions peuvent être déjà décodées, d'autres non. Rejouer toute la transaction à chaque correction de décodeur serait plus coûteux et moins précis. + +Le pipeline doit donc pouvoir faire : + +```text +raw transaction +→ extraction core transaction +→ extraction account keys, instructions, inner instructions, logs, balances +→ sélection des instructions non traitées +→ reconstruction d'un replay input contextualisé +→ decode/materialize uniquement sur ces inputs +``` + +## Scheduling instruction-level + +L'état `CoreInstructionProcessingState` prépare cette granularité. + +| État | Sens | +|-------------------|---------------------------------------------------------------| +| `Pending` | Instruction disponible pour premier traitement. | +| `Decoded` | Instruction décodée par au moins un décodeur. | +| `Materialized` | Instruction ayant produit une projection métier. | +| `Ignored` | Instruction ignorée volontairement par la politique courante. | +| `Failed` | Instruction en échec, à diagnostiquer. | +| `ReplayRequested` | Instruction à rejouer même si un traitement précédent existe. | + +Les états ne remplacent pas le ledger par module/version. Ils servent de vue rapide et de filtre de scheduling. + +## Décodage contextualisé + +Le décodeur ne doit pas être limité à `CoreInstructionRow.payload_json`. + +Certains protocoles Solana exigent de lire : + +- les inner instructions ; +- les logs Anchor ou non Anchor ; +- les balances avant/après ; +- les comptes résolus ; +- les loaded addresses ; +- les erreurs de transaction ; +- des séquences de logs liées à un CPI ; +- des instructions voisines de la même transaction. + +`CoreInstructionReplayInput` représente ce contrat de lecture : une instruction ciblée plus un contexte extrait depuis les tables `core`. Depuis le contrat `2`, ce contexte inclut également toutes les instructions outer de la signature, ordonnées numériquement et munies de leur payload retenu et de son hash. + +## Sélection de replay + +`CoreInstructionReplayFilter` permet de sélectionner les instructions par : + +- état de traitement ; +- `program_id` ; +- plage de slots. + +L'usage standard pour un worker de decode sera : + +```text +processing_state = Pending ou ReplayRequested +program_id = programme ciblé par le décodeur +plage de slots = optionnelle +``` + +Le repository charge les logs, balances, account keys et instructions outer nécessaires pour retourner `CoreInstructionReplayInput`. La liste outer inclut la cible et utilise les champs stables `instructionIndex`, `instructionPath`, `programId`, `payloadJson` et `payloadHash`. Cette extension lit les tables existantes et ne demande aucune migration. + +## Marquage de cycle de vie + +`CoreInstructionLifecycleMark` permet à un module de changer l'état d'une instruction après traitement. + +Exemples : + +- un décodeur reconnu marque l'instruction `Decoded` ; +- un materializer complet marque l'instruction `Materialized` ; +- un décodeur non concerné peut marquer `Ignored` dans un contexte contrôlé ; +- une erreur de parsing marque `Failed` ; +- une correction de décodeur peut remettre `ReplayRequested`. + +## Relation avec `kb_sol_ops_processing_ledger` + +Le processing ledger garde le détail par module, version et input. L'état sur l'instruction reste le dernier état opérationnel visible. + +Pour éviter les doublons, l'input key du ledger devra être stable, par exemple : + +```text +signature:instruction_path:program_id:processor_name:processor_version +``` + +La forme exacte n'est pas figée en `0.2.1`, mais elle doit rester déterministe. + +## Rétention du payload d'instruction et des logs + +Le payload JSON d'une instruction et le texte des logs sont utiles au début. Plus tard, après décodage et matérialisation fiables, ils pourront être compactés ou purgés avec conservation d'un hash. + +Les entities `CoreInstructionRow` et `CoreLogRow` préparent ce cas en autorisant : + +- payload ou texte présent ; +- payload ou texte absent ; +- hash conservé ; +- état de traitement consultable pour replay. + +La purge effective ne doit pas être implémentée avant que les diagnostics de replay soient fiables. + +## Décision pour `0.2.1` + +`0.2.1` ajoute les contrats Rust nécessaires, mais ne crée pas encore les tables SQL. Les migrations réelles restent prévues pour `0.2.3` et `0.2.4`. diff --git a/migration/khadhroony-bot2-reference/docs/LIVE_SOURCE_STRATEGY.md b/migration/khadhroony-bot2-reference/docs/LIVE_SOURCE_STRATEGY.md new file mode 100644 index 0000000..b9d6bda --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/LIVE_SOURCE_STRATEGY.md @@ -0,0 +1,104 @@ + + + +# Stratégie des sources de transactions + +## Décision actuelle + +Les sources temps réel payantes ne sont plus l’axe actif de `0.3.x`. + +La priorité est : + +```text +backfills HTTP sur comptes gratuits + -> transaction canonique + -> extraction core + -> décodeurs Core/Pump/Meteora/Raydium/Orca/Jupiter +``` + +Helius Developer, Triton, Chainstack, Shyft ou un autre provider seront testés seulement lorsque le pipeline pourra décoder et matérialiser immédiatement les transactions reçues. + +## Sources futures + +### HTTP JSON-RPC + +Usage : + +- backfill historique ; +- hydratation de signatures ; +- réparation ; +- validation ponctuelle. + +Méthodes principales : + +```text +getSignaturesForAddress +getTransaction +getSignatureStatuses +getSlot +``` + +### Helius WebSocket enrichi + +`transactionSubscribe` est une extension Helius, pas une méthode Solana standard. + +Elle sera implémentée dans des modules internes de `kb_rpc`, sans créer de crate séparée, puis convertie vers le modèle canonique commun. + +### Yellowstone gRPC + +Yellowstone fournit l’équivalent fonctionnel d’un flux de transactions complètes filtrées. Les endpoints Triton, Chainstack, Shyft ou compatibles doivent être supportés par configuration dans `kb_rpc`. + +### Solana WebSocket standard + +`logsSubscribe` par mention ou avec filtre `"all"` reste une solution de probe, de fallback ou de comparaison. Une notification de logs ne devient durable qu’après hydratation de la transaction ou enregistrement d’une observation technique légère. + +## Stockage commun + +Toutes les sources convergent vers : + +```text +kb_sol_raw_transactions +kb_sol_obs_transaction_observations +``` + +Les décodeurs ne connaissent ni le fournisseur, ni le protocole, ni la méthode d’acquisition. + +## Ordre de mise en œuvre + +```text +0.3.x transaction canonique, observations, backfill gratuit, extraction core +0.4.x décodeurs Core/SPL +0.5.x Pump +0.6.x Meteora +0.7.x Raydium +0.8.x Orca +0.9.x Jupiter et routeurs +0.10.x Helius transactionSubscribe, Yellowstone gRPC et comparaison +``` + +## Sécurité de reprise + +Pendant toute perte du flux principal : + +```text +trading = disabled +``` + +Le replay ou le backfill après reconnexion sert à remettre la base en cohérence. Un événement `replayed`, `backfilled` ou `repaired` ne doit pas déclencher rétroactivement un ordre. + +## Critères de choix futur + +La décision provider devra comparer : + +- couverture par programme et CPI ; +- latence par signature ; +- coût par Go, crédit ou forfait ; +- nombre de filtres et streams ; +- régions ; +- reconnexion et fenêtre de replay ; +- pertes et backpressure ; +- portabilité du protocole ; +- qualité du support. + +Les timings seront mesurés dans `kb_sol_obs_transaction_observations`. + diff --git a/migration/khadhroony-bot2-reference/docs/LOGGING.md b/migration/khadhroony-bot2-reference/docs/LOGGING.md new file mode 100644 index 0000000..d7f21b7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/LOGGING.md @@ -0,0 +1,106 @@ + + + +# Logging + +La journalisation est centralisée dans `kb_logging` et repose sur des événements `tracing` structurés. + +## Objectifs + +- séparer console, fichiers globaux et fichiers par crate ; +- conserver un fichier JSONL dédié aux erreurs ; +- retrouver immédiatement un échec de décodage ou de matérialisation ; +- corréler RPC, pipeline, store et processor sans dupliquer les décisions dans Tauri ; +- activer le debug d’une crate sans dépendre de son découpage interne en modules. + +## Arborescence d’un profil + +Pour un profil stocké sous `logs//`, la configuration crée : + +```text +logs//debug.log +logs//info.log +logs//error.jsonl +logs//app.log +logs///debug.log +logs///info.log +logs///error.jsonl +``` + +Les routes sont en rotation quotidienne. `tracing-appender` insère la date dans le nom physique produit à partir du préfixe et du suffixe configurés. + +Les niveaux sont cumulatifs : + +- `debug.log` reçoit `debug`, `info`, `warn` et `error` ; +- `info.log` reçoit `info`, `warn` et `error` ; +- `error.jsonl` reçoit uniquement `error` ; +- `app.log` reçoit les événements `kb_app_demo` à partir de `debug`. + +`error.jsonl` utilise le format JSON pour permettre la recherche par champ. Les autres fichiers utilisent le format humain sans ANSI. + +## Targets canoniques + +Le target est exactement le nom de la crate et provient de `src/constants.rs` : + +```text +kb_app_demo +kb_decoder_solana_core +kb_executor_metadata_spl_name_service +kb_executor_solana_core +kb_executor_spl_account_compression +kb_executor_spl_memo +kb_executor_spl_noop +kb_executor_spl_single_pool +kb_logging +kb_materializer_admin +kb_materializer_compliance_audit +kb_materializer_lifecycle +kb_materializer_staking +kb_pipeline +kb_rpc +kb_store_pg +``` + +La granularité passe par les champs `action`, `stage`, `window`, `campaign_id`, `signature`, `instruction_path`, `program_id`, `processor_name`, `status` et `error_code`. + +## Corrélation de campagne + +Les campagnes de backfill, extraction core et replay decode utilisent des identifiants stables : + +```text +capture_session_id +campaign_id +signature +slot +instruction_path +program_id +input_key +input_hash +processor_name +processor_version +``` + +Le pipeline journalise la sélection et l’agrégation. Le RPC journalise le transport et l’endpoint sélectionné sans exposer l’URL secrète. Le store journalise la persistance et les rollbacks. Le décodeur et chacun des matérialiseurs lifecycle, admin et compliance audit journalisent leur décision propre. + +## Fichier d’erreurs + +Les erreurs de décodage et de matérialisation sont émises au niveau `error` par la crate responsable, puis éventuellement par la frontière qui constate l’échec terminal. Elles apparaissent donc dans : + +- `logs//error.jsonl` ; +- `logs///error.jsonl`. + +Cette duplication volontaire fournit une vue globale et une vue isolée par composant. Les champs de corrélation permettent de regrouper les événements d’un même défaut. + +Une transaction on-chain échouée mais correctement décodée n’est pas écrite dans `error.jsonl` uniquement à cause de `meta.err`. En revanche, un payload déclaré compatible mais `failed` ou `unsupported` indique une lacune du code ou une évolution du protocole et doit être visible dans le fichier d’erreurs. + +## Frontend et Tauri + +Le frontend peut continuer à transmettre un identifiant logique tel que `kb_app_demo.frontend.demo_decode_replay`. Le backend le valide et le conserve dans le champ `frontend_target`, mais l’événement est émis avec le target canonique `kb_app_demo`. + +`kb_app_demo` ne doit pas recopier les détails internes d’un retry RPC, d’un parsing binaire ou d’un commit SQL. Il journalise l’invocation, la progression visible et le résumé reçu de la crate opérationnelle. + +## Sécurité + +Les logs ne contiennent jamais de clé privée, seed phrase, DSN non masqué, URL fournisseur secrète ou payload complet non borné. Pour un payload problématique, conserver la taille, un préfixe borné et un SHA-256. + +Le contrat normatif complet se trouve dans `docs/TRACING_CONTRACT.md`. diff --git a/migration/khadhroony-bot2-reference/docs/MATERIALIZATIONS.md b/migration/khadhroony-bot2-reference/docs/MATERIALIZATIONS.md new file mode 100644 index 0000000..e8eb5c6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/MATERIALIZATIONS.md @@ -0,0 +1,68 @@ + + + +# Catalogue des matérialisations + +Les matérialisations transforment des événements décodés en événements métier exploitables par la base de données, les validations, les agrégations et les futurs signaux de stratégie. + +## Matérialisations prioritaires + +- `trade` : swaps, buys, sells, fills et conversions entre actifs. +- `liquidity` : dépôts, retraits, ajout ou retrait de liquidité, bootstrap de pool. +- `lifecycle` : création, initialisation, migration, fermeture ou changement d'état d'un pool, marché, compte ou vault. +- `fee` : collecte, claim, sweep, buyback, mise à jour de configuration de frais. +- `admin` : changement d'autorité, update de config, pause, unpause, transfert de rôle. +- `token_account` : création ATA, transfert, mint, burn, close account, approve, revoke. +- `pool_state` : snapshots de réserves, ticks, bins, prix, liquidité active ou virtual reserves. +- `orderbook` : place order, cancel order, fill, settle funds, consume events. +- `reward` : émission, claim, distribution, farming, incentives. +- `risk` : signaux dérivés à partir des événements admin, liquidité, metadata, authority ou anomalies. + +## Projections natives actives dans `0.4.1` + +`kb_materializer_lifecycle` produit des événements lifecycle idempotents à partir d'observations exactes, réussies et commitées : + +| Domaine | Sortie stable | Entrées couvertes | +|----------------------|--------------------------------------------|--------------------------------------------------------------------------------------------------------------| +| Address Lookup Table | `address_lookup_table::0` | create, freeze, extend, deactivate, close | +| Program loaders | `program_loader:::0` | finalize immuable, initialize/deploy/upgrade/close/extend upgradeable, set length/deploy/retract/finalize v4 | +| Feature Gate | `feature_gate:revoke_pending_activation:0` | revoke pending activation | +| Comptes System | `system_account::0` | create, create with seed, create allow prefund, allocate, allocate with seed | +| Durable nonce System | `durable_nonce_account::0` | initialize, advance, authorize, upgrade, withdraw | +| Contexte ZK ElGamal | `zk_proof_context::0` | initialisation demandée après vérification réussie, fermeture | +| Rapport Slashing | `slashing_violation_report::0` | initialisation après preuve duplicate block acceptée, fermeture après rétention | + +La famille décodée n'impose pas à elle seule la famille matérialisée. `authorize_nonce_account` est un événement `Admin`, tandis qu'une vérification ZK ElGamal est un événement `Audit`; ces observations ne produisent une sortie `Lifecycle` que lorsque leur mutation durable exacte est reconnue. Une transaction échouée ou non commitée est toujours refusée. + +`kb_materializer_admin` produit des sorties `Admin` pour les assignations System, les écritures Config opaques et les changements d’autorité Loader. `kb_materializer_compliance_audit` produit des sorties `ComplianceAudit` pour les écritures/copies de bytecode Loader. `kb_materializer_staking` produit les projections instructionnelles Stake/Vote commitées : comptes stake/vote, autorités, lockup, délégation, vote state, retraits et dépôt de rewards. Les données complètes ne sont jamais recopiées : seules les clés, autorités, offsets, tailles, hashes, préfixes et paramètres bornés disponibles sont conservés. + +Le retrait d'un nonce account conserve une sémantique conditionnelle : le runtime ferme le compte uniquement lorsque la totalité du solde est retirée. La projection décrit l'opération commitée et ne prétend pas reconstruire l'état final transactionnel du compte. Les deltas SOL restent la responsabilité du graphe core. + +Une preuve ZK n'est jamais matérialisée. Seul le lifecycle du compte de contexte est projeté lorsqu'il existe. Un rapport Slashing conserve le signal de violation et son lifecycle, mais pas le contenu du compte de preuve externe ; `penaltyAppliedByProgram` reste faux car le programme ne retire pas lui-même du stake. L'ancien ZK Token Proof Program reste en decode/audit, car le runtime actuel est sans effet et aucun historique mainnet fiable n'a été observé. + +## Matérialisations spécialisées à prévoir + +- `nft` : NFT classiques, programmables et compressés. +- `metadata` : metadata Metaplex, creators, collection, update authority, URI et symbol. +- `oracle` : prix, publisher, confidence, staleness et sources d'oracle. +- `lending` : borrow, repay, collateral, liquidation et health factor. +- `staking` avancé : snapshots de comptes Stake/Vote avant/après, activation par epoch, crédits Vote cumulés et réconciliation avec sysvars. +- `governance` : proposal, vote, execute et update de realm/config. +- `bridge` : lock, mint wrapped asset, burn, redeem et message verification. +- `perpetuals` : position, funding, liquidation, collateral et PnL. +- `vault` : share mint/burn, deposit, withdraw, rebalance et fee collection. +- `routing` : route, route leg, quote, slippage, aggregator hop. +- `compliance_audit` : événements d'audit non directement matérialisables en trading. +- `token_metadata_risk` : signaux de risque issus des métadonnées de tokens, par exemple autorité mutable, creators suspects, symbol/URI incohérents ou changements de metadata. + +## Projections natives encore à terminer avant clôture + +Toutes les sémantiques matérialisables ne sont pas encore projetées. Les lots restants sont explicites : + +- Compute Budget : profil transactionnel agrégé regroupant toutes les instructions Compute Budget du message. + +Les précompiles de signature et les preuves ZK sans contexte restent des observations d'audit déjà suffisantes. Une projection supplémentaire n'est justifiée que si un consommateur `compliance_audit` exige une table dédiée. L'ancien ZK Token Proof no-op reste sans matérialisation. + +## Règle de conception + +Une matérialisation spécialisée ne doit être ajoutée que si la table générique ne suffit pas pour représenter correctement la sémantique du protocole. Le modèle commun reste prioritaire, puis les tables spécialisées complètent ce modèle. Chaque projection doit posséder une identité stable, un état cible explicite, une politique d'idempotence et une règle documentée pour les transactions échouées. diff --git a/migration/khadhroony-bot2-reference/docs/METAPLEX_TOKEN_METADATA_MATRIX.json b/migration/khadhroony-bot2-reference/docs/METAPLEX_TOKEN_METADATA_MATRIX.json new file mode 100644 index 0000000..ecd2123 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/METAPLEX_TOKEN_METADATA_MATRIX.json @@ -0,0 +1,1612 @@ +{ + "schema_version": 2, + "milestone": "0.4.7-pre.025", + "surface_code": "metadata_metaplex_token_metadata", + "program_id": "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s", + "program_family": "independent_metaplex_foundation_program", + "decode_status": "instruction_inventory_exhaustive_0_through_57_and_account_key_inventory_exhaustive_0_through_14", + "runtime_registration": "not_enabled_in_pre_008", + "official_sources": [ + { + "kind": "repository", + "value": "metaplex-foundation/mpl-token-metadata", + "ref": "main", + "status": "verified_2026-07-20" + }, + { + "kind": "rust_sdk", + "value": "mpl-token-metadata", + "version_requirement": "^5.1", + "observed_release": "5.1.1" + }, + { + "kind": "idl", + "value": "idls/token_metadata.json", + "idl_version": "1.14.0", + "sha": "5df4a24f62c2743125be096cc174680790c92c18" + }, + { + "kind": "generated_instruction_inventory", + "value": "clients/rust/src/generated/instructions/mod.rs", + "sha": "3c42eec629f82e44ba690a7c4bd2177cc78f1d49" + } + ], + "wire_contract": { + "instruction_discriminator": "u8", + "argument_encoding": "borsh", + "unknown_discriminator": "unsupported", + "truncated_payload": "failed", + "non_canonical_suffix": "pending_per_variant", + "foreign_program_id": "incompatible" + }, + "bounds": { + "max_instruction_bytes": 65536, + "max_accounts": 256, + "max_string_bytes": 4096, + "max_uri_bytes": 4096, + "max_creators": 64, + "max_authorization_payload_bytes": 65536, + "max_diagnostics": 64, + "instruction_payload_bytes_max": 16384, + "instruction_accounts_max": 64, + "name_bytes_max": 32, + "symbol_bytes_max": 10, + "uri_bytes_max": 200, + "creators_max": 5, + "seller_fee_basis_points_max": 10000 + }, + "current_sdk_instruction_modules": [ + "approve_collection_authority", + "approve_use_authority", + "bubblegum_set_collection_size", + "burn", + "burn_edition_nft", + "burn_nft", + "burn_v1", + "close_accounts", + "close_escrow_account", + "collect", + "convert_master_edition_v1_to_v2", + "create", + "create_escrow_account", + "create_master_edition_v3", + "create_metadata_account_v3", + "create_v1", + "delegate", + "delegate_authority_item_v1", + "delegate_collection_item_v1", + "delegate_collection_v1", + "delegate_data_item_v1", + "delegate_data_v1", + "delegate_locked_transfer_v1", + "delegate_print_delegate_v1", + "delegate_programmable_config_item_v1", + "delegate_programmable_config_v1", + "delegate_sale_v1", + "delegate_staking_v1", + "delegate_standard_v1", + "delegate_transfer_v1", + "delegate_utility_v1", + "deprecated_mint_new_edition_from_master_edition_via_printing_token", + "freeze_delegated_account", + "lock", + "lock_v1", + "migrate", + "mint", + "mint_new_edition_from_master_edition_via_token", + "mint_new_edition_from_master_edition_via_vault_proxy", + "mint_v1", + "print", + "print_v1", + "print_v2", + "puff_metadata", + "remove_creator_verification", + "resize", + "revoke", + "revoke_authority_item_v1", + "revoke_collection_authority", + "revoke_collection_item_v1", + "revoke_collection_v1", + "revoke_data_item_v1", + "revoke_data_v1", + "revoke_locked_transfer_v1", + "revoke_migration_v1", + "revoke_print_delegate_v1", + "revoke_programmable_config_item_v1", + "revoke_programmable_config_v1", + "revoke_sale_v1", + "revoke_staking_v1", + "revoke_standard_v1", + "revoke_transfer_v1", + "revoke_use_authority", + "revoke_utility_v1", + "set_and_verify_collection", + "set_and_verify_sized_collection_item", + "set_collection_size", + "set_token_standard", + "sign_metadata", + "thaw_delegated_account", + "transfer", + "transfer_out_of_escrow", + "transfer_v1", + "unlock", + "unlock_v1", + "unverify", + "unverify_collection", + "unverify_collection_v1", + "unverify_creator_v1", + "unverify_sized_collection_item", + "update", + "update_as_authority_item_delegate_v2", + "update_as_collection_delegate_v2", + "update_as_collection_item_delegate_v2", + "update_as_data_delegate_v2", + "update_as_data_item_delegate_v2", + "update_as_programmable_config_delegate_v2", + "update_as_programmable_config_item_delegate_v2", + "update_as_update_authority_v2", + "update_metadata_account_v2", + "update_primary_sale_happened_via_token", + "update_v1", + "use", + "use_v1", + "utilize", + "verify", + "verify_collection", + "verify_collection_v1", + "verify_creator_v1", + "verify_sized_collection_item" + ], + "historical_idl": { + "instruction_count": 58, + "discriminator_range": "0..57", + "status": "names_accounts_arguments_and_legacy_optional_rules_pending_exhaustive_extraction" + }, + "pda_families": [ + { + "code": "metadata", + "seed_shape": [ + "metadata", + "program_id", + "mint" + ], + "status": "implemented_pre_025" + }, + { + "code": "edition_or_master_edition", + "seed_shape": [ + "metadata", + "program_id", + "mint", + "edition" + ], + "status": "implemented_pre_026" + }, + { + "code": "edition_marker", + "status": "implemented_pre_027" + }, + { + "code": "collection_authority_record", + "status": "implemented_pre_029" + }, + { + "code": "use_authority_record", + "status": "implemented_pre_029" + }, + { + "code": "token_record", + "status": "implemented_pre_028" + }, + { + "code": "metadata_delegate_record", + "status": "implemented_pre_029", + "seeds": [ + "metadata", + "program_id", + "mint", + "metadata_delegate_role_seed", + "update_authority", + "delegate" + ] + }, + { + "code": "token_owned_escrow", + "status": "implemented_pre_030", + "token_owner_seeds": [ + "metadata", + "program_id", + "base_token", + "0", + "escrow" + ], + "creator_seeds": [ + "metadata", + "program_id", + "base_token", + "1", + "creator", + "escrow" + ] + }, + { + "code": "reservation_list_historical", + "status": "implemented_pre_031" + }, + { + "code": "holder_delegate_record", + "status": "implemented_pre_029", + "seeds": [ + "metadata", + "program_id", + "mint", + "holder_delegate_role_seed", + "owner", + "delegate" + ] + } + ], + "account_layouts": [ + { + "name": "Metadata", + "status": "implemented_pre_025" + }, + { + "name": "MasterEditionV1", + "status": "implemented_pre_026_historical_decode_only" + }, + { + "name": "MasterEditionV2", + "status": "implemented_pre_026" + }, + { + "name": "Edition", + "status": "implemented_pre_026" + }, + { + "name": "EditionMarker", + "status": "implemented_pre_027" + }, + { + "name": "EditionMarkerV2", + "status": "implemented_pre_027_experimental" + }, + { + "name": "TokenRecord", + "status": "implemented_pre_028" + }, + { + "name": "MetadataDelegateRecord", + "status": "implemented_pre_029" + }, + { + "name": "HolderDelegateRecord", + "status": "implemented_pre_029" + }, + { + "name": "CollectionAuthorityRecord", + "status": "implemented_pre_029" + }, + { + "name": "UseAuthorityRecord", + "status": "implemented_pre_029" + }, + { + "name": "TokenOwnedEscrow", + "status": "implemented_pre_030" + }, + { + "name": "ReservationListV1V2", + "status": "implemented_pre_031_decode_only", + "max_entries": 200, + "v1_max_bytes": 6949, + "v2_max_bytes": 9749, + "pda_seeds": [ + "metadata", + "program_id", + "master_edition", + "reservation", + "resource" + ], + "execution": "historical_never_execute" + } + ], + "provenance_boundaries": [ + "metaplex_token_metadata", + "token_2022_embedded_metadata", + "metaplex_core", + "metaplex_bubblegum" + ], + "external_json": { + "enabled": false, + "canonical_replay_dependency": false, + "status": "contract_reserved", + "ssrf_policy": "required_before_activation" + }, + "execution": { + "enabled": false, + "reason": "builders_accounts_preconditions_and_postconditions_not_yet_exhaustively_proved" + }, + "next_proofs": [ + "materialize Metadata facts with one canonical owner per fact and no duplication", + "materialize historical account state with explicit lifecycle and provenance" + ], + "implemented_instruction_groups": [ + { + "pre_release": "0.4.7-pre.002", + "group": "metadata_create_update_v2_v3", + "instructions": [ + { + "name": "UpdateMetadataAccountV2", + "code": "update_metadata_account_v2", + "discriminator": 15, + "status": "current", + "decode": "implemented", + "materialize": "pending", + "execute": "pending" + }, + { + "name": "CreateMetadataAccountV3", + "code": "create_metadata_account_v3", + "discriminator": 33, + "status": "current", + "decode": "implemented", + "materialize": "pending", + "execute": "pending" + } + ], + "wire": "official_borsh_mpl_token_metadata_5_1_1", + "runtime_registration": "pending_full_group_validation" + }, + { + "pre_release": "0.4.7-pre.003", + "group": "collection_verification_legacy_current", + "instructions": [ + { + "name": "VerifyCollection", + "code": "verify_collection", + "discriminator": 18, + "status": "current", + "decode": "implemented", + "materialize": "pending", + "execute": "pending" + }, + { + "name": "UnverifyCollection", + "code": "unverify_collection", + "discriminator": 22, + "status": "current", + "decode": "implemented", + "materialize": "pending", + "execute": "pending" + }, + { + "name": "SetAndVerifyCollection", + "code": "set_and_verify_collection", + "discriminator": 25, + "status": "current", + "decode": "implemented", + "materialize": "pending", + "execute": "pending" + } + ], + "wire": "single_u8_discriminator_official_mpl_token_metadata_5_1_1", + "account_contract": "ordered_required_accounts_plus_optional_collection_authority_record_and_bounded_remaining_accounts", + "runtime_registration": "pending_broader_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.004", + "group": "sized_collection_item_verification", + "instructions": [ + { + "name": "VerifySizedCollectionItem", + "code": "verify_sized_collection_item", + "discriminator": 30, + "status": "current", + "decode": "implemented", + "materialize": "pending", + "execute": "pending" + }, + { + "name": "UnverifySizedCollectionItem", + "code": "unverify_sized_collection_item", + "discriminator": 31, + "status": "current", + "decode": "implemented", + "materialize": "pending", + "execute": "pending" + }, + { + "name": "SetAndVerifySizedCollectionItem", + "code": "set_and_verify_sized_collection_item", + "discriminator": 32, + "status": "current", + "decode": "implemented", + "materialize": "pending", + "execute": "pending" + } + ], + "wire": "single_u8_discriminator_official_mpl_token_metadata_5_1_1", + "account_contract": "ordered_required_accounts_plus_optional_collection_authority_record; collection metadata is writable and collection authority is readonly signer", + "runtime_registration": "pending_broader_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.005", + "group": "collection_authority_administration", + "instructions": [ + { + "name": "ApproveCollectionAuthority", + "code": "approve_collection_authority", + "discriminator": 23, + "status": "current", + "wire": "u8 discriminator only", + "required_accounts": 7, + "optional_accounts": [ + "rent" + ], + "defaulted_accounts": [ + "system_program" + ], + "execution_policy": "future_current_surface" + }, + { + "name": "RevokeCollectionAuthority", + "code": "revoke_collection_authority", + "discriminator": 24, + "status": "current", + "wire": "u8 discriminator only", + "required_accounts": 5, + "optional_accounts": [], + "execution_policy": "future_current_surface" + } + ], + "provenance": "mpl-token-metadata 5.1.1 generated builders", + "materialization": "reserved_for_admin_and_lifecycle_pre_release" + }, + { + "pre_release": "0.4.7-pre.006", + "group": "use_authority_and_utilization", + "instructions": [ + { + "name": "Utilize", + "code": "utilize", + "discriminator": 19, + "status": "current", + "wire": "u8 discriminator + u64 number_of_uses", + "required_accounts": 9, + "optional_accounts": [ + "use_authority_record", + "burner" + ], + "execution_policy": "future_current_surface" + }, + { + "name": "ApproveUseAuthority", + "code": "approve_use_authority", + "discriminator": 20, + "status": "current", + "wire": "u8 discriminator + u64 number_of_uses", + "required_accounts": 10, + "optional_accounts": [ + "rent" + ], + "defaulted_accounts": [ + "token_program", + "system_program" + ], + "execution_policy": "future_current_surface" + }, + { + "name": "RevokeUseAuthority", + "code": "revoke_use_authority", + "discriminator": 21, + "status": "current", + "wire": "u8 discriminator only", + "required_accounts": 8, + "optional_accounts": [ + "rent" + ], + "defaulted_accounts": [ + "token_program", + "system_program" + ], + "execution_policy": "future_current_surface" + } + ], + "provenance": "mpl-token-metadata 5.1.1 generated builders", + "materialization": "reserved_for_metadata_admin_lifecycle_and_risk_audit" + }, + { + "pre_release": "0.4.7-pre.007", + "group": "creator_signature_and_primary_sale", + "instructions": [ + { + "name": "UpdatePrimarySaleHappenedViaToken", + "code": "update_primary_sale_happened_via_token", + "discriminator": 4, + "status": "current", + "wire": "u8 discriminator only", + "required_accounts": 3, + "execution_policy": "future_current_surface" + }, + { + "name": "SignMetadata", + "code": "sign_metadata", + "discriminator": 7, + "status": "current", + "wire": "u8 discriminator only", + "required_accounts": 2, + "execution_policy": "future_current_surface" + } + ], + "provenance": "mpl-token-metadata 5.1.1 generated builders", + "materialization": "reserved_for_metadata_and_creator_state_pre_release" + }, + { + "pre_release": "0.4.7-pre.008", + "group": "metadata_normalization_and_creator_unverification", + "instructions": [ + { + "name": "PuffMetadata", + "code": "puff_metadata", + "discriminator": 14, + "status": "current", + "wire": "u8 discriminator only", + "required_accounts": 1, + "execution_policy": "future_current_surface" + }, + { + "name": "RemoveCreatorVerification", + "code": "remove_creator_verification", + "discriminator": 28, + "status": "current", + "wire": "u8 discriminator only", + "required_accounts": 2, + "execution_policy": "future_current_surface" + }, + { + "name": "SetTokenStandard", + "code": "set_token_standard", + "discriminator": 35, + "status": "current", + "wire": "u8 discriminator only", + "required_accounts": 3, + "optional_accounts": [ + "edition" + ], + "execution_policy": "future_current_surface" + } + ], + "provenance": "mpl-token-metadata 5.1.1 generated builders", + "materialization": "reserved_for_metadata_creator_and_asset_classification_pre_release" + }, + { + "pre_release": "0.4.7-pre.009", + "group": "historical_metadata_create_v2", + "instructions": [ + { + "name": "CreateMetadataAccountV2", + "code": "create_metadata_account_v2", + "discriminator": 16, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + } + ], + "wire": "u8 discriminator + historical DataV2 + bool is_mutable using Borsh 0.10", + "account_contract": "metadata, mint, mint_authority signer, payer writable signer, update_authority, system_program, optional rent", + "provenance": "historical Metaplex Token Metadata instruction surface; current SDK DataV2 type reused only for exact compatible wire fields", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.010", + "group": "historical_metadata_v1_create_update", + "instructions": [ + { + "name": "CreateMetadataAccount", + "code": "create_metadata_account", + "discriminator": 0, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + }, + { + "name": "UpdateMetadataAccount", + "code": "update_metadata_account", + "discriminator": 1, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + } + ], + "wire": "u8 discriminator + historical Data/Option fields using Borsh 0.10", + "provenance": "historical generated Metaplex Token Metadata surface", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.011", + "group": "master_edition_creation_and_v1_conversion", + "instructions": [ + { + "name": "CreateMasterEditionV3", + "code": "create_master_edition_v3", + "discriminator": 17, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "ConvertMasterEditionV1ToV2", + "code": "convert_master_edition_v1_to_v2", + "discriminator": 12, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + } + ], + "wire": "CreateMasterEditionV3: u8 discriminator + Option max_supply; conversion: u8 discriminator only", + "account_contract": "exact generated Rust SDK account order and signer/writable flags; optional rent accepted for CreateMasterEditionV3", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.012", + "group": "printed_edition_mint_token_and_vault_proxy", + "instructions": [ + { + "name": "MintNewEditionFromMasterEditionViaToken", + "code": "mint_new_edition_from_master_edition_via_token", + "discriminator": 11, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "MintNewEditionFromMasterEditionViaVaultProxy", + "code": "mint_new_edition_from_master_edition_via_vault_proxy", + "discriminator": 13, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + } + ], + "wire": "u8 discriminator + MintNewEditionFromMasterEditionViaTokenArgs { edition: u64 }", + "account_contract": "exact generated Rust SDK account order and signer/writable flags; optional rent accepted after the required program accounts", + "edition_marker_contract": "edition marker PDA is writable and corresponds to floor(edition / EDITION_MARKER_BIT_SIZE); PDA derivation validation remains planned with account decoding", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.013", + "group": "collection_size_and_bubblegum_bridge", + "instructions": [ + { + "name": "SetCollectionSize", + "code": "set_collection_size", + "discriminator": 34, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "BubblegumSetCollectionSize", + "code": "bubblegum_set_collection_size", + "discriminator": 36, + "status": "bridge_current", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_until_bubblegum_boundary_proof" + } + ], + "wire": "u8 discriminator + SetCollectionSizeArgs { size: u64 }", + "account_contract": "exact generated Rust SDK account order and signer/writable flags; optional collection authority record accepted", + "provenance_boundary": "BubblegumSetCollectionSize is decoded as a Token Metadata bridge instruction and does not claim complete Bubblegum coverage", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.014", + "group": "programmable_delegate_and_revoke", + "instructions": [ + { + "name": "Delegate", + "code": "delegate", + "discriminator": 44, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_and_authorization_rules_proof" + }, + { + "name": "Revoke", + "code": "revoke", + "discriminator": 45, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_and_authorization_rules_proof" + } + ], + "wire": "u8 discriminator + DelegateArgs or RevokeArgs Borsh enum; DelegateArgs may contain optional AuthorizationData", + "account_contract": "14 positional accounts with Metaplex program placeholders for absent optional accounts; exact signer/writable invariants on authority, payer, metadata and optional writable records", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.015", + "group": "programmable_transfer_lock_unlock_burn_close", + "instructions": [ + { + "name": "Burn", + "code": "burn", + "discriminator": 41, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "Lock", + "code": "lock", + "discriminator": 46, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "Unlock", + "code": "unlock", + "discriminator": 47, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "Transfer", + "code": "transfer", + "discriminator": 49, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "CloseAccounts", + "code": "close_accounts", + "discriminator": 57, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + } + ], + "wire": "Borsh enums TransferArgs/LockArgs/UnlockArgs/BurnArgs; CloseAccounts discriminator only", + "account_contract": "exact generated Rust SDK positional accounts and signer/writable invariants; Metaplex program placeholders accepted for absent optional programmable accounts", + "runtime_registration": "pending_full_decoder_and_account_validation" + }, + { + "pre_release": "0.4.7-pre.016", + "group": "legacy_delegated_freeze_thaw_and_inventory_audit", + "instructions": [ + { + "name": "FreezeDelegatedAccount", + "code": "freeze_delegated_account", + "discriminator": 26, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + }, + { + "name": "ThawDelegatedAccount", + "code": "thaw_delegated_account", + "discriminator": 27, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + } + ], + "wire": "u8 discriminator only", + "account_contract": "delegate writable signer, token account writable, edition/mint/token program readonly", + "inventory_audit": "official generated Rust SDK instruction module index audited; remaining modules are tracked before account-state decoding", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.017", + "group": "legacy_nft_burn_and_printing_token_mint", + "instructions": [ + { + "name": "BurnNft", + "code": "burn_nft", + "discriminator": 29, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + }, + { + "name": "BurnEditionNft", + "code": "burn_edition_nft", + "discriminator": 37, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + }, + { + "name": "DeprecatedMintNewEditionFromMasterEditionViaPrintingToken", + "code": "deprecated_mint_new_edition_from_master_edition_via_printing_token", + "discriminator": 3, + "status": "deprecated_historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden_historical" + } + ], + "wire": "u8 discriminator only", + "account_contract": "exact generated Rust SDK positional signer/writable contracts; BurnNft collection metadata, deprecated printing-token reservation list are optional trailing accounts", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.018", + "group": "escrow_transfer_and_collect", + "instructions": [ + { + "name": "CreateEscrowAccount", + "code": "create_escrow_account", + "discriminator": 38, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "CloseEscrowAccount", + "code": "close_escrow_account", + "discriminator": 39, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "TransferOutOfEscrow", + "code": "transfer_out_of_escrow", + "discriminator": 40, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "Collect", + "code": "collect", + "discriminator": 54, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_authority_proof" + } + ], + "wire": "Create/Close/Collect discriminator only; TransferOutOfEscrow discriminator + amount u64", + "account_contract": "exact generated Rust SDK signer/writable contracts with optional trailing authority on create and transfer", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.019", + "group": "modern_create_wrapper", + "instructions": [ + { + "name": "Create", + "code": "create", + "discriminator": 42, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + } + ], + "wire": "u8 discriminator + CreateArgs enum", + "account_contract": "nine positional accounts with Metaplex placeholders for optional master edition and SPL Token Program", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.020", + "group": "modern_mint_wrapper", + "instructions": [ + { + "name": "Mint", + "code": "mint", + "discriminator": 43, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + } + ], + "wire": "u8 discriminator + MintArgs::V1 { amount: u64, authorization_data: Option }", + "account_contract": "fifteen positional accounts with Metaplex placeholders for optional token owner, master edition, token record, delegate record and authorization-rule accounts", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.021", + "group": "modern_print_wrapper", + "instructions": [ + { + "name": "Print", + "code": "print", + "discriminator": 55, + "variants": [ + "V1", + "V2" + ], + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + } + ], + "wire": "u8 discriminator + PrintArgs::{V1,V2} { edition: u64 }", + "account_contract": "eighteen positional accounts with a Metaplex placeholder or writable Token Record at position 6", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.022", + "group": "modern_update_wrapper", + "instructions": [ + { + "name": "Update", + "code": "update", + "discriminator": 50, + "variants": [ + "V1", + "AsUpdateAuthorityV2", + "AsAuthorityItemDelegateV2", + "AsCollectionDelegateV2", + "AsDataDelegateV2", + "AsProgrammableConfigDelegateV2", + "AsDataItemDelegateV2", + "AsCollectionItemDelegateV2", + "AsProgrammableConfigItemDelegateV2" + ], + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + } + ], + "wire": "u8 discriminator + UpdateArgs enum", + "account_contract": "eleven positional accounts with Metaplex placeholders for optional delegate record, token, edition and authorization-rule accounts", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.023", + "group": "modern_use_verify_unverify_wrappers", + "instructions": [ + { + "name": "Use", + "code": "use", + "discriminator": 51, + "variants": [ + "V1" + ], + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "Verify", + "code": "verify", + "discriminator": 52, + "variants": [ + "CreatorV1", + "CollectionV1" + ], + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "Unverify", + "code": "unverify", + "discriminator": 53, + "variants": [ + "CreatorV1", + "CollectionV1" + ], + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + } + ], + "wire": "u8 discriminator + UseArgs or VerificationArgs enum", + "account_contract": "twelve positional accounts for Use; eight positional accounts for Verify/Unverify, with Metaplex placeholders for optional accounts", + "runtime_registration": "pending_full_decoder_validation" + }, + { + "pre_release": "0.4.7-pre.024", + "group": "final_instruction_inventory_resize_migrate_and_legacy_gaps", + "instructions": [ + { + "name": "DeprecatedCreateMasterEdition", + "code": "deprecated_create_master_edition", + "discriminator": 2, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden" + }, + { + "name": "DeprecatedSetReservationList", + "code": "deprecated_set_reservation_list", + "discriminator": 5, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden" + }, + { + "name": "DeprecatedCreateReservationList", + "code": "deprecated_create_reservation_list", + "discriminator": 6, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden" + }, + { + "name": "DeprecatedMintPrintingTokensViaToken", + "code": "deprecated_mint_printing_tokens_via_token", + "discriminator": 8, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden" + }, + { + "name": "DeprecatedMintPrintingTokens", + "code": "deprecated_mint_printing_tokens", + "discriminator": 9, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden" + }, + { + "name": "CreateMasterEdition", + "code": "create_master_edition", + "discriminator": 10, + "status": "historical", + "decode": "implemented", + "materialize": "planned", + "execute": "forbidden" + }, + { + "name": "Migrate", + "code": "migrate", + "discriminator": 48, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + }, + { + "name": "Resize", + "code": "resize", + "discriminator": 56, + "status": "current", + "decode": "implemented", + "materialize": "planned", + "execute": "planned_after_stateful_proof" + } + ], + "wire": "u8 discriminator only", + "account_contract": "exact IDL/generated positional signer and writable flags", + "runtime_registration": "pending_full_decoder_validation", + "inventory_proof": { + "covered_discriminators": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 21, + 22, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + 41, + 42, + 43, + 44, + 45, + 46, + 47, + 48, + 49, + 50, + 51, + 52, + 53, + 54, + 55, + 56, + 57 + ], + "missing_discriminators": [], + "coverage_entries": 58 + } + } + ], + "implemented_account_groups": [ + { + "group": "metadata_account_v1", + "layouts": [ + "MetadataV1" + ], + "owner_validation": "exact_metaplex_token_metadata_program", + "pda_seeds": [ + "metadata", + "program_id", + "mint" + ], + "bounds": { + "account_bytes": 1024, + "name_bytes": 32, + "symbol_bytes": 10, + "uri_bytes": 200, + "creators": 5 + }, + "runtime_registration": "not_enabled" + }, + { + "group": "edition_and_master_edition_accounts", + "status": "implemented_pre_026", + "layouts": [ + "DeprecatedMasterEditionV1", + "MasterEditionV2", + "EditionV1" + ], + "owner": "mpl_token_metadata_program", + "pda_seeds": [ + "metadata", + "program_id", + "mint", + "edition" + ], + "bounds": { + "max_account_bytes": 128, + "edition_v1_exact_bytes": 41 + }, + "policy": { + "DeprecatedMasterEditionV1": "historical_decode_only", + "MasterEditionV2": "active_state", + "EditionV1": "active_state" + } + }, + { + "group": "edition_marker_accounts", + "status": "implemented_pre_027", + "layouts": [ + "EditionMarker", + "EditionMarkerV2" + ], + "owner": "mpl_token_metadata_program", + "pda_seeds": { + "EditionMarker": [ + "metadata", + "program_id", + "mint", + "edition", + "edition_div_248_decimal" + ], + "EditionMarkerV2": [ + "metadata", + "program_id", + "mint", + "edition", + "marker" + ] + }, + "bitmap": { + "EditionMarker": { + "ledger_bytes": 31, + "editions_per_group": 248, + "bit_order": "most_significant_bit_first" + }, + "EditionMarkerV2": { + "ledger": "borsh_vec_u8", + "max_decoder_ledger_bytes": 1048576, + "bit_order": "most_significant_bit_first" + } + }, + "runtime_registration": "not_enabled" + }, + { + "group": "token_record_account", + "status": "implemented_pre_028", + "layouts": [ + "TokenRecord" + ], + "owner": "mpl_token_metadata_program", + "length": 80, + "pda_seeds": [ + "metadata", + "program_id", + "mint", + "token_record", + "token_account" + ], + "fields": [ + "bump", + "state", + "rule_set_revision", + "delegate", + "delegate_role", + "locked_transfer" + ], + "token_states": [ + "Unlocked", + "Locked", + "Listed" + ], + "delegate_roles": [ + "Sale", + "Transfer", + "Utility", + "Staking", + "Standard", + "LockedTransfer", + "Migration" + ], + "runtime_registration": "not_enabled" + }, + { + "group": "delegate_and_authority_records", + "status": "implemented_pre_029", + "layouts": [ + "MetadataDelegateRecord", + "HolderDelegateRecord", + "CollectionAuthorityRecord", + "UseAuthorityRecord" + ], + "owner": "mpl_token_metadata_program", + "lengths": { + "MetadataDelegateRecord": 98, + "HolderDelegateRecord": 98, + "CollectionAuthorityRecord": "3_or_35", + "UseAuthorityRecord": 10 + }, + "pda_seeds": { + "MetadataDelegateRecord": [ + "metadata", + "program_id", + "mint", + "metadata_delegate_role_seed", + "update_authority", + "delegate" + ], + "HolderDelegateRecord": [ + "metadata", + "program_id", + "mint", + "holder_delegate_role_seed", + "owner", + "delegate" + ], + "CollectionAuthorityRecord": [ + "metadata", + "program_id", + "mint", + "collection_authority", + "authority" + ], + "UseAuthorityRecord": [ + "metadata", + "program_id", + "mint", + "user", + "authority" + ] + }, + "metadata_delegate_role_seeds": [ + "authority_item_delegate", + "collection_delegate", + "use_delegate", + "data_delegate", + "programmable_config_delegate", + "data_item_delegate", + "collection_item_delegate", + "prog_config_item_delegate" + ], + "holder_delegate_role_seeds": [ + "print_delegate" + ], + "runtime_registration": "not_enabled" + }, + { + "group": "token_owned_escrow_account", + "status": "implemented_pre_030", + "layouts": [ + "TokenOwnedEscrow" + ], + "owner": "mpl_token_metadata_program", + "lengths": { + "TokenOwner": 35, + "Creator": 67 + }, + "fields": [ + "base_token", + "authority", + "bump" + ], + "pda_seeds": { + "TokenOwner": [ + "metadata", + "program_id", + "base_token", + "0", + "escrow" + ], + "Creator": [ + "metadata", + "program_id", + "base_token", + "1", + "creator", + "escrow" + ] + }, + "stateful_instruction_effects": { + "CreateEscrowAccount": "creates canonical escrow snapshot", + "CloseEscrowAccount": "closes canonical escrow snapshot", + "TransferOutOfEscrow": "changes escrow-held attribute state without changing account identity", + "Migrate": "may create or update related programmable records; correlation pending", + "Resize": "changes allocated account bytes without changing canonical identity" + }, + "runtime_registration": "not_enabled" + }, + { + "group": "historical_reservation_lists", + "status": "implemented_pre_031_decode_only", + "layouts": [ + "ReservationListV1", + "ReservationListV2" + ], + "max_entries": 200, + "padding_policy": "zero_only" + }, + { + "release": "0.4.7-pre.032", + "name": "exhaustive_account_key_audit", + "status": "implemented", + "published_discriminants": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14 + ], + "missing": [], + "future_unknown_policy": "fail_closed" + } + ], + "account_key_inventory": [ + { + "key": "Uninitialized", + "discriminant": 0, + "decoder": "reject_uninitialized", + "lifecycle": "sentinel" + }, + { + "key": "EditionV1", + "discriminant": 1, + "decoder": "decode_edition_account", + "lifecycle": "active" + }, + { + "key": "MasterEditionV1", + "discriminant": 2, + "decoder": "decode_edition_account", + "lifecycle": "historical_materializable" + }, + { + "key": "ReservationListV1", + "discriminant": 3, + "decoder": "decode_reservation_list_account", + "lifecycle": "historical_materializable" + }, + { + "key": "MetadataV1", + "discriminant": 4, + "decoder": "decode_metadata_account", + "lifecycle": "active" + }, + { + "key": "ReservationListV2", + "discriminant": 5, + "decoder": "decode_reservation_list_account", + "lifecycle": "historical_materializable" + }, + { + "key": "MasterEditionV2", + "discriminant": 6, + "decoder": "decode_edition_account", + "lifecycle": "active" + }, + { + "key": "EditionMarker", + "discriminant": 7, + "decoder": "decode_edition_marker_account", + "lifecycle": "active" + }, + { + "key": "UseAuthorityRecord", + "discriminant": 8, + "decoder": "decode_use_authority_record_account", + "lifecycle": "active" + }, + { + "key": "CollectionAuthorityRecord", + "discriminant": 9, + "decoder": "decode_collection_authority_record_account", + "lifecycle": "active" + }, + { + "key": "TokenOwnedEscrow", + "discriminant": 10, + "decoder": "decode_token_owned_escrow_account", + "lifecycle": "active" + }, + { + "key": "TokenRecord", + "discriminant": 11, + "decoder": "decode_token_record_account", + "lifecycle": "active" + }, + { + "key": "MetadataDelegate", + "discriminant": 12, + "decoder": "decode_metadata_delegate_record_account", + "lifecycle": "active" + }, + { + "key": "EditionMarkerV2", + "discriminant": 13, + "decoder": "decode_edition_marker_account", + "lifecycle": "active_experimental" + }, + { + "key": "HolderDelegate", + "discriminant": 14, + "decoder": "decode_holder_delegate_record_account", + "lifecycle": "active" + } + ], + "account_inventory_proof": { + "published_key_discriminants": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10, + 11, + 12, + 13, + 14 + ], + "missing_discriminants": [], + "published_variants": 15, + "decoded_layout_variants": 14, + "explicit_sentinel_rejections": 1, + "unknown_future_policy": "fail_closed_until_inventory_and_decoder_are_updated", + "status": "implemented_pre_032" + }, + "canonical_account_model": { + "status": "implemented_pre_033", + "canonical_version": 1, + "stable_identity": "program_id:account_address", + "lifecycle_classes": [ + "active", + "active_experimental", + "historical" + ], + "materialization_policies": { + "active": "active_state", + "active_experimental": "active_state", + "historical": "historical_state" + }, + "execution_policies": { + "active": "current_candidate", + "active_experimental": "experimental_candidate", + "historical": "never_historical" + }, + "provenance_fields": [ + "source", + "slot", + "write_version", + "signature", + "instruction_index", + "inner_instruction_index", + "committed" + ], + "observation_sources": [ + "live", + "replay", + "backfill", + "repair", + "synthetic" + ], + "historical_policy": "historical layouts remain materializable during backfill/replay but are never executable" + }, + "materialization_policy": { + "decoder_rule": "decode every identifiable historical/current/experimental published wire and account layout", + "materializer_rule": "materialize every stable proven fact including historical/deprecated state with explicit lifecycle and provenance", + "executor_rule": "execute current or experimental callable operations only; historical and obsolete operations are never executable", + "state_authority": "owned account snapshots are authoritative for final state; instruction observations do not duplicate the same state", + "implemented_pre_034": [ + "MetadataV1 canonical active-state projection", + "creator/collection/uses/token-standard/programmable-config bounded payload retention", + "committed provenance requirement", + "stable account-based output identity" + ] + }, + "next_proof": "edition, marker, delegate, token-record, escrow and historical-state materialization without duplicates" +} diff --git a/migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_CLOSURE_AUDIT.md b/migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_CLOSURE_AUDIT.md new file mode 100644 index 0000000..746998f --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_CLOSURE_AUDIT.md @@ -0,0 +1,113 @@ + + + +# Audit de clôture Solana natif `0.4.1` + +## Objet + +Ce document fixe l’état final du jalon `0.4.1` : couverture native, preuves synthétiques, corpus mainnet observés et limites assumées. + +L’absence de transaction mainnet n’autorise ni à supprimer une surface officielle, ni à prétendre qu’elle a été observée. Elle impose une validation synthétique documentée et une tentative de découverte bornée lorsque la surface est officiellement déclarée. + +## Surfaces couvertes + +`kb_decoder_solana_core` couvre 18 surfaces exécutables natives ou assimilées : + +1. System Program ; +2. Vote Program ; +3. Stake Program ; +4. Config Program ; +5. Compute Budget Program ; +6. Address Lookup Table Program ; +7. ZK ElGamal Proof Program ; +8. Feature Program ; +9. ancien ZK Token Proof Program ; +10. Native Loader ; +11. BPF Loader v1 ; +12. BPF Loader v2 ; +13. BPF Loader Upgradeable ; +14. Loader v4 ; +15. Ed25519 precompile ; +16. secp256k1 precompile ; +17. secp256r1 precompile ; +18. Slashing Program. + +`StakeConfig11111111111111111111111111111111` est volontairement classé comme compte natif historique non exécutable. + +## Divergence Slashing corrigée + +Agave `v4.1.1` déclare le programme stateless Slashing sous : + +```text +S1ashing11111111111111111111111111111111111 +``` + +La surface a été ajoutée dans `pre.018` : deux instructions exactes, dispatch sans fallback, couverture déclarée et projection de rapport. Aucun corpus public observable n’a été trouvé ; la validation repose donc sur les sources officielles et les fixtures synthétiques. + +## Invariants automatiques + +Les tests garantissent que : + +- chaque entrée de `kb_program_ids::native_program_ids()` possède exactement une surface de décodeur ; +- chaque surface possède au moins une déclaration de couverture ; +- aucune déclaration n’utilise le fallback `unclassified_native_instruction` ; +- chaque déclaration référence une surface réellement déclarée ; +- les identités programme/surface/entrée sont uniques ; +- le Slashing Program est dispatché vers son parseur exact ; +- ZK Token Proof historique passe par le fallback actuel no-op sans prétendre vérifier une preuve. + +## Corpus et replays validés + +| Programme | Résultat de clôture | +|---------------------------|------------------------------------------------------------------------------| +| System | 817 inputs décodés, 258 sorties, 52 refus attendus sur transactions échouées | +| Config | 96 inputs décodés, 96 sorties admin | +| Stake | 448 inputs décodés, 447 sorties, 1 refus attendu | +| Vote | 600 inputs décodés, 600 sorties | +| Compute Budget | 2 746 inputs décodés, 1 603 profils transactionnels | +| Address Lookup Table | 104 inputs décodés, 104 sorties lifecycle | +| ZK ElGamal | 151 inputs décodés, 139 sorties de contexte | +| Slashing | zéro corpus public observable, validation synthétique | +| Feature | zéro corpus observable dans les campagnes adressées, validation synthétique | +| Loaders rares | zéro corpus observable pour certaines générations, validation synthétique | +| ZK Token Proof historique | zéro corpus public observable, runtime actuel no-op, validation synthétique | + +Les campagnes de clôture rapportées ont `unmatched = 0`, `failedInputs = 0`, `unsupported = 0` et ne produisent pas d’erreurs opérationnelles dans les journaux fournis. + +## Backfills + +Aucun backfill supplémentaire n’est requis pour clôturer `0.4.1`. + +Le mode `program_latest` ajouté dans `pre.019` reste disponible pour de futures recherches sans signature d’ancrage. Il ne doit pas être utilisé pour inventer un corpus absent sur des surfaces feature-gated ou historiquement rares. + +## Matérialisations + +La clôture instructionnelle est documentée dans `docs/NATIVE_SOLANA_MATERIALIZATION_AUDIT.md`. + +Aucune projection native stable immédiatement dérivable du graphe core actuel n’est identifiée comme manquante. Les snapshots finaux de comptes et métriques runtime restent différés. + +## Validations finales + +Validations utilisateur finales rapportées pour la clôture : + +```text +kb_program_ids 5 tests +kb_decoder_solana_core 114 tests +kb_materializer_lifecycle 16 tests +kb_materializer_admin 7 tests +kb_materializer_compliance_audit 7 tests +kb_materializer_staking 8 tests +kb_config 36 tests +kb_pipeline 45 tests +kb_store_pg 45 tests avec PostgreSQL réel +kb_app_demo 78 tests +cargo clippy --all-targets propre +``` + +Le démarrage Tauri validé après `pre.021-fix.001` confirme les 53 routes de logging et l’initialisation PostgreSQL. `pre.022` ne modifie pas la configuration Tauri. + +## Décision + +`0.4.1` est clôturée. + +La prochaine session doit utiliser `prompts/022_v0_4_2_executor_solana_core.md` et démarrer `0.4.2` sur l’infrastructure d’exécution et `kb_executor_solana_core`. diff --git a/migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_DECODER_MATRIX.json b/migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_DECODER_MATRIX.json new file mode 100644 index 0000000..b34fe57 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_DECODER_MATRIX.json @@ -0,0 +1,155 @@ +{ + "file": "docs/NATIVE_SOLANA_DECODER_MATRIX.json", + "version": 1, + "release": "0.4.2-pre.023", + "surface_count": 18, + "coverage_entry_count": 121, + "registry_contract": "Exact equality with kb_program_ids::native_program_ids and SolanaCoreDecoder::surfaces", + "completeness_contract": "Each surface has exact declared coverage count plus a dedicated official-enum or audited-wire completeness test", + "surfaces": [ + { + "surface_code": "solana_native_address_lookup_table", + "program_id": "AddressLookupTab1e1111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 5, + "source_contract": "solana-address-lookup-table-interface@3.1.x official instruction enum", + "completeness_test": "address_lookup_table::tests::coverage_uses_official_encoder_discriminants" + }, + { + "surface_code": "solana_native_bpf_loader_deprecated", + "program_id": "BPFLoader1111111111111111111111111111111111", + "runtime_status": "historical", + "expected_coverage_entries": 2, + "source_contract": "solana-loader-v2-interface@3.0.0 immutable write/finalize layout", + "completeness_test": "loaders::tests::coverage_declares_every_loader_entry" + }, + { + "surface_code": "solana_native_bpf_loader", + "program_id": "BPFLoader2111111111111111111111111111111111", + "runtime_status": "historical", + "expected_coverage_entries": 2, + "source_contract": "solana-loader-v2-interface@3.0.0 immutable write/finalize layout", + "completeness_test": "loaders::tests::coverage_declares_every_loader_entry" + }, + { + "surface_code": "solana_native_bpf_loader_upgradeable", + "program_id": "BPFLoaderUpgradeab1e11111111111111111111111", + "runtime_status": "current_with_historical_variants", + "expected_coverage_entries": 8, + "source_contract": "solana-loader-v3-interface@8.0.1 with wincode exact wire layout", + "completeness_test": "loaders::tests::coverage_declares_every_loader_entry" + }, + { + "surface_code": "solana_native_compute_budget", + "program_id": "ComputeBudget111111111111111111111111111111", + "runtime_status": "current_with_historical_variants", + "expected_coverage_entries": 6, + "source_contract": "solana-compute-budget-interface@3.0.0 plus historical RequestUnitsDeprecated", + "completeness_test": "compute_budget::tests::coverage_declares_current_and_historical_entries" + }, + { + "surface_code": "solana_native_config", + "program_id": "Config1111111111111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 1, + "source_contract": "solana-config-interface@2.0.0 generic bounded store contract", + "completeness_test": "config::tests::coverage_declares_one_generic_store_surface" + }, + { + "surface_code": "solana_native_ed25519", + "program_id": "Ed25519SigVerify111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 1, + "source_contract": "Agave Ed25519 precompile exact offset-table layout", + "completeness_test": "precompiles::tests::coverage_declares_exactly_three_signature_precompile_surfaces" + }, + { + "surface_code": "solana_native_feature", + "program_id": "Feature111111111111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 1, + "source_contract": "solana-feature-gate-interface revoke-pending-activation instruction contract", + "completeness_test": "feature::tests::coverage_declares_the_single_official_instruction" + }, + { + "surface_code": "solana_native_loader_v4", + "program_id": "LoaderV411111111111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 7, + "source_contract": "solana-loader-v4-interface@3.1.0 official instruction enum", + "completeness_test": "loaders::tests::coverage_declares_every_loader_entry" + }, + { + "surface_code": "solana_native_loader", + "program_id": "NativeLoader1111111111111111111111111111111", + "runtime_status": "runtime_dispatch", + "expected_coverage_entries": 1, + "source_contract": "Agave native-loader runtime direct-invocation behavior", + "completeness_test": "loaders::tests::coverage_declares_every_loader_entry" + }, + { + "surface_code": "solana_native_secp256k1", + "program_id": "KeccakSecp256k11111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 1, + "source_contract": "Agave secp256k1 precompile exact offset-table layout", + "completeness_test": "precompiles::tests::coverage_declares_exactly_three_signature_precompile_surfaces" + }, + { + "surface_code": "solana_native_secp256r1", + "program_id": "Secp256r1SigVerify1111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 1, + "source_contract": "Agave secp256r1 precompile exact offset-table layout", + "completeness_test": "precompiles::tests::coverage_declares_exactly_three_signature_precompile_surfaces" + }, + { + "surface_code": "solana_native_slashing", + "program_id": "S1ashing11111111111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 2, + "source_contract": "SIMD-0204 and Agave Slashing builtin instruction contract", + "completeness_test": "slashing::tests::coverage_declares_exactly_two_official_instructions" + }, + { + "surface_code": "solana_native_stake", + "program_id": "Stake11111111111111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 18, + "source_contract": "solana-stake-interface@4.3.x official StakeInstruction variants", + "completeness_test": "stake::tests::coverage_declares_every_official_stake_instruction" + }, + { + "surface_code": "solana_native_system", + "program_id": "11111111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 14, + "source_contract": "solana-system-interface@3.2.x official SystemInstruction variants", + "completeness_test": "system::tests::coverage_declares_all_current_system_variants" + }, + { + "surface_code": "solana_native_vote", + "program_id": "Vote111111111111111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 20, + "source_contract": "solana-vote-interface@6.0.x official VoteInstruction variants", + "completeness_test": "vote::tests::coverage_declares_every_official_vote_instruction" + }, + { + "surface_code": "solana_native_zk_elgamal_proof", + "program_id": "ZkE1Gama1Proof11111111111111111111111111111", + "runtime_status": "current", + "expected_coverage_entries": 13, + "source_contract": "solana-zk-elgamal-proof-interface official ProofInstruction variants", + "completeness_test": "zk_elgamal::tests::coverage_declares_all_thirteen_official_instructions" + }, + { + "surface_code": "solana_native_zk_token_proof", + "program_id": "ZkTokenProof1111111111111111111111111111111", + "runtime_status": "historical_with_current_noop", + "expected_coverage_entries": 18, + "source_contract": "audited historical ZK Token Proof wire table plus current runtime no-op behavior", + "completeness_test": "zk_token_proof::tests::coverage_declares_seventeen_historical_entries_and_current_noop_fallback" + } + ] +} diff --git a/migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_EXECUTION_MATRIX.json b/migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_EXECUTION_MATRIX.json new file mode 100644 index 0000000..1aabe9f --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_EXECUTION_MATRIX.json @@ -0,0 +1,1279 @@ +{ + "file": "docs/NATIVE_SOLANA_EXECUTION_MATRIX.json", + "version": 1, + "release": "0.4.2-pre.022", + "surface_count": 18, + "callable_operation_count": 109, + "surfaces": [ + { + "surface": "system", + "program_ids": [ + "11111111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.system.transfer", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.transfer_many", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.create_account", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.create_account_with_seed", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.allocate", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.allocate_with_seed", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.assign", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.assign_with_seed", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.transfer_with_seed", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.create_account_allow_prefund", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.nonce.create", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.nonce.create_with_seed", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.nonce.initialize", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.nonce.advance", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.nonce.authorize", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.nonce.withdraw", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.system.nonce.upgrade", + "construction_source": "solana-system-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "spend and signer ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "devnet transfer path validated", + "ui_exposure": "safe_subset" + } + ] + }, + { + "surface": "compute_budget", + "program_ids": [ + "ComputeBudget111111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.compute_budget.request_heap_frame", + "construction_source": "solana-compute-budget-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "resource ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "offline_only", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.compute_budget.set_compute_unit_limit", + "construction_source": "solana-compute-budget-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "resource ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "offline_only", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.compute_budget.set_compute_unit_price", + "construction_source": "solana-compute-budget-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "resource ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "offline_only", + "ui_exposure": "safe_subset" + }, + { + "operation_code": "solana_core.compute_budget.set_loaded_accounts_data_size_limit", + "construction_source": "solana-compute-budget-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "none", + "policy": "resource ceilings", + "unit_test_contract": "builder::tests", + "cluster_validation": "offline_only", + "ui_exposure": "safe_subset" + } + ] + }, + { + "surface": "address_lookup_table", + "program_ids": [ + "AddressLookupTab1e1111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.address_lookup_table.create", + "construction_source": "solana-address-lookup-table-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "required", + "policy": "authority, capacity, rent and cooldown", + "unit_test_contract": "address_lookup_table::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.address_lookup_table.extend", + "construction_source": "solana-address-lookup-table-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "required", + "policy": "authority, capacity, rent and cooldown", + "unit_test_contract": "address_lookup_table::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.address_lookup_table.freeze", + "construction_source": "solana-address-lookup-table-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "required", + "policy": "authority, capacity, rent and cooldown", + "unit_test_contract": "address_lookup_table::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.address_lookup_table.deactivate", + "construction_source": "solana-address-lookup-table-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "required", + "policy": "authority, capacity, rent and cooldown", + "unit_test_contract": "address_lookup_table::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.address_lookup_table.close", + "construction_source": "solana-address-lookup-table-interface official builders", + "construction_kind": "official_builder", + "stateful_preflight": "required", + "policy": "authority, capacity, rent and cooldown", + "unit_test_contract": "address_lookup_table::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "ed25519", + "program_ids": [ + "Ed25519SigVerify111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.ed25519.verify_inline", + "construction_source": "solana-ed25519-program official layout", + "construction_kind": "official_layout", + "stateful_preflight": "none", + "policy": "bounded offsets and payload", + "unit_test_contract": "precompiles::tests", + "cluster_validation": "offline_only", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.ed25519.verify_offsets", + "construction_source": "solana-ed25519-program official layout", + "construction_kind": "official_layout", + "stateful_preflight": "none", + "policy": "bounded offsets and payload", + "unit_test_contract": "precompiles::tests", + "cluster_validation": "offline_only", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "secp256k1", + "program_ids": [ + "KeccakSecp256k11111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.secp256k1.verify_inline", + "construction_source": "solana-secp256k1-program official layout", + "construction_kind": "official_layout", + "stateful_preflight": "none", + "policy": "index zero and bounded offsets", + "unit_test_contract": "precompiles::tests", + "cluster_validation": "offline_only", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.secp256k1.verify_offsets", + "construction_source": "solana-secp256k1-program official layout", + "construction_kind": "official_layout", + "stateful_preflight": "none", + "policy": "index zero and bounded offsets", + "unit_test_contract": "precompiles::tests", + "cluster_validation": "offline_only", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "secp256r1", + "program_ids": [ + "Secp256r1SigVerify1111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.secp256r1.verify_inline", + "construction_source": "solana-secp256r1-program official layout", + "construction_kind": "official_layout", + "stateful_preflight": "none", + "policy": "low-S and bounded offsets", + "unit_test_contract": "precompiles::tests", + "cluster_validation": "offline_only", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.secp256r1.verify_offsets", + "construction_source": "solana-secp256r1-program official layout", + "construction_kind": "official_layout", + "stateful_preflight": "none", + "policy": "low-S and bounded offsets", + "unit_test_contract": "precompiles::tests", + "cluster_validation": "offline_only", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "config", + "program_ids": [ + "Config1111111111111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.config.create_account", + "construction_source": "solana-config-interface wire contract", + "construction_kind": "official_wire", + "stateful_preflight": "required", + "policy": "owner, allocation and rent", + "unit_test_contract": "native_admin::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.config.store", + "construction_source": "solana-config-interface wire contract", + "construction_kind": "official_wire", + "stateful_preflight": "required", + "policy": "owner, allocation and rent", + "unit_test_contract": "native_admin::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "feature", + "program_ids": [ + "Feature111111111111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.feature.activate", + "construction_source": "solana-feature-gate-interface official sequences", + "construction_kind": "official_builder", + "stateful_preflight": "required", + "policy": "pending state and rent", + "unit_test_contract": "native_admin::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.feature.revoke_pending_activation", + "construction_source": "solana-feature-gate-interface official sequences", + "construction_kind": "official_builder", + "stateful_preflight": "required", + "policy": "pending state and rent", + "unit_test_contract": "native_admin::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "slashing", + "program_ids": [ + "S1ashing11111111111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.slashing.close_violation_report", + "construction_source": "solana-slashing-program interface and SIMD-0204", + "construction_kind": "official_wire", + "stateful_preflight": "required", + "policy": "proof shape, epoch window and retention", + "unit_test_contract": "slashing::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.slashing.submit_duplicate_block_proof", + "construction_source": "solana-slashing-program interface and SIMD-0204", + "construction_kind": "official_wire", + "stateful_preflight": "required", + "policy": "proof shape, epoch window and retention", + "unit_test_contract": "slashing::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "zk_elgamal", + "program_ids": [ + "ZkE1Gama1Proof11111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.zk_elgamal.verify_inline", + "construction_source": "solana-zk-elgamal-proof-interface POD contract", + "construction_kind": "official_wire", + "stateful_preflight": "required", + "policy": "proof size and context owner/space/rent", + "unit_test_contract": "zk_elgamal::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.zk_elgamal.verify_from_account", + "construction_source": "solana-zk-elgamal-proof-interface POD contract", + "construction_kind": "official_wire", + "stateful_preflight": "required", + "policy": "proof size and context owner/space/rent", + "unit_test_contract": "zk_elgamal::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.zk_elgamal.close_context_state", + "construction_source": "solana-zk-elgamal-proof-interface POD contract", + "construction_kind": "official_wire", + "stateful_preflight": "required", + "policy": "proof size and context owner/space/rent", + "unit_test_contract": "zk_elgamal::tests", + "cluster_validation": "localnet_devnet_preflight", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "stake", + "program_ids": [ + "Stake11111111111111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.stake.initialize", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.initialize_checked", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.create_account", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.create_account_with_seed", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.create_account_checked", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.create_account_with_seed_checked", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.split", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.split_with_seed", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.merge", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.create_account_and_delegate", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.create_account_with_seed_and_delegate", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.authorize", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.authorize_checked", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.authorize_with_seed", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.authorize_checked_with_seed", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.delegate", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.withdraw", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.deactivate", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.set_lockup", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.set_lockup_checked", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.get_minimum_delegation", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.deactivate_delinquent", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.move_stake", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.stake.move_lamports", + "construction_source": "solana-stake-interface official helpers and wincode wire", + "construction_kind": "official_builder_or_wire", + "stateful_preflight": "future", + "policy": "authority, epoch, lockup and rent", + "unit_test_contract": "stake::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "vote", + "program_ids": [ + "Vote111111111111111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.vote.initialize_account", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.initialize_account_v2", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.create_account", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.create_account_with_seed", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.create_account_v2", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.create_account_with_seed_v2", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.authorize", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.authorize_checked", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.authorize_with_seed", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.authorize_checked_with_seed", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.vote", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.vote_switch", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.withdraw", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.update_validator_identity", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.update_commission", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.update_vote_state", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.update_vote_state_switch", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.compact_update_vote_state", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.compact_update_vote_state_switch", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.tower_sync", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.tower_sync_switch", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.update_commission_collector", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.update_commission_bps", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.vote.deposit_delegator_rewards", + "construction_source": "solana-vote-interface official wire via wincode", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "authority, state version and tower admissibility", + "unit_test_contract": "vote::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "loader_v3", + "program_ids": [ + "BPFLoaderUpgradeab1e11111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.loader_v3.create_buffer", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.write", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.deploy_with_max_program_len", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.upgrade", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.set_buffer_authority", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.set_buffer_authority_checked", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.set_upgrade_authority", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.set_upgrade_authority_checked", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.close", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.close_any", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v3.extend_program", + "construction_source": "solana-loader-v3-interface official helpers and wincode", + "construction_kind": "official_builder", + "stateful_preflight": "future", + "policy": "owner, authority, PDA, state and rent", + "unit_test_contract": "loader_v3::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "loader_v4", + "program_ids": [ + "LoaderV411111111111111111111111111111111111" + ], + "execution_status": "callable", + "classification": "native_runtime_program_or_precompile", + "operations": [ + { + "operation_code": "solana_core.loader_v4.create_buffer", + "construction_source": "solana-loader-v4-interface official discriminants and accounts", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "owner, authority, lifecycle and rent", + "unit_test_contract": "loader_v4::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v4.set_program_length", + "construction_source": "solana-loader-v4-interface official discriminants and accounts", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "owner, authority, lifecycle and rent", + "unit_test_contract": "loader_v4::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v4.write", + "construction_source": "solana-loader-v4-interface official discriminants and accounts", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "owner, authority, lifecycle and rent", + "unit_test_contract": "loader_v4::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v4.copy", + "construction_source": "solana-loader-v4-interface official discriminants and accounts", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "owner, authority, lifecycle and rent", + "unit_test_contract": "loader_v4::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v4.deploy", + "construction_source": "solana-loader-v4-interface official discriminants and accounts", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "owner, authority, lifecycle and rent", + "unit_test_contract": "loader_v4::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v4.deploy_from_source", + "construction_source": "solana-loader-v4-interface official discriminants and accounts", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "owner, authority, lifecycle and rent", + "unit_test_contract": "loader_v4::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v4.retract", + "construction_source": "solana-loader-v4-interface official discriminants and accounts", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "owner, authority, lifecycle and rent", + "unit_test_contract": "loader_v4::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v4.transfer_authority", + "construction_source": "solana-loader-v4-interface official discriminants and accounts", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "owner, authority, lifecycle and rent", + "unit_test_contract": "loader_v4::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + }, + { + "operation_code": "solana_core.loader_v4.finalize", + "construction_source": "solana-loader-v4-interface official discriminants and accounts", + "construction_kind": "official_wire", + "stateful_preflight": "future", + "policy": "owner, authority, lifecycle and rent", + "unit_test_contract": "loader_v4::tests", + "cluster_validation": "future_stateful_validation", + "ui_exposure": "hidden" + } + ] + }, + { + "surface": "bpf_loader_v1", + "program_ids": [ + "BPFLoader1111111111111111111111111111111111" + ], + "execution_status": "non_invocable", + "classification": "historical_decode_only", + "operations": [], + "official_reason": "Deprecated immutable loader generation retained for historical decoding; no current client builder is exposed." + }, + { + "surface": "bpf_loader_v2", + "program_ids": [ + "BPFLoader2111111111111111111111111111111111" + ], + "execution_status": "non_invocable", + "classification": "historical_decode_only", + "operations": [], + "official_reason": "Immutable loader generation retained for historical decoding; deployment administration moved to newer loader interfaces." + }, + { + "surface": "native_loader", + "program_ids": [ + "NativeLoader1111111111111111111111111111111" + ], + "execution_status": "non_invocable", + "classification": "validator_software_loader", + "operations": [], + "official_reason": "Native Loader loads validator-native programs and has no standalone client instruction contract." + }, + { + "surface": "zk_token_proof_historical", + "program_ids": [ + "ZkTokenProof1111111111111111111111111111111" + ], + "execution_status": "non_invocable", + "classification": "historical_decode_only", + "operations": [], + "official_reason": "Historical ZK Token Proof instructions are retained for decoding; the current runtime surface is a no-op stub and no modern builder is invented." + } + ] +} diff --git a/migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_MATERIALIZATION_AUDIT.md b/migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_MATERIALIZATION_AUDIT.md new file mode 100644 index 0000000..5e161e6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_MATERIALIZATION_AUDIT.md @@ -0,0 +1,85 @@ + + + +# Audit des matérialisations Solana natives + +## Conclusion + +`0.4.1` clôt les matérialisations natives instructionnelles stables. Les projections actives couvrent les mutations ou profils qui peuvent être déduits de façon déterministe depuis le graphe core transactionnel et les événements décodés. + +Les sorties ne sont pas des snapshots finaux de comptes ou de runtime. Les états finaux qui exigent l’état précédent/suivant d’un compte, les sysvars historiques ou des métriques runtime restent différés jusqu’à enrichissement explicite du contrat core. + +## Projections actives par matérialiseur + +| Matérialiseur | Surfaces | Projection | +|------------------------------------|----------------------|---------------------------------------------------------------------------------------------------------------------| +| `kb_materializer_lifecycle` | Address Lookup Table | lifecycle create/freeze/extend/deactivate/close | +| `kb_materializer_lifecycle` | Feature | révocation de feature gate | +| `kb_materializer_lifecycle` | Loaders | déploiement, finalisation et transitions lifecycle stables | +| `kb_materializer_lifecycle` | System | durable nonce, create account et allocate | +| `kb_materializer_lifecycle` | ZK ElGamal | création ou fermeture d’un compte de contexte | +| `kb_materializer_lifecycle` | Slashing | initialisation ou fermeture d’un rapport de violation | +| `kb_materializer_admin` | System | assign et assign with seed | +| `kb_materializer_admin` | Config | écriture opaque bornée avec clés, taille, SHA-256 et préfixe | +| `kb_materializer_admin` | Loaders | changements d’autorité | +| `kb_materializer_compliance_audit` | Loaders | écritures/copies de bytecode bornées par offsets, tailles, SHA-256 et préfixes | +| `kb_materializer_compliance_audit` | Compute Budget | profil transactionnel agrégé, last-write-wins, une seule sortie par transaction | +| `kb_materializer_staking` | Stake | compte, délégation, désactivation, split, merge, withdraw, move stake/lamports, autorités et lockup instructionnels | +| `kb_materializer_staking` | Vote | compte, autorités, identité, commission, vote state, retraits et dépôt de rewards instructionnels | + +## Politique des transactions échouées + +Les transactions on-chain échouées restent décodables comme intentions ou diagnostics. Les matérialiseurs mutables utilisent la politique `SuccessfulCommittedOnly` et refusent les observations non commitées au lieu de produire une mutation réussie fictive. + +Le profil Compute Budget est une sortie `ComplianceAudit`, pas une mutation d’état ; il peut donc être matérialisé même lorsque la transaction échoue. Il décrit le budget demandé par les instructions, pas les unités réellement consommées. + +## Absences volontaires + +Les surfaces suivantes ne produisent pas de projection dédiée dans `kb_sol_mat_events` à la clôture de `0.4.1` : + +- Ed25519 ; +- secp256k1 ; +- secp256r1 ; +- preuves ZK sans compte de contexte ; +- ancien ZK Token Proof historique/no-op. + +Leur décodage est déjà un événement d’audit borné. Une matérialisation supplémentaire serait une duplication sans consommateur métier dédié. + +## États finaux différés + +Les éléments suivants ne doivent pas être présentés comme disponibles dans `0.4.1` : + +- état final exact d’un compte Stake ou Vote ; +- activation/désactivation Stake par epoch ; +- crédits Vote cumulés ; +- rewards recalculées par epoch ; +- état final d’un compte System après lamport drain conditionnel ; +- bytecode final complet d’un loader ; +- unités compute réellement consommées ; +- frais finaux réellement facturés ; +- résultat cryptographique recalculé pour signature ou preuve ZK. + +Ces états exigent une extension du contexte core ou un audit de comptes/snapshots dédié. + +## Validations finales `0.4.1` + +Les validations utilisateur finales couvrent : + +| Surface | Corpus / preuve | Résultat | +|----------------------|--------------------------------|----------------------------------------------------------------| +| System | replay ciblé | 817 inputs décodés, 258 sorties, 52 refus attendus | +| Config | replay ciblé | 96 inputs décodés, 96 sorties admin | +| Stake | replay ciblé | 448 inputs décodés, 447 sorties, 1 refus attendu | +| Vote | replay ciblé | 600 inputs décodés, 600 sorties | +| Compute Budget | replay ciblé | 2 746 inputs décodés, 1 603 profils | +| ZK ElGamal | replay ciblé | 151 inputs décodés, 139 sorties de contexte | +| Address Lookup Table | inventaire/replay ciblé | 104 inputs décodés, 104 sorties lifecycle | +| Slashing | sources officielles + fixtures | aucun corpus public observable, validation synthétique assumée | + +Toutes les campagnes de clôture rapportées se terminent avec `unmatched = 0`, `failedInputs = 0`, `unsupported = 0` et des journaux `error.jsonl` vides. + +## Décision de clôture + +Aucune projection native instructionnelle stable n’est identifiée comme manquante dans le périmètre `0.4.1`. + +La suite `0.4.2` peut se concentrer sur l’exécution : plans, simulation, signature, envoi et validation post-exécution, sans rouvrir le jalon de décodage/matérialisation natif. diff --git a/migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_PROGRAMS.md b/migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_PROGRAMS.md new file mode 100644 index 0000000..0a9e2ad --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/NATIVE_SOLANA_PROGRAMS.md @@ -0,0 +1,233 @@ + + + +# Inventaire et couverture des programmes Solana natifs + +## Périmètre + +Le registre `kb_program_ids::native_program_ids()` contient dix-huit surfaces exécutables : programmes runtime natifs, loaders, précompiles et programmes historiques encore observables. Les sysvars et comptes natifs non exécutables restent suivis séparément dans `registry/core_program_id_seed.toml`. + +Memo ne fait pas partie de ce périmètre. Les IDs officiels v1 `Memo1U...`, v3 `MemoSq...` et v4 `Memo4c...` sont réservés dans `kb_decoder_spl_memo`. + +## État du phasage + +| Phase | Contenu | État | +|-------|---------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------| +| A | modèle d’événement, lecture bornée, validation des comptes et diagnostics | implémenté dans `0.4.1-pre.001` | +| B | System Program et Compute Budget | implémenté, replay mainnet validé dans `pre.005` | +| C | Address Lookup Table et loaders | implémenté dans `pre.003` puis `pre.006` | +| D | Vote, Stake, Config et programmes historiques | implémenté : Config/Feature `pre.008`, Vote `pre.010` validé dans `pre.011`, Stake `pre.013` validé mainnet | +| E | précompiles et preuves ZK | précompiles signature `pre.014`, ZK ElGamal Proof `pre.015`, ancien ZK Token Proof `pre.016` | +| F | matérialisations justifiées, diagnostics globaux et clôture | nonce System/contextes ZK `pre.017`; Slashing et invariants de couverture `pre.018`; corpus final à valider | + +Le processor conserve l’identité `solana_native_classifier@0.4.1` pendant les deltas préparatoires. Les nouveaux décodeurs nécessitent donc un force replay ciblé ou global ; la version ne sera relevée qu’avec un changement explicite du contrat de processor. + +## Sources normatives couvertes + +| Surface | Source officielle | Sérialisation retenue | Couverture | +|---------------------------|---------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------|-----------------------------------------| +| System Program | `solana-system-interface 3.2.0`, enum `SystemInstruction` | schéma `wincode` officiel compatible avec le format bincode historique, lecture exacte sans octets suffixes | 14 variantes | +| Compute Budget actuel | `solana-compute-budget-interface 3.0.0` + runtime `solana-compute-budget-instruction 4.1.1` | tag `u8` puis entier little-endian/Borsh ; désérialisation runtime unchecked, suffixe accepté et audité | 5 entrées, dont le tag réservé `Unused` | +| Compute Budget historique | ancien `solana-sdk`, `RequestUnitsDeprecated` | tag `0`, `u32 units`, `u32 additional_fee` | 1 variante historique | +| Address Lookup Table | `solana-address-lookup-table-interface 3.1.0`, enum `ProgramInstruction` | schéma `wincode` officiel, lecture exacte | 5 variantes | +| BPF Loader immuable | `solana-loader-v2-interface 3.0.0`, enum `LoaderInstruction` | tag `u32 LE`, offset `u32`, longueur de vector `u64`, lecture exacte | write, finalize sur deux générations | +| BPF Loader upgradeable | `solana-loader-v3-interface 8.0.1`, enum `UpgradeableLoaderInstruction` | wire format historique compatible bincode, champs booléens suffixes avec valeurs historiques documentées | 8 variantes | +| Loader v4 | `solana-loader-v4-interface 3.1.0`, enum `LoaderV4Instruction` | tag et entiers little-endian, tailles exactes | 7 variantes | +| Config Program | `solana-config-interface 2.0.0` et processor `solana-config-program 2.2.20` | `ConfigKeys` en `solana_short_vec`, clés `(Pubkey, bool)`, puis payload typé opaque pour le décodeur générique | 1 écriture générique `store` | +| Feature Gate | `solana-feature-gate-interface 4.0.0`, `FeatureGateInstruction` | tag `u8` exact `0`, sans suffixe | 1 variante | +| Vote Program | `solana-vote-interface ^6.0`, enum `VoteInstruction` | schéma officiel `wincode`, discriminant historique `u32 LE`, lecture exacte sans octets suffixes | 20 variantes | +| Stake Program | `solana-stake-interface ^4.3`, enum `StakeInstruction` | miroir wire privé `wincode` exact du layout historique ; types sémantiques officiels consommés | 18 variantes | +| ZK ElGamal Proof | `solana-zk-elgamal-proof-interface ^0.1`, `ProofInstruction`, types `Pod`, runtime Agave | tag `u8`; preuve inline de taille officielle ou référence compte `u32 LE` sur instruction exacte de 5 octets | 13 variantes | +| ZK Token Proof historique | miroir wire local audité contre `solana-zk-token-sdk 3.1.14`, Agave `v2.0.0`/`v4.1.1` | tag `u8`; layout historique inline ou offset compte sur 5 octets ; fallback runtime actuel sans effet | 17 variantes + fallback no-op | +| Slashing stateless | Agave `v4.1.1` + `solana-program/slashing@fe8da3a` | tag `u8`; fermeture sur 1 octet ou preuve duplicate block sur 305 octets exacts | 2 variantes | + +Les versions de crates sont utilisées comme références reproductibles. Un futur changement d’interface doit entraîner un changement de version du décodeur et un replay ledger/version/hash. + +## System Program + +Program ID : `11111111111111111111111111111111`. + +| Tag historique compatible bincode/wincode `u32 LE` | Entry code | Paramètres structurés | Comptes principaux | État | +|---------------------------------------------------:|--------------------------------|------------------------------------|----------------------------------------|--------| +| 0 | `create_account` | lamports, space, owner | funding, new account | décodé | +| 1 | `assign` | owner | assigned account | décodé | +| 2 | `transfer` | lamports | funding, recipient | décodé | +| 3 | `create_account_with_seed` | base, seed, lamports, space, owner | funding, new account, base optionnelle | décodé | +| 4 | `advance_nonce_account` | aucun | nonce, recent blockhashes, authority | décodé | +| 5 | `withdraw_nonce_account` | lamports | nonce, recipient, sysvars, authority | décodé | +| 6 | `initialize_nonce_account` | authority | nonce, recent blockhashes, rent | décodé | +| 7 | `authorize_nonce_account` | authority | nonce, authority | décodé | +| 8 | `allocate` | space | allocated account | décodé | +| 9 | `allocate_with_seed` | base, seed, space, owner | derived account, base | décodé | +| 10 | `assign_with_seed` | base, seed, owner | derived account, base | décodé | +| 11 | `transfer_with_seed` | lamports, from seed, from owner | funding, base, recipient | décodé | +| 12 | `upgrade_nonce_account` | aucun | nonce account | décodé | +| 13 | `create_account_allow_prefund` | lamports, space, owner | new account, funding conditionnel | décodé | + +Le décodeur utilise le schéma `wincode` dérivé directement par `solana-system-interface 3.2.0`. `wincode::deserialize_exact` conserve la compatibilité octet pour octet avec le format historique de cette enum et refuse les octets suffixes. Le crate n’utilise aucune dépendance directe à `bincode`. + +`pre.017` matérialise les cinq opérations durable nonce sous `durable_nonce_account::0`. Initialize, advance, authorize et upgrade conservent leur transition explicite. Withdraw conserve le montant demandé et la règle officielle de fermeture conditionnelle : le compte n’est détruit que lorsque la totalité de son solde est retirée. La projection ne prétend pas reconstruire l’état final transactionnel du compte et ne duplique pas les deltas SOL du graphe core. + +Les flags signer/writable sont conservés sous deux formes : privilèges attendus par le contrat officiel et privilèges réellement résolus depuis core. Ils ne sont pas confondus, car une CPI signée par PDA ne se reflète pas nécessairement comme signer statique de la transaction. + +## Compute Budget + +Program ID : `ComputeBudget111111111111111111111111111111`. + +| Tag `u8` | Entry code | Taille décodée minimale | Paramètres | Statut | +|---------:|---------------------------------------|------------------------:|--------------------------|-----------------------| +| 0 | `unused_reserved` | 1 | aucun | `ignored` | +| 0 | `request_units_deprecated` | 9 | units, additional fee | `decoded`, historique | +| 1 | `request_heap_frame` | 5 | bytes `u32` | `decoded` | +| 2 | `set_compute_unit_limit` | 5 | compute unit limit `u32` | `decoded` | +| 3 | `set_compute_unit_price` | 9 | micro-lamports `u64` | `decoded` | +| 4 | `set_loaded_accounts_data_size_limit` | 5 | bytes `u32` | `decoded` | + +Le runtime Agave traite les variantes modernes avec `solana_borsh::v1::try_from_slice_unchecked`. Les octets suffixes sont donc acceptés après les 5 ou 9 octets utiles. Le décodeur reproduit cette règle et conserve `trailingDataByteLength`, `trailingDataSha256`, `trailingDataPrefixHex` et `trailingDataSemantics = ignored_by_runtime_borsh_unchecked`. Les payloads trop courts restent `failed`. Le tag `0` demeure dispatché par taille afin de distinguer le tag réservé actuel de la variante historique à neuf octets ; toute autre taille pour ce tag reste `failed`. Un tag inconnu est `unsupported` avec diagnostic borné et hash du payload disponible pour un replay futur. + + +## Address Lookup Table + +Program ID : `AddressLookupTab1e1111111111111111111111111`. + +| Tag `u32 LE` | Entry code | Paramètres | Comptes | État | +|-------------:|---------------------------|------------------------------------------------------------------|--------------------------------------------------|--------| +| 0 | `create_lookup_table` | recent slot, bump seed, politique historique du signer authority | table, authority, payer, System Program | décodé | +| 1 | `freeze_lookup_table` | aucun | table, authority | décodé | +| 2 | `extend_lookup_table` | adresses ajoutées, count, présence du financement | table, authority, paire optionnelle payer/System | décodé | +| 3 | `deactivate_lookup_table` | aucun | table, authority | décodé | +| 4 | `close_lookup_table` | aucun | table, authority, recipient | décodé | + +Le décodeur refuse une paire optionnelle d’extension incomplète et les octets suffixes. La création conserve `expectedSigner = null` pour l’autorité, car l’encodeur officiel actuel ne la marque plus signer tandis que les runtimes historiques antérieurs à v1.12 l’exigeaient. + +La projection `solana_native_lifecycle` produit une sortie `address_lookup_table::0` uniquement si l’observation est exacte, réussie et commitée. Les intentions issues de transactions échouées restent dans decode et sont refusées avant matérialisation. + +## Programmes runtime natifs principaux + +| Code canonique | Program ID | État au début de `pre.018` | +|--------------------|-----------------------------------------------|--------------------------------------------------------------------------------| +| `vote` | `Vote111111111111111111111111111111111111111` | 20 variantes décodées ; 500 `tower_sync` validés mainnet | +| `stake` | `Stake11111111111111111111111111111111111111` | 18 variantes décodées ; 339 instructions mainnet sur neuf entry codes observés | +| `config` | `Config1111111111111111111111111111111111111` | écriture générique `store` décodée ; corpus ciblé à inventorier | +| `zk_elgamal_proof` | `ZkE1Gama1Proof11111111111111111111111111111` | 13 variantes ; 151 instructions mainnet et 139 contextes matérialisés | + +## Loaders + +| Surface | Program ID | Variantes | Statut `pre.006` | +|------------------------|-----------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------|---------------------------------------------| +| Native Loader | `NativeLoader1111111111111111111111111111111` | invocation directe opaque | `ignored`, historique, aucun faux événement | +| BPF Loader deprecated | `BPFLoader1111111111111111111111111111111111` | write, finalize | décodé | +| BPF Loader v2 | `BPFLoader2111111111111111111111111111111111` | write, finalize | décodé | +| BPF Loader upgradeable | `BPFLoaderUpgradeab1e11111111111111111111111` | initialize buffer, write, deploy with max data length, upgrade, set authority, close, extend program, set authority checked | décodé | +| Loader v4 | `LoaderV411111111111111111111111111111111111` | write, copy, set program length, deploy, retract, transfer authority, finalize | décodé | + +Les payloads `write` ne recopient pas le bytecode dans l’événement : ils conservent offset, longueur et SHA-256. Les tailles de vectors, booléens, octets suffixes et paires de comptes optionnelles sont validés avant toute lecture. Les anciennes formes upgradeable sans le booléen suffixe sont acceptées avec la valeur par défaut officielle et marquées comme telles. + +La projection lifecycle produit `program_loader:::0` pour les mutations réussies et commitées : finalize immuable, initialize/deploy/upgrade/close/extend upgradeable, puis set length/deploy/retract/finalize v4. Les écritures, copies et changements d’autorité restent des événements decode de familles Audit ou Admin. + +## Slashing Program stateless + +Program ID : `S1ashing11111111111111111111111111111111111`. + +Agave `v4.1.1` le déclare comme stateless builtin derrière `enshrine_slashing_program`, avec un hash de build vérifié correspondant à la release officielle `solana-program/slashing@fe8da3a`. + +| Tag `u8` | Entry code | Taille exacte | Effet runtime observé | +|---------:|--------------------------|--------------:|------------------------------------------------------------------------| +| 0 | `close_violation_report` | 1 | ferme le rapport après au moins trois epochs et transfère ses lamports | +| 1 | `duplicate_block_proof` | 305 | vérifie une preuve externe puis stocke un rapport de violation | + +La preuve duplicate block est lue depuis un compte externe à l’offset `u64` de l’instruction. Le replay transactionnel ne possède pas le contenu historique de ce compte. Le décodeur conserve l’offset, le slot, les clés, racines et signatures intégrées à l’instruction, résume l’instruction Ed25519 précédente et ne prétend ni relire le compte ni recalculer les signatures. + +Une transaction réussie prouve seulement l’acceptation runtime, l’assignation/allocation du PDA préfinancé et l’écriture du rapport. Le programme ne brûle ni ne retire lui-même du stake : la projection `slashing_violation_report` conserve donc `penaltyAppliedByProgram = false` et décrit un signal destiné à une application de pénalité externe au programme. + +## Précompiles de signature `pre.014` + +| Code canonique | Program ID | Format | État `pre.014` | +|----------------|-----------------------------------------------|---------------------------------------------------------------------------|-----------------------------------------------------------------------------| +| `ed25519` | `Ed25519SigVerify111111111111111111111111111` | header 2 octets, offsets 14 octets, clé 32, signature 64 | décodé, zéro signature accepté seulement sur 2 octets | +| `secp256k1` | `KeccakSecp256k11111111111111111111111111111` | header 1 octet, offsets 11 octets, adresse 20, signature 64 + recovery ID | décodé, index `u8` explicites, zéro signature accepté seulement sur 1 octet | +| `secp256r1` | `Secp256r1SigVerify1111111111111111111111111` | header 2 octets, offsets 14 octets, clé compressée 33, signature 64 | décodé, zéro signature refusé, maximum 8 | + +Le contrat core `2` fournit les payloads de toutes les instructions outer. Ed25519 et secp256r1 interprètent `u16::MAX` comme la cible ; secp256k1 résout toujours un index explicite. Les références aux inner instructions ne sont pas autorisées. Les offsets et longueurs utilisent des additions vérifiées avant chaque slice. + +Chaque observation conserve les index bruts et résolus, la provenance, les longueurs, SHA-256 et préfixes bornés des composants, ainsi que `runtimeVerification`. Aucun message complet n’est recopié, aucune signature n’est recalculée et aucune sortie métier n’est matérialisée. Une transaction échouée reste `decoded` si le layout est valide, mais porte `not_asserted_transaction_failed` et reste non commitée. + +## Surfaces historiques ou complémentaires + +| Code canonique | Program ID | État après `pre.008` | +|------------------|-----------------------------------------------|-----------------------------------------------------------------------------------------------------| +| `feature` | `Feature111111111111111111111111111111111111` | `revoke_pending_activation` décodé et lifecycle matérialisable | +| `zk_token_proof` | `ZkTokenProof1111111111111111111111111111111` | 17 layouts historiques décodés + fallback `current_runtime_noop_invocation`; validation synthétique | + + + +### Ancien ZK Token Proof Program + +`pre.016` couvre les discriminants `0..16` de l’interface historique : fermeture du contexte, zero balance, withdraw, égalités de ciphertext/commitment, transfer, transfer with fee, validité de pubkey, range proofs simples et batchés, validité de grouped ciphertext à deux ou trois handles et fee sigma. Les tailles de preuve et de contexte sont maintenant conservées dans une table locale auditée contre les types `Pod` de `solana-zk-token-sdk 3.1.14`, sans dépendre de cette crate dépréciée. + +Le runtime historique Agave `v2.0.0` distinguait une preuve inline d’une preuve stockée dans le premier compte lorsque l’instruction faisait exactement cinq octets. Il imposait des arités distinctes pour le contexte, refusait les vérifications comme inner instructions et plaçait certains chemins derrière `enable_zk_proof_from_account` ou `enable_zk_transfer_with_fee`. Le runtime Agave `v4.1.1` est au contraire un stub qui retourne succès sans mutation. Le décodeur conserve ces deux références sans prétendre connaître la version runtime d’une transaction donnée. + +Aucune signature mainnet n’a été trouvée pour ce program ID lors du jalon. La surface est donc validée par fixtures synthétiques et sources officielles, sans critère de backfill. Les preuves inline sont seulement mesurées, hashées et préfixées de façon bornée ; les preuves externes conservent l’offset et la taille attendue. Aucune preuve n’est recalculée et aucune sortie n’est matérialisée. + +### Stake Program + +`pre.013` couvre les dix-huit variantes de `StakeInstruction`, tags `u32 LE` `0..17` : initialize, authorize, delegate, split, withdraw, deactivate, set lockup, merge, autorisations avec seed, variantes checked, minimum delegation, deactivate delinquent, `redelegate`, move stake et move lamports. `Redelegate` reste déclaré historique : le processor actuel renvoie `InvalidInstructionData` avant tout traitement de comptes ; une occurrence réelle doit donc provenir d’une transaction échouée et reste non commitée. + +L’enum officielle de `solana-stake-interface ^4.3` conserve le contrat Serde/Bincode historique, mais n’implémente pas directement `SchemaRead`/`SchemaWrite`. Pour respecter l’interdiction workspace de dépendre de `bincode`, le décodeur utilise un miroir wire privé, ordonné exactement comme l’enum officielle, décodé par `wincode::deserialize_exact`. Les tests vérifient manuellement les tags, pubkeys, entiers, enums imbriquées et longueurs de chaînes du layout historique. Les types sémantiques et le program ID officiels restent consommés depuis l’interface. + +Le processor BPF actuel supporte deux familles de comptes. Les layouts historiques sont détectés par la première sysvar héritée, généralement Clock ou Rent ; les layouts modernes retirent ces sysvars et placent directement les autorités aux positions attendues. `Split`, `SetLockup` et `SetLockupChecked` conservent volontairement les contraintes historiques plus souples du runtime. Le décodeur expose `accountLayout`, conserve les comptes additionnels et les privilèges attendus/observés, mais ne prétend pas rejouer les vérifications dépendant de l’état du stake account, du lockup, de la délégation ou des sysvars courantes. Aucune matérialisation Stake n’est ajoutée dans ce delta. + +### Compte Stake Config historique + +`StakeConfig11111111111111111111111111111111` n’est pas un programme exécutable. L’interface Stake officielle le déclare comme identifiant déprécié du compte de configuration historique du Stake Program. Il reste dans `kb_program_ids` comme `STAKE_CONFIG_ACCOUNT_ID` et dans le registre core avec `identifier_kind = well_known_account`, mais il est exclu des surfaces du décodeur et de l’exécuteur. + +### Config Program + +Le Config Program ne possède pas un enum d’instructions discriminé comparable à System ou Stake. Chaque invocation stocke un préfixe `ConfigKeys` sérialisé avec `solana_short_vec`, suivi d’un payload spécifique au type de configuration. Le décodeur valide la longueur compacte, les clés, les booléens signataires et l’ordre des comptes signataires, puis conserve le reste par taille, préfixe borné et SHA-256 avec la sémantique explicite `opaque_program_specific`. + +### Vote Program + +`pre.010` utilise `solana-vote-interface ^6.0` avec les features `serde` et `wincode`. Le discriminant wire historique reste un `u32` little-endian compris entre `0` et `19`, et `wincode::deserialize_exact` rejette les octets résiduels. `VoteInitV2` contient les autorités, la clé BLS, sa preuve de possession et les deux commissions en points de base ; les collecteurs Inflation Rewards et Block Revenue sont les comptes d’instruction 2 et 3. + +La couverture comprend : initialisations V1/V2, autorisations simples/checked/seed, vote et vote switch, mises à jour d’état normales/compactes, tower sync, retrait, identité, commissions historiques et en points de base, collecteurs différenciés et dépôt de récompenses délégateurs. Les tableaux BLS V2 sont conservés en hexadécimal exact. Les sysvars Rent, Clock et Slot Hashes sont vérifiées aux positions imposées par l’interface. Les privilèges attendus et observés restent distincts afin de ne pas confondre les signataires statiques et les signatures PDA de CPI. + +Aucune projection matérialisée spécifique à Vote n’est ajoutée dans ce delta : la mutation réelle dépend de l’état du vote account et du runtime. Une transaction échouée produit uniquement une observation non commitée. + +## ZK ElGamal Proof `pre.015` + +Program ID : `ZkE1Gama1Proof11111111111111111111111111111`. + +Le décodeur consomme `solana-zk-elgamal-proof-interface ^0.1` pour l’enum `ProofInstruction` et les tailles exactes des types `Pod`. Il couvre `CloseContextState` et les douze preuves : zero ciphertext, égalités ciphertext/ciphertext et ciphertext/commitment, validité de clé publique, percentage with cap, range proofs 64/128/256 bits et validités grouped/batched à deux ou trois handles. + +Deux modes de transport sont distingués conformément au runtime : + +- instruction de cinq octets : tag puis offset `u32 LE` vers un compte contenant la preuve ; +- autre taille exacte : preuve inline immédiatement après le tag, avec taille égale au type `ProofData` officiel. + +Pour une preuve inline, les blocs contexte et preuve sont séparés par leurs tailles `Pod`, puis conservés uniquement sous forme de longueur, SHA-256 et préfixe hexadécimal borné. Le décodeur ne recalcule aucune preuve cryptographique. Pour une transaction réussie, il indique seulement que l’instruction a été acceptée par le runtime. Pour une transaction échouée, aucune validité n’est affirmée et l’observation reste non commitée. + +Le mode référencé par compte a une limite historique structurelle : le graphe core de transaction ne contient pas le contenu du compte à l’instant d’exécution. L’événement conserve donc le compte source, l’offset, le type et la taille attendue, mais marque les octets de preuve comme indisponibles. Il ne tente ni lecture RPC actuelle, qui pourrait être divergente, ni reconstruction. + +Les comptes optionnels de contexte sont résolus selon le mode : positions 0/1 pour une preuve inline et 1/2 après le compte de preuve pour une preuve externe. La fermeture du contexte conserve le contexte, la destination des lamports et l’autorité signataire, accepte les octets suffixes comme le runtime et refuse que contexte et destination désignent le même compte. + +`pre.017` matérialise le lifecycle du compte de contexte sous `zk_proof_context::0`. Une vérification n’est éligible que lorsque `contextStateRequested = true`, que la transaction a réussi et que l’observation est commitée. La sortie conserve le compte, l’autorité et le type de preuve, mais jamais la preuve elle-même. La fermeture réussie décrit la récupération des lamports, la remise à zéro des données et le retour de l’owner au System Program. Les preuves sans contexte restent en decode/audit. + +## Statuts et politique d’échec + +- `decoded` signifie que le format complet a été validé et qu’une observation structurée a été produite. +- `ignored` est réservé à une entrée comprise mais sans événement utile, comme le tag Compute Budget `Unused`. +- `unsupported` conserve une variante inconnue ou une surface non encore implémentée sans faux événement. +- `failed` indique un payload absent, invalide, tronqué, non canonique ou des comptes incohérents. +- `unmatched` reste un défaut de dispatch et ne doit pas apparaître pour une sélection bornée aux surfaces déclarées. + +Une transaction on-chain échouée reste décodable comme intention. L’observation porte `transactionSucceeded = false` et `observation_committed = false`. Le matérialiseur `solana_native_lifecycle` est actif pour ALT, les mutations loader listées, Feature Gate, les durable nonce accounts System et les comptes de contexte ZK ElGamal. Il accepte les familles `Lifecycle`, `Admin` et `Audit` uniquement au niveau exact surface/entrée/paramètres, puis applique `SuccessfulCommittedOnly`. Une observation non commitée est refusée et une preuve ZK sans contexte est exclue avant ledger et politique. + +## Validation mainnet `pre.005` + +Les correctifs de `pre.005` ont été validés par les tests ciblés, `cargo clippy --all-targets` et deux campagnes Tauri. Le force replay ciblé a décodé 3/3 inputs sans échec. Le replay global a sélectionné, démarré et terminé 476 inputs : 476 décodés, zéro `failed`, `unsupported`, `unmatched` et zéro faux `materializationRefused`. Aucun output n’était attendu, car le corpus ne contenait pas d’opération ALT éligible. + +## Validation `pre.006`, `pre.008`, Vote mainnet et anomalie Compute Budget + +Les tests `pre.006`, Clippy et le replay Tauri global ont été validés : 476 inputs sélectionnés, 476 décodés, aucun échec, `unsupported`, `unmatched` ou refus de matérialisation. Les validations utilisateur de `pre.008` couvrent 256 tests ciblés, PostgreSQL réel, Clippy et un démarrage/replay Tauri propre. Le corpus initial ne contenait que System et Compute Budget : loaders, Config et Feature restent en attente d’un corpus réel. + +Le backfill Vote de 500 signatures a inséré 500 transactions et l’extraction core a terminé 500/500 sans échec. Le replay global suivant contient 500 instructions Vote réelles, toutes reconnues et décodées comme `tower_sync`, sans échec Vote. L’unique `failed` de la campagne provient d’un `set_compute_unit_limit` de 12 octets : le runtime l’accepte grâce à la désérialisation Borsh unchecked, alors que le décodeur strict exigeait encore 5 octets exacts. `pre.011` corrige cet écart. Le processor reste `solana_native_classifier@0.4.1` : un force replay est requis pour remplacer le résultat `failed` déjà enregistré. diff --git a/migration/khadhroony-bot2-reference/docs/NOMENCLATURE.md b/migration/khadhroony-bot2-reference/docs/NOMENCLATURE.md new file mode 100644 index 0000000..b045003 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/NOMENCLATURE.md @@ -0,0 +1,106 @@ + + + +# Nomenclature + +Ce document fixe les noms internes utilisés dans le workspace. + +## Programmes + +- `program_id` : adresse Solana réelle. +- `program_code` : nom interne stable du programme. +- `protocol_code` : famille protocolaire. +- `surface_code` : surface concrète. + +## Événements + +Le format canonique est : + +```text +. +``` + +Exemples : + +```text +pump_swap.buy +raydium_amm_v4.swap_base_in +meteora_dlmm.swap +jupiter_v6.route +``` + +## Tables Solana PostgreSQL + +Les tables Solana PostgreSQL utilisent le schéma courant du profil PostgreSQL, généralement `public`. Le projet ne crée pas de schémas applicatifs `raw`, `core`, `obs`, `decode`, `mat`, `catalog`, `agg`, `ops` ou `wallet`. + +Le format canonique est : + +```text +kb_sol__ +``` + +Domaines autorisés au départ : + +```text +raw +core +obs +decode +mat +catalog +agg +ops +wallet +``` + +Exemples valides : + +```text +kb_sol_raw_transactions +kb_sol_core_transactions +kb_sol_obs_transaction_observations +kb_sol_obs_program_observations +kb_sol_decode_decoded_events +kb_sol_mat_trade_events +kb_sol_catalog_tokens +kb_sol_ops_processing_ledger +``` + +Exemples interdits, écrits avec `DOT` pour que les audits textuels simples ne confondent pas documentation et usage réel : + +```text +raw DOT kb_sol_rpc_transactions +core DOT kb_sol_transactions +obs DOT kb_sol_program_observations +decode DOT kb_sol_decoded_events +mat DOT kb_sol_trade_events +catalog DOT kb_sol_tokens +ops DOT kb_sol_processing_ledger +``` + +## Surfaces de programmes + +Le nom canonique d'une surface de programme doit suivre le format : + +```text +__[_vN] +``` + +Le nom de crate correspondant doit suivre le format : + +```text +kb_decoder___[_vN] +``` + +Les anciens noms issus des IDL, des explorateurs ou des anciennes matrices doivent être conservés dans le registre sous `source_code` ou `normalized_code`, mais ne doivent pas être utilisés comme nouvelle référence stable si un nom canonique existe. + +Voir aussi : + +- `docs/DATABASE.md` ; +- `docs/PROGRAM_NAMING.md` ; +- `docs/PROGRAM_REGISTRY_CONTROL.md` ; +- `registry/program_registry_seed.toml`. + +Le préfixe `program_` est interdit pour les surfaces canoniques. Les entrées dont la fonction est inconnue doivent rester en `unknown_*` avec un statut de classification, sans création de crate cible. + +Les programmes core Solana/SPL sont documentés séparément des DEX et routers, même s'ils restent dans le registre machine. diff --git a/migration/khadhroony-bot2-reference/docs/POSTGRES_STORE.md b/migration/khadhroony-bot2-reference/docs/POSTGRES_STORE.md new file mode 100644 index 0000000..356c9e2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/POSTGRES_STORE.md @@ -0,0 +1,91 @@ + + + +# Store PostgreSQL canonique + +## Baseline active + +PostgreSQL reste le backend principal. Le store utilise le schéma courant du profil, généralement `public`, et ne qualifie pas les tables par un schéma applicatif explicite. + +La baseline active contient : + +```text +kb_sol_raw_transactions +kb_sol_obs_transaction_observations +kb_sol_core_transactions +kb_sol_core_account_keys +kb_sol_core_instructions +kb_sol_core_inner_instructions +kb_sol_core_logs +kb_sol_core_balance_changes +kb_sol_ops_processing_ledger +kb_sol_decode_events +kb_sol_decode_coverage_declarations +kb_sol_decode_coverage_observations +kb_sol_mat_events +``` + +## Initialisation + +Le projet utilise encore des DDL idempotents gérés par `kb_store_pg`. `_sqlx_migrations` n’est pas requis pour démarrer l’application. + +Les fichiers SQL actifs sont conservés comme représentation lisible de la baseline : + +```text +0001_canonical_transaction_store.sql +0002_core_store.sql +0003_processing_ledger.sql +0004_decode_materialization_store.sql +``` + +Au démarrage Tauri, `initialize_store_schema()` applique une seule fois, dans l’ordre, les DDL raw, core puis decode/mat. Les connexions ouvertes ensuite par les commandes de démo désactivent `auto_initialize_schema` et ne relancent plus les migrations. Les méthodes spécialisées restent disponibles pour les tests et outils qui demandent explicitement l’initialisation d’un sous-ensemble dépendant. + +## Extraction canonique vers core + +`CoreExtractionStore` sélectionne les lignes raw, vérifie le ledger puis persiste un `CoreExtractionBundle` dans une transaction PostgreSQL unique. + +Le mode normal skippe la combinaison déjà réussie : + +```text +processor version + signature + canonical hash +``` + +Le replay forcé remplace uniquement le graphe de la signature ciblée. Les autres signatures ne sont pas modifiées. + +## Tests PostgreSQL + +Les tests qui utilisent `KB_POSTGRES_TEST_URL` doivent être lancés sur une base dédiée ou suivis d’un nettoyage explicite. + +Depuis `0.4.1-pre.021`, les tests optionnels qui partagent une base PostgreSQL réelle acquièrent un verrou process-local test-only. Ce verrou évite les interblocages intermittents entre campagnes de tests parallèles sans changer le code runtime ni le schéma SQL. + +Les tests optionnels couvrent notamment le rollback atomique core/decode, la persistance réelle du ledger et la sémantique exacte des déclarations de couverture. `optional_postgres_core_extraction_rolls_back_partial_graph_from_env` : + +1. insère une ligne raw isolée ; +2. tente de persister un bundle contenant deux account keys avec le même index ; +3. attend une violation de l’index unique après le début de la transaction ; +4. vérifie qu’aucune transaction core, account key ou entrée ledger n’a été conservée et que le raw reste `received` ; +5. rejoue un bundle corrigé ; +6. vérifie le passage à `core_extracted` et le ledger `succeeded` ; +7. nettoie ses lignes de test. + +Commande de clôture : + +```bash +KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \ + cargo test -p kb_store_pg -- --nocapture +``` + +Les contrôles réels déjà effectués sur la base principale ont confirmé : + +- 70 transactions raw et core ; +- 70 entrées ledger `succeeded` ; +- 84 tentatives après un force replay de 14 signatures ; +- aucune duplication ni ligne fille orpheline ; +- cardinalité core inchangée après remplacement forcé. + + +## Couverture et skip decode + +`persist_decode_coverage_declarations()` synchronise le snapshot déclaré d’un processor/version et retourne des compteurs exacts : insertion réelle, modification/suppression du snapshot ou déclaration inchangée. + +Le test PostgreSQL `optional_postgres_same_version_and_hash_is_current_from_env` persiste un résultat `unsupported` puis vérifie que le même stage, processor, version, input key et input hash est reconnu comme courant par le ledger. diff --git a/migration/khadhroony-bot2-reference/docs/PROGRAM_CODE_INDEX.md b/migration/khadhroony-bot2-reference/docs/PROGRAM_CODE_INDEX.md new file mode 100644 index 0000000..ebf88f7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/PROGRAM_CODE_INDEX.md @@ -0,0 +1,42 @@ + + + +# Index court des programmes + +L'idée d'un préfixe hexadécimal `00` à `ff` peut être utile comme identifiant court interne, mais elle ne doit pas remplacer le nom canonique lisible. + +## Décision + +Le nom de crate reste descriptif : + +```text +kb_decoder_amm_pump_swap +kb_decoder_router_jupiter_aggregator_v6 +kb_decoder_clmm_orca_whirlpool +``` + +Un code court optionnel peut être ajouté dans le registre : + +```toml +registry_code = "00" +canonical_surface_code = "amm_pump_swap" +target_crate = "kb_decoder_amm_pump_swap" +``` + +## Pourquoi ne pas mettre `00_` dans les crates + +- Le code `00` ne décrit pas la fonction du programme. +- L'ordre peut changer quand de nouvelles surfaces sont ajoutées. +- Deux cent cinquante-six valeurs peuvent devenir insuffisantes si toutes les surfaces, sysvars, routers, vaults, perps, bridges et variantes historiques sont incluses. +- Les renommages de crates deviendraient fréquents et inutiles. + +## Usage acceptable + +Le code court est acceptable pour : + +- affichage UI compact ; +- clé stable optionnelle dans PostgreSQL ; +- tri manuel dans les matrices ; +- identifiant de configuration humain court. + +Le code court ne doit pas servir de préfixe principal de nommage Rust. diff --git a/migration/khadhroony-bot2-reference/docs/PROGRAM_IDS.md b/migration/khadhroony-bot2-reference/docs/PROGRAM_IDS.md new file mode 100644 index 0000000..03828c4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/PROGRAM_IDS.md @@ -0,0 +1,15 @@ + + + +# Identifiants de programmes + +`kb_program_ids` est la source unique des `program_id` connus du workspace. + +Les décodeurs et exécuteurs ne doivent pas redéfinir les mêmes chaînes dans leurs propres fichiers `constants.rs`. Ils doivent utiliser directement `kb_program_ids::XXX_PROGRAM_ID`. + +Les fichiers `constants.rs` locaux restent utiles pour les constantes propres à une surface : discriminators, opcodes, seeds, index de comptes, tailles de payload ou constantes Borsh. + +## Audit de génération + +Le crate central a été alimenté à partir des constantes existantes des décodeurs/exécuteurs et des fichiers registry. + diff --git a/migration/khadhroony-bot2-reference/docs/PROGRAM_ID_CONSTANTS_AUDIT.md b/migration/khadhroony-bot2-reference/docs/PROGRAM_ID_CONSTANTS_AUDIT.md new file mode 100644 index 0000000..32df785 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/PROGRAM_ID_CONSTANTS_AUDIT.md @@ -0,0 +1,18 @@ + + + +# Audit des constantes `PROGRAM_ID` des décodeurs + +Ce document décrit la correction appliquée aux crates de décodeurs classifiés. Chaque décodeur associé à un `program_id` dans `registry/program_registry_seed.toml` doit exposer ce `program_id` via `src/constants.rs`, le réexporter depuis `src/lib.rs`, puis l’utiliser dans `program_ids()` via `crate::XXX_PROGRAM_ID`. + +## Règle retenue + +- `src/constants.rs` contient les identifiants stables du programme, puis plus tard les discriminants, sélecteurs, discriminators Anchor, codes d’instruction et constantes de décodage. +- `src/lib.rs` réexporte les constantes publiques nécessaires. +- `src/decoder.rs` ne doit pas contenir de literal de `program_id` dans `program_ids()`. +- Les décodeurs génériques comme `kb_decoder_anchor` ne doivent pas inventer de `program_id`. + +## Portée + +La correction couvre les décodeurs classifiés présents dans le workspace et associés à un `program_id` non vide dans le registre. Les crates core Solana/SPL disposaient déjà de `constants.rs`. + diff --git a/migration/khadhroony-bot2-reference/docs/PROGRAM_NAMING.md b/migration/khadhroony-bot2-reference/docs/PROGRAM_NAMING.md new file mode 100644 index 0000000..ace9bad --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/PROGRAM_NAMING.md @@ -0,0 +1,152 @@ + + + +# Nommage canonique des programmes et surfaces + +Le nommage doit permettre de distinguer la fonction réelle du programme, sa famille protocolaire et sa version. + +## Format recommandé + +```text +__[_vN] +``` + +Le nom de crate correspondant est : + +```text +kb_decoder___[_vN] +``` + + +## Préfixes fonctionnels autorisés + +Le préfixe doit décrire la fonction principale du programme, pas seulement le fait qu'il s'agit d'un programme Solana. + +Exemples de préfixes acceptés : + +```text +core +token +amm +cpmm +clmm +dlmm +stable_swap +weighted_swap +orderbook +router +launchpad +lending +vault +staking +bridge +perpetuals +oracle +nft +metadata +admin +fees +governance +treasury +lock +vesting +unknown +``` + +Le préfixe `program` est interdit pour les nouveaux noms canoniques. Une entrée non classifiée doit utiliser `unknown_*` et ne doit pas déclencher la création d'une crate cible avant classification. + +## Cas Meteora DAMM + +`damm` est conservé comme identifiant de surface Meteora, mais pas comme préfixe fonctionnel. La forme attendue est donc `amm_meteora_damm_v1` ou `amm_meteora_damm_v2`. En revanche `cpmm`, `clmm`, `dlmm`, `stable_swap` et `weighted_swap` sont des mécanismes et peuvent être utilisés comme préfixes fonctionnels. + +## Colonnes à conserver + +Le registre ne doit pas perdre les anciens noms. Chaque programme doit conserver : + +- `program_id` : adresse Solana réelle ; +- `source_code` : nom brut issu de la liste, d’un IDL ou d’une ancienne documentation ; +- `normalized_code` : nom nettoyé sans faute évidente ; +- `canonical_surface_code` : nom cible stable ; +- `current_crate` : crate existant si la surface est déjà réservée ; +- `target_crate` : nom de crate à atteindre après migration contrôlée ; +- `registry_status` : statut de contrôle. + +## Exemples de normalisation + +| Nom source | Fonction | Famille | Nom canonique | +|---------------------------|-------------|------------|------------------------------------| +| `raydium_lp_v4` | `amm` | `raydium` | `amm_raydium_lp_v4` | +| `raydium_cpmm` | `cpmm` | `raydium` | `cpmm_raydium` | +| `raydium_clmm` | `clmm` | `raydium` | `clmm_raydium` | +| `meteora_dlmm` | `dlmm` | `meteora` | `dlmm_meteora` | +| `meteora_damm_v2` | `amm` | `meteora` | `amm_meteora_damm_v2` | +| `jupiter_agregator_v6` | `router` | `jupiter` | `router_jupiter_aggregator_v6` | +| `okx_labs_v2` | `router` | `okx` | `router_okx_labs_v2` | +| `openbook_v2` | `orderbook` | `openbook` | `orderbook_openbook_v2` | +| `phoenix` | `orderbook` | `phoenix` | `orderbook_phoenix_v1` | +| `pump_swap` | `amm` | `pump` | `amm_pump_swap` | +| `pump_fun` | `launchpad` | `pump` | `launchpad_pump_fun` | +| `pump_fees` | `admin` | `pump` | `admin_pump_fees` | +| `orca_whirlpool` | `clmm` | `orca` | `clmm_orca_whirlpool` | +| `metaplex_token_metadata` | `metadata` | `metaplex` | `metadata_metaplex_token_metadata` | +| `bubblegum` | `nft` | `metaplex` | `nft_metaplex_bubblegum` | + + +## Exceptions conservées + +Les programmes Solana/SPL de base restent groupés dans des crates techniques déjà lisibles : + +- `kb_decoder_solana_core` ; +- `kb_decoder_spl_token` ; +- `kb_decoder_spl_token_2022` ; +- `kb_decoder_spl_associated_token_account`. + +Ces crates ne sont pas renommées en `kb_decoder_core_*` ou `kb_decoder_token_*`, car elles servent de couche primitive avant les surfaces DEX/router. + +## Politique de migration + +La migration physique des dossiers ne doit pas être faite en masse. Pour chaque rename : + +1. prouver que le `program_id` correspond à la surface ; +2. créer ou renommer une seule crate ; +3. mettre à jour `Cargo.toml` ; +4. mettre à jour le registre ; +5. exécuter `cargo build` ; +6. supprimer l’ancien alias seulement après validation. + + + +## Préfixe hexadécimal + +Les préfixes `00` à `ff` sont interdits comme préfixes principaux de crate. Ils peuvent exister uniquement comme champ de registre optionnel `registry_code`. Le nom canonique doit rester lisible et fonctionnel. + +Exemple accepté : + +```toml +registry_code = "01" +canonical_surface_code = "amm_pump_swap" +target_crate = "kb_decoder_amm_pump_swap" +``` + +Exemple refusé : + +```text +kb_decoder_01_pump_swap +``` + +## Fichiers `constants.rs` + +Les crates peuvent avoir un fichier `src/constants.rs` pour regrouper les `program_id`, discriminants, sélecteurs, longueurs Borsh et autres constantes techniques. Les `program_id` publics sont réexportés dans `lib.rs`. Les constantes internes futures, par exemple des discriminants ou tailles de payload, doivent rester `pub(crate)` sauf besoin explicite d'API publique. + +Depuis un module interne de la même crate, une constante réexportée par `lib.rs` doit être appelée via `crate::CONSTANT_NAME`. Le chemin `crate::constants::CONSTANT_NAME` reste réservé aux constantes non réexportées. + +## Préfixes ajoutés après inspection IDL + +- `adapter` : wrapper technique ou adaptation de format, par exemple `adapter_saber_decimal_wrapper`. +- `rwa` : programme d'actifs réels tokenisés ou marchés globaux, par exemple `rwa_ondo_global_markets`. +- `vesting` : programme de vesting, streaming ou timelock. +- `wallet` : smart wallet applicatif ou surface d'approbation/exécution. + +## Préfixe `fees` + +Le préfixe `fees` est autorisé pour les programmes dont le rôle principal est la configuration, distribution ou réclamation de frais. Il doit être préféré à `admin` lorsque la surface est explicitement centrée sur le partage de frais. diff --git a/migration/khadhroony-bot2-reference/docs/PROGRAM_REGISTRY_CONTROL.md b/migration/khadhroony-bot2-reference/docs/PROGRAM_REGISTRY_CONTROL.md new file mode 100644 index 0000000..5afb9ab --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/PROGRAM_REGISTRY_CONTROL.md @@ -0,0 +1,328 @@ + + + +# Contrôle du registre des programmes + +Ce document transforme la liste de programmes fournie en registre contrôlable. Il ne valide pas encore chaque `program_id` sur chaîne ; il sert de base interne pour détecter les doublons, les aliases, les surfaces mal nommées et les crates manquantes. + +## Résumé + +- Entrées du registre : 150. +- `program_id` distincts : 149. +- Doublons exacts de `program_id` : 1. +- Registre machine : `registry/program_registry_seed.toml`. +- La colonne `Canonique` est la référence de tri et de migration. + +## Correction de principe + +La table de contrôle est maintenant triée par `Canonique`, avec `Canonique` en première colonne. C'est le nom qui doit piloter les futures crates, les noms de surfaces et les tables de couverture. + +Les programmes Solana/SPL de base ne sont plus mélangés avec les DEX, routers et protocoles applicatifs. Ils sont listés dans un tableau séparé parce qu'ils relèvent de la couche core : `kb_decoder_solana_core`, `kb_decoder_spl_token`, `kb_decoder_spl_token_2022` et futurs décodeurs primitifs. + +## Pourquoi `amm` et pas `damm` en préfixe ? + +Le préfixe doit représenter la fonction générique du programme. `amm` signifie Automated Market Maker. `damm` est un nom de surface Meteora, généralement interprété comme Dynamic AMM. Donc `damm` ne doit pas être utilisé comme préfixe fonctionnel général. + +La forme recommandée est : + +```text +amm_meteora_damm_v1 +amm_meteora_damm_v2 +``` + +et non : + +```text +damm_meteora_v1 +damm_meteora_v2 +``` + +À l'inverse, `dlmm`, `clmm`, `cpmm`, `stable_swap` et `weighted_swap` sont des types de mécanismes et peuvent être utilisés comme préfixes fonctionnels quand ils décrivent mieux la surface. + +## Pourquoi supprimer `program_` ? + +Le préfixe `program_` était un fallback technique. Il n'indique aucune fonction : ni AMM, ni router, ni lending, ni bridge, ni NFT. Il est donc insuffisant pour une nomenclature stable. + +Nouvelle règle : + +- aucune nouvelle surface canonique ne doit commencer par `program_` ; +- une surface non classifiée utilise temporairement `unknown_*` ; +- une surface `unknown_*` ne doit pas devenir une crate cible tant que sa fonction réelle n'est pas décidée ; +- le statut attendu est `needs_classification` ou `current_alias_needs_classification`. + +## Programmes core Solana/SPL + +Ces identifiants sont détaillés dans `docs/CORE_PROGRAM_IDS.md`. Ils sont couverts par les décodeurs core/primitifs ou par des crates spécialisées SPL futures, pas par une crate DEX dédiée. + +| Canonique | Program ID | Source | Type | Crate cible | Statut | +|--------------------------------------------|------------------------------------------------|-----------------------------------|----------------------|-------------------------------------------|-------------------------------------| +| `core_solana_address_lookup_table_v1` | `AddressLookupTab1e1111111111111111111111111` | `address_lookup_table` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_bpf_loader_deprecated_v1` | `BPFLoader1111111111111111111111111111111111` | `bpf_loader_deprecated` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_bpf_loader_v2` | `BPFLoader2111111111111111111111111111111111` | `bpf_loader` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_bpf_loader_upgradeable_v1` | `BPFLoaderUpgradeab1e11111111111111111111111` | `bpf_loader_upgradeable` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_compute_budget_v1` | `ComputeBudget111111111111111111111111111111` | `compute_budget` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_config_v1` | `Config1111111111111111111111111111111111111` | `config` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_ed25519_v1` | `Ed25519SigVerify111111111111111111111111111` | `ed25519` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_feature_v1` | `Feature111111111111111111111111111111111111` | `feature` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_incinerator_v1` | `1nc1nerator11111111111111111111111111111111` | `incinerator` | `well_known_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_loader_v4` | `LoaderV411111111111111111111111111111111111` | `loader_v4` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_native_loader_v1` | `NativeLoader1111111111111111111111111111111` | `native_loader` | `loader_program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_secp256k1_v1` | `KeccakSecp256k11111111111111111111111111111` | `secp256k1` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_secp256r1_v1` | `Secp256r1SigVerify1111111111111111111111111` | `secp256r1` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_stake_config_v1` | `StakeConfig11111111111111111111111111111111` | `stake_config` | `well_known_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_stake_v1` | `Stake11111111111111111111111111111111111111` | `stake` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_system_v1` | `11111111111111111111111111111111` | `system` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_sysvar_clock_v1` | `SysvarC1ock11111111111111111111111111111111` | `sysvar_clock` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_epoch_rewards_v1` | `SysvarEpochRewards1111111111111111111111111` | `sysvar_epoch_rewards` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_epoch_schedule_v1` | `SysvarEpochSchedu1e111111111111111111111111` | `sysvar_epoch_schedule` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_fees_v1` | `SysvarFees111111111111111111111111111111111` | `sysvar_fees` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_instructions_v1` | `Sysvar1nstructions1111111111111111111111111` | `sysvar_instructions` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_last_restart_slot_v1` | `SysvarLastRestartS1ot1111111111111111111111` | `sysvar_last_restart_slot` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_recent_blockhashes_v1` | `SysvarRecentB1ockHashes11111111111111111111` | `sysvar_recent_blockhashes` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_rent_v1` | `SysvarRent111111111111111111111111111111111` | `sysvar_rent` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_rewards_v1` | `SysvarRewards111111111111111111111111111111` | `sysvar_rewards` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_slot_hashes_v1` | `SysvarS1otHashes111111111111111111111111111` | `sysvar_slot_hashes` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_slot_history_v1` | `SysvarS1otHistory11111111111111111111111111` | `sysvar_slot_history` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_stake_history_v1` | `SysvarStakeHistory1111111111111111111111111` | `sysvar_stake_history` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_sysvar_v1` | `Sysvar1111111111111111111111111111111111111` | `sysvar` | `sysvar_account` | `kb_decoder_solana_core` | `core_recognized_account` | +| `core_solana_vote_v1` | `Vote111111111111111111111111111111111111111` | `vote` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_zk_elgamal_proof_v1` | `ZkE1Gama1Proof11111111111111111111111111111` | `zk_elgamal_proof` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_solana_zk_token_proof_v1` | `ZkTokenProof1111111111111111111111111111111` | `zk_token_proof` | `program` | `kb_decoder_solana_core` | `core_handled` | +| `core_spl_associated_token_account_v1` | `ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL` | `associated_token_account` | `program` | `kb_decoder_spl_associated_token_account` | `core_handled` | +| `core_spl_account_compression_v1` | `cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK` | `spl_account_compression` | `program` | `kb_decoder_spl_account_compression` | `core_specialized_reserved_current` | +| `core_spl_memo_v1` | `Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo` | `spl_memo_v1` | `program` | `kb_decoder_spl_memo` | `core_specialized_reserved_current` | +| `core_spl_memo_v3` | `MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr` | `spl_memo_v3` | `program` | `kb_decoder_spl_memo` | `core_specialized_reserved_current` | +| `core_spl_memo_v4` | `Memo4c2pN8afCj432Lb7RMVKi9PbQnnW7ewFFaV3oAH` | `spl_memo_v4` | `program` | `kb_decoder_spl_memo` | `core_specialized_reserved_current` | +| `core_spl_noop_v1` | `noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV` | `spl_noop` | `program` | `kb_decoder_spl_noop` | `core_specialized_reserved_current` | +| `core_spl_single_pool_v1` | `SVSPxpvHdN29nkVg9rPapPNDddN5DipNLRUFhyjFThE` | `spl_single_pool` | `program` | `kb_decoder_spl_single_pool` | `core_specialized_reserved_current` | +| `core_spl_name_service_v1` | `namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX` | `spl_name_service` | `program` | `kb_decoder_metadata_spl_name_service` | `core_specialized_reserved_current` | +| `core_spl_stake_pool_v1` | `SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy` | `stake_pool` | `program` | `kb_decoder_spl_stake_pool` | `core_specialized_reserved_current` | +| `core_spl_token_2022_v2022` | `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` | `token_2022` | `program` | `kb_decoder_spl_token_2022` | `core_handled` | +| `core_spl_token_2022_elgamal_registry_v1` | `regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg` | `spl_token_2022_elgamal_registry` | `program` | `kb_decoder_spl_token_2022` | `core_specialized_reserved_current` | +| `core_spl_token_v1` | `TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA` | `token` | `program` | `kb_decoder_spl_token` | `core_handled` | + + +## DEX, AMM, CPMM, CLMM, DLMM, stable swap, weighted swap et orderbooks + +| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut | +|------------------------------------|------------------------------------------------|--------------------------|--------------------------|-------------------------------------|-----------------------------------------------|------------------------| +| `amm_aldrin_v1` | `AMM55ShdkoGRB5jVYPjWziwk8m5MpwyDgsMWHaMSQWH6` | `aldrin_amm_v1` | `aldrin_amm_v1` | `-` | `kb_decoder_amm_aldrin_v1` | `reserved_missing` | +| `amm_aldrin_v2` | `CURVGoZn8zycx6FXwwevgBTB2gVvdbGTEpvMJDbgs2t4` | `aldrin_amm_v2` | `aldrin_amm_v2` | `-` | `kb_decoder_amm_aldrin_v2` | `reserved_missing` | +| `amm_alphaq` | `ALPHAQmeA7bjrVuccPsYPiCvsi428SNwte66Srvs4pHA` | `alphaq` | `alphaq` | `kb_decoder_alphaq` | `kb_decoder_amm_alphaq` | `current_alias` | +| `amm_believe` | `5qWya6UjwWnGVhdSBL3hyZ7B45jbk6Byt1hwd7ohEGXE` | `believe` | `believe` | `kb_decoder_believe` | `kb_decoder_amm_believe` | `current_alias` | +| `amm_bonk_swap` | `BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p` | `bonk_swap` | `bonk_swap` | `kb_decoder_bonkswap` | `kb_decoder_amm_bonk_swap` | `current_alias` | +| `amm_cropper_finance` | `CTMAxxk34HjKWxQ3QLZK1HpaLXmBveao3ESePXbiyfzh` | `cropper_finance` | `cropper_finance` | `-` | `kb_decoder_amm_cropper_finance` | `reserved_missing` | +| `amm_dexlab_swap` | `DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6N` | `dexlab_swap` | `dexlab_swap` | `-` | `kb_decoder_amm_dexlab_swap` | `reserved_missing` | +| `amm_fluxbeam` | `FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X` | `fluxbeam` | `fluxbeam` | `kb_decoder_fluxbeam` | `kb_decoder_amm_fluxbeam` | `current_alias` | +| `amm_fusion` | `fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9` | `fusion_amm` | `fusion_amm` | `kb_decoder_fusion_amm` | `kb_decoder_amm_fusion` | `current_alias` | +| `amm_goon_fi` | `goonERTdGsjnkZqWuVjs73BZ3Pb9qoCUdBUL17BnS5j` | `goon_fi` | `goon_fi` | `kb_decoder_goonfi` | `kb_decoder_amm_goon_fi` | `current_alias` | +| `amm_goon_fi_v2` | `goonuddtQRrWqqn5nFyczVKaie28f3kDkHWkHtURSLE` | `goon_fi_v2` | `goon_fi_v2` | `-` | `kb_decoder_amm_goon_fi_v2` | `reserved_missing` | +| `amm_goosefx_gamma` | `GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT` | `goose_fx_gamma` | `goosefx_gamma` | `kb_decoder_goosefx_gamma` | `kb_decoder_amm_goosefx_gamma` | `current_alias` | +| `amm_goosefx_v2` | `GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn` | `goose_fx_v2` | `goosefx_v2` | `kb_decoder_goosefx_v2` | `kb_decoder_amm_goosefx_v2` | `current_alias` | +| `amm_guac_swap` | `Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1` | `guac_swap` | `guac_swap` | `kb_decoder_guacswap` | `kb_decoder_amm_guac_swap` | `current_alias` | +| `amm_heaven_dex` | `HEAVENoP2qxoeuF8Dj2oT1GHEnu49U5mJYkdeC8BAX2o` | `heaven_dex` | `heaven_dex` | `-` | `kb_decoder_amm_heaven_dex` | `reserved_missing` | +| `amm_hylo_exchange` | `HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn` | `hylo_exchange` | `hylo_exchange` | `kb_decoder_hylo_exchange` | `kb_decoder_amm_hylo_exchange` | `current_alias` | +| `amm_invariant_swap` | `HyaB3W9q6XdA5xwpU4XnSZV94htfmbmqJXZcEbRaJutt` | `invariant_swap` | `invariant_swap` | `-` | `kb_decoder_amm_invariant_swap` | `reserved_missing` | +| `amm_lifinity_swap` | `EewxydAPCCVuNEyrVN68PuSYdQ7wKn27V9Gjeoi8dy3S` | `lifinity_swap` | `lifinity_swap` | `-` | `kb_decoder_amm_lifinity_swap` | `reserved_missing` | +| `amm_lifinity_swap_v2` | `2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c` | `lifinity_swap_v2` | `lifinity_swap_v2` | `-` | `kb_decoder_amm_lifinity_swap_v2` | `reserved_missing` | +| `amm_marcopolo_swap` | `9tKE7Mbmj4mxDjWatikzGAtkoWosiiZX9y6J4Hfm2R8H` | `marcopolo_swap` | `marcopolo_swap` | `-` | `kb_decoder_amm_marcopolo_swap` | `reserved_missing` | +| `amm_metadao_futarchy_amm` | `FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq` | `metadao_futarchy_amm` | `metadao_futarchy_amm` | `-` | `kb_decoder_amm_metadao_futarchy_amm` | `reserved_missing` | +| `amm_metadao_v0_5` | `AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ` | `metadao_amm_v0_5` | `metadao_amm_v0_5` | `kb_decoder_metadao_amm_v0_5` | `kb_decoder_amm_metadao_v0_5` | `current_alias` | +| `amm_meteora_damm_v1` | `Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB` | `meteora_damm_v1` | `meteora_damm_v1` | `kb_decoder_meteora_damm_v1` | `kb_decoder_amm_meteora_damm_v1` | `current_alias` | +| `amm_meteora_damm_v2` | `cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG` | `meteora_damm_v2` | `meteora_damm_v2` | `kb_decoder_meteora_damm_v2` | `kb_decoder_amm_meteora_damm_v2` | `current_alias` | +| `amm_obric_v2` | `obriQD1zbpyLz95G5n7nJe6a4DPjpFwa5XYPoNm113y` | `obric_v2` | `obric_v2` | `kb_decoder_obric_v2` | `kb_decoder_amm_obric_v2` | `current_alias` | +| `amm_one_dex` | `DEXYosS6oEGvk8uCDayvwEZz4qEyDJRf9nFgYCaqPMTm` | `one_dex` | `one_dex` | `kb_decoder_one_dex` | `kb_decoder_amm_one_dex` | `current_alias` | +| `amm_orca_token_swap` | `DjVE6JNiYqPL2QXyCUUh8rNjHrbz9hXHNYt99MQ59qw1` | `orca_token_swap` | `orca_token_swap` | `-` | `kb_decoder_amm_orca_token_swap` | `reserved_missing` | +| `amm_orca_token_swap_v2` | `9W959DqEETiGZocYWCQPaJ6sBmUzgfxXfqGeTEdp3aQP` | `orca_token_swap_v2` | `orca_token_swap_v2` | `-` | `kb_decoder_amm_orca_token_swap_v2` | `reserved_missing` | +| `amm_pancake_swap` | `HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq` | `pancake_swap` | `pancake_swap` | `kb_decoder_pancake_swap` | `kb_decoder_amm_pancake_swap` | `current_alias` | +| `amm_pump_swap` | `pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA` | `pump_swap` | `pump_swap` | `kb_decoder_pump_swap` | `kb_decoder_amm_pump_swap` | `current_alias` | +| `amm_raydium_lp_amm` | `5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h` | `raydium_lp_amm` | `raydium_lp_amm` | `-` | `kb_decoder_amm_raydium_lp_amm` | `reserved_missing` | +| `amm_raydium_lp_v2` | `RVKd61ztZW9GUwhRbbLoYVRE5Xf1B2tVscKqwZqXgEr` | `raydium_lp_v2` | `raydium_lp_v2` | `-` | `kb_decoder_amm_raydium_lp_v2` | `reserved_missing` | +| `amm_raydium_lp_v3` | `27haf8L6oxUeXrHrgEgsexjSY5hbVUWEmvv9Nyxg8vQv` | `raydium_lp_v3` | `raydium_lp_v3` | `-` | `kb_decoder_amm_raydium_lp_v3` | `reserved_missing` | +| `amm_raydium_lp_v4` | `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8` | `raydium_lp_v4` | `raydium_lp_v4` | `-` | `kb_decoder_amm_raydium_lp_v4` | `reserved_missing` | +| `amm_saros` | `SSwapUtytfBdBn1b9NUGG6foMVPtcWgpRU32HToDUZr` | `saros_amm` | `saros_amm` | `-` | `kb_decoder_amm_saros` | `reserved_missing` | +| `amm_solfi` | `SoLFiHG9TfgtdUXUjWAxi3LtvYuFyDLVhBWxdMZxyCe` | `solfi` | `solfi` | `kb_decoder_solfi` | `kb_decoder_amm_solfi` | `current_alias` | +| `amm_solfi_v2` | `SV2EYYJyRz2YhfXwXnhNAevDEui5Q6yrfyo13WtupPF` | `solfi_v2` | `solfi_v2` | `kb_decoder_solfi_v2` | `kb_decoder_amm_solfi_v2` | `current_alias` | +| `amm_step_finance_swap` | `SSwpMgqNDsyV7mAgN9ady4bDVu5ySjmmXejXvy2vLt1` | `step_finance_swap` | `step_finance_swap` | `-` | `kb_decoder_amm_step_finance_swap` | `reserved_missing` | +| `amm_stepn_dooar_swap` | `Dooar9JkhdZ7J3LHN3A7YCuoGRUggXhQaG4kijfLGU2j` | `stepn_dooar_swap` | `stepn_dooar_swap` | `-` | `kb_decoder_amm_stepn_dooar_swap` | `reserved_missing` | +| `amm_swap_v1` | `SwaPpA9LAaLfeLi3a68M4DjnLqgtticKg6CnyNwgAC8` | `swap` | `swap` | `-` | `kb_decoder_amm_swap_v1` | `reserved_missing` | +| `amm_vertigo` | `vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ` | `vertigo` | `vertigo` | `kb_decoder_vertigo` | `kb_decoder_amm_vertigo` | `current_alias` | +| `amm_virtuals` | `5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki` | `virtuals` | `virtuals` | `kb_decoder_virtuals` | `kb_decoder_amm_virtuals` | `current_alias` | +| `amm_woofi` | `WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb` | `woofi` | `woofi` | `kb_decoder_woofi` | `kb_decoder_amm_woofi` | `current_alias` | +| `amm_zero_fi` | `ZERor4xhbUycZ6gb9ntrhqscUcZmAbQDjEAtCf4hbZY` | `zero_fi` | `zero_fi` | `kb_decoder_zerofi` | `kb_decoder_amm_zero_fi` | `current_alias` | +| `amm_zora` | `zoRabwLGd5zXaV7Gxacppw8tcceXEiTrSKyNLSaSTUc` | `zora` | `zora` | `kb_decoder_zora` | `kb_decoder_amm_zora` | `current_alias` | +| `clmm_byreal` | `REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2` | `byreal_clmm` | `byreal_clmm` | `kb_decoder_byreal_clmm` | `kb_decoder_clmm_byreal` | `current_alias` | +| `clmm_crema_finance` | `CLMM9tUoggJu2wagPkkqs9eFG4BWhVBZWkP1qv3Sp7tR` | `crema_finance` | `crema_finance` | `-` | `kb_decoder_clmm_crema_finance` | `reserved_missing` | +| `clmm_cropper_whirlpool` | `H8W3ctz92svYg6mkn1UtGfu2aQr2fnUFHM1RhScEtQDt` | `cropper_whirlpool` | `cropper_whirlpool` | `-` | `kb_decoder_clmm_cropper_whirlpool` | `reserved_missing` | +| `clmm_orca_whirlpool` | `whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc` | `orca_whirlpool` | `orca_whirlpool` | `kb_decoder_orca_whirlpool` | `kb_decoder_clmm_orca_whirlpool` | `duplicate_program_id` | +| `clmm_orca_whirlpool` | `whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc` | `orca_whirlpool` | `orca_whirlpool` | `kb_decoder_orca_whirlpool` | `kb_decoder_clmm_orca_whirlpool` | `duplicate_program_id` | +| `clmm_raydium` | `CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK` | `raydium_clmm` | `raydium_clmm` | `kb_decoder_raydium_clmm` | `kb_decoder_clmm_raydium` | `current_alias` | +| `clmm_stabble` | `6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6` | `stabble_clmm` | `stabble_clmm` | `kb_decoder_stabble_clmm` | `kb_decoder_clmm_stabble` | `current_alias` | +| `cpmm_raydium` | `CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C` | `raydium_cpmm` | `raydium_cpmm` | `kb_decoder_raydium_cpmm` | `kb_decoder_cpmm_raydium` | `current_alias` | +| `dlmm_meteora` | `LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo` | `meteora_dlmm` | `meteora_dlmm` | `kb_decoder_meteora_dlmm` | `kb_decoder_dlmm_meteora` | `current_alias` | +| `orderbook_jupiter_limit_order` | `jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu` | `jupiter_limit_order` | `jupiter_limit_order` | `kb_decoder_jupiter_limit_order` | `kb_decoder_orderbook_jupiter_limit_order` | `current_alias` | +| `orderbook_jupiter_limit_order_v2` | `j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X` | `jupiter_limit_order_v2` | `jupiter_limit_order_v2` | `kb_decoder_jupiter_limit_order_v2` | `kb_decoder_orderbook_jupiter_limit_order_v2` | `current_alias` | +| `orderbook_manifest_v1` | `MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms` | `manifest` | `manifest` | `-` | `kb_decoder_orderbook_manifest_v1` | `reserved_missing` | +| `orderbook_openbook_v2` | `opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb` | `openbook_v2` | `openbook_v2` | `kb_decoder_openbook_v2` | `kb_decoder_orderbook_openbook_v2` | `current_alias` | +| `orderbook_phoenix_v1` | `PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY` | `phoenix` | `phoenix` | `-` | `kb_decoder_orderbook_phoenix_v1` | `reserved_missing` | +| `stable_swap_jupiter_stable` | `JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr` | `jupiter_stable` | `jupiter_stable` | `kb_decoder_jupiter_stable` | `kb_decoder_stable_swap_jupiter_stable` | `current_alias` | +| `stable_swap_mercurial` | `MERLuDFBMmsHnsBPZw2sDQZHvXFMwp8EdjudcU2HKky` | `mercurial_stable_swap` | `mercurial_stable_swap` | `-` | `kb_decoder_stable_swap_mercurial` | `reserved_missing` | +| `stable_swap_saber` | `SSwpkEEcbUqx4vtoEByFjSkhKdCT862DNVb52nZg1UZ` | `sabre_statble_swap` | `sabre_stable_swap` | `-` | `kb_decoder_stable_swap_saber` | `reserved_missing` | +| `stable_swap_stabble` | `swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ` | `stabble_stable_swap` | `stabble_stable_swap` | `kb_decoder_stabble_stable_swap` | `kb_decoder_stable_swap_stabble` | `current_alias` | +| `weighted_swap_stabble` | `swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW` | `stabble_weighted_swap` | `stabble_weighted_swap` | `kb_decoder_stabble_weighted_swap` | `kb_decoder_weighted_swap_stabble` | `current_alias` | + + +## Routers et surfaces de routing + +| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut | +|--------------------------------|------------------------------------------------|-------------------------|-------------------------|----------------------------------|-------------------------------------------|--------------------| +| `router_dflow_aggregator_v4` | `DF1ow4tspfHX9WJsAb9epbkA8hmpSEAtxXy1V27QBH` | `dflow_agregator_v4` | `dflow_aggregator_v4` | `kb_decoder_dflow_aggregator_v4` | `kb_decoder_router_dflow_aggregator_v4` | `current_alias` | +| `router_jupiter_aggregator_v4` | `JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB` | `jupiter_agregator_v4` | `jupiter_aggregator_v4` | `-` | `kb_decoder_router_jupiter_aggregator_v4` | `reserved_missing` | +| `router_jupiter_aggregator_v6` | `JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4` | `jupiter_agregator_v6` | `jupiter_aggregator_v6` | `-` | `kb_decoder_router_jupiter_aggregator_v6` | `reserved_missing` | +| `router_jupiter_dca` | `DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M` | `jupiter_dca` | `jupiter_dca` | `kb_decoder_jupiter_dca` | `kb_decoder_router_jupiter_dca` | `current_alias` | +| `router_okx_labs_v1` | `6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma` | `okx_labs_v1` | `okx_labs_v1` | `kb_decoder_okx_lab_v1` | `kb_decoder_router_okx_labs_v1` | `current_alias` | +| `router_okx_labs_v2` | `proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u` | `okx_labs_v2` | `okx_labs_v2` | `-` | `kb_decoder_router_okx_labs_v2` | `reserved_missing` | +| `router_raydium_amm_routing` | `routeUGWgWzqBWFcrCfv8tritsqukccJPu3q5GPP3xS` | `raydium_amm_routing` | `raydium_amm_routing` | `-` | `kb_decoder_router_raydium_amm_routing` | `reserved_missing` | +| `router_sanctum` | `stkitrT1Uoy18Dk1fTrgPw8W6MVzoCfYoAFT4MLsmhq` | `sanctum_router` | `sanctum_router` | `-` | `kb_decoder_router_sanctum` | `reserved_missing` | +| `router_tessera_v` | `TessVdML9pBGgG9yGks7o4HewRaXVAMuoVj4x83GLQH` | `tessera_v` | `tessera_v` | `-` | `kb_decoder_router_tessera_v` | `reserved_missing` | +| `router_titan_exchange_router` | `T1TANpTeScyeqVzzgNViGDNrkQ6qHz9KrSBS4aNXvGT` | `titan_exchange_router` | `titan_exchange_router` | `-` | `kb_decoder_router_titan_exchange_router` | `reserved_missing` | + + +## Launchpads et bonding curves + +| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut | +|--------------------------------------|------------------------------------------------|----------------------------|----------------------------|-----------------------------|-------------------------------------------------|--------------------| +| `launchpad_boop_fun` | `boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4` | `boop_fun` | `boop_fun` | `kb_decoder_boop_fun` | `kb_decoder_launchpad_boop_fun` | `current_alias` | +| `launchpad_letsbonk_fun` | `FfYek5vEz23cMkWsdJwG2oa6EphsvXSHrGpdALN4g6W1` | `letsbonk_fun` | `letsbonk_fun` | `-` | `kb_decoder_launchpad_letsbonk_fun` | `reserved_missing` | +| `launchpad_metadao_ico` | `moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM` | `metadao_ico` | `metadao_ico` | `-` | `kb_decoder_launchpad_metadao_ico` | `reserved_missing` | +| `launchpad_meteora_dbc` | `dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN` | `meteora_dbc` | `meteora_dbc` | `kb_decoder_meteora_dbc` | `kb_decoder_launchpad_meteora_dbc` | `current_alias` | +| `launchpad_moonit` | `MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG` | `moonit` | `moonit` | `kb_decoder_moonit` | `kb_decoder_launchpad_moonit` | `current_alias` | +| `launchpad_moonshot_token_authority` | `7rtiKSUDLBm59b1SBmD9oajcP8xE64vAGSMbAN5CXy1q` | `moonshot_token_authority` | `moonshot_token_authority` | `-` | `kb_decoder_launchpad_moonshot_token_authority` | `reserved_missing` | +| `launchpad_orca_wavebreak` | `waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF` | `orca_wavebreak` | `orca_wavebreak` | `kb_decoder_orca_wavebreak` | `kb_decoder_launchpad_orca_wavebreak` | `current_alias` | +| `launchpad_printr` | `T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint` | `printr` | `printr` | `kb_decoder_printr` | `kb_decoder_launchpad_printr` | `current_alias` | +| `launchpad_pump_fun` | `6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P` | `pump_fun` | `pump_fun` | `kb_decoder_pump_fun` | `kb_decoder_launchpad_pump_fun` | `current_alias` | +| `launchpad_pump_pumpup_ai` | `PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd` | `pumpup_ai` | `pumpup_ai` | `-` | `kb_decoder_launchpad_pump_pumpup_ai` | `reserved_missing` | +| `launchpad_raydium_launchlab` | `LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj` | `raydium_launchlab` | `raydium_launchlab` | `-` | `kb_decoder_launchpad_raydium_launchlab` | `reserved_missing` | + + +## Lending, vault, staking, bridge, perps, oracle et treasury + +| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut | +|---------------------------------------------------|------------------------------------------------|-------------------------------------------|-------------------------------------------|---------------------------------|--------------------------------------------------------------|--------------------| +| `bridge_circle_cctp_token_messenger_minter` | `CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3` | `cctp_token_messenger_minter` | `cctp_token_messenger_minter` | `-` | `kb_decoder_bridge_circle_cctp_token_messenger_minter` | `reserved_missing` | +| `bridge_circle_cctp_token_messenger_minter_v2` | `CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe` | `cctp_token_messenger_minter_v2` | `cctp_token_messenger_minter_v2` | `-` | `kb_decoder_bridge_circle_cctp_token_messenger_minter_v2` | `reserved_missing` | +| `bridge_de_bridge_destination` | `dst5MGcFPoBeREFAA5E3tU5ij8m5uVYwkzkSAbsLbNo` | `de_bridge_destination` | `de_bridge_destination` | `-` | `kb_decoder_bridge_de_bridge_destination` | `reserved_missing` | +| `bridge_de_bridge_source` | `src5qyZHqTqecJV4aY6Cb6zDZLMDzrDKKezs22MPHr4` | `de_bridge_source` | `de_bridge_source` | `-` | `kb_decoder_bridge_de_bridge_source` | `reserved_missing` | +| `bridge_layer_zero_endpoint` | `76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6` | `layer_zero_endpoint` | `layer_zero_endpoint` | `-` | `kb_decoder_bridge_layer_zero_endpoint` | `reserved_missing` | +| `bridge_layer_zero_executor` | `6doghB248px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn` | `layer_zero_executor` | `layer_zero_executor` | `-` | `kb_decoder_bridge_layer_zero_executor` | `reserved_missing` | +| `bridge_wormhole` | `wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb` | `whormhole_bridge` | `wormhole_bridge` | `-` | `kb_decoder_bridge_wormhole` | `reserved_missing` | +| `lending_jupiter_lend_borrow` | `jupr81YtYssSyPt8jbnGuiWon5f6x9TcDEFxYe3Bdzi` | `jupiter_lend_borrow` | `jupiter_lend_borrow` | `-` | `kb_decoder_lending_jupiter_lend_borrow` | `reserved_missing` | +| `lending_jupiter_lend_earn` | `jup3YeL8QhtSx1e253b2FDvsMNC87fDrgQZivbrndc9` | `jupiter_lend_earn` | `jupiter_lend_earn` | `-` | `kb_decoder_lending_jupiter_lend_earn` | `reserved_missing` | +| `lending_jupiter_lend_flash_loan` | `jupgfSgfuAXv4B6R2Uxu85Z1qdzgju79s6MfZekN6XS` | `jupiter_lend_flash_loan` | `jupiter_lend_flash_loan` | `-` | `kb_decoder_lending_jupiter_lend_flash_loan` | `reserved_missing` | +| `lending_jupiter_lend_liquidity` | `jupeiUmn818Jg1ekPURTpr4mFo29p46vygyykFJ3wZC` | `jupiter_lend_liquidity` | `jupiter_lend_liquidity` | `-` | `kb_decoder_lending_jupiter_lend_liquidity` | `reserved_missing` | +| `lending_kamino` | `KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD` | `kamino_lending` | `kamino_lending` | `-` | `kb_decoder_lending_kamino` | `reserved_missing` | +| `lending_marginfi` | `MFLQPPPPjNinkdKoy2odNFBhvpY43XtCDZjBwG2fwn5` | `marginfi` | `marginfi` | `-` | `kb_decoder_lending_marginfi` | `reserved_missing` | +| `lending_marginfi_v2` | `MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA` | `marginfi_v2` | `marginfi_v2` | `-` | `kb_decoder_lending_marginfi_v2` | `reserved_missing` | +| `lending_sharky_fi` | `SHARKobtfF1bHhxD2eqftjHBdVSCbKo9JtgK71FhELP` | `sharky_fi` | `sharky_fi` | `-` | `kb_decoder_lending_sharky_fi` | `reserved_missing` | +| `lending_solend_protocol` | `So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo` | `solend_protocol` | `solend_protocol` | `-` | `kb_decoder_lending_solend_protocol` | `reserved_missing` | +| `oracle_jupiter_prediction_market` | `3ZZuTbwC6aJbvteyVxXUS7gtFYdf7AuXeitx6VyvjvUp` | `jupiter_prediction_market` | `jupiter_prediction_market` | `-` | `kb_decoder_oracle_jupiter_prediction_market` | `reserved_missing` | +| `perpetuals_drift_v2` | `dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH` | `drift_v2` | `drift_v2` | `kb_decoder_drift_v2` | `kb_decoder_perpetuals_drift_v2` | `current_alias` | +| `perpetuals_jupiter` | `PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu` | `jupiter_perpetuals` | `jupiter_perpetuals` | `kb_decoder_jupiter_perpetuals` | `kb_decoder_perpetuals_jupiter` | `current_alias` | +| `perpetuals_zeta` | `ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD` | `zeta` | `zeta` | `kb_decoder_zeta` | `kb_decoder_perpetuals_zeta` | `current_alias` | +| `perpetuals_zeta_matching_engine` | `zDEXqXEG7gAyxb1Kg9mK5fPnUdENCGKzWrM21RMdWRq` | `zeta_matching_engine` | `zeta_matching_engine` | `-` | `kb_decoder_perpetuals_zeta_matching_engine` | `reserved_missing` | +| `staking_jito_tip_distribution` | `4R3gSG8BpU4t19KYj8CfnbtRpnT8gtk4dvTHxVRwc2r7` | `jito_tip_distribution` | `jito_tip_distribution` | `-` | `kb_decoder_staking_jito_tip_distribution` | `reserved_missing` | +| `staking_kamino_farm` | `FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr` | `kamino_farm` | `kamino_farm` | `-` | `kb_decoder_staking_kamino_farm` | `reserved_missing` | +| `staking_marinade_finance` | `MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD` | `marinade_finance` | `marinade_finance` | `-` | `kb_decoder_staking_marinade_finance` | `reserved_missing` | +| `staking_sanctum_multi_validator_spl_stake_pool` | `SPMBzsVUuoHA4Jm6KunbsotaahvVikZs1JyTW6iJvbn` | `sanctum_multi_validator_spl_stake_pool` | `sanctum_multi_validator_spl_stake_pool` | `-` | `kb_decoder_staking_sanctum_multi_validator_spl_stake_pool` | `reserved_missing` | +| `staking_sanctum_single_validator_spl_stake_pool` | `SP12tWFxD9oJsVWNavTTBZvMbA6gkAmxtVgxdqvyvhY` | `sanctum_single_validator_spl_stake_pool` | `sanctum_single_validator_spl_stake_pool` | `-` | `kb_decoder_staking_sanctum_single_validator_spl_stake_pool` | `reserved_missing` | +| `staking_solayer` | `sSo1iU21jBrU9VaJ8PJib1MtorefUV4fzC9GURa2KNn` | `solayer` | `solayer` | `kb_decoder_solayer` | `kb_decoder_staking_solayer` | `current_alias` | +| `treasury_helium_treasury_management` | `treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5` | `helium_treasury_management` | `helium_treasury_management` | `-` | `kb_decoder_treasury_helium_treasury_management` | `reserved_missing` | +| `vault_kamino` | `kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr` | `kamino_vault` | `kamino_vault` | `-` | `kb_decoder_vault_kamino` | `reserved_missing` | +| `vault_kamino_v2` | `KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd` | `kamino_vault_v2` | `kamino_vault_v2` | `-` | `kb_decoder_vault_kamino_v2` | `reserved_missing` | +| `vault_meteora` | `24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi` | `meteora_vault` | `meteora_vault` | `kb_decoder_meteora_vault` | `kb_decoder_vault_meteora` | `current_alias` | +| `vesting_streamflow` | `strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m` | `streamflow` | `streamflow` | `-` | `kb_decoder_vesting_streamflow` | `reserved_missing` | + + +## NFT, metadata, admin, governance et locks + +| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut | +|------------------------------------|------------------------------------------------|---------------------------|---------------------------|------------------------|-----------------------------------------------|--------------------| +| `lock_raydium_lp` | `LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE` | `raydium_lock_lp` | `raydium_lock_lp` | `-` | `kb_decoder_lock_raydium_lp` | `reserved_missing` | +| `admin_jupiter_lock` | `LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn` | `jupiter_lock` | `jupiter_lock` | `-` | `kb_decoder_admin_jupiter_lock` | `reserved_missing` | +| `admin_pump_fees` | `pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ` | `pump_fees` | `pump_fees` | `kb_decoder_pump_fees` | `kb_decoder_admin_pump_fees` | `current_alias` | +| `metadata_metaplex_token_metadata` | `metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s` | `metaplex_token_metadata` | `metaplex_token_metadata` | `-` | `kb_decoder_metadata_metaplex_token_metadata` | `reserved_missing` | +| `metadata_name_service` | `namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX` | `name_service` | `name_service` | `-` | `kb_decoder_metadata_spl_name_service` | `reserved_missing` | +| `nft_metaplex_bubblegum` | `BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY` | `bubblegum` | `bubblegum` | `-` | `kb_decoder_nft_metaplex_bubblegum` | `reserved_missing` | +| `nft_metaplex_mpl_core` | `CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d` | `mpl_core` | `mpl_core` | `-` | `kb_decoder_nft_metaplex_mpl_core` | `reserved_missing` | + + +## Surfaces à classifier + +Ces entrées ont un `program_id`, mais leur fonction réelle n'est pas encore assez claire pour créer une crate canonique. Elles doivent être étudiées avant renommage. + +| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut | +|---------------------------------------|------------------------------------------------|-------------------------------|-------------------------------|----------------------------------|-------------|--------------------------------------| +| `unknown_aquifer` | `AQU1FRd7papthgdrwPTTq5JacJh8YtwEXaBfKU3bTz45` | `aquifer` | `aquifer` | `-` | `-` | `needs_classification` | +| `unknown_axiom_trade` | `FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9` | `axiom_trade` | `axiom_trade` | `-` | `-` | `needs_classification` | +| `unknown_bison_fi` | `BiSoNHVpsVZW2F7rx2eQ59yQwKxzU5NvBcmKshCSUypi` | `bison_fi` | `bison_fi` | `kb_decoder_bisonfi` | `-` | `current_alias_needs_classification` | +| `unknown_carrot_defi` | `CarrotwivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s` | `carrot_defi` | `carrot_defi` | `-` | `-` | `needs_classification` | +| `unknown_clone` | `C1onEW2kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr` | `clone` | `clone` | `kb_decoder_clone` | `-` | `current_alias_needs_classification` | +| `unknown_gavel` | `srAMMzfVHVAtgSJc8iH6CfKzuWuUTzLHVCE81QU1rgi` | `gavel` | `gavel` | `kb_decoder_gavel` | `-` | `current_alias_needs_classification` | +| `unknown_hawk_fi` | `FqGg2Y1FNxMiGd51Q6UETixQWkF5fB92MysbYogRJb3P` | `hawk_fi` | `hawk_fi` | `-` | `-` | `needs_classification` | +| `unknown_humidi_fi` | `9H6tua7jkLhdm3w8BvgpTn5LZNU7g4ZynDmCiNN3q6Rp` | `humidi_fi` | `humidi_fi` | `-` | `-` | `needs_classification` | +| `unknown_hylo_stability_pool` | `HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ` | `hylo_stability_pool` | `hylo_stability_pool` | `kb_decoder_hylo_stability_pool` | `-` | `current_alias_needs_classification` | +| `unknown_jup_studio_authority` | `8rE9CtCjwhSmbwL5fbJBtRFsS3ohfMcDFeTTC7t4ciUA` | `jup_studio_authority` | `jup_studio_authority` | `-` | `-` | `needs_classification` | +| `unknown_jupiter_apepro_smart_wallet` | `JSW99DKmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw` | `jupiter_apepro_smart_wallet` | `jupiter_apepro_smart_wallet` | `-` | `-` | `needs_classification` | +| `unknown_kamino` | `6LtLpnUFNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc` | `kamino` | `kamino` | `-` | `-` | `needs_classification` | +| `unknown_metadao_bid_wall` | `WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx` | `metadao_bid_wall` | `metadao_bid_wall` | `-` | `-` | `needs_classification` | +| `unknown_numeraire` | `NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P` | `numeraire` | `numeraire` | `kb_decoder_numeraire` | `-` | `current_alias_needs_classification` | +| `unknown_ondo_global_markets` | `XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm` | `ondo_global_markets` | `ondo_global_markets` | `-` | `-` | `needs_classification` | +| `unknown_ore_v3` | `oreV3EG1i9BEgiAJ8b177Z2S2rMarzak4NMv1kULvWv` | `ore_v3` | `ore_v3` | `-` | `-` | `needs_classification` | +| `unknown_penguin_finance` | `PSwapMdSai8tjrEXcxFeQth87xC4rRsa4VA5mhGhXkP` | `pinguin_finance` | `penguin_finance` | `-` | `-` | `needs_classification` | +| `unknown_sabre_decimal_wrapper` | `DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB` | `sabre_decimal_wrapper` | `sabre_decimal_wrapper` | `-` | `-` | `needs_classification` | +| `unknown_sanctum_s_controller` | `5ocnV1qiCgaQR8Jb8xWnVbApfaygJ8tNoZfgPwsgx9kx` | `sanctum_s_controller` | `sanctum_s_controller` | `-` | `-` | `needs_classification` | +| `unknown_scorch` | `SCoRcH8c2dpjvcJD6FiPbCSQyQgu3PcUAWj2Xxx3mqn` | `scorch` | `scorch` | `kb_decoder_scorch` | `-` | `current_alias_needs_classification` | +| `unknown_swig` | `swigypWHEksbC64pWKwah1WTeh9JXwx8H1rJHLdbQMB` | `swig` | `swig` | `-` | `-` | `needs_classification` | + + +## Autres surfaces + +| Canonique | Program ID | Source | Normalisé | Crate actuelle | Crate cible | Statut | +|-----------|------------|--------|-----------|----------------|-------------|--------| + + +## Doublons et aliases à surveiller + +- Les noms `okx_v1`, `okx_v2`, `onchain_labs_dex_v1`, `onchain_labs_dex_v2` ne doivent pas être retenus comme noms canoniques tant que les `program_id` prouvés sont `okx_labs_v1` et `okx_labs_v2`. +- `goosefx_gamma` et `goosefx_v2` ne doivent pas être fusionnés tant que les `program_id` restent distincts. +- `raydium_pool_v4` doit être traité comme alias de documentation de `amm_raydium_lp_v4` si le `program_id` est `675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8`. +- `meteora_pools_amm` est un alias documentaire probable de `amm_meteora_damm_v1` si le `program_id` est `Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB`. + +## Règle de migration + +Aucun renommage physique massif ne doit être fait dans un delta général. Pour chaque surface : + +1. confirmer le `program_id` ; +2. confirmer la fonction réelle ; +3. choisir le `canonical_surface_code` ; +4. créer ou renommer une seule crate ; +5. mettre à jour `Cargo.toml` ; +6. exécuter `cargo build` ; +7. mettre à jour le registre ; +8. supprimer l'ancien alias seulement après validation. + + +## Comptes qui ne sont pas des programmes + +Les comptes qui ne sont pas prouvés comme programmes exécutables doivent rester hors du registre de surfaces. Le cas `BAGSB9TpGrZxQbEsrEznv5jXXdwyP6AXerN8aVRiAmcv` est documenté dans `docs/ACCOUNT_ONLY_CANDIDATES.md`. + +## Index hexadécimal optionnel + +Un préfixe `00` à `ff` ne doit pas remplacer le nom canonique. Il peut être ajouté comme champ `registry_code` dans le registre, mais les crates doivent rester descriptives. Voir `docs/PROGRAM_CODE_INDEX.md`. + +## Bags Fee Share + +| Canonique | Program id | Source | Fonction | Crate cible | Statut | +|--------------------------|------------------------------------------------|-------------------|----------|-------------------------------------|---------------------| +| `fees_bags_fee_share_v1` | `FEEhPbKVKnco9EXnaY3i4R5rQVUx91wgVfu8qokixywi` | Bags Fee Share V1 | `fees` | `kb_decoder_fees_bags_fee_share_v1` | `canonical_current` | +| `fees_bags_fee_share_v2` | `FEE2tBhCKAt7shrod19QttSVREUYPiyMzoku1mL1gqVK` | Bags Fee Share V2 | `fees` | `kb_decoder_fees_bags_fee_share_v2` | `canonical_current` | diff --git a/migration/khadhroony-bot2-reference/docs/PROJECT_OBJECTIVES.md b/migration/khadhroony-bot2-reference/docs/PROJECT_OBJECTIVES.md new file mode 100644 index 0000000..820a2c0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/PROJECT_OBJECTIVES.md @@ -0,0 +1,20 @@ + + + +# Objectifs du projet + +## Objectif court terme + +L'objectif court terme est de construire un système de trading assisté sur Solana. Le programme doit aider l'opérateur à observer les créations de tokens, créations de pools, migrations, changements de liquidité, swaps, claims de frais et opérations administratives qui peuvent influencer une décision d'achat ou de revente. + +Le système ne doit pas commencer par une stratégie autonome. Il doit d'abord fournir une observation fiable, des démos live, des garde-fous d'exécution et une validation post-transaction. + +## Objectif long terme + +L'objectif long terme est de construire une bibliothèque d'analyse Solana capable de rivaliser progressivement avec des outils d'exploration et d'analyse comme les indexeurs publics : parser la blockchain, classifier les programmes, décoder les instructions, matérialiser les événements métier, agréger les états et rendre les surfaces inconnues auditables. + +Ce second objectif impose de conserver une architecture large : raw immuable, registres de programmes, décodeurs par surface, matérialisateurs transversaux, exécuteurs séparés, tests anti-régression et documentation de classification. + +## Conséquence sur le squelette + +Le squelette doit rester plus large que le besoin immédiat de trading. En revanche, le développement actif doit rester priorisé : logging, configuration, stockage, RPC, wallet, démos live, puis surfaces prioritaires pour les listeners et l'exécution contrôlée. diff --git a/migration/khadhroony-bot2-reference/docs/RAW_STORAGE_LIFECYCLE.md b/migration/khadhroony-bot2-reference/docs/RAW_STORAGE_LIFECYCLE.md new file mode 100644 index 0000000..8cd800e --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/RAW_STORAGE_LIFECYCLE.md @@ -0,0 +1,115 @@ + + + +# Cycle de vie du stockage transactionnel Solana + +## Principe général + +Le stockage rejouable repose désormais sur une transaction Solana canonique unique par signature. Les formats d’origine JSON-RPC, WebSocket Helius ou Yellowstone Protobuf sont des formats de transport, pas des modèles de stockage métier. + +Le cycle est : + +```text +acquisition + -> normalisation canonique + -> persistance raw canonique + -> extraction core + -> décodage + -> matérialisation + -> compaction / archivage / purge éventuelle +``` + +## Transaction canonique + +`kb_sol_raw_transactions` conserve suffisamment d’information pour : + +- auditer une transaction ; +- reconstruire les tables `core` ; +- rejouer de nouvelles versions de décodeurs ; +- rejouer de nouvelles versions de matérialiseurs ; +- comparer plusieurs sources sans dépendre de leur format. + +La ligne est unique par signature. `canonical_format_version` permet de faire évoluer le contrat sans ambiguïté. + +## Observations d’acquisition + +`kb_sol_obs_transaction_observations` conserve les preuves techniques d’acquisition : + +- fournisseur et endpoint ; +- protocole et méthode ; +- origine live/backfill/replay/réparation ; +- commitment ; +- session et filtre ; +- timestamps de détection, réception, normalisation et persistance ; +- taille du message ; +- statut et erreur éventuelle. + +Cette table ne conserve pas le payload complet. Elle peut contenir plusieurs lignes pour une même signature. + +## Ancienne table WebSocket + +`kb_sol_raw_ws_notifications` est une table historique de `0.2.3`, supprimée pendant la transition validée `0.3.1`. Elle n’existe plus dans la baseline SQL active. + +Les nouvelles notifications WebSocket ne doivent pas être stockées intégralement par défaut. Une notification de logs peut rester temporairement dans une queue mémoire jusqu’à l’hydratation de la transaction. + +## États de rétention + +Les états de rétention de `kb_sol_raw_transactions` sont : + +```text +full +compacted +archived +purged +``` + +### `full` + +Le document canonique complet est présent dans PostgreSQL. + +### `compacted` + +Une représentation réduite ou un stockage externe permet encore l’audit minimal, mais le payload canonique complet n’est plus dans la ligne chaude. + +### `archived` + +Le payload a été déplacé vers un stockage d’archive durable. + +### `purged` + +Le payload n’est plus disponible. Le hash canonique, la signature, le slot et les données dérivées doivent rester suffisants pour les diagnostics autorisés. + +## États de traitement + +```text +received +core_extracted +decoded +materialized +failed +``` + +L’état décrit la progression de la transaction canonique, pas celle de chaque observation de source. + +## Conditions avant compaction ou purge + +Aucune purge destructive ne doit être activée avant validation de : + +- l’extraction core idempotente ; +- la couverture des décodeurs prioritaires ; +- la reconstruction depuis archive ; +- la stabilité du hash canonique ; +- la conservation du ledger de traitement ; +- la stratégie de sauvegarde PostgreSQL. + +## Payloads fournisseur de diagnostic + +Les payloads complets spécifiques à un fournisseur peuvent être exportés temporairement pour une session de test. Ils ne doivent pas devenir une dépendance du replay métier. + +Un export de diagnostic doit être : + +- opt-in ; +- borné en durée et en taille ; +- associé à une session ; +- supprimable indépendamment de PostgreSQL. + diff --git a/migration/khadhroony-bot2-reference/docs/RAW_STORE.md b/migration/khadhroony-bot2-reference/docs/RAW_STORE.md new file mode 100644 index 0000000..2e84e62 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/RAW_STORE.md @@ -0,0 +1,147 @@ + + + +# Store transactionnel Solana canonique + +## Historique + +`0.2.3` a créé deux tables minimales : + +```text +kb_sol_raw_rpc_transactions +kb_sol_raw_ws_notifications +``` + +Ces tables restent un jalon historique validé dans `CHANGELOG.md`. La transition a été appliquée et contrôlée sur PostgreSQL réel pendant `0.3.1`. + +Le modèle actif est : + +```text +kb_sol_raw_transactions +kb_sol_obs_transaction_observations +``` + +## Objectif cible + +Le store principal conserve une transaction Solana source-indépendante par signature. Les sources HTTP, WebSocket Helius et Yellowstone gRPC ne doivent pas produire plusieurs copies complètes du même contenu. + +```text +source provider + -> normalisation canonique + -> transaction unique + -> observations multiples +``` + +## Table `kb_sol_raw_transactions` + +Rôle : stocker la représentation canonique rejouable de la transaction et de ses metadata. + +Colonnes principales visées : + +| Colonne | Rôle | +|----------------------------|-----------------------------------------| +| `id` | clé primaire technique | +| `signature` | signature Solana unique | +| `slot` | slot de la transaction | +| `canonical_format_version` | version du contrat canonique | +| `canonical_json` | transaction et metadata normalisées | +| `canonical_json_hash` | hash déterministe du document canonique | +| `retention_state` | état de rétention | +| `processing_state` | état d’extraction et de traitement | +| `lifecycle_reason` | raison technique optionnelle | +| `created_at` | première insertion | +| `updated_at` | dernière évolution autorisée | + +Le contenu canonique ne contient pas de nom de fournisseur, endpoint, subscription id ou format de transport. + +`kb_model::CanonicalTransaction` fournit la sérialisation déterministe et le hash SHA-256. `kb_store_core::RawTransactionInsert::from_canonical` construit le DTO de persistance avec la signature primaire, le slot, `canonical_format_version`, `canonical_json` et `canonical_json_hash`. Le transport RPC ne dépend donc pas du backend PostgreSQL. + +## Table `kb_sol_obs_transaction_observations` + +Rôle : enregistrer chaque détection ou acquisition d’une signature sans recopier le payload complet. + +Colonnes principales visées : + +| Colonne | Rôle | +|-----------------------|-------------------------------------------------------| +| `id` | clé primaire technique | +| `observation_key` | clé idempotente de l’observation | +| `raw_transaction_id` | lien optionnel vers `kb_sol_raw_transactions` | +| `signature` | signature détectée ou reçue | +| `slot` | slot quand connu | +| `provider` | fournisseur | +| `endpoint_code` | endpoint de configuration | +| `protocol` | protocole de transport | +| `acquisition_method` | méthode ou type de stream | +| `origin` | `live`, `backfill`, `replay`, `repair` ou `migration` | +| `commitment` | commitment demandé ou reçu | +| `capture_session_id` | session de mesure ou d’ingestion | +| `filter_code` | filtre logique utilisé | +| `detected_at` | premier signal reçu | +| `received_at` | transaction complète reçue | +| `normalized_at` | fin de normalisation | +| `persisted_at` | fin de persistance | +| `payload_size_bytes` | taille du message source | +| `source_payload_hash` | hash optionnel du message source | +| `status` | résultat technique | +| `error_code` | erreur normalisée optionnelle | +| `error_message` | message de diagnostic optionnel | + +Cette table ne possède pas de `raw_json`, de `canonical_json` ni de payload Protobuf complet. + +## Notifications WebSocket + +`kb_sol_raw_ws_notifications` n’est plus conservée comme cible durable. + +Pour `logsSubscribe` : + +1. recevoir la signature et le slot ; +2. mémoriser temporairement le timestamp de détection ; +3. hydrater par `getTransaction` si nécessaire ; +4. écrire la transaction canonique ; +5. écrire une observation avec `detected_at`, `received_at` et le statut d’hydratation. + +Les payloads complets de notifications peuvent être exportés temporairement pour un benchmark borné, mais ils ne doivent pas être dupliqués en PostgreSQL. + +## Fusion par signature + +La signature est la clé d’unicité de la transaction canonique. + +- hash identique : conserver la ligne et ajouter une observation ; +- metadata plus complètes : appliquer un enrichissement déterministe ; +- différence incompatible : enregistrer un conflit et ne pas écraser silencieusement ; +- transaction absente ou erreur provider : écrire uniquement l’observation technique. + +## Cycle de vie + +Les états de rétention de la transaction canonique restent : + +```text +full +compacted +archived +purged +``` + +Les états de traitement restent : + +```text +received +core_extracted +decoded +materialized +failed +``` + +Les observations sont append-only et suivent une rétention technique distincte. + +## Baseline SQL active + +Après validation de la transition historique, le dossier `kb_store_pg/migrations/` a été consolidé : + +```text +0001_canonical_transaction_store.sql +0002_core_store.sql +``` + +La baseline ne contient que les tables et colonnes actives. Les anciens noms RPC/WS ne sont plus recréés. Le DDL runtime conserve temporairement un chemin de compatibilité idempotent pour les workspaces encore issus de `pre.001`. diff --git a/migration/khadhroony-bot2-reference/docs/REPLAY_PIPELINE.md b/migration/khadhroony-bot2-reference/docs/REPLAY_PIPELINE.md new file mode 100644 index 0000000..09d7cde --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/REPLAY_PIPELINE.md @@ -0,0 +1,80 @@ + + + +# Pipeline de replay + +Le replay ne doit pas être global par défaut. Chaque campagne reçoit un `campaign_id` et cible un ensemble explicite de processors, une version et un périmètre borné d’inputs. + +## Étapes + +```text +ingest -> extract -> observe -> decode -> materialize -> aggregate -> validate +``` + +Chaque étape doit être rejouable séparément avec son propre ledger versionné. + +## Sélection de décodage + +La sélection opérateur peut utiliser : + +- des signatures ; +- une plage de slots ; +- des `program_id` ; +- des instruction paths ; +- des états lifecycle ; +- une limite stricte. + +Le pipeline calcule ensuite `effective_program_ids` comme l’union des programmes déclarés par les décodeurs activés. Sans filtre program ID explicite, cette union est appliquée automatiquement au store. Avec un filtre explicite, chaque valeur doit être supportée par au moins un décodeur activé, sinon la campagne est refusée avant la requête SQL. + +Cette règle évite qu’un classifieur natif sélectionne des instructions SPL, Jupiter, Pump ou d’autres programmes non compatibles. Elle empêche également la resélection infinie d’un lot entièrement `unmatched` uniquement parce que les processors choisis ne couvrent pas ses programmes. + +## Force replay + +Le force replay contourne le skip processor/version/input/hash, mais reste borné par une liste de signatures explicites ou par une autorisation opérateur « toutes les signatures » combinée aux autres filtres et à la limite. Une requête sans l’un de ces deux scopes est refusée. + +Lorsqu’une campagne contient des signatures explicites, `force_replay=true` retire le filtre lifecycle pour ces signatures. Les inputs déjà terminaux peuvent alors être réellement rejoués. Le mode explicite « toutes les signatures » retire également ce filtre, mais conserve obligatoirement le scope des programmes compatibles, les slots et instruction paths éventuels ainsi que la limite. Sans signatures ni autorisation « toutes les signatures », la requête est refusée. + +Le remplacement ne touche que les sorties du processor, de la version et de l’input ciblés. Les autres processors et les autres versions restent intacts. + +## Skip + +Sans force replay, un input est skippé uniquement lorsque le ledger contient un succès ayant exactement : + +```text +stage +processor_name +processor_version +input_key +input_hash +``` + +Une campagne de validation doit pouvoir montrer successivement : + +```text +premier passage -> decoded/ignored/unsupported +second passage -> skipped +force replay -> exécution sans skip +passage suivant -> skipped +``` + +## Unmatched + +`unmatched` signifie qu’aucun décodeur compatible n’a accepté l’input après le filtre exact de programme. L’instruction n’est pas marquée globalement comme définitivement ignorée, car un futur processor peut la supporter. Les décodeurs doivent classer comme `unsupported` les instructions inconnues de leurs propres programmes afin de produire une couverture exploitable. + +## Traçabilité + +Les logs de campagne conservent : + +```text +campaign_id +signature_count +signature_sample +decoder_names +requested_program_ids +effective_program_ids +requested_processing_states +effective_processing_states +force_replay +``` + +Les logs par input conservent ensuite la signature exacte, le path, le programme, le processor, le hash et la décision terminale. diff --git a/migration/khadhroony-bot2-reference/docs/RPC_ENDPOINT_ROLES.md b/migration/khadhroony-bot2-reference/docs/RPC_ENDPOINT_ROLES.md new file mode 100644 index 0000000..813116f --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/RPC_ENDPOINT_ROLES.md @@ -0,0 +1,77 @@ + + + +# Rôles des endpoints RPC et streams + +Les rôles servent à sélectionner un endpoint selon l’opération, le provider, le protocole et les limites locales. + +## Rôles HTTP actifs + +| Rôle | Usage | +|---------------------|-----------------------------------------------------------------| +| `http_queries` | lectures courantes | +| `history_backfill` | `getSignaturesForAddress`, `getTransaction` et backfills ciblés | +| `http_transactions` | simulation, envoi et confirmation | +| `http_fallback` | endpoint de secours | +| `http_heavy` | appels lourds | + +`0.3.x` utilise principalement `history_backfill` avec les comptes gratuits. + +## Rôles WebSocket standard existants + +| Rôle | Usage | +|-------------------------|-------------------------------| +| `slot_notifications` | progression et santé du flux | +| `logs_subscribe` | logs ciblés | +| `program_subscribe` | comptes détenus par programme | +| `account_subscribe` | comptes précis | +| `program_logs` | compatibilité historique | +| `account_notifications` | compatibilité historique | + +## Rôles futurs `0.10.x` + +| Rôle | Usage | +|----------------------------------|-----------------------------------------------| +| `helius_transaction_stream` | extension Helius `transactionSubscribe` | +| `yellowstone_transaction_stream` | filtre transactions Yellowstone gRPC | +| `logs_all_probe` | comparaison temporaire `logsSubscribe("all")` | +| `realtime_transaction_hydration` | hydratation HTTP et réparation | + +Les transports Helius et Yellowstone restent implémentés dans `kb_rpc`. Les rôles ne créent pas de dépendance provider dans les décodeurs. + +## Request kinds actifs pour le backfill + +```text +get_signatures_for_address +get_transaction +get_signature_statuses +get_slot +get_version +``` + +## Request kinds futurs + +```text +transaction_subscribe +transaction_unsubscribe +yellowstone_transaction_subscribe +logs_subscribe_all +logs_unsubscribe +``` + +## Limites par rôle + +```text +requests_per_second +burst_capacity +max_concurrent_requests +max_subscriptions +pause_after_rate_limit_ms +``` + +Pour les streams gRPC, la configuration future ajoutera les limites de streams, filtres, adresses et taille de queue sans casser les rôles HTTP/WS existants. + +## Preuve runtime + +Un rôle ou un plan configuré ne garantit pas la capacité distante. La réponse du provider reste la preuve runtime. Les refus de plan, filtres invalides et limitations doivent être normalisés comme erreurs de `kb_rpc`. + diff --git a/migration/khadhroony-bot2-reference/docs/RUST_WORKSPACE_RULE_AUDIT.md b/migration/khadhroony-bot2-reference/docs/RUST_WORKSPACE_RULE_AUDIT.md new file mode 100644 index 0000000..83206a2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/RUST_WORKSPACE_RULE_AUDIT.md @@ -0,0 +1,236 @@ + + + +# Audit des règles Rust du workspace + +Violations détectées : **616**. + +## Synthèse + +| Code | Nombre | +|------------|-------:| +| `RUST010` | 156 | +| `RUST011` | 16 | +| `RUST013` | 19 | +| `RUST021` | 1 | +| `TRACE002` | 32 | +| `TRACE003` | 202 | +| `TRACE004` | 190 | + +## Premiers écarts + +- `RUST010` `kb_app_demo/src/demo_backfill.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_backfill.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_backfill.rs:8` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_config.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_config.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_core_extraction.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_core_extraction.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_core_extraction.rs:8` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_decode_replay.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_decode_replay.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_decode_replay.rs:8` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_execution_solana_core.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_execution_solana_core.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_execution_solana_core.rs:8` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_execution_spl.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_http.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_http.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_spl_ata.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_spl_token.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_spl_token_2022.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_sql_common.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_sql_common.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_sql_diag.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_sql_pg_core.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_sql_pg_raw.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_sql_replay_candidates.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_ws.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_ws.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_ws.rs:8` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/demo_ws.rs:993` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/frontend_log.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/lib.rs:13` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/lib.rs:14` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/main.rs:12` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/splash.rs:9` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_app_demo/src/splash.rs:10` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_config/src/settings.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_config/src/settings.rs:1223` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_api/src/contracts.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_metadata_metaplex_token_metadata/src/canonical.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_metadata_metaplex_token_metadata/src/decoder.rs:144` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_metadata_metaplex_token_metadata/src/instruction.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_metadata_metaplex_token_metadata/src/instruction.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/address_lookup_table.rs:326` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/compute_budget.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/compute_budget.rs:673` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/config.rs:322` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/feature.rs:167` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/loaders.rs:992` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/payload.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/payload.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/slashing.rs:461` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/stake.rs:805` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/system.rs:438` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/vote.rs:707` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/zk_elgamal.rs:522` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_solana_core/src/zk_token_proof.rs:693` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_associated_token_account/src/decoder.rs:335` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_associated_token_account/src/instruction.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_associated_token_account/src/instruction.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_elgamal_registry/src/decoder.rs:95` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_elgamal_registry/src/decoder.rs:162` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_elgamal_registry/src/registry.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_elgamal_registry/src/registry.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_elgamal_registry/src/state.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_elgamal_registry/src/state.rs:49` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_memo/src/decoder.rs:160` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_memo/src/memo.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_memo/src/memo.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_token/src/decoder.rs:144` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_token/src/token.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_token/src/token.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_token_2022/src/decoder.rs:144` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_token_2022/src/state.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_token_2022/src/state.rs:916` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_token_2022/src/token.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_decoder_spl_token_2022/src/token.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_execution_api/src/execution.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_execution_api/src/executor.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_execution_safety/src/safety.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_execution_solana/src/nonce.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_execution_solana/src/transaction.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_execution_solana/src/transaction.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_execution_solana/src/transaction.rs:654` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_execution_solana/src/transaction.rs:655` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/address_lookup_table.rs:321` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/builder.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/builder.rs:1374` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/intent.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/native_admin.rs:381` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/precompiles.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/slashing.rs:346` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/stake.rs:1497` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/validation.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/validation.rs:108` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/vote.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_solana_core/src/vote.rs:1846` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_associated_token_account/src/intent.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_elgamal_registry/src/builder.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_elgamal_registry/src/builder.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_elgamal_registry/src/intent.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_memo/src/builder.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_memo/src/builder.rs:194` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_memo/src/intent.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_token/src/builder.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_token/src/intent.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_token_2022/src/builder.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_token_2022/src/confidential.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_token_2022/src/confidential.rs:425` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_executor_spl_token_2022/src/intent.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_logging/src/tracing_runtime.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_logging/src/tracing_runtime.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_logging/src/tracing_runtime.rs:8` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_logging/src/tracing_runtime.rs:676` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_logging/src/tracing_runtime.rs:677` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_model/src/canonical_transaction.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_model/src/canonical_transaction.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_model/src/decoded.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_model/src/materialized.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_model/src/nomenclature.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_model/src/observation.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_model/src/solana.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/backfill.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/backfill.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/backfill.rs:8` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/core_extraction.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/core_extraction.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/core_extraction.rs:8` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/decode_replay.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/decode_replay.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/solana_elgamal_registry_stateful.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/solana_elgamal_registry_stateful.rs:227` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/solana_stateful.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/solana_token_stateful.rs:392` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_pipeline/src/solana_token_stateful.rs:470` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/execution_rpc.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/execution_rpc.rs:2251` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/get_transaction.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/standard_http.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/ws_client.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/ws_client.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/ws_session.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/ws_session.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/ws_session.rs:1219` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_rpc/src/ws_session.rs:1220` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_store_pg/src/queries/core_extraction_queries.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_store_pg/src/queries/core_queries.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_store_pg/src/queries/decode_pipeline_queries.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_store_pg/src/queries/table_diagnostics_queries.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_wallet/src/wallet.rs:6` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_wallet/src/wallet.rs:7` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_wallet/src/wallet.rs:261` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_wallet/src/wallet.rs:294` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_wallet/src/wallet.rs:299` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_wallet/src/wallet.rs:411` — ordinary `use` requires a trait-import justification marker +- `RUST010` `kb_wallet/src/wallet.rs:524` — ordinary `use` requires a trait-import justification marker +- `RUST011` `kb_executor_solana_core/src/lib.rs:129` — grouped re-export is forbidden +- `RUST011` `kb_executor_solana_core/src/lib.rs:137` — grouped re-export is forbidden +- `RUST011` `kb_executor_solana_core/src/lib.rs:145` — grouped re-export is forbidden +- `RUST011` `kb_executor_solana_core/src/lib.rs:159` — grouped re-export is forbidden +- `RUST011` `kb_executor_spl_token/src/lib.rs:29` — grouped re-export is forbidden +- `RUST011` `kb_executor_spl_token_2022/src/lib.rs:16` — grouped re-export is forbidden +- `RUST011` `kb_executor_spl_token_2022/src/lib.rs:47` — grouped re-export is forbidden +- `RUST011` `kb_rpc/src/lib.rs:167` — grouped re-export is forbidden +- `RUST011` `kb_rpc/src/lib.rs:175` — grouped re-export is forbidden +- `RUST011` `kb_rpc/src/lib.rs:180` — grouped re-export is forbidden +- `RUST011` `kb_rpc/src/lib.rs:188` — grouped re-export is forbidden +- `RUST011` `kb_rpc/src/lib.rs:198` — grouped re-export is forbidden +- `RUST011` `kb_rpc/src/lib.rs:204` — grouped re-export is forbidden +- `RUST011` `kb_rpc/src/lib.rs:209` — grouped re-export is forbidden +- `RUST011` `kb_rpc/src/lib.rs:232` — grouped re-export is forbidden +- `RUST011` `kb_rpc/src/lib.rs:257` — grouped re-export is forbidden +- `RUST013` `kb_decoder_metadata_metaplex_token_metadata/src/canonical.rs:6` — grouped imports are forbidden +- `RUST013` `kb_decoder_spl_token_2022/src/state.rs:916` — grouped imports are forbidden +- `RUST013` `kb_executor_solana_core/src/lib.rs:129` — grouped imports are forbidden +- `RUST013` `kb_executor_solana_core/src/lib.rs:137` — grouped imports are forbidden +- `RUST013` `kb_executor_solana_core/src/lib.rs:145` — grouped imports are forbidden +- `RUST013` `kb_executor_solana_core/src/lib.rs:159` — grouped imports are forbidden +- `RUST013` `kb_executor_spl_token/src/lib.rs:29` — grouped imports are forbidden +- `RUST013` `kb_executor_spl_token_2022/src/confidential.rs:425` — grouped imports are forbidden +- `RUST013` `kb_executor_spl_token_2022/src/lib.rs:16` — grouped imports are forbidden +- `RUST013` `kb_executor_spl_token_2022/src/lib.rs:47` — grouped imports are forbidden +- `RUST013` `kb_rpc/src/lib.rs:167` — grouped imports are forbidden +- `RUST013` `kb_rpc/src/lib.rs:175` — grouped imports are forbidden +- `RUST013` `kb_rpc/src/lib.rs:180` — grouped imports are forbidden +- `RUST013` `kb_rpc/src/lib.rs:188` — grouped imports are forbidden +- `RUST013` `kb_rpc/src/lib.rs:198` — grouped imports are forbidden +- `RUST013` `kb_rpc/src/lib.rs:204` — grouped imports are forbidden +- `RUST013` `kb_rpc/src/lib.rs:209` — grouped imports are forbidden +- `RUST013` `kb_rpc/src/lib.rs:232` — grouped imports are forbidden +- `RUST013` `kb_rpc/src/lib.rs:257` — grouped imports are forbidden +- `RUST021` `kb_app_demo/src/lib.rs:42` — crate-root re-export requires adjacent rustdoc +- `TRACE002` `kb_app_demo/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use` +- `TRACE002` `kb_decoder_metadata_metaplex_token_metadata/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use` +- `TRACE002` `kb_decoder_solana_core/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use` +- `TRACE002` `kb_decoder_spl_associated_token_account/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use` +- `TRACE002` `kb_decoder_spl_elgamal_registry/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use` +- `TRACE002` `kb_decoder_spl_memo/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use` +- `TRACE002` `kb_decoder_spl_token/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use` +- `TRACE002` `kb_decoder_spl_token_2022/src/lib.rs:1` — TRACING_TARGET must be re-exported with `pub(crate) use` + +Le rapport est tronqué aux 200 premiers écarts sur 616. + +## Stratégie de correction + +La mise en conformité est découpée avant toute nouvelle prérelease : + +1. `pre.035-delta-fix-002` : séparation des règles et audit automatisé reproductible ; +2. correctifs suivants : points d'entrée de crates, réexports `pub`/`pub(crate)`, rustdocs et chemins `crate::...` ; +3. correctifs suivants : imports de traits et suppression des imports ordinaires/groupés ; +4. correctifs suivants : `TRACING_TARGET`, dépendances `tracing`, macros et matrice de routes ; +5. correctifs suivants : helpers dupliqués et mutualisation inter-crates ; +6. fermeture : `cargo fmt --all`, audit strict, tests workspace et Clippy global. + +Aucune nouvelle prérelease ne doit être créée tant que cette séquence n'est pas terminée et validée localement. diff --git a/migration/khadhroony-bot2-reference/docs/SOLANA_INTERFACE_DEPENDENCIES.md b/migration/khadhroony-bot2-reference/docs/SOLANA_INTERFACE_DEPENDENCIES.md new file mode 100644 index 0000000..4b1cd93 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/SOLANA_INTERFACE_DEPENDENCIES.md @@ -0,0 +1,272 @@ + + + +# Dépendances d’interfaces Solana et SPL + +## Objectif + +Le workspace privilégie les interfaces officielles étroites afin d’éviter de recopier des enums, layouts, IDs ou builders déjà publiés par Solana ou SPL. Les dépendances de `[workspace.dependencies]` constituent un catalogue de versions cohérentes ; elles ne sont pas automatiquement ajoutées à toutes les crates. + +## Règle de consommation + +Une crate ajoute une interface uniquement lorsqu’elle utilise réellement au moins un de ses éléments : + +- enum d’instruction ou d’état ; +- encodeur/décodeur officiel ; +- type de compte ou paramètre ; +- program ID ou sysvar ID ; +- modèle RPC appartenant à sa responsabilité. + +Une déclaration à la racine ne justifie pas une dépendance inutilisée dans une crate feuille. Les features restent minimales et explicites. + +## Ordre des formats + +1. `wincode` lorsqu’il est officiellement exposé par l’interface ; +2. Borsh lorsqu’il correspond au contrat officiel ou au comportement du runtime ; +3. parseur local borné lorsque l’interface officielle ne fournit son helper qu’avec `bincode`. + +### Compatibilité de version `wincode` + +Le workspace reste épinglé sur `wincode ^0.5`. Les crates Solana modulaires actuellement utilisées avec la feature `wincode` publient leurs implémentations `SchemaRead`/`SchemaWrite` contre `wincode 0.5.x`. Ces traits sont nominalement distincts de ceux de `wincode 0.6.x` : une dépendance directe migrée seule vers `0.6` ne peut donc ni désérialiser `solana_nonce::versions::Versions`, ni sérialiser `solana_transaction::Transaction`. Cargo peut charger les deux versions simultanément, mais les implémentations de traits ne sont pas interchangeables et l’orphan rule interdit de les réimplémenter localement pour ces types externes. + +La migration vers `wincode 0.6` doit attendre que l’ensemble des crates Solana consommées migre de façon cohérente. Une double dépendance aliasée n’est acceptable que pour des types propres au workspace ; tous les appels portant sur des types Solana doivent utiliser la même version `0.5.x` que celle employée par leurs crates d’origine. Avant toute migration, vérifier avec `cargo tree -d` et `cargo tree -i wincode@`. + + +Le workspace n’ajoute pas de dépendance directe à `bincode`. Une feature officielle `bincode` seule n’est pas activée pour contourner cette règle. + +## Interfaces utilisées dans `kb_decoder_solana_core` + +| Surface | Interface | Features utiles | Stratégie actuelle | +|---------------------------|---------------------------------------------------|----------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------| +| Address Lookup Table | `solana-address-lookup-table-interface ^3.1` | `serde`, `wincode` | enum officielle et `wincode` exact | +| Compute Budget | `solana-compute-budget-interface ^3.0` | `borsh`, `serde` | enum officielle pour les fixtures ; parser borné aligné sur le Borsh unchecked du runtime | +| System Program | `solana-system-interface ^3.2` | `alloc`, `serde`, `std`, `solana-instruction`, `wincode` | enum officielle et `wincode` | +| Vote Program | `solana-vote-interface ^6.0` | `serde`, `wincode` | enum officielle et `wincode::deserialize_exact` | +| Stake Program | `solana-stake-interface ^4.3` | `borsh`, `serde`, `wincode` | types/ID officiels + miroir wire privé `wincode` exact, compatible avec le layout historique | +| Précompiles signature | sources runtime Agave et crates programme `3.0.0` | aucune nouvelle dépendance consommée | parseurs locaux bornés des tables d’offsets, résolution outer via contrat core `2`, sans cryptographie ni `bincode` | +| ZK ElGamal Proof | `solana-zk-elgamal-proof-interface ^0.1` | aucune feature additionnelle | enum `ProofInstruction` et tailles `Pod` officielles ; comportement des comptes vérifié contre le runtime Agave | +| ZK Token Proof historique | aucune dépendance runtime dédiée | miroir wire local borné | tags et tailles historiques audités contre `solana-zk-token-sdk 3.1.14`; comparaison Agave `v2.0.0`/`v4.1.1` | +| Slashing Program | Agave `v4.1.1`, `solana-program/slashing@fe8da3a` | aucune dépendance ajoutée | parseur local borné du contrat release vérifié ; compte de preuve externe non relu et aucune cryptographie locale | + +## Décision pour les précompiles de signature + +Les crates officielles Ed25519, secp256k1 et secp256r1 exposent les IDs, constantes et structures de layout, mais `kb_program_ids` centralise déjà les IDs et le delta n’exécute aucune vérification cryptographique. Ajouter ces dépendances uniquement pour recopier des tailles ou des structures `Pod` constituerait une dépendance sans consommation fonctionnelle. + +`kb_decoder_solana_core` reproduit les layouts exacts depuis les sources runtime Agave : table Ed25519/secp256r1 de 14 octets, table secp256k1 de 11 octets, sentinelle `u16::MAX` uniquement pour Ed25519/secp256r1, index `u8` toujours explicite pour secp256k1, règles zéro signature et limite secp256r1. Les fixtures manuelles rendent chaque offset visible et les tests couvrent les références inter-instructions et les bornes. Aucune dépendance `bincode` n’est ajoutée. + +## Transaction client dans `kb_execution_solana` + +`kb_execution_solana` n’importe plus l’agrégat `solana-sdk`. Sa frontière transactionnelle utilise les crates modulaires réellement consommées : `solana-instruction`, `solana-message`, `solana-transaction`, `solana-hash`, `solana-pubkey`, `solana-keypair`, `solana-signer`, `solana-nonce` et `solana-system-interface`. Les exécuteurs spécifiques conservent la même politique et ajoutent uniquement l’interface de programme nécessaire. + +Le message à signer provient de `solana_transaction::Transaction::message_data()`. La transaction complète utilise `wincode::serialize`, supporté officiellement par les types modulaires `Message` et `Transaction`. Cette stratégie remplace tout appel direct à `bincode` et reproduit le wire attendu par `getFeeForMessage`, `simulateTransaction` et `sendTransaction`. La taille sérialisée est refusée au-delà de 1 232 octets. + +## Identifiants natifs, Vote et loaders dans l’exécuteur + +`solana-sdk-ids` est conservé intentionnellement comme registre officiel modulaire des adresses natives et sysvars. Les chemins `solana_sdk_ids::sysvar::{clock,rent,slot_hashes,instructions}` ne constituent pas un retour au SDK monolithique : ils fournissent uniquement des `Pubkey` canoniques. `solana-instructions-sysvar` ne doit être ajouté que lorsqu’une crate appelle réellement ses fonctions d’introspection d’instructions, pas pour remplacer un simple ID officiel. + +`kb_executor_solana_core` consomme `solana-vote-interface ^6.0` avec `serde` et `wincode` pour construire les vingt variantes wire Vote actuelles. Il utilise `solana-instruction` pour `Instruction`/`AccountMeta`, `solana-pubkey::Pubkey` pour les adresses publiques, `solana-hash::Hash` pour les hashes et `solana-sdk-ids` pour Rent, Clock et SlotHashes. Les quatre créations composées utilisent `solana-system-interface`; aucune dépendance `solana-sdk` ni feature `bincode` n’est ajoutée à la crate. + +`kb_decoder_solana_core` et `kb_executor_solana_core` consomment désormais `solana-loader-v3-interface ^8.0` avec `wincode`. Le décodeur désérialise l’enum officielle de façon exacte tout en conservant la compatibilité du booléen optionnel historique des dernières variantes. L’exécuteur appelle les helpers officiels pour onze plans : création de buffer, écriture, déploiement, upgrade, changements d’autorité, fermetures et extension. + +`solana-loader-v4-interface ^3.1` publie l’enum et les comptes officiels, mais ses helpers de construction sont conditionnés par la feature `bincode` et l’enum ne fournit pas de schéma `wincode`. Pour respecter l’interdiction de `bincode`, `kb_executor_solana_core` reproduit localement le wire borné des sept discriminants (`u32 LE`, champs numériques et vecteur borné) et les métadonnées de comptes exactes pour neuf plans. La crate feuille n’ajoute donc pas une dépendance inutilisée à l’interface v4; le catalogue workspace la conserve comme source normative/versionnée. + +## Interfaces officielles non encore consommées + +| Interface | Propriétaire futur | Décision | +|------------------------------------------|------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------| +| `solana-feature-gate-interface ^4.0` | `kb_decoder_solana_core` | peut fournir les types officiels ; l’instruction actuelle reste un tag d’un octet vérifié depuis le builder/processor | +| `solana-feature-set-interface ^4.0` | classification/diagnostics natifs | ajouter seulement lorsqu’un type est consommé | +| `solana-loader-v4-interface ^3.1` | source normative Loader v4 | helpers officiels conditionnés par `bincode`; conserver le miroir wire local borné tant qu’aucun schéma `wincode` n’est publié | +| interfaces SPL | crates `kb_decoder_spl_*` et `kb_executor_spl_*` correspondantes | ne pas les rattacher au décodeur natif ni à l’application | +| types RPC/account/transaction status | `kb_rpc` | ne pas les ajouter à `kb_app_demo` ; l’application consomme les DTO du workspace | + +## Exceptions locales actuelles + +### SPL Memo v1, v3 et v4 + +`kb_decoder_spl_memo` et `kb_executor_spl_memo` consomment `spl-memo-interface ^2.1` sans feature additionnelle. La version effectivement résolue pendant les validations est 2.1.0. Elle publie les trois IDs exacts et un builder générique recevant explicitement le Program ID. Ce builder place les octets exacts du message dans `Instruction::data` et transforme chaque pubkey fournie en compte readonly signer, sans sérialisation intermédiaire. + +L’exécuteur appelle ce builder pour les trois générations et compare chaque plan au résultat officiel. Les différences runtime restent explicites : v1 ignore les comptes, v3/v4 les vérifient. La bibliothèque ne lie toutefois aucune génération à un cluster ou au dry-run : la simulation obligatoire établit la disponibilité effective du Program ID ciblé, puis les politiques communes gouvernent l’autorisation d’envoi et Mainnet. La démo du jalon retient séparément v4 sur Devnet. La taille packet finale reste contrôlée par `kb_execution_solana` après compilation du message. + +Le décodeur ne dépend pas du builder pour lire le wire : il analyse directement les octets base64 retenus par le core, avec une borne de 4 096 octets, puis valide l’UTF‑8. La source historique v1 prouve que les comptes sont ignorés ; les processors v3/v4 exigent au contraire que chaque compte fourni soit signer avant de valider l’UTF‑8. Ces règles restent séparées dans `docs/SPL_MEMO_MATRIX.json` et les tests comparent les trois IDs locaux aux constantes de l’interface officielle. + +L’archive source `0.4.2` ne contenait pas de `Cargo.lock`. Les compilations et validations `0.4.3` ont résolu `spl-memo-interface 2.1.0`, sans mise à jour automatique de la contrainte workspace. Cette version exacte a construit les trois générations et le parcours v4 Devnet réel. + +### SPL Token classique + +Le catalogue workspace déclare `spl-token-interface ^3.0` sans feature. Le tag officiel +`interface@v3.0.0` publie la version `3.0.0`, sans feature optionnelle ni dépendance à `bincode`. +`kb_decoder_spl_token` consomme l'ID officiel et utilise `TokenInstruction::unpack` dans les tests +différentiels. Le wire de production reste un parseur local borné afin de conserver les tailles +consommées, les suffixes acceptés par les processors, les diagnostics UTF-8 et la structure interne +de `Batch`, que l'enum officielle représente seulement par son tag parent. + +La surface compte 28 tags : `0..24`, `38` (`WithdrawExcessLamports`), `45` +(`UnwrapLamports`) et `255` (`Batch`). L'interface `3.0.0` fournit un builder pour chacun. La source +historique `program/src/processor.rs` ne suffit toutefois pas à prouver les deux nouveaux tags : le +processor p-token `1.0.0` sous le même Program ID implémente explicitement `UnwrapLamports` et +`Batch`. La publication de l'interface, la constructibilité, le binaire déployé par cluster et le +résultat de simulation restent quatre preuves séparées dans `docs/SPL_TOKEN_MATRIX.json`. + +Le processor p-token lit les champs numériques avec des contrôles de longueur minimale et peut +donc ignorer des octets suffixes selon l'opération. Le décodeur les conserve et les signale au lieu +de les accepter silencieusement. `Batch` est découpé selon les paires `account_count/data_len`, +interdit les données enfant vides et appelle uniquement le processor interne, ce qui rend un batch +imbriqué invalide. Le décodeur ajoute des bornes défensives de 64 enfants, 512 comptes cumulés et +16 384 octets par instruction. + +La résolution `3.0.0` et les tests différentiels du décodeur ont été confirmés dans le workspace le +15 juillet 2026. `kb_executor_spl_token` consomme les builders officiels de cette même interface. +Les variantes `InitializeMint`, `InitializeAccount`, `InitializeMultisig` et `InitializeAccount2` +restent decode-only dans la politique d'exécution ; les opérations anciennes toujours publiées et +utilisables, comme `Transfer` ou `SetAuthority`, ne sont pas confondues avec des opérations +obsolètes. + +Les signatures officielles de `unwrap_lamports` et `batch` ont été auditées au commit `46eaacd`. +Leur constructibilité est donc prouvée. Leur déploiement sur le programme classique Devnet a été +observé le 16 juillet 2026 par deux simulations sans signature ni envoi : `Batch` avec un enfant +`TransferChecked` de montant nul a réussi en 270 compute units, puis `UnwrapLamports` d'un lamport +depuis un compte wrapped SOL auxiliaire classique a réussi en 140 compute units. Localnet, Testnet +et Mainnet restent des preuves séparées. L'exécuteur ne bloque aucun cluster et laisse +l'autorisation d'envoi aux politiques communes. + +Sur Devnet, le programme classique a également confirmé un `TransferChecked` complet et un +lifecycle sans ATA couvrant `InitializeAccount3`, `MintToChecked`, `TransferChecked`, +`ApproveChecked`, `Revoke`, `BurnChecked` et `CloseAccount`. Sur Mainnet, un corpus canonique de 370 +instructions a été décodé et matérialisé sans entrée unsupported, failed ou unmatched. Localnet n'a +pas été exercé. La matrice conserve donc indépendamment la version d'interface, le processor de +référence, le builder, les observations réelles et le résultat de simulation/soumission par +instruction. + +### SPL Associated Token Account + +Le catalogue workspace déclare `spl-associated-token-account-interface ^2.0` avec la feature +`borsh`. Cargo a résolu la version `2.0.0` pendant le jalon `0.4.5`. Les crates +`kb_decoder_spl_associated_token_account` et `kb_executor_spl_associated_token_account` consomment +réellement l'enum, les builders, le Program ID et le helper officiel de dérivation ; le pipeline et +la démo consomment ensuite leurs contrats typés sans recopier l'interface. + +L'interface publie exactement trois variantes Borsh actuelles : `Create` (`0`), +`CreateIdempotent` (`1`) et `RecoverNested` (`2`). Le processor accepte aussi historiquement une +donnée vide comme `Create`; cette forme reste décodable mais n'est pas produite par le builder +actuel. La dérivation canonique utilise les seeds `[wallet, Token Program ID, mint]` sous le Program +ID ATA. Le Token Program fait donc partie de l'adresse et distingue nécessairement les ATA +classiques des ATA Token-2022 d'un même wallet et mint. + +Le décodeur conserve les comptes, flags, doublons, chemins outer/inner, transactions échouées, +adresses observées et dérivées, ainsi que les rôles distincts de `RecoverNested`. Les tests +différentiels comparent les trois builders et les vecteurs PDA au helper officiel. L'exécuteur +utilise les mêmes builders pour les trois variantes, impose simulation et dry-run par défaut, puis +laisse au pipeline stateful les contrôles de mint, owner, compte existant, rent, signataires et +postconditions. + +Les preuves Devnet du 16 juillet 2026 couvrent `CreateIdempotent` pour SPL Token classique et +Token-2022, y compris création, réutilisation, replay post-exécution idempotent et ATA Token-2022 +avec extension `ImmutableOwner`. Elles couvrent aussi un `RecoverNested` classique contrôlé : le +solde brut a été transféré vers l'ATA wallet, le compte nested a été fermé et les deux faits +lifecycle/risk ont été matérialisés sans dupliquer les CPI SPL Token. Le décodage général des +instructions et extensions Token-2022 reste réservé à `0.4.6`. + +### Token-2022 — audit initial `0.4.6-pre.001` + +Le catalogue workspace déclare `spl-token-2022-interface ^3.1` avec `serde` et +`spl-elgamal-registry-interface ^0.2` sans feature additionnelle. La résolution opérateur confirme +respectivement `3.1.1` avec `default + serde` et `0.2.1` avec `default`, consommées par +`kb_decoder_spl_token_2022` pour poursuivre l'audit. La matrice inventorie 48 tags de premier +niveau et 29 types TLV de production, dont le récent `PermissionedBurnExtension` `46` et son TLV +`PermissionedBurn` `28`. + +Les sous-discriminants, comptes, layouts, tailles, builders et processors restent explicitement +`pending`. L'interface ElGamal `0.2.1` confirme le Program ID +`regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg` et la seed PDA `elgamal-registry`. Le registre +garde toutefois une matrice, un dispatch et une politique d'exécution séparés du programme natif +ZK ElGamal Proof. Les TLV Token Metadata/Token Group et les pointeurs Transfer Hook ne donnent +aucun Program ID universel aux programmes externes qui implémentent ces interfaces. + +L'interface Token-2022 `3.1.1` publie le commit exact +`e18f9c6f9bf6044b934f48e3090e8e59e4820f02` (`interface@v3.1.1`). Au même commit, le processor +porte la version `11.0.0` et son lock source résout Token Group `0.7.2`, Token Metadata `1.0.0` et +le registre ElGamal `0.2.1`. Son dispatch tente d'abord les 48 tags Token-2022, puis les cinq +discriminants Token Metadata et les quatre discriminants Token Group. Cette incorporation ne donne +pas de Program ID universel aux interfaces : elle décrit seulement leur exécution directe par +Token-2022. L'égalité avec les binaires cluster reste à prouver séparément. + +Le `Cargo.lock` opérateur confirme le graphe réellement consommé par l'interface `3.1.1` : ZK +ElGamal Proof `0.1.3`, extraction de preuve confidentielle `0.6.1`, Token Group `0.7.2`, Token +Metadata `1.0.1` et TLV `0.9.1`. Token Metadata apparaît directement sous +`spl-token-2022-interface` dans `cargo tree -p ... -e features`. Le processor source `11.0.0` +audité figeait encore Token Metadata `1.0.0`. La publication officielle `1.0.1` ne change ni wire, +ni discriminant, ni builder : elle ajoute seulement, avant Borsh, une validation bornée des +longueurs de chaînes de `Initialize`, `UpdateField` et `RemoveKey`. Cette garde plus sûre devient +normative pour le décodeur ; `UpdateAuthority` et `Emit` restent sans chaîne. + +### ZK ElGamal Proof + +`kb_decoder_solana_core` consomme directement `solana-zk-elgamal-proof-interface ^0.1`. L’enum `ProofInstruction` fournit les treize discriminants et les types de `proof_data` fournissent les tailles `Pod` exactes pour séparer le contexte du corps de preuve inline. Le runtime Agave reste la source normative pour la sélection entre preuve inline et preuve stockée dans un compte, les positions de comptes optionnels et l’acceptation des octets suffixes de `CloseContextState`. + +Aucune dépendance à `bytemuck` n’est ajoutée directement : le décodeur ne convertit pas les octets en preuve cryptographique et n’appelle aucun vérificateur. Il utilise uniquement `size_of` sur les types officiels, puis conserve longueur, SHA-256 et préfixe borné. Le contenu d’un compte de preuve externe n’est pas récupéré par RPC pendant un replay historique. + +### ZK Token Proof historique + +`kb_decoder_solana_core` ne dépend plus de `solana-zk-token-sdk`. Cette crate historique est dépréciée et entraînait `bincode` transitivement. Le workspace conserve une table wire locale limitée aux tags `0..16`, aux tailles exactes de contexte/preuve et à l’offset `u32 LE`, auditée contre la version historique `3.1.14`; aucun code de preuve ni sérialiseur historique n’est recopié. + +Le comportement est documenté depuis deux références runtime distinctes : Agave `v2.0.0` pour le vérificateur historique et Agave `v4.1.1` pour le stub actuel sans effet. Aucun corpus mainnet n’ayant été observé, les tests sont synthétiques et n’exigent pas de backfill. + +### Stake Program + +`solana-stake-interface 4.3.1` publie `StakeInstruction` avec Serde et conserve ses builders derrière la feature `bincode`. La feature `wincode` fournit les schémas nécessaires aux types d’état, mais pas directement à l’enum d’instruction. Le décodeur et l’exécuteur consomment donc l’ID et les types sémantiques officiels, avec un miroir wire privé commun dans son ordre de variantes et de champs. Aucun helper `bincode` n’est activé. + +L’exécuteur reproduit les vingt-quatre helpers clients actuels : initialize/create, checked/seed, split/merge, create-and-delegate, quatre authorize, delegate, withdraw, deactivate, lockup, minimum delegation, delinquent deactivation et move. Les helpers `redelegate` et `redelegate_with_seed` sont explicitement exclus parce que la source officielle les marque dépréciés et « will not be enabled » ; ils restent uniquement dans le miroir historique du décodeur. + +Le contrat des comptes est aligné sur les builders officiels. Les contrôles stateful du processor BPF — état de compte, rent, epochs, compatibilité et autorités courantes — restent à l’orchestration localnet/devnet. + +### Config Program + +`solana-config-interface 2.x` expose son module d’instruction derrière sa feature `bincode`. Le décodeur et l’exécuteur conservent donc un wire local borné pour `ConfigKeys` : compact-u16 canonique, booléens stricts, ordre exact des signataires, rejet des doublons et payload métier opaque. Le builder de création reproduit la taille `max_config_space + serialized_size(ConfigKeys)` et l’initialisation `ConfigKeys vide + T::default()` à partir d’octets déjà sérialisés ; il n’active aucun helper `bincode`. + +### BPF Loader v2 + +`solana-loader-v2-interface 3.x` publie ses helpers d’instruction derrière `bincode`. Le décodeur conserve le layout local vérifié tant qu’aucun schéma officiel `wincode` n’est disponible. + +### Feature Gate + +`solana-feature-gate-interface 4.x` publie la séquence d’activation `transfer -> allocate -> assign` et `RevokePendingActivation`, mais les helpers restent derrière sa feature `bincode`. L’exécuteur utilise `Feature::size_of()` et reproduit ces instructions à partir des interfaces System étroites ; la révocation reste exactement `vec![0]` avec feature, incinerator et System Program dans l’ordre officiel. + +### Slashing Program + +La source normative est SIMD-0204 et le contrat du programme Slashing publié : deux instructions, un payload DuplicateBlockProof de 305 octets, un report PDA dérivé de `node_pubkey + slot LE + type`, et une instruction Ed25519 précédente dont les offsets pointent vers le payload Slashing. Aucune crate de sérialisation générale n’est nécessaire. L’exécuteur construit le plan atomique et laisse au pipeline stateful le compte de preuve, le rent et les fenêtres epoch. + +### ZK ElGamal Proof dans l’exécuteur + +`kb_executor_solana_core` dépend directement de `solana-zk-elgamal-proof-interface ^0.1` pour les types POD et leurs tailles. Les layouts d’instruction sont simples et officiels : discriminant + preuve inline, ou discriminant + offset `u32 LE`; les comptes de contexte suivent `ContextStateInfo`. La fermeture reproduit le builder officiel. Aucune vérification cryptographique n’est recalculée côté client. + +### Loaders v3 et v4 + +La migration Loader v3 est terminée dans `pre.020` : le décodeur utilise `wincode::deserialize_exact` et l’exécuteur utilise les helpers officiels de `solana-loader-v3-interface 8.x`. Les payloads tronqués, suffixés, les longueurs et les contrats de comptes restent bornés par les tests. + +Loader v4 conserve un parser et un constructeur locaux limités aux sept discriminants publiés. Le layout est suffisamment petit pour être audité explicitement : tag `u32 LE`, `Write { offset, Vec }`, `Copy { destination_offset, source_offset, length }`, `SetProgramLength { new_size }` et quatre variantes unitaires. Cette exception évite d’activer `bincode` uniquement pour appeler les helpers de l’interface. + +## `solana-sdk` + +`solana-sdk` avec la feature `full` peut rester disponible au niveau workspace pour les applications ou outils qui ont réellement besoin de l’agrégat complet. Les crates feuille doivent préférer les interfaces étroites afin de réduire les features transitives, les temps de compilation et les risques de dépendances inutilisées. + +## Vérification avant ajout + +Pour chaque nouvelle interface : + +1. lire ses features et ses modules réellement compilés ; +2. identifier le format officiel de sérialisation ; +3. confirmer la version compatible avec Agave ciblée ; +4. ajouter la dépendance uniquement à la crate propriétaire ; +5. créer une fixture depuis l’encodeur officiel lorsque disponible ; +6. tester les payloads tronqués, inconnus, suffixés et les comptes invalides ; +7. documenter toute divergence entre l’interface et le runtime. + +### Metaplex Token Metadata — audit et premier décodage `0.4.7-pre.002` + +Le catalogue workspace déclare `mpl-token-metadata ^5.1` avec la seule feature `serde`. La publication officielle résolue reste `5.1.1`; elle fournit les modules générés `accounts`, `instructions`, `types` et `errors` issus de l’IDL Metaplex, sans introduire Anchor. La crate officielle utilise Borsh `< 1.0`; le décodeur référence donc explicitement `borsh 0.10` sous l’alias workspace `borsh_0_10`, distinct de Borsh 1.x utilisé par d’autres interfaces du workspace. + +Le dépôt officiel `metaplex-foundation/mpl-token-metadata` confirme le Program ID `metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s`. L’IDL officiel audité porte la version `1.14.0` et le blob `5df4a24f62c2743125be096cc174680790c92c18`; l’inventaire Rust généré des instructions porte le blob `3c42eec629f82e44ba690a7c4bd2177cc78f1d49`. + +`kb_decoder_metadata_metaplex_token_metadata` implémente désormais `InstructionDecoder` pour `CreateMetadataAccountV3` et `UpdateMetadataAccountV2`. Les arguments sont lus avec les types Borsh officiels et projetés avec la feature `serde`; les payloads, chaînes, créateurs, frais, comptes et suffixes sont bornés. L’enregistrement dans le registre runtime reste différé jusqu’à validation de ce premier groupe. Les metadata Token-2022 incorporées, Metaplex Core, Bubblegum et le JSON externe restent des provenances ou composants distincts. + diff --git a/migration/khadhroony-bot2-reference/docs/SOLANA_NATIVE_RPC_CLOSURE_AUDIT.md b/migration/khadhroony-bot2-reference/docs/SOLANA_NATIVE_RPC_CLOSURE_AUDIT.md new file mode 100644 index 0000000..5341fe4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/SOLANA_NATIVE_RPC_CLOSURE_AUDIT.md @@ -0,0 +1,106 @@ + + + +# Audit de clôture native, exécution et RPC — `0.4.2` + +## Décision + +Le jalon `0.4.2` est clôturé. L’audit distingue quatre contrats indépendants : + +1. les opérations futures constructibles par `kb_executor_solana_core` ; +2. les instructions passées décodables par `kb_decoder_solana_core` ; +3. les méthodes standard HTTP/WebSocket exposées par `kb_rpc` ; +4. l’orchestration de sécurité, de simulation, d’envoi et de replay post-exécution. + +Une méthode transportable via JSON brut n’est pas considérée comme un adaptateur typé. Un builder stateless n’autorise pas à lui seul une opération dépendant d’un compte, d’une autorité, du rent, d’un slot ou d’un epoch. Une opération dangereuse peut être complète dans la bibliothèque tout en restant absente de l’UI et désactivée sur Mainnet. + +## Résultat synthétique + +| Domaine | Inventaire canonique | Contrat final `0.4.2` | +|-------------------------|-------------------------------------------------------------------------:|------------------------------------------------------------------------------------------------| +| Exécuteur natif | 18 surfaces, 14 appelables, 4 non invocables/historiques, 109 opérations | égalité imposée par test Rust ; builders comparés aux interfaces ou layouts officiels | +| Décodeur natif | 18 surfaces, 121 déclarations | égalité imposée avec `kb_program_ids::native_program_ids()` et `SolanaCoreDecoder::coverage()` | +| HTTP JSON-RPC | 52 méthodes | 52 contrats typés configurables, 0 méthode standard limitée au JSON brut | +| WebSocket JSON-RPC | 9 paires / 18 méthodes | 9 requêtes, 9 notifications et 9 runtimes persistants typés | +| Préflight stateful | ALT, Config, Feature, Slashing, ZK ElGamal | rapports `NotRequired` / `Ready` / `Blocked`, Localnet/Devnet uniquement | +| Parcours mutable validé | System transfer Devnet | simulation, signature, envoi, confirmation, canonical, core et decode replay réussis | + +## Exécuteur Solana Core + +`docs/NATIVE_SOLANA_EXECUTION_MATRIX.json` et `SOLANA_CORE_OPERATION_CODES` imposent : + +- exactement 18 surfaces ; +- exactement 14 surfaces appelables et 4 surfaces sans instruction client ; +- exactement 109 codes d’opération uniques ; +- Program IDs présents dans `kb_program_ids` ; +- contrat de construction, politique, tests, validation cluster et décision UI pour chaque opération ; +- distribution des préflights `27 none / 14 required / 68 future`. + +Les surfaces sans builder client sont BPF Loader v1, BPF Loader v2, Native Loader et l’ancien ZK Token Proof Program. Stake `Redelegate` reste historique `decode-only`. Cette classification évite d’inventer des instructions qui ne sont plus ou n’ont jamais été appelables par un client moderne. + +Les entrées `future` ne signifient pas « non implémenté ». Elles signifient que le wire et le plan sont couverts offline, mais que l’activation mutable exige encore une preuve stateful par scénario sur Localnet/Devnet. Aucune de ces opérations n’est activée sur Mainnet par `0.4.2`. + +## Décodeur Solana Core + +`docs/NATIVE_SOLANA_DECODER_MATRIX.json` couvre exactement les 18 surfaces du registre natif et 121 déclarations. Les tests de complétude parcourent les enums officielles ou les tables wire auditées pour System, Compute Budget, ALT, Stake, Vote, loaders, précompiles, Slashing et preuves ZK. + +Stake Config reste un compte bien connu non exécutable. Il ne possède ni dispatch d’instruction ni plan d’exécution. + +## HTTP JSON-RPC standard + +`docs/SOLANA_STANDARD_RPC_MATRIX.json` et `STANDARD_HTTP_METHODS` enregistrent 52 méthodes. Chacune possède un contrat de requête/résultat propre à `kb_rpc`. Les options facultatives restent indépendantes : commitment, `minContextSlot`, encodage, `dataSlice`, filtres, contexte, tri, niveau de détail, rewards et version maximale de transaction. + +Les DTO Agave ne font pas partie de l’API publique. La voie JSON brute reste une primitive interne ou d’extension fournisseur, pas un substitut à une méthode standard absente. + +## WebSocket JSON-RPC standard + +Les neuf paires standard possèdent paramètres, notifications et runtime persistant dans `kb_rpc`. `WsSession` fournit : + +- une socket multiplexée ; +- des identifiants locaux stables et distants remappables ; +- unsubscribe explicite ; +- timeouts bornés ; +- ping/pong et fermeture ; +- reconnexion exponentielle bornée ; +- réabonnement ; +- suppression terminale de `signatureSubscribe`. + +`blockSubscribe`, `slotsUpdatesSubscribe` et `voteSubscribe` sont désactivées par défaut, mais activables lorsqu’un endpoint les annonce explicitement. La première souscription réelle sert de probe paresseux. Une réponse `Method not found`, `not enabled`, `unavailable` ou `unsupported` désactive uniquement la capacité concernée pour la session ; les erreurs de paramètres, d’authentification ou de rate limit ne la désactivent pas. + +## Frontière Tauri et TS-rs + +Les payloads actifs de `kb_app_demo` et `kb_config` transmis par JSON utilisent des `number`, pas des `bigint`. Les valeurs d’identifiants sont contrôlées avec `Number.isSafeInteger` avant invocation. Les types génériques hors frontière Tauri conservent leurs contrats exacts lorsqu’ils ne sont pas sérialisés par `JSON.stringify`. + +Le navigateur SQL applique maintenant la limite maximale reçue du backend avant l’appel Tauri. Une valeur supérieure à 5 000 produit un diagnostic frontend et n’atteint plus PostgreSQL. + +## Preuves de validation + +Validations fournies le 13 juillet 2026 : + +| Crate / contrôle | Résultat | +|------------------------------------|----------:| +| `kb_program_ids` | 5 tests | +| `kb_decoder_solana_core` | 116 tests | +| `kb_executor_solana_core` | 88 tests | +| `kb_execution_api` | 22 tests | +| `kb_execution_safety` | 15 tests | +| `kb_execution_solana` | 12 tests | +| `kb_rpc` | 113 tests | +| `kb_pipeline` | 56 tests | +| `kb_config` | 41 tests | +| `kb_app_demo` | 88 tests | +| `kb_store_pg` avec PostgreSQL réel | 45 tests | +| `cargo clippy --all-targets` | propre | + +Le test Devnet opt-in a exécuté un transfert de 1 000 000 lamports avec airdrop nul depuis un wallet préfinancé. Il a confirmé simulation, signature, envoi, confirmation, hydratation canonique, extraction core et decode replay. `cargo tauri dev` a démarré Vite, initialisé les treize tables attendues et exercé les sessions WebSocket. + +L’archive de logs Mainnet/Tauri confirme des unsubscribe réels pour `slot`, `root` et `program`. Le refus de sélectionner un autre endpoint tant qu’une session est active est un garde-fou volontaire. L’ancien échec `JSON.stringify cannot serialize BigInt` n’est plus présent. + +## Limites conservées après clôture + +- Mainnet reste désactivé par défaut et exige une politique explicite. +- Les opérations stateful administratives non exercées sur cluster restent hors UI et bloquées avant toute future activation mutable. +- Le parcours validé utilise une transaction legacy avec recent blockhash ; durable nonce et transactions v0 sont couverts par les bibliothèques mais nécessitent leurs propres parcours opérateur avant exposition. +- Les méthodes WebSocket instables dépendent du nœud et peuvent être désactivées automatiquement à l’exécution. + +La clôture de `0.4.2` n’ouvre aucune version suivante et ne fixe aucun plan de sources historiques externes. diff --git a/migration/khadhroony-bot2-reference/docs/SOLANA_STANDARD_RPC_MATRIX.json b/migration/khadhroony-bot2-reference/docs/SOLANA_STANDARD_RPC_MATRIX.json new file mode 100644 index 0000000..440df20 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/SOLANA_STANDARD_RPC_MATRIX.json @@ -0,0 +1,430 @@ +{ + "file": "docs/SOLANA_STANDARD_RPC_MATRIX.json", + "version": 3, + "release": "0.4.2-pre.025", + "canonical_reference": "https://solana.com/docs/rpc", + "http_method_count": 52, + "http_typed_adapter_count": 52, + "http_raw_json_count": 0, + "ws_subscription_pair_count": 9, + "ws_method_count": 18, + "ws_unstable_subscription_count": 3, + "http_methods": [ + { + "method": "getAccountInfo", + "category": "accounts", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getBalance", + "category": "accounts", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getLargestAccounts", + "category": "accounts", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getMinimumBalanceForRentExemption", + "category": "accounts", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getMultipleAccounts", + "category": "accounts", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getProgramAccounts", + "category": "accounts", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getTokenAccountBalance", + "category": "tokens", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getTokenAccountsByDelegate", + "category": "tokens", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getTokenAccountsByOwner", + "category": "tokens", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getTokenLargestAccounts", + "category": "tokens", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getTokenSupply", + "category": "tokens", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getFeeForMessage", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getLatestBlockhash", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getRecentPrioritizationFees", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getSignaturesForAddress", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getSignatureStatuses", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getTransaction", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getTransactionCount", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "isBlockhashValid", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "requestAirdrop", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "sendTransaction", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "simulateTransaction", + "category": "transactions", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getBlock", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getBlockCommitment", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getBlockHeight", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getBlockProduction", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getBlocks", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getBlocksWithLimit", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getBlockTime", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getFirstAvailableBlock", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getRecentPerformanceSamples", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "minimumLedgerSlot", + "category": "blocks", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getClusterNodes", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getEpochInfo", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getEpochSchedule", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getGenesisHash", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "dedicated execution/acquisition adapter plus HttpClient/HttpEndpointPool" + }, + { + "method": "getHealth", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getHighestSnapshotSlot", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getIdentity", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getLeaderSchedule", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getMaxRetransmitSlot", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getMaxShredInsertSlot", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getSlot", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getSlotLeader", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getSlotLeaders", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getVersion", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getVoteAccounts", + "category": "cluster", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getInflationGovernor", + "category": "economics", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getInflationRate", + "category": "economics", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getInflationReward", + "category": "economics", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getStakeMinimumDelegation", + "category": "economics", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + }, + { + "method": "getSupply", + "category": "economics", + "contract": "typed_adapter", + "implementation": "configurable kb_rpc request/result contract plus typed HttpClient/HttpEndpointPool transport" + } + ], + "ws_subscriptions": [ + { + "subscribe_method": "accountSubscribe", + "unsubscribe_method": "accountUnsubscribe", + "notification_method": "accountNotification", + "stability": "stable", + "request_contract": "typed_adapter", + "persistent_runtime": true, + "library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription", + "notification_contract": "typed_adapter", + "explicit_capability_required": false + }, + { + "subscribe_method": "blockSubscribe", + "unsubscribe_method": "blockUnsubscribe", + "notification_method": "blockNotification", + "stability": "unstable", + "request_contract": "typed_adapter", + "persistent_runtime": true, + "library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription", + "notification_contract": "typed_adapter", + "explicit_capability_required": true + }, + { + "subscribe_method": "logsSubscribe", + "unsubscribe_method": "logsUnsubscribe", + "notification_method": "logsNotification", + "stability": "stable", + "request_contract": "typed_adapter", + "persistent_runtime": true, + "library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription", + "notification_contract": "typed_adapter", + "explicit_capability_required": false + }, + { + "subscribe_method": "programSubscribe", + "unsubscribe_method": "programUnsubscribe", + "notification_method": "programNotification", + "stability": "stable", + "request_contract": "typed_adapter", + "persistent_runtime": true, + "library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription", + "notification_contract": "typed_adapter", + "explicit_capability_required": false + }, + { + "subscribe_method": "rootSubscribe", + "unsubscribe_method": "rootUnsubscribe", + "notification_method": "rootNotification", + "stability": "stable", + "request_contract": "typed_adapter", + "persistent_runtime": true, + "library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription", + "notification_contract": "typed_adapter", + "explicit_capability_required": false + }, + { + "subscribe_method": "signatureSubscribe", + "unsubscribe_method": "signatureUnsubscribe", + "notification_method": "signatureNotification", + "stability": "stable", + "request_contract": "typed_adapter", + "persistent_runtime": true, + "library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription", + "notification_contract": "typed_adapter", + "explicit_capability_required": false + }, + { + "subscribe_method": "slotSubscribe", + "unsubscribe_method": "slotUnsubscribe", + "notification_method": "slotNotification", + "stability": "stable", + "request_contract": "typed_adapter", + "persistent_runtime": true, + "library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription", + "notification_contract": "typed_adapter", + "explicit_capability_required": false + }, + { + "subscribe_method": "slotsUpdatesSubscribe", + "unsubscribe_method": "slotsUpdatesUnsubscribe", + "notification_method": "slotsUpdatesNotification", + "stability": "unstable", + "request_contract": "typed_adapter", + "persistent_runtime": true, + "library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription", + "notification_contract": "typed_adapter", + "explicit_capability_required": true + }, + { + "subscribe_method": "voteSubscribe", + "unsubscribe_method": "voteUnsubscribe", + "notification_method": "voteNotification", + "stability": "unstable", + "request_contract": "typed_adapter", + "persistent_runtime": true, + "library_runtime": "kb_rpc::WsSession multiplexing, explicit unsubscribe, bounded reconnect and resubscription", + "notification_contract": "typed_adapter", + "explicit_capability_required": true + } + ], + "ws_typed_request_count": 9, + "ws_typed_notification_count": 9, + "ws_persistent_runtime_count": 9 +} diff --git a/migration/khadhroony-bot2-reference/docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json b/migration/khadhroony-bot2-reference/docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json new file mode 100644 index 0000000..646a034 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json @@ -0,0 +1,199 @@ +{ + "file": "docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json", + "version": 9, + "schemaVersion": 1, + "programId": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", + "systemProgramId": "11111111111111111111111111111111", + "supportedTokenPrograms": [ + {"code": "spl_token", "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "scope": "classic_token_account_lifecycle_only"}, + {"code": "spl_token_2022", "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", "scope": "ata_lifecycle_only_without_general_token_2022_instruction_or_extension_decode"} + ], + "interface": { + "workspaceConstraint": "^2.0", + "resolvedVersion": "2.0.0", + "resolutionEvidence": "docs_rs_latest_published_version_and_official_interface_tag", + "workspaceCargoResolutionStatus": "confirmed_by_operator_cargo_tree_2026-07-16", + "features": ["borsh"], + "bincodeEnabled": false, + "officialTag": "interface@v2.0.0", + "releaseCommit": "250da0e21a9853a7afddba85658ed8a9119c71c9", + "releaseDate": "2025-08-08", + "instructionSource": "https://github.com/solana-program/associated-token-account/blob/interface@v2.0.0/interface/src/instruction.rs", + "addressSource": "https://github.com/solana-program/associated-token-account/blob/interface@v2.0.0/interface/src/address.rs", + "publishedDocumentation": "https://docs.rs/spl-associated-token-account-interface/2.0.0/spl_associated_token_account_interface/" + }, + "processor": { + "auditedRelease": "program@v8.0.0", + "releaseCommit": "0b867b5", + "releaseDate": "2025-10-29", + "source": "https://github.com/solana-program/associated-token-account/blob/program@v8.0.0/program/src/processor.rs", + "deploymentBinaryEquality": {"localnet": "not_tested", "devnet": "not_proven", "mainnet": "not_proven"}, + "note": "Interface publication, processor source, cluster deployment and successful execution are tracked independently." + }, + "wire": { + "format": "Borsh enum with unit variants encoded as one u8 ordinal", + "strictSuffixPolicy": "reject", + "legacyEmptyCreate": {"decodeSupport": true, "builderEmits": false, "encodingHex": "", "status": "historical_runtime_and_transaction_parser_compatibility_to_be_reconfirmed_by_cluster_corpus"}, + "unknownOrTruncatedPolicy": "bounded diagnostic without lifecycle projection" + }, + "pdaDerivation": { + "programId": "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", + "seedOrder": ["wallet_address_bytes[32]", "token_program_id_bytes[32]", "mint_address_bytes[32]"], + "algorithm": "Pubkey::find_program_address", + "validationPolicy": "preserve observed address and report exact match or mismatch; never replace an observed account silently", + "differentialVectors": [ + {"wallet": "11111111111111111111111111111111", "mint": "So11111111111111111111111111111111111111112", "tokenProgramId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "expectedAta": "aqxoAhCwpy3oB1BpNw9hL1HdLYLgPpbPjzxDrrQj3Fs", "expectedBump": 254, "evidence": "PublicKey.findProgramAddressSync with the canonical official seed order"}, + {"wallet": "11111111111111111111111111111111", "mint": "So11111111111111111111111111111111111111112", "tokenProgramId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", "expectedAta": "2sZUUBGq1i6aE47ZoxCaCW89jmYm2EXLPPmNMgMDXHMS", "expectedBump": 250, "evidence": "PublicKey.findProgramAddressSync with the canonical official seed order"} + ] + }, + "limits": {"exactVariantCount": 3, "createRequiredAccountCount": 6, "recoverNestedRequiredAccountCount": 7, "maximumDiagnosticBytes": 256, "extraAccounts": "preserved_and_reported; never assigned an invented role"}, + "executionPreflight": { + "clusters": ["localnet", "devnet"], + "programIds": ["associated_token_program", "selected_token_program", "system_program_for_creation"], + "stateChecks": ["mint_exists_initialized_and_owned_by_selected_token_program", "canonical_pda_derivation", "strict_create_absence", "idempotent_absence_or_existing_account_compatibility", "recover_nested_three_account_relationships", "required_signers_available", "payer_balance_covers_rent_plus_fee_ceiling"], + "token2022Boundary": "base Mint and Token Account prefixes are validated while suffix extensions remain opaque; simulation determines extension-dependent runtime acceptance and exact account size", + "simulation": "required_before_signing_or_sending", + "defaultMode": "dry_run" + }, + "surfaceEquality": {"matrixVariantCount": 3, "officialInterfaceVariantCount": 3, "compiledDecoderCoverageStatus": "three_exact_compiled_instruction_declarations_operator_validated", "compiledExecutorCapabilityStatus": "three_exact_current_capabilities_operator_validated", "requiredFinalInvariant": "matrix == official interface enum == decoder coverage == executor capability declarations"}, + "instructions": [ + { + "name": "create", + "discriminant": 0, + "canonicalEncodingHex": "00", + "acceptedDecodeEncodingsHex": ["", "00"], + "status": "current_with_legacy_empty_wire_compatibility", + "accounts": [ + {"position": 0, "role": "funding_account", "signer": true, "writable": true, "expectedProgram": "system_account"}, + {"position": 1, "role": "associated_token_account", "signer": false, "writable": true, "expectedProgram": "derived_pda_before_creation_or_supported_token_program_after_creation"}, + {"position": 2, "role": "wallet_owner", "signer": false, "writable": false, "expectedProgram": "unconstrained_wallet_address"}, + {"position": 3, "role": "mint", "signer": false, "writable": false, "expectedProgram": "account_owned_by_token_program_at_runtime"}, + {"position": 4, "role": "system_program", "signer": false, "writable": false, "expectedAddress": "11111111111111111111111111111111"}, + {"position": 5, "role": "token_program", "signer": false, "writable": false, "allowedAddresses": ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"]} + ], + "addressRules": ["associated_token_account = PDA(wallet_owner, token_program, mint)", "observed associated_token_account is retained on mismatch"], + "tokenProgramDifferences": {"classic": "create and initialize a classic token account", "token2022": "size is obtained from the Token-2022 mint and immutable-owner initialization is performed before account initialization; extensions themselves remain out of decoder scope"}, + "runtimeRules": ["fails if the associated account already exists", "funding account pays rent and creation cost", "mint and created account must be coherent with the selected token program"], + "event": "spl_associated_token_account.create", + "authorizedProjections": ["token_accounts:ata_created"], + "forbiddenProjections": ["token_balance_snapshot", "duplicate_spl_token_initialize_account_cpi", "token_2022_extension_fact"], + "officialBuilder": {"available": true, "function": "create_associated_token_account", "canonicalWireOnly": true}, + "executorSupport": {"status": "supported", "operationCode": "spl_associated_token_account.create", "builder": "create_associated_token_account", "simulation": "required", "defaultMode": "dry_run"}, + "proofs": {"synthetic": ["canonical_00", "legacy_empty", "classic_token_program", "token_2022_program", "wrong_ata", "missing_account", "extra_account", "duplicate_account", "wrong_flags", "unknown_suffix", "outer", "inner_cpi", "committed", "failed_intent"], "localnet": "not_tested", "devnet": "not_tested", "mainnetSignatures": []} + }, + { + "name": "create_idempotent", + "discriminant": 1, + "canonicalEncodingHex": "01", + "acceptedDecodeEncodingsHex": ["01"], + "status": "current", + "accounts": [ + {"position": 0, "role": "funding_account", "signer": true, "writable": true, "expectedProgram": "system_account"}, + {"position": 1, "role": "associated_token_account", "signer": false, "writable": true, "expectedProgram": "derived_pda_or_supported_token_program"}, + {"position": 2, "role": "wallet_owner", "signer": false, "writable": false}, + {"position": 3, "role": "mint", "signer": false, "writable": false}, + {"position": 4, "role": "system_program", "signer": false, "writable": false, "expectedAddress": "11111111111111111111111111111111"}, + {"position": 5, "role": "token_program", "signer": false, "writable": false, "allowedAddresses": ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"]} + ], + "addressRules": ["associated_token_account = PDA(wallet_owner, token_program, mint)", "existing account must retain the selected token program, wallet owner and mint"], + "tokenProgramDifferences": {"classic": "existing classic account is accepted only when owner and mint match", "token2022": "existing Token-2022 account is accepted only when owner and mint match; no extension semantics are inferred"}, + "runtimeRules": ["creates when absent", "succeeds without recreating when an initialized compatible ATA already exists", "fails when an existing account has a different wallet owner, mint or account owner program"], + "event": "spl_associated_token_account.create_idempotent", + "authorizedProjections": ["token_accounts:ata_created_or_reused_idempotently"], + "forbiddenProjections": ["token_balance_snapshot", "duplicate_spl_token_initialize_account_cpi", "token_2022_extension_fact"], + "officialBuilder": {"available": true, "function": "create_associated_token_account_idempotent", "canonicalWireOnly": true}, + "executorSupport": {"status": "supported", "operationCode": "spl_associated_token_account.create_idempotent", "builder": "create_associated_token_account_idempotent", "simulation": "required", "defaultMode": "dry_run"}, + "proofs": {"synthetic": ["canonical_01", "absent_account", "compatible_existing_account", "conflicting_existing_owner", "conflicting_existing_mint", "conflicting_existing_program", "classic_token_program", "token_2022_program", "wrong_ata", "wrong_flags", "unexpected_suffix", "outer", "inner_cpi", "committed", "failed_intent"], "localnet": "not_tested", "devnet": ["5Vw4QFpMa8oJ8MsLrvDg4RFzxqUh27mTFRFzzwJFoA4uAry8rZQc6GP92aidzWugn3CeZ5NYNxDp7tZ6bh8XyN1P", "2H9VfhDySdXMteazm2P3s2fTttJBQzMJinQtKqsLtkjDg7zbFFgiWG81UxRQF4va3bbR2pUV4QGo2qErisR8CPbP", "3wWTkx45LoDR3UaU8VUfsWxwHuLFyJyhrnDrmijCa7pXPdmJKz3rQ1LdyDvb78dvNRiKqw5ee7HuZ6Gjj1Gpfv6q", "bMMjoHcwbaTvLXerdQ4kbGXGb7wydiTyvSEwwmoKKLQcRWWBzqGUMYHZztonNnER1iWZ7yKp9p3neeMhypX4QAJ", "4RZKA3KwEbSqgU8UnemTgvd3fBgJmoSK2nmaaTNP8nJGTVh6DrFwrHVZYCBvLFvhvzUdi9p1KRFe4hHqYt7JbVSG"], "mainnetSignatures": ["2F9RvuCkevfJN7ju252hAWoCE58wagCXCWHQF9Wsdtsxvp3HcuNJQFCwU7zGqe3z3b8pv3vPp4xY5EH7KLcaHQCY"], "realCorpusStatus": "controlled classic and Token-2022 CreateIdempotent creation then compatible-existing reuse confirmed, materialized once per signature and replayed idempotently; search-discovered Token-2022 outer transaction remains additional corpus"} + }, + { + "name": "recover_nested", + "discriminant": 2, + "canonicalEncodingHex": "02", + "acceptedDecodeEncodingsHex": ["02"], + "status": "current_recovery_operation", + "accounts": [ + {"position": 0, "role": "nested_associated_token_account", "signer": false, "writable": true}, + {"position": 1, "role": "nested_mint", "signer": false, "writable": false}, + {"position": 2, "role": "wallet_nested_mint_associated_token_account", "signer": false, "writable": true}, + {"position": 3, "role": "owner_associated_token_account", "signer": false, "writable": false}, + {"position": 4, "role": "owner_mint", "signer": false, "writable": false}, + {"position": 5, "role": "wallet_owner", "signer": true, "writable": true}, + {"position": 6, "role": "token_program", "signer": false, "writable": false, "allowedAddresses": ["TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"]} + ], + "addressRules": ["owner_associated_token_account = PDA(wallet_owner, token_program, owner_mint)", "nested_associated_token_account = PDA(owner_associated_token_account, token_program, nested_mint)", "wallet_nested_mint_associated_token_account = PDA(wallet_owner, token_program, nested_mint)"], + "tokenProgramDifferences": {"classic": "transfer the complete nested balance and close the nested classic token account", "token2022": "same ATA address relationships through Token-2022 CPIs; extension-specific transfer or close restrictions can prevent recovery and are not decoded as ATA facts"}, + "runtimeRules": ["all three derived addresses must match their observed accounts", "owner and nested token accounts must be initialized and coherent with their respective mints", "wallet owner must sign", "complete nested token balance is moved to the wallet destination ATA", "nested account is closed and its lamports are returned to the wallet"], + "event": "spl_associated_token_account.recover_nested", + "authorizedProjections": ["token_accounts:nested_ata_recovered", "risk:nested_ata_anti_pattern_recovered"], + "forbiddenProjections": ["duplicate_spl_token_transfer_cpi", "duplicate_spl_token_close_account_cpi", "token_balance_snapshot", "token_2022_extension_fact"], + "officialBuilder": {"available": true, "function": "recover_nested", "canonicalWireOnly": true}, + "executorSupport": {"status": "supported", "operationCode": "spl_associated_token_account.recover_nested", "builder": "recover_nested", "simulation": "required", "defaultMode": "dry_run"}, + "proofs": {"synthetic": ["canonical_02", "classic_token_program", "token_2022_program", "valid_three_pda_relationships", "wrong_nested_ata", "wrong_destination_ata", "wrong_owner_ata", "owner_and_nested_mints_not_inverted", "missing_account", "extra_account", "duplicate_account", "wrong_flags", "unexpected_suffix", "outer", "inner_cpi", "committed", "failed_intent"], "localnet": "not_tested", "devnet": ["37LG6GdMRp5ECG8qf1RSYxmriJk75UtCwqwzXZWpArebkKo6BQvGhYEnbDh2A6KWiUaHK28Zy1gGGZ5zihvrHskM"], "mainnetSignatures": [], "realCorpusStatus": "controlled classic RecoverNested confirmed with positive raw balance transfer, nested ATA closure, owner and destination post-validation, two distinct ATA lifecycle and risk projections, and idempotent second replay", "controlledDevnetFixture": {"walletOwner": "DwuAapPLg6pEJ4QhmxvPg8rzkavmYdg5FW4dfEMcg8B2", "ownerMint": "So11111111111111111111111111111111111111112", "ownerAssociatedTokenAccount": "3tdXq1W7vEbbkxQmT1r2fc349uYti98zpwLKhp7FFbF5", "nestedMint": "3fd5CqP1w4Y1xeMoAe3apA7Jf5SbL3vboxv7ihyXhC9A", "nestedAssociatedTokenAccount": "AAyvGeKpq6i3kxMcScJLcN2u5iyAsspr731LhgXygti5", "walletNestedMintAssociatedTokenAccount": "69TP2QoSx2inueFAEb6npjumH5o3wGJQDSwwAJLLfyWp", "rawAmountTransferred": "1000000000", "nestedAccountClosed": true, "destinationUiBalanceAfter": "1", "materializedOutputCount": 2}} + } + ], + "offlineCorpus": { + "status": "implemented_and_operator_validated_in_decoder_unit_tests", + "wire": ["create_00", "legacy_empty_create", "create_idempotent_01", "recover_nested_02", "unknown_tag", "rejected_suffix"], + "tokenPrograms": ["spl_token_classic", "token_2022"], + "locations": ["outer", "inner_cpi"], + "transactionOutcomes": ["committed", "failed_uncommitted_intent"], + "accountShapes": ["canonical", "missing", "extra", "duplicate", "wrong_flags", "wrong_derived_address", "unsupported_token_program", "wrong_system_program"], + "derivation": ["create_single_pda", "recover_nested_three_independent_pda_relationships", "official_helper_differential_vectors"], + "limits": "diagnostic count and payload prefix are bounded; no arbitrary payload or account repair" + }, + "materializationContract": { + "committedOnly": true, + "idempotenceIdentity": "signature + instruction_path + program_id + normalized_operation + ata", + "projectionOwners": [ + { + "crate": "kb_materializer_token_accounts", + "facts": [ + "ata_created", + "ata_created_or_reused_idempotently", + "nested_ata_recovered" + ], + "justification": "ATA creation and recovery are stable token-account lifecycle facts; this crate already owns instruction-level token-account mutations." + }, + { + "crate": "kb_materializer_risk", + "facts": [ + "nested_ata_anti_pattern_recovered" + ], + "justification": "A committed RecoverNested proves the distinct stable risk fact that a nested ATA anti-pattern existed and was recovered; no score or speculative severity is added." + } + ], + "explicitNoProjection": [ + { + "crate": "kb_materializer_lifecycle", + "reason": "Native program lifecycle remains owned here; duplicating ATA lifecycle would overlap kb_materializer_token_accounts." + }, + { + "crate": "kb_materializer_admin", + "reason": "ATA instructions do not change a mint authority, freeze authority, close authority or multisig configuration." + }, + { + "crate": "kb_materializer_fees", + "reason": "ATA wire does not prove an exact rent or network-fee amount; core balance changes and execution validation retain those facts." + } + ], + "implementationStatus": "pre_004 executor and stateful preflight operator validated; pre_005 classic and Token-2022 CreateIdempotent Devnet orchestration plus Tauri validated; pre_006 classic RecoverNested Devnet orchestration validated", + "idempotentOutcomeLimit": "CreateIdempotent parent proves successful ensure semantics but cannot distinguish created from reused without sibling CPI correlation; the projection preserves created_or_reused and invents neither boolean.", + "parentChildOwnership": "ATA parent owns only associated-address lifecycle and the distinct nested-ATA risk fact; SPL Token or Token-2022 CPI observations own token initialize, transfer and close mutations.", + "noSnapshotClaim": true, + "databaseMigrationStatus": "not_justified" + }, + "executionContract": {"simulationRequired": true, "dryRunDefault": true, "clusterPolicyOwnedByCommonLayers": true, "rentAndFeeCeilingRequired": true, "signerDeduplicationDoesNotChangeMetaOrder": true, "confirmedStateReadRetryBound": 20, "canonicalHydrationRpcRetryBoundPerAttempt": 2, "postValidation": {"create": "ATA exists, is owned by selected token program, and contains expected wallet owner and mint", "createIdempotent": "same invariant whether the ATA was created or reused", "recoverNested": "owner ATA and destination remain coherent, nested ATA is absent, and no balance value is invented by ATA projection"}}, + "deploymentAudit": { + "localnet": {"status": "not_tested", "evidence": []}, + "devnet": {"status": "classic and Token-2022 CreateIdempotent creation and compatible-existing reuse confirmed; classic RecoverNested positive-balance recovery confirmed", "evidence": ["5Vw4QFpMa8oJ8MsLrvDg4RFzxqUh27mTFRFzzwJFoA4uAry8rZQc6GP92aidzWugn3CeZ5NYNxDp7tZ6bh8XyN1P", "2H9VfhDySdXMteazm2P3s2fTttJBQzMJinQtKqsLtkjDg7zbFFgiWG81UxRQF4va3bbR2pUV4QGo2qErisR8CPbP", "3wWTkx45LoDR3UaU8VUfsWxwHuLFyJyhrnDrmijCa7pXPdmJKz3rQ1LdyDvb78dvNRiKqw5ee7HuZ6Gjj1Gpfv6q", "bMMjoHcwbaTvLXerdQ4kbGXGb7wydiTyvSEwwmoKKLQcRWWBzqGUMYHZztonNnER1iWZ7yKp9p3neeMhypX4QAJ", "4RZKA3KwEbSqgU8UnemTgvd3fBgJmoSK2nmaaTNP8nJGTVh6DrFwrHVZYCBvLFvhvzUdi9p1KRFe4hHqYt7JbVSG", "37LG6GdMRp5ECG8qf1RSYxmriJk75UtCwqwzXZWpArebkKo6BQvGhYEnbDh2A6KWiUaHK28Zy1gGGZ5zihvrHskM"], "controlledToken2022Fixture": {"mint": "3DxKAUfCZkR9oRfMXeRNpiVL464eKbTieymBrVhsoKTE", "mintOwner": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", "mintBaseLength": 82, "walletOwner": "DwuAapPLg6pEJ4QhmxvPg8rzkavmYdg5FW4dfEMcg8B2", "associatedTokenAccount": "6WfCJPt7GZQyMJAUS1AMHJrD7Vid8e9xUEQVgi5Q9C9Y", "accountLength": 170, "accountExtension": "immutable_owner", "creationSlot": 476702448, "existingReuseSlot": 476703343}}, + "mainnet": {"status": "search_discovered_token_2022_create_idempotent_not_yet_replayed", "evidence": ["2F9RvuCkevfJN7ju252hAWoCE58wagCXCWHQF9Wsdtsxvp3HcuNJQFCwU7zGqe3z3b8pv3vPp4xY5EH7KLcaHQCY"]}, + "rateLimitRecovery": "preserve confirmed signatures and resume bounded hydration; never recreate accounts blindly after HTTP 429" + }, + "nonClaims": [ + "No general Token-2022 instruction or extension decoding is implemented in 0.4.5.", + "No Token balance or final account snapshot is reconstructed from ATA instructions.", + "No cluster binary equality is inferred from interface publication.", + "The controlled Token-2022 proof covers ATA lifecycle only and does not claim general Token-2022 instruction or extension decoding.", + "No dedicated SQL table is justified by this audit tranche." + ] +} diff --git a/migration/khadhroony-bot2-reference/docs/SPL_ELGAMAL_REGISTRY_MATRIX.json b/migration/khadhroony-bot2-reference/docs/SPL_ELGAMAL_REGISTRY_MATRIX.json new file mode 100644 index 0000000..2320573 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/SPL_ELGAMAL_REGISTRY_MATRIX.json @@ -0,0 +1,121 @@ +{ + "file": "docs/SPL_ELGAMAL_REGISTRY_MATRIX.json", + "version": 1, + "programId": "regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg", + "interface": { + "crate": "spl-elgamal-registry-interface", + "resolvedVersion": "0.2.1", + "auditedCommitPrefix": "e18f9c6" + }, + "technicalBoundaries": { + "token2022ProgramId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "zkElgamalProofProgramId": "ZkE1Gama1Proof11111111111111111111111111111", + "registryDecodesProofInstructions": false, + "token2022DecoderOwnsRegistryInstructions": false + }, + "pda": { + "seedUtf8": "elgamal-registry", + "ownerAddressIsSeed": true, + "programIdIsDerivationProgram": true + }, + "instructions": [ + { + "name": "create_registry", + "tag": 0, + "wire": { + "exactLengthBytes": 2, + "fields": [ + { + "name": "tag", + "type": "u8", + "value": 0 + }, + { + "name": "proofInstructionOffset", + "type": "i8" + } + ] + }, + "accounts": [ + { + "position": 0, + "role": "registry_account", + "writable": true, + "signer": false + }, + { + "position": 1, + "role": "wallet_owner", + "writable": false, + "signer": true + }, + { + "position": 2, + "role": "system_program", + "writable": false, + "signer": false + }, + { + "position": 3, + "role": "instructions_sysvar_or_proof_context", + "writable": false, + "signer": false + } + ], + "builder": "create_registry", + "status": "current" + }, + { + "name": "update_registry", + "tag": 1, + "wire": { + "exactLengthBytes": 2, + "fields": [ + { + "name": "tag", + "type": "u8", + "value": 1 + }, + { + "name": "proofInstructionOffset", + "type": "i8" + } + ] + }, + "accounts": [ + { + "position": 0, + "role": "registry_account", + "writable": true, + "signer": false + }, + { + "position": 1, + "role": "instructions_sysvar_or_proof_context", + "writable": false, + "signer": false + }, + { + "position": 2, + "role": "registry_owner", + "writable": false, + "signer": true + } + ], + "builder": "update_registry", + "status": "current" + } + ], + "proofLocationSemantics": { + "offsetZero": "context_state_account", + "offsetNonZero": "relative_zk_proof_instruction", + "publishedInlineBuilderOffset": 1 + }, + "decoder": { + "crate": "kb_decoder_spl_elgamal_registry", + "coverageEqualityRequired": true, + "unknownTagPolicy": "failed", + "trailingBytesPolicy": "failed", + "failedTransactionCommitted": false + } +} diff --git a/migration/khadhroony-bot2-reference/docs/SPL_MEMO_MATRIX.json b/migration/khadhroony-bot2-reference/docs/SPL_MEMO_MATRIX.json new file mode 100644 index 0000000..5a14217 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/SPL_MEMO_MATRIX.json @@ -0,0 +1,266 @@ +{ + "file": "docs/SPL_MEMO_MATRIX.json", + "version": 8, + "matrixVersion": 4, + "scope": "SPL Memo v1, v3 et v4", + "interface": { + "crate": "spl-memo-interface", + "requestedVersion": "^2.1", + "auditedPublishedVersion": "2.1.0", + "lockfilePresentInInputArchive": false, + "builder": "spl_memo_interface::instruction::build_memo", + "builderAcceptsExplicitProgramId": true, + "wire": "octets exacts du message, sans préfixe ni discriminator", + "accountMetas": "ordre fourni, readonly, signer=true" + }, + "generations": [ + { + "generation": "v1", + "programId": "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo", + "surfaceCode": "spl_memo_v1", + "status": "historique immuable", + "instructionData": "octets UTF-8 bruts", + "emptyPayload": "accepte", + "accounts": "acceptes mais ignores par le runtime v1", + "signerRequirement": "aucune", + "accountOrder": "sans effet runtime mais conserve par le decodeur", + "duplicateAccounts": "sans effet runtime mais conserves par le decodeur", + "runtimeLogs": [], + "utf8Failure": "InvalidInstructionData", + "builderOfficial": "builder generique 2.1.0 avec program ID v1 explicite", + "currentInvocation": { + "mainnet": "transactions reelles observees et rejouees; aucun envoi execute dans cette tranche", + "devnet": "non verifie", + "localnet": "binaire historique non embarque" + }, + "realCorpusSignatures": [ + "imGSCgR83S3tFPTcZPqWLWRcL1piFX1vkYMRVsRdJeBM4p7viDb4V6Hq2x51ovTmbMgGBMdn8kyeNiAg9Ph6MfE", + "65J2cmiZAB5h7B3kyzhponNZQzMCbbxVn1A8cydvKKq1FYxYLEarG7ydjuuxFatXCG7Foju21XMUdRkT96jYBS3C" + ] + }, + { + "generation": "v3", + "programId": "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr", + "surfaceCode": "spl_memo_v3", + "status": "historique encore largement observe", + "instructionData": "octets UTF-8 bruts", + "emptyPayload": "accepte", + "accounts": "zero ou plusieurs; chaque compte fourni doit etre signer", + "signerRequirement": "tous les comptes fournis", + "accountOrder": "ordre de parcours et de logs conserve", + "duplicateAccounts": "parcourus et journalises dans leur ordre; aucun rejet dedie", + "runtimeLogs": [ + "Signed by pour chaque compte signer", + "Invalid UTF-8, from byte en cas d'erreur", + "Memo (len ): en cas de succes" + ], + "utf8Failure": "InvalidInstructionData", + "missingSignerFailure": "MissingRequiredSignature avant validation UTF-8", + "builderOfficial": "builder generique 2.1.0 avec program ID v3 explicite", + "currentInvocation": { + "mainnet": "transactions reelles observees et rejouees; aucun envoi execute dans cette tranche", + "devnet": "non verifie", + "localnet": "possible avec binaire officiel, non execute" + }, + "realCorpusSignatures": [ + "2DkiJJVAyJ1aJHn2ff8JhKA5C8wbakw76LZvgxmcQ1qtHNDprGvA6TntXXK5FSAeRMZyrDt8ujAkGMgBVcEhjsmT", + "3G18ZUSP29Aw12NwnAYRYgix87fa559nMzaBa3LSkCQZjJ5AYEjSDnQHLbgU3QZax2efmeJHdxu1LamSCxCJzg37" + ] + }, + { + "generation": "v4", + "programId": "Memo4c2pN8afCj432Lb7RMVKi9PbQnnW7ewFFaV3oAH", + "surfaceCode": "spl_memo_v4", + "status": "generation courante publiee avec implementation Pinocchio", + "instructionData": "octets UTF-8 bruts", + "emptyPayload": "accepte", + "accounts": "zero ou plusieurs; chaque compte fourni doit etre signer", + "signerRequirement": "tous les comptes fournis", + "accountOrder": "ordre de parcours et de logs conserve", + "duplicateAccounts": "aucun rejet dedie prouve; ordre et doublons conserves", + "runtimeLogs": [ + "signataires puis diagnostic UTF-8 ou memo selon l'implementation officielle" + ], + "utf8Failure": "InvalidInstructionData", + "missingSignerFailure": "MissingRequiredSignature avant validation UTF-8", + "builderOfficial": "builder generique 2.1.0 avec program ID v4 explicite", + "currentInvocation": { + "mainnet": "transactions reelles observees et rejouees; aucun envoi execute dans cette tranche", + "devnet": "simulation et trois executions v4 confirmees avec post-validation complete le 2026-07-15", + "localnet": "binaire officiel disponible, non execute" + }, + "realCorpusSignatures": [ + "2vgR56q2NR6ehu1uNnpt3te3RqhX2sXryXi4SpBHKUyVaeERr1HLYSLvDFRzd3eDeNejjLX9hVZJZ5GvtsTtBr1c", + "53aHhj6rt77hCkadHedpWS7GstdER5XqQrHDutZ7gAQrDvAxcDcktrUtwxZcCbTV2FnLzELqfwoJgvabjdLPWkQ5", + "3NxmPdF9W3GBiD8qF2VzvUhEDrTM48EEkaosTtzVceRhQc4sGEojDFppBsXYgqK9GCrCUo4wzAWciprsqZEmRyee", + "2njzDJUBar6TTPPcuwEDdkiW8kVLUsZXfdvUQNP7TC6La44E4kZtLFfdJ1Q8nPNJbKa9Vuby4TtiuLX2rQQTa65C" + ] + } + ], + "decoderContract": { + "maximumRetainedPayloadBytes": 4096, + "diagnosticHexPrefixBytes": 32, + "completeTextRetainedWhenUtf8Valid": true, + "failedTransactionRuntimeValidation": "not_proven_transaction_failed", + "failedTransactionCommitted": false, + "unknownProgramProducesObservation": false + }, + "materializationContract": { + "crate": "kb_materializer_transaction_annotations", + "processorName": "transaction_annotations", + "materializedFamily": "transaction_annotation", + "acceptedEntries": ["add_memo", "memo_intent", "invalid_memo_attempt"], + "projectedEntry": "add_memo", + "transactionPolicy": "SuccessfulCommittedOnly", + "failedOrUncommittedOutput": false, + "store": "kb_sol_mat_events", + "newMigrationRequired": false, + "idempotence": "ledger processor/version/input hash plus deterministic output key", + "readModel": { + "contract": "DecodePipelineStore.list_materialized_events", + "demo": "demo_decode_replay transaction annotation journal", + "boundedMaximumRows": 500, + "filters": ["processor exact", "family exact", "partial signature"], + "visualization": "table journal, not OHLC" + }, + "decodedOnlyFields": [ + "complete accounts and writable flags", + "payload hex diagnostic prefix", + "UTF-8 error offset", + "transaction error", + "decoder proof" + ] + }, + "executorContract": { + "crate": "kb_executor_spl_memo", + "operationCode": "spl_memo.add_memo", + "typedIntent": "SplMemoExecutionIntent", + "officialBuilder": "spl_memo_interface::instruction::build_memo", + "supportedGenerations": ["v4"], + "decodeOnlyGenerations": ["v1", "v3"], + "maximumMessageBytes": 566, + "maximumSignerOccurrences": 32, + "preserveSignerOrderAndDuplicates": true, + "inventWritableAccounts": false, + "requestedSpendLamports": 0, + "simulationRequired": true, + "dryRunDefault": true, + "allClustersConstructible": true, + "historicalGenerationSendEnabled": false, + "mainnetSendGovernedByCommonPolicy": true, + "clusterAvailabilityProvenBySimulation": true, + "demoCluster": "devnet", + "demoGeneration": "v4", + "postExecutionValidation": [ + "canonical_insert", + "core_extraction", + "memo_decode_replay", + "transaction_annotation_materialization" + ] + }, + "requiredCorpusCases": [ + "empty_payload", + "ascii", + "unicode_multibyte", + "near_practical_transaction_limit", + "invalid_utf8", + "zero_signer", + "one_signer", + "multiple_signers", + "non_signer_account", + "duplicate_accounts", + "additional_writable_account", + "outer_instruction", + "inner_instruction", + "successful_transaction", + "failed_transaction", + "malformed_base64", + "payload_over_decoder_limit" + ], + "mainnetReplayValidation": { + "date": "2026-07-14", + "acquisition": { + "programCampaigns": 3, + "canonicalTransactionsInserted": 300, + "missingTransactions": 0 + }, + "coreExtraction": { + "selected": 300, + "extracted": 300, + "failed": 0 + }, + "generations": [ + { + "generation": "v1", + "decodedInstructions": 102, + "materializedAnnotations": 69, + "refusedUncommittedIntents": 33 + }, + { + "generation": "v3", + "decodedInstructions": 439, + "materializedAnnotations": 422, + "refusedUncommittedIntents": 17, + "secondNonForcedReplaySelected": 0 + }, + { + "generation": "v4", + "decodedInstructions": 100, + "materializedAnnotations": 100, + "refusedUncommittedIntents": 0 + } + ], + "decodeFailures": 0, + "unmatchedInstructions": 0, + "operationalErrorLogEntries": 0 + }, + "devnetExecutionValidation": { + "date": "2026-07-15", + "generation": "v4", + "programId": "Memo4c2pN8afCj432Lb7RMVKi9PbQnnW7ewFFaV3oAH", + "simulationOnlyValidated": true, + "submittedTransactions": 3, + "confirmedTransactions": 3, + "canonicalTransactionsInserted": 3, + "coreTransactionsExtracted": 3, + "decodedInstructions": 3, + "materializedAnnotations": 3, + "annotationPayloadLengthBytes": 34, + "secondReplaySkipped": 3, + "secondReplayNewMaterializedOutputs": 0, + "decodeFailures": 0, + "materializationRefusals": 0, + "signatures": [ + "53aHhj6rt77hCkadHedpWS7GstdER5XqQrHDutZ7gAQrDvAxcDcktrUtwxZcCbTV2FnLzELqfwoJgvabjdLPWkQ5", + "3NxmPdF9W3GBiD8qF2VzvUhEDrTM48EEkaosTtzVceRhQc4sGEojDFppBsXYgqK9GCrCUo4wzAWciprsqZEmRyee", + "2njzDJUBar6TTPPcuwEDdkiW8kVLUsZXfdvUQNP7TC6La44E4kZtLFfdJ1Q8nPNJbKa9Vuby4TtiuLX2rQQTa65C" + ] + }, + "sources": [ + { + "kind": "official_interface_source", + "url": "https://docs.rs/spl-memo-interface/2.1.0/src/spl_memo_interface/instruction.rs.html" + }, + { + "kind": "official_interface_ids", + "url": "https://docs.rs/spl-memo-interface/2.1.0/src/spl_memo_interface/lib.rs.html" + }, + { + "kind": "official_v1_historical_source", + "url": "https://github.com/solana-program/memo/commit/479839141a99b7d0c3a35bc648c0fa1e4704ea3e" + }, + { + "kind": "official_v3_published_source", + "url": "https://docs.rs/spl-memo/3.0.0/src/spl_memo/processor.rs.html" + }, + { + "kind": "official_current_source", + "url": "https://github.com/solana-program/memo/blob/main/program/src/processor.rs" + } + ], + "documentedLimitations": [ + "v1 et v3 restent decodees depuis un corpus Mainnet reel et leur wire officiel reste audite, mais l'executeur les classe decode-only et ne construit que v4", + "l'invocabilite Localnet depend du deploiement explicite du binaire de generation demande", + "les differences de cout v3/v4 ne sont pas transformees en garantie statique; chaque plan reste soumis a la simulation et au plafond de frais" + ] +} diff --git a/migration/khadhroony-bot2-reference/docs/SPL_TOKEN_2022_CONFIDENTIAL_EXECUTION_AUDIT.md b/migration/khadhroony-bot2-reference/docs/SPL_TOKEN_2022_CONFIDENTIAL_EXECUTION_AUDIT.md new file mode 100644 index 0000000..f7670fa --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/SPL_TOKEN_2022_CONFIDENTIAL_EXECUTION_AUDIT.md @@ -0,0 +1,110 @@ + + + +# Audit d’exécution Confidential Transfer Token-2022 + +## Frontières de programme + +Les instructions Confidential Transfer restent des instructions du programme Token-2022. Les preuves associées sont des instructions distinctes du programme natif ZK ElGamal Proof ou des comptes de contexte déjà vérifiés. Le registre ElGamal reste une troisième surface avec son propre Program ID. + +Aucun builder Token-2022 ne doit incorporer silencieusement une preuve, déchiffrer un solde ou générer une clé privée. + +## Modes de preuve + +Chaque preuve est fournie selon un seul mode explicite : + +- `instruction_offset` : offset relatif signé non nul vers une instruction ZK distincte de la même transaction ; +- `context_state_account` : compte readonly contenant le contexte public d’une preuve déjà vérifiée ; le wire Token-2022 encode alors l’offset `0`. + +Un offset inline égal à zéro est invalide dans le contrat de l’exécuteur, car il rendrait le mode ambigu. + +## Inventaire audité + +| Opération | Preuves ordonnées | Classe d’exécution | +|-------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------| +| InitializeMint | aucune | builder public | +| UpdateMint | aucune | builder public | +| ConfigureAccount | PubkeyValidity | entrées cryptographiques appelant | +| ApproveAccount | aucune | builder public | +| EmptyAccount | ZeroCiphertext | entrées cryptographiques appelant | +| Deposit | aucune | builder public | +| Withdraw | CiphertextCommitmentEquality, BatchedRangeProofU64 | entrées cryptographiques appelant | +| Transfer | CiphertextCommitmentEquality, BatchedGroupedCiphertext3HandlesValidity, BatchedRangeProofU128 | entrées cryptographiques appelant | +| ApplyPendingBalance | aucune | builder public | +| EnableConfidentialCredits | aucune | builder public | +| DisableConfidentialCredits | aucune | builder public | +| EnableNonConfidentialCredits | aucune | builder public | +| DisableNonConfidentialCredits | aucune | builder public | +| TransferWithFee | CiphertextCommitmentEquality, BatchedGroupedCiphertext3HandlesValidity, PercentageWithFee, BatchedGroupedCiphertext2HandlesValidity, BatchedRangeProofU256 | entrées cryptographiques appelant | +| ConfigureAccountWithRegistry | PubkeyValidity | entrées cryptographiques appelant | + +La classe « builder public » signifie seulement que l’instruction Token-2022 ne nécessite pas de ciphertext ou preuve nouvellement généré. Elle ne dispense ni du préflight, ni de la simulation, ni des autorités et postconditions. + +## Plan de livraison + +- `pre.048` : types, modes de preuve, ordre des preuves et classification de support ; +- `pre.049` : builders publics Initialize/Update/Approve/Deposit/Apply/credit toggles ; +- `pre.050` : clés ElGamal et balances déchiffrables opaques, Initialize/Update Mint et Apply Pending Balance ; +- `pre.051` : Configure Account et Configure Account With Registry ; +- `pre.052` : Empty Account avec preuve ZeroCiphertext inline ou context-state ; +- `pre.053` : Withdraw avec Equality et Range U64 ; +- `pre.054` : Transfer avec Equality, Grouped Ciphertext Validity à trois handles et Range U128 ; +- `pre.055` : audit exact de Transfer With Fee, cinq preuves, comptes et payload ; +- `pre.056` : builder Transfer With Fee ; +- tranches ultérieures : Confidential Transfer Fee, Confidential Mint/Burn et Permissioned Confidential Burn. + +## `pre.053` — Withdraw + +`Withdraw` exige deux références de preuve dans l’ordre officiel : `CiphertextCommitmentEquality`, puis `BatchedRangeProofU64`. Les modes inline et context-state peuvent être combinés. L’exécuteur transporte la nouvelle balance déchiffrable opaque sans produire ni vérifier localement les secrets ou preuves. + + +## `pre.054` — Transfer + +`Transfer` transporte une nouvelle balance source déchiffrable de 36 octets et deux ciphertexts auditeur ElGamal de 64 octets. Il exige exactement trois références de preuve dans l'ordre officiel : `CiphertextCommitmentEquality`, `BatchedGroupedCiphertext3HandlesValidity`, puis `BatchedRangeProofU128`. Chaque preuve peut être inline ou context-state ; l'Instructions Sysvar n'est ajouté qu'une fois lorsqu'au moins un offset est utilisé. Aucune donnée cryptographique n'est générée par l'exécuteur. + + +## `pre.055` — audit Transfer With Fee + +`TransferWithFee` conserve le même payload cryptographique Token-2022 que `Transfer` : une balance source déchiffrable de 36 octets et deux ciphertexts auditeur ElGamal de 64 octets. Il n’ajoute pas de ciphertext withheld ou de montant de frais au payload Token-2022. Les informations de frais et le ciphertext de frais appartiennent aux contextes publics des preuves `PercentageWithFee` et `BatchedGroupedCiphertext2HandlesValidity`. + +Le wire exact contient le tag externe `27`, le sous-tag `13`, puis 169 octets de payload : `36 + 64 + 64 + 5 offsets i8`. La donnée complète mesure donc 171 octets. + +Les cinq preuves restent ordonnées : + +1. `CiphertextCommitmentEquality` ; +2. `BatchedGroupedCiphertext3HandlesValidity` pour le montant transféré ; +3. `PercentageWithFee` ; +4. `BatchedGroupedCiphertext2HandlesValidity` pour le ciphertext de frais ; +5. `BatchedRangeProofU256`. + +L’ordre des comptes est source writable, mint readonly, destination writable, Instructions Sysvar si au moins une preuve est inline, comptes context-state dans l’ordre des cinq preuves, autorité, puis signataires multisig. Aucun type opaque Token-2022 supplémentaire n’est nécessaire avant le builder de `pre.056`. + +## Builder Transfer With Fee — pre.056 + +Le builder `inner_transfer_with_fee` est activé avec les cinq preuves dans l’ordre officiel. Le payload reste limité à la nouvelle balance déchiffrable, aux deux ciphertexts auditeur et aux cinq offsets. Les comptes context-state suivent le même ordre que les preuves et l’Instructions Sysvar n’est présent qu’une fois lorsqu’au moins un offset inline est utilisé. + +## Confidential Transfer Fee public builder tranche (`pre.057`) + +The proof-free builder surface is restricted to subdiscriminants 0, 3, 4 and 5 +of the Token-2022 `ConfidentialTransferFeeExtension` envelope (external tag 37). +Initialization accepts one writable mint, an optional configuration authority +and one exact 32-byte ElGamal public key. Enable/disable harvest preserve simple +or multisig authority metas. Permissionless harvest preserves an ordered, +non-empty, duplicate-free source list bounded to 255 accounts. Withdrawals remain +separate because they require ciphertext-equality proof context. + +## Confidential Transfer Fee — retrait depuis plusieurs comptes (`pre.059`) + +Le builder `inner_withdraw_withheld_tokens_from_accounts` conserve l’ordre officiel suivant : Mint readonly, destination writable, Instructions Sysvar ou compte context-state, autorité withdraw-withheld, signataires multisig, puis sources writable. Le payload encode `num_token_accounts: u8`, l’offset de preuve et la nouvelle balance déchiffrable destination. + +La preuve requise est `CiphertextCiphertextEquality`. La liste des sources est non vide, unique, ordonnée et limitée à 255 comptes. Cette opération est explicitement sensible au front-running : une mutation d’un withheld ciphertext source après génération de la preuve fait échouer la transaction. Le parcours harvest-vers-Mint puis retrait-depuis-Mint reste l’alternative recommandée lorsque la stabilité des comptes sources ne peut pas être garantie. + +## `pre.060` — audit Confidential Mint/Burn + +L'interface publie six sous-instructions contiguës : `InitializeMint = 0`, `RotateSupplyElGamalPubkey = 1`, `UpdateDecryptableSupply = 2`, `Mint = 3`, `Burn = 4` et `ApplyPendingBurn = 5`. Cette famille reste dans l'enveloppe Token-2022 Confidential Mint/Burn et ne doit pas être confondue avec Permissioned Burn. + +`InitializeMint` transporte une clé publique ElGamal de supply et une supply déchiffrable initiale. `RotateSupplyElGamalPubkey` transporte la nouvelle clé et exige `CiphertextCiphertextEquality`. `UpdateDecryptableSupply` et `ApplyPendingBurn` n'exigent aucune preuve, mais restent soumis aux autorités et préconditions d'état. + +`Mint` et `Burn` exigent exactement trois preuves dans l'ordre : `CiphertextCommitmentEquality`, `BatchedGroupedCiphertext3HandlesValidity`, puis `BatchedRangeProofU128`. Les deux opérations transportent deux ciphertexts auditeur ; `Mint` transporte la nouvelle supply déchiffrable, tandis que `Burn` transporte la nouvelle balance disponible déchiffrable du compte source. Aucun secret, ciphertext ou preuve n'est généré par l'exécuteur. + +Le découpage prévu est : builders publics et rotation dans `pre.061`, puis Mint/Burn prouvés dans `pre.062`. Permissioned Confidential Burn reste une tranche indépendante. diff --git a/migration/khadhroony-bot2-reference/docs/SPL_TOKEN_2022_MATRIX.json b/migration/khadhroony-bot2-reference/docs/SPL_TOKEN_2022_MATRIX.json new file mode 100644 index 0000000..2639640 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/SPL_TOKEN_2022_MATRIX.json @@ -0,0 +1,2698 @@ +{ + "file": "docs/SPL_TOKEN_2022_MATRIX.json", + "version": "0.4.6-pre.026", + "schemaVersion": 1, + "matrixStatus": "exact_decoder_leaf_coverage_proven_pending_materialization_executor_preflight_and_cluster_corpus_completion", + "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "strictProgramBoundary": { + "acceptedProgramIds": [ + "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" + ], + "rejectedDistinctSurfaces": [ + "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL", + "ZkE1Gama1Proof11111111111111111111111111111", + "regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg" + ], + "externalInterfaceRule": "Transfer Hook, Token Group and Token Metadata implementations are dispatched only by an independently audited executable Program ID. A Token-2022 TLV pointer remains a link, not a decode of its target program." + }, + "interfaceAudit": { + "workspaceConstraint": "^3.1", + "workspaceFeatures": [ + "serde" + ], + "workspaceCargoLockPresentInProvidedArchive": true, + "workspaceCargoLockSha256": "248c02af8c78d3bb6ad747460fee0011f8141feef83364cf697e35e44bd4ceb9", + "workspaceCargoToolAvailableInAuditEnvironment": false, + "resolvedVersion": "3.1.1", + "resolutionStatus": "confirmed_by_operator_cargo_tree_2026-07-16", + "resolvedFeatures": [ + "default", + "serde" + ], + "leafConsumer": "kb_decoder_spl_token_2022", + "operatorEvidence": "cargo tree -i spl-token-2022-interface -e features", + "publishedCandidateAudited": "3.1.1", + "publishedCandidateStatus": "matches_confirmed_workspace_resolution", + "publishedDocumentation": "https://docs.rs/spl-token-2022-interface/3.1.1/spl_token_2022_interface/", + "officialRepository": "https://github.com/solana-program/token-2022", + "sourcePaths": [ + "interface/src/instruction.rs", + "interface/src/extension/mod.rs", + "interface/src/extension/*/instruction.rs", + "program/src/processor.rs", + "program/src/extension/*/processor.rs" + ], + "candidateDirectDependenciesRelevantToAudit": { + "solana-zk-elgamal-proof-interface": "^0.1.3", + "spl-token-confidential-transfer-proof-extraction": "^0.6.0", + "spl-token-group-interface": "^0.7.2", + "spl-token-metadata-interface": "^1.0.0", + "spl-type-length-value": "^0.9.1" + }, + "operatorWorkspaceResolvedDependencies": { + "solana-zk-elgamal-proof-interface": "0.1.3", + "spl-token-confidential-transfer-proof-extraction": "0.6.1", + "spl-token-group-interface": "0.7.2", + "spl-token-metadata-interface": "1.0.1", + "spl-type-length-value": "0.9.1" + }, + "operatorWorkspaceDependencyEvidence": { + "source": "attached Cargo.lock plus cargo tree -p spl-token-2022-interface -e features", + "tokenGroupConsumption": "direct dependency of spl-token-2022-interface 3.1.1", + "tokenMetadataConsumption": "direct dependency of spl-token-2022-interface 3.1.1", + "processorSourceLockMetadataVersion": "1.0.0", + "operatorWorkspaceMetadataVersion": "1.0.1", + "metadataVersionDriftStatus": "unpack_hardening_only_wire_and_builders_unchanged_by_source_diff" + }, + "operatorCommandsRequired": [ + "cargo tree -p kb_decoder_spl_token_2022 -e features", + "cargo tree -p kb_executor_spl_token_2022 -e features", + "cargo tree -i spl-token-2022-interface -e features", + "cargo tree -i spl-elgamal-registry-interface -e features", + "cargo metadata --format-version 1 --locked", + "cargo test -p kb_decoder_spl_token_2022", + "cargo clippy --all-targets" + ], + "sourceCommit": "e18f9c6f9bf6044b934f48e3090e8e59e4820f02", + "sourceTag": "interface@v3.1.1", + "sourceCommitDate": "2026-06-26" + }, + "operatorValidation": { + "date": "2026-07-16", + "cargoTestPackage": { + "command": "cargo test -p kb_decoder_spl_token_2022", + "status": "passed", + "passed": 8, + "failed": 0 + }, + "cargoClippyAllTargets": { + "command": "cargo clippy --all-targets", + "status": "clean" + } + }, + "tokenMetadataVersionDriftAudit": { + "processorSourceVersion": "1.0.0", + "operatorWorkspaceVersion": "1.0.1", + "releaseDate": "2026-06-29", + "releaseCommit": "7038969", + "changePullRequest": 87, + "mergedCommit": "fc97c60", + "changedProductionFile": "interface/src/instruction.rs", + "productionDiff": { + "additions": 78, + "deletions": 0 + }, + "wireDiscriminantsChanged": false, + "buildersChanged": false, + "changeKind": "pre_borsh_string_length_validation_before_existing_try_from_slice", + "affectedInstructions": { + "initialize": [ + "name", + "symbol", + "uri" + ], + "update_field": [ + "field_key_when_tag_3", + "value" + ], + "remove_key": [ + "key" + ] + }, + "unaffectedInstructions": [ + "update_authority", + "emit" + ], + "decoderRequirement": "validate every little_endian_u32 string length against remaining bounded input before allocation or UTF-8 materialization", + "malformedPolicy": "bounded invalid_instruction diagnostic without semantic event", + "evidence": "official interface@v1.0.1 release and solana-program/token-metadata pull request 87 files diff" + }, + "processorAudit": { + "auditedVersion": "11.0.0", + "sourceCommit": "e18f9c6f9bf6044b934f48e3090e8e59e4820f02", + "sourceAnchor": "same repository commit as interface@v3.1.1", + "programManifestInterfaceRequirement": "3.1.0 path dependency resolved to workspace interface 3.1.1 at the audited commit", + "dispatchStatus": "top_level_extension_and_embedded_interface_fallbacks_confirmed", + "resolvedInterfaceDependenciesFromSourceCargoLock": { + "spl-token-group-interface": "0.7.2", + "spl-token-metadata-interface": "1.0.0", + "spl-elgamal-registry-interface": "0.2.1" + }, + "deploymentBinaryEquality": { + "localnet": "not_tested", + "devnet": "not_proven", + "mainnet": "not_proven" + }, + "corpus": { + "offline": "not_started", + "devnet": "not_started", + "mainnet": "not_started" + }, + "rule": "Interface publication, processor implementation, builder constructibility, cluster deployment, observation, simulation and submission are independent proofs." + }, + "wireContract": { + "topLevelEncoding": "ordered_union: first try one_u8_PodTokenInstruction; if it fails try exact_8_byte_TokenMetadataInstruction discriminator; if that fails try exact_8_byte_TokenGroupInstruction discriminator", + "topLevelSuffixPolicy": "processor-specific exact decode: fixed Pod payloads use exact bytemuck sizes; metadata uses bounded Borsh and try_from_slice; group uses exact bytemuck sizes; remaining per-variant proof suffix rules pending", + "outerAndInner": true, + "failedTransactionPolicy": "preserve a bounded structured intent when the wire is readable; committed remains false", + "unknownInstructionPolicy": "bounded opaque diagnostic without semantic event", + "batch": { + "tag": 255, + "recordPrefix": [ + "account_count_u8", + "instruction_data_length_u8" + ], + "nestedBatchAllowed": false, + "bounds": "pending_workspace_policy_and_processor_audit" + } + }, + "baseInstructionWireAudit": { + "status": "published_interface_3_1_1_pack_and_unpack_exact_processor_suffix_acceptance_pending_per_variant", + "rowCount": 33, + "scope": "top-level u8 forms that are not extension-family envelopes", + "sizeIncludesTopLevelTag": true, + "sizeRuleMeaning": "canonical TokenInstruction::pack output for fixed forms; public unpack may accept a longer buffer when interfaceConsumption says its returned suffix is discarded", + "integerEndianness": "little_endian", + "compactOptions": { + "pubkey": { + "none": "one byte 0", + "some": "one byte 1 followed by 32-byte pubkey" + }, + "u64": { + "none": "one byte 0", + "some": "one byte 1 followed by u64_le" + }, + "invalidDiscriminantPolicy": "interface_unpack_rejects_values_other_than_0_or_1" + }, + "publicUnpackContract": { + "entryPoint": "TokenInstruction::unpack calls unpack_with_rest and discards the returned remainder", + "fixedPrefixConsequence": "a successful public interface unpack is not proof that the whole instruction buffer was consumed", + "decoderRequirement": "retain and diagnose every unconsumed bounded suffix until the exact processor path proves acceptance or rejection", + "runtimeClaim": "none_from_public_unpack_alone" + }, + "rows": [ + { + "tag": 0, + "name": "initialize_mint", + "sizeRule": "35_or_67", + "payload": "decimals_u8+mint_authority_32+compact_freeze_authority", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_mint" + }, + { + "tag": 1, + "name": "initialize_account", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_account" + }, + { + "tag": 2, + "name": "initialize_multisig", + "sizeRule": "exact_2", + "payload": "m_u8", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_multisig" + }, + { + "tag": 3, + "name": "transfer", + "sizeRule": "exact_9", + "payload": "amount_u64_le", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "transfer", + "status": "deprecated" + }, + { + "tag": 4, + "name": "approve", + "sizeRule": "exact_9", + "payload": "amount_u64_le", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "approve" + }, + { + "tag": 5, + "name": "revoke", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "revoke" + }, + { + "tag": 6, + "name": "set_authority", + "sizeRule": "3_or_35", + "payload": "authority_type_u8+compact_new_authority", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "set_authority" + }, + { + "tag": 7, + "name": "mint_to", + "sizeRule": "exact_9", + "payload": "amount_u64_le", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "mint_to" + }, + { + "tag": 8, + "name": "burn", + "sizeRule": "exact_9", + "payload": "amount_u64_le", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "burn" + }, + { + "tag": 9, + "name": "close_account", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "close_account" + }, + { + "tag": 10, + "name": "freeze_account", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "freeze_account" + }, + { + "tag": 11, + "name": "thaw_account", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "thaw_account" + }, + { + "tag": 12, + "name": "transfer_checked", + "sizeRule": "exact_10", + "payload": "amount_u64_le+decimals_u8", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "transfer_checked" + }, + { + "tag": 13, + "name": "approve_checked", + "sizeRule": "exact_10", + "payload": "amount_u64_le+decimals_u8", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "approve_checked" + }, + { + "tag": 14, + "name": "mint_to_checked", + "sizeRule": "exact_10", + "payload": "amount_u64_le+decimals_u8", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "mint_to_checked" + }, + { + "tag": 15, + "name": "burn_checked", + "sizeRule": "exact_10", + "payload": "amount_u64_le+decimals_u8", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "burn_checked" + }, + { + "tag": 16, + "name": "initialize_account2", + "sizeRule": "exact_33", + "payload": "owner_pubkey_32", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_account2" + }, + { + "tag": 17, + "name": "sync_native", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "sync_native" + }, + { + "tag": 18, + "name": "initialize_account3", + "sizeRule": "exact_33", + "payload": "owner_pubkey_32", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_account3" + }, + { + "tag": 19, + "name": "initialize_multisig2", + "sizeRule": "exact_2", + "payload": "m_u8", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_multisig2" + }, + { + "tag": 20, + "name": "initialize_mint2", + "sizeRule": "35_or_67", + "payload": "decimals_u8+mint_authority_32+compact_freeze_authority", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_mint2" + }, + { + "tag": 21, + "name": "get_account_data_size", + "sizeRule": "1_plus_2n_n_greater_or_equal_0", + "payload": "all_remaining_known_extension_type_u16_le", + "interfaceConsumption": "entire_remainder_rejected_if_odd_or_unknown_extension_type", + "officialBuilder": "get_account_data_size" + }, + { + "tag": 22, + "name": "initialize_immutable_owner", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_immutable_owner" + }, + { + "tag": 23, + "name": "amount_to_ui_amount", + "sizeRule": "exact_9", + "payload": "amount_u64_le", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "amount_to_ui_amount" + }, + { + "tag": 24, + "name": "ui_amount_to_amount", + "sizeRule": "minimum_1", + "payload": "entire_remaining_utf8_bytes_empty_allowed_by_interface", + "interfaceConsumption": "entire_remainder_utf8_validated", + "officialBuilder": "ui_amount_to_amount", + "decoderBound": "required_before_utf8_materialization" + }, + { + "tag": 25, + "name": "initialize_mint_close_authority", + "sizeRule": "2_or_34", + "payload": "compact_close_authority", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_mint_close_authority" + }, + { + "tag": 29, + "name": "reallocate", + "sizeRule": "1_plus_2n_n_greater_or_equal_0", + "payload": "all_remaining_known_extension_type_u16_le", + "interfaceConsumption": "entire_remainder_rejected_if_odd_or_unknown_extension_type", + "officialBuilder": "reallocate" + }, + { + "tag": 31, + "name": "create_native_mint", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "create_native_mint" + }, + { + "tag": 32, + "name": "initialize_non_transferable_mint", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_non_transferable_mint" + }, + { + "tag": 35, + "name": "initialize_permanent_delegate", + "sizeRule": "exact_33", + "payload": "delegate_pubkey_32", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "initialize_permanent_delegate" + }, + { + "tag": 38, + "name": "withdraw_excess_lamports", + "sizeRule": "exact_1", + "payload": "unit", + "interfaceConsumption": "tag_only_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "withdraw_excess_lamports" + }, + { + "tag": 45, + "name": "unwrap_lamports", + "sizeRule": "2_or_10", + "payload": "compact_amount_u64_le", + "interfaceConsumption": "fixed_prefix_suffix_returned_then_discarded_by_public_unpack", + "officialBuilder": "unwrap_lamports" + }, + { + "tag": 255, + "name": "batch", + "sizeRule": "minimum_1", + "payload": "entire_remaining_batch_record_wire", + "interfaceConsumption": "entire_remainder_preserved_as_batch_payload", + "officialBuilder": "no_dedicated_rust_interface_helper_identified", + "processorRecordAudit": "pending_bounds_accounts_and_nested_batch_rejection" + } + ], + "remainingProof": "exact processor helper path, suffix acceptance, accounts and flags for every row" + }, + "baseInstructionProcessorSuffixAudit": { + "status": "confirmed_from_exact_processor_11_0_0_source_commit", + "sourceCommit": "e18f9c6f9bf6044b934f48e3090e8e59e4820f02", + "sources": [ + { + "path": "interface/src/instruction.rs", + "sha256": "672b3890cb58e5700a67f202491069ed44dffcbfa0e7d0b83a0cfbbb05b3a287" + }, + { + "path": "program/src/pod_instruction.rs", + "sha256": "7b546580a90ab8d1f52143c21da931d26f8b68c7d2050b4eaf7307fc9c49c7e5" + }, + { + "path": "program/src/processor.rs", + "sha256": "9d11b834aabdb8c824ca59f7ea7bda4d217565a58359fbfaf37e5a35e8f3032d" + } + ], + "partition": [ + { + "policy": "exact_total_length_via_decode_instruction_data", + "tags": [ + 2, + 3, + 4, + 7, + 8, + 12, + 13, + 14, + 15, + 16, + 18, + 19, + 23, + 35 + ], + "count": 14, + "suffix": "rejected", + "proof": "decode_instruction_data requires input length equal to one plus size_of Pod payload" + }, + { + "policy": "compact_option_prefix_only", + "tags": [ + 0, + 6, + 20, + 25, + 45 + ], + "count": 5, + "suffix": "accepted_after_complete_none_or_some_prefix", + "proof": "specialized Pod plus compact-option helpers read the required prefix and do not require exhaustion" + }, + { + "policy": "tag_only_dispatch", + "tags": [ + 1, + 5, + 9, + 10, + 11, + 17, + 22, + 31, + 32, + 38 + ], + "count": 10, + "suffix": "accepted_without_inspection", + "proof": "processor branch invokes no instruction-data decoder after decode_instruction_type reads byte zero" + }, + { + "policy": "entire_remainder_is_payload", + "tags": [ + 21, + 24, + 29 + ], + "count": 3, + "suffix": "not_applicable_remainder_consumed", + "proof": "extension-type vectors parse all two-byte chunks; UI amount parses all remaining bytes as UTF-8", + "constraints": { + "extensionTypeVectors": "empty allowed; odd final chunk or unknown ExtensionType rejected", + "uiAmount": "empty allowed by UTF-8 parser; malformed UTF-8 rejected" + } + }, + { + "policy": "batch_record_stream", + "tags": [ + 255 + ], + "count": 1, + "suffix": "entire_remainder_must_be_complete_record_stream", + "proof": "top-level process routes tag 255 to process_batch and nested _process_inner rejects tag 255", + "record": { + "headerBytes": 2, + "accountCount": "u8", + "instructionDataLengthIncludingDiscriminator": "u8", + "minimumOuterInstructionBytes": 4, + "zeroInstructionDataLength": "rejected_by_inner_dispatch", + "truncatedHeaderOrPayload": "rejected", + "nestedBatch": "rejected", + "embeddedMetadataOrGroupFallback": "processor_dispatch_allows_when_record_length_fits_u8", + "maximumAccountsPerRecordFromWire": 255, + "maximumInstructionDataBytesPerRecordFromWire": 255, + "recordCountProcessorBound": "none_explicit_beyond_transaction_instruction_data_and_compute_limits", + "decoderRecordCountBound": "required" + } + } + ], + "partitionTagCount": 33, + "partitionTagsUnique": true, + "decoderPolicy": { + "exactPod": "reject suffix", + "acceptedRuntimeSuffix": "preserve bounded suffix and emit an explicit accepted_suffix diagnostic without inventing fields", + "fullRemainder": "decode only after global byte and element bounds", + "batch": "bound record count, account count, per-record data and total data before nested leaf decode" + }, + "remainingProof": "exact account order, signer and writable flags, optional and multisig tails for every base form" + }, + "baseInstructionBuilderAccountAudit": { + "status": "exact_official_interface_3_1_1_builder_metas_for_32_constructible_forms_and_explicit_batch_gap", + "source": { + "path": "interface/src/instruction.rs", + "commit": "e18f9c6f9bf6044b934f48e3090e8e59e4820f02", + "sha256": "672b3890cb58e5700a67f202491069ed44dffcbfa0e7d0b83a0cfbbb05b3a287" + }, + "rowCount": 33, + "flagLegend": { + "writable": "canonical AccountMeta writable flag emitted by the official builder", + "signer": "canonical AccountMeta signer flag; conditional_authority means true only when the multisig signer tail is empty" + }, + "multisigAuthorityTail": { + "builderAcceptedRange": "zero selects a single signing authority; a non-empty signer_pubkeys slice is appended without an explicit builder length check", + "runtimeStoredMultisigPositions": "1..11", + "meta": { + "role": "multisig_signer", + "writable": false, + "signer": true + }, + "authorityMetaWhenTailPresent": { + "writable": false, + "signer": false + }, + "authorityMetaWhenTailEmpty": { + "writable": false, + "signer": true + }, + "duplicatesPreserved": true + }, + "rows": [ + { + "tag": 0, + "name": "initialize_mint", + "builder": "initialize_mint", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "rent_sysvar", + "writable": false, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 1, + "name": "initialize_account", + "builder": "initialize_account", + "accounts": [ + { + "index": 0, + "role": "token_account", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "owner", + "writable": false, + "signer": false + }, + { + "index": 3, + "role": "rent_sysvar", + "writable": false, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 2, + "name": "initialize_multisig", + "builder": "initialize_multisig", + "accounts": [ + { + "index": 0, + "role": "multisig", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "rent_sysvar", + "writable": false, + "signer": false + } + ], + "tail": "multisig_configuration_keys_readonly_non_signer_1_to_11" + }, + { + "tag": 3, + "name": "transfer", + "builder": "transfer", + "accounts": [ + { + "index": 0, + "role": "source", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "destination", + "writable": true, + "signer": false + }, + { + "index": 2, + "role": "authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail", + "status": "deprecated" + }, + { + "tag": 4, + "name": "approve", + "builder": "approve", + "accounts": [ + { + "index": 0, + "role": "source", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "delegate", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "owner", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 5, + "name": "revoke", + "builder": "revoke", + "accounts": [ + { + "index": 0, + "role": "source", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "owner_or_delegate", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 6, + "name": "set_authority", + "builder": "set_authority", + "accounts": [ + { + "index": 0, + "role": "owned_account_or_mint", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "current_authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 7, + "name": "mint_to", + "builder": "mint_to", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "destination", + "writable": true, + "signer": false + }, + { + "index": 2, + "role": "mint_authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 8, + "name": "burn", + "builder": "burn", + "accounts": [ + { + "index": 0, + "role": "source", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": true, + "signer": false + }, + { + "index": 2, + "role": "authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 9, + "name": "close_account", + "builder": "close_account", + "accounts": [ + { + "index": 0, + "role": "source_account_or_mint", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "destination", + "writable": true, + "signer": false + }, + { + "index": 2, + "role": "close_authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 10, + "name": "freeze_account", + "builder": "freeze_account", + "accounts": [ + { + "index": 0, + "role": "token_account", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "freeze_authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 11, + "name": "thaw_account", + "builder": "thaw_account", + "accounts": [ + { + "index": 0, + "role": "token_account", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "freeze_authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 12, + "name": "transfer_checked", + "builder": "transfer_checked", + "accounts": [ + { + "index": 0, + "role": "source", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "destination", + "writable": true, + "signer": false + }, + { + "index": 3, + "role": "authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 13, + "name": "approve_checked", + "builder": "approve_checked", + "accounts": [ + { + "index": 0, + "role": "source", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "delegate", + "writable": false, + "signer": false + }, + { + "index": 3, + "role": "owner", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 14, + "name": "mint_to_checked", + "builder": "mint_to_checked", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "destination", + "writable": true, + "signer": false + }, + { + "index": 2, + "role": "mint_authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 15, + "name": "burn_checked", + "builder": "burn_checked", + "accounts": [ + { + "index": 0, + "role": "source", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": true, + "signer": false + }, + { + "index": 2, + "role": "authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 16, + "name": "initialize_account2", + "builder": "initialize_account2", + "accounts": [ + { + "index": 0, + "role": "token_account", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "rent_sysvar", + "writable": false, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 17, + "name": "sync_native", + "builder": "sync_native_or_sync_native_with_rent_sysvar", + "accounts": [ + { + "index": 0, + "role": "native_token_account", + "writable": true, + "signer": false + } + ], + "optionalAccounts": [ + { + "index": 1, + "role": "rent_sysvar", + "writable": false, + "signer": false + } + ], + "tail": "none_after_optional_rent" + }, + { + "tag": 18, + "name": "initialize_account3", + "builder": "initialize_account3", + "accounts": [ + { + "index": 0, + "role": "token_account", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": false, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 19, + "name": "initialize_multisig2", + "builder": "initialize_multisig2", + "accounts": [ + { + "index": 0, + "role": "multisig", + "writable": true, + "signer": false + } + ], + "tail": "multisig_configuration_keys_readonly_non_signer_1_to_11" + }, + { + "tag": 20, + "name": "initialize_mint2", + "builder": "initialize_mint2", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": true, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 21, + "name": "get_account_data_size", + "builder": "get_account_data_size", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": false, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 22, + "name": "initialize_immutable_owner", + "builder": "initialize_immutable_owner", + "accounts": [ + { + "index": 0, + "role": "token_account", + "writable": true, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 23, + "name": "amount_to_ui_amount", + "builder": "amount_to_ui_amount", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": false, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 24, + "name": "ui_amount_to_amount", + "builder": "ui_amount_to_amount", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": false, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 25, + "name": "initialize_mint_close_authority", + "builder": "initialize_mint_close_authority", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": true, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 29, + "name": "reallocate", + "builder": "reallocate", + "accounts": [ + { + "index": 0, + "role": "token_account", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "payer", + "writable": true, + "signer": true + }, + { + "index": 2, + "role": "system_program", + "writable": false, + "signer": false + }, + { + "index": 3, + "role": "owner", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail", + "processorAccountProof": "pending_exact_reallocate_module_reconfirmation" + }, + { + "tag": 31, + "name": "create_native_mint", + "builder": "create_native_mint", + "accounts": [ + { + "index": 0, + "role": "payer", + "writable": true, + "signer": true + }, + { + "index": 1, + "role": "native_mint_pda", + "writable": true, + "signer": false + }, + { + "index": 2, + "role": "system_program", + "writable": false, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 32, + "name": "initialize_non_transferable_mint", + "builder": "initialize_non_transferable_mint", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": true, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 35, + "name": "initialize_permanent_delegate", + "builder": "initialize_permanent_delegate", + "accounts": [ + { + "index": 0, + "role": "mint", + "writable": true, + "signer": false + } + ], + "tail": "none" + }, + { + "tag": 38, + "name": "withdraw_excess_lamports", + "builder": "withdraw_excess_lamports", + "accounts": [ + { + "index": 0, + "role": "source_account_or_mint", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "destination", + "writable": true, + "signer": false + }, + { + "index": 2, + "role": "authority", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 45, + "name": "unwrap_lamports", + "builder": "unwrap_lamports", + "accounts": [ + { + "index": 0, + "role": "native_source", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "destination", + "writable": true, + "signer": false + }, + { + "index": 2, + "role": "owner_or_delegate", + "writable": false, + "signer": "conditional_authority" + } + ], + "tail": "multisig_authority_tail" + }, + { + "tag": 255, + "name": "batch", + "builder": null, + "accounts": "concatenated_record_slices_each_sized_by_account_count_u8", + "tail": "outer_accounts_not_assigned_to_a_record_are_not_rejected_by_processor", + "builderStatus": "enum_pack_only_no_dedicated_rust_account_meta_builder" + } + ], + "processorFactsAlreadyConfirmed": { + "authorityValidation": "runtime-recognized 355-byte Token, inline Token or Token-2022 multisig consumes the remaining accounts as signer candidates; otherwise the authority account itself must sign and remaining accounts are ignored", + "multisigCandidateMatching": "only initialized stored signer positions count; candidate must carry signer flag; a stored position is counted at most once", + "initializeMultisig": "all accounts after multisig and optional rent become configuration keys; runtime validates m and n independently in 1..11 but does not enforce builder rule m <= n", + "syncNative": "second account is optional; when present it is parsed as Rent; accounts after it are ignored", + "batch": "each record receives its declared account prefix; a nested Batch is rejected; outer accounts left after the final record are not checked for exhaustion" + }, + "decoderRequirements": [ + "preserve_original_account_order_flags_and_duplicates", + "separate_builder_canonical_flags_from_processor_runtime_acceptance", + "preserve_multisig_candidate_tail_even_when_single_authority_runtime_ignores_it", + "diagnose_initialize_multisig_m_greater_than_n_as_processor_accepted_but_builder_rejected", + "diagnose_batch_unassigned_outer_accounts", + "never_invent_owner_mint_or_authority_from_state_not_present_in_wire_or_account_contract" + ], + "remainingProof": "processor account consumption for reallocate and exhaustive extra-account behavior by each row" + }, + "accountStateContract": { + "baseLengths": { + "mint": 82, + "account": 165, + "multisig": 355 + }, + "extendedMintPrefix": "82-byte Mint, padding through byte 164, AccountType at byte 165, then TLV", + "extendedAccountPrefix": "165-byte Account, AccountType at byte 165, then TLV", + "multisigRule": "identified by exact 355-byte base length and never treated as an extended Mint or Account", + "accountType": { + "encoding": "u8", + "values": [ + { + "value": 0, + "name": "uninitialized" + }, + { + "value": 1, + "name": "mint" + }, + { + "value": 2, + "name": "account" + } + ] + }, + "tlvHeader": { + "type": "u16_le", + "length": "u16_le", + "headerBytes": 4 + }, + "parserRequirements": [ + "bounded_iteration", + "preserve_order", + "preserve_unknown_type_and_bounded_bytes", + "detect_duplicate_types", + "detect_truncation", + "detect_invalid_padding", + "validate_account_type", + "validate_known_fixed_length", + "preserve_variable_length_without_invention" + ], + "finalSnapshotClaimedFromInstruction": false + }, + "surfaceEquality": { + "topLevelInstructionCount": 48, + "productionExtensionTypeCount": 29, + "compiledDecoderCoverage": "exact_100_of_100_routable_leaves", + "compiledExecutorCoverage": "partial_by_explicit_capability", + "materializationOwnership": "partial_by_unique_owner_phase_d", + "requiredFinalInvariant": "matrix == resolved interface == resolved processor dispatch == decoder coverage == projection ownership == executor capabilities", + "extensionDispatchFamilyCount": 15, + "extensionSubInstructionCount": 58, + "baseAndStandaloneTopLevelLeafCount": 33, + "embeddedTokenMetadataInstructionCount": 5, + "embeddedTokenGroupInstructionCount": 4, + "embeddedInterfaceFallbackCount": 9, + "processorRoutableLeafInstructionCount": 100 + }, + "instructionDefaults": { + "encoding": "u8_tag_plus_variant_payload", + "minimumSize": "pending_per_variant_audit", + "exactSizeOrSuffixRule": "pending_per_variant_audit", + "accounts": "pending_exact_order_flags_and_optional_accounts", + "stateTarget": "pending", + "decodedFields": "pending", + "authorizedProjections": [], + "executorSupport": { + "status": "not_started", + "reason": "decoder_matrix_and_projection_validation_required_first" + }, + "outer": true, + "inner": true, + "success": "decode", + "failure": "decode_as_uncommitted_intent_when_readable", + "proofs": { + "synthetic": [], + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested", + "realSignatures": [] + } + }, + "instructions": [ + { + "tag": 0, + "name": "initialize_mint", + "family": "base", + "status": "published_candidate_current", + "payload": "decimals_u8_mint_authority_pubkey_freeze_authority_coption" + }, + { + "tag": 1, + "name": "initialize_account", + "family": "base", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 2, + "name": "initialize_multisig", + "family": "base", + "status": "published_candidate_current", + "payload": "m_u8" + }, + { + "tag": 3, + "name": "transfer", + "family": "base", + "status": "published_candidate_deprecated", + "payload": "amount_u64_le" + }, + { + "tag": 4, + "name": "approve", + "family": "base", + "status": "published_candidate_current", + "payload": "amount_u64_le" + }, + { + "tag": 5, + "name": "revoke", + "family": "base", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 6, + "name": "set_authority", + "family": "base", + "status": "published_candidate_current", + "payload": "authority_type_u8_and_coption_pubkey" + }, + { + "tag": 7, + "name": "mint_to", + "family": "base", + "status": "published_candidate_current", + "payload": "amount_u64_le" + }, + { + "tag": 8, + "name": "burn", + "family": "base", + "status": "published_candidate_current", + "payload": "amount_u64_le" + }, + { + "tag": 9, + "name": "close_account", + "family": "base", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 10, + "name": "freeze_account", + "family": "base", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 11, + "name": "thaw_account", + "family": "base", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 12, + "name": "transfer_checked", + "family": "base", + "status": "published_candidate_current", + "payload": "amount_u64_le_decimals_u8" + }, + { + "tag": 13, + "name": "approve_checked", + "family": "base", + "status": "published_candidate_current", + "payload": "amount_u64_le_decimals_u8" + }, + { + "tag": 14, + "name": "mint_to_checked", + "family": "base", + "status": "published_candidate_current", + "payload": "amount_u64_le_decimals_u8" + }, + { + "tag": 15, + "name": "burn_checked", + "family": "base", + "status": "published_candidate_current", + "payload": "amount_u64_le_decimals_u8" + }, + { + "tag": 16, + "name": "initialize_account2", + "family": "base", + "status": "published_candidate_current", + "payload": "owner_pubkey" + }, + { + "tag": 17, + "name": "sync_native", + "family": "base", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 18, + "name": "initialize_account3", + "family": "base", + "status": "published_candidate_current", + "payload": "owner_pubkey" + }, + { + "tag": 19, + "name": "initialize_multisig2", + "family": "base", + "status": "published_candidate_current", + "payload": "m_u8" + }, + { + "tag": 20, + "name": "initialize_mint2", + "family": "base", + "status": "published_candidate_current", + "payload": "decimals_u8_mint_authority_pubkey_freeze_authority_coption" + }, + { + "tag": 21, + "name": "get_account_data_size", + "family": "base", + "status": "published_candidate_current", + "payload": "zero_or_more_extension_type_u16_le" + }, + { + "tag": 22, + "name": "initialize_immutable_owner", + "family": "immutable_owner", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 23, + "name": "amount_to_ui_amount", + "family": "base", + "status": "published_candidate_current", + "payload": "amount_u64_le" + }, + { + "tag": 24, + "name": "ui_amount_to_amount", + "family": "base", + "status": "published_candidate_current", + "payload": "utf8_remainder" + }, + { + "tag": 25, + "name": "initialize_mint_close_authority", + "family": "mint_close_authority", + "status": "published_candidate_current", + "payload": "coption_pubkey" + }, + { + "tag": 26, + "name": "transfer_fee_extension", + "family": "transfer_fee", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 27, + "name": "confidential_transfer_extension", + "family": "confidential_transfer", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 28, + "name": "default_account_state_extension", + "family": "default_account_state", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 29, + "name": "reallocate", + "family": "account_extension_management", + "status": "published_candidate_current", + "payload": "zero_or_more_extension_type_u16_le" + }, + { + "tag": 30, + "name": "memo_transfer_extension", + "family": "memo_transfer", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 31, + "name": "create_native_mint", + "family": "native_mint", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 32, + "name": "initialize_non_transferable_mint", + "family": "non_transferable", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 33, + "name": "interest_bearing_mint_extension", + "family": "interest_bearing_mint", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 34, + "name": "cpi_guard_extension", + "family": "cpi_guard", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 35, + "name": "initialize_permanent_delegate", + "family": "permanent_delegate", + "status": "published_candidate_current", + "payload": "delegate_pubkey" + }, + { + "tag": 36, + "name": "transfer_hook_extension", + "family": "transfer_hook", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 37, + "name": "confidential_transfer_fee_extension", + "family": "confidential_transfer_fee", + "status": "published_candidate_current", + "payload": "exact_subdiscriminant_and_variant_payload", + "decoderStatus": "structured_exact_wire", + "publicWire": { + "initializeConfidentialTransferFeeConfig": "tag,sub=0,authority[32],withdrawWithheldAuthorityElgamalPubkey[32]", + "withdrawWithheldTokensFromMint": "tag,sub=1,proofInstructionOffset[i8],newDecryptableAvailableBalance[36]", + "withdrawWithheldTokensFromAccounts": "tag,sub=2,numTokenAccounts[u8],proofInstructionOffset[i8],newDecryptableAvailableBalance[36]", + "harvestWithheldTokensToMint": "tag,sub=3", + "enableHarvestToMint": "tag,sub=4", + "disableHarvestToMint": "tag,sub=5" + } + }, + { + "tag": 38, + "name": "withdraw_excess_lamports", + "family": "base", + "status": "published_candidate_current", + "payload": "unit" + }, + { + "tag": 39, + "name": "metadata_pointer_extension", + "family": "metadata_pointer", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 40, + "name": "group_pointer_extension", + "family": "group_pointer", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 41, + "name": "group_member_pointer_extension", + "family": "group_member_pointer", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 42, + "name": "confidential_mint_burn_extension", + "family": "confidential_mint_burn", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 43, + "name": "scaled_ui_amount_extension", + "family": "scaled_ui_amount", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 44, + "name": "pausable_extension", + "family": "pausable", + "status": "published_candidate_current", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 45, + "name": "unwrap_lamports", + "family": "native_mint", + "status": "published_candidate_current", + "payload": "coption_u64_le" + }, + { + "tag": 46, + "name": "permissioned_burn_extension", + "family": "permissioned_burn", + "status": "published_candidate_recent", + "payload": "subdiscriminant_and_variant_payload_pending" + }, + { + "tag": 255, + "name": "batch", + "family": "batch", + "status": "published_candidate_current", + "payload": "bounded_sequence_pending_processor_limits" + } + ], + "extensionTypeDefaults": { + "encoding": "u16_le", + "tlvHeaderBytes": 4, + "length": "pending_exact_size_of_audit", + "padding": "pending_exact_account_len_audit", + "repeatAllowed": false, + "unknownPolicy": "preserve_bounded_opaque_entry", + "instructionFamilies": [], + "authoritiesAndFields": "pending_exact_layout_audit", + "authorizedProjections": [], + "executorSupport": "not_started", + "proofs": { + "synthetic": [], + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + } + }, + "extensionTypes": [ + { + "type": 0, + "name": "uninitialized", + "accountType": "uninitialized", + "length": 0, + "variableLength": false, + "status": "padding_sentinel" + }, + { + "type": 1, + "name": "transfer_fee_config", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 2, + "name": "transfer_fee_amount", + "accountType": "account", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 3, + "name": "mint_close_authority", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 4, + "name": "confidential_transfer_mint", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 5, + "name": "confidential_transfer_account", + "accountType": "account", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 6, + "name": "default_account_state", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 7, + "name": "immutable_owner", + "accountType": "account", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 8, + "name": "memo_transfer", + "accountType": "account", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 9, + "name": "non_transferable", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 10, + "name": "interest_bearing_config", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 11, + "name": "cpi_guard", + "accountType": "account", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 12, + "name": "permanent_delegate", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 13, + "name": "non_transferable_account", + "accountType": "account", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 14, + "name": "transfer_hook", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 15, + "name": "transfer_hook_account", + "accountType": "account", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 16, + "name": "confidential_transfer_fee_config", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 17, + "name": "confidential_transfer_fee_amount", + "accountType": "account", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 18, + "name": "metadata_pointer", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 19, + "name": "token_metadata", + "accountType": "mint", + "length": "variable", + "variableLength": true, + "status": "published_external_interface_state" + }, + { + "type": 20, + "name": "group_pointer", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 21, + "name": "token_group", + "accountType": "mint", + "variableLength": false, + "status": "published_external_interface_state" + }, + { + "type": 22, + "name": "group_member_pointer", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 23, + "name": "token_group_member", + "accountType": "mint", + "variableLength": false, + "status": "published_external_interface_state" + }, + { + "type": 24, + "name": "confidential_mint_burn", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 25, + "name": "scaled_ui_amount", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 26, + "name": "pausable", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 27, + "name": "pausable_account", + "accountType": "account", + "variableLength": false, + "status": "published_candidate" + }, + { + "type": 28, + "name": "permissioned_burn", + "accountType": "mint", + "variableLength": false, + "status": "published_candidate_recent" + } + ], + "knownExtensionCompatibility": { + "requiredAccountExtensions": { + "transfer_fee_config": [ + "transfer_fee_amount" + ], + "non_transferable": [ + "non_transferable_account", + "immutable_owner" + ], + "transfer_hook": [ + "transfer_hook_account" + ], + "pausable": [ + "pausable_account" + ] + }, + "invalidMintCombinations": [ + "confidential_transfer_fee_config_without_transfer_fee_config_and_confidential_transfer_mint", + "transfer_fee_config_plus_confidential_transfer_mint_without_confidential_transfer_fee_config", + "confidential_mint_burn_without_confidential_transfer_mint", + "scaled_ui_amount_with_interest_bearing_config", + "non_transferable_plus_confidential_transfer_mint_without_confidential_mint_burn" + ] + }, + "materializationAudit": { + "existingOwnersInspected": [ + "token_accounts", + "admin", + "fees", + "compliance_audit", + "risk", + "lifecycle" + ], + "currentToken2022InstructionClaims": [], + "provisionalOwnership": { + "token_accounts": [ + "committed_instructional_mint_and_account_lifecycle" + ], + "admin": [ + "committed_authority_and_configuration_changes" + ], + "fees": [ + "instruction_proven_transfer_fee_parameters_and_withheld_movements_only" + ], + "compliance_audit": [ + "memo_hook_cpi_guard_and_confidential_operation_facts" + ], + "risk": [ + "stable_fact_only_no_score" + ], + "lifecycle": [ + "no_token_2022_claim_until_non_overlap_is_proven" + ] + }, + "forbiddenClaims": [ + "final_balance_snapshot", + "final_withheld_total", + "accrued_interest", + "decrypted_confidential_state", + "duplicate_parent_and_cpi_fact" + ] + }, + "elgamalRegistryBoundary": { + "workspaceConstraint": "^0.2", + "resolvedVersion": "0.2.1", + "resolvedFeatures": [ + "default" + ], + "resolutionStatus": "confirmed_by_operator_cargo_tree_2026-07-16", + "leafConsumer": "kb_decoder_spl_token_2022_for_audit_only_pending_dedicated_crate_decision", + "operatorEvidence": "cargo tree -i spl-elgamal-registry-interface -e features", + "programId": "regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg", + "programIdEvidence": "spl-elgamal-registry-interface 0.2.1 declare_id", + "registryAddressSeedUtf8": "elgamal-registry", + "status": "identity_confirmed_separate_instruction_processor_and_cluster_audit_pending", + "matrix": "separate_matrix_required", + "sharedInstructionEnum": false, + "sharedDecoder": false, + "sharedExecutor": false, + "nativeZkElgamalProofProgramId": "ZkE1Gama1Proof11111111111111111111111111111", + "requiredInstructionAudit": [ + "instruction discriminants and exact wire", + "registry PDA derivation and wallet ownership relation", + "ElGamal public key and proof-account references", + "create_or_update lifecycle and authority/signature contract", + "cluster deployment and constructible builder status" + ], + "token2022DependencyAudit": "Confidential Token-2022 instructions may reference registry accounts or ElGamal public keys, but registry instructions remain a separate Program ID surface." + }, + "processorDispatch": { + "precedence": [ + "pod_token_instruction_u8", + "token_metadata_interface_8_byte_discriminator", + "token_group_interface_8_byte_discriminator", + "invalid_instruction" + ], + "source": "program/src/processor.rs Processor::_process_inner at commit e18f9c6f9bf6044b934f48e3090e8e59e4820f02", + "extensionFamilies": [ + { + "tag": 26, + "family": "transfer_fee", + "subInstructionCount": 6, + "subInstructions": [ + "initialize_transfer_fee_config", + "transfer_checked_with_fee", + "withdraw_withheld_tokens_from_mint", + "withdraw_withheld_tokens_from_accounts", + "harvest_withheld_tokens_to_mint", + "set_transfer_fee" + ] + }, + { + "tag": 27, + "family": "confidential_transfer", + "subInstructionCount": 15, + "subInstructions": [ + "initialize_mint", + "update_mint", + "configure_account", + "approve_account", + "empty_account", + "deposit", + "withdraw", + "transfer", + "apply_pending_balance", + "enable_confidential_credits", + "disable_confidential_credits", + "enable_non_confidential_credits", + "disable_non_confidential_credits", + "transfer_with_fee", + "configure_account_with_registry" + ] + }, + { + "tag": 28, + "family": "default_account_state", + "subInstructionCount": 2, + "subInstructions": [ + "initialize", + "update" + ] + }, + { + "tag": 30, + "family": "memo_transfer", + "subInstructionCount": 2, + "subInstructions": [ + "enable", + "disable" + ] + }, + { + "tag": 33, + "family": "interest_bearing_mint", + "subInstructionCount": 2, + "subInstructions": [ + "initialize", + "update_rate" + ] + }, + { + "tag": 34, + "family": "cpi_guard", + "subInstructionCount": 2, + "subInstructions": [ + "enable", + "disable" + ] + }, + { + "tag": 36, + "family": "transfer_hook", + "subInstructionCount": 2, + "subInstructions": [ + "initialize", + "update" + ] + }, + { + "tag": 37, + "family": "confidential_transfer_fee", + "subInstructionCount": 6, + "subInstructions": [ + "initialize_confidential_transfer_fee_config", + "withdraw_withheld_tokens_from_mint", + "withdraw_withheld_tokens_from_accounts", + "harvest_withheld_tokens_to_mint", + "enable_harvest_to_mint", + "disable_harvest_to_mint" + ] + }, + { + "tag": 39, + "family": "metadata_pointer", + "subInstructionCount": 2, + "subInstructions": [ + "initialize", + "update" + ] + }, + { + "tag": 40, + "family": "group_pointer", + "subInstructionCount": 2, + "subInstructions": [ + "initialize", + "update" + ] + }, + { + "tag": 41, + "family": "group_member_pointer", + "subInstructionCount": 2, + "subInstructions": [ + "initialize", + "update" + ] + }, + { + "tag": 42, + "family": "confidential_mint_burn", + "subInstructionCount": 6, + "subInstructions": [ + "initialize_mint", + "rotate_supply_elgamal_pubkey", + "update_decryptable_supply", + "mint", + "burn", + "apply_pending_burn" + ] + }, + { + "tag": 43, + "family": "scaled_ui_amount", + "subInstructionCount": 2, + "subInstructions": [ + "initialize", + "update_multiplier" + ] + }, + { + "tag": 44, + "family": "pausable", + "subInstructionCount": 3, + "subInstructions": [ + "initialize", + "pause", + "resume" + ] + }, + { + "tag": 46, + "family": "permissioned_burn", + "subInstructionCount": 4, + "subInstructions": [ + "initialize", + "burn", + "burn_checked", + "confidential_burn" + ] + } + ], + "embeddedInterfaceFallbacks": [ + { + "interface": "spl-token-metadata-interface", + "resolvedByProcessorCargoLock": "1.0.0", + "name": "initialize", + "discriminatorHex": "d2e11ea258b84d8d", + "encoding": "8_byte_sha256_prefix_plus_bounded_borsh", + "accounts": [ + { + "index": 0, + "role": "metadata", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "update_authority", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "mint", + "writable": false, + "signer": false + }, + { + "index": 3, + "role": "mint_authority", + "writable": false, + "signer": true + } + ] + }, + { + "interface": "spl-token-metadata-interface", + "resolvedByProcessorCargoLock": "1.0.0", + "name": "update_field", + "discriminatorHex": "dde9312db5cadcc8", + "encoding": "8_byte_sha256_prefix_plus_bounded_borsh", + "accounts": [ + { + "index": 0, + "role": "metadata", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "update_authority", + "writable": false, + "signer": true + } + ] + }, + { + "interface": "spl-token-metadata-interface", + "resolvedByProcessorCargoLock": "1.0.0", + "name": "remove_key", + "discriminatorHex": "ea122038598d25b5", + "encoding": "8_byte_sha256_prefix_plus_bounded_borsh", + "accounts": [ + { + "index": 0, + "role": "metadata", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "update_authority", + "writable": false, + "signer": true + } + ] + }, + { + "interface": "spl-token-metadata-interface", + "resolvedByProcessorCargoLock": "1.0.0", + "name": "update_authority", + "discriminatorHex": "d7e4a6e45464567b", + "encoding": "8_byte_sha256_prefix_plus_exact_borsh", + "accounts": [ + { + "index": 0, + "role": "metadata", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "current_update_authority", + "writable": false, + "signer": true + } + ] + }, + { + "interface": "spl-token-metadata-interface", + "resolvedByProcessorCargoLock": "1.0.0", + "name": "emit", + "discriminatorHex": "faa6b4fa0d0cb846", + "encoding": "8_byte_sha256_prefix_plus_exact_borsh", + "accounts": [ + { + "index": 0, + "role": "metadata", + "writable": false, + "signer": false + } + ] + }, + { + "interface": "spl-token-group-interface", + "resolvedByProcessorCargoLock": "0.7.2", + "name": "initialize_group", + "discriminatorHex": "79716c2736330004", + "encoding": "8_byte_sha256_prefix_plus_exact_pod", + "accounts": [ + { + "index": 0, + "role": "group", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "mint", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "mint_authority", + "writable": false, + "signer": true + } + ] + }, + { + "interface": "spl-token-group-interface", + "resolvedByProcessorCargoLock": "0.7.2", + "name": "update_group_max_size", + "discriminatorHex": "6c25ab8ff81e126e", + "encoding": "8_byte_sha256_prefix_plus_exact_pod", + "accounts": [ + { + "index": 0, + "role": "group", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "update_authority", + "writable": false, + "signer": true + } + ] + }, + { + "interface": "spl-token-group-interface", + "resolvedByProcessorCargoLock": "0.7.2", + "name": "update_group_authority", + "discriminatorHex": "a1695801edddd8cb", + "encoding": "8_byte_sha256_prefix_plus_exact_pod", + "accounts": [ + { + "index": 0, + "role": "group", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "current_update_authority", + "writable": false, + "signer": true + } + ] + }, + { + "interface": "spl-token-group-interface", + "resolvedByProcessorCargoLock": "0.7.2", + "name": "initialize_member", + "discriminatorHex": "9820deb0dfed7486", + "encoding": "8_byte_sha256_prefix_plus_exact_pod", + "accounts": [ + { + "index": 0, + "role": "member", + "writable": true, + "signer": false + }, + { + "index": 1, + "role": "member_mint", + "writable": false, + "signer": false + }, + { + "index": 2, + "role": "member_mint_authority", + "writable": false, + "signer": true + }, + { + "index": 3, + "role": "group", + "writable": true, + "signer": false + }, + { + "index": 4, + "role": "group_update_authority", + "writable": false, + "signer": true + } + ] + } + ], + "ownershipRule": "These nine interface instructions belong to the Token-2022 decoder only when the outer or inner executable program ID is Tokenz. A CPI to a third-party implementation belongs to that third-party Program ID decoder." + }, + "extensionImplementationStatus": { + "status": "all_published_extension_instruction_leaves_exact_wire_decoded", + "decodedLeafCount": 58, + "groups": [ + { + "envelopeTag": 26, + "group": "transfer_fee", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_transfer_fee_config", + "transfer_checked_with_fee", + "withdraw_withheld_tokens_from_mint", + "withdraw_withheld_tokens_from_accounts", + "harvest_withheld_tokens_to_mint", + "set_transfer_fee" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 28, + "group": "default_account_state", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_default_account_state", + "update_default_account_state" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 30, + "group": "memo_transfer", + "status": "exact_wire_decoded", + "leafInstructions": [ + "enable_required_transfer_memos", + "disable_required_transfer_memos" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 34, + "group": "cpi_guard", + "status": "exact_wire_decoded", + "leafInstructions": [ + "enable_cpi_guard", + "disable_cpi_guard" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 33, + "group": "interest_bearing_mint", + "status": "exact_wire_decoded_projected_and_official_builders_enabled", + "leafInstructions": [ + "initialize_interest_bearing_mint", + "update_interest_bearing_rate" + ], + "executorStatus": "official_interface_builders_enabled" + } + ], + "remainingOpaqueEnvelopeTags": [], + "nonClaim": "All 58 published extension instruction leaves and all nine embedded Metadata/Group interface leaves are decoded exactly; this does not claim materialization, executor, proof-context, cluster availability, or stateful preflight completion.", + "baseExtensionOperations": { + "status": "decoded_projected_and_official_builders_enabled", + "operations": [ + "reallocate", + "initialize_non_transferable_mint", + "initialize_permanent_delegate" + ] + }, + "embeddedInterfaceDecodedLeafCount": 9, + "processorDispatchEquality": "33 base leaves + 58 extension leaves + 5 metadata leaves + 4 group leaves = 100 routable leaves", + "embeddedInterfaceStatus": "exact_decoder_dispatch_added_for_all_nine_fallbacks", + "decodedExtensionGroups": [ + { + "envelopeTag": 27, + "group": "confidential_transfer", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_confidential_transfer_mint", + "update_confidential_transfer_mint", + "configure_confidential_transfer_account", + "approve_confidential_transfer_account", + "empty_confidential_transfer_account", + "deposit_confidential_tokens", + "withdraw_confidential_tokens", + "transfer_confidential_tokens", + "apply_confidential_pending_balance", + "enable_confidential_credits", + "disable_confidential_credits", + "enable_non_confidential_credits", + "disable_non_confidential_credits", + "transfer_confidential_tokens_with_fee", + "configure_confidential_transfer_account_with_registry" + ], + "executorStatus": "public_and_confidential_transfer_builders_progressively_enabled_through_transfer" + }, + { + "envelopeTag": 36, + "group": "transfer_hook", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_transfer_hook", + "update_transfer_hook_program_id" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 37, + "group": "confidential_transfer_fee", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_confidential_transfer_fee_config", + "withdraw_confidential_withheld_tokens_from_mint", + "withdraw_confidential_withheld_tokens_from_accounts", + "harvest_confidential_withheld_tokens_to_mint", + "enable_confidential_harvest_to_mint", + "disable_confidential_harvest_to_mint" + ], + "executorStatus": "pending_after_projection_and_stateful_preflight" + }, + { + "envelopeTag": 39, + "group": "metadata_pointer", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_metadata_pointer", + "update_metadata_pointer" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 40, + "group": "group_pointer", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_group_pointer", + "update_group_pointer" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 41, + "group": "group_member_pointer", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_group_member_pointer", + "update_group_member_pointer" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 42, + "group": "confidential_mint_burn", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_confidential_mint_burn", + "rotate_confidential_supply_elgamal_pubkey", + "update_confidential_decryptable_supply", + "confidential_mint", + "confidential_burn", + "apply_pending_confidential_burn" + ], + "executorStatus": "pending_after_projection_and_stateful_preflight" + }, + { + "envelopeTag": 43, + "group": "scaled_ui_amount", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_scaled_ui_amount", + "update_scaled_ui_amount_multiplier" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 44, + "group": "pausable", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_pausable_config", + "pause_token_mint", + "resume_token_mint" + ], + "executorStatus": "pending_after_projection" + }, + { + "envelopeTag": 46, + "group": "permissioned_burn", + "status": "exact_wire_decoded", + "leafInstructions": [ + "initialize_permissioned_burn", + "permissioned_burn", + "permissioned_burn_checked", + "permissioned_confidential_burn" + ], + "executorStatus": "pending_after_projection_and_stateful_preflight" + } + ], + "exactExtensionLeafCount": 30 + } +} diff --git a/migration/khadhroony-bot2-reference/docs/SPL_TOKEN_2022_SOURCE_AUDIT.md b/migration/khadhroony-bot2-reference/docs/SPL_TOKEN_2022_SOURCE_AUDIT.md new file mode 100644 index 0000000..00d2b2a --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/SPL_TOKEN_2022_SOURCE_AUDIT.md @@ -0,0 +1,251 @@ + + + +# Audit des sources Token-2022 résolues + +## Ancrage reproductible + +L'interface Cargo résolue `spl-token-2022-interface 3.1.1` publie le commit +`e18f9c6f9bf6044b934f48e3090e8e59e4820f02`, tagué `interface@v3.1.1` le 26 juin 2026. +L'audit est effectué sur ce commit détaché, jamais sur la branche `main` mouvante. + +Le `Cargo.lock` du workspace opérateur, SHA-256 +`248c02af8c78d3bb6ad747460fee0011f8141feef83364cf697e35e44bd4ceb9`, confirme que +`kb_decoder_spl_token_2022 0.4.5` consomme directement les interfaces Token-2022 et registre +ElGamal. Pour `spl-token-2022-interface 3.1.1`, il résout exactement : + +- `solana-zk-elgamal-proof-interface 0.1.3` ; +- `spl-token-confidential-transfer-proof-extraction 0.6.1` ; +- `spl-token-group-interface 0.7.2` ; +- `spl-token-metadata-interface 1.0.1` ; +- `spl-type-length-value 0.9.1`. + +La sortie `cargo tree -p spl-token-2022-interface -e features` prouve en particulier le chemin de +consommation direct de Token Metadata `1.0.1`. Cette version diffère du `1.0.0` figé dans le +`Cargo.lock` du processor source audité : les discriminants, layouts et builders devront donc être +comparés différentiellement avant le décodeur, sans supposer leur égalité. + +La comparaison officielle clôt cette dérive : `interface@v1.0.1`, publiée le 29 juin 2026 au +commit court `7038969`, contient le correctif fusionné `fc97c60` de la pull request `87`. Le diff +de production ajoute 78 lignes dans `interface/src/instruction.rs` sans suppression. Les +discriminants, structures de payload et builders existants ne changent pas ; `unpack()` valide +désormais les longueurs Borsh `u32 LE` avant `try_from_slice` pour : + +- `Initialize` : `name`, `symbol`, `uri` ; +- `UpdateField` : la chaîne de `Field::Key` lorsque le tag vaut `3`, puis `value` ; +- `RemoveKey` : `key`, après l'octet booléen `idempotent`. + +`UpdateAuthority` et `Emit` ne transportent pas de chaîne et restent inchangés. Le décodeur devra +appliquer la garde `1.0.1` avant toute allocation, même si le processor source figé résolvait +encore `1.0.0`. + +Au même commit : + +- `program/Cargo.toml` déclare le processor `spl-token-2022 11.0.0` ; +- sa dépendance path demande l'interface `3.1.0`, satisfaite par le package workspace `3.1.1` ; +- le `Cargo.lock` source résout `spl-token-group-interface 0.7.2`, + `spl-token-metadata-interface 1.0.0` et `spl-elgamal-registry-interface 0.2.1` ; +- l'égalité du binaire déployé avec cette source reste non prouvée sur Localnet, Devnet et Mainnet. + +## Dispatch réellement implémenté + +`Processor::_process_inner` applique cet ordre : + +1. `PodTokenInstruction`, discriminant `u8` ; +2. si ce décodage échoue, `TokenMetadataInstruction`, discriminant SPL de huit octets ; +3. si ce décodage échoue, `TokenGroupInstruction`, discriminant SPL de huit octets ; +4. sinon `InvalidInstruction`. + +La surface routable contient donc : + +| Famille | Enveloppes ou formes | +|------------------------------------------------------|---------------------:| +| Tags Token-2022 `u8` | 48 | +| Familles d'extensions parmi ces tags | 15 | +| Sous-instructions de ces familles | 58 | +| Formes Token-2022 hors enveloppes | 33 | +| Instructions Token Metadata exécutées par Token-2022 | 5 | +| Instructions Token Group exécutées par Token-2022 | 4 | +| Formes feuilles routables par le processor | 100 | + +Les 48 tags ne constituent donc pas, seuls, une déclaration de couverture maximale. Les quinze +tags d'extension sont des enveloppes et doivent être remplacés par leurs 58 sous-instructions dans +le comptage des formes feuilles. + +## Interfaces incorporées au processor + +Les neuf discriminants ci-dessous sont exécutés par Token-2022 lorsqu'ils ciblent le Program ID +`TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` : + +| Interface | Instruction | Discriminant hexadécimal | +|------------------------|--------------------------|--------------------------| +| Token Metadata `1.0.0` | `initialize` | `d2e11ea258b84d8d` | +| Token Metadata `1.0.0` | `update_field` | `dde9312db5cadcc8` | +| Token Metadata `1.0.0` | `remove_key` | `ea122038598d25b5` | +| Token Metadata `1.0.0` | `update_authority` | `d7e4a6e45464567b` | +| Token Metadata `1.0.0` | `emit` | `faa6b4fa0d0cb846` | +| Token Group `0.7.2` | `initialize_group` | `79716c2736330004` | +| Token Group `0.7.2` | `update_group_max_size` | `6c25ab8ff81e126e` | +| Token Group `0.7.2` | `update_group_authority` | `a1695801edddd8cb` | +| Token Group `0.7.2` | `initialize_member` | `9820deb0dfed7486` | + +Le propriétaire du décodage dépend toujours du Program ID exécutable : une CPI vers un programme +tiers implémentant la même interface appartient au décodeur de ce programme tiers. Un TLV pointeur +reste un lien et ne transfère pas la propriété du fait métier. + +## Wire public des instructions hors enveloppes + +Les 33 tags qui ne sont pas des enveloppes d'extension ont maintenant une ligne machine-readable +dans `baseInstructionWireAudit`. Les tailles incluent toujours le tag `u8`. L'interface `3.1.1` +encode les options d'instruction de manière compacte, distincte du `COption` à quatre octets des +états de compte : `0` tient sur un octet ; `1` est suivi de 32 octets pour une clé publique ou de +huit octets pour un `u64`. + +Ce point donne notamment les tailles exactes suivantes : + +- `InitializeMint` et `InitializeMint2` : 35 octets sans freeze authority, 67 avec ; +- `SetAuthority` : 3 octets sans nouvelle autorité, 35 avec ; +- `InitializeMintCloseAuthority` : 2 octets sans autorité, 34 avec ; +- `UnwrapLamports` : 2 octets sans montant, 10 avec. + +`GetAccountDataSize` et `Reallocate` consomment tout le suffixe par éléments `ExtensionType u16 LE` ; +un suffixe impair ou un type inconnu est rejeté par l'unpacker d'interface. `UiAmountToAmount` +consomme tout le suffixe comme UTF-8, y compris une chaîne vide au seul niveau de l'interface. +`Batch` conserve tout le suffixe comme wire de records, dont les bornes et la validation processor +restent à auditer. + +La distinction critique est conservée dans la matrice : `TokenInstruction::unpack()` appelle +`unpack_with_rest()` puis ignore le reste retourné. Pour les formes à préfixe fixe ou sans payload, +un succès de cet unpacker public ne prouve donc pas la consommation de tout le buffer, ni +l'acceptation du suffixe par le runtime. Le futur décodeur devra préserver et diagnostiquer tout +suffixe borné jusqu'à ce que le chemin exact du processor figé en établisse la règle variante par +variante. + +## Règles de suffixe du processor figé + +La comparaison a ensuite été menée sur les trois fichiers exacts du commit, avec leurs empreintes +SHA-256 enregistrées dans la matrice : + +- `interface/src/instruction.rs` : `672b3890…b3a287` ; +- `program/src/pod_instruction.rs` : `7b546580…49c7e5` ; +- `program/src/processor.rs` : `9d11b834…3032d`. + +Les 33 formes hors enveloppes forment une partition complète et sans doublon : + +| Règle processor | Tags | Nombre | +|------------------------------------------------|----------------------------------------|-------:| +| longueur totale Pod exacte | `2,3,4,7,8,12,13,14,15,16,18,19,23,35` | 14 | +| préfixe Pod + option compacte, suffixe accepté | `0,6,20,25,45` | 5 | +| tag seul, suffixe non inspecté et accepté | `1,5,9,10,11,17,22,31,32,38` | 10 | +| reste entier interprété comme payload | `21,24,29` | 3 | +| flux de records `Batch` | `255` | 1 | + +`decode_instruction_data()` exige exactement `1 + size_of::()` octets. À l'inverse, les +helpers privés des options compactes lisent la structure Pod, puis `0`, ou `1` suivi de la valeur, +sans vérifier l'épuisement du buffer. Pour les dix branches sans données, le processor n'appelle +aucun décodeur après la lecture du premier octet. Ces quinze formes acceptent donc réellement un +suffixe, même si celui-ci ne porte aucune sémantique publiée. Le décodeur devra le préserver sous +forme bornée et le diagnostiquer explicitement, sans créer de champ métier. + +Pour `GetAccountDataSize` et `Reallocate`, chaque morceau de deux octets doit former un +`ExtensionType` connu ; un dernier morceau incomplet ou un type inconnu échoue. Pour +`UiAmountToAmount`, tout le reste doit être UTF-8, la chaîne vide restant admise à ce niveau. + +`Batch` ne peut pas être vide : le plus petit wire total contient le tag externe, un header de deux +octets et au moins un discriminant interne, soit quatre octets. Chaque record encode en `u8` son +nombre de comptes et la longueur totale de son instruction interne. Un header ou payload tronqué +échoue ; une longueur interne nulle échoue au dispatch ; un `Batch` interne est rejeté par +`_process_inner`. Les fallbacks Token Metadata et Token Group restent en revanche joignables dans +un record lorsque leur wire tient dans les 255 octets. Le processor n'impose pas de nombre maximal +explicite de records au-delà des limites de transaction et de calcul : le décodeur devra donc poser +sa propre borne. + +## Comptes publiés par les builders de base + +La matrice contient désormais une ligne ordonnée pour chacune des 33 formes hors enveloppes. Elle +enregistre les metas exactes des 32 helpers Rust publiés : rôle, `writable`, `signer`, compte +optionnel et queue multisig. `Batch` reste la seule forme sans helper Rust dédié pour construire +ses metas ; `TokenInstruction::pack()` ne produit que ses données. + +Les autorités simples et multisig partagent le contrat classique des builders : l'autorité est +signataire lorsque la liste `signer_pubkeys` est vide ; sinon elle ne signe pas et chaque élément +de la queue est ajouté en lecture seule avec le flag signataire. Le builder ne borne pas la taille +de cette queue pour les opérations d'autorité et conserve ordre et doublons. Le runtime ne peut +toutefois satisfaire qu'un multisig stockant de un à onze positions. + +`InitializeMultisig` et `InitializeMultisig2` ont un contrat différent : les clés de configuration +ne signent pas. Le builder exige `m` et `n` dans `1..=11` ainsi que `m <= n`. Le processor consomme +tous les comptes restants comme clés de configuration, valide séparément `m` et `n`, mais ne +répète pas le contrôle `m <= n`. Une instruction manuelle `m > n` peut donc initialiser un état que +le builder refuse. Le décodeur doit conserver ce fait et le diagnostiquer sans le normaliser. + +`SyncNative` publie deux helpers : le premier fournit seulement le compte natif modifiable ; le +second ajoute Rent en lecture seule. Le processor traite effectivement le deuxième compte comme +optionnel, mais ignore ceux qui suivraient. `Batch` découpe les comptes selon le compteur `u8` de +chaque record ; les comptes externes restant après le dernier record ne sont pas rejetés. Ils +devront rester visibles comme comptes non assignés. + +Le contrat builder de `Reallocate` est fixé — compte à agrandir modifiable, payer modifiable et +signataire, System Program, propriétaire conditionnel puis queue multisig — mais la consommation +runtime détaillée reste ouverte jusqu'à reconfirmation du module séparé +`program/src/extension/reallocate.rs` au même commit. Cette limite empêche de déclarer l'audit +processor des comptes entièrement clos. + +## Sources auditées + +- dépôt `solana-program/token-2022`, commit figé ci-dessus ; +- `interface/src/instruction.rs` et `interface/src/extension/**/instruction.rs` ; +- `interface/src/extension/mod.rs` et les états d'extension ; +- `program/src/processor.rs` et `program/src/extension/**/processor.rs` ; +- dépôt `solana-program/token-metadata`, commit + `552935e00710478541e3394a8b2624c9ba7f503f`, tag `interface@v1.0.0` ; +- dépôt `solana-program/token-group`, commit + `2645064953937118572735993ed0ac1d07db5f14`, tag `interface@v0.7.2`. + +## Limites restantes + +Cette tranche confirme le wire public, les règles runtime de suffixe et les metas officielles des +builders des 33 formes hors enveloppes. Elle ne clôt pas encore l'audit par variante : consommation +processor complète des comptes, données de preuve, builders des extensions, statuts de +construction et compatibilité cluster doivent encore être renseignés avant le décodeur. + +## Première tranche verticale d'extensions + +Le décodeur structure désormais exactement douze feuilles issues de quatre enveloppes : les six +instructions Transfer Fee, les deux Default Account State, les deux Memo Transfer et les deux CPI +Guard. Les discriminants, champs numériques, options compactes et règles de longueur proviennent +des interfaces figées au commit audité. Les onze autres enveloppes restent opaques et ne sont pas +comptées comme décodées sémantiquement. + +Cette tranche ne déclare encore aucune capacité exécuteur d'extension. Conformément à l'ordre du +jalon, les projections propriétaires et leurs tests de non-duplication doivent être validés avant +d'activer les builders officiels correspondants. + +## Dispatch incorporé Token Metadata et Token Group + +Le dispatch de production ne peut pas se limiter au premier octet de `TokenInstruction`. Après +échec du décodage Token-2022 principal, le processor `11.0.0` audité tente successivement les +interfaces Token Metadata puis Token Group. Les neuf discriminants déjà inventoriés dans la +matrice sont donc désormais reconnus par le décodeur Token-2022 lorsque le Program ID réellement +exécuté est `Tokenz...` : cinq feuilles Metadata et quatre feuilles Group. + +Les chaînes Borsh Metadata sont bornées avant allocation et décodage. Les layouts Pod Group sont +traités avec une longueur exacte. Cette intégration ne décode pas un programme tiers implémentant +les mêmes interfaces : une CPI vers un autre Program ID reste la propriété du décodeur de ce +programme. + +L'égalité de surface à maintenir est désormais explicitement compilée et documentée : + +```text +33 feuilles hors enveloppes + 58 feuilles d'extensions + 5 Metadata + 4 Group = 100 +``` + +## Audit ElGamal restant + +La présence de `spl-elgamal-registry-interface 0.2.1`, de son Program ID `regVY...` et de la seed +`elgamal-registry` ne suffit pas à revendiquer sa couverture. La tranche dédiée doit encore fixer +les discriminants et le wire exacts, la dérivation PDA, la relation wallet/owner, les clés ElGamal, +les références de preuve, le lifecycle create/update, les autorités et signataires, les builders +actuels ou expérimentaux et les preuves de déploiement cluster. Toute référence au registre depuis +une instruction confidentielle Token-2022 reste un lien inter-programme et non une instruction du +registre. diff --git a/migration/khadhroony-bot2-reference/docs/SPL_TOKEN_2022_VALIDATION_MATRIX.json b/migration/khadhroony-bot2-reference/docs/SPL_TOKEN_2022_VALIDATION_MATRIX.json new file mode 100644 index 0000000..8cedd27 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/SPL_TOKEN_2022_VALIDATION_MATRIX.json @@ -0,0 +1,146 @@ +{ + "matrixVersion": 2, + "milestone": "0.4.6", + "status": "partially_confirmed", + "statusVocabulary": [ + "not_run", + "simulated", + "submitted", + "confirmed", + "unavailable", + "failed" + ], + "scenarios": [ + { + "id": "offline_full_regression", + "environment": "offline", + "status": "confirmed", + "requiredEvidence": [ + "test_suite", + "clippy" + ], + "evidence": [ + { + "kind": "test_suite", + "value": "kb_pipeline 120/120" + }, + { + "kind": "test_suite", + "value": "kb_executor_spl_token_2022 44/44" + }, + { + "kind": "test_suite", + "value": "kb_executor_spl_elgamal_registry 6/6" + }, + { + "kind": "clippy", + "value": "cargo clippy --all-targets clean" + } + ] + }, + { + "id": "token_2022_public_devnet", + "environment": "devnet", + "status": "not_run", + "requiredEvidence": [ + "simulation", + "signature", + "canonical_hydration", + "core_extraction", + "decode_replay", + "materialization", + "second_replay" + ] + }, + { + "id": "elgamal_registry_devnet", + "environment": "devnet", + "status": "not_run", + "requiredEvidence": [ + "simulation", + "signature", + "canonical_hydration", + "core_extraction", + "decode_replay", + "materialization", + "second_replay" + ] + }, + { + "id": "confidential_transfer_localnet_or_devnet", + "environment": "localnet_or_devnet", + "status": "not_run", + "requiredEvidence": [ + "simulation", + "proof_mode", + "signature", + "canonical_hydration", + "core_extraction", + "decode_replay", + "materialization", + "second_replay" + ] + }, + { + "id": "confidential_mint_burn_localnet_or_devnet", + "environment": "localnet_or_devnet", + "status": "not_run", + "requiredEvidence": [ + "simulation", + "proof_mode", + "signature", + "canonical_hydration", + "core_extraction", + "decode_replay", + "materialization", + "second_replay" + ] + }, + { + "id": "permissioned_confidential_burn_localnet_or_devnet", + "environment": "localnet_or_devnet", + "status": "not_run", + "requiredEvidence": [ + "simulation", + "proof_mode", + "signature", + "canonical_hydration", + "core_extraction", + "decode_replay", + "materialization", + "second_replay" + ] + }, + { + "id": "mainnet_observation_corpus", + "environment": "mainnet_observation", + "status": "not_run", + "requiredEvidence": [ + "signatures", + "outer_cpi", + "success_failure" + ] + }, + { + "id": "postgres_double_replay", + "environment": "postgres", + "status": "not_run", + "requiredEvidence": [ + "postgres_tests", + "first_replay", + "second_replay", + "no_duplicates" + ] + }, + { + "id": "tauri_smoke", + "environment": "tauri", + "status": "not_run", + "requiredEvidence": [ + "startup", + "routes", + "journal" + ] + } + ] +} diff --git a/migration/khadhroony-bot2-reference/docs/SPL_TOKEN_MATRIX.json b/migration/khadhroony-bot2-reference/docs/SPL_TOKEN_MATRIX.json new file mode 100644 index 0000000..2030d4c --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/SPL_TOKEN_MATRIX.json @@ -0,0 +1,1440 @@ +{ + "file": "docs/SPL_TOKEN_MATRIX.json", + "version": 3, + "schemaVersion": 2, + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "interface": { + "workspaceConstraint": "^3.0", + "expectedResolvedVersion": "3.0.0", + "cargoResolutionStatus": "validated_by_workspace_cargo_2026-07-15", + "features": [], + "bincodeEnabled": false, + "officialTag": "interface@v3.0.0", + "sourceCommit": "46eaacd", + "instructionSource": "https://github.com/solana-program/token/blob/interface@v3.0.0/interface/src/instruction.rs", + "stateSource": "https://github.com/solana-program/token/blob/interface@v3.0.0/interface/src/state.rs", + "legacyProcessorSource": "https://github.com/solana-program/token/blob/interface@v3.0.0/program/src/processor.rs", + "pTokenRelease": "p-token@v1.0.0" + }, + "limits": { + "minSigners": 1, + "maxSigners": 11, + "maxBatchInstructions": 64, + "maxBatchAccounts": 512, + "maxInstructionBytes": 16384, + "nestedBatchAllowed": false + }, + "deploymentAudit": { + "localnet": { + "status": "not_tested", + "evidence": [] + }, + "devnet": { + "status": "partially_validated", + "evidence": [ + "TransferChecked simulated and submitted with canonical post-validation on 2026-07-15", + "5Q71KeomfhHBueDsD92C7L21vHUpFGZnZzCFPGaCDAMHx6yV4Jy2fMDzCpze8mNr2c1GLSRT93mmBaYy2RGZ95ZY", + "Controlled no-ATA lifecycle validated InitializeAccount3, MintToChecked, TransferChecked, ApproveChecked, Revoke, BurnChecked and CloseAccount with canonical replay and idempotence on 2026-07-15", + "Batch tag 255 simulation-only succeeded on the classic program with one zero-amount TransferChecked child on 2026-07-16; 270 compute units; no runtime error", + "UnwrapLamports tag 45 simulation-only succeeded for one lamport on a non-associated classic native token account on 2026-07-16; 140 compute units; no runtime error" + ] + }, + "mainnet": { + "status": "real_corpus_decode_and_materialization_validated", + "evidence": [ + "370 classic SPL Token instructions decoded without unsupported, failed or unmatched input on 2026-07-15", + "371 committed materializations; 8 refused outputs matched 8 failed transactions; second replay selected 0 inputs", + "Observed materialized operations: approve 9, burn_checked 1, close_account 79, initialize_account 3, sync_native 56, transfer 2, transfer_checked 212" + ] + }, + "note": "Publication in interface 3.0.0 is tracked separately from processor deployment and successful simulation." + }, + "corpusAudit": { + "mainnet": { + "source": "real canonical transactions replayed from PostgreSQL", + "observedNormalizedOperations": { + "approve": 9, + "burn_checked": 1, + "close_account": 79, + "initialize_account": 3, + "sync_native": 56, + "transfer": 2, + "transfer_checked": 212 + }, + "materializedFamilies": { + "token_account": 362, + "risk": 9 + }, + "invariants": { + "uncommittedOutputs": 0, + "duplicatedBatchParents": 0, + "classicTokenFeeOutputs": 0, + "secondReplaySelected": 0 + }, + "normalizedOperationCaveat": "initialize_account is intentionally not assigned to a specific wire tag because the materialized operation name normalizes historical and current initialization variants" + }, + "devnet": { + "source": "controlled opt-in execution scenarios without ATA", + "transferChecked": "simulation, submission, confirmation, canonical hydration, core extraction, decode, one materialization and idempotent replay validated", + "lifecycle": "raw 82/165/165 account preparation followed by eleven simulation-first Token transactions; recovery after public RPC throttling was validated", + "recentInstructionProbe": { + "date": "2026-07-16", + "submission": false, + "batch": { + "success": true, + "computeUnits": 270, + "child": "TransferChecked amount=0", + "runtimeError": null + }, + "unwrapLamports": { + "success": true, + "computeUnits": 140, + "amountLamports": "1", + "nativeAccount": "3DjMgMbaNU4nLbv8avkZuTPXc31khcfp1bMPV2AcJkZk", + "runtimeError": null + } + } + } + }, + "instructions": [ + { + "name": "initialize_mint", + "tag": 0, + "status": "historical", + "layout": "1 + u8 + Pubkey + COption; 35 or 67 bytes", + "allowedSizes": [ + 35, + 67 + ], + "accounts": [ + "mint(w)", + "rent_sysvar" + ], + "authorityForm": "not_applicable", + "decodedFields": [ + "decimals", + "mint_authority", + "freeze_authority" + ], + "limits": [], + "event": "spl_token.initialize_mint", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "initialize_mint" + }, + "executorSupport": { + "status": "unsupported", + "reasonCode": "execution_spl_token_historical_variant_decode_only", + "reason": "obsolete Rent-dependent initialization variant; the decoder remains exhaustive and the executor uses the current no-Rent builder" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "initialize_account", + "tag": 1, + "status": "historical", + "layout": "tag only; 1 byte", + "allowedSizes": null, + "accounts": [ + "token_account(w)", + "mint", + "owner", + "rent_sysvar" + ], + "authorityForm": "not_applicable", + "decodedFields": [], + "limits": [], + "event": "spl_token.initialize_account", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "initialize_account" + }, + "executorSupport": { + "status": "unsupported", + "reasonCode": "execution_spl_token_historical_variant_decode_only", + "reason": "obsolete Rent-dependent initialization variant; the decoder remains exhaustive and the executor uses the current no-Rent builder" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "initialize_multisig", + "tag": 2, + "status": "historical", + "layout": "1 + u8; 2 bytes", + "allowedSizes": null, + "accounts": [ + "multisig_account(w)", + "rent_sysvar", + "members[1..11]" + ], + "authorityForm": "not_applicable", + "decodedFields": [ + "m", + "n_from_accounts" + ], + "limits": [ + "1 <= M <= N <= 11" + ], + "event": "spl_token.initialize_multisig", + "projections": [ + "token_accounts", + "admin", + "risk" + ], + "officialBuilder": { + "available": true, + "function": "initialize_multisig" + }, + "executorSupport": { + "status": "unsupported", + "reasonCode": "execution_spl_token_historical_variant_decode_only", + "reason": "obsolete Rent-dependent initialization variant; the decoder remains exhaustive and the executor uses the current no-Rent builder" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "transfer", + "tag": 3, + "status": "historical", + "layout": "1 + u64 LE; 9 bytes", + "allowedSizes": null, + "accounts": [ + "source(w)", + "destination(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "amount_raw" + ], + "limits": [], + "event": "spl_token.transfer", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "transfer" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.transfer", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "real_transaction_decoded_and_materialized_2026-07-15" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "5hNLki1wTmRtSkfi3DPmNPB91mGaCBmJ2KDY7bvvaQXPPNFdgYgJcuqQtmsidccbdZo73Du13K8rDbwvZd2fHfvh" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "approve", + "tag": 4, + "status": "historical", + "layout": "1 + u64 LE; 9 bytes", + "allowedSizes": null, + "accounts": [ + "source(w)", + "delegate", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "amount_raw" + ], + "limits": [], + "event": "spl_token.approve", + "projections": [ + "token_accounts", + "risk" + ], + "officialBuilder": { + "available": true, + "function": "approve" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.approve", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "real_transaction_decoded_and_materialized_2026-07-15" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "25GkUzBPcXWgBi6nAu8RAHRsohidKFVCVhmBMdy3Xr9JNZdjvJeiFBmdtQpyadEVx4BfVJZLyifwf9xsDyBEyh84" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "revoke", + "tag": 5, + "status": "historical", + "layout": "tag only; 1 byte", + "allowedSizes": null, + "accounts": [ + "source(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [], + "limits": [], + "event": "spl_token.revoke", + "projections": [ + "token_accounts", + "risk" + ], + "officialBuilder": { + "available": true, + "function": "revoke" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.revoke", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "submission_confirmation_and_replay_validated_2026-07-15", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "3osU5jfqDPdMqQKjTnRtvJLaQBxkwRCAJeSU1zEGCMHC5jFHAd5dV4SnHGK2hc6aBkLNLSzvqD7VgVjLxFfhk4MM" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "set_authority", + "tag": 6, + "status": "historical", + "layout": "1 + AuthorityType u8 + COption; 3 or 35 bytes", + "allowedSizes": [ + 3, + 35 + ], + "accounts": [ + "authority_target(w)", + "current_authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "authority_type", + "new_authority" + ], + "limits": [], + "event": "spl_token.set_authority", + "projections": [ + "admin", + "risk" + ], + "officialBuilder": { + "available": true, + "function": "set_authority" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.set_authority", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "mint_to", + "tag": 7, + "status": "historical", + "layout": "1 + u64 LE; 9 bytes", + "allowedSizes": null, + "accounts": [ + "mint(w)", + "destination(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "amount_raw" + ], + "limits": [], + "event": "spl_token.mint_to", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "mint_to" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.mint_to", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "burn", + "tag": 8, + "status": "historical", + "layout": "1 + u64 LE; 9 bytes", + "allowedSizes": null, + "accounts": [ + "source(w)", + "mint(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "amount_raw" + ], + "limits": [], + "event": "spl_token.burn", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "burn" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.burn", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "close_account", + "tag": 9, + "status": "historical", + "layout": "tag only; 1 byte", + "allowedSizes": null, + "accounts": [ + "source(w)", + "destination(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [], + "limits": [], + "event": "spl_token.close_account", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "close_account" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.close_account", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "submission_confirmation_and_replay_validated_2026-07-15", + "mainnet": "real_transaction_decoded_and_materialized_2026-07-15" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "27iYTpo9X5QNyQCXnfeHiVZqnqhszvrgsUDeC63ukjEF4KtYGPNJ4o4Vg4FGuzke5oHxybDnVvxFFMeqnHF2rSCB", + "hVS2YtL8zEbFPM1tyisWNScBoEkzfxDGeZKeZBpxA8rgEg6mQZXV67xdq6YUkhQX4GrfhPm87gvxupi9k56P9J8", + "4XUCaYXpeXPMBUmfHEWTzgiyoQUZNnF9jaZDAdrgi2qXABi6ZFBcuCUhgHQXqWtUb7V1gEVWXWmvZJKuyaNDqGrp" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "freeze_account", + "tag": 10, + "status": "historical", + "layout": "tag only; 1 byte", + "allowedSizes": null, + "accounts": [ + "token_account(w)", + "mint", + "freeze_authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [], + "limits": [], + "event": "spl_token.freeze_account", + "projections": [ + "token_accounts", + "risk" + ], + "officialBuilder": { + "available": true, + "function": "freeze_account" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.freeze_account", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "thaw_account", + "tag": 11, + "status": "historical", + "layout": "tag only; 1 byte", + "allowedSizes": null, + "accounts": [ + "token_account(w)", + "mint", + "freeze_authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [], + "limits": [], + "event": "spl_token.thaw_account", + "projections": [ + "token_accounts", + "risk" + ], + "officialBuilder": { + "available": true, + "function": "thaw_account" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.thaw_account", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "transfer_checked", + "tag": 12, + "status": "current", + "layout": "1 + u64 LE + u8; 10 bytes", + "allowedSizes": null, + "accounts": [ + "source(w)", + "mint", + "destination(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "amount_raw", + "decimals" + ], + "limits": [], + "event": "spl_token.transfer_checked", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "transfer_checked" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.transfer_checked", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "simulation_submission_confirmation_and_replay_validated_2026-07-15", + "mainnet": "real_transaction_decoded_and_materialized_2026-07-15" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "5Q71KeomfhHBueDsD92C7L21vHUpFGZnZzCFPGaCDAMHx6yV4Jy2fMDzCpze8mNr2c1GLSRT93mmBaYy2RGZ95ZY", + "2Xum6TUAKvu92gBUTCYS3LFtnYbMiR3bDq5VnrRS846ZgicsURqJrbD6LsJAJJD2jG3TPXVsMLY8UJx8NxDqPPbF", + "2125FHp6VjKaFApgDebwww1dGj1SFayKCwZPTwEieRWgKKGLe27Yjk9qKGT9zzc5Pc3Lsu9CRt8TDkJKdgJ1qpRP" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "approve_checked", + "tag": 13, + "status": "current", + "layout": "1 + u64 LE + u8; 10 bytes", + "allowedSizes": null, + "accounts": [ + "source(w)", + "mint", + "delegate", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "amount_raw", + "decimals" + ], + "limits": [], + "event": "spl_token.approve_checked", + "projections": [ + "token_accounts", + "risk" + ], + "officialBuilder": { + "available": true, + "function": "approve_checked" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.approve_checked", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "submission_confirmation_and_replay_validated_2026-07-15", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "4dC5AeizWUMSToEH8xjWjuUqK7GjBUEPD5kWGdi9LpdVBRni38dqocXjA8Wcx2SnLSAUKPaV3JbzKdpWAEu8ZXAR" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "mint_to_checked", + "tag": 14, + "status": "current", + "layout": "1 + u64 LE + u8; 10 bytes", + "allowedSizes": null, + "accounts": [ + "mint(w)", + "destination(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "amount_raw", + "decimals" + ], + "limits": [], + "event": "spl_token.mint_to_checked", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "mint_to_checked" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.mint_to_checked", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "submission_confirmation_and_replay_validated_2026-07-15", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "3yr7CtHeuQPFWFp424Q3NdPA1CUNm1xKd5z6hy1sW5HmbXnkudg4wXFmNhHo8mBJok1xHqqF9uaftqZugj4Aiw3f" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "burn_checked", + "tag": 15, + "status": "current", + "layout": "1 + u64 LE + u8; 10 bytes", + "allowedSizes": null, + "accounts": [ + "source(w)", + "mint(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "amount_raw", + "decimals" + ], + "limits": [], + "event": "spl_token.burn_checked", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "burn_checked" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.burn_checked", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "submission_confirmation_and_replay_validated_2026-07-15", + "mainnet": "real_transaction_decoded_and_materialized_2026-07-15" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "34nijQGRnUoDj6hmdh8fWw5p33dEBxDPG5n4x8QBE4TdzbzathYgCmxzJvutyHhgXj3dqwZgcVdbjtmqZKNDHC3i", + "5sZpdTv1YYLpQ8AynL2eMMeFVwJfCPp8Pwf2KC93XQDM6NiQo9FVvZaL5qp1EFqY98k6ZFTVEirhKoUP7xck8bDg", + "5mk53ez5rJfCPAGUW3uHbYJ1bkBhPNRKi9NAuk6gknXp9CQtKtqUxkVdy6dnxmRTMmewwuFY865V9J72TrwESH5g" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "initialize_account2", + "tag": 16, + "status": "historical", + "layout": "1 + Pubkey; 33 bytes", + "allowedSizes": null, + "accounts": [ + "token_account(w)", + "mint", + "rent_sysvar" + ], + "authorityForm": "not_applicable", + "decodedFields": [ + "owner" + ], + "limits": [], + "event": "spl_token.initialize_account2", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "initialize_account2" + }, + "executorSupport": { + "status": "unsupported", + "reasonCode": "execution_spl_token_historical_variant_decode_only", + "reason": "obsolete Rent-dependent initialization variant; the decoder remains exhaustive and the executor uses the current no-Rent builder" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "sync_native", + "tag": 17, + "status": "current", + "layout": "tag only; 1 byte", + "allowedSizes": null, + "accounts": [ + "native_token_account(w)", + "optional_rent_sysvar" + ], + "authorityForm": "not_applicable", + "decodedFields": [], + "limits": [], + "event": "spl_token.sync_native", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "sync_native" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.sync_native", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "real_transaction_decoded_and_materialized_2026-07-15" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "27iYTpo9X5QNyQCXnfeHiVZqnqhszvrgsUDeC63ukjEF4KtYGPNJ4o4Vg4FGuzke5oHxybDnVvxFFMeqnHF2rSCB" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "initialize_account3", + "tag": 18, + "status": "current", + "layout": "1 + Pubkey; 33 bytes", + "allowedSizes": null, + "accounts": [ + "token_account(w)", + "mint" + ], + "authorityForm": "not_applicable", + "decodedFields": [ + "owner" + ], + "limits": [], + "event": "spl_token.initialize_account3", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "initialize_account3" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.initialize_account", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "submission_confirmation_and_replay_validated_2026-07-15", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [ + "3yxsCf8tc1H2P9E3m6Kgu7J3XVeBw7wePuRTHvCaidQ74bsa2TP5xg4hpXAw1jkRSB3LrkLrXgx5eqvCQqRsXcfq" + ], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "initialize_multisig2", + "tag": 19, + "status": "current", + "layout": "1 + u8; 2 bytes", + "allowedSizes": null, + "accounts": [ + "multisig_account(w)", + "members[1..11]" + ], + "authorityForm": "not_applicable", + "decodedFields": [ + "m", + "n_from_accounts" + ], + "limits": [ + "1 <= M <= N <= 11" + ], + "event": "spl_token.initialize_multisig2", + "projections": [ + "token_accounts", + "admin", + "risk" + ], + "officialBuilder": { + "available": true, + "function": "initialize_multisig2" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.initialize_multisig", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "initialize_mint2", + "tag": 20, + "status": "current", + "layout": "1 + u8 + Pubkey + COption; 35 or 67 bytes", + "allowedSizes": [ + 35, + 67 + ], + "accounts": [ + "mint(w)" + ], + "authorityForm": "not_applicable", + "decodedFields": [ + "decimals", + "mint_authority", + "freeze_authority" + ], + "limits": [], + "event": "spl_token.initialize_mint2", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "initialize_mint2" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.initialize_mint", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "get_account_data_size", + "tag": 21, + "status": "current", + "layout": "tag; runtime ignores trailing data", + "allowedSizes": null, + "accounts": [ + "mint" + ], + "authorityForm": "not_applicable", + "decodedFields": [], + "limits": [], + "event": "spl_token.get_account_data_size", + "projections": [], + "officialBuilder": { + "available": true, + "function": "get_account_data_size" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.get_account_data_size", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": true, + "coreReplayValidation": "unavailable_in_current_core_input" + }, + "notes": [] + }, + { + "name": "initialize_immutable_owner", + "tag": 22, + "status": "current", + "layout": "tag only; classic runtime compatibility no-op", + "allowedSizes": null, + "accounts": [ + "token_account(w)" + ], + "authorityForm": "not_applicable", + "decodedFields": [], + "limits": [], + "event": "spl_token.initialize_immutable_owner", + "projections": [], + "officialBuilder": { + "available": true, + "function": "initialize_immutable_owner" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.initialize_immutable_owner", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [ + "Classic SPL Token compatibility no-op; no Token-2022 extension is inferred." + ] + }, + { + "name": "amount_to_ui_amount", + "tag": 23, + "status": "current", + "layout": "1 + u64 LE; 9 bytes", + "allowedSizes": null, + "accounts": [ + "mint" + ], + "authorityForm": "not_applicable", + "decodedFields": [ + "amount_raw" + ], + "limits": [], + "event": "spl_token.amount_to_ui_amount", + "projections": [], + "officialBuilder": { + "available": true, + "function": "amount_to_ui_amount" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.amount_to_ui_amount", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": true, + "coreReplayValidation": "unavailable_in_current_core_input" + }, + "notes": [] + }, + { + "name": "ui_amount_to_amount", + "tag": 24, + "status": "current", + "layout": "1 + UTF-8 remainder; at least 1 byte at wire layer", + "allowedSizes": null, + "accounts": [ + "mint" + ], + "authorityForm": "not_applicable", + "decodedFields": [ + "ui_amount_utf8" + ], + "limits": [], + "event": "spl_token.ui_amount_to_amount", + "projections": [], + "officialBuilder": { + "available": true, + "function": "ui_amount_to_amount" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.ui_amount_to_amount", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": true, + "coreReplayValidation": "unavailable_in_current_core_input" + }, + "notes": [] + }, + { + "name": "withdraw_excess_lamports", + "tag": 38, + "status": "current", + "layout": "tag only; 1 byte", + "allowedSizes": null, + "accounts": [ + "source(w)", + "destination(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [], + "limits": [], + "event": "spl_token.withdraw_excess_lamports", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "withdraw_excess_lamports" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.withdraw_excess_lamports", + "constructibility": "official_builder_proven", + "clusterDeployment": "separate_from_library_support" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "not_tested", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [] + }, + { + "name": "unwrap_lamports", + "tag": 45, + "status": "recent", + "layout": "1 + COption; 2 or 10 bytes", + "allowedSizes": [ + 2, + 10 + ], + "accounts": [ + "native_token_account(w)", + "destination(w)", + "authority", + "multisig_signers*" + ], + "authorityForm": "single_or_multisig", + "decodedFields": [ + "optional_amount_lamports" + ], + "limits": [], + "event": "spl_token.unwrap_lamports", + "projections": [ + "token_accounts" + ], + "officialBuilder": { + "available": true, + "function": "unwrap_lamports" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.unwrap_lamports", + "constructibility": "official_builder_proven", + "clusterDeployment": "devnet_simulation_succeeded_2026-07-16" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "simulation_succeeded_2026-07-16", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": false + }, + "notes": [ + "Published by interface 3.0.0; cluster deployment requires separate simulation evidence.", + "Devnet simulation-only succeeded for one lamport from a controlled non-associated wrapped SOL account; 140 compute units and no runtime error." + ] + }, + { + "name": "batch", + "tag": 255, + "status": "recent", + "layout": "1 + repeated(u8 account_count, u8 data_len, instruction_data[data_len])", + "allowedSizes": null, + "accounts": [ + "concatenated sub-instruction account slices" + ], + "authorityForm": "not_applicable", + "decodedFields": [ + "ordered_sub_instructions", + "account_slices" + ], + "limits": [ + "64 sub-instructions", + "512 total accounts", + "255 bytes per child", + "no nested Batch" + ], + "event": "spl_token.batch", + "projections": [ + "child_dependent" + ], + "officialBuilder": { + "available": true, + "function": "batch" + }, + "executorSupport": { + "status": "supported", + "operationCode": "spl_token.batch", + "constructibility": "official_builder_proven", + "clusterDeployment": "devnet_simulation_succeeded_2026-07-16" + }, + "clusterStatus": { + "localnet": "not_tested", + "devnet": "simulation_succeeded_2026-07-16", + "mainnet": "not_tested" + }, + "syntheticFixtures": [ + "minimal_valid", + "truncated", + "unexpected_suffix" + ], + "realSignatures": [], + "returnData": { + "writes": true, + "coreReplayValidation": "unavailable_in_current_core_input" + }, + "notes": [ + "Child identities derive from parent instruction path and child position.", + "Devnet simulation-only succeeded with one zero-amount TransferChecked child; 270 compute units and no runtime error." + ] + } + ] +} diff --git a/migration/khadhroony-bot2-reference/docs/SQL_DEMOS.md b/migration/khadhroony-bot2-reference/docs/SQL_DEMOS.md new file mode 100644 index 0000000..51e7000 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/SQL_DEMOS.md @@ -0,0 +1,109 @@ + + + +# Démos SQL + +## Fenêtres disponibles + +| Fenêtre | Rôle | +|------------------------------|------------------------------------------------------------------------------------------------------| +| `demo_sql_diag` | Profil, backend, DSN masqué, santé PostgreSQL et tables attendues. | +| `demo_sql_pg_raw` | Transactions canoniques et observations d’acquisition. | +| `demo_sql_pg_core` | Transactions, comptes, instructions, inner instructions, logs, balances et ledger. | +| `demo_sql_replay_candidates` | Exploration read-only, filtrage et export des signatures, programmes, mints, owners et account keys. | + +Depuis `0.3.4`, le diagnostic core inclut : + +```text +kb_sol_ops_processing_ledger +``` + +La fenêtre `demo_core_extraction` exécute le worker puis permet d’ouvrir les diagnostics raw/core et le sélecteur de candidats. Les fenêtres SQL restent read-only et ne modifient aucune donnée. + +## Sélecteur de candidats replay + +`demo_sql_replay_candidates` sépare l’exploration en cinq tableaux DataTables avec intégration Bootstrap 5 : + +1. transactions et signatures ; +2. programmes observés dans les instructions outer, inner et dans les logs reliés ; +3. mints ; +4. owners ; +5. account keys. + +Les requêtes PostgreSQL sont statiques, paramétrées et limitées à `5 000` lignes au maximum par chargement. Aucun éditeur SQL libre n’est exposé. + +### Transactions et signatures + +Filtres serveur disponibles : + +- fragment de signature ; +- plage inclusive de slots ; +- état raw ; +- statut du ledger `core_extraction` ; +- program ID exact avec portée `any`, `outer`, `inner` ou `logs` ; +- mint, owner ou account key exact ; +- limite et ordre des slots. + +Le tableau affiche aussi la présence du graphe core, le statut du ledger, sa version et son nombre de tentatives, ainsi que les cardinalités outer/inner. + +### Programmes + +Le tableau agrège les occurrences issues de : + +```text +kb_sol_core_instructions +kb_sol_core_inner_instructions +kb_sol_core_logs +``` + +Chaque ligne expose le code connu de `kb_program_ids` lorsqu’il existe, le nombre de transactions distinctes, d’instructions outer, d’instructions inner, de logs reliés et la plage de slots observée. Le filtre propose une liste des programmes enregistrés tout en conservant une saisie libre pour les identifiants inconnus ou partiels. + +Pour appliquer un programme au tableau des transactions, il faut cocher exactement une ligne puis utiliser « Utiliser comme filtre transactions ». Lorsqu’aucune ligne n’est cochée, l’action accepte aussi une seule ligne restant après le filtre local DataTables. + +### Mints, owners et comptes + +Les trois familles disposent de tableaux et filtres indépendants. Les mints et owners proviennent de `kb_sol_core_balance_changes`; les account keys proviennent de `kb_sol_core_account_keys` et ne sont pas classifiées automatiquement comme wallet, pool, mint ou token account. + +Chaque tableau affiche transactions distinctes, occurrences, occurrences par transaction, slots minimum/maximum et étendue. Une valeur sélectionnée peut être appliquée directement au filtre du tableau des transactions. + +### Sélection et export + +Dans chaque tableau : + +- la première colonne contient une case à cocher gérée par l’extension DataTables Select ; +- la case du header sélectionne ou désélectionne toutes les lignes correspondant au filtre local ; +- une sélection explicite est prioritaire ; +- sans sélection, la copie et l’export utilisent toutes les lignes correspondant au filtre local DataTables ; +- les identifiants peuvent être copiés dans le presse-papiers sous forme multiligne ; +- le frontend construit un CSV diagnostique avec BOM UTF-8, séparateur `;` et CRLF ; +- une commande Rust bornée écrit le fichier dans `data/exports_csv/` et ajoute un suffixe lorsque le nom existe déjà ; +- les valeurs longues sont tronquées visuellement, exposées intégralement par tooltip et copiables cellule par cellule ; +- tous les tableaux affichent 10 lignes par défaut et possèdent un reset complet des filtres. + +La copie multiligne des signatures peut être collée telle quelle dans le mode `Signatures explicites` de `demo_core_extraction`. L’export ne dépend plus du téléchargement HTML du WebView. + +### Lecture des colonnes transactions + +Les cardinalités sont séparées en quatre colonnes : `Outer inst.`, `Inner inst.`, `Outer pr.` et `Inner pr.`. Les deux premières comptent les instructions ; les deux dernières comptent les program IDs distincts. + +La colonne `Core` décrit le graphe structurel : + +- `absent` : aucune ligne `kb_sol_core_transactions` ; +- `présent` : graphe core présent et transaction Solana réussie ; +- `échec on-chain` : graphe core présent, mais la transaction Solana elle-même a retourné une erreur. + +`échec on-chain` n’est donc pas un échec de l’extracteur. Le tri DataTables utilise un rang numérique distinct pour ces trois états. + +### Pools et paires + +Aucun onglet pool/pair n’est ajouté dans `0.3.4`. Le core connaît des account keys mais ne peut pas déterminer de façon fiable leur rôle sémantique. Les futurs décodeurs et matérialiseurs alimenteront les tables catalogues de pools et de paires ; l’onglet pourra alors reposer sur des identifiants explicites plutôt que sur une heuristique. + +## Contrôles recommandés après extraction + +- comparer le nombre de transactions raw `core_extracted` et de transactions core ; +- vérifier l’absence de lignes filles orphelines ; +- vérifier l’unicité des chemins et indices ; +- comparer les statuts ledger avec les graphes core présents ; +- examiner les lignes raw `failed` et leur `lifecycle_reason`. + +Les requêtes prêtes à l’emploi sont dans `sql/validation/000_core_integrity.sql`. diff --git a/migration/khadhroony-bot2-reference/docs/SURFACE_CRATE_MATRIX.md b/migration/khadhroony-bot2-reference/docs/SURFACE_CRATE_MATRIX.md new file mode 100644 index 0000000..b465fa3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/SURFACE_CRATE_MATRIX.md @@ -0,0 +1,272 @@ + + + +# Matrice des crates réservés + +Ce document liste les crates actuellement présents dans le workspace. Un crate réservé ne signifie pas que le décodage, la matérialisation ou l’exécution est validé. + +## Principes + +- Les surfaces non classifiées ne doivent pas avoir de crate. +- Les décodeurs et exécuteurs sont réservés par surface canonique lorsque la surface est classifiée. +- Les détails de validation restent dans `registry/program_registry_seed.toml` et les docs spécialisées. + +## Solana + +| Surface | Décodeur | Exécuteur | +|---------------|--------------------------|---------------------------| +| `solana_core` | `kb_decoder_solana_core` | `kb_executor_solana_core` | + +## Spl + +| Surface | Décodeur | Exécuteur | +|--------------------------------|-------------------------------------------|--------------------------------------------| +| `spl_account_compression` | `kb_decoder_spl_account_compression` | `kb_executor_spl_account_compression` | +| `spl_associated_token_account` | `kb_decoder_spl_associated_token_account` | `kb_executor_spl_associated_token_account` | +| `spl_memo` | `kb_decoder_spl_memo` | `kb_executor_spl_memo` | +| `spl_name_service` | `kb_decoder_metadata_spl_name_service` | `kb_executor_metadata_spl_name_service` | +| `spl_noop` | `kb_decoder_spl_noop` | `kb_executor_spl_noop` | +| `spl_single_pool` | `kb_decoder_spl_single_pool` | `kb_executor_spl_single_pool` | +| `spl_stake_pool` | `kb_decoder_spl_stake_pool` | `kb_executor_spl_stake_pool` | +| `spl_token` | `kb_decoder_spl_token` | `kb_executor_spl_token` | +| `spl_token_2022` | `kb_decoder_spl_token_2022` | `kb_executor_spl_token_2022` | + + +## Amm + +| Surface | Décodeur | Exécuteur | +|----------------------------|---------------------------------------|----------------------------------------| +| `amm_aldrin_v1` | `kb_decoder_amm_aldrin_v1` | `kb_executor_amm_aldrin_v1` | +| `amm_aldrin_v2` | `kb_decoder_amm_aldrin_v2` | `kb_executor_amm_aldrin_v2` | +| `amm_alphaq` | `kb_decoder_amm_alphaq` | `kb_executor_amm_alphaq` | +| `amm_believe` | `kb_decoder_amm_believe` | `kb_executor_amm_believe` | +| `amm_bonk_swap` | `kb_decoder_amm_bonk_swap` | `kb_executor_amm_bonk_swap` | +| `amm_fluxbeam` | `kb_decoder_amm_fluxbeam` | `kb_executor_amm_fluxbeam` | +| `amm_goon_fi` | `kb_decoder_amm_goon_fi` | `kb_executor_amm_goon_fi` | +| `amm_goosefx_gamma` | `kb_decoder_amm_goosefx_gamma` | `kb_executor_amm_goosefx_gamma` | +| `amm_goosefx_v2` | `kb_decoder_amm_goosefx_v2` | `kb_executor_amm_goosefx_v2` | +| `amm_guac_swap` | `kb_decoder_amm_guac_swap` | `kb_executor_amm_guac_swap` | +| `amm_lifinity_swap_v2` | `kb_decoder_amm_lifinity_swap_v2` | `kb_executor_amm_lifinity_swap_v2` | +| `amm_metadao_futarchy_amm` | `kb_decoder_amm_metadao_futarchy_amm` | `kb_executor_amm_metadao_futarchy_amm` | +| `amm_metadao_v0_5` | `kb_decoder_amm_metadao_v0_5` | `kb_executor_amm_metadao_v0_5` | +| `amm_meteora_damm_v1` | `kb_decoder_amm_meteora_damm_v1` | `kb_executor_amm_meteora_damm_v1` | +| `amm_meteora_damm_v2` | `kb_decoder_amm_meteora_damm_v2` | `kb_executor_amm_meteora_damm_v2` | +| `amm_obric_v2` | `kb_decoder_amm_obric_v2` | `kb_executor_amm_obric_v2` | +| `amm_one_dex` | `kb_decoder_amm_one_dex` | `kb_executor_amm_one_dex` | +| `amm_pump_swap` | `kb_decoder_amm_pump_swap` | `kb_executor_amm_pump_swap` | +| `amm_raydium_lp_v4` | `kb_decoder_amm_raydium_lp_v4` | `kb_executor_amm_raydium_lp_v4` | +| `amm_solfi` | `kb_decoder_amm_solfi` | `kb_executor_amm_solfi` | +| `amm_solfi_v2` | `kb_decoder_amm_solfi_v2` | `kb_executor_amm_solfi_v2` | +| `amm_vertigo` | `kb_decoder_amm_vertigo` | `kb_executor_amm_vertigo` | +| `amm_virtuals` | `kb_decoder_amm_virtuals` | `kb_executor_amm_virtuals` | +| `amm_woofi` | `kb_decoder_amm_woofi` | `kb_executor_amm_woofi` | +| `amm_zero_fi` | `kb_decoder_amm_zero_fi` | `kb_executor_amm_zero_fi` | +| `amm_zora` | `kb_decoder_amm_zora` | `kb_executor_amm_zora` | + +## Cpmm + +| Surface | Décodeur | Exécuteur | +|----------------|---------------------------|----------------------------| +| `cpmm_raydium` | `kb_decoder_cpmm_raydium` | `kb_executor_cpmm_raydium` | + +## Clmm + +| Surface | Décodeur | Exécuteur | +|-----------------------|----------------------------------|-----------------------------------| +| `clmm_byreal` | `kb_decoder_clmm_byreal` | `kb_executor_clmm_byreal` | +| `clmm_fusion` | `kb_decoder_clmm_fusion` | `kb_executor_clmm_fusion` | +| `clmm_orca_whirlpool` | `kb_decoder_clmm_orca_whirlpool` | `kb_executor_clmm_orca_whirlpool` | +| `clmm_pancake_swap` | `kb_decoder_clmm_pancake_swap` | `kb_executor_clmm_pancake_swap` | +| `clmm_raydium` | `kb_decoder_clmm_raydium` | `kb_executor_clmm_raydium` | +| `clmm_stabble` | `kb_decoder_clmm_stabble` | `kb_executor_clmm_stabble` | + +## Dlmm + +| Surface | Décodeur | Exécuteur | +|----------------|---------------------------|----------------------------| +| `dlmm_meteora` | `kb_decoder_dlmm_meteora` | `kb_executor_dlmm_meteora` | + +## Stable Swap + +| Surface | Décodeur | Exécuteur | +|------------------------------|-----------------------------------------|------------------------------------------| +| `stable_swap_hylo_exchange` | `kb_decoder_stable_swap_hylo_exchange` | `kb_executor_stable_swap_hylo_exchange` | +| `stable_swap_jupiter_stable` | `kb_decoder_stable_swap_jupiter_stable` | `kb_executor_stable_swap_jupiter_stable` | +| `stable_swap_numeraire` | `kb_decoder_stable_swap_numeraire` | `kb_executor_stable_swap_numeraire` | +| `stable_swap_stabble` | `kb_decoder_stable_swap_stabble` | `kb_executor_stable_swap_stabble` | + +## Weighted Swap + +| Surface | Décodeur | Exécuteur | +|-------------------------|------------------------------------|-------------------------------------| +| `weighted_swap_stabble` | `kb_decoder_weighted_swap_stabble` | `kb_executor_weighted_swap_stabble` | + +## Launchpad + +| Surface | Décodeur | Exécuteur | +|-------------------------------|------------------------------------------|-------------------------------------------| +| `launchpad_boop_fun` | `kb_decoder_launchpad_boop_fun` | `kb_executor_launchpad_boop_fun` | +| `launchpad_metadao_ico` | `kb_decoder_launchpad_metadao_ico` | `kb_executor_launchpad_metadao_ico` | +| `launchpad_meteora_dbc` | `kb_decoder_launchpad_meteora_dbc` | `kb_executor_launchpad_meteora_dbc` | +| `launchpad_moonit` | `kb_decoder_launchpad_moonit` | `kb_executor_launchpad_moonit` | +| `launchpad_orca_wavebreak` | `kb_decoder_launchpad_orca_wavebreak` | `kb_executor_launchpad_orca_wavebreak` | +| `launchpad_printr` | `kb_decoder_launchpad_printr` | `kb_executor_launchpad_printr` | +| `launchpad_pump_fun` | `kb_decoder_launchpad_pump_fun` | `kb_executor_launchpad_pump_fun` | +| `launchpad_pump_pumpup_ai` | `kb_decoder_launchpad_pump_pumpup_ai` | `kb_executor_launchpad_pump_pumpup_ai` | +| `launchpad_raydium_launchlab` | `kb_decoder_launchpad_raydium_launchlab` | `kb_executor_launchpad_raydium_launchlab` | + +## Router + +| Surface | Décodeur | Exécuteur | +|--------------------------------|-------------------------------------------|--------------------------------------------| +| `router_dflow_aggregator_v4` | `kb_decoder_router_dflow_aggregator_v4` | `kb_executor_router_dflow_aggregator_v4` | +| `router_jupiter_aggregator_v4` | `kb_decoder_router_jupiter_aggregator_v4` | `kb_executor_router_jupiter_aggregator_v4` | +| `router_jupiter_aggregator_v6` | `kb_decoder_router_jupiter_aggregator_v6` | `kb_executor_router_jupiter_aggregator_v6` | +| `router_jupiter_dca` | `kb_decoder_router_jupiter_dca` | `kb_executor_router_jupiter_dca` | +| `router_okx_labs_v1` | `kb_decoder_router_okx_labs_v1` | `kb_executor_router_okx_labs_v1` | +| `router_okx_labs_v2` | `kb_decoder_router_okx_labs_v2` | `kb_executor_router_okx_labs_v2` | + +## Orderbook + +| Surface | Décodeur | Exécuteur | +|------------------------------------|-----------------------------------------------|------------------------------------------------| +| `orderbook_jupiter_limit_order` | `kb_decoder_orderbook_jupiter_limit_order` | `kb_executor_orderbook_jupiter_limit_order` | +| `orderbook_jupiter_limit_order_v2` | `kb_decoder_orderbook_jupiter_limit_order_v2` | `kb_executor_orderbook_jupiter_limit_order_v2` | +| `orderbook_openbook_v2` | `kb_decoder_orderbook_openbook_v2` | `kb_executor_orderbook_openbook_v2` | + +## Admin + +| Surface | Décodeur | Exécuteur | +|----------------------|---------------------------------|----------------------------------| +| `admin_jupiter_lock` | `kb_decoder_admin_jupiter_lock` | `kb_executor_admin_jupiter_lock` | +| `admin_pump_fees` | `kb_decoder_admin_pump_fees` | `kb_executor_admin_pump_fees` | + +## Fees + +| Surface | Décodeur | Exécuteur | +|--------------------------|-------------------------------------|--------------------------------------| +| `fees_bags_fee_share_v1` | `kb_decoder_fees_bags_fee_share_v1` | `kb_executor_fees_bags_fee_share_v1` | +| `fees_bags_fee_share_v2` | `kb_decoder_fees_bags_fee_share_v2` | `kb_executor_fees_bags_fee_share_v2` | + +## Lock + +| Surface | Décodeur | Exécuteur | +|-------------------|------------------------------|-------------------------------| +| `lock_raydium_lp` | `kb_decoder_lock_raydium_lp` | `kb_executor_lock_raydium_lp` | + +## Adapter + +| Surface | Décodeur | Exécuteur | +|---------------------------------|--------------------------------------------|---------------------------------------------| +| `adapter_saber_decimal_wrapper` | `kb_decoder_adapter_saber_decimal_wrapper` | `kb_executor_adapter_saber_decimal_wrapper` | + +## Governance + +| Surface | Décodeur | Exécuteur | +|-------------------------------|------------------------------------------|-------------------------------------------| +| `governance_metadao_bid_wall` | `kb_decoder_governance_metadao_bid_wall` | `kb_executor_governance_metadao_bid_wall` | + +## Bridge + +| Surface | Décodeur | Exécuteur | +|------------------------------------------------|-----------------------------------------------------------|------------------------------------------------------------| +| `bridge_circle_cctp_token_messenger_minter` | `kb_decoder_bridge_circle_cctp_token_messenger_minter` | `kb_executor_bridge_circle_cctp_token_messenger_minter` | +| `bridge_circle_cctp_token_messenger_minter_v2` | `kb_decoder_bridge_circle_cctp_token_messenger_minter_v2` | `kb_executor_bridge_circle_cctp_token_messenger_minter_v2` | +| `bridge_layer_zero_endpoint` | `kb_decoder_bridge_layer_zero_endpoint` | `kb_executor_bridge_layer_zero_endpoint` | +| `bridge_layer_zero_executor` | `kb_decoder_bridge_layer_zero_executor` | `kb_executor_bridge_layer_zero_executor` | + +## Rwa + +| Surface | Décodeur | Exécuteur | +|---------------------------|--------------------------------------|---------------------------------------| +| `rwa_ondo_global_markets` | `kb_decoder_rwa_ondo_global_markets` | `kb_executor_rwa_ondo_global_markets` | + +## Lending + +| Surface | Décodeur | Exécuteur | +|-----------------------------------|----------------------------------------------|-----------------------------------------------| +| `lending_clone` | `kb_decoder_lending_clone` | `kb_executor_lending_clone` | +| `lending_jupiter_lend_borrow` | `kb_decoder_lending_jupiter_lend_borrow` | `kb_executor_lending_jupiter_lend_borrow` | +| `lending_jupiter_lend_earn` | `kb_decoder_lending_jupiter_lend_earn` | `kb_executor_lending_jupiter_lend_earn` | +| `lending_jupiter_lend_flash_loan` | `kb_decoder_lending_jupiter_lend_flash_loan` | `kb_executor_lending_jupiter_lend_flash_loan` | +| `lending_jupiter_lend_liquidity` | `kb_decoder_lending_jupiter_lend_liquidity` | `kb_executor_lending_jupiter_lend_liquidity` | +| `lending_kamino` | `kb_decoder_lending_kamino` | `kb_executor_lending_kamino` | +| `lending_marginfi_v2` | `kb_decoder_lending_marginfi_v2` | `kb_executor_lending_marginfi_v2` | + +## Staking + +| Surface | Décodeur | Exécuteur | +|----------------------------|---------------------------------------|----------------------------------------| +| `staking_kamino_farm` | `kb_decoder_staking_kamino_farm` | `kb_executor_staking_kamino_farm` | +| `staking_marinade_finance` | `kb_decoder_staking_marinade_finance` | `kb_executor_staking_marinade_finance` | +| `staking_solayer` | `kb_decoder_staking_solayer` | `kb_executor_staking_solayer` | + +## Vault + +| Surface | Décodeur | Exécuteur | +|-----------------------------|----------------------------------------|-----------------------------------------| +| `vault_carrot_defi` | `kb_decoder_vault_carrot_defi` | `kb_executor_vault_carrot_defi` | +| `vault_hylo_stability_pool` | `kb_decoder_vault_hylo_stability_pool` | `kb_executor_vault_hylo_stability_pool` | +| `vault_kamino` | `kb_decoder_vault_kamino` | `kb_executor_vault_kamino` | +| `vault_kamino_v2` | `kb_decoder_vault_kamino_v2` | `kb_executor_vault_kamino_v2` | +| `vault_kamino_yvaults` | `kb_decoder_vault_kamino_yvaults` | `kb_executor_vault_kamino_yvaults` | +| `vault_meteora` | `kb_decoder_vault_meteora` | `kb_executor_vault_meteora` | + +## Vesting + +| Surface | Décodeur | Exécuteur | +|----------------------|---------------------------------|----------------------------------| +| `vesting_streamflow` | `kb_decoder_vesting_streamflow` | `kb_executor_vesting_streamflow` | + +## Nft + +| Surface | Décodeur | Exécuteur | +|--------------------------|-------------------------------------|--------------------------------------| +| `nft_metaplex_bubblegum` | `kb_decoder_nft_metaplex_bubblegum` | `kb_executor_nft_metaplex_bubblegum` | + +## Treasury + +| Surface | Décodeur | Exécuteur | +|---------------------------------------|--------------------------------------------------|---------------------------------------------------| +| `treasury_helium_treasury_management` | `kb_decoder_treasury_helium_treasury_management` | `kb_executor_treasury_helium_treasury_management` | + +## Wallet + +| Surface | Décodeur | Exécuteur | +|--------------------------------------|-------------------------------------------------|--------------------------------------------------| +| `wallet_jupiter_apepro_smart_wallet` | `kb_decoder_wallet_jupiter_apepro_smart_wallet` | `kb_executor_wallet_jupiter_apepro_smart_wallet` | + +## Perpetuals + +| Surface | Décodeur | Exécuteur | +|-----------------------|----------------------------------|-----------------------------------| +| `perpetuals_drift_v2` | `kb_decoder_perpetuals_drift_v2` | `kb_executor_perpetuals_drift_v2` | +| `perpetuals_jupiter` | `kb_decoder_perpetuals_jupiter` | `kb_executor_perpetuals_jupiter` | +| `perpetuals_zeta` | `kb_decoder_perpetuals_zeta` | `kb_executor_perpetuals_zeta` | + +## Matérialisateurs + +| Crate | +|---------------------------------------| +| `kb_materializer_admin` | +| `kb_materializer_bridge` | +| `kb_materializer_compliance_audit` | +| `kb_materializer_fees` | +| `kb_materializer_governance` | +| `kb_materializer_lending` | +| `kb_materializer_lifecycle` | +| `kb_materializer_liquidity` | +| `kb_materializer_metadata` | +| `kb_materializer_nft` | +| `kb_materializer_oracle` | +| `kb_materializer_orderbook` | +| `kb_materializer_perpetuals` | +| `kb_materializer_pool_state` | +| `kb_materializer_rewards` | +| `kb_materializer_risk` | +| `kb_materializer_routing` | +| `kb_materializer_staking` | +| `kb_materializer_token_accounts` | +| `kb_materializer_token_metadata_risk` | +| `kb_materializer_trades` | +| `kb_materializer_vault` | diff --git a/migration/khadhroony-bot2-reference/docs/TRACING_CONTRACT.md b/migration/khadhroony-bot2-reference/docs/TRACING_CONTRACT.md new file mode 100644 index 0000000..b098342 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/TRACING_CONTRACT.md @@ -0,0 +1,168 @@ + + + +# Contrat de tracing par crate + +## Objectif + +Chaque événement est émis par la crate qui prend réellement la décision. `kb_app_demo` journalise les frontières Tauri, les actions utilisateur, l’état des fenêtres et les résumés d’orchestration ; il ne recopie pas les décisions internes du RPC, du pipeline, du store, d’un décodeur, d’un matérialiseur ou d’un exécuteur. + +## Classification des crates + +Une crate est opérationnelle lorsqu’elle réalise au moins une des actions suivantes : + +- I/O réseau ou base de données ; +- orchestration concurrente, retry, pacing ou annulation ; +- décodage ou matérialisation ; +- construction ou simulation d’un plan d’exécution ; +- décision runtime mutable ayant un effet observable. + +Une crate passive contient uniquement des types, contrats, DTO, traits sans implémentation, registres ou constantes. Elle ne dépend pas de `tracing`. `kb_config` reste une exception de bootstrap : son chargement et sa validation précèdent l’installation du subscriber. + +## Cible canonique + +Toute crate qui déclare `tracing.workspace = true` doit : + +- posséder `src/constants.rs` ; +- définir exactement une constante `pub(crate) const TRACING_TARGET` ; +- utiliser comme valeur le nom exact du package Cargo ; +- appeler les macros avec `target: crate::constants::TRACING_TARGET` ; +- déplacer la granularité interne dans des champs structurés. + +Exemple : + +```rust +tracing::error!( + target: crate::constants::TRACING_TARGET, + action = "decoder_outcome_failure", + campaign_id = %campaign_id, + signature = %input.signature, + instruction_path = %input.instruction_path, + program_id = %input.program_id, + processor_name = %identity.name, + processor_version = %identity.version, + status = ?result.status, + diagnostics = ?result.diagnostics, + "contextual instruction was not decoded successfully" +); +``` + +Les targets `khbot.*`, les targets suffixés par module et les targets de fenêtre ne sont plus utilisés par les crates opérationnelles migrées. Les identifiants frontend historiques restent acceptés comme valeurs du champ `frontend_target`, mais l’événement Rust est émis sous `kb_app_demo`. + +## Crates actuellement routées + +La configuration couvre toutes les crates qui déclarent actuellement `tracing.workspace = true` : + +```text +kb_app_demo +kb_decoder_solana_core +kb_decoder_spl_associated_token_account +kb_decoder_spl_memo +kb_decoder_spl_token +kb_decoder_spl_token_2022 +kb_executor_metadata_spl_name_service +kb_execution_solana +kb_executor_solana_core +kb_executor_spl_account_compression +kb_executor_spl_associated_token_account +kb_executor_spl_memo +kb_executor_spl_noop +kb_executor_spl_single_pool +kb_logging +kb_materializer_admin +kb_materializer_compliance_audit +kb_materializer_lifecycle +kb_materializer_staking +kb_materializer_transaction_annotations +kb_pipeline +kb_rpc +kb_store_pg +kb_wallet +``` + +Le test `every_tracing_crate_has_one_canonical_target_constant` détecte automatiquement toute crate avec `tracing.workspace = true` et vérifie la présence d’une unique constante canonique. Le test de `kb_config` vérifie parallèlement la matrice de routes de chaque profil. + +Les routes sont filtrées au niveau de leur writer plutôt que par un `Filtered` layer distinct. Un seul filtre d’admission agrégé empêche en amont le formatage des événements refusés par toutes les routes. Cette architecture préserve toutes les sorties globales et par crate au-delà de la limite interne de 64 identifiants de filtres de `tracing-subscriber`. Le test `more_than_64_routes_compose_without_filtered_layer_ids` protège explicitement ce contrat. + +## Granularité et corrélation + +Le target identifie la crate. Les champs décrivent l’action et le contexte : + +```text +action +stage +window +campaign_id +capture_session_id +signature +slot +instruction_path +program_id +processor_name +processor_version +materializer_name +materializer_version +input_key +input_hash +status +decision +error_code +``` + +Les spans de campagne et d’input utilisent eux aussi le target canonique `kb_pipeline`. Les événements des autres crates conservent leur propre target et reprennent les identifiants de corrélation utiles dans leurs champs. + +## Politique des erreurs de décodage et de matérialisation + +Un événement `error` est obligatoire pour : + +- un input sélectionné sans décodeur compatible ; +- un résultat de décodeur `failed` ; +- un résultat de décodeur `unsupported` après dispatch vers une surface déclarée compatible ; +- un résultat de décodeur invalide au regard du contrat API ; +- un résultat de matérialiseur `failed` ; +- une erreur de persistance decode, couverture, ledger ou matérialisation ; +- une campagne qui termine avec `unmatched > 0` ou `failed_inputs > 0`. + +L’événement doit permettre de retrouver rapidement l’input et la cause. Il conserve, lorsque disponibles, la campagne, la signature, le slot, le chemin d’instruction, le program ID, l’identité et la version du processor, la clé/hash d’input, le statut et les diagnostics structurés. + +Les cas suivants ne sont pas des erreurs logicielles : + +- transaction Solana échouée mais payload correctement décodé ; +- observation non commitée conformément au statut on-chain ; +- matérialisation `refused` par la politique de transaction ; +- entrée volontairement `ignored` avec justification ; +- annulation coopérative demandée par l’opérateur. + +Ils sont journalisés en `debug`, `info` ou `warn` selon leur impact. + +## Responsabilité + +- `kb_rpc` : sélection d’endpoint, requêtes, réponses bornées, retry, rate limit et transport. +- `kb_execution_solana` : assemblage du message, contrôle du contrat de signataires, liaison de simulation et signature transactionnelle. +- `kb_store_pg` : transactions SQL, commit/rollback, compteurs et erreurs de persistance. +- `kb_pipeline` : sélection, dispatch, concurrence, annulation, backfill et agrégation. +- décodeur : reconnaissance, validation du format, décision et diagnostic borné. +- matérialiseur : applicabilité exacte, politique de transaction et sorties produites. +- exécuteur : support, construction, simulation et garde-fous. +- application : invocation Tauri, fenêtre, progression utilisateur et résumé final. + +## Données interdites + +Ne jamais journaliser : + +- clé privée, seed phrase ou transaction à signer ; +- DSN ou URL contenant un secret non masqué ; +- payload brut complet ou réponse RPC volumineuse ; +- donnée dynamique non bornée. + +Conserver à la place la taille, un préfixe borné et un SHA-256 lorsque l’audit du payload est nécessaire. + +## Évolution + +L’ajout ou la suppression de `tracing.workspace = true` impose dans le même delta : + +1. la constante canonique ou sa suppression ; +2. les événements réels de la crate ; +3. la mise à jour de `config/example.config.json` ; +4. la mise à jour des tests de contrat ; +5. la mise à jour de la liste ci-dessus. diff --git a/migration/khadhroony-bot2-reference/docs/TRANSACTION_ACQUISITION_MODEL.md b/migration/khadhroony-bot2-reference/docs/TRANSACTION_ACQUISITION_MODEL.md new file mode 100644 index 0000000..e2776bd --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/TRANSACTION_ACQUISITION_MODEL.md @@ -0,0 +1,137 @@ + + + +# Modèle d’acquisition des transactions Solana + +## Décision + +Le pipeline ne conserve pas un payload complet différent pour chaque fournisseur. Toutes les sources doivent produire une représentation canonique commune avant l’écriture principale. + +```text +getTransaction JSON-RPC +Helius transactionSubscribe JSON +Yellowstone gRPC Protobuf +logsSubscribe + getTransaction +backfill ou réparation + | + v +adaptateur de source dans kb_rpc + | + v +transaction Solana canonique + | + +--> kb_sol_raw_transactions + | + +--> kb_sol_obs_transaction_observations +``` + +## Transaction canonique + +`kb_sol_raw_transactions` contient une seule ligne par signature. Le contenu est indépendant du fournisseur et ne doit contenir aucun champ propre à Helius, Triton, Chainstack, Shyft ou un endpoint particulier. + +Le document canonique doit inclure, quand la source les fournit : + +- signature ; +- slot ; +- version de transaction ; +- message header ; +- static account keys ; +- Address Lookup Tables ; +- loaded writable et readonly addresses ; +- recent blockhash ; +- outer instructions ; +- inner instructions ; +- logs ; +- statut et erreur ; +- fee et compute units ; +- balances SOL avant/après ; +- balances SPL avant/après ; +- rewards ; +- return data ; +- block time. + +Les public keys et signatures restent en base58, avec validation stricte de leur taille décodée : 32 octets pour les clés et blockhashes, 64 octets pour les signatures. Les données binaires d’instruction restent en base64. Les montants SPL conservent le montant entier brut normalisé sans zéros non significatifs, les décimales et une représentation décimale fixe recalculée localement, sans dépendre de `uiAmount` ou `uiAmountString` du fournisseur. + +Depuis `0.3.2`, ce contrat est matérialisé par `kb_model::CanonicalTransaction` avec `canonical_format_version = 1`. La sérialisation trie récursivement les clés des objets JSON avant calcul d’un hash SHA-256 sur le document compact. Les tableaux conservent l’ordre Solana d’origine. Les détails de source, timestamps locaux et identifiants de subscription sont exclus du document hashé. + +L’adaptateur HTTP standard demande `encoding = json`, puis convertit explicitement les payloads d’instruction reçus en base58 vers la représentation base64 canonique. Les champs optionnels absents et `null` sont normalisés vers le même état interne afin de stabiliser le hash entre réponses compatibles. + +## Observations de source + +`kb_sol_obs_transaction_observations` décrit comment et quand une transaction a été détectée ou reçue. Elle ne duplique pas la transaction complète. + +Champs minimaux visés : + +| Champ | Rôle | +|-----------------------|----------------------------------------------------------------------------------------------| +| `observation_key` | clé idempotente de l’observation | +| `raw_transaction_id` | lien optionnel vers la transaction canonique | +| `signature` | signature observée | +| `slot` | slot observé quand connu | +| `provider` | fournisseur, par exemple `helius`, `triton`, `chainstack`, `shyft` | +| `endpoint_code` | endpoint de configuration utilisé | +| `protocol` | `solana_http_json_rpc`, `solana_ws_json_rpc`, `helius_ws` ou `yellowstone_grpc` | +| `acquisition_method` | `getTransaction`, `transactionSubscribe`, `yellowstone_transactions`, `logs_hydration`, etc. | +| `origin` | `live`, `backfill`, `replay`, `repair` ou `migration` | +| `commitment` | commitment demandé ou observé | +| `capture_session_id` | session ou campagne de capture | +| `filter_code` | profil de filtre utilisé | +| `detected_at` | réception du premier signal, par exemple le log | +| `received_at` | réception de la transaction complète | +| `normalized_at` | fin de normalisation canonique | +| `persisted_at` | fin d’écriture durable | +| `payload_size_bytes` | taille du message source reçu | +| `source_payload_hash` | hash optionnel du message source sans le conserver | +| `status` | `detected`, `received`, `normalized`, `persisted`, `failed` ou `missing` | +| `error_code` | code technique normalisé optionnel | +| `error_message` | message de diagnostic optionnel | + +Les observations permettent de comparer les sources par signature, sans multiplier le volume de stockage transactionnel. + +## Notifications WebSocket + +`kb_sol_raw_ws_notifications` n’est plus une cible durable. + +Une notification `logsSubscribe` peut être conservée temporairement en mémoire jusqu’à l’hydratation de la transaction. La base conserve ensuite seulement : + +- les timestamps utiles ; +- la signature et le slot ; +- la source et la méthode ; +- la taille ; +- le statut de l’hydratation ; +- le lien éventuel vers la transaction canonique. + +Les notifications non transactionnelles comme `slotSubscribe`, `accountSubscribe` ou `programSubscribe` pourront avoir des tables métier dédiées seulement si un besoin durable apparaît. Elles ne doivent pas être entassées dans une table générique de payloads WebSocket. + +## Fusion multi-source + +Pour une signature déjà présente : + +1. normaliser le nouveau message ; +2. calculer le hash canonique ; +3. ajouter l’observation de source ; +4. si le hash est identique, ne pas dupliquer la transaction ; +5. si la nouvelle source apporte uniquement des champs auparavant absents, appliquer un enrichissement déterministe ; +6. si des champs incompatibles diffèrent, ne pas écraser silencieusement et enregistrer un conflit technique. + +Les différences liées au commitment ou à la disponibilité progressive des metadata doivent être distinguées d’un conflit réel. + +## Raw provider facultatif + +Un payload fournisseur complet peut être exporté de façon bornée pour une campagne de diagnostic, par exemple en NDJSON ou Protobuf, mais il ne fait pas partie du stockage PostgreSQL transactionnel normal. + +Ces exports doivent être : + +- explicitement activés ; +- limités en durée et en volume ; +- associés à une session de capture ; +- supprimables sans affecter les replays métier. + +## Frontières des crates + +- `kb_rpc` contient les transports et adaptateurs HTTP, WebSocket Helius et Yellowstone gRPC. +- `kb_model` contient le contrat canonique source-indépendant. +- `kb_store_core` contient les DTOs et repositories de transactions et observations. +- `kb_store_pg` contient la migration, les queries et les repositories PostgreSQL. +- les décodeurs ne dépendent jamais de la source d’acquisition. + diff --git a/migration/khadhroony-bot2-reference/docs/V0_4_6_VALIDATION.md b/migration/khadhroony-bot2-reference/docs/V0_4_6_VALIDATION.md new file mode 100644 index 0000000..994b85f --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/V0_4_6_VALIDATION.md @@ -0,0 +1,66 @@ + + + +# Validation finale `0.4.6` + +## Périmètre + +Le jalon couvre Token-2022 et le registre ElGamal comme programmes distincts, ainsi que la régression des décodeurs Solana Core/SPL livrés auparavant. Il ne transforme pas Anchor, Metaplex ou les futurs DEX en surfaces implicitement couvertes. + +## Résultats de tests + +| Suite | Résultat | +|------------------------------------|---------:| +| `kb_store_core` | 41/41 | +| `kb_store_pg` avec PostgreSQL réel | 46/46 | +| `kb_pipeline` | 124/124 | +| `kb_app_demo` | 118/118 | +| `cargo clippy --all-targets` | propre | + +Les contrôles SQL sur `kb_sol_decode_events` et `kb_sol_mat_events` ne révèlent aucun doublon d’identité. + +## Token-2022 et ElGamal Registry + +- 55 instructions Token-2022 Mainnet ont été décodées sans échec persistant. +- Huit opérations publiques Token-2022 ont été confirmées sur Devnet : `MintToChecked`, `TransferChecked`, `ApproveChecked`, `Revoke`, `BurnChecked`, `FreezeAccount`, `ThawAccount` et `CloseAccount`. +- Chaque parcours Devnet a été simulé, confirmé, hydraté, extrait, décodé, matérialisé puis rejoué idempotemment. +- ElGamal Registry est validé synthétiquement/offline pour le wire, le PDA, l’état, les lectures RPC, les préflights et les preuves. +- La validation publique ElGamal Registry reste indisponible sur Devnet/Mainnet au 20 juillet 2026 ; aucune preuve réseau n’est revendiquée. + +## Loader immuable et Loader v4 + +Le parser `Write` du loader immuable lit désormais la longueur vectorielle en `u32` et les données à l’offset 12. Le replay Mainnet a supprimé 68 diagnostics `loader_vector_length_mismatch`. + +Les rejets persistants restants sont tous issus de transactions Solana échouées : + +| Diagnostic | Nombre | +|----------------------------------|-------:| +| `immutable_loader_tag_truncated` | 31 | +| `loader_v4_tag_truncated` | 44 | +| `loader_accounts_invalid` | 1 | + +Quatre variantes Loader v4 inconnues sont persistées comme traitement réussi avec résultat métier `Unsupported`. Elles ne sont pas assimilées à des décodages complets. + +## Replay des signatures incomplètes + +Le mode `incomplete_signatures` sélectionne les signatures contenant au moins une instruction `pending`, `failed`, `replay_requested` ou `unsupported`. La limite est appliquée au nombre de signatures avant expansion vers les instructions compatibles. Ce mode ne nécessite pas de force replay global. + +Deux campagnes PostgreSQL réelles successives ont produit exactement : + +```text +selected=81 +started=81 +completed=81 +unmatched=0 +notStarted=0 +failedInputs=76 +decoded=1 +unsupported=4 +failed=76 +``` + +L’instruction décodée appartient à une signature incomplète contenant également une instruction valide. La répétition identique du second passage confirme la sélection stable ; les événements et matérialisations restent idempotents. + +## Conclusion + +Tous les décodeurs livrés jusqu’à `0.4.6` respectent leur contrat sur les tests synthétiques, les scénarios stateful et les données réseau observées. Les payloads malformés sont rejetés fail-closed et les variantes inconnues sont classées explicitement `Unsupported`. Cette conclusion ne prétend pas que chaque variante rare possible a été observée sur un cluster public. diff --git a/migration/khadhroony-bot2-reference/docs/VERSION_HEADER_AUDIT.md b/migration/khadhroony-bot2-reference/docs/VERSION_HEADER_AUDIT.md new file mode 100644 index 0000000..9836fea --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/VERSION_HEADER_AUDIT.md @@ -0,0 +1,30 @@ + + + +# Audit des en-têtes de version + +Ce document regroupe les commandes utiles pour vérifier les en-têtes `file:` et `version:`. + +## Lignes `version` différentes de 1 + +La commande suivante utilise deux passes pour éviter les problèmes de lookahead et d'expansion d'historique Bash : + +```bash +rg -n -P '^\s*(//|#||\*/)?\s*$' +``` + +## Fichiers concernés uniquement + +```bash +rg -n -P '^\s*(//|#||\*/)?\s*$' | cut -d: -f1 | sort -u +``` + +## Fichiers commentables sans ligne `version` + +```bash +find . -path './target' -prune -o -path './node_modules' -prune -o -path './dist' -prune -o -path './idls' -prune -o -type f \( -name '*.rs' -o -name '*.toml' -o -name '*.sql' -o -name '*.md' -o -name '*.ts' -o -name '*.js' -o -name '*.html' -o -name '*.scss' -o -name '*.sass' \) -print | while read -r f; do + if ! head -n 5 "$f" | rg -q 'version\s*:'; then + echo "$f" + fi + done +``` diff --git a/migration/khadhroony-bot2-reference/docs/WS_LISTENERS.md b/migration/khadhroony-bot2-reference/docs/WS_LISTENERS.md new file mode 100644 index 0000000..e341a77 --- /dev/null +++ b/migration/khadhroony-bot2-reference/docs/WS_LISTENERS.md @@ -0,0 +1,69 @@ + + + +# Listeners WebSocket + +## Statut + +Les listeners temps réel ne dirigent plus `0.3.x`. Ils seront repris après les décodeurs Core, Pump, Meteora, Raydium, Orca et Jupiter. + +## Méthodes déjà présentes dans `demo_ws` + +```text +slotSubscribe +slotsUpdatesSubscribe +rootSubscribe +logsSubscribe +accountSubscribe +programSubscribe +signatureSubscribe +blockSubscribe +voteSubscribe +``` + +L’audit confirme encore l’absence de : + +```text +transactionSubscribe +transactionUnsubscribe +request kind transaction_subscribe +classification logs_subscribe_all +``` + +Ces ajouts sont reportés à `0.10.x`. + +## Modèle futur + +```text +Helius transactionSubscribe +Yellowstone gRPC transactions +logsSubscribe + hydration + | + v +transaction canonique commune + | + +--> kb_sol_raw_transactions + +--> kb_sol_obs_transaction_observations +``` + +Le payload complet de la notification WebSocket ne doit pas être stocké durablement lorsqu’une transaction canonique existe. + +## Cas d’usage + +Les flux devront détecter rapidement : + +- création de token ou pool ; +- premiers swaps ; +- migration ; +- changement de liquidité ; +- activité administrative dangereuse ; +- frais et buybacks ; +- changements sur les comptes critiques. + +## Sécurité + +- Aucun ordre direct depuis la couche transport. +- Toute perte du flux principal désactive le trading. +- Les événements rejoués ou backfillés mettent à jour l’état sans déclencher rétroactivement un ordre. +- Les queues, tailles, retries et exports de diagnostic restent bornés. + diff --git a/migration/khadhroony-bot2-reference/idls/001.README.md b/migration/khadhroony-bot2-reference/idls/001.README.md new file mode 100644 index 0000000..994a228 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/001.README.md @@ -0,0 +1,8 @@ + + + +# IDL locales + +Ce répertoire contient les IDL locales utilisées comme sources de connaissance pour les décodeurs. + +Les fichiers JSON ne doivent pas être modifiés pour ajouter des commentaires, car le format JSON standard ne les supporte pas. diff --git a/migration/khadhroony-bot2-reference/idls/bonkswap.BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p.json b/migration/khadhroony-bot2-reference/idls/bonkswap.BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p.json new file mode 100644 index 0000000..80b7b11 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/bonkswap.BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p.json @@ -0,0 +1 @@ +{"version":"0.1.1","name":"bonkswap","instructions":[{"name":"createPool","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":true},{"name":"poolYAccount","isMut":true,"isSigner":true},{"name":"adminXAccount","isMut":true,"isSigner":false},{"name":"adminYAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"projectOwner","isMut":false,"isSigner":false},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"lpFee","type":{"defined":"FixedPoint"}},{"name":"buybackFee","type":{"defined":"FixedPoint"}},{"name":"projectFee","type":{"defined":"FixedPoint"}},{"name":"mercantiFee","type":{"defined":"FixedPoint"}},{"name":"initialTokenX","type":{"defined":"Token"}},{"name":"initialTokenY","type":{"defined":"Token"}},{"name":"bump","type":"u8"}]},{"name":"createProvider","accounts":[{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"ownerXAccount","isMut":true,"isSigner":false},{"name":"ownerYAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"tokenXAmount","type":{"defined":"Token"}},{"name":"tokenYAmount","type":{"defined":"Token"}},{"name":"bump","type":"u8"}]},{"name":"createState","accounts":[{"name":"state","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"addTokens","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarco","isMut":true,"isSigner":false},{"name":"tokenProjectFirst","isMut":true,"isSigner":false},{"name":"tokenProjectSecond","isMut":true,"isSigner":false},{"name":"ownerXAccount","isMut":true,"isSigner":false},{"name":"ownerYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"ownerMarcoAccount","isMut":true,"isSigner":false},{"name":"ownerProjectFirstAccount","isMut":true,"isSigner":false},{"name":"ownerProjectSecondAccount","isMut":true,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"deltaX","type":{"defined":"Token"}},{"name":"deltaY","type":{"defined":"Token"}}]},{"name":"withdrawBuyback","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"buybackXAccount","isMut":true,"isSigner":false},{"name":"buybackYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"swap","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"swapperXAccount","isMut":true,"isSigner":false},{"name":"swapperYAccount","isMut":true,"isSigner":false},{"name":"swapper","isMut":true,"isSigner":true},{"name":"referrerXAccount","isMut":true,"isSigner":false},{"name":"referrerYAccount","isMut":true,"isSigner":false},{"name":"referrer","isMut":true,"isSigner":false},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"deltaIn","type":{"defined":"Token"}},{"name":"priceLimit","type":{"defined":"FixedPoint"}},{"name":"xToY","type":"bool"}]},{"name":"withdrawShares","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarco","isMut":true,"isSigner":false},{"name":"tokenProjectFirst","isMut":true,"isSigner":false},{"name":"tokenProjectSecond","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"ownerXAccount","isMut":true,"isSigner":false},{"name":"ownerYAccount","isMut":true,"isSigner":false},{"name":"ownerMarcoAccount","isMut":true,"isSigner":false},{"name":"ownerProjectFirstAccount","isMut":true,"isSigner":false},{"name":"ownerProjectSecondAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"shares","type":{"defined":"Token"}}]},{"name":"withdrawLpFee","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"ownerXAccount","isMut":true,"isSigner":false},{"name":"ownerYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"withdrawProjectFee","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"projectOwnerXAccount","isMut":true,"isSigner":false},{"name":"projectOwnerYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"projectOwner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"createFarm","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarco","isMut":false,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":true},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"supply","type":{"defined":"Token"}},{"name":"duration","type":"u64"},{"name":"bump","type":"u8"}]},{"name":"createDualFarm","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarco","isMut":false,"isSigner":false},{"name":"tokenProjectFirst","isMut":false,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":true},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":true},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"adminProjectFirstAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"supplyMarco","type":{"defined":"Token"}},{"name":"supplyProjectFirst","type":{"defined":"Token"}},{"name":"duration","type":"u64"},{"name":"bump","type":"u8"}]},{"name":"createTripleFarm","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarco","isMut":false,"isSigner":false},{"name":"tokenProjectFirst","isMut":false,"isSigner":false},{"name":"tokenProjectSecond","isMut":false,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":true},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":true},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":true},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"adminProjectFirstAccount","isMut":true,"isSigner":false},{"name":"adminProjectSecondAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"supplyMarco","type":{"defined":"Token"}},{"name":"supplyProjectFirst","type":{"defined":"Token"}},{"name":"supplyProjectSecond","type":{"defined":"Token"}},{"name":"duration","type":"u64"},{"name":"bump","type":"u8"}]},{"name":"withdrawRewards","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarco","isMut":true,"isSigner":false},{"name":"tokenProjectFirst","isMut":true,"isSigner":false},{"name":"tokenProjectSecond","isMut":true,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"ownerMarcoAccount","isMut":true,"isSigner":false},{"name":"ownerProjectFirstAccount","isMut":true,"isSigner":false},{"name":"ownerProjectSecondAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"closePool","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"buybackXAccount","isMut":true,"isSigner":false},{"name":"buybackYAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"withdrawMercantiFee","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"mercantiXAccount","isMut":true,"isSigner":false},{"name":"mercantiYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"addSupply","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"adminProjectFirstAccount","isMut":true,"isSigner":false},{"name":"adminProjectSecondAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"supplyMarco","type":{"defined":"Token"}},{"name":"supplyProjectFirst","type":{"defined":"Token"}},{"name":"supplyProjectSecond","type":{"defined":"Token"}},{"name":"duration","type":"u64"}]},{"name":"updateFees","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false}],"args":[{"name":"newBuybackFee","type":{"defined":"FixedPoint"}},{"name":"newProjectFee","type":{"defined":"FixedPoint"}},{"name":"newProviderFee","type":{"defined":"FixedPoint"}},{"name":"newMercantiFee","type":{"defined":"FixedPoint"}}]},{"name":"resetFarm","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarco","isMut":false,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"adminProjectFirstAccount","isMut":true,"isSigner":false},{"name":"adminProjectSecondAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"updateRewardTokens","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"bonkswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"tokenMarco","isMut":false,"isSigner":false},{"name":"newTokenMarcoAccount","isMut":true,"isSigner":true},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"Farm","type":{"kind":"struct","fields":[{"name":"pool","type":"publicKey"},{"name":"tokens","type":{"array":["publicKey",3]}},{"name":"tokenAccounts","type":{"array":["publicKey",3]}},{"name":"supply","type":{"array":[{"defined":"Token"},3]}},{"name":"supplyLeft","type":{"array":[{"defined":"Token"},3]}},{"name":"accumulatedSecondsPerShare","type":{"defined":"FixedPoint"}},{"name":"offsetSecondsPerShare","type":{"defined":"FixedPoint"}},{"name":"startTime","type":"u64"},{"name":"endTime","type":"u64"},{"name":"lastUpdate","type":"u64"},{"name":"bump","type":"u8"},{"name":"farmType","type":{"defined":"FarmType"}}]}},{"name":"PoolV2","type":{"kind":"struct","fields":[{"name":"tokenX","type":"publicKey"},{"name":"tokenY","type":"publicKey"},{"name":"poolXAccount","type":"publicKey"},{"name":"poolYAccount","type":"publicKey"},{"name":"admin","type":"publicKey"},{"name":"projectOwner","type":"publicKey"},{"name":"tokenXReserve","type":{"defined":"Token"}},{"name":"tokenYReserve","type":{"defined":"Token"}},{"name":"selfShares","type":{"defined":"Token"}},{"name":"allShares","type":{"defined":"Token"}},{"name":"buybackAmountX","type":{"defined":"Token"}},{"name":"buybackAmountY","type":{"defined":"Token"}},{"name":"projectAmountX","type":{"defined":"Token"}},{"name":"projectAmountY","type":{"defined":"Token"}},{"name":"mercantiAmountX","type":{"defined":"Token"}},{"name":"mercantiAmountY","type":{"defined":"Token"}},{"name":"lpAccumulatorX","type":{"defined":"FixedPoint"}},{"name":"lpAccumulatorY","type":{"defined":"FixedPoint"}},{"name":"constK","type":{"defined":"Product"}},{"name":"price","type":{"defined":"FixedPoint"}},{"name":"lpFee","type":{"defined":"FixedPoint"}},{"name":"buybackFee","type":{"defined":"FixedPoint"}},{"name":"projectFee","type":{"defined":"FixedPoint"}},{"name":"mercantiFee","type":{"defined":"FixedPoint"}},{"name":"farmCount","type":"u64"},{"name":"poolBump","type":"u8"},{"name":"lpToken","type":"publicKey"},{"name":"lpTokenMintBump","type":"u8"},{"name":"padding","type":{"array":["u64",8]}}]}},{"name":"Pool","type":{"kind":"struct","fields":[{"name":"tokenX","type":"publicKey"},{"name":"tokenY","type":"publicKey"},{"name":"poolXAccount","type":"publicKey"},{"name":"poolYAccount","type":"publicKey"},{"name":"admin","type":"publicKey"},{"name":"projectOwner","type":"publicKey"},{"name":"tokenXReserve","type":{"defined":"Token"}},{"name":"tokenYReserve","type":{"defined":"Token"}},{"name":"selfShares","type":{"defined":"Token"}},{"name":"allShares","type":{"defined":"Token"}},{"name":"buybackAmountX","type":{"defined":"Token"}},{"name":"buybackAmountY","type":{"defined":"Token"}},{"name":"projectAmountX","type":{"defined":"Token"}},{"name":"projectAmountY","type":{"defined":"Token"}},{"name":"mercantiAmountX","type":{"defined":"Token"}},{"name":"mercantiAmountY","type":{"defined":"Token"}},{"name":"lpAccumulatorX","type":{"defined":"FixedPoint"}},{"name":"lpAccumulatorY","type":{"defined":"FixedPoint"}},{"name":"constK","type":{"defined":"Product"}},{"name":"price","type":{"defined":"FixedPoint"}},{"name":"lpFee","type":{"defined":"FixedPoint"}},{"name":"buybackFee","type":{"defined":"FixedPoint"}},{"name":"projectFee","type":{"defined":"FixedPoint"}},{"name":"mercantiFee","type":{"defined":"FixedPoint"}},{"name":"farmCount","type":"u64"},{"name":"bump","type":"u8"}]}},{"name":"Provider","type":{"kind":"struct","fields":[{"name":"tokenX","type":"publicKey"},{"name":"tokenY","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"shares","type":{"defined":"Token"}},{"name":"lastFeeAccumulatorX","type":{"defined":"FixedPoint"}},{"name":"lastFeeAccumulatorY","type":{"defined":"FixedPoint"}},{"name":"lastSecondsPerShare","type":{"defined":"FixedPoint"}},{"name":"lastWithdrawTime","type":"u64"},{"name":"tokensOwedX","type":{"defined":"Token"}},{"name":"tokensOwedY","type":{"defined":"Token"}},{"name":"currentFarmCount","type":"u64"},{"name":"bump","type":"u8"}]}},{"name":"State","type":{"kind":"struct","fields":[{"name":"admin","type":"publicKey"},{"name":"programAuthority","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"nonce","type":"u8"}]}}],"types":[{"name":"FixedPoint","type":{"kind":"struct","fields":[{"name":"v","type":"u128"}]}},{"name":"Token","type":{"kind":"struct","fields":[{"name":"v","type":"u64"}]}},{"name":"Product","type":{"kind":"struct","fields":[{"name":"v","type":"u128"}]}},{"name":"FarmType","type":{"kind":"enum","variants":[{"name":"Single"},{"name":"Dual"},{"name":"Triple"}]}}],"errors":[{"code":6000,"name":"DeltaTooBig","msg":"Delta greater than provider's tokens"},{"code":6001,"name":"TokenUnderflow","msg":"Token amount underflow"},{"code":6002,"name":"WrongRatio","msg":"Wrong tokens ratio"},{"code":6003,"name":"TooMuchShares","msg":"Too much shares provided"},{"code":6004,"name":"SwapToBig","msg":"Swap too big"},{"code":6005,"name":"FeeExceeded","msg":"Fee exceeded 100%"},{"code":6007,"name":"ScalesNotEqual","msg":"Scales have to be equal"},{"code":6008,"name":"FeeExceededDeltaOut","msg":"Fees exceeded delta_out"},{"code":6009,"name":"PriceLimitExceeded","msg":"Price limit exceeded"},{"code":6010,"name":"MintMismatch","msg":"Mint mismatch"},{"code":6011,"name":"TokensAreTheSame","msg":"Tokens are the same"},{"code":6012,"name":"WrongFarm","msg":"Cannot add supply to wrong farm"},{"code":6013,"name":"RewardsExceedingSupply","msg":"Cannot withdraw rewards exceeding supply left"},{"code":6014,"name":"FarmNotEnded","msg":"Farm has not ended, cannot add additional rewards"},{"code":6015,"name":"ZeroAmount","msg":"Must provide a nonzero amount"},{"code":6016,"name":"InvariantDecreased","msg":"Invariant has changed"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/boop_fun.boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4.json b/migration/khadhroony-bot2-reference/idls/boop_fun.boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4.json new file mode 100644 index 0000000..e130eea --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/boop_fun.boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4.json @@ -0,0 +1 @@ +{"address":"boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4","metadata":{"name":"boop","version":"0.3.0","spec":"0.1.0","description":"Boop is a token launcher with fee sharing"},"instructions":[{"name":"add_operators","discriminator":[165,199,62,214,81,54,4,150],"accounts":[{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"authority","writable":true,"signer":true,"relations":["config"]},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"operators","type":{"vec":"pubkey"}}]},{"name":"buy_token","discriminator":[138,127,14,91,38,87,115,105],"accounts":[{"name":"mint"},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"trading_fees_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,114,97,100,105,110,103,95,102,101,101,115,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_sol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"recipient_token_account"},{"name":"buyer","writable":true,"signer":true},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"wsol","address":"So11111111111111111111111111111111111111112"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"buy_amount","type":"u64"},{"name":"amount_out_min","type":"u64"}]},{"name":"cancel_authority_transfer","discriminator":[94,131,125,184,183,24,125,229],"accounts":[{"name":"authority","signer":true,"relations":["config"]},{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"close_bonding_curve_vault","discriminator":[189,71,189,239,113,66,59,189],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"operator","writable":true,"signer":true},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"mint"},{"name":"recipient_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"recipient"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"recipient","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","docs":["System program for creating associated token if needed"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[]},{"name":"collect_meteora_trading_fees","discriminator":[249,95,126,91,81,162,83,250],"accounts":[{"name":"operator","writable":true,"signer":true},{"name":"protocol_fee_recipient","relations":["config"]},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"pool_authority"},{"name":"pool"},{"name":"position","writable":true},{"name":"token_a_account","docs":["The token account to receive token a"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"protocol_fee_recipient"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_a_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_b_account","docs":["The token account to receive token b"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,114,97,100,105,110,103,95,102,101,101,115,95,118,97,117,108,116]},{"kind":"account","path":"token_a_mint"}]}},{"name":"token_a_vault","writable":true},{"name":"token_b_vault","writable":true},{"name":"token_a_mint"},{"name":"token_b_mint"},{"name":"position_nft_account","writable":true},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority"},{"name":"cp_amm_program","address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"}],"args":[]},{"name":"collect_meteora_trading_fees_v2","discriminator":[96,39,109,46,5,161,15,57],"accounts":[{"name":"operator","writable":true,"signer":true},{"name":"protocol_fee_recipient","relations":["config"]},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"pool_authority"},{"name":"pool"},{"name":"position","writable":true},{"name":"token_a_account","docs":["The token account to receive token a"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"protocol_fee_recipient"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_a_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_b_account","docs":["The token account to receive token b"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,116,95,103,114,97,100,95,116,114,97,100,105,110,103,95,102,101,101,115,95,118,97,117,108,116]},{"kind":"account","path":"token_a_mint"}]}},{"name":"token_a_vault","writable":true},{"name":"token_b_vault","writable":true},{"name":"token_a_mint"},{"name":"token_b_mint"},{"name":"position_nft_account","writable":true},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority"},{"name":"cp_amm_program","address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"collect_trading_fees","discriminator":[189,38,205,234,81,77,25,1],"accounts":[{"name":"operator","writable":true,"signer":true},{"name":"protocol_fee_recipient","relations":["config"]},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"lock_program","address":"LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE"},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[108,111,99,107,95,99,112,95,97,117,116,104,111,114,105,116,121,95,115,101,101,100]}],"program":{"kind":"account","path":"lock_program"}}},{"name":"fee_nft_account","docs":["Fee token account"]},{"name":"locked_liquidity","docs":["Store the locked the information of liquidity"],"writable":true},{"name":"cpmm_program","address":"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"},{"name":"cp_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"pool_state","writable":true},{"name":"lp_mint","docs":["lp mint","address = pool_state.lp_mint"],"writable":true},{"name":"recipient_token_0_account","docs":["The token account for receive token_0"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"protocol_fee_recipient"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"vault_0_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"recipient_token_1_account","docs":["The token account for receive token_1"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,114,97,100,105,110,103,95,102,101,101,115,95,118,97,117,108,116]},{"kind":"account","path":"token_0_vault.mint"}]}},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"locked_lp_vault","docs":["locked lp token account"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"authority"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"system_program","docs":["System program"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["Associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program","docs":["token Program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[]},{"name":"collect_trading_fees_v2","discriminator":[180,138,160,155,243,88,168,7],"accounts":[{"name":"operator","writable":true,"signer":true},{"name":"protocol_fee_recipient","relations":["config"]},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"lock_program","address":"LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE"},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[108,111,99,107,95,99,112,95,97,117,116,104,111,114,105,116,121,95,115,101,101,100]}],"program":{"kind":"account","path":"lock_program"}}},{"name":"fee_nft_account","docs":["Fee token account"]},{"name":"locked_liquidity","docs":["Store the locked the information of liquidity"],"writable":true},{"name":"cpmm_program","address":"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"},{"name":"cp_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"pool_state","writable":true},{"name":"lp_mint","docs":["lp mint","address = pool_state.lp_mint"],"writable":true},{"name":"recipient_token_0_account","docs":["The token account for receive token_0"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"protocol_fee_recipient"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"vault_0_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"recipient_token_1_account","docs":["The token account for receive token_1"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,116,95,103,114,97,100,95,116,114,97,100,105,110,103,95,102,101,101,115,95,118,97,117,108,116]},{"kind":"account","path":"token_0_vault.mint"}]}},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"locked_lp_vault","docs":["locked lp token account"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"authority"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"system_program","docs":["System program"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["Associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program","docs":["token Program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[]},{"name":"complete_authority_transfer","discriminator":[81,233,91,132,175,31,151,141],"accounts":[{"name":"pending_authority","signer":true,"relations":["config"]},{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_meteora_pool","discriminator":[246,254,33,37,225,176,41,232],"accounts":[{"name":"operator","writable":true,"signer":true},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"cp_amm_config"},{"name":"pool_authority"},{"name":"pool","writable":true},{"name":"position","writable":true},{"name":"position_nft_mint","writable":true,"signer":true},{"name":"position_nft_account","writable":true},{"name":"token_a_mint"},{"name":"token_b_mint","address":"So11111111111111111111111111111111111111112"},{"name":"token_a_vault","writable":true},{"name":"token_b_vault","writable":true},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"token_a_mint"}]}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"token_a_mint"}]}},{"name":"bonding_curve_wsol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,119,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"token_a_mint"}]}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority"},{"name":"cp_amm_program","address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"}],"args":[]},{"name":"create_raydium_pool","discriminator":[65,45,119,77,204,178,84,2],"accounts":[{"name":"cpmm_program","address":"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"pool_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"token_0_mint"},{"kind":"account","path":"token_1_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"token_0_mint","writable":true},{"name":"token_1_mint","docs":["Token_1 mint, the key must be greater than token_0 mint."],"address":"So11111111111111111111111111111111111111112"},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,108,112,95,109,105,110,116]},{"kind":"account","path":"pool_state"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"token_0_mint"}]}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"token_0_mint"}]}},{"name":"bonding_curve_wsol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,119,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"token_0_mint"}]}},{"name":"creator_lp_token","writable":true},{"name":"token_0_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_0_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"token_1_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_1_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"create_pool_fee","docs":["create pool fee account"],"writable":true,"address":"DNXgeM9EiiaAbaWvwjHj9fQQLAX5ZsfHyvmYUNRAdNC8"},{"name":"observation_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"operator","writable":true,"signer":true},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[]},{"name":"create_raydium_random_pool","discriminator":[78,44,173,29,132,180,4,172],"accounts":[{"name":"cpmm_program","address":"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"pool_state","writable":true,"signer":true},{"name":"token_0_mint","writable":true},{"name":"token_1_mint","docs":["Token_1 mint, the key must be greater than token_0 mint."],"address":"So11111111111111111111111111111111111111112"},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,108,112,95,109,105,110,116]},{"kind":"account","path":"pool_state"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"token_0_mint"}]}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"token_0_mint"}]}},{"name":"bonding_curve_wsol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,119,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"token_0_mint"}]}},{"name":"creator_lp_token","writable":true},{"name":"token_0_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_0_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"token_1_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_1_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"create_pool_fee","docs":["create pool fee account"],"writable":true,"address":"DNXgeM9EiiaAbaWvwjHj9fQQLAX5ZsfHyvmYUNRAdNC8"},{"name":"observation_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"operator","writable":true,"signer":true},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[]},{"name":"create_token","discriminator":[84,52,204,228,24,140,234,75],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"metadata","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]}}},{"name":"mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,105,110,116]},{"kind":"account","path":"payer"},{"kind":"arg","path":"salt"}]}},{"name":"payer","writable":true,"signer":true},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"}],"args":[{"name":"salt","type":"u64"},{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"create_token_fallback","discriminator":[253,184,126,199,235,232,172,162],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"metadata","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]}}},{"name":"mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,105,110,116]},{"kind":"arg","path":"salt"}]}},{"name":"payer","writable":true,"signer":true},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"}],"args":[{"name":"salt","type":"u64"},{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"deploy_bonding_curve","discriminator":[180,89,199,76,168,236,217,138],"accounts":[{"name":"mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,105,110,116]},{"kind":"account","path":"payer"},{"kind":"arg","path":"salt"}]}},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_sol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"creator","type":"pubkey"},{"name":"salt","type":"u64"}]},{"name":"deploy_bonding_curve_fallback","discriminator":[53,230,172,84,77,174,22,61],"accounts":[{"name":"mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,105,110,116]},{"kind":"arg","path":"salt"}]}},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_sol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"creator","type":"pubkey"},{"name":"salt","type":"u64"}]},{"name":"deposit_into_raydium","discriminator":[168,89,99,30,117,49,88,224],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"operator","writable":true,"signer":true},{"name":"operator_wsol_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"operator"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_1_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"pool_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"token_0_mint"},{"kind":"account","path":"token_1_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"token_0_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_0_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"token_1_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_1_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"token_0_mint"}]}},{"name":"bonding_curve_wsol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,119,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"token_0_mint"}]}},{"name":"token_program","docs":["token Program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","docs":["System program"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["Program to create an ATA for the operator to recoup any potential remaining WSOL"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,108,112,95,109,105,110,116]},{"kind":"account","path":"pool_state"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"cpmm_program","address":"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"},{"name":"owner_lp_token","writable":true},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"token_0_mint"}]}},{"name":"token_0_mint","writable":true},{"name":"token_1_mint","docs":["token_1 mint, the key must greater than token_0 mint"],"address":"So11111111111111111111111111111111111111112"}],"args":[{"name":"lp_token_amount","type":"u64"},{"name":"maximum_token_0_amount","type":"u64"},{"name":"maximum_token_1_amount","type":"u64"}]},{"name":"graduate","discriminator":[45,235,225,181,17,218,64,130],"accounts":[{"name":"mint"},{"name":"wsol","address":"So11111111111111111111111111111111111111112"},{"name":"protocol_fee_recipient","writable":true},{"name":"token_distributor"},{"name":"token_distributor_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"token_distributor"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve_sol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_wsol_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,119,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"operator","writable":true,"signer":true},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[]},{"name":"initialize","discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"authority","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"protocol_fee_recipient","type":"pubkey"},{"name":"token_distributor","type":"pubkey"}]},{"name":"initiate_authority_transfer","discriminator":[210,43,101,215,119,140,106,218],"accounts":[{"name":"authority","signer":true,"relations":["config"]},{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"new_authority","type":"pubkey"}]},{"name":"lock_raydium_liquidity","discriminator":[173,255,148,6,122,99,140,22],"accounts":[{"name":"lock_program","address":"LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE"},{"name":"vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[108,111,99,107,95,99,112,95,97,117,116,104,111,114,105,116,121,95,115,101,101,100]}],"program":{"kind":"account","path":"lock_program"}}},{"name":"fee_nft_owner"},{"name":"fee_nft_mint","writable":true,"signer":true},{"name":"fee_nft_account","writable":true},{"name":"pool_state","writable":true},{"name":"locked_liquidity","docs":["Store the locked information of liquidity"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,111,99,107,101,100,95,108,105,113,117,105,100,105,116,121]},{"kind":"account","path":"fee_nft_mint"}],"program":{"kind":"account","path":"lock_program"}}},{"name":"lp_mint","docs":["The mint of liquidity token"],"writable":true},{"name":"liquidity_owner_lp","docs":["liquidity owner lp token account"],"writable":true},{"name":"locked_lp_vault","writable":true},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"operator","writable":true,"signer":true},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"token_0_vault.mint"}]}},{"name":"metadata_account","docs":["To store metaplex metadata"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]},{"kind":"account","path":"fee_nft_mint"}],"program":{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]}}},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the new account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to create/transfer mint/token account"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving fee NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_program","docs":["Program to create NFT metadata accunt"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"}],"args":[]},{"name":"remove_operators","discriminator":[42,20,89,83,222,37,4,109],"accounts":[{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"authority","writable":true,"signer":true,"relations":["config"]},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"operators","type":{"vec":"pubkey"}}]},{"name":"sell_token","discriminator":[109,61,40,187,230,176,135,174],"accounts":[{"name":"mint"},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"trading_fees_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,114,97,100,105,110,103,95,102,101,101,115,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve_sol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"seller_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"seller"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"seller","writable":true,"signer":true},{"name":"recipient","writable":true},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"sell_amount","type":"u64"},{"name":"amount_out_min","type":"u64"}]},{"name":"split_post_graduation_trading_fees","discriminator":[241,178,177,69,38,187,58,176],"accounts":[{"name":"operator","writable":true,"signer":true},{"name":"mint"},{"name":"wsol","address":"So11111111111111111111111111111111111111112"},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"trading_fees_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,116,95,103,114,97,100,95,116,114,97,100,105,110,103,95,102,101,101,115,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"fee_splitter_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"fee_splitter_config"},{"name":"fee_splitter_creator_vault","writable":true},{"name":"fee_splitter_vault_authority","writable":true},{"name":"fee_splitter_creator_vault_authority","writable":true},{"name":"fee_splitter_staking_mint"},{"name":"fee_splitter_wsol_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_splitter_vault_authority"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"wsol"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"fee_splitter_creator_vault_authority_wsol_vault","writable":true},{"name":"fee_splitter_treasury_wsol_vault","writable":true},{"name":"fee_splitter_team_wsol_vault","writable":true},{"name":"fee_splitter_reward_pool","writable":true},{"name":"fee_splitter_reward_pool_staking_vault","writable":true},{"name":"fee_splitter_reward_pool_reward_vault","writable":true},{"name":"fee_splitter_reward_pool_program"}],"args":[]},{"name":"split_trading_fees","discriminator":[96,126,225,47,185,213,50,58],"accounts":[{"name":"operator","writable":true,"signer":true},{"name":"mint"},{"name":"wsol","address":"So11111111111111111111111111111111111111112"},{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"vault_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"trading_fees_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,114,97,100,105,110,103,95,102,101,101,115,95,118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"fee_splitter_program","address":"boopEYztaEYSnajfMtjcRysyzyRcchgKsPboRZEbnJi"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"fee_splitter_config"},{"name":"fee_splitter_creator_vault","writable":true},{"name":"fee_splitter_vault_authority","writable":true},{"name":"fee_splitter_creator_vault_authority","writable":true},{"name":"fee_splitter_staking_mint"},{"name":"fee_splitter_wsol_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_splitter_vault_authority"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"wsol"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"fee_splitter_creator_vault_authority_wsol_vault","writable":true},{"name":"fee_splitter_treasury_wsol_vault","writable":true},{"name":"fee_splitter_team_wsol_vault","writable":true},{"name":"fee_splitter_reward_pool","writable":true},{"name":"fee_splitter_reward_pool_staking_vault","writable":true},{"name":"fee_splitter_reward_pool_reward_vault","writable":true},{"name":"fee_splitter_reward_pool_program"}],"args":[]},{"name":"swap_sol_for_tokens_on_raydium","discriminator":[107,248,131,239,152,234,54,35],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"output_token_mint"}]}},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"operator","writable":true,"signer":true},{"name":"vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"pool_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"output_token_mint"},{"kind":"account","path":"input_token_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"input_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"input_token_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"output_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"output_token_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"output_token_mint"}]}},{"name":"bonding_curve_wsol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,119,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"output_token_mint"}]}},{"name":"output_token_mint","docs":["token_0 mint, the key must smaller than token_1 mint"]},{"name":"input_token_mint","docs":["token_1 mint, the key must greater than token_0 mint"],"address":"So11111111111111111111111111111111111111112"},{"name":"token_program","docs":["input_token_mint and output_token_mint have the same token program"]},{"name":"cp_swap_program","address":"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"},{"name":"observation_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}],"program":{"kind":"account","path":"cp_swap_program"}}}],"args":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"}]},{"name":"swap_tokens_for_sol_on_raydium","discriminator":[216,172,130,148,34,98,215,163],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101]},{"kind":"account","path":"input_token_mint"}]}},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"operator","writable":true,"signer":true},{"name":"vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"pool_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"input_token_mint"},{"kind":"account","path":"output_token_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"input_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"input_token_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"output_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"output_token_mint"}],"program":{"kind":"const","value":[169,42,90,139,79,41,89,82,132,37,80,170,147,253,91,149,181,172,230,168,235,146,12,147,148,46,67,105,12,32,236,115]}}},{"name":"bonding_curve_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,118,97,117,108,116]},{"kind":"account","path":"input_token_mint"}]}},{"name":"bonding_curve_wsol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,95,99,117,114,118,101,95,119,115,111,108,95,118,97,117,108,116]},{"kind":"account","path":"input_token_mint"}]}},{"name":"input_token_mint","docs":["token_0 mint, the key must smaller than token_1 mint"]},{"name":"output_token_mint","docs":["token_1 mint, the key must greater than token_0 mint"],"address":"So11111111111111111111111111111111111111112"},{"name":"token_program","docs":["input_token_mint and output_token_mint have the same token program"]},{"name":"cp_swap_program","address":"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"},{"name":"observation_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}],"program":{"kind":"account","path":"cp_swap_program"}}}],"args":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"}]},{"name":"toggle_paused","discriminator":[54,83,147,198,123,97,218,72],"accounts":[{"name":"authority","writable":true,"signer":true,"relations":["config"]},{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}}],"args":[]},{"name":"update_config","discriminator":[29,158,252,191,10,83,219,99],"accounts":[{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"authority","signer":true,"relations":["config"]},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"new_protocol_fee_recipient","type":"pubkey"},{"name":"new_virtual_sol_reserves","type":"u64"},{"name":"new_virtual_token_reserves","type":"u64"},{"name":"new_graduation_target","type":"u64"},{"name":"new_graduation_fee","type":"u64"},{"name":"new_damping_term","type":"u8"},{"name":"new_swap_fee_basis_points","type":"u8"},{"name":"new_token_for_stakers_basis_points","type":"u16"},{"name":"new_token_amount_for_raydium_liquidity","type":"u64"},{"name":"new_max_graduation_price_deviation_basis_points","type":"u16"},{"name":"new_max_swap_amount_for_pool_price_correction_basis_points","type":"u16"}]}],"accounts":[{"name":"AmmConfig","discriminator":[218,244,33,104,203,203,43,111]},{"name":"BondingCurve","discriminator":[23,183,248,55,96,216,172,96]},{"name":"Config","discriminator":[155,12,170,224,30,250,204,130]},{"name":"LockedCpLiquidityState","discriminator":[25,10,238,197,207,234,73,22]}],"events":[{"name":"AuthorityTransferCancelledEvent","discriminator":[192,121,140,224,229,96,13,143]},{"name":"AuthorityTransferCompletedEvent","discriminator":[163,132,217,128,243,92,90,249]},{"name":"AuthorityTransferInitiatedEvent","discriminator":[121,246,95,155,229,109,148,205]},{"name":"BondingCurveDeployedEvent","discriminator":[225,80,178,34,217,39,184,148]},{"name":"BondingCurveDeployedFallbackEvent","discriminator":[106,252,243,115,199,159,247,31]},{"name":"BondingCurveVaultClosedEvent","discriminator":[185,36,156,82,189,164,207,79]},{"name":"ConfigUpdatedEvent","discriminator":[245,158,129,99,60,100,214,220]},{"name":"LiquidityDepositedIntoRaydiumEvent","discriminator":[236,50,97,27,198,101,248,20]},{"name":"OperatorsAddedEvent","discriminator":[247,58,112,56,203,186,112,152]},{"name":"OperatorsRemovedEvent","discriminator":[44,72,75,70,151,42,53,89]},{"name":"PausedToggledEvent","discriminator":[143,222,228,224,6,230,64,176]},{"name":"PostGraduationTradingFeesSplitEvent","discriminator":[34,231,16,81,36,203,158,196]},{"name":"RaydiumLiquidityLockedEvent","discriminator":[172,189,8,241,137,175,59,100]},{"name":"RaydiumPoolCreatedEvent","discriminator":[170,178,21,215,84,222,34,101]},{"name":"RaydiumRandomPoolCreatedEvent","discriminator":[152,251,128,152,158,235,83,53]},{"name":"SwapSolForTokensOnRaydiumEvent","discriminator":[247,1,8,166,221,116,113,98]},{"name":"SwapTokensForSolOnRaydiumEvent","discriminator":[76,249,221,162,65,70,118,32]},{"name":"TokenBoughtEvent","discriminator":[71,89,222,124,215,192,230,138]},{"name":"TokenCreatedEvent","discriminator":[96,122,113,138,50,227,149,57]},{"name":"TokenCreatedFallbackEvent","discriminator":[157,202,35,92,165,163,39,56]},{"name":"TokenGraduatedEvent","discriminator":[73,116,111,26,92,217,146,141]},{"name":"TokenSoldEvent","discriminator":[204,239,182,77,241,51,77,66]},{"name":"TradingFeesCollectedEvent","discriminator":[225,63,26,55,134,243,210,203]},{"name":"TradingFeesCollectedV2Event","discriminator":[23,246,130,250,11,49,240,179]},{"name":"TradingFeesSplitEvent","discriminator":[113,60,159,17,253,174,135,122]}],"errors":[{"code":6000,"name":"AmountInTooLow","msg":"Amount in too low"},{"code":6001,"name":"AmountOutTooLow","msg":"Amount out too low"},{"code":6002,"name":"AmountZero","msg":"Amount zero"},{"code":6003,"name":"ConfigNotInitialized","msg":"Config not initialized"},{"code":6004,"name":"CreatorIsNotProvided","msg":"Creator is not provided"},{"code":6005,"name":"DampingTermTooLow","msg":"Damping term too low"},{"code":6006,"name":"FirstBuyMustBeAtMost50PercentOfTotalSupply","msg":"First buy must be at most 50% of the total supply"},{"code":6007,"name":"GraduationFeeRelativeToTargetIsTooHigh","msg":"Graduation fee relative to graduation target is too high"},{"code":6008,"name":"InsufficientTokensOut","msg":"Insufficient tokens to transfer out of the bonding curve"},{"code":6009,"name":"InvalidBondingCurveStatus","msg":"Invalid bonding curve status"},{"code":6010,"name":"InvalidDampingTerm","msg":"Invalid damping term"},{"code":6011,"name":"InvalidMint","msg":"Invalid mint"},{"code":6012,"name":"InvalidProtocolFeeRecipient","msg":"Invalid protocol fee recipient"},{"code":6013,"name":"InvalidSqrtPrice","msg":"Invalid sqrt price"},{"code":6014,"name":"MathOverflow","msg":"Math overflow"},{"code":6015,"name":"MaxBasisPointsOffGraduationPriceTooHigh","msg":"Max basis points off graduation price too high"},{"code":6016,"name":"MaxGraduationPriceDeviationBasisPointsTooHigh","msg":"Max graduation price deviation basis points too high"},{"code":6017,"name":"MaxSwapAmountForPoolPriceCorrectionBasisPointsTooHigh","msg":"Max swap amount for pool price correction basis points too high"},{"code":6018,"name":"MintIsLargerThanOrEqualToNativeMint","msg":"Mint is larger than or equal to native mint"},{"code":6019,"name":"NoAuthorityTransferInProgress","msg":"No authority transfer in progress"},{"code":6020,"name":"NothingToDeposit","msg":"LP Token amount is too low"},{"code":6021,"name":"NothingToSplit","msg":"Nothing to split"},{"code":6022,"name":"NothingToLock","msg":"Nothing to lock"},{"code":6023,"name":"OperatorAlreadyAdded","msg":"Operator already added"},{"code":6024,"name":"OperatorDoesNotExist","msg":"Operator does not exist"},{"code":6025,"name":"Paused","msg":"Paused"},{"code":6026,"name":"PoolPriceOutOfRange","msg":"Pool is already created and has a price out of range when attempting to deposit liquidity"},{"code":6027,"name":"SwapFeeBasisPointsTooHigh","msg":"Swap fee basis points too high"},{"code":6028,"name":"SwapAmountTooHigh","msg":"Swap amount exceeds the reasonable limit to leave as liquidity"},{"code":6029,"name":"TokenAmountForRaydiumLiquidityTooHigh","msg":"Token amount for Raydium liquidity too high"},{"code":6030,"name":"TokenForStakersBasisPointsTooHigh","msg":"Token for stakers basis points too high"},{"code":6031,"name":"TokenGraduated","msg":"Token graduated"},{"code":6032,"name":"TokenNameTooLong","msg":"Token name too long"},{"code":6033,"name":"TokenNameTooShort","msg":"Token name too short"},{"code":6034,"name":"TokenSymbolTooLong","msg":"Token symbol too long"},{"code":6035,"name":"TokenSymbolTooShort","msg":"Token symbol too short"},{"code":6036,"name":"Unauthorized","msg":"Unauthorized"}],"types":[{"name":"AmmConfig","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"disable_create_pool","docs":["Status to control if new pool can be create"],"type":"bool"},{"name":"index","docs":["Config index"],"type":"u16"},{"name":"trade_fee_rate","docs":["The trade fee, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"protocol_fee_rate","docs":["The protocol fee"],"type":"u64"},{"name":"fund_fee_rate","docs":["The fund fee, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"create_pool_fee","docs":["Fee for create a new pool"],"type":"u64"},{"name":"protocol_owner","docs":["Address of the protocol fee owner"],"type":"pubkey"},{"name":"fund_owner","docs":["Address of the fund fee owner"],"type":"pubkey"},{"name":"padding","docs":["padding"],"type":{"array":["u64",16]}}]}},{"name":"AuthorityTransferCancelledEvent","type":{"kind":"struct","fields":[]}},{"name":"AuthorityTransferCompletedEvent","type":{"kind":"struct","fields":[{"name":"old_authority","type":"pubkey"},{"name":"new_authority","type":"pubkey"}]}},{"name":"AuthorityTransferInitiatedEvent","type":{"kind":"struct","fields":[{"name":"old_authority","type":"pubkey"},{"name":"new_authority","type":"pubkey"}]}},{"name":"BondingCurve","type":{"kind":"struct","fields":[{"name":"creator","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"virtual_sol_reserves","type":"u64"},{"name":"virtual_token_reserves","docs":["virtual token reserves is deprecated, we now use the xyk formula instead","and it only requires virtual sol reserves","to maintain backwards compatibility, if damping term is 30, we use the old formula","and we still need virtual_token_reserves to be set correctly"],"type":"u64"},{"name":"graduation_target","type":"u64"},{"name":"graduation_fee","type":"u64"},{"name":"sol_reserves","type":"u64"},{"name":"token_reserves","type":"u64"},{"name":"damping_term","docs":["In reality, this is now more like a bonding_curve_selector"],"type":"u8"},{"name":"swap_fee_basis_points","type":"u8"},{"name":"token_for_stakers_basis_points","type":"u16"},{"name":"status","type":{"defined":{"name":"BondingCurveStatus"}}}]}},{"name":"BondingCurveDeployedEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"creator","type":"pubkey"}]}},{"name":"BondingCurveDeployedFallbackEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"creator","type":"pubkey"}]}},{"name":"BondingCurveStatus","type":{"kind":"enum","variants":[{"name":"Trading"},{"name":"Graduated"},{"name":"PoolPriceCorrected"},{"name":"LiquidityProvisioned"},{"name":"LiquidityLocked"}]}},{"name":"BondingCurveVaultClosedEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"recipient","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"Config","type":{"kind":"struct","fields":[{"name":"is_paused","type":"bool"},{"name":"authority","type":"pubkey"},{"name":"pending_authority","type":"pubkey"},{"name":"operators","type":{"vec":"pubkey"}},{"name":"protocol_fee_recipient","type":"pubkey"},{"name":"token_distributor","type":"pubkey"},{"name":"virtual_sol_reserves","type":"u64"},{"name":"virtual_token_reserves","type":"u64"},{"name":"graduation_target","type":"u64"},{"name":"graduation_fee","type":"u64"},{"name":"damping_term","type":"u8"},{"name":"token_for_stakers_basis_points","type":"u16"},{"name":"swap_fee_basis_points","type":"u8"},{"name":"token_amount_for_raydium_liquidity","type":"u64"},{"name":"max_graduation_price_deviation_basis_points","type":"u16"},{"name":"max_swap_amount_for_pool_price_correction_basis_points","type":"u16"}]}},{"name":"ConfigUpdatedEvent","type":{"kind":"struct","fields":[{"name":"protocol_fee_recipient","type":"pubkey"},{"name":"virtual_sol_reserves","type":"u64"},{"name":"virtual_token_reserves","type":"u64"},{"name":"graduation_target","type":"u64"},{"name":"graduation_fee","type":"u64"},{"name":"damping_term","type":"u8"},{"name":"swap_fee_basis_points","type":"u8"},{"name":"token_for_stakers_basis_points","type":"u16"},{"name":"token_amount_for_raydium_liquidity","type":"u64"},{"name":"max_graduation_price_deviation_basis_points","type":"u16"},{"name":"max_swap_amount_for_pool_price_correction_basis_points","type":"u16"}]}},{"name":"LiquidityDepositedIntoRaydiumEvent","type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"lp_token_amount","type":"u64"},{"name":"tokens_deposited","type":"u64"},{"name":"wsol_deposited","type":"u64"}]}},{"name":"LockedCpLiquidityState","type":{"kind":"struct","fields":[{"name":"locked_lp_amount","docs":["The Locked liquidity amount without claimed lp fee"],"type":"u64"},{"name":"claimed_lp_amount","docs":["Claimed lp fee amount"],"type":"u64"},{"name":"unclaimed_lp_amount","docs":["Unclaimed lp fee amount"],"type":"u64"},{"name":"last_lp","docs":["Last updated cp pool lp total supply"],"type":"u64"},{"name":"last_k","docs":["Last updated cp pool k"],"type":"u128"},{"name":"recent_epoch","docs":["Account update recent epoch"],"type":"u64"},{"name":"pool_id","docs":["The ID of the pool with which this record is connected"],"type":"pubkey"},{"name":"fee_nft_mint","docs":["nft mint to check who has authority to collect fee"],"type":"pubkey"},{"name":"locked_owner","docs":["The owner who has locked liquidity"],"type":"pubkey"},{"name":"locked_lp_mint","docs":["The mint of locked lp token"],"type":"pubkey"},{"name":"padding","docs":["Unused bytes for future upgrades."],"type":{"array":["u64",8]}}]}},{"name":"OperatorsAddedEvent","type":{"kind":"struct","fields":[{"name":"operators","type":{"vec":"pubkey"}}]}},{"name":"OperatorsRemovedEvent","type":{"kind":"struct","fields":[{"name":"operators","type":{"vec":"pubkey"}}]}},{"name":"PausedToggledEvent","type":{"kind":"struct","fields":[{"name":"is_paused","type":"bool"}]}},{"name":"PostGraduationTradingFeesSplitEvent","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"creator","type":"pubkey"}]}},{"name":"RaydiumLiquidityLockedEvent","type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"lp_amount","type":"u64"}]}},{"name":"RaydiumPoolCreatedEvent","type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"mint","type":"pubkey"}]}},{"name":"RaydiumRandomPoolCreatedEvent","type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"mint","type":"pubkey"}]}},{"name":"SwapSolForTokensOnRaydiumEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"}]}},{"name":"SwapTokensForSolOnRaydiumEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"}]}},{"name":"TokenBoughtEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"},{"name":"swap_fee","type":"u64"},{"name":"buyer","type":"pubkey"},{"name":"recipient","type":"pubkey"}]}},{"name":"TokenCreatedEvent","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]}},{"name":"TokenCreatedFallbackEvent","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]}},{"name":"TokenGraduatedEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"sol_for_liquidity","type":"u64"},{"name":"graduation_fee","type":"u64"},{"name":"token_for_distributor","type":"u64"}]}},{"name":"TokenSoldEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"},{"name":"swap_fee","type":"u64"},{"name":"seller","type":"pubkey"},{"name":"recipient","type":"pubkey"}]}},{"name":"TradingFeesCollectedEvent","type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"mint","type":"pubkey"}]}},{"name":"TradingFeesCollectedV2Event","type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"mint","type":"pubkey"}]}},{"name":"TradingFeesSplitEvent","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"creator","type":"pubkey"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/bubblegum.BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY.json b/migration/khadhroony-bot2-reference/idls/bubblegum.BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY.json new file mode 100644 index 0000000..8131dde --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/bubblegum.BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY.json @@ -0,0 +1 @@ +{"version":"0.12.0","name":"bubblegum","instructions":[{"name":"burn","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"}]},{"name":"cancelRedeem","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":true,"isSigner":true},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"voucher","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}}]},{"name":"createTree","accounts":[{"name":"treeAuthority","isMut":true,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"treeCreator","isMut":false,"isSigner":true},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxDepth","type":"u32"},{"name":"maxBufferSize","type":"u32"},{"name":"public","type":{"option":"bool"}}]},{"name":"decompressV1","accounts":[{"name":"voucher","isMut":true,"isSigner":false},{"name":"leafOwner","isMut":true,"isSigner":true},{"name":"tokenAccount","isMut":true,"isSigner":false},{"name":"mint","isMut":true,"isSigner":false},{"name":"mintAuthority","isMut":true,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":false},{"name":"masterEdition","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"sysvarRent","isMut":false,"isSigner":false},{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false}],"args":[{"name":"metadata","type":{"defined":"MetadataArgs"}}]},{"name":"delegate","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":true},{"name":"previousLeafDelegate","isMut":false,"isSigner":false},{"name":"newLeafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"}]},{"name":"mintToCollectionV1","accounts":[{"name":"treeAuthority","isMut":true,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"treeDelegate","isMut":false,"isSigner":true},{"name":"collectionAuthority","isMut":false,"isSigner":true},{"name":"collectionAuthorityRecordPda","isMut":false,"isSigner":false},{"name":"collectionMint","isMut":false,"isSigner":false},{"name":"collectionMetadata","isMut":true,"isSigner":false},{"name":"editionAccount","isMut":false,"isSigner":false},{"name":"bubblegumSigner","isMut":false,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"metadataArgs","type":{"defined":"MetadataArgs"}}]},{"name":"mintV1","accounts":[{"name":"treeAuthority","isMut":true,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"treeDelegate","isMut":false,"isSigner":true},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"message","type":{"defined":"MetadataArgs"}}]},{"name":"redeem","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":true,"isSigner":true},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"voucher","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"}]},{"name":"setAndVerifyCollection","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"treeDelegate","isMut":false,"isSigner":false},{"name":"collectionAuthority","isMut":false,"isSigner":true},{"name":"collectionAuthorityRecordPda","isMut":false,"isSigner":false},{"name":"collectionMint","isMut":false,"isSigner":false},{"name":"collectionMetadata","isMut":true,"isSigner":false},{"name":"editionAccount","isMut":false,"isSigner":false},{"name":"bubblegumSigner","isMut":false,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"message","type":{"defined":"MetadataArgs"}},{"name":"collection","type":"publicKey"}]},{"name":"setDecompressibleState","accounts":[{"name":"treeAuthority","isMut":true,"isSigner":false},{"name":"treeCreator","isMut":false,"isSigner":true}],"args":[{"name":"decompressableState","type":{"defined":"DecompressibleState"}}]},{"name":"setTreeDelegate","accounts":[{"name":"treeAuthority","isMut":true,"isSigner":false},{"name":"treeCreator","isMut":false,"isSigner":true},{"name":"newTreeDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"transfer","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"newLeafOwner","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"}]},{"name":"unverifyCollection","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"treeDelegate","isMut":false,"isSigner":false},{"name":"collectionAuthority","isMut":false,"isSigner":true},{"name":"collectionAuthorityRecordPda","isMut":false,"isSigner":false},{"name":"collectionMint","isMut":false,"isSigner":false},{"name":"collectionMetadata","isMut":true,"isSigner":false},{"name":"editionAccount","isMut":false,"isSigner":false},{"name":"bubblegumSigner","isMut":false,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"message","type":{"defined":"MetadataArgs"}}]},{"name":"unverifyCreator","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"creator","isMut":false,"isSigner":true},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"message","type":{"defined":"MetadataArgs"}}]},{"name":"verifyCollection","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"treeDelegate","isMut":false,"isSigner":false},{"name":"collectionAuthority","isMut":false,"isSigner":true},{"name":"collectionAuthorityRecordPda","isMut":false,"isSigner":false},{"name":"collectionMint","isMut":false,"isSigner":false},{"name":"collectionMetadata","isMut":true,"isSigner":false},{"name":"editionAccount","isMut":false,"isSigner":false},{"name":"bubblegumSigner","isMut":false,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"message","type":{"defined":"MetadataArgs"}}]},{"name":"verifyCreator","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"creator","isMut":false,"isSigner":true},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"message","type":{"defined":"MetadataArgs"}}]},{"name":"updateMetadata","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"collectionMint","isMut":false,"isSigner":false,"isOptional":true},{"name":"collectionMetadata","isMut":false,"isSigner":false,"isOptional":true},{"name":"collectionAuthorityRecordPda","isMut":false,"isSigner":false,"isOptional":true},{"name":"leafOwner","isMut":false,"isSigner":false},{"name":"leafDelegate","isMut":false,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"currentMetadata","type":{"defined":"MetadataArgs"}},{"name":"updateArgs","type":{"defined":"UpdateArgs"}}]}],"accounts":[{"name":"TreeConfig","type":{"kind":"struct","fields":[{"name":"treeCreator","type":"publicKey"},{"name":"treeDelegate","type":"publicKey"},{"name":"totalMintCapacity","type":"u64"},{"name":"numMinted","type":"u64"},{"name":"isPublic","type":"bool"},{"name":"isDecompressible","type":{"defined":"DecompressibleState"}}]}},{"name":"Voucher","type":{"kind":"struct","fields":[{"name":"leafSchema","type":{"defined":"LeafSchema"}},{"name":"index","type":"u32"},{"name":"merkleTree","type":"publicKey"}]}}],"types":[{"name":"Creator","type":{"kind":"struct","fields":[{"name":"address","type":"publicKey"},{"name":"verified","type":"bool"},{"name":"share","type":"u8"}]}},{"name":"Uses","type":{"kind":"struct","fields":[{"name":"useMethod","type":{"defined":"UseMethod"}},{"name":"remaining","type":"u64"},{"name":"total","type":"u64"}]}},{"name":"Collection","type":{"kind":"struct","fields":[{"name":"verified","type":"bool"},{"name":"key","type":"publicKey"}]}},{"name":"MetadataArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"sellerFeeBasisPoints","type":"u16"},{"name":"primarySaleHappened","type":"bool"},{"name":"isMutable","type":"bool"},{"name":"editionNonce","type":{"option":"u8"}},{"name":"tokenStandard","type":{"option":{"defined":"TokenStandard"}}},{"name":"collection","type":{"option":{"defined":"Collection"}}},{"name":"uses","type":{"option":{"defined":"Uses"}}},{"name":"tokenProgramVersion","type":{"defined":"TokenProgramVersion"}},{"name":"creators","type":{"vec":{"defined":"Creator"}}}]}},{"name":"UpdateArgs","type":{"kind":"struct","fields":[{"name":"name","type":{"option":"string"}},{"name":"symbol","type":{"option":"string"}},{"name":"uri","type":{"option":"string"}},{"name":"creators","type":{"option":{"vec":{"defined":"Creator"}}}},{"name":"sellerFeeBasisPoints","type":{"option":"u16"}},{"name":"primarySaleHappened","type":{"option":"bool"}},{"name":"isMutable","type":{"option":"bool"}}]}},{"name":"Version","type":{"kind":"enum","variants":[{"name":"V1"}]}},{"name":"LeafSchema","type":{"kind":"enum","variants":[{"name":"V1","fields":[{"name":"id","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"delegate","type":"publicKey"},{"name":"nonce","type":"u64"},{"name":"data_hash","type":{"array":["u8",32]}},{"name":"creator_hash","type":{"array":["u8",32]}}]}]}},{"name":"TokenProgramVersion","type":{"kind":"enum","variants":[{"name":"Original"},{"name":"Token2022"}]}},{"name":"TokenStandard","type":{"kind":"enum","variants":[{"name":"NonFungible"},{"name":"FungibleAsset"},{"name":"Fungible"},{"name":"NonFungibleEdition"}]}},{"name":"UseMethod","type":{"kind":"enum","variants":[{"name":"Burn"},{"name":"Multiple"},{"name":"Single"}]}},{"name":"BubblegumEventType","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"LeafSchemaEvent"}]}},{"name":"DecompressibleState","type":{"kind":"enum","variants":[{"name":"Enabled"},{"name":"Disabled"}]}}],"errors":[{"code":6000,"name":"AssetOwnerMismatch","msg":""},{"code":6001,"name":"PublicKeyMismatch","msg":""},{"code":6002,"name":"HashingMismatch","msg":""},{"code":6003,"name":"UnsupportedSchemaVersion","msg":""},{"code":6004,"name":"CreatorShareTotalMustBe100","msg":""},{"code":6005,"name":"DuplicateCreatorAddress","msg":""},{"code":6006,"name":"CreatorDidNotVerify","msg":"Creator did not verify the metadata"},{"code":6007,"name":"CreatorNotFound","msg":""},{"code":6008,"name":"NoCreatorsPresent","msg":"No creators in creator Vec"},{"code":6009,"name":"CreatorHashMismatch","msg":"Provided creator Vec must result in provided creator hash"},{"code":6010,"name":"DataHashMismatch","msg":"Provided metadata must result in provided data hash"},{"code":6011,"name":"CreatorsTooLong","msg":"Creators list too long"},{"code":6012,"name":"MetadataNameTooLong","msg":""},{"code":6013,"name":"MetadataSymbolTooLong","msg":""},{"code":6014,"name":"MetadataUriTooLong","msg":""},{"code":6015,"name":"MetadataBasisPointsTooHigh","msg":"Basis points cannot exceed 10000"},{"code":6016,"name":"TreeAuthorityIncorrect","msg":"Tree creator or tree delegate must sign."},{"code":6017,"name":"InsufficientMintCapacity","msg":"Not enough unapproved mints left"},{"code":6018,"name":"NumericalOverflowError","msg":""},{"code":6019,"name":"IncorrectOwner","msg":"r"},{"code":6020,"name":"CollectionCannotBeVerifiedInThisInstruction","msg":""},{"code":6021,"name":"CollectionNotFound","msg":"Collection Not Found on Metadata"},{"code":6022,"name":"AlreadyVerified","msg":""},{"code":6023,"name":"AlreadyUnverified","msg":""},{"code":6024,"name":"UpdateAuthorityIncorrect","msg":"Incorrect leaf metadata update authority."},{"code":6025,"name":"LeafAuthorityMustSign","msg":"Tx must be signed by leaf owner or leaf delegate"},{"code":6026,"name":"CollectionMustBeSized","msg":""},{"code":6027,"name":"MetadataMintMismatch","msg":"Metadata mint does not match collection mint"},{"code":6028,"name":"InvalidCollectionAuthority","msg":""},{"code":6029,"name":"InvalidDelegateRecord","msg":""},{"code":6030,"name":"CollectionMasterEditionAccountInvalid","msg":"Edition account doesnt match collection"},{"code":6031,"name":"CollectionMustBeAUniqueMasterEdition","msg":"Collection Must Be a Unique Master Edition v2"},{"code":6032,"name":"UnknownExternalError","msg":"Could not convert to BubblegumError"},{"code":6033,"name":"DecompressionDisabled","msg":"Decompression disabled for this tree."},{"code":6034,"name":"MissingCollectionMintAccount","msg":"Missing collection mint account"},{"code":6035,"name":"MissingCollectionMetadataAccount","msg":"Missing collection metadata account"},{"code":6036,"name":"CollectionMismatch","msg":"Collection mismatch"},{"code":6037,"name":"MetadataImmutable","msg":"Metadata not mutable"},{"code":6038,"name":"PrimarySaleCanOnlyBeFlippedToTrue","msg":"Can only update primary sale to true"},{"code":6039,"name":"CreatorDidNotUnverify","msg":"Creator did not unverify the metadata"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/byreal_clmm.REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2.json b/migration/khadhroony-bot2-reference/idls/byreal_clmm.REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2.json new file mode 100644 index 0000000..777071d --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/byreal_clmm.REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2.json @@ -0,0 +1 @@ +{"address":"REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2","metadata":{"name":"byreal_clmm","version":"0.1.0","spec":"0.1.0","description":"Anchor client and source for Byreal concentrated liquidity AMM"},"instructions":[{"name":"claim_offchain_reward","docs":["claim offchain reward from the pool"],"discriminator":[195,87,221,149,141,195,146,19],"accounts":[{"name":"claimer","docs":["the address who claim the offchain reward."],"writable":true,"signer":true},{"name":"authority","docs":["The authority make decision that who can claim the offchain reward."],"signer":true},{"name":"admin_group","docs":["Initialize amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_id","docs":["the pool id, which is the pool state account."],"relations":["reward_config"]},{"name":"token_mint"},{"name":"claimer_token_account","docs":[""],"writable":true},{"name":"reward_vault_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"reward_config"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"reward_config","docs":["The offchain reward config account, it also is the reward vault account."],"pda":{"seeds":[{"kind":"const","value":[111,102,102,99,104,97,105,110,95,114,101,119,97,114,100]},{"kind":"account","path":"pool_id"}]}},{"name":"token_program","docs":["Spl token program or token program 2022"]},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"amount","type":"u64"}]},{"name":"close_position","docs":["Close the user's position and NFT account. If the NFT mint belongs to token2022, it will also be closed and the funds returned to the NFT owner.","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[123,134,81,0,49,68,98,98],"accounts":[{"name":"nft_owner","docs":["The position nft owner"],"writable":true,"signer":true},{"name":"position_nft_mint","docs":["Mint address bound to the personal position."],"writable":true},{"name":"position_nft_account","docs":["User token account where position NFT be minted to"],"writable":true},{"name":"personal_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"system_program","docs":["System program to close the position state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Token/Token2022 program to close token/mint account"]}],"args":[]},{"name":"close_protocol_position","docs":["close the protocol position, send the remaining fund fee to admin fee keeper","this instruction will be delete after all protocol position is closed"],"discriminator":[201,117,152,144,85,85,108,178],"accounts":[{"name":"admin_group","docs":["amm admin group account to store admin permissions.","anyone can collect fee, but only fee-manager in admin group can receive fee"],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"fee_keeper","writable":true},{"name":"protocol_position","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"collect_fund_fee","docs":["Collect the fund fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[167,138,78,149,223,194,6,126],"accounts":[{"name":"admin_group","docs":["amm admin group account to store admin permissions.","anyone can collect fee, but only fee-manager in admin group can receive fee"],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_protocol_fee","docs":["Collect the protocol fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[136,136,252,221,194,66,126,89],"accounts":[{"name":"admin_group","docs":["amm admin group account to store admin permissions.","anyone can collect fee, but only fee-manager in admin group can receive fee"],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_remaining_rewards","docs":["Collect remaining reward token for reward founder","","# Arguments","","* `ctx`- The context of accounts","* `reward_index` - the index to reward info",""],"discriminator":[18,237,166,197,34,16,213,144],"accounts":[{"name":"reward_funder","docs":["The founder who init reward info previously"],"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"funder_token_account","docs":["The funder's reward token account"],"writable":true},{"name":"pool_state","docs":["Set reward for this pool"],"writable":true},{"name":"reward_token_vault","docs":["Reward vault transfer remaining token to founder token account"]},{"name":"reward_vault_mint","docs":["The mint of reward token vault"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"create_amm_config","docs":["# Arguments","","* `ctx`- The accounts needed by instruction.","* `index` - The index of amm config, there may be multiple config.","* `tick_spacing` - The tickspacing binding with config, cannot be changed.","* `trade_fee_rate` - Trade fee rate, can be changed.","* `protocol_fee_rate` - The rate of protocol fee within trade fee.","* `fund_fee_rate` - The rate of fund fee within trade fee.",""],"discriminator":[137,52,237,212,215,117,108,104],"accounts":[{"name":"owner","docs":["Address to be set as normal manager in admin group."],"writable":true,"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"amm_config","docs":["Initialize config state account to store protocol owner address and fee rates."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,109,109,95,99,111,110,102,105,103]},{"kind":"arg","path":"index"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"u16"},{"name":"tick_spacing","type":"u16"},{"name":"trade_fee_rate","type":"u32"},{"name":"protocol_fee_rate","type":"u32"},{"name":"fund_fee_rate","type":"u32"}]},{"name":"create_operation_account","docs":["Creates an operation account for the program","","# Arguments","","* `ctx`- The context of accounts",""],"discriminator":[63,87,148,33,109,35,8,104],"accounts":[{"name":"owner","docs":["Address to be set as operation account owner."],"writable":true,"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"operation_state","docs":["Initialize operation state account to store operation owner address and white list mint."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_pool","docs":["Creates a pool for the given token pair and the initial price","","# Arguments","","* `ctx`- The context of accounts","* `sqrt_price_x64` - the initial sqrt price (amount_token_1 / amount_token_0) of the pool as a Q64.64","Note: The open_time must be smaller than the current block_timestamp on chain."],"discriminator":[233,146,209,142,207,104,64,188],"accounts":[{"name":"pool_creator","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"pool_manager","docs":["with pool_manager permission, the pool creator can create a pool."],"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"pool_state","docs":["Initialize an account to store the pool state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"token_mint_0"},{"kind":"account","path":"token_mint_1"}]}},{"name":"offchain_reward_config","docs":["Initialize an account to store the off-chain reward config"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,102,102,99,104,97,105,110,95,114,101,119,97,114,100]},{"kind":"account","path":"pool_state"}]}},{"name":"token_mint_0","docs":["Token_0 mint, the key must be smaller then token_1 mint."]},{"name":"token_mint_1","docs":["Token_1 mint"]},{"name":"token_vault_0","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_0"}]}},{"name":"token_vault_1","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_1"}]}},{"name":"observation_state","docs":["Initialize an account to store oracle observations"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"tick_array_bitmap","docs":["Initialize an account to store if a tick array is initialized."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,116,105,99,107,95,97,114,114,97,121,95,98,105,116,109,97,112,95,101,120,116,101,110,115,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"token_program_0","docs":["Spl token program or token program 2022"]},{"name":"token_program_1","docs":["Spl token program or token program 2022"]},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"sqrt_price_x64","type":"u128"},{"name":"open_time","type":"u64"}]},{"name":"create_pool_decay_fee","docs":["Creates a pool for the given token pair and the initial price with decay fee","# Arguments","","* `ctx`- The context of accounts","* `params` - The parameters for creating the pool with decay fee"],"discriminator":[252,154,210,191,22,217,136,252],"accounts":[{"name":"pool_creator","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"pool_manager","docs":["with pool_manager permission, the pool creator can create a pool."],"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"pool_state","docs":["Initialize an account to store the pool state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"token_mint_0"},{"kind":"account","path":"token_mint_1"}]}},{"name":"offchain_reward_config","docs":["Initialize an account to store the off-chain reward config"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,102,102,99,104,97,105,110,95,114,101,119,97,114,100]},{"kind":"account","path":"pool_state"}]}},{"name":"token_mint_0","docs":["Token_0 mint, the key must be smaller then token_1 mint."]},{"name":"token_mint_1","docs":["Token_1 mint"]},{"name":"token_vault_0","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_0"}]}},{"name":"token_vault_1","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_1"}]}},{"name":"observation_state","docs":["Initialize an account to store oracle observations"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"tick_array_bitmap","docs":["Initialize an account to store if a tick array is initialized."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,116,105,99,107,95,97,114,114,97,121,95,98,105,116,109,97,112,95,101,120,116,101,110,115,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"token_program_0","docs":["Spl token program or token program 2022"]},{"name":"token_program_1","docs":["Spl token program or token program 2022"]},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"CreatePoolDecayFeeParams"}}}]},{"name":"create_support_mint_associated","docs":["Create support token22 mint account which can create pool and send rewards with ignoring the not support extensions."],"discriminator":[17,251,65,92,136,242,14,169],"accounts":[{"name":"owner","docs":["Address to be set as protocol owner."],"writable":true,"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"token_mint","docs":["Support token mint"]},{"name":"support_mint_associated","docs":["Initialize support mint state account to store support mint address and bump."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,117,112,112,111,114,116,95,109,105,110,116]},{"kind":"account","path":"token_mint"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"decrease_liquidity","docs":["#[deprecated(note = \"Use `decrease_liquidity_v2` instead.\")]","Decreases liquidity for an existing position","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The amount by which liquidity will be decreased","* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity","* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity",""],"discriminator":[160,38,208,111,104,91,44,1],"accounts":[{"name":"nft_owner","docs":["The position owner or delegated authority"],"signer":true},{"name":"nft_account","docs":["The token account for the tokenized position"]},{"name":"personal_position","docs":["Decrease liquidity for this position"],"writable":true},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"token_vault_0","docs":["Token_0 vault"],"writable":true},{"name":"token_vault_1","docs":["Token_1 vault"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick","constraint = tick_array_lower.load()?.pool_id == pool_state.key()"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick","constraint = tick_array_upper.load()?.pool_id == pool_state.key()"],"writable":true},{"name":"recipient_token_account_0","docs":["The destination token account for receive amount_0"],"writable":true},{"name":"recipient_token_account_1","docs":["The destination token account for receive amount_1"],"writable":true},{"name":"token_program","docs":["SPL program to transfer out tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_min","type":"u64"},{"name":"amount_1_min","type":"u64"}]},{"name":"decrease_liquidity_v2","docs":["Decreases liquidity for an existing position, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The amount by which liquidity will be decreased","* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity","* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity",""],"discriminator":[58,127,188,62,79,82,196,96],"accounts":[{"name":"nft_owner","docs":["The position owner or delegated authority"],"signer":true},{"name":"nft_account","docs":["The token account for the tokenized position"]},{"name":"personal_position","docs":["Decrease liquidity for this position"],"writable":true},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"token_vault_0","docs":["Token_0 vault"],"writable":true},{"name":"token_vault_1","docs":["Token_1 vault"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick","constraint = tick_array_lower.load()?.pool_id == pool_state.key()"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick","constraint = tick_array_upper.load()?.pool_id == pool_state.key()"],"writable":true},{"name":"recipient_token_account_0","docs":["The destination token account for receive amount_0"],"writable":true},{"name":"recipient_token_account_1","docs":["The destination token account for receive amount_1"],"writable":true},{"name":"token_program","docs":["SPL program to transfer out tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_min","type":"u64"},{"name":"amount_1_min","type":"u64"}]},{"name":"deposit_offchain_reward","docs":["deposit offchain reward into the pool"],"discriminator":[97,125,48,169,92,241,44,142],"accounts":[{"name":"payer","docs":["the address paying to deposit the offchain reward."],"writable":true,"signer":true},{"name":"authority","docs":["The authority make decision that who can deposit the offchain reward."],"signer":true},{"name":"admin_group","docs":["Initialize amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_id","docs":["the pool id, which is the pool state account."],"relations":["reward_config"]},{"name":"token_mint"},{"name":"payer_token_account","docs":[""],"writable":true},{"name":"reward_vault_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"reward_config"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"reward_config","docs":["The offchain reward config account, it also is the reward vault account."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,102,102,99,104,97,105,110,95,114,101,119,97,114,100]},{"kind":"account","path":"pool_id"}]}},{"name":"token_program","docs":["Spl token program or token program 2022"]},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"}]},{"name":"increase_liquidity","docs":["#[deprecated(note = \"Use `increase_liquidity_v2` instead.\")]","Increases liquidity for an existing position, with amount paid by `payer`","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The desired liquidity to be added, can't be zero","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check",""],"discriminator":[46,156,243,118,13,205,251,178],"accounts":[{"name":"nft_owner","docs":["Pays to mint the position"],"signer":true},{"name":"nft_account","docs":["The token account for nft"]},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"personal_position","docs":["Increase liquidity for this position"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick","constraint = tick_array_lower.load()?.pool_id == pool_state.key()"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick","constraint = tick_array_upper.load()?.pool_id == pool_state.key()"],"writable":true},{"name":"token_account_0","docs":["The payer's token account for token_0"],"writable":true},{"name":"token_account_1","docs":["The token account spending token_1 to mint the position"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"}]},{"name":"increase_liquidity_v2","docs":["Increases liquidity for an existing position, with amount paid by `payer`, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The desired liquidity to be added, if zero, calculate liquidity base amount_0 or amount_1 according base_flag","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `base_flag` - Now, we don't use base_flag, it can be None;","We directly calculate liquidity from amount_0_max or amount_1_max according to the current price in pool.",""],"discriminator":[133,29,89,223,69,238,176,10],"accounts":[{"name":"nft_owner","docs":["Pays to mint the position"],"signer":true},{"name":"nft_account","docs":["The token account for nft"]},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"personal_position","docs":["Increase liquidity for this position"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick","constraint = tick_array_lower.load()?.pool_id == pool_state.key()"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick","constraint = tick_array_upper.load()?.pool_id == pool_state.key()"],"writable":true},{"name":"token_account_0","docs":["The payer's token account for token_0"],"writable":true},{"name":"token_account_1","docs":["The token account spending token_1 to mint the position"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"init_amm_admin_group","docs":["Initialize the AMM admin group account, which is used to manage the AMM protocol."],"discriminator":[209,108,32,246,157,214,237,86],"accounts":[{"name":"payer","docs":["only super admin can create admin group"],"writable":true,"signer":true,"address":"AY196f8U5EvM999PVnvLmyvaUnzL4GLiFaGKUgnJXN6o"},{"name":"admin_group","docs":["Initialize amm admin group account to store admin permissions."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"InitAdminGroupParams"}}}]},{"name":"initialize_reward","docs":["Initialize a reward info for a given pool and reward index","","# Arguments","","* `ctx`- The context of accounts","* `reward_index` - the index to reward info","* `open_time` - reward open timestamp","* `end_time` - reward end timestamp","* `emissions_per_second_x64` - Token reward per second are earned per unit of liquidity.",""],"discriminator":[95,135,192,196,242,129,230,68],"accounts":[{"name":"reward_funder","docs":["The founder deposit reward token to vault"],"writable":true,"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"funder_token_account","writable":true},{"name":"amm_config","docs":["For check the reward_funder authority"]},{"name":"pool_state","docs":["Set reward for this pool"],"writable":true},{"name":"operation_state","docs":["load info from the account to judge reward permission"],"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"reward_token_mint","docs":["Reward mint"]},{"name":"reward_token_vault","docs":["A pda, reward vault"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,114,101,119,97,114,100,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"reward_token_mint"}]}},{"name":"reward_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"param","type":{"defined":{"name":"InitializeRewardParam"}}}]},{"name":"open_position","docs":["#[deprecated(note = \"Use `open_position_with_token22_nft` instead.\")]","Creates a new position wrapped in a NFT","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which include tick low","* `tick_array_upper_start_index` - The start index of tick array which include tick upper","* `liquidity` - The liquidity to be added","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check",""],"discriminator":[135,128,47,77,15,152,240,49],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["Token account where position NFT will be minted","This account created in the contract by cpi to avoid large stack variables"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"position_nft_owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_nft_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","docs":["To store metaplex metadata"],"writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position"},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_program","docs":["Program to create NFT metadata"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"}]},{"name":"open_position_v2","docs":["#[deprecated(note = \"Use `open_position_with_token22_nft` instead.\")]","Creates a new position wrapped in a NFT, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which include tick low","* `tick_array_upper_start_index` - The start index of tick array which include tick upper","* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `with_metadata` - The flag indicating whether to create NFT mint metadata","* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[77,184,74,214,112,86,241,199],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["Token account where position NFT will be minted"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"position_nft_owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_nft_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","docs":["To store metaplex metadata"],"writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position"},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_program","docs":["Program to create NFT metadata"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"token_program_2022","docs":["Program to create mint account and mint tokens"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"with_metadata","type":"bool"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"open_position_with_token22_nft","docs":["Creates a new position wrapped in a Token2022 NFT without relying on metadata_program and metadata_account, reduce the cost for user to create a personal position.","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which include tick low","* `tick_array_upper_start_index` - The start index of tick array which include tick upper","* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `with_metadata` - The flag indicating whether to create NFT mint metadata","* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[77,255,174,82,125,29,201,46],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address, initialize in contract"],"writable":true,"signer":true},{"name":"position_nft_account","writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position"},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to transfer for token account"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program_2022","docs":["Program to create NFT mint/token account and transfer for token22 account"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"with_metadata","type":"bool"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"set_pool_quote_flag","docs":["设置 pool 的 quote token 标识"],"discriminator":[250,38,116,122,249,128,108,105],"accounts":[{"name":"authority","docs":["必须是 pool_manager 权限"],"signer":true},{"name":"admin_group","pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_state","writable":true}],"args":[{"name":"token1_as_quote","type":"bool"}]},{"name":"set_pool_trade_fee_rate","docs":["设置 pool 专属的 trade_fee_rate"],"discriminator":[109,117,170,34,87,202,50,20],"accounts":[{"name":"authority","docs":["必须是 pool_manager 权限(与 create_pool 一致)"],"signer":true},{"name":"admin_group","pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_state","writable":true}],"args":[{"name":"trade_fee_rate","type":"u32"}]},{"name":"set_reward_params","docs":["Reset reward param, start a new reward cycle or extend the current cycle.","","# Arguments","","* `ctx` - The context of accounts","* `reward_index` - The index of reward token in the pool.","* `emissions_per_second_x64` - The per second emission reward, when extend the current cycle,","new value can't be less than old value","* `open_time` - reward open timestamp, must be set when starting a new cycle","* `end_time` - reward end timestamp",""],"discriminator":[112,52,167,75,32,201,211,137],"accounts":[{"name":"authority","docs":["Address to be set as protocol owner. It pays to create factory state account."],"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"amm_config"},{"name":"pool_state","writable":true},{"name":"operation_state","docs":["load info from the account to judge reward permission"],"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"token_program","docs":["Token program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"reward_index","type":"u8"},{"name":"emissions_per_second_x64","type":"u128"},{"name":"open_time","type":"u64"},{"name":"end_time","type":"u64"}]},{"name":"set_swap_dynamic_fee_params","docs":["设置 swap-dynamic-fee 参数"],"discriminator":[169,23,133,74,115,251,233,52],"accounts":[{"name":"authority","docs":["必须是 pool_manager 权限 (与 create_pool 一致)"],"signer":true},{"name":"admin_group","pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_state","writable":true}],"args":[{"name":"params","type":{"defined":{"name":"SetSwapDynamicFeeParamsInput"}}}]},{"name":"swap","docs":["#[deprecated(note = \"Use `swap_v2` instead.\")]","Swaps one token for as much as possible of another token across a single pool","","# Arguments","","* `ctx` - The context of accounts","* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)","* `other_amount_threshold` - For slippage check","* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot","* `is_base_input` - swap base input or swap base output",""],"discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"tick_array","writable":true}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit_x64","type":"u128"},{"name":"is_base_input","type":"bool"}]},{"name":"swap_v2","docs":["Swaps one token for as much as possible of another token across a single pool, support token program 2022","","# Arguments","","* `ctx` - The context of accounts","* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)","* `other_amount_threshold` - For slippage check","* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot","* `is_base_input` - swap base input or swap base output",""],"discriminator":[43,4,237,11,26,201,30,98],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["SPL program 2022 for token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["Memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"input_vault_mint","docs":["The mint of token vault 0"]},{"name":"output_vault_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit_x64","type":"u128"},{"name":"is_base_input","type":"bool"}]},{"name":"swap_v3_dyn","docs":["swap_v3_dyn: 支持动态费率的 swap 指令","如果pool不支持 swap-dynamic-fee,则行为与 swap_v2 相同"],"discriminator":[229,46,213,132,105,40,40,228],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["SPL program 2022 for token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["Memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"input_vault_mint","docs":["The mint of token vault 0"]},{"name":"output_vault_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit_x64","type":"u128"},{"name":"is_base_input","type":"bool"}]},{"name":"transfer_reward_owner","docs":["Transfer reward owner","","# Arguments","","* `ctx`- The context of accounts","* `new_owner`- new owner pubkey",""],"discriminator":[7,22,12,83,242,43,48,121],"accounts":[{"name":"authority","docs":["Address to be set as operation account owner."],"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_state","writable":true}],"args":[{"name":"new_owner","type":"pubkey"}]},{"name":"update_amm_admin_group","docs":["Update the AMM admin group account, which is used to manage the AMM protocol."],"discriminator":[61,183,185,188,82,81,141,197],"accounts":[{"name":"payer","docs":["only super admin can create admin group"],"writable":true,"signer":true,"address":"AY196f8U5EvM999PVnvLmyvaUnzL4GLiFaGKUgnJXN6o"},{"name":"admin_group","docs":["update amm admin group account to store admin permissions."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}}],"args":[{"name":"params","type":{"defined":{"name":"UpdateAdminGroupParams"}}}]},{"name":"update_amm_config","docs":["Updates the owner of the amm config","Must be called by the current owner or admin","","# Arguments","","* `ctx`- The context of accounts","* `trade_fee_rate`- The new trade fee rate of amm config, be set when `param` is 0","* `protocol_fee_rate`- The new protocol fee rate of amm config, be set when `param` is 1","* `fund_fee_rate`- The new fund fee rate of amm config, be set when `param` is 2","* `new_owner`- The config's new owner, be set when `param` is 3","* `new_fund_owner`- The config's new fund owner, be set when `param` is 4","* `param`- The value can be 0 | 1 | 2 | 3 | 4, otherwise will report a error",""],"discriminator":[49,60,174,136,154,28,116,200],"accounts":[{"name":"owner","docs":["The amm config owner or admin"],"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"amm_config","docs":["Amm config account to be changed"],"writable":true}],"args":[{"name":"param","type":"u8"},{"name":"value","type":"u32"}]},{"name":"update_operation_account","docs":["Update the operation account","","# Arguments","","* `ctx`- The context of accounts","* `param`- The value can be 0 | 1 | 2 | 3, otherwise will report a error","* `keys`- update operation owner when the `param` is 0","remove operation owner when the `param` is 1","update whitelist mint when the `param` is 2","remove whitelist mint when the `param` is 3",""],"discriminator":[127,70,119,40,188,227,61,7],"accounts":[{"name":"owner","docs":["Address to be set as operation account owner."],"signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"operation_state","docs":["Initialize operation state account to store operation owner address and white list mint."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"param","type":"u8"},{"name":"keys","type":{"vec":"pubkey"}}]},{"name":"update_pool_status","docs":["Update pool status for given value","","# Arguments","","* `ctx`- The context of accounts","* `status` - The value of status",""],"discriminator":[130,87,108,6,46,224,117,123],"accounts":[{"name":"authority","signer":true},{"name":"admin_group","docs":["amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_state","writable":true}],"args":[{"name":"status","type":"u8"}]},{"name":"update_reward_infos","docs":["Update rewards info of the given pool, can be called for everyone","","# Arguments","","* `ctx`- The context of accounts",""],"discriminator":[163,172,224,52,11,154,106,223],"accounts":[{"name":"pool_state","docs":["The liquidity pool for which reward info to update"],"writable":true}],"args":[]},{"name":"withdraw_offchain_reward","docs":["withdraw offchain reward from the pool"],"discriminator":[86,53,59,76,217,38,71,213],"accounts":[{"name":"authority","docs":["The authority make decision that who can withdraw the offchain reward."],"signer":true},{"name":"admin_group","docs":["Initialize amm admin group account to store admin permissions."],"pda":{"seeds":[{"kind":"const","value":[97,100,109,105,110,95,103,114,111,117,112]}]}},{"name":"pool_id","docs":["the pool id, which is the pool state account."],"relations":["reward_config"]},{"name":"token_mint"},{"name":"receiver_token_account","docs":["the address who receive the withdrawn offchain reward."],"writable":true},{"name":"reward_vault_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"reward_config"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"reward_config","docs":["The offchain reward config account, it also is the reward vault account."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,102,102,99,104,97,105,110,95,114,101,119,97,114,100]},{"kind":"account","path":"pool_id"}]}},{"name":"token_program","docs":["Spl token program or token program 2022"]},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"amount","type":"u64"}]}],"accounts":[{"name":"AmmAdminGroup","discriminator":[128,128,234,30,61,172,188,123]},{"name":"AmmConfig","discriminator":[218,244,33,104,203,203,43,111]},{"name":"ObservationState","discriminator":[122,174,197,53,129,9,165,132]},{"name":"OffchainRewardConfig","discriminator":[118,52,115,150,99,69,164,76]},{"name":"OperationState","discriminator":[19,236,58,237,81,222,183,252]},{"name":"PersonalPositionState","discriminator":[70,111,150,126,230,15,25,117]},{"name":"PoolState","discriminator":[247,237,227,245,215,195,222,70]},{"name":"ProtocolPositionState","discriminator":[100,226,145,99,146,218,160,106]},{"name":"SupportMintAssociated","discriminator":[134,40,183,79,12,112,162,53]},{"name":"TickArrayBitmapExtension","discriminator":[60,150,36,219,97,128,139,153]}],"events":[{"name":"CollectPersonalFeeEvent","discriminator":[166,174,105,192,81,161,83,105]},{"name":"CollectProtocolFeeEvent","discriminator":[206,87,17,79,45,41,213,61]},{"name":"ConfigChangeEvent","discriminator":[247,189,7,119,106,112,95,151]},{"name":"CreatePersonalPositionEvent","discriminator":[100,30,87,249,196,223,154,206]},{"name":"DecreaseLiquidityEvent","discriminator":[58,222,86,58,68,50,85,56]},{"name":"IncreaseLiquidityEvent","discriminator":[49,79,105,212,32,34,30,84]},{"name":"LiquidityCalculateEvent","discriminator":[237,112,148,230,57,84,180,162]},{"name":"LiquidityChangeEvent","discriminator":[126,240,175,206,158,88,153,107]},{"name":"ModifyAmmAdminGroupEvent","discriminator":[218,118,87,81,53,80,18,235]},{"name":"PoolCreatedEvent","discriminator":[25,94,75,47,112,99,53,63]},{"name":"SwapEvent","discriminator":[64,198,205,232,38,8,113,226]},{"name":"UpdateRewardInfosEvent","discriminator":[109,127,186,78,114,65,37,236]}],"errors":[{"code":6000,"name":"LOK","msg":"LOK"},{"code":6001,"name":"NotApproved","msg":"Not approved"},{"code":6002,"name":"InvalidUpdateConfigFlag","msg":"invalid update amm config flag"},{"code":6003,"name":"AccountLack","msg":"Account lack"},{"code":6004,"name":"ClosePositionErr","msg":"Remove liquitity, collect fees owed and reward then you can close position account"},{"code":6005,"name":"ZeroMintAmount","msg":"Minting amount should be greater than 0"},{"code":6006,"name":"InvalidTickIndex","msg":"Tick out of range"},{"code":6007,"name":"TickInvalidOrder","msg":"The lower tick must be below the upper tick"},{"code":6008,"name":"TickLowerOverflow","msg":"The tick must be greater, or equal to the minimum tick(-443636)"},{"code":6009,"name":"TickUpperOverflow","msg":"The tick must be lesser than, or equal to the maximum tick(443636)"},{"code":6010,"name":"TickAndSpacingNotMatch","msg":"tick % tick_spacing must be zero"},{"code":6011,"name":"InvalidTickArray","msg":"Invalid tick array account"},{"code":6012,"name":"InvalidTickArrayBoundary","msg":"Invalid tick array boundary"},{"code":6013,"name":"SqrtPriceLimitOverflow","msg":"Square root price limit overflow"},{"code":6014,"name":"SqrtPriceX64","msg":"sqrt_price_x64 out of range"},{"code":6015,"name":"LiquiditySubValueErr","msg":"Liquidity sub delta L must be smaller than before"},{"code":6016,"name":"LiquidityAddValueErr","msg":"Liquidity add delta L must be greater, or equal to before"},{"code":6017,"name":"InvalidLiquidity","msg":"Invalid liquidity when update position"},{"code":6018,"name":"ForbidBothZeroForSupplyLiquidity","msg":"Both token amount must not be zero while supply liquidity"},{"code":6019,"name":"LiquidityInsufficient","msg":"Liquidity insufficient"},{"code":6020,"name":"TransactionTooOld","msg":"Transaction too old"},{"code":6021,"name":"PriceSlippageCheck","msg":"Price slippage check"},{"code":6022,"name":"TooLittleOutputReceived","msg":"Too little output received"},{"code":6023,"name":"TooMuchInputPaid","msg":"Too much input paid"},{"code":6024,"name":"ZeroAmountSpecified","msg":"Swap special amount can not be zero"},{"code":6025,"name":"InvalidInputPoolVault","msg":"Input pool vault is invalid"},{"code":6026,"name":"TooSmallInputOrOutputAmount","msg":"Swap input or output amount is too small"},{"code":6027,"name":"NotEnoughTickArrayAccount","msg":"Not enought tick array account"},{"code":6028,"name":"InvalidFirstTickArrayAccount","msg":"Invalid first tick array account"},{"code":6029,"name":"InvalidRewardIndex","msg":"Invalid reward index"},{"code":6030,"name":"FullRewardInfo","msg":"The init reward token reach to the max"},{"code":6031,"name":"RewardTokenAlreadyInUse","msg":"The init reward token already in use"},{"code":6032,"name":"ExceptRewardMint","msg":"The reward tokens must contain one of pool vault mint except the last reward"},{"code":6033,"name":"InvalidRewardInitParam","msg":"Invalid reward init param"},{"code":6034,"name":"InvalidRewardDesiredAmount","msg":"Invalid collect reward desired amount"},{"code":6035,"name":"InvalidRewardInputAccountNumber","msg":"Invalid collect reward input account number"},{"code":6036,"name":"InvalidRewardPeriod","msg":"Invalid reward period"},{"code":6037,"name":"NotApproveUpdateRewardEmissiones","msg":"Modification of emissiones is allowed within 72 hours from the end of the previous cycle"},{"code":6038,"name":"UnInitializedRewardInfo","msg":"uninitialized reward info"},{"code":6039,"name":"NotSupportMint","msg":"Not support token_2022 mint extension"},{"code":6040,"name":"MissingTickArrayBitmapExtensionAccount","msg":"Missing tickarray bitmap extension account"},{"code":6041,"name":"InsufficientLiquidityForDirection","msg":"Insufficient liquidity for this direction"},{"code":6042,"name":"MaxTokenOverflow","msg":"Max token overflow"},{"code":6043,"name":"CalculateOverflow","msg":"Calculate overflow"},{"code":6044,"name":"TransferFeeCalculateNotMatch","msg":"TransferFee calculate not match"},{"code":6045,"name":"IllegalAccountOwner","msg":"invalid account owner"},{"code":6046,"name":"InvalidAccount","msg":"Invalid account"},{"code":6047,"name":"DecayFeeNeitherOnSellMint0NorMint1","msg":"Invalid decay fee params"},{"code":6048,"name":"SwapDynamicFeeEnabled","msg":"Swap dynamic fee is enabled, must use swap_v3_dyn instruction"},{"code":6049,"name":"InvalidPythOracleAccount","msg":"Invalid pyth oracle account"},{"code":6050,"name":"PythPriceStale","msg":"Pyth price is stale"},{"code":6051,"name":"InvalidSwapDynamicFeeParams","msg":"Invalid swap dynamic fee params"}],"types":[{"name":"AmmAdminGroup","docs":["Holds the admin group information."],"type":{"kind":"struct","fields":[{"name":"fee_keeper","docs":["the address who can hold the fee,","anyone can trigger the fee collection action,"],"type":"pubkey"},{"name":"reward_config_manager","docs":["the address who can config the reward(config, deposit, withdraw),","deposit reward, set the account who can deposit the reward, withdraw the remaining reward(withdraw)"],"type":"pubkey"},{"name":"reward_claim_manager","docs":["the address who can manage the offchain reward claim"],"type":"pubkey"},{"name":"pool_manager","docs":["the address who can manage the pool create action,","without this account's permission, no one can create a pool"],"type":"pubkey"},{"name":"emergency_manager","docs":["the address who can manage the emergency action,","emergency action includes stop/resume the pool, stop/resume withdraw lp"],"type":"pubkey"},{"name":"normal_manager","docs":["normal action manager,","such as create amm config, update amm config"],"type":"pubkey"},{"name":"pad","docs":["The space required for the account. may be used for future extensions."],"type":{"array":["pubkey",6]}}]}},{"name":"AmmConfig","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"index","type":"u16"},{"name":"owner","docs":["Address of the protocol owner"],"type":"pubkey"},{"name":"protocol_fee_rate","docs":["The protocol fee"],"type":"u32"},{"name":"trade_fee_rate","docs":["The trade fee, denominated in hundredths of a bip (10^-6)"],"type":"u32"},{"name":"tick_spacing","docs":["The tick spacing"],"type":"u16"},{"name":"fund_fee_rate","docs":["The fund fee, denominated in hundredths of a bip (10^-6)"],"type":"u32"},{"name":"padding_u32","type":"u32"},{"name":"fund_owner","type":"pubkey"},{"name":"padding","type":{"array":["u64",3]}}]}},{"name":"CollectPersonalFeeEvent","docs":["Emitted when tokens are collected for a position"],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which underlying tokens were collected"],"type":"pubkey"},{"name":"recipient_token_account_0","docs":["The token account that received the collected token_0 tokens"],"type":"pubkey"},{"name":"recipient_token_account_1","docs":["The token account that received the collected token_1 tokens"],"type":"pubkey"},{"name":"amount_0","docs":["The amount of token_0 owed to the position that was collected"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_1 owed to the position that was collected"],"type":"u64"}]}},{"name":"CollectProtocolFeeEvent","docs":["Emitted when the collected protocol fees are withdrawn by the factory owner"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool whose protocol fee is collected"],"type":"pubkey"},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"type":"pubkey"},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"type":"pubkey"},{"name":"amount_0","docs":["The amount of token_0 protocol fees that is withdrawn"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_0 protocol fees that is withdrawn"],"type":"u64"}]}},{"name":"ConfigChangeEvent","docs":["Emitted when create or update a config"],"type":{"kind":"struct","fields":[{"name":"index","type":"u16"},{"name":"owner","type":"pubkey"},{"name":"protocol_fee_rate","type":"u32"},{"name":"trade_fee_rate","type":"u32"},{"name":"tick_spacing","type":"u16"},{"name":"fund_fee_rate","type":"u32"},{"name":"fund_owner","type":"pubkey"}]}},{"name":"CreatePersonalPositionEvent","docs":["Emitted when create a new position"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for which liquidity was added"],"type":"pubkey"},{"name":"minter","docs":["The address that create the position"],"type":"pubkey"},{"name":"nft_owner","docs":["The owner of the position and recipient of any minted liquidity"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity minted to the position range"],"type":"u128"},{"name":"deposit_amount_0","docs":["The amount of token_0 was deposit for the liquidity"],"type":"u64"},{"name":"deposit_amount_1","docs":["The amount of token_1 was deposit for the liquidity"],"type":"u64"},{"name":"deposit_amount_0_transfer_fee","docs":["The token transfer fee for deposit_amount_0"],"type":"u64"},{"name":"deposit_amount_1_transfer_fee","docs":["The token transfer fee for deposit_amount_1"],"type":"u64"}]}},{"name":"CreatePoolDecayFeeParams","type":{"kind":"struct","fields":[{"name":"sqrt_price_x64","docs":["The initial sqrt price of the pool, in x64 format."],"type":"u128"},{"name":"open_time","docs":["The open time of the pool, can be set to a future time; it should be None or future time.","If None, the pool will be opened immediately."],"type":{"option":"u64"}},{"name":"use_decay_fee","docs":["Whether to use decay fee for the pool.","If true, the pool will use decay fee."],"type":"bool"},{"name":"decay_fee_on_sell_mint0","docs":["Whether to use decay fee on sell for mint0 and mint1.","If true, the pool will use decay fee on sell for mint0 and mint1","these two fields are only used when use_decay_fee is true, and at least one of them must be true."],"type":"bool"},{"name":"decay_fee_on_sell_mint1","type":"bool"},{"name":"init_decay_fee_rate","docs":["The initial decay fee rate for the pool, in percentage.(1=1%)"],"type":"u8"},{"name":"decay_fee_decrease_rate","docs":["decrease rate for the decay fee, in percentage.(1=1%)"],"type":"u8"},{"name":"decay_fee_decrease_interval","docs":["The interval for decreasing the decay fee, in seconds.","how to calculate the current decay fee:","interval_count = (current_time - open_time) / decay_fee_decrease_interval","current_decay_fee = init_decay_fee_rate*((1-decay_fee_decrease_rate/100)^interval_count)"],"type":"u8"}]}},{"name":"DecreaseLiquidityEvent","docs":["Emitted when liquidity is decreased."],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which liquidity was decreased"],"type":"pubkey"},{"name":"liquidity","docs":["The amount by which liquidity for the position was decreased"],"type":"u128"},{"name":"decrease_amount_0","docs":["The amount of token_0 that was paid for the decrease in liquidity"],"type":"u64"},{"name":"decrease_amount_1","docs":["The amount of token_1 that was paid for the decrease in liquidity"],"type":"u64"},{"name":"fee_amount_0","type":"u64"},{"name":"fee_amount_1","docs":["The amount of token_1 fee"],"type":"u64"},{"name":"reward_amounts","docs":["The amount of rewards"],"type":{"array":["u64",3]}},{"name":"transfer_fee_0","docs":["The amount of token_0 transfer fee"],"type":"u64"},{"name":"transfer_fee_1","docs":["The amount of token_1 transfer fee"],"type":"u64"}]}},{"name":"IncreaseLiquidityEvent","docs":["Emitted when liquidity is increased."],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which liquidity was increased"],"type":"pubkey"},{"name":"liquidity","docs":["The amount by which liquidity for the NFT position was increased"],"type":"u128"},{"name":"amount_0","docs":["The amount of token_0 that was paid for the increase in liquidity"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_1 that was paid for the increase in liquidity"],"type":"u64"},{"name":"amount_0_transfer_fee","docs":["The token transfer fee for amount_0"],"type":"u64"},{"name":"amount_1_transfer_fee","docs":["The token transfer fee for amount_1"],"type":"u64"}]}},{"name":"InitAdminGroupParams","type":{"kind":"struct","fields":[{"name":"fee_keeper","docs":["the address who can hold the fee,","anyone can trigger the fee collection action,"],"type":"pubkey"},{"name":"reward_config_manager","docs":["the address who can config the reward(config, deposit, withdraw),","deposit reward, set the account who can deposit the reward, withdraw the remaining reward(withdraw)"],"type":"pubkey"},{"name":"reward_claim_manager","docs":["the address who can manage the offchain reward claim"],"type":"pubkey"},{"name":"pool_manager","docs":["the address who can manage the pool create action,","without this account's permission, no one can create a pool"],"type":"pubkey"},{"name":"emergency_manager","docs":["the address who can manage the emergency action,","emergency action includes stop/resume the pool, stop/resume withdraw lp"],"type":"pubkey"},{"name":"normal_manager","docs":["normal action manager,","such as create amm config, update amm config"],"type":"pubkey"}]}},{"name":"InitializeRewardParam","type":{"kind":"struct","fields":[{"name":"open_time","docs":["Reward open time"],"type":"u64"},{"name":"end_time","docs":["Reward end time"],"type":"u64"},{"name":"emissions_per_second_x64","docs":["Token reward per second are earned per unit of liquidity"],"type":"u128"}]}},{"name":"LiquidityCalculateEvent","docs":["Emitted when liquidity decreased or increase."],"type":{"kind":"struct","fields":[{"name":"pool_liquidity","docs":["The pool liquidity before decrease or increase"],"type":"u128"},{"name":"pool_sqrt_price_x64","docs":["The pool price when decrease or increase in liquidity"],"type":"u128"},{"name":"pool_tick","docs":["The pool tick when decrease or increase in liquidity"],"type":"i32"},{"name":"calc_amount_0","docs":["The amount of token_0 that was calculated for the decrease or increase in liquidity"],"type":"u64"},{"name":"calc_amount_1","docs":["The amount of token_1 that was calculated for the decrease or increase in liquidity"],"type":"u64"},{"name":"trade_fee_owed_0","type":"u64"},{"name":"trade_fee_owed_1","docs":["The amount of token_1 fee"],"type":"u64"},{"name":"transfer_fee_0","docs":["The amount of token_0 transfer fee without trade_fee_amount_0"],"type":"u64"},{"name":"transfer_fee_1","docs":["The amount of token_1 transfer fee without trade_fee_amount_0"],"type":"u64"}]}},{"name":"LiquidityChangeEvent","docs":["Emitted pool liquidity change when increase and decrease liquidity"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for swap"],"type":"pubkey"},{"name":"tick","docs":["The tick of the pool"],"type":"i32"},{"name":"tick_lower","docs":["The tick lower of position"],"type":"i32"},{"name":"tick_upper","docs":["The tick lower of position"],"type":"i32"},{"name":"liquidity_before","docs":["The liquidity of the pool before liquidity change"],"type":"u128"},{"name":"liquidity_after","docs":["The liquidity of the pool after liquidity change"],"type":"u128"}]}},{"name":"ModifyAmmAdminGroupEvent","type":{"kind":"struct","fields":[{"name":"fee_keeper","type":"pubkey"},{"name":"reward_config_manager","type":"pubkey"},{"name":"reward_claim_manager","type":"pubkey"},{"name":"pool_manager","type":"pubkey"},{"name":"emergency_manager","type":"pubkey"},{"name":"normal_manager","type":"pubkey"}]}},{"name":"Observation","docs":["The element of observations in ObservationState"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"block_timestamp","docs":["The block timestamp of the observation"],"type":"u32"},{"name":"tick_cumulative","docs":["the cumulative of tick during the duration time"],"type":"i64"},{"name":"padding","docs":["padding for feature update"],"type":{"array":["u64",4]}}]}},{"name":"ObservationState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"initialized","docs":["Whether the ObservationState is initialized"],"type":"bool"},{"name":"recent_epoch","docs":["recent update epoch"],"type":"u64"},{"name":"observation_index","docs":["the most-recently updated index of the observations array"],"type":"u16"},{"name":"pool_id","docs":["belongs to which pool"],"type":"pubkey"},{"name":"observations","docs":["observation array"],"type":{"array":[{"defined":{"name":"Observation"}},100]}},{"name":"padding","docs":["padding for feature update"],"type":{"array":["u64",4]}}]}},{"name":"OffchainRewardConfig","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"pool_id","docs":["the pool state address"],"type":"pubkey"},{"name":"reward_vault","docs":["the vault to hold the reward","vault address is this config address"],"type":"pubkey"},{"name":"vault_bump","docs":["Bump to identify vault PDA"],"type":{"array":["u8",1]}},{"name":"reward_mint_vec","docs":["reward token mint address list","reward token account is ATA(reward_vault, reward_mint)"],"type":{"vec":"pubkey"}}]}},{"name":"OperationState","docs":["Holds the current owner of the factory"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"operation_owners","docs":["Address of the operation owner"],"type":{"array":["pubkey",10]}},{"name":"whitelist_mints","docs":["The mint address of whitelist to emit reward"],"type":{"array":["pubkey",100]}}]}},{"name":"PersonalPositionState","type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":{"array":["u8",1]}},{"name":"nft_mint","docs":["Mint address of the tokenized position"],"type":"pubkey"},{"name":"pool_id","docs":["The ID of the pool with which this token is connected"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"fee_growth_inside_0_last_x64","docs":["The token_0 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"fee_growth_inside_1_last_x64","docs":["The token_1 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"token_fees_owed_0","docs":["The fees owed to the position owner in token_0, as of the last computation"],"type":"u64"},{"name":"token_fees_owed_1","docs":["The fees owed to the position owner in token_1, as of the last computation"],"type":"u64"},{"name":"reward_infos","type":{"array":[{"defined":{"name":"PositionRewardInfo"}},3]}},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u64",7]}}]}},{"name":"PoolCreatedEvent","docs":["Emitted when a pool is created and initialized with a starting price",""],"type":{"kind":"struct","fields":[{"name":"token_mint_0","docs":["The first token of the pool by address sort order"],"type":"pubkey"},{"name":"token_mint_1","docs":["The second token of the pool by address sort order"],"type":"pubkey"},{"name":"tick_spacing","docs":["The minimum number of ticks between initialized ticks"],"type":"u16"},{"name":"pool_state","docs":["The address of the created pool"],"type":"pubkey"},{"name":"sqrt_price_x64","docs":["The initial sqrt price of the pool, as a Q64.64"],"type":"u128"},{"name":"tick","docs":["The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool"],"type":"i32"},{"name":"token_vault_0","docs":["Vault of token_0"],"type":"pubkey"},{"name":"token_vault_1","docs":["Vault of token_1"],"type":"pubkey"}]}},{"name":"PoolState","docs":["The pool state","","PDA of `[POOL_SEED, config, token_mint_0, token_mint_1]`",""],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":{"array":["u8",1]}},{"name":"amm_config","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"token_mint_0","docs":["Token pair of the pool, where token_mint_0 address < token_mint_1 address"],"type":"pubkey"},{"name":"token_mint_1","type":"pubkey"},{"name":"token_vault_0","docs":["Token pair vault"],"type":"pubkey"},{"name":"token_vault_1","type":"pubkey"},{"name":"observation_key","docs":["observation account key"],"type":"pubkey"},{"name":"mint_decimals_0","docs":["mint0 and mint1 decimals"],"type":"u8"},{"name":"mint_decimals_1","type":"u8"},{"name":"tick_spacing","docs":["The minimum number of ticks between initialized ticks"],"type":"u16"},{"name":"liquidity","docs":["The currently in range liquidity available to the pool."],"type":"u128"},{"name":"sqrt_price_x64","docs":["The current price of the pool as a sqrt(token_1/token_0) Q64.64 value"],"type":"u128"},{"name":"tick_current","docs":["The current tick of the pool, i.e. according to the last tick transition that was run."],"type":"i32"},{"name":"padding3","type":"u16"},{"name":"padding4","type":"u16"},{"name":"fee_growth_global_0_x64","docs":["The fee growth as a Q64.64 number, i.e. fees of token_0 and token_1 collected per","unit of liquidity for the entire life of the pool."],"type":"u128"},{"name":"fee_growth_global_1_x64","type":"u128"},{"name":"protocol_fees_token_0","docs":["The amounts of token_0 and token_1 that are owed to the protocol."],"type":"u64"},{"name":"protocol_fees_token_1","type":"u64"},{"name":"swap_in_amount_token_0","docs":["The amounts in and out of swap token_0 and token_1"],"type":"u128"},{"name":"swap_out_amount_token_1","type":"u128"},{"name":"swap_in_amount_token_1","type":"u128"},{"name":"swap_out_amount_token_0","type":"u128"},{"name":"status","docs":["Bitwise representation of the state of the pool","bit0, 1: disable open position and increase liquidity, 0: normal","bit1, 1: disable decrease liquidity, 0: normal","bit2, 1: disable collect fee, 0: normal","bit3, 1: disable collect reward, 0: normal","bit4, 1: disable swap, 0: normal"],"type":"u8"},{"name":"padding","docs":["Leave blank for future use"],"type":{"array":["u8",3]}},{"name":"trade_fee_rate","docs":["如果该值不为0, 则会覆盖 amm config 中的 trade_fee_rate 参数,作为实际收取的 trade fee rate","The trade fee, denominated in hundredths of a bip (10^-6)"],"type":"u32"},{"name":"reward_infos","type":{"array":[{"defined":{"name":"RewardInfo"}},3]}},{"name":"tick_array_bitmap","docs":["Packed initialized tick array state"],"type":{"array":["u64",16]}},{"name":"total_fees_token_0","docs":["except protocol_fee and fund_fee"],"type":"u64"},{"name":"total_fees_claimed_token_0","docs":["except protocol_fee and fund_fee"],"type":"u64"},{"name":"total_fees_token_1","type":"u64"},{"name":"total_fees_claimed_token_1","type":"u64"},{"name":"fund_fees_token_0","type":"u64"},{"name":"fund_fees_token_1","type":"u64"},{"name":"open_time","type":"u64"},{"name":"recent_epoch","type":"u64"},{"name":"decay_fee_flag","docs":["--- decay fee flag","bit0, 1: use decay fee, 0: not use decay fee","bit1, 1: use decay fee on sell for mint0, 0: not use decay fee on sell for mint0","bit2, 1: use decay fee on sell for mint1, 0: not use decay fee on sell for mint1","--- swap-dynamic-fee-flag","bit3, 1: token1 as quote token; token0 as quote token","bit4, 1: 启用 swap-dynamic-fee; 0: 不启用 swap-dynamic-fee"],"type":"u8"},{"name":"decay_fee_init_fee_rate","docs":["The initial decay fee rate for the pool, in percentage.(1=1%)"],"type":"u8"},{"name":"decay_fee_decrease_rate","docs":["decrease rate for the decay fee, in percentage.(1=1%)"],"type":"u8"},{"name":"decay_fee_decrease_interval","docs":["The interval for decreasing the decay fee, in seconds."],"type":"u8"},{"name":"arbitrage_fee_buffer_ppm","docs":["arbitrage_fee: 手工设置的一个容忍度,价差小于该值,不收取 arbitrageFee;单位取 10^-6(percent-per-million);"],"type":"u16"},{"name":"trade_slippage_fee_base","docs":["trade_slippage_fee: 公式中的b值,单位取 1/1000"],"type":"u8"},{"name":"trade_slippage_fee_trade_size_threshold","docs":["归一化(按quote数量计算)的swap-size值; 单位取 100"],"type":"u8"},{"name":"imbalance_fee_base","docs":["公式中的b,单位取1/10"],"type":"u8"},{"name":"imbalance_fee_x","docs":["公式中的x;单位取 1/100"],"type":"u8"},{"name":"padding1_1","type":{"array":["u8",6]}},{"name":"token0_pyth_feed_id","docs":["Pyth feed id for base token."],"type":{"array":["u8",32]}},{"name":"token1_pyth_feed_id","docs":["Pyth feed id for quote token."],"type":{"array":["u8",32]}},{"name":"padding1","type":{"array":["u64",14]}},{"name":"padding2","type":{"array":["u64",32]}}]}},{"name":"PositionRewardInfo","type":{"kind":"struct","fields":[{"name":"growth_inside_last_x64","type":"u128"},{"name":"reward_amount_owed","type":"u64"}]}},{"name":"ProtocolPositionState","docs":["Info stored for each user's position"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"pool_id","docs":["The ID of the pool with which this token is connected"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"fee_growth_inside_0_last_x64","docs":["The token_0 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"fee_growth_inside_1_last_x64","docs":["The token_1 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"token_fees_owed_0","docs":["The fees owed to the position owner in token_0"],"type":"u64"},{"name":"token_fees_owed_1","docs":["The fees owed to the position owner in token_1"],"type":"u64"},{"name":"reward_growth_inside","docs":["The reward growth per unit of liquidity as of the last update to liquidity"],"type":{"array":["u128",3]}},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u64",7]}}]}},{"name":"RewardInfo","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"reward_state","docs":["Reward state"],"type":"u8"},{"name":"open_time","docs":["Reward open time"],"type":"u64"},{"name":"end_time","docs":["Reward end time"],"type":"u64"},{"name":"last_update_time","docs":["Reward last update time"],"type":"u64"},{"name":"emissions_per_second_x64","docs":["Q64.64 number indicates how many tokens per second are earned per unit of liquidity."],"type":"u128"},{"name":"reward_total_emissioned","docs":["The total amount of reward emissioned"],"type":"u64"},{"name":"reward_claimed","docs":["The total amount of claimed reward"],"type":"u64"},{"name":"token_mint","docs":["Reward token mint."],"type":"pubkey"},{"name":"token_vault","docs":["Reward vault token account."],"type":"pubkey"},{"name":"authority","docs":["The owner that has permission to set reward param"],"type":"pubkey"},{"name":"reward_growth_global_x64","docs":["Q64.64 number that tracks the total tokens earned per unit of liquidity since the reward","emissions were turned on."],"type":"u128"}]}},{"name":"SetSwapDynamicFeeParamsInput","type":{"kind":"struct","fields":[{"name":"enabled","docs":["是否启用 swap-dynamic-fee"],"type":"bool"},{"name":"arbitrage_fee_buffer_ppm","docs":["arbitrage_fee 缓冲值,单位 ppm (10^-6)"],"type":{"option":"u16"}},{"name":"trade_slippage_fee_base","docs":["trade_slippage_fee 基数,单位 1/1000"],"type":{"option":"u8"}},{"name":"trade_slippage_fee_trade_size_threshold","docs":["trade_slippage_fee 阈值,单位 100"],"type":{"option":"u8"}},{"name":"imbalance_fee_base","docs":["imbalance_fee 基数,单位 1/10"],"type":{"option":"u8"}},{"name":"imbalance_fee_x","docs":["imbalance_fee 阈值,单位 1/100"],"type":{"option":"u8"}},{"name":"token0_pyth_feed_id","docs":["token0 的 Pyth feed id"],"type":{"option":{"array":["u8",32]}}},{"name":"token1_pyth_feed_id","docs":["token1 的 Pyth feed id"],"type":{"option":{"array":["u8",32]}}}]}},{"name":"SupportMintAssociated","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"mint","docs":["Address of the supported token22 mint"],"type":"pubkey"},{"name":"padding","type":{"array":["u64",8]}}]}},{"name":"SwapEvent","docs":["Emitted by when a swap is performed for a pool"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for which token_0 and token_1 were swapped"],"type":"pubkey"},{"name":"sender","docs":["The address that initiated the swap call, and that received the callback"],"type":"pubkey"},{"name":"token_account_0","docs":["The payer token account in zero for one swaps, or the recipient token account","in one for zero swaps"],"type":"pubkey"},{"name":"token_account_1","docs":["The payer token account in one for zero swaps, or the recipient token account","in zero for one swaps"],"type":"pubkey"},{"name":"amount_0","docs":["The real delta amount of the token_0 of the pool or user"],"type":"u64"},{"name":"transfer_fee_0","docs":["The transfer fee charged by the withheld_amount of the token_0"],"type":"u64"},{"name":"amount_1","docs":["The real delta of the token_1 of the pool or user"],"type":"u64"},{"name":"transfer_fee_1","docs":["The transfer fee charged by the withheld_amount of the token_1"],"type":"u64"},{"name":"zero_for_one","docs":["if true, amount_0 is negtive and amount_1 is positive"],"type":"bool"},{"name":"sqrt_price_x64","docs":["The sqrt(price) of the pool after the swap, as a Q64.64"],"type":"u128"},{"name":"liquidity","docs":["The liquidity of the pool after the swap"],"type":"u128"},{"name":"tick","docs":["The log base 1.0001 of price of the pool after the swap"],"type":"i32"}]}},{"name":"TickArrayBitmapExtension","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"positive_tick_array_bitmap","docs":["Packed initialized tick array state for start_tick_index is positive"],"type":{"array":[{"array":["u64",8]},14]}},{"name":"negative_tick_array_bitmap","docs":["Packed initialized tick array state for start_tick_index is negitive"],"type":{"array":[{"array":["u64",8]},14]}}]}},{"name":"UpdateAdminGroupParams","type":{"kind":"struct","fields":[{"name":"fee_keeper","docs":["the address who can hold the fee,","anyone can trigger the fee collection action,"],"type":{"option":"pubkey"}},{"name":"reward_config_manager","docs":["the address who can config the reward(config, deposit, withdraw),","deposit reward, set the account who can deposit the reward, withdraw the remaining reward(withdraw)"],"type":{"option":"pubkey"}},{"name":"reward_claim_manager","docs":["the address who can manage the offchain reward claim"],"type":{"option":"pubkey"}},{"name":"pool_manager","docs":["the address who can manage the pool create action,","without this account's permission, no one can create a pool"],"type":{"option":"pubkey"}},{"name":"emergency_manager","docs":["the address who can manage the emergency action,","emergency action includes stop/resume the pool, stop/resume withdraw lp"],"type":{"option":"pubkey"}},{"name":"normal_manager","docs":["normal action manager,","such as create amm config, update amm config"],"type":{"option":"pubkey"}}]}},{"name":"UpdateRewardInfosEvent","docs":["Emitted when Reward are updated for a pool"],"type":{"kind":"struct","fields":[{"name":"reward_growth_global_x64","docs":["Reward info"],"type":{"array":["u128",3]}}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/carrot.CarrotwivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s.json b/migration/khadhroony-bot2-reference/idls/carrot.CarrotwivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s.json new file mode 100644 index 0000000..f3bbb6d --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/carrot.CarrotwivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"carrot","instructions":[{"name":"initVault","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"shares","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"InitVaultArgs"}}]},{"name":"transferVaultAuthority","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"newAuthority","isMut":false,"isSigner":false},{"name":"oldAuthority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"pauseVault","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true}],"args":[{"name":"args","type":{"defined":"PauseVaultArgs"}}]},{"name":"migrateVaultAccount","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"distributeVaultFees","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"shares","isMut":true,"isSigner":false},{"name":"sharesDestination","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateFee","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdateFeeArgs"}}]},{"name":"issue","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"shares","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"asset","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"userAssetAta","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"assetTokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"IssueArgs"}}]},{"name":"redeem","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"shares","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"asset","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"userAssetAta","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"assetTokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"RedeemArgs"}}]},{"name":"addAsset","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"assetOracle","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateAsset","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdateAssetArgs"}}]},{"name":"removeAsset","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"asset","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"removeStrategy","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateStrategy","accounts":[{"name":"vault","isMut":false,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"UpdateStrategyArgs"}}]},{"name":"marginfiSupplyStrategyInit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"marginfiGroup","isMut":false,"isSigner":false},{"name":"marginfiAccount","isMut":true,"isSigner":true},{"name":"marginfiBank","isMut":false,"isSigner":false},{"name":"marginfiBankLiquidityVault","isMut":false,"isSigner":false},{"name":"marginfiBankLiquidityVaultAuthority","isMut":false,"isSigner":false},{"name":"marginfiOracle","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"marginfiProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MarginfiSupplyStrategyInitArgs"}}]},{"name":"marginfiSupplyStrategyDeposit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"marginfiGroup","isMut":false,"isSigner":false},{"name":"marginfiAccount","isMut":true,"isSigner":false},{"name":"marginfiBank","isMut":true,"isSigner":false},{"name":"marginfiBankLiquidityVault","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"marginfiProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MarginfiSupplyStrategyDepositArgs"}}]},{"name":"marginfiSupplyStrategyWithdraw","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"marginfiGroup","isMut":false,"isSigner":false},{"name":"marginfiAccount","isMut":true,"isSigner":false},{"name":"marginfiBank","isMut":true,"isSigner":false},{"name":"marginfiBankLiquidityVault","isMut":true,"isSigner":false},{"name":"marginfiBankLiquidityVaultAuthority","isMut":true,"isSigner":false},{"name":"marginfiOracle","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"marginfiProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MarginfiSupplyStrategyWithdrawArgs"}}]},{"name":"marginfiSupplyStrategyClaimEmissions","accounts":[{"name":"vault","isMut":false,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"marginfiGroup","isMut":false,"isSigner":false},{"name":"marginfiAccount","isMut":true,"isSigner":false},{"name":"marginfiBank","isMut":true,"isSigner":false},{"name":"marginfiEmissionsVault","isMut":true,"isSigner":false},{"name":"marginfiEmissionsAuth","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"marginfiProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"klendSupplyStrategyInit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"assetPythOracle","isMut":true,"isSigner":false},{"name":"scopePrices","isMut":true,"isSigner":false},{"name":"userMetadata","isMut":true,"isSigner":false},{"name":"obligation","isMut":true,"isSigner":false},{"name":"obligationFarm","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":true,"isSigner":false},{"name":"lendingMarketAuthority","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":false,"isSigner":false},{"name":"reserveDestinationDepositCollateral","isMut":false,"isSigner":false},{"name":"reserveFarmState","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"kfarmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"KlendSupplyStrategyInitArgs"}}]},{"name":"klendSupplyStrategyDeposit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":true,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveDestinationDepositCollateral","isMut":true,"isSigner":false},{"name":"vaultCollateralAta","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false},{"name":"obligation","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"instructionsSysvar","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"assetTokenProgram","isMut":false,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"kfarmsProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"KlendSupplyStrategyDepositArgs"}}]},{"name":"klendSupplyStrategyWithdraw","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":true,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveDestinationDepositCollateral","isMut":true,"isSigner":false},{"name":"vaultCollateralAta","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false},{"name":"obligation","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"instructionsSysvar","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"assetTokenProgram","isMut":false,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"kfarmsProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"KlendSupplyStrategyWithdrawArgs"}}]},{"name":"klendSupplyStrategyClaimEmissions","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false},{"name":"userState","isMut":true,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false},{"name":"kfarmsGlobalConfig","isMut":false,"isSigner":false},{"name":"kfarmsVaultsAuthority","isMut":false,"isSigner":false},{"name":"kfarmsRewardsVault","isMut":true,"isSigner":false},{"name":"kfarmsRewardsTreasuryVault","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"kfarmsProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"solendSupplyStrategyInit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"assetPythOracle","isMut":false,"isSigner":false},{"name":"assetSwitchboardOracle","isMut":false,"isSigner":false},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":false,"isSigner":false},{"name":"reserveCollateralMint","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":false,"isSigner":false},{"name":"reserveDepositCollateralAta","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"solendProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"SolendSupplyStrategyInitArgs"}}]},{"name":"solendSupplyStrategyDeposit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"assetPythOracle","isMut":false,"isSigner":false},{"name":"assetSwitchboardOracle","isMut":false,"isSigner":false},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":true,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"vaultCollateralAta","isMut":true,"isSigner":false},{"name":"depositCollateralAta","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"solendProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"SolendSupplyStrategyDepositArgs"}}]},{"name":"solendSupplyStrategyWithdraw","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"assetPythOracle","isMut":false,"isSigner":false},{"name":"assetSwitchboardOracle","isMut":false,"isSigner":false},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":true,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"vaultCollateralAta","isMut":true,"isSigner":false},{"name":"withdrawCollateralAta","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"solendProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"SolendSupplyStrategyWithdrawArgs"}}]},{"name":"mangoSupplyStrategyInit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"mangoGroup","isMut":false,"isSigner":false},{"name":"mangoAccount","isMut":true,"isSigner":false},{"name":"mangoBank","isMut":false,"isSigner":false},{"name":"mangoVault","isMut":false,"isSigner":false},{"name":"pythOracle","isMut":false,"isSigner":false},{"name":"switchboardOracle","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"mangoProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MangoSupplyStrategyInitArgs"}}]},{"name":"mangoSupplyStrategyDeposit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"mangoGroup","isMut":false,"isSigner":false},{"name":"mangoAccount","isMut":true,"isSigner":false},{"name":"mangoBank","isMut":true,"isSigner":false},{"name":"mangoVault","isMut":true,"isSigner":false},{"name":"pythOracle","isMut":false,"isSigner":false},{"name":"switchboardOracle","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"mangoProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MangoSupplyStrategyDepositArgs"}}]},{"name":"mangoSupplyStrategyWithdraw","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"mangoGroup","isMut":false,"isSigner":false},{"name":"mangoAccount","isMut":true,"isSigner":false},{"name":"mangoBank","isMut":true,"isSigner":false},{"name":"mangoVault","isMut":true,"isSigner":false},{"name":"pythOracle","isMut":false,"isSigner":false},{"name":"switchboardOracle","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"mangoProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MangoSupplyStrategyWithdrawArgs"}}]},{"name":"driftSupplyStrategyInit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"driftUser","isMut":true,"isSigner":false},{"name":"driftUserStats","isMut":true,"isSigner":false},{"name":"driftState","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"driftSpotMarket","isMut":false,"isSigner":false},{"name":"driftPerpMarket","isMut":false,"isSigner":false},{"name":"driftSpotMarketVault","isMut":false,"isSigner":false},{"name":"spotPythOracle","isMut":false,"isSigner":false},{"name":"perpPythOracle","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"driftProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"DriftSupplyStrategyInitArgs"}}]},{"name":"driftSupplyStrategyDeposit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"driftUser","isMut":true,"isSigner":false},{"name":"driftUserStats","isMut":true,"isSigner":false},{"name":"driftState","isMut":true,"isSigner":false},{"name":"driftSpotMarket","isMut":true,"isSigner":false},{"name":"driftSpotMarketVault","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"driftProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"DriftSupplyStrategyDepositArgs"}}]},{"name":"driftSupplyStrategyWithdraw","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"driftUser","isMut":true,"isSigner":false},{"name":"driftUserStats","isMut":true,"isSigner":false},{"name":"driftState","isMut":true,"isSigner":false},{"name":"driftSpotMarketVault","isMut":true,"isSigner":false},{"name":"driftSpotMarket","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"driftProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"DriftSupplyStrategyWithdrawArgs"}}]},{"name":"driftInsuranceFundStrategyInit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"driftUserStats","isMut":true,"isSigner":false},{"name":"driftSpotMarket","isMut":true,"isSigner":false},{"name":"driftSpotMarketVault","isMut":false,"isSigner":false},{"name":"driftState","isMut":true,"isSigner":false},{"name":"driftInsuranceFundStake","isMut":true,"isSigner":false},{"name":"driftInsuranceFundVault","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"DriftInsuranceFundStrategyInitArgs"}}]},{"name":"driftInsuranceFundStrategyStake","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"driftUserStats","isMut":true,"isSigner":false},{"name":"driftSpotMarket","isMut":true,"isSigner":false},{"name":"driftSpotMarketVault","isMut":true,"isSigner":false},{"name":"driftState","isMut":false,"isSigner":false},{"name":"driftInsuranceFundStake","isMut":true,"isSigner":false},{"name":"driftInsuranceFundVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"DriftInsuranceFundStrategyStakeArgs"}}]},{"name":"driftInsuranceFundStrategyUnstake","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"driftUserStats","isMut":true,"isSigner":false},{"name":"driftSpotMarket","isMut":true,"isSigner":false},{"name":"driftInsuranceFundStake","isMut":true,"isSigner":false},{"name":"driftInsuranceFundVault","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"driftProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"DriftInsuranceFundStrategyUnstakeArgs"}}]},{"name":"driftInsuranceFundStrategyWithdraw","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"driftUserStats","isMut":true,"isSigner":false},{"name":"driftSpotMarket","isMut":true,"isSigner":false},{"name":"driftState","isMut":false,"isSigner":false},{"name":"driftInsuranceFundStake","isMut":true,"isSigner":false},{"name":"driftInsuranceFundVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"clendSupplyStrategyInit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"clendGroup","isMut":false,"isSigner":false},{"name":"clendAccount","isMut":true,"isSigner":true},{"name":"clendBank","isMut":false,"isSigner":false},{"name":"clendBankLiquidityVault","isMut":false,"isSigner":false},{"name":"clendBankLiquidityVaultAuthority","isMut":false,"isSigner":false},{"name":"clendOracle","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"clendProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ClendSupplyStrategyInitArgs"}}]},{"name":"clendSupplyStrategyDeposit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"clendGroup","isMut":false,"isSigner":false},{"name":"clendAccount","isMut":true,"isSigner":false},{"name":"clendBank","isMut":true,"isSigner":false},{"name":"clendBankLiquidityVault","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"clendProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ClendSupplyStrategyDepositArgs"}}]},{"name":"clendSupplyStrategyWithdraw","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategy","isMut":false,"isSigner":false},{"name":"clendGroup","isMut":false,"isSigner":false},{"name":"clendAccount","isMut":true,"isSigner":false},{"name":"clendBank","isMut":true,"isSigner":false},{"name":"clendBankLiquidityVault","isMut":true,"isSigner":false},{"name":"clendBankLiquidityVaultAuthority","isMut":true,"isSigner":false},{"name":"clendOracle","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"clendProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ClendSupplyStrategyWithdrawArgs"}}]},{"name":"chestStrategyInit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"assetTokenAccount","isMut":false,"isSigner":false},{"name":"chest","isMut":false,"isSigner":false},{"name":"coinTokenAccount","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ChestStrategyInitArgs"}}]},{"name":"chestStrategyDeposit","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"chest","isMut":true,"isSigner":false},{"name":"coin","isMut":true,"isSigner":false},{"name":"chestAssetReserve","isMut":true,"isSigner":false},{"name":"driftVault","isMut":true,"isSigner":false},{"name":"driftVaultDepositor","isMut":true,"isSigner":false},{"name":"driftVaultTokenAccount","isMut":true,"isSigner":false},{"name":"driftUser","isMut":true,"isSigner":false},{"name":"driftUserStats","isMut":true,"isSigner":false},{"name":"driftSpotMarket","isMut":true,"isSigner":false},{"name":"driftSpotMarketVault","isMut":true,"isSigner":false},{"name":"driftState","isMut":true,"isSigner":false},{"name":"vaultAssetTokenAccount","isMut":true,"isSigner":false},{"name":"vaultCoinTokenAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"chestProgram","isMut":false,"isSigner":false},{"name":"driftProgram","isMut":false,"isSigner":false},{"name":"driftVaultsProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ChestStrategyDepositArgs"}}]},{"name":"chestStrategyRequestWithdraw","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"chest","isMut":true,"isSigner":false},{"name":"coin","isMut":true,"isSigner":false},{"name":"chestCoinReserve","isMut":true,"isSigner":false},{"name":"redemptionRequest","isMut":true,"isSigner":false},{"name":"driftVault","isMut":true,"isSigner":false},{"name":"driftVaultDepositor","isMut":true,"isSigner":false},{"name":"driftUser","isMut":true,"isSigner":false},{"name":"assetDestination","isMut":false,"isSigner":false},{"name":"vaultCoinTokenAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"chestProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ChestStrategyRequestWithdrawArgs"}}]},{"name":"chestStrategyWithdraw","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"vaultAssetAta","isMut":true,"isSigner":false},{"name":"strategyAssetTokenAccount","isMut":true,"isSigner":false},{"name":"chest","isMut":true,"isSigner":false},{"name":"chestRedemptionRequest","isMut":false,"isSigner":false},{"name":"coin","isMut":true,"isSigner":false},{"name":"vaultCoinTokenAccount","isMut":true,"isSigner":false},{"name":"driftVault","isMut":true,"isSigner":false},{"name":"driftVaultDepositor","isMut":true,"isSigner":false},{"name":"driftUser","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"chestProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ChestStrategyWithdrawArgs"}}]},{"name":"updateSwitchboardOraclePrice","accounts":[{"name":"vault","isMut":true,"isSigner":false},{"name":"shares","isMut":false,"isSigner":false},{"name":"feed","isMut":true,"isSigner":false},{"name":"queue","isMut":false,"isSigner":false},{"name":"programState","isMut":false,"isSigner":false},{"name":"recentSlothashes","isMut":false,"isSigner":false},{"name":"rewardVault","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"switchboardOnDemandProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"logProgram","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"Vault","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"shares","type":"publicKey"},{"name":"fee","type":{"defined":"Fee"}},{"name":"paused","type":"bool"},{"name":"assetIndex","type":"u16"},{"name":"strategyIndex","type":"u16"},{"name":"assets","type":{"vec":{"defined":"Asset"}}},{"name":"strategies","type":{"vec":{"defined":"StrategyRecord"}}}]}},{"name":"Strategy","type":{"kind":"struct","fields":[{"name":"metadata","type":{"defined":"StrategyMetadata"}},{"name":"strategyType","type":{"defined":"StrategyType"}}]}}],"types":[{"name":"ChestStrategyDepositArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"failOnLowBalance","type":"bool"},{"name":"preserveRecordedBalance","type":"bool"}]}},{"name":"ChestStrategyInitArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"}]}},{"name":"ChestStrategyRequestWithdrawArgs","type":{"kind":"struct","fields":[{"name":"assetAmount","type":"u64"}]}},{"name":"ChestStrategyWithdrawArgs","type":{"kind":"struct","fields":[{"name":"failOnLowBalance","type":"bool"},{"name":"preserveRecordedBalance","type":"bool"}]}},{"name":"ClendSupplyStrategyDepositArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"ClendSupplyStrategyInitArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"}]}},{"name":"ClendSupplyStrategyWithdrawArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"DriftInsuranceFundStrategyInitArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"driftMarketIndex","type":"u16"}]}},{"name":"DriftInsuranceFundStrategyStakeArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"DriftInsuranceFundStrategyUnstakeArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"DriftSupplyStrategyDepositArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"DriftSupplyStrategyInitArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"driftSubAccountId","type":"u16"},{"name":"driftMarketIndex","type":"u16"}]}},{"name":"DriftSupplyStrategyWithdrawArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"InitVaultArgs","type":{"kind":"struct","fields":[{"name":"redemptionFeeBps","type":"u16"},{"name":"managementFeeBps","type":"u16"},{"name":"performanceFeeBps","type":"u16"}]}},{"name":"IssueArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"KlendSupplyStrategyDepositArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"KlendSupplyStrategyInitArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"}]}},{"name":"KlendSupplyStrategyWithdrawArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"MangoSupplyStrategyDepositArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"MangoSupplyStrategyInitArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"}]}},{"name":"MangoSupplyStrategyWithdrawArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"MarginfiSupplyStrategyDepositArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"MarginfiSupplyStrategyInitArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"}]}},{"name":"MarginfiSupplyStrategyWithdrawArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"PauseVaultArgs","type":{"kind":"struct","fields":[{"name":"paused","type":"bool"}]}},{"name":"RedeemArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"SolendSupplyStrategyDepositArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"SolendSupplyStrategyInitArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"}]}},{"name":"SolendSupplyStrategyWithdrawArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"UpdateAssetArgs","type":{"kind":"struct","fields":[{"name":"assetId","type":"u16"},{"name":"assetNewOracle","type":"publicKey"}]}},{"name":"UpdateFeeArgs","type":{"kind":"struct","fields":[{"name":"newRedemptionFeeBps","type":{"option":"u16"}},{"name":"newManagementFeeBps","type":{"option":"u16"}},{"name":"newPerformanceFeeBps","type":{"option":"u16"}}]}},{"name":"UpdateStrategyArgs","type":{"kind":"struct","fields":[{"name":"newStrategyType","type":{"defined":"StrategyType"}}]}},{"name":"Asset","type":{"kind":"struct","fields":[{"name":"assetId","type":"u16"},{"name":"mint","type":"publicKey"},{"name":"decimals","type":"u8"},{"name":"ata","type":"publicKey"},{"name":"oracle","type":"publicKey"}]}},{"name":"StrategyRecord","type":{"kind":"struct","fields":[{"name":"strategyId","type":"u16"},{"name":"assetId","type":"u16"},{"name":"balance","type":"u64"},{"name":"netEarnings","type":"i64"}]}},{"name":"StrategyMetadata","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"strategyId","type":"u16"},{"name":"assetMint","type":"publicKey"},{"name":"vault","type":"publicKey"}]}},{"name":"Fee","type":{"kind":"struct","fields":[{"name":"redemptionFeeBps","type":"u16"},{"name":"redemptionFeeAccumulated","type":"u64"},{"name":"managementFeeBps","type":"u16"},{"name":"managementFeeLastUpdate","type":"i64"},{"name":"managementFeeAccumulated","type":"u64"},{"name":"performanceFeeBps","type":"u16"}]}},{"name":"IssueEvent","type":{"kind":"struct","fields":[{"name":"depositor","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"amount","type":"u64"},{"name":"sharesMinted","type":"u64"},{"name":"managementFee","type":"u64"},{"name":"tvl","type":"u128"}]}},{"name":"RedeemEvent","type":{"kind":"struct","fields":[{"name":"withdrawer","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"amount","type":"u64"},{"name":"sharesBurned","type":"u64"},{"name":"redemptionFee","type":"u64"},{"name":"managementFee","type":"u64"},{"name":"tvl","type":"u128"}]}},{"name":"StrategyDepositEvent","type":{"kind":"struct","fields":[{"name":"strategy","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"depositAmount","type":"u64"},{"name":"netEarnings","type":"i64"},{"name":"strategyBalance","type":"u64"}]}},{"name":"StrategyWithdrawEvent","type":{"kind":"struct","fields":[{"name":"strategy","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"withdrawAmount","type":"u64"},{"name":"netEarnings","type":"i64"},{"name":"strategyBalance","type":"u64"}]}},{"name":"StrategyClaimEmissionsEvent","type":{"kind":"struct","fields":[{"name":"strategy","type":"publicKey"},{"name":"mint","type":"publicKey"}]}},{"name":"DistributeVaultFeesEvent","type":{"kind":"struct","fields":[{"name":"managementFeeAmountDistributed","type":"u64"},{"name":"performanceFeeAmountDistributed","type":"u64"},{"name":"redemptionFeeAmountDistributed","type":"u64"}]}},{"name":"UpdateSwitchboardOraclePriceEvent","type":{"kind":"struct","fields":[{"name":"vaultNav","type":"i128"},{"name":"vaultTvl","type":"u128"},{"name":"sharesSupply","type":"u64"}]}},{"name":"StrategyType","type":{"kind":"enum","variants":[{"name":"MarginfiSupply","fields":[{"name":"account","type":"publicKey"},{"name":"group","type":"publicKey"},{"name":"bank","type":"publicKey"},{"name":"bankLiquidityVault","type":"publicKey"},{"name":"bankLiquidityVaultAuthority","type":"publicKey"},{"name":"oracle","type":"publicKey"}]},{"name":"KlendSupply","fields":[{"name":"reserve","type":"publicKey"},{"name":"reserveCollateralMint","type":"publicKey"},{"name":"reserveLiquiditySupply","type":"publicKey"},{"name":"reserveDestinationDepositCollateral","type":"publicKey"},{"name":"reserveFarmState","type":"publicKey"},{"name":"lendingMarket","type":"publicKey"},{"name":"oracle","type":"publicKey"},{"name":"scopePrices","type":"publicKey"}]},{"name":"SolendSupply","fields":[{"name":"reserve","type":"publicKey"},{"name":"reserveCollateralMint","type":"publicKey"},{"name":"reserveLiquiditySupply","type":"publicKey"},{"name":"depositCollateralAta","type":"publicKey"},{"name":"lendingMarket","type":"publicKey"},{"name":"lendingMarketAuthority","type":"publicKey"},{"name":"pythOracle","type":"publicKey"},{"name":"switchboardOracle","type":"publicKey"}]},{"name":"MangoSupply","fields":[{"name":"group","type":"publicKey"},{"name":"account","type":"publicKey"},{"name":"bank","type":"publicKey"},{"name":"vault","type":"publicKey"},{"name":"pythOracle","type":"publicKey"},{"name":"switchboardOracle","type":"publicKey"}]},{"name":"DriftSupply","fields":[{"name":"state","type":"publicKey"},{"name":"signer","type":"publicKey"},{"name":"spotMarket","type":"publicKey"},{"name":"spotMarketVault","type":"publicKey"},{"name":"perpMarket","type":"publicKey"},{"name":"spotPythOracle","type":"publicKey"},{"name":"perpPythOracle","type":"publicKey"},{"name":"subAccountId","type":"u16"},{"name":"marketIndex","type":"u16"}]},{"name":"DriftInsuranceFund","fields":[{"name":"state","type":"publicKey"},{"name":"spotMarket","type":"publicKey"},{"name":"spotMarketVault","type":"publicKey"},{"name":"marketIndex","type":"u16"}]},{"name":"Chest","fields":[{"name":"chest","type":"publicKey"},{"name":"coin","type":"publicKey"},{"name":"driftVault","type":"publicKey"},{"name":"coinTokenAccount","type":"publicKey"},{"name":"assetDestination","type":"publicKey"},{"name":"redemptionRequest","type":{"option":"publicKey"}},{"name":"spotMarketIndex","type":"u16"}]},{"name":"ClendSupply","fields":[{"name":"account","type":"publicKey"},{"name":"group","type":"publicKey"},{"name":"bank","type":"publicKey"},{"name":"bankLiquidityVault","type":"publicKey"},{"name":"bankLiquidityVaultAuthority","type":"publicKey"},{"name":"oracle","type":"publicKey"}]}]}},{"name":"StrategyTypeSelection","type":{"kind":"enum","variants":[{"name":"MarginfiSupply"},{"name":"KlendSupply"},{"name":"SolendSupply"},{"name":"MangoSupply"},{"name":"DriftSupply"},{"name":"DriftInsuranceFund"},{"name":"Chest"},{"name":"ClendSupply"}]}},{"name":"CarrotEvent","type":{"kind":"enum","variants":[{"name":"Issue","fields":[{"defined":"IssueEvent"}]},{"name":"Redeem","fields":[{"defined":"RedeemEvent"}]},{"name":"StrategyDeposit","fields":[{"defined":"StrategyDepositEvent"}]},{"name":"StrategyWithdraw","fields":[{"defined":"StrategyWithdrawEvent"}]},{"name":"StrategyClaimEmissions","fields":[{"defined":"StrategyClaimEmissionsEvent"}]},{"name":"DistributeVaultFees","fields":[{"defined":"DistributeVaultFeesEvent"}]},{"name":"UpdateSwitchboardOraclePrice","fields":[{"defined":"UpdateSwitchboardOraclePriceEvent"}]}]}},{"name":"RoundingMode","type":{"kind":"enum","variants":[{"name":"RoundUp"},{"name":"RoundDown"},{"name":"Avg"}]}}],"errors":[{"code":6000,"name":"AssetAlreadyExists","msg":"Asset Already Exists"},{"code":6001,"name":"AssetNotFound","msg":"Asset Not Found"},{"code":6002,"name":"OracleNotFound","msg":"Oracle Not Found"},{"code":6003,"name":"OracleStalePrice","msg":"Oracle Returned Stale Price"},{"code":6004,"name":"StrategyNotFound","msg":"Strategy Not Found"},{"code":6005,"name":"AtaNotFound","msg":"Ata Not Found"},{"code":6006,"name":"StrategyAlreadyExists","msg":"Strategy Already Exists"},{"code":6007,"name":"InvalidStrategyType","msg":"Invalid Strategy Type"},{"code":6008,"name":"StrategyBalanceCalculationError","msg":"Strategy Balance Calculation Error"},{"code":6009,"name":"VaultIsPaused","msg":"Vault is Paused"},{"code":6010,"name":"InvalidVaultAuthority","msg":"Invalid Vault Authority"},{"code":6011,"name":"MigrationError","msg":"Account Migration Error"},{"code":6012,"name":"InvalidPriceConf","msg":"Invalid Price Confidence Interval"},{"code":6013,"name":"InvalidStrategyAccounts","msg":"Invalid Strategy Accounts"},{"code":6014,"name":"StrategyNotEmpty","msg":"Strategy Not Empty"},{"code":6015,"name":"AssetNotEmpty","msg":"Asset Not Empty"},{"code":6016,"name":"InvalidFeeBps","msg":"Invalid Fee Bps"},{"code":6017,"name":"InvalidReturnData","msg":"Invalid Return Data"},{"code":6018,"name":"InvalidCoinSupply","msg":"Invalid Coin Supply"},{"code":6019,"name":"InvalidNav","msg":"Invalid NAV"},{"code":6020,"name":"MathError","msg":"Math Error"},{"code":6021,"name":"ChestLowBalance","msg":"Chest Low Balance"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/cctp_v1.CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3.json b/migration/khadhroony-bot2-reference/idls/cctp_v1.CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3.json new file mode 100644 index 0000000..1c7342c --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/cctp_v1.CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"token_messenger_minter","instructions":[{"name":"initialize","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"upgradeAuthority","isMut":false,"isSigner":true},{"name":"authorityPda","isMut":false,"isSigner":false},{"name":"tokenMessenger","isMut":true,"isSigner":false},{"name":"tokenMinter","isMut":true,"isSigner":false},{"name":"tokenMessengerMinterProgramData","isMut":false,"isSigner":false},{"name":"tokenMessengerMinterProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitializeParams"}}]},{"name":"transferOwnership","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"tokenMessenger","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"TransferOwnershipParams"}}]},{"name":"acceptOwnership","accounts":[{"name":"pendingOwner","isMut":false,"isSigner":true},{"name":"tokenMessenger","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"AcceptOwnershipParams"}}]},{"name":"addRemoteTokenMessenger","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"owner","isMut":false,"isSigner":true},{"name":"tokenMessenger","isMut":false,"isSigner":false},{"name":"remoteTokenMessenger","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"AddRemoteTokenMessengerParams"}}]},{"name":"removeRemoteTokenMessenger","accounts":[{"name":"payee","isMut":true,"isSigner":true},{"name":"owner","isMut":false,"isSigner":true},{"name":"tokenMessenger","isMut":false,"isSigner":false},{"name":"remoteTokenMessenger","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"RemoveRemoteTokenMessengerParams"}}]},{"name":"depositForBurn","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"eventRentPayer","isMut":true,"isSigner":true},{"name":"senderAuthorityPda","isMut":false,"isSigner":false},{"name":"burnTokenAccount","isMut":true,"isSigner":false},{"name":"messageTransmitter","isMut":true,"isSigner":false},{"name":"tokenMessenger","isMut":false,"isSigner":false},{"name":"remoteTokenMessenger","isMut":false,"isSigner":false},{"name":"tokenMinter","isMut":false,"isSigner":false},{"name":"localToken","isMut":true,"isSigner":false},{"name":"burnTokenMint","isMut":true,"isSigner":false},{"name":"messageSentEventData","isMut":true,"isSigner":true},{"name":"messageTransmitterProgram","isMut":false,"isSigner":false},{"name":"tokenMessengerMinterProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"DepositForBurnParams"}}],"returns":"u64"},{"name":"depositForBurnWithCaller","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"eventRentPayer","isMut":true,"isSigner":true},{"name":"senderAuthorityPda","isMut":false,"isSigner":false},{"name":"burnTokenAccount","isMut":true,"isSigner":false},{"name":"messageTransmitter","isMut":true,"isSigner":false},{"name":"tokenMessenger","isMut":false,"isSigner":false},{"name":"remoteTokenMessenger","isMut":false,"isSigner":false},{"name":"tokenMinter","isMut":false,"isSigner":false},{"name":"localToken","isMut":true,"isSigner":false},{"name":"burnTokenMint","isMut":true,"isSigner":false},{"name":"messageSentEventData","isMut":true,"isSigner":true},{"name":"messageTransmitterProgram","isMut":false,"isSigner":false},{"name":"tokenMessengerMinterProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"DepositForBurnWithCallerParams"}}],"returns":"u64"},{"name":"replaceDepositForBurn","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"eventRentPayer","isMut":true,"isSigner":true},{"name":"senderAuthorityPda","isMut":false,"isSigner":false},{"name":"messageTransmitter","isMut":true,"isSigner":false},{"name":"tokenMessenger","isMut":false,"isSigner":false},{"name":"messageSentEventData","isMut":true,"isSigner":true},{"name":"messageTransmitterProgram","isMut":false,"isSigner":false},{"name":"tokenMessengerMinterProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"ReplaceDepositForBurnParams"}}],"returns":"u64"},{"name":"handleReceiveMessage","accounts":[{"name":"authorityPda","isMut":false,"isSigner":true},{"name":"tokenMessenger","isMut":false,"isSigner":false},{"name":"remoteTokenMessenger","isMut":false,"isSigner":false},{"name":"tokenMinter","isMut":false,"isSigner":false},{"name":"localToken","isMut":true,"isSigner":false},{"name":"tokenPair","isMut":false,"isSigner":false},{"name":"recipientTokenAccount","isMut":true,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"HandleReceiveMessageParams"}}]},{"name":"setTokenController","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"tokenMessenger","isMut":false,"isSigner":false},{"name":"tokenMinter","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetTokenControllerParams"}}]},{"name":"pause","accounts":[{"name":"pauser","isMut":false,"isSigner":true},{"name":"tokenMinter","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"PauseParams"}}]},{"name":"unpause","accounts":[{"name":"pauser","isMut":false,"isSigner":true},{"name":"tokenMinter","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"UnpauseParams"}}]},{"name":"updatePauser","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"tokenMessenger","isMut":false,"isSigner":false},{"name":"tokenMinter","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"UpdatePauserParams"}}]},{"name":"setMaxBurnAmountPerMessage","accounts":[{"name":"tokenController","isMut":false,"isSigner":true},{"name":"tokenMinter","isMut":false,"isSigner":false},{"name":"localToken","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetMaxBurnAmountPerMessageParams"}}]},{"name":"addLocalToken","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"tokenController","isMut":false,"isSigner":true},{"name":"tokenMinter","isMut":false,"isSigner":false},{"name":"localToken","isMut":true,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"localTokenMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"AddLocalTokenParams"}}]},{"name":"removeLocalToken","accounts":[{"name":"payee","isMut":true,"isSigner":true},{"name":"tokenController","isMut":false,"isSigner":true},{"name":"tokenMinter","isMut":false,"isSigner":false},{"name":"localToken","isMut":true,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"RemoveLocalTokenParams"}}]},{"name":"linkTokenPair","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"tokenController","isMut":false,"isSigner":true},{"name":"tokenMinter","isMut":false,"isSigner":false},{"name":"tokenPair","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"LinkTokenPairParams"}}]},{"name":"unlinkTokenPair","accounts":[{"name":"payee","isMut":true,"isSigner":true},{"name":"tokenController","isMut":false,"isSigner":true},{"name":"tokenMinter","isMut":false,"isSigner":false},{"name":"tokenPair","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"UninkTokenPairParams"}}]},{"name":"burnTokenCustody","accounts":[{"name":"payee","isMut":true,"isSigner":true},{"name":"tokenController","isMut":false,"isSigner":true},{"name":"tokenMinter","isMut":false,"isSigner":false},{"name":"localToken","isMut":false,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"custodyTokenMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"BurnTokenCustodyParams"}}]}],"accounts":[{"name":"TokenMessenger","type":{"kind":"struct","fields":[{"name":"owner","type":"publicKey"},{"name":"pendingOwner","type":"publicKey"},{"name":"localMessageTransmitter","type":"publicKey"},{"name":"messageBodyVersion","type":"u32"},{"name":"authorityBump","type":"u8"}]}},{"name":"RemoteTokenMessenger","type":{"kind":"struct","fields":[{"name":"domain","type":"u32"},{"name":"tokenMessenger","type":"publicKey"}]}},{"name":"TokenMinter","type":{"kind":"struct","fields":[{"name":"tokenController","type":"publicKey"},{"name":"pauser","type":"publicKey"},{"name":"paused","type":"bool"},{"name":"bump","type":"u8"}]}},{"name":"TokenPair","type":{"kind":"struct","fields":[{"name":"remoteDomain","type":"u32"},{"name":"remoteToken","type":"publicKey"},{"name":"localToken","type":"publicKey"},{"name":"bump","type":"u8"}]}},{"name":"LocalToken","type":{"kind":"struct","fields":[{"name":"custody","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"burnLimitPerMessage","type":"u64"},{"name":"messagesSent","type":"u64"},{"name":"messagesReceived","type":"u64"},{"name":"amountSent","type":"u128"},{"name":"amountReceived","type":"u128"},{"name":"bump","type":"u8"},{"name":"custodyBump","type":"u8"}]}}],"types":[{"name":"AcceptOwnershipParams","type":{"kind":"struct","fields":[]}},{"name":"AddRemoteTokenMessengerParams","type":{"kind":"struct","fields":[{"name":"domain","type":"u32"},{"name":"tokenMessenger","type":"publicKey"}]}},{"name":"DepositForBurnWithCallerParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"destinationDomain","type":"u32"},{"name":"mintRecipient","type":"publicKey"},{"name":"destinationCaller","type":"publicKey"}]}},{"name":"DepositForBurnParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"destinationDomain","type":"u32"},{"name":"mintRecipient","type":"publicKey"}]}},{"name":"HandleReceiveMessageParams","type":{"kind":"struct","fields":[{"name":"remoteDomain","type":"u32"},{"name":"sender","type":"publicKey"},{"name":"messageBody","type":"bytes"},{"name":"authorityBump","type":"u8"}]}},{"name":"InitializeParams","type":{"kind":"struct","fields":[{"name":"tokenController","type":"publicKey"},{"name":"localMessageTransmitter","type":"publicKey"},{"name":"messageBodyVersion","type":"u32"}]}},{"name":"RemoveRemoteTokenMessengerParams","type":{"kind":"struct","fields":[]}},{"name":"ReplaceDepositForBurnParams","type":{"kind":"struct","fields":[{"name":"originalMessage","type":"bytes"},{"name":"originalAttestation","type":"bytes"},{"name":"newDestinationCaller","type":"publicKey"},{"name":"newMintRecipient","type":"publicKey"}]}},{"name":"TransferOwnershipParams","type":{"kind":"struct","fields":[{"name":"newOwner","type":"publicKey"}]}},{"name":"AddLocalTokenParams","type":{"kind":"struct","fields":[]}},{"name":"BurnTokenCustodyParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"LinkTokenPairParams","type":{"kind":"struct","fields":[{"name":"localToken","type":"publicKey"},{"name":"remoteDomain","type":"u32"},{"name":"remoteToken","type":"publicKey"}]}},{"name":"PauseParams","type":{"kind":"struct","fields":[]}},{"name":"RemoveLocalTokenParams","type":{"kind":"struct","fields":[]}},{"name":"SetMaxBurnAmountPerMessageParams","type":{"kind":"struct","fields":[{"name":"burnLimitPerMessage","type":"u64"}]}},{"name":"SetTokenControllerParams","type":{"kind":"struct","fields":[{"name":"tokenController","type":"publicKey"}]}},{"name":"UninkTokenPairParams","type":{"kind":"struct","fields":[]}},{"name":"UnpauseParams","type":{"kind":"struct","fields":[]}},{"name":"UpdatePauserParams","type":{"kind":"struct","fields":[{"name":"newPauser","type":"publicKey"}]}},{"name":"TokenMinterError","type":{"kind":"enum","variants":[{"name":"InvalidAuthority"},{"name":"InvalidTokenMinterState"},{"name":"ProgramPaused"},{"name":"InvalidTokenPairState"},{"name":"InvalidLocalTokenState"},{"name":"InvalidPauser"},{"name":"InvalidTokenController"},{"name":"BurnAmountExceeded"},{"name":"InvalidAmount"}]}}],"events":[{"name":"OwnershipTransferStarted","fields":[{"name":"previousOwner","type":"publicKey","index":false},{"name":"newOwner","type":"publicKey","index":false}]},{"name":"OwnershipTransferred","fields":[{"name":"previousOwner","type":"publicKey","index":false},{"name":"newOwner","type":"publicKey","index":false}]},{"name":"DepositForBurn","fields":[{"name":"nonce","type":"u64","index":false},{"name":"burnToken","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"depositor","type":"publicKey","index":false},{"name":"mintRecipient","type":"publicKey","index":false},{"name":"destinationDomain","type":"u32","index":false},{"name":"destinationTokenMessenger","type":"publicKey","index":false},{"name":"destinationCaller","type":"publicKey","index":false}]},{"name":"MintAndWithdraw","fields":[{"name":"mintRecipient","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"mintToken","type":"publicKey","index":false}]},{"name":"RemoteTokenMessengerAdded","fields":[{"name":"domain","type":"u32","index":false},{"name":"tokenMessenger","type":"publicKey","index":false}]},{"name":"RemoteTokenMessengerRemoved","fields":[{"name":"domain","type":"u32","index":false},{"name":"tokenMessenger","type":"publicKey","index":false}]},{"name":"SetTokenController","fields":[{"name":"tokenController","type":"publicKey","index":false}]},{"name":"PauserChanged","fields":[{"name":"newAddress","type":"publicKey","index":false}]},{"name":"SetBurnLimitPerMessage","fields":[{"name":"token","type":"publicKey","index":false},{"name":"burnLimitPerMessage","type":"u64","index":false}]},{"name":"LocalTokenAdded","fields":[{"name":"custody","type":"publicKey","index":false},{"name":"mint","type":"publicKey","index":false}]},{"name":"LocalTokenRemoved","fields":[{"name":"custody","type":"publicKey","index":false},{"name":"mint","type":"publicKey","index":false}]},{"name":"TokenPairLinked","fields":[{"name":"localToken","type":"publicKey","index":false},{"name":"remoteDomain","type":"u32","index":false},{"name":"remoteToken","type":"publicKey","index":false}]},{"name":"TokenPairUnlinked","fields":[{"name":"localToken","type":"publicKey","index":false},{"name":"remoteDomain","type":"u32","index":false},{"name":"remoteToken","type":"publicKey","index":false}]},{"name":"Pause","fields":[]},{"name":"Unpause","fields":[]},{"name":"TokenCustodyBurned","fields":[{"name":"custodyTokenAccount","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"InvalidAuthority","msg":"Invalid authority"},{"code":6001,"name":"InvalidTokenMessengerState","msg":"Invalid token messenger state"},{"code":6002,"name":"InvalidTokenMessenger","msg":"Invalid token messenger"},{"code":6003,"name":"InvalidOwner","msg":"Invalid owner"},{"code":6004,"name":"MalformedMessage","msg":"Malformed message"},{"code":6005,"name":"InvalidMessageBodyVersion","msg":"Invalid message body version"},{"code":6006,"name":"InvalidAmount","msg":"Invalid amount"},{"code":6007,"name":"InvalidDestinationDomain","msg":"Invalid destination domain"},{"code":6008,"name":"InvalidDestinationCaller","msg":"Invalid destination caller"},{"code":6009,"name":"InvalidMintRecipient","msg":"Invalid mint recipient"},{"code":6010,"name":"InvalidSender","msg":"Invalid sender"},{"code":6011,"name":"InvalidTokenPair","msg":"Invalid token pair"},{"code":6012,"name":"InvalidTokenMint","msg":"Invalid token mint"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/cctp_v2.CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe.json b/migration/khadhroony-bot2-reference/idls/cctp_v2.CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe.json new file mode 100644 index 0000000..168bde5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/cctp_v2.CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe.json @@ -0,0 +1 @@ +{"address":"CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe","metadata":{"name":"token_messenger_minter_v2","version":"0.2.0","spec":"0.1.0","description":"Token Messenger and Minter for Cross-Chain Transfer Protocol V2","repository":"https://github.com/circlefin/solana-cctp-contracts"},"instructions":[{"name":"accept_ownership","discriminator":[172,23,43,13,238,213,85,150],"accounts":[{"name":"pending_owner","signer":true,"relations":["token_messenger"]},{"name":"token_messenger","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"AcceptOwnershipParams"}}}]},{"name":"add_local_token","discriminator":[213,199,205,18,98,124,73,198],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"token_controller","signer":true,"relations":["token_minter"]},{"name":"token_minter"},{"name":"local_token","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,111,99,97,108,95,116,111,107,101,110]},{"kind":"account","path":"local_token_mint"}]}},{"name":"custody_token_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,117,115,116,111,100,121]},{"kind":"account","path":"local_token_mint"}]}},{"name":"local_token_mint"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"AddLocalTokenParams"}}}]},{"name":"add_remote_token_messenger","discriminator":[12,149,172,165,111,202,24,33],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"owner","signer":true,"relations":["token_messenger"]},{"name":"token_messenger"},{"name":"remote_token_messenger","writable":true,"pda":{"seeds":[{"kind":"const","value":[114,101,109,111,116,101,95,116,111,107,101,110,95,109,101,115,115,101,110,103,101,114]},{"kind":"arg","path":"params.domain"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"AddRemoteTokenMessengerParams"}}}]},{"name":"burn_token_custody","discriminator":[233,136,180,175,112,41,62,71],"accounts":[{"name":"payee","writable":true,"signer":true},{"name":"token_controller","signer":true,"relations":["token_minter"]},{"name":"token_minter"},{"name":"local_token","pda":{"seeds":[{"kind":"const","value":[108,111,99,97,108,95,116,111,107,101,110]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}]}},{"name":"custody_token_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,117,115,116,111,100,121]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}]}},{"name":"custody_token_mint","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"BurnTokenCustodyParams"}}}]},{"name":"denylist_account","discriminator":[101,116,197,112,81,249,75,194],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"denylister","signer":true,"relations":["token_messenger"]},{"name":"token_messenger"},{"name":"denylist_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,110,121,108,105,115,116,95,97,99,99,111,117,110,116]},{"kind":"arg","path":"params.account"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"DenylistParams"}}}]},{"name":"deposit_for_burn","discriminator":[215,60,61,46,114,55,128,176],"accounts":[{"name":"owner","signer":true,"relations":["burn_token_account"]},{"name":"event_rent_payer","writable":true,"signer":true},{"name":"sender_authority_pda","pda":{"seeds":[{"kind":"const","value":[115,101,110,100,101,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"burn_token_account","writable":true},{"name":"denylist_account","docs":["Account is denylisted if the account exists at the expected PDA."],"pda":{"seeds":[{"kind":"const","value":[100,101,110,121,108,105,115,116,95,97,99,99,111,117,110,116]},{"kind":"account","path":"owner"}]}},{"name":"message_transmitter","writable":true},{"name":"token_messenger"},{"name":"remote_token_messenger"},{"name":"token_minter"},{"name":"local_token","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,111,99,97,108,95,116,111,107,101,110]},{"kind":"account","path":"burn_token_mint"}]}},{"name":"burn_token_mint","writable":true},{"name":"message_sent_event_data","writable":true,"signer":true},{"name":"message_transmitter_program","address":"CCTPV2Sm4AdWt5296sk4P66VBZ7bEhcARwFaaS9YPbeC"},{"name":"token_messenger_minter_program","address":"CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"DepositForBurnParams"}}}]},{"name":"deposit_for_burn_with_hook","discriminator":[111,245,62,131,204,108,223,155],"accounts":[{"name":"owner","signer":true,"relations":["burn_token_account"]},{"name":"event_rent_payer","writable":true,"signer":true},{"name":"sender_authority_pda","pda":{"seeds":[{"kind":"const","value":[115,101,110,100,101,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"burn_token_account","writable":true},{"name":"denylist_account","docs":["Account is denylisted if the account exists at the expected PDA."],"pda":{"seeds":[{"kind":"const","value":[100,101,110,121,108,105,115,116,95,97,99,99,111,117,110,116]},{"kind":"account","path":"owner"}]}},{"name":"message_transmitter","writable":true},{"name":"token_messenger"},{"name":"remote_token_messenger"},{"name":"token_minter"},{"name":"local_token","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,111,99,97,108,95,116,111,107,101,110]},{"kind":"account","path":"burn_token_mint"}]}},{"name":"burn_token_mint","writable":true},{"name":"message_sent_event_data","writable":true,"signer":true},{"name":"message_transmitter_program","address":"CCTPV2Sm4AdWt5296sk4P66VBZ7bEhcARwFaaS9YPbeC"},{"name":"token_messenger_minter_program","address":"CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"DepositForBurnWithHookParams"}}}]},{"name":"handle_receive_finalized_message","discriminator":[186,252,239,70,86,180,110,95],"accounts":[{"name":"authority_pda","signer":true,"pda":{"seeds":[{"kind":"const","value":[109,101,115,115,97,103,101,95,116,114,97,110,115,109,105,116,116,101,114,95,97,117,116,104,111,114,105,116,121]},{"kind":"const","value":[166,95,200,29,15,239,168,134,12,179,184,63,8,155,2,36,190,138,102,135,183,174,73,245,148,192,185,180,215,233,56,147]}],"program":{"kind":"const","value":[166,95,200,28,225,158,220,210,210,195,64,176,47,166,27,225,213,186,221,225,89,40,51,221,249,32,9,216,207,104,84,85]}}},{"name":"token_messenger"},{"name":"remote_token_messenger"},{"name":"token_minter"},{"name":"local_token","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,111,99,97,108,95,116,111,107,101,110]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}]}},{"name":"token_pair"},{"name":"fee_recipient_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"token_messenger.fee_recipient","account":"TokenMessenger"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"recipient_token_account","writable":true},{"name":"custody_token_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,117,115,116,111,100,121]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}]}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"HandleReceiveMessageParams"}}}]},{"name":"handle_receive_unfinalized_message","discriminator":[200,169,175,20,200,58,182,61],"accounts":[{"name":"authority_pda","signer":true,"pda":{"seeds":[{"kind":"const","value":[109,101,115,115,97,103,101,95,116,114,97,110,115,109,105,116,116,101,114,95,97,117,116,104,111,114,105,116,121]},{"kind":"const","value":[166,95,200,29,15,239,168,134,12,179,184,63,8,155,2,36,190,138,102,135,183,174,73,245,148,192,185,180,215,233,56,147]}],"program":{"kind":"const","value":[166,95,200,28,225,158,220,210,210,195,64,176,47,166,27,225,213,186,221,225,89,40,51,221,249,32,9,216,207,104,84,85]}}},{"name":"token_messenger"},{"name":"remote_token_messenger"},{"name":"token_minter"},{"name":"local_token","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,111,99,97,108,95,116,111,107,101,110]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}]}},{"name":"token_pair"},{"name":"fee_recipient_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"token_messenger.fee_recipient","account":"TokenMessenger"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"recipient_token_account","writable":true},{"name":"custody_token_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,117,115,116,111,100,121]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}]}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"HandleReceiveMessageParams"}}}]},{"name":"initialize","discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"upgrade_authority","signer":true},{"name":"authority_pda","pda":{"seeds":[{"kind":"const","value":[115,101,110,100,101,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"token_messenger","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,109,101,115,115,101,110,103,101,114]}]}},{"name":"token_minter","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,109,105,110,116,101,114]}]}},{"name":"token_messenger_minter_program_data"},{"name":"token_messenger_minter_program","address":"CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"InitializeParams"}}}]},{"name":"link_token_pair","discriminator":[68,162,24,104,125,46,130,12],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"token_controller","signer":true,"relations":["token_minter"]},{"name":"token_minter"},{"name":"token_pair","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,112,97,105,114]},{"kind":"arg","path":"params.remote_domain"},{"kind":"arg","path":"params.remote_token"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"LinkTokenPairParams"}}}]},{"name":"pause","discriminator":[211,22,221,251,74,121,193,47],"accounts":[{"name":"pauser","signer":true,"relations":["token_minter"]},{"name":"token_minter","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"PauseParams"}}}]},{"name":"remove_local_token","discriminator":[27,43,66,170,188,44,109,97],"accounts":[{"name":"payee","writable":true,"signer":true},{"name":"token_controller","signer":true,"relations":["token_minter"]},{"name":"token_minter"},{"name":"local_token","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,111,99,97,108,95,116,111,107,101,110]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}]}},{"name":"custody_token_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,117,115,116,111,100,121]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}]}},{"name":"custody_token_mint","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"RemoveLocalTokenParams"}}}]},{"name":"remove_remote_token_messenger","discriminator":[65,114,66,85,169,98,177,146],"accounts":[{"name":"payee","writable":true,"signer":true},{"name":"owner","signer":true,"relations":["token_messenger"]},{"name":"token_messenger"},{"name":"remote_token_messenger","writable":true,"pda":{"seeds":[{"kind":"const","value":[114,101,109,111,116,101,95,116,111,107,101,110,95,109,101,115,115,101,110,103,101,114]},{"kind":"account","path":"remote_token_messenger.domain","account":"RemoteTokenMessenger"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"RemoveRemoteTokenMessengerParams"}}}]},{"name":"set_fee_recipient","discriminator":[227,18,215,42,237,246,151,66],"accounts":[{"name":"owner","signer":true,"relations":["token_messenger"]},{"name":"token_messenger","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SetFeeRecipientParams"}}}]},{"name":"set_max_burn_amount_per_message","discriminator":[30,128,145,240,70,237,109,207],"accounts":[{"name":"token_controller","signer":true,"relations":["token_minter"]},{"name":"token_minter"},{"name":"local_token","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,111,99,97,108,95,116,111,107,101,110]},{"kind":"account","path":"local_token.mint","account":"LocalToken"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SetMaxBurnAmountPerMessageParams"}}}]},{"name":"set_min_fee","discriminator":[114,198,35,3,41,196,194,246],"accounts":[{"name":"min_fee_controller","signer":true,"relations":["token_messenger"]},{"name":"token_messenger","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SetMinFeeParams"}}}]},{"name":"set_min_fee_controller","discriminator":[195,142,74,84,234,94,180,113],"accounts":[{"name":"owner","signer":true,"relations":["token_messenger"]},{"name":"token_messenger","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SetMinFeeControllerParams"}}}]},{"name":"set_token_controller","discriminator":[88,6,98,10,79,59,15,24],"accounts":[{"name":"owner","signer":true,"relations":["token_messenger"]},{"name":"token_messenger"},{"name":"token_minter","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SetTokenControllerParams"}}}]},{"name":"transfer_ownership","discriminator":[65,177,215,73,53,45,99,47],"accounts":[{"name":"owner","signer":true,"relations":["token_messenger"]},{"name":"token_messenger","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"TransferOwnershipParams"}}}]},{"name":"undenylist_account","discriminator":[57,36,43,168,62,172,33,39],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"denylister","signer":true,"relations":["token_messenger"]},{"name":"token_messenger"},{"name":"denylist_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,110,121,108,105,115,116,95,97,99,99,111,117,110,116]},{"kind":"arg","path":"params.account"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"UndenylistParams"}}}]},{"name":"unlink_token_pair","discriminator":[52,198,100,114,104,174,85,58],"accounts":[{"name":"payee","writable":true,"signer":true},{"name":"token_controller","signer":true,"relations":["token_minter"]},{"name":"token_minter"},{"name":"token_pair","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,112,97,105,114]},{"kind":"account","path":"token_pair.remote_domain","account":"TokenPair"},{"kind":"account","path":"token_pair.remote_token","account":"TokenPair"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"UninkTokenPairParams"}}}]},{"name":"unpause","discriminator":[169,144,4,38,10,141,188,255],"accounts":[{"name":"pauser","signer":true,"relations":["token_minter"]},{"name":"token_minter","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"UnpauseParams"}}}]},{"name":"update_denylister","discriminator":[193,66,198,201,84,57,14,222],"accounts":[{"name":"owner","signer":true,"relations":["token_messenger"]},{"name":"token_messenger","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"UpdateDenylisterParams"}}}]},{"name":"update_pauser","discriminator":[140,171,211,132,57,201,16,254],"accounts":[{"name":"owner","signer":true,"relations":["token_messenger"]},{"name":"token_messenger"},{"name":"token_minter","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"UpdatePauserParams"}}}]}],"accounts":[{"name":"DenylistedAccount","discriminator":[186,58,212,239,102,131,157,146]},{"name":"LocalToken","discriminator":[159,131,58,170,193,84,128,182]},{"name":"MessageTransmitter","discriminator":[71,40,180,142,19,203,35,252]},{"name":"RemoteTokenMessenger","discriminator":[105,115,174,34,95,233,138,252]},{"name":"TokenMessenger","discriminator":[162,4,242,52,147,243,221,96]},{"name":"TokenMinter","discriminator":[122,133,84,63,57,159,171,206]},{"name":"TokenPair","discriminator":[17,214,45,176,229,149,197,71]}],"events":[{"name":"Denylisted","discriminator":[20,145,173,200,182,17,234,154]},{"name":"DenylisterChanged","discriminator":[249,170,81,180,185,175,138,72]},{"name":"DepositForBurn","discriminator":[144,252,145,146,6,74,167,235]},{"name":"FeeRecipientSet","discriminator":[99,140,80,35,245,176,179,110]},{"name":"LocalTokenAdded","discriminator":[146,8,224,150,122,173,23,39]},{"name":"LocalTokenRemoved","discriminator":[181,204,1,95,2,50,66,210]},{"name":"MinFeeControllerSet","discriminator":[239,12,122,105,231,114,13,196]},{"name":"MinFeeSet","discriminator":[60,127,101,230,216,129,188,98]},{"name":"MintAndWithdraw","discriminator":[75,67,229,70,162,126,0,71]},{"name":"OwnershipTransferStarted","discriminator":[183,253,239,246,140,179,133,105]},{"name":"OwnershipTransferred","discriminator":[172,61,205,183,250,50,38,98]},{"name":"Pause","discriminator":[194,251,232,196,118,95,111,219]},{"name":"PauserChanged","discriminator":[142,157,158,87,127,8,119,55]},{"name":"RemoteTokenMessengerAdded","discriminator":[251,29,63,244,48,114,210,175]},{"name":"RemoteTokenMessengerRemoved","discriminator":[255,121,137,39,230,125,11,30]},{"name":"SetBurnLimitPerMessage","discriminator":[98,152,88,191,245,30,27,209]},{"name":"SetTokenController","discriminator":[193,44,243,83,230,72,120,216]},{"name":"TokenCustodyBurned","discriminator":[219,143,107,226,67,75,178,46]},{"name":"TokenPairLinked","discriminator":[2,14,177,64,155,93,196,141]},{"name":"TokenPairUnlinked","discriminator":[78,232,230,208,180,212,246,72]},{"name":"UnDenylisted","discriminator":[150,39,227,20,162,180,5,242]},{"name":"Unpause","discriminator":[241,149,104,90,199,136,219,146]}],"errors":[{"code":6000,"name":"InvalidAuthority","msg":"Invalid authority"},{"code":6001,"name":"InvalidTokenMinterState","msg":"Invalid token minter state"},{"code":6002,"name":"ProgramPaused","msg":"Instruction is not allowed at this time"},{"code":6003,"name":"InvalidTokenPairState","msg":"Invalid token pair state"},{"code":6004,"name":"InvalidLocalTokenState","msg":"Invalid local token state"},{"code":6005,"name":"InvalidPauser","msg":"Invalid pauser"},{"code":6006,"name":"InvalidTokenController","msg":"Invalid token controller"},{"code":6007,"name":"BurnAmountExceeded","msg":"Burn amount exceeded"},{"code":6008,"name":"InvalidAmount","msg":"Invalid amount"}],"types":[{"name":"AcceptOwnershipParams","type":{"kind":"struct","fields":[]}},{"name":"AddLocalTokenParams","type":{"kind":"struct","fields":[]}},{"name":"AddRemoteTokenMessengerParams","type":{"kind":"struct","fields":[{"name":"domain","type":"u32"},{"name":"token_messenger","type":"pubkey"}]}},{"name":"BurnTokenCustodyParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"DenylistParams","type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"}]}},{"name":"Denylisted","type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"}]}},{"name":"DenylistedAccount","type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"}]}},{"name":"DenylisterChanged","type":{"kind":"struct","fields":[{"name":"old_denylister","type":"pubkey"},{"name":"new_denylister","type":"pubkey"}]}},{"name":"DepositForBurn","type":{"kind":"struct","fields":[{"name":"burn_token","type":"pubkey"},{"name":"amount","type":"u64"},{"name":"depositor","type":"pubkey"},{"name":"mint_recipient","type":"pubkey"},{"name":"destination_domain","type":"u32"},{"name":"destination_token_messenger","type":"pubkey"},{"name":"destination_caller","type":"pubkey"},{"name":"max_fee","type":"u64"},{"name":"min_finality_threshold","type":"u32"},{"name":"hook_data","type":"bytes"}]}},{"name":"DepositForBurnParams","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"destination_domain","type":"u32"},{"name":"mint_recipient","type":"pubkey"},{"name":"destination_caller","type":"pubkey"},{"name":"max_fee","type":"u64"},{"name":"min_finality_threshold","type":"u32"}]}},{"name":"DepositForBurnWithHookParams","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"destination_domain","type":"u32"},{"name":"mint_recipient","type":"pubkey"},{"name":"destination_caller","type":"pubkey"},{"name":"max_fee","type":"u64"},{"name":"min_finality_threshold","type":"u32"},{"name":"hook_data","type":"bytes"}]}},{"name":"FeeRecipientSet","type":{"kind":"struct","fields":[{"name":"new_fee_recipient","type":"pubkey"}]}},{"name":"HandleReceiveMessageParams","type":{"kind":"struct","fields":[{"name":"remote_domain","type":"u32"},{"name":"sender","type":"pubkey"},{"name":"finality_threshold_executed","type":"u32"},{"name":"message_body","type":"bytes"},{"name":"authority_bump","type":"u8"}]}},{"name":"InitializeParams","type":{"kind":"struct","fields":[{"name":"token_controller","type":"pubkey"},{"name":"denylister","type":"pubkey"},{"name":"fee_recipient","type":"pubkey"},{"name":"min_fee_controller","type":"pubkey"},{"name":"min_fee","type":"u32"},{"name":"message_body_version","type":"u32"}]}},{"name":"LinkTokenPairParams","type":{"kind":"struct","fields":[{"name":"local_token","type":"pubkey"},{"name":"remote_domain","type":"u32"},{"name":"remote_token","type":"pubkey"}]}},{"name":"LocalToken","type":{"kind":"struct","fields":[{"name":"custody","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"burn_limit_per_message","type":"u64"},{"name":"messages_sent","type":"u64"},{"name":"messages_received","type":"u64"},{"name":"amount_sent","type":"u128"},{"name":"amount_received","type":"u128"},{"name":"bump","type":"u8"},{"name":"custody_bump","type":"u8"}]}},{"name":"LocalTokenAdded","type":{"kind":"struct","fields":[{"name":"custody","type":"pubkey"},{"name":"mint","type":"pubkey"}]}},{"name":"LocalTokenRemoved","type":{"kind":"struct","fields":[{"name":"custody","type":"pubkey"},{"name":"mint","type":"pubkey"}]}},{"name":"MessageTransmitter","docs":["Main state of the MessageTransmitter program"],"type":{"kind":"struct","fields":[{"name":"owner","type":"pubkey"},{"name":"pending_owner","type":"pubkey"},{"name":"attester_manager","type":"pubkey"},{"name":"pauser","type":"pubkey"},{"name":"paused","type":"bool"},{"name":"local_domain","type":"u32"},{"name":"version","type":"u32"},{"name":"signature_threshold","type":"u32"},{"name":"enabled_attesters","type":{"vec":"pubkey"}},{"name":"max_message_body_size","type":"u64"}]}},{"name":"MinFeeControllerSet","type":{"kind":"struct","fields":[{"name":"new_min_fee_controller","type":"pubkey"}]}},{"name":"MinFeeSet","type":{"kind":"struct","fields":[{"name":"new_min_fee","type":"u32"}]}},{"name":"MintAndWithdraw","type":{"kind":"struct","fields":[{"name":"mint_recipient","type":"pubkey"},{"name":"amount","type":"u64"},{"name":"mint_token","type":"pubkey"},{"name":"fee_collected","type":"u64"}]}},{"name":"OwnershipTransferStarted","type":{"kind":"struct","fields":[{"name":"previous_owner","type":"pubkey"},{"name":"new_owner","type":"pubkey"}]}},{"name":"OwnershipTransferred","type":{"kind":"struct","fields":[{"name":"previous_owner","type":"pubkey"},{"name":"new_owner","type":"pubkey"}]}},{"name":"Pause","type":{"kind":"struct","fields":[]}},{"name":"PauseParams","type":{"kind":"struct","fields":[]}},{"name":"PauserChanged","type":{"kind":"struct","fields":[{"name":"new_address","type":"pubkey"}]}},{"name":"RemoteTokenMessenger","type":{"kind":"struct","fields":[{"name":"domain","type":"u32"},{"name":"token_messenger","type":"pubkey"}]}},{"name":"RemoteTokenMessengerAdded","type":{"kind":"struct","fields":[{"name":"domain","type":"u32"},{"name":"token_messenger","type":"pubkey"}]}},{"name":"RemoteTokenMessengerRemoved","type":{"kind":"struct","fields":[{"name":"domain","type":"u32"},{"name":"token_messenger","type":"pubkey"}]}},{"name":"RemoveLocalTokenParams","type":{"kind":"struct","fields":[]}},{"name":"RemoveRemoteTokenMessengerParams","type":{"kind":"struct","fields":[]}},{"name":"SetBurnLimitPerMessage","type":{"kind":"struct","fields":[{"name":"token","type":"pubkey"},{"name":"burn_limit_per_message","type":"u64"}]}},{"name":"SetFeeRecipientParams","type":{"kind":"struct","fields":[{"name":"new_fee_recipient","type":"pubkey"}]}},{"name":"SetMaxBurnAmountPerMessageParams","type":{"kind":"struct","fields":[{"name":"burn_limit_per_message","type":"u64"}]}},{"name":"SetMinFeeControllerParams","type":{"kind":"struct","fields":[{"name":"new_min_fee_controller","type":"pubkey"}]}},{"name":"SetMinFeeParams","type":{"kind":"struct","fields":[{"name":"new_min_fee","type":"u32"}]}},{"name":"SetTokenController","type":{"kind":"struct","fields":[{"name":"token_controller","type":"pubkey"}]}},{"name":"SetTokenControllerParams","type":{"kind":"struct","fields":[{"name":"token_controller","type":"pubkey"}]}},{"name":"TokenCustodyBurned","type":{"kind":"struct","fields":[{"name":"custody_token_account","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"TokenMessenger","type":{"kind":"struct","fields":[{"name":"denylister","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"pending_owner","type":"pubkey"},{"name":"message_body_version","type":"u32"},{"name":"authority_bump","type":"u8"},{"name":"fee_recipient","type":"pubkey"},{"name":"min_fee_controller","type":"pubkey"},{"name":"min_fee","type":"u32"}]}},{"name":"TokenMinter","type":{"kind":"struct","fields":[{"name":"token_controller","type":"pubkey"},{"name":"pauser","type":"pubkey"},{"name":"paused","type":"bool"},{"name":"bump","type":"u8"}]}},{"name":"TokenPair","type":{"kind":"struct","fields":[{"name":"remote_domain","type":"u32"},{"name":"remote_token","type":"pubkey"},{"name":"local_token","type":"pubkey"},{"name":"bump","type":"u8"}]}},{"name":"TokenPairLinked","type":{"kind":"struct","fields":[{"name":"local_token","type":"pubkey"},{"name":"remote_domain","type":"u32"},{"name":"remote_token","type":"pubkey"}]}},{"name":"TokenPairUnlinked","type":{"kind":"struct","fields":[{"name":"local_token","type":"pubkey"},{"name":"remote_domain","type":"u32"},{"name":"remote_token","type":"pubkey"}]}},{"name":"TransferOwnershipParams","type":{"kind":"struct","fields":[{"name":"new_owner","type":"pubkey"}]}},{"name":"UnDenylisted","type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"}]}},{"name":"UndenylistParams","type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"}]}},{"name":"UninkTokenPairParams","type":{"kind":"struct","fields":[]}},{"name":"Unpause","type":{"kind":"struct","fields":[]}},{"name":"UnpauseParams","type":{"kind":"struct","fields":[]}},{"name":"UpdateDenylisterParams","type":{"kind":"struct","fields":[{"name":"new_denylister","type":"pubkey"}]}},{"name":"UpdatePauserParams","type":{"kind":"struct","fields":[{"name":"new_pauser","type":"pubkey"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/clone.C1onEW2kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr.json b/migration/khadhroony-bot2-reference/idls/clone.C1onEW2kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr.json new file mode 100644 index 0000000..9f57bed --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/clone.C1onEW2kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"clone","instructions":[{"name":"initializeClone","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"clone","isMut":true,"isSigner":false},{"name":"collateralMint","isMut":false,"isSigner":false},{"name":"collateralVault","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"cometCollateralIldLiquidatorFeeBps","type":"u16"},{"name":"cometOnassetIldLiquidatorFeeBps","type":"u16"},{"name":"borrowLiquidatorFeeBps","type":"u16"},{"name":"treasuryAddress","type":"publicKey"},{"name":"collateralOracleIndex","type":"u8"},{"name":"collateralizationRatio","type":"u8"}]},{"name":"initializePools","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"clone","isMut":false,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeOracles","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"clone","isMut":false,"isSigner":false},{"name":"oracles","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateCloneParameters","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"clone","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"CloneParameters"}}]},{"name":"updatePoolParameters","accounts":[{"name":"auth","isMut":false,"isSigner":true},{"name":"clone","isMut":false,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false}],"args":[{"name":"index","type":"u8"},{"name":"params","type":{"defined":"PoolParameters"}}]},{"name":"updateOracles","accounts":[{"name":"auth","isMut":false,"isSigner":true},{"name":"clone","isMut":false,"isSigner":false},{"name":"oracles","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"UpdateOracleParameters"}}]},{"name":"initializeUser","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"authority","type":"publicKey"}]},{"name":"addPool","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"clone","isMut":false,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":false,"isSigner":false},{"name":"onassetTokenAccount","isMut":false,"isSigner":false},{"name":"underlyingAssetMint","isMut":false,"isSigner":false},{"name":"underlyingAssetTokenAccount","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"minOvercollateralRatio","type":"u16"},{"name":"maxLiquidationOvercollateralRatio","type":"u16"},{"name":"liquidityTradingFeeBps","type":"u16"},{"name":"treasuryTradingFeeBps","type":"u16"},{"name":"ilHealthScoreCoefficient","type":"u16"},{"name":"positionHealthScoreCoefficient","type":"u16"},{"name":"oracleInfoIndex","type":"u8"}]},{"name":"updatePrices","accounts":[{"name":"oracles","isMut":true,"isSigner":false}],"args":[{"name":"oracleIndices","type":"bytes"}]},{"name":"initializeBorrowPosition","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"oracles","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"userCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"userOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"poolIndex","type":"u8"},{"name":"onassetAmount","type":"u64"},{"name":"collateralAmount","type":"u64"}]},{"name":"addCollateralToBorrow","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":false,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"userCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"borrowIndex","type":"u8"},{"name":"amount","type":"u64"}]},{"name":"withdrawCollateralFromBorrow","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":false,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"oracles","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"userCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"borrowIndex","type":"u8"},{"name":"amount","type":"u64"}]},{"name":"payBorrowDebt","accounts":[{"name":"payer","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":false,"isSigner":false},{"name":"payerOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"user","type":"publicKey"},{"name":"borrowIndex","type":"u8"},{"name":"amount","type":"u64"}]},{"name":"borrowMore","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":false,"isSigner":false},{"name":"oracles","isMut":false,"isSigner":false},{"name":"userOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"borrowIndex","type":"u8"},{"name":"amount","type":"u64"}]},{"name":"addCollateralToComet","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"userCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"}]},{"name":"withdrawCollateralFromComet","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"oracles","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"userCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"}]},{"name":"addLiquidityToComet","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"oracles","isMut":true,"isSigner":false}],"args":[{"name":"poolIndex","type":"u8"},{"name":"collateralAmount","type":"u64"}]},{"name":"withdrawLiquidityFromComet","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"oracles","isMut":true,"isSigner":false}],"args":[{"name":"cometPositionIndex","type":"u8"},{"name":"amount","type":"u64"}]},{"name":"liquidateCometCollateralIld","accounts":[{"name":"liquidator","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":false,"isSigner":false},{"name":"oracles","isMut":false,"isSigner":false},{"name":"collateralMint","isMut":true,"isSigner":false},{"name":"liquidatorCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"user","type":"publicKey"},{"name":"cometPositionIndex","type":"u8"}]},{"name":"liquidateCometOnassetIld","accounts":[{"name":"liquidator","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":false,"isSigner":false},{"name":"oracles","isMut":false,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"liquidatorOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"liquidatorCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"user","type":"publicKey"},{"name":"cometPositionIndex","type":"u8"},{"name":"amount","type":"u64"}]},{"name":"liquidateBorrowPosition","accounts":[{"name":"liquidator","isMut":false,"isSigner":true},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"oracles","isMut":true,"isSigner":false},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"liquidatorCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"liquidatorOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"user","type":"publicKey"},{"name":"borrowIndex","type":"u8"},{"name":"amount","type":"u64"}]},{"name":"collectLpRewards","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":false,"isSigner":false},{"name":"collateralVault","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"userCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"userOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"cometPositionIndex","type":"u8"}]},{"name":"payImpermanentLossDebt","accounts":[{"name":"payer","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":false,"isSigner":false},{"name":"collateralMint","isMut":false,"isSigner":false},{"name":"collateralVault","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"payerCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"payerOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"user","type":"publicKey"},{"name":"cometPositionIndex","type":"u8"},{"name":"amount","type":"u64"},{"name":"paymentType","type":{"defined":"PaymentType"}}]},{"name":"closeUserAccount","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"destination","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"wrapAsset","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"clone","isMut":false,"isSigner":false},{"name":"pools","isMut":false,"isSigner":false},{"name":"underlyingAssetTokenAccount","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"userAssetTokenAccount","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"userOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"poolIndex","type":"u8"}]},{"name":"unwrapOnasset","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"clone","isMut":false,"isSigner":false},{"name":"pools","isMut":false,"isSigner":false},{"name":"underlyingAssetTokenAccount","isMut":true,"isSigner":false},{"name":"assetMint","isMut":false,"isSigner":false},{"name":"userAssetTokenAccount","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"userOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"poolIndex","type":"u8"}]},{"name":"removeCometPosition","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"userAccount","isMut":true,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false}],"args":[{"name":"cometPositionIndex","type":"u8"}]},{"name":"swap","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"clone","isMut":true,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"oracles","isMut":true,"isSigner":false},{"name":"userCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"userOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"onassetMint","isMut":true,"isSigner":false},{"name":"collateralMint","isMut":true,"isSigner":false},{"name":"collateralVault","isMut":true,"isSigner":false},{"name":"treasuryOnassetTokenAccount","isMut":true,"isSigner":false},{"name":"treasuryCollateralTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"cloneStaking","isMut":false,"isSigner":false,"isOptional":true},{"name":"userStakingAccount","isMut":false,"isSigner":false,"isOptional":true},{"name":"cloneStakingProgram","isMut":false,"isSigner":false,"isOptional":true}],"args":[{"name":"poolIndex","type":"u8"},{"name":"quantity","type":"u64"},{"name":"quantityIsInput","type":"bool"},{"name":"quantityIsCollateral","type":"bool"},{"name":"resultThreshold","type":"u64"}]},{"name":"createTokenMetadata","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"clone","isMut":false,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"metaplexProgram","isMut":false,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"metadataArgs","type":{"defined":"MetadataArgs"}}]},{"name":"removePool","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"clone","isMut":false,"isSigner":false},{"name":"pools","isMut":true,"isSigner":false},{"name":"underlyingAssetMint","isMut":false,"isSigner":false},{"name":"underlyingAssetTokenAccount","isMut":true,"isSigner":false},{"name":"treasuryAssetTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"poolIndex","type":"u8"}]}],"accounts":[{"name":"Clone","type":{"kind":"struct","fields":[{"name":"admin","type":"publicKey"},{"name":"auth","type":{"array":["publicKey",10]}},{"name":"bump","type":"u8"},{"name":"collateral","type":{"defined":"Collateral"}},{"name":"cometCollateralIldLiquidatorFeeBps","type":"u16"},{"name":"cometOnassetIldLiquidatorFeeBps","type":"u16"},{"name":"borrowLiquidatorFeeBps","type":"u16"},{"name":"treasuryAddress","type":"publicKey"},{"name":"eventCounter","type":"u64"},{"name":"nonAuthLiquidationsEnabled","type":"bool"}]}},{"name":"Pools","type":{"kind":"struct","fields":[{"name":"pools","type":{"vec":{"defined":"Pool"}}}]}},{"name":"Oracles","type":{"kind":"struct","fields":[{"name":"oracles","type":{"vec":{"defined":"OracleInfo"}}}]}},{"name":"User","type":{"kind":"struct","fields":[{"name":"borrows","type":{"vec":{"defined":"Borrow"}}},{"name":"comet","type":{"defined":"Comet"}}]}}],"types":[{"name":"MetadataArgs","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]}},{"name":"AssetInfo","type":{"kind":"struct","fields":[{"name":"onassetMint","type":"publicKey"},{"name":"oracleInfoIndex","type":"u8"},{"name":"ilHealthScoreCoefficient","type":"u16"},{"name":"positionHealthScoreCoefficient","type":"u16"},{"name":"minOvercollateralRatio","type":"u16"},{"name":"maxLiquidationOvercollateralRatio","type":"u16"}]}},{"name":"OracleInfo","type":{"kind":"struct","fields":[{"name":"source","type":{"defined":"OracleSource"}},{"name":"address","type":"publicKey"},{"name":"price","type":"i64"},{"name":"expo","type":"u8"},{"name":"status","type":{"defined":"Status"}},{"name":"lastUpdateSlot","type":"u64"},{"name":"rescaleFactor","type":"u8"}]}},{"name":"Pool","type":{"kind":"struct","fields":[{"name":"underlyingAssetTokenAccount","type":"publicKey"},{"name":"committedCollateralLiquidity","type":"u64"},{"name":"collateralIld","type":"i64"},{"name":"onassetIld","type":"i64"},{"name":"treasuryTradingFeeBps","type":"u16"},{"name":"liquidityTradingFeeBps","type":"u16"},{"name":"assetInfo","type":{"defined":"AssetInfo"}},{"name":"status","type":{"defined":"Status"}}]}},{"name":"Collateral","type":{"kind":"struct","fields":[{"name":"oracleInfoIndex","type":"u8"},{"name":"mint","type":"publicKey"},{"name":"vault","type":"publicKey"},{"name":"collateralizationRatio","type":"u8"},{"name":"scale","type":"u8"}]}},{"name":"Comet","type":{"kind":"struct","fields":[{"name":"collateralAmount","type":"u64"},{"name":"positions","type":{"vec":{"defined":"LiquidityPosition"}}}]}},{"name":"LiquidityPosition","type":{"kind":"struct","fields":[{"name":"poolIndex","type":"u8"},{"name":"committedCollateralLiquidity","type":"u64"},{"name":"collateralIldRebate","type":"i64"},{"name":"onassetIldRebate","type":"i64"}]}},{"name":"Borrow","type":{"kind":"struct","fields":[{"name":"poolIndex","type":"u8"},{"name":"borrowedOnasset","type":"u64"},{"name":"collateralAmount","type":"u64"}]}},{"name":"PaymentType","type":{"kind":"enum","variants":[{"name":"Onasset"},{"name":"Collateral"},{"name":"CollateralFromWallet"}]}},{"name":"CloneParameters","type":{"kind":"enum","variants":[{"name":"AddAuth","fields":[{"name":"address","type":"publicKey"}]},{"name":"RemoveAuth","fields":[{"name":"address","type":"publicKey"}]},{"name":"CometCollateralLiquidationFee","fields":[{"name":"value","type":"u16"}]},{"name":"CometOnassetLiquidationFee","fields":[{"name":"value","type":"u16"}]},{"name":"BorrowLiquidationFee","fields":[{"name":"value","type":"u16"}]},{"name":"TreasuryAddress","fields":[{"name":"address","type":"publicKey"}]},{"name":"CollateralizationRatio","fields":[{"name":"value","type":"u8"}]},{"name":"NonAuthLiquidationsEnabled","fields":[{"name":"value","type":"bool"}]}]}},{"name":"UpdateOracleParameters","type":{"kind":"enum","variants":[{"name":"Add","fields":[{"name":"address","type":"publicKey"},{"name":"source","type":{"defined":"OracleSource"}},{"name":"rescale_factor","type":{"option":"u8"}}]},{"name":"Remove","fields":[{"name":"index","type":"u8"}]},{"name":"Modify","fields":[{"name":"index","type":"u8"},{"name":"address","type":{"option":"publicKey"}},{"name":"source","type":{"option":{"defined":"OracleSource"}}},{"name":"status","type":{"option":{"defined":"Status"}}}]}]}},{"name":"PoolParameters","type":{"kind":"enum","variants":[{"name":"Status","fields":[{"name":"value","type":{"defined":"Status"}}]},{"name":"TreasuryTradingFee","fields":[{"name":"value","type":"u16"}]},{"name":"LiquidityTradingFee","fields":[{"name":"value","type":"u16"}]},{"name":"OracleInfoIndex","fields":[{"name":"value","type":"u8"}]},{"name":"MinOvercollateralRatio","fields":[{"name":"value","type":"u16"}]},{"name":"MaxLiquidationOvercollateralRatio","fields":[{"name":"value","type":"u16"}]},{"name":"IlHealthScoreCoefficient","fields":[{"name":"value","type":"u16"}]},{"name":"PositionHealthScoreCoefficient","fields":[{"name":"value","type":"u16"}]}]}},{"name":"Status","type":{"kind":"enum","variants":[{"name":"Active"},{"name":"Frozen"},{"name":"Extraction"},{"name":"Liquidation"},{"name":"Deprecation"}]}},{"name":"OracleSource","type":{"kind":"enum","variants":[{"name":"PYTH"},{"name":"SWITCHBOARD"}]}}],"events":[{"name":"SwapEvent","fields":[{"name":"eventId","type":"u64","index":false},{"name":"userAddress","type":"publicKey","index":false},{"name":"poolIndex","type":"u8","index":false},{"name":"inputIsCollateral","type":"bool","index":false},{"name":"input","type":"u64","index":false},{"name":"output","type":"u64","index":false},{"name":"tradingFee","type":"u64","index":false},{"name":"treasuryFee","type":"u64","index":false}]},{"name":"LiquidityDelta","fields":[{"name":"eventId","type":"u64","index":false},{"name":"userAddress","type":"publicKey","index":false},{"name":"poolIndex","type":"u8","index":false},{"name":"committedCollateralDelta","type":"i64","index":false},{"name":"collateralIldDelta","type":"i64","index":false},{"name":"onassetIldDelta","type":"i64","index":false}]},{"name":"PoolState","fields":[{"name":"eventId","type":"u64","index":false},{"name":"poolIndex","type":"u8","index":false},{"name":"onassetIld","type":"i64","index":false},{"name":"collateralIld","type":"i64","index":false},{"name":"committedCollateralLiquidity","type":"u64","index":false},{"name":"poolPrice","type":"u64","index":false},{"name":"poolScale","type":"u32","index":false}]},{"name":"BorrowUpdate","fields":[{"name":"eventId","type":"u64","index":false},{"name":"poolIndex","type":"u8","index":false},{"name":"userAddress","type":"publicKey","index":false},{"name":"isLiquidation","type":"bool","index":false},{"name":"collateralSupplied","type":"u64","index":false},{"name":"collateralDelta","type":"i64","index":false},{"name":"borrowedAmount","type":"u64","index":false},{"name":"borrowedDelta","type":"i64","index":false}]},{"name":"CometCollateralUpdate","fields":[{"name":"eventId","type":"u64","index":false},{"name":"userAddress","type":"publicKey","index":false},{"name":"collateralSupplied","type":"u64","index":false},{"name":"collateralDelta","type":"i64","index":false}]}],"errors":[{"code":6000,"name":"Unauthorized","msg":"Unauthorized"},{"code":6001,"name":"InvalidMintCollateralRatio","msg":"Invalid Mint Collateral Ratio"},{"code":6002,"name":"IntTypeConversionError","msg":"Integer Type Conversion Error"},{"code":6003,"name":"PoolNotFound","msg":"Pool Not Found"},{"code":6004,"name":"BumpNotFound","msg":"Bump not found"},{"code":6005,"name":"InvalidTokenAmount","msg":"Invalid Token Amount"},{"code":6006,"name":"ExpectedAccountNotFound","msg":"Expected Account Not Found"},{"code":6007,"name":"OutdatedOracle","msg":"Outdated Oracle"},{"code":6008,"name":"CheckedMathError","msg":"Checked Math Error"},{"code":6009,"name":"BorrowPositionUnableToLiquidate","msg":"Mint Position Unable to Liquidate"},{"code":6010,"name":"HealthScoreTooLow","msg":"Health Score Too Low"},{"code":6011,"name":"InvalidInputCollateralAccount","msg":"Invalid input collateral account"},{"code":6012,"name":"InvalidAccountLoaderOwner","msg":"Invalid Account loader owner"},{"code":6013,"name":"InvalidInputPositionIndex","msg":"Invalid input position index"},{"code":6014,"name":"InvalidTokenAccountBalance","msg":"Invalid token account balance"},{"code":6015,"name":"InequalityComparisonViolated","msg":"Inequality comparison violated"},{"code":6016,"name":"CometNotEmpty","msg":"Comet Not Empty"},{"code":6017,"name":"NotSubjectToLiquidation","msg":"Not Subject to Liquidation"},{"code":6018,"name":"LiquidationAmountTooLarge","msg":"Liquidation amount too large"},{"code":6019,"name":"NoRemainingAccountsSupplied","msg":"No remaining accounts supplied"},{"code":6020,"name":"InvalidOvercollateralizationRatios","msg":"Invalid over-collateralization ratios"},{"code":6021,"name":"IncorrectOracleAddress","msg":"Incorrect oracle address provided"},{"code":6022,"name":"InvalidValueRange","msg":"Value is in an incorrect range"},{"code":6023,"name":"InvalidAssetStability","msg":"Asset stable requirement violated"},{"code":6024,"name":"SlippageToleranceExceeded","msg":"Slippage tolerance exceeded"},{"code":6025,"name":"RequireOnlyonUSDCollateral","msg":"Collateral must be all in onUSD"},{"code":6026,"name":"RequireAllPositionsClosed","msg":"Positions must be all closed"},{"code":6027,"name":"FailedToLoadPyth","msg":"Failed to Load Pyth Price Feed"},{"code":6028,"name":"StatusPreventsAction","msg":"Status Prevents Action"},{"code":6029,"name":"PoolEmpty","msg":"Pool is empty"},{"code":6030,"name":"NoLiquidityToWithdraw","msg":"No liquidity to withdraw"},{"code":6031,"name":"InvalidStatus","msg":"Invalid Status"},{"code":6032,"name":"AuthArrayFull","msg":"Auth Array Full"},{"code":6033,"name":"AuthNotFound","msg":"Auth Not Found"},{"code":6034,"name":"InvalidOracleIndex","msg":"Invalid oracle index"},{"code":6035,"name":"InvalidPaymentType","msg":"Invalid Payment Type"},{"code":6036,"name":"InvalidConversion","msg":"Invalid Conversion"},{"code":6037,"name":"AuthAlreadyExists","msg":"Auth Already Exists"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/dflow_v4.DF1ow4tspfHX9JwWJsAb9epbkA8hmpSEAtxXy1V27QBH.json b/migration/khadhroony-bot2-reference/idls/dflow_v4.DF1ow4tspfHX9JwWJsAb9epbkA8hmpSEAtxXy1V27QBH.json new file mode 100644 index 0000000..d0a0765 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/dflow_v4.DF1ow4tspfHX9JwWJsAb9epbkA8hmpSEAtxXy1V27QBH.json @@ -0,0 +1 @@ +{"address":"DF1ow4tspfHX9JwWJsAb9epbkA8hmpSEAtxXy1V27QBH","metadata":{"name":"swap_orchestrator","version":"0.1.0","spec":"0.1.0","description":"DFlow Aggregator program"},"instructions":[{"name":"close_empty_token_account","docs":["Closes a token account if it has a zero balance"],"discriminator":[232,75,140,136,250,78,224,188],"accounts":[{"name":"token_account","docs":["The token account to close (supports both spl_token and spl_token_2022)"],"writable":true},{"name":"authority","docs":["The authority of the token account. Must sign."],"signer":true},{"name":"destination","docs":["The account to receive the rent from closing the token account."],"writable":true},{"name":"token_program","docs":["The token program (either spl_token or spl_token_2022)"]}],"args":[]},{"name":"close_order","docs":["Closes an order, returning tokens from the order vault"],"discriminator":[90,103,209,28,7,63,168,4],"accounts":[{"name":"order","writable":true},{"name":"order_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,100,101,114,95,118,97,117,108,116]},{"kind":"account","path":"order"}]}},{"name":"return_input_token_account","docs":["Account to which leftover tokens in the order vault should be returned."],"writable":true,"relations":["order"]},{"name":"return_rent_to","docs":["Account to which rent for the order account and vault should be returned."],"writable":true,"relations":["order"]},{"name":"closer","docs":["Account that is authorized to close the order"],"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_referral_token_account_idempotent","docs":["Create a referral token account if it doesn't already exist"],"discriminator":[46,232,41,144,85,37,170,175],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"project"},{"name":"referral_account"},{"name":"referral_token_account","docs":["The referral token account to create. This is a PDA of the Referral program whose seeds are","[b\"referral_ata\", referral_account, mint]."],"writable":true},{"name":"mint"},{"name":"system_program"},{"name":"token_program"},{"name":"referral_program"}],"args":[]},{"name":"fill_order","docs":["Fills an order, closing the order after filling it"],"discriminator":[232,122,115,25,199,143,136,162],"accounts":[{"name":"order","writable":true},{"name":"order_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,100,101,114,95,118,97,117,108,116]},{"kind":"account","path":"order"}]}},{"name":"output_token_account","docs":["Account to which output from the fill should be sent."],"writable":true,"relations":["order"]},{"name":"return_input_token_account","docs":["Account to which leftover tokens in the order vault should be returned."],"writable":true,"relations":["order"]},{"name":"return_rent_to","docs":["Account to which rent for the order account and vault should be returned."],"writable":true,"relations":["order"]},{"name":"filler_input_token_account","writable":true},{"name":"input_mint","docs":["Input mint for the swap. Must be specified if the order inputs a mint for which the filler's","associated token account doesn't exist."],"optional":true},{"name":"filler_output_token_account","writable":true},{"name":"output_mint","docs":["Output mint for the swap. Must be specified if the order outputs native SOL or a mint for","which the filler's associated token account doesn't exist."],"optional":true},{"name":"filler","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"FillOrderParams"}}}]},{"name":"init_market_ledger_idempotent","docs":["Initialize a market ledger if it doesn't already exist"],"discriminator":[183,43,139,79,125,225,35,159],"accounts":[{"name":"predictions_program"},{"name":"market_ledger"},{"name":"market_ledger_redemption_vault"},{"name":"market_ledger_order_vault"},{"name":"market_ledger_reduce_yes_vault"},{"name":"market_ledger_reduce_no_vault"},{"name":"settlement_escrow"},{"name":"escrow_mint"},{"name":"market_yes_outcome_mint"},{"name":"market_no_outcome_mint"},{"name":"payer"},{"name":"outcome_token_program"},{"name":"escrow_token_program"},{"name":"system_program"}],"args":[{"name":"market_id","type":{"array":["u8",32]}}]},{"name":"open_order","docs":["Opens an order, escrowing the input tokens in the order vault"],"discriminator":[206,88,88,143,38,136,50,224],"accounts":[{"name":"order","docs":["Order account that the instruction will create to store the order details"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,100,101,114]},{"kind":"account","path":"return_input_token_account"},{"kind":"arg","path":"params.order_account_id"}]}},{"name":"order_vault","docs":["Token account that the instruction will create to escrow the input tokens for the order"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,100,101,114,95,118,97,117,108,116]},{"kind":"account","path":"order"}]}},{"name":"input_token_account","docs":["Token account from which input tokens should be withdrawn to fund the order"],"writable":true},{"name":"output_token_account","docs":["Account to which output from the fill should be sent. If the order specifies that it should","output native SOL, then this should be the user's wallet, although any account is allowed.","If the order doesn't specify that it should output native SOL, then this must be an SPL","token account, and the token account's mint determines the order's output mint."]},{"name":"return_input_token_account","docs":["Account to which leftover tokens in the order vault should be returned when the order is","filled or closed. If the input is native SOL, this should be the user's wallet, although any","account is allowed. If the input is wrapped SOL or an SPL token, this must be an SPL token","account for the input mint."]},{"name":"input_mint"},{"name":"user_token_authority","docs":["Authority of the input token account"],"signer":true},{"name":"fee_payer","docs":["Account that pays the fee budget for the order"],"writable":true,"signer":true},{"name":"fee_receiver","docs":["Account that receives the fee budget funds"],"writable":true},{"name":"rent_depositor","docs":["Depositor for the order account and vault account rent"],"writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"OpenOrderParams"}}}]},{"name":"swap","docs":["Executes a token swap"],"discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"user_token_authority","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SwapParams"}}}]},{"name":"swap2","docs":["Executes a token swap with a slippage fee"],"discriminator":[65,75,63,76,235,91,91,136],"accounts":[{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"user_token_authority","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"Swap2Params"}}}]},{"name":"swap2_with_destination","docs":["Executes a token swap with a slippage fee. Outputs to the specified destination account."],"discriminator":[95,123,213,246,122,1,86,231],"accounts":[{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"user_token_authority","signer":true},{"name":"destination_token_account","writable":true},{"name":"destination_token_authority"},{"name":"destination_mint"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"Swap2Params"}}}]},{"name":"swap2_with_destination_native","docs":["Executes a token swap with a slippage fee. Outputs native SOL to the specified destination","account by unwrapping WSOL."],"discriminator":[222,100,184,146,186,196,105,165],"accounts":[{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"user_token_authority","signer":true},{"name":"destination_account","docs":["The destination account that will receive the native SOL"],"writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"Swap2Params"}}}]},{"name":"swap_with_destination","docs":["Executes a token swap. Outputs to the specified destination account."],"discriminator":[168,172,24,77,197,156,135,101],"accounts":[{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"user_token_authority","signer":true},{"name":"destination_token_account","writable":true},{"name":"destination_token_authority"},{"name":"destination_mint"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SwapParams"}}}]},{"name":"swap_with_destination_native","docs":["Executes a token swap. Outputs native SOL to the specified destination account by unwrapping","WSOL."],"discriminator":[205,77,127,108,241,32,196,195],"accounts":[{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"user_token_authority","signer":true},{"name":"destination_account","docs":["The destination account that will receive the native SOL"],"writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SwapParams"}}}]},{"name":"transfer_fee","docs":["Transfer an SPL token fee if the receiving account can receive it"],"discriminator":[129,164,196,21,177,48,180,162],"accounts":[{"name":"from"},{"name":"to"},{"name":"authority"},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount","type":"u64"}]},{"name":"transfer_sol","docs":["Transfer native SOL from one account to another"],"discriminator":[78,10,236,247,109,117,21,76],"accounts":[{"name":"from"},{"name":"to"},{"name":"system_program"}],"args":[{"name":"lamports","type":"u64"}]},{"name":"transfer_to_sponsor","docs":["Transfer an SPL token to a sponsor's token account, creating the sponsor's token account if","necessary."],"discriminator":[155,179,130,151,196,139,253,163],"accounts":[{"name":"user_token_authority"},{"name":"user_token_account"},{"name":"sponsor"},{"name":"sponsor_token_account"},{"name":"mint"},{"name":"token_program"},{"name":"associated_token_program"},{"name":"system_program"}],"args":[{"name":"amount","type":"u64"}]},{"name":"unwrap_sol","docs":["Unwrap all SOL in the provided wrapped SOL token account"],"discriminator":[99,40,14,105,45,107,172,201],"accounts":[{"name":"owner"},{"name":"wrapped_sol_associated_token_account"},{"name":"token_program"}],"args":[]},{"name":"wrap_sol","docs":["Wrap SOL, creating a wrapped SOL associated token account if necessary"],"discriminator":[47,62,155,172,131,205,37,201],"accounts":[{"name":"from"},{"name":"wrapped_sol_associated_token_account"},{"name":"native_mint"},{"name":"token_program"},{"name":"associated_token_program"},{"name":"system_program"}],"args":[{"name":"lamports","type":"u64"}]}],"accounts":[{"name":"Order","discriminator":[134,173,223,185,77,86,28,51]}],"events":[{"name":"FeeEvent","discriminator":[73,79,78,127,184,213,13,220]},{"name":"SwapEvent","discriminator":[64,198,205,232,38,8,113,226]}],"errors":[{"code":15000,"name":"SwapCpiFailed","msg":"Swap CPI failed"},{"code":15001,"name":"SlippageLimitExceeded","msg":"Slippage limit exceeded"},{"code":15002,"name":"FeeAccountNotSpecified","msg":"Fee account must be specified when platform_fee_bps is nonzero"},{"code":15003,"name":"InvalidFeeAccountOwner","msg":"Invalid fee account owner"},{"code":15004,"name":"InputAmountIsZero","msg":"input_amount must be greater than 0"},{"code":15005,"name":"OutputTokenAccountMismatch","msg":"Swap output token account did not match the order's output_token_account"},{"code":15006,"name":"OrderNotFillableAfterLastFillableSlot","msg":"Order's last_fillable_slot has passed"},{"code":15007,"name":"FillOrderNotAuthorized","msg":"filler is not authorized to fill the order"},{"code":15008,"name":"MintAccountNotSpecified","msg":"Instruction must specify mint account"},{"code":15009,"name":"ReturnInputTokenAccountMismatch","msg":"return_input_token_account did not match the order's return_input_token_account"},{"code":15010,"name":"ReturnRentToMismatch","msg":"return_rent_to did not match the order's return_rent_to"},{"code":15011,"name":"CloseOrderNotAuthorized","msg":"closer is not authorized to close the order"},{"code":15012,"name":"InvalidReturnInputTokenAccount","msg":"Invalid return_input_token_account"},{"code":15013,"name":"InvalidOutputTokenAccount","msg":"Invalid output_token_account"},{"code":15014,"name":"CouldNotReturnInput","msg":"Couldn't return input from vault"},{"code":15015,"name":"LegInputOverconsumption","msg":"Leg consumed more than allowed input amount"},{"code":15016,"name":"RouteInputUnderconsumption","msg":"Route consumed less than minimum allowed amount of input"},{"code":15017,"name":"LegConsumedOutput","msg":"Leg consumed output"},{"code":15018,"name":"LegConsumedLamports","msg":"Leg consumed lamports"},{"code":15019,"name":"PumpFunAmmCreatedFeeAta","msg":"Pump.fun AMM leg created fee/buyback ATA"},{"code":15020,"name":"SkippedLeg","msg":"A leg in the route was skipped"},{"code":15021,"name":"DestinationTokenAccountDoesNotExist","msg":"Destination token account does not exist"},{"code":15022,"name":"Token2022ProgramNotProvided","msg":"Token 2022 Program account not provided"},{"code":15023,"name":"InvalidLegInputTokenAccount","msg":"Invalid leg input token account"},{"code":15024,"name":"RaydiumLaunchlabCreatedFeeAta","msg":"Raydium Launchlab leg created fee ATA"},{"code":15025,"name":"LegUnderproducedOutput","msg":"Leg produced less output than expected"},{"code":15026,"name":"NoSettlementTokensReceived","msg":"No settlement tokens were received during the swap"},{"code":15027,"name":"InsufficientAccountsForOpenPredictionsOrder","msg":"Insufficient accounts provided for OpenPredictionsOrder action"},{"code":15028,"name":"InvalidTokenAccount","msg":"Invalid token account. Must be either spl_token or spl_token_2022 account"},{"code":15029,"name":"InvalidCloseAuthorityForNativeMint","msg":"InitAtaIdempotent cannot set a close authority on a WSOL ATA"},{"code":15998,"name":"InvalidLegAction"},{"code":15999,"name":"InvalidInstructionData"}],"types":[{"name":"A","type":{"kind":"enum","variants":[{"name":"B","fields":[{"defined":{"name":"C"}}]},{"name":"J","fields":[{"defined":{"name":"J"}}]}]}},{"name":"Action","type":{"kind":"enum","variants":[{"name":"WhirlpoolsSwap","fields":[{"defined":{"name":"WhirlpoolsSwapOptions"}}]},{"name":"ClearpoolsSwap","fields":[{"defined":{"name":"ClearpoolsSwapOptions"}}]},{"name":"RaydiumAmmSwap","fields":[{"defined":{"name":"RaydiumAmmSwapOptions"}}]},{"name":"LifinityV2Swap","fields":[{"defined":{"name":"LifinityV2SwapOptions"}}]},{"name":"MeteoraDlmmSwap","fields":[{"defined":{"name":"MeteoraDlmmSwapOptions"}}]},{"name":"RaydiumClmmSwap","fields":[{"defined":{"name":"RaydiumClmmSwapOptions"}}]},{"name":"RaydiumClmmSwapV2","fields":[{"defined":{"name":"RaydiumClmmSwapV2Options"}}]},{"name":"PhoenixSwap","fields":[{"defined":{"name":"PhoenixSwapOptions"}}]},{"name":"PumpFunBuy","fields":[{"defined":{"name":"PumpFunBuyOptions"}}]},{"name":"PumpFunSell","fields":[{"defined":{"name":"PumpFunSellOptions"}}]},{"name":"GammaSwap","fields":[{"defined":{"name":"GammaSwapOptions"}}]},{"name":"ObricV2Swap","fields":[{"defined":{"name":"ObricV2SwapOptions"}}]},{"name":"PumpFunAmmBuy","fields":[{"defined":{"name":"PumpFunAmmBuyOptions"}}]},{"name":"PumpFunAmmSell","fields":[{"defined":{"name":"PumpFunAmmSellOptions"}}]},{"name":"SolFiSwap","fields":[{"defined":{"name":"SolFiSwapOptions"}}]},{"name":"RubiconSwap","fields":[{"defined":{"name":"RubiconSwapOptions"}}]},{"name":"MeteoraDammV1Swap","fields":[{"defined":{"name":"MeteoraDammV1SwapOptions"}}]},{"name":"RaydiumCpSwap","fields":[{"defined":{"name":"RaydiumCpSwapOptions"}}]},{"name":"StabbleStableSwap","fields":[{"defined":{"name":"StabbleStableSwapOptions"}}]},{"name":"TesseraVSwap","fields":[{"defined":{"name":"TesseraVSwapOptions"}}]},{"name":"MeteoraDammV2Swap","fields":[{"defined":{"name":"MeteoraDammV2SwapOptions"}}]},{"name":"RaydiumLaunchlabSwap","fields":[{"defined":{"name":"RaydiumLaunchlabSwapOptions"}}]},{"name":"MeteoraDbcSwap","fields":[{"defined":{"name":"MeteoraDbcSwapOptions"}}]},{"name":"HumidiFiSwap","fields":[{"defined":{"name":"HumidiFiSwapOptions"}}]},{"name":"WhirlpoolsSwapV2","fields":[{"defined":{"name":"WhirlpoolsSwapV2Options"}}]},{"name":"MeteoraDlmmSwapV2","fields":[{"defined":{"name":"MeteoraDlmmSwapV2Options"}}]},{"name":"ZeroFiSwap","fields":[{"defined":{"name":"ZeroFiSwapOptions"}}]},{"name":"AlphaQSwap","fields":[{"defined":{"name":"AlphaQSwapOptions"}}]},{"name":"TokenSwap","fields":[{"defined":{"name":"TokenSwapOptions"}}]},{"name":"SolFiV2Swap","fields":[{"defined":{"name":"SolFiV2SwapOptions"}}]},{"name":"MozartSwap","fields":[{"defined":{"name":"MozartSwapOptions"}}]},{"name":"DFlowDynamicRouteV1","fields":[{"defined":{"name":"DFlowDynamicRouteV1Options"}}]},{"name":"HeavenSwap","fields":[{"defined":{"name":"HeavenSwapOptions"}}]},{"name":"NexusSwap","fields":[{"defined":{"name":"NexusSwapOptions"}}]},{"name":"SarosDlmmSwap","fields":[{"defined":{"name":"SarosDlmmSwapOptions"}}]},{"name":"TransferFee","fields":[{"defined":{"name":"TransferFeeOptions"}}]},{"name":"TransferFeeWithMint","fields":[{"defined":{"name":"TransferFeeOptions"}}]},{"name":"RecordId","fields":[{"defined":{"name":"RecordIdOptions"}}]},{"name":"RecordId2","fields":[{"defined":{"name":"RecordId2Options"}}]},{"name":"ManifestSwap","fields":[{"defined":{"name":"ManifestSwapOptions"}}]},{"name":"BisonFiSwap","fields":[{"defined":{"name":"BisonFiSwapOptions"}}]},{"name":"SanctumInfinitySwap","fields":[{"defined":{"name":"SanctumInfinitySwapOptions"}}]},{"name":"SanctumInfinityLiquidity","fields":[{"defined":{"name":"SanctumInfinityLiquidityOptions"}}]},{"name":"OpenPredictionsOrder","fields":[{"defined":{"name":"OpenPredictionsOrderOptions"}}]},{"name":"ScorchSwap","fields":[{"defined":{"name":"ScorchSwapOptions"}}]},{"name":"IncludeAccount"},{"name":"StabbleWeightedSwap","fields":[{"defined":{"name":"StabbleWeightedSwapOptions"}}]},{"name":"VertigoSwap","fields":[{"defined":{"name":"VertigoSwapOptions"}}]},{"name":"SetMinimumLegOutputs","fields":[{"defined":{"name":"SetMinimumLegOutputsOptions"}}]},{"name":"SetMinimumLegPrices","fields":[{"defined":{"name":"SetMinimumLegPricesOptions"}}]},{"name":"SetSponsor"},{"name":"WrapSol","fields":[{"defined":{"name":"WrapSolOptions"}}]},{"name":"UnwrapSol"},{"name":"KDEXSwap","fields":[{"defined":{"name":"KDEXSwapOptions"}}]},{"name":"DeriverseSwap","fields":[{"defined":{"name":"DeriverseSwapOptions"}}]},{"name":"VaultSwap","fields":[{"defined":{"name":"VaultSwapOptions"}}]},{"name":"MetaDaoSwap","fields":[{"defined":{"name":"MetaDaoSwapOptions"}}]},{"name":"D","fields":[{"defined":{"name":"O"}}]},{"name":"XoCashExchangeSwap","fields":[{"defined":{"name":"XoCashExchangeSwapOptions"}}]},{"name":"LemmingsFiSwap","fields":[{"defined":{"name":"LemmingsFiSwapOptions"}}]},{"name":"InitAtaIdempotent"},{"name":"SetMaxUnderconsumptionBps","fields":[{"defined":{"name":"SetMaxUnderconsumptionBpsOptions"}}]},{"name":"GhostSwap","fields":[{"defined":{"name":"GhostSwapOptions"}}]},{"name":"TransferFeeV2","fields":[{"defined":{"name":"TransferFeeOptions"}}]},{"name":"TransferFeeWithMintV2","fields":[{"defined":{"name":"TransferFeeOptions"}}]},{"name":"PumpFunBuyV2","fields":[{"defined":{"name":"PumpFunBuyV2Options"}}]},{"name":"PumpFunSellV2","fields":[{"defined":{"name":"PumpFunSellV2Options"}}]}]}},{"name":"AlphaQDynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"AlphaQSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"BisonFiDynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"BisonFiSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"C","type":{"kind":"struct","fields":[{"name":"_padding","type":"u8"}]}},{"name":"ClearpoolsSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"a_to_b","type":"bool"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"DFlowDynamicRouteV1Options","type":{"kind":"struct","fields":[{"name":"candidate_actions","type":{"vec":{"defined":{"name":"DynamicRouteV1CandidateAction"}}}},{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"DeriverseSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"DynamicRouteV1CandidateAction","type":{"kind":"enum","variants":[{"name":"SolFi","fields":[{"defined":{"name":"SolFiDynamicRouteV1Options"}}]},{"name":"Rubicon","fields":[{"defined":{"name":"RubiconDynamicRouteV1Options"}}]},{"name":"TesseraV","fields":[{"defined":{"name":"TesseraVDynamicRouteV1Options"}}]},{"name":"HumidiFi","fields":[{"defined":{"name":"HumidiFiDynamicRouteV1Options"}}]},{"name":"SolFiV2","fields":[{"defined":{"name":"SolFiV2DynamicRouteV1Options"}}]},{"name":"Mozart","fields":[{"defined":{"name":"MozartDynamicRouteV1Options"}}]},{"name":"ObricV2","fields":[{"defined":{"name":"ObricV2DynamicRouteV1Options"}}]},{"name":"Nexus","fields":[{"defined":{"name":"NexusDynamicRouteV1Options"}}]},{"name":"BisonFi","fields":[{"defined":{"name":"BisonFiDynamicRouteV1Options"}}]},{"name":"ZeroFi","fields":[{"defined":{"name":"ZeroFiDynamicRouteV1Options"}}]},{"name":"Manifest","fields":[{"defined":{"name":"ManifestDynamicRouteV1Options"}}]},{"name":"AlphaQ","fields":[{"defined":{"name":"AlphaQDynamicRouteV1Options"}}]},{"name":"LemmingsFi","fields":[{"defined":{"name":"LemmingsFiDynamicRouteV1Options"}}]}]}},{"name":"FeeEvent","type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"FillOrderParams","type":{"kind":"struct","fields":[{"name":"swap_actions","type":{"vec":{"defined":{"name":"Action"}}}},{"name":"platform_fee_ubps","type":"u32"}]}},{"name":"GammaSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"endorsed","type":"bool"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"GhostSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"HeavenSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"HumidiFiDynamicRouteV1Options","type":{"kind":"struct","fields":[{"name":"swap_id","type":"u64"}]}},{"name":"HumidiFiSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"swap_id","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"J","type":{"kind":"struct","fields":[{"name":"_padding","type":"u8"}]}},{"name":"KDEXSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"LegPrice","type":{"kind":"struct","fields":[{"name":"output_atoms_per_input_token","type":"u64"},{"name":"input_mint_decimals","type":"u8"}]}},{"name":"LemmingsFiDynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"LemmingsFiSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"LifinityV2SwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"ManifestDynamicRouteV1Options","type":{"kind":"struct","fields":[{"name":"include_global","type":"bool"}]}},{"name":"ManifestSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"MetaDaoSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"MeteoraDammV1SwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"MeteoraDammV2SwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"MeteoraDbcSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"is_rate_limiter_applied","type":"bool"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"MeteoraDlmmSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"num_bin_arrays","type":"u8"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"MeteoraDlmmSwapV2Options","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"num_bin_arrays","type":"u8"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"MozartDynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"MozartSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"NexusDynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"NexusSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"O","type":{"kind":"struct","fields":[{"name":"a","type":{"vec":{"defined":{"name":"A"}}}},{"name":"amount","type":"u64"},{"name":"orchestrator_flags","type":{"defined":{"name":"OrchestratorFlags"}}},{"name":"pfs","type":"u16"},{"name":"dfs","type":"u16"}]}},{"name":"ObricV2DynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"ObricV2SwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"OpenOrderParams","type":{"kind":"struct","fields":[{"name":"input_amount","type":"u64"},{"name":"quoted_out_amount","docs":["Quoted output amount after platform fee"],"type":"u64"},{"name":"fee_budget","docs":["Maximum amount in lamports that the fee payer will pay to have the order filled or closed"],"type":"u64"},{"name":"order_account_id","type":"u64"},{"name":"fillable_for_slots","docs":["Number of slots after the slot in which the order is opened for which the order is fillable.","If zero, the order is only fillable in the slot in which the open transaction landed."],"type":"u32"},{"name":"slippage_bps","docs":["Max allowed slippage in basis points. Along with `quoted_out_amount`, this is used to","calculate the minimum allowed output amount."],"type":"u16"},{"name":"closer","docs":["Account that is authorized to close the order"],"type":"pubkey"},{"name":"flags","docs":["Flags for the order"],"type":"u8"}]}},{"name":"OpenPredictionsOrderOptions","type":{"kind":"struct","fields":[{"name":"nonce","type":"u64"},{"name":"order_outcome","type":"u8"},{"name":"quoted_out_amount","docs":["Quoted output amount for the predictions order"],"type":"u64"},{"name":"slippage_bps","docs":["Slippage for the predictions order (separate from swap slippage)"],"type":"u16"},{"name":"platform_fee_recipient_vault","type":"pubkey"},{"name":"platform_fee_scale","type":"u16"}]}},{"name":"OrchestratorFlags","type":{"kind":"struct","fields":[{"name":"flags","type":"u8"}]}},{"name":"Order","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"closer","docs":["Account that is authorized to close the order"],"type":"pubkey"},{"name":"output_token_account","docs":["Account to which output from the fill should be sent. If the order specifies that it should","output native SOL, then this should be the user's wallet, although any account is allowed.","If the order doesn't specify that it should output native SOL, then this must be an SPL","token account, and the token account's mint determines the order's output mint."],"type":"pubkey"},{"name":"return_input_token_account","docs":["Account to which leftover tokens in the order vault should be returned when the order is","filled or closed. If the input is native SOL, this should be the user's wallet, although any","account is allowed. If the input is wrapped SOL or an SPL token, this must be an SPL token","account for the input mint."],"type":"pubkey"},{"name":"return_rent_to","docs":["Account to which rent for the account should be returned when the account is closed"],"type":"pubkey"},{"name":"id","docs":["ID used to produce a unique PDA for the order account"],"type":"u64"},{"name":"quoted_out_amount","type":"u64"},{"name":"last_fillable_slot","docs":["Highest slot at which the order can be filled"],"type":"u64"},{"name":"slippage_bps","docs":["Max allowed slippage in basis points. Along with `quoted_out_amount`, this is used to","calculate the minimum allowed output amount for the order."],"type":"u16"},{"name":"bump","type":"u8"},{"name":"vault_bump","docs":["Bump seed for the vault token account associated with the order"],"type":"u8"},{"name":"flags","docs":["Flags for the order"],"type":"u8"},{"name":"_padding1","type":"u8"},{"name":"_padding2","type":"u8"},{"name":"_padding3","type":"u8"}]}},{"name":"PhoenixSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"side","type":{"defined":{"name":"Side"}}},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"PumpFunAmmBuyOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"PumpFunAmmSellOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"PumpFunBuyOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"PumpFunBuyV2Options","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"PumpFunSellOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"PumpFunSellV2Options","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"RaydiumAmmSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"RaydiumClmmSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"num_remaining_accounts","type":"u8"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"RaydiumClmmSwapV2Options","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"num_remaining_accounts","type":"u8"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"RaydiumCpSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"RaydiumLaunchlabSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"RecordId2Options","type":{"kind":"struct","fields":[{"name":"id","type":{"array":["u8",4]}}]}},{"name":"RecordIdOptions","type":{"kind":"struct","fields":[{"name":"id","type":{"array":["u8",76]}}]}},{"name":"RubiconDynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"RubiconSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"SanctumInfinityLiquidityOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"lst_value_calc_accs","type":"u8"},{"name":"lst_index","type":"u32"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"SanctumInfinitySwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"src_lst_value_calc_accs","type":"u8"},{"name":"dst_lst_value_calc_accs","type":"u8"},{"name":"src_lst_index","type":"u32"},{"name":"dst_lst_index","type":"u32"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"SarosDlmmSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"ScorchSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"id","type":"u128"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"SetMaxUnderconsumptionBpsOptions","type":{"kind":"struct","fields":[{"name":"max_underconsumption_bps","type":"u16"}]}},{"name":"SetMinimumLegOutputsOptions","type":{"kind":"struct","fields":[{"name":"minimum_outputs","docs":["Minimum allowed output amount for each swap leg"],"type":{"vec":"u64"}}]}},{"name":"SetMinimumLegPricesOptions","type":{"kind":"struct","fields":[{"name":"minimum_prices","docs":["Minimum allowed price for each swap leg"],"type":{"vec":{"defined":{"name":"LegPrice"}}}}]}},{"name":"Side","type":{"kind":"enum","variants":[{"name":"Bid"},{"name":"Ask"}]}},{"name":"SolFiDynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"SolFiSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"SolFiV2DynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"SolFiV2SwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"StabbleStableSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"StabbleWeightedSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"Swap2Params","type":{"kind":"struct","fields":[{"name":"actions","type":{"vec":{"defined":{"name":"Action"}}}},{"name":"quoted_out_amount","docs":["Quoted output amount for the swap after platform fee"],"type":"u64"},{"name":"slippage_bps","docs":["Max allowed slippage in basis points. Along with `quoted_out_amount`, this is used to","calculate the minimum allowed output amount for the swap."],"type":"u16"},{"name":"platform_fee_bps","docs":["Platform fee in basis points. If nonzero, this percentage of the output amount will be","transferred to the platform fee account, and the instruction must include the platform fee","account as the last account. The platform fee account must be a valid token account for the","output mint, and it must specify the public key of the Referral program's DFlow project as","its owner."],"type":"u16"},{"name":"positive_slippage_fee_limit_pct","docs":["Limit on the positive slippage fee in percent. The positive slippage fee is limited to the","lesser of (1) the excess actual out amount above the quoted amount and (2) this percentage","of the actual out amount after platform fee."],"type":"u8"}]}},{"name":"SwapEvent","type":{"kind":"struct","fields":[{"name":"amm","type":"pubkey"},{"name":"input_mint","type":"pubkey"},{"name":"input_amount","type":"u64"},{"name":"output_mint","type":"pubkey"},{"name":"output_amount","type":"u64"}]}},{"name":"SwapParams","type":{"kind":"struct","fields":[{"name":"actions","type":{"vec":{"defined":{"name":"Action"}}}},{"name":"quoted_out_amount","docs":["Quoted output amount for the swap after platform fee"],"type":"u64"},{"name":"slippage_bps","docs":["Max allowed slippage in basis points. Along with `quoted_out_amount`, this is used to","calculate the minimum allowed output amount for the swap."],"type":"u16"},{"name":"platform_fee_bps","docs":["Platform fee in basis points. If nonzero, this percentage of the output amount will be","transferred to the platform fee account, and the instruction must include the platform fee","account as the last account. The platform fee account must be a valid token account for the","output mint, and it must specify the public key of the Referral program's DFlow project as","its owner."],"type":"u16"}]}},{"name":"TesseraVDynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"TesseraVSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"TokenSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"TransferFeeOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"VaultSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"VertigoSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"WhirlpoolsSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"a_to_b","type":"bool"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"WhirlpoolsSwapV2Options","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"a_to_b","type":"bool"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"WrapSolOptions","type":{"kind":"struct","fields":[{"name":"lamports","type":"u64"}]}},{"name":"XoCashExchangeSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}},{"name":"ZeroFiDynamicRouteV1Options","type":{"kind":"struct","fields":[]}},{"name":"ZeroFiSwapOptions","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"orchestrator_flags","docs":["Optional flags for the swap"],"type":{"defined":{"name":"OrchestratorFlags"}}}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/drift_v2.dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH.json b/migration/khadhroony-bot2-reference/idls/drift_v2.dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH.json new file mode 100644 index 0000000..ce5f81e --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/drift_v2.dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH.json @@ -0,0 +1 @@ +{"version":"2.150.0","name":"drift","instructions":[{"name":"initializeUser","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"subAccountId","type":"u16"},{"name":"name","type":{"array":["u8",32]}}]},{"name":"initializeUserStats","accounts":[{"name":"userStats","isMut":true,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeSignedMsgUserOrders","accounts":[{"name":"signedMsgUserOrders","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"numOrders","type":"u16"}]},{"name":"resizeSignedMsgUserOrders","accounts":[{"name":"signedMsgUserOrders","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"user","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"numOrders","type":"u16"}]},{"name":"initializeSignedMsgWsDelegates","accounts":[{"name":"signedMsgWsDelegates","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"delegates","type":{"vec":"publicKey"}}]},{"name":"changeSignedMsgWsDelegateStatus","accounts":[{"name":"signedMsgWsDelegates","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"delegate","type":"publicKey"},{"name":"add","type":"bool"}]},{"name":"initializeFuelOverflow","accounts":[{"name":"fuelOverflow","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"sweepFuel","accounts":[{"name":"fuelOverflow","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"signer","isMut":false,"isSigner":true}],"args":[]},{"name":"resetFuelSeason","accounts":[{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[]},{"name":"initializeReferrerName","accounts":[{"name":"referrerName","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"name","type":{"array":["u8",32]}}]},{"name":"deposit","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"amount","type":"u64"},{"name":"reduceOnly","type":"bool"}]},{"name":"withdraw","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"amount","type":"u64"},{"name":"reduceOnly","type":"bool"}]},{"name":"transferDeposit","accounts":[{"name":"fromUser","isMut":true,"isSigner":false},{"name":"toUser","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarketVault","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"amount","type":"u64"}]},{"name":"transferPools","accounts":[{"name":"fromUser","isMut":true,"isSigner":false},{"name":"toUser","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"depositFromSpotMarketVault","isMut":true,"isSigner":false},{"name":"depositToSpotMarketVault","isMut":true,"isSigner":false},{"name":"borrowFromSpotMarketVault","isMut":true,"isSigner":false},{"name":"borrowToSpotMarketVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false}],"args":[{"name":"depositFromMarketIndex","type":"u16"},{"name":"depositToMarketIndex","type":"u16"},{"name":"borrowFromMarketIndex","type":"u16"},{"name":"borrowToMarketIndex","type":"u16"},{"name":"depositAmount","type":{"option":"u64"}},{"name":"borrowAmount","type":{"option":"u64"}}]},{"name":"transferPerpPosition","accounts":[{"name":"fromUser","isMut":true,"isSigner":false},{"name":"toUser","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"amount","type":{"option":"i64"}}]},{"name":"placePerpOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"OrderParams"}}]},{"name":"cancelOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"orderId","type":{"option":"u32"}}]},{"name":"cancelOrderByUserId","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"userOrderId","type":"u8"}]},{"name":"cancelOrders","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"marketType","type":{"option":{"defined":"MarketType"}}},{"name":"marketIndex","type":{"option":"u16"}},{"name":"direction","type":{"option":{"defined":"PositionDirection"}}}]},{"name":"cancelOrdersByIds","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"orderIds","type":{"vec":"u32"}}]},{"name":"modifyOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"orderId","type":{"option":"u32"}},{"name":"modifyOrderParams","type":{"defined":"ModifyOrderParams"}}]},{"name":"modifyOrderByUserId","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"userOrderId","type":"u8"},{"name":"modifyOrderParams","type":{"defined":"ModifyOrderParams"}}]},{"name":"placeAndTakePerpOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"OrderParams"}},{"name":"successCondition","type":{"option":"u32"}}]},{"name":"placeAndMakePerpOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"taker","isMut":true,"isSigner":false},{"name":"takerStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"OrderParams"}},{"name":"takerOrderId","type":"u32"}]},{"name":"placeAndMakeSignedMsgPerpOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"taker","isMut":true,"isSigner":false},{"name":"takerStats","isMut":true,"isSigner":false},{"name":"takerSignedMsgUserOrders","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"OrderParams"}},{"name":"signedMsgOrderUuid","type":{"array":["u8",8]}}]},{"name":"placeSignedMsgTakerOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"signedMsgUserOrders","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"ixSysvar","isMut":false,"isSigner":false,"docs":["the supplied Sysvar could be anything else.","The Instruction Sysvar has not been implemented","in the Anchor framework yet, so this is the safe approach."]}],"args":[{"name":"signedMsgOrderParamsMessageBytes","type":"bytes"},{"name":"isDelegateSigner","type":"bool"}]},{"name":"placeSpotOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"OrderParams"}}]},{"name":"placeAndTakeSpotOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"OrderParams"}},{"name":"fulfillmentType","type":{"option":{"defined":"SpotFulfillmentType"}}},{"name":"makerOrderId","type":{"option":"u32"}}]},{"name":"placeAndMakeSpotOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"taker","isMut":true,"isSigner":false},{"name":"takerStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"OrderParams"}},{"name":"takerOrderId","type":"u32"},{"name":"fulfillmentType","type":{"option":{"defined":"SpotFulfillmentType"}}}]},{"name":"placeOrders","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"vec":{"defined":"OrderParams"}}}]},{"name":"beginSwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"outSpotMarketVault","isMut":true,"isSigner":false},{"name":"inSpotMarketVault","isMut":true,"isSigner":false},{"name":"outTokenAccount","isMut":true,"isSigner":false},{"name":"inTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"instructions","isMut":false,"isSigner":false,"docs":["Instructions Sysvar for instruction introspection"]}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"outMarketIndex","type":"u16"},{"name":"amountIn","type":"u64"}]},{"name":"endSwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"outSpotMarketVault","isMut":true,"isSigner":false},{"name":"inSpotMarketVault","isMut":true,"isSigner":false},{"name":"outTokenAccount","isMut":true,"isSigner":false},{"name":"inTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"instructions","isMut":false,"isSigner":false,"docs":["Instructions Sysvar for instruction introspection"]}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"outMarketIndex","type":"u16"},{"name":"limitPrice","type":{"option":"u64"}},{"name":"reduceOnly","type":{"option":{"defined":"SwapReduceOnly"}}}]},{"name":"updateUserName","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"subAccountId","type":"u16"},{"name":"name","type":{"array":["u8",32]}}]},{"name":"updateUserCustomMarginRatio","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"subAccountId","type":"u16"},{"name":"marginRatio","type":"u32"}]},{"name":"updateUserPerpPositionCustomMarginRatio","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"subAccountId","type":"u16"},{"name":"perpMarketIndex","type":"u16"},{"name":"marginRatio","type":"u16"}]},{"name":"updateUserMarginTradingEnabled","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"subAccountId","type":"u16"},{"name":"marginTradingEnabled","type":"bool"}]},{"name":"updateUserPoolId","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"subAccountId","type":"u16"},{"name":"poolId","type":"u8"}]},{"name":"updateUserDelegate","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"subAccountId","type":"u16"},{"name":"delegate","type":"publicKey"}]},{"name":"updateUserReduceOnly","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"subAccountId","type":"u16"},{"name":"reduceOnly","type":"bool"}]},{"name":"updateUserProtectedMakerOrders","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"protectedMakerModeConfig","isMut":true,"isSigner":false}],"args":[{"name":"subAccountId","type":"u16"},{"name":"protectedMakerOrders","type":"bool"}]},{"name":"deleteUser","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true}],"args":[]},{"name":"forceDeleteUser","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":false},{"name":"keeper","isMut":true,"isSigner":true},{"name":"driftSigner","isMut":false,"isSigner":false}],"args":[]},{"name":"deleteSignedMsgUserOrders","accounts":[{"name":"signedMsgUserOrders","isMut":true,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[]},{"name":"reclaimRent","accounts":[{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"enableUserHighLeverageMode","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"highLeverageModeConfig","isMut":true,"isSigner":false}],"args":[{"name":"subAccountId","type":"u16"}]},{"name":"fillPerpOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"filler","isMut":true,"isSigner":false},{"name":"fillerStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false}],"args":[{"name":"orderId","type":{"option":"u32"}},{"name":"makerOrderId","type":{"option":"u32"}}]},{"name":"revertFill","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"filler","isMut":true,"isSigner":false},{"name":"fillerStats","isMut":true,"isSigner":false}],"args":[]},{"name":"fillSpotOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"filler","isMut":true,"isSigner":false},{"name":"fillerStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false}],"args":[{"name":"orderId","type":{"option":"u32"}},{"name":"fulfillmentType","type":{"option":{"defined":"SpotFulfillmentType"}}},{"name":"makerOrderId","type":{"option":"u32"}}]},{"name":"triggerOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"filler","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false}],"args":[{"name":"orderId","type":"u32"}]},{"name":"forceCancelOrders","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"filler","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false}],"args":[]},{"name":"updateUserIdle","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"filler","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false}],"args":[]},{"name":"logUserBalances","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"user","isMut":true,"isSigner":false}],"args":[]},{"name":"disableUserHighLeverageMode","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"user","isMut":true,"isSigner":false},{"name":"highLeverageModeConfig","isMut":true,"isSigner":false}],"args":[{"name":"disableMaintenance","type":"bool"}]},{"name":"updateUserStatsReferrerStatus","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"userStats","isMut":true,"isSigner":false}],"args":[]},{"name":"adminUpdateUserStatsPausedOperations","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false}],"args":[{"name":"pausedOperations","type":"u8"}]},{"name":"settlePnl","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"spotMarketVault","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"settleMultiplePnls","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"spotMarketVault","isMut":false,"isSigner":false}],"args":[{"name":"marketIndexes","type":{"vec":"u16"}},{"name":"mode","type":{"defined":"SettlePnlMode"}}]},{"name":"settleFundingPayment","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false}],"args":[]},{"name":"settleExpiredMarket","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"liquidatePerp","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"liquidator","isMut":true,"isSigner":false},{"name":"liquidatorStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"liquidatorMaxBaseAssetAmount","type":"u64"},{"name":"limitPrice","type":{"option":"u64"}}]},{"name":"liquidatePerpWithFill","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"liquidator","isMut":true,"isSigner":false},{"name":"liquidatorStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"liquidateSpot","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"liquidator","isMut":true,"isSigner":false},{"name":"liquidatorStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false}],"args":[{"name":"assetMarketIndex","type":"u16"},{"name":"liabilityMarketIndex","type":"u16"},{"name":"liquidatorMaxLiabilityTransfer","type":"u128"},{"name":"limitPrice","type":{"option":"u64"}}]},{"name":"liquidateSpotWithSwapBegin","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"liquidator","isMut":true,"isSigner":false},{"name":"liquidatorStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"liabilitySpotMarketVault","isMut":true,"isSigner":false},{"name":"assetSpotMarketVault","isMut":true,"isSigner":false},{"name":"liabilityTokenAccount","isMut":true,"isSigner":false},{"name":"assetTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"instructions","isMut":false,"isSigner":false,"docs":["Instructions Sysvar for instruction introspection"]}],"args":[{"name":"assetMarketIndex","type":"u16"},{"name":"liabilityMarketIndex","type":"u16"},{"name":"swapAmount","type":"u64"}]},{"name":"liquidateSpotWithSwapEnd","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"liquidator","isMut":true,"isSigner":false},{"name":"liquidatorStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"liabilitySpotMarketVault","isMut":true,"isSigner":false},{"name":"assetSpotMarketVault","isMut":true,"isSigner":false},{"name":"liabilityTokenAccount","isMut":true,"isSigner":false},{"name":"assetTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"instructions","isMut":false,"isSigner":false,"docs":["Instructions Sysvar for instruction introspection"]}],"args":[{"name":"assetMarketIndex","type":"u16"},{"name":"liabilityMarketIndex","type":"u16"}]},{"name":"liquidateBorrowForPerpPnl","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"liquidator","isMut":true,"isSigner":false},{"name":"liquidatorStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false}],"args":[{"name":"perpMarketIndex","type":"u16"},{"name":"spotMarketIndex","type":"u16"},{"name":"liquidatorMaxLiabilityTransfer","type":"u128"},{"name":"limitPrice","type":{"option":"u64"}}]},{"name":"liquidatePerpPnlForDeposit","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"liquidator","isMut":true,"isSigner":false},{"name":"liquidatorStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false}],"args":[{"name":"perpMarketIndex","type":"u16"},{"name":"spotMarketIndex","type":"u16"},{"name":"liquidatorMaxPnlTransfer","type":"u128"},{"name":"limitPrice","type":{"option":"u64"}}]},{"name":"setUserStatusToBeingLiquidated","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[]},{"name":"resolvePerpPnlDeficit","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"spotMarketIndex","type":"u16"},{"name":"perpMarketIndex","type":"u16"}]},{"name":"resolvePerpBankruptcy","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"liquidator","isMut":true,"isSigner":false},{"name":"liquidatorStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"quoteSpotMarketIndex","type":"u16"},{"name":"marketIndex","type":"u16"}]},{"name":"resolveSpotBankruptcy","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"liquidator","isMut":true,"isSigner":false},{"name":"liquidatorStats","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"settleRevenueToInsuranceFund","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"spotMarketIndex","type":"u16"}]},{"name":"updateFundingRate","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"updatePrelaunchOracle","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":false,"isSigner":false},{"name":"oracle","isMut":true,"isSigner":false}],"args":[]},{"name":"updatePerpBidAskTwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"keeperStats","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[]},{"name":"updateSpotMarketCumulativeInterest","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"spotMarketVault","isMut":false,"isSigner":false}],"args":[]},{"name":"updateAmms","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"marketIndexes","type":{"vec":"u16"}}]},{"name":"updateSpotMarketExpiry","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"expiryTs","type":"i64"}]},{"name":"updateUserQuoteAssetInsuranceStake","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"insuranceFundStake","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"signer","isMut":false,"isSigner":true},{"name":"insuranceFundVault","isMut":true,"isSigner":false}],"args":[]},{"name":"updateUserGovTokenInsuranceStake","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"insuranceFundStake","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"signer","isMut":false,"isSigner":true},{"name":"insuranceFundVault","isMut":true,"isSigner":false}],"args":[]},{"name":"updateDelegateUserGovTokenInsuranceStake","accounts":[{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"insuranceFundStake","isMut":false,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeInsuranceFundStake","accounts":[{"name":"spotMarket","isMut":false,"isSigner":false},{"name":"insuranceFundStake","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"addInsuranceFundStake","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"insuranceFundStake","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"amount","type":"u64"}]},{"name":"requestRemoveInsuranceFundStake","accounts":[{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"insuranceFundStake","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"insuranceFundVault","isMut":true,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"amount","type":"u64"}]},{"name":"cancelRequestRemoveInsuranceFundStake","accounts":[{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"insuranceFundStake","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"insuranceFundVault","isMut":true,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"removeInsuranceFundStake","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"insuranceFundStake","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"beginInsuranceFundSwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"outInsuranceFundVault","isMut":true,"isSigner":false},{"name":"inInsuranceFundVault","isMut":true,"isSigner":false},{"name":"outTokenAccount","isMut":true,"isSigner":false},{"name":"inTokenAccount","isMut":true,"isSigner":false},{"name":"ifRebalanceConfig","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"instructions","isMut":false,"isSigner":false,"docs":["Instructions Sysvar for instruction introspection"]}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"outMarketIndex","type":"u16"},{"name":"amountIn","type":"u64"}]},{"name":"endInsuranceFundSwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"outInsuranceFundVault","isMut":true,"isSigner":false},{"name":"inInsuranceFundVault","isMut":true,"isSigner":false},{"name":"outTokenAccount","isMut":true,"isSigner":false},{"name":"inTokenAccount","isMut":true,"isSigner":false},{"name":"ifRebalanceConfig","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"instructions","isMut":false,"isSigner":false,"docs":["Instructions Sysvar for instruction introspection"]}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"outMarketIndex","type":"u16"}]},{"name":"transferProtocolIfSharesToRevenuePool","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"ifRebalanceConfig","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"amount","type":"u64"}]},{"name":"depositIntoInsuranceFundStake","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"insuranceFundStake","isMut":true,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"amount","type":"u64"}]},{"name":"updatePythPullOracle","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"pythSolanaReceiver","isMut":false,"isSigner":false},{"name":"encodedVaa","isMut":false,"isSigner":false},{"name":"priceFeed","isMut":true,"isSigner":false}],"args":[{"name":"feedId","type":{"array":["u8",32]}},{"name":"params","type":"bytes"}]},{"name":"postPythPullOracleUpdateAtomic","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"pythSolanaReceiver","isMut":false,"isSigner":false},{"name":"guardianSet","isMut":false,"isSigner":false},{"name":"priceFeed","isMut":true,"isSigner":false}],"args":[{"name":"feedId","type":{"array":["u8",32]}},{"name":"params","type":"bytes"}]},{"name":"postMultiPythPullOracleUpdatesAtomic","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"pythSolanaReceiver","isMut":false,"isSigner":false},{"name":"guardianSet","isMut":false,"isSigner":false}],"args":[{"name":"params","type":"bytes"}]},{"name":"pauseSpotMarketDepositWithdraw","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"keeper","isMut":false,"isSigner":true},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":false,"isSigner":false}],"args":[]},{"name":"initialize","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":true,"isSigner":false},{"name":"quoteAssetMint","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeSpotMarket","accounts":[{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"spotMarketMint","isMut":false,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"optimalUtilization","type":"u32"},{"name":"optimalBorrowRate","type":"u32"},{"name":"maxBorrowRate","type":"u32"},{"name":"oracleSource","type":{"defined":"OracleSource"}},{"name":"initialAssetWeight","type":"u32"},{"name":"maintenanceAssetWeight","type":"u32"},{"name":"initialLiabilityWeight","type":"u32"},{"name":"maintenanceLiabilityWeight","type":"u32"},{"name":"imfFactor","type":"u32"},{"name":"liquidatorFee","type":"u32"},{"name":"ifLiquidationFee","type":"u32"},{"name":"activeStatus","type":"bool"},{"name":"assetTier","type":{"defined":"AssetTier"}},{"name":"scaleInitialAssetWeightStart","type":"u64"},{"name":"withdrawGuardThreshold","type":"u64"},{"name":"orderTickSize","type":"u64"},{"name":"orderStepSize","type":"u64"},{"name":"ifTotalFactor","type":"u32"},{"name":"name","type":{"array":["u8",32]}}]},{"name":"deleteInitializedSpotMarket","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":true,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"insuranceFundVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"initializeSerumFulfillmentConfig","accounts":[{"name":"baseSpotMarket","isMut":false,"isSigner":false},{"name":"quoteSpotMarket","isMut":false,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"serumProgram","isMut":false,"isSigner":false},{"name":"serumMarket","isMut":false,"isSigner":false},{"name":"serumOpenOrders","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"serumFulfillmentConfig","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"updateSerumFulfillmentConfigStatus","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"serumFulfillmentConfig","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true}],"args":[{"name":"status","type":{"defined":"SpotFulfillmentConfigStatus"}}]},{"name":"initializeOpenbookV2FulfillmentConfig","accounts":[{"name":"baseSpotMarket","isMut":false,"isSigner":false},{"name":"quoteSpotMarket","isMut":false,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"openbookV2Program","isMut":false,"isSigner":false},{"name":"openbookV2Market","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"openbookV2FulfillmentConfig","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"openbookV2FulfillmentConfigStatus","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"openbookV2FulfillmentConfig","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true}],"args":[{"name":"status","type":{"defined":"SpotFulfillmentConfigStatus"}}]},{"name":"initializePhoenixFulfillmentConfig","accounts":[{"name":"baseSpotMarket","isMut":false,"isSigner":false},{"name":"quoteSpotMarket","isMut":false,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"phoenixProgram","isMut":false,"isSigner":false},{"name":"phoenixMarket","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"phoenixFulfillmentConfig","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"phoenixFulfillmentConfigStatus","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"phoenixFulfillmentConfig","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true}],"args":[{"name":"status","type":{"defined":"SpotFulfillmentConfigStatus"}}]},{"name":"initializePerpMarket","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":true,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"ammBaseAssetReserve","type":"u128"},{"name":"ammQuoteAssetReserve","type":"u128"},{"name":"ammPeriodicity","type":"i64"},{"name":"ammPegMultiplier","type":"u128"},{"name":"oracleSource","type":{"defined":"OracleSource"}},{"name":"contractTier","type":{"defined":"ContractTier"}},{"name":"marginRatioInitial","type":"u32"},{"name":"marginRatioMaintenance","type":"u32"},{"name":"liquidatorFee","type":"u32"},{"name":"ifLiquidationFee","type":"u32"},{"name":"imfFactor","type":"u32"},{"name":"activeStatus","type":"bool"},{"name":"baseSpread","type":"u32"},{"name":"maxSpread","type":"u32"},{"name":"maxOpenInterest","type":"u128"},{"name":"maxRevenueWithdrawPerPeriod","type":"u64"},{"name":"quoteMaxInsurance","type":"u64"},{"name":"orderStepSize","type":"u64"},{"name":"orderTickSize","type":"u64"},{"name":"minOrderSize","type":"u64"},{"name":"concentrationCoefScale","type":"u128"},{"name":"curveUpdateIntensity","type":"u8"},{"name":"ammJitIntensity","type":"u8"},{"name":"name","type":{"array":["u8",32]}},{"name":"lpPoolId","type":"u8"}]},{"name":"initializeAmmCache","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"ammCache","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"addMarketToAmmCache","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"ammCache","isMut":true,"isSigner":false},{"name":"perpMarket","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"deleteAmmCache","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"ammCache","isMut":true,"isSigner":false}],"args":[]},{"name":"updateInitialAmmCacheInfo","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"ammCache","isMut":true,"isSigner":false}],"args":[]},{"name":"initializePredictionMarket","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[]},{"name":"deleteInitializedPerpMarket","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":true,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"}]},{"name":"moveAmmPrice","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"baseAssetReserve","type":"u128"},{"name":"quoteAssetReserve","type":"u128"},{"name":"sqrtK","type":"u128"}]},{"name":"recenterPerpMarketAmm","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"pegMultiplier","type":"u128"},{"name":"sqrtK","type":"u128"}]},{"name":"recenterPerpMarketAmmCrank","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"spotMarket","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false}],"args":[{"name":"depth","type":{"option":"u128"}}]},{"name":"updatePerpMarketAmmSummaryStats","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"spotMarket","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"UpdatePerpMarketSummaryStatsParams"}}]},{"name":"updatePerpMarketExpiry","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"expiryTs","type":"i64"}]},{"name":"updatePerpMarketLpPoolPausedOperations","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"lpPausedOperations","type":"u8"}]},{"name":"updatePerpMarketLpPoolStatus","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"ammCache","isMut":true,"isSigner":false}],"args":[{"name":"lpStatus","type":"u8"}]},{"name":"updatePerpMarketLpPoolFeeTransferScalar","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"optionalLpFeeTransferScalar","type":{"option":"u8"}},{"name":"optionalLpNetPnlTransferScalar","type":{"option":"u8"}}]},{"name":"settleExpiredMarketPoolsToRevenuePool","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[]},{"name":"depositIntoPerpMarketFeePool","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"sourceVault","isMut":true,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"quoteSpotMarket","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"updatePerpMarketPnlPool","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"depositIntoSpotMarketVault","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"sourceVault","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"depositIntoSpotMarketRevenuePool","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"repegAmmCurve","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"newPegCandidate","type":"u128"}]},{"name":"updatePerpMarketAmmOracleTwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[]},{"name":"resetPerpMarketAmmOracleTwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[]},{"name":"updateK","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false}],"args":[{"name":"sqrtK","type":"u128"}]},{"name":"updatePerpMarketMarginRatio","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"marginRatioInitial","type":"u32"},{"name":"marginRatioMaintenance","type":"u32"}]},{"name":"updatePerpMarketHighLeverageMarginRatio","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"marginRatioInitial","type":"u16"},{"name":"marginRatioMaintenance","type":"u16"}]},{"name":"updatePerpMarketFundingPeriod","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"fundingPeriod","type":"i64"}]},{"name":"updatePerpMarketMaxImbalances","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"unrealizedMaxImbalance","type":"u64"},{"name":"maxRevenueWithdrawPerPeriod","type":"u64"},{"name":"quoteMaxInsurance","type":"u64"}]},{"name":"updatePerpMarketLiquidationFee","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"liquidatorFee","type":"u32"},{"name":"ifLiquidationFee","type":"u32"}]},{"name":"updatePerpMarketLpPoolId","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"lpPoolId","type":"u8"}]},{"name":"updateInsuranceFundUnstakingPeriod","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"insuranceFundUnstakingPeriod","type":"i64"}]},{"name":"updateSpotMarketPoolId","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"poolId","type":"u8"}]},{"name":"updateSpotMarketLiquidationFee","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"liquidatorFee","type":"u32"},{"name":"ifLiquidationFee","type":"u32"}]},{"name":"updateWithdrawGuardThreshold","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"withdrawGuardThreshold","type":"u64"}]},{"name":"updateSpotMarketIfFactor","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"spotMarketIndex","type":"u16"},{"name":"userIfFactor","type":"u32"},{"name":"totalIfFactor","type":"u32"}]},{"name":"updateSpotMarketRevenueSettlePeriod","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"revenueSettlePeriod","type":"i64"}]},{"name":"updateSpotMarketStatus","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"status","type":{"defined":"MarketStatus"}}]},{"name":"updateSpotMarketPausedOperations","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"pausedOperations","type":"u8"}]},{"name":"updateSpotMarketAssetTier","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"assetTier","type":{"defined":"AssetTier"}}]},{"name":"updateSpotMarketMarginWeights","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"initialAssetWeight","type":"u32"},{"name":"maintenanceAssetWeight","type":"u32"},{"name":"initialLiabilityWeight","type":"u32"},{"name":"maintenanceLiabilityWeight","type":"u32"},{"name":"imfFactor","type":"u32"}]},{"name":"updateSpotMarketBorrowRate","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"optimalUtilization","type":"u32"},{"name":"optimalBorrowRate","type":"u32"},{"name":"maxBorrowRate","type":"u32"},{"name":"minBorrowRate","type":{"option":"u8"}}]},{"name":"updateSpotMarketMaxTokenDeposits","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"maxTokenDeposits","type":"u64"}]},{"name":"updateSpotMarketMaxTokenBorrows","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"maxTokenBorrowsFraction","type":"u16"}]},{"name":"updateSpotMarketScaleInitialAssetWeightStart","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"scaleInitialAssetWeightStart","type":"u64"}]},{"name":"updateSpotMarketOracle","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oldOracle","isMut":false,"isSigner":false}],"args":[{"name":"oracle","type":"publicKey"},{"name":"oracleSource","type":{"defined":"OracleSource"}},{"name":"skipInvariantCheck","type":"bool"}]},{"name":"updateSpotMarketStepSizeAndTickSize","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"stepSize","type":"u64"},{"name":"tickSize","type":"u64"}]},{"name":"updateSpotMarketMinOrderSize","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"orderSize","type":"u64"}]},{"name":"updateSpotMarketOrdersEnabled","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"ordersEnabled","type":"bool"}]},{"name":"updateSpotMarketIfPausedOperations","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"pausedOperations","type":"u8"}]},{"name":"updateSpotMarketName","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"name","type":{"array":["u8",32]}}]},{"name":"updatePerpMarketStatus","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"status","type":{"defined":"MarketStatus"}}]},{"name":"updatePerpMarketPausedOperations","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"pausedOperations","type":"u8"}]},{"name":"updatePerpMarketContractTier","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"contractTier","type":{"defined":"ContractTier"}}]},{"name":"updatePerpMarketImfFactor","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"imfFactor","type":"u32"},{"name":"unrealizedPnlImfFactor","type":"u32"}]},{"name":"updatePerpMarketUnrealizedAssetWeight","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"unrealizedInitialAssetWeight","type":"u32"},{"name":"unrealizedMaintenanceAssetWeight","type":"u32"}]},{"name":"updatePerpMarketConcentrationCoef","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"concentrationScale","type":"u128"}]},{"name":"updatePerpMarketCurveUpdateIntensity","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"curveUpdateIntensity","type":"u8"}]},{"name":"updatePerpMarketReferencePriceOffsetDeadbandPct","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"referencePriceOffsetDeadbandPct","type":"u8"}]},{"name":"updatePerpFeeStructure","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"feeStructure","type":{"defined":"FeeStructure"}}]},{"name":"updateSpotFeeStructure","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"feeStructure","type":{"defined":"FeeStructure"}}]},{"name":"updateInitialPctToLiquidate","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"initialPctToLiquidate","type":"u16"}]},{"name":"updateLiquidationDuration","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"liquidationDuration","type":"u8"}]},{"name":"updateLiquidationMarginBufferRatio","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"liquidationMarginBufferRatio","type":"u32"}]},{"name":"updateOracleGuardRails","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"oracleGuardRails","type":{"defined":"OracleGuardRails"}}]},{"name":"updateStateSettlementDuration","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"settlementDuration","type":"u16"}]},{"name":"updateStateMaxNumberOfSubAccounts","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"maxNumberOfSubAccounts","type":"u16"}]},{"name":"updateStateMaxInitializeUserFee","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"maxInitializeUserFee","type":"u16"}]},{"name":"updatePerpMarketOracle","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oldOracle","isMut":false,"isSigner":false},{"name":"ammCache","isMut":true,"isSigner":false}],"args":[{"name":"oracle","type":"publicKey"},{"name":"oracleSource","type":{"defined":"OracleSource"}},{"name":"skipInvariantCheck","type":"bool"}]},{"name":"updatePerpMarketBaseSpread","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"baseSpread","type":"u32"}]},{"name":"updateAmmJitIntensity","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"ammJitIntensity","type":"u8"}]},{"name":"updatePerpMarketMaxSpread","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"maxSpread","type":"u32"}]},{"name":"updatePerpMarketStepSizeAndTickSize","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"stepSize","type":"u64"},{"name":"tickSize","type":"u64"}]},{"name":"updatePerpMarketName","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"name","type":{"array":["u8",32]}}]},{"name":"updatePerpMarketMinOrderSize","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"orderSize","type":"u64"}]},{"name":"updatePerpMarketMaxSlippageRatio","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"maxSlippageRatio","type":"u16"}]},{"name":"updatePerpMarketMaxFillReserveFraction","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"maxFillReserveFraction","type":"u16"}]},{"name":"updatePerpMarketMaxOpenInterest","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"maxOpenInterest","type":"u128"}]},{"name":"updatePerpMarketNumberOfUsers","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"numberOfUsers","type":{"option":"u32"}},{"name":"numberOfUsersWithBase","type":{"option":"u32"}}]},{"name":"updatePerpMarketFeeAdjustment","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"feeAdjustment","type":"i16"}]},{"name":"updateSpotMarketFeeAdjustment","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false}],"args":[{"name":"feeAdjustment","type":"i16"}]},{"name":"updatePerpMarketProtectedMakerParams","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"protectedMakerLimitPriceDivisor","type":{"option":"u8"}},{"name":"protectedMakerDynamicDivisor","type":{"option":"u8"}}]},{"name":"updatePerpMarketOracleLowRiskSlotDelayOverride","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"oracleLowRiskSlotDelayOverride","type":"i8"}]},{"name":"updatePerpMarketAmmSpreadAdjustment","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"ammSpreadAdjustment","type":"i8"},{"name":"ammInventorySpreadAdjustment","type":"i8"},{"name":"referencePriceOffset","type":"i32"}]},{"name":"updatePerpMarketOracleSlotDelayOverride","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[{"name":"oracleSlotDelayOverride","type":"i8"}]},{"name":"updateAdmin","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"admin","type":"publicKey"}]},{"name":"updateDiscountMint","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"discountMint","type":"publicKey"}]},{"name":"updateExchangeStatus","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"exchangeStatus","type":"u8"}]},{"name":"updatePerpAuctionDuration","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"minPerpAuctionDuration","type":"u8"}]},{"name":"updateSpotAuctionDuration","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"defaultSpotAuctionDuration","type":"u8"}]},{"name":"initializePrelaunchOracle","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"prelaunchOracle","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"PrelaunchOracleParams"}}]},{"name":"updatePrelaunchOracleParams","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"prelaunchOracle","isMut":true,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"PrelaunchOracleParams"}}]},{"name":"deletePrelaunchOracle","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"prelaunchOracle","isMut":true,"isSigner":false},{"name":"perpMarket","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"perpMarketIndex","type":"u16"}]},{"name":"initializePythPullOracle","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"pythSolanaReceiver","isMut":false,"isSigner":false},{"name":"priceFeed","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"feedId","type":{"array":["u8",32]}}]},{"name":"initializePythLazerOracle","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"lazerOracle","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"feedId","type":"u32"}]},{"name":"postPythLazerOracleUpdate","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"pythLazerStorage","isMut":false,"isSigner":false},{"name":"ixSysvar","isMut":false,"isSigner":false}],"args":[{"name":"pythMessage","type":"bytes"}]},{"name":"initializeHighLeverageModeConfig","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"highLeverageModeConfig","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxUsers","type":"u32"}]},{"name":"updateHighLeverageModeConfig","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"highLeverageModeConfig","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"maxUsers","type":"u32"},{"name":"reduceOnly","type":"bool"},{"name":"currentUsers","type":{"option":"u32"}}]},{"name":"initializeProtectedMakerModeConfig","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"protectedMakerModeConfig","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxUsers","type":"u32"}]},{"name":"updateProtectedMakerModeConfig","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"protectedMakerModeConfig","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"maxUsers","type":"u32"},{"name":"reduceOnly","type":"bool"},{"name":"currentUsers","type":{"option":"u32"}}]},{"name":"adminDeposit","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"adminTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"amount","type":"u64"}]},{"name":"initializeIfRebalanceConfig","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"ifRebalanceConfig","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"IfRebalanceConfigParams"}}]},{"name":"updateIfRebalanceConfig","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"ifRebalanceConfig","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"IfRebalanceConfigParams"}}]},{"name":"updateFeatureBitFlagsMmOracle","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"enable","type":"bool"}]},{"name":"zeroMmOracleFields","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":true,"isSigner":false}],"args":[]},{"name":"updateFeatureBitFlagsMedianTriggerPrice","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"enable","type":"bool"}]},{"name":"updateFeatureBitFlagsBuilderCodes","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"enable","type":"bool"}]},{"name":"initializeRevenueShare","accounts":[{"name":"revenueShare","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeRevenueShareEscrow","accounts":[{"name":"escrow","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"userStats","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"numOrders","type":"u16"}]},{"name":"resizeRevenueShareEscrowOrders","accounts":[{"name":"escrow","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"numOrders","type":"u16"}]},{"name":"changeApprovedBuilder","accounts":[{"name":"escrow","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"builder","type":"publicKey"},{"name":"maxFeeBps","type":"u16"},{"name":"add","type":"bool"}]},{"name":"initializeLpPool","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"lpPool","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"lpPoolTokenVault","isMut":true,"isSigner":false},{"name":"ammConstituentMapping","isMut":true,"isSigner":false},{"name":"constituentTargetBase","isMut":true,"isSigner":false},{"name":"constituentCorrelations","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"lpPoolId","type":"u8"},{"name":"minMintFee","type":"i64"},{"name":"maxAum","type":"u128"},{"name":"maxSettleQuoteAmountPerMarket","type":"u64"},{"name":"whitelistMint","type":"publicKey"}]},{"name":"updateFeatureBitFlagsSettleLpPool","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"enable","type":"bool"}]},{"name":"updateFeatureBitFlagsSwapLpPool","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"enable","type":"bool"}]},{"name":"updateFeatureBitFlagsMintRedeemLpPool","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"state","isMut":true,"isSigner":false}],"args":[{"name":"enable","type":"bool"}]},{"name":"initializeConstituent","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"lpPool","isMut":true,"isSigner":false},{"name":"constituentTargetBase","isMut":true,"isSigner":false},{"name":"constituentCorrelations","isMut":true,"isSigner":false},{"name":"constituent","isMut":true,"isSigner":false},{"name":"spotMarket","isMut":false,"isSigner":false},{"name":"spotMarketMint","isMut":false,"isSigner":false},{"name":"constituentVault","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"spotMarketIndex","type":"u16"},{"name":"decimals","type":"u8"},{"name":"maxWeightDeviation","type":"i64"},{"name":"swapFeeMin","type":"i64"},{"name":"swapFeeMax","type":"i64"},{"name":"maxBorrowTokenAmount","type":"u64"},{"name":"oracleStalenessThreshold","type":"u64"},{"name":"costToTrade","type":"i32"},{"name":"constituentDerivativeIndex","type":{"option":"i16"}},{"name":"constituentDerivativeDepegThreshold","type":"u64"},{"name":"derivativeWeight","type":"u64"},{"name":"volatility","type":"u64"},{"name":"gammaExecution","type":"u8"},{"name":"gammaInventory","type":"u8"},{"name":"xi","type":"u8"},{"name":"newConstituentCorrelations","type":{"vec":"i64"}}]},{"name":"updateConstituentStatus","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"constituent","isMut":true,"isSigner":false}],"args":[{"name":"newStatus","type":"u8"}]},{"name":"updateConstituentPausedOperations","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"constituent","isMut":true,"isSigner":false}],"args":[{"name":"pausedOperations","type":"u8"}]},{"name":"updateConstituentParams","accounts":[{"name":"lpPool","isMut":false,"isSigner":false},{"name":"constituentTargetBase","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"constituent","isMut":true,"isSigner":false}],"args":[{"name":"constituentParams","type":{"defined":"ConstituentParams"}}]},{"name":"updateLpPoolParams","accounts":[{"name":"lpPool","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"lpPoolParams","type":{"defined":"LpPoolParams"}}]},{"name":"addAmmConstituentMappingData","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"ammConstituentMapping","isMut":true,"isSigner":false},{"name":"constituentTargetBase","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"ammConstituentMappingData","type":{"vec":{"defined":"AddAmmConstituentMappingDatum"}}}]},{"name":"updateAmmConstituentMappingData","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"ammConstituentMapping","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"ammConstituentMappingData","type":{"vec":{"defined":"AddAmmConstituentMappingDatum"}}}]},{"name":"removeAmmConstituentMappingData","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"ammConstituentMapping","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"perpMarketIndex","type":"u16"},{"name":"constituentIndex","type":"u16"}]},{"name":"updateConstituentCorrelationData","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"constituentCorrelations","isMut":true,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"index1","type":"u16"},{"name":"index2","type":"u16"},{"name":"correlation","type":"i64"}]},{"name":"updateLpConstituentTargetBase","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"keeper","isMut":true,"isSigner":true},{"name":"ammConstituentMapping","isMut":false,"isSigner":false},{"name":"constituentTargetBase","isMut":true,"isSigner":false},{"name":"ammCache","isMut":false,"isSigner":false},{"name":"lpPool","isMut":false,"isSigner":false}],"args":[]},{"name":"updateLpPoolAum","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"keeper","isMut":true,"isSigner":true},{"name":"lpPool","isMut":true,"isSigner":false},{"name":"constituentTargetBase","isMut":true,"isSigner":false},{"name":"ammCache","isMut":true,"isSigner":false}],"args":[]},{"name":"updateAmmCache","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"state","isMut":false,"isSigner":false},{"name":"ammCache","isMut":true,"isSigner":false},{"name":"quoteMarket","isMut":false,"isSigner":false}],"args":[]},{"name":"overrideAmmCacheInfo","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"ammCache","isMut":true,"isSigner":false}],"args":[{"name":"marketIndex","type":"u16"},{"name":"overrideParams","type":{"defined":"OverrideAmmCacheParams"}}]},{"name":"lpPoolSwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"constituentTargetBase","isMut":false,"isSigner":false},{"name":"constituentCorrelations","isMut":false,"isSigner":false},{"name":"constituentInTokenAccount","isMut":true,"isSigner":false},{"name":"constituentOutTokenAccount","isMut":true,"isSigner":false},{"name":"userInTokenAccount","isMut":true,"isSigner":false},{"name":"userOutTokenAccount","isMut":true,"isSigner":false},{"name":"inConstituent","isMut":true,"isSigner":false},{"name":"outConstituent","isMut":true,"isSigner":false},{"name":"inMarketMint","isMut":false,"isSigner":false},{"name":"outMarketMint","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"outMarketIndex","type":"u16"},{"name":"inAmount","type":"u64"},{"name":"minOutAmount","type":"u64"}]},{"name":"viewLpPoolSwapFees","accounts":[{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"constituentTargetBase","isMut":false,"isSigner":false},{"name":"constituentCorrelations","isMut":false,"isSigner":false},{"name":"constituentInTokenAccount","isMut":true,"isSigner":false},{"name":"constituentOutTokenAccount","isMut":true,"isSigner":false},{"name":"inConstituent","isMut":true,"isSigner":false},{"name":"outConstituent","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"outMarketIndex","type":"u16"},{"name":"inAmount","type":"u64"},{"name":"inTargetWeight","type":"i64"},{"name":"outTargetWeight","type":"i64"}]},{"name":"lpPoolAddLiquidity","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"lpPool","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"inMarketMint","isMut":false,"isSigner":false},{"name":"inConstituent","isMut":true,"isSigner":false},{"name":"userInTokenAccount","isMut":true,"isSigner":false},{"name":"constituentInTokenAccount","isMut":true,"isSigner":false},{"name":"userLpTokenAccount","isMut":true,"isSigner":false},{"name":"lpMint","isMut":true,"isSigner":false},{"name":"constituentTargetBase","isMut":false,"isSigner":false},{"name":"lpPoolTokenVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"inAmount","type":"u128"},{"name":"minMintAmount","type":"u64"}]},{"name":"lpPoolRemoveLiquidity","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"lpPool","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"outMarketMint","isMut":false,"isSigner":false},{"name":"outConstituent","isMut":true,"isSigner":false},{"name":"userOutTokenAccount","isMut":true,"isSigner":false},{"name":"constituentOutTokenAccount","isMut":true,"isSigner":false},{"name":"userLpTokenAccount","isMut":true,"isSigner":false},{"name":"spotMarketTokenAccount","isMut":true,"isSigner":false},{"name":"lpMint","isMut":true,"isSigner":false},{"name":"constituentTargetBase","isMut":false,"isSigner":false},{"name":"lpPoolTokenVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"ammCache","isMut":false,"isSigner":false}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"inAmount","type":"u64"},{"name":"minOutAmount","type":"u128"}]},{"name":"viewLpPoolAddLiquidityFees","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"inMarketMint","isMut":false,"isSigner":false},{"name":"inConstituent","isMut":false,"isSigner":false},{"name":"lpMint","isMut":false,"isSigner":false},{"name":"constituentTargetBase","isMut":false,"isSigner":false}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"inAmount","type":"u128"}]},{"name":"viewLpPoolRemoveLiquidityFees","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"outMarketMint","isMut":false,"isSigner":false},{"name":"outConstituent","isMut":false,"isSigner":false},{"name":"lpMint","isMut":false,"isSigner":false},{"name":"constituentTargetBase","isMut":false,"isSigner":false}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"inAmount","type":"u64"}]},{"name":"beginLpSwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"signerOutTokenAccount","isMut":true,"isSigner":false,"docs":["Signer token accounts"]},{"name":"signerInTokenAccount","isMut":true,"isSigner":false},{"name":"constituentOutTokenAccount","isMut":true,"isSigner":false,"docs":["Constituent token accounts"]},{"name":"constituentInTokenAccount","isMut":true,"isSigner":false},{"name":"outConstituent","isMut":true,"isSigner":false,"docs":["Constituents"]},{"name":"inConstituent","isMut":true,"isSigner":false},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"instructions","isMut":false,"isSigner":false,"docs":["Instructions Sysvar for instruction introspection"]},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"outMarketIndex","type":"u16"},{"name":"amountIn","type":"u64"}]},{"name":"endLpSwap","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"signerOutTokenAccount","isMut":true,"isSigner":false,"docs":["Signer token accounts"]},{"name":"signerInTokenAccount","isMut":true,"isSigner":false},{"name":"constituentOutTokenAccount","isMut":true,"isSigner":false,"docs":["Constituent token accounts"]},{"name":"constituentInTokenAccount","isMut":true,"isSigner":false},{"name":"outConstituent","isMut":true,"isSigner":false,"docs":["Constituents"]},{"name":"inConstituent","isMut":true,"isSigner":false},{"name":"lpPool","isMut":false,"isSigner":false},{"name":"instructions","isMut":false,"isSigner":false,"docs":["Instructions Sysvar for instruction introspection"]},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"inMarketIndex","type":"u16"},{"name":"outMarketIndex","type":"u16"}]},{"name":"updateConstituentOracleInfo","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"keeper","isMut":true,"isSigner":true},{"name":"constituent","isMut":true,"isSigner":false},{"name":"spotMarket","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false}],"args":[]},{"name":"depositToProgramVault","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"constituent","isMut":true,"isSigner":false},{"name":"constituentTokenAccount","isMut":true,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdrawFromProgramVault","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"driftSigner","isMut":false,"isSigner":false},{"name":"constituent","isMut":true,"isSigner":false},{"name":"constituentTokenAccount","isMut":true,"isSigner":false},{"name":"spotMarket","isMut":true,"isSigner":false},{"name":"spotMarketVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"settlePerpToLpPool","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"lpPool","isMut":true,"isSigner":false},{"name":"keeper","isMut":true,"isSigner":true},{"name":"ammCache","isMut":true,"isSigner":false},{"name":"quoteMarket","isMut":true,"isSigner":false},{"name":"constituent","isMut":true,"isSigner":false},{"name":"constituentQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"quoteTokenVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"driftSigner","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"AmmCache","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"padding","type":{"array":["u8",3]}},{"name":"cache","type":{"vec":{"defined":"CacheInfo"}}}]}},{"name":"OpenbookV2FulfillmentConfig","type":{"kind":"struct","fields":[{"name":"pubkey","type":"publicKey"},{"name":"openbookV2ProgramId","type":"publicKey"},{"name":"openbookV2Market","type":"publicKey"},{"name":"openbookV2MarketAuthority","type":"publicKey"},{"name":"openbookV2EventHeap","type":"publicKey"},{"name":"openbookV2Bids","type":"publicKey"},{"name":"openbookV2Asks","type":"publicKey"},{"name":"openbookV2BaseVault","type":"publicKey"},{"name":"openbookV2QuoteVault","type":"publicKey"},{"name":"marketIndex","type":"u16"},{"name":"fulfillmentType","type":{"defined":"SpotFulfillmentType"}},{"name":"status","type":{"defined":"SpotFulfillmentConfigStatus"}},{"name":"padding","type":{"array":["u8",4]}}]}},{"name":"PhoenixV1FulfillmentConfig","type":{"kind":"struct","fields":[{"name":"pubkey","type":"publicKey"},{"name":"phoenixProgramId","type":"publicKey"},{"name":"phoenixLogAuthority","type":"publicKey"},{"name":"phoenixMarket","type":"publicKey"},{"name":"phoenixBaseVault","type":"publicKey"},{"name":"phoenixQuoteVault","type":"publicKey"},{"name":"marketIndex","type":"u16"},{"name":"fulfillmentType","type":{"defined":"SpotFulfillmentType"}},{"name":"status","type":{"defined":"SpotFulfillmentConfigStatus"}},{"name":"padding","type":{"array":["u8",4]}}]}},{"name":"SerumV3FulfillmentConfig","type":{"kind":"struct","fields":[{"name":"pubkey","type":"publicKey"},{"name":"serumProgramId","type":"publicKey"},{"name":"serumMarket","type":"publicKey"},{"name":"serumRequestQueue","type":"publicKey"},{"name":"serumEventQueue","type":"publicKey"},{"name":"serumBids","type":"publicKey"},{"name":"serumAsks","type":"publicKey"},{"name":"serumBaseVault","type":"publicKey"},{"name":"serumQuoteVault","type":"publicKey"},{"name":"serumOpenOrders","type":"publicKey"},{"name":"serumSignerNonce","type":"u64"},{"name":"marketIndex","type":"u16"},{"name":"fulfillmentType","type":{"defined":"SpotFulfillmentType"}},{"name":"status","type":{"defined":"SpotFulfillmentConfigStatus"}},{"name":"padding","type":{"array":["u8",4]}}]}},{"name":"HighLeverageModeConfig","type":{"kind":"struct","fields":[{"name":"maxUsers","type":"u32"},{"name":"currentUsers","type":"u32"},{"name":"reduceOnly","type":"u8"},{"name":"padding1","type":{"array":["u8",3]}},{"name":"currentMaintenanceUsers","type":"u32"},{"name":"padding2","type":{"array":["u8",24]}}]}},{"name":"IfRebalanceConfig","type":{"kind":"struct","fields":[{"name":"pubkey","type":"publicKey"},{"name":"totalInAmount","docs":["total amount to be sold"],"type":"u64"},{"name":"currentInAmount","docs":["amount already sold"],"type":"u64"},{"name":"currentOutAmount","docs":["amount already bought"],"type":"u64"},{"name":"currentOutAmountTransferred","docs":["amount already transferred to revenue pool"],"type":"u64"},{"name":"currentInAmountSinceLastTransfer","docs":["amount already bought in epoch"],"type":"u64"},{"name":"epochStartTs","docs":["start time of epoch"],"type":"i64"},{"name":"epochInAmount","docs":["amount already bought in epoch"],"type":"u64"},{"name":"epochMaxInAmount","docs":["max amount to swap in epoch"],"type":"u64"},{"name":"epochDuration","docs":["duration of epoch"],"type":"i64"},{"name":"outMarketIndex","docs":["market index to sell"],"type":"u16"},{"name":"inMarketIndex","docs":["market index to buy"],"type":"u16"},{"name":"maxSlippageBps","type":"u16"},{"name":"swapMode","type":"u8"},{"name":"status","type":"u8"},{"name":"padding2","type":{"array":["u8",32]}}]}},{"name":"InsuranceFundStake","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"ifShares","type":"u128"},{"name":"lastWithdrawRequestShares","type":"u128"},{"name":"ifBase","type":"u128"},{"name":"lastValidTs","type":"i64"},{"name":"lastWithdrawRequestValue","type":"u64"},{"name":"lastWithdrawRequestTs","type":"i64"},{"name":"costBasis","type":"i64"},{"name":"marketIndex","type":"u16"},{"name":"padding","type":{"array":["u8",14]}}]}},{"name":"ProtocolIfSharesTransferConfig","type":{"kind":"struct","fields":[{"name":"whitelistedSigners","type":{"array":["publicKey",4]}},{"name":"maxTransferPerEpoch","type":"u128"},{"name":"currentEpochTransfer","type":"u128"},{"name":"nextEpochTs","type":"i64"},{"name":"padding","type":{"array":["u128",8]}}]}},{"name":"LPPool","type":{"kind":"struct","fields":[{"name":"pubkey","docs":["address of the vault."],"type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"whitelistMint","type":"publicKey"},{"name":"constituentTargetBase","type":"publicKey"},{"name":"constituentCorrelations","type":"publicKey"},{"name":"maxAum","docs":["The current number of VaultConstituents in the vault, each constituent is pda(LPPool.address, constituent_index)","which constituent is the quote, receives revenue pool distributions. (maybe this should just be implied idx 0)","pub quote_constituent_index: u16,","QUOTE_PRECISION: Max AUM, Prohibit minting new DLP beyond this"],"type":"u128"},{"name":"lastAum","docs":["QUOTE_PRECISION: AUM of the vault in USD, updated lazily"],"type":"u128"},{"name":"cumulativeQuoteSentToPerpMarkets","docs":["QUOTE PRECISION: Cumulative quotes from settles"],"type":"u128"},{"name":"cumulativeQuoteReceivedFromPerpMarkets","type":"u128"},{"name":"totalMintRedeemFeesPaid","docs":["QUOTE_PRECISION: Total fees paid for minting and redeeming LP tokens"],"type":"i128"},{"name":"lastAumSlot","docs":["timestamp of last AUM slot"],"type":"u64"},{"name":"maxSettleQuoteAmount","type":"u64"},{"name":"padding","docs":["timestamp of last vAMM revenue rebalance"],"type":"u64"},{"name":"mintRedeemId","docs":["Every mint/redeem has a monotonically increasing id. This is the next id to use"],"type":"u64"},{"name":"settleId","type":"u64"},{"name":"minMintFee","docs":["PERCENTAGE_PRECISION"],"type":"i64"},{"name":"tokenSupply","type":"u64"},{"name":"volatility","type":"u64"},{"name":"constituents","type":"u16"},{"name":"quoteConsituentIndex","type":"u16"},{"name":"bump","type":"u8"},{"name":"gammaExecution","type":"u8"},{"name":"xi","type":"u8"},{"name":"targetOracleDelayFeeBpsPer10Slots","type":"u8"},{"name":"targetPositionDelayFeeBpsPer10Slots","type":"u8"},{"name":"lpPoolId","type":"u8"},{"name":"padding","type":{"array":["u8",174]}}]}},{"name":"Constituent","type":{"kind":"struct","fields":[{"name":"pubkey","docs":["address of the constituent"],"type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"lpPool","type":"publicKey"},{"name":"vault","type":"publicKey"},{"name":"totalSwapFees","docs":["total fees received by the constituent. Positive = fees received, Negative = fees paid"],"type":"i128"},{"name":"spotBalance","docs":["spot borrow-lend balance for constituent"],"type":{"defined":"ConstituentSpotBalance"}},{"name":"lastSpotBalanceTokenAmount","type":"i64"},{"name":"cumulativeSpotInterestAccruedTokenAmount","type":"i64"},{"name":"maxWeightDeviation","docs":["max deviation from target_weight allowed for the constituent","precision: PERCENTAGE_PRECISION"],"type":"i64"},{"name":"swapFeeMin","docs":["min fee charged on swaps to/from this constituent","precision: PERCENTAGE_PRECISION"],"type":"i64"},{"name":"swapFeeMax","docs":["max fee charged on swaps to/from this constituent","precision: PERCENTAGE_PRECISION"],"type":"i64"},{"name":"maxBorrowTokenAmount","docs":["Max Borrow amount:","precision: token precision"],"type":"u64"},{"name":"vaultTokenBalance","docs":["ata token balance in token precision"],"type":"u64"},{"name":"lastOraclePrice","type":"i64"},{"name":"lastOracleSlot","type":"u64"},{"name":"oracleStalenessThreshold","docs":["Delay allowed for valid AUM calculation"],"type":"u64"},{"name":"flashLoanInitialTokenAmount","type":"u64"},{"name":"nextSwapId","docs":["Every swap to/from this constituent has a monotonically increasing id. This is the next id to use"],"type":"u64"},{"name":"derivativeWeight","docs":["percentable of derivatve weight to go to this specific derivative PERCENTAGE_PRECISION. Zero if no derivative weight"],"type":"u64"},{"name":"volatility","type":"u64"},{"name":"constituentDerivativeDepegThreshold","type":"u64"},{"name":"constituentDerivativeIndex","docs":["The `constituent_index` of the parent constituent. -1 if it is a parent index","Example: if in a pool with SOL (parent) and dSOL (derivative),","SOL.constituent_index = 1, SOL.constituent_derivative_index = -1,","dSOL.constituent_index = 2, dSOL.constituent_derivative_index = 1"],"type":"i16"},{"name":"spotMarketIndex","type":"u16"},{"name":"constituentIndex","type":"u16"},{"name":"decimals","type":"u8"},{"name":"bump","type":"u8"},{"name":"vaultBump","type":"u8"},{"name":"gammaInventory","type":"u8"},{"name":"gammaExecution","type":"u8"},{"name":"xi","type":"u8"},{"name":"status","type":"u8"},{"name":"pausedOperations","type":"u8"},{"name":"padding","type":{"array":["u8",162]}}]}},{"name":"AmmConstituentMapping","type":{"kind":"struct","fields":[{"name":"lpPool","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"padding","type":{"array":["u8",3]}},{"name":"weights","type":{"vec":{"defined":"AmmConstituentDatum"}}}]}},{"name":"ConstituentTargetBase","type":{"kind":"struct","fields":[{"name":"lpPool","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"padding","type":{"array":["u8",3]}},{"name":"targets","type":{"vec":{"defined":"TargetsDatum"}}}]}},{"name":"ConstituentCorrelations","type":{"kind":"struct","fields":[{"name":"lpPool","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"padding","type":{"array":["u8",3]}},{"name":"correlations","type":{"vec":"i64"}}]}},{"name":"PrelaunchOracle","type":{"kind":"struct","fields":[{"name":"price","type":"i64"},{"name":"maxPrice","type":"i64"},{"name":"confidence","type":"u64"},{"name":"lastUpdateSlot","type":"u64"},{"name":"ammLastUpdateSlot","type":"u64"},{"name":"perpMarketIndex","type":"u16"},{"name":"padding","type":{"array":["u8",70]}}]}},{"name":"PerpMarket","type":{"kind":"struct","fields":[{"name":"pubkey","docs":["The perp market's address. It is a pda of the market index"],"type":"publicKey"},{"name":"amm","docs":["The automated market maker"],"type":{"defined":"AMM"}},{"name":"pnlPool","docs":["The market's pnl pool. When users settle negative pnl, the balance increases.","When users settle positive pnl, the balance decreases. Can not go negative."],"type":{"defined":"PoolBalance"}},{"name":"name","docs":["Encoded display name for the perp market e.g. SOL-PERP"],"type":{"array":["u8",32]}},{"name":"insuranceClaim","docs":["The perp market's claim on the insurance fund"],"type":{"defined":"InsuranceClaim"}},{"name":"unrealizedPnlMaxImbalance","docs":["The max pnl imbalance before positive pnl asset weight is discounted","pnl imbalance is the difference between long and short pnl. When it's greater than 0,","the amm has negative pnl and the initial asset weight for positive pnl is discounted","precision = QUOTE_PRECISION"],"type":"u64"},{"name":"expiryTs","docs":["The ts when the market will be expired. Only set if market is in reduce only mode"],"type":"i64"},{"name":"expiryPrice","docs":["The price at which positions will be settled. Only set if market is expired","precision = PRICE_PRECISION"],"type":"i64"},{"name":"nextFillRecordId","docs":["Every trade has a fill record id. This is the next id to be used"],"type":"u64"},{"name":"nextFundingRateRecordId","docs":["Every funding rate update has a record id. This is the next id to be used"],"type":"u64"},{"name":"nextCurveRecordId","docs":["Every amm k updated has a record id. This is the next id to be used"],"type":"u64"},{"name":"imfFactor","docs":["The initial margin fraction factor. Used to increase margin ratio for large positions","precision: MARGIN_PRECISION"],"type":"u32"},{"name":"unrealizedPnlImfFactor","docs":["The imf factor for unrealized pnl. Used to discount asset weight for large positive pnl","precision: MARGIN_PRECISION"],"type":"u32"},{"name":"liquidatorFee","docs":["The fee the liquidator is paid for taking over perp position","precision: LIQUIDATOR_FEE_PRECISION"],"type":"u32"},{"name":"ifLiquidationFee","docs":["The fee the insurance fund receives from liquidation","precision: LIQUIDATOR_FEE_PRECISION"],"type":"u32"},{"name":"marginRatioInitial","docs":["The margin ratio which determines how much collateral is required to open a position","e.g. margin ratio of .1 means a user must have $100 of total collateral to open a $1000 position","precision: MARGIN_PRECISION"],"type":"u32"},{"name":"marginRatioMaintenance","docs":["The margin ratio which determines when a user will be liquidated","e.g. margin ratio of .05 means a user must have $50 of total collateral to maintain a $1000 position","else they will be liquidated","precision: MARGIN_PRECISION"],"type":"u32"},{"name":"unrealizedPnlInitialAssetWeight","docs":["The initial asset weight for positive pnl. Negative pnl always has an asset weight of 1","precision: SPOT_WEIGHT_PRECISION"],"type":"u32"},{"name":"unrealizedPnlMaintenanceAssetWeight","docs":["The maintenance asset weight for positive pnl. Negative pnl always has an asset weight of 1","precision: SPOT_WEIGHT_PRECISION"],"type":"u32"},{"name":"numberOfUsersWithBase","docs":["number of users in a position (base)"],"type":"u32"},{"name":"numberOfUsers","docs":["number of users in a position (pnl) or pnl (quote)"],"type":"u32"},{"name":"marketIndex","type":"u16"},{"name":"status","docs":["Whether a market is active, reduce only, expired, etc","Affects whether users can open/close positions"],"type":{"defined":"MarketStatus"}},{"name":"contractType","docs":["Currently only Perpetual markets are supported"],"type":{"defined":"ContractType"}},{"name":"contractTier","docs":["The contract tier determines how much insurance a market can receive, with more speculative markets receiving less insurance","It also influences the order perp markets can be liquidated, with less speculative markets being liquidated first"],"type":{"defined":"ContractTier"}},{"name":"pausedOperations","type":"u8"},{"name":"quoteSpotMarketIndex","docs":["The spot market that pnl is settled in"],"type":"u16"},{"name":"feeAdjustment","docs":["Between -100 and 100, represents what % to increase/decrease the fee by","E.g. if this is -50 and the fee is 5bps, the new fee will be 2.5bps","if this is 50 and the fee is 5bps, the new fee will be 7.5bps"],"type":"i16"},{"name":"fuelBoostPosition","docs":["fuel multiplier for perp funding","precision: 10"],"type":"u8"},{"name":"fuelBoostTaker","docs":["fuel multiplier for perp taker","precision: 10"],"type":"u8"},{"name":"fuelBoostMaker","docs":["fuel multiplier for perp maker","precision: 10"],"type":"u8"},{"name":"poolId","type":"u8"},{"name":"highLeverageMarginRatioInitial","type":"u16"},{"name":"highLeverageMarginRatioMaintenance","type":"u16"},{"name":"protectedMakerLimitPriceDivisor","type":"u8"},{"name":"protectedMakerDynamicDivisor","type":"u8"},{"name":"lpFeeTransferScalar","type":"u8"},{"name":"lpStatus","type":"u8"},{"name":"lpPausedOperations","type":"u8"},{"name":"lpExchangeFeeExcluscionScalar","type":"u8"},{"name":"lastFillPrice","type":"u64"},{"name":"lpPoolId","type":"u8"},{"name":"padding","type":{"array":["u8",23]}}]}},{"name":"ProtectedMakerModeConfig","type":{"kind":"struct","fields":[{"name":"maxUsers","type":"u32"},{"name":"currentUsers","type":"u32"},{"name":"reduceOnly","type":"u8"},{"name":"padding","type":{"array":["u8",31]}}]}},{"name":"PythLazerOracle","type":{"kind":"struct","fields":[{"name":"price","type":"i64"},{"name":"publishTime","type":"u64"},{"name":"postedSlot","type":"u64"},{"name":"exponent","type":"i32"},{"name":"padding","type":{"array":["u8",4]}},{"name":"conf","type":"u64"}]}},{"name":"RevenueShare","type":{"kind":"struct","fields":[{"name":"authority","docs":["the owner of this account, a builder or referrer"],"type":"publicKey"},{"name":"totalReferrerRewards","type":"u64"},{"name":"totalBuilderRewards","type":"u64"},{"name":"padding","type":{"array":["u8",18]}}]}},{"name":"RevenueShareEscrow","type":{"kind":"struct","fields":[{"name":"authority","docs":["the owner of this account, a user"],"type":"publicKey"},{"name":"referrer","type":"publicKey"},{"name":"referrerBoostExpireTs","type":"u32"},{"name":"referrerRewardOffset","type":"i8"},{"name":"refereeFeeNumeratorOffset","type":"i8"},{"name":"referrerBoostNumerator","type":"i8"},{"name":"reservedFixed","type":{"array":["u8",17]}},{"name":"padding0","type":"u32"},{"name":"orders","type":{"vec":{"defined":"RevenueShareOrder"}}},{"name":"padding1","type":"u32"},{"name":"approvedBuilders","type":{"vec":{"defined":"BuilderInfo"}}}]}},{"name":"SignedMsgUserOrders","docs":["* This struct is a duplicate of SignedMsgUserOrdersZeroCopy\n * It is used to give anchor an struct to generate the idl for clients\n * The struct SignedMsgUserOrdersZeroCopy is used to load the data in efficiently"],"type":{"kind":"struct","fields":[{"name":"authorityPubkey","type":"publicKey"},{"name":"padding","type":"u32"},{"name":"signedMsgOrderData","type":{"vec":{"defined":"SignedMsgOrderId"}}}]}},{"name":"SignedMsgWsDelegates","docs":["* Used to store authenticated delegates for swift-like ws connections"],"type":{"kind":"struct","fields":[{"name":"delegates","type":{"vec":"publicKey"}}]}},{"name":"SpotMarket","type":{"kind":"struct","fields":[{"name":"pubkey","docs":["The address of the spot market. It is a pda of the market index"],"type":"publicKey"},{"name":"oracle","docs":["The oracle used to price the markets deposits/borrows"],"type":"publicKey"},{"name":"mint","docs":["The token mint of the market"],"type":"publicKey"},{"name":"vault","docs":["The vault used to store the market's deposits","The amount in the vault should be equal to or greater than deposits - borrows"],"type":"publicKey"},{"name":"name","docs":["The encoded display name for the market e.g. SOL"],"type":{"array":["u8",32]}},{"name":"historicalOracleData","type":{"defined":"HistoricalOracleData"}},{"name":"historicalIndexData","type":{"defined":"HistoricalIndexData"}},{"name":"revenuePool","docs":["Revenue the protocol has collected in this markets token","e.g. for SOL-PERP, funds can be settled in usdc and will flow into the USDC revenue pool"],"type":{"defined":"PoolBalance"}},{"name":"spotFeePool","docs":["The fees collected from swaps between this market and the quote market","Is settled to the quote markets revenue pool"],"type":{"defined":"PoolBalance"}},{"name":"insuranceFund","docs":["Details on the insurance fund covering bankruptcies in this markets token","Covers bankruptcies for borrows with this markets token and perps settling in this markets token"],"type":{"defined":"InsuranceFund"}},{"name":"totalSpotFee","docs":["The total spot fees collected for this market","precision: QUOTE_PRECISION"],"type":"u128"},{"name":"depositBalance","docs":["The sum of the scaled balances for deposits across users and pool balances","To convert to the deposit token amount, multiply by the cumulative deposit interest","precision: SPOT_BALANCE_PRECISION"],"type":"u128"},{"name":"borrowBalance","docs":["The sum of the scaled balances for borrows across users and pool balances","To convert to the borrow token amount, multiply by the cumulative borrow interest","precision: SPOT_BALANCE_PRECISION"],"type":"u128"},{"name":"cumulativeDepositInterest","docs":["The cumulative interest earned by depositors","Used to calculate the deposit token amount from the deposit balance","precision: SPOT_CUMULATIVE_INTEREST_PRECISION"],"type":"u128"},{"name":"cumulativeBorrowInterest","docs":["The cumulative interest earned by borrowers","Used to calculate the borrow token amount from the borrow balance","precision: SPOT_CUMULATIVE_INTEREST_PRECISION"],"type":"u128"},{"name":"totalSocialLoss","docs":["The total socialized loss from borrows, in the mint's token","precision: token mint precision"],"type":"u128"},{"name":"totalQuoteSocialLoss","docs":["The total socialized loss from borrows, in the quote market's token","preicision: QUOTE_PRECISION"],"type":"u128"},{"name":"withdrawGuardThreshold","docs":["no withdraw limits/guards when deposits below this threshold","precision: token mint precision"],"type":"u64"},{"name":"maxTokenDeposits","docs":["The max amount of token deposits in this market","0 if there is no limit","precision: token mint precision"],"type":"u64"},{"name":"depositTokenTwap","docs":["24hr average of deposit token amount","precision: token mint precision"],"type":"u64"},{"name":"borrowTokenTwap","docs":["24hr average of borrow token amount","precision: token mint precision"],"type":"u64"},{"name":"utilizationTwap","docs":["24hr average of utilization","which is borrow amount over token amount","precision: SPOT_UTILIZATION_PRECISION"],"type":"u64"},{"name":"lastInterestTs","docs":["Last time the cumulative deposit and borrow interest was updated"],"type":"u64"},{"name":"lastTwapTs","docs":["Last time the deposit/borrow/utilization averages were updated"],"type":"u64"},{"name":"expiryTs","docs":["The time the market is set to expire. Only set if market is in reduce only mode"],"type":"i64"},{"name":"orderStepSize","docs":["Spot orders must be a multiple of the step size","precision: token mint precision"],"type":"u64"},{"name":"orderTickSize","docs":["Spot orders must be a multiple of the tick size","precision: PRICE_PRECISION"],"type":"u64"},{"name":"minOrderSize","docs":["The minimum order size","precision: token mint precision"],"type":"u64"},{"name":"maxPositionSize","docs":["The maximum spot position size","if the limit is 0, there is no limit","precision: token mint precision"],"type":"u64"},{"name":"nextFillRecordId","docs":["Every spot trade has a fill record id. This is the next id to use"],"type":"u64"},{"name":"nextDepositRecordId","docs":["Every deposit has a deposit record id. This is the next id to use"],"type":"u64"},{"name":"initialAssetWeight","docs":["The initial asset weight used to calculate a deposits contribution to a users initial total collateral","e.g. if the asset weight is .8, $100 of deposits contributes $80 to the users initial total collateral","precision: SPOT_WEIGHT_PRECISION"],"type":"u32"},{"name":"maintenanceAssetWeight","docs":["The maintenance asset weight used to calculate a deposits contribution to a users maintenance total collateral","e.g. if the asset weight is .9, $100 of deposits contributes $90 to the users maintenance total collateral","precision: SPOT_WEIGHT_PRECISION"],"type":"u32"},{"name":"initialLiabilityWeight","docs":["The initial liability weight used to calculate a borrows contribution to a users initial margin requirement","e.g. if the liability weight is .9, $100 of borrows contributes $90 to the users initial margin requirement","precision: SPOT_WEIGHT_PRECISION"],"type":"u32"},{"name":"maintenanceLiabilityWeight","docs":["The maintenance liability weight used to calculate a borrows contribution to a users maintenance margin requirement","e.g. if the liability weight is .8, $100 of borrows contributes $80 to the users maintenance margin requirement","precision: SPOT_WEIGHT_PRECISION"],"type":"u32"},{"name":"imfFactor","docs":["The initial margin fraction factor. Used to increase liability weight/decrease asset weight for large positions","precision: MARGIN_PRECISION"],"type":"u32"},{"name":"liquidatorFee","docs":["The fee the liquidator is paid for taking over borrow/deposit","precision: LIQUIDATOR_FEE_PRECISION"],"type":"u32"},{"name":"ifLiquidationFee","docs":["The fee the insurance fund receives from liquidation","precision: LIQUIDATOR_FEE_PRECISION"],"type":"u32"},{"name":"optimalUtilization","docs":["The optimal utilization rate for this market.","Used to determine the markets borrow rate","precision: SPOT_UTILIZATION_PRECISION"],"type":"u32"},{"name":"optimalBorrowRate","docs":["The borrow rate for this market when the market has optimal utilization","precision: SPOT_RATE_PRECISION"],"type":"u32"},{"name":"maxBorrowRate","docs":["The borrow rate for this market when the market has 1000 utilization","precision: SPOT_RATE_PRECISION"],"type":"u32"},{"name":"decimals","docs":["The market's token mint's decimals. To from decimals to a precision, 10^decimals"],"type":"u32"},{"name":"marketIndex","type":"u16"},{"name":"ordersEnabled","docs":["Whether or not spot trading is enabled"],"type":"bool"},{"name":"oracleSource","type":{"defined":"OracleSource"}},{"name":"status","type":{"defined":"MarketStatus"}},{"name":"assetTier","docs":["The asset tier affects how a deposit can be used as collateral and the priority for a borrow being liquidated"],"type":{"defined":"AssetTier"}},{"name":"pausedOperations","type":"u8"},{"name":"ifPausedOperations","type":"u8"},{"name":"feeAdjustment","type":"i16"},{"name":"maxTokenBorrowsFraction","docs":["What fraction of max_token_deposits","disabled when 0, 1 => 1/10000 => .01% of max_token_deposits","precision: X/10000"],"type":"u16"},{"name":"flashLoanAmount","docs":["For swaps, the amount of token loaned out in the begin_swap ix","precision: token mint precision"],"type":"u64"},{"name":"flashLoanInitialTokenAmount","docs":["For swaps, the amount in the users token account in the begin_swap ix","Used to calculate how much of the token left the system in end_swap ix","precision: token mint precision"],"type":"u64"},{"name":"totalSwapFee","docs":["The total fees received from swaps","precision: token mint precision"],"type":"u64"},{"name":"scaleInitialAssetWeightStart","docs":["When to begin scaling down the initial asset weight","disabled when 0","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"minBorrowRate","docs":["The min borrow rate for this market when the market regardless of utilization","1 => 1/200 => .5%","precision: X/200"],"type":"u8"},{"name":"fuelBoostDeposits","docs":["fuel multiplier for spot deposits","precision: 10"],"type":"u8"},{"name":"fuelBoostBorrows","docs":["fuel multiplier for spot borrows","precision: 10"],"type":"u8"},{"name":"fuelBoostTaker","docs":["fuel multiplier for spot taker","precision: 10"],"type":"u8"},{"name":"fuelBoostMaker","docs":["fuel multiplier for spot maker","precision: 10"],"type":"u8"},{"name":"fuelBoostInsurance","docs":["fuel multiplier for spot insurance stake","precision: 10"],"type":"u8"},{"name":"tokenProgramFlag","type":"u8"},{"name":"poolId","type":"u8"},{"name":"padding","type":{"array":["u8",40]}}]}},{"name":"State","type":{"kind":"struct","fields":[{"name":"admin","type":"publicKey"},{"name":"whitelistMint","type":"publicKey"},{"name":"discountMint","type":"publicKey"},{"name":"signer","type":"publicKey"},{"name":"srmVault","type":"publicKey"},{"name":"perpFeeStructure","type":{"defined":"FeeStructure"}},{"name":"spotFeeStructure","type":{"defined":"FeeStructure"}},{"name":"oracleGuardRails","type":{"defined":"OracleGuardRails"}},{"name":"numberOfAuthorities","type":"u64"},{"name":"numberOfSubAccounts","type":"u64"},{"name":"lpCooldownTime","type":"u64"},{"name":"liquidationMarginBufferRatio","type":"u32"},{"name":"settlementDuration","type":"u16"},{"name":"numberOfMarkets","type":"u16"},{"name":"numberOfSpotMarkets","type":"u16"},{"name":"signerNonce","type":"u8"},{"name":"minPerpAuctionDuration","type":"u8"},{"name":"defaultMarketOrderTimeInForce","type":"u8"},{"name":"defaultSpotAuctionDuration","type":"u8"},{"name":"exchangeStatus","type":"u8"},{"name":"liquidationDuration","type":"u8"},{"name":"initialPctToLiquidate","type":"u16"},{"name":"maxNumberOfSubAccounts","type":"u16"},{"name":"maxInitializeUserFee","type":"u16"},{"name":"featureBitFlags","type":"u8"},{"name":"lpPoolFeatureBitFlags","type":"u8"},{"name":"padding","type":{"array":["u8",8]}}]}},{"name":"User","type":{"kind":"struct","fields":[{"name":"authority","docs":["The owner/authority of the account"],"type":"publicKey"},{"name":"delegate","docs":["An addresses that can control the account on the authority's behalf. Has limited power, cant withdraw"],"type":"publicKey"},{"name":"name","docs":["Encoded display name e.g. \"toly\""],"type":{"array":["u8",32]}},{"name":"spotPositions","docs":["The user's spot positions"],"type":{"array":[{"defined":"SpotPosition"},8]}},{"name":"perpPositions","docs":["The user's perp positions"],"type":{"array":[{"defined":"PerpPosition"},8]}},{"name":"orders","docs":["The user's orders"],"type":{"array":[{"defined":"Order"},32]}},{"name":"lastAddPerpLpSharesTs","docs":["The last time the user added perp lp positions"],"type":"i64"},{"name":"totalDeposits","docs":["The total values of deposits the user has made","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"totalWithdraws","docs":["The total values of withdrawals the user has made","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"totalSocialLoss","docs":["The total socialized loss the users has incurred upon the protocol","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"settledPerpPnl","docs":["Fees (taker fees, maker rebate, referrer reward, filler reward) and pnl for perps","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"cumulativeSpotFees","docs":["Fees (taker fees, maker rebate, filler reward) for spot","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"cumulativePerpFunding","docs":["Cumulative funding paid/received for perps","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"liquidationMarginFreed","docs":["The amount of margin freed during liquidation. Used to force the liquidation to occur over a period of time","Defaults to zero when not being liquidated","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"lastActiveSlot","docs":["The last slot a user was active. Used to determine if a user is idle"],"type":"u64"},{"name":"nextOrderId","docs":["Every user order has an order id. This is the next order id to be used"],"type":"u32"},{"name":"maxMarginRatio","docs":["Custom max initial margin ratio for the user"],"type":"u32"},{"name":"nextLiquidationId","docs":["The next liquidation id to be used for user"],"type":"u16"},{"name":"subAccountId","docs":["The sub account id for this user"],"type":"u16"},{"name":"status","docs":["Whether the user is active, being liquidated or bankrupt"],"type":"u8"},{"name":"isMarginTradingEnabled","docs":["Whether the user has enabled margin trading"],"type":"bool"},{"name":"idle","docs":["User is idle if they haven't interacted with the protocol in 1 week and they have no orders, perp positions or borrows","Off-chain keeper bots can ignore users that are idle"],"type":"bool"},{"name":"openOrders","docs":["number of open orders"],"type":"u8"},{"name":"hasOpenOrder","docs":["Whether or not user has open order"],"type":"bool"},{"name":"openAuctions","docs":["number of open orders with auction"],"type":"u8"},{"name":"hasOpenAuction","docs":["Whether or not user has open order with auction"],"type":"bool"},{"name":"marginMode","type":{"defined":"MarginMode"}},{"name":"poolId","type":"u8"},{"name":"padding1","type":{"array":["u8",3]}},{"name":"lastFuelBonusUpdateTs","type":"u32"},{"name":"padding","type":{"array":["u8",12]}}]}},{"name":"UserStats","type":{"kind":"struct","fields":[{"name":"authority","docs":["The authority for all of a users sub accounts"],"type":"publicKey"},{"name":"referrer","docs":["The address that referred this user"],"type":"publicKey"},{"name":"fees","docs":["Stats on the fees paid by the user"],"type":{"defined":"UserFees"}},{"name":"nextEpochTs","docs":["The timestamp of the next epoch","Epoch is used to limit referrer rewards earned in single epoch"],"type":"i64"},{"name":"makerVolume30d","docs":["Rolling 30day maker volume for user","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"takerVolume30d","docs":["Rolling 30day taker volume for user","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"fillerVolume30d","docs":["Rolling 30day filler volume for user","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"lastMakerVolume30dTs","docs":["last time the maker volume was updated"],"type":"i64"},{"name":"lastTakerVolume30dTs","docs":["last time the taker volume was updated"],"type":"i64"},{"name":"lastFillerVolume30dTs","docs":["last time the filler volume was updated"],"type":"i64"},{"name":"ifStakedQuoteAssetAmount","docs":["The amount of tokens staked in the quote spot markets if"],"type":"u64"},{"name":"numberOfSubAccounts","docs":["The current number of sub accounts"],"type":"u16"},{"name":"numberOfSubAccountsCreated","docs":["The number of sub accounts created. Can be greater than the number of sub accounts if user","has deleted sub accounts"],"type":"u16"},{"name":"referrerStatus","docs":["Flags for referrer status:","First bit (LSB): 1 if user is a referrer, 0 otherwise","Second bit: 1 if user was referred, 0 otherwise"],"type":"u8"},{"name":"disableUpdatePerpBidAskTwap","type":"bool"},{"name":"pausedOperations","type":"u8"},{"name":"fuelOverflowStatus","docs":["whether the user has a FuelOverflow account"],"type":"u8"},{"name":"fuelInsurance","docs":["accumulated fuel for token amounts of insurance"],"type":"u32"},{"name":"fuelDeposits","docs":["accumulated fuel for notional of deposits"],"type":"u32"},{"name":"fuelBorrows","docs":["accumulate fuel bonus for notional of borrows"],"type":"u32"},{"name":"fuelPositions","docs":["accumulated fuel for perp open interest"],"type":"u32"},{"name":"fuelTaker","docs":["accumulate fuel bonus for taker volume"],"type":"u32"},{"name":"fuelMaker","docs":["accumulate fuel bonus for maker volume"],"type":"u32"},{"name":"ifStakedGovTokenAmount","docs":["The amount of tokens staked in the governance spot markets if"],"type":"u64"},{"name":"lastFuelIfBonusUpdateTs","docs":["last unix ts user stats data was used to update if fuel (u32 to save space)"],"type":"u32"},{"name":"padding","type":{"array":["u8",12]}}]}},{"name":"ReferrerName","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"user","type":"publicKey"},{"name":"userStats","type":"publicKey"},{"name":"name","type":{"array":["u8",32]}}]}},{"name":"FuelOverflow","type":{"kind":"struct","fields":[{"name":"authority","docs":["The authority of this overflow account"],"type":"publicKey"},{"name":"fuelInsurance","type":"u128"},{"name":"fuelDeposits","type":"u128"},{"name":"fuelBorrows","type":"u128"},{"name":"fuelPositions","type":"u128"},{"name":"fuelTaker","type":"u128"},{"name":"fuelMaker","type":"u128"},{"name":"lastFuelSweepTs","type":"u32"},{"name":"lastResetTs","type":"u32"},{"name":"padding","type":{"array":["u128",6]}}]}}],"types":[{"name":"UpdatePerpMarketSummaryStatsParams","type":{"kind":"struct","fields":[{"name":"quoteAssetAmountWithUnsettledLp","type":{"option":"i64"}},{"name":"netUnsettledFundingPnl","type":{"option":"i64"}},{"name":"updateAmmSummaryStats","type":{"option":"bool"}},{"name":"excludeTotalLiqFee","type":{"option":"bool"}}]}},{"name":"ConstituentParams","type":{"kind":"struct","fields":[{"name":"maxWeightDeviation","type":{"option":"i64"}},{"name":"swapFeeMin","type":{"option":"i64"}},{"name":"swapFeeMax","type":{"option":"i64"}},{"name":"maxBorrowTokenAmount","type":{"option":"u64"}},{"name":"oracleStalenessThreshold","type":{"option":"u64"}},{"name":"costToTradeBps","type":{"option":"i32"}},{"name":"constituentDerivativeIndex","type":{"option":"i16"}},{"name":"derivativeWeight","type":{"option":"u64"}},{"name":"volatility","type":{"option":"u64"}},{"name":"gammaExecution","type":{"option":"u8"}},{"name":"gammaInventory","type":{"option":"u8"}},{"name":"xi","type":{"option":"u8"}}]}},{"name":"LpPoolParams","type":{"kind":"struct","fields":[{"name":"maxSettleQuoteAmount","type":{"option":"u64"}},{"name":"volatility","type":{"option":"u64"}},{"name":"gammaExecution","type":{"option":"u8"}},{"name":"xi","type":{"option":"u8"}},{"name":"maxAum","type":{"option":"u128"}},{"name":"whitelistMint","type":{"option":"publicKey"}}]}},{"name":"OverrideAmmCacheParams","type":{"kind":"struct","fields":[{"name":"quoteOwedFromLpPool","type":{"option":"i64"}},{"name":"lastSettleSlot","type":{"option":"u64"}},{"name":"lastFeePoolTokenAmount","type":{"option":"u128"}},{"name":"lastNetPnlPoolTokenAmount","type":{"option":"i128"}},{"name":"ammPositionScalar","type":{"option":"u8"}},{"name":"ammInventoryLimit","type":{"option":"i64"}}]}},{"name":"AddAmmConstituentMappingDatum","type":{"kind":"struct","fields":[{"name":"constituentIndex","type":"u16"},{"name":"perpMarketIndex","type":"u16"},{"name":"weight","type":"i64"}]}},{"name":"CacheInfo","type":{"kind":"struct","fields":[{"name":"oracle","type":"publicKey"},{"name":"lastFeePoolTokenAmount","type":"u128"},{"name":"lastNetPnlPoolTokenAmount","type":"i128"},{"name":"lastExchangeFees","type":"u128"},{"name":"lastSettleAmmExFees","type":"u128"},{"name":"lastSettleAmmPnl","type":"i128"},{"name":"position","docs":["BASE PRECISION"],"type":"i64"},{"name":"slot","type":"u64"},{"name":"lastSettleAmount","type":"u64"},{"name":"lastSettleSlot","type":"u64"},{"name":"lastSettleTs","type":"i64"},{"name":"quoteOwedFromLpPool","type":"i64"},{"name":"ammInventoryLimit","type":"i64"},{"name":"oraclePrice","type":"i64"},{"name":"oracleSlot","type":"u64"},{"name":"marketIndex","type":"u16"},{"name":"oracleSource","type":"u8"},{"name":"oracleValidity","type":"u8"},{"name":"lpStatusForPerpMarket","type":"u8"},{"name":"ammPositionScalar","type":"u8"},{"name":"padding","type":{"array":["u8",34]}}]}},{"name":"AmmCacheFixed","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"pad","type":{"array":["u8",3]}},{"name":"len","type":"u32"}]}},{"name":"LiquidatePerpRecord","type":{"kind":"struct","fields":[{"name":"marketIndex","type":"u16"},{"name":"oraclePrice","type":"i64"},{"name":"baseAssetAmount","type":"i64"},{"name":"quoteAssetAmount","type":"i64"},{"name":"lpShares","docs":["precision: AMM_RESERVE_PRECISION"],"type":"u64"},{"name":"fillRecordId","type":"u64"},{"name":"userOrderId","type":"u32"},{"name":"liquidatorOrderId","type":"u32"},{"name":"liquidatorFee","docs":["precision: QUOTE_PRECISION"],"type":"u64"},{"name":"ifFee","docs":["precision: QUOTE_PRECISION"],"type":"u64"}]}},{"name":"LiquidateSpotRecord","type":{"kind":"struct","fields":[{"name":"assetMarketIndex","type":"u16"},{"name":"assetPrice","type":"i64"},{"name":"assetTransfer","type":"u128"},{"name":"liabilityMarketIndex","type":"u16"},{"name":"liabilityPrice","type":"i64"},{"name":"liabilityTransfer","docs":["precision: token mint precision"],"type":"u128"},{"name":"ifFee","docs":["precision: token mint precision"],"type":"u64"}]}},{"name":"LiquidateBorrowForPerpPnlRecord","type":{"kind":"struct","fields":[{"name":"perpMarketIndex","type":"u16"},{"name":"marketOraclePrice","type":"i64"},{"name":"pnlTransfer","type":"u128"},{"name":"liabilityMarketIndex","type":"u16"},{"name":"liabilityPrice","type":"i64"},{"name":"liabilityTransfer","type":"u128"}]}},{"name":"LiquidatePerpPnlForDepositRecord","type":{"kind":"struct","fields":[{"name":"perpMarketIndex","type":"u16"},{"name":"marketOraclePrice","type":"i64"},{"name":"pnlTransfer","type":"u128"},{"name":"assetMarketIndex","type":"u16"},{"name":"assetPrice","type":"i64"},{"name":"assetTransfer","type":"u128"}]}},{"name":"PerpBankruptcyRecord","type":{"kind":"struct","fields":[{"name":"marketIndex","type":"u16"},{"name":"pnl","type":"i128"},{"name":"ifPayment","type":"u128"},{"name":"clawbackUser","type":{"option":"publicKey"}},{"name":"clawbackUserPayment","type":{"option":"u128"}},{"name":"cumulativeFundingRateDelta","type":"i128"}]}},{"name":"SpotBankruptcyRecord","type":{"kind":"struct","fields":[{"name":"marketIndex","type":"u16"},{"name":"borrowAmount","type":"u128"},{"name":"ifPayment","type":"u128"},{"name":"cumulativeDepositInterestDelta","type":"u128"}]}},{"name":"IfRebalanceConfigParams","type":{"kind":"struct","fields":[{"name":"totalInAmount","type":"u64"},{"name":"epochMaxInAmount","type":"u64"},{"name":"epochDuration","type":"i64"},{"name":"outMarketIndex","type":"u16"},{"name":"inMarketIndex","type":"u16"},{"name":"maxSlippageBps","type":"u16"},{"name":"swapMode","type":"u8"},{"name":"status","type":"u8"}]}},{"name":"ConstituentSpotBalance","type":{"kind":"struct","fields":[{"name":"scaledBalance","docs":["The scaled balance of the position. To get the token amount, multiply by the cumulative deposit/borrow","interest of corresponding market.","precision: token precision"],"type":"u128"},{"name":"cumulativeDeposits","docs":["The cumulative deposits/borrows a user has made into a market","precision: token mint precision"],"type":"i64"},{"name":"marketIndex","docs":["The market index of the corresponding spot market"],"type":"u16"},{"name":"balanceType","docs":["Whether the position is deposit or borrow"],"type":{"defined":"SpotBalanceType"}},{"name":"padding","type":{"array":["u8",5]}}]}},{"name":"AmmConstituentDatum","type":{"kind":"struct","fields":[{"name":"perpMarketIndex","type":"u16"},{"name":"constituentIndex","type":"u16"},{"name":"padding","type":{"array":["u8",4]}},{"name":"lastSlot","type":"u64"},{"name":"weight","docs":["PERCENTAGE_PRECISION. The weight this constituent has on the perp market"],"type":"i64"}]}},{"name":"AmmConstituentMappingFixed","type":{"kind":"struct","fields":[{"name":"lpPool","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"pad","type":{"array":["u8",3]}},{"name":"len","type":"u32"}]}},{"name":"TargetsDatum","type":{"kind":"struct","fields":[{"name":"costToTradeBps","type":"i32"},{"name":"padding","type":{"array":["u8",4]}},{"name":"targetBase","type":"i64"},{"name":"lastOracleSlot","type":"u64"},{"name":"lastPositionSlot","type":"u64"}]}},{"name":"ConstituentTargetBaseFixed","type":{"kind":"struct","fields":[{"name":"lpPool","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"pad","type":{"array":["u8",3]}},{"name":"len","docs":["total elements in the flattened `data` vec"],"type":"u32"}]}},{"name":"ConstituentCorrelationsFixed","type":{"kind":"struct","fields":[{"name":"lpPool","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"pad","type":{"array":["u8",3]}},{"name":"len","docs":["total elements in the flattened `data` vec"],"type":"u32"}]}},{"name":"MarketIdentifier","type":{"kind":"struct","fields":[{"name":"marketType","type":{"defined":"MarketType"}},{"name":"marketIndex","type":"u16"}]}},{"name":"HistoricalOracleData","type":{"kind":"struct","fields":[{"name":"lastOraclePrice","docs":["precision: PRICE_PRECISION"],"type":"i64"},{"name":"lastOracleConf","docs":["precision: PRICE_PRECISION"],"type":"u64"},{"name":"lastOracleDelay","docs":["number of slots since last update"],"type":"i64"},{"name":"lastOraclePriceTwap","docs":["precision: PRICE_PRECISION"],"type":"i64"},{"name":"lastOraclePriceTwap5min","docs":["precision: PRICE_PRECISION"],"type":"i64"},{"name":"lastOraclePriceTwapTs","docs":["unix_timestamp of last snapshot"],"type":"i64"}]}},{"name":"HistoricalIndexData","type":{"kind":"struct","fields":[{"name":"lastIndexBidPrice","docs":["precision: PRICE_PRECISION"],"type":"u64"},{"name":"lastIndexAskPrice","docs":["precision: PRICE_PRECISION"],"type":"u64"},{"name":"lastIndexPriceTwap","docs":["precision: PRICE_PRECISION"],"type":"u64"},{"name":"lastIndexPriceTwap5min","docs":["precision: PRICE_PRECISION"],"type":"u64"},{"name":"lastIndexPriceTwapTs","docs":["unix_timestamp of last snapshot"],"type":"i64"}]}},{"name":"PrelaunchOracleParams","type":{"kind":"struct","fields":[{"name":"perpMarketIndex","type":"u16"},{"name":"price","type":{"option":"i64"}},{"name":"maxPrice","type":{"option":"i64"}}]}},{"name":"OrderParams","type":{"kind":"struct","fields":[{"name":"orderType","type":{"defined":"OrderType"}},{"name":"marketType","type":{"defined":"MarketType"}},{"name":"direction","type":{"defined":"PositionDirection"}},{"name":"userOrderId","type":"u8"},{"name":"baseAssetAmount","type":"u64"},{"name":"price","type":"u64"},{"name":"marketIndex","type":"u16"},{"name":"reduceOnly","type":"bool"},{"name":"postOnly","type":{"defined":"PostOnlyParam"}},{"name":"bitFlags","type":"u8"},{"name":"maxTs","type":{"option":"i64"}},{"name":"triggerPrice","type":{"option":"u64"}},{"name":"triggerCondition","type":{"defined":"OrderTriggerCondition"}},{"name":"oraclePriceOffset","type":{"option":"i32"}},{"name":"auctionDuration","type":{"option":"u8"}},{"name":"auctionStartPrice","type":{"option":"i64"}},{"name":"auctionEndPrice","type":{"option":"i64"}}]}},{"name":"SignedMsgOrderParamsMessage","type":{"kind":"struct","fields":[{"name":"signedMsgOrderParams","type":{"defined":"OrderParams"}},{"name":"subAccountId","type":"u16"},{"name":"slot","type":"u64"},{"name":"uuid","type":{"array":["u8",8]}},{"name":"takeProfitOrderParams","type":{"option":{"defined":"SignedMsgTriggerOrderParams"}}},{"name":"stopLossOrderParams","type":{"option":{"defined":"SignedMsgTriggerOrderParams"}}},{"name":"maxMarginRatio","type":{"option":"u16"}},{"name":"builderIdx","type":{"option":"u8"}},{"name":"builderFeeTenthBps","type":{"option":"u16"}},{"name":"isolatedPositionDeposit","type":{"option":"u64"}}]}},{"name":"SignedMsgOrderParamsDelegateMessage","type":{"kind":"struct","fields":[{"name":"signedMsgOrderParams","type":{"defined":"OrderParams"}},{"name":"takerPubkey","type":"publicKey"},{"name":"slot","type":"u64"},{"name":"uuid","type":{"array":["u8",8]}},{"name":"takeProfitOrderParams","type":{"option":{"defined":"SignedMsgTriggerOrderParams"}}},{"name":"stopLossOrderParams","type":{"option":{"defined":"SignedMsgTriggerOrderParams"}}},{"name":"maxMarginRatio","type":{"option":"u16"}},{"name":"builderIdx","type":{"option":"u8"}},{"name":"builderFeeTenthBps","type":{"option":"u16"}},{"name":"isolatedPositionDeposit","type":{"option":"u64"}}]}},{"name":"SignedMsgTriggerOrderParams","type":{"kind":"struct","fields":[{"name":"triggerPrice","type":"u64"},{"name":"baseAssetAmount","type":"u64"}]}},{"name":"ModifyOrderParams","type":{"kind":"struct","fields":[{"name":"direction","type":{"option":{"defined":"PositionDirection"}}},{"name":"baseAssetAmount","type":{"option":"u64"}},{"name":"price","type":{"option":"u64"}},{"name":"reduceOnly","type":{"option":"bool"}},{"name":"postOnly","type":{"option":{"defined":"PostOnlyParam"}}},{"name":"bitFlags","type":{"option":"u8"}},{"name":"maxTs","type":{"option":"i64"}},{"name":"triggerPrice","type":{"option":"u64"}},{"name":"triggerCondition","type":{"option":{"defined":"OrderTriggerCondition"}}},{"name":"oraclePriceOffset","type":{"option":"i32"}},{"name":"auctionDuration","type":{"option":"u8"}},{"name":"auctionStartPrice","type":{"option":"i64"}},{"name":"auctionEndPrice","type":{"option":"i64"}},{"name":"policy","type":{"option":"u8"}}]}},{"name":"InsuranceClaim","type":{"kind":"struct","fields":[{"name":"revenueWithdrawSinceLastSettle","docs":["The amount of revenue last settled","Positive if funds left the perp market,","negative if funds were pulled into the perp market","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"maxRevenueWithdrawPerPeriod","docs":["The max amount of revenue that can be withdrawn per period","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"quoteMaxInsurance","docs":["The max amount of insurance that perp market can use to resolve bankruptcy and pnl deficits","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"quoteSettledInsurance","docs":["The amount of insurance that has been used to resolve bankruptcy and pnl deficits","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"lastRevenueWithdrawTs","docs":["The last time revenue was settled in/out of market"],"type":"i64"}]}},{"name":"PoolBalance","type":{"kind":"struct","fields":[{"name":"scaledBalance","docs":["To get the pool's token amount, you must multiply the scaled balance by the market's cumulative","deposit interest","precision: SPOT_BALANCE_PRECISION"],"type":"u128"},{"name":"marketIndex","docs":["The spot market the pool is for"],"type":"u16"},{"name":"padding","type":{"array":["u8",6]}}]}},{"name":"AMM","type":{"kind":"struct","fields":[{"name":"oracle","docs":["oracle price data public key"],"type":"publicKey"},{"name":"historicalOracleData","docs":["stores historically witnessed oracle data"],"type":{"defined":"HistoricalOracleData"}},{"name":"baseAssetAmountPerLp","docs":["accumulated base asset amount since inception per lp share","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"quoteAssetAmountPerLp","docs":["accumulated quote asset amount since inception per lp share","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"feePool","docs":["partition of fees from perp market trading moved from pnl settlements"],"type":{"defined":"PoolBalance"}},{"name":"baseAssetReserve","docs":["`x` reserves for constant product mm formula (x * y = k)","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"quoteAssetReserve","docs":["`y` reserves for constant product mm formula (x * y = k)","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"concentrationCoef","docs":["determines how close the min/max base asset reserve sit vs base reserves","allow for decreasing slippage without increasing liquidity and v.v.","precision: PERCENTAGE_PRECISION"],"type":"u128"},{"name":"minBaseAssetReserve","docs":["minimum base_asset_reserve allowed before AMM is unavailable","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"maxBaseAssetReserve","docs":["maximum base_asset_reserve allowed before AMM is unavailable","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"sqrtK","docs":["`sqrt(k)` in constant product mm formula (x * y = k). stored to avoid drift caused by integer math issues","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"pegMultiplier","docs":["normalizing numerical factor for y, its use offers lowest slippage in cp-curve when market is balanced","precision: PEG_PRECISION"],"type":"u128"},{"name":"terminalQuoteAssetReserve","docs":["y when market is balanced. stored to save computation","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"baseAssetAmountLong","docs":["always non-negative. tracks number of total longs in market (regardless of counterparty)","precision: BASE_PRECISION"],"type":"i128"},{"name":"baseAssetAmountShort","docs":["always non-positive. tracks number of total shorts in market (regardless of counterparty)","precision: BASE_PRECISION"],"type":"i128"},{"name":"baseAssetAmountWithAmm","docs":["tracks net position (longs-shorts) in market with AMM as counterparty","precision: BASE_PRECISION"],"type":"i128"},{"name":"baseAssetAmountWithUnsettledLp","docs":["tracks net position (longs-shorts) in market with LPs as counterparty","precision: BASE_PRECISION"],"type":"i128"},{"name":"maxOpenInterest","docs":["max allowed open interest, blocks trades that breach this value","precision: BASE_PRECISION"],"type":"u128"},{"name":"quoteAssetAmount","docs":["sum of all user's perp quote_asset_amount in market","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"quoteEntryAmountLong","docs":["sum of all long user's quote_entry_amount in market","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"quoteEntryAmountShort","docs":["sum of all short user's quote_entry_amount in market","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"quoteBreakEvenAmountLong","docs":["sum of all long user's quote_break_even_amount in market","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"quoteBreakEvenAmountShort","docs":["sum of all short user's quote_break_even_amount in market","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"userLpShares","docs":["total user lp shares of sqrt_k (protocol owned liquidity = sqrt_k - last_funding_rate)","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"lastFundingRate","docs":["last funding rate in this perp market (unit is quote per base)","precision: FUNDING_RATE_PRECISION"],"type":"i64"},{"name":"lastFundingRateLong","docs":["last funding rate for longs in this perp market (unit is quote per base)","precision: FUNDING_RATE_PRECISION"],"type":"i64"},{"name":"lastFundingRateShort","docs":["last funding rate for shorts in this perp market (unit is quote per base)","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"last24hAvgFundingRate","docs":["estimate of last 24h of funding rate perp market (unit is quote per base)","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"totalFee","docs":["total fees collected by this perp market","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"totalMmFee","docs":["total fees collected by the vAMM's bid/ask spread","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"totalExchangeFee","docs":["total fees collected by exchange fee schedule","precision: QUOTE_PRECISION"],"type":"u128"},{"name":"totalFeeMinusDistributions","docs":["total fees minus any recognized upnl and pool withdraws","precision: QUOTE_PRECISION"],"type":"i128"},{"name":"totalFeeWithdrawn","docs":["sum of all fees from fee pool withdrawn to revenue pool","precision: QUOTE_PRECISION"],"type":"u128"},{"name":"totalLiquidationFee","docs":["all fees collected by market for liquidations","precision: QUOTE_PRECISION"],"type":"u128"},{"name":"cumulativeFundingRateLong","docs":["accumulated funding rate for longs since inception in market"],"type":"i128"},{"name":"cumulativeFundingRateShort","docs":["accumulated funding rate for shorts since inception in market"],"type":"i128"},{"name":"totalSocialLoss","docs":["accumulated social loss paid by users since inception in market"],"type":"u128"},{"name":"askBaseAssetReserve","docs":["transformed base_asset_reserve for users going long","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"askQuoteAssetReserve","docs":["transformed quote_asset_reserve for users going long","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"bidBaseAssetReserve","docs":["transformed base_asset_reserve for users going short","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"bidQuoteAssetReserve","docs":["transformed quote_asset_reserve for users going short","precision: AMM_RESERVE_PRECISION"],"type":"u128"},{"name":"lastOracleNormalisedPrice","docs":["the last seen oracle price partially shrunk toward the amm reserve price","precision: PRICE_PRECISION"],"type":"i64"},{"name":"lastOracleReservePriceSpreadPct","docs":["the gap between the oracle price and the reserve price = y * peg_multiplier / x"],"type":"i64"},{"name":"lastBidPriceTwap","docs":["average estimate of bid price over funding_period","precision: PRICE_PRECISION"],"type":"u64"},{"name":"lastAskPriceTwap","docs":["average estimate of ask price over funding_period","precision: PRICE_PRECISION"],"type":"u64"},{"name":"lastMarkPriceTwap","docs":["average estimate of (bid+ask)/2 price over funding_period","precision: PRICE_PRECISION"],"type":"u64"},{"name":"lastMarkPriceTwap5min","docs":["average estimate of (bid+ask)/2 price over FIVE_MINUTES"],"type":"u64"},{"name":"lastUpdateSlot","docs":["the last blockchain slot the amm was updated"],"type":"u64"},{"name":"lastOracleConfPct","docs":["the pct size of the oracle confidence interval","precision: PERCENTAGE_PRECISION"],"type":"u64"},{"name":"netRevenueSinceLastFunding","docs":["the total_fee_minus_distribution change since the last funding update","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"lastFundingRateTs","docs":["the last funding rate update unix_timestamp"],"type":"i64"},{"name":"fundingPeriod","docs":["the peridocity of the funding rate updates"],"type":"i64"},{"name":"orderStepSize","docs":["the base step size (increment) of orders","precision: BASE_PRECISION"],"type":"u64"},{"name":"orderTickSize","docs":["the price tick size of orders","precision: PRICE_PRECISION"],"type":"u64"},{"name":"minOrderSize","docs":["the minimum base size of an order","precision: BASE_PRECISION"],"type":"u64"},{"name":"mmOracleSlot","docs":["the max base size a single user can have","precision: BASE_PRECISION"],"type":"u64"},{"name":"volume24h","docs":["estimated total of volume in market","QUOTE_PRECISION"],"type":"u64"},{"name":"longIntensityVolume","docs":["the volume intensity of long fills against AMM"],"type":"u64"},{"name":"shortIntensityVolume","docs":["the volume intensity of short fills against AMM"],"type":"u64"},{"name":"lastTradeTs","docs":["the blockchain unix timestamp at the time of the last trade"],"type":"i64"},{"name":"markStd","docs":["estimate of standard deviation of the fill (mark) prices","precision: PRICE_PRECISION"],"type":"u64"},{"name":"oracleStd","docs":["estimate of standard deviation of the oracle price at each update","precision: PRICE_PRECISION"],"type":"u64"},{"name":"lastMarkPriceTwapTs","docs":["the last unix_timestamp the mark twap was updated"],"type":"i64"},{"name":"baseSpread","docs":["the minimum spread the AMM can quote. also used as step size for some spread logic increases."],"type":"u32"},{"name":"maxSpread","docs":["the maximum spread the AMM can quote"],"type":"u32"},{"name":"longSpread","docs":["the spread for asks vs the reserve price"],"type":"u32"},{"name":"shortSpread","docs":["the spread for bids vs the reserve price"],"type":"u32"},{"name":"mmOraclePrice","docs":["MM oracle price"],"type":"i64"},{"name":"maxFillReserveFraction","docs":["the fraction of total available liquidity a single fill on the AMM can consume"],"type":"u16"},{"name":"maxSlippageRatio","docs":["the maximum slippage a single fill on the AMM can push"],"type":"u16"},{"name":"curveUpdateIntensity","docs":["the update intensity of AMM formulaic updates (adjusting k). 0-100"],"type":"u8"},{"name":"ammJitIntensity","docs":["the jit intensity of AMM. larger intensity means larger participation in jit. 0 means no jit participation.","(0, 100] is intensity for protocol-owned AMM. (100, 200] is intensity for user LP-owned AMM."],"type":"u8"},{"name":"oracleSource","docs":["the oracle provider information. used to decode/scale the oracle public key"],"type":{"defined":"OracleSource"}},{"name":"lastOracleValid","docs":["tracks whether the oracle was considered valid at the last AMM update"],"type":"bool"},{"name":"targetBaseAssetAmountPerLp","docs":["the target value for `base_asset_amount_per_lp`, used during AMM JIT with LP split","precision: BASE_PRECISION"],"type":"i32"},{"name":"perLpBase","docs":["expo for unit of per_lp, base 10 (if per_lp_base=X, then per_lp unit is 10^X)"],"type":"i8"},{"name":"oracleLowRiskSlotDelayOverride","docs":["the override for the state.min_perp_auction_duration","0 is no override, -1 is disable speed bump, 1-100 is literal speed bump"],"type":"i8"},{"name":"ammSpreadAdjustment","docs":["signed scale amm_spread similar to fee_adjustment logic (-100 = 0, 100 = double)"],"type":"i8"},{"name":"oracleSlotDelayOverride","type":"i8"},{"name":"mmOracleSequenceId","type":"u64"},{"name":"netUnsettledFundingPnl","type":"i64"},{"name":"quoteAssetAmountWithUnsettledLp","type":"i64"},{"name":"referencePriceOffset","type":"i32"},{"name":"ammInventorySpreadAdjustment","docs":["signed scale amm_spread similar to fee_adjustment logic (-100 = 0, 100 = double)"],"type":"i8"},{"name":"referencePriceOffsetDeadbandPct","type":"u8"},{"name":"padding","type":{"array":["u8",2]}},{"name":"lastFundingOracleTwap","type":"i64"}]}},{"name":"RevenueShareOrder","type":{"kind":"struct","fields":[{"name":"feesAccrued","docs":["fees accrued so far for this order slot. This is not exclusively fees from this order_id","and may include fees from other orders in the same market. This may be swept to the","builder's SpotPosition during settle_pnl."],"type":"u64"},{"name":"orderId","docs":["the order_id of the current active order in this slot. It's only relevant while bit_flag = Open"],"type":"u32"},{"name":"feeTenthBps","docs":["the builder fee on this order, in tenths of a bps, e.g. 100 = 0.01%"],"type":"u16"},{"name":"marketIndex","type":"u16"},{"name":"subAccountId","docs":["the subaccount_id of the user who created this order. It's only relevant while bit_flag = Open"],"type":"u16"},{"name":"builderIdx","docs":["the index of the RevenueShareEscrow.approved_builders list, that this order's fee will settle to. Ignored","if bit_flag = Referral."],"type":"u8"},{"name":"bitFlags","docs":["bitflags that describe the state of the order.","[`RevenueShareOrderBitFlag::Init`]: this order slot is available for use.","[`RevenueShareOrderBitFlag::Open`]: this order slot is occupied, `order_id` is the `sub_account_id`'s active order.","[`RevenueShareOrderBitFlag::Completed`]: this order has been filled or canceled, and is waiting to be settled into.","the builder's account order_id and sub_account_id are no longer relevant, it may be merged with other orders.","[`RevenueShareOrderBitFlag::Referral`]: this order stores referral rewards waiting to be settled for this market.","If it is set, no other bitflag should be set."],"type":"u8"},{"name":"userOrderIndex","docs":["the index into the User's orders list when this RevenueShareOrder was created, make sure to verify that order_id matches."],"type":"u8"},{"name":"marketType","type":{"defined":"MarketType"}},{"name":"padding","type":{"array":["u8",10]}}]}},{"name":"BuilderInfo","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"maxFeeTenthBps","type":"u16"},{"name":"padding","type":{"array":["u8",6]}}]}},{"name":"RevenueShareEscrowFixed","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"referrer","type":"publicKey"},{"name":"referrerBoostExpireTs","type":"u32"},{"name":"referrerRewardOffset","type":"i8"},{"name":"refereeFeeNumeratorOffset","type":"i8"},{"name":"referrerBoostNumerator","type":"i8"},{"name":"reservedFixed","type":{"array":["u8",17]}}]}},{"name":"SignedMsgOrderId","type":{"kind":"struct","fields":[{"name":"uuid","type":{"array":["u8",8]}},{"name":"maxSlot","type":"u64"},{"name":"orderId","type":"u32"},{"name":"padding","type":"u32"}]}},{"name":"SignedMsgUserOrdersFixed","type":{"kind":"struct","fields":[{"name":"userPubkey","type":"publicKey"},{"name":"padding","type":"u32"},{"name":"len","type":"u32"}]}},{"name":"InsuranceFund","type":{"kind":"struct","fields":[{"name":"vault","type":"publicKey"},{"name":"totalShares","type":"u128"},{"name":"userShares","type":"u128"},{"name":"sharesBase","type":"u128"},{"name":"unstakingPeriod","type":"i64"},{"name":"lastRevenueSettleTs","type":"i64"},{"name":"revenueSettlePeriod","type":"i64"},{"name":"totalFactor","type":"u32"},{"name":"userFactor","type":"u32"}]}},{"name":"OracleGuardRails","type":{"kind":"struct","fields":[{"name":"priceDivergence","type":{"defined":"PriceDivergenceGuardRails"}},{"name":"validity","type":{"defined":"ValidityGuardRails"}}]}},{"name":"PriceDivergenceGuardRails","type":{"kind":"struct","fields":[{"name":"markOraclePercentDivergence","type":"u64"},{"name":"oracleTwap5minPercentDivergence","type":"u64"}]}},{"name":"ValidityGuardRails","type":{"kind":"struct","fields":[{"name":"slotsBeforeStaleForAmm","type":"i64"},{"name":"slotsBeforeStaleForMargin","type":"i64"},{"name":"confidenceIntervalMaxSize","type":"u64"},{"name":"tooVolatileRatio","type":"i64"}]}},{"name":"FeeStructure","type":{"kind":"struct","fields":[{"name":"feeTiers","type":{"array":[{"defined":"FeeTier"},10]}},{"name":"fillerRewardStructure","type":{"defined":"OrderFillerRewardStructure"}},{"name":"referrerRewardEpochUpperBound","type":"u64"},{"name":"flatFillerFee","type":"u64"}]}},{"name":"FeeTier","type":{"kind":"struct","fields":[{"name":"feeNumerator","type":"u32"},{"name":"feeDenominator","type":"u32"},{"name":"makerRebateNumerator","type":"u32"},{"name":"makerRebateDenominator","type":"u32"},{"name":"referrerRewardNumerator","type":"u32"},{"name":"referrerRewardDenominator","type":"u32"},{"name":"refereeFeeNumerator","type":"u32"},{"name":"refereeFeeDenominator","type":"u32"}]}},{"name":"OrderFillerRewardStructure","type":{"kind":"struct","fields":[{"name":"rewardNumerator","type":"u32"},{"name":"rewardDenominator","type":"u32"},{"name":"timeBasedRewardLowerBound","type":"u128"}]}},{"name":"UserFees","type":{"kind":"struct","fields":[{"name":"totalFeePaid","docs":["Total taker fee paid","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"totalFeeRebate","docs":["Total maker fee rebate","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"totalTokenDiscount","docs":["Total discount from holding token","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"totalRefereeDiscount","docs":["Total discount from being referred","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"totalReferrerReward","docs":["Total reward to referrer","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"currentEpochReferrerReward","docs":["Total reward to referrer this epoch","precision: QUOTE_PRECISION"],"type":"u64"}]}},{"name":"SpotPosition","type":{"kind":"struct","fields":[{"name":"scaledBalance","docs":["The scaled balance of the position. To get the token amount, multiply by the cumulative deposit/borrow","interest of corresponding market.","precision: SPOT_BALANCE_PRECISION"],"type":"u64"},{"name":"openBids","docs":["How many spot non reduce only trigger orders the user has open","precision: token mint precision"],"type":"i64"},{"name":"openAsks","docs":["How many spot non reduce only trigger orders the user has open","precision: token mint precision"],"type":"i64"},{"name":"cumulativeDeposits","docs":["The cumulative deposits/borrows a user has made into a market","precision: token mint precision"],"type":"i64"},{"name":"marketIndex","docs":["The market index of the corresponding spot market"],"type":"u16"},{"name":"balanceType","docs":["Whether the position is deposit or borrow"],"type":{"defined":"SpotBalanceType"}},{"name":"openOrders","docs":["Number of open orders"],"type":"u8"},{"name":"padding","type":{"array":["u8",4]}}]}},{"name":"PerpPosition","type":{"kind":"struct","fields":[{"name":"lastCumulativeFundingRate","docs":["The perp market's last cumulative funding rate. Used to calculate the funding payment owed to user","precision: FUNDING_RATE_PRECISION"],"type":"i64"},{"name":"baseAssetAmount","docs":["the size of the users perp position","precision: BASE_PRECISION"],"type":"i64"},{"name":"quoteAssetAmount","docs":["Used to calculate the users pnl. Upon entry, is equal to base_asset_amount * avg entry price - fees","Updated when the user open/closes position or settles pnl. Includes fees/funding","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"quoteBreakEvenAmount","docs":["The amount of quote the user would need to exit their position at to break even","Updated when the user open/closes position or settles pnl. Includes fees/funding","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"quoteEntryAmount","docs":["The amount quote the user entered the position with. Equal to base asset amount * avg entry price","Updated when the user open/closes position. Excludes fees/funding","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"openBids","docs":["The amount of non reduce only trigger orders the user has open","precision: BASE_PRECISION"],"type":"i64"},{"name":"openAsks","docs":["The amount of non reduce only trigger orders the user has open","precision: BASE_PRECISION"],"type":"i64"},{"name":"settledPnl","docs":["The amount of pnl settled in this market since opening the position","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"lpShares","docs":["The number of lp (liquidity provider) shares the user has in this perp market","LP shares allow users to provide liquidity via the AMM","precision: BASE_PRECISION"],"type":"u64"},{"name":"lastBaseAssetAmountPerLp","docs":["The last base asset amount per lp the amm had","Used to settle the users lp position","precision: BASE_PRECISION"],"type":"i64"},{"name":"lastQuoteAssetAmountPerLp","docs":["The last quote asset amount per lp the amm had","Used to settle the users lp position","precision: QUOTE_PRECISION"],"type":"i64"},{"name":"padding","type":{"array":["u8",2]}},{"name":"maxMarginRatio","type":"u16"},{"name":"marketIndex","docs":["The market index for the perp market"],"type":"u16"},{"name":"openOrders","docs":["The number of open orders"],"type":"u8"},{"name":"perLpBase","type":"i8"}]}},{"name":"Order","type":{"kind":"struct","fields":[{"name":"slot","docs":["The slot the order was placed"],"type":"u64"},{"name":"price","docs":["The limit price for the order (can be 0 for market orders)","For orders with an auction, this price isn't used until the auction is complete","precision: PRICE_PRECISION"],"type":"u64"},{"name":"baseAssetAmount","docs":["The size of the order","precision for perps: BASE_PRECISION","precision for spot: token mint precision"],"type":"u64"},{"name":"baseAssetAmountFilled","docs":["The amount of the order filled","precision for perps: BASE_PRECISION","precision for spot: token mint precision"],"type":"u64"},{"name":"quoteAssetAmountFilled","docs":["The amount of quote filled for the order","precision: QUOTE_PRECISION"],"type":"u64"},{"name":"triggerPrice","docs":["At what price the order will be triggered. Only relevant for trigger orders","precision: PRICE_PRECISION"],"type":"u64"},{"name":"auctionStartPrice","docs":["The start price for the auction. Only relevant for market/oracle orders","precision: PRICE_PRECISION"],"type":"i64"},{"name":"auctionEndPrice","docs":["The end price for the auction. Only relevant for market/oracle orders","precision: PRICE_PRECISION"],"type":"i64"},{"name":"maxTs","docs":["The time when the order will expire"],"type":"i64"},{"name":"oraclePriceOffset","docs":["If set, the order limit price is the oracle price + this offset","precision: PRICE_PRECISION"],"type":"i32"},{"name":"orderId","docs":["The id for the order. Each users has their own order id space"],"type":"u32"},{"name":"marketIndex","docs":["The perp/spot market index"],"type":"u16"},{"name":"status","docs":["Whether the order is open or unused"],"type":{"defined":"OrderStatus"}},{"name":"orderType","docs":["The type of order"],"type":{"defined":"OrderType"}},{"name":"marketType","docs":["Whether market is spot or perp"],"type":{"defined":"MarketType"}},{"name":"userOrderId","docs":["User generated order id. Can make it easier to place/cancel orders"],"type":"u8"},{"name":"existingPositionDirection","docs":["What the users position was when the order was placed"],"type":{"defined":"PositionDirection"}},{"name":"direction","docs":["Whether the user is going long or short. LONG = bid, SHORT = ask"],"type":{"defined":"PositionDirection"}},{"name":"reduceOnly","docs":["Whether the order is allowed to only reduce position size"],"type":"bool"},{"name":"postOnly","docs":["Whether the order must be a maker"],"type":"bool"},{"name":"immediateOrCancel","docs":["Whether the order must be canceled the same slot it is placed"],"type":"bool"},{"name":"triggerCondition","docs":["Whether the order is triggered above or below the trigger price. Only relevant for trigger orders"],"type":{"defined":"OrderTriggerCondition"}},{"name":"auctionDuration","docs":["How many slots the auction lasts"],"type":"u8"},{"name":"postedSlotTail","docs":["Last 8 bits of the slot the order was posted on-chain (not order slot for signed msg orders)"],"type":"u8"},{"name":"bitFlags","docs":["Bitflags for further classification","0: is_signed_message"],"type":"u8"},{"name":"padding","type":{"array":["u8",1]}}]}},{"name":"SwapDirection","type":{"kind":"enum","variants":[{"name":"Add"},{"name":"Remove"}]}},{"name":"ModifyOrderId","type":{"kind":"enum","variants":[{"name":"UserOrderId","fields":["u8"]},{"name":"OrderId","fields":["u32"]}]}},{"name":"PositionDirection","type":{"kind":"enum","variants":[{"name":"Long"},{"name":"Short"}]}},{"name":"SpotFulfillmentType","type":{"kind":"enum","variants":[{"name":"SerumV3"},{"name":"Match"},{"name":"PhoenixV1"},{"name":"OpenbookV2"}]}},{"name":"SwapReduceOnly","type":{"kind":"enum","variants":[{"name":"In"},{"name":"Out"}]}},{"name":"TwapPeriod","type":{"kind":"enum","variants":[{"name":"FundingPeriod"},{"name":"FiveMin"}]}},{"name":"LiquidationMultiplierType","type":{"kind":"enum","variants":[{"name":"Discount"},{"name":"Premium"}]}},{"name":"SettlementDirection","type":{"kind":"enum","variants":[{"name":"ToLpPool"},{"name":"FromLpPool"},{"name":"None"}]}},{"name":"MarginRequirementType","type":{"kind":"enum","variants":[{"name":"Initial"},{"name":"Fill"},{"name":"Maintenance"}]}},{"name":"OracleValidity","type":{"kind":"enum","variants":[{"name":"NonPositive"},{"name":"TooVolatile"},{"name":"TooUncertain"},{"name":"StaleForMargin"},{"name":"InsufficientDataPoints"},{"name":"StaleForAMM","fields":[{"name":"immediate","type":"bool"},{"name":"lowRisk","type":"bool"}]},{"name":"Valid"}]}},{"name":"DriftAction","type":{"kind":"enum","variants":[{"name":"UpdateFunding"},{"name":"SettlePnl"},{"name":"TriggerOrder"},{"name":"FillOrderMatch"},{"name":"FillOrderAmmLowRisk"},{"name":"FillOrderAmmImmediate"},{"name":"Liquidate"},{"name":"MarginCalc"},{"name":"UpdateTwap"},{"name":"UpdateAMMCurve"},{"name":"OracleOrderPrice"},{"name":"UseMMOraclePrice"},{"name":"UpdateAmmCache"},{"name":"UpdateLpPoolAum"},{"name":"LpPoolSwap"}]}},{"name":"LogMode","type":{"kind":"enum","variants":[{"name":"None"},{"name":"ExchangeOracle"},{"name":"MMOracle"},{"name":"SafeMMOracle"},{"name":"Margin"}]}},{"name":"PositionUpdateType","type":{"kind":"enum","variants":[{"name":"Open"},{"name":"Increase"},{"name":"Reduce"},{"name":"Close"},{"name":"Flip"}]}},{"name":"DepositExplanation","type":{"kind":"enum","variants":[{"name":"None"},{"name":"Transfer"},{"name":"Borrow"},{"name":"RepayBorrow"},{"name":"Reward"}]}},{"name":"DepositDirection","type":{"kind":"enum","variants":[{"name":"Deposit"},{"name":"Withdraw"}]}},{"name":"OrderAction","type":{"kind":"enum","variants":[{"name":"Place"},{"name":"Cancel"},{"name":"Fill"},{"name":"Trigger"},{"name":"Expire"}]}},{"name":"OrderActionExplanation","type":{"kind":"enum","variants":[{"name":"None"},{"name":"InsufficientFreeCollateral"},{"name":"OraclePriceBreachedLimitPrice"},{"name":"MarketOrderFilledToLimitPrice"},{"name":"OrderExpired"},{"name":"Liquidation"},{"name":"OrderFilledWithAMM"},{"name":"OrderFilledWithAMMJit"},{"name":"OrderFilledWithMatch"},{"name":"OrderFilledWithMatchJit"},{"name":"MarketExpired"},{"name":"RiskingIncreasingOrder"},{"name":"ReduceOnlyOrderIncreasedPosition"},{"name":"OrderFillWithSerum"},{"name":"NoBorrowLiquidity"},{"name":"OrderFillWithPhoenix"},{"name":"OrderFilledWithAMMJitLPSplit"},{"name":"OrderFilledWithLPJit"},{"name":"DeriskLp"},{"name":"OrderFilledWithOpenbookV2"},{"name":"TransferPerpPosition"}]}},{"name":"LPAction","type":{"kind":"enum","variants":[{"name":"AddLiquidity"},{"name":"RemoveLiquidity"},{"name":"SettleLiquidity"},{"name":"RemoveLiquidityDerisk"}]}},{"name":"LiquidationType","type":{"kind":"enum","variants":[{"name":"LiquidatePerp"},{"name":"LiquidateSpot"},{"name":"LiquidateBorrowForPerpPnl"},{"name":"LiquidatePerpPnlForDeposit"},{"name":"PerpBankruptcy"},{"name":"SpotBankruptcy"}]}},{"name":"SettlePnlExplanation","type":{"kind":"enum","variants":[{"name":"None"},{"name":"ExpiredPosition"}]}},{"name":"StakeAction","type":{"kind":"enum","variants":[{"name":"Stake"},{"name":"UnstakeRequest"},{"name":"UnstakeCancelRequest"},{"name":"Unstake"},{"name":"UnstakeTransfer"},{"name":"StakeTransfer"},{"name":"AdminDeposit"}]}},{"name":"FillMode","type":{"kind":"enum","variants":[{"name":"Fill"},{"name":"PlaceAndMake"},{"name":"PlaceAndTake","fields":["bool","u8"]},{"name":"Liquidation"}]}},{"name":"PerpFulfillmentMethod","type":{"kind":"enum","variants":[{"name":"AMM","fields":[{"option":"u64"}]},{"name":"Match","fields":["publicKey","u16","u64"]}]}},{"name":"SpotFulfillmentMethod","type":{"kind":"enum","variants":[{"name":"ExternalMarket"},{"name":"Match","fields":["publicKey","u16"]}]}},{"name":"ConstituentStatus","type":{"kind":"enum","variants":[{"name":"ReduceOnly"},{"name":"Decommissioned"}]}},{"name":"MarginCalculationMode","type":{"kind":"enum","variants":[{"name":"Standard","fields":[{"name":"trackOpenOrdersFraction","type":"bool"}]},{"name":"Liquidation","fields":[{"name":"marketToTrackMarginRequirement","type":{"option":{"defined":"MarketIdentifier"}}}]}]}},{"name":"OracleSource","type":{"kind":"enum","variants":[{"name":"Pyth"},{"name":"Switchboard"},{"name":"QuoteAsset"},{"name":"Pyth1K"},{"name":"Pyth1M"},{"name":"PythStableCoin"},{"name":"Prelaunch"},{"name":"PythPull"},{"name":"Pyth1KPull"},{"name":"Pyth1MPull"},{"name":"PythStableCoinPull"},{"name":"SwitchboardOnDemand"},{"name":"PythLazer"},{"name":"PythLazer1K"},{"name":"PythLazer1M"},{"name":"PythLazerStableCoin"}]}},{"name":"OrderParamsBitFlag","type":{"kind":"enum","variants":[{"name":"ImmediateOrCancel"},{"name":"UpdateHighLeverageMode"}]}},{"name":"PostOnlyParam","type":{"kind":"enum","variants":[{"name":"None"},{"name":"MustPostOnly"},{"name":"TryPostOnly"},{"name":"Slide"}]}},{"name":"ModifyOrderPolicy","type":{"kind":"enum","variants":[{"name":"MustModify"},{"name":"ExcludePreviousFill"}]}},{"name":"PlaceAndTakeOrderSuccessCondition","type":{"kind":"enum","variants":[{"name":"PartialFill"},{"name":"FullFill"}]}},{"name":"PerpOperation","type":{"kind":"enum","variants":[{"name":"UpdateFunding"},{"name":"AmmFill"},{"name":"Fill"},{"name":"SettlePnl"},{"name":"SettlePnlWithPosition"},{"name":"Liquidation"},{"name":"AmmImmediateFill"},{"name":"SettleRevPool"}]}},{"name":"SpotOperation","type":{"kind":"enum","variants":[{"name":"UpdateCumulativeInterest"},{"name":"Fill"},{"name":"Deposit"},{"name":"Withdraw"},{"name":"Liquidation"}]}},{"name":"InsuranceFundOperation","type":{"kind":"enum","variants":[{"name":"Init"},{"name":"Add"},{"name":"RequestRemove"},{"name":"Remove"}]}},{"name":"PerpLpOperation","type":{"kind":"enum","variants":[{"name":"TrackAmmRevenue"},{"name":"SettleQuoteOwed"}]}},{"name":"ConstituentLpOperation","type":{"kind":"enum","variants":[{"name":"Swap"},{"name":"Deposit"},{"name":"Withdraw"}]}},{"name":"MarketStatus","type":{"kind":"enum","variants":[{"name":"Initialized"},{"name":"Active"},{"name":"FundingPaused"},{"name":"AmmPaused"},{"name":"FillPaused"},{"name":"WithdrawPaused"},{"name":"ReduceOnly"},{"name":"Settlement"},{"name":"Delisted"}]}},{"name":"LpStatus","type":{"kind":"enum","variants":[{"name":"Uncollateralized"},{"name":"Active"},{"name":"Decommissioning"}]}},{"name":"ContractType","type":{"kind":"enum","variants":[{"name":"Perpetual"},{"name":"Future"},{"name":"Prediction"}]}},{"name":"ContractTier","type":{"kind":"enum","variants":[{"name":"A"},{"name":"B"},{"name":"C"},{"name":"Speculative"},{"name":"HighlySpeculative"},{"name":"Isolated"}]}},{"name":"RevenueShareOrderBitFlag","type":{"kind":"enum","variants":[{"name":"Init"},{"name":"Open"},{"name":"Completed"},{"name":"Referral"}]}},{"name":"SettlePnlMode","type":{"kind":"enum","variants":[{"name":"MustSettle"},{"name":"TrySettle"}]}},{"name":"SpotBalanceType","type":{"kind":"enum","variants":[{"name":"Deposit"},{"name":"Borrow"}]}},{"name":"SpotFulfillmentConfigStatus","type":{"kind":"enum","variants":[{"name":"Enabled"},{"name":"Disabled"}]}},{"name":"AssetTier","type":{"kind":"enum","variants":[{"name":"Collateral"},{"name":"Protected"},{"name":"Cross"},{"name":"Isolated"},{"name":"Unlisted"}]}},{"name":"TokenProgramFlag","type":{"kind":"enum","variants":[{"name":"Token2022"},{"name":"TransferHook"}]}},{"name":"ExchangeStatus","type":{"kind":"enum","variants":[{"name":"DepositPaused"},{"name":"WithdrawPaused"},{"name":"AmmPaused"},{"name":"FillPaused"},{"name":"LiqPaused"},{"name":"FundingPaused"},{"name":"SettlePnlPaused"},{"name":"AmmImmediateFillPaused"}]}},{"name":"FeatureBitFlags","type":{"kind":"enum","variants":[{"name":"MmOracleUpdate"},{"name":"MedianTriggerPrice"},{"name":"BuilderCodes"},{"name":"BuilderReferral"}]}},{"name":"LpPoolFeatureBitFlags","type":{"kind":"enum","variants":[{"name":"SettleLpPool"},{"name":"SwapLpPool"},{"name":"MintRedeemLpPool"}]}},{"name":"UserStatus","type":{"kind":"enum","variants":[{"name":"BeingLiquidated"},{"name":"Bankrupt"},{"name":"ReduceOnly"},{"name":"AdvancedLp"},{"name":"ProtectedMakerOrders"}]}},{"name":"AssetType","type":{"kind":"enum","variants":[{"name":"Base"},{"name":"Quote"}]}},{"name":"OrderStatus","type":{"kind":"enum","variants":[{"name":"Init"},{"name":"Open"},{"name":"Filled"},{"name":"Canceled"}]}},{"name":"OrderType","type":{"kind":"enum","variants":[{"name":"Market"},{"name":"Limit"},{"name":"TriggerMarket"},{"name":"TriggerLimit"},{"name":"Oracle"}]}},{"name":"OrderTriggerCondition","type":{"kind":"enum","variants":[{"name":"Above"},{"name":"Below"},{"name":"TriggeredAbove"},{"name":"TriggeredBelow"}]}},{"name":"MarketType","type":{"kind":"enum","variants":[{"name":"Spot"},{"name":"Perp"}]}},{"name":"OrderBitFlag","type":{"kind":"enum","variants":[{"name":"SignedMessage"},{"name":"OracleTriggerMarket"},{"name":"SafeTriggerOrder"},{"name":"NewTriggerReduceOnly"},{"name":"HasBuilder"}]}},{"name":"ReferrerStatus","type":{"kind":"enum","variants":[{"name":"IsReferrer"},{"name":"IsReferred"},{"name":"BuilderReferral"}]}},{"name":"MarginMode","type":{"kind":"enum","variants":[{"name":"Default"},{"name":"HighLeverage"},{"name":"HighLeverageMaintenance"}]}},{"name":"FuelOverflowStatus","type":{"kind":"enum","variants":[{"name":"Exists"}]}},{"name":"UserStatsPausedOperations","type":{"kind":"enum","variants":[{"name":"UpdateBidAskTwap"},{"name":"AmmAtomicFill"},{"name":"AmmAtomicRiskIncreasingFill"}]}},{"name":"SignatureVerificationError","type":{"kind":"enum","variants":[{"name":"InvalidEd25519InstructionProgramId"},{"name":"InvalidEd25519InstructionDataLength"},{"name":"InvalidSignatureIndex"},{"name":"InvalidSignatureOffset"},{"name":"InvalidPublicKeyOffset"},{"name":"InvalidMessageOffset"},{"name":"InvalidMessageDataSize"},{"name":"InvalidInstructionIndex"},{"name":"MessageOffsetOverflow"},{"name":"InvalidMessageHex"},{"name":"InvalidMessageData"},{"name":"LoadInstructionAtFailed"}]}}],"events":[{"name":"NewUserRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"userAuthority","type":"publicKey","index":false},{"name":"user","type":"publicKey","index":false},{"name":"subAccountId","type":"u16","index":false},{"name":"name","type":{"array":["u8",32]},"index":false},{"name":"referrer","type":"publicKey","index":false}]},{"name":"DepositRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"userAuthority","type":"publicKey","index":false},{"name":"user","type":"publicKey","index":false},{"name":"direction","type":{"defined":"DepositDirection"},"index":false},{"name":"depositRecordId","type":"u64","index":false},{"name":"amount","type":"u64","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"oraclePrice","type":"i64","index":false},{"name":"marketDepositBalance","type":"u128","index":false},{"name":"marketWithdrawBalance","type":"u128","index":false},{"name":"marketCumulativeDepositInterest","type":"u128","index":false},{"name":"marketCumulativeBorrowInterest","type":"u128","index":false},{"name":"totalDepositsAfter","type":"u64","index":false},{"name":"totalWithdrawsAfter","type":"u64","index":false},{"name":"explanation","type":{"defined":"DepositExplanation"},"index":false},{"name":"transferUser","type":{"option":"publicKey"},"index":false},{"name":"signer","type":{"option":"publicKey"},"index":false},{"name":"userTokenAmountAfter","type":"i128","index":false}]},{"name":"SpotInterestRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"depositBalance","type":"u128","index":false},{"name":"cumulativeDepositInterest","type":"u128","index":false},{"name":"borrowBalance","type":"u128","index":false},{"name":"cumulativeBorrowInterest","type":"u128","index":false},{"name":"optimalUtilization","type":"u32","index":false},{"name":"optimalBorrowRate","type":"u32","index":false},{"name":"maxBorrowRate","type":"u32","index":false}]},{"name":"FundingPaymentRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"userAuthority","type":"publicKey","index":false},{"name":"user","type":"publicKey","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"fundingPayment","type":"i64","index":false},{"name":"baseAssetAmount","type":"i64","index":false},{"name":"userLastCumulativeFunding","type":"i64","index":false},{"name":"ammCumulativeFundingLong","type":"i128","index":false},{"name":"ammCumulativeFundingShort","type":"i128","index":false}]},{"name":"FundingRateRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"recordId","type":"u64","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"fundingRate","type":"i64","index":false},{"name":"fundingRateLong","type":"i128","index":false},{"name":"fundingRateShort","type":"i128","index":false},{"name":"cumulativeFundingRateLong","type":"i128","index":false},{"name":"cumulativeFundingRateShort","type":"i128","index":false},{"name":"oraclePriceTwap","type":"i64","index":false},{"name":"markPriceTwap","type":"u64","index":false},{"name":"periodRevenue","type":"i64","index":false},{"name":"baseAssetAmountWithAmm","type":"i128","index":false},{"name":"baseAssetAmountWithUnsettledLp","type":"i128","index":false}]},{"name":"CurveRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"recordId","type":"u64","index":false},{"name":"pegMultiplierBefore","type":"u128","index":false},{"name":"baseAssetReserveBefore","type":"u128","index":false},{"name":"quoteAssetReserveBefore","type":"u128","index":false},{"name":"sqrtKBefore","type":"u128","index":false},{"name":"pegMultiplierAfter","type":"u128","index":false},{"name":"baseAssetReserveAfter","type":"u128","index":false},{"name":"quoteAssetReserveAfter","type":"u128","index":false},{"name":"sqrtKAfter","type":"u128","index":false},{"name":"baseAssetAmountLong","type":"u128","index":false},{"name":"baseAssetAmountShort","type":"u128","index":false},{"name":"baseAssetAmountWithAmm","type":"i128","index":false},{"name":"totalFee","type":"i128","index":false},{"name":"totalFeeMinusDistributions","type":"i128","index":false},{"name":"adjustmentCost","type":"i128","index":false},{"name":"oraclePrice","type":"i64","index":false},{"name":"fillRecord","type":"u128","index":false},{"name":"numberOfUsers","type":"u32","index":false},{"name":"marketIndex","type":"u16","index":false}]},{"name":"SignedMsgOrderRecord","fields":[{"name":"user","type":"publicKey","index":false},{"name":"hash","type":"string","index":false},{"name":"matchingOrderParams","type":{"defined":"OrderParams"},"index":false},{"name":"userOrderId","type":"u32","index":false},{"name":"signedMsgOrderMaxSlot","type":"u64","index":false},{"name":"signedMsgOrderUuid","type":{"array":["u8",8]},"index":false},{"name":"ts","type":"i64","index":false}]},{"name":"OrderRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"user","type":"publicKey","index":false},{"name":"order","type":{"defined":"Order"},"index":false}]},{"name":"OrderActionRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"action","type":{"defined":"OrderAction"},"index":false},{"name":"actionExplanation","type":{"defined":"OrderActionExplanation"},"index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"marketType","type":{"defined":"MarketType"},"index":false},{"name":"filler","type":{"option":"publicKey"},"index":false},{"name":"fillerReward","type":{"option":"u64"},"index":false},{"name":"fillRecordId","type":{"option":"u64"},"index":false},{"name":"baseAssetAmountFilled","type":{"option":"u64"},"index":false},{"name":"quoteAssetAmountFilled","type":{"option":"u64"},"index":false},{"name":"takerFee","type":{"option":"u64"},"index":false},{"name":"makerFee","type":{"option":"i64"},"index":false},{"name":"referrerReward","type":{"option":"u32"},"index":false},{"name":"quoteAssetAmountSurplus","type":{"option":"i64"},"index":false},{"name":"spotFulfillmentMethodFee","type":{"option":"u64"},"index":false},{"name":"taker","type":{"option":"publicKey"},"index":false},{"name":"takerOrderId","type":{"option":"u32"},"index":false},{"name":"takerOrderDirection","type":{"option":{"defined":"PositionDirection"}},"index":false},{"name":"takerOrderBaseAssetAmount","type":{"option":"u64"},"index":false},{"name":"takerOrderCumulativeBaseAssetAmountFilled","type":{"option":"u64"},"index":false},{"name":"takerOrderCumulativeQuoteAssetAmountFilled","type":{"option":"u64"},"index":false},{"name":"maker","type":{"option":"publicKey"},"index":false},{"name":"makerOrderId","type":{"option":"u32"},"index":false},{"name":"makerOrderDirection","type":{"option":{"defined":"PositionDirection"}},"index":false},{"name":"makerOrderBaseAssetAmount","type":{"option":"u64"},"index":false},{"name":"makerOrderCumulativeBaseAssetAmountFilled","type":{"option":"u64"},"index":false},{"name":"makerOrderCumulativeQuoteAssetAmountFilled","type":{"option":"u64"},"index":false},{"name":"oraclePrice","type":"i64","index":false},{"name":"bitFlags","type":"u8","index":false},{"name":"takerExistingQuoteEntryAmount","type":{"option":"u64"},"index":false},{"name":"takerExistingBaseAssetAmount","type":{"option":"u64"},"index":false},{"name":"makerExistingQuoteEntryAmount","type":{"option":"u64"},"index":false},{"name":"makerExistingBaseAssetAmount","type":{"option":"u64"},"index":false},{"name":"triggerPrice","type":{"option":"u64"},"index":false},{"name":"builderIdx","type":{"option":"u8"},"index":false},{"name":"builderFee","type":{"option":"u64"},"index":false}]},{"name":"LPRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"user","type":"publicKey","index":false},{"name":"action","type":{"defined":"LPAction"},"index":false},{"name":"nShares","type":"u64","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"deltaBaseAssetAmount","type":"i64","index":false},{"name":"deltaQuoteAssetAmount","type":"i64","index":false},{"name":"pnl","type":"i64","index":false}]},{"name":"LiquidationRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"liquidationType","type":{"defined":"LiquidationType"},"index":false},{"name":"user","type":"publicKey","index":false},{"name":"liquidator","type":"publicKey","index":false},{"name":"marginRequirement","type":"u128","index":false},{"name":"totalCollateral","type":"i128","index":false},{"name":"marginFreed","type":"u64","index":false},{"name":"liquidationId","type":"u16","index":false},{"name":"bankrupt","type":"bool","index":false},{"name":"canceledOrderIds","type":{"vec":"u32"},"index":false},{"name":"liquidatePerp","type":{"defined":"LiquidatePerpRecord"},"index":false},{"name":"liquidateSpot","type":{"defined":"LiquidateSpotRecord"},"index":false},{"name":"liquidateBorrowForPerpPnl","type":{"defined":"LiquidateBorrowForPerpPnlRecord"},"index":false},{"name":"liquidatePerpPnlForDeposit","type":{"defined":"LiquidatePerpPnlForDepositRecord"},"index":false},{"name":"perpBankruptcy","type":{"defined":"PerpBankruptcyRecord"},"index":false},{"name":"spotBankruptcy","type":{"defined":"SpotBankruptcyRecord"},"index":false}]},{"name":"SettlePnlRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"user","type":"publicKey","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"pnl","type":"i128","index":false},{"name":"baseAssetAmount","type":"i64","index":false},{"name":"quoteAssetAmountAfter","type":"i64","index":false},{"name":"quoteEntryAmount","type":"i64","index":false},{"name":"settlePrice","type":"i64","index":false},{"name":"explanation","type":{"defined":"SettlePnlExplanation"},"index":false}]},{"name":"InsuranceFundRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"spotMarketIndex","type":"u16","index":false},{"name":"perpMarketIndex","type":"u16","index":false},{"name":"userIfFactor","type":"u32","index":false},{"name":"totalIfFactor","type":"u32","index":false},{"name":"vaultAmountBefore","type":"u64","index":false},{"name":"insuranceVaultAmountBefore","type":"u64","index":false},{"name":"totalIfSharesBefore","type":"u128","index":false},{"name":"totalIfSharesAfter","type":"u128","index":false},{"name":"amount","type":"i64","index":false}]},{"name":"InsuranceFundStakeRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"userAuthority","type":"publicKey","index":false},{"name":"action","type":{"defined":"StakeAction"},"index":false},{"name":"amount","type":"u64","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"insuranceVaultAmountBefore","type":"u64","index":false},{"name":"ifSharesBefore","type":"u128","index":false},{"name":"userIfSharesBefore","type":"u128","index":false},{"name":"totalIfSharesBefore","type":"u128","index":false},{"name":"ifSharesAfter","type":"u128","index":false},{"name":"userIfSharesAfter","type":"u128","index":false},{"name":"totalIfSharesAfter","type":"u128","index":false}]},{"name":"InsuranceFundSwapRecord","fields":[{"name":"rebalanceConfig","type":"publicKey","index":false},{"name":"inIfTotalSharesBefore","type":"u128","index":false},{"name":"outIfTotalSharesBefore","type":"u128","index":false},{"name":"inIfUserSharesBefore","type":"u128","index":false},{"name":"outIfUserSharesBefore","type":"u128","index":false},{"name":"inIfTotalSharesAfter","type":"u128","index":false},{"name":"outIfTotalSharesAfter","type":"u128","index":false},{"name":"inIfUserSharesAfter","type":"u128","index":false},{"name":"outIfUserSharesAfter","type":"u128","index":false},{"name":"ts","type":"i64","index":false},{"name":"inAmount","type":"u64","index":false},{"name":"outAmount","type":"u64","index":false},{"name":"outOraclePrice","type":"u64","index":false},{"name":"outOraclePriceTwap","type":"i64","index":false},{"name":"inVaultAmountBefore","type":"u64","index":false},{"name":"outVaultAmountBefore","type":"u64","index":false},{"name":"inFundVaultAmountAfter","type":"u64","index":false},{"name":"outFundVaultAmountAfter","type":"u64","index":false},{"name":"inMarketIndex","type":"u16","index":false},{"name":"outMarketIndex","type":"u16","index":false}]},{"name":"TransferProtocolIfSharesToRevenuePoolRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"amount","type":"u64","index":false},{"name":"shares","type":"u128","index":false},{"name":"ifVaultAmountBefore","type":"u64","index":false},{"name":"protocolSharesBefore","type":"u128","index":false},{"name":"transferAmount","type":"u64","index":false}]},{"name":"SwapRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"user","type":"publicKey","index":false},{"name":"amountOut","type":"u64","index":false},{"name":"amountIn","type":"u64","index":false},{"name":"outMarketIndex","type":"u16","index":false},{"name":"inMarketIndex","type":"u16","index":false},{"name":"outOraclePrice","type":"i64","index":false},{"name":"inOraclePrice","type":"i64","index":false},{"name":"fee","type":"u64","index":false}]},{"name":"SpotMarketVaultDepositRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"depositBalance","type":"u128","index":false},{"name":"cumulativeDepositInterestBefore","type":"u128","index":false},{"name":"cumulativeDepositInterestAfter","type":"u128","index":false},{"name":"depositTokenAmountBefore","type":"u64","index":false},{"name":"amount","type":"u64","index":false}]},{"name":"DeleteUserRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"userAuthority","type":"publicKey","index":false},{"name":"user","type":"publicKey","index":false},{"name":"subAccountId","type":"u16","index":false},{"name":"keeper","type":{"option":"publicKey"},"index":false}]},{"name":"FuelSweepRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"userStatsFuelInsurance","type":"u32","index":false},{"name":"userStatsFuelDeposits","type":"u32","index":false},{"name":"userStatsFuelBorrows","type":"u32","index":false},{"name":"userStatsFuelPositions","type":"u32","index":false},{"name":"userStatsFuelTaker","type":"u32","index":false},{"name":"userStatsFuelMaker","type":"u32","index":false},{"name":"fuelOverflowFuelInsurance","type":"u128","index":false},{"name":"fuelOverflowFuelDeposits","type":"u128","index":false},{"name":"fuelOverflowFuelBorrows","type":"u128","index":false},{"name":"fuelOverflowFuelPositions","type":"u128","index":false},{"name":"fuelOverflowFuelTaker","type":"u128","index":false},{"name":"fuelOverflowFuelMaker","type":"u128","index":false}]},{"name":"FuelSeasonRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"fuelInsurance","type":"u128","index":false},{"name":"fuelDeposits","type":"u128","index":false},{"name":"fuelBorrows","type":"u128","index":false},{"name":"fuelPositions","type":"u128","index":false},{"name":"fuelTaker","type":"u128","index":false},{"name":"fuelMaker","type":"u128","index":false},{"name":"fuelTotal","type":"u128","index":false}]},{"name":"RevenueShareSettleRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"builder","type":{"option":"publicKey"},"index":false},{"name":"referrer","type":{"option":"publicKey"},"index":false},{"name":"feeSettled","type":"u64","index":false},{"name":"marketIndex","type":"u16","index":false},{"name":"marketType","type":{"defined":"MarketType"},"index":false},{"name":"builderSubAccountId","type":"u16","index":false},{"name":"builderTotalReferrerRewards","type":"u64","index":false},{"name":"builderTotalBuilderRewards","type":"u64","index":false}]},{"name":"LPSettleRecord","fields":[{"name":"recordId","type":"u64","index":false},{"name":"lastTs","type":"i64","index":false},{"name":"lastSlot","type":"u64","index":false},{"name":"ts","type":"i64","index":false},{"name":"slot","type":"u64","index":false},{"name":"perpMarketIndex","type":"u16","index":false},{"name":"settleToLpAmount","type":"i64","index":false},{"name":"perpAmmPnlDelta","type":"i64","index":false},{"name":"perpAmmExFeeDelta","type":"i64","index":false},{"name":"lpAum","type":"u128","index":false},{"name":"lpPrice","type":"u128","index":false},{"name":"lpPool","type":"publicKey","index":false}]},{"name":"LPSwapRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"slot","type":"u64","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"outAmount","type":"u128","index":false},{"name":"inAmount","type":"u128","index":false},{"name":"outFee","type":"i128","index":false},{"name":"inFee","type":"i128","index":false},{"name":"outSpotMarketIndex","type":"u16","index":false},{"name":"inSpotMarketIndex","type":"u16","index":false},{"name":"outConstituentIndex","type":"u16","index":false},{"name":"inConstituentIndex","type":"u16","index":false},{"name":"outOraclePrice","type":"i64","index":false},{"name":"inOraclePrice","type":"i64","index":false},{"name":"lastAum","type":"u128","index":false},{"name":"lastAumSlot","type":"u64","index":false},{"name":"inMarketCurrentWeight","type":"i64","index":false},{"name":"outMarketCurrentWeight","type":"i64","index":false},{"name":"inMarketTargetWeight","type":"i64","index":false},{"name":"outMarketTargetWeight","type":"i64","index":false},{"name":"inSwapId","type":"u64","index":false},{"name":"outSwapId","type":"u64","index":false},{"name":"lpPool","type":"publicKey","index":false}]},{"name":"LPMintRedeemRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"slot","type":"u64","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"description","type":"u8","index":false},{"name":"amount","type":"u128","index":false},{"name":"fee","type":"i128","index":false},{"name":"spotMarketIndex","type":"u16","index":false},{"name":"constituentIndex","type":"u16","index":false},{"name":"oraclePrice","type":"i64","index":false},{"name":"mint","type":"publicKey","index":false},{"name":"lpAmount","type":"u64","index":false},{"name":"lpFee","type":"i64","index":false},{"name":"lpPrice","type":"u128","index":false},{"name":"mintRedeemId","type":"u64","index":false},{"name":"lastAum","type":"u128","index":false},{"name":"lastAumSlot","type":"u64","index":false},{"name":"inMarketCurrentWeight","type":"i64","index":false},{"name":"inMarketTargetWeight","type":"i64","index":false},{"name":"lpPool","type":"publicKey","index":false}]},{"name":"LPBorrowLendDepositRecord","fields":[{"name":"ts","type":"i64","index":false},{"name":"slot","type":"u64","index":false},{"name":"spotMarketIndex","type":"u16","index":false},{"name":"constituentIndex","type":"u16","index":false},{"name":"direction","type":{"defined":"DepositDirection"},"index":false},{"name":"tokenBalance","type":"i64","index":false},{"name":"lastTokenBalance","type":"i64","index":false},{"name":"interestAccruedTokenAmount","type":"i64","index":false},{"name":"amountDepositWithdraw","type":"u64","index":false},{"name":"lpPool","type":"publicKey","index":false}]}],"errors":[{"code":6000,"name":"InvalidSpotMarketAuthority","msg":"Invalid Spot Market Authority"},{"code":6001,"name":"InvalidInsuranceFundAuthority","msg":"Clearing house not insurance fund authority"},{"code":6002,"name":"InsufficientDeposit","msg":"Insufficient deposit"},{"code":6003,"name":"InsufficientCollateral","msg":"Insufficient collateral"},{"code":6004,"name":"SufficientCollateral","msg":"Sufficient collateral"},{"code":6005,"name":"MaxNumberOfPositions","msg":"Max number of positions taken"},{"code":6006,"name":"AdminControlsPricesDisabled","msg":"Admin Controls Prices Disabled"},{"code":6007,"name":"MarketDelisted","msg":"Market Delisted"},{"code":6008,"name":"MarketIndexAlreadyInitialized","msg":"Market Index Already Initialized"},{"code":6009,"name":"UserAccountAndUserPositionsAccountMismatch","msg":"User Account And User Positions Account Mismatch"},{"code":6010,"name":"UserHasNoPositionInMarket","msg":"User Has No Position In Market"},{"code":6011,"name":"InvalidInitialPeg","msg":"Invalid Initial Peg"},{"code":6012,"name":"InvalidRepegRedundant","msg":"AMM repeg already configured with amt given"},{"code":6013,"name":"InvalidRepegDirection","msg":"AMM repeg incorrect repeg direction"},{"code":6014,"name":"InvalidRepegProfitability","msg":"AMM repeg out of bounds pnl"},{"code":6015,"name":"SlippageOutsideLimit","msg":"Slippage Outside Limit Price"},{"code":6016,"name":"OrderSizeTooSmall","msg":"Order Size Too Small"},{"code":6017,"name":"InvalidUpdateK","msg":"Price change too large when updating K"},{"code":6018,"name":"AdminWithdrawTooLarge","msg":"Admin tried to withdraw amount larger than fees collected"},{"code":6019,"name":"MathError","msg":"Math Error"},{"code":6020,"name":"BnConversionError","msg":"Conversion to u128/u64 failed with an overflow or underflow"},{"code":6021,"name":"ClockUnavailable","msg":"Clock unavailable"},{"code":6022,"name":"UnableToLoadOracle","msg":"Unable To Load Oracles"},{"code":6023,"name":"PriceBandsBreached","msg":"Price Bands Breached"},{"code":6024,"name":"ExchangePaused","msg":"Exchange is paused"},{"code":6025,"name":"InvalidWhitelistToken","msg":"Invalid whitelist token"},{"code":6026,"name":"WhitelistTokenNotFound","msg":"Whitelist token not found"},{"code":6027,"name":"InvalidDiscountToken","msg":"Invalid discount token"},{"code":6028,"name":"DiscountTokenNotFound","msg":"Discount token not found"},{"code":6029,"name":"ReferrerNotFound","msg":"Referrer not found"},{"code":6030,"name":"ReferrerStatsNotFound","msg":"ReferrerNotFound"},{"code":6031,"name":"ReferrerMustBeWritable","msg":"ReferrerMustBeWritable"},{"code":6032,"name":"ReferrerStatsMustBeWritable","msg":"ReferrerMustBeWritable"},{"code":6033,"name":"ReferrerAndReferrerStatsAuthorityUnequal","msg":"ReferrerAndReferrerStatsAuthorityUnequal"},{"code":6034,"name":"InvalidReferrer","msg":"InvalidReferrer"},{"code":6035,"name":"InvalidOracle","msg":"InvalidOracle"},{"code":6036,"name":"OracleNotFound","msg":"OracleNotFound"},{"code":6037,"name":"LiquidationsBlockedByOracle","msg":"Liquidations Blocked By Oracle"},{"code":6038,"name":"MaxDeposit","msg":"Can not deposit more than max deposit"},{"code":6039,"name":"CantDeleteUserWithCollateral","msg":"Can not delete user that still has collateral"},{"code":6040,"name":"InvalidFundingProfitability","msg":"AMM funding out of bounds pnl"},{"code":6041,"name":"CastingFailure","msg":"Casting Failure"},{"code":6042,"name":"InvalidOrder","msg":"InvalidOrder"},{"code":6043,"name":"InvalidOrderMaxTs","msg":"InvalidOrderMaxTs"},{"code":6044,"name":"InvalidOrderMarketType","msg":"InvalidOrderMarketType"},{"code":6045,"name":"InvalidOrderForInitialMarginReq","msg":"InvalidOrderForInitialMarginReq"},{"code":6046,"name":"InvalidOrderNotRiskReducing","msg":"InvalidOrderNotRiskReducing"},{"code":6047,"name":"InvalidOrderSizeTooSmall","msg":"InvalidOrderSizeTooSmall"},{"code":6048,"name":"InvalidOrderNotStepSizeMultiple","msg":"InvalidOrderNotStepSizeMultiple"},{"code":6049,"name":"InvalidOrderBaseQuoteAsset","msg":"InvalidOrderBaseQuoteAsset"},{"code":6050,"name":"InvalidOrderIOC","msg":"InvalidOrderIOC"},{"code":6051,"name":"InvalidOrderPostOnly","msg":"InvalidOrderPostOnly"},{"code":6052,"name":"InvalidOrderIOCPostOnly","msg":"InvalidOrderIOCPostOnly"},{"code":6053,"name":"InvalidOrderTrigger","msg":"InvalidOrderTrigger"},{"code":6054,"name":"InvalidOrderAuction","msg":"InvalidOrderAuction"},{"code":6055,"name":"InvalidOrderOracleOffset","msg":"InvalidOrderOracleOffset"},{"code":6056,"name":"InvalidOrderMinOrderSize","msg":"InvalidOrderMinOrderSize"},{"code":6057,"name":"PlacePostOnlyLimitFailure","msg":"Failed to Place Post-Only Limit Order"},{"code":6058,"name":"UserHasNoOrder","msg":"User has no order"},{"code":6059,"name":"OrderAmountTooSmall","msg":"Order Amount Too Small"},{"code":6060,"name":"MaxNumberOfOrders","msg":"Max number of orders taken"},{"code":6061,"name":"OrderDoesNotExist","msg":"Order does not exist"},{"code":6062,"name":"OrderNotOpen","msg":"Order not open"},{"code":6063,"name":"FillOrderDidNotUpdateState","msg":"FillOrderDidNotUpdateState"},{"code":6064,"name":"ReduceOnlyOrderIncreasedRisk","msg":"Reduce only order increased risk"},{"code":6065,"name":"UnableToLoadAccountLoader","msg":"Unable to load AccountLoader"},{"code":6066,"name":"TradeSizeTooLarge","msg":"Trade Size Too Large"},{"code":6067,"name":"UserCantReferThemselves","msg":"User cant refer themselves"},{"code":6068,"name":"DidNotReceiveExpectedReferrer","msg":"Did not receive expected referrer"},{"code":6069,"name":"CouldNotDeserializeReferrer","msg":"Could not deserialize referrer"},{"code":6070,"name":"CouldNotDeserializeReferrerStats","msg":"Could not deserialize referrer stats"},{"code":6071,"name":"UserOrderIdAlreadyInUse","msg":"User Order Id Already In Use"},{"code":6072,"name":"NoPositionsLiquidatable","msg":"No positions liquidatable"},{"code":6073,"name":"InvalidMarginRatio","msg":"Invalid Margin Ratio"},{"code":6074,"name":"CantCancelPostOnlyOrder","msg":"Cant Cancel Post Only Order"},{"code":6075,"name":"InvalidOracleOffset","msg":"InvalidOracleOffset"},{"code":6076,"name":"CantExpireOrders","msg":"CantExpireOrders"},{"code":6077,"name":"CouldNotLoadMarketData","msg":"CouldNotLoadMarketData"},{"code":6078,"name":"PerpMarketNotFound","msg":"PerpMarketNotFound"},{"code":6079,"name":"InvalidMarketAccount","msg":"InvalidMarketAccount"},{"code":6080,"name":"UnableToLoadPerpMarketAccount","msg":"UnableToLoadMarketAccount"},{"code":6081,"name":"MarketWrongMutability","msg":"MarketWrongMutability"},{"code":6082,"name":"UnableToCastUnixTime","msg":"UnableToCastUnixTime"},{"code":6083,"name":"CouldNotFindSpotPosition","msg":"CouldNotFindSpotPosition"},{"code":6084,"name":"NoSpotPositionAvailable","msg":"NoSpotPositionAvailable"},{"code":6085,"name":"InvalidSpotMarketInitialization","msg":"InvalidSpotMarketInitialization"},{"code":6086,"name":"CouldNotLoadSpotMarketData","msg":"CouldNotLoadSpotMarketData"},{"code":6087,"name":"SpotMarketNotFound","msg":"SpotMarketNotFound"},{"code":6088,"name":"InvalidSpotMarketAccount","msg":"InvalidSpotMarketAccount"},{"code":6089,"name":"UnableToLoadSpotMarketAccount","msg":"UnableToLoadSpotMarketAccount"},{"code":6090,"name":"SpotMarketWrongMutability","msg":"SpotMarketWrongMutability"},{"code":6091,"name":"SpotMarketInterestNotUpToDate","msg":"SpotInterestNotUpToDate"},{"code":6092,"name":"SpotMarketInsufficientDeposits","msg":"SpotMarketInsufficientDeposits"},{"code":6093,"name":"UserMustSettleTheirOwnPositiveUnsettledPNL","msg":"UserMustSettleTheirOwnPositiveUnsettledPNL"},{"code":6094,"name":"CantUpdatePoolBalanceType","msg":"CantUpdatePoolBalanceType"},{"code":6095,"name":"InsufficientCollateralForSettlingPNL","msg":"InsufficientCollateralForSettlingPNL"},{"code":6096,"name":"AMMNotUpdatedInSameSlot","msg":"AMMNotUpdatedInSameSlot"},{"code":6097,"name":"AuctionNotComplete","msg":"AuctionNotComplete"},{"code":6098,"name":"MakerNotFound","msg":"MakerNotFound"},{"code":6099,"name":"MakerStatsNotFound","msg":"MakerNotFound"},{"code":6100,"name":"MakerMustBeWritable","msg":"MakerMustBeWritable"},{"code":6101,"name":"MakerStatsMustBeWritable","msg":"MakerMustBeWritable"},{"code":6102,"name":"MakerOrderNotFound","msg":"MakerOrderNotFound"},{"code":6103,"name":"CouldNotDeserializeMaker","msg":"CouldNotDeserializeMaker"},{"code":6104,"name":"CouldNotDeserializeMakerStats","msg":"CouldNotDeserializeMaker"},{"code":6105,"name":"AuctionPriceDoesNotSatisfyMaker","msg":"AuctionPriceDoesNotSatisfyMaker"},{"code":6106,"name":"MakerCantFulfillOwnOrder","msg":"MakerCantFulfillOwnOrder"},{"code":6107,"name":"MakerOrderMustBePostOnly","msg":"MakerOrderMustBePostOnly"},{"code":6108,"name":"CantMatchTwoPostOnlys","msg":"CantMatchTwoPostOnlys"},{"code":6109,"name":"OrderBreachesOraclePriceLimits","msg":"OrderBreachesOraclePriceLimits"},{"code":6110,"name":"OrderMustBeTriggeredFirst","msg":"OrderMustBeTriggeredFirst"},{"code":6111,"name":"OrderNotTriggerable","msg":"OrderNotTriggerable"},{"code":6112,"name":"OrderDidNotSatisfyTriggerCondition","msg":"OrderDidNotSatisfyTriggerCondition"},{"code":6113,"name":"PositionAlreadyBeingLiquidated","msg":"PositionAlreadyBeingLiquidated"},{"code":6114,"name":"PositionDoesntHaveOpenPositionOrOrders","msg":"PositionDoesntHaveOpenPositionOrOrders"},{"code":6115,"name":"AllOrdersAreAlreadyLiquidations","msg":"AllOrdersAreAlreadyLiquidations"},{"code":6116,"name":"CantCancelLiquidationOrder","msg":"CantCancelLiquidationOrder"},{"code":6117,"name":"UserIsBeingLiquidated","msg":"UserIsBeingLiquidated"},{"code":6118,"name":"LiquidationsOngoing","msg":"LiquidationsOngoing"},{"code":6119,"name":"WrongSpotBalanceType","msg":"WrongSpotBalanceType"},{"code":6120,"name":"UserCantLiquidateThemself","msg":"UserCantLiquidateThemself"},{"code":6121,"name":"InvalidPerpPositionToLiquidate","msg":"InvalidPerpPositionToLiquidate"},{"code":6122,"name":"InvalidBaseAssetAmountForLiquidatePerp","msg":"InvalidBaseAssetAmountForLiquidatePerp"},{"code":6123,"name":"InvalidPositionLastFundingRate","msg":"InvalidPositionLastFundingRate"},{"code":6124,"name":"InvalidPositionDelta","msg":"InvalidPositionDelta"},{"code":6125,"name":"UserBankrupt","msg":"UserBankrupt"},{"code":6126,"name":"UserNotBankrupt","msg":"UserNotBankrupt"},{"code":6127,"name":"UserHasInvalidBorrow","msg":"UserHasInvalidBorrow"},{"code":6128,"name":"DailyWithdrawLimit","msg":"DailyWithdrawLimit"},{"code":6129,"name":"DefaultError","msg":"DefaultError"},{"code":6130,"name":"InsufficientLPTokens","msg":"Insufficient LP tokens"},{"code":6131,"name":"CantLPWithPerpPosition","msg":"Cant LP with a market position"},{"code":6132,"name":"UnableToBurnLPTokens","msg":"Unable to burn LP tokens"},{"code":6133,"name":"TryingToRemoveLiquidityTooFast","msg":"Trying to remove liqudity too fast after adding it"},{"code":6134,"name":"InvalidSpotMarketVault","msg":"Invalid Spot Market Vault"},{"code":6135,"name":"InvalidSpotMarketState","msg":"Invalid Spot Market State"},{"code":6136,"name":"InvalidSerumProgram","msg":"InvalidSerumProgram"},{"code":6137,"name":"InvalidSerumMarket","msg":"InvalidSerumMarket"},{"code":6138,"name":"InvalidSerumBids","msg":"InvalidSerumBids"},{"code":6139,"name":"InvalidSerumAsks","msg":"InvalidSerumAsks"},{"code":6140,"name":"InvalidSerumOpenOrders","msg":"InvalidSerumOpenOrders"},{"code":6141,"name":"FailedSerumCPI","msg":"FailedSerumCPI"},{"code":6142,"name":"FailedToFillOnExternalMarket","msg":"FailedToFillOnExternalMarket"},{"code":6143,"name":"InvalidFulfillmentConfig","msg":"InvalidFulfillmentConfig"},{"code":6144,"name":"InvalidFeeStructure","msg":"InvalidFeeStructure"},{"code":6145,"name":"InsufficientIFShares","msg":"Insufficient IF shares"},{"code":6146,"name":"MarketActionPaused","msg":"the Market has paused this action"},{"code":6147,"name":"MarketPlaceOrderPaused","msg":"the Market status doesnt allow placing orders"},{"code":6148,"name":"MarketFillOrderPaused","msg":"the Market status doesnt allow filling orders"},{"code":6149,"name":"MarketWithdrawPaused","msg":"the Market status doesnt allow withdraws"},{"code":6150,"name":"ProtectedAssetTierViolation","msg":"Action violates the Protected Asset Tier rules"},{"code":6151,"name":"IsolatedAssetTierViolation","msg":"Action violates the Isolated Asset Tier rules"},{"code":6152,"name":"UserCantBeDeleted","msg":"User Cant Be Deleted"},{"code":6153,"name":"ReduceOnlyWithdrawIncreasedRisk","msg":"Reduce Only Withdraw Increased Risk"},{"code":6154,"name":"MaxOpenInterest","msg":"Max Open Interest"},{"code":6155,"name":"CantResolvePerpBankruptcy","msg":"Cant Resolve Perp Bankruptcy"},{"code":6156,"name":"LiquidationDoesntSatisfyLimitPrice","msg":"Liquidation Doesnt Satisfy Limit Price"},{"code":6157,"name":"MarginTradingDisabled","msg":"Margin Trading Disabled"},{"code":6158,"name":"InvalidMarketStatusToSettlePnl","msg":"Invalid Market Status to Settle Perp Pnl"},{"code":6159,"name":"PerpMarketNotInSettlement","msg":"PerpMarketNotInSettlement"},{"code":6160,"name":"PerpMarketNotInReduceOnly","msg":"PerpMarketNotInReduceOnly"},{"code":6161,"name":"PerpMarketSettlementBufferNotReached","msg":"PerpMarketSettlementBufferNotReached"},{"code":6162,"name":"PerpMarketSettlementUserHasOpenOrders","msg":"PerpMarketSettlementUserHasOpenOrders"},{"code":6163,"name":"PerpMarketSettlementUserHasActiveLP","msg":"PerpMarketSettlementUserHasActiveLP"},{"code":6164,"name":"UnableToSettleExpiredUserPosition","msg":"UnableToSettleExpiredUserPosition"},{"code":6165,"name":"UnequalMarketIndexForSpotTransfer","msg":"UnequalMarketIndexForSpotTransfer"},{"code":6166,"name":"InvalidPerpPositionDetected","msg":"InvalidPerpPositionDetected"},{"code":6167,"name":"InvalidSpotPositionDetected","msg":"InvalidSpotPositionDetected"},{"code":6168,"name":"InvalidAmmDetected","msg":"InvalidAmmDetected"},{"code":6169,"name":"InvalidAmmForFillDetected","msg":"InvalidAmmForFillDetected"},{"code":6170,"name":"InvalidAmmLimitPriceOverride","msg":"InvalidAmmLimitPriceOverride"},{"code":6171,"name":"InvalidOrderFillPrice","msg":"InvalidOrderFillPrice"},{"code":6172,"name":"SpotMarketBalanceInvariantViolated","msg":"SpotMarketBalanceInvariantViolated"},{"code":6173,"name":"SpotMarketVaultInvariantViolated","msg":"SpotMarketVaultInvariantViolated"},{"code":6174,"name":"InvalidPDA","msg":"InvalidPDA"},{"code":6175,"name":"InvalidPDASigner","msg":"InvalidPDASigner"},{"code":6176,"name":"RevenueSettingsCannotSettleToIF","msg":"RevenueSettingsCannotSettleToIF"},{"code":6177,"name":"NoRevenueToSettleToIF","msg":"NoRevenueToSettleToIF"},{"code":6178,"name":"NoAmmPerpPnlDeficit","msg":"NoAmmPerpPnlDeficit"},{"code":6179,"name":"SufficientPerpPnlPool","msg":"SufficientPerpPnlPool"},{"code":6180,"name":"InsufficientPerpPnlPool","msg":"InsufficientPerpPnlPool"},{"code":6181,"name":"PerpPnlDeficitBelowThreshold","msg":"PerpPnlDeficitBelowThreshold"},{"code":6182,"name":"MaxRevenueWithdrawPerPeriodReached","msg":"MaxRevenueWithdrawPerPeriodReached"},{"code":6183,"name":"MaxIFWithdrawReached","msg":"InvalidSpotPositionDetected"},{"code":6184,"name":"NoIFWithdrawAvailable","msg":"NoIFWithdrawAvailable"},{"code":6185,"name":"InvalidIFUnstake","msg":"InvalidIFUnstake"},{"code":6186,"name":"InvalidIFUnstakeSize","msg":"InvalidIFUnstakeSize"},{"code":6187,"name":"InvalidIFUnstakeCancel","msg":"InvalidIFUnstakeCancel"},{"code":6188,"name":"InvalidIFForNewStakes","msg":"InvalidIFForNewStakes"},{"code":6189,"name":"InvalidIFRebase","msg":"InvalidIFRebase"},{"code":6190,"name":"InvalidInsuranceUnstakeSize","msg":"InvalidInsuranceUnstakeSize"},{"code":6191,"name":"InvalidOrderLimitPrice","msg":"InvalidOrderLimitPrice"},{"code":6192,"name":"InvalidIFDetected","msg":"InvalidIFDetected"},{"code":6193,"name":"InvalidAmmMaxSpreadDetected","msg":"InvalidAmmMaxSpreadDetected"},{"code":6194,"name":"InvalidConcentrationCoef","msg":"InvalidConcentrationCoef"},{"code":6195,"name":"InvalidSrmVault","msg":"InvalidSrmVault"},{"code":6196,"name":"InvalidVaultOwner","msg":"InvalidVaultOwner"},{"code":6197,"name":"InvalidMarketStatusForFills","msg":"InvalidMarketStatusForFills"},{"code":6198,"name":"IFWithdrawRequestInProgress","msg":"IFWithdrawRequestInProgress"},{"code":6199,"name":"NoIFWithdrawRequestInProgress","msg":"NoIFWithdrawRequestInProgress"},{"code":6200,"name":"IFWithdrawRequestTooSmall","msg":"IFWithdrawRequestTooSmall"},{"code":6201,"name":"IncorrectSpotMarketAccountPassed","msg":"IncorrectSpotMarketAccountPassed"},{"code":6202,"name":"BlockchainClockInconsistency","msg":"BlockchainClockInconsistency"},{"code":6203,"name":"InvalidIFSharesDetected","msg":"InvalidIFSharesDetected"},{"code":6204,"name":"NewLPSizeTooSmall","msg":"NewLPSizeTooSmall"},{"code":6205,"name":"MarketStatusInvalidForNewLP","msg":"MarketStatusInvalidForNewLP"},{"code":6206,"name":"InvalidMarkTwapUpdateDetected","msg":"InvalidMarkTwapUpdateDetected"},{"code":6207,"name":"MarketSettlementAttemptOnActiveMarket","msg":"MarketSettlementAttemptOnActiveMarket"},{"code":6208,"name":"MarketSettlementRequiresSettledLP","msg":"MarketSettlementRequiresSettledLP"},{"code":6209,"name":"MarketSettlementAttemptTooEarly","msg":"MarketSettlementAttemptTooEarly"},{"code":6210,"name":"MarketSettlementTargetPriceInvalid","msg":"MarketSettlementTargetPriceInvalid"},{"code":6211,"name":"UnsupportedSpotMarket","msg":"UnsupportedSpotMarket"},{"code":6212,"name":"SpotOrdersDisabled","msg":"SpotOrdersDisabled"},{"code":6213,"name":"MarketBeingInitialized","msg":"Market Being Initialized"},{"code":6214,"name":"InvalidUserSubAccountId","msg":"Invalid Sub Account Id"},{"code":6215,"name":"InvalidTriggerOrderCondition","msg":"Invalid Trigger Order Condition"},{"code":6216,"name":"InvalidSpotPosition","msg":"Invalid Spot Position"},{"code":6217,"name":"CantTransferBetweenSameUserAccount","msg":"Cant transfer between same user account"},{"code":6218,"name":"InvalidPerpPosition","msg":"Invalid Perp Position"},{"code":6219,"name":"UnableToGetLimitPrice","msg":"Unable To Get Limit Price"},{"code":6220,"name":"InvalidLiquidation","msg":"Invalid Liquidation"},{"code":6221,"name":"SpotFulfillmentConfigDisabled","msg":"Spot Fulfillment Config Disabled"},{"code":6222,"name":"InvalidMaker","msg":"Invalid Maker"},{"code":6223,"name":"FailedUnwrap","msg":"Failed Unwrap"},{"code":6224,"name":"MaxNumberOfUsers","msg":"Max Number Of Users"},{"code":6225,"name":"InvalidOracleForSettlePnl","msg":"InvalidOracleForSettlePnl"},{"code":6226,"name":"MarginOrdersOpen","msg":"MarginOrdersOpen"},{"code":6227,"name":"TierViolationLiquidatingPerpPnl","msg":"TierViolationLiquidatingPerpPnl"},{"code":6228,"name":"CouldNotLoadUserData","msg":"CouldNotLoadUserData"},{"code":6229,"name":"UserWrongMutability","msg":"UserWrongMutability"},{"code":6230,"name":"InvalidUserAccount","msg":"InvalidUserAccount"},{"code":6231,"name":"CouldNotLoadUserStatsData","msg":"CouldNotLoadUserData"},{"code":6232,"name":"UserStatsWrongMutability","msg":"UserWrongMutability"},{"code":6233,"name":"InvalidUserStatsAccount","msg":"InvalidUserAccount"},{"code":6234,"name":"UserNotFound","msg":"UserNotFound"},{"code":6235,"name":"UnableToLoadUserAccount","msg":"UnableToLoadUserAccount"},{"code":6236,"name":"UserStatsNotFound","msg":"UserStatsNotFound"},{"code":6237,"name":"UnableToLoadUserStatsAccount","msg":"UnableToLoadUserStatsAccount"},{"code":6238,"name":"UserNotInactive","msg":"User Not Inactive"},{"code":6239,"name":"RevertFill","msg":"RevertFill"},{"code":6240,"name":"InvalidMarketAccountforDeletion","msg":"Invalid MarketAccount for Deletion"},{"code":6241,"name":"InvalidSpotFulfillmentParams","msg":"Invalid Spot Fulfillment Params"},{"code":6242,"name":"FailedToGetMint","msg":"Failed to Get Mint"},{"code":6243,"name":"FailedPhoenixCPI","msg":"FailedPhoenixCPI"},{"code":6244,"name":"FailedToDeserializePhoenixMarket","msg":"FailedToDeserializePhoenixMarket"},{"code":6245,"name":"InvalidPricePrecision","msg":"InvalidPricePrecision"},{"code":6246,"name":"InvalidPhoenixProgram","msg":"InvalidPhoenixProgram"},{"code":6247,"name":"InvalidPhoenixMarket","msg":"InvalidPhoenixMarket"},{"code":6248,"name":"InvalidSwap","msg":"InvalidSwap"},{"code":6249,"name":"SwapLimitPriceBreached","msg":"SwapLimitPriceBreached"},{"code":6250,"name":"SpotMarketReduceOnly","msg":"SpotMarketReduceOnly"},{"code":6251,"name":"FundingWasNotUpdated","msg":"FundingWasNotUpdated"},{"code":6252,"name":"ImpossibleFill","msg":"ImpossibleFill"},{"code":6253,"name":"CantUpdatePerpBidAskTwap","msg":"CantUpdatePerpBidAskTwap"},{"code":6254,"name":"UserReduceOnly","msg":"UserReduceOnly"},{"code":6255,"name":"InvalidMarginCalculation","msg":"InvalidMarginCalculation"},{"code":6256,"name":"CantPayUserInitFee","msg":"CantPayUserInitFee"},{"code":6257,"name":"CantReclaimRent","msg":"CantReclaimRent"},{"code":6258,"name":"InsuranceFundOperationPaused","msg":"InsuranceFundOperationPaused"},{"code":6259,"name":"NoUnsettledPnl","msg":"NoUnsettledPnl"},{"code":6260,"name":"PnlPoolCantSettleUser","msg":"PnlPoolCantSettleUser"},{"code":6261,"name":"OracleNonPositive","msg":"OracleInvalid"},{"code":6262,"name":"OracleTooVolatile","msg":"OracleTooVolatile"},{"code":6263,"name":"OracleTooUncertain","msg":"OracleTooUncertain"},{"code":6264,"name":"OracleStaleForMargin","msg":"OracleStaleForMargin"},{"code":6265,"name":"OracleInsufficientDataPoints","msg":"OracleInsufficientDataPoints"},{"code":6266,"name":"OracleStaleForAMM","msg":"OracleStaleForAMM"},{"code":6267,"name":"UnableToParsePullOracleMessage","msg":"Unable to parse pull oracle message"},{"code":6268,"name":"MaxBorrows","msg":"Can not borow more than max borrows"},{"code":6269,"name":"OracleUpdatesNotMonotonic","msg":"Updates must be monotonically increasing"},{"code":6270,"name":"OraclePriceFeedMessageMismatch","msg":"Trying to update price feed with the wrong feed id"},{"code":6271,"name":"OracleUnsupportedMessageType","msg":"The message in the update must be a PriceFeedMessage"},{"code":6272,"name":"OracleDeserializeMessageFailed","msg":"Could not deserialize the message in the update"},{"code":6273,"name":"OracleWrongGuardianSetOwner","msg":"Wrong guardian set owner in update price atomic"},{"code":6274,"name":"OracleWrongWriteAuthority","msg":"Oracle post update atomic price feed account must be drift program"},{"code":6275,"name":"OracleWrongVaaOwner","msg":"Oracle vaa owner must be wormhole program"},{"code":6276,"name":"OracleTooManyPriceAccountUpdates","msg":"Multi updates must have 2 or fewer accounts passed in remaining accounts"},{"code":6277,"name":"OracleMismatchedVaaAndPriceUpdates","msg":"Don't have the same remaining accounts number and pyth updates left"},{"code":6278,"name":"OracleBadRemainingAccountPublicKey","msg":"Remaining account passed does not match oracle update derived pda"},{"code":6279,"name":"FailedOpenbookV2CPI","msg":"FailedOpenbookV2CPI"},{"code":6280,"name":"InvalidOpenbookV2Program","msg":"InvalidOpenbookV2Program"},{"code":6281,"name":"InvalidOpenbookV2Market","msg":"InvalidOpenbookV2Market"},{"code":6282,"name":"NonZeroTransferFee","msg":"Non zero transfer fee"},{"code":6283,"name":"LiquidationOrderFailedToFill","msg":"Liquidation order failed to fill"},{"code":6284,"name":"InvalidPredictionMarketOrder","msg":"Invalid prediction market order"},{"code":6285,"name":"InvalidVerificationIxIndex","msg":"Ed25519 Ix must be before place and make SignedMsg order ix"},{"code":6286,"name":"SigVerificationFailed","msg":"SignedMsg message verificaiton failed"},{"code":6287,"name":"MismatchedSignedMsgOrderParamsMarketIndex","msg":"Market index mismatched b/w taker and maker SignedMsg order params"},{"code":6288,"name":"InvalidSignedMsgOrderParam","msg":"Invalid SignedMsg order param"},{"code":6289,"name":"PlaceAndTakeOrderSuccessConditionFailed","msg":"Place and take order success condition failed"},{"code":6290,"name":"InvalidHighLeverageModeConfig","msg":"Invalid High Leverage Mode Config"},{"code":6291,"name":"InvalidRFQUserAccount","msg":"Invalid RFQ User Account"},{"code":6292,"name":"RFQUserAccountWrongMutability","msg":"RFQUserAccount should be mutable"},{"code":6293,"name":"RFQUserAccountFull","msg":"RFQUserAccount has too many active RFQs"},{"code":6294,"name":"RFQOrderNotFilled","msg":"RFQ order not filled as expected"},{"code":6295,"name":"InvalidRFQOrder","msg":"RFQ orders must be jit makers"},{"code":6296,"name":"InvalidRFQMatch","msg":"RFQ matches must be valid"},{"code":6297,"name":"InvalidSignedMsgUserAccount","msg":"Invalid SignedMsg user account"},{"code":6298,"name":"SignedMsgUserAccountWrongMutability","msg":"SignedMsg account wrong mutability"},{"code":6299,"name":"SignedMsgUserOrdersAccountFull","msg":"SignedMsgUserAccount has too many active orders"},{"code":6300,"name":"SignedMsgOrderDoesNotExist","msg":"Order with SignedMsg uuid does not exist"},{"code":6301,"name":"InvalidSignedMsgOrderId","msg":"SignedMsg order id cannot be 0s"},{"code":6302,"name":"InvalidPoolId","msg":"Invalid pool id"},{"code":6303,"name":"InvalidProtectedMakerModeConfig","msg":"Invalid Protected Maker Mode Config"},{"code":6304,"name":"InvalidPythLazerStorageOwner","msg":"Invalid pyth lazer storage owner"},{"code":6305,"name":"UnverifiedPythLazerMessage","msg":"Verification of pyth lazer message failed"},{"code":6306,"name":"InvalidPythLazerMessage","msg":"Invalid pyth lazer message"},{"code":6307,"name":"PythLazerMessagePriceFeedMismatch","msg":"Pyth lazer message does not correspond to correct fed id"},{"code":6308,"name":"InvalidLiquidateSpotWithSwap","msg":"InvalidLiquidateSpotWithSwap"},{"code":6309,"name":"SignedMsgUserContextUserMismatch","msg":"User in SignedMsg message does not match user in ix context"},{"code":6310,"name":"UserFuelOverflowThresholdNotMet","msg":"User fuel overflow threshold not met"},{"code":6311,"name":"FuelOverflowAccountNotFound","msg":"FuelOverflow account not found"},{"code":6312,"name":"InvalidTransferPerpPosition","msg":"Invalid Transfer Perp Position"},{"code":6313,"name":"InvalidSignedMsgUserOrdersResize","msg":"Invalid SignedMsgUserOrders resize"},{"code":6314,"name":"CouldNotDeserializeHighLeverageModeConfig","msg":"Could not deserialize high leverage mode config"},{"code":6315,"name":"InvalidIfRebalanceConfig","msg":"Invalid If Rebalance Config"},{"code":6316,"name":"InvalidIfRebalanceSwap","msg":"Invalid If Rebalance Swap"},{"code":6317,"name":"InvalidRevenueShareResize","msg":"Invalid RevenueShare resize"},{"code":6318,"name":"BuilderRevoked","msg":"Builder has been revoked"},{"code":6319,"name":"InvalidBuilderFee","msg":"Builder fee is greater than max fee bps"},{"code":6320,"name":"RevenueShareEscrowAuthorityMismatch","msg":"RevenueShareEscrow authority mismatch"},{"code":6321,"name":"RevenueShareEscrowOrdersAccountFull","msg":"RevenueShareEscrow has too many active orders"},{"code":6322,"name":"InvalidRevenueShareAccount","msg":"Invalid RevenueShareAccount"},{"code":6323,"name":"CannotRevokeBuilderWithOpenOrders","msg":"Cannot revoke builder with open orders"},{"code":6324,"name":"UnableToLoadRevenueShareAccount","msg":"Unable to load builder account"},{"code":6325,"name":"InvalidConstituent","msg":"Invalid Constituent"},{"code":6326,"name":"InvalidAmmConstituentMappingArgument","msg":"Invalid Amm Constituent Mapping argument"},{"code":6327,"name":"ConstituentNotFound","msg":"Constituent not found"},{"code":6328,"name":"ConstituentCouldNotLoad","msg":"Constituent could not load"},{"code":6329,"name":"ConstituentWrongMutability","msg":"Constituent wrong mutability"},{"code":6330,"name":"WrongNumberOfConstituents","msg":"Wrong number of constituents passed to instruction"},{"code":6331,"name":"InsufficientConstituentTokenBalance","msg":"Insufficient constituent token balance"},{"code":6332,"name":"AMMCacheStale","msg":"Amm Cache data too stale"},{"code":6333,"name":"LpPoolAumDelayed","msg":"LP Pool AUM not updated recently"},{"code":6334,"name":"ConstituentOracleStale","msg":"Constituent oracle is stale"},{"code":6335,"name":"LpInvariantFailed","msg":"LP Invariant failed"},{"code":6336,"name":"InvalidConstituentDerivativeWeights","msg":"Invalid constituent derivative weights"},{"code":6337,"name":"MaxDlpAumBreached","msg":"Max DLP AUM Breached"},{"code":6338,"name":"SettleLpPoolDisabled","msg":"Settle Lp Pool Disabled"},{"code":6339,"name":"MintRedeemLpPoolDisabled","msg":"Mint/Redeem Lp Pool Disabled"},{"code":6340,"name":"LpPoolSettleInvariantBreached","msg":"Settlement amount exceeded"},{"code":6341,"name":"InvalidConstituentOperation","msg":"Invalid constituent operation"},{"code":6342,"name":"Unauthorized","msg":"Unauthorized for operation"},{"code":6343,"name":"InvalidLpPoolId","msg":"Invalid Lp Pool Id for Operation"},{"code":6344,"name":"MarketIndexNotFoundAmmCache","msg":"MarketIndexNotFoundAmmCache"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/fusion_amm.fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9.json b/migration/khadhroony-bot2-reference/idls/fusion_amm.fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9.json new file mode 100644 index 0000000..9991644 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/fusion_amm.fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9.json @@ -0,0 +1 @@ +{"address":"fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9","metadata":{"name":"fusionamm","version":"1.1.4","spec":"0.1.0"},"instructions":[{"name":"close_bundled_position","docs":["Close a bundled position in a FusionPool.","","### Authority","- `position_bundle_authority` - authority that owns the token corresponding to this desired position bundle.","","### Parameters","- `bundle_index` - The bundle index that we'd like to close.","","#### Special Errors","- `InvalidBundleIndex` - If the provided bundle index is out of bounds.","- `ClosePositionNotEmpty` - The provided position account is not empty."],"discriminator":[41,36,216,245,27,85,103,67],"accounts":[{"name":"bundled_position","writable":true},{"name":"position_bundle","writable":true},{"name":"position_bundle_token_account"},{"name":"position_bundle_authority","signer":true},{"name":"receiver","writable":true}],"args":[{"name":"bundle_index","type":"u16"}]},{"name":"close_limit_order","docs":["Close a limit order in a FusionPool. Burns the limit order token in the owner's wallet.","Mint and TokenAccount are based on Token-2022. And Mint account will be also closed.","","### Authority","- \"limit_order_authority\" - The authority that owns the limit order token.","","#### Special Errors","- `CloseLimitOrderNotEmpty` - The provided limit order account is not empty."],"discriminator":[76,124,128,15,213,87,37,250],"accounts":[{"name":"limit_order_authority","signer":true},{"name":"receiver","writable":true},{"name":"limit_order","writable":true},{"name":"limit_order_mint","writable":true},{"name":"limit_order_token_account","writable":true},{"name":"token_2022_program"}],"args":[]},{"name":"close_position","docs":["Close a position in a FusionPool. Burns the position token in the owner's wallet.","Mint and TokenAccount are based on Token-2022. And Mint account will be also closed.","","### Authority","- \"position_authority\" - The authority that owns the position token.","","#### Special Errors","- `ClosePositionNotEmpty` - The provided position account is not empty."],"discriminator":[123,134,81,0,49,68,98,98],"accounts":[{"name":"position_authority","signer":true},{"name":"receiver","writable":true},{"name":"position","writable":true},{"name":"position_mint","writable":true},{"name":"position_token_account","writable":true},{"name":"token_2022_program"}],"args":[]},{"name":"collect_fees","docs":["Collect fees accrued for this position.","","### Authority","- `position_authority` - authority that owns the token corresponding to this desired position."],"discriminator":[164,152,207,99,30,186,19,182],"accounts":[{"name":"fusion_pool"},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program"}],"args":[{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"collect_protocol_fees","docs":["Collect the protocol fees accrued in this FusionPool","","### Authority","- `collect_protocol_fees_authority` - assigned authority in the FusionPoolConfig that can collect protocol fees"],"discriminator":[22,67,23,98,150,178,70,220],"accounts":[{"name":"fusion_pools_config"},{"name":"fusion_pool","writable":true},{"name":"collect_protocol_fees_authority","signer":true},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"token_destination_a","writable":true},{"name":"token_destination_b","writable":true},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program"}],"args":[{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"decrease_limit_order","docs":["Remove tokens from a limit order in the FusionPool.","","### Parameters","- `amount` - The total amount of tokens the user is willing to withdraw."],"discriminator":[117,157,60,103,66,49,163,0],"accounts":[{"name":"limit_order_authority","signer":true},{"name":"fusion_pool","writable":true},{"name":"limit_order","writable":true},{"name":"limit_order_token_account"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array","writable":true},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program"}],"args":[{"name":"amount","type":"u64"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"decrease_liquidity","docs":["Withdraw liquidity from a position in the FusionPool. This call also updates the position's accrued fees.","","### Authority","- `position_authority` - authority that owns the token corresponding to this desired position.","","### Parameters","- `liquidity_amount` - The total amount of Liquidity the user desires to withdraw.","- `token_min_a` - The minimum amount of tokenA the user is willing to withdraw.","- `token_min_b` - The minimum amount of tokenB the user is willing to withdraw.","","#### Special Errors","- `LiquidityZero` - Provided liquidity amount is zero.","- `LiquidityTooHigh` - Provided liquidity exceeds u128::max.","- `TokenMinSubceeded` - The required token to perform this operation subceeds the user defined amount."],"discriminator":[160,38,208,111,104,91,44,1],"accounts":[{"name":"fusion_pool","writable":true},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program"},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_lower","writable":true},{"name":"tick_array_upper","writable":true}],"args":[{"name":"liquidity_amount","type":"u128"},{"name":"token_min_a","type":"u64"},{"name":"token_min_b","type":"u64"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"delete_position_bundle","docs":["Delete a PositionBundle account. Burns the position bundle token in the owner's wallet.","","### Authority","- `position_bundle_owner` - The owner that owns the position bundle token.","","### Special Errors","- `PositionBundleNotDeletable` - The provided position bundle has open positions."],"discriminator":[100,25,99,2,217,239,124,173],"accounts":[{"name":"position_bundle","writable":true},{"name":"position_bundle_mint","writable":true},{"name":"position_bundle_token_account","writable":true},{"name":"position_bundle_owner","signer":true},{"name":"receiver","writable":true},{"name":"token_program"}],"args":[]},{"name":"delete_token_badge","discriminator":[53,146,68,8,18,117,17,185],"accounts":[{"name":"fusion_pools_config"},{"name":"token_badge_authority","signer":true},{"name":"token_mint"},{"name":"token_badge","writable":true},{"name":"receiver","writable":true}],"args":[]},{"name":"idl_include","discriminator":[223,253,121,121,60,193,129,31],"accounts":[{"name":"tick_array"},{"name":"system_program"}],"args":[]},{"name":"increase_limit_order","docs":["Add tokens to a limit order in the FusionPool.","","### Parameters","- `amount` - The total amount of tokens the user is willing to deposit."],"discriminator":[177,144,89,236,250,186,125,99],"accounts":[{"name":"limit_order_authority","signer":true},{"name":"fusion_pool","writable":true},{"name":"limit_order","writable":true},{"name":"limit_order_token_account"},{"name":"token_mint"},{"name":"token_owner_account","writable":true},{"name":"token_vault","writable":true},{"name":"tick_array","writable":true},{"name":"token_program"},{"name":"memo_program"}],"args":[{"name":"amount","type":"u64"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"increase_liquidity","docs":["Add liquidity to a position in the FusionPool. This call also updates the position's accrued fees.","","### Authority","- `position_authority` - authority that owns the token corresponding to this desired position.","","### Parameters","- `liquidity_amount` - The total amount of Liquidity the user is willing to deposit.","- `token_max_a` - The maximum amount of tokenA the user is willing to deposit.","- `token_max_b` - The maximum amount of tokenB the user is willing to deposit.","","#### Special Errors","- `LiquidityZero` - Provided liquidity amount is zero.","- `LiquidityTooHigh` - Provided liquidity exceeds u128::max.","- `TokenMaxExceeded` - The required token to perform this operation exceeds the user defined amount."],"discriminator":[46,156,243,118,13,205,251,178],"accounts":[{"name":"fusion_pool","writable":true},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program"},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_lower","writable":true},{"name":"tick_array_upper","writable":true}],"args":[{"name":"liquidity_amount","type":"u128"},{"name":"token_max_a","type":"u64"},{"name":"token_max_b","type":"u64"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"initialize_config","docs":["Initializes a FusionPoolsConfig account that hosts info & authorities","required to govern a set of FusionPools.","","### Parameters","- `fee_authority` - Authority authorized to initialize fee-tiers and set customs fees.","- `collect_protocol_fees_authority` - Authority authorized to collect protocol fees.","- `token_badge_authority`","- `default_protocol_fee_rate`","- `default_order_protocol_fee_rate`"],"discriminator":[208,127,21,1,194,190,196,70],"accounts":[{"name":"fusion_pools_config","writable":true},{"name":"funder","writable":true,"signer":true},{"name":"system_program"}],"args":[{"name":"fee_authority","type":"pubkey"},{"name":"collect_protocol_fees_authority","type":"pubkey"},{"name":"token_badge_authority","type":"pubkey"},{"name":"default_protocol_fee_rate","type":"u16"}]},{"name":"initialize_pool","docs":["Initializes a FusionPool account.","","### Parameters","- `tick_spacing` - The desired tick spacing for this pool.","- `fee_rate` - The desired fee rate for this pool.","- `initial_sqrt_price` - The desired initial sqrt-price for this pool","","#### Special Errors","`InvalidTokenMintOrder` - The order of mints have to be ordered by","`SqrtPriceOutOfBounds` - provided initial_sqrt_price is not between 2^-64 to 2^64",""],"discriminator":[95,180,10,172,84,174,232,40],"accounts":[{"name":"fusion_pools_config"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_badge_a"},{"name":"token_badge_b"},{"name":"funder","writable":true,"signer":true},{"name":"fusion_pool","writable":true},{"name":"token_vault_a","writable":true,"signer":true},{"name":"token_vault_b","writable":true,"signer":true},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"system_program"},{"name":"rent"}],"args":[{"name":"tick_spacing","type":"u16"},{"name":"fee_rate","type":"u16"},{"name":"initial_sqrt_price","type":"u128"}]},{"name":"initialize_position_bundle","docs":["Initializes a PositionBundle account that bundles several positions.","A unique token will be minted to represent the position bundle in the users wallet."],"discriminator":[117,45,241,149,24,18,194,65],"accounts":[{"name":"position_bundle","writable":true},{"name":"position_bundle_mint","writable":true,"signer":true},{"name":"position_bundle_token_account","writable":true},{"name":"position_bundle_owner"},{"name":"funder","writable":true,"signer":true},{"name":"token_program"},{"name":"system_program"},{"name":"rent"},{"name":"associated_token_program"}],"args":[]},{"name":"initialize_position_bundle_with_metadata","docs":["Initializes a PositionBundle account that bundles several positions.","A unique token will be minted to represent the position bundle in the users wallet.","Additional Metaplex metadata is appended to identify the token."],"discriminator":[93,124,16,179,249,131,115,245],"accounts":[{"name":"position_bundle","writable":true},{"name":"position_bundle_mint","writable":true,"signer":true},{"name":"position_bundle_metadata","docs":["https://github.com/metaplex-foundation/metaplex-program-library/blob/773a574c4b34e5b9f248a81306ec24db064e255f/token-metadata/program/src/utils/metadata.rs#L100"],"writable":true},{"name":"position_bundle_token_account","writable":true},{"name":"position_bundle_owner"},{"name":"funder","writable":true,"signer":true},{"name":"metadata_update_auth"},{"name":"token_program"},{"name":"system_program"},{"name":"rent"},{"name":"associated_token_program"},{"name":"metadata_program"}],"args":[]},{"name":"initialize_tick_array","docs":["Initializes a tick_array account to represent a tick-range in a FusionPool.","","### Parameters","- `start_tick_index` - The starting tick index for this tick-array.","Has to be a multiple of TickArray size & the tick spacing of this pool.","","#### Special Errors","- `InvalidStartTick` - if the provided start tick is out of bounds or is not a multiple of","TICK_ARRAY_SIZE * tick spacing."],"discriminator":[11,188,193,214,141,91,149,184],"accounts":[{"name":"fusion_pool"},{"name":"funder","writable":true,"signer":true},{"name":"tick_array","writable":true},{"name":"system_program"}],"args":[{"name":"start_tick_index","type":"i32"}]},{"name":"initialize_token_badge","discriminator":[253,77,205,95,27,224,89,223],"accounts":[{"name":"fusion_pools_config"},{"name":"token_badge_authority","signer":true},{"name":"token_mint"},{"name":"token_badge","writable":true},{"name":"funder","writable":true,"signer":true},{"name":"system_program"}],"args":[]},{"name":"lock_position","docs":["Lock the position to prevent liquidity decrease.","","### Authority","- `position_authority` - The authority that owns the position token.","","#### Special Errors","- `PositionAlreadyLocked` - The provided position is already locked.","- `PositionNotLockable` - The provided position is not lockable (e.g. An empty position)."],"discriminator":[227,62,2,252,247,10,171,185],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"position_authority","signer":true},{"name":"position"},{"name":"position_mint"},{"name":"position_token_account","writable":true},{"name":"position_lock","writable":true},{"name":"fusion_pool"},{"name":"token_2022_program"},{"name":"system_program"}],"args":[{"name":"lock_type","type":{"defined":{"name":"PositionLockType"}}}]},{"name":"open_bundled_position","docs":["Open a bundled position in a FusionPool. No new tokens are issued","because the owner of the position bundle becomes the owner of the position.","The position will start off with 0 liquidity.","","### Authority","- `position_bundle_authority` - authority that owns the token corresponding to this desired position bundle.","","### Parameters","- `bundle_index` - The bundle index that we'd like to open.","- `tick_lower_index` - The tick specifying the lower end of the position range.","- `tick_upper_index` - The tick specifying the upper end of the position range.","","#### Special Errors","- `InvalidBundleIndex` - If the provided bundle index is out of bounds.","- `InvalidTickIndex` - If a provided tick is out of bounds, out of order or not a multiple of","the tick-spacing in this pool."],"discriminator":[169,113,126,171,213,172,212,49],"accounts":[{"name":"bundled_position","writable":true},{"name":"position_bundle","writable":true},{"name":"position_bundle_token_account"},{"name":"position_bundle_authority","signer":true},{"name":"fusion_pool"},{"name":"funder","writable":true,"signer":true},{"name":"system_program"},{"name":"rent"}],"args":[{"name":"bundle_index","type":"u16"},{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"}]},{"name":"open_limit_order","docs":["Open a limit order in a FusionPool. A unique token will be minted to represent the limit order","in the users wallet. The limit order will start off with 0 amount.","","### Parameters","- `tick_index` - The tick specifying the limit order execution price.","- `a_to_b` - The limit order swap direction.","- `with_token_metadata_extension` - If true, the token metadata extension will be initialized."],"discriminator":[157,32,218,183,71,29,18,147],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"owner"},{"name":"limit_order","writable":true},{"name":"limit_order_mint","writable":true,"signer":true},{"name":"limit_order_token_account","writable":true},{"name":"fusion_pool"},{"name":"token_2022_program"},{"name":"system_program"},{"name":"associated_token_program"},{"name":"metadata_update_auth"}],"args":[{"name":"tick_index","type":"i32"},{"name":"a_to_b","type":"bool"},{"name":"with_token_metadata_extension","type":"bool"}]},{"name":"open_position","docs":["Open a position in a FusionPool. A unique token will be minted to represent the position","in the users wallet. Additional TokenMetadata extension is initialized to identify the token.","Mint and TokenAccount are based on Token-2022.","The position will start off with 0 liquidity.","","### Parameters","- `tick_lower_index` - The tick specifying the lower end of the position range.","- `tick_upper_index` - The tick specifying the upper end of the position range.","- `with_token_metadata_extension` - If true, the token metadata extension will be initialized.","","#### Special Errors","- `InvalidTickIndex` - If a provided tick is out of bounds, out of order or not a multiple of","the tick-spacing in this pool."],"discriminator":[135,128,47,77,15,152,240,49],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"owner"},{"name":"position","writable":true},{"name":"position_mint","writable":true,"signer":true},{"name":"position_token_account","writable":true},{"name":"fusion_pool"},{"name":"token_2022_program"},{"name":"system_program"},{"name":"associated_token_program"},{"name":"metadata_update_auth"}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"with_token_metadata_extension","type":"bool"}]},{"name":"reset_pool_price","docs":["Resets the pool price to the provided value if the pool is empty.","","### Authority","- \"fee_authority\" - Set authority that can modify pool fees in the FusionPoolConfig","","### Parameters","- `sqrt_price` - The new sqrt_price of the pool.","","#### Special Errors","- `ResetPriceForNonEmptyPool` - Thrown if the pool is not empty."],"discriminator":[93,158,158,189,131,42,15,22],"accounts":[{"name":"fee_authority","signer":true},{"name":"fusion_pools_config"},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"fusion_pool","writable":true}],"args":[{"name":"sqrt_price","type":"u128"}]},{"name":"set_collect_protocol_fees_authority","docs":["Sets the fee authority to collect protocol fees for a FusionPoolConfig.","Only the current collect protocol fee authority has permission to invoke this instruction.","","### Authority","- \"fee_authority\" - Set authority that can collect protocol fees in the FusionPoolConfig"],"discriminator":[34,150,93,244,139,225,233,67],"accounts":[{"name":"fusion_pools_config","writable":true},{"name":"collect_protocol_fees_authority","signer":true},{"name":"new_collect_protocol_fees_authority"}],"args":[]},{"name":"set_default_protocol_fee_rate","docs":["Sets the default protocol fee rate for a FusionPoolConfig","Protocol fee rate is represented as a basis point.","Only the current fee authority has permission to invoke this instruction.","","### Authority","- \"fee_authority\" - Set authority that can modify pool fees in the FusionPoolConfig","","### Parameters","- `default_protocol_fee_rate` - Rate that is referenced during the initialization of a FusionPool using this config.","","#### Special Errors","- `ProtocolFeeRateMaxExceeded` - If the provided default_protocol_fee_rate exceeds MAX_PROTOCOL_FEE_RATE."],"discriminator":[107,205,249,226,151,35,86,0],"accounts":[{"name":"fusion_pools_config","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"default_protocol_fee_rate","type":"u16"}]},{"name":"set_fee_authority","docs":["Sets the fee authority for a FusionPoolConfig.","The fee authority can set the fee & protocol fee rate for individual pools or","set the default fee rate for newly minted pools.","Only the current fee authority has permission to invoke this instruction.","","### Authority","- \"fee_authority\" - Set authority that can modify pool fees in the FusionPoolConfig"],"discriminator":[31,1,50,87,237,101,97,132],"accounts":[{"name":"fusion_pools_config","writable":true},{"name":"fee_authority","signer":true},{"name":"new_fee_authority"}],"args":[]},{"name":"set_fee_rate","docs":["Sets the fee rate for a FusionPool.","Fee rate is represented as hundredths of a basis point.","Only the current fee authority has permission to invoke this instruction.","","### Authority","- \"fee_authority\" - Set authority that can modify pool fees in the FusionPoolConfig","","### Parameters","- `fee_rate` - The rate that the pool will use to calculate fees going onwards.","","#### Special Errors","- `FeeRateMaxExceeded` - If the provided fee_rate exceeds MAX_FEE_RATE."],"discriminator":[53,243,137,65,8,140,158,6],"accounts":[{"name":"fusion_pools_config"},{"name":"fusion_pool","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"fee_rate","type":"u16"}]},{"name":"set_position_range","docs":["Set the position range to a new one.","","### Authority","- `position_authority` - The authority that owns the position token.","","### Parameters","- `tick_lower_index` - The tick specifying the lower index of the position.","- `tick_upper_index` - The tick specifying the upper index of the position."],"discriminator":[192,22,176,176,155,49,153,96],"accounts":[{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account","writable":true},{"name":"fusion_pool"}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"}]},{"name":"set_protocol_fee_rate","docs":["Sets the protocol fee rate for a FusionPool.","Protocol fee rate is represented as a basis point.","Only the current fee authority has permission to invoke this instruction.","","### Authority","- \"fee_authority\" - Set authority that can modify pool fees in the FusionPoolConfig","","### Parameters","- `protocol_fee_rate` - The rate that the pool will use to calculate protocol fees going onwards.","","#### Special Errors","- `ProtocolFeeRateMaxExceeded` - If the provided default_protocol_fee_rate exceeds MAX_PROTOCOL_FEE_RATE."],"discriminator":[95,7,4,50,154,79,156,131],"accounts":[{"name":"fusion_pools_config"},{"name":"fusion_pool","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"protocol_fee_rate","type":"u16"}]},{"name":"set_token_badge_authority","discriminator":[207,202,4,32,205,79,13,178],"accounts":[{"name":"fusion_pools_config","writable":true},{"name":"fee_authority","signer":true},{"name":"new_token_badge_authority"}],"args":[]},{"name":"swap","docs":["Perform a swap in this FusionPool","","### Authority","- \"token_authority\" - The authority to withdraw tokens from the input token account.","","### Parameters","- `amount` - The amount of input or output token to swap from (depending on amount_specified_is_input).","- `other_amount_threshold` - The maximum/minimum of input/output token to swap into (depending on amount_specified_is_input).","- `sqrt_price_limit` - The maximum/minimum price the swap will swap to.","- `amount_specified_is_input` - Specifies the token the parameter `amount`represents. If true, the amount represents the input token of the swap.","- `a_to_b` - The direction of the swap. True if swapping from A to B. False if swapping from B to A.","","#### Special Errors","- `ZeroTradableAmount` - User provided parameter `amount` is 0.","- `InvalidSqrtPriceLimitDirection` - User provided parameter `sqrt_price_limit` does not match the direction of the trade.","- `SqrtPriceOutOfBounds` - User provided parameter `sqrt_price_limit` is over Whirlppool's max/min bounds for sqrt-price.","- `InvalidTickArraySequence` - User provided tick-arrays are not in sequential order required to proceed in this trade direction.","- `TickArraySequenceInvalidIndex` - The swap loop attempted to access an invalid array index during the query of the next initialized tick.","- `TickArrayIndexOutofBounds` - The swap loop attempted to access an invalid array index during tick crossing.","- `LiquidityOverflow` - Liquidity value overflowed 128bits during tick crossing.","- `InvalidTickSpacing` - The swap pool was initialized with tick-spacing of 0."],"discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program"},{"name":"token_authority","signer":true},{"name":"fusion_pool","writable":true},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_0","writable":true},{"name":"tick_array_1","writable":true},{"name":"tick_array_2","writable":true}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit","type":"u128"},{"name":"amount_specified_is_input","type":"bool"},{"name":"a_to_b","type":"bool"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"two_hop_swap","docs":["Perform a two-hop swap in this FusionPool","","### Authority","- \"token_authority\" - The authority to withdraw tokens from the input token account.","","### Parameters","- `amount` - The amount of input or output token to swap from (depending on amount_specified_is_input).","- `other_amount_threshold` - The maximum/minimum of input/output token to swap into (depending on amount_specified_is_input).","- `amount_specified_is_input` - Specifies the token the parameter `amount`represents. If true, the amount represents the input token of the swap.","- `a_to_b_one` - The direction of the swap of hop one. True if swapping from A to B. False if swapping from B to A.","- `a_to_b_two` - The direction of the swap of hop two. True if swapping from A to B. False if swapping from B to A.","- `sqrt_price_limit_one` - The maximum/minimum price the swap will swap to in the first hop.","- `sqrt_price_limit_two` - The maximum/minimum price the swap will swap to in the second hop.","","#### Special Errors","- `ZeroTradableAmount` - User provided parameter `amount` is 0.","- `InvalidSqrtPriceLimitDirection` - User provided parameter `sqrt_price_limit` does not match the direction of the trade.","- `SqrtPriceOutOfBounds` - User provided parameter `sqrt_price_limit` is over Whirlppool's max/min bounds for sqrt-price.","- `InvalidTickArraySequence` - User provided tick-arrays are not in sequential order required to proceed in this trade direction.","- `TickArraySequenceInvalidIndex` - The swap loop attempted to access an invalid array index during the query of the next initialized tick.","- `TickArrayIndexOutofBounds` - The swap loop attempted to access an invalid array index during tick crossing.","- `LiquidityOverflow` - Liquidity value overflowed 128bits during tick crossing.","- `InvalidTickSpacing` - The swap pool was initialized with tick-spacing of 0.","- `InvalidIntermediaryMint` - Error if the intermediary mint between hop one and two do not equal.","- `DuplicateTwoHopPool` - Error if fusion_pool one & two are the same pool."],"discriminator":[195,96,237,108,68,162,219,230],"accounts":[{"name":"fusion_pool_one","writable":true},{"name":"fusion_pool_two","writable":true},{"name":"token_mint_input"},{"name":"token_mint_intermediate"},{"name":"token_mint_output"},{"name":"token_program_input"},{"name":"token_program_intermediate"},{"name":"token_program_output"},{"name":"token_owner_account_input","writable":true},{"name":"token_vault_one_input","writable":true},{"name":"token_vault_one_intermediate","writable":true},{"name":"token_vault_two_intermediate","writable":true},{"name":"token_vault_two_output","writable":true},{"name":"token_owner_account_output","writable":true},{"name":"token_authority","signer":true},{"name":"tick_array_one_0","writable":true},{"name":"tick_array_one_1","writable":true},{"name":"tick_array_one_2","writable":true},{"name":"tick_array_two_0","writable":true},{"name":"tick_array_two_1","writable":true},{"name":"tick_array_two_2","writable":true},{"name":"memo_program"}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"amount_specified_is_input","type":"bool"},{"name":"a_to_b_one","type":"bool"},{"name":"a_to_b_two","type":"bool"},{"name":"sqrt_price_limit_one","type":"u128"},{"name":"sqrt_price_limit_two","type":"u128"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"update_fees","docs":["Update the accrued fees for a position.","","#### Special Errors","- `TickNotFound` - Provided tick array account does not contain the tick for this position.","- `LiquidityZero` - Position has zero liquidity and therefore already has the most updated fees values."],"discriminator":[225,27,13,6,69,84,172,191],"accounts":[{"name":"fusion_pool","writable":true},{"name":"position","writable":true},{"name":"tick_array_lower"},{"name":"tick_array_upper"}],"args":[]}],"accounts":[{"name":"FusionPool","discriminator":[254,204,207,98,25,181,29,67]},{"name":"FusionPoolsConfig","discriminator":[191,199,19,11,75,86,239,169]},{"name":"LimitOrder","discriminator":[137,183,212,91,115,29,141,227]},{"name":"Position","discriminator":[170,188,143,228,122,64,247,208]},{"name":"PositionBundle","discriminator":[129,169,175,65,185,95,32,100]},{"name":"PositionLock","discriminator":[163,137,166,27,112,14,172,118]},{"name":"TickArray","discriminator":[85,1,199,2,188,97,101,139]},{"name":"TokenBadge","discriminator":[116,219,204,229,249,116,255,150]}],"errors":[{"code":6000,"name":"InvalidEnum","msg":"Enum value could not be converted"},{"code":6001,"name":"InvalidStartTick","msg":"Invalid start tick index provided."},{"code":6002,"name":"TickArrayExistInPool","msg":"Tick-array already exists in this fusion_pool"},{"code":6003,"name":"TickArrayIndexOutofBounds","msg":"Attempt to search for a tick-array failed"},{"code":6004,"name":"InvalidTickSpacing","msg":"Tick-spacing is not supported"},{"code":6005,"name":"ClosePositionNotEmpty","msg":"Position is not empty It cannot be closed"},{"code":6006,"name":"DivideByZero","msg":"Unable to divide by zero"},{"code":6007,"name":"NumberCastError","msg":"Unable to cast number into BigInt"},{"code":6008,"name":"NumberDownCastError","msg":"Unable to down cast number"},{"code":6009,"name":"TickNotFound","msg":"Tick not found within tick array"},{"code":6010,"name":"InvalidTickIndex","msg":"Provided tick index is either out of bounds or uninitializable"},{"code":6011,"name":"SqrtPriceOutOfBounds","msg":"Provided sqrt price out of bounds"},{"code":6012,"name":"LiquidityZero","msg":"Liquidity amount must be greater than zero"},{"code":6013,"name":"LiquidityTooHigh","msg":"Liquidity amount must be less than i64::MAX"},{"code":6014,"name":"LiquidityOverflow","msg":"Liquidity overflow"},{"code":6015,"name":"LiquidityUnderflow","msg":"Liquidity underflow"},{"code":6016,"name":"LiquidityNetError","msg":"Tick liquidity net underflowed or overflowed"},{"code":6017,"name":"TokenMaxExceeded","msg":"Exceeded token max"},{"code":6018,"name":"TokenMinSubceeded","msg":"Did not meet token min"},{"code":6019,"name":"MissingOrInvalidDelegate","msg":"Position token account has a missing or invalid delegate"},{"code":6020,"name":"InvalidPositionTokenAmount","msg":"Position token amount must be 1"},{"code":6021,"name":"InvalidTimestampConversion","msg":"Timestamp should be convertible from i64 to u64"},{"code":6022,"name":"InvalidTimestamp","msg":"Timestamp should be greater than the last updated timestamp"},{"code":6023,"name":"InvalidTickArraySequence","msg":"Invalid tick array sequence provided for instruction."},{"code":6024,"name":"InvalidTokenMintOrder","msg":"Token Mint in wrong order"},{"code":6025,"name":"SetRangeForNonEmptyPosition","msg":"Position is not empty, range can't be set."},{"code":6026,"name":"Unused","msg":"Unused"},{"code":6027,"name":"RewardVaultAmountInsufficient","msg":"Reward vault requires amount to support emissions for at least one day"},{"code":6028,"name":"FeeRateMaxExceeded","msg":"Exceeded max fee rate"},{"code":6029,"name":"ProtocolFeeRateMaxExceeded","msg":"Exceeded max protocol fee rate"},{"code":6030,"name":"MultiplicationShiftRightOverflow","msg":"Multiplication with shift right overflow"},{"code":6031,"name":"MulDivOverflow","msg":"Muldiv overflow"},{"code":6032,"name":"MulDivInvalidInput","msg":"Invalid div_u256 input"},{"code":6033,"name":"MultiplicationOverflow","msg":"Multiplication overflow"},{"code":6034,"name":"InvalidSqrtPriceLimitDirection","msg":"Provided SqrtPriceLimit not in the same direction as the swap."},{"code":6035,"name":"ZeroTradableAmount","msg":"There are no tradable amount to swap."},{"code":6036,"name":"AmountOutBelowMinimum","msg":"Amount out below minimum threshold"},{"code":6037,"name":"AmountInAboveMaximum","msg":"Amount in above maximum threshold"},{"code":6038,"name":"TickArraySequenceInvalidIndex","msg":"Invalid index for tick array sequence"},{"code":6039,"name":"AmountCalcOverflow","msg":"Amount calculated overflows"},{"code":6040,"name":"AmountRemainingOverflow","msg":"Amount remaining overflows"},{"code":6041,"name":"InvalidIntermediaryMint","msg":"Invalid intermediary mint"},{"code":6042,"name":"DuplicateTwoHopPool","msg":"Duplicate two hop pool"},{"code":6043,"name":"InvalidBundleIndex","msg":"Bundle index is out of bounds"},{"code":6044,"name":"BundledPositionAlreadyOpened","msg":"Position has already been opened"},{"code":6045,"name":"BundledPositionAlreadyClosed","msg":"Position has already been closed"},{"code":6046,"name":"PositionBundleNotDeletable","msg":"Unable to delete PositionBundle with open positions"},{"code":6047,"name":"UnsupportedTokenMint","msg":"Token mint has unsupported attributes"},{"code":6048,"name":"RemainingAccountsInvalidSlice","msg":"Invalid remaining accounts"},{"code":6049,"name":"RemainingAccountsInsufficient","msg":"Insufficient remaining accounts"},{"code":6050,"name":"NoExtraAccountsForTransferHook","msg":"Unable to call transfer hook without extra accounts"},{"code":6051,"name":"IntermediateTokenAmountMismatch","msg":"Output and input amount mismatch"},{"code":6052,"name":"TransferFeeCalculationError","msg":"Transfer fee calculation failed"},{"code":6053,"name":"RemainingAccountsDuplicatedAccountsType","msg":"Same accounts type is provided more than once"},{"code":6054,"name":"FullRangeOnlyPool","msg":"This fusion_pool only supports full-range positions"},{"code":6055,"name":"TooManySupplementalTickArrays","msg":"Too many supplemental tick arrays provided"},{"code":6056,"name":"DifferentFusionPoolTickArrayAccount","msg":"TickArray account for different fusion_pool provided"},{"code":6057,"name":"PartialFillError","msg":"Trade resulted in partial fill"},{"code":6058,"name":"TakerOrderNotSupported","msg":"The limit order tick index is incorrect relative to the current market tick (taker order)"},{"code":6059,"name":"LimitOrderAmountExceeded","msg":"Exceeded the limit order deposited input amount"},{"code":6060,"name":"LimitOrderNotEmpty","msg":"Limit order is not empty and cannot be closed"},{"code":6061,"name":"LimitOrderIsFilled","msg":"Unable to increase a limit order because it's partially or fully filled"},{"code":6062,"name":"ZeroAmount","msg":"Zero amount"},{"code":6063,"name":"ResetPriceForNonEmptyPool","msg":"Can't reset the price because the pool is not empty"},{"code":6064,"name":"PositionLocked","msg":"Position locked"},{"code":6065,"name":"PositionNotLockable","msg":"Position is not lockable"},{"code":6066,"name":"RentCalculationError","msg":"Rent calculation error"}],"types":[{"name":"AccountsType","type":{"kind":"enum","variants":[{"name":"TransferHookA"},{"name":"TransferHookB"},{"name":"TransferHookInput"},{"name":"TransferHookIntermediate"},{"name":"TransferHookOutput"},{"name":"SupplementalTickArrays"},{"name":"SupplementalTickArraysOne"},{"name":"SupplementalTickArraysTwo"}]}},{"name":"FusionPool","type":{"kind":"struct","fields":[{"name":"bump","type":{"array":["u8",1]}},{"name":"version","type":"u16"},{"name":"token_mint_a","type":"pubkey"},{"name":"token_mint_b","type":"pubkey"},{"name":"token_vault_a","type":"pubkey"},{"name":"token_vault_b","type":"pubkey"},{"name":"tick_spacing","type":"u16"},{"name":"tick_spacing_seed","type":{"array":["u8",2]}},{"name":"fee_rate","type":"u16"},{"name":"protocol_fee_rate","type":"u16"},{"name":"unused_0","type":"u32"},{"name":"liquidity","type":"u128"},{"name":"sqrt_price","type":"u128"},{"name":"tick_current_index","type":"i32"},{"name":"protocol_fee_owed_a","type":"u64"},{"name":"protocol_fee_owed_b","type":"u64"},{"name":"fee_growth_global_a","type":"u128"},{"name":"fee_growth_global_b","type":"u128"},{"name":"orders_total_amount_a","type":"u64"},{"name":"orders_total_amount_b","type":"u64"},{"name":"orders_filled_amount_a","type":"u64"},{"name":"orders_filled_amount_b","type":"u64"},{"name":"olp_fee_owed_a","type":"u64"},{"name":"olp_fee_owed_b","type":"u64"},{"name":"ma_sqrt_price","type":"u128"},{"name":"last_swap_timestamp","type":"u64"},{"name":"reserved","type":{"array":["u8",116]}}]}},{"name":"FusionPoolsConfig","type":{"kind":"struct","fields":[{"name":"version","type":"u16"},{"name":"fee_authority","type":"pubkey"},{"name":"collect_protocol_fees_authority","type":"pubkey"},{"name":"token_badge_authority","type":"pubkey"},{"name":"default_protocol_fee_rate","type":"u16"},{"name":"unused_0","type":"u16"},{"name":"unused_1","type":"u16"},{"name":"reserved","type":{"array":["u8",170]}}]}},{"name":"LimitOrder","type":{"kind":"struct","fields":[{"name":"version","type":"u16"},{"name":"fusion_pool","type":"pubkey"},{"name":"limit_order_mint","type":"pubkey"},{"name":"tick_index","type":"i32"},{"name":"amount","type":"u64"},{"name":"a_to_b","type":"bool"},{"name":"age","type":"u64"},{"name":"reserved","type":{"array":["u8",128]}}]}},{"name":"MaybeTick","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Initialized","fields":[{"defined":{"name":"TickData"}}]}]}},{"name":"Position","type":{"kind":"struct","fields":[{"name":"version","type":"u16"},{"name":"fusion_pool","type":"pubkey"},{"name":"position_mint","type":"pubkey"},{"name":"liquidity","type":"u128"},{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"fee_growth_checkpoint_a","type":"u128"},{"name":"fee_owed_a","type":"u64"},{"name":"fee_growth_checkpoint_b","type":"u128"},{"name":"fee_owed_b","type":"u64"},{"name":"reserved","type":{"array":["u8",128]}}]}},{"name":"PositionBundle","type":{"kind":"struct","fields":[{"name":"position_bundle_mint","type":"pubkey"},{"name":"position_bitmap","type":{"array":["u8",32]}}]}},{"name":"PositionLock","type":{"kind":"struct","fields":[{"name":"position_mint","type":"pubkey"},{"name":"position_owner","type":"pubkey"},{"name":"fusion_pool","type":"pubkey"},{"name":"locked_timestamp","type":"u64"},{"name":"lock_type","type":{"defined":{"name":"PositionLockType"}}},{"name":"reserved","type":{"array":["u8",128]}}]}},{"name":"PositionLockType","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Permanent"}]}},{"name":"RemainingAccountsInfo","type":{"kind":"struct","fields":[{"name":"slices","type":{"vec":{"defined":{"name":"RemainingAccountsSlice"}}}}]}},{"name":"RemainingAccountsSlice","type":{"kind":"struct","fields":[{"name":"accounts_type","type":{"defined":{"name":"AccountsType"}}},{"name":"length","type":"u8"}]}},{"name":"TickArray","type":{"kind":"struct","fields":[{"name":"start_tick_index","type":"i32"},{"name":"fusion_pool","type":"pubkey"},{"name":"ticks","type":{"array":[{"defined":{"name":"MaybeTick"}},88]}}]}},{"name":"TickData","type":{"kind":"struct","fields":[{"name":"liquidity_net","type":"i128"},{"name":"liquidity_gross","type":"u128"},{"name":"fee_growth_outside_a","type":"u128"},{"name":"fee_growth_outside_b","type":"u128"},{"name":"age","type":"u64"},{"name":"open_orders_input","type":"u64"},{"name":"part_filled_orders_input","type":"u64"},{"name":"part_filled_orders_remaining_input","type":"u64"},{"name":"fulfilled_a_to_b_orders_input","type":"u64"},{"name":"fulfilled_b_to_a_orders_input","type":"u64"}]}},{"name":"TokenBadge","type":{"kind":"struct","fields":[{"name":"token_mint","type":"pubkey"},{"name":"reserved","type":{"array":["u8",128]}}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/goosefx_gamma.GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT.json b/migration/khadhroony-bot2-reference/idls/goosefx_gamma.GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT.json new file mode 100644 index 0000000..6eadf43 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/goosefx_gamma.GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT.json @@ -0,0 +1 @@ +{"address":"GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT","metadata":{"name":"gamma","version":"0.2.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"add_or_update_actor","discriminator":[113,136,228,230,107,185,128,227],"accounts":[{"name":"owner","docs":["Only admin(multisig) can add or update actor"],"writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"actor","writable":true,"pda":{"seeds":[{"kind":"const","value":[97,99,116,111,114]},{"kind":"account","path":"authority"}]}},{"name":"authority","docs":["Account for which the actor is being created.","This is the account that will be able to update the pool."]}],"args":[{"name":"is_admin","type":"bool"}]},{"name":"add_partner","docs":["Admin-permissioned instruction to add a new partner to a pool","","* `ctx` - The context of accounts",""],"discriminator":[180,111,45,157,241,187,234,88],"accounts":[{"name":"authority","signer":true},{"name":"amm_config"},{"name":"pool_state","writable":true,"relations":["partner"]},{"name":"pool_partners","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,97,114,116,110,101,114,95,105,110,102,111,115]},{"kind":"account","path":"pool_state"}]}},{"name":"partner"}],"args":[]},{"name":"calculate_rewards","docs":["Calculate rewards for the user","","* `ctx` - The context of accounts",""],"discriminator":[199,115,201,124,71,81,143,252],"accounts":[{"name":"signer","writable":true,"signer":true},{"name":"user","docs":["User for which we are calculating rewards"]},{"name":"pool_state"},{"name":"reward_info","writable":true,"pda":{"seeds":[{"kind":"const","value":[114,101,119,97,114,100,95,105,110,102,111,95,115,101,101,100]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"reward_info.start_at","account":"RewardInfo"},{"kind":"account","path":"reward_info.mint","account":"RewardInfo"}]}},{"name":"user_reward_info","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,114,101,119,97,114,100,95,105,110,102,111,95,115,101,101,100]},{"kind":"account","path":"reward_info"},{"kind":"account","path":"user"}]}},{"name":"user_pool_liquidity","docs":["User pool liquidity account"],"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,45,112,111,111,108,45,108,105,113,117,105,100,105,116,121]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"user"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"claim_partner_fees","docs":["Claim fees for a particular partner","","* `ctx` - The context of accounts",""],"discriminator":[114,71,103,57,160,205,242,185],"accounts":[{"name":"partner"},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state"},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"pool_partners","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,97,114,116,110,101,114,95,105,110,102,111,115]},{"kind":"account","path":"pool_state"}]}},{"name":"token_0_token_account","writable":true,"relations":["partner"]},{"name":"token_1_token_account","writable":true,"relations":["partner"]},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[]},{"name":"claim_rewards","docs":["Claim rewards for the user","Transfers the amount of tokens calculated for the user to their reward token account","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[4,144,132,71,116,23,151,80],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state"},{"name":"reward_info","pda":{"seeds":[{"kind":"const","value":[114,101,119,97,114,100,95,105,110,102,111,95,115,101,101,100]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"reward_info.start_at","account":"RewardInfo"},{"kind":"account","path":"reward_info.mint","account":"RewardInfo"}]}},{"name":"reward_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[114,101,119,97,114,100,95,118,97,117,108,116,95,115,101,101,100]},{"kind":"account","path":"reward_info"}]}},{"name":"user_token_account","writable":true},{"name":"user_reward_info","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,114,101,119,97,114,100,95,105,110,102,111,95,115,101,101,100]},{"kind":"account","path":"reward_info"},{"kind":"account","path":"user"}]}},{"name":"reward_mint","writable":true},{"name":"token_program","docs":["token Program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"collect_fund_fee","docs":["Collect the fund fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[167,138,78,149,223,194,6,126],"accounts":[{"name":"owner","docs":["Only admin or fund_owner can collect fee now"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores fund_owner"]},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"recipient_token_0_account","docs":["The address that receives the collected token_0 fund fees"],"writable":true},{"name":"recipient_token_1_account","docs":["The address that receives the collected token_1 fund fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_protocol_fee","docs":["Collect the protocol fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[136,136,252,221,194,66,126,89],"accounts":[{"name":"owner","docs":["Only admin or owner can collect fee now"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores owner"]},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"recipient_token_0_account","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_1_account","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"create_amm_config","docs":["The configuation of AMM protocol, include trade fee and protocol fee","# Arguments","","* `ctx`- The accounts needed by instruction.","* `index` - The index of amm config, there may be multiple config.","* `trade_fee_rate` - Trade fee rate, can be changed.","* `protocol_fee_rate` - The rate of protocol fee within tarde fee.","* `fund_fee_rate` - The rate of fund fee within tarde fee.",""],"discriminator":[137,52,237,212,215,117,108,104],"accounts":[{"name":"owner","docs":["Address to be set as protocol owner."],"writable":true,"signer":true,"address":"9QcHinaHcJFdzSHeiF1yGchcuQk3qPFNV13q6dZJbAny"},{"name":"amm_config","docs":["Initialize AmmConfig state account to store protocol owner address and fee rates"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,109,109,95,99,111,110,102,105,103]},{"kind":"arg","path":"index"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"u16"},{"name":"trade_fee_rate","type":"u64"},{"name":"protocol_fee_rate","type":"u64"},{"name":"fund_fee_rate","type":"u64"},{"name":"create_pool_fee","type":"u64"},{"name":"max_open_time","type":"u64"}]},{"name":"create_rewards","docs":["Create rewards for the pool","Initializes a new reward info account and a reward vault account","Transfers the rewards to the reward vault","","# Arguments","","* `ctx` - The context of accounts","* `start_time` - The start time of the reward","* `end_time` - The end time of the reward","* `reward_amount` - The amount of the reward",""],"discriminator":[124,251,145,232,5,173,159,223],"accounts":[{"name":"reward_provider","writable":true,"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state"},{"name":"reward_info","writable":true,"pda":{"seeds":[{"kind":"const","value":[114,101,119,97,114,100,95,105,110,102,111,95,115,101,101,100]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"start_time"},{"kind":"account","path":"reward_mint"}]}},{"name":"reward_providers_token_account","writable":true},{"name":"reward_vault","docs":["For reward to deposit into."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[114,101,119,97,114,100,95,118,97,117,108,116,95,115,101,101,100]},{"kind":"account","path":"reward_info"}]}},{"name":"reward_mint","writable":true},{"name":"token_program","docs":["token Program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"start_time","type":"u64"},{"name":"end_time","type":"u64"},{"name":"reward_amount","type":"u64"}]},{"name":"create_swap_referral","docs":["Initialize swap referrals for an existing AMM config","# Arguments","","* `ctx` - The accounts needed by the instruction, including those used by cpi to the referral program","* `name` - The project name, passed to the referral program. Must be less than 50 chars in length","* `default_share_bps` - Percentage share of fees to referrers. Must be less than 10_000"],"discriminator":[67,131,93,236,56,6,40,77],"accounts":[{"name":"admin","docs":["Admin signer for this operation"],"signer":true},{"name":"owner","address":"9QcHinaHcJFdzSHeiF1yGchcuQk3qPFNV13q6dZJbAny"},{"name":"payer","writable":true,"signer":true},{"name":"amm_config","docs":["The config acts as the base for its referral project"],"writable":true},{"name":"project","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"referral_program","address":"REFER4ZgmyYx9c6He5XfaTMiGfdLwRnkV4RPp9t9iF3"}],"args":[{"name":"name","type":"string"},{"name":"default_share_bps","type":"u16"}]},{"name":"deposit","docs":["Creates a pool for the given token pair and the initial price","","# Arguments","","* `ctx`- The context of accounts","* `lp_token_amount` - Pool token amount to transfer. token_a and token_b amount are set by the current exchange rate and size of the pool","* `maximum_token_0_amount` - Maximum token 0 amount to deposit, prevents excessive slippage","* `maximum_token_1_amount` - Maximum token 1 amount to deposit, prevents excessive slippage",""],"discriminator":[242,35,198,137,82,225,242,182],"accounts":[{"name":"owner","docs":["Owner of the liquidity provided"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state the owner is depositing into"],"writable":true},{"name":"user_pool_liquidity","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,45,112,111,111,108,45,108,105,113,117,105,100,105,116,121]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"owner"}]}},{"name":"token_0_account","docs":["The payer's token account to deposit token_0"],"writable":true},{"name":"token_1_account","docs":["The payer's token account to deposit token_1"],"writable":true},{"name":"token_0_vault","docs":["Pool vault for token_0 to deposit into","The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["Pool vault for token_1 to deposit into","The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["token Program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"pool_partners","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,97,114,116,110,101,114,95,105,110,102,111,115]},{"kind":"account","path":"pool_state"}]}}],"args":[{"name":"lp_token_amount","type":"u64"},{"name":"maximum_token_0_amount","type":"u64"},{"name":"maximum_token_1_amount","type":"u64"}]},{"name":"init_user_pool_liquidity","discriminator":[227,221,200,212,36,107,149,36],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"pool_state"},{"name":"user_pool_liquidity","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,45,112,111,111,108,45,108,105,113,117,105,100,105,116,121]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"user"}]}},{"name":"pool_partners","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,97,114,116,110,101,114,95,105,110,102,111,115]},{"kind":"account","path":"pool_state"}]}},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"}],"args":[{"name":"partner","type":{"option":"pubkey"}}]},{"name":"initialize","docs":["Creates a pool for the given token pair and the initial price","","# Arguments","","* `ctx`- The context of accounts","* `init_amount_0` - the initial amount_0 to deposit","* `init_amount_1` - the initial amount_1 to deposit","* `open_time` - the timestamp allowed for swap","* `max_trade_fee_rate` - The maximum trade fee that can be charged on swaps","* `volatility_factor` - The volatility factor of the pool to determine the trade fee",""],"discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"creator","docs":["Address paying to create the pool. It can be anyone."],"writable":true,"signer":true},{"name":"amm_config","docs":["Which amm config the pool belongs to"]},{"name":"authority","docs":["sign transactions on behalf of the pool","for vault and lp_mint"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Initialize an account to store the pool state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"token_0_mint"},{"kind":"account","path":"token_1_mint"}]}},{"name":"user_pool_liquidity","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,45,112,111,111,108,45,108,105,113,117,105,100,105,116,121]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"creator"}]}},{"name":"token_0_mint","docs":["Token_0 mint, the key must smaller than token_1 mint."]},{"name":"token_1_mint","docs":["Token_1 mint, the key must greater than token_0 mint."]},{"name":"creator_token_0","docs":["creator token 0 account"],"writable":true},{"name":"creator_token_1","docs":["creator token 1 account"],"writable":true},{"name":"token_0_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_0_mint"}]}},{"name":"token_1_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_1_mint"}]}},{"name":"create_pool_fee","docs":["create pool fee account"],"writable":true,"address":"8PhehuioLjhJ35A5eavazJSwoXcA4J7WwzgoWDBDFSuY"},{"name":"observation_state","docs":["an account to store oracle observations"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_0_program","docs":["Spl token program or token program 2022"]},{"name":"token_1_program","docs":["Spl token program or token program 2022"]},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"init_amount_0","type":"u64"},{"name":"init_amount_1","type":"u64"},{"name":"open_time","type":"u64"},{"name":"max_trade_fee_rate","type":"u64"},{"name":"volatility_factor","type":"u64"}]},{"name":"initialize_partner","docs":["Creates a new `Partner` account associated with a certain pool","","* `ctx` - The context of accounts","* `name` - The name associated with the partner","* `token_0_token_account` - Token account for receiving token0 tokens","* `token_1_token_account` - Token account for receiving token1 tokens",""],"discriminator":[165,62,179,112,129,50,173,144],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"authority","signer":true},{"name":"pool_state"},{"name":"pool_partners","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,97,114,116,110,101,114,95,105,110,102,111,115]},{"kind":"account","path":"pool_state"}]}},{"name":"partner","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"name","type":"string"},{"name":"token_0_token_account","type":"pubkey"},{"name":"token_1_token_account","type":"pubkey"}]},{"name":"initialize_pool_partners","docs":["Initializes a new `PoolPartnerInfos` account unique to a particular pool","","* `ctx` - The context of accounts","* `partner_share_rate` - A fraction representing the partner's share of protocol fees",""],"discriminator":[99,11,108,186,1,126,209,251],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"pool_state"},{"name":"pool_partners","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,97,114,116,110,101,114,95,105,110,102,111,115]},{"kind":"account","path":"pool_state"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"oracle_based_swap_base_input","docs":["Swap the tokens in the pool base input amount, using oracle price and Curve calculator combined.","","# Arguments","","* `ctx`- The context of accounts","* `amount_in` - input amount to transfer, output to DESTINATION is based on the exchange rate","* `minimum_amount_out` - Minimum amount of output token, prevents excessive slippage",""],"discriminator":[239,82,192,187,160,26,223,223],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"input_token_program","docs":["SPL program for input token transfers"]},{"name":"output_token_program","docs":["SPL program for output token transfers"]},{"name":"input_token_mint","docs":["The mint of input token"]},{"name":"output_token_mint","docs":["The mint of output token"]},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true}],"args":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"}]},{"name":"pool_config_update","discriminator":[196,212,99,63,59,117,248,223],"accounts":[{"name":"authority","signer":true},{"name":"pool_state","writable":true},{"name":"amm_config"}],"args":[{"name":"h1","type":"u128"}]},{"name":"pool_config_update_v2","discriminator":[228,161,199,222,53,172,121,238],"accounts":[{"name":"authority","signer":true},{"name":"pool_state","writable":true},{"name":"amm_config"}],"args":[{"name":"h1","type":"u128"},{"name":"price_fetched_at","type":"u64"}]},{"name":"pool_config_update_v3","discriminator":[123,135,23,15,174,100,120,152],"accounts":[{"name":"authority","signer":true},{"name":"pool_state","writable":true},{"name":"amm_config"},{"name":"observation_state","writable":true},{"name":"actor","pda":{"seeds":[{"kind":"const","value":[97,99,116,111,114]},{"kind":"account","path":"authority"}]}}],"args":[{"name":"h1","type":"u128"},{"name":"h2","type":"u128"},{"name":"price_fetched_at","type":"u64"},{"name":"min_trade_rate_at_oracle_price","type":{"option":"u32"}},{"name":"oracle_price_delay_fee_rate_per_second","type":{"option":"u32"}},{"name":"max_oracle_price_delay_fee","type":{"option":"u32"}},{"name":"drift_factor","type":{"option":"u32"}},{"name":"max_drift_factor","type":{"option":"u32"}},{"name":"volatility_factor","type":{"option":"u64"}}]},{"name":"rebalance_kamino","docs":["Migrate from Meteora Dlmm to Gamma"],"discriminator":[153,94,34,16,92,181,147,215],"accounts":[{"name":"signer","writable":true,"signer":true},{"name":"gamma_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"token_vault","docs":["The vault token account for token 0"],"writable":true},{"name":"token_mint","writable":true},{"name":"kamino_reserve","writable":true},{"name":"kamino_lending_market","writable":true,"address":"7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF"},{"name":"lending_market_authority"},{"name":"reserve_liquidity_supply","writable":true},{"name":"reserve_collateral_mint","writable":true},{"name":"gamma_pool_destination_collateral","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,107,97,109,105,110,111,95,100,101,112,111,115,105,116,115]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint"}]}},{"name":"instruction_sysvar_account","address":"Sysvar1nstructions1111111111111111111111111"},{"name":"liquidity_token_program"},{"name":"collateral_token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"kamino_program","address":"KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"swap_base_input","docs":["Swap the tokens in the pool base input amount","","# Arguments","","* `ctx`- The context of accounts","* `amount_in` - input amount to transfer, output to DESTINATION is based on the exchange rate","* `minimum_amount_out` - Minimum amount of output token, prevents excessive slippage",""],"discriminator":[143,190,90,218,196,30,51,222],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"input_token_program","docs":["SPL program for input token transfers"]},{"name":"output_token_program","docs":["SPL program for output token transfers"]},{"name":"input_token_mint","docs":["The mint of input token"]},{"name":"output_token_mint","docs":["The mint of output token"]},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true}],"args":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"}]},{"name":"swap_base_output","docs":["Swap the tokens in the pool base output amount","","# Arguments","","* `ctx`- The context of accounts","* `max_amount_in` - input amount prevents excessive slippage","* `amount_out` - amount of output token",""],"discriminator":[55,217,98,86,163,74,180,173],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"input_token_program","docs":["SPL program for input token transfers"]},{"name":"output_token_program","docs":["SPL program for output token transfers"]},{"name":"input_token_mint","docs":["The mint of input token"]},{"name":"output_token_mint","docs":["The mint of output token"]},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true}],"args":[{"name":"_max_amount_in","type":"u64"},{"name":"_amount_out","type":"u64"}]},{"name":"update_amm_config","docs":["Updates the owner of the amm config","Must be called by the current owner or admin","","# Arguments","","* `ctx`- The context of accounts","* `trade_fee_rate`- The new trade fee rate of amm config, be set when `param` is 0","* `protocol_fee_rate`- The new protocol fee rate of amm config, be set when `param` is 1","* `fund_fee_rate`- The new fund fee rate of amm config, be set when `param` is 2","* `new_owner`- The config's new owner, be set when `param` is 3","* `new_fund_owner`- The config's new fund owner, be set when `param` is 4","* `param`- The vaule can be 0 | 1 | 2 | 3 | 4, otherwise will report a error",""],"discriminator":[49,60,174,136,154,28,116,200],"accounts":[{"name":"owner","docs":["The amm config owner or admin"],"signer":true,"address":"9QcHinaHcJFdzSHeiF1yGchcuQk3qPFNV13q6dZJbAny"},{"name":"amm_config","docs":["The amm config account to update"],"writable":true}],"args":[{"name":"param","type":"u16"},{"name":"value","type":"u64"}]},{"name":"update_partner","docs":["Authority-permissioned instruction to update certain fields in a pool, e.g token-accounts","","* `ctx` - The context of accounts","* `token_account_0` - (optional) update for the token_0_token_account field","* `token_account_1` - (optional) update for the token_1_token_account field",""],"discriminator":[19,112,236,81,127,55,21,196],"accounts":[{"name":"authority","signer":true,"relations":["partner"]},{"name":"partner","writable":true}],"args":[{"name":"token_account_0","type":{"option":"pubkey"}},{"name":"token_account_1","type":{"option":"pubkey"}}]},{"name":"update_partner_fees","docs":["Updates claimable fee amounts for all `PartnerInfo`s in a `PoolPartnerInfos` account","","* `ctx` - The context of accounts",""],"discriminator":[154,142,122,49,239,161,232,29],"accounts":[{"name":"pool_state","writable":true},{"name":"pool_partners","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,97,114,116,110,101,114,95,105,110,102,111,115]},{"kind":"account","path":"pool_state"}]}}],"args":[]},{"name":"update_pool","docs":["Update pool status for given vaule","","# Arguments","","* `ctx`- The context of accounts","* `param`- The param of pool status","* `status` - The value",""],"discriminator":[239,214,170,78,36,35,30,34],"accounts":[{"name":"authority","signer":true},{"name":"pool_state","writable":true},{"name":"actor","pda":{"seeds":[{"kind":"const","value":[97,99,116,111,114]},{"kind":"account","path":"authority"}]}}],"args":[{"name":"param","type":"u32"},{"name":"value","type":"u64"}]},{"name":"withdraw","docs":["Withdraw lp for token0 ande token1","","# Arguments","","* `ctx`- The context of accounts","* `lp_token_amount` - Amount of pool tokens to burn. User receives an output of token a and b based on the percentage of the pool tokens that are returned.","* `minimum_token_0_amount` - Minimum amount of token 0 to receive, prevents excessive slippage","* `minimum_token_1_amount` - Minimum amount of token 1 to receive, prevents excessive slippage",""],"discriminator":[183,18,70,156,148,109,161,34],"accounts":[{"name":"owner","docs":["Owner of the liquidity provided"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state account"],"writable":true},{"name":"user_pool_liquidity","docs":["User pool liquidity account"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,45,112,111,111,108,45,108,105,113,117,105,100,105,116,121]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"owner"}]}},{"name":"token_0_account","docs":["The owner's token account for receive token_0"],"writable":true},{"name":"token_1_account","docs":["The owner's token account for receive token_1"],"writable":true},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["token Program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token_0 vault"],"writable":true},{"name":"vault_1_mint","docs":["The mint of token_1 vault"],"writable":true},{"name":"pool_partners","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,97,114,116,110,101,114,95,105,110,102,111,115]},{"kind":"account","path":"pool_state"}]}},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"kamino_program","address":"KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD"},{"name":"instruction_sysvar_account","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[{"name":"lp_token_amount","type":"u64"},{"name":"minimum_token_0_amount","type":"u64"},{"name":"minimum_token_1_amount","type":"u64"}]}],"accounts":[{"name":"Actor","discriminator":[46,77,47,204,204,54,34,88]},{"name":"AmmConfig","discriminator":[218,244,33,104,203,203,43,111]},{"name":"ObservationState","discriminator":[122,174,197,53,129,9,165,132]},{"name":"Partner","discriminator":[122,43,246,239,141,56,243,182]},{"name":"PoolPartnerInfos","discriminator":[130,98,226,231,158,49,44,231]},{"name":"PoolState","discriminator":[247,237,227,245,215,195,222,70]},{"name":"RewardInfo","discriminator":[39,7,129,22,241,96,83,133]},{"name":"UserPoolLiquidity","discriminator":[0,141,89,29,236,6,14,15]},{"name":"UserRewardInfo","discriminator":[110,57,251,139,250,236,213,178]}],"types":[{"name":"Actor","type":{"kind":"struct","fields":[{"name":"authority","type":"pubkey"},{"name":"is_admin","type":"bool"}]}},{"name":"AmmConfig","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"disable_create_pool","type":"bool"},{"name":"index","docs":["Config index"],"type":"u16"},{"name":"trade_fee_rate","docs":["The trade fee, denominated in hundredths of bip (10^-6)"],"type":"u64"},{"name":"protocol_fee_rate","docs":["The protocol fee"],"type":"u64"},{"name":"fund_fee_rate","docs":["The fund fee, denominated in hundredths of bip (10^-6)"],"type":"u64"},{"name":"create_pool_fee","docs":["Fee for creating a new pool"],"type":"u64"},{"name":"protocol_owner","docs":["Address of the protocol fee owner"],"type":"pubkey"},{"name":"fund_owner","docs":["Address of the fund fee owner"],"type":"pubkey"},{"name":"referral_project","docs":["Address of the referral project"],"type":"pubkey"},{"name":"max_open_time","docs":["Max open time for a pool in seconds"],"type":"u64"},{"name":"secondary_admin","type":"pubkey"},{"name":"padding","docs":["padding"],"type":{"array":["u64",7]}}]}},{"name":"LpChangeEvent","docs":["Emitted when deposit or withdraw"],"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"lp_amount_before","type":"u64"},{"name":"token_0_vault_before","type":"u64"},{"name":"token_1_vault_before","type":"u64"},{"name":"token_0_amount","type":"u64"},{"name":"token_1_amount","type":"u64"},{"name":"token_0_transfer_fee","type":"u64"},{"name":"token_1_transfer_fee","type":"u64"},{"name":"change_type","type":"u8"}]}},{"name":"MigrationEvent","docs":["Emitted when migration"],"type":{"kind":"struct","fields":[{"name":"from_pool","type":"pubkey"},{"name":"to_pool","type":"pubkey"},{"name":"token_0_amount_withdrawn","type":"u64"},{"name":"token_1_amount_withdrawn","type":"u64"},{"name":"lp_tokens_migrated","type":"u128"}]}},{"name":"Observation","docs":["The element of observations in ObservationState"],"serialization":"bytemuckunsafe","repr":{"kind":"rust","packed":true},"type":{"kind":"struct","fields":[{"name":"block_timestamp","docs":["The block timestamp of the observation"],"type":"u64"},{"name":"cumulative_token_0_price_x32","docs":["The cumulative of token0 price during the duration time, Q32.32, the remaining 64 bit for overflow"],"type":"u128"},{"name":"cumulative_token_1_price_x32","docs":["The cumulative of token1 price during the duration time, Q32.32, the remaining 64 bit for overflow"],"type":"u128"}]}},{"name":"ObservationState","serialization":"bytemuckunsafe","repr":{"kind":"rust","packed":true},"type":{"kind":"struct","fields":[{"name":"initialized","docs":["Whether the ObservationState is enabled"],"type":"bool"},{"name":"observation_index","docs":["The most recently updated index of the observations array"],"type":"u16"},{"name":"pool_id","type":"pubkey"},{"name":"observations","docs":["observation array"],"type":{"array":[{"defined":{"name":"Observation"}},100]}},{"name":"padding","docs":["padding"],"type":{"array":["u64",4]}}]}},{"name":"Partner","docs":["Account storing information for a protocol participating in the Gamma partner program"],"type":{"kind":"struct","fields":[{"name":"name","docs":["Protocol name"],"type":"string"},{"name":"authority","docs":["The authority for this account"],"type":"pubkey"},{"name":"pool_state","docs":["The pool which this partner account belongs to"],"type":"pubkey"},{"name":"token_0_token_account","docs":["The token-account that receives token0 tokens"],"type":"pubkey"},{"name":"token_1_token_account","docs":["The token-account that receives token1 tokens"],"type":"pubkey"}]}},{"name":"PartnerInfo","serialization":"bytemuckunsafe","repr":{"kind":"rust","packed":true},"type":{"kind":"struct","fields":[{"name":"partner","docs":["The address of the partner account."],"type":"pubkey"},{"name":"lp_token_linked_with_partner","docs":["This stores the LP tokens that are linked with the partner, i.e owned by customers of the partner."],"type":"u64"},{"name":"total_claimed_fee_amount_token_0","docs":["The total fee-amount token0 claimed by the partner"],"type":"u64"},{"name":"total_claimed_fee_amount_token_1","docs":["The total fee-amount token1 claimed by the partner"],"type":"u64"},{"name":"total_earned_fee_amount_token_0","docs":["The total fee-amount token0 calculated for the partner"],"type":"u64"},{"name":"total_earned_fee_amount_token_1","docs":["The total fee-amount token1 calculated for the partner"],"type":"u64"}]}},{"name":"PoolPartnerInfos","docs":["PDA storing all the information for valid pool partners"],"serialization":"bytemuckunsafe","repr":{"kind":"rust","packed":true},"type":{"kind":"struct","fields":[{"name":"last_observed_fee_amount_token_0","docs":["The observed fee-amount token0 as at the last infos update"],"type":"u64"},{"name":"last_observed_fee_amount_token_1","docs":["The observed fee-amount token1 as at the last infos update"],"type":"u64"},{"name":"infos","docs":["Partner infos"],"type":{"array":[{"defined":{"name":"PartnerInfo"}},5]}}]}},{"name":"PoolState","serialization":"bytemuckunsafe","repr":{"kind":"rust","packed":true},"type":{"kind":"struct","fields":[{"name":"amm_config","docs":["To which AmmConfig the pool belongs to"],"type":"pubkey"},{"name":"pool_creator","docs":["Pool Creator"],"type":"pubkey"},{"name":"token_0_vault","docs":["Vault to store Token A of the pool"],"type":"pubkey"},{"name":"token_1_vault","docs":["Vault to store Token B of the pool"],"type":"pubkey"},{"name":"h1","docs":["Scaled to 9 decimal places.","This means `token1/token0`"],"type":"u128"},{"name":"h2","type":"u64"},{"name":"h3","type":"u32"},{"name":"h4","type":"u32"},{"name":"token_0_mint","docs":["Mint info of Token A"],"type":"pubkey"},{"name":"token_1_mint","docs":["Mint info of Token B"],"type":"pubkey"},{"name":"token_0_program","docs":["token_0 program"],"type":"pubkey"},{"name":"token_1_program","docs":["token_1_program"],"type":"pubkey"},{"name":"observation_key","docs":["Observation account to store the oracle data"],"type":"pubkey"},{"name":"auth_bump","type":"u8"},{"name":"status","docs":["Bitwise represenation of the state of the pool","Bit0: 1 - Disable Deposit(value will be 1), 0 - Deposit can be done(normal)","Bit1: 1 - Disable Withdraw(value will be 2), 0 - Withdraw can be done(normal)","Bit2: 1 - Disable Swap(value will be 4), 0 - Swap can be done(normal)"],"type":"u8"},{"name":"_padding2","docs":["lp_mint decimals"],"type":"u8"},{"name":"mint_0_decimals","docs":["mint0 and mint1 decimals"],"type":"u8"},{"name":"mint_1_decimals","type":"u8"},{"name":"lp_supply","docs":["True circulating supply of lp_mint tokens without burns and lock-ups"],"type":"u64"},{"name":"protocol_fees_token_0","docs":["The amount of token_0 and token_1 owed to Liquidity Provider"],"type":"u64"},{"name":"protocol_fees_token_1","type":"u64"},{"name":"fund_fees_token_0","type":"u64"},{"name":"fund_fees_token_1","type":"u64"},{"name":"open_time","docs":["The timestamp allowed for swap in the pool"],"type":"u64"},{"name":"recent_epoch","docs":["recent epoch"],"type":"u64"},{"name":"cumulative_trade_fees_token_0","docs":["Trade fees of token_0 after every swap"],"type":"u128"},{"name":"cumulative_trade_fees_token_1","docs":["Trade fees of token_1 after every swap"],"type":"u128"},{"name":"cumulative_volume_token_0","docs":["Cummulative volume of token_0"],"type":"u128"},{"name":"cumulative_volume_token_1","docs":["Cummulative volume of token_1"],"type":"u128"},{"name":"latest_dynamic_fee_rate","docs":["latest dynamic fee rate"],"type":"u64"},{"name":"max_trade_fee_rate","type":"u64"},{"name":"volatility_factor","type":"u64"},{"name":"token_0_vault_amount","type":"u64"},{"name":"token_1_vault_amount","type":"u64"},{"name":"max_shared_token0","type":"u64"},{"name":"max_shared_token1","type":"u64"},{"name":"h16","type":"u32"},{"name":"h15","type":"u32"},{"name":"h14","type":"u32"},{"name":"h13","type":"u32"},{"name":"h12","type":"u32"},{"name":"h10","type":"u32"},{"name":"_padding3","type":{"array":["u8",8]}},{"name":"token_0_amount_in_kamino","type":"u64"},{"name":"token_1_amount_in_kamino","type":"u64"},{"name":"withdrawn_kamino_profit_token_0","type":"u64"},{"name":"withdrawn_kamino_profit_token_1","type":"u64"},{"name":"partner_share_rate","docs":["Fraction of protocol tokens shared with partners. Denominator is 1_000_000"],"type":"u64"},{"name":"partner_protocol_fees_token_0","docs":["amount of protocol fees owed to referral partners(token0)"],"type":"u64"},{"name":"partner_protocol_fees_token_1","docs":["amount of protocol fees owed to referral partners(token1)"],"type":"u64"},{"name":"min_swap_at_spot_price","docs":["We later in future want to allow any LP to come and create a Pool of sol-usdc, where they can configure all the params.","They also might want to define a max and min price for swaps, until which they want to allow swaps.","This can be thought of similar to the ranges in CLMM.","{token1/token0} with 9 Decimals"],"type":"u64"},{"name":"max_swap_at_spot_price","type":"u64"},{"name":"h20","type":"u128"},{"name":"padding","docs":["padding"],"type":{"array":["u64",1]}}]}},{"name":"RewardInfo","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"start_at","type":"u64"},{"name":"end_rewards_at","type":"u64"},{"name":"mint","type":"pubkey"},{"name":"total_to_disburse","type":"u64"},{"name":"rewarded_by","type":"pubkey"},{"name":"amount_disbursed","type":"u64"}]}},{"name":"SwapEvent","type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"input_vault_before","docs":["pool vault - trade_fees"],"type":"u64"},{"name":"output_vault_before","docs":["pool_vault - trade_fees"],"type":"u64"},{"name":"input_amount","docs":["calculate result without transfer fees"],"type":"u64"},{"name":"output_amount","docs":["calculate result without transfer fees"],"type":"u64"},{"name":"input_mint","docs":["input mint for the swap"],"type":"pubkey"},{"name":"output_mint","docs":["output mint for the swap"],"type":"pubkey"},{"name":"input_transfer_fee","docs":["transfer fees on input token using token extensions"],"type":"u64"},{"name":"output_transfer_fee","docs":["transfer fees on output token using token extensions"],"type":"u64"},{"name":"base_input","type":"bool"},{"name":"dynamic_fee","docs":["dynamic_fees after this swap"],"type":"u128"}]}},{"name":"UserPoolLiquidity","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"pool_state","type":"pubkey"},{"name":"token_0_deposited","type":"u128"},{"name":"token_1_deposited","type":"u128"},{"name":"token_0_withdrawn","type":"u128"},{"name":"token_1_withdrawn","type":"u128"},{"name":"lp_tokens_owned","type":"u128"},{"name":"_p1","type":"u64"},{"name":"_p2","type":"u8"},{"name":"first_investment_at","type":"u64"},{"name":"partner","type":{"option":"pubkey"}},{"name":"padding","type":{"array":["u8",15]}}]}},{"name":"UserRewardInfo","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"reward_info","type":"pubkey"},{"name":"pool_state","type":"pubkey"},{"name":"total_claimed","type":"u64"},{"name":"total_rewards","type":"u64"},{"name":"rewards_last_calculated_at","type":"u64"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/goosefx_v2.GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn.json b/migration/khadhroony-bot2-reference/idls/goosefx_v2.GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn.json new file mode 100644 index 0000000..da5ac1e --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/goosefx_v2.GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn.json @@ -0,0 +1 @@ +{"version":"0.3.0","name":"gfx_ssl_v2","instructions":[{"name":"crankPriceHistories","accounts":[{"name":"poolRegistry","isMut":false,"isSigner":false}],"args":[]},{"name":"internalSwap","accounts":[{"name":"poolRegistry","isMut":false,"isSigner":false},{"name":"pair","isMut":true,"isSigner":false},{"name":"sslAMainToken","isMut":true,"isSigner":false},{"name":"sslBMainToken","isMut":true,"isSigner":false},{"name":"sslASecondaryToken","isMut":true,"isSigner":false},{"name":"sslBSecondaryToken","isMut":true,"isSigner":false},{"name":"tokenAPriceHistory","isMut":true,"isSigner":false},{"name":"tokenAOracle","isMut":false,"isSigner":false},{"name":"tokenBPriceHistory","isMut":true,"isSigner":false},{"name":"tokenBOracle","isMut":false,"isSigner":false},{"name":"sslPoolASigner","isMut":false,"isSigner":false},{"name":"sslPoolBSigner","isMut":false,"isSigner":false},{"name":"eventEmitter","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"claimFees","accounts":[{"name":"poolRegistry","isMut":false,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"sslFeeVault","isMut":true,"isSigner":false},{"name":"ownerAta","isMut":true,"isSigner":false},{"name":"liquidityAccount","isMut":true,"isSigner":false},{"name":"eventEmitter","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"createLiquidityAccount","accounts":[{"name":"poolRegistry","isMut":false,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"liquidityAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"eventEmitter","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"closeLiquidityAccount","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"rentRecipient","isMut":true,"isSigner":false},{"name":"liquidityAccount","isMut":true,"isSigner":false},{"name":"eventEmitter","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"deposit","accounts":[{"name":"liquidityAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"userAta","isMut":true,"isSigner":false},{"name":"sslPoolSigner","isMut":false,"isSigner":false},{"name":"poolVault","isMut":true,"isSigner":false},{"name":"sslFeeVault","isMut":true,"isSigner":false},{"name":"poolRegistry","isMut":true,"isSigner":false},{"name":"eventEmitter","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdraw","accounts":[{"name":"liquidityAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"userAta","isMut":true,"isSigner":false},{"name":"sslPoolSigner","isMut":false,"isSigner":false},{"name":"poolVault","isMut":true,"isSigner":false},{"name":"sslFeeVault","isMut":true,"isSigner":false},{"name":"poolRegistry","isMut":true,"isSigner":false},{"name":"eventEmitter","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"swap","accounts":[{"name":"pair","isMut":true,"isSigner":false},{"name":"poolRegistry","isMut":true,"isSigner":false},{"name":"userWallet","isMut":false,"isSigner":false},{"name":"userWallet","isMut":false,"isSigner":true},{"name":"sslPoolInSigner","isMut":false,"isSigner":false},{"name":"sslPoolOutSigner","isMut":false,"isSigner":false},{"name":"userAtaIn","isMut":true,"isSigner":false},{"name":"userAtaOut","isMut":true,"isSigner":false},{"name":"sslOutMainVault","isMut":true,"isSigner":false},{"name":"sslOutSecondaryVault","isMut":true,"isSigner":false},{"name":"sslInMainVault","isMut":true,"isSigner":false},{"name":"sslInSecondaryVault","isMut":true,"isSigner":false},{"name":"sslOutFeeVault","isMut":true,"isSigner":false},{"name":"sslOutFeeDestination","isMut":true,"isSigner":false},{"name":"outputTokenPriceHistory","isMut":true,"isSigner":false},{"name":"outputTokenOracle","isMut":false,"isSigner":false},{"name":"inputTokenPriceHistory","isMut":true,"isSigner":false},{"name":"inputTokenOracle","isMut":false,"isSigner":false},{"name":"eventEmitter","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amountIn","type":"u64"},{"name":"minOut","type":"u64"}]}],"accounts":[{"name":"EventEmitter","type":{"kind":"struct","fields":[{"name":"eventId","type":"i64"}]}},{"name":"LiquidityAccount","type":{"kind":"struct","fields":[{"name":"poolRegistry","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"amountDeposited","type":"u64"},{"name":"lastObservedTap","type":"u64"},{"name":"lastClaimed","type":"i64"},{"name":"totalEarned","type":"u64"},{"name":"createdAt","type":"i64"},{"name":"lastDepositAt","type":"u64"},{"name":"lastObsRewardsPerShare","type":{"array":["u8",16]}},{"name":"space","type":{"array":["u8",104]}}]}},{"name":"HistoricalDecimal","type":{"kind":"struct","fields":[{"name":"num","type":"i64"},{"name":"scale","type":"u32"},{"name":"pad0","type":{"array":["u8",4]}}]}},{"name":"HistoricalPrice","type":{"kind":"struct","fields":[{"name":"price","type":{"defined":"HistoricalDecimal"}},{"name":"slot","type":"u64"}]}},{"name":"OraclePriceHistory","type":{"kind":"struct","fields":[{"name":"oracleType","type":"u8"},{"name":"minimumElapsedSlots","type":"u8"},{"name":"maxSlotPriceStaleness","type":"u8"},{"name":"backupOracleType","type":"u8"},{"name":"backupOracle2Type","type":"u8"},{"name":"pad0","type":{"array":["u8",3]}},{"name":"poolRegistry","type":"publicKey"},{"name":"oracleAddress","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"numUpdates","type":"u64"},{"name":"backupOracle","type":"publicKey"},{"name":"latestBackupOraclePrice","type":{"defined":"HistoricalPrice"}},{"name":"backupOracle2","type":"publicKey"},{"name":"latestBackupOracle2Price","type":{"defined":"HistoricalPrice"}},{"name":"space","type":{"array":["u8",16]}},{"name":"priceHistory","type":{"array":[{"defined":"HistoricalPrice"},256]}}]}},{"name":"Pair","type":{"kind":"struct","fields":[{"name":"poolRegistry","type":"publicKey"},{"name":"mints","type":{"defined":"(Pubkey,Pubkey)"}},{"name":"feeCollector","type":{"defined":"(Pubkey,Pubkey)"}},{"name":"normalRouteFeeRates","type":{"defined":"(u16,u16)"}},{"name":"totalFeesGeneratedNative","type":{"defined":"([u8;16],[u8;16])"}},{"name":"totalHistoricalVolume","type":{"array":["u8",16]}},{"name":"totalInternallySwapped","type":{"defined":"([u8;16],[u8;16])"}},{"name":"preferredRouteFeeRates","type":{"defined":"(u16,u16)"}},{"name":"space","type":{"array":["u8",124]}}]}},{"name":"SSLPool","type":{"kind":"struct","fields":[{"name":"status","type":"u8"},{"name":"assetType","type":"u8"},{"name":"pad0","type":{"array":["u8",6]}},{"name":"mint","type":"publicKey"},{"name":"mintDecimals","type":"u8"},{"name":"bump","type":"u8"},{"name":"pad1","type":{"array":["u8",6]}},{"name":"totalAccumulatedLpReward","type":"u64"},{"name":"totalLiquidityDeposits","type":"u64"},{"name":"oraclePriceHistories","type":{"array":["publicKey",3]}},{"name":"mathParams","type":{"defined":"SSLMathParams"}},{"name":"rewardsPerShare","type":{"array":["u8",16]}},{"name":"space","type":{"array":["u8",48]}}]}},{"name":"PoolRegistry","type":{"kind":"struct","fields":[{"name":"admin","type":"publicKey"},{"name":"seed","type":"publicKey"},{"name":"suspendAdmin","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"pricingAlgo","type":"u8"},{"name":"pad0","type":{"array":["u8",6]}},{"name":"numEntries","type":"u32"},{"name":"pad1","type":{"array":["u8",4]}},{"name":"categoricalPoolTokenRatios","type":{"array":["u16",16]}},{"name":"space","type":{"array":["u8",96]}},{"name":"entries","type":{"array":[{"defined":"SSLPool"},32]}}]}}],"types":[{"name":"ConfigPairParams","type":{"kind":"struct","fields":[{"name":"mintOneNormalRouteFeeRate","type":{"option":"u16"}},{"name":"mintTwoNormalRouteFeeRate","type":{"option":"u16"}},{"name":"mintOnePreferredRouteFeeRate","type":{"option":"u16"}},{"name":"mintTwoPreferredRouteFeeRate","type":{"option":"u16"}}]}},{"name":"SSLMathConfig","type":{"kind":"struct","fields":[{"name":"meanWindow","type":{"option":"u8"}},{"name":"stdWindow","type":{"option":"u8"}},{"name":"fixedPriceDistance","type":{"option":"u16"}},{"name":"minimumPriceDistance","type":{"option":"u16"}},{"name":"stdWeight","type":{"option":"u32"}},{"name":"latestPriceWeight","type":{"option":"u16"}},{"name":"checkPriceGapCount","type":{"option":"u16"}}]}},{"name":"SSLMathParams","type":{"kind":"struct","fields":[{"name":"meanWindow","type":"u8"},{"name":"stdWindow","type":"u8"},{"name":"fixedPriceDistance","type":"u16"},{"name":"minimumPriceDistance","type":"u16"},{"name":"deprecated0","type":"u16"},{"name":"latestPriceWeight","type":"u16"},{"name":"checkPriceGapCount","type":"u16"},{"name":"pad0","type":{"array":["u8",4]}},{"name":"stdWeight","type":"u32"},{"name":"pad1","type":{"array":["u8",4]}},{"name":"space","type":{"array":["u8",32]}}]}},{"name":"MaxPoolTokenRatio","type":{"kind":"struct","fields":[{"name":"inputToken","type":{"defined":"AssetType"}},{"name":"outputToken","type":{"defined":"AssetType"}},{"name":"poolTokenRatio","type":"u16"}]}},{"name":"PoolRegistryConfig","type":{"kind":"struct","fields":[{"name":"newAdmin","type":{"option":"publicKey"}},{"name":"newSuspendAdmin","type":{"option":"publicKey"}},{"name":"maxPoolTokenRatios","type":{"vec":{"defined":"MaxPoolTokenRatio"}}},{"name":"pricingAlgo","type":{"option":{"defined":"PricingAlgo"}}}]}},{"name":"OracleType","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Pyth"},{"name":"Switchboardv2"},{"name":"Invalid"}]}},{"name":"AssetType","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"BlueChip"},{"name":"Volatile"},{"name":"Stable"},{"name":"Invalid"}]}},{"name":"ConfigSSLPoolStatus","type":{"kind":"enum","variants":[{"name":"Active"},{"name":"Suspended"},{"name":"Locked"}]}},{"name":"PricingAlgo","type":{"kind":"enum","variants":[{"name":"Version1"},{"name":"Version2"},{"name":"Version3"},{"name":"Invalid"}]}},{"name":"SSLPoolStatus","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Active"},{"name":"Suspended"},{"name":"Locked"},{"name":"Invalid"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/guacswap.Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1.json b/migration/khadhroony-bot2-reference/idls/guacswap.Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1.json new file mode 100644 index 0000000..4f0b79b --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/guacswap.Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1.json @@ -0,0 +1 @@ +{"version":"0.1.1","name":"guacswap","instructions":[{"name":"createPool","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":true},{"name":"poolYAccount","isMut":true,"isSigner":true},{"name":"adminXAccount","isMut":true,"isSigner":false},{"name":"adminYAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"projectOwner","isMut":false,"isSigner":false},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"lpFee","type":{"defined":"FixedPoint"}},{"name":"buybackFee","type":{"defined":"FixedPoint"}},{"name":"projectFee","type":{"defined":"FixedPoint"}},{"name":"mercantiFee","type":{"defined":"FixedPoint"}},{"name":"initialTokenX","type":{"defined":"Token"}},{"name":"initialTokenY","type":{"defined":"Token"}},{"name":"bump","type":"u8"}]},{"name":"createProvider","accounts":[{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"ownerXAccount","isMut":true,"isSigner":false},{"name":"ownerYAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"tokenXAmount","type":{"defined":"Token"}},{"name":"tokenYAmount","type":{"defined":"Token"}},{"name":"bump","type":"u8"}]},{"name":"createState","accounts":[{"name":"state","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"addTokens","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"ownerXAccount","isMut":true,"isSigner":false},{"name":"ownerYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"ownerMarcoAccount","isMut":true,"isSigner":false},{"name":"ownerProjectFirstAccount","isMut":true,"isSigner":false},{"name":"ownerProjectSecondAccount","isMut":true,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"deltaX","type":{"defined":"Token"}},{"name":"deltaY","type":{"defined":"Token"}}]},{"name":"withdrawBuyback","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"buybackXAccount","isMut":true,"isSigner":false},{"name":"buybackYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"swap","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"swapperXAccount","isMut":true,"isSigner":false},{"name":"swapperYAccount","isMut":true,"isSigner":false},{"name":"swapper","isMut":true,"isSigner":true},{"name":"referrerXAccount","isMut":true,"isSigner":false},{"name":"referrerYAccount","isMut":true,"isSigner":false},{"name":"referrer","isMut":true,"isSigner":false},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"deltaIn","type":{"defined":"Token"}},{"name":"priceLimit","type":{"defined":"FixedPoint"}},{"name":"xToY","type":"bool"}]},{"name":"withdrawShares","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"ownerXAccount","isMut":true,"isSigner":false},{"name":"ownerYAccount","isMut":true,"isSigner":false},{"name":"ownerMarcoAccount","isMut":true,"isSigner":false},{"name":"ownerProjectFirstAccount","isMut":true,"isSigner":false},{"name":"ownerProjectSecondAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"shares","type":{"defined":"Token"}}]},{"name":"withdrawLpFee","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"ownerXAccount","isMut":true,"isSigner":false},{"name":"ownerYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"withdrawProjectFee","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"projectOwnerXAccount","isMut":true,"isSigner":false},{"name":"projectOwnerYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"projectOwner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"createFarm","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenMarco","isMut":false,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":true},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"supply","type":{"defined":"Token"}},{"name":"duration","type":"u64"},{"name":"bump","type":"u8"}]},{"name":"createDualFarm","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenMarco","isMut":false,"isSigner":false},{"name":"tokenProjectFirst","isMut":false,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":true},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":true},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"adminProjectFirstAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"supplyMarco","type":{"defined":"Token"}},{"name":"supplyProjectFirst","type":{"defined":"Token"}},{"name":"duration","type":"u64"},{"name":"bump","type":"u8"}]},{"name":"createTripleFarm","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_x"},{"kind":"account","type":"publicKey","account":"Mint","path":"token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"tokenMarco","isMut":false,"isSigner":false},{"name":"tokenProjectFirst","isMut":false,"isSigner":false},{"name":"tokenProjectSecond","isMut":false,"isSigner":false},{"name":"tokenMarcoAccount","isMut":true,"isSigner":true},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":true},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":true},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"adminProjectFirstAccount","isMut":true,"isSigner":false},{"name":"adminProjectSecondAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"supplyMarco","type":{"defined":"Token"}},{"name":"supplyProjectFirst","type":{"defined":"Token"}},{"name":"supplyProjectSecond","type":{"defined":"Token"}},{"name":"duration","type":"u64"},{"name":"bump","type":"u8"}]},{"name":"withdrawRewards","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","path":"owner"}]}},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"ownerMarcoAccount","isMut":true,"isSigner":false},{"name":"ownerProjectFirstAccount","isMut":true,"isSigner":false},{"name":"ownerProjectSecondAccount","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"closePool","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"buybackXAccount","isMut":true,"isSigner":false},{"name":"buybackYAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"withdrawMercantiFee","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"mercantiXAccount","isMut":true,"isSigner":false},{"name":"mercantiYAccount","isMut":true,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"addSupply","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"adminProjectFirstAccount","isMut":true,"isSigner":false},{"name":"adminProjectSecondAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"supplyMarco","type":{"defined":"Token"}},{"name":"supplyProjectFirst","type":{"defined":"Token"}},{"name":"supplyProjectSecond","type":{"defined":"Token"}},{"name":"duration","type":"u64"}]},{"name":"updateFees","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false}],"args":[{"name":"newBuybackFee","type":{"defined":"FixedPoint"}},{"name":"newProjectFee","type":{"defined":"FixedPoint"}},{"name":"newProviderFee","type":{"defined":"FixedPoint"}},{"name":"newMercantiFee","type":{"defined":"FixedPoint"}}]},{"name":"resetFarm","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"adminMarcoAccount","isMut":true,"isSigner":false},{"name":"adminProjectFirstAccount","isMut":true,"isSigner":false},{"name":"adminProjectSecondAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateRewardTokens","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"tokenMarco","isMut":false,"isSigner":false},{"name":"newTokenMarcoAccount","isMut":true,"isSigner":true},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"forceWithdrawRewards","accounts":[{"name":"state","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapstatev1"}]}},{"name":"pool","isMut":false,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswappoolv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_x"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool.token_y"}]}},{"name":"farm","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapfarmv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"}]}},{"name":"provider","isMut":true,"isSigner":false,"pda":{"seeds":[{"kind":"const","type":"string","value":"guacswapproviderv1"},{"kind":"account","type":"publicKey","account":"Pool","path":"pool"},{"kind":"account","type":"publicKey","account":"Provider","path":"provider.owner"}]}},{"name":"tokenMarcoAccount","isMut":true,"isSigner":false},{"name":"tokenProjectFirstAccount","isMut":true,"isSigner":false},{"name":"tokenProjectSecondAccount","isMut":true,"isSigner":false},{"name":"ownerMarcoAccount","isMut":true,"isSigner":false},{"name":"ownerProjectFirstAccount","isMut":true,"isSigner":false},{"name":"ownerProjectSecondAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"Farm","type":{"kind":"struct","fields":[{"name":"pool","type":"publicKey"},{"name":"tokens","type":{"array":["publicKey",3]}},{"name":"tokenAccounts","type":{"array":["publicKey",3]}},{"name":"supply","type":{"array":[{"defined":"Token"},3]}},{"name":"supplyLeft","type":{"array":[{"defined":"Token"},3]}},{"name":"accumulatedSecondsPerShare","type":{"defined":"FixedPoint"}},{"name":"offsetSecondsPerShare","type":{"defined":"FixedPoint"}},{"name":"startTime","type":"u64"},{"name":"endTime","type":"u64"},{"name":"lastUpdate","type":"u64"},{"name":"bump","type":"u8"},{"name":"farmType","type":{"defined":"FarmType"}}]}},{"name":"Pool","type":{"kind":"struct","fields":[{"name":"tokenX","type":"publicKey"},{"name":"tokenY","type":"publicKey"},{"name":"poolXAccount","type":"publicKey"},{"name":"poolYAccount","type":"publicKey"},{"name":"admin","type":"publicKey"},{"name":"projectOwner","type":"publicKey"},{"name":"tokenXReserve","type":{"defined":"Token"}},{"name":"tokenYReserve","type":{"defined":"Token"}},{"name":"selfShares","type":{"defined":"Token"}},{"name":"allShares","type":{"defined":"Token"}},{"name":"buybackAmountX","type":{"defined":"Token"}},{"name":"buybackAmountY","type":{"defined":"Token"}},{"name":"projectAmountX","type":{"defined":"Token"}},{"name":"projectAmountY","type":{"defined":"Token"}},{"name":"mercantiAmountX","type":{"defined":"Token"}},{"name":"mercantiAmountY","type":{"defined":"Token"}},{"name":"lpAccumulatorX","type":{"defined":"FixedPoint"}},{"name":"lpAccumulatorY","type":{"defined":"FixedPoint"}},{"name":"constK","type":{"defined":"Product"}},{"name":"price","type":{"defined":"FixedPoint"}},{"name":"lpFee","type":{"defined":"FixedPoint"}},{"name":"buybackFee","type":{"defined":"FixedPoint"}},{"name":"projectFee","type":{"defined":"FixedPoint"}},{"name":"mercantiFee","type":{"defined":"FixedPoint"}},{"name":"farmCount","type":"u64"},{"name":"bump","type":"u8"}]}},{"name":"Provider","type":{"kind":"struct","fields":[{"name":"tokenX","type":"publicKey"},{"name":"tokenY","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"shares","type":{"defined":"Token"}},{"name":"lastFeeAccumulatorX","type":{"defined":"FixedPoint"}},{"name":"lastFeeAccumulatorY","type":{"defined":"FixedPoint"}},{"name":"lastSecondsPerShare","type":{"defined":"FixedPoint"}},{"name":"lastWithdrawTime","type":"u64"},{"name":"tokensOwedX","type":{"defined":"Token"}},{"name":"tokensOwedY","type":{"defined":"Token"}},{"name":"currentFarmCount","type":"u64"},{"name":"bump","type":"u8"}]}},{"name":"State","type":{"kind":"struct","fields":[{"name":"admin","type":"publicKey"},{"name":"programAuthority","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"nonce","type":"u8"}]}}],"types":[{"name":"FixedPoint","type":{"kind":"struct","fields":[{"name":"v","type":"u128"}]}},{"name":"Token","type":{"kind":"struct","fields":[{"name":"v","type":"u64"}]}},{"name":"Product","type":{"kind":"struct","fields":[{"name":"v","type":"u128"}]}},{"name":"FarmType","type":{"kind":"enum","variants":[{"name":"Single"},{"name":"Dual"},{"name":"Triple"}]}}],"errors":[{"code":6000,"name":"DeltaTooBig","msg":"Delta greater than provider's tokens"},{"code":6001,"name":"TokenUnderflow","msg":"Token amount underflow"},{"code":6002,"name":"WrongRatio","msg":"Wrong tokens ratio"},{"code":6003,"name":"TooMuchShares","msg":"Too much shares provided"},{"code":6004,"name":"SwapToBig","msg":"Swap too big"},{"code":6005,"name":"FeeExceeded","msg":"Fee exceeded 100%"},{"code":6007,"name":"ScalesNotEqual","msg":"Scales have to be equal"},{"code":6008,"name":"FeeExceededDeltaOut","msg":"Fees exceeded delta_out"},{"code":6009,"name":"PriceLimitExceeded","msg":"Price limit exceeded"},{"code":6010,"name":"MintMismatch","msg":"Mint mismatch"},{"code":6011,"name":"TokensAreTheSame","msg":"Tokens are the same"},{"code":6012,"name":"WrongFarm","msg":"Cannot add supply to wrong farm"},{"code":6013,"name":"RewardsExceedingSupply","msg":"Cannot withdraw rewards exceeding supply left"},{"code":6014,"name":"FarmNotEnded","msg":"Farm has not ended, cannot add additional rewards"},{"code":6015,"name":"ZeroAmount","msg":"Must provide a nonzero amount"},{"code":6016,"name":"InvariantDecreased","msg":"Invariant has changed"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/helium_treasury_management.treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5.json b/migration/khadhroony-bot2-reference/idls/helium_treasury_management.treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5.json new file mode 100644 index 0000000..ce12606 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/helium_treasury_management.treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5.json @@ -0,0 +1 @@ +{"address":"treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5","metadata":{"name":"treasury_management","version":"0.2.3","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"initialize_treasury_management_v0","discriminator":[149,3,201,108,130,56,56,210],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"treasury_management","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,114,101,97,115,117,114,121,95,109,97,110,97,103,101,109,101,110,116]},{"kind":"account","path":"supply_mint"}]}},{"name":"treasury_mint"},{"name":"supply_mint"},{"name":"mint_authority","signer":true},{"name":"circuit_breaker","writable":true,"pda":{"seeds":[{"kind":"const","value":[97,99,99,111,117,110,116,95,119,105,110,100,111,119,101,100,95,98,114,101,97,107,101,114]},{"kind":"account","path":"treasury"}],"program":{"kind":"account","path":"circuit_breaker_program"}}},{"name":"treasury","writable":true,"pda":{"seeds":[{"kind":"account","path":"treasury_management"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"treasury_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"circuit_breaker_program","address":"circAbx64bbsscPbQzZAUvuXpHqrCe6fLMzc2uKXz9g"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"args","type":{"defined":{"name":"InitializeTreasuryManagementArgsV0"}}}]},{"name":"redeem_v0","discriminator":[235,127,171,139,119,77,235,118],"accounts":[{"name":"treasury_management","pda":{"seeds":[{"kind":"const","value":[116,114,101,97,115,117,114,121,95,109,97,110,97,103,101,109,101,110,116]},{"kind":"account","path":"supply_mint"}]}},{"name":"treasury_mint","relations":["treasury_management"]},{"name":"supply_mint","writable":true,"relations":["treasury_management"]},{"name":"treasury","writable":true,"relations":["treasury_management"]},{"name":"circuit_breaker","writable":true,"pda":{"seeds":[{"kind":"const","value":[97,99,99,111,117,110,116,95,119,105,110,100,111,119,101,100,95,98,114,101,97,107,101,114]},{"kind":"account","path":"treasury"}],"program":{"kind":"account","path":"circuit_breaker_program"}}},{"name":"from","writable":true},{"name":"to","writable":true},{"name":"owner","signer":true,"relations":["from"]},{"name":"circuit_breaker_program","address":"circAbx64bbsscPbQzZAUvuXpHqrCe6fLMzc2uKXz9g"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"args","type":{"defined":{"name":"RedeemArgsV0"}}}]},{"name":"update_treasury_management_v0","discriminator":[209,139,90,226,249,149,89,217],"accounts":[{"name":"treasury_management","writable":true},{"name":"authority","signer":true,"relations":["treasury_management"]}],"args":[{"name":"args","type":{"defined":{"name":"UpdateTreasuryManagementArgsV0"}}}]}],"accounts":[{"name":"AccountWindowedCircuitBreakerV0","discriminator":[134,11,69,100,90,132,174,187]},{"name":"TreasuryManagementV0","discriminator":[68,111,120,209,16,110,215,59]}],"errors":[{"code":6000,"name":"Frozen","msg":"Treasury management is currently frozen"},{"code":6001,"name":"ArithmeticError","msg":"Error in arithmetic"}],"types":[{"name":"AccountWindowedCircuitBreakerV0","type":{"kind":"struct","fields":[{"name":"token_account","type":"pubkey"},{"name":"authority","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"config","type":{"defined":{"name":"WindowedCircuitBreakerConfigV0"}}},{"name":"last_window","type":{"defined":{"name":"WindowV0"}}},{"name":"bump_seed","type":"u8"}]}},{"name":"Curve","type":{"kind":"enum","variants":[{"name":"ExponentialCurveV0","fields":[{"name":"k","type":"u128"}]}]}},{"name":"InitializeTreasuryManagementArgsV0","type":{"kind":"struct","fields":[{"name":"authority","type":"pubkey"},{"name":"curve","type":{"defined":{"name":"Curve"}}},{"name":"freeze_unix_time","type":"i64"},{"name":"window_config","type":{"defined":{"name":"WindowedCircuitBreakerConfigV0"}}}]}},{"name":"RedeemArgsV0","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"expected_output_amount","type":"u64"}]}},{"name":"ThresholdType","type":{"kind":"enum","variants":[{"name":"Percent"},{"name":"Absolute"}]}},{"name":"TreasuryManagementV0","type":{"kind":"struct","fields":[{"name":"treasury_mint","type":"pubkey"},{"name":"supply_mint","type":"pubkey"},{"name":"authority","type":"pubkey"},{"name":"treasury","type":"pubkey"},{"name":"curve","docs":["The bonding curve to use"],"type":{"defined":{"name":"Curve"}}},{"name":"freeze_unix_time","docs":["Freeze this curve at this time."],"type":"i64"},{"name":"bump_seed","type":"u8"}]}},{"name":"UpdateTreasuryManagementArgsV0","type":{"kind":"struct","fields":[{"name":"authority","type":"pubkey"},{"name":"curve","type":{"defined":{"name":"Curve"}}},{"name":"freeze_unix_time","type":"i64"}]}},{"name":"WindowV0","type":{"kind":"struct","fields":[{"name":"last_aggregated_value","type":"u64"},{"name":"last_unix_timestamp","type":"i64"}]}},{"name":"WindowedCircuitBreakerConfigV0","type":{"kind":"struct","fields":[{"name":"window_size_seconds","type":"u64"},{"name":"threshold_type","type":{"defined":{"name":"ThresholdType"}}},{"name":"threshold","type":"u64"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/hylo_exchange.HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn.json b/migration/khadhroony-bot2-reference/idls/hylo_exchange.HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn.json new file mode 100644 index 0000000..87e57a3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/hylo_exchange.HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn.json @@ -0,0 +1 @@ +{"address":"HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn","metadata":{"name":"hylo_exchange","version":"0.1.4","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"get_stats","discriminator":[241,65,112,185,230,140,139,177],"accounts":[{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"stablecoin_mint","pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}]},"relations":["hylo"]},{"name":"levercoin_mint","pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}]},"relations":["hylo"]},{"name":"sol_usd_pyth_feed"}],"args":[],"returns":{"defined":{"name":"ExchangeStats"}}},{"name":"harvest_yield","discriminator":[28,200,150,200,69,56,38,133],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"stablecoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}]},"relations":["hylo"]},{"name":"stablecoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}]}},{"name":"levercoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}]},"relations":["hylo"]},{"name":"levercoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"levercoin_mint"}]}},{"name":"stablecoin_fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}]}},{"name":"stablecoin_fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"stablecoin_fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"levercoin_fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"levercoin_mint"}]}},{"name":"levercoin_fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"levercoin_fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"stablecoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"levercoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool_auth","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,97,117,116,104]}],"program":{"kind":"account","path":"hylo_stability_pool"}}},{"name":"sol_usd_pyth_feed"},{"name":"hylo_stability_pool","address":"HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ"},{"name":"lst_registry","writable":true,"relations":["hylo"]},{"name":"lut_program","address":"AddressLookupTab1e1111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"initialize_lst_registry","discriminator":[148,180,49,29,133,6,9,59],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"registry_auth","pda":{"seeds":[{"kind":"const","value":[108,115,116,95,114,101,103,105,115,116,114,121,95,97,117,116,104]}]}},{"name":"lst_registry","writable":true},{"name":"lut_program","address":"AddressLookupTab1e1111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"slot","type":"u64"}]},{"name":"initialize_lst_registry_calculators","discriminator":[109,166,101,171,217,202,83,166],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"lst_registry_auth","pda":{"seeds":[{"kind":"const","value":[108,115,116,95,114,101,103,105,115,116,114,121,95,97,117,116,104]}]}},{"name":"lst_registry","writable":true,"relations":["hylo"]},{"name":"lut_program","address":"AddressLookupTab1e1111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"initialize_mints","discriminator":[189,84,85,142,177,200,57,22],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"stablecoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}]}},{"name":"levercoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"levercoin_mint"}]}},{"name":"stablecoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}]}},{"name":"levercoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}]}},{"name":"stablecoin_metadata","writable":true},{"name":"levercoin_metadata","writable":true},{"name":"metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"initialize_protocol","discriminator":[188,233,252,106,134,146,202,91],"accounts":[{"name":"admin","writable":true,"signer":true},{"name":"upgrade_authority","signer":true},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"treasury"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"program_data"},{"name":"hylo_exchange","address":"HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn"}],"args":[{"name":"oracle_interval_secs","type":"u64"},{"name":"stability_threshold_1","type":{"defined":{"name":"UFixValue64"}}},{"name":"stability_threshold_2","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_fees","type":{"defined":{"name":"StablecoinFees"}}},{"name":"levercoin_fees","type":{"defined":{"name":"LevercoinFees"}}},{"name":"yield_harvest_config","type":{"defined":{"name":"YieldHarvestConfig"}}}]},{"name":"mint_levercoin","discriminator":[91,156,221,157,151,186,223,231],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"vault_auth","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"levercoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"levercoin_mint"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"vault_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_header","pda":{"seeds":[{"kind":"const","value":[108,115,116,95,104,101,97,100,101,114]},{"kind":"account","path":"lst_mint"}]}},{"name":"user_lst_ta","writable":true},{"name":"user_levercoin_ta","writable":true},{"name":"lst_mint"},{"name":"levercoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}]},"relations":["hylo"]},{"name":"stablecoin_mint","pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}]}},{"name":"sol_usd_pyth_feed"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_lst_to_deposit","type":"u64"},{"name":"slippage_config","type":{"option":{"defined":{"name":"SlippageConfig"}}}}]},{"name":"mint_stablecoin","discriminator":[196,235,215,70,211,5,214,238],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"vault_auth","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"stablecoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"vault_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_header","pda":{"seeds":[{"kind":"const","value":[108,115,116,95,104,101,97,100,101,114]},{"kind":"account","path":"lst_mint"}]}},{"name":"user_lst_ta","writable":true},{"name":"user_stablecoin_ta","writable":true},{"name":"lst_mint"},{"name":"stablecoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}]},"relations":["hylo"]},{"name":"sol_usd_pyth_feed"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_lst_to_deposit","type":"u64"},{"name":"slippage_config","type":{"option":{"defined":{"name":"SlippageConfig"}}}}]},{"name":"redeem_levercoin","discriminator":[132,166,215,32,46,131,174,44],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"vault_auth","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"vault_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_header","pda":{"seeds":[{"kind":"const","value":[108,115,116,95,104,101,97,100,101,114]},{"kind":"account","path":"lst_mint"}]}},{"name":"user_levercoin_ta","writable":true},{"name":"user_lst_ta","writable":true},{"name":"levercoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}]},"relations":["hylo"]},{"name":"stablecoin_mint","pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}]},"relations":["hylo"]},{"name":"lst_mint"},{"name":"sol_usd_pyth_feed"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_to_redeem","type":"u64"},{"name":"slippage_config","type":{"option":{"defined":{"name":"SlippageConfig"}}}}]},{"name":"redeem_stablecoin","discriminator":[69,46,6,97,170,130,160,237],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"vault_auth","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"vault_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_header","pda":{"seeds":[{"kind":"const","value":[108,115,116,95,104,101,97,100,101,114]},{"kind":"account","path":"lst_mint"}]}},{"name":"user_stablecoin_ta","writable":true},{"name":"user_lst_ta","writable":true},{"name":"stablecoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}]},"relations":["hylo"]},{"name":"lst_mint"},{"name":"sol_usd_pyth_feed"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_to_redeem","type":"u64"},{"name":"slippage_config","type":{"option":{"defined":{"name":"SlippageConfig"}}}}]},{"name":"register_lst","discriminator":[167,114,254,24,190,221,82,107],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"lst_header","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,115,116,95,104,101,97,100,101,114]},{"kind":"account","path":"lst_mint"}]}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"vault_auth","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"lst_mint"}]}},{"name":"registry_auth","pda":{"seeds":[{"kind":"const","value":[108,115,116,95,114,101,103,105,115,116,114,121,95,97,117,116,104]}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"vault_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_mint"},{"name":"lst_registry","writable":true,"relations":["hylo"]},{"name":"lst_stake_pool_state"},{"name":"sanctum_calculator_program"},{"name":"sanctum_calculator_state"},{"name":"stake_pool_program_data"},{"name":"stake_pool_program"},{"name":"lut_program","address":"AddressLookupTab1e1111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"swap_lever_to_stable","discriminator":[167,111,84,179,69,7,135,48],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"sol_usd_pyth_feed"},{"name":"stablecoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}]},"relations":["hylo"]},{"name":"stablecoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}]}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user_stablecoin_ta","writable":true},{"name":"levercoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}]},"relations":["hylo"]},{"name":"levercoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"levercoin_mint"}]}},{"name":"user_levercoin_ta","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_levercoin","type":"u64"},{"name":"slippage_config","type":{"option":{"defined":{"name":"SlippageConfig"}}}}],"returns":{"defined":{"name":"SwapLeverToStableEventV1"}}},{"name":"swap_lst","discriminator":[37,144,201,101,137,182,241,42],"accounts":[{"name":"user","signer":true},{"name":"hylo"},{"name":"lst_a_mint"},{"name":"lst_a_user_ta","writable":true},{"name":"lst_a_vault_auth","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"lst_a_mint"}]}},{"name":"lst_a_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"lst_a_vault_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_a_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_a_header","pda":{"seeds":[{"kind":"const","value":[108,115,116,95,104,101,97,100,101,114]},{"kind":"account","path":"lst_a_mint"}]}},{"name":"lst_b_mint"},{"name":"lst_b_user_ta","writable":true},{"name":"lst_b_vault_auth","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"lst_b_mint"}]}},{"name":"lst_b_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"lst_b_vault_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_b_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lst_b_header","pda":{"seeds":[{"kind":"const","value":[108,115,116,95,104,101,97,100,101,114]},{"kind":"account","path":"lst_b_mint"}]}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"lst_a_mint"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lst_a_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_lst_a","type":"u64"},{"name":"slippage_config","type":{"option":{"defined":{"name":"SlippageConfig"}}}}]},{"name":"swap_stable_to_lever","discriminator":[123,194,84,140,192,193,193,161],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"sol_usd_pyth_feed"},{"name":"stablecoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}]},"relations":["hylo"]},{"name":"stablecoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}]}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user_stablecoin_ta","writable":true},{"name":"levercoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}]},"relations":["hylo"]},{"name":"levercoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"levercoin_mint"}]}},{"name":"user_levercoin_ta","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_stablecoin","type":"u64"},{"name":"slippage_config","type":{"option":{"defined":{"name":"SlippageConfig"}}}}],"returns":{"defined":{"name":"SwapStableToLeverEventV1"}}},{"name":"update_admin","discriminator":[161,176,40,213,60,184,179,228],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"upgrade_authority","signer":true},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"program_data"},{"name":"hylo_exchange","address":"HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_admin","type":"pubkey"}]},{"name":"update_levercoin_fees","discriminator":[183,123,195,23,63,151,104,31],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_levercoin_fees","type":{"defined":{"name":"LevercoinFees"}}}]},{"name":"update_lst_prices","discriminator":[3,34,88,178,240,40,85,148],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"lst_registry","writable":true,"relations":["hylo"]},{"name":"lut_program","address":"AddressLookupTab1e1111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"update_lst_swap_fee","discriminator":[129,125,10,188,94,219,63,217],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_lst_swap_fee","type":{"defined":{"name":"UFixValue64"}}}]},{"name":"update_oracle_conf_tolerance","discriminator":[59,81,210,112,245,81,187,151],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_oracle_conf_tolerance","type":{"defined":{"name":"UFixValue64"}}}]},{"name":"update_oracle_interval","discriminator":[67,242,44,246,210,94,99,214],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_oracle_interval_secs","type":"u64"}]},{"name":"update_sol_usd_oracle","discriminator":[95,236,18,102,201,114,89,54],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_oracle","type":"pubkey"}]},{"name":"update_stability_pool","discriminator":[241,157,233,64,49,184,93,137],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_stability_pool","type":"pubkey"}]},{"name":"update_stability_thresholds","discriminator":[250,58,160,105,52,84,93,167],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_stability_threshold_1","type":{"defined":{"name":"UFixValue64"}}},{"name":"new_stability_threshold_2","type":{"defined":{"name":"UFixValue64"}}}]},{"name":"update_stablecoin_fees","discriminator":[49,74,146,60,3,38,96,4],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_stablecoin_fees","type":{"defined":{"name":"StablecoinFees"}}}]},{"name":"update_treasury","discriminator":[60,16,243,66,96,59,254,131],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_treasury","type":"pubkey"}]},{"name":"update_yield_harvest_config","discriminator":[140,91,46,213,44,179,201,32],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["hylo"]},{"name":"hylo","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_yield_harvest_config","type":{"defined":{"name":"YieldHarvestConfig"}}}]},{"name":"withdraw_fees","discriminator":[198,212,171,109,144,215,174,89],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"treasury","writable":true,"relations":["hylo"]},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}]}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"fee_token_mint"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"fee_token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"treasury_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"treasury"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"fee_token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"fee_token_mint"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]}],"accounts":[{"name":"Hylo","discriminator":[114,161,169,210,204,175,149,174]},{"name":"LstHeader","discriminator":[125,135,217,151,122,202,138,59]},{"name":"PriceUpdateV2","discriminator":[34,241,35,99,157,126,244,205]}],"events":[{"name":"ExchangeStats","discriminator":[74,24,38,211,126,124,214,158]},{"name":"HarvestYieldEventV1","discriminator":[236,194,171,214,66,200,158,27]},{"name":"HarvestYieldEventV2","discriminator":[46,204,60,91,154,15,240,7]},{"name":"MintLevercoinEventV0","discriminator":[71,135,154,151,170,173,218,125]},{"name":"MintLevercoinEventV1","discriminator":[77,221,3,200,106,110,9,245]},{"name":"MintLevercoinEventV2","discriminator":[153,16,148,142,11,127,114,15]},{"name":"MintStablecoinEventV0","discriminator":[181,185,32,71,11,248,162,100]},{"name":"MintStablecoinEventV1","discriminator":[102,41,35,191,18,74,142,16]},{"name":"MintStablecoinEventV2","discriminator":[173,104,166,28,0,52,94,246]},{"name":"RedeemLevercoinEventV0","discriminator":[92,186,13,68,23,219,194,81]},{"name":"RedeemLevercoinEventV1","discriminator":[44,209,148,186,40,214,48,242]},{"name":"RedeemLevercoinEventV2","discriminator":[136,249,79,60,114,254,234,38]},{"name":"RedeemStablecoinEventV0","discriminator":[98,11,53,77,188,71,107,168]},{"name":"RedeemStablecoinEventV1","discriminator":[172,179,174,150,249,177,177,201]},{"name":"RedeemStablecoinEventV2","discriminator":[36,163,198,40,252,128,99,179]},{"name":"RegisterLstEvent","discriminator":[67,129,63,97,144,52,195,33]},{"name":"SwapLeverToStableEventV0","discriminator":[50,170,61,23,136,163,174,145]},{"name":"SwapLeverToStableEventV1","discriminator":[201,228,47,118,19,214,21,45]},{"name":"SwapLstEventV0","discriminator":[246,70,176,207,42,167,214,67]},{"name":"SwapStableToLeverEventV0","discriminator":[134,28,79,7,117,57,126,174]},{"name":"SwapStableToLeverEventV1","discriminator":[184,185,154,14,36,128,241,53]},{"name":"UpdateAdminEvent","discriminator":[225,152,171,87,246,63,66,234]},{"name":"UpdateLevercoinFeesEvent","discriminator":[84,176,60,195,204,115,242,169]},{"name":"UpdateLstPricesEvent","discriminator":[104,0,189,118,195,155,77,151]},{"name":"UpdateLstSwapFeeEvent","discriminator":[139,68,196,130,145,148,173,212]},{"name":"UpdateOracleAddressEvent","discriminator":[28,247,140,128,156,178,148,53]},{"name":"UpdateOracleConfEvent","discriminator":[190,89,82,192,231,31,206,91]},{"name":"UpdateOracleIntervalEvent","discriminator":[190,132,209,41,79,137,195,226]},{"name":"UpdateStabilityPoolEvent","discriminator":[220,82,162,130,152,212,135,63]},{"name":"UpdateStabilityThresholdsEvent","discriminator":[36,117,167,25,25,39,46,189]},{"name":"UpdateStablecoinFeesEvent","discriminator":[38,140,45,231,238,94,115,249]},{"name":"UpdateTreasuryEvent","discriminator":[104,210,213,227,230,150,146,19]},{"name":"UpdateYieldHarvestConfigEvent","discriminator":[67,168,96,20,78,117,245,206]},{"name":"WithdrawFeesEvent","discriminator":[236,118,138,90,139,173,177,89]}],"errors":[{"code":6000,"name":"LevercoinRedeemDisabled","msg":"Cannot redeem levercoin due to stability mode. NAV would be 0 or lower."},{"code":6001,"name":"LeverToStableDisabled","msg":"Levercoin to stablecoin swap disabled due to stability mode."},{"code":6002,"name":"StableToLeverDisabled","msg":"Stablecoin to levercoin swap disabled due to stability mode."},{"code":6003,"name":"SanctumCpi","msg":"Error during CPI to Sanctum LST/SOL calculator."},{"code":6004,"name":"LstRegistryAlreadyInitialized","msg":"LST registry cannot be initialized twice."},{"code":6005,"name":"LstRegistryEmpty","msg":"LST specific accounts found empty when attempting to load registry."},{"code":6006,"name":"LstRegistryPreamble","msg":"Sanctum calculator contexts in LST registry preamble are malformed."},{"code":6007,"name":"LstRegistryLookupTableDeser","msg":"Failed to deserialize registry lookup table."},{"code":6008,"name":"LstRegistryLookupTableInvalid","msg":"Contents of LST registry did not match remaining_accounts."},{"code":6009,"name":"LstBlockInvalid","msg":"Mint/vault/pool accounts in registry block do not match header."},{"code":6010,"name":"LstContextInvalid","msg":"Attempted to register an LST with invalid Sanctum context accounts."},{"code":6011,"name":"LstAdditionOverflow","msg":"Addition overflow while computing total SOL in LST registry."},{"code":6012,"name":"LstPriceOutdated","msg":"Cached LST price not from current epoch. Run pricing crank to update."},{"code":6013,"name":"LstPriceDelta","msg":"Failed to compute delta between current and previous LST prices."},{"code":6014,"name":"LstPriceEpochsInvalid","msg":"Found current epoch less than previous in LST header."},{"code":6015,"name":"LstSolAppreciation","msg":"Overflow while computing LST SOL appreciation."},{"code":6016,"name":"StablecoinMintDisabled","msg":"Stablecoin mint disabled. Protocol is in Mode2 or Depeg."},{"code":6017,"name":"LevercoinMintDisabled","msg":"Levercoin mint disabled. Protocol is in Depeg."},{"code":6018,"name":"YieldHarvestConfigValidation","msg":"Yield harvest configuration percentages failed validation."},{"code":6019,"name":"YieldHarvestDisabled","msg":"Yield harvest disabled due to protocol collateral ratio."},{"code":6020,"name":"YieldHarvestAllocation","msg":"Arithmetic error while computing yield harvest allocation."},{"code":6021,"name":"YieldHarvestEpoch","msg":"Yield harvest already occurred during this epoch."},{"code":6022,"name":"YieldHarvestHasNotRun","msg":"Yield harvest has not yet occurred during this epoch."},{"code":6023,"name":"ZeroRedeem","msg":"Cannot withdraw amount of 0 in a redemption action."},{"code":6024,"name":"ZeroMint","msg":"Cannot deposit amount of 0 in a minting action."},{"code":6025,"name":"ZeroSwap","msg":"Cannot swap amount of 0."},{"code":6026,"name":"IdentitySwap","msg":"Cannot swap from an asset to itself."}],"types":[{"name":"ExchangeStats","docs":["Stats"],"type":{"kind":"struct","fields":[{"name":"total_value_locked","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_supply","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_supply","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_ratio","type":{"defined":{"name":"UFixValue64"}}},{"name":"stability_mode","type":{"defined":{"name":"StabilityMode"}}},{"name":"yield_harvest_cache","type":{"defined":{"name":"YieldHarvestCache"}}}]}},{"name":"FeePair","docs":["Represents the spread of fees between mint and redeem for protocol tokens.","All fees must be in basis points to represent a fractional percentage","directly applicable to a token amount e.g. `0.XXXX` or `bips x 10^-4`."],"type":{"kind":"struct","fields":[{"name":"mint","type":{"defined":{"name":"UFixValue64"}}},{"name":"redeem","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"HarvestYieldEventV1","docs":["Cranks"],"type":{"kind":"struct","fields":[{"name":"harvestable_stablecoin","type":{"defined":{"name":"UFixValue64"}}},{"name":"fees_extracted","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_to_pool","type":{"defined":{"name":"UFixValue64"}}},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"HarvestYieldEventV2","type":{"kind":"struct","fields":[{"name":"harvest_token_mint","type":"pubkey"},{"name":"total_sol_harvested","type":{"defined":{"name":"UFixValue64"}}},{"name":"fees_extracted","type":{"defined":{"name":"UFixValue64"}}},{"name":"token_to_pool","type":{"defined":{"name":"UFixValue64"}}},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"Hylo","type":{"kind":"struct","fields":[{"name":"admin","type":"pubkey"},{"name":"treasury","type":"pubkey"},{"name":"lst_registry","type":"pubkey"},{"name":"stablecoin_mint","type":"pubkey"},{"name":"levercoin_mint","type":"pubkey"},{"name":"stability_pool","type":"pubkey"},{"name":"stablecoin_mint_bump","type":"u8"},{"name":"stablecoin_auth_bump","type":"u8"},{"name":"levercoin_mint_bump","type":"u8"},{"name":"levercoin_auth_bump","type":"u8"},{"name":"registry_auth_bump","type":"u8"},{"name":"total_sol_cache_bump","type":"u8"},{"name":"oracle_interval_secs","type":"u64"},{"name":"stablecoin_fees","type":{"defined":{"name":"StablecoinFees"}}},{"name":"levercoin_fees","type":{"defined":{"name":"LevercoinFees"}}},{"name":"total_sol_cache","type":{"defined":{"name":"TotalSolCache"}}},{"name":"yield_harvest_cache","type":{"defined":{"name":"YieldHarvestCache"}}},{"name":"yield_harvest_config","type":{"defined":{"name":"YieldHarvestConfig"}}},{"name":"stability_threshold_1","type":{"defined":{"name":"UFixValue64"}}},{"name":"stability_threshold_2","type":{"defined":{"name":"UFixValue64"}}},{"name":"oracle_conf_tolerance","type":{"defined":{"name":"UFixValue64"}}},{"name":"sol_usd_oracle","type":"pubkey"},{"name":"lst_swap_fee","type":{"defined":{"name":"UFixValue64"}}},{"name":"_reserved","type":{"array":["u8",78]}}]}},{"name":"LevercoinFees","type":{"kind":"struct","fields":[{"name":"normal","type":{"defined":{"name":"FeePair"}}},{"name":"mode_1","type":{"defined":{"name":"FeePair"}}},{"name":"mode_2","type":{"defined":{"name":"FeePair"}}}]}},{"name":"LstHeader","docs":["Header for a specific LST. Should be the first entry in the block of","accounts, and contains references to the rest of the accounts."],"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"vault","type":"pubkey"},{"name":"pool_state","type":"pubkey"},{"name":"stake_program","type":{"defined":{"name":"LstStakePoolProgram"}}},{"name":"prev_price_sol","type":{"defined":{"name":"LstSolPrice"}}},{"name":"price_sol","type":{"defined":{"name":"LstSolPrice"}}},{"name":"last_yield_harvest_epoch","type":"u64"},{"name":"_reserved","type":{"array":["u8",64]}}]}},{"name":"LstSolPrice","docs":["Captures the true LST price in SOL for the current epoch."],"type":{"kind":"struct","fields":[{"name":"price","type":{"defined":{"name":"UFixValue64"}}},{"name":"epoch","type":"u64"}]}},{"name":"LstStakePoolProgram","type":{"kind":"enum","variants":[{"name":"Spl"},{"name":"SanctumSpl"},{"name":"SanctumSplMulti"},{"name":"Marinade"}]}},{"name":"MintLevercoinEventV0","docs":["`MintLevercoin`"],"type":{"kind":"struct","fields":[{"name":"minted","type":{"defined":{"name":"UFixValue64"}}},{"name":"total_supply","type":"u64"},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_deposited","type":"u64"},{"name":"collateral_ratio","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"MintLevercoinEventV1","type":{"kind":"struct","fields":[{"name":"minted","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"MintLevercoinEventV2","type":{"kind":"struct","fields":[{"name":"minted","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"lst_mint","type":"pubkey"},{"name":"lst_sol_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_deposited","type":{"defined":{"name":"UFixValue64"}}},{"name":"fees_deposited","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"MintStablecoinEventV0","docs":["`MintStablecoin`"],"type":{"kind":"struct","fields":[{"name":"minted","type":{"defined":{"name":"UFixValue64"}}},{"name":"total_supply","type":"u64"},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_deposited","type":"u64"},{"name":"collateral_ratio","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"MintStablecoinEventV1","type":{"kind":"struct","fields":[{"name":"minted","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"MintStablecoinEventV2","type":{"kind":"struct","fields":[{"name":"minted","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"lst_mint","type":"pubkey"},{"name":"lst_sol_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_deposited","type":{"defined":{"name":"UFixValue64"}}},{"name":"fees_deposited","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"PriceFeedMessage","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"feed_id","docs":["`FeedId` but avoid the type alias because of compatibility issues with Anchor's `idl-build` feature."],"type":{"array":["u8",32]}},{"name":"price","type":"i64"},{"name":"conf","type":"u64"},{"name":"exponent","type":"i32"},{"name":"publish_time","docs":["The timestamp of this price update in seconds"],"type":"i64"},{"name":"prev_publish_time","docs":["The timestamp of the previous price update. This field is intended to allow users to","identify the single unique price update for any moment in time:","for any time t, the unique update is the one such that prev_publish_time < t <= publish_time.","","Note that there may not be such an update while we are migrating to the new message-sending logic,","as some price updates on pythnet may not be sent to other chains (because the message-sending","logic may not have triggered). We can solve this problem by making the message-sending mandatory","(which we can do once publishers have migrated over).","","Additionally, this field may be equal to publish_time if the message is sent on a slot where","where the aggregation was unsuccesful. This problem will go away once all publishers have","migrated over to a recent version of pyth-agent."],"type":"i64"},{"name":"ema_price","type":"i64"},{"name":"ema_conf","type":"u64"}]}},{"name":"PriceUpdateV2","docs":["A price update account. This account is used by the Pyth Receiver program to store a verified price update from a Pyth price feed.","It contains:","- `write_authority`: The write authority for this account. This authority can close this account to reclaim rent or update the account to contain a different price update.","- `verification_level`: The [`VerificationLevel`] of this price update. This represents how many Wormhole guardian signatures have been verified for this price update.","- `price_message`: The actual price update.","- `posted_slot`: The slot at which this price update was posted."],"type":{"kind":"struct","fields":[{"name":"write_authority","type":"pubkey"},{"name":"verification_level","type":{"defined":{"name":"VerificationLevel"}}},{"name":"price_message","type":{"defined":{"name":"PriceFeedMessage"}}},{"name":"posted_slot","type":"u64"}]}},{"name":"RedeemLevercoinEventV0","docs":["`RedeemLevercoin`"],"type":{"kind":"struct","fields":[{"name":"redeemed","type":"u64"},{"name":"total_supply","type":"u64"},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_withdrawn","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_ratio","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"RedeemLevercoinEventV1","type":{"kind":"struct","fields":[{"name":"redeemed","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"RedeemLevercoinEventV2","type":{"kind":"struct","fields":[{"name":"redeemed","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"lst_mint","type":"pubkey"},{"name":"lst_sol_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_withdrawn","type":{"defined":{"name":"UFixValue64"}}},{"name":"fees_deposited","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"RedeemStablecoinEventV0","docs":["`RedeemStablecoin`"],"type":{"kind":"struct","fields":[{"name":"redeemed","type":"u64"},{"name":"total_supply","type":"u64"},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_withdrawn","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_ratio","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"RedeemStablecoinEventV1","type":{"kind":"struct","fields":[{"name":"redeemed","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"RedeemStablecoinEventV2","type":{"kind":"struct","fields":[{"name":"redeemed","type":{"defined":{"name":"UFixValue64"}}},{"name":"nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"sol_usd_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"lst_mint","type":"pubkey"},{"name":"lst_sol_price","type":{"defined":{"name":"UFixValue64"}}},{"name":"collateral_withdrawn","type":{"defined":{"name":"UFixValue64"}}},{"name":"fees_deposited","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"RegisterLstEvent","docs":["Admin"],"type":{"kind":"struct","fields":[{"name":"header","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"vault","type":"pubkey"},{"name":"pool_state","type":"pubkey"}]}},{"name":"SlippageConfig","docs":["Client specified slippage tolerance paired with expected token amount."],"type":{"kind":"struct","fields":[{"name":"expected_token_out","type":{"defined":{"name":"UFixValue64"}}},{"name":"slippage_tolerance","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"StabilityMode","docs":["Mode of operation based on the protocol's current collateral ratio.","See whitepaper for more."],"type":{"kind":"enum","variants":[{"name":"Normal"},{"name":"Mode1"},{"name":"Mode2"},{"name":"Depeg"}]}},{"name":"StablecoinFees","type":{"kind":"struct","fields":[{"name":"normal","type":{"defined":{"name":"FeePair"}}},{"name":"mode_1","type":{"defined":{"name":"FeePair"}}}]}},{"name":"SwapLeverToStableEventV0","type":{"kind":"struct","fields":[{"name":"levercoin_burned","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_minted","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"SwapLeverToStableEventV1","type":{"kind":"struct","fields":[{"name":"levercoin_burned","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_minted_user","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_minted_fees","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"SwapLstEventV0","type":{"kind":"struct","fields":[{"name":"lst_a_mint","type":"pubkey"},{"name":"lst_a_in","type":{"defined":{"name":"UFixValue64"}}},{"name":"lst_a_fees_extracted","type":{"defined":{"name":"UFixValue64"}}},{"name":"lst_b_mint","type":"pubkey"},{"name":"lst_b_out","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"SwapStableToLeverEventV0","docs":["Swaps"],"type":{"kind":"struct","fields":[{"name":"stablecoin_burned","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_minted","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"SwapStableToLeverEventV1","type":{"kind":"struct","fields":[{"name":"stablecoin_burned","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_fees","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_minted","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"TotalSolCache","type":{"kind":"struct","fields":[{"name":"current_update_epoch","type":"u64"},{"name":"total_sol","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"UFixValue64","docs":["A value-space `Fix` where base is always 10 and bits are a concrete type.","Intended for serialized storage in Solana accounts where generics won't work."],"type":{"kind":"struct","fields":[{"name":"bits","type":"u64"},{"name":"exp","type":"i8"}]}},{"name":"UpdateAdminEvent","type":{"kind":"struct","fields":[{"name":"old_admin","type":"pubkey"},{"name":"new_admin","type":"pubkey"}]}},{"name":"UpdateLevercoinFeesEvent","type":{"kind":"struct","fields":[{"name":"old_levercoin_fees","type":{"defined":{"name":"LevercoinFees"}}},{"name":"new_levercoin_fees","type":{"defined":{"name":"LevercoinFees"}}}]}},{"name":"UpdateLstPricesEvent","type":{"kind":"struct","fields":[{"name":"updated_mints","type":{"vec":"pubkey"}},{"name":"new_total_sol","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"UpdateLstSwapFeeEvent","type":{"kind":"struct","fields":[{"name":"old_lst_swap_fee","type":{"defined":{"name":"UFixValue64"}}},{"name":"new_lst_swap_fee","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"UpdateOracleAddressEvent","type":{"kind":"struct","fields":[{"name":"old_oracle","type":"pubkey"},{"name":"new_oracle","type":"pubkey"}]}},{"name":"UpdateOracleConfEvent","type":{"kind":"struct","fields":[{"name":"old_oracle_conf_tolerance","type":{"defined":{"name":"UFixValue64"}}},{"name":"new_oracle_conf_tolerance","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"UpdateOracleIntervalEvent","type":{"kind":"struct","fields":[{"name":"old_oracle_interval_secs","type":"u64"},{"name":"new_oracle_interval_secs","type":"u64"}]}},{"name":"UpdateStabilityPoolEvent","type":{"kind":"struct","fields":[{"name":"old_stability_pool","type":"pubkey"},{"name":"new_stability_pool","type":"pubkey"}]}},{"name":"UpdateStabilityThresholdsEvent","type":{"kind":"struct","fields":[{"name":"old_stability_threshold_1","type":{"defined":{"name":"UFixValue64"}}},{"name":"old_stability_threshold_2","type":{"defined":{"name":"UFixValue64"}}},{"name":"new_stability_threshold_1","type":{"defined":{"name":"UFixValue64"}}},{"name":"new_stability_threshold_2","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"UpdateStablecoinFeesEvent","type":{"kind":"struct","fields":[{"name":"old_stablecoin_fees","type":{"defined":{"name":"StablecoinFees"}}},{"name":"new_stablecoin_fees","type":{"defined":{"name":"StablecoinFees"}}}]}},{"name":"UpdateTreasuryEvent","type":{"kind":"struct","fields":[{"name":"old_treasury","type":"pubkey"},{"name":"new_treasury","type":"pubkey"}]}},{"name":"UpdateYieldHarvestConfigEvent","type":{"kind":"struct","fields":[{"name":"old_yield_harvest_config","type":{"defined":{"name":"YieldHarvestConfig"}}},{"name":"new_yield_harvest_config","type":{"defined":{"name":"YieldHarvestConfig"}}}]}},{"name":"VerificationLevel","docs":["Pyth price updates are bridged to all blockchains via Wormhole.","Using the price updates on another chain requires verifying the signatures of the Wormhole guardians.","The usual process is to check the signatures for two thirds of the total number of guardians, but this can be cumbersome on Solana because of the transaction size limits,","so we also allow for partial verification.","","This enum represents how much a price update has been verified:","- If `Full`, we have verified the signatures for two thirds of the current guardians.","- If `Partial`, only `num_signatures` guardian signatures have been checked.","","# Warning","Using partially verified price updates is dangerous, as it lowers the threshold of guardians that need to collude to produce a malicious price update."],"type":{"kind":"enum","variants":[{"name":"Partial","fields":[{"name":"num_signatures","type":"u8"}]},{"name":"Full"}]}},{"name":"WithdrawFeesEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"vault","type":"pubkey"},{"name":"treasury_ata","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"YieldHarvestCache","docs":["Records epoch yield harvest information for off-chain consumers."],"type":{"kind":"struct","fields":[{"name":"epoch","type":"u64"},{"name":"stability_pool_cap","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_yield_to_pool","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"YieldHarvestConfig","docs":["Captures yield harvest configuration as two basis point values:"],"type":{"kind":"struct","fields":[{"name":"allocation","type":{"defined":{"name":"UFixValue64"}}},{"name":"fee","type":{"defined":{"name":"UFixValue64"}}}]}}],"constants":[{"name":"FEE_AUTH","type":{"array":["u8",8]},"value":"[102, 101, 101, 95, 97, 117, 116, 104]"},{"name":"HYLO","type":{"array":["u8",4]},"value":"[104, 121, 108, 111]"},{"name":"HYUSD","type":{"array":["u8",5]},"value":"[104, 121, 85, 83, 68]"},{"name":"LST_HEADER","type":{"array":["u8",10]},"value":"[108, 115, 116, 95, 104, 101, 97, 100, 101, 114]"},{"name":"LST_REGISTRY_AUTH","type":{"array":["u8",17]},"value":"[108, 115, 116, 95, 114, 101, 103, 105, 115, 116, 114, 121, 95, 97, 117, 116, 104]"},{"name":"MINT_AUTH","type":{"array":["u8",9]},"value":"[109, 105, 110, 116, 95, 97, 117, 116, 104]"},{"name":"POOL_AUTH","type":{"array":["u8",9]},"value":"[112, 111, 111, 108, 95, 97, 117, 116, 104]"},{"name":"VAULT_AUTH","type":{"array":["u8",10]},"value":"[118, 97, 117, 108, 116, 95, 97, 117, 116, 104]"},{"name":"XSOL","type":{"array":["u8",4]},"value":"[120, 83, 79, 76]"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/hylo_stability_pool.HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ.json b/migration/khadhroony-bot2-reference/idls/hylo_stability_pool.HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ.json new file mode 100644 index 0000000..8844cbd --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/hylo_stability_pool.HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ.json @@ -0,0 +1 @@ +{"address":"HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ","metadata":{"name":"hylo_stability_pool","version":"0.1.4","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"get_stats","discriminator":[241,65,112,185,230,140,139,177],"accounts":[{"name":"pool_config","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,99,111,110,102,105,103]}]}},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"stablecoin_mint","pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"levercoin_mint","pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"pool_auth","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,97,117,116,104]}]}},{"name":"stablecoin_pool","pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"levercoin_pool","pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lp_token_mint","pda":{"seeds":[{"kind":"const","value":[115,116,97,107,101,100,95,104,121,85,83,68]}]}},{"name":"sol_usd_pyth_feed"}],"args":[],"returns":{"defined":{"name":"StabilityPoolStats"}}},{"name":"initialize_lp_token_mint","discriminator":[188,32,168,40,166,152,159,156],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["pool_config"]},{"name":"pool_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,99,111,110,102,105,103]}]}},{"name":"lp_token_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"lp_token_mint"}]}},{"name":"lp_token_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,101,100,95,104,121,85,83,68]}]}},{"name":"lp_token_metadata","writable":true},{"name":"metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"initialize_stability_pool","discriminator":[220,103,253,62,133,245,74,222],"accounts":[{"name":"admin","writable":true,"signer":true},{"name":"upgrade_authority","signer":true},{"name":"pool_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,99,111,110,102,105,103]}]}},{"name":"pool_auth","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,97,117,116,104]}]}},{"name":"stablecoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"levercoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"stablecoin_mint","pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"levercoin_mint","pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"program_data"},{"name":"hylo_stability_pool","address":"HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ"}],"args":[]},{"name":"rebalance_lever_to_stable","discriminator":[194,252,113,42,31,248,184,211],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"pool_config","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,99,111,110,102,105,103]}]}},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"stablecoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"stablecoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool_auth","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,97,117,116,104]}]}},{"name":"levercoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"levercoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"sol_usd_pyth_feed"},{"name":"stablecoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"levercoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"hylo_event_authority"},{"name":"hylo_exchange_program","address":"HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"rebalance_stable_to_lever","discriminator":[210,59,29,161,20,10,3,68],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"pool_config","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,99,111,110,102,105,103]}]}},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"stablecoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"stablecoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool_auth","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,97,117,116,104]}]}},{"name":"levercoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"levercoin_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"sol_usd_pyth_feed"},{"name":"stablecoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"levercoin_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"hylo_event_authority"},{"name":"hylo_exchange_program","address":"HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"update_admin","discriminator":[161,176,40,213,60,184,179,228],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"upgrade_authority","signer":true},{"name":"pool_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,99,111,110,102,105,103]}]}},{"name":"program_data"},{"name":"hylo_stability_pool","address":"HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_admin","type":"pubkey"}]},{"name":"update_withdrawal_fee","discriminator":[165,235,231,25,127,99,244,133],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["pool_config"]},{"name":"pool_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,99,111,110,102,105,103]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_withdrawal_fee","type":{"defined":{"name":"UFixValue64"}}}]},{"name":"user_deposit","discriminator":[186,198,140,233,129,39,98,153],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"pool_config","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,99,111,110,102,105,103]}]}},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"stablecoin_mint","pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"levercoin_mint","pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"user_stablecoin_ta","writable":true},{"name":"user_lp_token_ta","writable":true},{"name":"pool_auth","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,97,117,116,104]}]}},{"name":"stablecoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"levercoin_pool","pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lp_token_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"lp_token_mint"}]}},{"name":"lp_token_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,101,100,95,104,121,85,83,68]}]}},{"name":"sol_usd_pyth_feed"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_stablecoin","type":"u64"}]},{"name":"user_withdraw","discriminator":[53,254,26,242,119,237,73,33],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"pool_config","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,99,111,110,102,105,103]}]}},{"name":"hylo","pda":{"seeds":[{"kind":"const","value":[104,121,108,111]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"stablecoin_mint","pda":{"seeds":[{"kind":"const","value":[104,121,85,83,68]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"user_stablecoin_ta","writable":true},{"name":"fee_auth","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,97,117,116,104]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user_lp_token_ta","writable":true},{"name":"pool_auth","pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,97,117,116,104]}]}},{"name":"stablecoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"stablecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"levercoin_mint","pda":{"seeds":[{"kind":"const","value":[120,83,79,76]}],"program":{"kind":"const","value":[245,187,72,160,4,116,48,134,197,164,152,189,233,219,27,124,201,65,103,243,58,82,140,90,13,150,83,40,223,158,124,33]}}},{"name":"levercoin_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_auth"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"levercoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user_levercoin_ta","writable":true},{"name":"lp_token_auth","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104]},{"kind":"account","path":"lp_token_mint"}]}},{"name":"lp_token_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,101,100,95,104,121,85,83,68]}]}},{"name":"sol_usd_pyth_feed"},{"name":"hylo_event_authority"},{"name":"hylo_exchange_program","address":"HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_lp_token","type":"u64"}]}],"accounts":[{"name":"Hylo","discriminator":[114,161,169,210,204,175,149,174]},{"name":"PoolConfig","discriminator":[26,108,14,123,116,230,129,43]},{"name":"PriceUpdateV2","discriminator":[34,241,35,99,157,126,244,205]}],"events":[{"name":"RebalanceLeverToStableEvent","discriminator":[177,3,181,27,187,44,49,109]},{"name":"RebalanceStableToLeverEvent","discriminator":[117,99,97,34,247,186,34,198]},{"name":"StabilityPoolStats","discriminator":[146,92,252,198,221,148,242,182]},{"name":"UpdateAdminEvent","discriminator":[225,152,171,87,246,63,66,234]},{"name":"UpdateWithdrawalFeeEvent","discriminator":[204,60,32,187,54,211,107,115]},{"name":"UserDepositEvent","discriminator":[70,55,110,118,235,187,239,33]},{"name":"UserWithdrawEventV0","discriminator":[142,135,121,155,248,252,59,161]},{"name":"UserWithdrawEventV1","discriminator":[51,241,35,135,62,237,83,122]}],"errors":[{"code":6000,"name":"RebalanceDisabled","msg":"Rebalance is disabled due to stability or pool amount constraint not \\\n being met."},{"code":6001,"name":"DepositDisabled","msg":"Deposits to pool disabled due to active rebalancing."}],"types":[{"name":"FeePair","docs":["Represents the spread of fees between mint and redeem for protocol tokens.","All fees must be in basis points to represent a fractional percentage","directly applicable to a token amount e.g. `0.XXXX` or `bips x 10^-4`."],"type":{"kind":"struct","fields":[{"name":"mint","type":{"defined":{"name":"UFixValue64"}}},{"name":"redeem","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"Hylo","type":{"kind":"struct","fields":[{"name":"admin","type":"pubkey"},{"name":"treasury","type":"pubkey"},{"name":"lst_registry","type":"pubkey"},{"name":"stablecoin_mint","type":"pubkey"},{"name":"levercoin_mint","type":"pubkey"},{"name":"stability_pool","type":"pubkey"},{"name":"stablecoin_mint_bump","type":"u8"},{"name":"stablecoin_auth_bump","type":"u8"},{"name":"levercoin_mint_bump","type":"u8"},{"name":"levercoin_auth_bump","type":"u8"},{"name":"registry_auth_bump","type":"u8"},{"name":"total_sol_cache_bump","type":"u8"},{"name":"oracle_interval_secs","type":"u64"},{"name":"stablecoin_fees","type":{"defined":{"name":"StablecoinFees"}}},{"name":"levercoin_fees","type":{"defined":{"name":"LevercoinFees"}}},{"name":"total_sol_cache","type":{"defined":{"name":"TotalSolCache"}}},{"name":"yield_harvest_cache","type":{"defined":{"name":"YieldHarvestCache"}}},{"name":"yield_harvest_config","type":{"defined":{"name":"YieldHarvestConfig"}}},{"name":"stability_threshold_1","type":{"defined":{"name":"UFixValue64"}}},{"name":"stability_threshold_2","type":{"defined":{"name":"UFixValue64"}}},{"name":"oracle_conf_tolerance","type":{"defined":{"name":"UFixValue64"}}},{"name":"sol_usd_oracle","type":"pubkey"},{"name":"lst_swap_fee","type":{"defined":{"name":"UFixValue64"}}},{"name":"_reserved","type":{"array":["u8",78]}}]}},{"name":"LevercoinFees","type":{"kind":"struct","fields":[{"name":"normal","type":{"defined":{"name":"FeePair"}}},{"name":"mode_1","type":{"defined":{"name":"FeePair"}}},{"name":"mode_2","type":{"defined":{"name":"FeePair"}}}]}},{"name":"PoolConfig","type":{"kind":"struct","fields":[{"name":"admin","type":"pubkey"},{"name":"pool_auth_bump","type":"u8"},{"name":"lp_token_auth_bump","type":"u8"},{"name":"lp_token_mint_bump","type":"u8"},{"name":"withdrawal_fee","type":{"defined":{"name":"UFixValue64"}}},{"name":"_reserved","type":{"array":["u8",55]}}]}},{"name":"PriceFeedMessage","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"feed_id","docs":["`FeedId` but avoid the type alias because of compatibility issues with Anchor's `idl-build` feature."],"type":{"array":["u8",32]}},{"name":"price","type":"i64"},{"name":"conf","type":"u64"},{"name":"exponent","type":"i32"},{"name":"publish_time","docs":["The timestamp of this price update in seconds"],"type":"i64"},{"name":"prev_publish_time","docs":["The timestamp of the previous price update. This field is intended to allow users to","identify the single unique price update for any moment in time:","for any time t, the unique update is the one such that prev_publish_time < t <= publish_time.","","Note that there may not be such an update while we are migrating to the new message-sending logic,","as some price updates on pythnet may not be sent to other chains (because the message-sending","logic may not have triggered). We can solve this problem by making the message-sending mandatory","(which we can do once publishers have migrated over).","","Additionally, this field may be equal to publish_time if the message is sent on a slot where","where the aggregation was unsuccesful. This problem will go away once all publishers have","migrated over to a recent version of pyth-agent."],"type":"i64"},{"name":"ema_price","type":"i64"},{"name":"ema_conf","type":"u64"}]}},{"name":"PriceUpdateV2","docs":["A price update account. This account is used by the Pyth Receiver program to store a verified price update from a Pyth price feed.","It contains:","- `write_authority`: The write authority for this account. This authority can close this account to reclaim rent or update the account to contain a different price update.","- `verification_level`: The [`VerificationLevel`] of this price update. This represents how many Wormhole guardian signatures have been verified for this price update.","- `price_message`: The actual price update.","- `posted_slot`: The slot at which this price update was posted."],"type":{"kind":"struct","fields":[{"name":"write_authority","type":"pubkey"},{"name":"verification_level","type":{"defined":{"name":"VerificationLevel"}}},{"name":"price_message","type":{"defined":{"name":"PriceFeedMessage"}}},{"name":"posted_slot","type":"u64"}]}},{"name":"RebalanceLeverToStableEvent","type":{"kind":"struct","fields":[{"name":"levercoin_swapped","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"RebalanceStableToLeverEvent","type":{"kind":"struct","fields":[{"name":"stablecoin_swapped","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"StabilityPoolStats","type":{"kind":"struct","fields":[{"name":"lp_token_nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"lp_token_supply","type":{"defined":{"name":"UFixValue64"}}},{"name":"stability_pool_cap","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_in_pool","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_in_pool","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"StablecoinFees","type":{"kind":"struct","fields":[{"name":"normal","type":{"defined":{"name":"FeePair"}}},{"name":"mode_1","type":{"defined":{"name":"FeePair"}}}]}},{"name":"TotalSolCache","type":{"kind":"struct","fields":[{"name":"current_update_epoch","type":"u64"},{"name":"total_sol","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"UFixValue64","docs":["A value-space `Fix` where base is always 10 and bits are a concrete type.","Intended for serialized storage in Solana accounts where generics won't work."],"type":{"kind":"struct","fields":[{"name":"bits","type":"u64"},{"name":"exp","type":"i8"}]}},{"name":"UpdateAdminEvent","type":{"kind":"struct","fields":[{"name":"old_admin","type":"pubkey"},{"name":"new_admin","type":"pubkey"}]}},{"name":"UpdateWithdrawalFeeEvent","type":{"kind":"struct","fields":[{"name":"old_withdrawal_fee","type":{"defined":{"name":"UFixValue64"}}},{"name":"new_withdrawal_fee","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"UserDepositEvent","type":{"kind":"struct","fields":[{"name":"stablecoin_deposited","type":{"defined":{"name":"UFixValue64"}}},{"name":"lp_token_nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"lp_token_minted","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"UserWithdrawEventV0","type":{"kind":"struct","fields":[{"name":"lp_token_burned","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_withdrawn","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_withdrawn","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"UserWithdrawEventV1","type":{"kind":"struct","fields":[{"name":"lp_token_burned","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_withdrawn","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_fees","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_nav","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_withdrawn","type":{"defined":{"name":"UFixValue64"}}},{"name":"levercoin_nav","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"VerificationLevel","docs":["Pyth price updates are bridged to all blockchains via Wormhole.","Using the price updates on another chain requires verifying the signatures of the Wormhole guardians.","The usual process is to check the signatures for two thirds of the total number of guardians, but this can be cumbersome on Solana because of the transaction size limits,","so we also allow for partial verification.","","This enum represents how much a price update has been verified:","- If `Full`, we have verified the signatures for two thirds of the current guardians.","- If `Partial`, only `num_signatures` guardian signatures have been checked.","","# Warning","Using partially verified price updates is dangerous, as it lowers the threshold of guardians that need to collude to produce a malicious price update."],"type":{"kind":"enum","variants":[{"name":"Partial","fields":[{"name":"num_signatures","type":"u8"}]},{"name":"Full"}]}},{"name":"YieldHarvestCache","docs":["Records epoch yield harvest information for off-chain consumers."],"type":{"kind":"struct","fields":[{"name":"epoch","type":"u64"},{"name":"stability_pool_cap","type":{"defined":{"name":"UFixValue64"}}},{"name":"stablecoin_yield_to_pool","type":{"defined":{"name":"UFixValue64"}}}]}},{"name":"YieldHarvestConfig","docs":["Captures yield harvest configuration as two basis point values:"],"type":{"kind":"struct","fields":[{"name":"allocation","type":{"defined":{"name":"UFixValue64"}}},{"name":"fee","type":{"defined":{"name":"UFixValue64"}}}]}}],"constants":[{"name":"POOL_AUTH","type":{"array":["u8",9]},"value":"[112, 111, 111, 108, 95, 97, 117, 116, 104]"},{"name":"POOL_CONFIG","type":{"array":["u8",11]},"value":"[112, 111, 111, 108, 95, 99, 111, 110, 102, 105, 103]"},{"name":"STAKED_HYUSD","type":{"array":["u8",12]},"value":"[115, 116, 97, 107, 101, 100, 95, 104, 121, 85, 83, 68]"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/jito_tip_distribution.4R3gSG8BpU4t19KYj8CfnbtRpnT8gtk4dvTHxVRwc2r7..json b/migration/khadhroony-bot2-reference/idls/jito_tip_distribution.4R3gSG8BpU4t19KYj8CfnbtRpnT8gtk4dvTHxVRwc2r7..json new file mode 100644 index 0000000..ddfecc4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/jito_tip_distribution.4R3gSG8BpU4t19KYj8CfnbtRpnT8gtk4dvTHxVRwc2r7..json @@ -0,0 +1 @@ +{"address":"4R3gSG8BpU4t19KYj8CfnbtRpnT8gtk4dvTHxVRwc2r7","metadata":{"name":"jito_tip_distribution","version":"0.1.5","spec":"0.1.0","description":"Tip distribution program, responsible for distributing funds to entitled parties."},"instructions":[{"name":"claim","docs":["Claims tokens from the [TipDistributionAccount]."],"discriminator":[62,198,214,193,213,159,108,210],"accounts":[{"name":"config"},{"name":"tip_distribution_account","writable":true},{"name":"merkle_root_upload_authority","signer":true},{"name":"claim_status","docs":["Status of the claim. Used to prevent the same party from claiming multiple times."],"writable":true},{"name":"claimant","docs":["Receiver of the funds."],"writable":true},{"name":"payer","docs":["Who is paying for the claim."],"writable":true,"signer":true},{"name":"system_program"}],"args":[{"name":"bump","type":"u8"},{"name":"amount","type":"u64"},{"name":"proof","type":{"vec":{"array":["u8",32]}}}]},{"name":"close_claim_status","docs":["Anyone can invoke this only after the [TipDistributionAccount] has expired.","This instruction will return any rent back to `claimant` and close the account"],"discriminator":[163,214,191,165,245,188,17,185],"accounts":[{"name":"config"},{"name":"claim_status","writable":true},{"name":"claim_status_payer","docs":["Receiver of the funds."],"writable":true}],"args":[]},{"name":"close_tip_distribution_account","docs":["Anyone can invoke this only after the [TipDistributionAccount] has expired.","This instruction will send any unclaimed funds to the designated `expired_funds_account`","before closing and returning the rent exempt funds to the validator."],"discriminator":[47,136,208,190,125,243,74,227],"accounts":[{"name":"config"},{"name":"expired_funds_account","writable":true},{"name":"tip_distribution_account","writable":true},{"name":"validator_vote_account","writable":true},{"name":"signer","docs":["Anyone can crank this instruction."],"writable":true,"signer":true}],"args":[{"name":"_epoch","type":"u64"}]},{"name":"initialize","docs":["Initialize a singleton instance of the [Config] account."],"discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"config","writable":true},{"name":"system_program"},{"name":"initializer","writable":true,"signer":true}],"args":[{"name":"authority","type":"pubkey"},{"name":"expired_funds_account","type":"pubkey"},{"name":"num_epochs_valid","type":"u64"},{"name":"max_validator_commission_bps","type":"u16"},{"name":"bump","type":"u8"}]},{"name":"initialize_merkle_root_upload_config","discriminator":[232,87,72,14,89,40,40,27],"accounts":[{"name":"config","writable":true},{"name":"merkle_root_upload_config","writable":true},{"name":"authority","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program"}],"args":[{"name":"authority","type":"pubkey"},{"name":"original_authority","type":"pubkey"}]},{"name":"initialize_tip_distribution_account","docs":["Initialize a new [TipDistributionAccount] associated with the given validator vote key","and current epoch."],"discriminator":[120,191,25,182,111,49,179,55],"accounts":[{"name":"config"},{"name":"tip_distribution_account","writable":true},{"name":"validator_vote_account","docs":["The validator's vote account is used to check this transaction's signer is also the authorized withdrawer."]},{"name":"signer","docs":["Must be equal to the supplied validator vote account's authorized withdrawer."],"writable":true,"signer":true},{"name":"system_program"}],"args":[{"name":"merkle_root_upload_authority","type":"pubkey"},{"name":"validator_commission_bps","type":"u16"},{"name":"bump","type":"u8"}]},{"name":"migrate_tda_merkle_root_upload_authority","discriminator":[13,226,163,144,56,202,214,23],"accounts":[{"name":"tip_distribution_account","writable":true},{"name":"merkle_root_upload_config"}],"args":[]},{"name":"update_config","docs":["Update config fields. Only the [Config] authority can invoke this."],"discriminator":[29,158,252,191,10,83,219,99],"accounts":[{"name":"config","writable":true},{"name":"authority","writable":true,"signer":true}],"args":[{"name":"new_config","type":{"defined":{"name":"Config"}}}]},{"name":"update_merkle_root_upload_config","discriminator":[128,227,159,139,176,128,118,2],"accounts":[{"name":"config"},{"name":"merkle_root_upload_config","writable":true},{"name":"authority","signer":true},{"name":"system_program"}],"args":[{"name":"authority","type":"pubkey"},{"name":"original_authority","type":"pubkey"}]},{"name":"upload_merkle_root","docs":["Uploads a merkle root to the provided [TipDistributionAccount]. This instruction may be","invoked many times as long as the account is at least one epoch old and not expired; and","no funds have already been claimed. Only the `merkle_root_upload_authority` has the","authority to invoke."],"discriminator":[70,3,110,29,199,190,205,176],"accounts":[{"name":"config"},{"name":"tip_distribution_account","writable":true},{"name":"merkle_root_upload_authority","writable":true,"signer":true}],"args":[{"name":"root","type":{"array":["u8",32]}},{"name":"max_total_claim","type":"u64"},{"name":"max_num_nodes","type":"u64"}]}],"accounts":[{"name":"ClaimStatus","discriminator":[22,183,249,157,247,95,150,96]},{"name":"Config","discriminator":[155,12,170,224,30,250,204,130]},{"name":"MerkleRootUploadConfig","discriminator":[213,125,30,192,25,121,87,33]},{"name":"TipDistributionAccount","discriminator":[85,64,113,198,234,94,120,123]}],"events":[{"name":"ClaimStatusClosedEvent","discriminator":[188,143,237,229,192,182,164,118]},{"name":"ClaimedEvent","discriminator":[144,172,209,86,144,87,84,115]},{"name":"ConfigUpdatedEvent","discriminator":[245,158,129,99,60,100,214,220]},{"name":"MerkleRootUploadAuthorityUpdatedEvent","discriminator":[83,157,58,165,200,171,8,106]},{"name":"MerkleRootUploadedEvent","discriminator":[94,233,236,49,52,224,181,167]},{"name":"TipDistributionAccountClosedEvent","discriminator":[246,152,49,154,9,79,25,58]},{"name":"TipDistributionAccountInitializedEvent","discriminator":[39,165,224,61,40,140,139,255]},{"name":"ValidatorCommissionBpsUpdatedEvent","discriminator":[4,34,92,25,228,88,51,206]}],"errors":[{"code":6000,"name":"AccountValidationFailure","msg":"Account failed validation."},{"code":6001,"name":"ArithmeticError","msg":"Encountered an arithmetic under/overflow error."},{"code":6002,"name":"ExceedsMaxClaim","msg":"The maximum number of funds to be claimed has been exceeded."},{"code":6003,"name":"ExceedsMaxNumNodes","msg":"The maximum number of claims has been exceeded."},{"code":6004,"name":"ExpiredTipDistributionAccount","msg":"The given TipDistributionAccount has expired."},{"code":6005,"name":"FundsAlreadyClaimed","msg":"The funds for the given index and TipDistributionAccount have already been claimed."},{"code":6006,"name":"InvalidParameters","msg":"Supplied invalid parameters."},{"code":6007,"name":"InvalidProof","msg":"The given proof is invalid."},{"code":6008,"name":"InvalidVoteAccountData","msg":"Failed to deserialize the supplied vote account data."},{"code":6009,"name":"MaxValidatorCommissionFeeBpsExceeded","msg":"Validator's commission basis points must be less than or equal to the Config account's max_validator_commission_bps."},{"code":6010,"name":"PrematureCloseTipDistributionAccount","msg":"The given TipDistributionAccount is not ready to be closed."},{"code":6011,"name":"PrematureCloseClaimStatus","msg":"The given ClaimStatus account is not ready to be closed."},{"code":6012,"name":"PrematureMerkleRootUpload","msg":"Must wait till at least one epoch after the tip distribution account was created to upload the merkle root."},{"code":6013,"name":"RootNotUploaded","msg":"No merkle root has been uploaded to the given TipDistributionAccount."},{"code":6014,"name":"Unauthorized","msg":"Unauthorized signer."},{"code":6015,"name":"InvalidTdaForMigration","msg":"TDA not valid for migration."}],"types":[{"name":"ClaimStatus","docs":["Gives us an audit trail of who and what was claimed; also enforces and only-once claim by any party."],"type":{"kind":"struct","fields":[{"name":"is_claimed","docs":["If true, the tokens have been claimed."],"type":"bool"},{"name":"claimant","docs":["Authority that claimed the tokens. Allows for delegated rewards claiming."],"type":"pubkey"},{"name":"claim_status_payer","docs":["The payer who created the claim."],"type":"pubkey"},{"name":"slot_claimed_at","docs":["When the funds were claimed."],"type":"u64"},{"name":"amount","docs":["Amount of funds claimed."],"type":"u64"},{"name":"expires_at","docs":["The epoch (upto and including) that tip funds can be claimed.","Copied since TDA can be closed, need to track to avoid making multiple claims"],"type":"u64"},{"name":"bump","docs":["The bump used to generate this account"],"type":"u8"}]}},{"name":"ClaimStatusClosedEvent","type":{"kind":"struct","fields":[{"name":"claim_status_payer","docs":["Account where funds were transferred to."],"type":"pubkey"},{"name":"claim_status_account","docs":["[ClaimStatus] account that was closed."],"type":"pubkey"}]}},{"name":"ClaimedEvent","type":{"kind":"struct","fields":[{"name":"tip_distribution_account","docs":["[TipDistributionAccount] claimed from."],"type":"pubkey"},{"name":"payer","docs":["User that paid for the claim, may or may not be the same as claimant."],"type":"pubkey"},{"name":"claimant","docs":["Account that received the funds."],"type":"pubkey"},{"name":"amount","docs":["Amount of funds to distribute."],"type":"u64"}]}},{"name":"Config","type":{"kind":"struct","fields":[{"name":"authority","docs":["Account with authority over this PDA."],"type":"pubkey"},{"name":"expired_funds_account","docs":["We want to expire funds after some time so that validators can be refunded the rent.","Expired funds will get transferred to this account."],"type":"pubkey"},{"name":"num_epochs_valid","docs":["Specifies the number of epochs a merkle root is valid for before expiring."],"type":"u64"},{"name":"max_validator_commission_bps","docs":["The maximum commission a validator can set on their distribution account."],"type":"u16"},{"name":"bump","docs":["The bump used to generate this account"],"type":"u8"}]}},{"name":"ConfigUpdatedEvent","type":{"kind":"struct","fields":[{"name":"authority","docs":["Who updated it."],"type":"pubkey"}]}},{"name":"MerkleRoot","type":{"kind":"struct","fields":[{"name":"root","docs":["The 256-bit merkle root."],"type":{"array":["u8",32]}},{"name":"max_total_claim","docs":["Maximum number of funds that can ever be claimed from this [MerkleRoot]."],"type":"u64"},{"name":"max_num_nodes","docs":["Maximum number of nodes that can ever be claimed from this [MerkleRoot]."],"type":"u64"},{"name":"total_funds_claimed","docs":["Total funds that have been claimed."],"type":"u64"},{"name":"num_nodes_claimed","docs":["Number of nodes that have been claimed."],"type":"u64"}]}},{"name":"MerkleRootUploadAuthorityUpdatedEvent","type":{"kind":"struct","fields":[{"name":"old_authority","type":"pubkey"},{"name":"new_authority","type":"pubkey"}]}},{"name":"MerkleRootUploadConfig","docs":["Singleton account that allows overriding TDA's merkle upload authority"],"type":{"kind":"struct","fields":[{"name":"override_authority","docs":["The authority that overrides the TipDistributionAccount merkle_root_upload_authority"],"type":"pubkey"},{"name":"original_upload_authority","docs":["The original merkle root upload authority that can be changed to the new overrided","authority. E.g. Jito Labs authority GZctHpWXmsZC1YHACTGGcHhYxjdRqQvTpYkb9LMvxDib"],"type":"pubkey"},{"name":"bump","docs":["The bump used to generate this account"],"type":"u8"}]}},{"name":"MerkleRootUploadedEvent","type":{"kind":"struct","fields":[{"name":"merkle_root_upload_authority","docs":["Who uploaded the root."],"type":"pubkey"},{"name":"tip_distribution_account","docs":["Where the root was uploaded to."],"type":"pubkey"}]}},{"name":"TipDistributionAccount","docs":["The account that validators register as **tip_receiver** with the tip-payment program."],"type":{"kind":"struct","fields":[{"name":"validator_vote_account","docs":["The validator's vote account, also the recipient of remaining lamports after","upon closing this account."],"type":"pubkey"},{"name":"merkle_root_upload_authority","docs":["The only account authorized to upload a merkle-root for this account."],"type":"pubkey"},{"name":"merkle_root","docs":["The merkle root used to verify user claims from this account."],"type":{"option":{"defined":{"name":"MerkleRoot"}}}},{"name":"epoch_created_at","docs":["Epoch for which this account was created."],"type":"u64"},{"name":"validator_commission_bps","docs":["The commission basis points this validator charges."],"type":"u16"},{"name":"expires_at","docs":["The epoch (upto and including) that tip funds can be claimed."],"type":"u64"},{"name":"bump","docs":["The bump used to generate this account"],"type":"u8"}]}},{"name":"TipDistributionAccountClosedEvent","type":{"kind":"struct","fields":[{"name":"expired_funds_account","docs":["Account where unclaimed funds were transferred to."],"type":"pubkey"},{"name":"tip_distribution_account","docs":["[TipDistributionAccount] closed."],"type":"pubkey"},{"name":"expired_amount","docs":["Unclaimed amount transferred."],"type":"u64"}]}},{"name":"TipDistributionAccountInitializedEvent","type":{"kind":"struct","fields":[{"name":"tip_distribution_account","type":"pubkey"}]}},{"name":"ValidatorCommissionBpsUpdatedEvent","type":{"kind":"struct","fields":[{"name":"tip_distribution_account","type":"pubkey"},{"name":"old_commission_bps","type":"u16"},{"name":"new_commission_bps","type":"u16"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/jupiter_aprepro_smart_wallet.JSW99DKmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw.json b/migration/khadhroony-bot2-reference/idls/jupiter_aprepro_smart_wallet.JSW99DKmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw.json new file mode 100644 index 0000000..7cc97ac --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/jupiter_aprepro_smart_wallet.JSW99DKmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"jupiter_smart_wallet","instructions":[{"name":"initialize","accounts":[{"name":"vault","isMut":false,"isSigner":false},{"name":"vaultInfo","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"userSigner","type":{"array":["u8",64]}},{"name":"label","type":"string"}]},{"name":"flashSwapApprove","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"wsolMint","isMut":false,"isSigner":false},{"name":"vaultWsol","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"vaultInfo","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"keeperInputAccount","isMut":true,"isSigner":false},{"name":"inputVault","isMut":true,"isSigner":false},{"name":"outputVault","isMut":true,"isSigner":false,"docs":["CHECKED: in implementation code"]},{"name":"inputTokenProgram","isMut":false,"isSigner":false},{"name":"outputTokenProgram","isMut":false,"isSigner":false},{"name":"instructionsSysvar","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"signature","type":{"array":["u8",64]}},{"name":"recoveryId","type":"u8"},{"name":"priorityFeeLamports","type":"u64"},{"name":"inputAmount","type":"u64"},{"name":"minOut","type":"u64"}]},{"name":"preFlashSwapApprove","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"vaultWsol","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"vaultInfo","isMut":true,"isSigner":false},{"name":"inputMint","isMut":true,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"keeperInputAccount","isMut":false,"isSigner":false},{"name":"referrer","isMut":true,"isSigner":false},{"name":"inputVault","isMut":true,"isSigner":false},{"name":"outputVault","isMut":true,"isSigner":false,"docs":["CHECKED: in implementation code"]},{"name":"inputTokenProgram","isMut":false,"isSigner":false},{"name":"outputTokenProgram","isMut":false,"isSigner":false},{"name":"instructionsSysvar","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"signature","type":{"array":["u8",64]}},{"name":"recoveryId","type":"u8"},{"name":"priorityFeeLamports","type":"u64"},{"name":"inputAmount","type":"u64"},{"name":"minOut","type":"u64"},{"name":"postSwapIxIndex","type":"u8"},{"name":"feeBps","type":"u16"},{"name":"referralShareBps","type":"u16"},{"name":"useDurableNonce","type":"bool"},{"name":"maxOut","type":"u64"}]},{"name":"postSwap","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"wsolMint","isMut":false,"isSigner":false},{"name":"vaultWsol","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"vaultInfo","isMut":true,"isSigner":false},{"name":"referrer","isMut":true,"isSigner":false},{"name":"outputVault","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"withdraw","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"vault","isMut":true,"isSigner":false},{"name":"vaultInfo","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"vaultTokenAccount","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"signature","type":{"array":["u8",64]}},{"name":"recoveryId","type":"u8"},{"name":"amount","type":"u64"}]},{"name":"withdrawToken","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"vault","isMut":true,"isSigner":false},{"name":"vaultInfo","isMut":true,"isSigner":false},{"name":"receiver","isMut":false,"isSigner":false},{"name":"receiverTokenAccount","isMut":true,"isSigner":false},{"name":"vaultTokenAccount","isMut":true,"isSigner":false},{"name":"mint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"signature","type":{"array":["u8",64]}},{"name":"recoveryId","type":"u8"},{"name":"amount","type":"u64"}]}],"accounts":[{"name":"VaultInfo","type":{"kind":"struct","fields":[{"name":"vault","type":"publicKey"},{"name":"bump","type":"u8"},{"name":"createdAt","type":"i64"},{"name":"label","type":"string"},{"name":"transientNonces","type":{"array":["u64",32]}},{"name":"userSigner","type":{"array":["u8",64]}},{"name":"swapInProgress","type":"bool"},{"name":"preSwapAmount","type":"u64"},{"name":"minOut","type":"u64"},{"name":"feeBps","type":"u16"},{"name":"referralShareBps","type":"u16"},{"name":"maxOut","type":"u64"},{"name":"reserved","type":{"array":["u8",31]}},{"name":"durableNonces","type":{"vec":"u64"}}]}}],"types":[{"name":"SwapApproval","type":{"kind":"struct","fields":[{"name":"nonce","type":"u64"},{"name":"inputMint","type":"publicKey"},{"name":"outputMint","type":"publicKey"},{"name":"inputAmount","type":"u64"},{"name":"priorityFeeLamports","type":"u64"},{"name":"useDurableNonce","type":"bool"}]}},{"name":"WithdrawApproval","type":{"kind":"struct","fields":[{"name":"nonce","type":"u64"},{"name":"amount","type":"u64"},{"name":"withdrawalAddress","type":"publicKey"}]}},{"name":"WithdrawTokenApproval","type":{"kind":"struct","fields":[{"name":"nonce","type":"u64"},{"name":"amount","type":"u64"},{"name":"mint","type":"publicKey"},{"name":"withdrawalAddress","type":"publicKey"}]}},{"name":"BurnApproval","type":{"kind":"struct","fields":[{"name":"nonce","type":"u64"},{"name":"mint","type":"publicKey"}]}}],"events":[{"name":"Initialize","fields":[{"name":"vaultKey","type":"publicKey","index":false},{"name":"userKey","type":{"array":["u8",64]},"index":false}]},{"name":"Withdraw","fields":[{"name":"vaultKey","type":"publicKey","index":false},{"name":"receiverKey","type":"publicKey","index":false},{"name":"mint","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]},{"name":"Swap","fields":[{"name":"userKey","type":{"array":["u8",64]},"index":false},{"name":"vaultKey","type":"publicKey","index":false},{"name":"inputMint","type":"publicKey","index":false},{"name":"outputMint","type":"publicKey","index":false},{"name":"inputAmount","type":"u64","index":false},{"name":"baseFeeAmount","type":"u64","index":false}]},{"name":"Refer","fields":[{"name":"vaultKey","type":"publicKey","index":false},{"name":"referrerKey","type":"publicKey","index":false},{"name":"shareAmount","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"MathOverflow"},{"code":6001,"name":"AddressMismatch"},{"code":6002,"name":"Unauthorized"},{"code":6003,"name":"UnauthorizedKeeper"},{"code":6004,"name":"ProgramMismatch"},{"code":6005,"name":"UnexpectedSwapProgram"},{"code":6006,"name":"UnexpectedSwapInstruction"},{"code":6007,"name":"WrongSwapOutAccount"},{"code":6008,"name":"InvalidSwapMint"},{"code":6009,"name":"ExceedSlippageSetting"},{"code":6010,"name":"AmountMismatch"},{"code":6011,"name":"MissingSwapInstructions"},{"code":6012,"name":"InvalidPublicKey"},{"code":6013,"name":"InvalidApproval"},{"code":6014,"name":"ExpiredApproval"},{"code":6015,"name":"ExpiredOrder"},{"code":6016,"name":"InsufficientSOLForFees"},{"code":6017,"name":"InvalidNonce"},{"code":6018,"name":"ViolateMinOut"},{"code":6019,"name":"FeeTooHigh"},{"code":6020,"name":"NonceInvalid"},{"code":6021,"name":"NonceUsed"},{"code":6022,"name":"NonceLimit"},{"code":6023,"name":"UnableToVerify"},{"code":6024,"name":"MissingPreSwapIx"},{"code":6025,"name":"MissingPostSwapIx"},{"code":6026,"name":"InvalidBps"},{"code":6027,"name":"TooLargeFee"},{"code":6028,"name":"InvalidReceiverTokenAccount"},{"code":6029,"name":"Deprecated"},{"code":6030,"name":"ViolateMaxOut"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/jupiter_dca.DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M.json b/migration/khadhroony-bot2-reference/idls/jupiter_dca.DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M.json new file mode 100644 index 0000000..bfcf77a --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/jupiter_dca.DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"dca","instructions":[{"name":"openDca","accounts":[{"name":"dca","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":true},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"userAta","isMut":true,"isSigner":false},{"name":"inAta","isMut":true,"isSigner":false},{"name":"outAta","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"applicationIdx","type":"u64"},{"name":"inAmount","type":"u64"},{"name":"inAmountPerCycle","type":"u64"},{"name":"cycleFrequency","type":"i64"},{"name":"minOutAmount","type":{"option":"u64"}},{"name":"maxOutAmount","type":{"option":"u64"}},{"name":"startAt","type":{"option":"i64"}},{"name":"closeWsolInAta","type":{"option":"bool"}}]},{"name":"openDcaV2","accounts":[{"name":"dca","isMut":true,"isSigner":false},{"name":"user","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"userAta","isMut":true,"isSigner":false},{"name":"inAta","isMut":true,"isSigner":false},{"name":"outAta","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"applicationIdx","type":"u64"},{"name":"inAmount","type":"u64"},{"name":"inAmountPerCycle","type":"u64"},{"name":"cycleFrequency","type":"i64"},{"name":"minOutAmount","type":{"option":"u64"}},{"name":"maxOutAmount","type":{"option":"u64"}},{"name":"startAt","type":{"option":"i64"}}]},{"name":"closeDca","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"dca","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"inAta","isMut":true,"isSigner":false},{"name":"outAta","isMut":true,"isSigner":false},{"name":"userInAta","isMut":true,"isSigner":false},{"name":"userOutAta","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"withdraw","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"dca","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"dcaAta","isMut":true,"isSigner":false},{"name":"userInAta","isMut":true,"isSigner":false,"isOptional":true},{"name":"userOutAta","isMut":true,"isSigner":false,"isOptional":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"withdrawParams","type":{"defined":"WithdrawParams"}}]},{"name":"deposit","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"dca","isMut":true,"isSigner":false},{"name":"inAta","isMut":true,"isSigner":false},{"name":"userInAta","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"depositIn","type":"u64"}]},{"name":"withdrawFees","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"mint","isMut":false,"isSigner":false},{"name":"feeAuthority","isMut":false,"isSigner":false,"docs":["CHECK"]},{"name":"programFeeAta","isMut":true,"isSigner":false},{"name":"adminFeeAta","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"initiateFlashFill","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"dca","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false,"docs":["The token to borrow"]},{"name":"keeperInAta","isMut":true,"isSigner":false,"docs":["The account to send borrowed tokens to"]},{"name":"inAta","isMut":true,"isSigner":false,"docs":["The account to borrow from"]},{"name":"outAta","isMut":false,"isSigner":false,"docs":["The account to repay to"]},{"name":"instructionsSysvar","isMut":false,"isSigner":false,"docs":["Solana Instructions Sysvar"]},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"fulfillFlashFill","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"dca","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"keeperInAta","isMut":false,"isSigner":false},{"name":"inAta","isMut":false,"isSigner":false},{"name":"outAta","isMut":false,"isSigner":false},{"name":"feeAuthority","isMut":false,"isSigner":false,"docs":["CHECK"]},{"name":"feeAta","isMut":true,"isSigner":false},{"name":"instructionsSysvar","isMut":false,"isSigner":false,"docs":["Solana Instructions Sysvar"]},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"repayAmount","type":"u64"}]},{"name":"initiateDlmmFill","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"dca","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false,"docs":["The token to borrow"]},{"name":"keeperInAta","isMut":true,"isSigner":false,"docs":["The account to send borrowed tokens to"]},{"name":"inAta","isMut":true,"isSigner":false,"docs":["The account to borrow from"]},{"name":"outAta","isMut":false,"isSigner":false,"docs":["The account to repay to"]},{"name":"instructionsSysvar","isMut":false,"isSigner":false,"docs":["Solana Instructions Sysvar"]},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"fulfillDlmmFill","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"dca","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"keeperInAta","isMut":false,"isSigner":false},{"name":"inAta","isMut":false,"isSigner":false},{"name":"outAta","isMut":false,"isSigner":false},{"name":"feeAuthority","isMut":false,"isSigner":false,"docs":["CHECK"]},{"name":"feeAta","isMut":true,"isSigner":false},{"name":"instructionsSysvar","isMut":false,"isSigner":false,"docs":["Solana Instructions Sysvar"]},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"repayAmount","type":"u64"}]},{"name":"transfer","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"dca","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"dcaOutAta","isMut":true,"isSigner":false},{"name":"userOutAta","isMut":true,"isSigner":false,"isOptional":true},{"name":"intermediateAccount","isMut":true,"isSigner":false,"isOptional":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"endAndClose","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"dca","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"inAta","isMut":true,"isSigner":false},{"name":"outAta","isMut":true,"isSigner":false},{"name":"user","isMut":true,"isSigner":false},{"name":"userOutAta","isMut":true,"isSigner":false,"isOptional":true},{"name":"initUserOutAta","isMut":true,"isSigner":false,"isOptional":true},{"name":"intermediateAccount","isMut":true,"isSigner":false,"isOptional":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"Dca","type":{"kind":"struct","fields":[{"name":"user","type":"publicKey"},{"name":"inputMint","type":"publicKey"},{"name":"outputMint","type":"publicKey"},{"name":"idx","type":"u64"},{"name":"nextCycleAt","type":"i64"},{"name":"inDeposited","type":"u64"},{"name":"inWithdrawn","type":"u64"},{"name":"outWithdrawn","type":"u64"},{"name":"inUsed","type":"u64"},{"name":"outReceived","type":"u64"},{"name":"inAmountPerCycle","type":"u64"},{"name":"cycleFrequency","type":"i64"},{"name":"nextCycleAmountLeft","type":"u64"},{"name":"inAccount","type":"publicKey"},{"name":"outAccount","type":"publicKey"},{"name":"minOutAmount","type":"u64"},{"name":"maxOutAmount","type":"u64"},{"name":"keeperInBalanceBeforeBorrow","type":"u64"},{"name":"dcaOutBalanceBeforeSwap","type":"u64"},{"name":"createdAt","type":"i64"},{"name":"bump","type":"u8"}]}}],"types":[{"name":"WithdrawParams","type":{"kind":"struct","fields":[{"name":"withdrawAmount","type":"u64"},{"name":"withdrawal","type":{"defined":"Withdrawal"}}]}},{"name":"Withdrawal","type":{"kind":"enum","variants":[{"name":"In"},{"name":"Out"}]}}],"events":[{"name":"CollectedFee","fields":[{"name":"userKey","type":"publicKey","index":false},{"name":"dcaKey","type":"publicKey","index":false},{"name":"mint","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]},{"name":"Filled","fields":[{"name":"userKey","type":"publicKey","index":false},{"name":"dcaKey","type":"publicKey","index":false},{"name":"inputMint","type":"publicKey","index":false},{"name":"outputMint","type":"publicKey","index":false},{"name":"inAmount","type":"u64","index":false},{"name":"outAmount","type":"u64","index":false},{"name":"feeMint","type":"publicKey","index":false},{"name":"fee","type":"u64","index":false}]},{"name":"Opened","fields":[{"name":"userKey","type":"publicKey","index":false},{"name":"dcaKey","type":"publicKey","index":false},{"name":"inDeposited","type":"u64","index":false},{"name":"inputMint","type":"publicKey","index":false},{"name":"outputMint","type":"publicKey","index":false},{"name":"cycleFrequency","type":"i64","index":false},{"name":"inAmountPerCycle","type":"u64","index":false},{"name":"createdAt","type":"i64","index":false}]},{"name":"Closed","fields":[{"name":"userKey","type":"publicKey","index":false},{"name":"dcaKey","type":"publicKey","index":false},{"name":"inDeposited","type":"u64","index":false},{"name":"inputMint","type":"publicKey","index":false},{"name":"outputMint","type":"publicKey","index":false},{"name":"cycleFrequency","type":"i64","index":false},{"name":"inAmountPerCycle","type":"u64","index":false},{"name":"createdAt","type":"i64","index":false},{"name":"totalInWithdrawn","type":"u64","index":false},{"name":"totalOutWithdrawn","type":"u64","index":false},{"name":"unfilledAmount","type":"u64","index":false},{"name":"userClosed","type":"bool","index":false}]},{"name":"Withdraw","fields":[{"name":"dcaKey","type":"publicKey","index":false},{"name":"inAmount","type":"u64","index":false},{"name":"outAmount","type":"u64","index":false},{"name":"userWithdraw","type":"bool","index":false}]},{"name":"Deposit","fields":[{"name":"dcaKey","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"InvalidAmount","msg":"Invalid deposit amount"},{"code":6001,"name":"InvalidCycleAmount","msg":"Invalid deposit amount"},{"code":6002,"name":"InvalidPair","msg":"Invalid pair"},{"code":6003,"name":"TooFrequent","msg":"Too frequent DCA cycle"},{"code":6004,"name":"InvalidMinPrice","msg":"Minimum price constraint must be greater than 0"},{"code":6005,"name":"InvalidMaxPrice","msg":"Maximum price constraint must be greater than 0"},{"code":6006,"name":"InAmountInsufficient","msg":"In amount needs to be more than in amount per cycle"},{"code":6007,"name":"Unauthorized","msg":"Wrong user"},{"code":6008,"name":"NoInATA","msg":"inAta not passed in"},{"code":6009,"name":"NoUserInATA","msg":"userInAta not passed in"},{"code":6010,"name":"NoOutATA","msg":"outAta not passed in"},{"code":6011,"name":"NoUserOutATA","msg":"userOutAta not passed in"},{"code":6012,"name":"InsufficientBalanceInProgram","msg":"Trying to withdraw more than available"},{"code":6013,"name":"InvalidDepositAmount","msg":"Deposit should be more than 0"},{"code":6014,"name":"UserInsufficientBalance","msg":"User has insufficient balance"},{"code":6015,"name":"UnauthorizedKeeper","msg":"Unauthorized Keeper"},{"code":6016,"name":"UnrecognizedProgram","msg":"Unrecognized Program"},{"code":6017,"name":"MathErrors","msg":"Calculation errors"},{"code":6018,"name":"KeeperNotTimeToFill","msg":"Not time to fill"},{"code":6019,"name":"OrderFillAmountWrong","msg":"Order amount wrong"},{"code":6020,"name":"SwapOutAmountBelowMinimum","msg":"Out amount below expectations"},{"code":6021,"name":"WrongAdmin","msg":"Wrong admin"},{"code":6022,"name":"MathOverflow","msg":"Overflow in arithmetic operation"},{"code":6023,"name":"AddressMismatch","msg":"Address Mismatch"},{"code":6024,"name":"ProgramMismatch","msg":"Program Mismatch"},{"code":6025,"name":"IncorrectRepaymentAmount","msg":"Incorrect Repayment Amount"},{"code":6026,"name":"CannotBorrowBeforeRepay","msg":"Cannot Borrow Before Repay"},{"code":6027,"name":"NoRepaymentInstructionFound","msg":"No Repayment Found"},{"code":6028,"name":"MissingSwapInstructions","msg":"Missing Swap Instruction"},{"code":6029,"name":"UnexpectedSwapProgram","msg":"Expected Instruction to use Jupiter Swap Program"},{"code":6030,"name":"InvalidSwapMint","msg":"Invalid Swap Mint"},{"code":6031,"name":"UnknownInstruction","msg":"Unknown Instruction"},{"code":6032,"name":"MissingRepayInstructions","msg":"Missing Repay Instruction"},{"code":6033,"name":"KeeperShortchanged","msg":"Keeper Shortchanged"},{"code":6034,"name":"WrongSwapOutAccount","msg":"Jup Swap to Wrong Out Account"},{"code":6035,"name":"WrongTransferAmount","msg":"Transfer amount should be exactly account balance"},{"code":6036,"name":"InsufficientBalanceForRent","msg":"Insufficient balance for rent"},{"code":6037,"name":"UnexpectedSolBalance","msg":"Unexpected SOL amount in intermediate account"},{"code":6038,"name":"InsufficientWsolForTransfer","msg":"Too little WSOL to perform transfer"},{"code":6039,"name":"MissedInstruction","msg":"Did not call initiate_flash_fill"},{"code":6040,"name":"WrongProgram","msg":"Did not call this program's initiate_flash_fill"},{"code":6041,"name":"BalanceNotZero","msg":"Can't close account with balance"},{"code":6042,"name":"UnexpectedWSOLLeftover","msg":"Should not have WSOL leftover in DCA out-token account"},{"code":6043,"name":"IntermediateAccountNotSet","msg":"Should pass in a WSOL intermediate account when transferring SOL"},{"code":6044,"name":"UnexpectedSwapInstruction","msg":"Did not call jup swap"},{"code":6045,"name":"SwapOutLessThanUserMinimum","msg":"Expect more from swap"},{"code":6046,"name":"SwapOutMoreThanUserMaximum","msg":"Expect less from swap"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/jupiter_limit_order.jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu.json b/migration/khadhroony-bot2-reference/idls/jupiter_limit_order.jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu.json new file mode 100644 index 0000000..1f67468 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/jupiter_limit_order.jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"limit_order","instructions":[{"name":"initializeOrder","accounts":[{"name":"base","isMut":false,"isSigner":true},{"name":"maker","isMut":true,"isSigner":true},{"name":"order","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false,"docs":["CHECK"]},{"name":"makerInputAccount","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"makerOutputAccount","isMut":false,"isSigner":false},{"name":"referral","isMut":false,"isSigner":false,"isOptional":true},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"makingAmount","type":"u64"},{"name":"takingAmount","type":"u64"},{"name":"expiredAt","type":{"option":"i64"}}]},{"name":"fillOrder","accounts":[{"name":"order","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"maker","isMut":true,"isSigner":false},{"name":"taker","isMut":false,"isSigner":true},{"name":"takerOutputAccount","isMut":true,"isSigner":false},{"name":"makerOutputAccount","isMut":true,"isSigner":false},{"name":"takerInputAccount","isMut":true,"isSigner":false},{"name":"feeAuthority","isMut":false,"isSigner":false},{"name":"programFeeAccount","isMut":true,"isSigner":false},{"name":"referral","isMut":true,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"makingAmount","type":"u64"},{"name":"maxTakingAmount","type":"u64"}]},{"name":"preFlashFillOrder","accounts":[{"name":"order","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"taker","isMut":false,"isSigner":true},{"name":"takerOutputAccount","isMut":true,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"inputMintTokenProgram","isMut":false,"isSigner":false},{"name":"instruction","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"makingAmount","type":"u64"}]},{"name":"flashFillOrder","accounts":[{"name":"order","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"maker","isMut":true,"isSigner":false},{"name":"taker","isMut":false,"isSigner":true},{"name":"makerOutputAccount","isMut":true,"isSigner":false},{"name":"takerInputAccount","isMut":true,"isSigner":false},{"name":"feeAuthority","isMut":false,"isSigner":false},{"name":"programFeeAccount","isMut":true,"isSigner":false},{"name":"referral","isMut":true,"isSigner":false,"isOptional":true},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"inputMintTokenProgram","isMut":false,"isSigner":false},{"name":"outputMint","isMut":false,"isSigner":false},{"name":"outputMintTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxTakingAmount","type":"u64"}]},{"name":"cancelOrder","accounts":[{"name":"order","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false,"docs":["CHECK"]},{"name":"maker","isMut":true,"isSigner":true},{"name":"makerInputAccount","isMut":true,"isSigner":false,"docs":["CHECK, it is not important if it is sol input mint"]},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false,"isOptional":true}],"args":[]},{"name":"cancelExpiredOrder","accounts":[{"name":"order","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false,"docs":["CHECK"]},{"name":"maker","isMut":true,"isSigner":false},{"name":"makerInputAccount","isMut":true,"isSigner":false,"docs":["CHECK, it is not important if it is sol input mint"]},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false,"isOptional":true}],"args":[]},{"name":"withdrawFee","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"feeAuthority","isMut":false,"isSigner":false,"docs":["CHECK"]},{"name":"programFeeAccount","isMut":true,"isSigner":false},{"name":"adminTokenAcocunt","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"initFee","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"feeAuthority","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"makerFee","type":"u64"},{"name":"makerStableFee","type":"u64"},{"name":"takerFee","type":"u64"},{"name":"takerStableFee","type":"u64"}]},{"name":"updateFee","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"feeAuthority","isMut":true,"isSigner":false}],"args":[{"name":"makerFee","type":"u64"},{"name":"makerStableFee","type":"u64"},{"name":"takerFee","type":"u64"},{"name":"takerStableFee","type":"u64"}]}],"accounts":[{"name":"Fee","type":{"kind":"struct","fields":[{"name":"makerFee","type":"u64"},{"name":"makerStableFee","type":"u64"},{"name":"takerFee","type":"u64"},{"name":"takerStableFee","type":"u64"}]}},{"name":"Order","type":{"kind":"struct","fields":[{"name":"maker","type":"publicKey"},{"name":"inputMint","type":"publicKey"},{"name":"outputMint","type":"publicKey"},{"name":"waiting","type":"bool"},{"name":"oriMakingAmount","type":"u64"},{"name":"oriTakingAmount","type":"u64"},{"name":"makingAmount","type":"u64"},{"name":"takingAmount","type":"u64"},{"name":"makerInputAccount","type":"publicKey"},{"name":"makerOutputAccount","type":"publicKey"},{"name":"reserve","type":"publicKey"},{"name":"borrowMakingAmount","type":"u64"},{"name":"expiredAt","type":{"option":"i64"}},{"name":"base","type":"publicKey"},{"name":"referral","type":{"option":"publicKey"}}]}}],"events":[{"name":"TradeEvent","fields":[{"name":"orderKey","type":"publicKey","index":false},{"name":"taker","type":"publicKey","index":false},{"name":"remainingInAmount","type":"u64","index":false},{"name":"remainingOutAmount","type":"u64","index":false},{"name":"inAmount","type":"u64","index":false},{"name":"outAmount","type":"u64","index":false}]},{"name":"CancelOrderEvent","fields":[{"name":"orderKey","type":"publicKey","index":false}]},{"name":"CreateOrderEvent","fields":[{"name":"orderKey","type":"publicKey","index":false},{"name":"maker","type":"publicKey","index":false},{"name":"inputMint","type":"publicKey","index":false},{"name":"outputMint","type":"publicKey","index":false},{"name":"inAmount","type":"u64","index":false},{"name":"outAmount","type":"u64","index":false},{"name":"expiredAt","type":{"option":"i64"},"index":false}]}],"errors":[{"code":6000,"name":"InvalidMakingAmount"},{"code":6001,"name":"InvalidTakingAmount"},{"code":6002,"name":"InvalidMaxTakingAmount"},{"code":6003,"name":"InvalidCalculation"},{"code":6004,"name":"InvalidInputAccount"},{"code":6005,"name":"InvalidOutputAccount"},{"code":6006,"name":"InvalidPair"},{"code":6007,"name":"MissingReferral"},{"code":6008,"name":"OrderExpired"},{"code":6009,"name":"OrderNotExpired"},{"code":6010,"name":"InvalidKeeper"},{"code":6011,"name":"MathOverflow"},{"code":6012,"name":"ProgramMismatch"},{"code":6013,"name":"UnknownInstruction"},{"code":6014,"name":"MissingRepayInstructions"},{"code":6015,"name":"InvalidOrder"},{"code":6016,"name":"InvalidBorrowMakingAmount"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/jupiter_limit_order_v2.j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X.json b/migration/khadhroony-bot2-reference/idls/jupiter_limit_order_v2.j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X.json new file mode 100644 index 0000000..4d7c8c5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/jupiter_limit_order_v2.j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X.json @@ -0,0 +1 @@ +{"address":"j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X","metadata":{"name":"limit_order_2","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"cancel_dust_order","discriminator":[197,112,189,164,79,48,23,246],"accounts":[{"name":"signer","signer":true},{"name":"maker","writable":true,"relations":["order"]},{"name":"order","writable":true},{"name":"input_mint_reserve","writable":true},{"name":"maker_input_mint_account","writable":true},{"name":"input_mint"},{"name":"input_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"CancelDustOrderParams"}}}]},{"name":"cancel_order","discriminator":[95,129,237,240,8,49,223,132],"accounts":[{"name":"signer","signer":true},{"name":"maker","writable":true,"relations":["order"]},{"name":"order","writable":true},{"name":"input_mint_reserve","writable":true},{"name":"maker_input_mint_account","writable":true,"optional":true},{"name":"input_mint"},{"name":"input_token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"fill_order","discriminator":[232,122,115,25,199,143,136,162],"accounts":[{"name":"taker","writable":true,"signer":true},{"name":"maker","writable":true,"relations":["order"]},{"name":"order","writable":true},{"name":"taker_input_mint_account","writable":true},{"name":"taker_output_mint_account","writable":true},{"name":"maker_output_mint_account","writable":true,"optional":true},{"name":"fee_account","writable":true},{"name":"order_input_mint_account","writable":true},{"name":"input_mint"},{"name":"input_token_program"},{"name":"output_mint"},{"name":"output_token_program"},{"name":"jupiter_program","address":"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"FillOrderParams"}}}]},{"name":"flash_fill_order","discriminator":[252,104,18,134,164,78,18,140],"accounts":[{"name":"taker","writable":true,"signer":true},{"name":"maker","writable":true,"relations":["order"]},{"name":"order","writable":true},{"name":"input_mint_reserve","writable":true},{"name":"maker_output_mint_account","writable":true,"optional":true},{"name":"taker_output_mint_account","writable":true,"optional":true},{"name":"fee_account","writable":true},{"name":"input_token_program"},{"name":"output_mint"},{"name":"output_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"FlashFillOrderParams"}}}]},{"name":"initialize_order","discriminator":[133,110,74,175,112,159,245,159],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"maker","signer":true},{"name":"order","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,100,101,114]},{"kind":"account","path":"maker"},{"kind":"arg","path":"unique_id"}]}},{"name":"input_mint_reserve","writable":true,"pda":{"seeds":[{"kind":"account","path":"order"},{"kind":"account","path":"input_token_program"},{"kind":"account","path":"input_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"maker_input_mint_account","writable":true},{"name":"fee","pda":{"seeds":[{"kind":"const","value":[102,101,101]}]}},{"name":"referral","optional":true},{"name":"input_mint"},{"name":"output_mint"},{"name":"input_token_program"},{"name":"output_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"InitializeOrderParams"}}}]},{"name":"pre_flash_fill_order","discriminator":[240,47,153,68,13,190,225,42],"accounts":[{"name":"taker","signer":true},{"name":"order","writable":true},{"name":"input_mint_reserve","writable":true},{"name":"taker_input_mint_account","writable":true},{"name":"input_mint"},{"name":"input_token_program"},{"name":"instruction","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"PreFlashFillOrderParams"}}}]},{"name":"update_fee","discriminator":[232,253,195,247,148,212,73,222],"accounts":[{"name":"admin","writable":true,"signer":true},{"name":"fee_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"UpdateFeeParams"}}}]},{"name":"withdraw_fee","discriminator":[14,122,231,218,31,238,223,150],"accounts":[{"name":"admin","writable":true,"signer":true},{"name":"fee_authority","docs":["CHECK"],"pda":{"seeds":[{"kind":"const","value":[102,101,101]}]}},{"name":"program_fee_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"token_program"},{"name":"mint"}],"args":[]}],"accounts":[{"name":"Fee","discriminator":[24,55,150,250,168,27,101,178]},{"name":"Order","discriminator":[134,173,223,185,77,86,28,51]}],"events":[{"name":"CancelDustOrderEvent","discriminator":[124,190,74,28,177,40,200,220]},{"name":"CancelOrderEvent","discriminator":[174,66,141,17,4,224,162,77]},{"name":"CreateOrderEvent","discriminator":[49,142,72,166,230,29,84,84]},{"name":"TradeEvent","discriminator":[189,219,127,211,78,230,97,238]}],"errors":[{"code":6000,"name":"InvalidMakingAmount"},{"code":6001,"name":"InvalidTakingAmount"},{"code":6002,"name":"InvalidMaxTakingAmount"},{"code":6003,"name":"InvalidInputAccount"},{"code":6004,"name":"InvalidMakerOutputAccount"},{"code":6005,"name":"InvalidTakerOutputAccount"},{"code":6006,"name":"InvalidPair"},{"code":6007,"name":"OrderExpired"},{"code":6008,"name":"OrderNotExpired"},{"code":6009,"name":"InvalidAdmin"},{"code":6010,"name":"InvalidKeeper"},{"code":6011,"name":"MathOverflow"},{"code":6012,"name":"ProgramMismatch"},{"code":6013,"name":"UnknownInstruction"},{"code":6014,"name":"MissingRepayInstructions"},{"code":6015,"name":"InvalidOrder"},{"code":6016,"name":"InvalidBorrowMakingAmount"},{"code":6017,"name":"InvalidExpiredDate"},{"code":6018,"name":"FeeBpsTooLow"},{"code":6019,"name":"FeeBpsTooHigh"},{"code":6020,"name":"TransferTaxMint"},{"code":6021,"name":"InvalidOutputMint"},{"code":6022,"name":"InvalidMinTakingAmount"},{"code":6023,"name":"InvalidSwapData"},{"code":6024,"name":"InvalidSwapInputAmount"},{"code":6025,"name":"InvalidSwappedAmount"},{"code":6026,"name":"InvalidDustOrder"},{"code":6027,"name":"UnsupportedMint"}],"types":[{"name":"CancelDustOrderEvent","type":{"kind":"struct","fields":[{"name":"order_key","type":"pubkey"},{"name":"one_usd_input_mint_amount","type":"u64"}]}},{"name":"CancelDustOrderParams","type":{"kind":"struct","fields":[{"name":"one_usd_input_mint_amount","type":"u64"}]}},{"name":"CancelOrderEvent","type":{"kind":"struct","fields":[{"name":"order_key","type":"pubkey"}]}},{"name":"CreateOrderEvent","type":{"kind":"struct","fields":[{"name":"order_key","type":"pubkey"},{"name":"maker","type":"pubkey"},{"name":"input_mint","type":"pubkey"},{"name":"output_mint","type":"pubkey"},{"name":"input_token_program","type":"pubkey"},{"name":"output_token_program","type":"pubkey"},{"name":"making_amount","type":"u64"},{"name":"taking_amount","type":"u64"},{"name":"expired_at","type":{"option":"i64"}},{"name":"fee_bps","type":"u16"},{"name":"fee_account","type":"pubkey"},{"name":"slippage_bps","type":"u16"}]}},{"name":"Fee","type":{"kind":"struct","fields":[{"name":"normal_fee_bps","type":"u16"},{"name":"stable_fee_bps","type":"u16"}]}},{"name":"FillOrderParams","type":{"kind":"struct","fields":[{"name":"input_amount","type":"u64"},{"name":"swap_data","type":"bytes"}]}},{"name":"FlashFillOrderParams","type":{"kind":"struct","fields":[{"name":"max_taking_amount","type":"u64"}]}},{"name":"InitializeOrderParams","type":{"kind":"struct","fields":[{"name":"unique_id","type":"u64"},{"name":"making_amount","type":"u64"},{"name":"taking_amount","type":"u64"},{"name":"expired_at","type":{"option":"i64"}},{"name":"fee_bps","type":{"option":"u16"}},{"name":"slippage_bps","type":{"option":"u16"}}]}},{"name":"Order","type":{"kind":"struct","fields":[{"name":"maker","type":"pubkey"},{"name":"input_mint","type":"pubkey"},{"name":"output_mint","type":"pubkey"},{"name":"input_token_program","type":"pubkey"},{"name":"output_token_program","type":"pubkey"},{"name":"input_mint_reserve","type":"pubkey"},{"name":"unique_id","type":"u64"},{"name":"ori_making_amount","type":"u64"},{"name":"ori_taking_amount","type":"u64"},{"name":"making_amount","type":"u64"},{"name":"taking_amount","type":"u64"},{"name":"borrow_making_amount","type":"u64"},{"name":"expired_at","type":{"option":"i64"}},{"name":"fee_bps","type":"u16"},{"name":"fee_account","type":"pubkey"},{"name":"created_at","type":"i64"},{"name":"updated_at","type":"i64"},{"name":"bump","type":"u8"},{"name":"slippage_bps","type":"u16"}]}},{"name":"PreFlashFillOrderParams","type":{"kind":"struct","fields":[{"name":"making_amount","type":"u64"}]}},{"name":"TradeEvent","type":{"kind":"struct","fields":[{"name":"order_key","type":"pubkey"},{"name":"taker","type":"pubkey"},{"name":"remaining_making_amount","type":"u64"},{"name":"remaining_taking_amount","type":"u64"},{"name":"making_amount","type":"u64"},{"name":"taking_amount","type":"u64"}]}},{"name":"UpdateFeeParams","type":{"kind":"struct","fields":[{"name":"normal_fee_bps","type":"u16"},{"name":"stable_fee_bps","type":"u16"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/jupiter_locker.LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn.json b/migration/khadhroony-bot2-reference/idls/jupiter_locker.LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn.json new file mode 100644 index 0000000..fbbf423 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/jupiter_locker.LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn.json @@ -0,0 +1 @@ +{"address":"LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn","metadata":{"name":"locker","version":"0.4.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"cancel_vesting_escrow","docs":["Cancel a vesting escrow.","- The claimable token will be transferred to recipient","- The remaining token will be transferred to the creator","This instruction supports both splToken and token2022","# Arguments","","* ctx - The accounts needed by instruction.","* remaining_accounts_info: additional accounts needed by instruction",""],"discriminator":[217,233,13,3,143,101,53,201],"accounts":[{"name":"escrow","docs":["Escrow."],"writable":true},{"name":"token_mint","docs":["Mint."],"writable":true,"relations":["escrow"]},{"name":"escrow_token","docs":["Escrow Token Account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"escrow"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"creator_token","docs":["Creator Token Account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"escrow"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"recipient_token","docs":["Receipient Token Account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"escrow"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"rent_receiver","docs":["CHECKED: The Token Account will receive the rent"],"writable":true},{"name":"signer","docs":["Signer."],"writable":true,"signer":true},{"name":"memo_program","docs":["Memo program."],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"token_program","docs":["Token program."]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"claim","docs":["Claim maximum amount from the vesting escrow","# Arguments","","* ctx - The accounts needed by instruction.","* max_amount - The maximum amount claimed by the recipient",""],"discriminator":[62,198,214,193,213,159,108,210],"accounts":[{"name":"escrow","docs":["Escrow."],"writable":true},{"name":"escrow_token","docs":["Escrow Token Account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"escrow"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"escrow"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"recipient","docs":["Recipient."],"writable":true,"signer":true,"relations":["escrow"]},{"name":"recipient_token","docs":["Recipient Token Account."],"writable":true},{"name":"token_program","docs":["Token program."],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"max_amount","type":"u64"}]},{"name":"claim_v2","docs":["Claim maximum amount from the vesting escrow","This instruction supports both splToken and token2022","# Arguments","","* ctx - The accounts needed by instruction.","* max_amount - The maximum amount claimed by the recipient","* remaining_accounts_info: additional accounts needed by instruction",""],"discriminator":[229,87,46,162,21,157,231,114],"accounts":[{"name":"escrow","docs":["Escrow."],"writable":true},{"name":"token_mint","docs":["Mint."],"relations":["escrow"]},{"name":"escrow_token","docs":["Escrow Token Account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"escrow"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"recipient","docs":["Recipient."],"writable":true,"signer":true,"relations":["escrow"]},{"name":"recipient_token","docs":["Recipient Token Account."],"writable":true},{"name":"memo_program","docs":["Memo program."],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"token_program","docs":["Token program."]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"max_amount","type":"u64"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"close_vesting_escrow","docs":["Close vesting escrow","- Close vesting escrow and escrow ATA and escrow metadata if recipient already claimed all tokens","- Rent receiver must be escrow's creator","This instruction supports both splToken and token2022","# Arguments","","* ctx - The accounts needed by instruction.","* remaining_accounts_info: additional accounts needed by instruction",""],"discriminator":[221,185,95,135,136,67,252,87],"accounts":[{"name":"escrow","docs":["Escrow."],"writable":true},{"name":"escrow_metadata","writable":true,"pda":{"seeds":[{"kind":"const","value":[101,115,99,114,111,119,95,109,101,116,97,100,97,116,97]},{"kind":"account","path":"escrow"}]}},{"name":"token_mint","docs":["Mint."],"writable":true},{"name":"escrow_token","writable":true},{"name":"creator_token","writable":true},{"name":"creator","docs":["Creator."],"writable":true,"signer":true,"relations":["escrow"]},{"name":"token_program","docs":["Token program."]},{"name":"memo_program","docs":["Memo program."],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"create_root_escrow","docs":["Create root escrow"],"discriminator":[116,212,12,188,77,226,32,201],"accounts":[{"name":"base","signer":true},{"name":"root_escrow","docs":["Root Escrow."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[114,111,111,116,95,101,115,99,114,111,119]},{"kind":"account","path":"base"},{"kind":"account","path":"token_mint"},{"kind":"arg","path":"params.version"}]}},{"name":"token_mint"},{"name":"payer","docs":["payer."],"writable":true,"signer":true},{"name":"creator"},{"name":"system_program","docs":["system program."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"CreateRootEscrowParameters"}}}]},{"name":"create_vesting_escrow","docs":["Create a vesting escrow for the given params","# Arguments","","* ctx - The accounts needed by instruction.","* params - The params needed by instruction.","* vesting_start_time - The creation time of this escrow","* cliff_time - Trade cliff time of the escrow","* frequency - How frequent the claimable amount will be updated","* cliff_unlock_amount - The amount unlocked after cliff time","* amount_per_period - The amount unlocked per vesting period","* number_of_period - The total number of vesting period","* update_recipient_mode - Decide who can update the recipient of the escrow","* cancel_mode - Decide who can cancel the the escrow",""],"discriminator":[23,100,197,94,222,153,38,90],"accounts":[{"name":"base","docs":["Base."],"writable":true,"signer":true},{"name":"escrow","docs":["Escrow."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[101,115,99,114,111,119]},{"kind":"account","path":"base"}]}},{"name":"escrow_token","docs":["Escrow Token Account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"escrow"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"sender_token.mint","account":"TokenAccount"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"sender","docs":["Sender."],"writable":true,"signer":true},{"name":"sender_token","docs":["Sender Token Account."],"writable":true},{"name":"recipient"},{"name":"token_program","docs":["Token program."],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","docs":["system program."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"CreateVestingEscrowParameters"}}}]},{"name":"create_vesting_escrow_from_root","docs":["Crate vesting escrow from root"],"discriminator":[6,238,161,108,252,114,246,91],"accounts":[{"name":"root_escrow","docs":["Root Escrow."],"writable":true},{"name":"base","pda":{"seeds":[{"kind":"const","value":[98,97,115,101]},{"kind":"account","path":"root_escrow"},{"kind":"account","path":"recipient"}]}},{"name":"escrow","docs":["Escrow."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[101,115,99,114,111,119]},{"kind":"account","path":"base"}]}},{"name":"escrow_token","docs":["Escrow Token Account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"escrow"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"root_escrow_token","writable":true,"pda":{"seeds":[{"kind":"account","path":"root_escrow"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_mint","docs":["Mint."],"relations":["root_escrow"]},{"name":"payer","docs":["Rent Payer"],"writable":true,"signer":true},{"name":"recipient"},{"name":"system_program","docs":["system program."],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Token program."]},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"CreateVestingEscrowFromRootParams"}}},{"name":"proof","type":{"vec":{"array":["u8",32]}}},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"create_vesting_escrow_metadata","docs":["Create vesting escrow metadata","# Arguments","","* ctx - The accounts needed by instruction.","* params - The params needed by instruction.","* name - The name of the vesting escrow","* description - The description of the vesting escrow","* creator_email - The email of the creator","* recipient_email - The email of the recipient",""],"discriminator":[93,78,33,103,173,125,70,0],"accounts":[{"name":"escrow","docs":["The [Escrow]."],"writable":true},{"name":"creator","docs":["Creator of the escrow."],"signer":true,"relations":["escrow"]},{"name":"escrow_metadata","docs":["The [ProposalMeta]."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[101,115,99,114,111,119,95,109,101,116,97,100,97,116,97]},{"kind":"account","path":"escrow"}]}},{"name":"payer","docs":["Payer of the [ProposalMeta]."],"writable":true,"signer":true},{"name":"system_program","docs":["system program."],"address":"11111111111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"CreateVestingEscrowMetadataParameters"}}}]},{"name":"create_vesting_escrow_v2","docs":["Create a vesting escrow for the given params","This instruction supports both splToken and token2022","# Arguments","","* ctx - The accounts needed by instruction.","* params - The params needed by instruction.","* vesting_start_time - The creation time of this escrow","* cliff_time - Trade cliff time of the escrow","* frequency - How frequent the claimable amount will be updated","* cliff_unlock_amount - The amount unlocked after cliff time","* amount_per_period - The amount unlocked per vesting period","* number_of_period - The total number of vesting period","* update_recipient_mode - Decide who can update the recipient of the escrow","* cancel_mode - Decide who can cancel the the escrow","* remaining_accounts_info: additional accounts needed by instruction",""],"discriminator":[181,155,104,183,182,128,35,47],"accounts":[{"name":"base","docs":["Base."],"writable":true,"signer":true},{"name":"escrow","docs":["Escrow."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[101,115,99,114,111,119]},{"kind":"account","path":"base"}]}},{"name":"token_mint"},{"name":"escrow_token","docs":["Escrow Token Account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"escrow"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"sender","docs":["Sender."],"writable":true,"signer":true},{"name":"sender_token","docs":["Sender Token Account."],"writable":true},{"name":"recipient"},{"name":"token_program","docs":["Token program."]},{"name":"system_program","docs":["system program."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"CreateVestingEscrowParameters"}}},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"fund_root_escrow","docs":["Fund root escrow"],"discriminator":[251,106,189,200,108,15,144,95],"accounts":[{"name":"root_escrow","docs":["Root Escrow."],"writable":true},{"name":"token_mint","relations":["root_escrow"]},{"name":"root_escrow_token","docs":["Escrow Token Account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"root_escrow"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"payer","docs":["Payer."],"writable":true,"signer":true},{"name":"payer_token","docs":["Payer Token Account."],"writable":true},{"name":"token_program","docs":["Token program."]},{"name":"system_program","docs":["system program."],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"max_amount","type":"u64"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"update_vesting_escrow_recipient","docs":["Update vesting escrow metadata","# Arguments","","* ctx - The accounts needed by instruction.","* new_recipient - The address of the new recipient","* new_recipient_email - The email of the new recipient",""],"discriminator":[26,242,127,255,237,109,47,206],"accounts":[{"name":"escrow","docs":["Escrow."],"writable":true},{"name":"escrow_metadata","docs":["Escrow metadata."],"writable":true,"optional":true},{"name":"signer","docs":["Signer."],"writable":true,"signer":true},{"name":"system_program","docs":["system program."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_recipient","type":"pubkey"},{"name":"new_recipient_email","type":{"option":"string"}}]}],"accounts":[{"name":"RootEscrow","discriminator":[253,209,220,107,206,191,71,158]},{"name":"VestingEscrow","discriminator":[244,119,183,4,73,116,135,195]},{"name":"VestingEscrowMetadata","discriminator":[24,204,166,104,87,158,76,13]}],"events":[{"name":"EventCancelVestingEscrow","discriminator":[113,2,117,173,195,39,101,155]},{"name":"EventCancelVestingEscrowV3","discriminator":[41,143,236,79,116,120,91,143]},{"name":"EventClaim","discriminator":[171,144,1,189,120,200,38,11]},{"name":"EventClaimV3","discriminator":[229,197,142,10,41,122,171,154]},{"name":"EventCloseClaimStatus","discriminator":[87,68,38,194,241,155,125,107]},{"name":"EventCloseVestingEscrow","discriminator":[45,141,253,209,196,133,21,204]},{"name":"EventCreateRootEscrow","discriminator":[105,216,97,182,27,224,199,228]},{"name":"EventCreateVestingEscrow","discriminator":[248,222,89,61,170,208,131,117]},{"name":"EventFundRootEscrow","discriminator":[74,8,68,181,198,235,138,81]},{"name":"EventUpdateVestingEscrowRecipient","discriminator":[206,218,33,65,133,237,131,57]}],"errors":[{"code":6000,"name":"MathOverflow","msg":"Math operation overflow"},{"code":6001,"name":"FrequencyIsZero","msg":"Frequency is zero"},{"code":6002,"name":"InvalidEscrowTokenAddress","msg":"Invalid escrow token address"},{"code":6003,"name":"InvalidUpdateRecipientMode","msg":"Invalid update recipient mode"},{"code":6004,"name":"InvalidCancelMode","msg":"Invalid cancel mode"},{"code":6005,"name":"NotPermitToDoThisAction","msg":"Not permit to do this action"},{"code":6006,"name":"InvalidRecipientTokenAccount","msg":"Invalid recipient token account"},{"code":6007,"name":"InvalidCreatorTokenAccount","msg":"Invalid creator token account"},{"code":6008,"name":"InvalidEscrowMetadata","msg":"Invalid escrow metadata"},{"code":6009,"name":"InvalidVestingStartTime","msg":"Invalid vesting start time"},{"code":6010,"name":"AlreadyCancelled","msg":"Already cancelled"},{"code":6011,"name":"CancelledAtIsZero","msg":"Cancelled timestamp is zero"},{"code":6012,"name":"IncorrectTokenProgramId","msg":"Invalid token program ID"},{"code":6013,"name":"TransferFeeCalculationFailure","msg":"Calculate transfer fee failure"},{"code":6014,"name":"UnsupportedMint","msg":"Unsupported mint"},{"code":6015,"name":"InvalidRemainingAccountSlice","msg":"Invalid remaining accounts"},{"code":6016,"name":"InsufficientRemainingAccounts","msg":"Insufficient remaining accounts"},{"code":6017,"name":"DuplicatedRemainingAccountTypes","msg":"Same accounts type is provided more than once"},{"code":6018,"name":"NoTransferHookProgram","msg":"Missing remaining accounts for transfer hook."},{"code":6019,"name":"ClaimingIsNotFinished","msg":"Claiming is not finished"},{"code":6020,"name":"InvalidMerkleProof","msg":"Invalid merkle proof"},{"code":6021,"name":"EscrowNotCancelled","msg":"Escrow is not cancelled"},{"code":6022,"name":"AmountIsZero","msg":"Amount is zero"},{"code":6023,"name":"InvalidParams","msg":"Invalid params"}],"types":[{"name":"AccountsType","type":{"kind":"enum","variants":[{"name":"TransferHookEscrow"}]}},{"name":"CreateRootEscrowParameters","type":{"kind":"struct","fields":[{"name":"max_claim_amount","type":"u64"},{"name":"max_escrow","type":"u64"},{"name":"version","type":"u64"},{"name":"root","type":{"array":["u8",32]}}]}},{"name":"CreateVestingEscrowFromRootParams","type":{"kind":"struct","fields":[{"name":"vesting_start_time","type":"u64"},{"name":"cliff_time","type":"u64"},{"name":"frequency","type":"u64"},{"name":"cliff_unlock_amount","type":"u64"},{"name":"amount_per_period","type":"u64"},{"name":"number_of_period","type":"u64"},{"name":"update_recipient_mode","type":"u8"},{"name":"cancel_mode","type":"u8"}]}},{"name":"CreateVestingEscrowMetadataParameters","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"description","type":"string"},{"name":"creator_email","type":"string"},{"name":"recipient_email","type":"string"}]}},{"name":"CreateVestingEscrowParameters","docs":["Accounts for [locker::create_vesting_escrow]."],"type":{"kind":"struct","fields":[{"name":"vesting_start_time","type":"u64"},{"name":"cliff_time","type":"u64"},{"name":"frequency","type":"u64"},{"name":"cliff_unlock_amount","type":"u64"},{"name":"amount_per_period","type":"u64"},{"name":"number_of_period","type":"u64"},{"name":"update_recipient_mode","type":"u8"},{"name":"cancel_mode","type":"u8"}]}},{"name":"EventCancelVestingEscrow","type":{"kind":"struct","fields":[{"name":"escrow","type":"pubkey"},{"name":"signer","type":"pubkey"},{"name":"claimable_amount","type":"u64"},{"name":"remaining_amount","type":"u64"},{"name":"cancelled_at","type":"u64"}]}},{"name":"EventCancelVestingEscrowV3","type":{"kind":"struct","fields":[{"name":"escrow","type":"pubkey"},{"name":"signer","type":"pubkey"},{"name":"remaining_amount","type":"u64"},{"name":"cancelled_at","type":"u64"}]}},{"name":"EventClaim","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"current_ts","type":"u64"},{"name":"escrow","type":"pubkey"}]}},{"name":"EventClaimV3","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"current_ts","type":"u64"},{"name":"escrow","type":"pubkey"},{"name":"vesting_start_time","type":"u64"},{"name":"cliff_time","type":"u64"},{"name":"frequency","type":"u64"},{"name":"cliff_unlock_amount","type":"u64"},{"name":"amount_per_period","type":"u64"},{"name":"number_of_period","type":"u64"},{"name":"recipient","type":"pubkey"}]}},{"name":"EventCloseClaimStatus","type":{"kind":"struct","fields":[{"name":"escrow","type":"pubkey"},{"name":"recipient","type":"pubkey"},{"name":"rent_receiver","type":"pubkey"}]}},{"name":"EventCloseVestingEscrow","type":{"kind":"struct","fields":[{"name":"escrow","type":"pubkey"}]}},{"name":"EventCreateRootEscrow","type":{"kind":"struct","fields":[{"name":"root_escrow","type":"pubkey"},{"name":"max_claim_amount","type":"u64"},{"name":"max_escrow","type":"u64"},{"name":"version","type":"u64"},{"name":"root","type":{"array":["u8",32]}}]}},{"name":"EventCreateVestingEscrow","type":{"kind":"struct","fields":[{"name":"vesting_start_time","type":"u64"},{"name":"cliff_time","type":"u64"},{"name":"frequency","type":"u64"},{"name":"cliff_unlock_amount","type":"u64"},{"name":"amount_per_period","type":"u64"},{"name":"number_of_period","type":"u64"},{"name":"update_recipient_mode","type":"u8"},{"name":"cancel_mode","type":"u8"},{"name":"recipient","type":"pubkey"},{"name":"escrow","type":"pubkey"}]}},{"name":"EventFundRootEscrow","type":{"kind":"struct","fields":[{"name":"root_escrow","type":"pubkey"},{"name":"funded_amount","type":"u64"}]}},{"name":"EventUpdateVestingEscrowRecipient","type":{"kind":"struct","fields":[{"name":"escrow","type":"pubkey"},{"name":"old_recipient","type":"pubkey"},{"name":"new_recipient","type":"pubkey"},{"name":"signer","type":"pubkey"}]}},{"name":"RemainingAccountsInfo","type":{"kind":"struct","fields":[{"name":"slices","type":{"vec":{"defined":{"name":"RemainingAccountsSlice"}}}}]}},{"name":"RemainingAccountsSlice","type":{"kind":"struct","fields":[{"name":"accounts_type","type":{"defined":{"name":"AccountsType"}}},{"name":"length","type":"u8"}]}},{"name":"RootEscrow","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"token_mint","docs":["token mint"],"type":"pubkey"},{"name":"creator","docs":["creator of the escrow"],"type":"pubkey"},{"name":"base","docs":["escrow base key"],"type":"pubkey"},{"name":"root","docs":["256 bit merkle root"],"type":{"array":["u8",32]}},{"name":"bump","docs":["bump"],"type":"u8"},{"name":"token_program_flag","docs":["token program flag"],"type":"u8"},{"name":"padding_0","docs":["padding"],"type":{"array":["u8",6]}},{"name":"max_claim_amount","docs":["max claim amount"],"type":"u64"},{"name":"max_escrow","docs":["max escrow"],"type":"u64"},{"name":"total_funded_amount","docs":["total funded amount"],"type":"u64"},{"name":"total_escrow_created","docs":["total escrow created"],"type":"u64"},{"name":"total_distribute_amount","docs":["total distributed amount"],"type":"u64"},{"name":"version","docs":["version"],"type":"u64"},{"name":"padding","docs":["padding"],"type":"u64"},{"name":"buffer","docs":["buffer"],"type":{"array":["u128",5]}}]}},{"name":"VestingEscrow","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"recipient","docs":["recipient address"],"type":"pubkey"},{"name":"token_mint","docs":["token mint"],"type":"pubkey"},{"name":"creator","docs":["creator of the escrow"],"type":"pubkey"},{"name":"base","docs":["escrow base key"],"type":"pubkey"},{"name":"escrow_bump","docs":["escrow bump"],"type":"u8"},{"name":"update_recipient_mode","docs":["update_recipient_mode"],"type":"u8"},{"name":"cancel_mode","docs":["cancel_mode"],"type":"u8"},{"name":"token_program_flag","docs":["token program flag"],"type":"u8"},{"name":"padding_0","docs":["padding"],"type":{"array":["u8",4]}},{"name":"cliff_time","docs":["cliff time"],"type":"u64"},{"name":"frequency","docs":["frequency"],"type":"u64"},{"name":"cliff_unlock_amount","docs":["cliff unlock amount"],"type":"u64"},{"name":"amount_per_period","docs":["amount per period"],"type":"u64"},{"name":"number_of_period","docs":["number of period"],"type":"u64"},{"name":"total_claimed_amount","docs":["total claimed amount"],"type":"u64"},{"name":"vesting_start_time","docs":["vesting start time"],"type":"u64"},{"name":"cancelled_at","docs":["cancelled_at"],"type":"u64"},{"name":"padding_1","docs":["buffer"],"type":"u64"},{"name":"buffer","docs":["buffer"],"type":{"array":["u128",5]}}]}},{"name":"VestingEscrowMetadata","docs":["Metadata about an escrow."],"type":{"kind":"struct","fields":[{"name":"escrow","docs":["The [Escrow]."],"type":"pubkey"},{"name":"name","docs":["Name of escrow."],"type":"string"},{"name":"description","docs":["Description of escrow."],"type":"string"},{"name":"creator_email","docs":["Email of creator"],"type":"string"},{"name":"recipient_email","docs":["Email of recipient"],"type":"string"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/jupiter_perpetuals.PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu.json b/migration/khadhroony-bot2-reference/idls/jupiter_perpetuals.PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu.json new file mode 100644 index 0000000..f6b69b0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/jupiter_perpetuals.PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"perpetuals","instructions":[{"name":"init","accounts":[{"name":"upgradeAuthority","isMut":true,"isSigner":true},{"name":"admin","isMut":false,"isSigner":false},{"name":"transferAuthority","isMut":true,"isSigner":false},{"name":"perpetuals","isMut":true,"isSigner":false},{"name":"perpetualsProgram","isMut":false,"isSigner":false},{"name":"perpetualsProgramData","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitParams"}}]},{"name":"addPool","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":true,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"AddPoolParams"}}]},{"name":"addCustody","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"custodyTokenMint","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"AddCustodyParams"}}]},{"name":"setCustodyConfig","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetCustodyConfigParams"}}]},{"name":"setPoolConfig","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetPoolConfigParams"}}]},{"name":"setPerpetualsConfig","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetPerpetualsConfigParams"}}]},{"name":"transferAdmin","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"newAdmin","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"TransferAdminParams"}}]},{"name":"withdrawFees2","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"receivingTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"WithdrawFees2Params"}}]},{"name":"createTokenMetadata","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":false,"isSigner":false},{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"CreateTokenMetadataParams"}}]},{"name":"createTokenLedger","accounts":[{"name":"tokenLedger","isMut":true,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"reallocCustody","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"custody","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"reallocPool","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"pool","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"createAndDelegateStakeAccount","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeInfo","isMut":true,"isSigner":false},{"name":"validatorVoteAccount","isMut":false,"isSigner":false},{"name":"stakeConfig","isMut":false,"isSigner":false},{"name":"wsolMint","isMut":false,"isSigner":false},{"name":"tempWsolAccount","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"stakeHistory","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"CreateAndDelegateStakeAccountParams"}}]},{"name":"unstake","accounts":[{"name":"operator","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeInfo","isMut":true,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"withdrawStake","accounts":[{"name":"keeper","isMut":true,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeInfo","isMut":true,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"stakeHistory","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"redeemStake","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":false,"isSigner":false},{"name":"stakeInfo","isMut":true,"isSigner":false}],"args":[]},{"name":"operatorSetCustodyConfig","accounts":[{"name":"operator","isMut":false,"isSigner":true},{"name":"custody","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"OperatorSetCustodyConfigParams"}}]},{"name":"operatorSetPoolConfig","accounts":[{"name":"operator","isMut":false,"isSigner":true},{"name":"pool","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"OperatorSetPoolConfigParams"}}]},{"name":"testInit","accounts":[{"name":"upgradeAuthority","isMut":true,"isSigner":true},{"name":"admin","isMut":false,"isSigner":false},{"name":"transferAuthority","isMut":true,"isSigner":false},{"name":"perpetuals","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"TestInitParams"}}]},{"name":"setTestTime","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetTestTimeParams"}}]},{"name":"setTokenLedger","accounts":[{"name":"tokenLedger","isMut":true,"isSigner":false},{"name":"tokenAccount","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"swap2","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"fundingAccount","isMut":true,"isSigner":false},{"name":"receivingAccount","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"receivingCustody","isMut":true,"isSigner":false},{"name":"receivingCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"receivingCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"receivingCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"dispensingCustody","isMut":true,"isSigner":false},{"name":"dispensingCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"dispensingCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"dispensingCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"Swap2Params"}}]},{"name":"swapWithTokenLedger","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"fundingAccount","isMut":true,"isSigner":false},{"name":"receivingAccount","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"receivingCustody","isMut":true,"isSigner":false},{"name":"receivingCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"receivingCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"dispensingCustody","isMut":true,"isSigner":false},{"name":"dispensingCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"dispensingCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenLedger","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instruction","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SwapWithTokenLedgerParams"}}]},{"name":"instantIncreasePositionPreSwap","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"fundingAccount","isMut":true,"isSigner":false},{"name":"receivingAccount","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"receivingCustody","isMut":true,"isSigner":false},{"name":"receivingCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"receivingCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"dispensingCustody","isMut":true,"isSigner":false},{"name":"dispensingCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"dispensingCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instruction","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InstantIncreasePositionPreSwapParams"}}]},{"name":"addLiquidity2","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"fundingAccount","isMut":true,"isSigner":false},{"name":"lpTokenAccount","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"AddLiquidity2Params"}}]},{"name":"removeLiquidity2","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"receivingAccount","isMut":true,"isSigner":false},{"name":"lpTokenAccount","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"RemoveLiquidity2Params"}}]},{"name":"createIncreasePositionMarketRequest","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"fundingAccount","isMut":true,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":false,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"referral","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"CreateIncreasePositionMarketRequestParams"}}]},{"name":"createDecreasePositionRequest2","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"receivingAccount","isMut":true,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":false,"isSigner":false},{"name":"desiredMint","isMut":false,"isSigner":false},{"name":"referral","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"CreateDecreasePositionRequest2Params"}}]},{"name":"createDecreasePositionMarketRequest","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"receivingAccount","isMut":true,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":false,"isSigner":false},{"name":"desiredMint","isMut":false,"isSigner":false},{"name":"referral","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"CreateDecreasePositionMarketRequestParams"}}]},{"name":"updateDecreasePositionRequest2","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"UpdateDecreasePositionRequest2Params"}}]},{"name":"closePositionRequest2","accounts":[{"name":"keeper","isMut":true,"isSigner":true,"isOptional":true},{"name":"owner","isMut":true,"isSigner":false},{"name":"ownerAta","isMut":true,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"closePositionRequest3","accounts":[{"name":"keeper","isMut":true,"isSigner":true,"isOptional":true},{"name":"owner","isMut":true,"isSigner":false},{"name":"ownerAta","isMut":true,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"increasePosition4","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"IncreasePosition4Params"}}]},{"name":"increasePositionPreSwap","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"keeperAta","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":false,"isSigner":false},{"name":"instruction","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"IncreasePositionPreSwapParams"}}]},{"name":"increasePositionWithInternalSwap","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"receivingCustody","isMut":true,"isSigner":false},{"name":"receivingCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"receivingCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"receivingCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"IncreasePositionWithInternalSwapParams"}}]},{"name":"decreasePosition4","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"owner","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"DecreasePosition4Params"}}]},{"name":"decreasePositionWithInternalSwap","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"owner","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"dispensingCustody","isMut":true,"isSigner":false},{"name":"dispensingCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"dispensingCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"dispensingCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"DecreasePositionWithInternalSwapParams"}}]},{"name":"decreasePositionWithTpsl","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"owner","isMut":false,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"DecreasePositionWithTpslParams"}}]},{"name":"decreasePositionWithTpslAndInternalSwap","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"owner","isMut":false,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"dispensingCustody","isMut":true,"isSigner":false},{"name":"dispensingCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"dispensingCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"DecreasePositionWithTpslAndInternalSwapParams"}}]},{"name":"liquidateFullPosition4","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"LiquidateFullPosition4Params"}}]},{"name":"refreshAssetsUnderManagement","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"RefreshAssetsUnderManagementParams"}}]},{"name":"setMaxGlobalSizes","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"custody","isMut":true,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetMaxGlobalSizesParams"}}]},{"name":"instantCreateTpsl","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"apiKeeper","isMut":false,"isSigner":true},{"name":"owner","isMut":true,"isSigner":true},{"name":"receivingAccount","isMut":true,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":false,"isSigner":false},{"name":"desiredMint","isMut":false,"isSigner":false},{"name":"referral","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InstantCreateTpslParams"}}]},{"name":"instantCreateLimitOrder","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"apiKeeper","isMut":false,"isSigner":true},{"name":"owner","isMut":true,"isSigner":true},{"name":"fundingAccount","isMut":true,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":false,"isSigner":false},{"name":"inputMint","isMut":false,"isSigner":false},{"name":"referral","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InstantCreateLimitOrderParams"}}]},{"name":"instantIncreasePosition","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"apiKeeper","isMut":false,"isSigner":true},{"name":"owner","isMut":true,"isSigner":true},{"name":"fundingAccount","isMut":true,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"tokenLedger","isMut":false,"isSigner":false,"isOptional":true},{"name":"referral","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InstantIncreasePositionParams"}}]},{"name":"instantDecreasePosition","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"apiKeeper","isMut":false,"isSigner":true},{"name":"owner","isMut":true,"isSigner":true},{"name":"receivingAccount","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"desiredMint","isMut":false,"isSigner":false},{"name":"referral","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InstantDecreasePositionParams"}}]},{"name":"instantDecreasePosition2","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"apiKeeper","isMut":false,"isSigner":true},{"name":"owner","isMut":true,"isSigner":true},{"name":"receivingAccount","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustody","isMut":true,"isSigner":false},{"name":"collateralCustodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"collateralCustodyTokenAccount","isMut":true,"isSigner":false},{"name":"desiredMint","isMut":false,"isSigner":false},{"name":"referral","isMut":false,"isSigner":false,"isOptional":true},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"positionRequestAta","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InstantDecreasePosition2Params"}}]},{"name":"instantUpdateLimitOrder","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"apiKeeper","isMut":false,"isSigner":true},{"name":"owner","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InstantUpdateLimitOrderParams"}}]},{"name":"instantUpdateTpsl","accounts":[{"name":"keeper","isMut":false,"isSigner":true},{"name":"apiKeeper","isMut":false,"isSigner":true},{"name":"owner","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"positionRequest","isMut":true,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InstantUpdateTpslParams"}}]},{"name":"getAddLiquidityAmountAndFee2","accounts":[{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"lpTokenMint","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"GetAddLiquidityAmountAndFee2Params"}}],"returns":{"defined":"AmountAndFee"}},{"name":"getRemoveLiquidityAmountAndFee2","accounts":[{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"custodyDovesPriceAccount","isMut":false,"isSigner":false},{"name":"custodyPythnetPriceAccount","isMut":false,"isSigner":false},{"name":"lpTokenMint","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"GetRemoveLiquidityAmountAndFee2Params"}}],"returns":{"defined":"AmountAndFee"}},{"name":"getAssetsUnderManagement2","accounts":[{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"GetAssetsUnderManagement2Params"}}],"returns":"u128"},{"name":"borrowFromCustody","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"borrowPosition","isMut":true,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"BorrowFromCustodyParams"}}]},{"name":"repayToCustody","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"borrowPosition","isMut":true,"isSigner":false},{"name":"custodyTokenAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"RepayToCustodyParams"}}]},{"name":"depositCollateralForBorrows","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"custody","isMut":false,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"borrowPosition","isMut":true,"isSigner":false},{"name":"collateralTokenAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"DepositParams"}}]},{"name":"withdrawCollateralForBorrows","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"borrowPosition","isMut":true,"isSigner":false},{"name":"collateralTokenAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"WithdrawParams"}}]},{"name":"liquidateBorrowPosition","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"borrowPosition","isMut":true,"isSigner":false},{"name":"collateralTokenAccount","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"LiquidateBorrowPositionParams"}}]},{"name":"partialLiquidateBorrowPosition","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"perpetuals","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"custody","isMut":true,"isSigner":false},{"name":"transferAuthority","isMut":false,"isSigner":false},{"name":"borrowPosition","isMut":true,"isSigner":false},{"name":"collateralTokenAccount","isMut":true,"isSigner":false},{"name":"lpTokenMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"PartialLiquidateBorrowPositionParams"}}]},{"name":"closeBorrowPosition","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"borrowPosition","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"CloseBorrowPositionParams"}}]}],"accounts":[{"name":"BorrowPosition","type":{"kind":"struct","fields":[{"name":"owner","type":"publicKey"},{"name":"pool","type":"publicKey"},{"name":"custody","type":"publicKey"},{"name":"openTime","type":"i64"},{"name":"updateTime","type":"i64"},{"name":"borrowSize","type":"u128"},{"name":"cumulativeCompoundedInterestSnapshot","type":"u128"},{"name":"lockedCollateral","type":"u64"},{"name":"bump","type":"u8"},{"name":"lastBorrowed","type":"i64"}]}},{"name":"Custody","type":{"kind":"struct","fields":[{"name":"pool","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"tokenAccount","type":"publicKey"},{"name":"decimals","type":"u8"},{"name":"isStable","type":"bool"},{"name":"oracle","type":{"defined":"OracleParams"}},{"name":"pricing","type":{"defined":"PricingParams"}},{"name":"permissions","type":{"defined":"Permissions"}},{"name":"targetRatioBps","type":"u64"},{"name":"assets","type":{"defined":"Assets"}},{"name":"fundingRateState","type":{"defined":"FundingRateState"}},{"name":"bump","type":"u8"},{"name":"tokenAccountBump","type":"u8"},{"name":"increasePositionBps","type":"u64"},{"name":"decreasePositionBps","type":"u64"},{"name":"maxPositionSizeUsd","type":"u64"},{"name":"dovesOracle","type":"publicKey"},{"name":"jumpRateState","type":{"defined":"JumpRateState"}},{"name":"dovesAgOracle","type":"publicKey"},{"name":"priceImpactBuffer","type":{"defined":"PriceImpactBuffer"}},{"name":"borrowLendParameters","type":{"defined":"BorrowLendParams"}},{"name":"borrowsFundingRateState","type":{"defined":"FundingRateState"}},{"name":"debt","type":"u128"},{"name":"borrowLendInterestsAccured","type":"u128"},{"name":"borrowLimitInTokenAmount","type":"u64"},{"name":"minInterestFeeBps","type":"u64"},{"name":"minInterestFeeGracePeriodSeconds","type":"u64"},{"name":"totalStakedAmountLamports","type":"u64"},{"name":"maxTotalStakedAmountLamports","type":"u64"},{"name":"externalSwapFeeMultiplierBps","type":"u64"},{"name":"disableClosePositionRequest","type":"bool"},{"name":"withdrawalLimitTokenAmount","type":"u64"},{"name":"withdrawalTokenAmountAccumulated","type":"u64"},{"name":"withdrawalLimitLastResetAt","type":"i64"},{"name":"withdrawalLimitIntervalSeconds","type":"u64"}]}},{"name":"Perpetuals","type":{"kind":"struct","fields":[{"name":"permissions","type":{"defined":"Permissions"}},{"name":"pools","type":{"vec":"publicKey"}},{"name":"admin","type":"publicKey"},{"name":"transferAuthorityBump","type":"u8"},{"name":"perpetualsBump","type":"u8"},{"name":"inceptionTime","type":"i64"}]}},{"name":"Pool","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"custodies","type":{"vec":"publicKey"}},{"name":"aumUsd","type":"u128"},{"name":"limit","type":{"defined":"Limit"}},{"name":"fees","type":{"defined":"Fees"}},{"name":"poolApr","type":{"defined":"PoolApr"}},{"name":"maxRequestExecutionSec","type":"i64"},{"name":"bump","type":"u8"},{"name":"lpTokenBump","type":"u8"},{"name":"inceptionTime","type":"i64"},{"name":"parameterUpdateOracle","type":{"defined":"Secp256k1Pubkey"}},{"name":"aumUsdUpdatedAt","type":"i64"},{"name":"maxTriggerPriceDiffBps","type":"u64"},{"name":"disableClosePositionRequest","type":"bool"},{"name":"maxLpTokenPriceChangeBps","type":"u64"},{"name":"aumUsdRefreshedAtSlot","type":"u64"}]}},{"name":"PositionRequest","type":{"kind":"struct","fields":[{"name":"owner","type":"publicKey"},{"name":"pool","type":"publicKey"},{"name":"custody","type":"publicKey"},{"name":"position","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"openTime","type":"i64"},{"name":"updateTime","type":"i64"},{"name":"sizeUsdDelta","type":"u64"},{"name":"collateralDelta","type":"u64"},{"name":"requestChange","type":{"defined":"RequestChange"}},{"name":"requestType","type":{"defined":"RequestType"}},{"name":"side","type":{"defined":"Side"}},{"name":"priceSlippage","type":{"option":"u64"}},{"name":"jupiterMinimumOut","type":{"option":"u64"}},{"name":"preSwapAmount","type":{"option":"u64"}},{"name":"triggerPrice","type":{"option":"u64"}},{"name":"triggerAboveThreshold","type":{"option":"bool"}},{"name":"entirePosition","type":{"option":"bool"}},{"name":"executed","type":"bool"},{"name":"counter","type":"u64"},{"name":"bump","type":"u8"},{"name":"referral","type":{"option":"publicKey"}}]}},{"name":"Position","type":{"kind":"struct","fields":[{"name":"owner","type":"publicKey"},{"name":"pool","type":"publicKey"},{"name":"custody","type":"publicKey"},{"name":"collateralCustody","type":"publicKey"},{"name":"openTime","type":"i64"},{"name":"updateTime","type":"i64"},{"name":"side","type":{"defined":"Side"}},{"name":"price","type":"u64"},{"name":"sizeUsd","type":"u64"},{"name":"collateralUsd","type":"u64"},{"name":"realisedPnlUsd","type":"i64"},{"name":"cumulativeInterestSnapshot","type":"u128"},{"name":"lockedAmount","type":"u64"},{"name":"bump","type":"u8"}]}},{"name":"StakeInfo","type":{"kind":"struct","fields":[{"name":"pool","type":"publicKey"},{"name":"stakeAccount","type":"publicKey"},{"name":"currentStakedAmountLamports","type":"u64"},{"name":"totalStakingRewardsLamports","type":"u64"},{"name":"lastUpdatedAt","type":"i64"},{"name":"deactivating","type":"bool"},{"name":"stakeAccountIndex","type":"u64"},{"name":"bump","type":"u8"}]}},{"name":"TokenLedger","type":{"kind":"struct","fields":[{"name":"tokenAccount","type":"publicKey"},{"name":"amount","type":"u64"}]}}],"types":[{"name":"AddCustodyParams","type":{"kind":"struct","fields":[{"name":"isStable","type":"bool"},{"name":"oracle","type":{"defined":"OracleParams"}},{"name":"pricing","type":{"defined":"PricingParams"}},{"name":"permissions","type":{"defined":"Permissions"}},{"name":"hourlyFundingDbps","type":"u64"},{"name":"targetRatioBps","type":"u64"},{"name":"increasePositionBps","type":"u64"},{"name":"decreasePositionBps","type":"u64"},{"name":"dovesOracle","type":"publicKey"},{"name":"maxPositionSizeUsd","type":"u64"},{"name":"jumpRate","type":{"defined":"JumpRateState"}},{"name":"priceImpactFeeFactor","type":"u64"},{"name":"priceImpactExponent","type":"f32"},{"name":"deltaImbalanceThresholdDecimal","type":"u64"},{"name":"maxFeeBps","type":"u64"},{"name":"dovesAgOracle","type":"publicKey"}]}},{"name":"AddLiquidity2Params","type":{"kind":"struct","fields":[{"name":"tokenAmountIn","type":"u64"},{"name":"minLpAmountOut","type":"u64"},{"name":"tokenAmountPreSwap","type":{"option":"u64"}}]}},{"name":"AddPoolParams","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"limit","type":{"defined":"Limit"}},{"name":"fees","type":{"defined":"Fees"}},{"name":"maxRequestExecutionSec","type":"i64"}]}},{"name":"BorrowFromCustodyParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"CloseBorrowPositionParams","type":{"kind":"struct","fields":[]}},{"name":"CreateAndDelegateStakeAccountParams","type":{"kind":"struct","fields":[{"name":"stakeAccountIndex","type":"u64"},{"name":"stakeAmountLamports","type":"u64"}]}},{"name":"CreateDecreasePositionMarketRequestParams","type":{"kind":"struct","fields":[{"name":"collateralUsdDelta","type":"u64"},{"name":"sizeUsdDelta","type":"u64"},{"name":"priceSlippage","type":"u64"},{"name":"jupiterMinimumOut","type":{"option":"u64"}},{"name":"entirePosition","type":{"option":"bool"}},{"name":"counter","type":"u64"}]}},{"name":"CreateDecreasePositionRequest2Params","type":{"kind":"struct","fields":[{"name":"collateralUsdDelta","type":"u64"},{"name":"sizeUsdDelta","type":"u64"},{"name":"requestType","type":{"defined":"RequestType"}},{"name":"priceSlippage","type":{"option":"u64"}},{"name":"jupiterMinimumOut","type":{"option":"u64"}},{"name":"triggerPrice","type":{"option":"u64"}},{"name":"triggerAboveThreshold","type":{"option":"bool"}},{"name":"entirePosition","type":{"option":"bool"}},{"name":"counter","type":"u64"}]}},{"name":"CreateIncreasePositionMarketRequestParams","type":{"kind":"struct","fields":[{"name":"sizeUsdDelta","type":"u64"},{"name":"collateralTokenDelta","type":"u64"},{"name":"side","type":{"defined":"Side"}},{"name":"priceSlippage","type":"u64"},{"name":"jupiterMinimumOut","type":{"option":"u64"}},{"name":"counter","type":"u64"}]}},{"name":"CreateTokenMetadataParams","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]}},{"name":"DecreasePosition4Params","type":{"kind":"struct","fields":[]}},{"name":"DecreasePositionWithInternalSwapParams","type":{"kind":"struct","fields":[]}},{"name":"DecreasePositionWithTpslAndInternalSwapParams","type":{"kind":"struct","fields":[]}},{"name":"DecreasePositionWithTpslParams","type":{"kind":"struct","fields":[]}},{"name":"DepositParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"GetAddLiquidityAmountAndFee2Params","type":{"kind":"struct","fields":[{"name":"tokenAmountIn","type":"u64"}]}},{"name":"GetAssetsUnderManagement2Params","type":{"kind":"struct","fields":[{"name":"mode","type":{"option":{"defined":"PriceCalcMode"}}}]}},{"name":"GetRemoveLiquidityAmountAndFee2Params","type":{"kind":"struct","fields":[{"name":"lpAmountIn","type":"u64"}]}},{"name":"IncreasePosition4Params","type":{"kind":"struct","fields":[]}},{"name":"IncreasePositionPreSwapParams","type":{"kind":"struct","fields":[]}},{"name":"IncreasePositionWithInternalSwapParams","type":{"kind":"struct","fields":[]}},{"name":"InitParams","type":{"kind":"struct","fields":[{"name":"allowSwap","type":"bool"},{"name":"allowAddLiquidity","type":"bool"},{"name":"allowRemoveLiquidity","type":"bool"},{"name":"allowIncreasePosition","type":"bool"},{"name":"allowDecreasePosition","type":"bool"},{"name":"allowCollateralWithdrawal","type":"bool"},{"name":"allowLiquidatePosition","type":"bool"}]}},{"name":"InstantCreateLimitOrderParams","type":{"kind":"struct","fields":[{"name":"sizeUsdDelta","type":"u64"},{"name":"collateralTokenDelta","type":"u64"},{"name":"side","type":{"defined":"Side"}},{"name":"triggerPrice","type":"u64"},{"name":"triggerAboveThreshold","type":"bool"},{"name":"counter","type":"u64"},{"name":"requestTime","type":"i64"}]}},{"name":"InstantCreateTpslParams","type":{"kind":"struct","fields":[{"name":"collateralUsdDelta","type":"u64"},{"name":"sizeUsdDelta","type":"u64"},{"name":"triggerPrice","type":"u64"},{"name":"triggerAboveThreshold","type":"bool"},{"name":"entirePosition","type":"bool"},{"name":"counter","type":"u64"},{"name":"requestTime","type":"i64"}]}},{"name":"InstantDecreasePosition2Params","type":{"kind":"struct","fields":[{"name":"collateralUsdDelta","type":"u64"},{"name":"sizeUsdDelta","type":"u64"},{"name":"priceSlippage","type":"u64"},{"name":"entirePosition","type":{"option":"bool"}},{"name":"requestTime","type":"i64"},{"name":"counter","type":"u64"}]}},{"name":"InstantDecreasePositionParams","type":{"kind":"struct","fields":[{"name":"collateralUsdDelta","type":"u64"},{"name":"sizeUsdDelta","type":"u64"},{"name":"priceSlippage","type":"u64"},{"name":"entirePosition","type":{"option":"bool"}},{"name":"requestTime","type":"i64"}]}},{"name":"InstantIncreasePositionPreSwapParams","type":{"kind":"struct","fields":[{"name":"amountIn","type":"u64"},{"name":"minAmountOut","type":"u64"}]}},{"name":"InstantIncreasePositionParams","type":{"kind":"struct","fields":[{"name":"sizeUsdDelta","type":"u64"},{"name":"collateralTokenDelta","type":{"option":"u64"}},{"name":"side","type":{"defined":"Side"}},{"name":"priceSlippage","type":"u64"},{"name":"requestTime","type":"i64"}]}},{"name":"InstantUpdateLimitOrderParams","type":{"kind":"struct","fields":[{"name":"sizeUsdDelta","type":"u64"},{"name":"triggerPrice","type":"u64"},{"name":"requestTime","type":"i64"}]}},{"name":"InstantUpdateTpslParams","type":{"kind":"struct","fields":[{"name":"sizeUsdDelta","type":"u64"},{"name":"triggerPrice","type":"u64"},{"name":"requestTime","type":"i64"}]}},{"name":"LiquidateBorrowPositionParams","type":{"kind":"struct","fields":[]}},{"name":"LiquidateFullPosition4Params","type":{"kind":"struct","fields":[]}},{"name":"OperatorSetCustodyConfigParams","type":{"kind":"struct","fields":[{"name":"pricing","type":{"defined":"PricingParams"}},{"name":"hourlyFundingDbps","type":"u64"},{"name":"targetRatioBps","type":"u64"},{"name":"increasePositionBps","type":"u64"},{"name":"decreasePositionBps","type":"u64"},{"name":"maxPositionSizeUsd","type":"u64"},{"name":"jumpRate","type":{"defined":"JumpRateState"}},{"name":"priceImpactFeeFactor","type":"u64"},{"name":"priceImpactExponent","type":"f32"},{"name":"deltaImbalanceThresholdDecimal","type":"u64"},{"name":"maxFeeBps","type":"u64"},{"name":"borrowLendParameters","type":{"defined":"BorrowLendParams"}},{"name":"borrowHourlyFundingDbps","type":"u64"},{"name":"borrowLimitInTokenAmount","type":"u64"},{"name":"minInterestFeeBps","type":"u64"},{"name":"minInterestFeeGracePeriodSeconds","type":"u64"},{"name":"maxTotalStakedAmountLamports","type":"u64"},{"name":"externalSwapFeeMultiplierBps","type":"u64"},{"name":"disableClosePositionRequest","type":"bool"},{"name":"withdrawalLimitTokenAmount","type":"u64"},{"name":"withdrawalLimitIntervalSeconds","type":"u64"}]}},{"name":"OperatorSetPoolConfigParams","type":{"kind":"struct","fields":[{"name":"fees","type":{"defined":"Fees"}},{"name":"limit","type":{"defined":"Limit"}},{"name":"maxRequestExecutionSec","type":"i64"},{"name":"maxTriggerPriceDiffBps","type":"u64"},{"name":"disableClosePositionRequest","type":"bool"},{"name":"maxLpTokenPriceChangeBps","type":"u64"}]}},{"name":"PartialLiquidateBorrowPositionParams","type":{"kind":"struct","fields":[]}},{"name":"RefreshAssetsUnderManagementParams","type":{"kind":"struct","fields":[]}},{"name":"RemoveLiquidity2Params","type":{"kind":"struct","fields":[{"name":"lpAmountIn","type":"u64"},{"name":"minAmountOut","type":"u64"}]}},{"name":"RepayToCustodyParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"SetCustodyConfigParams","type":{"kind":"struct","fields":[{"name":"oracle","type":{"defined":"OracleParams"}},{"name":"pricing","type":{"defined":"PricingParams"}},{"name":"permissions","type":{"defined":"Permissions"}},{"name":"hourlyFundingDbps","type":"u64"},{"name":"targetRatioBps","type":"u64"},{"name":"increasePositionBps","type":"u64"},{"name":"decreasePositionBps","type":"u64"},{"name":"dovesOracle","type":"publicKey"},{"name":"maxPositionSizeUsd","type":"u64"},{"name":"jumpRate","type":{"defined":"JumpRateState"}},{"name":"priceImpactFeeFactor","type":"u64"},{"name":"priceImpactExponent","type":"f32"},{"name":"deltaImbalanceThresholdDecimal","type":"u64"},{"name":"maxFeeBps","type":"u64"},{"name":"dovesAgOracle","type":"publicKey"},{"name":"borrowLendParameters","type":{"defined":"BorrowLendParams"}},{"name":"borrowHourlyFundingDbps","type":"u64"},{"name":"borrowLimitInTokenAmount","type":"u64"},{"name":"minInterestFeeBps","type":"u64"},{"name":"minInterestFeeGracePeriodSeconds","type":"u64"},{"name":"maxTotalStakedAmountLamports","type":"u64"},{"name":"externalSwapFeeMultiplierBps","type":"u64"},{"name":"disableClosePositionRequest","type":"bool"},{"name":"withdrawalLimitIntervalSeconds","type":"u64"},{"name":"withdrawalLimitTokenAmount","type":"u64"}]}},{"name":"SetMaxGlobalSizesParams","type":{"kind":"struct","fields":[{"name":"maxGlobalLongSize","type":"u64"},{"name":"maxGlobalShortSize","type":"u64"},{"name":"recoveryId","type":"u8"},{"name":"signature","type":{"array":["u8",64]}},{"name":"referenceId","type":{"array":["u8",16]}},{"name":"timestamp","type":"u64"}]}},{"name":"SetPerpetualsConfigParams","type":{"kind":"struct","fields":[{"name":"permissions","type":{"defined":"Permissions"}}]}},{"name":"SetPoolConfigParams","type":{"kind":"struct","fields":[{"name":"fees","type":{"defined":"Fees"}},{"name":"limit","type":{"defined":"Limit"}},{"name":"maxRequestExecutionSec","type":"i64"},{"name":"parameterUpdateOracle","type":{"defined":"Secp256k1Pubkey"}},{"name":"maxTriggerPriceDiffBps","type":"u64"},{"name":"disableClosePositionRequest","type":"bool"},{"name":"maxLpTokenPriceChangeBps","type":"u64"}]}},{"name":"SetTestTimeParams","type":{"kind":"struct","fields":[{"name":"time","type":"i64"}]}},{"name":"Swap2Params","type":{"kind":"struct","fields":[{"name":"amountIn","type":"u64"},{"name":"minAmountOut","type":"u64"}]}},{"name":"SwapWithTokenLedgerParams","type":{"kind":"struct","fields":[{"name":"minAmountOut","type":"u64"}]}},{"name":"TestInitParams","type":{"kind":"struct","fields":[{"name":"allowSwap","type":"bool"},{"name":"allowAddLiquidity","type":"bool"},{"name":"allowRemoveLiquidity","type":"bool"},{"name":"allowIncreasePosition","type":"bool"},{"name":"allowDecreasePosition","type":"bool"},{"name":"allowCollateralWithdrawal","type":"bool"},{"name":"allowLiquidatePosition","type":"bool"}]}},{"name":"TransferAdminParams","type":{"kind":"struct","fields":[]}},{"name":"UpdateDecreasePositionRequest2Params","type":{"kind":"struct","fields":[{"name":"sizeUsdDelta","type":"u64"},{"name":"triggerPrice","type":"u64"}]}},{"name":"WithdrawParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"WithdrawFees2Params","type":{"kind":"struct","fields":[]}},{"name":"PriceImpactBuffer","type":{"kind":"struct","fields":[{"name":"openInterest","type":{"array":["i64",60]}},{"name":"lastUpdated","type":"i64"},{"name":"feeFactor","type":"u64"},{"name":"exponent","type":"f32"},{"name":"deltaImbalanceThresholdDecimal","type":"u64"},{"name":"maxFeeBps","type":"u64"}]}},{"name":"Assets","type":{"kind":"struct","fields":[{"name":"feesReserves","type":"u64"},{"name":"owned","type":"u64"},{"name":"locked","type":"u64"},{"name":"guaranteedUsd","type":"u64"},{"name":"globalShortSizes","type":"u64"},{"name":"globalShortAveragePrices","type":"u64"}]}},{"name":"PricingParams","type":{"kind":"struct","fields":[{"name":"tradeImpactFeeScalar","type":"u64"},{"name":"buffer","type":"u64"},{"name":"swapSpread","type":"u64"},{"name":"maxLeverage","type":"u64"},{"name":"maxGlobalLongSizes","type":"u64"},{"name":"maxGlobalShortSizes","type":"u64"}]}},{"name":"FundingRateState","type":{"kind":"struct","fields":[{"name":"cumulativeInterestRate","type":"u128"},{"name":"lastUpdate","type":"i64"},{"name":"hourlyFundingDbps","type":"u64"}]}},{"name":"JumpRateState","type":{"kind":"struct","fields":[{"name":"minRateBps","type":"u64"},{"name":"maxRateBps","type":"u64"},{"name":"targetRateBps","type":"u64"},{"name":"targetUtilizationRate","type":"u64"}]}},{"name":"BorrowLendParams","type":{"kind":"struct","fields":[{"name":"borrowsLimitInBps","type":"u64"},{"name":"maintainanceMarginBps","type":"u64"},{"name":"protocolFeeBps","type":"u64"},{"name":"liquidationMargin","type":"u64"},{"name":"liquidationFeeBps","type":"u64"}]}},{"name":"OraclePrice","type":{"kind":"struct","fields":[{"name":"price","type":"u64"},{"name":"exponent","type":"i32"}]}},{"name":"Price","type":{"kind":"struct","fields":[{"name":"price","type":"u64"},{"name":"expo","type":"i32"},{"name":"publishTime","type":"i64"}]}},{"name":"OracleParams","type":{"kind":"struct","fields":[{"name":"oracleAccount","type":"publicKey"},{"name":"oracleType","type":{"defined":"OracleType"}},{"name":"buffer","type":"u64"},{"name":"maxPriceAgeSec","type":"u32"}]}},{"name":"AmountAndFee","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"fee","type":"u64"},{"name":"feeBps","type":"u64"}]}},{"name":"Permissions","type":{"kind":"struct","fields":[{"name":"allowSwap","type":"bool"},{"name":"allowAddLiquidity","type":"bool"},{"name":"allowRemoveLiquidity","type":"bool"},{"name":"allowIncreasePosition","type":"bool"},{"name":"allowDecreasePosition","type":"bool"},{"name":"allowCollateralWithdrawal","type":"bool"},{"name":"allowLiquidatePosition","type":"bool"}]}},{"name":"Fees","type":{"kind":"struct","fields":[{"name":"swapMultiplier","type":"u64"},{"name":"stableSwapMultiplier","type":"u64"},{"name":"addRemoveLiquidityBps","type":"u64"},{"name":"swapBps","type":"u64"},{"name":"taxBps","type":"u64"},{"name":"stableSwapBps","type":"u64"},{"name":"stableSwapTaxBps","type":"u64"},{"name":"liquidationRewardBps","type":"u64"},{"name":"protocolShareBps","type":"u64"}]}},{"name":"PoolApr","type":{"kind":"struct","fields":[{"name":"lastUpdated","type":"i64"},{"name":"feeAprBps","type":"u64"},{"name":"realizedFeeUsd","type":"u64"}]}},{"name":"Limit","type":{"kind":"struct","fields":[{"name":"maxAumUsd","type":"u128"},{"name":"tokenWeightageBufferBps","type":"u128"},{"name":"buffer","type":"u64"}]}},{"name":"Secp256k1Pubkey","type":{"kind":"struct","fields":[{"name":"prefix","type":"u8"},{"name":"key","type":{"array":["u8",32]}}]}},{"name":"PriceImpactMechanism","type":{"kind":"enum","variants":[{"name":"TradeSize"},{"name":"DeltaImbalance"}]}},{"name":"OracleType","type":{"kind":"enum","variants":[{"name":"None"},{"name":"Test"},{"name":"Pyth"}]}},{"name":"PriceCalcMode","type":{"kind":"enum","variants":[{"name":"Min"},{"name":"Max"},{"name":"Ignore"}]}},{"name":"PriceStaleTolerance","type":{"kind":"enum","variants":[{"name":"Strict"},{"name":"Loose"}]}},{"name":"TradePoolType","type":{"kind":"enum","variants":[{"name":"Increase"},{"name":"Decrease"}]}},{"name":"RequestType","type":{"kind":"enum","variants":[{"name":"Market"},{"name":"Trigger"}]}},{"name":"RequestChange","type":{"kind":"enum","variants":[{"name":"None"},{"name":"Increase"},{"name":"Decrease"}]}},{"name":"Side","type":{"kind":"enum","variants":[{"name":"None"},{"name":"Long"},{"name":"Short"}]}}],"events":[{"name":"CreatePositionRequestEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"positionKey","type":"publicKey","index":false},{"name":"positionSide","type":"u8","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionCollateralMint","type":"publicKey","index":false},{"name":"positionCollateralCustody","type":"publicKey","index":false},{"name":"positionRequestKey","type":"publicKey","index":false},{"name":"positionRequestMint","type":"publicKey","index":false},{"name":"sizeUsdDelta","type":"u64","index":false},{"name":"collateralDelta","type":"u64","index":false},{"name":"priceSlippage","type":{"option":"u64"},"index":false},{"name":"jupiterMinimumOut","type":{"option":"u64"},"index":false},{"name":"preSwapAmount","type":{"option":"u64"},"index":false},{"name":"requestChange","type":"u8","index":false},{"name":"openTime","type":"i64","index":false},{"name":"referral","type":{"option":"publicKey"},"index":false}]},{"name":"InstantCreateTpslEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"positionKey","type":"publicKey","index":false},{"name":"positionSide","type":"u8","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionCollateralCustody","type":"publicKey","index":false},{"name":"positionRequestKey","type":"publicKey","index":false},{"name":"positionRequestMint","type":"publicKey","index":false},{"name":"sizeUsdDelta","type":"u64","index":false},{"name":"collateralDelta","type":"u64","index":false},{"name":"entirePosition","type":"bool","index":false},{"name":"openTime","type":"i64","index":false}]},{"name":"InstantUpdateTpslEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"positionKey","type":"publicKey","index":false},{"name":"positionSide","type":"u8","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionCollateralCustody","type":"publicKey","index":false},{"name":"positionRequestKey","type":"publicKey","index":false},{"name":"positionRequestMint","type":"publicKey","index":false},{"name":"sizeUsdDelta","type":"u64","index":false},{"name":"collateralDelta","type":"u64","index":false},{"name":"entirePosition","type":"bool","index":false},{"name":"updateTime","type":"i64","index":false}]},{"name":"ClosePositionRequestEvent","fields":[{"name":"entirePosition","type":{"option":"bool"},"index":false},{"name":"executed","type":"bool","index":false},{"name":"requestChange","type":"u8","index":false},{"name":"requestType","type":"u8","index":false},{"name":"side","type":"u8","index":false},{"name":"positionRequestKey","type":"publicKey","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"mint","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"openTime","type":"i64","index":false}]},{"name":"IncreasePositionEvent","fields":[{"name":"positionKey","type":"publicKey","index":false},{"name":"positionSide","type":"u8","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionCollateralCustody","type":"publicKey","index":false},{"name":"positionSizeUsd","type":"u64","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionRequestKey","type":"publicKey","index":false},{"name":"positionRequestMint","type":"publicKey","index":false},{"name":"positionRequestChange","type":"u8","index":false},{"name":"positionRequestType","type":"u8","index":false},{"name":"positionRequestCollateralDelta","type":"u64","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"sizeUsdDelta","type":"u64","index":false},{"name":"collateralUsdDelta","type":"u64","index":false},{"name":"collateralTokenDelta","type":"u64","index":false},{"name":"price","type":"u64","index":false},{"name":"priceSlippage","type":{"option":"u64"},"index":false},{"name":"feeToken","type":"u64","index":false},{"name":"feeUsd","type":"u64","index":false},{"name":"openTime","type":"i64","index":false},{"name":"referral","type":{"option":"publicKey"},"index":false},{"name":"positionFeeUsd","type":"u64","index":false},{"name":"fundingFeeUsd","type":"u64","index":false},{"name":"priceImpactFeeUsd","type":"u64","index":false}]},{"name":"IncreasePositionPreSwapEvent","fields":[{"name":"positionRequestKey","type":"publicKey","index":false},{"name":"transferAmount","type":"u64","index":false},{"name":"collateralCustodyPreSwapAmount","type":"u64","index":false}]},{"name":"DecreasePositionEvent","fields":[{"name":"positionKey","type":"publicKey","index":false},{"name":"positionSide","type":"u8","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionCollateralCustody","type":"publicKey","index":false},{"name":"positionSizeUsd","type":"u64","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionRequestKey","type":"publicKey","index":false},{"name":"positionRequestMint","type":"publicKey","index":false},{"name":"positionRequestChange","type":"u8","index":false},{"name":"positionRequestType","type":"u8","index":false},{"name":"hasProfit","type":"bool","index":false},{"name":"pnlDelta","type":"u64","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"sizeUsdDelta","type":"u64","index":false},{"name":"transferAmountUsd","type":"u64","index":false},{"name":"transferToken","type":{"option":"u64"},"index":false},{"name":"price","type":"u64","index":false},{"name":"priceSlippage","type":{"option":"u64"},"index":false},{"name":"feeUsd","type":"u64","index":false},{"name":"openTime","type":"i64","index":false},{"name":"referral","type":{"option":"publicKey"},"index":false},{"name":"positionFeeUsd","type":"u64","index":false},{"name":"fundingFeeUsd","type":"u64","index":false},{"name":"priceImpactFeeUsd","type":"u64","index":false},{"name":"originalPositionCollateralUsd","type":"u64","index":false},{"name":"positionCollateralUsd","type":"u64","index":false},{"name":"positionOpenTime","type":"i64","index":false},{"name":"positionPrice","type":"u64","index":false}]},{"name":"DecreasePositionPostSwapEvent","fields":[{"name":"positionRequestKey","type":"publicKey","index":false},{"name":"swapAmount","type":"u64","index":false},{"name":"jupiterMinimumOut","type":{"option":"u64"},"index":false}]},{"name":"LiquidateFullPositionEvent","fields":[{"name":"positionKey","type":"publicKey","index":false},{"name":"positionSide","type":"u8","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionCollateralCustody","type":"publicKey","index":false},{"name":"positionCollateralMint","type":"publicKey","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionSizeUsd","type":"u64","index":false},{"name":"hasProfit","type":"bool","index":false},{"name":"pnlDelta","type":"u64","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"transferAmountUsd","type":"u64","index":false},{"name":"transferToken","type":"u64","index":false},{"name":"price","type":"u64","index":false},{"name":"feeUsd","type":"u64","index":false},{"name":"liquidationFeeUsd","type":"u64","index":false},{"name":"openTime","type":"i64","index":false},{"name":"positionFeeUsd","type":"u64","index":false},{"name":"fundingFeeUsd","type":"u64","index":false},{"name":"priceImpactFeeUsd","type":"u64","index":false},{"name":"originalPositionCollateralUsd","type":"u64","index":false},{"name":"positionCollateralUsd","type":"u64","index":false},{"name":"positionOpenTime","type":"i64","index":false},{"name":"positionPrice","type":"u64","index":false}]},{"name":"PoolSwapEvent","fields":[{"name":"receivingCustodyKey","type":"publicKey","index":false},{"name":"dispensingCustodyKey","type":"publicKey","index":false},{"name":"poolKey","type":"publicKey","index":false},{"name":"amountIn","type":"u64","index":false},{"name":"amountOut","type":"u64","index":false},{"name":"swapUsdAmount","type":"u64","index":false},{"name":"amountOutAfterFees","type":"u64","index":false},{"name":"feeBps","type":"u64","index":false},{"name":"ownerKey","type":"publicKey","index":false},{"name":"receivingAccountKey","type":"publicKey","index":false}]},{"name":"PoolSwapExactOutEvent","fields":[{"name":"receivingCustodyKey","type":"publicKey","index":false},{"name":"dispensingCustodyKey","type":"publicKey","index":false},{"name":"poolKey","type":"publicKey","index":false},{"name":"amountIn","type":"u64","index":false},{"name":"amountInAfterFees","type":"u64","index":false},{"name":"amountOut","type":"u64","index":false},{"name":"swapUsdAmount","type":"u64","index":false},{"name":"feeBps","type":"u64","index":false},{"name":"ownerKey","type":"publicKey","index":false},{"name":"receivingAccountKey","type":"publicKey","index":false}]},{"name":"AddLiquidityEvent","fields":[{"name":"custodyKey","type":"publicKey","index":false},{"name":"poolKey","type":"publicKey","index":false},{"name":"tokenAmountIn","type":"u64","index":false},{"name":"prePoolAmountUsd","type":"u128","index":false},{"name":"tokenAmountUsd","type":"u64","index":false},{"name":"feeBps","type":"u64","index":false},{"name":"tokenAmountAfterFee","type":"u64","index":false},{"name":"mintAmountUsd","type":"u64","index":false},{"name":"lpAmount","type":"u64","index":false},{"name":"postPoolAmountUsd","type":"u128","index":false}]},{"name":"RemoveLiquidityEvent","fields":[{"name":"custodyKey","type":"publicKey","index":false},{"name":"poolKey","type":"publicKey","index":false},{"name":"lpAmountIn","type":"u64","index":false},{"name":"removeAmountUsd","type":"u64","index":false},{"name":"feeBps","type":"u64","index":false},{"name":"removeTokenAmount","type":"u64","index":false},{"name":"tokenAmountAfterFee","type":"u64","index":false},{"name":"postPoolAmountUsd","type":"u128","index":false}]},{"name":"InstantCreateLimitOrderEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"positionKey","type":"publicKey","index":false},{"name":"positionSide","type":"u8","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionCollateralMint","type":"publicKey","index":false},{"name":"positionCollateralCustody","type":"publicKey","index":false},{"name":"positionRequestKey","type":"publicKey","index":false},{"name":"positionRequestMint","type":"publicKey","index":false},{"name":"sizeUsdDelta","type":"u64","index":false},{"name":"collateralDelta","type":"u64","index":false},{"name":"openTime","type":"i64","index":false}]},{"name":"InstantIncreasePositionEvent","fields":[{"name":"positionKey","type":"publicKey","index":false},{"name":"positionSide","type":"u8","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionCollateralCustody","type":"publicKey","index":false},{"name":"positionSizeUsd","type":"u64","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"sizeUsdDelta","type":"u64","index":false},{"name":"collateralUsdDelta","type":"u64","index":false},{"name":"collateralTokenDelta","type":"u64","index":false},{"name":"price","type":"u64","index":false},{"name":"priceSlippage","type":"u64","index":false},{"name":"feeToken","type":"u64","index":false},{"name":"feeUsd","type":"u64","index":false},{"name":"openTime","type":"i64","index":false},{"name":"referral","type":{"option":"publicKey"},"index":false},{"name":"positionFeeUsd","type":"u64","index":false},{"name":"fundingFeeUsd","type":"u64","index":false},{"name":"priceImpactFeeUsd","type":"u64","index":false}]},{"name":"InstantDecreasePositionEvent","fields":[{"name":"positionKey","type":"publicKey","index":false},{"name":"positionSide","type":"u8","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionCollateralCustody","type":"publicKey","index":false},{"name":"positionSizeUsd","type":"u64","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"desiredMint","type":"publicKey","index":false},{"name":"hasProfit","type":"bool","index":false},{"name":"pnlDelta","type":"u64","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"sizeUsdDelta","type":"u64","index":false},{"name":"transferAmountUsd","type":"u64","index":false},{"name":"transferToken","type":"u64","index":false},{"name":"price","type":"u64","index":false},{"name":"priceSlippage","type":"u64","index":false},{"name":"feeUsd","type":"u64","index":false},{"name":"openTime","type":"i64","index":false},{"name":"referral","type":{"option":"publicKey"},"index":false},{"name":"positionFeeUsd","type":"u64","index":false},{"name":"fundingFeeUsd","type":"u64","index":false},{"name":"originalPositionCollateralUsd","type":"u64","index":false},{"name":"positionCollateralUsd","type":"u64","index":false},{"name":"priceImpactFeeUsd","type":"u64","index":false},{"name":"positionOpenTime","type":"i64","index":false},{"name":"positionPrice","type":"u64","index":false},{"name":"positionRequest","type":{"option":"publicKey"},"index":false}]},{"name":"DepositCollateralEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"positionKey","type":"publicKey","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"depositAmount","type":"u64","index":false},{"name":"userTokenAccount","type":"publicKey","index":false},{"name":"time","type":"i64","index":false}]},{"name":"WithdrawCollateralEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"positionKey","type":"publicKey","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"withdrawAmount","type":"u64","index":false},{"name":"userTokenAccount","type":"publicKey","index":false},{"name":"custody","type":"publicKey","index":false},{"name":"previousCollateralAmount","type":"u64","index":false},{"name":"collateralAmount","type":"u64","index":false},{"name":"collateralAmountUsd","type":"u64","index":false},{"name":"marginUsd","type":"u64","index":false},{"name":"time","type":"i64","index":false}]},{"name":"BorrowFromCustodyEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"positionKey","type":"publicKey","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"sizeCustodyToken","type":"u64","index":false},{"name":"collateralAmount","type":"u64","index":false},{"name":"collateralAmountUsd","type":"u64","index":false},{"name":"marginUsd","type":"u64","index":false},{"name":"updateTime","type":"i64","index":false}]},{"name":"RepayToCustodyEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"positionKey","type":"publicKey","index":false},{"name":"positionMint","type":"publicKey","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"sizeCustodyToken","type":"u64","index":false},{"name":"updateTime","type":"i64","index":false},{"name":"interest","type":"u128","index":false}]},{"name":"LiquidateBorrowPositionEvent","fields":[{"name":"positionKey","type":"publicKey","index":false},{"name":"positionCustody","type":"publicKey","index":false},{"name":"positionSizeUsd","type":"u64","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"collateralLockedInUsd","type":"u64","index":false},{"name":"collateralLockedInLp","type":"u64","index":false},{"name":"remainingCollateralInLp","type":"u64","index":false},{"name":"custodyTokenPrice","type":"u64","index":false},{"name":"totalBorrowsInUsd","type":"u64","index":false},{"name":"liquidationFeeUsd","type":"u64","index":false},{"name":"liquidationTime","type":"i64","index":false}]},{"name":"RedeemStakeEvent","fields":[{"name":"custody","type":"publicKey","index":false},{"name":"stakeAccount","type":"publicKey","index":false},{"name":"stakeInfo","type":"publicKey","index":false},{"name":"stakeRewards","type":"u64","index":false},{"name":"custodyTotalStakedAmount","type":"u64","index":false},{"name":"currentStakedAmount","type":"u64","index":false},{"name":"totalStakingRewards","type":"u64","index":false},{"name":"redeemTime","type":"i64","index":false}]},{"name":"WithdrawStakeEvent","fields":[{"name":"custody","type":"publicKey","index":false},{"name":"stakeAccount","type":"publicKey","index":false},{"name":"stakeInfo","type":"publicKey","index":false},{"name":"stakeRewards","type":"u64","index":false},{"name":"custodyTotalStakedAmount","type":"u64","index":false},{"name":"totalStakingRewards","type":"u64","index":false},{"name":"withdrawAmount","type":"u64","index":false},{"name":"withdrawTime","type":"i64","index":false}]},{"name":"DelegateStakeEvent","fields":[{"name":"custody","type":"publicKey","index":false},{"name":"stakeAccount","type":"publicKey","index":false},{"name":"stakeInfo","type":"publicKey","index":false},{"name":"custodyTotalStakedAmount","type":"u64","index":false},{"name":"stakeAmount","type":"u64","index":false},{"name":"validatorVoteAccount","type":"publicKey","index":false},{"name":"delegateTime","type":"i64","index":false}]},{"name":"WithdrawFeesEvent","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"custody","type":"publicKey","index":false},{"name":"custodyMint","type":"publicKey","index":false},{"name":"receivingTokenAccount","type":"publicKey","index":false},{"name":"totalTradeSwapFees","type":"u64","index":false},{"name":"poolTradeSwapFees","type":"u64","index":false},{"name":"protocolTradeSwapFees","type":"u64","index":false},{"name":"totalBorrowLendingFees","type":"u64","index":false},{"name":"poolBorrowLendingFees","type":"u64","index":false},{"name":"protocolBorrowLendingFees","type":"u64","index":false},{"name":"poolTotalFeesUsd","type":"u64","index":false},{"name":"aprBpsBefore","type":"u64","index":false},{"name":"aprBpsAfter","type":"u64","index":false},{"name":"aprBpsUpdatedAt","type":"i64","index":false},{"name":"poolRealizedFeeUsdBefore","type":"u64","index":false},{"name":"poolRealizedFeeUsdAfter","type":"u64","index":false},{"name":"curtime","type":"i64","index":false}]}],"errors":[{"code":6000,"name":"MathOverflow","msg":"Overflow in arithmetic operation"},{"code":6001,"name":"UnsupportedOracle","msg":"Unsupported price oracle"},{"code":6002,"name":"InvalidOracleAccount","msg":"Invalid oracle account"},{"code":6003,"name":"StaleOraclePrice","msg":"Stale oracle price"},{"code":6004,"name":"InvalidOraclePrice","msg":"Invalid oracle price"},{"code":6005,"name":"InvalidEnvironment","msg":"Instruction is not allowed in production"},{"code":6006,"name":"InvalidCollateralAccount","msg":"Invalid collateral account"},{"code":6007,"name":"InvalidCollateralAmount","msg":"Invalid collateral amount"},{"code":6008,"name":"CollateralSlippage","msg":"Collateral slippage"},{"code":6009,"name":"InvalidPositionState","msg":"Invalid position state"},{"code":6010,"name":"InvalidPerpetualsConfig","msg":"Invalid perpetuals config"},{"code":6011,"name":"InvalidPoolConfig","msg":"Invalid pool config"},{"code":6012,"name":"InvalidInstruction","msg":"Invalid instruction"},{"code":6013,"name":"InvalidCustodyConfig","msg":"Invalid custody config"},{"code":6014,"name":"InvalidCustodyBalance","msg":"Invalid custody balance"},{"code":6015,"name":"InvalidArgument","msg":"Invalid argument"},{"code":6016,"name":"InvalidPositionRequest","msg":"Invalid position request"},{"code":6017,"name":"InvalidPositionRequestInputAta","msg":"Invalid position request input ata"},{"code":6018,"name":"InvalidMint","msg":"Invalid mint"},{"code":6019,"name":"InsufficientTokenAmount","msg":"Insufficient token amount"},{"code":6020,"name":"InsufficientAmountReturned","msg":"Insufficient token amount returned"},{"code":6021,"name":"MaxPriceSlippage","msg":"Price slippage limit exceeded"},{"code":6022,"name":"MaxLeverage","msg":"Position leverage limit exceeded"},{"code":6023,"name":"CustodyAmountLimit","msg":"Custody amount limit exceeded"},{"code":6024,"name":"PoolAmountLimit","msg":"Pool amount limit exceeded"},{"code":6025,"name":"PersonalPoolAmountLimit","msg":"Personal pool amount limit exceeded"},{"code":6026,"name":"UnsupportedToken","msg":"Token is not supported"},{"code":6027,"name":"InstructionNotAllowed","msg":"Instruction is not allowed at this time"},{"code":6028,"name":"JupiterProgramMismatch","msg":"Jupiter Program ID mismatch"},{"code":6029,"name":"ProgramMismatch","msg":"Program ID mismatch"},{"code":6030,"name":"AddressMismatch","msg":"Address mismatch"},{"code":6031,"name":"KeeperATAMissing","msg":"Missing keeper ATA"},{"code":6032,"name":"SwapAmountMismatch","msg":"Swap amount mismatch"},{"code":6033,"name":"CPINotAllowed","msg":"CPI not allowed"},{"code":6034,"name":"InvalidKeeper","msg":"Invalid Keeper"},{"code":6035,"name":"ExceedExecutionPeriod","msg":"Exceed execution period"},{"code":6036,"name":"InvalidRequestType","msg":"Invalid Request Type"},{"code":6037,"name":"InvalidTriggerPrice","msg":"Invalid Trigger Price"},{"code":6038,"name":"TriggerPriceSlippage","msg":"Trigger Price Slippage"},{"code":6039,"name":"MissingTriggerPrice","msg":"Missing Trigger Price"},{"code":6040,"name":"MissingPriceSlippage","msg":"Missing Price Slippage"},{"code":6041,"name":"InvalidPriceCalcMode","msg":"Invalid Price Calc Mode"},{"code":6042,"name":"RequestUpdatedTooRecent","msg":"Request Updated Too Recent"},{"code":6043,"name":"ExceedTokenWeightage","msg":"Exceed Token Weightage"},{"code":6044,"name":"OraclePublishTimeTooEarly","msg":"Oracle Publish Time Too Early"},{"code":6045,"name":"PullOraclePublishTimeTooEarly","msg":"Pull Oracle Publish Time Too Early"},{"code":6046,"name":"StalePullOraclePrice","msg":"Stale Pull Oracle Price"},{"code":6047,"name":"InvalidPullOraclePrice","msg":"Invalid Pull Oracle Price"},{"code":6048,"name":"PullOracleNotVerified","msg":"Pull Oracle Not Verified"},{"code":6049,"name":"PriceDiffTooLarge","msg":"Price Diff Between Pull and Push Oracle is Too Large"},{"code":6050,"name":"InvalidDovesOraclePrice","msg":"Invalid Doves Oracle Price"},{"code":6051,"name":"InvalidRequestTime","msg":"Invalid Request Time"},{"code":6052,"name":"PositionUpdatedTooRecent","msg":"Position Updated Too Recent"},{"code":6053,"name":"LedgerTokenAccountDoesNotMatch","msg":"Ledger token account does not match"},{"code":6054,"name":"InvalidTokenLedger","msg":"Invalid token ledger"},{"code":6055,"name":"OraclePriceDifferenceTooLarge","msg":"Oracle Price Difference Too Large"},{"code":6056,"name":"InvalidOracleSigner","msg":"Invalid Oracle Signer"},{"code":6057,"name":"InvalidOracleTimestamp","msg":"Invalid Oracle Timestamp"},{"code":6058,"name":"InvalidMaxGlobalLongSize","msg":"New Max Global Long Size Too Low"},{"code":6059,"name":"InvalidMaxGlobalShortSize","msg":"New Max Global Short Size Too Low"},{"code":6060,"name":"BorrowsDisabled","msg":"Borrows disabled for this custody"},{"code":6061,"name":"BorrowLimitsExceeded","msg":"Borrows limit exceeded for this custody"},{"code":6062,"name":"WithdrawExceedsMarginLimits","msg":"Withdraw exceeds margin of the custody"},{"code":6063,"name":"CannotLiquidate","msg":"Cannot liquidate"},{"code":6064,"name":"CannotDelegateStake","msg":"Cannot delegate stake to deactivated account"},{"code":6065,"name":"ExceededMaxTotalStakedAmount","msg":"Max total staked amount exceeded"},{"code":6066,"name":"LpTokenPriceChangeLimitExceeded","msg":"LP token price change exceeds limit"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/jupiter_stable.JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr.json b/migration/khadhroony-bot2-reference/idls/jupiter_stable.JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr.json new file mode 100644 index 0000000..106f2b7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/jupiter_stable.JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr.json @@ -0,0 +1 @@ +{"address":"JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr","metadata":{"name":"jup_stable","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"create_benefactor","discriminator":[184,241,45,0,53,40,201,54],"accounts":[{"name":"operator_authority","signer":true,"relations":["operator"]},{"name":"operator"},{"name":"payer","writable":true,"signer":true},{"name":"benefactor_authority"},{"name":"benefactor","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,101,110,101,102,97,99,116,111,114]},{"kind":"account","path":"benefactor_authority"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"mint_fee_rate","type":"u16"},{"name":"redeem_fee_rate","type":"u16"}]},{"name":"create_operator","discriminator":[145,40,238,75,181,252,59,11],"accounts":[{"name":"operator_authority","signer":true,"relations":["operator"]},{"name":"payer","writable":true,"signer":true},{"name":"operator"},{"name":"new_operator_authority"},{"name":"new_operator","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,111,114]},{"kind":"account","path":"new_operator_authority"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"role","type":{"defined":{"name":"OperatorRole"}}}]},{"name":"create_vault","discriminator":[29,237,247,208,193,82,54,135],"accounts":[{"name":"operator_authority","signer":true,"relations":["operator"]},{"name":"operator"},{"name":"payer","writable":true,"signer":true},{"name":"mint"},{"name":"config"},{"name":"authority","relations":["config"]},{"name":"vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116]},{"kind":"account","path":"mint"}]}},{"name":"token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"authority"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"delete_benefactor","discriminator":[216,227,84,147,79,177,152,147],"accounts":[{"name":"operator_authority","writable":true,"signer":true,"relations":["operator"]},{"name":"operator"},{"name":"receiver","writable":true},{"name":"benefactor","writable":true}],"args":[]},{"name":"delete_operator","discriminator":[208,84,168,116,138,201,98,16],"accounts":[{"name":"operator_authority","signer":true,"relations":["operator"]},{"name":"payer","writable":true,"signer":true},{"name":"operator"},{"name":"deleted_operator","writable":true}],"args":[]},{"name":"init","discriminator":[220,59,207,236,108,250,47,100],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"upgrade_authority","signer":true},{"name":"operator","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,111,114]},{"kind":"account","path":"upgrade_authority"}]}},{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[97,117,116,104,111,114,105,116,121]}]}},{"name":"mint","writable":true,"signer":true},{"name":"metadata","writable":true},{"name":"program_data"},{"name":"program","address":"JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr"},{"name":"metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"decimals","type":"u8"},{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"manage_benefactor","discriminator":[22,231,128,62,115,219,149,14],"accounts":[{"name":"operator_authority","writable":true,"signer":true,"relations":["operator"]},{"name":"operator"},{"name":"benefactor","writable":true}],"args":[{"name":"action","type":{"defined":{"name":"BenefactorManagementAction"}}}]},{"name":"manage_config","discriminator":[119,51,144,55,24,242,232,231],"accounts":[{"name":"operator_authority","writable":true,"signer":true,"relations":["operator"]},{"name":"operator"},{"name":"config","writable":true}],"args":[{"name":"action","type":{"defined":{"name":"ConfigManagementAction"}}}]},{"name":"manage_operator","discriminator":[82,172,106,235,147,246,96,85],"accounts":[{"name":"operator_authority","signer":true,"relations":["operator"]},{"name":"operator"},{"name":"managed_operator","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"action","type":{"defined":{"name":"OperatorManagementAction"}}}]},{"name":"manage_vault","discriminator":[165,7,106,242,73,193,195,128],"accounts":[{"name":"operator_authority","signer":true,"relations":["operator"]},{"name":"operator"},{"name":"vault","writable":true}],"args":[{"name":"action","type":{"defined":{"name":"VaultManagementAction"}}}]},{"name":"mint","discriminator":[51,57,225,47,182,146,137,166],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"user_collateral_token_account","writable":true},{"name":"user_lp_token_account","writable":true},{"name":"config","writable":true},{"name":"authority"},{"name":"lp_mint","writable":true},{"name":"vault","writable":true},{"name":"vault_mint"},{"name":"custodian"},{"name":"custodian_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"custodian"},{"kind":"account","path":"vault_token_program"},{"kind":"account","path":"vault_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"benefactor","writable":true},{"name":"lp_token_program"},{"name":"vault_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount","type":"u64"},{"name":"min_amount_out","type":"u64"}]},{"name":"redeem","discriminator":[184,12,86,149,70,196,97,225],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"user_lp_token_account","writable":true},{"name":"user_collateral_token_account","writable":true},{"name":"config","writable":true},{"name":"authority"},{"name":"lp_mint","writable":true},{"name":"vault","writable":true},{"name":"vault_token_account","writable":true},{"name":"vault_mint"},{"name":"benefactor","writable":true},{"name":"lp_token_program"},{"name":"vault_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount","type":"u64"},{"name":"min_amount_out","type":"u64"}]},{"name":"withdraw","discriminator":[183,18,70,156,148,109,161,34],"accounts":[{"name":"operator_authority","signer":true,"relations":["operator"]},{"name":"operator"},{"name":"custodian"},{"name":"custodian_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"custodian"},{"kind":"account","path":"token_program"},{"kind":"account","path":"vault_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"config"},{"name":"authority"},{"name":"vault","writable":true},{"name":"vault_token_account","writable":true},{"name":"vault_mint"},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"}]}],"accounts":[{"name":"Benefactor","discriminator":[98,159,41,233,19,232,104,12]},{"name":"Config","discriminator":[155,12,170,224,30,250,204,130]},{"name":"Operator","discriminator":[219,31,188,145,69,139,204,117]},{"name":"Vault","discriminator":[211,8,232,43,2,152,117,119]}],"events":[{"name":"MintV0Event","discriminator":[217,98,231,213,105,77,68,88]},{"name":"MintV1Event","discriminator":[63,113,88,48,163,187,48,83]},{"name":"RedeemV0Event","discriminator":[50,202,68,30,122,77,84,153]},{"name":"RedeemV1Event","discriminator":[67,51,207,92,219,144,44,2]}],"errors":[{"code":6000,"name":"SomeError","msg":""},{"code":6001,"name":"AdminArrayFull","msg":"Admin Array Full"},{"code":6002,"name":"NotAuthorized","msg":"Not Authorized"},{"code":6003,"name":"BadInput","msg":"Bad Input"},{"code":6004,"name":"BenefactorDisabled","msg":"Benefactor Disabled"},{"code":6005,"name":"BenefactorActive","msg":"Benefactor Active"},{"code":6006,"name":"VaultNotActive","msg":"Vault Not Active"},{"code":6007,"name":"InsufficientAmount","msg":"Insufficient Amount"},{"code":6008,"name":"InvalidFeeRate","msg":"Invalid Fee Rate"},{"code":6009,"name":"MintLimitExceeded","msg":"Mint Limit Exceeded"},{"code":6010,"name":"RedeemLimitExceeded","msg":"Redeem Limit Exceeded"},{"code":6011,"name":"SlippageToleranceExceeded","msg":"Slippage Tolerance Exceeded"},{"code":6012,"name":"MathOverflow","msg":"Math Overflow"},{"code":6013,"name":"InvalidLPMint","msg":"Invalid LP Mint"},{"code":6014,"name":"InvalidVaultMint","msg":"Invalid Vault Mint"},{"code":6015,"name":"InvalidAuthority","msg":"Invalid Authority"},{"code":6016,"name":"InvalidVaultTokenAccount","msg":"Invalid Vault Token Account"},{"code":6017,"name":"InvalidTokenProgram","msg":"Invalid Token Program"},{"code":6018,"name":"InvalidVaultFeeTokenAccount","msg":"Invalid Vault Fee Token Account"},{"code":6019,"name":"BadOracle","msg":"Bad Oracle"},{"code":6020,"name":"NoValidPrice","msg":"No Valid Price"},{"code":6021,"name":"InvalidBenefactor","msg":"Invalid Benefactor"},{"code":6022,"name":"InvalidCustodian","msg":"Invalid Custodian"},{"code":6023,"name":"InvalidPeriodLimit","msg":"Invalid Rate Limit Window"},{"code":6024,"name":"MissingOracleAccounts","msg":"Missing Oracle Accounts"},{"code":6025,"name":"NoOraclesFound","msg":"No Oracles Found"},{"code":6026,"name":"ZeroAmount","msg":"Zero Amount"},{"code":6027,"name":"ProtocolPaused","msg":"Protocol Paused"},{"code":6028,"name":"OperatorDisabled","msg":"Operator Disabled"},{"code":6029,"name":"VaultDisabled","msg":"Vault Disabled"},{"code":6030,"name":"VaultEnabled","msg":"Vault Enabled"},{"code":6031,"name":"VaultIsDry","msg":"Vault Is Dry"},{"code":6032,"name":"InvalidPegPriceUSD","msg":"Invalid Peg Price USD"},{"code":6033,"name":"NoValidOracle","msg":"No Valid Oracle"},{"code":6034,"name":"PriceConfidenceTooWide","msg":"Price Confidence Too Wide"},{"code":6035,"name":"OperatorCannotDeleteItself","msg":"Operator Cannot Delete Itself"}],"types":[{"name":"Benefactor","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"authority","type":"pubkey"},{"name":"status","type":{"defined":{"name":"BenefactorStatus"}}},{"name":"_padding0","type":{"array":["u8",7]}},{"name":"mint_fee_rate","type":"u16"},{"name":"redeem_fee_rate","type":"u16"},{"name":"_padding1","type":{"array":["u8",4]}},{"name":"period_limits","type":{"array":[{"defined":{"name":"PeriodLimit"}},4]}},{"name":"total_minted","type":{"array":["u8",16]}},{"name":"total_redeemed","type":{"array":["u8",16]}},{"name":"fee_overrides","type":{"array":[{"defined":{"name":"FeeOverride"}},4]}},{"name":"reserved","type":{"array":["u8",96]}}]}},{"name":"BenefactorManagementAction","type":{"kind":"enum","variants":[{"name":"Disable"},{"name":"SetStatus","fields":[{"name":"status","type":{"defined":{"name":"BenefactorStatus"}}}]},{"name":"UpdateFeeRates","fields":[{"name":"mint_fee_rate","type":"u16"},{"name":"redeem_fee_rate","type":"u16"}]},{"name":"UpdatePeriodLimit","fields":[{"name":"index","type":"u8"},{"name":"duration_seconds","type":"u64"},{"name":"max_mint_amount","type":"u64"},{"name":"max_redeem_amount","type":"u64"}]},{"name":"ResetPeriodLimit","fields":[{"name":"index","type":"u8"}]},{"name":"UpdateFeeOverride","fields":[{"name":"index","type":"u8"},{"name":"mint","type":"pubkey"},{"name":"mint_fee_rate","type":"u16"},{"name":"redeem_fee_rate","type":"u16"}]},{"name":"DeleteFeeOverride","fields":[{"name":"index","type":"u8"}]}]}},{"name":"BenefactorStatus","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Active"},{"name":"Disabled"}]}},{"name":"Config","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"authority","type":"pubkey"},{"name":"token_program","type":"pubkey"},{"name":"period_limits","type":{"array":[{"defined":{"name":"PeriodLimit"}},4]}},{"name":"peg_price_usd","type":"u64"},{"name":"decimals","type":"u8"},{"name":"is_mint_redeem_enabled","type":"u8"},{"name":"authority_bump","type":"u8"},{"name":"config_bump","type":"u8"},{"name":"_padding","type":{"array":["u8",4]}},{"name":"reserved","type":{"array":["u8",192]}}]}},{"name":"ConfigManagementAction","type":{"kind":"enum","variants":[{"name":"Pause"},{"name":"UpdatePauseFlag","fields":[{"name":"is_mint_redeem_enabled","type":"bool"}]},{"name":"UpdatePeriodLimit","fields":[{"name":"index","type":"u8"},{"name":"duration_seconds","type":"u64"},{"name":"max_mint_amount","type":"u64"},{"name":"max_redeem_amount","type":"u64"}]},{"name":"ResetPeriodLimit","fields":[{"name":"index","type":"u8"}]},{"name":"SetPegPriceUSD","fields":[{"name":"peg_price_usd","type":"u64"}]}]}},{"name":"DovesOracle","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"},{"name":"reserved1","type":{"array":["u8",32]}},{"name":"reserved2","type":{"array":["u8",32]}},{"name":"reserved3","type":{"array":["u8",24]}}]}},{"name":"EmptyOracle","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"reserved","type":{"array":["u8",32]}},{"name":"reserved1","type":{"array":["u8",32]}},{"name":"reserved2","type":{"array":["u8",32]}},{"name":"reserved3","type":{"array":["u8",24]}}]}},{"name":"FeeOverride","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"mint_fee_rate","type":"u16"},{"name":"redeem_fee_rate","type":"u16"},{"name":"_padding","type":{"array":["u8",4]}}]}},{"name":"MintV0Event","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"net_amount","type":"u64"},{"name":"oracle_price","type":"u64"},{"name":"one_to_one_amount","type":"u64"},{"name":"oracle_amount","type":"u64"},{"name":"mint_amount","type":"u64"}]}},{"name":"MintV1Event","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"net_amount","type":"u64"},{"name":"oracle_price","type":"u64"},{"name":"one_to_one_amount","type":"u64"},{"name":"oracle_amount","type":"u64"},{"name":"mint_amount","type":"u64"},{"name":"collateral","type":"pubkey"},{"name":"benefactor","type":"pubkey"}]}},{"name":"Operator","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"operator_authority","type":"pubkey"},{"name":"role","type":"u64"},{"name":"status","type":{"defined":{"name":"OperatorStatus"}}},{"name":"_padding0","type":{"array":["u8",7]}},{"name":"reserved","type":{"array":["u8",128]}}]}},{"name":"OperatorManagementAction","type":{"kind":"enum","variants":[{"name":"SetStatus","fields":[{"name":"status","type":{"defined":{"name":"OperatorStatus"}}}]},{"name":"SetRole","fields":[{"name":"role","type":{"defined":{"name":"OperatorRole"}}}]},{"name":"ClearRole","fields":[{"name":"role","type":{"defined":{"name":"OperatorRole"}}}]}]}},{"name":"OperatorRole","type":{"kind":"enum","variants":[{"name":"Admin"},{"name":"PeriodManager"},{"name":"GlobalDisabler"},{"name":"VaultManager"},{"name":"VaultDisabler"},{"name":"BenefactorManager"},{"name":"BenefactorDisabler"},{"name":"PegManager"},{"name":"CollateralManager"}]}},{"name":"OperatorStatus","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Enabled"},{"name":"Disabled"}]}},{"name":"OracleConfig","type":{"kind":"enum","variants":[{"name":"None"},{"name":"Pyth","fields":[{"array":["u8",32]},"pubkey"]},{"name":"SwitchboardOnDemand","fields":["pubkey"]},{"name":"Doves","fields":["pubkey"]}]}},{"name":"OracleType","repr":{"kind":"c"},"type":{"kind":"enum","variants":[{"name":"Empty","fields":[{"defined":{"name":"EmptyOracle"}}]},{"name":"Pyth","fields":[{"defined":{"name":"PythV2Oracle"}}]},{"name":"Doves","fields":[{"defined":{"name":"DovesOracle"}}]},{"name":"SwitchboardOnDemand","fields":[{"defined":{"name":"SwitchboardOnDemandOracle"}}]}]}},{"name":"PeriodLimit","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"duration_seconds","docs":["Window duration in seconds (0 = disabled)"],"type":"u64"},{"name":"max_mint_amount","docs":["Maximum mint amount in this window"],"type":"u64"},{"name":"max_redeem_amount","docs":["Maximum redeem amount in this window"],"type":"u64"},{"name":"minted_amount","docs":["Amount minted in current window"],"type":"u64"},{"name":"redeemed_amount","docs":["Amount redeemed in current window"],"type":"u64"},{"name":"window_start","docs":["Window start timestamp"],"type":"i64"}]}},{"name":"PythV2Oracle","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"feed_id","type":{"array":["u8",32]}},{"name":"account","type":"pubkey"},{"name":"reserved1","type":{"array":["u8",32]}},{"name":"reserved2","type":{"array":["u8",24]}}]}},{"name":"RedeemV0Event","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"net_amount","type":"u64"},{"name":"oracle_price","type":"u64"},{"name":"one_to_one_amount","type":"u64"},{"name":"oracle_amount","type":"u64"},{"name":"redeem_amount","type":"u64"}]}},{"name":"RedeemV1Event","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"net_amount","type":"u64"},{"name":"oracle_price","type":"u64"},{"name":"one_to_one_amount","type":"u64"},{"name":"oracle_amount","type":"u64"},{"name":"redeem_amount","type":"u64"},{"name":"collateral","type":"pubkey"},{"name":"benefactor","type":"pubkey"}]}},{"name":"SwitchboardOnDemandOracle","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"},{"name":"reserved","type":{"array":["u8",32]}},{"name":"reserved1","type":{"array":["u8",32]}},{"name":"reserved2","type":{"array":["u8",24]}}]}},{"name":"Vault","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"custodian","type":"pubkey"},{"name":"token_account","type":"pubkey"},{"name":"token_program","type":"pubkey"},{"name":"stalesness_threshold","type":"u64"},{"name":"min_oracle_price_usd","type":"u64"},{"name":"max_oracle_price_usd","type":"u64"},{"name":"status","type":{"defined":{"name":"VaultStatus"}}},{"name":"_padding1","type":{"array":["u8",7]}},{"name":"bump","type":"u8"},{"name":"decimals","type":"u8"},{"name":"_padding2","type":{"array":["u8",6]}},{"name":"oracles","type":{"array":[{"defined":{"name":"OracleType"}},5]}},{"name":"_padding3","type":{"array":["u8",3]}},{"name":"period_limits","type":{"array":[{"defined":{"name":"PeriodLimit"}},4]}},{"name":"reserved1","type":{"array":["u8",32]}},{"name":"total_minted","type":{"array":["u8",16]}},{"name":"total_redeemed","type":{"array":["u8",16]}},{"name":"reserved","type":{"array":["u8",256]}}]}},{"name":"VaultManagementAction","type":{"kind":"enum","variants":[{"name":"Disable"},{"name":"SetStatus","fields":[{"name":"status","type":{"defined":{"name":"VaultStatus"}}}]},{"name":"UpdateOracle","fields":[{"name":"index","type":"u8"},{"name":"oracle","type":{"defined":{"name":"OracleConfig"}}}]},{"name":"UpdatePeriodLimit","fields":[{"name":"index","type":"u8"},{"name":"duration_seconds","type":"u64"},{"name":"max_mint_amount","type":"u64"},{"name":"max_redeem_amount","type":"u64"}]},{"name":"ResetPeriodLimit","fields":[{"name":"index","type":"u8"}]},{"name":"SetCustodian","fields":[{"name":"new_custodian","type":"pubkey"}]},{"name":"SetStalesnessThreshold","fields":[{"name":"stalesness_threshold","type":"u64"}]},{"name":"SetMinOraclePrice","fields":[{"name":"min_oracle_price_usd","type":"u64"}]},{"name":"SetMaxOraclePrice","fields":[{"name":"max_oracle_price_usd","type":"u64"}]}]}},{"name":"VaultStatus","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Enabled"},{"name":"Disabled"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/jupiter_v4.JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB.json b/migration/khadhroony-bot2-reference/idls/jupiter_v4.JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB.json new file mode 100644 index 0000000..cd8a53a --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/jupiter_v4.JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"jupiter","instructions":[{"name":"route","accounts":[{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"destinationTokenAccount","isMut":false,"isSigner":false}],"args":[{"name":"swapLeg","type":{"defined":"SwapLeg"}},{"name":"inAmount","type":"u64"},{"name":"quotedOutAmount","type":"u64"},{"name":"slippageBps","type":"u16"},{"name":"platformFeeBps","type":"u8"}]},{"name":"whirlpoolSwapExactOutput","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenAuthority","isMut":false,"isSigner":true},{"name":"whirlpool","isMut":true,"isSigner":false},{"name":"tokenOwnerAccountA","isMut":true,"isSigner":false},{"name":"tokenVaultA","isMut":true,"isSigner":false},{"name":"tokenOwnerAccountB","isMut":true,"isSigner":false},{"name":"tokenVaultB","isMut":true,"isSigner":false},{"name":"tickArray0","isMut":true,"isSigner":false},{"name":"tickArray1","isMut":true,"isSigner":false},{"name":"tickArray2","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false,"docs":["Oracle is currently unused and will be enabled on subsequent updates"]}],"args":[{"name":"outAmount","type":"u64"},{"name":"inAmountWithSlippage","type":{"defined":"AmountWithSlippage"}},{"name":"aToB","type":"bool"},{"name":"platformFeeBps","type":"u8"}]},{"name":"raydiumSwapExactOutput","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"ammId","isMut":true,"isSigner":false},{"name":"ammAuthority","isMut":false,"isSigner":false},{"name":"ammOpenOrders","isMut":true,"isSigner":false},{"name":"poolCoinTokenAccount","isMut":true,"isSigner":false},{"name":"poolPcTokenAccount","isMut":true,"isSigner":false},{"name":"serumProgramId","isMut":false,"isSigner":false},{"name":"serumMarket","isMut":true,"isSigner":false},{"name":"serumBids","isMut":true,"isSigner":false},{"name":"serumAsks","isMut":true,"isSigner":false},{"name":"serumEventQueue","isMut":true,"isSigner":false},{"name":"serumCoinVaultAccount","isMut":true,"isSigner":false},{"name":"serumPcVaultAccount","isMut":true,"isSigner":false},{"name":"serumVaultSigner","isMut":false,"isSigner":false},{"name":"userSourceTokenAccount","isMut":true,"isSigner":false},{"name":"userDestinationTokenAccount","isMut":true,"isSigner":false},{"name":"userSourceOwner","isMut":false,"isSigner":true}],"args":[{"name":"outAmount","type":"u64"},{"name":"inAmountWithSlippage","type":{"defined":"AmountWithSlippage"}},{"name":"platformFeeBps","type":"u8"}]},{"name":"raydiumClmmSwapExactOutput","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"ammConfig","isMut":false,"isSigner":false},{"name":"poolState","isMut":true,"isSigner":false},{"name":"inputTokenAccount","isMut":true,"isSigner":false},{"name":"outputTokenAccount","isMut":true,"isSigner":false},{"name":"inputVault","isMut":true,"isSigner":false},{"name":"outputVault","isMut":true,"isSigner":false},{"name":"observationState","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tickArray","isMut":true,"isSigner":false}],"args":[{"name":"outAmount","type":"u64"},{"name":"inAmountWithSlippage","type":{"defined":"AmountWithSlippage"}},{"name":"platformFeeBps","type":"u8"}]},{"name":"createOpenOrders","accounts":[{"name":"openOrders","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"market","isMut":false,"isSigner":false}],"args":[]},{"name":"createOpenOrderV2","accounts":[{"name":"openOrders","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"market","isMut":false,"isSigner":false}],"args":[]},{"name":"mercurialSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"swapState","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"poolAuthority","isMut":false,"isSigner":false},{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"sourceTokenAccount","isMut":true,"isSigner":false},{"name":"destinationTokenAccount","isMut":true,"isSigner":false}],"args":[]},{"name":"cykuraSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"signer","isMut":false,"isSigner":true},{"name":"factoryState","isMut":false,"isSigner":false},{"name":"poolState","isMut":true,"isSigner":false},{"name":"inputTokenAccount","isMut":true,"isSigner":false},{"name":"outputTokenAccount","isMut":true,"isSigner":false},{"name":"inputVault","isMut":true,"isSigner":false},{"name":"outputVault","isMut":true,"isSigner":false},{"name":"lastObservationState","isMut":true,"isSigner":false},{"name":"coreProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"serumSwap","accounts":[{"name":"market","accounts":[{"name":"market","isMut":true,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"requestQueue","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"coinVault","isMut":true,"isSigner":false},{"name":"pcVault","isMut":true,"isSigner":false},{"name":"vaultSigner","isMut":false,"isSigner":false}]},{"name":"authority","isMut":false,"isSigner":true},{"name":"orderPayerTokenAccount","isMut":true,"isSigner":false},{"name":"coinWallet","isMut":true,"isSigner":false},{"name":"pcWallet","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"saberSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swap","isMut":false,"isSigner":false},{"name":"swapAuthority","isMut":false,"isSigner":false},{"name":"userAuthority","isMut":false,"isSigner":false},{"name":"inputUserAccount","isMut":true,"isSigner":false},{"name":"inputTokenAccount","isMut":true,"isSigner":false},{"name":"outputUserAccount","isMut":true,"isSigner":false},{"name":"outputTokenAccount","isMut":true,"isSigner":false},{"name":"feesTokenAccount","isMut":true,"isSigner":false}],"args":[]},{"name":"saberAddDecimals","accounts":[{"name":"addDecimalsProgram","isMut":false,"isSigner":false},{"name":"wrapper","isMut":false,"isSigner":false},{"name":"wrapperMint","isMut":true,"isSigner":false},{"name":"wrapperUnderlyingTokens","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"userUnderlyingTokens","isMut":true,"isSigner":false},{"name":"userWrappedTokens","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"tokenSwap","accounts":[{"name":"tokenSwapProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swap","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"source","isMut":true,"isSigner":false},{"name":"swapSource","isMut":true,"isSigner":false},{"name":"swapDestination","isMut":true,"isSigner":false},{"name":"destination","isMut":true,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"poolFee","isMut":true,"isSigner":false}],"args":[]},{"name":"senchaSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swap","isMut":true,"isSigner":false},{"name":"userAuthority","isMut":false,"isSigner":false},{"name":"inputUserAccount","isMut":true,"isSigner":false},{"name":"inputTokenAccount","isMut":true,"isSigner":false},{"name":"inputFeesAccount","isMut":true,"isSigner":false},{"name":"outputUserAccount","isMut":true,"isSigner":false},{"name":"outputTokenAccount","isMut":true,"isSigner":false},{"name":"outputFeesAccount","isMut":true,"isSigner":false}],"args":[]},{"name":"stepSwap","accounts":[{"name":"tokenSwapProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swap","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"source","isMut":true,"isSigner":false},{"name":"swapSource","isMut":true,"isSigner":false},{"name":"swapDestination","isMut":true,"isSigner":false},{"name":"destination","isMut":true,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"poolFee","isMut":true,"isSigner":false}],"args":[]},{"name":"cropperSwap","accounts":[{"name":"tokenSwapProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"swap","isMut":false,"isSigner":false},{"name":"swapState","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"source","isMut":true,"isSigner":false},{"name":"swapSource","isMut":true,"isSigner":false},{"name":"swapDestination","isMut":true,"isSigner":false},{"name":"destination","isMut":true,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"poolFee","isMut":true,"isSigner":false}],"args":[]},{"name":"raydiumSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"ammId","isMut":true,"isSigner":false},{"name":"ammAuthority","isMut":false,"isSigner":false},{"name":"ammOpenOrders","isMut":true,"isSigner":false},{"name":"poolCoinTokenAccount","isMut":true,"isSigner":false},{"name":"poolPcTokenAccount","isMut":true,"isSigner":false},{"name":"serumProgramId","isMut":false,"isSigner":false},{"name":"serumMarket","isMut":true,"isSigner":false},{"name":"serumBids","isMut":true,"isSigner":false},{"name":"serumAsks","isMut":true,"isSigner":false},{"name":"serumEventQueue","isMut":true,"isSigner":false},{"name":"serumCoinVaultAccount","isMut":true,"isSigner":false},{"name":"serumPcVaultAccount","isMut":true,"isSigner":false},{"name":"serumVaultSigner","isMut":false,"isSigner":false},{"name":"userSourceTokenAccount","isMut":true,"isSigner":false},{"name":"userDestinationTokenAccount","isMut":true,"isSigner":false},{"name":"userSourceOwner","isMut":false,"isSigner":true}],"args":[]},{"name":"cremaSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"clmmConfig","isMut":false,"isSigner":false},{"name":"clmmpool","isMut":true,"isSigner":false},{"name":"tokenA","isMut":false,"isSigner":false},{"name":"tokenB","isMut":false,"isSigner":false},{"name":"accountA","isMut":true,"isSigner":false},{"name":"accountB","isMut":true,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"tickArrayMap","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"partner","isMut":false,"isSigner":false},{"name":"partnerAtaA","isMut":true,"isSigner":false},{"name":"partnerAtaB","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"lifinitySwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"amm","isMut":false,"isSigner":false},{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"sourceInfo","isMut":true,"isSigner":false},{"name":"destinationInfo","isMut":true,"isSigner":false},{"name":"swapSource","isMut":true,"isSigner":false},{"name":"swapDestination","isMut":true,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"feeAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"pythAccount","isMut":false,"isSigner":false},{"name":"pythPcAccount","isMut":false,"isSigner":false},{"name":"configAccount","isMut":true,"isSigner":false}],"args":[]},{"name":"marinadeDeposit","accounts":[{"name":"marinadeFinanceProgram","isMut":false,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"msolMint","isMut":true,"isSigner":false},{"name":"liqPoolSolLegPda","isMut":true,"isSigner":false},{"name":"liqPoolMsolLeg","isMut":true,"isSigner":false},{"name":"liqPoolMsolLegAuthority","isMut":false,"isSigner":false},{"name":"reservePda","isMut":true,"isSigner":false},{"name":"transferFrom","isMut":true,"isSigner":false},{"name":"mintTo","isMut":true,"isSigner":false},{"name":"msolMintAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"userWsolTokenAccount","isMut":true,"isSigner":false},{"name":"tempWsolTokenAccount","isMut":true,"isSigner":false},{"name":"userTransferAuthority","isMut":true,"isSigner":true},{"name":"wsolMint","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"marinadeUnstake","accounts":[{"name":"marinadeFinanceProgram","isMut":false,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"msolMint","isMut":true,"isSigner":false},{"name":"liqPoolSolLegPda","isMut":true,"isSigner":false},{"name":"liqPoolMsolLeg","isMut":true,"isSigner":false},{"name":"treasuryMsolAccount","isMut":true,"isSigner":false},{"name":"getMsolFrom","isMut":true,"isSigner":false},{"name":"getMsolFromAuthority","isMut":false,"isSigner":true},{"name":"transferSolTo","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"userWsolTokenAccount","isMut":true,"isSigner":false}],"args":[]},{"name":"aldrinSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"poolSigner","isMut":false,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"baseTokenVault","isMut":true,"isSigner":false},{"name":"quoteTokenVault","isMut":true,"isSigner":false},{"name":"feePoolTokenAccount","isMut":true,"isSigner":false},{"name":"walletAuthority","isMut":false,"isSigner":true},{"name":"userBaseTokenAccount","isMut":true,"isSigner":false},{"name":"userQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"aldrinV2Swap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"poolSigner","isMut":false,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"baseTokenVault","isMut":true,"isSigner":false},{"name":"quoteTokenVault","isMut":true,"isSigner":false},{"name":"feePoolTokenAccount","isMut":true,"isSigner":false},{"name":"walletAuthority","isMut":false,"isSigner":true},{"name":"userBaseTokenAccount","isMut":true,"isSigner":false},{"name":"userQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"curve","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"whirlpoolSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenAuthority","isMut":false,"isSigner":true},{"name":"whirlpool","isMut":true,"isSigner":false},{"name":"tokenOwnerAccountA","isMut":true,"isSigner":false},{"name":"tokenVaultA","isMut":true,"isSigner":false},{"name":"tokenOwnerAccountB","isMut":true,"isSigner":false},{"name":"tokenVaultB","isMut":true,"isSigner":false},{"name":"tickArray0","isMut":true,"isSigner":false},{"name":"tickArray1","isMut":true,"isSigner":false},{"name":"tickArray2","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false,"docs":["Oracle is currently unused and will be enabled on subsequent updates"]}],"args":[]},{"name":"invariantSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"tickmap","isMut":true,"isSigner":false},{"name":"accountX","isMut":true,"isSigner":false},{"name":"accountY","isMut":true,"isSigner":false},{"name":"reserveX","isMut":true,"isSigner":false},{"name":"reserveY","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"meteoraSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"userSourceToken","isMut":true,"isSigner":false},{"name":"userDestinationToken","isMut":true,"isSigner":false},{"name":"aVault","isMut":true,"isSigner":false},{"name":"bVault","isMut":true,"isSigner":false},{"name":"aTokenVault","isMut":true,"isSigner":false},{"name":"bTokenVault","isMut":true,"isSigner":false},{"name":"aVaultLpMint","isMut":true,"isSigner":false},{"name":"bVaultLpMint","isMut":true,"isSigner":false},{"name":"aVaultLp","isMut":true,"isSigner":false},{"name":"bVaultLp","isMut":true,"isSigner":false},{"name":"adminTokenFee","isMut":true,"isSigner":false},{"name":"user","isMut":false,"isSigner":true},{"name":"vaultProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"goosefxSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"controller","isMut":false,"isSigner":false},{"name":"pair","isMut":true,"isSigner":false},{"name":"sslIn","isMut":true,"isSigner":false},{"name":"sslOut","isMut":true,"isSigner":false},{"name":"liabilityVaultIn","isMut":true,"isSigner":false},{"name":"swappedLiabilityVaultIn","isMut":true,"isSigner":false},{"name":"liabilityVaultOut","isMut":true,"isSigner":false},{"name":"swappedLiabilityVaultOut","isMut":true,"isSigner":false},{"name":"userInAta","isMut":true,"isSigner":false},{"name":"userOutAta","isMut":true,"isSigner":false},{"name":"feeCollectorAta","isMut":true,"isSigner":false},{"name":"userWallet","isMut":false,"isSigner":true},{"name":"feeCollector","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"deltafiSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"marketConfig","isMut":false,"isSigner":false},{"name":"swapInfo","isMut":true,"isSigner":false},{"name":"userSourceToken","isMut":true,"isSigner":false},{"name":"userDestinationToken","isMut":true,"isSigner":false},{"name":"swapSourceToken","isMut":true,"isSigner":false},{"name":"swapDestinationToken","isMut":true,"isSigner":false},{"name":"deltafiUser","isMut":true,"isSigner":false},{"name":"adminDestinationToken","isMut":true,"isSigner":false},{"name":"pythPriceBase","isMut":false,"isSigner":false},{"name":"pythPriceQuote","isMut":false,"isSigner":false},{"name":"userAuthority","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"balansolSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"pool","isMut":true,"isSigner":false},{"name":"taxMan","isMut":true,"isSigner":false},{"name":"bidMint","isMut":false,"isSigner":false},{"name":"treasurer","isMut":false,"isSigner":false},{"name":"srcTreasury","isMut":true,"isSigner":false},{"name":"srcAssociatedTokenAccount","isMut":true,"isSigner":false},{"name":"askMint","isMut":false,"isSigner":false},{"name":"dstTreasury","isMut":true,"isSigner":false},{"name":"dstAssociatedTokenAccount","isMut":true,"isSigner":false},{"name":"dstTokenAccountTaxman","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"marcoPoloSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"tokenX","isMut":false,"isSigner":false},{"name":"tokenY","isMut":false,"isSigner":false},{"name":"poolXAccount","isMut":true,"isSigner":false},{"name":"poolYAccount","isMut":true,"isSigner":false},{"name":"swapperXAccount","isMut":true,"isSigner":false},{"name":"swapperYAccount","isMut":true,"isSigner":false},{"name":"swapper","isMut":true,"isSigner":true},{"name":"referrerXAccount","isMut":true,"isSigner":false},{"name":"referrerYAccount","isMut":true,"isSigner":false},{"name":"referrer","isMut":true,"isSigner":false},{"name":"programAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"dradexSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"pair","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"dexUser","isMut":false,"isSigner":false},{"name":"marketUser","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"t0Vault","isMut":true,"isSigner":false},{"name":"t1Vault","isMut":true,"isSigner":false},{"name":"t0User","isMut":true,"isSigner":false},{"name":"t1User","isMut":true,"isSigner":false},{"name":"master","isMut":false,"isSigner":false},{"name":"signer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"logger","isMut":false,"isSigner":false}],"args":[]},{"name":"lifinityV2Swap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"amm","isMut":true,"isSigner":false},{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"sourceInfo","isMut":true,"isSigner":false},{"name":"destinationInfo","isMut":true,"isSigner":false},{"name":"swapSource","isMut":true,"isSigner":false},{"name":"swapDestination","isMut":true,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"feeAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"oracleMainAccount","isMut":false,"isSigner":false},{"name":"oracleSubAccount","isMut":false,"isSigner":false},{"name":"oraclePcAccount","isMut":false,"isSigner":false}],"args":[]},{"name":"raydiumClmmSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"ammConfig","isMut":false,"isSigner":false},{"name":"poolState","isMut":true,"isSigner":false},{"name":"inputTokenAccount","isMut":true,"isSigner":false},{"name":"outputTokenAccount","isMut":true,"isSigner":false},{"name":"inputVault","isMut":true,"isSigner":false},{"name":"outputVault","isMut":true,"isSigner":false},{"name":"observationState","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tickArray","isMut":true,"isSigner":false}],"args":[]},{"name":"phoenixSwap","accounts":[{"name":"swapProgram","isMut":false,"isSigner":false},{"name":"logAuthority","isMut":false,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"trader","isMut":false,"isSigner":true},{"name":"baseAccount","isMut":true,"isSigner":false},{"name":"quoteAccount","isMut":true,"isSigner":false},{"name":"baseVault","isMut":true,"isSigner":false},{"name":"quoteVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"claimBonk","accounts":[{"name":"openOrders","isMut":false,"isSigner":false},{"name":"bonkMint","isMut":false,"isSigner":false},{"name":"openOrdersBonkTokenAccount","isMut":true,"isSigner":false},{"name":"market","isMut":false,"isSigner":false},{"name":"openOrdersOwner","isMut":true,"isSigner":false},{"name":"claimerTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]}],"types":[{"name":"AmountWithSlippage","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"slippageBps","type":"u16"}]}},{"name":"SplitLegDeeper","type":{"kind":"struct","fields":[{"name":"percent","type":"u8"},{"name":"swapLeg","type":{"defined":"SwapLegSwap"}}]}},{"name":"SplitLeg","type":{"kind":"struct","fields":[{"name":"percent","type":"u8"},{"name":"swapLeg","type":{"defined":"SwapLegDeeper"}}]}},{"name":"Side","type":{"kind":"enum","variants":[{"name":"Bid"},{"name":"Ask"}]}},{"name":"SwapLegSwap","type":{"kind":"enum","variants":[{"name":"PlaceholderOne"},{"name":"PlaceholderTwo"},{"name":"Swap","fields":[{"name":"swap","type":{"defined":"Swap"}}]}]}},{"name":"SwapLegDeeper","type":{"kind":"enum","variants":[{"name":"Chain","fields":[{"name":"swap_legs","type":{"vec":{"defined":"SwapLegSwap"}}}]},{"name":"Split","fields":[{"name":"split_legs","type":{"vec":{"defined":"SplitLegDeeper"}}}]},{"name":"Swap","fields":[{"name":"swap","type":{"defined":"Swap"}}]}]}},{"name":"SwapLeg","type":{"kind":"enum","variants":[{"name":"Chain","fields":[{"name":"swap_legs","type":{"vec":{"defined":"SwapLegDeeper"}}}]},{"name":"Split","fields":[{"name":"split_legs","type":{"vec":{"defined":"SplitLeg"}}}]},{"name":"Swap","fields":[{"name":"swap","type":{"defined":"Swap"}}]}]}},{"name":"Swap","type":{"kind":"enum","variants":[{"name":"Saber"},{"name":"SaberAddDecimalsDeposit"},{"name":"SaberAddDecimalsWithdraw"},{"name":"TokenSwap"},{"name":"Sencha"},{"name":"Step"},{"name":"Cropper"},{"name":"Raydium"},{"name":"Crema","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"Lifinity"},{"name":"Mercurial"},{"name":"Cykura"},{"name":"Serum","fields":[{"name":"side","type":{"defined":"Side"}}]},{"name":"MarinadeDeposit"},{"name":"MarinadeUnstake"},{"name":"Aldrin","fields":[{"name":"side","type":{"defined":"Side"}}]},{"name":"AldrinV2","fields":[{"name":"side","type":{"defined":"Side"}}]},{"name":"Whirlpool","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"Invariant","fields":[{"name":"x_to_y","type":"bool"}]},{"name":"Meteora"},{"name":"GooseFX"},{"name":"DeltaFi","fields":[{"name":"stable","type":"bool"}]},{"name":"Balansol"},{"name":"MarcoPolo","fields":[{"name":"x_to_y","type":"bool"}]},{"name":"Dradex","fields":[{"name":"side","type":{"defined":"Side"}}]},{"name":"LifinityV2"},{"name":"RaydiumClmm"},{"name":"Openbook","fields":[{"name":"side","type":{"defined":"Side"}}]},{"name":"Phoenix","fields":[{"name":"side","type":{"defined":"Side"}}]}]}}],"events":[{"name":"Swap","fields":[{"name":"amm","type":"publicKey","index":false},{"name":"inputMint","type":"publicKey","index":false},{"name":"inputAmount","type":"u64","index":false},{"name":"outputMint","type":"publicKey","index":false},{"name":"outputAmount","type":"u64","index":false}]},{"name":"Fee","fields":[{"name":"account","type":"publicKey","index":false},{"name":"mint","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"EmptyRoute","msg":"Empty route"},{"code":6001,"name":"SlippageToleranceExceeded","msg":"Slippage tolerance exceeded"},{"code":6002,"name":"InvalidCalculation","msg":"Invalid calculation"},{"code":6003,"name":"MissingPlatformFeeAccount","msg":"Missing platform fee account"},{"code":6004,"name":"InvalidSlippage","msg":"Invalid slippage"},{"code":6005,"name":"NotEnoughPercent","msg":"Not enough percent to 100"},{"code":6006,"name":"InAmountsStackIsEmpty","msg":"In amounts stack is empty"},{"code":6007,"name":"OutAmountsStackIsEmpty","msg":"Out amounts stack is empty"},{"code":6008,"name":"NotEnoughAccountKeys","msg":"Not Enough Account keys"},{"code":6009,"name":"NonZeroMinimumOutAmountNotSupported","msg":"Non zero minimum out amount not supported"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/jupiter_v6.JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4.json b/migration/khadhroony-bot2-reference/idls/jupiter_v6.JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4.json new file mode 100644 index 0000000..f385542 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/jupiter_v6.JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4.json @@ -0,0 +1 @@ +{"address":"JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4","metadata":{"name":"jupiter","version":"0.1.0","spec":"0.1.0","description":"Jupiter aggregator program"},"instructions":[{"name":"claim","discriminator":[62,198,214,193,213,159,108,210],"accounts":[{"name":"wallet","writable":true,"address":"7JQeyNK55fkUPUmEotupBFpiBGpgEQYLe8Ht1VdSfxcP"},{"name":"program_authority","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"id","type":"u8"}],"returns":"u64"},{"name":"claim_token","discriminator":[116,206,27,191,166,19,0,73],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"wallet","address":"7JQeyNK55fkUPUmEotupBFpiBGpgEQYLe8Ht1VdSfxcP"},{"name":"program_authority"},{"name":"program_token_account","writable":true},{"name":"destination_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"wallet"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"mint"},{"name":"token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"id","type":"u8"}],"returns":"u64"},{"name":"close_token","discriminator":[26,74,236,151,104,64,183,249],"accounts":[{"name":"operator","signer":true,"address":"9RAufBfjGQjDfrwxeyKmZWPADHSb8HcoqCdrmpqvCr1g"},{"name":"wallet","writable":true,"address":"7JQeyNK55fkUPUmEotupBFpiBGpgEQYLe8Ht1VdSfxcP"},{"name":"program_authority"},{"name":"program_token_account","writable":true},{"name":"mint","writable":true},{"name":"token_program"}],"args":[{"name":"id","type":"u8"},{"name":"burn_all","type":"bool"}]},{"name":"create_token_ledger","discriminator":[232,242,197,253,240,143,129,52],"accounts":[{"name":"token_ledger","writable":true,"signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_token_account","discriminator":[147,241,123,100,244,132,174,118],"accounts":[{"name":"token_account","writable":true},{"name":"user","writable":true,"signer":true},{"name":"mint"},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bump","type":"u8"}]},{"name":"close_wsol_token_account","discriminator":[203,129,103,133,197,125,107,86],"accounts":[{"name":"token_account","writable":true},{"name":"user","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"exact_out_route","discriminator":[208,51,239,151,123,43,237,92],"accounts":[{"name":"token_program"},{"name":"user_transfer_authority","signer":true},{"name":"user_source_token_account","writable":true},{"name":"user_destination_token_account","writable":true},{"name":"destination_token_account","writable":true,"optional":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"token_2022_program","optional":true},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStep"}}}},{"name":"out_amount","type":"u64"},{"name":"quoted_in_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u8"}],"returns":"u64"},{"name":"route","discriminator":[229,23,203,151,122,227,173,42],"accounts":[{"name":"token_program"},{"name":"user_transfer_authority","signer":true},{"name":"user_source_token_account","writable":true},{"name":"user_destination_token_account","writable":true},{"name":"destination_token_account","writable":true,"optional":true},{"name":"destination_mint"},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStep"}}}},{"name":"in_amount","type":"u64"},{"name":"quoted_out_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u8"}],"returns":"u64"},{"name":"route_with_token_ledger","discriminator":[150,86,71,116,167,93,14,104],"accounts":[{"name":"token_program"},{"name":"user_transfer_authority","signer":true},{"name":"user_source_token_account","writable":true},{"name":"user_destination_token_account","writable":true},{"name":"destination_token_account","writable":true,"optional":true},{"name":"destination_mint"},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"token_ledger"},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStep"}}}},{"name":"quoted_out_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u8"}],"returns":"u64"},{"name":"set_token_ledger","discriminator":[228,85,185,112,78,79,77,2],"accounts":[{"name":"token_ledger","writable":true},{"name":"token_account"}],"args":[]},{"name":"shared_accounts_exact_out_route","discriminator":[176,209,105,168,154,125,69,62],"accounts":[{"name":"token_program"},{"name":"program_authority"},{"name":"user_transfer_authority","signer":true},{"name":"source_token_account","writable":true},{"name":"program_source_token_account","writable":true},{"name":"program_destination_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"token_2022_program","optional":true},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"id","type":"u8"},{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStep"}}}},{"name":"out_amount","type":"u64"},{"name":"quoted_in_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u8"}],"returns":"u64"},{"name":"shared_accounts_route","discriminator":[193,32,155,51,65,214,156,129],"accounts":[{"name":"token_program"},{"name":"program_authority"},{"name":"user_transfer_authority","signer":true},{"name":"source_token_account","writable":true},{"name":"program_source_token_account","writable":true},{"name":"program_destination_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"token_2022_program","optional":true},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"id","type":"u8"},{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStep"}}}},{"name":"in_amount","type":"u64"},{"name":"quoted_out_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u8"}],"returns":"u64"},{"name":"shared_accounts_route_with_token_ledger","discriminator":[230,121,143,80,119,159,106,170],"accounts":[{"name":"token_program"},{"name":"program_authority"},{"name":"user_transfer_authority","signer":true},{"name":"source_token_account","writable":true},{"name":"program_source_token_account","writable":true},{"name":"program_destination_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"token_2022_program","optional":true},{"name":"token_ledger"},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"id","type":"u8"},{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStep"}}}},{"name":"quoted_out_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u8"}],"returns":"u64"},{"name":"exact_out_route_v2","discriminator":[157,138,184,82,21,244,243,36],"accounts":[{"name":"user_transfer_authority","signer":true},{"name":"user_source_token_account","writable":true},{"name":"user_destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"source_token_program"},{"name":"destination_token_program"},{"name":"destination_token_account","writable":true,"optional":true},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"out_amount","type":"u64"},{"name":"quoted_in_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u16"},{"name":"positive_slippage_bps","type":"u16"},{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStepV2"}}}}],"returns":"u64"},{"name":"route_v2","discriminator":[187,100,250,204,49,196,175,20],"accounts":[{"name":"user_transfer_authority","signer":true},{"name":"user_source_token_account","writable":true},{"name":"user_destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"source_token_program"},{"name":"destination_token_program"},{"name":"destination_token_account","writable":true,"optional":true},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"in_amount","type":"u64"},{"name":"quoted_out_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u16"},{"name":"positive_slippage_bps","type":"u16"},{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStepV2"}}}}],"returns":"u64"},{"name":"shared_accounts_exact_out_route_v2","discriminator":[53,96,229,202,216,187,250,24],"accounts":[{"name":"program_authority"},{"name":"user_transfer_authority","signer":true},{"name":"source_token_account","writable":true},{"name":"program_source_token_account","writable":true},{"name":"program_destination_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"source_token_program"},{"name":"destination_token_program"},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"id","type":"u8"},{"name":"out_amount","type":"u64"},{"name":"quoted_in_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u16"},{"name":"positive_slippage_bps","type":"u16"},{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStepV2"}}}}],"returns":"u64"},{"name":"shared_accounts_route_v2","discriminator":[209,152,83,147,124,254,216,233],"accounts":[{"name":"program_authority"},{"name":"user_transfer_authority","signer":true},{"name":"source_token_account","writable":true},{"name":"program_source_token_account","writable":true},{"name":"program_destination_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"source_token_program"},{"name":"destination_token_program"},{"name":"event_authority","address":"D8cy77BBepLMngZx6ZukaTff5hCt1HrWyKk3Hnd9oitf"},{"name":"program"}],"args":[{"name":"id","type":"u8"},{"name":"in_amount","type":"u64"},{"name":"quoted_out_amount","type":"u64"},{"name":"slippage_bps","type":"u16"},{"name":"platform_fee_bps","type":"u16"},{"name":"positive_slippage_bps","type":"u16"},{"name":"route_plan","type":{"vec":{"defined":{"name":"RoutePlanStepV2"}}}}],"returns":"u64"}],"accounts":[{"name":"TokenLedger","discriminator":[156,247,9,188,54,108,85,77]}],"events":[{"name":"FeeEvent","discriminator":[73,79,78,127,184,213,13,220]},{"name":"SwapEvent","discriminator":[64,198,205,232,38,8,113,226]},{"name":"SwapsEvent","discriminator":[152,47,78,235,192,96,110,106]},{"name":"CandidateSwapResults","discriminator":[45,9,244,30,229,52,168,123]},{"name":"CandidateSwapQuoteError","discriminator":[248,134,37,55,145,177,114,79]},{"name":"BestSwapOutAmountViolation","discriminator":[124,66,196,51,218,173,46,93]}],"errors":[{"code":6000,"name":"EmptyRoute","msg":"Empty route"},{"code":6001,"name":"SlippageToleranceExceeded","msg":"Slippage tolerance exceeded"},{"code":6002,"name":"InvalidCalculation","msg":"Invalid calculation"},{"code":6003,"name":"MissingPlatformFeeAccount","msg":"Missing platform fee account"},{"code":6004,"name":"InvalidSlippage","msg":"Invalid slippage"},{"code":6005,"name":"NotEnoughPercent","msg":"Not enough percent to 100"},{"code":6006,"name":"InvalidInputIndex","msg":"Token input index is invalid"},{"code":6007,"name":"InvalidOutputIndex","msg":"Token output index is invalid"},{"code":6008,"name":"NotEnoughAccountKeys","msg":"Not Enough Account keys"},{"code":6009,"name":"NonZeroMinimumOutAmountNotSupported","msg":"Non zero minimum out amount not supported"},{"code":6010,"name":"InvalidRoutePlan","msg":"Invalid route plan"},{"code":6011,"name":"InvalidReferralAuthority","msg":"Invalid referral authority"},{"code":6012,"name":"LedgerTokenAccountDoesNotMatch","msg":"Token account doesn't match the ledger"},{"code":6013,"name":"InvalidTokenLedger","msg":"Invalid token ledger"},{"code":6014,"name":"IncorrectTokenProgramID","msg":"Token program ID is invalid"},{"code":6015,"name":"TokenProgramNotProvided","msg":"Token program not provided"},{"code":6016,"name":"SwapNotSupported","msg":"Swap not supported"},{"code":6017,"name":"ExactOutAmountNotMatched","msg":"Exact out amount doesn't match"},{"code":6018,"name":"SourceAndDestinationMintCannotBeTheSame","msg":"Source mint and destination mint cannot the same"},{"code":6019,"name":"InvalidMint","msg":"Invalid mint"},{"code":6020,"name":"InvalidProgramAuthority","msg":"Invalid program authority"},{"code":6021,"name":"InvalidOutputTokenAccount","msg":"Invalid output token account"},{"code":6022,"name":"InvalidFeeWallet","msg":"Invalid fee wallet"},{"code":6023,"name":"InvalidAuthority","msg":"Invalid authority"},{"code":6024,"name":"InsufficientFunds","msg":"Insufficient funds"},{"code":6025,"name":"InvalidTokenAccount","msg":"Invalid token account"},{"code":6026,"name":"BondingCurveAlreadyCompleted","msg":"Bonding curve already completed"}],"types":[{"name":"FeeEvent","type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"RemainingAccountsInfo","type":{"kind":"struct","fields":[{"name":"slices","type":{"vec":{"defined":{"name":"RemainingAccountsSlice"}}}}]}},{"name":"RemainingAccountsSlice","type":{"kind":"struct","fields":[{"name":"accounts_type","type":"u8"},{"name":"length","type":"u8"}]}},{"name":"AccountsType","type":{"kind":"enum","variants":[{"name":"TransferHookA"},{"name":"TransferHookB"},{"name":"TransferHookReward"},{"name":"TransferHookInput"},{"name":"TransferHookIntermediate"},{"name":"TransferHookOutput"},{"name":"SupplementalTickArrays"},{"name":"SupplementalTickArraysOne"},{"name":"SupplementalTickArraysTwo"}]}},{"name":"DefiTunaAccountsType","type":{"kind":"enum","variants":[{"name":"TransferHookA"},{"name":"TransferHookB"},{"name":"TransferHookInput"},{"name":"TransferHookIntermediate"},{"name":"TransferHookOutput"},{"name":"SupplementalTickArrays"},{"name":"SupplementalTickArraysOne"},{"name":"SupplementalTickArraysTwo"}]}},{"name":"RoutePlanStep","type":{"kind":"struct","fields":[{"name":"swap","type":{"defined":{"name":"Swap"}}},{"name":"percent","type":"u8"},{"name":"input_index","type":"u8"},{"name":"output_index","type":"u8"}]}},{"name":"RoutePlanStepV2","type":{"kind":"struct","fields":[{"name":"swap","type":{"defined":{"name":"Swap"}}},{"name":"bps","type":"u16"},{"name":"input_index","type":"u8"},{"name":"output_index","type":"u8"}]}},{"name":"Side","type":{"kind":"enum","variants":[{"name":"Bid"},{"name":"Ask"}]}},{"name":"BisonFiPredictSide","type":{"kind":"enum","variants":[{"name":"Yes"},{"name":"No"}]}},{"name":"Swap","type":{"kind":"enum","variants":[{"name":"Saber"},{"name":"SaberAddDecimalsDeposit"},{"name":"SaberAddDecimalsWithdraw"},{"name":"TokenSwap"},{"name":"Sencha"},{"name":"Step"},{"name":"Cropper"},{"name":"Raydium"},{"name":"Crema","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"Lifinity"},{"name":"Mercurial"},{"name":"Cykura"},{"name":"Serum","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"MarinadeDeposit"},{"name":"MarinadeUnstake"},{"name":"Aldrin","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"AldrinV2","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"Whirlpool","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"Invariant","fields":[{"name":"x_to_y","type":"bool"}]},{"name":"Meteora"},{"name":"GooseFX"},{"name":"DeltaFi","fields":[{"name":"stable","type":"bool"}]},{"name":"Balansol"},{"name":"MarcoPolo","fields":[{"name":"x_to_y","type":"bool"}]},{"name":"Dradex","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"LifinityV2"},{"name":"RaydiumClmm"},{"name":"Openbook","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"Phoenix","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"Symmetry","fields":[{"name":"from_token_id","type":"u64"},{"name":"to_token_id","type":"u64"}]},{"name":"TokenSwapV2"},{"name":"HeliumTreasuryManagementRedeemV0"},{"name":"StakeDexStakeWrappedSol"},{"name":"StakeDexSwapViaStake","fields":[{"name":"bridge_stake_seed","type":"u32"}]},{"name":"GooseFXV2"},{"name":"Perps"},{"name":"PerpsAddLiquidity"},{"name":"PerpsRemoveLiquidity"},{"name":"MeteoraDlmm"},{"name":"OpenBookV2","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"RaydiumClmmV2"},{"name":"StakeDexPrefundWithdrawStakeAndDepositStake","fields":[{"name":"bridge_stake_seed","type":"u32"}]},{"name":"Clone","fields":[{"name":"pool_index","type":"u8"},{"name":"quantity_is_input","type":"bool"},{"name":"quantity_is_collateral","type":"bool"}]},{"name":"SanctumS","fields":[{"name":"src_lst_value_calc_accs","type":"u8"},{"name":"dst_lst_value_calc_accs","type":"u8"},{"name":"src_lst_index","type":"u32"},{"name":"dst_lst_index","type":"u32"}]},{"name":"SanctumSAddLiquidity","fields":[{"name":"lst_value_calc_accs","type":"u8"},{"name":"lst_index","type":"u32"}]},{"name":"SanctumSRemoveLiquidity","fields":[{"name":"lst_value_calc_accs","type":"u8"},{"name":"lst_index","type":"u32"}]},{"name":"RaydiumCP"},{"name":"WhirlpoolSwapV2","fields":[{"name":"a_to_b","type":"bool"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"OneIntro"},{"name":"PumpWrappedBuy"},{"name":"PumpWrappedSell"},{"name":"PerpsV2"},{"name":"PerpsV2AddLiquidity"},{"name":"PerpsV2RemoveLiquidity"},{"name":"MoonshotWrappedBuy"},{"name":"MoonshotWrappedSell"},{"name":"StabbleStableSwap"},{"name":"StabbleWeightedSwap"},{"name":"Obric","fields":[{"name":"x_to_y","type":"bool"}]},{"name":"FoxBuyFromEstimatedCost"},{"name":"FoxClaimPartial","fields":[{"name":"is_y","type":"bool"}]},{"name":"SolFi","fields":[{"name":"is_quote_to_base","type":"bool"}]},{"name":"SolayerDelegateNoInit"},{"name":"SolayerUndelegateNoInit"},{"name":"TokenMill","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"DaosFunBuy"},{"name":"DaosFunSell"},{"name":"ZeroFi"},{"name":"StakeDexWithdrawWrappedSol"},{"name":"VirtualsBuy"},{"name":"VirtualsSell"},{"name":"Perena","fields":[{"name":"in_index","type":"u8"},{"name":"out_index","type":"u8"}]},{"name":"PumpSwapBuy"},{"name":"PumpSwapSell"},{"name":"Gamma"},{"name":"MeteoraDlmmSwapV2","fields":[{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"Woofi"},{"name":"MeteoraDammV2"},{"name":"MeteoraDynamicBondingCurveSwap"},{"name":"StabbleStableSwapV2"},{"name":"StabbleWeightedSwapV2"},{"name":"RaydiumLaunchlabBuy","fields":[{"name":"share_fee_rate","type":"u64"}]},{"name":"RaydiumLaunchlabSell","fields":[{"name":"share_fee_rate","type":"u64"}]},{"name":"BoopdotfunWrappedBuy"},{"name":"BoopdotfunWrappedSell"},{"name":"Plasma","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"GoonFi","fields":[{"name":"is_bid","type":"bool"},{"name":"blacklist_bump","type":"u8"}]},{"name":"HumidiFi","fields":[{"name":"swap_id","type":"u64"},{"name":"is_base_to_quote","type":"bool"}]},{"name":"MeteoraDynamicBondingCurveSwapWithRemainingAccounts"},{"name":"TesseraV","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"PumpWrappedBuyV2"},{"name":"PumpWrappedSellV2"},{"name":"PumpSwapBuyV2"},{"name":"PumpSwapSellV2"},{"name":"Heaven","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"SolFiV2","fields":[{"name":"is_quote_to_base","type":"bool"}]},{"name":"Aquifer"},{"name":"PumpWrappedBuyV3"},{"name":"PumpWrappedSellV3"},{"name":"PumpSwapBuyV3"},{"name":"PumpSwapSellV3"},{"name":"JupiterLendDeposit"},{"name":"JupiterLendRedeem"},{"name":"DefiTuna","fields":[{"name":"a_to_b","type":"bool"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"AlphaQ","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"RaydiumV2"},{"name":"SarosDlmm","fields":[{"name":"swap_for_y","type":"bool"}]},{"name":"Futarchy","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"MeteoraDammV2WithRemainingAccounts"},{"name":"Obsidian"},{"name":"WhaleStreet","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"DynamicV1","fields":[{"name":"candidate_swaps","type":{"vec":{"defined":{"name":"CandidateSwap"}}}},{"name":"best_position","type":{"option":"u8"}}]},{"name":"PumpWrappedBuyV4"},{"name":"PumpWrappedSellV4"},{"name":"CarrotIssue"},{"name":"CarrotRedeem"},{"name":"Manifest","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"BisonFi","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"HumidiFiV2","fields":[{"name":"swap_id","type":"u64"},{"name":"is_base_to_quote","type":"bool"}]},{"name":"PerenaStar","fields":[{"name":"is_mint","type":"bool"}]},{"name":"JupiterRfqV2","fields":[{"name":"side","type":{"defined":{"name":"Side"}}},{"name":"fill_data","type":"bytes"}]},{"name":"GoonFiV2","fields":[{"name":"is_bid","type":"bool"}]},{"name":"Scorch","fields":[{"name":"swap_id","type":"u128"}]},{"name":"VaultLiquidUnstake","fields":[{"name":"lst_amounts","type":{"array":["u64",5]}},{"name":"seed","type":"u64"}]},{"name":"XOrca"},{"name":"Quantum","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"WhaleStreetV2","fields":[{"name":"side","type":{"defined":{"name":"Side"}}},{"name":"auth_amount_in","type":"u64"},{"name":"auth","type":"u64"}]},{"name":"Riptide","fields":[{"name":"amount_is_token_a","type":"bool"}]},{"name":"RunnerRodeo"},{"name":"TaurusFi","fields":[{"name":"is_base_in","type":"bool"}]},{"name":"Omnipair"},{"name":"MSwap"},{"name":"Hylo","fields":[{"name":"swap_type","type":{"defined":{"name":"HyloSwapType"}}}]},{"name":"VoltrDeposit"},{"name":"VoltrWithdraw"},{"name":"SanctumSV2","fields":[{"name":"src_lst_value_calc_accs","type":"u8"},{"name":"dst_lst_value_calc_accs","type":"u8"},{"name":"src_lst_index","type":"u32"},{"name":"dst_lst_index","type":"u32"}]},{"name":"LemmingsFi","fields":[{"name":"is_base_in","type":"bool"}]},{"name":"ScaleVmmBuy"},{"name":"ScaleVmmSell"},{"name":"ScaleAmmBuy"},{"name":"ScaleAmmSell"},{"name":"BisonFiV2","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"Trends"},{"name":"HumaDeposit"},{"name":"HumaInstantWithdraw"},{"name":"Kipseli","fields":[{"name":"is_base_to_quote","type":"bool"}]},{"name":"DynamicV2","fields":[{"name":"candidate_swaps","type":{"vec":{"defined":{"name":"CandidateSwapWithBps"}}}},{"name":"max_split_quote_calls","type":"u8"},{"name":"max_split_candidates","type":"u8"}]},{"name":"PumpSwapBuyV3WithCashbackClaim"},{"name":"PumpSwapSellV3WithCashbackClaim"},{"name":"PumpWrappedBuyV4WithCashbackClaim"},{"name":"PumpWrappedSellV4WithCashbackClaim"},{"name":"GoonFiV3","fields":[{"name":"is_bid","type":"bool"}]},{"name":"PumpWrappedBuyV5","fields":[{"name":"claim_cashback","type":"bool"}]},{"name":"PumpWrappedSellV5","fields":[{"name":"claim_cashback","type":"bool"}]},{"name":"ZeroFiSwapV2"},{"name":"BisonFiPredict","fields":[{"name":"side","type":{"defined":{"name":"BisonFiPredictSide"}}},{"name":"is_buy","type":"bool"}]},{"name":"ByrealDynamicV3"},{"name":"Flux","fields":[{"name":"swap_id","type":"u64"},{"name":"base_to_quote","type":"bool"}]}]}},{"name":"CandidateSwapWithBps","type":{"kind":"struct","fields":[{"name":"candidate_swap","type":{"defined":{"name":"CandidateSwap"}}},{"name":"bps","type":"u32"}]}},{"name":"CandidateSwap","type":{"kind":"enum","variants":[{"name":"HumidiFi","fields":[{"name":"swap_id","type":"u64"},{"name":"is_base_to_quote","type":"bool"}]},{"name":"TesseraV","fields":[{"name":"side","type":{"defined":{"name":"Side"}}}]},{"name":"HumidiFiV2","fields":[{"name":"swap_id","type":"u64"},{"name":"is_base_to_quote","type":"bool"}]},{"name":"RaydiumV2"},{"name":"RaydiumClmm"},{"name":"Whirlpool","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"ZeroFi"},{"name":"BisonFiV2","fields":[{"name":"a_to_b","type":"bool"}]},{"name":"GoonFiV2","fields":[{"name":"is_bid","type":"bool"}]},{"name":"GoonFiV3","fields":[{"name":"is_bid","type":"bool"}]},{"name":"WhirlpoolV2","fields":[{"name":"a_to_b","type":"bool"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"ZeroFiSwapV2"}]}},{"name":"HyloSwapType","type":{"kind":"enum","variants":[{"name":"MintStable"},{"name":"RedeemStable"},{"name":"MintLever"},{"name":"RedeemLever"},{"name":"SwapStableToLever"},{"name":"SwapLeverToStable"},{"name":"StabilityPoolDeposit"},{"name":"StabilityPoolWithdraw"}]}},{"name":"SwapEvent","type":{"kind":"struct","fields":[{"name":"amm","type":"pubkey"},{"name":"input_mint","type":"pubkey"},{"name":"input_amount","type":"u64"},{"name":"output_mint","type":"pubkey"},{"name":"output_amount","type":"u64"}]}},{"name":"SwapEventV2","type":{"kind":"struct","fields":[{"name":"input_mint","type":"pubkey"},{"name":"input_amount","type":"u64"},{"name":"output_mint","type":"pubkey"},{"name":"output_amount","type":"u64"}]}},{"name":"SwapsEvent","type":{"kind":"struct","fields":[{"name":"swap_events","type":{"vec":{"defined":{"name":"SwapEventV2"}}}}]}},{"name":"TokenLedger","type":{"kind":"struct","fields":[{"name":"token_account","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"BestSwapOutAmountViolation","type":{"kind":"struct","fields":[{"name":"expected_out_amount","type":"u64"},{"name":"out_amount","type":"u64"}]}},{"name":"CandidateSwapResult","type":{"kind":"enum","variants":[{"name":"OutAmount","fields":["u64"]},{"name":"ProgramError","fields":["u64"]}]}},{"name":"CandidateSwapResults","type":{"kind":"struct","fields":[{"name":"results","type":{"vec":{"defined":{"name":"CandidateSwapResult"}}}}]}},{"name":"CandidateSwapQuoteError","type":{"kind":"struct","fields":[{"name":"candidate_index","type":"u64"},{"name":"in_amount","type":"u64"},{"name":"error_code","type":"u64"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/kamino.6LtLpnUFNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc.json b/migration/khadhroony-bot2-reference/idls/kamino.6LtLpnUFNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc.json new file mode 100644 index 0000000..eb8500e --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/kamino.6LtLpnUFNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"yvaults","instructions":[{"name":"initializeStrategy","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false,"docs":["Program owner also checked."]},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"sharesMintAuthority","isMut":true,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false}],"args":[{"name":"strategyType","type":"u64"},{"name":"tokenACollateralId","type":"u64"},{"name":"tokenBCollateralId","type":"u64"}]},{"name":"initializeKaminoReward","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"rewardMint","isMut":false,"isSigner":false},{"name":"rewardVault","isMut":true,"isSigner":true},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"kaminoRewardIndex","type":"u64"},{"name":"collateralToken","type":"u64"}]},{"name":"addKaminoRewards","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"rewardMint","isMut":false,"isSigner":false},{"name":"rewardVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"rewardAta","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"kaminoRewardIndex","type":"u64"},{"name":"amount","type":"u64"}]},{"name":"initializeGlobalConfig","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeCollateralInfo","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false},{"name":"collInfo","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateCollateralInfo","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":true,"isSigner":false}],"args":[{"name":"index","type":"u64"},{"name":"mode","type":"u64"},{"name":"value","type":{"array":["u8",32]}}]},{"name":"insertCollateralInfo","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":true,"isSigner":false}],"args":[{"name":"index","type":"u64"},{"name":"params","type":{"defined":"CollateralInfoParams"}}]},{"name":"initializeSharesMetadata","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":false,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"sharesMint","isMut":false,"isSigner":false},{"name":"sharesMetadata","isMut":true,"isSigner":false},{"name":"sharesMintAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"updateSharesMetadata","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":false,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"sharesMint","isMut":false,"isSigner":false},{"name":"sharesMetadata","isMut":true,"isSigner":false},{"name":"sharesMintAuthority","isMut":false,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"updateGlobalConfig","accounts":[{"name":"adminAuthority","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"key","type":"u16"},{"name":"index","type":"u16"},{"name":"value","type":{"array":["u8",32]}}]},{"name":"updateTreasuryFeeVault","accounts":[{"name":"signer","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"feeMint","isMut":false,"isSigner":false},{"name":"treasuryFeeVault","isMut":true,"isSigner":false},{"name":"treasuryFeeVaultAuthority","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"collateralId","type":"u16"}]},{"name":"updateStrategyConfig","accounts":[{"name":"adminAuthority","isMut":false,"isSigner":true},{"name":"newAccount","isMut":false,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"mode","type":"u16"},{"name":"value","type":{"array":["u8",128]}}]},{"name":"updateRewardMapping","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"rewardMint","isMut":false,"isSigner":false},{"name":"rewardVault","isMut":true,"isSigner":true},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"rewardIndex","type":"u8"},{"name":"collateralToken","type":"u8"}]},{"name":"openLiquidityPosition","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"tickArrayLower","isMut":true,"isSigner":false},{"name":"tickArrayUpper","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"positionMint","isMut":true,"isSigner":false},{"name":"positionMetadataAccount","isMut":true,"isSigner":false},{"name":"positionTokenAccount","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"system","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram2022","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"memoProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"poolProgram","isMut":false,"isSigner":false},{"name":"oldTickArrayLowerOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"oldTickArrayUpperOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"oldPositionOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"oldPositionMintOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"oldPositionTokenAccountOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"poolTokenVaultA","isMut":true,"isSigner":false},{"name":"poolTokenVaultB","isMut":true,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false,"isOptional":true},{"name":"consensusAccount","isMut":false,"isSigner":false}],"args":[{"name":"tickLowerIndex","type":"i64"},{"name":"tickUpperIndex","type":"i64"},{"name":"bump","type":"u8"}]},{"name":"closeStrategy","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"oldPositionOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"oldPositionMintOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"oldPositionTokenAccountOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"oldTickArrayLowerOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"oldTickArrayUpperOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"userTokenAAta","isMut":true,"isSigner":false},{"name":"userTokenBAta","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":true,"isSigner":false},{"name":"tokenBMint","isMut":true,"isSigner":false},{"name":"reward0Vault","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"reward1Vault","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"reward2Vault","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"kaminoReward0Vault","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"kaminoReward1Vault","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"kaminoReward2Vault","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"userReward0Ata","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"userReward1Ata","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"userReward2Ata","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"userKaminoReward0Ata","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"userKaminoReward1Ata","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"userKaminoReward2Ata","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"poolProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"system","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false,"isOptional":true}],"args":[]},{"name":"deposit","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"tickArrayLower","isMut":false,"isSigner":false},{"name":"tickArrayUpper","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"tokenAAta","isMut":true,"isSigner":false},{"name":"tokenBAta","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"sharesMintAuthority","isMut":false,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"tokenMaxA","type":"u64"},{"name":"tokenMaxB","type":"u64"}]},{"name":"invest","accounts":[{"name":"actionsAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"memoProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram2022","isMut":false,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"raydiumProtocolPositionOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"positionTokenAccount","isMut":true,"isSigner":false},{"name":"poolTokenVaultA","isMut":true,"isSigner":false},{"name":"poolTokenVaultB","isMut":true,"isSigner":false},{"name":"tickArrayLower","isMut":true,"isSigner":false},{"name":"tickArrayUpper","isMut":true,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"poolProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false,"isOptional":true}],"args":[{"name":"referencePriceTick","type":"i32"}]},{"name":"depositAndInvest","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false,"docs":["check that the pool is owned either by orca or by raydium"]},{"name":"position","isMut":true,"isSigner":false},{"name":"raydiumProtocolPositionOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"positionTokenAccount","isMut":true,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"poolTokenVaultA","isMut":true,"isSigner":false},{"name":"poolTokenVaultB","isMut":true,"isSigner":false},{"name":"tickArrayLower","isMut":true,"isSigner":false},{"name":"tickArrayUpper","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"tokenAAta","isMut":true,"isSigner":false},{"name":"tokenBAta","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"sharesMintAuthority","isMut":false,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram2022","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"memoProgram","isMut":false,"isSigner":false},{"name":"poolProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false,"isOptional":true}],"args":[{"name":"tokenMaxA","type":"u64"},{"name":"tokenMaxB","type":"u64"}]},{"name":"withdraw","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"tickArrayLower","isMut":true,"isSigner":false},{"name":"tickArrayUpper","isMut":true,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"poolTokenVaultA","isMut":true,"isSigner":false},{"name":"poolTokenVaultB","isMut":true,"isSigner":false},{"name":"tokenAAta","isMut":true,"isSigner":false},{"name":"tokenBAta","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"treasuryFeeTokenAVault","isMut":true,"isSigner":false},{"name":"treasuryFeeTokenBVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram2022","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"memoProgram","isMut":false,"isSigner":false},{"name":"positionTokenAccount","isMut":true,"isSigner":false},{"name":"poolProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false,"isOptional":true}],"args":[{"name":"sharesAmount","type":"u64"}]},{"name":"executiveWithdraw","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"raydiumProtocolPositionOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"positionTokenAccount","isMut":false,"isSigner":false},{"name":"tickArrayLower","isMut":true,"isSigner":false},{"name":"tickArrayUpper","isMut":true,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"poolTokenVaultA","isMut":true,"isSigner":false},{"name":"poolTokenVaultB","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"memoProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram2022","isMut":false,"isSigner":false},{"name":"poolProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false,"isOptional":true}],"args":[{"name":"action","type":"u8"}]},{"name":"collectFeesAndRewards","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"tickArrayLower","isMut":true,"isSigner":false},{"name":"tickArrayUpper","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"raydiumProtocolPositionOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"positionTokenAccount","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"poolTokenVaultA","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"poolTokenVaultB","isMut":true,"isSigner":false},{"name":"treasuryFeeTokenAVault","isMut":true,"isSigner":false},{"name":"treasuryFeeTokenBVault","isMut":true,"isSigner":false},{"name":"treasuryFeeVaultAuthority","isMut":false,"isSigner":false},{"name":"reward0Vault","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"reward1Vault","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"reward2Vault","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"poolRewardVault0","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"poolRewardVault1","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"poolRewardVault2","isMut":true,"isSigner":false,"docs":["If rewards are uninitialized, pass this as strategy."]},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"memoProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram2022","isMut":false,"isSigner":false},{"name":"poolProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false,"isOptional":true}],"args":[]},{"name":"swapRewards","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"rewardVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"treasuryFeeTokenAVault","isMut":true,"isSigner":false},{"name":"treasuryFeeTokenBVault","isMut":true,"isSigner":false},{"name":"treasuryFeeVaultAuthority","isMut":false,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"rewardMint","isMut":false,"isSigner":false},{"name":"userTokenAAta","isMut":true,"isSigner":false},{"name":"userTokenBAta","isMut":true,"isSigner":false},{"name":"userRewardTokenAccount","isMut":true,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"scopePricesReward","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"rewardTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"tokenAIn","type":"u64"},{"name":"tokenBIn","type":"u64"},{"name":"rewardIndex","type":"u64"},{"name":"rewardCollateralId","type":"u64"},{"name":"minCollateralTokenOut","type":"u64"}]},{"name":"checkExpectedVaultsBalances","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"tokenAAta","isMut":false,"isSigner":false},{"name":"tokenBAta","isMut":false,"isSigner":false}],"args":[{"name":"tokenAAtaBalance","type":"u64"},{"name":"tokenBAtaBalance","type":"u64"}]},{"name":"singleTokenDepositAndInvestWithMin","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false,"docs":["check that the pool is owned either by orca or by raydium"]},{"name":"position","isMut":true,"isSigner":false},{"name":"raydiumProtocolPositionOrBaseVaultAuthority","isMut":true,"isSigner":false},{"name":"positionTokenAccount","isMut":true,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"poolTokenVaultA","isMut":true,"isSigner":false},{"name":"poolTokenVaultB","isMut":true,"isSigner":false},{"name":"tickArrayLower","isMut":true,"isSigner":false},{"name":"tickArrayUpper","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"tokenAAta","isMut":true,"isSigner":false},{"name":"tokenBAta","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"sharesMintAuthority","isMut":false,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram2022","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"memoProgram","isMut":false,"isSigner":false},{"name":"poolProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false,"isOptional":true}],"args":[{"name":"tokenAMinPostDepositBalance","type":"u64"},{"name":"tokenBMinPostDepositBalance","type":"u64"}]},{"name":"singleTokenDepositWithMin","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":false,"isSigner":false},{"name":"tickArrayLower","isMut":false,"isSigner":false},{"name":"tickArrayUpper","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"tokenAAta","isMut":true,"isSigner":false},{"name":"tokenBAta","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"sharesMintAuthority","isMut":false,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"tokenAMinPostDepositBalance","type":"u64"},{"name":"tokenBMinPostDepositBalance","type":"u64"}]},{"name":"flashSwapUnevenVaultsStart","docs":["Start of a Flash swap uneven vaults.","","This needs to be the first instruction of the transaction or preceded only by a","ComputeBudget.","","This ix has to be paired with a `flash_swap_uneven_vaults_end` (`FlashSwapUnevenVaultsEnd`)","as the last instruction of the transaction. No other instruction targeted the program is","allowed.","The instructions between the start and end instructions are expected to perform the swap."],"accounts":[{"name":"swapper","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"tokenAAta","isMut":true,"isSigner":false},{"name":"tokenBAta","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"tickArrayLower","isMut":false,"isSigner":false},{"name":"tickArrayUpper","isMut":false,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false},{"name":"consensusAccount","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"aToB","type":"bool"}]},{"name":"flashSwapUnevenVaultsEnd","docs":["End of Flash swap uneven vaults.","","See [`flash_swap_uneven_vaults_start`] for details.","","Warning: This instruction is allowed to be used independently from","`FlashSwapUnevenVaultsStart` and shall not perform any operation","that can be exploited when used alone."],"accounts":[{"name":"swapper","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"tokenAAta","isMut":true,"isSigner":false},{"name":"tokenBAta","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"tickArrayLower","isMut":false,"isSigner":false},{"name":"tickArrayUpper","isMut":false,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false},{"name":"consensusAccount","isMut":false,"isSigner":false}],"args":[{"name":"minRepayAmount","type":"u64"},{"name":"amountToLeaveToUser","type":"u64"},{"name":"aToB","type":"bool"}]},{"name":"emergencySwap","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"poolTokenVaultA","isMut":true,"isSigner":false},{"name":"poolTokenVaultB","isMut":true,"isSigner":false},{"name":"tickArray0","isMut":true,"isSigner":false,"docs":["Payer must send this correctly."]},{"name":"tickArray1","isMut":true,"isSigner":false,"docs":["Payer must send this correctly."]},{"name":"tickArray2","isMut":true,"isSigner":false,"docs":["Payer must send this correctly."]},{"name":"oracle","isMut":true,"isSigner":false},{"name":"poolProgram","isMut":false,"isSigner":false},{"name":"scopePricesA","isMut":false,"isSigner":false},{"name":"scopePricesB","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":false,"isSigner":false},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"memoProgram","isMut":false,"isSigner":false}],"args":[{"name":"aToB","type":"bool"},{"name":"targetLimitBps","type":"u64"}]},{"name":"withdrawFromTreasury","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"treasuryFeeVault","isMut":true,"isSigner":false},{"name":"treasuryFeeVaultAuthority","isMut":true,"isSigner":false},{"name":"tokenAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"permisionlessWithdrawFromTreasury","accounts":[{"name":"signer","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"treasuryFeeVault","isMut":true,"isSigner":false},{"name":"treasuryFeeVaultAuthority","isMut":true,"isSigner":false},{"name":"tokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"withdrawFromTopup","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"topupVault","isMut":true,"isSigner":false},{"name":"system","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"changePool","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false},{"name":"oldPosition","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"newPool","isMut":false,"isSigner":false},{"name":"strategyRewardVault0OrBaseVaultAuthority","isMut":false,"isSigner":false},{"name":"strategyRewardVault1OrBaseVaultAuthority","isMut":false,"isSigner":false},{"name":"strategyRewardVault2OrBaseVaultAuthority","isMut":false,"isSigner":false}],"args":[]},{"name":"closeProgramAccount","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"program","isMut":false,"isSigner":false},{"name":"programData","isMut":false,"isSigner":false},{"name":"closingAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"orcaSwap","accounts":[{"name":"funder","isMut":true,"isSigner":true},{"name":"tokenATokenProgram","isMut":false,"isSigner":false},{"name":"tokenBTokenProgram","isMut":false,"isSigner":false},{"name":"memoProgram","isMut":false,"isSigner":false},{"name":"tokenAuthority","isMut":false,"isSigner":false},{"name":"whirlpool","isMut":false,"isSigner":false},{"name":"tokenOwnerAccountA","isMut":false,"isSigner":false},{"name":"tokenVaultA","isMut":false,"isSigner":false},{"name":"tokenOwnerAccountB","isMut":false,"isSigner":false},{"name":"tokenVaultB","isMut":false,"isSigner":false},{"name":"tokenMintA","isMut":false,"isSigner":false},{"name":"tokenMintB","isMut":false,"isSigner":false},{"name":"tickArray0","isMut":false,"isSigner":false},{"name":"tickArray1","isMut":false,"isSigner":false},{"name":"tickArray2","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"whirlpoolProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"otherAmountThreshold","type":"u64"},{"name":"sqrtPriceLimit","type":"u128"},{"name":"amountSpecifiedIsInput","type":"bool"},{"name":"aToB","type":"bool"}]},{"name":"signTerms","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"ownerSignatureState","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"signature","type":{"array":["u8",64]}}]},{"name":"updateStrategyAdmin","accounts":[{"name":"pendingAdmin","isMut":true,"isSigner":true},{"name":"strategy","isMut":true,"isSigner":false}],"args":[]},{"name":"resizeTokenInfos","accounts":[{"name":"signer","isMut":true,"isSigner":true},{"name":"tokenInfos","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"deprecateCollateralInfo","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenInfos","isMut":true,"isSigner":false}],"args":[{"name":"index","type":"u64"}]}],"accounts":[{"name":"Whirlpool","docs":["External types"],"type":{"kind":"struct","fields":[{"name":"whirlpoolsConfig","type":"publicKey"},{"name":"whirlpoolBump","type":{"array":["u8",1]}},{"name":"tickSpacing","type":"u16"},{"name":"tickSpacingSeed","type":{"array":["u8",2]}},{"name":"feeRate","type":"u16"},{"name":"protocolFeeRate","type":"u16"},{"name":"liquidity","type":"u128"},{"name":"sqrtPrice","type":"u128"},{"name":"tickCurrentIndex","type":"i32"},{"name":"protocolFeeOwedA","type":"u64"},{"name":"protocolFeeOwedB","type":"u64"},{"name":"tokenMintA","type":"publicKey"},{"name":"tokenVaultA","type":"publicKey"},{"name":"feeGrowthGlobalA","type":"u128"},{"name":"tokenMintB","type":"publicKey"},{"name":"tokenVaultB","type":"publicKey"},{"name":"feeGrowthGlobalB","type":"u128"},{"name":"rewardLastUpdatedTimestamp","type":"u64"},{"name":"rewardInfos","type":{"array":[{"defined":"WhirlpoolRewardInfo"},3]}}]}},{"name":"Position","type":{"kind":"struct","fields":[{"name":"whirlpool","type":"publicKey"},{"name":"positionMint","type":"publicKey"},{"name":"liquidity","type":"u128"},{"name":"tickLowerIndex","type":"i32"},{"name":"tickUpperIndex","type":"i32"},{"name":"feeGrowthCheckpointA","type":"u128"},{"name":"feeOwedA","type":"u64"},{"name":"feeGrowthCheckpointB","type":"u128"},{"name":"feeOwedB","type":"u64"},{"name":"rewardInfos","type":{"array":[{"defined":"PositionRewardInfo"},3]}}]}},{"name":"PoolState","type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"ammConfig","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"tokenMint0","docs":["Token pair of the pool, where token_mint_0 address < token_mint_1 address"],"type":"publicKey"},{"name":"tokenMint1","type":"publicKey"},{"name":"tokenVault0","docs":["Token pair vault"],"type":"publicKey"},{"name":"tokenVault1","type":"publicKey"},{"name":"observationKey","docs":["observation account key"],"type":"publicKey"},{"name":"mintDecimals0","docs":["mint0 and mint1 decimals"],"type":"u8"},{"name":"mintDecimals1","type":"u8"},{"name":"tickSpacing","docs":["The minimum number of ticks between initialized ticks"],"type":"u16"},{"name":"liquidity","docs":["The currently in range liquidity available to the pool."],"type":"u128"},{"name":"sqrtPriceX64","docs":["The current price of the pool as a sqrt(token_1/token_0) Q64.64 value"],"type":"u128"},{"name":"tickCurrent","docs":["The current tick of the pool, i.e. according to the last tick transition that was run."],"type":"i32"},{"name":"observationIndex","docs":["the most-recently updated index of the observations array"],"type":"u16"},{"name":"observationUpdateDuration","type":"u16"},{"name":"feeGrowthGlobal0X64","docs":["The fee growth as a Q64.64 number, i.e. fees of token_0 and token_1 collected per","unit of liquidity for the entire life of the pool."],"type":"u128"},{"name":"feeGrowthGlobal1X64","type":"u128"},{"name":"protocolFeesToken0","docs":["The amounts of token_0 and token_1 that are owed to the protocol."],"type":"u64"},{"name":"protocolFeesToken1","type":"u64"},{"name":"swapInAmountToken0","docs":["The amounts in and out of swap token_0 and token_1"],"type":"u128"},{"name":"swapOutAmountToken1","type":"u128"},{"name":"swapInAmountToken1","type":"u128"},{"name":"swapOutAmountToken0","type":"u128"},{"name":"status","docs":["Bitwise representation of the state of the pool","bit0, 1: disable open position and increase liquidity, 0: normal","bit1, 1: disable decrease liquidity, 0: normal","bit2, 1: disable collect fee, 0: normal","bit3, 1: disable collect reward, 0: normal","bit4, 1: disable swap, 0: normal"],"type":"u8"},{"name":"padding","docs":["Leave blank for future use"],"type":{"array":["u8",7]}},{"name":"rewardInfos","type":{"array":[{"defined":"RewardInfo"},3]}},{"name":"tickArrayBitmap","docs":["Packed initialized tick array state"],"type":{"array":["u64",16]}},{"name":"totalFeesToken0","docs":["except protocol_fee and fund_fee"],"type":"u64"},{"name":"totalFeesClaimedToken0","docs":["except protocol_fee and fund_fee"],"type":"u64"},{"name":"totalFeesToken1","type":"u64"},{"name":"totalFeesClaimedToken1","type":"u64"},{"name":"fundFeesToken0","type":"u64"},{"name":"fundFeesToken1","type":"u64"},{"name":"openTime","type":"u64"},{"name":"padding1","type":{"array":["u64",25]}},{"name":"padding2","type":{"array":["u64",32]}}]}},{"name":"PersonalPositionState","type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"nftMint","docs":["Mint address of the tokenized position"],"type":"publicKey"},{"name":"poolId","docs":["The ID of the pool with which this token is connected"],"type":"publicKey"},{"name":"tickLowerIndex","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tickUpperIndex","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"feeGrowthInside0LastX64","docs":["The token_0 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"feeGrowthInside1LastX64","docs":["The token_1 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"tokenFeesOwed0","docs":["The fees owed to the position owner in token_0, as of the last computation"],"type":"u64"},{"name":"tokenFeesOwed1","docs":["The fees owed to the position owner in token_1, as of the last computation"],"type":"u64"},{"name":"rewardInfos","type":{"array":[{"defined":"PositionRewardInfo"},3]}},{"name":"padding","type":{"array":["u64",8]}}]}},{"name":"ProtocolPositionState","type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"poolId","docs":["The ID of the pool with which this token is connected"],"type":"publicKey"},{"name":"tickLowerIndex","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tickUpperIndex","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"feeGrowthInside0LastX64","docs":["The token_0 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"feeGrowthInside1LastX64","docs":["The token_1 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"tokenFeesOwed0","docs":["The fees owed to the position owner in token_0"],"type":"u64"},{"name":"tokenFeesOwed1","docs":["The fees owed to the position owner in token_1"],"type":"u64"},{"name":"rewardGrowthInside","docs":["The reward growth per unit of liquidity as of the last update to liquidity"],"type":{"array":["u128",3]}},{"name":"padding","type":{"array":["u64",8]}}]}},{"name":"WhirlpoolStrategy","type":{"kind":"struct","fields":[{"name":"adminAuthority","type":"publicKey"},{"name":"globalConfig","type":"publicKey"},{"name":"baseVaultAuthority","type":"publicKey"},{"name":"baseVaultAuthorityBump","type":"u64"},{"name":"pool","type":"publicKey"},{"name":"poolTokenVaultA","type":"publicKey"},{"name":"poolTokenVaultB","type":"publicKey"},{"name":"tickArrayLower","type":"publicKey"},{"name":"tickArrayUpper","type":"publicKey"},{"name":"position","type":"publicKey"},{"name":"positionMint","type":"publicKey"},{"name":"positionMetadata","type":"publicKey"},{"name":"positionTokenAccount","type":"publicKey"},{"name":"tokenAVault","type":"publicKey"},{"name":"tokenBVault","type":"publicKey"},{"name":"deprecated0","type":{"array":["publicKey",2]}},{"name":"deprecated1","type":{"array":["u64",2]}},{"name":"tokenAMint","type":"publicKey"},{"name":"tokenBMint","type":"publicKey"},{"name":"tokenAMintDecimals","type":"u64"},{"name":"tokenBMintDecimals","type":"u64"},{"name":"tokenAAmounts","type":"u64"},{"name":"tokenBAmounts","type":"u64"},{"name":"tokenACollateralId","type":"u64"},{"name":"tokenBCollateralId","type":"u64"},{"name":"deprecated2","type":"publicKey"},{"name":"deprecated3","type":"publicKey"},{"name":"sharesMint","type":"publicKey"},{"name":"sharesMintDecimals","type":"u64"},{"name":"sharesMintAuthority","type":"publicKey"},{"name":"sharesMintAuthorityBump","type":"u64"},{"name":"sharesIssued","type":"u64"},{"name":"status","type":"u64"},{"name":"reward0Amount","type":"u64"},{"name":"reward0Vault","type":"publicKey"},{"name":"reward0CollateralId","type":"u64"},{"name":"reward0Decimals","type":"u64"},{"name":"reward1Amount","type":"u64"},{"name":"reward1Vault","type":"publicKey"},{"name":"reward1CollateralId","type":"u64"},{"name":"reward1Decimals","type":"u64"},{"name":"reward2Amount","type":"u64"},{"name":"reward2Vault","type":"publicKey"},{"name":"reward2CollateralId","type":"u64"},{"name":"reward2Decimals","type":"u64"},{"name":"depositCapUsd","type":"u64"},{"name":"feesACumulative","type":"u64"},{"name":"feesBCumulative","type":"u64"},{"name":"reward0AmountCumulative","type":"u64"},{"name":"reward1AmountCumulative","type":"u64"},{"name":"reward2AmountCumulative","type":"u64"},{"name":"depositCapUsdPerIxn","type":"u64"},{"name":"withdrawalCapA","type":{"defined":"WithdrawalCaps"}},{"name":"withdrawalCapB","type":{"defined":"WithdrawalCaps"}},{"name":"maxPriceDeviationBps","type":"u64"},{"name":"swapVaultMaxSlippageBps","type":"u32"},{"name":"swapVaultMaxSlippageFromReferenceBps","type":"u32"},{"name":"strategyType","type":"u64"},{"name":"padding0","type":"u64"},{"name":"withdrawFee","type":"u64"},{"name":"feesFee","type":"u64"},{"name":"reward0Fee","type":"u64"},{"name":"reward1Fee","type":"u64"},{"name":"reward2Fee","type":"u64"},{"name":"positionTimestamp","type":"u64"},{"name":"kaminoRewards","type":{"array":[{"defined":"KaminoRewardInfo"},3]}},{"name":"strategyDex","type":"u64"},{"name":"raydiumProtocolPositionOrBaseVaultAuthority","type":"publicKey"},{"name":"allowDepositWithoutInvest","type":"u64"},{"name":"raydiumPoolConfigOrBaseVaultAuthority","type":"publicKey"},{"name":"depositBlocked","type":"u8"},{"name":"creationStatus","type":"u8"},{"name":"investBlocked","type":"u8"},{"name":"shareCalculationMethod","docs":["share_calculation_method can be either DOLAR_BASED=0 or PROPORTION_BASED=1"],"type":"u8"},{"name":"withdrawBlocked","type":"u8"},{"name":"reservedFlag2","type":"u8"},{"name":"localAdminBlocked","type":"u8"},{"name":"flashVaultSwapAllowed","type":"u8"},{"name":"referenceSwapPriceA","type":{"defined":"Price"}},{"name":"referenceSwapPriceB","type":{"defined":"Price"}},{"name":"isCommunity","type":"u8"},{"name":"rebalanceType","type":"u8"},{"name":"flashSwapInProgress","type":"u8"},{"name":"padding1","type":{"array":["u8",5]}},{"name":"rebalanceRaw","type":{"defined":"RebalanceRaw"}},{"name":"padding2","type":{"array":["u8",7]}},{"name":"tokenAFeesFromRewardsCumulative","type":"u64"},{"name":"tokenBFeesFromRewardsCumulative","type":"u64"},{"name":"strategyLookupTable","type":"publicKey"},{"name":"lastSwapUnevenStepTimestamp","type":"u64"},{"name":"farm","type":"publicKey"},{"name":"rebalancesCap","type":{"defined":"WithdrawalCaps"}},{"name":"padding3NonZeroed","type":{"array":["u64",4]}},{"name":"tokenATokenProgram","type":"publicKey"},{"name":"tokenBTokenProgram","type":"publicKey"},{"name":"pendingAdmin","type":"publicKey"},{"name":"maxDeviationFromRefPriceOnInvestBps","type":"u32"},{"name":"padding3","type":"u32"},{"name":"lastInvestSlot","type":"u64"},{"name":"padding4","type":"u64"},{"name":"padding5","type":{"array":["u128",12]}},{"name":"padding6","type":{"array":["u128",32]}},{"name":"padding7","type":{"array":["u128",32]}},{"name":"padding8","type":{"array":["u128",32]}}]}},{"name":"GlobalConfig","type":{"kind":"struct","fields":[{"name":"emergencyMode","type":"u64"},{"name":"blockDeposit","type":"u64"},{"name":"blockInvest","type":"u64"},{"name":"blockWithdraw","type":"u64"},{"name":"blockCollectFees","type":"u64"},{"name":"blockCollectRewards","type":"u64"},{"name":"blockSwapRewards","type":"u64"},{"name":"blockSwapUnevenVaults","type":"u32"},{"name":"blockEmergencySwap","type":"u32"},{"name":"minWithdrawalFeeBps","type":"u64"},{"name":"scopeProgramId","type":"publicKey"},{"name":"deprecated","type":"publicKey"},{"name":"padding0NonZeroed","type":{"array":["u64",256]}},{"name":"actionsAuthority","type":"publicKey"},{"name":"adminAuthority","type":"publicKey"},{"name":"treasuryFeeVaults","type":{"array":["publicKey",256]}},{"name":"tokenInfos","type":"publicKey"},{"name":"blockLocalAdmin","type":"u64"},{"name":"minPerformanceFeeBps","type":"u64"},{"name":"minSwapUnevenSlippageToleranceBps","type":"u64"},{"name":"minReferencePriceSlippageToleranceBps","type":"u64"},{"name":"actionsAfterRebalanceDelaySeconds","type":"u64"},{"name":"treasuryFeeVaultReceiver","type":"publicKey"},{"name":"scopePriceIds","type":{"array":["publicKey",16]}},{"name":"maxDeviationFromRefPriceOnInvestBps","type":"u32"},{"name":"padding1","type":"u32"},{"name":"investCooldownSlots","type":"u64"},{"name":"minInvestTriggerValueUsd","type":"u64"},{"name":"padding","type":{"array":["u64",1968]}}]}},{"name":"CollateralInfos","type":{"kind":"struct","fields":[{"name":"infos","type":{"array":[{"defined":"CollateralInfo"},303]}}]}},{"name":"ScopeChainAccount","type":{"kind":"struct","fields":[{"name":"chainArray","type":{"array":[{"array":["u16",4]},512]}}]}},{"name":"TermsSignature","type":{"kind":"struct","fields":[{"name":"signature","type":{"array":["u8",64]}}]}}],"types":[{"name":"PositionRewardInfo","type":{"kind":"struct","fields":[{"name":"growthInsideCheckpoint","type":"u128"},{"name":"amountOwed","type":"u64"}]}},{"name":"WhirlpoolRewardInfo","type":{"kind":"struct","fields":[{"name":"mint","docs":["Reward token mint."],"type":"publicKey"},{"name":"vault","docs":["Reward vault token account."],"type":"publicKey"},{"name":"authority","docs":["Authority account that has permission to initialize the reward and set emissions."],"type":"publicKey"},{"name":"emissionsPerSecondX64","docs":["Q64.64 number that indicates how many tokens per second are earned per unit of liquidity."],"type":"u128"},{"name":"growthGlobalX64","docs":["Q64.64 number that tracks the total tokens earned per unit of liquidity since the reward","emissions were turned on."],"type":"u128"}]}},{"name":"RewardInfo","type":{"kind":"struct","fields":[{"name":"rewardState","docs":["Reward state"],"type":"u8"},{"name":"openTime","docs":["Reward open time"],"type":"u64"},{"name":"endTime","docs":["Reward end time"],"type":"u64"},{"name":"lastUpdateTime","docs":["Reward last update time"],"type":"u64"},{"name":"emissionsPerSecondX64","docs":["Q64.64 number indicates how many tokens per second are earned per unit of liquidity."],"type":"u128"},{"name":"rewardTotalEmissioned","docs":["The total amount of reward emissioned"],"type":"u64"},{"name":"rewardClaimed","docs":["The total amount of claimed reward"],"type":"u64"},{"name":"tokenMint","docs":["Reward token mint."],"type":"publicKey"},{"name":"tokenVault","docs":["Reward vault token account."],"type":"publicKey"},{"name":"authority","docs":["The owner that has permission to set reward param"],"type":"publicKey"},{"name":"rewardGrowthGlobalX64","docs":["Q64.64 number that tracks the total tokens earned per unit of liquidity since the reward","emissions were turned on."],"type":"u128"}]}},{"name":"RebalanceRaw","type":{"kind":"struct","fields":[{"name":"params","type":{"array":["u8",128]}},{"name":"state","type":{"array":["u8",256]}},{"name":"referencePriceType","type":"u8"}]}},{"name":"CollateralInfo","type":{"kind":"struct","fields":[{"name":"mint","type":"publicKey"},{"name":"lowerHeuristic","type":"u64"},{"name":"upperHeuristic","type":"u64"},{"name":"expHeuristic","type":"u64"},{"name":"maxTwapDivergenceBps","type":"u64"},{"name":"scopeTwapPriceChain","type":{"array":["u16",4]}},{"name":"scopePriceChain","type":{"array":["u16",4]}},{"name":"name","type":{"array":["u8",32]}},{"name":"maxAgePriceSeconds","type":"u64"},{"name":"maxAgeTwapSeconds","type":"u64"},{"name":"maxIgnorableAmountAsReward","type":"u64"},{"name":"disabled","type":"u8"},{"name":"padding0","type":{"array":["u8",7]}},{"name":"scopeStakingRateChain","type":{"array":["u16",4]}},{"name":"scopeFeed","type":"publicKey"},{"name":"padding","type":{"array":["u64",4]}}]}},{"name":"CollateralInfoParams","type":{"kind":"struct","fields":[{"name":"mint","type":"publicKey"},{"name":"lowerHeuristic","type":"u64"},{"name":"upperHeuristic","type":"u64"},{"name":"expHeuristic","type":"u64"},{"name":"maxTwapDivergenceBps","type":"u64"},{"name":"scopeTwapPriceChain","type":{"array":["u16",4]}},{"name":"scopePriceChain","type":{"array":["u16",4]}},{"name":"name","type":{"array":["u8",32]}},{"name":"maxAgePriceSeconds","type":"u64"},{"name":"maxAgeTwapSeconds","type":"u64"},{"name":"maxIgnorableAmountAsReward","type":"u64"},{"name":"disabled","type":"u8"},{"name":"scopeStakingRateChain","type":{"array":["u16",4]}},{"name":"scopeFeed","type":"publicKey"}]}},{"name":"KaminoRewardInfo","type":{"kind":"struct","fields":[{"name":"decimals","type":"u64"},{"name":"rewardVault","type":"publicKey"},{"name":"rewardMint","type":"publicKey"},{"name":"rewardCollateralId","type":"u64"},{"name":"lastIssuanceTs","type":"u64"},{"name":"rewardPerSecond","type":"u64"},{"name":"amountUncollected","type":"u64"},{"name":"amountIssuedCumulative","type":"u64"},{"name":"amountAvailable","type":"u64"}]}},{"name":"WithdrawalCaps","type":{"kind":"struct","fields":[{"name":"configCapacity","type":"i64"},{"name":"currentTotal","type":"i64"},{"name":"lastIntervalStartTimestamp","type":"u64"},{"name":"configIntervalLengthSeconds","type":"u64"}]}},{"name":"Price","type":{"kind":"struct","fields":[{"name":"value","type":"u64"},{"name":"exp","type":"u64"}]}},{"name":"RebalanceAutodriftParams","type":{"kind":"struct","fields":[{"name":"initDriftTicksPerEpoch","type":"u32"},{"name":"ticksBelowMid","type":"i32"},{"name":"ticksAboveMid","type":"i32"},{"name":"frontrunMultiplierBps","type":"u16"},{"name":"stakingRateASource","type":{"defined":"StakingRateSource"}},{"name":"stakingRateBSource","type":{"defined":"StakingRateSource"}},{"name":"initDriftDirection","type":{"defined":"DriftDirection"}}]}},{"name":"RebalanceAutodriftWindow","type":{"kind":"struct","fields":[{"name":"stakingRateA","type":{"option":{"defined":"Price"}}},{"name":"stakingRateB","type":{"option":{"defined":"Price"}}},{"name":"epoch","type":"u64"},{"name":"theoreticalTick","type":"i32"},{"name":"stratMidTick","type":"i32"}]}},{"name":"RebalanceAutodriftState","type":{"kind":"struct","fields":[{"name":"lastWindow","type":{"defined":"RebalanceAutodriftWindow"}},{"name":"currentWindow","type":{"defined":"RebalanceAutodriftWindow"}},{"name":"step","type":{"defined":"RebalanceAutodriftStep"}}]}},{"name":"RebalanceDriftParams","type":{"kind":"struct","fields":[{"name":"startMidTick","type":"i32"},{"name":"ticksBelowMid","type":"i32"},{"name":"ticksAboveMid","type":"i32"},{"name":"secondsPerTick","type":"u64"},{"name":"direction","type":{"defined":"DriftDirection"}}]}},{"name":"RebalanceDriftState","type":{"kind":"struct","fields":[{"name":"step","type":{"defined":"RebalanceDriftStep"}},{"name":"lastDriftTimestamp","type":"u64"},{"name":"lastMidTick","type":"i32"}]}},{"name":"RebalanceExpanderState","type":{"kind":"struct","fields":[{"name":"initialPoolPrice","type":"u128"},{"name":"expansionCount","type":"u16"}]}},{"name":"RebalanceManualState","type":{"kind":"struct","fields":[]}},{"name":"PeriodicRebalanceState","type":{"kind":"struct","fields":[{"name":"lastRebalanceTimestamp","type":"u64"}]}},{"name":"RebalancePricePercentageWithResetState","type":{"kind":"struct","fields":[{"name":"lastRebalanceLowerResetPoolPrice","type":"u128"},{"name":"lastRebalanceUpperResetPoolPrice","type":"u128"}]}},{"name":"RebalancePricePercentageState","type":{"kind":"struct","fields":[{"name":"lastRebalanceLowerPoolPrice","type":"u128"},{"name":"lastRebalanceUpperPoolPrice","type":"u128"}]}},{"name":"RebalanceTakeProfitState","type":{"kind":"struct","fields":[{"name":"step","type":{"defined":"RebalanceTakeProfitStep"}}]}},{"name":"BinAddLiquidityStrategy","type":{"kind":"enum","variants":[{"name":"Uniform","fields":[{"name":"currentBinIndex","type":"i32"},{"name":"lowerBinIndex","type":"i32"},{"name":"upperBinIndex","type":"i32"},{"name":"amountXToDeposit","type":"u64"},{"name":"amountYToDeposit","type":"u64"},{"name":"xCurrentBin","type":"u64"},{"name":"yCurrentBin","type":"u64"}]},{"name":"CurrentTick","fields":["i32"]}]}},{"name":"SimulationPrice","type":{"kind":"enum","variants":[{"name":"PoolPrice"},{"name":"SqrtPrice","fields":["u128"]},{"name":"TickIndex","fields":["i32"]}]}},{"name":"DexSpecificPrice","type":{"kind":"enum","variants":[{"name":"SqrtPrice","fields":["u128"]},{"name":"Q64_64","fields":["u128"]}]}},{"name":"RemoveLiquidityMode","type":{"kind":"enum","variants":[{"name":"Liquidity","fields":["u128"]},{"name":"Bps","fields":["u16"]},{"name":"All"}]}},{"name":"WithdrawalCapAccumulatorAction","type":{"kind":"enum","variants":[{"name":"KeepAccumulator"},{"name":"ResetAccumulator"}]}},{"name":"RebalanceEffects","type":{"kind":"enum","variants":[{"name":"NewRange","fields":["i32","i32"]},{"name":"WithdrawAndFreeze"}]}},{"name":"SwapLimit","type":{"kind":"enum","variants":[{"name":"Bps","fields":["u64"]},{"name":"Absolute","fields":[{"name":"srcAmountToSwap","docs":["Amount of src token expected by the user to perform the swap"],"type":"u64"},{"name":"dstAmountToVault","docs":["Amount of dst token the user provides in exchange"],"type":"u64"},{"name":"aToB","type":"bool"}]}]}},{"name":"MintingMethod","type":{"kind":"enum","variants":[{"name":"PriceBased"},{"name":"Proportional"}]}},{"name":"GlobalConfigOption","type":{"kind":"enum","variants":[{"name":"EmergencyMode"},{"name":"BlockDeposit"},{"name":"BlockInvest"},{"name":"BlockWithdraw"},{"name":"BlockCollectFees"},{"name":"BlockCollectRewards"},{"name":"BlockSwapRewards"},{"name":"BlockSwapUnevenVaults"},{"name":"WithdrawalFeeBps"},{"name":"DeprecatedSwapDiscountBps"},{"name":"ActionsAuthority"},{"name":"DeprecatedTreasuryFeeVaults"},{"name":"AdminAuthority"},{"name":"BlockEmergencySwap"},{"name":"BlockLocalAdmin"},{"name":"UpdateTokenInfos"},{"name":"ScopeProgramId"},{"name":"UpdateScopePriceId"},{"name":"MinPerformanceFeeBps"},{"name":"MinSwapUnevenSlippageToleranceBps"},{"name":"MinReferencePriceSlippageToleranceBps"},{"name":"ActionsAfterRebalanceDelaySeconds"},{"name":"TreasuryFeeVaultReceiver"},{"name":"AddScopePriceId"},{"name":"MaxDeviationFromRefPriceOnInvestBps"},{"name":"InvestCooldownSlots"},{"name":"MinInvestTriggerValueUsd"}]}},{"name":"StrategyConfigOption","type":{"kind":"enum","variants":[{"name":"UpdateDepositCap"},{"name":"UpdateDepositCapIxn"},{"name":"UpdateWithdrawalCapACapacity"},{"name":"UpdateWithdrawalCapAInterval"},{"name":"UpdateWithdrawalCapACurrentTotal"},{"name":"UpdateWithdrawalCapBCapacity"},{"name":"UpdateWithdrawalCapBInterval"},{"name":"UpdateWithdrawalCapBCurrentTotal"},{"name":"UpdateMaxDeviationBps"},{"name":"UpdateSwapVaultMaxSlippage"},{"name":"UpdateStrategyType"},{"name":"UpdateDepositFee"},{"name":"UpdateWithdrawFee"},{"name":"UpdateCollectFeesFee"},{"name":"UpdateReward0Fee"},{"name":"UpdateReward1Fee"},{"name":"UpdateReward2Fee"},{"name":"UpdateAdminAuthority"},{"name":"KaminoRewardIndex0TS"},{"name":"KaminoRewardIndex1TS"},{"name":"KaminoRewardIndex2TS"},{"name":"KaminoRewardIndex0RewardPerSecond"},{"name":"KaminoRewardIndex1RewardPerSecond"},{"name":"KaminoRewardIndex2RewardPerSecond"},{"name":"UpdateDepositBlocked"},{"name":"UpdateRaydiumProtocolPositionOrBaseVaultAuthority"},{"name":"UpdateRaydiumPoolConfigOrBaseVaultAuthority"},{"name":"UpdateInvestBlocked"},{"name":"UpdateWithdrawBlocked"},{"name":"UpdateLocalAdminBlocked"},{"name":"DeprecatedUpdateCollateralIdA"},{"name":"DeprecatedUpdateCollateralIdB"},{"name":"UpdateFlashVaultSwap"},{"name":"AllowDepositWithoutInvest"},{"name":"UpdateSwapVaultMaxSlippageFromRef"},{"name":"ResetReferencePrices"},{"name":"UpdateStrategyCreationState"},{"name":"UpdateIsCommunity"},{"name":"UpdateRebalanceType"},{"name":"UpdateRebalanceParams"},{"name":"UpdateDepositMintingMethod"},{"name":"UpdateLookupTable"},{"name":"UpdateReferencePriceType"},{"name":"UpdateReward0Amount"},{"name":"UpdateReward1Amount"},{"name":"UpdateReward2Amount"},{"name":"UpdateFarm"},{"name":"UpdateRebalancesCapCapacity"},{"name":"UpdateRebalancesCapInterval"},{"name":"UpdateRebalancesCapCurrentTotal"},{"name":"DeprecatedUpdateSwapUnevenAuthority"},{"name":"UpdatePendingStrategyAdmin"},{"name":"UpdateMaxDeviationFromRefPriceOnInvestBps"}]}},{"name":"StrategyStatus","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Active"},{"name":"Frozen"},{"name":"Rebalancing"},{"name":"NoPosition"}]}},{"name":"StrategyType","type":{"kind":"enum","variants":[{"name":"Stable"},{"name":"Pegged"},{"name":"Volatile"}]}},{"name":"CreationStatus","type":{"kind":"enum","variants":[{"name":"IGNORED"},{"name":"SHADOW"},{"name":"LIVE"},{"name":"DEPRECATED"},{"name":"STAGING"}]}},{"name":"ExecutiveWithdrawAction","type":{"kind":"enum","variants":[{"name":"Freeze"},{"name":"Unfreeze"},{"name":"Rebalance"}]}},{"name":"ReferencePriceType","type":{"kind":"enum","variants":[{"name":"POOL"},{"name":"TWAP"}]}},{"name":"LiquidityCalculationMode","type":{"kind":"enum","variants":[{"name":"Deposit"},{"name":"Withdraw"}]}},{"name":"UpdateCollateralInfoMode","type":{"kind":"enum","variants":[{"name":"CollateralId"},{"name":"LowerHeuristic"},{"name":"UpperHeuristic"},{"name":"ExpHeuristic"},{"name":"TwapDivergence"},{"name":"UpdateScopeTwap"},{"name":"UpdateScopeChain"},{"name":"UpdateName"},{"name":"UpdatePriceMaxAge"},{"name":"UpdateTwapMaxAge"},{"name":"UpdateDisabled"},{"name":"UpdateStakingRateChain"},{"name":"UpdateMaxIgnorableAmountAsReward"},{"name":"UpdateScopeFeed"}]}},{"name":"BalanceStatus","type":{"kind":"enum","variants":[{"name":"Balanced"},{"name":"Unbalanced"}]}},{"name":"RebalanceAutodriftStep","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Autodrifting"}]}},{"name":"StakingRateSource","type":{"kind":"enum","variants":[{"name":"Constant"},{"name":"Scope"}]}},{"name":"DriftDirection","type":{"kind":"enum","variants":[{"name":"Increasing"},{"name":"Decreasing"}]}},{"name":"RebalanceDriftStep","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Drifting"}]}},{"name":"ExpanderStep","type":{"kind":"enum","variants":[{"name":"ExpandOrContract","fields":["u16"]},{"name":"Recenter"}]}},{"name":"RebalanceTakeProfitToken","type":{"kind":"enum","variants":[{"name":"A"},{"name":"B"}]}},{"name":"RebalanceTakeProfitStep","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"TakingProfit"},{"name":"Finished"}]}},{"name":"RebalanceAction","type":{"kind":"enum","variants":[{"name":"NewPriceRange","fields":[{"defined":"DexSpecificPrice"},{"defined":"DexSpecificPrice"}]},{"name":"NewTickRange","fields":["i32","i32"]},{"name":"WithdrawAndFreeze"}]}},{"name":"RebalanceType","type":{"kind":"enum","variants":[{"name":"Manual"},{"name":"PricePercentage"},{"name":"PricePercentageWithReset"},{"name":"Drift"},{"name":"TakeProfit"},{"name":"PeriodicRebalance"},{"name":"Expander"},{"name":"Autodrift"}]}},{"name":"CollateralTestToken","type":{"kind":"enum","variants":[{"name":"USDC"},{"name":"USDH"},{"name":"SOL"},{"name":"ETH"},{"name":"BTC"},{"name":"MSOL"},{"name":"STSOL"},{"name":"USDT"},{"name":"ORCA"},{"name":"MNDE"},{"name":"HBB"},{"name":"JSOL"},{"name":"USH"},{"name":"DAI"},{"name":"LDO"},{"name":"SCNSOL"},{"name":"UXD"},{"name":"HDG"},{"name":"DUST"},{"name":"USDR"},{"name":"RATIO"},{"name":"UXP"},{"name":"JITOSOL"},{"name":"RAY"},{"name":"BONK"},{"name":"SAMO"},{"name":"LaineSOL"},{"name":"BSOL"}]}},{"name":"ScopePriceIdTest","type":{"kind":"enum","variants":[{"name":"SOL"},{"name":"ETH"},{"name":"BTC"},{"name":"SRM"},{"name":"RAY"},{"name":"FTT"},{"name":"MSOL"},{"name":"scnSOL_SOL"},{"name":"BNB"},{"name":"AVAX"},{"name":"DaoSOL_SOL"},{"name":"SaberMSOL_SOL"},{"name":"USDH"},{"name":"StSOL"},{"name":"CSOL_SOL"},{"name":"CETH_ETH"},{"name":"CBTC_BTC"},{"name":"CMSOL_SOL"},{"name":"wstETH"},{"name":"LDO"},{"name":"USDC"},{"name":"CUSDC_USDC"},{"name":"USDT"},{"name":"ORCA"},{"name":"MNDE"},{"name":"HBB"},{"name":"CORCA_ORCA"},{"name":"CSLND_SLND"},{"name":"CSRM_SRM"},{"name":"CRAY_RAY"},{"name":"CFTT_FTT"},{"name":"CSTSOL_STSOL"},{"name":"SLND"},{"name":"DAI"},{"name":"JSOL_SOL"},{"name":"USH"},{"name":"UXD"},{"name":"USDH_TWAP"},{"name":"USH_TWAP"},{"name":"UXD_TWAP"},{"name":"HDG"},{"name":"DUST"},{"name":"USDR"},{"name":"USDR_TWAP"},{"name":"RATIO"},{"name":"UXP"},{"name":"KUXDUSDCORCA"},{"name":"JITOSOL_SOL"},{"name":"SOL_EMA"},{"name":"ETH_EMA"},{"name":"BTC_EMA"},{"name":"SRM_EMA"},{"name":"RAY_EMA"},{"name":"FTT_EMA"},{"name":"MSOL_EMA"},{"name":"BNB_EMA"},{"name":"AVAX_EMA"},{"name":"STSOL_EMA"},{"name":"USDC_EMA"},{"name":"USDT_EMA"},{"name":"SLND_EMA"},{"name":"DAI_EMA"},{"name":"wstETH_TWAP"},{"name":"DUST_TWAP"},{"name":"BONK"},{"name":"BONK_TWAP"},{"name":"SAMO"},{"name":"SAMO_TWAP"},{"name":"BSOL"},{"name":"LaineSOL"}]}},{"name":"DEX","type":{"kind":"enum","variants":[{"name":"Orca"},{"name":"Raydium"},{"name":"Meteora"}]}}],"errors":[{"code":6000,"name":"IntegerOverflow","msg":"Integer overflow"},{"code":6001,"name":"OperationForbidden","msg":"Operation Forbidden"},{"code":6002,"name":"ZeroAmount","msg":"[DEPRECATED] Zero amount"},{"code":6003,"name":"UnableToDeserializeAccount","msg":"Unable to deserialize account"},{"code":6004,"name":"VaultBalanceDoesNotMatchTokenA","msg":"[DEPRECATED] Vault balance does not match for token A"},{"code":6005,"name":"VaultBalanceDoesNotMatchTokenB","msg":"[DEPRECATED] Vault balance does not match for token B"},{"code":6006,"name":"SharesIssuedAmountDoesNotMatch","msg":"[DEPRECATED] Shares issued amount does not match"},{"code":6007,"name":"GlobalConfigKeyError","msg":"Key is not present in global config"},{"code":6008,"name":"SystemInEmergencyMode","msg":"[DEPRECATED] System is in emergency mode"},{"code":6009,"name":"GlobalDepositBlocked","msg":"Global deposit is currently blocked"},{"code":6010,"name":"GlobalWithdrawBlocked","msg":"Global withdraw is currently blocked"},{"code":6011,"name":"GlobalInvestBlocked","msg":"Global invest is currently blocked"},{"code":6012,"name":"OutOfRangeIntegralConversion","msg":"Out of range integral conversion attempted"},{"code":6013,"name":"MathOverflow","msg":"[DEPRECATED] Mathematical operation with overflow"},{"code":6014,"name":"TooMuchLiquidityToWithdraw","msg":"Unable to withdraw more liquidity than available in position"},{"code":6015,"name":"DepositAmountsZero","msg":"Deposit amounts must be greater than zero"},{"code":6016,"name":"SharesZero","msg":"Number of shares to withdraw must be greater than zero"},{"code":6017,"name":"StrategyNotActive","msg":"Strategy not active"},{"code":6018,"name":"UnharvestedAmounts","msg":"There are unharvested gains"},{"code":6019,"name":"InvalidRewardMapping","msg":"Reward mapping incorrect"},{"code":6020,"name":"InvalidRewardIndex","msg":"Reward index incorrect"},{"code":6021,"name":"OwnRewardUninitialized","msg":"Cannot use uninitialized reward vault"},{"code":6022,"name":"PriceNotValid","msg":"Price is not valid"},{"code":6023,"name":"SwapRewardImbalanced","msg":"Must provide almost equal amounts of tokens"},{"code":6024,"name":"SwapRewardTooSmall","msg":"Swap reward is zero or less than requested"},{"code":6025,"name":"SwapRewardLessThanRequested","msg":"Swap reward is less than what user requested as minimum"},{"code":6026,"name":"SwapRewardLessThanMinimum","msg":"Swap reward is less than minimum acceptable"},{"code":6027,"name":"WrongDiscriminator","msg":"Wrong discriminator"},{"code":6028,"name":"WrongMint","msg":"Wrong mint"},{"code":6029,"name":"WrongVault","msg":"Wrong vault"},{"code":6030,"name":"SwapAmountsZero","msg":"Swap amounts must be greater than zero"},{"code":6031,"name":"PriceTooOld","msg":"Price too old"},{"code":6032,"name":"CannotInvestZeroAmount","msg":"Cannot invest zero amount"},{"code":6033,"name":"MaxInvestableZero","msg":"Cannot have zero investable amount"},{"code":6034,"name":"CollectFeesBlocked","msg":"Collect fees is blocked"},{"code":6035,"name":"CollectRewardsBlocked","msg":"Collect rewards is blocked"},{"code":6036,"name":"SwapRewardsBlocked","msg":"Swap rewards is blocked"},{"code":6037,"name":"WrongRewardCollateralID","msg":"Reward collateral ID is incorrect for strategy"},{"code":6038,"name":"InvalidPositionAccount","msg":"Position account doesn't match internal records"},{"code":6039,"name":"CouldNotDeserializeScope","msg":"Scope account could not be deserialized"},{"code":6040,"name":"WrongCollateralID","msg":"[DEPRECATED] Collateral ID invalid for strategy"},{"code":6041,"name":"CollateralTokensExceedDepositCap","msg":"Collaterals exceed deposit cap"},{"code":6042,"name":"SwapUnevenVaultsBlocked","msg":"Swap uneven vaults is blocked"},{"code":6043,"name":"VaultsAreAlreadyBalanced","msg":"Cannot swap as vaults are already balanced"},{"code":6044,"name":"CannotSwapUnevenOutOfRange","msg":"Cannot swap uneven vaults when position is out of range"},{"code":6045,"name":"DivideByZero","msg":"Cannot divide by zero"},{"code":6046,"name":"DeltaATooLarge","msg":"[DEPRECATED] Delta A too large"},{"code":6047,"name":"DeltaBTooLarge","msg":"[DEPRECATED] Delta B too large"},{"code":6048,"name":"CannotExecutiveWithdrawZeroAmount","msg":"[DEPRECATED] Cannot executive withdraw zero amount"},{"code":6049,"name":"CannotWithdrawZeroAmount","msg":"Cannot withdraw zero amount"},{"code":6050,"name":"CannotCollectFeesOnZeroLiquidityPosition","msg":"[DEPRECATED] Cannot collect fees on zero liquidity position"},{"code":6051,"name":"StrategyNotActiveWhenDepositing","msg":"Cannot deposit inactive position"},{"code":6052,"name":"CannotOpenPositionWithExistingPosition","msg":"Cannot open position with existing opened position"},{"code":6053,"name":"CollateralTokensExceedDepositCapPerIxn","msg":"Collaterals exceed deposit ixn cap"},{"code":6054,"name":"CannotDepositOutOfRange","msg":"Cannot deposit when strategy out of range"},{"code":6055,"name":"CannotInvestOutOfRange","msg":"Cannot invest when strategy out of range"},{"code":6056,"name":"WithdrawalCapReached","msg":"Withdrawal cap is reached"},{"code":6057,"name":"TimestampDecrease","msg":"Timestamp decrease"},{"code":6058,"name":"CPINotAllowed","msg":"CPI not allowed"},{"code":6059,"name":"OrcaPriceTooDifferentFromScope","msg":"Cannot use orca price as it is too different from scope price"},{"code":6060,"name":"LowerTickLargerThanUpperTick","msg":"Lower tick larger than upper tick"},{"code":6061,"name":"LowerTickTooLow","msg":"Lower tick is lower than the minimal supported low tick"},{"code":6062,"name":"UpperTickTooLarge","msg":"Upper tick is larger than the maximum supported tick"},{"code":6063,"name":"LowerTickNotMultipleOfTickSpacing","msg":"Lower tick is not a multiple of tick spacing"},{"code":6064,"name":"UpperTickNotMultipleOfTickSpacing","msg":"Upper tick is not a multiple of tick spacing"},{"code":6065,"name":"CannotChangeAdminAuthority","msg":"Cannot change admin authority"},{"code":6066,"name":"CannotResizeAccount","msg":"Cannot resize with smaller new size"},{"code":6067,"name":"ScopeChainUpdateFailed","msg":"Scope chain update failed"},{"code":6068,"name":"PriceTooDivergentFromTwap","msg":"Price too divergent from twap"},{"code":6069,"name":"ExistingRewardOverride","msg":"[DEPRECATED] Can not override the existing reward"},{"code":6070,"name":"WrongKaminoRewardId","msg":"Kamino reward id exceeds the available slots"},{"code":6071,"name":"KaminoRewardNotExist","msg":"Kamino reward is not initialized"},{"code":6072,"name":"KaminoRewardAlreadyExists","msg":"Kamino reward is already initialized"},{"code":6073,"name":"KaminoCollateralNotValid","msg":"Kamino collateral is not valid"},{"code":6074,"name":"KaminoRewardExceedsAvailableAmount","msg":"[DEPRECATED] Expected kamino reward is bigger then the available amount within the vault"},{"code":6075,"name":"SwapUnevenVaultsOvershoot","msg":"Swap uneven vaults result in the opposite unbalance of the vaults"},{"code":6076,"name":"BpsNotInRange","msg":"Bps parameter passed to instruction is not in range"},{"code":6077,"name":"EmergencySwapBlocked","msg":"Emergency Swap is blocked"},{"code":6078,"name":"StrategyNotFrozen","msg":"Strategy is expected to be frozen for this operation"},{"code":6079,"name":"UnexpectedTokenAmountsPostSwap","msg":"Token left in vault post swap are lower than expected"},{"code":6080,"name":"AccountNotBelongToDEX","msg":"Account doesn't belong to the DEX"},{"code":6081,"name":"WrongDEXProgramID","msg":"Wrong DEX program ID"},{"code":6082,"name":"OrcaRewardUninitialized","msg":"Cannot use uninitialized orca reward vault"},{"code":6083,"name":"InvalidAdminAuthority","msg":"Invalid admin authority"},{"code":6084,"name":"PriceIsBiggerThanHeuristic","msg":"Token price is bigger than heuristic"},{"code":6085,"name":"PriceIsLowerThanHeuristic","msg":"Token price is lower than heuristic"},{"code":6086,"name":"AccountDifferentThanExpected","msg":"Account different than expected"},{"code":6087,"name":"SwapAmountsTooSmall","msg":"Swap amount below the minimum value"},{"code":6088,"name":"InvalidDexProgramId","msg":"Invalid dex program id"},{"code":6089,"name":"StrategyDepositBlocked","msg":"Strategy deposit is currently blocked"},{"code":6090,"name":"StrategyInvestBlocked","msg":"Strategy invest is currently blocked"},{"code":6091,"name":"StrategyWithdrawBlocked","msg":"Strategy withdraw is currently blocked"},{"code":6092,"name":"WrongSwapVaultDirection","msg":"Vault swap can't be performed in the required direction"},{"code":6093,"name":"SwapVaultsTooBig","msg":"Provided amount for vault swap is over the limit"},{"code":6094,"name":"SwapVaultsCashOutputBelowMinimum","msg":"Token out for cash based vault swap is below minimum expected"},{"code":6095,"name":"FlashIxsNotEnded","msg":"Flash ixs initiated without the closing ix in the transaction"},{"code":6096,"name":"FlashTxWithUnexpectedIxs","msg":"Some unexpected instructions are present in the tx. Either before or after the flash ixs, or some ix target the same program between"},{"code":6097,"name":"FlashIxsAccountMismatch","msg":"Some accounts differ between the two flash ixs"},{"code":6098,"name":"FlashIxsIncludeScope","msg":"A scope ix is present in a flash tx"},{"code":6099,"name":"FlashVaultSwapBlocked","msg":"Flash vault swap is blocked on this strategy"},{"code":6100,"name":"FlashVaultSwapWrongAmountToLeave","msg":"Unexpected amount of tokens in ata prior flash vault swap (wrong amount_to_leave_to_user)"},{"code":6101,"name":"DepositLessThanMinimum","msg":"Deposit amount less than minimal allowed"},{"code":6102,"name":"DepositWithoutInvestDisallowed","msg":"Cannot deposit without invest"},{"code":6103,"name":"InvalidScopeChain","msg":"Invalid Scope Chain"},{"code":6104,"name":"InvalidScopeTWAPChain","msg":"Invalid Scope TWAP Chain"},{"code":6105,"name":"PositionHasRemainingLiquidity","msg":"Existent position has liquidity, new position creation is forbidden"},{"code":6106,"name":"PoolRebalancing","msg":"Deposit is not allowed as pool is rebalancing"},{"code":6107,"name":"PermissionlessRebalancingDisabled","msg":"Permissionless rebalancing is disabled"},{"code":6108,"name":"ManualRebalanceInvalidOwner","msg":"Only the owner of the strategy can manually rebalance it"},{"code":6109,"name":"InvalidRebalanceType","msg":"Invalid rebalance type for the strategy"},{"code":6110,"name":"NoRebalanceNecessary","msg":"No rebalance necessary based on current rebalance type/parameters"},{"code":6111,"name":"TickArraysDoNotMatchRebalance","msg":"The given tick arrays do not match the rebalance result"},{"code":6112,"name":"StrategyPositionNotValid","msg":"Expected strategy position to be initialized"},{"code":6113,"name":"CouldNotDeserializeRebalanceState","msg":"Rebalance state could not be deserialized"},{"code":6114,"name":"CouldNotSerializeRebalanceState","msg":"Rebalance state could not be serialized"},{"code":6115,"name":"CouldNotDeserializeRebalanceParams","msg":"Rebalance params could not be deserialized"},{"code":6116,"name":"NotEnoughTokensForRatio","msg":"Deposit is not allowed as token amounts are not enough to match our holdings ratio"},{"code":6117,"name":"AmountsRepresentZeroShares","msg":"The provided amounts are too small"},{"code":6118,"name":"MaxLossExceeded","msg":"Rouding errors exceed the maximal loss tolerance"},{"code":6119,"name":"RewardNotStrategyToken","msg":"Reward does not match strategy token"},{"code":6120,"name":"DecimalToU64ConversionFailed","msg":"Decimal to u64 conversion failed"},{"code":6121,"name":"DecimalOperationFailed","msg":"Decimal operation failed"},{"code":6122,"name":"VaultBalancesCausesWrongSharesIssuance","msg":"Deposit is not allowed as the strategy is not fully invested in the pool "},{"code":6123,"name":"TokenDisabled","msg":"Token cannot be used in strategy creation"},{"code":6124,"name":"InvalidReferencePriceType","msg":"Invalid reference price type"},{"code":6125,"name":"TokenToSwapNotEnough","msg":"Token amount to be swapped is not enough"},{"code":6126,"name":"TokenAccountBalanceMismatch","msg":"Token amount in ata is different than the expected amount"},{"code":6127,"name":"UnexpectedProgramIdForPrerequisiteIx","msg":"Unexpected programID for prerequisite ix"},{"code":6128,"name":"ComputeFeesAndRewardsUpdateError","msg":"Got an error from the dex specific function while computing the fees/rewards update"},{"code":6129,"name":"SharesNotZero","msg":"There must be no shares issued when closing a strategy"},{"code":6130,"name":"InvalidScopeStakingRateChain","msg":"Invalid Scope staking rate Chain"},{"code":6131,"name":"StakingRateNotValid","msg":"Staking rate (provided by Scope) is not valid"},{"code":6132,"name":"DecimalToU128ConversionFailed","msg":"Decimal to u128 conversion failed"},{"code":6133,"name":"DecimalNegativeSqrtRoot","msg":"Decimal sqrt on negative number"},{"code":6134,"name":"DriftingOppositeDirection","msg":"Drifting strategy is moving in the opposite direction"},{"code":6135,"name":"WrongRewardCollateralId","msg":"Wrong reward collateral_id"},{"code":6136,"name":"CollateralInfoAlreadyExists","msg":"Collateral info already exists for given index"},{"code":6137,"name":"InvestTooEarly","msg":"Invest is too early after the position was opened"},{"code":6138,"name":"SwapUnevenTooEarly","msg":"Swap uneven is too early after the position was opened"},{"code":6139,"name":"FlashSwapTooEarly","msg":"Flash swap is too early after the position was opened"},{"code":6140,"name":"RebalancesCapReached","msg":"Rebalance caps reached, no rebalances are allowed until the end of the current interval"},{"code":6141,"name":"SwapUnevenInvalidAuthority","msg":"Cannot swap uneven because authority is set and the given signer does not correspond"},{"code":6142,"name":"InvalidTick","msg":"Invalid tick requested"},{"code":6143,"name":"MeteoraMathOverflow","msg":"Meteora math overflowed"},{"code":6144,"name":"StrategyTickArrayNotValid","msg":"Expected strategy tick arrays to be initialized"},{"code":6145,"name":"WrongEventAuthority","msg":"Wrong event authority"},{"code":6146,"name":"StrategyFieldUpdateNotAllowed","msg":"Strategy field update is not allowed"},{"code":6147,"name":"UnsupportedDex","msg":"DEX is not supported for this operation"},{"code":6148,"name":"InvalidBPSValue","msg":"Invalid BPS value provided"},{"code":6149,"name":"RewardVaultOverrideNotAllowed","msg":"Reward vault override not allowed"},{"code":6150,"name":"ComputeFeesAndRewardsInvalidReward","msg":"Got invalid reward from the dex specific function while computing the fees/rewards update"},{"code":6151,"name":"EmptyTreasury","msg":"No tokens to withdraw from treasury fee vault"},{"code":6152,"name":"ChangingPoolRewardMintMismatch","msg":"New pool reward mint does not match the old pool reward mint"},{"code":6153,"name":"ProvidedRewardVaultMismatch","msg":"The provided reward vault does not match the strategy state"},{"code":6154,"name":"RepeatedMint","msg":"The provided reward vault does not match the strategy state"},{"code":6155,"name":"UnsupportedTokenExtension","msg":"The token extension is not supported by the program"},{"code":6156,"name":"UnsupportedDexForToken22","msg":"Cannot initialize strategy with this dex while having a mint with token22"},{"code":6157,"name":"GlobalConfigInvalidScopePriceIndex","msg":"Scope price index is not present in global config"},{"code":6158,"name":"GlobalConfigScopePricesIdsFull","msg":"Scope prices ids array in global config is full"},{"code":6159,"name":"GlobalConfigAlreadyContainsScopePriceId","msg":"Scope prices ids array already contains given scope price id"},{"code":6160,"name":"GlobalConfigExpectedScopePricesAccount","msg":"Expected scope prices account being passed in when adding or updating a scope prices feed"},{"code":6161,"name":"RewardScopePriceAccountNotPresent","msg":"A reward scope price account should have been passed in, but it is missing"},{"code":6162,"name":"IncorrectScopePricesAccountID","msg":"Incorrect scope prices account id passed in"},{"code":6163,"name":"CouldNotCalculatePriceTwap","msg":"Could not calculate get_price_usd_unchecked()"},{"code":6164,"name":"ReferencePriceTooFarFromPoolPrice","msg":"Reference price is too far from the current pool price"},{"code":6165,"name":"InvestCooldownNotElapsed","msg":"Invest cooldown has not elapsed"},{"code":6166,"name":"InvestAmountBelowMinimum","msg":"Uninvested value is below the minimum invest trigger"},{"code":6167,"name":"DepositAndInvestDisabled","msg":"Deposit and invest is disabled on mainnet"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/kamino_farms.FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr.json b/migration/khadhroony-bot2-reference/idls/kamino_farms.FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr.json new file mode 100644 index 0000000..730916d --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/kamino_farms.FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr.json @@ -0,0 +1 @@ +{"version":"1.6.5","name":"farms","instructions":[{"name":"initializeGlobalConfig","accounts":[{"name":"globalAdmin","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false},{"name":"treasuryVaultsAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateGlobalConfig","accounts":[{"name":"globalAdmin","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false}],"args":[{"name":"mode","type":"u8"},{"name":"value","type":{"array":["u8",32]}}]},{"name":"initializeFarm","accounts":[{"name":"farmAdmin","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"farmVault","isMut":true,"isSigner":false},{"name":"farmVaultsAuthority","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeFarmDelegated","accounts":[{"name":"farmAdmin","isMut":true,"isSigner":true},{"name":"farmDelegate","isMut":false,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"farmVaultsAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeReward","accounts":[{"name":"farmAdmin","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"rewardMint","isMut":false,"isSigner":false},{"name":"rewardVault","isMut":true,"isSigner":false},{"name":"rewardTreasuryVault","isMut":true,"isSigner":false},{"name":"farmVaultsAuthority","isMut":false,"isSigner":false},{"name":"treasuryVaultsAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"addRewards","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"rewardMint","isMut":false,"isSigner":false},{"name":"rewardVault","isMut":true,"isSigner":false},{"name":"farmVaultsAuthority","isMut":false,"isSigner":false},{"name":"payerRewardTokenAta","isMut":true,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"rewardIndex","type":"u64"}]},{"name":"updateFarmConfig","accounts":[{"name":"signer","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true}],"args":[{"name":"mode","type":"u16"},{"name":"data","type":"bytes"}]},{"name":"initializeUser","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"owner","isMut":false,"isSigner":false},{"name":"delegatee","isMut":false,"isSigner":false},{"name":"userState","isMut":true,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"transferOwnership","accounts":[{"name":"oldOwner","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"newOwner","isMut":false,"isSigner":false},{"name":"oldUserState","isMut":true,"isSigner":false},{"name":"newUserState","isMut":true,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"rewardUserOnce","accounts":[{"name":"delegateAuthority","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"userState","isMut":true,"isSigner":false}],"args":[{"name":"rewardIndex","type":"u64"},{"name":"amount","type":"u64"},{"name":"expectedRewardIssuedUnclaimed","type":"u64"}]},{"name":"refreshFarm","accounts":[{"name":"farmState","isMut":true,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true}],"args":[]},{"name":"stake","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"userState","isMut":true,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false},{"name":"farmVault","isMut":true,"isSigner":false},{"name":"userAta","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"setStakeDelegated","accounts":[{"name":"delegateAuthority","isMut":false,"isSigner":true},{"name":"userState","isMut":true,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false}],"args":[{"name":"newAmount","type":"u64"}]},{"name":"harvestReward","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"userState","isMut":true,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"rewardMint","isMut":false,"isSigner":false},{"name":"userRewardTokenAccount","isMut":true,"isSigner":false},{"name":"rewardsVault","isMut":true,"isSigner":false},{"name":"rewardsTreasuryVault","isMut":true,"isSigner":false},{"name":"farmVaultsAuthority","isMut":false,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"rewardIndex","type":"u64"}]},{"name":"unstake","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"userState","isMut":true,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true}],"args":[{"name":"stakeSharesScaled","type":"u128"}]},{"name":"refreshUserState","accounts":[{"name":"userState","isMut":true,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true}],"args":[]},{"name":"withdrawUnstakedDeposits","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"userState","isMut":true,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false},{"name":"userAta","isMut":true,"isSigner":false},{"name":"farmVault","isMut":true,"isSigner":false},{"name":"farmVaultsAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"withdrawTreasury","accounts":[{"name":"globalAdmin","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"rewardMint","isMut":false,"isSigner":false},{"name":"rewardTreasuryVault","isMut":true,"isSigner":false},{"name":"treasuryVaultAuthority","isMut":false,"isSigner":false},{"name":"withdrawDestinationTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"depositToFarmVault","accounts":[{"name":"depositor","isMut":false,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"farmVault","isMut":true,"isSigner":false},{"name":"depositorAta","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdrawFromFarmVault","accounts":[{"name":"withdrawAuthority","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"withdrawerTokenAccount","isMut":true,"isSigner":false},{"name":"farmVault","isMut":true,"isSigner":false},{"name":"farmVaultsAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdrawSlashedAmount","accounts":[{"name":"crank","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"slashedAmountSpillAddress","isMut":true,"isSigner":false},{"name":"farmVault","isMut":true,"isSigner":false},{"name":"farmVaultsAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateFarmAdmin","accounts":[{"name":"pendingFarmAdmin","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false}],"args":[]},{"name":"updateGlobalConfigAdmin","accounts":[{"name":"pendingGlobalAdmin","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false}],"args":[]},{"name":"withdrawReward","accounts":[{"name":"farmAdmin","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"rewardMint","isMut":false,"isSigner":false},{"name":"rewardVault","isMut":true,"isSigner":false},{"name":"farmVaultsAuthority","isMut":false,"isSigner":false},{"name":"adminRewardTokenAta","isMut":true,"isSigner":false},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"rewardIndex","type":"u64"}]},{"name":"updateSecondDelegatedAuthority","accounts":[{"name":"globalAdmin","isMut":true,"isSigner":true},{"name":"farmState","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"newSecondDelegatedAuthority","isMut":false,"isSigner":false}],"args":[]},{"name":"idlMissingTypes","accounts":[{"name":"globalAdmin","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false}],"args":[{"name":"globalConfigOptionKind","type":{"defined":"GlobalConfigOption"}},{"name":"farmConfigOptionKind","type":{"defined":"FarmConfigOption"}},{"name":"timeUnit","type":{"defined":"TimeUnit"}},{"name":"lockingMode","type":{"defined":"LockingMode"}},{"name":"rewardType","type":{"defined":"RewardType"}}]}],"accounts":[{"name":"FarmState","type":{"kind":"struct","fields":[{"name":"farmAdmin","type":"publicKey"},{"name":"globalConfig","type":"publicKey"},{"name":"token","type":{"defined":"TokenInfo"}},{"name":"rewardInfos","type":{"array":[{"defined":"RewardInfo"},10]}},{"name":"numRewardTokens","type":"u64"},{"name":"numUsers","docs":["Data used to calculate the rewards of the user"],"type":"u64"},{"name":"totalStakedAmount","docs":["The number of token in the `farm_vault` staked (getting rewards and fees)","Set such as `farm_vault.amount = total_staked_amount + total_pending_amount`"],"type":"u64"},{"name":"farmVault","type":"publicKey"},{"name":"farmVaultsAuthority","type":"publicKey"},{"name":"farmVaultsAuthorityBump","type":"u64"},{"name":"delegateAuthority","docs":["Only used for delegate farms","Set to `default()` otherwise"],"type":"publicKey"},{"name":"timeUnit","docs":["Raw representation of a `TimeUnit`","Seconds = 0, Slots = 1"],"type":"u8"},{"name":"isFarmFrozen","docs":["Automatically set to true in case of a full authority withdrawal","If true, the farm is frozen and no more deposits are allowed"],"type":"u8"},{"name":"isFarmDelegated","docs":["Indicates if the farm is a delegate farm","If true, the farm is a delegate farm and the `delegate_authority` is set*"],"type":"u8"},{"name":"isRewardUserOnceEnabled","docs":["If set to 1, indicates that the \"reward user once\" feature is enabled"],"type":"u8"},{"name":"isHarvestingPermissionless","type":"u8"},{"name":"padding0","type":{"array":["u8",3]}},{"name":"withdrawAuthority","docs":["Withdraw authority for the farm, allowed to lock deposited funds and withdraw them","Set to `default()` if unused (only the depositors can withdraw their funds)"],"type":"publicKey"},{"name":"depositWarmupPeriod","docs":["Delay between a user deposit and the moment it is considered as staked","0 if unused"],"type":"u32"},{"name":"withdrawalCooldownPeriod","docs":["Delay between a user unstake and the ability to withdraw his deposit."],"type":"u32"},{"name":"totalActiveStakeScaled","docs":["Total active stake of tokens in the farm (scaled from `Decimal` representation)."],"type":"u128"},{"name":"totalPendingStakeScaled","docs":["Total pending stake of tokens in the farm (scaled from `Decimal` representation).","(can be used by `withdraw_authority` but don't get rewards or fees)"],"type":"u128"},{"name":"totalPendingAmount","docs":["Total pending amount of tokens in the farm"],"type":"u64"},{"name":"slashedAmountCurrent","docs":["Slashed amounts from early withdrawal"],"type":"u64"},{"name":"slashedAmountCumulative","type":"u64"},{"name":"slashedAmountSpillAddress","type":"publicKey"},{"name":"lockingMode","docs":["Locking stake"],"type":"u64"},{"name":"lockingStartTimestamp","type":"u64"},{"name":"lockingDuration","type":"u64"},{"name":"lockingEarlyWithdrawalPenaltyBps","type":"u64"},{"name":"depositCapAmount","type":"u64"},{"name":"scopePrices","type":"publicKey"},{"name":"scopeOraclePriceId","type":"u64"},{"name":"scopeOracleMaxAge","type":"u64"},{"name":"pendingFarmAdmin","type":"publicKey"},{"name":"strategyId","type":"publicKey"},{"name":"delegatedRpsAdmin","type":"publicKey"},{"name":"vaultId","type":"publicKey"},{"name":"secondDelegatedAuthority","type":"publicKey"},{"name":"padding","type":{"array":["u64",74]}}]}},{"name":"GlobalConfig","type":{"kind":"struct","fields":[{"name":"globalAdmin","type":"publicKey"},{"name":"treasuryFeeBps","type":"u64"},{"name":"treasuryVaultsAuthority","type":"publicKey"},{"name":"treasuryVaultsAuthorityBump","type":"u64"},{"name":"pendingGlobalAdmin","type":"publicKey"},{"name":"padding1","type":{"array":["u128",126]}}]}},{"name":"UserState","type":{"kind":"struct","fields":[{"name":"userId","type":"u64"},{"name":"farmState","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"isFarmDelegated","docs":["Indicate if this user state is part of a delegated farm"],"type":"u8"},{"name":"padding0","type":{"array":["u8",7]}},{"name":"rewardsTallyScaled","docs":["Rewards tally used for computation of gained rewards","(scaled from `Decimal` representation)."],"type":{"array":["u128",10]}},{"name":"rewardsIssuedUnclaimed","docs":["Number of reward tokens ready for claim"],"type":{"array":["u64",10]}},{"name":"lastClaimTs","type":{"array":["u64",10]}},{"name":"activeStakeScaled","docs":["User stake deposited and usable, generating rewards and fees.","(scaled from `Decimal` representation)."],"type":"u128"},{"name":"pendingDepositStakeScaled","docs":["User stake deposited but not usable and not generating rewards yet.","(scaled from `Decimal` representation)."],"type":"u128"},{"name":"pendingDepositStakeTs","docs":["After this timestamp, pending user stake can be moved to user stake","Initialized to now() + delayed user stake period"],"type":"u64"},{"name":"pendingWithdrawalUnstakeScaled","docs":["User deposits unstaked, pending for withdrawal, not usable and not generating rewards.","(scaled from `Decimal` representation)."],"type":"u128"},{"name":"pendingWithdrawalUnstakeTs","docs":["After this timestamp, user can withdraw their deposit."],"type":"u64"},{"name":"bump","docs":["User bump used for account address validation"],"type":"u64"},{"name":"delegatee","docs":["Delegatee used for initialisation - useful to check against"],"type":"publicKey"},{"name":"lastStakeTs","type":"u64"},{"name":"rewardsIssuedCumulative","docs":["Cumulative rewards issued to the user - ONLY used for stats/analytics","DO NOT USE IN ANY CALCULATIONS","Old userStates will have this field populated only from the point of release","not reflecting any historical data before this was released"],"type":{"array":["u64",10]}},{"name":"padding1","type":{"array":["u64",40]}}]}},{"name":"OraclePrices","type":{"kind":"struct","fields":[{"name":"oracleMappings","type":"publicKey"},{"name":"prices","type":{"array":[{"defined":"DatedPrice"},512]}}]}}],"types":[{"name":"FarmConfigOption","type":{"kind":"enum","variants":[{"name":"UpdateRewardRps"},{"name":"UpdateRewardMinClaimDuration"},{"name":"WithdrawAuthority"},{"name":"DepositWarmupPeriod"},{"name":"WithdrawCooldownPeriod"},{"name":"RewardType"},{"name":"RpsDecimals"},{"name":"LockingMode"},{"name":"LockingStartTimestamp"},{"name":"LockingDuration"},{"name":"LockingEarlyWithdrawalPenaltyBps"},{"name":"DepositCapAmount"},{"name":"SlashedAmountSpillAddress"},{"name":"ScopePricesAccount"},{"name":"ScopeOraclePriceId"},{"name":"ScopeOracleMaxAge"},{"name":"UpdateRewardScheduleCurvePoints"},{"name":"UpdatePendingFarmAdmin"},{"name":"UpdateStrategyId"},{"name":"UpdateDelegatedRpsAdmin"},{"name":"UpdateVaultId"},{"name":"UpdateExtraDelegatedAuthority"},{"name":"UpdateIsRewardUserOnceEnabled"},{"name":"UpdateDelegatedAuthority"},{"name":"UpdateIsHarvestingPermissionless"}]}},{"name":"GlobalConfigOption","type":{"kind":"enum","variants":[{"name":"SetPendingGlobalAdmin"},{"name":"SetTreasuryFeeBps"}]}},{"name":"LockingMode","type":{"kind":"enum","variants":[{"name":"None"},{"name":"Continuous"},{"name":"WithExpiry"}]}},{"name":"RewardInfo","type":{"kind":"struct","fields":[{"name":"token","type":{"defined":"TokenInfo"}},{"name":"rewardsVault","type":"publicKey"},{"name":"rewardsAvailable","type":"u64"},{"name":"rewardScheduleCurve","type":{"defined":"RewardScheduleCurve"}},{"name":"minClaimDurationSeconds","type":"u64"},{"name":"lastIssuanceTs","type":"u64"},{"name":"rewardsIssuedUnclaimed","type":"u64"},{"name":"rewardsIssuedCumulative","type":"u64"},{"name":"rewardPerShareScaled","type":"u128"},{"name":"placeholder0","type":"u64"},{"name":"rewardType","type":"u8"},{"name":"rewardsPerSecondDecimals","type":"u8"},{"name":"padding0","type":{"array":["u8",6]}},{"name":"padding1","type":{"array":["u64",20]}}]}},{"name":"RewardPerTimeUnitPoint","type":{"kind":"struct","fields":[{"name":"tsStart","type":"u64"},{"name":"rewardPerTimeUnit","type":"u64"}]}},{"name":"RewardScheduleCurve","type":{"kind":"struct","fields":[{"name":"points","docs":["This is a stepwise function, meaning that each point represents","how many rewards are issued per time unit since the beginning","of that point until the beginning of the next point.","This is not a linear curve, there is no interpolation going on.","A curve can be [[t0, 100], [t1, 50], [t2, 0]]","meaning that from t0 to t1, 100 rewards are issued per time unit,","from t1 to t2, 50 rewards are issued per time unit, and after t2 it stops","Another curve, can be [[t0, 100], [u64::max, 0]]","meaning that from t0 to u64::max, 100 rewards are issued per time unit"],"type":{"array":[{"defined":"RewardPerTimeUnitPoint"},20]}}]}},{"name":"RewardType","type":{"kind":"enum","variants":[{"name":"Proportional"},{"name":"Constant"}]}},{"name":"TimeUnit","type":{"kind":"enum","variants":[{"name":"Seconds"},{"name":"Slots"}]}},{"name":"TokenInfo","type":{"kind":"struct","fields":[{"name":"mint","type":"publicKey"},{"name":"decimals","type":"u64"},{"name":"tokenProgram","type":"publicKey"},{"name":"padding","type":{"array":["u64",6]}}]}},{"name":"DatedPrice","type":{"kind":"struct","fields":[{"name":"price","type":{"defined":"Price"}},{"name":"lastUpdatedSlot","type":"u64"},{"name":"unixTimestamp","type":"u64"},{"name":"reserved","type":{"array":["u64",2]}},{"name":"reserved2","type":{"array":["u16",3]}},{"name":"index","type":"u16"}]}},{"name":"Price","type":{"kind":"struct","fields":[{"name":"value","type":"u64"},{"name":"exp","type":"u64"}]}}],"errors":[{"code":6000,"name":"StakeZero","msg":"Cannot stake 0 amount"},{"code":6001,"name":"UnstakeZero","msg":"Cannot unstake 0 amount"},{"code":6002,"name":"NothingToUnstake","msg":"Nothing to unstake"},{"code":6003,"name":"NoRewardToHarvest","msg":"No reward to harvest"},{"code":6004,"name":"NoRewardInList","msg":"Reward not present in reward list"},{"code":6005,"name":"RewardAlreadyInitialized","msg":"Reward already initialized"},{"code":6006,"name":"MaxRewardNumberReached","msg":"Max number of reward tokens reached"},{"code":6007,"name":"RewardDoesNotExist","msg":"Reward does not exist"},{"code":6008,"name":"WrongRewardVaultAccount","msg":"Reward vault exists but the account is wrong"},{"code":6009,"name":"RewardVaultMismatch","msg":"Reward vault pubkey does not match staking pool vault"},{"code":6010,"name":"RewardVaultAuthorityMismatch","msg":"Reward vault authority pubkey does not match staking pool vault"},{"code":6011,"name":"NothingStaked","msg":"Nothing staked, cannot collect any rewards"},{"code":6012,"name":"IntegerOverflow","msg":"Integer overflow"},{"code":6013,"name":"ConversionFailure","msg":"Conversion failure"},{"code":6014,"name":"UnexpectedAccount","msg":"Unexpected account in instruction"},{"code":6015,"name":"OperationForbidden","msg":"Operation forbidden"},{"code":6016,"name":"MathOverflow","msg":"Mathematical operation with overflow"},{"code":6017,"name":"MinClaimDurationNotReached","msg":"Minimum claim duration has not been reached"},{"code":6018,"name":"RewardsVaultHasDelegate","msg":"Reward vault has a delegate"},{"code":6019,"name":"RewardsVaultHasCloseAuthority","msg":"Reward vault has a close authority"},{"code":6020,"name":"FarmVaultHasDelegate","msg":"Farm vault has a delegate"},{"code":6021,"name":"FarmVaultHasCloseAuthority","msg":"Farm vault has a close authority"},{"code":6022,"name":"RewardsTreasuryVaultHasDelegate","msg":"Reward vault has a delegate"},{"code":6023,"name":"RewardsTreasuryVaultHasCloseAuthority","msg":"Reward vault has a close authority"},{"code":6024,"name":"UserAtaRewardVaultMintMissmatch","msg":"User ata and reward vault have different mints"},{"code":6025,"name":"UserAtaFarmTokenMintMissmatch","msg":"User ata and farm token have different mints"},{"code":6026,"name":"TokenFarmTokenMintMissmatch","msg":"Token mint and farm token have different mints"},{"code":6027,"name":"RewardAtaRewardMintMissmatch","msg":"Reward ata mint is different than reward mint"},{"code":6028,"name":"RewardAtaOwnerNotPayer","msg":"Reward ata owner is different than payer"},{"code":6029,"name":"InvalidGlobalConfigMode","msg":"Mode to update global_config is invalid"},{"code":6030,"name":"RewardIndexOutOfRange","msg":"Reward Index is higher than number of rewards"},{"code":6031,"name":"NothingToWithdraw","msg":"No tokens available to withdraw"},{"code":6032,"name":"UserDelegatedFarmNonDelegatedMissmatch","msg":"user, user_ref, authority and payer must match for non-delegated farm"},{"code":6033,"name":"AuthorityFarmDelegateMissmatch","msg":"Authority must match farm delegate authority"},{"code":6034,"name":"FarmNotDelegated","msg":"Farm not delegated, can not complete operation"},{"code":6035,"name":"FarmDelegated","msg":"Operation not allowed for delegated farm"},{"code":6036,"name":"UnstakeNotElapsed","msg":"Unstake lockup period is not elapsed. Deposit is locked until end of unstake period"},{"code":6037,"name":"PendingWithdrawalNotWithdrawnYet","msg":"Pending withdrawal already exist and not withdrawn yet"},{"code":6038,"name":"DepositZero","msg":"Cannot deposit zero amount directly to farm vault"},{"code":6039,"name":"InvalidConfigValue","msg":"Invalid config value"},{"code":6040,"name":"InvalidPenaltyPercentage","msg":"Invalid penalty percentage"},{"code":6041,"name":"EarlyWithdrawalNotAllowed","msg":"Early withdrawal not allowed"},{"code":6042,"name":"InvalidLockingTimestamps","msg":"Invalid locking timestamps"},{"code":6043,"name":"InvalidRpsCurvePoint","msg":"Invalid reward rate curve point"},{"code":6044,"name":"InvalidTimestamp","msg":"Invalid timestamp"},{"code":6045,"name":"DepositCapReached","msg":"Deposit cap reached"},{"code":6046,"name":"MissingScopePrices","msg":"Missing Scope Prices"},{"code":6047,"name":"ScopeOraclePriceTooOld","msg":"Scope Oracle Price Too Old"},{"code":6048,"name":"InvalidOracleConfig","msg":"Invalid Oracle Config"},{"code":6049,"name":"CouldNotDeserializeScope","msg":"Could not deserialize scope"},{"code":6050,"name":"RewardAtaOwnerNotAdmin","msg":"Reward ata owner is different than farm admin"},{"code":6051,"name":"WithdrawRewardZeroAvailable","msg":"Cannot withdraw reward as available amount is zero"},{"code":6052,"name":"RewardScheduleCurveSet","msg":"Cannot withdraw reward as reward schedule is set"},{"code":6053,"name":"UnsupportedTokenExtension","msg":"Cannot initialize farm while having a mint with token22 and requested extensions"},{"code":6054,"name":"InvalidFarmConfigUpdateAuthority","msg":"Invalid authority for updating farm config"},{"code":6055,"name":"InvalidTransferOwnershipOldOwner","msg":"Invalid authority for transfer ownersip new user state initialization"},{"code":6056,"name":"InvalidTransferOwnershipFarmState","msg":"Invalid farm state for transfer ownership new user state initialization"},{"code":6057,"name":"InvalidTransferOwnershipUserStateOwnerDelegatee","msg":"Invalid user state for transfer ownership, owner must match delegatee"},{"code":6058,"name":"InvalidTransferOwnershipFarmStateLockingMode","msg":"Invalid farm state locking mode for transfer ownership, must be 0"},{"code":6059,"name":"InvalidTransferOwnershipFarmStateWithdrawCooldownPeriod","msg":"Invalid farm state withdrawal cooldown period for transfer ownership, must be 0"},{"code":6060,"name":"InvalidTransferOwnershipStakeAmount","msg":"Invalid transfer ownership stake amount, must be equal to unstaked deposits"},{"code":6061,"name":"InvalidTransferOwnershipNewOwner","msg":"Invalid authority for transfer ownersip new user state initialization"},{"code":6062,"name":"InvalidTransferOwnershipFarmStateDepositWarmupPeriod","msg":"Invalid farm state deposit warmup period for transfer ownership, must be 0 if old user has stake"},{"code":6063,"name":"RewardUserOnceFeatureDisabled","msg":"Reward User Once feature is disabled"},{"code":6064,"name":"InvalidDelegatedAuthorityUpdate","msg":"Can not set delegate_authority to default pubkey - farm is delegated"},{"code":6065,"name":"UserTokenAccountOwnerMismatch","msg":"User token account owner does not match user state owner"},{"code":6066,"name":"HarvestingNotPermissionlessPayerMismatch","msg":"Harvesting is not permissionless, payer does not match user state owner"},{"code":6067,"name":"CurrentRewardIssuedUnclaimedMismatch","msg":"Current reward issued unclaimed does not match expected value"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/kamino_lending.KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD.json b/migration/khadhroony-bot2-reference/idls/kamino_lending.KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD.json new file mode 100644 index 0000000..566e666 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/kamino_lending.KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD.json @@ -0,0 +1 @@ +{"version":"1.23.0","name":"kamino_lending","instructions":[{"name":"initLendingMarket","accounts":[{"name":"lendingMarketOwner","isMut":true,"isSigner":true},{"name":"lendingMarket","isMut":true,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"quoteCurrency","type":{"array":["u8",32]}}]},{"name":"updateLendingMarket","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"lendingMarket","isMut":true,"isSigner":false}],"args":[{"name":"mode","type":"u64"},{"name":"value","type":{"array":["u8",72]}}]},{"name":"updateLendingMarketOwner","accounts":[{"name":"lendingMarketOwnerCached","isMut":false,"isSigner":true},{"name":"lendingMarket","isMut":true,"isSigner":false}],"args":[]},{"name":"initReserve","accounts":[{"name":"signer","isMut":true,"isSigner":true},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"feeReceiver","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveCollateralSupply","isMut":true,"isSigner":false},{"name":"initialLiquiditySource","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"cloneReserveConfig","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"targetLendingMarket","isMut":false,"isSigner":false,"docs":["The lending market of the [Self::target_reserve].","","The [Self::source_reserve] can come from a different market."]},{"name":"sourceReserve","isMut":false,"isSigner":false,"docs":["The reserve to be used as source for the configuration clone."]},{"name":"targetReserve","isMut":true,"isSigner":false,"docs":["The reserve to have its configuration replaced by a clone."]}],"args":[{"name":"customizations","type":{"defined":"ReserveConfigCustomizationArgs"}}]},{"name":"initFarmsForReserve","accounts":[{"name":"lendingMarketOwner","isMut":true,"isSigner":true},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"farmsProgram","isMut":false,"isSigner":false},{"name":"farmsGlobalConfig","isMut":false,"isSigner":false},{"name":"farmState","isMut":true,"isSigner":false},{"name":"farmsVaultAuthority","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"mode","type":"u8"}]},{"name":"updateReserveConfig","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false}],"args":[{"name":"mode","type":{"defined":"UpdateConfigMode"}},{"name":"value","type":"bytes"},{"name":"skipConfigIntegrityValidation","type":"bool"}]},{"name":"redeemFees","accounts":[{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveLiquidityFeeReceiver","isMut":true,"isSigner":false},{"name":"reserveSupplyLiquidity","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"withdrawProtocolFee","accounts":[{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":false,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"feeVault","isMut":true,"isSigner":false},{"name":"feeCollectorAta","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"seedDepositOnInitReserve","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"initialLiquiditySource","isMut":true,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"topupReserveRewards","docs":["Sponsor a reserve's rewards distribution by topping up its `rewards_amount_available` budget."],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"sourceLiquidity","isMut":true,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"socializeLoss","accounts":[{"name":"lendingMarketOwner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"socializeLossV2","accounts":[{"name":"socializeLossAccounts","accounts":[{"name":"lendingMarketOwner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"farmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"markObligationForDeleveraging","accounts":[{"name":"lendingMarketOwner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false}],"args":[{"name":"autodeleverageTargetLtvPct","type":"u8"}]},{"name":"refreshReserve","accounts":[{"name":"reserve","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"pythOracle","isMut":false,"isSigner":false,"isOptional":true},{"name":"switchboardPriceOracle","isMut":false,"isSigner":false,"isOptional":true},{"name":"switchboardTwapOracle","isMut":false,"isSigner":false,"isOptional":true},{"name":"scopePrices","isMut":false,"isSigner":false,"isOptional":true}],"args":[]},{"name":"refreshReservesBatch","accounts":[],"args":[{"name":"skipPriceUpdates","type":"bool"}]},{"name":"depositReserveLiquidity","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"reserve","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"userDestinationCollateral","isMut":true,"isSigner":false},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"redeemReserveCollateral","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"userSourceCollateral","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"}]},{"name":"initObligation","accounts":[{"name":"obligationOwner","isMut":false,"isSigner":true},{"name":"feePayer","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"seed1Account","isMut":false,"isSigner":false},{"name":"seed2Account","isMut":false,"isSigner":false},{"name":"ownerUserMetadata","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"InitObligationArgs"}}]},{"name":"initObligationFarmsForReserve","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"owner","isMut":false,"isSigner":false},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveFarmState","isMut":true,"isSigner":false},{"name":"obligationFarm","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"farmsProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"mode","type":"u8"}]},{"name":"refreshObligationFarmsForReserve","accounts":[{"name":"crank","isMut":false,"isSigner":true},{"name":"baseAccounts","accounts":[{"name":"obligation","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":false,"isSigner":false},{"name":"reserveFarmState","isMut":true,"isSigner":false,"docs":["`obligation_farm_user_state`'s own `farm_state` field, in the handler.","(Cannot live as an Anchor constraint because the handler is also reached","via `cpi_refresh_farms` from v2 host instructions, which bypass Anchor.)"]},{"name":"obligationFarmUserState","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false}]},{"name":"farmsProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"mode","type":"u8"}]},{"name":"refreshObligation","accounts":[{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"obligation","isMut":true,"isSigner":false}],"args":[]},{"name":"depositObligationCollateral","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"depositReserve","isMut":true,"isSigner":false},{"name":"reserveDestinationCollateral","isMut":true,"isSigner":false},{"name":"userSourceCollateral","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"}]},{"name":"depositObligationCollateralV2","accounts":[{"name":"depositAccounts","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"depositReserve","isMut":true,"isSigner":false},{"name":"reserveDestinationCollateral","isMut":true,"isSigner":false},{"name":"userSourceCollateral","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"farmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"}]},{"name":"withdrawObligationCollateral","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"withdrawReserve","isMut":true,"isSigner":false},{"name":"reserveSourceCollateral","isMut":true,"isSigner":false},{"name":"userDestinationCollateral","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"}]},{"name":"withdrawObligationCollateralV2","accounts":[{"name":"withdrawAccounts","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"withdrawReserve","isMut":true,"isSigner":false},{"name":"reserveSourceCollateral","isMut":true,"isSigner":false},{"name":"userDestinationCollateral","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"farmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"}]},{"name":"borrowObligationLiquidity","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"borrowReserve","isMut":true,"isSigner":false},{"name":"borrowReserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveSourceLiquidity","isMut":true,"isSigner":false},{"name":"borrowReserveLiquidityFeeReceiver","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"referrerTokenState","isMut":true,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"borrowObligationLiquidityV2","accounts":[{"name":"borrowAccounts","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"borrowReserve","isMut":true,"isSigner":false},{"name":"borrowReserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveSourceLiquidity","isMut":true,"isSigner":false},{"name":"borrowReserveLiquidityFeeReceiver","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"referrerTokenState","isMut":true,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"farmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"repayObligationLiquidity","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"repayReserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveDestinationLiquidity","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"repayObligationLiquidityV2","accounts":[{"name":"repayAccounts","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"repayReserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveDestinationLiquidity","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"farmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"repayAndWithdrawAndRedeem","accounts":[{"name":"repayAccounts","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"repayReserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveDestinationLiquidity","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"withdrawAccounts","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"withdrawReserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveSourceCollateral","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"placeholderUserDestinationCollateral","isMut":false,"isSigner":false,"isOptional":true},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"collateralFarmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"debtFarmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"repayAmount","type":"u64"},{"name":"withdrawCollateralAmount","type":"u64"}]},{"name":"depositAndWithdraw","accounts":[{"name":"depositAccounts","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveDestinationDepositCollateral","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"placeholderUserDestinationCollateral","isMut":false,"isSigner":false,"isOptional":true},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"withdrawAccounts","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"withdrawReserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveSourceCollateral","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"placeholderUserDestinationCollateral","isMut":false,"isSigner":false,"isOptional":true},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"depositFarmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"withdrawFarmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"},{"name":"withdrawCollateralAmount","type":"u64"}]},{"name":"depositReserveLiquidityAndObligationCollateral","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveDestinationDepositCollateral","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"placeholderUserDestinationCollateral","isMut":false,"isSigner":false,"isOptional":true},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"depositReserveLiquidityAndObligationCollateralV2","accounts":[{"name":"depositAccounts","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveDestinationDepositCollateral","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"placeholderUserDestinationCollateral","isMut":false,"isSigner":false,"isOptional":true},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"farmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"withdrawObligationCollateralAndRedeemReserveCollateral","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"withdrawReserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveSourceCollateral","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"placeholderUserDestinationCollateral","isMut":false,"isSigner":false,"isOptional":true},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"}]},{"name":"withdrawObligationCollateralAndRedeemReserveCollateralV2","accounts":[{"name":"withdrawAccounts","accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"withdrawReserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveSourceCollateral","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"placeholderUserDestinationCollateral","isMut":false,"isSigner":false,"isOptional":true},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"farmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"}]},{"name":"liquidateObligationAndRedeemReserveCollateral","accounts":[{"name":"liquidator","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"repayReserve","isMut":true,"isSigner":false},{"name":"repayReserveLiquidityMint","isMut":false,"isSigner":false},{"name":"repayReserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"withdrawReserve","isMut":true,"isSigner":false},{"name":"withdrawReserveLiquidityMint","isMut":false,"isSigner":false},{"name":"withdrawReserveCollateralMint","isMut":true,"isSigner":false},{"name":"withdrawReserveCollateralSupply","isMut":true,"isSigner":false},{"name":"withdrawReserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"withdrawReserveLiquidityFeeReceiver","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"userDestinationCollateral","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"repayLiquidityTokenProgram","isMut":false,"isSigner":false},{"name":"withdrawLiquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"},{"name":"minAcceptableReceivedLiquidityAmount","type":"u64"},{"name":"maxAllowedLtvOverridePercent","type":"u64"}]},{"name":"liquidateObligationAndRedeemReserveCollateralV2","accounts":[{"name":"liquidationAccounts","accounts":[{"name":"liquidator","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"repayReserve","isMut":true,"isSigner":false},{"name":"repayReserveLiquidityMint","isMut":false,"isSigner":false},{"name":"repayReserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"withdrawReserve","isMut":true,"isSigner":false},{"name":"withdrawReserveLiquidityMint","isMut":false,"isSigner":false},{"name":"withdrawReserveCollateralMint","isMut":true,"isSigner":false},{"name":"withdrawReserveCollateralSupply","isMut":true,"isSigner":false},{"name":"withdrawReserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"withdrawReserveLiquidityFeeReceiver","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"userDestinationCollateral","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"collateralTokenProgram","isMut":false,"isSigner":false},{"name":"repayLiquidityTokenProgram","isMut":false,"isSigner":false},{"name":"withdrawLiquidityTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"collateralFarmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"debtFarmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"},{"name":"minAcceptableReceivedLiquidityAmount","type":"u64"},{"name":"maxAllowedLtvOverridePercent","type":"u64"}]},{"name":"flashRepayReserveLiquidity","accounts":[{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveDestinationLiquidity","isMut":true,"isSigner":false},{"name":"userSourceLiquidity","isMut":true,"isSigner":false},{"name":"reserveLiquidityFeeReceiver","isMut":true,"isSigner":false},{"name":"referrerTokenState","isMut":true,"isSigner":false,"isOptional":true},{"name":"referrerAccount","isMut":true,"isSigner":false,"isOptional":true},{"name":"sysvarInfo","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"},{"name":"borrowInstructionIndex","type":"u8"}]},{"name":"flashBorrowReserveLiquidity","accounts":[{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveSourceLiquidity","isMut":true,"isSigner":false},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false},{"name":"reserveLiquidityFeeReceiver","isMut":true,"isSigner":false},{"name":"referrerTokenState","isMut":true,"isSigner":false,"isOptional":true},{"name":"referrerAccount","isMut":true,"isSigner":false,"isOptional":true},{"name":"sysvarInfo","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"liquidityAmount","type":"u64"}]},{"name":"requestElevationGroup","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false}],"args":[{"name":"elevationGroup","type":"u8"}]},{"name":"initReferrerTokenState","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":false,"isSigner":false},{"name":"referrer","isMut":false,"isSigner":false},{"name":"referrerTokenState","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"initUserMetadata","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"feePayer","isMut":true,"isSigner":true},{"name":"userMetadata","isMut":true,"isSigner":false},{"name":"referrerUserMetadata","isMut":false,"isSigner":false,"isOptional":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"userLookupTable","type":"publicKey"}]},{"name":"withdrawReferrerFees","accounts":[{"name":"referrer","isMut":true,"isSigner":true},{"name":"referrerTokenState","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveSupplyLiquidity","isMut":true,"isSigner":false},{"name":"referrerTokenAccount","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"initReferrerStateAndShortUrl","accounts":[{"name":"referrer","isMut":true,"isSigner":true},{"name":"referrerState","isMut":true,"isSigner":false},{"name":"referrerShortUrl","isMut":true,"isSigner":false},{"name":"referrerUserMetadata","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"shortUrl","type":"string"}]},{"name":"deleteReferrerStateAndShortUrl","accounts":[{"name":"referrer","isMut":true,"isSigner":true},{"name":"referrerState","isMut":true,"isSigner":false},{"name":"shortUrl","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"setObligationOrder","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false}],"args":[{"name":"index","type":"u8"},{"name":"order","type":{"defined":"ObligationOrder"}}]},{"name":"setBorrowOrder","accounts":[{"name":"owner","isMut":false,"isSigner":true,"docs":["The [Self::obligation]'s owner."]},{"name":"obligation","isMut":true,"isSigner":false,"docs":["The obligation to set the [BorrowOrder] on."]},{"name":"lendingMarket","isMut":false,"isSigner":false,"docs":["The [Self::obligation]'s market - needed to validate feature flags and minimum order value."]},{"name":"reserve","isMut":false,"isSigner":false,"docs":["The reserve matching the [Self::debt_liquidity_mint] - needed for the minimum order value","check. On cancellation, this account is ignored (but still must belong to the same market)."]},{"name":"filledDebtDestination","isMut":false,"isSigner":false,"docs":["The [BorrowOrder::filled_debt_destination] to set on order creation. Not editable on order","updates.","Ignored when cancelling the order."]},{"name":"debtLiquidityMint","isMut":false,"isSigner":false,"docs":["The [BorrowOrder::debt_liquidity_mint] to set on order creation. Not editable on order","updates.","Ignored when cancelling the order."]},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"orderConfig","type":{"defined":"BorrowOrderConfigArgs"}},{"name":"minExpectedCurrentRemainingDebtAmount","type":"u64"}]},{"name":"updateObligationConfig","accounts":[{"name":"owner","isMut":false,"isSigner":true,"docs":["Obligation's owner (signer - only the owner can modify the obligation's configuration)."]},{"name":"obligation","isMut":true,"isSigner":false,"docs":["The obligation to update."]},{"name":"borrowReserve","isMut":false,"isSigner":false,"isOptional":true,"docs":["The reserve of a selected one among the [Obligation::borrows].","","Required only if the config update affects a single borrow (for validating its new config)."]},{"name":"depositReserve","isMut":false,"isSigner":false,"isOptional":true,"docs":["The reserve of a selected one among the [Obligation::deposits].","","Required only if the config update affects a single deposit (for validating its new config)."]},{"name":"lendingMarket","isMut":false,"isSigner":false,"docs":["The lending market (for validating the obligation's new config)."]}],"args":[{"name":"mode","type":{"defined":"UpdateObligationConfigMode"}},{"name":"value","type":"bytes"}]},{"name":"rolloverFixedTermBorrow","accounts":[{"name":"rolloverAccounts","accounts":[{"name":"payer","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false,"docs":["The obligation with a borrow to be rolled over (or migrated)."]},{"name":"lendingMarket","isMut":false,"isSigner":false,"docs":["The [Self::obligation]'s market, needed for validations against the market-wide","configuration."]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false,"docs":["The [Self::lending_market]'s authority, needed to transfer the newly-borrowed funds out of","the [Self::reserve_source_liquidity]."]},{"name":"sourceBorrowReserve","isMut":true,"isSigner":false,"docs":["The reserve used by the existing [Self::obligation]'s borrow that needs to rollover from."]},{"name":"targetBorrowReserve","isMut":true,"isSigner":false,"docs":["The reserve to rollover the borrow into - possibly (but not necessarily) the same as","[Self::source_borrow_reserve]'s (but definitely of the same liquidity mint)."]},{"name":"liquidityMint","isMut":false,"isSigner":false,"docs":["The mint of [Self::source_borrow_reserve] and [Self::target_borrow_reserve], needed to","transfer funds between them."]},{"name":"sourceBorrowReserveLiquidity","isMut":true,"isSigner":false,"docs":["The vault of [Self::source_borrow_reserve], from which the funds are transferred."]},{"name":"targetBorrowReserveLiquidity","isMut":true,"isSigner":false,"docs":["The vault of [Self::target_borrow_reserve], from which the funds are transferred."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["The token program of [Self::liquidity_mint] - needed to execute the transfer."]}]},{"name":"sourceFarmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"targetFarmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"fillBorrowOrder","accounts":[{"name":"borrowAccounts","accounts":[{"name":"payer","isMut":false,"isSigner":true},{"name":"obligation","isMut":true,"isSigner":false,"docs":["The obligation with a [BorrowOrder]."]},{"name":"lendingMarket","isMut":false,"isSigner":false,"docs":["The [Self::obligation]'s market - needed for borrowing-related configuration."]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false,"docs":["The [Self::lending_market]'s authority, needed to transfer the newly-borrowed funds out of","the [Self::reserve_source_liquidity]."]},{"name":"borrowReserve","isMut":true,"isSigner":false,"docs":["The reserve to borrow from.","","Its mint must match the asset requested by the [BorrowOrder::debt_liquidity_mint]."]},{"name":"borrowReserveLiquidityMint","isMut":false,"isSigner":false,"docs":["The mint of [Self::borrow_reserve] - needed to execute the transfer."]},{"name":"reserveSourceLiquidity","isMut":true,"isSigner":false,"docs":["The vault of [Self::borrow_reserve], from which the funds are transferred."]},{"name":"borrowReserveLiquidityFeeReceiver","isMut":true,"isSigner":false,"docs":["The fee vault of [Self::borrow_reserve], to which the fees are transferred."]},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false,"docs":["The destination token account that should receive the newly borrowed funds.","","It must match [BorrowOrder::filled_debt_destination], owner and mint.","","**Warning:** An altered destination account will prevent an order from being filled."]},{"name":"referrerTokenState","isMut":true,"isSigner":false,"isOptional":true,"docs":["The referrer's account, for accumulating fees - needed if the [Obligation::has_referrer]."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["The token program of [Self::borrow_reserve] - needed to execute the transfer."]},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"farmsAccounts","accounts":[{"name":"obligationFarmUserState","isMut":true,"isSigner":false,"isOptional":true},{"name":"reserveFarmState","isMut":true,"isSigner":false,"isOptional":true}]},{"name":"farmsProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"initiateObligationOwnershipTransfer","accounts":[{"name":"owner","isMut":false,"isSigner":true,"docs":["Current owner must sign this transaction"]},{"name":"obligation","isMut":true,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"newOwner","type":"publicKey"}]},{"name":"approveObligationOwnershipTransfer","accounts":[{"name":"globalAdmin","isMut":false,"isSigner":true,"docs":["Global admin must sign this transaction"]},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"obligation","isMut":true,"isSigner":false},{"name":"pendingOwner","isMut":false,"isSigner":false,"docs":["The pending owner that will receive ownership after acceptance.","Must match the pending_owner stored in the obligation."]}],"args":[]},{"name":"acceptObligationOwnership","accounts":[{"name":"pendingOwner","isMut":false,"isSigner":true,"docs":["Pending owner must sign this transaction"]},{"name":"obligation","isMut":true,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[]},{"name":"abortObligationOwnershipTransfer","accounts":[{"name":"owner","isMut":false,"isSigner":true,"docs":["Current owner must sign this transaction"]},{"name":"obligation","isMut":true,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[]},{"name":"enqueueToWithdraw","accounts":[{"name":"owner","isMut":true,"isSigner":true,"docs":["The depositor holding ctokens."]},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false,"docs":["The market's authority, needed to initialize the [Self::owner_queued_collateral_vault]."]},{"name":"reserve","isMut":true,"isSigner":false},{"name":"userSourceCollateralTa","isMut":true,"isSigner":false,"docs":["The source of collateral to be enqueued."]},{"name":"userDestinationLiquidityTa","isMut":false,"isSigner":false,"docs":["The account to which the liquidity should be finally transferred later (to be recorded in","the ticket)."]},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false},{"name":"reserveCollateralMint","isMut":false,"isSigner":false},{"name":"collateralTokenProgram","isMut":false,"isSigner":false,"docs":["The collateral's program - needed for invoking the transfer to the vault *and* (implicitly)","for handling the `init_if_needed`."]},{"name":"withdrawTicket","isMut":true,"isSigner":false,"docs":["The new account to be initialized with the issued ticket's data."]},{"name":"ownerQueuedCollateralVault","isMut":true,"isSigner":false,"docs":["The per-owner \"this reserve's queued collateral\" vault (in which the collateral will be","locked)."]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["The System program - needed only for `init` / `init_if_needed` of the accounts above."]},{"name":"progressCallbackCustomAccount0","isMut":false,"isSigner":false,"isOptional":true,"docs":["The first one out of maximum 2 custom accounts that may be required if the optional \"ticket","progress callback\" is configured for this ticket.","","The expected custom accounts are validated based on the `progress_callback_type` instruction","argument. The currently supported ones are:","- for [ProgressCallbackType::None] no custom accounts are expected (this is the default","behavior, when the instruction data is zeroed or absent).","- for [ProgressCallbackType::KlendQueueAccountingHandlerOnKvault], only the `_0` custom","account is required:","- the `_0` custom account must represent the `VaultState` which disinvests from the","reserve (readonly; needed to be later passed as an input to the Kvault's handler);","- the `_1` custom account is ignored;","- the `owner` account (i.e. the signer) must be a Kvault-owned PDA base authority","associated with the vault indicated by `_0` (needed to prove that Kvault is CPI'ing this","handler)."]},{"name":"progressCallbackCustomAccount1","isMut":false,"isSigner":false,"isOptional":true,"docs":["The second possible account (see the `progress_callback_custom_account_0` above)."]},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"collateralAmount","type":"u64"},{"name":"progressCallbackType","type":{"defined":"ProgressCallbackType"}}]},{"name":"withdrawQueuedLiquidity","accounts":[{"name":"payer","isMut":true,"isSigner":true,"docs":["The executor of the permissionless tx (not necessarily the ticket owner)."]},{"name":"lendingMarket","isMut":false,"isSigner":false,"docs":["The lending market."]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false,"docs":["The market's authority, needed for burning the collateral (from","[Self::owner_queued_collateral_vault]) and transferring the liquidity (from","[Self::reserve_liquidity_supply])."]},{"name":"reserve","isMut":true,"isSigner":false,"docs":["The reserve."]},{"name":"reserveLiquidityMint","isMut":false,"isSigner":false,"docs":["The liquidity mint, needed to invoke the transfer."]},{"name":"reserveCollateralMint","isMut":true,"isSigner":false,"docs":["The collateral mint, needed to burn (`mut`!) the queued collateral."]},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false,"docs":["The liquidity supply vault (to withdraw the liquidity from)."]},{"name":"ownerQueuedCollateralVault","isMut":true,"isSigner":false,"docs":["The per-owner \"this reserve's queued collateral\" vault (from which the collateral will be","burnt)."]},{"name":"userDestinationLiquidity","isMut":true,"isSigner":false,"docs":["The token account to which the liquidity should be transferred (the one recorded in the","ticket)."]},{"name":"collateralTokenProgram","isMut":false,"isSigner":false,"docs":["The program of [Self::reserve_collateral_mint], needed for transfer."]},{"name":"liquidityTokenProgram","isMut":false,"isSigner":false,"docs":["The program of [Self::reserve_liquidity_mint], needed for transfer."]},{"name":"withdrawTicket","isMut":true,"isSigner":false,"docs":["The ticket's data itself.","","Note: in case of complete withdrawal, this account will be closed. In case of partial","withdrawal, its [WithdrawTicket::queued_collateral_amount] will simply be reduced, and the","ticket will maintain its position in the queue."]},{"name":"withdrawTicketOwner","isMut":true,"isSigner":false,"docs":["The owner of the [Self::withdraw_ticket]; needed only to return the rent of the","[WithdrawTicket] account (if it is getting fully-consumed and closed here)."]},{"name":"associatedTokenProgram","isMut":false,"isSigner":false,"docs":["The ATA program - needed for potential destination ATA creation."]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["The System program - needed for potential destination ATA creation."]},{"name":"progressCallbackProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["The progress callback program (if configured by the withdraw ticket)."]},{"name":"progressCallbackCustomAccount0","isMut":false,"isSigner":false,"isOptional":true,"docs":["The first one out of maximum 2 custom accounts that may be required if the withdraw ticket","defines a callback.","Please note that the constraints defined here do not mention `mut`, but a specific","[WithdrawTicket::progress_callback_type] may require it."]},{"name":"progressCallbackCustomAccount1","isMut":false,"isSigner":false,"isOptional":true,"docs":["The second possibly-required account (see the `progress_callback_custom_account_0` above)."]},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[],"returns":"bool"},{"name":"recoverInvalidTicketCollateral","accounts":[{"name":"payer","isMut":false,"isSigner":true,"docs":["The transaction executor.","","This instruction is, in principle, permissionless. However, only the ticket owner can use","arbitrary token account as destination for recovered collateral. Other signers can only","transfer the collateral to the ticket owner's ATA (see [Self::user_source_collateral])."]},{"name":"lendingMarket","isMut":false,"isSigner":false,"docs":["The lending market."]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false,"docs":["The market's authority, needed for transferring the collateral (from","[Self::owner_queued_collateral_vault])."]},{"name":"reserve","isMut":false,"isSigner":false,"docs":["The reserve, needed only to validate the other accounts."]},{"name":"reserveCollateralMint","isMut":false,"isSigner":false,"docs":["The collateral mint, needed to invoke the transfer."]},{"name":"ownerQueuedCollateralVault","isMut":true,"isSigner":false,"docs":["The per-owner \"this reserve's queued collateral\" vault (from which the collateral will be","recovered)."]},{"name":"userSourceCollateral","isMut":true,"isSigner":false,"docs":["The ticket's owner token account to which the ticket-locked collateral should be returned.","","Only the ticket's owner can indicate an arbitrary token account here. Permissionless","executors must indicate the ticket's owner ATA."]},{"name":"collateralTokenProgram","isMut":false,"isSigner":false,"docs":["The program of [Self::reserve_collateral_mint], needed for transfer."]},{"name":"withdrawTicket","isMut":true,"isSigner":false,"docs":["The ticket's account, necessarily marked as [WithdrawTicket::invalid] first (by the","`handler_withdraw_queued_liquidity`)."]},{"name":"withdrawTicketOwner","isMut":true,"isSigner":false,"docs":["The owner of the [Self::withdraw_ticket]; needed only to return the rent of the","[WithdrawTicket] account."]},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[{"name":"ticketSequenceNumber","type":"u64"}]},{"name":"cancelWithdrawTicket","accounts":[{"name":"owner","isMut":false,"isSigner":true,"docs":["The ticket owner (i.e. the only allowed to cancel their ticket)."]},{"name":"lendingMarket","isMut":false,"isSigner":false,"docs":["The lending market."]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false,"docs":["The market's authority, needed for transferring the collateral (from","[Self::owner_queued_collateral_vault])."]},{"name":"reserve","isMut":true,"isSigner":false,"docs":["The reserve."]},{"name":"reserveCollateralMint","isMut":false,"isSigner":false,"docs":["The collateral mint, needed to invoke the transfer."]},{"name":"ownerQueuedCollateralVault","isMut":true,"isSigner":false,"docs":["The per-owner \"this reserve's queued collateral\" vault (from which the collateral will be","transferred back to the owner)."]},{"name":"userDestinationCollateral","isMut":true,"isSigner":false,"docs":["Destination for returned ctokens (owner's token account)."]},{"name":"collateralTokenProgram","isMut":false,"isSigner":false,"docs":["The program of [Self::reserve_collateral_mint], needed for transfer."]},{"name":"withdrawTicket","isMut":true,"isSigner":false,"docs":["Ticket to cancel - remains as tombstone until queue processes it."]}],"args":[{"name":"ticketSequenceNumber","type":"u64"},{"name":"collateralAmountToCancel","type":"u64"}]},{"name":"initGlobalConfig","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false},{"name":"programData","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"updateGlobalConfig","accounts":[{"name":"globalAdmin","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false}],"args":[{"name":"mode","type":{"defined":"UpdateGlobalConfigMode"}},{"name":"value","type":"bytes"}]},{"name":"updateGlobalConfigAdmin","accounts":[{"name":"pendingAdmin","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false}],"args":[]},{"name":"idlMissingTypes","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false}],"args":[{"name":"reserveFarmKind","type":{"defined":"ReserveFarmKind"}},{"name":"feeCalculation","type":{"defined":"FeeCalculation"}},{"name":"reserveStatus","type":{"defined":"ReserveStatus"}},{"name":"updateConfigMode","type":{"defined":"UpdateConfigMode"}},{"name":"updateLendingMarketConfigValue","type":{"defined":"UpdateLendingMarketConfigValue"}},{"name":"updateLendingMarketConfigMode","type":{"defined":"UpdateLendingMarketMode"}}]}],"accounts":[{"name":"UserState","type":{"kind":"struct","fields":[{"name":"userId","type":"u64"},{"name":"farmState","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"isFarmDelegated","type":"u8"},{"name":"padding0","type":{"array":["u8",7]}},{"name":"rewardsTallyScaled","type":{"array":["u128",10]}},{"name":"rewardsIssuedUnclaimed","type":{"array":["u64",10]}},{"name":"lastClaimTs","type":{"array":["u64",10]}},{"name":"activeStakeScaled","type":"u128"},{"name":"pendingDepositStakeScaled","type":"u128"},{"name":"pendingDepositStakeTs","type":"u64"},{"name":"pendingWithdrawalUnstakeScaled","type":"u128"},{"name":"pendingWithdrawalUnstakeTs","type":"u64"},{"name":"bump","type":"u64"},{"name":"delegatee","type":"publicKey"},{"name":"lastStakeTs","type":"u64"},{"name":"padding1","type":{"array":["u64",50]}}]}},{"name":"GlobalConfig","type":{"kind":"struct","fields":[{"name":"globalAdmin","docs":["Global admin of the program"],"type":"publicKey"},{"name":"pendingAdmin","docs":["Pending admin must sign a specific transaction to become the global admin"],"type":"publicKey"},{"name":"feeCollector","docs":["Fee collector is the only allowed owner of token accounts receiving protocol fees"],"type":"publicKey"},{"name":"padding","docs":["Padding to make the struct size 1024 bytes"],"type":{"array":["u8",928]}}]}},{"name":"LendingMarket","type":{"kind":"struct","fields":[{"name":"version","docs":["Version of lending market"],"type":"u64"},{"name":"bumpSeed","docs":["Bump seed for derived authority address"],"type":"u64"},{"name":"lendingMarketOwner","docs":["Owner authority which can add new reserves"],"type":"publicKey"},{"name":"lendingMarketOwnerCached","docs":["Temporary cache of the lending market owner, used in update_lending_market_owner"],"type":"publicKey"},{"name":"quoteCurrency","docs":["Currency market prices are quoted in","e.g. \"USD\" null padded (`*b\"USD\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\"`) or a SPL token mint pubkey"],"type":{"array":["u8",32]}},{"name":"referralFeeBps","docs":["Referral fee for the lending market, as bps out of the total protocol fee"],"type":"u16"},{"name":"emergencyMode","type":"u8"},{"name":"autodeleverageEnabled","docs":["Whether the obligations on this market should be subject to auto-deleveraging after deposit","or borrow limit is crossed.","Besides this flag, the particular reserve's flag also needs to be enabled (logical `AND`).","**NOTE:** this also affects the individual \"target LTV\" deleveraging."],"type":"u8"},{"name":"borrowDisabled","type":"u8"},{"name":"priceRefreshTriggerToMaxAgePct","docs":["Refresh price from oracle only if it's older than this percentage of the price max age.","e.g. if the max age is set to 100s and this is set to 80%, the price will be refreshed if it's older than 80s.","Price is always refreshed if this set to 0."],"type":"u8"},{"name":"liquidationMaxDebtCloseFactorPct","docs":["Percentage of the total borrowed value in an obligation available for liquidation"],"type":"u8"},{"name":"insolvencyRiskUnhealthyLtvPct","docs":["Minimum acceptable unhealthy LTV before max_debt_close_factor_pct becomes 100%"],"type":"u8"},{"name":"minFullLiquidationValueThreshold","docs":["Minimum liquidation value threshold triggering full liquidation for an obligation, in full","units of the quote currency (e.g. `2` means \"$2\", not \"2 lamports of USDC\")."],"type":"u64"},{"name":"maxLiquidatableDebtMarketValueAtOnce","docs":["Max allowed liquidation value in one ix call"],"type":"u64"},{"name":"reserved0","docs":["[DEPRECATED] Global maximum unhealthy borrow value allowed for any obligation"],"type":{"array":["u8",8]}},{"name":"globalAllowedBorrowValue","docs":["Global maximum allowed borrow value allowed for any obligation"],"type":"u64"},{"name":"emergencyCouncil","docs":["The address of the emergency council, in charge of taking emergency actions on the market (e.g., enabling emergency mode)"],"type":"publicKey"},{"name":"reserved1","docs":["[DEPRECATED] Reward points multiplier per obligation type"],"type":{"array":["u8",8]}},{"name":"elevationGroups","docs":["Elevation groups are used to group together reserves that have the same risk parameters and can bump the ltv and liquidation threshold"],"type":{"array":[{"defined":"ElevationGroup"},32]}},{"name":"elevationGroupPadding","type":{"array":["u64",90]}},{"name":"minNetValueInObligationSf","docs":["Min net value accepted to be found in a position after any lending action in an obligation (scaled by quote currency decimals)"],"type":"u128"},{"name":"minValueSkipLiquidationLtvChecks","docs":["Minimum value to enforce smallest ltv priority checks on the collateral reserves on liquidation"],"type":"u64"},{"name":"name","docs":["Market name, zero-padded."],"type":{"array":["u8",32]}},{"name":"minValueSkipLiquidationBfChecks","docs":["Minimum value to enforce highest borrow factor priority checks on the debt reserves on liquidation"],"type":"u64"},{"name":"individualAutodeleverageMarginCallPeriodSecs","docs":["Time (in seconds) that must pass before liquidation is allowed on an obligation that has","been individually marked for auto-deleveraging."],"type":"u64"},{"name":"minInitialDepositAmount","docs":["Minimum amount of deposit at creation of a reserve to prevent artificial inflation","Note: this amount cannot be recovered, the ctoken associated are never minted"],"type":"u64"},{"name":"obligationOrderExecutionEnabled","docs":["Whether the obligation orders should be evaluated during liquidations."],"type":"u8"},{"name":"immutable","docs":["Whether the lending market is set as immutable."],"type":"u8"},{"name":"obligationOrderCreationEnabled","docs":["Whether new obligation orders can be created.","Note: updating or cancelling existing orders is *not* affected by this flag."],"type":"u8"},{"name":"priceTriggeredLiquidationDisabled","docs":["Whether the liquidation operations that are triggered by price changes should be disabled.","This includes regular liquidation (i.e. LTV exceeding the unhealthy threshold) and some","obligation orders' execution.","","*Caution:* this flag is *disabling* the liquidations when `1` - contrary to all the other","liquidation-driving flags (see e.g. [Self::autodeleverage_enabled])."],"type":"u8"},{"name":"matureReserveDebtLiquidationEnabled","docs":["Whether the debts that reached their reserve's [ReserveConfig::debt_maturity_timestamp] can","be liquidated."],"type":"u8"},{"name":"obligationBorrowDebtTermLiquidationEnabled","docs":["Whether the [Obligation::borrows] that reached their [ReserveConfig::debt_term_seconds] can","be liquidated."],"type":"u8"},{"name":"borrowOrderCreationEnabled","docs":["Whether new borrow orders can be created.","Note: updating or cancelling existing orders is *not* affected by this flag."],"type":"u8"},{"name":"borrowOrderExecutionEnabled","docs":["Whether the existing borrow orders can be filled."],"type":"u8"},{"name":"proposerAuthority","docs":["Authority that can propose creating of new reserves but cannot enable them."],"type":"publicKey"},{"name":"minBorrowOrderFillValue","docs":["Minimum value that can be filled in a single `fill_borrow_order()` call, in full units of","the quote currency (e.g. `2` means \"$2\", not \"2 lamports of USDC\")."],"type":"u64"},{"name":"withdrawTicketIssuanceEnabled","docs":["Whether any new withdraw tickets can be issued (i.e. whether new requests can enter the","withdraw queue)."],"type":"u8"},{"name":"withdrawTicketRedemptionEnabled","docs":["Whether the existing withdraw tickets can be redeemed (i.e. whether the tickets can be used","to transfer accumulated pending liquidity to destination accounts)."],"type":"u8"},{"name":"obligationBorrowRolloverConfigurationEnabled","docs":["Whether the owners can enable the borrow rollover/migration on their obligations.","","*Note 1:* the actual execution of (different kinds of) rollovers are enabled/disabled by:","- [Self::fixed_term_rollover_window_duration_seconds],","- [Self::open_term_rollover_window_duration_seconds],","- [Self::obligation_borrow_migration_to_fixed_execution_enabled].","","*Note 2:* when this configuration is disabled, the obligation owners can still disable their","rollover (i.e. set the obligation's flags to zeroes)."],"type":"u8"},{"name":"obligationBorrowMigrationToFixedExecutionEnabled","docs":["Whether the actual execution of a \"migration to fixed\" rollover flavor is allowed.","","See [FixedTermBorrowRolloverConfig::migration_to_fixed_enabled]."],"type":"u8"},{"name":"withdrawTicketCancellationEnabled","docs":["Whether the ticket owners can cancel their withdraw tickets (i.e. recover ctokens from the","queued collateral vault back to their wallet)."],"type":"u8"},{"name":"padding2","type":{"array":["u8",1]}},{"name":"reserveRewardsMaxAprBps","docs":["Maximum APR (in basis points; `FULL_BPS = 10_000` = 100%) at which reserves on this market","may distribute their `rewards_amount_per_slot`. `0` disables rewards on this market","entirely (`topup_reserve_rewards` is rejected). Bounded by `FULL_BPS` (100% APR) when set.","See [ReserveConfig::rewards_amount_per_slot] for the depositor-cap interaction."],"type":"u16"},{"name":"minWithdrawQueuedLiquidityValue","docs":["Minimum value that can be withdrawn in a single `withdraw_queued_liquidity()` call, in full","units of the quote currency (e.g. `2` means \"$2\", not \"2 lamports of USDC\")."],"type":"u64"},{"name":"fixedTermRolloverWindowDurationSeconds","docs":["A configurable time window (right before the end of a fixed debt term) during which an","auto-rollover into another *fixed* rate/term can happen.","","When zeroed, this rollover mode is effectively disabled.","Can only be enabled when [Self::min_partial_rollover_value] is configured.","","See [FixedTermBorrowRolloverConfig]."],"type":"u64"},{"name":"openTermRolloverWindowDurationSeconds","docs":["A configurable time window (right before the end of a fixed debt term) during which an","auto-rollover into a *variable* (indefinite) rate/term can happen.","","When zeroed, this rollover mode is effectively disabled.","Can only be enabled when [Self::min_partial_rollover_value] is configured.","","This will typically be shorter than [Self::fixed_term_rollover_window_duration_seconds],","acting as a fallback if a fixed reserve liquidity remains unavailable for considerable time."],"type":"u64"},{"name":"minPartialRolloverValue","docs":["Minimum dollar value for a partial rollover into a different reserve.","When the achievable rollover amount is below this threshold (and it's not a full rollover),","the rollover is rejected.","","In full units of the quote currency (e.g. `2` means \"$2\")."],"type":"u64"},{"name":"termBasedFullLiquidationDurationSecs","docs":["The time that must pass before an entire expired debt becomes liquidatable.","","For example:","Let's assume this duration is configured as 100 seconds; then:","- right after fixed-term debt expiration, effectively no debt can be liquidated.","- 30 seconds after expiration, we allow to 30% of the expired debt to be liquidated","- to be specific: at this point in time, we \"protect\" from liquidation 70% of the","[ObligationLiquidity::borrowed_amount_at_expiration] (regardless of how much interest","was accrued or how much debt was repaid while expired).","- 100 seconds after expiration we allow the entire debt to be liquidated.","","Only effective when [Self::obligation_borrow_debt_term_liquidation_enabled].","","Motivation note: this throttling feature gives an opportunity to execute a configured","auto-rollover (after a partial liquidation brings the debt size down so that there is enough","available liquidity in some compatible reserve).","","When zeroed, an entire expired debt can be liquidated right after expiration (i.e. no","throttling)."],"type":"u64"},{"name":"permissioningAuthority","docs":["If not NULL, operations encoded in permissioned_ops require a signature from this authority"],"type":"publicKey"},{"name":"permissionedOps","docs":["Bitmap of operations that require permissioning authority signature"],"type":"u64"},{"name":"padding1","type":{"array":["u64",153]}}]}},{"name":"Obligation","docs":["Lending market obligation state"],"type":{"kind":"struct","fields":[{"name":"tag","docs":["Version of the struct"],"type":"u64"},{"name":"lastUpdate","docs":["Last update to collateral, liquidity, or their market values"],"type":{"defined":"LastUpdate"}},{"name":"lendingMarket","docs":["Lending market address"],"type":"publicKey"},{"name":"owner","docs":["Owner authority which can borrow liquidity"],"type":"publicKey"},{"name":"deposits","docs":["Deposited collateral for the obligation, unique by deposit reserve address"],"type":{"array":[{"defined":"ObligationCollateral"},8]}},{"name":"lowestReserveDepositLiquidationLtv","docs":["Worst LTV for the collaterals backing the loan, represented as a percentage"],"type":"u64"},{"name":"depositedValueSf","docs":["Market value of deposits (scaled fraction)"],"type":"u128"},{"name":"borrows","docs":["Borrowed liquidity for the obligation, unique by borrow reserve address"],"type":{"array":[{"defined":"ObligationLiquidity"},5]}},{"name":"borrowFactorAdjustedDebtValueSf","docs":["Risk adjusted market value of borrows/debt (sum of price * borrowed_amount * borrow_factor) (scaled fraction)"],"type":"u128"},{"name":"borrowedAssetsMarketValueSf","docs":["Market value of borrows - used for max_liquidatable_borrowed_amount (scaled fraction)"],"type":"u128"},{"name":"allowedBorrowValueSf","docs":["The maximum borrow value at the weighted average loan to value ratio (scaled fraction)"],"type":"u128"},{"name":"unhealthyBorrowValueSf","docs":["The dangerous borrow value at the weighted average liquidation threshold (scaled fraction)"],"type":"u128"},{"name":"paddingDeprecatedAssetTiers","docs":["The asset tier of the deposits"],"type":{"array":["u8",13]}},{"name":"elevationGroup","docs":["The elevation group id the obligation opted into."],"type":"u8"},{"name":"numOfObsoleteDepositReserves","docs":["The number of obsolete reserves the obligation has a deposit in"],"type":"u8"},{"name":"hasDebt","docs":["Marked = 1 if borrows array is not empty, 0 = borrows empty"],"type":"u8"},{"name":"referrer","docs":["Wallet address of the referrer"],"type":"publicKey"},{"name":"borrowingDisabled","docs":["Marked = 1 if borrowing disabled, 0 = borrowing enabled"],"type":"u8"},{"name":"autodeleverageTargetLtvPct","docs":["A target LTV set by the market owner when marking this obligation for deleveraging.","Only effective when `deleveraging_margin_call_started_slot != 0`."],"type":"u8"},{"name":"lowestReserveDepositMaxLtvPct","docs":["The lowest max LTV found amongst the collateral deposits"],"type":"u8"},{"name":"numOfObsoleteBorrowReserves","docs":["The number of obsolete reserves the obligation has a borrow in"],"type":"u8"},{"name":"ownershipTransferState","docs":["State of ownership transfer, see [OwnershipTransferState]"],"type":"u8"},{"name":"reserved","type":{"array":["u8",3]}},{"name":"highestBorrowFactorPct","type":"u64"},{"name":"autodeleverageMarginCallStartedTimestamp","docs":["A timestamp at which the market owner most-recently marked this obligation for deleveraging.","Zero if not currently subject to deleveraging."],"type":"u64"},{"name":"obligationOrders","docs":["Owner-defined, permissionlessly-executed repay orders.","Typical use-cases would be a stop-loss and a take-profit (possibly co-existing)."],"type":{"array":[{"defined":"ObligationOrder"},2]}},{"name":"borrowOrder","docs":["Owner-defined, permissionlessly-executed borrow order applicable to this obligation.","Non-zeroed only on a newly-initialized fixed-rate, fixed-term obligation."],"type":{"defined":"BorrowOrder"}},{"name":"pendingOwner","docs":["Pending owner during ownership transfer process.","Pubkey::default() means no pending owner (similar to Option::None)"],"type":"publicKey"},{"name":"padding3","type":{"array":["u64",69]}}]}},{"name":"ReferrerState","type":{"kind":"struct","fields":[{"name":"shortUrl","type":"publicKey"},{"name":"owner","type":"publicKey"}]}},{"name":"ReferrerTokenState","docs":["Referrer account -> each owner can have multiple accounts for specific reserves"],"type":{"kind":"struct","fields":[{"name":"referrer","docs":["Pubkey of the referrer/owner"],"type":"publicKey"},{"name":"mint","docs":["Token mint for the account"],"type":"publicKey"},{"name":"amountUnclaimedSf","docs":["Amount that has been accumulated and not claimed yet -> available to claim (scaled fraction)"],"type":"u128"},{"name":"amountCumulativeSf","docs":["Amount that has been accumulated in total -> both already claimed and unclaimed (scaled fraction)"],"type":"u128"},{"name":"bump","docs":["Referrer token state bump, used for address validation"],"type":"u64"},{"name":"padding","type":{"array":["u64",31]}}]}},{"name":"ShortUrl","type":{"kind":"struct","fields":[{"name":"referrer","type":"publicKey"},{"name":"shortUrl","type":"string"}]}},{"name":"UserMetadata","docs":["Referrer account -> each owner can have multiple accounts for specific reserves"],"type":{"kind":"struct","fields":[{"name":"referrer","docs":["Pubkey of the referrer/owner - pubkey::default if no referrer"],"type":"publicKey"},{"name":"bump","docs":["Bump used for validation of account address"],"type":"u64"},{"name":"userLookupTable","docs":["User lookup table - used to store all user accounts - atas for each reserve mint, each obligation PDA, UserMetadata itself and all referrer_token_states if there is a referrer"],"type":"publicKey"},{"name":"owner","docs":["User metadata account owner"],"type":"publicKey"},{"name":"padding1","type":{"array":["u64",51]}},{"name":"padding2","type":{"array":["u64",64]}}]}},{"name":"Reserve","type":{"kind":"struct","fields":[{"name":"version","docs":["Version of the reserve"],"type":"u64"},{"name":"lastUpdate","docs":["Last slot when supply and rates updated"],"type":{"defined":"LastUpdate"}},{"name":"lendingMarket","docs":["Lending market address"],"type":"publicKey"},{"name":"farmCollateral","type":"publicKey"},{"name":"farmDebt","type":"publicKey"},{"name":"liquidity","docs":["Reserve liquidity"],"type":{"defined":"ReserveLiquidity"}},{"name":"reserveLiquidityPadding","type":{"array":["u64",150]}},{"name":"collateral","docs":["Reserve collateral"],"type":{"defined":"ReserveCollateral"}},{"name":"reserveCollateralPadding","type":{"array":["u64",150]}},{"name":"config","docs":["Reserve configuration values"],"type":{"defined":"ReserveConfig"}},{"name":"configPadding","type":{"array":["u64",112]}},{"name":"borrowedAmountOutsideElevationGroup","type":"u64"},{"name":"borrowedAmountsAgainstThisReserveInElevationGroups","docs":["Amount of token borrowed in lamport of debt asset in the given","elevation group when this reserve is part of the collaterals."],"type":{"array":["u64",32]}},{"name":"withdrawQueue","docs":["The tracker of ticket-based withdrawals."],"type":{"defined":"WithdrawQueue"}},{"name":"padding","type":{"array":["u64",204]}}]}},{"name":"WithdrawTicket","docs":["A finite-lifecycle account representing a specific depositor's place in the withdraw queue of","a specific reserve.","","The lifecycle:","1. The depositor holding ctokens wants to withdraw funds from the reserve, and finds out that","the required amount is not available (due to high utilization).","2. The depositor calls the `enqueue_to_withdraw` handler.","3. The handler transfers the depositor's ctokens to the reserve's internal \"pending\" vault.","4. The handler initializes a new [WithdrawTicket] account, with the next available sequence","number.","5. The depositor waits until his ticket is the next expected one for actual withdraw, and until","the reserve has enough liquidity.","6. Anyone (the depositor or a bot) calls the permissionless `withdraw_queued_liquidity`","handler. If the ticket became invalid (e.g. destination account no longer exists), then the","depositor can call the `recover_invalid_ticket_collateral` handler instead.","7. The handler transfers the liquidity amount according to the current exchange rate.","8. The handler closes the ticket account."],"type":{"kind":"struct","fields":[{"name":"sequenceNumber","docs":["This ticket's place in the queue; the same as used for PDA derivation."],"type":"u64"},{"name":"owner","docs":["The funds' owner (the user who called the `enqueue_to_withdraw` handler)."],"type":"publicKey"},{"name":"reserve","docs":["The reserve to withdraw from."],"type":"publicKey"},{"name":"userDestinationLiquidityTa","docs":["The token account to which the finally-available liquidity should be transferred (by the","`withdraw_queued_liquidity` handler)."],"type":"publicKey"},{"name":"queuedCollateralAmount","docs":["The amount of collateral still waiting to be withdrawn using this ticket."],"type":"u64"},{"name":"createdAtTimestamp","docs":["The timestamp at which the queue was entered.","","This is currently only a piece of metadata, not used by the logic."],"type":"u64"},{"name":"invalid","docs":["Whether the ticket has been found to be invalid (e.g. the [Self::user_destination_liquidity]","has been repurposed) by the `withdraw_queued_liquidity` handler.","To be specific: valid = `0`, invalid = `1`.","","An invalid ticket cannot be made valid again, and can only be passed to the","`recover_invalid_ticket_collateral` handler."],"type":"u8"},{"name":"progressCallbackType","docs":["One of the valid [ProgressCallbackType] representations."],"type":"u8"},{"name":"alignmentPadding","docs":["Inner padding, for alignment."],"type":{"array":["u8",6]}},{"name":"progressCallbackCustomAccounts","docs":["The (optional) accounts to be used by [Self::progress_callback_type]s."],"type":{"array":["publicKey",2]}},{"name":"endPadding","docs":["Trailing padding, for future developments."],"type":{"array":["u64",40]}}]}}],"types":[{"name":"ReserveConfigCustomizationArgs","docs":["A definition of optional customizations that should be applied after cloning the config."],"type":{"kind":"struct","fields":[{"name":"overrideFixedRateBps","docs":["A gate for [Self::fixed_borrow_rate_bps]."],"type":"u8"},{"name":"fixedBorrowRateBps","docs":["If [Self::override_fixed_rate_bps] is non-zero, this borrow rate will be used to override","the [ReserveConfig::borrow_rate_curve] with a fixed one."],"type":"u32"},{"name":"overrideDebtTermSeconds","docs":["A gate for [Self::debt_term_seconds]."],"type":"u8"},{"name":"debtTermSeconds","docs":["If [Self::override_debt_term_seconds] is non-zero, this value will be used to override the","[ReserveConfig::debt_term_seconds]."],"type":"u64"},{"name":"clearElevationGroups","docs":["Whether the target reserve should have zeroed [ReserveConfig::elevation_groups] (i.e. not","cloned from source).","","This customization is mandatory when cloning a reserve (with some elevation groups) into a","different market (where those elevation group indices would have different meaning)."],"type":"u8"}]}},{"name":"BorrowOrderConfigArgs","docs":["A subset of [BorrowOrderConfig] excluding the accounts passed via [SetBorrowOrder]."],"type":{"kind":"struct","fields":[{"name":"remainingDebtAmount","type":"u64"},{"name":"maxBorrowRateBps","type":"u32"},{"name":"minDebtTermSeconds","type":"u64"},{"name":"fillableUntilTimestamp","type":"u64"},{"name":"enableAutoRolloverOnFilledBorrows","type":"bool"}]}},{"name":"UpdateConfigMode","type":{"kind":"enum","variants":[{"name":"UpdateLoanToValuePct"},{"name":"UpdateMaxLiquidationBonusBps"},{"name":"UpdateLiquidationThresholdPct"},{"name":"UpdateProtocolLiquidationFee"},{"name":"UpdateProtocolTakeRate"},{"name":"UpdateFeesOriginationFee"},{"name":"UpdateFeesFlashLoanFee"},{"name":"DeprecatedUpdateFeesReferralFeeBps"},{"name":"UpdateDepositLimit"},{"name":"UpdateBorrowLimit"},{"name":"UpdateTokenInfoLowerHeuristic"},{"name":"UpdateTokenInfoUpperHeuristic"},{"name":"UpdateTokenInfoExpHeuristic"},{"name":"UpdateTokenInfoTwapDivergence"},{"name":"UpdateTokenInfoScopeTwap"},{"name":"UpdateTokenInfoScopeChain"},{"name":"UpdateTokenInfoName"},{"name":"UpdateTokenInfoPriceMaxAge"},{"name":"UpdateTokenInfoTwapMaxAge"},{"name":"UpdateScopePriceFeed"},{"name":"UpdatePythPrice"},{"name":"UpdateSwitchboardFeed"},{"name":"UpdateSwitchboardTwapFeed"},{"name":"UpdateBorrowRateCurve"},{"name":"DeprecatedUpdateEntireReserveConfig"},{"name":"UpdateDebtWithdrawalCap"},{"name":"UpdateDepositWithdrawalCap"},{"name":"DeprecatedUpdateDebtWithdrawalCapCurrentTotal"},{"name":"DeprecatedUpdateDepositWithdrawalCapCurrentTotal"},{"name":"UpdateBadDebtLiquidationBonusBps"},{"name":"UpdateMinLiquidationBonusBps"},{"name":"UpdateDeleveragingMarginCallPeriod"},{"name":"UpdateBorrowFactor"},{"name":"DeprecatedUpdateAssetTier"},{"name":"UpdateElevationGroup"},{"name":"UpdateDeleveragingThresholdDecreaseBpsPerDay"},{"name":"DeprecatedUpdateMultiplierSideBoost"},{"name":"DeprecatedUpdateMultiplierTagBoost"},{"name":"UpdateReserveStatus"},{"name":"UpdateFarmCollateral"},{"name":"UpdateFarmDebt"},{"name":"UpdateDisableUsageAsCollateralOutsideEmode"},{"name":"UpdateBlockBorrowingAboveUtilizationPct"},{"name":"UpdateBlockPriceUsage"},{"name":"UpdateBorrowLimitOutsideElevationGroup"},{"name":"UpdateBorrowLimitsInElevationGroupAgainstThisReserve"},{"name":"UpdateHostFixedInterestRateBps"},{"name":"UpdateAutodeleverageEnabled"},{"name":"UpdateDeleveragingBonusIncreaseBpsPerDay"},{"name":"UpdateProtocolOrderExecutionFee"},{"name":"UpdateProposerAuthorityLock"},{"name":"UpdateMinDeleveragingBonusBps"},{"name":"UpdateBlockCTokenUsage"},{"name":"UpdateDebtMaturityTimestamp"},{"name":"UpdateDebtTermSeconds"},{"name":"UpdateEarlyRepayRemainingInterestPct"},{"name":"UpdateReserveEmergencyMode"},{"name":"UpdateRewardsAmountPerSlot"},{"name":"UpdateReservePermissionedOps"}]}},{"name":"UpdateLendingMarketConfigValue","type":{"kind":"enum","variants":[{"name":"Bool","fields":["bool"]},{"name":"U8","fields":["u8"]},{"name":"U8Array","fields":[{"array":["u8",8]}]},{"name":"U16","fields":["u16"]},{"name":"U64","fields":["u64"]},{"name":"U128","fields":["u128"]},{"name":"Pubkey","fields":["publicKey"]},{"name":"ElevationGroup","fields":[{"defined":"ElevationGroup"}]},{"name":"Name","fields":[{"array":["u8",32]}]}]}},{"name":"UpdateLendingMarketMode","type":{"kind":"enum","variants":[{"name":"UpdateOwner"},{"name":"UpdateEmergencyMode"},{"name":"UpdateLiquidationCloseFactor"},{"name":"UpdateLiquidationMaxValue"},{"name":"DeprecatedUpdateGlobalUnhealthyBorrow"},{"name":"UpdateGlobalAllowedBorrow"},{"name":"UpdateEmergencyCouncil"},{"name":"UpdateMinFullLiquidationThreshold"},{"name":"UpdateInsolvencyRiskLtv"},{"name":"UpdateElevationGroup"},{"name":"UpdateReferralFeeBps"},{"name":"DeprecatedUpdateMultiplierPoints"},{"name":"UpdatePriceRefreshTriggerToMaxAgePct"},{"name":"UpdateAutodeleverageEnabled"},{"name":"UpdateBorrowingDisabled"},{"name":"UpdateMinNetValueObligationPostAction"},{"name":"UpdateMinValueLtvSkipPriorityLiqCheck"},{"name":"UpdateMinValueBfSkipPriorityLiqCheck"},{"name":"UpdatePaddingFields"},{"name":"UpdateName"},{"name":"UpdateIndividualAutodeleverageMarginCallPeriodSecs"},{"name":"UpdateInitialDepositAmount"},{"name":"UpdateObligationOrderExecutionEnabled"},{"name":"UpdateImmutableFlag"},{"name":"UpdateObligationOrderCreationEnabled"},{"name":"UpdateProposerAuthority"},{"name":"UpdatePriceTriggeredLiquidationDisabled"},{"name":"UpdateMatureReserveDebtLiquidationEnabled"},{"name":"UpdateObligationBorrowDebtTermLiquidationEnabled"},{"name":"UpdateBorrowOrderCreationEnabled"},{"name":"UpdateBorrowOrderExecutionEnabled"},{"name":"UpdateMinBorrowOrderFillValue"},{"name":"UpdateWithdrawTicketIssuanceEnabled"},{"name":"UpdateWithdrawTicketRedemptionEnabled"},{"name":"UpdateMinWithdrawQueuedLiquidityValue"},{"name":"UpdateFixedTermRolloverWindowDurationSeconds"},{"name":"UpdateOpenTermRolloverWindowDurationSeconds"},{"name":"UpdateObligationBorrowRolloverConfigurationEnabled"},{"name":"UpdateTermBasedFullLiquidationDurationSecs"},{"name":"UpdateObligationBorrowMigrationToFixedExecutionEnabled"},{"name":"UpdateMinPartialRolloverValue"},{"name":"UpdateWithdrawTicketCancellationEnabled"},{"name":"UpdatePermissioningAuthority"},{"name":"UpdatePermissionedOps"},{"name":"DeprecatedUpdateReserveRewardsMaxAprPct"},{"name":"UpdateReserveRewardsMaxAprBps"}]}},{"name":"UpdateGlobalConfigMode","type":{"kind":"enum","variants":[{"name":"PendingAdmin"},{"name":"FeeCollector"}]}},{"name":"LastUpdate","docs":["Last update state"],"type":{"kind":"struct","fields":[{"name":"slot","docs":["Last slot when updated"],"type":"u64"},{"name":"stale","docs":["True when marked stale, false when slot updated"],"type":"u8"},{"name":"priceStatus","docs":["Status of the prices used to calculate the last update"],"type":"u8"},{"name":"placeholder","type":{"array":["u8",6]}}]}},{"name":"ElevationGroup","type":{"kind":"struct","fields":[{"name":"maxLiquidationBonusBps","type":"u16"},{"name":"id","type":"u8"},{"name":"ltvPct","type":"u8"},{"name":"liquidationThresholdPct","type":"u8"},{"name":"allowNewLoans","type":"u8"},{"name":"maxReservesAsCollateral","type":"u8"},{"name":"padding0","type":"u8"},{"name":"debtReserve","docs":["Mandatory debt reserve for this elevation group"],"type":"publicKey"},{"name":"padding1","type":{"array":["u64",4]}}]}},{"name":"BorrowOrder","docs":["A borrow order.","","When the [Obligation::borrow_order] is populated (i.e. non-zeroed) on an Obligation, then the","permissionless \"fill\" operations may borrow liquidity to the owner according to this","specification."],"type":{"kind":"struct","fields":[{"name":"debtLiquidityMint","docs":["The asset to be borrowed.","The reserves used for [Obligation::borrows] *must* all provide exactly this asset."],"type":"publicKey"},{"name":"remainingDebtAmount","docs":["The amount of debt that still needs to be filled, in lamports."],"type":"u64"},{"name":"filledDebtDestination","docs":["The token account owned by the [Obligation::owner] and holding [Self::debt_liquidity_mint],","where the filled funds should be transferred to."],"type":"publicKey"},{"name":"minDebtTermSeconds","docs":["The minimum allowed debt term that the obligation owner agrees to.","The reserves used to fill this order *cannot* define their debt term *lower* than this.","","If zeroed, then only open-term reserves may be used."],"type":"u64"},{"name":"fillableUntilTimestamp","docs":["The time until which the borrow order can still be filled."],"type":"u64"},{"name":"placedAtTimestamp","docs":["The time at which this order was placed.","Currently, this is only a piece of metadata."],"type":"u64"},{"name":"lastUpdatedAtTimestamp","docs":["The time at which this order was most-recently updated (including: created).","Currently, this is only a piece of metadata."],"type":"u64"},{"name":"requestedDebtAmount","docs":["The amount of debt that was originally requested when this order was most-recently updated.","In other words: this field holds a value of [Self::remaining_debt_amount] captured at","[Self::last_updated_at_timestamp].","Currently, this is only a piece of metadata."],"type":"u64"},{"name":"maxBorrowRateBps","docs":["The maximum borrow rate that the obligation owner agrees to.","The reserves used for [Obligation::borrows] *cannot* define their maximum borrow rate","*higher* than this."],"type":"u32"},{"name":"active","docs":["Whether the [Self::remaining_debt_amount] is non-zero.","","This field is *not* used by smart contract logic (which prefers to treat the above","[Self::remaining_debt_amount]-based definition as the single source of truth). However, it","is useful for off-chain bots (order-searchers) to efficiently list (i.e. `memcmp` filter)","just the obligations that have active borrow orders."],"type":"u8"},{"name":"enableAutoRolloverOnFilledBorrows","docs":["When `1`, all [Obligation::borrows] that get filled by this order will have their","[FixedTermBorrowRolloverConfig::auto_rollover_enabled] flag set.","","Additionally, their rollover customizations:","- will exactly match this order's constraints regarding [Self::min_debt_term_seconds] and","[Self::max_borrow_rate_bps];","- will use the [FixedTermBorrowRolloverConfig::open_term_allowed] fallback.","","See [BorrowOrder::get_rollover_config_for_filled_borrow()].","","Clarification note: when `0`, this setting has no effect on any borrow (i.e. if an existing","borrow was independently marked for auto-rollover, it will *not* be unmarked when filled by","this order).","","Feature flag note: when [LendingMarket::obligation_borrow_rollover_configuration_enabled] is","disabled, this setting has no effect on any borrow (i.e. the fill will be successful, but","the borrow will not be marked for auto-rollover."],"type":"u8"},{"name":"padding1","docs":["Alignment padding."],"type":{"array":["u8",2]}},{"name":"endPadding","docs":["End padding."],"type":{"array":["u64",5]}}]}},{"name":"FixedTermBorrowRolloverConfig","docs":["Settings driving the auto-rollover (or migration) of an [ObligationLiquidity]'s borrow.","","This covers three flavors:","- *fixed-to-fixed*: a fixed-term borrow rolling into another fixed-term reserve,","- *fixed-to-open*: a fixed-term borrow rolling into an open-term reserve,","- *open-to-fixed*: an open-term borrow migrating into a fixed-term reserve.","","By its nature (not a special case), the zeroed struct means \"no auto-rollover/migration\"."],"type":{"kind":"struct","fields":[{"name":"autoRolloverEnabled","docs":["Whether this *fixed-term* borrow can be permissionlessly prolonged. The funds used to roll","over can come:","- either from a *fixed-term* reserve (same or a different one):","- This can only happen within [LendingMarket::fixed_term_rollover_window_duration_seconds].","- The target reserve must meet all the criteria defined in this config (see","[Self::max_borrow_rate_bps] and [Self::min_debt_term_seconds]).","- Note: not possible when [Self::min_debt_term_seconds] is `0` (open-term only).","- or from an *open-term* reserve:","- This can only happen within [LendingMarket::open_term_rollover_window_duration_seconds].","- The user must explicitly set [Self::open_term_allowed] here.","","This setting is not effective when the borrow is currently using an *open-term* reserve."],"type":"u8"},{"name":"openTermAllowed","docs":["When `1`, then [Self::auto_rollover_enabled] is allowed to roll this borrow over into any","open-term reserve.","","Please note that if such rollover actually happens, then [Self::max_borrow_rate_bps]","condition does not apply - technically, it could be evaluated, but open-term reserves","typically use float-rate (utilization-driven borrow rate curve) which has very high maximum","(when at 100% utilization) that would not meet any practical criteria here."],"type":"u8"},{"name":"migrationToFixedEnabled","docs":["Whether this *open-term* borrow can be permissionlessly migrated into a fixed-term reserve:","- This can happen at any moment (as soon as liquidity becomes available).","- The target fixed-term reserve must meet all the criteria defined in this config (see","[Self::max_borrow_rate_bps] and [Self::min_debt_term_seconds]).","","This setting is not effective when the borrow is currently using a *fixed-term* reserve.","","Cannot be enabled when [Self::min_debt_term_seconds] is `0` (open-term only), because","migrating into a fixed-term reserve contradicts the open-term-only intent."],"type":"u8"},{"name":"alignmentPadding","docs":["Internal alignment padding (free to reuse)."],"type":{"array":["u8",1]}},{"name":"maxBorrowRateBps","docs":["A maximum allowed borrow rate of a reserve that can be used for a rollover/migration.","","Note: this must be set (i.e. non-zero) when enabling any rollover/migration flavor, but is","of course not effective when rollover/migration is not enabled."],"type":"u32"},{"name":"minDebtTermSeconds","docs":["A minimum debt term (in seconds) of a fixed-term reserve that can be used for a","rollover/migration.","","When `0`, the owner only accepts open-term reserves as rollover targets — i.e. rolling over","(or migrating) into a fixed-term reserve is not allowed. This is consistent with the","semantics of [BorrowOrder::min_debt_term_seconds].","","This means that `0` is incompatible with [Self::migration_to_fixed_enabled] (which requires","a fixed-term target) — this combination is rejected at configuration time."],"type":"u64"}]}},{"name":"InitObligationArgs","type":{"kind":"struct","fields":[{"name":"tag","type":"u8"},{"name":"id","type":"u8"}]}},{"name":"ObligationCollateral","docs":["Obligation collateral state"],"type":{"kind":"struct","fields":[{"name":"depositReserve","docs":["Reserve collateral is deposited to"],"type":"publicKey"},{"name":"depositedAmount","docs":["Amount of collateral deposited"],"type":"u64"},{"name":"marketValueSf","docs":["Collateral market value in quote currency (scaled fraction)"],"type":"u128"},{"name":"borrowedAmountAgainstThisCollateralInElevationGroup","docs":["Debt amount (lamport) taken against this collateral.","(only meaningful if this obligation is part of an elevation group, otherwise 0)","This is only indicative of the debt computed on the last refresh obligation.","If the obligation have multiple collateral this value is the same for all of them."],"type":"u64"},{"name":"padding","type":{"array":["u64",9]}}]}},{"name":"ObligationLiquidity","docs":["Obligation liquidity state"],"type":{"kind":"struct","fields":[{"name":"borrowReserve","docs":["Reserve liquidity is borrowed from"],"type":"publicKey"},{"name":"cumulativeBorrowRateBsf","docs":["Borrow rate used for calculating interest (big scaled fraction)"],"type":{"defined":"BigFractionBytes"}},{"name":"lastBorrowedAtTimestamp","docs":["The timestamp at which this debt was taken.","","Conceptually, every borrow can be interpreted as \"closing the previous loan and starting a","new one\" (which would make a plain ` borrowed_at ` an even better name). But in terms of","implementation, this fields records when the *last* borrow operation from this reserve","happened (i.e. adding debt of the same reserve *does* move this timestamp).","","Note: this field is *not* only metadata: it is used in the logic, e.g. for enforcing the","fixed-term borrows (i.e. those induced by [ReserveConfig::debt_term_seconds])."],"type":"u64"},{"name":"borrowedAmountSf","docs":["Amount of liquidity borrowed plus interest (scaled fraction)"],"type":"u128"},{"name":"marketValueSf","docs":["Liquidity market value in quote currency (scaled fraction)"],"type":"u128"},{"name":"borrowFactorAdjustedMarketValueSf","docs":["Risk adjusted liquidity market value in quote currency - DEBUG ONLY - use market_value instead"],"type":"u128"},{"name":"borrowedAmountOutsideElevationGroups","docs":["Amount of liquidity borrowed outside of an elevation group"],"type":"u64"},{"name":"fixedTermBorrowRolloverConfig","docs":["The user's auto-rollover/migration opt-ins. Some settings are effective only for fixed-term","borrows, while others only for open-term borrows - see individual field docs."],"type":{"defined":"FixedTermBorrowRolloverConfig"}},{"name":"borrowedAmountAtExpiration","docs":["An amount of liquidity that was borrowed when this fixed-term borrow expired (i.e. zeroed if","this borrow is not fixed-term, or if it did not yet expire).","","Needed to honor the [LendingMarket::term_based_full_liquidation_duration_secs].","","This value is captured by [Self::capture_borrowed_amount_at_expiration] during obligation's","refresh - please see the method's docs for gotchas.","","Note on precision: we use a `u64` field, since the remaining space within this struct is","rather scarce, and we do not need sub-lamport precision for the liquidation throttling rate."],"type":"u64"},{"name":"padding2","type":{"array":["u64",4]}}]}},{"name":"ObligationOrder","docs":["A single obligation order.","See [Obligation::obligation_orders]."],"type":{"kind":"struct","fields":[{"name":"conditionThresholdSf","docs":["A threshold value used by the condition (scaled [Fraction]).","The exact meaning depends on the specific [Self::condition_type].","","Examples:","- when `condition_type == 2 (UserLtvBelow)`:","then a value of `0.455` here means that the order is active only when the obligation's","user LTV is less than `0.455` (i.e. < 45.5%).","- when `condition_type == 3 (DebtCollPriceRatioAbove)`:","assuming the obligation uses BTC collateral for SOL debt, then a value of `491.3` here","means that the order is active only when the BTC-SOL price is greater than `491.3` (i.e.","> 491.3 SOL per BTC)."],"type":"u128"},{"name":"opportunityParameterSf","docs":["A configuration parameter used by the opportunity (scaled [Fraction]).","The exact meaning depends on the specific [Self::opportunity_type].","","Examples:","- when `opportunity_type == 0 (DeleverageSingleDebtAmount)`:","Assuming the obligation uses BTC collateral for SOL debt, then a value of `1_234_000_000`","here means that a liquidator may repay up to 1234000000 lamports (i.e. 1.234 SOL) on this","obligation.","Note: the special value of [Fraction::MAX] is *not* allowed in this case.","- when `opportunity_type == 1 (DeleverageAllDebtAmount)`:","The only allowed value in this case is [Fraction::MAX] (to emphasize that *all* debt","should be repaid)."],"type":"u128"},{"name":"minExecutionBonusBps","docs":["A *minimum* additional fraction of collateral transferred to the liquidator, in bps.","","The minimum bonus is applied exactly when the [Self::condition_threshold_sf] is met, and","grows linearly towards the [Self::max_execution_bonus_bps].","","Example: a value of `50` here means 50bps == 0.5% bonus for an \"LTV > 65%\" order, when","executed precisely at the moment LTV exceeds 65%."],"type":"u16"},{"name":"maxExecutionBonusBps","docs":["A *maximum* additional fraction of collateral transferred to the liquidator, in bps.","","The maximum bonus is applied at the relevant \"extreme\" state of the obligation, i.e.:","- for a stop-loss condition, it is a point at which the obligation becomes liquidatable;","- for a take-profit condition, it is a point at which obligation has 0% LTV.","","In non-extreme states, the actual bonus value is interpolated linearly, starting from","[Self::min_execution_bonus_bps] (at the point specified by the order's condition).","","Example: a value of `300` here means 300bps == 3.0% bonus for a \"debt/coll price > 140\"","order, when executed at a higher price = 200, at which the obligation's LTV happens to","be equal to its liquidation LTV."],"type":"u16"},{"name":"conditionType","docs":["Serialized [ConditionType].","The entire order is void when this is zeroed (i.e. representing [ConditionType::Never]).","","Example: a value of `2` here denotes `UserLtvBelow` condition type. Of course, to","interpret this condition, we also need to take the [Self::condition_threshold_sf] into","account."],"type":"u8"},{"name":"opportunityType","docs":["Serialized [OpportunityType].","","Example: a value of `0` here denotes `DeleverageSingleDebtAmount` opportunity. Of course, to","interpret this opportunity, we also need to take the [Self::opportunity_parameter_sf] into","account."],"type":"u8"},{"name":"padding1","docs":["Alignment padding.","The fields above take up 2+2+1+1 bytes = 48 bits, which means we need 80 bits = 10 bytes to","align with `u128`s."],"type":{"array":["u8",10]}},{"name":"padding2","docs":["End padding.","The total size of a single instance is 8*u128 = 128 bytes."],"type":{"array":["u128",5]}}]}},{"name":"UpdateObligationConfigMode","docs":["A discriminator of a user-configurable piece of [Obligation].","","Implementation note: due to TS-side codegen quirks (and a \"convention\" currently seen e.g.","within reserve and market update operations), this is not a true Rust enum. The new value of","a config item is provided in a separate handler argument (borsh-serialized), and its expected","type is defined by each discriminator here. Additionally, each update mode acts on a specific","[ObligationConfigUpdateSubject] (e.g. the auto-rollover of fixed-term borrows is configured on","a per-borrow basis), which is also specified by separate handler arguments."],"type":{"kind":"enum","variants":[{"name":"FixedTermRolloverEnabled"},{"name":"FixedTermRolloverMaxBorrowRateBps"},{"name":"FixedTermRolloverMinDebtTermSeconds"},{"name":"FixedTermRolloverOpenTermAllowed"},{"name":"MigrationToFixedEnabled"}]}},{"name":"BigFractionBytes","type":{"kind":"struct","fields":[{"name":"value","type":{"array":["u64",4]}},{"name":"padding","type":{"array":["u64",2]}}]}},{"name":"FeeCalculation","docs":["Calculate fees exlusive or inclusive of an amount"],"type":{"kind":"enum","variants":[{"name":"Exclusive"},{"name":"Inclusive"}]}},{"name":"ReserveCollateral","docs":["Reserve collateral"],"type":{"kind":"struct","fields":[{"name":"mintPubkey","docs":["Reserve collateral mint address"],"type":"publicKey"},{"name":"mintTotalSupply","docs":["Reserve collateral mint supply, used for exchange rate"],"type":"u64"},{"name":"supplyVault","docs":["Reserve collateral supply address"],"type":"publicKey"},{"name":"padding1","type":{"array":["u128",32]}},{"name":"padding2","type":{"array":["u128",32]}}]}},{"name":"ReserveConfig","docs":["Reserve configuration values"],"type":{"kind":"struct","fields":[{"name":"status","docs":["Status of the reserve Active/Obsolete/Hidden"],"type":"u8"},{"name":"paddingDeprecatedAssetTier","docs":["Asset tier -> 0 - regular (collateral & debt), 1 - isolated collateral, 2 - isolated debt"],"type":"u8"},{"name":"hostFixedInterestRateBps","docs":["Flat rate that goes to the host"],"type":"u16"},{"name":"minDeleveragingBonusBps","docs":["Starting bonus for deleveraging-related liquidations, in bps."],"type":"u16"},{"name":"blockCtokenUsage","docs":["Boolean flag to block minting/redeeming of ctokens","Blocks usage of ctokens (minting or withdrawing from obligation)","Effectively blocks deposit_reserve_liquidity and withdraw_obligation_collateral"],"type":"u8"},{"name":"earlyRepayRemainingInterestPct","docs":["The percentage of remaining interest over the debt term that is charged as early repay penalty.","Only meaningful when `debt_term_seconds > 0`."],"type":"u8"},{"name":"emergencyMode","docs":["Whether the reserve is in emergency mode.","Blocks most user operations involving this reserve, similar to [LendingMarket::emergency_mode]","but scoped to a single reserve. Also cascades to obligations using this reserve as","collateral or debt, blocking borrows and withdrawals on other reserves but still","allowing repays and deposits."],"type":"u8"},{"name":"reserved1","docs":["Past reserved space - feel free to reuse."],"type":{"array":["u8",4]}},{"name":"protocolOrderExecutionFeePct","docs":["Cut of the order execution bonus that the protocol receives, as a percentage"],"type":"u8"},{"name":"protocolTakeRatePct","docs":["Protocol take rate is the amount borrowed interest protocol receives, as a percentage"],"type":"u8"},{"name":"protocolLiquidationFeePct","docs":["Cut of the liquidation bonus that the protocol receives, as a percentage"],"type":"u8"},{"name":"loanToValuePct","docs":["Target ratio of the value of borrows to deposits, as a percentage","0 if use as collateral is disabled"],"type":"u8"},{"name":"liquidationThresholdPct","docs":["Loan to value ratio at which an obligation can be liquidated, as percentage"],"type":"u8"},{"name":"minLiquidationBonusBps","docs":["Minimum bonus a liquidator receives when repaying part of an unhealthy obligation, as bps"],"type":"u16"},{"name":"maxLiquidationBonusBps","docs":["Maximum bonus a liquidator receives when repaying part of an unhealthy obligation, as bps"],"type":"u16"},{"name":"badDebtLiquidationBonusBps","docs":["Bad debt liquidation bonus for an undercollateralized obligation, as bps"],"type":"u16"},{"name":"deleveragingMarginCallPeriodSecs","docs":["Time in seconds that must pass before redemptions are enabled after the deposit limit is","crossed.","Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case."],"type":"u64"},{"name":"deleveragingThresholdDecreaseBpsPerDay","docs":["The rate at which the deleveraging threshold decreases, in bps per day.","Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case."],"type":"u64"},{"name":"fees","docs":["Program owner fees assessed, separate from gains due to interest accrual"],"type":{"defined":"ReserveFees"}},{"name":"borrowRateCurve","docs":["Borrow rate curve based on utilization"],"type":{"defined":"BorrowRateCurve"}},{"name":"borrowFactorPct","docs":["Borrow factor in percentage - used for risk adjustment"],"type":"u64"},{"name":"depositLimit","docs":["Maximum deposit limit of liquidity in native units, u64::MAX for inf"],"type":"u64"},{"name":"borrowLimit","docs":["Maximum amount borrowed, u64::MAX for inf, 0 to disable borrows (protected deposits)"],"type":"u64"},{"name":"tokenInfo","docs":["Token id from TokenInfos struct"],"type":{"defined":"TokenInfo"}},{"name":"depositWithdrawalCap","docs":["Deposit withdrawal caps - deposit & redeem"],"type":{"defined":"WithdrawalCaps"}},{"name":"debtWithdrawalCap","docs":["Debt withdrawal caps - borrow & repay"],"type":{"defined":"WithdrawalCaps"}},{"name":"elevationGroups","type":{"array":["u8",20]}},{"name":"disableUsageAsCollOutsideEmode","type":"u8"},{"name":"utilizationLimitBlockBorrowingAbovePct","docs":["Utilization (in percentage) above which borrowing is blocked. 0 to disable."],"type":"u8"},{"name":"autodeleverageEnabled","docs":["Whether this reserve should be subject to auto-deleveraging after deposit or borrow limit is","crossed.","Besides this flag, the lending market's flag also needs to be enabled (logical `AND`).","**NOTE:** the manual \"target LTV\" deleveraging is NOT affected by this flag."],"type":"u8"},{"name":"proposerAuthorityLocked","docs":["Boolean flag indicating whether the reserve is locked for the proposer authority.","","Once the proposer have finished preparing the reserve, it must be locked to prevent","further changes to the reserve configuration allowing review and voting on the proposal","without alteration during the voting period."],"type":"u8"},{"name":"borrowLimitOutsideElevationGroup","docs":["Maximum amount liquidity of this reserve borrowed outside all elevation groups","- u64::MAX for inf","- 0 to disable borrows outside elevation groups"],"type":"u64"},{"name":"borrowLimitAgainstThisCollateralInElevationGroup","docs":["Defines the maximum amount (in lamports of elevation group debt asset)","that can be borrowed when this reserve is used as collateral.","- u64::MAX for inf","- 0 to disable borrows in this elevation group (expected value for the debt asset)"],"type":{"array":["u64",32]}},{"name":"deleveragingBonusIncreaseBpsPerDay","docs":["The rate at which the deleveraging-related liquidation bonus increases, in bps per day.","Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case."],"type":"u64"},{"name":"debtMaturityTimestamp","docs":["The timestamp at which all [Obligation::borrows] using this reserve become liquidatable","(on the same terms as reserve-wide deleveraging).","Inactive when zeroed (i.e. debt never matures).","","Note: this feature is independent of [Self::debt_term_seconds] - the liquidation mechanism","is based directly on the timestamp defined here, on Reserve's level."],"type":"u64"},{"name":"debtTermSeconds","docs":["The duration after which any debt coming from this Reserve must be repaid.","Inactive when zeroed (i.e. funds can be borrowed indefinitely).","","Note: this feature is independent of [Self::debt_maturity_timestamp] - the liquidation","mechanism is based on the [ObligationLiquidity::last_borrowed_at_timestamp]."],"type":"u64"},{"name":"rewardsAmountPerSlot","docs":["Rewards distributed per slot to depositors. Drained from","[ReserveLiquidity::rewards_amount_available] into","[ReserveLiquidity::total_available_amount] at each refresh, capped by the","market-level [LendingMarket::reserve_rewards_max_apr_bps]. `0` disables.","","**Note:** because rewards inflate `total_available_amount`, a non-zero RPS on a","reserve with [Self::autodeleverage_enabled] and a finite [Self::deposit_limit]","will eventually cross the cap and arm the autodeleverage countdown. Size","`deposit_limit` and RPS together."],"type":"u64"},{"name":"permissionedOps","docs":["Bitmask of [PermissionedOp]s gated by the parent market's `permissioning_authority`","when this reserve is the operation's target. `0` = no operation is restricted at the","reserve level. Use [Reserve::get_permissioned_ops] for a typed view."],"type":"u64"}]}},{"name":"ReserveFarmKind","type":{"kind":"enum","variants":[{"name":"Collateral"},{"name":"Debt"}]}},{"name":"ReserveFees","docs":["Additional fee information on a reserve","","These exist separately from interest accrual fees, and are specifically for the program owner","and referral fee. The fees are paid out as a percentage of liquidity token amounts during","repayments and liquidations."],"type":{"kind":"struct","fields":[{"name":"originationFeeSf","docs":["Fee assessed on `BorrowObligationLiquidity`, as scaled fraction (60 bits fractional part)","Must be between `0` and `2^60`, such that `2^60 = 1`. A few examples for","clarity:","1% = (1 << 60) / 100 = 11529215046068470","0.01% (1 basis point) = 115292150460685","0.00001% (Aave origination fee) = 115292150461"],"type":"u64"},{"name":"flashLoanFeeSf","docs":["Fee for flash loan, expressed as scaled fraction.","0.3% (Aave flash loan fee) = 0.003 * 2^60 = 3458764513820541"],"type":"u64"},{"name":"padding","docs":["Used for allignment"],"type":{"array":["u8",8]}}]}},{"name":"ReserveLiquidity","docs":["Reserve liquidity"],"type":{"kind":"struct","fields":[{"name":"mintPubkey","docs":["Reserve liquidity mint address"],"type":"publicKey"},{"name":"supplyVault","docs":["Reserve liquidity supply address"],"type":"publicKey"},{"name":"feeVault","docs":["Reserve liquidity fee collection address"],"type":"publicKey"},{"name":"totalAvailableAmount","docs":["Total reserve liquidity available.","","Note: not all of this liquidity can be freely used for any purpose. Production code should","use the specialized getters - see e.g. [Reserve::total_available_liquidity_amount()],","[Reserve::freely_available_liquidity_amount()]."],"type":"u64"},{"name":"borrowedAmountSf","docs":["Reserve liquidity borrowed (scaled fraction)"],"type":"u128"},{"name":"marketPriceSf","docs":["Reserve liquidity market price in quote currency (scaled fraction)"],"type":"u128"},{"name":"marketPriceLastUpdatedTs","docs":["Unix timestamp of the market price (from the oracle)"],"type":"u64"},{"name":"mintDecimals","docs":["Reserve liquidity mint decimals"],"type":"u64"},{"name":"depositLimitCrossedTimestamp","docs":["Timestamp when the last refresh reserve detected that the liquidity amount is above the deposit cap. When this threshold is crossed, then redemptions (auto-deleverage) are enabled.","If the threshold is not crossed, then the timestamp is set to 0"],"type":"u64"},{"name":"borrowLimitCrossedTimestamp","docs":["Timestamp when the last refresh reserve detected that the borrowed amount is above the borrow cap. When this threshold is crossed, then redemptions (auto-deleverage) are enabled.","If the threshold is not crossed, then the timestamp is set to 0"],"type":"u64"},{"name":"cumulativeBorrowRateBsf","docs":["Reserve liquidity cumulative borrow rate (scaled fraction)"],"type":{"defined":"BigFractionBytes"}},{"name":"accumulatedProtocolFeesSf","docs":["Reserve cumulative protocol fees (scaled fraction)"],"type":"u128"},{"name":"accumulatedReferrerFeesSf","docs":["Reserve cumulative referrer fees (scaled fraction)"],"type":"u128"},{"name":"pendingReferrerFeesSf","docs":["Reserve pending referrer fees, to be claimed in refresh_obligation by referrer or protocol (scaled fraction)"],"type":"u128"},{"name":"absoluteReferralRateSf","docs":["Reserve referrer fee absolute rate calculated at each refresh_reserve operation (scaled fraction)"],"type":"u128"},{"name":"tokenProgram","docs":["Token program of the liquidity mint"],"type":"publicKey"},{"name":"rewardsAmountAvailable","docs":["Reserve rewards budget remaining for distribution.","","Tokens are deposited via `topup_reserve_rewards` and increase this counter (without","touching [Self::total_available_amount]). On every `refresh_reserve`, up to","`rewards_amount_per_slot * slots_elapsed` tokens are moved from this counter into","[Self::total_available_amount], inflating the cToken exchange rate, capped by the","market-level `reserve_rewards_max_apr_bps` cap."],"type":"u64"},{"name":"padding2","type":{"array":["u64",50]}},{"name":"padding3","type":{"array":["u128",32]}}]}},{"name":"ReserveStatus","type":{"kind":"enum","variants":[{"name":"Active"},{"name":"Obsolete"},{"name":"Hidden"}]}},{"name":"WithdrawQueue","docs":["A tracker of ticket-based withdrawals."],"type":{"kind":"struct","fields":[{"name":"queuedCollateralAmount","docs":["The part of [ReserveLiquidity::total_available_amount] locked for ticketed withdrawals."],"type":"u64"},{"name":"nextIssuedTicketSequenceNumber","docs":["The sequence number of the next ticket to be issued when enqueueing to withdraw.","Note: it is also a number of tickets issued so far."],"type":"u64"},{"name":"nextWithdrawableTicketSequenceNumber","docs":["The sequence number of the next ticket to be used for actually transferring the withdrawn","liquidity (assuming it is available in the reserve).","Note: it is also a number of fully-consumed tickets so far."],"type":"u64"}]}},{"name":"WithdrawalCaps","docs":["Reserve Withdrawal Caps State"],"type":{"kind":"struct","fields":[{"name":"configCapacity","type":"i64"},{"name":"currentTotal","type":"i64"},{"name":"lastIntervalStartTimestamp","type":"u64"},{"name":"configIntervalLengthSeconds","type":"u64"}]}},{"name":"PriceHeuristic","type":{"kind":"struct","fields":[{"name":"lower","docs":["Lower value of acceptable price"],"type":"u64"},{"name":"upper","docs":["Upper value of acceptable price"],"type":"u64"},{"name":"exp","docs":["Number of decimals of the previously defined values"],"type":"u64"}]}},{"name":"PythConfiguration","type":{"kind":"struct","fields":[{"name":"price","docs":["Pubkey of the base price feed (disabled if `null` or `default`)"],"type":"publicKey"}]}},{"name":"ScopeConfiguration","type":{"kind":"struct","fields":[{"name":"priceFeed","docs":["Pubkey of the scope price feed (disabled if `null` or `default`)"],"type":"publicKey"},{"name":"priceChain","docs":["This is the scope_id price chain that results in a price for the token"],"type":{"array":["u16",4]}},{"name":"twapChain","docs":["This is the scope_id price chain for the twap"],"type":{"array":["u16",4]}}]}},{"name":"SwitchboardConfiguration","type":{"kind":"struct","fields":[{"name":"priceAggregator","docs":["Pubkey of the base price feed (disabled if `null` or `default`)"],"type":"publicKey"},{"name":"twapAggregator","type":"publicKey"}]}},{"name":"TokenInfo","type":{"kind":"struct","fields":[{"name":"name","docs":["UTF-8 encoded name of the token (null-terminated)"],"type":{"array":["u8",32]}},{"name":"heuristic","docs":["Heuristics limits of acceptable price"],"type":{"defined":"PriceHeuristic"}},{"name":"maxTwapDivergenceBps","docs":["Max divergence between twap and price in bps"],"type":"u64"},{"name":"maxAgePriceSeconds","type":"u64"},{"name":"maxAgeTwapSeconds","type":"u64"},{"name":"scopeConfiguration","docs":["Scope price configuration"],"type":{"defined":"ScopeConfiguration"}},{"name":"switchboardConfiguration","docs":["Switchboard configuration"],"type":{"defined":"SwitchboardConfiguration"}},{"name":"pythConfiguration","docs":["Pyth configuration"],"type":{"defined":"PythConfiguration"}},{"name":"blockPriceUsage","type":"u8"},{"name":"reserved","type":{"array":["u8",7]}},{"name":"padding","type":{"array":["u64",19]}}]}},{"name":"ProgressCallbackType","docs":["A callback to be notified when the ticket is being processed.","","## Why an enum?","","Only reliable programs may be used for callbacks (since any error or panic returned from a CPI","aborts an entire transaction, which would stall the queue progress). Hence, we need a whitelist,","and the simplest initial implementation is a hardcoded enum. If we want to be able to add new","whitelist items without SC updates, we can implement such support using a special enum value","(e.g. `SPECIFIED_BY_PDA = 255`)."],"type":{"kind":"enum","variants":[{"name":"None"},{"name":"KlendQueueAccountingHandlerOnKvault"}]}},{"name":"BorrowRateCurve","type":{"kind":"struct","fields":[{"name":"points","type":{"array":[{"defined":"CurvePoint"},11]}}]}},{"name":"CurvePoint","type":{"kind":"struct","fields":[{"name":"utilizationRateBps","type":"u32"},{"name":"borrowRateBps","type":"u32"}]}}],"events":[{"name":"BorrowOrderCancelEvent","fields":[{"name":"before","type":{"defined":"BorrowOrder"},"index":false}]},{"name":"BorrowOrderFullFillEvent","fields":[{"name":"before","type":{"defined":"BorrowOrder"},"index":false}]},{"name":"BorrowOrderPartialFillEvent","fields":[{"name":"before","type":{"defined":"BorrowOrder"},"index":false},{"name":"after","type":{"defined":"BorrowOrder"},"index":false}]},{"name":"BorrowOrderPlaceEvent","fields":[{"name":"after","type":{"defined":"BorrowOrder"},"index":false}]},{"name":"BorrowOrderUpdateEvent","fields":[{"name":"before","type":{"defined":"BorrowOrder"},"index":false},{"name":"after","type":{"defined":"BorrowOrder"},"index":false}]}],"errors":[{"code":6000,"name":"InvalidMarketAuthority","msg":"Market authority is invalid"},{"code":6001,"name":"InvalidMarketOwner","msg":"Market owner is invalid"},{"code":6002,"name":"InvalidAccountOwner","msg":"Input account owner is not the program address"},{"code":6003,"name":"InvalidAmount","msg":"Input amount is invalid"},{"code":6004,"name":"InvalidConfig","msg":"Input config value is invalid"},{"code":6005,"name":"InvalidSigner","msg":"Signer is not allowed to perform this action"},{"code":6006,"name":"InvalidAccountInput","msg":"Invalid account input"},{"code":6007,"name":"MathOverflow","msg":"Math operation overflow"},{"code":6008,"name":"InsufficientLiquidity","msg":"Insufficient liquidity available"},{"code":6009,"name":"ReserveStale","msg":"Reserve state needs to be refreshed"},{"code":6010,"name":"WithdrawTooSmall","msg":"Withdraw amount too small"},{"code":6011,"name":"WithdrawTooLarge","msg":"Withdraw amount too large"},{"code":6012,"name":"BorrowTooSmall","msg":"Borrow amount too small to receive liquidity after fees"},{"code":6013,"name":"BorrowTooLarge","msg":"Borrow amount too large for deposited collateral"},{"code":6014,"name":"RepayTooSmall","msg":"Repay amount too small to transfer liquidity"},{"code":6015,"name":"LiquidationTooSmall","msg":"Liquidation amount too small to receive collateral"},{"code":6016,"name":"ObligationHealthy","msg":"Cannot liquidate healthy obligations"},{"code":6017,"name":"ObligationStale","msg":"Obligation state needs to be refreshed"},{"code":6018,"name":"ObligationReserveLimit","msg":"Obligation reserve limit exceeded"},{"code":6019,"name":"InvalidObligationOwner","msg":"Obligation owner is invalid"},{"code":6020,"name":"ObligationDepositsEmpty","msg":"Obligation deposits are empty"},{"code":6021,"name":"ObligationBorrowsEmpty","msg":"Obligation borrows are empty"},{"code":6022,"name":"ObligationDepositsZero","msg":"Obligation deposits have zero value"},{"code":6023,"name":"ObligationBorrowsZero","msg":"Obligation borrows have zero value"},{"code":6024,"name":"InvalidObligationCollateral","msg":"Invalid obligation collateral"},{"code":6025,"name":"InvalidObligationLiquidity","msg":"Invalid obligation liquidity"},{"code":6026,"name":"ObligationCollateralEmpty","msg":"Obligation collateral is empty"},{"code":6027,"name":"ObligationLiquidityEmpty","msg":"Obligation liquidity is empty"},{"code":6028,"name":"NegativeInterestRate","msg":"Interest rate is negative"},{"code":6029,"name":"InvalidOracleConfig","msg":"Input oracle config is invalid"},{"code":6030,"name":"InsufficientProtocolFeesToRedeem","msg":"Insufficient protocol fees to claim or no liquidity available"},{"code":6031,"name":"FlashBorrowCpi","msg":"No cpi flash borrows allowed"},{"code":6032,"name":"NoFlashRepayFound","msg":"No corresponding repay found for flash borrow"},{"code":6033,"name":"InvalidFlashRepay","msg":"Invalid repay found"},{"code":6034,"name":"FlashRepayCpi","msg":"No cpi flash repays allowed"},{"code":6035,"name":"MultipleFlashBorrows","msg":"Multiple flash borrows not allowed in the same transaction"},{"code":6036,"name":"FlashLoansDisabled","msg":"Flash loans are disabled for this reserve"},{"code":6037,"name":"SwitchboardV2Error","msg":"Switchboard error"},{"code":6038,"name":"CouldNotDeserializeScope","msg":"Cannot deserialize the scope price account"},{"code":6039,"name":"PriceTooOld","msg":"Price too old"},{"code":6040,"name":"PriceTooDivergentFromTwap","msg":"Price too divergent from twap"},{"code":6041,"name":"InvalidTwapPrice","msg":"Invalid twap price"},{"code":6042,"name":"GlobalEmergencyMode","msg":"Emergency mode is enabled"},{"code":6043,"name":"InvalidFlag","msg":"Invalid lending market config"},{"code":6044,"name":"PriceNotValid","msg":"Price is not valid"},{"code":6045,"name":"PriceIsBiggerThanHeuristic","msg":"Price is bigger than allowed by heuristic"},{"code":6046,"name":"PriceIsLowerThanHeuristic","msg":"Price lower than allowed by heuristic"},{"code":6047,"name":"PriceIsZero","msg":"Price is zero"},{"code":6048,"name":"PriceConfidenceTooWide","msg":"Price confidence too wide"},{"code":6049,"name":"IntegerOverflow","msg":"Conversion between integers failed"},{"code":6050,"name":"NoFarmForReserve","msg":"This reserve does not have a farm"},{"code":6051,"name":"IncorrectInstructionInPosition","msg":"Wrong instruction at expected position"},{"code":6052,"name":"NoPriceFound","msg":"No price found"},{"code":6053,"name":"InvalidTwapConfig","msg":"Invalid Twap configuration: Twap is enabled but one of the enabled price doesn't have a twap"},{"code":6054,"name":"InvalidPythPriceAccount","msg":"Pyth price account does not match configuration"},{"code":6055,"name":"InvalidSwitchboardAccount","msg":"Switchboard account(s) do not match configuration"},{"code":6056,"name":"InvalidScopePriceAccount","msg":"Scope price account does not match configuration"},{"code":6057,"name":"ObligationCollateralLtvZero","msg":"The obligation has one collateral with an LTV set to 0. Withdraw it before withdrawing other collaterals"},{"code":6058,"name":"InvalidObligationSeedsValue","msg":"Seeds must be default pubkeys for tag 0, and mint addresses for tag 1 or 2"},{"code":6059,"name":"DeprecatedInvalidObligationId","msg":"[DEPRECATED] Obligation id must be 0"},{"code":6060,"name":"InvalidBorrowRateCurvePoint","msg":"Invalid borrow rate curve point"},{"code":6061,"name":"InvalidUtilizationRate","msg":"Invalid utilization rate"},{"code":6062,"name":"CannotSocializeObligationWithCollateral","msg":"Obligation hasn't been fully liquidated and debt cannot be socialized."},{"code":6063,"name":"ObligationEmpty","msg":"Obligation has no borrows or deposits."},{"code":6064,"name":"WithdrawalCapReached","msg":"Withdrawal cap is reached"},{"code":6065,"name":"LastTimestampGreaterThanCurrent","msg":"The last interval start timestamp is greater than the current timestamp"},{"code":6066,"name":"LiquidationRewardTooSmall","msg":"The reward amount is less than the minimum acceptable received liquidity"},{"code":6067,"name":"IsolatedAssetTierViolation","msg":"Isolated Asset Tier Violation"},{"code":6068,"name":"InconsistentElevationGroup","msg":"The obligation's elevation group and the reserve's are not the same"},{"code":6069,"name":"InvalidElevationGroup","msg":"The elevation group chosen for the reserve does not exist in the lending market"},{"code":6070,"name":"InvalidElevationGroupConfig","msg":"The elevation group updated has wrong parameters set"},{"code":6071,"name":"UnhealthyElevationGroupLtv","msg":"The current obligation must have most or all its debt repaid before changing the elevation group"},{"code":6072,"name":"ElevationGroupNewLoansDisabled","msg":"Elevation group does not accept any new loans or any new borrows/withdrawals"},{"code":6073,"name":"ReserveDeprecated","msg":"Reserve was deprecated, no longer usable"},{"code":6074,"name":"ReferrerAccountNotInitialized","msg":"Referrer account not initialized"},{"code":6075,"name":"ReferrerAccountMintMissmatch","msg":"Referrer account mint does not match the operation reserve mint"},{"code":6076,"name":"ReferrerAccountWrongAddress","msg":"Referrer account address is not a valid program address"},{"code":6077,"name":"ReferrerAccountReferrerMissmatch","msg":"Referrer account referrer does not match the owner referrer"},{"code":6078,"name":"ReferrerAccountMissing","msg":"Referrer account missing for obligation with referrer"},{"code":6079,"name":"InsufficientReferralFeesToRedeem","msg":"Insufficient referral fees to claim or no liquidity available"},{"code":6080,"name":"CpiDisabled","msg":"CPI disabled for this instruction"},{"code":6081,"name":"ShortUrlNotAsciiAlphanumeric","msg":"Referrer short_url is not ascii alphanumeric"},{"code":6082,"name":"ReserveObsolete","msg":"Reserve is marked as obsolete"},{"code":6083,"name":"ElevationGroupAlreadyActivated","msg":"Obligation already part of the same elevation group"},{"code":6084,"name":"ObligationInObsoleteReserve","msg":"Obligation has a deposit or borrow in an obsolete reserve"},{"code":6085,"name":"ReferrerStateOwnerMismatch","msg":"Referrer state owner does not match the given signer"},{"code":6086,"name":"UserMetadataOwnerAlreadySet","msg":"User metadata owner is already set"},{"code":6087,"name":"CollateralNonLiquidatable","msg":"This collateral cannot be liquidated (LTV set to 0)"},{"code":6088,"name":"BorrowingDisabled","msg":"Borrowing is disabled"},{"code":6089,"name":"BorrowLimitExceeded","msg":"Cannot borrow above borrow limit"},{"code":6090,"name":"DepositLimitExceeded","msg":"Cannot deposit above deposit limit"},{"code":6091,"name":"BorrowingDisabledOutsideElevationGroup","msg":"Reserve does not accept any new borrows outside elevation group"},{"code":6092,"name":"NetValueRemainingTooSmall","msg":"Net value remaining too small"},{"code":6093,"name":"WorseLtvBlocked","msg":"Cannot get the obligation in a worse position"},{"code":6094,"name":"LiabilitiesBiggerThanAssets","msg":"Cannot have more liabilities than assets in a position"},{"code":6095,"name":"ReserveTokenBalanceMismatch","msg":"Reserve state and token account cannot drift"},{"code":6096,"name":"ReserveVaultBalanceMismatch","msg":"Reserve token account has been unexpectedly modified"},{"code":6097,"name":"ReserveAccountingMismatch","msg":"Reserve internal state accounting has been unexpectedly modified"},{"code":6098,"name":"BorrowingAboveUtilizationRateDisabled","msg":"Borrowing above set utilization rate is disabled"},{"code":6099,"name":"LiquidationBorrowFactorPriority","msg":"Liquidation must prioritize the debt with the highest borrow factor"},{"code":6100,"name":"LiquidationLowestLiquidationLtvPriority","msg":"Liquidation must prioritize the collateral with the lowest liquidation LTV"},{"code":6101,"name":"ElevationGroupBorrowLimitExceeded","msg":"Elevation group borrow limit exceeded"},{"code":6102,"name":"ElevationGroupWithoutDebtReserve","msg":"The elevation group does not have a debt reserve defined"},{"code":6103,"name":"ElevationGroupMaxCollateralReserveZero","msg":"The elevation group does not allow any collateral reserves"},{"code":6104,"name":"ElevationGroupHasAnotherDebtReserve","msg":"In elevation group attempt to borrow from a reserve that is not the debt reserve"},{"code":6105,"name":"ElevationGroupDebtReserveAsCollateral","msg":"The elevation group's debt reserve cannot be used as a collateral reserve"},{"code":6106,"name":"ObligationCollateralExceedsElevationGroupLimit","msg":"Obligation have more collateral than the maximum allowed by the elevation group"},{"code":6107,"name":"ObligationElevationGroupMultipleDebtReserve","msg":"Obligation is an elevation group but have more than one debt reserve"},{"code":6108,"name":"UnsupportedTokenExtension","msg":"Mint has a token (2022) extension that is not supported"},{"code":6109,"name":"InvalidTokenAccount","msg":"Can't have an spl token mint with a t22 account"},{"code":6110,"name":"DepositDisabledOutsideElevationGroup","msg":"Can't deposit into this reserve outside elevation group"},{"code":6111,"name":"CannotCalculateReferralAmountDueToSlotsMismatch","msg":"Cannot calculate referral amount due to slots mismatch"},{"code":6112,"name":"ObligationOwnersMustMatch","msg":"Obligation owners must match"},{"code":6113,"name":"ObligationsMustMatch","msg":"Obligations must match"},{"code":6114,"name":"LendingMarketsMustMatch","msg":"Lending markets must match"},{"code":6115,"name":"ObligationCurrentlyMarkedForDeleveraging","msg":"Obligation is already marked for deleveraging"},{"code":6116,"name":"MaximumWithdrawValueZero","msg":"Maximum withdrawable value of this collateral is zero, LTV needs improved"},{"code":6117,"name":"ZeroMaxLtvAssetsInDeposits","msg":"No max LTV 0 assets allowed in deposits for repay and withdraw"},{"code":6118,"name":"LowestLtvAssetsPriority","msg":"Withdrawing must prioritize the collateral with the lowest reserve max-LTV"},{"code":6119,"name":"WorseLtvThanUnhealthyLtv","msg":"Cannot get the obligation liquidatable"},{"code":6120,"name":"FarmAccountsMissing","msg":"Farm accounts to refresh are missing"},{"code":6121,"name":"RepayTooSmallForFullLiquidation","msg":"Repay amount is too small to satisfy the mandatory full liquidation"},{"code":6122,"name":"InsufficientRepayAmount","msg":"Liquidator provided repay amount lower than required by liquidation rules"},{"code":6123,"name":"OrderIndexOutOfBounds","msg":"Obligation order of the given index cannot exist"},{"code":6124,"name":"InvalidOrderConfiguration","msg":"Given order configuration has wrong parameters"},{"code":6125,"name":"OrderConfigurationNotSupportedByObligation","msg":"Given order configuration cannot be used with the current state of the obligation"},{"code":6126,"name":"OperationNotPermittedWithCurrentObligationOrders","msg":"Single debt, single collateral obligation orders have to be cancelled before changing the deposit/borrow count"},{"code":6127,"name":"OperationNotPermittedMarketImmutable","msg":"Cannot update lending market because it is set as immutable"},{"code":6128,"name":"OrderCreationDisabled","msg":"Creation of new orders is disabled"},{"code":6129,"name":"NoUpgradeAuthority","msg":"Cannot initialize global config because there is no upgrade authority to the program"},{"code":6130,"name":"InitialAdminDepositExecuted","msg":"Initial admin deposit in reserve already executed"},{"code":6131,"name":"ReserveHasNotReceivedInitialDeposit","msg":"Reserve has not received the initial deposit, cannot update config"},{"code":6132,"name":"CTokenUsageBlocked","msg":"CToken minting/redeeming is blocked for this reserve"},{"code":6133,"name":"CannotUseSameReserve","msg":"Cannot call ix with same reserve"},{"code":6134,"name":"TransactionIncludesRestrictedPrograms","msg":"Transaction includes restricted programs"},{"code":6135,"name":"BorrowOrderDebtLiquidityMintMismatch","msg":"There is no borrow order requesting debt in the given asset"},{"code":6136,"name":"BorrowOrderMaxBorrowRateExceeded","msg":"Reserve used for fill exceeds the maximum borrow rate specified by the order"},{"code":6137,"name":"BorrowOrderMinDebtTermInsufficient","msg":"Reserve used for fill defines a debt term shorter than specified by the order"},{"code":6138,"name":"BorrowOrderFillTimeLimitExceeded","msg":"Borrow order can no longer be filled"},{"code":6139,"name":"ReserveDebtMaturityReached","msg":"Cannot borrow from a reserve that reached its debt maturity timestamp"},{"code":6140,"name":"NonUpdatableOrderConfiguration","msg":"Some piece of the order's configuration cannot be updated (the order should be cancelled and placed again)"},{"code":6141,"name":"BorrowOrderExecutionDisabled","msg":"Execution of borrow orders is disabled"},{"code":6142,"name":"DebtReachedReserveDebtTerm","msg":"Cannot increase the debt that has reached its end of term configured by the reserve"},{"code":6143,"name":"ExpectationNotMet","msg":"The on-chain state does not meet expectation specified by the caller, so the operation must be aborted (to avoid race conditions)"},{"code":6144,"name":"BorrowOrderFillValueTooSmall","msg":"Available liquidity could not satisfy the minimum required borrow order fill value"},{"code":6145,"name":"WithdrawTicketIssuanceDisabled","msg":"Issuing new withdraw tickets is disabled by the market"},{"code":6146,"name":"WithdrawTicketRedemptionDisabled","msg":"Redeeming withdraw tickets is disabled by the market"},{"code":6147,"name":"WithdrawTicketStillValid","msg":"Recovering collateral is only available after the withdraw ticket has been marked invalid"},{"code":6148,"name":"WithdrawTicketRequiresFullRedemption","msg":"The withdraw ticket's current state requires that it is fully redeemed (e.g. due to owner ATA creation), but there is not enough liquidity"},{"code":6149,"name":"UserTokenBalanceMismatch","msg":"The user's token account has changed its balance in an unexpected way"},{"code":6150,"name":"WithdrawQueuedLiquidityValueTooSmall","msg":"Available liquidity could not satisfy the minimum required ticketed withdrawal value"},{"code":6151,"name":"InvalidTokenAccountState","msg":"Token account is in a state preventing the handler's operation (e.g. frozen or delegate)"},{"code":6152,"name":"WithdrawTicketInvalid","msg":"Cannot use ticket that was already marked invalid"},{"code":6153,"name":"BorrowOrderValueTooSmall","msg":"Borrow order's value would be below the market-configured minimum"},{"code":6154,"name":"WithdrawTicketValueTooSmall","msg":"Withdraw ticket's value would be below the market-configured minimum"},{"code":6155,"name":"InvalidWithdrawTicketProgressCallbackConfig","msg":"Invalid configuration or required custom accounts for the requested withdraw ticket callback type"},{"code":6156,"name":"WithdrawTicketProgressCallbackAccountsMissing","msg":"One or more accounts required by the ticket's configured progress callback are missing"},{"code":6157,"name":"BorrowRolloverConfigurationDisabled","msg":"Configuring auto-rollover on loans is disabled by market owner"},{"code":6158,"name":"InvalidObligationConfigUpdateSubject","msg":"Invalid specification of the Obligation's part to be configured"},{"code":6159,"name":"BorrowRolloverLiquidityMintMismatch","msg":"Auto-rollover must use a target reserve of the same token"},{"code":6160,"name":"ObligationBorrowRolloverNotApplicable","msg":"The given borrow is not fixed-term and does not require rolling over"},{"code":6161,"name":"ObligationBorrowOutsideRolloverWindow","msg":"The given borrow is outside the corresponding market-configured rollover window"},{"code":6162,"name":"ObligationBorrowRolloverNotEnabledByOwner","msg":"Obligation's owner did not opt-in for auto-rollover of the given borrow"},{"code":6163,"name":"ObligationBorrowRolloverTargetReserveMismatch","msg":"Obligation's owner did not allow to roll over into terms offered by the given reserve"},{"code":6164,"name":"BorrowRolloverExecutionDisabled","msg":"Executing auto-rollover is disabled by market owner"},{"code":6165,"name":"ObligationAccountingMismatch","msg":"Obligation internal state accounting has been unexpectedly modified"},{"code":6166,"name":"PartialRolloverValueTooSmall","msg":"Partial rollover amount is below the market-configured minimum value"},{"code":6167,"name":"ObligationBorrowRolloverConfigMismatch","msg":"Pre-existing rollover configuration of the loan cannot be overwritten by the operation"},{"code":6168,"name":"ObligationBorrowRolloverMustProlongDebtTerm","msg":"Rollover into existing borrow must prolong the remaining debt term"},{"code":6169,"name":"RolloverNotSupportedInElevationGroup","msg":"Rollover is not supported for obligations in an elevation group"},{"code":6170,"name":"WithdrawTicketCancellationDisabled","msg":"Cancelling withdraw tickets is disabled by the market"},{"code":6171,"name":"WithdrawTicketFullyCancelled","msg":"Cannot use ticket that was already fully-cancelled"},{"code":6172,"name":"CloneSourceReserveDisabled","msg":"Cannot clone config from a reserve that is disabled"},{"code":6173,"name":"CloneTargetReserveAlreadyInUse","msg":"Cannot clone config into a reserve that has been in use"},{"code":6174,"name":"ClonedReserveLiquidityMintMismatch","msg":"Cannot clone config between reserves of different mints"},{"code":6175,"name":"ReserveEmergencyMode","msg":"Reserve emergency mode is enabled"},{"code":6176,"name":"ObligationOwnershipTransferInProgress","msg":"Obligation ownership transfer is in progress"},{"code":6177,"name":"ObligationOwnershipTransferNotInInitiatedState","msg":"Obligation ownership transfer is not in initiated state"},{"code":6178,"name":"ObligationPendingOwnerNotSet","msg":"Obligation pending owner not set"},{"code":6179,"name":"ObligationInvalidPendingOwner","msg":"Invalid pending owner address"},{"code":6180,"name":"ObligationOwnershipTransferNotApproved","msg":"Obligation ownership transfer not approved by admin"},{"code":6181,"name":"ObligationHasActiveBorrowOrders","msg":"Obligation has active borrow orders"},{"code":6182,"name":"OnlyComputeBudgetCompanionIxsAllowed","msg":"Only ComputeBudget instructions may accompany this instruction"},{"code":6183,"name":"MissingPermissioner","msg":"Required permissioning account is missing"},{"code":6184,"name":"ReserveRewardsDisabled","msg":"Reserve rewards are disabled on this market (reserve_rewards_max_apr_bps is 0)"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/kamino_vault.kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr.json b/migration/khadhroony-bot2-reference/idls/kamino_vault.kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr.json new file mode 100644 index 0000000..231f87f --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/kamino_vault.kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"kamino_vault","instructions":[{"name":"initVault","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"baseTokenMint","isMut":false,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"adminTokenAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateReserveAllocation","accounts":[{"name":"signer","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserve","isMut":false,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"weight","type":"u64"},{"name":"cap","type":"u64"}]},{"name":"deposit","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"userTokenAta","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxAmount","type":"u64"}]},{"name":"withdraw","accounts":[{"name":"withdrawFromAvailable","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"userTokenAta","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false}]},{"name":"withdrawFromReserveAccounts","accounts":[{"name":"vaultState","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]}],"args":[{"name":"sharesAmount","type":"u64"}]},{"name":"invest","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"payerTokenAccount","isMut":true,"isSigner":false},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false,"docs":["CPI accounts"]},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[]},{"name":"updateVaultConfig","accounts":[{"name":"vaultAdminAuthority","isMut":false,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false}],"args":[{"name":"entry","type":{"defined":"VaultConfigField"}},{"name":"data","type":"bytes"}]},{"name":"withdrawPendingFees","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"tokenAta","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false,"docs":["CPI accounts"]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[]},{"name":"updateAdmin","accounts":[{"name":"pendingAdmin","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false}],"args":[]},{"name":"giveUpPendingFees","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxAmountToGiveUp","type":"u64"}]},{"name":"initializeSharesMetadata","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":false,"isSigner":false},{"name":"sharesMint","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"sharesMetadata","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"updateSharesMetadata","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"sharesMetadata","isMut":true,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"withdrawFromAvailable","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"userTokenAta","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false}],"args":[{"name":"sharesAmount","type":"u64"}]}],"accounts":[{"name":"Reserve","type":{"kind":"struct","fields":[{"name":"version","docs":["Version of the reserve"],"type":"u64"},{"name":"lastUpdate","docs":["Last slot when supply and rates updated"],"type":{"defined":"LastUpdate"}},{"name":"lendingMarket","docs":["Lending market address"],"type":"publicKey"},{"name":"farmCollateral","type":"publicKey"},{"name":"farmDebt","type":"publicKey"},{"name":"liquidity","docs":["Reserve liquidity"],"type":{"defined":"ReserveLiquidity"}},{"name":"reserveLiquidityPadding","type":{"array":["u64",150]}},{"name":"collateral","docs":["Reserve collateral"],"type":{"defined":"ReserveCollateral"}},{"name":"reserveCollateralPadding","type":{"array":["u64",150]}},{"name":"config","docs":["Reserve configuration values"],"type":{"defined":"ReserveConfig"}},{"name":"configPadding","type":{"array":["u64",116]}},{"name":"borrowedAmountOutsideElevationGroup","type":"u64"},{"name":"borrowedAmountsAgainstThisReserveInElevationGroups","docs":["Amount of token borrowed in lamport of debt asset in the given","elevation group when this reserve is part of the collaterals."],"type":{"array":["u64",32]}},{"name":"padding","type":{"array":["u64",207]}}]}},{"name":"VaultState","type":{"kind":"struct","fields":[{"name":"vaultAdminAuthority","type":"publicKey"},{"name":"baseVaultAuthority","type":"publicKey"},{"name":"baseVaultAuthorityBump","type":"u64"},{"name":"tokenMint","type":"publicKey"},{"name":"tokenMintDecimals","type":"u64"},{"name":"tokenVault","type":"publicKey"},{"name":"tokenProgram","type":"publicKey"},{"name":"sharesMint","type":"publicKey"},{"name":"sharesMintDecimals","type":"u64"},{"name":"tokenAvailable","type":"u64"},{"name":"sharesIssued","type":"u64"},{"name":"availableCrankFunds","type":"u64"},{"name":"padding0","type":"u64"},{"name":"performanceFeeBps","type":"u64"},{"name":"managementFeeBps","type":"u64"},{"name":"lastFeeChargeTimestamp","type":"u64"},{"name":"prevAumSf","type":"u128"},{"name":"pendingFeesSf","type":"u128"},{"name":"vaultAllocationStrategy","type":{"array":[{"defined":"VaultAllocation"},10]}},{"name":"minDepositAmount","type":"u64"},{"name":"minWithdrawAmount","type":"u64"},{"name":"minInvestAmount","type":"u64"},{"name":"minInvestDelaySlots","type":"u64"},{"name":"crankFundFeePerReserve","type":"u64"},{"name":"pendingAdmin","type":"publicKey"},{"name":"cumulativeEarnedInterestSf","type":"u128"},{"name":"cumulativeMgmtFeesSf","type":"u128"},{"name":"cumulativePerfFeesSf","type":"u128"},{"name":"name","type":{"array":["u8",40]}},{"name":"vaultLookupTable","type":"publicKey"},{"name":"vaultFarm","type":"publicKey"},{"name":"creationTimestamp","type":"u64"},{"name":"padding1","type":"u64"},{"name":"allocationAdmin","type":"publicKey"},{"name":"padding2","type":{"array":["u128",242]}}]}}],"types":[{"name":"LastUpdate","docs":["Last update state"],"type":{"kind":"struct","fields":[{"name":"slot","docs":["Last slot when updated"],"type":"u64"},{"name":"stale","docs":["True when marked stale, false when slot updated"],"type":"u8"},{"name":"priceStatus","docs":["Status of the prices used to calculate the last update"],"type":"u8"},{"name":"placeholder","type":{"array":["u8",6]}}]}},{"name":"BigFractionBytes","type":{"kind":"struct","fields":[{"name":"value","type":{"array":["u64",4]}},{"name":"padding","type":{"array":["u64",2]}}]}},{"name":"ReserveCollateral","docs":["Reserve collateral"],"type":{"kind":"struct","fields":[{"name":"mintPubkey","docs":["Reserve collateral mint address"],"type":"publicKey"},{"name":"mintTotalSupply","docs":["Reserve collateral mint supply, used for exchange rate"],"type":"u64"},{"name":"supplyVault","docs":["Reserve collateral supply address"],"type":"publicKey"},{"name":"padding1","type":{"array":["u128",32]}},{"name":"padding2","type":{"array":["u128",32]}}]}},{"name":"ReserveConfig","docs":["Reserve configuration values"],"type":{"kind":"struct","fields":[{"name":"status","docs":["Status of the reserve Active/Obsolete/Hidden"],"type":"u8"},{"name":"assetTier","docs":["Asset tier -> 0 - regular (collateral & debt), 1 - isolated collateral, 2 - isolated debt"],"type":"u8"},{"name":"hostFixedInterestRateBps","docs":["Flat rate that goes to the host"],"type":"u16"},{"name":"reserved2","docs":["[DEPRECATED] Boost for side (debt or collateral)"],"type":{"array":["u8",2]}},{"name":"reserved3","docs":["[DEPRECATED] Reward points multiplier per obligation type"],"type":{"array":["u8",8]}},{"name":"protocolTakeRatePct","docs":["Protocol take rate is the amount borrowed interest protocol receives, as a percentage"],"type":"u8"},{"name":"protocolLiquidationFeePct","docs":["Cut of the liquidation bonus that the protocol receives, as a percentage"],"type":"u8"},{"name":"loanToValuePct","docs":["Target ratio of the value of borrows to deposits, as a percentage","0 if use as collateral is disabled"],"type":"u8"},{"name":"liquidationThresholdPct","docs":["Loan to value ratio at which an obligation can be liquidated, as percentage"],"type":"u8"},{"name":"minLiquidationBonusBps","docs":["Minimum bonus a liquidator receives when repaying part of an unhealthy obligation, as bps"],"type":"u16"},{"name":"maxLiquidationBonusBps","docs":["Maximum bonus a liquidator receives when repaying part of an unhealthy obligation, as bps"],"type":"u16"},{"name":"badDebtLiquidationBonusBps","docs":["Bad debt liquidation bonus for an undercollateralized obligation, as bps"],"type":"u16"},{"name":"deleveragingMarginCallPeriodSecs","docs":["Time in seconds that must pass before redemptions are enabled after the deposit limit is","crossed.","Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case."],"type":"u64"},{"name":"deleveragingThresholdDecreaseBpsPerDay","docs":["The rate at which the deleveraging threshold decreases, in bps per day.","Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case."],"type":"u64"},{"name":"fees","docs":["Program owner fees assessed, separate from gains due to interest accrual"],"type":{"defined":"ReserveFees"}},{"name":"borrowRateCurve","docs":["Borrow rate curve based on utilization"],"type":{"defined":"BorrowRateCurve"}},{"name":"borrowFactorPct","docs":["Borrow factor in percentage - used for risk adjustment"],"type":"u64"},{"name":"depositLimit","docs":["Maximum deposit limit of liquidity in native units, u64::MAX for inf"],"type":"u64"},{"name":"borrowLimit","docs":["Maximum amount borrowed, u64::MAX for inf, 0 to disable borrows (protected deposits)"],"type":"u64"},{"name":"tokenInfo","docs":["Token id from TokenInfos struct"],"type":{"defined":"TokenInfo"}},{"name":"depositWithdrawalCap","docs":["Deposit withdrawal caps - deposit & redeem"],"type":{"defined":"WithdrawalCaps"}},{"name":"debtWithdrawalCap","docs":["Debt withdrawal caps - borrow & repay"],"type":{"defined":"WithdrawalCaps"}},{"name":"elevationGroups","type":{"array":["u8",20]}},{"name":"disableUsageAsCollOutsideEmode","type":"u8"},{"name":"utilizationLimitBlockBorrowingAbovePct","docs":["Utilization (in percentage) above which borrowing is blocked. 0 to disable."],"type":"u8"},{"name":"autodeleverageEnabled","docs":["Whether this reserve should be subject to auto-deleveraging after deposit or borrow limit is","crossed.","Besides this flag, the lending market's flag also needs to be enabled (logical `AND`).","**NOTE:** the manual \"target LTV\" deleveraging (enabled by the risk council for individual","obligations) is NOT affected by this flag."],"type":"u8"},{"name":"reserved1","type":{"array":["u8",1]}},{"name":"borrowLimitOutsideElevationGroup","docs":["Maximum amount liquidity of this reserve borrowed outside all elevation groups","- u64::MAX for inf","- 0 to disable borrows outside elevation groups"],"type":"u64"},{"name":"borrowLimitAgainstThisCollateralInElevationGroup","docs":["Defines the maximum amount (in lamports of elevation group debt asset)","that can be borrowed when this reserve is used as collateral.","- u64::MAX for inf","- 0 to disable borrows in this elevation group (expected value for the debt asset)"],"type":{"array":["u64",32]}},{"name":"deleveragingBonusIncreaseBpsPerDay","docs":["The rate at which the deleveraging-related liquidation bonus increases, in bps per day.","Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case."],"type":"u64"}]}},{"name":"ReserveFees","docs":["Additional fee information on a reserve","","These exist separately from interest accrual fees, and are specifically for the program owner","and referral fee. The fees are paid out as a percentage of liquidity token amounts during","repayments and liquidations."],"type":{"kind":"struct","fields":[{"name":"borrowFeeSf","docs":["Fee assessed on `BorrowObligationLiquidity`, as scaled fraction (60 bits fractional part)","Must be between `0` and `2^60`, such that `2^60 = 1`. A few examples for","clarity:","1% = (1 << 60) / 100 = 11529215046068470","0.01% (1 basis point) = 115292150460685","0.00001% (Aave borrow fee) = 115292150461"],"type":"u64"},{"name":"flashLoanFeeSf","docs":["Fee for flash loan, expressed as scaled fraction.","0.3% (Aave flash loan fee) = 0.003 * 2^60 = 3458764513820541"],"type":"u64"},{"name":"padding","docs":["Used for allignment"],"type":{"array":["u8",8]}}]}},{"name":"ReserveLiquidity","docs":["Reserve liquidity"],"type":{"kind":"struct","fields":[{"name":"mintPubkey","docs":["Reserve liquidity mint address"],"type":"publicKey"},{"name":"supplyVault","docs":["Reserve liquidity supply address"],"type":"publicKey"},{"name":"feeVault","docs":["Reserve liquidity fee collection address"],"type":"publicKey"},{"name":"availableAmount","docs":["Reserve liquidity available"],"type":"u64"},{"name":"borrowedAmountSf","docs":["Reserve liquidity borrowed (scaled fraction)"],"type":"u128"},{"name":"marketPriceSf","docs":["Reserve liquidity market price in quote currency (scaled fraction)"],"type":"u128"},{"name":"marketPriceLastUpdatedTs","docs":["Unix timestamp of the market price (from the oracle)"],"type":"u64"},{"name":"mintDecimals","docs":["Reserve liquidity mint decimals"],"type":"u64"},{"name":"depositLimitCrossedTimestamp","docs":["Timestamp when the last refresh reserve detected that the liquidity amount is above the deposit cap. When this threshold is crossed, then redemptions (auto-deleverage) are enabled.","If the threshold is not crossed, then the timestamp is set to 0"],"type":"u64"},{"name":"borrowLimitCrossedTimestamp","docs":["Timestamp when the last refresh reserve detected that the borrowed amount is above the borrow cap. When this threshold is crossed, then redemptions (auto-deleverage) are enabled.","If the threshold is not crossed, then the timestamp is set to 0"],"type":"u64"},{"name":"cumulativeBorrowRateBsf","docs":["Reserve liquidity cumulative borrow rate (scaled fraction)"],"type":{"defined":"BigFractionBytes"}},{"name":"accumulatedProtocolFeesSf","docs":["Reserve cumulative protocol fees (scaled fraction)"],"type":"u128"},{"name":"accumulatedReferrerFeesSf","docs":["Reserve cumulative referrer fees (scaled fraction)"],"type":"u128"},{"name":"pendingReferrerFeesSf","docs":["Reserve pending referrer fees, to be claimed in refresh_obligation by referrer or protocol (scaled fraction)"],"type":"u128"},{"name":"absoluteReferralRateSf","docs":["Reserve referrer fee absolute rate calculated at each refresh_reserve operation (scaled fraction)"],"type":"u128"},{"name":"tokenProgram","docs":["Token program of the liquidity mint"],"type":"publicKey"},{"name":"padding2","type":{"array":["u64",51]}},{"name":"padding3","type":{"array":["u128",32]}}]}},{"name":"WithdrawalCaps","docs":["Reserve Withdrawal Caps State"],"type":{"kind":"struct","fields":[{"name":"configCapacity","type":"i64"},{"name":"currentTotal","type":"i64"},{"name":"lastIntervalStartTimestamp","type":"u64"},{"name":"configIntervalLengthSeconds","type":"u64"}]}},{"name":"PriceHeuristic","type":{"kind":"struct","fields":[{"name":"lower","docs":["Lower value of acceptable price"],"type":"u64"},{"name":"upper","docs":["Upper value of acceptable price"],"type":"u64"},{"name":"exp","docs":["Number of decimals of the previously defined values"],"type":"u64"}]}},{"name":"PythConfiguration","type":{"kind":"struct","fields":[{"name":"price","docs":["Pubkey of the base price feed (disabled if `null` or `default`)"],"type":"publicKey"}]}},{"name":"ScopeConfiguration","type":{"kind":"struct","fields":[{"name":"priceFeed","docs":["Pubkey of the scope price feed (disabled if `null` or `default`)"],"type":"publicKey"},{"name":"priceChain","docs":["This is the scope_id price chain that results in a price for the token"],"type":{"array":["u16",4]}},{"name":"twapChain","docs":["This is the scope_id price chain for the twap"],"type":{"array":["u16",4]}}]}},{"name":"SwitchboardConfiguration","type":{"kind":"struct","fields":[{"name":"priceAggregator","docs":["Pubkey of the base price feed (disabled if `null` or `default`)"],"type":"publicKey"},{"name":"twapAggregator","type":"publicKey"}]}},{"name":"TokenInfo","type":{"kind":"struct","fields":[{"name":"name","docs":["UTF-8 encoded name of the token (null-terminated)"],"type":{"array":["u8",32]}},{"name":"heuristic","docs":["Heuristics limits of acceptable price"],"type":{"defined":"PriceHeuristic"}},{"name":"maxTwapDivergenceBps","docs":["Max divergence between twap and price in bps"],"type":"u64"},{"name":"maxAgePriceSeconds","type":"u64"},{"name":"maxAgeTwapSeconds","type":"u64"},{"name":"scopeConfiguration","docs":["Scope price configuration"],"type":{"defined":"ScopeConfiguration"}},{"name":"switchboardConfiguration","docs":["Switchboard configuration"],"type":{"defined":"SwitchboardConfiguration"}},{"name":"pythConfiguration","docs":["Pyth configuration"],"type":{"defined":"PythConfiguration"}},{"name":"blockPriceUsage","type":"u8"},{"name":"reserved","type":{"array":["u8",7]}},{"name":"padding","type":{"array":["u64",19]}}]}},{"name":"BorrowRateCurve","type":{"kind":"struct","fields":[{"name":"points","type":{"array":[{"defined":"CurvePoint"},11]}}]}},{"name":"CurvePoint","type":{"kind":"struct","fields":[{"name":"utilizationRateBps","type":"u32"},{"name":"borrowRateBps","type":"u32"}]}},{"name":"VaultConfigField","type":{"kind":"enum","variants":[{"name":"PerformanceFeeBps"},{"name":"ManagementFeeBps"},{"name":"MinDepositAmount"},{"name":"MinWithdrawAmount"},{"name":"MinInvestAmount"},{"name":"MinInvestDelaySlots"},{"name":"CrankFundFeePerReserve"},{"name":"PendingVaultAdmin"},{"name":"Name"},{"name":"LookupTable"},{"name":"Farm"},{"name":"AllocationAdmin"}]}},{"name":"VaultAllocation","type":{"kind":"struct","fields":[{"name":"reserve","type":"publicKey"},{"name":"ctokenVault","type":"publicKey"},{"name":"targetAllocationWeight","type":"u64"},{"name":"tokenAllocationCap","docs":["Maximum token invested in this reserve"],"type":"u64"},{"name":"ctokenVaultBump","type":"u64"},{"name":"configPadding","type":{"array":["u64",127]}},{"name":"ctokenAllocation","type":"u64"},{"name":"lastInvestSlot","type":"u64"},{"name":"tokenTargetAllocationSf","type":"u128"},{"name":"statePadding","type":{"array":["u64",128]}}]}}],"errors":[{"code":7000,"name":"DepositAmountsZero","msg":"DepositAmountsZero"},{"code":7001,"name":"SharesIssuedAmountDoesNotMatch","msg":"SharesIssuedAmountDoesNotMatch"},{"code":7002,"name":"MathOverflow","msg":"MathOverflow"},{"code":7003,"name":"IntegerOverflow","msg":"IntegerOverflow"},{"code":7004,"name":"WithdrawAmountBelowMinimum","msg":"Withdrawn amount is below minimum"},{"code":7005,"name":"TooMuchLiquidityToWithdraw","msg":"TooMuchLiquidityToWithdraw"},{"code":7006,"name":"ReserveAlreadyExists","msg":"ReserveAlreadyExists"},{"code":7007,"name":"ReserveNotPartOfAllocations","msg":"ReserveNotPartOfAllocations"},{"code":7008,"name":"CouldNotDeserializeAccountAsReserve","msg":"CouldNotDeserializeAccountAsReserve"},{"code":7009,"name":"ReserveNotProvidedInTheAccounts","msg":"ReserveNotProvidedInTheAccounts"},{"code":7010,"name":"ReserveAccountAndKeyMismatch","msg":"ReserveAccountAndKeyMismatch"},{"code":7011,"name":"OutOfRangeOfReserveIndex","msg":"OutOfRangeOfReserveIndex"},{"code":7012,"name":"CannotFindReserveInAllocations","msg":"OutOfRangeOfReserveIndex"},{"code":7013,"name":"InvestAmountBelowMinimum","msg":"Invested amount is below minimum"},{"code":7014,"name":"AdminAuthorityIncorrect","msg":"AdminAuthorityIncorrect"},{"code":7015,"name":"BaseVaultAuthorityIncorrect","msg":"BaseVaultAuthorityIncorrect"},{"code":7016,"name":"BaseVaultAuthorityBumpIncorrect","msg":"BaseVaultAuthorityBumpIncorrect"},{"code":7017,"name":"TokenMintIncorrect","msg":"TokenMintIncorrect"},{"code":7018,"name":"TokenMintDecimalsIncorrect","msg":"TokenMintDecimalsIncorrect"},{"code":7019,"name":"TokenVaultIncorrect","msg":"TokenVaultIncorrect"},{"code":7020,"name":"SharesMintDecimalsIncorrect","msg":"SharesMintDecimalsIncorrect"},{"code":7021,"name":"SharesMintIncorrect","msg":"SharesMintIncorrect"},{"code":7022,"name":"InitialAccountingIncorrect","msg":"InitialAccountingIncorrect"},{"code":7023,"name":"ReserveIsStale","msg":"Reserve is stale and must be refreshed before any operation"},{"code":7024,"name":"NotEnoughLiquidityDisinvestedToSendToUser","msg":"Not enough liquidity disinvested to send to user"},{"code":7025,"name":"BPSValueTooBig","msg":"BPS value is greater than 10000"},{"code":7026,"name":"DepositAmountBelowMinimum","msg":"Deposited amount is below minimum"},{"code":7027,"name":"ReserveSpaceExhausted","msg":"Vault have no space for new reserves"},{"code":7028,"name":"CannotWithdrawFromEmptyVault","msg":"Cannot withdraw from empty vault"},{"code":7029,"name":"TokensDepositedAmountDoesNotMatch","msg":"TokensDepositedAmountDoesNotMatch"},{"code":7030,"name":"AmountToWithdrawDoesNotMatch","msg":"Amount to withdraw does not match"},{"code":7031,"name":"LiquidityToWithdrawDoesNotMatch","msg":"Liquidity to withdraw does not match"},{"code":7032,"name":"UserReceivedAmountDoesNotMatch","msg":"User received amount does not match"},{"code":7033,"name":"SharesBurnedAmountDoesNotMatch","msg":"Shares burned amount does not match"},{"code":7034,"name":"DisinvestedLiquidityAmountDoesNotMatch","msg":"Disinvested liquidity amount does not match"},{"code":7035,"name":"SharesMintedAmountDoesNotMatch","msg":"SharesMintedAmountDoesNotMatch"},{"code":7036,"name":"AUMDecreasedAfterInvest","msg":"AUM decreased after invest"},{"code":7037,"name":"AUMBelowPendingFees","msg":"AUM is below pending fees"},{"code":7038,"name":"DepositAmountsZeroShares","msg":"Deposit amount results in 0 shares"},{"code":7039,"name":"WithdrawResultsInZeroShares","msg":"Withdraw amount results in 0 shares"},{"code":7040,"name":"CannotWithdrawZeroShares","msg":"Cannot withdraw zero shares"},{"code":7041,"name":"ManagementFeeGreaterThanMaxAllowed","msg":"Management fee is greater than maximum allowed"},{"code":7042,"name":"VaultAUMZero","msg":"Vault assets under management are empty"},{"code":7043,"name":"MissingReserveForBatchRefresh","msg":"Missing reserve for batch refresh"},{"code":7044,"name":"MinWithdrawAmountTooBig","msg":"Min withdraw amount is too big"},{"code":7045,"name":"InvestTooSoon","msg":"Invest is called too soon after last invest"},{"code":7046,"name":"WrongAdminOrAllocationAdmin","msg":"Wrong admin or allocation admin"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/kamino_vault_v2.KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd.json b/migration/khadhroony-bot2-reference/idls/kamino_vault_v2.KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd.json new file mode 100644 index 0000000..b4bf03e --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/kamino_vault_v2.KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd.json @@ -0,0 +1 @@ +{"version":"2.1.1","name":"kamino_vault","instructions":[{"name":"initVault","accounts":[{"name":"adminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"baseTokenMint","isMut":false,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"adminTokenAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"updateReserveAllocation","accounts":[{"name":"signer","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserve","isMut":false,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"reserveWhitelistEntry","isMut":false,"isSigner":false,"isOptional":true},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"weight","type":"u64"},{"name":"cap","type":"u64"}]},{"name":"deposit","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"userTokenAta","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"maxAmount","type":"u64"}]},{"name":"buy","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"userTokenAta","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"maxAmount","type":"u64"}]},{"name":"withdraw","accounts":[{"name":"withdrawFromAvailable","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"userTokenAta","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}]},{"name":"withdrawFromReserveAccounts","accounts":[{"name":"vaultState","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"sharesAmount","type":"u64"}]},{"name":"sell","accounts":[{"name":"withdrawFromAvailable","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"userTokenAta","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}]},{"name":"withdrawFromReserveAccounts","accounts":[{"name":"vaultState","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}]},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"sharesAmount","type":"u64"}]},{"name":"invest","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"payerTokenAccount","isMut":true,"isSigner":false},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false,"docs":["CPI accounts"]},{"name":"lendingMarket","isMut":false,"isSigner":false},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"reserveWhitelistEntry","isMut":false,"isSigner":false,"isOptional":true},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[]},{"name":"updateVaultConfig","accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false}],"args":[{"name":"entry","type":{"defined":"VaultConfigField"}},{"name":"data","type":"bytes"}]},{"name":"withdrawPendingFees","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":true,"isSigner":false},{"name":"tokenAta","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"lendingMarket","isMut":false,"isSigner":false,"docs":["CPI accounts"]},{"name":"lendingMarketAuthority","isMut":false,"isSigner":false},{"name":"reserveLiquiditySupply","isMut":true,"isSigner":false},{"name":"reserveCollateralMint","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"instructionSysvarAccount","isMut":false,"isSigner":false}],"args":[]},{"name":"updateAdmin","accounts":[{"name":"pendingAdmin","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false}],"args":[]},{"name":"giveUpPendingFees","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxAmountToGiveUp","type":"u64"}]},{"name":"initializeSharesMetadata","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":false,"isSigner":false},{"name":"sharesMint","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"sharesMetadata","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"updateSharesMetadata","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"sharesMetadata","isMut":true,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"withdrawFromAvailable","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"userTokenAta","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":true,"isSigner":false},{"name":"userSharesAta","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"sharesAmount","type":"u64"}]},{"name":"removeAllocation","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"reserve","isMut":false,"isSigner":false}],"args":[]},{"name":"initGlobalConfig","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false},{"name":"programData","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"updateGlobalConfig","accounts":[{"name":"globalAdmin","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false}],"args":[{"name":"update","type":{"defined":"UpdateGlobalConfigMode"}}]},{"name":"updateGlobalConfigAdmin","accounts":[{"name":"pendingAdmin","isMut":false,"isSigner":true},{"name":"globalConfig","isMut":true,"isSigner":false}],"args":[]},{"name":"addUpdateWhitelistedReserve","accounts":[{"name":"globalAdmin","isMut":true,"isSigner":true},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"reserve","isMut":false,"isSigner":false},{"name":"reserveWhitelistEntry","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"update","type":{"defined":"UpdateReserveWhitelistMode"}}]},{"name":"topupRewards","accounts":[{"name":"payer","isMut":false,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"payerTokenTa","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdrawRewards","accounts":[{"name":"vaultAdminAuthority","isMut":true,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"withdrawTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"redeemInKind","accounts":[{"name":"user","isMut":false,"isSigner":true},{"name":"vaultState","isMut":true,"isSigner":false},{"name":"globalConfig","isMut":false,"isSigner":false},{"name":"baseVaultAuthority","isMut":false,"isSigner":false},{"name":"reserve","isMut":true,"isSigner":false},{"name":"ctokenVault","isMut":true,"isSigner":false},{"name":"userCtokenTa","isMut":true,"isSigner":false},{"name":"ctokenMint","isMut":true,"isSigner":false},{"name":"userSharesTa","isMut":true,"isSigner":false},{"name":"sharesMint","isMut":true,"isSigner":false},{"name":"reserveCollateralTokenProgram","isMut":false,"isSigner":false},{"name":"sharesTokenProgram","isMut":false,"isSigner":false},{"name":"klendProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"sharesAmount","type":"u64"}]}],"accounts":[{"name":"Reserve","type":{"kind":"struct","fields":[{"name":"version","docs":["Version of the reserve"],"type":"u64"},{"name":"lastUpdate","docs":["Last slot when supply and rates updated"],"type":{"defined":"LastUpdate"}},{"name":"lendingMarket","docs":["Lending market address"],"type":"publicKey"},{"name":"farmCollateral","type":"publicKey"},{"name":"farmDebt","type":"publicKey"},{"name":"liquidity","docs":["Reserve liquidity"],"type":{"defined":"ReserveLiquidity"}},{"name":"reserveLiquidityPadding","type":{"array":["u64",150]}},{"name":"collateral","docs":["Reserve collateral"],"type":{"defined":"ReserveCollateral"}},{"name":"reserveCollateralPadding","type":{"array":["u64",150]}},{"name":"config","docs":["Reserve configuration values"],"type":{"defined":"ReserveConfig"}},{"name":"configPadding","type":{"array":["u64",116]}},{"name":"borrowedAmountOutsideElevationGroup","type":"u64"},{"name":"borrowedAmountsAgainstThisReserveInElevationGroups","docs":["Amount of token borrowed in lamport of debt asset in the given","elevation group when this reserve is part of the collaterals."],"type":{"array":["u64",32]}},{"name":"padding","type":{"array":["u64",207]}}]}},{"name":"GlobalConfig","type":{"kind":"struct","fields":[{"name":"globalAdmin","type":"publicKey"},{"name":"pendingAdmin","type":"publicKey"},{"name":"withdrawalPenaltyLamports","type":"u64"},{"name":"withdrawalPenaltyBps","type":"u64"},{"name":"padding","type":{"array":["u8",944]}}]}},{"name":"ReserveWhitelistEntry","type":{"kind":"struct","fields":[{"name":"tokenMint","docs":["The token mint is stored to solve the problem of finding all the whitelisted reserves for a particular token mint:","when storing the token mint inside the PDA, finding all the whitelisted reserves becomes a `getProgramAccounts` with","a filter on discriminator + the mint field","The reserve pubkey, as seed of the reserve whitelist PDA account, it stored so you can link back the PDA to its seeds","(for instance, in the operation above we easily find the reserve corresponding to the PDA)"],"type":"publicKey"},{"name":"reserve","type":"publicKey"},{"name":"whitelistAddAllocation","type":"u8"},{"name":"whitelistInvest","type":"u8"},{"name":"padding","type":{"array":["u8",62]}}]}},{"name":"VaultState","type":{"kind":"struct","fields":[{"name":"vaultAdminAuthority","type":"publicKey"},{"name":"baseVaultAuthority","type":"publicKey"},{"name":"baseVaultAuthorityBump","type":"u64"},{"name":"tokenMint","type":"publicKey"},{"name":"tokenMintDecimals","type":"u64"},{"name":"tokenVault","type":"publicKey"},{"name":"tokenProgram","type":"publicKey"},{"name":"sharesMint","type":"publicKey"},{"name":"sharesMintDecimals","type":"u64"},{"name":"tokenAvailable","type":"u64"},{"name":"sharesIssued","type":"u64"},{"name":"availableCrankFunds","type":"u64"},{"name":"unallocatedWeight","type":"u64"},{"name":"performanceFeeBps","type":"u64"},{"name":"managementFeeBps","type":"u64"},{"name":"lastFeeChargeTimestamp","type":"u64"},{"name":"prevAumSf","type":"u128"},{"name":"pendingFeesSf","type":"u128"},{"name":"vaultAllocationStrategy","type":{"array":[{"defined":"VaultAllocation"},25]}},{"name":"padding1","type":{"array":["u128",256]}},{"name":"minDepositAmount","type":"u64"},{"name":"minWithdrawAmount","type":"u64"},{"name":"minInvestAmount","type":"u64"},{"name":"minInvestDelaySlots","type":"u64"},{"name":"crankFundFeePerReserve","type":"u64"},{"name":"pendingAdmin","type":"publicKey"},{"name":"cumulativeEarnedInterestSf","type":"u128"},{"name":"cumulativeMgmtFeesSf","type":"u128"},{"name":"cumulativePerfFeesSf","type":"u128"},{"name":"name","type":{"array":["u8",40]}},{"name":"vaultLookupTable","type":"publicKey"},{"name":"vaultFarm","type":"publicKey"},{"name":"creationTimestamp","type":"u64"},{"name":"unallocatedTokensCap","type":"u64"},{"name":"allocationAdmin","type":"publicKey"},{"name":"withdrawalPenaltyLamports","type":"u64"},{"name":"withdrawalPenaltyBps","type":"u64"},{"name":"firstLossCapitalFarm","type":"publicKey"},{"name":"allowAllocationsInWhitelistedReservesOnly","type":"u8"},{"name":"allowInvestInWhitelistedReservesOnly","type":"u8"},{"name":"padding2","type":{"array":["u8",14]}},{"name":"rewardInfo","type":{"defined":"VaultRewardInfo"}},{"name":"padding3","type":{"array":["u128",232]}}]}}],"types":[{"name":"LastUpdate","docs":["Last update state"],"type":{"kind":"struct","fields":[{"name":"slot","docs":["Last slot when updated"],"type":"u64"},{"name":"stale","docs":["True when marked stale, false when slot updated"],"type":"u8"},{"name":"priceStatus","docs":["Status of the prices used to calculate the last update"],"type":"u8"},{"name":"placeholder","type":{"array":["u8",6]}}]}},{"name":"BigFractionBytes","type":{"kind":"struct","fields":[{"name":"value","type":{"array":["u64",4]}},{"name":"padding","type":{"array":["u64",2]}}]}},{"name":"ReserveCollateral","docs":["Reserve collateral"],"type":{"kind":"struct","fields":[{"name":"mintPubkey","docs":["Reserve collateral mint address"],"type":"publicKey"},{"name":"mintTotalSupply","docs":["Reserve collateral mint supply, used for exchange rate"],"type":"u64"},{"name":"supplyVault","docs":["Reserve collateral supply address"],"type":"publicKey"},{"name":"padding1","type":{"array":["u128",32]}},{"name":"padding2","type":{"array":["u128",32]}}]}},{"name":"ReserveConfig","docs":["Reserve configuration values"],"type":{"kind":"struct","fields":[{"name":"status","docs":["Status of the reserve Active/Obsolete/Hidden"],"type":"u8"},{"name":"assetTier","docs":["Asset tier -> 0 - regular (collateral & debt), 1 - isolated collateral, 2 - isolated debt"],"type":"u8"},{"name":"hostFixedInterestRateBps","docs":["Flat rate that goes to the host"],"type":"u16"},{"name":"minDeleveragingBonusBps","docs":["Starting bonus for deleveraging-related liquidations, in bps."],"type":"u16"},{"name":"blockCtokenUsage","docs":["Boolean flag to block minting/redeeming of ctokens","Blocks usage of ctokens (minting or withdrawing from obligation)","Effectively blocks deposit_reserve_liquidity and withdraw_obligation_collateral"],"type":"u8"},{"name":"reserved1","docs":["Past reserved space - feel free to reuse."],"type":{"array":["u8",6]}},{"name":"protocolOrderExecutionFeePct","docs":["Cut of the order execution bonus that the protocol receives, as a percentage"],"type":"u8"},{"name":"protocolTakeRatePct","docs":["Protocol take rate is the amount borrowed interest protocol receives, as a percentage"],"type":"u8"},{"name":"protocolLiquidationFeePct","docs":["Cut of the liquidation bonus that the protocol receives, as a percentage"],"type":"u8"},{"name":"loanToValuePct","docs":["Target ratio of the value of borrows to deposits, as a percentage","0 if use as collateral is disabled"],"type":"u8"},{"name":"liquidationThresholdPct","docs":["Loan to value ratio at which an obligation can be liquidated, as percentage"],"type":"u8"},{"name":"minLiquidationBonusBps","docs":["Minimum bonus a liquidator receives when repaying part of an unhealthy obligation, as bps"],"type":"u16"},{"name":"maxLiquidationBonusBps","docs":["Maximum bonus a liquidator receives when repaying part of an unhealthy obligation, as bps"],"type":"u16"},{"name":"badDebtLiquidationBonusBps","docs":["Bad debt liquidation bonus for an undercollateralized obligation, as bps"],"type":"u16"},{"name":"deleveragingMarginCallPeriodSecs","docs":["Time in seconds that must pass before redemptions are enabled after the deposit limit is","crossed.","Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case."],"type":"u64"},{"name":"deleveragingThresholdDecreaseBpsPerDay","docs":["The rate at which the deleveraging threshold decreases, in bps per day.","Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case."],"type":"u64"},{"name":"fees","docs":["Program owner fees assessed, separate from gains due to interest accrual"],"type":{"defined":"ReserveFees"}},{"name":"borrowRateCurve","docs":["Borrow rate curve based on utilization"],"type":{"defined":"BorrowRateCurve"}},{"name":"borrowFactorPct","docs":["Borrow factor in percentage - used for risk adjustment"],"type":"u64"},{"name":"depositLimit","docs":["Maximum deposit limit of liquidity in native units, u64::MAX for inf"],"type":"u64"},{"name":"borrowLimit","docs":["Maximum amount borrowed, u64::MAX for inf, 0 to disable borrows (protected deposits)"],"type":"u64"},{"name":"tokenInfo","docs":["Token id from TokenInfos struct"],"type":{"defined":"TokenInfo"}},{"name":"depositWithdrawalCap","docs":["Deposit withdrawal caps - deposit & redeem"],"type":{"defined":"WithdrawalCaps"}},{"name":"debtWithdrawalCap","docs":["Debt withdrawal caps - borrow & repay"],"type":{"defined":"WithdrawalCaps"}},{"name":"elevationGroups","type":{"array":["u8",20]}},{"name":"disableUsageAsCollOutsideEmode","type":"u8"},{"name":"utilizationLimitBlockBorrowingAbovePct","docs":["Utilization (in percentage) above which borrowing is blocked. 0 to disable."],"type":"u8"},{"name":"autodeleverageEnabled","docs":["Whether this reserve should be subject to auto-deleveraging after deposit or borrow limit is","crossed.","Besides this flag, the lending market's flag also needs to be enabled (logical `AND`).","**NOTE:** the manual \"target LTV\" deleveraging (enabled by the risk council for individual","obligations) is NOT affected by this flag."],"type":"u8"},{"name":"proposerAuthorityLocked","docs":["Boolean flag indicating whether the reserve is locked for the proposer authority.","","Once the proposer have finished preparing the reserve, it must be locked to prevent","further changes to the reserve configuration allowing review and voting on the proposal","without alteration during the voting period."],"type":"u8"},{"name":"borrowLimitOutsideElevationGroup","docs":["Maximum amount liquidity of this reserve borrowed outside all elevation groups","- u64::MAX for inf","- 0 to disable borrows outside elevation groups"],"type":"u64"},{"name":"borrowLimitAgainstThisCollateralInElevationGroup","docs":["Defines the maximum amount (in lamports of elevation group debt asset)","that can be borrowed when this reserve is used as collateral.","- u64::MAX for inf","- 0 to disable borrows in this elevation group (expected value for the debt asset)"],"type":{"array":["u64",32]}},{"name":"deleveragingBonusIncreaseBpsPerDay","docs":["The rate at which the deleveraging-related liquidation bonus increases, in bps per day.","Only relevant when `autodeleverage_enabled == 1`, and must not be 0 in such case."],"type":"u64"}]}},{"name":"ReserveFees","docs":["Additional fee information on a reserve","","These exist separately from interest accrual fees, and are specifically for the program owner","and referral fee. The fees are paid out as a percentage of liquidity token amounts during","repayments and liquidations."],"type":{"kind":"struct","fields":[{"name":"originationFeeSf","docs":["Fee assessed on `BorrowObligationLiquidity`, as scaled fraction (60 bits fractional part)","Must be between `0` and `2^60`, such that `2^60 = 1`. A few examples for","clarity:","1% = (1 << 60) / 100 = 11529215046068470","0.01% (1 basis point) = 115292150460685","0.00001% (Aave origination fee) = 115292150461"],"type":"u64"},{"name":"flashLoanFeeSf","docs":["Fee for flash loan, expressed as scaled fraction.","0.3% (Aave flash loan fee) = 0.003 * 2^60 = 3458764513820541"],"type":"u64"},{"name":"padding","docs":["Used for allignment"],"type":{"array":["u8",8]}}]}},{"name":"ReserveLiquidity","docs":["Reserve liquidity"],"type":{"kind":"struct","fields":[{"name":"mintPubkey","docs":["Reserve liquidity mint address"],"type":"publicKey"},{"name":"supplyVault","docs":["Reserve liquidity supply address"],"type":"publicKey"},{"name":"feeVault","docs":["Reserve liquidity fee collection address"],"type":"publicKey"},{"name":"availableAmount","docs":["Reserve liquidity available"],"type":"u64"},{"name":"borrowedAmountSf","docs":["Reserve liquidity borrowed (scaled fraction)"],"type":"u128"},{"name":"marketPriceSf","docs":["Reserve liquidity market price in quote currency (scaled fraction)"],"type":"u128"},{"name":"marketPriceLastUpdatedTs","docs":["Unix timestamp of the market price (from the oracle)"],"type":"u64"},{"name":"mintDecimals","docs":["Reserve liquidity mint decimals"],"type":"u64"},{"name":"depositLimitCrossedTimestamp","docs":["Timestamp when the last refresh reserve detected that the liquidity amount is above the deposit cap. When this threshold is crossed, then redemptions (auto-deleverage) are enabled.","If the threshold is not crossed, then the timestamp is set to 0"],"type":"u64"},{"name":"borrowLimitCrossedTimestamp","docs":["Timestamp when the last refresh reserve detected that the borrowed amount is above the borrow cap. When this threshold is crossed, then redemptions (auto-deleverage) are enabled.","If the threshold is not crossed, then the timestamp is set to 0"],"type":"u64"},{"name":"cumulativeBorrowRateBsf","docs":["Reserve liquidity cumulative borrow rate (scaled fraction)"],"type":{"defined":"BigFractionBytes"}},{"name":"accumulatedProtocolFeesSf","docs":["Reserve cumulative protocol fees (scaled fraction)"],"type":"u128"},{"name":"accumulatedReferrerFeesSf","docs":["Reserve cumulative referrer fees (scaled fraction)"],"type":"u128"},{"name":"pendingReferrerFeesSf","docs":["Reserve pending referrer fees, to be claimed in refresh_obligation by referrer or protocol (scaled fraction)"],"type":"u128"},{"name":"absoluteReferralRateSf","docs":["Reserve referrer fee absolute rate calculated at each refresh_reserve operation (scaled fraction)"],"type":"u128"},{"name":"tokenProgram","docs":["Token program of the liquidity mint"],"type":"publicKey"},{"name":"padding2","type":{"array":["u64",51]}},{"name":"padding3","type":{"array":["u128",32]}}]}},{"name":"WithdrawalCaps","docs":["Reserve Withdrawal Caps State"],"type":{"kind":"struct","fields":[{"name":"configCapacity","type":"i64"},{"name":"currentTotal","type":"i64"},{"name":"lastIntervalStartTimestamp","type":"u64"},{"name":"configIntervalLengthSeconds","type":"u64"}]}},{"name":"PriceHeuristic","type":{"kind":"struct","fields":[{"name":"lower","docs":["Lower value of acceptable price"],"type":"u64"},{"name":"upper","docs":["Upper value of acceptable price"],"type":"u64"},{"name":"exp","docs":["Number of decimals of the previously defined values"],"type":"u64"}]}},{"name":"PythConfiguration","type":{"kind":"struct","fields":[{"name":"price","docs":["Pubkey of the base price feed (disabled if `null` or `default`)"],"type":"publicKey"}]}},{"name":"ScopeConfiguration","type":{"kind":"struct","fields":[{"name":"priceFeed","docs":["Pubkey of the scope price feed (disabled if `null` or `default`)"],"type":"publicKey"},{"name":"priceChain","docs":["This is the scope_id price chain that results in a price for the token"],"type":{"array":["u16",4]}},{"name":"twapChain","docs":["This is the scope_id price chain for the twap"],"type":{"array":["u16",4]}}]}},{"name":"SwitchboardConfiguration","type":{"kind":"struct","fields":[{"name":"priceAggregator","docs":["Pubkey of the base price feed (disabled if `null` or `default`)"],"type":"publicKey"},{"name":"twapAggregator","type":"publicKey"}]}},{"name":"TokenInfo","type":{"kind":"struct","fields":[{"name":"name","docs":["UTF-8 encoded name of the token (null-terminated)"],"type":{"array":["u8",32]}},{"name":"heuristic","docs":["Heuristics limits of acceptable price"],"type":{"defined":"PriceHeuristic"}},{"name":"maxTwapDivergenceBps","docs":["Max divergence between twap and price in bps"],"type":"u64"},{"name":"maxAgePriceSeconds","type":"u64"},{"name":"maxAgeTwapSeconds","type":"u64"},{"name":"scopeConfiguration","docs":["Scope price configuration"],"type":{"defined":"ScopeConfiguration"}},{"name":"switchboardConfiguration","docs":["Switchboard configuration"],"type":{"defined":"SwitchboardConfiguration"}},{"name":"pythConfiguration","docs":["Pyth configuration"],"type":{"defined":"PythConfiguration"}},{"name":"blockPriceUsage","type":"u8"},{"name":"reserved","type":{"array":["u8",7]}},{"name":"padding","type":{"array":["u64",19]}}]}},{"name":"BorrowRateCurve","type":{"kind":"struct","fields":[{"name":"points","type":{"array":[{"defined":"CurvePoint"},11]}}]}},{"name":"CurvePoint","type":{"kind":"struct","fields":[{"name":"utilizationRateBps","type":"u32"},{"name":"borrowRateBps","type":"u32"}]}},{"name":"UpdateReserveWhitelistMode","type":{"kind":"enum","variants":[{"name":"Invest","fields":["u8"]},{"name":"AddAllocation","fields":["u8"]}]}},{"name":"VaultConfigField","type":{"kind":"enum","variants":[{"name":"PerformanceFeeBps"},{"name":"ManagementFeeBps"},{"name":"MinDepositAmount"},{"name":"MinWithdrawAmount"},{"name":"MinInvestAmount"},{"name":"MinInvestDelaySlots"},{"name":"CrankFundFeePerReserve"},{"name":"PendingVaultAdmin"},{"name":"Name"},{"name":"LookupTable"},{"name":"Farm"},{"name":"AllocationAdmin"},{"name":"UnallocatedWeight"},{"name":"UnallocatedTokensCap"},{"name":"WithdrawalPenaltyLamports"},{"name":"WithdrawalPenaltyBps"},{"name":"FirstLossCapitalFarm"},{"name":"AllowAllocationsInWhitelistedReservesOnly"},{"name":"AllowInvestInWhitelistedReservesOnly"},{"name":"RewardPerSecond"}]}},{"name":"VaultAllocation","type":{"kind":"struct","fields":[{"name":"reserve","type":"publicKey"},{"name":"ctokenVault","type":"publicKey"},{"name":"targetAllocationWeight","type":"u64"},{"name":"tokenAllocationCap","docs":["Maximum token invested in this reserve"],"type":"u64"},{"name":"ctokenVaultBump","type":"u64"},{"name":"configPadding","type":{"array":["u64",127]}},{"name":"ctokenAllocation","type":"u64"},{"name":"lastInvestSlot","type":"u64"},{"name":"tokenTargetAllocationSf","type":"u128"},{"name":"statePadding","type":{"array":["u64",128]}}]}},{"name":"VaultRewardInfo","type":{"kind":"struct","fields":[{"name":"rewardPerSecond","type":"u64"},{"name":"lastIssuanceTs","type":"u64"},{"name":"rewardsAvailable","docs":["Rewards available to distribute (topped up but not yet moved to vault.token_available)"],"type":"u64"},{"name":"cumulativeRewardsDistributedAnalytics","type":"u64"},{"name":"padding","type":{"array":["u64",8]}}]}},{"name":"UpdateGlobalConfigMode","type":{"kind":"enum","variants":[{"name":"PendingAdmin","fields":["publicKey"]},{"name":"MinWithdrawalPenaltyLamports","fields":["u64"]},{"name":"MinWithdrawalPenaltyBPS","fields":["u64"]}]}}],"events":[{"name":"DepositResultEvent","fields":[{"name":"sharesToMint","type":"u64","index":false},{"name":"tokenToDeposit","type":"u64","index":false},{"name":"crankFundsToDeposit","type":"u64","index":false}]},{"name":"DepositUserAtaBalanceEvent","fields":[{"name":"userAtaBalance","type":"u64","index":false}]},{"name":"RedeemInKindResultEvent","fields":[{"name":"sharesToBurn","type":"u64","index":false},{"name":"ctokensToSendToUser","type":"u64","index":false}]},{"name":"SharesToWithdrawEvent","fields":[{"name":"sharesAmount","type":"u64","index":false},{"name":"userSharesBefore","type":"u64","index":false}]},{"name":"WithdrawResultEvent","fields":[{"name":"sharesToBurn","type":"u64","index":false},{"name":"availableToSendToUser","type":"u64","index":false},{"name":"investedToDisinvestCtokens","type":"u64","index":false},{"name":"investedLiquidityToSendToUser","type":"u64","index":false}]}],"errors":[{"code":7000,"name":"DepositAmountsZero","msg":"Cannot deposit zero tokens"},{"code":7001,"name":"SharesIssuedAmountDoesNotMatch","msg":"Post check failed on share issued"},{"code":7002,"name":"MathOverflow","msg":"Math operation overflowed"},{"code":7003,"name":"IntegerOverflow","msg":"Integer conversion overflowed"},{"code":7004,"name":"WithdrawAmountBelowMinimum","msg":"Withdrawn amount is below minimum"},{"code":7005,"name":"TooMuchLiquidityToWithdraw","msg":"TooMuchLiquidityToWithdraw"},{"code":7006,"name":"ReserveAlreadyExists","msg":"ReserveAlreadyExists"},{"code":7007,"name":"ReserveNotPartOfAllocations","msg":"ReserveNotPartOfAllocations"},{"code":7008,"name":"CouldNotDeserializeAccountAsReserve","msg":"CouldNotDeserializeAccountAsReserve"},{"code":7009,"name":"ReserveNotProvidedInTheAccounts","msg":"ReserveNotProvidedInTheAccounts"},{"code":7010,"name":"ReserveAccountAndKeyMismatch","msg":"ReserveAccountAndKeyMismatch"},{"code":7011,"name":"OutOfRangeOfReserveIndex","msg":"OutOfRangeOfReserveIndex"},{"code":7012,"name":"CannotFindReserveInAllocations","msg":"OutOfRangeOfReserveIndex"},{"code":7013,"name":"InvestAmountBelowMinimum","msg":"Invested amount is below minimum"},{"code":7014,"name":"AdminAuthorityIncorrect","msg":"AdminAuthorityIncorrect"},{"code":7015,"name":"BaseVaultAuthorityIncorrect","msg":"BaseVaultAuthorityIncorrect"},{"code":7016,"name":"BaseVaultAuthorityBumpIncorrect","msg":"BaseVaultAuthorityBumpIncorrect"},{"code":7017,"name":"TokenMintIncorrect","msg":"TokenMintIncorrect"},{"code":7018,"name":"TokenMintDecimalsIncorrect","msg":"TokenMintDecimalsIncorrect"},{"code":7019,"name":"TokenVaultIncorrect","msg":"TokenVaultIncorrect"},{"code":7020,"name":"SharesMintDecimalsIncorrect","msg":"SharesMintDecimalsIncorrect"},{"code":7021,"name":"SharesMintIncorrect","msg":"SharesMintIncorrect"},{"code":7022,"name":"InitialAccountingIncorrect","msg":"InitialAccountingIncorrect"},{"code":7023,"name":"ReserveIsStale","msg":"Reserve is stale and must be refreshed before any operation"},{"code":7024,"name":"NotEnoughLiquidityDisinvestedToSendToUser","msg":"Not enough liquidity disinvested to send to user"},{"code":7025,"name":"BPSValueTooBig","msg":"BPS value is greater than 10000"},{"code":7026,"name":"DepositAmountBelowMinimum","msg":"Deposited amount is below minimum"},{"code":7027,"name":"ReserveSpaceExhausted","msg":"Vault have no space for new reserves"},{"code":7028,"name":"CannotWithdrawFromEmptyVault","msg":"Cannot withdraw from empty vault"},{"code":7029,"name":"TokensDepositedAmountDoesNotMatch","msg":"TokensDepositedAmountDoesNotMatch"},{"code":7030,"name":"AmountToWithdrawDoesNotMatch","msg":"Amount to withdraw does not match"},{"code":7031,"name":"LiquidityToWithdrawDoesNotMatch","msg":"Liquidity to withdraw does not match"},{"code":7032,"name":"UserReceivedAmountDoesNotMatch","msg":"User received amount does not match"},{"code":7033,"name":"SharesBurnedAmountDoesNotMatch","msg":"Shares burned amount does not match"},{"code":7034,"name":"DisinvestedLiquidityAmountDoesNotMatch","msg":"Disinvested liquidity amount does not match"},{"code":7035,"name":"SharesMintedAmountDoesNotMatch","msg":"SharesMintedAmountDoesNotMatch"},{"code":7036,"name":"AUMDecreasedAfterInvest","msg":"AUM decreased after invest"},{"code":7037,"name":"AUMBelowPendingFees","msg":"AUM is below pending fees"},{"code":7038,"name":"DepositAmountsZeroShares","msg":"Deposit amount results in 0 shares"},{"code":7039,"name":"WithdrawResultsInZeroShares","msg":"Withdraw amount results in 0 shares"},{"code":7040,"name":"CannotWithdrawZeroShares","msg":"Cannot withdraw zero shares"},{"code":7041,"name":"ManagementFeeGreaterThanMaxAllowed","msg":"Management fee is greater than maximum allowed"},{"code":7042,"name":"VaultAUMZero","msg":"Vault assets under management are empty"},{"code":7043,"name":"MissingReserveForBatchRefresh","msg":"Missing reserve for batch refresh"},{"code":7044,"name":"MinWithdrawAmountTooBig","msg":"Min withdraw amount is too big"},{"code":7045,"name":"InvestTooSoon","msg":"Invest is called too soon after last invest"},{"code":7046,"name":"WrongAdminOrAllocationAdmin","msg":"Wrong admin or allocation admin"},{"code":7047,"name":"ReserveHasNonZeroAllocationOrCTokens","msg":"Reserve has non-zero allocation or ctokens so cannot be removed"},{"code":7048,"name":"DepositAmountGreaterThanRequestedAmount","msg":"Deposit amount is greater than requested amount"},{"code":7049,"name":"WithdrawAmountLessThanWithdrawalPenalty","msg":"Withdraw amount is less than withdrawal penalty"},{"code":7050,"name":"CannotWithdrawZeroLamports","msg":"Cannot withdraw 0 lamports"},{"code":7051,"name":"NoUpgradeAuthority","msg":"Cannot initialize global config because there is no upgrade authority to the program"},{"code":7052,"name":"WithdrawalFeeBPSGreaterThanMaxAllowed","msg":"Withdrawal fee BPS is greater than maximum allowed"},{"code":7053,"name":"WithdrawalFeeLamportsGreaterThanMaxAllowed","msg":"Withdrawal fee lamports is greater than maximum allowed"},{"code":7054,"name":"ReserveNotWhitelisted","msg":"Reserve is not whitelisted"},{"code":7055,"name":"InvalidBoolLikeValue","msg":"Invalid bool-like value passed in (should be 0 or 1)"},{"code":7056,"name":"AUMDecreasedMoreThanExpected","msg":"AUM decreased more than expected during redeem in kind"},{"code":7057,"name":"RewardTopupAmountZero","msg":"Reward topup amount is zero"},{"code":7058,"name":"RewardTopupAmountNotExpected","msg":"Reward topup is not as expected after transfer"},{"code":7059,"name":"RewardWithdrawAmountZero","msg":"Reward withdraw amount is zero"},{"code":7060,"name":"RewardWithdrawAmountNotExpected","msg":"Reward withdraw is not as expected after transfer"},{"code":7061,"name":"RewardsStaleForFeeUpdate","msg":"Rewards are stale - must be refreshed before updating fees"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/layerzero_endpoint.76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6.json b/migration/khadhroony-bot2-reference/idls/layerzero_endpoint.76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6.json new file mode 100644 index 0000000..acbb23a --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/layerzero_endpoint.76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"endpoint","instructions":[{"name":"initEndpoint","docs":["--------------------------- Admin Instructions ---------------------------"],"accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"endpoint","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitEndpointParams"}}]},{"name":"transferAdmin","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"endpoint","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"TransferAdminParams"}}]},{"name":"setLzToken","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"endpoint","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetLzTokenParams"}}]},{"name":"registerLibrary","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"endpoint","isMut":false,"isSigner":false},{"name":"messageLibInfo","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"RegisterLibraryParams"}}]},{"name":"initDefaultSendLibrary","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"endpoint","isMut":false,"isSigner":false},{"name":"defaultSendLibraryConfig","isMut":true,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitDefaultSendLibraryParams"}}]},{"name":"setDefaultSendLibrary","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"endpoint","isMut":false,"isSigner":false},{"name":"defaultSendLibraryConfig","isMut":true,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetDefaultSendLibraryParams"}}]},{"name":"initDefaultReceiveLibrary","accounts":[{"name":"admin","isMut":true,"isSigner":true},{"name":"endpoint","isMut":false,"isSigner":false},{"name":"defaultReceiveLibraryConfig","isMut":true,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitDefaultReceiveLibraryParams"}}]},{"name":"setDefaultReceiveLibrary","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"endpoint","isMut":false,"isSigner":false},{"name":"defaultReceiveLibraryConfig","isMut":true,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetDefaultReceiveLibraryParams"}}]},{"name":"setDefaultReceiveLibraryTimeout","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"endpoint","isMut":false,"isSigner":false},{"name":"defaultReceiveLibraryConfig","isMut":true,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetDefaultReceiveLibraryTimeoutParams"}}]},{"name":"withdrawRent","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"endpoint","isMut":true,"isSigner":false},{"name":"receiver","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"WithdrawRentParams"}}]},{"name":"registerOapp","docs":["--------------------------- OApp Instructions ---------------------------"],"accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"oapp","isMut":false,"isSigner":true,"docs":["The PDA of the OApp"]},{"name":"oappRegistry","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"RegisterOAppParams"}}]},{"name":"initNonce","accounts":[{"name":"delegate","isMut":true,"isSigner":true,"docs":["only the delegate can initialize the nonce accounts"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"nonce","isMut":true,"isSigner":false},{"name":"pendingInboundNonce","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitNonceParams"}}]},{"name":"initSendLibrary","accounts":[{"name":"delegate","isMut":true,"isSigner":true,"docs":["only the delegate can initialize the send_library_config"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"sendLibraryConfig","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitSendLibraryParams"}}]},{"name":"setSendLibrary","accounts":[{"name":"signer","isMut":false,"isSigner":true,"docs":["The PDA of the OApp or delegate"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"sendLibraryConfig","isMut":true,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false,"isOptional":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetSendLibraryParams"}}]},{"name":"initReceiveLibrary","accounts":[{"name":"delegate","isMut":true,"isSigner":true,"docs":["only the delegate can initialize the send_library_config"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"receiveLibraryConfig","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitReceiveLibraryParams"}}]},{"name":"setReceiveLibrary","accounts":[{"name":"signer","isMut":false,"isSigner":true,"docs":["The PDA of the OApp or delegate"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"receiveLibraryConfig","isMut":true,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false,"isOptional":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetReceiveLibraryParams"}}]},{"name":"setReceiveLibraryTimeout","accounts":[{"name":"signer","isMut":false,"isSigner":true,"docs":["The PDA of the OApp or delegate"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"receiveLibraryConfig","isMut":true,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetReceiveLibraryTimeoutParams"}}]},{"name":"initConfig","accounts":[{"name":"delegate","isMut":false,"isSigner":true,"docs":["only the delegate can initialize the config accounts"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false,"docs":["The PDA signer to the message lib when the endpoint calls the message lib program."]},{"name":"messageLib","isMut":false,"isSigner":false,"docs":["the pda of the message_lib_program"]},{"name":"messageLibProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitConfigParams"}}]},{"name":"setConfig","accounts":[{"name":"signer","isMut":false,"isSigner":true,"docs":["The PDA of the OApp or delegate"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"messageLibInfo","isMut":false,"isSigner":false,"docs":["The PDA signer to the message lib when the endpoint calls the message lib program"]},{"name":"messageLib","isMut":false,"isSigner":false,"docs":["the pda of the message_lib_program"]},{"name":"messageLibProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetConfigParams"}}]},{"name":"quote","accounts":[{"name":"sendLibraryProgram","isMut":false,"isSigner":false},{"name":"sendLibraryConfig","isMut":false,"isSigner":false},{"name":"defaultSendLibraryConfig","isMut":false,"isSigner":false},{"name":"sendLibraryInfo","isMut":false,"isSigner":false,"docs":["The PDA signer to the send library when the endpoint calls the send library."]},{"name":"endpoint","isMut":false,"isSigner":false},{"name":"nonce","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"QuoteParams"}}],"returns":{"defined":"MessagingFee"}},{"name":"send","accounts":[{"name":"sender","isMut":false,"isSigner":true},{"name":"sendLibraryProgram","isMut":false,"isSigner":false},{"name":"sendLibraryConfig","isMut":false,"isSigner":false},{"name":"defaultSendLibraryConfig","isMut":false,"isSigner":false},{"name":"sendLibraryInfo","isMut":false,"isSigner":false,"docs":["The PDA signer to the send library when the endpoint calls the send library."]},{"name":"endpoint","isMut":false,"isSigner":false},{"name":"nonce","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SendParams"}}],"returns":{"defined":"MessagingReceipt"}},{"name":"initVerify","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"nonce","isMut":false,"isSigner":false},{"name":"payloadHash","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitVerifyParams"}}]},{"name":"verify","accounts":[{"name":"receiveLibrary","isMut":false,"isSigner":true,"docs":["The PDA of the receive library."]},{"name":"receiveLibraryConfig","isMut":false,"isSigner":false},{"name":"defaultReceiveLibraryConfig","isMut":false,"isSigner":false},{"name":"nonce","isMut":true,"isSigner":false},{"name":"pendingInboundNonce","isMut":true,"isSigner":false},{"name":"payloadHash","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"VerifyParams"}}]},{"name":"skip","accounts":[{"name":"signer","isMut":false,"isSigner":true,"docs":["The PDA of the OApp or delegate"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"nonce","isMut":true,"isSigner":false},{"name":"pendingInboundNonce","isMut":true,"isSigner":false},{"name":"payloadHash","isMut":true,"isSigner":false,"docs":["the payload hash needs to be initialized before it can be skipped and closed, in order to prevent someone","from skipping a payload hash that has been initialized and can be re-verified and executed after skipping"]},{"name":"endpoint","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SkipParams"}}]},{"name":"burn","accounts":[{"name":"signer","isMut":false,"isSigner":true,"docs":["The PDA of the OApp or delegate"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"nonce","isMut":false,"isSigner":false},{"name":"payloadHash","isMut":true,"isSigner":false,"docs":["close the account and return the lamports to endpoint settings account"]},{"name":"endpoint","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"BurnParams"}}]},{"name":"nilify","accounts":[{"name":"signer","isMut":false,"isSigner":true,"docs":["The PDA of the OApp or delegate"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"nonce","isMut":true,"isSigner":false},{"name":"pendingInboundNonce","isMut":true,"isSigner":false},{"name":"payloadHash","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"NilifyParams"}}]},{"name":"clear","accounts":[{"name":"signer","isMut":false,"isSigner":true,"docs":["The PDA of the OApp or delegate"]},{"name":"oappRegistry","isMut":false,"isSigner":false},{"name":"nonce","isMut":false,"isSigner":false},{"name":"payloadHash","isMut":true,"isSigner":false,"docs":["close the account and return the lamports to endpoint settings account"]},{"name":"endpoint","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"ClearParams"}}],"returns":{"array":["u8",32]}},{"name":"sendCompose","accounts":[{"name":"from","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"composeMessage","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SendComposeParams"}}]},{"name":"clearCompose","accounts":[{"name":"to","isMut":false,"isSigner":true},{"name":"composeMessage","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"ClearComposeParams"}}]},{"name":"setDelegate","accounts":[{"name":"oapp","isMut":false,"isSigner":true,"docs":["The PDA of the OApp"]},{"name":"oappRegistry","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SetDelegateParams"}}]},{"name":"lzReceiveAlert","accounts":[{"name":"executor","isMut":false,"isSigner":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"LzReceiveAlertParams"}}]},{"name":"lzComposeAlert","accounts":[{"name":"executor","isMut":false,"isSigner":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"LzComposeAlertParams"}}]}],"accounts":[{"name":"ComposeMessageState","type":{"kind":"struct","fields":[{"name":"received","type":"bool"},{"name":"bump","type":"u8"}]}},{"name":"EndpointSettings","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"bump","type":"u8"},{"name":"admin","type":"publicKey"},{"name":"lzTokenMint","type":{"option":"publicKey"}}]}},{"name":"OAppRegistry","type":{"kind":"struct","fields":[{"name":"delegate","type":"publicKey"},{"name":"bump","type":"u8"}]}},{"name":"MessageLibInfo","type":{"kind":"struct","fields":[{"name":"messageLibType","type":{"defined":"MessageLibType"}},{"name":"bump","type":"u8"},{"name":"messageLibBump","type":"u8"}]}},{"name":"ReceiveLibraryConfig","type":{"kind":"struct","fields":[{"name":"messageLib","type":"publicKey"},{"name":"timeout","type":{"option":{"defined":"ReceiveLibraryTimeout"}}},{"name":"bump","type":"u8"}]}},{"name":"SendLibraryConfig","docs":["the reason for not using Option::None to indicate default is to respect the spec on evm"],"type":{"kind":"struct","fields":[{"name":"messageLib","type":"publicKey"},{"name":"bump","type":"u8"}]}},{"name":"Nonce","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"outboundNonce","type":"u64"},{"name":"inboundNonce","type":"u64"}]}},{"name":"PayloadHash","type":{"kind":"struct","fields":[{"name":"hash","type":{"array":["u8",32]}},{"name":"bump","type":"u8"}]}},{"name":"PendingInboundNonce","type":{"kind":"struct","fields":[{"name":"nonces","type":{"vec":"u64"}},{"name":"bump","type":"u8"}]}}],"types":[{"name":"InitDefaultReceiveLibraryParams","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"}]}},{"name":"InitDefaultSendLibraryParams","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"}]}},{"name":"InitEndpointParams","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"admin","type":"publicKey"}]}},{"name":"RegisterLibraryParams","type":{"kind":"struct","fields":[{"name":"libProgram","type":"publicKey"},{"name":"libType","type":{"defined":"MessageLibType"}}]}},{"name":"SetDefaultReceiveLibraryParams","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"},{"name":"gracePeriod","type":"u64"}]}},{"name":"SetDefaultReceiveLibraryTimeoutParams","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"lib","type":"publicKey"},{"name":"expiry","type":"u64"}]}},{"name":"SetDefaultSendLibraryParams","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"}]}},{"name":"SetLzTokenParams","type":{"kind":"struct","fields":[{"name":"lzToken","type":{"option":"publicKey"}}]}},{"name":"TransferAdminParams","type":{"kind":"struct","fields":[{"name":"admin","type":"publicKey"}]}},{"name":"WithdrawRentParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"InitVerifyParams","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"receiver","type":"publicKey"},{"name":"nonce","type":"u64"}]}},{"name":"LzComposeAlertParams","type":{"kind":"struct","fields":[{"name":"from","type":"publicKey"},{"name":"to","type":"publicKey"},{"name":"guid","type":{"array":["u8",32]}},{"name":"index","type":"u16"},{"name":"computeUnits","type":"u64"},{"name":"value","type":"u64"},{"name":"message","type":"bytes"},{"name":"extraData","type":"bytes"},{"name":"reason","type":"bytes"}]}},{"name":"LzReceiveAlertParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"guid","type":{"array":["u8",32]}},{"name":"computeUnits","type":"u64"},{"name":"value","type":"u64"},{"name":"message","type":"bytes"},{"name":"extraData","type":"bytes"},{"name":"reason","type":"bytes"}]}},{"name":"BurnParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"payloadHash","type":{"array":["u8",32]}}]}},{"name":"ClearParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"guid","type":{"array":["u8",32]}},{"name":"message","type":"bytes"}]}},{"name":"ClearComposeParams","type":{"kind":"struct","fields":[{"name":"from","type":"publicKey"},{"name":"guid","type":{"array":["u8",32]}},{"name":"index","type":"u16"},{"name":"message","type":"bytes"}]}},{"name":"InitNonceParams","type":{"kind":"struct","fields":[{"name":"localOapp","type":"publicKey"},{"name":"remoteEid","type":"u32"},{"name":"remoteOapp","type":{"array":["u8",32]}}]}},{"name":"InitReceiveLibraryParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"eid","type":"u32"}]}},{"name":"InitSendLibraryParams","type":{"kind":"struct","fields":[{"name":"sender","type":"publicKey"},{"name":"eid","type":"u32"}]}},{"name":"NilifyParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"payloadHash","type":{"array":["u8",32]}}]}},{"name":"QuoteParams","type":{"kind":"struct","fields":[{"name":"sender","type":"publicKey"},{"name":"dstEid","type":"u32"},{"name":"receiver","type":{"array":["u8",32]}},{"name":"message","type":"bytes"},{"name":"options","type":"bytes"},{"name":"payInLzToken","type":"bool"}]}},{"name":"RegisterOAppParams","type":{"kind":"struct","fields":[{"name":"delegate","type":"publicKey"}]}},{"name":"SendParams","type":{"kind":"struct","fields":[{"name":"dstEid","type":"u32"},{"name":"receiver","type":{"array":["u8",32]}},{"name":"message","type":"bytes"},{"name":"options","type":"bytes"},{"name":"nativeFee","type":"u64"},{"name":"lzTokenFee","type":"u64"}]}},{"name":"SendComposeParams","type":{"kind":"struct","fields":[{"name":"to","type":"publicKey"},{"name":"guid","type":{"array":["u8",32]}},{"name":"index","type":"u16"},{"name":"message","type":"bytes"}]}},{"name":"SetDelegateParams","type":{"kind":"struct","fields":[{"name":"delegate","type":"publicKey"}]}},{"name":"SetReceiveLibraryParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"},{"name":"gracePeriod","type":"u64"}]}},{"name":"SetReceiveLibraryTimeoutParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"eid","type":"u32"},{"name":"lib","type":"publicKey"},{"name":"expiry","type":"u64"}]}},{"name":"SetSendLibraryParams","type":{"kind":"struct","fields":[{"name":"sender","type":"publicKey"},{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"}]}},{"name":"SkipParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"}]}},{"name":"VerifyParams","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"receiver","type":"publicKey"},{"name":"nonce","type":"u64"},{"name":"payloadHash","type":{"array":["u8",32]}}]}},{"name":"ReceiveLibraryTimeout","type":{"kind":"struct","fields":[{"name":"messageLib","type":"publicKey"},{"name":"expiry","type":"u64"}]}},{"name":"InitConfigParams","type":{"kind":"struct","fields":[{"name":"oapp","type":"publicKey"},{"name":"eid","type":"u32"}]}},{"name":"MessageLibType","type":{"kind":"enum","variants":[{"name":"Send"},{"name":"Receive"},{"name":"SendAndReceive"}]}},{"name":"MessagingFee","type":{"kind":"struct","fields":[{"name":"nativeFee","type":"u64"},{"name":"lzTokenFee","type":"u64"}]}},{"name":"MessagingReceipt","type":{"kind":"struct","fields":[{"name":"guid","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"fee","type":{"defined":"MessagingFee"}}]}},{"name":"SetConfigParams","type":{"kind":"struct","fields":[{"name":"oapp","type":"publicKey"},{"name":"eid","type":"u32"},{"name":"configType","type":"u32"},{"name":"config","type":"bytes"}]}},{"name":"AdminTransferredEvent","type":{"kind":"struct","fields":[{"name":"newAdmin","type":"publicKey"}]}},{"name":"ComposeDeliveredEvent","type":{"kind":"struct","fields":[{"name":"from","type":"publicKey"},{"name":"to","type":"publicKey"},{"name":"guid","type":{"array":["u8",32]}},{"name":"index","type":"u16"}]}},{"name":"ComposeSentEvent","type":{"kind":"struct","fields":[{"name":"from","type":"publicKey"},{"name":"to","type":"publicKey"},{"name":"guid","type":{"array":["u8",32]}},{"name":"index","type":"u16"},{"name":"message","type":"bytes"}]}},{"name":"DefaultReceiveLibrarySetEvent","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"}]}},{"name":"DefaultReceiveLibraryTimeoutSetEvent","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"timeout","type":{"option":{"defined":"ReceiveLibraryTimeout"}}}]}},{"name":"DefaultSendLibrarySetEvent","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"}]}},{"name":"DelegateSetEvent","type":{"kind":"struct","fields":[{"name":"newDelegate","type":"publicKey"}]}},{"name":"InboundNonceSkippedEvent","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"receiver","type":"publicKey"},{"name":"nonce","type":"u64"}]}},{"name":"LibraryRegisteredEvent","type":{"kind":"struct","fields":[{"name":"newLib","type":"publicKey"},{"name":"newLibProgram","type":"publicKey"}]}},{"name":"LzComposeAlertEvent","type":{"kind":"struct","fields":[{"name":"executor","type":"publicKey"},{"name":"from","type":"publicKey"},{"name":"to","type":"publicKey"},{"name":"guid","type":{"array":["u8",32]}},{"name":"index","type":"u16"},{"name":"computeUnits","type":"u64"},{"name":"value","type":"u64"},{"name":"message","type":"bytes"},{"name":"extraData","type":"bytes"},{"name":"reason","type":"bytes"}]}},{"name":"LzReceiveAlertEvent","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"executor","type":"publicKey"},{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"guid","type":{"array":["u8",32]}},{"name":"computeUnits","type":"u64"},{"name":"value","type":"u64"},{"name":"message","type":"bytes"},{"name":"extraData","type":"bytes"},{"name":"reason","type":"bytes"}]}},{"name":"LzTokenSetEvent","type":{"kind":"struct","fields":[{"name":"token","type":{"option":"publicKey"}}]}},{"name":"OAppRegisteredEvent","type":{"kind":"struct","fields":[{"name":"oapp","type":"publicKey"},{"name":"delegate","type":"publicKey"}]}},{"name":"PacketBurntEvent","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"receiver","type":"publicKey"},{"name":"nonce","type":"u64"},{"name":"payloadHash","type":{"array":["u8",32]}}]}},{"name":"PacketDeliveredEvent","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"receiver","type":"publicKey"},{"name":"nonce","type":"u64"}]}},{"name":"PacketNilifiedEvent","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"receiver","type":"publicKey"},{"name":"nonce","type":"u64"},{"name":"payloadHash","type":{"array":["u8",32]}}]}},{"name":"PacketSentEvent","type":{"kind":"struct","fields":[{"name":"encodedPacket","type":"bytes"},{"name":"options","type":"bytes"},{"name":"sendLibrary","type":"publicKey"}]}},{"name":"PacketVerifiedEvent","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"receiver","type":"publicKey"},{"name":"nonce","type":"u64"},{"name":"payloadHash","type":{"array":["u8",32]}}]}},{"name":"ReceiveLibrarySetEvent","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"}]}},{"name":"ReceiveLibraryTimeoutSetEvent","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"eid","type":"u32"},{"name":"timeout","type":{"option":{"defined":"ReceiveLibraryTimeout"}}}]}},{"name":"RentWithdrawnEvent","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"amount","type":"u64"}]}},{"name":"SendLibrarySetEvent","type":{"kind":"struct","fields":[{"name":"sender","type":"publicKey"},{"name":"eid","type":"u32"},{"name":"newLib","type":"publicKey"}]}}],"events":[{"name":"AdminTransferredEvent","fields":[{"name":"newAdmin","type":"publicKey","index":false}]},{"name":"ComposeDeliveredEvent","fields":[{"name":"from","type":"publicKey","index":false},{"name":"to","type":"publicKey","index":false},{"name":"guid","type":{"array":["u8",32]},"index":false},{"name":"index","type":"u16","index":false}]},{"name":"ComposeSentEvent","fields":[{"name":"from","type":"publicKey","index":false},{"name":"to","type":"publicKey","index":false},{"name":"guid","type":{"array":["u8",32]},"index":false},{"name":"index","type":"u16","index":false},{"name":"message","type":"bytes","index":false}]},{"name":"DefaultReceiveLibrarySetEvent","fields":[{"name":"eid","type":"u32","index":false},{"name":"newLib","type":"publicKey","index":false}]},{"name":"DefaultReceiveLibraryTimeoutSetEvent","fields":[{"name":"eid","type":"u32","index":false},{"name":"timeout","type":{"option":{"defined":"ReceiveLibraryTimeout"}},"index":false}]},{"name":"DefaultSendLibrarySetEvent","fields":[{"name":"eid","type":"u32","index":false},{"name":"newLib","type":"publicKey","index":false}]},{"name":"DelegateSetEvent","fields":[{"name":"newDelegate","type":"publicKey","index":false}]},{"name":"InboundNonceSkippedEvent","fields":[{"name":"srcEid","type":"u32","index":false},{"name":"sender","type":{"array":["u8",32]},"index":false},{"name":"receiver","type":"publicKey","index":false},{"name":"nonce","type":"u64","index":false}]},{"name":"LibraryRegisteredEvent","fields":[{"name":"newLib","type":"publicKey","index":false},{"name":"newLibProgram","type":"publicKey","index":false}]},{"name":"LzComposeAlertEvent","fields":[{"name":"executor","type":"publicKey","index":false},{"name":"from","type":"publicKey","index":false},{"name":"to","type":"publicKey","index":false},{"name":"guid","type":{"array":["u8",32]},"index":false},{"name":"index","type":"u16","index":false},{"name":"computeUnits","type":"u64","index":false},{"name":"value","type":"u64","index":false},{"name":"message","type":"bytes","index":false},{"name":"extraData","type":"bytes","index":false},{"name":"reason","type":"bytes","index":false}]},{"name":"LzReceiveAlertEvent","fields":[{"name":"receiver","type":"publicKey","index":false},{"name":"executor","type":"publicKey","index":false},{"name":"srcEid","type":"u32","index":false},{"name":"sender","type":{"array":["u8",32]},"index":false},{"name":"nonce","type":"u64","index":false},{"name":"guid","type":{"array":["u8",32]},"index":false},{"name":"computeUnits","type":"u64","index":false},{"name":"value","type":"u64","index":false},{"name":"message","type":"bytes","index":false},{"name":"extraData","type":"bytes","index":false},{"name":"reason","type":"bytes","index":false}]},{"name":"LzTokenSetEvent","fields":[{"name":"token","type":{"option":"publicKey"},"index":false}]},{"name":"OAppRegisteredEvent","fields":[{"name":"oapp","type":"publicKey","index":false},{"name":"delegate","type":"publicKey","index":false}]},{"name":"PacketBurntEvent","fields":[{"name":"srcEid","type":"u32","index":false},{"name":"sender","type":{"array":["u8",32]},"index":false},{"name":"receiver","type":"publicKey","index":false},{"name":"nonce","type":"u64","index":false},{"name":"payloadHash","type":{"array":["u8",32]},"index":false}]},{"name":"PacketDeliveredEvent","fields":[{"name":"srcEid","type":"u32","index":false},{"name":"sender","type":{"array":["u8",32]},"index":false},{"name":"receiver","type":"publicKey","index":false},{"name":"nonce","type":"u64","index":false}]},{"name":"PacketNilifiedEvent","fields":[{"name":"srcEid","type":"u32","index":false},{"name":"sender","type":{"array":["u8",32]},"index":false},{"name":"receiver","type":"publicKey","index":false},{"name":"nonce","type":"u64","index":false},{"name":"payloadHash","type":{"array":["u8",32]},"index":false}]},{"name":"PacketSentEvent","fields":[{"name":"encodedPacket","type":"bytes","index":false},{"name":"options","type":"bytes","index":false},{"name":"sendLibrary","type":"publicKey","index":false}]},{"name":"PacketVerifiedEvent","fields":[{"name":"srcEid","type":"u32","index":false},{"name":"sender","type":{"array":["u8",32]},"index":false},{"name":"receiver","type":"publicKey","index":false},{"name":"nonce","type":"u64","index":false},{"name":"payloadHash","type":{"array":["u8",32]},"index":false}]},{"name":"ReceiveLibrarySetEvent","fields":[{"name":"receiver","type":"publicKey","index":false},{"name":"eid","type":"u32","index":false},{"name":"newLib","type":"publicKey","index":false}]},{"name":"ReceiveLibraryTimeoutSetEvent","fields":[{"name":"receiver","type":"publicKey","index":false},{"name":"eid","type":"u32","index":false},{"name":"timeout","type":{"option":{"defined":"ReceiveLibraryTimeout"}},"index":false}]},{"name":"RentWithdrawnEvent","fields":[{"name":"receiver","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]},{"name":"SendLibrarySetEvent","fields":[{"name":"sender","type":"publicKey","index":false},{"name":"eid","type":"u32","index":false},{"name":"newLib","type":"publicKey","index":false}]}],"errors":[{"code":6000,"name":"InvalidSendLibrary"},{"code":6001,"name":"InvalidReceiveLibrary"},{"code":6002,"name":"SameValue"},{"code":6003,"name":"AccountNotFound"},{"code":6004,"name":"OnlySendLib"},{"code":6005,"name":"OnlyReceiveLib"},{"code":6006,"name":"InvalidExpiry"},{"code":6007,"name":"OnlyNonDefaultLib"},{"code":6008,"name":"InvalidAmount"},{"code":6009,"name":"InvalidNonce"},{"code":6010,"name":"Unauthorized"},{"code":6011,"name":"PayloadHashNotFound"},{"code":6012,"name":"ComposeNotFound"},{"code":6013,"name":"InvalidPayloadHash"},{"code":6014,"name":"LzTokenUnavailable"},{"code":6015,"name":"ReadOnlyAccount"},{"code":6016,"name":"InvalidMessageLib"},{"code":6017,"name":"WritableAccountNotAllowed"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/layerzero_executor.6doghB248px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn.json b/migration/khadhroony-bot2-reference/idls/layerzero_executor.6doghB248px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn.json new file mode 100644 index 0000000..02f889a --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/layerzero_executor.6doghB248px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"executor","instructions":[{"name":"initExecutor","docs":["--------------------------- Owner Instructions ---------------------------"],"accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"config","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitExecutorParams"}}]},{"name":"ownerSetConfig","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"config","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"OwnerSetConfigParams"}}]},{"name":"adminSetConfig","docs":["--------------------------- Admin Instructions ---------------------------"],"accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"config","isMut":true,"isSigner":false}],"args":[{"name":"params","type":{"defined":"AdminSetConfigParams"}}]},{"name":"nativeDrop","accounts":[{"name":"executor","isMut":true,"isSigner":true},{"name":"config","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["For native drop transfer"]},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"NativeDropParams"}}]},{"name":"execute","accounts":[{"name":"executor","isMut":true,"isSigner":true},{"name":"config","isMut":false,"isSigner":false},{"name":"endpointProgram","isMut":false,"isSigner":false},{"name":"endpointEventAuthority","isMut":false,"isSigner":false,"docs":["The authority for the endpoint program to emit events"]},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"ExecuteParams"}}]},{"name":"compose","accounts":[{"name":"executor","isMut":true,"isSigner":true},{"name":"config","isMut":false,"isSigner":false},{"name":"endpointProgram","isMut":false,"isSigner":false},{"name":"endpointEventAuthority","isMut":false,"isSigner":false,"docs":["The authority for the endpoint program to emit events"]},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"ComposeParams"}}]},{"name":"quoteExecutor","docs":["--------------------------- MsgLib Instructions ---------------------------"],"accounts":[{"name":"executorConfig","isMut":false,"isSigner":false},{"name":"priceFeedProgram","isMut":false,"isSigner":false},{"name":"priceFeedConfig","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"QuoteExecutorParams"}}],"returns":"u64"},{"name":"executable","accounts":[{"name":"nonce","isMut":false,"isSigner":false},{"name":"payloadHash","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"ExecutableParams"}}],"returns":{"defined":"ExecutionState"}}],"accounts":[{"name":"Nonce","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"outboundNonce","type":"u64"},{"name":"inboundNonce","type":"u64"}]}},{"name":"ExecutorConfig","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"owner","type":"publicKey"},{"name":"acl","type":{"defined":"Acl"}},{"name":"admins","type":{"vec":"publicKey"}},{"name":"executors","type":{"vec":"publicKey"}},{"name":"msglibs","type":{"vec":"publicKey"}},{"name":"paused","type":"bool"},{"name":"defaultMultiplierBps","type":"u16"},{"name":"priceFeed","type":"publicKey"},{"name":"dstConfigs","type":{"vec":{"defined":"DstConfig"}}}]}}],"types":[{"name":"AdminSetConfigParams","type":{"kind":"enum","variants":[{"name":"PriceFeed","fields":["publicKey"]},{"name":"DefaultMultiplierBps","fields":["u16"]},{"name":"DstConfigs","fields":[{"vec":{"defined":"DstConfig"}}]}]}},{"name":"ComposeParams","type":{"kind":"struct","fields":[{"name":"lzCompose","type":{"defined":"LzComposeParams"}},{"name":"computeUnits","type":"u64"},{"name":"value","type":"u64"}]}},{"name":"ExecutableParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"}]}},{"name":"ExecutionState","type":{"kind":"enum","variants":[{"name":"NotExecutable"},{"name":"VerifiedButNotExecutable"},{"name":"Executable"},{"name":"Executed"}]}},{"name":"ExecuteParams","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"lzReceive","type":{"defined":"LzReceiveParams"}},{"name":"value","type":"u64"},{"name":"computeUnits","type":"u64"}]}},{"name":"InitExecutorParams","type":{"kind":"struct","fields":[{"name":"owner","type":"publicKey"},{"name":"admins","type":{"vec":"publicKey"}},{"name":"executors","type":{"vec":"publicKey"}},{"name":"msglibs","type":{"vec":"publicKey"}},{"name":"priceFeed","type":"publicKey"}]}},{"name":"NativeDropParams","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"dstEid","type":"u32"},{"name":"oapp","type":"publicKey"},{"name":"nativeDropRequests","type":{"vec":{"defined":"NativeDropRequest"}}}]}},{"name":"NativeDropRequest","type":{"kind":"struct","fields":[{"name":"receiver","type":"publicKey"},{"name":"amount","type":"u64"}]}},{"name":"OwnerSetConfigParams","type":{"kind":"enum","variants":[{"name":"Admins","fields":[{"vec":"publicKey"}]},{"name":"Executors","fields":[{"vec":"publicKey"}]},{"name":"Msglibs","fields":[{"vec":"publicKey"}]},{"name":"Owner","fields":["publicKey"]},{"name":"Paused","fields":["bool"]},{"name":"Allowlist","fields":[{"vec":"publicKey"}]},{"name":"Denylist","fields":[{"vec":"publicKey"}]}]}},{"name":"DstConfig","type":{"kind":"struct","fields":[{"name":"eid","type":"u32"},{"name":"lzReceiveBaseGas","type":"u32"},{"name":"lzComposeBaseGas","type":"u32"},{"name":"multiplierBps","type":{"option":"u16"}},{"name":"floorMarginUsd","type":{"option":"u128"}},{"name":"nativeDropCap","type":"u128"}]}},{"name":"LzComposeParams","type":{"kind":"struct","fields":[{"name":"from","type":"publicKey"},{"name":"to","type":"publicKey"},{"name":"guid","type":{"array":["u8",32]}},{"name":"index","type":"u16"},{"name":"message","type":"bytes"},{"name":"extraData","type":"bytes"}]}},{"name":"LzReceiveParams","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"guid","type":{"array":["u8",32]}},{"name":"message","type":"bytes"},{"name":"extraData","type":"bytes"}]}},{"name":"LzOption","type":{"kind":"struct","fields":[{"name":"optionType","type":"u8"},{"name":"params","type":"bytes"}]}},{"name":"QuoteExecutorParams","type":{"kind":"struct","fields":[{"name":"msglib","type":"publicKey"},{"name":"dstEid","type":"u32"},{"name":"sender","type":"publicKey"},{"name":"calldataSize","type":"u64"},{"name":"options","type":{"vec":{"defined":"LzOption"}}}]}},{"name":"Acl","type":{"kind":"struct","fields":[{"name":"allowList","type":{"vec":"publicKey"}},{"name":"denyList","type":{"vec":"publicKey"}}]}},{"name":"NativeDropAppliedEvent","type":{"kind":"struct","fields":[{"name":"srcEid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"dstEid","type":"u32"},{"name":"oapp","type":"publicKey"},{"name":"nativeDropRequests","type":{"vec":{"defined":"NativeDropRequest"}}},{"name":"successes","type":{"vec":"bool"}}]}}],"events":[{"name":"NativeDropAppliedEvent","fields":[{"name":"srcEid","type":"u32","index":false},{"name":"sender","type":{"array":["u8",32]},"index":false},{"name":"nonce","type":"u64","index":false},{"name":"dstEid","type":"u32","index":false},{"name":"oapp","type":"publicKey","index":false},{"name":"nativeDropRequests","type":{"vec":{"defined":"NativeDropRequest"}},"index":false},{"name":"successes","type":{"vec":"bool"},"index":false}]}],"errors":[{"code":6000,"name":"InvalidSize"},{"code":6001,"name":"Paused"},{"code":6002,"name":"UnsupportedOptionType"},{"code":6003,"name":"ZeroLzComposeGasProvided"},{"code":6004,"name":"ZeroLzReceiveGasProvided"},{"code":6005,"name":"NativeAmountExceedsCap"},{"code":6006,"name":"NotAdmin"},{"code":6007,"name":"NotExecutor"},{"code":6008,"name":"MsgLibNotAllowed"},{"code":6009,"name":"TooManyAdmins"},{"code":6010,"name":"TooManyExecutors"},{"code":6011,"name":"TooManyOptionTypes"},{"code":6012,"name":"InvalidNativeDropRequestsLength"},{"code":6013,"name":"InvalidNativeDropReceiver"},{"code":6014,"name":"InsufficientBalance"},{"code":6015,"name":"EidNotSupported"},{"code":6016,"name":"ExecutorIsAdmin"},{"code":6017,"name":"InvalidOwner"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/lifinity_amm_v2.2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c.json b/migration/khadhroony-bot2-reference/idls/lifinity_amm_v2.2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c.json new file mode 100644 index 0000000..9d181e7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/lifinity_amm_v2.2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c.json @@ -0,0 +1 @@ +{"version":"0.1.1","name":"lifinity_amm_v2","instructions":[{"name":"swap","accounts":[{"name":"authority","isMut":false,"isSigner":false},{"name":"amm","isMut":true,"isSigner":false},{"name":"userTransferAuthority","isMut":false,"isSigner":true},{"name":"sourceInfo","isMut":true,"isSigner":false},{"name":"destinationInfo","isMut":true,"isSigner":false},{"name":"swapSource","isMut":true,"isSigner":false},{"name":"swapDestination","isMut":true,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"feeAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"oracleMainAccount","isMut":false,"isSigner":false},{"name":"oracleSubAccount","isMut":false,"isSigner":false},{"name":"oraclePcAccount","isMut":false,"isSigner":false}],"args":[{"name":"amountIn","type":"u64"},{"name":"minimumAmountOut","type":"u64"}]},{"name":"depositAllTokenTypes","accounts":[{"name":"amm","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"userTransferAuthorityInfo","isMut":false,"isSigner":true},{"name":"sourceAInfo","isMut":true,"isSigner":false},{"name":"sourceBInfo","isMut":true,"isSigner":false},{"name":"tokenA","isMut":true,"isSigner":false},{"name":"tokenB","isMut":true,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"destination","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"poolTokenAmount","type":"u64"},{"name":"maximumTokenAAmount","type":"u64"},{"name":"maximumTokenBAmount","type":"u64"}]},{"name":"withdrawAllTokenTypes","accounts":[{"name":"amm","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"userTransferAuthorityInfo","isMut":false,"isSigner":true},{"name":"sourceInfo","isMut":true,"isSigner":false},{"name":"tokenA","isMut":true,"isSigner":false},{"name":"tokenB","isMut":true,"isSigner":false},{"name":"poolMint","isMut":true,"isSigner":false},{"name":"destTokenAInfo","isMut":true,"isSigner":false},{"name":"destTokenBInfo","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"poolTokenAmount","type":"u64"},{"name":"minimumTokenAAmount","type":"u64"},{"name":"minimumTokenBAmount","type":"u64"}]}],"accounts":[{"name":"Amm","type":{"kind":"struct","fields":[{"name":"initializerKey","type":"publicKey"},{"name":"initializerDepositTokenAccount","type":"publicKey"},{"name":"initializerReceiveTokenAccount","type":"publicKey"},{"name":"initializerAmount","type":"u64"},{"name":"takerAmount","type":"u64"},{"name":"isInitialized","docs":["Is the swap initialized, with data written to it"],"type":"bool"},{"name":"bumpSeed","docs":["Bump seed used to generate the program address / authority"],"type":"u8"},{"name":"freezeTrade","docs":["Freeze swap"],"type":"u8"},{"name":"freezeDeposit","docs":["Freeze deposit"],"type":"u8"},{"name":"freezeWithdraw","docs":["Freeze withdraw"],"type":"u8"},{"name":"baseDecimals","docs":["Base decimals account"],"type":"u8"},{"name":"tokenProgramId","docs":["Token program ID associated with the swap"],"type":"publicKey"},{"name":"tokenAAccount","docs":["Address of token A liquidity account"],"type":"publicKey"},{"name":"tokenBAccount","docs":["Address of token B liquidity account"],"type":"publicKey"},{"name":"poolMint","docs":["Address of pool token mint"],"type":"publicKey"},{"name":"tokenAMint","docs":["Address of token A mint"],"type":"publicKey"},{"name":"tokenBMint","docs":["Address of token B mint"],"type":"publicKey"},{"name":"feeAccount","docs":["Address of pool fee account"],"type":"publicKey"},{"name":"oracleMainAccount","docs":["Oracle Main account"],"type":"publicKey"},{"name":"oracleSubAccount","docs":["Oracle Sub account"],"type":"publicKey"},{"name":"oraclePcAccount","docs":["oracle Pc account"],"type":"publicKey"},{"name":"fees","docs":["Fees associated with swap"],"type":{"defined":"AmmFees"}},{"name":"curve","docs":["Curve associated with swap"],"type":{"defined":"AmmCurve"}},{"name":"config","docs":["Curve associated with swap"],"type":{"defined":"AmmConfig"}},{"name":"ammPTemp1","docs":["Temp area"],"type":"publicKey"},{"name":"ammPTemp2","docs":["Temp area"],"type":"publicKey"},{"name":"ammPTemp3","docs":["Temp area"],"type":"publicKey"},{"name":"ammPTemp4","docs":["Temp area"],"type":"publicKey"},{"name":"ammPTemp5","docs":["Temp area"],"type":"publicKey"}]}}],"types":[{"name":"AmmFees","docs":["Encapsulates all fee information and calculations for swap operations"],"type":{"kind":"struct","fields":[{"name":"tradeFeeNumerator","type":"u64"},{"name":"tradeFeeDenominator","type":"u64"},{"name":"ownerTradeFeeNumerator","type":"u64"},{"name":"ownerTradeFeeDenominator","type":"u64"},{"name":"ownerWithdrawFeeNumerator","type":"u64"},{"name":"ownerWithdrawFeeDenominator","type":"u64"},{"name":"hostFeeNumerator","type":"u64"},{"name":"hostFeeDenominator","type":"u64"}]}},{"name":"AmmCurve","type":{"kind":"struct","fields":[{"name":"curveType","type":"u8"},{"name":"curveParameters","type":"u64"}]}},{"name":"AmmConfig","type":{"kind":"struct","fields":[{"name":"lastPrice","type":"u64"},{"name":"lastBalancedPrice","type":"u64"},{"name":"configDenominator","type":"u64"},{"name":"volumeX","type":"u64"},{"name":"volumeY","type":"u64"},{"name":"volumeXInY","type":"u64"},{"name":"depositCap","type":"u64"},{"name":"regressionTarget","type":"u64"},{"name":"oracleType","type":"u64"},{"name":"oracleStatus","type":"u64"},{"name":"oracleMainSlotLimit","type":"u64"},{"name":"oracleSubConfidenceLimit","type":"u64"},{"name":"oracleSubSlotLimit","type":"u64"},{"name":"oraclePcConfidenceLimit","type":"u64"},{"name":"oraclePcSlotLimit","type":"u64"},{"name":"stdSpread","type":"u64"},{"name":"stdSpreadBuffer","type":"u64"},{"name":"spreadCoefficient","type":"u64"},{"name":"priceBufferCoin","type":"i64"},{"name":"priceBufferPc","type":"i64"},{"name":"rebalanceRatio","type":"u64"},{"name":"feeTrade","type":"u64"},{"name":"feePlatform","type":"u64"},{"name":"oracleMainSlotBuffer","type":"u64"},{"name":"configTemp4","type":"u64"},{"name":"configTemp5","type":"u64"},{"name":"configTemp6","type":"u64"},{"name":"configTemp7","type":"u64"},{"name":"configTemp8","type":"u64"}]}},{"name":"CurveType","docs":["Curve types supported by the token-swap program."],"type":{"kind":"enum","variants":[{"name":"Standard"},{"name":"ConstantProduct"}]}}],"errors":[{"code":6000,"name":"AlreadyInUse","msg":"Swap account already in use"},{"code":6001,"name":"InvalidProgramAddress","msg":"Invalid program address generated from bump seed and key"},{"code":6002,"name":"InvalidOwner","msg":"Input account owner is not the program address"},{"code":6003,"name":"InvalidOutputOwner","msg":"Output pool account owner cannot be the program address"},{"code":6004,"name":"ExpectedMint","msg":"Deserialized account is not an SPL Token mint"},{"code":6005,"name":"ExpectedAccount","msg":"Deserialized account is not an SPL Token account"},{"code":6006,"name":"EmptySupply","msg":"Input token account empty"},{"code":6007,"name":"InvalidSupply","msg":"Pool token mint has a non-zero supply"},{"code":6008,"name":"InvalidDelegate","msg":"Token account has a delegate"},{"code":6009,"name":"InvalidInput","msg":"InvalidInput"},{"code":6010,"name":"IncorrectSwapAccount","msg":"Address of the provided swap token account is incorrect"},{"code":6011,"name":"IncorrectPoolMint","msg":"Address of the provided pool token mint is incorrect"},{"code":6012,"name":"InvalidOutput","msg":"InvalidOutput"},{"code":6013,"name":"CalculationFailure","msg":"General calculation failure due to overflow or underflow"},{"code":6014,"name":"InvalidInstruction","msg":"Invalid instruction"},{"code":6015,"name":"RepeatedMint","msg":"Swap input token accounts have the same mint"},{"code":6016,"name":"ExceededSlippage","msg":"Swap instruction exceeds desired slippage limit"},{"code":6017,"name":"InvalidCloseAuthority","msg":"Token account has a close authority"},{"code":6018,"name":"InvalidFreezeAuthority","msg":"Pool token mint has a freeze authority"},{"code":6019,"name":"IncorrectFeeAccount","msg":"Pool fee token account incorrect"},{"code":6020,"name":"ZeroTradingTokens","msg":"Given pool token amount results in zero trading tokens"},{"code":6021,"name":"FeeCalculationFailure","msg":"Fee calculation failed due to overflow, underflow, or unexpected 0"},{"code":6022,"name":"ConversionFailure","msg":"Conversion to u64 failed with an overflow or underflow"},{"code":6023,"name":"InvalidFee","msg":"The provided fee does not match the program owner's constraints"},{"code":6024,"name":"IncorrectTokenProgramId","msg":"The provided token program does not match the token program expected by the swap"},{"code":6025,"name":"IncorrectOracleAccount","msg":"Address of the provided oracle account is incorrect"},{"code":6026,"name":"IncorrectConfigAccount","msg":"Address of the provided config account is incorrect"},{"code":6027,"name":"UnsupportedCurveType","msg":"The provided curve type is not supported by the program owner"},{"code":6028,"name":"InvalidCurve","msg":"The provided curve parameters are invalid"},{"code":6029,"name":"UnsupportedCurveOperation","msg":"The operation cannot be performed on the given curve"},{"code":6030,"name":"InvalidPythStatus","msg":"Pyth oracle status is not 'trading'"},{"code":6031,"name":"InvalidPythPrice","msg":"Could not retrieve updated price feed from the Pyth oracle"},{"code":6032,"name":"IncorrectSigner","msg":"Address of the provided signer account is incorrect"},{"code":6033,"name":"ExceedPoolBalance","msg":"Swap amount exceeds pool balance"},{"code":6034,"name":"ProgramIsFrozen","msg":"Program is frozen"},{"code":6035,"name":"OracleConfidence","msg":"Oracle confidence is too high"},{"code":6036,"name":"OverCapAmount","msg":"Over Pool Cap Amount"},{"code":6037,"name":"InvalidUpdateAccount","msg":"Invalid update wallet address"},{"code":6038,"name":"InvalidUpdateParam","msg":"Invalid update param"},{"code":6039,"name":"InvalidInnerSwapAccount","msg":"Invalid inner swap account"},{"code":6040,"name":"ExceptionErr","msg":"Exception err"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/marginfi_v2.MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA.json b/migration/khadhroony-bot2-reference/idls/marginfi_v2.MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA.json new file mode 100644 index 0000000..c829799 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/marginfi_v2.MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA.json @@ -0,0 +1 @@ +{"address":"MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA","metadata":{"name":"marginfi","version":"0.1.8","spec":"0.1.0","description":"Borrow Lending Prime Broker"},"instructions":[{"name":"config_group_fee","docs":["(global fee admin only) Enable or disable program fees for any group. Does not require the","group admin to sign: the global fee state admin can turn program fees on or off for any","group"],"discriminator":[231,205,66,242,220,87,145,38],"accounts":[{"name":"marginfi_group","writable":true},{"name":"global_fee_admin","docs":["`global_fee_admin` of the FeeState"],"signer":true,"relations":["fee_state"]},{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}}],"args":[{"name":"enable_program_fee","type":"bool"}]},{"name":"configure_bank_rate_limits","docs":["(admin or delegate_limit_admin) Configure bank-level rate limits for withdraw/borrow.","Rate limits track net outflow in native tokens. Deposits offset withdraws.","Set to 0 to disable. Hourly and daily windows are independent."],"discriminator":[175,84,85,221,206,220,110,174],"accounts":[{"name":"group","relations":["bank"]},{"name":"admin","signer":true},{"name":"bank","writable":true}],"args":[{"name":"hourly_max_outflow","type":{"option":"u64"}},{"name":"daily_max_outflow","type":{"option":"u64"}}]},{"name":"configure_deleverage_withdrawal_limit","docs":["(admin or delegate_limit_admin) Set the daily withdrawal limit for deleverages per group."],"discriminator":[28,132,205,158,67,77,177,63],"accounts":[{"name":"marginfi_group","writable":true},{"name":"admin","signer":true}],"args":[{"name":"limit","type":"u32"}]},{"name":"configure_group_rate_limits","docs":["(admin or delegate_limit_admin) Configure group-level rate limits for withdraw/borrow.","Rate limits track aggregate net outflow in USD.","Example: $10M = 10_000_000. Set to 0 to disable."],"discriminator":[111,47,213,142,158,51,226,102],"accounts":[{"name":"marginfi_group","writable":true},{"name":"admin","signer":true}],"args":[{"name":"hourly_max_outflow_usd","type":{"option":"u64"}},{"name":"daily_max_outflow_usd","type":{"option":"u64"}}]},{"name":"drift_deposit","docs":["(user) Deposit into a Drift spot market through a marginfi account","* amount - in the underlying token (e.g., USDC), in native decimals"],"discriminator":[252,63,250,201,98,55,130,12],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"drift_oracle","docs":["The oracle account for the asset (not needed if using oracle type QuoteAsset)"],"optional":true},{"name":"liquidity_vault_authority","docs":["The bank's liquidity vault authority, which owns the Drift user account"],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["Used as an intermediary to deposit tokens into Drift"],"writable":true,"relations":["bank"]},{"name":"signer_token_account","docs":["Owned by authority, the source account for the token deposit"],"writable":true},{"name":"drift_state","docs":["The Drift state account"]},{"name":"integration_acc_2","docs":["The Drift user account owned by liquidity_vault_authority"],"writable":true,"relations":["bank"]},{"name":"integration_acc_3","docs":["The Drift user stats account owned by liquidity_vault_authority"],"writable":true,"relations":["bank"]},{"name":"integration_acc_1","docs":["The Drift spot market for this asset"],"writable":true,"relations":["bank"]},{"name":"drift_spot_market_vault","docs":["The Drift spot market vault that will receive tokens"],"writable":true},{"name":"mint","docs":["Bank's liquidity token mint"],"relations":["bank"]},{"name":"drift_program","address":"dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH"},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"}]},{"name":"drift_harvest_reward","docs":["(permissionless) Harvest rewards from admin deposits in Drift spot markets.","Rewards are always sent to the global fee wallet's canonical ATA.","The harvest spot market must be different from the bank's main drift spot market."],"discriminator":[167,161,240,194,138,54,87,189],"accounts":[{"name":"bank"},{"name":"fee_state","docs":["Global fee state that contains the global_fee_wallet"],"pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"liquidity_vault_authority","docs":["The bank's liquidity vault authority"],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"intermediary_token_account","docs":["To create this manually just send some of the reward token","to the liquidity vault authority address before claiming"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"liquidity_vault_authority"},{"kind":"account","path":"token_program"},{"kind":"account","path":"reward_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"destination_token_account","docs":["Destination token account must be owned by the global fee wallet"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_state"},{"kind":"account","path":"token_program"},{"kind":"account","path":"reward_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"drift_state","docs":["Drift accounts"]},{"name":"integration_acc_2","writable":true,"relations":["bank"]},{"name":"integration_acc_3","writable":true,"relations":["bank"]},{"name":"harvest_drift_spot_market","docs":["The harvest spot market - MUST be different from bank's Drift spot market (integration_acc_1)","This is the market that contains admin deposits to harvest"],"writable":true},{"name":"harvest_drift_spot_market_vault","docs":["The harvest spot market vault - derived from harvest_drift_spot_market"],"writable":true},{"name":"drift_signer","docs":["The Drift signer PDA"]},{"name":"reward_mint"},{"name":"drift_program","address":"dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH"},{"name":"token_program"}],"args":[]},{"name":"drift_init_user","docs":["(permissionless) Initialize a Drift user and user stats for a marginfi bank","Creates user with sub_account_id = 0 and empty name","Requires a minimum deposit to ensure the account remains active","* amount - minimum deposit amount (at least 10 units) in native decimals"],"discriminator":[29,18,236,190,29,254,114,169],"accounts":[{"name":"fee_payer","docs":["Pays to init the drift user and user stats accounts and provides initial deposit"],"writable":true,"signer":true},{"name":"signer_token_account","docs":["The fee payer must provide a nominal amount of bank tokens so the account is not empty.","This amount is irrecoverable and will prevent the account from being closed."],"writable":true},{"name":"bank"},{"name":"liquidity_vault_authority","docs":["The liquidity vault authority (PDA that will own the Drift user)"],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["Used as an intermediary to deposit a nominal amount of token into Drift."],"writable":true,"relations":["bank"]},{"name":"mint","docs":["Bank's liquidity token mint (e.g., USDC)"],"writable":true,"relations":["bank"]},{"name":"integration_acc_3","docs":["The user stats account to be created"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,115,116,97,116,115]},{"kind":"account","path":"liquidity_vault_authority"}],"program":{"kind":"const","value":[9,84,219,190,158,201,96,201,138,122,41,63,226,19,54,150,111,225,128,209,81,174,75,129,121,86,31,137,133,74,83,246]}},"relations":["bank"]},{"name":"integration_acc_2","docs":["The user account to be created (sub_account_id = 0)"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114]},{"kind":"account","path":"liquidity_vault_authority"},{"kind":"const","value":[0,0]}],"program":{"kind":"const","value":[9,84,219,190,158,201,96,201,138,122,41,63,226,19,54,150,111,225,128,209,81,174,75,129,121,86,31,137,133,74,83,246]}},"relations":["bank"]},{"name":"drift_state","writable":true},{"name":"integration_acc_1","writable":true,"relations":["bank"]},{"name":"drift_spot_market_vault","docs":["The Drift spot market vault where tokens will be deposited"],"writable":true},{"name":"drift_oracle","docs":["Oracle for the asset (can be null for USDC/market 0)"],"optional":true},{"name":"drift_program","address":"dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH"},{"name":"token_program"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"}]},{"name":"drift_withdraw","docs":["(user) Withdraw from a Drift spot market through a marginfi account","* amount - in the underlying token (e.g., USDC), in native decimals","* withdraw_all - if true, withdraws entire position"],"discriminator":[86,59,186,123,183,181,234,137],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"drift_oracle","docs":["The oracle account for the asset (not needed if using oracle type QuoteAsset)"],"optional":true},{"name":"liquidity_vault_authority","docs":["The bank's liquidity vault authority, which owns the Drift user account"],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["Receives tokens from Drift withdrawal"],"writable":true,"relations":["bank"]},{"name":"destination_token_account","docs":["Token account that will receive the withdrawn tokens"],"writable":true},{"name":"drift_state","docs":["The Drift state account"]},{"name":"integration_acc_2","docs":["The Drift user account owned by liquidity_vault_authority"],"writable":true,"relations":["bank"]},{"name":"integration_acc_3","docs":["The Drift user stats account owned by liquidity_vault_authority"],"writable":true,"relations":["bank"]},{"name":"integration_acc_1","docs":["The Drift spot market for this asset"],"writable":true,"relations":["bank"]},{"name":"drift_spot_market_vault","docs":["The Drift spot market vault that holds tokens"],"writable":true},{"name":"drift_reward_oracle","docs":["Optional: Oracle for first reward asset (only needed if rewards exist)"],"optional":true},{"name":"drift_reward_spot_market","docs":["Optional: Spot market for first reward asset (only needed if rewards exist)"],"optional":true},{"name":"drift_reward_mint","docs":["Optional: Mint for first reward asset (only needed if rewards exist)"],"optional":true},{"name":"drift_reward_oracle_2","docs":["Optional: Oracle for second reward asset (backup in case multiple rewards)"],"optional":true},{"name":"drift_reward_spot_market_2","docs":["Optional: Spot market for second reward asset (backup in case multiple rewards)"],"optional":true},{"name":"drift_reward_mint_2","docs":["Optional: Mint for second reward asset (backup in case multiple rewards)"],"optional":true},{"name":"drift_signer","docs":["The Drift signer PDA"]},{"name":"mint","docs":["Bank's liquidity token mint"],"relations":["bank"]},{"name":"drift_program","address":"dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH"},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"},{"name":"withdraw_all","type":{"option":"bool"}}]},{"name":"edit_global_fee_state","docs":["(global fee admin only) Adjust fees, admin, or the destination wallet"],"discriminator":[52,62,35,129,93,69,165,202],"accounts":[{"name":"global_fee_admin","docs":["Admin of the global FeeState"],"signer":true,"relations":["fee_state"]},{"name":"fee_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}}],"args":[{"name":"admin","type":"pubkey"},{"name":"fee_wallet","type":"pubkey"},{"name":"bank_init_flat_sol_fee","type":"u32"},{"name":"liquidation_flat_sol_fee","type":"u32"},{"name":"order_init_flat_sol_fee","type":"u32"},{"name":"program_fee_fixed","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"program_fee_rate","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liquidation_max_fee","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"order_execution_max_fee","type":{"defined":{"name":"WrappedI80F48"}}}]},{"name":"edit_staked_settings","docs":["(admin only) Edit the staked collateral settings for the group."],"discriminator":[11,108,215,87,240,9,66,241],"accounts":[{"name":"marginfi_group","relations":["staked_settings"]},{"name":"admin","signer":true,"relations":["marginfi_group"]},{"name":"staked_settings","writable":true}],"args":[{"name":"settings","type":{"defined":{"name":"StakedSettingsEditConfig"}}}]},{"name":"end_deleverage","docs":["(risk_admin only) End forced deleverage. Validates health did not worsen."],"discriminator":[114,14,250,143,252,104,214,209],"accounts":[{"name":"marginfi_account","writable":true},{"name":"liquidation_record","writable":true,"relations":["marginfi_account"]},{"name":"group","relations":["marginfi_account"]},{"name":"risk_admin","signer":true,"relations":["group"]}],"args":[]},{"name":"end_liquidation","docs":["(liquidation_receiver, set in start_liquidation) End receivership liquidation. Validates","health improved and seized assets are within fee limits. Charges a flat SOL fee."],"discriminator":[110,11,244,54,229,181,22,184],"accounts":[{"name":"marginfi_account","docs":["Account under liquidation"],"writable":true},{"name":"liquidation_record","docs":["The associated liquidation record PDA for the given `marginfi_account`"],"writable":true,"relations":["marginfi_account"]},{"name":"liquidation_receiver","writable":true,"signer":true,"relations":["liquidation_record"]},{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"global_fee_wallet","writable":true,"relations":["fee_state"]},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"init_bank_metadata","docs":["(permissionless) pay the rent to open a bank's metadata."],"discriminator":[94,239,50,136,137,204,254,213],"accounts":[{"name":"bank"},{"name":"fee_payer","docs":["Pays the init fee"],"writable":true,"signer":true},{"name":"metadata","docs":["Note: unique per-bank."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"account","path":"bank"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"init_global_fee_state","docs":["(Runs once per program) Configures the fee state account, where the global admin sets fees","that are assessed to the protocol"],"discriminator":[82,48,247,59,220,109,231,44],"accounts":[{"name":"payer","docs":["Pays the init fee"],"writable":true,"signer":true},{"name":"fee_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"admin","type":"pubkey"},{"name":"fee_wallet","type":"pubkey"},{"name":"bank_init_flat_sol_fee","type":"u32"},{"name":"liquidation_flat_sol_fee","type":"u32"},{"name":"order_init_flat_sol_fee","type":"u32"},{"name":"program_fee_fixed","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"program_fee_rate","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liquidation_max_fee","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"order_execution_max_fee","type":{"defined":{"name":"WrappedI80F48"}}}]},{"name":"init_staked_settings","docs":["(group admin only) Init the Staked Settings account, which is used to create staked","collateral banks, and must run before any staked collateral bank can be created with","`add_pool_permissionless`. Running this ix effectively opts the group into the staked","collateral feature."],"discriminator":[52,35,149,44,69,86,69,80],"accounts":[{"name":"marginfi_group"},{"name":"admin","signer":true,"relations":["marginfi_group"]},{"name":"fee_payer","docs":["Pays the init fee"],"writable":true,"signer":true},{"name":"staked_settings","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,101,100,95,115,101,116,116,105,110,103,115]},{"kind":"account","path":"marginfi_group"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"settings","type":{"defined":{"name":"StakedSettingsConfig"}}}]},{"name":"juplend_deposit","docs":["(user) Deposit into a JupLend lending pool through a marginfi account.","* amount - in the underlying token (e.g., USDC), in native decimals"],"discriminator":[114,11,218,81,183,165,143,255],"accounts":[{"name":"group","writable":true,"relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"signer_token_account","docs":["Owned by authority, the source account for the token deposit."],"writable":true},{"name":"liquidity_vault_authority","docs":["The bank's liquidity vault authority PDA (acts as signer for JupLend CPIs).","NOTE: JupLend marks the signer as writable in their deposit instruction."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["Bank liquidity vault (holds underlying mint and is used as depositor_token_account)."],"writable":true,"relations":["bank"]},{"name":"mint","docs":["Underlying mint."],"relations":["bank"]},{"name":"integration_acc_1","docs":["JupLend lending state account."],"writable":true,"relations":["bank"]},{"name":"f_token_mint","docs":["JupLend fToken mint."],"writable":true,"relations":["integration_acc_1"]},{"name":"integration_acc_2","docs":["Bank's fToken vault (validated via has_one on bank)."],"writable":true,"relations":["bank"]},{"name":"lending_admin"},{"name":"supply_token_reserves_liquidity","writable":true},{"name":"lending_supply_position_on_liquidity","writable":true},{"name":"rate_model"},{"name":"vault","writable":true},{"name":"liquidity","writable":true},{"name":"liquidity_program"},{"name":"rewards_rate_model"},{"name":"juplend_program","address":"jup3YeL8QhtSx1e253b2FDvsMNC87fDrgQZivbrndc9"},{"name":"token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"}]},{"name":"juplend_init_position","docs":["(permissionless) Initialize the bank-level JupLend position.","","This creates the bank's fToken ATA (owned by the bank liquidity vault authority) and","performs a nominal seed deposit into JupLend, then flips the bank from `Paused` to","`Operational`."],"discriminator":[176,255,151,106,5,207,74,215],"accounts":[{"name":"fee_payer","docs":["Provides a nominal deposit amount."],"writable":true,"signer":true},{"name":"signer_token_account","docs":["Token account owned by the fee payer holding the underlying mint."],"writable":true},{"name":"bank","writable":true},{"name":"liquidity_vault_authority","docs":["The bank's liquidity vault authority PDA (acts as signer for JupLend CPIs)."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["Bank liquidity vault (holds underlying mint and is used as depositor_token_account)."],"writable":true,"relations":["bank"]},{"name":"mint","docs":["Underlying mint (must match bank mint and JupLend lending state mint)."],"relations":["bank"]},{"name":"integration_acc_1","docs":["JupLend lending state account."],"writable":true,"relations":["bank"]},{"name":"f_token_mint","docs":["JupLend fToken mint."],"writable":true,"relations":["integration_acc_1"]},{"name":"integration_acc_2","docs":["Bank's fToken vault (validated via has_one on bank)."],"writable":true,"relations":["bank"]},{"name":"lending_admin"},{"name":"supply_token_reserves_liquidity","writable":true},{"name":"lending_supply_position_on_liquidity","writable":true},{"name":"rate_model"},{"name":"vault","writable":true},{"name":"liquidity","writable":true},{"name":"liquidity_program"},{"name":"rewards_rate_model"},{"name":"juplend_program","address":"jup3YeL8QhtSx1e253b2FDvsMNC87fDrgQZivbrndc9"},{"name":"token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"}]},{"name":"juplend_withdraw","docs":["(user) Withdraw from a JupLend lending pool through a marginfi account.","* amount - in the underlying token (e.g., USDC), in native decimals"],"discriminator":[245,164,253,202,53,77,251,221],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"destination_token_account","docs":["Token account that will receive the underlying withdrawal.","WARN: Completely unchecked!"],"writable":true},{"name":"liquidity_vault_authority","docs":["The bank's liquidity vault authority PDA (acts as signer for JupLend CPIs).","NOTE: JupLend marks the signer as writable in their withdraw instruction."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"mint","docs":["Underlying mint."],"relations":["bank"]},{"name":"integration_acc_1","docs":["JupLend lending state account."],"writable":true,"relations":["bank"]},{"name":"f_token_mint","docs":["JupLend fToken mint."],"writable":true,"relations":["integration_acc_1"]},{"name":"integration_acc_2","docs":["Bank's fToken vault (validated via has_one on bank)."],"writable":true,"relations":["bank"]},{"name":"integration_acc_3","docs":["Withdraw intermediary ATA (authority = liquidity_vault_authority).","This must be an ATA to satisfy JupLend's withdraw constraints."],"writable":true,"relations":["bank"]},{"name":"lending_admin"},{"name":"supply_token_reserves_liquidity","writable":true},{"name":"lending_supply_position_on_liquidity","writable":true},{"name":"rate_model"},{"name":"vault","writable":true},{"name":"claim_account","docs":["JupLend claim account for liquidity_vault_authority.","TEMPORARY: Mainnet currently requires this account (passing None causes ConstraintMut errors),","but an upcoming upgrade is expected to make it truly optional. The account is never actually","validated or used - you can pass any mutable account. We create the canonical PDA for consistency.","Seeds: [\"user_claim\", liquidity_vault_authority, mint] on Liquidity program."],"writable":true},{"name":"liquidity","writable":true},{"name":"liquidity_program"},{"name":"rewards_rate_model"},{"name":"juplend_program","address":"jup3YeL8QhtSx1e253b2FDvsMNC87fDrgQZivbrndc9"},{"name":"token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"},{"name":"withdraw_all","type":{"option":"bool"}}]},{"name":"kamino_deposit","docs":["(user) Deposit into a Kamino pool through a marginfi account","* amount - in the liquidity token (e.g. if there is a Kamino USDC bank, pass the amount of","USDC desired), in native decimals."],"discriminator":[237,8,188,187,115,99,49,85],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"signer_token_account","docs":["Owned by authority, the source account for the token deposit."],"writable":true},{"name":"liquidity_vault_authority","docs":["The bank's liquidity vault authority, which owns the Kamino obligation. Note: Kamino needs","this to be mut because `deposit` might return the rent here"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["Used as an intermediary to deposit token into Kamino"],"writable":true,"relations":["bank"]},{"name":"integration_acc_2","writable":true,"relations":["bank"]},{"name":"lending_market"},{"name":"lending_market_authority"},{"name":"integration_acc_1","docs":["The Kamino reserve that holds liquidity"],"writable":true,"relations":["bank"]},{"name":"mint","docs":["Bank's liquidity token mint (e.g., USDC). Kamino calls this the `reserve_liquidity_mint`"],"relations":["bank"]},{"name":"reserve_liquidity_supply","writable":true},{"name":"reserve_collateral_mint","docs":["The reserve's mint for tokenized representations of Kamino deposits."],"writable":true},{"name":"reserve_destination_deposit_collateral","docs":["The reserve's destination for tokenized representations of deposits. Note: the","`reserve_collateral_mint` will mint tokens directly to this account."],"writable":true},{"name":"obligation_farm_user_state","docs":["Required if the Kamino reserve has an active farm."],"writable":true,"optional":true},{"name":"reserve_farm_state","docs":["Required if the Kamino reserve has an active farm."],"writable":true,"optional":true},{"name":"kamino_program","address":"KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD"},{"name":"farms_program","docs":["Farms program for Kamino staking functionality"],"address":"FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr"},{"name":"collateral_token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"liquidity_token_program"},{"name":"instruction_sysvar_account","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[{"name":"amount","type":"u64"}]},{"name":"kamino_harvest_reward","docs":["(permissionless) Harvest the specified reward index from the Kamino Farm attached to this","bank. Rewards are always sent to the global fee wallet's canonical ATA.","","* `reward_index` — index of the reward token in the Kamino Farm's reward list"],"discriminator":[163,202,248,141,106,20,116,5],"accounts":[{"name":"bank"},{"name":"fee_state","docs":["Global fee state that contains the global_fee_admin"],"pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"destination_token_account","docs":["Destination token account must be owned by the global fee admin"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_state"},{"kind":"account","path":"token_program"},{"kind":"account","path":"reward_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"liquidity_vault_authority","docs":["The bank's liquidity vault authority, which owns the Kamino obligation."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"user_state","writable":true},{"name":"farm_state","writable":true},{"name":"global_config"},{"name":"reward_mint"},{"name":"user_reward_ata","docs":["An initialized ATA of type reward mint owned by liquidity vault"],"writable":true},{"name":"rewards_vault","writable":true},{"name":"rewards_treasury_vault","writable":true},{"name":"farm_vaults_authority"},{"name":"scope_prices","optional":true},{"name":"farms_program","address":"FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr"},{"name":"token_program"}],"args":[{"name":"reward_index","type":"u64"}]},{"name":"kamino_init_obligation","docs":["(permissionless) Initialize a Kamino obligation for a marginfi bank","* amount - In token, in native decimals. Must be >10 (i.e. 10 lamports, not 10 tokens). Lost","forever. Generally, try to make this the equivalent of around $1, in case Kamino ever","rounds small balances down to zero."],"discriminator":[253,177,160,225,70,156,217,109],"accounts":[{"name":"fee_payer","docs":["Pays to init the obligation and pays a nominal amount to ensure the obligation has a","non-zero balance."],"writable":true,"signer":true},{"name":"bank"},{"name":"signer_token_account","docs":["The fee payer must provide a nominal amount of bank tokens so the obligation is not empty.","This amount is irrecoverable and and will prevent the obligation from ever being closed,","even if the bank is otherwise empty (Kamino normally closes empty obligations automatically)"],"writable":true},{"name":"liquidity_vault_authority","docs":["The liquidity vault authority (PDA that will own the Kamino obligation). Note: Kamino needs","this to be mut because `deposit` might return the rent here"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["Used as an intermediary to deposit a nominal amount of token into the obligation."],"writable":true,"relations":["bank"]},{"name":"integration_acc_2","docs":["The obligation account to be created. Note that the key was already derived when","initializing the bank, and this must match the obligation recorded at that time."],"writable":true,"relations":["bank"]},{"name":"user_metadata","writable":true},{"name":"lending_market"},{"name":"lending_market_authority"},{"name":"integration_acc_1","writable":true,"relations":["bank"]},{"name":"mint","docs":["Bank's liquidity token mint (e.g., USDC). Kamino calls this the `reserve_liquidity_mint`"],"writable":true,"relations":["bank"]},{"name":"reserve_liquidity_supply","writable":true},{"name":"reserve_collateral_mint","docs":["The reserve's mint for tokenized representations of Kamino deposits."],"writable":true},{"name":"reserve_destination_deposit_collateral","docs":["The reserve's destination for tokenized representations of deposits. Note: the","`reserve_collateral_mint` will mint tokens directly to this account."],"writable":true},{"name":"pyth_oracle","optional":true},{"name":"switchboard_price_oracle","optional":true},{"name":"switchboard_twap_oracle","optional":true},{"name":"scope_prices","optional":true},{"name":"obligation_farm_user_state","docs":["Required if the Kamino reserve has an active farm."],"writable":true,"optional":true},{"name":"reserve_farm_state","docs":["Required if the Kamino reserve has an active farm."],"writable":true,"optional":true},{"name":"kamino_program","address":"KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD"},{"name":"farms_program","docs":["Farms program for Kamino staking functionality"],"address":"FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr"},{"name":"collateral_token_program","docs":["Note: the collateral token always uses Token classic, never Token22."],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"liquidity_token_program","docs":["Note: Kamino does not have full Token22 support, certain Token22 features are disallowed.","Expect this to update over time. Check with the Kamino source."]},{"name":"instruction_sysvar_account","address":"Sysvar1nstructions1111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"}]},{"name":"kamino_withdraw","docs":["(user) Withdraw from a Kamino pool through a marginfi account","* amount - in the collateral token (NOT liquidity token), in native decimals. Must convert","from collateral to liquidity token amounts using the current exchange rate.","* withdraw_all - if true, withdraw the entire mrgn balance (Note: due to rounding down, a","deposit and withdraw back to back may result in several lamports less)"],"discriminator":[199,101,41,45,213,98,224,200],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"destination_token_account","docs":["Token account that will receive the withdrawn tokens. Mint/owner are validated by the","SPL transfer; the caller controls the destination."],"writable":true},{"name":"liquidity_vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]},"relations":["bank"]},{"name":"integration_acc_2","writable":true,"relations":["bank"]},{"name":"lending_market","docs":["The Kamino lending market"]},{"name":"lending_market_authority","docs":["The Kamino lending market authority"]},{"name":"integration_acc_1","docs":["The Kamino reserve that holds liquidity"],"writable":true,"relations":["bank"]},{"name":"mint","docs":["The liquidity token mint (e.g., USDC)","Needs serde to get the mint decimals for transfer checked","TODO: rename to just 'mint' to make use of has_one and to be consistent with deposit"],"writable":true,"relations":["bank"]},{"name":"reserve_liquidity_supply","docs":["The reserve's liquidity supply account"],"writable":true},{"name":"reserve_collateral_mint","docs":["The reserve's collateral mint"],"writable":true},{"name":"reserve_source_collateral","docs":["The reserve's source for collateral tokens"],"writable":true},{"name":"obligation_farm_user_state","docs":["Optional farms accounts for Kamino staking functionality"],"writable":true,"optional":true},{"name":"reserve_farm_state","writable":true,"optional":true},{"name":"kamino_program","address":"KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD"},{"name":"farms_program","docs":["Farms program for Kamino staking functionality"],"address":"FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr"},{"name":"collateral_token_program","docs":["The token program for the collateral token"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"liquidity_token_program","docs":["The token program for the liquidity token"]},{"name":"instruction_sysvar_account","docs":["Used by kamino validate CPI calls"],"address":"Sysvar1nstructions1111111111111111111111111"}],"args":[{"name":"amount","type":"u64"},{"name":"withdraw_all","type":{"option":"bool"}}]},{"name":"lending_account_borrow","docs":["(account authority) Borrow assets from a bank. Accrues interest, records liability, applies","origination fee, transfers tokens, and runs a health check."],"discriminator":[4,126,116,53,48,5,212,31],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"destination_token_account","writable":true},{"name":"bank_liquidity_vault_authority","pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"relations":["bank"]},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"}]},{"name":"lending_account_clear_emissions","docs":["(permissionless) Zero out `emissions_outstanding` on a balance after emissions are disabled","on the bank."],"discriminator":[239,4,221,98,45,167,201,244],"accounts":[{"name":"marginfi_account","writable":true},{"name":"bank"}],"args":[]},{"name":"lending_account_close_balance","docs":["(account authority) Close a balance position with dust-level amounts."],"discriminator":[245,54,41,4,243,202,31,17],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true}],"args":[]},{"name":"lending_account_deposit","docs":["(account authority) Deposit assets into a bank. Accrues interest, records deposit, and","transfers tokens from the signer's token account to the bank's liquidity vault."],"discriminator":[171,94,235,103,82,64,212,140],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"signer_token_account","writable":true},{"name":"liquidity_vault","writable":true,"relations":["bank"]},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"},{"name":"deposit_up_to_limit","type":{"option":"bool"}}]},{"name":"lending_account_end_flashloan","docs":["(account authority) End a flash loan and run the health check."],"discriminator":[105,124,201,106,153,2,8,156],"accounts":[{"name":"marginfi_account","writable":true},{"name":"authority","signer":true,"relations":["marginfi_account"]}],"args":[]},{"name":"lending_account_liquidate","docs":["(permissionless) Liquidate a lending account balance of an unhealthy marginfi account.","The liquidator takes on the liability and receives discounted collateral (2.5% liquidator","fee + 2.5% insurance fee).","* `asset_amount` - amount of collateral to liquidate","* `liquidatee_accounts` - number of remaining accounts for the liquidatee","* `liquidator_accounts` - number of remaining accounts for the liquidator"],"discriminator":[214,169,151,213,251,167,86,219],"accounts":[{"name":"group","relations":["asset_bank","liab_bank","liquidator_marginfi_account","liquidatee_marginfi_account"]},{"name":"asset_bank","writable":true},{"name":"liab_bank","writable":true},{"name":"liquidator_marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"liquidatee_marginfi_account","writable":true},{"name":"bank_liquidity_vault_authority","pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"liab_bank"}]}},{"name":"bank_liquidity_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"liab_bank"}]}},{"name":"bank_insurance_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"liab_bank"}]}},{"name":"token_program"}],"args":[{"name":"asset_amount","type":"u64"},{"name":"liquidatee_accounts","type":"u8"},{"name":"liquidator_accounts","type":"u8"}]},{"name":"lending_account_pulse_health","docs":["(Permissionless) Refresh the internal risk engine health cache. Useful for liquidators and","other consumers that want to see the internal risk state of a user account. This cache is","read-only and serves no purpose except being populated by this ix.","* remaining accounts expected in the same order as borrow, etc. I.e., for each balance the","user has, pass bank and oracle: "],"discriminator":[186,52,117,97,34,74,39,253],"accounts":[{"name":"marginfi_account","writable":true}],"args":[]},{"name":"lending_account_repay","docs":["(account authority, or any signer during receivership) Repay borrowed assets. Accrues","interest, records repayment, and transfers tokens to the bank's liquidity vault."],"discriminator":[79,209,172,177,222,51,173,151],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","docs":["Must be marginfi_account's authority, unless in liquidation/deleverage receivership or order execution","","Note: during receivership and order execution, there are no signer checks whatsoever: any key can repay as","long as the invariants checked at the end of execution are met."],"signer":true},{"name":"bank","writable":true},{"name":"signer_token_account","writable":true},{"name":"liquidity_vault","writable":true,"relations":["bank"]},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"},{"name":"repay_all","type":{"option":"bool"}}]},{"name":"lending_account_start_flashloan","docs":["(account authority) Start a flash loan. Must have a corresponding `end_flashloan` ix in the","same tx. Health checks are skipped until the flash loan ends."],"discriminator":[14,131,33,220,81,186,180,107],"accounts":[{"name":"marginfi_account","writable":true},{"name":"authority","signer":true,"relations":["marginfi_account"]},{"name":"ixs_sysvar","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[{"name":"end_index","type":"u64"}]},{"name":"lending_account_withdraw","docs":["(account authority, or any signer during receivership) Withdraw assets from a bank. Accrues","interest, records withdrawal, transfers tokens, and runs a health check (skipped during","receivership)."],"discriminator":[36,72,74,19,210,210,192,192],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","docs":["Must be marginfi_account's authority, unless in liquidation/deleverage receivership or order execution","","Note: during receivership and order execution, there are no signer checks whatsoever: any key can repay as","long as the invariants checked at the end of execution are met."],"signer":true},{"name":"bank","writable":true},{"name":"destination_token_account","writable":true},{"name":"bank_liquidity_vault_authority","pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"relations":["bank"]},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"},{"name":"withdraw_all","type":{"option":"bool"}}]},{"name":"lending_pool_accrue_bank_interest","docs":["(permissionless) Accrue interest on a bank, updating share values and collecting fees."],"discriminator":[108,201,30,87,47,65,97,188],"accounts":[{"name":"group","relations":["bank"]},{"name":"bank","writable":true}],"args":[]},{"name":"lending_pool_add_bank","docs":["(admin only) Add a new bank to the lending pool"],"discriminator":[215,68,72,78,208,218,103,182],"accounts":[{"name":"marginfi_group","writable":true},{"name":"admin","signer":true,"relations":["marginfi_group"]},{"name":"fee_payer","docs":["Pays to init accounts and pays `fee_state.bank_init_flat_sol_fee` lamports to the protocol"],"writable":true,"signer":true},{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"global_fee_wallet","writable":true,"relations":["fee_state"]},{"name":"bank_mint"},{"name":"bank","writable":true,"signer":true},{"name":"liquidity_vault_authority","pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bank_config","type":{"defined":{"name":"BankConfigCompact"}}}]},{"name":"lending_pool_add_bank_drift","docs":["(group admin only) Add a Drift bank to the group."],"discriminator":[62,63,49,48,76,55,108,155],"accounts":[{"name":"group","writable":true},{"name":"admin","signer":true,"relations":["group"]},{"name":"fee_payer","writable":true,"signer":true},{"name":"bank_mint","docs":["Must match the mint used by `integration_acc_1`"]},{"name":"bank","writable":true,"pda":{"seeds":[{"kind":"account","path":"group"},{"kind":"account","path":"bank_mint"},{"kind":"arg","path":"bank_seed"}]}},{"name":"integration_acc_1","docs":["Drift spot market account that must match the bank mint"]},{"name":"integration_acc_2","docs":["Drift user account for the marginfi program (derived from liquidity_vault_authority)"],"pda":{"seeds":[{"kind":"const","value":[117,115,101,114]},{"kind":"account","path":"liquidity_vault_authority"},{"kind":"const","value":[0,0]}],"program":{"kind":"const","value":[9,84,219,190,158,201,96,201,138,122,41,63,226,19,54,150,111,225,128,209,81,174,75,129,121,86,31,137,133,74,83,246]}}},{"name":"integration_acc_3","docs":["Drift user stats account for the marginfi program (derived from liquidity_vault_authority)"],"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,115,116,97,116,115]},{"kind":"account","path":"liquidity_vault_authority"}],"program":{"kind":"const","value":[9,84,219,190,158,201,96,201,138,122,41,63,226,19,54,150,111,225,128,209,81,174,75,129,121,86,31,137,133,74,83,246]}}},{"name":"liquidity_vault_authority","docs":["Will be authority of the bank's liquidity vault. Used as intermediary for deposits/withdraws"],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["For Drift banks, the `liquidity_vault` never holds assets, but is instead used as an","intermediary when depositing/withdrawing, e.g., withdrawn funds move from Drift -> here ->","the user's token account."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","docs":["Note: Currently does nothing."],"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","docs":["Note: Currently does nothing."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","docs":["Note: Currently does nothing."],"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault","docs":["Note: Currently does nothing."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bank_config","type":{"defined":{"name":"DriftConfigCompact"}}},{"name":"bank_seed","type":"u64"}]},{"name":"lending_pool_add_bank_juplend","docs":["(admin) Add a JupLend bank to the marginfi group.","","Remaining accounts (for oracle validation):","0. underlying oracle feed (pyth push or switchboard pull)","1. JupLend `Lending` state"],"discriminator":[18,208,117,90,53,111,195,41],"accounts":[{"name":"group","writable":true},{"name":"admin","signer":true,"relations":["group"]},{"name":"fee_payer","writable":true,"signer":true},{"name":"bank_mint","docs":["Must match the mint used by the JupLend lending state."]},{"name":"bank","writable":true,"pda":{"seeds":[{"kind":"account","path":"group"},{"kind":"account","path":"bank_mint"},{"kind":"arg","path":"bank_seed"}]}},{"name":"integration_acc_1","docs":["JupLend lending state account that must match the bank mint."]},{"name":"liquidity_vault_authority","docs":["Will be authority of the bank's liquidity vault. Used as intermediary for deposits/withdraws."],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["For JupLend banks, the `liquidity_vault` is used as an intermediary when depositing/","withdrawing, e.g., withdrawn funds move from JupLend -> here -> the user's token account."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","docs":["Note: Currently does nothing."],"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","docs":["Note: Currently does nothing."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"f_token_mint","relations":["integration_acc_1"]},{"name":"integration_acc_2","docs":["The bank's fToken vault holds the fTokens received when depositing into JupLend.",""],"writable":true,"pda":{"seeds":[{"kind":"const","value":[102,95,116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"token_program","docs":["Token program for both underlying mint and fToken mint (SPL Token or Token-2022).","JupLend creates fToken mints using the same token program as the underlying."]},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bank_config","type":{"defined":{"name":"JuplendConfigCompact"}}},{"name":"bank_seed","type":"u64"}]},{"name":"lending_pool_add_bank_kamino","docs":["(group admin only) Add a Kamino bank to the group. Pass the oracle and reserve in remaining","accounts 0 and 1 respectively."],"discriminator":[118,53,16,243,255,245,149,241],"accounts":[{"name":"group","writable":true},{"name":"admin","signer":true,"relations":["group"]},{"name":"fee_payer","writable":true,"signer":true},{"name":"bank_mint","docs":["Must match the mint used by the Kamino reserve (integration_acc_1), Kamino calls this the","`reserve_liquidity_mint` aka `liquidity.mint_pubkey`"]},{"name":"bank","writable":true,"pda":{"seeds":[{"kind":"account","path":"group"},{"kind":"account","path":"bank_mint"},{"kind":"arg","path":"bank_seed"}]}},{"name":"integration_acc_1"},{"name":"integration_acc_2","docs":["Note: not yet initialized in this instruction, run `init_obligation` after."]},{"name":"liquidity_vault_authority","docs":["Will be authority of the bank's Kamino obligation (integration_acc_2). Note: When","depositing/withdrawing Kamino assets, the source/destination must also be owned by the","obligation authority.","Kamino assets, the source/destination must also be owned by the obligation authority. This","account owns the `liquidity_vault`, and thus acts as intermediary for deposits/withdraws"],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["For Kamino banks, the `liquidity_vault` never holds assets, but is instead used as an","intermediary when depositing/withdrawing, e.g., withdrawn funds move from Kamino -> here ->","the user's token account."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","docs":["Note: Currently does nothing."],"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","docs":["Note: Currently does nothing."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","docs":["Note: Currently does nothing."],"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault","docs":["Note: Currently does nothing."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bank_config","type":{"defined":{"name":"KaminoConfigCompact"}}},{"name":"bank_seed","type":"u64"}]},{"name":"lending_pool_add_bank_permissionless","docs":["(permissionless) Add a staked collateral bank. Requires a valid SPL single-pool LST mint."],"discriminator":[127,187,121,34,187,167,238,102],"accounts":[{"name":"marginfi_group","writable":true,"relations":["staked_settings"]},{"name":"staked_settings"},{"name":"fee_payer","writable":true,"signer":true},{"name":"bank_mint","docs":["Mint of the spl-single-pool LST (a PDA derived from `stake_pool`)","","because the sol_pool and stake_pool will not derive to a valid PDA which is also owned by","the staking program and spl-single-pool program."]},{"name":"sol_pool"},{"name":"stake_pool","docs":["this key.","","If derives the same `bank_mint`, then this must be the correct stake pool for that mint, and","we can subsequently use it to validate the `sol_pool`"]},{"name":"bank","writable":true,"pda":{"seeds":[{"kind":"account","path":"marginfi_group"},{"kind":"account","path":"bank_mint"},{"kind":"arg","path":"bank_seed"}]}},{"name":"liquidity_vault_authority","pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bank_seed","type":"u64"}]},{"name":"lending_pool_add_bank_solend","docs":["(admin) Add a Solend bank to the marginfi group"],"discriminator":[81,233,203,199,47,226,0,68],"accounts":[{"name":"group","writable":true},{"name":"admin","signer":true,"relations":["group"]},{"name":"fee_payer","writable":true,"signer":true},{"name":"bank_mint","docs":["Must match the mint used by `integration_acc_1`, Solend calls this the `liquidity.mint_pubkey`"]},{"name":"bank","writable":true,"pda":{"seeds":[{"kind":"account","path":"group"},{"kind":"account","path":"bank_mint"},{"kind":"arg","path":"bank_seed"}]}},{"name":"integration_acc_1","docs":["Solend reserve account that must match the bank mint"]},{"name":"integration_acc_2","docs":["Obligation PDA for this bank in Solend","Will be initialized and transferred to Solend in init_obligation instruction"],"pda":{"seeds":[{"kind":"const","value":[115,111,108,101,110,100,95,111,98,108,105,103,97,116,105,111,110]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault_authority","docs":["Will be authority of the bank's liquidity vault. Used as intermediary for deposits/withdraws"],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["For Solend banks, the `liquidity_vault` never holds assets, but is instead used as an","intermediary when depositing/withdrawing, e.g., withdrawn funds move from Solend -> here ->","the user's token account."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","docs":["Note: Currently does nothing."],"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","docs":["Note: Currently does nothing."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","docs":["Note: Currently does nothing."],"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault","docs":["Note: Currently does nothing."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bank_config","type":{"defined":{"name":"SolendConfigCompact"}}},{"name":"bank_seed","type":"u64"}]},{"name":"lending_pool_add_bank_with_seed","docs":["(admin only) A copy of lending_pool_add_bank with an additional bank seed.","This seed is used to create a PDA for the bank's signature.","lending_pool_add_bank is preserved for backwards compatibility."],"discriminator":[76,211,213,171,117,78,158,76],"accounts":[{"name":"marginfi_group","writable":true},{"name":"admin","signer":true,"relations":["marginfi_group"]},{"name":"fee_payer","docs":["Pays to init accounts and pays `fee_state.bank_init_flat_sol_fee` lamports to the protocol"],"writable":true,"signer":true},{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"global_fee_wallet","writable":true,"relations":["fee_state"]},{"name":"bank_mint"},{"name":"bank","writable":true,"pda":{"seeds":[{"kind":"account","path":"marginfi_group"},{"kind":"account","path":"bank_mint"},{"kind":"arg","path":"bank_seed"}]}},{"name":"liquidity_vault_authority","pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bank_config","type":{"defined":{"name":"BankConfigCompact"}}},{"name":"bank_seed","type":"u64"}]},{"name":"lending_pool_clone_bank","docs":["(admin only) Staging or localnet only, panics on mainnet","This instruction is used to clone a bank to a new PDA."],"discriminator":[214,93,17,236,177,228,78,17],"accounts":[{"name":"marginfi_group","writable":true},{"name":"admin","writable":true,"signer":true,"relations":["marginfi_group"]},{"name":"fee_payer","writable":true,"signer":true},{"name":"bank_mint"},{"name":"source_bank","docs":["Source bank to clone from mainnet program",""]},{"name":"bank","writable":true,"pda":{"seeds":[{"kind":"account","path":"marginfi_group"},{"kind":"account","path":"bank_mint"},{"kind":"arg","path":"bank_seed"}]}},{"name":"liquidity_vault_authority","pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bank_seed","type":"u64"}]},{"name":"lending_pool_clone_emode","docs":["(admin or emode_admin) Copies emode settings from one bank to another. Useful when applying","emode settings from e.g. one LST to another."],"discriminator":[146,167,94,106,184,202,15,10],"accounts":[{"name":"group","relations":["copy_from_bank","copy_to_bank"]},{"name":"signer","signer":true},{"name":"copy_from_bank"},{"name":"copy_to_bank","writable":true}],"args":[]},{"name":"lending_pool_close_bank","docs":["(admin only) Close a bank. Requires CLOSE_ENABLED_FLAG and zero positions/shares."],"discriminator":[22,115,7,130,227,85,0,47],"accounts":[{"name":"group","writable":true,"relations":["bank"]},{"name":"bank","writable":true},{"name":"admin","writable":true,"signer":true,"relations":["group"]}],"args":[]},{"name":"lending_pool_collect_bank_fees","docs":["(permissionless) Transfer accrued fees from the liquidity vault to insurance/fee/program","vaults."],"discriminator":[201,5,215,116,230,92,75,150],"accounts":[{"name":"group","relations":["bank"]},{"name":"bank","writable":true},{"name":"liquidity_vault_authority","pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"fee_ata","docs":["(validated in handler). Must already exist, may require initializing the ATA if it does not","already exist prior to this ix."],"writable":true},{"name":"token_program"}],"args":[]},{"name":"lending_pool_configure_bank","docs":["(admin only) Configure bank parameters. If the bank has `FREEZE_SETTINGS`, only","deposit/borrow limits are updated and all other config changes are silently ignored."],"discriminator":[121,173,156,40,93,148,56,237],"accounts":[{"name":"group","relations":["bank"]},{"name":"admin","signer":true,"relations":["group"]},{"name":"bank","writable":true}],"args":[{"name":"bank_config_opt","type":{"defined":{"name":"BankConfigOpt"}}}]},{"name":"lending_pool_configure_bank_emode","docs":["(emode_admin only)"],"discriminator":[17,175,91,57,239,86,49,71],"accounts":[{"name":"group","relations":["bank"]},{"name":"emode_admin","signer":true,"relations":["group"]},{"name":"bank","writable":true}],"args":[{"name":"emode_tag","type":"u16"},{"name":"entries","type":{"array":[{"defined":{"name":"EmodeEntry"}},10]}}]},{"name":"lending_pool_configure_bank_interest_only","docs":["(delegate_curve_admin only) Update interest rate config. Does nothing if bank has","`FREEZE_SETTINGS`."],"discriminator":[245,107,83,38,103,219,163,241],"accounts":[{"name":"group","relations":["bank"]},{"name":"delegate_curve_admin","signer":true,"relations":["group"]},{"name":"bank","writable":true}],"args":[{"name":"interest_rate_config","type":{"defined":{"name":"InterestRateConfigOpt"}}}]},{"name":"lending_pool_configure_bank_limits_only","docs":["(delegate_limit_admin only) Update deposit/borrow/init limits only."],"discriminator":[157,196,221,200,202,62,84,21],"accounts":[{"name":"group","relations":["bank"]},{"name":"delegate_limit_admin","signer":true,"relations":["group"]},{"name":"bank","writable":true}],"args":[{"name":"deposit_limit","type":{"option":"u64"}},{"name":"borrow_limit","type":{"option":"u64"}},{"name":"total_asset_value_init_limit","type":{"option":"u64"}}]},{"name":"lending_pool_configure_bank_oracle","docs":["(admin only)"],"discriminator":[209,82,255,171,124,21,71,81],"accounts":[{"name":"group","relations":["bank"]},{"name":"admin","signer":true,"relations":["group"]},{"name":"bank","writable":true}],"args":[{"name":"setup","type":"u8"},{"name":"oracle","type":"pubkey"}]},{"name":"lending_pool_force_tokenless_repay_complete","docs":["(risk_admin only) - Signals all of a bank's liability have been deleveraged. Used if a bank","still has liability dust after the risk admin has completed deleveraging all debts. The","risk admin is trusted not to execute this until all non-dust debts have been deleveraged."],"discriminator":[15,203,147,232,199,14,231,37],"accounts":[{"name":"group","relations":["bank"]},{"name":"risk_admin","signer":true,"relations":["group"]},{"name":"bank","writable":true}],"args":[]},{"name":"lending_pool_handle_bankruptcy","docs":["(risk_admin or admin, unless `PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG` is set on the bank)","Handle bad debt of a bankrupt marginfi account for a given bank. Covers bad debt from the","insurance fund and socializes any remainder among depositors."],"discriminator":[162,11,56,139,90,128,70,173],"accounts":[{"name":"group","relations":["bank","marginfi_account"]},{"name":"signer","docs":["Must be risk_admin or admin, unless the bank has PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG","set, in which case any signer is accepted."],"signer":true},{"name":"bank","writable":true},{"name":"marginfi_account","writable":true},{"name":"liquidity_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"token_program"}],"args":[]},{"name":"lending_pool_pulse_bank_price_cache","docs":["(Permissionless) Refresh the cached oracle price for a bank."],"discriminator":[192,19,201,135,105,203,32,222],"accounts":[{"name":"group","relations":["bank"]},{"name":"bank","writable":true}],"args":[]},{"name":"lending_pool_reclaim_emissions_vault","docs":["(permissionless) Reclaim all remaining tokens from the emissions vault","to the global fee wallet ATA, and disable emissions on the bank."],"discriminator":[206,67,186,225,41,30,95,216],"accounts":[{"name":"group","relations":["bank"]},{"name":"bank","writable":true},{"name":"emissions_mint"},{"name":"emissions_auth","pda":{"seeds":[{"kind":"const","value":[101,109,105,115,115,105,111,110,115,95,97,117,116,104,95,115,101,101,100]},{"kind":"account","path":"bank"},{"kind":"account","path":"emissions_mint"}]}},{"name":"emissions_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[101,109,105,115,115,105,111,110,115,95,116,111,107,101,110,95,97,99,99,111,117,110,116,95,115,101,101,100]},{"kind":"account","path":"bank"},{"kind":"account","path":"emissions_mint"}]}},{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"destination_account","docs":["emissions mint (validated in handler)."],"writable":true},{"name":"token_program"}],"args":[]},{"name":"lending_pool_set_fixed_oracle_price","docs":["(admin only)"],"discriminator":[28,126,127,127,60,37,211,125],"accounts":[{"name":"group","relations":["bank"]},{"name":"admin","signer":true,"relations":["group"]},{"name":"bank","writable":true}],"args":[{"name":"price","type":{"defined":{"name":"WrappedI80F48"}}}]},{"name":"lending_pool_update_fees_destination_account","docs":["(admin only) Set the destination wallet for permissionless fee withdrawals."],"discriminator":[102,4,121,243,237,110,95,13],"accounts":[{"name":"group","relations":["bank"]},{"name":"bank","writable":true},{"name":"admin","signer":true,"relations":["group"]},{"name":"destination_account","docs":["Bank fees will be sent to this account which must be an ATA of the bank's mint."]}],"args":[]},{"name":"lending_pool_withdraw_fees","docs":["(admin only) Withdraw collected group fees from the fee vault."],"discriminator":[92,140,215,254,170,0,83,174],"accounts":[{"name":"group","relations":["bank"]},{"name":"bank"},{"name":"admin","signer":true,"relations":["group"]},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"dst_token_account","writable":true},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"}]},{"name":"lending_pool_withdraw_fees_permissionless","docs":["(permissionless) Withdraw group fees to the pre-configured `fees_destination_account`."],"discriminator":[57,245,1,208,130,18,145,113],"accounts":[{"name":"group","relations":["bank"]},{"name":"bank"},{"name":"fee_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"fee_vault_authority","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"fees_destination_account","writable":true,"relations":["bank"]},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"}]},{"name":"lending_pool_withdraw_insurance","docs":["(admin only) Withdraw from the insurance vault."],"discriminator":[108,60,60,246,104,79,159,243],"accounts":[{"name":"group","relations":["bank"]},{"name":"bank"},{"name":"admin","signer":true,"relations":["group"]},{"name":"insurance_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116]},{"kind":"account","path":"bank"}]}},{"name":"insurance_vault_authority","pda":{"seeds":[{"kind":"const","value":[105,110,115,117,114,97,110,99,101,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"dst_token_account","writable":true},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"}]},{"name":"marginfi_account_close","docs":["(account authority) Close a marginfi account. Requires all balances to be empty and no","active flags (disabled, flashloan, receivership)."],"discriminator":[186,221,93,34,50,97,194,241],"accounts":[{"name":"marginfi_account","writable":true},{"name":"authority","signer":true,"relations":["marginfi_account"]},{"name":"fee_payer","writable":true,"signer":true}],"args":[]},{"name":"marginfi_account_close_order","docs":["(user) Close an existing Order, returning rent to the user"],"discriminator":[212,223,79,182,172,183,205,237],"accounts":[{"name":"marginfi_account","writable":true,"relations":["order"]},{"name":"authority","signer":true,"relations":["marginfi_account"]},{"name":"order","writable":true},{"name":"fee_recipient","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"marginfi_account_end_execute_order","docs":["(permissionless keeper) End Order execution","* Closes the Order (keeper keeps the rent)","* Closes the borrow position involved in the Order, the lending position remains open","* User health must be \"unchanged\" (within Order requirements i.e. minus slippage). Keeper","may keep any slippage in excess of what was needed to complete the Order as profit.","* `StartExecuteOrder` must appear earlier in the tx","* Must appear last in the tx","* CPI is forbidden","* Returns rent for ephemeral accounts created during `StartExecuteOrder`"],"discriminator":[115,42,20,93,121,84,178,83],"accounts":[{"name":"group","relations":["marginfi_account"]},{"name":"marginfi_account","docs":["The account owning the order"],"writable":true,"relations":["order"]},{"name":"executor","docs":["The executioner ☠️"],"signer":true,"relations":["execute_record"]},{"name":"fee_recipient","writable":true},{"name":"order","writable":true,"relations":["execute_record"]},{"name":"execute_record","docs":["This keeps track of the relevant state to be checked at the end of execution."],"writable":true},{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}}],"args":[]},{"name":"marginfi_account_init_liq_record","docs":["(permissionless) Initialize a liquidation record PDA for a marginfi account. The fee_payer","pays rent; the record is required for receivership liquidation."],"discriminator":[236,213,238,126,147,251,164,8],"accounts":[{"name":"marginfi_account","writable":true},{"name":"fee_payer","writable":true,"signer":true},{"name":"liquidation_record","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,95,114,101,99,111,114,100]},{"kind":"account","path":"marginfi_account"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"marginfi_account_initialize","docs":["Initialize a marginfi account for a given group. The account is a fresh keypair, and must","sign. If you are a CPI caller, consider using `marginfi_account_initialize_pda` instead, or","create the account manually and use `transfer_to_new_account` to gift it to the owner you","wish."],"discriminator":[43,78,61,255,148,52,249,154],"accounts":[{"name":"marginfi_group"},{"name":"marginfi_account","writable":true,"signer":true},{"name":"authority","signer":true},{"name":"fee_payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"marginfi_account_initialize_pda","docs":["The same as `marginfi_account_initialize`, except the created marginfi account uses a PDA","(Program Derived Address)","","seeds:","- marginfi_group","- authority: The account authority (owner)","- account_index: A u16 value to allow multiple accounts per authority","- third_party_id: Optional u16 for third-party tagging. Seeds < PDA_FREE_THRESHOLD can be","used freely. For a dedicated seed used by just your program (via CPI), contact us."],"discriminator":[87,177,91,80,218,119,245,31],"accounts":[{"name":"marginfi_group"},{"name":"marginfi_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,97,114,103,105,110,102,105,95,97,99,99,111,117,110,116]},{"kind":"account","path":"marginfi_group"},{"kind":"account","path":"authority"},{"kind":"arg","path":"account_index"},{"kind":"arg","path":"third_party_id.unwrap_or(0)"}]}},{"name":"authority","signer":true},{"name":"fee_payer","writable":true,"signer":true},{"name":"instructions_sysvar","docs":["Instructions sysvar for CPI validation",""],"address":"Sysvar1nstructions1111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"account_index","type":"u16"},{"name":"third_party_id","type":{"option":"u16"}}]},{"name":"marginfi_account_keeper_close_order","docs":["(permissionless keeper) Close an existing Order after the user account was closed, or it no","longer has the associated positions, or the user has executed","`marginfi_account_set_keeper_close_flags`. Keeper keeps the rent."],"discriminator":[128,114,71,46,194,71,186,106],"accounts":[{"name":"marginfi_account","docs":["marginfi account was closed.","The ownership check is checked in the handler or/and type checks are made in the handler."],"relations":["order"]},{"name":"fee_recipient","writable":true},{"name":"order","writable":true}],"args":[]},{"name":"marginfi_account_place_order","docs":["(user) Create a new Order.","* bank_keys - Currently only two keys: the lending position and borrowing position in the","users's Balances for which the order is being placed","* trigger - the type of order (stop loss, take profit, or both), and the threshold at which","to trigger the order, in dollars"],"discriminator":[244,112,75,138,143,108,7,186],"accounts":[{"name":"group","relations":["marginfi_account"]},{"name":"marginfi_account","writable":true},{"name":"fee_payer","writable":true,"signer":true},{"name":"authority","signer":true,"relations":["marginfi_account"]},{"name":"order","writable":true},{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"global_fee_wallet","writable":true,"relations":["fee_state"]},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bank_keys","type":{"vec":"pubkey"}},{"name":"trigger","type":{"defined":{"name":"OrderTrigger"}}}]},{"name":"marginfi_account_set_freeze","docs":["(admin only) Freeze or unfreeze a marginfi account. Frozen accounts can only be operated on","by the group admin."],"discriminator":[199,179,231,30,138,247,110,227],"accounts":[{"name":"group","relations":["marginfi_account"]},{"name":"marginfi_account","writable":true},{"name":"admin","signer":true}],"args":[{"name":"frozen","type":"bool"}]},{"name":"marginfi_account_set_keeper_close_flags","docs":["(user) Purge flags from some balances, enabling a Keeper to call","`marginfi_account_keeper_close_order` on associated Orders. Typically, use","`marginfi_account_close_order` instead if trying to close an Order."],"discriminator":[82,163,165,222,212,255,33,210],"accounts":[{"name":"marginfi_account","writable":true},{"name":"authority","signer":true,"relations":["marginfi_account"]}],"args":[{"name":"bank_keys_opt","type":{"option":{"vec":"pubkey"}}}]},{"name":"marginfi_account_start_execute_order","docs":["(permissionless keeper) Begin Order execution","* Enables the Keeper to withdraw/repay associated positions until the end of the tx","* Only one `StartExecuteOrder` is allowed per tx","* Must appear before `EndExecuteOrder` in the tx, and before any instructions except certain","allowed ones (compute budget, kamino refresh, etc)","* `EndExecuteOrder` must also appear in the tx","* CPI is forbidden","* Costs a small amount of rent, which is returned at the end of the tx, make sure you have","enough SOL to start the tx."],"discriminator":[1,70,140,134,183,29,208,224],"accounts":[{"name":"group","relations":["marginfi_account"]},{"name":"marginfi_account","docs":["The account owning the order"],"writable":true,"relations":["order"]},{"name":"fee_payer","writable":true,"signer":true},{"name":"executor","docs":["This account will have the authority to withdraw/repay as if they are the user authority","until the end of the tx.",""]},{"name":"order","writable":true},{"name":"execute_record","docs":["This keeps track of the relevant state to be checked at the end of execution."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[101,120,101,99,117,116,101,95,111,114,100,101,114]},{"kind":"account","path":"order"}]}},{"name":"instruction_sysvar","address":"Sysvar1nstructions1111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"marginfi_account_update_emissions_destination_account","docs":["(account authority) Set the wallet whose canonical ATA will receive off-chain emissions."],"discriminator":[73,185,162,201,111,24,116,185],"accounts":[{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"destination_account","docs":["the canonical ATA for each emissions mint."]}],"args":[]},{"name":"marginfi_group_configure","docs":["(admin only) Configure group admin keys and emode leverage caps. All admin keys must be","provided on every call. Emode leverage caps are set if provided, otherwise the existing","(non-zero) values are kept. Pass `Some(value)` to update, `None` to leave unchanged."],"discriminator":[62,199,81,78,33,13,236,61],"accounts":[{"name":"marginfi_group","writable":true},{"name":"admin","signer":true,"relations":["marginfi_group"]}],"args":[{"name":"new_admin","type":{"option":"pubkey"}},{"name":"new_emode_admin","type":{"option":"pubkey"}},{"name":"new_curve_admin","type":{"option":"pubkey"}},{"name":"new_limit_admin","type":{"option":"pubkey"}},{"name":"new_flow_admin","type":{"option":"pubkey"}},{"name":"new_emissions_admin","type":{"option":"pubkey"}},{"name":"new_metadata_admin","type":{"option":"pubkey"}},{"name":"new_risk_admin","type":{"option":"pubkey"}},{"name":"emode_max_init_leverage","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"emode_max_maint_leverage","type":{"option":{"defined":{"name":"WrappedI80F48"}}}}]},{"name":"marginfi_group_initialize","docs":["(admin only) Initialize a new marginfi group. The signer becomes the group admin."],"discriminator":[255,67,67,26,94,31,34,20],"accounts":[{"name":"marginfi_group","writable":true,"signer":true},{"name":"admin","writable":true,"signer":true},{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"migrate_curve","docs":["(Permissionless) Convert a bank from the legacy curve setup to the new setup, with no effect","on how interest accrues."],"discriminator":[151,254,50,13,112,235,152,72],"accounts":[{"name":"bank","writable":true}],"args":[]},{"name":"panic_pause","docs":["(global_fee_admin only) Pause the protocol. Auto-expires after 30 minutes. Limited to 3","pauses per day and 2 consecutive pauses."],"discriminator":[76,164,123,25,4,43,79,165],"accounts":[{"name":"global_fee_admin","docs":["Admin of the global FeeState (can trigger panic pause)"],"signer":true,"relations":["fee_state"]},{"name":"fee_state","docs":["Global fee state account containing the panic state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}}],"args":[]},{"name":"panic_unpause","docs":["(global_fee_admin only) Unpause the protocol before the auto-expiry."],"discriminator":[236,107,194,242,99,51,121,128],"accounts":[{"name":"global_fee_admin","docs":["Admin of the global FeeState (can manually unpause)"],"writable":true,"signer":true,"relations":["fee_state"]},{"name":"fee_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}}],"args":[]},{"name":"panic_unpause_permissionless","docs":["(permissionless) Unpause the protocol when pause time has expired"],"discriminator":[245,139,50,159,213,62,91,248],"accounts":[{"name":"fee_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}}],"args":[]},{"name":"propagate_fee_state","docs":["(Permissionless) Force any group to adopt the current FeeState settings"],"discriminator":[64,3,166,194,129,21,101,155],"accounts":[{"name":"fee_state","pda":{"seeds":[{"kind":"const","value":[102,101,101,115,116,97,116,101]}]}},{"name":"marginfi_group","docs":["Any group, this ix is permisionless and can propagate the fee to any group"],"writable":true}],"args":[]},{"name":"propagate_staked_settings","docs":["(permissionless) Propagate updated staked settings to a staked collateral bank."],"discriminator":[210,30,152,69,130,99,222,170],"accounts":[{"name":"marginfi_group","relations":["staked_settings"]},{"name":"staked_settings"},{"name":"bank","writable":true}],"args":[]},{"name":"purge_deleverage_balance","docs":["(risk admin only) Purge a user's lending balance without withdrawing anything. Only usable","after all the debt has been settled on a bank in deleveraging mode, e.g. when","`TOKENLESS_REPAYMENTS_ALLOWED` and `TOKENLESS_REPAYMENTS_COMPLETE`. used to purge remaining","lending assets in a now-worthless bank before it is fully sunset."],"discriminator":[132,187,25,149,181,59,253,136],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"risk_admin","signer":true,"relations":["group"]},{"name":"bank","writable":true}],"args":[]},{"name":"solend_deposit","docs":["(user) Deposit into a Solend reserve through a marginfi account","* amount - in the underlying token (e.g., USDC), in native decimals"],"discriminator":[56,127,176,148,12,25,3,24],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"signer_token_account","docs":["Owned by authority, the source account for the token deposit."],"writable":true},{"name":"liquidity_vault_authority","docs":["The bank's liquidity vault authority, which owns the Solend obligation"],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["Used as an intermediary to deposit tokens into Solend"],"writable":true,"relations":["bank"]},{"name":"integration_acc_2","docs":["The Solend obligation account"],"writable":true,"relations":["bank"]},{"name":"lending_market"},{"name":"lending_market_authority","docs":["Derived from the lending market"]},{"name":"integration_acc_1","docs":["The Solend reserve that holds liquidity"],"writable":true,"relations":["bank"]},{"name":"mint","docs":["Bank's liquidity token mint (e.g., USDC)"],"relations":["bank"]},{"name":"reserve_liquidity_supply","docs":["Reserve's liquidity supply SPL Token account"],"writable":true},{"name":"reserve_collateral_mint","docs":["The reserve's mint for cTokens"],"writable":true},{"name":"reserve_collateral_supply","docs":["The reserve's collateral supply account (where cTokens are stored)"],"writable":true},{"name":"user_collateral","docs":["The user's destination for cTokens (collateral). This is a temporary account owned by","liquidity_vault_authority that will hold cTokens between deposit and obligation update."],"writable":true},{"name":"pyth_price","docs":["Oracle accounts - required by Solend even if not actively used"]},{"name":"switchboard_feed"},{"name":"solend_program","address":"So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo"},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"}]},{"name":"solend_init_obligation","docs":["(permissionless) Initialize a Solend obligation for a marginfi bank","Requires a minimum deposit to ensure the obligation remains active","* amount - minimum deposit amount (at least 10 units) in native decimals"],"discriminator":[81,96,123,149,218,116,235,196],"accounts":[{"name":"fee_payer","docs":["Pays to init the obligation and pays a nominal amount to ensure the obligation has a","non-zero balance."],"writable":true,"signer":true},{"name":"bank"},{"name":"signer_token_account","docs":["The fee payer must provide a nominal amount of bank tokens so the obligation is not empty.","This amount is irrecoverable and will prevent the obligation from ever being closed."],"writable":true},{"name":"liquidity_vault_authority","docs":["The liquidity vault authority (PDA that will own the Solend obligation)"],"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","docs":["Used as an intermediary to deposit a nominal amount of token into the obligation."],"writable":true,"relations":["bank"]},{"name":"integration_acc_2","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,111,108,101,110,100,95,111,98,108,105,103,97,116,105,111,110]},{"kind":"account","path":"bank"}]},"relations":["bank"]},{"name":"lending_market"},{"name":"lending_market_authority","docs":["Derived from the lending market"]},{"name":"integration_acc_1","writable":true,"relations":["bank"]},{"name":"mint","docs":["Bank's liquidity token mint (e.g., USDC)"],"writable":true,"relations":["bank"]},{"name":"reserve_liquidity_supply","writable":true},{"name":"reserve_collateral_mint","docs":["The reserve's mint for cTokens"],"writable":true},{"name":"reserve_collateral_supply","docs":["The reserve's collateral supply account (where cTokens are stored)"],"writable":true},{"name":"user_collateral","docs":["The user's destination for cTokens (collateral). This is a temporary account owned by","liquidity_vault_authority that will hold cTokens between deposit and obligation update."],"writable":true},{"name":"pyth_price","docs":["Oracle accounts - required by Solend even if not actively used"]},{"name":"switchboard_feed"},{"name":"solend_program","address":"So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo"},{"name":"token_program"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"}]},{"name":"solend_withdraw","docs":["(user) Withdraw from a Solend reserve through a marginfi account","* amount - in collateral tokens (cTokens), in native decimals","* withdraw_all - withdraw entire position if true"],"discriminator":[238,144,170,199,21,72,155,36],"accounts":[{"name":"group","relations":["marginfi_account","bank"]},{"name":"marginfi_account","writable":true},{"name":"authority","signer":true},{"name":"bank","writable":true},{"name":"destination_token_account","docs":["Token account that will receive the withdrawn tokens. Mint/owner are validated by the","SPL transfer; the caller controls the destination."],"writable":true},{"name":"liquidity_vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"relations":["bank"]},{"name":"integration_acc_2","docs":["The Solend obligation account"],"writable":true,"relations":["bank"]},{"name":"lending_market","writable":true},{"name":"lending_market_authority","docs":["Derived from the lending market"]},{"name":"integration_acc_1","docs":["The Solend reserve that holds liquidity"],"writable":true,"relations":["bank"]},{"name":"mint","docs":["Bank's liquidity token mint (e.g., USDC)"],"relations":["bank"]},{"name":"reserve_liquidity_supply","docs":["Reserve's liquidity supply SPL Token account"],"writable":true},{"name":"reserve_collateral_mint","docs":["The reserve's mint for cTokens"],"writable":true},{"name":"reserve_collateral_supply","docs":["The reserve's collateral supply account (where cTokens are stored)"],"writable":true},{"name":"user_collateral","docs":["The user's destination for cTokens (collateral). This is a temporary account owned by","liquidity_vault_authority that holds cTokens."],"writable":true},{"name":"solend_program","address":"So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo"},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"},{"name":"withdraw_all","type":{"option":"bool"}}]},{"name":"start_deleverage","docs":["(risk_admin only) Begin forced deleverage on an account. Similar to start_liquidation but","does not require the account to be unhealthy."],"discriminator":[10,138,10,57,40,232,182,193],"accounts":[{"name":"marginfi_account","docs":["Account to deleverage"],"writable":true},{"name":"liquidation_record","docs":["The associated liquidation record PDA for the given `marginfi_account`"],"writable":true,"relations":["marginfi_account"]},{"name":"group","relations":["marginfi_account"]},{"name":"risk_admin","docs":["The risk admin will have the authority to withdraw/repay as if they are the user authority","until the end of the tx."],"signer":true,"relations":["group"]},{"name":"instruction_sysvar","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[]},{"name":"start_liquidation","docs":["(permissionless) Begin receivership liquidation on an unhealthy account. Snapshots health","and marks the account in receivership. Must have `end_liquidation` as the last ix in the tx."],"discriminator":[244,93,90,214,192,166,191,21],"accounts":[{"name":"marginfi_account","docs":["Account under liquidation"],"writable":true},{"name":"liquidation_record","docs":["The associated liquidation record PDA for the given `marginfi_account`"],"writable":true,"relations":["marginfi_account"]},{"name":"liquidation_receiver","docs":["This account will have the authority to withdraw/repay as if they are the user authority","until the end of the tx.",""]},{"name":"instruction_sysvar","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[]},{"name":"super_admin_deposit","docs":["(primary admin only) Deposit directly into a bank liquidity vault and raise","`asset_share_value` proportionally. No marginfi account is involved."],"discriminator":[241,189,199,17,207,225,64,75],"accounts":[{"name":"group","relations":["bank"]},{"name":"admin","signer":true,"relations":["group"]},{"name":"bank","writable":true},{"name":"admin_token_account","writable":true},{"name":"liquidity_vault","writable":true,"relations":["bank"]},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"}]},{"name":"super_admin_withdraw","docs":["(primary admin only) Withdraw directly from a bank liquidity vault and lower","`asset_share_value` proportionally. No marginfi account is involved."],"discriminator":[202,67,85,126,104,138,79,197],"accounts":[{"name":"group","relations":["bank"]},{"name":"admin","signer":true,"relations":["group"]},{"name":"bank","writable":true},{"name":"destination_token_account","writable":true},{"name":"liquidity_vault_authority","pda":{"seeds":[{"kind":"const","value":[108,105,113,117,105,100,105,116,121,95,118,97,117,108,116,95,97,117,116,104]},{"kind":"account","path":"bank"}]}},{"name":"liquidity_vault","writable":true,"relations":["bank"]},{"name":"token_program"}],"args":[{"name":"amount","type":"u64"}]},{"name":"transfer_to_new_account","docs":["(account authority) Transfer all positions to a new account under a new authority. The old","account is disabled. Pays a flat SOL fee to the protocol."],"discriminator":[28,79,129,231,169,69,69,65],"accounts":[{"name":"group","relations":["old_marginfi_account"]},{"name":"old_marginfi_account","writable":true},{"name":"new_marginfi_account","writable":true,"signer":true},{"name":"authority","signer":true},{"name":"fee_payer","writable":true,"signer":true},{"name":"new_authority"},{"name":"global_fee_wallet","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"transfer_to_new_account_pda","docs":["(account authority) Same as `transfer_to_new_account` except the resulting account is a PDA","","seeds:","- marginfi_group","- authority: The account authority (owner)","- account_index: A u16 value to allow multiple accounts per authority","- third_party_id: Optional u16 for third-party tagging. Seeds < PDA_FREE_THRESHOLD can be","used freely. For a dedicated seed used by just your program (via CPI), contact us."],"discriminator":[172,210,224,220,146,212,253,49],"accounts":[{"name":"group","relations":["old_marginfi_account"]},{"name":"old_marginfi_account","writable":true},{"name":"new_marginfi_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,97,114,103,105,110,102,105,95,97,99,99,111,117,110,116]},{"kind":"account","path":"group"},{"kind":"account","path":"new_authority"},{"kind":"arg","path":"account_index"},{"kind":"arg","path":"third_party_id.unwrap_or(0)"}]}},{"name":"authority","signer":true},{"name":"fee_payer","writable":true,"signer":true},{"name":"new_authority"},{"name":"global_fee_wallet","writable":true},{"name":"instructions_sysvar","docs":["Instructions sysvar for CPI validation"],"address":"Sysvar1nstructions1111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"account_index","type":"u16"},{"name":"third_party_id","type":{"option":"u16"}}]},{"name":"update_deleverage_withdrawals","docs":["(delegate_flow_admin only) Update the deleverage daily withdraw outflow with","aggregated data. The delegate flow admin aggregates","`DeleverageWithdrawFlowEvent` events off-chain and calls this instruction at intervals."],"discriminator":[56,3,181,118,27,247,207,227],"accounts":[{"name":"marginfi_group","writable":true},{"name":"delegate_flow_admin","signer":true,"relations":["marginfi_group"]}],"args":[{"name":"outflow_usd","type":"u32"},{"name":"update_seq","type":"u64"},{"name":"event_start_slot","type":"u64"},{"name":"event_end_slot","type":"u64"}]},{"name":"update_group_rate_limiter","docs":["(delegate_flow_admin only) Update the group rate limiter with aggregated","inflow/outflow. The delegate flow admin aggregates","`RateLimitFlowEvent` events off-chain, converts to USD, and calls this instruction at","intervals to update group rate limiter state."],"discriminator":[23,78,60,139,187,44,129,37],"accounts":[{"name":"marginfi_group","writable":true},{"name":"delegate_flow_admin","signer":true,"relations":["marginfi_group"]}],"args":[{"name":"outflow_usd","type":{"option":"u64"}},{"name":"inflow_usd","type":{"option":"u64"}},{"name":"update_seq","type":"u64"},{"name":"event_start_slot","type":"u64"},{"name":"event_end_slot","type":"u64"}]},{"name":"write_bank_metadata","docs":["(metadata admin only) Write ticker/description information for a bank on-chain. Optional, not","all Banks are guaranteed to have metadata."],"discriminator":[147,78,81,133,129,138,233,59],"accounts":[{"name":"group","relations":["bank"]},{"name":"bank","relations":["metadata"]},{"name":"metadata_admin","writable":true,"signer":true,"relations":["group"]},{"name":"metadata","writable":true}],"args":[{"name":"ticker","type":{"option":"bytes"}},{"name":"description","type":{"option":"bytes"}}]}],"accounts":[{"name":"Bank","discriminator":[142,49,166,242,50,66,97,188]},{"name":"BankMetadata","discriminator":[49,207,31,34,67,225,169,186]},{"name":"ExecuteOrderRecord","discriminator":[6,100,107,60,164,226,56,97]},{"name":"FeeState","discriminator":[63,224,16,85,193,36,235,220]},{"name":"Lending","discriminator":[135,199,82,16,249,131,182,241]},{"name":"LiquidationRecord","discriminator":[95,116,23,132,89,210,245,162]},{"name":"MarginfiAccount","discriminator":[67,178,130,109,126,114,28,42]},{"name":"MarginfiGroup","discriminator":[182,23,173,240,151,206,182,67]},{"name":"MinimalObligation","discriminator":[168,206,141,106,88,76,172,167]},{"name":"MinimalReserve","discriminator":[43,242,204,202,26,247,59,127]},{"name":"MinimalSpotMarket","discriminator":[100,177,8,107,168,65,65,39]},{"name":"MinimalUser","discriminator":[159,117,95,227,239,151,58,236]},{"name":"Order","discriminator":[134,173,223,185,77,86,28,51]},{"name":"SolendMinimalReserve","discriminator":[1]},{"name":"StakedSettings","discriminator":[157,140,6,77,89,173,173,125]}],"events":[{"name":"DeleverageEvent","discriminator":[161,8,108,204,209,198,12,30]},{"name":"DeleverageWithdrawFlowEvent","discriminator":[109,90,139,200,10,204,84,176]},{"name":"EditStakedSettingsEvent","discriminator":[29,58,155,191,75,220,145,206]},{"name":"HealthPulseEvent","discriminator":[183,159,218,110,61,220,65,1]},{"name":"KeeperCloseOrderEvent","discriminator":[46,152,11,174,92,157,77,64]},{"name":"LendingAccountBorrowEvent","discriminator":[223,96,81,10,156,99,26,59]},{"name":"LendingAccountDepositEvent","discriminator":[161,54,237,217,105,248,122,151]},{"name":"LendingAccountLiquidateEvent","discriminator":[166,160,249,154,183,39,23,242]},{"name":"LendingAccountRepayEvent","discriminator":[16,220,55,111,7,80,16,25]},{"name":"LendingAccountWithdrawEvent","discriminator":[3,220,148,243,33,249,54,88]},{"name":"LendingPoolBankAccrueInterestEvent","discriminator":[104,117,187,156,111,154,106,186]},{"name":"LendingPoolBankCollectFeesEvent","discriminator":[101,119,97,250,169,175,156,253]},{"name":"LendingPoolBankConfigureEvent","discriminator":[246,35,233,110,93,152,235,40]},{"name":"LendingPoolBankConfigureFrozenEvent","discriminator":[24,10,55,18,49,150,157,179]},{"name":"LendingPoolBankConfigureOracleEvent","discriminator":[119,140,110,253,150,64,210,62]},{"name":"LendingPoolBankCreateEvent","discriminator":[236,220,201,63,239,126,136,249]},{"name":"LendingPoolBankHandleBankruptcyEvent","discriminator":[166,77,41,140,36,94,10,57]},{"name":"LendingPoolBankSetFixedOraclePriceEvent","discriminator":[65,72,8,85,229,20,90,26]},{"name":"LendingPoolSuperAdminDepositEvent","discriminator":[99,152,211,30,58,165,210,71]},{"name":"LendingPoolSuperAdminWithdrawEvent","discriminator":[107,168,232,181,144,161,252,37]},{"name":"LiquidationReceiverEvent","discriminator":[40,131,224,220,151,83,24,230]},{"name":"MarginfiAccountCloseOrderEvent","discriminator":[158,34,122,98,23,146,229,212]},{"name":"MarginfiAccountCreateEvent","discriminator":[183,5,117,104,122,199,68,51]},{"name":"MarginfiAccountFreezeEvent","discriminator":[219,219,57,178,75,86,146,122]},{"name":"MarginfiAccountPlaceOrderEvent","discriminator":[1,105,79,28,142,242,99,145]},{"name":"MarginfiAccountTransferToNewAccount","discriminator":[59,105,171,110,223,136,80,89]},{"name":"MarginfiGroupConfigureEvent","discriminator":[241,104,172,167,41,195,199,170]},{"name":"MarginfiGroupCreateEvent","discriminator":[233,125,61,14,98,240,136,253]},{"name":"RateLimitFlowEvent","discriminator":[229,5,73,200,0,107,105,109]},{"name":"SetKeeperCloseFlagsEvent","discriminator":[193,230,93,128,117,87,96,21]}],"errors":[{"code":6000,"name":"InternalLogicError","msg":"Internal Marginfi logic error"},{"code":6001,"name":"BankNotFound","msg":"Invalid bank index"},{"code":6002,"name":"LendingAccountBalanceNotFound","msg":"Lending account balance not found"},{"code":6003,"name":"BankAssetCapacityExceeded","msg":"Bank deposit capacity exceeded"},{"code":6004,"name":"InvalidTransfer","msg":"Invalid transfer"},{"code":6005,"name":"MissingPythOrBankAccount","msg":"Missing Oracle, Bank, LST mint, or Sol Pool"},{"code":6006,"name":"MissingPythAccount","msg":"Missing Pyth account"},{"code":6007,"name":"MissingBankAccount","msg":"Missing Bank account"},{"code":6008,"name":"InvalidBankAccount","msg":"Invalid Bank account"},{"code":6009,"name":"RiskEngineInitRejected","msg":"RiskEngine rejected due to either bad health or stale oracles"},{"code":6010,"name":"LendingAccountBalanceSlotsFull","msg":"Lending account balance slots are full"},{"code":6011,"name":"BankAlreadyExists","msg":"Bank already exists"},{"code":6012,"name":"ZeroLiquidationAmount","msg":"Amount to liquidate must be positive"},{"code":6013,"name":"AccountNotBankrupt","msg":"Account is not bankrupt"},{"code":6014,"name":"BalanceNotBadDebt","msg":"Account balance is not bad debt"},{"code":6015,"name":"InvalidConfig","msg":"Invalid group config"},{"code":6016,"name":"BankPaused","msg":"Bank paused"},{"code":6017,"name":"BankReduceOnly","msg":"Bank is ReduceOnly mode"},{"code":6018,"name":"BankAccountNotFound","msg":"Bank is missing"},{"code":6019,"name":"OperationDepositOnly","msg":"Operation is deposit-only"},{"code":6020,"name":"OperationWithdrawOnly","msg":"Operation is withdraw-only"},{"code":6021,"name":"OperationBorrowOnly","msg":"Operation is borrow-only"},{"code":6022,"name":"OperationRepayOnly","msg":"Operation is repay-only"},{"code":6023,"name":"NoAssetFound","msg":"No asset found"},{"code":6024,"name":"NoLiabilityFound","msg":"No liability found"},{"code":6025,"name":"InvalidOracleSetup","msg":"Invalid oracle setup"},{"code":6026,"name":"IllegalUtilizationRatio","msg":"Invalid bank utilization ratio"},{"code":6027,"name":"BankLiabilityCapacityExceeded","msg":"Bank borrow cap exceeded"},{"code":6028,"name":"InvalidPrice","msg":"Invalid Price"},{"code":6029,"name":"IsolatedAccountIllegalState","msg":"Account can have only one liability when account is under isolated risk"},{"code":6030,"name":"EmissionsAlreadySetup","msg":"Emissions already setup"},{"code":6031,"name":"OracleNotSetup","msg":"Oracle is not set"},{"code":6032,"name":"InvalidSwitchboardDecimalConversion","msg":"Invalid switchboard decimal conversion"},{"code":6033,"name":"CannotCloseOutstandingEmissions","msg":"Cannot close balance because of outstanding emissions"},{"code":6034,"name":"EmissionsUpdateError","msg":"Update emissions error"},{"code":6035,"name":"AccountDisabled","msg":"Account disabled"},{"code":6036,"name":"AccountTempActiveBalanceLimitExceeded","msg":"Account can't temporarily open 3 balances, please close a balance first"},{"code":6037,"name":"AccountInFlashloan","msg":"Illegal action during flashloan"},{"code":6038,"name":"IllegalFlashloan","msg":"Illegal flashloan"},{"code":6039,"name":"IllegalFlag","msg":"Illegal flag"},{"code":6040,"name":"IllegalBalanceState","msg":"Illegal balance state"},{"code":6041,"name":"IllegalAccountAuthorityTransfer","msg":"Illegal account authority transfer"},{"code":6042,"name":"Unauthorized","msg":"Unauthorized"},{"code":6043,"name":"IllegalAction","msg":"Invalid account authority"},{"code":6044,"name":"T22MintRequired","msg":"Token22 Banks require mint account as first remaining account"},{"code":6045,"name":"InvalidFeeAta","msg":"Invalid ATA for global fee account"},{"code":6046,"name":"AddedStakedPoolManually","msg":"Use add pool permissionless instead"},{"code":6047,"name":"AssetTagMismatch","msg":"Staked SOL accounts can only deposit staked assets and borrow SOL"},{"code":6048,"name":"StakePoolValidationFailed","msg":"Stake pool validation failed: check the stake pool, mint, or sol pool"},{"code":6049,"name":"SwitchboardStalePrice","msg":"Switchboard oracle: stale price"},{"code":6050,"name":"PythPushStalePrice","msg":"Pyth Push oracle: stale price"},{"code":6051,"name":"WrongNumberOfOracleAccounts","msg":"Oracle error: wrong number of accounts"},{"code":6052,"name":"WrongOracleAccountKeys","msg":"Oracle error: wrong account keys"},{"code":6053,"name":"Vacated2","msg":"Vacated2"},{"code":6054,"name":"Vacated3","msg":"Vacated3"},{"code":6055,"name":"OracleMaxConfidenceExceeded","msg":"Oracle max confidence exceeded: try again later"},{"code":6056,"name":"PythPushInsufficientVerificationLevel","msg":"Pyth Push oracle: insufficient verification level"},{"code":6057,"name":"ZeroAssetPrice","msg":"Zero asset price"},{"code":6058,"name":"ZeroLiabilityPrice","msg":"Zero liability price"},{"code":6059,"name":"SwitchboardWrongAccountOwner","msg":"Switchboard oracle: wrong account owner"},{"code":6060,"name":"PythPushInvalidAccount","msg":"Pyth Push oracle: invalid account"},{"code":6061,"name":"SwitchboardInvalidAccount","msg":"Switchboard oracle: invalid account"},{"code":6062,"name":"MathError","msg":"Math error"},{"code":6063,"name":"InvalidEmissionsDestinationAccount","msg":"Invalid emissions destination account"},{"code":6064,"name":"SameAssetAndLiabilityBanks","msg":"Asset and liability bank cannot be the same"},{"code":6065,"name":"OverliquidationAttempt","msg":"Trying to withdraw more assets than available"},{"code":6066,"name":"NoLiabilitiesInLiabilityBank","msg":"Liability bank has no liabilities"},{"code":6067,"name":"AssetsInLiabilityBank","msg":"Liability bank has assets"},{"code":6068,"name":"HealthyAccount","msg":"Account is healthy and cannot be liquidated"},{"code":6069,"name":"ExhaustedLiability","msg":"Liability payoff too severe, exhausted liability"},{"code":6070,"name":"TooSeverePayoff","msg":"Liability payoff too severe, liability balance has assets"},{"code":6071,"name":"TooSevereLiquidation","msg":"Liquidation too severe, account above maintenance requirement"},{"code":6072,"name":"WorseHealthPostLiquidation","msg":"Liquidation would worsen account health"},{"code":6073,"name":"IntegrationPositionLimitExceeded","msg":"Exceeded the maximum allowed integration positions"},{"code":6074,"name":"MaxInitLeverageExceeded","msg":"Maximum initial leverage exceeded"},{"code":6075,"name":"BadEmodeConfig","msg":"The Emode config was invalid"},{"code":6076,"name":"PythPushInvalidWindowSize","msg":"TWAP window size does not match expected duration"},{"code":6077,"name":"InvalidFeesDestinationAccount","msg":"Invalid fees destination account"},{"code":6078,"name":"BankCannotClose","msg":"Banks cannot close when they have open positions or emissions outstanding"},{"code":6079,"name":"AccountAlreadyMigrated","msg":"Account already migrated"},{"code":6080,"name":"ProtocolPaused","msg":"Protocol is paused"},{"code":6081,"name":"MetadataTooLong","msg":"Metadata is too long"},{"code":6082,"name":"PauseLimitExceeded","msg":"Pause limit exceeded"},{"code":6083,"name":"ProtocolNotPaused","msg":"Protocol is not paused"},{"code":6084,"name":"BankKilledByBankruptcy","msg":"Bank killed by bankruptcy: bank shutdown and value of all holdings is zero"},{"code":6085,"name":"UnexpectedLiquidationState","msg":"Liquidation state issue. Check start before end, end last, and both unique"},{"code":6086,"name":"StartNotFirst","msg":"Liquidation start must be first instruction (other than compute program ixes)"},{"code":6087,"name":"StartRepeats","msg":"Only one liquidation event allowed per tx"},{"code":6088,"name":"EndNotLast","msg":"The end instruction must be the last ix in the tx"},{"code":6089,"name":"ForbiddenIx","msg":"Tried to call an instruction that is forbidden during liquidation"},{"code":6090,"name":"LiquidationPremiumTooHigh","msg":"Seized too much of the asset relative to liability repaid"},{"code":6091,"name":"NotAllowedInCPI","msg":"Start and end liquidation and flashloan must be top-level instructions"},{"code":6092,"name":"ZeroSupplyInStakePool","msg":"Stake pool supply is zero: cannot compute price"},{"code":6093,"name":"InvalidGroup","msg":"Invalid group: account constraint violated"},{"code":6094,"name":"InvalidLiquidityVault","msg":"Invalid liquidity vault: account constraint violated"},{"code":6095,"name":"InvalidLiquidationRecord","msg":"Invalid liquidation record: account constraint violated"},{"code":6096,"name":"InvalidLiquidationReceiver","msg":"Invalid liquidation receiver: account constraint violated"},{"code":6097,"name":"InvalidEmissionsMint","msg":"Invalid emissions mint: account constraint violated"},{"code":6098,"name":"InvalidMint","msg":"Invalid mint: account constraint violated"},{"code":6099,"name":"InvalidFeeWallet","msg":"Invalid fee wallet: account constraint violated"},{"code":6100,"name":"FixedOraclePriceNegative","msg":"Fixed oracle price must be zero or greater"},{"code":6101,"name":"DailyWithdrawalLimitExceeded","msg":"Daily withdrawal limit exceeded: try again later"},{"code":6102,"name":"ZeroWithdrawalLimit","msg":"Cannot set daily withdrawal limit to zero"},{"code":6103,"name":"AccountFrozen","msg":"Account is frozen by the group admin"},{"code":6104,"name":"DuplicateBalance","msg":"Cannot reference duplicate balances"},{"code":6105,"name":"InvalidBalanceCount","msg":"Invalid amount of balances referenced"},{"code":6106,"name":"LiquidatorOrderCloseNotAllowed","msg":"Liquidator not allowed to close order"},{"code":6107,"name":"OrderTriggerNotMet","msg":"Order trigger is yet to be met"},{"code":6108,"name":"UnexpectedOrderExecutionState","msg":"Order execution state issue. Check the necessary invariants i.e not in flashloan or disabled e.t.c"},{"code":6109,"name":"OrderLiabilityNotClosed","msg":"Order liability not closed"},{"code":6110,"name":"InvalidAssetOrLiabilitiesCount","msg":"Invalid asset or liabilities count"},{"code":6111,"name":"WorseHealthPostExecution","msg":"Account health can only worsen if account is healthy"},{"code":6112,"name":"InvalidOrderTakeProfitOrStopLoss","msg":"TP must be > 0, SL must be > 0 and TP > SL if both are set"},{"code":6113,"name":"InvalidSlippage","msg":"Max slippage must be less than 100%"},{"code":6114,"name":"OrderExecutionOverWithdrawal","msg":"Executor withdrew too much: slippage or max fee constraint violated"},{"code":6115,"name":"BankHourlyRateLimitExceeded","msg":"Bank hourly rate limit exceeded: try again later"},{"code":6116,"name":"BankDailyRateLimitExceeded","msg":"Bank daily rate limit exceeded: try again later"},{"code":6117,"name":"GroupHourlyRateLimitExceeded","msg":"Group hourly rate limit exceeded: try again later"},{"code":6118,"name":"GroupDailyRateLimitExceeded","msg":"Group daily rate limit exceeded: try again later"},{"code":6119,"name":"InvalidRateLimitPrice","msg":"Invalid rate limit price: pass oracle or pre-crank cache"},{"code":6120,"name":"GroupRateLimiterUpdateEmpty","msg":"Group rate limiter admin update must include inflow and/or outflow"},{"code":6121,"name":"GroupRateLimiterUpdateInvalidSlotRange","msg":"Group rate limiter admin update slot range is invalid"},{"code":6122,"name":"GroupRateLimiterUpdateFutureSlot","msg":"Group rate limiter admin update cannot reference future slots"},{"code":6123,"name":"GroupRateLimiterUpdateStale","msg":"Group rate limiter admin update is too stale"},{"code":6124,"name":"GroupRateLimiterUpdateOutOfOrderSlot","msg":"Group rate limiter admin update slot progression is out of order"},{"code":6125,"name":"GroupRateLimiterUpdateOutOfOrderSeq","msg":"Group rate limiter admin update sequence is out of order"},{"code":6126,"name":"DeleverageWithdrawalUpdateEmpty","msg":"Deleverage withdrawal admin update must include outflow"},{"code":6127,"name":"DeleverageWithdrawalUpdateInvalidSlotRange","msg":"Deleverage withdrawal admin update slot range is invalid"},{"code":6128,"name":"DeleverageWithdrawalUpdateFutureSlot","msg":"Deleverage withdrawal admin update cannot reference future slots"},{"code":6129,"name":"DeleverageWithdrawalUpdateStale","msg":"Deleverage withdrawal admin update is too stale"},{"code":6130,"name":"DeleverageWithdrawalUpdateOutOfOrderSlot","msg":"Deleverage withdrawal admin update slot progression is out of order"},{"code":6131,"name":"DeleverageWithdrawalUpdateOutOfOrderSeq","msg":"Deleverage withdrawal admin update sequence is out of order"},{"code":6200,"name":"WrongAssetTagForStandardInstructions","msg":"Wrong asset tag for standard instructions, expected DEFAULT, SOL, or STAKED asset tag"},{"code":6201,"name":"WrongAssetTagForKaminoInstructions","msg":"Wrong asset tag for Kamino instructions, expected KAMINO asset tag"},{"code":6202,"name":"CantAddPool","msg":"Cannot create a kamino bank with this instruction, use add_bank_kamino"},{"code":6203,"name":"KaminoReserveMintAddressMismatch","msg":"Kamino reserve mint address doesn't match the bank mint address"},{"code":6204,"name":"KaminoDepositFailed","msg":"Deposit failed: obligation deposit amount increase did not match the expected increase, left - actual, right - expected"},{"code":6205,"name":"KaminoWithdrawFailed","msg":"Withdraw failed: token vault increase did not match the expected increase, left - actual, right - expected"},{"code":6206,"name":"ReserveStale","msg":"Kamino Reserve data is stale - run refresh_reserve on kamino program first"},{"code":6207,"name":"InvalidObligationDepositCount","msg":"Kamino obligation must have exactly one active deposit, at index 0"},{"code":6208,"name":"ObligationDepositReserveMismatch","msg":"Kamino obligation deposit doesn't match the expected reserve"},{"code":6209,"name":"ObligationInitDepositInsufficient","msg":"Failed to meet minimum deposit amount requirement for init obligation"},{"code":6210,"name":"KaminoReserveValidationFailed","msg":"Kamino reserve validation failed"},{"code":6211,"name":"KaminoInvalidOracleSetup","msg":"Invalid oracle setup: only KaminoPythPush and KaminoSwitchboardPull are supported"},{"code":6212,"name":"MaxMaintLeverageExceeded","msg":"Maximum Maintenance leverage exceeded"},{"code":6213,"name":"InvalidKaminoReserve","msg":"Invalid Kamino reserve: account constraint violated"},{"code":6214,"name":"InvalidKaminoObligation","msg":"Invalid Kamino obligation: account constraint violated"},{"code":6300,"name":"DriftInvalidOracleSetup","msg":"Invalid oracle setup: only DriftPythPull and DriftSwitchboardPull are supported"},{"code":6301,"name":"DriftSpotMarketMintMismatch","msg":"Drift spot market mint does not match bank mint"},{"code":6302,"name":"WrongBankAssetTagForDriftOperation","msg":"Wrong bank asset tag for Drift operation"},{"code":6303,"name":"CantUseStandardOperationsOnDriftAssets","msg":"Cannot use standard operations on Drift assets"},{"code":6304,"name":"DriftSpotMarketValidationFailed","msg":"Drift spot market validation failed"},{"code":6305,"name":"DriftInvalidSpotPositions","msg":"Drift user has invalid spot positions (only first position can have balance)"},{"code":6306,"name":"DriftSpotPositionMarketMismatch","msg":"Drift spot position market does not match bank's configured market"},{"code":6307,"name":"DriftInvalidPositionType","msg":"Drift position has invalid balance type (must be deposit)"},{"code":6308,"name":"DriftScaledBalanceMismatch","msg":"Drift scaled balance change does not match expected amount"},{"code":6309,"name":"DriftWithdrawFailed","msg":"Drift withdrawal failed - token amount mismatch"},{"code":6310,"name":"DriftUserInitDepositInsufficient","msg":"Drift user initial deposit insufficient (minimum 10 units required)"},{"code":6311,"name":"InvalidDriftAccount","msg":"Invalid drift account"},{"code":6312,"name":"DriftAuthorityMismatch","msg":"Drift authority mismatch"},{"code":6313,"name":"DriftInvalidHarvestPositionIndex","msg":"Invalid harvest position index - must be between 2 and 7"},{"code":6314,"name":"DriftPositionEmpty","msg":"Drift position is empty"},{"code":6315,"name":"DriftInvalidBalanceType","msg":"Drift position has invalid balance type"},{"code":6316,"name":"DriftNoAdminDeposit","msg":"No admin deposits found in Drift positions 2-7 for this market"},{"code":6317,"name":"DriftHarvestSameMarket","msg":"Cannot harvest from the same market as the bank's main drift spot market"},{"code":6318,"name":"DriftBrickedAccount","msg":"Drift account bricked: too many active deposits from admin operations"},{"code":6319,"name":"DriftMissingRewardOracle","msg":"Drift reward oracle required when 2+ active deposits exist"},{"code":6320,"name":"DriftMissingRewardSpotMarket","msg":"Drift reward spot market required when 2+ active deposits exist"},{"code":6321,"name":"DriftMissingRewardAccounts","msg":"Drift account has admin deposits that require reward accounts to be provided"},{"code":6322,"name":"DriftSpotMarketStale","msg":"Drift spot market is stale, interest needs to be updated"},{"code":6323,"name":"InvalidDriftSpotMarket","msg":"Invalid Drift spot market: account constraint violated"},{"code":6324,"name":"InvalidDriftUser","msg":"Invalid Drift user: account constraint violated"},{"code":6325,"name":"InvalidDriftUserStats","msg":"Invalid Drift user stats: account constraint violated"},{"code":6326,"name":"DriftUnsupportedTokenDecimals","msg":"Drift cannot support tokens with more than 19 decimals"},{"code":6400,"name":"SolendInvalidOracleSetup","msg":"Invalid oracle setup: only SolendPythPull and SolendSwitchboardPull are supported"},{"code":6401,"name":"SolendReserveValidationFailed","msg":"Solend reserve validation failed"},{"code":6402,"name":"SolendObligationOwnerMismatch","msg":"Solend obligation owner mismatch"},{"code":6403,"name":"WrongBankAssetTagForSolendOperation","msg":"Wrong bank asset tag for Solend operation"},{"code":6404,"name":"CantUseStandardOperationsOnSolendAssets","msg":"Cannot use standard operations on Solend assets"},{"code":6405,"name":"SolendReserveMismatch","msg":"Solend reserve mismatch"},{"code":6406,"name":"SolendReserveMintMismatch","msg":"Solend reserve mint mismatch"},{"code":6407,"name":"SolendInvalidDepositPositions","msg":"Solend obligation has invalid deposits (only first position can have balance)"},{"code":6408,"name":"SolendDepositPositionReserveMismatch","msg":"Solend deposit position reserve does not match bank's configured reserve"},{"code":6409,"name":"SolendCTokenBalanceMismatch","msg":"Solend cToken balance change does not match expected amount"},{"code":6410,"name":"SolendWithdrawFailed","msg":"Solend withdrawal failed - token amount mismatch"},{"code":6411,"name":"SolendReserveStale","msg":"Solend reserve is stale"},{"code":6412,"name":"SolendDepositFailed","msg":"Solend deposit failed - collateral amount mismatch"},{"code":6413,"name":"InvalidSolendAccount","msg":"Invalid Solend account owner"},{"code":6414,"name":"InvalidSolendAccountVersion","msg":"Invalid Solend account version"},{"code":6415,"name":"InvalidSolendReserve","msg":"Invalid Solend reserve: account constraint violated"},{"code":6416,"name":"InvalidSolendObligation","msg":"Invalid Solend obligation: account constraint violated"},{"code":6500,"name":"JuplendInvalidOracleSetup","msg":"Invalid oracle setup: only JuplendPythPull and JuplendSwitchboardPull are supported"},{"code":6501,"name":"JuplendLendingValidationFailed","msg":"Juplend lending state validation failed"},{"code":6502,"name":"WrongBankAssetTagForJuplendOperation","msg":"Wrong bank asset tag for Juplend operation"},{"code":6503,"name":"CantUseStandardOperationsOnJuplendAssets","msg":"Cannot use standard operations on Juplend assets"},{"code":6504,"name":"JuplendLendingStale","msg":"Juplend lending state is stale"},{"code":6505,"name":"InvalidJuplendLending","msg":"Invalid Juplend lending: account constraint violated"},{"code":6506,"name":"JuplendLendingMintMismatch","msg":"Juplend lending mint mismatch"},{"code":6507,"name":"JuplendBankAlreadyActivated","msg":"Juplend bank is already activated"},{"code":6508,"name":"InvalidJuplendFTokenVault","msg":"Invalid Juplend fToken vault"},{"code":6509,"name":"JuplendDepositFailed","msg":"Juplend deposit failed"},{"code":6510,"name":"JuplendWithdrawFailed","msg":"Juplend withdraw failed"},{"code":6511,"name":"JuplendInitPositionDepositInsufficient","msg":"Juplend init position deposit insufficient"},{"code":6512,"name":"InvalidJuplendWithdrawIntermediaryAta","msg":"Invalid Juplend withdraw intermediary ATA"}],"types":[{"name":"AccountEventHeader","type":{"kind":"struct","fields":[{"name":"signer","type":{"option":"pubkey"}},{"name":"marginfi_account","type":"pubkey"},{"name":"marginfi_account_authority","type":"pubkey"},{"name":"marginfi_group","type":"pubkey"}]}},{"name":"Balance","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"active","docs":["Whether this balance slot is in use (nonzero = active)"],"type":"u8"},{"name":"bank_pk","docs":["The bank this balance corresponds to"],"type":"pubkey"},{"name":"bank_asset_tag","docs":["Inherited from the bank when the position is first created and CANNOT BE CHANGED after that.","Note that all balances created before the addition of this feature use `ASSET_TAG_DEFAULT`"],"type":"u8"},{"name":"tag","docs":["Tag used by orders to reference this balance (0 means unused/unassigned).","A tag may also have a non-zero value while having no orders."],"type":"u16"},{"name":"_pad0","type":{"array":["u8",4]}},{"name":"asset_shares","docs":["The user's asset (deposit) shares in the bank. Multiply by `bank.asset_share_value` for","the token amount."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_shares","docs":["The user's liability (borrow) shares in the bank. Multiply by `bank.liability_share_value`","for the token amount."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"emissions_outstanding","docs":["Unclaimed emissions rewards for this position"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"last_update","docs":["Unix timestamp (u64) of the last emissions calculation for this position"],"type":"u64"},{"name":"_padding","docs":["Reserved for future use"],"type":{"array":["u64",1]}}]}},{"name":"Bank","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"mint","docs":["The SPL token mint this bank manages"],"type":"pubkey"},{"name":"mint_decimals","docs":["Number of decimals of the `mint`. Must be < 24."],"type":"u8"},{"name":"group","docs":["The `MarginfiGroup` this bank belongs to"],"type":"pubkey"},{"name":"_pad0","type":{"array":["u8",7]}},{"name":"asset_share_value","docs":["Monotonically increases as interest rate accumulates. For typical banks, a user's asset","value in token = (number of shares the user has * asset_share_value).","* A float (arbitrary decimals)","* Initially 1"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_share_value","docs":["Monotonically increases as interest rate accumulates. For typical banks, a user's liabilty","value in token = (number of shares the user has * liability_share_value)","* A float (arbitrary decimals)","* Initially 1"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liquidity_vault","docs":["The SPL token account holding deposited liquidity"],"type":"pubkey"},{"name":"liquidity_vault_bump","docs":["PDA bump for the liquidity vault"],"type":"u8"},{"name":"liquidity_vault_authority_bump","docs":["PDA bump for the liquidity vault authority"],"type":"u8"},{"name":"insurance_vault","docs":["The SPL token account holding insurance fund tokens"],"type":"pubkey"},{"name":"insurance_vault_bump","docs":["PDA bump for the insurance vault"],"type":"u8"},{"name":"insurance_vault_authority_bump","docs":["PDA bump for the insurance vault authority"],"type":"u8"},{"name":"_pad1","type":{"array":["u8",4]}},{"name":"collected_insurance_fees_outstanding","docs":["Fees collected and pending withdraw for the `insurance_vault`"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"fee_vault","docs":["The SPL token account holding collected group fees"],"type":"pubkey"},{"name":"fee_vault_bump","docs":["PDA bump for the fee vault"],"type":"u8"},{"name":"fee_vault_authority_bump","docs":["PDA bump for the fee vault authority"],"type":"u8"},{"name":"_pad2","type":{"array":["u8",6]}},{"name":"collected_group_fees_outstanding","docs":["Fees collected and pending withdraw for the `fee_vault`"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"total_liability_shares","docs":["Sum of all liability shares held by all borrowers in this bank.","Multiply by `liability_share_value` to get the total liability amount in native token units."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"total_asset_shares","docs":["Sum of all asset shares held by all depositors in this bank.","Multiply by `asset_share_value` to get the total asset amount in native token units.","* For Kamino banks, this is the quantity of collateral tokens (NOT liquidity tokens) in the","bank, and also uses `mint_decimals`, though the mint itself will always show (6) decimals","exactly (i.e Kamino ignores this and treats it as if it was using `mint_decimals`)"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"last_update","docs":["Unix timestamp (i64) of the last interest accrual"],"type":"i64"},{"name":"config","docs":["The bank's configuration parameters (weights, limits, oracle setup, interest rate config)"],"type":{"defined":{"name":"BankConfig"}}},{"name":"flags","docs":["Bank flags bitfield (u64).","","- Bit 0 (1): `EMISSIONS_FLAG_BORROW_ACTIVE` — borrow-side emissions are active","- Bit 1 (2): `EMISSIONS_FLAG_LENDING_ACTIVE` — lending-side emissions are active","- Bit 2 (4): `PERMISSIONLESS_BAD_DEBT_SETTLEMENT_FLAG` — anyone can settle bad debt","- Bit 3 (8): `FREEZE_SETTINGS` — bank configuration is frozen (only limits can change)","- Bit 4 (16): `CLOSE_ENABLED_FLAG` — bank can be closed (set at creation for banks >= 0.1.4)","- Bit 5 (32): `TOKENLESS_REPAYMENTS_ALLOWED` — risk admin can repay debt without tokens","- Bit 6 (64): `TOKENLESS_REPAYMENTS_COMPLETE` — all debt cleared, lender purge enabled"],"type":"u64"},{"name":"emissions_rate","docs":["Emissions APR. Number of emitted tokens (emissions_mint) per 1e(bank.mint_decimal) tokens","(bank mint) (native amount) per 1 YEAR."],"type":"u64"},{"name":"emissions_remaining","docs":["Remaining emissions tokens available for distribution"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"emissions_mint","docs":["The SPL token mint used for emissions rewards"],"type":"pubkey"},{"name":"collected_program_fees_outstanding","docs":["Fees collected and pending withdraw for the `FeeState.global_fee_wallet`'s canonical ATA for `mint`"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"emode","docs":["Controls this bank's emode configuration, which enables some banks to treat the assets of","certain other banks more preferentially as collateral."],"type":{"defined":{"name":"EmodeSettings"}}},{"name":"fees_destination_account","docs":["Set with `update_fees_destination_account`. Fees can be withdrawn to the canonical ATA of","this wallet without the admin's input (withdraw_fees_permissionless). If pubkey default, the","bank doesn't support this feature, and the fees must be collected manually (withdraw_fees)."],"type":"pubkey"},{"name":"cache","docs":["Cached bank metrics (interest rates, oracle price, etc.)"],"type":{"defined":{"name":"BankCache"}}},{"name":"lending_position_count","docs":["Number of user lending positions currently open in this bank","* For banks created prior to 0.1.4, this is the number of positions opened/closed after","0.1.4 goes live, and may be negative.","* For banks created in 0.1.4 or later, this is the number of positions open in total, and","the bank may safely be closed if this is zero. Will never go negative."],"type":"i32"},{"name":"borrowing_position_count","docs":["Number of user borrowing positions currently open in this bank","* For banks created prior to 0.1.4, this is the number of positions opened/closed after","0.1.4 goes live, and may be negative.","* For banks created in 0.1.4 or later, this is the number of positions open in total, and","the bank may safely be closed if this is zero. Will never go negative."],"type":"i32"},{"name":"_padding_0","docs":["Reserved for future use"],"type":{"array":["u8",16]}},{"name":"integration_acc_1","docs":["Integration account slot 1 (default Pubkey for non-integrations).","- Kamino: reserve","- Drift: spot market","- Solend: reserve","- JupLend: lending state"],"type":"pubkey"},{"name":"integration_acc_2","docs":["Integration account slot 2 (default Pubkey for non-integrations).","- Kamino: obligation","- Drift: user","- Solend: obligation","- JupLend: fToken vault"],"type":"pubkey"},{"name":"integration_acc_3","docs":["Integration account slot 3 (default Pubkey for non-integrations).","- Drift: user stats","- JupLend: withdraw intermediary ATA (ATA of liquidity_vault_authority for bank mint)"],"type":"pubkey"},{"name":"rate_limiter","docs":["Rate limiter for controlling withdraw/borrow outflow.","Tracks net outflow (outflows - inflows) in native tokens."],"type":{"defined":{"name":"BankRateLimiter"}}},{"name":"_pad_0","type":{"array":["u8",16]}},{"name":"_padding_1","type":{"array":[{"array":["u64",2]},7]}}]}},{"name":"BankCache","docs":["A read-only cache of the bank's key metrics, e.g. spot interest/fee rates."],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"base_rate","docs":["Actual (spot) interest/fee rates of the bank, based on utilization","* APR (annual percentage rate) values","* From 0-1000%, as u32, e.g. u32::MAX = 1000%, u32::MAX/2 = 500%, etc"],"type":"u32"},{"name":"lending_rate","docs":["Equivalent to `base_rate` * utilization","* From 0-1000%, as u32, e.g. u32::MAX = 1000%, u32::MAX/2 = 500%, etc"],"type":"u32"},{"name":"borrowing_rate","docs":["Equivalent to `base_rate` * (1 + ir_fees) + fixed_fees","* From 0-1000%, as u32, e.g. u32::MAX = 1000%, u32::MAX/2 = 500%, etc"],"type":"u32"},{"name":"interest_accumulated_for","docs":["* in seconds"],"type":"u32"},{"name":"accumulated_since_last_update","docs":["equivalent to (share value increase in the last `interest_accumulated_for` seconds *","shares), i.e. the delta in `asset_share_value`, in token.","* Note: if the tx that triggered this cache update increased or decreased the net shares,","this value still reports using the PRE-CHANGE share amount, since interest is always","earned on that amount.","* in token, in native decimals, as I80F48"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"last_oracle_price","docs":["Oracle price used in the last instruction that consumed an oracle price","* Only updated when instruction uses an oracle price, not updated for operations that don't","require prices (e.g., deposit, repay)","* Price in USD, with no price bias","* Zero if never updated"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"last_oracle_price_timestamp","docs":["Unix timestamp (seconds) when last_oracle_price was last updated","* Used to determine staleness of cached price","* Zero if never updated"],"type":"i64"},{"name":"last_oracle_price_confidence","docs":["Confidence interval reported by the oracle when last_oracle_price was fetched","* Always non-negative","* Zero if never updated","* Note: this value is the confidence reported by oracles, multiplied by `STD_DEV_MULTIPLE`"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liq_cache_flags","docs":["Liquidation cache flags, set during receivership flow.","* 1 (LIQ_CACHE_LOCKED_FLAG) - We \"lock\" the liquidation cache when writing to it in Start","Liquidate as an additional safeguard, if the liquidation prices stored here were to be","edited between start and end, it would completely break the risk engine. End validates that","the lock is set, panics if not, and removes it - which prevents footguns if the cache was","e.g. accidently set to default. The lock is also removed when a Balance is closed via","withdraw_all, repay_all, or close_balance, but only when the account has","ACCOUNT_IN_RECEIVERSHIP set, so that operations on unrelated accounts sharing the same","bank do not interfere with an in-progress liquidation."],"type":"u8"},{"name":"_padding","type":{"array":["u8",23]}},{"name":"liquidation_price_rt","docs":["Cached real-time price for receivership liquidation."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liquidation_price_rt_confidence","docs":["Cached real-time price confidence for receivership liquidation."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liquidation_price_twap","docs":["Cached TWAP price for receivership liquidation."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liquidation_price_twap_confidence","docs":["Cached TWAP price confidence for receivership liquidation."],"type":{"defined":{"name":"WrappedI80F48"}}}]}},{"name":"BankConfig","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"asset_weight_init","docs":["Discount factor for asset values in initial margin calculation (0 to 1).","E.g., 0.8 means assets count as 80% of their value for borrowing purposes."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_weight_maint","docs":["Discount factor for asset values in maintenance margin calculation (0 to 2).","Used for liquidation eligibility. Generally >= asset_weight_init."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_weight_init","docs":["Premium factor for liability values in initial margin calculation (>= 1).","E.g., 1.2 means liabilities count as 120% of their value for borrowing purposes."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_weight_maint","docs":["Premium factor for liability values in maintenance margin calculation (>= 1).","Used for liquidation eligibility. Generally <= liability_weight_init."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"deposit_limit","docs":["Maximum total deposits allowed in this bank, in native token units (0 = no limit)"],"type":"u64"},{"name":"interest_rate_config","docs":["Interest rate model configuration"],"type":{"defined":{"name":"InterestRateConfig"}}},{"name":"operational_state","docs":["Current operational state of the bank (Paused, Operational, ReduceOnly, KilledByBankruptcy)"],"type":{"defined":{"name":"BankOperationalState"}}},{"name":"oracle_setup","docs":["Oracle type used for price feeds"],"type":{"defined":{"name":"OracleSetup"}}},{"name":"oracle_keys","docs":["Oracle account keys (usage depends on oracle_setup type)"],"type":{"array":["pubkey",5]}},{"name":"_pad0","type":{"array":["u8",6]}},{"name":"borrow_limit","docs":["Maximum total borrows allowed in this bank, in native token units (0 = no limit)"],"type":"u64"},{"name":"risk_tier","docs":["Risk tier for this bank (Collateral or Isolated)"],"type":{"defined":{"name":"RiskTier"}}},{"name":"asset_tag","docs":["Determines what kinds of assets users of this bank can interact with. Options:","* `ASSET_TAG_DEFAULT` (0) - A regular asset that can be comingled with any other regular","asset or with `ASSET_TAG_SOL`","* `ASSET_TAG_SOL` (1) - Accounts with a SOL position can comingle with **either**","`ASSET_TAG_DEFAULT` or `ASSET_TAG_STAKED` positions, but not both","* `ASSET_TAG_STAKED` (2) - Staked SOL assets. Accounts with a STAKED position can only","deposit other STAKED assets or SOL (`ASSET_TAG_SOL`) and can only borrow SOL","* `ASSET_TAG_KAMINO` (3) - Treated the same as `ASSET_TAG_DEFAULT`","* `ASSET_TAG_DRIFT` (4) - Treated the same as `ASSET_TAG_DEFAULT`","* `ASSET_TAG_SOLEND` (5) - Treated the same as `ASSET_TAG_DEFAULT`"],"type":"u8"},{"name":"config_flags","docs":["Flags for various config options","* 1 - Always set if bank created in 0.1.4 or later, or if migrated to the new pyth oracle","setup from a prior version. Not set in 0.1.3 or earlier banks using pyth that have not yet","migrated. Does nothing for banks that use switchboard.","* 2, 4, 8, 16, etc - reserved for future use."],"type":"u8"},{"name":"_pad1","type":{"array":["u8",5]}},{"name":"total_asset_value_init_limit","docs":["USD denominated limit for calculating asset value for initialization margin requirements.","Example, if total SOL deposits are equal to $1M and the limit it set to $500K, then SOL","assets will be discounted by 50%.","","In other words the max value of liabilities that can be backed by the asset is $500K. This","is useful for limiting the damage of oracle attacks.","","Value is UI USD value, for example value 100 -> $100"],"type":"u64"},{"name":"oracle_max_age","docs":["Time window in seconds for the oracle price feed to be considered live."],"type":"u16"},{"name":"_padding0","type":{"array":["u8",2]}},{"name":"oracle_max_confidence","docs":["From 0-100%, if the confidence exceeds this value, the oracle is considered invalid. Note:","the confidence adjustment is capped at 5% regardless of this value.","* 0 falls back to using the default 10% instead, i.e., U32_MAX_DIV_10","* A %, as u32, e.g. 100% = u32::MAX, 50% = u32::MAX/2, etc."],"type":"u32"},{"name":"fixed_price","docs":["Stored oracle price for `OracleSetup::Fixed`, otherwise does nothing"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"_padding1","type":{"array":["u8",16]}}]}},{"name":"BankConfigCompact","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"asset_weight_init","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_weight_maint","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_weight_init","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_weight_maint","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"deposit_limit","type":"u64"},{"name":"interest_rate_config","type":{"defined":{"name":"InterestRateConfigCompact"}}},{"name":"operational_state","type":{"defined":{"name":"BankOperationalState"}}},{"name":"borrow_limit","type":"u64"},{"name":"risk_tier","type":{"defined":{"name":"RiskTier"}}},{"name":"asset_tag","docs":["Determines what kinds of assets users of this bank can interact with. Options:","* `ASSET_TAG_DEFAULT` (0) - A regular asset that can be comingled with any other regular","asset or with `ASSET_TAG_SOL`","* `ASSET_TAG_SOL` (1) - Accounts with a SOL position can comingle with **either**","`ASSET_TAG_DEFAULT` or `ASSET_TAG_STAKED` positions, but not both","* `ASSET_TAG_STAKED` (2) - Staked SOL assets. Accounts with a STAKED position can only","deposit other STAKED assets or SOL (`ASSET_TAG_SOL`) and can only borrow SOL","* `ASSET_TAG_KAMINO` (3) - Treated the same as `ASSET_TAG_DEFAULT`","* `ASSET_TAG_DRIFT` (4) - Treated the same as `ASSET_TAG_DEFAULT`","* `ASSET_TAG_SOLEND` (5) - Treated the same as `ASSET_TAG_DEFAULT`"],"type":"u8"},{"name":"config_flags","docs":["Flags for various config options","* 1 - Always set if bank created in 0.1.4 or later, or if migrated to the new oracle setup","from a prior version. Not set in 0.1.3 or earlier banks that have not yet migrated.","* 2, 4, 8, 16, etc - reserved for future use."],"type":"u8"},{"name":"_pad0","type":{"array":["u8",5]}},{"name":"total_asset_value_init_limit","docs":["USD denominated limit for calculating asset value for initialization margin requirements.","Example, if total SOL deposits are equal to $1M and the limit it set to $500K, then SOL","assets will be discounted by 50%.","","In other words the max value of liabilities that can be backed by the asset is $500K. This","is useful for limiting the damage of oracle attacks.","","Value is UI USD value, for example value 100 -> $100"],"type":"u64"},{"name":"oracle_max_age","docs":["Time window in seconds for the oracle price feed to be considered live."],"type":"u16"},{"name":"oracle_max_confidence","docs":["From 0-100%, if the confidence exceeds this value, the oracle is considered invalid. Note:","the confidence adjustment is capped at 5% regardless of this value.","* 0% = use the default (10%)","* A %, as u32, e.g. 100% = u32::MAX, 50% = u32::MAX/2, etc."],"type":"u32"}]}},{"name":"BankConfigOpt","type":{"kind":"struct","fields":[{"name":"asset_weight_init","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"asset_weight_maint","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"liability_weight_init","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"liability_weight_maint","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"deposit_limit","type":{"option":"u64"}},{"name":"borrow_limit","type":{"option":"u64"}},{"name":"operational_state","type":{"option":{"defined":{"name":"BankOperationalState"}}}},{"name":"interest_rate_config","type":{"option":{"defined":{"name":"InterestRateConfigOpt"}}}},{"name":"risk_tier","type":{"option":{"defined":{"name":"RiskTier"}}}},{"name":"asset_tag","type":{"option":"u8"}},{"name":"total_asset_value_init_limit","type":{"option":"u64"}},{"name":"oracle_max_confidence","type":{"option":"u32"}},{"name":"oracle_max_age","type":{"option":"u16"}},{"name":"permissionless_bad_debt_settlement","type":{"option":"bool"}},{"name":"freeze_settings","type":{"option":"bool"}},{"name":"tokenless_repayments_allowed","type":{"option":"bool"}}]}},{"name":"BankMetadata","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"bank","docs":["Bank this metadata corresponds to"],"type":"pubkey"},{"name":"placeholder","type":"u64"},{"name":"ticker","docs":["The token's ticker name, e.g. USDC","* utf-8"],"type":{"array":["u8",64]}},{"name":"description","docs":["The token's plain english description, e.g US Dollar Coin","* utf-8"],"type":{"array":["u8",128]}},{"name":"data_blob","docs":["Reserved for future use. Room for a very small icon or something else cool"],"type":{"array":["u8",256]}},{"name":"end_description_byte","docs":["The last data byte in description (padding follows)"],"type":"u16"},{"name":"end_data_blob","docs":["The last data byte in data_blob (padding follows)"],"type":"u16"},{"name":"end_ticker_byte","docs":["The last data byte in ticker (padding follows)"],"type":"u8"},{"name":"bump","type":"u8"},{"name":"_pad0","type":{"array":["u8",2]}}]}},{"name":"BankOperationalState","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Paused"},{"name":"Operational"},{"name":"ReduceOnly"},{"name":"KilledByBankruptcy"}]}},{"name":"BankRateLimiter","docs":["Per-bank rate limiting configuration and state.","Tracks net outflow in native tokens."],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"hourly","docs":["Hourly window rate limiter (native tokens)."],"type":{"defined":{"name":"RateLimitWindow"}}},{"name":"daily","docs":["Daily window rate limiter (native tokens)."],"type":{"defined":{"name":"RateLimitWindow"}}}]}},{"name":"DeleverageEvent","type":{"kind":"struct","fields":[{"name":"marginfi_account","type":"pubkey"},{"name":"risk_admin","type":"pubkey"},{"name":"deleveragee_assets_seized","type":"f64"},{"name":"deleveragee_liability_repaid","type":"f64"}]}},{"name":"DeleverageWithdrawFlowEvent","docs":["Emitted for deleverage-only withdraw outflows.","The delegate flow admin aggregates these off-chain and","updates the deleverage daily withdraws via `update_deleverage_withdrawals`."],"type":{"kind":"struct","fields":[{"name":"group","type":"pubkey"},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"outflow_usd","docs":["Equity-denominated outflow value in USD, rounded to integer."],"type":"u32"},{"name":"current_timestamp","docs":["Unix timestamp when the flow was recorded"],"type":"i64"}]}},{"name":"DriftConfigCompact","docs":["Used to configure Drift banks. A simplified version of `BankConfigCompact` which omits most","values related to interest since Drift banks cannot earn interest or be borrowed against."],"type":{"kind":"struct","fields":[{"name":"oracle","type":"pubkey"},{"name":"asset_weight_init","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_weight_maint","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"deposit_limit","type":"u64"},{"name":"oracle_setup","docs":["Either `DriftPythPull` or `DriftSwitchboardPull`"],"type":{"defined":{"name":"OracleSetup"}}},{"name":"operational_state","docs":["Bank operational state - allows starting banks in paused state"],"type":{"defined":{"name":"BankOperationalState"}}},{"name":"risk_tier","docs":["Risk tier - determines if assets can be borrowed in isolation"],"type":{"defined":{"name":"RiskTier"}}},{"name":"config_flags","docs":["Config flags for future-proofing"],"type":"u8"},{"name":"total_asset_value_init_limit","type":"u64"},{"name":"oracle_max_age","type":"u16"},{"name":"oracle_max_confidence","docs":["Oracle confidence threshold (0 = use default 10%)"],"type":"u32"}]}},{"name":"EditStakedSettingsEvent","type":{"kind":"struct","fields":[{"name":"group","type":"pubkey"},{"name":"settings","type":{"defined":{"name":"StakedSettingsEditConfig"}}}]}},{"name":"EmodeConfig","docs":["An emode configuration. Each bank has one such configuration, but this may also be the","intersection of many configurations (see `reconcile_emode_configs`). For example, the risk","engine creates such an intersection from all the emode config of all banks the user is borrowing","from."],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"entries","type":{"array":[{"defined":{"name":"EmodeEntry"}},10]}}]}},{"name":"EmodeEntry","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"collateral_bank_emode_tag","docs":["emode_tag of the bank(s) whose collateral you wish to treat preferentially."],"type":"u16"},{"name":"flags","docs":["* APPLIES_TO_ISOLATED (1) - (NOT YET IMPLEMENTED) if set, isolated banks with this tag","also benefit. If not set, isolated banks continue to offer zero collateral, even if they","use this tag.","* 2, 4, 8, 16, 32, etc - reserved for future use"],"type":"u8"},{"name":"pad0","type":{"array":["u8",5]}},{"name":"asset_weight_init","docs":["Note: If set below the collateral bank's weight, does nothing."],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_weight_maint","docs":["Note: If set below the collateral bank's weight, does nothing."],"type":{"defined":{"name":"WrappedI80F48"}}}]}},{"name":"EmodeSettings","docs":["Controls the bank's e-mode configuration, allowing certain collateral sources to be treated more","favorably as collateral when used to borrow from this bank."],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"emode_tag","docs":["This bank's NON-unique id that other banks will use to determine what emode rate to use when","this bank is offered as collateral.","","For example, all stablecoin banks might share the same emode_tag, and in their entries, each","such stablecoin bank will recognize that collateral sources with this \"stable\" tag get","preferential weights. When a new stablecoin is added that is considered riskier, it may get","a new, less favorable emode tag, and eventually get upgraded to the same one as the other","stables","","* 0 is in an invalid tag and will do nothing."],"type":"u16"},{"name":"pad0","type":{"array":["u8",6]}},{"name":"timestamp","docs":["Unix timestamp from the system clock when emode state was last updated"],"type":"i64"},{"name":"flags","docs":["EMODE_ON (1) - If set, at least one entry is configured","2, 4, 8, etc, Reserved for future use"],"type":"u64"},{"name":"emode_config","type":{"defined":{"name":"EmodeConfig"}}}]}},{"name":"ExecuteOrderBalanceRecord","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"bank","type":"pubkey"},{"name":"is_asset","type":"u8"},{"name":"_pad0","type":{"array":["u8",5]}},{"name":"tag","type":"u16"},{"name":"shares","type":{"defined":{"name":"WrappedI80F48"}}}]}},{"name":"ExecuteOrderRecord","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"order","type":"pubkey"},{"name":"executor","type":"pubkey"},{"name":"balance_states","type":{"array":[{"defined":{"name":"ExecuteOrderBalanceRecord"}},14]}},{"name":"active_balance_count","type":"u8"},{"name":"inactive_balance_count","type":"u8"},{"name":"_reserved0","type":{"array":["u8",6]}},{"name":"order_start_health","type":{"defined":{"name":"WrappedI80F48"}}}]}},{"name":"FeeState","docs":["Unique per-program. The Program Owner uses this account to administrate fees collected by the protocol"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"key","docs":["The fee state's own key. A PDA derived from just `b\"feestate\"`"],"type":"pubkey"},{"name":"global_fee_admin","docs":["Can modify fees"],"type":"pubkey"},{"name":"global_fee_wallet","docs":["The base wallet for all protocol fees. All SOL fees go to this wallet. All non-SOL fees go","to the cannonical ATA of this wallet for that asset."],"type":"pubkey"},{"name":"placeholder0","type":"u64"},{"name":"bank_init_flat_sol_fee","docs":["Flat fee assessed when a new bank is initialized, in lamports.","* In SOL, in native decimals."],"type":"u32"},{"name":"bump_seed","type":"u8"},{"name":"_padding0","type":{"array":["u8",3]}},{"name":"liquidation_max_fee","docs":["Liquidators can claim at this premium, when liquidating an asset in receivership","liquidation, e.g. (1 + this) * amount repaid >= asset seized","* A percentage"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"program_fee_fixed","docs":["Fee collected by the program owner from all groups","* A percentage"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"program_fee_rate","docs":["Fee collected by the program owner from all groups","* A percentage"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"panic_state","docs":["When the global admin pauses the protocol in the event of an emergency, information about","the pause duration will be stored here and propagated to groups."],"type":{"defined":{"name":"PanicState"}}},{"name":"placeholder1","type":"u64"},{"name":"liquidation_flat_sol_fee","docs":["Flat fee assessed for insurance/program use when a liquidation is executed","* In SOL, in native decimals."],"type":"u32"},{"name":"order_init_flat_sol_fee","docs":["Flat fee assessed for preventing spam use when creating an order","* In SOL, in native decimals."],"type":"u32"},{"name":"order_execution_max_fee","docs":["Take-profit Orders can be executed at this premium, which Keepers are allowed to keep (no","pun intended) e.g. (1 + this) * amount repaid >= asset seized","* A percentage"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"_reserved1","type":{"array":["u8",32]}}]}},{"name":"FeeStateCache","docs":["Cached fee configuration propagated from the global FeeState"],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"global_fee_wallet","docs":["The wallet that receives program-level fees"],"type":"pubkey"},{"name":"program_fee_fixed","docs":["Fixed fee APR charged to borrowers (program-level)"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"program_fee_rate","docs":["Proportional fee rate on interest (program-level)"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"last_update","docs":["Unix timestamp of the last fee state propagation"],"type":"i64"}]}},{"name":"GroupEventHeader","type":{"kind":"struct","fields":[{"name":"signer","type":{"option":"pubkey"}},{"name":"marginfi_group","type":"pubkey"}]}},{"name":"GroupRateLimiter","docs":["Per-group rate limiting configuration and state.","Tracks aggregate net outflow in USD."],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"hourly","docs":["Hourly window rate limiter (USD)."],"type":{"defined":{"name":"RateLimitWindow"}}},{"name":"daily","docs":["Daily window rate limiter (USD)."],"type":{"defined":{"name":"RateLimitWindow"}}}]}},{"name":"HealthCache","docs":["A read-only cache of the internal risk engine's information. Only valid in borrow/withdraw if","the tx does not fail. To see the state in any context, e.g. to figure out if the risk engine is","failing due to some bad price information, use `pulse_health`."],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"asset_value","docs":["Internal risk engine asset value, using initial weight (e.g. what is used for borrowing","purposes), with all confidence adjustments, and other discounts on price.","* Uses EMA price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_value","docs":["Internal risk engine liability value, using initial weight (e.g. what is used for borrowing","purposes), with all confidence adjustments, and other discounts on price.","* Uses EMA price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_value_maint","docs":["Internal risk engine asset value, using maintenance weight (e.g. what is used for","liquidation purposes), with all confidence adjustments.","* Zero if the risk engine failed to load","* Uses SPOT price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_value_maint","docs":["Internal risk engine liability value, using maintenance weight (e.g. what is used for","liquidation purposes), with all confidence adjustments.","* Zero if the risk engine failed to load","* Uses SPOT price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_value_equity","docs":["The \"true\" value of assets without any confidence or weight adjustments. Internally, used","only for bankruptcies.","* Zero if the risk engine failed to load","* Uses EMA price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_value_equity","docs":["The \"true\" value of liabilities without any confidence or weight adjustments.","Internally, used only for bankruptcies.","* Zero if the risk engine failed to load","* Uses EMA price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"timestamp","docs":["Unix timestamp from the system clock when this cache was last updated"],"type":"i64"},{"name":"flags","docs":["The flags that indicate the state of the health cache. This is a u32 bitfield, where each","bit represents a flag.","","* HEALTHY = 1 - If set, the account cannot be liquidated. If 0, the account is unhealthy and","can be liquidated.","* ENGINE STATUS = 2 - If set, the engine did not error during the last health pulse. If 0,","the engine would have errored and this cache is likely invalid. `RiskEngineInitRejected`","is ignored and will allow the flag to be set anyways.","* ORACLE OK = 4 - If set, the engine did not error due to an oracle issue. If 0, engine was","passed a bad bank or oracle account, or an oracle was stale. Check the order in which","accounts were passed and ensure each balance has the correct banks/oracles, and that","oracle cranks ran recently enough. Check `internal_err` and `err_index` for more details","in some circumstances. Invalid if generated after borrow/withdraw (these instructions will","ignore oracle issues if health is still satisfactory with some balance zeroed out).","* 8, 16, 32, 64, 128, etc - reserved for future use"],"type":"u32"},{"name":"mrgn_err","docs":["If the engine errored, look here for the error code. If the engine returns ok, you may also","check here to see if the risk engine rejected this tx (3009)."],"type":"u32"},{"name":"prices","docs":["Each price corresponds to that index of Balances in the LendingAccount. Useful for debugging","or liquidator consumption, to determine how a user's position is priced internally.","* An f64 stored as bytes"],"type":{"array":[{"array":["u8",8]},16]}},{"name":"internal_err","docs":["Errors in asset oracles are ignored (with prices treated as zero). If you see a zero price","and the `ORACLE_OK` flag is not set, check here to see what error was ignored internally."],"type":"u32"},{"name":"err_index","docs":["Index in `balances` where `internal_err` appeared"],"type":"u8"},{"name":"program_version","docs":["Since 0.1.3, the version will be encoded here. See PROGRAM_VERSION."],"type":"u8"},{"name":"pad0","type":{"array":["u8",2]}},{"name":"internal_liq_err","docs":["Error code from the liquidation health check during the last health pulse (0 if none)"],"type":"u32"},{"name":"internal_bankruptcy_err","docs":["Error code from the bankruptcy check during the last health pulse (0 if none)"],"type":"u32"},{"name":"reserved0","type":{"array":["u8",32]}},{"name":"reserved1","type":{"array":["u8",16]}}]}},{"name":"HealthPulseEvent","type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"},{"name":"health_cache","type":{"defined":{"name":"HealthCache"}}}]}},{"name":"InterestRateConfig","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"optimal_utilization_rate","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"plateau_interest_rate","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"max_interest_rate","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"insurance_fee_fixed_apr","docs":["Goes to insurance, funds `collected_insurance_fees_outstanding`"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"insurance_ir_fee","docs":["Goes to insurance, funds `collected_insurance_fees_outstanding`"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"protocol_fixed_fee_apr","docs":["Earned by the group, goes to `collected_group_fees_outstanding`"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"protocol_ir_fee","docs":["Earned by the group, goes to `collected_group_fees_outstanding`"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"protocol_origination_fee","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"zero_util_rate","docs":["The base rate at utilization = 0","* a %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX"],"type":"u32"},{"name":"hundred_util_rate","docs":["The base rate at utilization = 100","* a %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX"],"type":"u32"},{"name":"points","docs":["The base rate at various points between 0 and 100%, exclusive. Essentially a piece-wise","linear curve.","* always in ascending order, e.g. points[0] = first kink point, points[1] = second kink","point, and so forth.","* points where util = 0 are unused"],"type":{"array":[{"defined":{"name":"RatePoint"}},5]}},{"name":"curve_type","docs":["Determines which interest rate curve implementation is active. 0 (INTEREST_CURVE_LEGACY) =","legacy three point curve, 1 (INTEREST_CURVE_SEVEN_POINT) = multi-point curve."],"type":"u8"},{"name":"_pad0","type":{"array":["u8",7]}},{"name":"_padding1","type":{"array":["u8",32]}},{"name":"_padding2","type":{"array":["u8",16]}},{"name":"_padding3","type":{"array":["u8",8]}}]}},{"name":"InterestRateConfigCompact","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"insurance_fee_fixed_apr","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"insurance_ir_fee","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"protocol_fixed_fee_apr","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"protocol_ir_fee","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"protocol_origination_fee","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"zero_util_rate","docs":["The base rate at utilization = 0","* a %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX"],"type":"u32"},{"name":"hundred_util_rate","docs":["The base rate at utilization = 100","* a %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX"],"type":"u32"},{"name":"points","docs":["The base rate at various points between 0 and 100%, exclusive. Essentially a piece-wise","linear curve.","* always in ascending order, e.g. points[0] = first kink point, points[1] = second kink","point, and so forth.","* points where util = 0 are unused"],"type":{"array":[{"defined":{"name":"RatePoint"}},5]}}]}},{"name":"InterestRateConfigOpt","type":{"kind":"struct","fields":[{"name":"insurance_fee_fixed_apr","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"insurance_ir_fee","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"protocol_fixed_fee_apr","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"protocol_ir_fee","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"protocol_origination_fee","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"zero_util_rate","docs":["The base rate at utilization = 0","* a %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX"],"type":{"option":"u32"}},{"name":"hundred_util_rate","docs":["The base rate at utilization = 100","* a %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX"],"type":{"option":"u32"}},{"name":"points","docs":["The base rate at various points between 0 and 100%, exclusive. Essentially a piece-wise","linear curve.","* always in ascending order, e.g. points[0] = first kink point, points[1] = second kink","point, and so forth.","* points where util = 0 are unused"],"type":{"option":{"array":[{"defined":{"name":"RatePoint"}},5]}}}]}},{"name":"JuplendConfigCompact","docs":["Used to configure JupLend banks. A simplified version of `BankConfigCompact` which omits most","values related to interest since JupLend banks cannot earn interest or be borrowed against.","","Note: JupLend banks do not take an Operational State, they always start in `Paused` state and","are set to `Operational` via `juplend_init_position` (seed deposit + protocol fToken vault)."],"type":{"kind":"struct","fields":[{"name":"oracle","type":"pubkey"},{"name":"asset_weight_init","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_weight_maint","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"deposit_limit","type":"u64"},{"name":"oracle_setup","docs":["Either `JuplendPythPull` or `JuplendSwitchboardPull`"],"type":{"defined":{"name":"OracleSetup"}}},{"name":"risk_tier","docs":["Isolated or Collateral"],"type":{"defined":{"name":"RiskTier"}}},{"name":"config_flags","docs":["Config flags for future-proofing, currently ignored"],"type":"u8"},{"name":"total_asset_value_init_limit","docs":["In $"],"type":"u64"},{"name":"oracle_max_age","docs":["In seconds"],"type":"u16"},{"name":"oracle_max_confidence","docs":["Oracle confidence threshold (0 = use default 10%)"],"type":"u32"}]}},{"name":"KaminoConfigCompact","docs":["Used to configure Kamino banks. A simplified version of `BankConfigCompact` which omits most","values related to interest since Kamino banks cannot earn interest or be borrowed against."],"type":{"kind":"struct","fields":[{"name":"oracle","type":"pubkey"},{"name":"asset_weight_init","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_weight_maint","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"deposit_limit","type":"u64"},{"name":"oracle_setup","docs":["Either `KaminoPythPush` or `KaminoSwitchboardPull`"],"type":{"defined":{"name":"OracleSetup"}}},{"name":"operational_state","docs":["Bank operational state - allows starting banks in paused state"],"type":{"defined":{"name":"BankOperationalState"}}},{"name":"risk_tier","docs":["Risk tier - determines if assets can be borrowed in isolation"],"type":{"defined":{"name":"RiskTier"}}},{"name":"config_flags","docs":["Config flags for future-proofing"],"type":"u8"},{"name":"total_asset_value_init_limit","type":"u64"},{"name":"oracle_max_age","docs":["Currently unused: Kamino's oracle age applies to kamino banks."],"type":"u16"},{"name":"oracle_max_confidence","docs":["Oracle confidence threshold (0 = use default 10%)"],"type":"u32"}]}},{"name":"KeeperCloseOrderEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"order","type":"pubkey"}]}},{"name":"Lending","docs":["Minimal representation of the on-chain JupLend `Lending` account.","","Notes:","- We intentionally use a **zero-copy** layout here to match how other integrations load large","external accounts (and to avoid paying Borsh (de)serialization cost on every access).","- `repr(C, packed)` keeps the byte layout identical to a field-by-field serialization","(i.e. no implicit padding). This is important because `Pubkey` has alignment=1 while `u64`","has alignment=8; using plain `repr(C)` would insert padding before the first `u64`."],"serialization":"bytemuck","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"f_token_mint","type":"pubkey"},{"name":"lending_id","type":"u16"},{"name":"decimals","docs":["number of decimals for the fToken, same as underlying mint"],"type":"u8"},{"name":"rewards_rate_model","docs":["PDA of rewards rate model (LRRM)"],"type":"pubkey"},{"name":"liquidity_exchange_price","docs":["exchange price in the liquidity layer (no rewards)"],"type":"u64"},{"name":"token_exchange_price","docs":["exchange price between fToken and underlying (with rewards)"],"type":"u64"},{"name":"last_update_timestamp","docs":["unix timestamp when exchange prices were updated last"],"type":"u64"},{"name":"token_reserves_liquidity","type":"pubkey"},{"name":"supply_position_on_liquidity","type":"pubkey"},{"name":"bump","type":"u8"}]}},{"name":"LendingAccount","docs":["The lending account holds up to 16 balance positions for a user."],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"balances","docs":["Array of balance positions (max 16). Sorted in descending order by bank_pk."],"type":{"array":[{"defined":{"name":"Balance"}},16]}},{"name":"last_tag_used","docs":["Last allocated balance tag (u16), used to find the next unused tag."],"type":"u16"},{"name":"_pad1","docs":["Reserved for future use"],"type":{"array":["u8",6]}},{"name":"_padding","docs":["Reserved for future use"],"type":{"array":["u64",7]}}]}},{"name":"LendingAccountBorrowEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"LendingAccountDepositEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"LendingAccountLiquidateEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"liquidatee_marginfi_account","type":"pubkey"},{"name":"liquidatee_marginfi_account_authority","type":"pubkey"},{"name":"asset_bank","type":"pubkey"},{"name":"asset_mint","type":"pubkey"},{"name":"liability_bank","type":"pubkey"},{"name":"liability_mint","type":"pubkey"},{"name":"liquidatee_pre_health","type":"f64"},{"name":"liquidatee_post_health","type":"f64"},{"name":"pre_balances","type":{"defined":{"name":"LiquidationBalances"}}},{"name":"post_balances","type":{"defined":{"name":"LiquidationBalances"}}}]}},{"name":"LendingAccountRepayEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"amount","type":"u64"},{"name":"close_balance","type":"bool"}]}},{"name":"LendingAccountWithdrawEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"amount","type":"u64"},{"name":"close_balance","type":"bool"}]}},{"name":"LendingPoolBankAccrueInterestEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"delta","type":"u64"},{"name":"fees_collected","type":"f64"},{"name":"insurance_collected","type":"f64"}]}},{"name":"LendingPoolBankCollectFeesEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"group_fees_collected","type":"f64"},{"name":"group_fees_outstanding","type":"f64"},{"name":"insurance_fees_collected","type":"f64"},{"name":"insurance_fees_outstanding","type":"f64"}]}},{"name":"LendingPoolBankConfigureEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"config","type":{"defined":{"name":"BankConfigOpt"}}}]}},{"name":"LendingPoolBankConfigureFrozenEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"deposit_limit","type":"u64"},{"name":"borrow_limit","type":"u64"}]}},{"name":"LendingPoolBankConfigureOracleEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"oracle_setup","type":"u8"},{"name":"oracle","type":"pubkey"}]}},{"name":"LendingPoolBankCreateEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"}]}},{"name":"LendingPoolBankHandleBankruptcyEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"bad_debt","type":"f64"},{"name":"covered_amount","type":"f64"},{"name":"socialized_amount","type":"f64"}]}},{"name":"LendingPoolBankSetFixedOraclePriceEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"price","type":{"defined":{"name":"WrappedI80F48"}}}]}},{"name":"LendingPoolSuperAdminDepositEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"transfer_amount","docs":["Amount requested in SPL transfer instruction."],"type":"u64"},{"name":"vault_inflow_amount","docs":["Assumed vault inflow. Token-2022 transfer fees are not handled by this instruction path."],"type":"u64"}]}},{"name":"LendingPoolSuperAdminWithdrawEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"vault_outflow_amount","type":"u64"}]}},{"name":"LiquidationBalances","type":{"kind":"struct","fields":[{"name":"liquidatee_asset_balance","type":"f64"},{"name":"liquidatee_liability_balance","type":"f64"},{"name":"liquidator_asset_balance","type":"f64"},{"name":"liquidator_liability_balance","type":"f64"}]}},{"name":"LiquidationCache","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"asset_value_maint","docs":["Internal risk engine asset value snapshot taken when liquidation begins, using maintenance","weight with all confidence adjustments.","* Uses SPOT price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_value_maint","docs":["Internal risk engine liability value snapshot taken when liquidation begins, using","maintenance weight with all confidence adjustments.","* Uses SPOT price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_value_equity","docs":["Actual cash value of assets pre-liquidation (inclusive of price adjustment for oracle","confidence, but without any weights)","* Liquidator is allowed to seize up to `liability_value_equity` - this amount","* Uses EMA price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"liability_value_equity","docs":["Actual cash value of liabilities pre-liquidation (inclusive of price adjustment for oracle","confidence, but without any weights)","* Liquidator is allowed to seize up to this amount - `asset_value_equity`","* Uses EMA price","* In dollars"],"type":{"defined":{"name":"WrappedI80F48"}}},{"name":"_placeholder","type":"u64"},{"name":"_reserved0","type":{"array":["u8",32]}}]}},{"name":"LiquidationEntry","docs":["Used to record key details of the last few liquidation events on the account"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"asset_amount_seized","docs":["Dollar amount seized","* An f64 stored as bytes"],"type":{"array":["u8",8]}},{"name":"liab_amount_repaid","docs":["Dollar amount repaid","* An f64 stored as bytes"],"type":{"array":["u8",8]}},{"name":"placeholder0","type":"u64"},{"name":"timestamp","type":"i64"},{"name":"_reserved0","type":{"array":["u8",16]}}]}},{"name":"LiquidationReceiverEvent","type":{"kind":"struct","fields":[{"name":"marginfi_account","type":"pubkey"},{"name":"liquidation_receiver","type":"pubkey"},{"name":"liquidatee_assets_seized","type":"f64"},{"name":"liquidatee_liability_repaid","type":"f64"},{"name":"lamps_fee_paid","type":"u32"}]}},{"name":"LiquidationRecord","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"key","docs":["This account's own key. A PDA derived from `marginfi_account`"],"type":"pubkey"},{"name":"marginfi_account","docs":["Account this record tracks"],"type":"pubkey"},{"name":"record_payer","docs":["The key that paid to create this account. At some point, we may allow this wallet to reclaim","the rent paid to open a record."],"type":"pubkey"},{"name":"liquidation_receiver","docs":["The liquidator taking receivership of the `marginfi_account` to complete a liquidation. Pays","the liquidation fee.","* Always pubkey default unless actively within a liquidation event."],"type":"pubkey"},{"name":"entries","docs":["Basic historical data for the last few liquidation events on this account"],"type":{"array":[{"defined":{"name":"LiquidationEntry"}},4]}},{"name":"cache","type":{"defined":{"name":"LiquidationCache"}}},{"name":"_reserved0","type":{"array":["u8",64]}},{"name":"_reserved2","type":{"array":["u8",16]}},{"name":"_reserved3","type":{"array":["u8",8]}}]}},{"name":"MarginfiAccount","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"group","type":"pubkey"},{"name":"authority","type":"pubkey"},{"name":"lending_account","type":{"defined":{"name":"LendingAccount"}}},{"name":"account_flags","docs":["The flags that indicate the state of the account. This is u64 bitfield, where each bit","represents a flag.","","Flags:MarginfiAccount","- 1: `ACCOUNT_DISABLED` - Indicates that the account is disabled and no further actions can","be taken on it.","- 2: `ACCOUNT_IN_FLASHLOAN` - Only set when an account is within a flash loan, e.g. when","start_flashloan is called, then unset when the flashloan ends.","- 4: `ACCOUNT_FLAG_DEPRECATED` - Deprecated, available for future use","- 8: `ACCOUNT_TRANSFER_AUTHORITY_DEPRECATED` - the admin has flagged with account to be","moved, original owner can now call `set_account_transfer_authority`","- 16: `ACCOUNT_IN_RECEIVERSHIP` - the account is eligible to be liquidated and has entered","receivership, a liquidator is able to control borrows and withdraws until the end of the","tx. This flag will only appear within a tx.","- 32: `ACCOUNT_IN_DELEVERAGE - the account is being deleveraged by the risk admin","- 64: `ACCOUNT_FROZEN` - the admin has frozen the account; only the group admin may perform","actions until unfrozen."],"type":"u64"},{"name":"emissions_destination_account","docs":["Wallet whose canonical ATA receives off-chain emissions distributions."],"type":"pubkey"},{"name":"health_cache","type":{"defined":{"name":"HealthCache"}}},{"name":"migrated_from","docs":["If this account was migrated from another one, store the original account key"],"type":"pubkey"},{"name":"migrated_to","docs":["If this account has been migrated to another one, store the destination account key"],"type":"pubkey"},{"name":"last_update","docs":["Unix timestamp (u64) of the last account interaction. Note: Bank.last_update uses i64."],"type":"u64"},{"name":"account_index","docs":["If a PDA-based account, the account index, a seed used to derive the PDA that can be chosen","arbitrarily (0.1.5 or later). Otherwise, does nothing."],"type":"u16"},{"name":"third_party_index","docs":["If a PDA-based account (0.1.5 or later), a \"vendor specific\" id. Values < PDA_FREE_THRESHOLD","can be used by anyone with no restrictions. Values >= PDA_FREE_THRESHOLD can only be used by","a particular program via CPI. These values require being added to a list, contact us for","more details. For legacy non-pda accounts, does nothing.","","Note: use a unique seed to tag accounts related to some particular program or campaign so","you can easily fetch them all later."],"type":"u16"},{"name":"bump","docs":["This account's bump, if a PDA-based account (0.1.5 or later). Otherwise, does nothing."],"type":"u8"},{"name":"_pad0","type":{"array":["u8",3]}},{"name":"liquidation_record","docs":["Stores information related to liquidations made against this account. A pda of this","account's key, and \"liq_record\"","* Typically pubkey default if this account has never been liquidated or close to liquidation","* Opening this account is permissionless. Typically the liquidator pays, but e.g. we may","also charge the user if they are opening a risky position on the front end."],"type":"pubkey"},{"name":"_padding0","type":{"array":["u64",7]}}]}},{"name":"MarginfiAccountCloseOrderEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"order","type":"pubkey"}]}},{"name":"MarginfiAccountCreateEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}}]}},{"name":"MarginfiAccountFreezeEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"frozen","type":"bool"}]}},{"name":"MarginfiAccountPlaceOrderEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"order","type":"pubkey"},{"name":"trigger","type":{"defined":{"name":"OrderTriggerType"}}},{"name":"stop_loss","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"take_profit","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"tags","type":{"array":["u16",2]}}]}},{"name":"MarginfiAccountTransferToNewAccount","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"old_account","type":"pubkey"},{"name":"old_account_authority","type":"pubkey"},{"name":"new_account_authority","type":"pubkey"}]}},{"name":"MarginfiGroup","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"admin","docs":["Broadly able to modify anything, and can set/remove other admins at will."],"type":"pubkey"},{"name":"group_flags","docs":["Bitmask for group settings flags.","* Bit 0 (1): `PROGRAM_FEES_ENABLED` — If set, program-level fees are enabled.","* Bits 1-63: Reserved for future use."],"type":"u64"},{"name":"fee_state_cache","docs":["Caches information from the global `FeeState` so the FeeState can be omitted on certain ixes"],"type":{"defined":{"name":"FeeStateCache"}}},{"name":"banks","docs":["For groups initialized in versions 0.1.2 or greater, this is an authoritative count","of the number of banks under this group. For groups initialized prior to 0.1.2,","a non-authoritative count of the number of banks initiated after 0.1.2 went live."],"type":"u16"},{"name":"pad0","type":{"array":["u8",6]}},{"name":"emode_admin","docs":["This admin can configure collateral ratios above (but not below) the collateral ratio of","certain banks, e.g. allow SOL to count as 90% collateral when borrowing an LST instead of","the default rate."],"type":"pubkey"},{"name":"delegate_curve_admin","docs":["Can modify the fields in `config.interest_rate_config` but nothing else, for every bank","under this group"],"type":"pubkey"},{"name":"delegate_limit_admin","docs":["Can modify the `deposit_limit`, `borrow_limit`, `total_asset_value_init_limit` but nothing","else, for every bank under this group"],"type":"pubkey"},{"name":"delegate_emissions_admin","docs":["Can modify the emissions `flags`, `emissions_rate` and `emissions_mint`, but nothing else,","for every bank under this group"],"type":"pubkey"},{"name":"panic_state_cache","docs":["When program keeper temporarily puts the program into panic mode, information about the","duration of the lockup will be available here."],"type":{"defined":{"name":"PanicStateCache"}}},{"name":"deleverage_withdraw_window_cache","docs":["Keeps track of the liquidity withdrawn from the group over the day as a result of","deleverages. Used as a protection mechanism against too big (and unwanted) withdrawals (e.g.","when the risk admin is compromised)."],"type":{"defined":{"name":"WithdrawWindowCache"}}},{"name":"risk_admin","docs":["Can run bankruptcy and forced deleverage ixes to e.g. sunset risky/illiquid assets"],"type":"pubkey"},{"name":"metadata_admin","docs":["Can modify a Bank's metadata, and nothing else."],"type":"pubkey"},{"name":"emode_max_init_leverage","docs":["Maximum leverage allowed for emode positions (initial margin), stored as u32 basis.","Use `u32_to_basis` to convert to I80F48. Range: 1-100."],"type":"u32"},{"name":"emode_max_maint_leverage","docs":["Maximum leverage allowed for emode positions (maintenance margin), stored as u32 basis.","Must be > emode_max_init_leverage. Range: 1-100."],"type":"u32"},{"name":"_padding","docs":["Reserved for future use"],"type":{"array":["u8",8]}},{"name":"rate_limiter","docs":["Rate limiter for controlling aggregate withdraw/borrow outflow across all banks.","Tracks net outflow in USD."],"type":{"defined":{"name":"GroupRateLimiter"}}},{"name":"rate_limiter_last_admin_update_slot","docs":["Last slot covered by an admin group rate limiter aggregation update."],"type":"u64"},{"name":"rate_limiter_last_admin_update_seq","docs":["Monotonic sequence number for admin group rate limiter updates.","This is used to enforce strict ordering and prevent duplicate/replayed batches","when slot ranges overlap or multiple updates happen in the same slot."],"type":"u64"},{"name":"deleverage_withdraw_last_admin_update_slot","docs":["Last slot covered by an admin deleverage withdraw-limit aggregation update."],"type":"u64"},{"name":"deleverage_withdraw_last_admin_update_seq","docs":["Monotonic sequence number for admin deleverage withdraw-limit updates."],"type":"u64"},{"name":"delegate_flow_admin","docs":["Can modify flow-control status for the group, i.e. update the withdraw caches with flow","information from banks. Typically this is a hot wallet that lives in e.g. some cron job. If","compromised, flow control can be effectively disabled until the admin is restored, which","does not itself compromise any funds, and is merely annoying."],"type":"pubkey"},{"name":"_padding_0","type":{"array":[{"array":["u64",2]},2]}},{"name":"_padding_1","type":{"array":[{"array":["u64",2]},32]}}]}},{"name":"MarginfiGroupConfigureEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}},{"name":"admin","type":{"option":"pubkey"}},{"name":"flags","type":"u64"}]}},{"name":"MarginfiGroupCreateEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"GroupEventHeader"}}}]}},{"name":"MinimalObligation","docs":["A minimal copy of Kamino's Obligation for zero-copy deserialization"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"tag","type":"u64"},{"name":"last_update_slot","docs":["Kamino obligations are only good for one slot, e.g. `refresh_obligation` must have run within the","same slot as any ix that needs a non-stale obligation e.g. withdraw."],"type":"u64"},{"name":"last_update_stale","docs":["True if the obligation is stale, which will cause various ixes like withdraw to fail. Typically","set to true in any tx that modifies obligation balance, and set to false at the end of a","successful `refresh_obligation`","* 0 = false, 1 = true"],"type":"u8"},{"name":"last_update_price_status","docs":["Each bit represents a passed check in price status.","* 63 = all checks passed","","Otherwise:","* PRICE_LOADED = 0b_0000_0001; // 1","* PRICE_AGE_CHECKED = 0b_0000_0010; // 2","* TWAP_CHECKED = 0b_0000_0100; // 4","* TWAP_AGE_CHECKED = 0b_0000_1000; // 8","* HEURISTIC_CHECKED = 0b_0001_0000; // 16","* PRICE_USAGE_ALLOWED = 0b_0010_0000; // 32"],"type":"u8"},{"name":"last_update_placeholder","type":{"array":["u8",6]}},{"name":"lending_market","type":"pubkey"},{"name":"owner","docs":["For mrgn banks, the bank's Liquidity Vault Authority (a pda which can be derived if the bank","key is known)"],"type":"pubkey"},{"name":"deposits","type":{"array":[{"defined":{"name":"MinimalObligationCollateral"}},8]}},{"name":"lowest_reserve_deposit_liquidation_ltv","type":"u64"},{"name":"deposited_value_sf","type":{"array":["u8",16]}},{"name":"padding_part1","type":{"array":["u8",512]}},{"name":"padding_part2","type":{"array":["u8",512]}},{"name":"padding_part3","type":{"array":["u8",512]}},{"name":"padding_part4","type":{"array":["u8",512]}},{"name":"padding_part5a","type":{"array":["u8",64]}},{"name":"padding_part5c","type":{"array":["u8",24]}}]}},{"name":"MinimalObligationCollateral","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"deposit_reserve","type":"pubkey"},{"name":"deposited_amount","docs":["In collateral token (NOT liquidity token), use `collateral_to_liquidity` to convert back to","liquidity token!","* Always 6 decimals"],"type":"u64"},{"name":"market_value_sf","docs":["* In dollars, based on last oracle price update","* Actually an I68F60, stored as a u128 (i.e. BN) in Kamino.","* A float (arbitrary decimals)"],"type":{"array":["u8",16]}},{"name":"borrowed_amount_against_this_collateral_in_elevation_group","type":"u64"},{"name":"padding","type":{"array":["u64",9]}}]}},{"name":"MinimalReserve","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"version","type":"u64"},{"name":"slot","docs":["Kamino reserves are only good for one slot, e.g. `refresh_reserve` must have run within the","same slot as any ix that needs a non-stale reserve e.g. withdraw."],"type":"u64"},{"name":"stale","docs":["True if the reserve is stale, which will cause various ixes like withdraw to fail. Typically","set to true in any tx that modifies reserve balance, and set to false at the end of a","successful `refresh_reserve`","* 0 = false, 1 = true"],"type":"u8"},{"name":"price_status","docs":["Each bit represents a passed check in price status.","* 63 = all checks passed","","Otherwise:","* PRICE_LOADED = 0b_0000_0001; // 1","* PRICE_AGE_CHECKED = 0b_0000_0010; // 2","* TWAP_CHECKED = 0b_0000_0100; // 4","* TWAP_AGE_CHECKED = 0b_0000_1000; // 8","* HEURISTIC_CHECKED = 0b_0001_0000; // 16","* PRICE_USAGE_ALLOWED = 0b_0010_0000; // 32"],"type":"u8"},{"name":"placeholder","type":{"array":["u8",6]}},{"name":"lending_market","type":"pubkey"},{"name":"farm_collateral","type":"pubkey"},{"name":"farm_debt","type":"pubkey"},{"name":"mint_pubkey","type":"pubkey"},{"name":"supply_vault","docs":["* A PDA"],"type":"pubkey"},{"name":"fee_vault","docs":["* A PDA"],"type":"pubkey"},{"name":"available_amount","docs":["In simple terms: (amount in supply vault - outstanding borrows)","* In token, with `mint_decimals`"],"type":"u64"},{"name":"borrowed_amount_sf","docs":["* In token, with `mint_decimals`","* Actually an I68F60, stored as a u128 (i.e. BN) in Kamino."],"type":{"array":["u8",16]}},{"name":"market_price_sf","docs":["* Actually an I68F60, stored as a u128 (i.e. BN) in Kamino."],"type":{"array":["u8",16]}},{"name":"market_price_last_updated_ts","type":"u64"},{"name":"mint_decimals","type":"u64"},{"name":"deposit_limit_crossed_timestamp","type":"u64"},{"name":"borrow_limit_crossed_timestamp","type":"u64"},{"name":"cumulative_borrow_rate_bsf","type":{"array":["u8",48]}},{"name":"accumulated_protocol_fees_sf","docs":["* In token, with `mint_decimals`","* Actually an I68F60, stored as a u128 (i.e. BN) in Kamino."],"type":{"array":["u8",16]}},{"name":"accumulated_referrer_fees_sf","docs":["* In token, with `mint_decimals`","* Actually an I68F60, stored as a u128 (i.e. BN) in Kamino."],"type":{"array":["u8",16]}},{"name":"pending_referrer_fees_sf","docs":["* In token, with `mint_decimals`","* Actually an I68F60, stored as a u128 (i.e. BN) in Kamino."],"type":{"array":["u8",16]}},{"name":"absolute_referral_rate_sf","docs":["* In token, with `mint_decimals`","* Actually an I68F60, stored as a u128 (i.e. BN) in Kamino."],"type":{"array":["u8",16]}},{"name":"token_program","docs":["Token or Token22. If token22, note that Kamino does not support all Token22 extensions."],"type":"pubkey"},{"name":"padding2_part1","type":{"array":["u8",256]}},{"name":"padding2_part2","type":{"array":["u8",128]}},{"name":"padding2_part3","type":{"array":["u8",24]}},{"name":"padding3","type":{"array":["u8",512]}},{"name":"padding_part1","type":{"array":["u8",512]}},{"name":"padding_part2","type":{"array":["u8",512]}},{"name":"padding_part3","type":{"array":["u8",128]}},{"name":"padding_part4","type":{"array":["u8",48]}},{"name":"collateral_mint_pubkey","docs":["Mints collateral tokens","* A PDA","* technically 6 decimals, but uses `mint_decimals` regardless for all purposes","* authority = lending_market_authority"],"type":"pubkey"},{"name":"mint_total_supply","docs":["Total number of collateral tokens","* uses `mint_decimals`, even though it's technically 6 decimals under the hood"],"type":"u64"},{"name":"collateral_supply_vault","docs":["* A PDA"],"type":"pubkey"},{"name":"padding1_reserve_collateral","type":{"array":["u8",512]}},{"name":"padding2_reserve_collateral","type":{"array":["u8",512]}},{"name":"padding4_part1","type":{"array":["u8",4096]}},{"name":"padding4_part2","type":{"array":["u8",512]}},{"name":"padding4_part3","type":{"array":["u8",256]}},{"name":"padding4_part4","type":{"array":["u8",64]}},{"name":"padding4_part5","type":{"array":["u8",32]}},{"name":"padding4_part6","type":{"array":["u8",8]}}]}},{"name":"MinimalSpotMarket","docs":["Minimal representation of Drift's SpotMarket account","Only includes the fields we actually need for marginfi integration","https://github.com/drift-labs/protocol-v2/tree/master/programs/drift/src/state/spot_market.rs#L35"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"pubkey","docs":["The address of the spot market. It is a pda of the market index"],"type":"pubkey"},{"name":"oracle","docs":["The oracle used to price the markets deposits/borrows"],"type":"pubkey"},{"name":"mint","docs":["The token mint of the market"],"type":"pubkey"},{"name":"vault","docs":["The vault used to store the market's deposits"],"type":"pubkey"},{"name":"_padding1","type":{"array":[{"array":["u64",4]},9]}},{"name":"_padding2","type":{"array":["u8",8]}},{"name":"deposit_balance","docs":["All the fields we need for testing (stored as raw bytes for simplicity)"],"type":{"array":["u8",16]}},{"name":"borrow_balance","type":{"array":["u8",16]}},{"name":"cumulative_deposit_interest","type":{"array":["u8",16]}},{"name":"cumulative_borrow_interest","type":{"array":["u8",16]}},{"name":"_padding3","type":{"array":["u64",9]}},{"name":"last_interest_ts","docs":["Last time the cumulative deposit and borrow interest was updated","Offset: 568 bytes from start of struct (including discriminator)"],"type":"u64"},{"name":"_padding4","type":{"array":["u64",13]}},{"name":"decimals","type":"u32"},{"name":"market_index","type":"u16"},{"name":"_padding5","type":{"array":["u16",24]}},{"name":"_padding6","type":{"array":["u8",1]}},{"name":"pool_id","type":"u8"},{"name":"_padding7","docs":["Padding to reach 776 bytes total (including discriminator)"],"type":{"array":["u64",5]}}]}},{"name":"MinimalUser","docs":["Minimal representation of Drift's User account","Only includes the fields we actually need"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"authority","docs":["The owner/authority of the account"],"type":"pubkey"},{"name":"delegate","docs":["An addresses that can control the account on the authority's behalf"],"type":"pubkey"},{"name":"name","docs":["Encoded display name for the account"],"type":{"array":["u8",32]}},{"name":"spot_positions","docs":["The user's spot positions (8 positions)"],"type":{"array":[{"defined":{"name":"SpotPosition"}},8]}},{"name":"_padding1","docs":["Skip to the fields we need at the end"],"type":{"array":["u64",256]}},{"name":"_padding2","type":{"array":["u64",128]}},{"name":"_padding3","type":{"array":["u64",64]}},{"name":"_padding4","type":{"array":["u64",32]}},{"name":"_padding5","type":{"array":["u64",8]}},{"name":"_padding6","type":{"array":["u64",2]}},{"name":"_padding7","type":{"array":["u16",1]}},{"name":"sub_account_id","docs":["Sub account id for this user account"],"type":"u16"},{"name":"status","type":{"defined":{"name":"UserStatus"}}},{"name":"_padding8","type":{"array":["u8",27]}}]}},{"name":"OracleSetup","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"None"},{"name":"PythLegacy"},{"name":"SwitchboardV2"},{"name":"PythPushOracle"},{"name":"SwitchboardPull"},{"name":"StakedWithPythPush"},{"name":"KaminoPythPush"},{"name":"KaminoSwitchboardPull"},{"name":"Fixed"},{"name":"DriftPythPull"},{"name":"DriftSwitchboardPull"},{"name":"SolendPythPull"},{"name":"SolendSwitchboardPull"},{"name":"FixedKamino"},{"name":"FixedDrift"},{"name":"JuplendPythPull"},{"name":"JuplendSwitchboardPull"},{"name":"FixedJuplend"}]}},{"name":"Order","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"marginfi_account","type":"pubkey"},{"name":"stop_loss","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"take_profit","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"placeholder","docs":["Reserved for future use"],"type":"u64"},{"name":"max_slippage","docs":["* a %, as u32, out of 100%, e.g. 50% = .5 * u32::MAX"],"type":"u32"},{"name":"pad0","type":{"array":["u8",4]}},{"name":"tags","docs":["Active tags (currently 2). Remaining capacity is stored in padding for layout compatibility.","Padding byte `ORDER_TAG_PADDING - 1` stores the tag count for forward compatibility. (u16 *","2 = 4 bytes)"],"type":{"array":["u16",2]}},{"name":"pad1","type":{"array":["u8",4]}},{"name":"_tags_padding","type":{"array":["u8",32]}},{"name":"trigger","docs":["Stop Loss (0), Take Profit (1), or Both (2)"],"type":{"defined":{"name":"OrderTriggerType"}}},{"name":"bump","docs":["Bump to derive this pda"],"type":"u8"},{"name":"pad2","type":{"array":["u8",6]}},{"name":"_reserved1","type":{"array":[{"array":["u8",32]},4]}}]}},{"name":"OrderTrigger","repr":{"kind":"c"},"type":{"kind":"enum","variants":[{"name":"StopLoss","fields":[{"name":"threshold","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"max_slippage","type":"u32"}]},{"name":"TakeProfit","fields":[{"name":"threshold","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"max_slippage","type":"u32"}]},{"name":"Both","fields":[{"name":"stop_loss","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"take_profit","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"max_slippage","type":"u32"}]}]}},{"name":"OrderTriggerType","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"StopLoss"},{"name":"TakeProfit"},{"name":"Both"}]}},{"name":"PanicState","docs":["Panic state for emergency protocol pausing"],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"pause_flags","docs":["Whether the protocol is currently paused (1 = paused, 0 = not paused)"],"type":"u8"},{"name":"daily_pause_count","docs":["Number of times paused today (resets every 24 hours)"],"type":"u8"},{"name":"consecutive_pause_count","docs":["Number of consecutive pauses (resets when unpause happens)"],"type":"u8"},{"name":"_reserved","type":{"array":["u8",5]}},{"name":"pause_start_timestamp","docs":["Timestamp when the current pause started (0 if not paused)","* When a pause is extended before expiring, this could be in the future."],"type":"i64"},{"name":"last_daily_reset_timestamp","docs":["Timestamp of the last daily reset (for tracking daily pause count)"],"type":"i64"},{"name":"_reserved_space","docs":["Reserved for future use (making total struct 32 bytes)"],"type":{"array":["u8",8]}}]}},{"name":"PanicStateCache","docs":["Cached panic state information for fast checking during user operations"],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"pause_flags","docs":["Whether the protocol is currently paused (1 = paused, 0 = not paused)"],"type":"u8"},{"name":"_reserved","type":{"array":["u8",7]}},{"name":"pause_start_timestamp","docs":["Timestamp when the current pause started (0 if not paused)"],"type":"i64"},{"name":"last_cache_update","docs":["Timestamp when this cache was last updated"],"type":"i64"}]}},{"name":"RateLimitFlowEvent","docs":["Emitted when a bank-level inflow or outflow is recorded.","The delegate flow admin aggregates these off-chain and","updates the group rate limiter via `update_group_rate_limiter`."],"type":{"kind":"struct","fields":[{"name":"group","type":"pubkey"},{"name":"bank","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"flow_direction","docs":["0 = outflow (withdraw/borrow), 1 = inflow (deposit/repay)"],"type":"u8"},{"name":"native_amount","docs":["Amount in native tokens"],"type":"u64"},{"name":"mint_decimals","type":"u8"},{"name":"current_timestamp","docs":["Unix timestamp when the flow was recorded"],"type":"i64"}]}},{"name":"RateLimitWindow","docs":["A sliding window rate limiter that tracks net outflow over a time window.","Uses weighted blend of previous and current windows for smooth transitions.","","Net outflow = (withdraws + borrows) - (deposits + repays).","A negative net outflow increases remaining capacity for subsequent outflows."],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"max_outflow","docs":["Maximum net outflow allowed per window (0 = disabled).","For bank-level: denominated in native tokens.","For group-level: denominated in USD."],"type":"u64"},{"name":"window_duration","docs":["Window duration in seconds (e.g., 3600 for hourly, 86400 for daily)."],"type":"u64"},{"name":"window_start","docs":["Unix timestamp when the current window started."],"type":"i64"},{"name":"prev_window_outflow","docs":["Net outflow accumulated in the previous window.","Signed to allow tracking when inflows exceed outflows."],"type":"i64"},{"name":"cur_window_outflow","docs":["Net outflow accumulated in the current window.","Signed to allow tracking when inflows exceed outflows."],"type":"i64"}]}},{"name":"RatePoint","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"util","docs":["The utilization rate where `rate` applies","* a %, as u32, out of 100%, e.g. 50% = .5 * u32::MAX"],"type":"u32"},{"name":"rate","docs":["The base rate that applies","* a %, as u32, out of 1000%, e.g. 100% = 0.1 * u32::MAX"],"type":"u32"}]}},{"name":"RiskTier","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Collateral"},{"name":"Isolated"}]}},{"name":"SetKeeperCloseFlagsEvent","type":{"kind":"struct","fields":[{"name":"header","type":{"defined":{"name":"AccountEventHeader"}}},{"name":"bank_keys","type":{"option":{"vec":"pubkey"}}}]}},{"name":"SolendConfigCompact","docs":["Used to configure Solend banks. A simplified version of `BankConfigCompact` which omits most","values related to interest since Solend banks cannot earn interest or be borrowed against."],"type":{"kind":"struct","fields":[{"name":"oracle","type":"pubkey"},{"name":"asset_weight_init","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_weight_maint","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"deposit_limit","type":"u64"},{"name":"oracle_setup","docs":["Either `SolendPythPull` or `SolendSwitchboardPull`"],"type":{"defined":{"name":"OracleSetup"}}},{"name":"operational_state","docs":["Bank operational state - allows starting banks in paused state"],"type":{"defined":{"name":"BankOperationalState"}}},{"name":"risk_tier","docs":["Risk tier - determines if assets can be borrowed in isolation"],"type":{"defined":{"name":"RiskTier"}}},{"name":"config_flags","docs":["Config flags for future-proofing"],"type":"u8"},{"name":"total_asset_value_init_limit","type":"u64"},{"name":"oracle_max_age","type":"u16"},{"name":"oracle_max_confidence","docs":["Oracle confidence threshold (0 = use default 10%)"],"type":"u32"}]}},{"name":"SolendMinimalReserve","serialization":"bytemuck","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"last_update_slot","docs":["Last slot when supply and rates updated"],"type":"u64"},{"name":"last_update_stale","docs":["True when marked stale"],"type":"u8"},{"name":"lending_market","docs":["Lending market address"],"type":"pubkey"},{"name":"liquidity_mint_pubkey","type":"pubkey"},{"name":"liquidity_mint_decimals","type":"u8"},{"name":"liquidity_supply_pubkey","type":"pubkey"},{"name":"liquidity_pyth_oracle_pubkey","type":"pubkey"},{"name":"liquidity_switchboard_oracle_pubkey","type":"pubkey"},{"name":"liquidity_available_amount","type":"u64"},{"name":"liquidity_borrowed_amount_wads","type":{"array":["u8",16]}},{"name":"liquidity_cumulative_borrow_rate_wads","type":{"array":["u8",16]}},{"name":"liquidity_market_price","type":{"array":["u8",16]}},{"name":"collateral_mint_pubkey","type":"pubkey"},{"name":"collateral_mint_total_supply","type":"u64"},{"name":"collateral_supply_pubkey","type":"pubkey"},{"name":"config_optimal_utilization_rate","type":"u8"},{"name":"config_loan_to_value_ratio","type":"u8"},{"name":"config_liquidation_bonus","type":"u8"},{"name":"config_liquidation_threshold","type":"u8"},{"name":"_padding_to_fees_64","type":{"array":["u8",64]}},{"name":"_padding_to_fees_6","type":{"array":["u8",6]}},{"name":"liquidity_accumulated_protocol_fees_wads","type":{"array":["u8",16]}},{"name":"_padding_final_128","type":{"array":["u8",128]}},{"name":"_padding_final_64","type":{"array":["u8",64]}},{"name":"_padding_final_32","type":{"array":["u8",32]}},{"name":"_padding_final_6","type":{"array":["u8",6]}}]}},{"name":"SpotBalanceType","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Deposit"},{"name":"Borrow"}]}},{"name":"SpotPosition","docs":["Minimal representation of a spot position within a User account"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"scaled_balance","docs":["The scaled balance of the position.","* Precision: SPOT_BALANCE_PRECISION"],"type":"u64"},{"name":"open_bids","docs":["How many spot bids the user has open","* Precision: token mint precision"],"type":"i64"},{"name":"open_asks","docs":["How many spot asks the user has open","* Precision: token mint precision"],"type":"i64"},{"name":"cumulative_deposits","docs":["The cumulative deposits/borrows a user has made","* Precision: token mint precision"],"type":"i64"},{"name":"market_index","docs":["The market index of the corresponding spot market"],"type":"u16"},{"name":"balance_type","docs":["Whether the position is deposit or borrow"],"type":{"defined":{"name":"SpotBalanceType"}}},{"name":"open_orders","docs":["Number of open orders"],"type":"u8"},{"name":"padding","docs":["Padding"],"type":{"array":["u8",4]}}]}},{"name":"StakedSettings","docs":["Unique per-group. Staked Collateral banks created under a group automatically use these","settings. Groups that have not created this struct cannot create staked collateral banks. When","this struct updates, changes must be permissionlessly propagated to staked collateral banks.","Administrators can also edit the bank manually, i.e. with configure_bank, to temporarily make","changes such as raising the deposit limit for a single bank."],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"key","docs":["This account's own key. A PDA derived from `marginfi_group` and `STAKED_SETTINGS_SEED`"],"type":"pubkey"},{"name":"marginfi_group","docs":["Group for which these settings apply"],"type":"pubkey"},{"name":"oracle","docs":["Generally, the Pyth push oracle for SOL"],"type":"pubkey"},{"name":"asset_weight_init","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_weight_maint","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"deposit_limit","type":"u64"},{"name":"total_asset_value_init_limit","type":"u64"},{"name":"oracle_max_age","type":"u16"},{"name":"risk_tier","type":{"defined":{"name":"RiskTier"}}},{"name":"_pad0","type":{"array":["u8",5]}},{"name":"_reserved0","docs":["The following values are irrelevant because staked collateral positions do not support","borrowing."],"type":{"array":["u8",8]}},{"name":"_reserved1","type":{"array":["u8",32]}},{"name":"_reserved2","type":{"array":["u8",64]}}]}},{"name":"StakedSettingsConfig","type":{"kind":"struct","fields":[{"name":"oracle","type":"pubkey"},{"name":"asset_weight_init","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"asset_weight_maint","type":{"defined":{"name":"WrappedI80F48"}}},{"name":"deposit_limit","type":"u64"},{"name":"total_asset_value_init_limit","type":"u64"},{"name":"oracle_max_age","type":"u16"},{"name":"risk_tier","docs":["WARN: You almost certainly want \"Collateral\", using Isolated risk tier makes the asset","worthless as collateral, and is generally useful only when creating a staked collateral pool","for rewards purposes only."],"type":{"defined":{"name":"RiskTier"}}}]}},{"name":"StakedSettingsEditConfig","type":{"kind":"struct","fields":[{"name":"oracle","type":{"option":"pubkey"}},{"name":"asset_weight_init","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"asset_weight_maint","type":{"option":{"defined":{"name":"WrappedI80F48"}}}},{"name":"deposit_limit","type":{"option":"u64"}},{"name":"total_asset_value_init_limit","type":{"option":"u64"}},{"name":"oracle_max_age","type":{"option":"u16"}},{"name":"risk_tier","docs":["WARN: You almost certainly want \"Collateral\", using Isolated risk tier makes the asset","worthless as collateral, making all outstanding accounts eligible to be liquidated, and is","generally useful only when creating a staked collateral pool for rewards purposes only."],"type":{"option":{"defined":{"name":"RiskTier"}}}}]}},{"name":"UserStatus","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Active"},{"name":"BeingLiquidated"},{"name":"Bankrupt"},{"name":"ReduceOnly"},{"name":"AdvancedLp"},{"name":"ProtectedMakerOrders"}]}},{"name":"WithdrawWindowCache","docs":["Tracks deleverage withdrawal limits to protect against compromised risk admin"],"repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"daily_limit","docs":["Maximum USD value that can be withdrawn per day via deleverage (0 = no limit)"],"type":"u32"},{"name":"withdrawn_today","docs":["USD value withdrawn today via deleverage (approximate, rounded)"],"type":"u32"},{"name":"last_daily_reset_timestamp","docs":["Unix timestamp of the last daily counter reset"],"type":"i64"}]}},{"name":"WrappedI80F48","serialization":"bytemuck","repr":{"kind":"c","align":8},"type":{"kind":"struct","fields":[{"name":"value","type":{"array":["u8",16]}}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/marinade_finance.MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD.json b/migration/khadhroony-bot2-reference/idls/marinade_finance.MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD.json new file mode 100644 index 0000000..8b5674a --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/marinade_finance.MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"marinade_finance","instructions":[{"name":"initialize","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"reservePda","isMut":false,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"msolMint","isMut":false,"isSigner":false},{"name":"operationalSolAccount","isMut":false,"isSigner":false},{"name":"liqPool","accounts":[{"name":"lpMint","isMut":false,"isSigner":false},{"name":"solLegPda","isMut":false,"isSigner":false},{"name":"msolLeg","isMut":false,"isSigner":false}]},{"name":"treasuryMsolAccount","isMut":false,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"data","type":{"defined":"InitializeData"}}]},{"name":"changeAuthority","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"adminAuthority","isMut":false,"isSigner":true}],"args":[{"name":"data","type":{"defined":"ChangeAuthorityData"}}]},{"name":"addValidator","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"managerAuthority","isMut":false,"isSigner":true},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"validatorVote","isMut":false,"isSigner":false},{"name":"duplicationFlag","isMut":true,"isSigner":false,"docs":["by initializing this account we mark the validator as added"]},{"name":"rentPayer","isMut":true,"isSigner":true},{"name":"clock","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"score","type":"u32"}]},{"name":"removeValidator","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"managerAuthority","isMut":false,"isSigner":true},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"duplicationFlag","isMut":true,"isSigner":false},{"name":"operationalSolAccount","isMut":true,"isSigner":false}],"args":[{"name":"index","type":"u32"},{"name":"validatorVote","type":"publicKey"}]},{"name":"setValidatorScore","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"managerAuthority","isMut":false,"isSigner":true},{"name":"validatorList","isMut":true,"isSigner":false}],"args":[{"name":"index","type":"u32"},{"name":"validatorVote","type":"publicKey"},{"name":"score","type":"u32"}]},{"name":"configValidatorSystem","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"managerAuthority","isMut":false,"isSigner":true}],"args":[{"name":"extraRuns","type":"u32"}]},{"name":"deposit","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"msolMint","isMut":true,"isSigner":false},{"name":"liqPoolSolLegPda","isMut":true,"isSigner":false},{"name":"liqPoolMsolLeg","isMut":true,"isSigner":false},{"name":"liqPoolMsolLegAuthority","isMut":false,"isSigner":false},{"name":"reservePda","isMut":true,"isSigner":false},{"name":"transferFrom","isMut":true,"isSigner":true},{"name":"mintTo","isMut":true,"isSigner":false,"docs":["user mSOL Token account to send the mSOL"]},{"name":"msolMintAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"lamports","type":"u64"}]},{"name":"depositStakeAccount","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeAuthority","isMut":false,"isSigner":true},{"name":"duplicationFlag","isMut":true,"isSigner":false},{"name":"rentPayer","isMut":true,"isSigner":true},{"name":"msolMint","isMut":true,"isSigner":false},{"name":"mintTo","isMut":true,"isSigner":false,"docs":["user mSOL Token account to send the mSOL"]},{"name":"msolMintAuthority","isMut":false,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false}],"args":[{"name":"validatorIndex","type":"u32"}]},{"name":"liquidUnstake","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"msolMint","isMut":true,"isSigner":false},{"name":"liqPoolSolLegPda","isMut":true,"isSigner":false},{"name":"liqPoolMsolLeg","isMut":true,"isSigner":false},{"name":"treasuryMsolAccount","isMut":true,"isSigner":false},{"name":"getMsolFrom","isMut":true,"isSigner":false},{"name":"getMsolFromAuthority","isMut":false,"isSigner":true},{"name":"transferSolTo","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"msolAmount","type":"u64"}]},{"name":"addLiquidity","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"lpMint","isMut":true,"isSigner":false},{"name":"lpMintAuthority","isMut":false,"isSigner":false},{"name":"liqPoolMsolLeg","isMut":false,"isSigner":false},{"name":"liqPoolSolLegPda","isMut":true,"isSigner":false},{"name":"transferFrom","isMut":true,"isSigner":true},{"name":"mintTo","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"lamports","type":"u64"}]},{"name":"removeLiquidity","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"lpMint","isMut":true,"isSigner":false},{"name":"burnFrom","isMut":true,"isSigner":false},{"name":"burnFromAuthority","isMut":false,"isSigner":true},{"name":"transferSolTo","isMut":true,"isSigner":false},{"name":"transferMsolTo","isMut":true,"isSigner":false},{"name":"liqPoolSolLegPda","isMut":true,"isSigner":false},{"name":"liqPoolMsolLeg","isMut":true,"isSigner":false},{"name":"liqPoolMsolLegAuthority","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"tokens","type":"u64"}]},{"name":"configLp","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"adminAuthority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"ConfigLpParams"}}]},{"name":"configMarinade","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"adminAuthority","isMut":false,"isSigner":true}],"args":[{"name":"params","type":{"defined":"ConfigMarinadeParams"}}]},{"name":"orderUnstake","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"msolMint","isMut":true,"isSigner":false},{"name":"burnMsolFrom","isMut":true,"isSigner":false},{"name":"burnMsolAuthority","isMut":false,"isSigner":true},{"name":"newTicketAccount","isMut":true,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"msolAmount","type":"u64"}]},{"name":"claim","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"reservePda","isMut":true,"isSigner":false},{"name":"ticketAccount","isMut":true,"isSigner":false},{"name":"transferSolTo","isMut":true,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"stakeReserve","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"validatorVote","isMut":true,"isSigner":false},{"name":"reservePda","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":true},{"name":"stakeDepositAuthority","isMut":false,"isSigner":false},{"name":"rentPayer","isMut":true,"isSigner":true},{"name":"clock","isMut":false,"isSigner":false},{"name":"epochSchedule","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"stakeHistory","isMut":false,"isSigner":false},{"name":"stakeConfig","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false}],"args":[{"name":"validatorIndex","type":"u32"}]},{"name":"updateActive","accounts":[{"name":"common","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeWithdrawAuthority","isMut":false,"isSigner":false},{"name":"reservePda","isMut":true,"isSigner":false},{"name":"msolMint","isMut":true,"isSigner":false},{"name":"msolMintAuthority","isMut":false,"isSigner":false},{"name":"treasuryMsolAccount","isMut":true,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"stakeHistory","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}]},{"name":"validatorList","isMut":true,"isSigner":false}],"args":[{"name":"stakeIndex","type":"u32"},{"name":"validatorIndex","type":"u32"}]},{"name":"updateDeactivated","accounts":[{"name":"common","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeWithdrawAuthority","isMut":false,"isSigner":false},{"name":"reservePda","isMut":true,"isSigner":false},{"name":"msolMint","isMut":true,"isSigner":false},{"name":"msolMintAuthority","isMut":false,"isSigner":false},{"name":"treasuryMsolAccount","isMut":true,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"stakeHistory","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}]},{"name":"operationalSolAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"stakeIndex","type":"u32"}]},{"name":"deactivateStake","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"reservePda","isMut":false,"isSigner":false},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeDepositAuthority","isMut":false,"isSigner":false},{"name":"splitStakeAccount","isMut":true,"isSigner":true},{"name":"splitStakeRentPayer","isMut":true,"isSigner":true},{"name":"clock","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"epochSchedule","isMut":false,"isSigner":false},{"name":"stakeHistory","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false}],"args":[{"name":"stakeIndex","type":"u32"},{"name":"validatorIndex","type":"u32"}]},{"name":"emergencyUnstake","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"validatorManagerAuthority","isMut":false,"isSigner":true},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeDepositAuthority","isMut":false,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false}],"args":[{"name":"stakeIndex","type":"u32"},{"name":"validatorIndex","type":"u32"}]},{"name":"partialUnstake","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"validatorManagerAuthority","isMut":false,"isSigner":true},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeDepositAuthority","isMut":false,"isSigner":false},{"name":"reservePda","isMut":false,"isSigner":false},{"name":"splitStakeAccount","isMut":true,"isSigner":true},{"name":"splitStakeRentPayer","isMut":true,"isSigner":true},{"name":"clock","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"stakeHistory","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false}],"args":[{"name":"stakeIndex","type":"u32"},{"name":"validatorIndex","type":"u32"},{"name":"desiredUnstakeAmount","type":"u64"}]},{"name":"mergeStakes","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"destinationStake","isMut":true,"isSigner":false},{"name":"sourceStake","isMut":true,"isSigner":false},{"name":"stakeDepositAuthority","isMut":false,"isSigner":false},{"name":"stakeWithdrawAuthority","isMut":false,"isSigner":false},{"name":"operationalSolAccount","isMut":true,"isSigner":false},{"name":"clock","isMut":false,"isSigner":false},{"name":"stakeHistory","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false}],"args":[{"name":"destinationStakeIndex","type":"u32"},{"name":"sourceStakeIndex","type":"u32"},{"name":"validatorIndex","type":"u32"}]},{"name":"redelegate","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"stakeDepositAuthority","isMut":false,"isSigner":false},{"name":"reservePda","isMut":false,"isSigner":false},{"name":"splitStakeAccount","isMut":true,"isSigner":true},{"name":"splitStakeRentPayer","isMut":true,"isSigner":true},{"name":"destValidatorAccount","isMut":false,"isSigner":false},{"name":"redelegateStakeAccount","isMut":true,"isSigner":true},{"name":"clock","isMut":false,"isSigner":false},{"name":"stakeHistory","isMut":false,"isSigner":false},{"name":"stakeConfig","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false}],"args":[{"name":"stakeIndex","type":"u32"},{"name":"sourceValidatorIndex","type":"u32"},{"name":"destValidatorIndex","type":"u32"}]},{"name":"pause","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"pauseAuthority","isMut":false,"isSigner":true}],"args":[]},{"name":"resume","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"pauseAuthority","isMut":false,"isSigner":true}],"args":[]},{"name":"withdrawStakeAccount","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"msolMint","isMut":true,"isSigner":false},{"name":"burnMsolFrom","isMut":true,"isSigner":false},{"name":"burnMsolAuthority","isMut":true,"isSigner":true},{"name":"treasuryMsolAccount","isMut":true,"isSigner":false},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"stakeWithdrawAuthority","isMut":false,"isSigner":false},{"name":"stakeDepositAuthority","isMut":false,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"splitStakeAccount","isMut":true,"isSigner":true},{"name":"splitStakeRentPayer","isMut":true,"isSigner":true},{"name":"clock","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"stakeProgram","isMut":false,"isSigner":false}],"args":[{"name":"stakeIndex","type":"u32"},{"name":"validatorIndex","type":"u32"},{"name":"msolAmount","type":"u64"},{"name":"beneficiary","type":"publicKey"}]},{"name":"reallocValidatorList","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"adminAuthority","isMut":false,"isSigner":true},{"name":"validatorList","isMut":true,"isSigner":false},{"name":"rentFunds","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"capacity","type":"u32"}]},{"name":"reallocStakeList","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"adminAuthority","isMut":false,"isSigner":true},{"name":"stakeList","isMut":true,"isSigner":false},{"name":"rentFunds","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"capacity","type":"u32"}]}],"accounts":[{"name":"TicketAccountData","type":{"kind":"struct","fields":[{"name":"stateAddress","type":"publicKey"},{"name":"beneficiary","type":"publicKey"},{"name":"lamportsAmount","type":"u64"},{"name":"createdEpoch","type":"u64"}]}},{"name":"State","type":{"kind":"struct","fields":[{"name":"msolMint","type":"publicKey"},{"name":"adminAuthority","type":"publicKey"},{"name":"operationalSolAccount","type":"publicKey"},{"name":"treasuryMsolAccount","type":"publicKey"},{"name":"reserveBumpSeed","type":"u8"},{"name":"msolMintAuthorityBumpSeed","type":"u8"},{"name":"rentExemptForTokenAcc","type":"u64"},{"name":"rewardFee","type":{"defined":"Fee"}},{"name":"stakeSystem","type":{"defined":"StakeSystem"}},{"name":"validatorSystem","type":{"defined":"ValidatorSystem"}},{"name":"liqPool","type":{"defined":"LiqPool"}},{"name":"availableReserveBalance","type":"u64"},{"name":"msolSupply","type":"u64"},{"name":"msolPrice","type":"u64"},{"name":"circulatingTicketCount","docs":["count tickets for delayed-unstake"],"type":"u64"},{"name":"circulatingTicketBalance","docs":["total lamports amount of generated and not claimed yet tickets"],"type":"u64"},{"name":"lentFromReserve","type":"u64"},{"name":"minDeposit","type":"u64"},{"name":"minWithdraw","type":"u64"},{"name":"stakingSolCap","type":"u64"},{"name":"emergencyCoolingDown","type":"u64"},{"name":"pauseAuthority","docs":["emergency pause"],"type":"publicKey"},{"name":"paused","type":"bool"},{"name":"delayedUnstakeFee","type":{"defined":"FeeCents"}},{"name":"withdrawStakeAccountFee","type":{"defined":"FeeCents"}},{"name":"withdrawStakeAccountEnabled","type":"bool"},{"name":"lastStakeMoveEpoch","type":"u64"},{"name":"stakeMoved","type":"u64"},{"name":"maxStakeMovedPerEpoch","type":{"defined":"Fee"}}]}}],"types":[{"name":"SplitStakeAccountInfo","type":{"kind":"struct","fields":[{"name":"account","type":"publicKey"},{"name":"index","type":"u32"}]}},{"name":"U64ValueChange","type":{"kind":"struct","fields":[{"name":"old","type":"u64"},{"name":"new","type":"u64"}]}},{"name":"U32ValueChange","type":{"kind":"struct","fields":[{"name":"old","type":"u32"},{"name":"new","type":"u32"}]}},{"name":"FeeValueChange","type":{"kind":"struct","fields":[{"name":"old","type":{"defined":"Fee"}},{"name":"new","type":{"defined":"Fee"}}]}},{"name":"FeeCentsValueChange","type":{"kind":"struct","fields":[{"name":"old","type":{"defined":"FeeCents"}},{"name":"new","type":{"defined":"FeeCents"}}]}},{"name":"PubkeyValueChange","type":{"kind":"struct","fields":[{"name":"old","type":"publicKey"},{"name":"new","type":"publicKey"}]}},{"name":"BoolValueChange","type":{"kind":"struct","fields":[{"name":"old","type":"bool"},{"name":"new","type":"bool"}]}},{"name":"ChangeAuthorityData","type":{"kind":"struct","fields":[{"name":"admin","type":{"option":"publicKey"}},{"name":"validatorManager","type":{"option":"publicKey"}},{"name":"operationalSolAccount","type":{"option":"publicKey"}},{"name":"treasuryMsolAccount","type":{"option":"publicKey"}},{"name":"pauseAuthority","type":{"option":"publicKey"}}]}},{"name":"ConfigLpParams","type":{"kind":"struct","fields":[{"name":"minFee","type":{"option":{"defined":"Fee"}}},{"name":"maxFee","type":{"option":{"defined":"Fee"}}},{"name":"liquidityTarget","type":{"option":"u64"}},{"name":"treasuryCut","type":{"option":{"defined":"Fee"}}}]}},{"name":"ConfigMarinadeParams","type":{"kind":"struct","fields":[{"name":"rewardsFee","type":{"option":{"defined":"Fee"}}},{"name":"slotsForStakeDelta","type":{"option":"u64"}},{"name":"minStake","type":{"option":"u64"}},{"name":"minDeposit","type":{"option":"u64"}},{"name":"minWithdraw","type":{"option":"u64"}},{"name":"stakingSolCap","type":{"option":"u64"}},{"name":"liquiditySolCap","type":{"option":"u64"}},{"name":"withdrawStakeAccountEnabled","type":{"option":"bool"}},{"name":"delayedUnstakeFee","type":{"option":{"defined":"FeeCents"}}},{"name":"withdrawStakeAccountFee","type":{"option":{"defined":"FeeCents"}}},{"name":"maxStakeMovedPerEpoch","type":{"option":{"defined":"Fee"}}}]}},{"name":"InitializeData","type":{"kind":"struct","fields":[{"name":"adminAuthority","type":"publicKey"},{"name":"validatorManagerAuthority","type":"publicKey"},{"name":"minStake","type":"u64"},{"name":"rewardsFee","type":{"defined":"Fee"}},{"name":"liqPool","type":{"defined":"LiqPoolInitializeData"}},{"name":"additionalStakeRecordSpace","type":"u32"},{"name":"additionalValidatorRecordSpace","type":"u32"},{"name":"slotsForStakeDelta","type":"u64"},{"name":"pauseAuthority","type":"publicKey"}]}},{"name":"LiqPoolInitializeData","type":{"kind":"struct","fields":[{"name":"lpLiquidityTarget","type":"u64"},{"name":"lpMaxFee","type":{"defined":"Fee"}},{"name":"lpMinFee","type":{"defined":"Fee"}},{"name":"lpTreasuryCut","type":{"defined":"Fee"}}]}},{"name":"Fee","type":{"kind":"struct","fields":[{"name":"basisPoints","type":"u32"}]}},{"name":"FeeCents","docs":["FeeCents, same as Fee but / 1_000_000 instead of 10_000","1 FeeCent = 0.0001%, 10_000 FeeCent = 1%, 1_000_000 FeeCent = 100%"],"type":{"kind":"struct","fields":[{"name":"bpCents","type":"u32"}]}},{"name":"LiqPool","type":{"kind":"struct","fields":[{"name":"lpMint","type":"publicKey"},{"name":"lpMintAuthorityBumpSeed","type":"u8"},{"name":"solLegBumpSeed","type":"u8"},{"name":"msolLegAuthorityBumpSeed","type":"u8"},{"name":"msolLeg","type":"publicKey"},{"name":"lpLiquidityTarget","docs":["Liquidity target. If the Liquidity reach this amount, the fee reaches lp_min_discount_fee"],"type":"u64"},{"name":"lpMaxFee","docs":["Liquidity pool max fee"],"type":{"defined":"Fee"}},{"name":"lpMinFee","docs":["SOL/mSOL Liquidity pool min fee"],"type":{"defined":"Fee"}},{"name":"treasuryCut","docs":["Treasury cut"],"type":{"defined":"Fee"}},{"name":"lpSupply","type":"u64"},{"name":"lentFromSolLeg","type":"u64"},{"name":"liquiditySolCap","type":"u64"}]}},{"name":"List","type":{"kind":"struct","fields":[{"name":"account","type":"publicKey"},{"name":"itemSize","type":"u32"},{"name":"count","type":"u32"},{"name":"reserved1","type":"publicKey"},{"name":"reserved2","type":"u32"}]}},{"name":"StakeRecord","type":{"kind":"struct","fields":[{"name":"stakeAccount","type":"publicKey"},{"name":"lastUpdateDelegatedLamports","type":"u64"},{"name":"lastUpdateEpoch","type":"u64"},{"name":"isEmergencyUnstaking","type":"u8"}]}},{"name":"StakeList","type":{"kind":"struct","fields":[]}},{"name":"StakeSystem","type":{"kind":"struct","fields":[{"name":"stakeList","type":{"defined":"List"}},{"name":"delayedUnstakeCoolingDown","type":"u64"},{"name":"stakeDepositBumpSeed","type":"u8"},{"name":"stakeWithdrawBumpSeed","type":"u8"},{"name":"slotsForStakeDelta","docs":["set by admin, how much slots before the end of the epoch, stake-delta can start"],"type":"u64"},{"name":"lastStakeDeltaEpoch","docs":["Marks the start of stake-delta operations, meaning that if somebody starts a delayed-unstake ticket","after this var is set with epoch_num the ticket will have epoch_created = current_epoch+1","(the user must wait one more epoch, because their unstake-delta will be execute in this epoch)"],"type":"u64"},{"name":"minStake","type":"u64"},{"name":"extraStakeDeltaRuns","docs":["can be set by validator-manager-auth to allow a second run of stake-delta to stake late stakers in the last minute of the epoch","so we maximize user's rewards"],"type":"u32"}]}},{"name":"ValidatorRecord","type":{"kind":"struct","fields":[{"name":"validatorAccount","docs":["Validator vote pubkey"],"type":"publicKey"},{"name":"activeBalance","docs":["Validator total balance in lamports"],"type":"u64"},{"name":"score","type":"u32"},{"name":"lastStakeDeltaEpoch","type":"u64"},{"name":"duplicationFlagBumpSeed","type":"u8"}]}},{"name":"ValidatorList","type":{"kind":"struct","fields":[]}},{"name":"ValidatorSystem","type":{"kind":"struct","fields":[{"name":"validatorList","type":{"defined":"List"}},{"name":"managerAuthority","type":"publicKey"},{"name":"totalValidatorScore","type":"u32"},{"name":"totalActiveBalance","docs":["sum of all active lamports staked"],"type":"u64"},{"name":"autoAddValidatorEnabled","docs":["DEPRECATED, no longer used"],"type":"u8"}]}}],"events":[{"name":"ChangeAuthorityEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"adminChange","type":{"option":{"defined":"PubkeyValueChange"}},"index":false},{"name":"validatorManagerChange","type":{"option":{"defined":"PubkeyValueChange"}},"index":false},{"name":"operationalSolAccountChange","type":{"option":{"defined":"PubkeyValueChange"}},"index":false},{"name":"treasuryMsolAccountChange","type":{"option":{"defined":"PubkeyValueChange"}},"index":false},{"name":"pauseAuthorityChange","type":{"option":{"defined":"PubkeyValueChange"}},"index":false}]},{"name":"ConfigLpEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"minFeeChange","type":{"option":{"defined":"FeeValueChange"}},"index":false},{"name":"maxFeeChange","type":{"option":{"defined":"FeeValueChange"}},"index":false},{"name":"liquidityTargetChange","type":{"option":{"defined":"U64ValueChange"}},"index":false},{"name":"treasuryCutChange","type":{"option":{"defined":"FeeValueChange"}},"index":false}]},{"name":"ConfigMarinadeEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"rewardsFeeChange","type":{"option":{"defined":"FeeValueChange"}},"index":false},{"name":"slotsForStakeDeltaChange","type":{"option":{"defined":"U64ValueChange"}},"index":false},{"name":"minStakeChange","type":{"option":{"defined":"U64ValueChange"}},"index":false},{"name":"minDepositChange","type":{"option":{"defined":"U64ValueChange"}},"index":false},{"name":"minWithdrawChange","type":{"option":{"defined":"U64ValueChange"}},"index":false},{"name":"stakingSolCapChange","type":{"option":{"defined":"U64ValueChange"}},"index":false},{"name":"liquiditySolCapChange","type":{"option":{"defined":"U64ValueChange"}},"index":false},{"name":"withdrawStakeAccountEnabledChange","type":{"option":{"defined":"BoolValueChange"}},"index":false},{"name":"delayedUnstakeFeeChange","type":{"option":{"defined":"FeeCentsValueChange"}},"index":false},{"name":"withdrawStakeAccountFeeChange","type":{"option":{"defined":"FeeCentsValueChange"}},"index":false},{"name":"maxStakeMovedPerEpochChange","type":{"option":{"defined":"FeeValueChange"}},"index":false}]},{"name":"InitializeEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"params","type":{"defined":"InitializeData"},"index":false},{"name":"stakeList","type":"publicKey","index":false},{"name":"validatorList","type":"publicKey","index":false},{"name":"msolMint","type":"publicKey","index":false},{"name":"operationalSolAccount","type":"publicKey","index":false},{"name":"lpMint","type":"publicKey","index":false},{"name":"lpMsolLeg","type":"publicKey","index":false},{"name":"treasuryMsolAccount","type":"publicKey","index":false}]},{"name":"EmergencyPauseEvent","fields":[{"name":"state","type":"publicKey","index":false}]},{"name":"ResumeEvent","fields":[{"name":"state","type":"publicKey","index":false}]},{"name":"ReallocValidatorListEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"count","type":"u32","index":false},{"name":"newCapacity","type":"u32","index":false}]},{"name":"ReallocStakeListEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"count","type":"u32","index":false},{"name":"newCapacity","type":"u32","index":false}]},{"name":"DeactivateStakeEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"epoch","type":"u64","index":false},{"name":"stakeIndex","type":"u32","index":false},{"name":"stakeAccount","type":"publicKey","index":false},{"name":"lastUpdateStakeDelegation","type":"u64","index":false},{"name":"splitStakeAccount","type":{"option":{"defined":"SplitStakeAccountInfo"}},"index":false},{"name":"validatorIndex","type":"u32","index":false},{"name":"validatorVote","type":"publicKey","index":false},{"name":"totalStakeTarget","type":"u64","index":false},{"name":"validatorStakeTarget","type":"u64","index":false},{"name":"totalActiveBalance","type":"u64","index":false},{"name":"delayedUnstakeCoolingDown","type":"u64","index":false},{"name":"validatorActiveBalance","type":"u64","index":false},{"name":"totalUnstakeDelta","type":"u64","index":false},{"name":"unstakedAmount","type":"u64","index":false}]},{"name":"MergeStakesEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"epoch","type":"u64","index":false},{"name":"destinationStakeIndex","type":"u32","index":false},{"name":"destinationStakeAccount","type":"publicKey","index":false},{"name":"lastUpdateDestinationStakeDelegation","type":"u64","index":false},{"name":"sourceStakeIndex","type":"u32","index":false},{"name":"sourceStakeAccount","type":"publicKey","index":false},{"name":"lastUpdateSourceStakeDelegation","type":"u64","index":false},{"name":"validatorIndex","type":"u32","index":false},{"name":"validatorVote","type":"publicKey","index":false},{"name":"extraDelegated","type":"u64","index":false},{"name":"returnedStakeRent","type":"u64","index":false},{"name":"validatorActiveBalance","type":"u64","index":false},{"name":"totalActiveBalance","type":"u64","index":false},{"name":"operationalSolBalance","type":"u64","index":false}]},{"name":"RedelegateEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"epoch","type":"u64","index":false},{"name":"stakeIndex","type":"u32","index":false},{"name":"stakeAccount","type":"publicKey","index":false},{"name":"lastUpdateDelegation","type":"u64","index":false},{"name":"sourceValidatorIndex","type":"u32","index":false},{"name":"sourceValidatorVote","type":"publicKey","index":false},{"name":"sourceValidatorScore","type":"u32","index":false},{"name":"sourceValidatorBalance","type":"u64","index":false},{"name":"sourceValidatorStakeTarget","type":"u64","index":false},{"name":"destValidatorIndex","type":"u32","index":false},{"name":"destValidatorVote","type":"publicKey","index":false},{"name":"destValidatorScore","type":"u32","index":false},{"name":"destValidatorBalance","type":"u64","index":false},{"name":"destValidatorStakeTarget","type":"u64","index":false},{"name":"redelegateAmount","type":"u64","index":false},{"name":"splitStakeAccount","type":{"option":{"defined":"SplitStakeAccountInfo"}},"index":false},{"name":"redelegateStakeIndex","type":"u32","index":false},{"name":"redelegateStakeAccount","type":"publicKey","index":false}]},{"name":"StakeReserveEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"epoch","type":"u64","index":false},{"name":"stakeIndex","type":"u32","index":false},{"name":"stakeAccount","type":"publicKey","index":false},{"name":"validatorIndex","type":"u32","index":false},{"name":"validatorVote","type":"publicKey","index":false},{"name":"totalStakeTarget","type":"u64","index":false},{"name":"validatorStakeTarget","type":"u64","index":false},{"name":"reserveBalance","type":"u64","index":false},{"name":"totalActiveBalance","type":"u64","index":false},{"name":"validatorActiveBalance","type":"u64","index":false},{"name":"totalStakeDelta","type":"u64","index":false},{"name":"amount","type":"u64","index":false}]},{"name":"UpdateActiveEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"epoch","type":"u64","index":false},{"name":"stakeIndex","type":"u32","index":false},{"name":"stakeAccount","type":"publicKey","index":false},{"name":"validatorIndex","type":"u32","index":false},{"name":"validatorVote","type":"publicKey","index":false},{"name":"delegationChange","type":{"defined":"U64ValueChange"},"index":false},{"name":"delegationGrowthMsolFees","type":{"option":"u64"},"index":false},{"name":"extraLamports","type":"u64","index":false},{"name":"extraMsolFees","type":{"option":"u64"},"index":false},{"name":"validatorActiveBalance","type":"u64","index":false},{"name":"totalActiveBalance","type":"u64","index":false},{"name":"msolPriceChange","type":{"defined":"U64ValueChange"},"index":false},{"name":"rewardFeeUsed","type":{"defined":"Fee"},"index":false},{"name":"totalVirtualStakedLamports","type":"u64","index":false},{"name":"msolSupply","type":"u64","index":false}]},{"name":"UpdateDeactivatedEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"epoch","type":"u64","index":false},{"name":"stakeIndex","type":"u32","index":false},{"name":"stakeAccount","type":"publicKey","index":false},{"name":"balanceWithoutRentExempt","type":"u64","index":false},{"name":"lastUpdateDelegatedLamports","type":"u64","index":false},{"name":"msolFees","type":{"option":"u64"},"index":false},{"name":"msolPriceChange","type":{"defined":"U64ValueChange"},"index":false},{"name":"rewardFeeUsed","type":{"defined":"Fee"},"index":false},{"name":"operationalSolBalance","type":"u64","index":false},{"name":"totalVirtualStakedLamports","type":"u64","index":false},{"name":"msolSupply","type":"u64","index":false}]},{"name":"ClaimEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"epoch","type":"u64","index":false},{"name":"ticket","type":"publicKey","index":false},{"name":"beneficiary","type":"publicKey","index":false},{"name":"circulatingTicketBalance","type":"u64","index":false},{"name":"circulatingTicketCount","type":"u64","index":false},{"name":"reserveBalance","type":"u64","index":false},{"name":"userBalance","type":"u64","index":false},{"name":"amount","type":"u64","index":false}]},{"name":"OrderUnstakeEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"ticketEpoch","type":"u64","index":false},{"name":"ticket","type":"publicKey","index":false},{"name":"beneficiary","type":"publicKey","index":false},{"name":"circulatingTicketBalance","type":"u64","index":false},{"name":"circulatingTicketCount","type":"u64","index":false},{"name":"userMsolBalance","type":"u64","index":false},{"name":"burnedMsolAmount","type":"u64","index":false},{"name":"solAmount","type":"u64","index":false},{"name":"feeBpCents","type":"u32","index":false},{"name":"totalVirtualStakedLamports","type":"u64","index":false},{"name":"msolSupply","type":"u64","index":false}]},{"name":"AddLiquidityEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"solOwner","type":"publicKey","index":false},{"name":"userSolBalance","type":"u64","index":false},{"name":"userLpBalance","type":"u64","index":false},{"name":"solLegBalance","type":"u64","index":false},{"name":"lpSupply","type":"u64","index":false},{"name":"solAddedAmount","type":"u64","index":false},{"name":"lpMinted","type":"u64","index":false},{"name":"totalVirtualStakedLamports","type":"u64","index":false},{"name":"msolSupply","type":"u64","index":false}]},{"name":"LiquidUnstakeEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"msolOwner","type":"publicKey","index":false},{"name":"liqPoolSolBalance","type":"u64","index":false},{"name":"liqPoolMsolBalance","type":"u64","index":false},{"name":"treasuryMsolBalance","type":{"option":"u64"},"index":false},{"name":"userMsolBalance","type":"u64","index":false},{"name":"userSolBalance","type":"u64","index":false},{"name":"msolAmount","type":"u64","index":false},{"name":"msolFee","type":"u64","index":false},{"name":"treasuryMsolCut","type":"u64","index":false},{"name":"solAmount","type":"u64","index":false},{"name":"lpLiquidityTarget","type":"u64","index":false},{"name":"lpMaxFee","type":{"defined":"Fee"},"index":false},{"name":"lpMinFee","type":{"defined":"Fee"},"index":false},{"name":"treasuryCut","type":{"defined":"Fee"},"index":false}]},{"name":"RemoveLiquidityEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"solLegBalance","type":"u64","index":false},{"name":"msolLegBalance","type":"u64","index":false},{"name":"userLpBalance","type":"u64","index":false},{"name":"userSolBalance","type":"u64","index":false},{"name":"userMsolBalance","type":"u64","index":false},{"name":"lpMintSupply","type":"u64","index":false},{"name":"lpBurned","type":"u64","index":false},{"name":"solOutAmount","type":"u64","index":false},{"name":"msolOutAmount","type":"u64","index":false}]},{"name":"AddValidatorEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"validator","type":"publicKey","index":false},{"name":"index","type":"u32","index":false},{"name":"score","type":"u32","index":false}]},{"name":"RemoveValidatorEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"validator","type":"publicKey","index":false},{"name":"index","type":"u32","index":false},{"name":"operationalSolBalance","type":"u64","index":false}]},{"name":"SetValidatorScoreEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"validator","type":"publicKey","index":false},{"name":"index","type":"u32","index":false},{"name":"scoreChange","type":{"defined":"U32ValueChange"},"index":false}]},{"name":"DepositStakeAccountEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"stake","type":"publicKey","index":false},{"name":"delegated","type":"u64","index":false},{"name":"withdrawer","type":"publicKey","index":false},{"name":"stakeIndex","type":"u32","index":false},{"name":"validator","type":"publicKey","index":false},{"name":"validatorIndex","type":"u32","index":false},{"name":"validatorActiveBalance","type":"u64","index":false},{"name":"totalActiveBalance","type":"u64","index":false},{"name":"userMsolBalance","type":"u64","index":false},{"name":"msolMinted","type":"u64","index":false},{"name":"totalVirtualStakedLamports","type":"u64","index":false},{"name":"msolSupply","type":"u64","index":false}]},{"name":"DepositEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"solOwner","type":"publicKey","index":false},{"name":"userSolBalance","type":"u64","index":false},{"name":"userMsolBalance","type":"u64","index":false},{"name":"solLegBalance","type":"u64","index":false},{"name":"msolLegBalance","type":"u64","index":false},{"name":"reserveBalance","type":"u64","index":false},{"name":"solSwapped","type":"u64","index":false},{"name":"msolSwapped","type":"u64","index":false},{"name":"solDeposited","type":"u64","index":false},{"name":"msolMinted","type":"u64","index":false},{"name":"totalVirtualStakedLamports","type":"u64","index":false},{"name":"msolSupply","type":"u64","index":false}]},{"name":"WithdrawStakeAccountEvent","fields":[{"name":"state","type":"publicKey","index":false},{"name":"epoch","type":"u64","index":false},{"name":"stake","type":"publicKey","index":false},{"name":"lastUpdateStakeDelegation","type":"u64","index":false},{"name":"stakeIndex","type":"u32","index":false},{"name":"validator","type":"publicKey","index":false},{"name":"validatorIndex","type":"u32","index":false},{"name":"userMsolBalance","type":"u64","index":false},{"name":"userMsolAuth","type":"publicKey","index":false},{"name":"msolBurned","type":"u64","index":false},{"name":"msolFees","type":"u64","index":false},{"name":"splitStake","type":"publicKey","index":false},{"name":"beneficiary","type":"publicKey","index":false},{"name":"splitLamports","type":"u64","index":false},{"name":"feeBpCents","type":"u32","index":false},{"name":"totalVirtualStakedLamports","type":"u64","index":false},{"name":"msolSupply","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"WrongReserveOwner","msg":"Wrong reserve owner. Must be a system account"},{"code":6001,"name":"NonEmptyReserveData","msg":"Reserve must have no data, but has data"},{"code":6002,"name":"InvalidInitialReserveLamports","msg":"Invalid initial reserve lamports"},{"code":6003,"name":"ZeroValidatorChunkSize","msg":"Zero validator chunk size"},{"code":6004,"name":"TooBigValidatorChunkSize","msg":"Too big validator chunk size"},{"code":6005,"name":"ZeroCreditChunkSize","msg":"Zero credit chunk size"},{"code":6006,"name":"TooBigCreditChunkSize","msg":"Too big credit chunk size"},{"code":6007,"name":"TooLowCreditFee","msg":"Too low credit fee"},{"code":6008,"name":"InvalidMintAuthority","msg":"Invalid mint authority"},{"code":6009,"name":"MintHasInitialSupply","msg":"Non empty initial mint supply"},{"code":6010,"name":"InvalidOwnerFeeState","msg":"Invalid owner fee state"},{"code":6011,"name":"InvalidProgramId","msg":"Invalid program id. For using program from another account please update id in the code"},{"code":6012,"name":"UnexpectedAccount","msg":"Unexpected account"},{"code":6013,"name":"CalculationFailure","msg":"Calculation failure"},{"code":6014,"name":"StakeAccountWithLockup","msg":"You can't deposit a stake-account with lockup"},{"code":6015,"name":"MinStakeIsTooLow","msg":"Min stake is too low"},{"code":6016,"name":"LpMaxFeeIsTooHigh","msg":"Lp max fee is too high"},{"code":6017,"name":"BasisPointsOverflow","msg":"Basis points overflow"},{"code":6018,"name":"LpFeesAreWrongWayRound","msg":"LP min fee > LP max fee"},{"code":6019,"name":"LiquidityTargetTooLow","msg":"Liquidity target too low"},{"code":6020,"name":"TicketNotDue","msg":"Ticket not due. Wait more epochs"},{"code":6021,"name":"TicketNotReady","msg":"Ticket not ready. Wait a few hours and try again"},{"code":6022,"name":"WrongBeneficiary","msg":"Wrong Ticket Beneficiary"},{"code":6023,"name":"StakeAccountNotUpdatedYet","msg":"Stake Account not updated yet"},{"code":6024,"name":"StakeNotDelegated","msg":"Stake Account not delegated"},{"code":6025,"name":"StakeAccountIsEmergencyUnstaking","msg":"Stake Account is emergency unstaking"},{"code":6026,"name":"InsufficientLiquidity","msg":"Insufficient Liquidity in the Liquidity Pool"},{"code":6027,"name":"NotUsed6027"},{"code":6028,"name":"InvalidAdminAuthority","msg":"Invalid admin authority"},{"code":6029,"name":"InvalidValidatorManager","msg":"Invalid validator system manager"},{"code":6030,"name":"InvalidStakeListDiscriminator","msg":"Invalid stake list account discriminator"},{"code":6031,"name":"InvalidValidatorListDiscriminator","msg":"Invalid validator list account discriminator"},{"code":6032,"name":"TreasuryCutIsTooHigh","msg":"Treasury cut is too high"},{"code":6033,"name":"RewardsFeeIsTooHigh","msg":"Reward fee is too high"},{"code":6034,"name":"StakingIsCapped","msg":"Staking is capped"},{"code":6035,"name":"LiquidityIsCapped","msg":"Liquidity is capped"},{"code":6036,"name":"UpdateWindowIsTooLow","msg":"Update window is too low"},{"code":6037,"name":"MinWithdrawIsTooHigh","msg":"Min withdraw is too high"},{"code":6038,"name":"WithdrawAmountIsTooLow","msg":"Withdraw amount is too low"},{"code":6039,"name":"DepositAmountIsTooLow","msg":"Deposit amount is too low"},{"code":6040,"name":"NotEnoughUserFunds","msg":"Not enough user funds"},{"code":6041,"name":"WrongTokenOwnerOrDelegate","msg":"Wrong token owner or delegate"},{"code":6042,"name":"TooEarlyForStakeDelta","msg":"Too early for stake delta"},{"code":6043,"name":"RequiredDelegatedStake","msg":"Required delegated stake"},{"code":6044,"name":"RequiredActiveStake","msg":"Required active stake"},{"code":6045,"name":"RequiredDeactivatingStake","msg":"Required deactivating stake"},{"code":6046,"name":"DepositingNotActivatedStake","msg":"Depositing not activated stake"},{"code":6047,"name":"TooLowDelegationInDepositingStake","msg":"Too low delegation in the depositing stake"},{"code":6048,"name":"WrongStakeBalance","msg":"Wrong deposited stake balance"},{"code":6049,"name":"WrongValidatorAccountOrIndex","msg":"Wrong validator account or index"},{"code":6050,"name":"WrongStakeAccountOrIndex","msg":"Wrong stake account or index"},{"code":6051,"name":"UnstakingOnPositiveDelta","msg":"Delta stake is positive so we must stake instead of unstake"},{"code":6052,"name":"StakingOnNegativeDelta","msg":"Delta stake is negative so we must unstake instead of stake"},{"code":6053,"name":"MovingStakeIsCapped","msg":"Moving stake during an epoch is capped"},{"code":6054,"name":"StakeMustBeUninitialized","msg":"Stake must be uninitialized"},{"code":6055,"name":"DestinationStakeMustBeDelegated","msg":"Destination stake must be delegated"},{"code":6056,"name":"DestinationStakeMustNotBeDeactivating","msg":"Destination stake must not be deactivating"},{"code":6057,"name":"DestinationStakeMustBeUpdated","msg":"Destination stake must be updated"},{"code":6058,"name":"InvalidDestinationStakeDelegation","msg":"Invalid destination stake delegation"},{"code":6059,"name":"SourceStakeMustBeDelegated","msg":"Source stake must be delegated"},{"code":6060,"name":"SourceStakeMustNotBeDeactivating","msg":"Source stake must not be deactivating"},{"code":6061,"name":"SourceStakeMustBeUpdated","msg":"Source stake must be updated"},{"code":6062,"name":"InvalidSourceStakeDelegation","msg":"Invalid source stake delegation"},{"code":6063,"name":"InvalidDelayedUnstakeTicket","msg":"Invalid delayed unstake ticket"},{"code":6064,"name":"ReusingDelayedUnstakeTicket","msg":"Reusing delayed unstake ticket"},{"code":6065,"name":"EmergencyUnstakingFromNonZeroScoredValidator","msg":"Emergency unstaking from non zero scored validator"},{"code":6066,"name":"WrongValidatorDuplicationFlag","msg":"Wrong validator duplication flag"},{"code":6067,"name":"RedepositingMarinadeStake","msg":"Redepositing marinade stake"},{"code":6068,"name":"RemovingValidatorWithBalance","msg":"Removing validator with balance"},{"code":6069,"name":"RedelegateOverTarget","msg":"Redelegate will put validator over stake target"},{"code":6070,"name":"SourceAndDestValidatorsAreTheSame","msg":"Source and Dest Validators are the same"},{"code":6071,"name":"UnregisteredMsolMinted","msg":"Some mSOL tokens was minted outside of marinade contract"},{"code":6072,"name":"UnregisteredLPMinted","msg":"Some LP tokens was minted outside of marinade contract"},{"code":6073,"name":"ListIndexOutOfBounds","msg":"List index out of bounds"},{"code":6074,"name":"ListOverflow","msg":"List overflow"},{"code":6075,"name":"AlreadyPaused","msg":"Requested pause and already Paused"},{"code":6076,"name":"NotPaused","msg":"Requested resume, but not Paused"},{"code":6077,"name":"ProgramIsPaused","msg":"Emergency Pause is Active"},{"code":6078,"name":"InvalidPauseAuthority","msg":"Invalid pause authority"},{"code":6079,"name":"SelectedStakeAccountHasNotEnoughFunds","msg":"Selected Stake account has not enough funds"},{"code":6080,"name":"BasisPointCentsOverflow","msg":"Basis point CENTS overflow"},{"code":6081,"name":"WithdrawStakeAccountIsNotEnabled","msg":"Withdraw stake account is not enabled"},{"code":6082,"name":"WithdrawStakeAccountFeeIsTooHigh","msg":"Withdraw stake account fee is too high"},{"code":6083,"name":"DelayedUnstakeFeeIsTooHigh","msg":"Delayed unstake fee is too high"},{"code":6084,"name":"WithdrawStakeLamportsIsTooLow","msg":"Withdraw stake account value is too low"},{"code":6085,"name":"StakeAccountRemainderTooLow","msg":"Stake account remainder too low"},{"code":6086,"name":"ShrinkingListWithDeletingContents","msg":"Capacity of the list must be not less than it's current size"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/metadao_amm_v0.5.AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ.json b/migration/khadhroony-bot2-reference/idls/metadao_amm_v0.5.AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ.json new file mode 100644 index 0000000..1a3998f --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/metadao_amm_v0.5.AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ.json @@ -0,0 +1 @@ +{"version":"0.5.0","name":"amm","instructions":[{"name":"createAmm","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"amm","isMut":true,"isSigner":false},{"name":"lpMint","isMut":true,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"vaultAtaBase","isMut":true,"isSigner":false},{"name":"vaultAtaQuote","isMut":true,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"CreateAmmArgs"}}]},{"name":"addLiquidity","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"amm","isMut":true,"isSigner":false},{"name":"lpMint","isMut":true,"isSigner":false},{"name":"userLpAccount","isMut":true,"isSigner":false},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"vaultAtaBase","isMut":true,"isSigner":false},{"name":"vaultAtaQuote","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"AddLiquidityArgs"}}]},{"name":"removeLiquidity","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"amm","isMut":true,"isSigner":false},{"name":"lpMint","isMut":true,"isSigner":false},{"name":"userLpAccount","isMut":true,"isSigner":false},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"vaultAtaBase","isMut":true,"isSigner":false},{"name":"vaultAtaQuote","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"RemoveLiquidityArgs"}}]},{"name":"swap","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"amm","isMut":true,"isSigner":false},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"vaultAtaBase","isMut":true,"isSigner":false},{"name":"vaultAtaQuote","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"SwapArgs"}}]},{"name":"crankThatTwap","accounts":[{"name":"amm","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"Amm","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"createdAtSlot","type":"u64"},{"name":"lpMint","type":"publicKey"},{"name":"baseMint","type":"publicKey"},{"name":"quoteMint","type":"publicKey"},{"name":"baseMintDecimals","type":"u8"},{"name":"quoteMintDecimals","type":"u8"},{"name":"baseAmount","type":"u64"},{"name":"quoteAmount","type":"u64"},{"name":"oracle","type":{"defined":"TwapOracle"}},{"name":"seqNum","type":"u64"},{"name":"vaultAtaBase","type":"publicKey"},{"name":"vaultAtaQuote","type":"publicKey"}]}}],"types":[{"name":"CommonFields","type":{"kind":"struct","fields":[{"name":"slot","type":"u64"},{"name":"unixTimestamp","type":"i64"},{"name":"user","type":"publicKey"},{"name":"amm","type":"publicKey"},{"name":"postBaseReserves","type":"u64"},{"name":"postQuoteReserves","type":"u64"},{"name":"oracleLastPrice","type":"u128"},{"name":"oracleLastObservation","type":"u128"},{"name":"oracleAggregator","type":"u128"},{"name":"seqNum","type":"u64"}]}},{"name":"AddLiquidityArgs","type":{"kind":"struct","fields":[{"name":"quoteAmount","docs":["How much quote token you will deposit to the pool"],"type":"u64"},{"name":"maxBaseAmount","docs":["The maximum base token you will deposit to the pool"],"type":"u64"},{"name":"minLpTokens","docs":["The minimum LP token you will get back"],"type":"u64"}]}},{"name":"CreateAmmArgs","type":{"kind":"struct","fields":[{"name":"twapInitialObservation","type":"u128"},{"name":"twapMaxObservationChangePerUpdate","type":"u128"},{"name":"twapStartDelaySlots","type":"u64"}]}},{"name":"RemoveLiquidityArgs","type":{"kind":"struct","fields":[{"name":"lpTokensToBurn","type":"u64"},{"name":"minQuoteAmount","type":"u64"},{"name":"minBaseAmount","type":"u64"}]}},{"name":"SwapArgs","type":{"kind":"struct","fields":[{"name":"swapType","type":{"defined":"SwapType"}},{"name":"inputAmount","type":"u64"},{"name":"outputAmountMin","type":"u64"}]}},{"name":"TwapOracle","type":{"kind":"struct","fields":[{"name":"lastUpdatedSlot","type":"u64"},{"name":"lastPrice","docs":["A price is the number of quote units per base unit multiplied by 1e12.","You cannot simply divide by 1e12 to get a price you can display in the UI","because the base and quote decimals may be different. Instead, do:","ui_price = (price * (10**(base_decimals - quote_decimals))) / 1e12"],"type":"u128"},{"name":"lastObservation","docs":["If we did a raw TWAP over prices, someone could push the TWAP heavily with","a few extremely large outliers. So we use observations, which can only move","by `max_observation_change_per_update` per update."],"type":"u128"},{"name":"aggregator","docs":["Running sum of slots_per_last_update * last_observation.","","Assuming latest observations are as big as possible (u64::MAX * 1e12),","we can store 18 million slots worth of observations, which turns out to","be ~85 days worth of slots.","","Assuming that latest observations are 100x smaller than they could theoretically","be, we can store 8500 days (23 years) worth of them. Even this is a very","very conservative assumption - META/USDC prices should be between 1e9 and","1e15, which would overflow after 1e15 years worth of slots.","","So in the case of an overflow, the aggregator rolls back to 0. It's the","client's responsibility to sanity check the assets or to handle an","aggregator at T2 being smaller than an aggregator at T1."],"type":"u128"},{"name":"maxObservationChangePerUpdate","docs":["The most that an observation can change per update."],"type":"u128"},{"name":"initialObservation","docs":["What the initial `latest_observation` is set to."],"type":"u128"},{"name":"startDelaySlots","docs":["Number of slots after amm.created_at_slot to start recording TWAP"],"type":"u64"}]}},{"name":"SwapType","type":{"kind":"enum","variants":[{"name":"Buy"},{"name":"Sell"}]}}],"events":[{"name":"SwapEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"inputAmount","type":"u64","index":false},{"name":"outputAmount","type":"u64","index":false},{"name":"swapType","type":{"defined":"SwapType"},"index":false}]},{"name":"AddLiquidityEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"quoteAmount","type":"u64","index":false},{"name":"maxBaseAmount","type":"u64","index":false},{"name":"minLpTokens","type":"u64","index":false},{"name":"baseAmount","type":"u64","index":false},{"name":"lpTokensMinted","type":"u64","index":false}]},{"name":"RemoveLiquidityEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"lpTokensBurned","type":"u64","index":false},{"name":"minQuoteAmount","type":"u64","index":false},{"name":"minBaseAmount","type":"u64","index":false},{"name":"baseAmount","type":"u64","index":false},{"name":"quoteAmount","type":"u64","index":false}]},{"name":"CreateAmmEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"twapInitialObservation","type":"u128","index":false},{"name":"twapMaxObservationChangePerUpdate","type":"u128","index":false},{"name":"lpMint","type":"publicKey","index":false},{"name":"baseMint","type":"publicKey","index":false},{"name":"quoteMint","type":"publicKey","index":false},{"name":"vaultAtaBase","type":"publicKey","index":false},{"name":"vaultAtaQuote","type":"publicKey","index":false}]},{"name":"CrankThatTwapEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false}]}],"errors":[{"code":6000,"name":"AssertFailed","msg":"An assertion failed"},{"code":6001,"name":"NoSlotsPassed","msg":"Can't get a TWAP before some observations have been stored"},{"code":6002,"name":"NoReserves","msg":"Can't swap through a pool without token reserves on either side"},{"code":6003,"name":"InputAmountOverflow","msg":"Input token amount is too large for a swap, causes overflow"},{"code":6004,"name":"AddLiquidityCalculationError","msg":"Add liquidity calculation error"},{"code":6005,"name":"DecimalScaleError","msg":"Error in decimal scale conversion"},{"code":6006,"name":"SameTokenMints","msg":"You can't create an AMM pool where the token mints are the same"},{"code":6007,"name":"SwapSlippageExceeded","msg":"A user wouldn't have gotten back their `output_amount_min`, reverting"},{"code":6008,"name":"InsufficientBalance","msg":"The user had insufficient balance to do this"},{"code":6009,"name":"ZeroLiquidityRemove","msg":"Must remove a non-zero amount of liquidity"},{"code":6010,"name":"ZeroLiquidityToAdd","msg":"Cannot add liquidity with 0 tokens on either side"},{"code":6011,"name":"ZeroMinLpTokens","msg":"Must specify a non-zero `min_lp_tokens` when adding to an existing pool"},{"code":6012,"name":"AddLiquiditySlippageExceeded","msg":"LP wouldn't have gotten back `lp_token_min`"},{"code":6013,"name":"AddLiquidityMaxBaseExceeded","msg":"LP would have spent more than `max_base_amount`"},{"code":6014,"name":"InsufficientQuoteAmount","msg":"`quote_amount` must be greater than 100000000 when initializing a pool"},{"code":6015,"name":"ZeroSwapAmount","msg":"Users must swap a non-zero amount"},{"code":6016,"name":"ConstantProductInvariantFailed","msg":"K should always be increasing"},{"code":6017,"name":"CastingOverflow","msg":"Casting has caused an overflow"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/metadao_futarchy.FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq.json b/migration/khadhroony-bot2-reference/idls/metadao_futarchy.FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq.json new file mode 100644 index 0000000..6d88d2c --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/metadao_futarchy.FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq.json @@ -0,0 +1 @@ +{"version":"0.6.1","name":"futarchy","instructions":[{"name":"initializeDao","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"daoCreator","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"squadsMultisig","isMut":true,"isSigner":false},{"name":"squadsMultisigVault","isMut":false,"isSigner":false},{"name":"squadsProgram","isMut":false,"isSigner":false},{"name":"squadsProgramConfig","isMut":false,"isSigner":false},{"name":"squadsProgramConfigTreasury","isMut":true,"isSigner":false},{"name":"spendingLimit","isMut":true,"isSigner":false},{"name":"futarchyAmmBaseVault","isMut":true,"isSigner":false},{"name":"futarchyAmmQuoteVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitializeDaoParams"}}]},{"name":"initializeProposal","accounts":[{"name":"proposal","isMut":true,"isSigner":false},{"name":"squadsProposal","isMut":false,"isSigner":false},{"name":"squadsMultisig","isMut":false,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"question","isMut":false,"isSigner":false},{"name":"quoteVault","isMut":false,"isSigner":false},{"name":"baseVault","isMut":false,"isSigner":false},{"name":"proposer","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"stakeToProposal","accounts":[{"name":"proposal","isMut":true,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"stakerBaseAccount","isMut":true,"isSigner":false},{"name":"proposalBaseAccount","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"staker","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"StakeToProposalParams"}}]},{"name":"unstakeFromProposal","accounts":[{"name":"proposal","isMut":true,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"stakerBaseAccount","isMut":true,"isSigner":false},{"name":"proposalBaseAccount","isMut":true,"isSigner":false},{"name":"stakeAccount","isMut":true,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"staker","isMut":true,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"UnstakeFromProposalParams"}}]},{"name":"launchProposal","accounts":[{"name":"proposal","isMut":true,"isSigner":false},{"name":"baseVault","isMut":false,"isSigner":false},{"name":"quoteVault","isMut":false,"isSigner":false},{"name":"passBaseMint","isMut":false,"isSigner":false},{"name":"passQuoteMint","isMut":false,"isSigner":false},{"name":"failBaseMint","isMut":false,"isSigner":false},{"name":"failQuoteMint","isMut":false,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"ammPassBaseVault","isMut":true,"isSigner":false},{"name":"ammPassQuoteVault","isMut":true,"isSigner":false},{"name":"ammFailBaseVault","isMut":true,"isSigner":false},{"name":"ammFailQuoteVault","isMut":true,"isSigner":false},{"name":"squadsMultisig","isMut":false,"isSigner":false},{"name":"squadsProposal","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"finalizeProposal","accounts":[{"name":"proposal","isMut":true,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"question","isMut":true,"isSigner":false},{"name":"squadsProposal","isMut":true,"isSigner":false},{"name":"squadsMultisig","isMut":false,"isSigner":false},{"name":"squadsMultisigProgram","isMut":false,"isSigner":false},{"name":"ammPassBaseVault","isMut":true,"isSigner":false},{"name":"ammPassQuoteVault","isMut":true,"isSigner":false},{"name":"ammFailBaseVault","isMut":true,"isSigner":false},{"name":"ammFailQuoteVault","isMut":true,"isSigner":false},{"name":"ammBaseVault","isMut":true,"isSigner":false},{"name":"ammQuoteVault","isMut":true,"isSigner":false},{"name":"vaultProgram","isMut":false,"isSigner":false},{"name":"vaultEventAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"quoteVault","isMut":true,"isSigner":false},{"name":"quoteVaultUnderlyingTokenAccount","isMut":true,"isSigner":false},{"name":"passQuoteMint","isMut":true,"isSigner":false},{"name":"failQuoteMint","isMut":true,"isSigner":false},{"name":"passBaseMint","isMut":true,"isSigner":false},{"name":"failBaseMint","isMut":true,"isSigner":false},{"name":"baseVault","isMut":true,"isSigner":false},{"name":"baseVaultUnderlyingTokenAccount","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"updateDao","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"squadsMultisigVault","isMut":false,"isSigner":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"daoParams","type":{"defined":"UpdateDaoParams"}}]},{"name":"resizeDao","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"spotSwap","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"ammBaseVault","isMut":true,"isSigner":false},{"name":"ammQuoteVault","isMut":true,"isSigner":false},{"name":"user","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"SpotSwapParams"}}]},{"name":"conditionalSwap","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"ammBaseVault","isMut":true,"isSigner":false},{"name":"ammQuoteVault","isMut":true,"isSigner":false},{"name":"proposal","isMut":false,"isSigner":false},{"name":"ammPassBaseVault","isMut":true,"isSigner":false},{"name":"ammPassQuoteVault","isMut":true,"isSigner":false},{"name":"ammFailBaseVault","isMut":true,"isSigner":false},{"name":"ammFailQuoteVault","isMut":true,"isSigner":false},{"name":"trader","isMut":false,"isSigner":true},{"name":"userInputAccount","isMut":true,"isSigner":false},{"name":"userOutputAccount","isMut":true,"isSigner":false},{"name":"baseVault","isMut":true,"isSigner":false},{"name":"baseVaultUnderlyingTokenAccount","isMut":true,"isSigner":false},{"name":"quoteVault","isMut":true,"isSigner":false},{"name":"quoteVaultUnderlyingTokenAccount","isMut":true,"isSigner":false},{"name":"passBaseMint","isMut":true,"isSigner":false},{"name":"failBaseMint","isMut":true,"isSigner":false},{"name":"passQuoteMint","isMut":true,"isSigner":false},{"name":"failQuoteMint","isMut":true,"isSigner":false},{"name":"conditionalVaultProgram","isMut":false,"isSigner":false},{"name":"vaultEventAuthority","isMut":false,"isSigner":false},{"name":"question","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"ConditionalSwapParams"}}]},{"name":"provideLiquidity","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"liquidityProvider","isMut":false,"isSigner":true},{"name":"liquidityProviderBaseAccount","isMut":true,"isSigner":false},{"name":"liquidityProviderQuoteAccount","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"ammBaseVault","isMut":true,"isSigner":false},{"name":"ammQuoteVault","isMut":true,"isSigner":false},{"name":"ammPosition","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"ProvideLiquidityParams"}}]},{"name":"withdrawLiquidity","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"positionAuthority","isMut":false,"isSigner":true},{"name":"liquidityProviderBaseAccount","isMut":true,"isSigner":false},{"name":"liquidityProviderQuoteAccount","isMut":true,"isSigner":false},{"name":"ammBaseVault","isMut":true,"isSigner":false},{"name":"ammQuoteVault","isMut":true,"isSigner":false},{"name":"ammPosition","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"WithdrawLiquidityParams"}}]},{"name":"collectFees","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"baseTokenAccount","isMut":true,"isSigner":false},{"name":"quoteTokenAccount","isMut":true,"isSigner":false},{"name":"ammBaseVault","isMut":true,"isSigner":false},{"name":"ammQuoteVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"executeSpendingLimitChange","accounts":[{"name":"proposal","isMut":true,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"squadsProposal","isMut":true,"isSigner":false},{"name":"squadsMultisig","isMut":false,"isSigner":false},{"name":"squadsMultisigProgram","isMut":false,"isSigner":false},{"name":"vaultTransaction","isMut":false,"isSigner":false}],"args":[]},{"name":"sponsorProposal","accounts":[{"name":"proposal","isMut":true,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"teamAddress","isMut":false,"isSigner":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"collectMeteoraDammFees","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"squadsMultisig","isMut":true,"isSigner":false},{"name":"squadsMultisigVault","isMut":true,"isSigner":false},{"name":"squadsMultisigVaultTransaction","isMut":true,"isSigner":false},{"name":"squadsMultisigProposal","isMut":true,"isSigner":false},{"name":"squadsMultisigPermissionlessAccount","isMut":false,"isSigner":true},{"name":"meteoraClaimPositionFeesAccounts","accounts":[{"name":"dammV2Program","isMut":false,"isSigner":false},{"name":"dammV2EventAuthority","isMut":false,"isSigner":false},{"name":"poolAuthority","isMut":false,"isSigner":false},{"name":"pool","isMut":false,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"tokenAAccount","isMut":true,"isSigner":false,"docs":["Token account of base tokens recipient"]},{"name":"tokenBAccount","isMut":true,"isSigner":false,"docs":["Token account of quote tokens recipient"]},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"tokenAMint","isMut":false,"isSigner":false},{"name":"tokenBMint","isMut":false,"isSigner":false},{"name":"positionNftAccount","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":false},{"name":"tokenAProgram","isMut":false,"isSigner":false},{"name":"tokenBProgram","isMut":false,"isSigner":false}]},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"squadsProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"initiateVaultSpendOptimisticProposal","accounts":[{"name":"squadsMultisig","isMut":false,"isSigner":false},{"name":"squadsMultisigVault","isMut":false,"isSigner":false},{"name":"squadsSpendingLimit","isMut":false,"isSigner":false},{"name":"squadsProposal","isMut":false,"isSigner":false},{"name":"squadsVaultTransaction","isMut":false,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"daoQuoteVaultAccount","isMut":false,"isSigner":false},{"name":"proposer","isMut":false,"isSigner":true},{"name":"recipient","isMut":false,"isSigner":false},{"name":"recipientQuoteAccount","isMut":false,"isSigner":false},{"name":"squadsProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"params","type":{"defined":"InitiateVaultSpendOptimisticProposalParams"}}]},{"name":"finalizeOptimisticProposal","accounts":[{"name":"squadsMultisig","isMut":true,"isSigner":false},{"name":"squadsProposal","isMut":true,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"squadsProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"adminEnqueueMultisigProposalApproval","accounts":[{"name":"dao","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"squadsMultisig","isMut":false,"isSigner":false},{"name":"squadsMultisigProposal","isMut":false,"isSigner":false},{"name":"enqueuedApproval","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"AdminEnqueueMultisigProposalApprovalArgs"}}]},{"name":"executeMultisigProposalApproval","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"rentReceiver","isMut":true,"isSigner":true},{"name":"squadsMultisig","isMut":true,"isSigner":false},{"name":"squadsMultisigProposal","isMut":true,"isSigner":false},{"name":"enqueuedApproval","isMut":true,"isSigner":false},{"name":"squadsMultisigProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"adminExecuteMultisigProposal","accounts":[{"name":"dao","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"squadsMultisig","isMut":true,"isSigner":false},{"name":"squadsMultisigProposal","isMut":true,"isSigner":false},{"name":"squadsMultisigVaultTransaction","isMut":true,"isSigner":false},{"name":"squadsMultisigProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"adminCancelProposal","accounts":[{"name":"proposal","isMut":true,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"question","isMut":true,"isSigner":false},{"name":"squadsProposal","isMut":true,"isSigner":false},{"name":"squadsMultisig","isMut":false,"isSigner":false},{"name":"squadsMultisigProgram","isMut":false,"isSigner":false},{"name":"ammPassBaseVault","isMut":true,"isSigner":false},{"name":"ammPassQuoteVault","isMut":true,"isSigner":false},{"name":"ammFailBaseVault","isMut":true,"isSigner":false},{"name":"ammFailQuoteVault","isMut":true,"isSigner":false},{"name":"ammBaseVault","isMut":true,"isSigner":false},{"name":"ammQuoteVault","isMut":true,"isSigner":false},{"name":"vaultProgram","isMut":false,"isSigner":false},{"name":"vaultEventAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"quoteVault","isMut":true,"isSigner":false},{"name":"quoteVaultUnderlyingTokenAccount","isMut":true,"isSigner":false},{"name":"passQuoteMint","isMut":true,"isSigner":false},{"name":"failQuoteMint","isMut":true,"isSigner":false},{"name":"passBaseMint","isMut":true,"isSigner":false},{"name":"failBaseMint","isMut":true,"isSigner":false},{"name":"baseVault","isMut":true,"isSigner":false},{"name":"baseVaultUnderlyingTokenAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"adminRemoveProposal","accounts":[{"name":"proposal","isMut":true,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"AmmPosition","type":{"kind":"struct","fields":[{"name":"dao","type":"publicKey"},{"name":"positionAuthority","type":"publicKey"},{"name":"liquidity","type":"u128"}]}},{"name":"Dao","type":{"kind":"struct","fields":[{"name":"amm","docs":["Embedded FutarchyAmm - 1:1 relationship"],"type":{"defined":"FutarchyAmm"}},{"name":"nonce","docs":["`nonce` + `dao_creator` are PDA seeds"],"type":"u64"},{"name":"daoCreator","type":"publicKey"},{"name":"pdaBump","type":"u8"},{"name":"squadsMultisig","type":"publicKey"},{"name":"squadsMultisigVault","type":"publicKey"},{"name":"baseMint","type":"publicKey"},{"name":"quoteMint","type":"publicKey"},{"name":"proposalCount","type":"u32"},{"name":"passThresholdBps","type":"u16"},{"name":"secondsPerProposal","type":"u32"},{"name":"twapInitialObservation","docs":["For manipulation-resistance the TWAP is a time-weighted average observation,","where observation tries to approximate price but can only move by","`twap_max_observation_change_per_update` per update. Because it can only move","a little bit per update, you need to check that it has a good initial observation.","Otherwise, an attacker could create a very high initial observation in the pass","market and a very low one in the fail market to force the proposal to pass.","","We recommend setting an initial observation around the spot price of the token,","and max observation change per update around 2% the spot price of the token.","For example, if the spot price of META is $400, we'd recommend setting an initial","observation of 400 (converted into the AMM prices) and a max observation change per","update of 8 (also converted into the AMM prices). Observations can be updated once","a minute, so 2% allows the proposal market to reach double the spot price or 0","in 50 minutes."],"type":"u128"},{"name":"twapMaxObservationChangePerUpdate","type":"u128"},{"name":"twapStartDelaySeconds","docs":["Forces TWAP calculation to start after `twap_start_delay_seconds` seconds"],"type":"u32"},{"name":"minQuoteFutarchicLiquidity","docs":["As an anti-spam measure and to help liquidity, you need to lock up some liquidity","in both futarchic markets in order to create a proposal.","","For example, for META, we can use a `min_quote_futarchic_liquidity` of","5000 * 1_000_000 (5000 USDC) and a `min_base_futarchic_liquidity` of","10 * 1_000_000_000 (10 META)."],"type":"u64"},{"name":"minBaseFutarchicLiquidity","type":"u64"},{"name":"baseToStake","docs":["Minimum amount of base tokens that must be staked to launch a proposal"],"type":"u64"},{"name":"seqNum","type":"u64"},{"name":"initialSpendingLimit","type":{"option":{"defined":"InitialSpendingLimit"}}},{"name":"teamSponsoredPassThresholdBps","docs":["The percentage, in basis points, the pass price needs to be above the","fail price in order for the proposal to pass for team-sponsored proposals.","","Can be negative to allow for team-sponsored proposals to pass by default."],"type":"i16"},{"name":"teamAddress","type":"publicKey"},{"name":"optimisticProposal","type":{"option":{"defined":"OptimisticProposal"}}},{"name":"isOptimisticGovernanceEnabled","type":"bool"}]}},{"name":"OldDao","type":{"kind":"struct","fields":[{"name":"amm","docs":["Embedded FutarchyAmm - 1:1 relationship"],"type":{"defined":"FutarchyAmm"}},{"name":"nonce","docs":["`nonce` + `dao_creator` are PDA seeds"],"type":"u64"},{"name":"daoCreator","type":"publicKey"},{"name":"pdaBump","type":"u8"},{"name":"squadsMultisig","type":"publicKey"},{"name":"squadsMultisigVault","type":"publicKey"},{"name":"baseMint","type":"publicKey"},{"name":"quoteMint","type":"publicKey"},{"name":"proposalCount","type":"u32"},{"name":"passThresholdBps","type":"u16"},{"name":"secondsPerProposal","type":"u32"},{"name":"twapInitialObservation","docs":["For manipulation-resistance the TWAP is a time-weighted average observation,","where observation tries to approximate price but can only move by","`twap_max_observation_change_per_update` per update. Because it can only move","a little bit per update, you need to check that it has a good initial observation.","Otherwise, an attacker could create a very high initial observation in the pass","market and a very low one in the fail market to force the proposal to pass.","","We recommend setting an initial observation around the spot price of the token,","and max observation change per update around 2% the spot price of the token.","For example, if the spot price of META is $400, we'd recommend setting an initial","observation of 400 (converted into the AMM prices) and a max observation change per","update of 8 (also converted into the AMM prices). Observations can be updated once","a minute, so 2% allows the proposal market to reach double the spot price or 0","in 50 minutes."],"type":"u128"},{"name":"twapMaxObservationChangePerUpdate","type":"u128"},{"name":"twapStartDelaySeconds","docs":["Forces TWAP calculation to start after `twap_start_delay_seconds` seconds"],"type":"u32"},{"name":"minQuoteFutarchicLiquidity","docs":["As an anti-spam measure and to help liquidity, you need to lock up some liquidity","in both futarchic markets in order to create a proposal.","","For example, for META, we can use a `min_quote_futarchic_liquidity` of","5000 * 1_000_000 (5000 USDC) and a `min_base_futarchic_liquidity` of","10 * 1_000_000_000 (10 META)."],"type":"u64"},{"name":"minBaseFutarchicLiquidity","type":"u64"},{"name":"baseToStake","docs":["Minimum amount of base tokens that must be staked to launch a proposal"],"type":"u64"},{"name":"seqNum","type":"u64"},{"name":"initialSpendingLimit","type":{"option":{"defined":"InitialSpendingLimit"}}},{"name":"teamSponsoredPassThresholdBps","docs":["The percentage, in basis points, the pass price needs to be above the","fail price in order for the proposal to pass for team-sponsored proposals.","","Can be negative to allow for team-sponsored proposals to pass by default."],"type":"i16"},{"name":"teamAddress","type":"publicKey"}]}},{"name":"EnqueuedMultisigProposalApproval","type":{"kind":"struct","fields":[{"name":"dao","type":"publicKey"},{"name":"transactionIndex","type":"u64"},{"name":"pdaBump","type":"u8"}]}},{"name":"Proposal","type":{"kind":"struct","fields":[{"name":"number","type":"u32"},{"name":"proposer","type":"publicKey"},{"name":"timestampEnqueued","type":"i64"},{"name":"state","type":{"defined":"ProposalState"}},{"name":"baseVault","type":"publicKey"},{"name":"quoteVault","type":"publicKey"},{"name":"dao","type":"publicKey"},{"name":"pdaBump","type":"u8"},{"name":"question","type":"publicKey"},{"name":"durationInSeconds","type":"u32"},{"name":"squadsProposal","type":"publicKey"},{"name":"passBaseMint","type":"publicKey"},{"name":"passQuoteMint","type":"publicKey"},{"name":"failBaseMint","type":"publicKey"},{"name":"failQuoteMint","type":"publicKey"},{"name":"isTeamSponsored","type":"bool"}]}},{"name":"StakeAccount","type":{"kind":"struct","fields":[{"name":"proposal","type":"publicKey"},{"name":"staker","type":"publicKey"},{"name":"amount","type":"u64"},{"name":"bump","type":"u8"}]}}],"types":[{"name":"CommonFields","type":{"kind":"struct","fields":[{"name":"slot","type":"u64"},{"name":"unixTimestamp","type":"i64"},{"name":"daoSeqNum","type":"u64"}]}},{"name":"AdminEnqueueMultisigProposalApprovalArgs","type":{"kind":"struct","fields":[{"name":"transactionIndex","type":"u64"}]}},{"name":"ConditionalSwapParams","type":{"kind":"struct","fields":[{"name":"market","type":{"defined":"Market"}},{"name":"swapType","type":{"defined":"SwapType"}},{"name":"inputAmount","type":"u64"},{"name":"minOutputAmount","type":"u64"}]}},{"name":"InitializeDaoParams","type":{"kind":"struct","fields":[{"name":"twapInitialObservation","type":"u128"},{"name":"twapMaxObservationChangePerUpdate","type":"u128"},{"name":"twapStartDelaySeconds","type":"u32"},{"name":"minQuoteFutarchicLiquidity","type":"u64"},{"name":"minBaseFutarchicLiquidity","type":"u64"},{"name":"baseToStake","type":"u64"},{"name":"passThresholdBps","type":"u16"},{"name":"secondsPerProposal","type":"u32"},{"name":"nonce","type":"u64"},{"name":"initialSpendingLimit","type":{"option":{"defined":"InitialSpendingLimit"}}},{"name":"teamSponsoredPassThresholdBps","type":"i16"},{"name":"teamAddress","type":"publicKey"}]}},{"name":"InitiateVaultSpendOptimisticProposalParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"ProvideLiquidityParams","type":{"kind":"struct","fields":[{"name":"quoteAmount","docs":["How much quote token you will deposit to the pool"],"type":"u64"},{"name":"maxBaseAmount","docs":["The maximum base token you will deposit to the pool"],"type":"u64"},{"name":"minLiquidity","docs":["The minimum liquidity you will be assigned"],"type":"u128"},{"name":"positionAuthority","docs":["The account that will own the LP position, usually the same as the","liquidity provider"],"type":"publicKey"}]}},{"name":"SpotSwapParams","type":{"kind":"struct","fields":[{"name":"inputAmount","type":"u64"},{"name":"swapType","type":{"defined":"SwapType"}},{"name":"minOutputAmount","type":"u64"}]}},{"name":"StakeToProposalParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"UnstakeFromProposalParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"UpdateDaoParams","type":{"kind":"struct","fields":[{"name":"passThresholdBps","type":{"option":"u16"}},{"name":"secondsPerProposal","type":{"option":"u32"}},{"name":"twapInitialObservation","type":{"option":"u128"}},{"name":"twapMaxObservationChangePerUpdate","type":{"option":"u128"}},{"name":"twapStartDelaySeconds","type":{"option":"u32"}},{"name":"minQuoteFutarchicLiquidity","type":{"option":"u64"}},{"name":"minBaseFutarchicLiquidity","type":{"option":"u64"}},{"name":"baseToStake","type":{"option":"u64"}},{"name":"teamSponsoredPassThresholdBps","type":{"option":"i16"}},{"name":"teamAddress","type":{"option":"publicKey"}},{"name":"isOptimisticGovernanceEnabled","type":{"option":"bool"}}]}},{"name":"WithdrawLiquidityParams","type":{"kind":"struct","fields":[{"name":"liquidityToWithdraw","docs":["How much liquidity to withdraw"],"type":"u128"},{"name":"minBaseAmount","docs":["Minimum base tokens to receive"],"type":"u64"},{"name":"minQuoteAmount","docs":["Minimum quote tokens to receive"],"type":"u64"}]}},{"name":"OptimisticProposal","type":{"kind":"struct","fields":[{"name":"squadsProposal","docs":["The squads proposal currently enqueued for execution if not challenged by a new proposal."],"type":"publicKey"},{"name":"enqueuedTimestamp","docs":["The timestamp when the active optimistic squads proposal was enqueued."],"type":"i64"}]}},{"name":"InitialSpendingLimit","type":{"kind":"struct","fields":[{"name":"amountPerMonth","type":"u64"},{"name":"members","type":{"vec":"publicKey"}}]}},{"name":"FutarchyAmm","type":{"kind":"struct","fields":[{"name":"state","type":{"defined":"PoolState"}},{"name":"totalLiquidity","type":"u128"},{"name":"baseMint","type":"publicKey"},{"name":"quoteMint","type":"publicKey"},{"name":"ammBaseVault","type":"publicKey"},{"name":"ammQuoteVault","type":"publicKey"}]}},{"name":"TwapOracle","type":{"kind":"struct","fields":[{"name":"aggregator","docs":["Running sum of seconds_since_last_update * last_observation.","","Assuming latest observations are as big as possible (u64::MAX * 1e12),","we can store 18 million seconds worth of observations, which turns out to","be ~213 days.","","Assuming that latest observations are 100x smaller than they could theoretically","be, we can store ~57 years worth of them. Even this is a very","very conservative assumption - META/USDC prices should be between 1e9 and","1e15, which would overflow after 1e15 years.","","So in the case of an overflow, the aggregator rolls back to 0. It's the","client's responsibility to sanity check the assets or to handle an","aggregator at T2 being smaller than an aggregator at T1."],"type":"u128"},{"name":"lastUpdatedTimestamp","type":"i64"},{"name":"createdAtTimestamp","type":"i64"},{"name":"lastPrice","docs":["A price is the number of quote units per base unit multiplied by 1e12.","You cannot simply divide by 1e12 to get a price you can display in the UI","because the base and quote decimals may be different. Instead, do:","ui_price = (price * (10**(base_decimals - quote_decimals))) / 1e12"],"type":"u128"},{"name":"lastObservation","docs":["If we did a raw TWAP over prices, someone could push the TWAP heavily with","a few extremely large outliers. So we use observations, which can only move","by `max_observation_change_per_update` per update."],"type":"u128"},{"name":"maxObservationChangePerUpdate","docs":["The most that an observation can change per update."],"type":"u128"},{"name":"initialObservation","docs":["What the initial `latest_observation` is set to."],"type":"u128"},{"name":"startDelaySeconds","docs":["Number of seconds after amm.created_at_slot to start recording TWAP"],"type":"u32"}]}},{"name":"Pool","type":{"kind":"struct","fields":[{"name":"oracle","type":{"defined":"TwapOracle"}},{"name":"quoteReserves","type":"u64"},{"name":"baseReserves","type":"u64"},{"name":"quoteProtocolFeeBalance","type":"u64"},{"name":"baseProtocolFeeBalance","type":"u64"}]}},{"name":"PoolState","type":{"kind":"enum","variants":[{"name":"Spot","fields":[{"name":"spot","type":{"defined":"Pool"}}]},{"name":"Futarchy","fields":[{"name":"spot","type":{"defined":"Pool"}},{"name":"pass","type":{"defined":"Pool"}},{"name":"fail","type":{"defined":"Pool"}}]}]}},{"name":"Market","type":{"kind":"enum","variants":[{"name":"Spot"},{"name":"Pass"},{"name":"Fail"}]}},{"name":"SwapType","type":{"kind":"enum","variants":[{"name":"Buy"},{"name":"Sell"}]}},{"name":"Token","type":{"kind":"enum","variants":[{"name":"Base"},{"name":"Quote"}]}},{"name":"ProposalState","type":{"kind":"enum","variants":[{"name":"Draft","fields":[{"name":"amountStaked","type":"u64"}]},{"name":"Pending"},{"name":"Passed"},{"name":"Failed"},{"name":"Removed"}]}}],"events":[{"name":"CollectFeesEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"baseTokenAccount","type":"publicKey","index":false},{"name":"quoteTokenAccount","type":"publicKey","index":false},{"name":"ammBaseVault","type":"publicKey","index":false},{"name":"ammQuoteVault","type":"publicKey","index":false},{"name":"quoteMint","type":"publicKey","index":false},{"name":"baseMint","type":"publicKey","index":false},{"name":"quoteFeesCollected","type":"u64","index":false},{"name":"baseFeesCollected","type":"u64","index":false},{"name":"postAmmState","type":{"defined":"FutarchyAmm"},"index":false}]},{"name":"InitializeDaoEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"baseMint","type":"publicKey","index":false},{"name":"quoteMint","type":"publicKey","index":false},{"name":"passThresholdBps","type":"u16","index":false},{"name":"secondsPerProposal","type":"u32","index":false},{"name":"twapInitialObservation","type":"u128","index":false},{"name":"twapMaxObservationChangePerUpdate","type":"u128","index":false},{"name":"twapStartDelaySeconds","type":"u32","index":false},{"name":"minQuoteFutarchicLiquidity","type":"u64","index":false},{"name":"minBaseFutarchicLiquidity","type":"u64","index":false},{"name":"baseToStake","type":"u64","index":false},{"name":"initialSpendingLimit","type":{"option":{"defined":"InitialSpendingLimit"}},"index":false},{"name":"squadsMultisig","type":"publicKey","index":false},{"name":"squadsMultisigVault","type":"publicKey","index":false},{"name":"teamSponsoredPassThresholdBps","type":"i16","index":false},{"name":"teamAddress","type":"publicKey","index":false}]},{"name":"UpdateDaoEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"passThresholdBps","type":"u16","index":false},{"name":"secondsPerProposal","type":"u32","index":false},{"name":"twapInitialObservation","type":"u128","index":false},{"name":"twapMaxObservationChangePerUpdate","type":"u128","index":false},{"name":"twapStartDelaySeconds","type":"u32","index":false},{"name":"minQuoteFutarchicLiquidity","type":"u64","index":false},{"name":"minBaseFutarchicLiquidity","type":"u64","index":false},{"name":"baseToStake","type":"u64","index":false},{"name":"teamSponsoredPassThresholdBps","type":"i16","index":false},{"name":"teamAddress","type":"publicKey","index":false},{"name":"isOptimisticGovernanceEnabled","type":"bool","index":false}]},{"name":"InitializeProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"dao","type":"publicKey","index":false},{"name":"question","type":"publicKey","index":false},{"name":"quoteVault","type":"publicKey","index":false},{"name":"baseVault","type":"publicKey","index":false},{"name":"proposer","type":"publicKey","index":false},{"name":"number","type":"u32","index":false},{"name":"pdaBump","type":"u8","index":false},{"name":"durationInSeconds","type":"u32","index":false},{"name":"squadsProposal","type":"publicKey","index":false},{"name":"squadsMultisig","type":"publicKey","index":false},{"name":"squadsMultisigVault","type":"publicKey","index":false}]},{"name":"StakeToProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"staker","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"totalStaked","type":"u64","index":false}]},{"name":"UnstakeFromProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"staker","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"totalStaked","type":"u64","index":false}]},{"name":"LaunchProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"dao","type":"publicKey","index":false},{"name":"timestampEnqueued","type":"i64","index":false},{"name":"totalStaked","type":"u64","index":false},{"name":"postAmmState","type":{"defined":"FutarchyAmm"},"index":false}]},{"name":"FinalizeProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"dao","type":"publicKey","index":false},{"name":"passMarketTwap","type":"u128","index":false},{"name":"failMarketTwap","type":"u128","index":false},{"name":"threshold","type":"u128","index":false},{"name":"state","type":{"defined":"ProposalState"},"index":false},{"name":"squadsProposal","type":"publicKey","index":false},{"name":"squadsMultisig","type":"publicKey","index":false},{"name":"postAmmState","type":{"defined":"FutarchyAmm"},"index":false},{"name":"isTeamSponsored","type":"bool","index":false}]},{"name":"SpotSwapEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"user","type":"publicKey","index":false},{"name":"swapType","type":{"defined":"SwapType"},"index":false},{"name":"inputAmount","type":"u64","index":false},{"name":"outputAmount","type":"u64","index":false},{"name":"minOutputAmount","type":"u64","index":false},{"name":"postAmmState","type":{"defined":"FutarchyAmm"},"index":false}]},{"name":"ConditionalSwapEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"trader","type":"publicKey","index":false},{"name":"market","type":{"defined":"Market"},"index":false},{"name":"swapType","type":{"defined":"SwapType"},"index":false},{"name":"inputAmount","type":"u64","index":false},{"name":"outputAmount","type":"u64","index":false},{"name":"minOutputAmount","type":"u64","index":false},{"name":"postAmmState","type":{"defined":"FutarchyAmm"},"index":false}]},{"name":"ProvideLiquidityEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"liquidityProvider","type":"publicKey","index":false},{"name":"positionAuthority","type":"publicKey","index":false},{"name":"quoteAmount","type":"u64","index":false},{"name":"baseAmount","type":"u64","index":false},{"name":"liquidityMinted","type":"u128","index":false},{"name":"minLiquidity","type":"u128","index":false},{"name":"postAmmState","type":{"defined":"FutarchyAmm"},"index":false}]},{"name":"WithdrawLiquidityEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"liquidityProvider","type":"publicKey","index":false},{"name":"liquidityWithdrawn","type":"u128","index":false},{"name":"minBaseAmount","type":"u64","index":false},{"name":"minQuoteAmount","type":"u64","index":false},{"name":"baseAmount","type":"u64","index":false},{"name":"quoteAmount","type":"u64","index":false},{"name":"postAmmState","type":{"defined":"FutarchyAmm"},"index":false}]},{"name":"SponsorProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"dao","type":"publicKey","index":false},{"name":"teamAddress","type":"publicKey","index":false}]},{"name":"RemoveProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"dao","type":"publicKey","index":false},{"name":"admin","type":"publicKey","index":false}]},{"name":"AdminCancelProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"proposal","type":"publicKey","index":false},{"name":"dao","type":"publicKey","index":false},{"name":"admin","type":"publicKey","index":false},{"name":"postAmmState","type":{"defined":"FutarchyAmm"},"index":false}]},{"name":"CollectMeteoraDammFeesEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false},{"name":"baseTokenAccount","type":"publicKey","index":false},{"name":"quoteTokenAccount","type":"publicKey","index":false},{"name":"quoteMint","type":"publicKey","index":false},{"name":"baseMint","type":"publicKey","index":false},{"name":"quoteFeesCollected","type":"u64","index":false},{"name":"baseFeesCollected","type":"u64","index":false}]},{"name":"AdminFixPositionAuthorityEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"admin","type":"publicKey","index":false},{"name":"ammPosition","type":"publicKey","index":false},{"name":"oldAuthority","type":"publicKey","index":false},{"name":"newAuthority","type":"publicKey","index":false}]},{"name":"InitiateVaultSpendOptimisticProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"proposer","type":"publicKey","index":false},{"name":"squadsProposal","type":"publicKey","index":false},{"name":"squadsMultisig","type":"publicKey","index":false},{"name":"squadsMultisigVault","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"recipient","type":"publicKey","index":false},{"name":"daoQuoteVaultAccount","type":"publicKey","index":false},{"name":"recipientQuoteAccount","type":"publicKey","index":false},{"name":"enqueuedTimestamp","type":"i64","index":false}]},{"name":"FinalizeOptimisticProposalEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"dao","type":"publicKey","index":false},{"name":"squadsProposal","type":"publicKey","index":false}]}],"errors":[{"code":6000,"name":"AmmTooOld","msg":"Amms must have been created within 5 minutes (counted in slots) of proposal initialization"},{"code":6001,"name":"InvalidInitialObservation","msg":"An amm has an `initial_observation` that doesn't match the `dao`'s config"},{"code":6002,"name":"InvalidMaxObservationChange","msg":"An amm has a `max_observation_change_per_update` that doesn't match the `dao`'s config"},{"code":6003,"name":"InvalidStartDelaySlots","msg":"An amm has a `start_delay_slots` that doesn't match the `dao`'s config"},{"code":6004,"name":"InvalidSettlementAuthority","msg":"One of the vaults has an invalid `settlement_authority`"},{"code":6005,"name":"ProposalTooYoung","msg":"Proposal is too young to be executed or rejected"},{"code":6006,"name":"MarketsTooYoung","msg":"Markets too young for proposal to be finalized. TWAP might need to be cranked"},{"code":6007,"name":"ProposalAlreadyFinalized","msg":"This proposal has already been finalized"},{"code":6008,"name":"InvalidVaultNonce","msg":"A conditional vault has an invalid nonce. A nonce should encode the proposal number"},{"code":6009,"name":"ProposalNotPassed","msg":"This proposal can't be executed because it isn't in the passed state"},{"code":6010,"name":"InsufficientLiquidity","msg":"More liquidity needs to be in the AMM to launch this proposal"},{"code":6011,"name":"ProposalDurationTooShort","msg":"Proposal duration must be longer 1 day and longer than 2 times the TWAP start delay"},{"code":6012,"name":"PassThresholdTooHigh","msg":"Pass threshold must be less than 10%"},{"code":6013,"name":"QuestionMustBeBinary","msg":"Question must have exactly 2 outcomes for binary futarchy"},{"code":6014,"name":"InvalidSquadsProposalStatus","msg":"Squads proposal must be in Active status"},{"code":6015,"name":"CastingOverflow","msg":"Casting overflow. If you're seeing this, please report this"},{"code":6016,"name":"InsufficientBalance","msg":"Insufficient balance"},{"code":6017,"name":"ZeroLiquidityRemove","msg":"Cannot remove zero liquidity"},{"code":6018,"name":"SwapSlippageExceeded","msg":"Swap slippage exceeded"},{"code":6019,"name":"AssertFailed","msg":"Assert failed"},{"code":6020,"name":"InvalidAdmin","msg":"Invalid admin"},{"code":6021,"name":"ProposalNotInDraftState","msg":"Proposal is not in draft state"},{"code":6022,"name":"InsufficientTokenBalance","msg":"Insufficient token balance"},{"code":6023,"name":"InvalidAmount","msg":"Invalid amount"},{"code":6024,"name":"InsufficientStakeToLaunch","msg":"Insufficient stake to launch proposal"},{"code":6025,"name":"StakerNotFound","msg":"Staker not found in proposal"},{"code":6026,"name":"PoolNotInSpotState","msg":"Pool must be in spot state"},{"code":6027,"name":"InvalidDaoCreateLiquidity","msg":"If you're providing liquidity, you must provide both base and quote token accounts"},{"code":6028,"name":"InvalidStakeAccount","msg":"Invalid stake account"},{"code":6029,"name":"InvariantViolated","msg":"An invariant was violated. You should get in contact with the MetaDAO team if you see this"},{"code":6030,"name":"ProposalNotActive","msg":"Proposal needs to be active to perform a conditional swap"},{"code":6031,"name":"InvalidTransaction","msg":"This Squads transaction should only contain calls to update spending limits"},{"code":6032,"name":"ProposalAlreadySponsored","msg":"Proposal has already been sponsored"},{"code":6033,"name":"InvalidTeamSponsoredPassThreshold","msg":"Team sponsored pass threshold must be between -10% and 10%"},{"code":6034,"name":"InvalidTargetK","msg":"Target K must be greater than the current K"},{"code":6035,"name":"InvalidTransactionMessage","msg":"Failed to compile transaction message for Squads vault transaction"},{"code":6036,"name":"InvalidMint","msg":"Base mint and quote mint must be different"},{"code":6037,"name":"ProposalNotReadyToUnstake","msg":"Proposal is not ready to be unstaked"},{"code":6038,"name":"OptimisticGovernanceDisabled","msg":"Optimistic governance is disabled"},{"code":6039,"name":"ActiveOptimisticProposalAlreadyEnqueued","msg":"An active optimistic proposal is already enqueued"},{"code":6040,"name":"OptimisticProposalAlreadyPassed","msg":"Optimistic proposal has already passed"},{"code":6041,"name":"InvalidSpendingLimitMint","msg":"Invalid spending limit mint. Must be the same as the DAO's quote mint"},{"code":6042,"name":"NoActiveOptimisticProposal","msg":"No active optimistic proposal"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/metadao_launchpad.moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM.json b/migration/khadhroony-bot2-reference/idls/metadao_launchpad.moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM.json new file mode 100644 index 0000000..0bf7c0c --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/metadao_launchpad.moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM.json @@ -0,0 +1 @@ +{"version":"0.7.0","name":"launchpad_v7","instructions":[{"name":"initializeLaunch","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"baseMint","isMut":true,"isSigner":false},{"name":"tokenMetadata","isMut":true,"isSigner":false},{"name":"launchSigner","isMut":false,"isSigner":false},{"name":"quoteVault","isMut":true,"isSigner":false},{"name":"baseVault","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"launchAuthority","isMut":false,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"additionalTokensRecipient","isMut":false,"isSigner":false,"isOptional":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"InitializeLaunchArgs"}}]},{"name":"startLaunch","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"launchAuthority","isMut":false,"isSigner":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"fund","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"fundingRecord","isMut":true,"isSigner":false},{"name":"launchQuoteVault","isMut":true,"isSigner":false},{"name":"funder","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"funderQuoteAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"setFundingRecordApproval","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"fundingRecord","isMut":true,"isSigner":false},{"name":"launchAuthority","isMut":false,"isSigner":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"approvedAmount","type":"u64"}]},{"name":"completeLaunch","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"launchAuthority","isMut":false,"isSigner":true,"isOptional":true},{"name":"tokenMetadata","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"launchSigner","isMut":true,"isSigner":false},{"name":"launchQuoteVault","isMut":true,"isSigner":false},{"name":"launchBaseVault","isMut":true,"isSigner":false},{"name":"treasuryQuoteAccount","isMut":true,"isSigner":false},{"name":"baseMint","isMut":true,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"daoOwnedLpPosition","isMut":true,"isSigner":false},{"name":"futarchyAmmBaseVault","isMut":true,"isSigner":false},{"name":"futarchyAmmQuoteVault","isMut":true,"isSigner":false},{"name":"dao","isMut":true,"isSigner":false},{"name":"squadsMultisig","isMut":true,"isSigner":false},{"name":"squadsMultisigVault","isMut":false,"isSigner":false},{"name":"spendingLimit","isMut":true,"isSigner":false},{"name":"bidWall","isMut":true,"isSigner":false},{"name":"bidWallQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"feeRecipient","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"staticAccounts","accounts":[{"name":"futarchyProgram","isMut":false,"isSigner":false},{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"futarchyEventAuthority","isMut":false,"isSigner":false},{"name":"squadsProgram","isMut":false,"isSigner":false},{"name":"squadsProgramConfig","isMut":false,"isSigner":false},{"name":"squadsProgramConfigTreasury","isMut":true,"isSigner":false},{"name":"bidWallProgram","isMut":false,"isSigner":false},{"name":"bidWallEventAuthority","isMut":false,"isSigner":false}]},{"name":"meteoraAccounts","accounts":[{"name":"dammV2Program","isMut":false,"isSigner":false},{"name":"config","isMut":false,"isSigner":false},{"name":"token2022Program","isMut":false,"isSigner":false},{"name":"positionNftAccount","isMut":true,"isSigner":false},{"name":"pool","isMut":true,"isSigner":false},{"name":"position","isMut":true,"isSigner":false},{"name":"positionNftMint","isMut":true,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"tokenAVault","isMut":true,"isSigner":false},{"name":"tokenBVault","isMut":true,"isSigner":false},{"name":"poolCreatorAuthority","isMut":false,"isSigner":false},{"name":"poolAuthority","isMut":false,"isSigner":false},{"name":"dammV2EventAuthority","isMut":false,"isSigner":false}]},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"refund","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"fundingRecord","isMut":true,"isSigner":false},{"name":"launchQuoteVault","isMut":true,"isSigner":false},{"name":"launchSigner","isMut":false,"isSigner":false},{"name":"funder","isMut":false,"isSigner":false},{"name":"funderQuoteAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"claim","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"fundingRecord","isMut":true,"isSigner":false},{"name":"launchSigner","isMut":false,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"launchBaseVault","isMut":true,"isSigner":false},{"name":"funder","isMut":false,"isSigner":false},{"name":"funderTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"closeLaunch","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"claimAdditionalTokenAllocation","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"launchSigner","isMut":false,"isSigner":false},{"name":"launchBaseVault","isMut":true,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"additionalTokensRecipient","isMut":false,"isSigner":false},{"name":"additionalTokensRecipientTokenAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"initializePerformancePackage","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"launchSigner","isMut":false,"isSigner":false},{"name":"launchBaseVault","isMut":true,"isSigner":false},{"name":"baseMint","isMut":true,"isSigner":false},{"name":"dao","isMut":false,"isSigner":false},{"name":"squadsMultisig","isMut":false,"isSigner":false},{"name":"squadsMultisigVault","isMut":false,"isSigner":false},{"name":"performancePackage","isMut":true,"isSigner":false},{"name":"performancePackageTokenAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"squadsProgram","isMut":false,"isSigner":false},{"name":"priceBasedPerformancePackageProgram","isMut":false,"isSigner":false},{"name":"priceBasedPerformancePackageEventAuthority","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"resizeFundingRecord","accounts":[{"name":"fundingRecord","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"resizeLaunch","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"extendLaunch","accounts":[{"name":"launch","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ExtendLaunchArgs"}}]}],"accounts":[{"name":"FundingRecord","type":{"kind":"struct","fields":[{"name":"pdaBump","docs":["The PDA bump."],"type":"u8"},{"name":"funder","docs":["The funder."],"type":"publicKey"},{"name":"launch","docs":["The launch."],"type":"publicKey"},{"name":"committedAmount","docs":["The amount of USDC that has been committed by the funder."],"type":"u64"},{"name":"isTokensClaimed","docs":["Whether the tokens have been claimed."],"type":"bool"},{"name":"isUsdcRefunded","docs":["Whether the USDC has been refunded."],"type":"bool"},{"name":"approvedAmount","docs":["The amount of USDC that the launch authority has approved for the funder.","If zero, the funder has not been approved for any amount."],"type":"u64"},{"name":"committedAmountAccumulator","docs":["Running integral of committed_amount over time (committed_amount * seconds)."],"type":"u128"},{"name":"lastAccumulatorUpdate","docs":["Unix timestamp of the last accumulator update."],"type":"i64"}]}},{"name":"OldFundingRecord","type":{"kind":"struct","fields":[{"name":"pdaBump","docs":["The PDA bump."],"type":"u8"},{"name":"funder","docs":["The funder."],"type":"publicKey"},{"name":"launch","docs":["The launch."],"type":"publicKey"},{"name":"committedAmount","docs":["The amount of USDC that has been committed by the funder."],"type":"u64"},{"name":"isTokensClaimed","docs":["Whether the tokens have been claimed."],"type":"bool"},{"name":"isUsdcRefunded","docs":["Whether the USDC has been refunded."],"type":"bool"},{"name":"approvedAmount","docs":["The amount of USDC that the launch authority has approved for the funder.","If zero, the funder has not been approved for any amount."],"type":"u64"}]}},{"name":"Launch","type":{"kind":"struct","fields":[{"name":"pdaBump","docs":["The PDA bump."],"type":"u8"},{"name":"minimumRaiseAmount","docs":["The minimum amount of USDC that must be raised, otherwise","everyone can get their USDC back."],"type":"u64"},{"name":"monthlySpendingLimitAmount","docs":["The monthly spending limit the DAO allocates to the team. Must be","less than 1/6th of the minimum raise amount (so 6 months of burn)."],"type":"u64"},{"name":"monthlySpendingLimitMembers","docs":["The wallets that have access to the monthly spending limit."],"type":{"vec":"publicKey"}},{"name":"launchAuthority","docs":["The account that can start the launch."],"type":"publicKey"},{"name":"launchSigner","docs":["The launch signer address. Needed because Raydium pools need a SOL payer and this PDA can't hold SOL."],"type":"publicKey"},{"name":"launchSignerPdaBump","docs":["The PDA bump for the launch signer."],"type":"u8"},{"name":"launchQuoteVault","docs":["The USDC vault that will hold the USDC raised until the launch is over."],"type":"publicKey"},{"name":"launchBaseVault","docs":["The token vault, used to send tokens to Raydium."],"type":"publicKey"},{"name":"baseMint","docs":["The token that will be minted to funders and that will control the DAO."],"type":"publicKey"},{"name":"quoteMint","docs":["The USDC mint."],"type":"publicKey"},{"name":"unixTimestampStarted","docs":["The unix timestamp when the launch was started."],"type":{"option":"i64"}},{"name":"unixTimestampClosed","docs":["The unix timestamp when the launch stopped taking new contributions."],"type":{"option":"i64"}},{"name":"totalCommittedAmount","docs":["The amount of USDC that has been committed by the users."],"type":"u64"},{"name":"state","docs":["The state of the launch."],"type":{"defined":"LaunchState"}},{"name":"seqNum","docs":["The sequence number of this launch. Useful for sorting events."],"type":"u64"},{"name":"secondsForLaunch","docs":["The number of seconds that the launch will be live for."],"type":"u32"},{"name":"dao","docs":["The DAO, if the launch is complete."],"type":{"option":"publicKey"}},{"name":"daoVault","docs":["The DAO treasury that USDC / LP is sent to, if the launch is complete."],"type":{"option":"publicKey"}},{"name":"performancePackageGrantee","docs":["The address that will receive the performance package tokens."],"type":"publicKey"},{"name":"performancePackageTokenAmount","docs":["The amount of tokens to be granted to the performance package grantee."],"type":"u64"},{"name":"monthsUntilInsidersCanUnlock","docs":["The number of months that insiders must wait before unlocking their tokens."],"type":"u8"},{"name":"teamAddress","docs":["The initial address used to sponsor team proposals."],"type":"publicKey"},{"name":"totalApprovedAmount","docs":["The amount of USDC that the launch authority has approved across all funders."],"type":"u64"},{"name":"additionalTokensAmount","docs":["The amount of additional tokens to be minted on a successful launch."],"type":"u64"},{"name":"additionalTokensRecipient","docs":["The token account that will receive the additional tokens."],"type":{"option":"publicKey"}},{"name":"additionalTokensClaimed","docs":["Are the additional tokens claimed"],"type":"bool"},{"name":"unixTimestampCompleted","docs":["The unix timestamp when the launch was completed."],"type":{"option":"i64"}},{"name":"isPerformancePackageInitialized","docs":["Whether the performance package has been initialized."],"type":"bool"},{"name":"accumulatorActivationDelaySeconds","docs":["Number of seconds after launch start before the funding accumulator","begins tracking."],"type":"u32"},{"name":"hasBidWall","docs":["Whether the launch has a bid wall."],"type":"bool"}]}},{"name":"OldLaunch","type":{"kind":"struct","fields":[{"name":"pdaBump","docs":["The PDA bump."],"type":"u8"},{"name":"minimumRaiseAmount","docs":["The minimum amount of USDC that must be raised, otherwise","everyone can get their USDC back."],"type":"u64"},{"name":"monthlySpendingLimitAmount","docs":["The monthly spending limit the DAO allocates to the team. Must be","less than 1/6th of the minimum raise amount (so 6 months of burn)."],"type":"u64"},{"name":"monthlySpendingLimitMembers","docs":["The wallets that have access to the monthly spending limit."],"type":{"vec":"publicKey"}},{"name":"launchAuthority","docs":["The account that can start the launch."],"type":"publicKey"},{"name":"launchSigner","docs":["The launch signer address. Needed because Raydium pools need a SOL payer and this PDA can't hold SOL."],"type":"publicKey"},{"name":"launchSignerPdaBump","docs":["The PDA bump for the launch signer."],"type":"u8"},{"name":"launchQuoteVault","docs":["The USDC vault that will hold the USDC raised until the launch is over."],"type":"publicKey"},{"name":"launchBaseVault","docs":["The token vault, used to send tokens to Raydium."],"type":"publicKey"},{"name":"baseMint","docs":["The token that will be minted to funders and that will control the DAO."],"type":"publicKey"},{"name":"quoteMint","docs":["The USDC mint."],"type":"publicKey"},{"name":"unixTimestampStarted","docs":["The unix timestamp when the launch was started."],"type":{"option":"i64"}},{"name":"unixTimestampClosed","docs":["The unix timestamp when the launch stopped taking new contributions."],"type":{"option":"i64"}},{"name":"totalCommittedAmount","docs":["The amount of USDC that has been committed by the users."],"type":"u64"},{"name":"state","docs":["The state of the launch."],"type":{"defined":"LaunchState"}},{"name":"seqNum","docs":["The sequence number of this launch. Useful for sorting events."],"type":"u64"},{"name":"secondsForLaunch","docs":["The number of seconds that the launch will be live for."],"type":"u32"},{"name":"dao","docs":["The DAO, if the launch is complete."],"type":{"option":"publicKey"}},{"name":"daoVault","docs":["The DAO treasury that USDC / LP is sent to, if the launch is complete."],"type":{"option":"publicKey"}},{"name":"performancePackageGrantee","docs":["The address that will receive the performance package tokens."],"type":"publicKey"},{"name":"performancePackageTokenAmount","docs":["The amount of tokens to be granted to the performance package grantee."],"type":"u64"},{"name":"monthsUntilInsidersCanUnlock","docs":["The number of months that insiders must wait before unlocking their tokens."],"type":"u8"},{"name":"teamAddress","docs":["The initial address used to sponsor team proposals."],"type":"publicKey"},{"name":"totalApprovedAmount","docs":["The amount of USDC that the launch authority has approved across all funders."],"type":"u64"},{"name":"additionalTokensAmount","docs":["The amount of additional tokens to be minted on a successful launch."],"type":"u64"},{"name":"additionalTokensRecipient","docs":["The token account that will receive the additional tokens."],"type":{"option":"publicKey"}},{"name":"additionalTokensClaimed","docs":["Are the additional tokens claimed"],"type":"bool"},{"name":"unixTimestampCompleted","docs":["The unix timestamp when the launch was completed."],"type":{"option":"i64"}},{"name":"isPerformancePackageInitialized","type":"bool"},{"name":"accumulatorActivationDelaySeconds","docs":["Number of seconds after launch start before the funding accumulator","begins tracking."],"type":"u32"}]}}],"types":[{"name":"CommonFields","type":{"kind":"struct","fields":[{"name":"slot","type":"u64"},{"name":"unixTimestamp","type":"i64"},{"name":"launchSeqNum","type":"u64"}]}},{"name":"ExtendLaunchArgs","type":{"kind":"struct","fields":[{"name":"durationSeconds","type":"u32"}]}},{"name":"InitializeLaunchArgs","type":{"kind":"struct","fields":[{"name":"minimumRaiseAmount","type":"u64"},{"name":"monthlySpendingLimitAmount","type":"u64"},{"name":"monthlySpendingLimitMembers","type":{"vec":"publicKey"}},{"name":"secondsForLaunch","type":"u32"},{"name":"tokenName","type":"string"},{"name":"tokenSymbol","type":"string"},{"name":"tokenUri","type":"string"},{"name":"performancePackageGrantee","type":"publicKey"},{"name":"performancePackageTokenAmount","type":"u64"},{"name":"monthsUntilInsidersCanUnlock","type":"u8"},{"name":"teamAddress","type":"publicKey"},{"name":"additionalTokensAmount","type":"u64"},{"name":"accumulatorActivationDelaySeconds","type":"u32"},{"name":"hasBidWall","type":"bool"}]}},{"name":"LaunchState","type":{"kind":"enum","variants":[{"name":"Initialized"},{"name":"Live"},{"name":"Closed"},{"name":"Complete"},{"name":"Refunding"}]}}],"events":[{"name":"LaunchInitializedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"minimumRaiseAmount","type":"u64","index":false},{"name":"launchAuthority","type":"publicKey","index":false},{"name":"launchSigner","type":"publicKey","index":false},{"name":"launchSignerPdaBump","type":"u8","index":false},{"name":"launchUsdcVault","type":"publicKey","index":false},{"name":"launchTokenVault","type":"publicKey","index":false},{"name":"performancePackageGrantee","type":"publicKey","index":false},{"name":"performancePackageTokenAmount","type":"u64","index":false},{"name":"monthsUntilInsidersCanUnlock","type":"u8","index":false},{"name":"monthlySpendingLimitAmount","type":"u64","index":false},{"name":"monthlySpendingLimitMembers","type":{"vec":"publicKey"},"index":false},{"name":"baseMint","type":"publicKey","index":false},{"name":"quoteMint","type":"publicKey","index":false},{"name":"pdaBump","type":"u8","index":false},{"name":"secondsForLaunch","type":"u32","index":false},{"name":"additionalTokensAmount","type":"u64","index":false},{"name":"additionalTokensRecipient","type":{"option":"publicKey"},"index":false},{"name":"accumulatorActivationDelaySeconds","type":"u32","index":false},{"name":"hasBidWall","type":"bool","index":false}]},{"name":"LaunchStartedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"launchAuthority","type":"publicKey","index":false},{"name":"slotStarted","type":"u64","index":false}]},{"name":"LaunchFundedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"fundingRecord","type":"publicKey","index":false},{"name":"launch","type":"publicKey","index":false},{"name":"funder","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"totalCommittedByFunder","type":"u64","index":false},{"name":"totalCommitted","type":"u64","index":false},{"name":"committedAmountAccumulator","type":"u128","index":false}]},{"name":"FundingRecordApprovalSetEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"fundingRecord","type":"publicKey","index":false},{"name":"funder","type":"publicKey","index":false},{"name":"approvedAmount","type":"u64","index":false},{"name":"totalApproved","type":"u64","index":false}]},{"name":"LaunchCompletedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"finalState","type":{"defined":"LaunchState"},"index":false},{"name":"totalCommitted","type":"u64","index":false},{"name":"dao","type":{"option":"publicKey"},"index":false},{"name":"daoTreasury","type":{"option":"publicKey"},"index":false},{"name":"totalApprovedAmount","type":"u64","index":false},{"name":"bidWall","type":{"option":"publicKey"},"index":false},{"name":"bidWallAmount","type":"u64","index":false}]},{"name":"LaunchRefundedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"funder","type":"publicKey","index":false},{"name":"usdcRefunded","type":"u64","index":false},{"name":"fundingRecord","type":"publicKey","index":false}]},{"name":"LaunchClaimEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"funder","type":"publicKey","index":false},{"name":"tokensClaimed","type":"u64","index":false},{"name":"fundingRecord","type":"publicKey","index":false}]},{"name":"LaunchCloseEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"newState","type":{"defined":"LaunchState"},"index":false}]},{"name":"LaunchClaimAdditionalTokenAllocationEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"additionalTokensAmount","type":"u64","index":false},{"name":"additionalTokensRecipient","type":"publicKey","index":false}]},{"name":"LaunchPerformancePackageInitializedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"performancePackage","type":"publicKey","index":false}]},{"name":"LaunchExtendedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"launch","type":"publicKey","index":false},{"name":"oldSecondsForLaunch","type":"u32","index":false},{"name":"newSecondsForLaunch","type":"u32","index":false}]}],"errors":[{"code":6000,"name":"InvalidAmount","msg":"Invalid amount"},{"code":6001,"name":"SupplyNonZero","msg":"Supply must be zero"},{"code":6002,"name":"InvalidSecondsForLaunch","msg":"Launch period must be between 1 hour and 2 weeks"},{"code":6003,"name":"InsufficientFunds","msg":"Insufficient funds"},{"code":6004,"name":"InvalidTokenKey","msg":"Token mint key must end in 'meta'"},{"code":6005,"name":"InvalidLaunchState","msg":"Invalid launch state"},{"code":6006,"name":"LaunchPeriodNotOver","msg":"Launch period not over"},{"code":6007,"name":"LaunchExpired","msg":"Launch is complete, no more funding allowed"},{"code":6008,"name":"LaunchNotRefunding","msg":"For you to get a refund, either the launch needs to be in a refunding state or the launch must have been over-committed"},{"code":6009,"name":"LaunchNotInitialized","msg":"Launch must be initialized to be started"},{"code":6010,"name":"FreezeAuthoritySet","msg":"Freeze authority can't be set on launchpad tokens"},{"code":6011,"name":"InvalidMonthlySpendingLimit","msg":"Monthly spending limit must be less than 1/6th of the minimum raise amount and cannot be 0"},{"code":6012,"name":"InvalidMonthlySpendingLimitMembers","msg":"There can only be at most 10 monthly spending limit members"},{"code":6013,"name":"InvalidPriceBasedPremineAmount","msg":"Cannot do more than a 50% premine, minimum is 10 atoms of token"},{"code":6014,"name":"InvalidPerformancePackageMinUnlockTime","msg":"Insiders must be forced to wait at least 18 months before unlocking their tokens"},{"code":6015,"name":"LaunchAuthorityNotSet","msg":"Launch authority must be set to complete the launch until 2 days after closing"},{"code":6016,"name":"FinalRaiseAmountTooLow","msg":"The final amount raised must be greater than or equal to the minimum raise amount"},{"code":6017,"name":"TokensAlreadyClaimed","msg":"Tokens already claimed"},{"code":6018,"name":"MoneyAlreadyRefunded","msg":"Money already refunded"},{"code":6019,"name":"InvariantViolated","msg":"An invariant was violated. You should get in contact with the MetaDAO team if you see this"},{"code":6020,"name":"LaunchNotLive","msg":"Launch must be live to be closed"},{"code":6021,"name":"InvalidMinimumRaiseAmount","msg":"Minimum raise amount must be greater than or equal to $0.5 so that there's enough liquidity for the launch"},{"code":6022,"name":"FinalRaiseAmountAlreadySet","msg":"The final raise amount has already been set"},{"code":6023,"name":"TotalApprovedAmountTooLow","msg":"Total approved amount must be greater than or equal to the minimum raise amount"},{"code":6024,"name":"InvalidAdditionalTokensRecipient","msg":"Invalid additional tokens recipient - should be set if additional tokens amount is greater than 0"},{"code":6025,"name":"NoAdditionalTokensRecipientSet","msg":"No additional tokens recipient set"},{"code":6026,"name":"AdditionalTokensAlreadyClaimed","msg":"Additional tokens already claimed"},{"code":6027,"name":"FundingRecordApprovalPeriodOver","msg":"Funding record approval period is over"},{"code":6028,"name":"PerformancePackageAlreadyInitialized","msg":"Performance package already initialized"},{"code":6029,"name":"InvalidDao","msg":"Invalid DAO"},{"code":6030,"name":"InvalidAccumulatorActivationDelaySeconds","msg":"Accumulator activation delay must be less than the launch duration"},{"code":6031,"name":"ExtendDurationExceedsMax","msg":"The extend duration would exceed the maximum allowed launch duration"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/metdao_bid_wall.WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx.json b/migration/khadhroony-bot2-reference/idls/metdao_bid_wall.WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx.json new file mode 100644 index 0000000..45aa0a6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/metdao_bid_wall.WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx.json @@ -0,0 +1 @@ +{"version":"0.7.0","name":"bid_wall","instructions":[{"name":"initializeBidWall","accounts":[{"name":"bidWall","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"feeRecipient","isMut":false,"isSigner":false},{"name":"creator","isMut":false,"isSigner":true},{"name":"authority","isMut":false,"isSigner":false},{"name":"bidWallQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"creatorQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"daoTreasury","isMut":false,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"InitializeBidWallArgs"}}]},{"name":"closeBidWall","accounts":[{"name":"bidWall","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":false},{"name":"feeRecipient","isMut":false,"isSigner":false},{"name":"bidWallQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"authorityQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"feeRecipientQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"sellTokens","accounts":[{"name":"bidWall","isMut":true,"isSigner":false},{"name":"user","isMut":false,"isSigner":true},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"userQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"bidWallQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"daoTreasury","isMut":false,"isSigner":false},{"name":"daoTreasuryQuoteTokenAccount","isMut":false,"isSigner":false},{"name":"baseMint","isMut":true,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"SellTokensArgs"}}]},{"name":"collectFees","accounts":[{"name":"bidWall","isMut":true,"isSigner":false},{"name":"cranker","isMut":false,"isSigner":true},{"name":"bidWallQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"feeRecipientQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]},{"name":"cancelBidWall","accounts":[{"name":"bidWall","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"authority","isMut":false,"isSigner":true},{"name":"feeRecipient","isMut":false,"isSigner":false},{"name":"bidWallQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"authorityQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"feeRecipientQuoteTokenAccount","isMut":true,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"BidWall","type":{"kind":"struct","fields":[{"name":"nonce","docs":["The nonce of the bid wall."],"type":"u64"},{"name":"createdTimestamp","docs":["When the bid wall was created."],"type":"i64"},{"name":"initialAmmQuoteReserves","docs":["The initial quote (USDC) reserves of the Futarchy AMM."],"type":"u64"},{"name":"quoteAmount","docs":["The current amount of quote tokens assigned to the bid wall.","This is different from the amount in the bid wall quote token account,","because anyone can transfer quote tokens to the bid wall, and we don't want that to affect the bid wall's NAV calculation."],"type":"u64"},{"name":"feesCollected","docs":["The fees collected by the bid wall."],"type":"u64"},{"name":"baseBoughtAmount","docs":["The amount of base tokens bought up by the bid wall."],"type":"u64"},{"name":"seqNum","docs":["The event sequence number of the bid wall."],"type":"u64"},{"name":"creator","docs":["The authority of the bid wall."],"type":"publicKey"},{"name":"authority","docs":["The authority of the bid wall."],"type":"publicKey"},{"name":"daoTreasury","docs":["The DAO treasury address."],"type":"publicKey"},{"name":"baseMint","docs":["The mint of the token being sold into the bid wall."],"type":"publicKey"},{"name":"feeRecipient","docs":["The recipient of the fees collected by the bid wall."],"type":"publicKey"},{"name":"durationSeconds","docs":["The minimum duration in seconds before the bid wall can be closed."],"type":"u32"},{"name":"pdaBump","docs":["The PDA bump."],"type":"u8"}]}}],"types":[{"name":"CommonFields","type":{"kind":"struct","fields":[{"name":"slot","type":"u64"},{"name":"unixTimestamp","type":"i64"},{"name":"bidWallSeqNum","type":"u64"}]}},{"name":"InitializeBidWallArgs","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"nonce","type":"u64"},{"name":"initialAmmQuoteReserves","type":"u64"},{"name":"durationSeconds","type":"u32"}]}},{"name":"SellTokensArgs","type":{"kind":"struct","fields":[{"name":"amountIn","type":"u64"},{"name":"minAmountOut","type":"u64"}]}}],"events":[{"name":"BidWallInitializedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"bidWall","type":"publicKey","index":false},{"name":"nonce","type":"u64","index":false},{"name":"initialAmmQuoteReserves","type":"u64","index":false},{"name":"amount","type":"u64","index":false},{"name":"creator","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"daoTreasury","type":"publicKey","index":false},{"name":"baseMint","type":"publicKey","index":false},{"name":"feeRecipient","type":"publicKey","index":false},{"name":"durationSeconds","type":"u32","index":false},{"name":"pdaBump","type":"u8","index":false}]},{"name":"BidWallTokensSoldEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"bidWall","type":"publicKey","index":false},{"name":"amountIn","type":"u64","index":false},{"name":"amountOut","type":"u64","index":false},{"name":"fee","type":"u64","index":false},{"name":"postBidWallQuoteTokenAccountAmount","type":"u64","index":false},{"name":"postBidWallBaseBoughtAmount","type":"u64","index":false},{"name":"user","type":"publicKey","index":false}]},{"name":"BidWallFeesCollectedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"bidWall","type":"publicKey","index":false},{"name":"feesCollected","type":"u64","index":false},{"name":"postBidWallQuoteTokenAccountAmount","type":"u64","index":false}]},{"name":"BidWallClosedEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"bidWall","type":"publicKey","index":false}]},{"name":"BidWallCanceledEvent","fields":[{"name":"common","type":{"defined":"CommonFields"},"index":false},{"name":"bidWall","type":"publicKey","index":false}]}],"errors":[{"code":6000,"name":"BidWallExpired","msg":"Bid wall expired"},{"code":6001,"name":"BidWallNotExpired","msg":"Bid wall not expired"},{"code":6002,"name":"FeeRecipientMismatch","msg":"Fee recipient mismatch"},{"code":6003,"name":"InsufficientQuoteReserves","msg":"Insufficient quote reserves"},{"code":6004,"name":"BidWallDepleted","msg":"Bid wall depleted"},{"code":6005,"name":"InvalidInputAmount","msg":"Invalid input amount"},{"code":6006,"name":"InvalidCrankAddress","msg":"Invalid crank address"},{"code":6007,"name":"InsufficientOutputAmount","msg":"Insufficient output amount"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/meteora_damm_v2.cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG.json b/migration/khadhroony-bot2-reference/idls/meteora_damm_v2.cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG.json new file mode 100644 index 0000000..cef0305 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/meteora_damm_v2.cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG.json @@ -0,0 +1 @@ +{"address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG","metadata":{"name":"cp_amm","version":"0.2.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"add_liquidity","discriminator":[181,157,89,67,143,182,52,72],"accounts":[{"name":"pool","writable":true,"relations":["position"]},{"name":"position","writable":true},{"name":"token_a_account","docs":["The user token a account"],"writable":true},{"name":"token_b_account","docs":["The user token b account"],"writable":true},{"name":"token_a_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"token_b_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"token_a_mint","docs":["The mint of token a"],"relations":["pool"]},{"name":"token_b_mint","docs":["The mint of token b"],"relations":["pool"]},{"name":"position_nft_account","docs":["The token account for nft"]},{"name":"owner","docs":["owner of position"],"signer":true},{"name":"token_a_program","docs":["Token a program"]},{"name":"token_b_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"AddLiquidityParameters"}}}]},{"name":"claim_position_fee","discriminator":[180,38,154,17,133,33,162,211],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","relations":["position"]},{"name":"position","writable":true},{"name":"token_a_account","docs":["The user token a account"],"writable":true},{"name":"token_b_account","docs":["The user token b account"],"writable":true},{"name":"token_a_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"token_b_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"token_a_mint","docs":["The mint of token a"],"relations":["pool"]},{"name":"token_b_mint","docs":["The mint of token b"],"relations":["pool"]},{"name":"position_nft_account","docs":["The token account for nft"]},{"name":"owner","docs":["owner of position"],"signer":true},{"name":"token_a_program","docs":["Token a program"]},{"name":"token_b_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"claim_protocol_fee","discriminator":[165,228,133,48,99,249,255,33],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","writable":true},{"name":"token_a_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"token_b_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"token_a_mint","docs":["The mint of token a"],"relations":["pool"]},{"name":"token_b_mint","docs":["The mint of token b"],"relations":["pool"]},{"name":"token_a_account","writable":true},{"name":"token_b_account","writable":true},{"name":"operator","docs":["Claim fee operator"]},{"name":"signer","docs":["Operator"],"signer":true},{"name":"token_a_program","docs":["Token a program"]},{"name":"token_b_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"max_amount_a","type":"u64"},{"name":"max_amount_b","type":"u64"}]},{"name":"claim_reward","discriminator":[149,95,181,242,94,90,158,162],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","writable":true,"relations":["position"]},{"name":"position","writable":true},{"name":"reward_vault","docs":["The vault token account for reward token"],"writable":true},{"name":"reward_mint"},{"name":"user_token_account","writable":true},{"name":"position_nft_account","docs":["The token account for nft"]},{"name":"owner","docs":["owner of position"],"signer":true},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u8"},{"name":"skip_reward","type":"u8"}]},{"name":"close_config","discriminator":[145,9,72,157,95,125,61,85],"accounts":[{"name":"config","writable":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"rent_receiver","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"close_operator_account","discriminator":[171,9,213,74,120,23,3,29],"accounts":[{"name":"operator","writable":true},{"name":"signer","signer":true},{"name":"rent_receiver","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"close_position","discriminator":[123,134,81,0,49,68,98,98],"accounts":[{"name":"position_nft_mint","docs":["position_nft_mint"],"writable":true},{"name":"position_nft_account","docs":["The token account for nft"],"writable":true},{"name":"pool","writable":true,"relations":["position"]},{"name":"position","writable":true},{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"rent_receiver","writable":true},{"name":"owner","docs":["Owner of position"],"signer":true},{"name":"token_program","docs":["Program to create NFT mint/token account and transfer for token22 account"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"close_token_badge","discriminator":[108,146,86,110,179,254,10,104],"accounts":[{"name":"token_badge","writable":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"rent_receiver","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"create_config","docs":["OPERATOR FUNCTIONS /////"],"discriminator":[201,207,243,114,75,111,47,189],"accounts":[{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]},{"kind":"arg","path":"index"}]}},{"name":"operator"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"index","type":"u64"},{"name":"config_parameters","type":{"defined":{"name":"StaticConfigParameters"}}}]},{"name":"create_dynamic_config","discriminator":[81,251,122,78,66,57,208,82],"accounts":[{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]},{"kind":"arg","path":"index"}]}},{"name":"operator"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"index","type":"u64"},{"name":"config_parameters","type":{"defined":{"name":"DynamicConfigParameters"}}}]},{"name":"create_operator_account","docs":["ADMIN FUNCTIONS /////"],"discriminator":[221,64,246,149,240,153,229,163],"accounts":[{"name":"operator","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,111,114]},{"kind":"account","path":"whitelisted_address"}]}},{"name":"whitelisted_address"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"permission","type":"u128"}]},{"name":"create_position","discriminator":[48,215,197,153,96,203,180,133],"accounts":[{"name":"owner"},{"name":"position_nft_mint","docs":["position_nft_mint"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["position nft account"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110,95,110,102,116,95,97,99,99,111,117,110,116]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"pool","writable":true},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"payer","docs":["Address paying to create the position. Can be anyone"],"writable":true,"signer":true},{"name":"token_program","docs":["Program to create NFT mint/token account and transfer for token22 account"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"create_token_badge","discriminator":[88,206,0,91,60,175,151,118],"accounts":[{"name":"token_badge","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,98,97,100,103,101]},{"kind":"account","path":"token_mint"}]}},{"name":"token_mint"},{"name":"operator"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"dummy_ix","discriminator":[234,95,176,185,7,42,35,159],"accounts":[{"name":"pod_aligned_fee_time_scheduler"},{"name":"pod_aligned_fee_rate_limiter"},{"name":"pod_aligned_fee_market_cap_scheduler"}],"args":[{"name":"_ixs","type":{"defined":{"name":"DummyParams"}}}]},{"name":"fix_config_fee_params","discriminator":[38,30,216,81,250,177,243,254],"accounts":[{"name":"config","writable":true},{"name":"operator"},{"name":"signer","signer":true}],"args":[{"name":"params","type":{"defined":{"name":"BaseFeeParameters"}}}]},{"name":"fix_pool_fee_params","discriminator":[132,98,81,196,44,58,120,193],"accounts":[{"name":"pool","writable":true},{"name":"operator"},{"name":"signer","signer":true}],"args":[{"name":"params","type":{"defined":{"name":"BaseFeeParameters"}}}]},{"name":"fix_pool_layout_version","discriminator":[166,158,69,35,81,167,200,215],"accounts":[{"name":"pool","writable":true},{"name":"operator"},{"name":"signer","signer":true}],"args":[]},{"name":"fund_reward","discriminator":[188,50,249,165,93,151,38,63],"accounts":[{"name":"pool","writable":true},{"name":"reward_vault","writable":true},{"name":"reward_mint"},{"name":"funder_token_account","writable":true},{"name":"funder","signer":true},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u8"},{"name":"amount","type":"u64"},{"name":"carry_forward","type":"bool"}]},{"name":"initialize_customizable_pool","discriminator":[20,161,241,24,189,221,180,2],"accounts":[{"name":"creator"},{"name":"position_nft_mint","docs":["position_nft_mint"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["position nft account"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110,95,110,102,116,95,97,99,99,111,117,110,116]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"payer","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","docs":["Initialize an account to store the pool state"],"writable":true},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_a_mint","docs":["Token a mint"]},{"name":"token_b_mint","docs":["Token b mint"]},{"name":"token_a_vault","docs":["Token a vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"token_a_mint"},{"kind":"account","path":"pool"}]}},{"name":"token_b_vault","docs":["Token b vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"token_b_mint"},{"kind":"account","path":"pool"}]}},{"name":"payer_token_a","docs":["payer token a account"],"writable":true},{"name":"payer_token_b","docs":["creator token b account"],"writable":true},{"name":"token_a_program","docs":["Program to create mint account and mint tokens"]},{"name":"token_b_program","docs":["Program to create mint account and mint tokens"]},{"name":"token_2022_program","docs":["Program to create NFT mint/token account and transfer for token22 account"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"InitializeCustomizablePoolParameters"}}}]},{"name":"initialize_pool","docs":["USER FUNCTIONS ////"],"discriminator":[95,180,10,172,84,174,232,40],"accounts":[{"name":"creator"},{"name":"position_nft_mint","docs":["position_nft_mint"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["position nft account"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110,95,110,102,116,95,97,99,99,111,117,110,116]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"payer","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"config","docs":["Which config the pool belongs to."]},{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","docs":["Initialize an account to store the pool state"],"writable":true},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_a_mint","docs":["Token a mint"]},{"name":"token_b_mint","docs":["Token b mint"]},{"name":"token_a_vault","docs":["Token a vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"token_a_mint"},{"kind":"account","path":"pool"}]}},{"name":"token_b_vault","docs":["Token b vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"token_b_mint"},{"kind":"account","path":"pool"}]}},{"name":"payer_token_a","docs":["payer token a account"],"writable":true},{"name":"payer_token_b","docs":["creator token b account"],"writable":true},{"name":"token_a_program","docs":["Program to create mint account and mint tokens"]},{"name":"token_b_program","docs":["Program to create mint account and mint tokens"]},{"name":"token_2022_program","docs":["Program to create NFT mint/token account and transfer for token22 account"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"InitializePoolParameters"}}}]},{"name":"initialize_pool_with_dynamic_config","discriminator":[149,82,72,197,253,252,68,15],"accounts":[{"name":"creator"},{"name":"position_nft_mint","docs":["position_nft_mint"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["position nft account"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110,95,110,102,116,95,97,99,99,111,117,110,116]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"payer","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"pool_creator_authority","signer":true,"relations":["config"]},{"name":"config","docs":["Which config the pool belongs to."]},{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","docs":["Initialize an account to store the pool state"],"writable":true},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_a_mint","docs":["Token a mint"]},{"name":"token_b_mint","docs":["Token b mint"]},{"name":"token_a_vault","docs":["Token a vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"token_a_mint"},{"kind":"account","path":"pool"}]}},{"name":"token_b_vault","docs":["Token b vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"token_b_mint"},{"kind":"account","path":"pool"}]}},{"name":"payer_token_a","docs":["payer token a account"],"writable":true},{"name":"payer_token_b","docs":["creator token b account"],"writable":true},{"name":"token_a_program","docs":["Program to create mint account and mint tokens"]},{"name":"token_b_program","docs":["Program to create mint account and mint tokens"]},{"name":"token_2022_program","docs":["Program to create NFT mint/token account and transfer for token22 account"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"InitializeCustomizablePoolParameters"}}}]},{"name":"initialize_reward","discriminator":[95,135,192,196,242,129,230,68],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","writable":true},{"name":"reward_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[114,101,119,97,114,100,95,118,97,117,108,116]},{"kind":"account","path":"pool"},{"kind":"arg","path":"reward_index"}]}},{"name":"reward_mint"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u8"},{"name":"reward_duration","type":"u64"},{"name":"funder","type":"pubkey"}]},{"name":"lock_inner_position","discriminator":[72,19,49,204,18,122,23,90],"accounts":[{"name":"pool","relations":["position"]},{"name":"position","writable":true},{"name":"position_nft_account","docs":["The token account for nft"]},{"name":"owner","docs":["owner of position"],"signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"VestingParameters"}}}]},{"name":"lock_position","discriminator":[227,62,2,252,247,10,171,185],"accounts":[{"name":"pool","relations":["position"]},{"name":"position","writable":true},{"name":"vesting","writable":true,"signer":true},{"name":"position_nft_account","docs":["The token account for nft"]},{"name":"owner","docs":["owner of position"],"signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"VestingParameters"}}}]},{"name":"permanent_lock_position","discriminator":[165,176,125,6,231,171,186,213],"accounts":[{"name":"pool","writable":true,"relations":["position"]},{"name":"position","writable":true},{"name":"position_nft_account","docs":["The token account for nft"]},{"name":"owner","docs":["owner of position"],"signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"permanent_lock_liquidity","type":"u128"}]},{"name":"refresh_vesting","discriminator":[9,94,216,14,116,204,247,0],"accounts":[{"name":"pool","relations":["position"]},{"name":"position","writable":true},{"name":"position_nft_account","docs":["The token account for nft"]},{"name":"owner"}],"args":[]},{"name":"remove_all_liquidity","discriminator":[10,51,61,35,112,105,24,85],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","writable":true,"relations":["position"]},{"name":"position","writable":true},{"name":"token_a_account","docs":["The user token a account"],"writable":true},{"name":"token_b_account","docs":["The user token b account"],"writable":true},{"name":"token_a_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"token_b_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"token_a_mint","docs":["The mint of token a"],"relations":["pool"]},{"name":"token_b_mint","docs":["The mint of token b"],"relations":["pool"]},{"name":"position_nft_account","docs":["The token account for nft"]},{"name":"owner","docs":["owner of position"],"signer":true},{"name":"token_a_program","docs":["Token a program"]},{"name":"token_b_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"token_a_amount_threshold","type":"u64"},{"name":"token_b_amount_threshold","type":"u64"}]},{"name":"remove_liquidity","discriminator":[80,85,209,72,24,206,177,108],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","writable":true,"relations":["position"]},{"name":"position","writable":true},{"name":"token_a_account","docs":["The user token a account"],"writable":true},{"name":"token_b_account","docs":["The user token b account"],"writable":true},{"name":"token_a_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"token_b_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"token_a_mint","docs":["The mint of token a"],"relations":["pool"]},{"name":"token_b_mint","docs":["The mint of token b"],"relations":["pool"]},{"name":"position_nft_account","docs":["The token account for nft"]},{"name":"owner","docs":["owner of position"],"signer":true},{"name":"token_a_program","docs":["Token a program"]},{"name":"token_b_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"RemoveLiquidityParameters"}}}]},{"name":"set_pool_status","discriminator":[112,87,135,223,83,204,132,53],"accounts":[{"name":"pool","writable":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"status","type":"u8"}]},{"name":"split_position","discriminator":[172,241,221,138,161,29,253,42],"accounts":[{"name":"pool","writable":true,"relations":["first_position","second_position"]},{"name":"first_position","docs":["The first position"],"writable":true},{"name":"first_position_nft_account","docs":["The token account for position nft"]},{"name":"second_position","docs":["The second position"],"writable":true},{"name":"second_position_nft_account","docs":["The token account for position nft"]},{"name":"first_owner","docs":["Owner of first position"],"signer":true},{"name":"second_owner","docs":["Owner of second position"],"signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SplitPositionParameters"}}}]},{"name":"split_position2","discriminator":[221,147,228,207,140,212,17,119],"accounts":[{"name":"pool","writable":true,"relations":["first_position","second_position"]},{"name":"first_position","docs":["The first position"],"writable":true},{"name":"first_position_nft_account","docs":["The token account for position nft"]},{"name":"second_position","docs":["The second position"],"writable":true},{"name":"second_position_nft_account","docs":["The token account for position nft"]},{"name":"first_owner","docs":["Owner of first position"],"signer":true},{"name":"second_owner","docs":["Owner of second position"],"signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"numerator","type":"u32"}]},{"name":"swap","discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","docs":["Pool account"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"token_a_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"token_b_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"token_a_mint","docs":["The mint of token a"]},{"name":"token_b_mint","docs":["The mint of token b"]},{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"token_a_program","docs":["Token a program"]},{"name":"token_b_program","docs":["Token b program"]},{"name":"referral_token_account","docs":["referral token account"],"writable":true,"optional":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"_params","type":{"defined":{"name":"SwapParameters"}}}]},{"name":"swap2","discriminator":[65,75,63,76,235,91,91,136],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","docs":["Pool account"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"token_a_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"token_b_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"token_a_mint","docs":["The mint of token a"]},{"name":"token_b_mint","docs":["The mint of token b"]},{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"token_a_program","docs":["Token a program"]},{"name":"token_b_program","docs":["Token b program"]},{"name":"referral_token_account","docs":["referral token account"],"writable":true,"optional":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"_params","type":{"defined":{"name":"SwapParameters2"}}}]},{"name":"update_pool_fees","discriminator":[118,217,203,179,60,8,70,89],"accounts":[{"name":"pool","writable":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"UpdatePoolFeesParameters"}}}]},{"name":"update_reward_duration","discriminator":[138,174,196,169,213,235,254,107],"accounts":[{"name":"pool","writable":true},{"name":"signer","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u8"},{"name":"new_duration","type":"u64"}]},{"name":"update_reward_funder","discriminator":[211,28,48,32,215,160,35,23],"accounts":[{"name":"pool","writable":true},{"name":"signer","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u8"},{"name":"new_funder","type":"pubkey"}]},{"name":"withdraw_ineligible_reward","discriminator":[148,206,42,195,247,49,103,8],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","writable":true},{"name":"reward_vault","writable":true},{"name":"reward_mint"},{"name":"funder_token_account","writable":true},{"name":"funder","signer":true},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"zap_protocol_fee","discriminator":[213,155,187,34,56,182,91,240],"accounts":[{"name":"pool_authority","address":"HLnpSz9h2S4hiLQ43rnSD9XkcUThA7B8hQMKmDaiTLcC"},{"name":"pool","writable":true},{"name":"token_vault","writable":true},{"name":"token_mint"},{"name":"receiver_token","writable":true},{"name":"operator","docs":["zap claim fee operator"]},{"name":"signer","docs":["Operator"],"signer":true},{"name":"token_program","docs":["Token program"]},{"name":"sysvar_instructions","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[{"name":"max_amount","type":"u64"}]}],"accounts":[{"name":"Config","discriminator":[155,12,170,224,30,250,204,130]},{"name":"Operator","discriminator":[219,31,188,145,69,139,204,117]},{"name":"PodAlignedFeeMarketCapScheduler","discriminator":[251,130,208,253,245,27,145,203]},{"name":"PodAlignedFeeRateLimiter","discriminator":[160,219,8,251,179,7,16,117]},{"name":"PodAlignedFeeTimeScheduler","discriminator":[239,132,138,213,67,154,130,70]},{"name":"Pool","discriminator":[241,154,109,4,17,177,109,188]},{"name":"Position","discriminator":[170,188,143,228,122,64,247,208]},{"name":"TokenBadge","discriminator":[116,219,204,229,249,116,255,150]},{"name":"Vesting","discriminator":[100,149,66,138,95,200,128,241]}],"events":[{"name":"EvtClaimPositionFee","discriminator":[198,182,183,52,97,12,49,56]},{"name":"EvtClaimProtocolFee","discriminator":[186,244,75,251,188,13,25,33]},{"name":"EvtClaimReward","discriminator":[218,86,147,200,235,188,215,231]},{"name":"EvtCloseConfig","discriminator":[36,30,239,45,58,132,14,5]},{"name":"EvtClosePosition","discriminator":[20,145,144,68,143,142,214,178]},{"name":"EvtCreateConfig","discriminator":[131,207,180,174,180,73,165,54]},{"name":"EvtCreateDynamicConfig","discriminator":[231,197,13,164,248,213,133,152]},{"name":"EvtCreatePosition","discriminator":[156,15,119,198,29,181,221,55]},{"name":"EvtCreateTokenBadge","discriminator":[141,120,134,116,34,28,114,160]},{"name":"EvtFundReward","discriminator":[104,233,237,122,199,191,121,85]},{"name":"EvtInitializePool","discriminator":[228,50,246,85,203,66,134,37]},{"name":"EvtInitializeReward","discriminator":[129,91,188,3,246,52,185,249]},{"name":"EvtLiquidityChange","discriminator":[197,171,78,127,224,211,87,13]},{"name":"EvtLockPosition","discriminator":[168,63,108,83,219,82,2,200]},{"name":"EvtPermanentLockPosition","discriminator":[145,143,162,218,218,80,67,11]},{"name":"EvtSetPoolStatus","discriminator":[100,213,74,3,95,91,228,146]},{"name":"EvtSplitPosition2","discriminator":[165,32,203,174,72,100,233,103]},{"name":"EvtSplitPosition3","discriminator":[232,117,190,218,85,162,207,78]},{"name":"EvtSwap2","discriminator":[189,66,51,168,38,80,117,153]},{"name":"EvtUpdatePoolFees","discriminator":[76,165,246,102,102,217,156,44]},{"name":"EvtUpdateRewardDuration","discriminator":[149,135,65,231,129,153,65,57]},{"name":"EvtUpdateRewardFunder","discriminator":[76,154,208,13,40,115,246,146]},{"name":"EvtWithdrawIneligibleReward","discriminator":[248,215,184,78,31,180,179,168]}],"errors":[{"code":6000,"name":"MathOverflow","msg":"Math operation overflow"},{"code":6001,"name":"InvalidFee","msg":"Invalid fee setup"},{"code":6002,"name":"ExceededSlippage","msg":"Exceeded slippage tolerance"},{"code":6003,"name":"PoolDisabled","msg":"Pool disabled"},{"code":6004,"name":"ExceedMaxFeeBps","msg":"Exceeded max fee bps"},{"code":6005,"name":"InvalidAdmin","msg":"Invalid admin"},{"code":6006,"name":"AmountIsZero","msg":"Amount is zero"},{"code":6007,"name":"TypeCastFailed","msg":"Type cast error"},{"code":6008,"name":"UnableToModifyActivationPoint","msg":"Unable to modify activation point"},{"code":6009,"name":"InvalidAuthorityToCreateThePool","msg":"Invalid authority to create the pool"},{"code":6010,"name":"InvalidActivationType","msg":"Invalid activation type"},{"code":6011,"name":"InvalidActivationPoint","msg":"Invalid activation point"},{"code":6012,"name":"InvalidQuoteMint","msg":"Quote token must be SOL,USDC"},{"code":6013,"name":"InvalidFeeCurve","msg":"Invalid fee curve"},{"code":6014,"name":"InvalidPriceRange","msg":"Invalid Price Range"},{"code":6015,"name":"PriceRangeViolation","msg":"Trade is over price range"},{"code":6016,"name":"InvalidParameters","msg":"Invalid parameters"},{"code":6017,"name":"InvalidCollectFeeMode","msg":"Invalid collect fee mode"},{"code":6018,"name":"InvalidInput","msg":"Invalid input"},{"code":6019,"name":"CannotCreateTokenBadgeOnSupportedMint","msg":"Cannot create token badge on supported mint"},{"code":6020,"name":"InvalidTokenBadge","msg":"Invalid token badge"},{"code":6021,"name":"InvalidMinimumLiquidity","msg":"Invalid minimum liquidity"},{"code":6022,"name":"InvalidVestingInfo","msg":"Invalid vesting information"},{"code":6023,"name":"InsufficientLiquidity","msg":"Insufficient liquidity"},{"code":6024,"name":"InvalidVestingAccount","msg":"Invalid vesting account"},{"code":6025,"name":"InvalidPoolStatus","msg":"Invalid pool status"},{"code":6026,"name":"UnsupportNativeMintToken2022","msg":"Unsupported native mint token2022"},{"code":6027,"name":"InvalidRewardIndex","msg":"Invalid reward index"},{"code":6028,"name":"InvalidRewardDuration","msg":"Invalid reward duration"},{"code":6029,"name":"RewardInitialized","msg":"Reward already initialized"},{"code":6030,"name":"RewardUninitialized","msg":"Reward not initialized"},{"code":6031,"name":"InvalidRewardVault","msg":"Invalid reward vault"},{"code":6032,"name":"MustWithdrawnIneligibleReward","msg":"Must withdraw ineligible reward"},{"code":6033,"name":"IdenticalRewardDuration","msg":"Reward duration is the same"},{"code":6034,"name":"RewardCampaignInProgress","msg":"Reward campaign in progress"},{"code":6035,"name":"IdenticalFunder","msg":"Identical funder"},{"code":6036,"name":"InvalidFunder","msg":"Invalid funder"},{"code":6037,"name":"RewardNotEnded","msg":"Reward not ended"},{"code":6038,"name":"FeeInverseIsIncorrect","msg":"Fee inverse is incorrect"},{"code":6039,"name":"PositionIsNotEmpty","msg":"Position is not empty"},{"code":6040,"name":"InvalidPoolCreatorAuthority","msg":"Invalid pool creator authority"},{"code":6041,"name":"InvalidConfigType","msg":"Invalid config type"},{"code":6042,"name":"InvalidPoolCreator","msg":"Invalid pool creator"},{"code":6043,"name":"RewardVaultFrozenSkipRequired","msg":"Reward vault is frozen, must skip reward to proceed"},{"code":6044,"name":"InvalidSplitPositionParameters","msg":"Invalid parameters for split position"},{"code":6045,"name":"UnsupportPositionHasVestingLock","msg":"Unsupported split position has vesting lock"},{"code":6046,"name":"SamePosition","msg":"Same position"},{"code":6047,"name":"InvalidBaseFeeMode","msg":"Invalid base fee mode"},{"code":6048,"name":"InvalidFeeRateLimiter","msg":"Invalid fee rate limiter"},{"code":6049,"name":"FailToValidateSingleSwapInstruction","msg":"Fail to validate single swap instruction in rate limiter"},{"code":6050,"name":"InvalidFeeTimeScheduler","msg":"Invalid fee scheduler"},{"code":6051,"name":"UndeterminedError","msg":"Undetermined error"},{"code":6052,"name":"InvalidPoolVersion","msg":"Invalid pool version"},{"code":6053,"name":"InvalidAuthority","msg":"Invalid authority to do that action"},{"code":6054,"name":"InvalidPermission","msg":"Invalid permission"},{"code":6055,"name":"InvalidFeeMarketCapScheduler","msg":"Invalid fee market cap scheduler"},{"code":6056,"name":"CannotUpdateBaseFee","msg":"Cannot update base fee"},{"code":6057,"name":"InvalidDynamicFeeParameters","msg":"Invalid dynamic fee parameters"},{"code":6058,"name":"InvalidUpdatePoolFeesParameters","msg":"Invalid update pool fees parameters"},{"code":6059,"name":"MissingOperatorAccount","msg":"Missing operator account"},{"code":6060,"name":"IncorrectATA","msg":"Incorrect ATA"},{"code":6061,"name":"InvalidZapOutParameters","msg":"Invalid zap out parameters"},{"code":6062,"name":"InvalidWithdrawProtocolFeeZapAccounts","msg":"Invalid withdraw protocol fee zap accounts"},{"code":6063,"name":"MintRestrictedFromZap","msg":"SOL,USDC protocol fee cannot be withdrawn via zap"},{"code":6064,"name":"CpiDisabled","msg":"CPI disabled"},{"code":6065,"name":"MissingZapOutInstruction","msg":"Missing zap out instruction"},{"code":6066,"name":"InvalidZapAccounts","msg":"Invalid zap accounts"},{"code":6067,"name":"InvalidCompoundingFeeBps","msg":"Invalid compounding fee bps"}],"types":[{"name":"AddLiquidityParameters","type":{"kind":"struct","fields":[{"name":"liquidity_delta","docs":["delta liquidity"],"type":"u128"},{"name":"token_a_amount_threshold","docs":["maximum token a amount"],"type":"u64"},{"name":"token_b_amount_threshold","docs":["maximum token b amount"],"type":"u64"}]}},{"name":"BaseFeeInfo","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"data","type":{"array":["u8",32]}}]}},{"name":"BaseFeeParameters","type":{"kind":"struct","fields":[{"name":"data","type":{"array":["u8",27]}}]}},{"name":"BaseFeeStruct","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"base_fee_info","type":{"defined":{"name":"BaseFeeInfo"}}},{"name":"padding_1","type":"u64"}]}},{"name":"BorshFeeMarketCapScheduler","type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","type":"u64"},{"name":"number_of_period","type":"u16"},{"name":"sqrt_price_step_bps","type":"u32"},{"name":"scheduler_expiration_duration","type":"u32"},{"name":"reduction_factor","type":"u64"},{"name":"base_fee_mode","type":"u8"}]}},{"name":"BorshFeeRateLimiter","docs":["we denote reference_amount = x0, cliff_fee_numerator = c, fee_increment = i","if input_amount <= x0, then fee = input_amount * c","","if input_amount > x0, then input_amount = x0 + (a * x0 + b)","if a < max_index","then fee = x0 * c + x0 * (c + i) + .... + x0 * (c + i*a) + b * (c + i * (a+1))","then fee = x0 * (c + c*a + i*a*(a+1)/2) + b * (c + i * (a+1))","","if a >= max_index","if a = max_index + d, input_amount = x0 + max_index * x0 + (d * x0 + b)","then fee = x0 * (c + c*max_index + i*max_index*(max_index+1)/2) + (d * x0 + b) * MAX_FEE"],"type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","type":"u64"},{"name":"fee_increment_bps","type":"u16"},{"name":"max_limiter_duration","type":"u32"},{"name":"max_fee_bps","type":"u32"},{"name":"reference_amount","type":"u64"},{"name":"base_fee_mode","type":"u8"}]}},{"name":"BorshFeeTimeScheduler","type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","type":"u64"},{"name":"number_of_period","type":"u16"},{"name":"period_frequency","type":"u64"},{"name":"reduction_factor","type":"u64"},{"name":"base_fee_mode","type":"u8"}]}},{"name":"Config","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"vault_config_key","docs":["Vault config key"],"type":"pubkey"},{"name":"pool_creator_authority","docs":["Only pool_creator_authority can use the current config to initialize new pool. When it's Pubkey::default, it's a public config."],"type":"pubkey"},{"name":"pool_fees","docs":["Pool fee"],"type":{"defined":{"name":"PoolFeesConfig"}}},{"name":"activation_type","docs":["Activation type"],"type":"u8"},{"name":"collect_fee_mode","docs":["Collect fee mode"],"type":"u8"},{"name":"config_type","docs":["Config type mode, 0 for static, 1 for dynamic"],"type":"u8"},{"name":"_padding_0","docs":["padding 0"],"type":{"array":["u8",5]}},{"name":"index","docs":["config index"],"type":"u64"},{"name":"sqrt_min_price","docs":["sqrt min price"],"type":"u128"},{"name":"sqrt_max_price","docs":["sqrt max price"],"type":"u128"},{"name":"_padding_1","docs":["Fee curve point","Padding for further use"],"type":{"array":["u64",10]}}]}},{"name":"DummyParams","type":{"kind":"struct","fields":[{"name":"borsh_fee_time_scheduler_params","type":{"defined":{"name":"BorshFeeTimeScheduler"}}},{"name":"borsh_fee_rate_limiter_params","type":{"defined":{"name":"BorshFeeRateLimiter"}}},{"name":"borsh_fee_market_cap_scheduler_params","type":{"defined":{"name":"BorshFeeMarketCapScheduler"}}}]}},{"name":"DynamicConfigParameters","type":{"kind":"struct","fields":[{"name":"pool_creator_authority","type":"pubkey"}]}},{"name":"DynamicFeeConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"initialized","type":"u8"},{"name":"padding","type":{"array":["u8",7]}},{"name":"max_volatility_accumulator","type":"u32"},{"name":"variable_fee_control","type":"u32"},{"name":"bin_step","type":"u16"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"padding_1","type":{"array":["u8",8]}},{"name":"bin_step_u128","type":"u128"}]}},{"name":"DynamicFeeParameters","type":{"kind":"struct","fields":[{"name":"bin_step","type":"u16"},{"name":"bin_step_u128","type":"u128"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"max_volatility_accumulator","type":"u32"},{"name":"variable_fee_control","type":"u32"}]}},{"name":"DynamicFeeStruct","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"initialized","type":"u8"},{"name":"padding","type":{"array":["u8",7]}},{"name":"max_volatility_accumulator","type":"u32"},{"name":"variable_fee_control","type":"u32"},{"name":"bin_step","type":"u16"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"last_update_timestamp","type":"u64"},{"name":"bin_step_u128","type":"u128"},{"name":"sqrt_price_reference","type":"u128"},{"name":"volatility_accumulator","type":"u128"},{"name":"volatility_reference","type":"u128"}]}},{"name":"EvtClaimPositionFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"fee_a_claimed","type":"u64"},{"name":"fee_b_claimed","type":"u64"}]}},{"name":"EvtClaimProtocolFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"token_a_amount","type":"u64"},{"name":"token_b_amount","type":"u64"}]}},{"name":"EvtClaimReward","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"mint_reward","type":"pubkey"},{"name":"reward_index","type":"u8"},{"name":"total_reward","type":"u64"}]}},{"name":"EvtCloseConfig","docs":["Close config"],"type":{"kind":"struct","fields":[{"name":"config","docs":["Config pubkey"],"type":"pubkey"},{"name":"admin","docs":["admin pk"],"type":"pubkey"}]}},{"name":"EvtClosePosition","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"position_nft_mint","type":"pubkey"}]}},{"name":"EvtCreateConfig","docs":["Create static config"],"type":{"kind":"struct","fields":[{"name":"pool_fees","type":{"defined":{"name":"PoolFeeParameters"}}},{"name":"vault_config_key","type":"pubkey"},{"name":"pool_creator_authority","type":"pubkey"},{"name":"activation_type","type":"u8"},{"name":"sqrt_min_price","type":"u128"},{"name":"sqrt_max_price","type":"u128"},{"name":"collect_fee_mode","type":"u8"},{"name":"index","type":"u64"},{"name":"config","type":"pubkey"}]}},{"name":"EvtCreateDynamicConfig","docs":["Create dynamic config"],"type":{"kind":"struct","fields":[{"name":"config","type":"pubkey"},{"name":"pool_creator_authority","type":"pubkey"},{"name":"index","type":"u64"}]}},{"name":"EvtCreatePosition","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"position_nft_mint","type":"pubkey"}]}},{"name":"EvtCreateTokenBadge","docs":["Create token badge"],"type":{"kind":"struct","fields":[{"name":"token_mint","type":"pubkey"}]}},{"name":"EvtFundReward","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"funder","type":"pubkey"},{"name":"mint_reward","type":"pubkey"},{"name":"reward_index","type":"u8"},{"name":"amount","type":"u64"},{"name":"transfer_fee_excluded_amount_in","type":"u64"},{"name":"reward_duration_end","type":"u64"},{"name":"pre_reward_rate","type":"u128"},{"name":"post_reward_rate","type":"u128"}]}},{"name":"EvtInitializePool","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"token_a_mint","type":"pubkey"},{"name":"token_b_mint","type":"pubkey"},{"name":"creator","type":"pubkey"},{"name":"payer","type":"pubkey"},{"name":"alpha_vault","type":"pubkey"},{"name":"pool_fees","type":{"defined":{"name":"PoolFeeParameters"}}},{"name":"sqrt_min_price","type":"u128"},{"name":"sqrt_max_price","type":"u128"},{"name":"activation_type","type":"u8"},{"name":"collect_fee_mode","type":"u8"},{"name":"liquidity","type":"u128"},{"name":"sqrt_price","type":"u128"},{"name":"activation_point","type":"u64"},{"name":"token_a_flag","type":"u8"},{"name":"token_b_flag","type":"u8"},{"name":"token_a_amount","type":"u64"},{"name":"token_b_amount","type":"u64"},{"name":"total_amount_a","type":"u64"},{"name":"total_amount_b","type":"u64"},{"name":"pool_type","type":"u8"}]}},{"name":"EvtInitializeReward","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"reward_mint","type":"pubkey"},{"name":"funder","type":"pubkey"},{"name":"creator","type":"pubkey"},{"name":"reward_index","type":"u8"},{"name":"reward_duration","type":"u64"}]}},{"name":"EvtLiquidityChange","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"token_a_amount","type":"u64"},{"name":"token_b_amount","type":"u64"},{"name":"transfer_fee_included_token_a_amount","type":"u64"},{"name":"transfer_fee_included_token_b_amount","type":"u64"},{"name":"reserve_a_amount","type":"u64"},{"name":"reserve_b_amount","type":"u64"},{"name":"liquidity_delta","type":"u128"},{"name":"token_a_amount_threshold","type":"u64"},{"name":"token_b_amount_threshold","type":"u64"},{"name":"change_type","type":"u8"}]}},{"name":"EvtLockPosition","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"vesting","type":"pubkey"},{"name":"cliff_point","type":"u64"},{"name":"period_frequency","type":"u64"},{"name":"cliff_unlock_liquidity","type":"u128"},{"name":"liquidity_per_period","type":"u128"},{"name":"number_of_period","type":"u16"}]}},{"name":"EvtPermanentLockPosition","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"lock_liquidity_amount","type":"u128"},{"name":"total_permanent_locked_liquidity","type":"u128"}]}},{"name":"EvtSetPoolStatus","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"status","type":"u8"}]}},{"name":"EvtSplitPosition2","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"first_owner","type":"pubkey"},{"name":"second_owner","type":"pubkey"},{"name":"first_position","type":"pubkey"},{"name":"second_position","type":"pubkey"},{"name":"current_sqrt_price","type":"u128"},{"name":"amount_splits","type":{"defined":{"name":"SplitAmountInfo"}}},{"name":"first_position_info","type":{"defined":{"name":"SplitPositionInfo"}}},{"name":"second_position_info","type":{"defined":{"name":"SplitPositionInfo"}}},{"name":"split_position_parameters","type":{"defined":{"name":"SplitPositionParameters2"}}}]}},{"name":"EvtSplitPosition3","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"first_owner","type":"pubkey"},{"name":"second_owner","type":"pubkey"},{"name":"first_position","type":"pubkey"},{"name":"second_position","type":"pubkey"},{"name":"current_sqrt_price","type":"u128"},{"name":"amount_splits","type":{"defined":{"name":"SplitAmountInfo2"}}},{"name":"first_position_info","type":{"defined":{"name":"SplitPositionInfo2"}}},{"name":"second_position_info","type":{"defined":{"name":"SplitPositionInfo2"}}},{"name":"split_position_parameters","type":{"defined":{"name":"SplitPositionParameters3"}}}]}},{"name":"EvtSwap2","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"trade_direction","type":"u8"},{"name":"collect_fee_mode","type":"u8"},{"name":"has_referral","type":"bool"},{"name":"params","type":{"defined":{"name":"SwapParameters2"}}},{"name":"swap_result","type":{"defined":{"name":"SwapResult2"}}},{"name":"included_transfer_fee_amount_in","type":"u64"},{"name":"included_transfer_fee_amount_out","type":"u64"},{"name":"excluded_transfer_fee_amount_out","type":"u64"},{"name":"current_timestamp","type":"u64"},{"name":"reserve_a_amount","type":"u64"},{"name":"reserve_b_amount","type":"u64"}]}},{"name":"EvtUpdatePoolFees","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"operator","type":"pubkey"},{"name":"params","type":{"defined":{"name":"UpdatePoolFeesParameters"}}}]}},{"name":"EvtUpdateRewardDuration","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"reward_index","type":"u8"},{"name":"old_reward_duration","type":"u64"},{"name":"new_reward_duration","type":"u64"}]}},{"name":"EvtUpdateRewardFunder","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"reward_index","type":"u8"},{"name":"old_funder","type":"pubkey"},{"name":"new_funder","type":"pubkey"}]}},{"name":"EvtWithdrawIneligibleReward","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"reward_mint","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"InitializeCustomizablePoolParameters","type":{"kind":"struct","fields":[{"name":"pool_fees","docs":["pool fees"],"type":{"defined":{"name":"PoolFeeParameters"}}},{"name":"sqrt_min_price","docs":["sqrt min price"],"type":"u128"},{"name":"sqrt_max_price","docs":["sqrt max price"],"type":"u128"},{"name":"has_alpha_vault","docs":["has alpha vault"],"type":"bool"},{"name":"liquidity","docs":["initialize liquidity"],"type":"u128"},{"name":"sqrt_price","docs":["The init price of the pool as a sqrt(token_b/token_a) Q64.64 value. Market cap fee scheduler minimum price will be derived from this value"],"type":"u128"},{"name":"activation_type","docs":["activation type"],"type":"u8"},{"name":"collect_fee_mode","docs":["collect fee mode"],"type":"u8"},{"name":"activation_point","docs":["activation point"],"type":{"option":"u64"}}]}},{"name":"InitializePoolParameters","type":{"kind":"struct","fields":[{"name":"liquidity","docs":["initialize liquidity"],"type":"u128"},{"name":"sqrt_price","docs":["The init price of the pool as a sqrt(token_b/token_a) Q64.64 value"],"type":"u128"},{"name":"activation_point","docs":["activation point"],"type":{"option":"u64"}}]}},{"name":"InnerVesting","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"cliff_point","type":"u64"},{"name":"period_frequency","type":"u64"},{"name":"cliff_unlock_liquidity","type":"u128"},{"name":"liquidity_per_period","type":"u128"},{"name":"total_released_liquidity","type":"u128"},{"name":"number_of_period","type":"u16"},{"name":"padding","type":{"array":["u8",14]}}]}},{"name":"Operator","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"whitelisted_address","type":"pubkey"},{"name":"permission","type":"u128"},{"name":"padding","type":{"array":["u64",2]}}]}},{"name":"PodAlignedFeeMarketCapScheduler","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","type":"u64"},{"name":"base_fee_mode","type":"u8"},{"name":"padding","type":{"array":["u8",5]}},{"name":"number_of_period","type":"u16"},{"name":"sqrt_price_step_bps","type":"u32"},{"name":"scheduler_expiration_duration","type":"u32"},{"name":"reduction_factor","type":"u64"}]}},{"name":"PodAlignedFeeRateLimiter","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","type":"u64"},{"name":"base_fee_mode","type":"u8"},{"name":"padding","type":{"array":["u8",5]}},{"name":"fee_increment_bps","type":"u16"},{"name":"max_limiter_duration","type":"u32"},{"name":"max_fee_bps","type":"u32"},{"name":"reference_amount","type":"u64"}]}},{"name":"PodAlignedFeeTimeScheduler","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","type":"u64"},{"name":"base_fee_mode","type":"u8"},{"name":"padding","type":{"array":["u8",5]}},{"name":"number_of_period","type":"u16"},{"name":"period_frequency","type":"u64"},{"name":"reduction_factor","type":"u64"}]}},{"name":"Pool","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"pool_fees","docs":["Pool fee"],"type":{"defined":{"name":"PoolFeesStruct"}}},{"name":"token_a_mint","docs":["token a mint"],"type":"pubkey"},{"name":"token_b_mint","docs":["token b mint"],"type":"pubkey"},{"name":"token_a_vault","docs":["token a vault"],"type":"pubkey"},{"name":"token_b_vault","docs":["token b vault"],"type":"pubkey"},{"name":"whitelisted_vault","docs":["Whitelisted vault to be able to buy pool before activation_point"],"type":"pubkey"},{"name":"padding_0","docs":["padding, previously partner pubkey, be careful when using this field"],"type":{"array":["u8",32]}},{"name":"liquidity","docs":["liquidity share"],"type":"u128"},{"name":"padding_1","docs":["padding, previous reserve amount, be careful to use that field"],"type":"u128"},{"name":"protocol_a_fee","docs":["protocol a fee"],"type":"u64"},{"name":"protocol_b_fee","docs":["protocol b fee"],"type":"u64"},{"name":"padding_2","type":"u128"},{"name":"sqrt_min_price","docs":["min price"],"type":"u128"},{"name":"sqrt_max_price","docs":["max price"],"type":"u128"},{"name":"sqrt_price","docs":["current price"],"type":"u128"},{"name":"activation_point","docs":["Activation point, can be slot or timestamp"],"type":"u64"},{"name":"activation_type","docs":["Activation type, 0 means by slot, 1 means by timestamp"],"type":"u8"},{"name":"pool_status","docs":["pool status, 0: enable, 1 disable"],"type":"u8"},{"name":"token_a_flag","docs":["token a flag"],"type":"u8"},{"name":"token_b_flag","docs":["token b flag"],"type":"u8"},{"name":"collect_fee_mode","docs":["0 is collect fee in both token, 1 only collect fee only in token b"],"type":"u8"},{"name":"pool_type","docs":["pool type"],"type":"u8"},{"name":"fee_version","docs":["pool fee version, 0: max_fee is still capped at 50%, 1: max_fee is capped at 99%"],"type":"u8"},{"name":"padding_3","docs":["padding"],"type":"u8"},{"name":"fee_a_per_liquidity","docs":["cumulative"],"type":{"array":["u8",32]}},{"name":"fee_b_per_liquidity","docs":["cumulative"],"type":{"array":["u8",32]}},{"name":"permanent_lock_liquidity","type":"u128"},{"name":"metrics","docs":["metrics"],"type":{"defined":{"name":"PoolMetrics"}}},{"name":"creator","docs":["pool creator"],"type":"pubkey"},{"name":"token_a_amount","docs":["token a amount"],"type":"u64"},{"name":"token_b_amount","docs":["token b amount"],"type":"u64"},{"name":"layout_version","docs":["layout version: version 0: haven't track token_a_amount and token_b_amount, version 1: track token_a_amount and token_b_amount"],"type":"u8"},{"name":"padding_4","docs":["Padding for further use"],"type":{"array":["u8",7]}},{"name":"padding_5","docs":["Padding for further use"],"type":{"array":["u64",3]}},{"name":"reward_infos","docs":["Farming reward information"],"type":{"array":[{"defined":{"name":"RewardInfo"}},2]}}]}},{"name":"PoolFeeParameters","docs":["Information regarding fee charges"],"type":{"kind":"struct","fields":[{"name":"base_fee","docs":["Base fee"],"type":{"defined":{"name":"BaseFeeParameters"}}},{"name":"compounding_fee_bps","docs":["compounding fee bps, only have value if CollectFeeMode::Compounding"],"type":"u16"},{"name":"padding","docs":["padding for future use"],"type":"u8"},{"name":"dynamic_fee","docs":["dynamic fee"],"type":{"option":{"defined":{"name":"DynamicFeeParameters"}}}}]}},{"name":"PoolFeesConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"base_fee","type":{"defined":{"name":"BaseFeeInfo"}}},{"name":"dynamic_fee","type":{"defined":{"name":"DynamicFeeConfig"}}},{"name":"protocol_fee_percent","type":"u8"},{"name":"padding_0","type":"u8"},{"name":"referral_fee_percent","type":"u8"},{"name":"padding_1","type":{"array":["u8",3]}},{"name":"compounding_fee_bps","docs":["Compounding fee bps, only non-zero if collect_fee_mode is compounding"],"type":"u16"},{"name":"padding_2","type":{"array":["u64",5]}}]}},{"name":"PoolFeesStruct","docs":["Information regarding fee charges","trading_fee = amount * trade_fee_numerator / denominator","protocol_fee = trading_fee * protocol_fee_percentage / 100","referral_fee = protocol_fee * referral_percentage / 100"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"base_fee","docs":["Trade fees are extra token amounts that are held inside the token","accounts during a trade, making the value of liquidity tokens rise.","Trade fee numerator"],"type":{"defined":{"name":"BaseFeeStruct"}}},{"name":"protocol_fee_percent","docs":["Protocol trading fees are extra token amounts that are held inside the token","accounts during a trade, with the equivalent in pool tokens minted to","the protocol of the program.","Protocol trade fee numerator"],"type":"u8"},{"name":"padding_0","docs":["padding for future use"],"type":"u8"},{"name":"referral_fee_percent","docs":["referral fee"],"type":"u8"},{"name":"padding_1","docs":["padding"],"type":{"array":["u8",3]}},{"name":"compounding_fee_bps","docs":["compounding fee bps, only non-zero in CollectFeeMode::Compounding"],"type":"u16"},{"name":"dynamic_fee","docs":["dynamic fee"],"type":{"defined":{"name":"DynamicFeeStruct"}}},{"name":"init_sqrt_price","type":"u128"}]}},{"name":"PoolMetrics","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"total_lp_a_fee","type":"u128"},{"name":"total_lp_b_fee","type":"u128"},{"name":"total_protocol_a_fee","type":"u64"},{"name":"total_protocol_b_fee","type":"u64"},{"name":"padding_0","type":{"array":["u64",2]}},{"name":"total_position","type":"u64"},{"name":"padding","type":"u64"}]}},{"name":"Position","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"nft_mint","docs":["nft mint"],"type":"pubkey"},{"name":"fee_a_per_token_checkpoint","docs":["fee a checkpoint"],"type":{"array":["u8",32]}},{"name":"fee_b_per_token_checkpoint","docs":["fee b checkpoint"],"type":{"array":["u8",32]}},{"name":"fee_a_pending","docs":["fee a pending"],"type":"u64"},{"name":"fee_b_pending","docs":["fee b pending"],"type":"u64"},{"name":"unlocked_liquidity","docs":["unlock liquidity"],"type":"u128"},{"name":"vested_liquidity","docs":["vesting liquidity"],"type":"u128"},{"name":"permanent_locked_liquidity","docs":["permanent locked liquidity"],"type":"u128"},{"name":"metrics","docs":["metrics"],"type":{"defined":{"name":"PositionMetrics"}}},{"name":"reward_infos","docs":["Farming reward information"],"type":{"array":[{"defined":{"name":"UserRewardInfo"}},2]}},{"name":"inner_vesting","docs":["inner vesting info"],"type":{"defined":{"name":"InnerVesting"}}},{"name":"padding","docs":["padding for future usage"],"type":"u128"}]}},{"name":"PositionMetrics","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"total_claimed_a_fee","type":"u64"},{"name":"total_claimed_b_fee","type":"u64"}]}},{"name":"RemoveLiquidityParameters","type":{"kind":"struct","fields":[{"name":"liquidity_delta","docs":["delta liquidity"],"type":"u128"},{"name":"token_a_amount_threshold","docs":["minimum token a amount"],"type":"u64"},{"name":"token_b_amount_threshold","docs":["minimum token b amount"],"type":"u64"}]}},{"name":"RewardInfo","docs":["Stores the state relevant for tracking liquidity mining rewards"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"initialized","docs":["Indicates if the reward has been initialized"],"type":"u8"},{"name":"reward_token_flag","docs":["reward token flag"],"type":"u8"},{"name":"_padding_0","docs":["padding"],"type":{"array":["u8",6]}},{"name":"_padding_1","docs":["Padding to ensure `reward_rate: u128` is 16-byte aligned"],"type":{"array":["u8",8]}},{"name":"mint","docs":["Reward token mint."],"type":"pubkey"},{"name":"vault","docs":["Reward vault token account."],"type":"pubkey"},{"name":"funder","docs":["Authority account that allows to fund rewards"],"type":"pubkey"},{"name":"reward_duration","docs":["reward duration"],"type":"u64"},{"name":"reward_duration_end","docs":["reward duration end"],"type":"u64"},{"name":"reward_rate","docs":["reward rate"],"type":"u128"},{"name":"reward_per_token_stored","docs":["Reward per token stored"],"type":{"array":["u8",32]}},{"name":"last_update_time","docs":["The last time reward states were updated."],"type":"u64"},{"name":"cumulative_seconds_with_empty_liquidity_reward","docs":["Accumulated seconds when the farm distributed rewards but the bin was empty.","These rewards will be carried over to the next reward time window."],"type":"u64"}]}},{"name":"SplitAmountInfo","type":{"kind":"struct","fields":[{"name":"permanent_locked_liquidity","type":"u128"},{"name":"unlocked_liquidity","type":"u128"},{"name":"fee_a","type":"u64"},{"name":"fee_b","type":"u64"},{"name":"reward_0","type":"u64"},{"name":"reward_1","type":"u64"}]}},{"name":"SplitAmountInfo2","type":{"kind":"struct","fields":[{"name":"permanent_locked_liquidity","type":"u128"},{"name":"unlocked_liquidity","type":"u128"},{"name":"vested_liquidity","type":"u128"},{"name":"fee_a","type":"u64"},{"name":"fee_b","type":"u64"},{"name":"reward_0","type":"u64"},{"name":"reward_1","type":"u64"}]}},{"name":"SplitPositionInfo","type":{"kind":"struct","fields":[{"name":"liquidity","type":"u128"},{"name":"fee_a","type":"u64"},{"name":"fee_b","type":"u64"},{"name":"reward_0","type":"u64"},{"name":"reward_1","type":"u64"}]}},{"name":"SplitPositionInfo2","type":{"kind":"struct","fields":[{"name":"unlocked_liquidity","type":"u128"},{"name":"permanent_locked_liquidity","type":"u128"},{"name":"vested_liquidity","type":"u128"},{"name":"fee_a","type":"u64"},{"name":"fee_b","type":"u64"},{"name":"reward_0","type":"u64"},{"name":"reward_1","type":"u64"}]}},{"name":"SplitPositionParameters","type":{"kind":"struct","fields":[{"name":"unlocked_liquidity_percentage","docs":["Percentage of unlocked liquidity to split to the second position"],"type":"u8"},{"name":"permanent_locked_liquidity_percentage","docs":["Percentage of permanent locked liquidity to split to the second position"],"type":"u8"},{"name":"fee_a_percentage","docs":["Percentage of fee A pending to split to the second position"],"type":"u8"},{"name":"fee_b_percentage","docs":["Percentage of fee B pending to split to the second position"],"type":"u8"},{"name":"reward_0_percentage","docs":["Percentage of reward 0 pending to split to the second position"],"type":"u8"},{"name":"reward_1_percentage","docs":["Percentage of reward 1 pending to split to the second position"],"type":"u8"},{"name":"inner_vesting_liquidity_percentage","docs":["Percentage of inner vesting liquidity"],"type":"u8"},{"name":"padding","docs":["padding for future"],"type":{"array":["u8",15]}}]}},{"name":"SplitPositionParameters2","type":{"kind":"struct","fields":[{"name":"unlocked_liquidity_numerator","type":"u32"},{"name":"permanent_locked_liquidity_numerator","type":"u32"},{"name":"fee_a_numerator","type":"u32"},{"name":"fee_b_numerator","type":"u32"},{"name":"reward_0_numerator","type":"u32"},{"name":"reward_1_numerator","type":"u32"}]}},{"name":"SplitPositionParameters3","type":{"kind":"struct","fields":[{"name":"unlocked_liquidity_numerator","type":"u32"},{"name":"permanent_locked_liquidity_numerator","type":"u32"},{"name":"fee_a_numerator","type":"u32"},{"name":"fee_b_numerator","type":"u32"},{"name":"reward_0_numerator","type":"u32"},{"name":"reward_1_numerator","type":"u32"},{"name":"inner_vesting_liquidity_numerator","type":"u32"}]}},{"name":"StaticConfigParameters","type":{"kind":"struct","fields":[{"name":"pool_fees","type":{"defined":{"name":"PoolFeeParameters"}}},{"name":"sqrt_min_price","type":"u128"},{"name":"sqrt_max_price","type":"u128"},{"name":"vault_config_key","type":"pubkey"},{"name":"pool_creator_authority","type":"pubkey"},{"name":"activation_type","type":"u8"},{"name":"collect_fee_mode","type":"u8"}]}},{"name":"SwapParameters","type":{"kind":"struct","fields":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"}]}},{"name":"SwapParameters2","type":{"kind":"struct","fields":[{"name":"amount_0","docs":["When it's exact in, partial fill, this will be amount_in. When it's exact out, this will be amount_out"],"type":"u64"},{"name":"amount_1","docs":["When it's exact in, partial fill, this will be minimum_amount_out. When it's exact out, this will be maximum_amount_in"],"type":"u64"},{"name":"swap_mode","docs":["Swap mode, refer [SwapMode]"],"type":"u8"}]}},{"name":"SwapResult2","type":{"kind":"struct","fields":[{"name":"included_fee_input_amount","type":"u64"},{"name":"excluded_fee_input_amount","type":"u64"},{"name":"amount_left","type":"u64"},{"name":"output_amount","type":"u64"},{"name":"next_sqrt_price","type":"u128"},{"name":"claiming_fee","type":"u64"},{"name":"protocol_fee","type":"u64"},{"name":"compounding_fee","type":"u64"},{"name":"referral_fee","type":"u64"}]}},{"name":"TokenBadge","docs":["Parameter that set by the protocol"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"token_mint","docs":["token mint"],"type":"pubkey"},{"name":"_padding","docs":["Reserve"],"type":{"array":["u8",128]}}]}},{"name":"UpdatePoolFeesParameters","type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","docs":["Base fee update mode:","- None: skip base fee update","- Some: update new cliff_fee_numerator if base fee is static"],"type":{"option":"u64"}},{"name":"dynamic_fee","docs":["Dynamic fee update mode:","- None: skip dynamic fee update","- Some(with default value): disable dynamic fee","- Some(with non default value): enable dynamic fee if disabled or update dynamic fee if enabled"],"type":{"option":{"defined":{"name":"DynamicFeeParameters"}}}}]}},{"name":"UserRewardInfo","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"reward_per_token_checkpoint","docs":["The latest update reward checkpoint"],"type":{"array":["u8",32]}},{"name":"reward_pendings","docs":["Current pending rewards"],"type":"u64"},{"name":"total_claimed_rewards","docs":["Total claimed rewards"],"type":"u64"}]}},{"name":"Vesting","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"position","type":"pubkey"},{"name":"inner_vesting","type":{"defined":{"name":"InnerVesting"}}},{"name":"padding2","type":{"array":["u128",4]}}]}},{"name":"VestingParameters","type":{"kind":"struct","fields":[{"name":"cliff_point","type":{"option":"u64"}},{"name":"period_frequency","type":"u64"},{"name":"cliff_unlock_liquidity","type":"u128"},{"name":"liquidity_per_period","type":"u128"},{"name":"number_of_period","type":"u16"}]}}],"constants":[{"name":"BIN_STEP_BPS_DEFAULT","type":"u16","value":"1"},{"name":"BIN_STEP_U128_DEFAULT_LE_BYTES","type":{"array":["u8",16]},"value":"[203, 16, 199, 186, 184, 141, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0]"},{"name":"CURRENT_POOL_VERSION","type":"u8","value":"1"},{"name":"CUSTOMIZABLE_POOL_PREFIX","type":"bytes","value":"[99, 112, 111, 111, 108]"},{"name":"FEE_DENOMINATOR","docs":["Default fee denominator. DO NOT simply update it as it will break logic that depends on it as default value."],"type":"u64","value":"1000000000"},{"name":"MAX_BASIS_POINT","docs":["Max basis point. 100% in pct"],"type":"u16","value":"10000"},{"name":"MAX_FEE_NUMERATOR_V0","type":"u64","value":"500000000"},{"name":"MAX_FEE_NUMERATOR_V1","type":"u64","value":"990000000"},{"name":"MAX_SQRT_PRICE_LE_BYTES","type":{"array":["u8",16]},"value":"[155, 87, 105, 78, 169, 26, 92, 132, 177, 196, 254, 255, 0, 0, 0, 0]"},{"name":"MIN_FEE_NUMERATOR","type":"u64","value":"100000"},{"name":"MIN_SQRT_PRICE_LE_BYTES","type":{"array":["u8",16]},"value":"[80, 59, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]"},{"name":"POOL_AUTHORITY_PREFIX","type":"bytes","value":"[112, 111, 111, 108, 95, 97, 117, 116, 104, 111, 114, 105, 116, 121]"},{"name":"POOL_PREFIX","type":"bytes","value":"[112, 111, 111, 108]"},{"name":"POSITION_NFT_ACCOUNT_PREFIX","type":"bytes","value":"[112, 111, 115, 105, 116, 105, 111, 110, 95, 110, 102, 116, 95, 97, 99, 99, 111, 117, 110, 116]"},{"name":"POSITION_PREFIX","type":"bytes","value":"[112, 111, 115, 105, 116, 105, 111, 110]"},{"name":"SPLIT_POSITION_DENOMINATOR","type":"u32","value":"1000000000"},{"name":"TOKEN_VAULT_PREFIX","type":"bytes","value":"[116, 111, 107, 101, 110, 95, 118, 97, 117, 108, 116]"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/meteora_dbc.dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN.json b/migration/khadhroony-bot2-reference/idls/meteora_dbc.dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN.json new file mode 100644 index 0000000..98ed38b --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/meteora_dbc.dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN.json @@ -0,0 +1 @@ +{"address":"dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN","metadata":{"name":"dynamic_bonding_curve","version":"0.1.10","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"claim_creator_trading_fee","discriminator":[82,220,250,189,3,85,107,45],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"pool","writable":true},{"name":"token_a_account","docs":["The treasury token a account"],"writable":true},{"name":"token_b_account","docs":["The treasury token b account"],"writable":true},{"name":"base_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"quote_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"base_mint","docs":["The mint of token a"],"relations":["pool"]},{"name":"quote_mint","docs":["The mint of token b"]},{"name":"creator","signer":true},{"name":"token_base_program","docs":["Token a program"]},{"name":"token_quote_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"max_base_amount","type":"u64"},{"name":"max_quote_amount","type":"u64"}]},{"name":"claim_partner_pool_creation_fee","discriminator":[250,238,26,4,139,10,101,248],"accounts":[{"name":"config","relations":["pool"]},{"name":"pool","writable":true},{"name":"fee_claimer","signer":true},{"name":"fee_receiver","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"claim_protocol_fee","discriminator":[165,228,133,48,99,249,255,33],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"config","relations":["pool"]},{"name":"pool","writable":true},{"name":"base_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"quote_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"base_mint","docs":["The mint of token a"],"relations":["pool"]},{"name":"quote_mint","docs":["The mint of token b"],"relations":["config"]},{"name":"token_base_account","writable":true},{"name":"token_quote_account","writable":true},{"name":"operator"},{"name":"signer","docs":["Signer"],"signer":true},{"name":"token_base_program","docs":["Token a program"]},{"name":"token_quote_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"max_base_amount","type":"u64"},{"name":"max_quote_amount","type":"u64"}]},{"name":"claim_protocol_pool_creation_fee","discriminator":[114,205,83,188,240,153,25,54],"accounts":[{"name":"config","relations":["pool"]},{"name":"pool","writable":true},{"name":"operator"},{"name":"signer","docs":["Operator"],"signer":true},{"name":"treasury","writable":true,"address":"6aYhxiNGmG8AyU25rh2R7iFu4pBrqnQHpNUGhmsEXRcm"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"claim_trading_fee","discriminator":[8,236,89,49,152,125,177,81],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"config","relations":["pool"]},{"name":"pool","writable":true},{"name":"token_a_account","docs":["The treasury token a account"],"writable":true},{"name":"token_b_account","docs":["The treasury token b account"],"writable":true},{"name":"base_vault","docs":["The vault token account for input token"],"writable":true,"relations":["pool"]},{"name":"quote_vault","docs":["The vault token account for output token"],"writable":true,"relations":["pool"]},{"name":"base_mint","docs":["The mint of token a"],"relations":["pool"]},{"name":"quote_mint","docs":["The mint of token b"],"relations":["config"]},{"name":"fee_claimer","signer":true},{"name":"token_base_program","docs":["Token a program"]},{"name":"token_quote_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"max_amount_a","type":"u64"},{"name":"max_amount_b","type":"u64"}]},{"name":"close_claim_protocol_fee_operator","discriminator":[8,41,87,35,80,48,121,26],"accounts":[{"name":"claim_fee_operator","writable":true},{"name":"rent_receiver","writable":true},{"name":"signer","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"close_operator_account","discriminator":[171,9,213,74,120,23,3,29],"accounts":[{"name":"operator","writable":true},{"name":"signer","signer":true},{"name":"rent_receiver","writable":true}],"args":[]},{"name":"create_config","discriminator":[201,207,243,114,75,111,47,189],"accounts":[{"name":"config","writable":true,"signer":true},{"name":"fee_claimer"},{"name":"leftover_receiver"},{"name":"quote_mint","docs":["quote mint"]},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"config_parameters","type":{"defined":{"name":"ConfigParameters"}}}]},{"name":"create_locker","docs":["PERMISSIONLESS FUNCTIONS ///","create locker"],"discriminator":[167,90,137,154,75,47,17,84],"accounts":[{"name":"virtual_pool","docs":["Virtual pool"],"writable":true},{"name":"config","docs":["Config"],"relations":["virtual_pool"]},{"name":"pool_authority","writable":true,"address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"base_vault","writable":true,"relations":["virtual_pool"]},{"name":"base_mint","writable":true,"relations":["virtual_pool"]},{"name":"base","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,97,115,101,95,108,111,99,107,101,114]},{"kind":"account","path":"virtual_pool"}]}},{"name":"creator","relations":["virtual_pool"]},{"name":"escrow","writable":true},{"name":"escrow_token","writable":true},{"name":"payer","writable":true,"signer":true},{"name":"token_program"},{"name":"locker_program","address":"LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn"},{"name":"locker_event_authority"},{"name":"system_program","docs":["System program."],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_operator_account","discriminator":[221,64,246,149,240,153,229,163],"accounts":[{"name":"operator","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,111,114]},{"kind":"account","path":"whitelisted_address"}]}},{"name":"whitelisted_address"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"permission","type":"u128"}]},{"name":"create_partner_metadata","docs":["PARTNER FUNCTIONS ///"],"discriminator":[192,168,234,191,188,226,227,255],"accounts":[{"name":"partner_metadata","docs":["Partner metadata"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,97,114,116,110,101,114,95,109,101,116,97,100,97,116,97]},{"kind":"account","path":"fee_claimer"}]}},{"name":"payer","docs":["Payer of the partner metadata."],"writable":true,"signer":true},{"name":"fee_claimer","docs":["Fee claimer for partner"],"signer":true},{"name":"system_program","docs":["System program."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"metadata","type":{"defined":{"name":"CreatePartnerMetadataParameters"}}}]},{"name":"create_virtual_pool_metadata","discriminator":[45,97,187,103,254,109,124,134],"accounts":[{"name":"virtual_pool","writable":true},{"name":"virtual_pool_metadata","docs":["Virtual pool metadata"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[118,105,114,116,117,97,108,95,112,111,111,108,95,109,101,116,97,100,97,116,97]},{"kind":"account","path":"virtual_pool"}]}},{"name":"creator","signer":true},{"name":"payer","docs":["Payer of the virtual pool metadata."],"writable":true,"signer":true},{"name":"system_program","docs":["System program."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"metadata","type":{"defined":{"name":"CreateVirtualPoolMetadataParameters"}}}]},{"name":"creator_withdraw_surplus","discriminator":[165,3,137,7,28,134,76,80],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"config","relations":["virtual_pool"]},{"name":"virtual_pool","writable":true},{"name":"token_quote_account","docs":["The receiver token account"],"writable":true},{"name":"quote_vault","docs":["The vault token account for output token"],"writable":true,"relations":["virtual_pool"]},{"name":"quote_mint","docs":["The mint of quote token"],"relations":["config"]},{"name":"creator","signer":true},{"name":"token_quote_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"initialize_virtual_pool_with_spl_token","docs":["POOL CREATOR FUNCTIONS ////"],"discriminator":[140,85,215,176,102,54,104,79],"accounts":[{"name":"config","docs":["Which config the pool belongs to."]},{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"creator","signer":true},{"name":"base_mint","writable":true,"signer":true},{"name":"quote_mint","relations":["config"]},{"name":"pool","docs":["Initialize an account to store the pool state"],"writable":true},{"name":"base_vault","docs":["Token a vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"base_mint"},{"kind":"account","path":"pool"}]}},{"name":"quote_vault","docs":["Token b vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"quote_mint"},{"kind":"account","path":"pool"}]}},{"name":"mint_metadata","writable":true},{"name":"metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"payer","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"token_quote_program","docs":["Program to create mint account and mint tokens"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"InitializePoolParameters"}}}]},{"name":"initialize_virtual_pool_with_token2022","discriminator":[169,118,51,78,145,110,220,155],"accounts":[{"name":"config","docs":["Which config the pool belongs to."]},{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"creator","signer":true},{"name":"base_mint","docs":["Unique token mint address, initialize in contract"],"writable":true,"signer":true},{"name":"quote_mint","relations":["config"]},{"name":"pool","docs":["Initialize an account to store the pool state"],"writable":true},{"name":"base_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"base_mint"},{"kind":"account","path":"pool"}]}},{"name":"quote_vault","docs":["Token quote vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,118,97,117,108,116]},{"kind":"account","path":"quote_mint"},{"kind":"account","path":"pool"}]}},{"name":"payer","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"token_quote_program","docs":["Program to create mint account and mint tokens"]},{"name":"token_program","docs":["token program for base mint"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"InitializePoolParameters"}}}]},{"name":"migrate_meteora_damm","discriminator":[27,1,48,22,180,63,118,217],"accounts":[{"name":"virtual_pool","docs":["virtual pool"],"writable":true,"relations":["migration_metadata"]},{"name":"migration_metadata","writable":true},{"name":"config","relations":["virtual_pool"]},{"name":"pool_authority","writable":true,"address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"pool","writable":true},{"name":"damm_config","docs":["pool config"]},{"name":"lp_mint","writable":true},{"name":"token_a_mint","writable":true},{"name":"token_b_mint"},{"name":"a_vault","writable":true},{"name":"b_vault","writable":true},{"name":"a_token_vault","writable":true},{"name":"b_token_vault","writable":true},{"name":"a_vault_lp_mint","writable":true},{"name":"b_vault_lp_mint","writable":true},{"name":"a_vault_lp","writable":true},{"name":"b_vault_lp","writable":true},{"name":"base_vault","writable":true,"relations":["virtual_pool"]},{"name":"quote_vault","writable":true,"relations":["virtual_pool"]},{"name":"virtual_pool_lp","writable":true},{"name":"protocol_token_a_fee","writable":true},{"name":"protocol_token_b_fee","writable":true},{"name":"payer","writable":true,"signer":true},{"name":"rent"},{"name":"mint_metadata","writable":true},{"name":"metadata_program"},{"name":"amm_program","address":"Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB"},{"name":"vault_program"},{"name":"token_program","docs":["token_program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program"},{"name":"system_program","docs":["System program."],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"migrate_meteora_damm_claim_lp_token","discriminator":[139,133,2,30,91,145,127,154],"accounts":[{"name":"virtual_pool","relations":["migration_metadata"]},{"name":"migration_metadata","docs":["migration metadata"],"writable":true},{"name":"pool_authority","writable":true,"address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"lp_mint","relations":["migration_metadata"]},{"name":"source_token","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_authority"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"migration_metadata"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"destination_token","writable":true,"pda":{"seeds":[{"kind":"account","path":"owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"migration_metadata"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"owner"},{"name":"sender","signer":true},{"name":"token_program","docs":["token_program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[]},{"name":"migrate_meteora_damm_lock_lp_token","discriminator":[177,55,238,157,251,88,165,42],"accounts":[{"name":"virtual_pool","relations":["migration_metadata"]},{"name":"migration_metadata","docs":["migration_metadata"],"writable":true},{"name":"pool_authority","writable":true,"address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"pool","writable":true,"relations":["lock_escrow"]},{"name":"lp_mint","relations":["migration_metadata"]},{"name":"lock_escrow","writable":true},{"name":"owner","relations":["lock_escrow"]},{"name":"source_tokens","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_authority"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"migration_metadata"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"escrow_vault","writable":true},{"name":"amm_program","address":"Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB"},{"name":"a_vault"},{"name":"b_vault"},{"name":"a_vault_lp"},{"name":"b_vault_lp"},{"name":"a_vault_lp_mint"},{"name":"b_vault_lp_mint"},{"name":"token_program","docs":["token_program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[]},{"name":"migration_damm_v2","discriminator":[156,169,230,103,53,228,80,64],"accounts":[{"name":"virtual_pool","docs":["virtual pool"],"writable":true},{"name":"migration_metadata"},{"name":"config","docs":["virtual pool config key"],"relations":["virtual_pool"]},{"name":"pool_authority","writable":true,"address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"pool","writable":true},{"name":"first_position_nft_mint","writable":true,"signer":true},{"name":"first_position_nft_account","writable":true},{"name":"first_position","writable":true},{"name":"second_position_nft_mint","writable":true,"signer":true,"optional":true},{"name":"second_position_nft_account","writable":true,"optional":true},{"name":"second_position","writable":true,"optional":true},{"name":"damm_pool_authority"},{"name":"amm_program","address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"},{"name":"base_mint","writable":true},{"name":"quote_mint","writable":true},{"name":"token_a_vault","writable":true},{"name":"token_b_vault","writable":true},{"name":"base_vault","writable":true,"relations":["virtual_pool"]},{"name":"quote_vault","writable":true,"relations":["virtual_pool"]},{"name":"payer","writable":true,"signer":true},{"name":"token_base_program"},{"name":"token_quote_program"},{"name":"token_2022_program"},{"name":"damm_event_authority"},{"name":"system_program","docs":["System program."],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"migration_damm_v2_create_metadata","discriminator":[109,189,19,36,195,183,222,82],"accounts":[{"name":"virtual_pool"},{"name":"config"},{"name":"migration_metadata"},{"name":"payer"},{"name":"system_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"migration_meteora_damm_create_metadata","docs":["migrate damm v1"],"discriminator":[47,94,126,115,221,226,194,133],"accounts":[{"name":"virtual_pool"},{"name":"config","relations":["virtual_pool"]},{"name":"migration_metadata","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,101,111,114,97]},{"kind":"account","path":"virtual_pool"}]}},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"partner_withdraw_surplus","discriminator":[168,173,72,100,201,98,38,92],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"config","relations":["virtual_pool"]},{"name":"virtual_pool","writable":true},{"name":"token_quote_account","docs":["The receiver token account"],"writable":true},{"name":"quote_vault","docs":["The vault token account for output token"],"writable":true,"relations":["virtual_pool"]},{"name":"quote_mint","docs":["The mint of quote token"],"relations":["config"]},{"name":"fee_claimer","signer":true},{"name":"token_quote_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"swap","docs":["TRADING BOTS FUNCTIONS ////"],"discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"config","docs":["config key"],"relations":["pool"]},{"name":"pool","docs":["Pool account"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"base_vault","docs":["The vault token account for base token"],"writable":true,"relations":["pool"]},{"name":"quote_vault","docs":["The vault token account for quote token"],"writable":true,"relations":["pool"]},{"name":"base_mint","docs":["The mint of base token"]},{"name":"quote_mint","docs":["The mint of quote token"]},{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"token_base_program","docs":["Token base program"]},{"name":"token_quote_program","docs":["Token quote program"]},{"name":"referral_token_account","docs":["referral token account"],"writable":true,"optional":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SwapParameters"}}}]},{"name":"swap2","discriminator":[65,75,63,76,235,91,91,136],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"config","docs":["config key"],"relations":["pool"]},{"name":"pool","docs":["Pool account"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"base_vault","docs":["The vault token account for base token"],"writable":true,"relations":["pool"]},{"name":"quote_vault","docs":["The vault token account for quote token"],"writable":true,"relations":["pool"]},{"name":"base_mint","docs":["The mint of base token"]},{"name":"quote_mint","docs":["The mint of quote token"]},{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"token_base_program","docs":["Token base program"]},{"name":"token_quote_program","docs":["Token quote program"]},{"name":"referral_token_account","docs":["referral token account"],"writable":true,"optional":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"SwapParameters2"}}}]},{"name":"transfer_pool_creator","discriminator":[20,7,169,33,58,147,166,33],"accounts":[{"name":"virtual_pool","writable":true},{"name":"config","relations":["virtual_pool"]},{"name":"creator","signer":true},{"name":"new_creator"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"withdraw_leftover","discriminator":[20,198,202,237,235,243,183,66],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"config","relations":["virtual_pool"]},{"name":"virtual_pool","writable":true},{"name":"token_base_account","docs":["The receiver token account, withdraw to ATA"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"leftover_receiver"},{"kind":"account","path":"token_base_program"},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"base_vault","docs":["The vault token account for output token"],"writable":true,"relations":["virtual_pool"]},{"name":"base_mint","docs":["The mint of quote token"],"relations":["virtual_pool"]},{"name":"leftover_receiver","relations":["config"]},{"name":"token_base_program","docs":["Token base program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"withdraw_migration_fee","docs":["BOTH partner and creator FUNCTIONS ///"],"discriminator":[237,142,45,23,129,6,222,162],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"config","relations":["virtual_pool"]},{"name":"virtual_pool","writable":true},{"name":"token_quote_account","docs":["The receiver token account"],"writable":true},{"name":"quote_vault","docs":["The vault token account for output token"],"writable":true,"relations":["virtual_pool"]},{"name":"quote_mint","docs":["The mint of quote token"],"relations":["config"]},{"name":"sender","signer":true},{"name":"token_quote_program","docs":["Token b program"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"flag","type":"u8"}]},{"name":"zap_protocol_fee","discriminator":[213,155,187,34,56,182,91,240],"accounts":[{"name":"pool_authority","address":"FhVo3mqL8PW5pH5U2CN4XE33DokiyZnUwuGpH2hmHLuM"},{"name":"config","relations":["pool"]},{"name":"pool","writable":true},{"name":"token_vault","writable":true},{"name":"token_mint"},{"name":"receiver_token","writable":true},{"name":"operator","docs":["zap claim fee operator"]},{"name":"signer","docs":["Operator"],"signer":true},{"name":"token_program","docs":["Token program"]},{"name":"sysvar_instructions","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[{"name":"max_amount","type":"u64"}]}],"accounts":[{"name":"ClaimFeeOperator","discriminator":[166,48,134,86,34,200,188,150]},{"name":"Config","discriminator":[155,12,170,224,30,250,204,130]},{"name":"LockEscrow","discriminator":[190,106,121,6,200,182,21,75]},{"name":"MeteoraDammMigrationMetadata","discriminator":[17,155,141,215,207,4,133,156]},{"name":"Operator","discriminator":[219,31,188,145,69,139,204,117]},{"name":"PartnerMetadata","discriminator":[68,68,130,19,16,209,98,156]},{"name":"PoolConfig","discriminator":[26,108,14,123,116,230,129,43]},{"name":"VirtualPool","discriminator":[213,224,5,209,98,69,119,92]},{"name":"VirtualPoolMetadata","discriminator":[217,37,82,250,43,47,228,254]}],"events":[{"name":"EvtClaimCreatorTradingFee","discriminator":[154,228,215,202,133,155,214,138]},{"name":"EvtClaimPoolCreationFee","discriminator":[149,111,149,44,136,64,175,62]},{"name":"EvtClaimProtocolFee","discriminator":[186,244,75,251,188,13,25,33]},{"name":"EvtClaimProtocolLiquidityMigrationFee","discriminator":[81,168,116,31,161,86,27,35]},{"name":"EvtClaimTradingFee","discriminator":[26,83,117,240,92,202,112,254]},{"name":"EvtCloseClaimFeeOperator","discriminator":[111,39,37,55,110,216,194,23]},{"name":"EvtCreateClaimFeeOperator","discriminator":[21,6,153,120,68,116,28,177]},{"name":"EvtCreateConfig","discriminator":[131,207,180,174,180,73,165,54]},{"name":"EvtCreateConfigV2","discriminator":[163,74,66,187,119,195,26,144]},{"name":"EvtCreateMeteoraMigrationMetadata","discriminator":[99,167,133,63,214,143,175,139]},{"name":"EvtCreatorWithdrawSurplus","discriminator":[152,73,21,15,66,87,53,157]},{"name":"EvtCurveComplete","discriminator":[229,231,86,84,156,134,75,24]},{"name":"EvtInitializePool","discriminator":[228,50,246,85,203,66,134,37]},{"name":"EvtPartnerClaimPoolCreationFee","discriminator":[174,223,44,150,145,98,89,195]},{"name":"EvtPartnerMetadata","discriminator":[200,127,6,55,13,32,8,150]},{"name":"EvtPartnerWithdrawMigrationFee","discriminator":[181,105,127,67,8,187,120,57]},{"name":"EvtPartnerWithdrawSurplus","discriminator":[195,56,152,9,232,72,35,22]},{"name":"EvtSwap","discriminator":[27,60,21,213,138,170,187,147]},{"name":"EvtSwap2","discriminator":[189,66,51,168,38,80,117,153]},{"name":"EvtUpdatePoolCreator","discriminator":[107,225,165,237,91,158,213,220]},{"name":"EvtVirtualPoolMetadata","discriminator":[188,18,72,76,195,91,38,74]},{"name":"EvtWithdrawLeftover","discriminator":[191,189,104,143,111,156,94,229]},{"name":"EvtWithdrawMigrationFee","discriminator":[26,203,84,85,161,23,100,214]}],"errors":[{"code":6000,"name":"MathOverflow","msg":"Math operation overflow"},{"code":6001,"name":"InvalidFee","msg":"Invalid fee setup"},{"code":6002,"name":"ExceededSlippage","msg":"Exceeded slippage tolerance"},{"code":6003,"name":"ExceedMaxFeeBps","msg":"Exceeded max fee bps"},{"code":6004,"name":"InvalidAdmin","msg":"Invalid admin"},{"code":6005,"name":"AmountIsZero","msg":"Amount is zero"},{"code":6006,"name":"TypeCastFailed","msg":"Type cast error"},{"code":6007,"name":"InvalidActivationType","msg":"Invalid activation type"},{"code":6008,"name":"InvalidQuoteMint","msg":"Invalid quote mint"},{"code":6009,"name":"InvalidCollectFeeMode","msg":"Invalid collect fee mode"},{"code":6010,"name":"InvalidMigrationFeeOption","msg":"Invalid migration fee option"},{"code":6011,"name":"InvalidInput","msg":"Invalid input"},{"code":6012,"name":"NotEnoughLiquidity","msg":"Not enough liquidity"},{"code":6013,"name":"PoolIsCompleted","msg":"Pool is completed"},{"code":6014,"name":"PoolIsIncompleted","msg":"Pool is incompleted"},{"code":6015,"name":"InvalidMigrationOption","msg":"Invalid migration option"},{"code":6016,"name":"InvalidTokenDecimals","msg":"Invalid token decimals"},{"code":6017,"name":"InvalidTokenType","msg":"Invalid token type"},{"code":6018,"name":"InvalidFeePercentage","msg":"Invalid fee percentage"},{"code":6019,"name":"InvalidQuoteThreshold","msg":"Invalid quote threshold"},{"code":6020,"name":"InvalidTokenSupply","msg":"Invalid token supply"},{"code":6021,"name":"InvalidCurve","msg":"Invalid curve"},{"code":6022,"name":"NotPermitToDoThisAction","msg":"Not permit to do this action"},{"code":6023,"name":"InvalidOwnerAccount","msg":"Invalid owner account"},{"code":6024,"name":"InvalidConfigAccount","msg":"Invalid config account"},{"code":6025,"name":"SurplusHasBeenWithdraw","msg":"Surplus has been withdraw"},{"code":6026,"name":"LeftoverHasBeenWithdraw","msg":"Leftover has been withdraw"},{"code":6027,"name":"TotalBaseTokenExceedMaxSupply","msg":"Total base token is exceeded max supply"},{"code":6028,"name":"UnsupportNativeMintToken2022","msg":"Unsupport native mint token 2022"},{"code":6029,"name":"InsufficientLiquidityForMigration","msg":"Insufficient liquidity for migration"},{"code":6030,"name":"MissingPoolConfigInRemainingAccount","msg":"Missing pool config in remaining account"},{"code":6031,"name":"InvalidVestingParameters","msg":"Invalid vesting parameters"},{"code":6032,"name":"InvalidLeftoverAddress","msg":"Invalid leftover address"},{"code":6033,"name":"InsufficientLiquidity","msg":"Liquidity in bonding curve is insufficient"},{"code":6034,"name":"InvalidFeeScheduler","msg":"Invalid fee scheduler"},{"code":6035,"name":"InvalidCreatorTradingFeePercentage","msg":"Invalid creator trading fee percentage"},{"code":6036,"name":"InvalidNewCreator","msg":"Invalid new creator"},{"code":6037,"name":"InvalidTokenAuthorityOption","msg":"Invalid token authority option"},{"code":6038,"name":"InvalidAccount","msg":"Invalid account for the instruction"},{"code":6039,"name":"InvalidMigratorFeePercentage","msg":"Invalid migrator fee percentage"},{"code":6040,"name":"MigrationFeeHasBeenWithdraw","msg":"Migration fee has been withdraw"},{"code":6041,"name":"InvalidBaseFeeMode","msg":"Invalid base fee mode"},{"code":6042,"name":"InvalidFeeRateLimiter","msg":"Invalid fee rate limiter"},{"code":6043,"name":"FailToValidateSingleSwapInstruction","msg":"Fail to validate single swap instruction in rate limiter"},{"code":6044,"name":"InvalidMigratedPoolFee","msg":"Invalid migrated pool fee params"},{"code":6045,"name":"UndeterminedError","msg":"Undertermined error"},{"code":6046,"name":"RateLimiterNotSupported","msg":"Rate limiter not supported"},{"code":6047,"name":"AmountLeftIsNotZero","msg":"Amount left is not zero"},{"code":6048,"name":"NextSqrtPriceIsSmallerThanStartSqrtPrice","msg":"Next sqrt price is smaller than start sqrt price"},{"code":6049,"name":"InvalidMinBaseFee","msg":"Invalid min base fee"},{"code":6050,"name":"AccountInvariantViolation","msg":"Account invariant violation"},{"code":6051,"name":"InvalidPoolCreationFee","msg":"Invalid pool creation fee"},{"code":6052,"name":"PoolCreationFeeHasBeenClaimed","msg":"Pool creation fee has been claimed"},{"code":6053,"name":"Unauthorized","msg":"Not permit to do this action"},{"code":6054,"name":"ZeroPoolCreationFee","msg":"Pool creation fee is zero"},{"code":6055,"name":"InvalidMigrationLockedLiquidity","msg":"Invalid migration locked liquidity"},{"code":6056,"name":"InvalidFeeMarketCapScheduler","msg":"Invalid fee market cap scheduler"},{"code":6057,"name":"FirstSwapValidationFailed","msg":"Fail to validate first swap with minimum fee"},{"code":6058,"name":"IncorrectATA","msg":"Incorrect ATA"},{"code":6059,"name":"InsufficientPoolLamports","msg":"Pool has insufficient lamports to perform the operation"},{"code":6060,"name":"InvalidPermission","msg":"Invalid permission"},{"code":6061,"name":"InvalidWithdrawProtocolFeeZapAccounts","msg":"Invalid withdraw protocol fee zap accounts"},{"code":6062,"name":"MintRestrictedFromZap","msg":"SOL,USDC protocol fee cannot be withdrawn via zap"},{"code":6063,"name":"InvalidZapOutParameters","msg":"Invalid zap out parameters"},{"code":6064,"name":"CpiDisabled","msg":"CPI disabled"},{"code":6065,"name":"MissingZapOutInstruction","msg":"Missing zap out instruction"},{"code":6066,"name":"InvalidZapAccounts","msg":"Invalid zap accounts"},{"code":6067,"name":"InvalidCompoundingParameters","msg":"Invalid compounding parameters"}],"types":[{"name":"BaseFeeConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","type":"u64"},{"name":"second_factor","type":"u64"},{"name":"third_factor","type":"u64"},{"name":"first_factor","type":"u16"},{"name":"base_fee_mode","type":"u8"},{"name":"padding_0","type":{"array":["u8",5]}}]}},{"name":"BaseFeeParameters","type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","type":"u64"},{"name":"first_factor","type":"u16"},{"name":"second_factor","type":"u64"},{"name":"third_factor","type":"u64"},{"name":"base_fee_mode","type":"u8"}]}},{"name":"ClaimFeeOperator","docs":["Parameter that set by the protocol"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"operator","docs":["operator"],"type":"pubkey"},{"name":"_padding","docs":["Reserve"],"type":{"array":["u8",128]}}]}},{"name":"Config","type":{"kind":"struct","fields":[{"name":"pool_fees","type":{"defined":{"name":"PoolFees"}}},{"name":"activation_duration","type":"u64"},{"name":"vault_config_key","type":"pubkey"},{"name":"pool_creator_authority","type":"pubkey"},{"name":"activation_type","type":"u8"},{"name":"partner_fee_numerator","type":"u64"},{"name":"padding","type":{"array":["u8",219]}}]}},{"name":"ConfigParameters","type":{"kind":"struct","fields":[{"name":"pool_fees","type":{"defined":{"name":"PoolFeeParameters"}}},{"name":"collect_fee_mode","type":"u8"},{"name":"migration_option","type":"u8"},{"name":"activation_type","type":"u8"},{"name":"token_type","type":"u8"},{"name":"token_decimal","type":"u8"},{"name":"partner_liquidity_percentage","type":"u8"},{"name":"partner_permanent_locked_liquidity_percentage","type":"u8"},{"name":"creator_liquidity_percentage","type":"u8"},{"name":"creator_permanent_locked_liquidity_percentage","type":"u8"},{"name":"migration_quote_threshold","type":"u64"},{"name":"sqrt_start_price","type":"u128"},{"name":"locked_vesting","type":{"defined":{"name":"LockedVestingParams"}}},{"name":"migration_fee_option","type":"u8"},{"name":"token_supply","type":{"option":{"defined":{"name":"TokenSupplyParams"}}}},{"name":"creator_trading_fee_percentage","type":"u8"},{"name":"token_update_authority","type":"u8"},{"name":"migration_fee","type":{"defined":{"name":"MigrationFee"}}},{"name":"migrated_pool_fee","type":{"defined":{"name":"MigratedPoolFee"}}},{"name":"pool_creation_fee","docs":["pool creation fee in SOL lamports value"],"type":"u64"},{"name":"partner_liquidity_vesting_info","type":{"defined":{"name":"LiquidityVestingInfoParams"}}},{"name":"creator_liquidity_vesting_info","type":{"defined":{"name":"LiquidityVestingInfoParams"}}},{"name":"migrated_pool_base_fee_mode","type":"u8"},{"name":"migrated_pool_market_cap_fee_scheduler_params","type":{"defined":{"name":"MigratedPoolMarketCapFeeSchedulerParams"}}},{"name":"enable_first_swap_with_min_fee","type":"bool"},{"name":"compounding_fee_bps","type":"u16"},{"name":"padding","docs":["padding for future use"],"type":{"array":["u8",2]}},{"name":"curve","type":{"vec":{"defined":{"name":"LiquidityDistributionParameters"}}}}]}},{"name":"CreatePartnerMetadataParameters","type":{"kind":"struct","fields":[{"name":"padding","type":{"array":["u8",96]}},{"name":"name","type":"string"},{"name":"website","type":"string"},{"name":"logo","type":"string"}]}},{"name":"CreateVirtualPoolMetadataParameters","type":{"kind":"struct","fields":[{"name":"padding","type":{"array":["u8",96]}},{"name":"name","type":"string"},{"name":"website","type":"string"},{"name":"logo","type":"string"}]}},{"name":"DynamicFeeConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"initialized","type":"u8"},{"name":"padding","type":{"array":["u8",7]}},{"name":"max_volatility_accumulator","type":"u32"},{"name":"variable_fee_control","type":"u32"},{"name":"bin_step","type":"u16"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"padding2","type":{"array":["u8",8]}},{"name":"bin_step_u128","type":"u128"}]}},{"name":"DynamicFeeParameters","type":{"kind":"struct","fields":[{"name":"bin_step","type":"u16"},{"name":"bin_step_u128","type":"u128"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"max_volatility_accumulator","type":"u32"},{"name":"variable_fee_control","type":"u32"}]}},{"name":"EvtClaimCreatorTradingFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"token_base_amount","type":"u64"},{"name":"token_quote_amount","type":"u64"}]}},{"name":"EvtClaimPoolCreationFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"receiver","type":"pubkey"},{"name":"creation_fee","type":"u64"}]}},{"name":"EvtClaimProtocolFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"token_base_amount","type":"u64"},{"name":"token_quote_amount","type":"u64"}]}},{"name":"EvtClaimProtocolLiquidityMigrationFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"token_base_amount","type":"u64"},{"name":"token_quote_amount","type":"u64"}]}},{"name":"EvtClaimTradingFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"token_base_amount","type":"u64"},{"name":"token_quote_amount","type":"u64"}]}},{"name":"EvtCloseClaimFeeOperator","docs":["Close claim fee operator"],"type":{"kind":"struct","fields":[{"name":"claim_fee_operator","type":"pubkey"},{"name":"operator","type":"pubkey"}]}},{"name":"EvtCreateClaimFeeOperator","docs":["Create claim fee operator"],"type":{"kind":"struct","fields":[{"name":"operator","type":"pubkey"}]}},{"name":"EvtCreateConfig","docs":["Create config"],"type":{"kind":"struct","fields":[{"name":"config","type":"pubkey"},{"name":"quote_mint","type":"pubkey"},{"name":"fee_claimer","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"pool_fees","type":{"defined":{"name":"PoolFeeParameters"}}},{"name":"collect_fee_mode","type":"u8"},{"name":"migration_option","type":"u8"},{"name":"activation_type","type":"u8"},{"name":"token_decimal","type":"u8"},{"name":"token_type","type":"u8"},{"name":"partner_permanent_locked_liquidity_percentage","type":"u8"},{"name":"partner_liquidity_percentage","type":"u8"},{"name":"creator_permanent_locked_liquidity_percentage","type":"u8"},{"name":"creator_liquidity_percentage","type":"u8"},{"name":"swap_base_amount","type":"u64"},{"name":"migration_quote_threshold","type":"u64"},{"name":"migration_base_amount","type":"u64"},{"name":"sqrt_start_price","type":"u128"},{"name":"locked_vesting","type":{"defined":{"name":"LockedVestingParams"}}},{"name":"migration_fee_option","type":"u8"},{"name":"fixed_token_supply_flag","type":"u8"},{"name":"pre_migration_token_supply","type":"u64"},{"name":"post_migration_token_supply","type":"u64"},{"name":"curve","type":{"vec":{"defined":{"name":"LiquidityDistributionParameters"}}}}]}},{"name":"EvtCreateConfigV2","type":{"kind":"struct","fields":[{"name":"config","type":"pubkey"},{"name":"quote_mint","type":"pubkey"},{"name":"fee_claimer","type":"pubkey"},{"name":"leftover_receiver","type":"pubkey"},{"name":"config_parameters","type":{"defined":{"name":"ConfigParameters"}}}]}},{"name":"EvtCreateMeteoraMigrationMetadata","type":{"kind":"struct","fields":[{"name":"virtual_pool","type":"pubkey"}]}},{"name":"EvtCreatorWithdrawSurplus","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"surplus_amount","type":"u64"}]}},{"name":"EvtCurveComplete","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"config","type":"pubkey"},{"name":"base_reserve","type":"u64"},{"name":"quote_reserve","type":"u64"}]}},{"name":"EvtInitializePool","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"config","type":"pubkey"},{"name":"creator","type":"pubkey"},{"name":"base_mint","type":"pubkey"},{"name":"pool_type","type":"u8"},{"name":"activation_point","type":"u64"}]}},{"name":"EvtPartnerClaimPoolCreationFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"partner","type":"pubkey"},{"name":"creation_fee","type":"u64"},{"name":"fee_receiver","type":"pubkey"}]}},{"name":"EvtPartnerMetadata","docs":["Create partner metadata"],"type":{"kind":"struct","fields":[{"name":"partner_metadata","type":"pubkey"},{"name":"fee_claimer","type":"pubkey"}]}},{"name":"EvtPartnerWithdrawMigrationFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"fee","type":"u64"}]}},{"name":"EvtPartnerWithdrawSurplus","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"surplus_amount","type":"u64"}]}},{"name":"EvtSwap","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"config","type":"pubkey"},{"name":"trade_direction","type":"u8"},{"name":"has_referral","type":"bool"},{"name":"params","type":{"defined":{"name":"SwapParameters"}}},{"name":"swap_result","type":{"defined":{"name":"SwapResult"}}},{"name":"amount_in","type":"u64"},{"name":"current_timestamp","type":"u64"}]}},{"name":"EvtSwap2","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"config","type":"pubkey"},{"name":"trade_direction","type":"u8"},{"name":"has_referral","type":"bool"},{"name":"swap_parameters","type":{"defined":{"name":"SwapParameters2"}}},{"name":"swap_result","type":{"defined":{"name":"SwapResult2"}}},{"name":"quote_reserve_amount","type":"u64"},{"name":"migration_threshold","type":"u64"},{"name":"current_timestamp","type":"u64"}]}},{"name":"EvtUpdatePoolCreator","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"creator","type":"pubkey"},{"name":"new_creator","type":"pubkey"}]}},{"name":"EvtVirtualPoolMetadata","docs":["Create virtual pool metadata"],"type":{"kind":"struct","fields":[{"name":"virtual_pool_metadata","type":"pubkey"},{"name":"virtual_pool","type":"pubkey"}]}},{"name":"EvtWithdrawLeftover","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"leftover_receiver","type":"pubkey"},{"name":"leftover_amount","type":"u64"}]}},{"name":"EvtWithdrawMigrationFee","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"fee","type":"u64"},{"name":"flag","type":"u8"}]}},{"name":"InitializePoolParameters","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]}},{"name":"LiquidityDistributionConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"sqrt_price","type":"u128"},{"name":"liquidity","type":"u128"}]}},{"name":"LiquidityDistributionParameters","type":{"kind":"struct","fields":[{"name":"sqrt_price","type":"u128"},{"name":"liquidity","type":"u128"}]}},{"name":"LiquidityVestingInfo","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"is_initialized","type":"u8"},{"name":"vesting_percentage","type":"u8"},{"name":"_padding","type":{"array":["u8",2]}},{"name":"bps_per_period","type":"u16"},{"name":"number_of_periods","type":"u16"},{"name":"frequency","type":"u32"},{"name":"cliff_duration_from_migration_time","type":"u32"}]}},{"name":"LiquidityVestingInfoParams","type":{"kind":"struct","fields":[{"name":"vesting_percentage","type":"u8"},{"name":"bps_per_period","type":"u16"},{"name":"number_of_periods","type":"u16"},{"name":"cliff_duration_from_migration_time","type":"u32"},{"name":"frequency","type":"u32"}]}},{"name":"LockEscrow","docs":["State of lock escrow account"],"type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"escrow_vault","type":"pubkey"},{"name":"bump","type":"u8"},{"name":"total_locked_amount","type":"u64"},{"name":"lp_per_token","type":"u128"},{"name":"unclaimed_fee_pending","type":"u64"},{"name":"a_fee","type":"u64"},{"name":"b_fee","type":"u64"}]}},{"name":"LockedVestingConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"amount_per_period","type":"u64"},{"name":"cliff_duration_from_migration_time","type":"u64"},{"name":"frequency","type":"u64"},{"name":"number_of_period","type":"u64"},{"name":"cliff_unlock_amount","type":"u64"},{"name":"_padding","type":"u64"}]}},{"name":"LockedVestingParams","type":{"kind":"struct","fields":[{"name":"amount_per_period","type":"u64"},{"name":"cliff_duration_from_migration_time","type":"u64"},{"name":"frequency","type":"u64"},{"name":"number_of_period","type":"u64"},{"name":"cliff_unlock_amount","type":"u64"}]}},{"name":"MeteoraDammMigrationMetadata","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"virtual_pool","docs":["pool"],"type":"pubkey"},{"name":"padding_0","docs":["!!! BE CAREFUL to use tombstone field, previous is pool creator"],"type":{"array":["u8",32]}},{"name":"partner","docs":["partner"],"type":"pubkey"},{"name":"lp_mint","docs":["lp mint"],"type":"pubkey"},{"name":"partner_locked_liquidity","docs":["partner locked liquidity"],"type":"u64"},{"name":"partner_liquidity","docs":["partner liquidity"],"type":"u64"},{"name":"creator_locked_liquidity","docs":["creator locked liquidity"],"type":"u64"},{"name":"creator_liquidity","docs":["creator liquidity"],"type":"u64"},{"name":"_padding_0","docs":["padding"],"type":"u8"},{"name":"creator_locked_status","docs":["flag to check whether liquidity token is locked for creator"],"type":"u8"},{"name":"partner_locked_status","docs":["flag to check whether liquidity token is locked for partner"],"type":"u8"},{"name":"creator_claim_status","docs":["flag to check whether creator has claimed liquidity token"],"type":"u8"},{"name":"partner_claim_status","docs":["flag to check whether partner has claimed liquidity token"],"type":"u8"},{"name":"_padding","docs":["Reserve"],"type":{"array":["u8",107]}}]}},{"name":"MigratedPoolFee","type":{"kind":"struct","fields":[{"name":"collect_fee_mode","type":"u8"},{"name":"dynamic_fee","type":"u8"},{"name":"pool_fee_bps","type":"u16"}]}},{"name":"MigratedPoolMarketCapFeeSchedulerParams","type":{"kind":"struct","fields":[{"name":"number_of_period","type":"u16"},{"name":"sqrt_price_step_bps","type":"u16"},{"name":"scheduler_expiration_duration","type":"u32"},{"name":"reduction_factor","type":"u64"}]}},{"name":"MigrationFee","type":{"kind":"struct","fields":[{"name":"fee_percentage","type":"u8"},{"name":"creator_fee_percentage","type":"u8"}]}},{"name":"Operator","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"whitelisted_address","type":"pubkey"},{"name":"permission","type":"u128"},{"name":"padding","type":{"array":["u64",2]}}]}},{"name":"PartnerMetadata","docs":["Metadata for a partner."],"type":{"kind":"struct","fields":[{"name":"fee_claimer","docs":["fee claimer"],"type":"pubkey"},{"name":"padding","docs":["padding for future use"],"type":{"array":["u128",6]}},{"name":"name","docs":["Name of partner."],"type":"string"},{"name":"website","docs":["Website of partner."],"type":"string"},{"name":"logo","docs":["Logo of partner"],"type":"string"}]}},{"name":"PoolConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"quote_mint","docs":["quote mint"],"type":"pubkey"},{"name":"fee_claimer","docs":["Address to get the fee"],"type":"pubkey"},{"name":"leftover_receiver","docs":["Address to receive extra base token after migration, in case token is fixed supply"],"type":"pubkey"},{"name":"pool_fees","docs":["Pool fee"],"type":{"defined":{"name":"PoolFeesConfig"}}},{"name":"partner_liquidity_vesting_info","type":{"defined":{"name":"LiquidityVestingInfo"}}},{"name":"creator_liquidity_vesting_info","type":{"defined":{"name":"LiquidityVestingInfo"}}},{"name":"padding_0","docs":["Padding for future use"],"type":{"array":["u8",14]}},{"name":"padding_1","docs":["Previously was protocol and referral fee percent. Beware of tombstone."],"type":"u16"},{"name":"collect_fee_mode","docs":["Collect fee mode"],"type":"u8"},{"name":"migration_option","docs":["migration option"],"type":"u8"},{"name":"activation_type","docs":["whether mode slot or timestamp"],"type":"u8"},{"name":"token_decimal","docs":["token decimals"],"type":"u8"},{"name":"version","docs":["version"],"type":"u8"},{"name":"token_type","docs":["token type of base token"],"type":"u8"},{"name":"quote_token_flag","docs":["quote token flag"],"type":"u8"},{"name":"partner_permanent_locked_liquidity_percentage","docs":["partner locked liquidity percentage"],"type":"u8"},{"name":"partner_liquidity_percentage","docs":["partner liquidity percentage"],"type":"u8"},{"name":"creator_permanent_locked_liquidity_percentage","docs":["creator post migration fee percentage"],"type":"u8"},{"name":"creator_liquidity_percentage","docs":["creator liquidity percentage"],"type":"u8"},{"name":"migration_fee_option","docs":["migration fee option"],"type":"u8"},{"name":"fixed_token_supply_flag","docs":["flag to indicate whether token is dynamic supply (0) or fixed supply (1)"],"type":"u8"},{"name":"creator_trading_fee_percentage","docs":["creator trading fee percentage"],"type":"u8"},{"name":"token_update_authority","docs":["token update authority"],"type":"u8"},{"name":"migration_fee_percentage","docs":["migration fee percentage"],"type":"u8"},{"name":"creator_migration_fee_percentage","docs":["creator migration fee percentage"],"type":"u8"},{"name":"padding_2","type":{"array":["u8",7]}},{"name":"swap_base_amount","docs":["swap base amount"],"type":"u64"},{"name":"migration_quote_threshold","docs":["migration quote threshold (in quote token)"],"type":"u64"},{"name":"migration_base_threshold","docs":["migration base threshold (in base token)"],"type":"u64"},{"name":"migration_sqrt_price","docs":["migration sqrt price"],"type":"u128"},{"name":"locked_vesting_config","docs":["locked vesting config"],"type":{"defined":{"name":"LockedVestingConfig"}}},{"name":"pre_migration_token_supply","docs":["pre migration token supply"],"type":"u64"},{"name":"post_migration_token_supply","docs":["post migration token supply"],"type":"u64"},{"name":"migrated_collect_fee_mode","docs":["migrated pool collect fee mode"],"type":"u8"},{"name":"migrated_dynamic_fee","docs":["migrated dynamic fee option."],"type":"u8"},{"name":"migrated_pool_fee_bps","docs":["migrated pool fee in bps"],"type":"u16"},{"name":"migrated_pool_base_fee_mode","type":"u8"},{"name":"enable_first_swap_with_min_fee","type":"u8"},{"name":"migrated_compounding_fee_bps","docs":["compounding fee bps for migrated DAMM v2 pool, should only be non-zero if migrated_collect_fee_mode is 2 (Compounding)"],"type":"u16"},{"name":"pool_creation_fee","docs":["pool creation fee in lamports value"],"type":"u64"},{"name":"migrated_pool_base_fee_bytes","docs":["serialized MigratedPoolMarketCapFeeSchedulerParams, only used when migrated_pool_base_fee_mode is market cap scheduler"],"type":{"array":["u8",16]}},{"name":"sqrt_start_price","docs":["minimum price"],"type":"u128"},{"name":"curve","docs":["curve, only use 20 point firstly, we can extend that latter"],"type":{"array":[{"defined":{"name":"LiquidityDistributionConfig"}},20]}}]}},{"name":"PoolFeeParameters","docs":["Information regarding fee charges"],"type":{"kind":"struct","fields":[{"name":"base_fee","docs":["Base fee"],"type":{"defined":{"name":"BaseFeeParameters"}}},{"name":"dynamic_fee","docs":["dynamic fee"],"type":{"option":{"defined":{"name":"DynamicFeeParameters"}}}}]}},{"name":"PoolFees","docs":["Information regarding fee charges"],"type":{"kind":"struct","fields":[{"name":"trade_fee_numerator","type":"u64"},{"name":"trade_fee_denominator","type":"u64"},{"name":"protocol_trade_fee_numerator","type":"u64"},{"name":"protocol_trade_fee_denominator","type":"u64"}]}},{"name":"PoolFeesConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"base_fee","type":{"defined":{"name":"BaseFeeConfig"}}},{"name":"dynamic_fee","type":{"defined":{"name":"DynamicFeeConfig"}}}]}},{"name":"PoolMetrics","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"total_protocol_base_fee","type":"u64"},{"name":"total_protocol_quote_fee","type":"u64"},{"name":"total_trading_base_fee","type":"u64"},{"name":"total_trading_quote_fee","type":"u64"}]}},{"name":"SwapParameters","type":{"kind":"struct","fields":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"}]}},{"name":"SwapParameters2","type":{"kind":"struct","fields":[{"name":"amount_0","docs":["When it's exact in, partial fill, this will be amount_in. When it's exact out, this will be amount_out"],"type":"u64"},{"name":"amount_1","docs":["When it's exact in, partial fill, this will be minimum_amount_out. When it's exact out, this will be maximum_amount_in"],"type":"u64"},{"name":"swap_mode","docs":["Swap mode, refer [SwapMode]"],"type":"u8"}]}},{"name":"SwapResult","docs":["Encodes all results of swapping"],"type":{"kind":"struct","fields":[{"name":"actual_input_amount","type":"u64"},{"name":"output_amount","type":"u64"},{"name":"next_sqrt_price","type":"u128"},{"name":"trading_fee","type":"u64"},{"name":"protocol_fee","type":"u64"},{"name":"referral_fee","type":"u64"}]}},{"name":"SwapResult2","type":{"kind":"struct","fields":[{"name":"included_fee_input_amount","type":"u64"},{"name":"excluded_fee_input_amount","type":"u64"},{"name":"amount_left","type":"u64"},{"name":"output_amount","type":"u64"},{"name":"next_sqrt_price","type":"u128"},{"name":"trading_fee","type":"u64"},{"name":"protocol_fee","type":"u64"},{"name":"referral_fee","type":"u64"}]}},{"name":"TokenSupplyParams","type":{"kind":"struct","fields":[{"name":"pre_migration_token_supply","docs":["pre migration token supply"],"type":"u64"},{"name":"post_migration_token_supply","docs":["post migration token supply","because DBC allow user to swap over the migration quote threshold, so in extreme case user may swap more than allowed buffer on curve","that result the total supply in post migration may be increased a bit (between pre_migration_token_supply and post_migration_token_supply)"],"type":"u64"}]}},{"name":"VirtualPool","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"volatility_tracker","docs":["volatility tracker"],"type":{"defined":{"name":"VolatilityTracker"}}},{"name":"config","docs":["config key"],"type":"pubkey"},{"name":"creator","docs":["creator"],"type":"pubkey"},{"name":"base_mint","docs":["base mint"],"type":"pubkey"},{"name":"base_vault","docs":["base vault"],"type":"pubkey"},{"name":"quote_vault","docs":["quote vault"],"type":"pubkey"},{"name":"base_reserve","docs":["base reserve"],"type":"u64"},{"name":"quote_reserve","docs":["quote reserve"],"type":"u64"},{"name":"protocol_base_fee","docs":["protocol base fee"],"type":"u64"},{"name":"protocol_quote_fee","docs":["protocol quote fee"],"type":"u64"},{"name":"partner_base_fee","docs":["partner base fee"],"type":"u64"},{"name":"partner_quote_fee","docs":["trading quote fee"],"type":"u64"},{"name":"sqrt_price","docs":["current price"],"type":"u128"},{"name":"activation_point","docs":["Activation point"],"type":"u64"},{"name":"pool_type","docs":["pool type, spl token or token2022"],"type":"u8"},{"name":"is_migrated","docs":["is migrated"],"type":"u8"},{"name":"is_partner_withdraw_surplus","docs":["is partner withdraw surplus"],"type":"u8"},{"name":"is_protocol_withdraw_surplus","docs":["is protocol withdraw surplus"],"type":"u8"},{"name":"migration_progress","docs":["migration progress"],"type":"u8"},{"name":"is_withdraw_leftover","docs":["is withdraw leftover"],"type":"u8"},{"name":"is_creator_withdraw_surplus","docs":["is creator withdraw surplus"],"type":"u8"},{"name":"migration_fee_withdraw_status","docs":["migration fee withdraw status","bit 1 (0b010) creator","bit 2 (0b100) partner"],"type":"u8"},{"name":"metrics","docs":["pool metrics"],"type":{"defined":{"name":"PoolMetrics"}}},{"name":"finish_curve_timestamp","docs":["The time curve is finished"],"type":"u64"},{"name":"creator_base_fee","docs":["creator base fee"],"type":"u64"},{"name":"creator_quote_fee","docs":["creator quote fee"],"type":"u64"},{"name":"legacy_creation_fee_bits","docs":["legacy creation fee bits, we dont use this now"],"type":"u8"},{"name":"creation_fee_bits","docs":["pool creation fee claim status"],"type":"u8"},{"name":"has_swap","docs":["Cached flag"],"type":"u8"},{"name":"_padding_0","docs":["Padding for further use"],"type":{"array":["u8",5]}},{"name":"protocol_liquidity_migration_fee_bps","type":"u16"},{"name":"_padding_1","type":{"array":["u8",6]}},{"name":"protocol_migration_base_fee_amount","type":"u64"},{"name":"protocol_migration_quote_fee_amount","type":"u64"},{"name":"_padding_2","docs":["Padding for further use"],"type":{"array":["u64",3]}}]}},{"name":"VirtualPoolMetadata","docs":["Metadata for a virtual pool."],"type":{"kind":"struct","fields":[{"name":"virtual_pool","docs":["virtual pool"],"type":"pubkey"},{"name":"padding","docs":["padding for future use"],"type":{"array":["u128",6]}},{"name":"name","docs":["Name of project."],"type":"string"},{"name":"website","docs":["Website of project."],"type":"string"},{"name":"logo","docs":["Logo of project"],"type":"string"}]}},{"name":"VolatilityTracker","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"last_update_timestamp","type":"u64"},{"name":"padding","type":{"array":["u8",8]}},{"name":"sqrt_price_reference","type":"u128"},{"name":"volatility_accumulator","type":"u128"},{"name":"volatility_reference","type":"u128"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/meteora_dlmm.LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo.json b/migration/khadhroony-bot2-reference/idls/meteora_dlmm.LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo.json new file mode 100644 index 0000000..31fdbda --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/meteora_dlmm.LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo.json @@ -0,0 +1 @@ +{"address":"LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo","metadata":{"name":"lb_clmm","version":"0.12.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"add_liquidity","discriminator":[181,157,89,67,143,182,52,72],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension","bin_array_lower","bin_array_upper"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"token_x_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_y_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"liquidity_parameter","type":{"defined":{"name":"LiquidityParameter"}}}]},{"name":"add_liquidity2","discriminator":[228,162,78,28,70,219,116,115],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"sender","signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"liquidity_parameter","type":{"defined":{"name":"LiquidityParameter"}}},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"add_liquidity_by_strategy","discriminator":[7,3,150,127,148,40,61,200],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension","bin_array_lower","bin_array_upper"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"token_x_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_y_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"liquidity_parameter","type":{"defined":{"name":"LiquidityParameterByStrategy"}}}]},{"name":"add_liquidity_by_strategy2","discriminator":[3,221,149,218,111,141,118,213],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"sender","signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"liquidity_parameter","type":{"defined":{"name":"LiquidityParameterByStrategy"}}},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"add_liquidity_by_strategy_one_side","discriminator":[41,5,238,175,100,225,6,205],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension","bin_array_lower","bin_array_upper"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token","writable":true},{"name":"reserve","writable":true},{"name":"token_mint"},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"liquidity_parameter","type":{"defined":{"name":"LiquidityParameterByStrategyOneSide"}}}]},{"name":"add_liquidity_by_weight","discriminator":[28,140,238,99,231,162,21,149],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension","bin_array_lower","bin_array_upper"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"token_x_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_y_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"liquidity_parameter","type":{"defined":{"name":"LiquidityParameterByWeight"}}}]},{"name":"add_liquidity_by_weight2","discriminator":[209,59,63,91,111,200,153,228],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"sender","signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"liquidity_parameter","type":{"defined":{"name":"LiquidityParameterByWeight"}}},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"add_liquidity_one_side","discriminator":[94,155,103,151,70,95,220,165],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension","bin_array_lower","bin_array_upper"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token","writable":true},{"name":"reserve","writable":true},{"name":"token_mint"},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"liquidity_parameter","type":{"defined":{"name":"LiquidityOneSideParameter"}}}]},{"name":"add_liquidity_one_side_precise","discriminator":[161,194,103,84,171,71,250,154],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension","bin_array_lower","bin_array_upper"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token","writable":true},{"name":"reserve","writable":true},{"name":"token_mint"},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"parameter","type":{"defined":{"name":"AddLiquiditySingleSidePreciseParameter"}}}]},{"name":"add_liquidity_one_side_precise2","discriminator":[33,51,163,201,117,98,125,231],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token","writable":true},{"name":"reserve","writable":true},{"name":"token_mint"},{"name":"sender","signer":true},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"liquidity_parameter","type":{"defined":{"name":"AddLiquiditySingleSidePreciseParameter2"}}},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"cancel_limit_order","discriminator":[132,156,132,31,67,40,232,97],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array_bitmap_extension","limit_order"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"limit_order","writable":true},{"name":"owner_token_x","docs":["When dont set it tokenAccount to prevent unnecessary create token account (rent fee) when user only withdraw token y"],"writable":true},{"name":"owner_token_y","writable":true},{"name":"owner","signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"bins","type":{"vec":"i32"}},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"claim_fee","discriminator":[169,32,79,137,136,232,70,137],"accounts":[{"name":"lb_pair","writable":true,"relations":["position","bin_array_lower","bin_array_upper"]},{"name":"position","writable":true},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"claim_fee2","discriminator":[112,191,101,171,28,144,127,187],"accounts":[{"name":"lb_pair","writable":true,"relations":["position"]},{"name":"position","writable":true},{"name":"sender","signer":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"token_program_x"},{"name":"token_program_y"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"min_bin_id","type":"i32"},{"name":"max_bin_id","type":"i32"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"claim_reward","discriminator":[149,95,181,242,94,90,158,162],"accounts":[{"name":"lb_pair","writable":true,"relations":["position","bin_array_lower","bin_array_upper"]},{"name":"position","writable":true},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"reward_vault","writable":true},{"name":"reward_mint"},{"name":"user_token_account","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u64"}]},{"name":"claim_reward2","discriminator":[190,3,127,119,178,87,157,183],"accounts":[{"name":"lb_pair","writable":true,"relations":["position"]},{"name":"position","writable":true},{"name":"sender","signer":true},{"name":"reward_vault","writable":true},{"name":"reward_mint"},{"name":"user_token_account","writable":true},{"name":"token_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u64"},{"name":"min_bin_id","type":"i32"},{"name":"max_bin_id","type":"i32"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"close_bin_array","discriminator":[68,174,88,80,181,204,19,224],"accounts":[{"name":"lb_pair","relations":["bin_array"]},{"name":"bin_array","writable":true},{"name":"rent_receiver","writable":true,"signer":true},{"name":"signer","signer":true}],"args":[]},{"name":"close_claim_fee_operator_account","discriminator":[184,213,88,31,179,101,130,36],"accounts":[{"name":"claim_fee_operator","writable":true},{"name":"rent_receiver","writable":true},{"name":"signer","signer":true}],"args":[]},{"name":"close_limit_order_if_empty","discriminator":[57,124,36,155,126,249,93,171],"accounts":[{"name":"limit_order","writable":true},{"name":"owner","signer":true},{"name":"rent_receiver","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"close_operator_account","discriminator":[171,9,213,74,120,23,3,29],"accounts":[{"name":"operator","writable":true},{"name":"signer","signer":true},{"name":"rent_receiver","writable":true}],"args":[]},{"name":"close_position","discriminator":[123,134,81,0,49,68,98,98],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"rent_receiver","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"close_position2","discriminator":[174,90,35,115,186,40,147,226],"accounts":[{"name":"position","writable":true},{"name":"sender","signer":true},{"name":"rent_receiver","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"close_position_if_empty","discriminator":[59,124,212,118,91,152,110,157],"accounts":[{"name":"position","writable":true},{"name":"sender","signer":true},{"name":"rent_receiver","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"close_preset_parameter","discriminator":[4,148,145,100,134,26,181,61],"accounts":[{"name":"preset_parameter","writable":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"rent_receiver","writable":true}],"args":[]},{"name":"close_preset_parameter2","discriminator":[39,25,95,107,116,17,115,28],"accounts":[{"name":"preset_parameter","writable":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"rent_receiver","writable":true}],"args":[]},{"name":"close_token_badge","discriminator":[108,146,86,110,179,254,10,104],"accounts":[{"name":"token_badge","writable":true},{"name":"rent_receiver","writable":true},{"name":"operator"},{"name":"signer","signer":true}],"args":[]},{"name":"create_operator_account","discriminator":[221,64,246,149,240,153,229,163],"accounts":[{"name":"operator","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,111,114]},{"kind":"account","path":"whitelisted_signer"}]}},{"name":"whitelisted_signer"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"permission","type":"u128"}]},{"name":"decrease_position_length","discriminator":[194,219,136,32,25,96,105,37],"accounts":[{"name":"rent_receiver","writable":true},{"name":"position","writable":true},{"name":"owner","signer":true,"relations":["position"]},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"length_to_remove","type":"u16"},{"name":"side","type":"u8"}]},{"name":"for_idl_type_generation_do_not_call","discriminator":[180,105,69,80,95,50,73,108],"accounts":[{"name":"dummy_zc_account"}],"args":[{"name":"_ix","type":{"defined":{"name":"DummyIx"}}}]},{"name":"fund_reward","discriminator":[188,50,249,165,93,151,38,63],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array"]},{"name":"reward_vault","writable":true},{"name":"reward_mint"},{"name":"funder_token_account","writable":true},{"name":"funder","signer":true},{"name":"bin_array","writable":true},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u64"},{"name":"amount","type":"u64"},{"name":"carry_forward","type":"bool"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"go_to_a_bin","discriminator":[146,72,174,224,40,253,84,174],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array_bitmap_extension","from_bin_array","to_bin_array"]},{"name":"bin_array_bitmap_extension","optional":true},{"name":"from_bin_array","optional":true},{"name":"to_bin_array","optional":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"bin_id","type":"i32"}]},{"name":"increase_oracle_length","discriminator":[190,61,125,87,103,79,158,173],"accounts":[{"name":"oracle","writable":true},{"name":"funder","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"length_to_add","type":"u64"}]},{"name":"increase_position_length","discriminator":[80,83,117,211,66,13,33,149],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"lb_pair","relations":["position"]},{"name":"position","writable":true},{"name":"owner","signer":true,"relations":["position"]},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"length_to_add","type":"u16"},{"name":"side","type":"u8"}]},{"name":"increase_position_length2","discriminator":[255,210,204,71,115,137,225,113],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"lb_pair","relations":["position"]},{"name":"position","writable":true},{"name":"owner","signer":true,"relations":["position"]},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"minimum_upper_bin_id","type":"i32"}]},{"name":"initialize_bin_array","discriminator":[35,86,19,185,78,212,75,211],"accounts":[{"name":"lb_pair"},{"name":"bin_array","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,105,110,95,97,114,114,97,121]},{"kind":"account","path":"lb_pair"},{"kind":"arg","path":"index"}]}},{"name":"funder","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"i64"}]},{"name":"initialize_bin_array_bitmap_extension","discriminator":[47,157,226,180,12,240,33,71],"accounts":[{"name":"lb_pair"},{"name":"bin_array_bitmap_extension","docs":["Initialize an account to store if a bin array is initialized."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[98,105,116,109,97,112]},{"kind":"account","path":"lb_pair"}]}},{"name":"funder","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent"}],"args":[]},{"name":"initialize_customizable_permissionless_lb_pair","discriminator":[46,39,41,135,111,183,200,64],"accounts":[{"name":"lb_pair","writable":true},{"name":"bin_array_bitmap_extension","writable":true,"optional":true,"pda":{"seeds":[{"kind":"const","value":[98,105,116,109,97,112]},{"kind":"account","path":"lb_pair"}]}},{"name":"token_mint_x"},{"name":"token_mint_y"},{"name":"reserve_x","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_x"}]}},{"name":"reserve_y","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_y"}]}},{"name":"oracle","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"lb_pair"}]}},{"name":"user_token_x"},{"name":"funder","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"user_token_y"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"CustomizableParams"}}}]},{"name":"initialize_customizable_permissionless_lb_pair2","discriminator":[243,73,129,126,51,19,241,107],"accounts":[{"name":"lb_pair","writable":true},{"name":"bin_array_bitmap_extension","writable":true,"optional":true,"pda":{"seeds":[{"kind":"const","value":[98,105,116,109,97,112]},{"kind":"account","path":"lb_pair"}]}},{"name":"token_mint_x"},{"name":"token_mint_y"},{"name":"reserve_x","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_x"}]}},{"name":"reserve_y","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_y"}]}},{"name":"oracle","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"lb_pair"}]}},{"name":"user_token_x"},{"name":"funder","writable":true,"signer":true},{"name":"token_badge_x","optional":true},{"name":"token_badge_y","optional":true},{"name":"token_program_x"},{"name":"token_program_y"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"user_token_y"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"CustomizableParams"}}}]},{"name":"initialize_lb_pair","discriminator":[45,154,237,210,221,15,166,92],"accounts":[{"name":"lb_pair","writable":true},{"name":"bin_array_bitmap_extension","writable":true,"optional":true,"pda":{"seeds":[{"kind":"const","value":[98,105,116,109,97,112]},{"kind":"account","path":"lb_pair"}]}},{"name":"token_mint_x"},{"name":"token_mint_y"},{"name":"reserve_x","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_x"}]}},{"name":"reserve_y","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_y"}]}},{"name":"oracle","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"lb_pair"}]}},{"name":"preset_parameter"},{"name":"funder","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"active_id","type":"i32"},{"name":"bin_step","type":"u16"}]},{"name":"initialize_lb_pair2","discriminator":[73,59,36,120,237,83,108,198],"accounts":[{"name":"lb_pair","writable":true},{"name":"bin_array_bitmap_extension","writable":true,"optional":true,"pda":{"seeds":[{"kind":"const","value":[98,105,116,109,97,112]},{"kind":"account","path":"lb_pair"}]}},{"name":"token_mint_x"},{"name":"token_mint_y"},{"name":"reserve_x","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_x"}]}},{"name":"reserve_y","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_y"}]}},{"name":"oracle","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"lb_pair"}]}},{"name":"preset_parameter"},{"name":"funder","writable":true,"signer":true},{"name":"token_badge_x","optional":true},{"name":"token_badge_y","optional":true},{"name":"token_program_x"},{"name":"token_program_y"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"InitializeLbPair2Params"}}}]},{"name":"initialize_permission_lb_pair","discriminator":[108,102,213,85,251,3,53,21],"accounts":[{"name":"base","signer":true},{"name":"lb_pair","writable":true},{"name":"bin_array_bitmap_extension","writable":true,"optional":true,"pda":{"seeds":[{"kind":"const","value":[98,105,116,109,97,112]},{"kind":"account","path":"lb_pair"}]}},{"name":"token_mint_x"},{"name":"token_mint_y"},{"name":"reserve_x","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_x"}]}},{"name":"reserve_y","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"account","path":"token_mint_y"}]}},{"name":"oracle","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"lb_pair"}]}},{"name":"payer","writable":true,"signer":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"token_badge_x","optional":true},{"name":"token_badge_y","optional":true},{"name":"token_program_x"},{"name":"token_program_y"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"ix_data","type":{"defined":{"name":"InitPermissionPairIx"}}}]},{"name":"initialize_position","discriminator":[219,192,234,71,190,191,102,80],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"position","writable":true,"signer":true},{"name":"lb_pair"},{"name":"owner","signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"lower_bin_id","type":"i32"},{"name":"width","type":"i32"}]},{"name":"initialize_position2","discriminator":[143,19,242,145,213,15,104,115],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"position","writable":true,"signer":true},{"name":"lb_pair"},{"name":"owner","signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"lower_bin_id","type":"i32"},{"name":"width","type":"i32"}]},{"name":"initialize_position_by_operator","discriminator":[251,189,190,244,117,254,35,148],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"base","signer":true},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"lb_pair"},{"kind":"account","path":"base"},{"kind":"arg","path":"lower_bin_id"},{"kind":"arg","path":"width"}]}},{"name":"lb_pair"},{"name":"owner"},{"name":"operator","docs":["operator"],"signer":true},{"name":"operator_token_x"},{"name":"owner_token_x"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"lower_bin_id","type":"i32"},{"name":"width","type":"i32"},{"name":"fee_owner","type":"pubkey"},{"name":"lock_release_point","type":"u64"}]},{"name":"initialize_position_pda","discriminator":[46,82,125,146,85,141,228,153],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"base","signer":true},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"lb_pair"},{"kind":"account","path":"base"},{"kind":"arg","path":"lower_bin_id"},{"kind":"arg","path":"width"}]}},{"name":"lb_pair"},{"name":"owner","docs":["owner"],"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"lower_bin_id","type":"i32"},{"name":"width","type":"i32"}]},{"name":"initialize_preset_parameter","discriminator":[66,188,71,211,98,109,14,186],"accounts":[{"name":"preset_parameter","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,101,115,101,116,95,112,97,114,97,109,101,116,101,114,50]},{"kind":"arg","path":"ix.index"}]}},{"name":"operator"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"ix","type":{"defined":{"name":"InitPresetParametersIx"}}}]},{"name":"initialize_reward","discriminator":[95,135,192,196,242,129,230,68],"accounts":[{"name":"lb_pair","writable":true},{"name":"reward_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"lb_pair"},{"kind":"arg","path":"reward_index"}]}},{"name":"reward_mint"},{"name":"token_badge","optional":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u64"},{"name":"reward_duration","type":"u64"},{"name":"funder","type":"pubkey"}]},{"name":"initialize_token_badge","discriminator":[253,77,205,95,27,224,89,223],"accounts":[{"name":"token_mint"},{"name":"token_badge","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,98,97,100,103,101]},{"kind":"account","path":"token_mint"}]}},{"name":"operator"},{"name":"signer","signer":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"place_limit_order","discriminator":[108,176,33,186,146,229,1,197],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"reserve","writable":true},{"name":"token_mint"},{"name":"limit_order","writable":true,"signer":true},{"name":"payer","writable":true,"signer":true},{"name":"owner"},{"name":"user_token","writable":true},{"name":"sender","signer":true},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"PlaceLimitOrderParams"}}},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"rebalance_liquidity","discriminator":[92,4,176,193,119,185,83,9],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"owner","signer":true,"relations":["position"]},{"name":"rent_payer","writable":true,"signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"RebalanceLiquidityParams"}}},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"remove_all_liquidity","discriminator":[10,51,61,35,112,105,24,85],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension","bin_array_lower","bin_array_upper"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"token_x_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_y_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"remove_liquidity","discriminator":[80,85,209,72,24,206,177,108],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension","bin_array_lower","bin_array_upper"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"token_x_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_y_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"bin_liquidity_removal","type":{"vec":{"defined":{"name":"BinLiquidityReduction"}}}}]},{"name":"remove_liquidity2","discriminator":[230,215,82,127,241,101,227,146],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"sender","signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"bin_liquidity_removal","type":{"vec":{"defined":{"name":"BinLiquidityReduction"}}}},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"remove_liquidity_by_range","discriminator":[26,82,102,152,240,74,105,26],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension","bin_array_lower","bin_array_upper"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"sender","signer":true},{"name":"token_x_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_y_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"from_bin_id","type":"i32"},{"name":"to_bin_id","type":"i32"},{"name":"bps_to_remove","type":"u16"}]},{"name":"remove_liquidity_by_range2","discriminator":[204,2,195,145,53,145,145,205],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"user_token_x","writable":true},{"name":"user_token_y","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"sender","signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"from_bin_id","type":"i32"},{"name":"to_bin_id","type":"i32"},{"name":"bps_to_remove","type":"u16"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"set_activation_point","discriminator":[91,249,15,165,26,129,254,125],"accounts":[{"name":"lb_pair","writable":true},{"name":"signer","signer":true}],"args":[{"name":"activation_point","type":"u64"}]},{"name":"set_pair_status","discriminator":[67,248,231,137,154,149,217,174],"accounts":[{"name":"lb_pair","writable":true},{"name":"operator"},{"name":"signer","signer":true}],"args":[{"name":"status","type":"u8"}]},{"name":"set_pair_status_permissionless","discriminator":[78,59,152,211,70,183,46,208],"accounts":[{"name":"lb_pair","writable":true},{"name":"signer","signer":true}],"args":[{"name":"status","type":"u8"}]},{"name":"set_permissionless_operation_bits","discriminator":[84,58,203,139,163,81,190,186],"accounts":[{"name":"position","writable":true},{"name":"owner","signer":true,"relations":["position"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"bits","type":"u8"}]},{"name":"set_pre_activation_duration","discriminator":[165,61,201,244,130,159,22,100],"accounts":[{"name":"lb_pair","writable":true},{"name":"signer","signer":true}],"args":[{"name":"pre_activation_duration","type":"u64"}]},{"name":"set_pre_activation_swap_address","discriminator":[57,139,47,123,216,80,223,10],"accounts":[{"name":"lb_pair","writable":true},{"name":"signer","signer":true}],"args":[{"name":"pre_activation_swap_address","type":"pubkey"}]},{"name":"swap","discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"oracle","writable":true,"relations":["lb_pair"]},{"name":"host_fee_in","writable":true,"optional":true},{"name":"user","signer":true},{"name":"token_x_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_y_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_in","type":"u64"},{"name":"min_amount_out","type":"u64"}]},{"name":"swap2","discriminator":[65,75,63,76,235,91,91,136],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"oracle","writable":true,"relations":["lb_pair"]},{"name":"host_fee_in","writable":true,"optional":true},{"name":"user","signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_in","type":"u64"},{"name":"min_amount_out","type":"u64"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"swap_exact_out","discriminator":[250,73,101,33,38,207,75,184],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"oracle","writable":true,"relations":["lb_pair"]},{"name":"host_fee_in","writable":true,"optional":true},{"name":"user","signer":true},{"name":"token_x_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_y_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"max_in_amount","type":"u64"},{"name":"out_amount","type":"u64"}]},{"name":"swap_exact_out2","discriminator":[43,215,247,132,137,60,243,81],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"oracle","writable":true,"relations":["lb_pair"]},{"name":"host_fee_in","writable":true,"optional":true},{"name":"user","signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"max_in_amount","type":"u64"},{"name":"out_amount","type":"u64"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"swap_with_price_impact","discriminator":[56,173,230,208,173,228,156,205],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"oracle","writable":true,"relations":["lb_pair"]},{"name":"host_fee_in","writable":true,"optional":true},{"name":"user","signer":true},{"name":"token_x_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_y_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_in","type":"u64"},{"name":"active_id","type":{"option":"i32"}},{"name":"max_price_impact_bps","type":"u16"}]},{"name":"swap_with_price_impact2","discriminator":[74,98,192,214,177,51,75,51],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array_bitmap_extension"]},{"name":"bin_array_bitmap_extension","writable":true,"optional":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"oracle","writable":true,"relations":["lb_pair"]},{"name":"host_fee_in","writable":true,"optional":true},{"name":"user","signer":true},{"name":"token_x_program"},{"name":"token_y_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_in","type":"u64"},{"name":"active_id","type":{"option":"i32"}},{"name":"max_price_impact_bps","type":"u16"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"update_base_fee_parameters","discriminator":[75,168,223,161,16,195,3,47],"accounts":[{"name":"lb_pair","writable":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"fee_parameter","type":{"defined":{"name":"BaseFeeParameter"}}}]},{"name":"update_dynamic_fee_parameters","discriminator":[92,161,46,246,255,189,22,22],"accounts":[{"name":"lb_pair","writable":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"fee_parameter","type":{"defined":{"name":"DynamicFeeParameter"}}}]},{"name":"update_fees_and_reward2","discriminator":[32,142,184,154,103,65,184,88],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position"]},{"name":"owner","signer":true}],"args":[{"name":"min_bin_id","type":"i32"},{"name":"max_bin_id","type":"i32"}]},{"name":"update_fees_and_rewards","discriminator":[154,230,250,13,236,209,75,223],"accounts":[{"name":"position","writable":true},{"name":"lb_pair","writable":true,"relations":["position","bin_array_lower","bin_array_upper"]},{"name":"bin_array_lower","writable":true},{"name":"bin_array_upper","writable":true},{"name":"owner","signer":true}],"args":[]},{"name":"update_position_operator","discriminator":[202,184,103,143,180,191,116,217],"accounts":[{"name":"position","writable":true},{"name":"owner","signer":true,"relations":["position"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"operator","type":"pubkey"}]},{"name":"update_reward_duration","discriminator":[138,174,196,169,213,235,254,107],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array"]},{"name":"operator"},{"name":"signer","signer":true},{"name":"bin_array","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u64"},{"name":"new_duration","type":"u64"}]},{"name":"update_reward_funder","discriminator":[211,28,48,32,215,160,35,23],"accounts":[{"name":"lb_pair","writable":true},{"name":"operator"},{"name":"signer","signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u64"},{"name":"new_funder","type":"pubkey"}]},{"name":"withdraw_ineligible_reward","discriminator":[148,206,42,195,247,49,103,8],"accounts":[{"name":"lb_pair","writable":true,"relations":["bin_array"]},{"name":"reward_vault","writable":true},{"name":"reward_mint"},{"name":"funder_token_account","writable":true},{"name":"funder","signer":true},{"name":"bin_array","writable":true},{"name":"token_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"reward_index","type":"u64"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"withdraw_protocol_fee","discriminator":[158,201,158,189,33,93,162,103],"accounts":[{"name":"lb_pair","writable":true},{"name":"reserve_x","writable":true,"relations":["lb_pair"]},{"name":"reserve_y","writable":true,"relations":["lb_pair"]},{"name":"token_x_mint","relations":["lb_pair"]},{"name":"token_y_mint","relations":["lb_pair"]},{"name":"receiver_token_x","writable":true},{"name":"receiver_token_y","writable":true},{"name":"operator"},{"name":"signer","docs":["operator"],"signer":true},{"name":"token_x_program"},{"name":"token_y_program"}],"args":[{"name":"max_amount_x","type":"u64"},{"name":"max_amount_y","type":"u64"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]},{"name":"zap_protocol_fee","discriminator":[213,155,187,34,56,182,91,240],"accounts":[{"name":"lb_pair","writable":true},{"name":"reserve","writable":true},{"name":"token_mint"},{"name":"receiver_token","writable":true},{"name":"operator"},{"name":"signer","docs":["operator"],"signer":true},{"name":"token_program"},{"name":"sysvar_instructions","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[{"name":"max_amount","type":"u64"},{"name":"remaining_accounts_info","type":{"defined":{"name":"RemainingAccountsInfo"}}}]}],"accounts":[{"name":"BinArray","discriminator":[92,142,92,220,5,148,70,181]},{"name":"BinArrayBitmapExtension","discriminator":[80,111,124,113,55,237,18,5]},{"name":"ClaimFeeOperator","discriminator":[166,48,134,86,34,200,188,150]},{"name":"DummyZcAccount","discriminator":[94,107,238,80,208,48,180,8]},{"name":"LbPair","discriminator":[33,11,49,98,181,101,177,13]},{"name":"LimitOrder","discriminator":[137,183,212,91,115,29,141,227]},{"name":"Operator","discriminator":[219,31,188,145,69,139,204,117]},{"name":"Oracle","discriminator":[139,194,131,179,140,179,229,244]},{"name":"PositionV2","discriminator":[117,176,212,199,245,180,133,182]},{"name":"PresetParameter","discriminator":[242,62,244,34,181,112,58,170]},{"name":"PresetParameter2","discriminator":[171,236,148,115,162,113,222,174]},{"name":"TokenBadge","discriminator":[116,219,204,229,249,116,255,150]}],"events":[{"name":"AddLiquidity","discriminator":[31,94,125,90,227,52,61,186]},{"name":"CancelLimitOrderEvt","discriminator":[131,234,194,133,9,14,189,209]},{"name":"ClaimFee","discriminator":[75,122,154,48,140,74,123,163]},{"name":"ClaimFee2","discriminator":[232,171,242,97,58,77,35,45]},{"name":"ClaimReward","discriminator":[148,116,134,204,22,171,85,95]},{"name":"ClaimReward2","discriminator":[27,143,244,33,80,43,110,146]},{"name":"CloseLimitOrderEvt","discriminator":[142,135,8,76,92,63,118,83]},{"name":"CompositionFee","discriminator":[128,151,123,106,17,102,113,142]},{"name":"DecreasePositionLength","discriminator":[52,118,235,85,172,169,15,128]},{"name":"DynamicFeeParameterUpdate","discriminator":[88,88,178,135,194,146,91,243]},{"name":"FeeParameterUpdate","discriminator":[48,76,241,117,144,215,242,44]},{"name":"FundReward","discriminator":[246,228,58,130,145,170,79,204]},{"name":"GoToABin","discriminator":[59,138,76,68,138,131,176,67]},{"name":"IncreaseObservation","discriminator":[99,249,17,121,166,156,207,215]},{"name":"IncreasePositionLength","discriminator":[157,239,42,204,30,56,223,46]},{"name":"InitializeReward","discriminator":[211,153,88,62,149,60,177,70]},{"name":"LbPairCreate","discriminator":[185,74,252,125,27,215,188,111]},{"name":"PlaceLimitOrderEvt","discriminator":[43,79,27,169,244,28,225,63]},{"name":"PositionClose","discriminator":[255,196,16,107,28,202,53,128]},{"name":"PositionCreate","discriminator":[144,142,252,84,157,53,37,121]},{"name":"Rebalancing","discriminator":[0,109,117,179,61,91,199,200]},{"name":"RemoveLiquidity","discriminator":[116,244,97,232,103,31,152,58]},{"name":"SetPositionPermissionlessOperationBitsEvt","discriminator":[195,229,147,245,29,125,48,168]},{"name":"Swap","discriminator":[81,108,227,190,205,208,10,196]},{"name":"Swap2Evt","discriminator":[46,116,82,215,148,27,84,77]},{"name":"UpdatePositionLockReleasePoint","discriminator":[133,214,66,224,64,12,7,191]},{"name":"UpdatePositionOperator","discriminator":[39,115,48,204,246,47,66,57]},{"name":"UpdateRewardDuration","discriminator":[223,245,224,153,49,29,163,172]},{"name":"UpdateRewardFunder","discriminator":[224,178,174,74,252,165,85,180]},{"name":"WithdrawIneligibleReward","discriminator":[231,189,65,149,102,215,154,244]}],"errors":[{"code":6000,"name":"InvalidStartBinIndex","msg":"Invalid start bin index"},{"code":6001,"name":"InvalidBinId","msg":"Invalid bin id"},{"code":6002,"name":"InvalidInput","msg":"Invalid input data"},{"code":6003,"name":"ExceededAmountSlippageTolerance","msg":"Exceeded amount slippage tolerance"},{"code":6004,"name":"ExceededBinSlippageTolerance","msg":"Exceeded bin slippage tolerance"},{"code":6005,"name":"CompositionFactorFlawed","msg":"Composition factor flawed"},{"code":6006,"name":"NonPresetBinStep","msg":"Non preset bin step"},{"code":6007,"name":"ZeroLiquidity","msg":"Zero liquidity"},{"code":6008,"name":"InvalidPosition","msg":"Invalid position"},{"code":6009,"name":"BinArrayNotFound","msg":"Bin array not found"},{"code":6010,"name":"InvalidTokenMint","msg":"Invalid token mint"},{"code":6011,"name":"InvalidAccountForSingleDeposit","msg":"Invalid account for single deposit"},{"code":6012,"name":"PairInsufficientLiquidity","msg":"Pair insufficient liquidity"},{"code":6013,"name":"InvalidFeeOwner","msg":"Invalid fee owner"},{"code":6014,"name":"InvalidFeeWithdrawAmount","msg":"Invalid fee withdraw amount"},{"code":6015,"name":"InvalidAdmin","msg":"Invalid admin"},{"code":6016,"name":"IdenticalFeeOwner","msg":"Identical fee owner"},{"code":6017,"name":"InvalidBps","msg":"Invalid basis point"},{"code":6018,"name":"MathOverflow","msg":"Math operation overflow"},{"code":6019,"name":"TypeCastFailed","msg":"Type cast error"},{"code":6020,"name":"InvalidRewardIndex","msg":"Invalid reward index"},{"code":6021,"name":"InvalidRewardDuration","msg":"Invalid reward duration"},{"code":6022,"name":"RewardInitialized","msg":"Reward already initialized"},{"code":6023,"name":"RewardUninitialized","msg":"Reward not initialized"},{"code":6024,"name":"IdenticalFunder","msg":"Identical funder"},{"code":6025,"name":"RewardCampaignInProgress","msg":"Reward campaign in progress"},{"code":6026,"name":"IdenticalRewardDuration","msg":"Reward duration is the same"},{"code":6027,"name":"InvalidBinArray","msg":"Invalid bin array"},{"code":6028,"name":"NonContinuousBinArrays","msg":"Bin arrays must be continuous"},{"code":6029,"name":"InvalidRewardVault","msg":"Invalid reward vault"},{"code":6030,"name":"NonEmptyPosition","msg":"Position is not empty"},{"code":6031,"name":"UnauthorizedAccess","msg":"Unauthorized access"},{"code":6032,"name":"InvalidFeeParameter","msg":"Invalid fee parameter"},{"code":6033,"name":"MissingOracle","msg":"Missing oracle account"},{"code":6034,"name":"InsufficientSample","msg":"Insufficient observation sample"},{"code":6035,"name":"InvalidLookupTimestamp","msg":"Invalid lookup timestamp"},{"code":6036,"name":"BitmapExtensionAccountIsNotProvided","msg":"Bitmap extension account is not provided"},{"code":6037,"name":"CannotFindNonZeroLiquidityBinArrayId","msg":"Cannot find non-zero liquidity binArrayId"},{"code":6038,"name":"BinIdOutOfBound","msg":"Bin id out of bound"},{"code":6039,"name":"InsufficientOutAmount","msg":"Insufficient amount in for minimum out"},{"code":6040,"name":"InvalidPositionWidth","msg":"Invalid position width"},{"code":6041,"name":"ExcessiveFeeUpdate","msg":"Excessive fee update"},{"code":6042,"name":"PoolDisabled","msg":"Pool disabled"},{"code":6043,"name":"InvalidPoolType","msg":"Invalid pool type"},{"code":6044,"name":"ExceedMaxWhitelist","msg":"Whitelist for wallet is full"},{"code":6045,"name":"InvalidIndex","msg":"Invalid index"},{"code":6046,"name":"RewardNotEnded","msg":"Reward not ended"},{"code":6047,"name":"MustWithdrawnIneligibleReward","msg":"Must withdraw ineligible reward"},{"code":6048,"name":"UnauthorizedAddress","msg":"Unauthorized address"},{"code":6049,"name":"OperatorsAreTheSame","msg":"Cannot update because operators are the same"},{"code":6050,"name":"WithdrawToWrongTokenAccount","msg":"Withdraw to wrong token account"},{"code":6051,"name":"WrongRentReceiver","msg":"Wrong rent receiver"},{"code":6052,"name":"AlreadyPassActivationPoint","msg":"Already activated"},{"code":6053,"name":"ExceedMaxSwappedAmount","msg":"Swapped amount is exceeded max swapped amount"},{"code":6054,"name":"InvalidStrategyParameters","msg":"Invalid strategy parameters"},{"code":6055,"name":"LiquidityLocked","msg":"Liquidity locked"},{"code":6056,"name":"BinRangeIsNotEmpty","msg":"Bin range is not empty"},{"code":6057,"name":"NotExactAmountOut","msg":"Amount out is not matched with exact amount out"},{"code":6058,"name":"InvalidActivationType","msg":"Invalid activation type"},{"code":6059,"name":"InvalidActivationDuration","msg":"Invalid activation duration"},{"code":6060,"name":"MissingTokenAmountAsTokenLaunchProof","msg":"Missing token amount as token launch owner proof"},{"code":6061,"name":"InvalidQuoteToken","msg":"Quote token must be SOL or USDC"},{"code":6062,"name":"InvalidBinStep","msg":"Invalid bin step"},{"code":6063,"name":"InvalidBaseFee","msg":"Invalid base fee"},{"code":6064,"name":"InvalidPreActivationDuration","msg":"Invalid pre-activation duration"},{"code":6065,"name":"AlreadyPassPreActivationSwapPoint","msg":"Already pass pre-activation swap point"},{"code":6066,"name":"InvalidStatus","msg":"Invalid status"},{"code":6067,"name":"ExceededMaxOracleLength","msg":"Exceed max oracle length"},{"code":6068,"name":"InvalidMinimumLiquidity","msg":"Invalid minimum liquidity"},{"code":6069,"name":"NotSupportMint","msg":"Not support token_2022 mint extension"},{"code":6070,"name":"UnsupportedMintExtension","msg":"Unsupported mint extension"},{"code":6071,"name":"UnsupportNativeMintToken2022","msg":"Unsupported native mint token2022"},{"code":6072,"name":"UnmatchTokenMint","msg":"Unmatch token mint"},{"code":6073,"name":"UnsupportedTokenMint","msg":"Unsupported token mint"},{"code":6074,"name":"InsufficientRemainingAccounts","msg":"Insufficient remaining accounts"},{"code":6075,"name":"InvalidRemainingAccountSlice","msg":"Invalid remaining account slice"},{"code":6076,"name":"DuplicatedRemainingAccountTypes","msg":"Duplicated remaining account types"},{"code":6077,"name":"MissingRemainingAccountForTransferHook","msg":"Missing remaining account for transfer hook"},{"code":6078,"name":"NoTransferHookProgram","msg":"Remaining account was passed for transfer hook but there's no hook program"},{"code":6079,"name":"ZeroFundedAmount","msg":"Zero funded amount"},{"code":6080,"name":"InvalidSide","msg":"Invalid side"},{"code":6081,"name":"InvalidResizeLength","msg":"Invalid resize length"},{"code":6082,"name":"NotSupportAtTheMoment","msg":"Not support at the moment"},{"code":6083,"name":"InvalidRebalanceParameters","msg":"Invalid rebalance parameters"},{"code":6084,"name":"InvalidRewardAccounts","msg":"Invalid reward accounts"},{"code":6085,"name":"UndeterminedError","msg":"Undetermined error"},{"code":6086,"name":"ReallocExceedMaxLengthPerInstruction","msg":"Realloc exceed max length per instruction"},{"code":6087,"name":"InvalidBaseFeeMantissa","msg":"Mantissa cannot more than two significant digits"},{"code":6088,"name":"InvalidPositionOwner","msg":"Invalid position owner"},{"code":6089,"name":"InvalidPoolAddress","msg":"Invalid pool address"},{"code":6090,"name":"InvalidTokenBadgeType","msg":"Invalid token badge type"},{"code":6091,"name":"InvalidTransferHookAuthority","msg":"Invalid transfer hook authority"},{"code":6092,"name":"AmountXIsNegative","msg":"Amount x is negative"},{"code":6093,"name":"AmountYIsNegative","msg":"Amount y is negative"},{"code":6094,"name":"InvalidPoolCreator","msg":"Invalid pool creator"},{"code":6095,"name":"InvalidFunctionType","msg":"Invalid function type"},{"code":6096,"name":"InvalidPermission","msg":"Invalid permission"},{"code":6097,"name":"IncorrectATA","msg":"Incorrect ATA"},{"code":6098,"name":"InvalidWithdrawProtocolFeeZapAccounts","msg":"Invalid withdraw protocol fee zap accounts"},{"code":6099,"name":"MintRestrictedFromZap","msg":"SOL,USDC protocol fee cannot be withdrawn via zap"},{"code":6100,"name":"CpiDisabled","msg":"CPI disabled"},{"code":6101,"name":"MissingZapOutInstruction","msg":"Missing zap out instruction"},{"code":6102,"name":"InvalidZapAccounts","msg":"Invalid zap accounts"},{"code":6103,"name":"InvalidZapOutParameters","msg":"Invalid zap out parameters"},{"code":6104,"name":"InsufficientInAmount","msg":"Insufficient in amount"},{"code":6105,"name":"InvalidPlaceLimitOrderParameters","msg":"Invalid place limit order parameters"},{"code":6106,"name":"InvalidLimitOrderOwner","msg":"Invalid limit order owner"},{"code":6107,"name":"InvalidCancelLimitOrderParameters","msg":"Invalid cancel limit order parameters"},{"code":6108,"name":"CannotFindLimitOrderByBinId","msg":"Cannot find limit order by bin id"},{"code":6109,"name":"CancelNonEmptyLimitOrder","msg":"Cannot cancel non-empty limit order"},{"code":6110,"name":"InvalidCollectFeeMode","msg":"Invalid collect fee mode"}],"types":[{"name":"AccountsType","type":{"kind":"enum","variants":[{"name":"TransferHookX"},{"name":"TransferHookY"},{"name":"TransferHookReward"},{"name":"TransferHookMultiReward","fields":["u8"]},{"name":"TransferHookReferral"}]}},{"name":"ActivationType","docs":["Type of the activation"],"repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Slot"},{"name":"Timestamp"}]}},{"name":"AddLiquidity","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"from","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"amounts","type":{"array":["u64",2]}},{"name":"active_bin_id","type":"i32"}]}},{"name":"AddLiquidityParams","type":{"kind":"struct","fields":[{"name":"min_delta_id","type":"i32"},{"name":"max_delta_id","type":"i32"},{"name":"x0","type":"u64"},{"name":"y0","type":"u64"},{"name":"delta_x","type":"u64"},{"name":"delta_y","type":"u64"},{"name":"bit_flag","type":"u8"},{"name":"favor_x_in_active_id","type":"bool"},{"name":"padding","type":{"array":["u8",16]}}]}},{"name":"AddLiquiditySingleSidePreciseParameter","type":{"kind":"struct","fields":[{"name":"bins","type":{"vec":{"defined":{"name":"CompressedBinDepositAmount"}}}},{"name":"decompress_multiplier","type":"u64"}]}},{"name":"AddLiquiditySingleSidePreciseParameter2","type":{"kind":"struct","fields":[{"name":"bins","type":{"vec":{"defined":{"name":"CompressedBinDepositAmount"}}}},{"name":"decompress_multiplier","type":"u64"},{"name":"max_amount","type":"u64"}]}},{"name":"BaseFeeParameter","type":{"kind":"struct","fields":[{"name":"protocol_share","docs":["Portion of swap fees retained by the protocol by controlling protocol_share parameter. protocol_swap_fee = protocol_share * total_swap_fee"],"type":"u16"},{"name":"base_factor","docs":["Base factor for base fee rate"],"type":"u16"},{"name":"base_fee_power_factor","docs":["Base fee power factor"],"type":"u8"}]}},{"name":"Bin","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"amount_x","docs":["Amount of token X in the bin for market making. This already excluded protocol fees."],"type":"u64"},{"name":"amount_y","docs":["Amount of token Y in the bin for market making. This already excluded protocol fees."],"type":"u64"},{"name":"price","docs":["Bin price"],"type":"u128"},{"name":"liquidity_supply","docs":["Bin MM liquidity supply."],"type":"u128"},{"name":"fulfilled_order_amount_x","docs":["Total fulfilled order amount x"],"type":"u64"},{"name":"fulfilled_order_amount_y","docs":["Total fulfilled order amount y"],"type":"u64"},{"name":"limit_order_fee_ask_side","docs":["Limit order fee collected by ask side orders"],"type":"u64"},{"name":"limit_order_fee_bid_side","docs":["Limit order fee collected by bid side orders"],"type":"u64"},{"name":"fee_amount_x_per_token_stored","docs":["Swap fee amount of token X per liquidity deposited."],"type":"u128"},{"name":"fee_amount_y_per_token_stored","docs":["Swap fee amount of token Y per liquidity deposited."],"type":"u128"},{"name":"open_order_amount","docs":["Pending open limit orders amount in the bin."],"type":"u64"},{"name":"total_processing_order_amount","docs":["Total processing order amount"],"type":"u64"},{"name":"processed_order_remaining_amount","docs":["Remaining in processing open limit orders amount in the bin."],"type":"u64"},{"name":"order_age","docs":["Age"],"type":"u32"},{"name":"limit_order_ask_side","docs":["limit order flag"],"type":"u8"},{"name":"_padding_1","docs":["padding"],"type":{"array":["u8",3]}}]}},{"name":"BinArray","docs":["An account to contain a range of bin. For example: Bin 100 <-> 200.","For example:","BinArray index: 0 contains bin 0 <-> 599","index: 2 contains bin 600 <-> 1199, ..."],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"index","type":"i64"},{"name":"version","type":"u8"},{"name":"_padding_1","type":{"array":["u8",7]}},{"name":"lb_pair","type":"pubkey"},{"name":"bins","type":{"array":[{"defined":{"name":"Bin"}},70]}}]}},{"name":"BinArrayBitmapExtension","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"positive_bin_array_bitmap","docs":["Packed initialized bin array state for start_bin_index is positive"],"type":{"array":[{"array":["u64",8]},12]}},{"name":"negative_bin_array_bitmap","docs":["Packed initialized bin array state for start_bin_index is negative"],"type":{"array":[{"array":["u64",8]},12]}}]}},{"name":"BinLimitOrderAmount","type":{"kind":"struct","fields":[{"name":"id","type":"i32"},{"name":"amount","type":"u64"}]}},{"name":"BinLiquidityDistribution","type":{"kind":"struct","fields":[{"name":"bin_id","docs":["Define the bin ID wish to deposit to."],"type":"i32"},{"name":"distribution_x","docs":["DistributionX (or distributionY) is the percentages of amountX (or amountY) you want to add to each bin."],"type":"u16"},{"name":"distribution_y","docs":["DistributionX (or distributionY) is the percentages of amountX (or amountY) you want to add to each bin."],"type":"u16"}]}},{"name":"BinLiquidityDistributionByWeight","type":{"kind":"struct","fields":[{"name":"bin_id","docs":["Define the bin ID wish to deposit to."],"type":"i32"},{"name":"weight","docs":["weight of liquidity distributed for this bin id"],"type":"u16"}]}},{"name":"BinLiquidityReduction","type":{"kind":"struct","fields":[{"name":"bin_id","type":"i32"},{"name":"bps_to_remove","type":"u16"}]}},{"name":"CancelLimitOrderEvt","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"from","type":"pubkey"},{"name":"limit_order","type":"pubkey"},{"name":"amounts","type":{"array":["u64",2]}},{"name":"active_id","type":"i32"},{"name":"bins","type":{"vec":"i32"}}]}},{"name":"ClaimFee","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"fee_x","type":"u64"},{"name":"fee_y","type":"u64"}]}},{"name":"ClaimFee2","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"fee_x","type":"u64"},{"name":"fee_y","type":"u64"},{"name":"active_bin_id","type":"i32"}]}},{"name":"ClaimFeeOperator","docs":["Parameter that set by the protocol"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"operator","docs":["operator"],"type":"pubkey"},{"name":"_padding","docs":["Reserve"],"type":{"array":["u8",128]}}]}},{"name":"ClaimReward","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"reward_index","type":"u64"},{"name":"total_reward","type":"u64"}]}},{"name":"ClaimReward2","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"reward_index","type":"u64"},{"name":"total_reward","type":"u64"},{"name":"active_bin_id","type":"i32"}]}},{"name":"CloseLimitOrderEvt","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"limit_order","type":"pubkey"}]}},{"name":"CompositionFee","type":{"kind":"struct","fields":[{"name":"from","type":"pubkey"},{"name":"bin_id","type":"i16"},{"name":"token_x_fee_amount","type":"u64"},{"name":"token_y_fee_amount","type":"u64"},{"name":"protocol_token_x_fee_amount","type":"u64"},{"name":"protocol_token_y_fee_amount","type":"u64"}]}},{"name":"CompressedBinDepositAmount","type":{"kind":"struct","fields":[{"name":"bin_id","type":"i32"},{"name":"amount","type":"u32"}]}},{"name":"CustomizableParams","type":{"kind":"struct","fields":[{"name":"active_id","docs":["Pool price"],"type":"i32"},{"name":"bin_step","docs":["Bin step"],"type":"u16"},{"name":"base_factor","docs":["Base factor"],"type":"u16"},{"name":"activation_type","docs":["Activation type. 0 = Slot, 1 = Time. Check ActivationType enum"],"type":"u8"},{"name":"has_alpha_vault","docs":["Whether the pool has an alpha vault"],"type":"bool"},{"name":"activation_point","docs":["Decide when does the pool start trade. None = Now"],"type":{"option":"u64"}},{"name":"creator_pool_on_off_control","docs":["Pool creator have permission to enable/disable pool with restricted program validation. Only applicable for customizable permissionless pool."],"type":"bool"},{"name":"base_fee_power_factor","docs":["Base fee power factor"],"type":"u8"},{"name":"concrete_function_type","docs":["Concrete function type"],"type":"u8"},{"name":"collect_fee_mode","docs":["Collect fee mode"],"type":"u8"},{"name":"padding","docs":["Padding, for future use"],"type":{"array":["u8",60]}}]}},{"name":"DecreasePositionLength","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"length_to_remove","type":"u16"},{"name":"side","type":"u8"}]}},{"name":"DummyIx","type":{"kind":"struct","fields":[{"name":"_pair_status","type":{"defined":{"name":"PairStatus"}}},{"name":"_pair_type","type":{"defined":{"name":"PairType"}}},{"name":"_activation_type","type":{"defined":{"name":"ActivationType"}}},{"name":"_token_program_flag","type":{"defined":{"name":"TokenProgramFlags"}}},{"name":"_resize_side","type":{"defined":{"name":"ResizeSide"}}},{"name":"_rounding","type":{"defined":{"name":"Rounding"}}}]}},{"name":"DummyZcAccount","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"position_bin_data","type":{"defined":{"name":"PositionBinData"}}}]}},{"name":"DynamicFeeParameter","type":{"kind":"struct","fields":[{"name":"filter_period","docs":["Filter period determine high frequency trading time window."],"type":"u16"},{"name":"decay_period","docs":["Decay period determine when the volatile fee start decay / decrease."],"type":"u16"},{"name":"reduction_factor","docs":["Reduction factor controls the volatile fee rate decrement rate."],"type":"u16"},{"name":"variable_fee_control","docs":["Used to scale the variable fee component depending on the dynamic of the market"],"type":"u32"},{"name":"max_volatility_accumulator","docs":["Maximum number of bin crossed can be accumulated. Used to cap volatile fee rate."],"type":"u32"}]}},{"name":"DynamicFeeParameterUpdate","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"filter_period","docs":["Filter period determine high frequency trading time window."],"type":"u16"},{"name":"decay_period","docs":["Decay period determine when the volatile fee start decay / decrease."],"type":"u16"},{"name":"reduction_factor","docs":["Reduction factor controls the volatile fee rate decrement rate."],"type":"u16"},{"name":"variable_fee_control","docs":["Used to scale the variable fee component depending on the dynamic of the market"],"type":"u32"},{"name":"max_volatility_accumulator","docs":["Maximum number of bin crossed can be accumulated. Used to cap volatile fee rate."],"type":"u32"}]}},{"name":"FeeInfo","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"fee_x_per_token_complete","type":"u128"},{"name":"fee_y_per_token_complete","type":"u128"},{"name":"fee_x_pending","type":"u64"},{"name":"fee_y_pending","type":"u64"}]}},{"name":"FeeParameterUpdate","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"protocol_share","type":"u16"},{"name":"base_factor","type":"u16"},{"name":"base_fee_power_factor","type":"u8"}]}},{"name":"FundReward","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"funder","type":"pubkey"},{"name":"reward_index","type":"u64"},{"name":"amount","type":"u64"}]}},{"name":"GoToABin","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"from_bin_id","type":"i32"},{"name":"to_bin_id","type":"i32"}]}},{"name":"IncreaseObservation","type":{"kind":"struct","fields":[{"name":"oracle","type":"pubkey"},{"name":"new_observation_length","type":"u64"}]}},{"name":"IncreasePositionLength","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"length_to_add","type":"u16"},{"name":"side","type":"u8"}]}},{"name":"InitPermissionPairIx","type":{"kind":"struct","fields":[{"name":"active_id","type":"i32"},{"name":"bin_step","type":"u16"},{"name":"base_factor","type":"u16"},{"name":"base_fee_power_factor","type":"u8"},{"name":"activation_type","type":"u8"},{"name":"padding0","type":"u16"},{"name":"concrete_function_type","type":"u8"},{"name":"collect_fee_mode","type":"u8"}]}},{"name":"InitPresetParametersIx","type":{"kind":"struct","fields":[{"name":"index","type":"u16"},{"name":"bin_step","docs":["Bin step. Represent the price increment / decrement."],"type":"u16"},{"name":"base_factor","docs":["Used for base fee calculation. base_fee_rate = base_factor * bin_step * 10 * 10^base_fee_power_factor"],"type":"u16"},{"name":"filter_period","docs":["Filter period determine high frequency trading time window."],"type":"u16"},{"name":"decay_period","docs":["Decay period determine when the volatile fee start decay / decrease."],"type":"u16"},{"name":"reduction_factor","docs":["Reduction factor controls the volatile fee rate decrement rate."],"type":"u16"},{"name":"variable_fee_control","docs":["Used to scale the variable fee component depending on the dynamic of the market"],"type":"u32"},{"name":"max_volatility_accumulator","docs":["Maximum number of bin crossed can be accumulated. Used to cap volatile fee rate."],"type":"u32"},{"name":"protocol_share","docs":["Portion of swap fees retained by the protocol by controlling protocol_share parameter. protocol_swap_fee = protocol_share * total_swap_fee"],"type":"u16"},{"name":"base_fee_power_factor","docs":["Base fee power factor"],"type":"u8"},{"name":"concrete_function_type","docs":["function type"],"type":"u8"},{"name":"collect_fee_mode","docs":["collect fee mode"],"type":"u8"}]}},{"name":"InitializeLbPair2Params","type":{"kind":"struct","fields":[{"name":"active_id","docs":["Pool price"],"type":"i32"},{"name":"padding","docs":["Padding, for future use"],"type":{"array":["u8",96]}}]}},{"name":"InitializeReward","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"reward_mint","type":"pubkey"},{"name":"funder","type":"pubkey"},{"name":"reward_index","type":"u64"},{"name":"reward_duration","type":"u64"}]}},{"name":"LbPair","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"parameters","type":{"defined":{"name":"StaticParameters"}}},{"name":"v_parameters","type":{"defined":{"name":"VariableParameters"}}},{"name":"bump_seed","type":{"array":["u8",1]}},{"name":"bin_step_seed","docs":["Bin step signer seed"],"type":{"array":["u8",2]}},{"name":"pair_type","docs":["Type of the pair"],"type":"u8"},{"name":"active_id","docs":["Active bin id"],"type":"i32"},{"name":"bin_step","docs":["Bin step. Represent the price increment / decrement."],"type":"u16"},{"name":"status","docs":["Status of the pair. Check PairStatus enum."],"type":"u8"},{"name":"require_base_factor_seed","docs":["Require base factor seed"],"type":"u8"},{"name":"base_factor_seed","docs":["Base factor seed"],"type":{"array":["u8",2]}},{"name":"activation_type","docs":["Activation type"],"type":"u8"},{"name":"creator_pool_on_off_control","docs":["Allow pool creator to enable/disable pool with restricted validation. Only applicable for customizable permissionless pair type."],"type":"u8"},{"name":"token_x_mint","docs":["Token X mint"],"type":"pubkey"},{"name":"token_y_mint","docs":["Token Y mint"],"type":"pubkey"},{"name":"reserve_x","docs":["LB token X vault"],"type":"pubkey"},{"name":"reserve_y","docs":["LB token Y vault"],"type":"pubkey"},{"name":"protocol_fee","docs":["Uncollected protocol fee"],"type":{"defined":{"name":"ProtocolFee"}}},{"name":"_padding_1","docs":["padding 1"],"type":{"array":["u8",32]}},{"name":"reward_infos","docs":["Farming reward information"],"type":{"array":[{"defined":{"name":"RewardInfo"}},2]}},{"name":"oracle","docs":["Oracle pubkey"],"type":"pubkey"},{"name":"bin_array_bitmap","docs":["Packed initialized bin array state"],"type":{"array":["u64",16]}},{"name":"last_updated_at","docs":["Last time the pool fee parameter was updated"],"type":"i64"},{"name":"_padding_2","docs":["_padding_2"],"type":{"array":["u8",32]}},{"name":"pre_activation_swap_address","docs":["Address allowed to swap when the current point is greater than or equal to the pre-activation point. The pre-activation point is calculated as `activation_point - pre_activation_duration`."],"type":"pubkey"},{"name":"base_key","docs":["Base keypair. Only required for permission pair"],"type":"pubkey"},{"name":"activation_point","docs":["Time point to enable the pair. Only applicable for permission pair."],"type":"u64"},{"name":"pre_activation_duration","docs":["Duration before activation activation_point. Used to calculate pre-activation time point for pre_activation_swap_address"],"type":"u64"},{"name":"_padding_3","docs":["_padding 3"],"type":{"array":["u8",8]}},{"name":"_padding_4","docs":["_padding_4"],"type":"u64"},{"name":"creator","docs":["Pool creator"],"type":"pubkey"},{"name":"token_mint_x_program_flag","docs":["token_mint_x_program_flag"],"type":"u8"},{"name":"token_mint_y_program_flag","docs":["token_mint_y_program_flag"],"type":"u8"},{"name":"version","docs":["version to know whether we have reset tombstone fields"],"type":"u8"},{"name":"_reserved","docs":["Reserved space for future use"],"type":{"array":["u8",21]}}]}},{"name":"LbPairCreate","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"bin_step","type":"u16"},{"name":"token_x","type":"pubkey"},{"name":"token_y","type":"pubkey"}]}},{"name":"LimitOrder","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"lb_pair","docs":["The LB pair of this LO"],"type":"pubkey"},{"name":"owner","docs":["Owner of the LO. Client rely on this to to fetch their LOs."],"type":"pubkey"},{"name":"bin_count","docs":["Bin count"],"type":"u16"},{"name":"_padding_0","docs":["Padding"],"type":{"array":["u8",14]}},{"name":"padding_1","docs":["Reserved space for future use"],"type":{"array":["u64",4]}}]}},{"name":"LiquidityOneSideParameter","type":{"kind":"struct","fields":[{"name":"amount","docs":["Amount of X token or Y token to deposit"],"type":"u64"},{"name":"active_id","docs":["Active bin that integrator observe off-chain"],"type":"i32"},{"name":"max_active_bin_slippage","docs":["max active bin slippage allowed"],"type":"i32"},{"name":"bin_liquidity_dist","docs":["Liquidity distribution to each bins"],"type":{"vec":{"defined":{"name":"BinLiquidityDistributionByWeight"}}}}]}},{"name":"LiquidityParameter","type":{"kind":"struct","fields":[{"name":"amount_x","docs":["Amount of X token to deposit"],"type":"u64"},{"name":"amount_y","docs":["Amount of Y token to deposit"],"type":"u64"},{"name":"bin_liquidity_dist","docs":["Liquidity distribution to each bins"],"type":{"vec":{"defined":{"name":"BinLiquidityDistribution"}}}}]}},{"name":"LiquidityParameterByStrategy","type":{"kind":"struct","fields":[{"name":"amount_x","docs":["Amount of X token to deposit"],"type":"u64"},{"name":"amount_y","docs":["Amount of Y token to deposit"],"type":"u64"},{"name":"active_id","docs":["Active bin that integrator observe off-chain"],"type":"i32"},{"name":"max_active_bin_slippage","docs":["max active bin slippage allowed"],"type":"i32"},{"name":"strategy_parameters","docs":["strategy parameters"],"type":{"defined":{"name":"StrategyParameters"}}}]}},{"name":"LiquidityParameterByStrategyOneSide","type":{"kind":"struct","fields":[{"name":"amount","docs":["Amount of X token or Y token to deposit"],"type":"u64"},{"name":"active_id","docs":["Active bin that integrator observe off-chain"],"type":"i32"},{"name":"max_active_bin_slippage","docs":["max active bin slippage allowed"],"type":"i32"},{"name":"strategy_parameters","docs":["strategy parameters"],"type":{"defined":{"name":"StrategyParameters"}}}]}},{"name":"LiquidityParameterByWeight","type":{"kind":"struct","fields":[{"name":"amount_x","docs":["Amount of X token to deposit"],"type":"u64"},{"name":"amount_y","docs":["Amount of Y token to deposit"],"type":"u64"},{"name":"active_id","docs":["Active bin that integrator observe off-chain"],"type":"i32"},{"name":"max_active_bin_slippage","docs":["max active bin slippage allowed"],"type":"i32"},{"name":"bin_liquidity_dist","docs":["Liquidity distribution to each bins"],"type":{"vec":{"defined":{"name":"BinLiquidityDistributionByWeight"}}}}]}},{"name":"Operator","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"signer","type":"pubkey"},{"name":"permission","type":"u128"},{"name":"padding","type":{"array":["u64",2]}}]}},{"name":"Oracle","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"idx","docs":["Index of latest observation"],"type":"u64"},{"name":"active_size","docs":["Size of active sample. Active sample is initialized observation."],"type":"u64"},{"name":"length","docs":["Number of observations"],"type":"u64"}]}},{"name":"PairStatus","docs":["Pair status. 0 = Enabled, 1 = Disabled. Putting 0 as enabled for backward compatibility."],"repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Enabled"},{"name":"Disabled"}]}},{"name":"PairType","docs":["Type of the Pair. 0 = Permissionless, 1 = Permission, 2 = CustomizablePermissionless. Putting 0 as permissionless for backward compatibility."],"repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Permissionless"},{"name":"Permission"},{"name":"CustomizablePermissionless"},{"name":"PermissionlessV2"}]}},{"name":"PlaceLimitOrderEvt","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"sender","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"limit_order","type":"pubkey"},{"name":"active_id","type":"i32"},{"name":"params","type":{"defined":{"name":"PlaceLimitOrderParams"}}}]}},{"name":"PlaceLimitOrderParams","type":{"kind":"struct","fields":[{"name":"is_ask_side","type":"bool"},{"name":"padding","type":{"array":["u8",16]}},{"name":"relative_bin","type":{"option":{"defined":{"name":"RelativeBin"}}}},{"name":"bins","type":{"vec":{"defined":{"name":"BinLimitOrderAmount"}}}}]}},{"name":"PositionBinData","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"liquidity_share","type":"u128"},{"name":"reward_info","type":{"defined":{"name":"UserRewardInfo"}}},{"name":"fee_info","type":{"defined":{"name":"FeeInfo"}}}]}},{"name":"PositionClose","type":{"kind":"struct","fields":[{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"}]}},{"name":"PositionCreate","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"}]}},{"name":"PositionV2","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"lb_pair","docs":["The LB pair of this position"],"type":"pubkey"},{"name":"owner","docs":["Owner of the position. Client rely on this to to fetch their positions."],"type":"pubkey"},{"name":"liquidity_shares","docs":["Liquidity shares of this position in bins (lower_bin_id <-> upper_bin_id). This is the same as LP concept."],"type":{"array":["u128",70]}},{"name":"reward_infos","docs":["Farming reward information"],"type":{"array":[{"defined":{"name":"UserRewardInfo"}},70]}},{"name":"fee_infos","docs":["Swap fee to claim information"],"type":{"array":[{"defined":{"name":"FeeInfo"}},70]}},{"name":"lower_bin_id","docs":["Lower bin ID"],"type":"i32"},{"name":"upper_bin_id","docs":["Upper bin ID"],"type":"i32"},{"name":"last_updated_at","docs":["Last updated timestamp"],"type":"i64"},{"name":"total_claimed_fee_x_amount","docs":["Total claimed token fee X"],"type":"u64"},{"name":"total_claimed_fee_y_amount","docs":["Total claimed token fee Y"],"type":"u64"},{"name":"total_claimed_rewards","docs":["Total claimed rewards"],"type":{"array":["u64",2]}},{"name":"operator","docs":["Operator of position"],"type":"pubkey"},{"name":"lock_release_point","docs":["Time point which the locked liquidity can be withdraw"],"type":"u64"},{"name":"_padding_0","docs":["_padding_0, previous subjected_to_bootstrap_liquidity_locking, BE CAREFUL FOR TOMBSTONE WHEN REUSE !!"],"type":"u8"},{"name":"fee_owner","docs":["Address is able to claim fee in this position, only valid for bootstrap_liquidity_position"],"type":"pubkey"},{"name":"version","docs":["version to know whether we have reset tombstone fields"],"type":"u8"},{"name":"permissionless_operation_bits","type":"u8"},{"name":"_reserved","docs":["Reserved space for future use"],"type":{"array":["u8",85]}}]}},{"name":"PresetParameter","type":{"kind":"struct","fields":[{"name":"bin_step","docs":["Bin step. Represent the price increment / decrement."],"type":"u16"},{"name":"base_factor","docs":["Used for base fee calculation. base_fee_rate = base_factor * bin_step * 10 * 10^base_fee_power_factor"],"type":"u16"},{"name":"filter_period","docs":["Filter period determine high frequency trading time window."],"type":"u16"},{"name":"decay_period","docs":["Decay period determine when the volatile fee start decay / decrease."],"type":"u16"},{"name":"reduction_factor","docs":["Reduction factor controls the volatile fee rate decrement rate."],"type":"u16"},{"name":"variable_fee_control","docs":["Used to scale the variable fee component depending on the dynamic of the market"],"type":"u32"},{"name":"max_volatility_accumulator","docs":["Maximum number of bin crossed can be accumulated. Used to cap volatile fee rate."],"type":"u32"},{"name":"min_bin_id","docs":["Min bin id supported by the pool based on the configured bin step."],"type":"i32"},{"name":"max_bin_id","docs":["Max bin id supported by the pool based on the configured bin step."],"type":"i32"},{"name":"protocol_share","docs":["Portion of swap fees retained by the protocol by controlling protocol_share parameter. protocol_swap_fee = protocol_share * total_swap_fee"],"type":"u16"}]}},{"name":"PresetParameter2","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"bin_step","docs":["Bin step. Represent the price increment / decrement."],"type":"u16"},{"name":"base_factor","docs":["Used for base fee calculation. base_fee_rate = base_factor * bin_step * 10 * 10^base_fee_power_factor"],"type":"u16"},{"name":"filter_period","docs":["Filter period determine high frequency trading time window."],"type":"u16"},{"name":"decay_period","docs":["Decay period determine when the volatile fee start decay / decrease."],"type":"u16"},{"name":"variable_fee_control","docs":["Used to scale the variable fee component depending on the dynamic of the market"],"type":"u32"},{"name":"max_volatility_accumulator","docs":["Maximum number of bin crossed can be accumulated. Used to cap volatile fee rate."],"type":"u32"},{"name":"reduction_factor","docs":["Reduction factor controls the volatile fee rate decrement rate."],"type":"u16"},{"name":"protocol_share","docs":["Portion of swap fees retained by the protocol by controlling protocol_share parameter. protocol_swap_fee = protocol_share * total_swap_fee"],"type":"u16"},{"name":"index","docs":["index"],"type":"u16"},{"name":"base_fee_power_factor","docs":["Base fee power factor"],"type":"u8"},{"name":"concrete_function_type","docs":["function type, to check whether the pool should have LM farming or other functions in the future, refer ConcreteFunctionType"],"type":"u8"},{"name":"collect_fee_mode","docs":["collect fee mode"],"type":"u8"},{"name":"padding_0","type":{"array":["u8",7]}},{"name":"padding_1","docs":["Padding 1 for future use"],"type":{"array":["u64",19]}}]}},{"name":"ProtocolFee","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"amount_x","type":"u64"},{"name":"amount_y","type":"u64"}]}},{"name":"RebalanceLiquidityParams","type":{"kind":"struct","fields":[{"name":"active_id","docs":["active id"],"type":"i32"},{"name":"max_active_bin_slippage","docs":["max active bin slippage allowed"],"type":"u16"},{"name":"should_claim_fee","docs":["a flag to indicate that whether fee should be harvested"],"type":"bool"},{"name":"should_claim_reward","docs":["a flag to indicate that whether rewards should be harvested"],"type":"bool"},{"name":"min_withdraw_x_amount","docs":["threshold for withdraw token x"],"type":"u64"},{"name":"max_deposit_x_amount","docs":["threshold for deposit token x"],"type":"u64"},{"name":"min_withdraw_y_amount","docs":["threshold for withdraw token y"],"type":"u64"},{"name":"max_deposit_y_amount","docs":["threshold for deposit token y"],"type":"u64"},{"name":"shrink_mode","docs":["shrink mode"],"type":"u8"},{"name":"padding","docs":["padding 32 bytes for future usage"],"type":{"array":["u8",31]}},{"name":"removes","docs":["removes"],"type":{"vec":{"defined":{"name":"RemoveLiquidityParams"}}}},{"name":"adds","docs":["adds"],"type":{"vec":{"defined":{"name":"AddLiquidityParams"}}}}]}},{"name":"Rebalancing","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"active_bin_id","type":"i32"},{"name":"x_withdrawn_amount","type":"u64"},{"name":"x_added_amount","type":"u64"},{"name":"y_withdrawn_amount","type":"u64"},{"name":"y_added_amount","type":"u64"},{"name":"x_fee_amount","type":"u64"},{"name":"y_fee_amount","type":"u64"},{"name":"old_min_id","type":"i32"},{"name":"old_max_id","type":"i32"},{"name":"new_min_id","type":"i32"},{"name":"new_max_id","type":"i32"},{"name":"rewards","type":{"array":["u64",2]}}]}},{"name":"RelativeBin","type":{"kind":"struct","fields":[{"name":"active_id","docs":["Active bin that integrator observe off-chain"],"type":"i32"},{"name":"max_active_bin_slippage","docs":["max active bin slippage allowed"],"type":"i32"}]}},{"name":"RemainingAccountsInfo","type":{"kind":"struct","fields":[{"name":"slices","type":{"vec":{"defined":{"name":"RemainingAccountsSlice"}}}}]}},{"name":"RemainingAccountsSlice","type":{"kind":"struct","fields":[{"name":"accounts_type","type":{"defined":{"name":"AccountsType"}}},{"name":"length","type":"u8"}]}},{"name":"RemoveLiquidity","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"from","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"amounts","type":{"array":["u64",2]}},{"name":"active_bin_id","type":"i32"}]}},{"name":"RemoveLiquidityParams","type":{"kind":"struct","fields":[{"name":"min_bin_id","type":{"option":"i32"}},{"name":"max_bin_id","type":{"option":"i32"}},{"name":"bps","type":"u16"},{"name":"padding","type":{"array":["u8",16]}}]}},{"name":"ResizeSide","docs":["Side of resize, 0 for lower and 1 for upper"],"repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Lower"},{"name":"Upper"}]}},{"name":"RewardInfo","docs":["Stores the state relevant for tracking liquidity mining rewards"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"mint","docs":["Reward token mint."],"type":"pubkey"},{"name":"vault","docs":["Reward vault token account."],"type":"pubkey"},{"name":"funder","docs":["Authority account that allows to fund rewards"],"type":"pubkey"},{"name":"reward_duration","docs":["LM reward duration in seconds."],"type":"u64"},{"name":"reward_duration_end","docs":["LM reward duration end time."],"type":"u64"},{"name":"reward_rate","docs":["LM reward rate"],"type":"u128"},{"name":"last_update_time","docs":["The last time reward states were updated."],"type":"u64"},{"name":"cumulative_seconds_with_empty_liquidity_reward","docs":["Accumulated seconds where when farm distribute rewards, but the bin is empty. The reward will be accumulated for next reward time window."],"type":"u64"}]}},{"name":"Rounding","type":{"kind":"enum","variants":[{"name":"Up"},{"name":"Down"}]}},{"name":"SetPositionPermissionlessOperationBitsEvt","type":{"kind":"struct","fields":[{"name":"position","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"old_bits","type":"u8"},{"name":"new_bits","type":"u8"}]}},{"name":"StaticParameters","docs":["Parameter that set by the protocol"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"base_factor","docs":["Used for base fee calculation. base_fee_rate = base_factor * bin_step * 10 * 10^base_fee_power_factor"],"type":"u16"},{"name":"filter_period","docs":["Filter period determine high frequency trading time window."],"type":"u16"},{"name":"decay_period","docs":["Decay period determine when the volatile fee start decay / decrease."],"type":"u16"},{"name":"reduction_factor","docs":["Reduction factor controls the volatile fee rate decrement rate."],"type":"u16"},{"name":"variable_fee_control","docs":["Used to scale the variable fee component depending on the dynamic of the market"],"type":"u32"},{"name":"max_volatility_accumulator","docs":["Maximum number of bin crossed can be accumulated. Used to cap volatile fee rate."],"type":"u32"},{"name":"min_bin_id","docs":["Min bin id supported by the pool based on the configured bin step."],"type":"i32"},{"name":"max_bin_id","docs":["Max bin id supported by the pool based on the configured bin step."],"type":"i32"},{"name":"protocol_share","docs":["Portion of swap fees retained by the protocol by controlling protocol_share parameter. protocol_swap_fee = protocol_share * total_swap_fee"],"type":"u16"},{"name":"base_fee_power_factor","docs":["Base fee power factor"],"type":"u8"},{"name":"function_type","docs":["function type"],"type":"u8"},{"name":"collect_fee_mode","docs":["Collect fee mode"],"type":"u8"},{"name":"_padding","docs":["Padding for bytemuck safe alignment"],"type":{"array":["u8",3]}}]}},{"name":"StrategyParameters","type":{"kind":"struct","fields":[{"name":"min_bin_id","docs":["min bin id"],"type":"i32"},{"name":"max_bin_id","docs":["max bin id"],"type":"i32"},{"name":"strategy_type","docs":["strategy type"],"type":{"defined":{"name":"StrategyType"}}},{"name":"parameteres","docs":["parameters"],"type":{"array":["u8",64]}}]}},{"name":"StrategyType","type":{"kind":"enum","variants":[{"name":"SpotOneSide"},{"name":"CurveOneSide"},{"name":"BidAskOneSide"},{"name":"SpotBalanced"},{"name":"CurveBalanced"},{"name":"BidAskBalanced"},{"name":"SpotImBalanced"},{"name":"CurveImBalanced"},{"name":"BidAskImBalanced"}]}},{"name":"Swap","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"from","type":"pubkey"},{"name":"start_bin_id","type":"i32"},{"name":"end_bin_id","type":"i32"},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"},{"name":"swap_for_y","type":"bool"},{"name":"fee","type":"u64"},{"name":"protocol_fee","type":"u64"},{"name":"fee_bps","type":"u128"},{"name":"host_fee","type":"u64"}]}},{"name":"Swap2Evt","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"from","type":"pubkey"},{"name":"start_bin_id","type":"i32"},{"name":"end_bin_id","type":"i32"},{"name":"swap_for_y","type":"bool"},{"name":"fee_bps","type":"u128"},{"name":"amount_in","docs":["Total amount, user transfer out"],"type":"u64"},{"name":"amount_left","docs":["Leftover amount"],"type":"u64"},{"name":"amount_out","docs":["Total amount transfer to user, including transfer fee"],"type":"u64"},{"name":"mm_fee","docs":["Market maker fee"],"type":"u64"},{"name":"protocol_fee","docs":["Total protocol fee"],"type":"u64"},{"name":"limit_order_fee","docs":["Total limit order fee"],"type":"u64"},{"name":"host_fee","docs":["Total host fee"],"type":"u64"},{"name":"fees_on_input","docs":["check if fees on input"],"type":"bool"},{"name":"fees_on_token_x","docs":["check if fees is on token x"],"type":"bool"}]}},{"name":"TokenBadge","docs":["Parameter that set by the protocol"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"token_mint","docs":["token mint"],"type":"pubkey"},{"name":"_padding","docs":["Reserve"],"type":{"array":["u8",128]}}]}},{"name":"TokenProgramFlags","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"TokenProgram"},{"name":"TokenProgram2022"}]}},{"name":"UpdatePositionLockReleasePoint","type":{"kind":"struct","fields":[{"name":"position","type":"pubkey"},{"name":"current_point","type":"u64"},{"name":"new_lock_release_point","type":"u64"},{"name":"old_lock_release_point","type":"u64"},{"name":"sender","type":"pubkey"}]}},{"name":"UpdatePositionOperator","type":{"kind":"struct","fields":[{"name":"position","type":"pubkey"},{"name":"old_operator","type":"pubkey"},{"name":"new_operator","type":"pubkey"}]}},{"name":"UpdateRewardDuration","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"reward_index","type":"u64"},{"name":"old_reward_duration","type":"u64"},{"name":"new_reward_duration","type":"u64"}]}},{"name":"UpdateRewardFunder","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"reward_index","type":"u64"},{"name":"old_funder","type":"pubkey"},{"name":"new_funder","type":"pubkey"}]}},{"name":"UserRewardInfo","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"reward_per_token_completes","type":{"array":["u128",2]}},{"name":"reward_pendings","type":{"array":["u64",2]}}]}},{"name":"VariableParameters","docs":["Parameters that changes based on dynamic of the market"],"serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"volatility_accumulator","docs":["Volatility accumulator measure the number of bin crossed since reference bin ID. Normally (without filter period taken into consideration), reference bin ID is the active bin of last swap.","It affects the variable fee rate"],"type":"u32"},{"name":"volatility_reference","docs":["Volatility reference is decayed volatility accumulator. It is always <= volatility_accumulator"],"type":"u32"},{"name":"index_reference","docs":["Active bin id of last swap."],"type":"i32"},{"name":"_padding","docs":["Padding for bytemuck safe alignment"],"type":{"array":["u8",4]}},{"name":"last_update_timestamp","docs":["Last timestamp the variable parameters was updated"],"type":"i64"},{"name":"_padding_1","docs":["Padding for bytemuck safe alignment"],"type":{"array":["u8",8]}}]}},{"name":"WithdrawIneligibleReward","type":{"kind":"struct","fields":[{"name":"lb_pair","type":"pubkey"},{"name":"reward_mint","type":"pubkey"},{"name":"amount","type":"u64"}]}}],"constants":[{"name":"BASIS_POINT_MAX","type":"u16","value":"10000"},{"name":"BIN_ARRAY","type":"bytes","value":"[98, 105, 110, 95, 97, 114, 114, 97, 121]"},{"name":"BIN_ARRAY_BITMAP_SEED","type":"bytes","value":"[98, 105, 116, 109, 97, 112]"},{"name":"BIN_ARRAY_BITMAP_SIZE","type":"i32","value":"512"},{"name":"CLAIM_PROTOCOL_FEE_OPERATOR","type":"bytes","value":"[99, 102, 95, 111, 112, 101, 114, 97, 116, 111, 114]"},{"name":"DEFAULT_BIN_PER_POSITION","type":"u64","value":"70"},{"name":"EXTENSION_BINARRAY_BITMAP_SIZE","type":"u64","value":"12"},{"name":"FEE_DENOMINATOR","type":"u64","value":"1000000000"},{"name":"HOST_FEE_BPS","docs":["Host fee. 20%"],"type":"u16","value":"2000"},{"name":"ILM_PROTOCOL_SHARE","type":"u16","value":"2000"},{"name":"LIMIT_ORDER_FEE_SHARE","type":"u16","value":"5000"},{"name":"MAX_BASE_FEE","docs":["Maximum base fee, base_fee / 10^9 = fee_in_percentage"],"type":"u128","value":"100000000"},{"name":"MAX_BIN_ID_PER_BIN_STEP","docs":["Maximum bin ID per bin step. Computed based on 1 bps. Used for bin id bound estimation."],"type":"i32","value":"351639"},{"name":"MAX_BIN_PER_ARRAY","type":"u64","value":"70"},{"name":"MAX_BIN_PER_LIMIT_ORDER","type":"u64","value":"50"},{"name":"MAX_BIN_STEP","docs":["Maximum bin step"],"type":"u16","value":"400"},{"name":"MAX_FEE_RATE","docs":["Maximum fee rate. 10%"],"type":"u64","value":"100000000"},{"name":"MAX_PROTOCOL_SHARE","docs":["Maximum protocol share of the fee. 25%"],"type":"u16","value":"2500"},{"name":"MAX_RESIZE_LENGTH","type":"u64","value":"91"},{"name":"MAX_REWARD_BIN_SPLIT","type":"u64","value":"15"},{"name":"MAX_REWARD_DURATION","type":"u64","value":"31536000"},{"name":"MINIMUM_LIQUIDITY","type":"u128","value":"1000000"},{"name":"MIN_BASE_FEE","docs":["Minimum base fee"],"type":"u128","value":"100000"},{"name":"MIN_REWARD_DURATION","type":"u64","value":"1"},{"name":"NUM_REWARDS","type":"u64","value":"2"},{"name":"OPERATOR_PREFIX","type":"bytes","value":"[111, 112, 101, 114, 97, 116, 111, 114]"},{"name":"ORACLE","type":"bytes","value":"[111, 114, 97, 99, 108, 101]"},{"name":"POSITION","type":"bytes","value":"[112, 111, 115, 105, 116, 105, 111, 110]"},{"name":"POSITION_MAX_LENGTH","type":"u64","value":"1400"},{"name":"PRESET_PARAMETER","type":"bytes","value":"[112, 114, 101, 115, 101, 116, 95, 112, 97, 114, 97, 109, 101, 116, 101, 114]"},{"name":"PRESET_PARAMETER2","type":"bytes","value":"[112, 114, 101, 115, 101, 116, 95, 112, 97, 114, 97, 109, 101, 116, 101, 114, 50]"},{"name":"PROTOCOL_SHARE","type":"u16","value":"500"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/meteora_pools_amm.Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB.json b/migration/khadhroony-bot2-reference/idls/meteora_pools_amm.Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB.json new file mode 100644 index 0000000..3c1bd2d --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/meteora_pools_amm.Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB.json @@ -0,0 +1 @@ +{"version":"0.5.2","name":"amm","docs":["Program for AMM"],"instructions":[{"name":"initializePermissionedPool","docs":["Initialize a new permissioned pool."],"accounts":[{"name":"pool","isMut":true,"isSigner":true,"docs":["Pool account (arbitrary address)"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"tokenAMint","isMut":false,"isSigner":false,"docs":["Token A mint of the pool. Eg: USDT"]},{"name":"tokenBMint","isMut":false,"isSigner":false,"docs":["Token B mint of the pool. Eg: USDC"]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token A. Token A of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token B. Token B of the pool will be deposit / withdraw from this vault account."]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault A"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault B"]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn vault LP upon deposit/withdraw from the vault."]},{"name":"adminTokenA","isMut":true,"isSigner":false,"docs":["Admin token account for pool token A mint. Used to bootstrap the pool with initial liquidity."]},{"name":"adminTokenB","isMut":true,"isSigner":false,"docs":["Admin token account for pool token B mint. Used to bootstrap the pool with initial liquidity."]},{"name":"adminPoolLp","isMut":true,"isSigner":false,"docs":["Admin pool LP token account. Used to receive LP during first deposit (initialize pool)","Admin pool LP token account. Used to receive LP during first deposit (initialize pool)"]},{"name":"protocolTokenAFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token A. Used to receive trading fee."]},{"name":"protocolTokenBFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token B. Used to receive trading fee."]},{"name":"admin","isMut":true,"isSigner":true,"docs":["Admin account. This account will be the admin of the pool, and the payer for PDA during initialize pool."]},{"name":"feeOwner","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false,"docs":["Rent account."]},{"name":"mintMetadata","isMut":true,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. The pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]},{"name":"associatedTokenProgram","isMut":false,"isSigner":false,"docs":["Associated token program."]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program."]}],"args":[{"name":"curveType","type":{"defined":"CurveType"}}]},{"name":"initializePermissionlessPool","docs":["Initialize a new permissionless pool."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA address)"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"tokenAMint","isMut":false,"isSigner":false,"docs":["Token A mint of the pool. Eg: USDT"]},{"name":"tokenBMint","isMut":false,"isSigner":false,"docs":["Token B mint of the pool. Eg: USDC"]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token A. Token A of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token B. Token B of the pool will be deposit / withdraw from this vault account."]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault A"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault B"]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn vault LP upon deposit/withdraw from the vault."]},{"name":"payerTokenA","isMut":true,"isSigner":false,"docs":["Payer token account for pool token A mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerTokenB","isMut":true,"isSigner":false,"docs":["Admin token account for pool token B mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerPoolLp","isMut":true,"isSigner":false},{"name":"protocolTokenAFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token A. Used to receive trading fee."]},{"name":"protocolTokenBFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token B. Used to receive trading fee."]},{"name":"payer","isMut":true,"isSigner":true,"docs":["Admin account. This account will be the admin of the pool, and the payer for PDA during initialize pool."]},{"name":"feeOwner","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false,"docs":["Rent account."]},{"name":"mintMetadata","isMut":true,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. The pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]},{"name":"associatedTokenProgram","isMut":false,"isSigner":false,"docs":["Associated token program."]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program."]}],"args":[{"name":"curveType","type":{"defined":"CurveType"}},{"name":"tokenAAmount","type":"u64"},{"name":"tokenBAmount","type":"u64"}]},{"name":"initializePermissionlessPoolWithFeeTier","docs":["Initialize a new permissionless pool with customized fee tier"],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA address)"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"tokenAMint","isMut":false,"isSigner":false,"docs":["Token A mint of the pool. Eg: USDT"]},{"name":"tokenBMint","isMut":false,"isSigner":false,"docs":["Token B mint of the pool. Eg: USDC"]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token A. Token A of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token B. Token B of the pool will be deposit / withdraw from this vault account."]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault A"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault B"]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn vault LP upon deposit/withdraw from the vault."]},{"name":"payerTokenA","isMut":true,"isSigner":false,"docs":["Payer token account for pool token A mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerTokenB","isMut":true,"isSigner":false,"docs":["Admin token account for pool token B mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerPoolLp","isMut":true,"isSigner":false},{"name":"protocolTokenAFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token A. Used to receive trading fee."]},{"name":"protocolTokenBFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token B. Used to receive trading fee."]},{"name":"payer","isMut":true,"isSigner":true,"docs":["Admin account. This account will be the admin of the pool, and the payer for PDA during initialize pool."]},{"name":"feeOwner","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false,"docs":["Rent account."]},{"name":"mintMetadata","isMut":true,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. The pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]},{"name":"associatedTokenProgram","isMut":false,"isSigner":false,"docs":["Associated token program."]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program."]}],"args":[{"name":"curveType","type":{"defined":"CurveType"}},{"name":"tradeFeeBps","type":"u64"},{"name":"tokenAAmount","type":"u64"},{"name":"tokenBAmount","type":"u64"}]},{"name":"enableOrDisablePool","docs":["Enable or disable a pool. A disabled pool allow only remove balanced liquidity operation."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"admin","isMut":false,"isSigner":true,"docs":["Admin account. Must be owner of the pool."]}],"args":[{"name":"enable","type":"bool"}]},{"name":"swap","docs":["Swap token A to B, or vice versa. An amount of trading fee will be charged for liquidity provider, and the admin of the pool."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"userSourceToken","isMut":true,"isSigner":false,"docs":["User token account. Token from this account will be transfer into the vault by the pool in exchange for another token of the pool."]},{"name":"userDestinationToken","isMut":true,"isSigner":false,"docs":["User token account. The exchanged token will be transfer into this account from the pool."]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token a. token a of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token b. token b of the pool will be deposit / withdraw from this vault account."]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["Lp token mint of vault a"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["Lp token mint of vault b"]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"protocolTokenFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account. Used to receive trading fee. It's mint field must matched with user_source_token mint field."]},{"name":"user","isMut":false,"isSigner":true,"docs":["User account. Must be owner of user_source_token."]},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. the pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]}],"args":[{"name":"inAmount","type":"u64"},{"name":"minimumOutAmount","type":"u64"}]},{"name":"removeLiquiditySingleSide","docs":["Withdraw only single token from the pool. Only supported by pool with stable swap curve."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"userPoolLp","isMut":true,"isSigner":false,"docs":["User pool lp token account. LP will be burned from this account upon success liquidity removal."]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token A. Token A of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token B. Token B of the pool will be deposit / withdraw from this vault account."]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault A"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault B"]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"userDestinationToken","isMut":true,"isSigner":false,"docs":["User token account to receive token upon success liquidity removal."]},{"name":"user","isMut":false,"isSigner":true,"docs":["User account. Must be owner of the user_pool_lp account."]},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. The pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]}],"args":[{"name":"poolTokenAmount","type":"u64"},{"name":"minimumOutAmount","type":"u64"}]},{"name":"addImbalanceLiquidity","docs":["Deposit tokens to the pool in an imbalance ratio. Only supported by pool with stable swap curve."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"userPoolLp","isMut":true,"isSigner":false,"docs":["user pool lp token account. lp will be burned from this account upon success liquidity removal."]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token a. token a of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token b. token b of the pool will be deposit / withdraw from this vault account."]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault a"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault b"]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"userAToken","isMut":true,"isSigner":false,"docs":["User token A account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"userBToken","isMut":true,"isSigner":false,"docs":["User token B account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"user","isMut":false,"isSigner":true,"docs":["User account. Must be owner of user_a_token, and user_b_token."]},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. the pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]}],"args":[{"name":"minimumPoolTokenAmount","type":"u64"},{"name":"tokenAAmount","type":"u64"},{"name":"tokenBAmount","type":"u64"}]},{"name":"removeBalanceLiquidity","docs":["Withdraw tokens from the pool in a balanced ratio. User will still able to withdraw from pool even the pool is disabled. This allow user to exit their liquidity when there's some unforeseen event happen."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"userPoolLp","isMut":true,"isSigner":false,"docs":["user pool lp token account. lp will be burned from this account upon success liquidity removal."]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token a. token a of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token b. token b of the pool will be deposit / withdraw from this vault account."]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault a"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault b"]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"userAToken","isMut":true,"isSigner":false,"docs":["User token A account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"userBToken","isMut":true,"isSigner":false,"docs":["User token B account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"user","isMut":false,"isSigner":true,"docs":["User account. Must be owner of user_a_token, and user_b_token."]},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. the pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]}],"args":[{"name":"poolTokenAmount","type":"u64"},{"name":"minimumATokenOut","type":"u64"},{"name":"minimumBTokenOut","type":"u64"}]},{"name":"addBalanceLiquidity","docs":["Deposit tokens to the pool in a balanced ratio."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"userPoolLp","isMut":true,"isSigner":false,"docs":["user pool lp token account. lp will be burned from this account upon success liquidity removal."]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token a. token a of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token b. token b of the pool will be deposit / withdraw from this vault account."]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault a"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault b"]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"userAToken","isMut":true,"isSigner":false,"docs":["User token A account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"userBToken","isMut":true,"isSigner":false,"docs":["User token B account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"user","isMut":false,"isSigner":true,"docs":["User account. Must be owner of user_a_token, and user_b_token."]},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. the pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]}],"args":[{"name":"poolTokenAmount","type":"u64"},{"name":"maximumTokenAAmount","type":"u64"},{"name":"maximumTokenBAmount","type":"u64"}]},{"name":"setPoolFees","docs":["Update trading fee charged for liquidity provider, and admin."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"feeOperator","isMut":false,"isSigner":true,"docs":["Fee operator account"]}],"args":[{"name":"fees","type":{"defined":"PoolFees"}},{"name":"newPartnerFeeNumerator","type":"u64"}]},{"name":"overrideCurveParam","docs":["Update swap curve parameters. This function do not allow update of curve type. For example: stable swap curve to constant product curve. Only supported by pool with stable swap curve.","Only amp is allowed to be override. The other attributes of stable swap curve will be ignored."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"admin","isMut":false,"isSigner":true,"docs":["Admin account."]}],"args":[{"name":"curveType","type":{"defined":"CurveType"}}]},{"name":"getPoolInfo","docs":["Get the general information of the pool."],"accounts":[{"name":"pool","isMut":false,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"lpMint","isMut":false,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"aVaultLp","isMut":false,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":false,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"aVault","isMut":false,"isSigner":false,"docs":["Vault account for token a. token a of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":false,"isSigner":false,"docs":["Vault account for token b. token b of the pool will be deposit / withdraw from this vault account."]},{"name":"aVaultLpMint","isMut":false,"isSigner":false,"docs":["LP token mint of vault a"]},{"name":"bVaultLpMint","isMut":false,"isSigner":false,"docs":["LP token mint of vault b"]}],"args":[]},{"name":"bootstrapLiquidity","docs":["Bootstrap the pool when liquidity is depleted."],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"userPoolLp","isMut":true,"isSigner":false,"docs":["user pool lp token account. lp will be burned from this account upon success liquidity removal."]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token a. token a of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token b. token b of the pool will be deposit / withdraw from this vault account."]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault a"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault b"]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"userAToken","isMut":true,"isSigner":false,"docs":["User token A account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"userBToken","isMut":true,"isSigner":false,"docs":["User token B account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"user","isMut":false,"isSigner":true,"docs":["User account. Must be owner of user_a_token, and user_b_token."]},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. the pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]}],"args":[{"name":"tokenAAmount","type":"u64"},{"name":"tokenBAmount","type":"u64"}]},{"name":"createMintMetadata","docs":["Create mint metadata account for old pools"],"accounts":[{"name":"pool","isMut":false,"isSigner":false,"docs":["Pool account"]},{"name":"lpMint","isMut":false,"isSigner":false,"docs":["LP mint account of the pool"]},{"name":"aVaultLp","isMut":false,"isSigner":false,"docs":["Vault A LP account of the pool"]},{"name":"mintMetadata","isMut":true,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program."]},{"name":"payer","isMut":true,"isSigner":true,"docs":["Payer"]}],"args":[]},{"name":"createLockEscrow","docs":["Create lock account"],"accounts":[{"name":"pool","isMut":false,"isSigner":false,"docs":["Pool account"]},{"name":"lockEscrow","isMut":true,"isSigner":false,"docs":["Lock account"]},{"name":"owner","isMut":false,"isSigner":false},{"name":"lpMint","isMut":false,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"payer","isMut":true,"isSigner":true,"docs":["Payer account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program."]}],"args":[]},{"name":"lock","docs":["Lock Lp token"],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account"]},{"name":"lpMint","isMut":false,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"lockEscrow","isMut":true,"isSigner":false,"docs":["Lock account"]},{"name":"owner","isMut":true,"isSigner":true,"docs":["Can be anyone"]},{"name":"sourceTokens","isMut":true,"isSigner":false,"docs":["owner lp token account"]},{"name":"escrowVault","isMut":true,"isSigner":false,"docs":["Escrow vault"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]},{"name":"aVault","isMut":false,"isSigner":false,"docs":["Vault account for token a. token a of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":false,"isSigner":false,"docs":["Vault account for token b. token b of the pool will be deposit / withdraw from this vault account."]},{"name":"aVaultLp","isMut":false,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":false,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"aVaultLpMint","isMut":false,"isSigner":false,"docs":["LP token mint of vault a"]},{"name":"bVaultLpMint","isMut":false,"isSigner":false,"docs":["LP token mint of vault b"]}],"args":[{"name":"maxAmount","type":"u64"}]},{"name":"claimFee","docs":["Claim fee"],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"lockEscrow","isMut":true,"isSigner":false,"docs":["Lock account"]},{"name":"owner","isMut":true,"isSigner":true,"docs":["Owner of lock account"]},{"name":"sourceTokens","isMut":true,"isSigner":false,"docs":["owner lp token account"]},{"name":"escrowVault","isMut":true,"isSigner":false,"docs":["Escrow vault"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token a. token a of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token b. token b of the pool will be deposit / withdraw from this vault account."]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault a"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault b"]},{"name":"userAToken","isMut":true,"isSigner":false,"docs":["User token A account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"userBToken","isMut":true,"isSigner":false,"docs":["User token B account. Token will be transfer from this account if it is add liquidity operation. Else, token will be transfer into this account."]},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. the pool will deposit/withdraw liquidity from the vault."]}],"args":[{"name":"maxAmount","type":"u64"}]},{"name":"createConfig","docs":["Create config"],"accounts":[{"name":"config","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"configParameters","type":{"defined":"ConfigParameters"}}]},{"name":"closeConfig","docs":["Close config"],"accounts":[{"name":"config","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"rentReceiver","isMut":true,"isSigner":false}],"args":[]},{"name":"initializePermissionlessConstantProductPoolWithConfig","docs":["Initialize permissionless pool with config"],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA address)"]},{"name":"config","isMut":false,"isSigner":false},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"tokenAMint","isMut":false,"isSigner":false,"docs":["Token A mint of the pool. Eg: USDT"]},{"name":"tokenBMint","isMut":false,"isSigner":false,"docs":["Token B mint of the pool. Eg: USDC"]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token A. Token A of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token B. Token B of the pool will be deposit / withdraw from this vault account."]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault A"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault B"]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn vault LP upon deposit/withdraw from the vault."]},{"name":"payerTokenA","isMut":true,"isSigner":false,"docs":["Payer token account for pool token A mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerTokenB","isMut":true,"isSigner":false,"docs":["Admin token account for pool token B mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerPoolLp","isMut":true,"isSigner":false},{"name":"protocolTokenAFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token A. Used to receive trading fee."]},{"name":"protocolTokenBFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token B. Used to receive trading fee."]},{"name":"payer","isMut":true,"isSigner":true,"docs":["Admin account. This account will be the admin of the pool, and the payer for PDA during initialize pool."]},{"name":"rent","isMut":false,"isSigner":false,"docs":["Rent account."]},{"name":"mintMetadata","isMut":true,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. The pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]},{"name":"associatedTokenProgram","isMut":false,"isSigner":false,"docs":["Associated token program."]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program."]}],"args":[{"name":"tokenAAmount","type":"u64"},{"name":"tokenBAmount","type":"u64"}]},{"name":"initializePermissionlessConstantProductPoolWithConfig2","docs":["Initialize permissionless pool with config 2"],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA address)"]},{"name":"config","isMut":false,"isSigner":false},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"tokenAMint","isMut":false,"isSigner":false,"docs":["Token A mint of the pool. Eg: USDT"]},{"name":"tokenBMint","isMut":false,"isSigner":false,"docs":["Token B mint of the pool. Eg: USDC"]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token A. Token A of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token B. Token B of the pool will be deposit / withdraw from this vault account."]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault A"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault B"]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn vault LP upon deposit/withdraw from the vault."]},{"name":"payerTokenA","isMut":true,"isSigner":false,"docs":["Payer token account for pool token A mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerTokenB","isMut":true,"isSigner":false,"docs":["Admin token account for pool token B mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerPoolLp","isMut":true,"isSigner":false},{"name":"protocolTokenAFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token A. Used to receive trading fee."]},{"name":"protocolTokenBFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token B. Used to receive trading fee."]},{"name":"payer","isMut":true,"isSigner":true,"docs":["Admin account. This account will be the admin of the pool, and the payer for PDA during initialize pool."]},{"name":"rent","isMut":false,"isSigner":false,"docs":["Rent account."]},{"name":"mintMetadata","isMut":true,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. The pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]},{"name":"associatedTokenProgram","isMut":false,"isSigner":false,"docs":["Associated token program."]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program."]}],"args":[{"name":"tokenAAmount","type":"u64"},{"name":"tokenBAmount","type":"u64"},{"name":"activationPoint","type":{"option":"u64"}}]},{"name":"initializeCustomizablePermissionlessConstantProductPool","docs":["Initialize permissionless pool with customizable params"],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA address)"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["LP token mint of the pool"]},{"name":"tokenAMint","isMut":false,"isSigner":false,"docs":["Token A mint of the pool. Eg: USDT"]},{"name":"tokenBMint","isMut":false,"isSigner":false,"docs":["Token B mint of the pool. Eg: USDC"]},{"name":"aVault","isMut":true,"isSigner":false,"docs":["Vault account for token A. Token A of the pool will be deposit / withdraw from this vault account."]},{"name":"bVault","isMut":true,"isSigner":false,"docs":["Vault account for token B. Token B of the pool will be deposit / withdraw from this vault account."]},{"name":"aTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault A"]},{"name":"bTokenVault","isMut":true,"isSigner":false,"docs":["Token vault account of vault B"]},{"name":"aVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault A"]},{"name":"bVaultLpMint","isMut":true,"isSigner":false,"docs":["LP token mint of vault B"]},{"name":"aVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."]},{"name":"bVaultLp","isMut":true,"isSigner":false,"docs":["LP token account of vault B. Used to receive/burn vault LP upon deposit/withdraw from the vault."]},{"name":"payerTokenA","isMut":true,"isSigner":false,"docs":["Payer token account for pool token A mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerTokenB","isMut":true,"isSigner":false,"docs":["Admin token account for pool token B mint. Used to bootstrap the pool with initial liquidity."]},{"name":"payerPoolLp","isMut":true,"isSigner":false},{"name":"protocolTokenAFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token A. Used to receive trading fee."]},{"name":"protocolTokenBFee","isMut":true,"isSigner":false,"docs":["Protocol fee token account for token B. Used to receive trading fee."]},{"name":"payer","isMut":true,"isSigner":true,"docs":["Admin account. This account will be the admin of the pool, and the payer for PDA during initialize pool."]},{"name":"rent","isMut":false,"isSigner":false,"docs":["Rent account."]},{"name":"mintMetadata","isMut":true,"isSigner":false},{"name":"metadataProgram","isMut":false,"isSigner":false},{"name":"vaultProgram","isMut":false,"isSigner":false,"docs":["Vault program. The pool will deposit/withdraw liquidity from the vault."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program."]},{"name":"associatedTokenProgram","isMut":false,"isSigner":false,"docs":["Associated token program."]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program."]}],"args":[{"name":"tokenAAmount","type":"u64"},{"name":"tokenBAmount","type":"u64"},{"name":"params","type":{"defined":"CustomizableParams"}}]},{"name":"updateActivationPoint","docs":["Update activation slot"],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"admin","isMut":false,"isSigner":true,"docs":["Admin account."]}],"args":[{"name":"newActivationPoint","type":"u64"}]},{"name":"withdrawProtocolFees","docs":["Withdraw protocol fee"],"accounts":[{"name":"pool","isMut":false,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"aVaultLp","isMut":false,"isSigner":false},{"name":"protocolTokenAFee","isMut":true,"isSigner":false},{"name":"protocolTokenBFee","isMut":true,"isSigner":false},{"name":"treasuryTokenA","isMut":true,"isSigner":false},{"name":"treasuryTokenB","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"setWhitelistedVault","docs":["Set whitelisted vault"],"accounts":[{"name":"pool","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"whitelistedVault","type":"publicKey"}]},{"name":"partnerClaimFee","docs":["Partner claim fee"],"accounts":[{"name":"pool","isMut":true,"isSigner":false,"docs":["Pool account (PDA)"]},{"name":"aVaultLp","isMut":false,"isSigner":false},{"name":"protocolTokenAFee","isMut":true,"isSigner":false},{"name":"protocolTokenBFee","isMut":true,"isSigner":false},{"name":"partnerTokenA","isMut":true,"isSigner":false},{"name":"partnerTokenB","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"partnerAuthority","isMut":false,"isSigner":true}],"args":[{"name":"maxAmountA","type":"u64"},{"name":"maxAmountB","type":"u64"}]}],"accounts":[{"name":"Config","type":{"kind":"struct","fields":[{"name":"poolFees","type":{"defined":"PoolFees"}},{"name":"activationDuration","type":"u64"},{"name":"vaultConfigKey","type":"publicKey"},{"name":"poolCreatorAuthority","docs":["Only pool_creator_authority can use the current config to initialize new pool. When it's Pubkey::default, it's a public config."],"type":"publicKey"},{"name":"activationType","docs":["Activation type"],"type":"u8"},{"name":"partnerFeeNumerator","type":"u64"},{"name":"padding","type":{"array":["u8",219]}}]}},{"name":"LockEscrow","docs":["State of lock escrow account"],"type":{"kind":"struct","fields":[{"name":"pool","docs":["Pool address"],"type":"publicKey"},{"name":"owner","docs":["Owner address"],"type":"publicKey"},{"name":"escrowVault","docs":["Vault address, store the lock user lock"],"type":"publicKey"},{"name":"bump","docs":["bump, used to sign"],"type":"u8"},{"name":"totalLockedAmount","docs":["Total locked amount"],"type":"u64"},{"name":"lpPerToken","docs":["Lp per token, virtual price of lp token"],"type":"u128"},{"name":"unclaimedFeePending","docs":["Unclaimed fee pending"],"type":"u64"},{"name":"aFee","docs":["Total a fee claimed so far"],"type":"u64"},{"name":"bFee","docs":["Total b fee claimed so far"],"type":"u64"}]}},{"name":"Pool","docs":["State of pool account"],"type":{"kind":"struct","fields":[{"name":"lpMint","docs":["LP token mint of the pool"],"type":"publicKey"},{"name":"tokenAMint","docs":["Token A mint of the pool. Eg: USDT"],"type":"publicKey"},{"name":"tokenBMint","docs":["Token B mint of the pool. Eg: USDC"],"type":"publicKey"},{"name":"aVault","docs":["Vault account for token A. Token A of the pool will be deposit / withdraw from this vault account."],"type":"publicKey"},{"name":"bVault","docs":["Vault account for token B. Token B of the pool will be deposit / withdraw from this vault account."],"type":"publicKey"},{"name":"aVaultLp","docs":["LP token account of vault A. Used to receive/burn the vault LP upon deposit/withdraw from the vault."],"type":"publicKey"},{"name":"bVaultLp","docs":["LP token account of vault B. Used to receive/burn the vault LP upon deposit/withdraw from the vault."],"type":"publicKey"},{"name":"aVaultLpBump","docs":["\"A\" vault lp bump. Used to create signer seeds."],"type":"u8"},{"name":"enabled","docs":["Flag to determine whether the pool is enabled, or disabled."],"type":"bool"},{"name":"protocolTokenAFee","docs":["Protocol fee token account for token A. Used to receive trading fee."],"type":"publicKey"},{"name":"protocolTokenBFee","docs":["Protocol fee token account for token B. Used to receive trading fee."],"type":"publicKey"},{"name":"feeLastUpdatedAt","docs":["Fee last updated timestamp"],"type":"u64"},{"name":"padding0","type":{"array":["u8",24]}},{"name":"fees","docs":["Store the fee charges setting."],"type":{"defined":"PoolFees"}},{"name":"poolType","docs":["Pool type"],"type":{"defined":"PoolType"}},{"name":"stake","docs":["Stake pubkey of SPL stake pool"],"type":"publicKey"},{"name":"totalLockedLp","docs":["Total locked lp token"],"type":"u64"},{"name":"bootstrapping","docs":["bootstrapping config"],"type":{"defined":"Bootstrapping"}},{"name":"partnerInfo","type":{"defined":"PartnerInfo"}},{"name":"padding","docs":["Padding for future pool field"],"type":{"defined":"Padding"}},{"name":"curveType","docs":["The type of the swap curve supported by the pool."],"type":{"defined":"CurveType"}}]}}],"types":[{"name":"TokenMultiplier","docs":["Multiplier for the pool token. Used to normalized token with different decimal into the same precision."],"type":{"kind":"struct","fields":[{"name":"tokenAMultiplier","docs":["Multiplier for token A of the pool."],"type":"u64"},{"name":"tokenBMultiplier","docs":["Multiplier for token B of the pool."],"type":"u64"},{"name":"precisionFactor","docs":["Record the highest token decimal in the pool. For example, Token A is 6 decimal, token B is 9 decimal. This will save value of 9."],"type":"u8"}]}},{"name":"PoolFees","docs":["Information regarding fee charges"],"type":{"kind":"struct","fields":[{"name":"tradeFeeNumerator","docs":["Trade fees are extra token amounts that are held inside the token","accounts during a trade, making the value of liquidity tokens rise.","Trade fee numerator"],"type":"u64"},{"name":"tradeFeeDenominator","docs":["Trade fee denominator"],"type":"u64"},{"name":"protocolTradeFeeNumerator","docs":["Protocol trading fees are extra token amounts that are held inside the token","accounts during a trade, with the equivalent in pool tokens minted to","the protocol of the program.","Protocol trade fee numerator"],"type":"u64"},{"name":"protocolTradeFeeDenominator","docs":["Protocol trade fee denominator"],"type":"u64"}]}},{"name":"Depeg","docs":["Contains information for depeg pool"],"type":{"kind":"struct","fields":[{"name":"baseVirtualPrice","docs":["The virtual price of staking / interest bearing token"],"type":"u64"},{"name":"baseCacheUpdated","docs":["The last time base_virtual_price is updated"],"type":"u64"},{"name":"depegType","docs":["Type of the depeg pool"],"type":{"defined":"DepegType"}}]}},{"name":"ConfigParameters","type":{"kind":"struct","fields":[{"name":"tradeFeeNumerator","type":"u64"},{"name":"protocolTradeFeeNumerator","type":"u64"},{"name":"activationDuration","type":"u64"},{"name":"vaultConfigKey","type":"publicKey"},{"name":"poolCreatorAuthority","type":"publicKey"},{"name":"activationType","type":"u8"},{"name":"index","type":"u64"},{"name":"partnerFeeNumerator","type":"u64"}]}},{"name":"CustomizableParams","type":{"kind":"struct","fields":[{"name":"tradeFeeNumerator","docs":["Trading fee."],"type":"u32"},{"name":"activationPoint","docs":["The pool start trading."],"type":{"option":"u64"}},{"name":"hasAlphaVault","docs":["Whether the pool support alpha vault"],"type":"bool"},{"name":"activationType","docs":["Activation type"],"type":"u8"},{"name":"padding","docs":["Padding"],"type":{"array":["u8",90]}}]}},{"name":"Padding","docs":["Padding for future pool fields"],"type":{"kind":"struct","fields":[{"name":"padding0","docs":["Padding 0"],"type":{"array":["u8",6]}},{"name":"padding1","docs":["Padding 1"],"type":{"array":["u64",21]}},{"name":"padding2","docs":["Padding 2"],"type":{"array":["u64",21]}}]}},{"name":"PartnerInfo","type":{"kind":"struct","fields":[{"name":"feeNumerator","type":"u64"},{"name":"partnerAuthority","type":"publicKey"},{"name":"pendingFeeA","type":"u64"},{"name":"pendingFeeB","type":"u64"}]}},{"name":"Bootstrapping","type":{"kind":"struct","fields":[{"name":"activationPoint","docs":["Activation point, can be slot or timestamp"],"type":"u64"},{"name":"whitelistedVault","docs":["Whitelisted vault to be able to buy pool before activation_point"],"type":"publicKey"},{"name":"poolCreator","docs":["Need to store pool creator in lauch pool, so they can modify liquidity before activation_point"],"type":"publicKey"},{"name":"activationType","docs":["Activation type, 0 means by slot, 1 means by timestamp"],"type":"u8"}]}},{"name":"ActivationType","docs":["Type of the activation"],"type":{"kind":"enum","variants":[{"name":"Slot"},{"name":"Timestamp"}]}},{"name":"RoundDirection","docs":["Rounding direction"],"type":{"kind":"enum","variants":[{"name":"Floor"},{"name":"Ceiling"}]}},{"name":"TradeDirection","docs":["Trade (swap) direction"],"type":{"kind":"enum","variants":[{"name":"AtoB"},{"name":"BtoA"}]}},{"name":"NewCurveType","docs":["Type of the swap curve"],"type":{"kind":"enum","variants":[{"name":"ConstantProduct"},{"name":"Stable","fields":[{"name":"amp","docs":["Amplification coefficient"],"type":"u64"},{"name":"token_multiplier","docs":["Multiplier for the pool token. Used to normalized token with different decimal into the same precision."],"type":{"defined":"TokenMultiplier"}},{"name":"depeg","docs":["Depeg pool information. Contains functions to allow token amount to be repeg using stake / interest bearing token virtual price"],"type":{"defined":"Depeg"}},{"name":"last_amp_updated_timestamp","docs":["The last amp updated timestamp. Used to prevent update_curve_info called infinitely many times within a short period"],"type":"u64"}]},{"name":"NewCurve","fields":[{"name":"field_one","type":"u64"},{"name":"field_two","type":"u64"}]}]}},{"name":"CurveType","docs":["Type of the swap curve"],"type":{"kind":"enum","variants":[{"name":"ConstantProduct"},{"name":"Stable","fields":[{"name":"amp","docs":["Amplification coefficient"],"type":"u64"},{"name":"token_multiplier","docs":["Multiplier for the pool token. Used to normalized token with different decimal into the same precision."],"type":{"defined":"TokenMultiplier"}},{"name":"depeg","docs":["Depeg pool information. Contains functions to allow token amount to be repeg using stake / interest bearing token virtual price"],"type":{"defined":"Depeg"}},{"name":"last_amp_updated_timestamp","docs":["The last amp updated timestamp. Used to prevent update_curve_info called infinitely many times within a short period"],"type":"u64"}]}]}},{"name":"DepegType","docs":["Type of depeg pool"],"type":{"kind":"enum","variants":[{"name":"None"},{"name":"Marinade"},{"name":"Lido"},{"name":"SplStake"}]}},{"name":"Rounding","docs":["Round up, down"],"type":{"kind":"enum","variants":[{"name":"Up"},{"name":"Down"}]}},{"name":"PoolType","docs":["Pool type"],"type":{"kind":"enum","variants":[{"name":"Permissioned"},{"name":"Permissionless"}]}}],"events":[{"name":"AddLiquidity","fields":[{"name":"lpMintAmount","type":"u64","index":false},{"name":"tokenAAmount","type":"u64","index":false},{"name":"tokenBAmount","type":"u64","index":false}]},{"name":"RemoveLiquidity","fields":[{"name":"lpUnmintAmount","type":"u64","index":false},{"name":"tokenAOutAmount","type":"u64","index":false},{"name":"tokenBOutAmount","type":"u64","index":false}]},{"name":"BootstrapLiquidity","fields":[{"name":"lpMintAmount","type":"u64","index":false},{"name":"tokenAAmount","type":"u64","index":false},{"name":"tokenBAmount","type":"u64","index":false},{"name":"pool","type":"publicKey","index":false}]},{"name":"Swap","fields":[{"name":"inAmount","type":"u64","index":false},{"name":"outAmount","type":"u64","index":false},{"name":"tradeFee","type":"u64","index":false},{"name":"protocolFee","type":"u64","index":false},{"name":"hostFee","type":"u64","index":false}]},{"name":"SetPoolFees","fields":[{"name":"tradeFeeNumerator","type":"u64","index":false},{"name":"tradeFeeDenominator","type":"u64","index":false},{"name":"protocolTradeFeeNumerator","type":"u64","index":false},{"name":"protocolTradeFeeDenominator","type":"u64","index":false},{"name":"pool","type":"publicKey","index":false}]},{"name":"PoolInfo","fields":[{"name":"tokenAAmount","type":"u64","index":false},{"name":"tokenBAmount","type":"u64","index":false},{"name":"virtualPrice","type":"f64","index":false},{"name":"currentTimestamp","type":"u64","index":false}]},{"name":"TransferAdmin","fields":[{"name":"admin","type":"publicKey","index":false},{"name":"newAdmin","type":"publicKey","index":false},{"name":"pool","type":"publicKey","index":false}]},{"name":"OverrideCurveParam","fields":[{"name":"newAmp","type":"u64","index":false},{"name":"updatedTimestamp","type":"u64","index":false},{"name":"pool","type":"publicKey","index":false}]},{"name":"PoolCreated","fields":[{"name":"lpMint","type":"publicKey","index":false},{"name":"tokenAMint","type":"publicKey","index":false},{"name":"tokenBMint","type":"publicKey","index":false},{"name":"poolType","type":{"defined":"PoolType"},"index":false},{"name":"pool","type":"publicKey","index":false}]},{"name":"PoolEnabled","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"enabled","type":"bool","index":false}]},{"name":"MigrateFeeAccount","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"newAdminTokenAFee","type":"publicKey","index":false},{"name":"newAdminTokenBFee","type":"publicKey","index":false},{"name":"tokenAAmount","type":"u64","index":false},{"name":"tokenBAmount","type":"u64","index":false}]},{"name":"CreateLockEscrow","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"owner","type":"publicKey","index":false}]},{"name":"Lock","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false}]},{"name":"ClaimFee","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"owner","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"aFee","type":"u64","index":false},{"name":"bFee","type":"u64","index":false}]},{"name":"CreateConfig","fields":[{"name":"tradeFeeNumerator","type":"u64","index":false},{"name":"protocolTradeFeeNumerator","type":"u64","index":false},{"name":"config","type":"publicKey","index":false}]},{"name":"CloseConfig","fields":[{"name":"config","type":"publicKey","index":false}]},{"name":"WithdrawProtocolFees","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"protocolAFee","type":"u64","index":false},{"name":"protocolBFee","type":"u64","index":false},{"name":"protocolAFeeOwner","type":"publicKey","index":false},{"name":"protocolBFeeOwner","type":"publicKey","index":false}]},{"name":"PartnerClaimFees","fields":[{"name":"pool","type":"publicKey","index":false},{"name":"feeA","type":"u64","index":false},{"name":"feeB","type":"u64","index":false},{"name":"partner","type":"publicKey","index":false}]}],"errors":[{"code":6000,"name":"MathOverflow","msg":"Math operation overflow"},{"code":6001,"name":"InvalidFee","msg":"Invalid fee setup"},{"code":6002,"name":"InvalidInvariant","msg":"Invalid invariant d"},{"code":6003,"name":"FeeCalculationFailure","msg":"Fee calculation failure"},{"code":6004,"name":"ExceededSlippage","msg":"Exceeded slippage tolerance"},{"code":6005,"name":"InvalidCalculation","msg":"Invalid curve calculation"},{"code":6006,"name":"ZeroTradingTokens","msg":"Given pool token amount results in zero trading tokens"},{"code":6007,"name":"ConversionError","msg":"Math conversion overflow"},{"code":6008,"name":"FaultyLpMint","msg":"LP mint authority must be 'A' vault lp, without freeze authority, and 0 supply"},{"code":6009,"name":"MismatchedTokenMint","msg":"Token mint mismatched"},{"code":6010,"name":"MismatchedLpMint","msg":"LP mint mismatched"},{"code":6011,"name":"MismatchedOwner","msg":"Invalid lp token owner"},{"code":6012,"name":"InvalidVaultAccount","msg":"Invalid vault account"},{"code":6013,"name":"InvalidVaultLpAccount","msg":"Invalid vault lp account"},{"code":6014,"name":"InvalidPoolLpMintAccount","msg":"Invalid pool lp mint account"},{"code":6015,"name":"PoolDisabled","msg":"Pool disabled"},{"code":6016,"name":"InvalidAdminAccount","msg":"Invalid admin account"},{"code":6017,"name":"InvalidProtocolFeeAccount","msg":"Invalid protocol fee account"},{"code":6018,"name":"SameAdminAccount","msg":"Same admin account"},{"code":6019,"name":"IdenticalSourceDestination","msg":"Identical user source and destination token account"},{"code":6020,"name":"ApyCalculationError","msg":"Apy calculation error"},{"code":6021,"name":"InsufficientSnapshot","msg":"Insufficient virtual price snapshot"},{"code":6022,"name":"NonUpdatableCurve","msg":"Current curve is non-updatable"},{"code":6023,"name":"MisMatchedCurve","msg":"New curve is mismatched with old curve"},{"code":6024,"name":"InvalidAmplification","msg":"Amplification is invalid"},{"code":6025,"name":"UnsupportedOperation","msg":"Operation is not supported"},{"code":6026,"name":"ExceedMaxAChanges","msg":"Exceed max amplification changes"},{"code":6027,"name":"InvalidRemainingAccountsLen","msg":"Invalid remaining accounts length"},{"code":6028,"name":"InvalidRemainingAccounts","msg":"Invalid remaining account"},{"code":6029,"name":"MismatchedDepegMint","msg":"Token mint B doesn't matches depeg type token mint"},{"code":6030,"name":"InvalidApyAccount","msg":"Invalid APY account"},{"code":6031,"name":"InvalidTokenMultiplier","msg":"Invalid token multiplier"},{"code":6032,"name":"InvalidDepegInformation","msg":"Invalid depeg information"},{"code":6033,"name":"UpdateTimeConstraint","msg":"Update time constraint violated"},{"code":6034,"name":"ExceedMaxFeeBps","msg":"Exceeded max fee bps"},{"code":6035,"name":"InvalidAdmin","msg":"Invalid admin"},{"code":6036,"name":"PoolIsNotPermissioned","msg":"Pool is not permissioned"},{"code":6037,"name":"InvalidDepositAmount","msg":"Invalid deposit amount"},{"code":6038,"name":"InvalidFeeOwner","msg":"Invalid fee owner"},{"code":6039,"name":"NonDepletedPool","msg":"Pool is not depleted"},{"code":6040,"name":"AmountNotPeg","msg":"Token amount is not 1:1"},{"code":6041,"name":"AmountIsZero","msg":"Amount is zero"},{"code":6042,"name":"TypeCastFailed","msg":"Type cast error"},{"code":6043,"name":"AmountIsNotEnough","msg":"Amount is not enough"},{"code":6044,"name":"InvalidActivationDuration","msg":"Invalid activation duration"},{"code":6045,"name":"PoolIsNotLaunchPool","msg":"Pool is not launch pool"},{"code":6046,"name":"UnableToModifyActivationPoint","msg":"Unable to modify activation point"},{"code":6047,"name":"InvalidAuthorityToCreateThePool","msg":"Invalid authority to create the pool"},{"code":6048,"name":"InvalidActivationType","msg":"Invalid activation type"},{"code":6049,"name":"InvalidActivationPoint","msg":"Invalid activation point"},{"code":6050,"name":"PreActivationSwapStarted","msg":"Pre activation swap window started"},{"code":6051,"name":"InvalidPoolType","msg":"Invalid pool type"},{"code":6052,"name":"InvalidQuoteMint","msg":"Quote token must be SOL,USDC"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/meteora_vault.24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi.json b/migration/khadhroony-bot2-reference/idls/meteora_vault.24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi.json new file mode 100644 index 0000000..61034fa --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/meteora_vault.24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi.json @@ -0,0 +1 @@ +{"version":"0.7.2","name":"vault","docs":["Program for vault"],"instructions":[{"name":"initialize","docs":["initialize new vault"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["This is base account for all vault","No need base key now because we only allow 1 vault per token now","Vault account"]},{"name":"payer","isMut":true,"isSigner":true,"docs":["Payer can be anyone"]},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["Token vault account"]},{"name":"tokenMint","isMut":false,"isSigner":false,"docs":["Token mint account"]},{"name":"lpMint","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false,"docs":["rent"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["system_program"]}],"args":[]},{"name":"enableVault","docs":["enable vault"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["Vault account"]},{"name":"admin","isMut":false,"isSigner":true,"docs":["Admin account"]}],"args":[{"name":"enabled","type":"u8"}]},{"name":"setOperator","docs":["set new operator"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["Vault account"]},{"name":"operator","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true,"docs":["admin"]}],"args":[]},{"name":"initializeStrategy","docs":["Initialize a strategy and add strategy to vault.strategies index"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["Vault account"]},{"name":"strategyProgram","isMut":false,"isSigner":false},{"name":"strategy","isMut":true,"isSigner":false,"docs":["Strategy account"]},{"name":"reserve","isMut":true,"isSigner":false},{"name":"collateralVault","isMut":true,"isSigner":false,"docs":["Collateral vault account"]},{"name":"collateralMint","isMut":false,"isSigner":false,"docs":["Collateral mint account"]},{"name":"admin","isMut":true,"isSigner":true,"docs":["Admin account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program account"]},{"name":"rent","isMut":false,"isSigner":false,"docs":["Rent account"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Token program account"]}],"args":[{"name":"bumps","type":{"defined":"StrategyBumps"}},{"name":"strategyType","type":{"defined":"StrategyType"}}]},{"name":"removeStrategy","docs":["remove a strategy"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["Vault account"]},{"name":"strategy","isMut":true,"isSigner":false,"docs":["Strategy account"]},{"name":"strategyProgram","isMut":false,"isSigner":false},{"name":"collateralVault","isMut":true,"isSigner":false,"docs":["Collateral vault account"]},{"name":"reserve","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["token_vault"]},{"name":"feeVault","isMut":true,"isSigner":false,"docs":["fee_vault"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["lp_mint"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]},{"name":"admin","isMut":false,"isSigner":true,"docs":["admin"]}],"args":[]},{"name":"removeStrategy2","docs":["remove a strategy by advance payment"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["Vault account"]},{"name":"strategy","isMut":true,"isSigner":false,"docs":["Strategy account"]},{"name":"strategyProgram","isMut":false,"isSigner":false},{"name":"collateralVault","isMut":true,"isSigner":false,"docs":["Collateral vault account"]},{"name":"reserve","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["token_vault"]},{"name":"tokenAdminAdvancePayment","isMut":true,"isSigner":false,"docs":["token_advance_payemnt","the owner of token_advance_payment must be admin"]},{"name":"tokenVaultAdvancePayment","isMut":true,"isSigner":false,"docs":["token_vault_advance_payment","the account must be different from token_vault","the owner of token_advance_payment must be vault"]},{"name":"feeVault","isMut":true,"isSigner":false,"docs":["fee_vault"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["lp_mint"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]},{"name":"admin","isMut":false,"isSigner":true,"docs":["admin"]}],"args":[{"name":"maxAdminPayAmount","type":"u64"}]},{"name":"collectDust","docs":["collect token, that someone send wrongly","also help in case Mango reimbursement"],"accounts":[{"name":"vault","isMut":false,"isSigner":false,"docs":["vault"]},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["Token vault, must be different from vault.token_vault"]},{"name":"tokenAdmin","isMut":true,"isSigner":false,"docs":["token admin, enforce owner is admin to avoid mistake"]},{"name":"admin","isMut":false,"isSigner":true,"docs":["admin"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]}],"args":[]},{"name":"addStrategy","docs":["add a strategy"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["vault"]},{"name":"strategy","isMut":false,"isSigner":false,"docs":["strategy"]},{"name":"admin","isMut":false,"isSigner":true,"docs":["admin"]}],"args":[]},{"name":"depositStrategy","docs":["deposit liquidity to a strategy"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["vault"]},{"name":"strategy","isMut":true,"isSigner":false,"docs":["strategy"]},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["token_vault"]},{"name":"feeVault","isMut":true,"isSigner":false,"docs":["fee_vault"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["lp_mint"]},{"name":"strategyProgram","isMut":false,"isSigner":false},{"name":"collateralVault","isMut":true,"isSigner":false,"docs":["collateral_vault"]},{"name":"reserve","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]},{"name":"operator","isMut":false,"isSigner":true,"docs":["operator"]}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdrawStrategy","docs":["withdraw liquidity from a strategy"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["vault"]},{"name":"strategy","isMut":true,"isSigner":false,"docs":["strategy"]},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["token_vault"]},{"name":"feeVault","isMut":true,"isSigner":false,"docs":["fee_vault"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["lp_mint"]},{"name":"strategyProgram","isMut":false,"isSigner":false},{"name":"collateralVault","isMut":true,"isSigner":false,"docs":["collateral_vault"]},{"name":"reserve","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]},{"name":"operator","isMut":false,"isSigner":true,"docs":["operator"]}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdraw2","docs":["Withdraw v2. Withdraw from token vault if no remaining accounts are available. Else, it will attempt to withdraw from strategy and token vault. This method just proxy between 2 methods. Protocol integration should be using withdraw instead of this function."],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["vault"]},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["token_vault"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["lp_mint"]},{"name":"userToken","isMut":true,"isSigner":false,"docs":["user_token"]},{"name":"userLp","isMut":true,"isSigner":false,"docs":["user_lp"]},{"name":"user","isMut":false,"isSigner":true,"docs":["user"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]}],"args":[{"name":"unmintAmount","type":"u64"},{"name":"minOutAmount","type":"u64"}]},{"name":"deposit","docs":["user deposit liquidity to vault"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["vault"]},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["token_vault"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["lp_mint"]},{"name":"userToken","isMut":true,"isSigner":false,"docs":["user_token"]},{"name":"userLp","isMut":true,"isSigner":false,"docs":["user_lp"]},{"name":"user","isMut":false,"isSigner":true,"docs":["user"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]}],"args":[{"name":"tokenAmount","type":"u64"},{"name":"minimumLpTokenAmount","type":"u64"}]},{"name":"withdraw","docs":["user withdraw liquidity from vault"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["vault"]},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["token_vault"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["lp_mint"]},{"name":"userToken","isMut":true,"isSigner":false,"docs":["user_token"]},{"name":"userLp","isMut":true,"isSigner":false,"docs":["user_lp"]},{"name":"user","isMut":false,"isSigner":true,"docs":["user"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]}],"args":[{"name":"unmintAmount","type":"u64"},{"name":"minOutAmount","type":"u64"}]},{"name":"withdrawDirectlyFromStrategy","docs":["user withdraw liquidity from vault, if vault reserve doesn't have enough liquidity, it will withdraw from the strategy firstly"],"accounts":[{"name":"vault","isMut":true,"isSigner":false,"docs":["vault"]},{"name":"strategy","isMut":true,"isSigner":false,"docs":["strategy"]},{"name":"reserve","isMut":true,"isSigner":false},{"name":"strategyProgram","isMut":false,"isSigner":false},{"name":"collateralVault","isMut":true,"isSigner":false,"docs":["collateral_vault"]},{"name":"tokenVault","isMut":true,"isSigner":false,"docs":["token_vault"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["lp_mint"]},{"name":"feeVault","isMut":true,"isSigner":false,"docs":["fee_vault"]},{"name":"userToken","isMut":true,"isSigner":false,"docs":["user_token"]},{"name":"userLp","isMut":true,"isSigner":false,"docs":["user_lp"]},{"name":"user","isMut":false,"isSigner":true,"docs":["user"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token_program"]}],"args":[{"name":"unmintAmount","type":"u64"},{"name":"minOutAmount","type":"u64"}]}],"accounts":[{"name":"Vault","docs":["Vault struct"],"type":{"kind":"struct","fields":[{"name":"enabled","docs":["The flag, if admin set enable = false, then the user can only withdraw and cannot deposit in the vault."],"type":"u8"},{"name":"bumps","docs":["Vault nonce, to create vault seeds"],"type":{"defined":"VaultBumps"}},{"name":"totalAmount","docs":["The total liquidity of the vault, including remaining tokens in token_vault and the liquidity in all strategies."],"type":"u64"},{"name":"tokenVault","docs":["Token account, hold liquidity in vault reserve"],"type":"publicKey"},{"name":"feeVault","docs":["Hold lp token of vault, each time rebalance crank is called, vault calculate performance fee and mint corresponding lp token amount to fee_vault. fee_vault is owned by treasury address"],"type":"publicKey"},{"name":"tokenMint","docs":["Token mint that vault supports"],"type":"publicKey"},{"name":"lpMint","docs":["Lp mint of vault"],"type":"publicKey"},{"name":"strategies","docs":["The list of strategy addresses that vault supports, vault can support up to MAX_STRATEGY strategies at the same time."],"type":{"array":["publicKey",30]}},{"name":"base","docs":["The base address to create vault seeds"],"type":"publicKey"},{"name":"admin","docs":["Admin of vault"],"type":"publicKey"},{"name":"operator","docs":["Person who can send the crank. Operator can only send liquidity to strategies that admin defined, and claim reward to account of treasury address"],"type":"publicKey"},{"name":"lockedProfitTracker","docs":["Stores information for locked profit."],"type":{"defined":"LockedProfitTracker"}}]}},{"name":"Strategy","docs":["Strategy struct"],"type":{"kind":"struct","fields":[{"name":"reserve","docs":["Lending pool address, that the strategy will deposit/withdraw balance"],"type":"publicKey"},{"name":"collateralVault","docs":["The token account, that holds the collateral token"],"type":"publicKey"},{"name":"strategyType","docs":["Specify type of strategy"],"type":{"defined":"StrategyType"}},{"name":"currentLiquidity","docs":["The liquidity in strategy at the time vault deposit/withdraw from a lending protocol"],"type":"u64"},{"name":"bumps","docs":["Hold some bumps, in case the strategy needs to use other seeds to sign a CPI call."],"type":{"array":["u8",10]}},{"name":"vault","docs":["Vault address, that the strategy belongs"],"type":"publicKey"},{"name":"isDisable","docs":["If we remove strategy by remove_strategy2 endpoint, this account will be never added again"],"type":"u8"}]}}],"types":[{"name":"VaultBumps","docs":["Vault bumps struct"],"type":{"kind":"struct","fields":[{"name":"vaultBump","docs":["vault_bump"],"type":"u8"},{"name":"tokenVaultBump","docs":["token_vault_bump"],"type":"u8"}]}},{"name":"StrategyBumps","docs":["Strategy bumps struct"],"type":{"kind":"struct","fields":[{"name":"strategyIndex","docs":["strategy_index"],"type":"u8"},{"name":"otherBumps","docs":["Bumps of PDAs for the integrated protocol."],"type":{"array":["u8",10]}}]}},{"name":"LockedProfitTracker","docs":["LockedProfitTracker struct"],"type":{"kind":"struct","fields":[{"name":"lastUpdatedLockedProfit","docs":["The total locked profit from the last report"],"type":"u64"},{"name":"lastReport","docs":["The last timestamp (in seconds) rebalancing"],"type":"u64"},{"name":"lockedProfitDegradation","docs":["Rate per second of degradation"],"type":"u64"}]}},{"name":"StrategyType","docs":["StrategyType struct"],"type":{"kind":"enum","variants":[{"name":"PortFinanceWithoutLM"},{"name":"PortFinanceWithLM"},{"name":"SolendWithoutLM"},{"name":"Mango"},{"name":"SolendWithLM"},{"name":"ApricotWithoutLM"},{"name":"Francium"},{"name":"Tulip"},{"name":"Vault"},{"name":"Drift"},{"name":"Frakt"},{"name":"Marginfi"}]}}],"events":[{"name":"AddLiquidity","fields":[{"name":"lpMintAmount","type":"u64","index":false},{"name":"tokenAmount","type":"u64","index":false}]},{"name":"RemoveLiquidity","fields":[{"name":"lpUnmintAmount","type":"u64","index":false},{"name":"tokenAmount","type":"u64","index":false}]},{"name":"StrategyDeposit","fields":[{"name":"strategyType","type":{"defined":"StrategyType"},"index":false},{"name":"tokenAmount","type":"u64","index":false}]},{"name":"StrategyWithdraw","fields":[{"name":"strategyType","type":{"defined":"StrategyType"},"index":false},{"name":"collateralAmount","type":"u64","index":false},{"name":"estimatedTokenAmount","type":"u64","index":false}]},{"name":"ClaimReward","fields":[{"name":"strategyType","type":{"defined":"StrategyType"},"index":false},{"name":"tokenAmount","type":"u64","index":false},{"name":"mintAccount","type":"publicKey","index":false}]},{"name":"PerformanceFee","fields":[{"name":"lpMintMore","type":"u64","index":false}]},{"name":"ReportLoss","fields":[{"name":"strategy","type":"publicKey","index":false},{"name":"loss","type":"u64","index":false}]},{"name":"TotalAmount","fields":[{"name":"totalAmount","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"VaultIsDisabled","msg":"Vault is disabled"},{"code":6001,"name":"ExceededSlippage","msg":"Exceeded slippage tolerance"},{"code":6002,"name":"StrategyIsNotExisted","msg":"Strategy is not existed"},{"code":6003,"name":"UnAuthorized","msg":"UnAuthorized"},{"code":6004,"name":"MathOverflow","msg":"Math operation overflow"},{"code":6005,"name":"ProtocolIsNotSupported","msg":"Protocol is not supported"},{"code":6006,"name":"UnMatchReserve","msg":"Reserve does not support token mint"},{"code":6007,"name":"InvalidLockedProfitDegradation","msg":"lockedProfitDegradation is invalid"},{"code":6008,"name":"MaxStrategyReached","msg":"Maximum number of strategies have been reached"},{"code":6009,"name":"StrategyExisted","msg":"Strategy existed"},{"code":6010,"name":"InvalidUnmintAmount","msg":"Invalid unmint amount"},{"code":6011,"name":"InvalidAccountsForStrategy","msg":"Invalid accounts for strategy"},{"code":6012,"name":"InvalidBump","msg":"Invalid bump"},{"code":6013,"name":"AmountMustGreaterThanZero","msg":"Amount must be greater than 0"},{"code":6014,"name":"MangoIsNotSupportedAnymore","msg":"Mango is not supported anymore"},{"code":6015,"name":"StrategyIsNotSupported","msg":"Strategy is not supported"},{"code":6016,"name":"PayAmountIsExeeced","msg":"Pay amount is exceeded"},{"code":6017,"name":"FeeVaultIsNotSet","msg":"Fee vault is not set"},{"code":6018,"name":"LendingAssertionViolation","msg":"deposit amount in lending is not matched"},{"code":6019,"name":"HaveMoneyInLending","msg":"Cannot remove strategy becase we have some in lending"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/moonit.MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG.json b/migration/khadhroony-bot2-reference/idls/moonit.MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG.json new file mode 100644 index 0000000..8f66db1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/moonit.MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"token_launchpad","instructions":[{"name":"tokenMint","accounts":[{"name":"sender","isMut":true,"isSigner":true},{"name":"backendAuthority","isMut":false,"isSigner":true},{"name":"curveAccount","isMut":true,"isSigner":false},{"name":"mint","isMut":true,"isSigner":true},{"name":"mintMetadata","isMut":true,"isSigner":false,"docs":["Type validating that the account is owned by the System Program = uninitialized","seeds should ensure that the address is correct"]},{"name":"curveTokenAccount","isMut":true,"isSigner":false},{"name":"configAccount","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"mplTokenMetadata","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"mintParams","type":{"defined":"TokenMintParams"}}]},{"name":"buy","accounts":[{"name":"sender","isMut":true,"isSigner":true},{"name":"senderTokenAccount","isMut":true,"isSigner":false},{"name":"curveAccount","isMut":true,"isSigner":false},{"name":"curveTokenAccount","isMut":true,"isSigner":false},{"name":"dexFee","isMut":true,"isSigner":false},{"name":"helioFee","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"configAccount","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"data","type":{"defined":"TradeParams"}}]},{"name":"sell","accounts":[{"name":"sender","isMut":true,"isSigner":true},{"name":"senderTokenAccount","isMut":true,"isSigner":false},{"name":"curveAccount","isMut":true,"isSigner":false},{"name":"curveTokenAccount","isMut":true,"isSigner":false},{"name":"dexFee","isMut":true,"isSigner":false},{"name":"helioFee","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"configAccount","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"data","type":{"defined":"TradeParams"}}]},{"name":"migrateFunds","accounts":[{"name":"backendAuthority","isMut":false,"isSigner":true,"docs":["BE Authority"]},{"name":"migrationAuthority","isMut":true,"isSigner":true,"docs":["Migration Authority","Owner and Payer over Token Accounts, needs to be mutable"]},{"name":"curveAccount","isMut":true,"isSigner":false,"docs":["Curve Account","The account is closed after this instruction"]},{"name":"curveTokenAccount","isMut":true,"isSigner":false,"docs":["Curve Token Account","The account is closed after this instruction"]},{"name":"migrationAuthorityTokenAccount","isMut":true,"isSigner":false,"docs":["Authority token Account","Init on demand"]},{"name":"mint","isMut":true,"isSigner":false,"docs":["InterfaceAccount: checks program ownership + deserialize into Mint"]},{"name":"dexFeeAccount","isMut":true,"isSigner":false},{"name":"helioFeeAccount","isMut":true,"isSigner":false},{"name":"configAccount","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"configInit","accounts":[{"name":"configAuthority","isMut":true,"isSigner":true},{"name":"configAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"data","type":{"defined":"ConfigParams"}}]},{"name":"configUpdate","accounts":[{"name":"configAuthority","isMut":false,"isSigner":true},{"name":"configAccount","isMut":true,"isSigner":false}],"args":[{"name":"data","type":{"defined":"ConfigParams"}}]}],"accounts":[{"name":"ConfigAccount","type":{"kind":"struct","fields":[{"name":"migrationAuthority","type":"publicKey"},{"name":"backendAuthority","type":"publicKey"},{"name":"configAuthority","type":"publicKey"},{"name":"helioFee","type":"publicKey"},{"name":"dexFee","type":"publicKey"},{"name":"feeBps","type":"u16"},{"name":"dexFeeShare","type":"u8"},{"name":"migrationFee","type":"u64"},{"name":"marketcapThreshold","type":"u64"},{"name":"marketcapCurrency","type":{"defined":"Currency"}},{"name":"minSupportedDecimalPlaces","type":"u8"},{"name":"maxSupportedDecimalPlaces","type":"u8"},{"name":"minSupportedTokenSupply","type":"u64"},{"name":"maxSupportedTokenSupply","type":"u64"},{"name":"bump","type":"u8"},{"name":"coefB","type":"u32"}]}},{"name":"CurveAccount","type":{"kind":"struct","fields":[{"name":"totalSupply","type":"u64"},{"name":"curveAmount","type":"u64"},{"name":"mint","type":"publicKey"},{"name":"decimals","type":"u8"},{"name":"collateralCurrency","type":{"defined":"Currency"}},{"name":"curveType","type":{"defined":"CurveType"}},{"name":"marketcapThreshold","type":"u64"},{"name":"marketcapCurrency","type":{"defined":"Currency"}},{"name":"migrationFee","type":"u64"},{"name":"coefB","type":"u32"},{"name":"bump","type":"u8"},{"name":"migrationTarget","type":{"defined":"MigrationTarget"}}]}}],"types":[{"name":"TokenMintParams","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"decimals","type":"u8"},{"name":"collateralCurrency","type":"u8"},{"name":"amount","type":"u64"},{"name":"curveType","type":"u8"},{"name":"migrationTarget","type":"u8"}]}},{"name":"TradeParams","type":{"kind":"struct","fields":[{"name":"tokenAmount","type":"u64"},{"name":"collateralAmount","type":"u64"},{"name":"fixedSide","type":"u8"},{"name":"slippageBps","type":"u64"}]}},{"name":"ConfigParams","type":{"kind":"struct","fields":[{"name":"migrationAuthority","type":{"option":"publicKey"}},{"name":"backendAuthority","type":{"option":"publicKey"}},{"name":"configAuthority","type":{"option":"publicKey"}},{"name":"helioFee","type":{"option":"publicKey"}},{"name":"dexFee","type":{"option":"publicKey"}},{"name":"feeBps","type":{"option":"u16"}},{"name":"dexFeeShare","type":{"option":"u8"}},{"name":"migrationFee","type":{"option":"u64"}},{"name":"marketcapThreshold","type":{"option":"u64"}},{"name":"marketcapCurrency","type":{"option":"u8"}},{"name":"minSupportedDecimalPlaces","type":{"option":"u8"}},{"name":"maxSupportedDecimalPlaces","type":{"option":"u8"}},{"name":"minSupportedTokenSupply","type":{"option":"u64"}},{"name":"maxSupportedTokenSupply","type":{"option":"u64"}},{"name":"coefB","type":{"option":"u32"}}]}},{"name":"Currency","type":{"kind":"enum","variants":[{"name":"Sol"}]}},{"name":"CurveType","type":{"kind":"enum","variants":[{"name":"LinearV1"},{"name":"ConstantProductV1"}]}},{"name":"MigrationTarget","type":{"kind":"enum","variants":[{"name":"Raydium"},{"name":"Meteora"}]}},{"name":"TradeType","type":{"kind":"enum","variants":[{"name":"Buy"},{"name":"Sell"}]}},{"name":"FixedSide","type":{"kind":"enum","variants":[{"name":"ExactIn"},{"name":"ExactOut"}]}}],"events":[{"name":"TradeEvent","fields":[{"name":"amount","type":"u64","index":false},{"name":"collateralAmount","type":"u64","index":false},{"name":"dexFee","type":"u64","index":false},{"name":"helioFee","type":"u64","index":false},{"name":"allocation","type":"u64","index":false},{"name":"curve","type":"publicKey","index":false},{"name":"costToken","type":"publicKey","index":false},{"name":"sender","type":"publicKey","index":false},{"name":"type","type":{"defined":"TradeType"},"index":false},{"name":"label","type":"string","index":true}]},{"name":"MigrationEvent","fields":[{"name":"tokensMigrated","type":"u64","index":false},{"name":"tokensBurned","type":"u64","index":false},{"name":"collateralMigrated","type":"u64","index":false},{"name":"fee","type":"u64","index":false},{"name":"label","type":"string","index":true}]}],"errors":[{"code":6000,"name":"InsufficientBalance","msg":"Insufficient SOL to pay for the transaction."},{"code":6001,"name":"InvalidAmount","msg":"The amount must be available in the curve ."},{"code":6002,"name":"InvalidSlippage","msg":"The slippage must be under 100 percent."},{"code":6003,"name":"SlippageOverflow","msg":"The cost amount is not in the allowed slippage interval."},{"code":6004,"name":"ThresholdReached","msg":"Threshold limit exceeded."},{"code":6005,"name":"InvalidTokenAccount","msg":"Trade disabled, market cap threshold reached."},{"code":6006,"name":"InvalidCurveAccount","msg":"Invalid curve account."},{"code":6007,"name":"InvalidFeeAccount","msg":"Invalid fee account address."},{"code":6008,"name":"CurveLimit","msg":"Curve limit exceeded."},{"code":6009,"name":"InvalidCurveType","msg":"Invalid curve type."},{"code":6010,"name":"InvalidCurrency","msg":"Invalid currency."},{"code":6011,"name":"Arithmetics","msg":"Artithmetics error"},{"code":6012,"name":"ThresholdNotHit","msg":"Market Cap threshold not hit, cannot migrate funds yet"},{"code":6013,"name":"InvalidAuthority","msg":"Invalid Authority provided."},{"code":6014,"name":"TradeAmountTooLow","msg":"Trade amount too low , resulting in 0 costs"},{"code":6015,"name":"ConfigFieldMissing","msg":"Config field needs to be present during initialization"},{"code":6016,"name":"DifferentCurrencies","msg":"Unsupported different currency types"},{"code":6017,"name":"BasisPointTooHigh","msg":"Basis points too high"},{"code":6018,"name":"FeeShareTooHigh","msg":"Fee share too High"},{"code":6019,"name":"TokenDecimalsOutOfRange","msg":"Token decimals are not within the supported range"},{"code":6020,"name":"TokenNameTooLong","msg":"Token Name too long, max supported length is 32 bytes"},{"code":6021,"name":"TokenSymbolTooLong","msg":"Token Symbol too long, max supported length is 10 bytes"},{"code":6022,"name":"TokenURITooLong","msg":"Token URI too long, max supported length is 200 bytes"},{"code":6023,"name":"IncorrectDecimalPlacesBounds","msg":"Minimum Decimal Places cannot be lower than Maximum Decimal Places"},{"code":6024,"name":"IncorrectTokenSupplyBounds","msg":"Minimum Token Supply cannot be lower than Maximum Token Supply"},{"code":6025,"name":"TotalSupplyOutOfBounds","msg":"Token Total Supply out of bounds"},{"code":6026,"name":"FinalCollateralTooLow","msg":"This setup will produce final collateral amount less than the migration fee"},{"code":6027,"name":"CoefficientZero","msg":"One of the Coefficients is equal to ZERO"},{"code":6028,"name":"MarketCapThresholdTooLow","msg":"Market cap Threshold under the Hard lower bound limits"},{"code":6029,"name":"CoefBOutofBounds","msg":"Default coef_b set out of hard limit bounds"},{"code":6030,"name":"IncorrectMarketCap","msg":"For Constant Product the Market Cap threshold cannot be higher than 325 SOL"},{"code":6031,"name":"IncorrectDecimals","msg":"For Constant Product the Decimal places cannot be other than 9"},{"code":6032,"name":"IncorrectMaxSupply","msg":"For Constant Product the Maximal Token Supply cannot be other than 1_000_000_000"},{"code":6033,"name":"MarketCapTooHigh","msg":"Market Cap Threshold set too high, will not be hit even if Curve Hard Limit reached"},{"code":6034,"name":"InvalidMigrationTarget","msg":"This Migration Target is not supported!"},{"code":6035,"name":"General","msg":"General error"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/numeraire.NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P.json b/migration/khadhroony-bot2-reference/idls/numeraire.NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P.json new file mode 100644 index 0000000..fb67676 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/numeraire.NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P.json @@ -0,0 +1 @@ +{"address":"NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P","metadata":{"name":"numeraire","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"add_liquidity","docs":["Pool Owner/Whitelist only functions"],"discriminator":[181,157,89,67,143,182,52,72],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"const","value":[108,105,113,117,105,100,105,116,121]}]}},{"name":"payer_lp_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"payer"},{"kind":"account","path":"token_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"data","type":{"defined":{"name":"AddLiquidityData"}}}],"returns":"u64"},{"name":"compound","discriminator":[165,208,251,78,242,160,141,47],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"const","value":[108,105,113,117,105,100,105,116,121]}]}},{"name":"payer_lp_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"payer"},{"kind":"account","path":"token_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[]},{"name":"create_pool","discriminator":[233,146,209,142,207,104,64,188],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"arg","path":"data.pool_seed"}]}},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"const","value":[108,105,113,117,105,100,105,116,121]}]}},{"name":"creator_lp_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"payer"},{"kind":"account","path":"token_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","writable":true,"signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"data","type":{"defined":{"name":"CreateStablePoolData"}}}]},{"name":"init_virtual_stable_pair","discriminator":[228,18,163,161,101,204,106,172],"accounts":[{"name":"x_mint"},{"name":"pair","writable":true,"pda":{"seeds":[{"kind":"account","path":"x_mint"},{"kind":"arg","path":"data.pair_seed"}]}},{"name":"pair_authority","pda":{"seeds":[{"kind":"account","path":"pair"},{"kind":"const","value":[97,117,116,104,111,114,105,116,121]}]}},{"name":"x_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"pair"},{"kind":"const","value":[108,105,113,117,105,100,105,116,121]}]}},{"name":"x_adder","writable":true,"pda":{"seeds":[{"kind":"account","path":"payer"},{"kind":"account","path":"token_program"},{"kind":"account","path":"x_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","writable":true,"signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"]},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"data","type":{"defined":{"name":"InitVirtualStablePairData"}}}]},{"name":"remove_all_liquidity","discriminator":[10,51,61,35,112,105,24,85],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"const","value":[108,105,113,117,105,100,105,116,121]}]}},{"name":"payer_lp_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"payer"},{"kind":"account","path":"token_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[]},{"name":"remove_liquidity","discriminator":[80,85,209,72,24,206,177,108],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"const","value":[108,105,113,117,105,100,105,116,121]}]}},{"name":"payer_lp_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"payer"},{"kind":"account","path":"token_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"data","type":{"defined":{"name":"RemoveLiquidityData"}}}],"returns":"u64"},{"name":"set_fee","discriminator":[18,154,24,18,237,214,19,80],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"payer","signer":true}],"args":[{"name":"data","type":{"defined":{"name":"SetFeeData"}}}]},{"name":"set_inv_t_max","discriminator":[218,209,244,237,211,236,98,58],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"payer","signer":true}],"args":[{"name":"data","type":{"defined":{"name":"SetInvTMaxData"}}}]},{"name":"set_lp_token_metadata","discriminator":[71,73,56,155,202,142,100,150],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"const","value":[108,105,113,117,105,100,105,116,121]}]}},{"name":"metadata_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"account","path":"token_metadata_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"account","path":"token_metadata_program"}}},{"name":"payer","signer":true},{"name":"token_metadata_program","docs":["Solana ecosystem accounts"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"instructions","address":"Sysvar1nstructions1111111111111111111111111"}],"args":[{"name":"data","type":{"defined":{"name":"SetMetadataData"}}}]},{"name":"set_numeraire_owner","discriminator":[6,199,177,104,86,61,93,253],"accounts":[{"name":"numeraire_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"pair_mint","optional":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","docs":["Solana ecosystem accounts"],"address":"11111111111111111111111111111111"}],"args":[{"name":"data","type":{"defined":{"name":"SetOwnerData"}}}]},{"name":"set_numeraire_status","discriminator":[10,17,5,71,204,171,126,173],"accounts":[{"name":"numeraire_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"pair_mint","optional":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","docs":["Solana ecosystem accounts"],"address":"11111111111111111111111111111111"}],"args":[{"name":"data","type":{"defined":{"name":"SetStatusData"}}}]},{"name":"set_numeraire_whitelisted_pool_creator","discriminator":[147,51,31,255,111,2,189,173],"accounts":[{"name":"numeraire_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"pair_mint","optional":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","docs":["Solana ecosystem accounts"],"address":"11111111111111111111111111111111"}],"args":[{"name":"data","type":{"defined":{"name":"SetWhilelistedAddrData"}}}]},{"name":"set_owner","discriminator":[72,202,120,52,77,128,96,197],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"payer","signer":true}],"args":[{"name":"data","type":{"defined":{"name":"SetOwnerData"}}}]},{"name":"set_rate","discriminator":[99,58,170,238,160,120,74,11],"accounts":[{"name":"numeraire_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"pair_mint","optional":true},{"name":"payer","writable":true,"signer":true},{"name":"system_program","docs":["Solana ecosystem accounts"],"address":"11111111111111111111111111111111"}],"args":[{"name":"data","type":{"defined":{"name":"SetRateData"}}}]},{"name":"set_status","docs":["Pool Owner only functions"],"discriminator":[181,184,224,203,193,29,177,224],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"payer","signer":true}],"args":[{"name":"data","type":{"defined":{"name":"SetStatusData"}}}]},{"name":"set_whitelisted_adder","discriminator":[74,38,140,129,228,73,236,105],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"payer","signer":true}],"args":[{"name":"data","type":{"defined":{"name":"SetWhilelistedAddrData"}}}]},{"name":"skim","discriminator":[238,120,221,138,82,60,100,218],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"const","value":[108,105,113,117,105,100,105,116,121]}]}},{"name":"payer_lp_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"payer"},{"kind":"account","path":"token_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[]},{"name":"swap_exact_in","discriminator":[104,104,131,86,161,189,180,216],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"in_mint","writable":true},{"name":"out_mint","writable":true},{"name":"in_trader","writable":true},{"name":"out_trader","writable":true},{"name":"in_vault","writable":true,"optional":true},{"name":"out_vault","writable":true,"optional":true},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","docs":["The trader account (executing the swap, paying for all rents)"],"signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"data","type":{"defined":{"name":"SwapExactInHintlessData"}}}],"returns":"u64"},{"name":"swap_exact_in_hinted","discriminator":[98,239,244,233,16,236,40,49],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"in_mint","writable":true},{"name":"out_mint","writable":true},{"name":"in_trader","writable":true},{"name":"out_trader","writable":true},{"name":"in_vault","writable":true,"optional":true},{"name":"out_vault","writable":true,"optional":true},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","docs":["The trader account (executing the swap, paying for all rents)"],"signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"data","type":{"defined":{"name":"SwapExactInData"}}}],"returns":"u64"},{"name":"swap_exact_in_quote","discriminator":[68,209,177,170,185,100,29,191],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"in_mint","writable":true},{"name":"out_mint","writable":true},{"name":"in_trader","writable":true},{"name":"out_trader","writable":true},{"name":"in_vault","writable":true,"optional":true},{"name":"out_vault","writable":true,"optional":true},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","docs":["The trader account (executing the swap, paying for all rents)"],"signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"data","type":{"defined":{"name":"SwapExactInData"}}}],"returns":"u64"},{"name":"swap_exact_out","discriminator":[250,73,101,33,38,207,75,184],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"in_mint","writable":true},{"name":"out_mint","writable":true},{"name":"in_trader","writable":true},{"name":"out_trader","writable":true},{"name":"in_vault","writable":true,"optional":true},{"name":"out_vault","writable":true,"optional":true},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","docs":["The trader account (executing the swap, paying for all rents)"],"signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"data","type":{"defined":{"name":"SwapExactOutHintlessData"}}}],"returns":"u64"},{"name":"swap_exact_out_hinted","discriminator":[153,208,206,70,62,234,98,182],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"}]}},{"name":"in_mint","writable":true},{"name":"out_mint","writable":true},{"name":"in_trader","writable":true},{"name":"out_trader","writable":true},{"name":"in_vault","writable":true,"optional":true},{"name":"out_vault","writable":true,"optional":true},{"name":"numeraire_config","pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103]}]}},{"name":"payer","docs":["The trader account (executing the swap, paying for all rents)"],"signer":true},{"name":"token_program","docs":["Solana ecosystem accounts"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"data","type":{"defined":{"name":"SwapExactOutData"}}}],"returns":"u64"}],"accounts":[{"name":"NumeraireConfig","discriminator":[230,62,124,43,102,101,88,63]},{"name":"StablePool","discriminator":[239,91,93,162,171,14,42,66]},{"name":"VirtualStablePair","discriminator":[112,153,135,223,53,247,129,101]}],"events":[{"name":"AddLiquidity","discriminator":[31,94,125,90,227,52,61,186]},{"name":"Quote","discriminator":[133,244,92,134,193,24,187,158]},{"name":"RemoveLiquidity","discriminator":[116,244,97,232,103,31,152,58]},{"name":"SwapExactIn","discriminator":[147,136,213,11,150,23,141,152]},{"name":"SwapExactOut","discriminator":[71,66,127,123,231,29,227,92]}],"errors":[{"code":6000,"name":"OnlyOwner","msg":"Attempt to call an owner only function not by the owner"},{"code":6001,"name":"InvalidTokenAccountData","msg":"Token account data is not as expected"},{"code":6002,"name":"InvalidAccountData","msg":"Account data is not as expected"},{"code":6003,"name":"DecimalsMismatch","msg":"Token pair decimals mismatch"},{"code":6004,"name":"InvalidTokenExtension","msg":"Token should not have this extension"},{"code":6005,"name":"IncorrectAccounts","msg":"Wrong account(s) passed as input"},{"code":6006,"name":"IncorrectAuthority","msg":"Wrong authority passed as input"},{"code":6007,"name":"InvalidDelegate","msg":"Delegate not allowed"},{"code":6008,"name":"UnsupportedFeature","msg":"This feature is currently unsupported or unenabled"},{"code":6009,"name":"FunctionPaused","msg":"This function is currently paused"},{"code":6010,"name":"InvalidFee","msg":"Fee must be less than 100 basis points"},{"code":6011,"name":"InvalidPoolCreate","msg":"Token mints must be ordered by pubkey"},{"code":6012,"name":"InvalidCurveParams","msg":"Curve params must be positive"},{"code":6013,"name":"InsufficientLiquidity","msg":"Pool has too little liquidity for action"},{"code":6014,"name":"LiquidityAddUnbalanced","msg":"Liquidity add does not make pool balanced (or curve params are asymmetric)"},{"code":6015,"name":"InsufficientBalance","msg":"Input is more than trader balance"},{"code":6016,"name":"InputTooSmall","msg":"Input is below the minimum expected"},{"code":6017,"name":"InputTooBig","msg":"Input is more than available liquidity"},{"code":6018,"name":"OutputTooSmall","msg":"Output is below the minimum expected"},{"code":6019,"name":"InvariantOverflow","msg":"Invariant computation overflowed"},{"code":6020,"name":"InvariantViolated","msg":"Invariant does not hold"},{"code":6021,"name":"LiquidityAddTooSmall","msg":"Depositing too little liquidity"},{"code":6022,"name":"UnsupportedTokenProgram","msg":"Only Token Program 2022 and/or Token Program are supported"},{"code":6023,"name":"InvalidPoolWeights","msg":"Some pool weights are zero or nonzero and shouldn't be"},{"code":6024,"name":"SwapOverflowError","msg":"A swap math operation overflowed"},{"code":6025,"name":"LiquidityMathOverflow","msg":"An add/remove liquidity math operation overflowed"},{"code":6026,"name":"InvalidBalanceDeltas","msg":"Some add/remove balance deltas are nonzero and shouldn't be"},{"code":6027,"name":"InvalidPoolParams","msg":"The provided pool params were not well formed"},{"code":6028,"name":"InvalidHints","msg":"The hints provided do not bound the swap amounts"},{"code":6029,"name":"FeeError","msg":"Unexpected fee result"},{"code":6030,"name":"VaultOverflowError","msg":"A vault math operation overflowed"},{"code":6031,"name":"OutputTooBig","msg":"Output is above the maximum expected"},{"code":6032,"name":"Unreachable","msg":"This cannot happen"},{"code":6033,"name":"InvalidAction","msg":"This action is not allowed"}],"types":[{"name":"AddLiquidity","type":{"kind":"struct","fields":[{"name":"lp_token_mint_amount","type":"u64"},{"name":"x_reserve_deltas","type":{"array":["u64",10]}},{"name":"y_reserve_deltas","type":{"array":["u64",10]}},{"name":"inv_L_deltas","type":{"array":["u64",10]}},{"name":"min_lp_token_mint_amount","type":"u64"},{"name":"trader","type":"pubkey"},{"name":"pool","type":"pubkey"}]}},{"name":"AddLiquidityData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"max_amounts_in","type":{"array":["u64",10]}},{"name":"min_lp_token_mint_amount","type":"u64"},{"name":"take_swaps","type":"u8"},{"name":"swap_paths","type":{"array":["u8",10]}},{"name":"swap_amounts","type":{"array":["u64",10]}}]}},{"name":"CreateStablePoolData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"decimals","type":"u8"},{"name":"fee_num","type":"u32"},{"name":"fee_denom","type":"u32"},{"name":"pool_seed","type":"pubkey"},{"name":"weights","type":{"array":["u32",10]}},{"name":"inv_T","type":"u64"},{"name":"inv_T_max","type":"u64"}]}},{"name":"InitVirtualStablePairData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"decimals","type":"u8"},{"name":"init_amount","type":"u64"},{"name":"curve_Amp","type":"u128"},{"name":"curve_a","type":"u128"},{"name":"curve_b","type":"u128"},{"name":"curve_alpha","type":"u64"},{"name":"curve_beta","type":"u64"},{"name":"pair_seed","type":"pubkey"}]}},{"name":"NumeraireConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"owner","type":"pubkey"},{"name":"status","type":"u32"},{"name":"rate_mints","type":{"array":["pubkey",10]}},{"name":"rate_nums","type":{"array":["u32",10]}},{"name":"rate_denoms","type":{"array":["u32",10]}},{"name":"_padding","type":{"array":["u8",12]}},{"name":"padding","type":{"array":["u8",1024]}}]}},{"name":"Quote","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"}]}},{"name":"RemoveLiquidity","type":{"kind":"struct","fields":[{"name":"lp_token_redeem_amount","type":"u64"},{"name":"x_reserve_deltas","type":{"array":["u64",10]}},{"name":"y_reserve_deltas","type":{"array":["u64",10]}},{"name":"inv_L_deltas","type":{"array":["u64",10]}},{"name":"min_amounts_out","type":{"array":["u64",10]}},{"name":"trader","type":"pubkey"},{"name":"pool","type":"pubkey"}]}},{"name":"RemoveLiquidityData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"lp_token_redeem_amount","type":"u64"},{"name":"min_amounts_out","type":{"array":["u64",10]}},{"name":"out_index","type":"u8"}]}},{"name":"SetFeeData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"fee_num","type":"u32"},{"name":"fee_denom","type":"u32"}]}},{"name":"SetInvTMaxData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"inv_T_max","type":"u64"}]}},{"name":"SetMetadataData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]}},{"name":"SetOwnerData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"owner","type":"pubkey"}]}},{"name":"SetRateData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"rate_mint","type":"pubkey"},{"name":"rate_num","type":"u32"},{"name":"rate_denom","type":"u32"}]}},{"name":"SetStatusData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"status","type":"u32"}]}},{"name":"SetWhilelistedAddrData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"whitelisted_addr","type":"pubkey"}]}},{"name":"StablePool","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"pool_seed","type":"pubkey"},{"name":"lp_mint","type":"pubkey"},{"name":"whitelisted_adder","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"inv_T","type":"u64"},{"name":"inv_T_max","type":"u64"},{"name":"pairs","type":{"array":[{"defined":{"name":"VirtualStablePair"}},10]}},{"name":"weights","type":{"array":["u32",10]}},{"name":"total_weight","type":"u64"},{"name":"status","type":"u32"},{"name":"fee_num","type":"u32"},{"name":"fee_denom","type":"u32"},{"name":"decimals","type":"u8"},{"name":"num_stables","type":"u8"},{"name":"_padding","type":{"array":["u8",2]}},{"name":"padding","type":{"array":["u8",128]}}]}},{"name":"SwapExactIn","type":{"kind":"struct","fields":[{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"},{"name":"min_amount_out","type":"u64"},{"name":"trader","type":"pubkey"},{"name":"in_index","type":"u8"},{"name":"out_index","type":"u8"},{"name":"pool","type":"pubkey"}]}},{"name":"SwapExactInData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"in_index","type":"u8"},{"name":"out_index","type":"u8"},{"name":"exact_amount_in","type":"u64"},{"name":"min_amount_out","type":"u64"},{"name":"hints","type":{"array":["u64",10]}},{"name":"path_hints","type":{"array":["u8",10]}}]}},{"name":"SwapExactInHintlessData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"in_index","type":"u8"},{"name":"out_index","type":"u8"},{"name":"exact_amount_in","type":"u64"},{"name":"min_amount_out","type":"u64"}]}},{"name":"SwapExactOut","type":{"kind":"struct","fields":[{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"},{"name":"max_amount_in","type":"u64"},{"name":"trader","type":"pubkey"},{"name":"in_index","type":"u8"},{"name":"out_index","type":"u8"},{"name":"pool","type":"pubkey"}]}},{"name":"SwapExactOutData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"in_index","type":"u8"},{"name":"out_index","type":"u8"},{"name":"exact_amount_out","type":"u64"},{"name":"max_amount_in","type":"u64"},{"name":"hints","type":{"array":["u64",10]}},{"name":"path_hints","type":{"array":["u8",10]}}]}},{"name":"SwapExactOutHintlessData","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"in_index","type":"u8"},{"name":"out_index","type":"u8"},{"name":"exact_amount_out","type":"u64"},{"name":"max_amount_in","type":"u64"}]}},{"name":"VirtualStablePair","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"pair_authority","type":"pubkey"},{"name":"x_reserve_amount","type":"u64"},{"name":"y_reserve","type":"u64"},{"name":"curve_Amp","docs":["Curve params. Define bonding curve shape: x + y + a + b - A / (x + a) - A / (y + b) - D = 0"],"type":"u128"},{"name":"curve_a","type":"u128"},{"name":"curve_b","type":"u128"},{"name":"inv_L","docs":["Invariant constant. For invariant: x/L + y/L + a + b - A / (x/L + a) - A / (y/L + b) - D = 0"],"type":"u128"},{"name":"owner","type":"pubkey"},{"name":"x_mint","type":"pubkey"},{"name":"x_vault","type":"pubkey"},{"name":"curve_alpha","type":"u64"},{"name":"curve_beta","type":"u64"},{"name":"newest_rate_num","type":"u32"},{"name":"newest_rate_denom","type":"u32"},{"name":"decimals","type":"u8"},{"name":"pair_index","type":"u8"},{"name":"x_is_2022","type":"u8"},{"name":"_padding","type":{"array":["u8",5]}},{"name":"padding","type":{"array":["u8",128]}}]}}],"constants":[{"name":"AUTHORITY_SEED","type":"bytes","value":"[97, 117, 116, 104, 111, 114, 105, 116, 121]"},{"name":"CONFIG_SEED","type":"bytes","value":"[99, 111, 110, 102, 105, 103]"},{"name":"LIQUIDITY_SEED","type":"bytes","value":"[108, 105, 113, 117, 105, 100, 105, 116, 121]"},{"name":"LP_TOKEN_PROGRAM","type":"pubkey","value":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"MAX_STABLES_PER_POOL","type":"u8","value":"10"},{"name":"NORMALIZED_VALUE_DECIMALS","type":"u8","value":"6"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/okx_dex_router.proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u.json b/migration/khadhroony-bot2-reference/idls/okx_dex_router.proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u.json new file mode 100644 index 0000000..0366a0a --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/okx_dex_router.proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u.json @@ -0,0 +1 @@ +{"address":"proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u","metadata":{"name":"OKX: DEX Router","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"claim","discriminator":[62,198,214,193,213,159,108,210],"accounts":[{"name":"signer","writable":true,"signer":true,"address":"CjoV5B96reuCfPh2rRK11G1QptG97jZdyZArTn3EN1Mj"},{"name":"receiver","writable":true},{"name":"source_token_account","writable":true,"optional":true},{"name":"destination_token_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"address":"ARu4n5mFdZogZAravu7CcizaojWnS6oqka37gdLT5SZn"},{"name":"token_mint","optional":true},{"name":"token_program","optional":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[]},{"name":"claim_cashback_pumpfun","discriminator":[83,1,245,125,201,207,209,249],"accounts":[{"name":"signer","signer":true,"address":"CjoV5B96reuCfPh2rRK11G1QptG97jZdyZArTn3EN1Mj"},{"name":"sa_authority","docs":["PumpFun transfers claimed SOL directly to this account (account[0] in the CPI)."],"writable":true,"address":"ARu4n5mFdZogZAravu7CcizaojWnS6oqka37gdLT5SZn"},{"name":"user_volume_accumulator","docs":["Holds native SOL accumulated as cashback; emptied by PumpFun during claim."],"writable":true},{"name":"receiver","docs":["after PumpFun deposits it there."],"writable":true},{"name":"event_authority","address":"Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"pumpfun_program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"}],"args":[]},{"name":"claim_cashback_pumpswap","discriminator":[253,242,176,173,217,78,100,25],"accounts":[{"name":"signer","signer":true,"address":"CjoV5B96reuCfPh2rRK11G1QptG97jZdyZArTn3EN1Mj"},{"name":"sa_authority","writable":true,"address":"ARu4n5mFdZogZAravu7CcizaojWnS6oqka37gdLT5SZn"},{"name":"user_volume_accumulator","writable":true},{"name":"wsol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"spl_token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"uva_wsol_ata","writable":true},{"name":"sa_wsol_ata","docs":["PumpSwap requires account[5] to be a token account owned by authority_pda","(same owner as user_volume_accumulator). WSOL is deposited here by PumpSwap,","then forwarded to receiver_wsol_ata via a second SPL transfer."],"writable":true},{"name":"receiver_wsol_ata","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","address":"GS4CU59F31iL7aR2Q8zVS8DRrcRnXX1yjQ66TqNVQnaR"},{"name":"pumpswap_program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"}],"args":[]},{"name":"create_token_account","discriminator":[147,241,123,100,244,132,174,118],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"owner"},{"name":"token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"token_mint"},{"kind":"account","path":"owner"}]}},{"name":"token_mint","address":"So11111111111111111111111111111111111111112"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bump","type":"u8"}]},{"name":"create_token_account_with_seed","discriminator":[125,191,239,140,66,8,9,228],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"owner"},{"name":"token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"token_mint"},{"kind":"account","path":"owner"},{"kind":"arg","path":"seed"}]}},{"name":"token_mint","address":"So11111111111111111111111111111111111111112"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bump","type":"u8"},{"name":"seed","type":"u32"}]},{"name":"init_token_ledger","discriminator":[157,116,66,59,137,180,49,157],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"token_ledger","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"u8"}]},{"name":"proxy_swap","discriminator":[19,44,130,148,72,56,44,238],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"sa_authority","optional":true,"pda":{"seeds":[{"kind":"const","value":[111,107,120,95,115,97]}]}},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}}]},{"name":"set_token_ledger","discriminator":[228,85,185,112,78,79,77,2],"accounts":[{"name":"token_ledger","writable":true},{"name":"token_account","docs":["SPL / Token-2022 token account to snapshot."]}],"args":[]},{"name":"swap","discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"payer","signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}}]},{"name":"swap_tob","discriminator":[170,41,85,177,132,80,31,53],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"trim_rate","type":"u8"}]},{"name":"swap_tob_enhanced","discriminator":[190,156,169,176,149,154,161,108],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"trim_rate","type":"u8"},{"name":"charge_rate","type":"u16"}]},{"name":"swap_tob_v2","docs":["Used to support commission distribution between parent and child nodes in TOB business"],"discriminator":[72,1,215,242,8,75,54,216],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"parent_commission_account","writable":true},{"name":"child_commission_account","writable":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"total_commission_info","type":"u32"},{"name":"parent_commission_rate","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"trim_rate","type":"u8"}]},{"name":"swap_tob_with_receiver","discriminator":[223,170,216,234,204,6,241,25],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"sol_receiver","docs":["Optional SOL receiver account","- None: normal swap or SOL stays with payer","- Some: SOL receiver when converting wSOL -> SOL"],"writable":true,"optional":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"trim_rate","type":"u8"}]},{"name":"swap_tob_with_receiver_token_ledger","discriminator":[239,93,10,202,161,134,127,130],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"sol_receiver","docs":["Optional SOL receiver account","- None: normal swap or SOL stays with payer","- Some: SOL receiver when converting wSOL -> SOL"],"writable":true,"optional":true},{"name":"token_ledger"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgsTokenLedger"}}},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"trim_rate","type":"u8"}]},{"name":"swap_tob_with_token_ledger","discriminator":[36,92,147,219,26,176,159,90],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"token_ledger"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgsTokenLedger"}}},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"trim_rate","type":"u8"}]},{"name":"swap_toc","discriminator":[187,201,212,51,16,155,236,60],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"}]},{"name":"swap_toc_v2","docs":["Used to support commission distribution between parent and child nodes in TOC business"],"discriminator":[127,214,107,189,23,90,47,104],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"parent_commission_account","writable":true},{"name":"child_commission_account","writable":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"total_commission_info","type":"u32"},{"name":"parent_commission_rate","type":"u32"},{"name":"platform_fee_rate","type":"u16"}]},{"name":"wrap_unwrap","discriminator":[220,101,139,249,41,190,118,199],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"payer_wsol_account","writable":true},{"name":"wsol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"temp_wsol_account","writable":true,"optional":true,"pda":{"seeds":[{"kind":"const","value":[116,101,109,112,95,119,115,111,108]},{"kind":"account","path":"payer"}]}},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"authority_pda","docs":["Used for signing fee transfers from authority_pda (SOL) or wsol_sa (WSOL)"],"writable":true,"optional":true},{"name":"wsol_sa","docs":["This is the authority_pda's associated token account for WSOL"],"writable":true,"optional":true},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"PlatformFeeWrapUnwrapArgs"}}}]},{"name":"wrap_unwrap_with_receiver","docs":["Wrap/Unwrap with optional specified receiver","- Wrap (SOL -> WSOL): receiver is WSOL token account (ATA)","- Unwrap (WSOL -> SOL): receiver is system account (EOA)","Transfer amount:","- From fee: amount_in","- To fee: amount_in - fees"],"discriminator":[123,25,47,134,233,167,171,170],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"payer_wsol_account","writable":true},{"name":"wsol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"temp_wsol_account","writable":true,"optional":true,"pda":{"seeds":[{"kind":"const","value":[116,101,109,112,95,119,115,111,108]},{"kind":"account","path":"payer"}]}},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"authority_pda","docs":["Used for signing fee transfers from authority_pda (SOL) or wsol_sa (WSOL)"],"writable":true,"optional":true},{"name":"wsol_sa","docs":["This is the authority_pda's associated token account for WSOL"],"writable":true,"optional":true},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"receiver","docs":["- Wrap: WSOL token account (ATA) to receive WSOL","- Unwrap: System account (EOA) to receive SOL"],"writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"PlatformFeeWrapUnwrapArgs"}}}]}],"accounts":[{"name":"TokenLedger","discriminator":[156,247,9,188,54,108,85,77]}],"events":[{"name":"SwapCpiEvent2","discriminator":[21,94,224,53,220,232,193,94]},{"name":"SwapEvent","discriminator":[64,198,205,232,38,8,113,226]},{"name":"SwapTobV2CpiEvent2","discriminator":[244,105,184,90,60,165,36,33]},{"name":"SwapTocV2CpiEvent2","discriminator":[102,16,181,62,201,71,212,41]},{"name":"SwapWithFeesCpiEvent2","discriminator":[12,134,38,93,167,151,42,69]},{"name":"SwapWithFeesCpiEventEnhanced2","discriminator":[84,79,219,247,218,156,219,177]}],"errors":[{"code":6000,"name":"RoutesCannotBeEmpty","msg":"Routes cannot be empty"},{"code":6001,"name":"TooManyRoutes","msg":"Too many routes"},{"code":6002,"name":"MultipleOrNoOutputNode","msg":"Multiple or no output node"},{"code":6003,"name":"MultipleOrNoInputNode","msg":"Multiple or no input node"},{"code":6004,"name":"NotAllNodesProcessed","msg":"Not all nodes are processed"},{"code":6005,"name":"InputIndexShouldStartFromZero","msg":"Input index should start from zero"},{"code":6006,"name":"CircularDependency","msg":"Circular dependency detected in DAG"},{"code":6007,"name":"InvalidWeight","msg":"Invalid weight"},{"code":6008,"name":"InvalidNodeOrder","msg":"Invalid node order"},{"code":6009,"name":"TargetNodeNotFound","msg":"Target node not found"},{"code":6010,"name":"MinReturnNotReached","msg":"Min return not reached"},{"code":6011,"name":"AmountInMustBeGreaterThanZero","msg":"amount_in must be greater than 0"},{"code":6012,"name":"SlippageTooHigh","msg":"slippage must be less than or equal to 100%"},{"code":6013,"name":"InvalidExpectAmountOut","msg":"invalid expect amount out"},{"code":6014,"name":"WeightsMustSumTo100","msg":"weights must sum to 100%"},{"code":6015,"name":"InvalidShareAmount","msg":"Invalid share amount"},{"code":6016,"name":"InvalidCommissionRate","msg":"Invalid commission rate"},{"code":6017,"name":"InvalidParentCommissionRate","msg":"Invalid parent commission rate"},{"code":6018,"name":"InvalidTrimRate","msg":"Invalid trim rate"},{"code":6019,"name":"InvalidChargeRate","msg":"Invalid charge rate"},{"code":6020,"name":"InvalidCommissionTemporaryTokenAccount","msg":"Invalid commission temporary token account"},{"code":6021,"name":"InvalidAccountsLength","msg":"Invalid accounts length"},{"code":6022,"name":"SwapAuthorityIsNotSigner","msg":"Swap authority is not signer"},{"code":6023,"name":"InvalidAuthorityPda","msg":"Invalid authority pda"},{"code":6024,"name":"InvalidSwapAuthority","msg":"Invalid swap authority"},{"code":6025,"name":"InvalidProgramId","msg":"Invalid program id"},{"code":6026,"name":"InvalidPool","msg":"Invalid pool"},{"code":6027,"name":"InvalidTokenMint","msg":"Invalid token mint"},{"code":6028,"name":"CalculationError","msg":"Calculation error"},{"code":6029,"name":"InvalidSanctumLstStateListData","msg":"Invalid sanctum lst state list data"},{"code":6030,"name":"InvalidSanctumLstStateListIndex","msg":"Invalid sanctum lst state list index"},{"code":6031,"name":"InvalidSanctumSwapAccounts","msg":"Invalid sanctum swap accounts"},{"code":6032,"name":"InvalidSwapAuthorityAccount","msg":"Invalid swap authority account"},{"code":6033,"name":"InvalidBridgeSeed","msg":"Bridge Seed Error"},{"code":6034,"name":"InvalidBundleInput","msg":"Invalid accounts and instruction length"},{"code":6035,"name":"InvalidPlatformFeeRate","msg":"Invalid platform fee rate"},{"code":6036,"name":"AmountOutMustBeGreaterThanZero","msg":"Amount out must be greater than 0"},{"code":6037,"name":"InvalidDampingTerm","msg":"Invalid DampingTerm"},{"code":6038,"name":"InvalidMint","msg":"Invalid mint"},{"code":6039,"name":"InvalidPlatformFeeAccount","msg":"Invalid platform fee account"},{"code":6040,"name":"InvalidTrimAccount","msg":"Invalid trim account"},{"code":6041,"name":"InvalidChargeAccount","msg":"Invalid charge account"},{"code":6042,"name":"InvalidPlatformFeeAmount","msg":"Invalid platform fee amount"},{"code":6043,"name":"InvalidFeeTokenAccount","msg":"Invalid fee token account"},{"code":6044,"name":"InvalidSaAuthority","msg":"Invalid sa authority"},{"code":6045,"name":"InvalidNodeFromAccounts","msg":"Invalid node from accounts"},{"code":6046,"name":"InvalidNodeToAccounts","msg":"Invalid node to accounts"},{"code":6047,"name":"InvalidSourceTokenAccount","msg":"Invalid source token account"},{"code":6048,"name":"InvalidTokenAccount","msg":"Invalid token account"},{"code":6049,"name":"InvalidDestinationTokenAccount","msg":"Invalid destination token account"},{"code":6050,"name":"CommissionAccountIsNone","msg":"Commission account is none"},{"code":6051,"name":"PlatformFeeAccountIsNone","msg":"Platform fee account is none"},{"code":6052,"name":"TrimAccountIsNone","msg":"Trim account is none"},{"code":6053,"name":"ChargeAccountIsNone","msg":"Charge account is none"},{"code":6054,"name":"InvalidFeeAccount","msg":"Invalid fee account"},{"code":6055,"name":"InvalidTokenOwner","msg":"Invalid token owner"},{"code":6056,"name":"SaAuthorityIsNone","msg":"Sa authority is none"},{"code":6057,"name":"SourceTokenSaIsNone","msg":"Source token sa is none"},{"code":6058,"name":"SourceTokenProgramIsNone","msg":"Source token program is none"},{"code":6059,"name":"DestinationTokenSaIsNone","msg":"Destination token sa is none"},{"code":6060,"name":"DestinationTokenProgramIsNone","msg":"Destination token program is none"},{"code":6061,"name":"ResultMustBeGreaterThanZero","msg":"Calculation result must be greater than zero"},{"code":6062,"name":"InvalidAccountData","msg":"Invalid account data"},{"code":6063,"name":"InvalidRfqParameters","msg":"Invalid RFQ parameters"},{"code":6064,"name":"TobAuthorityPdaRequired","msg":"TOB mode requires authority PDA"},{"code":6065,"name":"TobWsolSaRequired","msg":"TOB mode with WSOL fees requires wsol_sa account"},{"code":6066,"name":"InvalidWsolSa","msg":"Invalid WSOL SA account"},{"code":6067,"name":"InvalidCommissionAccount","msg":"Invalid commission account"},{"code":6068,"name":"InvalidActualAmountIn","msg":"Invalid actual amount in"},{"code":6069,"name":"UnexpectedSaTokenAccount","msg":"Unexpected SA token account in CPI"},{"code":6070,"name":"InvalidSourceTokenSaMint","msg":"Invalid source token sa mint"},{"code":6071,"name":"InvalidDestinationTokenSaMint","msg":"Invalid destination token sa mint"},{"code":6072,"name":"InsufficientFunds","msg":"Insufficient funds"},{"code":6073,"name":"SaAuthorityLamportsDecreased","msg":"Sa authority lamports decreased"},{"code":6074,"name":"InvalidTokenProgram","msg":"Invalid token program"},{"code":6075,"name":"InvalidSigner","msg":"Invalid signer"},{"code":6076,"name":"InvalidAssociatedTokenProgram","msg":"Invalid associated token program"},{"code":6077,"name":"TokenProgramIsNone","msg":"Token program is none"},{"code":6078,"name":"AssociatedTokenProgramIsNone","msg":"Associated token program is none"},{"code":6079,"name":"SystemProgramIsNone","msg":"System program is none"},{"code":6080,"name":"InsufficientBalance","msg":"Insufficient balance for transfer"},{"code":6081,"name":"SolReceiverMustBeSystemAccount","msg":"SOL receiver must be a system account"},{"code":6082,"name":"SolReceiverRequiresAccCloseFlag","msg":"SOL receiver requires acc_close_flag to be true"},{"code":6083,"name":"DestinationMustBeWsolForSolReceiver","msg":"Destination must be wSOL when sol_receiver is specified"},{"code":6084,"name":"InvalidGoonfiParameters","msg":"Invalid goonfi parameters"},{"code":6085,"name":"AdapterAbort","msg":"Adapter abort"},{"code":6086,"name":"InconsistentFeeAccountTypes","msg":"Inconsistent fee account types"},{"code":6087,"name":"ReceiverMustBeTokenAccount","msg":"Receiver must be a token account"},{"code":6088,"name":"ReceiverMustBeWsolAccount","msg":"Receiver must be a WSOL token account"},{"code":6089,"name":"InconsistentCommissionAndTrimAccountTypes","msg":"The commission fee account and trim account are different types."},{"code":6090,"name":"NoAvailableNodeIndexForReturnToStart","msg":"No available node index to support returning to START_INDEX"},{"code":6091,"name":"InvalidTokenLedgerTokenAccount","msg":"Invalid token ledger token account"},{"code":6092,"name":"TokenLedgerDeltaCalculationFailure","msg":"Token ledger delta calculation failed"},{"code":6093,"name":"InvalidTokenAccountOwner","msg":"Token account wallet owner mismatch"},{"code":6094,"name":"CashbackFlagMismatch","msg":"Cashback flag mismatch"}],"types":[{"name":"Dex","type":{"kind":"enum","variants":[{"name":"SplTokenSwap"},{"name":"StableSwap"},{"name":"Whirlpool"},{"name":"MeteoraDynamicpool"},{"name":"RaydiumSwap"},{"name":"RaydiumStableSwap"},{"name":"RaydiumClmmSwap"},{"name":"AldrinExchangeV1"},{"name":"AldrinExchangeV2"},{"name":"LifinityV1"},{"name":"LifinityV2"},{"name":"RaydiumClmmSwapV2"},{"name":"FluxBeam"},{"name":"MeteoraDlmm"},{"name":"RaydiumCpmmSwap"},{"name":"OpenBookV2"},{"name":"WhirlpoolV2"},{"name":"Phoenix"},{"name":"ObricV2"},{"name":"SanctumAddLiq"},{"name":"SanctumRemoveLiq"},{"name":"SanctumNonWsolSwap"},{"name":"SanctumWsolSwap"},{"name":"PumpfunBuy","fields":[{"name":"is_cash_back","type":"bool"}]},{"name":"PumpfunSell","fields":[{"name":"is_cash_back","type":"bool"}]},{"name":"StabbleSwap"},{"name":"SanctumRouter"},{"name":"MeteoraVaultDeposit"},{"name":"MeteoraVaultWithdraw"},{"name":"Saros"},{"name":"MeteoraLst"},{"name":"Solfi"},{"name":"QualiaSwap"},{"name":"Zerofi"},{"name":"PumpfunammBuy","fields":[{"name":"is_cash_back","type":"bool"}]},{"name":"PumpfunammSell","fields":[{"name":"is_cash_back","type":"bool"}]},{"name":"Virtuals"},{"name":"VertigoBuy"},{"name":"VertigoSell"},{"name":"PerpetualsAddLiq"},{"name":"PerpetualsRemoveLiq"},{"name":"PerpetualsSwap"},{"name":"RaydiumLaunchpad"},{"name":"LetsBonkFun"},{"name":"Woofi"},{"name":"MeteoraDbc"},{"name":"MeteoraDlmmSwap2"},{"name":"MeteoraDAMMV2"},{"name":"Gavel"},{"name":"BoopfunBuy"},{"name":"BoopfunSell"},{"name":"MeteoraDbc2"},{"name":"GooseFX"},{"name":"Dooar"},{"name":"Numeraire"},{"name":"SaberDecimalWrapperDeposit"},{"name":"SaberDecimalWrapperWithdraw"},{"name":"SarosDlmm"},{"name":"OneDexSwap"},{"name":"Manifest"},{"name":"ByrealClmm"},{"name":"PancakeSwapV3Swap"},{"name":"PancakeSwapV3SwapV2"},{"name":"Tessera"},{"name":"SolRfq","fields":[{"name":"rfq_id","type":"u64"},{"name":"expected_maker_amount","type":"u64"},{"name":"expected_taker_amount","type":"u64"},{"name":"maker_send_amount","type":"u64"},{"name":"taker_send_amount","type":"u64"},{"name":"expiry","type":"u64"},{"name":"maker_use_native_sol","type":"bool"},{"name":"taker_use_native_sol","type":"bool"}]},{"name":"Humidifi"},{"name":"HeavenBuy"},{"name":"HeavenSell"},{"name":"SolfiV2"},{"name":"Goonfi"},{"name":"MoonitBuy"},{"name":"MoonitSell"},{"name":"RaydiumSwapV2"},{"name":"Whalestreet"},{"name":"SugarMoneyBuy","fields":[{"name":"bonding_curve_bump","type":"u8"},{"name":"bonding_curve_sol_associated_account_bump","type":"u8"}]},{"name":"SugarMoneySell","fields":[{"name":"bonding_curve_bump","type":"u8"},{"name":"bonding_curve_sol_associated_account_bump","type":"u8"}]},{"name":"MeteoraDAMMV2Swap2"},{"name":"AlphaQ"},{"name":"FutarchyAmm"},{"name":"PumpfunBuyV2"},{"name":"PumpfunSellV2"},{"name":"HumidifiSwap2","fields":[{"name":"swap_id","type":"u64"}]},{"name":"Scorch","fields":[{"name":"id","type":"u128"}]},{"name":"JupiterLendDeposit"},{"name":"JupiterLendRedeem"},{"name":"TaurusFi"},{"name":"BisonFi"},{"name":"GoonfiV2"},{"name":"Quantum"},{"name":"BoopfunBuy2"},{"name":"BoopfunSell2"},{"name":"ByrealClmm2"},{"name":"Dooar2"},{"name":"HeavenBuy2"},{"name":"HeavenSell2"},{"name":"MoonitBuy2"},{"name":"MoonitSell2"},{"name":"SaberDecimalWrapperDeposit2"},{"name":"SaberDecimalWrapperWithdraw2"},{"name":"ByrealPropAmm"},{"name":"SanctumPrefundSwapViaStake","fields":[{"name":"swap_id","type":"u64"}]},{"name":"AbyssAmm"},{"name":"Aquifer"},{"name":"WhalestreetV2","fields":[{"name":"auth_amount_in","type":"u64"},{"name":"auth","type":"u64"}]},{"name":"SolfiV2WithSig","fields":[{"name":"unix_timestamp","type":"u64"},{"name":"msg_amount_in","type":"u64"},{"name":"expect_amount_out","type":"u64"},{"name":"slippage","type":"u16"},{"name":"user_request_slot","type":"u64"},{"name":"signature","type":{"array":["u8",64]}},{"name":"recovery_id","type":"u8"}]},{"name":"FusionAmm"},{"name":"ScaleAmmBuy"},{"name":"ScaleAmmSell"},{"name":"ScaleVmmBuy"},{"name":"ScaleVmmSell"},{"name":"PumpfunBuy3"},{"name":"PumpfunSell3"},{"name":"PumpfunammBuy2"},{"name":"PumpfunammSell2"},{"name":"Riptide"},{"name":"ZerofiSwapV2"},{"name":"TaurusFiV2"},{"name":"SolRfqV2","fields":[{"name":"taker_side","type":{"defined":{"name":"RfqSide"}}},{"name":"rfq_id","type":"u64"},{"name":"expire_at","type":"i64"},{"name":"levels","type":{"vec":{"defined":{"name":"RfqLevel"}}}}]}]}},{"name":"PlatformFeeWrapUnwrapArgs","type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"amount_in","type":"u64"},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"tob","type":"bool"}]}},{"name":"RfqLevel","type":{"kind":"struct","fields":[{"name":"base_atoms","type":"u64"},{"name":"quote_atoms","type":"u64"}]}},{"name":"RfqSide","repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"Bid"},{"name":"Ask"}]}},{"name":"Route","type":{"kind":"struct","fields":[{"name":"dex","type":{"defined":{"name":"Dex"}}},{"name":"weight","type":"u16"},{"name":"index","type":"u8"}]}},{"name":"SwapArgs","type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"amount_in","type":"u64"},{"name":"expect_amount_out","type":"u64"},{"name":"slippage","type":"u16"},{"name":"routes","type":{"vec":{"defined":{"name":"Route"}}}}]}},{"name":"SwapArgsTokenLedger","docs":["Swap args for token-ledger swaps: no `amount_in` parameter.","`amount_in` is derived on-chain from `token_ledger` delta."],"type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"expect_amount_out","type":"u64"},{"name":"slippage","type":"u16"},{"name":"routes","type":{"vec":{"defined":{"name":"Route"}}}}]}},{"name":"SwapCpiEvent2","type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"source_mint","type":"pubkey"},{"name":"destination_mint","type":"pubkey"},{"name":"source_token_account_owner","type":"pubkey"},{"name":"destination_token_account_owner","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"source_token_change","type":"u64"},{"name":"destination_token_change","type":"u64"}]}},{"name":"SwapEvent","type":{"kind":"struct","fields":[{"name":"dex","type":{"defined":{"name":"Dex"}}},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"}]}},{"name":"SwapTobV2CpiEvent2","type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"source_mint","type":"pubkey"},{"name":"destination_mint","type":"pubkey"},{"name":"source_token_account_owner","type":"pubkey"},{"name":"destination_token_account_owner","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"source_token_change","type":"u64"},{"name":"destination_token_change","type":"u64"},{"name":"commission_direction","type":"bool"},{"name":"total_commission_rate","type":"u32"},{"name":"parent_commission_rate","type":"u32"},{"name":"parent_commission_amount","type":"u64"},{"name":"parent_commission_account","type":"pubkey"},{"name":"child_commission_rate","type":"u32"},{"name":"child_commission_amount","type":"u64"},{"name":"child_commission_account","type":"pubkey"},{"name":"platform_fee_rate","type":"u16"},{"name":"platform_fee_amount","type":"u64"},{"name":"platform_fee_account","type":"pubkey"},{"name":"trim_rate","type":"u8"},{"name":"trim_amount","type":"u64"},{"name":"trim_account","type":"pubkey"}]}},{"name":"SwapTocV2CpiEvent2","type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"source_mint","type":"pubkey"},{"name":"destination_mint","type":"pubkey"},{"name":"source_token_account_owner","type":"pubkey"},{"name":"destination_token_account_owner","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"source_token_change","type":"u64"},{"name":"destination_token_change","type":"u64"},{"name":"commission_direction","type":"bool"},{"name":"total_commission_rate","type":"u32"},{"name":"parent_commission_rate","type":"u32"},{"name":"parent_commission_amount","type":"u64"},{"name":"parent_commission_account","type":"pubkey"},{"name":"child_commission_rate","type":"u32"},{"name":"child_commission_amount","type":"u64"},{"name":"child_commission_account","type":"pubkey"},{"name":"platform_fee_rate","type":"u16"},{"name":"platform_fee_amount","type":"u64"},{"name":"platform_fee_account","type":"pubkey"}]}},{"name":"SwapWithFeesCpiEvent2","type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"source_mint","type":"pubkey"},{"name":"destination_mint","type":"pubkey"},{"name":"source_token_account_owner","type":"pubkey"},{"name":"destination_token_account_owner","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"source_token_change","type":"u64"},{"name":"destination_token_change","type":"u64"},{"name":"commission_direction","type":"bool"},{"name":"commission_rate","type":"u32"},{"name":"commission_amount","type":"u64"},{"name":"commission_account","type":"pubkey"},{"name":"platform_fee_rate","type":"u16"},{"name":"platform_fee_amount","type":"u64"},{"name":"platform_fee_account","type":"pubkey"},{"name":"trim_rate","type":"u8"},{"name":"trim_amount","type":"u64"},{"name":"trim_account","type":"pubkey"}]}},{"name":"SwapWithFeesCpiEventEnhanced2","type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"source_mint","type":"pubkey"},{"name":"destination_mint","type":"pubkey"},{"name":"source_token_account_owner","type":"pubkey"},{"name":"destination_token_account_owner","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"source_token_change","type":"u64"},{"name":"destination_token_change","type":"u64"},{"name":"commission_direction","type":"bool"},{"name":"commission_rate","type":"u32"},{"name":"commission_amount","type":"u64"},{"name":"commission_account","type":"pubkey"},{"name":"platform_fee_rate","type":"u16"},{"name":"platform_fee_amount","type":"u64"},{"name":"platform_fee_account","type":"pubkey"},{"name":"trim_rate","type":"u8"},{"name":"trim_amount","type":"u64"},{"name":"trim_account","type":"pubkey"},{"name":"charge_rate","type":"u16"},{"name":"charge_amount","type":"u64"},{"name":"charge_account","type":"pubkey"}]}},{"name":"TokenLedger","docs":["Token ledger PDA used to snapshot a token account balance within a transaction.","","Seeds: `[b\"token_ledger\", [index]]`, where `index = 0..=255` (u8)."],"type":{"kind":"struct","fields":[{"name":"token_account","type":"pubkey"},{"name":"amount","type":"u64"}]}}],"constants":[{"name":"SEED_SA","type":"bytes","value":"[111, 107, 120, 95, 115, 97]"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/okx_lab_v1.6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma.json b/migration/khadhroony-bot2-reference/idls/okx_lab_v1.6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma.json new file mode 100644 index 0000000..723685a --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/okx_lab_v1.6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma.json @@ -0,0 +1 @@ +{"address":"6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma","metadata":{"name":"dex_solana","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"claim","discriminator":[62,198,214,193,213,159,108,210],"accounts":[{"name":"signer","writable":true,"signer":true,"address":"CjoV5B96reuCfPh2rRK11G1QptG97jZdyZArTn3EN1Mj"},{"name":"receiver","writable":true},{"name":"source_token_account","writable":true,"optional":true},{"name":"destination_token_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"address":"HV1KXxWFaSeriyFvXyx48FqG9BoFbfinB8njCJonqP7K"},{"name":"token_mint","optional":true},{"name":"token_program","optional":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[]},{"name":"claim_cashback_pumpfun","discriminator":[83,1,245,125,201,207,209,249],"accounts":[{"name":"signer","signer":true,"address":"CjoV5B96reuCfPh2rRK11G1QptG97jZdyZArTn3EN1Mj"},{"name":"sa_authority","docs":["PumpFun transfers claimed SOL directly to this account (account[0] in the CPI)."],"writable":true,"address":"HV1KXxWFaSeriyFvXyx48FqG9BoFbfinB8njCJonqP7K"},{"name":"user_volume_accumulator","docs":["Holds native SOL accumulated as cashback; emptied by PumpFun during claim."],"writable":true},{"name":"receiver","docs":["after PumpFun deposits it there."],"writable":true},{"name":"event_authority","address":"Ce6TQqeHC9p8KetsN6JsjHK7UTZk7nasjjnr7XxXp9F1"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"pumpfun_program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"}],"args":[]},{"name":"claim_cashback_pumpswap","discriminator":[253,242,176,173,217,78,100,25],"accounts":[{"name":"signer","signer":true,"address":"CjoV5B96reuCfPh2rRK11G1QptG97jZdyZArTn3EN1Mj"},{"name":"sa_authority","writable":true,"address":"HV1KXxWFaSeriyFvXyx48FqG9BoFbfinB8njCJonqP7K"},{"name":"user_volume_accumulator","writable":true},{"name":"wsol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"spl_token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"uva_wsol_ata","writable":true},{"name":"sa_wsol_ata","docs":["PumpSwap requires account[5] to be a token account owned by authority_pda","(same owner as user_volume_accumulator). WSOL is deposited here by PumpSwap,","then forwarded to receiver_wsol_ata via a second SPL transfer."],"writable":true},{"name":"receiver_wsol_ata","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","address":"GS4CU59F31iL7aR2Q8zVS8DRrcRnXX1yjQ66TqNVQnaR"},{"name":"pumpswap_program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"}],"args":[]},{"name":"create_token_account","discriminator":[147,241,123,100,244,132,174,118],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"owner"},{"name":"token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"token_mint"},{"kind":"account","path":"owner"}]}},{"name":"token_mint","address":"So11111111111111111111111111111111111111112"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bump","type":"u8"}]},{"name":"create_token_account_with_seed","discriminator":[125,191,239,140,66,8,9,228],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"owner"},{"name":"token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"token_mint"},{"kind":"account","path":"owner"},{"kind":"arg","path":"seed"}]}},{"name":"token_mint","address":"So11111111111111111111111111111111111111112"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"bump","type":"u8"},{"name":"seed","type":"u32"}]},{"name":"proxy_swap","discriminator":[19,44,130,148,72,56,44,238],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"sa_authority","optional":true,"pda":{"seeds":[{"kind":"const","value":[111,107,120,95,115,97]}]}},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"}],"args":[{"name":"data","type":{"defined":{"name":"SwapArgs"}}},{"name":"order_id","type":"u64"}]},{"name":"swap","discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"payer","signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"}],"args":[{"name":"data","type":{"defined":{"name":"SwapArgs"}}},{"name":"order_id","type":"u64"}]},{"name":"swap_tob_v3","discriminator":[14,191,44,246,142,225,224,157],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"commission_info","type":"u32"},{"name":"trim_rate","type":"u8"},{"name":"platform_fee_rate","type":"u16"},{"name":"order_id","type":"u64"}]},{"name":"swap_tob_v3_enhanced","discriminator":[236,71,155,68,198,98,14,118],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"commission_info","type":"u32"},{"name":"trim_rate","type":"u8"},{"name":"charge_rate","type":"u16"},{"name":"platform_fee_rate","type":"u16"},{"name":"order_id","type":"u64"}]},{"name":"swap_tob_v3_with_receiver","docs":["Swap ToB with optional specified receiver","- For normal token swaps: sol_receiver should be None","- For swap to SOL with custom receiver: sol_receiver should be Some and acc_close_flag must be true"],"discriminator":[63,114,246,131,51,2,247,29],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"sol_receiver","docs":["Optional SOL receiver account","- None: normal swap or SOL stays with payer","- Some: SOL receiver when converting wSOL -> SOL"],"writable":true,"optional":true}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"commission_info","type":"u32"},{"name":"trim_rate","type":"u8"},{"name":"platform_fee_rate","type":"u16"},{"name":"order_id","type":"u64"}]},{"name":"swap_v3","discriminator":[240,224,38,33,176,31,241,175],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"order_id","type":"u64"}]},{"name":"swap_v3_with_cpi_event","discriminator":[184,104,79,156,107,182,120,138],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"source_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"source_mint"},{"name":"destination_mint"},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"sa_authority","writable":true,"optional":true},{"name":"source_token_sa","writable":true,"optional":true},{"name":"destination_token_sa","writable":true,"optional":true},{"name":"source_token_program","optional":true},{"name":"destination_token_program","optional":true},{"name":"associated_token_program","optional":true,"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","optional":true,"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"args","type":{"defined":{"name":"SwapArgs"}}},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"order_id","type":"u64"}]},{"name":"wrap_unwrap_v3","discriminator":[180,178,191,54,70,8,13,224],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"payer_wsol_account","writable":true},{"name":"wsol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"temp_wsol_account","writable":true,"optional":true,"pda":{"seeds":[{"kind":"const","value":[116,101,109,112,95,119,115,111,108]},{"kind":"account","path":"payer"}]}},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"authority_pda","docs":["Used for signing fee transfers from authority_pda (SOL) or wsol_sa (WSOL)"],"writable":true,"optional":true},{"name":"wsol_sa","docs":["This is the authority_pda's associated token account for WSOL"],"writable":true,"optional":true},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"args","type":{"defined":{"name":"PlatformFeeWrapUnwrapArgs"}}}]},{"name":"wrap_unwrap_v3_with_receiver","docs":["Wrap/Unwrap with optional specified receiver","- Wrap (SOL -> WSOL): receiver is WSOL token account (ATA)","- Unwrap (WSOL -> SOL): receiver is system account (EOA)","Transfer amount:","- From fee: amount_in","- To fee: amount_in - fees"],"discriminator":[70,211,190,165,47,40,213,95],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"payer_wsol_account","writable":true},{"name":"wsol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"temp_wsol_account","writable":true,"optional":true,"pda":{"seeds":[{"kind":"const","value":[116,101,109,112,95,119,115,111,108]},{"kind":"account","path":"payer"}]}},{"name":"commission_account","writable":true,"optional":true},{"name":"platform_fee_account","writable":true,"optional":true},{"name":"authority_pda","docs":["Used for signing fee transfers from authority_pda (SOL) or wsol_sa (WSOL)"],"writable":true,"optional":true},{"name":"wsol_sa","docs":["This is the authority_pda's associated token account for WSOL"],"writable":true,"optional":true},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"receiver","docs":["- Wrap: WSOL token account (ATA) to receive WSOL","- Unwrap: System account (EOA) to receive SOL"],"writable":true}],"args":[{"name":"args","type":{"defined":{"name":"PlatformFeeWrapUnwrapArgs"}}}]}],"events":[{"name":"SwapEvent","discriminator":[64,198,205,232,38,8,113,226]},{"name":"SwapWithFeesCpiEvent","discriminator":[189,97,67,12,37,209,247,29]}],"errors":[{"code":6000,"name":"TooManyHops","msg":"Too many hops"},{"code":6001,"name":"MinReturnNotReached","msg":"Min return not reached"},{"code":6002,"name":"AmountInMustBeGreaterThanZero","msg":"amount_in must be greater than 0"},{"code":6003,"name":"InvalidExpectAmountOut","msg":"invalid expect amount out"},{"code":6004,"name":"AmountsAndRoutesMustHaveTheSameLength","msg":"amounts and routes must have the same length"},{"code":6005,"name":"TotalAmountsMustBeEqualToAmountIn","msg":"total_amounts must be equal to amount_in"},{"code":6006,"name":"DexesAndWeightsMustHaveTheSameLength","msg":"dexes and weights must have the same length"},{"code":6007,"name":"WeightsMustSumTo100","msg":"weights must sum to 100"},{"code":6008,"name":"InvalidSourceTokenAccount","msg":"Invalid source token account"},{"code":6009,"name":"InvalidDestinationTokenAccount","msg":"Invalid destination token account"},{"code":6010,"name":"InvalidTokenAccount","msg":"Invalid token account"},{"code":6011,"name":"InvalidCommissionRate","msg":"Invalid commission rate"},{"code":6012,"name":"InvalidTrimRate","msg":"Invalid trim rate"},{"code":6013,"name":"InvalidChargeRate","msg":"Invalid charge rate"},{"code":6014,"name":"InvalidCommissionTokenAccount","msg":"Invalid commission token account"},{"code":6015,"name":"InvalidCommissionTemporaryTokenAccount","msg":"Invalid commission temporary token account"},{"code":6016,"name":"InvalidAccountsLength","msg":"Invalid accounts length"},{"code":6017,"name":"InvalidHopAccounts","msg":"Invalid hop accounts"},{"code":6018,"name":"InvalidHopFromAccount","msg":"Invalid hop from account"},{"code":6019,"name":"SwapAuthorityIsNotSigner","msg":"Swap authority is not signer"},{"code":6020,"name":"InvalidAuthorityPda","msg":"Invalid authority pda"},{"code":6021,"name":"InvalidSwapAuthority","msg":"Invalid swap authority"},{"code":6022,"name":"InvalidProgramId","msg":"Invalid program id"},{"code":6023,"name":"InvalidPool","msg":"Invalid pool"},{"code":6024,"name":"InvalidTokenMint","msg":"Invalid token mint"},{"code":6025,"name":"CalculationError","msg":"Calculation error"},{"code":6026,"name":"InvalidSanctumLstStateListData","msg":"Invalid sanctum lst state list data"},{"code":6027,"name":"InvalidSanctumLstStateListIndex","msg":"Invalid sanctum lst state list index"},{"code":6028,"name":"InvalidSanctumSwapAccounts","msg":"Invalid sanctum swap accounts"},{"code":6029,"name":"InvalidSwapAuthorityAccounts","msg":"Invalid swap authority account"},{"code":6030,"name":"InvalidBridgeSeed","msg":"Bridge Seed Error"},{"code":6031,"name":"InvalidBundleInput","msg":"Invalid accounts and instruction length"},{"code":6032,"name":"MissingSaAccount","msg":"SA is required"},{"code":6033,"name":"InvalidPlatformFeeRate","msg":"Invalid platform fee rate"},{"code":6034,"name":"AmountOutMustBeGreaterThanZero","msg":"Amount out must be greater than 0"},{"code":6035,"name":"InvalidDampingTerm","msg":"Invalid DampingTerm"},{"code":6036,"name":"InvalidMint","msg":"Invalid mint"},{"code":6037,"name":"InvalidPlatformFeeAmount","msg":"Invalid platform fee amount"},{"code":6038,"name":"InvalidFeeTokenAccount","msg":"Invalid fee token account"},{"code":6039,"name":"InvalidSaAuthority","msg":"Invalid sa authority"},{"code":6040,"name":"CommissionAccountIsNone","msg":"Commission account is none"},{"code":6041,"name":"PlatformFeeAccountIsNone","msg":"Platform fee account is none"},{"code":6042,"name":"TrimAccountIsNone","msg":"Trim account is none"},{"code":6043,"name":"ChargeAccountIsNone","msg":"Charge account is none"},{"code":6044,"name":"InvalidFeeAccount","msg":"Invalid fee account"},{"code":6045,"name":"InvalidSourceTokenSa","msg":"Invalid source token sa"},{"code":6046,"name":"SaAuthorityIsNone","msg":"Sa authority is none"},{"code":6047,"name":"SourceTokenSaIsNone","msg":"Source token sa is none"},{"code":6048,"name":"SourceTokenProgramIsNone","msg":"Source token program is none"},{"code":6049,"name":"DestinationTokenSaIsNone","msg":"Destination token sa is none"},{"code":6050,"name":"DestinationTokenProgramIsNone","msg":"Destination token program is none"},{"code":6051,"name":"ResultMustBeGreaterThanZero","msg":"Calculation result must be greater than zero"},{"code":6052,"name":"InvalidAccountData","msg":"Invalid account data"},{"code":6053,"name":"InvalidRfqParameters","msg":"Invalid RFQ parameters"},{"code":6054,"name":"TobAuthorityPdaRequired","msg":"TOB mode requires authority PDA"},{"code":6055,"name":"TobWsolSaRequired","msg":"TOB mode with WSOL fees requires wsol_sa account"},{"code":6056,"name":"InvalidWsolSa","msg":"Invalid WSOL SA account"},{"code":6057,"name":"InvalidTrimAccount","msg":"Invalid trim account"},{"code":6058,"name":"InvalidChargeAccount","msg":"Invalid charge account"},{"code":6059,"name":"InvalidCommissionAccount","msg":"Invalid commission account"},{"code":6060,"name":"InvalidPlatformFeeAccount","msg":"Invalid platform fee account"},{"code":6061,"name":"InvalidActualAmountIn","msg":"Invalid actual amount in"},{"code":6062,"name":"UnexpectedSaTokenAccount","msg":"Unexpected SA token account in CPI"},{"code":6063,"name":"InvalidSourceTokenSaMint","msg":"Invalid source token sa mint"},{"code":6064,"name":"InvalidDestinationTokenSaMint","msg":"Invalid destination token sa mint"},{"code":6065,"name":"AdapterAbort","msg":"Adapter abort"},{"code":6066,"name":"InsufficientFunds","msg":"Insufficient funds"},{"code":6067,"name":"InvalidDiffLamports","msg":"Invalid diff lamports"},{"code":6068,"name":"InvalidTokenProgram","msg":"Invalid token program"},{"code":6069,"name":"InvalidSigner","msg":"Invalid signer"},{"code":6070,"name":"InvalidAssociatedTokenProgram","msg":"Invalid associated token program"},{"code":6071,"name":"SolReceiverMustBeSystemAccount","msg":"SOL receiver must be a system account"},{"code":6072,"name":"InsufficientBalance","msg":"Insufficient balance for transfer"},{"code":6073,"name":"SolReceiverRequiresAccCloseFlag","msg":"SOL receiver requires acc_close_flag to be true"},{"code":6074,"name":"DestinationMustBeWsolForSolReceiver","msg":"Destination must be wSOL when sol_receiver is specified"},{"code":6075,"name":"InvalidGoonfiParameters","msg":"Invalid Goonfi parameters"},{"code":6076,"name":"InvalidTrimAmount","msg":"Invalid trim amount"},{"code":6077,"name":"ReceiverMustBeTokenAccount","msg":"Receiver must be a token account for wrap operation"},{"code":6078,"name":"ReceiverMustBeWsolAccount","msg":"Receiver must be a WSOL token account for wrap operation"},{"code":6079,"name":"TokenProgramIsNone","msg":"Token program is none"},{"code":6080,"name":"AssociatedTokenProgramIsNone","msg":"Associated token program is none"},{"code":6081,"name":"SystemProgramIsNone","msg":"System program is none"}],"types":[{"name":"Dex","type":{"kind":"enum","variants":[{"name":"SplTokenSwap"},{"name":"StableSwap"},{"name":"Whirlpool"},{"name":"MeteoraDynamicpool"},{"name":"RaydiumSwap"},{"name":"RaydiumStableSwap"},{"name":"RaydiumClmmSwap"},{"name":"AldrinExchangeV1"},{"name":"AldrinExchangeV2"},{"name":"LifinityV1"},{"name":"LifinityV2"},{"name":"RaydiumClmmSwapV2"},{"name":"FluxBeam"},{"name":"MeteoraDlmm"},{"name":"RaydiumCpmmSwap"},{"name":"OpenBookV2"},{"name":"WhirlpoolV2"},{"name":"Phoenix"},{"name":"ObricV2"},{"name":"SanctumAddLiq"},{"name":"SanctumRemoveLiq"},{"name":"SanctumNonWsolSwap"},{"name":"SanctumWsolSwap"},{"name":"PumpfunBuy"},{"name":"PumpfunSell"},{"name":"StabbleSwap"},{"name":"SanctumRouter"},{"name":"MeteoraVaultDeposit"},{"name":"MeteoraVaultWithdraw"},{"name":"Saros"},{"name":"MeteoraLst"},{"name":"Solfi"},{"name":"QualiaSwap"},{"name":"Zerofi"},{"name":"PumpfunammBuy"},{"name":"PumpfunammSell"},{"name":"Virtuals"},{"name":"VertigoBuy"},{"name":"VertigoSell"},{"name":"PerpetualsAddLiq"},{"name":"PerpetualsRemoveLiq"},{"name":"PerpetualsSwap"},{"name":"RaydiumLaunchpad"},{"name":"LetsBonkFun"},{"name":"Woofi"},{"name":"MeteoraDbc"},{"name":"MeteoraDlmmSwap2"},{"name":"MeteoraDAMMV2"},{"name":"Gavel"},{"name":"BoopfunBuy"},{"name":"BoopfunSell"},{"name":"MeteoraDbc2"},{"name":"GooseFX"},{"name":"Dooar"},{"name":"Numeraire"},{"name":"SaberDecimalWrapperDeposit"},{"name":"SaberDecimalWrapperWithdraw"},{"name":"SarosDlmm"},{"name":"OneDexSwap"},{"name":"Manifest"},{"name":"ByrealClmm"},{"name":"PancakeSwapV3Swap"},{"name":"PancakeSwapV3SwapV2"},{"name":"Tessera"},{"name":"SolRfq","fields":[{"name":"rfq_id","type":"u64"},{"name":"expected_maker_amount","type":"u64"},{"name":"expected_taker_amount","type":"u64"},{"name":"maker_send_amount","type":"u64"},{"name":"taker_send_amount","type":"u64"},{"name":"expiry","type":"u64"},{"name":"maker_use_native_sol","type":"bool"},{"name":"taker_use_native_sol","type":"bool"}]},{"name":"PumpfunBuy2"},{"name":"PumpfunammBuy2"},{"name":"Humidifi"},{"name":"HeavenBuy"},{"name":"HeavenSell"},{"name":"SolfiV2"},{"name":"PumpfunBuy3"},{"name":"PumpfunSell3"},{"name":"PumpfunammBuy3"},{"name":"PumpfunammSell3"},{"name":"Goonfi"},{"name":"MoonitBuy"},{"name":"MoonitSell"},{"name":"RaydiumSwapV2"},{"name":"Whalestreet"},{"name":"SugarMoneyBuy","fields":[{"name":"bonding_curve_bump","type":"u8"},{"name":"bonding_curve_sol_associated_account_bump","type":"u8"}]},{"name":"SugarMoneySell","fields":[{"name":"bonding_curve_bump","type":"u8"},{"name":"bonding_curve_sol_associated_account_bump","type":"u8"}]},{"name":"MeteoraDAMMV2Swap2"},{"name":"AlphaQ"},{"name":"FutarchyAmm"},{"name":"PumpfunSell2"},{"name":"HumidifiSwap2","fields":[{"name":"swap_id","type":"u64"}]},{"name":"Scorch","fields":[{"name":"id","type":"u128"}]},{"name":"JupiterLendDeposit"},{"name":"JupiterLendRedeem"},{"name":"TaurusFi"},{"name":"BisonFi"},{"name":"GoonfiV2"},{"name":"Quantum"},{"name":"BoopfunBuy2"},{"name":"BoopfunSell2"},{"name":"ByrealClmm2"},{"name":"Dooar2"},{"name":"HeavenBuy2"},{"name":"HeavenSell2"},{"name":"MoonitBuy2"},{"name":"MoonitSell2"},{"name":"SaberDecimalWrapperDeposit2"},{"name":"SaberDecimalWrapperWithdraw2"},{"name":"ByrealPropAmm"},{"name":"SanctumPrefundSwapViaStake","fields":[{"name":"swap_id","type":"u64"}]},{"name":"AbyssAmm"},{"name":"Aquifer"},{"name":"WhalestreetV2","fields":[{"name":"auth_amount_in","type":"u64"},{"name":"auth","type":"u64"}]},{"name":"SolfiV2WithSig","fields":[{"name":"unix_timestamp","type":"u64"},{"name":"msg_amount_in","type":"u64"},{"name":"expect_amount_out","type":"u64"},{"name":"slippage","type":"u16"},{"name":"user_request_slot","type":"u64"},{"name":"signature","type":{"array":["u8",64]}},{"name":"recovery_id","type":"u8"}]}]}},{"name":"PlatformFeeWrapUnwrapArgs","type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"amount_in","type":"u64"},{"name":"commission_info","type":"u32"},{"name":"platform_fee_rate","type":"u16"},{"name":"tob","type":"bool"}]}},{"name":"Route","type":{"kind":"struct","fields":[{"name":"dexes","type":{"vec":{"defined":{"name":"Dex"}}}},{"name":"weights","type":"bytes"}]}},{"name":"SwapArgs","type":{"kind":"struct","fields":[{"name":"amount_in","type":"u64"},{"name":"expect_amount_out","type":"u64"},{"name":"min_return","type":"u64"},{"name":"amounts","type":{"vec":"u64"}},{"name":"routes","type":{"vec":{"vec":{"defined":{"name":"Route"}}}}}]}},{"name":"SwapEvent","type":{"kind":"struct","fields":[{"name":"dex","type":{"defined":{"name":"Dex"}}},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"}]}},{"name":"SwapWithFeesCpiEvent","type":{"kind":"struct","fields":[{"name":"order_id","type":"u64"},{"name":"source_mint","type":"pubkey"},{"name":"destination_mint","type":"pubkey"},{"name":"source_token_account_owner","type":"pubkey"},{"name":"destination_token_account_owner","type":"pubkey"},{"name":"amount_in","type":"u64"},{"name":"source_token_change","type":"u64"},{"name":"destination_token_change","type":"u64"},{"name":"commission_direction","type":"bool"},{"name":"commission_rate","type":"u32"},{"name":"commission_amount","type":"u64"},{"name":"commission_account","type":"pubkey"},{"name":"platform_fee_rate","type":"u16"},{"name":"platform_fee_amount","type":"u64"},{"name":"platform_fee_account","type":"pubkey"},{"name":"trim_rate","type":"u8"},{"name":"trim_amount","type":"u64"},{"name":"trim_account","type":"pubkey"}]}}],"constants":[{"name":"SEED_SA","type":"bytes","value":"[111, 107, 120, 95, 115, 97]"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/ondo_gm.XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm.json b/migration/khadhroony-bot2-reference/idls/ondo_gm.XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm.json new file mode 100644 index 0000000..c58d355 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/ondo_gm.XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm.json @@ -0,0 +1 @@ +{"address":"XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm","metadata":{"name":"ondo_gm","version":"0.1.0","spec":"0.1.0","description":"Ondo Finance Global Markets"},"instructions":[{"name":"add_to_whitelist","docs":["Add an address to the whitelist","Signer must have the ADMIN_ROLE_WHITELIST role"],"discriminator":[157,211,52,54,144,81,5,55],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account that has the authority to add an address to the whitelist"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_WHITELIST` role","# PDA Seeds","- ADMIN_ROLE_WHITELIST","- The authority's address",""],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,87,104,105,116,101,108,105,115,116]},{"kind":"account","path":"authority"}]}},{"name":"whitelist","docs":["The Whitelist account being created","# PDA Seeds","- WHITELIST_SEED","- Address being whitelisted"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[119,104,105,116,101,108,105,115,116]},{"kind":"arg","path":"address_to_whitelist"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"address_to_whitelist","type":"pubkey"}]},{"name":"batch_close_attestation_accounts","docs":["Batch close attestation accounts","","Accounts to close are passed via remaining_accounts, constraints:","1. Accounts must be marked writable","2. No other accounts should present in `remaining_accounts`","3. Each attestation account must be created by the recipient","4. Each attestation must be older than 30 seconds","Unpermissioned"],"discriminator":[73,167,240,82,80,48,205,207],"accounts":[{"name":"closer","docs":["The user closing the attestation accounts"],"signer":true},{"name":"recipient","docs":["The recipient of the lamports from closed attestation accounts","Must be the creator of each attestation"],"writable":true},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"burn_usdon","docs":["Burn USDon tokens (admin function)","Signer must have the BURNER_ROLE_USDON role"],"discriminator":[173,55,240,198,34,223,191,82],"accounts":[{"name":"authority","docs":["The account with the authority to burn USDon tokens"],"writable":true,"signer":true},{"name":"permanent_delegate","docs":["The permanent delegate PDA (also the mint authority)","# PDA Seeds","- MINT_AUTHORITY_SEED",""],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"usdon_manager_state","docs":["The USDonManagerState account containing USDon configuration","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has either the `BURNER_ROLE_USDON` role","or the `ADMIN_ROLE_USDON` role","# PDA Seeds","- Role seed (from the account's role field)","- The authority's address"],"pda":{"seeds":[{"kind":"account","path":"authority_role_account.role","account":"Roles"},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The USDon mint"],"writable":true},{"name":"token_program","docs":["The token program (Token-2022)"]},{"name":"destination","docs":["The token account to burn tokens from"],"writable":true}],"args":[{"name":"amount","type":"u64"}]},{"name":"close_attestation_account","docs":["Close a single attestation account","","The attestation account must be older than 30 seconds to be closed.","The rent from the closed account is returned to the recipient (original creator).","Unpermissioned"],"discriminator":[201,34,214,89,249,220,97,101],"accounts":[{"name":"closer","docs":["The user closing the attestation account"],"signer":true},{"name":"attestation","docs":["The attestation account to close","# PDA Seeds","- ATTESTATION_ID_SEED","- _attestation_id"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,116,116,101,115,116,97,116,105,111,110,95,105,100]},{"kind":"arg","path":"_attestation_id"}]}},{"name":"recipient","docs":["The recipient of the lamports from the closed attestation account","Must be the creator of the attestation",""],"writable":true},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"_attestation_id","type":{"array":["u8",16]}}]},{"name":"enable_oracle_price","docs":["Enable or disable oracle price for USDon","Signer must have the ADMIN_ROLE_USDON_MANAGER role"],"discriminator":[31,98,115,216,241,185,41,109],"accounts":[{"name":"authority","docs":["The account with the authority to execute the operation"],"signer":true},{"name":"usdon_manager_state","docs":["The USDonManagerState account to be modified","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `ADMIN_ROLE_USDON_MANAGER` role","# PDA Seeds","- ADMIN_ROLE_USDON_MANAGER","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,85,83,68,111,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}}],"args":[{"name":"is_enabled","type":"bool"}]},{"name":"grant_gmtoken_factory_role","docs":["Grants the specified GM Token Factory role to the user","Signer must have the ADMIN_ROLE_GMTOKEN_FACTORY role"],"discriminator":[219,108,46,79,46,104,74,179],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to grant a GM Token Factory role"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_FACTORY` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_FACTORY`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,70,97,99,116,111,114,121]},{"kind":"account","path":"authority"}]}},{"name":"role_to_grant","docs":["The new `Roles` account being created for the user","# PDA Seeds","- Role seed (from RoleType)","- User's address"],"writable":true,"pda":{"seeds":[{"kind":"arg","path":"role"},{"kind":"arg","path":"user"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"user","type":"pubkey"}]},{"name":"grant_gmtoken_manager_role","docs":["Grant a GM Token Manager role","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[245,248,145,72,159,93,154,176],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to grant GM Token Manager roles"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- ADMIN_ROLE_GMTOKEN_MANAGER","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"role_to_grant","docs":["The new `Roles` account being created for the user","# PDA Seeds","- Role seed (from RoleType)","- User's address"],"writable":true,"pda":{"seeds":[{"kind":"arg","path":"role"},{"kind":"arg","path":"user"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"user","type":"pubkey"}]},{"name":"grant_gmtoken_role","docs":["Grants the specified GMToken role to the user","Signer must have the ADMIN_ROLE_GMTOKEN role"],"discriminator":[80,114,198,5,12,2,84,105],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to grant GM Token roles"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN` role","# PDA Seeds","- ADMIN_ROLE_GMTOKEN","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110]},{"kind":"account","path":"authority"}]}},{"name":"role_to_grant","docs":["The new Roles account being created for the user","# PDA Seeds","- Role seed (from RoleType)","- User's address"],"writable":true,"pda":{"seeds":[{"kind":"arg","path":"role"},{"kind":"arg","path":"user"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"user","type":"pubkey"}]},{"name":"grant_role","docs":["Grants the specified role to a user","The signer must be the upgrade authority of the program"],"discriminator":[218,234,128,15,82,33,236,253],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to grant roles, must be the program upgrade authority"],"signer":true},{"name":"role_to_grant","docs":["The Roles account to be initialized","# PDA Seeds","- The role seed (from RoleType)","- The user's address"],"writable":true,"pda":{"seeds":[{"kind":"arg","path":"role"},{"kind":"arg","path":"user"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"},{"name":"program","docs":["The Ondo Global Markets program"],"address":"XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm"},{"name":"program_data","docs":["The ProgramData account of the Ondo Global Markets program"]}],"args":[{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"user","type":"pubkey"}]},{"name":"grant_sanity_configurer_role","docs":["Grant a configurer role for sanity check","Signer must have the ADMIN_ROLE_ONDO_SANITY_CHECK role"],"discriminator":[22,112,48,145,46,77,61,33],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to grant the configurer role"],"signer":true},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `ADMIN_ROLE_ONDO_SANITY_CHECK` role","# PDA Seeds","- `ADMIN_ROLE_ONDO_SANITY_CHECK`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,79,110,100,111,83,97,110,105,116,121,67,104,101,99,107]},{"kind":"account","path":"authority"}]}},{"name":"role_to_grant","docs":["The new `Roles` account being created for the user","# PDA Seeds","- Role seed (from RoleType)","- User's address"],"writable":true,"pda":{"seeds":[{"kind":"arg","path":"role"},{"kind":"arg","path":"user"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"user","type":"pubkey"}]},{"name":"grant_sanity_setter_role","docs":["Grant a setter role for sanity check","Signer must have the ADMIN_ROLE_ONDO_SANITY_CHECK role"],"discriminator":[209,154,110,150,18,113,63,227],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to grant the setter role"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_ONDO_SANITY_CHECK` role","# PDA Seeds","- `RoleType::ADMIN_ROLE_ONDO_SANITY_CHECK`","- `authority` (the authority's address)"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,79,110,100,111,83,97,110,105,116,121,67,104,101,99,107]},{"kind":"account","path":"authority"}]}},{"name":"role_to_grant","docs":["The role account to be initialized for the user","# PDA Seeds","- `role.seed()` (the seed for the setter role)","- `user` (the user's address)"],"writable":true,"pda":{"seeds":[{"kind":"arg","path":"role"},{"kind":"arg","path":"user"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"user","type":"pubkey"}]},{"name":"grant_usdon_role","docs":["Grants the specified USDon role to a user","Signer must have the GUARDIAN_USDON role"],"discriminator":[107,112,6,81,156,182,208,9],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to grant the role"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `GUARDIAN_USDON` role","# PDA Seeds","- GUARDIAN_USDON","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[71,117,97,114,100,105,97,110,85,83,68,111,110]},{"kind":"account","path":"authority"}]}},{"name":"role_to_grant","docs":["The new `Roles` account being created for the user","# PDA Seeds","- Role seed (from RoleType)","- User's address"],"writable":true,"pda":{"seeds":[{"kind":"arg","path":"role"},{"kind":"arg","path":"user"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"user","type":"pubkey"}]},{"name":"init_mint","docs":["Initialize a new GM token mint (without permanent delegate)","Signer must have the DEPLOYER_ROLE_GMTOKEN_FACTORY role"],"discriminator":[126,176,233,16,66,117,209,125],"accounts":[{"name":"payer","docs":["The payer account funding the mint account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to deploy new GM tokens"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the user has `DEPLOYER_ROLE_GMTOKEN_FACTORY`","# PDA Seeds","- `DEPLOYER_ROLE_GMTOKEN_FACTORY`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[68,101,112,108,111,121,101,114,82,111,108,101,71,77,84,111,107,101,110,70,97,99,116,111,114,121]},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The new mint account to be initialized",""],"writable":true,"signer":true},{"name":"mint_authority","docs":["The mint authority PDA that will control the mint","# PDA Seeds","- `MINT_AUTHORITY_SEED`",""],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["The token program (Token-2022)"]},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account containing factory configuration","# PDA Seeds","- GMTOKEN_MANAGER_STATE_SEED"],"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"freeze_authority","type":"pubkey"}]},{"name":"init_mint_delegate","docs":["Initialize a new token mint with permanent delegate (for USDon)","Signer must have the DEPLOYER_ROLE_GMTOKEN_FACTORY role"],"discriminator":[66,23,77,148,164,53,165,83],"accounts":[{"name":"payer","docs":["The payer account funding the mint account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to deploy new tokens"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `DEPLOYER_ROLE_GMTOKEN_FACTORY` role","# PDA Seeds","- `DEPLOYER_ROLE_GMTOKEN_FACTORY`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[68,101,112,108,111,121,101,114,82,111,108,101,71,77,84,111,107,101,110,70,97,99,116,111,114,121]},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The new mint account to be initialized with permanent delegate",""],"writable":true,"signer":true},{"name":"mint_authority","docs":["The mint authority PDA that will control the mint and act as permanent delegate","# PDA Seeds","- `MINT_AUTHORITY_SEED`",""],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["The token program (Token-2022)"]},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account containing factory configuration","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"freeze_authority","type":"pubkey"}]},{"name":"initialize_gmtoken_manager","docs":["Initialize the GM token manager state","","Sets up the manager with pause states for factory, redemptions, and minting,","and configures the secp256k1 attestation signer address.","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[118,57,145,134,233,31,145,47],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to initialize the GM Token Manager"],"signer":true},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be initialized","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"factory_paused","type":"bool"},{"name":"redemptions_paused","type":"bool"},{"name":"minting_paused","type":"bool"},{"name":"attestation_signer_secp","type":{"array":["u8",20]}},{"name":"trading_hours_offset","type":"i64"}]},{"name":"initialize_sanity_check","docs":["Initialize sanity check parameters for a token","","Sets up price deviation and time delay checks to ensure safe trading.","Signer must have the ADMIN_ROLE_ONDO_SANITY_CHECK role"],"discriminator":[99,169,10,74,68,74,22,72],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to initialize the sanity check"],"signer":true},{"name":"sanity_check","docs":["The sanity check account to be initialized","# PDA Seeds","- `ORACLE_SANITY_CHECK_SEED`","- `mint` (the mint address of the GM Token for which the sanity check is being initialized)"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,97,110,105,116,121,95,99,104,101,99,107]},{"kind":"account","path":"mint"}]}},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `ADMIN_ROLE_ONDO_SANITY_CHECK` role","# PDA Seeds","- `RoleType::ADMIN_ROLE_ONDO_SANITY_CHECK`","- `authority` (the authority's address)"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,79,110,100,111,83,97,110,105,116,121,67,104,101,99,107]},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The GM Token mint for which the sanity check is being initialized"]},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"last_price","type":"u64"},{"name":"allowed_deviation_bps","type":"u64"},{"name":"max_time_delay","type":"i64"}]},{"name":"initialize_token_limit","docs":["Initialize token-level rate limits","","Sets rate limits for the token and default limits for users trading this token.","Signer must have the DEPLOYER_ROLE_GMTOKEN_FACTORY role"],"discriminator":[187,218,162,146,4,49,163,240],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to initialize token limit accounts"],"signer":true},{"name":"mint","docs":["The GM Token or USDon mint"]},{"name":"token_limit","docs":["The `TokenLimit` account to be initialized","# PDA Seeds","- `TOKEN_LIMIT_ACCOUNT_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"mint"}]}},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `DEPLOYER_ROLE_GMTOKEN_FACTORY` role","# PDA Seeds","- `DEPLOYER_ROLE_GMTOKEN_FACTORY`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[68,101,112,108,111,121,101,114,82,111,108,101,71,77,84,111,107,101,110,70,97,99,116,111,114,121]},{"kind":"account","path":"authority"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"rate_limit","type":{"option":"u64"}},{"name":"limit_window","type":{"option":"u64"}},{"name":"default_user_rate_limit","type":{"option":"u64"}},{"name":"default_limit_window","type":{"option":"u64"}}]},{"name":"initialize_usdon_manager","docs":["Initialize the USDon manager state","","Sets up the manager with the USDon mint, initial price, oracle configuration,","and vault addresses for USDC and USDon tokens.","Signer must have the GUARDIAN_USDON role"],"discriminator":[9,32,232,105,2,166,13,47],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to initialize the USDon Manager"],"signer":true},{"name":"mint_authority","docs":["The mint authority PDA","# PDA Seeds","- MINT_AUTHORITY_SEED","","Does not need to be checked for correctness as it is uninitialized."],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"usdon_mint","docs":["The USDon mint account","Must be initialized with mint authority as `mint_authority`","and use the token-2022 program"]},{"name":"usdon_vault","docs":["The USDon vault token account","Must be the ATA for `usdon_mint` owned by `usdon_manager_state`"],"pda":{"seeds":[{"kind":"account","path":"usdon_manager_state"},{"kind":"const","value":[6,221,246,225,238,117,143,222,24,66,93,188,228,108,205,218,182,26,252,77,131,185,13,39,254,189,249,40,216,161,139,252]},{"kind":"account","path":"usdon_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"usdc_vault","docs":["The USDC vault token account","Must be the ATA for USDC mint owned by `usdon_manager_state`"],"pda":{"seeds":[{"kind":"account","path":"usdon_manager_state"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"const","value":[198,250,122,243,190,219,173,58,61,101,243,106,171,201,116,49,177,187,228,194,210,246,224,228,124,166,2,3,69,47,93,97]}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"usdon_manager_state","docs":["The USDonManagerState account to be initialized","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `GUARDIAN_USDON` role","# PDA Seeds","- GUARDIAN_USDON","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[71,117,97,114,100,105,97,110,85,83,68,111,110]},{"kind":"account","path":"authority"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"oracle_price_enabled","type":"bool"},{"name":"oracle_price_max_age","type":"u64"},{"name":"usdc_price_update_address","type":"pubkey"}]},{"name":"initialize_user","docs":["Initialize a user account with optional rate limits"],"discriminator":[111,17,185,250,60,122,38,254],"accounts":[{"name":"payer","docs":["Pays for account creation"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to initialize an `OndoUser` account"],"signer":true},{"name":"user","docs":["The user for whom the OndoUser account is being initialized"]},{"name":"mint","docs":["The GM Token mint associated with the OndoUser account"]},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"ondo_user","docs":["The `OndoUser` account to be initialized","# PDA seeds:","- `ONDO_USER_SEED`","- User's address,","- The GM Token's mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[79,110,100,111,85,115,101,114]},{"kind":"account","path":"user"},{"kind":"account","path":"mint"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"rate_limit","type":{"option":"u64"}},{"name":"limit_window","type":{"option":"u64"}}]},{"name":"mint_gm","docs":["Mint GM tokens directly (admin function)","Signer must have the MINTER_ROLE_GMTOKEN role"],"discriminator":[117,223,58,111,44,36,16,43],"accounts":[{"name":"payer","docs":["Pays for destination account if needed"],"writable":true,"signer":true},{"name":"authority","docs":["The account with the authority to mint GM Tokens,"],"signer":true},{"name":"user","docs":["The user receiving the minted tokens"]},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `MINTER_ROLE_GMTOKEN` role","# PDA Seeds","- `MINTER_ROLE_GMTOKEN`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[77,105,110,116,101,114,82,111,108,101,71,77,84,111,107,101,110]},{"kind":"account","path":"authority"}]}},{"name":"oracle_sanity_check","docs":["The `OracleSanityCheck` account validating oracle price updates","# PDA Seeds","- `ORACLE_SANITY_CHECK_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,97,110,105,116,121,95,99,104,101,99,107]},{"kind":"account","path":"mint"}]}},{"name":"mint_authority","docs":["The mint authority PDA","# PDA Seeds","- `MINT_AUTHORITY_SEED`",""],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"mint","docs":["The GM Token mint to mint from"],"writable":true,"relations":["oracle_sanity_check"]},{"name":"destination","docs":["The destination token account to mint tokens to"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"usdon_manager_state","docs":["The `USDonManagerState` account for validation","# PDA Seeds","- `USDON_MANAGER_STATE_SEED`"],"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"token_program","docs":["The token program (Token-2022)"]},{"name":"associated_token_program","docs":["The associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"amount","type":"u64"}]},{"name":"mint_usdon","docs":["Mint USDon tokens (admin function)","Signer must have the MINTER_ROLE_USDON role"],"discriminator":[178,178,234,133,225,144,48,129],"accounts":[{"name":"authority","docs":["The account with the authority to mint USDon tokens"],"writable":true,"signer":true},{"name":"mint_authority","docs":["The mint authority PDA","# PDA Seeds","- MINT_AUTHORITY_SEED",""],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"usdon_manager_state","docs":["The USDonManagerState account containing USDon configuration","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"authority_role_account","docs":["The Roles account verifying the authority has either the `MINTER_ROLE_USDON`","or `ADMIN_ROLE_USDON` role","# PDA Seeds","- Role seed (from the account's role field)","- The authority's address"],"pda":{"seeds":[{"kind":"account","path":"authority_role_account.role","account":"Roles"},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The USDon mint"],"writable":true},{"name":"token_program","docs":["The token program (Token-2022)"]},{"name":"destination","docs":["The destination token account to mint tokens to"],"writable":true}],"args":[{"name":"amount","type":"u64"}]},{"name":"mint_with_usdc","docs":["Mint GM tokens by paying with USDC","","Requires a valid attestation with price, amount, and expiration."],"discriminator":[128,28,133,173,71,142,185,206],"accounts":[{"name":"user","docs":["The user performing the USDC swap, pays for account creation if needed"],"writable":true,"signer":true},{"name":"mint","docs":["The GM Token mint involved in the swap"],"writable":true},{"name":"mint_authority","docs":["The mint authority PDA","# PDA Seeds","- MINT_AUTHORITY_SEED","Does not need to be checked for correctness as it is uninitialized."],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"ondo_user","docs":["The OndoUser account tracking user-specific state for this mint","# PDA Seeds","- ONDO_USER_SEED","- User's address","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[79,110,100,111,85,115,101,114]},{"kind":"account","path":"user"},{"kind":"account","path":"mint"}]}},{"name":"token_limit_account","docs":["The TokenLimit account enforcing mint/burn limits for the GM Token","# PDA Seeds","- TOKEN_LIMIT_ACCOUNT_SEED","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"mint"}]}},{"name":"sanity_check_account","docs":["The OracleSanityCheck account validating oracle price updates","# PDA Seeds","- ORACLE_SANITY_CHECK_SEED","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,97,110,105,116,121,95,99,104,101,99,107]},{"kind":"account","path":"mint"}]}},{"name":"user_token_account","docs":["The user's associated token account for the GM Token"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"attestation_id_account","docs":["The attestation ID account preventing attestation reuse","# PDA Seeds","- ATTESTATION_ID_SEED","- Attestation ID (16-byte array)","Existence means the attestation has been used."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,116,116,101,115,116,97,116,105,111,110,95,105,100]},{"kind":"arg","path":"attestation_id"}]}},{"name":"whitelist","docs":["The Whitelist account verifying the user is authorized","# PDA Seeds","- WHITELIST_SEED","- User's address","Validated in instruction handler - returns UserNotWhitelisted if not initialized."],"pda":{"seeds":[{"kind":"const","value":[119,104,105,116,101,108,105,115,116]},{"kind":"account","path":"user"}]}},{"name":"token_program","docs":["The token program (Token-2022)"]},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["The associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"spl_token_program","docs":["The legacy SPL Token program","Required when performing swaps between USDC and USDon tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"usdc_price_update","docs":["The oracle price update account for USDC/USD price feed","Provides real-time price data for USDC token","Used in swap operations to calculate exchange rates","Must be recent (within oracle_price_max_age) to ensure price accuracy","`has_one` ensures that this account matches the expected oracle account stored in the `usdon_manager_state`."],"relations":["usdon_manager_state"]},{"name":"usdc_vault","docs":["The USDC vault storing USDC tokens received from users during swaps"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"usdon_manager_state"},{"kind":"account","path":"spl_token_program"},{"kind":"account","path":"usdc_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"usdon_vault","docs":["The USDon vault storing USDon tokens received from users during swaps"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"usdon_manager_state"},{"kind":"account","path":"token_program"},{"kind":"account","path":"usdon_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"usdc_mint"},{"name":"user_usdc_token_account","docs":["The user's USDC token account"],"writable":true},{"name":"usdon_mint","docs":["The USDon mint (Token-2022)"],"writable":true},{"name":"user_usdon_token_account","docs":["The user's USDon token account"],"writable":true},{"name":"usdon_manager_state","docs":["The USDonManagerState account used as authority for vault operations","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"gmtoken_manager_state","docs":["The GmTokenManagerState account managing GM Token operations","- Stores protocol parameters like factory, redemption, and minting paused.","# PDA Seeds","- GMTOKEN_MANAGER_STATE_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}},{"name":"instructions","address":"Sysvar1nstructions1111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"attestation_id","type":{"array":["u8",16]}},{"name":"price","type":"u64"},{"name":"amount","type":"u64"},{"name":"expiration","type":"i64"}]},{"name":"mint_with_usdon","docs":["Mint GM tokens by paying with USDon","","Requires a valid attestation with price, amount, and expiration."],"discriminator":[25,116,92,45,43,188,95,58],"accounts":[{"name":"user","docs":["The user performing the USDon swap, pays for account creation if needed"],"writable":true,"signer":true},{"name":"mint","docs":["The GM Token mint involved in the swap"],"writable":true},{"name":"mint_authority","docs":["The mint authority PDA","# PDA Seeds","- MINT_AUTHORITY_SEED","Does not need to be checked for correctness as it is uninitialized."],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"ondo_user","docs":["The OndoUser account tracking user-specific state for this mint","# PDA Seeds","- ONDO_USER_SEED","- User's address","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[79,110,100,111,85,115,101,114]},{"kind":"account","path":"user"},{"kind":"account","path":"mint"}]}},{"name":"token_limit_account","docs":["The TokenLimit account enforcing mint/burn limits for the GM Token","# PDA Seeds","- TOKEN_LIMIT_ACCOUNT_SEED","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"mint"}]}},{"name":"sanity_check_account","docs":["The OracleSanityCheck account validating oracle price updates","# PDA Seeds","- ORACLE_SANITY_CHECK_SEED","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,97,110,105,116,121,95,99,104,101,99,107]},{"kind":"account","path":"mint"}]}},{"name":"user_token_account","docs":["The user's associated token account for the GM Token"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"attestation_id_account","docs":["The attestation ID account preventing attestation reuse","# PDA Seeds","- ATTESTATION_ID_SEED","- Attestation ID (16-byte array)","Existence means the attestation has been used."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,116,116,101,115,116,97,116,105,111,110,95,105,100]},{"kind":"arg","path":"attestation_id"}]}},{"name":"whitelist","docs":["The Whitelist account verifying the user is authorized","# PDA Seeds","- WHITELIST_SEED","- User's address","Validated in instruction handler - returns UserNotWhitelisted if not initialized."],"pda":{"seeds":[{"kind":"const","value":[119,104,105,116,101,108,105,115,116]},{"kind":"account","path":"user"}]}},{"name":"token_program","docs":["The token program (Token-2022)"]},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["The associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"usdon_vault","docs":["The USDon vault storing USDon tokens received from users during swaps"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"usdon_manager_state"},{"kind":"account","path":"token_program"},{"kind":"account","path":"usdon_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"usdon_mint","docs":["The USDon mint (Token-2022)"],"writable":true},{"name":"user_usdon_token_account","docs":["The user's USDon token account"],"writable":true},{"name":"usdon_manager_state","docs":["The USDonManagerState account used as authority for vault operations","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"gmtoken_manager_state","docs":["The GmTokenManagerState account managing GM Token operations","- Stores protocol parameters like factory, redemption, and minting paused.","# PDA Seeds","- GMTOKEN_MANAGER_STATE_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}},{"name":"instructions","address":"Sysvar1nstructions1111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"attestation_id","type":{"array":["u8",16]}},{"name":"price","type":"u64"},{"name":"amount","type":"u64"},{"name":"expiration","type":"i64"}]},{"name":"pause_global_minting","docs":["Pause all mints globally","Signer must have the PAUSER_ROLE_GMTOKEN_MANAGER role"],"discriminator":[191,70,149,85,20,141,242,239],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `PAUSER_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `PAUSER_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[80,97,117,115,101,114,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[]},{"name":"pause_global_minting_admin","docs":["Pause minting globally (admin function)","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[20,43,34,201,214,148,151,75],"accounts":[{"name":"authority","docs":["The account with the authority to execute the unpause/configuration operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[]},{"name":"pause_global_redemption","docs":["Pause all redemption globally","Signer must have the PAUSER_ROLE_GMTOKEN_MANAGER role"],"discriminator":[169,5,1,19,183,71,133,147],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `PAUSER_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `PAUSER_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[80,97,117,115,101,114,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[]},{"name":"pause_global_redemption_admin","docs":["Pause redemption globally (admin function)","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[77,80,68,48,7,94,111,183],"accounts":[{"name":"authority","docs":["The account with the authority to execute the unpause/configuration operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[]},{"name":"pause_token","docs":["Pause all transfers for a GM token by invoking the pausable token extension","Signer must have the PAUSER_ROLE_GMTOKEN role"],"discriminator":[226,150,72,211,159,51,226,39],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause operation"],"writable":true,"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `PAUSER_ROLE_GMTOKEN` role","# PDA Seeds","- `PAUSER_ROLE_GMTOKEN`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[80,97,117,115,101,114,82,111,108,101,71,77,84,111,107,101,110]},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The GM Token mint to pause"],"writable":true},{"name":"mint_authority","docs":["The mint authority PDA that has pausable authority","# PDA Seeds","- MINT_AUTHORITY_SEED",""],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"token_program","docs":["The token program (Token-2022)"]}],"args":[]},{"name":"pause_token_factory","docs":["Pause the GM token factory","Signer must have the PAUSER_ROLE_GMTOKEN_FACTORY role"],"discriminator":[79,227,221,11,182,89,193,150],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `PAUSER_ROLE_GMTOKEN_FACTORY` role","# PDA Seeds","- `PAUSER_ROLE_GMTOKEN_FACTORY`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[80,97,117,115,101,114,82,111,108,101,71,77,84,111,107,101,110,70,97,99,116,111,114,121]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[]},{"name":"pause_token_factory_admin","docs":["Pause the GM token factory (admin version)","Signer must have the ADMIN_ROLE_GMTOKEN_FACTORY role"],"discriminator":[244,155,26,17,39,152,51,18],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause/unpause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has `ADMIN_ROLE_GMTOKEN_FACTORY` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_FACTORY`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,70,97,99,116,111,114,121]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[]},{"name":"pause_token_minting","docs":["Pause minting for a specific token","Signer must have the PAUSER_ROLE_GMTOKEN_MANAGER role"],"discriminator":[105,219,64,183,163,148,17,126],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `PAUSER_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `PAUSER_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[80,97,117,115,101,114,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"token_limit_account","docs":["The `TokenLimit` account for the specific GM Token to pause","# PDA Seeds","- `TOKEN_LIMIT_ACCOUNT_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"token_limit_account.mint","account":"TokenLimit"}]}}],"args":[]},{"name":"pause_token_minting_admin","docs":["Pause mints for a specific token (admin function)","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[8,3,163,16,201,93,94,145],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause/unpause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"token_limit_account","docs":["The `TokenLimit` account for the specific GM Token to pause/unpause","# PDA Seeds","- `TOKEN_LIMIT_ACCOUNT_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"token_limit_account.mint","account":"TokenLimit"}]}}],"args":[]},{"name":"pause_token_redemption","docs":["Pause redemptions for a specific token","Signer must have the PAUSER_ROLE_GMTOKEN_MANAGER role"],"discriminator":[232,134,32,8,70,234,54,216],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `PAUSER_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `PAUSER_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[80,97,117,115,101,114,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"token_limit_account","docs":["The `TokenLimit` account for the specific GM Token to pause","# PDA Seeds","- `TOKEN_LIMIT_ACCOUNT_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"token_limit_account.mint","account":"TokenLimit"}]}}],"args":[]},{"name":"pause_token_redemption_admin","docs":["Pause redemptions for a specific token (admin function)","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[62,162,192,223,22,143,110,191],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause/unpause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"token_limit_account","docs":["The `TokenLimit` account for the specific GM Token to pause/unpause","# PDA Seeds","- `TOKEN_LIMIT_ACCOUNT_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"token_limit_account.mint","account":"TokenLimit"}]}}],"args":[]},{"name":"redeem_for_usdc","docs":["Redeem GM tokens for USDC","","Requires a valid attestation with price, amount, and expiration."],"discriminator":[150,9,215,220,255,157,74,78],"accounts":[{"name":"user","docs":["The user performing the USDC swap, pays for account creation if needed"],"writable":true,"signer":true},{"name":"mint","docs":["The GM Token mint involved in the swap"],"writable":true},{"name":"mint_authority","docs":["The mint authority PDA","# PDA Seeds","- MINT_AUTHORITY_SEED","Does not need to be checked for correctness as it is uninitialized."],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"ondo_user","docs":["The OndoUser account tracking user-specific state for this mint","# PDA Seeds","- ONDO_USER_SEED","- User's address","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[79,110,100,111,85,115,101,114]},{"kind":"account","path":"user"},{"kind":"account","path":"mint"}]}},{"name":"token_limit_account","docs":["The TokenLimit account enforcing mint/burn limits for the GM Token","# PDA Seeds","- TOKEN_LIMIT_ACCOUNT_SEED","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"mint"}]}},{"name":"sanity_check_account","docs":["The OracleSanityCheck account validating oracle price updates","# PDA Seeds","- ORACLE_SANITY_CHECK_SEED","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,97,110,105,116,121,95,99,104,101,99,107]},{"kind":"account","path":"mint"}]}},{"name":"user_token_account","docs":["The user's associated token account for the GM Token"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"attestation_id_account","docs":["The attestation ID account preventing attestation reuse","# PDA Seeds","- ATTESTATION_ID_SEED","- Attestation ID (16-byte array)","Existence means the attestation has been used."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,116,116,101,115,116,97,116,105,111,110,95,105,100]},{"kind":"arg","path":"attestation_id"}]}},{"name":"whitelist","docs":["The Whitelist account verifying the user is authorized","# PDA Seeds","- WHITELIST_SEED","- User's address","Validated in instruction handler - returns UserNotWhitelisted if not initialized."],"pda":{"seeds":[{"kind":"const","value":[119,104,105,116,101,108,105,115,116]},{"kind":"account","path":"user"}]}},{"name":"token_program","docs":["The token program (Token-2022)"]},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["The associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"spl_token_program","docs":["The legacy SPL Token program","Required when performing swaps between USDC and USDon tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"usdc_price_update","docs":["The oracle price update account for USDC/USD price feed","Provides real-time price data for USDC token","Used in swap operations to calculate exchange rates","Must be recent (within oracle_price_max_age) to ensure price accuracy","`has_one` ensures that this account matches the expected oracle account stored in the `usdon_manager_state`."],"relations":["usdon_manager_state"]},{"name":"usdc_vault","docs":["The USDC vault storing USDC tokens received from users during swaps"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"usdon_manager_state"},{"kind":"account","path":"spl_token_program"},{"kind":"account","path":"usdc_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"usdon_vault","docs":["The USDon vault storing USDon tokens received from users during swaps"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"usdon_manager_state"},{"kind":"account","path":"token_program"},{"kind":"account","path":"usdon_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"usdc_mint"},{"name":"user_usdc_token_account","docs":["The user's USDC token account"],"writable":true},{"name":"usdon_mint","docs":["The USDon mint (Token-2022)"],"writable":true},{"name":"user_usdon_token_account","docs":["The user's USDon token account"],"writable":true},{"name":"usdon_manager_state","docs":["The USDonManagerState account used as authority for vault operations","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"gmtoken_manager_state","docs":["The GmTokenManagerState account managing GM Token operations","- Stores protocol parameters like factory, redemption, and minting paused.","# PDA Seeds","- GMTOKEN_MANAGER_STATE_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}},{"name":"instructions","address":"Sysvar1nstructions1111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"attestation_id","type":{"array":["u8",16]}},{"name":"price","type":"u64"},{"name":"amount","type":"u64"},{"name":"expiration","type":"i64"}]},{"name":"redeem_for_usdon","docs":["Redeem GM tokens for USDon","","Requires a valid attestation with price, amount, and expiration."],"discriminator":[231,121,93,33,143,252,82,13],"accounts":[{"name":"user","docs":["The user performing the USDon swap, pays for account creation if needed"],"writable":true,"signer":true},{"name":"mint","docs":["The GM Token mint involved in the swap"],"writable":true},{"name":"mint_authority","docs":["The mint authority PDA","# PDA Seeds","- MINT_AUTHORITY_SEED","Does not need to be checked for correctness as it is uninitialized."],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"ondo_user","docs":["The OndoUser account tracking user-specific state for this mint","# PDA Seeds","- ONDO_USER_SEED","- User's address","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[79,110,100,111,85,115,101,114]},{"kind":"account","path":"user"},{"kind":"account","path":"mint"}]}},{"name":"token_limit_account","docs":["The TokenLimit account enforcing mint/burn limits for the GM Token","# PDA Seeds","- TOKEN_LIMIT_ACCOUNT_SEED","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"mint"}]}},{"name":"sanity_check_account","docs":["The OracleSanityCheck account validating oracle price updates","# PDA Seeds","- ORACLE_SANITY_CHECK_SEED","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,97,110,105,116,121,95,99,104,101,99,107]},{"kind":"account","path":"mint"}]}},{"name":"user_token_account","docs":["The user's associated token account for the GM Token"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"attestation_id_account","docs":["The attestation ID account preventing attestation reuse","# PDA Seeds","- ATTESTATION_ID_SEED","- Attestation ID (16-byte array)","Existence means the attestation has been used."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,116,116,101,115,116,97,116,105,111,110,95,105,100]},{"kind":"arg","path":"attestation_id"}]}},{"name":"whitelist","docs":["The Whitelist account verifying the user is authorized","# PDA Seeds","- WHITELIST_SEED","- User's address","Validated in instruction handler - returns UserNotWhitelisted if not initialized."],"pda":{"seeds":[{"kind":"const","value":[119,104,105,116,101,108,105,115,116]},{"kind":"account","path":"user"}]}},{"name":"token_program","docs":["The token program (Token-2022)"]},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["The associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"usdon_vault","docs":["The USDon vault storing USDon tokens received from users during swaps"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"usdon_manager_state"},{"kind":"account","path":"token_program"},{"kind":"account","path":"usdon_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"usdon_mint","docs":["The USDon mint (Token-2022)"],"writable":true},{"name":"user_usdon_token_account","docs":["The user's USDon token account"],"writable":true},{"name":"usdon_manager_state","docs":["The USDonManagerState account used as authority for vault operations","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"gmtoken_manager_state","docs":["The GmTokenManagerState account managing GM Token operations","- Stores protocol parameters like factory, redemption, and minting paused.","# PDA Seeds","- GMTOKEN_MANAGER_STATE_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}},{"name":"instructions","address":"Sysvar1nstructions1111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"attestation_id","type":{"array":["u8",16]}},{"name":"price","type":"u64"},{"name":"amount","type":"u64"},{"name":"expiration","type":"i64"}]},{"name":"remove_from_whitelist","docs":["Remove an address from the whitelist","Signer must have the ADMIN_ROLE_WHITELIST role"],"discriminator":[7,144,216,239,243,236,193,235],"accounts":[{"name":"authority","docs":["The account with the authority to remove an address from the whitelist"],"signer":true},{"name":"recipient","docs":["Receives the lamports from closing the Whitelist account"],"writable":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_WHITELIST` role","# PDA Seeds","- `ADMIN_ROLE_WHITELIST`","- The authority's address",""],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,87,104,105,116,101,108,105,115,116]},{"kind":"account","path":"authority"}]}},{"name":"whitelist","docs":["The Whitelist account being closed","# PDA Seeds","- `WHITELIST_SEED`","- Address being removed from whitelist"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[119,104,105,116,101,108,105,115,116]},{"kind":"arg","path":"address_to_remove"}]}}],"args":[{"name":"address_to_remove","type":"pubkey"}]},{"name":"resume_global_minting","docs":["Resume all mints globally","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[18,40,173,140,250,6,10,207],"accounts":[{"name":"authority","docs":["The account with the authority to execute the unpause/configuration operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[]},{"name":"resume_global_redemption","docs":["Resume all redemption globally","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[80,47,24,168,44,185,215,97],"accounts":[{"name":"authority","docs":["The account with the authority to execute the unpause/configuration operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[]},{"name":"resume_token","docs":["Resume all transfers for a GM token by invoking the pausable token extension to remove a pause.","Signer must have the UNPAUSER_ROLE_GMTOKEN role"],"discriminator":[173,116,46,190,81,191,249,83],"accounts":[{"name":"authority","docs":["The account with the authority to execute the resume operation"],"writable":true,"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `UNPAUSER_ROLE_GMTOKEN` role","# PDA Seeds","- `UNPAUSER_ROLE_GMTOKEN`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[85,110,112,97,117,115,101,114,82,111,108,101,71,77,84,111,107,101,110]},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The GM Token mint to resume"],"writable":true},{"name":"mint_authority","docs":["The mint authority PDA that has pausable authority","# PDA Seeds","- `MINT_AUTHORITY_SEED`",""],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"token_program","docs":["The token program (Token-2022)"]}],"args":[]},{"name":"resume_token_factory","docs":["Resume the GM token factory","Signer must have the ADMIN_ROLE_GMTOKEN_FACTORY role"],"discriminator":[235,37,239,145,244,212,31,37],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause/unpause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has `ADMIN_ROLE_GMTOKEN_FACTORY` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_FACTORY`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,70,97,99,116,111,114,121]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[]},{"name":"resume_token_minting","docs":["Resume mints for a token (admin function)","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[188,102,226,249,194,29,81,248],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause/unpause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"token_limit_account","docs":["The `TokenLimit` account for the specific GM Token to pause/unpause","# PDA Seeds","- `TOKEN_LIMIT_ACCOUNT_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"token_limit_account.mint","account":"TokenLimit"}]}}],"args":[]},{"name":"resume_token_redemption","docs":["Resume redemptions for a token (admin function)","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[152,249,213,178,85,141,122,45],"accounts":[{"name":"authority","docs":["The account with the authority to execute the pause/unpause operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"token_limit_account","docs":["The `TokenLimit` account for the specific GM Token to pause/unpause","# PDA Seeds","- `TOKEN_LIMIT_ACCOUNT_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"token_limit_account.mint","account":"TokenLimit"}]}}],"args":[]},{"name":"retrieve_tokens","docs":["Retrieve (withdraw) tokens from a vault controlled by the USDon manager","","Allows admins to withdraw any tokens (USDC, USDon, etc.) from vaults","owned by the usdon_manager_state PDA.","Signer must have the ADMIN_ROLE_USDON_MANAGER role"],"discriminator":[208,194,68,55,183,22,93,135],"accounts":[{"name":"authority","docs":["The account with the authority to execute the retrieval operation"],"signer":true},{"name":"usdon_manager_state","docs":["The USDonManagerState account used as authority for vault operations","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `ADMIN_ROLE_USDON_MANAGER` role","# PDA Seeds","- ADMIN_ROLE_USDON_MANAGER","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,85,83,68,111,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"token_mint","docs":["The mint of the token being retrieved"]},{"name":"source_vault","docs":["The source vault token account (must be owned by usdon_manager_state)"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"usdon_manager_state"},{"kind":"account","path":"token_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"destination","docs":["The destination token account to receive the tokens"],"writable":true},{"name":"token_program","docs":["The token program (SPL Token or Token-2022)"]}],"args":[{"name":"amount","type":"u64"}]},{"name":"revoke_gmtoken_factory_role","docs":["Revokes the specified GM Token Factory role from the user","Signer must have the ADMIN_ROLE_GMTOKEN_FACTORY role"],"discriminator":[214,48,33,96,99,36,94,198],"accounts":[{"name":"authority","docs":["The account with the authority to revoke a GM Token Factory role"],"signer":true},{"name":"recipient","docs":["Receives the lamports from closing the account"],"writable":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_FACTORY` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_FACTORY`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,70,97,99,116,111,114,121]},{"kind":"account","path":"authority"}]}},{"name":"role_to_revoke","docs":["The Roles account being closed","# PDA Seeds","- `role_to_revoke.role.seed()` (the seed for the role)","- `role_to_revoke.address` (the user's address)"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"role_to_revoke.role","account":"Roles"},{"kind":"account","path":"role_to_revoke.address","account":"Roles"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"revoke_gmtoken_manager_role","docs":["Revoke a role from the GM token manager","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[202,63,132,81,219,242,100,90],"accounts":[{"name":"authority","docs":["The account with the authority to revoke GM Token Manager roles"],"signer":true},{"name":"recipient","docs":["Receives the lamports from closing the Roles account"],"writable":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"role_to_revoke","docs":["The Roles account being closed","# PDA Seeds","- `role_to_revoke.role.seed()` (the seed for the role)","- `role_to_revoke.address` (the user's address)"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"role_to_revoke.role","account":"Roles"},{"kind":"account","path":"role_to_revoke.address","account":"Roles"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"revoke_gmtoken_role","docs":["Revokes the specified GMToken role from the user","Signer must have the ADMIN_ROLE_GMTOKEN role"],"discriminator":[108,55,0,39,40,39,103,144],"accounts":[{"name":"recipient","docs":["The recipient of the closed account lamports"],"writable":true},{"name":"authority","docs":["The account with the authority to revoke GM Token roles"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN` role","# PDA Seeds","- ADMIN_ROLE_GMTOKEN","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110]},{"kind":"account","path":"authority"}]}},{"name":"role_to_revoke","docs":["The Roles account being closed","# PDA Seeds","- `role_to_revoke.role.seed()` (the seed for the role)","- `role_to_revoke.address` (the user's address)"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"role_to_revoke.role","account":"Roles"},{"kind":"account","path":"role_to_revoke.address","account":"Roles"}]}}],"args":[]},{"name":"revoke_role","docs":["Revoke a role by closing the Roles account and reclaim rent","Signer must be the upgrade authority of the program"],"discriminator":[179,232,2,180,48,227,82,7],"accounts":[{"name":"recipient","docs":["Receives funds from account closure"],"writable":true},{"name":"authority","docs":["The account with the authority to revoke roles, must be the program upgrade authority"],"signer":true},{"name":"role_to_revoke","docs":["The Roles account to be closed","# PDA Seeds","- `role_to_revoke.role.seed()` (the seed for the role)","- `role_to_revoke.address` (the user's address)"],"writable":true,"pda":{"seeds":[{"kind":"arg","path":"_role"},{"kind":"account","path":"role_to_revoke.address","account":"Roles"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"},{"name":"program","docs":["The Ondo Global Markets program"],"address":"XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm"},{"name":"program_data","docs":["The ProgramData account of the Ondo Global Markets program"]}],"args":[{"name":"_role","type":{"defined":{"name":"RoleType"}}}]},{"name":"revoke_sanity_configurer_role","docs":["Revoke a configurer role for sanity check","Signer must have the ADMIN_ROLE_ONDO_SANITY_CHECK role"],"discriminator":[170,122,0,190,220,165,67,86],"accounts":[{"name":"recipient","docs":["Receives the lamports from closing the Roles account"],"writable":true},{"name":"authority","docs":["The account with the authority to revoke the configurer role"],"signer":true},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `ADMIN_ROLE_ONDO_SANITY_CHECK` role","# PDA Seeds","- `ADMIN_ROLE_ONDO_SANITY_CHECK`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,79,110,100,111,83,97,110,105,116,121,67,104,101,99,107]},{"kind":"account","path":"authority"}]}},{"name":"role_to_revoke","docs":["The Roles account being closed","# PDA Seeds","- `role_to_revoke.role.seed()` (the seed for the configurer role)","- `role_to_revoke.address` (the user's address)"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"role_to_revoke.role","account":"Roles"},{"kind":"account","path":"role_to_revoke.address","account":"Roles"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"revoke_sanity_setter_role","docs":["Revoke a setter role for sanity check","Signer must have the ADMIN_ROLE_ONDO_SANITY_CHECK role"],"discriminator":[224,165,35,213,84,53,9,229],"accounts":[{"name":"recipient","docs":["Receives the lamports from closing the Roles account"],"writable":true},{"name":"authority","docs":["The account that has the authority to revoke the setter role"],"signer":true},{"name":"authority_role_account","docs":["The role account of the admin","# PDA Seeds","- `RoleType::ADMIN_ROLE_ONDO_SANITY_CHECK`","- `authority` (the authority's address)"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,79,110,100,111,83,97,110,105,116,121,67,104,101,99,107]},{"kind":"account","path":"authority"}]}},{"name":"role_to_revoke","docs":["The role account to be closed","# PDA Seeds","- `role_to_revoke.role.seed()` (the seed for the setter role)","- `role_to_revoke.address` (the user's address)"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"role_to_revoke.role","account":"Roles"},{"kind":"account","path":"role_to_revoke.address","account":"Roles"}]}},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"revoke_usdon_role","docs":["Revokes the specified USDon role from a user","Signer must have the GUARDIAN_USDON role"],"discriminator":[231,35,217,203,112,165,182,69],"accounts":[{"name":"authority","docs":["The account with the authority to revoke a role"],"signer":true},{"name":"recipient","docs":["Receives the lamports from closing the `Roles` account"],"writable":true},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `GUARDIAN_USDON` role","# PDA Seeds","- GUARDIAN_USDON","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[71,117,97,114,100,105,97,110,85,83,68,111,110]},{"kind":"account","path":"authority"}]}},{"name":"role_to_revoke","docs":["The Roles account being closed","# PDA Seeds","- Role seed (from RoleType)","- User's address"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"role_to_revoke.role","account":"Roles"},{"kind":"account","path":"role_to_revoke.address","account":"Roles"}]}}],"args":[]},{"name":"set_allowed_deviation_bps","docs":["Set allowed price deviation in basis points","Signer must have the CONFIGURER_ROLE_ONDO_SANITY_CHECK role"],"discriminator":[46,36,29,85,194,180,86,124],"accounts":[{"name":"authority","docs":["The account with the authority to configure the sanity check"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `CONFIGURER_ROLE_ONDO_SANITY_CHECK` role","# PDA Seeds","- `CONFIGURER_ROLE_ONDO_SANITY_CHECK`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[67,111,110,102,105,103,117,114,101,114,82,111,108,101,79,110,100,111,83,97,110,105,116,121,67,104,101,99,107]},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The GM Token mint"]},{"name":"sanity_check_account","docs":["The OracleSanityCheck account to be configured","# PDA Seeds","- `ORACLE_SANITY_CHECK_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,97,110,105,116,121,95,99,104,101,99,107]},{"kind":"account","path":"mint"}]}}],"args":[{"name":"allowed_deviation_bps","type":"u64"}]},{"name":"set_attestation_signer_secp","docs":["Update the secp256k1 attestation signer address","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[121,157,129,65,181,51,55,0],"accounts":[{"name":"authority","docs":["The account with the authority to execute the unpause/configuration operation"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The `GmTokenManagerState` account to be modified","# PDA Seeds","- `GMTOKEN_MANAGER_STATE_SEED`"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[{"name":"attestation_signer_secp","type":{"array":["u8",20]}}]},{"name":"set_last_price","docs":["Update the last price in sanity check","Signer must have the SETTER_ROLE_ONDO_SANITY_CHECK role"],"discriminator":[58,56,189,177,199,145,145,43],"accounts":[{"name":"authority","docs":["The account with the authority to set the last price"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `SETTER_ROLE_ONDO_SANITY_CHECK` role","# PDA Seeds","- `SETTER_ROLE_ONDO_SANITY_CHECK`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[83,101,116,116,101,114,82,111,108,101,79,110,100,111,83,97,110,105,116,121,67,104,101,99,107]},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The GM Token mint"]},{"name":"sanity_check_account","docs":["The `OracleSanityCheck` account to be updated","# PDA Seeds","- `ORACLE_SANITY_CHECK_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,97,110,105,116,121,95,99,104,101,99,107]},{"kind":"account","path":"mint"}]}}],"args":[{"name":"last_price","type":"u64"}]},{"name":"set_max_time_delay","docs":["Set maximum time delay for sanity check","Signer must have the CONFIGURER_ROLE_ONDO_SANITY_CHECK role"],"discriminator":[145,127,145,218,239,173,252,9],"accounts":[{"name":"authority","docs":["The account with the authority to configure the sanity check"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `CONFIGURER_ROLE_ONDO_SANITY_CHECK` role","# PDA Seeds","- `CONFIGURER_ROLE_ONDO_SANITY_CHECK`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[67,111,110,102,105,103,117,114,101,114,82,111,108,101,79,110,100,111,83,97,110,105,116,121,67,104,101,99,107]},{"kind":"account","path":"authority"}]}},{"name":"mint","docs":["The GM Token mint"]},{"name":"sanity_check_account","docs":["The OracleSanityCheck account to be configured","# PDA Seeds","- `ORACLE_SANITY_CHECK_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,97,110,105,116,121,95,99,104,101,99,107]},{"kind":"account","path":"mint"}]}}],"args":[{"name":"max_time_delay","type":"i64"}]},{"name":"set_ondo_user_limits","docs":["Set rate limit for a user","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[86,133,101,215,245,143,28,159],"accounts":[{"name":"authority","docs":["The account with the authority to set user limits"],"signer":true},{"name":"mint","docs":["The GM Token mint"],"relations":["token_limit"]},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}},{"name":"ondo_user","docs":["The `OndoUser` account to update","# PDA Seeds","- `ONDO_USER_SEED`","- User's owner address","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[79,110,100,111,85,115,101,114]},{"kind":"account","path":"ondo_user.owner","account":"OndoUser"},{"kind":"account","path":"mint"}]}},{"name":"token_limit","docs":["The TokenLimit account","# PDA Seeds","- TOKEN_LIMIT_ACCOUNT_SEED","- Mint address"],"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"mint"}]}}],"args":[{"name":"rate_limit","type":"u64"},{"name":"limit_window","type":"u64"}]},{"name":"set_oracle_price_max_age","docs":["Set the maximum age for oracle price data. When USDC oracle prices are more stale than `oracle_price_max_age`","swaps will halt.","Signer must have the ADMIN_ROLE_USDON_MANAGER role"],"discriminator":[198,173,23,10,148,48,248,61],"accounts":[{"name":"authority","docs":["The account with the authority to execute the operation"],"signer":true},{"name":"usdon_manager_state","docs":["The USDonManagerState account to be modified","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `ADMIN_ROLE_USDON_MANAGER` role","# PDA Seeds","- ADMIN_ROLE_USDON_MANAGER","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,85,83,68,111,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}}],"args":[{"name":"oracle_price_max_age","type":"u64"}]},{"name":"set_token_limit","docs":["Update token-level rate limits","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER role"],"discriminator":[216,80,182,245,223,87,77,53],"accounts":[{"name":"authority","docs":["The account with the authority to update token limits"],"writable":true,"signer":true},{"name":"mint","docs":["The GM Token or USDon mint"]},{"name":"token_limit","docs":["The `TokenLimit` account to be updated","# PDA Seeds","- `TOKEN_LIMIT_ACCOUNT_SEED`","- Mint address"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110]},{"kind":"account","path":"mint"}]}},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER`","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,71,77,84,111,107,101,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}}],"args":[{"name":"rate_limit","type":{"option":"u64"}},{"name":"limit_window","type":{"option":"u64"}},{"name":"default_user_rate_limit","type":{"option":"u64"}},{"name":"default_user_limit_window","type":{"option":"u64"}}]},{"name":"set_trading_hours_offset","docs":["Set the trading hours offset","Signer must have the ADMIN_ROLE_GMTOKEN_MANAGER or ISSUANCE_HOURS_ROLE role"],"discriminator":[65,114,20,50,234,67,255,126],"accounts":[{"name":"authority","docs":["The account with the authority to set the trading hours offset"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `ADMIN_ROLE_GMTOKEN_MANAGER` role","# PDA Seeds","- `ADMIN_ROLE_GMTOKEN_MANAGER` or `ISSUANCE_HOURS_ROLE`","- The authority's address"],"pda":{"seeds":[{"kind":"account","path":"authority_role_account.role","account":"Roles"},{"kind":"account","path":"authority"}]}},{"name":"gmtoken_manager_state","docs":["The GmTokenManagerState account to be modified"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,109,116,111,107,101,110,95,109,97,110,97,103,101,114]}]}}],"args":[{"name":"new_trading_hours_offset","type":"i64"}]},{"name":"set_usdc_price_update_address","docs":["Set the USDC price update address","Signer must have the ADMIN_ROLE_USDON_MANAGER role"],"discriminator":[12,142,252,201,224,148,254,32],"accounts":[{"name":"authority","docs":["The account with the authority to execute the operation"],"signer":true},{"name":"usdon_manager_state","docs":["The USDonManagerState account to be modified","# PDA Seeds","- USDON_MANAGER_STATE_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"authority_role_account","docs":["The Roles account verifying the authority has the `ADMIN_ROLE_USDON_MANAGER` role","# PDA Seeds","- ADMIN_ROLE_USDON_MANAGER","- The authority's address"],"pda":{"seeds":[{"kind":"const","value":[65,100,109,105,110,82,111,108,101,85,83,68,111,110,77,97,110,97,103,101,114]},{"kind":"account","path":"authority"}]}}],"args":[{"name":"new_price_update_address","type":"pubkey"}]},{"name":"update_scaled_ui_multiplier","docs":["Update the UI multiplier for token display","Signer must have the UPDATE_MULTIPLIER_ROLE role"],"discriminator":[96,212,189,168,220,27,240,70],"accounts":[{"name":"authority","docs":["The account with the authority to update the multiplier"],"writable":true,"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has the `UPDATE_MULTIPLIER_ROLE` role"],"pda":{"seeds":[{"kind":"const","value":[85,112,100,97,116,101,77,117,108,116,105,112,108,105,101,114,82,111,108,101]},{"kind":"account","path":"authority"}]}},{"name":"mint_authority","docs":["Does not need to be checked for correctness as it is uninitialized."],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"mint","docs":["The mint whose scaled UI multiplier is being updated"],"writable":true},{"name":"usdon_manager_state","docs":["The USDon manager state account, validates that the mint is not the USDon mint"],"pda":{"seeds":[{"kind":"const","value":[117,115,100,111,110,95,109,97,110,97,103,101,114]}]}},{"name":"token_program","docs":["The token program (should be the spl_token_2022 program)"]}],"args":[{"name":"new_multiplier","type":"f64"},{"name":"timestamp","type":"i64"}]},{"name":"update_token_metadata","docs":["Update a token's metadata (name, symbol, URI)","Signer must have the UPDATE_METADATA_ROLE role"],"discriminator":[243,6,8,23,126,181,251,158],"accounts":[{"name":"payer","docs":["Pays for fees if needed"],"writable":true,"signer":true},{"name":"authority","docs":["The operator updating the metadata, if multisig then this is different to the payer","Otherwise, the operator is the same as the payer"],"signer":true},{"name":"authority_role_account","docs":["The `Roles` account verifying the authority has `UPDATE_METADATA_ROLE` role"],"pda":{"seeds":[{"kind":"const","value":[85,112,100,97,116,101,77,101,116,97,100,97,116,97,82,111,108,101]},{"kind":"account","path":"authority"}]}},{"name":"mint_authority","docs":["Does not need to be checked for correctness as it is uninitialized."],"pda":{"seeds":[{"kind":"const","value":[109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"mint","docs":["The mint whose metadata is being updated"],"writable":true},{"name":"token_program","docs":["The token program (should be the spl_token_2022 program)"]},{"name":"system_program","docs":["The system program"],"address":"11111111111111111111111111111111"}],"args":[{"name":"new_name","type":{"option":"string"}},{"name":"new_symbol","type":{"option":"string"}},{"name":"new_uri","type":{"option":"string"}}]}],"accounts":[{"name":"Attestation","discriminator":[152,125,183,86,36,146,121,73]},{"name":"GMTokenManagerState","discriminator":[120,3,104,163,207,32,156,24]},{"name":"OndoUser","discriminator":[20,5,255,14,176,93,189,142]},{"name":"OracleSanityCheck","discriminator":[27,186,33,40,113,34,206,76]},{"name":"Roles","discriminator":[177,37,17,201,242,158,212,65]},{"name":"TokenLimit","discriminator":[130,254,128,120,255,3,217,51]},{"name":"USDonManagerState","discriminator":[4,39,95,41,51,43,174,91]},{"name":"Whitelist","discriminator":[204,176,52,79,146,121,54,247]}],"events":[{"name":"GMTokenDeployed","discriminator":[155,46,81,223,209,203,89,46]},{"name":"GMTokenMintingPaused","discriminator":[215,17,82,34,76,211,151,22]},{"name":"GMTokenPaused","discriminator":[147,68,57,240,27,46,44,201]},{"name":"GMTokenRedemptionPaused","discriminator":[246,249,230,73,152,65,61,214]},{"name":"RateLimitTokenSet","discriminator":[133,18,77,154,53,236,197,235]},{"name":"RateLimitUserSet","discriminator":[175,49,222,84,192,105,171,129]},{"name":"RoleGranted","discriminator":[220,183,89,228,143,63,246,58]},{"name":"RoleRevoked","discriminator":[167,183,52,229,126,206,62,61]},{"name":"SanityCheckSet","discriminator":[9,207,117,155,44,153,31,234]},{"name":"SanityCheckUpdated","discriminator":[173,97,103,183,67,230,249,1]},{"name":"SetTradingHoursOffset","discriminator":[122,8,3,44,200,105,217,208]},{"name":"TokenFactoryPaused","discriminator":[24,185,192,62,218,128,226,103]},{"name":"TokenManagerMintingPaused","discriminator":[96,11,118,59,8,78,247,254]},{"name":"TokenManagerRedemptionPaused","discriminator":[66,222,165,60,32,59,135,161]},{"name":"TokensRetrieved","discriminator":[217,35,1,89,38,122,145,187]},{"name":"TradeExecuted","discriminator":[41,110,64,129,60,79,179,80]},{"name":"UserAddedToWhitelist","discriminator":[206,139,181,72,116,111,42,42]},{"name":"UserRemovedFromWhitelist","discriminator":[106,223,245,11,213,150,2,15]}],"errors":[{"code":6000,"name":"MissingOrMismatchedSecpIx","msg":"Missing or mismatched secp256k1 verification instruction"},{"code":6001,"name":"MalformedSecpIx","msg":"Malformed secp256k1 instruction"},{"code":6002,"name":"WrongSigCount","msg":"Wrong signature count"},{"code":6003,"name":"WrongDigestLen","msg":"Expected 32-byte hash"},{"code":6004,"name":"DigestMismatch","msg":"Digest mismatch"},{"code":6005,"name":"AddressMismatch","msg":"Recovered address mismatch"}],"types":[{"name":"Attestation","docs":["Attestation account to track consumed attestations","each consumed attestation is stored in its own account"],"type":{"kind":"struct","fields":[{"name":"attestation_id","type":{"array":["u8",16]}},{"name":"creator","type":"pubkey"},{"name":"created_at","type":"i64"},{"name":"bump","type":"u8"}]}},{"name":"GMTokenDeployed","docs":["Event emitted when a GM Token is deployed","Fields:","- gm_token: The public key of the deployed GM Token"],"type":{"kind":"struct","fields":[{"name":"gm_token","type":"pubkey"}]}},{"name":"GMTokenManagerState","docs":["GM Token Manager State account - tracks global state for GM Token operations"],"type":{"kind":"struct","fields":[{"name":"execution_id","type":{"option":"u128"}},{"name":"factory_paused","type":"bool"},{"name":"redemption_paused","type":"bool"},{"name":"minting_paused","type":"bool"},{"name":"bump","type":"u8"},{"name":"attestation_signer_secp","docs":["Ethereum address (20 bytes) for secp256k1 signature verification","Used to verify attestation signatures for buy/sell operations","All zeros ([0u8; 20]) means not set"],"type":{"array":["u8",20]}},{"name":"trading_hours_offset","docs":["Trading hours offset from UTC in seconds","Positive values are east of UTC, negative values are west of UTC"],"type":"i64"}]}},{"name":"GMTokenMintingPaused","docs":["Event emitted when minting for a GM Token is paused or unpaused","Fields:","- is_paused: Boolean indicating if minting/redemptions are paused","- token: The address of the GM Token","- pauser: The address of the operator who performed the pause/unpause action"],"type":{"kind":"struct","fields":[{"name":"is_paused","type":"bool"},{"name":"token","type":"pubkey"},{"name":"pauser","type":"pubkey"}]}},{"name":"GMTokenPaused","docs":["Event emitted when a GM Token is paused/unpaused at the mint-level","Fields:","- is_paused: Boolean indicating whether the token is paused","- token: The address of the GM Token","- pauser: The address of the operator who performed the pause/unpaused action"],"type":{"kind":"struct","fields":[{"name":"is_paused","type":"bool"},{"name":"token","type":"pubkey"},{"name":"pauser","type":"pubkey"}]}},{"name":"GMTokenRedemptionPaused","docs":["Event emitted redemptions for a GM Token are paused or unpaused","Fields:","- is_paused: Boolean indicating if redemptions are paused","- token: The address of the GM Token","- pauser: The address of the operator who performed the pause/unpause action"],"type":{"kind":"struct","fields":[{"name":"is_paused","type":"bool"},{"name":"token","type":"pubkey"},{"name":"pauser","type":"pubkey"}]}},{"name":"OndoUser","docs":["OndoUser state account - tracks user-specific data for a GM Token"],"type":{"kind":"struct","fields":[{"name":"owner","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"rate_limit","type":{"option":"u64"}},{"name":"limit_window","type":{"option":"u64"}},{"name":"mint_capacity_used","type":{"option":"u64"}},{"name":"mint_last_updated","type":{"option":"i64"}},{"name":"redeem_capacity_used","type":{"option":"u64"}},{"name":"redeem_last_updated","type":{"option":"i64"}},{"name":"bump","type":"u8"}]}},{"name":"OracleSanityCheck","docs":["OracleSanityCheck state account - tracks sanity check parameters for a specific mint"],"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"last_price","type":"u64"},{"name":"allowed_deviation_bps","type":"u64"},{"name":"max_time_delay","type":"i64"},{"name":"price_last_updated","type":"i64"},{"name":"bump","type":"u8"}]}},{"name":"RateLimitTokenSet","docs":["Event emitted when a rate limit is set for a token","Fields:","- token: The public key of the token for which the rate limit is set","- limit: The rate limit value","- limit_window: The time window for the rate limit"],"type":{"kind":"struct","fields":[{"name":"token","type":"pubkey"},{"name":"limit","type":{"option":"u64"}},{"name":"limit_window","type":{"option":"u64"}}]}},{"name":"RateLimitUserSet","docs":["Event emitted when a rate limit is set for a user","Fields:","- user: The public key of the user for whom the rate limit is set","- limit: The rate limit value"],"type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"limit","type":"u64"}]}},{"name":"RoleGranted","docs":["Event emitted when a role is granted to a user","Fields:","- role: The role that was granted","- grantee: The public key of the user who was granted the role","- granter: The public key of the user who granted the role"],"type":{"kind":"struct","fields":[{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"grantee","type":"pubkey"},{"name":"granter","type":"pubkey"}]}},{"name":"RoleRevoked","docs":["Event emitted when a role is revoked from a user","Fields:","- role: The role that was revoked","- grantee: The public key of the user who had the role revoked","- revoker: The public key of the user who revoked the role"],"type":{"kind":"struct","fields":[{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"grantee","type":"pubkey"},{"name":"revoker","type":"pubkey"}]}},{"name":"RoleType","type":{"kind":"enum","variants":[{"name":"MinterRoleUSDon"},{"name":"BurnerRoleUSDon"},{"name":"AdminRoleUSDon"},{"name":"AdminRoleUSDonManager"},{"name":"GuardianUSDon"},{"name":"DeployerRoleGMTokenFactory"},{"name":"PauserRoleGMTokenFactory"},{"name":"AdminRoleGMTokenFactory"},{"name":"MinterRoleGMToken"},{"name":"AdminRoleGMToken"},{"name":"PauserRoleGMTokenManager"},{"name":"PauserRoleGMToken"},{"name":"UnpauserRoleGMToken"},{"name":"AdminRoleGMTokenManager"},{"name":"IssuanceHoursRole"},{"name":"SetterRoleOndoSanityCheck"},{"name":"ConfigurerRoleOndoSanityCheck"},{"name":"AdminRoleOndoSanityCheck"},{"name":"AdminRoleWhitelist"},{"name":"UpdateMultiplierRole"},{"name":"UpdateMetadataRole"}]}},{"name":"Roles","docs":["Roles state account - tracks role assignments for addresses"],"type":{"kind":"struct","fields":[{"name":"address","type":"pubkey"},{"name":"role","type":{"defined":{"name":"RoleType"}}},{"name":"bump","type":"u8"}]}},{"name":"SanityCheckSet","docs":["Event emitted when a sanity check is set for a mint","Fields:","- mint: The public key of the mint for which the sanity check is set","- allowed_deviation_bps: The allowed deviation in basis points","- max_time_delay: The maximum time delay for the sanity check"],"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"allowed_deviation_bps","type":"u64"},{"name":"max_time_delay","type":"i64"}]}},{"name":"SanityCheckUpdated","docs":["Event emitted when a sanity check is updated for a mint","Fields:","- mint: The public key of the mint for which the sanity check is updated","- last_price: The last recorded price (optional)","- allowed_deviation_bps: The allowed deviation in basis points (optional)","- max_time_delay: The maximum time delay for the sanity check (optional)"],"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"last_price","type":{"option":"u64"}},{"name":"allowed_deviation_bps","type":{"option":"u64"}},{"name":"max_time_delay","type":{"option":"i64"}}]}},{"name":"SetTradingHoursOffset","docs":["Event emitted when the trading hours offset is set","Fields:","- prev_trading_hours_offset: The previous trading hours offset","- new_trading_hours_offset: The new trading hours offset"],"type":{"kind":"struct","fields":[{"name":"prev_trading_hours_offset","type":"i64"},{"name":"new_trading_hours_offset","type":"i64"}]}},{"name":"TokenFactoryPaused","docs":["Event emitted when the Token Factory is paused or unpaused","Fields:","- is_paused: Boolean indicating if the Token Factory is paused","- pauser: The address of the operator who performed the pause/unpause action"],"type":{"kind":"struct","fields":[{"name":"is_paused","type":"bool"},{"name":"pauser","type":"pubkey"}]}},{"name":"TokenLimit","docs":["TokenLimit state account - tracks global token limit parameters for a specific GM Token"],"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"rate_limit","type":{"option":"u64"}},{"name":"limit_window","type":{"option":"u64"}},{"name":"mint_capacity_used","type":{"option":"u64"}},{"name":"mint_last_updated","type":{"option":"i64"}},{"name":"redeem_capacity_used","type":{"option":"u64"}},{"name":"redeem_last_updated","type":{"option":"i64"}},{"name":"redemption_paused","type":"bool"},{"name":"minting_paused","type":"bool"},{"name":"default_user_rate_limit","type":{"option":"u64"}},{"name":"default_user_limit_window","type":{"option":"u64"}},{"name":"bump","type":"u8"}]}},{"name":"TokenManagerMintingPaused","docs":["Event emitted when GM Token minting is globally paused or unpaused","Fields:","- is_paused: Boolean indicating if the minting is globally paused","- pauser: The address of the user who performed the pause/unpause action"],"type":{"kind":"struct","fields":[{"name":"is_paused","type":"bool"},{"name":"pauser","type":"pubkey"}]}},{"name":"TokenManagerRedemptionPaused","docs":["Event emitted when GM Token redemptions are globally paused or unpaused","Fields:","- is_paused: Boolean indicating if redemptions are globally paused","- pauser: The address of the operator who performed the pause/unpause action"],"type":{"kind":"struct","fields":[{"name":"is_paused","type":"bool"},{"name":"pauser","type":"pubkey"}]}},{"name":"TokensRetrieved","docs":["Event emitted when tokens are retrieved (withdrawn) from a vault by an admin","Fields:","- token: The public key of the token mint being withdrawn","- to: The destination address receiving the tokens","- amount: The amount of tokens withdrawn","- authority: The public key of the admin who executed the withdrawal"],"type":{"kind":"struct","fields":[{"name":"token","type":"pubkey"},{"name":"to","type":"pubkey"},{"name":"amount","type":"u64"},{"name":"authority","type":"pubkey"}]}},{"name":"TradeExecuted","docs":["Event emitted when a trade is executed","Fields:","- execution_id: The unique identifier of the trade execution"],"type":{"kind":"struct","fields":[{"name":"execution_id","type":"u128"}]}},{"name":"USDonManagerState","docs":["USDonManagerState state account - tracks global configuration for the USDon stablecoin system"],"type":{"kind":"struct","fields":[{"name":"owner","type":"pubkey"},{"name":"usdon_mint","type":"pubkey"},{"name":"oracle_price_enabled","type":"bool"},{"name":"oracle_price_max_age","type":"u64"},{"name":"usdc_price_update","type":"pubkey"},{"name":"usdc_vault","type":"pubkey"},{"name":"usdon_vault","type":"pubkey"},{"name":"bump","type":"u8"}]}},{"name":"UserAddedToWhitelist","docs":["Event emitted when a user is added to the whitelist","Fields:","- user: The public key of the user being added to the whitelist","- added_by: The public key of the admin who added the user to the whitelist"],"type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"added_by","type":"pubkey"}]}},{"name":"UserRemovedFromWhitelist","docs":["Event emitted when a user is removed from the whitelist","Fields:","- user: The public key of the user being removed from the whitelist","- removed_by: The public key of the admin who removed the user from the whitelist"],"type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"removed_by","type":"pubkey"}]}},{"name":"Whitelist","docs":["Whitelist account - tracks whitelisted addresses.","","Used as a marker account - presence of the account indicates whitelisting."],"type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/openbook_v2.opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb.json b/migration/khadhroony-bot2-reference/idls/openbook_v2.opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb.json new file mode 100644 index 0000000..4869fc9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/openbook_v2.opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"openbook_v2","instructions":[{"name":"createMarket","docs":["Create a [`Market`](crate::state::Market) for a given token pair."],"accounts":[{"name":"market","isMut":true,"isSigner":true},{"name":"marketAuthority","isMut":false,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false,"docs":["Accounts are initialized by client,","anchor discriminator is set first when ix exits,"]},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"marketBaseVault","isMut":true,"isSigner":false},{"name":"marketQuoteVault","isMut":true,"isSigner":false},{"name":"baseMint","isMut":false,"isSigner":false},{"name":"quoteMint","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"oracleA","isMut":false,"isSigner":false,"isOptional":true},{"name":"oracleB","isMut":false,"isSigner":false,"isOptional":true},{"name":"collectFeeAdmin","isMut":false,"isSigner":false},{"name":"openOrdersAdmin","isMut":false,"isSigner":false,"isOptional":true},{"name":"consumeEventsAdmin","isMut":false,"isSigner":false,"isOptional":true},{"name":"closeMarketAdmin","isMut":false,"isSigner":false,"isOptional":true},{"name":"eventAuthority","isMut":false,"isSigner":false},{"name":"program","isMut":false,"isSigner":false}],"args":[{"name":"name","type":"string"},{"name":"oracleConfig","type":{"defined":"OracleConfigParams"}},{"name":"quoteLotSize","type":"i64"},{"name":"baseLotSize","type":"i64"},{"name":"makerFee","type":"i64"},{"name":"takerFee","type":"i64"},{"name":"timeExpiry","type":"i64"}]},{"name":"closeMarket","docs":["Close a [`Market`](crate::state::Market) (only","[`close_market_admin`](crate::state::Market::close_market_admin))."],"accounts":[{"name":"closeMarketAdmin","isMut":false,"isSigner":true},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false},{"name":"solDestination","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"createOpenOrdersIndexer","docs":["Create an [`OpenOrdersIndexer`](crate::state::OpenOrdersIndexer) account."],"accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"owner","isMut":false,"isSigner":true},{"name":"openOrdersIndexer","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"closeOpenOrdersIndexer","docs":["Close an [`OpenOrdersIndexer`](crate::state::OpenOrdersIndexer) account."],"accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"openOrdersIndexer","isMut":true,"isSigner":false},{"name":"solDestination","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"createOpenOrdersAccount","docs":["Create an [`OpenOrdersAccount`](crate::state::OpenOrdersAccount)."],"accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"owner","isMut":false,"isSigner":true},{"name":"delegateAccount","isMut":false,"isSigner":false,"isOptional":true},{"name":"openOrdersIndexer","isMut":true,"isSigner":false},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"market","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"name","type":"string"}]},{"name":"closeOpenOrdersAccount","docs":["Close an [`OpenOrdersAccount`](crate::state::OpenOrdersAccount)."],"accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"openOrdersIndexer","isMut":true,"isSigner":false},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"solDestination","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"placeOrder","docs":["Place an order.","","Different types of orders have different effects on the order book,","as described in [`PlaceOrderType`](crate::state::PlaceOrderType).","","`price_lots` refers to the price in lots: the number of quote lots","per base lot. It is ignored for `PlaceOrderType::Market` orders.","","`expiry_timestamp` is a unix timestamp for when this order should","expire. If 0 is passed in, the order will never expire. If the time","is in the past, the instruction is skipped. Timestamps in the future","are reduced to now + 65,535s.","","`limit` determines the maximum number of orders from the book to fill,","and can be used to limit CU spent. When the limit is reached, processing","stops and the instruction succeeds."],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"openOrdersAdmin","isMut":false,"isSigner":true,"isOptional":true},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false},{"name":"marketVault","isMut":true,"isSigner":false},{"name":"oracleA","isMut":false,"isSigner":false,"isOptional":true},{"name":"oracleB","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"PlaceOrderArgs"}}],"returns":{"option":"u128"}},{"name":"editOrder","docs":["Edit an order."],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"openOrdersAdmin","isMut":false,"isSigner":true,"isOptional":true},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false},{"name":"marketVault","isMut":true,"isSigner":false},{"name":"oracleA","isMut":false,"isSigner":false,"isOptional":true},{"name":"oracleB","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"clientOrderId","type":"u64"},{"name":"expectedCancelSize","type":"i64"},{"name":"placeOrder","type":{"defined":"PlaceOrderArgs"}}],"returns":{"option":"u128"}},{"name":"editOrderPegged","docs":["Edit an order pegged."],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"openOrdersAdmin","isMut":false,"isSigner":true,"isOptional":true},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false},{"name":"marketVault","isMut":true,"isSigner":false},{"name":"oracleA","isMut":false,"isSigner":false,"isOptional":true},{"name":"oracleB","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"clientOrderId","type":"u64"},{"name":"expectedCancelSize","type":"i64"},{"name":"placeOrder","type":{"defined":"PlaceOrderPeggedArgs"}}],"returns":{"option":"u128"}},{"name":"placeOrders","docs":["Place multiple orders"],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"openOrdersAdmin","isMut":false,"isSigner":true,"isOptional":true},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false},{"name":"marketQuoteVault","isMut":true,"isSigner":false},{"name":"marketBaseVault","isMut":true,"isSigner":false},{"name":"oracleA","isMut":false,"isSigner":false,"isOptional":true},{"name":"oracleB","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"ordersType","type":{"defined":"PlaceOrderType"}},{"name":"bids","type":{"vec":{"defined":"PlaceMultipleOrdersArgs"}}},{"name":"asks","type":{"vec":{"defined":"PlaceMultipleOrdersArgs"}}},{"name":"limit","type":"u8"}],"returns":{"vec":{"option":"u128"}}},{"name":"cancelAllAndPlaceOrders","docs":["Cancel orders and place multiple orders."],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"openOrdersAdmin","isMut":false,"isSigner":true,"isOptional":true},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false},{"name":"marketQuoteVault","isMut":true,"isSigner":false},{"name":"marketBaseVault","isMut":true,"isSigner":false},{"name":"oracleA","isMut":false,"isSigner":false,"isOptional":true},{"name":"oracleB","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"ordersType","type":{"defined":"PlaceOrderType"}},{"name":"bids","type":{"vec":{"defined":"PlaceMultipleOrdersArgs"}}},{"name":"asks","type":{"vec":{"defined":"PlaceMultipleOrdersArgs"}}},{"name":"limit","type":"u8"}],"returns":{"vec":{"option":"u128"}}},{"name":"placeOrderPegged","docs":["Place an oracle-peg order."],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"openOrdersAdmin","isMut":false,"isSigner":true,"isOptional":true},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false},{"name":"marketVault","isMut":true,"isSigner":false},{"name":"oracleA","isMut":false,"isSigner":false,"isOptional":true},{"name":"oracleB","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"PlaceOrderPeggedArgs"}}],"returns":{"option":"u128"}},{"name":"placeTakeOrder","docs":["Place an order that shall take existing liquidity off of the book, not","add a new order off the book.","","This type of order allows for instant token settlement for the taker."],"accounts":[{"name":"signer","isMut":true,"isSigner":true},{"name":"penaltyPayer","isMut":true,"isSigner":true},{"name":"market","isMut":true,"isSigner":false},{"name":"marketAuthority","isMut":false,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"marketBaseVault","isMut":true,"isSigner":false},{"name":"marketQuoteVault","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"oracleA","isMut":false,"isSigner":false,"isOptional":true},{"name":"oracleB","isMut":false,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"openOrdersAdmin","isMut":false,"isSigner":true,"isOptional":true}],"args":[{"name":"args","type":{"defined":"PlaceTakeOrderArgs"}}]},{"name":"consumeEvents","docs":["Process up to `limit` [events](crate::state::AnyEvent).","","When a user places a 'take' order, they do not know beforehand which","market maker will have placed the 'make' order that they get executed","against. This prevents them from passing in a market maker's","[`OpenOrdersAccount`](crate::state::OpenOrdersAccount), which is needed","to credit/debit the relevant tokens to/from the maker. As such, Openbook","uses a 'crank' system, where `place_order` only emits events, and","`consume_events` handles token settlement.","","Currently, there are two types of events: [`FillEvent`](crate::state::FillEvent)s","and [`OutEvent`](crate::state::OutEvent)s.","","A `FillEvent` is emitted when an order is filled, and it is handled by","debiting whatever the taker is selling from the taker and crediting","it to the maker, and debiting whatever the taker is buying from the","maker and crediting it to the taker. Note that *no tokens are moved*,","these are just debits and credits to each party's [`Position`](crate::state::Position).","","An `OutEvent` is emitted when a limit order needs to be removed from","the book during a `place_order` invocation, and it is handled by","crediting whatever the maker would have sold (quote token in a bid,","base token in an ask) back to the maker."],"accounts":[{"name":"consumeEventsAdmin","isMut":false,"isSigner":true,"isOptional":true},{"name":"market","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false}],"args":[{"name":"limit","type":"u64"}]},{"name":"consumeGivenEvents","docs":["Process the [events](crate::state::AnyEvent) at the given positions."],"accounts":[{"name":"consumeEventsAdmin","isMut":false,"isSigner":true,"isOptional":true},{"name":"market","isMut":true,"isSigner":false},{"name":"eventHeap","isMut":true,"isSigner":false}],"args":[{"name":"slots","type":{"vec":"u64"}}]},{"name":"cancelOrder","docs":["Cancel an order by its `order_id`.","","Note that this doesn't emit an [`OutEvent`](crate::state::OutEvent) because a","maker knows that they will be passing in their own [`OpenOrdersAccount`](crate::state::OpenOrdersAccount)."],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"market","isMut":false,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false}],"args":[{"name":"orderId","type":"u128"}]},{"name":"cancelOrderByClientOrderId","docs":["Cancel an order by its `client_order_id`.","","Note that this doesn't emit an [`OutEvent`](crate::state::OutEvent) because a","maker knows that they will be passing in their own [`OpenOrdersAccount`](crate::state::OpenOrdersAccount)."],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"market","isMut":false,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false}],"args":[{"name":"clientOrderId","type":"u64"}],"returns":"i64"},{"name":"cancelAllOrders","docs":["Cancel up to `limit` orders, optionally filtering by side"],"accounts":[{"name":"signer","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"market","isMut":false,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false}],"args":[{"name":"sideOption","type":{"option":{"defined":"Side"}}},{"name":"limit","type":"u8"}]},{"name":"deposit","docs":["Deposit a certain amount of `base` and `quote` lamports into one's","[`Position`](crate::state::Position).","","Makers might wish to `deposit`, rather than have actual tokens moved for","each trade, in order to reduce CUs."],"accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"marketBaseVault","isMut":true,"isSigner":false},{"name":"marketQuoteVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"baseAmount","type":"u64"},{"name":"quoteAmount","type":"u64"}]},{"name":"refill","docs":["Refill a certain amount of `base` and `quote` lamports. The amount being passed is the","total lamports that the [`Position`](crate::state::Position) will have.","","Makers might wish to `refill`, rather than have actual tokens moved for","each trade, in order to reduce CUs."],"accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"marketBaseVault","isMut":true,"isSigner":false},{"name":"marketQuoteVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"baseAmount","type":"u64"},{"name":"quoteAmount","type":"u64"}]},{"name":"settleFunds","docs":["Withdraw any available tokens."],"accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"penaltyPayer","isMut":true,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"marketAuthority","isMut":false,"isSigner":false},{"name":"marketBaseVault","isMut":true,"isSigner":false},{"name":"marketQuoteVault","isMut":true,"isSigner":false},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"referrerAccount","isMut":true,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"settleFundsExpired","docs":["Withdraw any available tokens when the market is expired (only","[`close_market_admin`](crate::state::Market::close_market_admin))."],"accounts":[{"name":"closeMarketAdmin","isMut":false,"isSigner":true},{"name":"owner","isMut":true,"isSigner":true},{"name":"penaltyPayer","isMut":true,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"marketAuthority","isMut":false,"isSigner":false},{"name":"marketBaseVault","isMut":true,"isSigner":false},{"name":"marketQuoteVault","isMut":true,"isSigner":false},{"name":"userBaseAccount","isMut":true,"isSigner":false},{"name":"userQuoteAccount","isMut":true,"isSigner":false},{"name":"referrerAccount","isMut":true,"isSigner":false,"isOptional":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"sweepFees","docs":["Sweep fees, as a [`Market`](crate::state::Market)'s admin."],"accounts":[{"name":"collectFeeAdmin","isMut":false,"isSigner":true},{"name":"market","isMut":true,"isSigner":false},{"name":"marketAuthority","isMut":false,"isSigner":false},{"name":"marketQuoteVault","isMut":true,"isSigner":false},{"name":"tokenReceiverAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"setDelegate","docs":["Update the [`delegate`](crate::state::OpenOrdersAccount::delegate) of an open orders account."],"accounts":[{"name":"owner","isMut":true,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"delegateAccount","isMut":false,"isSigner":false,"isOptional":true}],"args":[]},{"name":"setMarketExpired","docs":["Set market to expired before pruning orders and closing the market (only","[`close_market_admin`](crate::state::Market::close_market_admin))."],"accounts":[{"name":"closeMarketAdmin","isMut":false,"isSigner":true},{"name":"market","isMut":true,"isSigner":false}],"args":[]},{"name":"pruneOrders","docs":["Remove orders from the book when the market is expired (only","[`close_market_admin`](crate::state::Market::close_market_admin))."],"accounts":[{"name":"closeMarketAdmin","isMut":false,"isSigner":true},{"name":"openOrdersAccount","isMut":true,"isSigner":false},{"name":"market","isMut":false,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false}],"args":[{"name":"limit","type":"u8"}]},{"name":"stubOracleCreate","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"owner","isMut":false,"isSigner":true},{"name":"oracle","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"price","type":"f64"}]},{"name":"stubOracleClose","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"oracle","isMut":true,"isSigner":false},{"name":"solDestination","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"stubOracleSet","accounts":[{"name":"owner","isMut":false,"isSigner":true},{"name":"oracle","isMut":true,"isSigner":false}],"args":[{"name":"price","type":"f64"}]}],"accounts":[{"name":"Market","type":{"kind":"struct","fields":[{"name":"bump","docs":["PDA bump"],"type":"u8"},{"name":"baseDecimals","docs":["Number of decimals used for the base token.","","Used to convert the oracle's price into a native/native price."],"type":"u8"},{"name":"quoteDecimals","type":"u8"},{"name":"padding1","type":{"array":["u8",5]}},{"name":"marketAuthority","type":"publicKey"},{"name":"timeExpiry","docs":["No expiry = 0. Market will expire and no trading allowed after time_expiry"],"type":"i64"},{"name":"collectFeeAdmin","docs":["Admin who can collect fees from the market"],"type":"publicKey"},{"name":"openOrdersAdmin","docs":["Admin who must sign off on all order creations"],"type":{"defined":"NonZeroPubkeyOption"}},{"name":"consumeEventsAdmin","docs":["Admin who must sign off on all event consumptions"],"type":{"defined":"NonZeroPubkeyOption"}},{"name":"closeMarketAdmin","docs":["Admin who can set market expired, prune orders and close the market"],"type":{"defined":"NonZeroPubkeyOption"}},{"name":"name","docs":["Name. Trailing zero bytes are ignored."],"type":{"array":["u8",16]}},{"name":"bids","docs":["Address of the BookSide account for bids"],"type":"publicKey"},{"name":"asks","docs":["Address of the BookSide account for asks"],"type":"publicKey"},{"name":"eventHeap","docs":["Address of the EventHeap account"],"type":"publicKey"},{"name":"oracleA","docs":["Oracles account address"],"type":{"defined":"NonZeroPubkeyOption"}},{"name":"oracleB","type":{"defined":"NonZeroPubkeyOption"}},{"name":"oracleConfig","docs":["Oracle configuration"],"type":{"defined":"OracleConfig"}},{"name":"quoteLotSize","docs":["Number of quote native in a quote lot. Must be a power of 10.","","Primarily useful for increasing the tick size on the market: A lot price","of 1 becomes a native price of quote_lot_size/base_lot_size becomes a","ui price of quote_lot_size*base_decimals/base_lot_size/quote_decimals."],"type":"i64"},{"name":"baseLotSize","docs":["Number of base native in a base lot. Must be a power of 10.","","Example: If base decimals for the underlying asset is 6, base lot size","is 100 and and base position lots is 10_000 then base position native is","1_000_000 and base position ui is 1."],"type":"i64"},{"name":"seqNum","docs":["Total number of orders seen"],"type":"u64"},{"name":"registrationTime","docs":["Timestamp in seconds that the market was registered at."],"type":"i64"},{"name":"makerFee","docs":["Fees","","Fee (in 10^-6) when matching maker orders.","maker_fee < 0 it means some of the taker_fees goes to the maker","maker_fee > 0, it means no taker_fee to the maker, and maker fee goes to the referral"],"type":"i64"},{"name":"takerFee","docs":["Fee (in 10^-6) for taker orders, always >= 0."],"type":"i64"},{"name":"feesAccrued","docs":["Total fees accrued in native quote"],"type":"u128"},{"name":"feesToReferrers","docs":["Total fees settled in native quote"],"type":"u128"},{"name":"referrerRebatesAccrued","docs":["Referrer rebates to be distributed"],"type":"u64"},{"name":"feesAvailable","docs":["Fees generated and available to withdraw via sweep_fees"],"type":"u64"},{"name":"makerVolume","docs":["Cumulative maker volume (same as taker volume) in quote native units"],"type":"u128"},{"name":"takerVolumeWoOo","docs":["Cumulative taker volume in quote native units due to place take orders"],"type":"u128"},{"name":"baseMint","type":"publicKey"},{"name":"quoteMint","type":"publicKey"},{"name":"marketBaseVault","type":"publicKey"},{"name":"baseDepositTotal","type":"u64"},{"name":"marketQuoteVault","type":"publicKey"},{"name":"quoteDepositTotal","type":"u64"},{"name":"reserved","type":{"array":["u8",128]}}]}},{"name":"OpenOrdersAccount","type":{"kind":"struct","fields":[{"name":"owner","type":"publicKey"},{"name":"market","type":"publicKey"},{"name":"name","type":{"array":["u8",32]}},{"name":"delegate","type":{"defined":"NonZeroPubkeyOption"}},{"name":"accountNum","type":"u32"},{"name":"bump","type":"u8"},{"name":"version","type":"u8"},{"name":"padding","type":{"array":["u8",2]}},{"name":"position","type":{"defined":"Position"}},{"name":"openOrders","type":{"array":[{"defined":"OpenOrder"},24]}}]}},{"name":"OpenOrdersIndexer","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"createdCounter","type":"u32"},{"name":"addresses","type":{"vec":"publicKey"}}]}},{"name":"StubOracle","type":{"kind":"struct","fields":[{"name":"owner","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"price","type":"f64"},{"name":"lastUpdateTs","type":"i64"},{"name":"lastUpdateSlot","type":"u64"},{"name":"deviation","type":"f64"},{"name":"reserved","type":{"array":["u8",104]}}]}},{"name":"BookSide","type":{"kind":"struct","fields":[{"name":"roots","type":{"array":[{"defined":"OrderTreeRoot"},2]}},{"name":"reservedRoots","type":{"array":[{"defined":"OrderTreeRoot"},4]}},{"name":"reserved","type":{"array":["u8",256]}},{"name":"nodes","type":{"defined":"OrderTreeNodes"}}]}},{"name":"EventHeap","docs":["Container for the different EventTypes.","","Events are stored in a fixed-array of nodes. Free nodes are connected by a single-linked list","starting at free_head while used nodes form a circular doubly-linked list starting at","used_head."],"type":{"kind":"struct","fields":[{"name":"header","type":{"defined":"EventHeapHeader"}},{"name":"nodes","type":{"array":[{"defined":"EventNode"},600]}},{"name":"reserved","type":{"array":["u8",64]}}]}}],"types":[{"name":"NonZeroPubkeyOption","docs":["Like `Option`, but implemented for `Pubkey` to be used with `zero_copy`"],"type":{"kind":"struct","fields":[{"name":"key","type":"publicKey"}]}},{"name":"Position","type":{"kind":"struct","fields":[{"name":"bidsBaseLots","docs":["Base lots in open bids"],"type":"i64"},{"name":"asksBaseLots","docs":["Base lots in open asks"],"type":"i64"},{"name":"baseFreeNative","type":"u64"},{"name":"quoteFreeNative","type":"u64"},{"name":"lockedMakerFees","type":"u64"},{"name":"referrerRebatesAvailable","type":"u64"},{"name":"penaltyHeapCount","docs":["Count of ixs when events are added to the heap","To avoid this, send remaining accounts in order to process the events"],"type":"u64"},{"name":"makerVolume","docs":["Cumulative maker volume in quote native units (display only)"],"type":"u128"},{"name":"takerVolume","docs":["Cumulative taker volume in quote native units (display only)"],"type":"u128"},{"name":"bidsQuoteLots","docs":["Quote lots in open bids"],"type":"i64"},{"name":"reserved","type":{"array":["u8",64]}}]}},{"name":"OpenOrder","type":{"kind":"struct","fields":[{"name":"id","type":"u128"},{"name":"clientId","type":"u64"},{"name":"lockedPrice","docs":["Price at which user's assets were locked"],"type":"i64"},{"name":"isFree","type":"u8"},{"name":"sideAndTree","type":"u8"},{"name":"padding","type":{"array":["u8",6]}}]}},{"name":"OracleConfig","type":{"kind":"struct","fields":[{"name":"confFilter","type":"f64"},{"name":"maxStalenessSlots","type":"i64"},{"name":"reserved","type":{"array":["u8",72]}}]}},{"name":"OracleConfigParams","type":{"kind":"struct","fields":[{"name":"confFilter","type":"f32"},{"name":"maxStalenessSlots","type":{"option":"u32"}}]}},{"name":"EventHeapHeader","type":{"kind":"struct","fields":[{"name":"freeHead","type":"u16"},{"name":"usedHead","type":"u16"},{"name":"count","type":"u16"},{"name":"padd","type":"u16"},{"name":"seqNum","type":"u64"}]}},{"name":"EventNode","type":{"kind":"struct","fields":[{"name":"next","type":"u16"},{"name":"prev","type":"u16"},{"name":"pad","type":{"array":["u8",4]}},{"name":"event","type":{"defined":"AnyEvent"}}]}},{"name":"AnyEvent","type":{"kind":"struct","fields":[{"name":"eventType","type":"u8"},{"name":"padding","type":{"array":["u8",143]}}]}},{"name":"FillEvent","type":{"kind":"struct","fields":[{"name":"eventType","type":"u8"},{"name":"takerSide","type":"u8"},{"name":"makerOut","type":"u8"},{"name":"makerSlot","type":"u8"},{"name":"padding","type":{"array":["u8",4]}},{"name":"timestamp","type":"u64"},{"name":"seqNum","type":"u64"},{"name":"maker","type":"publicKey"},{"name":"makerTimestamp","type":"u64"},{"name":"taker","type":"publicKey"},{"name":"takerClientOrderId","type":"u64"},{"name":"price","type":"i64"},{"name":"pegLimit","type":"i64"},{"name":"quantity","type":"i64"},{"name":"makerClientOrderId","type":"u64"},{"name":"reserved","type":{"array":["u8",8]}}]}},{"name":"OutEvent","type":{"kind":"struct","fields":[{"name":"eventType","type":"u8"},{"name":"side","type":"u8"},{"name":"ownerSlot","type":"u8"},{"name":"padding0","type":{"array":["u8",5]}},{"name":"timestamp","type":"u64"},{"name":"seqNum","type":"u64"},{"name":"owner","type":"publicKey"},{"name":"quantity","type":"i64"},{"name":"padding1","type":{"array":["u8",80]}}]}},{"name":"InnerNode","docs":["InnerNodes and LeafNodes compose the binary tree of orders.","","Each InnerNode has exactly two children, which are either InnerNodes themselves,","or LeafNodes. The children share the top `prefix_len` bits of `key`. The left","child has a 0 in the next bit, and the right a 1."],"type":{"kind":"struct","fields":[{"name":"tag","type":"u8"},{"name":"padding","type":{"array":["u8",3]}},{"name":"prefixLen","docs":["number of highest `key` bits that all children share","e.g. if it's 2, the two highest bits of `key` will be the same on all children"],"type":"u32"},{"name":"key","docs":["only the top `prefix_len` bits of `key` are relevant"],"type":"u128"},{"name":"children","docs":["indexes into `BookSide::nodes`"],"type":{"array":["u32",2]}},{"name":"childEarliestExpiry","docs":["The earliest expiry timestamp for the left and right subtrees.","","Needed to be able to find and remove expired orders without having to","iterate through the whole bookside."],"type":{"array":["u64",2]}},{"name":"reserved","type":{"array":["u8",40]}}]}},{"name":"LeafNode","docs":["LeafNodes represent an order in the binary tree"],"type":{"kind":"struct","fields":[{"name":"tag","docs":["NodeTag"],"type":"u8"},{"name":"ownerSlot","docs":["Index into the owning OpenOrdersAccount's OpenOrders"],"type":"u8"},{"name":"timeInForce","docs":["Time in seconds after `timestamp` at which the order expires.","A value of 0 means no expiry."],"type":"u16"},{"name":"padding","type":{"array":["u8",4]}},{"name":"key","docs":["The binary tree key, see new_node_key()"],"type":"u128"},{"name":"owner","docs":["Address of the owning OpenOrdersAccount"],"type":"publicKey"},{"name":"quantity","docs":["Number of base lots to buy or sell, always >=1"],"type":"i64"},{"name":"timestamp","docs":["The time the order was placed"],"type":"u64"},{"name":"pegLimit","docs":["If the effective price of an oracle pegged order exceeds this limit,","it will be considered invalid and may be removed.","","Only applicable in the oracle_pegged OrderTree"],"type":"i64"},{"name":"clientOrderId","docs":["User defined id for this order, used in FillEvents"],"type":"u64"}]}},{"name":"AnyNode","type":{"kind":"struct","fields":[{"name":"tag","type":"u8"},{"name":"data","type":{"array":["u8",87]}}]}},{"name":"OrderTreeRoot","type":{"kind":"struct","fields":[{"name":"maybeNode","type":"u32"},{"name":"leafCount","type":"u32"}]}},{"name":"OrderTreeNodes","docs":["A binary tree on AnyNode::key()","","The key encodes the price in the top 64 bits."],"type":{"kind":"struct","fields":[{"name":"orderTreeType","type":"u8"},{"name":"padding","type":{"array":["u8",3]}},{"name":"bumpIndex","type":"u32"},{"name":"freeListLen","type":"u32"},{"name":"freeListHead","type":"u32"},{"name":"reserved","type":{"array":["u8",512]}},{"name":"nodes","type":{"array":[{"defined":"AnyNode"},1024]}}]}},{"name":"I80F48","docs":["Nothing in Rust shall use these types. They only exist so that the Anchor IDL","knows about them and typescript can deserialize it."],"type":{"kind":"struct","fields":[{"name":"val","type":"i128"}]}},{"name":"PlaceOrderArgs","type":{"kind":"struct","fields":[{"name":"side","type":{"defined":"Side"}},{"name":"priceLots","type":"i64"},{"name":"maxBaseLots","type":"i64"},{"name":"maxQuoteLotsIncludingFees","type":"i64"},{"name":"clientOrderId","type":"u64"},{"name":"orderType","type":{"defined":"PlaceOrderType"}},{"name":"expiryTimestamp","type":"u64"},{"name":"selfTradeBehavior","type":{"defined":"SelfTradeBehavior"}},{"name":"limit","type":"u8"}]}},{"name":"PlaceMultipleOrdersArgs","type":{"kind":"struct","fields":[{"name":"priceLots","type":"i64"},{"name":"maxQuoteLotsIncludingFees","type":"i64"},{"name":"expiryTimestamp","type":"u64"}]}},{"name":"PlaceOrderPeggedArgs","type":{"kind":"struct","fields":[{"name":"side","type":{"defined":"Side"}},{"name":"priceOffsetLots","type":"i64"},{"name":"pegLimit","type":"i64"},{"name":"maxBaseLots","type":"i64"},{"name":"maxQuoteLotsIncludingFees","type":"i64"},{"name":"clientOrderId","type":"u64"},{"name":"orderType","type":{"defined":"PlaceOrderType"}},{"name":"expiryTimestamp","type":"u64"},{"name":"selfTradeBehavior","type":{"defined":"SelfTradeBehavior"}},{"name":"limit","type":"u8"}]}},{"name":"PlaceTakeOrderArgs","type":{"kind":"struct","fields":[{"name":"side","type":{"defined":"Side"}},{"name":"priceLots","type":"i64"},{"name":"maxBaseLots","type":"i64"},{"name":"maxQuoteLotsIncludingFees","type":"i64"},{"name":"orderType","type":{"defined":"PlaceOrderType"}},{"name":"limit","type":"u8"}]}},{"name":"OracleType","type":{"kind":"enum","variants":[{"name":"Pyth"},{"name":"Stub"},{"name":"SwitchboardV1"},{"name":"SwitchboardV2"},{"name":"RaydiumCLMM"}]}},{"name":"OrderState","type":{"kind":"enum","variants":[{"name":"Valid"},{"name":"Invalid"},{"name":"Skipped"}]}},{"name":"BookSideOrderTree","type":{"kind":"enum","variants":[{"name":"Fixed"},{"name":"OraclePegged"}]}},{"name":"EventType","type":{"kind":"enum","variants":[{"name":"Fill"},{"name":"Out"}]}},{"name":"NodeTag","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"InnerNode"},{"name":"LeafNode"},{"name":"FreeNode"},{"name":"LastFreeNode"}]}},{"name":"PlaceOrderType","type":{"kind":"enum","variants":[{"name":"Limit"},{"name":"ImmediateOrCancel"},{"name":"PostOnly"},{"name":"Market"},{"name":"PostOnlySlide"}]}},{"name":"PostOrderType","type":{"kind":"enum","variants":[{"name":"Limit"},{"name":"PostOnly"},{"name":"PostOnlySlide"}]}},{"name":"SelfTradeBehavior","docs":["Self trade behavior controls how taker orders interact with resting limit orders of the same account.","This setting has no influence on placing a resting or oracle pegged limit order that does not match","immediately, instead it's the responsibility of the user to correctly configure his taker orders."],"type":{"kind":"enum","variants":[{"name":"DecrementTake"},{"name":"CancelProvide"},{"name":"AbortTransaction"}]}},{"name":"Side","type":{"kind":"enum","variants":[{"name":"Bid"},{"name":"Ask"}]}},{"name":"SideAndOrderTree","docs":["SideAndOrderTree is a storage optimization, so we don't need two bytes for the data"],"type":{"kind":"enum","variants":[{"name":"BidFixed"},{"name":"AskFixed"},{"name":"BidOraclePegged"},{"name":"AskOraclePegged"}]}},{"name":"OrderParams","type":{"kind":"enum","variants":[{"name":"Market"},{"name":"ImmediateOrCancel","fields":[{"name":"price_lots","type":"i64"}]},{"name":"Fixed","fields":[{"name":"price_lots","type":"i64"},{"name":"order_type","type":{"defined":"PostOrderType"}}]},{"name":"OraclePegged","fields":[{"name":"price_offset_lots","type":"i64"},{"name":"order_type","type":{"defined":"PostOrderType"}},{"name":"peg_limit","type":"i64"}]}]}},{"name":"OrderTreeType","type":{"kind":"enum","variants":[{"name":"Bids"},{"name":"Asks"}]}}],"events":[{"name":"DepositLog","fields":[{"name":"openOrdersAccount","type":"publicKey","index":false},{"name":"signer","type":"publicKey","index":false},{"name":"baseAmount","type":"u64","index":false},{"name":"quoteAmount","type":"u64","index":false}]},{"name":"FillLog","fields":[{"name":"market","type":"publicKey","index":false},{"name":"takerSide","type":"u8","index":false},{"name":"makerSlot","type":"u8","index":false},{"name":"makerOut","type":"bool","index":false},{"name":"timestamp","type":"u64","index":false},{"name":"seqNum","type":"u64","index":false},{"name":"maker","type":"publicKey","index":false},{"name":"makerClientOrderId","type":"u64","index":false},{"name":"makerFee","type":"u64","index":false},{"name":"makerTimestamp","type":"u64","index":false},{"name":"taker","type":"publicKey","index":false},{"name":"takerClientOrderId","type":"u64","index":false},{"name":"takerFeeCeil","type":"u64","index":false},{"name":"price","type":"i64","index":false},{"name":"quantity","type":"i64","index":false}]},{"name":"MarketMetaDataLog","fields":[{"name":"market","type":"publicKey","index":false},{"name":"name","type":"string","index":false},{"name":"baseMint","type":"publicKey","index":false},{"name":"quoteMint","type":"publicKey","index":false},{"name":"baseDecimals","type":"u8","index":false},{"name":"quoteDecimals","type":"u8","index":false},{"name":"baseLotSize","type":"i64","index":false},{"name":"quoteLotSize","type":"i64","index":false}]},{"name":"TotalOrderFillEvent","fields":[{"name":"side","type":"u8","index":false},{"name":"taker","type":"publicKey","index":false},{"name":"totalQuantityPaid","type":"u64","index":false},{"name":"totalQuantityReceived","type":"u64","index":false},{"name":"fees","type":"u64","index":false}]},{"name":"SetDelegateLog","fields":[{"name":"openOrdersAccount","type":"publicKey","index":false},{"name":"delegate","type":{"option":"publicKey"},"index":false}]},{"name":"SettleFundsLog","fields":[{"name":"openOrdersAccount","type":"publicKey","index":false},{"name":"baseNative","type":"u64","index":false},{"name":"quoteNative","type":"u64","index":false},{"name":"referrerRebate","type":"u64","index":false},{"name":"referrer","type":{"option":"publicKey"},"index":false}]},{"name":"SweepFeesLog","fields":[{"name":"market","type":"publicKey","index":false},{"name":"amount","type":"u64","index":false},{"name":"receiver","type":"publicKey","index":false}]},{"name":"OpenOrdersPositionLog","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"openOrdersAccountNum","type":"u32","index":false},{"name":"market","type":"publicKey","index":false},{"name":"bidsBaseLots","type":"i64","index":false},{"name":"bidsQuoteLots","type":"i64","index":false},{"name":"asksBaseLots","type":"i64","index":false},{"name":"baseFreeNative","type":"u64","index":false},{"name":"quoteFreeNative","type":"u64","index":false},{"name":"lockedMakerFees","type":"u64","index":false},{"name":"referrerRebatesAvailable","type":"u64","index":false},{"name":"makerVolume","type":"u128","index":false},{"name":"takerVolume","type":"u128","index":false}]}],"errors":[{"code":6000,"name":"SomeError","msg":""},{"code":6001,"name":"InvalidInputNameLength","msg":"Name lenght above limit"},{"code":6002,"name":"InvalidInputMarketExpired","msg":"Market cannot be created as expired"},{"code":6003,"name":"InvalidInputMarketFees","msg":"Taker fees should be positive and if maker fees are negative, greater or equal to their abs value"},{"code":6004,"name":"InvalidInputLots","msg":"Lots cannot be negative"},{"code":6005,"name":"InvalidInputLotsSize","msg":"Lots size above market limits"},{"code":6006,"name":"InvalidInputOrdersAmounts","msg":"Input amounts above limits"},{"code":6007,"name":"InvalidInputCancelSize","msg":"Price lots should be greater than zero"},{"code":6008,"name":"InvalidInputPriceLots","msg":"Expected cancel size should be greater than zero"},{"code":6009,"name":"InvalidInputPegLimit","msg":"Peg limit should be greater than zero"},{"code":6010,"name":"InvalidInputOrderType","msg":"The order type is invalid. A taker order must be Market or ImmediateOrCancel"},{"code":6011,"name":"InvalidInputOrderId","msg":"Order id cannot be zero"},{"code":6012,"name":"InvalidInputHeapSlots","msg":"Slot above heap limit"},{"code":6013,"name":"InvalidOracleTypes","msg":"Cannot combine two oracles of different providers"},{"code":6014,"name":"InvalidSecondOracle","msg":"Cannot configure secondary oracle without primary"},{"code":6015,"name":"NoCloseMarketAdmin","msg":"This market does not have a `close_market_admin` and thus cannot be closed."},{"code":6016,"name":"InvalidCloseMarketAdmin","msg":"The signer of this transaction is not this market's `close_market_admin`."},{"code":6017,"name":"InvalidOpenOrdersAdmin","msg":"The `open_orders_admin` required by this market to sign all instructions that creates orders is missing or is not valid"},{"code":6018,"name":"InvalidConsumeEventsAdmin","msg":"The `consume_events_admin` required by this market to sign all instructions that consume events is missing or is not valid"},{"code":6019,"name":"InvalidMarketVault","msg":"Provided `market_vault` is invalid"},{"code":6020,"name":"IndexerActiveOO","msg":"Cannot be closed due to the existence of open orders accounts"},{"code":6021,"name":"OraclePegInvalidOracleState","msg":"Cannot place a peg order due to invalid oracle state"},{"code":6022,"name":"UnknownOracleType","msg":"oracle type cannot be determined"},{"code":6023,"name":"OracleConfidence","msg":"an oracle does not reach the confidence threshold"},{"code":6024,"name":"OracleStale","msg":"an oracle is stale"},{"code":6025,"name":"OrderIdNotFound","msg":"Order id not found on the orderbook"},{"code":6026,"name":"EventHeapContainsElements","msg":"Event heap contains elements and market can't be closed"},{"code":6027,"name":"InvalidOrderPostIOC","msg":"ImmediateOrCancel is not a PostOrderType"},{"code":6028,"name":"InvalidOrderPostMarket","msg":"Market is not a PostOrderType"},{"code":6029,"name":"WouldSelfTrade","msg":"would self trade"},{"code":6030,"name":"MarketHasExpired","msg":"The Market has already expired."},{"code":6031,"name":"InvalidPriceLots","msg":"Price lots should be greater than zero"},{"code":6032,"name":"InvalidOraclePrice","msg":"Oracle price above market limits"},{"code":6033,"name":"MarketHasNotExpired","msg":"The Market has not expired yet."},{"code":6034,"name":"NoOwnerOrDelegate","msg":"No correct owner or delegate."},{"code":6035,"name":"NoOwner","msg":"No correct owner"},{"code":6036,"name":"OpenOrdersFull","msg":"No free order index in open orders account"},{"code":6037,"name":"BookContainsElements","msg":"Book contains elements"},{"code":6038,"name":"OpenOrdersOrderNotFound","msg":"Could not find order in user account"},{"code":6039,"name":"InvalidPostAmount","msg":"Amount to post above book limits"},{"code":6040,"name":"DisabledOraclePeg","msg":"Oracle peg orders are not enabled for this market"},{"code":6041,"name":"NonEmptyMarket","msg":"Cannot close a non-empty market"},{"code":6042,"name":"NonEmptyOpenOrdersPosition","msg":"Cannot close a non-empty open orders account"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/orca_wavebreak.waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF.json b/migration/khadhroony-bot2-reference/idls/orca_wavebreak.waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF.json new file mode 100644 index 0000000..2c861b2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/orca_wavebreak.waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF.json @@ -0,0 +1 @@ +{"version":"1.1.5","name":"orca_wavebreak_program","instructions":[{"name":"PermissionConsumeTopLevel","accounts":[{"name":"consumer","isMut":true,"isSigner":true,"docs":["The consumer of the permission"]},{"name":"permissionConfig","isMut":false,"isSigner":false,"docs":["The permission config account"]},{"name":"consumedPermission","isMut":true,"isSigner":false,"docs":["The permission program account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"instructions","isMut":false,"isSigner":false,"docs":["Sysvar instructions"]}],"args":[{"name":"permissionMessage","type":{"defined":"PermissionMessage"}},{"name":"permissionSignature","type":{"defined":"PermissionSignature"}}],"discriminant":{"type":"u8","value":0}},{"name":"PermissionConsumeCpi","accounts":[{"name":"consumer","isMut":true,"isSigner":true,"docs":["The consumer of the permission"]},{"name":"consumerProgramAuthority","isMut":false,"isSigner":true,"docs":["Any pda of the consumer program"]},{"name":"permissionConfig","isMut":false,"isSigner":false,"docs":["The permission config account"]},{"name":"consumedPermission","isMut":true,"isSigner":false,"docs":["The permission program account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]}],"args":[{"name":"permissionMessage","type":{"defined":"PermissionMessage"}},{"name":"permissionSignature","type":{"defined":"PermissionSignature"}},{"name":"consumerProgramAuthoritySeeds","type":{"vec":"bytes"}}],"discriminant":{"type":"u8","value":1}},{"name":"PermissionConfigInitialize","accounts":[{"name":"authority","isMut":true,"isSigner":true,"docs":["The authority that is allowed to initialize a permission config"]},{"name":"permissionConfig","isMut":true,"isSigner":false,"docs":["The permission config account"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]}],"args":[{"name":"permissionAuthority","type":{"defined":"PermissionSigner"}},{"name":"consumerProgram","type":"publicKey"}],"discriminant":{"type":"u8","value":2}},{"name":"PermissionConfigUpdate","accounts":[{"name":"authority","isMut":true,"isSigner":true,"docs":["The authority that is allowed to update a permission config"]},{"name":"permissionConfig","isMut":true,"isSigner":false,"docs":["The permission config account"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]}],"args":[{"name":"update","type":{"defined":"PermissionConfigUpdateType"}}],"discriminant":{"type":"u8","value":3}},{"name":"PermissionConfigClose","accounts":[{"name":"authority","isMut":true,"isSigner":true,"docs":["The authority that is allowed to close a permission config"]},{"name":"permissionConfig","isMut":true,"isSigner":false,"docs":["The permission config account"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]}],"args":[],"discriminant":{"type":"u8","value":4}},{"name":"PermissionRevoke","accounts":[{"name":"funder","isMut":true,"isSigner":true,"docs":["The funder of the consumed permission PDA"]},{"name":"permissionConfig","isMut":false,"isSigner":false,"docs":["The permission config account"]},{"name":"consumedPermission","isMut":true,"isSigner":false,"docs":["The consumed permission account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]}],"args":[{"name":"permissionMessage","type":{"defined":"PermissionMessage"}},{"name":"permissionSignature","type":{"defined":"PermissionSignature"}}],"discriminant":{"type":"u8","value":5}},{"name":"PermissionRefund","accounts":[{"name":"consumedPermission","isMut":true,"isSigner":false,"docs":["The consumed permission account"]},{"name":"refundDestination","isMut":true,"isSigner":false,"docs":["The destination to refund the rent to"]}],"args":[],"discriminant":{"type":"u8","value":6}},{"name":"ReservedPermissionA","accounts":[],"args":[],"discriminant":{"type":"u8","value":7}},{"name":"TokenBuyExactIn","accounts":[{"name":"buyer","isMut":true,"isSigner":true,"docs":["The buyer of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"baseMint","isMut":true,"isSigner":false,"docs":["The base token mint account"]},{"name":"baseAta","isMut":true,"isSigner":false,"docs":["The base token ata for the buyer"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"quoteAta","isMut":true,"isSigner":false,"docs":["The quote token ata for the buyer"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the base token"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the quote token"]}],"args":[{"name":"amountIn","type":"u64"},{"name":"allowPartialFill","type":"bool"},{"name":"priceThreshold","type":{"option":{"tuple":["u64","u64"]}}}],"discriminant":{"type":"u8","value":8}},{"name":"TokenBuyExactOut","accounts":[{"name":"buyer","isMut":true,"isSigner":true,"docs":["The buyer of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"baseMint","isMut":true,"isSigner":false,"docs":["The base token mint account"]},{"name":"baseAta","isMut":true,"isSigner":false,"docs":["The base token ata for the buyer"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"quoteAta","isMut":true,"isSigner":false,"docs":["The quote token ata for the buyer"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the base token"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the quote token"]}],"args":[{"name":"amountOut","type":"u64"},{"name":"allowPartialFill","type":"bool"},{"name":"priceThreshold","type":{"option":{"tuple":["u64","u64"]}}}],"discriminant":{"type":"u8","value":9}},{"name":"TokenSellExactIn","accounts":[{"name":"seller","isMut":true,"isSigner":true,"docs":["The seller of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"baseMint","isMut":true,"isSigner":false,"docs":["The base token mint account"]},{"name":"baseAta","isMut":true,"isSigner":false,"docs":["The base token ata for the seller"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"quoteAta","isMut":true,"isSigner":false,"docs":["The quote token ata for the seller"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the base token"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the quote token"]}],"args":[{"name":"amountIn","type":"u64"},{"name":"allowPartialFill","type":"bool"},{"name":"priceThreshold","type":{"option":{"tuple":["u64","u64"]}}}],"discriminant":{"type":"u8","value":10}},{"name":"TokenSellExactOut","accounts":[{"name":"seller","isMut":true,"isSigner":true,"docs":["The seller of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"baseMint","isMut":true,"isSigner":false,"docs":["The base token mint account"]},{"name":"baseAta","isMut":true,"isSigner":false,"docs":["The base token ata for the seller"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"quoteAta","isMut":true,"isSigner":false,"docs":["The quote token ata for the seller"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the base token"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the quote token"]}],"args":[{"name":"amountOut","type":"u64"},{"name":"allowPartialFill","type":"bool"},{"name":"priceThreshold","type":{"option":{"tuple":["u64","u64"]}}}],"discriminant":{"type":"u8","value":11}},{"name":"TokenRefund","accounts":[{"name":"signer","isMut":true,"isSigner":true,"docs":["The signer of the transaction"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"signerQuoteAta","isMut":true,"isSigner":false,"docs":["The signer's quote token ata account"]},{"name":"baseMint","isMut":true,"isSigner":false,"docs":["The base token mint account"]},{"name":"signerBaseAta","isMut":true,"isSigner":false,"docs":["The signer's base token ata account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The base token program"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The quote token program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]}],"args":[],"discriminant":{"type":"u8","value":12}},{"name":"ReservedTokenY","accounts":[],"args":[],"discriminant":{"type":"u8","value":13}},{"name":"ReservedTokenZ","accounts":[],"args":[],"discriminant":{"type":"u8","value":14}},{"name":"ReservedTokenA","accounts":[],"args":[],"discriminant":{"type":"u8","value":15}},{"name":"AuthorityConfigInitialize","accounts":[{"name":"authority","isMut":true,"isSigner":true,"docs":["The authority of the config"]},{"name":"authorityConfig","isMut":true,"isSigner":false,"docs":["The authority config account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]}],"args":[],"discriminant":{"type":"u8","value":16}},{"name":"AuthorityConfigGrant","accounts":[{"name":"authority","isMut":false,"isSigner":true,"docs":["The authority that is allowed to update the config"]},{"name":"authorityConfig","isMut":true,"isSigner":false,"docs":["The authority config account"]}],"args":[{"name":"account","type":"publicKey"},{"name":"privileges","type":{"vec":{"defined":"Privilege"}}}],"discriminant":{"type":"u8","value":17}},{"name":"AuthorityConfigRevoke","accounts":[{"name":"authority","isMut":false,"isSigner":true,"docs":["The authority that is allowed to update the config"]},{"name":"authorityConfig","isMut":true,"isSigner":false,"docs":["The authority config account"]}],"args":[{"name":"account","type":"publicKey"},{"name":"privileges","type":{"vec":{"defined":"Privilege"}}}],"discriminant":{"type":"u8","value":18}},{"name":"ReservedAuthorityConfigY","accounts":[],"args":[],"discriminant":{"type":"u8","value":19}},{"name":"ReservedAuthorityConfigZ","accounts":[],"args":[],"discriminant":{"type":"u8","value":20}},{"name":"ReservedAuthorityConfigA","accounts":[],"args":[],"discriminant":{"type":"u8","value":21}},{"name":"ReservedAuthorityConfigB","accounts":[],"args":[],"discriminant":{"type":"u8","value":22}},{"name":"ReservedAuthorityConfigC","accounts":[],"args":[],"discriminant":{"type":"u8","value":23}},{"name":"MintConfigInitialize","accounts":[{"name":"authority","isMut":true,"isSigner":true,"docs":["The authority that is allowed to initialize a mint config"]},{"name":"mintConfig","isMut":true,"isSigner":false,"docs":["The mint config account"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]}],"args":[{"name":"tokenMint","type":"publicKey"},{"name":"instructionDiscriminator","type":"u8"}],"discriminant":{"type":"u8","value":24}},{"name":"MintConfigClose","accounts":[{"name":"authority","isMut":true,"isSigner":true,"docs":["The authority that is allowed to close a mint config"]},{"name":"mintConfig","isMut":true,"isSigner":false,"docs":["The mint config account"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]}],"args":[],"discriminant":{"type":"u8","value":25}},{"name":"MintConfigUpdate","accounts":[{"name":"authority","isMut":false,"isSigner":true,"docs":["The authority that is allowed to update a mint config"]},{"name":"mintConfig","isMut":true,"isSigner":false,"docs":["The mint config account"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]}],"args":[{"name":"update","type":{"defined":"MintConfigUpdateType"}}],"discriminant":{"type":"u8","value":26}},{"name":"ReservedMintConfigY","accounts":[],"args":[],"discriminant":{"type":"u8","value":27}},{"name":"ReservedMintConfigZ","accounts":[],"args":[],"discriminant":{"type":"u8","value":28}},{"name":"ReservedMintConfigA","accounts":[],"args":[],"discriminant":{"type":"u8","value":29}},{"name":"ReservedMintConfigB","accounts":[],"args":[],"discriminant":{"type":"u8","value":30}},{"name":"ReservedMintConfigC","accounts":[],"args":[],"discriminant":{"type":"u8","value":31}},{"name":"GraduateWhirlpool","accounts":[{"name":"signer","isMut":true,"isSigner":true,"docs":["The signer of the transaction"]},{"name":"lpAuthority","isMut":false,"isSigner":false,"docs":["The lp authority of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"signerQuoteAta","isMut":true,"isSigner":false,"docs":["The quote token ata account"]},{"name":"lpAuthorityQuoteAta","isMut":true,"isSigner":false,"docs":["The lp authority quote token ata account"]},{"name":"whirlpoolQuoteVault","isMut":true,"isSigner":false,"docs":["The whirlpool quote vault account"]},{"name":"baseMint","isMut":true,"isSigner":false,"docs":["The base token mint account"]},{"name":"baseVault","isMut":true,"isSigner":false,"docs":["The config base vault account"]},{"name":"lpAuthorityBaseAta","isMut":true,"isSigner":false,"docs":["The lp authority base token ata account"]},{"name":"whirlpoolBaseVault","isMut":true,"isSigner":false,"docs":["The whirlpool base vault account"]},{"name":"whirlpoolConfig","isMut":false,"isSigner":false,"docs":["The whirlpool config account"]},{"name":"feeTier","isMut":false,"isSigner":false,"docs":["The fee tier account"]},{"name":"whirlpool","isMut":true,"isSigner":false,"docs":["The whirlpool account"]},{"name":"oracle","isMut":true,"isSigner":false,"docs":["The whirlpool oracle account"]},{"name":"position","isMut":true,"isSigner":false,"docs":["The position account"]},{"name":"positionMint","isMut":true,"isSigner":false,"docs":["The position mint account"]},{"name":"positionTokenAccount","isMut":true,"isSigner":false,"docs":["The position token account for the bonding curve"]},{"name":"lpAuthorityTokenAccount","isMut":true,"isSigner":false,"docs":["The position token account for the lp authority"]},{"name":"lowerTickArray","isMut":true,"isSigner":false,"docs":["The lower tick array account"]},{"name":"upperTickArray","isMut":true,"isSigner":false,"docs":["The upper tick array account"]},{"name":"quoteTokenBadge","isMut":false,"isSigner":false,"docs":["The quote token badge account"]},{"name":"baseTokenBadge","isMut":false,"isSigner":false,"docs":["The base token badge account"]},{"name":"whirlpoolInitAuthority","isMut":false,"isSigner":false,"docs":["The whirlpool init authority account"]},{"name":"whirlpoolUpdateAuthority","isMut":false,"isSigner":false,"docs":["The whirlpool update authority account"]},{"name":"lockConfig","isMut":true,"isSigner":false,"docs":["The lock config account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["The system program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The ata program"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The quote token program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The base token program"]},{"name":"token22Program","isMut":false,"isSigner":false,"docs":["The token 22 program"]},{"name":"memoProgram","isMut":false,"isSigner":false,"docs":["The memo program"]},{"name":"whirlpoolProgram","isMut":false,"isSigner":false,"docs":["The whirlpool program"]},{"name":"rent","isMut":false,"isSigner":false,"docs":["The rent sysvar"]}],"args":[],"discriminant":{"type":"u8","value":32}},{"name":"GraduateManual","accounts":[{"name":"signer","isMut":true,"isSigner":true,"docs":["The signer of the transaction"]},{"name":"destination","isMut":false,"isSigner":false,"docs":["The destination of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote vault account"]},{"name":"signerQuoteAta","isMut":true,"isSigner":false,"docs":["The quote token ata account"]},{"name":"destinationQuoteAta","isMut":true,"isSigner":false,"docs":["The destination quote ata account"]},{"name":"baseMint","isMut":true,"isSigner":false,"docs":["The base mint account"]},{"name":"destinationBaseAta","isMut":true,"isSigner":false,"docs":["The destination base ata account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["The system program account"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The ata program account"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The quote token program account"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The base token program account"]}],"args":[],"discriminant":{"type":"u8","value":33}},{"name":"ReservedGraduateX","accounts":[],"args":[],"discriminant":{"type":"u8","value":34}},{"name":"ReservedGraduateY","accounts":[],"args":[],"discriminant":{"type":"u8","value":35}},{"name":"ReservedGraduateZ","accounts":[],"args":[],"discriminant":{"type":"u8","value":36}},{"name":"ReservedGraduateA","accounts":[],"args":[],"discriminant":{"type":"u8","value":37}},{"name":"ReservedGraduateB","accounts":[],"args":[],"discriminant":{"type":"u8","value":38}},{"name":"ReservedGraduateC","accounts":[],"args":[],"discriminant":{"type":"u8","value":39}},{"name":"CreateLockedlaunch","accounts":[{"name":"creator","isMut":true,"isSigner":true,"docs":["The creator of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"baseMint","isMut":true,"isSigner":true,"docs":["The token mint account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"mintConfig","isMut":false,"isSigner":false,"docs":["The base token mint config account"]},{"name":"metadata","isMut":true,"isSigner":false,"docs":["The metadata account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the new token"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the quote token"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]},{"name":"metaplexProgram","isMut":false,"isSigner":false,"docs":["The metaplex metadata program"]}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}],"discriminant":{"type":"u8","value":40}},{"name":"CreateLaunch","accounts":[{"name":"creator","isMut":true,"isSigner":true,"docs":["The creator of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"baseMint","isMut":true,"isSigner":true,"docs":["The token mint account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"mintConfig","isMut":false,"isSigner":false,"docs":["The base token mint config account"]},{"name":"metadata","isMut":true,"isSigner":false,"docs":["The metadata account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the new token"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the quote token"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]},{"name":"metaplexProgram","isMut":false,"isSigner":false,"docs":["The metaplex metadata program"]}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"startPrice","type":{"option":"u128"}},{"name":"endPrice","type":{"option":"u128"}},{"name":"controlPoints","type":{"option":{"array":["u16",4]}}},{"name":"graduationTarget","type":{"option":"u64"}},{"name":"graduationMethods","type":{"option":{"array":[{"defined":"GraduationMethod"},8]}}},{"name":"launchTime","type":{"option":"i64"}},{"name":"graduationTime","type":{"option":"i64"}},{"name":"baseAllocationBps","type":{"option":"u16"}}],"discriminant":{"type":"u8","value":41}},{"name":"CreatePresale","accounts":[{"name":"creator","isMut":true,"isSigner":true,"docs":["The creator of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"baseMint","isMut":true,"isSigner":true,"docs":["The token mint account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"mintConfig","isMut":false,"isSigner":false,"docs":["The base token mint config account"]},{"name":"metadata","isMut":true,"isSigner":false,"docs":["The metadata account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the new token"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the quote token"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]},{"name":"metaplexProgram","isMut":false,"isSigner":false,"docs":["The metaplex metadata program"]}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"tokenPrice","type":{"option":"u128"}},{"name":"graduationTarget","type":{"option":"u64"}},{"name":"graduationMethods","type":{"option":{"array":[{"defined":"GraduationMethod"},8]}}},{"name":"launchTime","type":{"option":"i64"}},{"name":"graduationTime","type":{"option":"i64"}},{"name":"minReserveBps","type":{"option":"u16"}},{"name":"baseAllocationBps","type":{"option":"u16"}}],"discriminant":{"type":"u8","value":42}},{"name":"ReservedCreateY","accounts":[],"args":[],"discriminant":{"type":"u8","value":43}},{"name":"ReservedCreateZ","accounts":[],"args":[],"discriminant":{"type":"u8","value":44}},{"name":"ReservedCreateA","accounts":[],"args":[],"discriminant":{"type":"u8","value":45}},{"name":"ReservedCreateB","accounts":[],"args":[],"discriminant":{"type":"u8","value":46}},{"name":"ReservedCreateC","accounts":[],"args":[],"discriminant":{"type":"u8","value":47}},{"name":"BondingCurveInitialize","accounts":[{"name":"authority","isMut":true,"isSigner":true,"docs":["The authority that is allowed to initialize a bonding curve"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"baseMint","isMut":false,"isSigner":false,"docs":["The token mint account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the new token"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The token program for the quote token"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]}],"args":[{"name":"startPrice","type":"u128"},{"name":"endPrice","type":"u128"},{"name":"controlPoints","type":{"array":["u16",4]}},{"name":"creator","type":"publicKey"},{"name":"graduationMethods","type":{"array":[{"defined":"GraduationMethod"},8]}},{"name":"swapFeeBps","type":"u16"},{"name":"quoteFeeBps","type":"u16"},{"name":"baseFeeBps","type":"u16"},{"name":"launchTime","type":"i64"},{"name":"creatorReward","type":"u64"},{"name":"graduationReward","type":"u64"},{"name":"graduationTarget","type":"u64"},{"name":"graduationTime","type":"i64"},{"name":"minReserveBps","type":"u16"},{"name":"buyRequiresPermission","type":"bool"},{"name":"buyPermissionBitmap","type":{"array":["u8",32]}},{"name":"sellRequiresPermission","type":"bool"},{"name":"sellPermissionBitmap","type":{"array":["u8",32]}},{"name":"maxBuyAmount","type":"u64"},{"name":"maxSellAmount","type":"u64"},{"name":"retainMintAuthority","type":"bool"},{"name":"baseAllocationBps","type":"u16"}],"discriminant":{"type":"u8","value":48}},{"name":"BondingCurveCollectFees","accounts":[{"name":"signer","isMut":true,"isSigner":true,"docs":["The signer of the transaction"]},{"name":"feeAuthority","isMut":false,"isSigner":false,"docs":["The fee authority of the program"]},{"name":"feeAuthorityAta","isMut":true,"isSigner":false,"docs":["The fee authority associated token account"]},{"name":"bondingCurve","isMut":false,"isSigner":false,"docs":["The bonding curve account"]},{"name":"baseMint","isMut":false,"isSigner":false,"docs":["The base token mint account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The quote token program"]}],"args":[],"discriminant":{"type":"u8","value":49}},{"name":"BondingCurveGraduate","accounts":[{"name":"signer","isMut":true,"isSigner":true,"docs":["The signer of the transaction"]},{"name":"creator","isMut":true,"isSigner":false,"docs":["The creator of the token"]},{"name":"feeAuthority","isMut":true,"isSigner":false,"docs":["The fee authority of the token"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"signerQuoteAta","isMut":true,"isSigner":false,"docs":["The signer's quote token ata account"]},{"name":"creatorQuoteAta","isMut":true,"isSigner":false,"docs":["The creator's quote token ata account"]},{"name":"feeAuthorityQuoteAta","isMut":true,"isSigner":false,"docs":["The fee authority's quote token ata account"]},{"name":"baseMint","isMut":true,"isSigner":false,"docs":["The base token mint account"]},{"name":"creatorBaseAta","isMut":true,"isSigner":false,"docs":["The creator's base token ata account"]},{"name":"feeAuthorityBaseAta","isMut":true,"isSigner":false,"docs":["The fee authority's base token ata account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["System program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The quote token program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The base token program"]}],"args":[],"discriminant":{"type":"u8","value":50}},{"name":"BondingCurveClose","accounts":[{"name":"authority","isMut":false,"isSigner":true,"docs":["The authority that is allowed to close a bonding curve"]},{"name":"creator","isMut":true,"isSigner":false,"docs":["The creator of the bonding curve"]},{"name":"feeAuthority","isMut":false,"isSigner":false,"docs":["The fee authority of the bonding curve"]},{"name":"bondingCurve","isMut":true,"isSigner":false,"docs":["The bonding curve account"]},{"name":"quoteMint","isMut":false,"isSigner":false,"docs":["The quote token mint account"]},{"name":"quoteVault","isMut":true,"isSigner":false,"docs":["The quote token vault"]},{"name":"feeAuthorityQuoteAta","isMut":true,"isSigner":false,"docs":["The fee authority's quote token ata account"]},{"name":"baseMint","isMut":false,"isSigner":false,"docs":["The base token mint account"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]},{"name":"quoteTokenProgram","isMut":false,"isSigner":false,"docs":["The quote token program"]},{"name":"baseTokenProgram","isMut":false,"isSigner":false,"docs":["The base token program"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["The system program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The associated token account program"]}],"args":[],"discriminant":{"type":"u8","value":51}},{"name":"ReservedBondingCurveX","accounts":[],"args":[],"discriminant":{"type":"u8","value":52}},{"name":"ReservedBondingCurveY","accounts":[],"args":[],"discriminant":{"type":"u8","value":53}},{"name":"ReservedBondingCurveZ","accounts":[],"args":[],"discriminant":{"type":"u8","value":54}},{"name":"ReservedBondingCurveA","accounts":[],"args":[],"discriminant":{"type":"u8","value":55}},{"name":"LpHarvest","accounts":[{"name":"lpAuthority","isMut":true,"isSigner":true,"docs":["The authority of the lp escrow account"]},{"name":"lpEscrow","isMut":false,"isSigner":false,"docs":["The LP escrow account"]},{"name":"whirlpool","isMut":true,"isSigner":false,"docs":["The whirlpool account"]},{"name":"position","isMut":true,"isSigner":false,"docs":["The position account"]},{"name":"positionTokenAccount","isMut":false,"isSigner":false,"docs":["The position token account"]},{"name":"lowerTickArray","isMut":false,"isSigner":false,"docs":["The lower tick array account"]},{"name":"upperTickArray","isMut":false,"isSigner":false,"docs":["The upper tick array account"]},{"name":"tokenAMint","isMut":true,"isSigner":false,"docs":["The token mint a account"]},{"name":"tokenBMint","isMut":true,"isSigner":false,"docs":["The token mint b account"]},{"name":"tokenAAta","isMut":true,"isSigner":false,"docs":["The token owner a account"]},{"name":"tokenBAta","isMut":true,"isSigner":false,"docs":["The token owner b account"]},{"name":"tokenAVault","isMut":true,"isSigner":false,"docs":["The token vault a account"]},{"name":"tokenBVault","isMut":true,"isSigner":false,"docs":["The token vault b account"]},{"name":"reward1Mint","isMut":false,"isSigner":false,"docs":["The reward mint 1 account"]},{"name":"reward2Mint","isMut":false,"isSigner":false,"docs":["The reward mint 2 account"]},{"name":"reward3Mint","isMut":false,"isSigner":false,"docs":["The reward mint 3 account"]},{"name":"reward1Ata","isMut":true,"isSigner":false,"docs":["The reward owner 1 account"]},{"name":"reward2Ata","isMut":true,"isSigner":false,"docs":["The reward owner 2 account"]},{"name":"reward3Ata","isMut":true,"isSigner":false,"docs":["The reward owner 3 account"]},{"name":"reward1Vault","isMut":true,"isSigner":false,"docs":["The reward vault 1 account"]},{"name":"reward2Vault","isMut":true,"isSigner":false,"docs":["The reward vault 2 account"]},{"name":"reward3Vault","isMut":true,"isSigner":false,"docs":["The reward vault 3 account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["The system program"]},{"name":"tokenAProgram","isMut":false,"isSigner":false,"docs":["The token a program"]},{"name":"tokenBProgram","isMut":false,"isSigner":false,"docs":["The token b program"]},{"name":"reward1TokenProgram","isMut":false,"isSigner":false,"docs":["The token reward 1 program"]},{"name":"reward2TokenProgram","isMut":false,"isSigner":false,"docs":["The token reward 2 program"]},{"name":"reward3TokenProgram","isMut":false,"isSigner":false,"docs":["The token reward 3 program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The ata program"]},{"name":"memoProgram","isMut":false,"isSigner":false,"docs":["The memo program"]},{"name":"whirlpoolProgram","isMut":false,"isSigner":false,"docs":["The whirlpool program"]}],"args":[],"discriminant":{"type":"u8","value":56}},{"name":"LpTransfer","accounts":[{"name":"lpAuthority","isMut":true,"isSigner":true,"docs":["The authority of the lp escrow account"]},{"name":"newLpAuthority","isMut":false,"isSigner":false,"docs":["The new authority of the lp escrow account"]},{"name":"lpEscrow","isMut":false,"isSigner":false,"docs":["The lp escrow account"]},{"name":"newLpEscrow","isMut":false,"isSigner":false,"docs":["The new lp escrow account"]},{"name":"positionMint","isMut":false,"isSigner":false,"docs":["The position mint account"]},{"name":"position","isMut":false,"isSigner":false,"docs":["The position account"]},{"name":"positionTokenAccount","isMut":true,"isSigner":false,"docs":["The position token account"]},{"name":"newPositionTokenAccount","isMut":true,"isSigner":false,"docs":["The new position token account"]},{"name":"lockConfig","isMut":true,"isSigner":false,"docs":["The lock config account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["The system program"]},{"name":"token22Program","isMut":false,"isSigner":false,"docs":["The token 22 program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The ata program"]},{"name":"whirlpoolProgram","isMut":false,"isSigner":false,"docs":["The whirlpool program"]}],"args":[],"discriminant":{"type":"u8","value":57}},{"name":"LpTakeover","accounts":[{"name":"authority","isMut":true,"isSigner":true,"docs":["The authority account"]},{"name":"authorityConfig","isMut":false,"isSigner":false,"docs":["The authority config account"]},{"name":"lpAuthority","isMut":false,"isSigner":false,"docs":["The authority of the lp escrow account"]},{"name":"newLpAuthority","isMut":false,"isSigner":false,"docs":["The new authority of the lp escrow account"]},{"name":"lpEscrow","isMut":false,"isSigner":false,"docs":["The lp escrow account"]},{"name":"newLpEscrow","isMut":false,"isSigner":false,"docs":["The new lp escrow account"]},{"name":"positionMint","isMut":false,"isSigner":false,"docs":["The position mint account"]},{"name":"position","isMut":false,"isSigner":false,"docs":["The position account"]},{"name":"positionTokenAccount","isMut":true,"isSigner":false,"docs":["The position token account"]},{"name":"newPositionTokenAccount","isMut":true,"isSigner":false,"docs":["The new position token account"]},{"name":"lockConfig","isMut":true,"isSigner":false,"docs":["The lock config account"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["The system program"]},{"name":"token22Program","isMut":false,"isSigner":false,"docs":["The token 22 program"]},{"name":"ataProgram","isMut":false,"isSigner":false,"docs":["The ata program"]},{"name":"whirlpoolProgram","isMut":false,"isSigner":false,"docs":["The whirlpool program"]}],"args":[],"discriminant":{"type":"u8","value":58}},{"name":"ReservedLpX","accounts":[],"args":[],"discriminant":{"type":"u8","value":59}},{"name":"ReservedLpY","accounts":[],"args":[],"discriminant":{"type":"u8","value":60}},{"name":"ReservedLpZ","accounts":[],"args":[],"discriminant":{"type":"u8","value":61}},{"name":"ReservedLpA","accounts":[],"args":[],"discriminant":{"type":"u8","value":62}},{"name":"ReservedLpB","accounts":[],"args":[],"discriminant":{"type":"u8","value":63}}],"accounts":[{"name":"AuthorityConfig","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"defined":"AccountDiscriminator"}},{"name":"authorities","type":{"array":[{"defined":"ProgramAuthority"},64]}}]}},{"name":"BondingCurve","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"defined":"AccountDiscriminator"}},{"name":"baseMint","type":"publicKey"},{"name":"quoteMint","type":"publicKey"},{"name":"creator","type":"publicKey"},{"name":"retainMintAuthority","type":"bool"},{"name":"buyRequiresPermission","type":"bool"},{"name":"buyPermissionBitmap","type":{"array":["u8",32]}},{"name":"sellRequiresPermission","type":"bool"},{"name":"sellPermissionBitmap","type":{"array":["u8",32]}},{"name":"quoteFeeBps","type":"u16"},{"name":"baseFeeBps","type":"u16"},{"name":"controlPoints","type":{"array":["u16",4]}},{"name":"startPrice","type":"u128"},{"name":"endPrice","type":"u128"},{"name":"quoteAmount","type":"u64"},{"name":"baseAmount","type":"u64"},{"name":"launchTime","type":"i64"},{"name":"creatorReward","type":"u64"},{"name":"graduationTarget","type":"u64"},{"name":"graduationTime","type":"i64"},{"name":"graduationReward","type":"u64"},{"name":"maxBuyAmount","type":"u64"},{"name":"maxSellAmount","type":"u64"},{"name":"swapFeeBps","type":"u16"},{"name":"baseAllocationBps","type":"u16"},{"name":"graduationMethods","type":{"array":[{"defined":"GraduationMethodData"},8]}},{"name":"minReserveBps","type":"u16"},{"name":"padding1","type":{"array":["u8",2]}},{"name":"premintedSupply","type":"u64"},{"name":"padding2","type":{"array":["u8",728]}}]}},{"name":"MintConfig","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"defined":"AccountDiscriminator"}},{"name":"instructionDiscriminator","type":"u8"},{"name":"quoteMint","type":"publicKey"},{"name":"createRequiresPermission","type":"bool"},{"name":"createPermissionBitmap","type":{"array":["u8",32]}},{"name":"defaultBuyRequiresPermission","type":"bool"},{"name":"defaultBuyPermissionBitmap","type":{"array":["u8",32]}},{"name":"defaultSellRequiresPermission","type":"bool"},{"name":"defaultSellPermissionBitmap","type":{"array":["u8",32]}},{"name":"padding1","type":{"array":["u8",3]}},{"name":"defaultCreatorReward","type":"u64"},{"name":"defaultGraduationReward","type":"u64"},{"name":"defaultGraduationTarget","type":"u64"},{"name":"defaultMaxBuyAmount","type":"u64"},{"name":"defaultMaxSellAmount","type":"u64"},{"name":"defaultStartPrice","type":"u128"},{"name":"defaultEndPrice","type":"u128"},{"name":"defaultControlPoints","type":{"array":["u16",4]}},{"name":"defaultSwapFeeBps","type":"u16"},{"name":"defaultQuoteFeeBps","type":"u16"},{"name":"defaultBaseFeeBps","type":"u16"},{"name":"padding2","type":{"array":["u8",1826]}}]}},{"name":"PermissionConfig","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"defined":"AccountDiscriminator"}},{"name":"consumerProgram","type":"publicKey"},{"name":"allowedSigners","type":{"array":[{"defined":"PermissionSigner"},3]}},{"name":"padding","type":{"array":["u8",124]}}]}},{"name":"ConsumedPermission","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"defined":"AccountDiscriminator"}},{"name":"padding1","type":{"array":["u8",7]}},{"name":"safeToCloseSlot","type":"u64"},{"name":"refundDestination","type":"publicKey"},{"name":"padding2","type":{"array":["u8",16]}}]}}],"types":[{"name":"MetaplexMetadata","type":{"kind":"struct","fields":[{"name":"discriminator","type":"u8"},{"name":"updateAuthority","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"data","type":{"defined":"MetaplexData"}},{"name":"primarySaleHappened","type":"bool"},{"name":"mutable","type":"bool"},{"name":"editionNonce","type":{"option":"u8"}},{"name":"tokenStandard","type":{"option":{"defined":"MetaplexTokenStandard"}}},{"name":"collection","type":{"option":{"defined":"MetaplexCollection"}}},{"name":"uses","type":{"option":{"defined":"MetaplexUses"}}},{"name":"collectionDetails","type":{"option":{"defined":"MetaplexCollectionDetails"}}},{"name":"programmableConfig","type":{"option":{"defined":"MetaplexProgrammableConfig"}}}]}},{"name":"MetaplexData","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"sellerFeeBasisPoints","type":"u16"},{"name":"creators","type":{"option":{"vec":{"defined":"MetaplexCreator"}}}}]}},{"name":"MetaplexCreator","type":{"kind":"struct","fields":[{"name":"address","type":"publicKey"},{"name":"verified","type":"bool"},{"name":"share","type":"u8"}]}},{"name":"MetaplexCollection","type":{"kind":"struct","fields":[{"name":"verified","type":"bool"},{"name":"key","type":"publicKey"}]}},{"name":"MetaplexUses","type":{"kind":"struct","fields":[{"name":"useMethod","type":{"defined":"MetaplexUseMethod"}},{"name":"remaining","type":"u64"},{"name":"total","type":"u64"}]}},{"name":"TokenMint","type":{"kind":"struct","fields":[{"name":"mintAuthorityFlag","type":"u32"},{"name":"mintAuthority","type":"publicKey"},{"name":"supply","type":"u64"},{"name":"decimals","type":"u8"},{"name":"isInitialized","type":"bool"},{"name":"freezeAuthorityFlag","type":"u32"},{"name":"freezeAuthority","type":"publicKey"}]}},{"name":"TokenAccount","type":{"kind":"struct","fields":[{"name":"mint","type":"publicKey"},{"name":"owner","type":"publicKey"},{"name":"amount","type":"u64"},{"name":"delegateFlag","type":"u32"},{"name":"delegate","type":"publicKey"},{"name":"state","type":{"defined":"TokenAccountState"}},{"name":"isNativeFlag","type":"u32"},{"name":"nativeAmount","type":"u64"},{"name":"delegateAmount","type":"u64"},{"name":"closeAuthorityFlag","type":"u32"},{"name":"closeAuthority","type":"publicKey"}]}},{"name":"TokenMetadata","type":{"kind":"struct","fields":[{"name":"updateAuthority","type":"publicKey"},{"name":"mint","type":"publicKey"},{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"additionalMetadata","type":{"vec":{"tuple":["string","string"]}}}]}},{"name":"TokenMetadataPointer","type":{"kind":"struct","fields":[{"name":"updateAuthority","type":"publicKey"},{"name":"metadataAddress","type":"publicKey"}]}},{"name":"WhirlpoolFeeTier","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"array":["u8",8]}},{"name":"whirlpoolConfig","type":"publicKey"},{"name":"tickSpacing","type":"u16"},{"name":"defaultFeeRate","type":"u16"}]}},{"name":"WhirlpoolAdaptiveFeeTier","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"array":["u8",8]}},{"name":"whirlpoolConfig","type":"publicKey"},{"name":"feeTierIndex","type":"u16"},{"name":"tickSpacing","type":"u16"},{"name":"initializePoolAuthority","type":"publicKey"},{"name":"delegatedFeeAuthority","type":"publicKey"},{"name":"defaultBaseFeeRate","type":"u16"},{"name":"filterPeriod","type":"u16"},{"name":"decayPeriod","type":"u16"},{"name":"reductionFactor","type":"u16"},{"name":"adaptiveFeeControlFactor","type":"u32"},{"name":"maxVolatilityAccumulator","type":"u32"},{"name":"tickGroupSize","type":"u16"},{"name":"majorSwapThresholdTicks","type":"u16"}]}},{"name":"WhirlpoolConfig","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"array":["u8",8]}},{"name":"feeAuthority","type":"publicKey"},{"name":"collectProtocolFeesAuthority","type":"publicKey"},{"name":"rewardEmissionsSuperAuthority","type":"publicKey"},{"name":"defaultProtocolFeeRate","type":"u16"},{"name":"featureFlags","type":"u16"}]}},{"name":"Whirlpool","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"array":["u8",8]}},{"name":"whirlpoolConfig","type":"publicKey"},{"name":"whirlpoolBump","type":"u8"},{"name":"tickSpacing","type":"u16"},{"name":"feeTierIndex","type":"u16"},{"name":"feeRate","type":"u16"},{"name":"protocolFeeRate","type":"u16"},{"name":"liquidity","type":"u128"},{"name":"sqrtPrice","type":"u128"},{"name":"tickCurrentIndex","type":"i32"},{"name":"protocolFeeOwedA","type":"u64"},{"name":"protocolFeeOwedB","type":"u64"},{"name":"tokenAMint","type":"publicKey"},{"name":"tokenAVault","type":"publicKey"},{"name":"feeGrowthGlobalA","type":"u128"},{"name":"tokenBMint","type":"publicKey"},{"name":"tokenBVault","type":"publicKey"},{"name":"feeGrowthGlobalB","type":"u128"},{"name":"rewardLastUpdatedTimestamp","type":"u64"},{"name":"rewardInfos","type":{"array":[{"defined":"WhirlpoolRewardInfo"},3]}}]}},{"name":"WhirlpoolRewardInfo","type":{"kind":"struct","fields":[{"name":"mint","type":"publicKey"},{"name":"vault","type":"publicKey"},{"name":"authority","type":"publicKey"},{"name":"emissionsPerSecondX64","type":"u128"},{"name":"growthGlobalX64","type":"u128"}]}},{"name":"WhirlpoolLockConfig","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"array":["u8",8]}},{"name":"position","type":"publicKey"},{"name":"positionOwner","type":"publicKey"},{"name":"whirlpool","type":"publicKey"},{"name":"lockedTimestamp","type":"u64"},{"name":"lockType","type":{"defined":"LockTypeLabel"}}]}},{"name":"WhirlpoolPosition","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"array":["u8",8]}},{"name":"whirlpool","type":"publicKey"},{"name":"positionMint","type":"publicKey"},{"name":"liquidity","type":"u128"},{"name":"tickLowerIndex","type":"i32"},{"name":"tickUpperIndex","type":"i32"},{"name":"feeGrowthCheckpointA","type":"u128"},{"name":"feeOwedA","type":"u64"},{"name":"feeGrowthCheckpointB","type":"u128"},{"name":"feeOwedB","type":"u64"},{"name":"rewardInfos","type":{"array":[{"defined":"WhirlpoolPositionRewardInfo"},3]}}]}},{"name":"WhirlpoolPositionRewardInfo","type":{"kind":"struct","fields":[{"name":"growthInsideCheckpoint","type":"u128"},{"name":"amountOwed","type":"u64"}]}},{"name":"WhirlpoolTickArray","type":{"kind":"struct","fields":[{"name":"discriminator","type":{"array":["u8",8]}},{"name":"startTickIndex","type":"i32"},{"name":"ticks","type":{"array":["u8",9944]}},{"name":"whirlpool","type":"publicKey"}]}},{"name":"ProgramAuthority","type":{"kind":"struct","fields":[{"name":"key","type":"publicKey"},{"name":"privileges","type":{"array":["u8",32]}}]}},{"name":"GraduationMethodData","type":{"kind":"struct","fields":[{"name":"label","type":{"defined":"GraduationMethodLabel"}},{"name":"graduated","type":"bool"},{"name":"feeTierIndex","type":"u16"},{"name":"splitBps","type":"u16"},{"name":"destination","type":"publicKey"},{"name":"unlocked","type":"bool"},{"name":"padding","type":{"array":["u8",89]}}]}},{"name":"PermissionSigner","type":{"kind":"struct","fields":[{"name":"bytes","type":{"array":["u8",33]}}]}},{"name":"PermissionSignature","type":{"kind":"struct","fields":[{"name":"recoveryId","type":"u8"},{"name":"bytes","type":{"array":["u8",64]}}]}},{"name":"MetaplexTokenStandard","type":{"kind":"enum","variants":[{"name":"NonFungible"},{"name":"FungibleAsset"},{"name":"Fungible"},{"name":"NonFungibleEdition"},{"name":"ProgrammableNonFungible"},{"name":"ProgrammableNonFungibleEdition"}]}},{"name":"MetaplexUseMethod","type":{"kind":"enum","variants":[{"name":"Burn"},{"name":"Multiple"},{"name":"Single"}]}},{"name":"MetaplexCollectionDetails","type":{"kind":"enum","variants":[{"name":"V1","fields":[{"name":"size","type":"u64"}]},{"name":"V2","fields":[{"name":"padding","type":{"array":["u8",8]}}]}]}},{"name":"MetaplexProgrammableConfig","type":{"kind":"enum","variants":[{"name":"V1","fields":[{"name":"rule_set","type":{"option":"publicKey"}}]}]}},{"name":"TokenAccountState","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Initialized"},{"name":"Frozen"}]}},{"name":"LockTypeLabel","type":{"kind":"enum","variants":[{"name":"Permanent"}]}},{"name":"BondingCurveCreationType","type":{"kind":"enum","variants":[{"name":"Manual"},{"name":"Launch"},{"name":"Lockedlaunch"},{"name":"Presale"}]}},{"name":"Event","type":{"kind":"enum","variants":[{"name":"BondingCurveCreated","fields":[{"name":"creation_type","type":{"defined":"BondingCurveCreationType"}},{"name":"base_mint","type":"publicKey"},{"name":"quote_mint","type":"publicKey"},{"name":"creator","type":"publicKey"},{"name":"start_price","type":"u128"},{"name":"end_price","type":"u128"},{"name":"control_points","type":{"array":["u16",4]}},{"name":"swap_fee_bps","type":"u16"},{"name":"quote_fee_bps","type":"u16"},{"name":"base_fee_bps","type":"u16"},{"name":"base_allocation_bps","type":"u16"},{"name":"launch_time","type":"i64"},{"name":"creator_reward","type":"u64"},{"name":"graduation_methods","type":{"array":[{"defined":"GraduationMethodData"},8]}},{"name":"graduation_target","type":"u64"},{"name":"graduation_time","type":"i64"},{"name":"graduation_reward","type":"u64"},{"name":"retain_mint_authority","type":"bool"},{"name":"min_reserve_bps","type":"u16"},{"name":"buy_requires_permission","type":"bool"},{"name":"buy_permission_bitmap","type":{"array":["u8",32]}},{"name":"max_buy_amount","type":"u64"},{"name":"sell_requires_permission","type":"bool"},{"name":"sell_permission_bitmap","type":{"array":["u8",32]}},{"name":"max_sell_amount","type":"u64"},{"name":"quote_token_program","type":"publicKey"},{"name":"base_token_program","type":"publicKey"},{"name":"quote_token_decimals","type":"u8"},{"name":"base_token_decimals","type":"u8"}]},{"name":"TokenBought","fields":[{"name":"buyer","type":"publicKey"},{"name":"base_mint","type":"publicKey"},{"name":"quote_mint","type":"publicKey"},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"},{"name":"price_before","type":"u128"},{"name":"price_after","type":"u128"},{"name":"quote_amount_before","type":"u64"},{"name":"quote_amount_after","type":"u64"},{"name":"base_amount_before","type":"u64"},{"name":"base_amount_after","type":"u64"},{"name":"protocol_fee","type":"u64"}]},{"name":"TokenSold","fields":[{"name":"seller","type":"publicKey"},{"name":"base_mint","type":"publicKey"},{"name":"quote_mint","type":"publicKey"},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"},{"name":"price_before","type":"u128"},{"name":"price_after","type":"u128"},{"name":"quote_amount_before","type":"u64"},{"name":"quote_amount_after","type":"u64"},{"name":"base_amount_before","type":"u64"},{"name":"base_amount_after","type":"u64"},{"name":"protocol_fee","type":"u64"}]},{"name":"TokenRefunded","fields":[{"name":"signer","type":"publicKey"},{"name":"base_mint","type":"publicKey"},{"name":"quote_mint","type":"publicKey"},{"name":"price","type":"u128"},{"name":"quote_amount","type":"u64"},{"name":"base_amount","type":"u64"}]},{"name":"BondingCurveGraduated","fields":[{"name":"graduator","type":"publicKey"},{"name":"base_mint","type":"publicKey"},{"name":"quote_mint","type":"publicKey"},{"name":"final_price","type":"u128"},{"name":"graduation_methods","type":{"array":[{"defined":"GraduationMethodData"},8]}}]},{"name":"BondingCurveClosed","fields":[{"name":"bonding_curve","type":"publicKey"}]}]}},{"name":"MintConfigUpdateType","type":{"kind":"enum","variants":[{"name":"DefaultCreatorReward","fields":["u64"]},{"name":"DefaultGraduationReward","fields":["u64"]},{"name":"DefaultGraduationTarget","fields":["u64"]},{"name":"DefaultMaxBuyAmount","fields":["u64"]},{"name":"DefaultMaxSellAmount","fields":["u64"]},{"name":"DefaultBuyPermissionBitmap","fields":[{"option":{"array":["u8",32]}}]},{"name":"DefaultSellPermissionBitmap","fields":[{"option":{"array":["u8",32]}}]},{"name":"CreatePermissionBitmap","fields":[{"option":{"array":["u8",32]}}]},{"name":"PriceCurve","fields":["u128","u128",{"array":["u16",4]}]},{"name":"DefaultSwapFeeBps","fields":["u16"]},{"name":"DefaultBaseFeeBps","fields":["u16"]},{"name":"DefaultQuoteFeeBps","fields":["u16"]}]}},{"name":"PermissionConfigUpdateType","type":{"kind":"enum","variants":[{"name":"AllowedSigner1","fields":[{"defined":"PermissionSigner"}]},{"name":"AllowedSigner2","fields":[{"defined":"PermissionSigner"}]},{"name":"AllowedSigner3","fields":[{"defined":"PermissionSigner"}]}]}},{"name":"AccountDiscriminator","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Closed"},{"name":"BondingCurve"},{"name":"AuthorityConfig"},{"name":"PermissionConfig"},{"name":"ConsumedPermission"},{"name":"MintConfig"}]}},{"name":"Privilege","type":{"kind":"enum","variants":[{"name":"AuthorityConfigUpdate"},{"name":"ReceiveProtocolFees"},{"name":"MintConfigInitialize"},{"name":"MintConfigUpdate"},{"name":"MintConfigClose"},{"name":"PermissionConfigInitialize"},{"name":"PermissionConfigUpdate"},{"name":"PermissionConfigClose"},{"name":"BondingCurveInitialize"},{"name":"BondingCurveUpdate"},{"name":"BondingCurveClose"},{"name":"ExecuteLpTakeover"}]}},{"name":"GraduationMethodLabel","type":{"kind":"enum","variants":[{"name":"None"},{"name":"Whirlpool"},{"name":"Manual"}]}},{"name":"GraduationMethod","type":{"kind":"enum","variants":[{"name":"None"},{"name":"Whirlpool","fields":[{"name":"split_bps","type":"u16"},{"name":"fee_tier_index","type":"u16"},{"name":"destination","type":"publicKey"},{"name":"unlocked","type":"bool"}]},{"name":"Manual","fields":[{"name":"split_bps","type":"u16"},{"name":"destination","type":"publicKey"}]}]}},{"name":"PermissionMessage","type":{"kind":"enum","variants":[{"name":"V1","fields":[{"name":"nonce","type":"u64"},{"name":"consumer_program","type":"publicKey"},{"name":"permission_signer","type":{"defined":"PermissionSigner"}},{"name":"permission_subject","type":"publicKey"},{"name":"valid_until","type":"u64"},{"name":"permission_type","type":"u8"},{"name":"instruction_discriminators","type":{"vec":"bytes"}}]}]}}],"errors":[{"code":6000,"name":"InvalidAccountRole","msg":"Invalid account role"},{"code":6001,"name":"IncorrectAccountAddress","msg":"Incorrect account address"},{"code":6002,"name":"PermissionActive","msg":"Permission still active"},{"code":6003,"name":"PermissionExpired","msg":"Permission expired"},{"code":6004,"name":"PermissionInvalidSignature","msg":"Permission invalid signature"},{"code":6005,"name":"WhirlpoolError","msg":"Error from whirlpool lib"},{"code":6006,"name":"UnknownInstructionDiscriminator","msg":"Unknown instruction discriminator"},{"code":6007,"name":"IncorrectProgramId","msg":"Incorrect program id"},{"code":6008,"name":"NotEnoughAccountKeys","msg":"Not enough account keys"},{"code":6009,"name":"ArithmeticError","msg":"Arithmetic error"},{"code":6010,"name":"QuoteError","msg":"Error calculating quote from math-lib"},{"code":6011,"name":"SlippageExceeded","msg":"Slippage exceeded"},{"code":6012,"name":"PriceError","msg":"Error calculating token price"},{"code":6013,"name":"GraduationConditionsNotMet","msg":"Token has not met graduation conditions"},{"code":6014,"name":"WhirlpoolAlreadyExists","msg":"Whirlpool already exists"},{"code":6015,"name":"IncorrectGraduationInstruction","msg":"Incorrect graduation instruction"},{"code":6016,"name":"BondingCurveNotExpired","msg":"Bonding curve is still active"},{"code":6017,"name":"InsufficientTokensForGraduation","msg":"Insufficient tokens for graduation"},{"code":6018,"name":"MissingPermissionConsumeInstruction","msg":"Missing permission consume instruction"},{"code":6019,"name":"BondingCurveNotActive","msg":"Bonding curve is either not launched yet or is ready to be graduated"},{"code":6020,"name":"InvalidPriceCurve","msg":"Invalid price curve"},{"code":6021,"name":"GraduationMethodNotCalled","msg":"Graduation method not called"},{"code":6022,"name":"InvalidAccountData","msg":"Invalid account data"},{"code":6023,"name":"InvalidSeeds","msg":"Invalid seeds"},{"code":6024,"name":"IncorrectOwner","msg":"Account is not owned by the right program"},{"code":6025,"name":"InvalidInstructionData","msg":"Invalid instruction data"},{"code":6026,"name":"ReservedX","msg":"Reserved for future use"},{"code":6027,"name":"MissingPrivileges","msg":"An account does not have the required privileges"},{"code":6028,"name":"NoMoreAuthoritySlots","msg":"No more authority slots"},{"code":6029,"name":"EmitEventError","msg":"Event serialization failed"}],"metadata":{"origin":"shank","address":"waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF"}} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/orca_whirlpool.whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.json b/migration/khadhroony-bot2-reference/idls/orca_whirlpool.whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.json new file mode 100644 index 0000000..7a4dee9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/orca_whirlpool.whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc.json @@ -0,0 +1 @@ +{"address":"whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc","metadata":{"name":"whirlpool","version":"0.9.0","spec":"0.1.0"},"instructions":[{"name":"close_bundled_position","discriminator":[41,36,216,245,27,85,103,67],"accounts":[{"name":"bundled_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,117,110,100,108,101,100,95,112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_bundle.position_bundle_mint","account":"PositionBundle"},{"kind":"arg","path":"bundle_index"}]}},{"name":"position_bundle","writable":true},{"name":"position_bundle_token_account"},{"name":"position_bundle_authority","signer":true},{"name":"receiver","writable":true}],"args":[{"name":"bundle_index","type":"u16"}]},{"name":"close_position","discriminator":[123,134,81,0,49,68,98,98],"accounts":[{"name":"position_authority","signer":true},{"name":"receiver","writable":true},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_mint"}]}},{"name":"position_mint","writable":true},{"name":"position_token_account","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[]},{"name":"close_position_with_token_extensions","discriminator":[1,182,135,59,155,25,99,223],"accounts":[{"name":"position_authority","signer":true},{"name":"receiver","writable":true},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_mint"}]}},{"name":"position_mint","writable":true},{"name":"position_token_account","writable":true},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[]},{"name":"collect_fees","discriminator":[164,152,207,99,30,186,19,182],"accounts":[{"name":"whirlpool","relations":["position"]},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_owner_account_a","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_b","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[]},{"name":"collect_fees_v2","discriminator":[207,117,95,191,229,180,226,15],"accounts":[{"name":"whirlpool","relations":["position"]},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_b","writable":true},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"collect_protocol_fees","discriminator":[22,67,23,98,150,178,70,220],"accounts":[{"name":"whirlpools_config","relations":["whirlpool"]},{"name":"whirlpool","writable":true},{"name":"collect_protocol_fees_authority","signer":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"token_destination_a","writable":true},{"name":"token_destination_b","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[]},{"name":"collect_protocol_fees_v2","discriminator":[103,128,222,134,114,200,22,200],"accounts":[{"name":"whirlpools_config","relations":["whirlpool"]},{"name":"whirlpool","writable":true},{"name":"collect_protocol_fees_authority","signer":true},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"token_destination_a","writable":true},{"name":"token_destination_b","writable":true},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"collect_reward","discriminator":[70,5,132,87,86,235,177,34],"accounts":[{"name":"whirlpool","relations":["position"]},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"reward_owner_account","writable":true},{"name":"reward_vault","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"collect_reward_v2","discriminator":[177,107,37,180,160,19,49,209],"accounts":[{"name":"whirlpool","relations":["position"]},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"reward_owner_account","writable":true},{"name":"reward_mint"},{"name":"reward_vault","writable":true},{"name":"reward_token_program"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"reward_index","type":"u8"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"decrease_liquidity","discriminator":[160,38,208,111,104,91,44,1],"accounts":[{"name":"whirlpool","writable":true,"relations":["position"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_lower","writable":true},{"name":"tick_array_upper","writable":true}],"args":[{"name":"liquidity_amount","type":"u128"},{"name":"token_min_a","type":"u64"},{"name":"token_min_b","type":"u64"}]},{"name":"decrease_liquidity_v2","discriminator":[58,127,188,62,79,82,196,96],"accounts":[{"name":"whirlpool","writable":true,"relations":["position"]},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_lower","writable":true},{"name":"tick_array_upper","writable":true}],"args":[{"name":"liquidity_amount","type":"u128"},{"name":"token_min_a","type":"u64"},{"name":"token_min_b","type":"u64"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"delete_position_bundle","discriminator":[100,25,99,2,217,239,124,173],"accounts":[{"name":"position_bundle","writable":true},{"name":"position_bundle_mint","writable":true},{"name":"position_bundle_token_account","writable":true},{"name":"position_bundle_owner","signer":true},{"name":"receiver","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[]},{"name":"delete_token_badge","discriminator":[53,146,68,8,18,117,17,185],"accounts":[{"name":"whirlpools_config","relations":["whirlpools_config_extension","token_badge"]},{"name":"whirlpools_config_extension"},{"name":"token_badge_authority","signer":true},{"name":"token_mint"},{"name":"token_badge","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,98,97,100,103,101]},{"kind":"account","path":"whirlpools_config"},{"kind":"account","path":"token_mint"}]}},{"name":"receiver","writable":true}],"args":[]},{"name":"idl_include","discriminator":[223,253,121,121,60,193,129,31],"accounts":[{"name":"tick_array"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"increase_liquidity","discriminator":[46,156,243,118,13,205,251,178],"accounts":[{"name":"whirlpool","writable":true,"relations":["position"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_lower","writable":true},{"name":"tick_array_upper","writable":true}],"args":[{"name":"liquidity_amount","type":"u128"},{"name":"token_max_a","type":"u64"},{"name":"token_max_b","type":"u64"}]},{"name":"increase_liquidity_by_token_amounts_v2","discriminator":[239,251,9,124,210,198,53,43],"accounts":[{"name":"whirlpool","writable":true,"relations":["position"]},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_lower","writable":true},{"name":"tick_array_upper","writable":true}],"args":[{"name":"method","type":{"defined":{"name":"IncreaseLiquidityMethod"}}},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"increase_liquidity_v2","discriminator":[133,29,89,223,69,238,176,10],"accounts":[{"name":"whirlpool","writable":true,"relations":["position"]},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"position_authority","signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_lower","writable":true},{"name":"tick_array_upper","writable":true}],"args":[{"name":"liquidity_amount","type":"u128"},{"name":"token_max_a","type":"u64"},{"name":"token_max_b","type":"u64"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"initialize_adaptive_fee_tier","discriminator":[77,99,208,200,141,123,117,48],"accounts":[{"name":"whirlpools_config"},{"name":"adaptive_fee_tier","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,116,105,101,114]},{"kind":"account","path":"whirlpools_config"},{"kind":"arg","path":"fee_tier_index"}]}},{"name":"funder","writable":true,"signer":true},{"name":"fee_authority","signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"fee_tier_index","type":"u16"},{"name":"tick_spacing","type":"u16"},{"name":"initialize_pool_authority","type":"pubkey"},{"name":"delegated_fee_authority","type":"pubkey"},{"name":"default_base_fee_rate","type":"u16"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"adaptive_fee_control_factor","type":"u32"},{"name":"max_volatility_accumulator","type":"u32"},{"name":"tick_group_size","type":"u16"},{"name":"major_swap_threshold_ticks","type":"u16"}]},{"name":"initialize_config","discriminator":[208,127,21,1,194,190,196,70],"accounts":[{"name":"config","writable":true,"signer":true},{"name":"funder","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"fee_authority","type":"pubkey"},{"name":"collect_protocol_fees_authority","type":"pubkey"},{"name":"reward_emissions_super_authority","type":"pubkey"},{"name":"default_protocol_fee_rate","type":"u16"}]},{"name":"initialize_config_extension","discriminator":[55,9,53,9,114,57,209,52],"accounts":[{"name":"config"},{"name":"config_extension","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,111,110,102,105,103,95,101,120,116,101,110,115,105,111,110]},{"kind":"account","path":"config"}]}},{"name":"funder","writable":true,"signer":true},{"name":"fee_authority","signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"initialize_dynamic_tick_array","discriminator":[41,33,165,200,120,231,142,50],"accounts":[{"name":"whirlpool"},{"name":"funder","writable":true,"signer":true},{"name":"tick_array","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"whirlpool"},{"kind":"arg","path":"start_tick_index"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"start_tick_index","type":"i32"},{"name":"idempotent","type":"bool"}]},{"name":"initialize_fee_tier","discriminator":[183,74,156,160,112,2,42,30],"accounts":[{"name":"config"},{"name":"fee_tier","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,116,105,101,114]},{"kind":"account","path":"config"},{"kind":"arg","path":"tick_spacing"}]}},{"name":"funder","writable":true,"signer":true},{"name":"fee_authority","signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"tick_spacing","type":"u16"},{"name":"default_fee_rate","type":"u16"}]},{"name":"initialize_pool","discriminator":[95,180,10,172,84,174,232,40],"accounts":[{"name":"whirlpools_config","relations":["fee_tier"]},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"funder","writable":true,"signer":true},{"name":"whirlpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[119,104,105,114,108,112,111,111,108]},{"kind":"account","path":"whirlpools_config"},{"kind":"account","path":"token_mint_a"},{"kind":"account","path":"token_mint_b"},{"kind":"arg","path":"tick_spacing"}]}},{"name":"token_vault_a","writable":true,"signer":true},{"name":"token_vault_b","writable":true,"signer":true},{"name":"fee_tier"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"bumps","type":{"defined":{"name":"WhirlpoolBumps"}}},{"name":"tick_spacing","type":"u16"},{"name":"initial_sqrt_price","type":"u128"}]},{"name":"initialize_pool_v2","discriminator":[207,45,87,242,27,63,204,67],"accounts":[{"name":"whirlpools_config","relations":["fee_tier"]},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_badge_a","pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,98,97,100,103,101]},{"kind":"account","path":"whirlpools_config"},{"kind":"account","path":"token_mint_a"}]}},{"name":"token_badge_b","pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,98,97,100,103,101]},{"kind":"account","path":"whirlpools_config"},{"kind":"account","path":"token_mint_b"}]}},{"name":"funder","writable":true,"signer":true},{"name":"whirlpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[119,104,105,114,108,112,111,111,108]},{"kind":"account","path":"whirlpools_config"},{"kind":"account","path":"token_mint_a"},{"kind":"account","path":"token_mint_b"},{"kind":"arg","path":"tick_spacing"}]}},{"name":"token_vault_a","writable":true,"signer":true},{"name":"token_vault_b","writable":true,"signer":true},{"name":"fee_tier"},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"tick_spacing","type":"u16"},{"name":"initial_sqrt_price","type":"u128"}]},{"name":"initialize_pool_with_adaptive_fee","discriminator":[143,94,96,76,172,124,119,199],"accounts":[{"name":"whirlpools_config","relations":["adaptive_fee_tier"]},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_badge_a","pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,98,97,100,103,101]},{"kind":"account","path":"whirlpools_config"},{"kind":"account","path":"token_mint_a"}]}},{"name":"token_badge_b","pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,98,97,100,103,101]},{"kind":"account","path":"whirlpools_config"},{"kind":"account","path":"token_mint_b"}]}},{"name":"funder","writable":true,"signer":true},{"name":"initialize_pool_authority","signer":true},{"name":"whirlpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[119,104,105,114,108,112,111,111,108]},{"kind":"account","path":"whirlpools_config"},{"kind":"account","path":"token_mint_a"},{"kind":"account","path":"token_mint_b"},{"kind":"account","path":"adaptive_fee_tier.fee_tier_index","account":"AdaptiveFeeTier"}]}},{"name":"oracle","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"whirlpool"}]}},{"name":"token_vault_a","writable":true,"signer":true},{"name":"token_vault_b","writable":true,"signer":true},{"name":"adaptive_fee_tier"},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"initial_sqrt_price","type":"u128"},{"name":"trade_enable_timestamp","type":{"option":"u64"}}]},{"name":"initialize_position_bundle","discriminator":[117,45,241,149,24,18,194,65],"accounts":[{"name":"position_bundle","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110,95,98,117,110,100,108,101]},{"kind":"account","path":"position_bundle_mint"}]}},{"name":"position_bundle_mint","writable":true,"signer":true},{"name":"position_bundle_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"position_bundle_owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_bundle_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"position_bundle_owner"},{"name":"funder","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[]},{"name":"initialize_position_bundle_with_metadata","discriminator":[93,124,16,179,249,131,115,245],"accounts":[{"name":"position_bundle","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110,95,98,117,110,100,108,101]},{"kind":"account","path":"position_bundle_mint"}]}},{"name":"position_bundle_mint","writable":true,"signer":true},{"name":"position_bundle_metadata","writable":true},{"name":"position_bundle_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"position_bundle_owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_bundle_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"position_bundle_owner"},{"name":"funder","writable":true,"signer":true},{"name":"metadata_update_auth","address":"3axbTs2z5GBy6usVbNVoqEgZMng3vZvMnAoX29BFfwhr"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"}],"args":[]},{"name":"initialize_reward","discriminator":[95,135,192,196,242,129,230,68],"accounts":[{"name":"reward_authority","signer":true},{"name":"funder","writable":true,"signer":true},{"name":"whirlpool","writable":true},{"name":"reward_mint"},{"name":"reward_vault","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"initialize_reward_v2","discriminator":[91,1,77,50,235,229,133,49],"accounts":[{"name":"reward_authority","signer":true},{"name":"funder","writable":true,"signer":true},{"name":"whirlpool","writable":true},{"name":"reward_mint"},{"name":"reward_token_badge","pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,98,97,100,103,101]},{"kind":"account","path":"whirlpool.whirlpools_config","account":"Whirlpool"},{"kind":"account","path":"reward_mint"}]}},{"name":"reward_vault","writable":true,"signer":true},{"name":"reward_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"initialize_tick_array","discriminator":[11,188,193,214,141,91,149,184],"accounts":[{"name":"whirlpool"},{"name":"funder","writable":true,"signer":true},{"name":"tick_array","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"whirlpool"},{"kind":"arg","path":"start_tick_index"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"start_tick_index","type":"i32"}]},{"name":"initialize_token_badge","discriminator":[253,77,205,95,27,224,89,223],"accounts":[{"name":"whirlpools_config","relations":["whirlpools_config_extension"]},{"name":"whirlpools_config_extension"},{"name":"token_badge_authority","signer":true},{"name":"token_mint"},{"name":"token_badge","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,111,107,101,110,95,98,97,100,103,101]},{"kind":"account","path":"whirlpools_config"},{"kind":"account","path":"token_mint"}]}},{"name":"funder","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"lock_position","discriminator":[227,62,2,252,247,10,171,185],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"position_authority","signer":true},{"name":"position","pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_mint"}]}},{"name":"position_mint"},{"name":"position_token_account","writable":true},{"name":"lock_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[108,111,99,107,95,99,111,110,102,105,103]},{"kind":"account","path":"position"}]}},{"name":"whirlpool","relations":["position"]},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"lock_type","type":{"defined":{"name":"LockType"}}}]},{"name":"migrate_repurpose_reward_authority_space","discriminator":[214,161,248,79,152,98,172,231],"accounts":[{"name":"whirlpool","writable":true}],"args":[]},{"name":"open_bundled_position","discriminator":[169,113,126,171,213,172,212,49],"accounts":[{"name":"bundled_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,117,110,100,108,101,100,95,112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_bundle.position_bundle_mint","account":"PositionBundle"},{"kind":"arg","path":"bundle_index"}]}},{"name":"position_bundle","writable":true},{"name":"position_bundle_token_account"},{"name":"position_bundle_authority","signer":true},{"name":"whirlpool"},{"name":"funder","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"bundle_index","type":"u16"},{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"}]},{"name":"open_position","discriminator":[135,128,47,77,15,152,240,49],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"owner"},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_mint"}]}},{"name":"position_mint","writable":true,"signer":true},{"name":"position_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"whirlpool"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"bumps","type":{"defined":{"name":"OpenPositionBumps"}}},{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"}]},{"name":"open_position_with_metadata","discriminator":[242,29,134,48,58,110,14,60],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"owner"},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_mint"}]}},{"name":"position_mint","writable":true,"signer":true},{"name":"position_metadata_account","writable":true},{"name":"position_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"whirlpool"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"metadata_update_auth","address":"3axbTs2z5GBy6usVbNVoqEgZMng3vZvMnAoX29BFfwhr"}],"args":[{"name":"bumps","type":{"defined":{"name":"OpenPositionWithMetadataBumps"}}},{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"}]},{"name":"open_position_with_token_extensions","discriminator":[212,47,95,92,114,102,131,250],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"owner"},{"name":"position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_mint"}]}},{"name":"position_mint","writable":true,"signer":true},{"name":"position_token_account","writable":true},{"name":"whirlpool"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_update_auth","address":"3axbTs2z5GBy6usVbNVoqEgZMng3vZvMnAoX29BFfwhr"}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"with_token_metadata_extension","type":"bool"}]},{"name":"reposition_liquidity_v2","discriminator":[191,169,224,11,131,19,158,253],"accounts":[{"name":"whirlpool","writable":true,"relations":["position"]},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"position_authority","signer":true},{"name":"funder","writable":true,"signer":true},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_vault_b","writable":true},{"name":"existing_tick_array_lower","writable":true},{"name":"existing_tick_array_upper","writable":true},{"name":"new_tick_array_lower","writable":true},{"name":"new_tick_array_upper","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"new_tick_lower_index","type":"i32"},{"name":"new_tick_upper_index","type":"i32"},{"name":"method","type":{"defined":{"name":"RepositionLiquidityMethod"}}},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"reset_position_range","discriminator":[164,123,180,141,194,100,160,175],"accounts":[{"name":"funder","writable":true,"signer":true},{"name":"position_authority","signer":true},{"name":"whirlpool","relations":["position"]},{"name":"position","writable":true},{"name":"position_token_account"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"new_tick_lower_index","type":"i32"},{"name":"new_tick_upper_index","type":"i32"}]},{"name":"set_adaptive_fee_constants","discriminator":[133,158,212,189,237,12,73,39],"accounts":[{"name":"whirlpool","relations":["oracle"]},{"name":"whirlpools_config","relations":["whirlpool"]},{"name":"oracle","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"filter_period","type":{"option":"u16"}},{"name":"decay_period","type":{"option":"u16"}},{"name":"reduction_factor","type":{"option":"u16"}},{"name":"adaptive_fee_control_factor","type":{"option":"u32"}},{"name":"max_volatility_accumulator","type":{"option":"u32"}},{"name":"tick_group_size","type":{"option":"u16"}},{"name":"major_swap_threshold_ticks","type":{"option":"u16"}}]},{"name":"set_collect_protocol_fees_authority","discriminator":[34,150,93,244,139,225,233,67],"accounts":[{"name":"whirlpools_config","writable":true},{"name":"collect_protocol_fees_authority","signer":true},{"name":"new_collect_protocol_fees_authority"}],"args":[]},{"name":"set_config_extension_authority","discriminator":[44,94,241,116,24,188,60,143],"accounts":[{"name":"whirlpools_config","relations":["whirlpools_config_extension"]},{"name":"whirlpools_config_extension","writable":true},{"name":"config_extension_authority","signer":true},{"name":"new_config_extension_authority"}],"args":[]},{"name":"set_config_feature_flag","discriminator":[71,173,228,18,67,247,210,57],"accounts":[{"name":"whirlpools_config","writable":true},{"name":"authority","signer":true}],"args":[{"name":"feature_flag","type":{"defined":{"name":"ConfigFeatureFlag"}}}]},{"name":"set_default_base_fee_rate","discriminator":[229,66,84,251,164,134,183,7],"accounts":[{"name":"whirlpools_config","relations":["adaptive_fee_tier"]},{"name":"adaptive_fee_tier","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"default_base_fee_rate","type":"u16"}]},{"name":"set_default_fee_rate","discriminator":[118,215,214,157,182,229,208,228],"accounts":[{"name":"whirlpools_config","relations":["fee_tier"]},{"name":"fee_tier","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"default_fee_rate","type":"u16"}]},{"name":"set_default_protocol_fee_rate","discriminator":[107,205,249,226,151,35,86,0],"accounts":[{"name":"whirlpools_config","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"default_protocol_fee_rate","type":"u16"}]},{"name":"set_delegated_fee_authority","discriminator":[193,234,231,147,138,57,3,122],"accounts":[{"name":"whirlpools_config","relations":["adaptive_fee_tier"]},{"name":"adaptive_fee_tier","writable":true},{"name":"fee_authority","signer":true},{"name":"new_delegated_fee_authority"}],"args":[]},{"name":"set_fee_authority","discriminator":[31,1,50,87,237,101,97,132],"accounts":[{"name":"whirlpools_config","writable":true},{"name":"fee_authority","signer":true},{"name":"new_fee_authority"}],"args":[]},{"name":"set_fee_rate","discriminator":[53,243,137,65,8,140,158,6],"accounts":[{"name":"whirlpools_config","relations":["whirlpool"]},{"name":"whirlpool","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"fee_rate","type":"u16"}]},{"name":"set_fee_rate_by_delegated_fee_authority","discriminator":[121,121,54,114,131,230,162,104],"accounts":[{"name":"whirlpool","writable":true},{"name":"adaptive_fee_tier"},{"name":"delegated_fee_authority","signer":true}],"args":[{"name":"fee_rate","type":"u16"}]},{"name":"set_initialize_pool_authority","discriminator":[125,43,127,235,149,26,106,236],"accounts":[{"name":"whirlpools_config","relations":["adaptive_fee_tier"]},{"name":"adaptive_fee_tier","writable":true},{"name":"fee_authority","signer":true},{"name":"new_initialize_pool_authority"}],"args":[]},{"name":"set_preset_adaptive_fee_constants","discriminator":[132,185,66,148,83,88,134,198],"accounts":[{"name":"whirlpools_config","relations":["adaptive_fee_tier"]},{"name":"adaptive_fee_tier","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"adaptive_fee_control_factor","type":"u32"},{"name":"max_volatility_accumulator","type":"u32"},{"name":"tick_group_size","type":"u16"},{"name":"major_swap_threshold_ticks","type":"u16"}]},{"name":"set_protocol_fee_rate","discriminator":[95,7,4,50,154,79,156,131],"accounts":[{"name":"whirlpools_config","relations":["whirlpool"]},{"name":"whirlpool","writable":true},{"name":"fee_authority","signer":true}],"args":[{"name":"protocol_fee_rate","type":"u16"}]},{"name":"set_reward_authority","discriminator":[34,39,183,252,83,28,85,127],"accounts":[{"name":"whirlpool","writable":true},{"name":"reward_authority","signer":true},{"name":"new_reward_authority"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"set_reward_authority_by_super_authority","discriminator":[240,154,201,198,148,93,56,25],"accounts":[{"name":"whirlpools_config","relations":["whirlpool"]},{"name":"whirlpool","writable":true},{"name":"reward_emissions_super_authority","signer":true},{"name":"new_reward_authority"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"set_reward_emissions","discriminator":[13,197,86,168,109,176,27,244],"accounts":[{"name":"whirlpool","writable":true},{"name":"reward_authority","signer":true},{"name":"reward_vault"}],"args":[{"name":"reward_index","type":"u8"},{"name":"emissions_per_second_x64","type":"u128"}]},{"name":"set_reward_emissions_super_authority","discriminator":[207,5,200,209,122,56,82,183],"accounts":[{"name":"whirlpools_config","writable":true},{"name":"reward_emissions_super_authority","signer":true},{"name":"new_reward_emissions_super_authority"}],"args":[]},{"name":"set_reward_emissions_v2","discriminator":[114,228,72,32,193,48,160,102],"accounts":[{"name":"whirlpool","writable":true},{"name":"reward_authority","signer":true},{"name":"reward_vault"}],"args":[{"name":"reward_index","type":"u8"},{"name":"emissions_per_second_x64","type":"u128"}]},{"name":"set_token_badge_attribute","discriminator":[224,88,65,33,138,147,246,137],"accounts":[{"name":"whirlpools_config","relations":["whirlpools_config_extension","token_badge"]},{"name":"whirlpools_config_extension"},{"name":"token_badge_authority","signer":true},{"name":"token_mint","relations":["token_badge"]},{"name":"token_badge","writable":true}],"args":[{"name":"attribute","type":{"defined":{"name":"TokenBadgeAttribute"}}}]},{"name":"set_token_badge_authority","discriminator":[207,202,4,32,205,79,13,178],"accounts":[{"name":"whirlpools_config","relations":["whirlpools_config_extension"]},{"name":"whirlpools_config_extension","writable":true},{"name":"config_extension_authority","signer":true},{"name":"new_token_badge_authority"}],"args":[]},{"name":"swap","discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_authority","signer":true},{"name":"whirlpool","writable":true},{"name":"token_owner_account_a","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_0","writable":true},{"name":"tick_array_1","writable":true},{"name":"tick_array_2","writable":true},{"name":"oracle","pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"whirlpool"}]}}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit","type":"u128"},{"name":"amount_specified_is_input","type":"bool"},{"name":"a_to_b","type":"bool"}]},{"name":"swap_v2","discriminator":[43,4,237,11,26,201,30,98],"accounts":[{"name":"token_program_a"},{"name":"token_program_b"},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"token_authority","signer":true},{"name":"whirlpool","writable":true},{"name":"token_mint_a"},{"name":"token_mint_b"},{"name":"token_owner_account_a","writable":true},{"name":"token_vault_a","writable":true},{"name":"token_owner_account_b","writable":true},{"name":"token_vault_b","writable":true},{"name":"tick_array_0","writable":true},{"name":"tick_array_1","writable":true},{"name":"tick_array_2","writable":true},{"name":"oracle","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"whirlpool"}]}}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit","type":"u128"},{"name":"amount_specified_is_input","type":"bool"},{"name":"a_to_b","type":"bool"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"transfer_locked_position","discriminator":[179,121,229,46,67,138,194,138],"accounts":[{"name":"position_authority","signer":true},{"name":"receiver","writable":true},{"name":"position","pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_mint"}]},"relations":["lock_config"]},{"name":"position_mint"},{"name":"position_token_account","writable":true},{"name":"destination_token_account","writable":true},{"name":"lock_config","writable":true},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[]},{"name":"two_hop_swap","discriminator":[195,96,237,108,68,162,219,230],"accounts":[{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_authority","signer":true},{"name":"whirlpool_one","writable":true},{"name":"whirlpool_two","writable":true},{"name":"token_owner_account_one_a","writable":true},{"name":"token_vault_one_a","writable":true},{"name":"token_owner_account_one_b","writable":true},{"name":"token_vault_one_b","writable":true},{"name":"token_owner_account_two_a","writable":true},{"name":"token_vault_two_a","writable":true},{"name":"token_owner_account_two_b","writable":true},{"name":"token_vault_two_b","writable":true},{"name":"tick_array_one_0","writable":true},{"name":"tick_array_one_1","writable":true},{"name":"tick_array_one_2","writable":true},{"name":"tick_array_two_0","writable":true},{"name":"tick_array_two_1","writable":true},{"name":"tick_array_two_2","writable":true},{"name":"oracle_one","pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"whirlpool_one"}]}},{"name":"oracle_two","pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"whirlpool_two"}]}}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"amount_specified_is_input","type":"bool"},{"name":"a_to_b_one","type":"bool"},{"name":"a_to_b_two","type":"bool"},{"name":"sqrt_price_limit_one","type":"u128"},{"name":"sqrt_price_limit_two","type":"u128"}]},{"name":"two_hop_swap_v2","discriminator":[186,143,209,29,254,2,194,117],"accounts":[{"name":"whirlpool_one","writable":true},{"name":"whirlpool_two","writable":true},{"name":"token_mint_input"},{"name":"token_mint_intermediate"},{"name":"token_mint_output"},{"name":"token_program_input"},{"name":"token_program_intermediate"},{"name":"token_program_output"},{"name":"token_owner_account_input","writable":true},{"name":"token_vault_one_input","writable":true},{"name":"token_vault_one_intermediate","writable":true},{"name":"token_vault_two_intermediate","writable":true},{"name":"token_vault_two_output","writable":true},{"name":"token_owner_account_output","writable":true},{"name":"token_authority","signer":true},{"name":"tick_array_one_0","writable":true},{"name":"tick_array_one_1","writable":true},{"name":"tick_array_one_2","writable":true},{"name":"tick_array_two_0","writable":true},{"name":"tick_array_two_1","writable":true},{"name":"tick_array_two_2","writable":true},{"name":"oracle_one","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"whirlpool_one"}]}},{"name":"oracle_two","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,114,97,99,108,101]},{"kind":"account","path":"whirlpool_two"}]}},{"name":"memo_program","address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"amount_specified_is_input","type":"bool"},{"name":"a_to_b_one","type":"bool"},{"name":"a_to_b_two","type":"bool"},{"name":"sqrt_price_limit_one","type":"u128"},{"name":"sqrt_price_limit_two","type":"u128"},{"name":"remaining_accounts_info","type":{"option":{"defined":{"name":"RemainingAccountsInfo"}}}}]},{"name":"update_fees_and_rewards","discriminator":[154,230,250,13,236,209,75,223],"accounts":[{"name":"whirlpool","writable":true,"relations":["position"]},{"name":"position","writable":true},{"name":"tick_array_lower"},{"name":"tick_array_upper"}],"args":[]}],"accounts":[{"name":"AdaptiveFeeTier","discriminator":[147,16,144,116,47,146,149,46]},{"name":"DynamicTickArray","discriminator":[17,216,246,142,225,199,218,56]},{"name":"FeeTier","discriminator":[56,75,159,76,142,68,190,105]},{"name":"LockConfig","discriminator":[106,47,238,159,124,12,160,192]},{"name":"Oracle","discriminator":[139,194,131,179,140,179,229,244]},{"name":"Position","discriminator":[170,188,143,228,122,64,247,208]},{"name":"PositionBundle","discriminator":[129,169,175,65,185,95,32,100]},{"name":"TickArray","discriminator":[69,97,189,190,110,7,66,187]},{"name":"TokenBadge","discriminator":[116,219,204,229,249,116,255,150]},{"name":"Whirlpool","discriminator":[63,149,209,12,225,128,99,9]},{"name":"WhirlpoolsConfig","discriminator":[157,20,49,224,217,87,193,254]},{"name":"WhirlpoolsConfigExtension","discriminator":[2,99,215,163,240,26,153,58]}],"events":[{"name":"LiquidityDecreased","discriminator":[166,1,36,71,112,202,181,171]},{"name":"LiquidityIncreased","discriminator":[30,7,144,181,102,254,155,161]},{"name":"LiquidityRepositioned","discriminator":[95,130,181,132,251,50,195,38]},{"name":"PoolInitialized","discriminator":[100,118,173,87,12,198,254,229]},{"name":"PositionOpened","discriminator":[237,175,243,230,147,117,101,121]},{"name":"Traded","discriminator":[225,202,73,175,147,43,160,150]}],"errors":[{"code":6000,"name":"InvalidEnum","msg":"Enum value could not be converted"},{"code":6001,"name":"InvalidStartTick","msg":"Invalid start tick index provided."},{"code":6002,"name":"TickArrayExistInPool","msg":"Tick-array already exists in this whirlpool"},{"code":6003,"name":"TickArrayIndexOutofBounds","msg":"Attempt to search for a tick-array failed"},{"code":6004,"name":"InvalidTickSpacing","msg":"Tick-spacing is not supported"},{"code":6005,"name":"ClosePositionNotEmpty","msg":"Position is not empty It cannot be closed"},{"code":6006,"name":"DivideByZero","msg":"Unable to divide by zero"},{"code":6007,"name":"NumberCastError","msg":"Unable to cast number into BigInt"},{"code":6008,"name":"NumberDownCastError","msg":"Unable to down cast number"},{"code":6009,"name":"TickNotFound","msg":"Tick not found within tick array"},{"code":6010,"name":"InvalidTickIndex","msg":"Provided tick index is either out of bounds or uninitializable"},{"code":6011,"name":"SqrtPriceOutOfBounds","msg":"Provided sqrt price out of bounds"},{"code":6012,"name":"LiquidityZero","msg":"Liquidity amount must be greater than zero"},{"code":6013,"name":"LiquidityTooHigh","msg":"Liquidity amount must be less than i64::MAX"},{"code":6014,"name":"LiquidityOverflow","msg":"Liquidity overflow"},{"code":6015,"name":"LiquidityUnderflow","msg":"Liquidity underflow"},{"code":6016,"name":"LiquidityNetError","msg":"Tick liquidity net underflowed or overflowed"},{"code":6017,"name":"TokenMaxExceeded","msg":"Exceeded token max"},{"code":6018,"name":"TokenMinSubceeded","msg":"Did not meet token min"},{"code":6019,"name":"MissingOrInvalidDelegate","msg":"Position token account has a missing or invalid delegate"},{"code":6020,"name":"InvalidPositionTokenAmount","msg":"Position token amount must be 1"},{"code":6021,"name":"InvalidTimestampConversion","msg":"Timestamp should be convertible from i64 to u64"},{"code":6022,"name":"InvalidTimestamp","msg":"Timestamp should be greater than the last updated timestamp"},{"code":6023,"name":"InvalidTickArraySequence","msg":"Invalid tick array sequence provided for instruction."},{"code":6024,"name":"InvalidTokenMintOrder","msg":"Token Mint in wrong order"},{"code":6025,"name":"RewardNotInitialized","msg":"Reward not initialized"},{"code":6026,"name":"InvalidRewardIndex","msg":"Invalid reward index"},{"code":6027,"name":"RewardVaultAmountInsufficient","msg":"Reward vault requires amount to support emissions for at least one day"},{"code":6028,"name":"FeeRateMaxExceeded","msg":"Exceeded max fee rate"},{"code":6029,"name":"ProtocolFeeRateMaxExceeded","msg":"Exceeded max protocol fee rate"},{"code":6030,"name":"MultiplicationShiftRightOverflow","msg":"Multiplication with shift right overflow"},{"code":6031,"name":"MulDivOverflow","msg":"Muldiv overflow"},{"code":6032,"name":"MulDivInvalidInput","msg":"Invalid div_u256 input"},{"code":6033,"name":"MultiplicationOverflow","msg":"Multiplication overflow"},{"code":6034,"name":"InvalidSqrtPriceLimitDirection","msg":"Provided SqrtPriceLimit not in the same direction as the swap."},{"code":6035,"name":"ZeroTradableAmount","msg":"There are no tradable amount to swap."},{"code":6036,"name":"AmountOutBelowMinimum","msg":"Amount out below minimum threshold"},{"code":6037,"name":"AmountInAboveMaximum","msg":"Amount in above maximum threshold"},{"code":6038,"name":"TickArraySequenceInvalidIndex","msg":"Invalid index for tick array sequence"},{"code":6039,"name":"AmountCalcOverflow","msg":"Amount calculated overflows"},{"code":6040,"name":"AmountRemainingOverflow","msg":"Amount remaining overflows"},{"code":6041,"name":"InvalidIntermediaryMint","msg":"Invalid intermediary mint"},{"code":6042,"name":"DuplicateTwoHopPool","msg":"Duplicate two hop pool"},{"code":6043,"name":"InvalidBundleIndex","msg":"Bundle index is out of bounds"},{"code":6044,"name":"BundledPositionAlreadyOpened","msg":"Position has already been opened"},{"code":6045,"name":"BundledPositionAlreadyClosed","msg":"Position has already been closed"},{"code":6046,"name":"PositionBundleNotDeletable","msg":"Unable to delete PositionBundle with open positions"},{"code":6047,"name":"UnsupportedTokenMint","msg":"Token mint has unsupported attributes"},{"code":6048,"name":"RemainingAccountsInvalidSlice","msg":"Invalid remaining accounts"},{"code":6049,"name":"RemainingAccountsInsufficient","msg":"Insufficient remaining accounts"},{"code":6050,"name":"NoExtraAccountsForTransferHook","msg":"Unable to call transfer hook without extra accounts"},{"code":6051,"name":"IntermediateTokenAmountMismatch","msg":"Output and input amount mismatch"},{"code":6052,"name":"TransferFeeCalculationError","msg":"Transfer fee calculation failed"},{"code":6053,"name":"RemainingAccountsDuplicatedAccountsType","msg":"Same accounts type is provided more than once"},{"code":6054,"name":"FullRangeOnlyPool","msg":"This whirlpool only supports full-range positions"},{"code":6055,"name":"TooManySupplementalTickArrays","msg":"Too many supplemental tick arrays provided"},{"code":6056,"name":"DifferentWhirlpoolTickArrayAccount","msg":"TickArray account for different whirlpool provided"},{"code":6057,"name":"PartialFillError","msg":"Trade resulted in partial fill"},{"code":6058,"name":"PositionNotLockable","msg":"Position is not lockable"},{"code":6059,"name":"OperationNotAllowedOnLockedPosition","msg":"Operation not allowed on locked position"},{"code":6060,"name":"SameTickRangeNotAllowed","msg":"Cannot reset position range with same tick range"},{"code":6061,"name":"InvalidAdaptiveFeeConstants","msg":"Invalid adaptive fee constants"},{"code":6062,"name":"InvalidFeeTierIndex","msg":"Invalid fee tier index"},{"code":6063,"name":"InvalidTradeEnableTimestamp","msg":"Invalid trade enable timestamp"},{"code":6064,"name":"TradeIsNotEnabled","msg":"Trade is not enabled yet"},{"code":6065,"name":"RentCalculationError","msg":"Rent calculation error"},{"code":6066,"name":"FeatureIsNotEnabled","msg":"Feature is not enabled"},{"code":6067,"name":"PositionWithTokenExtensionsRequired","msg":"This whirlpool only supports open_position_with_token_extensions instruction"},{"code":6068,"name":"AdaptiveFeeConstantsUnchanged","msg":"Provided adaptive fee constants are unchanged"},{"code":6069,"name":"PriceSlippageOutOfBounds","msg":"Price outside slippage bounds"}],"types":[{"name":"AccountsType","type":{"kind":"enum","variants":[{"name":"TransferHookA"},{"name":"TransferHookB"},{"name":"TransferHookReward"},{"name":"TransferHookInput"},{"name":"TransferHookIntermediate"},{"name":"TransferHookOutput"},{"name":"SupplementalTickArrays"},{"name":"SupplementalTickArraysOne"},{"name":"SupplementalTickArraysTwo"},{"name":"TransferHookDepositA"},{"name":"TransferHookDepositB"},{"name":"TransferHookWithdrawalA"},{"name":"TransferHookWithdrawalB"}]}},{"name":"AdaptiveFeeConstants","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"adaptive_fee_control_factor","type":"u32"},{"name":"max_volatility_accumulator","type":"u32"},{"name":"tick_group_size","type":"u16"},{"name":"major_swap_threshold_ticks","type":"u16"},{"name":"reserved","type":{"array":["u8",16]}}]}},{"name":"AdaptiveFeeTier","type":{"kind":"struct","fields":[{"name":"whirlpools_config","type":"pubkey"},{"name":"fee_tier_index","type":"u16"},{"name":"tick_spacing","type":"u16"},{"name":"initialize_pool_authority","type":"pubkey"},{"name":"delegated_fee_authority","type":"pubkey"},{"name":"default_base_fee_rate","type":"u16"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"adaptive_fee_control_factor","type":"u32"},{"name":"max_volatility_accumulator","type":"u32"},{"name":"tick_group_size","type":"u16"},{"name":"major_swap_threshold_ticks","type":"u16"}]}},{"name":"AdaptiveFeeVariables","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"last_reference_update_timestamp","type":"u64"},{"name":"last_major_swap_timestamp","type":"u64"},{"name":"volatility_reference","type":"u32"},{"name":"tick_group_index_reference","type":"i32"},{"name":"volatility_accumulator","type":"u32"},{"name":"reserved","type":{"array":["u8",16]}}]}},{"name":"ConfigFeatureFlag","type":{"kind":"enum","variants":[{"name":"TokenBadge","fields":["bool"]}]}},{"name":"DynamicTick","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Initialized","fields":[{"defined":{"name":"DynamicTickData"}}]}]}},{"name":"DynamicTickArray","type":{"kind":"struct","fields":[{"name":"start_tick_index","type":"i32"},{"name":"whirlpool","type":"pubkey"},{"name":"tick_bitmap","type":"u128"},{"name":"ticks","type":{"array":[{"defined":{"name":"DynamicTick"}},88]}}]}},{"name":"DynamicTickData","type":{"kind":"struct","fields":[{"name":"liquidity_net","type":"i128"},{"name":"liquidity_gross","type":"u128"},{"name":"fee_growth_outside_a","type":"u128"},{"name":"fee_growth_outside_b","type":"u128"},{"name":"reward_growths_outside","type":{"array":["u128",3]}}]}},{"name":"FeeTier","type":{"kind":"struct","fields":[{"name":"whirlpools_config","type":"pubkey"},{"name":"tick_spacing","type":"u16"},{"name":"default_fee_rate","type":"u16"}]}},{"name":"IncreaseLiquidityMethod","type":{"kind":"enum","variants":[{"name":"ByTokenAmounts","fields":[{"name":"token_max_a","type":"u64"},{"name":"token_max_b","type":"u64"},{"name":"min_sqrt_price","type":"u128"},{"name":"max_sqrt_price","type":"u128"}]}]}},{"name":"LiquidityDecreased","type":{"kind":"struct","fields":[{"name":"whirlpool","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"token_a_amount","type":"u64"},{"name":"token_b_amount","type":"u64"},{"name":"token_a_transfer_fee","type":"u64"},{"name":"token_b_transfer_fee","type":"u64"}]}},{"name":"LiquidityIncreased","type":{"kind":"struct","fields":[{"name":"whirlpool","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"token_a_amount","type":"u64"},{"name":"token_b_amount","type":"u64"},{"name":"token_a_transfer_fee","type":"u64"},{"name":"token_b_transfer_fee","type":"u64"}]}},{"name":"LiquidityRepositioned","type":{"kind":"struct","fields":[{"name":"whirlpool","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"existing_range_tick_lower_index","type":"i32"},{"name":"existing_range_tick_upper_index","type":"i32"},{"name":"new_range_tick_lower_index","type":"i32"},{"name":"new_range_tick_upper_index","type":"i32"},{"name":"existing_range_liquidity","type":"u128"},{"name":"new_range_liquidity","type":"u128"},{"name":"existing_range_token_a_amount","type":"u64"},{"name":"existing_range_token_b_amount","type":"u64"},{"name":"new_range_token_a_amount","type":"u64"},{"name":"new_range_token_b_amount","type":"u64"},{"name":"token_a_transfer_amount","type":"u64"},{"name":"token_a_transfer_fee","type":"u64"},{"name":"is_token_a_transfer_from_owner","type":"bool"},{"name":"token_b_transfer_amount","type":"u64"},{"name":"token_b_transfer_fee","type":"u64"},{"name":"is_token_b_transfer_from_owner","type":"bool"}]}},{"name":"LockConfig","type":{"kind":"struct","fields":[{"name":"position","type":"pubkey"},{"name":"position_owner","type":"pubkey"},{"name":"whirlpool","type":"pubkey"},{"name":"locked_timestamp","type":"u64"},{"name":"lock_type","type":{"defined":{"name":"LockTypeLabel"}}}]}},{"name":"LockType","type":{"kind":"enum","variants":[{"name":"Permanent"}]}},{"name":"LockTypeLabel","type":{"kind":"enum","variants":[{"name":"Permanent"}]}},{"name":"OpenPositionBumps","type":{"kind":"struct","fields":[{"name":"position_bump","type":"u8"}]}},{"name":"OpenPositionWithMetadataBumps","type":{"kind":"struct","fields":[{"name":"position_bump","type":"u8"},{"name":"metadata_bump","type":"u8"}]}},{"name":"Oracle","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"whirlpool","type":"pubkey"},{"name":"trade_enable_timestamp","type":"u64"},{"name":"adaptive_fee_constants","type":{"defined":{"name":"AdaptiveFeeConstants"}}},{"name":"adaptive_fee_variables","type":{"defined":{"name":"AdaptiveFeeVariables"}}},{"name":"reserved","type":{"array":["u8",128]}}]}},{"name":"PoolInitialized","type":{"kind":"struct","fields":[{"name":"whirlpool","type":"pubkey"},{"name":"whirlpools_config","type":"pubkey"},{"name":"token_mint_a","type":"pubkey"},{"name":"token_mint_b","type":"pubkey"},{"name":"tick_spacing","type":"u16"},{"name":"token_program_a","type":"pubkey"},{"name":"token_program_b","type":"pubkey"},{"name":"decimals_a","type":"u8"},{"name":"decimals_b","type":"u8"},{"name":"initial_sqrt_price","type":"u128"}]}},{"name":"Position","type":{"kind":"struct","fields":[{"name":"whirlpool","type":"pubkey"},{"name":"position_mint","type":"pubkey"},{"name":"liquidity","type":"u128"},{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"fee_growth_checkpoint_a","type":"u128"},{"name":"fee_owed_a","type":"u64"},{"name":"fee_growth_checkpoint_b","type":"u128"},{"name":"fee_owed_b","type":"u64"},{"name":"reward_infos","type":{"array":[{"defined":{"name":"PositionRewardInfo"}},3]}}]}},{"name":"PositionBundle","type":{"kind":"struct","fields":[{"name":"position_bundle_mint","type":"pubkey"},{"name":"position_bitmap","type":{"array":["u8",32]}}]}},{"name":"PositionOpened","type":{"kind":"struct","fields":[{"name":"whirlpool","type":"pubkey"},{"name":"position","type":"pubkey"},{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"}]}},{"name":"PositionRewardInfo","type":{"kind":"struct","fields":[{"name":"growth_inside_checkpoint","type":"u128"},{"name":"amount_owed","type":"u64"}]}},{"name":"RemainingAccountsInfo","type":{"kind":"struct","fields":[{"name":"slices","type":{"vec":{"defined":{"name":"RemainingAccountsSlice"}}}}]}},{"name":"RemainingAccountsSlice","type":{"kind":"struct","fields":[{"name":"accounts_type","type":{"defined":{"name":"AccountsType"}}},{"name":"length","type":"u8"}]}},{"name":"RepositionLiquidityMethod","type":{"kind":"enum","variants":[{"name":"ByLiquidity","fields":[{"name":"new_liquidity_amount","type":"u128"},{"name":"existing_range_token_min_a","type":"u64"},{"name":"existing_range_token_min_b","type":"u64"},{"name":"new_range_token_max_a","type":"u64"},{"name":"new_range_token_max_b","type":"u64"}]}]}},{"name":"Tick","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"initialized","type":"bool"},{"name":"liquidity_net","type":"i128"},{"name":"liquidity_gross","type":"u128"},{"name":"fee_growth_outside_a","type":"u128"},{"name":"fee_growth_outside_b","type":"u128"},{"name":"reward_growths_outside","type":{"array":["u128",3]}}]}},{"name":"TickArray","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"start_tick_index","type":"i32"},{"name":"ticks","type":{"array":[{"defined":{"name":"Tick"}},88]}},{"name":"whirlpool","type":"pubkey"}]}},{"name":"TokenBadge","type":{"kind":"struct","fields":[{"name":"whirlpools_config","type":"pubkey"},{"name":"token_mint","type":"pubkey"},{"name":"attribute_require_non_transferable_position","type":"bool"}]}},{"name":"TokenBadgeAttribute","type":{"kind":"enum","variants":[{"name":"RequireNonTransferablePosition","fields":["bool"]}]}},{"name":"Traded","type":{"kind":"struct","fields":[{"name":"whirlpool","type":"pubkey"},{"name":"a_to_b","type":"bool"},{"name":"pre_sqrt_price","type":"u128"},{"name":"post_sqrt_price","type":"u128"},{"name":"input_amount","type":"u64"},{"name":"output_amount","type":"u64"},{"name":"input_transfer_fee","type":"u64"},{"name":"output_transfer_fee","type":"u64"},{"name":"lp_fee","type":"u64"},{"name":"protocol_fee","type":"u64"}]}},{"name":"Whirlpool","type":{"kind":"struct","fields":[{"name":"whirlpools_config","type":"pubkey"},{"name":"whirlpool_bump","type":{"array":["u8",1]}},{"name":"tick_spacing","type":"u16"},{"name":"fee_tier_index_seed","type":{"array":["u8",2]}},{"name":"fee_rate","type":"u16"},{"name":"protocol_fee_rate","type":"u16"},{"name":"liquidity","type":"u128"},{"name":"sqrt_price","type":"u128"},{"name":"tick_current_index","type":"i32"},{"name":"protocol_fee_owed_a","type":"u64"},{"name":"protocol_fee_owed_b","type":"u64"},{"name":"token_mint_a","type":"pubkey"},{"name":"token_vault_a","type":"pubkey"},{"name":"fee_growth_global_a","type":"u128"},{"name":"token_mint_b","type":"pubkey"},{"name":"token_vault_b","type":"pubkey"},{"name":"fee_growth_global_b","type":"u128"},{"name":"reward_last_updated_timestamp","type":"u64"},{"name":"reward_infos","type":{"array":[{"defined":{"name":"WhirlpoolRewardInfo"}},3]}}]}},{"name":"WhirlpoolBumps","type":{"kind":"struct","fields":[{"name":"whirlpool_bump","type":"u8"}]}},{"name":"WhirlpoolRewardInfo","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"vault","type":"pubkey"},{"name":"extension","type":{"array":["u8",32]}},{"name":"emissions_per_second_x64","type":"u128"},{"name":"growth_global_x64","type":"u128"}]}},{"name":"WhirlpoolsConfig","type":{"kind":"struct","fields":[{"name":"fee_authority","type":"pubkey"},{"name":"collect_protocol_fees_authority","type":"pubkey"},{"name":"reward_emissions_super_authority","type":"pubkey"},{"name":"default_protocol_fee_rate","type":"u16"},{"name":"feature_flags","type":"u16"}]}},{"name":"WhirlpoolsConfigExtension","type":{"kind":"struct","fields":[{"name":"whirlpools_config","type":"pubkey"},{"name":"config_extension_authority","type":"pubkey"},{"name":"token_badge_authority","type":"pubkey"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/pancakeswap.HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq.json b/migration/khadhroony-bot2-reference/idls/pancakeswap.HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq.json new file mode 100644 index 0000000..ca82772 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/pancakeswap.HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq.json @@ -0,0 +1 @@ +{"address":"HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq","metadata":{"name":"amm_v3","version":"0.1.0","spec":"0.1.0","description":"Anchor client and source for Pancake concentrated liquidity AMM"},"instructions":[{"name":"close_position","docs":["Close the user's position and NFT account. If the NFT mint belongs to token2022, it will also be closed and the funds returned to the NFT owner.","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[123,134,81,0,49,68,98,98],"accounts":[{"name":"nft_owner","docs":["The position nft owner"],"writable":true,"signer":true},{"name":"position_nft_mint","docs":["Mint address bound to the personal position."],"writable":true},{"name":"position_nft_account","docs":["User token account where position NFT be minted to"],"writable":true},{"name":"personal_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"system_program","docs":["System program to close the position state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Token/Token2022 program to close token/mint account"]}],"args":[]},{"name":"collect_fund_fee","docs":["Collect the fund fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[167,138,78,149,223,194,6,126],"accounts":[{"name":"owner","docs":["Only admin or fund_owner can collect fee now"],"signer":true},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores fund_owner"]},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_protocol_fee","docs":["Collect the protocol fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[136,136,252,221,194,66,126,89],"accounts":[{"name":"owner","docs":["Only admin or config owner can collect fee now"],"signer":true},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores owner"]},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_remaining_rewards","docs":["Collect remaining reward token for reward founder","","# Arguments","","* `ctx`- The context of accounts","* `reward_index` - the index to reward info",""],"discriminator":[18,237,166,197,34,16,213,144],"accounts":[{"name":"reward_funder","docs":["The founder who init reward info previously"],"signer":true},{"name":"funder_token_account","docs":["The funder's reward token account"],"writable":true},{"name":"pool_state","docs":["Set reward for this pool"],"writable":true},{"name":"reward_token_vault","docs":["Reward vault transfer remaining token to founder token account"]},{"name":"reward_vault_mint","docs":["The mint of reward token vault"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"create_amm_config","docs":["# Arguments","","* `ctx`- The accounts needed by instruction.","* `index` - The index of amm config, there may be multiple config.","* `tick_spacing` - The tickspacing binding with config, cannot be changed.","* `trade_fee_rate` - Trade fee rate, can be changed.","* `protocol_fee_rate` - The rate of protocol fee within trade fee.","* `fund_fee_rate` - The rate of fund fee within trade fee.",""],"discriminator":[137,52,237,212,215,117,108,104],"accounts":[{"name":"owner","docs":["Address to be set as protocol owner."],"writable":true,"signer":true,"address":"DmwXqqK5Zuj619au6q2Jx3TMr9ZV1837uxJcEwyvXVtV"},{"name":"amm_config","docs":["Initialize config state account to store protocol owner address and fee rates."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,109,109,95,99,111,110,102,105,103]},{"kind":"arg","path":"index"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"u16"},{"name":"tick_spacing","type":"u16"},{"name":"trade_fee_rate","type":"u32"},{"name":"protocol_fee_rate","type":"u32"},{"name":"fund_fee_rate","type":"u32"}]},{"name":"create_operation_account","docs":["Creates an operation account for the program","","# Arguments","","* `ctx`- The context of accounts",""],"discriminator":[63,87,148,33,109,35,8,104],"accounts":[{"name":"owner","docs":["Address to be set as operation account owner."],"writable":true,"signer":true,"address":"DmwXqqK5Zuj619au6q2Jx3TMr9ZV1837uxJcEwyvXVtV"},{"name":"operation_state","docs":["Initialize operation state account to store operation owner address and white list mint."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_permissionless_farm_switch","docs":["Creates a permissionless farm switch account for the program","","# Arguments","","* `ctx`- The context of accounts",""],"discriminator":[88,242,198,70,241,6,196,251],"accounts":[{"name":"owner","writable":true,"signer":true,"address":"DmwXqqK5Zuj619au6q2Jx3TMr9ZV1837uxJcEwyvXVtV"},{"name":"permissionless_farm_switch","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,101,114,109,105,115,115,105,111,110,108,101,115,115,95,102,97,114,109,95,115,119,105,116,99,104]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_pool","docs":["Creates a pool for the given token pair and the initial price","","# Arguments","","* `ctx`- The context of accounts","* `sqrt_price_x64` - the initial sqrt price (amount_token_1 / amount_token_0) of the pool as a Q64.64","Note: The open_time must be smaller than the current block_timestamp on chain."],"discriminator":[233,146,209,142,207,104,64,188],"accounts":[{"name":"pool_creator","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"pool_state","docs":["Initialize an account to store the pool state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"token_mint_0"},{"kind":"account","path":"token_mint_1"}]}},{"name":"token_mint_0","docs":["Token_0 mint, the key must be smaller then token_1 mint."]},{"name":"token_mint_1","docs":["Token_1 mint"]},{"name":"token_vault_0","docs":["Token_0 vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_0"}]}},{"name":"token_vault_1","docs":["Token_1 vault for the pool"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_1"}]}},{"name":"observation_state","docs":["Initialize an account to store oracle observations"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"tick_array_bitmap","docs":["Initialize an account to store if a tick array is initialized."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,116,105,99,107,95,97,114,114,97,121,95,98,105,116,109,97,112,95,101,120,116,101,110,115,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"token_program_0","docs":["Spl token program or token program 2022"]},{"name":"token_program_1","docs":["Spl token program or token program 2022"]},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"sqrt_price_x64","type":"u128"},{"name":"open_time","type":"u64"}]},{"name":"create_support_mint_associated","docs":["Create support token22 mint account which can create pool and send rewards with ignoring the not support extensions."],"discriminator":[17,251,65,92,136,242,14,169],"accounts":[{"name":"owner","docs":["Address to be set as protocol owner."],"writable":true,"signer":true,"address":"DmwXqqK5Zuj619au6q2Jx3TMr9ZV1837uxJcEwyvXVtV"},{"name":"token_mint","docs":["Support token mint"]},{"name":"support_mint_associated","docs":["Initialize support mint state account to store support mint address and bump."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,117,112,112,111,114,116,95,109,105,110,116]},{"kind":"account","path":"token_mint"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"decrease_liquidity","docs":["#[deprecated(note = \"Use `decrease_liquidity_v2` instead.\")]","Decreases liquidity for an existing position","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The amount by which liquidity will be decreased","* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity","* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity",""],"discriminator":[160,38,208,111,104,91,44,1],"accounts":[{"name":"nft_owner","docs":["The position owner or delegated authority"],"signer":true},{"name":"nft_account","docs":["The token account for the tokenized position"]},{"name":"personal_position","docs":["Decrease liquidity for this position"],"writable":true},{"name":"pool_state","writable":true},{"name":"protocol_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"personal_position.tick_lower_index","account":"PersonalPositionState"},{"kind":"account","path":"personal_position.tick_upper_index","account":"PersonalPositionState"}]}},{"name":"token_vault_0","docs":["Token_0 vault"],"writable":true},{"name":"token_vault_1","docs":["Token_1 vault"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"recipient_token_account_0","docs":["The destination token account for receive amount_0"],"writable":true},{"name":"recipient_token_account_1","docs":["The destination token account for receive amount_1"],"writable":true},{"name":"token_program","docs":["SPL program to transfer out tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_min","type":"u64"},{"name":"amount_1_min","type":"u64"}]},{"name":"decrease_liquidity_v2","docs":["Decreases liquidity for an existing position, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The amount by which liquidity will be decreased","* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity","* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity",""],"discriminator":[58,127,188,62,79,82,196,96],"accounts":[{"name":"nft_owner","docs":["The position owner or delegated authority"],"signer":true},{"name":"nft_account","docs":["The token account for the tokenized position"]},{"name":"personal_position","docs":["Decrease liquidity for this position"],"writable":true},{"name":"pool_state","writable":true},{"name":"protocol_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"personal_position.tick_lower_index","account":"PersonalPositionState"},{"kind":"account","path":"personal_position.tick_upper_index","account":"PersonalPositionState"}]}},{"name":"token_vault_0","docs":["Token_0 vault"],"writable":true},{"name":"token_vault_1","docs":["Token_1 vault"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"recipient_token_account_0","docs":["The destination token account for receive amount_0"],"writable":true},{"name":"recipient_token_account_1","docs":["The destination token account for receive amount_1"],"writable":true},{"name":"token_program","docs":["SPL program to transfer out tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_min","type":"u64"},{"name":"amount_1_min","type":"u64"}]},{"name":"increase_liquidity","docs":["#[deprecated(note = \"Use `increase_liquidity_v2` instead.\")]","Increases liquidity for an existing position, with amount paid by `payer`","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The desired liquidity to be added, can't be zero","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check",""],"discriminator":[46,156,243,118,13,205,251,178],"accounts":[{"name":"nft_owner","docs":["Pays to mint the position"],"signer":true},{"name":"nft_account","docs":["The token account for nft"]},{"name":"pool_state","writable":true},{"name":"protocol_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"personal_position.tick_lower_index","account":"PersonalPositionState"},{"kind":"account","path":"personal_position.tick_upper_index","account":"PersonalPositionState"}]}},{"name":"personal_position","docs":["Increase liquidity for this position"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"token_account_0","docs":["The payer's token account for token_0"],"writable":true},{"name":"token_account_1","docs":["The token account spending token_1 to mint the position"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"}]},{"name":"increase_liquidity_v2","docs":["Increases liquidity for an existing position, with amount paid by `payer`, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The desired liquidity to be added, if zero, calculate liquidity base amount_0 or amount_1 according base_flag","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `base_flag` - must be specified if liquidity is zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[133,29,89,223,69,238,176,10],"accounts":[{"name":"nft_owner","docs":["Pays to mint the position"],"signer":true},{"name":"nft_account","docs":["The token account for nft"]},{"name":"pool_state","writable":true},{"name":"protocol_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"personal_position.tick_lower_index","account":"PersonalPositionState"},{"kind":"account","path":"personal_position.tick_upper_index","account":"PersonalPositionState"}]}},{"name":"personal_position","docs":["Increase liquidity for this position"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"token_account_0","docs":["The payer's token account for token_0"],"writable":true},{"name":"token_account_1","docs":["The token account spending token_1 to mint the position"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"initialize_reward","docs":["Initialize a reward info for a given pool and reward index","","# Arguments","","* `ctx`- The context of accounts","* `reward_index` - the index to reward info","* `open_time` - reward open timestamp","* `end_time` - reward end timestamp","* `emissions_per_second_x64` - Token reward per second are earned per unit of liquidity.",""],"discriminator":[95,135,192,196,242,129,230,68],"accounts":[{"name":"reward_funder","docs":["The founder deposit reward token to vault"],"writable":true,"signer":true},{"name":"funder_token_account","writable":true},{"name":"amm_config","docs":["For check the reward_funder authority"]},{"name":"pool_state","docs":["Set reward for this pool"],"writable":true},{"name":"operation_state","docs":["load info from the account to judge reward permission"],"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"permissionless_farm_switch","pda":{"seeds":[{"kind":"const","value":[112,101,114,109,105,115,115,105,111,110,108,101,115,115,95,102,97,114,109,95,115,119,105,116,99,104]}]}},{"name":"reward_token_mint","docs":["Reward mint"]},{"name":"reward_token_vault","docs":["A pda, reward vault"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,114,101,119,97,114,100,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"reward_token_mint"}]}},{"name":"reward_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"param","type":{"defined":{"name":"InitializeRewardParam"}}}]},{"name":"open_position","docs":["#[deprecated(note = \"Use `open_position_with_token22_nft` instead.\")]","Creates a new position wrapped in a NFT","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which include tick low","* `tick_array_upper_start_index` - The start index of tick array which include tick upper","* `liquidity` - The liquidity to be added","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check",""],"discriminator":[135,128,47,77,15,152,240,49],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["Token account where position NFT will be minted","This account created in the contract by cpi to avoid large stack variables"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"position_nft_owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_nft_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","docs":["To store metaplex metadata"],"writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position","docs":["Store the information of market marking in range"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_lower_index"},{"kind":"arg","path":"tick_upper_index"}]}},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_program","docs":["Program to create NFT metadata"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"}]},{"name":"open_position_v2","docs":["#[deprecated(note = \"Use `open_position_with_token22_nft` instead.\")]","Creates a new position wrapped in a NFT, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which include tick low","* `tick_array_upper_start_index` - The start index of tick array which include tick upper","* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `with_metadata` - The flag indicating whether to create NFT mint metadata","* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[77,184,74,214,112,86,241,199],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["Token account where position NFT will be minted"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"position_nft_owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_nft_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","docs":["To store metaplex metadata"],"writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position","docs":["Store the information of market marking in range"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_lower_index"},{"kind":"arg","path":"tick_upper_index"}]}},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_program","docs":["Program to create NFT metadata"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"token_program_2022","docs":["Program to create mint account and mint tokens"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"with_metadata","type":"bool"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"open_position_with_token22_nft","docs":["Creates a new position wrapped in a Token2022 NFT without relying on metadata_program and metadata_account, reduce the cost for user to create a personal position.","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which include tick low","* `tick_array_upper_start_index` - The start index of tick array which include tick upper","* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `with_metadata` - The flag indicating whether to create NFT mint metadata","* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[77,255,174,82,125,29,201,46],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address, initialize in contract"],"writable":true,"signer":true},{"name":"position_nft_account","writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position","docs":["Store the information of market marking in range"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_lower_index"},{"kind":"arg","path":"tick_upper_index"}]}},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to transfer for token account"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program_2022","docs":["Program to create NFT mint/token account and transfer for token22 account"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"with_metadata","type":"bool"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"set_reward_params","docs":["Reset reward param, start a new reward cycle or extend the current cycle.","","# Arguments","","* `ctx` - The context of accounts","* `reward_index` - The index of reward token in the pool.","* `emissions_per_second_x64` - The per second emission reward, when extend the current cycle,","new value can't be less than old value","* `open_time` - reward open timestamp, must be set when starting a new cycle","* `end_time` - reward end timestamp",""],"discriminator":[112,52,167,75,32,201,211,137],"accounts":[{"name":"authority","docs":["Address to be set as protocol owner. It pays to create factory state account."],"signer":true},{"name":"amm_config"},{"name":"pool_state","writable":true},{"name":"operation_state","docs":["load info from the account to judge reward permission"],"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"token_program","docs":["Token program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"reward_index","type":"u8"},{"name":"emissions_per_second_x64","type":"u128"},{"name":"open_time","type":"u64"},{"name":"end_time","type":"u64"}]},{"name":"swap","docs":["#[deprecated(note = \"Use `swap_v2` instead.\")]","Swaps one token for as much as possible of another token across a single pool","","# Arguments","","* `ctx` - The context of accounts","* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)","* `other_amount_threshold` - For slippage check","* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot","* `is_base_input` - swap base input or swap base output",""],"discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"tick_array","writable":true}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit_x64","type":"u128"},{"name":"is_base_input","type":"bool"}]},{"name":"swap_router_base_in","docs":["Swap token for as much as possible of another token across the path provided, base input","","# Arguments","","* `ctx` - The context of accounts","* `amount_in` - Token amount to be swapped in","* `amount_out_minimum` - Panic if output amount is below minimum amount. For slippage.",""],"discriminator":[69,125,115,218,245,186,242,196],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"input_token_account","docs":["The token account that pays input tokens for the swap"],"writable":true},{"name":"input_token_mint","docs":["The mint of input token"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["SPL program 2022 for token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["Memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"amount_in","type":"u64"},{"name":"amount_out_minimum","type":"u64"}]},{"name":"swap_v2","docs":["Swaps one token for as much as possible of another token across a single pool, support token program 2022","","# Arguments","","* `ctx` - The context of accounts","* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)","* `other_amount_threshold` - For slippage check","* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot","* `is_base_input` - swap base input or swap base output",""],"discriminator":[43,4,237,11,26,201,30,98],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["SPL program 2022 for token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["Memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"input_vault_mint","docs":["The mint of token vault 0"]},{"name":"output_vault_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit_x64","type":"u128"},{"name":"is_base_input","type":"bool"}]},{"name":"toggle_permissionless_farm_switch","docs":["Toggle the permissionless farm switch account","","# Arguments","","* `ctx`- The context of accounts","* `is_on`- Is the permissionless farm allowed or not",""],"discriminator":[150,112,54,233,238,161,7,86],"accounts":[{"name":"owner","writable":true,"signer":true,"address":"DmwXqqK5Zuj619au6q2Jx3TMr9ZV1837uxJcEwyvXVtV"},{"name":"permissionless_farm_switch","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,101,114,109,105,115,115,105,111,110,108,101,115,115,95,102,97,114,109,95,115,119,105,116,99,104]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"is_on","type":"bool"}]},{"name":"transfer_reward_owner","docs":["Transfer reward owner","","# Arguments","","* `ctx`- The context of accounts","* `new_owner`- new owner pubkey",""],"discriminator":[7,22,12,83,242,43,48,121],"accounts":[{"name":"authority","docs":["Address to be set as operation account owner."],"signer":true,"address":"DmwXqqK5Zuj619au6q2Jx3TMr9ZV1837uxJcEwyvXVtV"},{"name":"pool_state","writable":true}],"args":[{"name":"new_owner","type":"pubkey"}]},{"name":"update_amm_config","docs":["Updates the owner of the amm config","Must be called by the current owner or admin","","# Arguments","","* `ctx`- The context of accounts","* `trade_fee_rate`- The new trade fee rate of amm config, be set when `param` is 0","* `protocol_fee_rate`- The new protocol fee rate of amm config, be set when `param` is 1","* `fund_fee_rate`- The new fund fee rate of amm config, be set when `param` is 2","* `new_owner`- The config's new owner, be set when `param` is 3","* `new_fund_owner`- The config's new fund owner, be set when `param` is 4","* `param`- The value can be 0 | 1 | 2 | 3 | 4, otherwise will report a error",""],"discriminator":[49,60,174,136,154,28,116,200],"accounts":[{"name":"owner","docs":["The amm config owner or admin"],"signer":true,"address":"DmwXqqK5Zuj619au6q2Jx3TMr9ZV1837uxJcEwyvXVtV"},{"name":"amm_config","docs":["Amm config account to be changed"],"writable":true}],"args":[{"name":"param","type":"u8"},{"name":"value","type":"u32"}]},{"name":"update_operation_account","docs":["Update the operation account","","# Arguments","","* `ctx`- The context of accounts","* `param`- The value can be 0 | 1 | 2 | 3, otherwise will report a error","* `keys`- update operation owner when the `param` is 0","remove operation owner when the `param` is 1","update whitelist mint when the `param` is 2","remove whitelist mint when the `param` is 3",""],"discriminator":[127,70,119,40,188,227,61,7],"accounts":[{"name":"owner","docs":["Address to be set as operation account owner."],"signer":true,"address":"DmwXqqK5Zuj619au6q2Jx3TMr9ZV1837uxJcEwyvXVtV"},{"name":"operation_state","docs":["Initialize operation state account to store operation owner address and white list mint."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"param","type":"u8"},{"name":"keys","type":{"vec":"pubkey"}}]},{"name":"update_pool_status","docs":["Update pool status for given value","","# Arguments","","* `ctx`- The context of accounts","* `status` - The value of status",""],"discriminator":[130,87,108,6,46,224,117,123],"accounts":[{"name":"authority","signer":true,"address":"DmwXqqK5Zuj619au6q2Jx3TMr9ZV1837uxJcEwyvXVtV"},{"name":"pool_state","writable":true}],"args":[{"name":"status","type":"u8"}]},{"name":"update_reward_infos","docs":["Update rewards info of the given pool, can be called for everyone","","# Arguments","","* `ctx`- The context of accounts",""],"discriminator":[163,172,224,52,11,154,106,223],"accounts":[{"name":"pool_state","docs":["The liquidity pool for which reward info to update"],"writable":true}],"args":[]}],"accounts":[{"name":"AmmConfig","discriminator":[218,244,33,104,203,203,43,111]},{"name":"ObservationState","discriminator":[122,174,197,53,129,9,165,132]},{"name":"OperationState","discriminator":[19,236,58,237,81,222,183,252]},{"name":"PermissionlessFarmSwitch","discriminator":[37,92,147,78,250,100,147,137]},{"name":"PersonalPositionState","discriminator":[70,111,150,126,230,15,25,117]},{"name":"PoolState","discriminator":[247,237,227,245,215,195,222,70]},{"name":"ProtocolPositionState","discriminator":[100,226,145,99,146,218,160,106]},{"name":"SupportMintAssociated","discriminator":[134,40,183,79,12,112,162,53]},{"name":"TickArrayBitmapExtension","discriminator":[60,150,36,219,97,128,139,153]},{"name":"TickArrayState","discriminator":[192,155,85,205,49,249,129,42]}],"events":[{"name":"CollectPersonalFeeEvent","discriminator":[166,174,105,192,81,161,83,105]},{"name":"CollectProtocolFeeEvent","discriminator":[206,87,17,79,45,41,213,61]},{"name":"ConfigChangeEvent","discriminator":[247,189,7,119,106,112,95,151]},{"name":"CreatePersonalPositionEvent","discriminator":[100,30,87,249,196,223,154,206]},{"name":"DecreaseLiquidityEvent","discriminator":[58,222,86,58,68,50,85,56]},{"name":"IncreaseLiquidityEvent","discriminator":[49,79,105,212,32,34,30,84]},{"name":"LiquidityCalculateEvent","discriminator":[237,112,148,230,57,84,180,162]},{"name":"LiquidityChangeEvent","discriminator":[126,240,175,206,158,88,153,107]},{"name":"PoolCreatedEvent","discriminator":[25,94,75,47,112,99,53,63]},{"name":"SwapEvent","discriminator":[64,198,205,232,38,8,113,226]},{"name":"UpdateRewardInfosEvent","discriminator":[109,127,186,78,114,65,37,236]}],"errors":[{"code":6000,"name":"LOK","msg":"LOK"},{"code":6001,"name":"NotApproved","msg":"Not approved"},{"code":6002,"name":"InvalidUpdateConfigFlag","msg":"invalid update amm config flag"},{"code":6003,"name":"AccountLack","msg":"Account lack"},{"code":6004,"name":"ClosePositionErr","msg":"Remove liquitity, collect fees owed and reward then you can close position account"},{"code":6005,"name":"ZeroMintAmount","msg":"Minting amount should be greater than 0"},{"code":6006,"name":"InvalidTickIndex","msg":"Tick out of range"},{"code":6007,"name":"TickInvalidOrder","msg":"The lower tick must be below the upper tick"},{"code":6008,"name":"TickLowerOverflow","msg":"The tick must be greater, or equal to the minimum tick(-443636)"},{"code":6009,"name":"TickUpperOverflow","msg":"The tick must be lesser than, or equal to the maximum tick(443636)"},{"code":6010,"name":"TickAndSpacingNotMatch","msg":"tick % tick_spacing must be zero"},{"code":6011,"name":"InvalidTickArray","msg":"Invalid tick array account"},{"code":6012,"name":"InvalidTickArrayBoundary","msg":"Invalid tick array boundary"},{"code":6013,"name":"SqrtPriceLimitOverflow","msg":"Square root price limit overflow"},{"code":6014,"name":"SqrtPriceX64","msg":"sqrt_price_x64 out of range"},{"code":6015,"name":"LiquiditySubValueErr","msg":"Liquidity sub delta L must be smaller than before"},{"code":6016,"name":"LiquidityAddValueErr","msg":"Liquidity add delta L must be greater, or equal to before"},{"code":6017,"name":"InvalidLiquidity","msg":"Invalid liquidity when update position"},{"code":6018,"name":"ForbidBothZeroForSupplyLiquidity","msg":"Both token amount must not be zero while supply liquidity"},{"code":6019,"name":"LiquidityInsufficient","msg":"Liquidity insufficient"},{"code":6020,"name":"TransactionTooOld","msg":"Transaction too old"},{"code":6021,"name":"PriceSlippageCheck","msg":"Price slippage check"},{"code":6022,"name":"TooLittleOutputReceived","msg":"Too little output received"},{"code":6023,"name":"TooMuchInputPaid","msg":"Too much input paid"},{"code":6024,"name":"ZeroAmountSpecified","msg":"Swap special amount can not be zero"},{"code":6025,"name":"InvalidInputPoolVault","msg":"Input pool vault is invalid"},{"code":6026,"name":"TooSmallInputOrOutputAmount","msg":"Swap input or output amount is too small"},{"code":6027,"name":"NotEnoughTickArrayAccount","msg":"Not enought tick array account"},{"code":6028,"name":"InvalidFirstTickArrayAccount","msg":"Invalid first tick array account"},{"code":6029,"name":"InvalidRewardIndex","msg":"Invalid reward index"},{"code":6030,"name":"FullRewardInfo","msg":"The init reward token reach to the max"},{"code":6031,"name":"RewardTokenAlreadyInUse","msg":"The init reward token already in use"},{"code":6032,"name":"ExceptRewardMint","msg":"The reward tokens must contain one of pool vault mint except the last reward"},{"code":6033,"name":"InvalidRewardInitParam","msg":"Invalid reward init param"},{"code":6034,"name":"InvalidRewardDesiredAmount","msg":"Invalid collect reward desired amount"},{"code":6035,"name":"InvalidRewardInputAccountNumber","msg":"Invalid collect reward input account number"},{"code":6036,"name":"InvalidRewardPeriod","msg":"Invalid reward period"},{"code":6037,"name":"NotApproveUpdateRewardEmissiones","msg":"Modification of emissiones is allowed within 72 hours from the end of the previous cycle"},{"code":6038,"name":"UnInitializedRewardInfo","msg":"uninitialized reward info"},{"code":6039,"name":"NotSupportMint","msg":"Not support token_2022 mint extension"},{"code":6040,"name":"MissingTickArrayBitmapExtensionAccount","msg":"Missing tickarray bitmap extension account"},{"code":6041,"name":"InsufficientLiquidityForDirection","msg":"Insufficient liquidity for this direction"},{"code":6042,"name":"MaxTokenOverflow","msg":"Max token overflow"},{"code":6043,"name":"CalculateOverflow","msg":"Calculate overflow"},{"code":6044,"name":"TransferFeeCalculateNotMatch","msg":"TransferFee calculate not match"}],"types":[{"name":"AmmConfig","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"index","type":"u16"},{"name":"owner","docs":["Address of the protocol owner"],"type":"pubkey"},{"name":"protocol_fee_rate","docs":["The protocol fee"],"type":"u32"},{"name":"trade_fee_rate","docs":["The trade fee, denominated in hundredths of a bip (10^-6)"],"type":"u32"},{"name":"tick_spacing","docs":["The tick spacing"],"type":"u16"},{"name":"fund_fee_rate","docs":["The fund fee, denominated in hundredths of a bip (10^-6)"],"type":"u32"},{"name":"padding_u32","type":"u32"},{"name":"fund_owner","type":"pubkey"},{"name":"padding","type":{"array":["u64",3]}}]}},{"name":"CollectPersonalFeeEvent","docs":["Emitted when tokens are collected for a position"],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which underlying tokens were collected"],"type":"pubkey"},{"name":"recipient_token_account_0","docs":["The token account that received the collected token_0 tokens"],"type":"pubkey"},{"name":"recipient_token_account_1","docs":["The token account that received the collected token_1 tokens"],"type":"pubkey"},{"name":"amount_0","docs":["The amount of token_0 owed to the position that was collected"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_1 owed to the position that was collected"],"type":"u64"}]}},{"name":"CollectProtocolFeeEvent","docs":["Emitted when the collected protocol fees are withdrawn by the factory owner"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool whose protocol fee is collected"],"type":"pubkey"},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"type":"pubkey"},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"type":"pubkey"},{"name":"amount_0","docs":["The amount of token_0 protocol fees that is withdrawn"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_0 protocol fees that is withdrawn"],"type":"u64"}]}},{"name":"ConfigChangeEvent","docs":["Emitted when create or update a config"],"type":{"kind":"struct","fields":[{"name":"index","type":"u16"},{"name":"owner","type":"pubkey"},{"name":"protocol_fee_rate","type":"u32"},{"name":"trade_fee_rate","type":"u32"},{"name":"tick_spacing","type":"u16"},{"name":"fund_fee_rate","type":"u32"},{"name":"fund_owner","type":"pubkey"}]}},{"name":"CreatePersonalPositionEvent","docs":["Emitted when create a new position"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for which liquidity was added"],"type":"pubkey"},{"name":"minter","docs":["The address that create the position"],"type":"pubkey"},{"name":"nft_owner","docs":["The owner of the position and recipient of any minted liquidity"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity minted to the position range"],"type":"u128"},{"name":"deposit_amount_0","docs":["The amount of token_0 was deposit for the liquidity"],"type":"u64"},{"name":"deposit_amount_1","docs":["The amount of token_1 was deposit for the liquidity"],"type":"u64"},{"name":"deposit_amount_0_transfer_fee","docs":["The token transfer fee for deposit_amount_0"],"type":"u64"},{"name":"deposit_amount_1_transfer_fee","docs":["The token transfer fee for deposit_amount_1"],"type":"u64"}]}},{"name":"DecreaseLiquidityEvent","docs":["Emitted when liquidity is decreased."],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which liquidity was decreased"],"type":"pubkey"},{"name":"liquidity","docs":["The amount by which liquidity for the position was decreased"],"type":"u128"},{"name":"decrease_amount_0","docs":["The amount of token_0 that was paid for the decrease in liquidity"],"type":"u64"},{"name":"decrease_amount_1","docs":["The amount of token_1 that was paid for the decrease in liquidity"],"type":"u64"},{"name":"fee_amount_0","type":"u64"},{"name":"fee_amount_1","docs":["The amount of token_1 fee"],"type":"u64"},{"name":"reward_amounts","docs":["The amount of rewards"],"type":{"array":["u64",3]}},{"name":"transfer_fee_0","docs":["The amount of token_0 transfer fee"],"type":"u64"},{"name":"transfer_fee_1","docs":["The amount of token_1 transfer fee"],"type":"u64"}]}},{"name":"IncreaseLiquidityEvent","docs":["Emitted when liquidity is increased."],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which liquidity was increased"],"type":"pubkey"},{"name":"liquidity","docs":["The amount by which liquidity for the NFT position was increased"],"type":"u128"},{"name":"amount_0","docs":["The amount of token_0 that was paid for the increase in liquidity"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_1 that was paid for the increase in liquidity"],"type":"u64"},{"name":"amount_0_transfer_fee","docs":["The token transfer fee for amount_0"],"type":"u64"},{"name":"amount_1_transfer_fee","docs":["The token transfer fee for amount_1"],"type":"u64"}]}},{"name":"InitializeRewardParam","type":{"kind":"struct","fields":[{"name":"open_time","docs":["Reward open time"],"type":"u64"},{"name":"end_time","docs":["Reward end time"],"type":"u64"},{"name":"emissions_per_second_x64","docs":["Token reward per second are earned per unit of liquidity"],"type":"u128"}]}},{"name":"LiquidityCalculateEvent","docs":["Emitted when liquidity decreased or increase."],"type":{"kind":"struct","fields":[{"name":"pool_liquidity","docs":["The pool liquidity before decrease or increase"],"type":"u128"},{"name":"pool_sqrt_price_x64","docs":["The pool price when decrease or increase in liquidity"],"type":"u128"},{"name":"pool_tick","docs":["The pool tick when decrease or increase in liquidity"],"type":"i32"},{"name":"calc_amount_0","docs":["The amount of token_0 that was calculated for the decrease or increase in liquidity"],"type":"u64"},{"name":"calc_amount_1","docs":["The amount of token_1 that was calculated for the decrease or increase in liquidity"],"type":"u64"},{"name":"trade_fee_owed_0","type":"u64"},{"name":"trade_fee_owed_1","docs":["The amount of token_1 fee"],"type":"u64"},{"name":"transfer_fee_0","docs":["The amount of token_0 transfer fee without trade_fee_amount_0"],"type":"u64"},{"name":"transfer_fee_1","docs":["The amount of token_1 transfer fee without trade_fee_amount_0"],"type":"u64"}]}},{"name":"LiquidityChangeEvent","docs":["Emitted pool liquidity change when increase and decrease liquidity"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for swap"],"type":"pubkey"},{"name":"tick","docs":["The tick of the pool"],"type":"i32"},{"name":"tick_lower","docs":["The tick lower of position"],"type":"i32"},{"name":"tick_upper","docs":["The tick lower of position"],"type":"i32"},{"name":"liquidity_before","docs":["The liquidity of the pool before liquidity change"],"type":"u128"},{"name":"liquidity_after","docs":["The liquidity of the pool after liquidity change"],"type":"u128"}]}},{"name":"Observation","docs":["The element of observations in ObservationState"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"block_timestamp","docs":["The block timestamp of the observation"],"type":"u32"},{"name":"tick_cumulative","docs":["the cumulative of tick during the duration time"],"type":"i64"},{"name":"padding","docs":["padding for feature update"],"type":{"array":["u64",4]}}]}},{"name":"ObservationState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"initialized","docs":["Whether the ObservationState is initialized"],"type":"bool"},{"name":"recent_epoch","docs":["recent update epoch"],"type":"u64"},{"name":"observation_index","docs":["the most-recently updated index of the observations array"],"type":"u16"},{"name":"pool_id","docs":["belongs to which pool"],"type":"pubkey"},{"name":"observations","docs":["observation array"],"type":{"array":[{"defined":{"name":"Observation"}},100]}},{"name":"padding","docs":["padding for feature update"],"type":{"array":["u64",4]}}]}},{"name":"OperationState","docs":["Holds the current owner of the factory"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"operation_owners","docs":["Address of the operation owner"],"type":{"array":["pubkey",10]}},{"name":"whitelist_mints","docs":["The mint address of whitelist to emit reward"],"type":{"array":["pubkey",100]}}]}},{"name":"PermissionlessFarmSwitch","type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"is_on","docs":["Is the permissionless farm allowed or not"],"type":"bool"}]}},{"name":"PersonalPositionState","type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":{"array":["u8",1]}},{"name":"nft_mint","docs":["Mint address of the tokenized position"],"type":"pubkey"},{"name":"pool_id","docs":["The ID of the pool with which this token is connected"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"fee_growth_inside_0_last_x64","docs":["The token_0 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"fee_growth_inside_1_last_x64","docs":["The token_1 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"token_fees_owed_0","docs":["The fees owed to the position owner in token_0, as of the last computation"],"type":"u64"},{"name":"token_fees_owed_1","docs":["The fees owed to the position owner in token_1, as of the last computation"],"type":"u64"},{"name":"reward_infos","type":{"array":[{"defined":{"name":"PositionRewardInfo"}},3]}},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u64",7]}}]}},{"name":"PoolCreatedEvent","docs":["Emitted when a pool is created and initialized with a starting price",""],"type":{"kind":"struct","fields":[{"name":"token_mint_0","docs":["The first token of the pool by address sort order"],"type":"pubkey"},{"name":"token_mint_1","docs":["The second token of the pool by address sort order"],"type":"pubkey"},{"name":"tick_spacing","docs":["The minimum number of ticks between initialized ticks"],"type":"u16"},{"name":"pool_state","docs":["The address of the created pool"],"type":"pubkey"},{"name":"sqrt_price_x64","docs":["The initial sqrt price of the pool, as a Q64.64"],"type":"u128"},{"name":"tick","docs":["The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool"],"type":"i32"},{"name":"token_vault_0","docs":["Vault of token_0"],"type":"pubkey"},{"name":"token_vault_1","docs":["Vault of token_1"],"type":"pubkey"}]}},{"name":"PoolState","docs":["The pool state","","PDA of `[POOL_SEED, config, token_mint_0, token_mint_1]`",""],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":{"array":["u8",1]}},{"name":"amm_config","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"token_mint_0","docs":["Token pair of the pool, where token_mint_0 address < token_mint_1 address"],"type":"pubkey"},{"name":"token_mint_1","type":"pubkey"},{"name":"token_vault_0","docs":["Token pair vault"],"type":"pubkey"},{"name":"token_vault_1","type":"pubkey"},{"name":"observation_key","docs":["observation account key"],"type":"pubkey"},{"name":"mint_decimals_0","docs":["mint0 and mint1 decimals"],"type":"u8"},{"name":"mint_decimals_1","type":"u8"},{"name":"tick_spacing","docs":["The minimum number of ticks between initialized ticks"],"type":"u16"},{"name":"liquidity","docs":["The currently in range liquidity available to the pool."],"type":"u128"},{"name":"sqrt_price_x64","docs":["The current price of the pool as a sqrt(token_1/token_0) Q64.64 value"],"type":"u128"},{"name":"tick_current","docs":["The current tick of the pool, i.e. according to the last tick transition that was run."],"type":"i32"},{"name":"padding3","type":"u16"},{"name":"padding4","type":"u16"},{"name":"fee_growth_global_0_x64","docs":["The fee growth as a Q64.64 number, i.e. fees of token_0 and token_1 collected per","unit of liquidity for the entire life of the pool."],"type":"u128"},{"name":"fee_growth_global_1_x64","type":"u128"},{"name":"protocol_fees_token_0","docs":["The amounts of token_0 and token_1 that are owed to the protocol."],"type":"u64"},{"name":"protocol_fees_token_1","type":"u64"},{"name":"swap_in_amount_token_0","docs":["The amounts in and out of swap token_0 and token_1"],"type":"u128"},{"name":"swap_out_amount_token_1","type":"u128"},{"name":"swap_in_amount_token_1","type":"u128"},{"name":"swap_out_amount_token_0","type":"u128"},{"name":"status","docs":["Bitwise representation of the state of the pool","bit0, 1: disable open position and increase liquidity, 0: normal","bit1, 1: disable decrease liquidity, 0: normal","bit2, 1: disable collect fee, 0: normal","bit3, 1: disable collect reward, 0: normal","bit4, 1: disable swap, 0: normal"],"type":"u8"},{"name":"padding","docs":["Leave blank for future use"],"type":{"array":["u8",7]}},{"name":"reward_infos","type":{"array":[{"defined":{"name":"RewardInfo"}},3]}},{"name":"tick_array_bitmap","docs":["Packed initialized tick array state"],"type":{"array":["u64",16]}},{"name":"total_fees_token_0","docs":["except protocol_fee and fund_fee"],"type":"u64"},{"name":"total_fees_claimed_token_0","docs":["except protocol_fee and fund_fee"],"type":"u64"},{"name":"total_fees_token_1","type":"u64"},{"name":"total_fees_claimed_token_1","type":"u64"},{"name":"fund_fees_token_0","type":"u64"},{"name":"fund_fees_token_1","type":"u64"},{"name":"open_time","type":"u64"},{"name":"recent_epoch","type":"u64"},{"name":"padding1","type":{"array":["u64",24]}},{"name":"padding2","type":{"array":["u64",32]}}]}},{"name":"PositionRewardInfo","type":{"kind":"struct","fields":[{"name":"growth_inside_last_x64","type":"u128"},{"name":"reward_amount_owed","type":"u64"}]}},{"name":"ProtocolPositionState","docs":["Info stored for each user's position"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"pool_id","docs":["The ID of the pool with which this token is connected"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"fee_growth_inside_0_last_x64","docs":["The token_0 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"fee_growth_inside_1_last_x64","docs":["The token_1 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"token_fees_owed_0","docs":["The fees owed to the position owner in token_0"],"type":"u64"},{"name":"token_fees_owed_1","docs":["The fees owed to the position owner in token_1"],"type":"u64"},{"name":"reward_growth_inside","docs":["The reward growth per unit of liquidity as of the last update to liquidity"],"type":{"array":["u128",3]}},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u64",7]}}]}},{"name":"RewardInfo","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"reward_state","docs":["Reward state"],"type":"u8"},{"name":"open_time","docs":["Reward open time"],"type":"u64"},{"name":"end_time","docs":["Reward end time"],"type":"u64"},{"name":"last_update_time","docs":["Reward last update time"],"type":"u64"},{"name":"emissions_per_second_x64","docs":["Q64.64 number indicates how many tokens per second are earned per unit of liquidity."],"type":"u128"},{"name":"reward_total_emissioned","docs":["The total amount of reward emissioned"],"type":"u64"},{"name":"reward_claimed","docs":["The total amount of claimed reward"],"type":"u64"},{"name":"token_mint","docs":["Reward token mint."],"type":"pubkey"},{"name":"token_vault","docs":["Reward vault token account."],"type":"pubkey"},{"name":"authority","docs":["The owner that has permission to set reward param"],"type":"pubkey"},{"name":"reward_growth_global_x64","docs":["Q64.64 number that tracks the total tokens earned per unit of liquidity since the reward","emissions were turned on."],"type":"u128"}]}},{"name":"SupportMintAssociated","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"mint","docs":["Address of the supported token22 mint"],"type":"pubkey"},{"name":"padding","type":{"array":["u64",8]}}]}},{"name":"SwapEvent","docs":["Emitted by when a swap is performed for a pool"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for which token_0 and token_1 were swapped"],"type":"pubkey"},{"name":"sender","docs":["The address that initiated the swap call, and that received the callback"],"type":"pubkey"},{"name":"token_account_0","docs":["The payer token account in zero for one swaps, or the recipient token account","in one for zero swaps"],"type":"pubkey"},{"name":"token_account_1","docs":["The payer token account in one for zero swaps, or the recipient token account","in zero for one swaps"],"type":"pubkey"},{"name":"amount_0","docs":["The real delta amount of the token_0 of the pool or user"],"type":"u64"},{"name":"transfer_fee_0","docs":["The transfer fee charged by the withheld_amount of the token_0"],"type":"u64"},{"name":"amount_1","docs":["The real delta of the token_1 of the pool or user"],"type":"u64"},{"name":"transfer_fee_1","docs":["The transfer fee charged by the withheld_amount of the token_1"],"type":"u64"},{"name":"zero_for_one","docs":["if true, amount_0 is negtive and amount_1 is positive"],"type":"bool"},{"name":"sqrt_price_x64","docs":["The sqrt(price) of the pool after the swap, as a Q64.64"],"type":"u128"},{"name":"liquidity","docs":["The liquidity of the pool after the swap"],"type":"u128"},{"name":"tick","docs":["The log base 1.0001 of price of the pool after the swap"],"type":"i32"}]}},{"name":"TickArrayBitmapExtension","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"positive_tick_array_bitmap","docs":["Packed initialized tick array state for start_tick_index is positive"],"type":{"array":[{"array":["u64",8]},14]}},{"name":"negative_tick_array_bitmap","docs":["Packed initialized tick array state for start_tick_index is negitive"],"type":{"array":[{"array":["u64",8]},14]}}]}},{"name":"TickArrayState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"start_tick_index","type":"i32"},{"name":"ticks","type":{"array":[{"defined":{"name":"TickState"}},60]}},{"name":"initialized_tick_count","type":"u8"},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u8",107]}}]}},{"name":"TickState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"tick","type":"i32"},{"name":"liquidity_net","docs":["Amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left)"],"type":"i128"},{"name":"liquidity_gross","docs":["The total position liquidity that references this tick"],"type":"u128"},{"name":"fee_growth_outside_0_x64","docs":["Fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)","only has relative meaning, not absolute — the value depends on when the tick is initialized"],"type":"u128"},{"name":"fee_growth_outside_1_x64","type":"u128"},{"name":"reward_growths_outside_x64","type":{"array":["u128",3]}},{"name":"padding","type":{"array":["u32",13]}}]}},{"name":"UpdateRewardInfosEvent","docs":["Emitted when Reward are updated for a pool"],"type":{"kind":"struct","fields":[{"name":"reward_growth_global_x64","docs":["Reward info"],"type":{"array":["u128",3]}}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/phoenix_eternal.EtrnLzgbS7nMMy5fbD42kXiUzGg8XQzJ972Xtk1cjWih.json b/migration/khadhroony-bot2-reference/idls/phoenix_eternal.EtrnLzgbS7nMMy5fbD42kXiUzGg8XQzJ972Xtk1cjWih.json new file mode 100644 index 0000000..e69de29 diff --git a/migration/khadhroony-bot2-reference/idls/printr.T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint.json b/migration/khadhroony-bot2-reference/idls/printr.T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint.json new file mode 100644 index 0000000..2899019 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/printr.T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint.json @@ -0,0 +1 @@ +{"address":"T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint","metadata":{"name":"printr","version":"1.0.0","spec":"0.1.0","description":"Printr on Solana"},"instructions":[{"name":"authorized_spend_all_quote","discriminator":[55,106,238,14,152,246,186,129],"accounts":[{"name":"authorizor","docs":["This is a Printr PDA.","You can see the expected seed in the [Self::apply] static function."],"writable":true},{"name":"authorizor_wallet","writable":true,"optional":true},{"name":"swap","accounts":[{"name":"payer","docs":["The user performing the swap"],"writable":true,"signer":true},{"name":"printr_authority","docs":["This is the account that funds the graduation process.",""],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"input_wallet","writable":true},{"name":"output_wallet","docs":["The mint of this token account determines what's being sold, whether telecoin or quote."],"writable":true},{"name":"telecoin_mint","writable":true},{"name":"quote_mint","writable":true},{"name":"dbc_pool_authority","writable":true},{"name":"dbc_config"},{"name":"dbc_pool","writable":true},{"name":"dbc_telecoin_vault","writable":true},{"name":"dbc_quote_vault","writable":true},{"name":"dbc_event_authority"},{"name":"dbc_migration_metadata","docs":["DEPRECATED: Meteora no longer uses this one.",""],"writable":true},{"name":"damm_printr_partner_config"},{"name":"damm_pool","writable":true},{"name":"damm_pool_authority"},{"name":"damm_telecoin_vault","writable":true},{"name":"damm_quote_vault","writable":true},{"name":"damm_position_nft_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,100,97,109,109,95,112,111,115,105,116,105,111,110,95,110,102,116,95,109,105,110,116]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"damm_position_nft_account","writable":true},{"name":"damm_position","writable":true},{"name":"damm_event_authority"},{"name":"telecoin_token_program","docs":["We'll need this to init the mint and init the token account."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"dbc_program","docs":["The DBC program that abstracts the bonding curve logic."],"address":"dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN"},{"name":"damm_program","docs":["The DAMM V2 program that abstracts the AMM logic."],"address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"},{"name":"system_program","docs":["New accounts, native transfers, etc."],"address":"11111111111111111111111111111111"}]}],"args":[{"name":"max_price","type":{"defined":{"name":"SqrtBitshiftedPrice"}}},{"name":"extra_seed","type":"bytes"}]},{"name":"buyback_and_burn","discriminator":[121,156,154,165,194,86,180,130],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"printr_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"payer_quote_wallet","writable":true},{"name":"buyback_telecoin_wallet","docs":["Owned by printr_authority (PDA)."],"writable":true},{"name":"telecoin_mint","writable":true},{"name":"quote_mint","docs":["UncheckedAccount to avoid self-CPI exit handler corruption."],"writable":true},{"name":"dbc_pool_authority","writable":true},{"name":"dbc_config"},{"name":"dbc_pool","writable":true},{"name":"dbc_telecoin_vault","writable":true},{"name":"dbc_quote_vault","writable":true},{"name":"dbc_event_authority"},{"name":"dbc_migration_metadata","writable":true},{"name":"damm_printr_partner_config"},{"name":"damm_pool","writable":true},{"name":"damm_pool_authority"},{"name":"damm_telecoin_vault","writable":true},{"name":"damm_quote_vault","writable":true},{"name":"damm_position_nft_mint","writable":true},{"name":"damm_position_nft_account","writable":true},{"name":"damm_position","writable":true},{"name":"damm_event_authority"},{"name":"telecoin_token_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program"},{"name":"dbc_program","address":"dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN"},{"name":"damm_program","address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"printr_config","pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,99,111,110,102,105,103]}]}},{"name":"dev_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"quote_fee_vault","docs":["Stores all quote fees across all telecoins."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,115,95,102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"quote_mint"}]}},{"name":"printr_quote_fee_wallet","writable":true,"pda":{"seeds":[{"kind":"account","path":"printr_config.printr_fee_authority","account":"PrintrConfig"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"revshare1_quote_fee_wallet","docs":["If relevant then authority is checked via [CollectProtocolTransfers]"],"writable":true,"optional":true},{"name":"revshare2_quote_fee_wallet","docs":["If relevant then authority is checked via [CollectProtocolTransfers]"],"writable":true,"optional":true},{"name":"instructions_sysvar","address":"Sysvar1nstructions1111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[],"returns":{"defined":{"name":"FeesClaimed"}}},{"name":"cancel_authorized_spend_all_quote","discriminator":[167,1,184,69,242,171,240,207],"accounts":[{"name":"authorizor","docs":["This is a Printr PDA.","You can see the expected seed in the [Self::apply] static function."],"writable":true},{"name":"authorizor_wallet","docs":["The SPL token account associated with the authorizor."],"writable":true,"optional":true},{"name":"quote_mint","docs":["The quote mint that was meant to be swapped against the telecoin token."]},{"name":"recipient","docs":["The authorizor's recipient can cancel their own swap order."],"writable":true,"signer":true},{"name":"recipient_wallet","docs":["The recipient's wallet if applicable."],"writable":true,"optional":true},{"name":"quote_token_program","docs":["Allows us to swap."]},{"name":"system_program","docs":["We need to transfer."],"address":"11111111111111111111111111111111"}],"args":[{"name":"max_price","type":{"defined":{"name":"SqrtBitshiftedPrice"}}},{"name":"telecoin_mint","type":"pubkey"},{"name":"extra_seed","type":"bytes"}]},{"name":"claim_damm_fees","discriminator":[221,192,167,35,180,110,107,114],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"printr_authority","pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"printr_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,99,111,110,102,105,103]}]}},{"name":"telecoin_mint"},{"name":"quote_mint","docs":["The quote mint is the token that will be used to buy telecoin tokens."]},{"name":"telecoin_fee_vault_authority","docs":["Authority for the telecoin fee vault.","For staking telecoins: the staking pool PDA.","For non-staking telecoins: printr_config.staking_authority.",""]},{"name":"telecoin_fee_vault","docs":["Stores telecoin fees.","","For staking telecoins: staking pool's fee vault (authority = staking_pool).","","For non-staking telecoins: generic staking wallet (authority =","printr_config.staking_authority).",""],"writable":true},{"name":"dev_config","docs":["The dev config stores information about the dev and must match the damm pool.","","We know this is the right dev config because its seed matches the telecoin mint which in","turn matches the dammv2 pool."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"quote_fee_vault","docs":["Stores all quote fees across all telecoins."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,115,95,102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"quote_mint"}]}},{"name":"damm_position_nft_account"},{"name":"damm_position","writable":true},{"name":"damm_pool_authority"},{"name":"damm_pool","writable":true},{"name":"damm_base_vault","writable":true},{"name":"damm_quote_vault","writable":true},{"name":"damm_event_authority"},{"name":"telecoin_token_program","docs":["We'll need this to transfer."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","docs":["For the telecoin fees."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"system_program","docs":["Necessary for creating new accounts."],"address":"11111111111111111111111111111111"},{"name":"damm_v2_program","docs":["The DAMM program that abstracts the bonding curve logic."],"address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"}],"args":[],"returns":{"defined":{"name":"FeesClaimed"}}},{"name":"claim_dbc_fees","discriminator":[162,222,128,90,59,115,34,130],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"printr_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"printr_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,99,111,110,102,105,103]}]}},{"name":"telecoin_mint"},{"name":"quote_mint","docs":["The quote mint is the token that is be used to buy telecoin tokens."]},{"name":"any_telecoin_wallet","docs":["Therefore any old telecoin wallet will do, it will not be used."],"writable":true},{"name":"printr_intermediary_authority","pda":{"seeds":[{"kind":"const","value":[105,110,116,101,114,109,101,100,105,97,114,121,95,116,111,107,101,110,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"printr_intermediary_quote_wallet","docs":["Useful only for $wSOL quote token.","We will collect some fee amount to support smart graduation in swap ix."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"printr_intermediary_authority"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"dbc_pool_authority"},{"name":"dbc_pool","writable":true},{"name":"dbc_base_vault","writable":true},{"name":"dbc_quote_vault","writable":true},{"name":"dbc_event_authority"},{"name":"dev_config","docs":["The dev config stores information about the dev and must match the DBC pool.","","We know this is the right dev config because its seed matches the telecoin mint which in","turn matches the dbc pool."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"quote_fee_vault","docs":["Shared vault per quote mint. Per-telecoin accounting is tracked in DevConfig."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,115,95,102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"quote_mint"}]}},{"name":"telecoin_token_program","docs":["We'll need this to transfer."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"associated_token_program","docs":["Used to create dev's telecoin ATA."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["Necessary for creating new accounts."],"address":"11111111111111111111111111111111"},{"name":"dbc_program","docs":["The DBC program that abstracts the bonding curve logic."],"address":"dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN"}],"args":[],"returns":{"defined":{"name":"FeesClaimed"}}},{"name":"claim_printr_authority_lamports","discriminator":[52,14,140,114,41,105,0,48],"accounts":[{"name":"admin","writable":true,"signer":true},{"name":"printr_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"system_program","docs":["Necessary for creating new accounts."],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"claim_staking_rewards","discriminator":[229,141,170,69,111,94,6,72],"accounts":[{"name":"payer","docs":["Anyone can call this instruction."],"writable":true,"signer":true},{"name":"position_owner","writable":true},{"name":"printr_config","docs":["Printr config for authorizing transfers."],"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,99,111,110,102,105,103]}]}},{"name":"telecoin_mint","docs":["The telecoin mint."]},{"name":"quote_mint","docs":["The quote token mint."]},{"name":"staking_pool","docs":["The staking pool for this telecoin."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,105,110,103,95,112,111,111,108]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"stake_position","docs":["The stake position being claimed from."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,101,95,112,111,115,105,116,105,111,110]},{"kind":"account","path":"telecoin_mint"},{"kind":"account","path":"position_owner"},{"kind":"account","path":"stake_position.position_nonce","account":"StakePosition"}]}},{"name":"owner_quote_account","docs":["Owner's quote token account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"position_owner"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"owner_telecoin_account","docs":["Owner's telecoin token account."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"position_owner"},{"kind":"account","path":"telecoin_token_program"},{"kind":"account","path":"telecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"quote_fee_vault","docs":["Stores all quote fees across Printr so be gentle with it."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,115,95,102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"quote_mint"}]}},{"name":"pool_telecoin_vault","docs":["Pool's telecoin vault.","Contains both trading fees and staked telecoin."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"staking_pool"},{"kind":"account","path":"telecoin_token_program"},{"kind":"account","path":"telecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"telecoin_token_program","docs":["Checks the account owner."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"associated_token_program","docs":["ATA boy"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["Needed because `owner_quote_account` and `owner_telecoin_account` are `init_if_needed`."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"close_stake_position","discriminator":[8,63,74,143,105,42,28,64],"accounts":[{"name":"position_owner","docs":["Only the position owner can close the position.","Receives the rent refund from the closed position account."],"writable":true,"signer":true},{"name":"telecoin_mint","docs":["The telecoin mint."]},{"name":"staking_pool","docs":["The staking pool for this telecoin."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,105,110,103,95,112,111,111,108]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"stake_position","docs":["The stake position being closed. Rent is refunded to `position_owner`."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,101,95,112,111,115,105,116,105,111,110]},{"kind":"account","path":"telecoin_mint"},{"kind":"account","path":"position_owner"},{"kind":"account","path":"stake_position.position_nonce","account":"StakePosition"}]}},{"name":"owner_telecoin_account","docs":["Owner's telecoin token account. Receives the staked telecoin back."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"position_owner"},{"kind":"account","path":"telecoin_token_program"},{"kind":"account","path":"telecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool_telecoin_vault","docs":["Pool's telecoin vault holding the staked telecoin."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"staking_pool"},{"kind":"account","path":"telecoin_token_program"},{"kind":"account","path":"telecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"telecoin_token_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["Needed for `init_if_needed` on `owner_telecoin_account` and for `close` on the position."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"create_printr_config","discriminator":[160,191,66,212,5,106,20,215],"accounts":[{"name":"admin","writable":true,"signer":true},{"name":"printr_fee_authority"},{"name":"questflow_ai_fee_authority"},{"name":"printr_staking_authority"},{"name":"printr_config","docs":["We use `init_if_needed` purposefully.","Call this ix again to update the config."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,99,111,110,102,105,103]}]}},{"name":"system_program","docs":["Necessary for creating new accounts."],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_stake_position","discriminator":[92,168,96,133,102,121,86,138],"accounts":[{"name":"payer","docs":["We can create positions on behalf of others."],"writable":true,"signer":true},{"name":"position_owner","writable":true},{"name":"printr_authority","pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"dev_config","docs":["Dev config must indicate this telecoin uses staking for fee distribution."],"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"staking_pool","docs":["The staking pool for this telecoin.","Will be lazily initialized if this is the first time anyone stakes this telecoin."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,105,110,103,95,112,111,111,108]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"stake_position","docs":["The stake position being created.","The ID can be reused, just cannot be used at the same time as another position."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,101,95,112,111,115,105,116,105,111,110]},{"kind":"account","path":"telecoin_mint"},{"kind":"account","path":"position_owner"},{"kind":"arg","path":"position_nonce"}]}},{"name":"payer_telecoin_account","docs":["Payer doesn't necessarily equal to the user but most likely will."],"writable":true},{"name":"pool_telecoin_vault","docs":["Staking pool's telecoin vault."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"staking_pool"},{"kind":"account","path":"telecoin_token_program"},{"kind":"account","path":"telecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"telecoin_mint","docs":["The telecoin mint."]},{"name":"telecoin_token_program","docs":["Checks the account owner."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","docs":["Related to the telecoin vault ATA."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"position_nonce","type":"u32"},{"name":"to_stake","type":{"defined":{"name":"TokenAmount"}}},{"name":"lock_period","type":{"defined":{"name":"LockPeriod"}}}]},{"name":"deploy_telecoin","discriminator":[4,248,20,1,125,63,15,0],"accounts":[{"name":"payer","docs":["Creating a launch is a permission-less operation.","This could be a Printr user or a BE."],"writable":true,"signer":true},{"name":"printr_authority","pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"dev_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"telecoin_mint","docs":["This is the telecoin mint which we initialize according to the telecoin params."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,116,101,108,101,99,111,105,110,95,109,105,110,116]},{"kind":"arg","path":"telecoin_params"}]}},{"name":"telecoin_token_program","docs":["We'll need this to init the mint."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","docs":["Necessary for creating new accounts."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"telecoin_params_compact","type":{"defined":{"name":"TelecoinParamsCompact"}}}]},{"name":"deploy_telecoin2","discriminator":[37,155,54,236,186,225,201,137],"accounts":[{"name":"payer","docs":["Creating a launch is a permission-less operation.","This could be a Printr user or a BE."],"writable":true,"signer":true},{"name":"printr_authority","pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"dev_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"telecoin_mint","docs":["This is the telecoin mint which we initialize according to the telecoin params."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,116,101,108,101,99,111,105,110,95,109,105,110,116]},{"kind":"arg","path":"telecoin_params"}]}},{"name":"telecoin_token_program","docs":["We'll need this to init the mint."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"system_program","docs":["Necessary for creating new accounts."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"telecoin_params","type":{"defined":{"name":"TelecoinParams"}}}]},{"name":"distribute_telecoin_quote_fees","discriminator":[31,197,88,239,123,165,197,208],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"printr_authority","pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"printr_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,99,111,110,102,105,103]}]}},{"name":"quote_mint","docs":["The quote mint is the token that is be used to buy telecoin tokens."]},{"name":"telecoin_mint"},{"name":"quote_fee_vault","docs":["Stores all quote fees across all telecoins.","How much can we claim now is tracked in the dev config."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,115,95,102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"quote_mint"}]}},{"name":"dev_config","docs":["The dev config stores information about how much a dev can claim.","The existence of this account implies that the telecoin mint was created by Printr."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"dev"},{"name":"dev_quote_wallet","writable":true,"pda":{"seeds":[{"kind":"account","path":"dev"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"printr_quote_fee_wallet","writable":true,"pda":{"seeds":[{"kind":"account","path":"printr_config.printr_fee_authority","account":"PrintrConfig"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"revshare1_quote_fee_wallet","docs":["If relevant then authority is checked via [CollectProtocolTransfers]"],"writable":true,"optional":true},{"name":"revshare2_quote_fee_wallet","docs":["If relevant then authority is checked via [CollectProtocolTransfers]"],"writable":true,"optional":true},{"name":"dbc_pool"},{"name":"dbc_config"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"associated_token_program","docs":["Used to create dev's quote ATA."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["Necessary for creating new accounts."],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"estimate_swap","discriminator":[219,196,183,82,59,37,166,59],"accounts":[{"name":"dbc_config"},{"name":"dbc_pool"},{"name":"damm_pool","docs":["Might be uninitialized if the pool is not yet graduated."]}],"args":[{"name":"params","type":{"defined":{"name":"SwapIntent"}}}],"returns":{"defined":{"name":"EstimateSwapOutput"}}},{"name":"estimate_teleport_with_lz","discriminator":[11,141,166,118,153,255,62,132],"accounts":[{"name":"peer"}],"args":[{"name":"params","type":{"defined":{"name":"EstimateTeleportWithLzParams"}}}],"returns":{"defined":{"name":"TokenAmount"}}},{"name":"lz_receive","discriminator":[8,179,120,109,33,118,189,80],"accounts":[{"name":"payer","docs":["The LZ executor."],"writable":true,"signer":true},{"name":"peer","docs":["Represents where did the message come from."]},{"name":"telecoin_teleport_config","docs":["If this account does not exist then the provided peer must be set as a default peer.","Otherwise we parse this account's configuration and assert it's been upheld."]},{"name":"printr_authority","docs":["Global Printr telecoin mint authority"],"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"telecoin_mint","docs":["We'll mint these tokens.",""],"writable":true},{"name":"recipient"},{"name":"recipient_destination_telecoin_wallet","writable":true,"pda":{"seeds":[{"kind":"account","path":"recipient"},{"kind":"account","path":"telecoin_token_program"},{"kind":"account","path":"telecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"telecoin_token_program","docs":["Telecoin mint is always token program 2022."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","docs":["In case we need to create the ATA"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["In case we need to create the ATA"],"address":"11111111111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"LzReceiveParams"}}}]},{"name":"lz_receive_types","discriminator":[221,17,246,159,248,128,31,96],"accounts":[],"args":[{"name":"params","type":{"defined":{"name":"LzReceiveParams"}}}],"returns":{"vec":{"defined":{"name":"LzAccount"}}}},{"name":"lz_set_peer","discriminator":[199,123,81,116,208,49,213,232],"accounts":[{"name":"admin","docs":["Printr staff."],"writable":true,"signer":true},{"name":"peer","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,101,101,114]},{"kind":"arg","path":"peer_address"}]}},{"name":"lz_oapp_registry","writable":true,"pda":{"seeds":[{"kind":"const","value":[79,65,112,112]},{"kind":"account","path":"peer"}],"program":{"kind":"account","path":"endpoint_program"}}},{"name":"endpoint_emit_cpi_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"account","path":"endpoint_program"}}},{"name":"endpoint_program","address":"76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"peer_address","type":{"array":["u8",32]}}]},{"name":"print_telecoin","discriminator":[214,30,4,102,207,73,108,35],"accounts":[{"name":"payer","docs":["Creating a launch is a permission-less operation.","This could be a Printr user or a BE."],"writable":true,"signer":true},{"name":"printr_authority","pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"dev_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"telecoin_mint","docs":["This is the telecoin mint which we initialize according to the telecoin params in the","Meteora CPI.",""],"writable":true},{"name":"quote_mint","docs":["The quote mint is the token that will be used to buy telecoin tokens."]},{"name":"initial_buy_authority","docs":["When a telecoin is printed we perform an initial buy on behalf of the dev.","","There are three options how the initial buy can happen:","1. No deposit PDA","2. Deposit PDA of native $SOL","3. Deposit PDA with an associated token account.","","See `DepositPda` enum for more details.",""],"writable":true},{"name":"initial_buy_source","writable":true},{"name":"dev_on_solana","writable":true},{"name":"dev_destination_telecoin_wallet","docs":["Associated token account cannot exist because we've just created the telecoin mint.","Will not be initialized if initial spend is zero.",""],"writable":true,"optional":true},{"name":"dbc_config","docs":["We'll init the config first.",""],"writable":true},{"name":"dbc_pool_authority"},{"name":"dbc_pool","docs":["The bonding curve state, initialized after the config.",""],"writable":true},{"name":"dbc_telecoin_vault","writable":true},{"name":"dbc_quote_vault","writable":true},{"name":"dbc_event_authority"},{"name":"dbc_migration_metadata","docs":["DEPRECATED: Meteora no longer uses this one.","","If provided then we create a migration metadata account for the DBC pool.",""],"writable":true,"optional":true},{"name":"telecoin_token_program","docs":["We'll need this to init the mint and init the token account."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"associated_token_program","docs":["Used to create dev's telecoin ATA."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["Necessary for creating new accounts."],"address":"11111111111111111111111111111111"},{"name":"dbc_program","docs":["The DBC program that abstracts the bonding curve logic."],"address":"dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"telecoin_params_compact","type":{"defined":{"name":"TelecoinParamsCompact"}}}]},{"name":"print_telecoin2","discriminator":[166,60,38,43,238,32,2,160],"accounts":[{"name":"payer","docs":["Creating a launch is a permission-less operation.","This could be a Printr user or a BE."],"writable":true,"signer":true},{"name":"printr_authority","pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"dev_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"telecoin_mint","docs":["This is the telecoin mint which we initialize according to the telecoin params in the","Meteora CPI.",""],"writable":true},{"name":"quote_mint","docs":["The quote mint is the token that will be used to buy telecoin tokens."]},{"name":"initial_buy_authority","docs":["When a telecoin is printed we perform an initial buy on behalf of the dev.","","There are three options how the initial buy can happen:","1. No deposit PDA","2. Deposit PDA of native $SOL","3. Deposit PDA with an associated token account.","","See `DepositPda` enum for more details.",""],"writable":true},{"name":"initial_buy_source","writable":true},{"name":"dev_on_solana","writable":true},{"name":"dev_destination_telecoin_wallet","docs":["Associated token account cannot exist because we've just created the telecoin mint.","Will not be initialized if initial spend is zero.",""],"writable":true,"optional":true},{"name":"dbc_config","docs":["We'll init the config first.",""],"writable":true},{"name":"dbc_pool_authority"},{"name":"dbc_pool","docs":["The bonding curve state, initialized after the config.",""],"writable":true},{"name":"dbc_telecoin_vault","writable":true},{"name":"dbc_quote_vault","writable":true},{"name":"dbc_event_authority"},{"name":"dbc_migration_metadata","docs":["DEPRECATED: Meteora no longer uses this one.","","If provided then we create a migration metadata account for the DBC pool.",""],"writable":true,"optional":true},{"name":"telecoin_token_program","docs":["We'll need this to init the mint and init the token account."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"associated_token_program","docs":["Used to create dev's telecoin ATA."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["Necessary for creating new accounts."],"address":"11111111111111111111111111111111"},{"name":"dbc_program","docs":["The DBC program that abstracts the bonding curve logic."],"address":"dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"telecoin_params","type":{"defined":{"name":"TelecoinParams"}}}]},{"name":"refresh_staking2","discriminator":[36,197,195,27,8,224,118,27],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"printr_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,99,111,110,102,105,103]}]}},{"name":"printr_quote_fee_wallet","writable":true,"pda":{"seeds":[{"kind":"account","path":"printr_config.printr_fee_authority","account":"PrintrConfig"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"quote_fee_vault","docs":["Stores all quote fees across Printr."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,115,95,102,101,101,95,118,97,117,108,116]},{"kind":"account","path":"quote_mint"}]}},{"name":"revshare1_quote_fee_wallet","writable":true,"optional":true},{"name":"revshare2_quote_fee_wallet","writable":true,"optional":true},{"name":"staking_pool","docs":["The staking pool for this telecoin."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,116,97,107,105,110,103,95,112,111,111,108]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"dev_config","docs":["The dev config stores information about accumulated quote fees."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"dbc_config"},{"name":"dbc_pool","docs":["DBC config."]},{"name":"telecoin_mint","docs":["The telecoin mint."]},{"name":"quote_mint","docs":["The quote mint is the token that is be used to buy telecoin tokens."]},{"name":"pool_telecoin_vault","docs":["Telecoin vault that holds staked tokens and accumulated telecoin fees."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"staking_pool"},{"kind":"account","path":"telecoin_token_program"},{"kind":"account","path":"telecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"protocol_telecoin_wallet","docs":["Protocol's telecoin wallet. Receives the protocol's share of telecoin rewards.","ATA of `printr_config.staking_authority` for the telecoin mint."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"protocol_telecoin_authority"},{"kind":"account","path":"telecoin_token_program"},{"kind":"account","path":"telecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"protocol_telecoin_authority"},{"name":"telecoin_token_program","docs":["Checks the account owner."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"associated_token_program","docs":["Related to the telecoin vault ATA."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["Need to init the staking pool if not yet."],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"set_dev_config_dev","discriminator":[175,111,205,104,111,167,15,28],"accounts":[{"name":"admin","signer":true},{"name":"telecoin_mint","docs":["The telecoin mint whose dev config we want to update."]},{"name":"dev_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_dev","type":"pubkey"}]},{"name":"set_dev_config_fees","discriminator":[75,184,89,133,143,57,183,121],"accounts":[{"name":"admin","signer":true},{"name":"telecoin_mint","docs":["The telecoin mint whose dev config we want to update."]},{"name":"dev_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,101,118,95,99,111,110,102,105,103]},{"kind":"account","path":"telecoin_mint"}]}}],"args":[{"name":"params","type":{"defined":{"name":"SetDevConfigFeesParams"}}}]},{"name":"set_peer_conf","discriminator":[81,211,81,152,212,153,58,71],"accounts":[{"name":"admin","docs":["Printr staff."],"signer":true},{"name":"peer","writable":true}],"args":[{"name":"params","type":{"defined":{"name":"SetPeerConfParams"}}}]},{"name":"set_telecoin_teleport_conf","discriminator":[100,47,61,148,198,138,62,49],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"admin","docs":["Printr staff."],"signer":true},{"name":"telecoin_teleport_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,101,108,101,99,111,105,110,95,116,101,108,101,112,111,114,116,95,99,111,110,102,105,103]},{"kind":"arg","path":"telecoin_id"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"telecoin_id","type":{"defined":{"name":"TelecoinId"}}},{"name":"params","type":{"defined":{"name":"SetTelecoinTeleportConfParams"}}}]},{"name":"spend_all_quote_no_graduation","discriminator":[198,170,217,245,59,73,114,60],"accounts":[{"name":"payer","docs":["The entity performing the swap.","Owner of the input token account.","","If the quote token is $wSOL then we transfer everything from this account to the","intermediary token account."],"writable":true,"signer":true},{"name":"printr_intermediary_authority","pda":{"seeds":[{"kind":"const","value":[105,110,116,101,114,109,101,100,105,97,114,121,95,116,111,107,101,110,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"printr_intermediary_quote_wallet","docs":["To support post hooks with native $SOL we need to create an intermediate ATA for the quote","token.","All the quote tokens will be transferred to this account first before being swapped.","The account is not closed so that it can be reused in the future without paying rent."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"printr_intermediary_authority"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"input_quote_wallet","docs":["We will use everything that's in this token account to buy as much telecoin as possible.","","Can be none for $wSOL quote if wrapping is needed."],"writable":true,"optional":true},{"name":"recipient"},{"name":"recipient_quote_wallet","writable":true,"pda":{"seeds":[{"kind":"account","path":"recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"output_telecoin_wallet","docs":["The owner of the output account will not match the payer for a posthook ix.",""],"writable":true,"pda":{"seeds":[{"kind":"account","path":"recipient"},{"kind":"account","path":"telecoin_token_program"},{"kind":"account","path":"telecoin_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"telecoin_mint","writable":true},{"name":"quote_mint","writable":true},{"name":"dbc_pool_authority","writable":true},{"name":"dbc_config"},{"name":"dbc_pool","writable":true},{"name":"dbc_telecoin_vault","writable":true},{"name":"dbc_quote_vault","writable":true},{"name":"dbc_event_authority"},{"name":"damm_pool","writable":true},{"name":"damm_pool_authority"},{"name":"damm_telecoin_vault","writable":true},{"name":"damm_quote_vault","writable":true},{"name":"damm_event_authority"},{"name":"telecoin_token_program","docs":["We'll need this to init the mint and init the token account."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"dbc_program","docs":["The DBC program that abstracts the bonding curve logic."],"address":"dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN"},{"name":"damm_program","docs":["The DAMM V2 program that abstracts the AMM logic."],"address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"},{"name":"associated_token_program","docs":["Used to create ATA if needed."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["We might need to refill the input token account with $wSOL."],"address":"11111111111111111111111111111111"}],"args":[{"name":"max_price","type":{"defined":{"name":"SqrtBitshiftedPrice"}}}]},{"name":"swap","discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"payer","docs":["The user performing the swap"],"writable":true,"signer":true},{"name":"printr_authority","docs":["This is the account that funds the graduation process.",""],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"input_wallet","writable":true},{"name":"output_wallet","docs":["The mint of this token account determines what's being sold, whether telecoin or quote."],"writable":true},{"name":"telecoin_mint","writable":true},{"name":"quote_mint","writable":true},{"name":"dbc_pool_authority","writable":true},{"name":"dbc_config"},{"name":"dbc_pool","writable":true},{"name":"dbc_telecoin_vault","writable":true},{"name":"dbc_quote_vault","writable":true},{"name":"dbc_event_authority"},{"name":"dbc_migration_metadata","docs":["DEPRECATED: Meteora no longer uses this one.",""],"writable":true},{"name":"damm_printr_partner_config"},{"name":"damm_pool","writable":true},{"name":"damm_pool_authority"},{"name":"damm_telecoin_vault","writable":true},{"name":"damm_quote_vault","writable":true},{"name":"damm_position_nft_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,114,105,110,116,114,95,100,97,109,109,95,112,111,115,105,116,105,111,110,95,110,102,116,95,109,105,110,116]},{"kind":"account","path":"telecoin_mint"}]}},{"name":"damm_position_nft_account","writable":true},{"name":"damm_position","writable":true},{"name":"damm_event_authority"},{"name":"telecoin_token_program","docs":["We'll need this to init the mint and init the token account."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program","docs":["We'll need this to do transfers."]},{"name":"dbc_program","docs":["The DBC program that abstracts the bonding curve logic."],"address":"dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN"},{"name":"damm_program","docs":["The DAMM V2 program that abstracts the AMM logic."],"address":"cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"},{"name":"system_program","docs":["New accounts, native transfers, etc."],"address":"11111111111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"SwapIntent"}}}]},{"name":"teleport_with_lz","discriminator":[166,121,233,8,125,40,179,47],"accounts":[{"name":"payer","docs":["This is the user that wants to send telecoin tokens."],"writable":true,"signer":true},{"name":"telecoin_mint","docs":["The telecoin mint that we burn here."],"writable":true},{"name":"telecoin_teleport_config","docs":["If this account does not exist then the provided peer must be set as a default peer.","Otherwise we parse this account's configuration and assert it's been upheld."]},{"name":"peer"},{"name":"user_source_telecoin_wallet","docs":["Where to burn from."],"writable":true},{"name":"telecoin_token_program","docs":["Needed to burn telecoin tokens."],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"params","type":{"defined":{"name":"TeleportWithLzParams"}}}]}],"accounts":[{"name":"DevConfig","discriminator":[92,158,1,106,83,222,4,81]},{"name":"Peer","discriminator":[50,8,19,55,40,253,37,58]},{"name":"PoolConfig","discriminator":[26,108,14,123,116,230,129,43]},{"name":"PrintrConfig","discriminator":[184,34,37,157,37,223,65,252]},{"name":"StakePosition","discriminator":[78,165,30,111,171,125,11,220]},{"name":"StakingPool","discriminator":[203,19,214,220,220,154,24,102]},{"name":"TelecoinTeleportConfig","discriminator":[146,117,46,78,137,112,71,57]},{"name":"VirtualPool","discriminator":[213,224,5,209,98,69,119,92]}],"events":[{"name":"BuybackAndBurnEvent","discriminator":[158,146,152,190,14,197,155,158]},{"name":"ClaimStakingRewardsEvent","discriminator":[109,78,167,39,50,229,74,85]},{"name":"CreateStakePositionEvent","discriminator":[47,51,210,240,178,138,154,198]},{"name":"DeployTelecoinEvent","discriminator":[133,217,193,229,51,195,248,98]},{"name":"PrintTelecoinEvent","discriminator":[202,7,212,34,205,71,111,139]},{"name":"RefreshStaking2Event","discriminator":[39,218,120,153,29,229,190,254]},{"name":"SetDevConfigDevEvent","discriminator":[89,201,9,41,16,123,16,57]},{"name":"TeleportWithLzEvent","discriminator":[166,170,61,125,224,166,227,98]}],"types":[{"name":"Accumulator","docs":["Accumulator value tracking cumulative rewards per unit of weighted stake.","","Used in the staking system where rewards are distributed proportionally across","all stakers. Values are scaled by 2^64 for precision:","","```text","accumulator += (new_rewards << 64) / total_weighted_stake","pending_rewards = weighted_stake * (current_acc - last_acc) >> 64","```"],"type":{"kind":"struct","fields":["u128"]}},{"name":"BaseFeeConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"cliff_fee_numerator","type":"u64"},{"name":"second_factor","type":"u64"},{"name":"third_factor","type":"u64"},{"name":"first_factor","type":"u16"},{"name":"base_fee_mode","type":"u8"},{"name":"padding_0","type":{"array":["u8",5]}}]}},{"name":"BuybackAndBurnEvent","type":{"kind":"struct","fields":[{"name":"telecoin_id","type":{"defined":{"name":"TelecoinId"}}},{"name":"quote_spent","type":{"defined":{"name":"TokenAmount"}}},{"name":"telecoin_burned","type":{"defined":{"name":"TokenAmount"}}}]}},{"name":"ClaimStakingRewardsEvent","type":{"kind":"struct","fields":[{"name":"owner","docs":["Owner of the position."],"type":"pubkey"},{"name":"telecoin_mint","docs":["Telecoin mint."],"type":"pubkey"},{"name":"position_nonce","docs":["Position nonce."],"type":"u32"},{"name":"quote_rewards","docs":["Quote token rewards claimed."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"telecoin_rewards","docs":["Telecoin rewards claimed."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"position_closed","docs":["Whether the position was closed. Always false for `claim_staking_rewards`;","set to true only by `close_stake_position`."],"type":"bool"},{"name":"returned_stake","docs":["Amount returned to user if closed. Always zero for `claim_staking_rewards`;","the staked amount is only returned by `close_stake_position`."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"quote_token_accumulator","docs":["Position's quote token accumulator after claim."],"type":{"defined":{"name":"Accumulator"}}},{"name":"telecoin_accumulator","docs":["Position's telecoin accumulator after claim."],"type":{"defined":{"name":"Accumulator"}}},{"name":"telecoin_id","docs":["Telecoin ID."],"type":{"defined":{"name":"TelecoinId"}}}]}},{"name":"CreateStakePositionEvent","type":{"kind":"struct","fields":[{"name":"owner","docs":["User that will receive the staking rewards and the telecoins back."],"type":"pubkey"},{"name":"telecoin_id","docs":["Telecoin ID."],"type":{"defined":{"name":"TelecoinId"}}},{"name":"position_nonce","docs":["Position nonce for this user's stake."],"type":"u32"},{"name":"staked","docs":["Amount of telecoin staked."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"lock_period","docs":["Lock period selected."],"type":{"defined":{"name":"LockPeriod"}}},{"name":"staked_at","docs":["Timestamp when staking was created."],"type":"i64"},{"name":"quote_token_accumulator","docs":["Pool's quote token accumulator at position creation (position's initial checkpoint)."],"type":{"defined":{"name":"Accumulator"}}},{"name":"telecoin_accumulator","docs":["Pool's telecoin accumulator at position creation (position's initial checkpoint)."],"type":{"defined":{"name":"Accumulator"}}}]}},{"name":"DeployTelecoinEvent","docs":["Some Printr related details about the deploy."],"type":{"kind":"struct","fields":[{"name":"protocol_version","docs":["The protocol version of the graduating telecoin token."],"type":{"defined":{"name":"PrintrProtocolVersion"}}},{"name":"telecoin_id","docs":["The Printr Telecoin ID."],"type":{"defined":{"name":"TelecoinId"}}},{"name":"dev_on_solana","docs":["The dev's wallet on Solana.","","Will be default if this telecoin doesn't have a solana dev wallet."],"type":"pubkey"}]}},{"name":"DevConfig","docs":["Configuration for a dev of a telecoin token."],"type":{"kind":"struct","fields":[{"name":"fee_sink","docs":["Used to identify the dev for fee distribution.","","Will be the default key if this telecoin doesn't have a solana dev wallet.","That can happen if the telecoin was not printed on Solana and only deployed here","additionally without a bonding curve."],"type":{"defined":{"name":"FeeSink"}}},{"name":"protocol_version","docs":["Can be used to distinguish between breaking changes in the future."],"type":{"defined":{"name":"PrintrProtocolVersion"}}},{"name":"telecoin_id","docs":["What's the canonical telecoin ID at the time of creation.","We try not to break how telecoin IDs are derived but we neither make guarantees they won't","change."],"type":{"defined":{"name":"TelecoinId"}}},{"name":"fee_sink_quote_fee","docs":["When claiming fees we accumulate them in this counter.","When distributing the fees we reset this to zero and transfer the amount.","","# Migration","","After `PrintrProtocolVersion(3)` inclusive this field collects _all_ quote fees, both dev","fees and protocol fees."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"fees","docs":["Custom fee configuration set at print/deploy time."],"type":{"defined":{"name":"DevConfigFees"}}},{"name":"flags","docs":["Bit flags for fee distribution lifecycle."],"type":{"defined":{"name":"DevConfigFlags"}}},{"name":"_padding","type":"u8"}]}},{"name":"DevConfigFees","docs":["Custom fee overrides stored in [DevConfig].","","# Migration","","Until `PrintrProtocolVersion(3)` exclusive we used to store 8 padding bytes."],"type":{"kind":"struct","fields":[{"name":"dbc_protocol_share_of_trading_fee_pct","docs":["Protocol's share of the bonding curve trading fee as a percentage (0-100).","","For example, if the total DBC trading fee is 2% and this value is 15,","then 0.3% goes to the protocol and 1.7% goes to the dev.","","0 means not set.","This is going to be the case for telecoins printed before this struct was introduced.","In that case we use the defaults to keep the behavior consistent with what it was before."],"type":"u8"},{"name":"amm_protocol_share_of_trading_fee_pct","docs":["Protocol's share of the AMM trading fee as a percentage (0-100).","","Same semantics as [Self::dbc_protocol_share_of_trading_fee_pct] but for AMM trades."],"type":"u8"},{"name":"rev_shares","docs":["Up to 2 revshare entries for protocol fee distribution."],"type":{"array":[{"defined":{"name":"RevShareSlot"}},2]}}]}},{"name":"DevConfigFlags","docs":["Anchor-serializable wrapper around [printr_sdk::dev_config::DevConfigFlags]."],"type":{"kind":"struct","fields":["u8"]}},{"name":"DynamicFeeConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"initialized","type":"u8"},{"name":"padding","type":{"array":["u8",7]}},{"name":"max_volatility_accumulator","type":"u32"},{"name":"variable_fee_control","type":"u32"},{"name":"bin_step","type":"u16"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"padding2","type":{"array":["u8",8]}},{"name":"bin_step_u128","type":"u128"}]}},{"name":"EstimateSwapOutput","docs":["This is the returned value from this ix."],"type":{"kind":"struct","fields":[{"name":"estimate","docs":["The swap estimate."],"type":{"defined":{"name":"SwapEstimate"}}},{"name":"which_remaining_accounts_are_owned_by_system_program","docs":["The remaining accounts are mapped onto this list.","The index will be `true` if the account is owned by the system program."],"type":{"vec":"bool"}}]}},{"name":"EstimateTeleportWithLzParams","docs":["Parameters for the [TeleportWithLz] instruction."],"type":{"kind":"struct","fields":[{"name":"message_len","docs":["How long is the message to send.","","See [omnichain_codec::OutgoingMessage] for the message format."],"type":"u16"},{"name":"lz_options","docs":[""],"type":"bytes"},{"name":"lz_destination_endpoint_id","docs":["The chain ID of the destination chain.","",""],"type":"u32"}]}},{"name":"FeeSink","type":{"kind":"type","alias":"pubkey"}},{"name":"FeesClaimed","docs":["Fee claiming endpoints return how much fees were claimed - in total - for each token."],"type":{"kind":"struct","fields":[{"name":"in_telecoin","type":{"defined":{"name":"TokenAmount"}}},{"name":"in_quote","type":{"defined":{"name":"TokenAmount"}}}]}},{"name":"LiquidityDistributionConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"sqrt_price","type":"u128"},{"name":"liquidity","type":"u128"}]}},{"name":"LiquidityVestingInfo","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"is_initialized","type":"u8"},{"name":"vesting_percentage","type":"u8"},{"name":"_padding","type":{"array":["u8",2]}},{"name":"bps_per_period","type":"u16"},{"name":"number_of_periods","type":"u16"},{"name":"frequency","type":"u32"},{"name":"cliff_duration_from_migration_time","type":"u32"}]}},{"name":"LockPeriod","docs":["Lock periods for staking, each with its own reward multiplier."],"type":{"kind":"enum","variants":[{"name":"SevenDays"},{"name":"FourteenDays"},{"name":"ThirtyDays"},{"name":"SixtyDays"},{"name":"NinetyDays"},{"name":"OneHundredEightyDays"},{"name":"TenSeconds"}]}},{"name":"LockedVestingConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"amount_per_period","type":"u64"},{"name":"cliff_duration_from_migration_time","type":"u64"},{"name":"frequency","type":"u64"},{"name":"number_of_period","type":"u64"},{"name":"cliff_unlock_amount","type":"u64"},{"name":"_padding","type":"u64"}]}},{"name":"LzAccount","docs":["same to anchor_lang::prelude::AccountMeta"],"type":{"kind":"struct","fields":[{"name":"pubkey","type":"pubkey"},{"name":"is_signer","type":"bool"},{"name":"is_writable","type":"bool"}]}},{"name":"LzReceiveParams","type":{"kind":"struct","fields":[{"name":"src_eid","type":"u32"},{"name":"sender","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"guid","type":{"array":["u8",32]}},{"name":"message","type":"bytes"},{"name":"extra_data","type":"bytes"}]}},{"name":"Peer","docs":["A peer in omnichain network is a connection living on another chain,","typically a smart contract.","","A peer is authorized by the Printr staff to send and receive messages","authoritatively.","This suggests a reciprocal relationship between the two peers.","","To trust a message we must validate that it was sent by a peer.","Validation is done by relaying networks such as Axelar or LayerZero.","Acts as a connection to another chain."],"type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"address","docs":["The address of the peer on the other chain.","Is part of the seed"],"type":{"array":["u8",32]}},{"name":"is_lz_enabled","docs":["Admin can enable and disable the LZ communication channel to the peer.","","Multiple peers can be enabled."],"type":"bool"},{"name":"is_lz_default","docs":["Use this peer as the default for LZ teleports.","","Multiple peers can be set as default in which case they are equivalent from the POV of the","security of the protocol."],"type":"bool"}]}},{"name":"PoolConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"quote_mint","docs":["quote mint"],"type":"pubkey"},{"name":"fee_claimer","docs":["Address to get the fee"],"type":"pubkey"},{"name":"leftover_receiver","docs":["Address to receive extra base token after migration, in case token is fixed supply"],"type":"pubkey"},{"name":"pool_fees","docs":["Pool fee"],"type":{"defined":{"name":"PoolFeesConfig"}}},{"name":"partner_liquidity_vesting_info","type":{"defined":{"name":"LiquidityVestingInfo"}}},{"name":"creator_liquidity_vesting_info","type":{"defined":{"name":"LiquidityVestingInfo"}}},{"name":"padding_0","docs":["Padding for future use"],"type":{"array":["u8",14]}},{"name":"padding_1","docs":["Previously was protocol and referral fee percent. Beware of tombstone."],"type":"u16"},{"name":"collect_fee_mode","docs":["Collect fee mode"],"type":"u8"},{"name":"migration_option","docs":["migration option"],"type":"u8"},{"name":"activation_type","docs":["whether mode slot or timestamp"],"type":"u8"},{"name":"token_decimal","docs":["token decimals"],"type":"u8"},{"name":"version","docs":["version"],"type":"u8"},{"name":"token_type","docs":["token type of base token"],"type":"u8"},{"name":"quote_token_flag","docs":["quote token flag"],"type":"u8"},{"name":"partner_permanent_locked_liquidity_percentage","docs":["partner locked liquidity percentage"],"type":"u8"},{"name":"partner_liquidity_percentage","docs":["partner liquidity percentage"],"type":"u8"},{"name":"creator_permanent_locked_liquidity_percentage","docs":["creator post migration fee percentage"],"type":"u8"},{"name":"creator_liquidity_percentage","docs":["creator liquidity percentage"],"type":"u8"},{"name":"migration_fee_option","docs":["migration fee option"],"type":"u8"},{"name":"fixed_token_supply_flag","docs":["flag to indicate whether token is dynamic supply (0) or fixed supply (1)"],"type":"u8"},{"name":"creator_trading_fee_percentage","docs":["creator trading fee percentage"],"type":"u8"},{"name":"token_update_authority","docs":["token update authority"],"type":"u8"},{"name":"migration_fee_percentage","docs":["migration fee percentage"],"type":"u8"},{"name":"creator_migration_fee_percentage","docs":["creator migration fee percentage"],"type":"u8"},{"name":"padding_2","type":{"array":["u8",7]}},{"name":"swap_base_amount","docs":["swap base amount"],"type":"u64"},{"name":"migration_quote_threshold","docs":["migration quote threshold (in quote token)"],"type":"u64"},{"name":"migration_base_threshold","docs":["migration base threshold (in base token)"],"type":"u64"},{"name":"migration_sqrt_price","docs":["migration sqrt price"],"type":"u128"},{"name":"locked_vesting_config","docs":["locked vesting config"],"type":{"defined":{"name":"LockedVestingConfig"}}},{"name":"pre_migration_token_supply","docs":["pre migration token supply"],"type":"u64"},{"name":"post_migration_token_supply","docs":["post migration token supply"],"type":"u64"},{"name":"migrated_collect_fee_mode","docs":["migrated pool collect fee mode"],"type":"u8"},{"name":"migrated_dynamic_fee","docs":["migrated dynamic fee option."],"type":"u8"},{"name":"migrated_pool_fee_bps","docs":["migrated pool fee in bps"],"type":"u16"},{"name":"migrated_pool_base_fee_mode","type":"u8"},{"name":"enable_first_swap_with_min_fee","type":"u8"},{"name":"migrated_compounding_fee_bps","docs":["compounding fee bps for migrated DAMM v2 pool, should only be non-zero if migrated_collect_fee_mode is 2 (Compounding)"],"type":"u16"},{"name":"pool_creation_fee","docs":["pool creation fee in lamports value"],"type":"u64"},{"name":"migrated_pool_base_fee_bytes","docs":["serialized MigratedPoolMarketCapFeeSchedulerParams, only used when migrated_pool_base_fee_mode is market cap scheduler"],"type":{"array":["u8",16]}},{"name":"sqrt_start_price","docs":["minimum price"],"type":"u128"},{"name":"curve","docs":["curve, only use 20 point firstly, we can extend that latter"],"type":{"array":[{"defined":{"name":"LiquidityDistributionConfig"}},20]}}]}},{"name":"PoolFeesConfig","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"base_fee","type":{"defined":{"name":"BaseFeeConfig"}}},{"name":"dynamic_fee","type":{"defined":{"name":"DynamicFeeConfig"}}}]}},{"name":"PoolMetrics","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"total_protocol_base_fee","type":"u64"},{"name":"total_protocol_quote_fee","type":"u64"},{"name":"total_trading_base_fee","type":"u64"},{"name":"total_trading_quote_fee","type":"u64"}]}},{"name":"PrintTelecoinEvent","docs":["Some Printr related details about the launch on top of meteora events."],"type":{"kind":"struct","fields":[{"name":"protocol_version","docs":["The protocol version of the graduating telecoin token."],"type":{"defined":{"name":"PrintrProtocolVersion"}}},{"name":"telecoin_id","docs":["The Printr Telecoin ID."],"type":{"defined":{"name":"TelecoinId"}}},{"name":"quote_mint","docs":["Which mint is used as a quote."],"type":"pubkey"},{"name":"dev_on_solana","docs":["The dev's wallet on Solana."],"type":"pubkey"}]}},{"name":"PrintrConfig","docs":["Configuration struct holding the public keys for various Printr program wallets.","","# Fields","- `printr_fee_authority`: Receives Printr's portion of the protocol fees.","- `questflow_ai_fee_authority`: Receives QuestFlow AI's portion of the protocol fees.","- `staking_authority`: Receives all fees that are in telecoin.","","# Authority","","This account is an authority over several token account vaults that hold protocol and dev fees."],"type":{"kind":"struct","fields":[{"name":"printr_fee_authority","docs":["This account receives Printr's portion of the protocol fees."],"type":"pubkey"},{"name":"questflow_ai_fee_authority","docs":["This account receives QuestFlow AI's portion of the protocol fees."],"type":"pubkey"},{"name":"_padding_1","docs":["# Migration","","See [Self::migrate_if_necessary]."],"type":{"array":["u8",32]}},{"name":"staking_authority","docs":["Receives all fees that are in telecoin."],"type":"pubkey"},{"name":"_padding_2","docs":["# Migration","","See [Self::migrate_if_necessary]."],"type":{"array":["u8",2]}},{"name":"_padding_3","docs":["# Migration","","See [Self::migrate_if_necessary]."],"type":{"array":["u8",2]}},{"name":"_padding_4","docs":["# Migration","","See [Self::migrate_if_necessary]."],"type":{"array":["u8",2]}},{"name":"_padding_5","docs":["# Migration","","See [Self::migrate_if_necessary]."],"type":{"array":["u8",2]}},{"name":"_padding_6","docs":["# Migration","","See [Self::migrate_if_necessary]."],"type":{"array":["u8",1]}},{"name":"last_update","docs":["# Migration","","This field was originally padding.","It was therefore set to 0.","We repurposed it to [PrintrProtocolVersion] to be able to track migrations of this struct."],"type":{"defined":{"name":"PrintrProtocolVersion"}}}]}},{"name":"PrintrProtocolVersion","docs":["Stores the protocol version that the telecoin was created with."],"type":{"kind":"struct","fields":["u8"]}},{"name":"ProtocolFeeSink","docs":["Lookup table for protocol fee sinks.","","Main raison d'etre for this is to avoid storage cost and migrations on Solana.","We agreed with the business side of Printr that the list will be rather static and won't require","additions in near future.","So we buy ourselves time by skipping the need for migrations this way."],"repr":{"kind":"rust"},"type":{"kind":"enum","variants":[{"name":"None"},{"name":"QuestflowAi"}]}},{"name":"RefreshStaking2Event","type":{"kind":"struct","fields":[{"name":"telecoin_id","docs":["Telecoin ID, derives the mint."],"type":{"defined":{"name":"TelecoinId"}}},{"name":"quote_rewards","docs":["Quote token rewards distributed to stakers."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"protocol_quote_fees","docs":["Quote token fees sent to protocol + revshare wallets."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"telecoin_rewards","docs":["Telecoin rewards added."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"quote_token_accumulator","docs":["New quote token accumulator value."],"type":{"defined":{"name":"Accumulator"}}},{"name":"telecoin_accumulator","docs":["New telecoin accumulator value."],"type":{"defined":{"name":"Accumulator"}}},{"name":"protocol_telecoin_fees","docs":["Telecoin fees sent to the protocol staking wallet."],"type":{"defined":{"name":"TokenAmount"}}}]}},{"name":"RevShareSlot","type":{"kind":"struct","fields":[{"name":"sink","type":{"defined":{"name":"ProtocolFeeSink"}}},{"name":"share_of_protocol_fee_pct","docs":["This partner's share of the protocol fee as a percentage (0-100).","","The fee hierarchy is:","1. A trade incurs a **trading fee** (e.g. 1% of trade volume).","2. The protocol takes a share of that trading fee","([DevConfigFees::dbc_protocol_share_of_trading_fee_pct] or","[DevConfigFees::amm_protocol_share_of_trading_fee_pct], e.g. 75%).","3. This field is a percentage of *that* protocol share (e.g. 10%).","","Concrete example: 1% trading fee, 75% protocol share, 10% revshare →","effective revshare = 1% × 75% × 10% = 0.075% of trade volume."],"type":"u8"}]}},{"name":"SetDevConfigDevEvent","docs":["Emitted whenever an admin updates the fee sink on a dev config."],"type":{"kind":"struct","fields":[{"name":"telecoin_id","docs":["The Printr Telecoin ID whose dev config was updated."],"type":{"defined":{"name":"TelecoinId"}}},{"name":"telecoin_mint","docs":["The telecoin mint whose dev config was updated."],"type":"pubkey"},{"name":"previous_fee_sink","docs":["The fee sink before the update."],"type":{"defined":{"name":"FeeSink"}}},{"name":"new_fee_sink","docs":["The fee sink after the update."],"type":{"defined":{"name":"FeeSink"}}}]}},{"name":"SetDevConfigFeesParams","type":{"kind":"struct","fields":[{"name":"dbc_protocol_share_of_trading_fee_pct","type":"u8"},{"name":"amm_protocol_share_of_trading_fee_pct","type":"u8"}]}},{"name":"SetPeerConfParams","docs":["Parameters for the [SetPeerConf] instruction."],"type":{"kind":"struct","fields":[{"name":"enable_lz","type":{"option":"bool"}},{"name":"set_as_default","type":{"option":"bool"}}]}},{"name":"SetTelecoinTeleportConfParams","docs":["Parameters for the [SetTelecoinTeleportConf] instruction."],"type":{"kind":"struct","fields":[{"name":"set_lz_peer_config","type":{"option":{"defined":{"name":"TeleportPathConfig"}}}}]}},{"name":"SqrtBitshiftedPrice","docs":["This is a sqrt and bitshifted price ready to be compared to Meteora prices."],"type":{"kind":"struct","fields":[{"name":"price","docs":["The inner raw value."],"type":"u128"}]}},{"name":"StakePosition","docs":["Individual user's stake position in a staking pool.","","PDA seeds:","```ignore","[","SEED_PREFIX_FOR_STAKE_POSITION,","telecoin_mint.as_ref(),","owner.as_ref(),","position_nonce.to_le_bytes().as_ref(),","]","```","","The position_nonce allows users to have multiple stake positions per telecoin."],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump seed for PDA derivation."],"type":"u8"},{"name":"position_nonce","docs":["Position nonce - allows multiple positions per user per telecoin.","","Stored as u32 and used in PDA derivation as little-endian bytes (to_le_bytes()).","There can be only one position per (telecoin, owner, position_nonce) tuple at the time.","However, position nonces can be reused after a position is closed (unlocked and fully","claimed)."],"type":"u32"},{"name":"staked","docs":["Amount of telecoin staked."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"lock_period","docs":["Lock period selected by user."],"type":{"defined":{"name":"LockPeriod"}}},{"name":"staked_at_clock_timestamp","docs":["Timestamp from Clock sysvar when the stake was created."],"type":"i64"},{"name":"quote_token_last_accumulator","docs":["Quote token accumulator value when user last claimed or staked."],"type":{"defined":{"name":"Accumulator"}}},{"name":"telecoin_last_accumulator","docs":["Telecoin accumulator value when user last claimed or staked."],"type":{"defined":{"name":"Accumulator"}}}]}},{"name":"StakingPool","docs":["One pool per telecoin. Users stake the telecoin and earn rewards in multiple tokens:","","- Quote token trading fees","- Telecoin trading fees","","PDA seeds: `[b\"staking_pool\", telecoin_mint.as_ref()]`","","Note: Mint addresses can be derived from telecoin_id, so they're not stored here."],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump seed for PDA derivation."],"type":"u8"},{"name":"refreshed_at_clock_slot","docs":["Last slot when the pool was refreshed."],"type":"u64"},{"name":"telecoin_id","docs":["Telecoin ID this pool is associated with."],"type":{"defined":{"name":"TelecoinId"}}},{"name":"total_weighted_stake","docs":["Total weighted stake across all users.","","weighted_stake = sum(user_staked_amount × lock_multiplier_bps / 10000)"],"type":"u128"},{"name":"quote_token_accumulator","docs":["Accumulator for quote token rewards."],"type":{"defined":{"name":"Accumulator"}}},{"name":"telecoin_accumulator","docs":["Accumulator for telecoin rewards."],"type":{"defined":{"name":"Accumulator"}}},{"name":"last_telecoin_vault_balance","docs":["Last observed telecoin vault balance for calculating new rewards."],"type":{"defined":{"name":"TokenAmount"}}}]}},{"name":"SwapEstimate","type":{"kind":"struct","fields":[{"name":"actual_input","docs":["What goes into a pool (either bonding curve or AMM).","Does not include the fees."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"input_fee","docs":["What's spent on fees. (Input)"],"type":{"defined":{"name":"TokenAmount"}}},{"name":"output_fee","docs":["What's spent on fees. (Output)"],"type":{"defined":{"name":"TokenAmount"}}},{"name":"output","docs":["What comes out of the pool.","Does not include the fees."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"next_price","docs":["The sqrt price after the swap."],"type":{"defined":{"name":"SqrtBitshiftedPrice"}}},{"name":"current_price","docs":["The sqrt price after the swap."],"type":{"defined":{"name":"SqrtBitshiftedPrice"}}}]}},{"name":"SwapIntent","docs":["Represents user's intent to swap tokens in Printr ecosystem.","We translate this intent to specific Meteora CPIs."],"type":{"kind":"enum","variants":[{"name":"SpendQuote","fields":[{"name":"sell","docs":["Quote amount"],"type":{"defined":{"name":"TokenAmount"}}},{"name":"max_price","docs":["In Printr we apply slippage to price."],"type":{"defined":{"name":"SqrtBitshiftedPrice"}}}]},{"name":"SellTelecoin","fields":[{"name":"sell","docs":["telecoin amount"],"type":{"defined":{"name":"TokenAmount"}}},{"name":"min_price","docs":["In Printr we apply slippage to price."],"type":{"defined":{"name":"SqrtBitshiftedPrice"}}}]},{"name":"SpendAllQuote","fields":[{"name":"special_value","docs":["Must be equal to [printr_sdk::consts::SPEND_ALL_SPECIAL_VALUE]."],"type":"u64"},{"name":"max_price","docs":["In Printr we apply slippage to price."],"type":{"defined":{"name":"SqrtBitshiftedPrice"}}}]}]}},{"name":"TelecoinId","docs":["Printr's Token ID is a hash of the ABI-encoded telecoin params."],"type":{"kind":"struct","fields":[{"array":["u8",32]}]}},{"name":"TelecoinParams","docs":["The core primitive of the Printr protocol.","","These telecoin params uniquely identify a token launched with Printr."],"type":{"kind":"struct","fields":[{"name":"salt","type":{"array":["u8",32]}},{"name":"dev_addresses","docs":["# Note","","At this point, `dev_addresses` has become a misnomer.","It contains more information than that."],"type":"bytes"},{"name":"name","type":{"array":["u8",32]}},{"name":"symbol","type":{"array":["u8",32]}},{"name":"packed_params","type":{"array":["u8",32]}},{"name":"chains","type":{"vec":{"array":["u8",32]}}},{"name":"quote_pairs","type":{"vec":{"array":["u8",32]}}},{"name":"quote_prices","type":"bytes"}]}},{"name":"TelecoinParamsCompact","docs":["On Solana we're dealing with txs size limits.","Being able to compress the telecoin params into a smaller structure helps us submit smaller txs.","","You can always convert this back to [TelecoinParams]."],"type":{"kind":"struct","fields":[{"name":"version","docs":["It's nice to version this in case we need to further compact it or do other changes."],"type":"u8"},{"name":"data","docs":["The compact representation of the telecoin params."],"type":"bytes"}]}},{"name":"TelecoinTeleportConfig","docs":["Overrides default configuration for a specific telecoin with respect to omnichain transfers."],"type":{"kind":"struct","fields":[{"name":"lz_peer","docs":["If set then only the specified peer can be used to teleport this telecoin."],"type":{"defined":{"name":"TeleportPathConfig"}}}]}},{"name":"TeleportPathConfig","docs":["Defines how teleports should be handled."],"type":{"kind":"enum","variants":[{"name":"Default"},{"name":"Disabled"},{"name":"Peers","fields":[{"name":"teleport_any_direction1","docs":["This peer can be used for teleports in any direction: mint and burn."],"type":"pubkey"},{"name":"teleport_any_direction2","docs":["This peer can be used for teleports in any direction: mint and burn."],"type":"pubkey"},{"name":"teleport_in_only","docs":["This peer can be used for teleports in only: mint"],"type":"pubkey"}]}]}},{"name":"TeleportWithLzEvent","docs":["Event emitted by the [TeleportWithLz] instruction."],"type":{"kind":"struct","fields":[{"name":"guid","type":{"array":["u8",32]}},{"name":"nonce","type":"u64"},{"name":"lz_destination_endpoint_id","type":"u32"},{"name":"telecoin_id","type":{"defined":{"name":"TelecoinId"}}},{"name":"destination_recipient_address","type":{"array":["u8",32]}},{"name":"destination_peer_address","type":{"array":["u8",32]}},{"name":"source_user_address","type":"pubkey"},{"name":"teleporting","type":{"defined":{"name":"TokenAmount"}}}]}},{"name":"TeleportWithLzParams","docs":["Parameters for the [TeleportWithLz] instruction.","Used to construct [OutgoingMessage]."],"type":{"kind":"struct","fields":[{"name":"to_send","docs":["Burns this much telecoin tokens and mints them again on the other chain."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"destination_recipient_address","docs":["Who to mint the telecoin tokens to."],"type":{"array":["u8",32]}},{"name":"telecoin_id","docs":["Printr Telecoin ID derives the telecoin mint."],"type":{"defined":{"name":"TelecoinId"}}},{"name":"lamports_gas","docs":["How much to pay LZ for the transaction."],"type":{"defined":{"name":"TokenAmount"}}},{"name":"lz_options","docs":[""],"type":"bytes"},{"name":"lz_destination_endpoint_id","docs":["The chain ID of the destination chain.","",""],"type":"u32"}]}},{"name":"TokenAmount","docs":["A token amount in its smallest denomination as seen in a token account."],"type":{"kind":"struct","fields":[{"name":"amount","docs":["Wraps around this value to give it a meaning."],"type":"u64"}]}},{"name":"VirtualPool","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"volatility_tracker","docs":["volatility tracker"],"type":{"defined":{"name":"VolatilityTracker"}}},{"name":"config","docs":["config key"],"type":"pubkey"},{"name":"creator","docs":["creator"],"type":"pubkey"},{"name":"base_mint","docs":["base mint"],"type":"pubkey"},{"name":"base_vault","docs":["base vault"],"type":"pubkey"},{"name":"quote_vault","docs":["quote vault"],"type":"pubkey"},{"name":"base_reserve","docs":["base reserve"],"type":"u64"},{"name":"quote_reserve","docs":["quote reserve"],"type":"u64"},{"name":"protocol_base_fee","docs":["protocol base fee"],"type":"u64"},{"name":"protocol_quote_fee","docs":["protocol quote fee"],"type":"u64"},{"name":"partner_base_fee","docs":["partner base fee"],"type":"u64"},{"name":"partner_quote_fee","docs":["trading quote fee"],"type":"u64"},{"name":"sqrt_price","docs":["current price"],"type":"u128"},{"name":"activation_point","docs":["Activation point"],"type":"u64"},{"name":"pool_type","docs":["pool type, spl token or token2022"],"type":"u8"},{"name":"is_migrated","docs":["is migrated"],"type":"u8"},{"name":"is_partner_withdraw_surplus","docs":["is partner withdraw surplus"],"type":"u8"},{"name":"is_protocol_withdraw_surplus","docs":["is protocol withdraw surplus"],"type":"u8"},{"name":"migration_progress","docs":["migration progress"],"type":"u8"},{"name":"is_withdraw_leftover","docs":["is withdraw leftover"],"type":"u8"},{"name":"is_creator_withdraw_surplus","docs":["is creator withdraw surplus"],"type":"u8"},{"name":"migration_fee_withdraw_status","docs":["migration fee withdraw status","bit 1 (0b010) creator","bit 2 (0b100) partner"],"type":"u8"},{"name":"metrics","docs":["pool metrics"],"type":{"defined":{"name":"PoolMetrics"}}},{"name":"finish_curve_timestamp","docs":["The time curve is finished"],"type":"u64"},{"name":"creator_base_fee","docs":["creator base fee"],"type":"u64"},{"name":"creator_quote_fee","docs":["creator quote fee"],"type":"u64"},{"name":"legacy_creation_fee_bits","docs":["legacy creation fee bits, we dont use this now"],"type":"u8"},{"name":"creation_fee_bits","docs":["pool creation fee claim status"],"type":"u8"},{"name":"has_swap","docs":["Cached flag"],"type":"u8"},{"name":"_padding_0","docs":["Padding for further use"],"type":{"array":["u8",5]}},{"name":"protocol_liquidity_migration_fee_bps","type":"u16"},{"name":"_padding_1","type":{"array":["u8",6]}},{"name":"protocol_migration_base_fee_amount","type":"u64"},{"name":"protocol_migration_quote_fee_amount","type":"u64"},{"name":"_padding_2","docs":["Padding for further use"],"type":{"array":["u64",3]}}]}},{"name":"VolatilityTracker","serialization":"bytemuck","repr":{"kind":"c"},"type":{"kind":"struct","fields":[{"name":"last_update_timestamp","type":"u64"},{"name":"padding","type":{"array":["u8",8]}},{"name":"sqrt_price_reference","type":"u128"},{"name":"volatility_accumulator","type":"u128"},{"name":"volatility_reference","type":"u128"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json b/migration/khadhroony-bot2-reference/idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json new file mode 100644 index 0000000..d2804b5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/pump_fees.pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ.json @@ -0,0 +1 @@ +{"address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ","metadata":{"name":"pump_fees","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"claim_social_fee_pda","discriminator":[225,21,251,133,161,30,199,226],"accounts":[{"name":"recipient","writable":true},{"name":"social_fee_pda","writable":true},{"name":"fee_program_global","pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"social_claim_authority","signer":true,"relations":["fee_program_global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"user_id","type":"string"},{"name":"platform","type":"u8"}],"returns":{"option":{"defined":{"name":"SocialFeePdaClaimed"}}}},{"name":"claim_social_fee_pda_v2","discriminator":[17,77,240,134,58,188,53,149],"accounts":[{"name":"recipient","writable":true},{"name":"social_fee_pda","writable":true},{"name":"quote_mint","docs":["Quote mint for claim"],"writable":true},{"name":"associated_social_fee_pda","writable":true,"pda":{"seeds":[{"kind":"account","path":"social_fee_pda"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"associated_recipient","writable":true,"pda":{"seeds":[{"kind":"account","path":"recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"quote_token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"fee_program_global","pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"social_claim_authority","signer":true,"relations":["fee_program_global"]},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"user_id","type":"string"},{"name":"platform","type":"u8"}],"returns":{"option":{"defined":{"name":"SocialFeePdaClaimed"}}}},{"name":"crank_donation_fee_pda","discriminator":[220,10,189,167,169,17,25,69],"accounts":[{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"payer","docs":["Pays rent when [`temp_wsol_token_account`] is created (`init_if_needed`); receives rent when it is closed after the relay CPI."],"writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"fee_program_global","pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"donation_fee_pda","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,111,110,97,116,105,111,110,45,102,101,101,45,112,100,97]},{"kind":"account","path":"donation_fee_pda.base_mint","account":"DonationFeePda"},{"kind":"account","path":"donation_fee_pda.config_id","account":"DonationFeePda"}]}},{"name":"quote_mint","docs":["Quote mint from donation fee pda."],"writable":true},{"name":"donation_fee_pda_ata","docs":["WSOL ATA owned by `donation_fee_pda`."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"donation_fee_pda"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"donation_relay_program","address":"RLAYHr9TRFcKB2ubYQhspcnXiaGpaVzNQvHytt47RZu"},{"name":"donation_relay_event_authority"},{"name":"mint_whitelist"},{"name":"epoch_tracker","writable":true},{"name":"debouncer","writable":true},{"name":"debouncer_ata","writable":true}],"args":[]},{"name":"create_donation_fee_pda","discriminator":[244,139,16,88,14,255,122,26],"accounts":[{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"payer","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"fee_program_global","pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"donation_fee_pda","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,111,110,97,116,105,111,110,45,102,101,101,45,112,100,97]},{"kind":"account","path":"base_mint"},{"kind":"account","path":"config_id"}]}},{"name":"config_id","docs":["stored on the PDA, so distinct `config_id`s for the same `base_mint` derive distinct addresses."]},{"name":"base_mint"},{"name":"bonding_curve","pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pool"},{"name":"sharing_config","docs":["(derived from `[SHARING_CONFIG_SEED, base_mint]`)"],"pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"base_mint"}]}}],"args":[]},{"name":"create_fee_sharing_config","docs":["Create Fee Sharing Config"],"discriminator":[195,78,86,76,111,52,251,213],"accounts":[{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"payer","writable":true,"signer":true},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"mint"},{"name":"sharing_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"mint"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"pump_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pool","writable":true,"optional":true},{"name":"pump_amm_program","optional":true,"address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"pump_amm_event_authority","optional":true,"pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}}}],"args":[]},{"name":"create_social_fee_pda","discriminator":[144,224,59,211,78,248,202,220],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"social_fee_pda","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"fee_program_global","pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"user_id","type":"string"},{"name":"platform","type":"u8"}]},{"name":"extend_fee_config","docs":["Realloc the fee_config PDA to [`FeeConfig::CURRENT_SIZE`] (signer pays rent delta)."],"discriminator":[68,179,244,90,173,56,17,217],"accounts":[{"name":"fee_config","writable":true},{"name":"user","signer":true},{"name":"config_program_id"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"get_fees","docs":["Get Fees"],"discriminator":[231,37,126,85,207,91,63,52],"accounts":[{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"account","path":"config_program_id"}]}},{"name":"config_program_id"}],"args":[{"name":"is_pump_pool","type":"bool"},{"name":"market_cap_lamports","type":"u128"},{"name":"trade_size_lamports","type":"u64"},{"name":"is_new_quote_mint","type":{"defined":{"name":"OptionBool"}}}],"returns":{"defined":{"name":"Fees"}}},{"name":"initialize_buyback","discriminator":[250,129,236,160,227,36,103,134],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"buyback_vault","writable":true},{"name":"buyback_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"buyback_vault"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"mint"},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"index","type":"u8"}]},{"name":"initialize_fee_config","docs":["Initialize FeeConfig admin"],"discriminator":[62,162,20,133,121,65,145,27],"accounts":[{"name":"admin","writable":true,"signer":true,"address":"8LWu7QM2dGR1G8nKDHthckea57bkCzXyBTAKPJUBDHo8"},{"name":"fee_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"account","path":"config_program_id"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"config_program_id"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"initialize_fee_program_global","discriminator":[35,215,130,84,233,56,124,167],"accounts":[{"name":"authority","writable":true,"signer":true,"relations":["pump_global"]},{"name":"pump_global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"fee_program_global","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"social_claim_authority","type":"pubkey"},{"name":"disable_flags","type":"u8"},{"name":"claim_rate_limit","type":"u64"}]},{"name":"reset_fee_sharing_config","docs":["Reset Fee Sharing Config and distribute pending fees first"],"discriminator":[10,2,182,95,16,127,129,186],"accounts":[{"name":"new_admin"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"authority","signer":true},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"mint","relations":["sharing_config"]},{"name":"sharing_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve","pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"sharing_config"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"pump_program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"pump_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_amm_program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"amm_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}}},{"name":"wsol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"coin_creator_vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"sharing_config"}],"program":{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}}},{"name":"coin_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"coin_creator_vault_authority"},{"kind":"account","path":"token_program"},{"kind":"account","path":"wsol_mint"}],"program":{"kind":"account","path":"associated_token_program"}}}],"args":[]},{"name":"reset_fee_sharing_config_v2","docs":["Reset Fee Sharing Config and distribute pending fees first"],"discriminator":[169,245,17,209,94,91,248,128],"accounts":[{"name":"new_admin"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"authority","writable":true,"signer":true},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"mint","relations":["sharing_config"]},{"name":"sharing_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve","pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"sharing_config"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"pump_creator_vault"},{"kind":"account","path":"token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"pump_program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"pump_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_amm_program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"amm_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}}},{"name":"quote_mint"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"coin_creator_vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"sharing_config"}],"program":{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}}},{"name":"coin_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"coin_creator_vault_authority"},{"kind":"account","path":"token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}}],"args":[]},{"name":"revoke_fee_sharing_authority","docs":["Revoke Fee Sharing Authority"],"discriminator":[18,233,158,39,185,207,58,104],"accounts":[],"args":[]},{"name":"set_authority","discriminator":[133,250,37,21,110,163,26,121],"accounts":[{"name":"authority","writable":true,"signer":true,"relations":["fee_program_global"]},{"name":"fee_program_global","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"new_authority","type":"pubkey"}]},{"name":"set_claim_rate_limit","discriminator":[185,211,159,174,212,49,88,4],"accounts":[{"name":"authority","writable":true,"signer":true,"relations":["fee_program_global"]},{"name":"fee_program_global","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"claim_rate_limit","type":"u64"}]},{"name":"set_disable_flags","discriminator":[194,217,112,35,114,222,51,190],"accounts":[{"name":"authority","writable":true,"signer":true,"relations":["fee_program_global"]},{"name":"fee_program_global","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"disable_flags","type":"u8"}]},{"name":"set_social_claim_authority","discriminator":[147,54,184,154,136,237,185,153],"accounts":[{"name":"authority","writable":true,"signer":true,"relations":["fee_program_global"]},{"name":"fee_program_global","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"social_claim_authority","type":"pubkey"}]},{"name":"sweep_buyback","discriminator":[138,33,204,38,207,161,159,226],"accounts":[{"name":"destination","writable":true},{"name":"authority","writable":true,"signer":true,"relations":["buyback_vault"]},{"name":"buyback_vault","writable":true},{"name":"buyback_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"buyback_vault"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"destination_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"destination"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"mint"},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"index","type":"u8"}]},{"name":"transfer_fee_sharing_authority","docs":["Transfer Fee Sharing Authority"],"discriminator":[202,10,75,200,164,34,210,96],"accounts":[],"args":[]},{"name":"update_admin","docs":["Update admin (only callable by admin)"],"discriminator":[161,176,40,213,60,184,179,228],"accounts":[{"name":"admin","signer":true,"relations":["fee_config"]},{"name":"fee_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"account","path":"config_program_id"}]}},{"name":"new_admin"},{"name":"config_program_id"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"update_buyback_authority","discriminator":[66,98,113,202,121,37,219,107],"accounts":[{"name":"authority","writable":true,"signer":true,"relations":["fee_program_global"]},{"name":"fee_program_global","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"buyback_vault","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"index","type":"u8"},{"name":"new_authority","type":"pubkey"}]},{"name":"update_buyback_claim_rate_limit","discriminator":[186,95,135,190,255,199,137,170],"accounts":[{"name":"authority","writable":true,"signer":true,"relations":["fee_program_global"]},{"name":"fee_program_global","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,45,112,114,111,103,114,97,109,45,103,108,111,98,97,108]}]}},{"name":"buyback_vault","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"index","type":"u8"},{"name":"claim_rate_limit","type":"i64"}]},{"name":"update_fee_config","docs":["Set/Replace fee parameters entirely (only callable by admin)"],"discriminator":[104,184,103,242,88,151,107,20],"accounts":[{"name":"fee_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"account","path":"config_program_id"}]}},{"name":"admin","signer":true,"relations":["fee_config"]},{"name":"config_program_id"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"flat_fees","type":{"defined":{"name":"Fees"}}}]},{"name":"update_fee_shares","docs":["Update Fee Shares, make sure to distribute all the fees before calling this"],"discriminator":[189,13,136,99,187,164,237,35],"accounts":[{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"authority","signer":true},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"mint","relations":["sharing_config"]},{"name":"sharing_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve","pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"sharing_config"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"pump_program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"pump_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_amm_program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"amm_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}}},{"name":"wsol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"coin_creator_vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"sharing_config"}],"program":{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}}},{"name":"coin_creator_vault_ata","writable":true}],"args":[{"name":"shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}}]},{"name":"update_fee_shares_v2","docs":["Update Fee Shares, make sure to distribute all the fees before calling this"],"discriminator":[111,251,49,6,78,78,106,18],"accounts":[{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"authority","writable":true,"signer":true},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"mint","relations":["sharing_config"]},{"name":"sharing_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"mint"}]}},{"name":"bonding_curve","pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"sharing_config"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"pump_creator_vault"},{"kind":"account","path":"token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"pump_program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"pump_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_amm_program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"amm_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}}},{"name":"quote_mint"},{"name":"token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"coin_creator_vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"sharing_config"}],"program":{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}}},{"name":"coin_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"coin_creator_vault_authority"},{"kind":"account","path":"token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}}],"args":[{"name":"shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}}]},{"name":"update_stable_fee_config","docs":["Set/Replace fee parameters entirely (only callable by admin)"],"discriminator":[107,169,100,179,134,155,146,221],"accounts":[{"name":"fee_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"account","path":"config_program_id"}]}},{"name":"admin","signer":true,"relations":["fee_config"]},{"name":"config_program_id"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}}]},{"name":"upsert_fee_tiers","docs":["Update or expand fee tiers (only callable by admin)"],"discriminator":[227,23,150,12,77,86,94,4],"accounts":[{"name":"fee_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"account","path":"config_program_id"}]}},{"name":"admin","signer":true,"relations":["fee_config"]},{"name":"config_program_id"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"offset","type":"u8"}]},{"name":"upsert_stable_fee_tiers","docs":["Update or expand fee tiers (only callable by admin)"],"discriminator":[181,160,162,252,74,76,224,221],"accounts":[{"name":"fee_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"account","path":"config_program_id"}]}},{"name":"admin","signer":true,"relations":["fee_config"]},{"name":"config_program_id"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"offset","type":"u8"}]}],"accounts":[{"name":"BondingCurve","discriminator":[23,183,248,55,96,216,172,96]},{"name":"BuybackVault","discriminator":[153,166,71,144,179,189,137,251]},{"name":"DonationFeePda","discriminator":[246,197,96,9,193,30,93,115]},{"name":"FeeConfig","discriminator":[143,52,146,187,219,123,76,155]},{"name":"FeeProgramGlobal","discriminator":[162,165,245,49,29,37,55,242]},{"name":"Global","discriminator":[167,232,232,177,200,108,114,127]},{"name":"Pool","discriminator":[241,154,109,4,17,177,109,188]},{"name":"SharingConfig","discriminator":[216,74,9,0,56,140,93,75]},{"name":"SocialFeePda","discriminator":[139,96,53,17,42,169,206,150]}],"events":[{"name":"CreateFeeSharingConfigEvent","discriminator":[133,105,170,200,184,116,251,88]},{"name":"DonationFeePdaCranked","discriminator":[30,208,107,93,177,0,223,78]},{"name":"DonationFeePdaCreated","discriminator":[94,20,137,239,35,77,225,235]},{"name":"ExtendFeeConfigEvent","discriminator":[226,203,224,35,153,10,88,51]},{"name":"InitializeFeeConfigEvent","discriminator":[89,138,244,230,10,56,226,126]},{"name":"InitializeFeeProgramGlobalEvent","discriminator":[40,233,156,78,95,0,8,199]},{"name":"ResetFeeSharingConfigEvent","discriminator":[203,204,151,226,120,55,214,243]},{"name":"SetAuthorityEvent","discriminator":[18,175,132,66,208,201,87,242]},{"name":"SetClaimRateLimitEvent","discriminator":[13,143,143,235,181,19,51,40]},{"name":"SetDisableFlagsEvent","discriminator":[5,8,179,65,49,55,145,126]},{"name":"SetSocialClaimAuthorityEvent","discriminator":[60,118,127,132,239,52,254,14]},{"name":"SocialFeePdaClaimed","discriminator":[50,18,193,65,237,210,234,236]},{"name":"SocialFeePdaCreated","discriminator":[183,183,218,147,24,124,137,169]},{"name":"SweepBuybackEvent","discriminator":[43,56,42,214,153,57,166,137]},{"name":"UpdateAdminEvent","discriminator":[225,152,171,87,246,63,66,234]},{"name":"UpdateFeeConfigEvent","discriminator":[90,23,65,35,62,244,188,208]},{"name":"UpdateFeeSharesEvent","discriminator":[21,186,196,184,91,228,225,203]},{"name":"UpdateStableFeeConfigEvent","discriminator":[94,5,43,237,103,147,232,245]},{"name":"UpsertFeeTiersEvent","discriminator":[171,89,169,187,122,186,33,204]},{"name":"UpsertStableFeeTiersEvent","discriminator":[232,237,237,52,98,146,73,243]}],"errors":[{"code":6000,"name":"UnauthorizedProgram","msg":"Only Pump and PumpSwap programs can call this instruction"},{"code":6001,"name":"InvalidAdmin","msg":"Invalid admin"},{"code":6002,"name":"NoFeeTiers","msg":"No fee tiers provided"},{"code":6003,"name":"TooManyFeeTiers","msg":"format"},{"code":6004,"name":"OffsetNotContinuous","msg":"The offset should be <= fee_config.fee_tiers.len()"},{"code":6005,"name":"FeeTiersNotSorted","msg":"Fee tiers must be sorted by market cap threshold (ascending)"},{"code":6006,"name":"InvalidFeeTotal","msg":"Fee total must not exceed 10_000bps"},{"code":6007,"name":"InvalidSharingConfig","msg":"Invalid Sharing Config"},{"code":6008,"name":"InvalidPool","msg":"Invalid Pool"},{"code":6009,"name":"SharingConfigAdminRevoked","msg":"Sharing config authority has been revoked - sharing config can only be updated once"},{"code":6010,"name":"NoShareholders","msg":"No shareholders provided"},{"code":6011,"name":"TooManyShareholders","msg":"format"},{"code":6012,"name":"DuplicateShareholder","msg":"Duplicate shareholder address"},{"code":6013,"name":"NotEnoughRemainingAccounts","msg":"Not enough remaining accounts"},{"code":6014,"name":"InvalidShareTotal","msg":"Invalid share total - must equal 10_000 basis points"},{"code":6015,"name":"ShareCalculationOverflow","msg":"Share calculation overflow"},{"code":6016,"name":"NotAuthorized","msg":"The given account is not authorized to execute this instruction."},{"code":6017,"name":"ZeroShareNotAllowed","msg":"Shareholder cannot have zero share"},{"code":6018,"name":"SharingConfigNotActive","msg":"Fee sharing config is not active"},{"code":6019,"name":"AmmAccountsRequiredForGraduatedCoin","msg":"AMM accounts are required for graduated coins"},{"code":6020,"name":"ShareholderAccountMismatch","msg":"Remaining account key doesn't match shareholder address"},{"code":6021,"name":"FeatureDeactivated","msg":"Feature is currently deactivated"},{"code":6022,"name":"UserIdTooLong","msg":"User ID exceeds maximum length"},{"code":6023,"name":"DeprecatedInstruction","msg":"Instruction is deprecated"},{"code":6024,"name":"FeeSharesAlreadyUpdated","msg":"Reward split can only be updated once"},{"code":6025,"name":"MathOverflow","msg":"Math overflow"},{"code":6026,"name":"InvalidBuybackIndex","msg":"Invalid buybackindex"},{"code":6027,"name":"ClaimRateLimitExceeded","msg":"Claim rate limit exceeded"},{"code":6028,"name":"InvalidFeeConfigAccount","msg":"Account is not a valid FeeConfig for this instruction"},{"code":6029,"name":"AccountTypeNotSupported","msg":"Account type not supported"},{"code":6030,"name":"InvalidMint","msg":"Mint does not match quote mint"}],"types":[{"name":"BondingCurve","type":{"kind":"struct","fields":[{"name":"virtual_token_reserves","type":"u64"},{"name":"virtual_quote_reserves","type":"u64"},{"name":"real_token_reserves","type":"u64"},{"name":"real_quote_reserves","type":"u64"},{"name":"token_total_supply","type":"u64"},{"name":"complete","type":"bool"},{"name":"creator","type":"pubkey"},{"name":"is_mayhem_mode","type":"bool"}]}},{"name":"BuybackVault","type":{"kind":"struct","fields":[{"name":"authority","type":"pubkey"},{"name":"total_claimed","type":"u64"},{"name":"total_claimed_token1","type":"u64"},{"name":"total_claimed_token2","type":"u64"},{"name":"last_claimed","type":"i64"},{"name":"claim_rate_limit","type":"i64"},{"name":"_reserved","type":{"array":["u8",128]}}]}},{"name":"ConfigStatus","type":{"kind":"enum","variants":[{"name":"Paused"},{"name":"Active"}]}},{"name":"CreateFeeSharingConfigEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"bonding_curve","type":"pubkey"},{"name":"pool","type":{"option":"pubkey"}},{"name":"sharing_config","type":"pubkey"},{"name":"admin","type":"pubkey"},{"name":"initial_shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}},{"name":"status","type":{"defined":{"name":"ConfigStatus"}}}]}},{"name":"DonationFeePda","docs":["Escrow PDA for donation relay: one per (mint, donation campaign `config_id`)."],"type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"version","type":"u8"},{"name":"config_id","type":"pubkey"},{"name":"base_mint","type":"pubkey"},{"name":"quote_mint","type":"pubkey"},{"name":"creator","type":"pubkey"},{"name":"total_donated","type":"u64"},{"name":"last_crank_ts","type":"i64"},{"name":"_reserved","type":{"array":["u8",64]}}]}},{"name":"DonationFeePdaCranked","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"signer","type":"pubkey"},{"name":"donation_fee_pda","type":"pubkey"},{"name":"config_id","type":"pubkey"},{"name":"base_mint","type":"pubkey"},{"name":"quote_mint","type":"pubkey"},{"name":"creator","type":"pubkey"},{"name":"amount","type":"u64"}]}},{"name":"DonationFeePdaCreated","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"created_by","type":"pubkey"},{"name":"donation_fee_pda","type":"pubkey"},{"name":"config_id","type":"pubkey"},{"name":"base_mint","type":"pubkey"},{"name":"quote_mint","type":"pubkey"},{"name":"creator","type":"pubkey"}]}},{"name":"ExtendFeeConfigEvent","type":{"kind":"struct","fields":[{"name":"fee_config","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"current_size","type":"u64"},{"name":"new_size","type":"u64"},{"name":"timestamp","type":"i64"}]}},{"name":"FeeConfig","type":{"kind":"struct","fields":[{"name":"bump","docs":["The bump for the PDA"],"type":"u8"},{"name":"admin","docs":["The admin account that can update the fee config"],"type":"pubkey"},{"name":"flat_fees","docs":["The flat fees for non-pump pools"],"type":{"defined":{"name":"Fees"}}},{"name":"fee_tiers","docs":["The fee tiers"],"type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"stable_fee_tiers","docs":["The fee tiers"],"type":{"vec":{"defined":{"name":"FeeTier"}}}}]}},{"name":"FeeProgramGlobal","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"authority","type":"pubkey"},{"name":"disable_flags","type":"u8"},{"name":"social_claim_authority","type":"pubkey"},{"name":"claim_rate_limit","type":"u64"},{"name":"_reserved","type":{"array":["u8",256]}}]}},{"name":"FeeTier","type":{"kind":"struct","fields":[{"name":"market_cap_lamports_threshold","type":"u128"},{"name":"fees","type":{"defined":{"name":"Fees"}}}]}},{"name":"Fees","type":{"kind":"struct","fields":[{"name":"lp_fee_bps","type":"u64"},{"name":"protocol_fee_bps","type":"u64"},{"name":"creator_fee_bps","type":"u64"}]}},{"name":"Global","type":{"kind":"struct","fields":[{"name":"initialized","type":"bool"},{"name":"authority","type":"pubkey"},{"name":"fee_recipient","type":"pubkey"},{"name":"initial_virtual_token_reserves","type":"u64"},{"name":"initial_virtual_sol_reserves","type":"u64"},{"name":"initial_real_token_reserves","type":"u64"},{"name":"token_total_supply","type":"u64"},{"name":"fee_basis_points","type":"u64"},{"name":"withdraw_authority","type":"pubkey"},{"name":"enable_migrate","type":"bool"},{"name":"pool_migration_fee","type":"u64"},{"name":"creator_fee_basis_points","type":"u64"},{"name":"fee_recipients","type":{"array":["pubkey",7]}},{"name":"set_creator_authority","type":"pubkey"},{"name":"admin_set_creator_authority","type":"pubkey"},{"name":"create_v2_enabled","type":"bool"},{"name":"whitelist_pda","type":"pubkey"},{"name":"reserved_fee_recipient","type":"pubkey"},{"name":"mayhem_mode_enabled","type":"bool"},{"name":"reserved_fee_recipients","type":{"array":["pubkey",7]}}]}},{"name":"InitializeFeeConfigEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin","type":"pubkey"},{"name":"fee_config","type":"pubkey"}]}},{"name":"InitializeFeeProgramGlobalEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"authority","type":"pubkey"},{"name":"social_claim_authority","type":"pubkey"},{"name":"disable_flags","type":"u8"},{"name":"claim_rate_limit","type":"u64"}]}},{"name":"OptionBool","type":{"kind":"struct","fields":["bool"]}},{"name":"Pool","type":{"kind":"struct","fields":[{"name":"pool_bump","type":"u8"},{"name":"index","type":"u16"},{"name":"creator","type":"pubkey"},{"name":"base_mint","type":"pubkey"},{"name":"quote_mint","type":"pubkey"},{"name":"lp_mint","type":"pubkey"},{"name":"pool_base_token_account","type":"pubkey"},{"name":"pool_quote_token_account","type":"pubkey"},{"name":"lp_supply","type":"u64"},{"name":"coin_creator","type":"pubkey"},{"name":"is_mayhem_mode","type":"bool"}]}},{"name":"ResetFeeSharingConfigEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"sharing_config","type":"pubkey"},{"name":"old_admin","type":"pubkey"},{"name":"old_shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}},{"name":"new_admin","type":"pubkey"},{"name":"new_shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}},{"name":"old_version","type":"u8"},{"name":"new_version","type":"u8"}]}},{"name":"SetAuthorityEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"old_authority","type":"pubkey"},{"name":"new_authority","type":"pubkey"}]}},{"name":"SetClaimRateLimitEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"claim_rate_limit","type":"u64"}]}},{"name":"SetDisableFlagsEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"disable_flags","type":"u8"}]}},{"name":"SetSocialClaimAuthorityEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"social_claim_authority","type":"pubkey"}]}},{"name":"Shareholder","type":{"kind":"struct","fields":[{"name":"address","type":"pubkey"},{"name":"share_bps","type":"u16"}]}},{"name":"SharingConfig","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"version","type":"u8"},{"name":"status","type":{"defined":{"name":"ConfigStatus"}}},{"name":"mint","type":"pubkey"},{"name":"admin","type":"pubkey"},{"name":"admin_revoked","type":"bool"},{"name":"shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}}]}},{"name":"SocialFeePda","docs":["Platform identifier: 0=pump, 1=twitter, etc."],"type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"version","type":"u8"},{"name":"user_id","docs":["Max 20 characters to fit u64::MAX (18,446,744,073,709,551,615) as a string.","Actual storage: 4 bytes (length prefix) + 20 bytes (content) = 24 bytes."],"type":"string"},{"name":"platform","type":"u8"},{"name":"total_claimed","type":"u64"},{"name":"last_claimed","type":"u64"},{"name":"_reserved","type":{"array":["u8",128]}}]}},{"name":"SocialFeePdaClaimed","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"user_id","type":"string"},{"name":"platform","type":"u8"},{"name":"social_fee_pda","type":"pubkey"},{"name":"recipient","type":"pubkey"},{"name":"social_claim_authority","type":"pubkey"},{"name":"amount_claimed","type":"u64"},{"name":"claimable_before","type":"u64"},{"name":"lifetime_claimed","type":"u64"},{"name":"recipient_balance_before","type":"u64"},{"name":"recipient_balance_after","type":"u64"},{"name":"quote_mint","type":"pubkey"}]}},{"name":"SocialFeePdaCreated","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"user_id","type":"string"},{"name":"platform","type":"u8"},{"name":"social_fee_pda","type":"pubkey"},{"name":"created_by","type":"pubkey"}]}},{"name":"SweepBuybackEvent","type":{"kind":"struct","fields":[{"name":"index","type":"u8"},{"name":"sol_amount","type":"u64"},{"name":"token_amount","type":"u64"},{"name":"destination","type":"pubkey"},{"name":"buyback_vault","type":"pubkey"},{"name":"mint","type":"pubkey"}]}},{"name":"UpdateAdminEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"old_admin","type":"pubkey"},{"name":"new_admin","type":"pubkey"}]}},{"name":"UpdateFeeConfigEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin","type":"pubkey"},{"name":"fee_config","type":"pubkey"},{"name":"fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"flat_fees","type":{"defined":{"name":"Fees"}}}]}},{"name":"UpdateFeeSharesEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"sharing_config","type":"pubkey"},{"name":"admin","type":"pubkey"},{"name":"new_shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}},{"name":"version","type":"u8"}]}},{"name":"UpdateStableFeeConfigEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin","type":"pubkey"},{"name":"fee_config","type":"pubkey"},{"name":"stable_fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"flat_fees","type":{"defined":{"name":"Fees"}}}]}},{"name":"UpsertFeeTiersEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin","type":"pubkey"},{"name":"fee_config","type":"pubkey"},{"name":"fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"offset","type":"u8"}]}},{"name":"UpsertStableFeeTiersEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin","type":"pubkey"},{"name":"fee_config","type":"pubkey"},{"name":"stable_fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"offset","type":"u8"}]}}],"constants":[{"name":"AMM_CREATOR_VAULT_AUTHORITY_SEED","type":{"array":["u8",13]},"value":"[99, 114, 101, 97, 116, 111, 114, 95, 118, 97, 117, 108, 116]"},{"name":"BUYBACK_VAULT_SEED","type":{"array":["u8",13]},"value":"[98, 117, 121, 98, 97, 99, 107, 45, 118, 97, 117, 108, 116]"},{"name":"DEBOUNCER_V1","type":"bytes","value":"[100, 101, 98, 111, 117, 110, 99, 101, 114, 95, 118, 49]"},{"name":"DONATION_FEE_PDA_SEED","type":{"array":["u8",16]},"value":"[100, 111, 110, 97, 116, 105, 111, 110, 45, 102, 101, 101, 45, 112, 100, 97]"},{"name":"EPOCH_TRACKER_V1","type":"bytes","value":"[101, 112, 111, 99, 104, 95, 116, 114, 97, 99, 107, 101, 114, 95, 118, 49]"},{"name":"FEE_CONFIG_SEED","type":"bytes","value":"[102, 101, 101, 95, 99, 111, 110, 102, 105, 103]"},{"name":"FEE_PROGRAM_GLOBAL_SEED","type":{"array":["u8",18]},"value":"[102, 101, 101, 45, 112, 114, 111, 103, 114, 97, 109, 45, 103, 108, 111, 98, 97, 108]"},{"name":"IX_DONATE_PUBKEY_CONFIG_ID_WITH_PAYER_V1","type":{"array":["u8",8]},"value":"[120, 217, 57, 241, 135, 104, 139, 184]"},{"name":"MAX_BUYBACK_INDEX","type":"u8","value":"8"},{"name":"MINT_WHITELIST_V1","type":"bytes","value":"[109, 105, 110, 116, 95, 119, 104, 105, 116, 101, 108, 105, 115, 116, 95, 118, 49]"},{"name":"PUMP_CREATOR_VAULT_SEED","type":{"array":["u8",13]},"value":"[99, 114, 101, 97, 116, 111, 114, 45, 118, 97, 117, 108, 116]"},{"name":"PUMP_GLOBAL_SEED","docs":["Bonding Curve Program Global Seed"],"type":{"array":["u8",6]},"value":"[103, 108, 111, 98, 97, 108]"},{"name":"SHARING_CONFIG_SEED","type":{"array":["u8",14]},"value":"[115, 104, 97, 114, 105, 110, 103, 45, 99, 111, 110, 102, 105, 103]"},{"name":"SOCIAL_FEE_PDA_SEED","type":{"array":["u8",14]},"value":"[115, 111, 99, 105, 97, 108, 45, 102, 101, 101, 45, 112, 100, 97]"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/pump_fun.6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P.json b/migration/khadhroony-bot2-reference/idls/pump_fun.6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P.json new file mode 100644 index 0000000..670461a --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/pump_fun.6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P.json @@ -0,0 +1 @@ +{"address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P","metadata":{"name":"pump","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"add_quote_mint","discriminator":[111,121,21,56,40,24,94,209],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","writable":true,"signer":true,"relations":["global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"quote_mint","type":"pubkey"}]},{"name":"admin_set_creator","docs":["Allows Global::admin_set_creator_authority to override the bonding curve creator"],"discriminator":[69,25,171,142,57,239,13,4],"accounts":[{"name":"admin_set_creator_authority","signer":true,"relations":["global"]},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"mint"},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"creator","type":"pubkey"}]},{"name":"admin_set_idl_authority","discriminator":[8,217,96,231,144,104,192,5],"accounts":[{"name":"authority","signer":true,"relations":["global"]},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"idl_account","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"program_signer","pda":{"seeds":[]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"idl_authority","type":"pubkey"}]},{"name":"admin_update_token_incentives","discriminator":[209,11,115,87,213,23,124,204],"accounts":[{"name":"authority","writable":true,"signer":true,"relations":["global"]},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"global_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"mint"},{"name":"global_incentive_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"global_volume_accumulator"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"start_time","type":"i64"},{"name":"end_time","type":"i64"},{"name":"seconds_in_a_day","type":"i64"},{"name":"day_number","type":"u64"},{"name":"pump_token_supply_per_day","type":"u64"}]},{"name":"buy","docs":["Buys tokens from a bonding curve."],"discriminator":[102,6,61,18,1,218,235,234],"accounts":[{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"fee_recipient","writable":true},{"name":"mint"},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"associated_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"associated_user","writable":true},{"name":"user","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program"},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"bonding_curve.creator","account":"BondingCurve"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}],"program":{"kind":"account","path":"fee_program"}}},{"name":"fee_program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"}],"args":[{"name":"amount","type":"u64"},{"name":"max_sol_cost","type":"u64"},{"name":"track_volume","type":{"defined":{"name":"OptionBool"}}}]},{"name":"buy_exact_quote_in_v2","discriminator":[194,171,28,70,104,77,91,47],"accounts":[{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"base_mint"},{"name":"quote_mint"},{"name":"base_token_program"},{"name":"quote_token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"fee_recipient","writable":true},{"name":"associated_quote_fee_recipient","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"buyback_fee_recipient","writable":true},{"name":"associated_quote_buyback_fee_recipient","writable":true,"pda":{"seeds":[{"kind":"account","path":"buyback_fee_recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"base_mint"}]}},{"name":"associated_base_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"base_token_program"},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"associated_quote_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user","writable":true,"signer":true},{"name":"associated_base_user","writable":true},{"name":"associated_quote_user","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"bonding_curve.creator","account":"BondingCurve"}]}},{"name":"associated_creator_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"creator_vault"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"sharing_config","docs":["seeds; the account is intentionally not deserialized here because it may be uninitialized","for mints that have not created a fee sharing config. Handlers must check","`data_is_empty()` / owner before reading."],"pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[12,53,255,169,5,90,142,86,141,168,247,188,7,86,21,39,76,241,201,44,164,31,64,0,156,81,106,164,20,194,124,112]}}},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"associated_user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"account","path":"user_volume_accumulator"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}],"program":{"kind":"account","path":"fee_program"}}},{"name":"fee_program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"}],"args":[{"name":"spendable_quote_in","type":"u64"},{"name":"min_tokens_out","type":"u64"}]},{"name":"buy_exact_sol_in","docs":["Given a budget of spendable SOL, buy at least min_tokens_out tokens.","Fees are deducted from spendable_sol_in.","","# Quote formulas","Where:","- total_fee_bps = protocol_fee_bps + creator_fee_bps (creator_fee_bps is 0 if no creator)","- floor(a/b) = a / b (integer division)","- ceil(a/b) = (a + b - 1) / b","","SOL → tokens quote","To calculate tokens_out for a given spendable_sol_in:","1. net_sol = floor(spendable_sol_in * 10_000 / (10_000 + total_fee_bps))","2. fees = ceil(net_sol * protocol_fee_bps / 10_000) + ceil(net_sol * creator_fee_bps / 10_000) (creator_fee_bps is 0 if no creator)","3. if net_sol + fees > spendable_sol_in: net_sol = net_sol - (net_sol + fees - spendable_sol_in)","4. tokens_out = floor((net_sol - 1) * virtual_token_reserves / (virtual_sol_reserves + net_sol - 1))","","Reverse quote (tokens → SOL)","To calculate spendable_sol_in for a desired number of tokens:","1. net_sol = ceil(tokens * virtual_sol_reserves / (virtual_token_reserves - tokens)) + 1","2. spendable_sol_in = ceil(net_sol * (10_000 + total_fee_bps) / 10_000)","","Rent","Separately make sure the instruction's payer has enough SOL to cover rent for:","- creator_vault: rent.minimum_balance(0)","- user_volume_accumulator: rent.minimum_balance(UserVolumeAccumulator::LEN)"],"discriminator":[56,252,116,8,158,223,205,95],"accounts":[{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"fee_recipient","writable":true},{"name":"mint"},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"associated_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"associated_user","writable":true},{"name":"user","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program"},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"bonding_curve.creator","account":"BondingCurve"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}],"program":{"kind":"account","path":"fee_program"}}},{"name":"fee_program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"}],"args":[{"name":"spendable_sol_in","type":"u64"},{"name":"min_tokens_out","type":"u64"},{"name":"track_volume","type":{"defined":{"name":"OptionBool"}}}]},{"name":"buy_v2","discriminator":[184,23,238,97,103,197,211,61],"accounts":[{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"base_mint"},{"name":"quote_mint"},{"name":"base_token_program"},{"name":"quote_token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"fee_recipient","writable":true},{"name":"associated_quote_fee_recipient","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"buyback_fee_recipient","writable":true},{"name":"associated_quote_buyback_fee_recipient","writable":true,"pda":{"seeds":[{"kind":"account","path":"buyback_fee_recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"base_mint"}]}},{"name":"associated_base_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"base_token_program"},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"associated_quote_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user","writable":true,"signer":true},{"name":"associated_base_user","writable":true},{"name":"associated_quote_user","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"bonding_curve.creator","account":"BondingCurve"}]}},{"name":"associated_creator_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"creator_vault"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"sharing_config","docs":["seeds; the account is intentionally not deserialized here because it may be uninitialized","for mints that have not created a fee sharing config. Handlers must check","`data_is_empty()` / owner before reading."],"pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[12,53,255,169,5,90,142,86,141,168,247,188,7,86,21,39,76,241,201,44,164,31,64,0,156,81,106,164,20,194,124,112]}}},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"associated_user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"account","path":"user_volume_accumulator"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}],"program":{"kind":"account","path":"fee_program"}}},{"name":"fee_program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"}],"args":[{"name":"amount","type":"u64"},{"name":"max_sol_cost","type":"u64"}]},{"name":"claim_cashback","discriminator":[37,58,35,126,190,53,228,197],"accounts":[{"name":"user","writable":true},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"}],"args":[]},{"name":"claim_cashback_v2","discriminator":[122,243,204,65,94,116,29,55],"accounts":[{"name":"user","writable":true},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"quote_mint"},{"name":"quote_token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"associated_user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"account","path":"user_volume_accumulator"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"associated_quote_user","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"}],"args":[]},{"name":"claim_token_incentives","discriminator":[16,4,71,28,204,1,40,27],"accounts":[{"name":"user"},{"name":"user_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"global_incentive_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"global_volume_accumulator"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"mint","relations":["global_volume_accumulator"]},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"payer","writable":true,"signer":true}],"args":[]},{"name":"close_user_volume_accumulator","discriminator":[249,69,164,218,150,103,84,138],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"collect_creator_fee","docs":["Collects creator_fee from creator_vault to the coin creator account"],"discriminator":[20,22,86,123,198,28,219,132],"accounts":[{"name":"creator","writable":true},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"creator"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"collect_creator_fee_v2","docs":["Collects creator_fee from creator_vault to the coin creator account"],"discriminator":[207,17,138,242,4,34,19,56],"accounts":[{"name":"creator","writable":true},{"name":"creator_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"creator"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"creator"}]}},{"name":"creator_vault_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"creator_vault"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"quote_mint"},{"name":"quote_token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"create","docs":["Creates a new coin and bonding curve."],"discriminator":[24,30,200,40,5,28,7,119],"accounts":[{"name":"mint","writable":true,"signer":true},{"name":"mint_authority","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,45,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"associated_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"mpl_token_metadata","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"metadata","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]},{"kind":"account","path":"mint"}],"program":{"kind":"account","path":"mpl_token_metadata"}}},{"name":"user","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"creator","type":"pubkey"}]},{"name":"create_v2","docs":["Creates a new spl-22 coin and bonding curve."],"discriminator":[214,144,76,236,95,139,49,180],"accounts":[{"name":"mint","writable":true,"signer":true},{"name":"mint_authority","pda":{"seeds":[{"kind":"const","value":[109,105,110,116,45,97,117,116,104,111,114,105,116,121]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"associated_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"user","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"mayhem_program_id","writable":true,"address":"MAyhSmzXzV1pTf7LsNkrNwkWKTo4ougAJ1PPg47MD4e"},{"name":"global_params","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,45,112,97,114,97,109,115]}],"program":{"kind":"const","value":[5,42,229,215,167,218,167,36,166,234,176,167,41,84,145,133,90,212,160,103,22,96,103,76,78,3,69,89,128,61,101,163]}}},{"name":"sol_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[115,111,108,45,118,97,117,108,116]}],"program":{"kind":"const","value":[5,42,229,215,167,218,167,36,166,234,176,167,41,84,145,133,90,212,160,103,22,96,103,76,78,3,69,89,128,61,101,163]}}},{"name":"mayhem_state","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,97,121,104,101,109,45,115,116,97,116,101]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[5,42,229,215,167,218,167,36,166,234,176,167,41,84,145,133,90,212,160,103,22,96,103,76,78,3,69,89,128,61,101,163]}}},{"name":"mayhem_token_vault","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"creator","type":"pubkey"},{"name":"is_mayhem_mode","type":"bool"},{"name":"is_cashback_enabled","type":{"defined":{"name":"OptionBool"}}}]},{"name":"distribute_creator_fees","docs":["Distributes creator fees to shareholders based on their share percentages","The creator vault needs to have at least the minimum distributable amount to distribute fees","This can be checked with the get_minimum_distributable_fee instruction"],"discriminator":[165,114,103,0,121,206,247,81],"accounts":[{"name":"mint","relations":["sharing_config"]},{"name":"bonding_curve","pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"sharing_config","pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[12,53,255,169,5,90,142,86,141,168,247,188,7,86,21,39,76,241,201,44,164,31,64,0,156,81,106,164,20,194,124,112]}}},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"bonding_curve.creator","account":"BondingCurve"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"}],"args":[],"returns":{"defined":{"name":"DistributeCreatorFeesEvent"}}},{"name":"distribute_creator_fees_v2","discriminator":[255,203,19,79,244,68,8,159],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"mint","relations":["sharing_config"]},{"name":"bonding_curve","pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"sharing_config","pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[12,53,255,169,5,90,142,86,141,168,247,188,7,86,21,39,76,241,201,44,164,31,64,0,156,81,106,164,20,194,124,112]}}},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"bonding_curve.creator","account":"BondingCurve"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"creator_vault_quote_token_account","docs":["Deserialized manually in the handler for non-legacy quote mints."],"writable":true,"pda":{"seeds":[{"kind":"account","path":"creator_vault"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"quote_mint"},{"name":"quote_token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[{"name":"initialize_ata","type":"bool"}],"returns":{"defined":{"name":"DistributeCreatorFeesEvent"}}},{"name":"extend_account","docs":["Extends the size of program-owned accounts"],"discriminator":[234,102,194,203,150,72,62,229],"accounts":[{"name":"account","writable":true},{"name":"user","signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"get_minimum_distributable_fee","docs":["Permissionless instruction to check the minimum required fees for distribution","Returns the minimum required balance from the creator_vault and whether distribution can proceed"],"discriminator":[117,225,127,202,134,95,68,35],"accounts":[{"name":"mint","relations":["sharing_config"]},{"name":"bonding_curve","pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"sharing_config","pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[12,53,255,169,5,90,142,86,141,168,247,188,7,86,21,39,76,241,201,44,164,31,64,0,156,81,106,164,20,194,124,112]}}},{"name":"creator_vault","pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"bonding_curve.creator","account":"BondingCurve"}]}}],"args":[],"returns":{"defined":{"name":"MinimumDistributableFeeEvent"}}},{"name":"init_user_volume_accumulator","discriminator":[94,6,202,115,255,96,232,183],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"user"},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"initialize","docs":["Creates the global state."],"discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"user","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"migrate","docs":["Migrates liquidity to pump_amm if the bonding curve is complete"],"discriminator":[155,234,231,146,236,158,162,30],"accounts":[{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"withdraw_authority","writable":true,"relations":["global"]},{"name":"mint"},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"associated_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"mint"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user","signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"pump_amm","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"const","value":[0,0]},{"kind":"account","path":"pool_authority"},{"kind":"account","path":"mint"},{"kind":"account","path":"wsol_mint"}],"program":{"kind":"account","path":"pump_amm"}}},{"name":"pool_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,45,97,117,116,104,111,114,105,116,121]},{"kind":"account","path":"mint"}]}},{"name":"pool_authority_mint_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_authority"},{"kind":"account","path":"mint"},{"kind":"account","path":"mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"pool_authority_wsol_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_authority"},{"kind":"account","path":"token_program"},{"kind":"account","path":"wsol_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"amm_global_config","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,99,111,110,102,105,103]}],"program":{"kind":"account","path":"pump_amm"}}},{"name":"wsol_mint","address":"So11111111111111111111111111111111111111112"},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,108,112,95,109,105,110,116]},{"kind":"account","path":"pool"}],"program":{"kind":"account","path":"pump_amm"}}},{"name":"user_pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_authority"},{"kind":"account","path":"token_2022_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"pool_base_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"mint"},{"kind":"account","path":"mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"pool_quote_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"token_program"},{"kind":"account","path":"wsol_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"pump_amm_event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"account","path":"pump_amm"}}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[]},{"name":"migrate_bonding_curve_creator","discriminator":[87,124,52,191,52,38,214,232],"accounts":[{"name":"mint","relations":["sharing_config"]},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"sharing_config","pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[12,53,255,169,5,90,142,86,141,168,247,188,7,86,21,39,76,241,201,44,164,31,64,0,156,81,106,164,20,194,124,112]}}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"migrate_v2","docs":["Migrates liquidity to pump_amm if the bonding curve is complete"],"discriminator":[187,203,18,31,206,237,254,41],"accounts":[{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"withdraw_authority","writable":true,"relations":["global"]},{"name":"base_mint"},{"name":"quote_mint"},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"base_mint"}]}},{"name":"associated_base_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"base_token_program"},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"associated_quote_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"user","signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"pump_amm","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"const","value":[0,0]},{"kind":"account","path":"pool_authority"},{"kind":"account","path":"base_mint"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"pump_amm"}}},{"name":"pool_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,45,97,117,116,104,111,114,105,116,121]},{"kind":"account","path":"base_mint"}]}},{"name":"pool_authority_mint_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_authority"},{"kind":"account","path":"base_token_program"},{"kind":"account","path":"base_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"pool_authority_quote_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_authority"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"amm_global_config","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,99,111,110,102,105,103]}],"program":{"kind":"account","path":"pump_amm"}}},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,108,112,95,109,105,110,116]},{"kind":"account","path":"pool"}],"program":{"kind":"account","path":"pump_amm"}}},{"name":"user_pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_authority"},{"kind":"account","path":"token_2022_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"pool_base_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"base_token_program"},{"kind":"account","path":"base_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"pool_quote_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"base_token_program"},{"name":"quote_token_program"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"pump_amm_event_authority"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"remove_quote_mint","discriminator":[177,65,223,38,88,209,158,155],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","writable":true,"signer":true,"relations":["global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"quote_mint","type":"pubkey"}]},{"name":"sell","docs":["Sells tokens into a bonding curve.","For cashback coins, pass as remaining_accounts: [0] user_volume_accumulator,","[1] bonding_curve_v2. If provided and valid, creator_fee goes to user_volume_accumulator.","Otherwise, falls back to transferring creator_fee to creator_vault."],"discriminator":[51,230,133,164,1,127,131,173],"accounts":[{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"fee_recipient","writable":true},{"name":"mint"},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"associated_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"associated_user","writable":true},{"name":"user","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"bonding_curve.creator","account":"BondingCurve"}]}},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"},{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}],"program":{"kind":"account","path":"fee_program"}}},{"name":"fee_program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"}],"args":[{"name":"amount","type":"u64"},{"name":"min_sol_output","type":"u64"}]},{"name":"sell_v2","discriminator":[93,246,130,60,231,233,64,178],"accounts":[{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"base_mint"},{"name":"quote_mint"},{"name":"base_token_program"},{"name":"quote_token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"fee_recipient","writable":true},{"name":"associated_quote_fee_recipient","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"buyback_fee_recipient","writable":true},{"name":"associated_quote_buyback_fee_recipient","writable":true,"pda":{"seeds":[{"kind":"account","path":"buyback_fee_recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"base_mint"}]}},{"name":"associated_base_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"base_token_program"},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"associated_quote_bonding_curve","writable":true,"pda":{"seeds":[{"kind":"account","path":"bonding_curve"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user","writable":true,"signer":true},{"name":"associated_base_user","writable":true},{"name":"associated_quote_user","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"bonding_curve.creator","account":"BondingCurve"}]}},{"name":"associated_creator_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"creator_vault"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"sharing_config","docs":["seeds; the account is intentionally not deserialized here because it may be uninitialized","for mints that have not created a fee sharing config. Handlers must check","`data_is_empty()` / owner before reading."],"pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[12,53,255,169,5,90,142,86,141,168,247,188,7,86,21,39,76,241,201,44,164,31,64,0,156,81,106,164,20,194,124,112]}}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"associated_user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"account","path":"user_volume_accumulator"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}],"program":{"kind":"account","path":"fee_program"}}},{"name":"fee_program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"}],"args":[{"name":"amount","type":"u64"},{"name":"min_sol_output","type":"u64"}]},{"name":"set_creator","docs":["Allows Global::set_creator_authority to set the bonding curve creator from Metaplex metadata or input argument"],"discriminator":[254,148,255,112,207,142,170,165],"accounts":[{"name":"set_creator_authority","signer":true,"relations":["global"]},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"mint"},{"name":"metadata","pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]}}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"creator","type":"pubkey"}]},{"name":"set_mayhem_virtual_params","discriminator":[61,169,188,191,153,149,42,97],"accounts":[{"name":"sol_vault_authority","writable":true,"signer":true,"pda":{"seeds":[{"kind":"const","value":[115,111,108,45,118,97,117,108,116]}],"program":{"kind":"const","value":[5,42,229,215,167,218,167,36,166,234,176,167,41,84,145,133,90,212,160,103,22,96,103,76,78,3,69,89,128,61,101,163]}}},{"name":"mayhem_token_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"sol_vault_authority"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"mint"},{"name":"global","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"token_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"set_metaplex_creator","docs":["Syncs the bonding curve creator with the Metaplex metadata creator if it exists"],"discriminator":[138,96,174,217,48,85,197,246],"accounts":[{"name":"mint"},{"name":"metadata","pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]}}},{"name":"bonding_curve","writable":true,"pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"mint"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"set_params","docs":["Sets the global state parameters."],"discriminator":[27,234,178,52,147,2,187,141],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","writable":true,"signer":true,"relations":["global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"initial_virtual_token_reserves","type":"u64"},{"name":"initial_virtual_sol_reserves","type":"u64"},{"name":"initial_real_token_reserves","type":"u64"},{"name":"token_total_supply","type":"u64"},{"name":"fee_basis_points","type":"u64"},{"name":"withdraw_authority","type":"pubkey"},{"name":"enable_migrate","type":"bool"},{"name":"pool_migration_fee","type":"u64"},{"name":"creator_fee_basis_points","type":"u64"},{"name":"set_creator_authority","type":"pubkey"},{"name":"admin_set_creator_authority","type":"pubkey"}]},{"name":"set_reserved_fee_recipients","discriminator":[111,172,162,232,114,89,213,142],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","signer":true,"relations":["global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"whitelist_pda","type":"pubkey"}]},{"name":"set_virtual_quote_reserves","discriminator":[101,135,191,104,9,88,20,96],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","writable":true,"signer":true,"relations":["global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"initial_virtual_quote_reserves","type":"u64"}]},{"name":"sync_user_volume_accumulator","discriminator":[86,31,192,87,163,87,79,238],"accounts":[{"name":"user"},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"toggle_cashback_enabled","discriminator":[115,103,224,255,189,89,86,195],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","writable":true,"signer":true,"relations":["global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"enabled","type":"bool"}]},{"name":"toggle_create_v2","discriminator":[28,255,230,240,172,107,203,171],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","writable":true,"signer":true,"relations":["global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"enabled","type":"bool"}]},{"name":"toggle_mayhem_mode","discriminator":[1,9,111,208,100,31,255,163],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","writable":true,"signer":true,"relations":["global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"enabled","type":"bool"}]},{"name":"update_buyback_config","discriminator":[251,224,171,146,160,26,113,233],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","writable":true,"signer":true,"relations":["global"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"buyback_basis_points","type":{"option":"u64"}}]},{"name":"update_global_authority","discriminator":[227,181,74,196,208,21,97,213],"accounts":[{"name":"global","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108]}]}},{"name":"authority","signer":true,"relations":["global"]},{"name":"new_authority"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]}],"accounts":[{"name":"BondingCurve","discriminator":[23,183,248,55,96,216,172,96]},{"name":"FeeConfig","discriminator":[143,52,146,187,219,123,76,155]},{"name":"Global","discriminator":[167,232,232,177,200,108,114,127]},{"name":"GlobalVolumeAccumulator","discriminator":[202,42,246,43,142,190,30,255]},{"name":"SharingConfig","discriminator":[216,74,9,0,56,140,93,75]},{"name":"UserVolumeAccumulator","discriminator":[86,255,112,14,102,53,154,250]}],"events":[{"name":"AdminSetCreatorEvent","discriminator":[64,69,192,104,29,30,25,107]},{"name":"AdminSetIdlAuthorityEvent","discriminator":[245,59,70,34,75,185,109,92]},{"name":"AdminUpdateTokenIncentivesEvent","discriminator":[147,250,108,120,247,29,67,222]},{"name":"ClaimCashbackEvent","discriminator":[226,214,246,33,7,242,147,229]},{"name":"ClaimTokenIncentivesEvent","discriminator":[79,172,246,49,205,91,206,232]},{"name":"CloseUserVolumeAccumulatorEvent","discriminator":[146,159,189,172,146,88,56,244]},{"name":"CollectCreatorFeeEvent","discriminator":[122,2,127,1,14,191,12,175]},{"name":"CompleteEvent","discriminator":[95,114,97,156,212,46,152,8]},{"name":"CompletePumpAmmMigrationEvent","discriminator":[189,233,93,185,92,148,234,148]},{"name":"CreateEvent","discriminator":[27,114,169,77,222,235,99,118]},{"name":"DistributeCreatorFeesEvent","discriminator":[165,55,129,112,4,179,202,40]},{"name":"ExtendAccountEvent","discriminator":[97,97,215,144,93,146,22,124]},{"name":"InitUserVolumeAccumulatorEvent","discriminator":[134,36,13,72,232,101,130,216]},{"name":"MigrateBondingCurveCreatorEvent","discriminator":[155,167,104,220,213,108,243,3]},{"name":"MinimumDistributableFeeEvent","discriminator":[168,216,132,239,235,182,49,52]},{"name":"ReservedFeeRecipientsEvent","discriminator":[43,188,250,18,221,75,187,95]},{"name":"SetCreatorEvent","discriminator":[237,52,123,37,245,251,72,210]},{"name":"SetMetaplexCreatorEvent","discriminator":[142,203,6,32,127,105,191,162]},{"name":"SetParamsEvent","discriminator":[223,195,159,246,62,48,143,131]},{"name":"SyncUserVolumeAccumulatorEvent","discriminator":[197,122,167,124,116,81,91,255]},{"name":"TradeEvent","discriminator":[189,219,127,211,78,230,97,238]},{"name":"UpdateGlobalAuthorityEvent","discriminator":[182,195,137,42,35,206,207,247]},{"name":"UpdateMayhemVirtualParamsEvent","discriminator":[117,123,228,182,161,168,220,214]}],"errors":[{"code":6000,"name":"NotAuthorized","msg":"The given account is not authorized to execute this instruction."},{"code":6001,"name":"AlreadyInitialized","msg":"The program is already initialized."},{"code":6002,"name":"TooMuchSolRequired","msg":"slippage: Too much SOL required to buy the given amount of tokens."},{"code":6003,"name":"TooLittleSolReceived","msg":"slippage: Too little SOL received to sell the given amount of tokens."},{"code":6004,"name":"MintDoesNotMatchBondingCurve","msg":"The mint does not match the bonding curve."},{"code":6005,"name":"BondingCurveComplete","msg":"The bonding curve has completed and liquidity migrated to raydium."},{"code":6006,"name":"BondingCurveNotComplete","msg":"The bonding curve has not completed."},{"code":6007,"name":"NotInitialized","msg":"The program is not initialized."},{"code":6008,"name":"WithdrawTooFrequent","msg":"Withdraw too frequent"},{"code":6009,"name":"NewSizeShouldBeGreaterThanCurrentSize","msg":"new_size should be > current_size"},{"code":6010,"name":"AccountTypeNotSupported","msg":"Account type not supported"},{"code":6011,"name":"InitialRealTokenReservesShouldBeLessThanTokenTotalSupply","msg":"initial_real_token_reserves should be less than token_total_supply"},{"code":6012,"name":"InitialVirtualTokenReservesShouldBeGreaterThanInitialRealTokenReserves","msg":"initial_virtual_token_reserves should be greater than initial_real_token_reserves"},{"code":6013,"name":"FeeBasisPointsGreaterThanMaximum","msg":"fee_basis_points greater than maximum"},{"code":6014,"name":"AllZerosWithdrawAuthority","msg":"Withdraw authority cannot be set to System Program ID"},{"code":6015,"name":"PoolMigrationFeeShouldBeLessThanFinalRealSolReserves","msg":"pool_migration_fee should be less than final_real_sol_reserves"},{"code":6016,"name":"PoolMigrationFeeShouldBeGreaterThanCreatorFeePlusMaxMigrateFees","msg":"pool_migration_fee should be greater than creator_fee + MAX_MIGRATE_FEES"},{"code":6017,"name":"DisabledWithdraw","msg":"Migrate instruction is disabled"},{"code":6018,"name":"DisabledMigrate","msg":"Migrate instruction is disabled"},{"code":6019,"name":"InvalidCreator","msg":"Invalid creator pubkey"},{"code":6020,"name":"BuyZeroAmount","msg":"Buy zero amount"},{"code":6021,"name":"NotEnoughTokensToBuy","msg":"Not enough tokens to buy"},{"code":6022,"name":"SellZeroAmount","msg":"Sell zero amount"},{"code":6023,"name":"NotEnoughTokensToSell","msg":"Not enough tokens to sell"},{"code":6024,"name":"Overflow","msg":"Overflow"},{"code":6025,"name":"Truncation","msg":"Truncation"},{"code":6026,"name":"DivisionByZero","msg":"Division by zero"},{"code":6027,"name":"NotEnoughRemainingAccounts","msg":"Not enough remaining accounts"},{"code":6028,"name":"AllFeeRecipientsShouldBeNonZero","msg":"All fee recipients should be non-zero"},{"code":6029,"name":"UnsortedNotUniqueFeeRecipients","msg":"Unsorted or not unique fee recipients"},{"code":6030,"name":"CreatorShouldNotBeZero","msg":"Creator should not be zero"},{"code":6031,"name":"StartTimeInThePast"},{"code":6032,"name":"EndTimeInThePast"},{"code":6033,"name":"EndTimeBeforeStartTime"},{"code":6034,"name":"TimeRangeTooLarge"},{"code":6035,"name":"EndTimeBeforeCurrentDay"},{"code":6036,"name":"SupplyUpdateForFinishedRange"},{"code":6037,"name":"DayIndexAfterEndIndex"},{"code":6038,"name":"DayInActiveRange"},{"code":6039,"name":"InvalidIncentiveMint"},{"code":6040,"name":"BuyNotEnoughSolToCoverRent","msg":"Buy: Not enough SOL to cover for rent exemption."},{"code":6041,"name":"BuyNotEnoughSolToCoverFees","msg":"Buy: Not enough SOL to cover for fees."},{"code":6042,"name":"BuySlippageBelowMinTokensOut","msg":"Slippage: Would buy less tokens than expected min_tokens_out"},{"code":6043,"name":"NameTooLong"},{"code":6044,"name":"SymbolTooLong"},{"code":6045,"name":"UriTooLong"},{"code":6046,"name":"CreateV2Disabled"},{"code":6047,"name":"CpitializeMayhemFailed"},{"code":6048,"name":"MayhemModeDisabled"},{"code":6049,"name":"CreatorMigratedToSharingConfig","msg":"creator has been migrated to sharing config, use pump_fees::reset_fee_sharing_config instead"},{"code":6050,"name":"UnableToDistributeCreatorVaultMigratedToSharingConfig","msg":"creator_vault has been migrated to sharing config, use pump:distribute_creator_fees instead"},{"code":6051,"name":"SharingConfigNotActive","msg":"Sharing config is not active"},{"code":6052,"name":"UnableToDistributeCreatorFeesToExecutableRecipient","msg":"The recipient account is executable, so it cannot receive lamports, remove it from the team first"},{"code":6053,"name":"BondingCurveAndSharingConfigCreatorMismatch","msg":"Bonding curve creator does not match sharing config"},{"code":6054,"name":"ShareholdersAndRemainingAccountsMismatch","msg":"Remaining accounts do not match shareholders, make sure to pass exactly the same pubkeys in the same order"},{"code":6055,"name":"InvalidShareBps","msg":"Share bps must be greater than 0"},{"code":6056,"name":"CashbackNotEnabled","msg":"Cashback is not enabled"},{"code":6057,"name":"BuybackFeeRecipientNotAuthorized","msg":"Buyback fee recipient not authorized"},{"code":6058,"name":"AllBuybackFeeRecipientsShouldBeNonZero"},{"code":6059,"name":"NotUniqueBuybackFeeRecipients"},{"code":6060,"name":"BuybackBasisPointsOutOfRange","msg":"buyback_basis_points must be <= 10_000"},{"code":6061,"name":"WrongBuybackFeeRecipientsCount","msg":"buyback fee recipients require exactly 8 remaining accounts (or none)"},{"code":6062,"name":"BuybackFeeRecipientMissing"},{"code":6063,"name":"UnsupportedQuoteMint","msg":"Unsupported quote mint"},{"code":6064,"name":"InvalidQuoteTokenProgram","msg":"Create v2: quote token program must be legacy SPL Token"},{"code":6065,"name":"InvalidAssociatedQuoteBondingCurve","msg":"Create v2: associated quote bonding curve address does not match derivation"},{"code":6066,"name":"QuoteMintWhitelistFull","msg":"Quote mint whitelist is full"},{"code":6067,"name":"QuoteMintAlreadyWhitelisted","msg":"Quote mint is already whitelisted"},{"code":6068,"name":"QuoteMintNotWhitelisted","msg":"Quote mint is not in the whitelist"},{"code":6069,"name":"QuoteMintNotEligibleForWhitelist","msg":"Quote mint cannot be added or removed via whitelist (default or native SOL mint)"},{"code":6070,"name":"UnableToDistributeCreatorFeesToUninitializedAccount","msg":"Unable to distribute creator fees to uninitialized account"},{"code":6071,"name":"MayhemModeQuoteMintNotAllowed","msg":"Mayhem mode quote mint not allowed"}],"types":[{"name":"AdminSetCreatorEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin_set_creator_authority","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"bonding_curve","type":"pubkey"},{"name":"old_creator","type":"pubkey"},{"name":"new_creator","type":"pubkey"}]}},{"name":"AdminSetIdlAuthorityEvent","type":{"kind":"struct","fields":[{"name":"idl_authority","type":"pubkey"}]}},{"name":"AdminUpdateTokenIncentivesEvent","type":{"kind":"struct","fields":[{"name":"start_time","type":"i64"},{"name":"end_time","type":"i64"},{"name":"day_number","type":"u64"},{"name":"token_supply_per_day","type":"u64"},{"name":"mint","type":"pubkey"},{"name":"seconds_in_a_day","type":"i64"},{"name":"timestamp","type":"i64"}]}},{"name":"BondingCurve","type":{"kind":"struct","fields":[{"name":"virtual_token_reserves","type":"u64"},{"name":"virtual_quote_reserves","type":"u64"},{"name":"real_token_reserves","type":"u64"},{"name":"real_quote_reserves","type":"u64"},{"name":"token_total_supply","type":"u64"},{"name":"complete","type":"bool"},{"name":"creator","type":"pubkey"},{"name":"is_mayhem_mode","type":"bool"},{"name":"is_cashback_coin","type":"bool"},{"name":"quote_mint","type":"pubkey"}]}},{"name":"ClaimCashbackEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"amount","type":"u64"},{"name":"timestamp","type":"i64"},{"name":"total_claimed","type":"u64"},{"name":"total_cashback_earned","type":"u64"}]}},{"name":"ClaimTokenIncentivesEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"amount","type":"u64"},{"name":"timestamp","type":"i64"},{"name":"total_claimed_tokens","type":"u64"},{"name":"current_sol_volume","type":"u64"}]}},{"name":"CloseUserVolumeAccumulatorEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"timestamp","type":"i64"},{"name":"total_unclaimed_tokens","type":"u64"},{"name":"total_claimed_tokens","type":"u64"},{"name":"current_sol_volume","type":"u64"},{"name":"last_update_timestamp","type":"i64"}]}},{"name":"CollectCreatorFeeEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"creator","type":"pubkey"},{"name":"creator_fee","type":"u64"},{"name":"quote_mint","type":"pubkey"}]}},{"name":"CompleteEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"bonding_curve","type":"pubkey"},{"name":"timestamp","type":"i64"},{"name":"quote_mint","type":"pubkey"}]}},{"name":"CompletePumpAmmMigrationEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"mint_amount","type":"u64"},{"name":"sol_amount","type":"u64"},{"name":"pool_migration_fee","type":"u64"},{"name":"bonding_curve","type":"pubkey"},{"name":"timestamp","type":"i64"},{"name":"pool","type":"pubkey"},{"name":"quote_mint","type":"pubkey"}]}},{"name":"ConfigStatus","type":{"kind":"enum","variants":[{"name":"Paused"},{"name":"Active"}]}},{"name":"CreateEvent","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"mint","type":"pubkey"},{"name":"bonding_curve","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"creator","type":"pubkey"},{"name":"timestamp","type":"i64"},{"name":"virtual_token_reserves","type":"u64"},{"name":"virtual_sol_reserves","type":"u64"},{"name":"real_token_reserves","type":"u64"},{"name":"token_total_supply","type":"u64"},{"name":"token_program","type":"pubkey"},{"name":"is_mayhem_mode","type":"bool"},{"name":"is_cashback_enabled","type":"bool"},{"name":"quote_mint","type":"pubkey"},{"name":"virtual_quote_reserves","type":"u64"}]}},{"name":"DistributeCreatorFeesEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"bonding_curve","type":"pubkey"},{"name":"sharing_config","type":"pubkey"},{"name":"admin","type":"pubkey"},{"name":"shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}},{"name":"distributed","type":"u64"},{"name":"quote_mint","type":"pubkey"}]}},{"name":"ExtendAccountEvent","type":{"kind":"struct","fields":[{"name":"account","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"current_size","type":"u64"},{"name":"new_size","type":"u64"},{"name":"timestamp","type":"i64"}]}},{"name":"FeeConfig","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"admin","type":"pubkey"},{"name":"flat_fees","type":{"defined":{"name":"Fees"}}},{"name":"fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"stable_fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}}]}},{"name":"FeeTier","type":{"kind":"struct","fields":[{"name":"market_cap_lamports_threshold","type":"u128"},{"name":"fees","type":{"defined":{"name":"Fees"}}}]}},{"name":"Fees","type":{"kind":"struct","fields":[{"name":"lp_fee_bps","type":"u64"},{"name":"protocol_fee_bps","type":"u64"},{"name":"creator_fee_bps","type":"u64"}]}},{"name":"Global","type":{"kind":"struct","fields":[{"name":"initialized","docs":["Unused"],"type":"bool"},{"name":"authority","type":"pubkey"},{"name":"fee_recipient","type":"pubkey"},{"name":"initial_virtual_token_reserves","type":"u64"},{"name":"initial_virtual_sol_reserves","type":"u64"},{"name":"initial_real_token_reserves","type":"u64"},{"name":"token_total_supply","type":"u64"},{"name":"fee_basis_points","type":"u64"},{"name":"withdraw_authority","type":"pubkey"},{"name":"enable_migrate","docs":["Unused"],"type":"bool"},{"name":"pool_migration_fee","type":"u64"},{"name":"creator_fee_basis_points","type":"u64"},{"name":"fee_recipients","type":{"array":["pubkey",7]}},{"name":"set_creator_authority","type":"pubkey"},{"name":"admin_set_creator_authority","type":"pubkey"},{"name":"create_v2_enabled","type":"bool"},{"name":"whitelist_pda","type":"pubkey"},{"name":"reserved_fee_recipient","type":"pubkey"},{"name":"mayhem_mode_enabled","type":"bool"},{"name":"reserved_fee_recipients","type":{"array":["pubkey",7]}},{"name":"is_cashback_enabled","type":"bool"},{"name":"buyback_fee_recipients","type":{"array":["pubkey",8]}},{"name":"buyback_basis_points","type":"u64"},{"name":"initial_virtual_quote_reserves","type":"u64"},{"name":"whitelisted_quote_mints","type":{"array":["pubkey",1]}}]}},{"name":"GlobalVolumeAccumulator","type":{"kind":"struct","fields":[{"name":"start_time","type":"i64"},{"name":"end_time","type":"i64"},{"name":"seconds_in_a_day","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"total_token_supply","type":{"array":["u64",30]}},{"name":"sol_volumes","type":{"array":["u64",30]}}]}},{"name":"InitUserVolumeAccumulatorEvent","type":{"kind":"struct","fields":[{"name":"payer","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"timestamp","type":"i64"}]}},{"name":"MigrateBondingCurveCreatorEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"bonding_curve","type":"pubkey"},{"name":"sharing_config","type":"pubkey"},{"name":"old_creator","type":"pubkey"},{"name":"new_creator","type":"pubkey"}]}},{"name":"MinimumDistributableFeeEvent","type":{"kind":"struct","fields":[{"name":"minimum_required","type":"u64"},{"name":"distributable_fees","type":"u64"},{"name":"can_distribute","type":"bool"}]}},{"name":"OptionBool","type":{"kind":"struct","fields":["bool"]}},{"name":"ReservedFeeRecipientsEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"reserved_fee_recipient","type":"pubkey"},{"name":"reserved_fee_recipients","type":{"array":["pubkey",7]}}]}},{"name":"SetCreatorEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"bonding_curve","type":"pubkey"},{"name":"creator","type":"pubkey"}]}},{"name":"SetMetaplexCreatorEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"bonding_curve","type":"pubkey"},{"name":"metadata","type":"pubkey"},{"name":"creator","type":"pubkey"}]}},{"name":"SetParamsEvent","type":{"kind":"struct","fields":[{"name":"initial_virtual_token_reserves","type":"u64"},{"name":"initial_virtual_sol_reserves","type":"u64"},{"name":"initial_real_token_reserves","type":"u64"},{"name":"final_real_sol_reserves","type":"u64"},{"name":"token_total_supply","type":"u64"},{"name":"fee_basis_points","type":"u64"},{"name":"withdraw_authority","type":"pubkey"},{"name":"enable_migrate","type":"bool"},{"name":"pool_migration_fee","type":"u64"},{"name":"creator_fee_basis_points","type":"u64"},{"name":"fee_recipients","type":{"array":["pubkey",8]}},{"name":"timestamp","type":"i64"},{"name":"set_creator_authority","type":"pubkey"},{"name":"admin_set_creator_authority","type":"pubkey"}]}},{"name":"Shareholder","type":{"kind":"struct","fields":[{"name":"address","type":"pubkey"},{"name":"share_bps","type":"u16"}]}},{"name":"SharingConfig","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"version","type":"u8"},{"name":"status","type":{"defined":{"name":"ConfigStatus"}}},{"name":"mint","type":"pubkey"},{"name":"admin","type":"pubkey"},{"name":"admin_revoked","type":"bool"},{"name":"shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}}]}},{"name":"SyncUserVolumeAccumulatorEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"total_claimed_tokens_before","type":"u64"},{"name":"total_claimed_tokens_after","type":"u64"},{"name":"timestamp","type":"i64"}]}},{"name":"TradeEvent","docs":["ix_name: \"buy\" | \"sell\" | \"buy_exact_sol_in\""],"type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"sol_amount","type":"u64"},{"name":"token_amount","type":"u64"},{"name":"is_buy","type":"bool"},{"name":"user","type":"pubkey"},{"name":"timestamp","type":"i64"},{"name":"virtual_sol_reserves","type":"u64"},{"name":"virtual_token_reserves","type":"u64"},{"name":"real_sol_reserves","type":"u64"},{"name":"real_token_reserves","type":"u64"},{"name":"fee_recipient","type":"pubkey"},{"name":"fee_basis_points","type":"u64"},{"name":"fee","type":"u64"},{"name":"creator","type":"pubkey"},{"name":"creator_fee_basis_points","type":"u64"},{"name":"creator_fee","type":"u64"},{"name":"track_volume","type":"bool"},{"name":"total_unclaimed_tokens","type":"u64"},{"name":"total_claimed_tokens","type":"u64"},{"name":"current_sol_volume","type":"u64"},{"name":"last_update_timestamp","type":"i64"},{"name":"ix_name","type":"string"},{"name":"mayhem_mode","type":"bool"},{"name":"cashback_fee_basis_points","type":"u64"},{"name":"cashback","type":"u64"},{"name":"buyback_fee_basis_points","type":"u64"},{"name":"buyback_fee","type":"u64"},{"name":"shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}},{"name":"quote_mint","type":"pubkey"},{"name":"quote_amount","type":"u64"},{"name":"virtual_quote_reserves","type":"u64"},{"name":"real_quote_reserves","type":"u64"}]}},{"name":"UpdateGlobalAuthorityEvent","type":{"kind":"struct","fields":[{"name":"global","type":"pubkey"},{"name":"authority","type":"pubkey"},{"name":"new_authority","type":"pubkey"},{"name":"timestamp","type":"i64"}]}},{"name":"UpdateMayhemVirtualParamsEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"virtual_token_reserves","type":"u64"},{"name":"virtual_sol_reserves","type":"u64"},{"name":"new_virtual_token_reserves","type":"u64"},{"name":"new_virtual_sol_reserves","type":"u64"},{"name":"real_token_reserves","type":"u64"},{"name":"real_sol_reserves","type":"u64"}]}},{"name":"UserVolumeAccumulator","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"needs_claim","type":"bool"},{"name":"total_unclaimed_tokens","type":"u64"},{"name":"total_claimed_tokens","type":"u64"},{"name":"current_sol_volume","type":"u64"},{"name":"last_update_timestamp","type":"i64"},{"name":"has_total_claimed_tokens","type":"bool"},{"name":"cashback_earned","type":"u64"},{"name":"total_cashback_claimed","type":"u64"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/pump_swap.pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA.json b/migration/khadhroony-bot2-reference/idls/pump_swap.pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA.json new file mode 100644 index 0000000..daf2337 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/pump_swap.pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA.json @@ -0,0 +1 @@ +{"address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA","metadata":{"name":"pump_amm","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"admin_set_coin_creator","docs":["Overrides the coin creator for a canonical pump pool"],"discriminator":[242,40,117,145,73,96,105,104],"accounts":[{"name":"admin_set_coin_creator_authority","signer":true,"relations":["global_config"]},{"name":"global_config"},{"name":"pool","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"coin_creator","type":"pubkey"}]},{"name":"admin_update_token_incentives","discriminator":[209,11,115,87,213,23,124,204],"accounts":[{"name":"admin","writable":true,"signer":true,"relations":["global_config"]},{"name":"global_config","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,99,111,110,102,105,103]}]}},{"name":"global_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"mint"},{"name":"global_incentive_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"global_volume_accumulator"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"start_time","type":"i64"},{"name":"end_time","type":"i64"},{"name":"seconds_in_a_day","type":"i64"},{"name":"day_number","type":"u64"},{"name":"token_supply_per_day","type":"u64"}]},{"name":"buy","docs":["For cashback coins, optionally pass user_volume_accumulator_wsol_ata as remaining_accounts[0].","If provided and valid, the ATA will be initialized if needed."],"discriminator":[102,6,61,18,1,218,235,234],"accounts":[{"name":"pool","writable":true},{"name":"user","writable":true,"signer":true},{"name":"global_config"},{"name":"base_mint","relations":["pool"]},{"name":"quote_mint","relations":["pool"]},{"name":"user_base_token_account","writable":true},{"name":"user_quote_token_account","writable":true},{"name":"pool_base_token_account","writable":true,"relations":["pool"]},{"name":"pool_quote_token_account","writable":true,"relations":["pool"]},{"name":"protocol_fee_recipient"},{"name":"protocol_fee_recipient_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"protocol_fee_recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"base_token_program"},{"name":"quote_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"coin_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"coin_creator_vault_authority"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"coin_creator_vault_authority","pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"pool.coin_creator","account":"Pool"}]}},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}],"program":{"kind":"account","path":"fee_program"}}},{"name":"fee_program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"}],"args":[{"name":"base_amount_out","type":"u64"},{"name":"max_quote_amount_in","type":"u64"},{"name":"track_volume","type":{"defined":{"name":"OptionBool"}}}]},{"name":"buy_exact_quote_in","docs":["Given a budget of spendable_quote_in, buy at least min_base_amount_out","Fees will be deducted from spendable_quote_in","","f(quote) = tokens, where tokens >= min_base_amount_out","","Make sure the payer has enough SOL to cover creation of the following accounts (unless already created):","- protocol_fee_recipient_token_account: rent.minimum_balance(TokenAccount::LEN)","- coin_creator_vault_ata: rent.minimum_balance(TokenAccount::LEN)","- user_volume_accumulator: rent.minimum_balance(UserVolumeAccumulator::LEN)","","For cashback coins, optionally pass user_volume_accumulator_wsol_ata as remaining_accounts[0].","If provided and valid, the ATA will be initialized if needed."],"discriminator":[198,46,21,82,180,217,232,112],"accounts":[{"name":"pool","writable":true},{"name":"user","writable":true,"signer":true},{"name":"global_config"},{"name":"base_mint","relations":["pool"]},{"name":"quote_mint","relations":["pool"]},{"name":"user_base_token_account","writable":true},{"name":"user_quote_token_account","writable":true},{"name":"pool_base_token_account","writable":true,"relations":["pool"]},{"name":"pool_quote_token_account","writable":true,"relations":["pool"]},{"name":"protocol_fee_recipient"},{"name":"protocol_fee_recipient_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"protocol_fee_recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"base_token_program"},{"name":"quote_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"coin_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"coin_creator_vault_authority"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"coin_creator_vault_authority","pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"pool.coin_creator","account":"Pool"}]}},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}],"program":{"kind":"account","path":"fee_program"}}},{"name":"fee_program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"}],"args":[{"name":"spendable_quote_in","type":"u64"},{"name":"min_base_amount_out","type":"u64"},{"name":"track_volume","type":{"defined":{"name":"OptionBool"}}}]},{"name":"claim_cashback","discriminator":[37,58,35,126,190,53,228,197],"accounts":[{"name":"user","writable":true},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"quote_mint"},{"name":"quote_token_program"},{"name":"user_volume_accumulator_wsol_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"user_volume_accumulator"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user_wsol_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"}],"args":[]},{"name":"claim_token_incentives","discriminator":[16,4,71,28,204,1,40,27],"accounts":[{"name":"user"},{"name":"user_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"global_incentive_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"global_volume_accumulator"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"mint","relations":["global_volume_accumulator"]},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"payer","writable":true,"signer":true}],"args":[]},{"name":"close_user_volume_accumulator","discriminator":[249,69,164,218,150,103,84,138],"accounts":[{"name":"user","writable":true,"signer":true},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"collect_coin_creator_fee","discriminator":[160,57,89,42,181,139,43,66],"accounts":[{"name":"quote_mint"},{"name":"quote_token_program"},{"name":"coin_creator"},{"name":"coin_creator_vault_authority","pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"coin_creator"}]}},{"name":"coin_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"coin_creator_vault_authority"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"coin_creator_token_account","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"create_config","discriminator":[201,207,243,114,75,111,47,189],"accounts":[{"name":"admin","writable":true,"signer":true,"address":"8LWu7QM2dGR1G8nKDHthckea57bkCzXyBTAKPJUBDHo8"},{"name":"global_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,99,111,110,102,105,103]}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"lp_fee_basis_points","type":"u64"},{"name":"protocol_fee_basis_points","type":"u64"},{"name":"protocol_fee_recipients","type":{"array":["pubkey",8]}},{"name":"coin_creator_fee_basis_points","type":"u64"},{"name":"admin_set_coin_creator_authority","type":"pubkey"}]},{"name":"create_pool","discriminator":[233,146,209,142,207,104,64,188],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"arg","path":"index"},{"kind":"account","path":"creator"},{"kind":"account","path":"base_mint"},{"kind":"account","path":"quote_mint"}]}},{"name":"global_config"},{"name":"creator","writable":true,"signer":true},{"name":"base_mint"},{"name":"quote_mint"},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,108,112,95,109,105,110,116]},{"kind":"account","path":"pool"}]}},{"name":"user_base_token_account","writable":true},{"name":"user_quote_token_account","writable":true},{"name":"user_pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"creator"},{"kind":"account","path":"token_2022_program"},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool_base_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"base_token_program"},{"kind":"account","path":"base_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool_quote_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"base_token_program"},{"name":"quote_token_program"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"index","type":"u16"},{"name":"base_amount_in","type":"u64"},{"name":"quote_amount_in","type":"u64"},{"name":"coin_creator","type":"pubkey"},{"name":"is_mayhem_mode","type":"bool"},{"name":"is_cashback_coin","type":{"defined":{"name":"OptionBool"}}}]},{"name":"deposit","discriminator":[242,35,198,137,82,225,242,182],"accounts":[{"name":"pool","writable":true},{"name":"global_config"},{"name":"user","signer":true},{"name":"base_mint","relations":["pool"]},{"name":"quote_mint","relations":["pool"]},{"name":"lp_mint","writable":true,"relations":["pool"]},{"name":"user_base_token_account","writable":true},{"name":"user_quote_token_account","writable":true},{"name":"user_pool_token_account","writable":true},{"name":"pool_base_token_account","writable":true,"relations":["pool"]},{"name":"pool_quote_token_account","writable":true,"relations":["pool"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"lp_token_amount_out","type":"u64"},{"name":"max_base_amount_in","type":"u64"},{"name":"max_quote_amount_in","type":"u64"}]},{"name":"disable","discriminator":[185,173,187,90,216,15,238,233],"accounts":[{"name":"admin","signer":true,"relations":["global_config"]},{"name":"global_config","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"disable_create_pool","type":"bool"},{"name":"disable_deposit","type":"bool"},{"name":"disable_withdraw","type":"bool"},{"name":"disable_buy","type":"bool"},{"name":"disable_sell","type":"bool"}]},{"name":"extend_account","discriminator":[234,102,194,203,150,72,62,229],"accounts":[{"name":"account","writable":true},{"name":"user","signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"init_user_volume_accumulator","discriminator":[94,6,202,115,255,96,232,183],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"user"},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"migrate_pool_coin_creator","docs":["Migrate Pool Coin Creator to Sharing Config"],"discriminator":[208,8,159,4,74,175,16,58],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"pool.index","account":"Pool"},{"kind":"account","path":"pool.creator","account":"Pool"},{"kind":"account","path":"pool.base_mint","account":"Pool"},{"kind":"account","path":"pool.quote_mint","account":"Pool"}]}},{"name":"sharing_config","pda":{"seeds":[{"kind":"const","value":[115,104,97,114,105,110,103,45,99,111,110,102,105,103]},{"kind":"account","path":"pool.base_mint","account":"Pool"}],"program":{"kind":"const","value":[12,53,255,169,5,90,142,86,141,168,247,188,7,86,21,39,76,241,201,44,164,31,64,0,156,81,106,164,20,194,124,112]}}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"sell","discriminator":[51,230,133,164,1,127,131,173],"accounts":[{"name":"pool","writable":true},{"name":"user","writable":true,"signer":true},{"name":"global_config"},{"name":"base_mint","relations":["pool"]},{"name":"quote_mint","relations":["pool"]},{"name":"user_base_token_account","writable":true},{"name":"user_quote_token_account","writable":true},{"name":"pool_base_token_account","writable":true,"relations":["pool"]},{"name":"pool_quote_token_account","writable":true,"relations":["pool"]},{"name":"protocol_fee_recipient"},{"name":"protocol_fee_recipient_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"protocol_fee_recipient"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"base_token_program"},{"name":"quote_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program","address":"pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"},{"name":"coin_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"coin_creator_vault_authority"},{"kind":"account","path":"quote_token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"coin_creator_vault_authority","pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"pool.coin_creator","account":"Pool"}]}},{"name":"fee_config","pda":{"seeds":[{"kind":"const","value":[102,101,101,95,99,111,110,102,105,103]},{"kind":"const","value":[12,20,222,252,130,94,198,118,148,37,8,24,187,101,64,101,244,41,141,49,86,213,113,180,212,248,9,12,24,233,168,99]}],"program":{"kind":"account","path":"fee_program"}}},{"name":"fee_program","address":"pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"}],"args":[{"name":"base_amount_in","type":"u64"},{"name":"min_quote_amount_out","type":"u64"}]},{"name":"set_coin_creator","docs":["Sets Pool::coin_creator from Metaplex metadata creator or BondingCurve::creator"],"discriminator":[210,149,128,45,188,58,78,175],"accounts":[{"name":"pool","writable":true},{"name":"metadata","pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]},{"kind":"account","path":"pool.base_mint","account":"Pool"}],"program":{"kind":"const","value":[11,112,101,177,227,209,124,69,56,157,82,127,107,4,195,205,88,184,108,115,26,160,253,181,73,182,209,188,3,248,41,70]}}},{"name":"bonding_curve","pda":{"seeds":[{"kind":"const","value":[98,111,110,100,105,110,103,45,99,117,114,118,101]},{"kind":"account","path":"pool.base_mint","account":"Pool"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"set_reserved_fee_recipients","discriminator":[111,172,162,232,114,89,213,142],"accounts":[{"name":"global_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,99,111,110,102,105,103]}]}},{"name":"admin","signer":true,"relations":["global_config"]},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"whitelist_pda","type":"pubkey"}]},{"name":"sync_user_volume_accumulator","discriminator":[86,31,192,87,163,87,79,238],"accounts":[{"name":"user"},{"name":"global_volume_accumulator","pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]}]}},{"name":"user_volume_accumulator","writable":true,"pda":{"seeds":[{"kind":"const","value":[117,115,101,114,95,118,111,108,117,109,101,95,97,99,99,117,109,117,108,97,116,111,114]},{"kind":"account","path":"user"}]}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"toggle_cashback_enabled","discriminator":[115,103,224,255,189,89,86,195],"accounts":[{"name":"admin","signer":true,"relations":["global_config"]},{"name":"global_config","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"enabled","type":"bool"}]},{"name":"toggle_mayhem_mode","discriminator":[1,9,111,208,100,31,255,163],"accounts":[{"name":"admin","signer":true,"relations":["global_config"]},{"name":"global_config","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"enabled","type":"bool"}]},{"name":"transfer_creator_fees_to_pump","docs":["Transfer creator fees to pump creator vault","If coin creator fees are currently below rent.minimum_balance(TokenAccount::LEN)","The transfer will be skipped"],"discriminator":[139,52,134,85,228,229,108,241],"accounts":[{"name":"wsol_mint","docs":["Pump Canonical Pool are quoted in wSOL"]},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"coin_creator"},{"name":"coin_creator_vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"coin_creator"}]}},{"name":"coin_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"coin_creator_vault_authority"},{"kind":"account","path":"token_program"},{"kind":"account","path":"wsol_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pump_creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"coin_creator"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"transfer_creator_fees_to_pump_v2","discriminator":[1,33,78,185,33,67,44,92],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"quote_mint"},{"name":"token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"coin_creator"},{"name":"coin_creator_vault_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,118,97,117,108,116]},{"kind":"account","path":"coin_creator"}]}},{"name":"coin_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"coin_creator_vault_authority"},{"kind":"account","path":"token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pump_creator_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,45,118,97,117,108,116]},{"kind":"account","path":"coin_creator"}],"program":{"kind":"const","value":[1,86,224,246,147,102,90,207,68,219,21,104,191,23,91,170,81,137,203,151,245,210,255,59,101,93,43,182,253,109,24,176]}}},{"name":"pump_creator_vault_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"pump_creator_vault"},{"kind":"account","path":"token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"associated_token_program"}}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"update_admin","discriminator":[161,176,40,213,60,184,179,228],"accounts":[{"name":"admin","signer":true,"relations":["global_config"]},{"name":"global_config","writable":true},{"name":"new_admin"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"update_buyback_config","discriminator":[251,224,171,146,160,26,113,233],"accounts":[{"name":"admin","signer":true,"relations":["global_config"]},{"name":"global_config","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"buyback_basis_points","type":{"option":"u64"}}]},{"name":"update_fee_config","discriminator":[104,184,103,242,88,151,107,20],"accounts":[{"name":"admin","signer":true,"relations":["global_config"]},{"name":"global_config","writable":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"lp_fee_basis_points","type":"u64"},{"name":"protocol_fee_basis_points","type":"u64"},{"name":"protocol_fee_recipients","type":{"array":["pubkey",8]}},{"name":"coin_creator_fee_basis_points","type":"u64"},{"name":"admin_set_coin_creator_authority","type":"pubkey"}]},{"name":"withdraw","discriminator":[183,18,70,156,148,109,161,34],"accounts":[{"name":"pool","writable":true},{"name":"global_config"},{"name":"user","signer":true},{"name":"base_mint","relations":["pool"]},{"name":"quote_mint","relations":["pool"]},{"name":"lp_mint","writable":true,"relations":["pool"]},{"name":"user_base_token_account","writable":true},{"name":"user_quote_token_account","writable":true},{"name":"user_pool_token_account","writable":true},{"name":"pool_base_token_account","writable":true,"relations":["pool"]},{"name":"pool_quote_token_account","writable":true,"relations":["pool"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_2022_program","address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"lp_token_amount_in","type":"u64"},{"name":"min_base_amount_out","type":"u64"},{"name":"min_quote_amount_out","type":"u64"}]}],"accounts":[{"name":"BondingCurve","discriminator":[23,183,248,55,96,216,172,96]},{"name":"FeeConfig","discriminator":[143,52,146,187,219,123,76,155]},{"name":"GlobalConfig","discriminator":[149,8,156,202,160,252,176,217]},{"name":"GlobalVolumeAccumulator","discriminator":[202,42,246,43,142,190,30,255]},{"name":"Pool","discriminator":[241,154,109,4,17,177,109,188]},{"name":"SharingConfig","discriminator":[216,74,9,0,56,140,93,75]},{"name":"UserVolumeAccumulator","discriminator":[86,255,112,14,102,53,154,250]}],"events":[{"name":"AdminSetCoinCreatorEvent","discriminator":[45,220,93,24,25,97,172,104]},{"name":"AdminUpdateTokenIncentivesEvent","discriminator":[147,250,108,120,247,29,67,222]},{"name":"BuyEvent","discriminator":[103,244,82,31,44,245,119,119]},{"name":"ClaimCashbackEvent","discriminator":[226,214,246,33,7,242,147,229]},{"name":"ClaimTokenIncentivesEvent","discriminator":[79,172,246,49,205,91,206,232]},{"name":"CloseUserVolumeAccumulatorEvent","discriminator":[146,159,189,172,146,88,56,244]},{"name":"CollectCoinCreatorFeeEvent","discriminator":[232,245,194,238,234,218,58,89]},{"name":"CreateConfigEvent","discriminator":[107,52,89,129,55,226,81,22]},{"name":"CreatePoolEvent","discriminator":[177,49,12,210,160,118,167,116]},{"name":"DepositEvent","discriminator":[120,248,61,83,31,142,107,144]},{"name":"DisableEvent","discriminator":[107,253,193,76,228,202,27,104]},{"name":"ExtendAccountEvent","discriminator":[97,97,215,144,93,146,22,124]},{"name":"InitUserVolumeAccumulatorEvent","discriminator":[134,36,13,72,232,101,130,216]},{"name":"MigratePoolCoinCreatorEvent","discriminator":[170,221,82,199,147,165,247,46]},{"name":"ReservedFeeRecipientsEvent","discriminator":[43,188,250,18,221,75,187,95]},{"name":"SellEvent","discriminator":[62,47,55,10,165,3,220,42]},{"name":"SetBondingCurveCoinCreatorEvent","discriminator":[242,231,235,102,65,99,189,211]},{"name":"SetMetaplexCoinCreatorEvent","discriminator":[150,107,199,123,124,207,102,228]},{"name":"SyncUserVolumeAccumulatorEvent","discriminator":[197,122,167,124,116,81,91,255]},{"name":"UpdateAdminEvent","discriminator":[225,152,171,87,246,63,66,234]},{"name":"UpdateFeeConfigEvent","discriminator":[90,23,65,35,62,244,188,208]},{"name":"WithdrawEvent","discriminator":[22,9,133,26,160,44,71,192]}],"errors":[{"code":6000,"name":"FeeBasisPointsExceedsMaximum"},{"code":6001,"name":"ZeroBaseAmount"},{"code":6002,"name":"ZeroQuoteAmount"},{"code":6003,"name":"TooLittlePoolTokenLiquidity"},{"code":6004,"name":"ExceededSlippage"},{"code":6005,"name":"InvalidAdmin"},{"code":6006,"name":"UnsupportedBaseMint"},{"code":6007,"name":"UnsupportedQuoteMint"},{"code":6008,"name":"InvalidBaseMint"},{"code":6009,"name":"InvalidQuoteMint"},{"code":6010,"name":"InvalidLpMint"},{"code":6011,"name":"AllProtocolFeeRecipientsShouldBeNonZero"},{"code":6012,"name":"UnsortedNotUniqueProtocolFeeRecipients"},{"code":6013,"name":"InvalidProtocolFeeRecipient"},{"code":6014,"name":"InvalidPoolBaseTokenAccount"},{"code":6015,"name":"InvalidPoolQuoteTokenAccount"},{"code":6016,"name":"BuyMoreBaseAmountThanPoolReserves"},{"code":6017,"name":"DisabledCreatePool"},{"code":6018,"name":"DisabledDeposit"},{"code":6019,"name":"DisabledWithdraw"},{"code":6020,"name":"DisabledBuy"},{"code":6021,"name":"DisabledSell"},{"code":6022,"name":"SameMint"},{"code":6023,"name":"Overflow"},{"code":6024,"name":"Truncation"},{"code":6025,"name":"DivisionByZero"},{"code":6026,"name":"NewSizeLessThanCurrentSize"},{"code":6027,"name":"AccountTypeNotSupported"},{"code":6028,"name":"OnlyCanonicalPumpPoolsCanHaveCoinCreator"},{"code":6029,"name":"InvalidAdminSetCoinCreatorAuthority"},{"code":6030,"name":"StartTimeInThePast"},{"code":6031,"name":"EndTimeInThePast"},{"code":6032,"name":"EndTimeBeforeStartTime"},{"code":6033,"name":"TimeRangeTooLarge"},{"code":6034,"name":"EndTimeBeforeCurrentDay"},{"code":6035,"name":"SupplyUpdateForFinishedRange"},{"code":6036,"name":"DayIndexAfterEndIndex"},{"code":6037,"name":"DayInActiveRange"},{"code":6038,"name":"InvalidIncentiveMint"},{"code":6039,"name":"BuyNotEnoughQuoteTokensToCoverFees","msg":"buy: Not enough quote tokens to cover for fees."},{"code":6040,"name":"BuySlippageBelowMinBaseAmountOut","msg":"buy: slippage - would buy less tokens than expected min_base_amount_out"},{"code":6041,"name":"MayhemModeDisabled"},{"code":6042,"name":"OnlyPumpPoolsMayhemMode"},{"code":6043,"name":"MayhemModeInDesiredState"},{"code":6044,"name":"NotEnoughRemainingAccounts"},{"code":6045,"name":"InvalidSharingConfigBaseMint"},{"code":6046,"name":"InvalidSharingConfigCoinCreator"},{"code":6047,"name":"CoinCreatorMigratedToSharingConfig","msg":"coin creator has been migrated to sharing config, use pump_fees::reset_fee_sharing_config instead"},{"code":6048,"name":"CreatorVaultMigratedToSharingConfig","msg":"creator_vault has been migrated to sharing config, use pump:distribute_creator_fees instead"},{"code":6049,"name":"CashbackNotEnabled","msg":"Cashback is disabled"},{"code":6050,"name":"OnlyPumpPoolsCashback"},{"code":6051,"name":"CashbackNotInDesiredState"},{"code":6052,"name":"TokensInVaultLessThanCashbackEarned"},{"code":6053,"name":"BuybackFeeRecipientNotAuthorized","msg":"Buyback fee recipient not authorized"},{"code":6054,"name":"AllBuybackFeeRecipientsShouldBeNonZero"},{"code":6055,"name":"NotUniqueBuybackFeeRecipients"},{"code":6056,"name":"BuybackBasisPointsOutOfRange","msg":"buyback_basis_points must be <= 10_000"},{"code":6057,"name":"WrongBuybackFeeRecipientsCount","msg":"buyback fee recipients require exactly 8 remaining accounts (or none)"},{"code":6058,"name":"BuybackFeeRecipientMissing"}],"types":[{"name":"AdminSetCoinCreatorEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin_set_coin_creator_authority","type":"pubkey"},{"name":"base_mint","type":"pubkey"},{"name":"pool","type":"pubkey"},{"name":"old_coin_creator","type":"pubkey"},{"name":"new_coin_creator","type":"pubkey"}]}},{"name":"AdminUpdateTokenIncentivesEvent","type":{"kind":"struct","fields":[{"name":"start_time","type":"i64"},{"name":"end_time","type":"i64"},{"name":"day_number","type":"u64"},{"name":"token_supply_per_day","type":"u64"},{"name":"mint","type":"pubkey"},{"name":"seconds_in_a_day","type":"i64"},{"name":"timestamp","type":"i64"}]}},{"name":"BondingCurve","type":{"kind":"struct","fields":[{"name":"virtual_token_reserves","type":"u64"},{"name":"virtual_sol_reserves","type":"u64"},{"name":"real_token_reserves","type":"u64"},{"name":"real_sol_reserves","type":"u64"},{"name":"token_total_supply","type":"u64"},{"name":"complete","type":"bool"},{"name":"creator","type":"pubkey"},{"name":"is_mayhem_mode","type":"bool"},{"name":"is_cashback_coin","type":"bool"}]}},{"name":"BuyEvent","docs":["ix_name: \"buy\" | \"buy_exact_quote_in\""],"type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"base_amount_out","type":"u64"},{"name":"max_quote_amount_in","type":"u64"},{"name":"user_base_token_reserves","type":"u64"},{"name":"user_quote_token_reserves","type":"u64"},{"name":"pool_base_token_reserves","type":"u64"},{"name":"pool_quote_token_reserves","type":"u64"},{"name":"quote_amount_in","type":"u64"},{"name":"lp_fee_basis_points","type":"u64"},{"name":"lp_fee","type":"u64"},{"name":"protocol_fee_basis_points","type":"u64"},{"name":"protocol_fee","type":"u64"},{"name":"quote_amount_in_with_lp_fee","type":"u64"},{"name":"user_quote_amount_in","type":"u64"},{"name":"pool","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"user_base_token_account","type":"pubkey"},{"name":"user_quote_token_account","type":"pubkey"},{"name":"protocol_fee_recipient","type":"pubkey"},{"name":"protocol_fee_recipient_token_account","type":"pubkey"},{"name":"coin_creator","type":"pubkey"},{"name":"coin_creator_fee_basis_points","type":"u64"},{"name":"coin_creator_fee","type":"u64"},{"name":"track_volume","type":"bool"},{"name":"total_unclaimed_tokens","type":"u64"},{"name":"total_claimed_tokens","type":"u64"},{"name":"current_sol_volume","type":"u64"},{"name":"last_update_timestamp","type":"i64"},{"name":"min_base_amount_out","type":"u64"},{"name":"ix_name","type":"string"},{"name":"cashback_fee_basis_points","type":"u64"},{"name":"cashback","type":"u64"},{"name":"buyback_fee_basis_points","type":"u64"},{"name":"buyback_fee","type":"u64"}]}},{"name":"ClaimCashbackEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"amount","type":"u64"},{"name":"timestamp","type":"i64"},{"name":"total_claimed","type":"u64"},{"name":"total_cashback_earned","type":"u64"}]}},{"name":"ClaimTokenIncentivesEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"amount","type":"u64"},{"name":"timestamp","type":"i64"},{"name":"total_claimed_tokens","type":"u64"},{"name":"current_sol_volume","type":"u64"}]}},{"name":"CloseUserVolumeAccumulatorEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"timestamp","type":"i64"},{"name":"total_unclaimed_tokens","type":"u64"},{"name":"total_claimed_tokens","type":"u64"},{"name":"current_sol_volume","type":"u64"},{"name":"last_update_timestamp","type":"i64"}]}},{"name":"CollectCoinCreatorFeeEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"coin_creator","type":"pubkey"},{"name":"coin_creator_fee","type":"u64"},{"name":"coin_creator_vault_ata","type":"pubkey"},{"name":"coin_creator_token_account","type":"pubkey"}]}},{"name":"ConfigStatus","type":{"kind":"enum","variants":[{"name":"Paused"},{"name":"Active"}]}},{"name":"CreateConfigEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin","type":"pubkey"},{"name":"lp_fee_basis_points","type":"u64"},{"name":"protocol_fee_basis_points","type":"u64"},{"name":"protocol_fee_recipients","type":{"array":["pubkey",8]}},{"name":"coin_creator_fee_basis_points","type":"u64"},{"name":"admin_set_coin_creator_authority","type":"pubkey"}]}},{"name":"CreatePoolEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"index","type":"u16"},{"name":"creator","type":"pubkey"},{"name":"base_mint","type":"pubkey"},{"name":"quote_mint","type":"pubkey"},{"name":"base_mint_decimals","type":"u8"},{"name":"quote_mint_decimals","type":"u8"},{"name":"base_amount_in","type":"u64"},{"name":"quote_amount_in","type":"u64"},{"name":"pool_base_amount","type":"u64"},{"name":"pool_quote_amount","type":"u64"},{"name":"minimum_liquidity","type":"u64"},{"name":"initial_liquidity","type":"u64"},{"name":"lp_token_amount_out","type":"u64"},{"name":"pool_bump","type":"u8"},{"name":"pool","type":"pubkey"},{"name":"lp_mint","type":"pubkey"},{"name":"user_base_token_account","type":"pubkey"},{"name":"user_quote_token_account","type":"pubkey"},{"name":"coin_creator","type":"pubkey"},{"name":"is_mayhem_mode","type":"bool"}]}},{"name":"DepositEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"lp_token_amount_out","type":"u64"},{"name":"max_base_amount_in","type":"u64"},{"name":"max_quote_amount_in","type":"u64"},{"name":"user_base_token_reserves","type":"u64"},{"name":"user_quote_token_reserves","type":"u64"},{"name":"pool_base_token_reserves","type":"u64"},{"name":"pool_quote_token_reserves","type":"u64"},{"name":"base_amount_in","type":"u64"},{"name":"quote_amount_in","type":"u64"},{"name":"lp_mint_supply","type":"u64"},{"name":"pool","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"user_base_token_account","type":"pubkey"},{"name":"user_quote_token_account","type":"pubkey"},{"name":"user_pool_token_account","type":"pubkey"}]}},{"name":"DisableEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin","type":"pubkey"},{"name":"disable_create_pool","type":"bool"},{"name":"disable_deposit","type":"bool"},{"name":"disable_withdraw","type":"bool"},{"name":"disable_buy","type":"bool"},{"name":"disable_sell","type":"bool"}]}},{"name":"ExtendAccountEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"account","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"current_size","type":"u64"},{"name":"new_size","type":"u64"}]}},{"name":"FeeConfig","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"admin","type":"pubkey"},{"name":"flat_fees","type":{"defined":{"name":"Fees"}}},{"name":"fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}},{"name":"stable_fee_tiers","type":{"vec":{"defined":{"name":"FeeTier"}}}}]}},{"name":"FeeTier","type":{"kind":"struct","fields":[{"name":"market_cap_lamports_threshold","type":"u128"},{"name":"fees","type":{"defined":{"name":"Fees"}}}]}},{"name":"Fees","type":{"kind":"struct","fields":[{"name":"lp_fee_bps","type":"u64"},{"name":"protocol_fee_bps","type":"u64"},{"name":"creator_fee_bps","type":"u64"}]}},{"name":"GlobalConfig","type":{"kind":"struct","fields":[{"name":"admin","docs":["The admin pubkey"],"type":"pubkey"},{"name":"lp_fee_basis_points","type":"u64"},{"name":"protocol_fee_basis_points","type":"u64"},{"name":"disable_flags","docs":["Flags to disable certain functionality","bit 0 - Disable create pool","bit 1 - Disable deposit","bit 2 - Disable withdraw","bit 3 - Disable buy","bit 4 - Disable sell"],"type":"u8"},{"name":"protocol_fee_recipients","docs":["Addresses of the protocol fee recipients"],"type":{"array":["pubkey",8]}},{"name":"coin_creator_fee_basis_points","type":"u64"},{"name":"admin_set_coin_creator_authority","docs":["The admin authority for setting coin creators"],"type":"pubkey"},{"name":"whitelist_pda","type":"pubkey"},{"name":"reserved_fee_recipient","type":"pubkey"},{"name":"mayhem_mode_enabled","type":"bool"},{"name":"reserved_fee_recipients","type":{"array":["pubkey",7]}},{"name":"is_cashback_enabled","type":"bool"},{"name":"buyback_fee_recipients","type":{"array":["pubkey",8]}},{"name":"buyback_basis_points","type":"u64"}]}},{"name":"GlobalVolumeAccumulator","type":{"kind":"struct","fields":[{"name":"start_time","type":"i64"},{"name":"end_time","type":"i64"},{"name":"seconds_in_a_day","type":"i64"},{"name":"mint","type":"pubkey"},{"name":"total_token_supply","type":{"array":["u64",30]}},{"name":"sol_volumes","type":{"array":["u64",30]}}]}},{"name":"InitUserVolumeAccumulatorEvent","type":{"kind":"struct","fields":[{"name":"payer","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"timestamp","type":"i64"}]}},{"name":"MigratePoolCoinCreatorEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"base_mint","type":"pubkey"},{"name":"pool","type":"pubkey"},{"name":"sharing_config","type":"pubkey"},{"name":"old_coin_creator","type":"pubkey"},{"name":"new_coin_creator","type":"pubkey"}]}},{"name":"OptionBool","type":{"kind":"struct","fields":["bool"]}},{"name":"Pool","type":{"kind":"struct","fields":[{"name":"pool_bump","type":"u8"},{"name":"index","type":"u16"},{"name":"creator","type":"pubkey"},{"name":"base_mint","type":"pubkey"},{"name":"quote_mint","type":"pubkey"},{"name":"lp_mint","type":"pubkey"},{"name":"pool_base_token_account","type":"pubkey"},{"name":"pool_quote_token_account","type":"pubkey"},{"name":"lp_supply","docs":["True circulating supply without burns and lock-ups"],"type":"u64"},{"name":"coin_creator","type":"pubkey"},{"name":"is_mayhem_mode","type":"bool"},{"name":"is_cashback_coin","type":"bool"}]}},{"name":"ReservedFeeRecipientsEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"reserved_fee_recipient","type":"pubkey"},{"name":"reserved_fee_recipients","type":{"array":["pubkey",7]}}]}},{"name":"SellEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"base_amount_in","type":"u64"},{"name":"min_quote_amount_out","type":"u64"},{"name":"user_base_token_reserves","type":"u64"},{"name":"user_quote_token_reserves","type":"u64"},{"name":"pool_base_token_reserves","type":"u64"},{"name":"pool_quote_token_reserves","type":"u64"},{"name":"quote_amount_out","type":"u64"},{"name":"lp_fee_basis_points","type":"u64"},{"name":"lp_fee","type":"u64"},{"name":"protocol_fee_basis_points","type":"u64"},{"name":"protocol_fee","type":"u64"},{"name":"quote_amount_out_without_lp_fee","type":"u64"},{"name":"user_quote_amount_out","type":"u64"},{"name":"pool","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"user_base_token_account","type":"pubkey"},{"name":"user_quote_token_account","type":"pubkey"},{"name":"protocol_fee_recipient","type":"pubkey"},{"name":"protocol_fee_recipient_token_account","type":"pubkey"},{"name":"coin_creator","type":"pubkey"},{"name":"coin_creator_fee_basis_points","type":"u64"},{"name":"coin_creator_fee","type":"u64"},{"name":"cashback_fee_basis_points","type":"u64"},{"name":"cashback","type":"u64"},{"name":"buyback_fee_basis_points","type":"u64"},{"name":"buyback_fee","type":"u64"}]}},{"name":"SetBondingCurveCoinCreatorEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"base_mint","type":"pubkey"},{"name":"pool","type":"pubkey"},{"name":"bonding_curve","type":"pubkey"},{"name":"coin_creator","type":"pubkey"}]}},{"name":"SetMetaplexCoinCreatorEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"base_mint","type":"pubkey"},{"name":"pool","type":"pubkey"},{"name":"metadata","type":"pubkey"},{"name":"coin_creator","type":"pubkey"}]}},{"name":"Shareholder","type":{"kind":"struct","fields":[{"name":"address","type":"pubkey"},{"name":"share_bps","type":"u16"}]}},{"name":"SharingConfig","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"version","type":"u8"},{"name":"status","type":{"defined":{"name":"ConfigStatus"}}},{"name":"mint","type":"pubkey"},{"name":"admin","type":"pubkey"},{"name":"admin_revoked","type":"bool"},{"name":"shareholders","type":{"vec":{"defined":{"name":"Shareholder"}}}}]}},{"name":"SyncUserVolumeAccumulatorEvent","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"total_claimed_tokens_before","type":"u64"},{"name":"total_claimed_tokens_after","type":"u64"},{"name":"timestamp","type":"i64"}]}},{"name":"UpdateAdminEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin","type":"pubkey"},{"name":"new_admin","type":"pubkey"}]}},{"name":"UpdateFeeConfigEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"admin","type":"pubkey"},{"name":"lp_fee_basis_points","type":"u64"},{"name":"protocol_fee_basis_points","type":"u64"},{"name":"protocol_fee_recipients","type":{"array":["pubkey",8]}},{"name":"coin_creator_fee_basis_points","type":"u64"},{"name":"admin_set_coin_creator_authority","type":"pubkey"}]}},{"name":"UserVolumeAccumulator","type":{"kind":"struct","fields":[{"name":"user","type":"pubkey"},{"name":"needs_claim","type":"bool"},{"name":"total_unclaimed_tokens","type":"u64"},{"name":"total_claimed_tokens","type":"u64"},{"name":"current_sol_volume","type":"u64"},{"name":"last_update_timestamp","type":"i64"},{"name":"has_total_claimed_tokens","type":"bool"},{"name":"cashback_earned","type":"u64"},{"name":"total_cashback_claimed","type":"u64"}]}},{"name":"WithdrawEvent","type":{"kind":"struct","fields":[{"name":"timestamp","type":"i64"},{"name":"lp_token_amount_in","type":"u64"},{"name":"min_base_amount_out","type":"u64"},{"name":"min_quote_amount_out","type":"u64"},{"name":"user_base_token_reserves","type":"u64"},{"name":"user_quote_token_reserves","type":"u64"},{"name":"pool_base_token_reserves","type":"u64"},{"name":"pool_quote_token_reserves","type":"u64"},{"name":"base_amount_out","type":"u64"},{"name":"quote_amount_out","type":"u64"},{"name":"lp_mint_supply","type":"u64"},{"name":"pool","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"user_base_token_account","type":"pubkey"},{"name":"user_quote_token_account","type":"pubkey"},{"name":"user_pool_token_account","type":"pubkey"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/pumpup.PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd.json b/migration/khadhroony-bot2-reference/idls/pumpup.PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd.json new file mode 100644 index 0000000..3230b1d --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/pumpup.PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd.json @@ -0,0 +1 @@ +{"address":"PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd","metadata":{"name":"pumpup","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"add_liquidity","discriminator":[181,157,89,67,143,182,52,72],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,112,111,111,108]},{"kind":"account","path":"pool.token_a_mint","account":"Pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"token_a_vault","docs":["CHECK"],"writable":true},{"name":"token_b_vault","docs":["CHECK"],"writable":true},{"name":"lp_mint","docs":["CHECK"],"writable":true},{"name":"user_token_a","writable":true},{"name":"user_token_b","writable":true},{"name":"user_lp_token","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"token_a_amount","type":"u64"},{"name":"token_b_amount","type":"u64"},{"name":"minimum_lp_tokens","type":"u64"}]},{"name":"buy","discriminator":[102,6,61,18,1,218,235,234],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,99,111,110,102,105,103]}]}},{"name":"mint"},{"name":"pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_sol_account"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool_sol_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,112,111,111,108]},{"kind":"account","path":"mint"}]}},{"name":"ai_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"config"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pumpup_fee","writable":true},{"name":"user_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"token_amount","type":"u64"},{"name":"max_sol_amount","type":"u64"}]},{"name":"create","discriminator":[24,30,200,40,5,28,7,119],"accounts":[{"name":"creator","writable":true,"signer":true},{"name":"mint","writable":true,"signer":true},{"name":"mint_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"pool_sol_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,112,111,111,108]},{"kind":"account","path":"mint"}]}},{"name":"pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_sol_account"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","writable":true},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"create_leverage","discriminator":[71,62,173,225,174,65,205,227],"accounts":[{"name":"creator","writable":true,"signer":true},{"name":"mint","writable":true,"signer":true},{"name":"mint_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"pool_sol_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,112,111,111,108]},{"kind":"account","path":"mint"}]}},{"name":"pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_sol_account"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","writable":true},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"feed","discriminator":[46,213,237,176,190,113,182,94],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,99,111,110,102,105,103]}]}},{"name":"mint"},{"name":"original","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,111,114,105,103,105,110,97,108,46,100,97,116,97]},{"kind":"account","path":"mint"}]}},{"name":"migration_address","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"datas","type":{"array":["f64",33]}}]},{"name":"inference","discriminator":[1,249,84,211,187,82,82,51],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,99,111,110,102,105,103]}]}},{"name":"mint"},{"name":"original","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,111,114,105,103,105,110,97,108,46,100,97,116,97]},{"kind":"account","path":"mint"}]}},{"name":"pool_sol_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,112,111,111,108]},{"kind":"account","path":"mint"}]}},{"name":"migration_address","writable":true,"signer":true},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"period","type":"u64"}]},{"name":"init_ai_token","discriminator":[195,26,226,86,175,143,32,58],"accounts":[{"name":"creator","writable":true,"signer":true},{"name":"config","pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,99,111,110,102,105,103]}]}},{"name":"mint"},{"name":"ai_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"config"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"initialize","discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"config","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,99,111,110,102,105,103]}]}},{"name":"fee_address","docs":["CHECK"],"writable":true},{"name":"migration_address","docs":["CHECK"],"writable":true},{"name":"mint_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"authority_address","writable":true,"signer":true},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"fee_rate","type":"u64"}]},{"name":"initialize_divide","discriminator":[82,21,35,146,161,99,102,28],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,112,111,111,108]},{"kind":"account","path":"pool.token_a_mint","account":"Pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"token_b_mint"},{"name":"fee_address"},{"name":"fee_recipient","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_address"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_b_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"payer","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"fee_rate","type":"u16"}]},{"name":"initialize_lp_token","discriminator":[124,21,221,232,250,37,120,35],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,112,111,111,108]},{"kind":"account","path":"pool.token_a_mint","account":"Pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"lp_mint","docs":["LP代币Mint账户 - 用于铸造流动性提供者代币"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,108,112,95,109,105,110,116]},{"kind":"account","path":"pool"}]}},{"name":"payer","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[]},{"name":"initialize_pool","discriminator":[95,180,10,172,84,174,232,40],"accounts":[{"name":"pool","docs":["新的流动性池账户"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,112,111,111,108]},{"kind":"account","path":"token_a_mint"},{"kind":"account","path":"token_b_mint"}]}},{"name":"token_a_mint","docs":["代币A的Mint账户"]},{"name":"token_b_mint","docs":["代币B的Mint账户(USDT)"]},{"name":"config","pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,99,111,110,102,105,103]}]}},{"name":"fee_address"},{"name":"fee_recipient","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_address"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_b_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"payer","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[]},{"name":"initialize_tokena","discriminator":[209,97,45,4,27,32,11,205],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,112,111,111,108]},{"kind":"account","path":"pool.token_a_mint","account":"Pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"token_a_mint","docs":["代币Mint账户"]},{"name":"token_a_vault","docs":["代币的保险库账户 - 存储池中的对应代币A"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,118,97,117,108,116]},{"kind":"account","path":"pool"},{"kind":"account","path":"token_a_mint"}]}},{"name":"payer","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[]},{"name":"initialize_tokenb","discriminator":[218,101,38,193,111,170,244,254],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,112,111,111,108]},{"kind":"account","path":"pool.token_a_mint","account":"Pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"token_b_mint","docs":["代币B的Mint账户(USDT)"]},{"name":"token_b_vault","docs":["代币的保险库账户 - 存储池中的对应代币A"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,118,97,117,108,116]},{"kind":"account","path":"pool"},{"kind":"account","path":"token_b_mint"}]}},{"name":"payer","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[]},{"name":"migrate_pool","discriminator":[55,170,171,123,210,69,39,172],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,112,111,111,108]},{"kind":"account","path":"pool.token_a_mint","account":"Pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"token_b_mint"},{"name":"fee_address2"},{"name":"fee_recipient2","writable":true,"pda":{"seeds":[{"kind":"account","path":"fee_address2"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_b_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"payer","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[]},{"name":"quick_create","discriminator":[127,6,145,127,36,205,4,225],"accounts":[{"name":"creator","writable":true,"signer":true},{"name":"mint","writable":true,"signer":true},{"name":"mint_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"pool_sol_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,112,111,111,108]},{"kind":"account","path":"mint"}]}},{"name":"pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_sol_account"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","writable":true},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"quick_create_leverage","discriminator":[24,2,81,246,97,77,131,127],"accounts":[{"name":"creator","writable":true,"signer":true},{"name":"mint","writable":true,"signer":true},{"name":"mint_authority","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,109,105,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"pool_sol_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,112,111,111,108]},{"kind":"account","path":"mint"}]}},{"name":"pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_sol_account"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","writable":true},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]},{"name":"remove_liquidity","docs":["移除流动性"],"discriminator":[80,85,209,72,24,206,177,108],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,112,111,111,108]},{"kind":"account","path":"pool.token_a_mint","account":"Pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"token_a_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,118,97,117,108,116]},{"kind":"account","path":"pool"},{"kind":"account","path":"pool.token_a_mint","account":"Pool"}]}},{"name":"token_b_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,118,97,117,108,116]},{"kind":"account","path":"pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"lp_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,108,112,95,109,105,110,116]},{"kind":"account","path":"pool"}]}},{"name":"user_token_a","writable":true},{"name":"user_token_b","writable":true},{"name":"user_lp_token","writable":true},{"name":"user","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"lp_token_amount","type":"u64"},{"name":"minimum_token_a","type":"u64"},{"name":"minimum_token_b","type":"u64"}]},{"name":"sell","discriminator":[51,230,133,164,1,127,131,173],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,99,111,110,102,105,103]}]}},{"name":"mint"},{"name":"pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_sol_account"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool_sol_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,112,111,111,108]},{"kind":"account","path":"mint"}]}},{"name":"ai_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"config"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pumpup_fee","writable":true},{"name":"user_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"user"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"user","writable":true,"signer":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"token_amount","type":"u64"},{"name":"min_sol_amount","type":"u64"}]},{"name":"swap","discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,112,111,111,108]},{"kind":"account","path":"pool.token_a_mint","account":"Pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"token_a_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,118,97,117,108,116]},{"kind":"account","path":"pool"},{"kind":"account","path":"pool.token_a_mint","account":"Pool"}]}},{"name":"token_b_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,114,119,97,46,118,97,117,108,116]},{"kind":"account","path":"pool"},{"kind":"account","path":"pool.token_b_mint","account":"Pool"}]}},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"fee_recipient_token","writable":true},{"name":"fee_recipient_token2","writable":true},{"name":"user","writable":true,"signer":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"}]},{"name":"withdraw","discriminator":[183,18,70,156,148,109,161,34],"accounts":[{"name":"config","pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,99,111,110,102,105,103]}]}},{"name":"mint","writable":true},{"name":"pool_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool_sol_account"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool_sol_account","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,117,109,112,117,112,46,112,111,111,108]},{"kind":"account","path":"mint"}]}},{"name":"ai_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"config"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pumpup_fee","writable":true},{"name":"to_sol_account","writable":true,"signer":true},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"launch_account","writable":true},{"name":"launch_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"launch_account"},{"kind":"account","path":"token_program"},{"kind":"account","path":"mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"pumpup_migration_amount","type":"u64"}]}],"accounts":[{"name":"AIOriginalDataAccount","discriminator":[125,158,65,141,197,244,39,160]},{"name":"BondingCurve","discriminator":[23,183,248,55,96,216,172,96]},{"name":"Pool","discriminator":[241,154,109,4,17,177,109,188]},{"name":"PumpupConfiguration","discriminator":[36,175,125,65,27,3,84,217]}],"events":[{"name":"CreateMintEvent","discriminator":[239,193,191,44,156,194,85,141]},{"name":"FeedEvent","discriminator":[250,174,105,83,244,245,23,11]},{"name":"MintLeverageEvent","discriminator":[64,149,175,150,7,170,9,69]},{"name":"RWAInitEvent","discriminator":[171,102,39,251,156,173,88,8]},{"name":"RWATradeEvent","discriminator":[163,38,91,101,120,148,151,90]},{"name":"TradeEvent","discriminator":[189,219,127,211,78,230,97,238]}],"errors":[{"code":6000,"name":"InvalidAmount","msg":"Invalid amount provided"},{"code":6001,"name":"InvalidFeeRecipient","msg":"Invalid fee recipient"},{"code":6002,"name":"InsufficientLiquidity","msg":"Insufficient liquidity in the pool"},{"code":6003,"name":"SlippageExceeded","msg":"Slippage tolerance exceeded"},{"code":6004,"name":"InvalidTokenMint","msg":"Invalid token mint"},{"code":6005,"name":"DuplicateTokens","msg":"Duplicate tokens"},{"code":6006,"name":"InvalidTokenAVault","msg":"Invalid token_a_vault account"},{"code":6007,"name":"InvalidTokenBVault","msg":"Invalid token_b_vault account"},{"code":6008,"name":"InvalidLpMint","msg":"Invalid lp_mint account"},{"code":6009,"name":"PoolPaused","msg":"Pool is paused"},{"code":6010,"name":"Unauthorized","msg":"Unauthorized access"},{"code":6011,"name":"InvalidFeeRate","msg":"Invalid fee rate"},{"code":6012,"name":"PoolAlreadyExists","msg":"Pool already exists"},{"code":6013,"name":"MathOverflow","msg":"Mathematical overflow"},{"code":6014,"name":"InvalidLpTokenAmount","msg":"Invalid LP token amount"},{"code":6015,"name":"MinimumLiquidityNotMet","msg":"Minimum liquidity not met"},{"code":6016,"name":"TokenBMustBeUsdt","msg":"Token B must be USDT"},{"code":6017,"name":"InvalidPayer","msg":"Invalid payer"}],"types":[{"name":"AIOriginalDataAccount","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"period","type":"u64"},{"name":"period_done","type":"bool"},{"name":"regulated","type":"bool"},{"name":"sol","type":"f64"},{"name":"datas","type":{"array":["f64",42]}}]}},{"name":"BondingCurve","type":{"kind":"struct","fields":[{"name":"launch_token_surplus","type":"u64"},{"name":"virtual_sol","type":"u64"},{"name":"real_sol","type":"u64"},{"name":"pool_sol_reserves","type":"u64"},{"name":"pool_token_reserves","type":"u64"},{"name":"current_leverage_index","type":"u8"},{"name":"leverage","type":{"vec":{"array":["u64",3]}}}]}},{"name":"CreateMintEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"},{"name":"pool_sol_address","type":"pubkey"},{"name":"pool_token_address","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"timestamp","type":"i64"},{"name":"launch_token_surplus","type":"u64"},{"name":"pool_sol_reserves","type":"u64"},{"name":"pool_token_reserves","type":"u64"},{"name":"is_leverage","type":"bool"}]}},{"name":"FeedEvent","type":{"kind":"struct","fields":[{"name":"period","type":"u64"}]}},{"name":"InitProgress","type":{"kind":"enum","variants":[{"name":"START"},{"name":"COMPLETED"}]}},{"name":"MintLeverageEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"period","type":"u64"},{"name":"regulated","type":"bool"},{"name":"timestamp","type":"i64"},{"name":"launch_token_surplus","type":"u64"},{"name":"pool_sol_reserves","type":"u64"},{"name":"pool_token_reserves","type":"u64"},{"name":"leverage","type":{"vec":{"array":["u64",3]}}}]}},{"name":"Pool","type":{"kind":"struct","fields":[{"name":"token_a_mint","docs":["代币A的mint地址"],"type":"pubkey"},{"name":"token_b_mint","docs":["代币B的mint地址(USDT)"],"type":"pubkey"},{"name":"token_a_vault","docs":["代币A金库账户"],"type":"pubkey"},{"name":"token_b_vault","docs":["代币B金库账户"],"type":"pubkey"},{"name":"lp_mint","docs":["LP代币mint"],"type":"pubkey"},{"name":"fee_recipient","docs":["手续费接收地址"],"type":"pubkey"},{"name":"token_a_reserve","docs":["代币A储备量"],"type":"u64"},{"name":"token_b_reserve","docs":["代币B储备量"],"type":"u64"},{"name":"lp_token_supply","docs":["LP代币总供应量"],"type":"u64"},{"name":"fee_rate","docs":["手续费率(基点,100=1%)"],"type":"u16"},{"name":"bump","docs":["PDA bump"],"type":"u8"},{"name":"fee_recipient2","type":"pubkey"},{"name":"fee_rate2","docs":["分润比(基点,100=1%)"],"type":"u16"}]}},{"name":"PumpupConfiguration","serialization":"bytemuckunsafe","repr":{"kind":"rust","packed":true},"type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"fee_rate","type":"u64"},{"name":"authority_address","type":"pubkey"},{"name":"fee_address","type":"pubkey"},{"name":"migration_address","type":"pubkey"}]}},{"name":"RWAInitEvent","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"init_progress","type":{"defined":{"name":"InitProgress"}}},{"name":"token_a_mint","type":"pubkey"},{"name":"token_b_mint","type":"pubkey"},{"name":"token_a_vault","type":"pubkey"},{"name":"token_b_vault","type":"pubkey"},{"name":"timestamp","type":"i64"},{"name":"lp_mint","type":"pubkey"},{"name":"fee_recipient","type":"pubkey"},{"name":"token_a_reserve","type":"u64"},{"name":"token_b_reserve","type":"u64"},{"name":"lp_token_supply","type":"u64"},{"name":"fee_rate","type":"u16"}]}},{"name":"RWATradeEvent","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"event_type","type":{"defined":{"name":"TradeEventType"}}},{"name":"token_a_mint","type":"pubkey"},{"name":"token_a_amount","type":"u64"},{"name":"token_b_mint","type":"pubkey"},{"name":"token_b_amount","type":"u64"},{"name":"fee_amount","type":"u64"},{"name":"user","type":"pubkey"},{"name":"pool_tokena_reserves","type":"u64"},{"name":"pool_tokenb_reserves","type":"u64"},{"name":"timestamp","type":"i64"}]}},{"name":"TradeEvent","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"sol_amount","type":"u64"},{"name":"token_amount","type":"u64"},{"name":"ai_token_amount","type":"u64"},{"name":"event_type","type":{"defined":{"name":"TradeEventType"}}},{"name":"user","type":"pubkey"},{"name":"timestamp","type":"i64"},{"name":"pumpup_fee","type":"u64"},{"name":"pool_real_sol_amount","type":"u64"},{"name":"pool_sol_reserves","type":"u64"},{"name":"pool_token_reserves","type":"u64"}]}},{"name":"TradeEventType","type":{"kind":"enum","variants":[{"name":"BUY"},{"name":"SELL"},{"name":"WITHDRAW"},{"name":"RwaBuy"},{"name":"RwaSell"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/raydium_clmm.CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK.json b/migration/khadhroony-bot2-reference/idls/raydium_clmm.CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK.json new file mode 100644 index 0000000..de07bf6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/raydium_clmm.CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK.json @@ -0,0 +1 @@ +{"address":"CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK","metadata":{"name":"raydium_clmm","version":"0.1.0","spec":"0.1.0","description":"Anchor client and source for Raydium concentrated liquidity AMM"},"instructions":[{"name":"close_limit_order","docs":["Close a limit order","Closes the limit order account when unfilled amount is zero","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[76,124,128,15,213,87,37,250],"accounts":[{"name":"signer","signer":true},{"name":"rent_receiver","writable":true},{"name":"limit_order","writable":true}],"args":[]},{"name":"close_position","docs":["Close the user's position and NFT account. If the NFT mint belongs to token2022, it will also be closed and the funds returned to the NFT owner.","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[123,134,81,0,49,68,98,98],"accounts":[{"name":"nft_owner","docs":["The position nft owner"],"writable":true,"signer":true},{"name":"position_nft_mint","docs":["Mint address bound to the personal position."],"writable":true},{"name":"position_nft_account","docs":["User token account where position NFT be minted to"],"writable":true},{"name":"personal_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"system_program","docs":["System program to close the position state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Token/Token2022 program to close token/mint account"]}],"args":[]},{"name":"close_protocol_position","docs":["The CLMM protocol decides to discard the protocol position account, which can reduce users' opening costs.","After the original protocol position account is closed, the gas fee will be refunded to the user who created it.","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[201,117,152,144,85,85,108,178],"accounts":[{"name":"admin","writable":true,"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"protocol_position","writable":true}],"args":[]},{"name":"collect_fund_fee","docs":["Collect the fund fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[167,138,78,149,223,194,6,126],"accounts":[{"name":"owner","docs":["Only admin or fund_owner can collect fee now"],"signer":true},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores fund_owner"]},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_protocol_fee","docs":["Collect the protocol fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[136,136,252,221,194,66,126,89],"accounts":[{"name":"owner","docs":["Only admin or config owner can collect fee now"],"signer":true},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores owner"]},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_remaining_rewards","docs":["Collect remaining reward token for reward founder","","# Arguments","","* `ctx` - The context of accounts","* `reward_index` - the index to reward info",""],"discriminator":[18,237,166,197,34,16,213,144],"accounts":[{"name":"reward_funder","docs":["The founder who init reward info previously"],"signer":true},{"name":"funder_token_account","docs":["The funder's reward token account"],"writable":true},{"name":"pool_state","docs":["Set reward for this pool"],"writable":true},{"name":"reward_token_vault","docs":["Reward vault transfer remaining token to founder token account"],"writable":true},{"name":"reward_vault_mint","docs":["The mint of reward token vault"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"create_amm_config","docs":["# Arguments","","* `ctx` - The accounts needed by instruction.","* `index` - The index of amm config, there may be multiple config.","* `tick_spacing` - The tickspacing binding with config, cannot be changed.","* `trade_fee_rate` - Trade fee rate, can be changed.","* `protocol_fee_rate` - The rate of protocol fee within trade fee.","* `fund_fee_rate` - The rate of fund fee within trade fee.","* `limit_fee_rate` - The rate of limit order fee.",""],"discriminator":[137,52,237,212,215,117,108,104],"accounts":[{"name":"owner","docs":["Address to be set as protocol owner."],"writable":true,"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"amm_config","docs":["Initialize config state account to store protocol owner address and fee rates."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,109,109,95,99,111,110,102,105,103]},{"kind":"arg","path":"index"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"u16"},{"name":"tick_spacing","type":"u16"},{"name":"trade_fee_rate","type":"u32"},{"name":"protocol_fee_rate","type":"u32"},{"name":"fund_fee_rate","type":"u32"}]},{"name":"create_customizable_pool","docs":["Creates a pool for the given token pair with customizable parameters","","# Arguments","","* `ctx` - The context of accounts","* `customizable_params` - the customizable parameters",""],"discriminator":[43,68,212,167,89,47,164,1],"accounts":[{"name":"pool_creator","docs":["Address paying to create the pool"],"writable":true,"signer":true},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"pool_state","docs":["Initialize an account to store the pool state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"token_mint_0"},{"kind":"account","path":"token_mint_1"}]}},{"name":"token_mint_0","docs":["Token_0 mint, the key must be smaller then token_1 mint."]},{"name":"token_mint_1","docs":["Token_1 mint"]},{"name":"token_vault_0","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_0"}]}},{"name":"token_vault_1","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_1"}]}},{"name":"observation_state","docs":["Initialize an account to store oracle observations"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"tick_array_bitmap","docs":["Initialize an account to store if a tick array is initialized."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,116,105,99,107,95,97,114,114,97,121,95,98,105,116,109,97,112,95,101,120,116,101,110,115,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"token_program_0","docs":["Spl token program or token program 2022"]},{"name":"token_program_1","docs":["Spl token program or token program 2022"]},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"customizable_params","type":{"defined":{"name":"CreateCustomizableParams"}}}]},{"name":"create_dynamic_fee_config","docs":["Create dynamic fee config","","# Arguments","","* `ctx` - The context of accounts","* `index` - The index of dynamic fee config","* `filter_period` - The period of filter","* `decay_period` - The period of decay","* `reduction_factor` - The reduction factor","* `adaptive_fee_control_factor` - The adaptive fee control factor","* `max_volatility_accumulator` - The max volatility accumulator","* `tick_group_size` - The tick group size"],"discriminator":[189,14,181,120,85,118,227,62],"accounts":[{"name":"owner","writable":true,"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"dynamic_fee_config","writable":true,"pda":{"seeds":[{"kind":"const","value":[100,121,110,97,109,105,99,95,102,101,101,95,99,111,110,102,105,103]},{"kind":"arg","path":"index"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"u16"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"dynamic_fee_control","type":"u32"},{"name":"max_volatility_accumulator","type":"u32"}]},{"name":"create_operation_account","docs":["Creates an operation account for the program","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[63,87,148,33,109,35,8,104],"accounts":[{"name":"owner","docs":["Address to be set as operation account owner."],"writable":true,"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"operation_state","docs":["Initialize operation state account to store operation owner address and white list mint."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_pool","docs":["Creates a pool for the given token pair and the initial price","","# Arguments","","* `ctx` - The context of accounts","* `sqrt_price_x64` - the initial sqrt price (amount_token_1 / amount_token_0) of the pool as a Q64.64","Note: The open_time must be smaller than the current block_timestamp on chain."],"discriminator":[233,146,209,142,207,104,64,188],"accounts":[{"name":"pool_creator","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"pool_state","docs":["Initialize an account to store the pool state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"token_mint_0"},{"kind":"account","path":"token_mint_1"}]}},{"name":"token_mint_0","docs":["Token_0 mint, the key must be smaller then token_1 mint."]},{"name":"token_mint_1","docs":["Token_1 mint"]},{"name":"token_vault_0","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_0"}]}},{"name":"token_vault_1","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_1"}]}},{"name":"observation_state","docs":["Initialize an account to store oracle observations"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"tick_array_bitmap","docs":["Initialize an account to store if a tick array is initialized."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,116,105,99,107,95,97,114,114,97,121,95,98,105,116,109,97,112,95,101,120,116,101,110,115,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"token_program_0","docs":["Spl token program or token program 2022"]},{"name":"token_program_1","docs":["Spl token program or token program 2022"]},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"sqrt_price_x64","type":"u128"},{"name":"open_time","type":"u64"}]},{"name":"create_support_mint_associated","docs":["Create support token22 mint account which can create pool and send rewards while ignoring unsupported extensions."],"discriminator":[17,251,65,92,136,242,14,169],"accounts":[{"name":"owner","docs":["Address to be set as protocol owner."],"writable":true,"signer":true},{"name":"token_mint","docs":["Support token mint"]},{"name":"support_mint_associated","docs":["Initialize support mint state account to store support mint address and bump."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,117,112,112,111,114,116,95,109,105,110,116]},{"kind":"account","path":"token_mint"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"decrease_limit_order","docs":["Decrease/Cancel a limit order","First settle the filled portion of the order; only the remaining unfilled amount can be decreased.","Even if the specified amount is very large, the decrease will not exceed the remaining unfilled order amount.","It is recommended to call the `settle_limit_order` instruction before invoking this instruction to settle any filled portions of the order.","Make sure to check your order's remaining unfilled amount.","# Arguments","","* `ctx` - The context of accounts","* `amount` - The decrease amount of the order,","* `amount_min` - The minimum amount of the order, which serves as a slippage check",""],"discriminator":[117,157,60,103,66,49,163,0],"accounts":[{"name":"owner","signer":true},{"name":"pool_state","writable":true},{"name":"tick_array","writable":true},{"name":"limit_order","writable":true},{"name":"input_token_account","docs":["The payer's limit order token account"],"writable":true},{"name":"output_token_account","docs":["The owner's output token account (opposite direction from input)"],"writable":true},{"name":"input_vault","docs":["The address that holds limit order token"],"writable":true},{"name":"output_vault","docs":["The address that holds output tokens"],"writable":true},{"name":"input_vault_mint","docs":["The mint of token vault"]},{"name":"output_vault_mint","docs":["The mint of output vault"]},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["SPL program 2022 for token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount","type":"u64"},{"name":"amount_min","type":"u64"}]},{"name":"decrease_liquidity","docs":["#[deprecated(note = \"Use `decrease_liquidity_v2` instead.\")]","Decreases liquidity for an existing position","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The amount by which liquidity will be decreased","* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity","* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity",""],"discriminator":[160,38,208,111,104,91,44,1],"accounts":[{"name":"nft_owner","docs":["The position owner or delegated authority"],"signer":true},{"name":"nft_account","docs":["The token account for the tokenized position"]},{"name":"personal_position","docs":["Decrease liquidity for this position"],"writable":true},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"token_vault_0","docs":["Token_0 vault"],"writable":true},{"name":"token_vault_1","docs":["Token_1 vault"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"recipient_token_account_0","docs":["The destination token account for receive amount_0"],"writable":true},{"name":"recipient_token_account_1","docs":["The destination token account for receive amount_1"],"writable":true},{"name":"token_program","docs":["SPL program to transfer out tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_min","type":"u64"},{"name":"amount_1_min","type":"u64"}]},{"name":"decrease_liquidity_v2","docs":["Decreases liquidity for an existing position, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The amount by which liquidity will be decreased","* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity","* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity",""],"discriminator":[58,127,188,62,79,82,196,96],"accounts":[{"name":"nft_owner","docs":["The position owner or delegated authority"],"signer":true},{"name":"nft_account","docs":["The token account for the tokenized position"]},{"name":"personal_position","docs":["Decrease liquidity for this position"],"writable":true},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"token_vault_0","docs":["Token_0 vault"],"writable":true},{"name":"token_vault_1","docs":["Token_1 vault"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"recipient_token_account_0","docs":["The destination token account for receive amount_0"],"writable":true},{"name":"recipient_token_account_1","docs":["The destination token account for receive amount_1"],"writable":true},{"name":"token_program","docs":["SPL program to transfer out tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_min","type":"u64"},{"name":"amount_1_min","type":"u64"}]},{"name":"increase_limit_order","docs":["Increase a limit order","","# Arguments","","* `ctx` - The context of accounts","* `amount` - The increase amount of the order","* `is_increase` - The direction of the order, true: increase, false: decrease",""],"discriminator":[177,144,89,236,250,186,125,99],"accounts":[{"name":"owner","signer":true},{"name":"pool_state"},{"name":"tick_array","writable":true},{"name":"limit_order","writable":true},{"name":"input_token_account","docs":["The payer's limit order token account"],"writable":true},{"name":"input_vault","docs":["The address that holds limit order token"],"writable":true},{"name":"input_vault_mint","docs":["The mint of token vault"]},{"name":"input_token_program","docs":["SPL-TOKEN / SPL-TOKEN2022 program for token transfers"]}],"args":[{"name":"amount","type":"u64"}]},{"name":"increase_liquidity","docs":["#[deprecated(note = \"Use `increase_liquidity_v2` instead.\")]","Increases liquidity for an existing position, with amount paid by `payer`","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The desired liquidity to be added, can't be zero","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check",""],"discriminator":[46,156,243,118,13,205,251,178],"accounts":[{"name":"nft_owner","docs":["Pays to mint the position"],"signer":true},{"name":"nft_account","docs":["The token account for nft"]},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"personal_position","docs":["Increase liquidity for this position"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"token_account_0","docs":["The payer's token account for token_0"],"writable":true},{"name":"token_account_1","docs":["The token account spending token_1 to mint the position"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"}]},{"name":"increase_liquidity_v2","docs":["Increases liquidity for an existing position, with amount paid by `payer`, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The desired liquidity to be added, if zero, calculate liquidity base amount_0 or amount_1 according base_flag","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `base_flag` - must be specified if liquidity is zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[133,29,89,223,69,238,176,10],"accounts":[{"name":"nft_owner","docs":["Pays to mint the position"],"signer":true},{"name":"nft_account","docs":["The token account for nft"]},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"personal_position","docs":["Increase liquidity for this position"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"token_account_0","docs":["The payer's token account for token_0"],"writable":true},{"name":"token_account_1","docs":["The token account spending token_1 to mint the position"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"initialize_reward","docs":["Initialize a reward info for a given pool and reward index","","# Arguments","","* `ctx` - The context of accounts","* `reward_index` - the index to reward info","* `open_time` - reward open timestamp","* `end_time` - reward end timestamp","* `emissions_per_second_x64` - Token reward per second are earned per unit of liquidity.",""],"discriminator":[95,135,192,196,242,129,230,68],"accounts":[{"name":"reward_funder","docs":["The founder deposit reward token to vault"],"writable":true,"signer":true},{"name":"funder_token_account","writable":true},{"name":"amm_config","docs":["For check the reward_funder authority"]},{"name":"pool_state","docs":["Set reward for this pool"],"writable":true},{"name":"operation_state","docs":["load info from the account to judge reward permission"],"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"reward_token_mint","docs":["Reward mint"]},{"name":"reward_token_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,114,101,119,97,114,100,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"reward_token_mint"}]}},{"name":"reward_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"param","type":{"defined":{"name":"InitializeRewardParam"}}}]},{"name":"open_limit_order","docs":["Place a limit order","# Arguments","","* `ctx` - The context of accounts","* `nonce_index` - The limit order nonce account index, used to create user's order nonce PDA account.","* `zero_for_one` - The direction of the order, true: zero for one, false: one for zero","* `tick_index` - The index of the tick","* `amount` - The amount of the order","* `with_metadata` - The flag indicating whether to create NFT mint metadata",""],"discriminator":[157,32,218,183,71,29,18,147],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"pool_state","docs":["The pool"],"writable":true},{"name":"tick_array","writable":true},{"name":"limit_order_nonce","docs":["The order nonce account PDA"],"writable":true},{"name":"limit_order","docs":["The order account PDA"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"payer"},{"kind":"account","path":"limit_order_nonce"},{"kind":"account","path":"limit_order_nonce.order_nonce","account":"LimitOrderNonce"}]}},{"name":"input_token_account","docs":["The payer's limit order token account"],"writable":true},{"name":"input_vault","docs":["The address that holds limit order token"],"writable":true},{"name":"input_vault_mint","docs":["The mint of token vault"]},{"name":"input_token_program","docs":["SPL-TOKEN / SPL-TOKEN2022 program for input token transfers"]},{"name":"system_program","docs":["SPL program for system operations"],"address":"11111111111111111111111111111111"}],"args":[{"name":"nonce_index","type":"u8"},{"name":"zero_for_one","type":"bool"},{"name":"tick_index","type":"i32"},{"name":"amount","type":"u64"}]},{"name":"open_position","docs":["#[deprecated(note = \"Use `open_position_with_token22_nft` instead.\")]","Creates a new position wrapped in a NFT","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which includes tick low","* `tick_array_upper_start_index` - The start index of tick array which includes tick upper","* `liquidity` - The liquidity to be added","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check",""],"discriminator":[135,128,47,77,15,152,240,49],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["Token account where position NFT will be minted","This account created in the contract by cpi to avoid large stack variables"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"position_nft_owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_nft_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","docs":["To store metaplex metadata"],"writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position"},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_program","docs":["Program to create NFT metadata"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"}]},{"name":"open_position_v2","docs":["#[deprecated(note = \"Use `open_position_with_token22_nft` instead.\")]","Creates a new position wrapped in a NFT, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which includes tick low","* `tick_array_upper_start_index` - The start index of tick array which includes tick upper","* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `with_metadata` - The flag indicating whether to create NFT mint metadata","* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[77,184,74,214,112,86,241,199],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address"],"writable":true,"signer":true},{"name":"position_nft_account","docs":["Token account where position NFT will be minted"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"position_nft_owner"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"position_nft_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"metadata_account","docs":["To store metaplex metadata"],"writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position"},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"metadata_program","docs":["Program to create NFT metadata"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"token_program_2022","docs":["Program to create mint account and mint tokens"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"with_metadata","type":"bool"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"open_position_with_token22_nft","docs":["Creates a new position wrapped in a Token2022 NFT without relying on metadata_program and metadata_account, reduce the cost for user to create a personal position.","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which includes tick low","* `tick_array_upper_start_index` - The start index of tick array which includes tick upper","* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `with_metadata` - The flag indicating whether to create NFT mint metadata","* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[77,255,174,82,125,29,201,46],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address, initialize in contract"],"writable":true,"signer":true},{"name":"position_nft_account","writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position"},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to transfer for token account"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program_2022","docs":["Program to create NFT mint/token account and transfer for token22 account"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"with_metadata","type":"bool"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"set_reward_params","docs":["Reset reward param, start a new reward cycle or extend the current cycle.","","# Arguments","","* `ctx` - The context of accounts","* `reward_index` - The index of reward token in the pool.","* `emissions_per_second_x64` - The per second emission reward, when extend the current cycle,","new value can't be less than old value","* `open_time` - reward open timestamp, must be set when starting a new cycle","* `end_time` - reward end timestamp",""],"discriminator":[112,52,167,75,32,201,211,137],"accounts":[{"name":"authority","docs":["Address to be set as protocol owner. It pays to create factory state account."],"signer":true},{"name":"amm_config"},{"name":"pool_state","writable":true},{"name":"operation_state","docs":["load info from the account to judge reward permission"],"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"token_program","docs":["Token program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"reward_index","type":"u8"},{"name":"emissions_per_second_x64","type":"u128"},{"name":"open_time","type":"u64"},{"name":"end_time","type":"u64"}]},{"name":"settle_limit_order","docs":["Settle a limit order","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[205,78,116,33,92,105,26,96],"accounts":[{"name":"signer","signer":true},{"name":"pool_state"},{"name":"tick_array"},{"name":"limit_order","writable":true},{"name":"output_token_account","docs":["The owner's output token account (opposite direction from input)"],"writable":true},{"name":"output_vault","docs":["The address that holds output tokens"],"writable":true},{"name":"output_vault_mint","docs":["The mint of output vault"]},{"name":"output_token_program","docs":["SPL-TOKEN / SPL-TOKEN2022 program for token transfers"]}],"args":[]},{"name":"swap","docs":["#[deprecated(note = \"Use `swap_v2` instead.\")]","Swaps one token for as much as possible of another token across a single pool","","# Arguments","","* `ctx` - The context of accounts","* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)","* `other_amount_threshold` - For slippage check","* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot","* `is_base_input` - swap base input or swap base output",""],"discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"tick_array","writable":true}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit_x64","type":"u128"},{"name":"is_base_input","type":"bool"}]},{"name":"swap_router_base_in","docs":["Swap token for as much as possible of another token across the path provided, base input","","# Arguments","","* `ctx` - The context of accounts","* `amount_in` - Token amount to be swapped in","* `amount_out_minimum` - Panic if output amount is below minimum amount. For slippage.",""],"discriminator":[69,125,115,218,245,186,242,196],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"input_token_account","docs":["The token account that pays input tokens for the swap"],"writable":true},{"name":"input_token_mint","docs":["The mint of input token"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["SPL program 2022 for token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["Memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"amount_in","type":"u64"},{"name":"amount_out_minimum","type":"u64"}]},{"name":"swap_v2","docs":["Swaps one token for as much as possible of another token across a single pool, support token program 2022","","# Arguments","","* `ctx` - The context of accounts","* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)","* `other_amount_threshold` - For slippage check","* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot","* `is_base_input` - swap base input or swap base output",""],"discriminator":[43,4,237,11,26,201,30,98],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["SPL program 2022 for token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["Memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"input_vault_mint","docs":["The mint of token vault 0"]},{"name":"output_vault_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit_x64","type":"u128"},{"name":"is_base_input","type":"bool"}]},{"name":"transfer_reward_owner","docs":["Transfer reward owner","","# Arguments","","* `ctx` - The context of accounts","* `new_owner` - new owner pubkey",""],"discriminator":[7,22,12,83,242,43,48,121],"accounts":[{"name":"authority","docs":["Address to be set as operation account owner."],"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"pool_state","writable":true}],"args":[{"name":"new_owner","type":"pubkey"}]},{"name":"update_amm_config","docs":["Updates the owner of the amm config","Must be called by the current owner or admin","","# Arguments","","* `ctx` - The context of accounts","* `trade_fee_rate` - The new trade fee rate of amm config, be set when `param` is 0","* `protocol_fee_rate` - The new protocol fee rate of amm config, be set when `param` is 1","* `fund_fee_rate` - The new fund fee rate of amm config, be set when `param` is 2","* `new_owner` - The config's new owner, be set when `param` is 3","* `new_fund_owner` - The config's new fund owner, be set when `param` is 4","* `limit_protocol_fee_rate` - The new limit protocol fee rate of amm config, be set when `param` is 5","* `limit_fund_fee_rate` - The new limit fund fee rate of amm config, be set when `param` is 6","* `param` - The value can be 0 | 1 | 2 | 3 | 4 | 5 | 6, otherwise will report an error",""],"discriminator":[49,60,174,136,154,28,116,200],"accounts":[{"name":"owner","docs":["The amm config owner or admin"],"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"amm_config","docs":["Amm config account to be changed"],"writable":true}],"args":[{"name":"param","type":"u8"},{"name":"value","type":"u32"}]},{"name":"update_dynamic_fee_config","docs":["Updates the dynamic fee configuration constants.","","This function allows updating the parameters that control dynamic fee calculation,","such as filter period, decay period, reduction factor, fee control factor, etc.","Only the admin or the amm config owner can update these settings.","","# Arguments","","* `ctx` - The context of accounts","* `filter_period` - Period that determines the high frequency trading time window (in seconds)","* `decay_period` - Period that determines when the dynamic fee starts to decrease (in seconds)","* `reduction_factor` - Dynamic fee rate decrement rate, used for volatility reference decay","* `dynamic_fee_control` - Factor used to scale the dynamic fee component","* `max_volatility_accumulator` - Maximum value for the volatility accumulator","* `create_pool_authority` - The new authority pubkey that can use this dynamic fee config to create pools"],"discriminator":[7,7,80,8,2,199,132,240],"accounts":[{"name":"owner","docs":["The admin has permission to update the config"],"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"dynamic_fee_config","docs":["The dynamic fee config account to be updated"],"writable":true}],"args":[{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"dynamic_fee_control","type":"u32"},{"name":"max_volatility_accumulator","type":"u32"}]},{"name":"update_operation_account","docs":["Update the operation account","","# Arguments","","* `ctx` - The context of accounts","* `param` - The value can be 0 | 1 | 2 | 3, otherwise will report an error","* `keys`- update operation owner when the `param` is 0","remove operation owner when the `param` is 1","update whitelist mint when the `param` is 2","remove whitelist mint when the `param` is 3",""],"discriminator":[127,70,119,40,188,227,61,7],"accounts":[{"name":"owner","docs":["Address to be set as operation account owner."],"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"operation_state","docs":["Initialize operation state account to store operation owner address and white list mint."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"param","type":"u8"},{"name":"keys","type":{"vec":"pubkey"}}]},{"name":"update_pool_status","docs":["Update pool status for given value","","# Arguments","","* `ctx` - The context of accounts","* `status` - The value of status",""],"discriminator":[130,87,108,6,46,224,117,123],"accounts":[{"name":"authority","signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"pool_state","writable":true}],"args":[{"name":"status","type":"u8"}]},{"name":"update_reward_infos","docs":["Update rewards info of the given pool, can be called for everyone","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[163,172,224,52,11,154,106,223],"accounts":[{"name":"pool_state","docs":["The liquidity pool for which reward info to update"],"writable":true}],"args":[]}],"accounts":[{"name":"AmmConfig","discriminator":[218,244,33,104,203,203,43,111]},{"name":"DynamicFeeConfig","discriminator":[244,226,88,82,14,59,246,220]},{"name":"LimitOrderNonce","discriminator":[17,153,236,57,25,199,231,248]},{"name":"LimitOrderState","discriminator":[1,238,5,142,207,62,44,228]},{"name":"ObservationState","discriminator":[122,174,197,53,129,9,165,132]},{"name":"OperationState","discriminator":[19,236,58,237,81,222,183,252]},{"name":"PersonalPositionState","discriminator":[70,111,150,126,230,15,25,117]},{"name":"PoolState","discriminator":[247,237,227,245,215,195,222,70]},{"name":"ProtocolPositionState","discriminator":[100,226,145,99,146,218,160,106]},{"name":"SupportMintAssociated","discriminator":[134,40,183,79,12,112,162,53]},{"name":"TickArrayBitmapExtension","discriminator":[60,150,36,219,97,128,139,153]},{"name":"TickArrayState","discriminator":[192,155,85,205,49,249,129,42]}],"events":[{"name":"CollectPersonalFeeEvent","discriminator":[166,174,105,192,81,161,83,105]},{"name":"CollectProtocolFeeEvent","discriminator":[206,87,17,79,45,41,213,61]},{"name":"ConfigChangeEvent","discriminator":[247,189,7,119,106,112,95,151]},{"name":"CreatePersonalPositionEvent","discriminator":[100,30,87,249,196,223,154,206]},{"name":"DecreaseLimitOrderEvent","discriminator":[70,48,40,221,219,237,212,163]},{"name":"DecreaseLiquidityEvent","discriminator":[58,222,86,58,68,50,85,56]},{"name":"IncreaseLimitOrderEvent","discriminator":[11,120,13,204,199,87,19,200]},{"name":"IncreaseLiquidityEvent","discriminator":[49,79,105,212,32,34,30,84]},{"name":"LiquidityCalculateEvent","discriminator":[237,112,148,230,57,84,180,162]},{"name":"LiquidityChangeEvent","discriminator":[126,240,175,206,158,88,153,107]},{"name":"OpenLimitOrderEvent","discriminator":[106,24,71,85,57,169,158,216]},{"name":"PoolCreatedEvent","discriminator":[25,94,75,47,112,99,53,63]},{"name":"SettleLimitOrderEvent","discriminator":[88,119,77,164,125,124,10,194]},{"name":"SwapEvent","discriminator":[64,198,205,232,38,8,113,226]},{"name":"UpdateRewardInfosEvent","discriminator":[109,127,186,78,114,65,37,236]}],"errors":[{"code":6000,"name":"NotApproved","msg":"Not approved"},{"code":6001,"name":"InvalidUpdateConfigFlag","msg":"invalid update amm config flag"},{"code":6002,"name":"AccountLack","msg":"Account lack"},{"code":6003,"name":"ClosePositionErr","msg":"Remove liquidity, collect fees owed and reward then you can close position account"},{"code":6004,"name":"InvalidTickIndex","msg":"Tick out of range"},{"code":6005,"name":"TickInvalidOrder","msg":"The lower tick must be below the upper tick"},{"code":6006,"name":"TickLowerOverflow","msg":"The tick must be greater, or equal to the minimum tick(-443636)"},{"code":6007,"name":"TickUpperOverflow","msg":"The tick must be lesser than, or equal to the maximum tick(443636)"},{"code":6008,"name":"TickAndSpacingNotMatch","msg":"tick % tick_spacing must be zero"},{"code":6009,"name":"InvalidTickArray","msg":"Invalid tick array account"},{"code":6010,"name":"InvalidTickArrayBoundary","msg":"Invalid tick array boundary"},{"code":6011,"name":"SqrtPriceLimitOverflow","msg":"Square root price limit overflow"},{"code":6012,"name":"SqrtPriceX64","msg":"sqrt_price_x64 out of range"},{"code":6013,"name":"LiquiditySubValueErr","msg":"Liquidity sub delta L must be smaller than before"},{"code":6014,"name":"LiquidityAddValueErr","msg":"Liquidity add delta L must be greater, or equal to before"},{"code":6015,"name":"ForbidBothZeroForSupplyLiquidity","msg":"Both token amount must not be zero while supply liquidity"},{"code":6016,"name":"LiquidityInsufficient","msg":"Liquidity insufficient"},{"code":6017,"name":"PriceSlippageCheck","msg":"Price slippage check"},{"code":6018,"name":"TooLittleOutputReceived","msg":"Too little output received"},{"code":6019,"name":"TooMuchInputPaid","msg":"Too much input paid"},{"code":6020,"name":"ZeroAmountSpecified","msg":"Swap special amount can not be zero"},{"code":6021,"name":"InvalidInputPoolVault","msg":"Input pool vault is invalid"},{"code":6022,"name":"TooSmallInputOrOutputAmount","msg":"Swap input or output amount is too small"},{"code":6023,"name":"NotEnoughTickArrayAccount","msg":"Not enough tick array account"},{"code":6024,"name":"InvalidFirstTickArrayAccount","msg":"Invalid first tick array account"},{"code":6025,"name":"InvalidRewardIndex","msg":"Invalid reward index"},{"code":6026,"name":"FullRewardInfo","msg":"The init reward token reach to the max"},{"code":6027,"name":"RewardTokenAlreadyInUse","msg":"The init reward token already in use"},{"code":6028,"name":"ExceptRewardMint","msg":"The reward tokens must contain one of pool vault mint except the last reward"},{"code":6029,"name":"InvalidRewardInitParam","msg":"Invalid reward init param"},{"code":6030,"name":"InvalidRewardInputAccountNumber","msg":"Invalid collect reward input account number"},{"code":6031,"name":"InvalidRewardPeriod","msg":"Invalid reward period"},{"code":6032,"name":"NotApproveUpdateRewardEmissions","msg":"Modification of emissions is allowed within 72 hours from the end of the previous cycle"},{"code":6033,"name":"UnInitializedRewardInfo","msg":"uninitialized reward info"},{"code":6034,"name":"NotSupportMint","msg":"Not support token_2022 mint extension"},{"code":6035,"name":"MissingTickArrayBitmapExtensionAccount","msg":"Missing tickarray bitmap extension account"},{"code":6036,"name":"InsufficientLiquidityForDirection","msg":"Insufficient liquidity for this direction"},{"code":6037,"name":"MaxTokenOverflow","msg":"Max token overflow"},{"code":6038,"name":"CalculateOverflow","msg":"Calculate overflow"},{"code":6039,"name":"TransferFeeCalculateNotMatch","msg":"TransferFee calculate not match"},{"code":6040,"name":"OrderAlreadyFilled","msg":"Order already fully filled, cannot modify"},{"code":6041,"name":"InvalidOrderPhase","msg":"Invalid order phase"},{"code":6042,"name":"InvalidLimitOrderAmount","msg":"Invalid limit order amount"},{"code":6043,"name":"OrderPhaseSaturated","msg":"Tick order phase saturated"},{"code":6044,"name":"InvalidDynamicFeeConfigParams","msg":"Invalid dynamic fee config params"},{"code":6045,"name":"InvalidFeeOn","msg":"Invalid fee on which token (must be 0, 1, or 2)"},{"code":6046,"name":"ZeroSqrtPrice","msg":"sqrt_price_x64 must be greater than 0"},{"code":6047,"name":"ZeroLiquidity","msg":"liquidity must be greater than 0"},{"code":6048,"name":"MissingBaseFlag","msg":"base_flag is required when liquidity is zero"},{"code":6049,"name":"MissingMintAccount","msg":"Mint account is required but not provided"},{"code":6050,"name":"MissingTokenProgram2022","msg":"Token-2022 program is required but not provided"}],"types":[{"name":"AmmConfig","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"index","type":"u16"},{"name":"owner","docs":["Address of the protocol owner"],"type":"pubkey"},{"name":"protocol_fee_rate","docs":["The protocol fee"],"type":"u32"},{"name":"trade_fee_rate","docs":["The trade fee, denominated in hundredths of a bip (10^-6)"],"type":"u32"},{"name":"tick_spacing","docs":["The tick spacing"],"type":"u16"},{"name":"fund_fee_rate","docs":["The fund fee, denominated in hundredths of a bip (10^-6)"],"type":"u32"},{"name":"padding_u32","type":"u32"},{"name":"fund_owner","docs":["The fund owner"],"type":"pubkey"},{"name":"padding","type":{"array":["u64",3]}}]}},{"name":"CollectFeeOn","type":{"kind":"enum","variants":[{"name":"FromInput"},{"name":"Token0Only"},{"name":"Token1Only"}]}},{"name":"CollectPersonalFeeEvent","docs":["Emitted when tokens are collected for a position"],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which underlying tokens were collected"],"type":"pubkey"},{"name":"recipient_token_account_0","docs":["The token account that received the collected token_0 tokens"],"type":"pubkey"},{"name":"recipient_token_account_1","docs":["The token account that received the collected token_1 tokens"],"type":"pubkey"},{"name":"amount_0","docs":["The amount of token_0 owed to the position that was collected"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_1 owed to the position that was collected"],"type":"u64"}]}},{"name":"CollectProtocolFeeEvent","docs":["Emitted when the collected protocol fees are withdrawn by the factory owner"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool whose protocol fee is collected"],"type":"pubkey"},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"type":"pubkey"},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"type":"pubkey"},{"name":"amount_0","docs":["The amount of token_0 protocol fees that is withdrawn"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_0 protocol fees that is withdrawn"],"type":"u64"}]}},{"name":"ConfigChangeEvent","docs":["Emitted when create or update a config"],"type":{"kind":"struct","fields":[{"name":"index","type":"u16"},{"name":"owner","type":"pubkey"},{"name":"protocol_fee_rate","type":"u32"},{"name":"trade_fee_rate","type":"u32"},{"name":"tick_spacing","type":"u16"},{"name":"fund_fee_rate","type":"u32"},{"name":"fund_owner","type":"pubkey"}]}},{"name":"CreateCustomizableParams","type":{"kind":"struct","fields":[{"name":"sqrt_price_x64","type":"u128"},{"name":"collect_fee_on","type":{"defined":{"name":"CollectFeeOn"}}},{"name":"enable_dynamic_fee","type":"bool"}]}},{"name":"CreatePersonalPositionEvent","docs":["Emitted when create a new position"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for which liquidity was added"],"type":"pubkey"},{"name":"minter","docs":["The address that create the position"],"type":"pubkey"},{"name":"nft_owner","docs":["The owner of the position and recipient of any minted liquidity"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity minted to the position range"],"type":"u128"},{"name":"deposit_amount_0","docs":["The amount of token_0 was deposit for the liquidity"],"type":"u64"},{"name":"deposit_amount_1","docs":["The amount of token_1 was deposit for the liquidity"],"type":"u64"},{"name":"deposit_amount_0_transfer_fee","docs":["The token transfer fee for deposit_amount_0"],"type":"u64"},{"name":"deposit_amount_1_transfer_fee","docs":["The token transfer fee for deposit_amount_1"],"type":"u64"}]}},{"name":"DecreaseLimitOrderEvent","type":{"kind":"struct","fields":[{"name":"pool_id","docs":["The pool whose limit order was decreased"],"type":"pubkey"},{"name":"limit_order","docs":["The limit order account"],"type":"pubkey"},{"name":"zero_for_one","docs":["Direction of the limit order (true if zero_for_one)"],"type":"bool"},{"name":"tick_index","docs":["Tick index of the limit order"],"type":"i32"},{"name":"total_amount","docs":["Total amount of the limit order"],"type":"u64"},{"name":"filled_amount","docs":["Filled amount of the limit order"],"type":"u64"},{"name":"settled_output_amount","docs":["Amount of output tokens settled (excluding fees)"],"type":"u64"},{"name":"decreased_amount","docs":["Amount of input tokens returned to user"],"type":"u64"}]}},{"name":"DecreaseLiquidityEvent","docs":["Emitted when liquidity is decreased."],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which liquidity was decreased"],"type":"pubkey"},{"name":"liquidity","docs":["The amount by which liquidity for the position was decreased"],"type":"u128"},{"name":"decrease_amount_0","docs":["The amount of token_0 that was paid for the decrease in liquidity"],"type":"u64"},{"name":"decrease_amount_1","docs":["The amount of token_1 that was paid for the decrease in liquidity"],"type":"u64"},{"name":"fee_amount_0","type":"u64"},{"name":"fee_amount_1","docs":["The amount of token_1 fee"],"type":"u64"},{"name":"reward_amounts","docs":["The amount of rewards"],"type":{"array":["u64",3]}},{"name":"transfer_fee_0","docs":["The amount of token_0 transfer fee"],"type":"u64"},{"name":"transfer_fee_1","docs":["The amount of token_1 transfer fee"],"type":"u64"}]}},{"name":"DynamicFeeConfig","type":{"kind":"struct","fields":[{"name":"index","type":"u16"},{"name":"filter_period","type":"u16"},{"name":"decay_period","type":"u16"},{"name":"reduction_factor","type":"u16"},{"name":"dynamic_fee_control","type":"u32"},{"name":"max_volatility_accumulator","type":"u32"},{"name":"padding","type":{"array":["u64",8]}}]}},{"name":"DynamicFeeInfo","docs":["Dynamic fee information for pool configuration"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"filter_period","docs":["Period that determines the high frequency trading time window (in seconds)."],"type":"u16"},{"name":"decay_period","docs":["Period that determines when the dynamic fee starts to decrease (in seconds)."],"type":"u16"},{"name":"reduction_factor","docs":["Dynamic fee rate decrement rate, used for volatility reference decay."],"type":"u16"},{"name":"dynamic_fee_control","docs":["Factor used to scale the dynamic fee component in the fee rate calculation."],"type":"u32"},{"name":"max_volatility_accumulator","docs":["Maximum value for the volatility accumulator, used to cap the dynamic fee rate."],"type":"u32"},{"name":"tick_spacing_index_reference","docs":["Active tick spacing index at the last reference update."],"type":"i32"},{"name":"volatility_reference","docs":["Volatility reference value, stores the decayed volatility accumulator."],"type":"u32"},{"name":"volatility_accumulator","docs":["Volatility accumulator, used to calculate the dynamic fee rate."],"type":"u32"},{"name":"last_update_timestamp","docs":["Last timestamp (block time) when `volatility_reference` and `tick_spacing_index_reference` were updated."],"type":"u64"},{"name":"padding","docs":["Reserved for future upgrades."],"type":{"array":["u8",46]}}]}},{"name":"IncreaseLimitOrderEvent","type":{"kind":"struct","fields":[{"name":"pool_id","docs":["The pool whose limit order was increased"],"type":"pubkey"},{"name":"limit_order","docs":["The limit order account"],"type":"pubkey"},{"name":"zero_for_one","docs":["Direction of the limit order (true if zero_for_one)"],"type":"bool"},{"name":"tick_index","docs":["Tick index of the limit order"],"type":"i32"},{"name":"total_amount","docs":["Total amount of the limit order"],"type":"u64"},{"name":"increased_amount","docs":["Increased amount of the limit order"],"type":"u64"},{"name":"transfer_fee","docs":["Transfer fee of the limit order"],"type":"u64"}]}},{"name":"IncreaseLiquidityEvent","docs":["Emitted when liquidity is increased."],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which liquidity was increased"],"type":"pubkey"},{"name":"liquidity","docs":["The amount by which liquidity for the NFT position was increased"],"type":"u128"},{"name":"amount_0","docs":["The amount of token_0 that was paid for the increase in liquidity"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_1 that was paid for the increase in liquidity"],"type":"u64"},{"name":"amount_0_transfer_fee","docs":["The token transfer fee for amount_0"],"type":"u64"},{"name":"amount_1_transfer_fee","docs":["The token transfer fee for amount_1"],"type":"u64"}]}},{"name":"InitializeRewardParam","type":{"kind":"struct","fields":[{"name":"open_time","docs":["Reward open time"],"type":"u64"},{"name":"end_time","docs":["Reward end time"],"type":"u64"},{"name":"emissions_per_second_x64","docs":["Token reward per second are earned per unit of liquidity"],"type":"u128"}]}},{"name":"LimitOrderNonce","type":{"kind":"struct","fields":[{"name":"user_wallet","type":"pubkey"},{"name":"nonce_index","type":"u8"},{"name":"order_nonce","type":"u64"},{"name":"padding","type":{"array":["u64",4]}}]}},{"name":"LimitOrderState","type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"owner","docs":["Owner of this limit order"],"type":"pubkey"},{"name":"tick_index","type":"i32"},{"name":"zero_for_one","type":"bool"},{"name":"order_phase","docs":["Order phase of this limit order, aligned with `TickState.order_phase` for FIFO semantics"],"type":"u64"},{"name":"total_amount","docs":["Total amount of the limit order"],"type":"u64"},{"name":"filled_amount","docs":["Filled amount of the limit order (informational, for events/display)"],"type":"u64"},{"name":"settle_base","docs":["The unfilled amount when the current computation segment started.","Set to total_amount on open, reset on decrease. Never updated during settle —","this avoids cascading floor-division error across multiple settles."],"type":"u64"},{"name":"settled_output","docs":["Cumulative output paid to user in the current segment. Reset on decrease.","Each settle computes total_output from the segment base and pays the diff","(total_output - settled_output), limiting dust deduction to once per segment."],"type":"u64"},{"name":"open_time","docs":["Order open time"],"type":"u64"},{"name":"unfilled_ratio_x64","docs":["Snapshot of `TickState.unfilled_ratio_x64` at the time this segment started (Q64.64).","Initialized to Q64 on open; reset to tick's current ratio on decrease."],"type":"u128"},{"name":"padding","type":{"array":["u64",4]}}]}},{"name":"LiquidityCalculateEvent","docs":["Emitted when liquidity decreased or increase."],"type":{"kind":"struct","fields":[{"name":"pool_liquidity","docs":["The pool liquidity before decrease or increase"],"type":"u128"},{"name":"pool_sqrt_price_x64","docs":["The pool price when decrease or increase in liquidity"],"type":"u128"},{"name":"pool_tick","docs":["The pool tick when decrease or increase in liquidity"],"type":"i32"},{"name":"calc_amount_0","docs":["The amount of token_0 that was calculated for the decrease or increase in liquidity"],"type":"u64"},{"name":"calc_amount_1","docs":["The amount of token_1 that was calculated for the decrease or increase in liquidity"],"type":"u64"},{"name":"trade_fee_owed_0","type":"u64"},{"name":"trade_fee_owed_1","docs":["The amount of token_1 fee"],"type":"u64"},{"name":"transfer_fee_0","docs":["The amount of token_0 transfer fee without trade_fee_amount_0"],"type":"u64"},{"name":"transfer_fee_1","docs":["The amount of token_1 transfer fee without trade_fee_amount_0"],"type":"u64"}]}},{"name":"LiquidityChangeEvent","docs":["Emitted pool liquidity change when increase and decrease liquidity"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for swap"],"type":"pubkey"},{"name":"tick","docs":["The tick of the pool"],"type":"i32"},{"name":"tick_lower","docs":["The tick lower of position"],"type":"i32"},{"name":"tick_upper","docs":["The tick lower of position"],"type":"i32"},{"name":"liquidity_before","docs":["The liquidity of the pool before liquidity change"],"type":"u128"},{"name":"liquidity_after","docs":["The liquidity of the pool after liquidity change"],"type":"u128"}]}},{"name":"Observation","docs":["The element of observations in ObservationState"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"block_timestamp","docs":["The block timestamp of the observation"],"type":"u32"},{"name":"tick_cumulative","docs":["the cumulative of tick during the duration time"],"type":"i64"},{"name":"padding","docs":["padding for feature update"],"type":{"array":["u64",4]}}]}},{"name":"ObservationState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"initialized","docs":["Whether the ObservationState is initialized"],"type":"bool"},{"name":"recent_epoch","docs":["recent update epoch"],"type":"u64"},{"name":"observation_index","docs":["the most-recently updated index of the observations array"],"type":"u16"},{"name":"pool_id","docs":["belongs to which pool"],"type":"pubkey"},{"name":"observations","docs":["observation array"],"type":{"array":[{"defined":{"name":"Observation"}},100]}},{"name":"padding","docs":["padding for feature update"],"type":{"array":["u64",4]}}]}},{"name":"OpenLimitOrderEvent","docs":["Emitted when a limit order is opened"],"type":{"kind":"struct","fields":[{"name":"pool_id","docs":["The pool whose limit order was opened"],"type":"pubkey"},{"name":"limit_order","docs":["The limit order account"],"type":"pubkey"},{"name":"zero_for_one","docs":["Direction of the limit order (true if zero_for_one)"],"type":"bool"},{"name":"tick_index","docs":["Tick index of the limit order"],"type":"i32"},{"name":"total_amount","docs":["Total amount of the limit order"],"type":"u64"},{"name":"transfer_fee","docs":["Transfer fee of the limit order"],"type":"u64"}]}},{"name":"OperationState","docs":["Holds the current owner of the factory"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"operation_owners","docs":["Address of the operation owner"],"type":{"array":["pubkey",10]}},{"name":"whitelist_mints","docs":["The mint address of whitelist to emit reward"],"type":{"array":["pubkey",100]}}]}},{"name":"PersonalPositionState","type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":{"array":["u8",1]}},{"name":"nft_mint","docs":["Mint address of the tokenized position"],"type":"pubkey"},{"name":"pool_id","docs":["The ID of the pool with which this token is connected"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"fee_growth_inside_0_last_x64","docs":["The token_0 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"fee_growth_inside_1_last_x64","docs":["The token_1 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"token_fees_owed_0","docs":["The fees owed to the position owner in token_0, as of the last computation"],"type":"u64"},{"name":"token_fees_owed_1","docs":["The fees owed to the position owner in token_1, as of the last computation"],"type":"u64"},{"name":"reward_infos","type":{"array":[{"defined":{"name":"PositionRewardInfo"}},3]}},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u64",7]}}]}},{"name":"PoolCreatedEvent","docs":["Emitted when a pool is created and initialized with a starting price",""],"type":{"kind":"struct","fields":[{"name":"token_mint_0","docs":["The first token of the pool by address sort order"],"type":"pubkey"},{"name":"token_mint_1","docs":["The second token of the pool by address sort order"],"type":"pubkey"},{"name":"tick_spacing","docs":["The minimum number of ticks between initialized ticks"],"type":"u16"},{"name":"pool_state","docs":["The address of the created pool"],"type":"pubkey"},{"name":"sqrt_price_x64","docs":["The initial sqrt price of the pool, as a Q64.64"],"type":"u128"},{"name":"tick","docs":["The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool"],"type":"i32"},{"name":"token_vault_0","docs":["Vault of token_0"],"type":"pubkey"},{"name":"token_vault_1","docs":["Vault of token_1"],"type":"pubkey"}]}},{"name":"PoolState","docs":["The pool state","","PDA of `[POOL_SEED, config, token_mint_0, token_mint_1]`",""],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":{"array":["u8",1]}},{"name":"amm_config","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"token_mint_0","docs":["Token pair of the pool, where token_mint_0 address < token_mint_1 address"],"type":"pubkey"},{"name":"token_mint_1","type":"pubkey"},{"name":"token_vault_0","docs":["Token pair vault"],"type":"pubkey"},{"name":"token_vault_1","type":"pubkey"},{"name":"observation_key","docs":["observation account key"],"type":"pubkey"},{"name":"mint_decimals_0","docs":["mint0 and mint1 decimals"],"type":"u8"},{"name":"mint_decimals_1","type":"u8"},{"name":"tick_spacing","docs":["The minimum number of ticks between initialized ticks"],"type":"u16"},{"name":"liquidity","docs":["The currently in range liquidity available to the pool."],"type":"u128"},{"name":"sqrt_price_x64","docs":["The current price of the pool as a sqrt(token_1/token_0) Q64.64 value"],"type":"u128"},{"name":"tick_current","docs":["The current tick of the pool, i.e. according to the last tick transition that was run."],"type":"i32"},{"name":"padding3","type":"u16"},{"name":"padding4","type":"u16"},{"name":"fee_growth_global_0_x64","docs":["The fee growth as a Q64.64 number, i.e. fees of token_0 and token_1 collected per","unit of liquidity for the entire life of the pool."],"type":"u128"},{"name":"fee_growth_global_1_x64","type":"u128"},{"name":"protocol_fees_token_0","docs":["The amounts of token_0 and token_1 that are owed to the protocol."],"type":"u64"},{"name":"protocol_fees_token_1","type":"u64"},{"name":"padding5","type":{"array":["u128",4]}},{"name":"status","docs":["Bitwise representation of the state of the pool","bit0, 1: disable open position and increase liquidity, 0: normal","bit1, 1: disable decrease liquidity, 0: normal","bit2, 1: disable collect fee, 0: normal","bit3, 1: disable collect reward, 0: normal","bit4, 1: disable swap, 0: normal","bit5, 1: disable limit order, 0: normal"],"type":"u8"},{"name":"fee_on","docs":["Fee on which token (0 = FromInput, 1 = Token0Only, 2 = Token1Only)"],"type":"u8"},{"name":"padding","docs":["Leave blank for future use"],"type":{"array":["u8",6]}},{"name":"reward_infos","type":{"array":[{"defined":{"name":"RewardInfo"}},3]}},{"name":"tick_array_bitmap","docs":["Packed initialized tick array state"],"type":{"array":["u64",16]}},{"name":"padding6","type":{"array":["u64",4]}},{"name":"fund_fees_token_0","type":"u64"},{"name":"fund_fees_token_1","type":"u64"},{"name":"open_time","type":"u64"},{"name":"recent_epoch","type":"u64"},{"name":"dynamic_fee_info","docs":["Dynamic fee info"],"type":{"defined":{"name":"DynamicFeeInfo"}}},{"name":"padding1","type":{"array":["u64",14]}},{"name":"padding2","type":{"array":["u64",32]}}]}},{"name":"PositionRewardInfo","type":{"kind":"struct","fields":[{"name":"growth_inside_last_x64","type":"u128"},{"name":"reward_amount_owed","type":"u64"}]}},{"name":"ProtocolPositionState","docs":["Info stored for each user's position"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"pool_id","docs":["The ID of the pool with which this token is connected"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"fee_growth_inside_0_last_x64","docs":["The token_0 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"fee_growth_inside_1_last_x64","docs":["The token_1 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"token_fees_owed_0","docs":["The fees owed to the position owner in token_0"],"type":"u64"},{"name":"token_fees_owed_1","docs":["The fees owed to the position owner in token_1"],"type":"u64"},{"name":"reward_growth_inside","docs":["The reward growth per unit of liquidity as of the last update to liquidity"],"type":{"array":["u128",3]}},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u64",7]}}]}},{"name":"RewardInfo","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"reward_state","docs":["Reward state"],"type":"u8"},{"name":"open_time","docs":["Reward open time"],"type":"u64"},{"name":"end_time","docs":["Reward end time"],"type":"u64"},{"name":"last_update_time","docs":["Reward last update time"],"type":"u64"},{"name":"emissions_per_second_x64","docs":["Q64.64 number indicates how many tokens per second are earned per unit of liquidity."],"type":"u128"},{"name":"reward_total_emitted","docs":["The total amount of reward emitted"],"type":"u64"},{"name":"reward_claimed","docs":["The total amount of claimed reward"],"type":"u64"},{"name":"token_mint","docs":["Reward token mint."],"type":"pubkey"},{"name":"token_vault","docs":["Reward vault token account."],"type":"pubkey"},{"name":"authority","docs":["The owner that has permission to set reward param"],"type":"pubkey"},{"name":"reward_growth_global_x64","docs":["Q64.64 number that tracks the total tokens earned per unit of liquidity since the reward","emissions were turned on."],"type":"u128"}]}},{"name":"SettleLimitOrderEvent","docs":["Emitted when a limit order is settled and proceeds are transferred"],"type":{"kind":"struct","fields":[{"name":"pool_id","docs":["The pool whose limit order was settled"],"type":"pubkey"},{"name":"limit_order","docs":["The limit order account"],"type":"pubkey"},{"name":"zero_for_one","docs":["Direction of the limit order (true if zero_for_one)"],"type":"bool"},{"name":"tick_index","docs":["Tick index of the limit order"],"type":"i32"},{"name":"total_amount","docs":["Total amount of the limit order"],"type":"u64"},{"name":"filled_amount","docs":["Filled amount of the limit order"],"type":"u64"},{"name":"settled_amount_out","docs":["Amount of output tokens transferred (excluding fees)"],"type":"u64"}]}},{"name":"SupportMintAssociated","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"mint","docs":["Address of the supported token22 mint"],"type":"pubkey"},{"name":"padding","type":{"array":["u64",8]}}]}},{"name":"SwapEvent","docs":["Emitted by when a swap is performed for a pool"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for which token_0 and token_1 were swapped"],"type":"pubkey"},{"name":"sender","docs":["The address that initiated the swap call, and that received the callback"],"type":"pubkey"},{"name":"token_account_0","docs":["The payer token account in zero for one swaps, or the recipient token account","in one for zero swaps"],"type":"pubkey"},{"name":"token_account_1","docs":["The payer token account in one for zero swaps, or the recipient token account","in zero for one swaps"],"type":"pubkey"},{"name":"amount_0","docs":["The real delta amount of the token_0 of the pool or user"],"type":"u64"},{"name":"transfer_fee_0","docs":["The transfer fee charged by the withheld_amount of the token_0"],"type":"u64"},{"name":"amount_1","docs":["The real delta of the token_1 of the pool or user"],"type":"u64"},{"name":"transfer_fee_1","docs":["The transfer fee charged by the withheld_amount of the token_1"],"type":"u64"},{"name":"zero_for_one","docs":["if true, amount_0 is negtive and amount_1 is positive"],"type":"bool"},{"name":"sqrt_price_x64","docs":["The sqrt(price) of the pool after the swap, as a Q64.64"],"type":"u128"},{"name":"liquidity","docs":["The liquidity of the pool after the swap"],"type":"u128"},{"name":"tick","docs":["The log base 1.0001 of price of the pool after the swap"],"type":"i32"},{"name":"trade_fee_0","docs":["Total AMM trade fee (lp + protocol + fund) charged in token_0 during this swap"],"type":"u64"},{"name":"trade_fee_1","docs":["Total AMM trade fee (lp + protocol + fund) charged in token_1 during this swap"],"type":"u64"}]}},{"name":"TickArrayBitmapExtension","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"positive_tick_array_bitmap","docs":["Packed initialized tick array state for start_tick_index is positive"],"type":{"array":[{"array":["u64",8]},14]}},{"name":"negative_tick_array_bitmap","docs":["Packed initialized tick array state for start_tick_index is negitive"],"type":{"array":[{"array":["u64",8]},14]}}]}},{"name":"TickArrayState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"start_tick_index","type":"i32"},{"name":"ticks","type":{"array":[{"defined":{"name":"TickState"}},60]}},{"name":"initialized_tick_count","type":"u8"},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u8",107]}}]}},{"name":"TickState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"tick","type":"i32"},{"name":"liquidity_net","docs":["Amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left)"],"type":"i128"},{"name":"liquidity_gross","docs":["The total position liquidity that references this tick"],"type":"u128"},{"name":"fee_growth_outside_0_x64","docs":["Fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)","only has relative meaning, not absolute — the value depends on when the tick is initialized"],"type":"u128"},{"name":"fee_growth_outside_1_x64","type":"u128"},{"name":"reward_growths_outside_x64","docs":["Reward growth per unit of liquidity like fee, array of Q64.64"],"type":{"array":["u128",3]}},{"name":"order_phase","docs":["Order phase of the tick, used as a FIFO cohort index for limit orders"],"type":"u64"},{"name":"orders_amount","docs":["The amount of limit orders that have never been matched,","only counts newly opened orders, not partially filled ones"],"type":"u64"},{"name":"part_filled_orders_remaining","docs":["Remaining part filled orders amount"],"type":"u64"},{"name":"unfilled_ratio_x64","docs":["Cumulative unfilled ratio for the current part-filled cohort (Q64.64 format).","Starts at Q64(1) when a new cohort forms, multiplied down as fills occur."],"type":"u128"},{"name":"padding","type":{"array":["u32",3]}}]}},{"name":"UpdateRewardInfosEvent","docs":["Emitted when Reward are updated for a pool"],"type":{"kind":"struct","fields":[{"name":"reward_growth_global_x64","docs":["Reward info"],"type":{"array":["u128",3]}}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/raydium_cpmm.CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C.json b/migration/khadhroony-bot2-reference/idls/raydium_cpmm.CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C.json new file mode 100644 index 0000000..8499ebe --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/raydium_cpmm.CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C.json @@ -0,0 +1 @@ +{"address":"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C","metadata":{"name":"raydium_cp_swap","version":"0.2.0","spec":"0.1.0","description":"Raydium constant product AMM, supports Token2022 and without Openbook"},"instructions":[{"name":"close_permission_pda","docs":["Close a permission account","","# Arguments","","* `ctx`- The context of accounts",""],"discriminator":[156,84,32,118,69,135,70,123],"accounts":[{"name":"owner","writable":true,"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"permission_authority"},{"name":"permission","docs":["Initialize config state account to store protocol owner address and fee rates."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,101,114,109,105,115,115,105,111,110]},{"kind":"account","path":"permission_authority"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"collect_creator_fee","docs":["Collect the creator fee","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[20,22,86,123,198,28,219,132],"accounts":[{"name":"creator","docs":["Only pool creator can collect fee"],"writable":true,"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores fund_owner"]},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"creator_token_0","docs":["The address that receives the collected token_0 fund fees"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"creator"},{"kind":"account","path":"token_0_program"},{"kind":"account","path":"vault_0_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"creator_token_1","docs":["The address that receives the collected token_1 fund fees"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"creator"},{"kind":"account","path":"token_1_program"},{"kind":"account","path":"vault_1_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_0_program","docs":["Spl token program or token program 2022"]},{"name":"token_1_program","docs":["Spl token program or token program 2022"]},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"collect_fund_fee","docs":["Collect the fund fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[167,138,78,149,223,194,6,126],"accounts":[{"name":"owner","docs":["Only admin or fund_owner can collect fee now"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores fund_owner"]},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"recipient_token_0_account","docs":["The address that receives the collected token_0 fund fees"],"writable":true},{"name":"recipient_token_1_account","docs":["The address that receives the collected token_1 fund fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_protocol_fee","docs":["Collect the protocol fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[136,136,252,221,194,66,126,89],"accounts":[{"name":"owner","docs":["Only admin or owner can collect fee now"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores owner"]},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"recipient_token_0_account","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_1_account","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"create_amm_config","docs":["# Arguments","","* `ctx`- The accounts needed by instruction.","* `index` - The index of amm config, there may be multiple config.","* `trade_fee_rate` - Trade fee rate, can be changed.","* `protocol_fee_rate` - The rate of protocol fee within trade fee.","* `fund_fee_rate` - The rate of fund fee within trade fee.",""],"discriminator":[137,52,237,212,215,117,108,104],"accounts":[{"name":"owner","docs":["Address to be set as protocol owner."],"writable":true,"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"amm_config","docs":["Initialize config state account to store protocol owner address and fee rates."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,109,109,95,99,111,110,102,105,103]},{"kind":"arg","path":"index"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"u16"},{"name":"trade_fee_rate","type":"u64"},{"name":"protocol_fee_rate","type":"u64"},{"name":"fund_fee_rate","type":"u64"},{"name":"create_pool_fee","type":"u64"},{"name":"creator_fee_rate","type":"u64"}]},{"name":"create_permission_pda","docs":["Create a permission account","","# Arguments","","* `ctx`- The context of accounts",""],"discriminator":[135,136,2,216,137,169,181,202],"accounts":[{"name":"owner","writable":true,"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"permission_authority"},{"name":"permission","docs":["Initialize config state account to store protocol owner address and fee rates."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,101,114,109,105,115,115,105,111,110]},{"kind":"account","path":"permission_authority"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"deposit","docs":["Deposit lp token to the pool","","# Arguments","","* `ctx`- The context of accounts","* `lp_token_amount` - Increased number of LPs","* `maximum_token_0_amount` - Maximum token 0 amount to deposit, prevents excessive slippage","* `maximum_token_1_amount` - Maximum token 1 amount to deposit, prevents excessive slippage",""],"discriminator":[242,35,198,137,82,225,242,182],"accounts":[{"name":"owner","docs":["Pays to mint the position"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","writable":true},{"name":"owner_lp_token","docs":["Owner lp token account"],"writable":true},{"name":"token_0_account","docs":["The payer's token account for token_0"],"writable":true},{"name":"token_1_account","docs":["The payer's token account for token_1"],"writable":true},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["token Program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"lp_mint","docs":["Lp token mint"],"writable":true}],"args":[{"name":"lp_token_amount","type":"u64"},{"name":"maximum_token_0_amount","type":"u64"},{"name":"maximum_token_1_amount","type":"u64"}]},{"name":"initialize","docs":["Creates a pool for the given token pair and the initial price","","# Arguments","","* `ctx`- The context of accounts","* `init_amount_0` - the initial amount_0 to deposit","* `init_amount_1` - the initial amount_1 to deposit","* `open_time` - the timestamp allowed for swap",""],"discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"creator","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"authority","docs":["pool vault and lp mint authority"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["PDA account:","seeds = [","POOL_SEED.as_bytes(),","amm_config.key().as_ref(),","token_0_mint.key().as_ref(),","token_1_mint.key().as_ref(),","],","","Or random account: must be signed by cli"],"writable":true},{"name":"token_0_mint","docs":["Token_0 mint, the key must smaller than token_1 mint."]},{"name":"token_1_mint","docs":["Token_1 mint, the key must grater then token_0 mint."]},{"name":"lp_mint","docs":["pool lp mint"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,108,112,95,109,105,110,116]},{"kind":"account","path":"pool_state"}]}},{"name":"creator_token_0","docs":["payer token0 account"],"writable":true},{"name":"creator_token_1","docs":["creator token1 account"],"writable":true},{"name":"creator_lp_token","docs":["creator lp token account"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"creator"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_0_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_0_mint"}]}},{"name":"token_1_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_1_mint"}]}},{"name":"create_pool_fee","docs":["create pool fee account"],"writable":true,"address":"DNXgeM9EiiaAbaWvwjHj9fQQLAX5ZsfHyvmYUNRAdNC8"},{"name":"observation_state","docs":["an account to store oracle observations"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_0_program","docs":["Spl token program or token program 2022"]},{"name":"token_1_program","docs":["Spl token program or token program 2022"]},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"init_amount_0","type":"u64"},{"name":"init_amount_1","type":"u64"},{"name":"open_time","type":"u64"}]},{"name":"initialize_with_permission","docs":["Create a pool with permission","","# Arguments","","* `ctx`- The context of accounts","* `init_amount_0` - the initial amount_0 to deposit","* `init_amount_1` - the initial amount_1 to deposit","* `open_time` - the timestamp allowed for swap","* `creator_fee_on` - creator fee model, 0:both token0 and token1 (depends on the input), 1: only token0, 2: only token1",""],"discriminator":[63,55,254,65,49,178,89,121],"accounts":[{"name":"payer","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"creator"},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"authority","docs":["pool vault and lp mint authority"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["PDA account:","seeds = [","POOL_SEED.as_bytes(),","amm_config.key().as_ref(),","token_0_mint.key().as_ref(),","token_1_mint.key().as_ref(),","],","","Or random account: must be signed by cli"],"writable":true},{"name":"token_0_mint","docs":["Token_0 mint, the key must smaller than token_1 mint."]},{"name":"token_1_mint","docs":["Token_1 mint, the key must grater then token_0 mint."]},{"name":"lp_mint","docs":["pool lp mint"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,108,112,95,109,105,110,116]},{"kind":"account","path":"pool_state"}]}},{"name":"payer_token_0","docs":["payer token0 account"],"writable":true},{"name":"payer_token_1","docs":["payer token1 account"],"writable":true},{"name":"payer_lp_token","docs":["payer lp token account"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"payer"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"lp_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_0_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_0_mint"}]}},{"name":"token_1_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_1_mint"}]}},{"name":"create_pool_fee","docs":["create pool fee account"],"writable":true,"address":"DNXgeM9EiiaAbaWvwjHj9fQQLAX5ZsfHyvmYUNRAdNC8"},{"name":"observation_state","docs":["an account to store oracle observations"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"permission","pda":{"seeds":[{"kind":"const","value":[112,101,114,109,105,115,115,105,111,110]},{"kind":"account","path":"payer"}]}},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_0_program","docs":["Spl token program or token program 2022"]},{"name":"token_1_program","docs":["Spl token program or token program 2022"]},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"}],"args":[{"name":"init_amount_0","type":"u64"},{"name":"init_amount_1","type":"u64"},{"name":"open_time","type":"u64"},{"name":"creator_fee_on","type":{"defined":{"name":"CreatorFeeOn"}}}]},{"name":"swap_base_input","docs":["Swap the tokens in the pool base input amount","","# Arguments","","* `ctx`- The context of accounts","* `amount_in` - input amount to transfer, output to DESTINATION is based on the exchange rate","* `minimum_amount_out` - Minimum amount of output token, prevents excessive slippage",""],"discriminator":[143,190,90,218,196,30,51,222],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"input_token_program","docs":["SPL program for input token transfers"]},{"name":"output_token_program","docs":["SPL program for output token transfers"]},{"name":"input_token_mint","docs":["The mint of input token"]},{"name":"output_token_mint","docs":["The mint of output token"]},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true}],"args":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"}]},{"name":"swap_base_output","docs":["Swap the tokens in the pool base output amount","","# Arguments","","* `ctx`- The context of accounts","* `max_amount_in` - input amount prevents excessive slippage","* `amount_out` - amount of output token",""],"discriminator":[55,217,98,86,163,74,180,173],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"input_token_program","docs":["SPL program for input token transfers"]},{"name":"output_token_program","docs":["SPL program for output token transfers"]},{"name":"input_token_mint","docs":["The mint of input token"]},{"name":"output_token_mint","docs":["The mint of output token"]},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true}],"args":[{"name":"max_amount_in","type":"u64"},{"name":"amount_out","type":"u64"}]},{"name":"update_amm_config","docs":["Updates the owner of the amm config","Must be called by the current owner or admin","","# Arguments","","* `ctx`- The context of accounts","* `trade_fee_rate`- The new trade fee rate of amm config, be set when `param` is 0","* `protocol_fee_rate`- The new protocol fee rate of amm config, be set when `param` is 1","* `fund_fee_rate`- The new fund fee rate of amm config, be set when `param` is 2","* `new_owner`- The config's new owner, be set when `param` is 3","* `new_fund_owner`- The config's new fund owner, be set when `param` is 4","* `param`- The value can be 0 | 1 | 2 | 3 | 4, otherwise will report a error",""],"discriminator":[49,60,174,136,154,28,116,200],"accounts":[{"name":"owner","docs":["The amm config owner or admin"],"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"amm_config","docs":["Amm config account to be changed"],"writable":true}],"args":[{"name":"param","type":"u8"},{"name":"value","type":"u64"}]},{"name":"update_pool_status","docs":["Update pool status for given value","","# Arguments","","* `ctx`- The context of accounts","* `status` - The value of status",""],"discriminator":[130,87,108,6,46,224,117,123],"accounts":[{"name":"authority","signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"pool_state","writable":true}],"args":[{"name":"status","type":"u8"}]},{"name":"withdraw","docs":["Withdraw lp for token0 and token1","","# Arguments","","* `ctx`- The context of accounts","* `lp_token_amount` - Amount of pool tokens to burn. User receives an output of token a and b based on the percentage of the pool tokens that are returned.","* `minimum_token_0_amount` - Minimum amount of token 0 to receive, prevents excessive slippage","* `minimum_token_1_amount` - Minimum amount of token 1 to receive, prevents excessive slippage",""],"discriminator":[183,18,70,156,148,109,161,34],"accounts":[{"name":"owner","docs":["Pays to mint the position"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state account"],"writable":true},{"name":"owner_lp_token","docs":["Owner lp token account"],"writable":true},{"name":"token_0_account","docs":["The token account for receive token_0,"],"writable":true},{"name":"token_1_account","docs":["The token account for receive token_1"],"writable":true},{"name":"token_0_vault","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_1_vault","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["token Program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token_0 vault"]},{"name":"vault_1_mint","docs":["The mint of token_1 vault"]},{"name":"lp_mint","docs":["Pool lp token mint"],"writable":true},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"lp_token_amount","type":"u64"},{"name":"minimum_token_0_amount","type":"u64"},{"name":"minimum_token_1_amount","type":"u64"}]}],"accounts":[{"name":"AmmConfig","discriminator":[218,244,33,104,203,203,43,111]},{"name":"ObservationState","discriminator":[122,174,197,53,129,9,165,132]},{"name":"Permission","discriminator":[224,83,28,79,10,253,161,28]},{"name":"PoolState","discriminator":[247,237,227,245,215,195,222,70]}],"events":[{"name":"LpChangeEvent","discriminator":[121,163,205,201,57,218,117,60]},{"name":"SwapEvent","discriminator":[64,198,205,232,38,8,113,226]}],"errors":[{"code":6000,"name":"NotApproved","msg":"Not approved"},{"code":6001,"name":"InvalidOwner","msg":"Input account owner is not the program address"},{"code":6002,"name":"EmptySupply","msg":"Input token account empty"},{"code":6003,"name":"InvalidInput","msg":"InvalidInput"},{"code":6004,"name":"IncorrectLpMint","msg":"Address of the provided lp token mint is incorrect"},{"code":6005,"name":"ExceededSlippage","msg":"Exceeds desired slippage limit"},{"code":6006,"name":"ZeroTradingTokens","msg":"Given pool token amount results in zero trading tokens"},{"code":6007,"name":"NotSupportMint","msg":"Not support token_2022 mint extension"},{"code":6008,"name":"InvalidVault","msg":"invaild vault"},{"code":6009,"name":"InitLpAmountTooLess","msg":"Init lp amount is too less(Because 100 amount lp will be locked)"},{"code":6010,"name":"TransferFeeCalculateNotMatch","msg":"TransferFee calculate not match"},{"code":6011,"name":"MathOverflow","msg":"Math overflow"},{"code":6012,"name":"InsufficientVault","msg":"Insufficient vault"},{"code":6013,"name":"InvalidFeeModel","msg":"Invalid fee model"},{"code":6014,"name":"NoFeeCollect","msg":"Fee is zero"}],"types":[{"name":"AmmConfig","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"disable_create_pool","docs":["Status to control if new pool can be create"],"type":"bool"},{"name":"index","docs":["Config index"],"type":"u16"},{"name":"trade_fee_rate","docs":["The trade fee, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"protocol_fee_rate","docs":["The protocol fee"],"type":"u64"},{"name":"fund_fee_rate","docs":["The fund fee, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"create_pool_fee","docs":["Fee for create a new pool"],"type":"u64"},{"name":"protocol_owner","docs":["Address of the protocol fee owner"],"type":"pubkey"},{"name":"fund_owner","docs":["Address of the fund fee owner"],"type":"pubkey"},{"name":"creator_fee_rate","docs":["The pool creator fee, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"padding","docs":["padding"],"type":{"array":["u64",15]}}]}},{"name":"CreatorFeeOn","type":{"kind":"enum","variants":[{"name":"BothToken"},{"name":"OnlyToken0"},{"name":"OnlyToken1"}]}},{"name":"LpChangeEvent","docs":["Emitted when deposit and withdraw"],"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"lp_amount_before","type":"u64"},{"name":"token_0_vault_before","docs":["pool vault sub trade fees"],"type":"u64"},{"name":"token_1_vault_before","docs":["pool vault sub trade fees"],"type":"u64"},{"name":"token_0_amount","docs":["calculate result without transfer fee"],"type":"u64"},{"name":"token_1_amount","docs":["calculate result without transfer fee"],"type":"u64"},{"name":"token_0_transfer_fee","type":"u64"},{"name":"token_1_transfer_fee","type":"u64"},{"name":"change_type","type":"u8"}]}},{"name":"Observation","docs":["The element of observations in ObservationState"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"block_timestamp","docs":["The block timestamp of the observation"],"type":"u64"},{"name":"cumulative_token_0_price_x32","docs":["the cumulative of token0 price during the duration time, Q32.32, the remaining 64 bit for overflow"],"type":"u128"},{"name":"cumulative_token_1_price_x32","docs":["the cumulative of token1 price during the duration time, Q32.32, the remaining 64 bit for overflow"],"type":"u128"}]}},{"name":"ObservationState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"initialized","docs":["Whether the ObservationState is initialized"],"type":"bool"},{"name":"observation_index","docs":["the most-recently updated index of the observations array"],"type":"u16"},{"name":"pool_id","type":"pubkey"},{"name":"observations","docs":["observation array"],"type":{"array":[{"defined":{"name":"Observation"}},100]}},{"name":"padding","docs":["padding for feature update"],"type":{"array":["u64",4]}}]}},{"name":"Permission","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"authority","docs":["authority"],"type":"pubkey"},{"name":"padding","docs":["padding"],"type":{"array":["u64",30]}}]}},{"name":"PoolState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"amm_config","docs":["Which config the pool belongs"],"type":"pubkey"},{"name":"pool_creator","docs":["pool creator"],"type":"pubkey"},{"name":"token_0_vault","docs":["Token A"],"type":"pubkey"},{"name":"token_1_vault","docs":["Token B"],"type":"pubkey"},{"name":"lp_mint","docs":["Pool tokens are issued when A or B tokens are deposited.","Pool tokens can be withdrawn back to the original A or B token."],"type":"pubkey"},{"name":"token_0_mint","docs":["Mint information for token A"],"type":"pubkey"},{"name":"token_1_mint","docs":["Mint information for token B"],"type":"pubkey"},{"name":"token_0_program","docs":["token_0 program"],"type":"pubkey"},{"name":"token_1_program","docs":["token_1 program"],"type":"pubkey"},{"name":"observation_key","docs":["observation account to store oracle data"],"type":"pubkey"},{"name":"auth_bump","type":"u8"},{"name":"status","docs":["Bitwise representation of the state of the pool","bit0, 1: disable deposit(value is 1), 0: normal","bit1, 1: disable withdraw(value is 2), 0: normal","bit2, 1: disable swap(value is 4), 0: normal"],"type":"u8"},{"name":"lp_mint_decimals","type":"u8"},{"name":"mint_0_decimals","docs":["mint0 and mint1 decimals"],"type":"u8"},{"name":"mint_1_decimals","type":"u8"},{"name":"lp_supply","docs":["True circulating supply without burns and lock ups"],"type":"u64"},{"name":"protocol_fees_token_0","docs":["The amounts of token_0 and token_1 that are owed to the liquidity provider."],"type":"u64"},{"name":"protocol_fees_token_1","type":"u64"},{"name":"fund_fees_token_0","type":"u64"},{"name":"fund_fees_token_1","type":"u64"},{"name":"open_time","docs":["The timestamp allowed for swap in the pool."],"type":"u64"},{"name":"recent_epoch","docs":["recent epoch"],"type":"u64"},{"name":"creator_fee_on","docs":["Creator fee collect mode","0: both token_0 and token_1 can be used as trade fees. It depends on what the input token is when swapping","1: only token_0 as trade fee","2: only token_1 as trade fee"],"type":"u8"},{"name":"enable_creator_fee","type":"bool"},{"name":"padding1","type":{"array":["u8",6]}},{"name":"creator_fees_token_0","type":"u64"},{"name":"creator_fees_token_1","type":"u64"},{"name":"padding","docs":["padding for future updates"],"type":{"array":["u64",28]}}]}},{"name":"SwapEvent","docs":["Emitted when swap"],"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"input_vault_before","docs":["pool vault sub trade fees"],"type":"u64"},{"name":"output_vault_before","docs":["pool vault sub trade fees"],"type":"u64"},{"name":"input_amount","docs":["calculate result without transfer fee"],"type":"u64"},{"name":"output_amount","docs":["calculate result without transfer fee"],"type":"u64"},{"name":"input_transfer_fee","type":"u64"},{"name":"output_transfer_fee","type":"u64"},{"name":"base_input","type":"bool"},{"name":"input_mint","type":"pubkey"},{"name":"output_mint","type":"pubkey"},{"name":"trade_fee","type":"u64"},{"name":"creator_fee","docs":["Amount of fee tokens going to creator"],"type":"u64"},{"name":"creator_fee_on_input","type":"bool"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/raydium_launchlab.LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj.json b/migration/khadhroony-bot2-reference/idls/raydium_launchlab.LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj.json new file mode 100644 index 0000000..52f4970 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/raydium_launchlab.LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj.json @@ -0,0 +1 @@ +{"address":"LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj","metadata":{"name":"raydium_launchpad","version":"0.2.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"buy_exact_in","docs":["Use the given amount of quote tokens to purchase base tokens.","# Arguments","","* `ctx` - The context of accounts","* `amount_in` - Amount of quote token to purchase","* `minimum_amount_out` - Minimum amount of base token to receive (slippage protection)","* `share_fee_rate` - Fee rate for the share",""],"discriminator":[250,234,13,123,213,156,19,236],"accounts":[{"name":"payer","docs":["The user performing the swap operation","Must sign the transaction and pay for fees"],"writable":true,"signer":true},{"name":"authority","docs":["PDA that acts as the authority for pool vault operations","Generated using AUTH_SEED"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"global_config","docs":["Global configuration account containing protocol-wide settings","Used to read protocol fee rates and curve type"]},{"name":"platform_config","docs":["Platform configuration account containing platform-wide settings","Used to read platform fee rate"]},{"name":"pool_state","docs":["The pool state account where the swap will be performed","Contains current pool parameters and balances"],"writable":true},{"name":"user_base_token","docs":["The user's token account for base tokens (tokens being bought)","Will receive the output tokens after the swap"],"writable":true},{"name":"user_quote_token","docs":["The user's token account for quote tokens (tokens being sold)","Will be debited for the input amount"],"writable":true},{"name":"base_vault","docs":["The pool's vault for base tokens","Will be debited to send tokens to the user"],"writable":true},{"name":"quote_vault","docs":["The pool's vault for quote tokens","Will receive the input tokens from the user"],"writable":true},{"name":"base_token_mint","docs":["The mint of the base token","Used for transfer fee calculations if applicable"]},{"name":"quote_token_mint","docs":["The mint of the quote token"]},{"name":"base_token_program","docs":["SPL Token program for base token transfers"]},{"name":"quote_token_program","docs":["SPL Token program for quote token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"},{"name":"share_fee_rate","type":"u64"}]},{"name":"buy_exact_out","docs":["Use quote tokens to purchase the given amount of base tokens.","# Arguments","","* `ctx` - The context of accounts","* `amount_out` - Amount of base token to receive","* `maximum_amount_in` - Maximum amount of quote token to purchase (slippage protection)","* `share_fee_rate` - Fee rate for the share"],"discriminator":[24,211,116,40,105,3,153,56],"accounts":[{"name":"payer","docs":["The user performing the swap operation","Must sign the transaction and pay for fees"],"writable":true,"signer":true},{"name":"authority","docs":["PDA that acts as the authority for pool vault operations","Generated using AUTH_SEED"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"global_config","docs":["Global configuration account containing protocol-wide settings","Used to read protocol fee rates and curve type"]},{"name":"platform_config","docs":["Platform configuration account containing platform-wide settings","Used to read platform fee rate"]},{"name":"pool_state","docs":["The pool state account where the swap will be performed","Contains current pool parameters and balances"],"writable":true},{"name":"user_base_token","docs":["The user's token account for base tokens (tokens being bought)","Will receive the output tokens after the swap"],"writable":true},{"name":"user_quote_token","docs":["The user's token account for quote tokens (tokens being sold)","Will be debited for the input amount"],"writable":true},{"name":"base_vault","docs":["The pool's vault for base tokens","Will be debited to send tokens to the user"],"writable":true},{"name":"quote_vault","docs":["The pool's vault for quote tokens","Will receive the input tokens from the user"],"writable":true},{"name":"base_token_mint","docs":["The mint of the base token","Used for transfer fee calculations if applicable"]},{"name":"quote_token_mint","docs":["The mint of the quote token"]},{"name":"base_token_program","docs":["SPL Token program for base token transfers"]},{"name":"quote_token_program","docs":["SPL Token program for quote token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_out","type":"u64"},{"name":"maximum_amount_in","type":"u64"},{"name":"share_fee_rate","type":"u64"}]},{"name":"claim_creator_fee","docs":["Claim the fee from the exclusive creator fee vault.","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[26,97,138,203,132,171,141,252],"accounts":[{"name":"creator","docs":["The pool creator"],"writable":true,"signer":true},{"name":"fee_vault_authority","pda":{"seeds":[{"kind":"const","value":[99,114,101,97,116,111,114,95,102,101,101,95,118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"creator_fee_vault","docs":["The creator fee vault"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"creator"},{"kind":"account","path":"quote_mint"}]}},{"name":"recipient_token_account","writable":true,"pda":{"seeds":[{"kind":"account","path":"creator"},{"kind":"account","path":"token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"quote_mint","docs":["The mint for the quote token"]},{"name":"token_program","docs":["SPL Token program for the quote token"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["Required for associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[]},{"name":"claim_platform_fee","docs":["Claim platform fee","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[156,39,208,135,76,237,61,72],"accounts":[{"name":"platform_fee_wallet","docs":["Only the wallet stored in platform_config can collect platform fees"],"writable":true,"signer":true},{"name":"authority","docs":["PDA that acts as the authority for pool vault and mint operations","Generated using AUTH_SEED"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Account that stores the pool's state and parameters","PDA generated using POOL_SEED and both token mints"],"writable":true},{"name":"platform_config","docs":["The platform config account"]},{"name":"quote_vault","writable":true},{"name":"recipient_token_account","docs":["The address that receives the collected quote token fees"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"platform_fee_wallet"},{"kind":"account","path":"token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"quote_mint","docs":["The mint of quote token vault"]},{"name":"token_program","docs":["SPL program for input token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["Required for associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[]},{"name":"claim_platform_fee_from_vault","docs":["Claim the fee from the exclusive platform fee vault.","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[117,241,198,168,248,218,80,29],"accounts":[{"name":"platform_fee_wallet","docs":["Only the wallet stored in platform_config can collect platform fees"],"writable":true,"signer":true},{"name":"fee_vault_authority","pda":{"seeds":[{"kind":"const","value":[112,108,97,116,102,111,114,109,95,102,101,101,95,118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"platform_config","docs":["The platform config account"]},{"name":"platform_fee_vault","docs":["The platform fee vault"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"platform_config"},{"kind":"account","path":"quote_mint"}]}},{"name":"recipient_token_account","docs":["The address that receives the collected quote token fees"],"writable":true,"pda":{"seeds":[{"kind":"account","path":"platform_fee_wallet"},{"kind":"account","path":"token_program"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"quote_mint","docs":["The mint of quote token vault"]},{"name":"token_program","docs":["SPL program for input token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["Required for associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[]},{"name":"claim_vested_token","docs":["Claim vested token","# Arguments"],"discriminator":[49,33,104,30,189,157,79,35],"accounts":[{"name":"beneficiary","docs":["The beneficiary of the vesting account"],"writable":true,"signer":true},{"name":"authority","docs":["PDA that acts as the authority for pool vault and mint operations","Generated using AUTH_SEED"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Account that stores the pool's state and parameters","PDA generated using POOL_SEED and both token mints"],"writable":true},{"name":"vesting_record","docs":["The vesting record account"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,101,115,116,105,110,103]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"beneficiary"}]}},{"name":"base_vault","docs":["The pool's vault for base tokens","Will be debited to send tokens to the user"],"writable":true},{"name":"user_base_token","writable":true,"pda":{"seeds":[{"kind":"account","path":"beneficiary"},{"kind":"account","path":"base_token_program"},{"kind":"account","path":"base_token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"base_token_mint","docs":["The mint for the base token (token being sold)","Created in this instruction with specified decimals"]},{"name":"base_token_program","docs":["SPL Token program for the base token","Must be the standard Token program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"associated_token_program","docs":["Required for associated token program"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"}],"args":[]},{"name":"close_platform_global_access","docs":["Close a platform's permission to use a global config."],"discriminator":[123,180,184,129,111,185,187,59],"accounts":[{"name":"owner","signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"global_config"},{"name":"platform_config"},{"name":"platform_global_access","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,108,97,116,102,111,114,109,95,103,108,111,98,97,108,95,97,99,99,101,115,115]},{"kind":"account","path":"platform_config"},{"kind":"account","path":"global_config"}]}}],"args":[]},{"name":"collect_fee","docs":["Collects accumulated fees from the pool","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[60,173,247,103,4,93,130,48],"accounts":[{"name":"owner","docs":["Only protocol_fee_owner saved in global_config can collect protocol fee now"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"global_config","docs":["Global config account stores owner"]},{"name":"quote_vault","docs":["The address that holds pool tokens for quote token"],"writable":true},{"name":"quote_mint","docs":["The mint of quote token vault"]},{"name":"recipient_token_account","docs":["The address that receives the collected quote token fees"],"writable":true},{"name":"token_program","docs":["SPL program for input token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[]},{"name":"collect_migrate_fee","docs":["Collects migrate fees from the pool","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[255,186,150,223,235,118,201,186],"accounts":[{"name":"owner","docs":["Only migrate_fee_owner saved in global_config can collect migrate fee now"],"signer":true},{"name":"authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"global_config","docs":["Global config account stores owner"]},{"name":"quote_vault","docs":["The address that holds pool tokens for quote token"],"writable":true},{"name":"quote_mint","docs":["The mint of quote token vault"]},{"name":"recipient_token_account","docs":["The address that receives the collected quote token fees"],"writable":true},{"name":"token_program","docs":["SPL program for input token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[]},{"name":"create_config","docs":["Creates a new configuration","# Arguments","","* `ctx` - The accounts needed by instruction","* `curve_type` - The type of bonding curve (0: ConstantProduct)","* `index` - The index of config, there may be multiple config with the same curve type.","* `trade_fee_rate` - Trade fee rate, must be less than RATE_DENOMINATOR_VALUE",""],"discriminator":[201,207,243,114,75,111,47,189],"accounts":[{"name":"owner","docs":["The protocol owner/admin account","Must match the predefined admin address","Has authority to create and modify protocol configurations"],"writable":true,"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"global_config","docs":["Global configuration account that stores protocol-wide settings","PDA generated using GLOBAL_CONFIG_SEED, quote token mint, and curve type","Stores fee rates and protocol parameters"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[103,108,111,98,97,108,95,99,111,110,102,105,103]},{"kind":"account","path":"quote_token_mint"},{"kind":"arg","path":"curve_type"},{"kind":"arg","path":"index"}]}},{"name":"quote_token_mint","docs":["The mint address of the quote token (token used for buying)","This will be the standard token used for all pools with this config"]},{"name":"protocol_fee_owner","docs":["Account that will receive protocol fees"]},{"name":"migrate_fee_owner","docs":["Account that will receive migrate fees"]},{"name":"migrate_to_amm_wallet","docs":["The control wallet address for migrating to amm"]},{"name":"migrate_to_cpswap_wallet","docs":["The control wallet address for migrating to cpswap"]},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"}],"args":[{"name":"curve_type","type":"u8"},{"name":"index","type":"u16"},{"name":"migrate_fee","type":"u64"},{"name":"trade_fee_rate","type":"u64"}]},{"name":"create_platform_config","docs":["Create platform config account","# Arguments","","* `ctx` - The context of accounts","# Fields","* `fee_rate` - Fee rate of the platform","* `name` - Name of the platform","* `web` - Website of the platform","* `img` - Image link of the platform",""],"discriminator":[176,90,196,175,253,113,220,20],"accounts":[{"name":"platform_admin","docs":["The account paying for the initialization costs"],"writable":true,"signer":true},{"name":"platform_fee_wallet"},{"name":"platform_nft_wallet"},{"name":"platform_config","docs":["The platform config account"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,108,97,116,102,111,114,109,95,99,111,110,102,105,103]},{"kind":"account","path":"platform_admin"}]}},{"name":"cpswap_config"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"transfer_fee_extension_authority"},{"name":"platform_vesting_wallet"}],"args":[{"name":"platform_params","type":{"defined":{"name":"PlatformParams"}}}]},{"name":"create_platform_global_access","docs":["Create a platform permission to use a global config when authorization is enabled."],"discriminator":[162,91,146,199,93,133,234,237],"accounts":[{"name":"owner","writable":true,"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"global_config"},{"name":"platform_config"},{"name":"platform_global_access","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,108,97,116,102,111,114,109,95,103,108,111,98,97,108,95,97,99,99,101,115,115]},{"kind":"account","path":"platform_config"},{"kind":"account","path":"global_config"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_platform_vesting_account","docs":["Create vesting account","# Arguments","","* `ctx` - The context of accounts","* `share` - The share amount of base token to be vested",""],"discriminator":[146,71,173,69,98,19,15,106],"accounts":[{"name":"platform_vesting_wallet","docs":["The account paying for the initialization costs","This can be any account with sufficient SOL to cover the transaction"],"writable":true,"signer":true},{"name":"beneficiary","docs":["The beneficiary is used to receive the allocated linear release of tokens.","Once this account is set, it cannot be modified, so please ensure the validity of this account,","otherwise, the unlocked tokens will not be claimable."],"writable":true},{"name":"platform_config","docs":["Platform config account to be changed"]},{"name":"pool_state","docs":["The pool state account"],"writable":true},{"name":"platform_vesting_record","docs":["The vesting record account"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,101,115,116,105,110,103]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"beneficiary"}]}},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_vesting_account","docs":["Create vesting account","# Arguments","","* `ctx` - The context of accounts","* `share` - The share amount of base token to be vested",""],"discriminator":[129,178,2,13,217,172,230,218],"accounts":[{"name":"creator","docs":["The account paying for the initialization costs","This can be any account with sufficient SOL to cover the transaction"],"writable":true,"signer":true},{"name":"beneficiary","docs":["The beneficiary is used to receive the allocated linear release of tokens.","Once this account is set, it cannot be modified, so please ensure the validity of this account,","otherwise, the unlocked tokens will not be claimable."],"writable":true},{"name":"pool_state","docs":["The pool state account"],"writable":true},{"name":"vesting_record","docs":["The vesting record account"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,101,115,116,105,110,103]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"beneficiary"}]}},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"}],"args":[{"name":"share_amount","type":"u64"}]},{"name":"initialize","docs":["Initializes a new trading pool","# Arguments","","* `ctx` - The context of accounts containing pool and token information",""],"discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"payer","docs":["The account paying for the initialization costs","This can be any account with sufficient SOL to cover the transaction"],"writable":true,"signer":true},{"name":"creator"},{"name":"global_config","docs":["Global configuration account containing protocol-wide settings","Includes settings like quote token mint and fee parameters"]},{"name":"platform_config","docs":["Platform configuration account containing platform info","Includes settings like the fee_rate, name, web, img of the platform"]},{"name":"authority","docs":["PDA that acts as the authority for pool vault and mint operations","Generated using AUTH_SEED"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Account that stores the pool's state and parameters","PDA generated using POOL_SEED and both token mints"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"base_mint"},{"kind":"account","path":"quote_mint"}]}},{"name":"base_mint","docs":["The mint for the base token (token being sold)","Created in this instruction with specified decimals"],"writable":true,"signer":true},{"name":"quote_mint","docs":["The mint for the quote token (token used to buy)","Must match the quote_mint specified in global config"]},{"name":"base_vault","docs":["Token account that holds the pool's base tokens","PDA generated using POOL_VAULT_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"base_mint"}]}},{"name":"quote_vault","docs":["Token account that holds the pool's quote tokens","PDA generated using POOL_VAULT_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"quote_mint"}]}},{"name":"metadata_account","docs":["Account to store the base token's metadata","Created using Metaplex metadata program"],"writable":true},{"name":"base_token_program","docs":["SPL Token program for the base token","Must be the standard Token program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"quote_token_program","docs":["SPL Token program for the quote token"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"metadata_program","docs":["Metaplex Token Metadata program","Used to create metadata for the base token"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"rent_program","docs":["Required for rent exempt calculations"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"base_mint_param","type":{"defined":{"name":"MintParams"}}},{"name":"curve_param","type":{"defined":{"name":"CurveParams"}}},{"name":"vesting_param","type":{"defined":{"name":"VestingParams"}}}]},{"name":"initialize_v2","docs":["Initializes a new trading pool","# Arguments","","* `ctx` - The context of accounts containing pool and token information",""],"discriminator":[67,153,175,39,218,16,38,32],"accounts":[{"name":"payer","docs":["The account paying for the initialization costs","This can be any account with sufficient SOL to cover the transaction"],"writable":true,"signer":true},{"name":"creator"},{"name":"global_config","docs":["Global configuration account containing protocol-wide settings","Includes settings like quote token mint and fee parameters"]},{"name":"platform_config","docs":["Platform configuration account containing platform info","Includes settings like the fee_rate, name, web, img of the platform"]},{"name":"authority","docs":["PDA that acts as the authority for pool vault and mint operations","Generated using AUTH_SEED"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Account that stores the pool's state and parameters","PDA generated using POOL_SEED and both token mints"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"base_mint"},{"kind":"account","path":"quote_mint"}]}},{"name":"base_mint","docs":["The mint for the base token (token being sold)","Created in this instruction with specified decimals"],"writable":true,"signer":true},{"name":"quote_mint","docs":["The mint for the quote token (token used to buy)","Must match the quote_mint specified in global config"]},{"name":"base_vault","docs":["Token account that holds the pool's base tokens","PDA generated using POOL_VAULT_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"base_mint"}]}},{"name":"quote_vault","docs":["Token account that holds the pool's quote tokens","PDA generated using POOL_VAULT_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"quote_mint"}]}},{"name":"metadata_account","docs":["Account to store the base token's metadata","Created using Metaplex metadata program"],"writable":true},{"name":"base_token_program","docs":["SPL Token program for the base token","Must be the standard Token program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"quote_token_program","docs":["SPL Token program for the quote token"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"metadata_program","docs":["Metaplex Token Metadata program","Used to create metadata for the base token"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"rent_program","docs":["Required for rent exempt calculations"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"base_mint_param","type":{"defined":{"name":"MintParams"}}},{"name":"curve_param","type":{"defined":{"name":"CurveParams"}}},{"name":"vesting_param","type":{"defined":{"name":"VestingParams"}}},{"name":"amm_fee_on","type":{"defined":{"name":"AmmCreatorFeeOn"}}}]},{"name":"initialize_with_token_2022","docs":["Initializes a new trading pool with base token belongs to spl-token-2022,","pool created by this instruction must be migrated to cpswap after fundraising ends, i.e., curve_param.migrate_type = 1","# Arguments","","* `ctx` - The context of accounts containing pool and token information",""],"discriminator":[37,190,126,222,44,154,171,17],"accounts":[{"name":"payer","docs":["The account paying for the initialization costs","This can be any account with sufficient SOL to cover the transaction"],"writable":true,"signer":true},{"name":"creator"},{"name":"global_config","docs":["Global configuration account containing protocol-wide settings","Includes settings like quote token mint and fee parameters"]},{"name":"platform_config","docs":["Platform configuration account containing platform info","Includes settings like the fee_rate, name, web, img of the platform"]},{"name":"authority","docs":["PDA that acts as the authority for pool vault and mint operations","Generated using AUTH_SEED"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Account that stores the pool's state and parameters","PDA generated using POOL_SEED and both token mints"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"base_mint"},{"kind":"account","path":"quote_mint"}]}},{"name":"base_mint","docs":["The mint for the base token (token being sold)","Created in this instruction with specified decimals"],"writable":true,"signer":true},{"name":"quote_mint","docs":["The mint for the quote token (token used to buy)","Must match the quote_mint specified in global config"]},{"name":"base_vault","docs":["Token account that holds the pool's base tokens","PDA generated using POOL_VAULT_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"base_mint"}]}},{"name":"quote_vault","docs":["Token account that holds the pool's quote tokens","PDA generated using POOL_VAULT_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"quote_mint"}]}},{"name":"base_token_program","docs":["SPL Token program for the base token"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"quote_token_program","docs":["SPL Token program for the quote token"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"base_mint_param","type":{"defined":{"name":"MintParams"}}},{"name":"curve_param","type":{"defined":{"name":"CurveParams"}}},{"name":"vesting_param","type":{"defined":{"name":"VestingParams"}}},{"name":"amm_fee_on","type":{"defined":{"name":"AmmCreatorFeeOn"}}},{"name":"transfer_fee_extension_param","type":{"option":{"defined":{"name":"TransferFeeExtensionParams"}}}}]},{"name":"migrate_to_amm","docs":["# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[207,82,192,145,254,207,145,223],"accounts":[{"name":"payer","docs":["Only migrate_to_amm_wallet can migrate to cpswap pool","This signer must match the migrate_to_amm_wallet saved in global_config"],"writable":true,"signer":true},{"name":"base_mint","docs":["The mint for the base token (token being sold)"]},{"name":"quote_mint","docs":["The mint for the quote token (token used to buy)"]},{"name":"openbook_program","address":"srmqPvymJeFKQ4zGQed1GFppgkRHL9kaELCbyksJtPX"},{"name":"market","docs":["Account created and asigned to openbook_program but not been initialized"],"writable":true},{"name":"request_queue","docs":["Account created and asigned to openbook_program but not been initialized"],"writable":true},{"name":"event_queue","docs":["Account created and asigned to openbook_program but not been initialized"],"writable":true},{"name":"bids","docs":["Account created and asigned to openbook_program but not been initialized"],"writable":true},{"name":"asks","docs":["Account created and asigned to openbook_program but not been initialized"],"writable":true},{"name":"market_vault_signer"},{"name":"market_base_vault","docs":["Token account that holds the market's base tokens"],"writable":true},{"name":"market_quote_vault","docs":["Token account that holds the market's quote tokens"],"writable":true},{"name":"amm_program","address":"675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"},{"name":"amm_pool","writable":true,"pda":{"seeds":[{"kind":"account","path":"amm_program"},{"kind":"account","path":"market"},{"kind":"const","value":[97,109,109,95,97,115,115,111,99,105,97,116,101,100,95,115,101,101,100]}],"program":{"kind":"account","path":"amm_program"}}},{"name":"amm_authority","pda":{"seeds":[{"kind":"const","value":[97,109,109,32,97,117,116,104,111,114,105,116,121]}],"program":{"kind":"account","path":"amm_program"}}},{"name":"amm_open_orders","writable":true,"pda":{"seeds":[{"kind":"account","path":"amm_program"},{"kind":"account","path":"market"},{"kind":"const","value":[111,112,101,110,95,111,114,100,101,114,95,97,115,115,111,99,105,97,116,101,100,95,115,101,101,100]}],"program":{"kind":"account","path":"amm_program"}}},{"name":"amm_lp_mint","writable":true,"pda":{"seeds":[{"kind":"account","path":"amm_program"},{"kind":"account","path":"market"},{"kind":"const","value":[108,112,95,109,105,110,116,95,97,115,115,111,99,105,97,116,101,100,95,115,101,101,100]}],"program":{"kind":"account","path":"amm_program"}}},{"name":"amm_base_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"amm_program"},{"kind":"account","path":"market"},{"kind":"const","value":[99,111,105,110,95,118,97,117,108,116,95,97,115,115,111,99,105,97,116,101,100,95,115,101,101,100]}],"program":{"kind":"account","path":"amm_program"}}},{"name":"amm_quote_vault","writable":true,"pda":{"seeds":[{"kind":"account","path":"amm_program"},{"kind":"account","path":"market"},{"kind":"const","value":[112,99,95,118,97,117,108,116,95,97,115,115,111,99,105,97,116,101,100,95,115,101,101,100]}],"program":{"kind":"account","path":"amm_program"}}},{"name":"amm_target_orders","writable":true,"pda":{"seeds":[{"kind":"account","path":"amm_program"},{"kind":"account","path":"market"},{"kind":"const","value":[116,97,114,103,101,116,95,97,115,115,111,99,105,97,116,101,100,95,115,101,101,100]}],"program":{"kind":"account","path":"amm_program"}}},{"name":"amm_config","pda":{"seeds":[{"kind":"const","value":[97,109,109,95,99,111,110,102,105,103,95,97,99,99,111,117,110,116,95,115,101,101,100]}],"program":{"kind":"account","path":"amm_program"}}},{"name":"amm_create_fee_destination","writable":true},{"name":"authority","docs":["PDA that acts as the authority for pool vault operations","Generated using AUTH_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Account that stores the pool's state and parameters","PDA generated using POOL_SEED and both token mints"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"base_mint"},{"kind":"account","path":"quote_mint"}]}},{"name":"global_config","docs":["Global config account stores owner"]},{"name":"base_vault","docs":["The pool's vault for base tokens","Will be fully drained during migration"],"writable":true},{"name":"quote_vault","docs":["The pool's vault for quote tokens","Will be fully drained during migration"],"writable":true},{"name":"pool_lp_token","writable":true},{"name":"spl_token_program","docs":["SPL Token program for the base token","Must be the standard Token program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving fee NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"rent_program","docs":["Required for rent exempt calculations"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"base_lot_size","type":"u64"},{"name":"quote_lot_size","type":"u64"},{"name":"market_vault_signer_nonce","type":"u8"}]},{"name":"migrate_to_cpswap","docs":["# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[136,92,200,103,28,218,144,140],"accounts":[{"name":"payer","docs":["Only migrate_to_cpswap_wallet can migrate to cpswap pool","This signer must match the migrate_to_cpswap_wallet saved in global_config"],"writable":true,"signer":true},{"name":"base_mint","docs":["The mint for the base token (token being sold)"],"writable":true},{"name":"quote_mint","docs":["The mint for the quote token (token used to buy)"]},{"name":"platform_config","docs":["Platform configuration account containing platform-wide settings","Used to read platform fee rate"]},{"name":"cpswap_program","address":"CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"},{"name":"cpswap_pool","docs":["PDA account:","seeds = [","b\"pool\",","cpswap_config.key().as_ref(),","token_0_mint.key().as_ref(),","token_1_mint.key().as_ref(),","],","seeds::program = cpswap_program,","","Or random account: must be signed by cli"],"writable":true},{"name":"cpswap_authority","pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,110,100,95,108,112,95,109,105,110,116,95,97,117,116,104,95,115,101,101,100]}],"program":{"kind":"account","path":"cpswap_program"}}},{"name":"cpswap_lp_mint","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,108,112,95,109,105,110,116]},{"kind":"account","path":"cpswap_pool"}],"program":{"kind":"account","path":"cpswap_program"}}},{"name":"cpswap_base_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"cpswap_pool"},{"kind":"account","path":"base_mint"}],"program":{"kind":"account","path":"cpswap_program"}}},{"name":"cpswap_quote_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"cpswap_pool"},{"kind":"account","path":"quote_mint"}],"program":{"kind":"account","path":"cpswap_program"}}},{"name":"cpswap_config"},{"name":"cpswap_create_pool_fee","writable":true},{"name":"cpswap_observation","writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"cpswap_pool"}],"program":{"kind":"account","path":"cpswap_program"}}},{"name":"lock_program","address":"LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE"},{"name":"lock_authority","pda":{"seeds":[{"kind":"const","value":[108,111,99,107,95,99,112,95,97,117,116,104,111,114,105,116,121,95,115,101,101,100]}],"program":{"kind":"account","path":"lock_program"}}},{"name":"lock_lp_vault","writable":true},{"name":"authority","docs":["PDA that acts as the authority for pool vault operations","Generated using AUTH_SEED"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"pool_state","docs":["Account that stores the pool's state and parameters","PDA generated using POOL_SEED and both token mints"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"base_mint"},{"kind":"account","path":"quote_mint"}]}},{"name":"global_config","docs":["Global config account stores owner"]},{"name":"base_vault","docs":["The pool's vault for base tokens","Will be fully drained during migration"],"writable":true},{"name":"quote_vault","docs":["The pool's vault for quote tokens","Will be fully drained during migration"],"writable":true},{"name":"pool_lp_token","writable":true},{"name":"base_token_program","docs":["SPL Token program for the base token","Must be the standard Token program"]},{"name":"quote_token_program","docs":["SPL Token program for the quote token"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving fee NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["Required for account creation"],"address":"11111111111111111111111111111111"},{"name":"rent_program","docs":["Required for rent exempt calculations"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"metadata_program","docs":["Program to create NFT metadata accunt"],"address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"}],"args":[]},{"name":"remove_platform_curve_param","docs":["Remove platform launch param","# Arguments","","* `ctx` - The context of accounts","* `index` - The index of the curve param to remove",""],"discriminator":[27,30,62,169,93,224,24,145],"accounts":[{"name":"platform_admin","docs":["The account paying for the initialization costs"],"signer":true},{"name":"platform_config","docs":["Platform config account to be changed"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,108,97,116,102,111,114,109,95,99,111,110,102,105,103]},{"kind":"account","path":"platform_admin"}]}}],"args":[{"name":"index","type":"u8"}]},{"name":"sell_exact_in","docs":["Use the given amount of base tokens to sell for quote tokens.","# Arguments","","* `ctx` - The context of accounts","* `amount_in` - Amount of base token to sell","* `minimum_amount_out` - Minimum amount of quote token to receive (slippage protection)","* `share_fee_rate` - Fee rate for the share",""],"discriminator":[149,39,222,155,211,124,152,26],"accounts":[{"name":"payer","docs":["The user performing the swap operation","Must sign the transaction and pay for fees"],"writable":true,"signer":true},{"name":"authority","docs":["PDA that acts as the authority for pool vault operations","Generated using AUTH_SEED"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"global_config","docs":["Global configuration account containing protocol-wide settings","Used to read protocol fee rates and curve type"]},{"name":"platform_config","docs":["Platform configuration account containing platform-wide settings","Used to read platform fee rate"]},{"name":"pool_state","docs":["The pool state account where the swap will be performed","Contains current pool parameters and balances"],"writable":true},{"name":"user_base_token","docs":["The user's token account for base tokens (tokens being bought)","Will receive the output tokens after the swap"],"writable":true},{"name":"user_quote_token","docs":["The user's token account for quote tokens (tokens being sold)","Will be debited for the input amount"],"writable":true},{"name":"base_vault","docs":["The pool's vault for base tokens","Will be debited to send tokens to the user"],"writable":true},{"name":"quote_vault","docs":["The pool's vault for quote tokens","Will receive the input tokens from the user"],"writable":true},{"name":"base_token_mint","docs":["The mint of the base token","Used for transfer fee calculations if applicable"]},{"name":"quote_token_mint","docs":["The mint of the quote token"]},{"name":"base_token_program","docs":["SPL Token program for base token transfers"]},{"name":"quote_token_program","docs":["SPL Token program for quote token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_in","type":"u64"},{"name":"minimum_amount_out","type":"u64"},{"name":"share_fee_rate","type":"u64"}]},{"name":"sell_exact_out","docs":["Sell base tokens for the given amount of quote tokens.","# Arguments","","* `ctx` - The context of accounts","* `amount_out` - Amount of quote token to receive","* `maximum_amount_in` - Maximum amount of base token to purchase (slippage protection)","* `share_fee_rate` - Fee rate for the share",""],"discriminator":[95,200,71,34,8,9,11,166],"accounts":[{"name":"payer","docs":["The user performing the swap operation","Must sign the transaction and pay for fees"],"writable":true,"signer":true},{"name":"authority","docs":["PDA that acts as the authority for pool vault operations","Generated using AUTH_SEED"],"pda":{"seeds":[{"kind":"const","value":[118,97,117,108,116,95,97,117,116,104,95,115,101,101,100]}]}},{"name":"global_config","docs":["Global configuration account containing protocol-wide settings","Used to read protocol fee rates and curve type"]},{"name":"platform_config","docs":["Platform configuration account containing platform-wide settings","Used to read platform fee rate"]},{"name":"pool_state","docs":["The pool state account where the swap will be performed","Contains current pool parameters and balances"],"writable":true},{"name":"user_base_token","docs":["The user's token account for base tokens (tokens being bought)","Will receive the output tokens after the swap"],"writable":true},{"name":"user_quote_token","docs":["The user's token account for quote tokens (tokens being sold)","Will be debited for the input amount"],"writable":true},{"name":"base_vault","docs":["The pool's vault for base tokens","Will be debited to send tokens to the user"],"writable":true},{"name":"quote_vault","docs":["The pool's vault for quote tokens","Will receive the input tokens from the user"],"writable":true},{"name":"base_token_mint","docs":["The mint of the base token","Used for transfer fee calculations if applicable"]},{"name":"quote_token_mint","docs":["The mint of the quote token"]},{"name":"base_token_program","docs":["SPL Token program for base token transfers"]},{"name":"quote_token_program","docs":["SPL Token program for quote token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"event_authority","pda":{"seeds":[{"kind":"const","value":[95,95,101,118,101,110,116,95,97,117,116,104,111,114,105,116,121]}]}},{"name":"program"}],"args":[{"name":"amount_out","type":"u64"},{"name":"maximum_amount_in","type":"u64"},{"name":"share_fee_rate","type":"u64"}]},{"name":"update_config","docs":["Updates configuration parameters","# Arguments","","* `ctx` - The context of accounts","* `param` - Parameter to update:","- 0: Update trade_fee_rate","- 1: Update fee owner","- 12: Toggle whether explicit platform authorization is required","* `value` - New value for the selected parameter",""],"discriminator":[29,158,252,191,10,83,219,99],"accounts":[{"name":"owner","docs":["The global config owner or admin"],"signer":true,"address":"GThUX1Atko4tqhN2NaiTazWSeFWMuiUvfFnyJyUghFMJ"},{"name":"global_config","docs":["Global config account to be changed"],"writable":true}],"args":[{"name":"param","type":"u8"},{"name":"value","type":"u64"}]},{"name":"update_platform_config","docs":["Update platform config","# Arguments","","* `ctx` - The context of accounts","* `param` - Parameter to update",""],"discriminator":[195,60,76,129,146,45,67,143],"accounts":[{"name":"platform_admin","docs":["The account paying for the initialization costs"],"signer":true},{"name":"platform_config","docs":["Platform config account to be changed"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,108,97,116,102,111,114,109,95,99,111,110,102,105,103]},{"kind":"account","path":"platform_admin"}]}}],"args":[{"name":"param","type":{"defined":{"name":"PlatformConfigParam"}}}]},{"name":"update_platform_curve_param","docs":["Update platform launch param","# Arguments","","* `ctx` - The context of accounts","* `bonding_curve_param` - Parameter to update",""],"discriminator":[138,144,138,250,220,128,4,57],"accounts":[{"name":"platform_admin","docs":["The account paying for the initialization costs"],"writable":true,"signer":true},{"name":"platform_config","docs":["Platform config account to be changed"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,108,97,116,102,111,114,109,95,99,111,110,102,105,103]},{"kind":"account","path":"platform_admin"}]}},{"name":"global_config","docs":["Global configuration account containing protocol-wide settings","Includes settings like quote token mint and fee parameters"]},{"name":"system_program","docs":["System program for lamport transfers"],"address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"u8"},{"name":"bonding_curve_param","type":{"defined":{"name":"BondingCurveParam"}}}]}],"accounts":[{"name":"GlobalConfig","discriminator":[149,8,156,202,160,252,176,217]},{"name":"PlatformConfig","discriminator":[160,78,128,0,248,83,230,160]},{"name":"PlatformGlobalAccess","discriminator":[174,147,132,240,137,219,243,16]},{"name":"PoolState","discriminator":[247,237,227,245,215,195,222,70]},{"name":"VestingRecord","discriminator":[106,243,221,205,230,126,85,83]}],"events":[{"name":"ClaimVestedEvent","discriminator":[21,194,114,87,120,211,226,32]},{"name":"CreateVestingEvent","discriminator":[150,152,11,179,52,210,191,125]},{"name":"PoolCreateEvent","discriminator":[151,215,226,9,118,161,115,174]},{"name":"TradeEvent","discriminator":[189,219,127,211,78,230,97,238]}],"errors":[{"code":6000,"name":"NotApproved","msg":"Not approved"},{"code":6001,"name":"InvalidOwner","msg":"Input account owner is not the program address"},{"code":6002,"name":"InvalidInput","msg":"InvalidInput"},{"code":6003,"name":"InputNotMatchCurveConfig","msg":"The input params are not match with curve type in config"},{"code":6004,"name":"ExceededSlippage","msg":"Exceeds desired slippage limit"},{"code":6005,"name":"PoolFunding","msg":"Pool funding"},{"code":6006,"name":"PoolMigrated","msg":"Pool migrated"},{"code":6007,"name":"MigrateTypeNotMatch","msg":"Migrate type not match"},{"code":6008,"name":"MathOverflow","msg":"Math overflow"},{"code":6009,"name":"NoAssetsToCollect","msg":"No assets to collect"},{"code":6010,"name":"VestingRatioTooHigh","msg":"Vesting ratio too high"},{"code":6011,"name":"VestingSettingEnded","msg":"Vesting setting ended"},{"code":6012,"name":"VestingNotStarted","msg":"Vesting not started"},{"code":6013,"name":"NoVestingSchedule","msg":"No vesting schedule"},{"code":6014,"name":"InvalidPlatformInfo","msg":"The platform info input is invalid"},{"code":6015,"name":"PoolNotMigrated","msg":"Pool not migrated"},{"code":6016,"name":"InvalidCpSwapConfig","msg":"The input cp swap config account is invalid"},{"code":6017,"name":"NoSupportExtension","msg":"No support extension"},{"code":6018,"name":"NotEnoughRemainingAccounts","msg":"Not enough remaining accounts"},{"code":6019,"name":"TransferFeeCalculateNotMatch","msg":"TransferFee calculate not match"},{"code":6020,"name":"CurveParamIsNotExist","msg":"Curve param is not exist"},{"code":6021,"name":"InvalidTotalLockedAmount","msg":"Total locked amount must great or equal to the platform vesting share amount"},{"code":6022,"name":"PlatformGlobalAccessDenied","msg":"Platform is not authorized to use this global config"},{"code":6023,"name":"InvalidPlatformGlobalAccess","msg":"Invalid platform-global access account"}],"types":[{"name":"AmmCreatorFeeOn","docs":["migrate to cpmm, creator fee on quote token or both token"],"type":{"kind":"enum","variants":[{"name":"QuoteToken"},{"name":"BothToken"}]}},{"name":"BondingCurveParam","type":{"kind":"struct","fields":[{"name":"migrate_type","docs":["Migrate to AMM or CpSwap, 0: amm, 1: cpswap,","Neither 0 nor 1: invalid"],"type":"u8"},{"name":"migrate_cpmm_fee_on","docs":["The migrate fee on, 0 means fee on the quote token, 1 means fee on both token","Neither 0 nor 1: invalid"],"type":"u8"},{"name":"supply","docs":["The supply of the token,","0: invalid"],"type":"u64"},{"name":"total_base_sell","docs":["The total base sell of the token","0: invalid"],"type":"u64"},{"name":"total_quote_fund_raising","docs":["The total quote fund raising of the token","0: invalid"],"type":"u64"},{"name":"total_locked_amount","docs":["total amount of tokens to be unlocked","u64::MAX: invalid"],"type":"u64"},{"name":"cliff_period","docs":["Waiting time in seconds before unlocking after fundraising ends","u64::MAX: invalid"],"type":"u64"},{"name":"unlock_period","docs":["Unlocking period in seconds","u64::MAX: invalid"],"type":"u64"}]}},{"name":"ClaimVestedEvent","docs":["Emitted when vesting token claimed by beneficiary"],"type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"beneficiary","type":"pubkey"},{"name":"claim_amount","type":"u64"}]}},{"name":"ConstantCurve","type":{"kind":"struct","fields":[{"name":"supply","type":"u64"},{"name":"total_base_sell","type":"u64"},{"name":"total_quote_fund_raising","type":"u64"},{"name":"migrate_type","type":"u8"}]}},{"name":"CreateVestingEvent","docs":["Emitted when vest_account created"],"type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"beneficiary","type":"pubkey"},{"name":"share_amount","type":"u64"}]}},{"name":"CurveParams","type":{"kind":"enum","variants":[{"name":"Constant","fields":[{"name":"data","type":{"defined":{"name":"ConstantCurve"}}}]},{"name":"Fixed","fields":[{"name":"data","type":{"defined":{"name":"FixedCurve"}}}]},{"name":"Linear","fields":[{"name":"data","type":{"defined":{"name":"LinearCurve"}}}]}]}},{"name":"FixedCurve","type":{"kind":"struct","fields":[{"name":"supply","type":"u64"},{"name":"total_quote_fund_raising","type":"u64"},{"name":"migrate_type","type":"u8"}]}},{"name":"GlobalConfig","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"epoch","docs":["Account update epoch"],"type":"u64"},{"name":"curve_type","docs":["0: Constant Product Curve","1: Fixed Price Curve","2: Linear Price Curve"],"type":"u8"},{"name":"index","docs":["Config index"],"type":"u16"},{"name":"migrate_fee","docs":["The fee of migrate to amm"],"type":"u64"},{"name":"trade_fee_rate","docs":["The trade fee rate, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"max_share_fee_rate","docs":["The maximum share fee rate, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"min_base_supply","docs":["The minimum base supply, the value without decimals"],"type":"u64"},{"name":"max_lock_rate","docs":["The maximum lock rate, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"min_base_sell_rate","docs":["The minimum base sell rate, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"min_base_migrate_rate","docs":["The minimum base migrate rate, denominated in hundredths of a bip (10^-6)"],"type":"u64"},{"name":"min_quote_fund_raising","docs":["The minimum quote fund raising, the value with decimals"],"type":"u64"},{"name":"quote_mint","docs":["Mint information for quote token"],"type":"pubkey"},{"name":"protocol_fee_owner","docs":["Protocol Fee owner"],"type":"pubkey"},{"name":"migrate_fee_owner","docs":["Migrate Fee owner"],"type":"pubkey"},{"name":"migrate_to_amm_wallet","docs":["Migrate to amm control wallet"],"type":"pubkey"},{"name":"migrate_to_cpswap_wallet","docs":["Migrate to cpswap wallet"],"type":"pubkey"},{"name":"requires_platform_auth","docs":["Whether a platform must be explicitly authorized before using this global config"],"type":"u8"},{"name":"padding_alignment","docs":["padding alignment"],"type":{"array":["u8",7]}},{"name":"padding","docs":["padding for future updates"],"type":{"array":["u64",15]}}]}},{"name":"LinearCurve","type":{"kind":"struct","fields":[{"name":"supply","type":"u64"},{"name":"total_quote_fund_raising","type":"u64"},{"name":"migrate_type","type":"u8"}]}},{"name":"MigrateNftInfo","docs":["Represents the parameters for initializing a platform config account(Only support MigrateType::CPSWAP)","# Fields","* `platform_scale` - Scale of the platform liquidity quantity rights will be converted into NFT","* `creator_scale` - Scale of the token creator liquidity quantity rights will be converted into NFT","* `burn_scale` - Scale of liquidity directly to burn","","* platform_scale + creator_scale + burn_scale = RATE_DENOMINATOR_VALUE"],"type":{"kind":"struct","fields":[{"name":"platform_scale","type":"u64"},{"name":"creator_scale","type":"u64"},{"name":"burn_scale","type":"u64"}]}},{"name":"MintParams","docs":["Represents the parameters for initializing a new token mint","# Fields","* `decimals` - Number of decimal places for the token","* `name` - Name of the token","* `symbol` - Symbol/ticker of the token","* `uri` - URI pointing to token metadata"],"type":{"kind":"struct","fields":[{"name":"decimals","type":"u8"},{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"uri","type":"string"}]}},{"name":"PlatformConfig","type":{"kind":"struct","fields":[{"name":"epoch","docs":["The epoch for update interval"],"type":"u64"},{"name":"platform_fee_wallet","docs":["The platform fee wallet"],"type":"pubkey"},{"name":"platform_nft_wallet","docs":["The platform nft wallet to receive the platform NFT after migration if platform_scale is not 0(Only support MigrateType::CPSWAP)"],"type":"pubkey"},{"name":"platform_scale","docs":["Scale of the platform liquidity quantity rights will be converted into NFT(Only support MigrateType::CPSWAP)"],"type":"u64"},{"name":"creator_scale","docs":["Scale of the token creator liquidity quantity rights will be converted into NFT(Only support MigrateType::CPSWAP)"],"type":"u64"},{"name":"burn_scale","docs":["Scale of liquidity directly to burn"],"type":"u64"},{"name":"fee_rate","docs":["The platform fee rate"],"type":"u64"},{"name":"name","docs":["The platform name"],"type":{"array":["u8",64]}},{"name":"web","docs":["The platform website"],"type":{"array":["u8",256]}},{"name":"img","docs":["The platform img link"],"type":{"array":["u8",256]}},{"name":"cpswap_config","docs":["The platform specifies the trade fee rate after migration to cp swap"],"type":"pubkey"},{"name":"creator_fee_rate","docs":["Creator fee rate"],"type":"u64"},{"name":"transfer_fee_extension_auth","docs":["If the base token belongs to token2022, then you can choose to support the transferfeeConfig extension, which includes permissions such as `transfer_fee_config_authority`` and `withdraw_withheld_authority`.","When initializing mint, `withdraw_withheld_authority` and `transfer_fee_config_authority` both belongs to the contract.","Once the token is migrated to AMM, the authorities will be reset to this value"],"type":"pubkey"},{"name":"platform_vesting_wallet","type":"pubkey"},{"name":"platform_vesting_scale","type":"u64"},{"name":"platform_cp_creator","docs":["If a valid platform_cp_creator is configured for the platform,","it will be used as the creator for the AMM pool during migration to cpswap pool."],"type":"pubkey"},{"name":"padding","docs":["padding for future updates"],"type":{"array":["u8",108]}},{"name":"curve_params","docs":["The parameters for launching the pool"],"type":{"vec":{"defined":{"name":"PlatformCurveParam"}}}}]}},{"name":"PlatformConfigInfo","type":{"kind":"struct","fields":[{"name":"fee_wallet","type":"pubkey"},{"name":"nft_wallet","type":"pubkey"},{"name":"migrate_nft_info","type":{"defined":{"name":"MigrateNftInfo"}}},{"name":"fee_rate","type":"u64"},{"name":"name","type":"string"},{"name":"web","type":"string"},{"name":"img","type":"string"},{"name":"transfer_fee_extension_auth","type":"pubkey"},{"name":"creator_fee_rate","type":"u64"},{"name":"platform_vesting_scale","type":"u64"},{"name":"vesting_wallet","type":"pubkey"}]}},{"name":"PlatformConfigParam","type":{"kind":"enum","variants":[{"name":"FeeWallet","fields":["pubkey"]},{"name":"NFTWallet","fields":["pubkey"]},{"name":"MigrateNftInfo","fields":[{"defined":{"name":"MigrateNftInfo"}}]},{"name":"FeeRate","fields":["u64"]},{"name":"Name","fields":["string"]},{"name":"Web","fields":["string"]},{"name":"Img","fields":["string"]},{"name":"CpSwapConfig"},{"name":"AllInfo","fields":[{"defined":{"name":"PlatformConfigInfo"}}]},{"name":"VestingWallet","fields":["pubkey"]},{"name":"PlatformVestingScale","fields":["u64"]},{"name":"PlatformCPCreator","fields":["pubkey"]}]}},{"name":"PlatformCurveParam","type":{"kind":"struct","fields":[{"name":"epoch","docs":["The epoch for update interval, 0 means not update"],"type":"u64"},{"name":"index","docs":["The curve params index"],"type":"u8"},{"name":"global_config","docs":["The global config address"],"type":"pubkey"},{"name":"bonding_curve_param","docs":["bonding curve param"],"type":{"defined":{"name":"BondingCurveParam"}}},{"name":"padding","docs":["padding for future updates"],"type":{"array":["u64",50]}}]}},{"name":"PlatformGlobalAccess","type":{"kind":"struct","fields":[{"name":"bump","type":"u8"},{"name":"global_config","type":"pubkey"},{"name":"platform_config","type":"pubkey"},{"name":"padding","docs":["padding for future updates"],"type":{"array":["u64",8]}}]}},{"name":"PlatformParams","docs":["Represents the parameters for initializing a platform config account","# Fields","* `migrate_nft_info` - The platform configures liquidity info during migration(Only support MigrateType::CPSWAP)","* `fee_rate` - Fee rate of the platform","* `name` - Name of the platform","* `web` - Website of the platform","* `img` - Image link of the platform","/// * `creator_fee_rate` - The fee rate charged by the creator for each transaction."],"type":{"kind":"struct","fields":[{"name":"migrate_nft_info","type":{"defined":{"name":"MigrateNftInfo"}}},{"name":"fee_rate","type":"u64"},{"name":"name","type":"string"},{"name":"web","type":"string"},{"name":"img","type":"string"},{"name":"creator_fee_rate","type":"u64"},{"name":"platform_vesting_scale","type":"u64"}]}},{"name":"PoolCreateEvent","docs":["Emitted when pool created"],"type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"creator","type":"pubkey"},{"name":"config","type":"pubkey"},{"name":"base_mint_param","type":{"defined":{"name":"MintParams"}}},{"name":"curve_param","type":{"defined":{"name":"CurveParams"}}},{"name":"vesting_param","type":{"defined":{"name":"VestingParams"}}},{"name":"amm_fee_on","type":{"defined":{"name":"AmmCreatorFeeOn"}}}]}},{"name":"PoolState","docs":["Represents the state of a trading pool in the protocol","Stores all essential information about pool balances, fees, and configuration"],"type":{"kind":"struct","fields":[{"name":"epoch","docs":["Account update epoch"],"type":"u64"},{"name":"auth_bump","docs":["Bump seed used for PDA address derivation"],"type":"u8"},{"name":"status","docs":["Current status of the pool","* 0: Pool is funding","* 1: Pool funding is end, waiting for migration","* 2: Pool migration is done"],"type":"u8"},{"name":"base_decimals","docs":["Decimals of the pool base token"],"type":"u8"},{"name":"quote_decimals","docs":["Decimals of the pool quote token"],"type":"u8"},{"name":"migrate_type","docs":["Migrate to AMM or CpSwap, 0: amm, 1: cpswap"],"type":"u8"},{"name":"supply","docs":["Supply of the pool base token"],"type":"u64"},{"name":"total_base_sell","docs":["Total sell amount of the base token"],"type":"u64"},{"name":"virtual_base","docs":["For different curves, virtual_base and virtual_quote have different meanings","For constant product curve, virtual_base and virtual_quote are virtual liquidity, virtual_quote/virtual_base is the initial price","For linear price curve, virtual_base is the price slope parameter a, virtual_quote has no effect","For fixed price curve, virtual_quote/virtual_base is the initial price"],"type":"u64"},{"name":"virtual_quote","type":"u64"},{"name":"real_base","docs":["Actual base token amount in the pool","Represents the real tokens available for trading"],"type":"u64"},{"name":"real_quote","docs":["Actual quote token amount in the pool","Represents the real tokens available for trading"],"type":"u64"},{"name":"total_quote_fund_raising","docs":["The total quote fund raising of the pool"],"type":"u64"},{"name":"quote_protocol_fee","docs":["Accumulated trading fees in quote tokens","Can be collected by the protocol fee owner"],"type":"u64"},{"name":"platform_fee","docs":["Accumulated platform fees in quote tokens","Can be collected by the platform wallet stored in platform config"],"type":"u64"},{"name":"migrate_fee","docs":["The fee of migrate to amm"],"type":"u64"},{"name":"vesting_schedule","docs":["Vesting schedule for the base token"],"type":{"defined":{"name":"VestingSchedule"}}},{"name":"global_config","docs":["Public key of the global configuration account","Contains protocol-wide settings this pool adheres to"],"type":"pubkey"},{"name":"platform_config","docs":["Public key of the platform configuration account","Contains platform-wide settings this pool adheres to"],"type":"pubkey"},{"name":"base_mint","docs":["Public key of the base mint address"],"type":"pubkey"},{"name":"quote_mint","docs":["Public key of the quote mint address"],"type":"pubkey"},{"name":"base_vault","docs":["Public key of the base token vault","Holds the actual base tokens owned by the pool"],"type":"pubkey"},{"name":"quote_vault","docs":["Public key of the quote token vault","Holds the actual quote tokens owned by the pool"],"type":"pubkey"},{"name":"creator","docs":["The creator of base token"],"type":"pubkey"},{"name":"token_program_flag","docs":["token program bits","bit0: base token program flag","0: spl_token_program","1: token_program_2022","","bit1: quote token program flag","0: spl_token_program","1: token_program_2022"],"type":"u8"},{"name":"amm_creator_fee_on","docs":["migrate to cpmm, creator fee on quote token or both token"],"type":{"defined":{"name":"AmmCreatorFeeOn"}}},{"name":"platform_vesting_share","docs":["platform vesting token amount"],"type":"u64"},{"name":"padding","docs":["padding for future updates"],"type":{"array":["u8",54]}}]}},{"name":"PoolStatus","docs":["Represents the different states a pool can be in","* Fund - Initial state where pool is accepting funds","* Migrate - Pool funding has ended and waiting for migration","* Trade - Pool migration is complete and amm trading is enabled"],"type":{"kind":"enum","variants":[{"name":"Fund"},{"name":"Migrate"},{"name":"Trade"}]}},{"name":"TradeDirection","docs":["Specifies the direction of a trade in the bonding curve","This is important because curves can treat tokens differently through weights or offsets"],"type":{"kind":"enum","variants":[{"name":"Buy"},{"name":"Sell"}]}},{"name":"TradeEvent","docs":["Emitted when trade process"],"type":{"kind":"struct","fields":[{"name":"pool_state","type":"pubkey"},{"name":"total_base_sell","type":"u64"},{"name":"virtual_base","type":"u64"},{"name":"virtual_quote","type":"u64"},{"name":"real_base_before","type":"u64"},{"name":"real_quote_before","type":"u64"},{"name":"real_base_after","type":"u64"},{"name":"real_quote_after","type":"u64"},{"name":"amount_in","type":"u64"},{"name":"amount_out","type":"u64"},{"name":"protocol_fee","type":"u64"},{"name":"platform_fee","type":"u64"},{"name":"creator_fee","type":"u64"},{"name":"share_fee","type":"u64"},{"name":"trade_direction","type":{"defined":{"name":"TradeDirection"}}},{"name":"pool_status","type":{"defined":{"name":"PoolStatus"}}},{"name":"exact_in","type":"bool"}]}},{"name":"TransferFeeExtensionParams","type":{"kind":"struct","fields":[{"name":"transfer_fee_basis_points","docs":["denominator is 10000, currently, this value cannot exceed 5%, which is 500."],"type":"u16"},{"name":"maximum_fee","docs":["Maximum fee on each transfers, the value must exceed supply * transfer_fee_basis_points / 10000"],"type":"u64"}]}},{"name":"VestingParams","type":{"kind":"struct","fields":[{"name":"total_locked_amount","type":"u64"},{"name":"cliff_period","type":"u64"},{"name":"unlock_period","type":"u64"}]}},{"name":"VestingRecord","type":{"kind":"struct","fields":[{"name":"epoch","docs":["Account update epoch"],"type":"u64"},{"name":"pool","docs":["The pool state account"],"type":"pubkey"},{"name":"beneficiary","docs":["The beneficiary of the vesting account"],"type":"pubkey"},{"name":"claimed_amount","docs":["The amount of tokens claimed"],"type":"u64"},{"name":"token_share_amount","docs":["The share amount of the token to be vested"],"type":"u64"},{"name":"padding","docs":["padding for future updates"],"type":{"array":["u64",8]}}]}},{"name":"VestingSchedule","type":{"kind":"struct","fields":[{"name":"total_locked_amount","type":"u64"},{"name":"cliff_period","type":"u64"},{"name":"unlock_period","type":"u64"},{"name":"start_time","type":"u64"},{"name":"allocated_share_amount","docs":["Total allocated share amount of the base token, not greater than total_locked_amount"],"type":"u64"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/raydium_lock.LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE.json b/migration/khadhroony-bot2-reference/idls/raydium_lock.LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE.json new file mode 100644 index 0000000..eeb48fa --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/raydium_lock.LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"raydium_liquidity_locking","instructions":[{"name":"lockClmmPosition","docs":["Lock an existing clmm's position","","# Arguments","","* `ctx` - The context of accounts","* `with_metadata` - Create NFT with metadata or not",""],"accounts":[{"name":"authority","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true,"docs":["Pay to create account lamports"]},{"name":"positionNftOwner","isMut":false,"isSigner":true,"docs":["The position NFT owner"]},{"name":"feeNftOwner","isMut":false,"isSigner":false},{"name":"positionNftAccount","isMut":true,"isSigner":false,"docs":["The token account for the tokenized position"]},{"name":"personalPosition","isMut":false,"isSigner":false,"docs":["Lock personal position associated with position_nft_account's mint"]},{"name":"positionNftMint","isMut":false,"isSigner":false,"docs":["Position NFT mint to create a ATA `locked_nft_account` belongs to program"]},{"name":"lockedNftAccount","isMut":true,"isSigner":false,"docs":["Program's token account where `position_nft_account` will transfer to"]},{"name":"lockedPosition","isMut":true,"isSigner":false,"docs":["Store the locked information of the personal position"]},{"name":"feeNftMint","isMut":true,"isSigner":true,"docs":["Create a unique fee nft mint"]},{"name":"feeNftAccount","isMut":true,"isSigner":false,"docs":["Token account where fee nft will be minted to"]},{"name":"metadataAccount","isMut":true,"isSigner":false,"docs":["To store metaplex metadata"]},{"name":"metadataProgram","isMut":false,"isSigner":false,"docs":["Program to create NFT metadata account"]},{"name":"associatedTokenProgram","isMut":false,"isSigner":false,"docs":["Program to create an ATA for receiving fee NFT"]},{"name":"rent","isMut":false,"isSigner":false,"docs":["Sysvar for token mint and ATA creation"]},{"name":"feeNftTokenProgram","isMut":false,"isSigner":false,"docs":["Token program fee NFT mint/account belongs to"]},{"name":"lockedNftTokenProgram","isMut":false,"isSigner":false,"docs":["Token program position NFT mint/account belongs to"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["Program to create new account"]}],"args":[{"name":"withMetadata","type":"bool"}]},{"name":"collectClmmFeesAndRewards","docs":["collect clmm locked postion fees and rewards","# Arguments","","* `ctx` - The context of accounts",""],"accounts":[{"name":"authority","isMut":false,"isSigner":false},{"name":"feeNftOwner","isMut":false,"isSigner":true,"docs":["The owner who has fee nft account"]},{"name":"feeNftAccount","isMut":false,"isSigner":false,"docs":["Fee token account"]},{"name":"lockedPosition","isMut":false,"isSigner":false,"docs":["Store the locked the information of position"]},{"name":"clmmProgram","isMut":false,"isSigner":false,"docs":["clmm program"]},{"name":"lockedNftAccount","isMut":true,"isSigner":false,"docs":["Program ATA locked NFT account or user ATA position NFT account"]},{"name":"personalPosition","isMut":true,"isSigner":false,"docs":["Decrease liquidity for this position"]},{"name":"poolState","isMut":true,"isSigner":false},{"name":"protocolPosition","isMut":true,"isSigner":false},{"name":"token0Vault","isMut":true,"isSigner":false,"docs":["The address that holds pool tokens for token_0"]},{"name":"token1Vault","isMut":true,"isSigner":false,"docs":["The address that holds pool tokens for token_1"]},{"name":"tickArrayLower","isMut":true,"isSigner":false,"docs":["Stores init state for the lower tick"]},{"name":"tickArrayUpper","isMut":true,"isSigner":false,"docs":["Stores init state for the upper tick"]},{"name":"recipientToken0Account","isMut":true,"isSigner":false,"docs":["The destination token account for receive amount_0"]},{"name":"recipientToken1Account","isMut":true,"isSigner":false,"docs":["The destination token account for receive amount_1"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["SPL program to transfer out tokens"]},{"name":"tokenProgram2022","isMut":false,"isSigner":false,"docs":["Token program 2022"]},{"name":"memoProgram","isMut":false,"isSigner":false,"docs":["memo program"]},{"name":"vault0Mint","isMut":false,"isSigner":false,"docs":["The mint of token vault 0"]},{"name":"vault1Mint","isMut":false,"isSigner":false,"docs":["The mint of token vault 1"]}],"args":[]},{"name":"lockCpLiquidity","docs":["Lock cpSwap liquidity and mint a nft to collect locked liquidity's fee.","","# Arguments","","* `ctx` - The context of accounts","* `lp_amount` - The lp amount to lock","* `with_metadata` - Create NFT with metadata or not",""],"accounts":[{"name":"authority","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true,"docs":["Pay to create account lamports"]},{"name":"liquidityOwner","isMut":false,"isSigner":true,"docs":["who want to lock liquidity"]},{"name":"feeNftOwner","isMut":false,"isSigner":false},{"name":"feeNftMint","isMut":true,"isSigner":true,"docs":["Create a unique fee nft mint"]},{"name":"feeNftAccount","isMut":true,"isSigner":false,"docs":["Token account where fee nft will be minted to"]},{"name":"poolState","isMut":false,"isSigner":false,"docs":["Indicates which pool the locked liquidity belong to"]},{"name":"lockedLiquidity","isMut":true,"isSigner":false,"docs":["Store the locked information of liquidity"]},{"name":"lpMint","isMut":false,"isSigner":false,"docs":["The mint of liquidity token"]},{"name":"liquidityOwnerLp","isMut":true,"isSigner":false,"docs":["liquidity owner lp token account"]},{"name":"lockedLpVault","isMut":true,"isSigner":false,"docs":["Locked lp token deposit to"]},{"name":"token0Vault","isMut":true,"isSigner":false,"docs":["The address that holds pool tokens for token_0"]},{"name":"token1Vault","isMut":true,"isSigner":false,"docs":["The address that holds pool tokens for token_1"]},{"name":"metadataAccount","isMut":true,"isSigner":false,"docs":["To store metaplex metadata"]},{"name":"rent","isMut":false,"isSigner":false,"docs":["Sysvar for token mint and ATA creation"]},{"name":"systemProgram","isMut":false,"isSigner":false,"docs":["Program to create the new account"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["Program to create/transfer mint/token account"]},{"name":"associatedTokenProgram","isMut":false,"isSigner":false,"docs":["Program to create an ATA for receiving fee NFT"]},{"name":"metadataProgram","isMut":false,"isSigner":false,"docs":["Program to create NFT metadata accunt"]}],"args":[{"name":"lpAmount","type":"u64"},{"name":"withMetadata","type":"bool"}]},{"name":"collectCpFees","docs":["collect cpSwap locked liquidity's fees","# Arguments","","* `ctx` - The context of accounts","* `fee_lp_amount` - The amount lp want to claim",""],"accounts":[{"name":"authority","isMut":false,"isSigner":false},{"name":"feeNftOwner","isMut":false,"isSigner":true,"docs":["Fee nft owner who is allowed to receive fees"]},{"name":"feeNftAccount","isMut":false,"isSigner":false,"docs":["Fee token account"]},{"name":"lockedLiquidity","isMut":true,"isSigner":false,"docs":["Store the locked the information of liquidity"]},{"name":"cpSwapProgram","isMut":false,"isSigner":false,"docs":["cpswap program"]},{"name":"cpAuthority","isMut":false,"isSigner":false},{"name":"poolState","isMut":true,"isSigner":false,"docs":["Pool state account"]},{"name":"lpMint","isMut":true,"isSigner":false,"docs":["lp mint"]},{"name":"recipientToken0Account","isMut":true,"isSigner":false,"docs":["The token account for receive token_0"]},{"name":"recipientToken1Account","isMut":true,"isSigner":false,"docs":["The token account for receive token_1"]},{"name":"token0Vault","isMut":true,"isSigner":false,"docs":["The address that holds pool tokens for token_0"]},{"name":"token1Vault","isMut":true,"isSigner":false,"docs":["The address that holds pool tokens for token_1"]},{"name":"vault0Mint","isMut":false,"isSigner":false,"docs":["The mint of token_0 vault"]},{"name":"vault1Mint","isMut":false,"isSigner":false,"docs":["The mint of token_1 vault"]},{"name":"lockedLpVault","isMut":true,"isSigner":false,"docs":["locked lp token account"]},{"name":"tokenProgram","isMut":false,"isSigner":false,"docs":["token Program"]},{"name":"tokenProgram2022","isMut":false,"isSigner":false,"docs":["Token program 2022"]},{"name":"memoProgram","isMut":false,"isSigner":false,"docs":["memo program"]}],"args":[{"name":"feeLpAmount","type":"u64"}]}],"accounts":[{"name":"LockedCpLiquidityState","type":{"kind":"struct","fields":[{"name":"lockedLpAmount","docs":["The Locked liquidity amount without claimed lp fee"],"type":"u64"},{"name":"claimedLpAmount","docs":["Claimed lp fee amount"],"type":"u64"},{"name":"unclaimedLpAmount","docs":["Unclaimed lp fee amount"],"type":"u64"},{"name":"lastLp","docs":["Last updated cp pool lp total supply"],"type":"u64"},{"name":"lastK","docs":["Last updated cp pool k"],"type":"u128"},{"name":"recentEpoch","docs":["Account update recent epoch"],"type":"u64"},{"name":"poolId","docs":["The ID of the pool with which this record is connected"],"type":"publicKey"},{"name":"feeNftMint","docs":["nft mint to check who has authority to collect fee"],"type":"publicKey"},{"name":"lockedOwner","docs":["The owner who has locked liquidity"],"type":"publicKey"},{"name":"lockedLpMint","docs":["The mint of locked lp token"],"type":"publicKey"},{"name":"padding","docs":["Unused bytes for future upgrades."],"type":{"array":["u64",8]}}]}},{"name":"LockedClmmPositionState","type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":{"array":["u8",1]}},{"name":"positionOwner","docs":["The owner who has locked clmm NFT"],"type":"publicKey"},{"name":"poolId","docs":["The ID of the pool with which this record is connected"],"type":"publicKey"},{"name":"positionId","docs":["The ID of the position with which this record is connected"],"type":"publicKey"},{"name":"lockedNftAccount","docs":["Program ATA locked NFT account or user ATA position NFT account"],"type":"publicKey"},{"name":"feeNftMint","docs":["nft mint to check who has authority to collect fee"],"type":"publicKey"},{"name":"recentEpoch","docs":["account update recent epoch"],"type":"u64"},{"name":"padding","docs":["Unused bytes for future upgrades."],"type":{"array":["u64",8]}}]}}],"events":[{"name":"SettleCpFeeEvent","fields":[{"name":"deltaAmount","type":"u64","index":false},{"name":"unclaimedAmount","type":"u64","index":false},{"name":"lockedAmount","type":"u64","index":false},{"name":"currPoolLp","type":"u64","index":false},{"name":"lastPoolLp","type":"u64","index":false},{"name":"currK","type":"u128","index":false},{"name":"lastK","type":"u128","index":false}]}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/saber_decimal.DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB.json b/migration/khadhroony-bot2-reference/idls/saber_decimal.DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB.json new file mode 100644 index 0000000..e827bc7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/saber_decimal.DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB.json @@ -0,0 +1 @@ +{"version":"0.0.0","name":"add_decimals","instructions":[{"name":"initializeWrapper","accounts":[{"name":"wrapper","isMut":true,"isSigner":false},{"name":"wrapperUnderlyingTokens","isMut":false,"isSigner":false},{"name":"underlyingMint","isMut":false,"isSigner":false},{"name":"wrapperMint","isMut":false,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"deposit","accounts":[{"name":"wrapper","isMut":false,"isSigner":false},{"name":"wrapperMint","isMut":true,"isSigner":false},{"name":"wrapperUnderlyingTokens","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"userUnderlyingTokens","isMut":true,"isSigner":false},{"name":"userWrappedTokens","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"depositAmount","type":"u64"}]},{"name":"withdraw","accounts":[{"name":"wrapper","isMut":false,"isSigner":false},{"name":"wrapperMint","isMut":true,"isSigner":false},{"name":"wrapperUnderlyingTokens","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"userUnderlyingTokens","isMut":true,"isSigner":false},{"name":"userWrappedTokens","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxBurnAmount","type":"u64"}]}],"accounts":[{"name":"WrappedToken","type":{"kind":"struct","fields":[{"name":"decimals","type":"u8"},{"name":"multiplier","type":"u64"},{"name":"wrapperUnderlyingMint","type":"publicKey"},{"name":"wrapperUnderlyingTokens","type":"publicKey"},{"name":"wrapperMint","type":"publicKey"}]}}],"events":[{"name":"InitEvent","fields":[{"name":"payer","type":"publicKey","index":false},{"name":"decimals","type":"u8","index":false},{"name":"multiplier","type":"u64","index":false},{"name":"wrapperUnderlyingMint","type":"publicKey","index":false},{"name":"wrapperUnderlyingTokens","type":"publicKey","index":false},{"name":"wrapperMint","type":"publicKey","index":false}]},{"name":"DepositEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"underlyingMint","type":"publicKey","index":false},{"name":"wrappedMint","type":"publicKey","index":false},{"name":"depositAmount","type":"u64","index":false},{"name":"mintAmount","type":"u64","index":false}]},{"name":"WithdrawEvent","fields":[{"name":"owner","type":"publicKey","index":false},{"name":"underlyingMint","type":"publicKey","index":false},{"name":"wrappedMint","type":"publicKey","index":false},{"name":"withdrawAmount","type":"u64","index":false},{"name":"burnAmount","type":"u64","index":false},{"name":"dustAmount","type":"u64","index":false}]}],"errors":[{"code":300,"name":"InitNonEmptyAccount","msg":"Wrapper underlying tokens account must be empty."},{"code":301,"name":"InitWrapperSupplyNonZero","msg":"Supply of the wrapper mint is non-zero"},{"code":302,"name":"InitWrapperUnderlyingOwnerMismatch","msg":"Owner of the wrapper underlying tokens account must be the wrapper"},{"code":303,"name":"InitWrapperUnderlyingMintMismatch","msg":"Underlying mint does not match underlying tokens account mint"},{"code":304,"name":"InitMintAuthorityMismatch","msg":"Mint authority mismatch"},{"code":305,"name":"InitMultiplierOverflow","msg":"Initial decimals too high"},{"code":306,"name":"InitWrapperDecimalsTooLow","msg":"The number of target decimals must be greater than or equal to the underlying asset's decimals."},{"code":307,"name":"MintAmountOverflow","msg":"Mint amount overflow. This error happens when the token cannot support this many decimals added to the token."},{"code":308,"name":"InvalidBurnAmount","msg":"Failed to convert burn amount from withdraw amount."},{"code":309,"name":"InvalidWithdrawAmount","msg":"Failed to convert withdraw amount from wrapped amount."},{"code":310,"name":"InsufficientUnderlyingBalance","msg":"User does not have enough underlying tokens"},{"code":311,"name":"InsufficientWrappedBalance","msg":"User does not have enough wrapped tokens"},{"code":312,"name":"ZeroAmount","msg":"Cannot send zero tokens"},{"code":313,"name":"UnknownAction","msg":"Unknown router action"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/squads.SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf.json b/migration/khadhroony-bot2-reference/idls/squads.SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf.json new file mode 100644 index 0000000..92bcbac --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/squads.SQDS4ep65T869zMMBKyuUq6aD6EgTu8psMjkvj52pCf.json @@ -0,0 +1 @@ +{"version":"2.0.0","name":"squads_multisig_program","instructions":[{"name":"programConfigInit","docs":["Initialize the program config."],"accounts":[{"name":"programConfig","isMut":true,"isSigner":false},{"name":"initializer","isMut":true,"isSigner":true,"docs":["The hard-coded account that is used to initialize the program config once."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ProgramConfigInitArgs"}}]},{"name":"programConfigSetAuthority","docs":["Set the `authority` parameter of the program config."],"accounts":[{"name":"programConfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"ProgramConfigSetAuthorityArgs"}}]},{"name":"programConfigSetMultisigCreationFee","docs":["Set the `multisig_creation_fee` parameter of the program config."],"accounts":[{"name":"programConfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"ProgramConfigSetMultisigCreationFeeArgs"}}]},{"name":"programConfigSetTreasury","docs":["Set the `treasury` parameter of the program config."],"accounts":[{"name":"programConfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"ProgramConfigSetTreasuryArgs"}}]},{"name":"multisigCreate","docs":["Create a multisig."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"createKey","isMut":false,"isSigner":true,"docs":["An ephemeral signer that is used as a seed for the Multisig PDA.","Must be a signer to prevent front-running attack by someone else but the original creator."]},{"name":"creator","isMut":true,"isSigner":true,"docs":["The creator of the multisig."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MultisigCreateArgs"}}]},{"name":"multisigCreateV2","docs":["Create a multisig."],"accounts":[{"name":"programConfig","isMut":false,"isSigner":false,"docs":["Global program config account."]},{"name":"treasury","isMut":true,"isSigner":false,"docs":["The treasury where the creation fee is transferred to."]},{"name":"multisig","isMut":true,"isSigner":false},{"name":"createKey","isMut":false,"isSigner":true,"docs":["An ephemeral signer that is used as a seed for the Multisig PDA.","Must be a signer to prevent front-running attack by someone else but the original creator."]},{"name":"creator","isMut":true,"isSigner":true,"docs":["The creator of the multisig."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MultisigCreateArgsV2"}}]},{"name":"multisigAddMember","docs":["Add a new member to the controlled multisig."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged or credited in case the multisig account needs to reallocate space,","for example when adding a new member or a spending limit.","This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[{"name":"args","type":{"defined":"MultisigAddMemberArgs"}}]},{"name":"multisigRemoveMember","docs":["Remove a member/key from the controlled multisig."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged or credited in case the multisig account needs to reallocate space,","for example when adding a new member or a spending limit.","This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[{"name":"args","type":{"defined":"MultisigRemoveMemberArgs"}}]},{"name":"multisigSetTimeLock","docs":["Set the `time_lock` config parameter for the controlled multisig."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged or credited in case the multisig account needs to reallocate space,","for example when adding a new member or a spending limit.","This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[{"name":"args","type":{"defined":"MultisigSetTimeLockArgs"}}]},{"name":"multisigChangeThreshold","docs":["Set the `threshold` config parameter for the controlled multisig."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged or credited in case the multisig account needs to reallocate space,","for example when adding a new member or a spending limit.","This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[{"name":"args","type":{"defined":"MultisigChangeThresholdArgs"}}]},{"name":"multisigSetConfigAuthority","docs":["Set the multisig `config_authority`."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged or credited in case the multisig account needs to reallocate space,","for example when adding a new member or a spending limit.","This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[{"name":"args","type":{"defined":"MultisigSetConfigAuthorityArgs"}}]},{"name":"multisigSetRentCollector","docs":["Set the multisig `rent_collector`."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged or credited in case the multisig account needs to reallocate space,","for example when adding a new member or a spending limit.","This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[{"name":"args","type":{"defined":"MultisigSetRentCollectorArgs"}}]},{"name":"multisigAddSpendingLimit","docs":["Create a new spending limit for the controlled multisig."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"spendingLimit","isMut":true,"isSigner":false},{"name":"rentPayer","isMut":true,"isSigner":true,"docs":["This is usually the same as `config_authority`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"MultisigAddSpendingLimitArgs"}}]},{"name":"multisigRemoveSpendingLimit","docs":["Remove the spending limit from the controlled multisig."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"configAuthority","isMut":false,"isSigner":true,"docs":["Multisig `config_authority` that must authorize the configuration change."]},{"name":"spendingLimit","isMut":true,"isSigner":false},{"name":"rentCollector","isMut":true,"isSigner":false,"docs":["This is usually the same as `config_authority`, but can be a different account if needed."]}],"args":[{"name":"args","type":{"defined":"MultisigRemoveSpendingLimitArgs"}}]},{"name":"configTransactionCreate","docs":["Create a new config transaction."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false},{"name":"creator","isMut":false,"isSigner":true,"docs":["The member of the multisig that is creating the transaction."]},{"name":"rentPayer","isMut":true,"isSigner":true,"docs":["The payer for the transaction account rent."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ConfigTransactionCreateArgs"}}]},{"name":"configTransactionExecute","docs":["Execute a config transaction.","The transaction must be `Approved`."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false,"docs":["The multisig account that owns the transaction."]},{"name":"member","isMut":false,"isSigner":true,"docs":["One of the multisig members with `Execute` permission."]},{"name":"proposal","isMut":true,"isSigner":false,"docs":["The proposal account associated with the transaction."]},{"name":"transaction","isMut":false,"isSigner":false,"docs":["The transaction to execute."]},{"name":"rentPayer","isMut":true,"isSigner":true,"isOptional":true,"docs":["The account that will be charged/credited in case the config transaction causes space reallocation,","for example when adding a new member, adding or removing a spending limit.","This is usually the same as `member`, but can be a different account if needed."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["We might need it in case reallocation is needed."]}],"args":[]},{"name":"vaultTransactionCreate","docs":["Create a new vault transaction."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false},{"name":"creator","isMut":false,"isSigner":true,"docs":["The member of the multisig that is creating the transaction."]},{"name":"rentPayer","isMut":true,"isSigner":true,"docs":["The payer for the transaction account rent."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"VaultTransactionCreateArgs"}}]},{"name":"vaultTransactionExecute","docs":["Execute a vault transaction.","The transaction must be `Approved`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false,"docs":["The proposal account associated with the transaction."]},{"name":"transaction","isMut":false,"isSigner":false,"docs":["The transaction to execute."]},{"name":"member","isMut":false,"isSigner":true}],"args":[]},{"name":"batchCreate","docs":["Create a new batch."],"accounts":[{"name":"multisig","isMut":true,"isSigner":false},{"name":"batch","isMut":true,"isSigner":false},{"name":"creator","isMut":false,"isSigner":true,"docs":["The member of the multisig that is creating the batch."]},{"name":"rentPayer","isMut":true,"isSigner":true,"docs":["The payer for the batch account rent."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"BatchCreateArgs"}}]},{"name":"batchAddTransaction","docs":["Add a transaction to the batch."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false,"docs":["Multisig account this batch belongs to."]},{"name":"proposal","isMut":false,"isSigner":false,"docs":["The proposal account associated with the batch."]},{"name":"batch","isMut":true,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false,"docs":["`VaultBatchTransaction` account to initialize and add to the `batch`."]},{"name":"member","isMut":false,"isSigner":true,"docs":["Member of the multisig."]},{"name":"rentPayer","isMut":true,"isSigner":true,"docs":["The payer for the batch transaction account rent."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"BatchAddTransactionArgs"}}]},{"name":"batchExecuteTransaction","docs":["Execute a transaction from the batch."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false,"docs":["Multisig account this batch belongs to."]},{"name":"member","isMut":false,"isSigner":true,"docs":["Member of the multisig."]},{"name":"proposal","isMut":true,"isSigner":false,"docs":["The proposal account associated with the batch.","If `transaction` is the last in the batch, the `proposal` status will be set to `Executed`."]},{"name":"batch","isMut":true,"isSigner":false},{"name":"transaction","isMut":false,"isSigner":false,"docs":["Batch transaction to execute."]}],"args":[]},{"name":"proposalCreate","docs":["Create a new multisig proposal."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false},{"name":"creator","isMut":false,"isSigner":true,"docs":["The member of the multisig that is creating the proposal."]},{"name":"rentPayer","isMut":true,"isSigner":true,"docs":["The payer for the proposal account rent."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ProposalCreateArgs"}}]},{"name":"proposalActivate","docs":["Update status of a multisig proposal from `Draft` to `Active`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"member","isMut":true,"isSigner":true},{"name":"proposal","isMut":true,"isSigner":false}],"args":[]},{"name":"proposalApprove","docs":["Approve a multisig proposal on behalf of the `member`.","The proposal must be `Active`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"member","isMut":true,"isSigner":true},{"name":"proposal","isMut":true,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ProposalVoteArgs"}}]},{"name":"proposalReject","docs":["Reject a multisig proposal on behalf of the `member`.","The proposal must be `Active`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"member","isMut":true,"isSigner":true},{"name":"proposal","isMut":true,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ProposalVoteArgs"}}]},{"name":"proposalCancel","docs":["Cancel a multisig proposal on behalf of the `member`.","The proposal must be `Approved`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"member","isMut":true,"isSigner":true},{"name":"proposal","isMut":true,"isSigner":false}],"args":[{"name":"args","type":{"defined":"ProposalVoteArgs"}}]},{"name":"spendingLimitUse","docs":["Use a spending limit to transfer tokens from a multisig vault to a destination account."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false,"docs":["The multisig account the `spending_limit` is for."]},{"name":"member","isMut":false,"isSigner":true},{"name":"spendingLimit","isMut":true,"isSigner":false,"docs":["The SpendingLimit account to use."]},{"name":"vault","isMut":true,"isSigner":false,"docs":["Multisig vault account to transfer tokens from."]},{"name":"destination","isMut":true,"isSigner":false,"docs":["Destination account to transfer tokens to."]},{"name":"systemProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["In case `spending_limit.mint` is SOL."]},{"name":"mint","isMut":false,"isSigner":false,"isOptional":true,"docs":["The mint of the tokens to transfer in case `spending_limit.mint` is an SPL token."]},{"name":"vaultTokenAccount","isMut":true,"isSigner":false,"isOptional":true,"docs":["Multisig vault token account to transfer tokens from in case `spending_limit.mint` is an SPL token."]},{"name":"destinationTokenAccount","isMut":true,"isSigner":false,"isOptional":true,"docs":["Destination token account in case `spending_limit.mint` is an SPL token."]},{"name":"tokenProgram","isMut":false,"isSigner":false,"isOptional":true,"docs":["In case `spending_limit.mint` is an SPL token."]}],"args":[{"name":"args","type":{"defined":"SpendingLimitUseArgs"}}]},{"name":"configTransactionAccountsClose","docs":["Closes a `ConfigTransaction` and the corresponding `Proposal`.","`transaction` can be closed if either:","- the `proposal` is in a terminal state: `Executed`, `Rejected`, or `Cancelled`.","- the `proposal` is stale."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false,"docs":["ConfigTransaction corresponding to the `proposal`."]},{"name":"rentCollector","isMut":true,"isSigner":false,"docs":["The rent collector."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"vaultTransactionAccountsClose","docs":["Closes a `VaultTransaction` and the corresponding `Proposal`.","`transaction` can be closed if either:","- the `proposal` is in a terminal state: `Executed`, `Rejected`, or `Cancelled`.","- the `proposal` is stale and not `Approved`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false},{"name":"transaction","isMut":true,"isSigner":false,"docs":["VaultTransaction corresponding to the `proposal`."]},{"name":"rentCollector","isMut":true,"isSigner":false,"docs":["The rent collector."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"vaultBatchTransactionAccountClose","docs":["Closes a `VaultBatchTransaction` belonging to the `batch` and `proposal`.","`transaction` can be closed if either:","- it's marked as executed within the `batch`;","- the `proposal` is in a terminal state: `Executed`, `Rejected`, or `Cancelled`.","- the `proposal` is stale and not `Approved`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"proposal","isMut":false,"isSigner":false},{"name":"batch","isMut":true,"isSigner":false,"docs":["`Batch` corresponding to the `proposal`."]},{"name":"transaction","isMut":true,"isSigner":false,"docs":["`VaultBatchTransaction` account to close.","The transaction must be the current last one in the batch."]},{"name":"rentCollector","isMut":true,"isSigner":false,"docs":["The rent collector."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"batchAccountsClose","docs":["Closes Batch and the corresponding Proposal accounts for proposals in terminal states:","`Executed`, `Rejected`, or `Cancelled` or stale proposals that aren't `Approved`.","","This instruction is only allowed to be executed when all `VaultBatchTransaction` accounts","in the `batch` are already closed: `batch.size == 0`."],"accounts":[{"name":"multisig","isMut":false,"isSigner":false},{"name":"proposal","isMut":true,"isSigner":false},{"name":"batch","isMut":true,"isSigner":false,"docs":["`Batch` corresponding to the `proposal`."]},{"name":"rentCollector","isMut":true,"isSigner":false,"docs":["The rent collector."]},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]}],"accounts":[{"name":"Batch","docs":["Stores data required for serial execution of a batch of multisig vault transactions.","Vault transaction is a transaction that's executed on behalf of the multisig vault PDA","and wraps arbitrary Solana instructions, typically calling into other Solana programs.","The transactions themselves are stored in separate PDAs associated with the this account."],"type":{"kind":"struct","fields":[{"name":"multisig","docs":["The multisig this belongs to."],"type":"publicKey"},{"name":"creator","docs":["Member of the Multisig who submitted the batch."],"type":"publicKey"},{"name":"index","docs":["Index of this batch within the multisig transactions."],"type":"u64"},{"name":"bump","docs":["PDA bump."],"type":"u8"},{"name":"vaultIndex","docs":["Index of the vault this batch belongs to."],"type":"u8"},{"name":"vaultBump","docs":["Derivation bump of the vault PDA this batch belongs to."],"type":"u8"},{"name":"size","docs":["Number of transactions in the batch."],"type":"u32"},{"name":"executedTransactionIndex","docs":["Index of the last executed transaction within the batch.","0 means that no transactions have been executed yet."],"type":"u32"}]}},{"name":"VaultBatchTransaction","docs":["Stores data required for execution of one transaction from a batch."],"type":{"kind":"struct","fields":[{"name":"bump","docs":["PDA bump."],"type":"u8"},{"name":"ephemeralSignerBumps","docs":["Derivation bumps for additional signers.","Some transactions require multiple signers. Often these additional signers are \"ephemeral\" keypairs","that are generated on the client with a sole purpose of signing the transaction and be discarded immediately after.","When wrapping such transactions into multisig ones, we replace these \"ephemeral\" signing keypairs","with PDAs derived from the transaction's `transaction_index` and controlled by the Multisig Program;","during execution the program includes the seeds of these PDAs into the `invoke_signed` calls,","thus \"signing\" on behalf of these PDAs."],"type":"bytes"},{"name":"message","docs":["data required for executing the transaction."],"type":{"defined":"VaultTransactionMessage"}}]}},{"name":"ConfigTransaction","docs":["Stores data required for execution of a multisig configuration transaction.","Config transaction can perform a predefined set of actions on the Multisig PDA, such as adding/removing members,","changing the threshold, etc."],"type":{"kind":"struct","fields":[{"name":"multisig","docs":["The multisig this belongs to."],"type":"publicKey"},{"name":"creator","docs":["Member of the Multisig who submitted the transaction."],"type":"publicKey"},{"name":"index","docs":["Index of this transaction within the multisig."],"type":"u64"},{"name":"bump","docs":["bump for the transaction seeds."],"type":"u8"},{"name":"actions","docs":["Action to be performed on the multisig."],"type":{"vec":{"defined":"ConfigAction"}}}]}},{"name":"Multisig","type":{"kind":"struct","fields":[{"name":"createKey","docs":["Key that is used to seed the multisig PDA."],"type":"publicKey"},{"name":"configAuthority","docs":["The authority that can change the multisig config.","This is a very important parameter as this authority can change the members and threshold.","","The convention is to set this to `Pubkey::default()`.","In this case, the multisig becomes autonomous, so every config change goes through","the normal process of voting by the members.","","However, if this parameter is set to any other key, all the config changes for this multisig","will need to be signed by the `config_authority`. We call such a multisig a \"controlled multisig\"."],"type":"publicKey"},{"name":"threshold","docs":["Threshold for signatures."],"type":"u16"},{"name":"timeLock","docs":["How many seconds must pass between transaction voting settlement and execution."],"type":"u32"},{"name":"transactionIndex","docs":["Last transaction index. 0 means no transactions have been created."],"type":"u64"},{"name":"staleTransactionIndex","docs":["Last stale transaction index. All transactions up until this index are stale.","This index is updated when multisig config (members/threshold/time_lock) changes."],"type":"u64"},{"name":"rentCollector","docs":["The address where the rent for the accounts related to executed, rejected, or cancelled","transactions can be reclaimed. If set to `None`, the rent reclamation feature is turned off."],"type":{"option":"publicKey"}},{"name":"bump","docs":["Bump for the multisig PDA seed."],"type":"u8"},{"name":"members","docs":["Members of the multisig."],"type":{"vec":{"defined":"Member"}}}]}},{"name":"ProgramConfig","docs":["Global program configuration account."],"type":{"kind":"struct","fields":[{"name":"authority","docs":["The authority which can update the config."],"type":"publicKey"},{"name":"multisigCreationFee","docs":["The lamports amount charged for creating a new multisig account.","This fee is sent to the `treasury` account."],"type":"u64"},{"name":"treasury","docs":["The treasury account to send charged fees to."],"type":"publicKey"},{"name":"reserved","docs":["Reserved for future use."],"type":{"array":["u8",64]}}]}},{"name":"Proposal","docs":["Stores the data required for tracking the status of a multisig proposal.","Each `Proposal` has a 1:1 association with a transaction account, e.g. a `VaultTransaction` or a `ConfigTransaction`;","the latter can be executed only after the `Proposal` has been approved and its time lock is released."],"type":{"kind":"struct","fields":[{"name":"multisig","docs":["The multisig this belongs to."],"type":"publicKey"},{"name":"transactionIndex","docs":["Index of the multisig transaction this proposal is associated with."],"type":"u64"},{"name":"status","docs":["The status of the transaction."],"type":{"defined":"ProposalStatus"}},{"name":"bump","docs":["PDA bump."],"type":"u8"},{"name":"approved","docs":["Keys that have approved/signed."],"type":{"vec":"publicKey"}},{"name":"rejected","docs":["Keys that have rejected."],"type":{"vec":"publicKey"}},{"name":"cancelled","docs":["Keys that have cancelled (Approved only)."],"type":{"vec":"publicKey"}}]}},{"name":"SpendingLimit","type":{"kind":"struct","fields":[{"name":"multisig","docs":["The multisig this belongs to."],"type":"publicKey"},{"name":"createKey","docs":["Key that is used to seed the SpendingLimit PDA."],"type":"publicKey"},{"name":"vaultIndex","docs":["The index of the vault that the spending limit is for."],"type":"u8"},{"name":"mint","docs":["The token mint the spending limit is for.","Pubkey::default() means SOL.","use NATIVE_MINT for Wrapped SOL."],"type":"publicKey"},{"name":"amount","docs":["The amount of tokens that can be spent in a period.","This amount is in decimals of the mint,","so 1 SOL would be `1_000_000_000` and 1 USDC would be `1_000_000`."],"type":"u64"},{"name":"period","docs":["The reset period of the spending limit.","When it passes, the remaining amount is reset, unless it's `Period::OneTime`."],"type":{"defined":"Period"}},{"name":"remainingAmount","docs":["The remaining amount of tokens that can be spent in the current period.","When reaches 0, the spending limit cannot be used anymore until the period reset."],"type":"u64"},{"name":"lastReset","docs":["Unix timestamp marking the last time the spending limit was reset (or created)."],"type":"i64"},{"name":"bump","docs":["PDA bump."],"type":"u8"},{"name":"members","docs":["Members of the multisig that can use the spending limit.","In case a member is removed from the multisig, the spending limit will remain existent","(until explicitly deleted), but the removed member will not be able to use it anymore."],"type":{"vec":"publicKey"}},{"name":"destinations","docs":["The destination addresses the spending limit is allowed to sent funds to.","If empty, funds can be sent to any address."],"type":{"vec":"publicKey"}}]}},{"name":"VaultTransaction","docs":["Stores data required for tracking the voting and execution status of a vault transaction.","Vault transaction is a transaction that's executed on behalf of the multisig vault PDA","and wraps arbitrary Solana instructions, typically calling into other Solana programs."],"type":{"kind":"struct","fields":[{"name":"multisig","docs":["The multisig this belongs to."],"type":"publicKey"},{"name":"creator","docs":["Member of the Multisig who submitted the transaction."],"type":"publicKey"},{"name":"index","docs":["Index of this transaction within the multisig."],"type":"u64"},{"name":"bump","docs":["bump for the transaction seeds."],"type":"u8"},{"name":"vaultIndex","docs":["Index of the vault this transaction belongs to."],"type":"u8"},{"name":"vaultBump","docs":["Derivation bump of the vault PDA this transaction belongs to."],"type":"u8"},{"name":"ephemeralSignerBumps","docs":["Derivation bumps for additional signers.","Some transactions require multiple signers. Often these additional signers are \"ephemeral\" keypairs","that are generated on the client with a sole purpose of signing the transaction and be discarded immediately after.","When wrapping such transactions into multisig ones, we replace these \"ephemeral\" signing keypairs","with PDAs derived from the MultisigTransaction's `transaction_index` and controlled by the Multisig Program;","during execution the program includes the seeds of these PDAs into the `invoke_signed` calls,","thus \"signing\" on behalf of these PDAs."],"type":"bytes"},{"name":"message","docs":["data required for executing the transaction."],"type":{"defined":"VaultTransactionMessage"}}]}}],"types":[{"name":"BatchAddTransactionArgs","type":{"kind":"struct","fields":[{"name":"ephemeralSigners","docs":["Number of ephemeral signing PDAs required by the transaction."],"type":"u8"},{"name":"transactionMessage","type":"bytes"}]}},{"name":"BatchCreateArgs","type":{"kind":"struct","fields":[{"name":"vaultIndex","docs":["Index of the vault this transaction belongs to."],"type":"u8"},{"name":"memo","type":{"option":"string"}}]}},{"name":"ConfigTransactionCreateArgs","type":{"kind":"struct","fields":[{"name":"actions","type":{"vec":{"defined":"ConfigAction"}}},{"name":"memo","type":{"option":"string"}}]}},{"name":"MultisigAddSpendingLimitArgs","type":{"kind":"struct","fields":[{"name":"createKey","docs":["Key that is used to seed the SpendingLimit PDA."],"type":"publicKey"},{"name":"vaultIndex","docs":["The index of the vault that the spending limit is for."],"type":"u8"},{"name":"mint","docs":["The token mint the spending limit is for."],"type":"publicKey"},{"name":"amount","docs":["The amount of tokens that can be spent in a period.","This amount is in decimals of the mint,","so 1 SOL would be `1_000_000_000` and 1 USDC would be `1_000_000`."],"type":"u64"},{"name":"period","docs":["The reset period of the spending limit.","When it passes, the remaining amount is reset, unless it's `Period::OneTime`."],"type":{"defined":"Period"}},{"name":"members","docs":["Members of the multisig that can use the spending limit.","In case a member is removed from the multisig, the spending limit will remain existent","(until explicitly deleted), but the removed member will not be able to use it anymore."],"type":{"vec":"publicKey"}},{"name":"destinations","docs":["The destination addresses the spending limit is allowed to sent funds to.","If empty, funds can be sent to any address."],"type":{"vec":"publicKey"}},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigAddMemberArgs","type":{"kind":"struct","fields":[{"name":"newMember","type":{"defined":"Member"}},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigRemoveMemberArgs","type":{"kind":"struct","fields":[{"name":"oldMember","type":"publicKey"},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigChangeThresholdArgs","type":{"kind":"struct","fields":[{"name":"newThreshold","type":"u16"},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigSetTimeLockArgs","type":{"kind":"struct","fields":[{"name":"timeLock","type":"u32"},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigSetConfigAuthorityArgs","type":{"kind":"struct","fields":[{"name":"configAuthority","type":"publicKey"},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigSetRentCollectorArgs","type":{"kind":"struct","fields":[{"name":"rentCollector","type":{"option":"publicKey"}},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigCreateArgs","type":{"kind":"struct","fields":[{"name":"configAuthority","docs":["The authority that can configure the multisig: add/remove members, change the threshold, etc.","Should be set to `None` for autonomous multisigs."],"type":{"option":"publicKey"}},{"name":"threshold","docs":["The number of signatures required to execute a transaction."],"type":"u16"},{"name":"members","docs":["The members of the multisig."],"type":{"vec":{"defined":"Member"}}},{"name":"timeLock","docs":["How many seconds must pass between transaction voting, settlement, and execution."],"type":"u32"},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigCreateArgsV2","type":{"kind":"struct","fields":[{"name":"configAuthority","docs":["The authority that can configure the multisig: add/remove members, change the threshold, etc.","Should be set to `None` for autonomous multisigs."],"type":{"option":"publicKey"}},{"name":"threshold","docs":["The number of signatures required to execute a transaction."],"type":"u16"},{"name":"members","docs":["The members of the multisig."],"type":{"vec":{"defined":"Member"}}},{"name":"timeLock","docs":["How many seconds must pass between transaction voting, settlement, and execution."],"type":"u32"},{"name":"rentCollector","docs":["The address where the rent for the accounts related to executed, rejected, or cancelled","transactions can be reclaimed. If set to `None`, the rent reclamation feature is turned off."],"type":{"option":"publicKey"}},{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"MultisigRemoveSpendingLimitArgs","type":{"kind":"struct","fields":[{"name":"memo","docs":["Memo is used for indexing only."],"type":{"option":"string"}}]}},{"name":"ProgramConfigInitArgs","type":{"kind":"struct","fields":[{"name":"authority","docs":["The authority that can configure the program config: change the treasury, etc."],"type":"publicKey"},{"name":"multisigCreationFee","docs":["The fee that is charged for creating a new multisig."],"type":"u64"},{"name":"treasury","docs":["The treasury where the creation fee is transferred to."],"type":"publicKey"}]}},{"name":"ProgramConfigSetAuthorityArgs","type":{"kind":"struct","fields":[{"name":"newAuthority","type":"publicKey"}]}},{"name":"ProgramConfigSetMultisigCreationFeeArgs","type":{"kind":"struct","fields":[{"name":"newMultisigCreationFee","type":"u64"}]}},{"name":"ProgramConfigSetTreasuryArgs","type":{"kind":"struct","fields":[{"name":"newTreasury","type":"publicKey"}]}},{"name":"ProposalCreateArgs","type":{"kind":"struct","fields":[{"name":"transactionIndex","docs":["Index of the multisig transaction this proposal is associated with."],"type":"u64"},{"name":"draft","docs":["Whether the proposal should be initialized with status `Draft`."],"type":"bool"}]}},{"name":"ProposalVoteArgs","type":{"kind":"struct","fields":[{"name":"memo","type":{"option":"string"}}]}},{"name":"SpendingLimitUseArgs","type":{"kind":"struct","fields":[{"name":"amount","docs":["Amount of tokens to transfer."],"type":"u64"},{"name":"decimals","docs":["Decimals of the token mint. Used for double-checking against incorrect order of magnitude of `amount`."],"type":"u8"},{"name":"memo","docs":["Memo used for indexing."],"type":{"option":"string"}}]}},{"name":"VaultTransactionCreateArgs","type":{"kind":"struct","fields":[{"name":"vaultIndex","docs":["Index of the vault this transaction belongs to."],"type":"u8"},{"name":"ephemeralSigners","docs":["Number of ephemeral signing PDAs required by the transaction."],"type":"u8"},{"name":"transactionMessage","type":"bytes"},{"name":"memo","type":{"option":"string"}}]}},{"name":"TransactionMessage","docs":["Unvalidated instruction data, must be treated as untrusted."],"type":{"kind":"struct","fields":[{"name":"numSigners","docs":["The number of signer pubkeys in the account_keys vec."],"type":"u8"},{"name":"numWritableSigners","docs":["The number of writable signer pubkeys in the account_keys vec."],"type":"u8"},{"name":"numWritableNonSigners","docs":["The number of writable non-signer pubkeys in the account_keys vec."],"type":"u8"},{"name":"accountKeys","docs":["The list of unique account public keys (including program IDs) that will be used in the provided instructions."],"type":{"defined":"SmallVec"}},{"name":"instructions","docs":["The list of instructions to execute."],"type":{"defined":"SmallVec"}},{"name":"addressTableLookups","docs":["List of address table lookups used to load additional accounts","for this transaction."],"type":{"defined":"SmallVec"}}]}},{"name":"CompiledInstruction","type":{"kind":"struct","fields":[{"name":"programIdIndex","type":"u8"},{"name":"accountIndexes","docs":["Indices into the tx's `account_keys` list indicating which accounts to pass to the instruction."],"type":{"defined":"SmallVec"}},{"name":"data","docs":["Instruction data."],"type":{"defined":"SmallVec"}}]}},{"name":"MessageAddressTableLookup","docs":["Address table lookups describe an on-chain address lookup table to use","for loading more readonly and writable accounts in a single tx."],"type":{"kind":"struct","fields":[{"name":"accountKey","docs":["Address lookup table account key"],"type":"publicKey"},{"name":"writableIndexes","docs":["List of indexes used to load writable account addresses"],"type":{"defined":"SmallVec"}},{"name":"readonlyIndexes","docs":["List of indexes used to load readonly account addresses"],"type":{"defined":"SmallVec"}}]}},{"name":"Member","type":{"kind":"struct","fields":[{"name":"key","type":"publicKey"},{"name":"permissions","type":{"defined":"Permissions"}}]}},{"name":"Permissions","docs":["Bitmask for permissions."],"type":{"kind":"struct","fields":[{"name":"mask","type":"u8"}]}},{"name":"VaultTransactionMessage","type":{"kind":"struct","fields":[{"name":"numSigners","docs":["The number of signer pubkeys in the account_keys vec."],"type":"u8"},{"name":"numWritableSigners","docs":["The number of writable signer pubkeys in the account_keys vec."],"type":"u8"},{"name":"numWritableNonSigners","docs":["The number of writable non-signer pubkeys in the account_keys vec."],"type":"u8"},{"name":"accountKeys","docs":["Unique account pubkeys (including program IDs) required for execution of the tx.","The signer pubkeys appear at the beginning of the vec, with writable pubkeys first, and read-only pubkeys following.","The non-signer pubkeys follow with writable pubkeys first and read-only ones following.","Program IDs are also stored at the end of the vec along with other non-signer non-writable pubkeys:","","```plaintext","[pubkey1, pubkey2, pubkey3, pubkey4, pubkey5, pubkey6, pubkey7, pubkey8]","|---writable---| |---readonly---| |---writable---| |---readonly---|","|------------signers-------------| |----------non-singers-----------|","```"],"type":{"vec":"publicKey"}},{"name":"instructions","docs":["List of instructions making up the tx."],"type":{"vec":{"defined":"MultisigCompiledInstruction"}}},{"name":"addressTableLookups","docs":["List of address table lookups used to load additional accounts","for this transaction."],"type":{"vec":{"defined":"MultisigMessageAddressTableLookup"}}}]}},{"name":"MultisigCompiledInstruction","docs":["Concise serialization schema for instructions that make up a transaction.","Closely mimics the Solana transaction wire format."],"type":{"kind":"struct","fields":[{"name":"programIdIndex","type":"u8"},{"name":"accountIndexes","docs":["Indices into the tx's `account_keys` list indicating which accounts to pass to the instruction."],"type":"bytes"},{"name":"data","docs":["Instruction data."],"type":"bytes"}]}},{"name":"MultisigMessageAddressTableLookup","docs":["Address table lookups describe an on-chain address lookup table to use","for loading more readonly and writable accounts into a transaction."],"type":{"kind":"struct","fields":[{"name":"accountKey","docs":["Address lookup table account key."],"type":"publicKey"},{"name":"writableIndexes","docs":["List of indexes used to load writable accounts."],"type":"bytes"},{"name":"readonlyIndexes","docs":["List of indexes used to load readonly accounts."],"type":"bytes"}]}},{"name":"Vote","type":{"kind":"enum","variants":[{"name":"Approve"},{"name":"Reject"},{"name":"Cancel"}]}},{"name":"ConfigAction","type":{"kind":"enum","variants":[{"name":"AddMember","fields":[{"name":"newMember","type":{"defined":"Member"}}]},{"name":"RemoveMember","fields":[{"name":"oldMember","type":"publicKey"}]},{"name":"ChangeThreshold","fields":[{"name":"newThreshold","type":"u16"}]},{"name":"SetTimeLock","fields":[{"name":"newTimeLock","type":"u32"}]},{"name":"AddSpendingLimit","fields":[{"name":"createKey","docs":["Key that is used to seed the SpendingLimit PDA."],"type":"publicKey"},{"name":"vaultIndex","docs":["The index of the vault that the spending limit is for."],"type":"u8"},{"name":"mint","docs":["The token mint the spending limit is for."],"type":"publicKey"},{"name":"amount","docs":["The amount of tokens that can be spent in a period.","This amount is in decimals of the mint,","so 1 SOL would be `1_000_000_000` and 1 USDC would be `1_000_000`."],"type":"u64"},{"name":"period","docs":["The reset period of the spending limit.","When it passes, the remaining amount is reset, unless it's `Period::OneTime`."],"type":{"defined":"Period"}},{"name":"members","docs":["Members of the multisig that can use the spending limit.","In case a member is removed from the multisig, the spending limit will remain existent","(until explicitly deleted), but the removed member will not be able to use it anymore."],"type":{"vec":"publicKey"}},{"name":"destinations","docs":["The destination addresses the spending limit is allowed to sent funds to.","If empty, funds can be sent to any address."],"type":{"vec":"publicKey"}}]},{"name":"RemoveSpendingLimit","fields":[{"name":"spendingLimit","type":"publicKey"}]},{"name":"SetRentCollector","fields":[{"name":"newRentCollector","type":{"option":"publicKey"}}]}]}},{"name":"Permission","type":{"kind":"enum","variants":[{"name":"Initiate"},{"name":"Vote"},{"name":"Execute"}]}},{"name":"ProposalStatus","docs":["The status of a proposal.","Each variant wraps a timestamp of when the status was set."],"type":{"kind":"enum","variants":[{"name":"Draft","fields":[{"name":"timestamp","type":"i64"}]},{"name":"Active","fields":[{"name":"timestamp","type":"i64"}]},{"name":"Rejected","fields":[{"name":"timestamp","type":"i64"}]},{"name":"Approved","fields":[{"name":"timestamp","type":"i64"}]},{"name":"Executing"},{"name":"Executed","fields":[{"name":"timestamp","type":"i64"}]},{"name":"Cancelled","fields":[{"name":"timestamp","type":"i64"}]}]}},{"name":"Period","docs":["The reset period of the spending limit."],"type":{"kind":"enum","variants":[{"name":"OneTime"},{"name":"Day"},{"name":"Week"},{"name":"Month"}]}}],"errors":[{"code":6000,"name":"DuplicateMember","msg":"Found multiple members with the same pubkey"},{"code":6001,"name":"EmptyMembers","msg":"Members array is empty"},{"code":6002,"name":"TooManyMembers","msg":"Too many members, can be up to 65535"},{"code":6003,"name":"InvalidThreshold","msg":"Invalid threshold, must be between 1 and number of members with Vote permission"},{"code":6004,"name":"Unauthorized","msg":"Attempted to perform an unauthorized action"},{"code":6005,"name":"NotAMember","msg":"Provided pubkey is not a member of multisig"},{"code":6006,"name":"InvalidTransactionMessage","msg":"TransactionMessage is malformed."},{"code":6007,"name":"StaleProposal","msg":"Proposal is stale"},{"code":6008,"name":"InvalidProposalStatus","msg":"Invalid proposal status"},{"code":6009,"name":"InvalidTransactionIndex","msg":"Invalid transaction index"},{"code":6010,"name":"AlreadyApproved","msg":"Member already approved the transaction"},{"code":6011,"name":"AlreadyRejected","msg":"Member already rejected the transaction"},{"code":6012,"name":"AlreadyCancelled","msg":"Member already cancelled the transaction"},{"code":6013,"name":"InvalidNumberOfAccounts","msg":"Wrong number of accounts provided"},{"code":6014,"name":"InvalidAccount","msg":"Invalid account provided"},{"code":6015,"name":"RemoveLastMember","msg":"Cannot remove last member"},{"code":6016,"name":"NoVoters","msg":"Members don't include any voters"},{"code":6017,"name":"NoProposers","msg":"Members don't include any proposers"},{"code":6018,"name":"NoExecutors","msg":"Members don't include any executors"},{"code":6019,"name":"InvalidStaleTransactionIndex","msg":"`stale_transaction_index` must be <= `transaction_index`"},{"code":6020,"name":"NotSupportedForControlled","msg":"Instruction not supported for controlled multisig"},{"code":6021,"name":"TimeLockNotReleased","msg":"Proposal time lock has not been released"},{"code":6022,"name":"NoActions","msg":"Config transaction must have at least one action"},{"code":6023,"name":"MissingAccount","msg":"Missing account"},{"code":6024,"name":"InvalidMint","msg":"Invalid mint"},{"code":6025,"name":"InvalidDestination","msg":"Invalid destination"},{"code":6026,"name":"SpendingLimitExceeded","msg":"Spending limit exceeded"},{"code":6027,"name":"DecimalsMismatch","msg":"Decimals don't match the mint"},{"code":6028,"name":"UnknownPermission","msg":"Member has unknown permission"},{"code":6029,"name":"ProtectedAccount","msg":"Account is protected, it cannot be passed into a CPI as writable"},{"code":6030,"name":"TimeLockExceedsMaxAllowed","msg":"Time lock exceeds the maximum allowed (90 days)"},{"code":6031,"name":"IllegalAccountOwner","msg":"Account is not owned by Multisig program"},{"code":6032,"name":"RentReclamationDisabled","msg":"Rent reclamation is disabled for this multisig"},{"code":6033,"name":"InvalidRentCollector","msg":"Invalid rent collector address"},{"code":6034,"name":"ProposalForAnotherMultisig","msg":"Proposal is for another multisig"},{"code":6035,"name":"TransactionForAnotherMultisig","msg":"Transaction is for another multisig"},{"code":6036,"name":"TransactionNotMatchingProposal","msg":"Transaction doesn't match proposal"},{"code":6037,"name":"TransactionNotLastInBatch","msg":"Transaction is not last in batch"},{"code":6038,"name":"BatchNotEmpty","msg":"Batch is not empty"},{"code":6039,"name":"SpendingLimitInvalidAmount","msg":"Invalid SpendingLimit amount"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/stabble_clmm.6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6.json b/migration/khadhroony-bot2-reference/idls/stabble_clmm.6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6.json new file mode 100644 index 0000000..24a105f --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/stabble_clmm.6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6.json @@ -0,0 +1 @@ +{"address":"6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6","metadata":{"name":"amm_v3","version":"0.1.0","spec":"0.1.0","description":"Anchor client and source for Raydium concentrated liquidity AMM"},"instructions":[{"name":"close_position","docs":["Close the user's position and NFT account. If the NFT mint belongs to token2022, it will also be closed and the funds returned to the NFT owner.","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[123,134,81,0,49,68,98,98],"accounts":[{"name":"nft_owner","docs":["The position nft owner"],"writable":true,"signer":true},{"name":"position_nft_mint","docs":["Mint address bound to the personal position."],"writable":true},{"name":"position_nft_account","docs":["User token account where position NFT be minted to"],"writable":true},{"name":"personal_position","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"system_program","docs":["System program to close the position state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Token/Token2022 program to close token/mint account"]}],"args":[]},{"name":"close_protocol_position","docs":["The CLMM protocol decides to discard the protocol position account, which can reduce users' opening costs.","After the original protocol position account is closed, the gas fee will be refunded to the user who created it.","","# Arguments","","* `ctx` - The context of accounts",""],"discriminator":[201,117,152,144,85,85,108,178],"accounts":[{"name":"admin","writable":true,"signer":true,"address":"3kXrf8w8Z6EjLJU4S8dAkpRL2von8z7Eh3kJnFrmo7Z2"},{"name":"protocol_position","writable":true}],"args":[]},{"name":"collect_fund_fee","docs":["Collect the fund fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[167,138,78,149,223,194,6,126],"accounts":[{"name":"owner","docs":["Only admin or fund_owner can collect fee now"],"signer":true},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores fund_owner"]},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_protocol_fee","docs":["Collect the protocol fee accrued to the pool","","# Arguments","","* `ctx` - The context of accounts","* `amount_0_requested` - The maximum amount of token_0 to send, can be 0 to collect fees in only token_1","* `amount_1_requested` - The maximum amount of token_1 to send, can be 0 to collect fees in only token_0",""],"discriminator":[136,136,252,221,194,66,126,89],"accounts":[{"name":"owner","docs":["Only admin or config owner can collect fee now"],"signer":true},{"name":"pool_state","docs":["Pool state stores accumulated protocol fee amount"],"writable":true},{"name":"amm_config","docs":["Amm config account stores owner"]},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"writable":true},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"writable":true},{"name":"token_program","docs":["The SPL program to perform token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["The SPL program 2022 to perform token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"amount_0_requested","type":"u64"},{"name":"amount_1_requested","type":"u64"}]},{"name":"collect_remaining_rewards","docs":["Collect remaining reward token for reward founder","","# Arguments","","* `ctx`- The context of accounts","* `reward_index` - the index to reward info",""],"discriminator":[18,237,166,197,34,16,213,144],"accounts":[{"name":"reward_funder","docs":["The founder who init reward info previously"],"signer":true},{"name":"funder_token_account","docs":["The funder's reward token account"],"writable":true},{"name":"pool_state","docs":["Set reward for this pool"],"writable":true},{"name":"reward_token_vault","docs":["Reward vault transfer remaining token to founder token account"],"writable":true},{"name":"reward_vault_mint","docs":["The mint of reward token vault"]},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"reward_index","type":"u8"}]},{"name":"create_amm_config","docs":["# Arguments","","* `ctx`- The accounts needed by instruction.","* `index` - The index of clmm config, there may be multiple config.","* `tick_spacing` - The tickspacing binding with config, cannot be changed.","* `trade_fee_rate` - Trade fee rate, can be changed.","* `protocol_fee_rate` - The rate of protocol fee within trade fee.","* `fund_fee_rate` - The rate of fund fee within trade fee.",""],"discriminator":[137,52,237,212,215,117,108,104],"accounts":[{"name":"owner","docs":["Address to be set as protocol owner."],"writable":true,"signer":true,"address":"3kXrf8w8Z6EjLJU4S8dAkpRL2von8z7Eh3kJnFrmo7Z2"},{"name":"amm_config","docs":["Initialize config state account to store protocol owner address and fee rates."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[97,109,109,95,99,111,110,102,105,103]},{"kind":"arg","path":"index"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"index","type":"u16"},{"name":"tick_spacing","type":"u16"},{"name":"trade_fee_rate","type":"u32"},{"name":"protocol_fee_rate","type":"u32"},{"name":"fund_fee_rate","type":"u32"}]},{"name":"create_operation_account","docs":["Creates an operation account for the program","","# Arguments","","* `ctx`- The context of accounts",""],"discriminator":[63,87,148,33,109,35,8,104],"accounts":[{"name":"owner","docs":["Address to be set as operation account owner."],"writable":true,"signer":true,"address":"3kXrf8w8Z6EjLJU4S8dAkpRL2von8z7Eh3kJnFrmo7Z2"},{"name":"operation_state","docs":["Initialize operation state account to store operation owner address and white list mint."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"create_pool","docs":["Creates a pool for the given token pair and the initial price","","# Arguments","","* `ctx`- The context of accounts","* `sqrt_price_x64` - the initial sqrt price (amount_token_1 / amount_token_0) of the pool as a Q64.64","Note: The open_time must be smaller than the current block_timestamp on chain."],"discriminator":[233,146,209,142,207,104,64,188],"accounts":[{"name":"pool_creator","docs":["Address paying to create the pool. Can be anyone"],"writable":true,"signer":true},{"name":"amm_config","docs":["Which config the pool belongs to."]},{"name":"pool_state","docs":["Initialize an account to store the pool state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"amm_config"},{"kind":"account","path":"token_mint_0"},{"kind":"account","path":"token_mint_1"}]}},{"name":"token_mint_0","docs":["Token_0 mint, the key must be smaller then token_1 mint."]},{"name":"token_mint_1","docs":["Token_1 mint"]},{"name":"token_vault_0","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_0"}]}},{"name":"token_vault_1","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"token_mint_1"}]}},{"name":"observation_state","docs":["Initialize an account to store oracle observations"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,98,115,101,114,118,97,116,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"tick_array_bitmap","docs":["Initialize an account to store if a tick array is initialized."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,116,105,99,107,95,97,114,114,97,121,95,98,105,116,109,97,112,95,101,120,116,101,110,115,105,111,110]},{"kind":"account","path":"pool_state"}]}},{"name":"token_program_0","docs":["Spl token program or token program 2022"]},{"name":"token_program_1","docs":["Spl token program or token program 2022"]},{"name":"system_program","docs":["To create a new program account"],"address":"11111111111111111111111111111111"},{"name":"rent","docs":["Sysvar for program account"],"address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"sqrt_price_x64","type":"u128"},{"name":"open_time","type":"u64"}]},{"name":"create_support_mint_associated","docs":["Create support token22 mint account which can create pool and send rewards with ignoring the not support extensions."],"discriminator":[17,251,65,92,136,242,14,169],"accounts":[{"name":"owner","docs":["Address to be set as protocol owner."],"writable":true,"signer":true},{"name":"token_mint","docs":["Support token mint"]},{"name":"support_mint_associated","docs":["Initialize support mint state account to store support mint address and bump."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[115,117,112,112,111,114,116,95,109,105,110,116]},{"kind":"account","path":"token_mint"}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"decrease_liquidity_v2","docs":["Decreases liquidity for an existing position, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The amount by which liquidity will be decreased","* `amount_0_min` - The minimum amount of token_0 that should be accounted for the burned liquidity","* `amount_1_min` - The minimum amount of token_1 that should be accounted for the burned liquidity",""],"discriminator":[58,127,188,62,79,82,196,96],"accounts":[{"name":"nft_owner","docs":["The position owner or delegated authority"],"signer":true},{"name":"nft_account","docs":["The token account for the tokenized position"]},{"name":"personal_position","docs":["Decrease liquidity for this position"],"writable":true},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"token_vault_0","docs":["Token_0 vault"],"writable":true},{"name":"token_vault_1","docs":["Token_1 vault"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"recipient_token_account_0","docs":["The destination token account for receive amount_0"],"writable":true},{"name":"recipient_token_account_1","docs":["The destination token account for receive amount_1"],"writable":true},{"name":"token_program","docs":["SPL program to transfer out tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_min","type":"u64"},{"name":"amount_1_min","type":"u64"}]},{"name":"increase_liquidity_v2","docs":["Increases liquidity for an existing position, with amount paid by `payer`, support Token2022","","# Arguments","","* `ctx` - The context of accounts","* `liquidity` - The desired liquidity to be added, if zero, calculate liquidity base amount_0 or amount_1 according base_flag","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `base_flag` - must be specified if liquidity is zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[133,29,89,223,69,238,176,10],"accounts":[{"name":"nft_owner","docs":["Pays to mint the position"],"signer":true},{"name":"nft_account","docs":["The token account for nft"]},{"name":"pool_state","writable":true},{"name":"protocol_position"},{"name":"personal_position","docs":["Increase liquidity for this position"],"writable":true},{"name":"tick_array_lower","docs":["Stores init state for the lower tick"],"writable":true},{"name":"tick_array_upper","docs":["Stores init state for the upper tick"],"writable":true},{"name":"token_account_0","docs":["The payer's token account for token_0"],"writable":true},{"name":"token_account_1","docs":["The token account spending token_1 to mint the position"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"token_program","docs":["Program to create mint account and mint tokens"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"initialize_reward","docs":["Initialize a reward info for a given pool and reward index","","# Arguments","","* `ctx`- The context of accounts","* `reward_index` - the index to reward info","* `open_time` - reward open timestamp","* `end_time` - reward end timestamp","* `emissions_per_second_x64` - Token reward per second are earned per unit of liquidity.",""],"discriminator":[95,135,192,196,242,129,230,68],"accounts":[{"name":"reward_funder","docs":["The founder deposit reward token to vault"],"writable":true,"signer":true},{"name":"funder_token_account","writable":true},{"name":"amm_config","docs":["For check the reward_funder authority"]},{"name":"pool_state","docs":["Set reward for this pool"],"writable":true},{"name":"operation_state","docs":["load info from the account to judge reward permission"],"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"reward_token_mint","docs":["Reward mint"]},{"name":"reward_token_vault","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108,95,114,101,119,97,114,100,95,118,97,117,108,116]},{"kind":"account","path":"pool_state"},{"kind":"account","path":"reward_token_mint"}]}},{"name":"reward_token_program"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"param","type":{"defined":{"name":"InitializeRewardParam"}}}]},{"name":"open_position_with_token22_nft","docs":["Creates a new position wrapped in a Token2022 NFT without relying on metadata_program and metadata_account, reduce the cost for user to create a personal position.","","# Arguments","","* `ctx` - The context of accounts","* `tick_lower_index` - The low boundary of market","* `tick_upper_index` - The upper boundary of market","* `tick_array_lower_start_index` - The start index of tick array which include tick low","* `tick_array_upper_start_index` - The start index of tick array which include tick upper","* `liquidity` - The liquidity to be added, if zero, and the base_flag is specified, calculate liquidity base amount_0_max or amount_1_max according base_flag, otherwise open position with zero liquidity","* `amount_0_max` - The max amount of token_0 to spend, which serves as a slippage check","* `amount_1_max` - The max amount of token_1 to spend, which serves as a slippage check","* `with_metadata` - The flag indicating whether to create NFT mint metadata","* `base_flag` - if the liquidity specified as zero, true: calculate liquidity base amount_0_max otherwise base amount_1_max",""],"discriminator":[77,255,174,82,125,29,201,46],"accounts":[{"name":"payer","docs":["Pays to mint the position"],"writable":true,"signer":true},{"name":"position_nft_owner"},{"name":"position_nft_mint","docs":["Unique token mint address, initialize in contract"],"writable":true,"signer":true},{"name":"position_nft_account","writable":true},{"name":"pool_state","docs":["Add liquidity for this pool"],"writable":true},{"name":"protocol_position"},{"name":"tick_array_lower","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_lower_start_index"}]}},{"name":"tick_array_upper","writable":true,"pda":{"seeds":[{"kind":"const","value":[116,105,99,107,95,97,114,114,97,121]},{"kind":"account","path":"pool_state"},{"kind":"arg","path":"tick_array_upper_start_index"}]}},{"name":"personal_position","docs":["personal position state"],"writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,115,105,116,105,111,110]},{"kind":"account","path":"position_nft_mint"}]}},{"name":"token_account_0","docs":["The token_0 account deposit token to the pool"],"writable":true},{"name":"token_account_1","docs":["The token_1 account deposit token to the pool"],"writable":true},{"name":"token_vault_0","docs":["The address that holds pool tokens for token_0"],"writable":true},{"name":"token_vault_1","docs":["The address that holds pool tokens for token_1"],"writable":true},{"name":"rent","docs":["Sysvar for token mint and ATA creation"],"address":"SysvarRent111111111111111111111111111111111"},{"name":"system_program","docs":["Program to create the position manager state account"],"address":"11111111111111111111111111111111"},{"name":"token_program","docs":["Program to transfer for token account"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Program to create an ATA for receiving position NFT"],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"token_program_2022","docs":["Program to create NFT mint/token account and transfer for token22 account"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"vault_0_mint","docs":["The mint of token vault 0"]},{"name":"vault_1_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"tick_lower_index","type":"i32"},{"name":"tick_upper_index","type":"i32"},{"name":"tick_array_lower_start_index","type":"i32"},{"name":"tick_array_upper_start_index","type":"i32"},{"name":"liquidity","type":"u128"},{"name":"amount_0_max","type":"u64"},{"name":"amount_1_max","type":"u64"},{"name":"with_metadata","type":"bool"},{"name":"base_flag","type":{"option":"bool"}}]},{"name":"set_reward_params","docs":["Reset reward param, start a new reward cycle or extend the current cycle.","","# Arguments","","* `ctx` - The context of accounts","* `reward_index` - The index of reward token in the pool.","* `emissions_per_second_x64` - The per second emission reward, when extend the current cycle,","new value can't be less than old value","* `open_time` - reward open timestamp, must be set when starting a new cycle","* `end_time` - reward end timestamp",""],"discriminator":[112,52,167,75,32,201,211,137],"accounts":[{"name":"authority","docs":["Address to be set as protocol owner. It pays to create factory state account."],"signer":true},{"name":"amm_config"},{"name":"pool_state","writable":true},{"name":"operation_state","docs":["load info from the account to judge reward permission"],"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"token_program","docs":["Token program"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["Token program 2022"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"}],"args":[{"name":"reward_index","type":"u8"},{"name":"emissions_per_second_x64","type":"u128"},{"name":"open_time","type":"u64"},{"name":"end_time","type":"u64"}]},{"name":"swap_router_base_in","docs":["Swap token for as much as possible of another token across the path provided, base input","","# Arguments","","* `ctx` - The context of accounts","* `amount_in` - Token amount to be swapped in","* `amount_out_minimum` - Panic if output amount is below minimum amount. For slippage.",""],"discriminator":[69,125,115,218,245,186,242,196],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"input_token_account","docs":["The token account that pays input tokens for the swap"],"writable":true},{"name":"input_token_mint","docs":["The mint of input token"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["SPL program 2022 for token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["Memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"}],"args":[{"name":"amount_in","type":"u64"},{"name":"amount_out_minimum","type":"u64"}]},{"name":"swap_v2","docs":["Swaps one token for as much as possible of another token across a single pool, support token program 2022","","# Arguments","","* `ctx` - The context of accounts","* `amount` - Arranged in pairs with other_amount_threshold. (amount_in, amount_out_minimum) or (amount_out, amount_in_maximum)","* `other_amount_threshold` - For slippage check","* `sqrt_price_limit` - The Q64.64 sqrt price √P limit. If zero for one, the price cannot","* `is_base_input` - swap base input or swap base output",""],"discriminator":[43,4,237,11,26,201,30,98],"accounts":[{"name":"payer","docs":["The user performing the swap"],"signer":true},{"name":"amm_config","docs":["The factory state to read protocol fees"]},{"name":"pool_state","docs":["The program account of the pool in which the swap will be performed"],"writable":true},{"name":"input_token_account","docs":["The user token account for input token"],"writable":true},{"name":"output_token_account","docs":["The user token account for output token"],"writable":true},{"name":"input_vault","docs":["The vault token account for input token"],"writable":true},{"name":"output_vault","docs":["The vault token account for output token"],"writable":true},{"name":"observation_state","docs":["The program account for the most recent oracle observation"],"writable":true},{"name":"token_program","docs":["SPL program for token transfers"],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"token_program_2022","docs":["SPL program 2022 for token transfers"],"address":"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"},{"name":"memo_program","docs":["Memo program"],"address":"MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"},{"name":"input_vault_mint","docs":["The mint of token vault 0"]},{"name":"output_vault_mint","docs":["The mint of token vault 1"]}],"args":[{"name":"amount","type":"u64"},{"name":"other_amount_threshold","type":"u64"},{"name":"sqrt_price_limit_x64","type":"u128"},{"name":"is_base_input","type":"bool"}]},{"name":"transfer_reward_owner","docs":["Transfer reward owner","","# Arguments","","* `ctx`- The context of accounts","* `new_owner`- new owner pubkey",""],"discriminator":[7,22,12,83,242,43,48,121],"accounts":[{"name":"authority","docs":["Address to be set as operation account owner."],"signer":true,"address":"3kXrf8w8Z6EjLJU4S8dAkpRL2von8z7Eh3kJnFrmo7Z2"},{"name":"pool_state","writable":true}],"args":[{"name":"new_owner","type":"pubkey"}]},{"name":"update_amm_config","docs":["Updates the owner of the clmm config","Must be called by the current owner or admin","","# Arguments","","* `ctx`- The context of accounts","* `trade_fee_rate`- The new trade fee rate of clmm config, be set when `param` is 0","* `protocol_fee_rate`- The new protocol fee rate of clmm config, be set when `param` is 1","* `fund_fee_rate`- The new fund fee rate of clmm config, be set when `param` is 2","* `new_owner`- The config's new owner, be set when `param` is 3","* `new_fund_owner`- The config's new fund owner, be set when `param` is 4","* `param`- The value can be 0 | 1 | 2 | 3 | 4, otherwise will report a error",""],"discriminator":[49,60,174,136,154,28,116,200],"accounts":[{"name":"owner","docs":["The clmm config owner or admin"],"signer":true,"address":"3kXrf8w8Z6EjLJU4S8dAkpRL2von8z7Eh3kJnFrmo7Z2"},{"name":"amm_config","docs":["Amm config account to be changed"],"writable":true}],"args":[{"name":"param","type":"u8"},{"name":"value","type":"u32"}]},{"name":"update_operation_account","docs":["Update the operation account","","# Arguments","","* `ctx`- The context of accounts","* `param`- The value can be 0 | 1 | 2 | 3, otherwise will report a error","* `keys`- update operation owner when the `param` is 0","remove operation owner when the `param` is 1","update whitelist mint when the `param` is 2","remove whitelist mint when the `param` is 3",""],"discriminator":[127,70,119,40,188,227,61,7],"accounts":[{"name":"owner","docs":["Address to be set as operation account owner."],"signer":true,"address":"3kXrf8w8Z6EjLJU4S8dAkpRL2von8z7Eh3kJnFrmo7Z2"},{"name":"operation_state","docs":["Initialize operation state account to store operation owner address and white list mint."],"writable":true,"pda":{"seeds":[{"kind":"const","value":[111,112,101,114,97,116,105,111,110]}]}},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[{"name":"param","type":"u8"},{"name":"keys","type":{"vec":"pubkey"}}]},{"name":"update_pool_status","docs":["Update pool status for given value","","# Arguments","","* `ctx`- The context of accounts","* `status` - The value of status",""],"discriminator":[130,87,108,6,46,224,117,123],"accounts":[{"name":"authority","signer":true,"address":"3kXrf8w8Z6EjLJU4S8dAkpRL2von8z7Eh3kJnFrmo7Z2"},{"name":"pool_state","writable":true}],"args":[{"name":"status","type":"u8"}]},{"name":"update_reward_infos","docs":["Update rewards info of the given pool, can be called for everyone","","# Arguments","","* `ctx`- The context of accounts",""],"discriminator":[163,172,224,52,11,154,106,223],"accounts":[{"name":"pool_state","docs":["The liquidity pool for which reward info to update"],"writable":true}],"args":[]}],"accounts":[{"name":"AmmConfig","discriminator":[218,244,33,104,203,203,43,111]},{"name":"ObservationState","discriminator":[122,174,197,53,129,9,165,132]},{"name":"OperationState","discriminator":[19,236,58,237,81,222,183,252]},{"name":"PersonalPositionState","discriminator":[70,111,150,126,230,15,25,117]},{"name":"PoolState","discriminator":[247,237,227,245,215,195,222,70]},{"name":"ProtocolPositionState","discriminator":[100,226,145,99,146,218,160,106]},{"name":"SupportMintAssociated","discriminator":[134,40,183,79,12,112,162,53]},{"name":"TickArrayBitmapExtension","discriminator":[60,150,36,219,97,128,139,153]},{"name":"TickArrayState","discriminator":[192,155,85,205,49,249,129,42]}],"events":[{"name":"CollectPersonalFeeEvent","discriminator":[166,174,105,192,81,161,83,105]},{"name":"CollectProtocolFeeEvent","discriminator":[206,87,17,79,45,41,213,61]},{"name":"ConfigChangeEvent","discriminator":[247,189,7,119,106,112,95,151]},{"name":"CreatePersonalPositionEvent","discriminator":[100,30,87,249,196,223,154,206]},{"name":"DecreaseLiquidityEvent","discriminator":[58,222,86,58,68,50,85,56]},{"name":"IncreaseLiquidityEvent","discriminator":[49,79,105,212,32,34,30,84]},{"name":"LiquidityCalculateEvent","discriminator":[237,112,148,230,57,84,180,162]},{"name":"LiquidityChangeEvent","discriminator":[126,240,175,206,158,88,153,107]},{"name":"PoolCreatedEvent","discriminator":[25,94,75,47,112,99,53,63]},{"name":"SwapEvent","discriminator":[64,198,205,232,38,8,113,226]},{"name":"UpdateRewardInfosEvent","discriminator":[109,127,186,78,114,65,37,236]}],"errors":[{"code":6000,"name":"LOK","msg":"LOK"},{"code":6001,"name":"NotApproved","msg":"Not approved"},{"code":6002,"name":"InvalidUpdateConfigFlag","msg":"invalid update clmm config flag"},{"code":6003,"name":"AccountLack","msg":"Account lack"},{"code":6004,"name":"ClosePositionErr","msg":"Remove liquitity, collect fees owed and reward then you can close position account"},{"code":6005,"name":"ZeroMintAmount","msg":"Minting amount should be greater than 0"},{"code":6006,"name":"InvalidTickIndex","msg":"Tick out of range"},{"code":6007,"name":"TickInvalidOrder","msg":"The lower tick must be below the upper tick"},{"code":6008,"name":"TickLowerOverflow","msg":"The tick must be greater, or equal to the minimum tick(-443636)"},{"code":6009,"name":"TickUpperOverflow","msg":"The tick must be lesser than, or equal to the maximum tick(443636)"},{"code":6010,"name":"TickAndSpacingNotMatch","msg":"tick % tick_spacing must be zero"},{"code":6011,"name":"InvalidTickArray","msg":"Invalid tick array account"},{"code":6012,"name":"InvalidTickArrayBoundary","msg":"Invalid tick array boundary"},{"code":6013,"name":"SqrtPriceLimitOverflow","msg":"Square root price limit overflow"},{"code":6014,"name":"SqrtPriceX64","msg":"sqrt_price_x64 out of range"},{"code":6015,"name":"LiquiditySubValueErr","msg":"Liquidity sub delta L must be smaller than before"},{"code":6016,"name":"LiquidityAddValueErr","msg":"Liquidity add delta L must be greater, or equal to before"},{"code":6017,"name":"InvalidLiquidity","msg":"Invalid liquidity when update position"},{"code":6018,"name":"ForbidBothZeroForSupplyLiquidity","msg":"Both token amount must not be zero while supply liquidity"},{"code":6019,"name":"LiquidityInsufficient","msg":"Liquidity insufficient"},{"code":6020,"name":"TransactionTooOld","msg":"Transaction too old"},{"code":6021,"name":"PriceSlippageCheck","msg":"Price slippage check"},{"code":6022,"name":"TooLittleOutputReceived","msg":"Too little output received"},{"code":6023,"name":"TooMuchInputPaid","msg":"Too much input paid"},{"code":6024,"name":"ZeroAmountSpecified","msg":"Swap special amount can not be zero"},{"code":6025,"name":"InvalidInputPoolVault","msg":"Input pool vault is invalid"},{"code":6026,"name":"TooSmallInputOrOutputAmount","msg":"Swap input or output amount is too small"},{"code":6027,"name":"NotEnoughTickArrayAccount","msg":"Not enought tick array account"},{"code":6028,"name":"InvalidFirstTickArrayAccount","msg":"Invalid first tick array account"},{"code":6029,"name":"InvalidRewardIndex","msg":"Invalid reward index"},{"code":6030,"name":"FullRewardInfo","msg":"The init reward token reach to the max"},{"code":6031,"name":"RewardTokenAlreadyInUse","msg":"The init reward token already in use"},{"code":6032,"name":"ExceptRewardMint","msg":"The reward tokens must contain one of pool vault mint except the last reward"},{"code":6033,"name":"InvalidRewardInitParam","msg":"Invalid reward init param"},{"code":6034,"name":"InvalidRewardDesiredAmount","msg":"Invalid collect reward desired amount"},{"code":6035,"name":"InvalidRewardInputAccountNumber","msg":"Invalid collect reward input account number"},{"code":6036,"name":"InvalidRewardPeriod","msg":"Invalid reward period"},{"code":6037,"name":"NotApproveUpdateRewardEmissiones","msg":"Modification of emissiones is allowed within 72 hours from the end of the previous cycle"},{"code":6038,"name":"UnInitializedRewardInfo","msg":"uninitialized reward info"},{"code":6039,"name":"NotSupportMint","msg":"Not support token_2022 mint extension"},{"code":6040,"name":"MissingTickArrayBitmapExtensionAccount","msg":"Missing tickarray bitmap extension account"},{"code":6041,"name":"InsufficientLiquidityForDirection","msg":"Insufficient liquidity for this direction"},{"code":6042,"name":"MaxTokenOverflow","msg":"Max token overflow"},{"code":6043,"name":"CalculateOverflow","msg":"Calculate overflow"},{"code":6044,"name":"TransferFeeCalculateNotMatch","msg":"TransferFee calculate not match"}],"types":[{"name":"AmmConfig","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"index","type":"u16"},{"name":"owner","docs":["Address of the protocol owner"],"type":"pubkey"},{"name":"protocol_fee_rate","docs":["The protocol fee"],"type":"u32"},{"name":"trade_fee_rate","docs":["The trade fee, denominated in hundredths of a bip (10^-6)"],"type":"u32"},{"name":"tick_spacing","docs":["The tick spacing"],"type":"u16"},{"name":"fund_fee_rate","docs":["The fund fee, denominated in hundredths of a bip (10^-6)"],"type":"u32"},{"name":"padding_u32","type":"u32"},{"name":"fund_owner","type":"pubkey"},{"name":"padding","type":{"array":["u64",3]}}]}},{"name":"CollectPersonalFeeEvent","docs":["Emitted when tokens are collected for a position"],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which underlying tokens were collected"],"type":"pubkey"},{"name":"recipient_token_account_0","docs":["The token account that received the collected token_0 tokens"],"type":"pubkey"},{"name":"recipient_token_account_1","docs":["The token account that received the collected token_1 tokens"],"type":"pubkey"},{"name":"amount_0","docs":["The amount of token_0 owed to the position that was collected"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_1 owed to the position that was collected"],"type":"u64"}]}},{"name":"CollectProtocolFeeEvent","docs":["Emitted when the collected protocol fees are withdrawn by the factory owner"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool whose protocol fee is collected"],"type":"pubkey"},{"name":"recipient_token_account_0","docs":["The address that receives the collected token_0 protocol fees"],"type":"pubkey"},{"name":"recipient_token_account_1","docs":["The address that receives the collected token_1 protocol fees"],"type":"pubkey"},{"name":"amount_0","docs":["The amount of token_0 protocol fees that is withdrawn"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_0 protocol fees that is withdrawn"],"type":"u64"}]}},{"name":"ConfigChangeEvent","docs":["Emitted when create or update a config"],"type":{"kind":"struct","fields":[{"name":"index","type":"u16"},{"name":"owner","type":"pubkey"},{"name":"protocol_fee_rate","type":"u32"},{"name":"trade_fee_rate","type":"u32"},{"name":"tick_spacing","type":"u16"},{"name":"fund_fee_rate","type":"u32"},{"name":"fund_owner","type":"pubkey"}]}},{"name":"CreatePersonalPositionEvent","docs":["Emitted when create a new position"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for which liquidity was added"],"type":"pubkey"},{"name":"personal_position","docs":["The personal position"],"type":"pubkey"},{"name":"nft_mint","docs":["The nft mint address for the position"],"type":"pubkey"},{"name":"minter","docs":["The address that create the position"],"type":"pubkey"},{"name":"nft_owner","docs":["The owner of the position and recipient of any minted liquidity"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity minted to the position range"],"type":"u128"},{"name":"deposit_amount_0","docs":["The amount of token_0 was deposit for the liquidity"],"type":"u64"},{"name":"deposit_amount_1","docs":["The amount of token_1 was deposit for the liquidity"],"type":"u64"},{"name":"deposit_amount_0_transfer_fee","docs":["The token transfer fee for deposit_amount_0"],"type":"u64"},{"name":"deposit_amount_1_transfer_fee","docs":["The token transfer fee for deposit_amount_1"],"type":"u64"}]}},{"name":"DecreaseLiquidityEvent","docs":["Emitted when liquidity is decreased."],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which liquidity was decreased"],"type":"pubkey"},{"name":"liquidity","docs":["The amount by which liquidity for the position was decreased"],"type":"u128"},{"name":"decrease_amount_0","docs":["The amount of token_0 that was paid for the decrease in liquidity"],"type":"u64"},{"name":"decrease_amount_1","docs":["The amount of token_1 that was paid for the decrease in liquidity"],"type":"u64"},{"name":"fee_amount_0","type":"u64"},{"name":"fee_amount_1","docs":["The amount of token_1 fee"],"type":"u64"},{"name":"reward_amounts","docs":["The amount of rewards"],"type":{"array":["u64",3]}},{"name":"transfer_fee_0","docs":["The amount of token_0 transfer fee"],"type":"u64"},{"name":"transfer_fee_1","docs":["The amount of token_1 transfer fee"],"type":"u64"}]}},{"name":"IncreaseLiquidityEvent","docs":["Emitted when liquidity is increased."],"type":{"kind":"struct","fields":[{"name":"position_nft_mint","docs":["The ID of the token for which liquidity was increased"],"type":"pubkey"},{"name":"liquidity","docs":["The amount by which liquidity for the NFT position was increased"],"type":"u128"},{"name":"amount_0","docs":["The amount of token_0 that was paid for the increase in liquidity"],"type":"u64"},{"name":"amount_1","docs":["The amount of token_1 that was paid for the increase in liquidity"],"type":"u64"},{"name":"amount_0_transfer_fee","docs":["The token transfer fee for amount_0"],"type":"u64"},{"name":"amount_1_transfer_fee","docs":["The token transfer fee for amount_1"],"type":"u64"}]}},{"name":"InitializeRewardParam","type":{"kind":"struct","fields":[{"name":"open_time","docs":["Reward open time"],"type":"u64"},{"name":"end_time","docs":["Reward end time"],"type":"u64"},{"name":"emissions_per_second_x64","docs":["Token reward per second are earned per unit of liquidity"],"type":"u128"}]}},{"name":"LiquidityCalculateEvent","docs":["Emitted when liquidity decreased or increase."],"type":{"kind":"struct","fields":[{"name":"pool_liquidity","docs":["The pool liquidity before decrease or increase"],"type":"u128"},{"name":"pool_sqrt_price_x64","docs":["The pool price when decrease or increase in liquidity"],"type":"u128"},{"name":"pool_tick","docs":["The pool tick when decrease or increase in liquidity"],"type":"i32"},{"name":"calc_amount_0","docs":["The amount of token_0 that was calculated for the decrease or increase in liquidity"],"type":"u64"},{"name":"calc_amount_1","docs":["The amount of token_1 that was calculated for the decrease or increase in liquidity"],"type":"u64"},{"name":"trade_fee_owed_0","type":"u64"},{"name":"trade_fee_owed_1","docs":["The amount of token_1 fee"],"type":"u64"},{"name":"transfer_fee_0","docs":["The amount of token_0 transfer fee without trade_fee_amount_0"],"type":"u64"},{"name":"transfer_fee_1","docs":["The amount of token_1 transfer fee without trade_fee_amount_0"],"type":"u64"}]}},{"name":"LiquidityChangeEvent","docs":["Emitted pool liquidity change when increase and decrease liquidity"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for swap"],"type":"pubkey"},{"name":"tick","docs":["The tick of the pool"],"type":"i32"},{"name":"tick_lower","docs":["The tick lower of position"],"type":"i32"},{"name":"tick_upper","docs":["The tick lower of position"],"type":"i32"},{"name":"liquidity_before","docs":["The liquidity of the pool before liquidity change"],"type":"u128"},{"name":"liquidity_after","docs":["The liquidity of the pool after liquidity change"],"type":"u128"}]}},{"name":"Observation","docs":["The element of observations in ObservationState"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"block_timestamp","docs":["The block timestamp of the observation"],"type":"u32"},{"name":"tick_cumulative","docs":["the cumulative of tick during the duration time"],"type":"i64"},{"name":"padding","docs":["padding for feature update"],"type":{"array":["u64",4]}}]}},{"name":"ObservationState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"initialized","docs":["Whether the ObservationState is initialized"],"type":"bool"},{"name":"recent_epoch","docs":["recent update epoch"],"type":"u64"},{"name":"observation_index","docs":["the most-recently updated index of the observations array"],"type":"u16"},{"name":"pool_id","docs":["belongs to which pool"],"type":"pubkey"},{"name":"observations","docs":["observation array"],"type":{"array":[{"defined":{"name":"Observation"}},100]}},{"name":"padding","docs":["padding for feature update"],"type":{"array":["u64",4]}}]}},{"name":"OperationState","docs":["Holds the current owner of the factory"],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"operation_owners","docs":["Address of the operation owner"],"type":{"array":["pubkey",10]}},{"name":"whitelist_mints","docs":["The mint address of whitelist to emit reward"],"type":{"array":["pubkey",100]}}]}},{"name":"PersonalPositionState","type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":{"array":["u8",1]}},{"name":"nft_mint","docs":["Mint address of the tokenized position"],"type":"pubkey"},{"name":"pool_id","docs":["The ID of the pool with which this token is connected"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"fee_growth_inside_0_last_x64","docs":["The token_0 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"fee_growth_inside_1_last_x64","docs":["The token_1 fee growth of the aggregate position as of the last action on the individual position"],"type":"u128"},{"name":"token_fees_owed_0","docs":["The fees owed to the position owner in token_0, as of the last computation"],"type":"u64"},{"name":"token_fees_owed_1","docs":["The fees owed to the position owner in token_1, as of the last computation"],"type":"u64"},{"name":"reward_infos","type":{"array":[{"defined":{"name":"PositionRewardInfo"}},3]}},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u64",7]}}]}},{"name":"PoolCreatedEvent","docs":["Emitted when a pool is created and initialized with a starting price",""],"type":{"kind":"struct","fields":[{"name":"token_mint_0","docs":["The first token of the pool by address sort order"],"type":"pubkey"},{"name":"token_mint_1","docs":["The second token of the pool by address sort order"],"type":"pubkey"},{"name":"tick_spacing","docs":["The minimum number of ticks between initialized ticks"],"type":"u16"},{"name":"pool_state","docs":["The address of the created pool"],"type":"pubkey"},{"name":"sqrt_price_x64","docs":["The initial sqrt price of the pool, as a Q64.64"],"type":"u128"},{"name":"tick","docs":["The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool"],"type":"i32"},{"name":"token_vault_0","docs":["Vault of token_0"],"type":"pubkey"},{"name":"token_vault_1","docs":["Vault of token_1"],"type":"pubkey"},{"name":"amm_config","docs":["The PublicKey of the config account with fee rates etc"],"type":"pubkey"}]}},{"name":"PoolState","docs":["The pool state","","PDA of `[POOL_SEED, config, token_mint_0, token_mint_1]`",""],"serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":{"array":["u8",1]}},{"name":"amm_config","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"token_mint_0","docs":["Token pair of the pool, where token_mint_0 address < token_mint_1 address"],"type":"pubkey"},{"name":"token_mint_1","type":"pubkey"},{"name":"token_vault_0","docs":["Token pair vault"],"type":"pubkey"},{"name":"token_vault_1","type":"pubkey"},{"name":"observation_key","docs":["observation account key"],"type":"pubkey"},{"name":"mint_decimals_0","docs":["mint0 and mint1 decimals"],"type":"u8"},{"name":"mint_decimals_1","type":"u8"},{"name":"tick_spacing","docs":["The minimum number of ticks between initialized ticks"],"type":"u16"},{"name":"liquidity","docs":["The currently in range liquidity available to the pool."],"type":"u128"},{"name":"sqrt_price_x64","docs":["The current price of the pool as a sqrt(token_1/token_0) Q64.64 value"],"type":"u128"},{"name":"tick_current","docs":["The current tick of the pool, i.e. according to the last tick transition that was run."],"type":"i32"},{"name":"padding3","type":"u16"},{"name":"padding4","type":"u16"},{"name":"fee_growth_global_0_x64","docs":["The fee growth as a Q64.64 number, i.e. fees of token_0 and token_1 collected per","unit of liquidity for the entire life of the pool."],"type":"u128"},{"name":"fee_growth_global_1_x64","type":"u128"},{"name":"protocol_fees_token_0","docs":["The amounts of token_0 and token_1 that are owed to the protocol."],"type":"u64"},{"name":"protocol_fees_token_1","type":"u64"},{"name":"swap_in_amount_token_0","docs":["The amounts in and out of swap token_0 and token_1"],"type":"u128"},{"name":"swap_out_amount_token_1","type":"u128"},{"name":"swap_in_amount_token_1","type":"u128"},{"name":"swap_out_amount_token_0","type":"u128"},{"name":"status","docs":["Bitwise representation of the state of the pool","bit0, 1: disable open position and increase liquidity, 0: normal","bit1, 1: disable decrease liquidity, 0: normal","bit2, 1: disable collect fee, 0: normal","bit3, 1: disable collect reward, 0: normal","bit4, 1: disable swap, 0: normal"],"type":"u8"},{"name":"padding","docs":["Leave blank for future use"],"type":{"array":["u8",7]}},{"name":"reward_infos","type":{"array":[{"defined":{"name":"RewardInfo"}},3]}},{"name":"tick_array_bitmap","docs":["Packed initialized tick array state"],"type":{"array":["u64",16]}},{"name":"total_fees_token_0","docs":["except protocol_fee and fund_fee"],"type":"u64"},{"name":"total_fees_claimed_token_0","docs":["except protocol_fee and fund_fee"],"type":"u64"},{"name":"total_fees_token_1","type":"u64"},{"name":"total_fees_claimed_token_1","type":"u64"},{"name":"fund_fees_token_0","type":"u64"},{"name":"fund_fees_token_1","type":"u64"},{"name":"open_time","type":"u64"},{"name":"recent_epoch","type":"u64"},{"name":"padding1","type":{"array":["u64",24]}},{"name":"padding2","type":{"array":["u64",32]}}]}},{"name":"PositionRewardInfo","type":{"kind":"struct","fields":[{"name":"growth_inside_last_x64","type":"u128"},{"name":"reward_amount_owed","type":"u64"}]}},{"name":"ProtocolPositionState","docs":["Info stored for each user's position"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"pool_id","docs":["The ID of the pool with which this token is connected"],"type":"pubkey"},{"name":"tick_lower_index","docs":["The lower bound tick of the position"],"type":"i32"},{"name":"tick_upper_index","docs":["The upper bound tick of the position"],"type":"i32"},{"name":"liquidity","docs":["The amount of liquidity owned by this position"],"type":"u128"},{"name":"fee_growth_inside_0_last_x64","docs":["The token_0 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"fee_growth_inside_1_last_x64","docs":["The token_1 fee growth per unit of liquidity as of the last update to liquidity or fees owed"],"type":"u128"},{"name":"token_fees_owed_0","docs":["The fees owed to the position owner in token_0"],"type":"u64"},{"name":"token_fees_owed_1","docs":["The fees owed to the position owner in token_1"],"type":"u64"},{"name":"reward_growth_inside","docs":["The reward growth per unit of liquidity as of the last update to liquidity"],"type":{"array":["u128",3]}},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u64",7]}}]}},{"name":"RewardInfo","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"reward_state","docs":["Reward state"],"type":"u8"},{"name":"open_time","docs":["Reward open time"],"type":"u64"},{"name":"end_time","docs":["Reward end time"],"type":"u64"},{"name":"last_update_time","docs":["Reward last update time"],"type":"u64"},{"name":"emissions_per_second_x64","docs":["Q64.64 number indicates how many tokens per second are earned per unit of liquidity."],"type":"u128"},{"name":"reward_total_emissioned","docs":["The total amount of reward emissioned"],"type":"u64"},{"name":"reward_claimed","docs":["The total amount of claimed reward"],"type":"u64"},{"name":"token_mint","docs":["Reward token mint."],"type":"pubkey"},{"name":"token_vault","docs":["Reward vault token account."],"type":"pubkey"},{"name":"authority","docs":["The owner that has permission to set reward param"],"type":"pubkey"},{"name":"reward_growth_global_x64","docs":["Q64.64 number that tracks the total tokens earned per unit of liquidity since the reward","emissions were turned on."],"type":"u128"}]}},{"name":"SupportMintAssociated","docs":["Holds the current owner of the factory"],"type":{"kind":"struct","fields":[{"name":"bump","docs":["Bump to identify PDA"],"type":"u8"},{"name":"mint","docs":["Address of the supported token22 mint"],"type":"pubkey"},{"name":"padding","type":{"array":["u64",8]}}]}},{"name":"SwapEvent","docs":["Emitted by when a swap is performed for a pool"],"type":{"kind":"struct","fields":[{"name":"pool_state","docs":["The pool for which token_0 and token_1 were swapped"],"type":"pubkey"},{"name":"sender","docs":["The address that initiated the swap call, and that received the callback"],"type":"pubkey"},{"name":"token_account_0","docs":["The payer token account in zero for one swaps, or the recipient token account","in one for zero swaps"],"type":"pubkey"},{"name":"token_account_1","docs":["The payer token account in one for zero swaps, or the recipient token account","in zero for one swaps"],"type":"pubkey"},{"name":"amount_0","docs":["The real delta amount of the token_0 of the pool or user"],"type":"u64"},{"name":"transfer_fee_0","docs":["The transfer fee charged by the withheld_amount of the token_0"],"type":"u64"},{"name":"amount_1","docs":["The real delta of the token_1 of the pool or user"],"type":"u64"},{"name":"transfer_fee_1","docs":["The transfer fee charged by the withheld_amount of the token_1"],"type":"u64"},{"name":"zero_for_one","docs":["if true, amount_0 is negtive and amount_1 is positive"],"type":"bool"},{"name":"sqrt_price_x64","docs":["The sqrt(price) of the pool after the swap, as a Q64.64"],"type":"u128"},{"name":"liquidity","docs":["The liquidity of the pool after the swap"],"type":"u128"},{"name":"tick","docs":["The log base 1.0001 of price of the pool after the swap"],"type":"i32"}]}},{"name":"TickArrayBitmapExtension","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"positive_tick_array_bitmap","docs":["Packed initialized tick array state for start_tick_index is positive"],"type":{"array":[{"array":["u64",8]},14]}},{"name":"negative_tick_array_bitmap","docs":["Packed initialized tick array state for start_tick_index is negitive"],"type":{"array":[{"array":["u64",8]},14]}}]}},{"name":"TickArrayState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"pool_id","type":"pubkey"},{"name":"start_tick_index","type":"i32"},{"name":"ticks","type":{"array":[{"defined":{"name":"TickState"}},60]}},{"name":"initialized_tick_count","type":"u8"},{"name":"recent_epoch","type":"u64"},{"name":"padding","type":{"array":["u8",107]}}]}},{"name":"TickState","serialization":"bytemuckunsafe","repr":{"kind":"c","packed":true},"type":{"kind":"struct","fields":[{"name":"tick","type":"i32"},{"name":"liquidity_net","docs":["Amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left)"],"type":"i128"},{"name":"liquidity_gross","docs":["The total position liquidity that references this tick"],"type":"u128"},{"name":"fee_growth_outside_0_x64","docs":["Fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)","only has relative meaning, not absolute — the value depends on when the tick is initialized"],"type":"u128"},{"name":"fee_growth_outside_1_x64","type":"u128"},{"name":"reward_growths_outside_x64","type":{"array":["u128",3]}},{"name":"padding","type":{"array":["u32",13]}}]}},{"name":"UpdateRewardInfosEvent","docs":["Emitted when Reward are updated for a pool"],"type":{"kind":"struct","fields":[{"name":"reward_growth_global_x64","docs":["Reward info"],"type":{"array":["u128",3]}}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/stabble_stable_swap.swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ.json b/migration/khadhroony-bot2-reference/idls/stabble_stable_swap.swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ.json new file mode 100644 index 0000000..77d460f --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/stabble_stable_swap.swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ.json @@ -0,0 +1 @@ +{"address":"swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ","metadata":{"name":"stable_swap","version":"1.5.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"accept_owner","discriminator":[176,23,41,28,23,111,8,4],"accounts":[{"name":"pending_owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"approve_strategy","discriminator":[7,141,162,60,71,115,26,146],"accounts":[{"name":"admin_only","accounts":[{"name":"pool","writable":true},{"name":"vault"},{"name":"admin","signer":true}]},{"name":"strategy","writable":true}],"args":[]},{"name":"change_amp_factor","discriminator":[56,238,189,35,200,157,42,66],"accounts":[{"name":"pool","writable":true},{"name":"vault"},{"name":"admin","signer":true}],"args":[{"name":"new_amp_factor","type":"u16"},{"name":"ramp_duration","type":"u32"}]},{"name":"change_max_supply","discriminator":[93,176,0,205,69,63,87,80],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[{"name":"new_max_supply","type":"u64"}]},{"name":"change_swap_fee","discriminator":[231,15,132,51,132,165,64,170],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[{"name":"new_swap_fee","type":"u64"}]},{"name":"change_swap_fee_privileged","discriminator":[155,200,14,91,105,38,220,119],"accounts":[{"name":"pool","writable":true},{"name":"vault"},{"name":"admin","signer":true}],"args":[{"name":"new_swap_fee","type":"u64"}]},{"name":"close_strategy","discriminator":[56,247,170,246,89,221,134,200],"accounts":[{"name":"owner_only","accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}]},{"name":"strategy","writable":true},{"name":"rent_collector","writable":true}],"args":[]},{"name":"create_strategy","discriminator":[152,160,107,148,245,190,127,224],"accounts":[{"name":"owner_only","accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}]},{"name":"strategy","writable":true}],"args":[{"name":"amp_min_factor","type":"u16"},{"name":"amp_max_factor","type":"u16"},{"name":"ramp_min_step","type":"u16"},{"name":"ramp_max_step","type":"u16"},{"name":"ramp_min_duration","type":"u32"},{"name":"ramp_max_duration","type":"u32"}]},{"name":"deposit","docs":["add liquidity"],"discriminator":[242,35,198,137,82,225,242,182],"accounts":[{"name":"user","signer":true},{"name":"user_pool_token","writable":true},{"name":"mint","writable":true},{"name":"pool","writable":true},{"name":"pool_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"token_program"},{"name":"token_program_2022"}],"args":[{"name":"amounts","type":{"vec":"u64"}},{"name":"minimum_amount_out","type":"u64"}]},{"name":"exec_strategy","discriminator":[249,46,55,57,31,38,61,27],"accounts":[{"name":"strategy"},{"name":"pool","writable":true}],"args":[{"name":"ramp_step","type":"u16"},{"name":"ramp_duration","type":"u32"}]},{"name":"initialize","docs":["initialize a pool"],"discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"owner","signer":true},{"name":"mint"},{"name":"pool","writable":true},{"name":"pool_authority"},{"name":"withdraw_authority"},{"name":"vault"}],"args":[{"name":"amp_factor","type":"u16"},{"name":"swap_fee","type":"u64"},{"name":"max_caps","type":{"vec":"u64"}}]},{"name":"pause","discriminator":[211,22,221,251,74,121,193,47],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"reject_owner","discriminator":[238,206,198,215,51,178,133,228],"accounts":[{"name":"pending_owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"shutdown","docs":["shutdown the zero-liquidity pool"],"discriminator":[146,204,241,213,86,21,253,211],"accounts":[{"name":"owner","writable":true},{"name":"pool","writable":true}],"args":[]},{"name":"swap","docs":["swap"],"discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"user","signer":true},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"vault_token_in","writable":true},{"name":"vault_token_out","writable":true},{"name":"beneficiary_token_out","writable":true},{"name":"pool","writable":true},{"name":"withdraw_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"vault_program"},{"name":"token_program"}],"args":[{"name":"amount_in","type":{"option":"u64"}},{"name":"minimum_amount_out","type":"u64"}]},{"name":"swap_v2","discriminator":[43,4,237,11,26,201,30,98],"accounts":[{"name":"user","signer":true},{"name":"mint_in"},{"name":"mint_out"},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"vault_token_in","writable":true},{"name":"vault_token_out","writable":true},{"name":"beneficiary_token_out","writable":true},{"name":"pool","writable":true},{"name":"withdraw_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"vault_program"},{"name":"token_program"},{"name":"token_2022_program"}],"args":[{"name":"amount_in","type":{"option":"u64"}},{"name":"minimum_amount_out","type":"u64"}]},{"name":"transfer_owner","discriminator":[245,25,221,175,106,229,225,45],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[{"name":"new_owner","type":"pubkey"}]},{"name":"unpause","discriminator":[169,144,4,38,10,141,188,255],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"withdraw","docs":["remove liquidity"],"discriminator":[183,18,70,156,148,109,161,34],"accounts":[{"name":"user","signer":true},{"name":"user_pool_token","writable":true},{"name":"mint","writable":true},{"name":"pool","writable":true},{"name":"withdraw_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"vault_program"},{"name":"token_program"},{"name":"token_program_2022"}],"args":[{"name":"amount","type":"u64"},{"name":"minimum_amounts_out","type":{"vec":"u64"}}]}],"accounts":[{"name":"Pool","discriminator":[241,154,109,4,17,177,109,188]},{"name":"Strategy","discriminator":[174,110,39,119,82,106,169,102]},{"name":"Vault","discriminator":[211,8,232,43,2,152,117,119]}],"events":[{"name":"PoolBalanceUpdatedEvent","discriminator":[172,82,114,207,27,103,211,4]},{"name":"PoolUpdatedEvent","discriminator":[128,39,94,221,230,222,127,141]}],"types":[{"name":"Pool","type":{"kind":"struct","fields":[{"name":"owner","type":"pubkey"},{"name":"vault","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"authority_bump","type":"u8"},{"name":"is_active","type":"bool"},{"name":"amp_initial_factor","type":"u16"},{"name":"amp_target_factor","type":"u16"},{"name":"ramp_start_ts","type":"i64"},{"name":"ramp_stop_ts","type":"i64"},{"name":"swap_fee","type":"u64"},{"name":"tokens","type":{"vec":{"defined":{"name":"PoolToken"}}}},{"name":"pending_owner","type":{"option":"pubkey"}},{"name":"max_supply","type":"u64"}]}},{"name":"PoolBalanceUpdatedData","type":{"kind":"struct","fields":[{"name":"balances","type":{"vec":"u64"}}]}},{"name":"PoolBalanceUpdatedEvent","type":{"kind":"struct","fields":[{"name":"pubkey","type":"pubkey"},{"name":"data","type":{"defined":{"name":"PoolBalanceUpdatedData"}}}]}},{"name":"PoolToken","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"decimals","type":"u8"},{"name":"scaling_up","type":"bool"},{"name":"scaling_factor","type":"u64"},{"name":"balance","type":"u64"}]}},{"name":"PoolUpdatedData","type":{"kind":"struct","fields":[{"name":"is_active","type":"bool"},{"name":"amp_initial_factor","type":"u16"},{"name":"amp_target_factor","type":"u16"},{"name":"ramp_start_ts","type":"i64"},{"name":"ramp_stop_ts","type":"i64"},{"name":"swap_fee","type":"u64"},{"name":"max_supply","type":"u64"}]}},{"name":"PoolUpdatedEvent","type":{"kind":"struct","fields":[{"name":"pubkey","type":"pubkey"},{"name":"data","type":{"defined":{"name":"PoolUpdatedData"}}}]}},{"name":"Strategy","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"is_active","type":"bool"},{"name":"amp_min_factor","type":"u16"},{"name":"amp_max_factor","type":"u16"},{"name":"ramp_min_step","type":"u16"},{"name":"ramp_max_step","type":"u16"},{"name":"ramp_min_duration","type":"u32"},{"name":"ramp_max_duration","type":"u32"}]}},{"name":"Vault","type":{"kind":"struct","fields":[{"name":"admin","type":"pubkey"},{"name":"withdraw_authority","docs":["PDA of pool programs seeded by vault address"],"type":"pubkey"},{"name":"withdraw_authority_bump","docs":["bump seed of withdraw_authority PDA"],"type":"u8"},{"name":"authority_bump","docs":["bump seed of vault_authority PDA"],"type":"u8"},{"name":"is_active","type":"bool"},{"name":"beneficiary","type":"pubkey"},{"name":"beneficiary_fee","type":"u64"},{"name":"pending_admin","type":{"option":"pubkey"}}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/stabble_weighted_swap.swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW.json b/migration/khadhroony-bot2-reference/idls/stabble_weighted_swap.swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW.json new file mode 100644 index 0000000..d735135 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/stabble_weighted_swap.swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW.json @@ -0,0 +1 @@ +{"address":"swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW","metadata":{"name":"weighted_swap","version":"1.5.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"accept_owner","discriminator":[176,23,41,28,23,111,8,4],"accounts":[{"name":"pending_owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"change_max_supply","discriminator":[93,176,0,205,69,63,87,80],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[{"name":"new_max_supply","type":"u64"}]},{"name":"change_swap_fee","discriminator":[231,15,132,51,132,165,64,170],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[{"name":"new_swap_fee","type":"u64"}]},{"name":"deposit","docs":["add liquidity"],"discriminator":[242,35,198,137,82,225,242,182],"accounts":[{"name":"user","signer":true},{"name":"user_pool_token","writable":true},{"name":"mint","writable":true},{"name":"pool","writable":true},{"name":"pool_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"token_program"},{"name":"token_program_2022"}],"args":[{"name":"amounts","type":{"vec":"u64"}},{"name":"minimum_amount_out","type":"u64"}]},{"name":"initialize","docs":["initialize a pool"],"discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"owner","signer":true},{"name":"mint"},{"name":"pool","writable":true},{"name":"pool_authority"},{"name":"withdraw_authority"},{"name":"vault"}],"args":[{"name":"swap_fee","type":"u64"},{"name":"weights","type":{"vec":"u64"}},{"name":"max_caps","type":{"vec":"u64"}}]},{"name":"pause","discriminator":[211,22,221,251,74,121,193,47],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"reject_owner","discriminator":[238,206,198,215,51,178,133,228],"accounts":[{"name":"pending_owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"shutdown","docs":["shutdown the zero-liquidity pool"],"discriminator":[146,204,241,213,86,21,253,211],"accounts":[{"name":"owner","writable":true},{"name":"pool","writable":true}],"args":[]},{"name":"swap","docs":["swap"],"discriminator":[248,198,158,145,225,117,135,200],"accounts":[{"name":"user","signer":true},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"vault_token_in","writable":true},{"name":"vault_token_out","writable":true},{"name":"beneficiary_token_out","writable":true},{"name":"pool","writable":true},{"name":"withdraw_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"vault_program"},{"name":"token_program"}],"args":[{"name":"amount_in","type":{"option":"u64"}},{"name":"minimum_amount_out","type":"u64"}]},{"name":"swap_v2","discriminator":[43,4,237,11,26,201,30,98],"accounts":[{"name":"user","signer":true},{"name":"mint_in"},{"name":"mint_out"},{"name":"user_token_in","writable":true},{"name":"user_token_out","writable":true},{"name":"vault_token_in","writable":true},{"name":"vault_token_out","writable":true},{"name":"beneficiary_token_out","writable":true},{"name":"pool","writable":true},{"name":"withdraw_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"vault_program"},{"name":"token_program"},{"name":"token_2022_program"}],"args":[{"name":"amount_in","type":{"option":"u64"}},{"name":"minimum_amount_out","type":"u64"}]},{"name":"transfer_owner","discriminator":[245,25,221,175,106,229,225,45],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[{"name":"new_owner","type":"pubkey"}]},{"name":"unpause","discriminator":[169,144,4,38,10,141,188,255],"accounts":[{"name":"owner","signer":true},{"name":"pool","writable":true}],"args":[]},{"name":"withdraw","docs":["remove liquidity"],"discriminator":[183,18,70,156,148,109,161,34],"accounts":[{"name":"user","signer":true},{"name":"user_pool_token","writable":true},{"name":"mint","writable":true},{"name":"pool","writable":true},{"name":"withdraw_authority"},{"name":"vault"},{"name":"vault_authority"},{"name":"vault_program"},{"name":"token_program"},{"name":"token_program_2022"}],"args":[{"name":"amount","type":"u64"},{"name":"minimum_amounts_out","type":{"vec":"u64"}}]}],"accounts":[{"name":"Pool","discriminator":[241,154,109,4,17,177,109,188]},{"name":"Vault","discriminator":[211,8,232,43,2,152,117,119]}],"events":[{"name":"PoolBalanceUpdatedEvent","discriminator":[172,82,114,207,27,103,211,4]},{"name":"PoolUpdatedEvent","discriminator":[128,39,94,221,230,222,127,141]}],"types":[{"name":"Pool","type":{"kind":"struct","fields":[{"name":"owner","type":"pubkey"},{"name":"vault","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"authority_bump","type":"u8"},{"name":"is_active","type":"bool"},{"name":"invariant","type":"u64"},{"name":"swap_fee","type":"u64"},{"name":"tokens","type":{"vec":{"defined":{"name":"PoolToken"}}}},{"name":"pending_owner","type":{"option":"pubkey"}},{"name":"max_supply","type":"u64"}]}},{"name":"PoolBalanceUpdatedData","type":{"kind":"struct","fields":[{"name":"balances","type":{"vec":"u64"}}]}},{"name":"PoolBalanceUpdatedEvent","type":{"kind":"struct","fields":[{"name":"pubkey","type":"pubkey"},{"name":"data","type":{"defined":{"name":"PoolBalanceUpdatedData"}}}]}},{"name":"PoolToken","type":{"kind":"struct","fields":[{"name":"mint","type":"pubkey"},{"name":"decimals","type":"u8"},{"name":"scaling_up","type":"bool"},{"name":"scaling_factor","type":"u64"},{"name":"balance","type":"u64"},{"name":"weight","type":"u64"}]}},{"name":"PoolUpdatedData","type":{"kind":"struct","fields":[{"name":"is_active","type":"bool"},{"name":"swap_fee","type":"u64"},{"name":"max_supply","type":"u64"}]}},{"name":"PoolUpdatedEvent","type":{"kind":"struct","fields":[{"name":"pubkey","type":"pubkey"},{"name":"data","type":{"defined":{"name":"PoolUpdatedData"}}}]}},{"name":"Vault","type":{"kind":"struct","fields":[{"name":"admin","type":"pubkey"},{"name":"withdraw_authority","docs":["PDA of pool programs seeded by vault address"],"type":"pubkey"},{"name":"withdraw_authority_bump","docs":["bump seed of withdraw_authority PDA"],"type":"u8"},{"name":"authority_bump","docs":["bump seed of vault_authority PDA"],"type":"u8"},{"name":"is_active","type":"bool"},{"name":"beneficiary","type":"pubkey"},{"name":"beneficiary_fee","type":"u64"},{"name":"pending_admin","type":{"option":"pubkey"}}]}}]} diff --git a/migration/khadhroony-bot2-reference/idls/streamflow.strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m.json b/migration/khadhroony-bot2-reference/idls/streamflow.strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m.json new file mode 100644 index 0000000..3068154 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/streamflow.strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"timelock","instructions":[{"name":"create","accounts":[{"name":"sender","isMut":true,"isSigner":true},{"name":"senderTokens","isMut":true,"isSigner":false},{"name":"recipient","isMut":true,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":true},{"name":"escrowTokens","isMut":true,"isSigner":false},{"name":"recipientTokens","isMut":true,"isSigner":false},{"name":"streamflowTreasury","isMut":true,"isSigner":false},{"name":"streamflowTreasuryTokens","isMut":true,"isSigner":false},{"name":"withdrawor","isMut":true,"isSigner":false},{"name":"partner","isMut":true,"isSigner":false},{"name":"partnerTokens","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"feeOracle","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"timelockProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"startTime","type":"u64"},{"name":"netAmountDeposited","type":"u64"},{"name":"period","type":"u64"},{"name":"amountPerPeriod","type":"u64"},{"name":"cliff","type":"u64"},{"name":"cliffAmount","type":"u64"},{"name":"cancelableBySender","type":"bool"},{"name":"cancelableByRecipient","type":"bool"},{"name":"automaticWithdrawal","type":"bool"},{"name":"transferableBySender","type":"bool"},{"name":"transferableByRecipient","type":"bool"},{"name":"canTopup","type":"bool"},{"name":"streamName","type":{"array":["u8",64]}},{"name":"withdrawFrequency","type":"u64"},{"name":"pausable","type":{"option":"bool"}},{"name":"canUpdateRate","type":{"option":"bool"}}]},{"name":"createUnchecked","accounts":[{"name":"sender","isMut":true,"isSigner":true},{"name":"senderTokens","isMut":true,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":false},{"name":"escrowTokens","isMut":true,"isSigner":false},{"name":"withdrawor","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"feeOracle","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"timelockProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"startTime","type":"u64"},{"name":"netAmountDeposited","type":"u64"},{"name":"period","type":"u64"},{"name":"amountPerPeriod","type":"u64"},{"name":"cliff","type":"u64"},{"name":"cliffAmount","type":"u64"},{"name":"cancelableBySender","type":"bool"},{"name":"cancelableByRecipient","type":"bool"},{"name":"automaticWithdrawal","type":"bool"},{"name":"transferableBySender","type":"bool"},{"name":"transferableByRecipient","type":"bool"},{"name":"canTopup","type":"bool"},{"name":"streamName","type":{"array":["u8",64]}},{"name":"withdrawFrequency","type":"u64"},{"name":"recipient","type":"publicKey"},{"name":"partner","type":"publicKey"},{"name":"pausable","type":"bool"},{"name":"canUpdateRate","type":"bool"}]},{"name":"createUncheckedWithPayer","accounts":[{"name":"payer","isMut":true,"isSigner":true},{"name":"sender","isMut":true,"isSigner":true},{"name":"senderTokens","isMut":true,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":false},{"name":"escrowTokens","isMut":true,"isSigner":false},{"name":"withdrawor","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"feeOracle","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"timelockProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"startTime","type":"u64"},{"name":"netAmountDeposited","type":"u64"},{"name":"period","type":"u64"},{"name":"amountPerPeriod","type":"u64"},{"name":"cliff","type":"u64"},{"name":"cliffAmount","type":"u64"},{"name":"cancelableBySender","type":"bool"},{"name":"cancelableByRecipient","type":"bool"},{"name":"automaticWithdrawal","type":"bool"},{"name":"transferableBySender","type":"bool"},{"name":"transferableByRecipient","type":"bool"},{"name":"canTopup","type":"bool"},{"name":"streamName","type":{"array":["u8",64]}},{"name":"withdrawFrequency","type":"u64"},{"name":"recipient","type":"publicKey"},{"name":"partner","type":"publicKey"},{"name":"pausable","type":"bool"},{"name":"canUpdateRate","type":"bool"}]},{"name":"withdraw","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"recipient","isMut":true,"isSigner":false},{"name":"recipientTokens","isMut":true,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":false},{"name":"escrowTokens","isMut":true,"isSigner":false},{"name":"streamflowTreasury","isMut":true,"isSigner":false},{"name":"streamflowTreasuryTokens","isMut":true,"isSigner":false},{"name":"partner","isMut":true,"isSigner":false},{"name":"partnerTokens","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"cancel","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"sender","isMut":true,"isSigner":false},{"name":"senderTokens","isMut":true,"isSigner":false},{"name":"recipient","isMut":true,"isSigner":false},{"name":"recipientTokens","isMut":true,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":false},{"name":"escrowTokens","isMut":true,"isSigner":false},{"name":"streamflowTreasury","isMut":true,"isSigner":false},{"name":"streamflowTreasuryTokens","isMut":true,"isSigner":false},{"name":"partner","isMut":true,"isSigner":false},{"name":"partnerTokens","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"transferRecipient","accounts":[{"name":"authority","isMut":true,"isSigner":true},{"name":"newRecipient","isMut":true,"isSigner":false},{"name":"newRecipientTokens","isMut":true,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"topup","accounts":[{"name":"sender","isMut":true,"isSigner":true},{"name":"senderTokens","isMut":true,"isSigner":false},{"name":"metadata","isMut":true,"isSigner":false},{"name":"escrowTokens","isMut":true,"isSigner":false},{"name":"streamflowTreasury","isMut":true,"isSigner":false},{"name":"streamflowTreasuryTokens","isMut":true,"isSigner":false},{"name":"withdrawor","isMut":true,"isSigner":false},{"name":"partner","isMut":true,"isSigner":false},{"name":"partnerTokens","isMut":true,"isSigner":false},{"name":"mint","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"pause","accounts":[{"name":"sender","isMut":false,"isSigner":true},{"name":"metadata","isMut":true,"isSigner":false}],"args":[]},{"name":"unpause","accounts":[{"name":"sender","isMut":false,"isSigner":true},{"name":"metadata","isMut":true,"isSigner":false}],"args":[]},{"name":"update","accounts":[{"name":"sender","isMut":true,"isSigner":true},{"name":"metadata","isMut":true,"isSigner":false},{"name":"withdrawor","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"enableAutomaticWithdrawal","type":{"option":"bool"}},{"name":"withdrawFrequency","type":{"option":"u64"}},{"name":"amountPerPeriod","type":{"option":"u64"}}]}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/tensor-cnft.TCMPhJdwDryooaGtiocG1u3xcYbRpiJzb283XfCZsDp.json b/migration/khadhroony-bot2-reference/idls/tensor-cnft.TCMPhJdwDryooaGtiocG1u3xcYbRpiJzb283XfCZsDp.json new file mode 100644 index 0000000..088d031 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/tensor-cnft.TCMPhJdwDryooaGtiocG1u3xcYbRpiJzb283XfCZsDp.json @@ -0,0 +1 @@ +{"version":"2.0.1","name":"tcomp","constants":[{"name":"CURRENT_TCOMP_VERSION","type":"u8","value":"1"},{"name":"TCOMP_FEE_BPS","type":"u16","value":"150"},{"name":"MAX_EXPIRY_SEC","type":"i64","value":"31536000"},{"name":"MAKER_BROKER_PCT","type":"u16","value":"0"},{"name":"LIST_STATE_SIZE","type":{"defined":"usize"},"value":"8 + 1 + 1 + (32 * 2) + 8 + 33 + 8 + (33 * 2) + 128"},{"name":"BID_STATE_SIZE","type":{"defined":"usize"},"value":"8 + 1 + 1 + (32 * 2) + 1 + 32 + 2 + 33 + 4 * 2 + 8 + 33 + 8 + (33 * 3) + 128"}],"instructions":[{"name":"tcompNoop","accounts":[{"name":"tcompSigner","isMut":false,"isSigner":true}],"args":[{"name":"event","type":{"defined":"TcompEvent"}}]},{"name":"withdrawFees","accounts":[{"name":"tswap","isMut":true,"isSigner":false},{"name":"tcomp","isMut":true,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true,"docs":["We ask also for a signature just to make sure this wallet can actually sign things"]},{"name":"owner","isMut":true,"isSigner":true},{"name":"destination","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"buy","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"listState","isMut":true,"isSigner":false},{"name":"buyer","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"metaHash","type":{"array":["u8",32]}},{"name":"creatorShares","type":"bytes"},{"name":"creatorVerified","type":{"vec":"bool"}},{"name":"sellerFeeBasisPoints","type":"u16"},{"name":"maxAmount","type":"u64"},{"name":"optionalRoyaltyPct","type":{"option":"u16"}}]},{"name":"buySpl","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"tcompAta","isMut":true,"isSigner":false},{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"listState","isMut":true,"isSigner":false},{"name":"buyer","isMut":false,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"payerSource","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":false},{"name":"ownerDest","isMut":true,"isSigner":false},{"name":"currency","isMut":false,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"takerBrokerAta","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBrokerAta","isMut":true,"isSigner":false,"isOptional":true},{"name":"rentDest","isMut":true,"isSigner":false},{"name":"rentPayer","isMut":true,"isSigner":true}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"metaHash","type":{"array":["u8",32]}},{"name":"creatorShares","type":"bytes"},{"name":"creatorVerified","type":{"vec":"bool"}},{"name":"sellerFeeBasisPoints","type":"u16"},{"name":"maxAmount","type":"u64"},{"name":"optionalRoyaltyPct","type":{"option":"u16"}}]},{"name":"buyCore","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"listState","isMut":true,"isSigner":false},{"name":"asset","isMut":true,"isSigner":false},{"name":"collection","isMut":false,"isSigner":false,"isOptional":true},{"name":"buyer","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"rentDest","isMut":true,"isSigner":false},{"name":"mplCoreProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"maxAmount","type":"u64"}]},{"name":"list","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"owner","isMut":false,"isSigner":false},{"name":"delegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"listState","isMut":true,"isSigner":false},{"name":"rentPayer","isMut":true,"isSigner":true}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}},{"name":"amount","type":"u64"},{"name":"expireInSec","type":{"option":"u64"}},{"name":"currency","type":{"option":"publicKey"}},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"makerBroker","type":{"option":"publicKey"}}]},{"name":"delist","accounts":[{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"listState","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}}]},{"name":"edit","accounts":[{"name":"listState","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"tcompProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"},{"name":"expireInSec","type":{"option":"u64"}},{"name":"currency","type":{"option":"publicKey"}},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"makerBroker","type":{"option":"publicKey"}}]},{"name":"listCore","accounts":[{"name":"asset","isMut":true,"isSigner":false},{"name":"collection","isMut":false,"isSigner":false,"isOptional":true},{"name":"listState","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":true},{"name":"mplCoreProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true}],"args":[{"name":"amount","type":"u64"},{"name":"expireInSec","type":{"option":"u64"}},{"name":"currency","type":{"option":"publicKey"}},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"makerBroker","type":{"option":"publicKey"}}]},{"name":"delistCore","accounts":[{"name":"asset","isMut":true,"isSigner":false},{"name":"collection","isMut":false,"isSigner":false,"isOptional":true},{"name":"owner","isMut":true,"isSigner":true},{"name":"listState","isMut":true,"isSigner":false},{"name":"mplCoreProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"rentDest","isMut":true,"isSigner":true}],"args":[]},{"name":"bid","accounts":[{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"rentPayer","isMut":true,"isSigner":true}],"args":[{"name":"bidId","type":"publicKey"},{"name":"target","type":{"defined":"Target"}},{"name":"targetId","type":"publicKey"},{"name":"field","type":{"option":{"defined":"Field"}}},{"name":"fieldId","type":{"option":"publicKey"}},{"name":"amount","type":"u64"},{"name":"quantity","type":"u32"},{"name":"expireInSec","type":{"option":"u64"}},{"name":"currency","type":{"option":"publicKey"}},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"makerBroker","type":{"option":"publicKey"}}]},{"name":"cancelBid","accounts":[{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[]},{"name":"closeExpiredBid","accounts":[{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[]},{"name":"closeExpiredListing","accounts":[{"name":"listState","isMut":true,"isSigner":false},{"name":"owner","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"dataHash","type":{"array":["u8",32]}},{"name":"creatorHash","type":{"array":["u8",32]}}]},{"name":"closeExpiredListingCore","accounts":[{"name":"listState","isMut":true,"isSigner":false},{"name":"asset","isMut":true,"isSigner":false},{"name":"collection","isMut":false,"isSigner":false,"isOptional":true},{"name":"owner","isMut":false,"isSigner":false},{"name":"mplCoreProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[]},{"name":"takeBidMetaHash","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"seller","isMut":true,"isSigner":false},{"name":"delegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tensorswapProgram","isMut":false,"isSigner":false},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"whitelist","isMut":false,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"metaHash","type":{"array":["u8",32]}},{"name":"creatorShares","type":"bytes"},{"name":"creatorVerified","type":{"vec":"bool"}},{"name":"sellerFeeBasisPoints","type":"u16"},{"name":"minAmount","type":"u64"},{"name":"optionalRoyaltyPct","type":{"option":"u16"}}]},{"name":"takeBidFullMeta","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"treeAuthority","isMut":false,"isSigner":false},{"name":"seller","isMut":true,"isSigner":false},{"name":"delegate","isMut":false,"isSigner":false},{"name":"merkleTree","isMut":true,"isSigner":false},{"name":"logWrapper","isMut":false,"isSigner":false},{"name":"compressionProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"bubblegumProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tensorswapProgram","isMut":false,"isSigner":false},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"whitelist","isMut":false,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"nonce","type":"u64"},{"name":"index","type":"u32"},{"name":"root","type":{"array":["u8",32]}},{"name":"metaArgs","type":{"defined":"TMetadataArgs"}},{"name":"minAmount","type":"u64"},{"name":"optionalRoyaltyPct","type":{"option":"u16"}}]},{"name":"takeBidLegacy","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"seller","isMut":true,"isSigner":true},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"whitelist","isMut":false,"isSigner":false},{"name":"nftSellerAcc","isMut":true,"isSigner":false},{"name":"nftMint","isMut":false,"isSigner":false},{"name":"nftMetadata","isMut":true,"isSigner":false},{"name":"ownerAtaAcc","isMut":true,"isSigner":false},{"name":"nftEdition","isMut":false,"isSigner":false},{"name":"ownerTokenRecord","isMut":true,"isSigner":false},{"name":"destTokenRecord","isMut":true,"isSigner":false},{"name":"pnftShared","accounts":[{"name":"tokenMetadataProgram","isMut":false,"isSigner":false},{"name":"instructions","isMut":false,"isSigner":false},{"name":"authorizationRulesProgram","isMut":false,"isSigner":false}]},{"name":"nftEscrow","isMut":true,"isSigner":false,"docs":["Implicitly checked via transfer. Will fail if wrong account"]},{"name":"tempEscrowTokenRecord","isMut":true,"isSigner":false},{"name":"authRules","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tensorswapProgram","isMut":false,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"mintProof","isMut":false,"isSigner":false,"docs":["intentionally not deserializing, it would be dummy in the case of VOC/FVC based verification"]},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"minAmount","type":"u64"},{"name":"optionalRoyaltyPct","type":{"option":"u16"}},{"name":"rulesAccPresent","type":"bool"},{"name":"authorizationData","type":{"option":{"defined":"AuthorizationDataLocal"}}}]},{"name":"takeBidT22","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"seller","isMut":true,"isSigner":true},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"whitelist","isMut":false,"isSigner":false},{"name":"nftSellerAcc","isMut":true,"isSigner":false},{"name":"nftMint","isMut":false,"isSigner":false},{"name":"ownerAtaAcc","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tensorswapProgram","isMut":false,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"mintProof","isMut":false,"isSigner":false,"docs":["intentionally not deserializing, it would be dummy in the case of VOC/FVC based verification"]},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"minAmount","type":"u64"}]},{"name":"takeBidWns","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"seller","isMut":true,"isSigner":true},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"whitelist","isMut":false,"isSigner":false},{"name":"nftSellerAcc","isMut":true,"isSigner":false},{"name":"nftMint","isMut":false,"isSigner":false},{"name":"ownerAtaAcc","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"associatedTokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tensorswapProgram","isMut":false,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"mintProof","isMut":false,"isSigner":false,"docs":["intentionally not deserializing, it would be dummy in the case of VOC/FVC based verification"]},{"name":"rentDest","isMut":true,"isSigner":false},{"name":"approveAccount","isMut":true,"isSigner":false},{"name":"distribution","isMut":true,"isSigner":false},{"name":"wnsProgram","isMut":false,"isSigner":false},{"name":"distributionProgram","isMut":false,"isSigner":false},{"name":"extraMetas","isMut":false,"isSigner":false}],"args":[{"name":"minAmount","type":"u64"}]},{"name":"takeBidCore","accounts":[{"name":"tcomp","isMut":true,"isSigner":false},{"name":"seller","isMut":true,"isSigner":true},{"name":"bidState","isMut":true,"isSigner":false},{"name":"owner","isMut":true,"isSigner":false},{"name":"takerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"makerBroker","isMut":true,"isSigner":false,"isOptional":true},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"whitelist","isMut":false,"isSigner":false},{"name":"asset","isMut":true,"isSigner":false},{"name":"collection","isMut":false,"isSigner":false,"isOptional":true},{"name":"mplCoreProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tcompProgram","isMut":false,"isSigner":false},{"name":"tensorswapProgram","isMut":false,"isSigner":false},{"name":"cosigner","isMut":false,"isSigner":true},{"name":"mintProof","isMut":false,"isSigner":false,"docs":["intentionally not deserializing, it would be dummy in the case of VOC/FVC based verification"]},{"name":"rentDest","isMut":true,"isSigner":false}],"args":[{"name":"minAmount","type":"u64"}]}],"accounts":[{"name":"ListState","type":{"kind":"struct","fields":[{"name":"version","type":"u8"},{"name":"bump","type":{"array":["u8",1]}},{"name":"owner","type":"publicKey"},{"name":"assetId","type":"publicKey"},{"name":"amount","type":"u64"},{"name":"currency","type":{"option":"publicKey"}},{"name":"expiry","type":"i64"},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"makerBroker","type":{"option":"publicKey"}},{"name":"rentPayer","docs":["owner is the rent payer when this is PublicKey::default"],"type":"publicKey"},{"name":"reserved","type":{"array":["u8",32]}},{"name":"reserved1","type":{"array":["u8",64]}}]}},{"name":"BidState","type":{"kind":"struct","fields":[{"name":"version","type":"u8"},{"name":"bump","type":{"array":["u8",1]}},{"name":"owner","type":"publicKey"},{"name":"bidId","docs":["Randomly picked pubkey used in bid seeds. To avoid dangling bids can use assetId here."],"type":"publicKey"},{"name":"target","type":{"defined":"Target"}},{"name":"targetId","type":"publicKey"},{"name":"field","type":{"option":{"defined":"Field"}}},{"name":"fieldId","type":{"option":"publicKey"}},{"name":"quantity","type":"u32"},{"name":"filledQuantity","type":"u32"},{"name":"amount","type":"u64"},{"name":"currency","type":{"option":"publicKey"}},{"name":"expiry","type":"i64"},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"makerBroker","type":{"option":"publicKey"}},{"name":"margin","type":{"option":"publicKey"}},{"name":"updatedAt","type":"i64"},{"name":"cosigner","type":"publicKey"},{"name":"rentPayer","docs":["owner is the rent payer when this is PublicKey::default"],"type":"publicKey"},{"name":"reserved","type":{"array":["u8",8]}},{"name":"reserved1","type":{"array":["u8",16]}},{"name":"reserved2","type":{"array":["u8",32]}}]}}],"types":[{"name":"TUses","type":{"kind":"struct","fields":[{"name":"useMethod","type":{"defined":"TUseMethod"}},{"name":"remaining","type":"u64"},{"name":"total","type":"u64"}]}},{"name":"TCollection","type":{"kind":"struct","fields":[{"name":"verified","type":"bool"},{"name":"key","type":"publicKey"}]}},{"name":"TMetadataArgs","type":{"kind":"struct","fields":[{"name":"name","docs":["The name of the asset"],"type":"string"},{"name":"symbol","docs":["The symbol for the asset"],"type":"string"},{"name":"uri","docs":["URI pointing to JSON representing the asset"],"type":"string"},{"name":"sellerFeeBasisPoints","docs":["Royalty basis points that goes to creators in secondary sales (0-10000)"],"type":"u16"},{"name":"primarySaleHappened","type":"bool"},{"name":"isMutable","type":"bool"},{"name":"editionNonce","docs":["nonce for easy calculation of editions, if present"],"type":{"option":"u8"}},{"name":"tokenStandard","docs":["Since we cannot easily change Metadata, we add the new DataV2 fields here at the end."],"type":{"option":{"defined":"TTokenStandard"}}},{"name":"collection","docs":["Collection"],"type":{"option":{"defined":"TCollection"}}},{"name":"uses","docs":["Uses"],"type":{"option":{"defined":"TUses"}}},{"name":"tokenProgramVersion","type":{"defined":"TTokenProgramVersion"}},{"name":"creatorShares","type":"bytes"},{"name":"creatorVerified","type":{"vec":"bool"}}]}},{"name":"MakeEvent","type":{"kind":"struct","fields":[{"name":"maker","type":"publicKey"},{"name":"bidId","type":{"option":"publicKey"}},{"name":"target","type":{"defined":"Target"}},{"name":"targetId","type":"publicKey"},{"name":"field","type":{"option":{"defined":"Field"}}},{"name":"fieldId","type":{"option":"publicKey"}},{"name":"amount","type":"u64"},{"name":"quantity","type":"u32"},{"name":"currency","type":{"option":"publicKey"}},{"name":"expiry","type":"i64"},{"name":"privateTaker","type":{"option":"publicKey"}},{"name":"assetId","type":{"option":"publicKey"}}]}},{"name":"TakeEvent","type":{"kind":"struct","fields":[{"name":"taker","type":"publicKey"},{"name":"bidId","type":{"option":"publicKey"}},{"name":"target","type":{"defined":"Target"}},{"name":"targetId","type":"publicKey"},{"name":"field","type":{"option":{"defined":"Field"}}},{"name":"fieldId","type":{"option":"publicKey"}},{"name":"amount","type":"u64"},{"name":"quantity","type":"u32"},{"name":"tcompFee","type":"u64"},{"name":"takerBrokerFee","type":"u64"},{"name":"makerBrokerFee","type":"u64"},{"name":"creatorFee","type":"u64"},{"name":"currency","type":{"option":"publicKey"}},{"name":"assetId","type":{"option":"publicKey"}}]}},{"name":"AuthorizationDataLocal","type":{"kind":"struct","fields":[{"name":"payload","type":{"vec":{"defined":"TaggedPayload"}}}]}},{"name":"TaggedPayload","type":{"kind":"struct","fields":[{"name":"name","type":"string"},{"name":"payload","type":{"defined":"PayloadTypeLocal"}}]}},{"name":"SeedsVecLocal","type":{"kind":"struct","fields":[{"name":"seeds","docs":["The vector of derivation seeds."],"type":{"vec":"bytes"}}]}},{"name":"ProofInfoLocal","type":{"kind":"struct","fields":[{"name":"proof","docs":["The merkle proof."],"type":{"vec":{"array":["u8",32]}}}]}},{"name":"TTokenProgramVersion","type":{"kind":"enum","variants":[{"name":"Original"},{"name":"Token2022"}]}},{"name":"TTokenStandard","type":{"kind":"enum","variants":[{"name":"NonFungible"},{"name":"FungibleAsset"},{"name":"Fungible"},{"name":"NonFungibleEdition"}]}},{"name":"TUseMethod","type":{"kind":"enum","variants":[{"name":"Burn"},{"name":"Multiple"},{"name":"Single"}]}},{"name":"TcompEvent","type":{"kind":"enum","variants":[{"name":"Maker","fields":[{"defined":"MakeEvent"}]},{"name":"Taker","fields":[{"defined":"TakeEvent"}]}]}},{"name":"PayloadTypeLocal","type":{"kind":"enum","variants":[{"name":"Pubkey","fields":["publicKey"]},{"name":"Seeds","fields":[{"defined":"SeedsVecLocal"}]},{"name":"MerkleProof","fields":[{"defined":"ProofInfoLocal"}]},{"name":"Number","fields":["u64"]}]}},{"name":"Target","type":{"kind":"enum","variants":[{"name":"AssetId"},{"name":"Whitelist"}]}},{"name":"Field","type":{"kind":"enum","variants":[{"name":"Name"}]}}],"errors":[{"code":6100,"name":"ArithmeticError","msg":"arithmetic error"},{"code":6101,"name":"ExpiryTooLarge","msg":"expiry too large"},{"code":6102,"name":"BadOwner","msg":"bad owner"},{"code":6103,"name":"BadListState","msg":"bad list state"},{"code":6104,"name":"BadRoyaltiesPct","msg":"royalties pct must be between 0 and 100"},{"code":6105,"name":"PriceMismatch","msg":"price mismatch"},{"code":6106,"name":"CreatorMismatch","msg":"creator mismatch"},{"code":6107,"name":"InsufficientBalance","msg":"insufficient balance"},{"code":6108,"name":"BidExpired","msg":"bid has expired"},{"code":6109,"name":"TakerNotAllowed","msg":"taker not allowed"},{"code":6110,"name":"BadBidField","msg":"cannot pass bid field"},{"code":6111,"name":"BidNotYetExpired","msg":"bid not yet expired"},{"code":6112,"name":"BadMargin","msg":"bad margin"},{"code":6113,"name":"WrongIxForBidTarget","msg":"wrong ix for bid target called"},{"code":6114,"name":"WrongTargetId","msg":"wrong target id"},{"code":6115,"name":"MissingFvc","msg":"creator array missing first verified creator"},{"code":6116,"name":"MissingCollection","msg":"metadata missing collection"},{"code":6117,"name":"CannotModifyTarget","msg":"cannot modify bid target, create a new bid"},{"code":6118,"name":"TargetIdMustEqualBidId","msg":"target id and bid id must be the same for single bids"},{"code":6119,"name":"CurrencyNotYetEnabled","msg":"currency not yet enabled"},{"code":6120,"name":"MakerBrokerNotYetEnabled","msg":"maker broker not yet enabled"},{"code":6121,"name":"OptionalRoyaltiesNotYetEnabled","msg":"optional royalties not yet enabled"},{"code":6122,"name":"WrongStateVersion","msg":"wrong state version"},{"code":6123,"name":"WrongBidFieldId","msg":"wrong field id"},{"code":6124,"name":"BrokerMismatch","msg":"broker mismatch"},{"code":6125,"name":"AssetIdMismatch","msg":"asset id mismatch"},{"code":6126,"name":"ListingExpired","msg":"listing has expired"},{"code":6127,"name":"ListingNotYetExpired","msg":"listing not yet expired"},{"code":6128,"name":"BadQuantity","msg":"bad quantity passed in"},{"code":6129,"name":"BidFullyFilled","msg":"bid fully filled"},{"code":6130,"name":"BadWhitelist","msg":"bad whitelist"},{"code":6131,"name":"ForbiddenCollection","msg":"forbidden collection"},{"code":6132,"name":"BadCosigner","msg":"bad cosigner"},{"code":6133,"name":"BadMintProof","msg":"bad mint proof"},{"code":6134,"name":"CurrencyMismatch","msg":"Currency mismatch"},{"code":6135,"name":"BidBalanceNotEmptied","msg":"The bid balance was not emptied"},{"code":6136,"name":"BadRentDest","msg":"Bad rent dest."},{"code":6137,"name":"CurrencyNotYetWhitelisted","msg":"currency not yet whitelisted"},{"code":6138,"name":"MakerBrokerNotYetWhitelisted","msg":"maker broker not yet whitelisted"},{"code":6139,"name":"WrongTokenRecordDerivation","msg":"token record derivation is wrong"}]} diff --git a/migration/khadhroony-bot2-reference/idls/vertigo.vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ.json b/migration/khadhroony-bot2-reference/idls/vertigo.vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ.json new file mode 100644 index 0000000..4998453 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/vertigo.vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ.json @@ -0,0 +1 @@ +{"address":"vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ","metadata":{"name":"amm","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"buy","discriminator":[102,6,61,18,1,218,235,234],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"owner"},{"kind":"account","path":"mint_a"},{"kind":"account","path":"mint_b"}]}},{"name":"user","signer":true},{"name":"owner"},{"name":"mint_a"},{"name":"mint_b"},{"name":"user_ta_a","docs":["Can be any token account owned by the user for this mint"],"writable":true},{"name":"user_ta_b","docs":["Can be any token account owned by the user for this mint"],"writable":true},{"name":"vault_a","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"mint_a"}]}},{"name":"vault_b","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"mint_b"}]}},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"program","address":"vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ"}],"args":[{"name":"params","type":{"defined":{"name":"SwapParams"}}}]},{"name":"claim","discriminator":[62,198,214,193,213,159,108,210],"accounts":[{"name":"pool","writable":true},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"claimer","signer":true},{"name":"mint_a","docs":["The token mint of the tokens used in the pool."]},{"name":"vault_a","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"mint_a"}]}},{"name":"receiver_ta_a","docs":["Can be any token account owned by the user for this mint"],"writable":true},{"name":"token_program_a"}],"args":[]},{"name":"create","discriminator":[24,30,200,40,5,28,7,119],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"owner","signer":true},{"name":"token_wallet_authority","signer":true},{"name":"mint_a"},{"name":"mint_b"},{"name":"token_wallet_b","docs":["Token wallet that funds the pool with token B."],"writable":true},{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"owner"},{"kind":"account","path":"mint_a"},{"kind":"account","path":"mint_b"}]}},{"name":"vault_a","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"mint_a"}]}},{"name":"vault_b","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"mint_b"}]}},{"name":"token_program_a","docs":["Required programs and sysvars"]},{"name":"token_program_b"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"params","type":{"defined":{"name":"CreateParams"}}}]},{"name":"quote_buy","discriminator":[83,9,231,110,146,31,40,12],"accounts":[{"name":"pool","pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"owner"},{"kind":"account","path":"mint_a"},{"kind":"account","path":"mint_b"}]}},{"name":"owner"},{"name":"user"},{"name":"mint_a"},{"name":"mint_b"},{"name":"program","address":"vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ"}],"args":[{"name":"params","type":{"defined":{"name":"SwapParams"}}}],"returns":{"defined":{"name":"SwapResult"}}},{"name":"quote_sell","discriminator":[5,178,49,206,140,231,131,145],"accounts":[{"name":"pool","pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"owner"},{"kind":"account","path":"mint_a"},{"kind":"account","path":"mint_b"}]}},{"name":"owner"},{"name":"user"},{"name":"mint_a"},{"name":"mint_b"},{"name":"program","address":"vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ"}],"args":[{"name":"params","type":{"defined":{"name":"SwapParams"}}}],"returns":{"defined":{"name":"SwapResult"}}},{"name":"sell","discriminator":[51,230,133,164,1,127,131,173],"accounts":[{"name":"pool","writable":true,"pda":{"seeds":[{"kind":"const","value":[112,111,111,108]},{"kind":"account","path":"owner"},{"kind":"account","path":"mint_a"},{"kind":"account","path":"mint_b"}]}},{"name":"user","signer":true},{"name":"owner"},{"name":"mint_a"},{"name":"mint_b"},{"name":"user_ta_a","docs":["Can be any token account owned by the user for this mint"],"writable":true},{"name":"user_ta_b","docs":["Can be any token account owned by the user for this mint"],"writable":true},{"name":"vault_a","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"mint_a"}]}},{"name":"vault_b","writable":true,"pda":{"seeds":[{"kind":"account","path":"pool"},{"kind":"account","path":"mint_b"}]}},{"name":"token_program_a"},{"name":"token_program_b"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"program","address":"vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ"}],"args":[{"name":"params","type":{"defined":{"name":"SwapParams"}}}]}],"accounts":[{"name":"Pool","discriminator":[241,154,109,4,17,177,109,188]}],"events":[{"name":"BuyEvent","discriminator":[103,244,82,31,44,245,119,119]},{"name":"PoolCreated","discriminator":[202,44,41,88,104,220,157,82]},{"name":"SellEvent","discriminator":[62,47,55,10,165,3,220,42]}],"errors":[{"code":6000,"name":"PoolDisabled","msg":"Pool Disabled"},{"code":6001,"name":"OwnerMustBeSystemAccount","msg":"Owner must be a system account (wallet), not a PDA"},{"code":6002,"name":"InvalidFeeDecay","msg":"Invalid Fee Decay"},{"code":6003,"name":"InvalidInitialTokenReserves","msg":"Initial token reserves must be greater than 0"},{"code":6004,"name":"InvalidMint","msg":"Invalid Mint"},{"code":6005,"name":"InvalidTokenAccount","msg":"Invalid Token Account"},{"code":6006,"name":"InsufficientFunds","msg":"Insufficient Funds"},{"code":6007,"name":"InvalidShift","msg":"Shift must be greater than 0"},{"code":6008,"name":"InvalidFees","msg":"Fees must be between 0 and 10000 basis points"},{"code":6009,"name":"MathOverflow","msg":"MathOverflow"},{"code":6010,"name":"InsufficientOutput","msg":"Insufficient output"},{"code":6011,"name":"InsufficientInput","msg":"Insufficient input"},{"code":6012,"name":"IllegalClaimant","msg":"Illegal Claimant"},{"code":6013,"name":"PoolEmpty","msg":"Pool Empty"}],"types":[{"name":"BuyEvent","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"amount_a","type":"u64"},{"name":"amount_b","type":"u64"},{"name":"new_reserves_a","type":"u128"},{"name":"new_reserves_b","type":"u128"},{"name":"fee_a","type":"u64"}]}},{"name":"CreateParams","type":{"kind":"struct","fields":[{"name":"shift","type":"u128"},{"name":"initial_token_b_reserves","type":"u64"},{"name":"fee_params","type":{"defined":{"name":"FeeParams"}}}]}},{"name":"FeeParams","type":{"kind":"struct","fields":[{"name":"normalization_period","docs":["The normalization period in slots.","During this period, the fee will decay from 100% to the base fee."],"type":"u64"},{"name":"decay","docs":["Normalization period fee decay rate.","Higher values decay faster."],"type":"f64"},{"name":"reference","docs":["Reference slot for the fee calculation."],"type":"u64"},{"name":"royalties_bps","docs":["Royalties in basis points."],"type":"u16"},{"name":"privileged_swapper","docs":["Number of fee exempt buys."],"type":{"option":"pubkey"}}]}},{"name":"Pool","type":{"kind":"struct","fields":[{"name":"enabled","type":"bool"},{"name":"owner","type":"pubkey"},{"name":"mint_a","type":"pubkey"},{"name":"mint_b","type":"pubkey"},{"name":"token_a_reserves","type":"u128"},{"name":"token_b_reserves","type":"u128"},{"name":"shift","type":"u128"},{"name":"royalties","type":"u64"},{"name":"vertigo_fees","type":"u64"},{"name":"bump","type":"u8"},{"name":"fee_params","type":{"defined":{"name":"FeeParams"}}}]}},{"name":"PoolCreated","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"mint_a","type":"pubkey"},{"name":"mint_b","type":"pubkey"},{"name":"owner","type":"pubkey"},{"name":"initial_token_reserves","type":"u64"},{"name":"shift","type":"u128"},{"name":"fee_params","type":{"defined":{"name":"FeeParams"}}}]}},{"name":"SellEvent","type":{"kind":"struct","fields":[{"name":"pool","type":"pubkey"},{"name":"user","type":"pubkey"},{"name":"amount_a","type":"u64"},{"name":"amount_b","type":"u64"},{"name":"new_reserves_a","type":"u128"},{"name":"new_reserves_b","type":"u128"},{"name":"fee_a","type":"u64"}]}},{"name":"SwapParams","type":{"kind":"struct","fields":[{"name":"amount","type":"u64"},{"name":"limit","type":"u64"}]}},{"name":"SwapResult","type":{"kind":"struct","fields":[{"name":"new_reserves_a","type":"u128"},{"name":"new_reserves_b","type":"u128"},{"name":"amount_a","type":"u64"},{"name":"amount_b","type":"u64"},{"name":"fee_a","type":"u64"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/virtuals.5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki.json b/migration/khadhroony-bot2-reference/idls/virtuals.5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki.json new file mode 100644 index 0000000..c0c7861 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/virtuals.5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki.json @@ -0,0 +1 @@ +{"address":"5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki","metadata":{"name":"virtuals_program","version":"0.1.0","spec":"0.1.0","description":"Created with Anchor"},"instructions":[{"name":"buy","discriminator":[102,6,61,18,1,218,235,234],"accounts":[{"name":"user","signer":true},{"name":"vpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,112,111,111,108]},{"kind":"account","path":"token_mint"}]}},{"name":"token_mint"},{"name":"user_virtuals_ata","writable":true},{"name":"user_token_ata","writable":true},{"name":"vpool_token_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"platform_prototype","writable":true,"address":"933jV351WDG23QTcHPqLFJxyYRrEPWRTR3qoPWi3jwEL"},{"name":"platform_prototype_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"platform_prototype"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"const","value":[40,82,144,230,223,30,203,194,218,8,182,83,3,69,230,99,143,96,63,54,49,170,42,186,128,143,177,157,236,50,113,76]}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vpool_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"const","value":[40,82,144,230,223,30,203,194,218,8,182,83,3,69,230,99,143,96,63,54,49,170,42,186,128,143,177,157,236,50,113,76]}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"amount","type":"u64"},{"name":"max_amount_out","type":"u64"}]},{"name":"claim_fees","discriminator":[82,251,233,156,12,52,184,202],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"vpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,112,111,111,108]},{"kind":"account","path":"token_mint"}]}},{"name":"virtuals_mint","address":"3iQL8BFS2vE7mww4ehAqQHAsbmRNCrPxizWAT2Zfyr9y"},{"name":"token_mint"},{"name":"vpool_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"virtuals_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vpool_token_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"platform","writable":true,"address":"FxKXqCKXdxPbfYau83ZkGUXgcZWhKBTfAjGg2KzYSopS"},{"name":"platform_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"platform"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"virtuals_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"platform_token_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"platform"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"creator_virtuals_ata","writable":true},{"name":"creator_token_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool.creator","account":"VirtualsPool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"pool","writable":true},{"name":"lp_mint","writable":true},{"name":"lock_escrow","writable":true},{"name":"escrow_vault","writable":true},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"virtuals_vault","writable":true},{"name":"token_vault","writable":true},{"name":"virtuals_token_vault","writable":true},{"name":"token_token_vault","writable":true},{"name":"virtuals_vault_lp_mint","writable":true},{"name":"token_vault_lp_mint","writable":true},{"name":"virtuals_vault_lp","writable":true},{"name":"token_vault_lp","writable":true},{"name":"vault_program","address":"24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"dynamic_amm_program","address":"Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB"}],"args":[]},{"name":"create_meteora_pool","discriminator":[246,254,33,37,225,176,41,232],"accounts":[{"name":"vpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,112,111,111,108]},{"kind":"account","path":"token_mint"}]}},{"name":"meteora_deployer","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,101,111,114,97,95,100,101,112,108,111,121,101,114]}]}},{"name":"meteora_deployer_virtuals_ata","writable":true},{"name":"meteora_deployer_token_ata","writable":true},{"name":"vpool_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"virtuals_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vpool_token_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lock_escrow","writable":true},{"name":"escrow_vault","writable":true},{"name":"pool","writable":true},{"name":"config","address":"B6FT4AHhQJ7BttrC1i223km5bYRUA2nVwBR7k5TVwLtx"},{"name":"lp_mint","writable":true},{"name":"virtuals_mint","address":"3iQL8BFS2vE7mww4ehAqQHAsbmRNCrPxizWAT2Zfyr9y"},{"name":"token_mint"},{"name":"virtuals_vault","writable":true},{"name":"token_vault","writable":true},{"name":"virtuals_token_vault","writable":true},{"name":"token_token_vault","writable":true},{"name":"virtuals_vault_lp_mint","writable":true},{"name":"token_vault_lp_mint","writable":true},{"name":"virtuals_vault_lp","writable":true},{"name":"token_vault_lp","writable":true},{"name":"pool_virtuals_ata","writable":true},{"name":"pool_token_ata","writable":true},{"name":"meteora_deployer_pool_lp","writable":true},{"name":"protocol_virtuals_fee","writable":true},{"name":"protocol_token_fee","writable":true},{"name":"payer","writable":true,"signer":true},{"name":"token_metadata","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"account","path":"metadata_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"account","path":"metadata_program"}}},{"name":"rent","docs":["Rent account."],"address":"SysvarRent111111111111111111111111111111111"},{"name":"mint_metadata","writable":true},{"name":"metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"vault_program","address":"24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi"},{"name":"token_program","docs":["Token program."],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Associated token program."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["System program."],"address":"11111111111111111111111111111111"},{"name":"dynamic_amm_program","address":"Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB"}],"args":[]},{"name":"initialize","docs":["# Initialize Pool","","This is where Virtuals creates a new mint address and pool and mints the initial 1,000,000,000 tokens.","This enabels Virtuals to premine addresses and \\","sell\\\\","them to customers without any delays of mining the","address. While this instruction could be permissioned, it is currently permissionless. As such, we make","sure to handle all the necessary checks such as: address suffix, zero supply, 6 decimals, correct mint","authority."],"discriminator":[175,175,109,31,13,152,155,237],"accounts":[{"name":"payer","writable":true,"signer":true},{"name":"virtuals_mint","address":"3iQL8BFS2vE7mww4ehAqQHAsbmRNCrPxizWAT2Zfyr9y"},{"name":"token_mint","writable":true},{"name":"vpool_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"virtuals_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vpool_token_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,112,111,111,108]},{"kind":"account","path":"token_mint"}]}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]},{"name":"initialize_meteora_accounts","discriminator":[53,12,118,158,253,239,185,214],"accounts":[{"name":"vpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,112,111,111,108]},{"kind":"account","path":"token_mint"}]}},{"name":"meteora_deployer","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,101,111,114,97,95,100,101,112,108,111,121,101,114]}]}},{"name":"meteora_deployer_virtuals_ata","writable":true},{"name":"meteora_deployer_token_ata","writable":true},{"name":"vpool_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"virtuals_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vpool_token_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"lock_escrow","writable":true},{"name":"escrow_vault","writable":true},{"name":"pool","writable":true},{"name":"config","address":"B6FT4AHhQJ7BttrC1i223km5bYRUA2nVwBR7k5TVwLtx"},{"name":"lp_mint","writable":true},{"name":"virtuals_mint","address":"3iQL8BFS2vE7mww4ehAqQHAsbmRNCrPxizWAT2Zfyr9y"},{"name":"token_mint"},{"name":"virtuals_vault","writable":true},{"name":"token_vault","writable":true},{"name":"virtuals_token_vault","writable":true},{"name":"token_token_vault","writable":true},{"name":"virtuals_vault_lp_mint","writable":true},{"name":"token_vault_lp_mint","writable":true},{"name":"virtuals_vault_lp","writable":true},{"name":"token_vault_lp","writable":true},{"name":"pool_virtuals_ata","writable":true},{"name":"pool_token_ata","writable":true},{"name":"meteora_deployer_pool_lp","writable":true},{"name":"protocol_virtuals_fee","writable":true},{"name":"protocol_token_fee","writable":true},{"name":"payer","writable":true,"signer":true},{"name":"token_metadata","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"account","path":"metadata_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"account","path":"metadata_program"}}},{"name":"rent","docs":["Rent account."],"address":"SysvarRent111111111111111111111111111111111"},{"name":"mint_metadata","writable":true},{"name":"metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"vault_program","address":"24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi"},{"name":"token_program","docs":["Token program."],"address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","docs":["Associated token program."],"address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","docs":["System program."],"address":"11111111111111111111111111111111"},{"name":"dynamic_amm_program","address":"Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB"}],"args":[]},{"name":"launch","docs":["","This is where a user comes along and purchases a mint address and pool. The user will provide a name,","symbol and URI containing offchain metadata. The contract will initialize a new onchain metadata","account containing these values, then revoke the mint authority.",""],"discriminator":[153,241,93,225,22,69,74,61],"accounts":[{"name":"creator","writable":true,"signer":true},{"name":"creator_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"creator"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"const","value":[40,82,144,230,223,30,203,194,218,8,182,83,3,69,230,99,143,96,63,54,49,170,42,186,128,143,177,157,236,50,113,76]}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_mint","writable":true},{"name":"platform_prototype","writable":true,"address":"933jV351WDG23QTcHPqLFJxyYRrEPWRTR3qoPWi3jwEL"},{"name":"platform_prototype_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"platform_prototype"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"const","value":[40,82,144,230,223,30,203,194,218,8,182,83,3,69,230,99,143,96,63,54,49,170,42,186,128,143,177,157,236,50,113,76]}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,112,111,111,108]},{"kind":"account","path":"token_mint"}]}},{"name":"token_metadata","writable":true,"pda":{"seeds":[{"kind":"const","value":[109,101,116,97,100,97,116,97]},{"kind":"account","path":"metadata_program"},{"kind":"account","path":"token_mint"}],"program":{"kind":"account","path":"metadata_program"}}},{"name":"metadata_program","address":"metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"},{"name":"rent","address":"SysvarRent111111111111111111111111111111111"}],"args":[{"name":"symbol","type":"string"},{"name":"name","type":"string"},{"name":"uri","type":"string"}]},{"name":"sell","discriminator":[51,230,133,164,1,127,131,173],"accounts":[{"name":"user","signer":true},{"name":"vpool","writable":true,"pda":{"seeds":[{"kind":"const","value":[118,112,111,111,108]},{"kind":"account","path":"token_mint"}]}},{"name":"token_mint"},{"name":"user_virtuals_ata","writable":true},{"name":"user_token_ata","writable":true},{"name":"vpool_token_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"platform_prototype","writable":true,"address":"933jV351WDG23QTcHPqLFJxyYRrEPWRTR3qoPWi3jwEL"},{"name":"platform_prototype_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"platform_prototype"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"const","value":[40,82,144,230,223,30,203,194,218,8,182,83,3,69,230,99,143,96,63,54,49,170,42,186,128,143,177,157,236,50,113,76]}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vpool_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"vpool"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"const","value":[40,82,144,230,223,30,203,194,218,8,182,83,3,69,230,99,143,96,63,54,49,170,42,186,128,143,177,157,236,50,113,76]}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"}],"args":[{"name":"amount","type":"u64"},{"name":"min_amount_out","type":"u64"}]},{"name":"update_pool_creator","discriminator":[113,225,166,185,94,231,96,28],"accounts":[{"name":"creator","writable":true,"signer":true,"relations":["vpool"]},{"name":"new_creator"},{"name":"virtuals_mint","address":"3iQL8BFS2vE7mww4ehAqQHAsbmRNCrPxizWAT2Zfyr9y"},{"name":"token_mint"},{"name":"new_creator_virtuals_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"new_creator"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"virtuals_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"new_creator_token_ata","writable":true,"pda":{"seeds":[{"kind":"account","path":"new_creator"},{"kind":"const","value":[6,221,246,225,215,101,161,147,217,203,225,70,206,235,121,172,28,180,133,237,95,91,55,145,58,140,245,133,126,255,0,169]},{"kind":"account","path":"token_mint"}],"program":{"kind":"const","value":[140,151,37,143,78,36,137,241,187,61,16,41,20,142,13,131,11,90,19,153,218,255,16,132,4,142,123,216,219,233,248,89]}}},{"name":"vpool","pda":{"seeds":[{"kind":"const","value":[118,112,111,111,108]},{"kind":"account","path":"token_mint"}]}},{"name":"token_program","address":"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"},{"name":"associated_token_program","address":"ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"},{"name":"system_program","address":"11111111111111111111111111111111"}],"args":[]}],"accounts":[{"name":"VirtualsPool","discriminator":[71,118,5,203,5,98,135,116]}],"events":[{"name":"BuyEvent","discriminator":[103,244,82,31,44,245,119,119]},{"name":"GraduationEvent","discriminator":[10,246,223,127,48,98,149,55]},{"name":"LaunchEvent","discriminator":[27,193,47,130,115,92,239,94]},{"name":"SellEvent","discriminator":[62,47,55,10,165,3,220,42]}],"errors":[{"code":6000,"name":"InvalidMintAddress","msg":"Invalid mint address"},{"code":6001,"name":"InvalidMintParams","msg":"Invalid mint params"},{"code":6002,"name":"CurveError","msg":"Curve error"},{"code":6003,"name":"InvalidFee","msg":"Fee cannot exceed 100%"},{"code":6004,"name":"NameTooLong","msg":"Name too long. Max length = 20"},{"code":6005,"name":"SymbolTooLong","msg":"Symbol too long. Max length = 10"},{"code":6006,"name":"URITooLong","msg":"URI too long. Max length = 200"},{"code":6007,"name":"InvalidAmount","msg":"Amount must be >0"},{"code":6008,"name":"SlippageExceeded","msg":"Slippage exceeded"},{"code":6009,"name":"InvalidVPoolState","msg":"Invalid vpool state"},{"code":6010,"name":"InvalidName","msg":"Invalid name"},{"code":6011,"name":"InvalidSymbol","msg":"Invalid symbol"},{"code":6012,"name":"InvalidURI","msg":"Invalid URI"}],"types":[{"name":"BuyEvent","type":{"kind":"struct","fields":[{"name":"buy_amount","type":"u64"},{"name":"virtuals_amount","type":"u64"}]}},{"name":"GraduationEvent","type":{"kind":"struct","fields":[{"name":"vpool","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"balance","type":"u64"}]}},{"name":"LaunchEvent","type":{"kind":"struct","fields":[{"name":"vpool","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"creator","type":"pubkey"}]}},{"name":"PoolState","type":{"kind":"enum","variants":[{"name":"Initialized"},{"name":"Active"},{"name":"Graduated"},{"name":"Migrated"}]}},{"name":"SellEvent","type":{"kind":"struct","fields":[{"name":"sell_amount","type":"u64"},{"name":"virtuals_amount","type":"u64"}]}},{"name":"VirtualsPool","type":{"kind":"struct","fields":[{"name":"creator","type":"pubkey"},{"name":"mint","type":"pubkey"},{"name":"virtual_y","type":"u64"},{"name":"graduation_x","type":"u64"},{"name":"state","type":{"defined":{"name":"PoolState"}}},{"name":"bump","type":"u8"}]}}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/woofi.WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb.json b/migration/khadhroony-bot2-reference/idls/woofi.WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb.json new file mode 100644 index 0000000..dd5fa49 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/woofi.WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"woofi","instructions":[{"name":"createConfig","accounts":[{"name":"authority","isMut":true,"isSigner":true},{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"createWooracle","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"feedAccount","isMut":false,"isSigner":false},{"name":"priceUpdate","isMut":false,"isSigner":false},{"name":"quoteTokenMint","isMut":false,"isSigner":false},{"name":"quoteFeedAccount","isMut":false,"isSigner":false},{"name":"quotePriceUpdate","isMut":false,"isSigner":false}],"args":[{"name":"maximumAge","type":"u64"}]},{"name":"setOracleMaximumAge","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"maximumAge","type":"u64"}]},{"name":"setStaleDuration","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"staleDuration","type":"i64"}]},{"name":"setWooBound","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"bound","type":"u64"}]},{"name":"setWooRange","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"rangeMin","type":"u128"},{"name":"rangeMax","type":"u128"}]},{"name":"setWooPrice","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"price","type":"u128"}]},{"name":"setWooCoeff","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"coeff","type":"u64"}]},{"name":"setWooSpread","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"spread","type":"u64"}]},{"name":"setWooAdmin","accounts":[{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"adminAuthority","type":{"vec":"publicKey"}}]},{"name":"setGuardianAdmin","accounts":[{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"guardianAuthority","type":{"vec":"publicKey"}}]},{"name":"setLendingManager","accounts":[{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"lendingManagers","type":{"vec":"publicKey"}}]},{"name":"setSuperchargerVaultWhitelist","accounts":[{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"superchargerVaults","type":{"vec":"publicKey"}}]},{"name":"setWooState","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"price","type":"u128"},{"name":"coeff","type":"u64"},{"name":"spread","type":"u64"}]},{"name":"getPrice","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"priceUpdate","isMut":false,"isSigner":false},{"name":"quotePriceUpdate","isMut":false,"isSigner":false}],"args":[],"returns":{"defined":"GetPriceResult"}},{"name":"createPool","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"quoteTokenMint","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"woopool","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":true},{"name":"wooracle","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"createWooAmmPool","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"wooammpool","isMut":true,"isSigner":false},{"name":"wooracleA","isMut":false,"isSigner":false},{"name":"woopoolA","isMut":false,"isSigner":false},{"name":"wooracleB","isMut":false,"isSigner":false},{"name":"woopoolB","isMut":false,"isSigner":false},{"name":"woopoolQuote","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"setPoolAdmin","accounts":[{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"adminAuthority","type":{"vec":"publicKey"}}]},{"name":"setFeeAdmin","accounts":[{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"feeAuthority","type":{"vec":"publicKey"}}]},{"name":"setPauseRole","accounts":[{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"pauseAuthority","type":{"vec":"publicKey"}}]},{"name":"pause","accounts":[{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[]},{"name":"unpause","accounts":[{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[]},{"name":"setPoolFeeRate","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"woopool","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"feeRate","type":"u16"}]},{"name":"setPoolMaxGamma","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"woopool","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"maxGamma","type":"u128"}]},{"name":"setPoolMaxNotionalSwap","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"woopool","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"maxNotionalSwap","type":"u128"}]},{"name":"setPoolCapBal","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"woopool","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"capBal","type":"u128"}]},{"name":"setPoolMinSwapAmount","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"woopool","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"minSwapAmount","type":"u128"}]},{"name":"tryQuery","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracleFrom","isMut":false,"isSigner":false},{"name":"woopoolFrom","isMut":false,"isSigner":false},{"name":"priceUpdateFrom","isMut":true,"isSigner":false},{"name":"wooracleTo","isMut":false,"isSigner":false},{"name":"woopoolTo","isMut":false,"isSigner":false},{"name":"priceUpdateTo","isMut":true,"isSigner":false},{"name":"quotePriceUpdate","isMut":false,"isSigner":false}],"args":[{"name":"fromAmount","type":"u128"}],"returns":{"defined":"QueryResult"}},{"name":"query","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracleFrom","isMut":false,"isSigner":false},{"name":"woopoolFrom","isMut":false,"isSigner":false},{"name":"tokenVaultFrom","isMut":true,"isSigner":false},{"name":"priceUpdateFrom","isMut":true,"isSigner":false},{"name":"wooracleTo","isMut":false,"isSigner":false},{"name":"woopoolTo","isMut":false,"isSigner":false},{"name":"tokenVaultTo","isMut":false,"isSigner":false},{"name":"priceUpdateTo","isMut":true,"isSigner":false},{"name":"woopoolQuote","isMut":false,"isSigner":false},{"name":"quotePriceUpdate","isMut":false,"isSigner":false},{"name":"quoteTokenVault","isMut":false,"isSigner":false}],"args":[{"name":"fromAmount","type":"u128"},{"name":"minToAmount","type":"u128"}],"returns":{"defined":"QueryResult"}},{"name":"swap","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"payer","isMut":false,"isSigner":true},{"name":"wooracleFrom","isMut":true,"isSigner":false},{"name":"woopoolFrom","isMut":true,"isSigner":false},{"name":"tokenOwnerAccountFrom","isMut":true,"isSigner":false},{"name":"tokenVaultFrom","isMut":true,"isSigner":false},{"name":"priceUpdateFrom","isMut":true,"isSigner":false},{"name":"wooracleTo","isMut":true,"isSigner":false},{"name":"woopoolTo","isMut":true,"isSigner":false},{"name":"tokenOwnerAccountTo","isMut":true,"isSigner":false},{"name":"tokenVaultTo","isMut":true,"isSigner":false},{"name":"priceUpdateTo","isMut":true,"isSigner":false},{"name":"woopoolQuote","isMut":true,"isSigner":false},{"name":"quotePriceUpdate","isMut":false,"isSigner":false},{"name":"quoteTokenVault","isMut":true,"isSigner":false},{"name":"rebateTo","isMut":false,"isSigner":false}],"args":[{"name":"fromAmount","type":"u128"},{"name":"minToAmount","type":"u128"}]},{"name":"deposit","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"tokenOwnerAccount","isMut":true,"isSigner":false},{"name":"woopool","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u128"}]},{"name":"withdraw","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"toTokenAccount","isMut":true,"isSigner":false},{"name":"woopool","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u128"}]},{"name":"claimFee","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"woopool","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"claimFeeToAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"claimFeeAmount","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"woopool","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"claimFeeToAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"claimAmount","type":"u128"}]},{"name":"incaseTokenGotStuck","accounts":[{"name":"tokenMint","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"toTokenAccount","isMut":true,"isSigner":false},{"name":"woopool","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u128"}]},{"name":"setWooconfigNewAuthority","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"wooconfig","isMut":true,"isSigner":false},{"name":"newAuthority","isMut":false,"isSigner":false}],"args":[]},{"name":"claimWooconfigAuthority","accounts":[{"name":"newAuthority","isMut":false,"isSigner":true},{"name":"wooconfig","isMut":true,"isSigner":false}],"args":[]},{"name":"claimWooracleAuthority","accounts":[{"name":"newAuthority","isMut":false,"isSigner":true},{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooracle","isMut":true,"isSigner":false}],"args":[]},{"name":"claimWoopoolAuthority","accounts":[{"name":"newAuthority","isMut":false,"isSigner":true},{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"woopool","isMut":true,"isSigner":false}],"args":[]},{"name":"claimWooammpoolAuthority","accounts":[{"name":"newAuthority","isMut":false,"isSigner":true},{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"wooammpool","isMut":true,"isSigner":false}],"args":[]},{"name":"repayByLendingManager","accounts":[{"name":"wooconfig","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"woopool","isMut":true,"isSigner":false},{"name":"tokenVault","isMut":true,"isSigner":false},{"name":"superChargerVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"repayAmount","type":"u128"}]}],"accounts":[{"name":"WooAmmPool","type":{"kind":"struct","fields":[{"name":"wooconfig","type":"publicKey"},{"name":"wooammpoolBump","type":{"array":["u8",1]}},{"name":"authority","type":"publicKey"},{"name":"wooracleA","type":"publicKey"},{"name":"woopoolA","type":"publicKey"},{"name":"feedAccountA","type":"publicKey"},{"name":"priceUpdateA","type":"publicKey"},{"name":"tokenMintA","type":"publicKey"},{"name":"tokenVaultA","type":"publicKey"},{"name":"wooracleB","type":"publicKey"},{"name":"woopoolB","type":"publicKey"},{"name":"feedAccountB","type":"publicKey"},{"name":"priceUpdateB","type":"publicKey"},{"name":"tokenMintB","type":"publicKey"},{"name":"tokenVaultB","type":"publicKey"},{"name":"quoteTokenMint","type":"publicKey"},{"name":"quoteFeedAccount","type":"publicKey"},{"name":"quotePriceUpdate","type":"publicKey"},{"name":"quoteWoopool","type":"publicKey"},{"name":"quoteVault","type":"publicKey"}]}},{"name":"WooConfig","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"paused","type":"bool"},{"name":"woopoolAdminAuthority","type":{"vec":"publicKey"}},{"name":"wooracleAdminAuthority","type":{"vec":"publicKey"}},{"name":"feeAuthority","type":{"vec":"publicKey"}},{"name":"guardianAuthority","type":{"vec":"publicKey"}},{"name":"pauseAuthority","type":{"vec":"publicKey"}},{"name":"lendingManagerAuthority","type":{"vec":"publicKey"}},{"name":"superchargerVaultWhitelist","type":{"vec":"publicKey"}},{"name":"newAuthority","type":"publicKey"}]}},{"name":"WooPool","type":{"kind":"struct","fields":[{"name":"wooconfig","type":"publicKey"},{"name":"woopoolBump","type":{"array":["u8",1]}},{"name":"authority","type":"publicKey"},{"name":"wooracle","type":"publicKey"},{"name":"feeRate","type":"u16"},{"name":"maxGamma","type":"u128"},{"name":"maxNotionalSwap","type":"u128"},{"name":"capBal","type":"u128"},{"name":"minSwapAmount","type":"u128"},{"name":"unclaimedFee","type":"u128"},{"name":"tokenMint","type":"publicKey"},{"name":"tokenVault","type":"publicKey"},{"name":"quoteTokenMint","type":"publicKey"},{"name":"baseDecimals","type":"u8"}]}},{"name":"Wooracle","type":{"kind":"struct","fields":[{"name":"wooconfig","type":"publicKey"},{"name":"authority","type":"publicKey"},{"name":"tokenMint","type":"publicKey"},{"name":"feedAccount","type":"publicKey"},{"name":"priceUpdate","type":"publicKey"},{"name":"maximumAge","type":"u64"},{"name":"priceDecimals","type":"u8"},{"name":"quoteDecimals","type":"u8"},{"name":"baseDecimals","type":"u8"},{"name":"updatedAt","type":"i64"},{"name":"staleDuration","type":"i64"},{"name":"bound","type":"u64"},{"name":"price","type":"u128"},{"name":"coeff","type":"u64"},{"name":"spread","type":"u64"},{"name":"rangeMin","type":"u128"},{"name":"rangeMax","type":"u128"},{"name":"quoteTokenMint","type":"publicKey"},{"name":"quoteFeedAccount","type":"publicKey"},{"name":"quotePriceUpdate","type":"publicKey"}]}}],"types":[{"name":"GetPriceResult","type":{"kind":"struct","fields":[{"name":"priceOut","type":"u128"},{"name":"feasibleOut","type":"bool"}]}},{"name":"GetStateResult","type":{"kind":"struct","fields":[{"name":"priceOut","type":"u128"},{"name":"spread","type":"u64"},{"name":"coeff","type":"u64"},{"name":"feasibleOut","type":"bool"}]}},{"name":"QueryResult","type":{"kind":"struct","fields":[{"name":"toAmount","type":"u128"},{"name":"swapFee","type":"u128"}]}}],"events":[{"name":"PauseEvent","fields":[{"name":"wooconfig","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false}]},{"name":"UnPauseEvent","fields":[{"name":"wooconfig","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false}]},{"name":"DepositEvent","fields":[{"name":"tokenMint","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"depositAmount","type":"u128","index":false}]},{"name":"WithdrawEvent","fields":[{"name":"tokenMint","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"withdrawAmount","type":"u128","index":false}]},{"name":"RepayEvent","fields":[{"name":"tokenMint","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"repayAmount","type":"u128","index":false}]},{"name":"ClaimFeeEvent","fields":[{"name":"quoteTokenMint","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"claimFeeToAccount","type":"publicKey","index":false},{"name":"claimAmount","type":"u128","index":false}]},{"name":"WooPoolAdminUpdatedEvent","fields":[{"name":"wooconfig","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"admins","type":{"vec":"publicKey"},"index":false}]},{"name":"FeeAdminUpdatedEvent","fields":[{"name":"wooconfig","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"feeAdmins","type":{"vec":"publicKey"},"index":false}]},{"name":"PauseRoleUpdatedEvent","fields":[{"name":"wooconfig","type":"publicKey","index":false},{"name":"authority","type":"publicKey","index":false},{"name":"pauseRoles","type":{"vec":"publicKey"},"index":false}]},{"name":"SwapEvent","fields":[{"name":"sender","type":"publicKey","index":false},{"name":"fromTokenMint","type":"publicKey","index":false},{"name":"toTokenMint","type":"publicKey","index":false},{"name":"fromAmount","type":"u128","index":false},{"name":"toAmount","type":"u128","index":false},{"name":"fromAccount","type":"publicKey","index":false},{"name":"toAccount","type":"publicKey","index":false},{"name":"rebateTo","type":"publicKey","index":false},{"name":"swapVol","type":"u128","index":false},{"name":"swapFee","type":"u128","index":false}]}],"errors":[{"code":6000,"name":"DivideByZero","msg":"Unable to divide by zero"},{"code":6001,"name":"NumberCastError","msg":"Unable to cast number into BigInt"},{"code":6002,"name":"FeeRateMaxExceeded","msg":"Exceeded max fee rate"},{"code":6003,"name":"MathOverflow","msg":"Mathematical operation with overflow"},{"code":6004,"name":"MulDivOverflow","msg":"Muldiv overflow"},{"code":6005,"name":"ProtocolFeeMaxExceeded","msg":"Exceeded max protocol fee"},{"code":6006,"name":"ProtocolFeeNotEnough","msg":"Protocol fee not enough"},{"code":6007,"name":"RebateFeeMaxExceeded","msg":"Exceeded max rebate fee"},{"code":6008,"name":"RebateFeeNotEnough","msg":"Rebate fee not enough"},{"code":6009,"name":"ReserveMaxExceeded","msg":"Exceeded max reserve"},{"code":6010,"name":"ReserveNotEnough","msg":"Reserve not enough"},{"code":6011,"name":"ReserveLessThanFee","msg":"Reserve less than fee"},{"code":6012,"name":"TooManyAuthorities","msg":"Too Many Authorities"},{"code":6013,"name":"WooOracleBoundLimit","msg":"Woo oracle bound exceed limit"},{"code":6014,"name":"WooOracleNotFeasible","msg":"Woo oracle is not feasible"},{"code":6015,"name":"WooOraclePriceNotValid","msg":"Woo oracle price is not valid"},{"code":6016,"name":"WooOraclePriceRangeMin","msg":"Woo oracle price below range MIN"},{"code":6017,"name":"WooOraclePriceRangeMax","msg":"Woo oracle price exceed range MAX"},{"code":6018,"name":"WooOracleSpreadExceed","msg":"Woo oracle spread exceed 1E18"},{"code":6019,"name":"WooPoolExceedMaxNotionalValue","msg":"Woo pp exceed max notional value"},{"code":6020,"name":"WooPoolExceedMaxGamma","msg":"Woo pp exceed max gamma"},{"code":6021,"name":"NotEnoughBalance","msg":"Src Balance < LP Deposit Amount."},{"code":6022,"name":"NotEnoughOut","msg":"Not enough out"},{"code":6023,"name":"AmountOutBelowMinimum","msg":"Amount out below minimum threshold"},{"code":6024,"name":"BalanceCapExceeds","msg":"Amount exceeds max balance cap"},{"code":6025,"name":"SwapPoolInvalid","msg":"Swap pool invalid"},{"code":6026,"name":"InvalidAuthority","msg":"invalid authority"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/idls/zeta.ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD.json b/migration/khadhroony-bot2-reference/idls/zeta.ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD.json new file mode 100644 index 0000000..789b291 --- /dev/null +++ b/migration/khadhroony-bot2-reference/idls/zeta.ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD.json @@ -0,0 +1 @@ +{"version":"0.1.0","name":"zeta","instructions":[{"name":"initializeZetaPricing","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true}],"args":[{"name":"args","type":{"defined":"InitializeZetaPricingArgs"}}]},{"name":"updateZetaPricingPubkeys","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdateZetaPricingPubkeysArgs"}}]},{"name":"initializeZetaGroup","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"underlyingMint","isMut":false,"isSigner":false},{"name":"zetaProgram","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"greeks","isMut":true,"isSigner":false},{"name":"perpSyncQueue","isMut":true,"isSigner":false},{"name":"underlying","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"insuranceVault","isMut":true,"isSigner":false},{"name":"socializedLossAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"usdcMint","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"InitializeZetaGroupArgs"}}]},{"name":"overrideExpiry","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"zetaGroup","isMut":true,"isSigner":false}],"args":[{"name":"args","type":{"defined":"OverrideExpiryArgs"}}]},{"name":"migrateToCrossMarginAccount","accounts":[{"name":"crossMarginAccount","isMut":true,"isSigner":false},{"name":"pricing","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[]},{"name":"initializeCrossMarginAccountManager","accounts":[{"name":"crossMarginAccountManager","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"zetaProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeCrossMarginAccount","accounts":[{"name":"crossMarginAccount","isMut":true,"isSigner":false},{"name":"crossMarginAccountManager","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"zetaProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"subaccountIndex","type":"u8"}]},{"name":"initializeMarginAccount","accounts":[{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"zetaProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeSpreadAccount","accounts":[{"name":"spreadAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"zetaProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false}],"args":[]},{"name":"closeCrossMarginAccountManager","accounts":[{"name":"crossMarginAccountManager","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[]},{"name":"closeCrossMarginAccount","accounts":[{"name":"crossMarginAccount","isMut":true,"isSigner":false},{"name":"crossMarginAccountManager","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true}],"args":[{"name":"subaccountIndex","type":"u8"}]},{"name":"closeMarginAccount","accounts":[{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"zetaGroup","isMut":false,"isSigner":false}],"args":[]},{"name":"closeSpreadAccount","accounts":[{"name":"spreadAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"zetaGroup","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeUnderlying","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"zetaProgram","isMut":false,"isSigner":false},{"name":"state","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"underlying","isMut":true,"isSigner":false},{"name":"underlyingMint","isMut":false,"isSigner":false}],"args":[{"name":"flexUnderlying","type":"bool"}]},{"name":"initializePerpSyncQueue","accounts":[{"name":"admin","isMut":false,"isSigner":true},{"name":"zetaProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"perpSyncQueue","isMut":true,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"initializeMarketIndexes","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marketIndexes","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"pricing","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"initializeMarketNode","accounts":[{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marketNode","isMut":true,"isSigner":false},{"name":"greeks","isMut":true,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"InitializeMarketNodeArgs"}}]},{"name":"halt","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"unhalt","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"updateHaltState","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"HaltStateArgs"}}]},{"name":"updateVolatility","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"greeks","isMut":true,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdateVolatilityArgs"}}]},{"name":"updateInterestRate","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"greeks","isMut":true,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdateInterestRateArgs"}}]},{"name":"addPerpMarketIndex","accounts":[{"name":"marketIndexes","isMut":true,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"addMarketIndexes","accounts":[{"name":"marketIndexes","isMut":true,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false}],"args":[]},{"name":"initializeZetaState","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"treasuryWallet","isMut":true,"isSigner":false},{"name":"referralsAdmin","isMut":false,"isSigner":false},{"name":"referralsRewardsWallet","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"usdcMint","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"secondaryAdmin","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"InitializeStateArgs"}}]},{"name":"initializeZetaTreasuryWallet","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"treasuryWallet","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"usdcMint","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true}],"args":[]},{"name":"initializeZetaReferralsRewardsWallet","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"referralsRewardsWallet","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"usdcMint","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true}],"args":[]},{"name":"updateAdmin","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"newAdmin","isMut":true,"isSigner":true}],"args":[]},{"name":"updateSecondaryAdmin","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"newAdmin","isMut":true,"isSigner":true}],"args":[]},{"name":"updateReferralsAdmin","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"newAdmin","isMut":true,"isSigner":true}],"args":[]},{"name":"updateZetaState","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdateStateArgs"}}]},{"name":"updateOracle","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"oracle","isMut":false,"isSigner":false}],"args":[]},{"name":"updateOracleBackupFeed","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"oracle","isMut":false,"isSigner":false}],"args":[]},{"name":"updatePricingParameters","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdatePricingParametersArgs"}}]},{"name":"updateMarginParameters","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdateMarginParametersArgs"}},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"updateZetaGroupMarginParameters","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdateMarginParametersArgs"}}]},{"name":"updatePerpParameters","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdatePerpParametersArgs"}},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"updateZetaGroupPerpParameters","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdatePerpParametersArgs"}}]},{"name":"updateZetaGroupExpiryParameters","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"defined":"UpdateZetaGroupExpiryArgs"}}]},{"name":"toggleZetaGroupPerpsOnly","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[]},{"name":"cleanZetaMarkets","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false}],"args":[]},{"name":"cleanZetaMarketHalted","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"market","isMut":false,"isSigner":false},{"name":"bids","isMut":false,"isSigner":false},{"name":"asks","isMut":false,"isSigner":false}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"settlePositionsHalted","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"initializeMarketStrikes","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"expireSeriesOverride","accounts":[],"args":[{"name":"args","type":{"defined":"ExpireSeriesOverrideArgs"}}]},{"name":"expireSeries","accounts":[],"args":[{"name":"settlementNonce","type":"u8"}]},{"name":"initializeZetaMarket","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marketIndexes","isMut":false,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"market","isMut":true,"isSigner":false},{"name":"requestQueue","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"baseMint","isMut":true,"isSigner":false},{"name":"quoteMint","isMut":true,"isSigner":false},{"name":"zetaBaseVault","isMut":true,"isSigner":false},{"name":"zetaQuoteVault","isMut":true,"isSigner":false},{"name":"dexBaseVault","isMut":true,"isSigner":false},{"name":"dexQuoteVault","isMut":true,"isSigner":false},{"name":"vaultOwner","isMut":false,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"args","type":{"defined":"InitializeMarketArgs"}}]},{"name":"initializeMarketTifEpochCycle","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"market","isMut":true,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false}],"args":[{"name":"epochLength","type":"u16"}]},{"name":"updatePricingV2","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"perpMarket","isMut":false,"isSigner":false},{"name":"perpBids","isMut":false,"isSigner":false},{"name":"perpAsks","isMut":false,"isSigner":false}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"applyPerpFunding","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":false,"isSigner":false}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"deposit","accounts":[{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"socializedLossAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"depositV2","accounts":[{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"socializedLossAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"depositInsuranceVault","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"insuranceVault","isMut":true,"isSigner":false},{"name":"insuranceDepositAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"zetaVault","isMut":true,"isSigner":false},{"name":"socializedLossAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"depositInsuranceVaultV2","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"insuranceVault","isMut":true,"isSigner":false},{"name":"insuranceDepositAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"zetaVault","isMut":true,"isSigner":false},{"name":"socializedLossAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdraw","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"greeks","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"socializedLossAccount","isMut":true,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdrawV2","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"socializedLossAccount","isMut":true,"isSigner":false}],"args":[{"name":"amount","type":"u64"}]},{"name":"withdrawInsuranceVault","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"insuranceVault","isMut":true,"isSigner":false},{"name":"insuranceDepositAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"percentageAmount","type":"u64"}]},{"name":"withdrawInsuranceVaultV2","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"pricing","isMut":true,"isSigner":false},{"name":"insuranceVault","isMut":true,"isSigner":false},{"name":"insuranceDepositAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[{"name":"percentageAmount","type":"u64"}]},{"name":"initializeOpenOrders","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"market","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrdersMap","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeOpenOrdersV2","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"market","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrdersMap","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeOpenOrdersV3","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"crossMarginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"payer","isMut":true,"isSigner":true},{"name":"market","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrdersMap","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"closeOpenOrders","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"market","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrdersMap","isMut":true,"isSigner":false}],"args":[{"name":"mapNonce","type":"u8"}]},{"name":"closeOpenOrdersV2","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"market","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrdersMap","isMut":true,"isSigner":false}],"args":[{"name":"mapNonce","type":"u8"}]},{"name":"closeOpenOrdersV3","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":false,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"crossMarginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":true,"isSigner":true},{"name":"market","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrdersMap","isMut":true,"isSigner":false}],"args":[{"name":"mapNonce","type":"u8"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"initializeWhitelistDepositAccount","accounts":[{"name":"whitelistDepositAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"user","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"initializeWhitelistInsuranceAccount","accounts":[{"name":"whitelistInsuranceAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"user","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"initializeWhitelistTradingFeesAccount","accounts":[{"name":"whitelistTradingFeesAccount","isMut":true,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"user","isMut":false,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"initializeInsuranceDepositAccount","accounts":[{"name":"insuranceDepositAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":false},{"name":"payer","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false},{"name":"whitelistInsuranceAccount","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"initializeCombinedInsuranceVault","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"insuranceVault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"usdcMint","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"initializeCombinedVault","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"usdcMint","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"initializeCombinedSocializedLossAccount","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"socializedLossAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"usdcMint","isMut":false,"isSigner":false},{"name":"admin","isMut":true,"isSigner":true},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"nonce","type":"u8"}]},{"name":"placeOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"marketAccounts","accounts":[{"name":"market","isMut":true,"isSigner":false},{"name":"requestQueue","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"orderPayerTokenAccount","isMut":true,"isSigner":false},{"name":"coinVault","isMut":true,"isSigner":false},{"name":"pcVault","isMut":true,"isSigner":false},{"name":"coinWallet","isMut":true,"isSigner":false},{"name":"pcWallet","isMut":true,"isSigner":false}]},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"marketNode","isMut":true,"isSigner":false},{"name":"marketMint","isMut":true,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false}],"args":[{"name":"price","type":"u64"},{"name":"size","type":"u64"},{"name":"side","type":{"defined":"Side"}},{"name":"clientOrderId","type":{"option":"u64"}}]},{"name":"placeOrderV2","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"marketAccounts","accounts":[{"name":"market","isMut":true,"isSigner":false},{"name":"requestQueue","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"orderPayerTokenAccount","isMut":true,"isSigner":false},{"name":"coinVault","isMut":true,"isSigner":false},{"name":"pcVault","isMut":true,"isSigner":false},{"name":"coinWallet","isMut":true,"isSigner":false},{"name":"pcWallet","isMut":true,"isSigner":false}]},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"marketNode","isMut":true,"isSigner":false},{"name":"marketMint","isMut":true,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false}],"args":[{"name":"price","type":"u64"},{"name":"size","type":"u64"},{"name":"side","type":{"defined":"Side"}},{"name":"orderType","type":{"defined":"OrderType"}},{"name":"clientOrderId","type":{"option":"u64"}}]},{"name":"placeOrderV3","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"marketAccounts","accounts":[{"name":"market","isMut":true,"isSigner":false},{"name":"requestQueue","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"orderPayerTokenAccount","isMut":true,"isSigner":false},{"name":"coinVault","isMut":true,"isSigner":false},{"name":"pcVault","isMut":true,"isSigner":false},{"name":"coinWallet","isMut":true,"isSigner":false},{"name":"pcWallet","isMut":true,"isSigner":false}]},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"marketNode","isMut":true,"isSigner":false},{"name":"marketMint","isMut":true,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false}],"args":[{"name":"price","type":"u64"},{"name":"size","type":"u64"},{"name":"side","type":{"defined":"Side"}},{"name":"orderType","type":{"defined":"OrderType"}},{"name":"clientOrderId","type":{"option":"u64"}},{"name":"tag","type":{"option":"string"}}]},{"name":"placePerpOrder","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"marketAccounts","accounts":[{"name":"market","isMut":true,"isSigner":false},{"name":"requestQueue","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"orderPayerTokenAccount","isMut":true,"isSigner":false},{"name":"coinVault","isMut":true,"isSigner":false},{"name":"pcVault","isMut":true,"isSigner":false},{"name":"coinWallet","isMut":true,"isSigner":false},{"name":"pcWallet","isMut":true,"isSigner":false}]},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"marketMint","isMut":true,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false},{"name":"perpSyncQueue","isMut":true,"isSigner":false}],"args":[{"name":"price","type":"u64"},{"name":"size","type":"u64"},{"name":"side","type":{"defined":"Side"}},{"name":"orderType","type":{"defined":"OrderType"}},{"name":"clientOrderId","type":{"option":"u64"}},{"name":"tag","type":{"option":"string"}}]},{"name":"placePerpOrderV2","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"marketAccounts","accounts":[{"name":"market","isMut":true,"isSigner":false},{"name":"requestQueue","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"orderPayerTokenAccount","isMut":true,"isSigner":false},{"name":"coinVault","isMut":true,"isSigner":false},{"name":"pcVault","isMut":true,"isSigner":false},{"name":"coinWallet","isMut":true,"isSigner":false},{"name":"pcWallet","isMut":true,"isSigner":false}]},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"marketMint","isMut":true,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false},{"name":"perpSyncQueue","isMut":true,"isSigner":false}],"args":[{"name":"price","type":"u64"},{"name":"size","type":"u64"},{"name":"side","type":{"defined":"Side"}},{"name":"orderType","type":{"defined":"OrderType"}},{"name":"clientOrderId","type":{"option":"u64"}},{"name":"tag","type":{"option":"string"}},{"name":"tifOffset","type":{"option":"u16"}}]},{"name":"placeOrderV4","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"marketAccounts","accounts":[{"name":"market","isMut":true,"isSigner":false},{"name":"requestQueue","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"orderPayerTokenAccount","isMut":true,"isSigner":false},{"name":"coinVault","isMut":true,"isSigner":false},{"name":"pcVault","isMut":true,"isSigner":false},{"name":"coinWallet","isMut":true,"isSigner":false},{"name":"pcWallet","isMut":true,"isSigner":false}]},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"marketNode","isMut":true,"isSigner":false},{"name":"marketMint","isMut":true,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false}],"args":[{"name":"price","type":"u64"},{"name":"size","type":"u64"},{"name":"side","type":{"defined":"Side"}},{"name":"orderType","type":{"defined":"OrderType"}},{"name":"clientOrderId","type":{"option":"u64"}},{"name":"tag","type":{"option":"string"}},{"name":"tifOffset","type":{"option":"u16"}}]},{"name":"placePerpOrderV3","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"rent","isMut":false,"isSigner":false},{"name":"marketAccounts","accounts":[{"name":"market","isMut":true,"isSigner":false},{"name":"requestQueue","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"orderPayerTokenAccount","isMut":true,"isSigner":false},{"name":"coinVault","isMut":true,"isSigner":false},{"name":"pcVault","isMut":true,"isSigner":false},{"name":"coinWallet","isMut":true,"isSigner":false},{"name":"pcWallet","isMut":true,"isSigner":false}]},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"marketMint","isMut":true,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false},{"name":"perpSyncQueue","isMut":true,"isSigner":false}],"args":[{"name":"price","type":"u64"},{"name":"size","type":"u64"},{"name":"side","type":{"defined":"Side"}},{"name":"orderType","type":{"defined":"OrderType"}},{"name":"clientOrderId","type":{"option":"u64"}},{"name":"tag","type":{"option":"string"}},{"name":"tifOffset","type":{"option":"u16"}},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"cancelOrder","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"side","type":{"defined":"Side"}},{"name":"orderId","type":"u128"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"cancelOrderNoError","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"side","type":{"defined":"Side"}},{"name":"orderId","type":"u128"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"cancelAllMarketOrders","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"cancelOrderHalted","accounts":[{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"side","type":{"defined":"Side"}},{"name":"orderId","type":"u128"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"cancelOrderByClientOrderId","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"clientOrderId","type":"u64"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"cancelOrderByClientOrderIdNoError","accounts":[{"name":"authority","isMut":false,"isSigner":true},{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"clientOrderId","type":"u64"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"pruneExpiredTifOrders","accounts":[{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"state","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}],"args":[]},{"name":"forceCancelOrderByOrderIdV2","accounts":[{"name":"pricing","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"side","type":{"defined":"Side"}},{"name":"orderId","type":"u128"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"forceCancelOrderByOrderId","accounts":[{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"side","type":{"defined":"Side"}},{"name":"orderId","type":"u128"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"forceCancelOrdersV2","accounts":[{"name":"pricing","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"forceCancelOrders","accounts":[{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"cancelAccounts","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"openOrders","isMut":true,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"bids","isMut":true,"isSigner":false},{"name":"asks","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false}]}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"crankEventQueue","docs":["The only events that need to be cranked are maker fills."],"accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"pricing","isMut":false,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"eventQueue","isMut":true,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"perpSyncQueue","isMut":true,"isSigner":false}],"args":[{"name":"asset","type":{"defined":"Asset"}}]},{"name":"collectTreasuryFunds","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"treasuryWallet","isMut":true,"isSigner":false},{"name":"collectionTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"amount","type":"u64"}]},{"name":"treasuryMovement","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"insuranceVault","isMut":true,"isSigner":false},{"name":"treasuryWallet","isMut":true,"isSigner":false},{"name":"referralsRewardsWallet","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[{"name":"treasuryMovementType","type":{"defined":"TreasuryMovementType"}},{"name":"amount","type":"u64"}]},{"name":"rebalanceInsuranceVault","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaVault","isMut":true,"isSigner":false},{"name":"insuranceVault","isMut":true,"isSigner":false},{"name":"treasuryWallet","isMut":true,"isSigner":false},{"name":"socializedLossAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"liquidateV2","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"liquidator","isMut":false,"isSigner":true},{"name":"liquidatorAccount","isMut":true,"isSigner":false},{"name":"pricing","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"market","isMut":false,"isSigner":false},{"name":"liquidatedAccount","isMut":true,"isSigner":false}],"args":[{"name":"size","type":"u64"},{"name":"asset","type":{"defined":"Asset"}}]},{"name":"liquidate","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"liquidator","isMut":false,"isSigner":true},{"name":"liquidatorMarginAccount","isMut":true,"isSigner":false},{"name":"greeks","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false},{"name":"market","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"liquidatedMarginAccount","isMut":true,"isSigner":false}],"args":[{"name":"size","type":"u64"}]},{"name":"burnVaultTokens","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"mint","isMut":true,"isSigner":false},{"name":"vault","isMut":true,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"settleDexFunds","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"market","isMut":true,"isSigner":false},{"name":"zetaBaseVault","isMut":true,"isSigner":false},{"name":"zetaQuoteVault","isMut":true,"isSigner":false},{"name":"dexBaseVault","isMut":true,"isSigner":false},{"name":"dexQuoteVault","isMut":true,"isSigner":false},{"name":"vaultOwner","isMut":false,"isSigner":false},{"name":"mintAuthority","isMut":false,"isSigner":false},{"name":"serumAuthority","isMut":false,"isSigner":false},{"name":"dexProgram","isMut":false,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"positionMovement","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"spreadAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true},{"name":"greeks","isMut":false,"isSigner":false},{"name":"oracle","isMut":false,"isSigner":false},{"name":"oracleBackupFeed","isMut":false,"isSigner":false},{"name":"oracleBackupProgram","isMut":false,"isSigner":false}],"args":[{"name":"movementType","type":{"defined":"MovementType"}},{"name":"movements","type":{"vec":{"defined":"PositionMovementArg"}}}]},{"name":"transferExcessSpreadBalance","accounts":[{"name":"zetaGroup","isMut":false,"isSigner":false},{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"spreadAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[]},{"name":"toggleMarketMaker","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true},{"name":"marginAccount","isMut":true,"isSigner":false}],"args":[{"name":"isMarketMaker","type":"bool"}]},{"name":"initializeReferrerAccount","accounts":[{"name":"referrer","isMut":true,"isSigner":true},{"name":"referrerAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"referUser","accounts":[{"name":"user","isMut":true,"isSigner":true},{"name":"referrerAccount","isMut":false,"isSigner":false},{"name":"referralAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[]},{"name":"initializeReferrerAlias","accounts":[{"name":"referrer","isMut":true,"isSigner":true},{"name":"referrerAlias","isMut":true,"isSigner":false},{"name":"referrerAccount","isMut":true,"isSigner":false},{"name":"systemProgram","isMut":false,"isSigner":false}],"args":[{"name":"alias","type":"string"}]},{"name":"setReferralsRewards","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"referralsAdmin","isMut":false,"isSigner":true}],"args":[{"name":"args","type":{"vec":{"defined":"SetReferralsRewardsArgs"}}}]},{"name":"claimReferralsRewards","accounts":[{"name":"state","isMut":false,"isSigner":false},{"name":"referralsRewardsWallet","isMut":true,"isSigner":false},{"name":"userReferralsAccount","isMut":true,"isSigner":false},{"name":"userTokenAccount","isMut":true,"isSigner":false},{"name":"tokenProgram","isMut":false,"isSigner":false},{"name":"user","isMut":false,"isSigner":true}],"args":[]},{"name":"editDelegatedPubkey","accounts":[{"name":"marginAccount","isMut":true,"isSigner":false},{"name":"authority","isMut":false,"isSigner":true}],"args":[{"name":"newKey","type":"publicKey"}]},{"name":"resetNumFlexUnderlyings","accounts":[{"name":"state","isMut":true,"isSigner":false},{"name":"admin","isMut":false,"isSigner":true}],"args":[]}],"accounts":[{"name":"Pricing","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"markPrices","type":{"array":["u64",5]}},{"name":"markPricesPadding","type":{"array":["u64",20]}},{"name":"updateTimestamps","type":{"array":["u64",5]}},{"name":"updateTimestampsPadding","type":{"array":["u64",20]}},{"name":"fundingDeltas","type":{"array":[{"defined":"AnchorDecimal"},5]}},{"name":"fundingDeltasPadding","type":{"array":[{"defined":"AnchorDecimal"},20]}},{"name":"latestFundingRates","type":{"array":[{"defined":"AnchorDecimal"},5]}},{"name":"latestFundingRatesPadding","type":{"array":[{"defined":"AnchorDecimal"},20]}},{"name":"latestMidpoints","type":{"array":["u64",5]}},{"name":"latestMidpointsPadding","type":{"array":["u64",20]}},{"name":"oracles","type":{"array":["publicKey",5]}},{"name":"oraclesPadding","type":{"array":["publicKey",20]}},{"name":"oracleBackupFeeds","type":{"array":["publicKey",5]}},{"name":"oracleBackupFeedsPadding","type":{"array":["publicKey",20]}},{"name":"markets","type":{"array":["publicKey",5]}},{"name":"marketsPadding","type":{"array":["publicKey",20]}},{"name":"perpSyncQueues","type":{"array":["publicKey",5]}},{"name":"perpSyncQueuesPadding","type":{"array":["publicKey",20]}},{"name":"perpParameters","type":{"array":[{"defined":"PerpParameters"},5]}},{"name":"perpParametersPadding","type":{"array":[{"defined":"PerpParameters"},20]}},{"name":"marginParameters","type":{"array":[{"defined":"MarginParameters"},5]}},{"name":"marginParametersPadding","type":{"array":[{"defined":"MarginParameters"},20]}},{"name":"products","type":{"array":[{"defined":"Product"},5]}},{"name":"productsPadding","type":{"array":[{"defined":"Product"},20]}},{"name":"zetaGroupKeys","type":{"array":["publicKey",5]}},{"name":"zetaGroupKeysPadding","type":{"array":["publicKey",20]}},{"name":"totalInsuranceVaultDeposits","type":"u64"},{"name":"lastWithdrawTimestamp","type":"u64"},{"name":"netOutflowSum","type":"i64"},{"name":"haltForcePricing","type":{"array":["bool",5]}},{"name":"haltForcePricingPadding","type":{"array":["bool",20]}},{"name":"padding","type":{"array":["u8",2707]}}]}},{"name":"Greeks","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"markPrices","type":{"array":["u64",46]}},{"name":"markPricesPadding","type":{"array":["u64",91]}},{"name":"perpMarkPrice","type":"u64"},{"name":"productGreeks","type":{"array":[{"defined":"ProductGreeks"},22]}},{"name":"productGreeksPadding","type":{"array":[{"defined":"ProductGreeks"},44]}},{"name":"updateTimestamp","type":{"array":["u64",2]}},{"name":"updateTimestampPadding","type":{"array":["u64",4]}},{"name":"retreatExpirationTimestamp","type":{"array":["u64",2]}},{"name":"retreatExpirationTimestampPadding","type":{"array":["u64",4]}},{"name":"interestRate","type":{"array":["i64",2]}},{"name":"interestRatePadding","type":{"array":["i64",4]}},{"name":"nodes","type":{"array":["u64",5]}},{"name":"volatility","type":{"array":["u64",10]}},{"name":"volatilityPadding","type":{"array":["u64",20]}},{"name":"nodeKeys","type":{"array":["publicKey",138]}},{"name":"haltForcePricing","type":{"array":["bool",6]}},{"name":"perpUpdateTimestamp","type":"u64"},{"name":"perpFundingDelta","type":{"defined":"AnchorDecimal"}},{"name":"perpLatestFundingRate","type":{"defined":"AnchorDecimal"}},{"name":"perpLatestMidpoint","type":"u64"},{"name":"padding","type":{"array":["u8",1593]}}]}},{"name":"MarketIndexes","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"initialized","type":"bool"},{"name":"indexes","type":{"array":["u8",138]}}]}},{"name":"OpenOrdersMap","type":{"kind":"struct","fields":[{"name":"userKey","type":"publicKey"}]}},{"name":"CrossOpenOrdersMap","type":{"kind":"struct","fields":[{"name":"userKey","type":"publicKey"},{"name":"subaccountIndex","type":"u8"}]}},{"name":"State","type":{"kind":"struct","fields":[{"name":"admin","type":"publicKey"},{"name":"stateNonce","type":"u8"},{"name":"serumNonce","type":"u8"},{"name":"mintAuthNonce","type":"u8"},{"name":"numUnderlyings","type":"u8"},{"name":"numFlexUnderlyings","type":"u8"},{"name":"null","type":{"array":["u8",7]}},{"name":"strikeInitializationThresholdSeconds","type":"u32"},{"name":"pricingFrequencySeconds","type":"u32"},{"name":"liquidatorLiquidationPercentage","type":"u32"},{"name":"insuranceVaultLiquidationPercentage","type":"u32"},{"name":"nativeD1TradeFeePercentage","type":"u64"},{"name":"nativeD1UnderlyingFeePercentage","type":"u64"},{"name":"nativeWhitelistUnderlyingFeePercentage","type":"u64"},{"name":"nativeDepositLimit","type":"u64"},{"name":"expirationThresholdSeconds","type":"u32"},{"name":"positionMovementFeeBps","type":"u8"},{"name":"marginConcessionPercentage","type":"u8"},{"name":"treasuryWalletNonce","type":"u8"},{"name":"nativeOptionTradeFeePercentage","type":"u64"},{"name":"nativeOptionUnderlyingFeePercentage","type":"u64"},{"name":"referralsAdmin","type":"publicKey"},{"name":"referralsRewardsWalletNonce","type":"u8"},{"name":"maxPerpDeltaAge","type":"u16"},{"name":"secondaryAdmin","type":"publicKey"},{"name":"vaultNonce","type":"u8"},{"name":"insuranceVaultNonce","type":"u8"},{"name":"deprecatedTotalInsuranceVaultDeposits","type":"u64"},{"name":"nativeWithdrawLimit","type":"u64"},{"name":"withdrawLimitEpochSeconds","type":"u32"},{"name":"nativeOpenInterestLimit","type":"u64"},{"name":"haltStates","type":{"array":[{"defined":"HaltStateV2"},5]}},{"name":"haltStatesPadding","type":{"array":[{"defined":"HaltStateV2"},20]}},{"name":"padding","type":{"array":["u8",338]}}]}},{"name":"Underlying","type":{"kind":"struct","fields":[{"name":"mint","type":"publicKey"}]}},{"name":"SettlementAccount","type":{"kind":"struct","fields":[{"name":"settlementPrice","type":"u64"},{"name":"strikes","type":{"array":["u64",23]}}]}},{"name":"PerpSyncQueue","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"head","type":"u16"},{"name":"length","type":"u16"},{"name":"queue","type":{"array":[{"defined":"AnchorDecimal"},600]}}]}},{"name":"ZetaGroup","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"noncePadding","type":{"array":["u8",2]}},{"name":"frontExpiryIndex","type":"u8"},{"name":"haltState","type":{"defined":"HaltState"}},{"name":"underlyingMint","type":"publicKey"},{"name":"oracle","type":"publicKey"},{"name":"greeks","type":"publicKey"},{"name":"pricingParameters","type":{"defined":"PricingParameters"}},{"name":"marginParameters","type":{"defined":"MarginParameters"}},{"name":"marginParametersPadding","type":{"array":["u8",104]}},{"name":"products","type":{"array":[{"defined":"Product"},46]}},{"name":"productsPadding","type":{"array":[{"defined":"Product"},91]}},{"name":"perp","type":{"defined":"Product"}},{"name":"expirySeries","type":{"array":[{"defined":"ExpirySeries"},2]}},{"name":"expirySeriesPadding","type":{"array":[{"defined":"ExpirySeries"},4]}},{"name":"deprecatedPadding","type":{"array":["u8",8]}},{"name":"asset","type":{"defined":"Asset"}},{"name":"expiryIntervalSeconds","type":"u32"},{"name":"newExpiryThresholdSeconds","type":"u32"},{"name":"perpParameters","type":{"defined":"PerpParameters"}},{"name":"perpSyncQueue","type":"publicKey"},{"name":"oracleBackupFeed","type":"publicKey"},{"name":"perpsOnly","type":"bool"},{"name":"flexUnderlying","type":"bool"},{"name":"padding","type":{"array":["u8",964]}}]}},{"name":"MarketNode","type":{"kind":"struct","fields":[{"name":"index","type":"u8"},{"name":"nonce","type":"u8"},{"name":"nodeUpdates","type":{"array":["i64",5]}},{"name":"interestUpdate","type":"i64"}]}},{"name":"SpreadAccount","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"nonce","type":"u8"},{"name":"balance","type":"u64"},{"name":"seriesExpiry","type":{"array":["u64",5]}},{"name":"seriesExpiryPadding","type":"u64"},{"name":"positions","type":{"array":[{"defined":"Position"},46]}},{"name":"positionsPadding","type":{"array":[{"defined":"Position"},92]}},{"name":"asset","type":{"defined":"Asset"}},{"name":"padding","type":{"array":["u8",262]}}]}},{"name":"CrossMarginAccountManager","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"authority","type":"publicKey"},{"name":"accounts","type":{"array":[{"defined":"CrossMarginAccountInfo"},25]}}]}},{"name":"CrossMarginAccount","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"delegatedPubkey","type":"publicKey"},{"name":"balance","type":"u64"},{"name":"subaccountIndex","type":"u8"},{"name":"nonce","type":"u8"},{"name":"forceCancelFlag","type":"bool"},{"name":"accountType","type":{"defined":"MarginAccountType"}},{"name":"openOrdersNonces","type":{"array":["u8",5]}},{"name":"openOrdersNoncesPadding","type":{"array":["u8",20]}},{"name":"rebalanceAmount","type":"i64"},{"name":"lastFundingDeltas","type":{"array":[{"defined":"AnchorDecimal"},5]}},{"name":"lastFundingDeltasPadding","type":{"array":[{"defined":"AnchorDecimal"},20]}},{"name":"productLedgers","type":{"array":[{"defined":"ProductLedger"},5]}},{"name":"productLedgersPadding","type":{"array":[{"defined":"ProductLedger"},20]}},{"name":"padding","type":{"array":["u8",2000]}}]}},{"name":"MarginAccount","type":{"kind":"struct","fields":[{"name":"authority","type":"publicKey"},{"name":"nonce","type":"u8"},{"name":"balance","type":"u64"},{"name":"forceCancelFlag","type":"bool"},{"name":"openOrdersNonce","type":{"array":["u8",138]}},{"name":"seriesExpiry","type":{"array":["u64",5]}},{"name":"seriesExpiryPadding","type":"u64"},{"name":"productLedgers","type":{"array":[{"defined":"ProductLedger"},46]}},{"name":"productLedgersPadding","type":{"array":[{"defined":"ProductLedger"},91]}},{"name":"perpProductLedger","type":{"defined":"ProductLedger"}},{"name":"rebalanceAmount","type":"i64"},{"name":"asset","type":{"defined":"Asset"}},{"name":"accountType","type":{"defined":"MarginAccountType"}},{"name":"lastFundingDelta","type":{"defined":"AnchorDecimal"}},{"name":"delegatedPubkey","type":"publicKey"},{"name":"padding","type":{"array":["u8",338]}}]}},{"name":"SocializedLossAccount","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"overbankruptAmount","type":"u64"}]}},{"name":"WhitelistDepositAccount","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"userKey","type":"publicKey"}]}},{"name":"WhitelistInsuranceAccount","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"userKey","type":"publicKey"}]}},{"name":"InsuranceDepositAccount","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"amount","type":"u64"}]}},{"name":"WhitelistTradingFeesAccount","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"userKey","type":"publicKey"}]}},{"name":"ReferrerAccount","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"hasAlias","type":"bool"},{"name":"referrer","type":"publicKey"},{"name":"pendingRewards","type":"u64"},{"name":"claimedRewards","type":"u64"}]}},{"name":"ReferralAccount","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"referrer","type":"publicKey"},{"name":"user","type":"publicKey"},{"name":"timestamp","type":"u64"},{"name":"pendingRewards","type":"u64"},{"name":"claimedRewards","type":"u64"}]}},{"name":"ReferrerAlias","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"alias","type":{"array":["u8",15]}},{"name":"referrer","type":"publicKey"}]}}],"types":[{"name":"ProductGreeks","type":{"kind":"struct","fields":[{"name":"delta","type":"u64"},{"name":"vega","type":{"defined":"AnchorDecimal"}},{"name":"volatility","type":{"defined":"AnchorDecimal"}}]}},{"name":"AnchorDecimal","type":{"kind":"struct","fields":[{"name":"flags","type":"u32"},{"name":"hi","type":"u32"},{"name":"lo","type":"u32"},{"name":"mid","type":"u32"}]}},{"name":"HaltStateV2","type":{"kind":"struct","fields":[{"name":"halted","type":"bool"},{"name":"timestamp","type":"u64"},{"name":"spotPrice","type":"u64"},{"name":"marketCleaned","type":"bool"}]}},{"name":"HaltState","type":{"kind":"struct","fields":[{"name":"halted","type":"bool"},{"name":"spotPrice","type":"u64"},{"name":"timestamp","type":"u64"},{"name":"markPricesSet","type":{"array":["bool",2]}},{"name":"markPricesSetPadding","type":{"array":["bool",3]}},{"name":"perpMarkPriceSet","type":"bool"},{"name":"marketNodesCleaned","type":{"array":["bool",2]}},{"name":"marketNodesCleanedPadding","type":{"array":["bool",4]}},{"name":"marketCleaned","type":{"array":["bool",46]}},{"name":"marketCleanedPadding","type":{"array":["bool",91]}},{"name":"perpMarketCleaned","type":"bool"}]}},{"name":"PricingParameters","type":{"kind":"struct","fields":[{"name":"optionTradeNormalizer","type":{"defined":"AnchorDecimal"}},{"name":"futureTradeNormalizer","type":{"defined":"AnchorDecimal"}},{"name":"maxVolatilityRetreat","type":{"defined":"AnchorDecimal"}},{"name":"maxInterestRetreat","type":{"defined":"AnchorDecimal"}},{"name":"maxDelta","type":"u64"},{"name":"minDelta","type":"u64"},{"name":"minVolatility","type":"u64"},{"name":"maxVolatility","type":"u64"},{"name":"minInterestRate","type":"i64"},{"name":"maxInterestRate","type":"i64"}]}},{"name":"MarginParameters","type":{"kind":"struct","fields":[{"name":"futureMarginInitial","type":"u64"},{"name":"futureMarginMaintenance","type":"u64"}]}},{"name":"PerpParameters","type":{"kind":"struct","fields":[{"name":"minFundingRatePercent","type":"i64"},{"name":"maxFundingRatePercent","type":"i64"},{"name":"impactCashDelta","type":"u64"}]}},{"name":"ExpirySeries","type":{"kind":"struct","fields":[{"name":"activeTs","type":"u64"},{"name":"expiryTs","type":"u64"},{"name":"dirty","type":"bool"},{"name":"padding","type":{"array":["u8",15]}}]}},{"name":"Strike","type":{"kind":"struct","fields":[{"name":"isSet","type":"bool"},{"name":"value","type":"u64"}]}},{"name":"Product","type":{"kind":"struct","fields":[{"name":"market","type":"publicKey"},{"name":"strike","type":{"defined":"Strike"}},{"name":"dirty","type":"bool"},{"name":"kind","type":{"defined":"Kind"}}]}},{"name":"Position","type":{"kind":"struct","fields":[{"name":"size","type":"i64"},{"name":"costOfTrades","type":"u64"}]}},{"name":"OrderState","type":{"kind":"struct","fields":[{"name":"closingOrders","type":"u64"},{"name":"openingOrders","type":{"array":["u64",2]}}]}},{"name":"ProductLedger","type":{"kind":"struct","fields":[{"name":"position","type":{"defined":"Position"}},{"name":"orderState","type":{"defined":"OrderState"}}]}},{"name":"CrossMarginAccountInfo","type":{"kind":"struct","fields":[{"name":"initialized","type":"bool"},{"name":"name","type":{"array":["u8",10]}}]}},{"name":"HaltStateArgs","type":{"kind":"struct","fields":[{"name":"asset","type":{"defined":"Asset"}},{"name":"spotPrice","type":"u64"},{"name":"timestamp","type":"u64"}]}},{"name":"HaltArgs","type":{"kind":"struct","fields":[{"name":"spotPrices","type":{"array":["u64",5]}},{"name":"timestamp","type":"u64"}]}},{"name":"UpdateVolatilityArgs","type":{"kind":"struct","fields":[{"name":"expiryIndex","type":"u8"},{"name":"volatility","type":{"array":["u64",5]}}]}},{"name":"UpdateInterestRateArgs","type":{"kind":"struct","fields":[{"name":"expiryIndex","type":"u8"},{"name":"interestRate","type":"i64"}]}},{"name":"SetReferralsRewardsArgs","type":{"kind":"struct","fields":[{"name":"referralsAccountKey","type":"publicKey"},{"name":"pendingRewards","type":"u64"},{"name":"overwrite","type":"bool"}]}},{"name":"ExpireSeriesOverrideArgs","type":{"kind":"struct","fields":[{"name":"settlementNonce","type":"u8"},{"name":"settlementPrice","type":"u64"}]}},{"name":"InitializeMarketArgs","type":{"kind":"struct","fields":[{"name":"asset","type":{"defined":"Asset"}},{"name":"marketNonce","type":"u8"},{"name":"baseMintNonce","type":"u8"},{"name":"quoteMintNonce","type":"u8"},{"name":"zetaBaseVaultNonce","type":"u8"},{"name":"zetaQuoteVaultNonce","type":"u8"},{"name":"dexBaseVaultNonce","type":"u8"},{"name":"dexQuoteVaultNonce","type":"u8"},{"name":"vaultSignerNonce","type":"u64"}]}},{"name":"InitializeStateArgs","type":{"kind":"struct","fields":[{"name":"stateNonce","type":"u8"},{"name":"serumNonce","type":"u8"},{"name":"mintAuthNonce","type":"u8"},{"name":"strikeInitializationThresholdSeconds","type":"u32"},{"name":"pricingFrequencySeconds","type":"u32"},{"name":"liquidatorLiquidationPercentage","type":"u32"},{"name":"insuranceVaultLiquidationPercentage","type":"u32"},{"name":"nativeD1TradeFeePercentage","type":"u64"},{"name":"nativeD1UnderlyingFeePercentage","type":"u64"},{"name":"nativeOptionTradeFeePercentage","type":"u64"},{"name":"nativeOptionUnderlyingFeePercentage","type":"u64"},{"name":"nativeWhitelistUnderlyingFeePercentage","type":"u64"},{"name":"nativeDepositLimit","type":"u64"},{"name":"expirationThresholdSeconds","type":"u32"},{"name":"positionMovementFeeBps","type":"u8"},{"name":"marginConcessionPercentage","type":"u8"},{"name":"maxPerpDeltaAgeSeconds","type":"u16"},{"name":"nativeWithdrawLimit","type":"u64"},{"name":"withdrawLimitEpochSeconds","type":"u32"},{"name":"nativeOpenInterestLimit","type":"u64"}]}},{"name":"InitializeMarketNodeArgs","type":{"kind":"struct","fields":[{"name":"nonce","type":"u8"},{"name":"index","type":"u8"}]}},{"name":"OverrideExpiryArgs","type":{"kind":"struct","fields":[{"name":"expiryIndex","type":"u8"},{"name":"activeTs","type":"u64"},{"name":"expiryTs","type":"u64"}]}},{"name":"UpdateStateArgs","type":{"kind":"struct","fields":[{"name":"strikeInitializationThresholdSeconds","type":"u32"},{"name":"pricingFrequencySeconds","type":"u32"},{"name":"liquidatorLiquidationPercentage","type":"u32"},{"name":"insuranceVaultLiquidationPercentage","type":"u32"},{"name":"nativeD1TradeFeePercentage","type":"u64"},{"name":"nativeD1UnderlyingFeePercentage","type":"u64"},{"name":"nativeOptionTradeFeePercentage","type":"u64"},{"name":"nativeOptionUnderlyingFeePercentage","type":"u64"},{"name":"nativeWhitelistUnderlyingFeePercentage","type":"u64"},{"name":"nativeDepositLimit","type":"u64"},{"name":"expirationThresholdSeconds","type":"u32"},{"name":"positionMovementFeeBps","type":"u8"},{"name":"marginConcessionPercentage","type":"u8"},{"name":"maxPerpDeltaAgeSeconds","type":"u16"},{"name":"nativeWithdrawLimit","type":"u64"},{"name":"withdrawLimitEpochSeconds","type":"u32"},{"name":"nativeOpenInterestLimit","type":"u64"}]}},{"name":"UpdatePricingParametersArgs","type":{"kind":"struct","fields":[{"name":"optionTradeNormalizer","type":"u64"},{"name":"futureTradeNormalizer","type":"u64"},{"name":"maxVolatilityRetreat","type":"u64"},{"name":"maxInterestRetreat","type":"u64"},{"name":"minDelta","type":"u64"},{"name":"maxDelta","type":"u64"},{"name":"minInterestRate","type":"i64"},{"name":"maxInterestRate","type":"i64"},{"name":"minVolatility","type":"u64"},{"name":"maxVolatility","type":"u64"}]}},{"name":"UpdateMarginParametersArgs","type":{"kind":"struct","fields":[{"name":"futureMarginInitial","type":"u64"},{"name":"futureMarginMaintenance","type":"u64"}]}},{"name":"UpdatePerpParametersArgs","type":{"kind":"struct","fields":[{"name":"minFundingRatePercent","type":"i64"},{"name":"maxFundingRatePercent","type":"i64"},{"name":"perpImpactCashDelta","type":"u64"}]}},{"name":"InitializeZetaGroupArgs","type":{"kind":"struct","fields":[{"name":"perpsOnly","type":"bool"},{"name":"flexUnderlying","type":"bool"},{"name":"assetOverride","type":{"option":{"defined":"Asset"}}},{"name":"zetaGroupNonce","type":"u8"},{"name":"underlyingNonce","type":"u8"},{"name":"greeksNonce","type":"u8"},{"name":"vaultNonce","type":"u8"},{"name":"insuranceVaultNonce","type":"u8"},{"name":"socializedLossAccountNonce","type":"u8"},{"name":"perpSyncQueueNonce","type":"u8"},{"name":"interestRate","type":"i64"},{"name":"volatility","type":{"array":["u64",5]}},{"name":"optionTradeNormalizer","type":"u64"},{"name":"futureTradeNormalizer","type":"u64"},{"name":"maxVolatilityRetreat","type":"u64"},{"name":"maxInterestRetreat","type":"u64"},{"name":"maxDelta","type":"u64"},{"name":"minDelta","type":"u64"},{"name":"minInterestRate","type":"i64"},{"name":"maxInterestRate","type":"i64"},{"name":"minVolatility","type":"u64"},{"name":"maxVolatility","type":"u64"},{"name":"futureMarginInitial","type":"u64"},{"name":"futureMarginMaintenance","type":"u64"},{"name":"expiryIntervalSeconds","type":"u32"},{"name":"newExpiryThresholdSeconds","type":"u32"},{"name":"minFundingRatePercent","type":"i64"},{"name":"maxFundingRatePercent","type":"i64"},{"name":"perpImpactCashDelta","type":"u64"}]}},{"name":"UpdateZetaGroupExpiryArgs","type":{"kind":"struct","fields":[{"name":"expiryIntervalSeconds","type":"u32"},{"name":"newExpiryThresholdSeconds","type":"u32"}]}},{"name":"UpdateGreeksArgs","type":{"kind":"struct","fields":[{"name":"index","type":"u8"},{"name":"theo","type":"u64"},{"name":"delta","type":"u32"},{"name":"gamma","type":"u32"},{"name":"volatility","type":"u32"}]}},{"name":"PositionMovementArg","type":{"kind":"struct","fields":[{"name":"index","type":"u8"},{"name":"size","type":"i64"}]}},{"name":"UpdateZetaPricingPubkeysArgs","type":{"kind":"struct","fields":[{"name":"asset","type":{"defined":"Asset"}},{"name":"oracle","type":"publicKey"},{"name":"oracleBackupFeed","type":"publicKey"},{"name":"market","type":"publicKey"},{"name":"perpSyncQueue","type":"publicKey"},{"name":"zetaGroupKey","type":"publicKey"}]}},{"name":"InitializeZetaPricingArgs","type":{"kind":"struct","fields":[{"name":"minFundingRatePercent","type":"i64"},{"name":"maxFundingRatePercent","type":"i64"},{"name":"perpImpactCashDelta","type":"u64"},{"name":"marginInitial","type":"u64"},{"name":"marginMaintenance","type":"u64"},{"name":"pricingNonce","type":"u8"}]}},{"name":"ExpirySeriesStatus","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Initialized"},{"name":"Live"},{"name":"Expired"},{"name":"ExpiredDirty"}]}},{"name":"Kind","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Call"},{"name":"Put"},{"name":"Future"},{"name":"Perp"}]}},{"name":"OrderType","type":{"kind":"enum","variants":[{"name":"Limit"},{"name":"PostOnly"},{"name":"FillOrKill"},{"name":"ImmediateOrCancel"},{"name":"PostOnlySlide"}]}},{"name":"Side","type":{"kind":"enum","variants":[{"name":"Uninitialized"},{"name":"Bid"},{"name":"Ask"}]}},{"name":"Asset","type":{"kind":"enum","variants":[{"name":"SOL"},{"name":"BTC"},{"name":"ETH"},{"name":"APT"},{"name":"ARB"},{"name":"UNDEFINED"}]}},{"name":"MovementType","type":{"kind":"enum","variants":[{"name":"Undefined"},{"name":"Lock"},{"name":"Unlock"}]}},{"name":"TreasuryMovementType","type":{"kind":"enum","variants":[{"name":"Undefined"},{"name":"ToTreasuryFromInsurance"},{"name":"ToInsuranceFromTreasury"},{"name":"ToTreasuryFromReferralsRewards"},{"name":"ToReferralsRewardsFromTreasury"}]}},{"name":"OrderCompleteType","type":{"kind":"enum","variants":[{"name":"Cancel"},{"name":"Fill"},{"name":"Booted"}]}},{"name":"MarginRequirement","type":{"kind":"enum","variants":[{"name":"Initial"},{"name":"Maintenance"},{"name":"MaintenanceIncludingOrders"},{"name":"MarketMakerConcession"}]}},{"name":"MarginAccountType","type":{"kind":"enum","variants":[{"name":"Normal"},{"name":"MarketMaker"}]}},{"name":"PlaceOrderType","type":{"kind":"enum","variants":[{"name":"PlaceOrder"},{"name":"PlacePerpOrder"}]}},{"name":"ValidationType","type":{"kind":"enum","variants":[{"name":"Place"},{"name":"Cancel"},{"name":"OpenOrders"}]}},{"name":"TraitType","type":{"kind":"enum","variants":[{"name":"MarginAccount"},{"name":"CrossMarginAccount"}]}}],"events":[{"name":"TradeEvent","fields":[{"name":"marginAccount","type":"publicKey","index":false},{"name":"index","type":"u8","index":false},{"name":"size","type":"u64","index":false},{"name":"costOfTrades","type":"u64","index":false},{"name":"isBid","type":"bool","index":false},{"name":"clientOrderId","type":"u64","index":false},{"name":"orderId","type":"u128","index":false}]},{"name":"TradeEventV2","fields":[{"name":"marginAccount","type":"publicKey","index":false},{"name":"index","type":"u8","index":false},{"name":"size","type":"u64","index":false},{"name":"costOfTrades","type":"u64","index":false},{"name":"isBid","type":"bool","index":false},{"name":"clientOrderId","type":"u64","index":false},{"name":"orderId","type":"u128","index":false},{"name":"asset","type":"u8","index":false},{"name":"user","type":"publicKey","index":false},{"name":"isTaker","type":"bool","index":false},{"name":"sequenceNumber","type":"u64","index":false}]},{"name":"TradeEventV3","fields":[{"name":"marginAccount","type":"publicKey","index":false},{"name":"index","type":"u8","index":false},{"name":"size","type":"u64","index":false},{"name":"costOfTrades","type":"u64","index":false},{"name":"isBid","type":"bool","index":false},{"name":"clientOrderId","type":"u64","index":false},{"name":"orderId","type":"u128","index":false},{"name":"asset","type":{"defined":"Asset"},"index":false},{"name":"user","type":"publicKey","index":false},{"name":"isTaker","type":"bool","index":false},{"name":"sequenceNumber","type":"u64","index":false}]},{"name":"PositionMovementEvent","fields":[{"name":"netBalanceTransfer","type":"i64","index":false},{"name":"marginAccountBalance","type":"u64","index":false},{"name":"spreadAccountBalance","type":"u64","index":false},{"name":"movementFees","type":"u64","index":false}]},{"name":"PlaceOrderEvent","fields":[{"name":"fee","type":"u64","index":false},{"name":"oraclePrice","type":"u64","index":false},{"name":"orderId","type":"u128","index":false},{"name":"expiryTs","type":"u64","index":false}]},{"name":"LiquidationEvent","fields":[{"name":"liquidatorReward","type":"u64","index":false},{"name":"insuranceReward","type":"u64","index":false},{"name":"costOfTrades","type":"u64","index":false},{"name":"size","type":"i64","index":false},{"name":"remainingLiquidateeBalance","type":"u64","index":false},{"name":"remainingLiquidatorBalance","type":"u64","index":false},{"name":"markPrice","type":"u64","index":false},{"name":"underlyingPrice","type":"u64","index":false},{"name":"liquidatee","type":"publicKey","index":false},{"name":"liquidator","type":"publicKey","index":false},{"name":"asset","type":{"defined":"Asset"},"index":false}]},{"name":"OrderCompleteEvent","fields":[{"name":"marginAccount","type":"publicKey","index":false},{"name":"user","type":"publicKey","index":false},{"name":"asset","type":{"defined":"Asset"},"index":false},{"name":"marketIndex","type":"u8","index":false},{"name":"side","type":{"defined":"Side"},"index":false},{"name":"unfilledSize","type":"u64","index":false},{"name":"orderId","type":"u128","index":false},{"name":"clientOrderId","type":"u64","index":false},{"name":"orderCompleteType","type":{"defined":"OrderCompleteType"},"index":false}]},{"name":"ApplyFundingEvent","fields":[{"name":"marginAccount","type":"publicKey","index":false},{"name":"user","type":"publicKey","index":false},{"name":"asset","type":{"defined":"Asset"},"index":false},{"name":"balanceChange","type":"i64","index":false},{"name":"remainingBalance","type":"u64","index":false},{"name":"fundingRate","type":"i64","index":false},{"name":"oraclePrice","type":"u64","index":false}]}],"errors":[{"code":6000,"name":"DepositOverflow","msg":"Deposit overflow"},{"code":6001,"name":"Unreachable","msg":"Unreachable"},{"code":6002,"name":"FailedInitialMarginRequirement","msg":"Failed initial margin requirement"},{"code":6003,"name":"LiquidatorFailedMarginRequirement","msg":"Liquidator failed margin requirement"},{"code":6004,"name":"CannotLiquidateOwnAccount","msg":"Cannot liquidate own account"},{"code":6005,"name":"CrankInvalidRemainingAccounts","msg":"Invalid cranking remaining accounts"},{"code":6006,"name":"IncorrectTickSize","msg":"Incorrect tick size"},{"code":6007,"name":"ZeroPrice","msg":"ZeroPrice"},{"code":6008,"name":"ZeroSize","msg":"ZeroSize"},{"code":6009,"name":"ZeroWithdrawableBalance","msg":"Zero withdrawable balance"},{"code":6010,"name":"DepositAmountExceeded","msg":"Deposit amount exceeds limit and user is not whitelisted"},{"code":6011,"name":"WithdrawalAmountExceedsWithdrawableBalance","msg":"Withdrawal amount exceeds withdrawable balance"},{"code":6012,"name":"AccountHasSufficientMarginPostCancels","msg":"Account has sufficient margin post cancels"},{"code":6013,"name":"OverBankrupt","msg":"Over bankrupt"},{"code":6014,"name":"AccountHasSufficientMargin","msg":"Account has sufficient margin"},{"code":6015,"name":"UserHasNoActiveOrders","msg":"User has no active orders"},{"code":6016,"name":"InvalidExpirationInterval","msg":"Invalid expiration interval"},{"code":6017,"name":"ProductMarketsAlreadyInitialized","msg":"Product markets already initialized"},{"code":6018,"name":"InvalidProductMarketKey","msg":"Invalid product market key"},{"code":6019,"name":"MarketNotLive","msg":"Market not live"},{"code":6020,"name":"MarketPricingNotReady","msg":"Market pricing not ready"},{"code":6021,"name":"UserHasRemainingOrdersOnExpiredMarket","msg":"User has remaining orders on expired market"},{"code":6022,"name":"InvalidSeriesExpiration","msg":"Invalid series expiration"},{"code":6023,"name":"InvalidExpiredOrderCancel","msg":"Invalid expired order cancel"},{"code":6024,"name":"NoMarketsToAdd","msg":"No markets to add"},{"code":6025,"name":"UserHasUnsettledPositions","msg":"User has unsettled positions"},{"code":6026,"name":"NoMarginAccountsToSettle","msg":"No margin accounts to settle"},{"code":6027,"name":"CannotSettleUserWithActiveOrders","msg":"Cannot settle users with active orders"},{"code":6028,"name":"OrderbookNotEmpty","msg":"Orderbook not empty"},{"code":6029,"name":"InvalidNumberOfAccounts","msg":"Invalid number of accounts"},{"code":6030,"name":"InvalidMarketAccounts","msg":"Bids or Asks don't match the Market"},{"code":6031,"name":"ProductStrikeUninitialized","msg":"Product strike uninitialized"},{"code":6032,"name":"PricingNotUpToDate","msg":"Pricing not up to date"},{"code":6033,"name":"RetreatsAreStale","msg":"Retreats are stale"},{"code":6034,"name":"ProductDirty","msg":"Product dirty"},{"code":6035,"name":"ProductStrikesInitialized","msg":"Product strikes initialized"},{"code":6036,"name":"StrikeInitializationNotReady","msg":"Strike initialization not ready"},{"code":6037,"name":"UnsupportedKind","msg":"Unsupported kind"},{"code":6038,"name":"InvalidZetaGroup","msg":"Invalid zeta group"},{"code":6039,"name":"InvalidMarginAccount","msg":"Invalid margin account"},{"code":6040,"name":"InvalidGreeksAccount","msg":"Invalid greeks account"},{"code":6041,"name":"InvalidSettlementAccount","msg":"Invalid settlement account"},{"code":6042,"name":"InvalidCancelAuthority","msg":"Invalid cancel authority"},{"code":6043,"name":"CannotUpdatePricingAfterExpiry","msg":"Cannot update pricing after expiry"},{"code":6044,"name":"LoadAccountDiscriminatorAlreadySet","msg":"Account discriminator already set"},{"code":6045,"name":"AccountAlreadyInitialized","msg":"Account already initialized"},{"code":6046,"name":"GreeksAccountSeedsMismatch","msg":"Greeks account seeds mismatch"},{"code":6047,"name":"ZetaGroupAccountSeedsMismatch","msg":"Zeta group account seeds mismatch"},{"code":6048,"name":"MarginAccountSeedsMismatch","msg":"Margin account seeds mismatch"},{"code":6049,"name":"OpenOrdersAccountSeedsMismatch","msg":"Open orders account seeds mismatch"},{"code":6050,"name":"MarketNodeAccountSeedsMismatch","msg":"Market node seeds mismatch"},{"code":6051,"name":"UserTradingFeeWhitelistAccountSeedsMismatch","msg":"User trading fee whitelist account seeds mismatch"},{"code":6052,"name":"UserDepositWhitelistAccountSeedsMismatch","msg":"User deposit whitelist account seeds mismatch"},{"code":6053,"name":"MarketIndexesUninitialized","msg":"Market indexes uninitialized"},{"code":6054,"name":"MarketIndexesAlreadyInitialized","msg":"Market indexes already initialized"},{"code":6055,"name":"CannotGetUnsetStrike","msg":"Cannot get unset strike"},{"code":6056,"name":"CannotSetInitializedStrike","msg":"Cannot set initialized strike"},{"code":6057,"name":"CannotResetUninitializedStrike","msg":"Cannot set initialized strike"},{"code":6058,"name":"CrankMarginAccountNotMutable","msg":"CrankMarginAccountNotMutable"},{"code":6059,"name":"InvalidAdminSigner","msg":"InvalidAdminSigner"},{"code":6060,"name":"UserHasActiveOrders","msg":"User still has active orders"},{"code":6061,"name":"UserForceCancelInProgress","msg":"User has a force cancel in progress"},{"code":6062,"name":"FailedPriceBandCheck","msg":"Failed price band check"},{"code":6063,"name":"UnsortedOpenOrdersAccounts","msg":"Unsorted open orders accounts"},{"code":6064,"name":"AccountNotMutable","msg":"Account not mutable"},{"code":6065,"name":"AccountDiscriminatorMismatch","msg":"Account discriminator mismatch"},{"code":6066,"name":"InvalidMarketNodeIndex","msg":"Invalid market node index"},{"code":6067,"name":"InvalidMarketNode","msg":"Invalid market node"},{"code":6068,"name":"LUTOutOfBounds","msg":"Lut out of bounds"},{"code":6069,"name":"RebalanceInsuranceInvalidRemainingAccounts","msg":"Rebalance insurance vault with no margin accounts"},{"code":6070,"name":"InvalidMintDecimals","msg":"Invalid mint decimals"},{"code":6071,"name":"InvalidZetaGroupOracle","msg":"Invalid oracle for this zeta group"},{"code":6072,"name":"InvalidZetaGroupDepositMint","msg":"Invalid zeta group deposit mint"},{"code":6073,"name":"InvalidZetaGroupRebalanceMint","msg":"Invalid zeta group rebalance insurance vault mint"},{"code":6074,"name":"InvalidDepositAmount","msg":"Invalid deposit amount"},{"code":6075,"name":"InvalidTokenAccountOwner","msg":"Invalid token account owner"},{"code":6076,"name":"InvalidWithdrawAmount","msg":"Invalid withdraw amount"},{"code":6077,"name":"InvalidDepositRemainingAccounts","msg":"Invalid number of remaining accounts in deposit"},{"code":6078,"name":"InvalidPlaceOrderRemainingAccounts","msg":"Invalid number of remaining accounts in place order"},{"code":6079,"name":"ClientOrderIdCannotBeZero","msg":"ClientOrderIdCannotBeZero"},{"code":6080,"name":"ZetaGroupHalted","msg":"Zeta group halted"},{"code":6081,"name":"ZetaGroupNotHalted","msg":"Zeta group not halted"},{"code":6082,"name":"HaltMarkPriceNotSet","msg":"Halt mark price not set"},{"code":6083,"name":"HaltMarketsNotCleaned","msg":"Halt markets not cleaned"},{"code":6084,"name":"HaltMarketNodesNotCleaned","msg":"Halt market nodes not cleaned"},{"code":6085,"name":"CannotExpireOptionsAfterExpirationThreshold","msg":"Cannot expire options after expiration threshold"},{"code":6086,"name":"PostOnlyInCross","msg":"Post only order in cross"},{"code":6087,"name":"FillOrKillNotFullSize","msg":"Fill or kill order was not filled for full size"},{"code":6088,"name":"InvalidOpenOrdersMapOwner","msg":"Invalid open orders map owner"},{"code":6089,"name":"AccountDidNotSerialize","msg":"Failed to serialize the account"},{"code":6090,"name":"OpenOrdersWithNonEmptyPositions","msg":"Cannot close open orders account with non empty positions"},{"code":6091,"name":"CannotCloseNonEmptyMarginAccount","msg":"Cannot close margin account that is not empty"},{"code":6092,"name":"InvalidTagLength","msg":"Invalid tag length"},{"code":6093,"name":"NakedShortCallIsNotAllowed","msg":"Naked short call is not allowed"},{"code":6094,"name":"InvalidSpreadAccount","msg":"Invalid spread account"},{"code":6095,"name":"CannotCloseNonEmptySpreadAccount","msg":"Cannot close non empty spread account"},{"code":6096,"name":"SpreadAccountSeedsMismatch","msg":"Spread account seeds mismatch"},{"code":6097,"name":"SpreadAccountHasUnsettledPositions","msg":"Spread account seeds mismatch"},{"code":6098,"name":"SpreadAccountInvalidExpirySeriesState","msg":"Spread account invalid expiry series state"},{"code":6099,"name":"InsufficientFundsToCollateralizeSpreadAccount","msg":"Insufficient funds to collateralize spread account"},{"code":6100,"name":"FailedMaintenanceMarginRequirement","msg":"Failed maintenance margin requirement"},{"code":6101,"name":"InvalidMovement","msg":"Invalid movement"},{"code":6102,"name":"MovementOnExpiredSeries","msg":"Movement on expired series"},{"code":6103,"name":"InvalidMovementSize","msg":"Invalid movement size"},{"code":6104,"name":"ExceededMaxPositionMovements","msg":"Exceeded max position movements"},{"code":6105,"name":"ExceededMaxSpreadAccountContracts","msg":"Exceeded max spread account contracts"},{"code":6106,"name":"OraclePriceIsInvalid","msg":"Fetched oracle price is invalid"},{"code":6107,"name":"InvalidUnderlyingMint","msg":"Provided underlying mint address is invalid"},{"code":6108,"name":"InvalidReferrerAlias","msg":"Invalid referrer alias - Invalid length"},{"code":6109,"name":"ReferrerAlreadyHasAlias","msg":"Referrer already has alias"},{"code":6110,"name":"InvalidTreasuryMovementAmount","msg":"Invalid treasury movement amount"},{"code":6111,"name":"InvalidReferralsAdminSigner","msg":"Invalid referrals admin signer"},{"code":6112,"name":"InvalidSetReferralsRewardsRemainingAccounts","msg":"Invalid set referrals rewards remaining accounts"},{"code":6113,"name":"SetReferralsRewardsAccountNotMutable","msg":"Referrals account not mutable"},{"code":6114,"name":"InvalidClaimReferralsRewardsAmount","msg":"Invalid claim referrals rewards: not enough in refererals rewards wallet"},{"code":6115,"name":"InvalidClaimReferralsRewardsAccount","msg":"Invalid claim referrals rewards: referrals account is not a referral or referrer account"},{"code":6116,"name":"ReferralAccountSeedsMismatch","msg":"Referral account seeds mismatch"},{"code":6117,"name":"ReferrerAccountSeedsMismatch","msg":"Referrer account seeds mismatch"},{"code":6118,"name":"ProtectedMmMarginAccount","msg":"Market maker accounts are protected from liquidation"},{"code":6119,"name":"CannotWithdrawWithOpenOrders","msg":"Cannot withdraw with open orders"},{"code":6120,"name":"FundingRateNotUpToDate","msg":"Perp funding rate not up to date"},{"code":6121,"name":"PerpSyncQueueFull","msg":"Perp taker/maker sync queue is full"},{"code":6122,"name":"PerpSyncQueueAccountSeedsMismatch","msg":"PerpSyncQueue account seeds mismatch"},{"code":6123,"name":"PerpSyncQueueEmpty","msg":"Program tried to pop from an empty perpSyncQueue"},{"code":6124,"name":"InvalidNonPerpMarket","msg":"Perp product index given in placeOrder, use placePerpOrder"},{"code":6125,"name":"InvalidPerpMarket","msg":"Non-perp product index given in placePerpOrder, use placeOrder"},{"code":6126,"name":"CannotInitializePerpMarketNode","msg":"Not allowed to initialize market node for a perp market"},{"code":6127,"name":"DeprecatedInstruction","msg":"Instruction is deprecated, please use the newer version"},{"code":6128,"name":"ForceCancelExpiredTIFOrdersOnly","msg":"Can only force cancel expired TIF orders"},{"code":6129,"name":"InvalidPlaceOrderAuthority","msg":"Invalid place order authority"},{"code":6130,"name":"InvalidOpenOrdersAuthority","msg":"Invalid open orders authority"},{"code":6131,"name":"InsuranceVaultSeedsMismatch","msg":"Insurance vault seeds mismatch"},{"code":6132,"name":"OpenInterestLimitBreach","msg":"Open interest limit breach, decrease your position"},{"code":6133,"name":"WithdrawLimitBreach","msg":"Withdraw limit breach, wait to withdraw more"},{"code":6134,"name":"InvalidPricingOracle","msg":"Invalid oracle for this pricing account"},{"code":6135,"name":"PricingAccountSeedsMismatch","msg":"Pricing account seeds mismatch"},{"code":6136,"name":"ZetaHalted","msg":"Zeta exchange is halted"},{"code":6137,"name":"ZetaNotHalted","msg":"Zeta exchange is not halted"},{"code":6138,"name":"NotFreshCrossMarginAccount","msg":"Cross margin account is not unused, close it and make a new one"},{"code":6139,"name":"CannotCloseNonEmptyMarginAccountManager","msg":"Cannot close margin account manager that is not empty"},{"code":6140,"name":"CannotMigrateWithOpenOrders","msg":"Cannot migrate to cross margin account with open orders, close all open orders"},{"code":6141,"name":"InvalidMarginAccountType","msg":"Invalid margin account type - account is not MarginAccount or CrossMarginAccount"},{"code":6142,"name":"MarginAccountAssetMismatch","msg":"Margin account asset mismatched with instruction argument asset"},{"code":6143,"name":"FeatureUnavailable","msg":"Feature is not available yet"},{"code":6144,"name":"MarginAccountCannotLiquidateCrossMarginAccount","msg":"MarginAccount cannot liquidate CrossMarginAccount"}]} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_app_cli/Cargo.toml b/migration/khadhroony-bot2-reference/kb_app_cli/Cargo.toml new file mode 100644 index 0000000..250b4e3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_cli/Cargo.toml @@ -0,0 +1,22 @@ +# file: kb_app_cli/Cargo.toml +# version: 2 + +[package] +name = "kb_app_cli" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[[bin]] +name = "kb_app_cli" +path = "src/main.rs" + +[dependencies] +kb_core = { path = "../kb_core" } +kb_model = { path = "../kb_model" } +kb_pipeline = { path = "../kb_pipeline" } +kb_logging = { path = "../kb_logging" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_app_cli/README.md b/migration/khadhroony-bot2-reference/kb_app_cli/README.md new file mode 100644 index 0000000..8025523 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_cli/README.md @@ -0,0 +1,17 @@ + + + +# kb_app_cli + +Ce crate fournit les commandes CLI de contrôle, replay et diagnostic. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_app_cli/src/main.rs b/migration/khadhroony-bot2-reference/kb_app_cli/src/main.rs new file mode 100644 index 0000000..5df8d22 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_cli/src/main.rs @@ -0,0 +1,12 @@ +// file: kb_app_cli/src/main.rs +// version: 1 + +//! Binary entry point for the `kb_app_cli` crate. + +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +fn main() { + std::println!("kb_app_cli skeleton is ready"); +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/Cargo.toml b/migration/khadhroony-bot2-reference/kb_app_demo/Cargo.toml new file mode 100644 index 0000000..c813d4d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/Cargo.toml @@ -0,0 +1,68 @@ +# file: kb_app_demo/Cargo.toml +# version: 26 + +[package] +name = "kb_app_demo" +edition.workspace = true +version.workspace = true +license.workspace = true +authors.workspace = true +publish.workspace = true + +[lib] +# The `_lib` suffix may seem redundant, but it is necessary +# to make the lib name unique and wouldn't conflict with the bin name. +# This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519 +name = "kb_app_demo_lib" +crate-type = ["staticlib", "cdylib", "rlib"] + +[build-dependencies] +tauri-build.workspace = true + +[dependencies] +chrono.workspace = true +fs2.workspace = true +kb_config = { path = "../kb_config" } +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_decoder_solana_core = { path = "../kb_decoder_solana_core" } +kb_decoder_spl_associated_token_account = { path = "../kb_decoder_spl_associated_token_account" } +kb_decoder_spl_elgamal_registry = { path = "../kb_decoder_spl_elgamal_registry" } +kb_decoder_spl_memo = { path = "../kb_decoder_spl_memo" } +kb_decoder_spl_token = { path = "../kb_decoder_spl_token" } +kb_decoder_spl_token_2022 = { path = "../kb_decoder_spl_token_2022" } +kb_execution_api = { path = "../kb_execution_api" } +kb_executor_spl_associated_token_account = { path = "../kb_executor_spl_associated_token_account" } +kb_executor_spl_elgamal_registry = { path = "../kb_executor_spl_elgamal_registry" } +kb_executor_spl_token = { path = "../kb_executor_spl_token" } +kb_executor_spl_token_2022 = { path = "../kb_executor_spl_token_2022" } +kb_logging = { path = "../kb_logging" } +kb_materializer_admin = { path = "../kb_materializer_admin" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_materializer_compliance_audit = { path = "../kb_materializer_compliance_audit" } +kb_materializer_fees = { path = "../kb_materializer_fees" } +kb_materializer_lifecycle = { path = "../kb_materializer_lifecycle" } +kb_materializer_risk = { path = "../kb_materializer_risk" } +kb_materializer_staking = { path = "../kb_materializer_staking" } +kb_materializer_token_accounts = { path = "../kb_materializer_token_accounts" } +kb_materializer_transaction_annotations = { path = "../kb_materializer_transaction_annotations" } +kb_model = { path = "../kb_model" } +kb_pipeline = { path = "../kb_pipeline" } +kb_program_ids = { path = "../kb_program_ids" } +kb_store_core = { path = "../kb_store_core" } +kb_store_pg = { path = "../kb_store_pg" } +kb_rpc = { path = "../kb_rpc" } +kb_wallet = { path = "../kb_wallet" } +rustls.workspace = true +serde.workspace = true +serde_json.workspace = true +solana-pubkey.workspace = true +spl-associated-token-account-interface.workspace = true +tauri.workspace = true +tauri-plugin-tracing.workspace = true +tokio.workspace = true +tracing.workspace = true +ts-rs.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/README.md b/migration/khadhroony-bot2-reference/kb_app_demo/README.md new file mode 100644 index 0000000..b039a7d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/README.md @@ -0,0 +1,144 @@ + + + +# kb_app_demo + +Application Tauri de démonstration pour valider progressivement les briques runtime de `khadhroony-bot2`. + +## Fenêtres disponibles + +- `main` : accueil et rendu du README workspace ; +- `demo_config` : configuration active, profil actif et schéma JSON ; +- `demo_http` : test manuel du pool HTTP JSON-RPC standard ; +- `demo_ws` : test manuel des neuf subscriptions standard via la session persistante typée de `kb_rpc` ; +- `demo_backfill` : campagnes HTTP bornées par signatures, programme, token ou pool ; +- `demo_core_extraction` : extraction canonique vers core par signatures, pending, plage de slots ou program id ; +- `demo_decode_replay` : replay commun de décodage et matérialisation par signatures, programme, état et instruction path ; +- `demo_execution_solana_core` : fenêtre unique de simulation et d’exécution System transfer, SPL Memo v4, SPL Token `TransferChecked` ou création ATA sur Devnet, avec wallet persistant, confirmation opérateur et replay post-exécution ; +- `demo_sql_diag`, `demo_sql_pg_raw`, `demo_sql_pg_core` : diagnostics PostgreSQL ; +- `demo_sql_replay_candidates` : filtres read-only et exports de signatures, programmes et entités ; +- `splash` : écran de chargement. + +## Commande de développement + +```bash +cargo tauri dev -c kb_app_demo/tauri.conf.json +``` + + +## Démo WebSocket `0.4.2-pre.025` + +`demo_ws` est désormais un adaptateur Tauri mince vers `kb_rpc::WsSession`. La fenêtre sélectionne un rôle et une méthode, convertit les champs UI en `StandardWsRequest`, affiche les souscriptions actives et relaie les événements typés avec une limitation de débit vers la WebView. Elle ne gère plus directement la socket, les IDs JSON-RPC, les réponses subscribe/unsubscribe, les timeouts, la reconnexion ou le réabonnement. + +La même connexion reste partagée entre les subscriptions compatibles d’un endpoint. Les trois méthodes instables ne sont disponibles que lorsque la configuration de l’endpoint annonce explicitement leur request kind. Si le nœud rejette une de ces méthodes comme absente ou non activée, `kb_rpc` désactive automatiquement sa capacité pour la session. Les IDs distants affichés sont actualisés après un réabonnement ; l’identifiant local stable reste interne à la bibliothèque. + +Les payloads Tauri actifs utilisent des nombres JSON et non des `BigInt`. Le frontend refuse tout identifiant d’unsubscribe qui n’est pas un entier sûr JavaScript avant d’appeler `invoke`. + +## Démo backfill `0.3.3` + +`demo_backfill` construit des requêtes bornées vers le moteur réutilisable de `kb_pipeline`, affiche les événements de progression et permet un arrêt coopératif. La fenêtre n’implémente elle-même ni pagination RPC ni persistance. + +## Démo extraction core `0.3.4` + +`demo_core_extraction` réutilise le template global et des accordéons Bootstrap 5. Elle expose : + +- signatures explicites ; +- lot raw en état `received` ; +- plage inclusive de slots ; +- replay par program id déjà présent dans core ; +- limite, concurrence, force replay par signatures et mode explicite « toutes les signatures » ; +- version du processor ; +- journal borné, résumé JSON et arrêt coopératif. + +La logique profonde reste dans `kb_pipeline`. Tauri ne construit que la requête, connecte `kb_store_pg` et relaie les événements. + +## Démo candidats replay SQL `0.3.4` + +`demo_sql_replay_candidates` utilise DataTables avec les intégrations Bootstrap 5 et Select. Elle expose cinq tableaux indépendants : + +- transactions/signatures filtrables par raw, ledger, slots, programme et entité ; +- programmes agrégés depuis outer instructions, inner instructions et logs reliés ; +- mints issus des balance changes core ; +- owners issus des balance changes core ; +- account keys issus des comptes résolus core. + +Les requêtes PostgreSQL sont statiques, paramétrées et bornées. Chaque ligne possède une case à cocher et le header permet de sélectionner ou désélectionner toutes les lignes filtrées. DataTables affiche dix lignes par défaut. Les lignes sélectionnées, ou à défaut les lignes correspondant au filtre local, peuvent être copiées sous forme multiligne ou exportées en CSV. Le frontend produit un CSV UTF-8 avec BOM et séparateur point-virgule, puis une commande Rust l’écrit sous `./data/exports_csv/` sans écraser un fichier existant. LibreOffice Calc peut conserver son dialogue d’import de texte, car CSV ne transporte pas de métadonnées normalisées d’encodage ou de séparateur. Le sélecteur de programmes connus est alimenté par le registre énumérable de `kb_program_ids`. La copie des signatures est directement réutilisable dans `demo_core_extraction`. + +Les tableaux mints, owners et comptes exposent les transactions distinctes, les occurrences core, le ratio occurrences par transaction, les slots minimum et maximum et leur étendue. Les headers documentent ces métriques avec des tooltips Bootstrap 5. + +Les cartes de filtres possèdent un bouton `Réinitialiser` qui restaure leurs valeurs par défaut, efface le filtre local DataTables, annule la sélection courante et recharge le tableau. Les limites sont contrôlées contre `maximumLimit` avant l’invocation Tauri ; une valeur supérieure à la borne backend n’est plus envoyée à PostgreSQL. Les signatures, program IDs, mints, owners et account keys sont tronqués visuellement pour conserver des tableaux lisibles. Leur valeur complète reste accessible par tooltip et par une action de copie individuelle directement dans la cellule, en complément des copies multiligne. + +## Politique future pour les transactions Solana échouées + +Les futures étapes de décodage doivent traiter les transactions confirmées dont `meta.err` est renseigné. Leur message, leurs instructions, leurs inner instructions disponibles, leurs logs, leur erreur et leur consommation de ressources restent des données d’observation importantes. Elles doivent être décodées avec un statut explicite d’échec on-chain et ne doivent pas être confondues avec un échec du pipeline. + +La matérialisation doit rester prudente : aucune mutation métier supposée réussie ne doit être produite à partir d’une transaction atomiquement annulée. Les événements d’intention, d’échec, d’audit, de diagnostic et les frais réellement prélevés peuvent en revanche être conservés dans des familles dédiées. + +## Démo replay decode `0.4.0` + +`demo_decode_replay` consomme les inputs contextualisés déjà présents dans core. La fenêtre expose les signatures copiées depuis `demo_sql_replay_candidates`, le program ID, les états d’instruction, les instruction paths, les décodeurs disponibles, leur version, la concurrence bornée, le force replay sécurisé, l’arrêt coopératif et la matérialisation optionnelle uniquement lorsqu’un matérialiseur est enregistré. Elle affiche les événements de progression, le résumé par processor ainsi que des diagnostics read-only sur les tables decode, mat, ledger et couverture. Elle propose aussi un journal borné des annotations de transaction commitées, filtrable par signature ; cette vue typée ne donne pas accès au JSON libre ni aux autres familles matérialisées. + +Pour valider Memo sur un corpus réel, l’opérateur enchaîne `demo_backfill`, `demo_core_extraction`, la sélection dans `demo_sql_replay_candidates`, puis `demo_decode_replay` avec `spl_memo` et l’option de matérialisation. Le journal d’annotations doit alors afficher la signature et le path attendus. Un second replay sans force doit être compté comme `skipped` et ne doit créer aucune annotation supplémentaire. + +À partir de `pre.020`, la démo enregistre trois matérialiseurs natifs : lifecycle, admin et compliance audit. `0.4.3-pre.002` ajoute staking et les annotations de transaction Memo. Le pipeline sélectionne chacun par surface/entrée exacte ; une observation non pertinente ne crée ni ledger ni refus artificiel. + +La première implémentation concrète est le classifieur `solana_core_native`, qui reconnaît toutes les surfaces natives enregistrées et les classe `unsupported` tant que leur décodage maximal n’est pas implémenté en `0.4.1`. Cette classification volontaire évite tout faux événement tout en alimentant la couverture observée. + +## Démo exécution Solana Core `0.4.2-pre.010` + +`demo_execution_solana_core` est un adaptateur Tauri mince vers `kb_pipeline::execute_devnet_system_transfer`. La fenêtre ne propose que les profils Devnet dont le wallet temporaire est activé et persistant. Elle permet : + +- de générer une adresse destinataire jetable sans exposer sa clé privée ; +- de simuler le message exact sans signature ni envoi ; +- de signer et envoyer après confirmation opérateur explicite ; +- d’afficher le plan, les comptes et signataires requis, les plafonds, l’erreur et les logs de simulation ; +- de suivre l’envoi, la confirmation, l’insertion canonique, l’extraction core et le decode replay ciblé ; +- de demander une annulation coopérative entre deux étapes. + +Le backend vérifie l’existence du destinataire et refuse un transfert inférieur au minimum rent-exempt lorsque l’adresse est neuve. Les secrets restent confinés dans `kb_wallet`; les payloads TS-rs ne contiennent que des clés publiques et des diagnostics. Les opérations administratives natives plus dangereuses restent hors de cette fenêtre même lorsqu’elles seront disponibles dans les bibliothèques. + +`0.4.3-pre.005` ajoute dans cette même fenêtre un panneau SPL Memo v4, sans nouvelle WebView. Il appelle `kb_pipeline::execute_devnet_memo`, reste en simulation seule par défaut et réutilise le verrou, le profil et la confirmation opérateur du parcours System. Après un envoi autorisé, le résumé affiche la signature, la confirmation, l’insertion canonique, l’extraction core, le decode replay, le nombre d’annotations `transaction_annotation` et la validation du second replay idempotent. Les soldes et frais Memo sont sérialisés en chaînes décimales afin de préserver leur exactitude aux frontières JSON Tauri. + +La validation Tauri réelle a couvert le refus d’envoi sans confirmation, une simulation Memo réussie et trois envois v4 confirmés. Chaque résumé terminal a indiqué `annotationCount=1` et `idempotenceValidated=true`. Le clic accidentel sur le bouton System sans destinataire produit indépendamment le refus attendu « Le destinataire est obligatoire » et ne concerne pas le parcours Memo. + +`0.4.4-pre.007` ajoute dans la même WebView un panneau SPL Token limité à `TransferChecked`, choisi comme parcours opérateur représentatif et non destructif. Le montant brut reste une chaîne décimale, le mint et les decimals sont explicites, et le backend affiche séparément préflight stateful, plan, simulation, confirmation, extraction, decode, nombre de matérialisations et second replay idempotent. La simulation reste le comportement par défaut ; l’envoi exige la confirmation commune et une autorité résolue par le wallet du profil. + +La même fenêtre expose un journal read-only de `kb_sol_mat_events` borné à 500 lignes. Les filtres de signature, mint, compte, famille et opération sont contrôlés ; mint, compte et opération sont comparés exactement au payload typé après la requête PostgreSQL bornée. Le tableau montre slot, opération, famille, montant brut et signature, puis le payload JSON complet de la ligne sélectionnée. Il ne reconstruit ni snapshot final, ni solde, ni OHLC. + +La validation Tauri du 15 juillet 2026 a démarré Vite et l'application, initialisé le schéma +PostgreSQL, puis réussi le préflight et la simulation Token. Le bouton d'envoi a d'abord été refusé +sans confirmation opérateur, puis refusé avec un diagnostic exact lorsque l'autorité Token fournie +ne correspondait pas au wallet persistant du profil. Ce comportement est attendu : la WebView ne +transporte aucune clé externe et l'orchestrateur ne peut signer qu'avec le wallet sélectionné. Un +envoi depuis cette fenêtre exige donc des comptes dont l'autorité est ce wallet ; le parcours +backend Devnet aligné a déjà validé l'envoi et la matérialisation complets. + +`0.4.5-pre.005` ajoute dans la même fenêtre le panneau SPL Associated Token Account. Il sélectionne +le Token Program classique ou Token-2022, dérive l'ATA readonly depuis wallet, programme et mint, +puis propose `Create` ou `CreateIdempotent`. Le backend conserve simulation seule par défaut, +affiche préflight, plafond rent/frais et postconditions, et exige la confirmation commune avant +l'envoi. Après confirmation, il hydrate la transaction, extrait le core, décode les instructions +ATA et CPI Token, matérialise leurs faits propriétaires puis vérifie un second replay idempotent. + +Le journal ATA réutilise la requête PostgreSQL bornée du journal Token et limite la vue aux faits +lifecycle du processor `spl_token_accounts`. Les filtres signature, mint, ATA et opération sont +exacts ; aucune clé privée ni logique de dérivation n'est exposée dans la WebView. `RecoverNested` +reste hors UI et a été validé séparément par le test opt-in Devnet contrôlé. + +## Frontière de tracing + +`kb_app_demo` utilise uniquement le target canonique `kb_app_demo`. Les anciens identifiants frontend sont validés puis conservés comme champ `frontend_target`; ils ne deviennent plus des targets Rust distincts. + +L’application journalise les invocations Tauri, les actions utilisateur, l’état des fenêtres, la progression relayée et les résumés. Les détails de transport RPC, de parsing, de dispatch, de matérialisation et de persistance appartiennent respectivement à `kb_rpc`, `kb_pipeline`, aux processors et à `kb_store_pg`. + +## Backfill depuis la dernière occurrence + +Dans `demo_backfill`, la direction « Avant, plus anciennes » accepte désormais une signature d’ancrage vide pour les modes programme, token et pool. Le premier appel `getSignaturesForAddress` est alors effectué sans `before`, donc depuis les signatures les plus récentes. La direction « Après, plus récentes » conserve une ancre obligatoire. + +## Validation de clôture `0.4.2` + +`kb_app_demo` compte 88 tests. `cargo tauri dev -c kb_app_demo/tauri.conf.json` démarre Vite, initialise les tables PostgreSQL et permet d’exercer les sessions WebSocket. Aucun `npm run build` séparé n’est requis pour ce jalon : le serveur Vite lancé par Tauri constitue la validation frontend retenue. + +## Token-2022 validation matrix + +La fenêtre `DemoExecutionSpl` regroupe les exécutions Devnet SPL Memo, SPL Token classique et Associated Token Account. Elle réutilise les profils, la simulation obligatoire, la confirmation opérateur, l’hydratation canonique, l’extraction core, le décodage, la matérialisation et le second replay. La fenêtre Solana Core est désormais limitée au System Program. Token-2022 et ElGamal Registry seront ajoutés à cette fenêtre dédiée sans fusionner leurs contrats techniques. diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/build.rs b/migration/khadhroony-bot2-reference/kb_app_demo/build.rs new file mode 100644 index 0000000..3e5528d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/build.rs @@ -0,0 +1,12 @@ +// file: kb_app_demo/build.rs +// version: 1 + +//! Build script for the `kb_app_demo` crate. + +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +fn main() { + tauri_build::build() +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/capabilities/default.json b/migration/khadhroony-bot2-reference/kb_app_demo/capabilities/default.json new file mode 100644 index 0000000..78969af --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/capabilities/default.json @@ -0,0 +1,25 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "default", + "description": "Capability for app", + "windows": [ + "splash", + "main", + "demo_config", + "demo_http", + "demo_ws", + "demo_backfill", + "demo_core_extraction", + "demo_execution_solana_core", + "demo_decode_replay", + "demo_sql_diag", + "demo_sql_pg_raw", + "demo_sql_pg_core", + "demo_sql_replay_candidates", + "demo_execution_spl" + ], + "permissions": [ + "core:default", + "tracing:default" + ] +} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_backfill.html b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_backfill.html new file mode 100644 index 0000000..1f3c835 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_backfill.html @@ -0,0 +1,188 @@ + + + + + + + + + Khadhroony Bot2 - Backfill HTTP + + + + +
+ +
+ +
+
+
+
+
+

Backfill transactionnel canonique

+

Les signatures sont découvertes avec getSignaturesForAddress, hydratées avec getTransaction, puis stockées dans les tables canoniques et d'observation. Une seule campagne peut fonctionner à la fois.

+
+
+ +
+
+

+ +

+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+ +
+

+ +

+
+
+ + + +
+
+
+ +
+

+ +

+
+
+
+
+
+
Optionnelle pour « Avant, plus anciennes » : vide, la recherche commence aux transactions les plus récentes. Obligatoire pour « Après, plus récentes ».
+
+
+
+
+ +
+
+
+ +
+

+ +

+
+
+
+
+
+
Optionnelle pour « Avant, plus anciennes » : vide, la recherche commence aux transactions les plus récentes. Obligatoire pour « Après, plus récentes ».
+
+
+
+
+ +
+
+
+ +
+

+ +

+
+
+
+
+
+
Optionnelle pour « Avant, plus anciennes » : vide, la recherche commence aux transactions les plus récentes. Obligatoire pour « Après, plus récentes ».
+
+
+
+
+ +
+
+
+ +
+

+ +

+
+
+
+

Journal

+
+ +

Résumé JSON

+ +
+
+
+
+
+
+
+ +
+
+
+
© 2026 SASEDEV
+
+
+
+ + + + \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_config.html b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_config.html new file mode 100644 index 0000000..b889903 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_config.html @@ -0,0 +1,93 @@ + + + + + + + + + Khadhroony-Bot2 - Configuration + + + + +
+ +
+ +
+
+
+
+
+
+
+

Profil actif

+
+
Fichier
+
Chargement...
+
Profil
+
Chargement...
+
Environnement
+
Chargement...
+
+
+
+
+ +
+
+
+

Configuration active

+
Chargement...
+
+
+
+ +
+
+
+

Configuration complète

+
Chargement...
+
+
+
+ +
+
+
+

Schéma JSON embarqué

+
Chargement...
+
+
+
+
+
+
+
+ +
+
+
+
+ © 2026 SASEDEV +
+
+
+
+ + + + + \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_core_extraction.html b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_core_extraction.html new file mode 100644 index 0000000..d902858 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_core_extraction.html @@ -0,0 +1,160 @@ + + + + + + + + + Khadhroony Bot2 - Extraction canonical vers core + + + + +
+ +
+ +
+
+
+
+
+

Extraction transactionnelle canonical vers core

+

Chaque transaction canonique sélectionnée est validée, normalisée dans les tables core et enregistrée dans le ledger de traitement au sein d'une seule transaction PostgreSQL.

+
+
+ +
+
+

+ +

+
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+
+
+ +
+

+ +

+
+
+ + + +
+
+
+ +
+

+ +

+
+
+

Sélectionne les transactions canoniques dont l'état de traitement est received, dans l'ordre des slots.

+ +
+
+
+ +
+

+ +

+
+
+
+
+ + +
+
+ + +
+
+ +
+
+
+ +
+

+ +

+
+
+

Sélectionne les transactions raw ayant déjà au moins une instruction core résolue pour ce programme. Ce mode sert aux replays après changement de version ou de hash.

+ + + +
+
+
+ +
+

+ +

+
+
+
+

Journal

+
+ +

Résumé JSON

+ +
+
+
+
+
+
+
+ +
+
+
+
© 2026 SASEDEV
+
+
+
+ + + + \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_decode_replay.html b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_decode_replay.html new file mode 100644 index 0000000..9a2ff17 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_decode_replay.html @@ -0,0 +1,166 @@ + + + + + + + + + Décodage et matérialisation + + + + + +
+ +
+
+
+
+
+
+
+
+

Sélection bornée

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+

Dispatch et replay

+
+
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
Sans signatures explicites, cocher Toutes les signatures pour autoriser un force replay borné. Le filtre d’état est alors ignoré, mais les programmes, paths et la limite restent appliqués.
+
+ + +
+
Chargement de la disponibilité des matérialiseurs…
+
+
+ +
+
+
+
+
+
+
+
+
+ + + +
+

Journal

+ +

Résumé

+ +

Diagnostics

+ +
+
+
+
+
+
+
+
+ + +
+
+ + +
+ +
+

Annotations non chargées.

+
+ + + + + + + + + + + + + + +
SlotSignaturePathGénérationTexteOctetsSignataires vérifiésSHA-256 / provenance
+
+
+
+
+
+
+
+
+
+
© 2026 SASEDEV
+
+ + + + diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_execution_solana_core.html b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_execution_solana_core.html new file mode 100644 index 0000000..8d6582f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_execution_solana_core.html @@ -0,0 +1,73 @@ + + + + + + + + +Exécution Solana Core Devnet + + + +
+ +
+
+
+
+
Cette fenêtre est strictement réservée aux opérations Solana Core. Les opérations SPL sont disponibles dans « Exécution SPL Devnet ».
+
+
+

System Program

solana_native_classifier
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

Journal

+
+
+
+
+
+
+
+

Résumé

+
+
+
+
+

Plan exact

+

Simulation exacte

+

Confirmation et replay

+
+
+
+
+
+
+
+ + + \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_execution_spl.html b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_execution_spl.html new file mode 100644 index 0000000..2ca6b96 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_execution_spl.html @@ -0,0 +1,427 @@ + + + + + + + + Exécution SPL Devnet + + + + +
+ +
+
+
+
+ +
+
+
+
+

+
+
+
+ + +
+
+
+ +
+ + +
+
Une adresse générée ici est jetable : sa clé privée n’est ni conservée ni exposée.
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+
+
+

Exécutions par décodeur

Ouvrir une seule famille à la fois +
+
+
+

+
+
+
+ + +
566 octets UTF-8 maximum. La matérialisation `transaction_annotation` et le second replay d’idempotence sont obligatoires après envoi.
+
+
+ + +
+
+ Ce parcours réutilise le profil, la confirmation opérateur et l’option de force replay du panneau System ci-dessus. Aucune clé privée ne traverse la WebView. +
+
+ + +
+
+
+
+
+

+
+
+
+ Le mint et les decimals sont explicites. Le préflight vérifie les deux comptes, le solde et l’autorité avant toute simulation. L’envoi réutilise le profil et la confirmation opérateur communs. +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
Doit correspondre au wallet persistant du profil pour un envoi.
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+
+

+
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
Actualisé depuis le backend avec les seeds wallet, Token Program et mint.
+
+
+ + + +
+
+
+
+
+

+
+
+
Chaque opération est simulée sur le message exact avant toute signature. Les scénarios sont indépendants et réutilisent la fixture chargée dans les deux terminaux.
+
+
Aucune fixture chargée.
+
+
+
+
+
+
+
+
+
+
+
+
+ + +
+
+
+
+
+

+
+
+
Première démo : dériver le Registry PDA et construire le plan exact avec un Proof Context State existant.
+
+
+
+
+
+
+
+
+
+ Scénarios avec preuve. CreateRegistry, UpdateRegistry et ConfigureAccountWithRegistry restent dans le panneau ElGamal/Confidential et exigent un Proof Context State valide. Ils ne sont pas présentés comme des scénarios publics exécutables. +
+
+
+
+

Résultats détaillés

Plan, simulation, confirmation et journaux métier +
+
+
+

+ +

+
+
+ +
+
+
+
+

+ +

+
+
+ +
+
+
+
+

+ +

+
+
+ +
+
+
+
+
+
+

+
+
+
+
+
+
+

Journal métier SPL Token

+
Événements commités uniquement, sans solde final reconstruit ni OHLC.
+
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + + + + + + + + + + + + + + +
SlotOpérationFamilleMontant brutSignature
Aucune lecture.
+
+ + +
+
+
+
+
+
+

+
+
+
+
+
+
+

Journal lifecycle ATA

+
Création, réutilisation idempotente et récupération nested, sans duplication des CPI Token.
+
+ +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+
+
+
+
+
+
+
+
+

Résumé

+ +
+
+
+
+
+

Journal

+ +
+ +
+
+
+
+
+
+
+
+
© 2026 SASEDEV
+
+
+
+ + + + \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_http.html b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_http.html new file mode 100644 index 0000000..81277d1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_http.html @@ -0,0 +1,96 @@ + + + + + + + + + Khadhroony Bot2 - HTTP JSON-RPC + + + + +
+ +
+ +
+
+
+
+
+
+
+

Requête HTTP Solana standard

+

Cette démo route une requête JSON-RPC HTTP par rôle d'endpoint.

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
Si renseigné, ce tableau JSON remplace le premier argument et la config JSON.
+
+ + +
+
+
+
+
+
+
+

Endpoints HTTP

+ +
+ +
+
+
+
+
+

Résultat

+ +
+ +
+
+
+
+
+
+
+ +
+
+
+
© 2026 SASEDEV
+
+
+
+ + + + + \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_sql_diag.html b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_sql_diag.html new file mode 100644 index 0000000..e2ee18a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_sql_diag.html @@ -0,0 +1,115 @@ + + + + + + + + + Khadhroony-Bot2 - SQL diagnostics + + + + +
+ +
+ +
+
+
+
+
+
+
+
+

Diagnostic PostgreSQL

+
+ + +
+
+
+
Statut
+
Chargement...
+
Fichier config
+
+
Profil
+
+
Backend
+
+
DSN
+
+
Schema courant
+
+
Healthcheck
+
+
Migrations
+
+
+
+
+
+ +
+
+
+

Tables raw/core connues

+
+ + + + + + + + + + + + + + +
TableDomaineRôleExisteLignesSlot minSlot maxDernière insertion
+
+
+
+
+ +
+
+
+

Payload JSON

+
Chargement...
+
+
+
+
+
+
+
+ +
+
+
+
+ © 2026 SASEDEV +
+
+
+
+ + + + + \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_sql_pg_core.html b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_sql_pg_core.html new file mode 100644 index 0000000..03c522b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_sql_pg_core.html @@ -0,0 +1,102 @@ + + + + + + + + + Khadhroony-Bot2 - PostgreSQL core store + + + + +
+ +
+ +
+
+
+
+
+
+
+
+

Core store Solana

+ +
+
+
Statut
+
Chargement...
+
Profil
+
+
DSN
+
+
+
+
+
+ +
+
+
+

Tables core

+
+ + + + + + + + + + + + + + +
TableDomaineRôleExisteLignesSlot minSlot maxDernière insertion
+
+
+
+
+ +
+
+
+

Payload JSON

+
Chargement...
+
+
+
+
+
+
+
+ +
+
+
+
+ © 2026 SASEDEV +
+
+
+
+ + + + + \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_sql_pg_raw.html b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_sql_pg_raw.html new file mode 100644 index 0000000..74a8a75 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_sql_pg_raw.html @@ -0,0 +1,102 @@ + + + + + + + + + Khadhroony-Bot2 - PostgreSQL canonical acquisition + + + + +
+ +
+ +
+
+
+
+
+
+
+
+

Acquisition transactionnelle Solana

+ +
+
+
Statut
+
Chargement...
+
Profil
+
+
DSN
+
+
+
+
+
+ +
+
+
+

Transactions canoniques et observations

+
+ + + + + + + + + + + + + + +
TableDomaineRôleExisteLignesSlot minSlot maxDernière insertion
+
+
+
+
+ +
+
+
+

Payload JSON

+
Chargement...
+
+
+
+
+
+
+
+ +
+
+
+
+ © 2026 SASEDEV +
+
+
+
+ + + + + diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_sql_replay_candidates.html b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_sql_replay_candidates.html new file mode 100644 index 0000000..8fd431f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_sql_replay_candidates.html @@ -0,0 +1,443 @@ + + + + + + + + + Khadhroony Bot2 - Candidats de replay SQL + + + + +
+ +
+ +
+
+
+
+
+

Exploration read-only des candidats

+

Les requêtes sont paramétrées, bornées et sans SQL libre. Utilisez les cases à cocher pour sélectionner les lignes. La case du header sélectionne ou désélectionne toutes les lignes correspondant au filtre local. Sans sélection explicite, la copie et l’export CSV utilisent toutes les lignes filtrées. Les CSV UTF-8 avec BOM et séparateur point-virgule sont écrits par le backend Tauri dans ./data/exports_csv/. LibreOffice Calc peut toujours afficher son dialogue d’import, car le format CSV ne décrit pas formellement son encodage ni son séparateur.

+
+ DSN : + + Limite serveur maximale : + +
+
+
+ + + +
+
+
+
+

Filtres PostgreSQL bornés

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+ +
+
+
+
+

Transactions candidates

+
Aucune requête exécutée.
+
+
+
+ + +
+
Les identifiants longs sont tronqués à l’écran. Survolez-les pour afficher leur valeur complète ou utilisez le bouton de copie individuel.
+
+
+
+ + + + + + + + + + + + + + + + + + +
SélectionSignatureSlotRawCoreLedgerVersionEssaisOuter inst.Inner inst.Outer pr.Inner pr.Mise à jour
+
+
+
+
+ +
+
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+
+
+
+
+
+

Programmes indexés

+
Aucune requête exécutée.
+
+
+
+ + + +
+
Cochez un programme. Sans sélection, l’action accepte une seule ligne restant après le filtre local.
+
+
+
+ + + + + + + + + + + + + + +
SélectionCode connuProgram IDTransactionsOuterInnerLogs liésSlot minSlot max
+
+
+
+
+ +
+
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+
+
+
+
+
+

Mints

+
Aucune requête exécutée.
+
Source : kb_sol_core_balance_changes.mint.
+
+
+
+ + + +
+
Cochez une ligne. Sans sélection, l’action accepte une seule ligne restant après le filtre local.
+
+
+
+ + + + + + + + + + + + + +
SélectionMintTransactionsOccurrencesOcc./txSlot minSlot maxÉtendue
+
+
+
+
+ +
+
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+
+
+
+
+
+

Owners

+
Aucune requête exécutée.
+
Source : kb_sol_core_balance_changes.owner.
+
+
+
+ + + +
+
Cochez une ligne. Sans sélection, l’action accepte une seule ligne restant après le filtre local.
+
+
+
+ + + + + + + + + + + + + +
SélectionOwnerTransactionsOccurrencesOcc./txSlot minSlot maxÉtendue
+
+
+
+
+ +
+
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
+
+
+
+
+
+

Comptes

+
Aucune requête exécutée.
+
Source : kb_sol_core_account_keys.account_key.
+
+
+
+ + + +
+
Cochez une ligne. Sans sélection, l’action accepte une seule ligne restant après le filtre local.
+
+
+
+ + + + + + + + + + + + + +
SélectionAccount keyTransactionsOccurrencesOcc./txSlot minSlot maxÉtendue
+
+
+
+
+
+
+
+
+ +
+
+
+
© 2026 SASEDEV
+
+
+
+ + + + + + diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_ws.html b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_ws.html new file mode 100644 index 0000000..703f9ca --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/demo_ws.html @@ -0,0 +1,113 @@ + + + + + + + + + Khadhroony Bot2 - WebSocket standard + + + + +
+ +
+ +
+
+
+
+
+
+
+

Souscription WebSocket Solana standard

+

Cette démo garde une connexion WebSocket ouverte dans AppState. Le bouton Souscrire réutilise la même connexion tant que l’endpoint reste identique ; la déconnexion est explicite ou réalisée à la fermeture globale de l’application.

+

Les notifications très fréquentes sont limitées côté Rust puis bornées côté interface pour éviter de bloquer la WebView.

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + + +
+
+
+
+
+
+
+
+

Statut

+ Déconnecté +
+ +
+
+
+
+
+

Endpoints WebSocket

+ +
+ +
+
+
+
+
+

Messages

+ +
+ +
+
+
+
+
+
+
+ +
+
+
+
© 2026 SASEDEV
+
+
+
+ + + + + diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/fonts/DOS_Amazigh.ttf b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/fonts/DOS_Amazigh.ttf new file mode 100644 index 0000000000000000000000000000000000000000..e558a292b7dd67a49faf02263e03335e7fa239a1 GIT binary patch literal 24672 zcmeHv34k0$x&K$)J2SgG_kHcmbnoo!?C#8-v%9m&CY#MkE|NeXWOER55dwt7fIxzT zLqv#xh!GVPD~HO zb#--hbya<5)ienr@&T$yJ8ItC`A^M#B}laF1>|m-ecb%%ZExN&NTi;Q^QCj=w|9Lo zJoa@WaVySGS}^74g_}wjeE`=VA+o%%a^3O`UvfOuLFCGbt0mv8;; zXX}X;b9?VzxBT1<%6i3tvX|jpJ7f8})eC;{a~0)3k2+RuSbyeO7hQ1C@p%6#Tz_W6 z#?>2!iE?P)aX7Cc?kj}%eQ(co%iQCBPf_(%m-!n>LtY3&!C>~aeKIKlzW`p#P{&I znJ=#`KzqG-<2Q^_CITnWB;X)T22P|Yz)3U} zIGLsar_fQxUYbhNfzxOP@Fz=gB~I7CZGw4j?m$aVF0&bx91K&qy12@t+z%%Jw;90cEc%9x)n}KK3 zdBAh%1Hg0XeBdV90^Cd&7{8$N=tAHJ=px|xbTM!XT>`v-J_NjwE(KmhA2#;T#dI0) zgLFCY650y<5PbxADO~~lFkNZ9Mwii5z{}~Qz^!yO@FVmw;1zTY@Jjl)@hV+K+khXX zPXMo`Yk?o5PXe!@PXRwppEiC@+vqyrC+IW4Yw5GVPtx_kPtgs)Pt)g(SLiyr5%?MU zH{fUKCgA7jX5jVo1>g;Ii}5mjp8g$pBi#!8H~J#*Cb|uHGkpp81-jk%8Qns60RNr7 z47`=@1b&gW18<|dfM24o7(b=k=|6yX(A~f<(>=gDX$NpS4Fm6@dySXqE3^~%AM`cg z-E<%D9{M_P2i*_+Dt*Ivk%s93;Jx&pz@79U@N4u<;C=KE@ayy~;QjQl@dAB=z72eU zz61PE`Y!N6`X2C`^nKt%^oa31eTyCiK1`1RzfC^?eus7eze_&?evckEenQ`;9|Iqu zCxDOAlfcL5Dc}$2Y2Yq;#(0i?NdE==5j_ihoOT0$OwR$Ipq~Jrr00#@^c1}Se41Va zK0_}7|BHSKe3pI&+)XbV&(d@B3h*cNbKvvzD)0q*4frDM0lq}98@Pvl3H%xT3ivYp z8u$wR2KaOOE$~(Po$(C4MsEQ3&|cs#==Z?a=?}nP(wo3v(LUp8`ZfI#_#1i)_*?oD z@OQKy_y&yt_fpAtihfT9@DGB3Zwdk2Clufxg$jI2R001atj3eHU)X>nq8eBdH9$ku z0-=Z?EUB?q+4!ibsi4JTwc2b}tKDwq*4FYBD{^ERvmnY_Y`D{6u~l2DEJ}4%wS~`6 zOAX2*O*X1^nsu8sFj{diI-NF~-CkYIdDYcizbwO#lxeH1&1$Q*R#{ZkEpI|uZVJ+< z+wQbf^TD;4$aivGPA8w&$SXB9m8<1~h-@{u(`v1*v01EY4O+}Mp%$(PX$U%p$5O+G z!%;!5n_t}HF>{?xSpc=mGG;-Px7DDNthSn3uDjM!D{n$AToKX`pH2@-)YR5;4Yf6l z+(JgK+vO@>spa}{wH8^NM7p|G4nU0^y`kEzc6k%-=cZKGa@}qpIu(b@#prgU7jQ88 ze0ICX<8W}E!(q4A%B#rYf=JgmZi%R?76+r> z?{Ii=&h&E^3>{=(+?GIPG`0)7{{eJEnhr{J^IKp8*ce^>y z?RL4GPEN@pGP1jQIP5Nuy}H)osrJZjMJ-$iX(&6vSdH85_IQFppgRz7yPY@~V==co z6f&=Py(r}7GRWd2N;*BY7z&5mhn}|hYJBo0+|PxOh6)v_vwOW>pD!E+dKtNuTvuJ4 z*BgoW{ha6bqYz*9`63ZcBJJ|aPVxp^c84`!54iYNl*PZ-2xrdNQ6S17Y+x5fdHqF#RXB)8+M_SJi&<9>9$3j5ij40 z5?lyrsEk^pD;y3-BFQ8$oJxhmet#ek4hI5_jp4ASMWdV-jfTUa5K<9jp?nx6{ZUUi z?D2);ewU{@?uw)H{C&2`J0P=X5~4aGK-_r&=~rx_a>;_*-@9FE7s;e4KR6A8{sB;xT{j8o-; zC>cul;&Fd4o(g&VH7QRj#J8dZ7eX2;a!Z#lnZl9HGB!6SlhJ4_mQ2NBU0tbEQHJo=hbYxXM|nB%I>S_zBD>78byibR5FD$)a-Tk9Iax*m?{jqyO((HLlq^G&$O*W>j% z)b_6Np@xQrOs1m)*wES8(7>%~Xh@~Tk8i+vHp_Y0Y(qmjjg(mgcXW0pv(bizXk9}~ zG8A>Tgj$k(E6U=(A(?4_0nj@!lFeqDnhFJAwx=hX)ihntW_5ky#B8>=x21*ia=C1_ zu@R{zWTAW(CAF4#HXBc7TeV2s)f#El_||S*!Bwp(3sYk3)M!gfODicXyU}C9=)@(zOl2fDN*n3OY}AIttiXak%k#FYe?@c;+Qs#amI|^-qzN(w%%e} z+t3isr;i;weR^-PzrVNG-7|9ricX(i>_s$VMr(hjx7gTH9B9=W{R8?yYq8kciV`!5 zy+x#9{@{S$gM)I3=?j3>rgBQaUm~nr;D0_Q9enw?$x{dWdL|5v>+dd%EnirmSgh6H z^B#X79El~8TK(}2jZL|Hdl$?jSW!Z)venp~Zm%yGipJ|wdb%}}ZEk7n=!8KfkC`yK z=a|X4h-M=!l%AGjICDIMJf;JC%jrraU$JuX_ITQugU)9Vj52~TMliYFc2tQKIPZql zS_LMl!A@KR%W4@cl@CDEzMJ)6=&(P8j`}pT&ljO5{t~+18?=w!(xzy)!-qh)s}*m` zcP;;m??V3*8NYy*PR4hPD~)Z&WyVFu7UN=LvvHzvuCdNI*;r<*F_sz=j1yi^p1)F# zUl%M)KF*c+m%wqbUZ>I^tl%Elw*#=7u?v0y_yO4Fhn1gT ziyqed@ocrjLeIe(Z-=!F4;A{!29?GR&w`u12<$O{cOZ_L1^XYiekX8EQ}8g)0D)$7$lW?Nsk z9vT{pdaAe{+=Aj;hfu?*Abqr{+aH*> zaQg#O;`-1KN?8xI6fHh~ZKT|08`^Ab!S!mhmiY^}55~9C(AKS7=lq3fefx!5x5l^P zJ<8`Wdk@JFbZEw4IRia`I?xkShsA~SP#L1M9_Ng-p4QRMp(%KFO@7w=g;UW|eW;cD zJO_F)IV_2N%3jb?40Ha)VQOz5CJmw8Gfbgf2<;Os;2s+6OO2GJ6XO^d4Md}XOqk_p za35)K@n~?vXmFKiaA#<6IcRVTXfWM1Fd>bcOHnIobnM;<(hZTr>l=f*k<+ppRa!xD zA%sSRUW91~3lL65I2++ogliFQLD+%tFoJU_8ukPdFC!>R@luxdouFkeskxn?^9g}b+ z+d8xLw_fK;y1R4Y-t5#9`)EVvV>S#c;@(p2JMfAuETO zp2JMfVW#IW({q^VIn4ANW_k`YJ%^c|!%WXrW_k`YJ%^dT6QaqiZa+B$fG4xXTcC+Oe_I(UK(o}hy#=->%Dc!Ca|po6r*zaVuwK*?TE zv==o8ImUS#@|%&iAlJCD`W+oU!fff|>Ae%U9PVDVHam)1F zy!7?gPgnLvbeZ<9NRQwpIXE%ub25Z~Ef(FZGn;1HIt?A1nQiPupvCmVAq#pI`SAJZ2Srd!3k$$)6Pk}Wvzmd4iYQg=n92xY<`&LR-vo6sOqrCSPT?(<(ZLzu(I*=mJsX z+0%Gj+w?8VdV7~`ncjYU<4&p2V7O@V{oZg;v~?_= zHzgX+eNejQj`Yc&_X!Nj@*LIZOFW1yx|y%LWNZ%ZFxDACEtsh);%dZ%m??*iOx6on z#77Xbeh@><`E`i-z9i4=8YmDx1O*|AS&k)F`2g02nIySlet;dZ8?g;>05RvYGlCx& zFUKj?CA>v%XF6Eu^k%*3V7e#mwRt-WJ?Ty?Oci#wIC)gBS| zGO>PPLg^N_$j_Zy`mtLqnjm@l9QNam_Ctzp+R59EWb?2cNydDco9RF^s?ZF9m^lbH ziz%4jlbg%$A4cp!97W9e+-$xt!TnH;_h)5FAVkm)*0S!D0=M7?Fe~B*)FO5vwj%Z; z=6vqx6yg}a|6lp~%Fo{Uq7i1w?9YtpC(3|pWx%yE;941QtqizU23#uxu9X4T%7ANSz_l{qS{ZPy zOoeM@z_pJ8HVeO6h>L<<=&JVs<1!sb*#n%* zbR3DzrAxp3W$kmeeQp>!z(N1ym?#l$pC%WP<*VvUZv zkIKH4BjfYWIzEu|#>L`k$XXwKsVgz7K{tdD$8=(*X5n+BD;&dvS5f_rk3sz>ev+*&731zT_br>Wl7eG3*6$hY3D5+ql(RV80#wk7v7;P%VN+% zu$YiWzcZs{A8Q(2XYwyFVeWsN+tH`XRO<~1GH>dQG&OT(~n zwR|f^%PqZGd=vIX%iHVADfT*>PS&)y5N6+NC=3@gJh6gRAQ-!1Un)CwS>Fy#q}tp4 z8Sl2#wPRPn5nFoDo$y53!xP4>8-kDZeac=}={t6{N=zyZvoCgnn5L*UYYDA5NYDHi zdvEMmCviFG&I>x355b55VI?Dm@ihJE=9sgzj$q7ryjfH6?k?-vQS=_GJG>vB>5M0Z z1BF=HhNT?vx`fnBp;lnjZ0s-xnO$Bw$E$ms{%kNc<6;n8im6P@64wb!RnlS+qTf|| zTm*PRB{D0P7sS~NY(7zgel|4(QYUW04+dWam&PyAKrAS;|No%-#}MCklb2*t#&)AJ(V%N(ylTaXpV- z2#v6EgAkIEV~(13te`T-3^J#bBbN5M)P%6H7rEFI4kmGu?NRTVdd`hYv?Vv5Gc`5y z!rND9D{jAVX6mY?H=Q?29r>bif;#KGo0eYSzG~&2m&{eSxHhPBFS&E&m97g+UGYf2 z9KK&*@4f*!IN4;8HVls+L%}LN4;7C#FUs+V^9*1+!ON15bz*Zod6j^b?v75sIZko} zZQf$g93VMpn(}@*99Ou{H^*z(rTomj1=EWO?H}9*7)|))#$NP;A7Z1EZWyL6yk4ab zVoV(Cp(&JUf)kj;W^S^Q%L!-*Wh#jxX0Db%oJ8ET%cO-C#GIc)%xg}qh*_&BuwOQP zz%Lt+TrP<{5zOVd0ya*X5Wn3oTV4`{hHKMdMColRudx($7+87|nnJ}Uuf(PTF;f#W z)iF!Yn<@{L+!UKQ7Ar4P&bf#)g4 z{eZ@QOq(13cWs7q+gPsr9`qc=&Y})_8S5VC)tG4s$6yJ^Dwc2zMPjgoW3Yr{u!Li< zgk!LTW3Yr{u!Li$hX`*J|TcSl&A@AOu`@!X?&U58?~!U-;KFz#Rsd&KWa5qtR*`#d47#rMsjP4 zMd+ip(B`rc)FkqyAHxLVZ6+HJY(uG4K6;SN^Uu>J{K65utq;5{L?46!fnMU57T~_@ z1z;VPJpf_E70a_0*D5YgcKNZVjbnECarppqYVK+e0_#V-LczNknOj$(0JC_GnQgGI zlzoOA`=R^7N62fbaFBw1^IB`T(_8R%igDWR-P(lIbI+wFC@;H9pGcLib%`aVn_c46 zlsL^@x>+p2n^fK*0FrhRgm2)09In;48gFgf0G(gUB1c(ZBBlJ4#$Q<_rgar4^R=8db@ z_V+BFn$1pK+#?r*kd@|3;#E({g=YK{wMC&SV~u!T*(ZIye!Ml04DX+-l{PSXTn(p- z?EO>=S@HZ&25%VriT2o|dyj5Z_Kl2t^ik!Zku#KQ_Fr`#ruv`NRfW22sH+CEhiN=h zuBxDBZ9>8zayIIfN#J zyzJGA?rnM++E8_q^bvKGQ|R$`@i1q+84IQ$KwyWHxu$#=_cHG>5-Z_YzEl&7#A0#V zyKYUKGve5@N7?HiS@`6W%B@lpI5Gx@_wxV08iP?;TZajVb=%4^a7c>Hogl2ZWSlHD z61yNA5VJMNT#c>45aJHRY;|`bb|LO#?#6V^ie&E_617ErIaT$=2ct)?{_}ZuZQhd$$QiEnPF`GwQxxH?VG#kkT#(lDK1&aHp(K2Q(nDNgyjnjB~h+W@5S zt(6PnrlQ9RAnR)u#H{!^m|JwJ{xL!ygN0K>cEuYP;>|sKO4WPzh<$wz;eh%M(Il|` zi6yAYN zLl2eCSPrj4SgzW!NO4%WDu0jY+g?SNvL{Qyevv};!|}; zHG}aM@iR@Z*Z$RMQQtSM;X!eSUlY2rtxi`ZqjkBM&33hjzM=Q^mmUUte4FfNpXIMd z-G`>zkY9;a8L2S^pfLq18dCroQvk|L00JujjVS<)DFBTr0F5aCjVS<)DFBTr0F5aC zjVT~CCesNiwYW62xHPo5G_<%hw74|1xHPo5G_<%hw74|1xHPo5G_<%hv^Z9|(2OUL zco~6x98KuvJnuM99#9r}<&@Xt@Y}(%&y&Su9SSxhW^p;{G{wBC==xY9VBu!2^sxCf zpl0OaDjQHPRoVL&IyK+HS$e=bYC20r#dnI{Ju(LW1Y=;@YMY=Z)KZEK875=UG+Q<1 z?h%vcDWGZS8wIZozbWz-#HP}vt{cusU8&umDOdki^o-=oI)?(EsI+O)n4REda#lu3 zlQFir$T$O{koJ;G?vu+FZ0V%RTjRt4q5~V|Y_QK7Xv1Mtm+#r`b`jl~CJMhJp zpc6&Wz97i=Ip~iN#BtNB83%>OD-<3Fg~w4O4hoNh!sDRuI4C?03Xg-rw-2yUW*MtMzR|URiuC80kDb@ib2YBy$dmtU~2yeMxLsFCAEk!oz>SX%1 zscFA;bQdB>{J^I3@-M%l)pw;n(~z&2R=ktv%0ssAC~G2-k?)G%MGyKZ$Q6g#vU5P+ zd>Qc(+8EvxQ+FGe^tf zA}jo?9d%C%4R1mD%kY#af?+<(Mqp-yxB=Zh3C|U z{@JsHdwj)K%(eIFG$son@lT!-xp45EY)LoH+EgXremWZEFR>|q20NZ z*c>k1*V@HSYAwWmXJ=R}N|k=#VD8Sm7<)ylbAtVZVrnbym{O{srxbodx3-9hvF#8PyKMbBygF%}+>gZ#Q zzvAeVd!5;xs9;`Wqm7);tSMDtuq~h+t0V~aNO8(EAMC9Cu{To}V7PWs5W^RyO0NiS z)7r~kdP($`zJ-mQ;@;94ae8T6dG28q7WK1ER)r@C_`JAB)Uv&Zeg8Us5~@Roy!Rnd zTl#for1a)PrB6Up{Z{EHZ06|4rz7QLF#S?7{Ia15giQVc0j8W3SPpTC`o?UV=3K!KRua0Cb1ERYzYg=Eu;$t7_<1Z!jedJ?T6p>O|yEdJ_x$`xHT5;@o zymRyUo4WQ0!bcQZUsFC*?CUd&@;!VrmqIal(;nQ!#mt*tLwECDZ?VbvAl}S=fJV%n z*HSQvrJvIdj7?m`|If_n!h4+4@9H7rd1Ndht%?-^KTjc^te8)1Z?4{Uw|>8 zi9|}-w|_kvg!f$mbl?hC|lN`-0t=H5DDGvO)Z9J7uw1mY?^w-LPMm&zIKNflaTcUllNb!SL1yWcm*5Y=Nm@p!29!i@<8G}2M`^|zYh7#jrjf1 z51cRS;qrCmdcMwid_}yeehT^7ay|Ge$$|WB$Zs_BC$Znj?8klTk0jlVp%-^TB`8W( zbf|B;4eX`-D~S_+S)1^^4%TDe zP1axsWW{@krK#d;@3Lw71-Ub;yzytpyKb2-J$RVCL1-cPq*yC9iOb7$!sEiN75Ifp znNNzf8p>d@aJ{A1{f$z_LV64xpBt1ne;w)iX$ zS~pt1V*QP6Gd{L>89rk8WbG~XYWu17R~#!GJDon~^{y7z>8>xkr@LSBjPqRUUEmA( zPV(L6_xLyXclh@Q>I2h*wZTV1i$dpyt>McewUKF&vm>`e9*$N=zZbhA_LaC2-(J^M zcXcA4*pS$kcqQ>x;;rPinzkx%qP1igdA@u7-q~R8H3*J)@jm1nfAT)(dUpgIqq#K*YS<2$|@zGJlxU$NQ@m&6);8TeRyFL(`l z<#gn2gfEl7=)`~h*onLeIcFke-ns0MI}cyqJC((kk@4(F`1r*f)OYaNIrxh3Ie6|W z*}fI{mhl-lTZyxE5b*!gQx1I>xo!pB;vRg*>Tt&pGN(`mU(1>Xog<6Smo-6qX~rCC z!B@9(Gz()t8{gq*#q;N&jqT`%dFYd4&`VwTrPq9XSL--P?c*^UPr%1O7DCJq;cH@J z(MJpL&E|3VLfBG#y(5l&6hVA{_(lp~&ey>q6u}pc!e*ASc6!Jz%m@9zfTJEoI~W-=j(g8(aNf2SZQ?opbSE}63f5IM%=b*|mEIl`@`PmX zh=1_?nSO^)bWawl=C!SNvJ zR=M{(;){JRr=&m;V;!jr$HojD!)Zn&$OdI11G+rJ;kzeLs-KS=DYrm0N43BC!1aiaOT@k#epJ)x96eFT!W1Cz3*-2;F*dMZo#cFQhQB^71KQG(z61!%<+tq}G2K`xl|J8C! ziUAP?Qy^je|Ng07Ai~dky!3rWl&KtadWPuF{1p*{cPB+XCm(1!4-5K8QC~hk7L|Bx z>j1b2qeL)2Mz9hn&Pay6zC0S$ds&0jTbCeI-3xL*|F5ECW%skX3JGa#1yR}Oi0ppN z>9En9svED1Q^jy1nD0v?6x33kS$UB01$Kp`XhgP2Zt0Ncq&JMifmM`Q=$o;$+3x2& zE*@xp5sAK4BM{@Ie>83D5Xi>SEIu@OwHE6F;#bdBL6+y5HHBOD=isxK=f(noorM;}P-Z9hr=Ss0KT@gb& z0JGU}xAb{>-|&cHr~16-*5LaY^Pk#Ko^zp{r5-VP^Y4=)>ct+p?Zz%jNjaOCX~P{M z!=7^qv!CLrrJxUKIG+iU5>GJ!OaXs!|L%lMh1=)8nGU?0>U)HSzhEfwx^%YDkBM@L z1JE6=iovT43x4X>?V=`P+Vd(tWKynVu7FeJhN@F6ujMVG&Z^S6-m960UpzVn1pOtG z+aMUH=ouS%tTo#(0RkwbGu!N4LSH$1g{A?0*$zL@?hS3b?1H(fB z>)%Y$>Eqo*R@MDfeaF^jiOoStpPaukAuVaE)3)J!$g1; z)myyvZo2ZTkMroEvwlwi*a-CoJEs6c^`39$f25Zbx5j_Pt=cw2Er(^7=0D51OC#cIV)csWNkn zZSeq)R_@!rC;u=f@1_X?p?X!Yx7&AO-b9E;Vs7#c^1vwmsg9vEtrM|im33+PGwWCV zz*M>UAr)`=wUe+0AyAaq!ez;TYA7bAb15;K-c6O`n#m(ohJ|$F6CTfYF?el)q3$k0 znvMe;@IcTGI)xJC?~AozD~l$aLXatjRoQi#qt}!0Oj~(x`6*4Y5J9+6L*1`J>!+;I zcpHdc88G7%fdm10O?wh~TsmGll|Fn| z*H^(v586<3EYsu`U7OUsl|i9eTO-xTMRdcBgT4wF+8IdUxyG7pKiD^l=egLt=+*p| zhLNsaz`n>o%!msMgl@{k*GN?fkxPpniuwh2#tAQ`d0j`k--%sw!FgP1mg~2Na6LP| znf6)pV|9GCu(#dW!FFi;y*Us~TtwhXwAV~TF+jcxy z8Y_eKQw6i)GqlS-&&FS{LUu#82l~@bu2*z_n|XB%o{b`EOhnjF@!{d^*9632pey zgf2vZgstG)($BrHV1KvgZQK8gFQu&Y^SsOb1`j`U{^kL0dJQ=CV!EuI8&sbAG=6g_l&L zG>cq$jZ{l9@hj20Sqv99b1%+}xUbFqun~5=?@Ev}O3RNfCs!~)K+oFt;ON*>eh%`0FV2 zUq1rR)S4d{8M2TWsG!&}F{x1FK#W1AG`?mPpVco$&U9MXjBdI7FY#_68@+i@y=5V~ zjSP`UCdYcTwov0!;eWWOKybfg&zsNQCzD9P9bLC;u0!}G``PCyHX_LpWAbpT(=6kx zs+FyeCRNwC$bGUj{%elwi$SbJs(MYZ_&c5lzAc-yARZ;~rd&a%9}*l^m(D%P96qWk zC)-D)KYmXvm+j9w{}l^F1h4I{Um*AeI&G+VL!TgQuT$cYvzn_2LO9En!Px!X0FM+v zqyMiettfx^^d2Naeyn1^jyOxD^1_7m%*F>x$p70o95!04D03FQr~LfrM~*1s{#P&9 zjcBm9QMqlNd9x&kri)|dL=l&!b3YOP9PLvMqc*(g#K+hU1GMsoZ7umVHY%@lGO*He zFD?dka}i`e1i#rF;<%n={nXv8zBvOr4fpY@sJtWy?1;0ex%2&2cf}nDS{2l?DTa%R zMbIs3%r_%yl~_sr^X*ajAh}53h^L%Tr&@Fnc-_kPx-+hjc4$V*m#>iQC=Cjz;(k6M z%n?lALegU$fS>icLQ?2XZ zt@B^q&RzX*+x#5_@W8J{{3SH3WeS?1@AdT}8a{Q10GY zO*VniZ=BFipMoKB1c?WdVpnqX_5i!!Ku)_U4;L7UB18}k$zt^xMCIsv?B*`TaPdgT9D%n5$1zwNXe z(mx`uj@dUYNxxN2HG%I|% zzo0eG;6A--+D!AMGM})gobgn(`mFF zO}k5EmCnZ}T-va^GSYRt{l5tI=5in2xBX6q2^xLqnR)L&$oOVCvte`}OVHc<_siPP zS{0wcJo!EhZIpG`3`HwSI0TMuyVU=84?PA*8iRcuTIVl4a;?bBgg^I>cR%gES$kj1 zFszfNhZ$%l*(B~ooDL<4G8H*LzTs)t9)G{=;%P@2HtE$2Lnks?e0j?h#y6-r(^)t1 z(GS0|9-yay{wDT71wosUJyDkRA_tbD&j(gPyG7p?F1RA5_%x&>NEH1jluu@Y6yU|r z+RLrA)acIznw86h1z2N%pJt`ZbW^`nEuS>`Fz2&F3fI4kk ztYYG}%6!M{%P&igBnwCx!x}rUC0Iu@`4hWokUv92iuGzS_D>j*QAW#$*)5OhsAkFB0N|d0RY}N3GrvuJe*nTpd+!*GZg*-D{ zY$8AFqG+&REXQQ4X!`k@8~{(T3kga~{bT&wi`t({#&tujyOg<3TcB^7b8B4mf{5-D~rr+U>7X z`?kHn0@HgOS~iTooJK&4uOm*orYD0XXFE2AAep8%dML&Lu*l4{^Xw`(AL_kwQ{;lw zFwg#44Sj%B&nCf)|5{~C;8H$jVn}ZH=dI{9Xl#{6n4|6M(h4% zZjh!U>xbV?ZI`(OM6a$izmUJ(Z;c|ov=4xX_Vp@(Hw@URWEkE?5C^a2`YhAg-#yQ( z^ja=m>YO>(W6xQ1y~~N zO~p*D?F7K6ZNC4CE1GXFHE7zW3zZtF$=EW;5O3?!o+SjwgNZY|XI>_k6&kC*QQbA! zYS~xUta5qJ>5a?s8b^~ol^Gqh z@!b*;P{`1C)!Nr-xFQ5Qs113ZyX{o7t4o)97V+&3(D{~Ex;JynZ+R7YK26wr?7sT6 zQ4()&EPv2lnvRe*&&5oh$ z@o&LK8Ms`ZO4tN3phTZ|)sbhnHt_4^sh0Bmj`V89+gy^RBfKv4l%~}m6z#_|+%Noz z#%yi5=Om%|tFb&tO3sd8WHVBe{rnx=o@Y>cB6;zT`AJL#x{%b3_;7&t?2|E7WvQFO zn%@xi%Z(1d!0m3vMg_QB4CS2$3WC_fRJVl(#iNDPDl9=xm zxk@@rqQkRMEuc_!<*U|p*9oOYCAeJc?SIE$!=0nlnu?b~wAgSC`itUD?!^YKzy<5z z6sP=Yd~NIbN__BC<(y;0L?ejvO90=o}YSy}F!Td@1D9%-UvP&w+zdf)%Kk7kuqb4=Fe}KCTHdI{0&~anx5Ebn*8=HEZ|Yw>xcV6 zw92;pQI{ND9!~xtr=aZSAptdIr9a1c_XqhKS>6$|zgV|kZ$1b}uzk0-mIWj=UN94R(D6P-a<#}$l zcMGKFz84NWdv1!J>YV0!V+QzSX^T{JwAZxiTR0bp0PQPBvm=GLVy4vu%oFu@FLTbF z=7qyQ$AFxj|E&8SUf#+Z!w$2QiBx@H^m`vY30yv!jP4moY9E%Ss6xvxPGdEhWZaI8 zB)&LwC-Is6-g~CHH5M=u<)HQ~bjjIC#@OvU*Xcfa7#R^H?e$8YJN5xo3n4&PlK?X{E*`_-XEo~OVS8IH#RVzcYQuv?S zIi^`F8xp&5A3{xc%*5JbUdSAkYTN0I<($euQFLqtJWVkGecM6O&AJUtOWxbAJaPTR zh8&Bc&*j6kK$X*o-Ds4->iF2@E2W+m-OuDcO*-|hX26#WNeKmD+N6l zT6qq3-<=KT7N>}xfLEKrt`uL8q>}&{B@)A@%qn}58e`mBbOL`i(Y`>$w~20{R>{TL z0W6Kbgm=rr?9W9awxmz#@j9B5pvOf3d{Hk*KfHN^!fGwv#j{kMl+%>C{P~CHT(z8= z?4<2sU$-5A_XPVyMIr#Wo7(Qg$o|q@KHSE3r6?*vY7hWfNJr_x(+I@ET9A5%M_1 zd}>4XX)E6Fr7npxJs8&$8s+CQH}iWoQNBmwu9C_2-WGya;dtVB&cUhESBaHFx0CBj z6Pr=Rpft=QCF+k&4_MBuf3H8+?(CJF1XP_`5)ziLO8=}PpuL5-{Wm7gBq9=VMZ&Vc z&ylnp9I{LHh1jUm0o7ILd=4Dnw0mc5V|q*PYi8V4#dRwQQkX`Hbx}XLM^@3ZX{P-~ zTQBhsI@-CHl`Te`K|-srsuR(wh4GLnW!fXMO3wf4IR zombBHq+=Uj{M9|#adx;fQAAn7m1DzijC!Yvx7b&i&~14Bj!L=GZ50EHg~<@@gp0wY zXMjdNey*o7*ku|g2qd0p15Xa+4);0)G>vjTT4$0MgOZLNHac=8=4?1q?X}b{FG;7< z*5sw=f@m7LhA~>ht37`B-ZX|0HGwO{dQ|qvQp@;7Hap9wEkWRb14WzoLr{(Gi*`+q z!|W3fqw_#p8S$H;tilc-JWkB>xM)*TYyJP!j|hI2q;30tn7NVC@N)aN06G54O-gy1 zc28x{-C-MIflDy9%p7Y?EqJcf@Yb;_<=6@aMV$&?3Ru*dIdXNEaQ`S|?Dm zt}bw%Ild4C3)98-skjwyV)ULI|J_)T>AAbbh(C;8IgF#%g-MHJAAfMCLsVKkGguzbgR&F?S((;OX;G(XY&a!^>a+h4#`!?@7_bMJ zWR2G9kvOX%dbAwaT{0{;l{1SwQWjoa7SA&dFfw0B9G}oWNK*#qgTyn1xq5j9msoh% zQtSZ{K;^GNuZ%-J+0}0g%@cy9dPg`4tZSfvzq7j}ho(W6`s;89@xk0>FcAPQx3s)Z zl3PgjM?sncFDcAh?;#zO!xPY7CJV1Z-xlA( z%klrxisE=3J`ND1qzX6Qe?X0wd54FFMh`MM5L0WB5_RA_B~h@;j~+E2xVu{J6ig#m zAFhb8q{=x19|Qe8;-*xF{Q;p0U=ke~`Fim&ZM=L{iQkG6|K7r5V}DU`j4nyZ;-xY_ zso`IflHIKT$lb}|8rCzo;(Fqln^#K=OFC(f`+NESyEX>4Tx04R}tkv&MmKpe$iKcrGC3U&~2$WWc^q9Ts9 z3Pq?8YK2xEOfLO`CJjl7i=*ILaPVWX>fqw6tAnc`2!4RL3r>nIQsV!TLW>v=j{EWM z-sA2a;BQu$Y6cU4s#!)Rl@xOMRU!0>00z;AAi@$e^*K>Y!*hJy!^hXVIM4Dv_vh$W z3MK=50&#-rhDE$VJhN%(ocD>NtSl+S=fo2RU6A;Z>$1yloJ$V-d1lneX6A{b#6qcy zER!7{35wza#g^{v49#>NRA);4}N!R7N;iNq(~Czd9m$}F(9-Hv>LYkeQevU z6Cm&mTxlJDtqIJ2lHTZOu_It$8@RacX!0I#xdRM8>5?HiQh=tvSOnhB=$rDu&@B*N zb9-y=cl8p%u}*{mPN=HvXySxoa`Or%5^IixfaC9zFhTCLvDjcx#q ztx#2Oxi{m?hul0oJR&3SE0q~gRj=-n85!Zr@#Dwkpa0QEBnrO-0L|asUsNx)Zz{jT z`-=QKN}t{KJ?qzAySkNY)t}3jH7b3IxZk_wKGM}WVXegZu+p87^?%nNAl_xOxY@F= z-t^qy-ULC2`F!4Mf2i*eS$!J!{xyCSF?=199kX%IaQ%$+F`^4${v9BG@ZJab_{mc| ze(53Jc>FRRT$}?k?1jE3cDn!&^Lr@1DM|OJ{)g$txVrzguOIKd_S2uq^cnDlTwD53 zx#3eeU*qteD1tizQS##^)7c%bJYZRJNa=Vovrqan(wB3%3<)RL+VQLkm-U^$RzMPw zG*=_@ed|k(E4EZ1$r{tM_s-|~c00F)Yfn#`jCm#1&w{ZjB$JiyW4xC=45F@IoNMCp z`gv`}pHY_8lfsj<*ALMv4~XO^hsr?gq?^}O=F42(v)_r?c|?!^k>TFLc;%&Ek;5Jn z?})bDBqBJTo#~k}eZKS0r0SOjn>=2> z`hH2?(-MzOS0Lm6$8VRq@lCIN`W%}5V_f6(%qa+Ac}=>%hQEh#gX+MPm3v4H@~}tU z&}#a3_@B$s$x0A&UbTHDVr1p>y=W(0o8j@5pG$NtNp5RMITD-RW>LCz& z|K|K?d(Y{*yf1OW&=A6M=17PLcA>+r>#z%gy#xf2`HXU05s~2>9^(-LK?uX^h=>>? zdPa~$7k2Y&MP0&l&UVxJarUVIh(gD=ReYB?!GSUM?va#r|1f(x(GlNDd+a;l!>7;i=kI+7fAaAUaMkVcJFmWt-+uEKad~!* zAi&uUI7Z;uBYFN)97z&AIX|D$HMr(U>`9=z38#HA{o2*?T|cSwlB;b&KfQO!68onA zA_Y&-nB2*>;J{0qWbK~(x36R!;s%#n?nAFY`f2)#8-WS?VfO+PWF?dK?w!yfNzn&v z4*D=ZLBSG8we9tW`=odHWIM3YVrOSCfi?e9&pch@n_lmf{7!-F`yYLR4?lT=m#!}H z&YNFI>}-sK0h@vXk?|}S?ws~J=L}3+wimQ+b`KWM=K9)G8{Ze11W$`4YiFf$fy_UW zFfoY$h`H?+z#87h!=48SJn4)&DC-Qci5t*S2m28J`YQnjr@(6b9--s`wn!Q*=-@7m z?DJqn2mCAWYVxY->yiXOhcOxnJ7t|K_L=_AMJK2LgjC5F*Y~MNmMnU&(;r0|yWQ$< z|68VS0QfOzKoBLyh( z@5!C8T?hSlF`x&)Vc&=(kBWsvjP!iCv1LVbDQOXa%)Zb9_zR$?+!50C8j@h<;0kriCAO*`)!|yhn2ZDo z=t76C>xQ3kOnOJ(C=wR%ua62Cjfm>ohGbagD-SmLah_L_{I;C4tW?7#d71Z{pvmwz z1>-aq){-j-Jnuw_{5vXup`& z%1O1V*WiDK7)I90R$i#T5{7aursGO--B`e=Zfq%5+wzrIc?R_TeU55MCHvVJW8Wv~ zXiAbd#&1nuA=zpyCP63KVvsy+4=||=PgWg&feWiXn;F!x5}2`qC7Cz$eMHv{b(Bw@ ze2z~)e~!1m_&UDy`YU7bAUGU)gdo^;VKj;ZZan>c_6yYneR3Kqi4mzJgQxsjJ9}*h zKu$^WT5p4Nyl-22J-sT<4u4p_X&L8Fh@QPxk&)iJY`K~AItP?qPCGy9bF?2AwuH>N zyY>}H-pdr!5;9{Tq{-H>K%$de=T@hnmz$!7LU~v1Z*gGWdFjQEXOdu3+E#Pjr%4`| z)=U;fg#b+IhO$zZ5LvPPbEQ;eAk&|tEIC6kagVCxO5-jIdYw6h%9J?VK~!186o6x? z0_yt9e41#UiI*`XrT$6+#S}nvQKpmq(}wNqf~2g&IE4gNvOPPV*Ks|lD#-q=iK;T% zdl8ct&;h9YiY*JV7sn_es4F3-1k160lj$@*&%jp8brCa&S1$lL27!fB#&jv{gCL^; zm`ws_{mp1Y#jU&;_K&{sDw}BdHQDm1io275&?DAQW2G`C zAdE_20uQmk;sU!z)ArPjByVu{%TPfWtB3^6U(^ zhvUM+k+9g~(z+B2u?73+0dSUfHMvqvfo`ZZB%5MAfjlu@ZK~9)BkbZ;C#st-B?Q?@ zM-lyX_Kjsj2vI3Dh>HywLS+}qRs^l|**az9 zrZrvPw={{cg&gmJGGt3c2d!$>b z@A1l`hY$ho4n5BH-HC?1me$dsLBA+uhH12Nij5LKIAts7=Gmf>YjvJ)i3AwUdPeo1 zPMqd6j_{K{Mc)fL5M*Ad(p9~6PM0~f!cF^q&VlqL&cWG2Tbu(((G)!|tEEq^RL3-W zbt$HRJWXOJ$}vvgA9Rc&3z<)|{D@#DI>bDTKr=Nxpsz3!>Y-1Me zD=1fTFDiwKz9bp)kaUQyLCHv|79cB^{{PslKhlmHXP3N0`a`{#WC0-+0tarjw~FxWj3L5764Q&sgY1nj%r5PwXqIT9D9 zNvWNJEV9|=>(_N`(Usy_(9Fd7(6OxId~dq{B032J)q8BEV8Wcq-zOzk>e^^0!j}aw zaf@M$X#q&6D(l1khznAVv)vBYcXv43?QnNI;(Wiu)0-P~-B7T2`N0J~e)b%H@V$SJ z?>zeizw+n}{QY-+4X<8Z;@MqH8faXgN0A$Bx*<+_q}P}0S*l<8869s3VQIJVK)({3 zOf+DWY&bf^hZBQWhep4(Vkjt(1vVis%Wr)0^bPDA~VW@z}15bFcR;7^eGO9BfjwZtGGPh<8bKF$B2vbeVg5}0Ag(= z=ExCd{a$4CT%9Y5lU%nkFlL-X8YB@`&5RVvE@HrrdaoEApBR9ZMPJ)v1|6BObFn`b>u?v{R8WSbv%#l`u1?ksLvSd>G6rmcskfZ_(57l*;$QO$ zX&O<`@TbRfuh^#aqx;kBoBq2h9*E~H6B@fNikfOn!N(`-BMap^+Yij`OlaEVaY=A( zag|c^mbNEZlK7y0yl7b$V<4)(VPGYAR)HoVUV?h&1&{nMS@oI5!xHkiUkfN zP4WsC>)xFJ2~EQ8VA_N*n7tC9$E;iXcGd1bMpGFjaqR zjLupIN>g$q_4q{^KSpwn?~PX< z1fM>8iiek%=wrnBen^5JUp>I};fTxq4uAUnzrr89|DSLf_V{1k`AvM|3vc7DKcbI$ z+0C9GO|bD^x7za3?7ysY$eq=$C1$hWh%i6ke${Q8FqUP|aYOyc7iBqh@hrV>J=`k= z3fz1VjeWWe&Gg7SYO@QqJzggz!ZJ3_E0kYQ3oJ{4|3+@tWl3P_R+Vs+nT@ z&t;oUw!(QS?xW7#_#8VKXy}VD7)LuIkPyHa@#yja`o72Y;SR4oyuz^;eDw4wLJ<5P zfBk3p(e3B>rK{KR-+bj)@%Z7(h%sUpI(&Y6jeWOYUqe|*feO{%?eTy9BF*~Rp)o8G z$4TgpO1ha@A?PnruU7qh$pdnA|Fg9S=~QFIIL=joBB4^$RG&*bdg1oFcWD72r?Fog z9M4-Xm#Moo8KizBD&U~b7QD6agwEJz*zS=G(fp}e&EMS12v^zn-WRUmF)@%oGm3YC z46UjueH{9HyDs47_71yUhnw3w{O~6q;_h(7n{PbE%a0xc;7EJkVN$>hA^2z7`?IV) zMyC&+H2da7WOXebvn_*^LNeH#e>y6W*@-0`vrtZim*B5_udUZtDx4%Vt`#703IrAh zuTI;Vm<@AmDXr+-154t1cBlJ2ZD5$S60HFO4!K7XFs8s#AyOeOegE{Fnic6{c^9w_ zEOX8F5vz7ONicODOGo{^*g)v?*$MN2$hew_rN_`Re)MN+1_h^toud*t?Xxh+WmS?y z_g?zYBJV=Q^~@LfG6t2f`4Ng z7qNJcT3Oq^CKZgzrz;px);#BGVMZ)KVz(X3Vm>t+XpWvZ-$QG_=>a&jkIA_T7X5e9 zilG~zfn5A=tTYCh&gAgtNq=EVfCsg8f+3h_ds}_TElPOt-%1CfcnM^pzoPB9DK>Gj z%ep8#SA~&Ne05S>24`$bga!Rrh+`#FA&;Y>v;Lf;-omcy@cixukr9s{Ug7!Op!EFx zkAI3k_|aeDNaF8);pgxhU;a8S&(3gn>~TH5{_^a>*jfUV*sLO@o<_?IGIp>@?2r|< z?QpKCgxGec21yHl+YUw8IC7^s*QlxZG<>Ib(pkvvi&nlcs>ie5GqT%yVsi�oyJ0 zbSl@y%0g^XxNd!6=7{z2uVu7Sf2JqrL1p1FmT2vplV3LrIN@Y%3*gf#gJfIVd;c&h zAmHxyfV1;GF3!*JvyVT+2OociM-MLXYrpUg`Z!dEMS!l8SrvL1QL*iOTbLs^dy<#r zY<1*bx>!zyEosu{>B@+xQPEcRQ$_(2m-Q4KARr(6Q#c?6nre$J%KN{zst{{6edipk z(4m^u^#vC!IAGpTJdY7AMJOy|`0;K90Cwp$H*s&yy1qDIXUFMP1fn)fDyf@$3d! z&P0Hq8~cL?v9T1akY zCe?Qe%jpzTc1wH4oX(S7U+VH`5s~@>VrB^?K{6@zU_7{?tP~Uc3^@v0lV4?oCwi`_ zLbq$|u9SIgaMpB<;-p(?e{|FMpyu--j^lZONw8KZ6=Y^uOb=5jFF!jQ4At}NTihLw zc;%%lyz=NFfWb*cA-Zl7$~vNljKWzff^=3~Tl_@0ZVQY;;ftILoK;CqpcYirB)J{k z12mH+;Xmd<6=g#hdP0SIo`>lbdWcm;c5%m;E~HqloGpJ$jQ{m+WvWGw%OJ~4Ku2*Z zEtL^!Ya7}CRG!X_KQ6_=w7LGE#RhIkdx}XeJenXIs!)V-6X-aYzzGgEU}vd~G5-1{8KfT5*#P;}Ku42@j0$QV-lV6f*h z)4@=wv&K8R!YNYQcC4S@4D3rPv~a5H29(gkK4TxasF6ub=96OyV03b=W*66iFy^tL z0ZG$qWiI7jFa%jd>@h%+2VS04zFed;=hdDq$(aa#*kzMw|J!NaGsNRlKf5frml|^f z3;=qksLx9hCiCzjK{~Nx)jVu2)R?H(p@z<_UZrjVU9q*Aka{nUuM-*D0hMr^z)ynP zVvaO7AksZJwN9}>ZJY$j$YJI-mDwjEUU~2kPj7Eb@Ua zbG5fIp+HNqvalLIno{+wD<>#cEV6moPPHghrUc|7T;`R+YA4#dwoa*DCCOb*&g+Fw z5OTdr%iKX^)F`_3oQ9s%k~nkARcE~iG`Zv&BDA5Bf8sn$n);NBu5!C!b#EgHkY0cy zv<`X_8W&Zfsg+cnV4Z@y!x8&^hwJM*eDdTeUU_tds|Oc29(z3b{23lzUCzmG-z&xF zzF$Ti)i#FH2~n1q^XPSsJm8D^MK2NFMuVmi#o`IoU=Q0LMCAO#NAY8&8QHDgZy|Xr z#;Uf8z?!gnBLG#Yf>f$$Ie7H~tuCcV)1ScHYW+)Aj6{}TN|U$oko>Kt5;b;l35}9G zLvTTdnqq0DmafNyNag!bpodx?kE}vXoxD#JcFUTol$KZ=FT>1((hdiAit|a*r|F1F z!MF%ii9c*bN|o{~0I&I2r*EUN+Eiz~Iprqt_kTj8bs|=(G9Z})?46qROIe;Bz3g1l zdD_65o`EESjM^5@^TKIZ4qCN!Wo(+87Q5#_^0i6;WxXfG2S4V1TtmON&R9}gI4DM^ zYrX`SDp4B~JiGO#e$pKR_7XnjR`TQ+0Xz&=um8%+|ut`vTFv2TLrxv!nAr4 zT)~RR7t_pRt~;Uf^-ap#I#HzMo43A*kf z{=L(wt`v`*l@nanXLp}Kf(}iWP6kCU_}VVc=emU4^Oma~?v~FSxKt}6Qb_Fo%lwk| zZ%Bksn%m1FlZ};`fhWyf)X}1B6olId0@yIU zRtO-6Q5^5R|0()Ny!Of?Twa`^?<01*4lg~rniJo?kJ#^bdG)IGIm!C=W$D549?#&U zqt!keSkf3P5wJ*}vYc{Z_ImQDl7(S(<@Qcu%L$RWa^N(;*^F%yB|6Jod{k0akiHkT z4~mHDGAB5pFI&>SC*kQm12!EG*J~Yk`#aG19k*8$W1$r$mxs;04!FrJT}9p<2S60(2xsf(u-9f`NGb&drB=K)fzh_v7*It^(71a>PL;vrt^-1dHRG*DxwSPhfEv#{)$U1GmGN5K94erlWm|8$=aIQol9;9m&rz zRI5Mu{1g1akNzWmaQhU$|H|9=+i(6nzVyw&}@{HHA6dcoc%lW5e5zr za?BLsQzP3?6-?clFC|bYPm-fXRzy{;No6{mDy0K-c?=FwBOK;lboxHUvw=g^t_;+( zNINai15rk6mvgK*e6l3Ep%cUy5jjwtcU?EyhJa2{pochcn6bSZN02S+-9w^`XI#xJ zcB}dZfD@^?PS*}Di~#1<@cH?(9NLICr(FR>CI*b;d~ZQIv!_`tZipTcDa(4<^`Szj z&bl2)87jE~JUBbY-SL1!-{aB61)kpA;PUJocQN8@w@2UicyQj~C!c+W51xL4|NBS( z5j}{%^YWYc`(OKYy!zlAPj8Nh0CouIW5li-iXx1_E)0J@^btY2)oKGvxl-~>Js(^+ zznO{iVtZfAaJLO6F5T0DqD7m)VVe+8CLG+1nR~%nF0r#py=>?U_5@Li(gJHk*Voc@ zcLKL_Lm5E2fkskV-KL@3)ZDcpM3T?zWd`3@cJu{@Z~pN#pUJsFtR`d z7w2c&_GXb{S1H^{?cGv7KPT+v^(`PyIjb^{S&%v<<8W}t_Rq>s_cjnC!EVcx>S~yE*knXkG&bDQ|pEF<85fk%tZ0H?MPlAC? zX~zW61gpM@YxKPekWrnLyoSbC&%R_@8A-Jxd%2-5XET7d4awATQX(EX&u(3=F)Af! z?cH;*d%0F_36ZSGjfVm{Yb**doo1%i09;cZ$Yyf1`$lh`O`asWG<_#tAy_R0(G_&d z*Pf2;FX_p#z<{V%>sEkLD`Y1t6vKk+g1c^h;sfi7YWj9=8$#oVzm-nAq}fkY^s4}n ziSoRxb2|DgvRD1UqY`w&wp=QbLkpuW}aB;~cx{#^qN!s_$ej1?y zhd=rFhqwwm{QhfS!|#3dn>gzNK6?5byRgI6`2}Jm?vBUR;(YnW+FFIA$91cCs&UPDasPP_XQEF6r=pC^V zY2kux2M^LA>1+o#WF3I>#=6#QaycQ9Y_v>t61f+3GMLQr7N^$*NtH-M*1;i%K9-Ar z6e?G=LAl4Ztvol$X?t{MEtS@kruw-Sw~FZ#wP1ueK!UB%8sQ~@1B8g523t|?R0Wg~X|M1?c37AJ40RhB7#ZDk0wOOMsAmZb+nW-bIzC^NQ zQr!12m1#G*R6#h*o_)Q+r)ucP8k^YzOwW^BH50WDw2x0>4=>=$PcU72k~d3U3Is$J zz!_%er)18`$tiutGrhBa0_N)Aj|-X;68ko-_s~-s07gU-orE#b9iC|%-`Ob^TFTCu z?kSF~09(D8xU3`5&%e-f>H<@>aJ{7f!qm?cM`G7(T-I1^543G#T*R-Y9%B;2^p4OC zao|XkdF^V4cR%?AS7(=abnyTme*P)`ukZd_e0;pY7xxeFKfd#~@Wq!N1 zuudO7P{AB1ELAzE8?(G%k92WM%i#5VH&CI^vcP`HDs5?tQI>EhWoOdcSa6eti88AT zMD+H%mmJeibe4j)Rt{&f&zMy8^5bLY1WyT`od7XYot;W5-HA2B_X>42YHSsW!!^h8 z)*@B>IrshQ0G%pXV-=sJ(8nR1y)6m#Z~94SFuN?o9!!|Dl-v%uVqi`$xu%LHby+*N zm#IJ)J)5*}%c~-$5d;_(NSb>7cy@)$pGVsGv`mk2KMf;%Cw}@CtMEVFVD^q zW5l69&PnOC&&Y@;H^iGSzX|~OH}8H2fBM09@&55Se*4jv@ekhlRlI!p5Pc**eSVD) z0xtLGxINzC*dOu6qnC#YFjB1$?PGFI^N1W&uHfBnWa)(2yVC>k!dymG z;oN~rutpMHV^O^60HSVV{Ygl-#kwED6W ztD*B#65GL8du}xq%ZhP7Q5Y*7u3#hf2IshCW!*m37uOqnJWfi@!_ItmeTx9#@yieK z;Nk+uzQ?ie(S@MYog+kOdVIHJ;SB;(&U0Fq1`;P)woc?!z(zJ60B5$7S3$5JDJy@k zT$YSQE1DxMGWPn4(<_y*ED9nxnYd7aRino@n2##87+1H6OJiJ_GulcC#1?`8>vYXh zB}B&~PF%w39%@mnTyE4&e&GPMDe6_ZB~cdBZqIsbReXSAzy_%o za*}X(K{>sO?%eyE>+3mAlDPvZ3W^rg{u3m%fl_Su6jC%tMx!rDLX25OS|kjW;1EFe zbES0>;G7E$MkFAr~#BI5TDBHKn zvQ^1pa!0qrStp1D?vDN3r+sy{M^E6-zV}!7mp}V1ZrS6%`@%2cH@^IJoOOGA^y~>< zxp;_+v3IyD?l5_iWw1q%#gr#k(js7RB>l^z1x0&Y~) z)*6#cq1pXOhjZdKNQ5QD_2_7bz@O4l3%LD4>}d zV8?#QY7Am1aFSEv;Qf06U=gMAoY_k;cVQAAsUfc0+=eT3L$=R-G_#Sl3O7rXL^ruj zsn2BT>c&JZEX)?K#g}d-zSz-3kjEjcl0G~~i-aThC3ynwNHt1*n3SIYRv0nn9+G;Y z#(k$`NsD!v>aRD!!4|143p(v3UFKp+kOUhbmoYAJ+%!=l$N*rf4yU*$vzam#Yr|y@ zYFhYMs;QJb!L@%6B5Cth_RMPvkgO(~NkUjKXX)D`Ji5kQQ9|CrkjEyrwSZZXBddZM zmFLuSrj=xyd{fQs&1kR+__9S{OVZ2l$AX7+pMMfV*rZ*U5;YPLqqag3i#%z}Hikk6 z;LN^sV|+7w&NMnlL=g0BaT7~T08l3ZF~*T;!MPecF`bIt7}5}1lo*^=oKc0~d2hI0 z8n23M28f^w12}O!_6Q=lJlmrS!23_0;-CNE zTlkC5Kg2h$9^-eu_;vi!7vIFQ+avD!175p&2>@_=ILyGst_$d6k7u{nxVpH^nNT85 zJm8Jwp`W%TcDDphOQD4j4Q-~5U?!3=NT&`PG!GdyExGp3r0?I;znjmd5;7@%Q;9}k zxQ6-mFAO}aRDM>g8LJxo0yLKW%U@zV`bvy0URr&R53 z$o0HY*2YF`>5ZllYe;VEMJ2(DI^(nF*ZB0wvqAaq>T(=qMBLsTaB;RDsvdnrPh!^v z1U=igNeoUJpB0+VyVNpe+y5!0B=tVORHCZ~&cA4|Vyb9J)8jNNm;zI!YDoPqQ!1{v z9aGF}RC9MK2ePGKT8e31azv`*O9Vz>$vQSqG)ao%j`Chb$=!l-%;G%;H*T~3!B$=h zzE=F?JWZ+#a8Z*YzN}25*dUyf(Wgbb(3B)0rY@y2V}De#)ZL;7{*M3aV?-xm2+GFl z6v-?=(Fxr2IkUoUw1u)IP>yj;nsyN+%+}dZ2GW_ok_7HPkWxd*>V*lr^rerbt|gC69byrQ1m;=l6lpZm9nY{QycS?p#B!n$|>MX z`iV=ed}$2FqFAUb`!t!g-srO`+AzuWkKYkwss1JrX9z?8DG8aV=^~J**c|G*XkvE} z$9SM6UPju2Fo9tP2+4FDi1q4h3-)AzQW#X$$ulfCx=ItSPS;2aIejFK#~uW6vESkP zuE&o*`w)Np-rpdS_D~mvX@6&%yvWi_ugLi&G ziRlbb2>hY9<{lDmunNQ^O=7M8i}QE`$qWu5^Ze43=`#Hu%4)#VL}L{4P0}YF2%+Xqu&x8(MSR=3YS+dGi!5uD3mn0EmXx4Y9(6%#5_5w zCM+B}K*RVi^Pf5Spyl5KmY;!z@vn3Of=yEvPI1-ZRQ0@Bym?SUD^ex`S8_Dzw_P^J z($BjDo^h;W_80`NBBu#;Vyb;ql}%>a8?tc@-j?aX^W);u>n!s~FG&LY<+(~rR#vVY ztz*@**j_)eg+#?m>fa+O_DBP2B!PFo}ocd8!waK+3>i1~k z!POc?Q#Gi8iY^*;Sj)B=$43&2!m*rkrh}8r>t_*Xzc4F4kE5r$u_{bN#5lBC#YmOh z>(N7!Bs!5q&p~W!88vE0k0P=Agt7msRf^LXD{7As$88iV2ZL>4=&G=B328Lr7lnlw zc6x6}!OJ9;Mv|Qnf0>Ab8SMX1M@waewelaG$ zcevc22A zJ=;~}5B})mL<^DFIu+PK7{xqivOY4S!8Vts{D`C#TG1|eQcX^Jwg&12r99atQEH^4 zTsa*(u2(=x3Y~O926~8F(O~L;o%!UL@up?-h@kf8TrvQ%g3?h^10b7JY`yHeGJAGo zZpnnaua&+X?&dwi#L{RQuCO@to*PvD1_KxwaeI5fk3aYbSC;-= z4QV{tn@4w@wue~R>iPL$YG7W}zAj!TQI@6Epr)SrDzm+b<4dCuAr8SAr@)MbS=5r! zNtddq&0I=PQ5v39s+g)7R)KkiLMt&&b28KF)$$pA^zaG4pGVIzotPn0d@p8>@bu`z zyuMV04pW^S1FdNW<3i=sZ5_kUg@Pzc4;bBI@nQ*rLK!GxsQ} z@h$T{OPHY?&u0n59}}!G*w^&jxe6vKT%Epuw9grNMHU5hH4@k)+IgR8Haiw+6`N9+ z-CfJC)K|vXC-zG`+Py9?PNz#%RO5I)<0yy}M?flq>ijNaGp9D32zCBQrxK*ErB)^@ zC}Nvp8-;URsK;E^&WNBMV?=GRoK`O0LsLZG$Ks-xtBTijcC?|C{5`r4ei~gw3_@dl z?6KQ*^IY4(9_n@hs`oV6**SSzlJ zpSHpWk|!4_CEBnX3=2^7EZvDUF>7f@2m%iM0bRGl`ECI5uI~=x)Q^Ch!yO);U*h@U z1|abH?Q{I$559$;+&;taJbnwm|IV+?{&0HMI$0Mzn;c8LDES7O{ah++8T03*o@bCS z4xy9eP>OS6ml11fHuU`WVZL-(3&wA+)Dtvz$XZ`dr1{zJqM~hC>G)y>)t($r#?~h5 ziQc57q7WPdqf%AI_E6jLl0L~(e=mzgYp;zY{q7P4-7Iic1ZC6Mkr$sBoLCAH%+xTD zg<#Wz1Ehe87HHxpCH_{Zgg!{Y(-UmJs@XDkc3G&y;W$#72O||D(FMV=kGMS?@!Bgd z;rwimi}SM?!RtEHbJbLiSVK8Is(r%-ad3-uZ4%WF4{RIraB`&2bZ)EEg-=RV6q;G2 zwnx`sp+5aSF^YP) zxsQO{8YR)UZ{B>`nKG=sX+}QMRUi}ir_ILW+NeI{u{uJ1s+gzqfkUM@eJo;rH;IVu z?epfuW>!s-#8t#+lp(oVwaH5&;-|&{mIf^8wq-$!(#9?{2_VOVF^N~jIZ9Hr*)a)b z(mCn{1-1C7PUfd$kC#4Knd8FEdtZz#@Y*plgv|7pNQCnxgTuf|OuoZ7EbFAn8GRT+N)YtjEiu#?V>Pt!4ozveZTQ0Q-pL0Jr z^=1~X$1A7C%X+az+O}IV{xK+x7hu=zap;LB*S7$OeYeNH3+Uq!uU+4FznGQ>V0J_2+k;3&c>(`zOk<`^-&TN4Uq;T>>+G?-M z+chKis>qTjg0&{Aeo7H2@kNrvY^<87^*xlJUzx4Z$X`j6s=?uJ+VIRm!bKcD#V#Ff zMdegk8RKLWgt_7Xq-Ig?*~fU##CnpyCsRr}4efFrhTB+rmv&1=*_`3AfV|5q_bYs&ectF;Rw;l(l=&sXBNaXe5u!bfo;=AsYPt=yPhjl`s^+< zbu(sD3y>zZA4`mKcx!Z4INK}eBdN#J=)G^mk*0A}b`H3nEngdXzjhSnHUzrvQ_M3H z6Rgd3iK*f_)@R&AQLxMermtVIjDVu!;_3E2dr7=IMg%2|B?m#GKXTNYmP2}m&S7*B z3Amxfz4Y_8Z_p#lxTdb&8ne<#{#N>;U6m$ECDCytS4jUE$O$e9@Mn2v;-Xl0PEO<+ zL_mpi5-cLv1wjbiNH-r75bC+ak^;7--9u)F645ll;Xbh@{rXXYwQ7@ymEQRK6a?+_ z@PJ>-G-DY^?t9=Ehu+@H{W*3WW~Jw2f5i2j;Mwgp{>^*;3IE~KpW>~HNBEz<@jH0+ z;UzwL`V8m0GY89=s<`4C!D8q_V+**FLzN$l1_P5WNwWPpuYOm0l6G#@x6y`(5fX6c zW@T!Wo|A2_9?=5pj*vlaH0tSPEm_l!U3|baudFyQt<@Oz9?CNEp=RK9B34qOlRU~L z?t%6yOxX9Yi`D`Xg?R5nTw_cRQTzF7cl~{4xIN5B~z41pLom z{dIix@fWcR9S*%<7dn)L6O(6C5qiz!@AP?3y66kZt7=iQqX4ql!20P;d%#!s6O>?OjP0W8dB+^E%TmMvcksVp)RDfrS#rqxDWHEtHq z$QoJ<3dywZ=%n86R`9>6A}Tq1I}7w1sXMj(!V?Tu<=|NOfi-8<{GC?W*zoQv3xDi~ zUXSNDclhYjrw{?Y@c3n1UYsK`8cF)4&ur+QOR17v+jFc3vgHL|am1B%n3Z-1x*0m%`qmW~ZL8&Lc@R3rK%x7u`2~!tMvvpREBr4N& z(osZeoQuF6{XUvOZ{XQI&XR(I!@6e$IL3t0kAaqBX1UTdGsRF*;(_i=&&zlB`CAU zGnS;}pT`=+PgwvZiUOI^chV?2Q`;}Pnfzkya&xFPh0z~o-^ z+XhhGiT?+7*^pEvE>Oh47BckzVgZ44R3YOT6u@n_sTAfmR{9DrY+{8-vBIc<-;?9x zs-(ChPcHsjGqrRQhyH**_P9E~z*z_U@RN`6*B|~6fA!=8eBo-)lHSMm3Ap$)~+g;$#f*a zkL1*F_lmcVG0D1R#IT~WHVF- zTVe6GL(M12BI}6^NNolua^eSOBly3_11f7N#>^i~E zKKTrvKfA%@#Tj0G=^@U~_NkK+B#_FS7;q3FsKd^vtA?_)*EWnlVqsfL?JFe@xGa5h zqu^evFHQTtPWdbmn6x#EKlEW;=+CAr|- z=Xt3~!kIcRIx}|De$d?V(V4X~yoc%R&?#t_QbsWbfRg$Koz;;JzYfT=vCIW76|Yg^ zV-^jHw$eK#Gnk}NbnkQh%!xN&vp9RR9a+!6ip5Z3iM#DQU?yXCrrzKs(UEj+Fc#(H zQodLfKxVps=J6#(Lm;YBn;)cMuf;4#AkDdzl$DlZZ8Wht(-ty%dx*xSph$)G>6Xh{ z+Nx}FvP<)`-SbK#D3(T6R)A~)acB&OW_hseM^LFC*8VOgmP!C6W@jU1oa9G=?zK5g z7Hj~}+3vLbq-H}B%tb$E5VRY~{W1u^#Uu`vS@bC+R8}2-OdrW~>=R5C#Iq@-$#1Ro zvxW+$JqMG>VK&@h4NzV+k4`YEK}a#+Q334jLclRbobUEH-|g}A?uh^RlON%)KYkA% z+&#r_J$?(n^Oavh7X+U^{~WJ9dW2&i@#*s?c>M5XT;JUoyQF48NUh~cFKHqQVrKWU z436nmk0f6*cFCI&&O$eWyWPuG8&N)QN3kfpG!{5q zn9vc78(O}6=a zXn_k%O3j!-x+y>sI$~Ik3b|$rh|{|^dsp?`$nsuD$8t*6K_plzn@J^DtCCL?ZprH$ zdC$Dwx4uv1%8<%=e~6jATgb=iAiiY94m!Z6%UlsN%^;aGqOWO5xajosTiuN%j}-sx zovO>RNDt@dpN*ADH_;(VBAu>DJcY5Ah0dk%eU*66d}j!gbl_l#f`noC8sm@vlZm)h zfXwRWqDEfX;h@vGmQqi33BcV_z!33kcblhypuKgvIku^=<1~=(f=SDP3KFp24^;GT zfA9hR{HNc=k8hsf?FTR8fB5RJ;}^d0WjwvRgD~Lb2Uqy`=~Hwe;FSlDaC>)1LmBaK zPe*G-d_+5Gk6;Oa@j!8%1gkThQT?rdZ+ITAOuK(rcfxX#)iyGg6{~KL{;UF7x|5au zHK#D+2?yF5tZWe7s^4;3wN3z|Tj^b$mDpe-9AK%lD+hGd$Zkm@twL?xpw=t{6Qd;# zdQ!85+rX34A3GC49F!2|llxS*R4fG_efk+*f8`~do$ZEU=Y5Y|*Wqlx!{In2#%V=l z2-Lbs3R;iHYWeQw3jaM-By4L?@d_nPi1@-Dv=36u7e&)c&o=N!RhFu)Xp+W|e&5*2 zdb4n**UrDgX3$l)1;!F>@jr@zMn#qQC#)~u_?y1^ZlYN}%&(M|c z5ilMZEN+jjvUdxp=OwyZL#$18@J z5+sa5S8-W)b#HMTy$CgCOhiU?=n-&Fm}tgMhfryGlX`q5IIYDk%Y({{E>~)z*5%a- zP}GW9wrA9HkmK9BFbJQ1>%H&dPd@w}?%3nEUU?J0_x3O1wTD;u^!f&8U56Nno)M2O zAK=*cczSz1yk9!SGV^jTDFCdlGLZIrB1&? zJvld+hLs!RBpde)Rp_%x(1`2LUa*~_ok3tJV&+nZ8@FDJ(W{Hhv1#9Pb|kx9hr^-A z(oS#rNvT?rJ=MYMdgldu1wYrf`k4wMZfiz z)W+3FN%Sa_!u6v3ie0t9m{jCT#0-ixi|gtpm^S@&QzeCEm*$0Yr^KxJpymoX{3^}{ zP_JntS~j8rnMtFPY#-$cyU0!Y%onC$j0f1Tp*T^l8cj6uO^&BRzGwouxpE#Uj(Tr{ z5oxL8xci}znb;(_Xj>C=AC>f@dq5?vT-)Ea zBR1HeQzVl00#KGyFHDVTjc<>42q9qCb?8DEiXE{>7dq^_9rhj2lein>z>9srF#_ND z;3xR@$3Mm&ef%T5d3K3^^!nHF>tFgB-gxN|NZ{@m{qtQ~4k`3nv*oszeZgt2GBse8 z!kO986t5J<3Z}43j9DjTm4%yaU3OPJ+MA8HLnzO<9tx~MWzOf8h(*Kp^oFQA2EMe( zlLyHRO>VQU;+{CF4O4q7nW86c7T4{PG=`Cj8h6v|*|r2htnJ;LY>Fw|8F_EvisfwJ zKs8k`%SmI}9iKBAa*5yi=MPn1zuM4+#cU%Q(O8pu8J(KL<=6- zDs?Shpf_?kJM}Ci%LNNpv=6GfRQ%l9xe}+>F%-X;E5(93Fj#1s)&{J#)x?|0JyY*$ zGdvqip#$Kd<(`@H4c7!?Jcvq42~#UeNEZ}^PJbAUU&TTrhMuRZ!$1~~tE)KdfhePI zjwZmQs*29%)h#Bpf@I$4BxV&bPnas7-oyNJ9DkL~={LTA6#J~FBd-%_{Pw<;pui=}I-@-S)^i@1K-{I!2 z$1z3(8Q3L#jEQZaQ&|@?Zm@*s*2iIv?HdV8gGHSYu{1U@B>@KvOX8nZuq+2%W>G^i zDiq^c-6c5)*GCqv2e?#8i?{+mmhOGx7bfSIsZ|&|R zE6^o2xVF}XpNo^iO@LI|D|g@Y*Z7mo^JA?6F@t%erOH-Q7KbY?uyt=s6+%?NMgs~w zC@b3SIR;Sbf#2R9@#N`q^nH()A3em?gNyq;D3!n^YzEkxcEjRqu5L6cL_5!@lykFG zC2)C{FW?tFq^garkhKn$tc5$}NmPJcPXrfXKq>kxEr=(z$_+imTsZyzTHJ>9Be2re zFJ6yiu>!@C38_94mqKUGL$Q2i#pXqk3gM00|yG@-r zrzj-JbWm8-Hi+3Q`Ic}lj9G=m(LJ35qO9@6P;osvtd!bRT-U&SuiF`mcK?$FW5uL$ zUMRabXGyPtH0<+~@xV;Mn661&0wtyiyrb3PD!EeJxIr^O2xnx8?S|7m`p!(!vc8B> z@8L?qRPs@DRRz5Vy^OKS+$f96=`2j3r^a-pIh%4$u z5`FAJ5NEqRcDteS8%G7|vmJ1Ii0FiPa(%$R{QkG_C!f5FbLsH+UU?h;@aw;h2WRIH z08g(Ei0pB`KSKxs$8qG*Y~O@g4kQq_&qt&pd{vCmq-3>BD^SWRfiTMksjne9q6%DX z5ZJN-RXz`XdC@N0u!iz(1wdCO|D|D9VI+x29`N{cKDWm9HODP^BG6K~@j>nl!G4<1 ziKh%$P%#uoV}sR1Y(0gW~3AQTfCNUN? z2?tOK_E_Xy0&sUYB7^`k;_~7Q``sSr=L1%Cdw0b7+3x-ii+ONuXJtzv{y0sDVr_V| zf956=Qcr@N^Y8+|ovK056@nAR*Q9iQoZFeHh)sKTpsOV@xypQ(LfR6D+N7?O{OU8n zxxVA?mn`vM%nQLIYsu=C!=R&oSrLCql3)AC*v5ipVL+Z$iuJH$(*3!dG(S_S zCRKQXNv|@2yv^mO3jho^BQ(x**jETc@vU8=SC1WZTJKzim zZWYt>_5qQJWmvk?mS;~VVe#fP77NaFli)r^?036CU3={PJ@h?d><8Z}0sC%;Z~gfD z_0k1Y(Ey60MLq6-f1Y)8pXOgU6A7rI<3$sKr(NGQexgA)^ znd3@eJ%J07*kGSw!$R>UKW|iyg$8uN{9Q6v<0e14*o8R9DZ`l=Xu3Tk0!`_ZwHwS? zSuBC0rI|&AdD~@!q70%t%iRDpjaojLA4VB<)U5WTg=0z$MR3*)LQ~g=1MZFobfLq= z{v5k*I4l3`+kb)o@aa$R#{Lq&{nk5p`;{+X7dl+u9dLEN#~~8OejG~`BkmXxAm}=Q zgizGA^;k2BXjM^r!8wX5CXr-5yLn6orO}LCal)uYSA?hy!z@2FxgizO7z~9c)S`+*@i1A2O%s)sM65qxv?5l5;PM(wR7BLQ-YC68mQ4M z3sQ7iA}qCMa6(Z(??{!kVGdX!1J=QY)oZL(n75$*sS&rf^Vs(|9FDlWILCgsLmcTc zF-G)##BNr2ei5`@aluXAH?fu~JW`4n*1@2a0uxHX|BIdxooz91G}4;gTPg$z+#x$9OCl<|@&8c#)dLlW#Q6 zf`gK@K#38_ntLbXIK!6_F&@VjF2Yr5bQ5zmp=49!#BFv94(K425RQ$ZqX(3h+9aG9 zo~ln40vuzH5W{SbuFfy9?*u)8kDflk|NiZNiwNSEAH9L!e)H$?)+;X~LU42Fu?qp8 z-`vhSr3(RPXXgM0A+2LPj!R9)lY1pgN&U=*2PCkgu>1u}Y4!_dXItUnOsbfZeK#sWXsd5;tY~Z&!}58*I`C&> z(iP*7+=ehvjgH42&#!Or{PqrK`#riYAPCX-LtjXDwg&**-rnK-eE;H-xj9M^MR5Y<>O;?>jO?}3DJXvK zCi|1ZPxPuAboyt-G?Ot+dTf6^Ri$kBqd2+%xkvC?9;T!!iT$)3_1b7L+lEyH|JEd+ z0ml&Jpyi+hSP%xCm3Sots;YOY)Z#oT>Pwk}&=U2qJ+P zQ|1q4Y_*vv`3%Ncsh(zPaE?WYtUOGu=KWF56h>^HG9&md+486pm!)~1aJ72@Nx1!dJ&|BP{m}Prl#Q>r@_KA?t zL9-(1jFl6~ZLt_ti@Z`Ss-ODwL9JQEe*9d4K>pdk_$b42SyXdAWsYOI1EnC;#4Ld> z!7;YWDnImez(DE<(NXD0rV^{B{s|Wgm3tjpx;Wd%;7mwM=d7Jjm6DRLcN!*F`(^8a z2yN-9$aAiEtXlC;wryl-u1vCWzl8+&epwJoSQ`Kwjy<{%5V}$R{`N4>^yO#(yDp&b zBYyPLpMfO4_VyR+8QzPYaf=0w_PR5wJ=iCW`6yFUiwPQ>xTFOh6DzC!0uy04)tHfb zsMkslCYW4fALDpgvWNhg*?F+VLl`=&q`pb}s2~eDla8>ecVx1_*!sds&tKZ3C@cMe z%NQ`XqBKz^O%JdafF~4WD(F-Rb4+UR-_@b z_2XLgNuOK`^a{di&Mt{Y%`{uue~-;n*Wt=#&Lfj&1*CkR(yyrkHB}(O=(MNkBp9&T z_8fS#b7?mHl!Qn1L=ymIO46qQQ-A;>HiT*+NcQyg`3#wKWqK}`IlxGIb%9|Y$8qNH zwErh6x6F293f#w^&*wujAdxG>=Sb%EVXCxS-2!{hmFi8kuT@qMlvSq8+DVRn{I*%} zG2y}@qn+z}nHXVqPPnI_yIMbBI)|MF&8CFHirE*1Sw}x0jKZ8DjK6a^f4YJ#1@&br zCD%MLTCopBacou*U`fgn*+RMnFiB|cVkVGCGQlOx3Gk9gO^GoWalYT-=5WASw?|~e zv%4ESIJ>~*KH%Bi5h8+40`87SNB}-mID!q z#Juk)%Vv~B7B-1^|K^a`eM1Iy0ntI12s46h?`POF7M>X?2 zn=_v=eK@m#rXR<(a{TvAG;tZ%1O%2Y_ihiOVl zPHI+lo9?VPq7Vp<;NS8eXWk)&tu>{kK%#6Mk^Yw)G%?pz_-_kno_ydOrDabNt7C24 zC9efq9vWAuE}cw=W-@_SAeHCA!hNr7($IcAn5yu z<1wP^0*=QX-}}+~0D!N)`G%iZX(0{XJA1DNzB%Qx640H*FfNq@E8_*s_jrQdZkR^N zg)Ck$BYHtf#q8OYr!+bdQBqPGeiY9yXd7w#g*YQ(IMIYF$!~S*bHn*rds7K_o0OxS zKF{HwJw}gIgI-Y#BVqbXY-^yff-7;c?{wC_T(z5Q)$)5ACHn;7gp%#nK%|sPKFY64 zPFrH4roQF?D~Q$F*-7ra^?*v2&XAQ#vD0hlj?@?rRA8z;EtKT5mbi6Lej`_027Q z_VE)uxH!XWufAl)BRS2XkUH9Y?<7uFWY~t6bK6S!z|0D&>(JrWCntJB9OFKc7m)=! z!U!1^H2t37mN6VrqAkpcDm7WdM+9ez+KRYQAurQCy(wjxt>(Fm_GcrIrjZ=pZF>E{ zNam3*Bh6j;y^5YKg2!_f1q<46S)U?C z)Ju)?O9Vru0#IbOWNkFtM(oY>rF!qN(|)F;Uc`&3mpyd8JHz1!JioggRhq}DC4uw( zKqr6yvrq8n?|lb9x_*MMKY9)S)!V;}#}6-YeTcKhfKF{`wh%3lrz@xKY^;1wi=^j; z%Ey(3vP!xRx*85M2V7sLxJkCvpOMn}YnlVx4sY!wBh<3uskUYb9%;8c%XKQB_qL+8 zYB1BJhb2`PS*aovk}ATdKo8V|B`Wd;PE%dmJSS7OC%=iXU{Qox8t$hcT%hB5fuX#6 ziN;$03H?NPV~-3p0xvBBSF(~lVI2tAbcn=GsssSs9S+#?0B}6^fB?I$^MjQF4tVch&ZDDPIir;x>XKkz>ldy)7jG0_%U5Z%vM_}G zWYECULq$2fRAmUjg;(1 z6Ec#b<}&vBicA#HNzGRhva)8oMV%;%sn9@I3Ro;FY2lz*MYcd+&>}&_DvzfLfr9v8 z^Pcq;WOKSZ@&H6CY>lQQC!Oi2^U1{0(4U_zbMK@Wc4-PqWr?u2rD)cH7U)@<-I^oR zlNg=L+SH?+ylS*)dZ3Yrq7zft6qKWCQIeRH0qsnk+3lB2!LV!=FiU>H&>UBCn1uFH znWIaVLNpL%*EKrKLyG$t_6fz)6*T26E|F*|i%In+PLOeXqwllD4i){rg#;xie>+ z%;$(J7Qz7Xupk@K0LHe&?FzWrm4fMD4EHjss4ip~i^*AzK3jCQR5WZ4M!6nDV+{uL zl@p>#f)g3<$(f1k=9jyZa-vEQO*OvRFrlIeP#Q?fJSZ)-TNktva*>$^s_cCWZgQ`( zBbuu2vhffhmyFk4vuFD~x-b~8o0~g?F5s*iXZhY9@cjA)ufF^cyIqIa4?TmsT{k4b zHE2^#9Ael8h-J>r%C)hUoHkph((i1$#zHT!TLsRg2GhXjg{)nHUMph*GHHMk9k44?t7L3 zIIsS_&}&JL7BUB|6|jC~RFWH@Qbx_hOykyi=@8}j4 zn_SRQL`%yG@=_wxzEBmnM#W5!`TUy=Kla1xZ$p% zNbbv9<4V&ia<=PmbGXCJ;Q#=5>GBGfd%;gWdxAfC_pkAvKK}@R`|(@&TW|e59-Lj^ z&>uk#wCKCz5g~{g9AH$(DSl$PMzIu>K^|Qr5J~o?`RfqBh*w}-u1P}a+B};q>hYuu zvsoFPE8)9rJpXD7@4U&T?qjAj70%j|)B-VVROa+wVGxU!^uM-PTe2Oe<}r2bQHg}m zp&MD}K`G0kuFRfJD*!AA0?uPqS=G+fKrh+e%^ezyP*8#SPwe zBH@z}T^N#%K1Q6M?}sWxB!2qgC)n>gTwPtxV~zT_umYA*N6s3Hq?H?yO($^mUKFY~ z&5_J$sX%qK$>z&wl(Z^Un_IO(G{5JCqBHptG7DD3uN2t1f@ANM8$rtnOX?(7%REsj zRD%2wI*AjOazLRT*^N_2svRv~0w0I>0amIy70zxn2vqxK3{=-kRCWS;cC70DzTVk)$P&`N$0*QT|P0K2;1_1*$R-%t>sMvZw(- z#2NBs+X9k6(3B-a5_@L}>*@UWbUf0OpafBKV;KjLBLm(9D29tH{qZ_cp08!}b#W4b zEYzoBDG6qa?{;R0n*HV#i2c}^Ww5LGjE8mWLWe_t#7mdwhy?!lyMKc}|Jl1ZFyeP# zdmI1t*M0@(`wq`<4|sIB$CK+jeDeHreC4&*@cGT1!btKso~5KmwC7YDi=?;w{PFivv_nOVl@4HVjpqKpcn1cVS9$d;IL<&+zQ}248sm3NA0sKoZZd zZ*leD0swG#IO1%-D?dlgm6Cg9ovUc3sDxYE>QpBJ303Qyqm%{5wQdRME^!S203ZNK zL_t)HAcJfvOKe-hSy3^eBuzm=8ia@vd8!gqHsL)|Rd-HC#%V=H2PMyY;8Gjt&m1k4 zgkX|j$t*q-(YLjyOCJHJjI&&6+UQk+r~A0{y-GzSXX{pJncZw>8H*k(RoGsIz_GTc z%YDdBk9;hdN4D)fSJ9K%XNhWRU0MMojWosc1IuIpFuS8 zk#)z*(*D(nDt(kc-J9vU)1i3MqZ((=Oq|3~mR?O86|N+|^eDNif|K`DGr1303t&Z% z%K$r(c42(a>OLh`5A_nmOJ)m8^F>#qr-Wu-=_oX@>AcU2twwpAGbgU9yqp$A&HU^H zTpMZ6BeNpt&$MSkmVRwZtO}~PL^Cg9CDsbl{Wi6v6~MA&eWV)SNmW>5jcu3f8G*oO z&!6L^2M_Szyu-&&pW|P@`)&N`$M51{x5Gbq>znwMH@^lF=mN0c?eN|wpW>yYrtk)@|)q7HtO%e17NAwrQQ{mYS*y)(fk|3b4kkya)X=i?L-jX)9@HWdg#< zaBT7WDjT2JY94lq9DhqRO`K%d)4t%!^Rb`5vC5kCd;v-@t$9LqeGO{kkfr@WNRT;! zB#7&qJAC)up8^2B@y?fVezr${jOca&R}U_5I1JMGXZsyu9}&9H_`v1ksM~# znb$H5xh`J$lk8Gc8+X$FXgI7%f>>E!J=>RL1=E&# zwIoZ9#VN4Ybr@J}We`QBMOy4`O-fxG#xirKw$E!VU2D(A1#wUtguXG3NtqU|JQeLG zZVf$@lAz=~pJI5YlWoaOQ8Z#q%e-6UbNZCcVhyC;^8Bh*zEi4%g`!Mxfmv}_`3=4L z@Bv~3-u>uf{Lv5p5`XjjWBmN(EBMD>`*pne>Z>?L;Pad3cz!3iI={qg4`0E};TE4> z_qf=fp^q`uMUVwZ~!jo_oSq^pA z#zWTx=l}3ue4N2*tk{v{^Cy81P2(u3={XDKZPI#G(MyGmn)I4MD)pLa-g|NtGRsW} zauvI2+|%^)WDc z!n>JjUAdooi#e@1SI$!T_Jnft`D%N*TPe}C7$>h@Yr58dfnI9YpQLK7EZE1nA1bbV zbSMS3xzowSGoV{cBoW&2P;qaLx#!q)KWnOHUU-iY8Pb*H$e0Nt>u;at2zDWh6CO#O86qNu=U3_zO?yb! zv@nUd;(yjsgq#wQ(i9g!biuMEsR2&CY@#awIK+V})VsK`lN5a&QJlQUdh??n$3Eg)^EXz%u)$H??(MM~(I+>HKu3y|_m3~wx zj)FZ}!JKsNYYpF6_M);++JH;lzs>Jg1}{0Ev*?te;SK3 z$G%3`XpWD^=@ufg#Jn=h;U4}A!?T=4>m0Dg!+ptxgj(_duz{uQ4tD^uNe z-P!UCM-v^{&nvR)x}m+TAAYtA9lCB%cPD_eAo%}&@NN9*hu_EZ{(xV7=?nO~?|c(q zeDn~vJ#ahr2G`{r}VUroEPB$#vL@ea^ja%Aw}!s_N>lu4cR2%_c>P zP>U2GP%tc8HUxbT48ef?!GF*27k{w{SOh2=7C}m+&BkI=bp27?1&XB)=K=Z7+TK2Xd*x|{^pK94PsY;Xz=*0YdCp6 zzf2j27{iDdM+m*MN`KGy5oQ7cwbPlP^Pc{?0`*GVj3_(6g)Rk<`Z1BCq$f6VtPw@3 zl5x%dBB>CbZ8R)*NQ^jDzCL3Yy&!@<@|jhzDc4mCMdJ%J7uI*)$_T4RU}xm)p33Oc zts3|wj5%pUsJNoDz67IkzA@tbY1;-}>?c)5%JQ-6CIPF{lP%h&K^Gaj{Q>vxoa41u z?qIuVRq=eQ8BfYYN}}7}*c>cJaFsZUA<%2wtr3*S(%JJ_NZw#AjTY(Q@%K>t0;wWm zGEaT`tyxq2R&_hFkzRJ7Q1IRKzN8lnL#z@Hw}>>esWn2;w&zD#n9x${sL)BJG^MW! ztz>i{*5gxLlg%yOcrq}2Y3TB$p?1Zq$u@l0)3(SQ=Ze)WQb?}hw$}^sF_*PGOmtTtLv9e1U3&f@Dr!=;A&`oHPo)l zzS6mu0xQlroe`>1*J_!FfDo{01Aga6e~dqS`Z>VB zKY!=D_~EyH35O%_#j{JCZnl0hU2zm;!_^`asR9sCiTv=OD2=~s8cketos7BZR~1W4 zL&4QyNG;?T+*A6&AZoyfh@AKU8!ufrY#THGN^Eh!{s)4%NM5K%O3#=Ds#PjB8FuK9 zYsIkS1x`pMXQ-#BNv-mQdQKEUUX} ztR79%jRgy6Dhq~Atv%5= z#}D8A5bwP526l%Ye7iU~>tCDWG7ECye9z+1uDHL~g)?;wIdmH(J;Re`$5Ii@Np0f# z&kYM(sD{#tVaA*XHB#0Z8}PG>ae5Bzt%8*il9Ht1wB#+!gSu}?DS=AO&wA}8E*$-A zatTU;VWRp^G=G*MIUr*fz|s@Wn{!~UY}5GFI4wcD3uCq`v(G&8WO9SdM1Vtw_AZuD zoWH2duSQf;vwukGYL*k7iZ?3cDI+q-%s3p52q8=q=#8hU!QqX}phgW2bk*_R+-26Rt60zJ_=8WuWTyz@4gV-x6oyRx-a6&I;zJ2|hB+%@+e5dCTyq~e6UF;R| zOV46P)7Xn4q(||V1k#d~p$y(B+azEIaF*6&YN?W(o20pO$F|1dNs*o92q5!7gBB1< zWDPD;Ku9!hV(+W(9vfjmbN}sq)$f~z#q<-<oA2hQ=r)dOpR`gz(81Zzp0}TWPc}T$V^L5oiej_3rWywQ7>J9 zj|dlW)~zdp@>i5g&X118@A23ngn(uct@`re6MXvRS2#P_;N7=h$7a*ua5!SKX|ZVo znr4fxi)forK%dua0ZuzNlJ0cdA~?~j@ZXao>lFG96DBq_)yiyp)}%GTC^pv z$V)R0q?2URolNpml-93v1s3JaEc<1(c3%dsr|uf2>m@ktcMO<9hlT&IZ$DS3DjWUF;A7V-s35 zp~0K?F7V*_CC;~JxWC=tKm6p!_=7Khg1gNY|NPzW;fLS+7OwUYPp@y#ga&8ZQ$&u~ zbq9nE>o<4m}^rKW*HI`E3h zEW^Mgxy+OvB}_sjjgst=DYt=ga|K=v#L2SOTf3y*&XagyTBXd-nM864={_A`uW&M_oL9z?$1<+Ky%Ho{aWCxH&{TzS?8cv^Z%u*mp<7W5j0DB7m?z?$J=lO8+o?;U^|<{}qI#Y)q0`Btfik zF%~u6JF@i^3%jda6U%ONGHW(_;IR|$s)ks-{tUptk-GuP8{&m=faqtr`@+0^84aR* zwo5*(Y;B3`qQ|#2%B=Zx&g`!^g_dXa*hOcSfgkNJKr>-wEH_o^gtlLwyLD*-HG4%cSBt^Mu?uXq`E?!9fuDj;MEfbnB{YmfPAQ~#5j{H0ok?an z!<~YtN<%_+c3H1O5;M?bI#$}oRnO|uXlX@aNL(@#GlAnKM?Ci=Zmhl`WfaBZ&j#48?jE0TESZz%SH&e$_8{Ig~ zUD;w+`ijj3NgYsLLg|n>JUuxu!HXp>sG{XddYw;7t0zOn8NL;yWsG@&F#%SWH2poA zf6K&-+}_pVJ}LT@mC>(aLA=n#V?#>Qbm#@8r&|_)^S;zG*}kTEP37qnVCGwL2J!0Z z1_1ENy*oHN-GZ6XbrDSydSBlmh=wL0G^y~>;-aexV3vNX27Y)Y!e| zL`w#>_Krm2)TG`PM0dS|pYV?Nk)=Hzl{c`{z4xfvT=JxaKW0Jox37E-Fo_`sJ6<fQWj#J)+*`C;hcC{)8Opn6b%uMvBTwVKLIfjtY@cl>4I1MLWtJoa>VSMb99Q1;IB_%K+MFwq%pBz z03^jr67T!_8AKHj#fX4@9>nAn#$KSvXGVI?-_Aq!?-RtV{HX2JdT$v~_Nwem@SJ?_ zhBY5q7KsvA8$K|lT8;hn^E4Ypg#p`Hjyp%alj?Ue|0JL4lLTDWi0*)LY1G>wz%&EX zvnQIPh9SAqYW6G*c03KV3obhY&9V_AO77uNRP3Y>Gdan-z9=Kp6I`=^*A&`rZKW(7 za-tbtHI~9~V*%T*Wk&26O&|r#bRFaRdWY-X9^35(_wSwq0Cu|rPEIxoP0XSDgv>a{ zDl}QD>A@t}e?R`V@>y$BnH8$gi|c7=lp^DWMCEe2tW8pw?dJ+_(DC-O14x1MT{h?S z$O_wJ_C-4TH~QU?fBniPP|k9T4nsmo<#Z&xus)*{sH^mjO%5O;&5#wSt90Na5IBxG zE)QqWJYyRN-@i#jM&-C9)KFO$06fRwnN`?dQ=lO7R?#V&z=cc!Nb~uaL;@mh{y27e zCe{%TlxKFj?xb3~tmxuH5;A(pvP0o!Q|hq1P;%la`PA%jJcK2bEe}4Ch^fzwxu|3G zts@cEFw7Rtdm~JP+a!uc48m!%Pqgevm_#Y*nkGwrX1o{$U*^1*B283Ld0#bYF~|az zQr!*aI29KCKL?R`8o-%(zTX4T!|cT^E)=dqM`YX^(GjVf6xWTu;UOQeNxjo5GVIiOboFP)bGkRfdP(GMMMhB zP#sXjzMw-iXDX?t%@{+*Sd|FFiMn!{>m3UGK-ANl$M_otA6$-LgrQ*M>5+uc02tQg zC;-X*wjK+>v2ivv))EaD65xWM>6(H(=`9X%TMBjZH1kf=`fb}5hre;dN40Y3WT3;f>4KSIYH{?QxXz;ArxKf~FE@O*c~7~~`W(9mghl`(DKIysEzyDuBYiEXea*x|dt)eY2Qf8{ zG>6YXAf_Q^lZn?ejlTD84=F3fWw_}xcb@OY zB3pOnr$&tyhNO{=s+ZI3K*ljv4$iMn+f9)*92(jqmJCW+`a3sJPR7rZ%XxIB%fhBG zPfV66!Q)MV^P-(R^Svbzi=}R*^DT0x!K#f(tBSz+=pe0xC<1T!U45#0WJWW5KM)}_ zloV4RfBGe^cYD0^)~i$T+jUVDzf0ZCm(y@G0x*d$@JpmaCjbHqxMKn6@JIk_iu|ST zWyZ`c8R?tiKPhvudEpMOQV1q<=n069y+cth8BwOP##7XZ<6sSCm}QQW zsxr!5q>3g%xA^M$70$LNxVPQnY9DdBIYHY151u~9cC*1N=e@4v!BV7{J-~1YL}HdUJ`B?TN{&P3)sma3YVE zQSRXsZ<@32jL~OvE@#n^KcLJBu8{^L4vM9WnC(uRD>1XEF2XLIH zaWc)q<)nGhcl#l)Lx5rJOG%Eo=ENaWwiva>7tYf?#BoL=Z~GRSSz6g$qw-NnPWO^b zhr&IvU#edzEx;QUjnfLsX>o(s*60fzXPNl0;5e}Ks>`16>D6x92FGKEE=HVe+v%r9 z%cGBe@+sP;!K?T0;LgQalGIanR^8XbrBX6ddp~G}&+!ZGbDq0Z#A2#Li?2|rz`vCp z(n=tOmTzBrjW3rv&Yu6P5{UEPZ|Z2uj-3!InQH*bkF95f@|e6Z#sx4Y@9)W+3YJVq zmJ&N9e?HSq)N59Q?$Y#mGYx=eDG5hW%&+<~NDDP4_EYS%9_=`ltC_6EJyoJ6n-(sGU{b4y*h{<9m-x62=0;tr* zWJiTJwY$mzMU&Nx9r*%hzzyH8*R?#NDqLitaLzxG>GPO<@P_%y_cvE$!_ zp+F8pSD5;)G6~@@w!Wg1V!0eC;l;z53}BE>E?Plcu6BZaa(RPS&dza&jL)Aw!=~Bb zmCY$)1WvYF+&|so!Luv0O@kkQ`3ZjalON%{+2FtZ<`3}Bz1MN~>=YdX`{Mx}N3@~A zw%ts*AFHO7(_G>b;8tOccnj;q<@3#0+R2V4k;-2zP{$S4MS%8~9tW64ku_H8?lC>g zoJY^B#p0%%V4`a44^2)^)-s!oOI5SjemTRrqnjLS96eN@`t_9$_kz@_rm%3144Xr6 zEr;Ho+ngq1PVn>MEEUdSwq0Pa9pq)3FEVep6ufH=I?{~>BJD>o)KtjK18?;Tp;dsO+zj%o2-2oqdw*&(R-PLV1l2FciXbWj zI#Yp~h}oOrf=t#H1>VIv`_o8gV)Np4e$7fN2uSMW*c16lsV$tbgQC(w$i?)#ekD>m z(n121anTT!kltd;v6GZ19Y`~OiWN*4%KIuzf<6ms>>CBFN01*jejaItMt9xF|g3f!$uAY^qd-Us$F0 zflarBi^)}51m|Swm&7xjPt7RYe@kzsG^b<-6ihwg(%KW6pX7lP001BWNkl)Sln^6^}Dqz=wB^1F}65%7oYDuL#v(`oO3YEkGkGif5rQxm!Q%4R}@Yx8~5t1D*J>nBndoPYr2#@%~( ze7(cd>r4F3-~AbWx_gFSzxNh?`-5K|em3CY^XE9-Y_VxuG!$^?I$Z5;daxiyG;NzX z@Ys#X^H)Sp$4*C>H28_>niC?P(n zgg$hNr@U)eha^DgHitgP6=160q|u27Cq*NCWAaE~>d_g3> z2)E^4gv(%hF@ItI?YM@nBj3jK%Bywl%Va`yRovz`8 zv&<-^SN4-hHgw2$y7aV^fRR(UvH(RYbs?nQ3RpZ=4Z4sRyCUzbqKr2Y7Y?397l8#| z^31Z4Z^>MKEzUF!Wfl^dr$UaiV$c=QDu_i7?l!TYzbNMCwkA3eExj ze~g80+w_fq7#S4$Vwf~}6`J}>jx1Vf&ds-pBG0?47*Na6_H^6gYIne)o!ODW!f6 zI!3OjoN7-4r_IOBqQ800@t+QB6;6R^O1NC|}Li2o5c&(V3Kxi|@a|VqEfl_1(Ur~9^!BEvq|P%}CC!5@ zRZ(7_?E*8Uz_>+eDOdQW)+f4{k`zi0eP_MKlNrBUOq3q4oGO#T*W=MZGZn>&vdN-m zmlv#va^A+7crOwMPDGTv_0Mj9Bn3st7pS$JLJ7c&#DV3E;FMVF>t|JDJCmwef82S! zB#Dz~BGN(z$8vPZBw-3bBvR{HH3U_0Z%!bhg8HD@SL8jF$^t$kg^)fvyFVQ;4W=Mf zomx$Owreo)!!_Q*7VJvA-kFi^fC2-CE#mJ8R#}TB(}hz1ZHm95i$boYmdigKCVAEr zB3J)Y?&(_LvI1W_VG6N1aX1l1OhKOURBnm-t8b-@!lq;QP3DdV!n6eo}RQeAUkgM~*ngZW6x@MA&Rv5H`3u9t!K8Q^E3V zeZ#Kq4=ttew`!c2@e}hH<-+44M6}_+u-Kv8bAN+ z0YV^r`2N?>whb7-&GjCqr(2wyY|wRapiK9Nj#Yuq@1|5anQOM)nidkTXoIaSutm(I zgr-Mz1$tExOj)3!xJEO%2-offH40Hio5<@VW^y1lRvVm%hzJ!{l@F3LYhuJA*pFj2 zkjoPnR&5-&{i4p^RVce)UJ7|jBO>AGw1%MW~(shYNn2$ zqR64L2P!4%ECoa`&$7y7YC`!vA6TzT31k)QmvA^nC6hvfFl4}^^ys(rNp{B^l4~YZ z41TjxE!D+(tc!7l&_T8n3P-dq=i`}EX}8^H*YlSY?T{1eteO&kNmsvQGieH6=ClzZ zJJ*Kpn4nNZg7VKoTY%@T>B=05mcFrQy+>&w=vktHi!^8r;y6}lYO&9ACyD6Q^zyyn zQJ?_kex8>nCL=~-v$A;vdM4aBiyS#lU-!-BnNpr=N${lFug40>J$q=uG@8~vJugrZ zp$(yT_uz<&lPw-!UE%-u=uhyY=MQnO+2Vir;Mei~Yj5Gs=^45hakkxJci7|D9dWij zL1f0Z-QaYyMaL2Q;{n(EUEd`S4Q8Jsaz`+{lA*Z9mYAPZ>=rKnTryLx?|AxbC=Zrv zET&wW5gYMj*8aK*sFzv~n%Bme{7*8FC^OF1Cgd$DsYCkhIZBR4zt!qffm30Ix+J`m zENB&KGLEzYWbj8@7B;03uUE63oawT?h%0YCN>)-17mFpzWbTzUb5(&x6+3=`R#xfH zNMeno!jyz1RpGV``1JFKc=Y5s?wp_Etv6ml+ctgBjEu9>t-6j7!t^*co3=7cTk52i z>|bmfl`&=?yT!tE>%DXY0jl z!>&DKEKRa%`5T4{WiE{JmV#>KTUFSkc&BWv&aodz+WA$F6U+qJ1CyW!5gKX+;VTH6 zB%_7l5(mlfS~v@gXw8WELa7@4cM^$=+eBo>*|tTDz#sqYBmDc%{sthzzj*h%_y=GA zHnwep!!hFKu*czeLcW8t$I*t()tumbkzPIF zCH6SgVDz*+n?vac(sJBR4>hW8W)vvxleiU9(2R6UV=)gui{yFZqH|8i9OtF+yZ`PH z+npR2(Os^K_O4J%-UKP(9c2)<7+FV%Qh~q=Ne>=- zZ5zPct2}SF8+`KF16*G3u-!Cx_wCoQ-L{C_`vY&<<^`_{2Aq*A&Z5M(Ee_ewE{N9w z9jovtHmd6t1?mzeQmnXE&QMcLd6jy5-|pV)Ux+fVO{Drq?@p=GYSe|9X0ZngRc{Vd~w;j(FlN)*)#H$T6xMIVS#)ek+Ld{h{fJ7>LsX5K?L0 zyiantb6UqNF=m8)6lHI!Nd8@^KW-US{$ zyT*32!P%z8{un`l@cEOk@S`t2#-~?L@cPLGe)VhLz&o$JiO3O$?znIs83zFC3b0^R zYS)iGY2>}guwxXIPU-z$IyT8RUTjVg{bwN*&3|BKLIr& zezI4Sxjy2;%6BQ2iM`S$s4HI5pCaVCA{%hq>0~m>(o0qP+tLCQm;8|aZ)QOU$$jD~ z@4+QF$D1udUYDn;uqX%gRUM7Yt8setpwO1MMSJp0WJ$hwjv=V3#g&HxXY@VtvnxSKnAGjBH3%j^4P$K}CTb)Y{9@|E9MF0H0t;-Fp zI43#1$99_&C%cKo2FR;}XWD7B@Z=o-uDO9riX^q@U{gUN(cbf1c209;v$-6U)bt8x z1>F>#ISXx6<~|E^(5fjx>@)BtHjqd_*)aP^>cQ}W){F@VO!+;U4eXTtT{ew{>1*P$ zO^sq|R19G%9WSaoRF=+qIV!XSf7ro(%?1$=CG|z`1eyT2yt%=v7kAJ!z-N!1;Be1YOR<_WAP z@|vJdD#R(cN>uk5xkebR7~2W$>VmQMr;1ACh1osSB~ZH4wG`(IlmRRycAP)3S;}kA zZ^4E7lles#G3)Cp6^vC~tfeX%m4_&@3Z`kFSK3DvI)xQViHiyTD8WA%zcUyd8Kr~-lDsk1R9xiL zZYTRJkP(-$h0zLC%^IMm1X?>l-EV)^GhZeBn+w5~y zGUa5N*%wSp{#)+FCaRz;g`95X-b|*iXS>WDeoll?D;rTH!(6^k6v1UTrY;l z0$4wUHIi6WMTm{-%=H9DE?4ly!q#g9aG^TOCDG)>W`SZ(HVjiD7nRt}f^B1A;S$Q# zpoyNT_0}zlGY{3QoCzjWbt*aYP<3B;VZOPdvrUuK{}}6gbcsSQ0oME2a{vQ=euq3Vg8y*&D}$v~%>5mEtfp|q*0 z$!n++yj+;c5xb?^nWJ*hQp6({@JiH6$@5`#X4hCJ-9+Rh%Nl@lJ5eO4n#Ns$q#Z%- zGjd+ytQ>|H9MNT$uBsF43+E9tn5f#a^wyZqG!i4=9dfY)>}+}<)yg`}M#Q2cyMYZ)rtL@QI;ryOA`}MOH7gi93_>W=k)S{`gnFL&1f7;oIYi_6^(9XI&&@dNz3pZyh%9PzI{_yOLz|2poU_r>q7 zJ0MU%1Y_Uru?ZU$57LiH89kX2z~}7wOdFIk932%GY{8l=D+kph(Oi+_64PBTt~#TN zuQg9}zV3o|DdAfze?rHM1{L?u8P{T3xp4rroSxQLqocXH_(c^ta&1ad2;5voO1L6R z$`rvG@EH~@SzUc{wMg7|tnSoi691Q3)2!(ZaG@afFpSLStZsf+>5ynq|Grz_Z`;`Q z6878e1~)f*{N&>=05INv_s!|zVvJzM;OjdUzY)i-!#1>ju)j@#3f~tvOB1UaES-r2ZIC@7NJVod)iGk3t|#sy zJzI|#<<*%WY0OFLF z3zYz`6D(+hvtUVutsK={zzPtGSKtjfwpGRlHI3$iVoi=dEE`KOXe+1*KvK`RX5ESb zezI9o&I%~gI!SDZ%;z9>71<&0|xhr!ezuT|^@& zAs7~cQ$Q(*!c@>`q{@E&;HQPuF)hd>n42~rMG+|I_k?~%IkbHbJ2LlQhkzzDgGPJA zoA)m8=<*r>@PGdJFYyOo{uJ+>-NC>7_CLhCuiV4suEW#o9fbV^WLpH+fS)|9uM7gtu37Sb=xWT&^ z(KZdfdh`q*|Kee9bM%e3aej7+!{In7InSNmUH~hE(9@-JPj0n!Q<|0X0uZ&-aaj&v zUdQES6vLtur_$9qQCm!kt3|(ZH-PFg@+BZ@1%j9Z8~o+Xc62pCG+9_2$$8DGJDlCG zBGGe%|0XX_S^z4igva~bR$PtUpCKY=_RDWLQBp|Uem%0KKvz9Bc0h}C7EnLWbdhon zmvKIiR&dXh}-~D0P)ge7T!78U>;We(W zl$dTOHkYJ6Go_h@1rw=?1rlT;pGV>(}u9 zYj5EgJAC={5^d8=jgD>8;^O2C`{N#8K6{F@%~qX@HcS(}7-G&cAduI*@-!XfOtjc% zPF}1%y#bq$kdL8eMiHDC?io_k{JG{jM@z4bxdlg>Kt5)1o{_!q+s-Rrd8 zCtWfZTGgRpuMf18)OznpDwTFS4qQODvqeM33zG7mTmvb_lhbs;kE9%p8|Qk}8To-63GKn--sc@fAM#{42b2_Z%O7@GiQp z!?R~sxO?|}D1O6qKpu}B7~CsI-%@dS%@31s=c61IdbL6{W$xQcra_wmn_Nh&4xCzd z?-o?2D|NW4iry&2%bJfit<*#wX)?%WzdxZ!Y=RKXWc z%nO1^T>0y)l)0=>iFhq{3T3Xk7uOT1Hd+C(6iKx8_f@oLTbSB;xf~jbjCyo##G}&R zJ1Kh87|&b)p;gBx-(9h~Y?GdEqD)JDbBhW9Z?YXI&$VTXk<@j9nYoznu;>)<=o4=D zw@jELlEjbM?gc6B_wraj%yXBT}m1H=8Ck^CmQCLxYZc>!N3u2mH||AK?!l zeTolG@8Ey>{y)K+cVES!JE9HEq;lMb08oS7evc>Dm)LGLc=h5gu6H+SO2dU*C%ayx z1UP3d9Cp{<#M*(o%+~!qPFxSu(mN zRrQN1fXj!sH$9ZSv|%PzJGrnLqhXaZ7^@09uQweyq?t%8=6*2yWhUO9EILpY+$UD& zH76c`dCqjQa5(+<|Ls?%fL<4u{z~U@&~|JHq`&?z&EkVZCrS(=RC8 z4i0BF<843;B0o8PfzCj3&JAcZv+WF2(25qj+$G83_29%J;otGGFZqb;IFs}FwJ$Fd zJTfT<75&+Bo%LMT$Lhwi&aMNGg9GJCavdYbw-$Y zQi85l!IhcVU`?aSTM|e=T$RCN||5f**?zkG60H0@hc12 znI%iaAcVOYGaW6`vPQ5oh`=!>VTp|eQU66SbEuRyExl8g*fg+gBWtE!6H$T(G(&SD z#{StiL-D^q9P#|-3jft=~{&hFa1`NjGut(cA zc>U}?j$MaGm(Osz*(_^+WGU^W&bVp*Dc^S0xshw3LcXGAj;Truh430p8QY1b8F0yp zSlMfb==N4Lskln0T^}9hl?r|qcb&01QJTB?Bw`!&1gh{M@8W*_LCc$ zFS}HrEW0?9IOux;h5i8H2_Uf25}J|)=Y*l80WxnW^0rYn>x#}CLX5qqT~DuKP#|#Z zgI&`!AP}BByTpS>PjK(z9B;q*YOe&%{qNEcR5xww&S;LGqiI|B4$2XyDPwUi^0BCo1E$mm*v^?Mf}-|9S98ofe;rJb$s|zW07vy!cZs^# zBfud9 z$9AlcP5y{e6eNNKR)*&26#v^yS3+bY6FHII!iM;*@mL zY|}C(Rsqy^2&OpIf0ka*Cm0-n6?KlN>0c-IUY}w7H-t%~B?5gHNOTINaMq|IEWOQ> zB7kSnxN(g~ZyX@U_!_B!?AsSbFc8IhwkUyFt{|G?(FjIKD1;Dr_khHm zLw|0|=i7#WrU`m9EfAOky4Ydg9np0i8fu2+8PqfdkMwX+#FNA1nN4tF3MLHuBn)4M z;epAqjBzfO*kF7t7(1p@N4BeJdk7RJpI>{HMjpT-80Bapcs7kQfTuG!3KDrqN8xCqz{5?Fx_HF- z$tjMp!~S^0NxK0vqvH;z?G_zJbTQ(j-QsG0gY%OUTW{bdD07a5oP8xVl)W|K0ta$t>@lqJfh8PHngH<_78 zH?1Kzuhc%cEb3KC#x94TsD-d(S2XB&G5P#b5b+xta27w`4Ts+tpL+^Efw)nO0(srh z+|v?CH(G`kt;cAc4zMi>$IZxMQ_jeZ6_Uwl=mNYw|Q1I);jQOa;NF;?ZFeZE{7~a^R9=CJ@)$Q17JWmqaPVG-JUuTMBu4mW(|6 zLX9p#H^GQkwMf+nV&pgs&0|6f#%FstJo`)u001BWNklwB2J`7|q#ygr0yTkT^!%qcr+AyrtqQsZM5&VH zUr}kZ?)I(cD%9S?65_DJ2dM}!kdtFc5QtQPF#dW}I%%Z3g$YQMblylLO8Sm@DzijR z&oN7XW%2qN^kU_9%C=t9`<(PtGLwB0+6sZxXC`&sX(Zzba2SghCfk)|g%R*lsLY&0 zOiqy@yCRc@YC2o$#$jH+e^&j5b2doGU>?Qv=BuH*&z|j*y_zAmYV}0$xI~}tqrfvAh zaOuo@CQS%~z!LcrMtQY(oy&MlfkZSbDM(5k8@;EA0zw$?KTWfk)9(i;!Lk@xG4|%! zN}6V*HNnxzFqB*~Cmz(vJzOn$Td= zv^`Kvb3=!z-u@xmyKW*djTxx z0c%6Z0@s;y3`KI1jkQm3_`P;{QD+o4sYWcSVmFbN)n{+WYQf#4j;oN1Vl&Mqw+(Wg zm$d&W5ngeKP=RZ|daiZJ?3YCvONCEWyG}^;BdN;0asaTaJjuwg%ex_k8ULJcUWxvs3I`nn74s-L^Q~xur_$_o zRD0WI z@3)s=)9s<9^3h`F&@&h~!5e0U7JU=%N9jPaZqB5$9+W)%F=sJJeP|VnL;C45@*sCn zsM<>pN(CN!2dpoeXnFek;}M&-#pz~?!|{km&!1t_ws`%{eY6em)w4_79QU|)dV%Zx z4*$!K{t#d6ukgJ)Z{WZD_OIaW`*(4D=&(B+00vIl4I=YEnHKkOrIyV4E&~@fE|rU1 zZ=GA#fy_aOma8|)sBqgV~cad1teH*<#~NFIo4I9E(M9Rg7WaP2HeRh-Z)9l<>*Ej{%z#d z1d{t-QtC-uAEvc2=la?upl@!`Or@ro`f3HSdX{@#5zL-IpdBZWlr7ThkOi8;PGDfl zY%s81R~dwmJ;mvghz;j0cT$lq%I_y@qcWVGO%sMOBk0UXEUiEU4g}%=4lNjrF2;$; zHIaI0Ime)d^~%M@$6Bkl{&^aPzke}~2m(3N*V9gC!C+9YO|uY!J;}t`LqxMv1v3J| zpkG3j=P~6o48F~I?;LuLIXx4566e$RHf@XDVULbmw4uSaZP7L@ZuUDoxqgl}?!JO; z+u%R`(7;qX zVNQX11H3ZZd`ahrgi=z1x|ZMY&uwj2>b6)&F$tZL1WB(Yzq+@H4$A6*tHGM$i;|5( zMS&{H1Q}Xmep{Ktr@M*Ql&PyCoGpQs*ot%Puo1y?gN$L}Ch9iMpU8a&mq6&cehz4w zCk*`|aCUZz2VXtG^UE82@bx!w|K1${z|C%tP1_<;9Fj2tg)q}$B4@PNbD=St9pBOx zDTN+HdN3)%@{0;$y&!5{z4SEOa4xEU6*n>{bj=E7R+m)aBq0G*C<#jo! zu*!-2d3%qx!xizGNf&|+#ngVDe0Zn)bxn?l{^&8*F1Cp{%o5Tmp8qK@7d`V+%S&uh;6gM8+Tv9^P6k@(a%4^kDh#v z^LC5>>RUg+x88gchYomj`5b|OwrLP3;Mg5;jIq~(350-vh|U0;Gh1cRva$NJQ>qog z$m;_TX%@kft!uXZo{ogsV`rqr6DR>!`#4QgJhj&#M;4};{p7!$Qjh1td0g8XZ{tG! z&*jpmibClBY1d`Q&dw?%`M$)1vArN<>?4w)-(pPH=+y#enmB{NE~EyN#?`Vl$sDr` zUC$KHR0OVAc(0&1gZ8=B#L23pIYJUJc~`@;c` z9zRFdMZEpyD>yqnnKs|)$;ov1jzS(hsFw2(C~09pR4R=pv`fmsK3Php<3vqff6nQ^ z@CzP9sz5QayDy6lEsI6v##!+WUhG=(=Pt@2lr013wx7)vPlB6h^QelQRF<;SgzVSs zC9$y9M}D!GTY~JgzgWab_Go^E=VCj8Of;*3XCB9h6vsqa-!B~ITW7pPiQVU;DBj9~ z$?|5rmfLLrA&=s2igGm?Fkd5i;%^R6kbp7?tCF?S^c0_h1^?4$=-l@#1ZYMaz^Y+8 z(4jGj_5`I^8vu^eoc4d@a)<;d+rn&pKIo2po4|aYzPSjmyC#~JW+`N z9-dj?fDjmnQ3!XE08-c;6<#^Ni+iW%IC2L1`Ep}XFPPr zK}DJqCpybYUySbzzm6blZM48-RpQPCL9CK@xr0!X(gY!sUy5QFgK(Juwk!}XDa~3q zQ*?gE|CV_Glh6<1bbp%sNBe-wx(P}b$0}1-qkguQsQazp>jNaCv!+pMLx$LJ0Wq8*kz4^aKEKb#;TyrX7Ahh&4EL9-<@g zQdyS~vhFsSZ9&4ugbP?K3hQ#o-+CFaRLp;ISkv;9?)qk|1P`cE5!$7}t^w?*6d7)V z0WEhQ{Q65!+VZe2JMGUB3OWI8rH{z0yNJjazszvwgqAZ)a*AL{#VYuz${d0jFw>S} z0w-bR{-(hTb63dUt!V|?b|etO1sz-R+FF!u}imHy*RzNI#P$3vkZW#V&%~^ z23e2AKu%&MUNi9QO;3XzO+PD216Ud6CrJR}VACR~^HZWVDcI6fboF4zFk>pFgV_|^ z$r+Ty_Y6~q&Dvk|y3D^^x>k_|$~epLNV|vmt;Pk z*H`$HkN*aL@%VFm?d%Tz`|tlI-oAeqpFeqqCs&uab8?R7W7qg(J9R2YMj#IbapZ`m zk4Vl5J2wG{Ug|*DLJDH4?wT|vDaqKtO(ejWnmX3E*#1jJ)Cjx!awrbt#Ri9L>} z1|cW?Cj+=s62raQne&`4=ddlWI$%?;GmT(k*}U8Iwb)fKHczTj%#~{{-I@!-Sj{9d zzD|t{Ujx`;^a*P%sff9rUXoqBv|Og;hUs6Vma);FDl)}Yw5I%@W9*U+K#UQOA3wtv z51-)lWP|T~>s_>MgXdS*xO0Ani}N#dF%AlqB3O)ba4d|tv5bWzG07S^d+kLo1IWDi zP7B!<);-@mk^dzXzs^S>=VEUwHd!C9DgsIAM;418nz)dOu0=w{rFCaxuCs&8tX3vj zSyo)U_352`MoMQy%pT3wDRxFFT@P%L1WvhCZg)@ShgmP=^!vox9gG)gvgPhb*@jW< zxUoJY zg5Vqt7D%I|MifM8DV#*YCCW}W>XNla#X*W+<$aB6;LFnfV%OhZQ_pj?mIcG=VG{Rjv$v!jmV8D$TM51NB4j04Jm-h> zlA08mkvXVdv*x~YydpZqmXmm0O?)6JaRAsWV^!w zO%rhU)4|I8wV z8C_p!0-@prN{eDz%!;ubz-R4Rzj_)Yn$9MSDlk)UrFNB z^w)T>d5fHA#bQZ5#N3r)+m$GU6;^@6m=PNblAN`Nx8^Ae&S;U2xi}=_3l^#$kZWpP z>_(AaTG8!_BtPG$WZ_QT8_Iv8j!jU7D`GdLEOE#R!XTd#5pg*53V}p4`PmT&Up{<- zPrrDK`*+Xq;WxfEz5m78=_LOjs87BXg2YhCQ%!7J8pobHS&r6$c~<09iSyWq!*w94 zU|U#a#k3IQ?D;xcMU+?tw?#fvsbI>MB8Bz3N)_5Wu(@vQRMjBljO`ifa2&98X7^b; zN3mF+BDy#zu%g3L?tW{iF1mDWUP#!fm>83sr}9z;k&Fd#z;+Bo?q>DaK=X{-aIHKh z%Z$Xng}iw-MuK8lVwgRqMv*KwwoJ2^J)_#ox&$7nB8^mjG}6&viZ?F=AQ`98+6lIn z3gR5qx;>QSDm-?9X)ZG<$n%eipM4ag3ZWgo6V)8>c$SZaF;hRr!g28H4WU;E=Dv8M zVL?*B;0H`7@xMkliOAf0!B2^r)7KXHxe{=3z>H}YlcN~BwBE~5)<~)1r|9IdDMTs# zZ5QJt@}{?3CWsd9n)L*D_zX#LJy=&o=Ba26L$Nz5>4reqwwpm+xA$Zv8b*pHu!0c= zKdZp~evWb8&k|&rI>m_n`-f&)CDYuv$j^=gjWrC5GLCA}+^?rl?7++j7h5|(!64B4 zl_ra(X)oorK=2BWVDf8HdqtHm0jk=RdvH8i2tAuZE1=V44;h3m0?E10@i>YR$}7xv zgNf2q$okfDrbopPGtfvokppf1YJW2cT*b)Pwi|$eXV+J_dwP!ZlM{3?;*Wp!WBmRX ze}}tagMa$|ck$ty?_hsC;5fwi8}5dpwwunw5PD&@ImhhRD*zn3PVa4=4-8c%*ijoP z(NDC{8p_oVyj;?y;HklsoocmUZdnQq~ zqaDdlQ+d^Tu*VY8U#su1rg=jPudzV)OcZLDN<^|v;<VQx9P@M(+?KqiF(8Hk-lV zy&E=jz$c$Qz_ZJ1eD~Y$;{5b9`}DK*;8MZ01o%412P=X#j9(jE-`yf%mpqu6GXMu= zilB;hrfhItridCWOa$q4T(y2z&$0jgc7Kc3SClhQ5rMo?Z6v{=6=m2HT}x5ni4M2) zOl$5jXS%(V3pQGF!HfGNtul8vq#YZZS#?%%C3pRl0+Qa2FrC~)={!LdK%W3Zmq0Q+BUY^8cQ&5EG~FB~EIR z0CD2gjWa^BbDj2qf6bBY_yU_Wc1N-BjEZ@CsJVi|YUy<~WUB`$0;bsxD@ z;i714>nVe#i&s&KCz~PJ$&pG|UeX@{q0%$I^iVm);Xip)u^l_XJgNr=MD&2CoM#L} z*DJcpI?iF@_+Pv0%Gr;TJrmn5MVdtNA{z&;SW+#}?YUzl@dV!2HXmXopcDv*>=)ZK zbp5+=UY8jSHT_vEgh0pF9>J@QN3l6&prYbH6T+am5r-N7K_D+iMA@xkyL9JFkgVX| z3b2+$(lr6GBS4Zc{3s7(@ezb zcB2ORa`t=DpQmL`07(v_L#1J19xqAoA@l+oyW;_mpFGFkef%Zfd;3*<dgwUsP3<`voM@Z>an4Zqe*og)7G zIViGS@Ip4|dPdj4uq%9lSG0PP8ipaLIZHl*xtA7}M}!=w(aVn2u~miWFpLG+LWk3# zRg%MjV=1i(y>W9q~t6bM$;NsUd61(VrDupffmO^oO?f4BtiwvcS2ktfCG zdAl2H4bsZaQvy5O{k}^$-|H~_yrigd9)QmUX!bk5Q+g#5C-(N7 zi75aSX#%w3B$yUJA&CCrEXz&9Z1_m|9gDlZcC8k^5SFQ=X1u2nzrjAtc5>&rbW*^l zWr%oPH<>I_lAP#*nCu%%Mv8PHp(u*hV5(j{=Q$U_>>%a(KuyMSNcE8&(Gv?)I*k|c zf`I}~H(OlpZg8{T;bgPLvzr_I(a-)GfBMy@cy!p~x8C?V{^5HcB7}hb@rYM1?jaCx zv)>OY&y=jI@gqe!Z@M&&vfAd#j+S|O$wGqy1G;3uGoxwL&;%=wF3 zhlN*kjAUoF4<=HblOyXniQH%aN_$SCD>|Sqb^;eVUtFvXQGXi%?!zAaqLLE6T$*Q>>p?B9ux&6N1@|3N#^B8Yjhu`#47g;R@jtG z+=fwHiCJ|cXREwk1c_zQ$Oeil3V%~l3{3OCX+8>uXJzIg+e_9V>~{xf8G|G5EOkMk zf_S5+wnM&0{WzB>snRk<4=S9_skajGBrW7MQe3Tlg%qxu%5bshDJk3}GBUKA*`q?| z*0f{)lZlILo*lV~7G?7z&LLh@7zdC*I~*%AyMl%CYEG&LGFEXg@aB+ftC!jw|(|54rJSl=L+D(ps zbpbbaPex}Pl2eYPWCqDe<`2Rlr8UEM5`*YmH4&T4~8vf1qzK>Tg z?t;+U7PXcusa;^?D-Y0uJ-r` z-~T2yo6Tg0aX22aZCmUP2b>H)uTK}!NIEYegG)iIBxA;Y7A*k~&Y-0gYgAh^up$bu zlkm-Gx0M$0V|%KWju$vF7V4>KEK$!wXZxACT({~8(M$O(c|}4@77ns2K+-Xf%f-~_ zXS+~&XZlIx__sRXj5hYIkCkHPkO-!`;5gRh#JKE%C0H$TMoxmDI-bzvr7DH0FkBbq z4C0fOL8c2xzQ|TdIDokhM9Z8?l`0BlT&D!`cykz}3sEYJ2#^)pl9EKEZw-m_O>a(Z zL*S`Mt76fm=8+KrBc~%{70>;IorUYH+P9?DOvkmLY&eG5d-gYqDc&#_OK~tp%fC5(jg)3$LZE;)H0Tg}JE=g}Gyx4Y0;Gr&n`#_cnBo9A z-6@gV%y-7*Ps86KFeuEhXqk6{_x+L`u07Em^?=`JKo^w-9a(BlkjBN3+1c>IGUuG+wne{P?!|sj;ymkLR zKK|+<0JcC$zyA0C^#9-|S5I-j-Qu_3_&R?1Yu~`hc8i<+9_J^gxHvh(^P6jIng;jI z?&JCHMs&dEu)^YVYR4C>cU`INz_Je-FA1s=d3HVZ%|><#Ik%ZHvLqlZjaa-tbFaJ4 z!p_LlB)T(PF6O5gB;A3il%3nv0{{RZ07*naR4hwYYg+$#p7hKP0A(ziytIRpW)zPd zV9I!e)fB%dCzZT>lqV+fwj5M+=yf#4dWR9^@$POkr8 zo;^xYT8MQ3TWUcRFM}NLr##C!cR~9=8MQXc)cGYMY}%fraCLoyXV0&(KOXVz58eSY z<8U})*X?m}b_Rg);L%gO{_4HDB7UiBQ*j^ic<795n|fb+qdR~=RSS-cLbRE=W=b!d z6)ckfBs?V<4$#E4*I9`*jkGpRZY}Tc6*g;S)=6H-uTo?_T`H>3A+0LQnMM=q!zw$+ zmkGcUCg3$Qy;$(rT=-QzD$X;FIg3*G^XEyEG#k_GEZls!PiG)eCIL0E-BL&g+ZE2C z)v6a;iMPm(gWI7a2Xv|kSw(z@>o%S+0Z)=_cDfV^rLpL3CK7csmpaZ^js-aB%7POX zlva?HWrXY1VhgQB4HQ;%#qrB1K}A*kGMvm??1`M}ch29J7Xamqkc%nwvP!Dzte<}_ zas*L8+vp@S1VS??cQ+wS{>^gNjAm6+VP)Pk2yn$}j`fA>EQS`Q=iLJ;VJhBdtHQuQ zBrU2H07TKf>Q$###xWAg?h+5T;(=m)6PX)HHI0IF?w&+(O-|)P)}L36f{lzssJTv( z1)%KnDe=Q10*%#r8C<|wr5`PgO5y=Tz)8EoUwr&i{NZPRi_dpgc<=NAzx|E>93Q;- z7OwVt9F9l4arXibo?YU4e}lL0zlQ7m4xc=Fh_~*&GU>xu>o0aj&x*|q&tYZ7xq0in z=Vf*fz{WxiYHpmp+<}eu0l^W~LlT3SS&0{FQj?<)%%w&}uJ}!H86QXX=+d^UD$W8i z$1Jx(Th->OdgLY?Q`)^P4f6SVu(YrZ>x`@nz(S zqUP4XwxyYeVXmz1zO&b*ONuR1`6o+@r%o(Qv4yNyvsp89p){$;4)butl|c^radC2h zmx~ZTv^W%uZ1*=sx^pWvdj=BnMXCF-DI2}0Fh`pQgDW))#{Z5yh!~RYG%!>LGtj(l zY`2HVbOF?1G&5URGv}R-z-+Z=c@S(cg-Mr!+)M`(Yizi`5j>;^BSkyN9wy}6CQlS@ zlPL~%iOf#x#EzydYH&&A^nOmaZ5p&qgLxL1(S!z@rorwo+6;{zza6?bC~=3N6XAgg z$3sya$ohBAi7u1;gd{A6#yw1`6E&6HQKq-7EQ$1f6-9ZDeQ1UIMmjcWhJtH7Wn%}dLl$j@VK6~H50B|wnT-i#zDLxW9d(EE(%SQdx)7XdUfnhiiwq~3lk=2A`e z0A;WEB{#R`q|2r&ohTzX3&m-c%s9zl>c4Jtd8w}K%|){FiAWc-#G+#qHRF@*LNta9 zZl8lGgQljlg7t1GoOT3w7ULL25&E-ZF5<>m8Nx^%8lOVH5IDw3EP#z{JPZ03vlM(9 zg+n;C7r-Rw=6`If2|}JaBqvk4JqSNgj}rBGE^5BXe4oIBdgq|$XmxWS-`J0?-%n8aL|Ayr1rWjzuNbZcBzJvEk@s#0>*-Jp59q)y8$ z0&Eb#%y{OG{|iKe4=6<95A{5f0wqXmqjck$QhM@rCFY^yN0YxQGZ6b~3W55fD5yGq zICID3XcWH^H}S|~_c?Y0y@&1aWSP@YW9Xz&>@OA+=XqS7wE#Df>T=6H9KqlSbOe2h zvLC+Y=SQX(3Q8k_!KoOUb-|pxmc!tU8>gf;#@Oc-%-FOWG=Zk|9tXjzV|PT19SDpr z9u+w)Q181-{ch?~ak|0HKH{%F z`ze0(@KanJkNDx+AK(Y?et`3n7SFDC*c}hpG%eb;Mb~wcz*ppm*u^PU9*zgWOc~Z3 z%T6Ep<3Z7h%^5ZZJGGE{4vBLV%_$Q3hIE875IK)Uu(6e;BC_iNuccic`@?x{4?Jb?&`vCn`^uT%?X=`pk0HY+58y19QZF z5R^ml%(Lkzv{jA*gju_Ok872T5bA?kTpO*BvMJFEPH65rw50lEqJWVq`5DegP8yF=2^XCc-B(j0c@O-0CM5J z!CuKOwBP_(>n*m(ZQ1y7LCE#MGL{put;IhPMID6PStC7lVaFv{_kD-XgE~LMe8b7; zv++1H&+@f2`-FU@A_G>9Md1kaNJ;@26=j7h!6s{Z7)2!?~Z6Fpluo)yAIE9uJP8rd*~u?=sG;QyvCn={5SaM z$phTqoZ!Ft&adLm={dIT2D?KCBEtE}X)k`J&SFmXq67(;IdiT^HS2=T3fJcdO7PJD z1!Qpi`ADS7PMp7YIS0C!#+Lt*L7^RN?N6t(ESRog%5<=$8CxdI2(rQ>WO$G;$qk<7IIXRtRhp0`2MDe3xom4 zY>GrK?V3%V%?LS49eH#{@_QtS+O$o^SMaPW&G2;C6M9f&T!ffdl#cxW{pu*u) zM|z<`MfU&G_O3mWTuGMLvFjcYnORjV79UNr$?4Zjw|070GYibFq?NEM0m2G`r0>#C zlLY+;K_3LH1VPYirO{)1HT#&Io_=?eY&O|su^yQj8SdBg!SBP&u3h)YB2_?lv8pm7 zBiygwYj)1DbNZ{acfS~C?c60BwSp;)q{^DroV71ghq3*-46y`?$ZcJCmN)bHl8=>p zr>{9T!(aWZI2Pwc@Z`)?*+hMiqxqUMmRBjPwRBTfE3u8>2;@qoWws=PG+T1d4vyr_ zQ^r)TJDXuQDoYVX5hPt&sfj$2`z?e_Mnrv4x*n)MBg{oGwI%OK*YKg=)|X4Q!N#1BG_Yl{p8j|3A83c zkvYqmInN`e037gHHFvnZG`P^Sg{)(!)O@jY+~ry$5mh;T-KNv$NZG`(z3`hLGk=u`9 zz-gXvbGpUF?%+RMf>mk~deF*f?Q0b2KCk&~v`wh;y0Ck|s>Ijc2Y=IL6Qid<0Be}X zJ+2ytkQA@vjUYjoK$1`~3bt6)k`PL*p0Qoz;?1FbO*RZ?qm~CZCtXqnHQDEC55h1ilaB*_pz$($9`rTLYL$xeiA*v$}edg_u)Y?@u-; zDz!hcSmbP^%(}+0ac&-R&Ez0S_iP5J%xO|XX}P6V%6H~QMp^&+G))M*u|~PX#R%*A z=7=wzKF6n@Kg0X)zJa%%JW6v)>6&CIl(Sot7k_b14Qg_Fx2wW!S7cu#UCULCy&KuK zaxWz1d+zHT*eT5cGwk;2zpPnT&$Z{P{($2COH~eg4qUr_wwzJz)8-B(O%1)*4A2TI zFgf|He*1D?oCDCav09*~XKsn0n#H<@Xt@{(hf)^o5O*0QG)0|mg7uqa$)LK%rCRpu zMe=bY4XlTIYsQl$&&y16ZC97WJA_bNR5JstXNUa5g1xeaUwz5j9AzSO(M^&C45PyU z_tYjm=l89d8tRM1f2>&*V~XW%gKUdhmK!`f;(m6&hnye= zCHNCffqEs=JTwfXaiwL?$AFy18s>YPvz@tv2(k}4t3^v6Gc+a+^$`x941>3vWat*CPSU?BiE6a zn&*A%_#|qt7d30Wb={`kbuR~)(LIpkRpE3B%NbZz0Am<%>#asxY`8BI2`i|0NQ$_v_PXyiBY-cVy_7% zGK_3$4RwsAAf8!idMN{Rz#YTRGctr(`!`4jX9>kW#G{6E-rSnsu z?ok_4hh^R>bbDqVKsP_sk>wQG46!;KmZXTRB{v%rRo_~obKYP3-|V98q!)l(aKx)7 zM1I))eVE!7OIXjol;StOw?Wx%9C5sxKm=UumnHb@L10;US=GO@3WV7Kpj0P z-Dce7f+y_Xs=;%5INV3iTcV=3xNif6;d zt%dY|K~$QB_2ZgBn$0_;@`gFH&%O83wcNjd2h6OQU)tD9R%%taO)yrP+jHj!%(+A8 zSazGXnbw8$EH?CLu z@P#`F3;D%4{DyqxEf!gMHWUlVj7UFEz(7N3ZkJ48B&X(R7?utjMbPZn3iwDN6thtV zA#l{GRuPH-C{EBND>#-pY!a%K4Ae)ehI&N4vWL(I1-c-MxwF*enFOGlng@W9?LR{k5uB&CC&(`-{aeZMp+9jaTu^0N5nWQ z2O9R=^9v|uo8J};@(lRxvrD$|YMwQAb@lq3|MBR_7pD>9h%5pYxBhO#Tnl@_8oxT<-#*zAsc#Aq> z&E!%JX0J7<8+|ad$@aqreQTfoEUBSx{+D!?%<>)*e@^}m2%-tjXydZEhdOH_2e-xl zwt6yVZvISU7jVl0pVN1A!&1l}$X5^d8C?;n*CEflcBSlEW`6IO5yHam9#1oV|M@cz z0e|qpn|S}-H^E?B-ySgxLqZ5qvVPkp(qU%GlHB^E&-#XWiv{l|R%FEo9A8 z)?40YeN_U^X3(}u-rnZZUr^Pk$AP53hDMmtgp0NudH>n+8dJYSU+I>G*&T$@RSys8 zE(2HRJIeB_on=MMH(fS=grv((&KW>&geva#`Eh9$t60wkLH?9mb)cliTmX*IfZw_io=Ujqpqlm zWX>YADYJY*5X8?pZ z9zMcu1b+I-r}*ce{0e{Z$Aob}_tRjP)w^{H&uxqybtmpP#(Mjm>d!TGgy7FOe zS$w|NGeItie-6@Qqg0%}K#ei@jl?6Q@9-x~2&caZnDq=_#@vUQR zY-PN;!OYpRY@949sA5?a5>%s5fLK`?iwYqs2UNL$)kp!=4AW9hr$tff|B_BX#dL|A z!hUVUAwOAspGEC31UB~-jf1qyL|X|gt*vdZA>JsU&f2zo3WUy@l&H;XE$p&_AfT1g z3Yn|ScucRc%pfidaLDH=3kDQICTHtS#g?^r92_TBR~IM22ij(d+xE79H`rsf)|eL3 z+|Y8(vXirrV@j89GEUb*+Uv1Z&D22yV)j-UcjJx0P9qu&7@WrzlsZ6zB6dMGZztGC zMtojUyj{Z{aYs__&5SSgWv$tQ`)o#W5$mW3nf<*g6uHy5n_k_GeqEUxHfx`}T9X&R zByz+YV`70P{EH7i#6SJySNQzy2H(AUjDPRDKg3&)UdL~rKEq+R2NB`L?SyYUeuC@c zEdXE~h7`9ImghIlm^oIEiCU7#J{(pCtL1#{`P=yX*c|Vq{$ydm*9tR8o2PYfbld#) zCOd5`6FV$!%LI3J3F@WwIDH*ybqJbhR+aZ^06%I+2f2mj*rnY?S`eA*z#>_Lbpu`W zbG#-GWHd$$D^=CFc~Cb);d%xXehUh?ZSomLHozH*Q}T1YfVP42YV{x|B}p6S6%dq^ z+o_gTGljCvAa2_j!-R4w8%hXrR|bZhjo;DDyQKVtEdPWzMWlo}K-2 zuYCRVH^tt+k$=GUCw$wRErrJIs2$YG0JXo{RXdqUsvoE-Je%0>3( z8Je2fBS>tQFO^(dK-4xh<`Z#VbHnGI39`>%DAHcl=e+*BwRgzLgr)6IM*rx4_<~o? zs_q7sQ5uQ87o?8cYDg~NoI_|4I+=5L4j?Vd5s6Kd7%XwISw;t>%()PjjKd&hf?LH4 zX=YqHrsY4*^ExTOTRjZL)Bqa^Oh>ZQUXtbdMu`&;mD`fbgOKNfSh#qevkn~ny8fou-nNd$1?7=(xm_u`#22z>dn$r81G_I!ZYt zkuez)KTDn|qF4%v1qlK*kg9@)PB5`Gw9&p(sCmb2&MBANS)eMjT2{9Hb!05Lx5>0= zj!7v%l>R=Mou%_MD#ve`F}uikXHlQ9I_FyUPC87hdZan)dzFRF0hJ7OonOq7Zn83b z<_;5<*M1rmprG~x!UE_A3WlZPdV!$l{0O@7)VbwC#bM|B-MXbSN0QcDjQJ$5Y}*`x zIWF0!n5la_^Kvu#^UNfj2z`hqIy8BES!0c;*hToeEvaE9Uz*PS5C%Gd(0hl z^_+{G3!4z+p&~QEN(&_{|II9KZUq~#x4zps+JY5gb4djzIS^Vp%|f6vJ_}S^PhZ;v zp~t3a2ximpjMtg0!*0aW7dN=PJm6x#!)cxoXU1VaU}oU2fBGTjIpW=KypFfue2mM( z4l@Iom*Qg>g0q21zuU8f{ym!RT=;9{C0RKOHW?cYx#O|GXRy7*jmm+$czKWUaeMrif&(tD1^ffZUK&xaCPLW+?(5UZ}fx=l4YX%;7B{bCJw|})}pL?W1XD$1UF9{Zj zRPohX%T|I)y0Mbl9oRs!?t1GQ46gqQ0@Mn^gMdOn2&w+|+_l{B_4>!E9Hz!xu6jLC zlGvtZi>Wlh(LwrGcSGffPYxry0u;DT$~b>fF*{_DEu5*dsPwoHQHS{@8-NFYE}8F< zJ#76w8k$K!w6v~YTa5$hqH30%C8z#CC$xJDNcHE2yO&1J!mf~j{;p5$jGD;`*6We< z-YHy}YQ`&T0E4t_kxk~j`}|Yu$I^8tZXB~j=6aLaSASqkpq3paVN>ANr_h&#ex>I3 z9Q=>Aw|^ly?7V)fCMert&})J>8bvA%Tu@omB`z#o`Te-V&GCpiMqC{(@bKypm>D;x zTii_(9wZW~VE_Oi07*naR9`*Br_a8?|M-i4iH~ky;)jpk!oU5kAK=YLk8yo>C+clh z(&Aou=9)qA3Qn?m{+t7*pWm{T$BDcc#C|kd1)}<SY9tn=~&gzIfL3?q_oO% zsfj-uHSkJoTRRi$XVF5cD9)BYd!ot6~pt$ZCqklw!O?`0D#vVmDPxJ^uR8(~(PVBR(y zr~dl3;`_}p_Tughi+&woD(!dnF)TD|fW9<>5$H};f5IV=BpU9C^ zvp+LyA2xSN(#)?)5RY8bE2miz>q3uq@V&bCX`ZkjcL+q7IpW#P3j_+d*zfV^@(Ta_ zH^0C?{^+N;o=^A>KllND@XiOA<_XVluJMh>ui^R4t=nfb84KmN{xRNS~KP}HJ z2_T5cAr;t-MLLHBQ0BAEsD?SS9T2Q2h@8fZx$XM20)&JapJVD}hi^&lsb9Sw%Sj|n z#GC9K8$7OF4EEv#6;LHi8HKt<*AV19Z|F=SI_Lnw0SM(~Uw+;=3}QpbE^jj$6H~cn z=9z2LC>^^*2)zv{Y=*f>qvvYzg|G>J>rP&`_a!oIzuoOfyv1# z47kD^>(dvAeTFFCu;`r4)I*nBaf5HPZSl`nMK8H2?YfVY*jw1l{>_n>S>MBw_xtOQ zKEs#KuJQJpui<;&eiyrOk<;8w5iv%L!+DIo+hPV^p4y>p6 zy5{h7zF0@g30aW*Y&fIVX-f$exks!~XN{j%pfm@eDJqq!CC7yjS1$qfSyXn5tnAXu z$#F65sUew4v_NA6kVfRZRabhdc?4A!Zepbhh6hAE}FPc{3< zq@psu65&wH(mMOIjapk%Mz|sn`+a9zUpeavFJBq`5O6mlhg{OzCBldeq3b)pT1Hvc z;i|A?r%5Y~FZr1QHZDa5@OCE6^Z6CUXC(W^oumflwGl4r?st;O&GPGL+~|D+Qt=Ep zbLD9EAl=0KN)+ejt~p?n1-HbA5}8XazcZ{EAmUhs-<%$fUecU;#ggxI@RUL(e;dTyn@XsKJ48SHA@qyH?CNmuj} z5!gLeYD!A|$FVlEbtkd?C+p`NJccs!TO5>SgFN-w%^pxDKcHfOH$tb|osHG^vx#RY z3@?@$S%5*&%xn;Mpjk2tn##?K9p=6F8S{bG{HQDkEX4vl7CLB+uvewP)csanM9Tx5 zH@!p!X4{}=cc&9B4m(5!e*Ci!@#4iT-hS&fJa}+{-8f)o#ymw(01o@5NuC)v%`=8U z81x7kM5CH0GtFzU6#muFOqFnxm1q{N2#iJ6N0yLE;=y-%*Y3=c)#Y9T*DDIZt<<;2 z80s*e-aQdnNe2?hprcW0~nv@aP22)bK~afn+>s%RIUL=XI~x<5piX+ z)a$OV$?{ULOPlzwY8R9r|HYRgXFgTT^4B|nEZjX{M z=S%_5vqp_MoBwdb?|SvHy`Ll)E}S(Vt+~QY#G~`wW%d&OJ}750OEiaSf?;MAD{EL} z^r)Gv+?9mi|CuLGKIqB8X#LSx_F@vzBE5%fMNYl;y-hc~_0egg()1ZIX20O!mDkV> zGrQnqR;uKCX-QkvW3PFkCa`5uQcx*a8*^d#^I@HL&zrMRqG?70I@O}tvOlc67K(Fn zjzjZCOBw}@iKvv33EVcK{`BPl(5BScTZ{TJE{>d+YhQIvmV>~+@@ekhrBYg1WE$oLKn1R=?9^rVp z!?W9$c<1pOcyYY01e_Rg@YXW-&QrL^e)~{vVksMH99rLkRU^io0Naf$SvDwMWJkwq zGL4y8#aXR_%JhnRaVHj&jpqh8Gu)P>vtWib$%B)juDuHsaC4?1)oL7Ii`co-H{+L2 zUo64oBL8h12lTv50i+pCLKvvz<1Vt|h1QO8FXonE@a)?gW3wK3OZNJ-pL~vr4@oS= zB28Z2Q(Uk1Ssy5tNzd$MOpa?D6O)jOsWWe&Z0e}#d~%~?m^&K-#k|q(Qul8+i<1ue zYvS0NIm>yTadFsTnj?Pt^N;cJ`iS@6djs!&;|b=->3?xqYr~MkPY@%E`4l0VzvrwD zyYxV32;X%=Hgi->TNVUr z7Lt)1>G)jb_^d98F>)o6MXNagY2A-NHj6!id!$f)XfYs^Ok?a}oYe59w$2O;m37IW zX0CGxp+2vS6w$=#xueWcE2B6rOo`4agd6!|wW9jN=-m`J| zC8y^1^G;1Ope`}B&e;xAc-HR01l5sI2ifgJS#Ylqy0`5q^%)MqB&>cyn{CFr>}99$ zlNY^71?86J<))Ewm|qdSLMh|gf;UTTnt&t~Wd(OsVvTH|(il7OCU6+CGYj03Pp*ue za*SE!otecsEEFW1OrmTPL+QDQjU1ZGzb{|!qo(ZnOOTfd>@-6}>rIMLEA`efmG?nS zoKFwUC(k~^TNhXO55D_1@vXPs!8Auazj=Y* zzj%g$0uH-99v&`nb39fiI=`RDI|5cb8#73)w2b^2 zVD1@5deo7_&@cNb8S>7N$M^7-Gb?0|vtWi9f?&~dFq)l>z1Y#-X3hQmPSlQT{nOey zudu$uI?HP<6ldej&0Vt?<~gpOALDLNS9@`c6sR)=xA83pa9D4GFH-bPuMQ4fiFBG_ z9)~LF3NnwaHt=lcU6eF2b`H~If;Drc@Rip|igqBQprw^r#Lj_SG1IjHvi0V7D>#YN zmg7T9p2Y$;U!xm_{cga^+dF*t@$V4=;Rk>8K6bkSx5qnN9QF$v6NUnoc z#$7xlEO@}m3wh%2b2#jE{9_X}=-k&n18m3#=bDn+Q5fSAbmr+-*8k>#pCp*`BhRH7X_AdCdIJE}mbrzTv)M@|(>OYIbXdQz^6U?=63e*;v zRRX>3IP3zF%y~d}Jwlm*NH(5Ge_fEX!rD?hYglhvOyNMt;Bk}{2*LOQWm zSmHRCBc|k}6JsoUxg;UwjAMaa#5jUIPszDF%+_+fL4&my25&1V7*RD_I_sG{n4n4> z%W@%Oz($QXK;f#WW;Zwc-YWoTtumIJ#%SB=p!r%p=Z&mn!8N06GFuaVaqU{^+uhkP zLDZ_!8gvaUo5u|ZPVhWjNO1Q-0v0j`4b|b5}YFW=-owvsQz?H)jS8%S6;t;BE( zHos#fECEinK3L1`TXeX-$TZK`k0XBayASby|Mr)dIpXiU^DX@Fy>DX-1D@YplZlecXbw-9B{)nex504@A1ZVfHd`RhCV0Ksq=cHEYu<* z=I8KQyb`yxh+<$p<{rWX^c;mAz1qLa`lcx4c|`~<*HoFi<})^Z$LXB(Zlr%EyYuvl zX>W)kA2OArLL-GBhJqL4kNs{3Ift%i%9pno6H*_U!Sh_@HaW&8+jpysD@y2GXa@dS zaGl57Nz6p(TtEh6Hx7zSvCs89&jl%>bmHf^Um{?)8@1&+L}5rk7}gjW62=lC6;+|2 zuBE}2=Vh)}((l&Bn>qyKCa3SPV{!wxvDDN7U?X|)eLgeUT8dOwE*GOOuaEfM=g)9) z*yD{S53$?r5CS3WMjTHQn3sD%NRchHSj#hj(=;Otp;fpDwv2kl2Ad$k`N^w%pW2T7 zKA6!1+H@0d=c;J68?p5?nW$&<1GHO1ifmY|A0fp;nOkyh0d~e-lGo4jJ8QBzq8Y!>YoYq#&^a5HMyh-JJ^M-w z|BE}|bZ73X3ISGTtnFGb!Mu$teNnfEa=!u1mM+&RxbSGN$skdHk|@5IbyhBWz&xFs zNh6x&1`O!Ca~6MSVhj{YBPnM^OD__r)6-a^G*U)N(wvD%YPW*eqxoK=HkpYnU>&!B z|Ms!W>)v^WHJf8xlsD2?uy{o*CZzxSPyZ6Xc>V?6y?TVd{oZ%*&f_PTW5mnj4VW1Z z4i|_q;^uURHy=L6^V_8^5TTGbRoOWt-AfV4b+X36o#1vQTuWOyP8Yn4-F$%76?W!{;TR zB*iS`{sASEuPP(4LIhyY8=vJ)*o#|-iq{zBMv*tEIW@x62%r{3n`11SUDBMf!X zv4_BJJU>A(<5?n3WzKCJhs1=`zt7i*s=Jsl>B_IO9A?ujDqXS-V%kiZ7CB%k7VA|H z))a!I7?)BGA{VJ3At|9^zaK$@_OZN zj9m!mdOA=&rwU`E8iOoDu1%es@h7W-Vm<$tC5($l$c&gkv3$13`cV1nP`+a!sYr}^ zN=7p|z$^)XG0;%n2d8~OE6l`-S32lyf-sj_E~uHkHg=3u)0OgoEu-e_A>3iv2!zu# z;jr5ya>VWF4nqidaB&GD;N|Tt4!a#DE#p8Zyh-oA1 z(IXqAs%aJtlZ*+(T3;v$7PrA-0gUJS9cptHV>O2J>XgVPza7{#)sw`OB+a<(^z{Cd z+E(V|$C1tl@%(eS+Ps`^X%~y^c@spfOd>PhP--wXV2c6VV|85TCOS`ygLPwo(g;J0 zy?~xnf1@=}73vUK507BI>61X@vzA)sMS_57;<8XepeCF>Bm^&rIHY9{BM=R)U3U(p z=kl6fVMAIC+9{Gk2qK!s8cc9%uu$d7i6JgANIYw*oHdFx3`PQ;n*b7CiQ7g&6-(3<1Az$B z9Dx{@pP!oI#J0HA_2M@2X+l?s~IR&><0K#&Ab*O{hl>$f= zMdZZ+irLE;^Fg{?>v1=iHg-TtgM8@6NV;9a~f}5#a=}6m9L0^X?TG@TJ6(zOLY4=TU%*&9dHnp_b zsb&T986^E2`uXd zj^O~S6>w!lgzUOcJtoxirl3|+s4gp4!=$7mrmUg%F&0+#qv|v+78!6LO++TDDlmjZ_I{_RGk`OhcTo=woSX z%!bJPIcDLHh*lJKj=s{b_=ZD*5I4-Y2jS zo^`qx2UE~)wN6OS;EKP~OE_iANrNNm==9-h_(^WWN#b3>Ay#PTkaC(>$xtg|I!&`+ z9i@%`^1)ubyeX2E5SG}eVkQ$71|apC%fAK9Fz@$cLFFm{0991Ykuk-kVOqX_DAMgI zGR8m{h5=WX1EzV#Z$A7zu5XX{-nYJiH{W=?6z2roofgN{VW@^W(===Jm2^owP@5&j zLWV__O&iioXkv$B{ zOHx||H_d|Q>U!p;V#W6vB_#!J^>UV8r5Sbt;8fLh#wO|w$Zut|N?ixWC$a$inRzr1DL*s=(}1M4QE?&FVaFpCE?<@4KfqqYO-{ zsI~6k%z8FV?fF(PV_ZkUwW;wGUhG{D8&AjioaYAPl=VytE_LLO5-w3o*@C3(?8Dm8 zV`(x%Ykf#9mS`0zoMvuyWLjMas!)cEbK#i4n2`54B#UG1}~0ubt*GQaOlY&-S8+~RG^qjQhh(?;A2WPX*1F#vdZdxH=H1`4>F zC(QENs|@RzkS*A5gojrL(aci;JciUv55tI?(;Y4kd%USPz2yegj2tWMRAAkW&b3{xs z86sM~Z=fe=mu!6{*VGNA0bkod$DuWwBC%OTmUEOWzMPlAE(EvVY-tCyjC?)ADd#O zIBCFE#|y?js)5KUNJTR!K-d~-L?zBpzYj5n(?oDopW5<1<3e~WM(lI^y>3h=&(fnB%-&d^n;uu%kR%0Rrswn4W;* zGHPgTg8cC|2XqtTn(o%bcBV=LbInKt_Z{Tf14-5{AG0#GWKV2G+|MD-GI~oeHu)}T zBm^zUrSVJS!C*|wnp&I9HYwD&Ef~R0jY}I0c?$rz#BiQ)aY}ia{I4As919jod^Qk{TKNAy9dZ z&ajfNPiCJCV*u`Oo|MO=*#nWdu_@i%s1N+?`KmXX{zZ!EjOTqFwej(O>FpYh7+z+N{wuLg-qUF+Ntz+}% zy*+MvbY;Agi{1LR)7&5O3~e>qi_KEabK_J`93_zo?VDi7Bn+!IMj8Z?hBij)$}uZy ztZ@(N`Ff(-Y!v^92o4F%lxGl;_HI{HF4z2CFC8nGq$eVb!keLu#sXqGpb2dpQiE1F zW=&)Zr`)trhZ)^`Myc8KdYJ>Rgphj*tP0pS|0Y>KGiRz_L*}QFzj<&c!UVmnPrm`< zPGlk5rnv*O!a3`tI-n8#D}ztTn6o%bM58I#^$f@o%k?X2G*y4c(tNNBK-6<7M%IlV zwW_}SXO7ZvN~#&i#KP#Un}Sx4fLkWL1(7t7u&k@7*Sm?h)DquXFP70YH3n7Z+J~<5 zT=!6iKX~t(ZUqV;iWt)v-Dx`EG|xEfc6j{Y5&rtOzs8?`_%nQR`w|~qy@tR0{&(@- z>u;8s&@Wy-!+zZ1;o%Z8rD3^D-Q0BdS)xCO>LC`zd%60tQ*&!l-oJ} zJ<}qRImBfgMM}!#=#yZ%slA-u{-}-?@_^PAW#E>{689DlH$ItkM*Y1sz-{-d4JH(J zkT=%YyeReTW}NfEvfGXI??UIkIrrA0EL_V7QCZ0c6YwJbC>|=+WQ0 z$Dy1l**u!mcp)ICKshyM<1^VrGl<8ODg#7K$14Da2B4ITv4B||7Cbl^sP?}Z+K_~r ze!+Pr`5XjnW8thZz+ORFnpXoqwa=AzRIyXjn#k6%k=TgFh{PRW_z$z5ZA%4Wl6cidVnNoK ztAi(rn^av@mlupNf>)e?XrLVUSesS_h-qg<7CBm#L1y2GC_Vhq&TGc%fvaFjh2-$#D2fSYPxk;-$dp(yWk)TCq|HwXNoU;_}~ij9C4Z!Da8;5?Dr!g zGp5soU;OG*JbrwICyyUu*ah4jC+v0u#&L&Xj=0$E7qCNB7+egy763wSVpQhD34|B7 zr^s6{YZquKR&!l<%Pgt8UY7tjgG|Ow4t%eV2>rHu6$+V3qb|10& zz0u5x<+T&?SO+@MbS#Zv41?jOX^RHTfL;VfKv-hsa(-=kFM(yT2MpBbrJ8~fo}}o! zuw-I~-8PYG)+}iueJGDlHhmEZSsTeioZ5Ou*M+id4|>xz4{u}*3M`(9wGoufuWULg zb-y8m)*vK#B@hQ?D<)lj^ZJQFfXZ1S7HZ3x-S?$jvOY>wJSB=`)r4&h)HcmTXxQus znUyS>UNsonkpjG`h{L)CZRAz5a&q?PX6?TfmwpHT@hD9|3u0^lPNqeT@kPBP(~w6UD|i%HweF#+F% zA$gEgKbYDiNI5b&;3y@6Bs-K-5FRm7fycG^EfR3ytD+3An49z~F0=j6&97nO@>yNS z0jMEsvP-R0*AcA|QPe&n_Ds3Q=)vXg0Oq9*+zlfxc6&U#d5Qn`!(ZT^fAI=Q2XDL$X2$2wpCdA3KaLnez?DILwZoV>f zOVd>IoQjovV(i$N!{YRp%YGd+!f76eI7cNzu1?Bn0}9YlB+N=ux*ta+AC_}Dd6s$Y zCWeg%YM!Nx$jtY-MZR`Cp61EaqSQzb;Yh#oAICBHI$Rc#JZIi2%NLKgQvo`LFk&17 zj>i)|fBF(%eEAZuJ-WbytBZwco)=K#`uYgqh^wm$+})j)IXC9gpaKM*VksbH$;wH3 znAynvZfG7ZiH;9x#x5dao&fMvX1E!Q&%b!CnH=eoO~B!>ONH+u&mY2o5URWCxEu9| zIn|uJ%C@mTLwBx3ka~q-&c1}uUT6~?-LJ?XY(A4db%O-3?o50hol2HT_87c#nOQ#P z0{RA9y!8UB&9(O?Od%_ttIcnxzKd}ySn+auqblM;sC+4UY@IVt&jxr@3*5aZXULHx zE87#bkkb%LZCfulkvcAsTRvyJzLr#E3}myb*A_{jDaXrU@id60QqQu`$m|ypq5db& zv#!gUL_5+fJ?R*hmJ#-AO3B2l=hG3Suyfv00|HW$Ds!(rZ(~il5R5e{HT+aTq}$?8X77 zIN@%ZaJ4_+Fz#`Ece`$?$jt%jx-zi}p>at&Z#5-JPiPSt88Z~8 zSrr$Bnl54Ct)4=@hnNc1YA_+Q`j#1F_G;u@XvDVoBL|@9J;S_#W-W4-+8hqUVs16f zagkRu@aW+KTwmXU0bE|}@yp-*4xfGg0`I>481H=J_2Q(Ong0A)7k1XU=i<;_W}x*O zZ)S{Sb3wK^GX`kf%0pGu0?t6##rNl!sZvBza`w&)&2C7t?U1sONjPy+l6n$`feXXE zTqo9Qj1I#x&vUtkI-|4w9+FJ|icNIGps13VRrvd9hK`_ejMP@2GBbQ(H{TGlM6OEr z^!v1ZCM#5xf^1yY5I$C@{`%;REQ(VOwbN*1#y+Fa(y%FlooBUeI5u;rl_Pt-@WHs? zlbZ!dioKSsPB(DCOP5+Xjj2cAmt>_*vE#X5qc4!KYK`?tqWU+pQ}r;{J^1EsLXKyl zusqu&X)O#_I|Hr$-D|=nqmf2lNU*1wO(Y^?o~hlQBYP$&wYi(K7GK;K79eVj zEvw3%%n3GZv>j*G?x{&(Ku-9TSAdEQg5A$jiz_?#oUdIO5o-3Ed^U&TD?;hd$GcNy zC}reedXdFrgM9*xsGQ$Y-6C1~DA@KAe~4uiYL2nSGs-jbUcM8oB-Jt2S|lsOSnTEM z3|WI>(qHDJT01ggm13>Y=<)&s>Cu>Gd$T#fWWXj`S92{YkXmD&=A}v94Fj$Ym$*G0 z@%n>D`0V*p{Ler8OMG(k0)P6}H}PlR`91~;xVby(wMIIJI?#GPtzC;v{SvuryQgb<2w~K~|;5mzB5PcpdM&{dg%LUFXDbp=jVe z_OBqr);TvtJ}onRyupf!8L6}emx8#m^7C!1fC*$4^YXI>i2P-d=^RqQvfJ&-*9PPw zJRh=*-7s{)3kGAz*@KeRM?&E_B7{K|gk<6v$hb0>5U(U@TRhT8IFb4zN9^ff%=1Sw zSU$nJ^UTViB@m^2NPxNJrOV8@E*m}LKSXirs zw5EP$10nZ6_&&$$*`Jm99$N{Ww{Ky?@XBSCO(dtfTI4!@u6L6jV@qe;LyD4V1) z1krDqosF{%6P7G&NKT*JLA5v;c~P2Q$rU@NC^GJ9eA0f{$UUjFG&$hb(||L8Vi_b-Ot;P4mK`7wV+g`URK;v8-x6aD zb0=z3`j{N?wovM22C5j@k#5cvfFdP~Mn*?+7Ypt6Q|{1I8QP|{>J0C@%O&>$Jh-cZcj&C9WHU4PYZxf!k|%Q zoNqK5n;4M^03?rPUGI0?V<8*UGPNdug&-ld&)S2*rYq>8Tj}rm>jk)CdMCpiD&^$b-5uA<&0x{`+jqFq^|C&MEST8G!== zabclqBSck>(vH)LgGlBu%Hl~kg)Bnm2g6`ncFPtZrC_7&#hE2wCA)HtMe-LyKui*) zpa1d`JbQkF*B)Kq{rBEP2!xy4JB;Ii$Wb(Obs9DwLV?6VC{5IOV=^-vV>oiXo^*to zcq3|@xyuGn02%`Ko;Wk_Kt#ns8Ne8Vdl1z3M`&UE+Evff9LwAMPpVR9M_FQlX=Zj=57`vUpV2k~WwzM) zMk4AkV1CRJt6ZpY7CY$$7C7z)>*yo?b4G0s;O7^&~1JtXs`i5h5>;Hx2HSYO%onp zKEx0RKl=4g@qc{$3+%#xfA@p$<8QwI9ZYd9v%ZlThusd7lCbN&Y_(foZCV&c|ESE8 zx@Z77fE@|<@mzLL1wE+QpAC3e;`Ibb$t&DPX&&#{!8+4dG9IvN@Wm_F*W`fPx5RxJ zo;%J_x0yvn#OEoy*YQ4QWVRbG*_E1aG}~PRM&9K4*thd){+$4f8ueJA!A&L*H8YY` z;yI@w!eY!+4o4l(uCkD^xM)i085Nf>F7;2K#bHn`4rYDJbAyKfQ=l#ndW^9+Wl2^z zGKyocJw%eH6XFbnp*FuNu}HWbgM1V>qot}~P_m-(uF8W;)+A%_9w-rk5`w?@xQv)# zcNoEuaeF*rKHg!sAFgl_p|0wy*9NKPo0kdG4SX{H#{S{z5eI^-b z5(oX#Fkf68lzCVPq0V^&IP6DL5EzDeMuV2i88cGZkLG#H&lFJ`7{dAAJ@cu$yGFM1 zZ{Bpu&Y)ItA4b%%Go1XMMFfVD-IgAm?=R+Izkm*UV`(HrWadMt6JJ-_^rWV-nT_Y7 z(Ons-lR(F${ri~Oc<8SxJ9Fz8^pgf_!4!OUntf+{azF~jBC`ZPQ01cpCG~IfAfqV= zM3W<)O{qR{-DIT-#M2bDjGwH?b6pm6w zjC>LK;E624iTp7OK+GP?Gk}bnGNEDPDv;c&P_%KJXvtL`G|t-hg0ZsK9vGyIuPQlX z0eSy?L&F|BfvI-wIAr6~moIR!+vC00pWxGH&+zBJ{A>LD#peJ4|H=3N6z{zDI;MHT z^P6kz#t{#82TXCsX`0;3ddQbkI!!KaT#@q+|05PF<83$TH_jJNHMDzDJ~A83q>D>CGJBqo%z3` zacpiVh>W78Zw*G?#*pl%X0P*{?>H|IyN};CFJIo`_ISd#-hTsczx@~l z;CMV?zgwD~X_^s+fDj|1xIg(G&M_+KcKhjsgoSibmu0a*{3V+kO{9x=Yf?kv>&r1{ z4s#_#w3kYJZC>xuz;Iy*;#*V9l4v;Rd9D&)sT?7~FaXmuiT5rV)$|(kG#3Upgk(}C z8q~bL)TF-HB*}az7T`#h_qQSB^_hD5{JO}yrTmrz)4cD9;TBjtD<16Q?&EdGl zByU#T(iYU(`I1%dP-B0^xbMllR=9WAEib>3)_|w}`Plx9E&Jy)0-Y$E?8*1(C|PJD z;;dnY@*Ze(OBt{^R~5@#o*NA?`B7;sWB;H7cUHk3@&OojC<^YnCUYy-W8LkOiqFuI zQjFT5$sfzYyrhD|r2G#0PY)O8Ui}t@85Z5AT0@KG8RBS*(fn$U8#Shn1AGSO6TQid2=g?&5UTu5C7aI`97}b?AJq4cVdX&0P zh){8ebE6rn5VJe-d}^)}JD{BOv9U8UXr1Rh{wXr!&4-UMgn*y@?zi~IzxoM2x_ORo zKX{CP@V!6A)&79rzj%h%t{&jY!`JZi<#XKPhzEyD98Y(P$tU+(Cvry$))Vr$;Su~f z({}Il_i-wI0w!U#HpQ&36*)(%ua+RKK2=DWKs8p)y8M4&ZjG`1%f+i_sRVAiN)&dC z_MnMIfB)u^9^vZ$wWh$)w{o>s8qj%=lH{U0J?*O~%-qxsc6ZL98)(Ql)b$T7j4 z8(*#Mn_~?WEm4*?~eA!>Jj2<)KY!ux$1ItP!qahEx~^ zJ9o4EI-O3S5O8r==2@S=xWUWoTbxc2AAIvo>~;fA(+NWu@Wzve`108`KKuMjy#D$F zoTjPVcM|Mqc5(N@RoxNN$2xBQ=?Piy+KweWl?wMs~8B>#DL4ka| zO8UOV1(RCSp!9}@EU_nQQEEy7vL=|Exo!~SFcwMwJV%^jTx4l!jRl0#=n`R>1t#Id z?(7>D_*)AeLaL zsW{JuwoWSN7vb_Su2z=3F?+J)bV~-xu<3WQB^O4T5R)njKPcUrU ziUiwI?;eOP@x91bb>PT7oO~YZ{v5BF*X|t5TT>uT&DlUFHoDG@yCSrOlyT=~+XQ=z zx{Gj=B?Q{JYgPPL0J0oV5K{D}0QDumzKIsQb(>x1EWuha_phD^Q(W0m3;VOIGpVVF1eiPtDD|I8Hxc3mSuVi4S2`*rrG`}*00708uL9nyj%FcDOjWWhY z1eYn~R9AE|L6#jXK^s0@ZS{aTfbV#Qm*eCrqn?8XuQ>Z4!bfBDT{V~P>~{vUiFf9w74ly7_M z(G|YDxx>?!&++i$3diXVpTB&D*RLMpZaN7B68<6)oDND7 zVqzdJy7VwdAfTEk#m2X<%qh&QXH3+%S?Ty=inz${kH!2YV;l!ei2)@-nrlsRpqar# zp0UiXugkR=mZ}|ysPbB2Y?6`IVIU)O)&mF}HNYWf7L+5cC?IRIpqay3poOw|M2_M? zWKjNj>32>n_F=yRGw|uBPx0yJFK~6S!~5TS1N+^Go7*G6gcF_c^5qeSiyba64tVk6 z2KywR&fQi%(3lAvV+l%@ETVMoV4(_#Vpb!tXxkZyd)0GpSpwBEN{&suQ4r+!6X&QH zLNdE=oS_*6^P~@95MWvBwJGJp0Ar3K2Rq9eX}MO}^*pb6@M26Ri;i&&SSEhW^(*X^ zFzzq&fX2l#H`^raLi3FY**Jt!j7LlpIKrBTI!qF{JUjUrzc^f!ca(vWY=jp%M|0G0 z^%yyUQOnK0oldH-EC3H{2w2Vpar$=xV?PW|&0i0?plrgSns5fNAM31l2mxUTDwbw9 z4CT7t+eVT+PC`qP|yYJbk_$c*4>24mwnQH5*%Jz#MH@OX;*U_i9q38cNMX>hDlIIXzO z@=6tr)f8f1QJ`wR&@w4h>!ZwsaWU#74Zp}d-Nx~jd`zX%_6TP?K0BMyjCP>&?TgsolFJ`0F6TEl##W(-VFGp z5<5K1gw9vp?8g!R{a^kFfBofW_~zv!{M|qJL%jFoO`N6~$LWZ}xWh+Zyu@L*!{e(5 zOT!Dsql-(N=1F`uZ01~VZff)jf(LT3zVj7F&SOc+P>|bAlc!bWgb<~Igy#8s|dz2QyW!7CtYAe z2sqtMnCBUX{SILan5P+YWE}Rpg@ud}Ap{ILS2sHb&s+}XIWcE)L(MalCG(NFe2#3c zGmIfMI$W~qvSHS47?RYtvV}QkZ!yJ+?NcEw#F;USi;=+`V+l?V!g4>Rzb^su2~`Y( zHb{YqZ$fIAj;9HTjBz)BK)5?i;21IN2Jp+Sk=)r5)- z!JhYrxKem-hq7lEXf{$|7XQv-#=0`&RNi0CD!vofHiH>sYOq6M-a`n*>6?hK-;Kp- zdK`zec9-Y3es5*GgUs_x`&W^Ig@Pxc(bXBpq0nJk?L?)mTD;2{Q=VlMEl_@E1&)tbVHF^sUTN+gU91~YTY z8YaPh+~YJ)i$J;i_fvwwzfJ$N1e z+S}j6yN{n>H|`K)Ox`$ANQl;eS;ZE(+@8$d<}HN?Z=EQuc4J*(*8dektBog7A5a&p zKa-7ZSVSWPY#KzZ&xp?Tl_tWx+1yujpsFoc`wh8X*?N?xO*Vnqqvih9KuPMe@;cS) zpA!-ov$CZOf^thjsU!u53}9v-Oq5Y=TYkXld&W4&;`|C;-q2$5M6KaxI!R{4dS7E@ z@nUKa$07IW@UpYcp@WW`FR|GVW`DkZe}=renw)B6ACyEK?6i(AX%_g5r|1HO^H3u*~r5X zlGARz7x@X24O!2(Y?|ifeS&d!oDhbk@TPG912{a`;pvwz@ch{|_PY^}A6?<%Vy~oT zx$IT$*D*i9l$wb2(IN{e?{eQ{v6LG}o1x57;w;B7@_eW;%g|*ADVeC=+-d{H4O*89 z7^8&YEuD^eq8F86M4VC~AAo@dAS`R@?lf(npDav6Sk0$!bXKUkZ&2O4{lb*znjLAeg0JzPRl^fz|PMbQhak@Ptla71#Mzx?HhSBsEtQzbg z?N51K)_7`4bL?{&i3l+y&?FUiIr7V(q9wna7%yJlmL@QREio=d{g4dBq`WTYKj)|6 z@~~GXgVJaa%-HYug`knM)OlUU>?&?PS93Qy?yf2 z9SdX7&ue70r|yTLvYOOehg7x{mPr6WxxZwd`aN-g2Kv@{Uch>GZHriB8jFg^JMb# z65WH&>F}S#=ZXOCqs4UodsDyIvUu9*o0{C(Ek?c?YO=a|@=RXh={w)w z7M?8wobP!+KrH)CuB!?tP1F!DJyWm(SIfCtkP*R{HPJCHCR(}A(g3h{kd)!6suauh zZ;3_gG}EvTYpqd87?H7120XiYiQO=i)Qtr+-6O^sH+M%oytu>^XFR{T#>2xUcDoUC zjQG*7eu97U>4*6H@Bbm5Ts^=W4_?D=7z+^5vR!Oc0Co-2`V+(JA_!NBaW9?EtNuEd zB0kea*bZs4H?!JXyPi>*11|8&mP4LKNGI)ZmwKm8t>=%i>aAN;p{7U!CCNEe`R=c8M|@7Duge0|pMNu;aKiT{-hv&5#NwRsi_&#Y<&!W1YN1xGsO6bEbj1 zSG@7)fdUl@Q)@pn7jh%>`bRbFhAa?5i!w zjPx)><7v&lxXPMY1AL7`I~oegs79z+?`1MM^s*PJc{s?OH|1jDq=BU6jXXG9U8;@SYi<@gOfFFPS8~pi)Kf|->4*&4me*@oo>zx$!N0@mo zO309kz=gPB=lyz1K>J7V8WY-`%l$giZ^`AMnKl|*S4fj&{>}b;?wqsSTb-XO3M7-} z+GwrTS@CTbdM>pyuL9Qh+54zROu}&kr}`ew8ujAN*e)40j5}>)FE*`b1v>!keesu( z#GrnbDxFR=rXEl-cF9=U2o= zm~Jgy)AHV8;4*_tO&n?c*;S&6GFL;1H47rP%V*cP zy*=T{lLvU?$s>$Iz$rBZAyAQzTGW=^5kg1G#wblZ8Ho^)-)btBh^2Ks#QKPJz9LxH z$n&H(&HlCmY_&^Bqi`7*DtW(o<~z@b0xAZqY?eiFz(9zI9BQ-eA&QFf$Pzph$rHyc z+m9)~IwF*uEF+giyO|`KJ zJWE}xlo*mwh%}%H7j;GkfRs#nuInH_e-hXV)#5WXR=H^4>TsR@I@)L)G5-&sIIj1O zwKj5g^*IaW0<)KQO=1?fHt5u#Zob=U zkBp=Su;EUp{&f`NotI)#v!BY`Q|69x%A#i%$uSPtvcRloy3A%RQGC4xBCdXJ;q z)YY#db1sr;=IaQYY|iUzwE%1fZC32JZi3V1BDgMazkALCcgpW^#Z-ol@J z@Ljz5=mEZXaf1n2l@cqMC>w6cN;grGUhX-sYbU-^PQCqVYM*`4@KDdhY84Fk*O}U@ z+23!sn@~3XlWb}aT}`n4KJWE)O924C|uji{96khxH_` z{=3vVl{C>`y)*Uxdo@{+L+4hH`eBYLV|O`VgVt7aWiBC#PLhsJ*-bTaiKLa%Du{zZ z4xy_eXI?T+!9W;kkw(w500cM$aGDp*uEEUJ(K%m;C|y$-I^KnZi(q0mVq^r8O-@BP z=Q%DhI7#lC=U^*4F3`}_95Dcz6Pp-OvM7gPP*PY5%go@?h@$+wQlJN5V8U*!4Hiik z;q+@50*-f!Q}cc|fSGW2cf!rh9gepry!-AGJiIy}gn;XtBbXT%hdsgoobHwx$6_89 zS&=V9lwx|s06wDXwdO2w4bYJXiJ`6ye0>7gAh+d!Ek;ZAy5h9^;o!J-3rMRRCl7dMalAo|)z>pt*`UCh&Nr1kJ z0mByjhXg@_A<-tRU5Kyc?s6Zqvok&2Rdw&pjELhu;&tMYS=GI?8`$mX?y6h&W=2Mw zbAB%uhaO?Q}2o$5lLQo~quE508zN0(9R?D^BnITleAUNjFxowMoaB-NG1dr+9P zxvi-{CqWQsDerLVA6n*ddi{vn>o33bNyUo zYIdRk3&&D9P`ZD(RL}&esEzW*utPn3y&B6D{K^bZ%7LzaoPXC8J8~v$)rK-$bxUvoA?i&Ffc1$M!+# zOB)ot=WblwBBeZlCN_#u9#jk5=D!V0n|QnCY9o%)b6;*tai-20v$Wx?-1T#^wvf4& z8?NXd^h=t6J@LM}YeUTpm1@3$NM|U?4Xs-{bsAyjdaYF`B2)EFJ3qaVOIlrAy(dDB zc3cxaSFC@Mv1nR5icnZhGGesx?od@w+y+ZQnBBMRdFq%kR7plU^pG}CqYkK#91l_z zo3BIjs(}u_ckt_BnsJzBc=lMG#g`BFP((P*2izQHJX|jLv!DDme(?FvVcFw9|BdhA zw?FwNz`##_^#xAzA+pYN25Fr4U={v5AlQDOVQ8N->U=WZn@BO#gg&z&<`1mfo>j*Q zV<>RfJMPst<06dbMjE*mk_1*H3{vHYA>t zuz)^+hqeX}{Oj!9*jLK&MjUnXbtW6;#P{P}FiuK^DkTv#mx;PKf>8iUaoEMGwQ_e9 zF$qa4%H(VF31&E#jHEa;mjKvDR5-qY$(T$Tq$V$xee3I1zyDP1O7tSTw{dCUIHD(c zcG2bqBbqt*a76foASuA8W&>reCNjRIXUAM+QbIo~#PU+l92|FjzR4k@7@5uo1O&`X z3Px*K>+t5Q2VAZe#%O%&Td#3*I)=@6S+l#X^Wnm2rE*c2kk>La2uOTAW#mq_s*Ua# zNHjP=5-q-t4dEPUF7qJE1cil3O7kQnExKpyg|7tTiLDKEWKqb{rWDq>$^&w@QPS(4;6uxe3ZNYcY%|uINokLo6C1YGb5(z1F6qGK3LK zDfD=gH4uK5V<#9T@%MzPCaQfqEyJFUjCUM|8)`w4T%bII++@E>fGj+ZDrP;0=BUz8 zC+)9zvr6kS{O~nxmYIe7sgRgZq2aEpeIka+i$kh5I`Yv!p~Q}eHqD1W&(oBlfB9^s zVHf*?Fqgf+=V905WkEI9B~``3nPjpiWFNArNe!}6Dd$>8Mo@Cu4n<2cC2T3+Icq$5 zMo)nUKyWc?NZXRfKTQ!(dCAfJmzQU}tH=%p0>0RCwNY(KpKBZ8wlnzIta| z(<&)wqY2(Yn%k{YYPzW#vT>7d+I-?}kh1Zk2qLF4UBVGEw?#{(I>qAIy&mr)UBcu@ zHa{#<8(5-P=$kX-`l!`5i-Agmlvni|^{a@w`$t1cV}Jh(jX51EOyhpBMmw7*Oo6)U z=z_#1!j`jyZq}t+`3w9$0t!GH~|7m!o zz2>?VmG11J)u6EB*i^ODnEuRzIT?3fGGf^9=<)~zuzEhZ{=&@}grr#S5)`;WjCsKR zY4?OC&z%}JfLJ^Oa>)kULCl_Wk`r%y<<9bXj_dfK!5sy}jQRm%ZE!PII=c^4XGyNb zMFWs1(8&}=u1`{cvy{z}WUzE$V+6xv(QZL zxH}ISP4ooN#ffBdE)*wLdB(GgaZ6$sWmXx%$Q!$&YQNOk7^gfCS_yv0+rq3-Mb4OFIP-Pr|7cXz{=FJ0`J&e(q=a7W8tQHKwn5eNvZ;rxo zx{&QP2w*6I^C36X{T|lDdN&!6od&5oeB^qP65jH#D^i(iPQ&D$jc{=8L&l|Z`KW!( z#zrjiYa^wNeK855!89(1t*Zh6MSMzBQ&>xXP>S3z0N8hljD+UlgN9i#NVqTl!)CI9IIzM+x zQ8pG!)aGe2b;H=4l=Eb2+PyHM0|Dpj8g$8|LSwEd#@$ zz#*T<^tElXWkJ`DF8Y(RW3~dm(*G&^6_GCP!2csGlMSY#OJ1fb4*3_Fua` zh?+*OyN-T4YrC!TMjkO}=H=WEdO+0G*37jb4L8p#^8iSf;04p5%fOhH&{C7RhKT-7 z&~pJYjrKc}L=4kHI#}?F$ zCS~2CEb{Xv+W2d5N5!F{_awPE;-ZacD06gOj?}klHrWu1nZx@I0=Cki2#s2pcsmQzd)&5^Yh;e*%J`w1Qo1L3adQEd4=6gu}UTEmRZ}OUiS4t3^+v zV&fnu%87Pck1-F&32)y%;xPCKjXTg?W2Z zl#_U_C-&+4rpe@(DvpOwK%N0+2V->9x`)l$bKOjP(Rd|fEj0~9#ie-bY3J;K>(PQw zj(tXfX|NH1fYsW~<_h2n!5^Vrn(F2?|mRhToHKh;&W4qLKD#u=Y5nga2^73WHqGJv+LrD z!CWp{N)cm>9>iC0-j~^nLZT^DlGz>!jxr_cVze<_t*aytVIZjr3q<^EsShkDOf-z* zv{rqVw{T=&lp&gF3ex0m!J_ zcSz|hNcP@slWpj`^COH_cyYYJ{pAt=`iFmkfA{4ta54t})i=J2Km7J@;nmG8eszDw zY$h->F4rrrcEzV3y~cUDR>NSURM!7m??nCZT(9(ISm2*PfaMlZ1>QcVDc$O7sCz0m zs{s0t=C6*%+zs*>3=o)kw+Uk&J&S_@_g;=Jjj`N%FWE68d!PK|cI)4}p8S3zj#2gq zQ#}i>V_~tIiCk?!dkmeyA9)ZfF4sK$(|`H{PQ`bkoTadGQiz6B5q;Gf#^_A(3{9M` zY5*VIk%#= z4Q;H-yyI+I!@|%bAkjesoQ$4~(Gm3y$j&sWkmK2G{PO-P9dh)N3o6CT(O^JRH{Ye< zELovTM3_<@^5f$bhr@*1+hfqDD&VI-`vQ-T3tqlF;iFf#@%0=`Y~GUVcFH?-J~U2d zHj#q4QDjkhKhzv5shLo!<{WXRE6Ru-ZgK|8BwcMhy*YoFxj)mWrm|<9Y(j@(d{xT$ zE%T4$(p&NgNn{L5!!hP(n3=N3Ii%X~LF$w0$)O-ta%5xdp%hQ-Jn~t}kyEnSbUr&r zBVL!%oKWuq-Tcxj6+I;rS|WfI(j(2>;@@he`dwR4D?I$3dFt+fBW&ot1LLG(FXV2Q zHQ{|>c_jA5*7`%H|MhVwGE;d6>63sA0enU%Rr5rp$twjEVW>J~ot*jt8zN<rGWOrl~z(LsdBX(8z?#NvdqSNkK=3xMFw za9u1&>5fg(KBqM`T<%$=BcUo`f$D07&<&sK>(D%nBfER5?>ojl%CLFppI_8oD?pXu zE@|q_BQqAR!066MW{~Y(nZ$^?!lmaw57em)yBdU)2l{C@7$y(~td>T8fCm4gX5SkR zVEeVZ&wdxlmL^)%{r?8~ zSpB$-6ojo}dsF_s13&GYPdD?7HxKV{GaqoAXS{uU01@H#aKd@Hf)wHP?G66sm%qgS z{#W0}XV(XO_x2V3;dlNXzV-TRSlKn7g)3ckO((CDLc);MqSB2LHEUvoZesj=G;d9aY`Z5AmoO)U`&9eUmEpSLd<$b$iGMpM#?-|!?(6(x$ zG8!g7XJw}^3nMS{S*ba!%k`*Cu3U&uN*ZNS05UZ@(26Meh{Yj*j}&7j2C)X>^=2^0 za~>QdQ=3G?OYi4!Ft}V+Jf5$Z=Ls)g+`u^>w7%yBm+OLgHu&Z@KFSeC%d)~u8uMgu zxh(k}2OTSATB&nRJoMB({K>(ZM>OM&DLkQhAd*(J4FPWScJ09lfeKfScX885*NcS) zEg4ivbkF%fw+yJBA8sJ&byAU^@8@O#Q!WT?IO3)OffJ23BHf=#Rh-ucczl3&E}PBf z;>CnMxt7X%XpHGKc|_5DnR15h)-RW6y-gvJ8al1&SQ=R*rv0To7=&`T4rNzuBeaho z1yq_hbjrwsq(C9_D8@`>9R(i(t_C61AdogJ=^qPK;;vifYLO?J{lf_o2n^Ll97MbF4=#hrD)SXVkC@UL_d0ZEZ$IF7#Va9BN z0hso>Iv?cvB*Gwjvqzdo2wA$;OznN*_waivpN_T)2YN!j*EI`U{9^86YJhzWbDPD* zZAVIj&JPU*nCsz5{l(m|TE?4YlPf)liyI;%6~f2>(?Bf0Lp<$+f9)94Q0tlXEE?$W zh^kPK&Id`lDWheb?KQsfDhl=#wM`Er$0Th{FlqSE@L>PvXI@O1%Sz9qRc;-w&tO-) zeSE;DuRg;4`HY{x`2u&x6J|5v;d;RoL8O27{XfH>fB89HACLGifAf#=-A}#&ttcdN zpl*I!9}me+OB#8~^{O8XI`=>5J|UyZde~t(v=irNUSxi{p`n(-%FkQ#BlcRY$Qlhn zsQKQF=9inMc5iFJdp@ThfaGp}ZEK9tnECPZ)^xx5JoWW0ON9nK06v(yz3ut3R~Pp4 zKjVEVAe1VbjB^2b)rv4_15)AdtPKrcAGU7>;%qs$g*STK%TMe9B8UhcM8hySOW3yoh?%U;YQ9_?c zu^6N+R2a3BWNH8uxzap9l!T+TXlAKHyz5_; zTeJ*NYK*mrdBfbSyb;|r$u;`vGfg;|St+fvd0JpcP^S1RRo?^dA_PX}6@7ex{Opt+ zI=QTGD&}OiYXZ1KD2SN4o2IN@uUuv>)gF~34J^#PNSM7W*1~9o$>?lY zC?*98bZ4o0SeaZBBXZsyOBfxeE;)c?5$-)+8h&#;9S}NH)o!>F@ud*FbaI z{5DyZ?Uo~-Mdqfk85jftJH}Jx>`yM+<#yGd(~B6Y4z=2#xYx!qR55mX=j*MSN>ID0 z=Jjmo4ZT&PD&Ws4P#Nz;g{Lxy`uNaU#L%@D_JmIHb{0hfu!l$o3hI1B1 z2dT%;-~0;y;>Ul1lbP}Nzy2-!2jBcQzV_l3R%`Kay<`eYGdk!8Nc2SRvlwIyk;`Tn zz)v)(^c3xF_t_ty^_xvwdbVo1@}}AJW>ZhKHmq76JzE16Ly4-Ws1^j1drcK{8Cjg| zo8|V58Pb(Eowc4n_}tM#S1zZ|yoS}iWqyywymG$k_wo;IeyLw0(p%&?GsW7TI%9(t3q>m{5ojshm0K8y7PYbPla!A)`ZxxKaTDE{?xk z$9;&^!7Qg)b9oE(28=t(wix(#Vz_P5k zTvq@HW>+agIp%^YPE9=O_vwokDD(BD5k`FTJfyO4!()@)&Rk9WyP7=S5bwFtfu(%b zRM>c~!{u|NOME}h#rIvs{m59UVvCszEMIK2* zu3pL)pDX4uBuAq0_FNk|#b-my!OMWflFYpbQr}lhusn02B#UpA5*hDP2BoSGd*O7= zEp;#5hh~UTPtwaoiW`(S4PTOBV1MU@kAy(qbCeOK{a5WEpF!&5e905f>mS{gkcKRhiz+8+<=@FsK~c>oo|VGgOtfPd zv4o=8D>L2)&pZ}rv9(3yG~Lj%-HBf-WEnP{73FzN;d z&2^7G^H?HO$%N^7dT|=FG1`A53~aLdj2GNr9`VhOKE}i4jJJ>Xcy)7!uO9F5Z+`q& z_{sfSeDn4WfB5Nl@H^l97MK}d-oJzM9*23tY$hz$ZoIXc>MZSu#m-S7zbxLL9C{5z zkvlGWJ-dO2&o*z~v#N&K>!ex_m@QEC2~fEcfnrSY?glbk4?B1x>ldT_aI`14Qk~I@ z@OG0}2LFR?oA${HyP@ivOp`8k9gSY*J-b!LE?)6L$S*zdAoJ$q-R^Xzt-yjOXw0*X zNRo3QV~mI}O}a8MVtM+o%mU;TsU~7xxY`d}1Wr!@iGlFoa@Bc8`N7vpfnsrz%(#dG zr8gHm9JIXCG(i#Jx~y1Mhk2gL)RWq#p#`WVO|*GVRWtcaS(;L-0qQ1msqyu`XdN?J z6Hykeb=I<3>tfR%D7=^Jio2UbXncS96&S$n%>na#i@UoM9?uKH41;hum`taJ^Rc+| z(TIkMZZBPPg(|PMf2g@KDJnAniDq;6B+5dkw|eKQSPAE%&ouOQdPt4M9CIB3lGzEw zF=j>OOk?X@Ze~=Rwkp}sF&gBI#69v&Cpq3kszS6f38E^KwaP~XM03Eq)FCA(#-nPn zP>h@6$0x=_q24N`vOeESl#HJ-7i=h!y__zMe0(#3$8LltMN}v>QYuG+L zLLf4Eupp+rsqt4jG6a$?7%BQKF(Q1)!6wRsJUIZ#hGgQ(1O~d)Mp8$J$ga)8P%yV| zLsJMAGnt~n_LW>9x;7_NT5*n}s|#VO)_ZYQ4~YOeoGMOif<|^P%A||u}WC?>tk_bbR zBaCsi+5w*T(Y#I5mu(uOlnB)p$6R?)RAgu&?^DFX$xV)|1Nv)@7+*!LWUOywNv1?} z!{nr4-G;SFW^ej;)eJot*o$=fb!nKJ=rKnIlEP)VVm1bc$>7JI{~Z6~$A5{{JN$!h z{uch^x4(z;^@=z5@4(E#aL?#ilNE%zmoKCg3MHt{b@y6o-vL{xV?#V`;RfL=@HD~U znT5li+kF0a?miuDu8F6`@O}Hv{Huqvn}*H~H-~ z&bu%xHZ?qqF@2m+@0ojA<`0bkmY~e=eBk}ZwaS$O3?Ma3q6pL)q$1W- zkNGg+becf`CZizmTw$EVMgJxa5%TROPa%>?Ag0yf?zp0`DH4>UT(m}-whGrZA=(Ks zN%V0{u!EY~_LiZMlHlk6>qTimxw0up~6WF7sO3Y{kF0ybKW(E=TEiS4JYWSDBCj16fZ~{|6gC9 z9;F@fw8e(w#QjcaWO8B-a|6Q8xHC`|Q`Akf5t2MOfMLD@*Sw?pZ4y8(xki`VIccbq z4*4pTfSMfCdQ&RMb*ck(*vmX|(BLzNY-Q0j%hwyWqseef(zPR-B~{>(LglMHMjVN8 zVBzzj$-Bc=r6@b>sx*{JpPyJ{Y$`}s?6!cBIel_KrIYYFbM{do4~8_Xca@=>&W--~ z+3~_Rp^7}?@F6#055iGXuBUpyY?D_&?F zq&J{|NFxkumC_|9i3Yt5iQISDlt}bHYPQiZDu&H&d!?!Y+NeO|mIYYB6rI3;3wwdL z_Jqa;dSo_IF$uEK99I#7HRW&zQE2lhdHBw2w^6|k7*$?16~8<;3~F+WYcJ|DB%%(D z-38R1ib25+N=nV?(yPkEzE@$hO#?iLULtHZuTs5MhBtkjABksss1>Hpp-@~gUn8yD zqvy4npm%)cLB}9JamO}j_qgso?*x;!pB=yyqm90Nc!z)clfTB_KD@>6zy1XO;J1Gd zS}8nUuUPB~??V)&iR{GHS+ENlX{OQ7qy}t|#&$PW=>WhJy;MICWK}ca0&4sBI`0UD|}Tru+a7@N>eJX)^Ileh)qf|eX5GLl$jfio4^IlN$52@7h+sE zxH&J2gL451JRGLrg6v|EMlC8yP*rS0&;YVH=t}l$N1VzgTG_-`I=XU1rvweQpp(IT z$|Ynf{>*e;L(XMrg1u)nEup~@9mv_WG-~3wq6pA49@tR>AgBIB*a}LEZp743uVh)Kx(P0E74IG! zEL9mwk$CUcyJ)7R@<2eo@1a3N1wM$lor*%Yed3J|=)onkDVv{S5=o<~)(|es6dGDAHzp}e zUeK($V)9F8DGLN$3-BZOdXrZV6iEa@WZjf!-jV>WH zUJ8Oj+%P*g&kn*C4PLr@*F&sP^E)JKw+4aLYNC#zGyJ?M-}XZ^!MU*;i(F~Y4A)3% zi`p{!HVu_VoEoHetl;Sx8%)%tITRPzwoyz*nrL*mTyc1+ zyEY9hbpwye6mX9ukq;ZSp#yIEks)GN4^N-=d1@qZTXb7~is`^l-J;zgThCN$*s6o! zVA@}~ZH$GN05|Y%4Gy8p0k8J#HZN{?-~7`}?p7u!)I)S0`oK3 z6i>aCROHddAdH{Y)En8%rUJ(~59b-qxqxM-Gt%lj!!oce7Sp70b92Bvn?j9t z;WKHi^Whe1B`+G=TJM{dISJveGAh!2kP*mSn0toOx~89aG?5dW>C&XawBPyEfHLMo zh#6rIKy#P~f?+*ioyYzC1utLR;Kkh$hvN*pTHKrtP?~VQTId*sG*6~z@{*o7(XV0i z;TI^3IyT}StM$P-HNHnh!DYKG2AJ9f{-Gls3^!ik@hn_m3j*k+0TB7vFe9U1DT>v* z5~W2(0nX>LGRH2(wM#^KkRh9kw00!|xrxwG(JZqxL5fYhQ92haKDfSw=A8u{oVYW9 z95n^hH32Dl%!^IA)}X40bSBhMN)xo!Q766aK1(t3hPvrD)qB0XhT_1X!HzwoSjsaz zqH2VmqRpK~FE(6Ib)IfU`YGpa%Rz}tx>g_dB~25xu#M-2R8dpt{Z_%z{8^lmdF?E38 zoGm^TF*fYFuJIa<8>-ykQqu=in#4LLD$(S}g=Nt%F#8-imCO@IT)s9%EinUF#L*=* zr4=dkR7F&IynV%0?-dfg;sMxWovX~OWRz%$_(HEwB$AGYHTcXMhy(A7-iB*|o55Z~ za}~+vk*rSu`zY5)MJ)9n4m-b7VMZuc@QA)E@qEg^6IXa33uD0azEr%QDoPtv&7p## z^7TGG^PL@9rce83@GDHwNagP#l94#PTRdk8Rkz{qpwaSdVIDV8Rny4Nfh(Uzy(HV} znJ@Uye(-*;|1@%Mfo-+1{N56dO9`MmeItR9*arpZ7NVR1H^?$OZHhc>FC z@a%elKE_cwb2aYZ7Q&YeVC@jJS(^#(w;k#|wvCn-^yFY`rJMGOuWg9mjpUDkZ_4j^8 z#aY1ZYt!(BJ4lHM=KkFU#^|hf(ne)drptN3x;jjg&dn~EasBcE(`;}&O_*l`qhquT zhejpj;c947!I6|0^Hjn=4)YXpBt@a^eL!!s?kYaVT4^}vU{?=q6mD;iaL!@1!RdFN zO+YN77K?Rg*b)kGn1k)SQYtj`9xyskl~WO{6k$>-QmHu?Hx$icLcwz6^+``&koWwpEb5Mb%pYfWHh0r3c>VMTSWCBx1e+)*E(n0 zruCzA1JT-5jLXv&vd+G&huh@n>9Fp*dj3DwI+f3Y|$rdVOv!^Ck| z6uZvsP=gh5lo_1=W|?^(F$7W=p^pZ z1sz3GGsO(=3C@?nXX>jQOWkno*vS>O7icY(op-@~U#a4b?^)!h$9*I2?K!53%do8Y zQFstd0jUN-8=Z4x!)rQRmIVypda*bi4ZIIDXl=q7IjMx|qDz9v##&KpoG01Ia4I%5 zsntlXaEx+jwz%i>tLD z3ia>e9B!sLH*_wUQWEm#rqNJI))&5J97!|S!blp-2)K9DA2A8pxq37CjSydg32xBp{bH=jh#2|Jhn|uaU78ZS618|7VNJ zu|)|8znf_`Fh=Ewqjj~I523l6V)Ho78cGrD8VY7-!(nncuUM`wUyy`ozI%ItnKZO2 zks4Yn%m)KwG^}+nMu(r15oez#WYeT^zO1;MEzA_AWTXfW4;Os#t9M`k=f?%sdCXI2 z#7{SKYyug^Xp#RI4jdaBMWa>Dg*95`#v^_H#X6i$Q=n7F=9PWXlyYo}opbOUT}e%( zC?hl#);cJqpf!chsZ2%fS{4iE9F($9IyCX~Z1UlLIj1d49g1p!M3c*Ihi z$}ZNyot5&0Ger*tb0N+H2@*vuwJyMk;%rn$odgF2JQcF(Ju01wTNMl5WDvm}!#Mrt zYD-j>k`Guk*I?;pDEMGCnqE^|iYAR~6YP|WD9&BqhOaZ#l+h>=NLo>e-frg**9dJ@ z)N#9Mr2a-hL*)rR#fB$0xC7h8p5D!s8zvJETp*jtDN$vpG8#gr=2FrmRVIXvw`Pt= zs8mbUX-+JtOY%B~d2_yO%wyUe{!il7Ohl? z`a#(2l^BAQX*^YRzt1_-D*t^^8TT|tY8rroymM2)hYfYLHts!vE}WbV(gUri)tHv& zw!YZ|(;?^){={C$b3y~1qz6jr()3hS3+HvL zwZiqf;^plrH&oselnUp?q(rxYvM1!M&Ti6AJ3*HXS5azqnTJe@F=3g6M?w!Wb-+fY zS=&Rs22U^#Bw8&bJpeF1C_zY47q`NZw#@d1h)G>!QlnK)uJ0;E&ZXK1<*u|G4&;6$*%_i?ub}Vpdlf6D;{HK38&4YYq+>@#)G!NcNa{|wre)@VB4>$L?FgL) z6a&MehD9={aR>2;`^XxPP|Ja4wWekdU`eWnxi|A6nEZJU>s>IZnon4*#r^q$$!Odi zPq?lt?k|t{#_QMk^4&fDw?F?g{Oo*>fArno$0sjd;pOdZFfkR;oQ5=fssoze`6L7_d*c(vElRr@fcYLvzuH$`(|3<4HNKZ&+%zGXib+ zSGQ7z?S_-Lihy0J{Zka<`kvM1nhk?xPcQux({~T$<>ykN2WJ3N-wZ#)B5mu(mHSV9 ze9~U**K=U*p@cvDhtZ1Q9Amj!tgFjZH8bg`6ATA_o&$T}c$(p3qvvDskP3#N@run$ zVP?Quhs$LRNF)dPv`LQAjl(4~V_7Wh>R={=o0|zr5#GJK;Qrkeo*C;Jb&MuZ@eYR> zw|56jlfvEYp@f9TW-w8BWo$@BTvTXO5Ay^l!uhh~4P7f$F(Vk^+9W4u#6n%`a2;Jj zV`Bqnoy+;X&IY7nip{RG4$ejWENq_c9S#O&3VD-XeRame!-AWe2`^uq@X<$iczC#E zXXrp@1)TM;k-QijO3Nfv>EM_xpC6X|(xfqopL1Ppwcb1zXWNu@92ih8pi}Zti+N2s zmMWVUg$9G-P^3K+5fMRYlbj}@6aowsff_~NZ+L7wGgjwMv9S9h?EJM?k_RJ zD2;cIkNDcF+j@;aYQAWPWL+2Qyi+%!2MzKyrco|P)5+G3y;K^PyyP0R9`&A=qmZ84 zjiSjc7Ig11XQTLK6f?W${Ir_lTqyN*5>$16WP?jv*40#v&v<~^>>EJ{woao_mdJ5G z``mDyMz4Y0Z6+w;#f^y-NfRNi&!>D=^F|HaK!45PvRYeJ%TYJ`M15l>H%4iWN-fy5 zS@-jR3eBDI5w%6O96&X6|Htc(TS4sr03ZNKL_t&)la35O$9P;WP)gzEaKP7JzQX)j5yW!1B8{+};?fj|1%H}F6GhyMwV^9<`8uIqx;IRFHsbYTWCe*j%pnuj(6 zF;Km}2PiOMqb7My-zyP)nY$_yKXlLy0J?M5|4M( zfdqK)uRC9~DOlwXxQ)~edvo09FjfR24S}A`rhXP3`pF)Wc3RkRjk#;^wJ~R{$R+SA4u0g{H#z@OGG*sFsTrX>hf)SLXH8yakn+aMQXsts7oN^(ZkLaVqJR2Mi zCO37S0}`2H=))-ve0o6_q-Y}qLPS8j8R&<$%Q+tHJ@p@iRPr0>YViC;#aX~ zE;Quuo;8Q}ix@}1gF>7tT?tjwYm$RK8sHqAyRtMkiU)#|ntY1-b3w_@#hz$?1U<3> zlm;}O)&&m&)lTaq-Ln?0O}{x$viY)ot_RiRjmBAGjCBUQ0P<(}1y zfpD9BZfFZ?)-(Z$=AKzYvAE39S!s_ch9WBB5zMtFEYL%v8+h7#FrfVjJ~0t+f4-pg z&q%aMB6N*XJH*b})?6Blwn5HK9XIA_%oT;ZS_NFRzG;u?vEjyCt%_di5mIhwr}WGk zgc|slHjplAsBT*+0_}Hoe3=yTiKAvb^bGB7BOwdVr`m0a~>$8VNp`G z)@V2?-F$93<1gMe3~Pou#>GrUZ4`7u)qFv!lD0P#9ie$zt}e#tgeD+qaHY%VJZnso z!Ma*puPc}tr_+q%X%4!H>x%Q^HOKXsDd=|GG_zqwH z+6&C{gk`ZoN3z)P8bnxQX!<-)n4*^Z@$pg)TvuY4EPHtC!_P~(o$-OwMpqfiF6)K~ zqzjePbNc(({F5nKX&b24ChEvZhr*LN3`bZrDdlGiBrutSDH5)`=gJo) zMPRk9pXm4E=L~w);QAX)Zj!DmB1xIz+F9;5S$ZQa2urq7p+N`6L45vw_#a38W1?!N zds`4%WVgL^aP%|CQl=_F+AFTIW$T=^E(o6}5R!y}rQVWj+T}L;n#V;cvS`#%!MXAwQ;*bvP;Zht?nz|E1r>smdu4amR z0I4ins0_MjfR;2F(zSy)*FF?g6T@{5ZbikUXAZYHn}em{KtQ%B4+Q1#!8i2IydJqY zcekcgvO$mxk4~pnMSesSDMsIc08^n2P4Ym}MaN-)W1oxjWQ=E`X`%BTt8-a1T=GS0 zxdVYZ zI^;f7_VvxfXQPm=T773+AP>F`*`LF8gg5t=@&2Xr*bj%%p1GTT5RWOx#1Keo@apkQ)oXVHUU2)*)!?ChKB}R_`bW7fq% zPYUy_VQru_YomfL*oH`uLbD6a!@62{){#yckWQ^a@vNbtqlwb_yo3fXQk|`Jc>CrN zdJ4$pG#hxoVznNAv6+&3JQ@*0MA>~;#dw?3%>haWH|d~J4v4eTK|c&IUcEd8Cuyy4 ze!K<~E#(5j%RW$gMb4!*(X@&+=A+5zaiVM|nj@)z08tdvudBoQxZ-?XtM{BK()j@j z=y7Qf0cQg(?{u0$1Y8yy4W`0C;BcI`kU9hXI7e@ib#HArmJK>3ORlQcQ=zK$r$14PlB>1@J8&BT_9V>jx2a&D&q6&thBmZtTEZrODW zXf^HUx?#%Aq!g-f7a}Mx%N5@F zXpU7rr*#f4`VLHS_*^|Gw3XXim;*exIeo%HMR90`d_z?4!!za z%lB~pC|a;FR${8nsJI#Sanm#=(oew)dV{rgxV06fRwwyz2LspRbAhgGT~i(m7UXj% zd_{(?Pu2JL$h_3c(-RrXn-m^;8qSsZ^wcd$?r9y}qN(?)Eu?sFpxO-zXzzR&jK73Q ziD3k{;jF*lL_5`4AE;F+zKmA5E*2CWEPZrTHB&H@a@J#6T+sO{!r?gK_Vx&E2B1Jn z0X?)@ITWofMITly>-BQELK&P*%3Spv# zLRyeQW-aK|l4#%~?T0JGhGbEkQM^K@_0cRUZM-2?Oy|>tn!L}{fTZTRG*hw0%pNZLMSA{{K^F_>;Zl4Db$ND+&bt0!k`+=2q@h>24&Dcr#+&;Gytus$4KRW< zBW!QnFbwcC)sbte?(jk4zA&lwh(@YW>0(7JPnAS0`KT?YjZqcgw8?pEIusf>nabw3 zTZ*;Zf;(hxi`jv3Aw95usPz6D^iA3-s<um-c_4?&Ta#YjP zi3Ao)Ph%$|Iwe*A$l|;j&IkInM{1)u6%q&3F?+R#J8k62?q|M?0r{HZ++sar6mJxX zQ~rB4A%;>Z!f~2my~B@w`3wBuv!CK84{!0kS6{<_`<=fJqcuKz`+(y#e12;G z8QHXuKzyk(xuX_E>^@j=!}i>Q5T$mj<#ANA-J1K-vu~;ZvTcO@iF{qE2A59+hClJr z^d3LA+uzx%2LVw!?8_D3Jx2Fi?QynK0s*RKl&JGDF4>jLLE=h>#nlxf!a zIwZGg7RCI!x=76qMYwf7BPJ*6B99B70|^LrPy z+Q#Y_c@&Wm6A)oiwEG13grpNSG)Y9{++}Kda(YeCPTnC#Q3>OrY)TSdXNu7#iPBG5 z6I`4nNud#em}d!G!&G)r_-#X_~{$-{pJbl*!I{zg&3%vsZ(85lCJ z2CYW3rI7mk858xoYm#Vy5y*2*xV0x_r#|aTapQ}?H=A5_23uTHdJ`&}bKVC{5p`s_ z;zAz=3M!bYMac>{7!m5o7)nuN$pu;~6%1?wH6%nPMVD3;&pm6)KsY68Ntf!?IZRsP z{(OOV4wzKQSXDGQ{HbyvZ$lfnx&w78@21l&#_U^e_5pr^VpfejntQ;sL-W`}5*k*( zQ;qAGwkIuLH-;<~8pvH!#MnoY>z`62 zSJJ3-lByYe{`M>UtH1ek{MFkx`1y5+5&w8_t{ z_-R#UG0@6#sAJ@gr3QU-pZ)Q967Glv1Y`cSB3 zXWY4u>gB{WN6_b$Xqfn;G7Dyt}{Pr$2jx z7cWkD{rU#;Z1B~a$ACByMU+?NJt<|>I>)d!G}8%PGFoM%Iw0MIWwE$iLbE(g3WwQ* z=0E11s+>(&BMEQcU2y+!f%CxepmBG1#PMJW`H2f_RB zOqiHUdR>AJe$pD&MMEc2Sy%0&Gjz(-o+gu>Cg;guSuC#Wio-m?T8DWK`g-d;CZn@v z`*@gOv<|t25lvfHi^IWSo+ezD6ssQ-<|$~zUC5rLxW@8&Pwy{Yk7ZpWGov(t zA-_9Z=k&gnQjxAM&{`^V*J8p|Yx#0h8ntrPx!|TAe}1BvCVFu)N=s4+=gI&fp|Ht) zSnKElJU%)z^&bLtPHPszgv@yd7q3ZrrqeI2XdErXy(ve2uBqs>eHt`HG(Ke_SB!+_ zK$khAVO{HJPUqr~Pepwy!Fm^(^u~!iy$?UqoKnGBmUYe7lH$JR-0jn64V-{B{&as@}KUp&hee&Xi^>$)$Wehdn~gwz`n%dviSEx-Pgm9I#r8 zfAhn?z`y?4j{qY4XW#xV{^YyggV73)muuar?U|5a3)O0_#ZHV44=B=m&5hrTE^FjQ z&zL#La|+e@xb@+`PsZ4{g<4h`cOFXEm0;9! zO=)N+^SHYuPS2b5RKqytZfL{1iF893hmKLQ*%=Z&BWCjh5Z8*T{*-E%pa0q;3N$mu zZvAy)005X|B;}kdMk-!tiacZWt~dxY3R)H~rZGC3IHic0P>A}hxe62YV3su{EAq_wCcnEZ!X$lchI?~5eF*{8<(%*HCf&zfA-d$j9C}phknLb%b*cp)m9vbv> zbRJI#T%w4i!aPu&13ht`0^8tnwK!kbOkq~B@lZzLd|5)I)I4FHLo>N7p}~9g;uKsO z*Eo#P8uO%MBOTVo^%^MUhuPqCJOotThi3KWbO3W`1Rl>MGpzHtT$kV|u61a@QL?#1WKp>1i?bjb8c}B*9C=jE%d9e; zF1?R5NXQL9G>1!(TE!upstSxn6RcRlB)YmL9HuGk9We(xr)T;wC`-eWbXX_KaWEfK zVIN}dipmNVX6|+1D0#_R>DchpI@;ib9#}1ub>p?OZ)&6@Qtc|5b9lzRD(>kfI;Umh zsR}(%9hH-%J~+OnbI25OC1~be3~|s5!?Lb@boL12t)w|7@5Ote>d?v0e}a5z#K#ge zzrplB<@VMZt>*1U#J5$&CmOC#W^ytHMZAEH4P>2F9Ws~!f>t`qI;``!E+H#44eT`0 zZiO>eaE%Ug;i~=|6Pu=P9LAu<60Ow8)Hh{ZEcKXL+VJ~iTon3dM@t@}$9`PLMN^xn zv|Vxu)eXu{1=Uzn%gCMGRWiwG{d#J z7on;-YJZmEkZsG1I(}%td)RwVBUWiy8#`u-%yHck2yyTyvfqgat8=(L9&zRc#%SCg zkGL!gCS&mLfBF;r+n@dozFM#NlTW{m|MS2f)+Rlb zcaU=4+&9MWfF*U}8LNkTdhS!r4^z`TKh@4q^;~n~7{AAU@7H5S||nBl01s3669L|v9PB|auCB&Ahw(qsm!&{P2l?c+>)O~xZypb?~~=z!x( zuB*+BbJ0e!&^3jtt3(cH(oqHgB03SK+}4OhJw7ftpBKD*b;8YQf{KWD&=D{Bj=g+w zjL}V;U3GK*CNoToK1$I~*UOrlkkfI({o@kck{1rn(4;{sWFHd<4-Z%T=tsZ8<+8#$ zkK^%-S1(UEoo1}74er<4!&y}5PIz{GH2w)lic#_3Rhi<4DQCt<08-f%)q96&QuzQ$ zD0xD1Qc*n5!S&EZx>j;>P7B*JN93L}sM7WGrP+Es< z=V)A&P`IS^OK3e2FdLmepVV81rY#IkQbT?`PGAPE*T7VmCWFNW1nh7icoz!y)w;qiRU4Xlc~suF1tbokQvCp{Md`3?7qJ#qsY z43DT9J}2$77ZDRA8f{VvTIAOG*5R0ogFH2pYDL8WtD~G{l+Z_WG!+d7Rdsk%n$Dp; z*OQBQLZek-Cxq@%G`TDy#~7VBlLdWT@E9o}fw53gMBwP0tW>zpR0S_*?(?9gXPYKzifIBy1zO|hZ@$8J zKKZ!PH=`lUa^t|vBOB7u`~E42^1gS7vH7 zU_I5YGO5Rgb2M!L6P32RIF_gN-5SYMj?A>(43k(>wFG9sx~&YY8iZZapWg#P!PYfk z0a+0`qoZ1)O?{*aQOA~O4a>N1Z8{YDDXMi9_tng(7=6T_?1MP5SWuV+O2|JPdpH}; zzaM}88D8Jr;iKC-JX|g~FAIM1<>&a*pZyrW_3AbL>rcOpkMCXr1YFkzvzc(1<|v^+ zA8SZY1$b?J@NK<63zMW4X{{Pr#*QSRZQh=vr{BQic&{mGVuj>6$J_WGa~rG3;_rIc zwKKk|CMJXY9_pQlnfDOKotU7_Khluxu+_ZpX6N$WV5C9uH{>I=p8@@vJNmClo2LH{ zUvD&2<nvw?OT zns6Tr+GHw2Drm2T^Q_iEhi6?Fh8z!5*5)S1Y)a^?l7>YDO@LO1fYMqI_8xEFUP@e4 zJZIP!8ghtVDs)TlLt)RPK+4DWsxrGE&_E0FHb=+n)dp&<(u5bcC%k%ji{otY_;`&2 zfk1b>Tvs^MvF?Td03ZNKL_t&+jKgMQ@cEnjVxks}zqD4cXOGpcI2>lYdHWa|5Tl?e z#CiSd%{@N*;vG(h35PUb@Qh`(nI5lHq^~15fv3U4?acwlLojpGM!}xLI)3+X!JBty zTo;SeVZzLF&`(aWbt@eg035k(_zLu1?IzQExhIY9D62kjCIIb zX{GWYMk%UvpMY>l&P}h)^kglltq5|*JofFGV7J%06|miWIcDLW5vb0r?FNP%w|WXn2kjo z*fPoGm=O#cII?i0HeNcarp9Kqw)|WQIk!nV;eE>DLxpN9QiaOxeOtm-EnG773dU)a z_xOD6A_}ZfoV=x$wM1_fgnOt_YkKlwjAx}eimvynWCF9GnddcGX_Kj8n~LFKg)|k! zN|A>34wDI95^o1;m-c)KJJ1^2Ig*M3bi4T+*87`rIYjkzCc00h7%J6 zYEnxxRXQTWX&o``6o|xCCU#dZH)b>C%qwe-+e^XR#NnVJnQzd4yM`8x@z#JUS~b3$ z&)eKOk}%St_S;1x+^o_1Znma+s2Wn{9=+P7L(@)niuA@vbp1S}30bN%5FB?I)>UIF zP$zD)wB@rT*(=X%;h9GyhL|7TOTd!`*~B5OXAkQ=O7axp<;@Kqt{42~*S~@5y5RA0 z#joDI#XtYypX1$f#XtJpKfp&fx41jqgvc{*BemXV22H~+9X^7*&k5~^FvDi39W+p5 z;q5*sa%}b`RQ+>rYZjav;Q3(cl04Iu!Hu#2eo|k z`X-@(hl9boM!lFW4#C<`aOX5dkLkTDaR|ccH0dsjeo$4-C;^xzoeR&T-IIDvFvFOT zttgM+ga#2Po0cR1IBSAD!x1ng<0Z@4FzIkGc>VDWrb*=n@w!-?FAMHo96&0eoDS=< zMr4~ZwJp%FON>T(c9Yo`fSh@3U1)a0!S8T+w77q`!kExX-@m&62s&q_3E%$qE8N~5 zaDRWnG%4714OCg9Dx;coXSJ>i^4vVc1IAe!5E(*tK{h%xR>r7+5Jo*Q5#XJRO>X#X zI$zt60c@0@r~|O^zJ}&D=xKs+1Vh0DN<5Wg)jJ=1}z$=;~ezf z#^i{R;BXz*&F#%Gf41PhZ9$4~oC2NO$6{n%9TW&Rwba=zl>;f&KEXwh{viL%L&T!q*FzYoK%=4ik>C zxqtU~#l!iU>B&Gq#O)ega1}w3$7(5me?t1Lb9gvw+&{#%<3pntq7@XJb@=kD2i!kg zU{@Pt4e@i`-5lfnpK)_K;PWr9c=vF@Ww8ONo+hl;=LRXTHgwivCgV1v71TVzu4|?z z6Uwuj*#jn+ez0dOKIzn>37S$_hn?=fcXf2v>Nr#Mvq4>aUnDf^g!p`_aPnyav@s=8 zC+!WMK#D=00q;P{fH>nVP@z&PhojK=h0mudBf!Kk00A~N>Z+JT5lgzW7Xz%AV=Xkw z`TB__KMV=uhe{}H(pqyE^e_iX?tpa)rF5pB6A`>(AQ^8p5mqF$-fe@$D8cni5kC^NKlIzHG*AtH>3KVI-20-xVeq}kW>1Uo6))WDw)|* zN5{m>Xy@a;M~ut5Ry61*IE`tH@QjLwGLp_bFoM~rl2liS(kA*qkzz@rRw^#TR7ZG; zs)Gd2k6J1sH4@~3VGkvpc>F5`(Kuz3zOq7mS7;X$TSicY2jBPM#_}GDMzi%Wrs{X} zDCqExau3c8V}+!t9C8#}s&A64tn|`-Grw*8^?ubKwXDa4w&zCD3p1`U6SRxS!nnz= zvdU5UHV+G4&)k4owOcRAnAc$}66doiNp7HY9xT6kxX0b`gtw0mn2o`|`{|GHXP^BP zzxVMc_>jJG5ZjUEi*X4tlp!^;+ejDG#?~SU_GrNUt2$DCT zU*>HZZawTO9v`w9lIB({BJ)xg>$x`Cq^40#3`aSjV}7D#9c;tg#8*D;0LME`SngEn z!QK0yz+3OR@sxOa@$`eR=j{rAhj8tOZ%xmjY;Ro04|wb5dRc>E)vRGx2j>`bL{AVi zI7ur}oD@oPqOg{zo9MjhV{DON?WN7q7!7L^;t*;m4VAOwoa3A+9Q1VY&`<$wK-7a~ z)QJKuI6H}YhFvYD*bNms16{5wm`&CY+lb_R^7T8setnw}_Q%K2 zAep4i<;iRW@bQX>WkQO_EG2sxp!saQo_o3o4k>>7wnU?EFozC%C2syt?%#057 z(4fP1zJ4%{hY9l(8fc?6j&m?MyIfZs=P6T#6J7Uan(^}P2J>X_@Nfy{OFqN}9p@<` zq~V$$4-;-qGwyCZ&X)z3#X`mW>UlO;t;4IAC(O~X&AHI@+}<8RAiTXlBLQub3HoCl z5px?Os$QrAz=ZR4!TGx2a$DS)r+R_Wt>2 zU*hiOh?~PQZp=|o=r15Wjc&h!XMSHnkkuB4mX|I9e zhK(^m?r1zvk+U=FJ&dA?1!z=oV`ebE{^}!KmIaU33;vhy{{WBcivQxbeg}W=({JJC zFysDw4ue|faJ^hG8-rIjcVK4RpZ{Op-gH-zBs&xPj+rkJar0(YR&{kXdzmI@#Ni?m z6bG2m0tkX6NFzXk-a~Jp7Z9W$dK3ZrB?u!(5HJ$7kRXPfA)9PAdtr4gnR%B8U(Akv zoMWq-$Gx|jngXh_@-7h`?&jv_oNs%?(DmEB3pV z6p`nO@)Y!2?0rP2B~GBqyi=EE_6*aJ(k;E9cuKbvL@{X_!ya@z^{}&%-!(`YXlAhU z>I!XQLom-P&gU7Ii%?jtHL$(G@i?enk}!^)pv{*B<58%a@86wqa~g0s_W1ty-r{_o z;Z~31X~4r*ljxH(Jr-r4fm?laG%kzN48gEQ45P9OC8k}zq)$_d;4vnVuX4xhg!C(w z)QEXc*9&cQ^&Xufk=_f{@^YEu>*uvwpfEet%o32%+XF?z<8T;aR<5;zwncQ>8fg+0 z(H?q>BlpGUV-n}&uIu2Oi`w&>e#EPr1D0ilwH7x7tfxaPWFOBH?jO$4so6Q7+=o@$ zxU7Qm_kAZWy`JNqyE+dnYjkR!mkFIU82cW_vB&Bd%evsRZ@p0`bBRR=sNb?UT&5W? z!0T5>^nH(Mn(=U+u&fS;vByWZH#ko-R_8D&Q$dCn3m(n}2j}qJPv6GoYh4|jQhU#r z8Sl^MIK1jia^VhfSjGxfcu_+wqk{-z2xbNz9_L6Ylco}zZ05@}gyT5KUK1Rl zv&zI6z_QWnuPCljiWa9>x6r_b9Ml}Kk49IVit2F8g3LS3RS&;w5aKNIL7<_MHk>1{ zfwG^P=Taw?VuO5zFv;solduO^i+%5XMB$AwI1WQZmBN54cWRFMd#;O4&ZvM&7w2?- zn6?g+QRA3vt68pf%7v9_P%cr-wfzRA19FI-B4iB+CAm$Ci_3@RLgi?_$geGwoD3Y|9p<%#td@KhellH({E79?c%L^U^xuR z2lW*#wQ5jWgBegsrLKtpx*4RJG#g-c%G_cGre(q7G~s{xvp>c!&iD9(Prr*l{NX>q z$G1lq06+Tt3w(6C5t)lIxILb*xE1do?$KF`SH}}(Wk7ArgQ$HI^R?!pd2{EHfG7Zu zf*HVjeC4eV!FG2j61OX(Ml^>f!4Qx4G_Ezvrf!;tA2|8T318b3pX&Jw8aad&sd`7DmT-}=@ofPlNZGw$wZ3_}mM zN}lk#`iPu*C+^Y1VDVeO^5$PRrS3_=6HY?rvm}6(GfmI@Pv*#pwFSP4jOLD(kQX6gH@X?L1Ys`)Tur= zzcYj5urb=0=Xs)@(OVOBgJH|`?6EEka)=qqZuRi+LeaMbL!C_cgMN3O;{)9}hebCa z6XLqEqA2Eob~aLPfppwmXV8PxNnE0eB;HBT=Ds%o%eo|jprmBs!h26HHcIF7gmE0u zcU>IV7%PSJ(97?D2xBr4+l z-6IyIPLD$eON3SL_p$E;i8mzoid*9~IgLXcI!JVux)-b2-m3!A2FLQ@yD7w&Fu=DVWK*Zc|Lm;xM z>D6`T%i<)@_m!r$q?<`Y?n_e-M)mktBRt)IjU{v>abnIfh?W?27ff78-Nys{v8KFj zOn>zK&(i{CaR~2wPDWdxGN+QT3p>`N(!A#2EE+4^zj^1+Wcgx%*{npXk>7{_!wQYqqoA`}Ca?(%)xM5>0O{rD2E|M_0J8VdDZ+r3$SQ;%Tm!u?8f9!PU@_i>B{z;_NqT>cO{$TsiiAyLJ)IY6!`Mqr`FOch zQU|V(RoV~6RlFI{r9lckuiN4pePq;`;bqfV`U|vW8ZQ84xox&wrQjv6 zhv($;ev>Bsxxns=#RD&Bt{X$n4b!4JVf5erH-F3kFdjPG-%mIm2hF;5a@vesM7M@) zPeFl^nF^)+GcAOS*^&dOq&l+3faIBeqz&w}IH6W5Z8xAH#s~#=S$w&f#s*l5SGTT` z?P>&N^PbUl0kIU9ROcKPbuAA0!D6B#8OBEGphC4pGV9V~zq`BO!}|+9`PL1_v4?XG z=gR`Odi?a~@A1(`H~7I1K89PJx&ssLA1+|en5Grq`t%m3qo7smDwJ$v36E!q^^uHR zU|Hpu`jB<3*I^hWmM7>uC99S*Cr>V^K}g1331tgK4<%zG=P`^z6Aw+VcM`qhoHP>0 z!%*lxgGQDzU2#B9qV7-|L7A*lTsuN*P6j-rpV3&rnogQ5H5v-98R)_RAqxAvWb+c{ zWi7JmLK7bhlf)#bq{+!(M$NMV!@xtMsN_5dLZ_hJ5vN}w9uMT}DgBpmDTOXXcJ>xj^5k)eVaOis( zJUP*liIeE6Il|IF1vbIzToy~FnZSBq1w>y=z+})QpTCO(8Lw=Y&WIEL>cvsN>truo zbr7~J3;Mp-h_P_rI5CjYG9)JhIa5{{*dpu(tdU$5s-kk8APd*a%QW&6m#=Oo6g$gVu9pHM- zBBZo@a_kb_Scg(!Kna%hmJdb~gFuIWS@*#3${z5MhDaqWP`MW;0n7x#9ZNkv)5HHwFH= zgE(Kk{-wind+p)s@3)S>Y%2{K)`ro*%GZr2$^n#bWZ$M^EC(+ya#(7}m^KX`o^V+w zlwJr#f9GssGSAUzR~Mz38IH)_pr;z`23zOFpJ8(uTGxi3_x?}4Q4KsC0-jFPSo6F4mvpSh-z32<>RKtN&c)Y@3`X*`gdE^vlq zG017wW=12)AA+_s7)kWc&MBg(H1K6{n5G5B7>oy@F+1n6EDrr3$oMdduJC-GlQ#6@ z5uOQOe|?X?`J1os*=KKXdwayYcMrIn7o1K5ZeES(I;+U67~6DRht75wd#^Dja-@Bi z1^}du@#-|{#j|EG6PA@@gKAAV!Xpz9Vu}-J4jmp4BZdG2XXG>b@DMYsj?5i~fz6Rum>}Q41K~ zJF}tDA~a1sNnCfGN2i8OU5uuX%D|Woou!P7=1=Y}xP9YpmPSMreYJ`;6T;<^WyNnKCLf;#8^0aUVR_AjYDfUU*p6-Rt zN};|wpW?AtDE7fw{YZqH!=PvYM+f_r9o|2jGZ_K!U?z3Ome29k`v*xiWf>?AU8g98 z4qj%C+Tz&Ym}DqI+5k?=f)5Xm+9VtFokiantSc~Mjl-zLdH6{X@>j>BYMkZs&dZ9v zWAyz2)+AJYbt^9Of=*+)f&}5!>4wr{u`%yqDnwd%Qj!;bw+s(dLV1g=Y3HtNJ7* z+IJWNgebYO?_j+tsOYP6@S1)yjsw_>Z$jVq@Bn7#aGqxPjJ*@GyAQ)C0|B>URVf2e z9*{(t9yHVZQff3PG#nWcAI%{nFecwOp$OKZK-<|$-i?dgQi-@U{C{nvkqzj*&8 z{@`2R!9V$pe~6poK_n}x=a(j17<#?!enq+`Z<>z{tGL$o@)O#}g1N90FiTur7gTfy zJWVal4aTs8T2{x;JBx|AYPg%pM%1Qt>>Xp~H_&E0JaM1$69_hKEE8HPmUi>|%`6^X zfUsV%ispKg;HxPAlXPVIrpH~&6m|?MpVB=)bqv&Kh=KjV$>uNuLp5t`rBE<%)2c;R z(9V(42ZL*C<{X-_Em(_0+nE~wRq98e%VbSc+t7xu)F*c%(t2Wt|Nc59~pj-2_z&Vb`JTHRE^ctt*y$50=TLs&P zBHB`ndl8eTKoRrVa3Z7sR`j!wYsxDZ4zZC!98kq#~Y{PIR7(WG_E{I@y z9Y`qJY7E8rBp*!JC{Z30Laa?N)Ud3gYi(jL6p@HI%Mq#eG1`c-T+*=KotFWG({t5# zM$s{+O{heq^h)Iq_XR0JcA31=hkZnUlfS(%6y|kRG!B^N1$}4GbsanlMLi&9K^~A# z4J{eu*_Fm9ninVgNQOto#9Y)c4G?{>M(1#16@&$KcYoGF6=UdojDw90(Xx0&&Vkjr zXzFzw2fR8S#8B+qp;vZ=*K1}ip|>4uCrINOqL;Mn4v|$}-LF^gV&sjrl0!QLRhWdtt6H$EMf^`n5GUUuGN+0}exvX$CIKs>rXb>&qm~YZ#cZXPoC5 zowZoKG>Dz;V1t)HbnDbCcp`MR1K=?BJq|;UyT=ROKRjUU#h>6j&v5_}Gp$vlKh3Ps zQ%OHghA`L;szD#D#bsIbb;L04G+2NI#R|`# zMVs^EIA)Dx9!11k{}!M2`3nE=SZx|~3WE$rgHgKuOQz+=PR1;A8xiBCryFe|r~(}|Xox*ZhIzm{ zd=`0Cd`^>VUbA6^X4*X{4cQXu9wbd`*GclgC$C@Q>-#$#`T>9Sx4*#u{1<P)AJst7>;Q`aK$na73Y6~ljnQzpbG%jJ)AaWjOw8^cmg%J(8!$u2TPcG6~ zYb}N}OyKKHLM>$pU*nQ@yUnt1AjnmQ8E<}u+xg&oPT}9E$;_MIr=5lQiTe`y?)y}J z`CjCk001BWNklA-qKHKTLt#d|Ay?&{Q^fz@@F0!$yLWB8v=W3UN{hVm3mo14d znonsaEKw?`Z5E{y4nAnBqV9qkH*(=RSWzK(k3x~Tie|1|p~#`U$=-~_a)CD~N(I&b zrbcXCy;6JC5GQI{6E!Tj;7XKIKz&y&EF?xqbfS~cHyzOT@*WQl6CNIBeEYN4c>DGQ z=R7`qIOFTDA8j7{^{saikt)#)@Xy-cU^pFaWFp%Lj-lyE{llDFehe?aSC$R!ptZg zHMv?NnlI&p63Ps9DC3@l_ogg1$K>Wk>|r~Ko3pGNlgKto(KRIWRMwcAu=c&WU&ki9 zLS~p*9F(KThZ@ z;r7PF&qUgwri@c*01kt!ub68aw7G@^K~);Z%+Wb?H1=tvKWQ{>*T~P#f*`R4%yy&7;UvG(| zA@%8ga>Bs{t@56Sp@-=@^p>Ncl#px$0d?##(W24~jp3MQ9URHvr0*AOyr*GA*BK0xtfhHb@zvcun1SQi%i0gu!dUc%a9L&?dXZ;bw9)ks(i z?jDv5PKN>Wx(Xdy_i3Z?b3@l*94w}JNeDhlQ>&2gZKCEnH4n@{rxZv-grV_x zcQ|%Jt6$xU%j98g7YAH@7o-vDSpieb=kxt7xgaNG)8uAhCi8XLiI*&Mj+4|RA6B*~ zd|_7TDQh1aEag<&Wbs&ml1w|9x*JIzmsvcG?k`8HDbLfz7CocrKa$hIkwm0bRYw`+ zlqz}FenIs_wg=&P5w|TSBVEt;%@wxcLgljCyyWt^TXbcjRKbH+KNdIQh1#e?7DZJx zWK7#8nkxo3WzB9`&QtSLTVo`L+gRKlkMN%HI89il6+_=g7vlf(FaI2W^vj>)-}&AT z@#~*{8z0}kmI3wZuzIKFlwC{g~3o;J&9jL3^k;nofr z^8iW|Ugix|KBA;H#l(arP%>j#_1C15JC4n^==0?B5GXbdVZZ6E6^C=9MS5(gDbh5Z zHf>qjCb)hmWRybhyyP^q!K%9KclVEi%#D&k&ANU9Qf`fja)9LV!ZEr<8r1BWOq{tc zt4lJ;XY;F|k<3zj8f$QS8e_5GoyV+HY*PC6G>Ojnq-Js<(r0n2tfyciLJV(0#&$Bz zljt*TJX|JuFbSOugSf7f#wECYJ7pJyBHvoTLSm1W)kl-E@L40Wkge%pa^hnyuZc{Q z)%$EPL+A&Ko^5m|Uc6WIlnZ%5GP7bloTqH~#u4ofdQ*wq(g->dmVzM09AmjG5th|s zUNm=Hn?G+DV<*&X=e@XX_ck`I>+13K-90`$Ucdw#`VMbzZ}1vNZSFgX^cn{^&vUNT$j?4Z6TZBAl)Z-x zdi}hE#=co1yuLZ&?tH=B`GU)bJIu~&jFS#2jg8EWfaXS{K4UbZPIe7(VefDp25E}* z+U55=5yAR|46l?>)cCrjd!Pr=tx*nU)>4P`}X!0%#06@5BL{<^Vj%4e)?m4ay;R``0d}vZ+_?7_~}>g z@&4ffeJAFKy%px%>KrbMX!OT%NJH@FqD;*qn`#H5%{ARaOgGeoMuR{bqB}(g`_zaOu$}efbG>-h&~LQM4gK&gGTlm#Y7KVwe)qXe-6nSEnk!X3R0OVR zv&%)#Pd9*kWz9WJKgb4rFGNbIfgZouz^Z=MJpbB0dF`&u7@BDJ&iQ3QfYbMaCNsaF z1v)`eD8?zCuSrg+Xow3U&-RN{RB=Ae*ni1*BN`-8vAM@%_gqbC@;IOHGupVARM%2y zvU;okZz9bxrz$ctmc>ORJZ5HQ1(tK-EG@aVVz1x#7Q@itcpUKhwKT++%YtR~YGOm^ zIw=C(>fyRTH?<%Vq|O+^Fo>DXpiS5`$#k;sExOLAKF`O1_lJiG505im-HbRMM*w0< zB?vyj7@-ZB7(K#Z$MEE}3FK(r6f}blJ{dF_HK`Ke9GGbni;(9OvKI1Te~%MuN)%6P zDQb3oPC<^(-DGSdke(}sDsla3ksES{jRsGPhN)5HWZIlqrm*IzGOLEy$#p`C{tCsi zS8d!dblGqr-8*8!B@VKII?ol#;$1k?VD&;NP=$MG=4{YQSD zVoXuUQXe|f{mT?I=%AB@UUPBQH-#D9-F!x;`Pt`r!g-Qx@XlIvgT-fW-(Xr+eD&~% zyYmI-dBOep646&#>xjd+MHv%;0v^z9D02eZxOc{2=sLV6##i@`aO^SI4nx=B)!`rm zqE2F%Om^;iD~{YjC*4`u1COJiIWlB%ad`1KEzu;+s6%^atT>DJ@?IYv&v^eRF62al z$bYpicw9tE5@O#xr?M=d$<|2Y-0A)!8S-7a4nQ=-3fbrLvSL~n%*%phUT`{$_~gxN z9EJ|lvPg{C(8C(Laac|2pcEbGxXk;^*PqF=j zNmA-+Xav1(WE{Uxxfm}6Uji#QjQ+ewOovZk!@NJ(N zndJiuUnN*LY^A69sWn7}RCIL?eb?d84`Ogd1|J?C@UySK#82OSg`eEL!yo?I_wl>m z{~-?jfS-N+9=E5H?1$5YWnCjfCZrbxzYdoTIQJ>Cyx)*qK`pt|0#q)^-0ncR;tr#2 z;#8ot%{VIF>5;Y>#vyW#xt)8(O+I{OXqBI z>b1D$+t*T$EKaTy%_fP4S|FvY{ z|8mOuWCXf)(8^mi|54hmPYv6Bi)X?Z3ev&i@iK{wfJn`!)QNwzk#=ab zVRAYN@!n(Bf>aQ3p;W&*jp}mkVuaOsn(=U%R3|Sq{Gqcr^gYILz%A)z-1PR$;@8!uz~w&68%2|)9R$T?<5**=xx0BhpvNn((L}> zum1qQ`S-qyu^0Mz7$}GypEIp2CHiA9r(=rV36#6V7xcEYf8-9QN!y!M1UVfwcj#~I z%Z$>LFPRT9T*N{}8AzetZQ(+keUE_JEt#=nPD5qGOF1>1 z3dt5Xnu3>AVs+t4FJxlYjRkEqz6tSd#p5&@s|2$a^!hdgSr&}Uw1fZVjZ&)4eRj^v z0+n{V7p^I$v2M!cPV#5pb-2G=@ak~D+nXbP^5r}H??3vN_~QJ4+hN3i`FnqeZ@qql zhsy=Cxt+5t%$r)hq&QPE<~BBLb;?uTu~G?C#m2cp;kc+5)uW~H+4;%**CuEBg|yx0 zcAL-ZxJt#ue#1z3PMYvDB@@lh-fY8n*e%aR$A3jpGhI0oxjEZY$9IikaqVZQ9(r&! zK>#}Ttk&`jG8~JsfUGek$6G0IQw&VfSaF63*Ujv9^pJmD8r&R#)uzXw2(={ZM�vpda(Jigr|ukmlLJ zTG6!ylx$r&qP2&EG{Tq5f^qC{b2GquV47CE|8N!^sWlje4zF&;=&Eb25fiMDGSr(f zqL=eA5l*K8!_edY{;ZU2i+Nci`jZ<)Ns}vu+R%4cR)={ODmHZ#^`s#V$5=%o^!$lF zS{i|Qam8H|sq6YR7n%x<8?l)Yi+8%lw1F~FyUR-74c9KAd7&|mG}3&sm>Fikt4UU% z7;@skxWridhiWfU7eI| zAo;p7An7WDurL@B7i~3@3T8vvYz3pUap+(MgUhtUOx`h=7)8cFmP71ikIBzp(2Fw* zuP&gStMgbWp>am{q@Zo4um^?8Kv9W1Uiny_an|_ScdC>=p>+!k*-Y|NN?O-qQOcHgaCQP}zwwl+4d~kp@vd2op zxF(t~0GuzAMi_Z~bbG@0zx^>*@A1|BLmb3eBaDJ&^>BWc;!!o@fj#ii=@=XE<0uWS zRh9@D;N#mHym@sL{qE4?ahkwh7yyscj6>f?&3!0vtq}t=QUpe_ogE&}6FR%f@IVI) zZ*ERF4g&zjVHjXLi)C4HSr%NT88?Rmj^l{er&XhvD9T%`5lKfdm0I<_2Y!;AdaG!>@ho6C4kc*-Zhx zv=;1>@-dmzBnNhEsL9x%n)%oB=G9QHsn&9xw$J44^SP>rscg(S4|d_wk@$h~NKvzkz9)@&4ffqY=7Hot{ z`$)}<=q(!C;>2Iqh3_#C;)>yK+R9|A+E#QBZJ7+*M7*z=eNlT*(-^aC4Z^Nx@&4WX z^A!47ehzA#gN=3@|6>TS>tib)Y~DBPi`D45*Z$SsS*>1M?J-Gt+@ zhgC#2(1_jYqaHKT^sG@MC}|3qJ(hXJ={Vp?6{*u1^0yP{h+3q3Bqe=$XbC zn9LR2C{h`8p`Ep=InU5VLZXmrG)}Ielm;KM;0~J4u-5zDiqX^RW1&taqXTntjkduF z7|9SU7)%NK+G|uoDngvEC`Oc!?m$&ckzuB&)g+5rhDbt#E$?U2I2&fCbZBiXkf{2- z4mg5yFOg7JBdtHnIdzI4hI6y3j(v3w(2U#AjD+*k84^zbb=(f0HMrr%>jctlQ(&3b z=-^8MZPk0gvuaf}LsqVD$`yB-U+mNY-uYlKCJo2Kqacby-)SbTfrr;PE};~^zCEI| z2A6rkVd(WWoDPz#x(%~t19zC$1HL_h;yP1?ExYP@3owTJCBoRiu7<}PWoeIw2lyCC2t4x*4vZV5b(r4;Aiuvrp zl&P~~Xtg>gD6sc93|%ytBBlRI#E_t(w}%6M=ZD`z-&u6V;H2nkFby+gusA20+rE#{ zagUb?cjpThjh2$*=tYmNL$l6U9LEv!x?t>k+>ApsCCphCtn1QRgL!p$yj(CXi$-Zl z26PzOiN3gxwBbPe@AY+W4kL!H!>VgHe0HJro5CDRhO5F9xfur>Msea^RvA9LI*fSx z>ITPQ#7CzS?k^KMWALqyUMU@Yj_eU;%HrFqs1O|r#r)xh;b2(H9wn0`_zf`1y(#=F zUJfobeMCi_9^@x%%Hk)+DpB@6R6dlVv19(QqJ;YdR{}CrKFn&NXB)>zgDI|ms-zoI z{&|Tud7*JQyi*IN;W0&dP8g#;!y@S_sM6}i+f?-IoaD!&1GR9C184x$CZTnGn+6R0 zj9R;BoM~Hy%C+ZhvqOpgoUZv+J8rGT?Bsbc4m~_1Z~V~r@jW|h<(a@K>g@J(gf#|# z{NumG|MpjZj=N>T?|=Fo{L|n41N`8VZ{zFxyUYkJ3`R|)iU;qO0OAB~eyH6R=Yoq! zIjpXF+q`k(EP|`nx##DHxjiR%vXRZCVf?JFgUYs&YbHRJ-CXsNagfDjgk=8y+){>f z1@1SFj55P2F0w`VDtopy%!w?OMx0BNTU38YHjbR`mEp+K-)%n-)4`$(XmmnrSGs6HlT3zfwE+tc>isrF({Zrg^grw zz*wAqs=OBTc^=$i*;Sg?*7>JuCK;$q@j$6b<>pEUOm)uP<~MvsyP>qKsC2NbKWMnU z*2Nc2i6KuiY;+W~&Kmn6y+kTVOvpaonT!NVZzz_nI_8po{q^gk7-^}2P@s_xgN+Sf zXDyD$5#y-(&edx~i$>BET@e>XPR?Nx5hy6ZFhp9x`j9*{zD; zg}5KeHVs4D{59HmnsRg<1_Wge|4%F^uV)h%cDd1Jh@H~X^PEs)QZXDKwxMwinj+^T zq7~M+Tcug#(1a-Bo2Hl?V^RbuMP;y-HrJ3tY$(x;DHO?^6;~|-QmF1VGcAcGTjBtv z6f!Q1Oyt2Wlm+KGa|2COZuv+QixKu}xR%>SGUId{GOeS{^w0?j5hryUV}1}CDJwDw z&T?)EVFp^St8gyGNWxLCVKL*bbLzfd`|vR_;d<*Yr-~ctDJfM5n(-iG){@K^ow!Q6iGj zo{ZiD9K^+0PO}bY0-YWZ7wzbRtk*1<~ri+cHxyDUzt;kt#GJ?Opa9K!`Sc4J`G;LLk3QA1S6yv z!$c{uAPjIRrvidf%ohpLy?T#nUW#T#qEW0$FnoH(saF8Ai>SM2Y2=5_f-#`CV(R6b zkLSSm~8Z}^SF(r^UL?A&0lHzOxoOLVKBP0zcX!2t#UI&>|7?^ zH504o&lwY=3ll}k41zWZ>?k324g#*QN%*AIC8DbrklKmYt&v$t|1n__qv$5>oj-~N zi!1=2=%)ajM~P~wMwHZ|u-2pVH9+UC=~jOTACnJxtc zG8iSXHnv8QkkG98kU<>igIV+DeKu$af3IB`b;3+~G1vp9Udx<{B3qk?WnFTyH*ktn zAf%k-MfHTG&L=pw=aaIWu$2~*wQ1PF${cX&98NJyS&q~Bl(k7LEDV=3QPl0zWcPMJ z*K#D>7y|ih3g3ybR6ucsNg&jYaS#e`XsFQj(O{~xT}EG3<0rp>4w5t5-&oa5t8tkJ z2u9HaITNeKFm+Zmx2yA5R*i(x;}f3%ACZXY5s*>gIXC#pWB|rmFetJNf^k)Fe2@2} z-h0+iNn6!@E+@xOwjTsTHy5re3x!zysDz@e_sP6E8E9qfn1IxVp~~2gI2{JOJ{@s? zp73y)aT+A!c5{{VpG8ao#UjDR>+JC!jwWhvO8(^vM0*F8m! zJ;aEW`sX-tHx#`aVsc!`kn*2 z$#G8%sREWvMEd1ATTIh}o5QGj=K;)&^JR`}Fg!crzF~q?At_^xz$Fy#ko={_z1f;}Jx_zxe52_d;l`Y=Rktr=Z_dUzSLCxxBVi;ukQ`fkNy46rFrWpG{dU zQPGXLh6cIy{@6rq)%Vd;i0$9S{Z#KUCG|M5B`EecI|(4vXtweb<8W?h001BWNkl^Vjf8H6DSS8rY8?da4PaXOhB6B{Xgw+FoiZ;q zZtP7#@2Z*)R*sk;4oM32gAD>p(e|D(oInpvp@kBzi=J-=K=c|^*&k)k!|%Q>U8 z7|-HRt>@qST-TfDJ*NgAu(9doi~`L**Tqv}C2)$^NSgHYuy^qqrDBXwx}M8d{Dv|Y zmZ;D2DmkL`>QoM+5l&%vlk4nMXV2-pggj%SlIcJhO((g=$_+6u+90XkF=(*E(fU05 z)CZSmS=(;aX3eBQg=BA=Y)zqPfK8yCadK8B2({g*!ISM0)wJ(SY={D~98k?b;SJ4F zh}}vQZHwdS5Mz;oF4ovyJeW+b(uf=kYs7?s6x2n373w(=hR$GCP4BvTJj@erj|Ux&P{w2^sbgvD2ys<5 zn#vgxme1nEys+=B3`qezPIF>_Xj57m0HHK}MAh{2(}nz=%W$p9qfHUQPZ*+HY3XX? z)L87~UKH|XeT-< z3{G`8+3J*P*VFoWyuHK?l5}B+MQs{%!;mlm4k)ixYr*Q*4~ph4>l(c+0;7U8CE=Iv z-r@Fm#HVlG;^$v|fq(wvAK_u0@Q=RtLwx^j(A9UsWLf0j&R87!VdE0ctsK{8w$Pr7 zcG(>A1`V%(_S&7jcBidTsT%uXYA+bdyeO{WMmg=Q+6v13xndB+W2Q}JH@C690ws^k zvklC}Rjkt$^tVcMc}{a#gKpc{%M?3Am2p**d9s6E(yp;nynkp)f!-U665Cmc z1ybD=1ySo2RJ!XW=daQR5@V{-8bzk8ngoR=#>B>(GB;zP zW~g$iaPdq^ilDa&%~>*zLJ=Vo8?Kl)%rR;s{O+;}IvpaNf)PwOAHnr^i7wz*ZV{s9 z4G^+b3#Mleg^6)efcKK48Vsb29xFlIt%uISC~6-D4<>8kS3NfG0dE9J zPVqV(=Zm;)VufZZXALP>jlVjubK0y+R8DlK)?rxR8FV)76Au?@VoWF5uOUy@xs>PI zccEFQOh1=3tyzv~?vl-lau=G+%AGzI0m>w?lu*v7)j_T!3hah#U(?2wJvSfJG0FzC zX^cF4h)|CV3qi0=p&gZGKgkk0U3cs1FwG0zyt)zBVs;q&PE4kpL_5K|;vp4c)JTUJ ztn!s$n6+p&vybm54)ckHqr*2C9n=|(drDG}z#s`|IU&)~Lj;!hUL`2nG?*+OSf6ph zRo;8XWm&-N(RV$jWx-eX4{%NxF6U_$U9;_?Y(!|>I*cJ%%SNxM*MXYXn|&=8+_a#rPnXLKxD{)lf z3T2E)$qsy)n@bVO;sq%a9n^5ktr5-4Hx_#)P6ZPzHH*(GDyfCdJhK{o%9I9AN5+=D zKu*2&gxh6E>c6qZ3*{Chc^` z4Q~5$9OSG0=UHelKXXr`ojvlY=%E+Vu8ZeH?Ltl1H8o5Ca*aT9akJz-LaaEhtf76Q zvFZe5-|r%7LsS?lV87LjHz;MM6Q!nc=d2YYdfGfpsIgI`eApO1oJ=yo^@L8F5L+Cxn)W!}I7Ct%3eSRuTrR8TN+xOl>)RvdMIt%~>T(;=Lop5#hiNe^ zV$J0BUMagx_D9M+%_oAS8NO0rDn;B$tWVGta>&~aT4&X2M(1F*V2kL=9I0`d)#*5B zO1Fc{Jo_-ulgjfXGQQ{2c& zNbCf#QsBi9&2ch$OM5^nR>PX;l?HCD0e3z6z7zef8pv@k1B`(3T4SOsb&)+<%_8M| zT7Vp!cUYZ~1~Jf_wTXAkVCP|thk>p1sd)ofn%G6p17pDKBHtppO2^%(h*Du@9F&&4 zYFu1+?)FyD^6)+e1|FITpT)vaWI!n59bE4bv(?ZUgEyxW-rYaq$DexqV>zxjZoC%PFI7_}G886A3WRAxm{&3Kd^H|%77C%&nCp{}F<(c2 zTV#ywTIO=1xO)gK#J#};BHA5-m9JC|Iym3=sQxu&Wl=R~Yvm#{Z6oNaWz_|@*qAxE z_dv=1FK7fgqguxM!?QX{*CEj@8pJm5E^0_KN3dc7&`b<1CtMT^XQJ~efqDx=5X2~J zNR#fBn)C^(Dgy#yzOOjq`s}8bb2Be%DoR-pWA8kcW!2aiHT0oQbaD==Z5Fc>#`akh z+=T1NNJL1q5ps&S3|-1&=E@S!JI-=Q<^3!XhM_BRZ7B<)k5uFh`n#>pqPr-|!!8)q zIMPOFSwU*36n@TRu~w9sefikrn0Va;Wi>FSj}y&+c-au8)LG4T)z7R=qEVe&pko^< z>EnbHov;noMs$15X~sEv!hv2kA zK}$CbWfXO9M$@WDZzRySOl}3%SWqhhIC`c>Skk!8A?D5#350+VRk>Z~@v?3D3BF~TYg z*g}RokoJz=T5!*>s+~1DNHE64>47-%1FLuP^Lo!llPHt!(I9~cLl|qVFc9($Gh|Y# zi~vT4Z?ju*FoV#=Nj~#o=<)W|4aUBcqMY&}O`w)*rm;HwXJ=WPZH{@Do;@sCd~$n> zzkT-(KYMovBF4K1iM{)K-}!dj<4kT0RSlr&SqwPRedl?VR1(inN$ZP6F*S>E${Vc~ z@ru*2y*<1b%h{OU|UTo70Upy{|DXv-)6&8Qq)HjqVlV+3L@Gp zp6HtBB{vjKP*aJw@rvviptA*%9uO_hDLROqG_EVijO(Va1{!37w(L73Rle3|{6yHP_2Xo2!eSaDpMb3gXYn2J6vx7JX-nm^*7EMrw5q zr%_GM^xTDe)sc@m<;*G9+7f|`psG&y->}ao#)vpmFRQ~eKMG2u1GB#C@cLvh_7cb9 zy?o}yIdyyoI?aJ!y~jvhj35iI5%z8GJ^Hc3WtsssY&-N4!}R_#;a~mibA0#HZ^^!@ z1IRd74h?j~t^waLs%0wsCoq+&&6ZD9_NUPWo4h}Cn9`(3UTepQFj)#*Zp0B5mrVGwv45BS@6U*Z4x$zS15-+hjM z>)XGEfBPT&7RI5+ysnt$1(#*U$G5kbmsJ@&9)3-7yCBnPg4@q(h|7ZAK)qh`MwGV& zd+>g_evj-sI&bF+&+PtqJ6$hL&Zt1{H$S`iFExff*r37J2n3~5)y`Cosk}Ef*2Mdn z;Kgh2B?~q4r)i%>)A~SM(xwTh&L`@i@n;O#sL`h~s@?k;-0W}ubH#u?Yc6+vpJ?x9 zYQU-3!2LVdk#`)%8u`!EuqdSV%w$6*pw=%`Bh&x>zxxx;i#Q+}JXcfU=glXWcoU_B znu!EtzA|ZHJl+jDImzk8YyT@+6Tht2{cWiJeTgi>nhaQd2x~A zHVhVru@?u`)q#w`ppMncx?-87F&q?C3r)1`1dSn$B4IGxDg|?Be!@8l22AV|A}9|z zEo?g*b$6_2SfEj^tBZ)W6v33C)>5!{>Po$;ff;2~=s-)(*N!2;XL)Y9m44<-CD9;}GGctV7XAM)(DieL!3CQ0crq>C!TPs+-&z&XH#(S@<{1y?N!brVw+*?nQ0jNo!c&Sk z%VWNbp(Y)PvS+sEGSjqv8pnOHaMR_&;3c5xt7?Bkex@-7AMPK;L!s-kJ=Ca1-4`ql zY3#h-=RP91;d6&PXpxH`nxZwUMJnT8Dp(w)Cn!e)ub7|eHFC>2JWey_Rqo}}IN~_; z;=oT9=VeYbZrzVo)lGv4hoP5T-)V+t#%UbjAeroa-@$v2LqFp4_jmZ{=7b-A{sn&i z{tmCk9t_66_dEXvzW?dRxGXECWx;VAv959*dSeyA;-s6a5EgF4+Hm##+6N@>b!;}w zl%cR@L}wJ*JjSek_BtJi<~RKmOu#{LwFdg4KKcXTSY>IE@DkT_^eRy0>;(S8`^e z*6heks{`UDXLF0JQxWj!8!L~qKRF*T5N*?8Hgx_y_Cx*?Cee~>q@7C8bYpl%V$U1SVhJ1BvbCb=wPgSNX?8ZF=yad|v zM2F@;g!U!~^gI;zx$#ri-iP)PYWaLVjDR*hT6k`=*4u`xJmnR{7;J~jI!Cj+&RWc? z#GB3Q8sjTtvIT9kwWH#-HutNBEcBfIzZy_bhAwyc;^xiK(6b(5&!QM{4YxN4-1Lk_ zn<^vmICRK0FByG-5&|7nVLJWaisQMwAG7IHP6czBGj7*)2?0@8*=0@izd(yFD9USy z^~st?&Z1-}i}J`WymuZRz%bZkV5C3a_Z^HSMaV>#9P}Q9RNw@&tFE&c4;_|e#dKLP zn-yJeF^+vKR1xW`zR+g|XI`$#j@)>Qh!b0=qCWUYO*hd2H-ES^Ld*0>np#d>w466e z+TB1!W$&?olc82XWsHHzH`wqy(Y3N1!sL+zWB{9Go(4mwaK3{BcygXr`l>nx2dC%I zxG(b>uc=XeXsyF-PxR!V=6Ox`96=q$%_gRSjChB+s9z^-bggDzC)#)T&D8wvFhKxJ z>s+`DFY6kcu5suvEo-JRMQp+d(&V`?T+xN=lpzohZlmWcL?q>|*v>n+6)+tYniYVf zsa(v|4)I5n;-O6afMecHR~yf0egcjWJ+jV2#Jvcepu@aer7=r;g3QVX$#< z<-8nsKx>&zEOL!D!hw;(?9p3`(ONt@kIOuR2)H>M@%H8xKYxFR`)S6k@m@aeNkP^j z&gi)lGB>qwe5F65EwhlzT8yd;YtkVQ703!jnFw>cJkFo9dtro6*9qM6B|#all5($` z_}T^mdO@tp-Z!qIhPszNSr;`0w;Q%#-Jp3&j^$-lxPyjpqopY{mImCsM%1Xm0vD!w za977dra`04qdSQrOW{ zgWvsKeEI%E8XoJRrF+uB8oS>F5t?*VZZzu5O&-7Q`&0~#()pok9b`>0x9H(berAb+ zaSNu~iP_ymByGR%uerFlZtA)L%G-Rp)^I-21cRPHME4p~YJZ=x1D>_<^U~d?ZtkxKs?7J`Ob!z1Dxxd`_Ge^X~am3V>Q%YB63e5q-cKsTaI#IA+<#JUT2)lJ!#fN2a z3kEeO6KvRD*x|_%icnM(b1rjp^J11_vl71KHqZ1>)J3Ed2IJ6U7&_J82?E^r7T#n7 z&8%56wvP7U*eE1(rF;^%EdrcN#j>>~=G_Jzj}4j6nim?PMq*SFxri!;CP`9#q;XnS zC%;R9(yYnIs`*=-6SUNt-~y+VETL#3qdieYxZWIz|C*-hERF-A!r49>gA z0O+iNfXXI0WOHi1!!X3>hE|;E_A@OrA!+m}nyu6&Y!+9OJ0UANiTR9dYeyr zeX)18EG|keL}TtMFA9ni=hZtbZjDH_ z>^FqY5T?c9Fbsn9>;2PrR$f~Ni8;&4D4Qf{lL`oQOCeQ?JpT%!v687$JKs}A%PZGH z$!tq&k{fhHzMg{EU?5zhR<;|?Hv8~bRnt+^*tPW|-?$dabzRS!oSrZCqV}7|`>~A47s*Cu|0xkF0sAaFtLzu6(n$Wfe zxM}6wxGvPfUa>#w<;`pIrjF(fgX_9{z4E>xdU^A^OWVA1opv8m*k}4sIdsR4#23hr z#A1WWdBCl`siNsdU3opRYY$Pj(PqEmzC_>f=UcAlt$7SR1wEJSzGy0$^)#XWoz^AF ztvX}STZ_xenAQbj*H`9hSiSr<_? zB<2OZGF8H4-X>G+J-`bJLjLe3)2Pa~HDB>Bwpqyq-GS50A{fOG0mIP2GjO>q+DMAY z6dQny7=kHrAWp@)F4rC+>ML_piaAG1l!|PURx-~E4lbmm$Y#jDWt7g1p=doF2P~_@ z`8>gSmWZaI6N+PoZsiL##tr2rH(8Tj=#)qsCeE6r`~eagG*h}Dhhlo{`#7u&W>&aOvca%Boj4;X^FVpCSbafLonx_*_p zj5KfrA`4Hx7#eU^7)J9R0?I@wR^zgK;ikgbXOv*tptZrID^b!T`fL(nIrJ7#N8Y*` zGz6?3Y5LtTa~%6F$>6A*DHCaIj>+<0jVausqw!H}6jYxZ@{V1OQ*hER>S=zwOp$^f zjGWdWYcR-~BXJ{!N%`l&6`GJ}n1R_~^WV$#X3lv0v)$23(#w9x0%?R3Xcx24Uq@&XuK% z8Nu1GkM`+kTS#(qLQI~q4RLlC?hHrg@5{VkS)C*S*bWB>YBetl`o4?WVrwWh7Ma1v zygKw2=+*yW=sFl<@OZi4VVbb44u(27CywN^b1)?IFfyXu7sDVEhgrSJ_W*7qO5CX7 ziu3S{*39Q9vq|~l6|Ff^&+=i}9vyF+7TCO8brZ5N5#pNGP`b)Km00;Qt!lgbr26Du z)xtK4+$UsXEiVm1PL~b)SvBZyR6O}}3pqz8e=gOTz`RGu-7g|ra>^D8&j9Yuk2nm2 zXzF#q*IRLXUY)~Fzx)FK?9cxclXv*X-}@nc`+GmY>K!igg41|_a|@tMlLE+4qr)cB zzFCvJPbcNlS-);TbCfrCb>`YLw_c`1<$Kpf{N9=^BBvEip8xn)jS8kE$UI?Oo93`D(@x6RuCV z@(WHoOst)uNXb;n83t8TpXV?G8#6NS3!3Mv8s{ro)+pB z#Kfl8JXg<{7ayszVG^7p(NV*x4J31IZI2kykW7P6L}P_xiLo_VbCdFTIWv7i5!=GW z10k3x>1S3GHm4Cf%j(q3Nwu3~lKGHc_jPqz43Z+T(l7;cwC!+^i=cKYYPqC4PjNa~ zq!f}VRX7K>7qcs``pBqdQ!Qxts5WCS1k)|5EIECa>1@M*CTpH3(3t;EXOB zcM~c7&iUvxohZpI1V+}6Xvn!hheE?1j^8;K^Hn<&G`d1Bf4oc>`aZrt>i~rO4)ul7 zR9I})bo!|7%V8~8D~3e#>atuzg4HJ2X{KzD3*-h7q?C3Fnbnl`0q0$e_X-)q`2;?E zO?2lD*4n5orn({s4-KY@u(d;ZtDvzBQIC1k^ZNX24CH#boQ$%KPuT6>?KFYFPK zf9Ln-$MrLv>H+@-{lb2uqo>w82CVF$K{6A3A8L;cUzop96{qqNq^J=eb4}M*3=$i0 zm;ZmbsqVD8Pm{grss5~u0aUNfyESjn?dE)*3=yPlG0x9G@wj>3x%s(G%{D2G*YVlg zw|IAdkGu0Dj>Cvorz6gn2|xMrm-zYJJN*3a1Ag%4Eq>>_-^ZKd2_GIG(OZjGhXd{} z=eYOg^-b7cjd~4iR|2=Y-Mx?AY7DsuUv8kgB|Czv7^4yi&tsLg>vYrDX0=c`%E0D6 ztlgox>3Vl)i-I|+|o0i zc#RcQAlTeSxr-soR^r>W%(W6_gi0r^4OL<1Lp%jRGne~MHDxF*4ERdBy(NtVv#7$_ zJ)hte<{p%}-*Iy?n!C8x7bY}P$J2o6vcOn_zPAx&j7_MKiLTMF&5Kecoe0f?8*FTv z3}sOpsd0?W_y5P*n>AaqT<2lm%FL?T`wZQEXJQ~h5C{ntCEAh)(T=dfFAj$z{NgwN zRln##ep4tyGG&@FK?nqJae+JaaE3ipW#;ljPAjvr_BnmQjv#US^f_nmU6qx&*7_y} zRBC8-z_B=)I{9=BQnUzH1up;$u~8=w%z)e^2HKL#F*ra#PJ-*TaPqEe=6pS}D>aww z*0N^GVJ3^pgvte)YjhU&(a1s^9tpKe=2dCBnTlAW1FGob^>dNVt+HEp(E=Wi=c2(> zDk8Gc>?Z276H3C|K_`07f?OYJNoU+1QjB22qm>SbVs;?Gn*u5=b&tg;2Lhj{iGN1MVM>cypRD7=^3dglQb2KHnDy>`3KNs_Z1*GmJD! zWl@MY&O;ee6b`HC8tzlPk1B{>YW4$L0kOePuEmkw92?klsBp~45{rYr-X|mlUlyXHHXzoJ0;Xod4c{Ew^c<(Zb zCF4aE&2HF7qETA*A4snqm^l*-cy~NNd6fr4rD+e-Yv(=2AvC{tj|Y5n|A3QSf`5VY zc$gO$ZNi`hz;RwOiheat7|a+X4nDk(M75^$d{sKVw&?0<@So0ZY!9`377G8K{7e#W zBZa(y6pA)sGcAC&?^m^-JVoiF?-a$`44FD|!Y$B&>c_2zde^_F6NSR{=OxKRs|=QU zZ=(5kv}j-X|ID;WAH8^qt7*XRUcbR# z{q|S*^8OwkjQ{!vKf-4(U*Y!ZD*yc12C2(pEp~&+^-zw_g9ke_26_EPVMQ3`n$|~X z&Sox--OTIXQkjm)nz9W-rjAulr%CRiTry&b+6&zDYCU5#`9``Xf!rdF4b^mo`UvYs@==a8d5Fwqs{d9(0I6|iCvMl(3disTe#)hI^!8wQhZouQ?IlBk%cO#rD^xxE!CMkg; z1*16YP?d;G3GSa%ISe;qhJq%#=whtmBqlDxq}Qz)_?&&r*zYGiJRWg>f5N<2jDx}L z?GDp4U@!(tbb_u;aH45Z%#KZm08zs}NtvU%pui;En>|1Vg|?ltYbD21QF%zRSWZUH zC@_Win8o@`xz<`kf$-(m?{GTX9Osh`O+^GVC|vEQPy~CzFqGyzxx{L{vB3$AVN1`V zsG*g6i`0PTMiZi$b8=-;Mr*yRX}}O!*pktj8abD#dx1&}kL+@skVok#o^*~{3?dx) ztkp7618kkkYhQ~-R92VtV4j#yOfGFtyIb z!BKk#mPL4Xf55kQk2szeT<=HhrU6C|@SYv%2m&yEVhR9cs_tGc@ z>p3>Clm~{)93sdNWCr(#6TZHG!2R)rqqSJ9g9GE`{tAi|?vE!}>u_3@TzK71Q;-N` zxASuNQ#4AfSe~8X>$puY4L0gd~PoX;9%;4>xS$`X0?JiBViA&4qL!0}HJV2w0VO1J7?qrKs@qN9zo8Ns{tQ}&M)&Kxcet7MxSn=6o@Z#K@bdZw z)-EExtT4uM@lIO>@fKfuJG!N&IBzX&u4-s0>ynwU{drMqCf3Z(1&CcUHI?w@X@}_j zfgtx%Ro0$qRh-x+0@^T$AQ_v)&mTxu6yGg(T>9=1s%*-MH;x6ao*{|jgSO4kHs1sH z8m&vwKJW37-=pVyU!Z)`GI`UhEgPX!G;l>K53#McIl<&)XB?L^?v^tS%Nal54=^c% zM|;BT7F?ewv^F4td7~C*?>k&57`5Hh?}QGSdn62JpRAG{ar*oLx$?%u;BjT zjKL^qqi}OQg(gI3VU1D2HJR%oDrq5os-+W((Y3!vHXZZaqfv9t9)?7rxS$Ho`#_EL z0jRz?bW z0H?FX6%ouhMBQqP`-!gRMk&lo&`2gqq|)K{vG;<)zRs!UxIVKpHZvTTaQZ|^PI_|} zY3uAmL{@S|KP(F#4=0?WDcj9{7uXff2dP0q6{*Q22jTj9!Zez!7iVT1PIJu1HZcn| z>>b{F*!235Hf?R8%$6a9Dz4+Y&iGY=lIER5BC)2gS?5=-&xvs&>of(GbCB01OG3l@ z(F)sVDpiQt=FXj5(MifaIHO9^pGilWa*A!ziU3wmLUZXn2|7L(y$qt$w`Z{84zzS( zLfY?OvT4_<$%@}PjRVeeS>GX(INYasaTv!Dex5^28>O|XV7-U44*T5%>l_|V=MZBw zj@XR`hqJ}vJ#ME7$0fMl7USxWL!8nAg0p^bZl@rXKEj?XKD>Pq%;TuU{8{fX+XaITNRf|@`&#Shdx1tbPW_8d@7X!C%ZsQX{8J?h(|H0FnP1|+T6GJ*K z#Unkn$$ytVnHr`96>b3t)pu(>72WUJL71XO!4Ho9Qs+}T_vB|{gbvx8{34JAu9XCuZ=);W*UX~ymCJ~wsqY@vv7cYns!)d*u0 zmL zy3k`hz;Xjld4EmXWv$AZvDQ`5PmA-=Tqw_>Xbv|z(5ERIkd32RAv_R`Z)%*4DG?)s z3Ax8tyI=;kEEdi=oT5AJG#W8Q7qL6caD?FcOT}!5ga$Yu@D!t(90n7zn5_%1W1Yh= z=v*)qopv$kF*=y!*obaxijK*Wf-IhI>q4gTFa-92jY2}B1M=wD=XI+T;pS=zjhS`1 z$x8-d&I1+}NLs+`T3ze1NES*m)<>o1Q|LS+vo!&vgHc#)Xk^Dh;k<;w1QFrkaKhXB zQ*IpR#ewF4=3nn8jHAJ6Ua%Vn=oq(=wWFSKT9(*gan7*~8Ph7G)9L-C=628;tWyoZ z*iU27i!)b8W_FHFCJ|!q^biqW?5o$z1BeD$tw?(XUZ{p3dB_#7o#|z)b4#k>n$ilY z{;qS@cA;X1_>6F)7{MjkN6o)hG1J&O=-B*I%$?Tp9;dGjNdw)a(X*V+2Pz5J$9n-` z0@hGeCBl;Hwk^XGruw?Y-;s>05_9Nyo~!*T4Lu2jnJpd;M@)mk825+6dB$kMfNUBJ z6bLhWe0~3b>uF4a7@X`e>%g@QvWjo-?qkeWU;-GebN!TtpjxSLek@^cVs?GAFKo~$ z-=q0OW@w<2D9An3SGo%BG6LJeyEfhtI15oXZnEt=r7r=|88CRjDYnlBKGg?oizIJ(5vlxqby07ikcH7}RpjaNwd>=jaJiQ!wRFx1siO zF{?|=mEcDb_a77^z`+=t<{4+Z;AXeSV1l;(KmPjf@%h`=Fk0a+|MaK$(Wjr{@pQ(U z`#W6k_Lwc<;dIOc_QkopC#ChL6hW~GbxN)CDRFCC4c8MWk>@qjl{ehwOMWu1T<4e@ z1U<`*1R%kk0a4W(u4%G)mRX45_H_S4wu=jZXO9PtgebQF6eT~X6;ZIWuXJh7ZGkV=wg&4 zsHo}2CljO8JgyK(fePe8J(}Tg)a!D}fn@Q}5@l1HVbG<(*CE;^70F+G@fII{e1q?O z?7exR7GS?X$ENT4dcv!h`=D2| zE=LlPB6!D`t;OTR8QwFF(b+nEUblGMosJkMQw)>D>nE*f<-&|QLPr5OpOCUQFqn!qEe*gpiD)|oOUhRa)8%3qrPLrMq`W>bsOUMlRBKuD6X@>9 z#%d~3hy$H4Ov%n?i_HM$f=QVI%8zDAqmGv3gj!Sxi^7%iGOhSnNdc$jLX;36C0be{40 z-4UlHAola(aI+u7nlm~y2{CWiS`X)DTwhJ0z%vS(RUQBUz-UZ-pXFJ|`P6Bcs3|i- zh-!2)H5e+v#GTz!B`HO^bX06%Ji>F-D?8Y8Wq7|vJGm*aW0T#XDP9`7lPu&LN)gee zDR40o%L7AE`0y$+U~7-&&UsIQ?#^XTh&kh0>1+nn1v)Bv)P@F?s&Zy?v(&*&Fl$c} zO0zhJ*@hUSz%rm559gV2JkPMs;r40|0^xQyfjKm-1mM+vkHb9U{&k5%0QjZ5MGHiq|A~=OU7Atjz@%Exv?y@)Kw%U3l1NV@Sw#^DF79ZM>4|yq``21~qgG#uI)? z-v!ZqDl+*w_w5P9KBZJt$rSMeCXnW6PdF@RXr(Y}gTrzT6lk~L?Q+1;Eig*KgD_jh z%og`_#_rzWv)d1HV#c5ih&&$72N956cYMUFt6RJ|+~M(j!e=i&%qbl; zJE?t)J3c*f>5E7!GmIwYH^!pQ*EIHwnq~oN*<3u=qlq-p3lMih-w^F^iW6zcEV;lO zbXSnh83A^Uz#i2!j;i<)RZWyRYPu3y=UvpAc!yNC=A6;oVN+FDE{nx91x?{R$M_=( z2=F*+{PZWEU>FV91IObWO@ZQKGAcXi%4i~rl;uPL<|@RQE2=jUt#$G$Th~Ah8cGGz z9Tzo|-g{`HG3ta!`bfny!SPq=sHt^$_3{db!&&5gQ9yPKLAjuz^a*VjhF#tl&1R!I*;?bU>XMuNY@CN{;m|o{907g|1`<> z-iKj?rX~EhkkLKE(S|Ag_6XBt}?Sg#lBC~&s1VVP@%*o*ILgdS53v!!gJ9Q zs;JR+DZVIbNEB&|1Hu0zxiwkbcKJGOpHpzq0d8m+R{pBoU z5eZe$$`AolGY4|C7#FwiBp={70j;P`@meP5c8(ELS}F0n#agTqetPA*K+Ra1Us3fl zyeeYqN?3{Jxx9=oY!>FgCgC>v z5!Qv_%QP6=pJ!0qb9bZ3dx}YBSH04MgO>v+vRf_|Fi$jD&*}ZR-V-me=2j9T5#6~S zzOK#7R;Ei;Z_*KgN#v}B=~^!g+WOe<>6*%YL#)kfOHVbz&nsNZwc*B(NXD9_7%t)B zkHQ9$12!*2YpaO9C~=@{HpW#GETb2K@g+xNNjS@4r^7={6d`G_BU^f9cn`23sK z_?s_&gU|2Z;g4Uw!hipxe}w%wVX+qX$1?!J)i{9^VYXXZ-KTh;+#mS#x*Oj-Co3`q zR|s$TkD|F{ft0b>Z|b-rv^EUY0CFXtTUpF*<;AX}kXFl(=&}Ij*-a_EKU?C`bJ9}y z->c#4?*s4G(DmHIdK`6baABEc&RdhDjo0NG&CiCQ-|^ahr>W$7+_PuBgwH>u#3mnd z>^){{19IyeZl)a$IOE&XBMxo`0XWzN79J|r%8nUl??LdmUru;^yoV;jXa?L&SAg^Q z?cKL{TxNWB^%5UlzrfYF!{hk~tq5N}ypHCYz<%1*EMjWd1(GS-YPG2ztP%lczaK;s z6&6E<>~#j9$;DT-u{ime5{n?CEXD9BO>R%(JbX;z%@pjEF#@Y~$}?vWu&S#R@u1|o9E^F3`)axhDmEpAZ9|*9ujz&XY2*)rD7Im zqO1h7rL0!V6jo{|DG~uo>;o_NL8^;!_8yMPJ*6V*&1+f-6RYGXozNpKa-mr?F-h~C zZkLJLzi`dUGvj^9S7mkrKx5RwRFtD26b17u_CbTG(|ZEIX$X<%Z`q{A=NK3{;Gv49 zSH#&&wSinVQ}dj)>56I! zD;t2wAgCO=sYBz!dJx1dsZHhUUw0DRP|%R2D6%;(iAh}T*F7N1d!OCMQ)6ESQcs|K z0#&UB2C)eD*+E}{qeEnJ?{6t?s~UH!FyiN*_1?3H2P-Bpi8eZoW8Pbm7BNT~!h<<4 z7DfkmamOBCy}gf(vWAXy>VyREh7npR3|eEhwn`ffWJdx)Wy*cNZd%VUQ@%*Yy>*Liu-99721aN``TdQ zubgSy8AO)OtUhCeomiSz)%UmZBtfHV1lGdeOji*cF&0g5-eW>UU6^4P>Z4aB^38(- zt|xWX4%u`C^9Sq=o!-dJm}t7ewBy~?)$pDIpt<-FY7d%}fStA=1h=(@nsQ%z7c zNTY4U_PKQ2rl+QhG~EjnrfS~7+bjU)OT>{i6KS;%Qs=HwIUvc`jX3vME}f>DJ81TE z=pOIssq{1x<%xUNnh|eNUNT!YFraLVdtAJQQ3^(ln9K;q1Mchz4|c{FBQ4W8k61%! zQqWPt_4(bm7`4Hu2fUnvPsZ;b-{I|ig7psjVZ_b2$Dj;;^X5zZ=KeLlcl`=~`}%kI zM<0D3SHrH$hAP00YUsf&|EYXR^KgHLvo>ePDix@OC87h3BUp&dk%r@?kPlDSIA~NB z`mq_!=qz2f`Oc2A)ad8p_gm}Bw7)}p7*gJpKp~k+QQ;zeG#6aL&_&YpoCn}tKCQIK z)}3byySSiP@eI4TtY;m^0oo~8>v29Wn9mj*HG#$iDrccyN0($O4R*@XOms;SQ9`7G z^K`qRW>ca_yHz^SVs*^2q^M=3+`yr@)yABz)UZ7~9sv->F*pT-8S@fdPMr_b)A&1F z)KuoiQvuf6V$u|gpJb|~m5Lhg8ii!`fqov>lShm+N(DI=?Z)C5%6o9sjh147a$Y3& za`>dnZAQb8qmPO%v{>cm2q7You_~O9Bl3udEL-P7)1EVP1By8qM5K~pvsW~uL=?1@ zX`rH2ABRlbgq#g#2y1L{5pfV~4yI@-7Cwiy4jLLW9M0C^c$#s&pP&X4 zsCJR!N)5^#{#)hRQtmP7-jgZ=I!1|9JOBV707*naRAywu1`>V%GP}3Z^@``0sabP! zf77BPWLjQQ?;kOa29q&W_d`krgB}+Q zvKHs@>iQaw$1|>X6P))rFE&uAod>hWG#Y3X4Y0s?b$y-q939tWFTBQ|QP}YF<1%QC zJzc!Sd5_yHi90B5llUQLwKd(mcS{?`PkJUDx<}!pZ6@UVFbiolPhea$wpl}U8x?EU z$Gbxbr$%F4nqFQF1h`r|(y>_Hhak6#bzF{9Q`KiOT4m3Tka2Dilgpvwc6Gbo<3IfJ zm-sio`2~J_`wIWZfBTnV_^5!__jj0v0lQ(!&gsF75ICIAk^OPO7-=&K=9!Jzv!@=s zJv?bl*Se-meEFrh$#`8eTGr1@B}V~oUZ=iyTdSGv(k}XooLXw|ccA3sB z@a#n44SDhEOlZzMuJp>hra-R%ZSF1y5-2dOdf^_8iC633y#^28^*TS}g`{DcG)QDH zMm=CO2G%<$Qkcw$y&iCI3oJ8C+_!9UrB6&q2F}jm?fD3!2-X8%KE8!f8t>*KX76#b z3%ChhN{l#>p+X8Ta`1^niD#M_diliVanfjGh>}$fLzP+@IjA3nm9j zG)HLAf#d`jqkA%CFf|N0XVha64#%}wR@4}-vfO%8{z5@aMR!Y!E$Fh0>q!k_X+GDvvH%!DZtyVZNcpxU1}MGvY~&Sm*Wnt~TTMbu0~NLe*#|JLrU0-&Prs>z zYZ@Q86zh{>lOj?ZJNQU%Zwe$=o4Ux)%p%5!L~{U?0Z{gENZ(rx(W%NX*2EFfk5oqI z!A9qyO3EruO{*pq>69GZfdhhEXywB3IyS^UIS#9`&VxqQ1oS#amxT4@p%Cc7X*i&j zinLsU_qNukaI@h7IiI+u*`had^~Ql$|VZC6B1 znME{-AX3Yrg;FFcuNEhV`hR!s)L@I2y{9Xl`Y3=i{O-#dDp- z3|g1c`Pt!OmS>cH&w8^=(uzD-*x@%-c+uca*gY6X>_@c7vq{8ux6)6jEmc$K^fjx!~r6) zC#L5zGDnzV@`1uQn8@yOIGj%SKmY1)@Vmzc{7--S6a4J^Kg41!#zEt&cW?3W%U77K z$KiB>_l#*6u%D*j`LN6#Tk7^Pr%n^J?mZ>TvC2}ul%6I*12L#z-pCV{=0VMHp~Vn; z0TQ`21!0p-24z__rcO5 zNas6_6lT|KI;C-Ii{~C8YO%gsxTK;rHPGwfK1I_V)fALT3+a>0F=xUWV{+sWQP8EH zjxmSMCu1iM%$9qsqN_X^7rjF0Z_kHQ!!G}Y5&LJW~sBJVC3 z!{}Ui(gx*&qaH^As?c>xJ?vx7W})*26DgNb$z)a~ji^Gi8JZWZ2-XHFx{YY%IGBJc z8&&0Y)(sHyHLUfcsfqW1jScW-&SxyhxlE68yC$^15|ONm>?V)0OrR(i@nQX`JfP9h zHCP%*5Q{K)q5rK0FY#c^Zr4CI^a|!wD%-`g_i$VybBdTh{v8DYt{4KV$A~i$3Qnjm zWmjrX1a=%GP5JXPSeHgdH`Qr%#)v$_)_oHKDV9E3$qk&54DP~E2&^K~6`wyayaNU^ zz-WafQuBuxGqc!GAdM#F6BibWi}6bo1K6Dn`e&mR7Uyt(I74fN4_;o^rcIrbf<;3n z3+gn)le% zq+_bgY390!1UIokagR*;*eDY~^a5~6_OdybZ;FBD^OPo*vSFV09&Y7wy%8VLh4_H3 z0SvQvU<>S_qDEh-p!r1{B6-jF_~s^R%T4wMNcq1YU_Xv9T4Nf5>+Lw0IItsF&za#8 zVvtz4JyT{qv>Dm*`p|npF5dwT<0dEcBFzFhWG{P!^ASqVUd!G%N^Vi73M}4qo#?f1xW0H;I6L!kr7q5ShclM0EHt+!6oeoe$ z*bmc_&F?y~fGfmU`bU{*GBATKnVr!vDd$28W07>vGY!sjrqe)t$d+>In+#oQOiH++ zXYEwcsK_9376*7}8j=}Ty+VUab(wBT=Vu0kZFsMzGXR|PS>gqEOX)~$6&tZO0sT%* z&u%g}oo)PG3&4c8v&F;146Q?BrF0CAR)BNC5b1E3F$^kPcLXCLaL^C4QQ7&n20n_D zuPlCPC7aXmKEw(H3Utt&E=$yGM)zTBU5GAGB|?TpM)EXLZUmOahDGh;#v8vs>A%xw zOnc0-xVUCYhHP4xDdM2e+6ywkKHX$5%sYZRw~xq+HX7_zm1~RSu8TmGO&P7(K!ioc zCDMQ-H1MQK5$>FWwG3laUZm;wRGLNv%5sf{hIk}{q=8m3ZVF&4;w2)wNU98msL7*u zJ_!Cpv#$VMp7wO@m;vh?Y&0b^qrs>(oDRspiLRZ_`%Fjns0S8YnLdc=AJuyI3Uv^N zM>1?nPTwwSIgKHBPx(OC;`N*l10$tM+)B`04j2cG#i@vrxqPl9R2lJ7>?VWLY~h!< z&M9brAC712cVo2JM-`POKcw@;U7I?Zxx!E=gMu)?2nEY|UUHOGLcNnAoQ8t+T+2T~ zSK1H_QR(o~^{wJg#OIegWkQ}A5N_A&+A%#}R27VbE{KNm+L4QZ6qg=M<9yu50(I#! z2+11=VmYYN*EA`33Pw>%!aDEmEYv{)(dL#GT`!=@^QG#zk`HfhqVu!MuIomtEO$7b zXY8jDw^w^u?_#vl8H#{G2mP<6V$wzwuAfnXI>zu-@yO5Y&lvwue=g7?H*-2WUc0gu zZ5r8_nqE9Q6h}r#bWxARwJ=(+G3TyQlZPKK%UJu>o_`R=uAXSG@Mo z0`q2UWD6*&BnhgTE?ThwWLy(9$a@x3h+%RmAcaFYf@J6I<2`=y)mQlZ?hXFOpZsHd z`sxJ;3a5DvOr)4l@OV7I3?p=OPrpAMgVs(PC{iMxjH;S)-TX-Y{Q7>#6b*^M`L;89 z7D`w%M|vFERMayyJ9ugq;5$qTD&rpZu}J35`iwu%8#%bmYszR(qOP~}v*%?+HQu70 zPk6;&GKSjxH+nzKYHkpzo0iZvY@p|y4SmgXRpBu=gO}%-DsW}J%9)8m*ou*(4)^^# zY~jEkKs{goK$1yh>T}$%Qk^q9i@h2#st^nKcs}Fc&amF$%8WSq;NheJEEM*AMGB(= zZiWepfunP9-r?jujz07kd#y1jjXQh7H*X$e?3%*ea>ToHh^G9r55M>1Ag4>2s7$dc z1Xv5IV2l>=RHJGPc&QOjzc&_j)kByls#IczREMqBsxPo+jWqu%+T_GhrFuiiKfk-Q zGB_g`YsyM%Vh}e}u9**t2;Mq89%qb$320PowloEV(>ezGC7ZjLL1iQ>AfS{dt}hL@45~#DopkbnZ+S$~o z1e81t4^o7|pf#XPem$uX07ob7h@LEqkLX9B{RWM=IQckz{&*O8Aeb7L0Stk`j0AyV)mH!RzPYJ^x$`e}WeV)`N z)<9}tq@BBpvV!2e%%F+#AS#=ErT6YRXow^7It@e6568yMdtVJua&zxf2C~T!O3Ac~ zm?aohvUOveQ1Yw`6l|5BzZ8uYSPTR}A%W>_j2grMBIG2yIQU?kmtZ<~v!9|_T3{-8 z_85!~In>r+974R(IYuUFt)l~Ph#|Aq;WRI}+3Tz?O@o?bK9=%c!`oAk8HAqK@DYpW zEzdkgQ$SThcC}BEgh4B?3YD()GEOQy1EbbuaF|Tm0FY-mD)fAc2NA_#UvgznZrU=N zSPsf;j2H;g1{q;`W zi@A}C&#p?;e;2g#);kP2uh~U3TQE*IW!Q?sa0P!cRLpa)9dOG4bT=9}&yf}`;^t!c zY_x*b1n0v#%@PFm0BaZ>&@MvAgig<|VT~Vr_yK~rTUHI&TG+E ztzX=?CFoE^!(kQ0*7su6VslVIXkl(%(UEz@I=Ms-Vdg#yqwl!5YTVcHViF@2;v)g>bSd*2vKM+E zJutn0?5UF;Y&LIvq zPF#?wc^Sb<6w2zL6${+6Kyx^|Kc?5!no3+xoD>_Qa5`JqcgLVHjYx4okOKuW3`hbk zs@GgrCP~Rzmt<57FhnY4P0_4*9E-OZRjjj`qlHcSd|FQa(*K4=C=Cb{HD+v3mCJU+ z6bpHvOHJWV_pdZc;%B+OH;5E)=|aaz<0zt0H{t6j)uO)*;zE;|XQS58^f&M~aBaYGL*2%{($rp^myiqKrO z6iePmENC3L=$nxkc+d49x1_? z5wAz2iY{B#)S*QTCYVcOm|rW71-(x+VqXSe@pov_^&=?$zrOs6-RC>IC3`SZakFYs7K=1ZcWAx@)bA1HF)v_-J5vvkNq> zX)q-ckjwCN#V$}1jcMDh??kSB{`8-KizX~>7f2*pl%D>&?8y~xfvNg8pmk)W?Lz#0 za)x_I5nZ6$ia~lsPvefS5VsV_K5d=2Mb~Nsh#KQUG+O*~ScteA|wYISm^>Ql_zxSu=vlwJ6If3C{s`u!j9_e0qVY6yM1uNNW4;l? zwIRYKNPmsy9HM{N?Rzt}?TOd%2okmAZIRi&0Pc9W5AR^Dg;p9AO>cL@ zh}VzzIJ*V2UEtW`=oVNG(usvV6o3H=11b32LlPdAGtS;epPBF!IB`A_f=^DWd*9jv zJ{hkuX@d`TS0E5ZZE&^=tg{%k$-vGy7|h2RmQ<)gbc8vSiX*31VwD=;WPX5%95P+9 z$Y|CJ6?gqrP#)LL;8bU?v2aF_#j`f*=@C>jIRy%0m?*B>T7b2)WKA=o3W4T^cdQB8{=6Qx#;#jnaXjmG6#6ml1xP5F&ov$-pr@;ul zLu1$yDakB`0|~ug;o6-pjDm_ANqL$U>*{l_G8rpcRS#~m9iqlx=+V<<4@FgA82D&f zw^$47!f+;O(~jo_gH|G|InemC+q-96?L!9TvbfM}`k0CBgMN23rq++6&;Yq9YPoe6 zjEs&voRXQr!d*MXp{3`y3=Fx6)u2@~CaLCGqn3=@euZ#S*C^twuTFfjWN=@6sJQU*0=w|V3tMnauGyuHEIZVG01o{=~QgCdN> zurfNLRWQc|Go>djoZq8R>Qan5zegJRbT!y*v-v&%bX9I%C|I`~mA4S>rdA-{{*~O& z+XN!aT!X#bL7tdM;M*5`tDmX!EAd3abHXFCKvnjy7olDF9b+=12B+FS`1Hss(=C@{BL9*8v?zm)rwS9fIXy4uNw+jtzeUIw0nYxi&>x$ii;r(!!m-2M@e$K7;9)*t)Eb}Myu`!#gh#jF z=oZ}QA3{Cpmw#F0$x%i z1&ru9Mhis^WHg3J4x#rCXB>|~D1LoC;>C+Sj1I9#A<#KR8j+afrXwv-yj6BNrck}B zLYdatq=&n;ffp{wiDp$OKFEKi@6aAco znG_MP0_T$fm-ieUjcX)3{jPBsMEsB_jp7ati+Ws)&l9x4e6 zI~oUB5JN##F0vF4CLhho5|xjAXo!6@{0rt3IpT848%^kNaUQOBC z=kq;WQd>*Yw%G1oj|SNT`cBlSjgQ&^K|7Esb6DnH%$2Q?hU@Ue~n+>e~V9!_jq@Dgi?f)opC+xpg?%F zzrpo5fr#+gbOQz8>>Vbhv9N;&LN0KmI0uGT@cjS>Gb~0*>WR{rXol&SF(`!(^$wri zyuw!x@8FqnHH^8h`R(0n{N1}RF)4!wJHxTVk8eN1&A7vh=?YqDVQZ&C9GlYB9%gzu z$SjdHlu5UW|3*TW&do!^2=v*O)+{$+_D%43GXbaWq7#|DQri`qQ&QB-u6j2647!{^l1>r_Z+ zoZ?hd(-;HfBiC?(t#I;yKB z8I-Ae&Wlxq)~GuTj)(~3XjW@a3FP?ZdKZdrk)NEt(Rq(?3^81uQ=_ef^Ernot*GhN z8ZaWpNh>8N+%b0u_4N(NVLiR)swv@2+0Joi;%+C3ZIV35wO1jXVy}RR8%j-mif&4I zxZ3F85N*<+BG8u~9#44paE$B#h2tD_93yMY)>YZY$pM`d!6;MBVu=VwQ>M6Xh55-s zlO&ZXyCeF7Vv=B#q#Wm{rz1sBUS$_V2*h#4;E)-g35^sC7oTM(or9T@FG9)Fttgtz z72Cmk7aZLwXxqGZ(Wx9Ef0+wK*|VhoQuah3Ktx4xZG@T872iy`I5xRO>zZ4AE);mj zI?|xzvK11SYCy_OlmwE1r|gVsU8u_#G|2!^iZCxrH5HfNpRWTKKC7?h9tB}ybf$$70LXJLioFM#pobG+@U^iIfKh@ z>F5jjiQ=5`eA6s?`lCP$&y!+*z=H;BY?S-~7M-58fP)__L2c!Jq%&hj1L&AnzU@ptZq%n&3U-a5{xd-?mBT z3k$Dx41P8mMwizVZ!C?A-gteCp1C*7;g#;a-~2T3T(q7oN!E&_yYq8c4ZY8Msp+Au zMY_S<;0}3WZEAd>e|!$wN!x$#npiy-A?;j0zJwA#cZhbWK}JU}EIG03=2!Ntf4HcH zJ5rBl4xnNkOgfy4&PB-_E0}0<`I|Ri;q~zW2Ybfi9e#UwhhILv!9awUh2Bx=IdYMR}bIf(FLIK&d<;w+?*a_ zemUc2+G8>!K#b8CEQ_sr6EI+?vshA$16S73GEkjXk{J?zr&PR%7s{w`GtIA(o3_{w zD~3%!_M)DQt?3*seMF*gWiR0DR#-Wxik*S9s{TzY4R$HtIH0D}ZU_bYpabF<-ENIh zm>0KhUC>$gj$CI8sd^pXy=q+egKsp&Q&z6fW7eH)|6^IxQk3mP3 zT$8igIERL09CW5!B09^e=q|o3KfXqCQ4 zRcN;r6ljP>fvRRf$JnykAdHBxEH)$Y!LX>N#h%VfzOE@JcD61wmV?3=8xgG)uoOm# zV~^9>;r5DicB}WnwJjOYrnnp+j9og|b>z!jW?-80vR?++l#ZSO!9URK5 z2&^W@JH>QGlQLiB2FoauI-Ee766K6*rmO`o20fu^lh3B&kk98GXJ$b)H1f61Hm_+F zTy|s(lurW?s0PVvkj-=r#dTIXpy})>nD3dykl`xHs0`ZVR=>t%yfjMWA}cI{c2K%F0afi zYa`JbsjeAJ+@<`rjz0N$l@7gp0rji0RWF#@$WO9zS0&*=K#jQ>&=+GpRhUaioS&e} z3T7E^H2c-ttQ}wF1eKa1rX3Ddz8AfIk+7ocmsyWsp)?IUbwPbGxQ+j-wZ>_l@#6Xl zzx@60@o#_qOML(K1^(B+_^P4FT>^^%1g!w)X!WV9>(YyKX?hfH zk@>c;e}b-0#A|=3Cx<>AV1PQ;xM2%c%$oN8@j92BdpX*xf2S%FJ!_%GuD<>pT%)tC zFHTKv?YBX6xe=x2K5XJc8%$=)DIqF{m-&g5Ue`vxA27vmm3yaLYN;)~-Q z9{mDdfUi#noV~@1-455oE_hvd#wXV=@h7K8e0zEXdBV|K9NdCY8O-d$fIS+@c@ADE z#Xl;&BL&MrBmCeN7*cpQAMn9+joax8_s0YN=G))m7Z0y7>d>@Xc>MF1-@~hMhtD71 z0tCD}AMmMNurp(%S5qYK`og#bV47F&>^7)H-_%SQMAl5VU4}o}n}e7ti2soQekLgAO^FwK$s&b_ef$ z%$@BWe3FtH5fl-}!_n+0qs9%2osep!Xg!}3r*NQ?Qp_afm}y+-O=bwqZbB)S#RhF) zjz}6-h^c&Fr3N|Qi-;a604gHR)dogI1#F#%)CN<-!I#Es+}YR?eVoPGlPFb=0m=H#zIEUuArTs6ns1Q?_NLwcW{0d_85>$CL@O2pMb zK&3)GGkR4)YEkr@#cjB(oiuPs&3=l9+@)}koV!ybiXA=O@6nvf1zXLF;Al_ag_3<65tS2s>1IfrnIvOX()IQBrG&l|W zfPg@a(u{er@H%K`D-rVCX%U2bf~M4b<14>vEAVHR}1C$wqj-G9Rta_&(P? z6}f32n6$>>bi%*>>;H(uJmbIl;~(LZ7ccQ}I^oszHKxJfa5~}b!$W2-g*{!t7~Pm) zk$Atn*1v8JC_4GDa!|uhG(g?#*=Mf73M}M{%vjkhw~ceR`R$Ia>SveQb<&3xFQ9+4 z-49+G5%JbOv2IA2oCXJ3C{iJZwBucUt4*i|38X z=?C@DboMSJW+;uF9-xTu_V|FmzxxKiKivbwxYi?PZ}GxR`16l{2+tn>>D^cO@vy@e z^BXKSAij&6Gt%rmM+szVfN3k5s%>^f5z%dasWONtF&zx1V#rdu7gS~f6?)qm(X`ZL zr9|V`F~e%Jh?_;P>S{Nl9_3({Xcss}4-jV^vqy;VNOaGbbP`Y}LvTykJc$&>F&rF8 zck9I@i6keVP&Kw33t<)M-AtU#f*|QmRO@_FQ&%=!UR{sDgiwTe4pB|RU;q&2#Rezc zsB<+&W51uONnJ8s@-hcHA&?YI%*gqS>;&ClK%q#L=4@50#DjfQIvYq~g}?^{Io~2u ztKnwJaVm9vf^kjN_9N*uJSVlz?o1#>bRy%E7&g?Oepn&TN0 zvW-hfe3;&Fn*MW%*$A;pD(+D+M_C-mlrkwrR8r+u9~)lp1H&T?ij?57>qrCgaR`%H z92xx=%z`vA6@!e{R4$zhnaJQfkfA6Vtn;}b&lFFgClAkq2P;|?l8lxD-JC@fqSDwW zhKz^uDvz0iu3c5bsf5zIXf*ix{t-rNOoPE_bk@M9{AC^E3PJk#1$e za5N2B3weS!3vRl|^Tb6OBfdicnz2mgrxj~?!E--8q@}xV{E`$KSZh0bXccd9q}SJC zQ$O4oEb)aZk{K7xx1=FhQ-7@b@~&qQHwFB<$aY?&1W+2zQbtTP)%RS+{&eZ!qRvr7 z__x3KIezi_Yut_#{<|Ol1fRbA0E@GjIK+v}ODGtP)-e_^Fp?H$aXQb{`Lbf5y-y>$ zb{|5W`?_KHM6|(PSby)j$)L4q-$s*JH?MuPuwFj2IW?*q<(}bJo7II4Gc4*$qn|s9 zdU9`|s`k3r{PG4fm)=JSP8$d)wvDj((i>^H+te9~J%eUmfjXV$x!X^#FA6tM9Cw!C zCQ|>D3|M}9?>M2pZLwA=reK6##85CEJuNfdo*r>C?yxrlc4ok7nPHU1C)Y1=uxC6h zGp_Z3H_I8nczBJs^AZ2_qaWahuRg|M2EI7F#ZO*-78=(1gm30Uj>>f$&gPU}nwY{q znsTxa={KonyH*2U4tqHE_{H7VFhuy(=?()a?3DqSF_FPfUwn$IVTaG(eT7e_8@$?G z<9CO*LAI$i&NfOAwF#0tapBDB!m)H_r|yi2ThkSwq2f>#FXrYsiFI z+{32HMt*&@F5J08m5C-+Dw}1+1|e0+RC1nSRK1lcg4SFiPdN{oFKgfOfPf_ZR~*b9 zT^eKhRrBKN$g2uL_c^cCdxm9~5we7OFD_~%W3I8*1QU_j^WrcX6%3{{;h@d|L5qkg zdxrCk^rC8BkqsB>BAV*;6OnSAsmbvg#@9{M@6eR1tZN2^#$2i3Ud&W9usX(nam=6F zPj#=89EGK1L#~fxSXa`EBHdBw067jg3bUX*i#j-i!Fd-b*MrFhPAo)*o+L$99@Qmp z+WTVuq+%=;h``Rnvd8r}!TN9wA5Ul8?01-*i-jO}HKFM`GP^=i=Q2=JF@u|p2|8{^ z7zY#9%wjWBWwtgp@yI#dauBe12N(70(_j$gxJWL~M1jKg!+RKGU}8qKj>F$kE4K35{5}Rt%q_QaYs&t{&XMrjPuWY=Nah}{$6{&SI zLH#%u5>w8WU9%mTQ$BB9u5zEj0IXuPB1fY5E_7dss(9ixsxML@a`X|4Ec!IKVi=`^ z+{g#5xmID&HrqKgtq8-N(M%u^zIgpDe)a9w_{E!V@z1~aL;S^`{ulu8a6CpM%&@lw zkB)FpoM$*^qsM~^J|~q+Hm}h`zHGo$_ZZv-`0CoQLFKYuz0WZ5>@PGm{5%bJV?dVc zClX9U;daZFJ^q@BLA1r_=+qcg@-V4$38Wm4r2!sz{Y>)uK0Vd{%Hej;^NhKV(DO4J zVaXb7>AV%|xYGl3Z+)yrhWtE`cLM>ZUenAX3l_MbIj0g=*s$}Y9}+)jNJ)LhNR3O@ zdgC<;zt+(wV$^w=aW(F6whN3_aO`nC>~J*iug?#dw88B#27iD! z7=O2%@$0*9aahjylUJW$r$;Cvd~>{qW5!Ff!@?G?%nt9CGrV{<&fa3Lhb+U|kqHCk z*pv@i;l@n(+xxHK*<)r0jfhU05g&{@d@x<(w~w#!{TCnNqw5#={NW82Z}Dopf{l*T zlNoV$I$+cWul6^%KR*Vh90=y(bb)(C|#gO#43d|iHI+fgg87ijnA>#0cf zU&N~df~W~N;G!dR-b1HQ=~%=8KI+M+pfMZ@N-|XEuro?lZ{BQ`m<#ZFBgy_lBDx2( z?MJeCO)nV(JT=9o&A@I_z>2~S5h%sa;U`^QYgP2#)&ZL6kIdC|YS#csTXV7FN`o$L z_&#(&OS3kmq}p&N8?ML(s8mCV!d-RtRlyt^?Pf|%D`&1=#$_)iOhI~$D%H5MHlNH? z8uPVT9kY)JwW~Hf!SanwXmpH@O(`jgPQ=LW%_NXJ=Q+kn`Q~tM4W-0&$`p8?eW5TPxO8 zF_0jkhfRA9qaGY`%=DJ)JFm_78H`r2;6Rhk25w@YrU-L%IG%lyM z@uSZ^t_G9mdBHg{W70mZy@FEFWt>v9SR4>U8?N-3M{Pg=Zm;&3MuXEbSCKot*qqJy z5m^BeKLgom&PL`Q_ST^g%1*hVEO$+(E>xSn3U*__SiNKJ^|;UZ%$orFdZsFC1l}6@ zbN~G5X?R<$^%mf~YJ%lmAkj>=9LFeA(t%Ny+?i9Z@5Awk7uQ!<;vO-MBNprM<=fZz zyRW{&?;r2*zx?da@nV0C#aW!^1=hJhZ`PrKX78GIgtw*#tD1_thCKZ{K`r3>tUnvZ zSY5E=IrH_W7!M^*ks6elxx?CJTtIQ(?+}JNIWUsqaJ=zvy`0j1pJhTLx4F;A-4~nK79G)>~Z1 zzdy`JoZW)^^ATSi?s0T89+nxerX838%Zw{MKojBH<%lm&4*-BqZ(d?h3NLpzI9UsQ zd;}3=P$L!}hQXuKIm?`QfnvY`7$Pg}=o~CN{K@_mh!}6qhhPlMAx8A0VTUU{;A$Ab z3>@q^_$KKAyK%rDUBAN3xDRqF1P_?qFouCU1K&Kn#cmi&9lDCgYt0n6!!Jp?NECP_vFp5|uT-{*a{AZk0qO)l?r2bGWrWW@ps37%T|D#< zOm3VDh0nXYQ$QEA67IACxfzBKhg3d0RmghX^VIY*YBV>UFra!0N3>zs!5x&Llbg5c zDq7IC?%q}z+%yb;P^Mmf@VkW-&{&E$j^8o|Q57T#viYQ@$Y}p0b0go>L?mJ?if&=+ zk7Hh;@imQ<1%fZ8a;P15^W&geikzU3Hajb~jqvI^Qg`EIfym^SQynT-LQsVnQ9)_a znhVBNL?zE!YKpD%8HEjqZe&(a$UaU8e!dP!dQDV@XDl{!8B3$H2E4@6a1%q~*@>K4 zhP5txhild6lx0WeL>G_uLC3)LfM$9ePIGQ7RWm#T5TkL*^PKe6L2ruUGM?97{dc2U z_*<-I74*C=MW$&DMG8tGo31H=P?e=nqy%Xn3Z8x{H=#j_QT8CEf|P=22#p{V2BTpk zT1(7TY!_$Rur7_U_aLLP)4C$yYMNl3i+@+qtSjtg!NI$JzA{1$0NO`;RbR3VlR=qC z5Map~;Iw|jFk`m1uqsGl7)&%V3;X!r}v^bg{nC5Uu&L zYu#Y|Rbs*Fp_*OwR76MH60i4G(S7z1xGoiN3yR*THkteJGZn)O8w1v7X6ddNUuYbd z`3XiO^{ZcMa=cMDsNvG7=5ld^{H_^kjK=??!m~I_6POt1d4|>+qZu&h5cP39pP`h( zehL}rqcJeX;Pw3-{^pC%@zwnUe(>@I{?*U^C0^a^@o-vjIGw-o zT~P?=iNSDZwN=gUbHjhvGPw(j<;f<9=<-0Gs5QYtCFc_-cpVG(9K5w<=94sm+d8VZ zUBYC&*BjXsH|l3*YSUIZDAFWaAL~xCRbP+XMg;YTOzp1o57G?t1w5>GiTR1IZ%qs~ zZk1FC$RW^o`z*Y!@0c&VexN|l&O$2<#}3!y9&hg+@cQu{UmV`RM*S@aIJgCmc7|of ze13!i!Lo-Tg=;flpnx17?2L!=5r#DG&j%Q#a5GE+HJ{RgKQ6P*;})mhL*1m4{3c;EmW##af$tf5tSl|t&@GwgO!5j* zGZJVUzh2|K?b1gbEeX*xj4IlikXb}xl>r4B9clIL=;dRgYQU?fLy@qQZwrGba`8w+ zZ`Duw>MkU`vTT3@`LBsH6J|%L2?7ubE((a8F$~Q?Mxd2$sGU^l%T->NYE^^Fzj> z+ctageR(#yiStn#oFu_YQp{w-Ep(nN5YF`fQr@pIx+(^qE>ZX+4~of04RUfuW=vYc#auojM5?8V*DW;Y zCfraw9M7=UM?ypJ5EvqSEw#G2!G#LwlrZv<4G%}&o%M71G`FF56@=Gg)X&54+?K7Hgoda8d$l`i8<$PzKpT`~u`IT7S`#NN`> z6WpF^&BpsQ@^zWmwHa^29i$sQ6Qj$pGGD5uS3J=A_Hh+E(@!C&F@*IX|9ZF2&z5x# zK0ecl?VuE4whMm#yD#wfufM_082k@E`6+(z@h5QJ;hVchSnHxhU;wG`+h7h#9 zxG1q5`B`HU>cQR(pjNN34cGEZ`%G_bHt8}oOQ(@z@Wx*M-i>@~m__}mLDSS^nHU3y z^9lFo1Ac#ahc6CyIQRu;AI|h^H3Vw38RGsB8qUQ#7*hCjx&Z(j+>D>U`4abb#_hPr z2UjCw> zQEtaLJBOdY{(bg3`RwL{5OFkUjM~IOp~>-=I;Qn77>1ZuBq-jPj#L;d6KHcDQxM{8 zc*nhsr=18=bzggLVm-ayScUD>w|am&sNrf83{prE2Y9COrc74hM>S0b_xC59jtdUQ zGh8eb_xllM(3rFVdk+s^G@}1MYj4^t$B|`;o$Kxqk;_}~01yBP61B3rtGdlvHY1bF zG$ZMo{;mFizR5H)lgYZJ>DKgAb#<{e5+DgYygip#+^;^kM}%(?nUB>~9{}Rz%gor^ z?>*<-bEuCfp(Bg~rgGl|V&jP5YQ}^7>~C%qY(l~8)={9Wk7Q)B;QRu}V^EQh%X9=R zmwqXVKO$I#a3K1PMjy{0%EyLg`(Q(VW@6odWdr)m+mY)Q!}S~$0~~3;DY!%w{Apag zOfn+SvrAqB=6@gIT!NVoWZn_wAcZm3i)g$ilmZr7hfzxYqCQ3*2`Cvg>~2Q5-wbU8 zlyvUey`dZjBXoUHbn%N(y3%!ntvFaUkSW z4t~b9lmGVDnSF`==oZZY3MD-U@o+sPBAl!jqY<9lC5<}-1IEcRbR`~`O_x>PIeD@m zpsETiio(s~0|q*h-m@F~9%mT$bu+M1edLqxQLwLDRE0!mfSZRc8f_!0O7}U1Gc)VW zqI5q??3&_LRSty$1;UQ;S`N`~?tU+w>Yz7`8_d202l`Nw?q&zzH-9g<0Alwy-Ocq~Pp&dcP0BM?;033xb9jkpj#m)W!=jSi*+3E=%x;?JyEwnMX>uOvzI~0QOtX$#a<__nJ72Z5~g>AdX z-EL!(89Hm|wJgdp9V8pzO&mJ;amp4=W>g^^iJ9AGDJ#>&ff?RIk|oZgKFND>z8Dyo zXkTI&>QRibaYTKXbm3xz+RotNp&g8~v}P<<1&TsiV=0KBa0-E$UMfXt2qQ&vA2IVv zDFVinks%XDf@n%g{b0eFqS+$%O`6(Ba)={kt-Fw18Gq093?Z_t^iC2UvBMGaPdqkmiAROEITD5Wej+kaA&TtveSoufbQW%=lya8;VU?IO(e=v^ z_K_XttlEqybk`2#&|Y*bQ7B{X<AKNVLk?|fIJRu00bWr5R5mK!-!~AH^3P{(y2_c*TlRXK-S17m!H7M-7d~e?c zd1wfSI-mzRdN$b2bGt@RdKaiF3bdVVvZ}%wB1(;u#+G0B2S7bAZ+)Oo}kd zsw)+wv~JsV+o3fYPznnNzBHYN6ogLLeBn;(!5Li$;C@#p=4G*c!*G8Pp>XC{a7;l} zszF+=wY`qLSy_KAd&lHh^in+o{Gk8nmN{gjDw9K{(@V95Y6q z@*;_)BLDhLxs7pp55(udXNW1hE=||rbh$*`ws?PYjf<00ynK3wU;pVX{?9-D4$Q#c zzIuhfdi^;Fg!|1Fi>gFblxVtk0GhP(X|N5k8tR(yd8hR7Y0A0RyZ#xrFWAdxwrI*S zo^el|q53oNGvW9aP2THc!%vYgfRXB<`g+S(@=*Y$W?8!6L}bhd6B@BE`N&gcq-{n5*x&0nXSzy!E&}^{N9hOo;5MkTa&@fmCiHZc4lJV{C9)^M2c83Suxc=XbNJ)j( zSOB?Cqv!!ZZ5YZKWiF+YL9>P;!dAEV!`&5LpFP92tMT>24X)ZPN+BUY&@f;lP@4|_ z`SNQ#Em!!sy~l06!=~Hgi__;T{&u!_(FiIt%jGcKClAx5G@4a}sbd7ImGpG87 znP_l53Ui;T2Yh&i;<;>p4#T&Hnjhr%p#zFp*SKVe=WLF`GE#Mc zKgUbQGSiewsfKnoVqZCMR&~!p_8yOJOveI`W@+AhI*oE53gi%oQ0VYddFm7&@<Sq-9IhVGX5J5r45sK=^VEGGd&rnOD5{$=pStid7mWHBYY=8?T1Hi>MYO>c; zgCN5AEgCnc)=7r@do6ZvBtao)CFaBz=|kG1s)iXtC@_R&#d}jP2oJ~@?@+cwq6eG=4xeKj*H<2w&$>Stu-7vTEkZmcrjhAwhrRj80TaQ z=-$wb4emDWn6Ylg`?MeX=-6(gLeS6@j;aG5@|Xo-aJ|>P3%5J9eM4MGiLy{oa-8|y zHGR_{29~>TbV*<^ev?|@e!Ii{zD5AMKt#VKB?{^CfdR~ga!#2;^@Z7Jm_`|v>#%}| zj@nBxj?W;I`Y_cWCN)Do%aSt`1xF25%!4I!sGRvT1~$DT&au41K0KMDOGy0^qH7YU zU^qO}nVi^^@#Sb88rM}$T(k)%i-m21S4%uQKf%Ag{TBb*KmTX6oxzWvJjE|xzs9nx z2FLO~`@A+Sx~{X)gzkBhu8_5=3zMiNl3AsLN-*D4aazF@zh>p2tfD$$~CL(hV zyjfRXj|uUcG{3zG^i3~f^Mg~lNJm`GTFz<*) zFY*JLYy@YTRE|Li!f`C`QB3fWZR8=pHpby-w<%Ms*#(vZZ14E!r~~JOI1vKG+esl5 znyx|9b=bExmPH9gmUOt?Z=Bx>7>;frDIn6GZSU~0-Jx+ZY)Qg4*MiV$gRN=t{{9mG@9npEq82z+CCrJF4-QW8L(Qkf-NG$t~eFp7d?B&hwYIlBmmt3uFV`H62N3*QKZP|Uk@uN+m4T~r@ zpM-QK8jdN~A^Pf4y@&Z;?j6B>H+WarNg0Gup~VOF{~loK8p!cJ~31q}_b9XGN} z?XI=ogW+JU5707mLqv@oSa$5FVMxCrUt@5Qw0F(208OL~1e91k+Sa_(LC=$N-BTdCmVq zoaDG>nhyW~2b$<9*>sFUM4r3+L+^it^(#+(M;cQZ*5v~Gwm~H&3=G!A654cNW^9^0 zI^E)G`+&Wh>n(&G?O95Roz~cP4FrG%32TmJ?09D>1#a3JTiszHCBVQ+DcrRNLSPt& zDujgA)`w-S3YgBIF&dRnc+d?NLSZ2!v|%ijf&k%9cULz5R|qUb2?1-uWr(pr;hsar zLI^MvzC3+~+j@tu?mpnZef=dqJ$r`l@2{~?8VuzaVB%;_+C+`6-@u0M!Q_mVC?j16 z8>^U43gx4LG%M%+8<<5z!4Z^~1n{vykmqvfOC|la(D*2gF({QpU3V~=Q7#Gy0+#C% z+f9SE)gWn$l(y9nQsCjC#>r^~A$g21845wqZb4)oKrv-n!dVwN9^ywvL(c(i&BQ;E zk33Q@w2T(jY`z^u4dQX=8y#qN{Y5=RRYPa%+~+X|c3jcj68RZYKj$>QmpSy!4;nk# zW=(KvtkVCNflFxWo3FkAa?Oq4{07Yd$T8kK{T4gK?j$o-9_u}{h2EbO$qC%7I0Y`~ zA%^d}RrWKkre}NwdA`DV%BFAZrF7jO_bZP(en0wjkg3In51~;D9dWnyQ9P0;Q5X#I zNN{t$ag0>YLX&Xr+&nOk!kIkvF6W6oi#x|MVkSqKw>!GHNeVS%UNVQf>Vn1w-Tk}} z5}SQ(4f<4pwzJJBIToHg(=BWGWM#TS0J@{$3mezj_iL5bjLsNTWnnWzRRI84mZgjQ zvDZ*431wsEZGLl~mE1dglNZ6neRPxEyfiZwRe^ooqH9_#7bVI<4f)vpJRUQFM9)k! zz!E|V2qlO2lwt%&`t#hIVXPJlZ1**uoUUPv!QFO;QYk#__IPo=gf<3$`tT6|P`3sk z``u*kEN&Q$F>b~gs9EE_Zt;W#h+1sw7TdPLX;p!Z1BiqKa~E>4%t;<*s08w!PZERT zBw<&3sV8{=2#vB!Vj6R+$$Q4VXFbFvXp{po=c}ne-|Cbcl(G-ZJS$<$si%ExIL;pB zY`w}E0})OdYt9@?kW!cX^$hIGUaoQgh;rC8F*wNVw(0{f-p%Ek_l0SvEr8NHJR2ts z6hx@I7SGSl@tbeH#;?D7he8Vc?dvb_*|TTR+Mv}MZQBl?r8~Q#mc8$YvHlW+lRRq( z0HuqzYnnPGLixb>WONZJ$l*=Lp+sytIqZGICKlC;(@VNmw!k%m!$^!}!a28bh8Wh^))O*Qxqz&1k&vHRTc^Me!1Jl%dWM-h6IHtNAI z`mkAebq2K;jxY($c%cx*Vge7o^+z%Z5IIRy91}S>}0pmm|yshtWu1cKB5?AdGiWtfzP@Jg>4_%EDS>kL_;`{vu?{|0j z<&#(VKi>ZVmk-x?{p307u7Nfsh^jFMII+r@)3PT`EX_oHxkKluXgx|2dq{JTxq)XC zJCiL_;*yqO4l^UsG%S1!CMy*bNlMqPK7`E`y!_P=F>-A5T{IrR`l249EW z>(i$?mXJ;SX4d7BXD&-Vu<^ikJHo&sJFmeL5m8!z?+^ z)>`zJNuG{CW{7C|K_rjm@yxu^G}p{bTJTjPXBhGwGlf@3QPL2FF-_64=M1FKH@lv6 zd~oj$Q;oy*jd~`WhNvI*zKIY5FeVj=Qo8wv4E1;>q#WJP{bFZ`lj<{>g`Kfwmml0q znr#z3-gg{q?119XRW|@{2#{pwqVKj_{MWbNn`kj&1FWfB#R9?aem1bANI=|H4yr#*qw1GX5J1P97D8lxGTOBxRYT zg@jQa7`WJ!%g?;Fp0%s z6e3f)>>?BGlkA`VXPGRJ8yo;lG`WMZ45tRI-~)6aPk(Oi&ukUpyAa0=Ir8tDeV2)v zi~Jh@2QsnVMI=m}n?vv8Soa}i;Gi37v~Kai*HAX5lcM;}DV0iCy#~8HPe*jdnL3B~ zyh)=R2i6G^P*OP~QUN98U@j>@sHDQ8tZ-Lvu~Y?4iV9DwHK^U9GZK5#p>eakb~hX& zJUgRts!E)w65sCc@xIyOv+4wES)g_q<5qdX`0~jMyf}G+51U(@JY3;pw?}C@oTvgN z^-4K`()ntT1LQRsoJ#u{o34R|ohjZM4GF@J&pv@+;OpHT-fphZ!Qf{1fY(o+<7~0S zs$8Iy#n=ERSBgAI98F7_Xy&q{98J$~P!j#{IITle)E{FaJgc zBv2G`$VuJrT5KL_R11am+Rmi)h-}QJb1n@pmlO4GEdiNWfDX!FymLSYHl+DCdF%mE z#OCHDD}GKNAF+rM)A23TOc54?bK&APDkV5-=5t3E|9%LDL{JRJpqDYx0e>I7~!Ha-svrTJq$UqY;&rq-y{YQrOv8%ibDD5)h(y_aB+FUUe|0 zn2aEc-yn=`&00RXy)_wQPA_}ha7{?_#(Boje*oS(GZcV9-aH?EEC_%39+Nn_ z^5dJO%;OcC?4?h66kH#xv!@zbp5CHT=C_x^Q*aL>P^lnde#7^&qXHc|-GMtR=O1oKFc=jRf((c^kaUehy`@09y{EJxtu zW>@`9;4!Yu#}%^42m0>HInV8R!?zimyi5#J7+pBU?9xm zE|Zblp4yjk(Le#x5@r}mK|moPl^o@I!b%MK*Y*BAzR3Z5d|*h*^`=76W;PoQFeT&2 zM`9ee1x6HO@Yl>eklo>s6cTB7cMP&TpBm0Ri`dJugNP=YZ1QMV5`e305%YmjRLvZFXTHK^d1@6H2I}-ezQad6bX3KXQ!)eVec2xS}a-k_>q zsAIN(qb%Eg5^uK{)dwMFAzPQsMvm^B-}w z+u|3`KEbcPc!Sf`68GCJ_DzlTVhO{HeZ5Cjlo)15aup9hu89iGbSU3dMCX;V##s?z z5!E{J1djj9G?^EXuY-vCXKRc&>>V$B9@l3($@3+k156=v4$MbJhBZY`Csg7i7)~6i zsEE8pa(!g3VI<0Y*D3u2250`*5G(;;XdE-6Y%@V=f1CUK>?mG**c`r=C`XazH-C;s zyO2NM;gS#;-z7K+XPMZ8PhOntGPtD9Kf7Y?91(_RLP!D`8bE0&fKu9o1#Jwr^&YFT z!qxU3m)m>XG&`(|65Fmp#|9t)!Hm`^!U__owKb;NnHG1gWs*x_qXZYKz-6;VA;=n4 z5}}j|yS9Ng1_ddsNa01b!dbb%rmbPXsDy-OgRO1{$-ZXBZeWw`Jh1_zAc5Pi#^vT7 z7t0fzr~+r@!hT-{HuVnYs}m?8A%(Ovz??Yw@;sPu$l=X@&nX2I`|%H$H@KLEEjfQv z(Xs9H1dJ0Eu!$Xh-&lF`Nkp*m8}0@xs{%?%ES80xvvbi&L%hV4j+rw%LN362DuDej zamZtMhE04Bbz_>Tl8#~n|5D1D!o`buXoAzl$3%Zmfd!s`S;?0T5zl(QpwGRITz(JY z4l{e<*lZlTX7VILo^@y-P8DF;zw^ZR3*D!Qk6;IQ>XYUqNKr%^(OJDz|1HxL<5$F(Q}Hu|YT5ICe6W+*|fIzmxZ`HOzzT*f>)QDFmbxfm^4) z<_-HQ>}IMbYVQ7j+H?69k@G#X9}8wBqJpeRmf=3CNCaLygNH#>PxY?Nqq&%qaVyL8 zcUlik{l014_>w@|b}+DVu#3|ZRAq@msS!a2uqY+!ma*N}Sg#gP61e`?4c=Yf**U4M zgAxLtKYfC~`{|GI?8zA(wp*N^tcRkTIe))V`a~g+Ne9a@hmQm2h)@olY7adt*>Qzj zqdW&-hs_rAvEWpU96lodlXA+H4Dd)Hc+as>Bxur!du-%G)^}iqc@*WdKOgKVbTk_j zLP9BPNY!>7I@1j*f>%$UVz=L;Z99B(`2oNE{(CHx#Q*TKFY(#4=h(LmKHT15QC4_z za*FMKZw={|OYHXbFwdK-V;;-y2fIb~Qgfa(olt)TTk=sE(vR3IBS9bq@whYr;dqT= zZ00(-E$}qJa7d2G@t4FjBYDiB6Y}oW5uW77J;hBR9hxf2feeUDv?vXR9x&LK0!_}GErdBxfB^+oaA1dIbPxW91#s>rGkWw zkTeFXvKqWQuJ;>U?l-t>wm9!9+;p|wUzyOdLF@KqNrbJg0g!8287*sUx(=JJ#xE{D z#do`VT(vvP3YSZ?T4Ucfw($mGEel+fE7ZEfO|!?=ba+D6=&ECbg%D_s#!@IStk=O> zmMDZk-E}xG7N~^8uC4Lqi`V%1^>aK~tgvZotjY?K1Ps~G$X2&dNvj*9oivZSQHBTR zfXd=&#bWj!4s}cuiwsY!_cXtTBwQ0wwt(g6-eUH%LB}1Qh)^wxL8jPC6#HDuadVov zfhpzuk`;b#72p`D%rS;Dg-hUM$&*r$#HRl-+bYxa#Uq*lBPdW9P-LWrs5a{jUU;g( zionjE1xwikf4Mu*vhnz)lb#BxY{D5+0P<)ED8v3@f=1C%r??(bR~lrFa&+?#X(N=j_&8tcUZb=!bQK#&+1 zJDXq4=rrTzzC~4*I6qn7_F<2I{OXVR>iq}o+7^YBSQZ7oeDw+b_NPCx23}f23W?i? z%`hvR)q(I-^XD;?9404(qxl^15#e*f$I7G#GjeDTEM!hdAC5q2Uu8BlgORBQQ`ie+ z=R;?i%Q#0lZGI_XW79ldTk~-KSYrU+7e({9Mhy3{8NmsEkE&EZ^Q)cHxizROivp!8 zu-oskt!w=DyKiy3-{WV`KfzC4y@C`1oBa-k8E30Cz`*@>i&kqa%3?6^J6K=*0026y z=??G(Gw)49q#Vz5I+XDknL>Lhqac_UJNOYyVdg8P1m+Cqk}o*L*&dM}^9_I2qlYN}Gnz4t4UU6;Sb1ZdX{&2sm5?w1o-dzZ)9mp6?i%;a9*xVm zzt;^kGgd+kjkNM)>)N^mg8}T&VSxoIDN*YVyS9dAYlKB0yr@ob<+6Gu5zfjAFP0~$ zq{OCeP_stsjJZITvZ{naV>Fghp(N$J6zu(5N{QF&3m7&iWr1Z;;^_fqBWnWiVse&wQ8>oxB7-?m2(1V}vy7bTBE3r9;Tu zl$ly?f#Ceo=KFzB#?;Jjf)NK@MH|E+6;gJTh2|H+sQjX-<|jd9r{By*lTm6OH8=Y@ z%K!t%8jBpjl;#QMqh1_QhU7POu}e5jn~nUNG{SuFpnNo)V@=e7>`a~n5`83mdS25Q zPSjBTIZ0q#*@%Q72RsGFg2=ownWUIRTe<(@ykH=9cAi^4}K^w4Z@ zpF=E$rbP5nQ_iHL>onS~gOX*CZyA7J?s*IwrrrR|42^rI4GP5r<2J-pfzfpu_q!dm z*2DXETH81ow_g>7ol90yV!Q9~+ppi^H}Aej-D#}L0tSp9J$r&*zWE%BvcP6%V`WZO z3*2wE(Ot@uOu>Af z`-pBY0;V_uD?L~&h{D4V=Ge#0#7P%@;|o{=wRKfN2!W>Su-)(QyYIfm`^`Phs|EhU zm%qTp=?Ut#MXU9oz-ihR+q%Zda*3Bu&v13O#m&P5US3>aSMR5<(4kDz*`0w7CdNE& zeLZI1Nhw`A5U~?nkKCK4f~SmP=ds82$UVnba{M6UsCg12ZR9!os8DuX$!DqO%s@Hw z{*f$bVy~M$W|~r9iWvn6MN^!Zj*FdIUgvHyqLk>OqFK zs>6h#%^|44mjJlpmdB!=iT{EJf5NnhQ!w$^+MILl#|V25AN?BU=^Wcq6cUw_KrfdC z*yV@rb`7dUadacVgrlCU+>Gg*pqvjb<^J4k4g%rhdaT)^D+jaj#J8CsP;*U*G46%{ z03ZNKL_t(A&4QzIu;g)MU|$+dHQ6a zFq**o9YHy{Aux}WkDRN@F|0?NkeneGPf6bCAP1j;l_nr6k;15}|FxsZUlk+w1H+%fUZ@>%aT?kMWB)uc4I0!)AxGlQr%( zTWofF3oPUsJ&x1`hw-q9wUvc&{~b6_N1sAE0%h~mNx}A$I%*$R!-esN!`R*g*hCYV%^|%qCEyRKzX9ZS@kNGA z@c4G7!{x&kb=Tti-5n~au+tsRRfU2CR;mKPxa%5_01A?5xC0YV2*OTxP$W<7dZ3HwJB?OdH zWG$q?O;`Dx+9?xDVZN_E_# z^+lT?-$%ouCjelyLsAIKr5a3Vj4>!HHAoZVn|sU)O>s-VXwBuU<>n69oA2}AGtOr| zw8Z%r#Am$8+c<)nX^`nrl!J8n` z4Ud_PLp|$`Iq~zRbhadS{rt?%)FcRj(qcTNkpWUHy`>n$GDx%j4me*X4YbXW%Q~u3 zRLsY!_5*X%fy*LBsV`^da(JGe|BKN1cs7$J=Imn@A=i6w`h~r@@X^KDKV{@#*-`22 zNz0;rnO%k!)7U!qF7(DYm^Yx00U?EDL@`PdJ8kSNqBht!EdW@_v647hE}*qW(`l?0 zCE8A-)qOK60=L`F7&KjnLP4GJk?Njk!!S~wFiX=7>DCGH>g_+;3L2v!M!wzJP?AEng0iq~x$I*d;__EYNc z#W@LiX7AzwRB%RO~dK?x5?$n3`%nsJw z=s60msUQTVqrTF_jTrSay%scMbjG?Tm(q@Bc1A-9>zHhe!O6l#0QNJv%d*7pF2A)- z%({gj!pC-pr^N!T>2TBT@z6DRUM+`cBf|!#s>DWjwtjFk#D$Rf=I$eE-QuiV;OqSz z)>1)|K%+Z!M&lpe{T@mXlu&rNIt2lEx4A(jt+Vq@yGKO=r_!2Q-L!ku#$YW9yg9kR zL$k-Gt?_OBfW6BNHq3ZYF0s=cHl{>k>U%oPW7GxV!zd*)hK-l~``vd#{#=`ND zF=s?+qeXKBQOq13;Edff<;U9%l6qrh^F8=dl_jdOK-+2T>KZ3YE7AM&hxhpPyLSK) z{@t4&;m0pufngijDX=t{Sv_W5%aN0mU*{62pLs_99<7W6QNYQT5%dp)z=L3JoORsn z=CYWlf$BrI3ul|?+iVwPF{{hM5{Zroi%;DSe>BOEv_E!Y-Aq+?;mc!Y;fQ10VbQX%M1y^ zRkO!hjS7>8uE8he3K#2hbjF}H8s{b9zHJ~o!djKMs8-l_4Zhvo;h}3#kg(vmlQ)08 zxrTPfUI~d8)e4)g#cjL8oAnbcRf*f%8U)5t7C5h#C^iBw7i+v)oq+*-Uq7HCg_Te! zh;iN3D22dfyN4jcQYv(=E@;_c)7Ey3$v3iqzc2+0TZ*_5P17b!F+3YP7jqWz9T2DN z7!)UH5e37b87+MF`514qQGz`oaAb-aE1p52qY?hZj{SJ2KW0jsDDovdhorik{gNC! zv^@_zH3Z8Z`0LBaDCwQ};ONYu;>B;B>B!T>W0B*{FAfr1qJ&qWelF{=Q3O+Y#_X6k ze;5)NqhNR{{v`)|h8U|!6>nntSkZ=t?Mo$K!!0Wv~ zW@e!4H1=(a`)!R)-Qad-H+VseRvX-HwgW523@l3Jls(oMLp`KS2hkKZ^JygRs*_$m*co!h6m%`8{Vryvxjrj??(;Ne;Ie&o*+M+8|e zDVvW$`i_M*118R^CFrnIdg#=QYz-4OVg4E&8DMfg%B;WV`v3LpE(`lC6si~)+w1!s zs-ncxvlD!{xxqhu{bxKhHU9eLr})*6-hdgn-)`LfF2@;ne;nlK*vLm*gJ@KYV}qwm z@*dGVKuDBVEYJiKZP#&;FYq(XZ%~KuJfOthCdnK()D-plqY-5q^nS=VbP8>l*^(1L z8AnG$CaEO;b)@L7pUnC-;dOQowCI0E6voyKwL4Ga&GZBJ+T_1DTJqJOKG)Hn~&H(?MJZnBcK;G;6Gzj6& zn_+{C)f(Cuyt}!?cXuE0Q15YFZ(-Qry4hl@JJedE5Mr411%N^d+_p6uqfrQ_@G^{* zDsfsYp`<`56*{Bw)$Ip#tf7dYBw^dM*mezey2DaR2v?_+B(XIuBm|6Opj+MH&H4!z zQbHTX$9e+><3}eKc(p#qUA;v~5}%zs!RylteB3_ZM3yL}L|bcI>pdh9UX&|TLZM}i z%Vq~9gl&RdKCqnu?k^JYU;`gYWPGlVLUsswNdqNQY@8}DlM*p+g5@hy;>rHe;DhJx z^9hCIm|MmZjNow8oUbnCZHE4V| zWCDXdkAj6XSeQBGD^mK?tVr>M=@xkFG#gk?70zhOeh_aPfxkcB8`~{M>V4DiVom%lLR^+r&0(S1p>z= zeHdkfebiB3fc9%=&k$SbCI=|B?i*y{;qS>Uz*ag&Y-f0HHd}1=4Vuo%qq^2MnU#`m z))#1vbz@$ZrFEZ{gk@E^&+v*F?-r9nqR}0U*YNhwZr`v8c+a{k^9&obTBB(-woQZE z{T>fZYhUY5UEOvNLJUoO&*~K}7V7%`0sr##Tm0+ymuR#G0a%w6{^s+~@Z}el`Q2>y zs4A8BN?>1oldf%yDW%Mj^X)W)Pa)mc@(pwudbepZ|!jZ*K6npMHw} z>5E^1K=^oj2f)q*bL7%ViR0@8L`*S+oRQmQvk=1QOFF`HgfdbbpYh_z1dt-ijG{)i z|GvF3s7c6zbT68UxEe>9Ca&QOjx_9vg3`q;IAasBGf{3kPneBTR(0jOT2p2(2mxhI z$d-v~mt}=Bn&iY*L5$UT&n^xlVEJ>%E&;-?VK7mu9!&!D=Ub$>m)Bzkmx%NJ0@olE7BCXxZ2Xc6nxVh<6Wozkk45Rd{uJ zfl?MYt5*2rdm%BUM=>`&nLI_+oJN(^~SGcOTc+f3AuTEeDqYwhW-`_(& ze1sZ|oPv14DY(k#FUG|sWl-%K8s9^>cLmKcwa$jyAEJ-twgKB0b&-LZgTjWL{LI!V z1bpIQo$|`1IJN=^Vo{FaJY&vyrsAG~a4F)*DPbt#Z%Xmp2hS@}eA%i$2Fg>zvPq;B zh0V7YFg3%cBYc#n89l$H<7flu4S*P#&Lk-WDPxp5FR>XVujBdYi@&Utxvy;IHg=le zQ~TLsqWo6MaY2#5PpQi=9pXukiF24|%I!6Y@Z4FsIc3aFLRL3~$qqLKvD-RNqTz>F z<2<_0$86^87dCv&+g_jH*yJ8`u~qB7FYIIVx4)_ zboI!fv4>d3{Pm@2y5%T;pyL=sjt!}pXW}Kvn_;8_BQK9NqoNSdWbzbM6G77mfm}rE zGUtE^n(B*bvJ@f`klx&@y-%&#<#ulk&(78>FavKdFY(6@A8`4w!GHYumpEN6p|!Ek z839r#C~5scy3QPN?K4YOkW)TYQ_#c?N0`oz&s=ee7)V6SV^o;nl4xR}>IkNmqDPpKbZGhC^T?Li z(Phk8j_S~?AaFd`5F1F%E-3-|GYxB^M4FvV0-q7Vwpsv5rPvM3>i#MOR->-`3MW9N65LW1Dt ze}%nvwJ|6NkdRnO8%NYQNBgJC6SSto$L)h{d>1G9>64c@TdweNbBFJ?cW8}92ZMqo zS~fTlB^uU{5NO!oSye$0;rn_6Z492RPVjVfiZ9Ne;l#N+e{=HzXVnTn{^T{9uEn?a zSGeBad3q@I2%;CH)M-3_hEj~{p)R|0Go?HXMhB%!I zm;{`2@Lb5w_04T?`YCOm1N{;s)0;<9pxkO02gFknB?K?$G*`UEk|YWkyRlH1V=g$s zN-4hmf{^=(51;N~IF^<~=<-yIf*&hGS$h;9<;SKO3*u~iQz-f2*gQ>t;F3v32wSs@78&|tu4T=Oi28~{Z@1R;f!AJZ_u zY?vWDDLoOe+1J=LEh;4;-CShn7~(}y;PaQyVVLpF)g7AFHUL5p+RotiVQU#zjD8cw z#WVVV=Ju@&Fv%I0Wq(Zy`dCgeP9Ibb_Erpq-Z%m>v(_JMbRr0P&W|J6k0R&RpxMog zT5-%MPGt&@#-5br0~tz5NGVWAg>7BqtM9(U+nZ}VU9Ipx|Mg$ve7yz<0j)K(vH8%2 zvU2;TZEb{EWO9U9i5akuJVW|L?=uU|r;4GJ>h)oE@){nRct=VJg-G6gRH{c@RVI5H zPbjrUGa@9l5(T^VSX<-(gbBu%Z~%n$Tp$9^j){dlwr-ApAF1nonyZ}lTJfA~_^mxk z!mXnmyEn%*@a*$SiSQtKR_GAUPJ!Rhn06pW#onkLKt8;e4=5NoP-*s6lz^C>X-@o1 zqDFWa527)WivlCGeB&K=O%(S-e&WFZD*2vEJk+n&OJj^QRKo6Uf(X~!2WX@5hpP|x z*lfVe*y}OrVB58*q=aE$XF3#QLG;#isD#2rxp47BjI~rCBHY(IXg0W5uJQ8h0{`;; zTm19oTWq=-JFNi%E{Y{qs=)X40|W@A5ZHGeGz?lcSjiF>#RBi^2P{;9=j$`bA$gvJIm zcEQZKllZKmhtLk_KOuJHiHrHv{D&ilpegnM6MFaPB6H@R^3E$KO;_CbK0Y%%Ud>}X zjws4*Js|mEo@VF_VB*vsrBJd&k=wIoP2z28zyoZhy(Y_(JtCV(1wS3L~@&O9|YgOkFmqQAd+q_ zc#KBa*R^$GR)7)$jms>RBw)xkz%c1)Bqwbq0wk=9vlKRJK?p+IFzTkmhpSt3+B!Zz z?Du%s?olX(Ldnq)S{ujA(s1weQtd)XG_7^4hKn~6-ZeCAAp6)SX0QR6>=zb7pi~7) zB~ca%nmKr1FtFXXC{-IH%`^|T_+wWl*SQjN01>d7)hVQiQkWa-XV>EdN$bpt{?%!25&Df@%7b5bVlQ^UcST^FFwJlTA=MZ)O9^H zzXptbQ(KUxD8`Wke+?%@4P*cLd~`m$e`w)+@gXl`&I0vmMU~0vr5sSp5#GsR9guZD zkM~cGpuvoEhC<~<&{&XPr$wL;Q#kHLleFJt^B1o8$I`$wG@-{nBb3<~?7x}Jvzpr^ zqfs09{tnSOI{HzK{c~oDBI2|bGA02a1}Sg`KA>z?>GXb2KG^T=`eQN!&5x;(*O(VBE~EO?h})E^*uJ?RzMo(H&~tqLd0RPcQJz)g^v?^)24+ zA5aQ`l~h=eLT5T$H4WGqYIVjyxQ}Nr=Vh*2Alw21pDs?Z=^7h@#HpM-E1B@u zC(emf`x|PIDIsKu5D%VDNO(!G?T|fSkBs_^GsJx2j`OmiBc{W zAqH)726A)+TYun$WeKQB=Iqd)eu?x0%*r@wrv0%JO3QJcJFf_f+M|!hiyiA{5iL{O z8&g?ESCFtXQk{LB5oRzZBiF<>T?ZlTJg`!Prt=&vz5fr50F9~yL4>w3Fpeqew1FUJ zRs~?+b+{nH)3X&;r9!I>uI{&3FG@T+J+Uk;1X?yQMnjMsV&j8~ozU zYmgvZ-*2#5+NS1i^MLb{HJYwVor{@6_QRWJPo7FT)CAA8JT%RM`3x#$_ezT1okJ7K zBXd%-@p^pts~^3i5g%!S9M6WP--~?akdF9$9+fQpY@L8Lb(2yK3WLA@>Ua3?u)$BB zJ;Sfwe1WPcux}gGZR=QGd(U>dv)Ptq2>>=SiM_eUNG2R+##0h$$WbO}(xQ10*|W~F zM>zY3q%mbYP28R`JdYf29iDu_LuafD-Zux2~O%#_&-h0vunhY}f7(jc`% zB4lMCvwI1Jd*-9}Nye13N*qTT02rRnO*Z_Ck9hVobI{Wb#erwmGtU&h8#U3)o*3Q_ zQJUL$kGhu%rv|0S5k|m!P{;r*z+?u$75Kl`OO^u#D|@DDBePDPN6`3ty>oLPA1Xqk zjX9t48>lG4dO!&U!`k{}pa;W&w(GEIc32iAzI(XFUAu!I;H)SiiLh@v8;ztjT4Qil zlqiHiZR|{L>CE|-s=!hexNG*f+1A*K7C(Oa5`rZ5ZHvp@J??Y^5Md!CN}S3`s_d< zMAWZ^cfRqSW#Q0$?VPoQAV}rHl^r@SrM-Mc+t-k=|7PWD)@ihV(1~nbW83Ix2Eg06 z$+8v3*x59juQ$qd{e_eV*3twHw?YbUL(CNI+%E(Dv71D@MZ(Ss80H}wq(_5|aYZQE z^C287(@XK#Y4XE{+E>+@Va1{hFUW>5?O;rPylvc1hyO-Xy0jFb9rp&dMJHiEzI#b( zkL=oOI{u!2FQr+|Au4b06&`Y?tsE?Te-h+ekH@(*8syr>N+yXN2pVR*!NpVr8!^Xc z4+#R=GwG=SYr4 zfdTY*b@R^1$jDYvvYmm2dzA5*V zEbv`HeSnKcqq#vM#gU;7jn4W0{W+eyP%bz&kS?bco-`_2azK?yO;dl48-)7Wg`KD*U0{{E{!3}w1z#()3i&+zjvKE?I@ z1D1;gl$5x+f55UTaI##W(>ltvk6D8-bDZMrZcM8}#y1d{wV4h8G)`qFxfxo&DW`m- zOaIiFI7JY?l_k;{;9PUb{v2j(N+S|6V85UHf+g|*TtQ>=VL08_*sW_r+#+X2TwJu5 zk`hhVI%76DM2TUTt+R>>ZR~SXRRx3~eEt3szy0o8T<&)G)vH(d5C8tJv07Bv?rYRd zW6zIaNcUZB-6Fg_-Kj$YwNbclkXJw>aQ%qurTP50UPjuFMEH_d+5dhlICB3Uq);Mc zoqVYt`D>upFc??gSisEPM}6H-{Yai~n8^XC5}OQC1V(Da_ZCM)PR87kIE)FJYOV%J z4$q_Ov1vISYX8O=dLA!IXo7^?TEUZ_?K6Yy%3vmr{7`8Ce#yV4{l3FEKH`WYP{PZ* z58O#O$W}^gQQ^}uEXxcWwTtdqGjn zcW!O6O!2FeCuq73xAh(;vc!ojAt7*5E%ArVEq;CT9t8<3rLu;cLtSh4j#AiEg+f@M zduKY7U@Vlv-e@$eo$(fMUvKdG;u*9tXmyAG{j2|i|8(^YDj^|h2hEIsxcwfA1U|1$ z@OpKQPuEW%h@mxu+2Hy59ILj#AFtlyqMR$E$FRpUy-Be&HF$Xltz%?H(IS`WgsK>q zUlB6?J@C*M_MG}`3^s{-MN99=!uD3E5{tf@qU?{@MQK_qd10&CpN^`ay>849dlgcc{!f0#RG*} zG^g$ZIm8okWDW`p&qFnmpG|#w%wmn_nS$BwoLNvrSr4;w20?yb2*74#kd)WBPRIaa zrFVvr_W?Ny{e;My@f+uX{06}cvqvNxfZ^R0rq2Nup2W-B;CqQs?;I_qD@=1tSuZ0l z6r_+Aj3LX8cE$j@Kt;be0FVdQ=~7B)HnxE_)`@wuYXAnM04imp4#13k(_po*F3@BM z#xdAE0}N(#ooj5#en%w*jAMpLy9Sw!^T86{3>mrOmm>f{eI%4U_D**o6408Vggs}q z))25#Zf!I!@9uGadxAw-qOKcMRe@*cXV}$ybX{j9+ZpHYN62+%B)yoBq8<@=N|A(^ z;(6M*h$c_?!oicq$xI`LH7q-bN^^8kXn4uwt@TmkK#`wer~>go&bd*MVHu4aS-qp* zH#LM1D3motYr7V0r?DstoGh1k*l$5ZSd=ABmP_1i@9_^`{Rtl)9`Izj!2j}-pWq_wEr^k+qX4&Ma@@zhZnsa7soIib`IV&IjlOqr{Rcmykql~0>V=Rvg;T7W*M&8_3K&c zNjM%|t4a9vpld)qh@B|}bC?Z`?weEl{>o+855x_8p8&Qm9maXa^n&qyN5YPM`kG^N zt&ublG=BF!?yuj^`~HZX!J{>>wEzCy!!;@r76f38z3%Y5TH*D{IW}#L5A_xu8z>=g zu~^}<-lAoL&K+N+Y_q*CO00_#ANCtmg;lULO^b)FMbYvLQz z*H{%L9_lU5muq}_c7c!Edz@8E7^88^Y-5lt5Ge+RP6RU&@#aDv!`ELtF*Gz*YUiw2 z9ExKIkYwLONIPgP-0zBk-55WM0kF1A9=YFRa`}^n!6FQ~H#Ev&L4a;8t1%)_9H5k` zDl^ULJlsTjGc+8!msljq6`><}=olh1Io~`f*Fk=v;Ouxwlpj3f=1X~KJ~CoSo9kp@ zHXaV(V6?qH}%)Uyb|A+wUjrX^AxHvn-U;p@1>~<}*)__ujIhd3JMjt8PWIWOoDw1jX5m-}W1+n2F zhmyza48C88`x&`Oyi9zL)8t+?rK8a&t)Qerr!}_q z9>y3ub6yq}e0K_iY|}dnZp?K+J~_ejR{NZWRQ3vz{<>2}8Rq+ckRP2C&jWh^hJP){ zQ?yY4>`@YHGzEAbdme^+CvwVUWC#RL4H4drhh+G3KaR$OGM6NYqVm;+iNR9nsZ3d8 zaync;=Je3RI6<7RJ%<`Z3@&C&*b*Gt2jVz;{|gcs%$kxT3mfUslZD9Ro#PlV8e?C_ zLv`aqEWEGiDPK7CHR(r{h1WZAF2aid5^?-yJaXx&+9<9|BQV`#b$RgaAUFEdH~kiP zHpY$;wOxzb-3B-NEw*g~4aUx?pjNWL^W_?)RCu?!#Y!sYBLciQImf%r9X{4O2mn7n zeF_1Ix~=hSaf*}5#vR@5HmJK6zqxv60catx6bc<1Fl(z6Wwvv@U?IMkjTeQxEa2o@6NP|9Zvd2vH#oEhyI1VKqie80$Qqamdj zq>%P?6ztPN5)25!vBu=?Xm%8O|9$^n-@%zY zC)d7^Vt=OBHw@!2IG&GK@?&H(>m2H30_VN|kYxpf6PCv*p|x>}0rD1`up=GL$soIl zrRs=@@S4GD~_F*@ZwTw2r+QS`ONa1TGx_BGl`S6wLnTyi;|=TR2_7PR7NRt?Z0zo8&xFSdnX(c zPGXRi&olk1!#Lsh-+YHR&u{Va<_hBNxBD}%@lh`U zfR7-=z(rU5ZjH_Q$NKLpCWlWn_dSoJ*Rrm4**xoFs}z{K%<(>sftN;JO;Jrn_))*b z7=!aT!Wx5TSJ#+Az<2j|*m#G}U%tZohY$Gux8LCJ|L{kE2><<`{V9I_$tO6RP8h?4 zX%Z5yB_s4`GMJ`-DF&p(7`%Io8;;eW(MJa)birJ6{vg&uVY7=pEt|BmpUgF{{TD0^ zdbl6p&3=0>+-pxR!>&clC9h3A)==6Dn)3_3Ff?~#ZVvQfxHpRjZQX)bnbD5`?-w7P zN~Y$`;d)30&1#61$KRdLv&Qt&HG-L% zQKC};^5%7O+o%%IxURRT_-xOfU2Eb5P9mo?`ZE&5*wJwB7GGc!>X|3B=E~j}3DbOi zHUp?~J_Ci(Wz|6|`+wv<#}MH-9q{gSz`OGSQ;LX*;f#SZ2EV#}iK}6YuOHsy7$^M2 zi%$_a;ch$vAUHC(_5(PK7*oJsfBi4`@_3J5Ts_DA=<$nZukroi0|w`C2$Psv8G{=) z;1DMq;smc(??@t#LSir?3^^G>b1dil#0gtFDB3*0z<>?m6eb)}z~`GQ9Ad!RAeqG@ zM_3}H1UMsufWJSy!{0o-#joza!zbH4e)a4VJlkF4`SuDy>znIg3zs7;j3t<45X4fb zxRvJI*Y+W$m|!evexp1rV+dqT5pBmP(+5Cwy+)hj6os2Dy#7X5j;!6`qLoRU1>$)+ zqnVU!3T2Wpb2lax%{S&vOnu9mvIZ9`T#-WIMJ>=T9^hPlXE)L2CJcgC8Wz(}*g35a z&p!?gK!2@;PFhLB32W3MTy6yTq9aws2xmwH7i6h?BNPbTJ=Fe-*PB-G= z1tLq_;QQPZhGh_taM@Rl*U_9=0nLla3w1i8xuH(~yXFhmC@M)lEi9orI&*Qe%sS(GXcu%^uiH6^S_m=XcDKdxJYq}<=MZ3xG_B(l zu^k+)w*#23@Zx5N!8uqVT0m&G*UN-=~n{x%t@zmwjG?VtD-wc>=X<-%eUp6|`|GANTX-%`l;NqF#b#uu@ZyF3u8wz)F^uR!Z3)Qa6`WAQcZ271ouIL)J&Kcr#Q{nNOQ%^8e!_Gtm~QN zekE9QHG6mo0zYA3IS-RvHS_wR>*VA1g=Js=1cLqqD`fut?HVGkhKlX8%Rm{aPM8`b zxHP?msnt}(iWHmXi#hf&Uh{g<7?E{mU2o=Lm!Wg20AQ(4TUvX=Or_RHwXBg;*Dx)C z3~QY9Pb?Z&W>R{!fmOc?()!zyfi(tWm~ekO;ut2}hY_zfd%WIV;k(lVetP=~hv|${ z2>8X#OAOZI*86flKfSue&JXyzw_o8m_wR7#i0{LQ#EgK1SNj`@40MAG{fxzyES?Wr z@R@Ol6KrnM85ooJWCUiI7y-+A?-&DF98-k14g`zcumy~ffjtRhYwreZt;a*0uy-E! zam17oqDELf#DFW~k@P-31nFw8Ham5?W)N9C-(SNWk5efupy*!FT+z&6prKN76Xp0`}LX<}Jy)R-g9B1w2=`%ZH> zWW{KQdFJvlkNTm!zyU*CjG*$Vl#puNh-#hlfsq)n3;4F*+Y&#%F&RTkV3V$%6Jq=tIm8g-;@V50xs z_8&94U(s-s;lO&)$8!qc1r&oj$He{vB%pHu%;*GjBMImz)^kLEEmo`7;LSSSCY!l7 zHGJ{D@+CIz1w?oS30iTd?M-D?P$|Yrf!@cT)eK{-QrFN$GW7Rs7O&i0M@OEbek9Z~ zB`~|o)fd$ohLgg?H!$HRF7f%M=J%JSeGZuc7u z&VdMOSW%0ZJmD_;3dG#23=biH9wWh7i8jb+Z&X7PV@%05HtK?sG8rOiG*q)bGg~LA ziPjV%oH3=CCbp}-1GNQr z)|N=FDFj@p+1WpTB_>ziSp3E3pX252HQs;tfbGV^JBJt|cH1pNh!PpX2^MnRZS7D7 zOH&9~95rp3b~?^1>=R_d=+Eck*+UH1kN z{lLjubDk1?#7t%_;*2gEhAimxmsu-w!_W>ep(U%7XG}tBP|-EwbVg^*j2^lk8uAD= zKi8(}X_HiHWGza6&}T8zMFgCvvKGtpZHs!Yxx($BFR?n(CW0|sjJFaqB1=nn9!Grj z;T^s{eZVs)edWOWnyKq+eGb?+mN+bg(m}H8v zq9HGS-;tk1QunvX>xDW46VwLSorMDWI(TZmiDN`MwnS};mtw9J8<0-)THoAffLJZY* z4TGz~bM2UoTK-2cycc&)GE^ounWAG9tu6*=|TmQ4SE+UBc8N_ZJaN@i{*Lo&m9W2{_`_Z+i?MCKHcYn((z-oLvA(J zJew2n`XFG=WaZ_cvG`jzC{59{F1Dd2|F66^67^%9w4E4txFE$xqRdKW6vG;|MUV6{EZu zY%QAQHg;`h`@Gr%@XE&W5t+R_V^l3I?v~q9EiP%f>#W#0)Z5MLI?v{h2Jdh@pW&>- zv#V=-`~DsN>FcjC#tA=p{v7}HpZ*Gm^BK-qn2o_{l;X=ei{t5p?XZRIWHa)vg%}ZH z#K!xc-9jrf`A2r}#Za31rjXW57-+U_){LLUU&5pcoNJJ~=;mVMhFH`GgkvZ!;rgm6Q zM2|Cu7Xb-RhV4DRjqpm;_AG4=D^ji&pdy<4QvpUYONwU_2`Z^D z+%ddUGw7;L>YC)CCN{+Mc+<-@Qn2--NsY1Ra&{osoIkT!@CvDybgg} zfG?iEfwKnAIvmCm{`LL$_kFUoA zUJrYGJ05W6B()5HPqx=^#^8P$@w@lm;l=(6pWbXiMuu?C4@RdJd9yN=dYn;UYAMDj zu|_eq%a9*0?7*n++*Ohm82&KtMTIm>5r!^@tj732TLreuVHdZFI zWO`e0kW5;wMls3iG{qK`9ji?PS$1e88KX;nfq@y{V9d+W1^8Z z1!DgDsd0{OOZY}1sm3ooMQ&Drt+^@EVdhqf#KzL`N+1FjwcLYX=MU2r$hHlH9;$v|WcEiK#dP z&Ic*nIX17%TDWa(TSx4EV%nJ&VMRhkYjF1*?xdMR?CkS1U6|TpxoLk49zh>_IO6>G4u>m?zA{Oqc-Pew58crYSjh!k1pJ^3kE84Q&C>chFyND%$%^=w1to` zP5}&y!Dm#p&UUOJIAf8hPLR8WLp2VAv#C`|N_Q5@bAGp{7_svM4(BssN;plSTu)3~ z2BYV((xP`8UucyN#}TfHsguYFqbRW;*-UZpTYUN}V5*G&bjHgf;Hbrj{NnvVJfU+&0rsHNk{O zsV(3Y6l4d-j z=ilO+`+Gd!?eXtE`wXw1-QeAa2N~X^2&4$>telC=xZ3R`ddxeJ(h*tXls4XDGkAm$ zm$=;`q+qGXTw-a!sL!)6g7lx=8O+kOi|A-c&oyYy#XU%WN`dBMk_>uSf33r*^S957Z zzWTf9>9dzUqU)YbBvRu!L`1X5Wh!xH3BBv&s{1~-afIA45L@X{4L)UcfyTw?tWHAN zyuUoli>kEj#zrnrn&-enV=`GkY^tn9uK$c2VKpj=l;K#|Ps-wQ)|Q4ln@aAT$IotF z;EU(4vEOX)&EX#Z!!LggOBPd{@aFm%cAE{(Q-CoBU)_I?|KpFp#UG9jNMP)Z!;~UE z-Cg5%5AWso7>l>#317Z{i|fr6S8l)*qZnGHR2ia>crua@Om+HZ#76KK!ALP;Zyav7 zS8&F`nOOQ#XD#;LV<3xbzrjyHc(&Q&?eQM(;)p*VUcniIe>vX4Td7f|6!8#79Am)7 zI^1lws`(bQ-WjV|y{dHr0pl3EW;dUKnYHYkL`49A8?x4uu-!U5+>J;!VRtoPJVgu} z17@SHyS4c4y935k#O?Df ztU7vo>FjwPFiEi{fLnZ3T`yWE>EY(FgAme`_p9K4TFO* zgmDaT&gc|Nkc>E2%`*hno-gLwUMW6Oh$!zrh1Zyo$=MmmnoX3FuZa@gJYv%JhH(Ka&Ld}t8 zu7+%oywaGGe*T(uTb`l1qlYQLIg1bzybfDpO4#oPJUpD?HHWpD(4l!wG7z$4$~&8i zBVj7cjA{zn#Mk5;RnFIko(8Lful$+iekWXn5*pZ)n=HHhu9(iePB%ZZDk|@ePiexq zWPa!7CFf`7gCsXSxv|kLLJz^E#;@n1p{Le2*OP~VSsIW~H*%r)_nD(*ed;qxje~Nl zL!zcZ&DUd1pLdz>9k=(Cm$X2}%)^9gcCyiXb2yDi3^*dZxZM{zasEE5##hV`S|$;^ zt?MyBB3oKh4?_|Ule0Q=u`feZ3u_o-2p|CeWP*!a001BWNklmm0M=S;v@wk_;vA;(J{!n8v+)Do z-o3~F^^gCAuRq*j=Lh`y^Pk`+uU-l}Ap|&UOOc-aO7gosP7?-iOR?SV6*0x8gY8?t z>JYWoGjR9(2tB)O)o%nmE5Zp2gQ0mZOS5eCHvOaZT3KVbG>_O>+h?+4ZVH)ZehRe_ zH|mX89lomxYj(w@OHG$HiDu4%YMS(pa^9iCQhOD)VoNhk+zsGcQ@r+U$^Ty*x1bF zOzqql)ySY`2*Y*2TD>1~9T}UNZrOp>dMRCO$SF(f^ySDpUBc2QkPB<5dS^hTggs-t zp6iJvMkzO-J!4Y7&KbL!07Wy_)tmu{X80aCR%xhStC(L|7_v&o7CCE(5fKT`h8_O$ zlb_*gyT$L{euH1V_yjQ}j49xg+ZV9L;4q#sh9myZKl}#&c>fNk6yS(3#)z%+*gA_l zWxVA3WHZdu2+kOMbGXBp0>0SY;x~tP;6hQh7>pN56A~N|j!BT;0LH(+ zeS?kPATZ+;f?U_}0B;AJV#1jNHqK+~Hn^WoxDRJ+$l~pI#Fg`4FdkySU*5dIzZ~A- zx!>Z=<_dumzIwRB>+2WT4;wg3aGCy6Xs6m-m6y$_Ex8UZBvM0P5K}dc%uxyy!*JeW z7;I^rV$=nvO@wo%YrtG-T53%=S`@=U5$l0VHh* zQxb~cZa*wEG8NvOAw$~vW=eQPS>=-$jV;M$TCL7kwAWM);bPrxq{ug_Z|e};mPXW4 z-&11>MJPjpLIY`asEQ=Tpw~x6QEfdKs7A+F+c=0r+@ho2M$pgfL>tA9C#7)?O$-w=vT?9*!9?a-PV#Xua&(vq7W(LKa)>{xTjj^GWswO#ciM^@oq83Bc3^EcY zeIBiACpFJropzI03sF}P=Wudv4w!yg$_yCWJjYtnR%u4j?xmuwmUOkotE=sn)+H??y~_|ADtl6SO2Jr$WlFoi|Z)O+~Uz|zguFWp&q5^CgHj- zATq{j!g-uZ!C(y)7EK7!97>A1f;Hve$dpLj@*bW;-Sv8V@xfpL8 zLo9_}YWA}bBF-~<-58>(D7cB5LdgS;4V#4lQ0m<^TbFL?STS?s9(i`Bl<@8S2fRO? z5K>K{{%{)cujxI0>0ZKHgP>W(Q%cx6uhBLMfBCbY;N|TNu6G-Cv^6kJhc!epWL-3> zvk<$pA;SX5P87o{r};7>r4Wkc0Pdn)&NS4)`W5z@ zEv6W7IG@1G`t$fgX88q| zekw{&?SrHGNFCFl8LR`RR^LtNjgq?mPdz(h-O@A7x!sSLUg9x2*K9ztmcX!*os-?R32cgZPKkzZO3HGB*dZ_`_5+Ki8giQ%nC zYERRIV+=?PT=@Yv!yaEB-eXD;Uu?yQYv%_1w=aH*zq$JwAtijWy~exogqvZ9pIpDd z-FU!xn!p4M#=(_*(@cxg$vT&S^4xF=5)y;F_=W(o{j{lQbV(+k)(GOukqh_Vn8xK) z7n)%%xw*($!MUk05{Hz~Tj?39a8IUB8$#2#$mCz5ml!F^-#cS)K2IQ6SZ82~6?$h_ z7#Iue_Zt8SF#;ikl6@@5tm!7%rO^VHBAxY#Tg|kThD{K{5K?JoMcbB;Ql?K*^MXUC zd|8vou&^o;m!B6?LW*iArp-l)qM^=+i?syj3^)PD^Mvy`;A%e<1UMs>?Ms+qDl{gd zYBZ3PH)*}C&k~hho9*PS6Y6_=AB?SQ%z2~QZXI?s1Ll-s@gXmb9oNPtao-qLib+Mb zq~T$tG^3*EI?1#&Dca?Ecx&X}4HYej1dZ2amsq1W5_gSnMYfxQp9GQ3O+(v^eqdYN_ZX(`;&*H}E`MA{KYj)NJ5lYG_~#;GD%c zhJuV%S}B!0Sx&rqmgZ-YuO&A$t)4$|(kyTtOtlFf8hw>GHm>5SJa0qQ*}c$N%X6yN zXN_r^$36#;Sjq zr9n1KOZt4)&NrIUgjh5)*3|#YgB`O##9KZPV4gBC? zNR7j|>dxA|p%<|=gVJVsEZW>fj_U<2`D2t~TAB|n5#gqVz09S(SgkGC&b0C)=mIbL zLWImh8%;}qdtCz1--B#2l^G}1bg_Qh{(6HcMi`ZR{r&I1#Fy{Bhb6*)`1#LqyW8XW z^$iB^${>jhVLJ?hZZ~0M5?u=~P z$F+7~Y5&3KqOZZFz{Sh%4Ai*gbL)vfJd1cU4R!RfKIid8TN~lI?5L4^6w}APsS+j< zgVWlpB8Gdd2~%liSInYXHU@V{;VfFS(Yf}l;gy_NtPEz>SQU>V!TrzeB1QReZ4q^! z4;LCq?qZ2r#a{vCw1I2U@G^X8>bjmyqtUFFGO%WfxZ+Jib&yKDoCdb{y=!vIwJJEq zrVOaDU|r0-YdU#fedf%xB7UfbZM+bWv__=W2u`xh1B@ZuY_{0B4V;mf!gq%c7_7&B zv%wf9{O`Z}2Yh|F$01Jm$?h7P@q|MRh@3ELHt+5B3a1qD!tZg8g2r!*gY_0Ww}H0~ zd%wf>cED*mVQ>RZG2q#-$CcgS`{{&Z7~#oaPsO~b*L%DQrvNlN6I<8RLxYZp<=#dtx*lDQh@R%*)m9|PS?%s&?{gm zYHUYFyqPD^35B|6ozYBXMoJYGHp@N^M zh@0z8-ONCMVa@%LgCuW?sX@l_@FTB@^7pf5jHox+$`s&XaK%lU74;GWpNQ?&E4oY_ zawJWO*r)e&v}V&ZMnb+*HCi#I%@xe=TGgvNCvd0t4q*xy2CwU%n>DS6slHd%O7^K$ z$}(jtJ|v@Ca+-TX2Wrq<8FFxWUph)-&W=-C{muvpvXvO!e3_2Hye$g?Z5taG7MIZLG{jwxZ|Yk_haCp?_Tvj69G;It9(&f@L;5o4GN zB{j#zY`m{7`ZJVlE||=d_o`PKE{()ogZw|jg%%MHf`%4F4!F76#;-6XHk2uvYja88CPEKG=0 zbhrbU8TW?=Y&RPm&L{lcmtW#Aj`;bj*ZBO^3tVltYFd?W8qXrDQK#>gWk8Lo6RpfH z7L7=YPCn*-%e}-Q50X1ikBd&o=omcU+JIHc=_BM;;;xnAIf~WENND+aF*D)3I9TCD zS3*q`TVtS6?TvUnlXbp8X>H-Q?$osOl9(HRF*S{A{@sm{D^_I$R1n^l8G|0Q?G@d$ zG8W)obW1DV9bKek`DnG_>T2(rW-PSUi3Vjg6T44Wu+0WKY~jtBQ5Ox`sZkx0eib1jb;J&8vgk^V!D{l zg}}P)Czuz!m{kYx6=t*WAPuJe~uU1J*GI}lVOkB%?@|rgv5-E^|;wMJlpOIy*mNe`VCIg zgy)+pyx!g75JrqC;vr6g;MxI(21DAoImr!k)_QW*hgt&;x$)H{WU9v17=lTFb%YdC)tWTzwBeGG(BUkv2i>IYs`7^{BOtxT z)EIpLD5!3xU1cP;mq^siY_3b(mq4;lPO61WSa7$Xr-+ z{;25jjCw>&puvNzP>O>NIK~hV6XV0-jECbW_nfMqfz`EhI88X5M|?PsaNpnKdOP6x z&0aN>L75~qFSIn`-0A{R21WT=b1X#OyP3GjDCb7!>G0``8@vn=V+b1Q#6?@~on|On z;4}p|WAN`jdxQVx*MEj@@7}|EFHDLqTUysG_1f)zWi?u|3(al)yEFs+Xd$(^L|xdbCbA^l2%;m&~^=qR9Msk;=3{LU|4C<@F-ct0Oo-a#qaHrVqy> zuJ?QV&F_APfBgCz{QTuh{7--NR~Ved`-cO*zq^NbLKh@5FqFhK9E^}+eFs}QWM}j^ z*Wprsn@yWh+eeXQW|HAGpqBfDgBeRC?-{vvOEZ_fG9WT*=o4nt%~snxOLb_QN-SNA zQl!eP ze2mMP+)1S}Tdr5VVNLnDjNR*z*mmgD8+_G$h$uB?j=Y{MHJdP{&XIIRrM@U%;6{?d zy^eOqSZLAgjGh*slx%#cLs0;TCUw#=FIpYNEOFAiZ#^P!abe5E{WR1mO1`rVr z=Q9!~JlpPZqdB=_j7Z0#sKO^#H}GWfPw&1hHO9-$6+BtoYw|V=S&VJN)tP zJG{QS#l!dxpI*JdH-|g?_V6B)dboIFupx`UED|IloKwP|-+qE$zWf~LAj9Xud4v>^ zQo>+8j3HdP4G4rcH_vdMCX7*7S}(TO*cyks@qnL!@Uv%c@Sop*gFl?^vD;i>QiuF8V;?8v(xskNi z^d9X%M(B{d7=$8hP1Z)U(g$VJ z@qP0%Fo#C*zGDos#sI%7j$>JKLU)rkvDC!?{(>LyGih##k-GZq_oTVD!o9y5w< z^xzgCZDu&4zsnB}6h)Vp_0`$8LFx*^1Tol7*^w`$4h77bL$E>M1SBf7@9oy%aF_s^ zu;2NDSdt-_5TyV%RGKZTnoQ?%<0k`;DJadlEtJ7HpC>qL#cWKQpG;#iM7Y`yrTLpe zESujHQ|A&WgZI=>k>vqBJdB+ab#L)cAWKDGnHdRDVLR)LMs*1?c{ISoal(t+EygJo z#LcKpdww^qvuX$>zr$Hsqf_M4Xyj%l8!)ut?*#!Tf-#d8chXeK)KZ59Aqqnv>!K55 znxan3>JYsd~AWc!i9jrdE$F8{)hM zbot(JEfDfhFqx8O)EQ{Cw7Er5C1dh}Dp-vsI-RG+EXtMxyOjW#W)MPD(F>-C*yK*; zLd6&Zvmp##B8-wsJghPBmS7Fz{&ES$E#U6f`goJ5!Vk z3(SqpmJM2=@|Rw&b~8jGQF$zTm1p$n`TmuS4xv5?y)hWg48duEVYaNH(&V*G>IL`u zIlX=@zqzBG(2{;#3`OT`(dz-?EFw_s*Q8F z8Md-V>(@JL;giFQVUKUd2i%7<24k`F1B|*!yJ!&-;Ii&(t{vinTGZ%@eCQ{2(WSj$fuErLy>Y0H`&E~`iQ^I!ZkYX()TJ)1+QkO<( z)7EGr^kB^{+>{c?NHHCZ0pVPnvh&6glT__FiiJVg4s}3OhANrnO2UVY1u+#gDHuVk zg!*oURpEgtB%DW~%x(suX2d93TrxmtVqUW2vowd&5axA9YBrX%f$We%T~k@P_V(_G z+v^RkwgZA@pXLc>hzV{oGgf3n6<1ewIrOJqLtC`Qa)?er*4E%Hwj0q*hEN$7mxr{h z`aJ4}Z_L#oBWQy)PO;D%oo1Ok=Nb)b8}*@X^@BxfXzAIXAUF3_f>8}h@){FY#nfYS zM6*oN=AEO)yeYfP8&h(#%dka1pD~1SiWQk=YKpH)i|0p7OJYhG!-O$}YCK{pbv>$n zM+$mVik_sod)7)bXd6?OI%!WB6+s`TfEXD;?^jF_QJouejxQ&|Ze#WD#kW9Q%nAFA z$7u{WP66M%`+!%sS9S2C=v~!TW;qHKmOvS_X)`OvDX}5~`h4@aLK=NJ1A<;hH4YM1 zPGZKC0*FMK@rze4@L&DuFYz#*@%8uL+_58><6kOIAb0_Sh-qE+EK_=ktaKPM%Y+fFA8R)YwBD#c7 zx30-+`$%Rnpws3i-P=NpcsQT%#f#^-+6oQ+>$mU3RTaSC93GB`rYWH^49vb8g$|v6 zwUOF@TBNdNe{{%Z`)3Wyqd|)jasTmVg92_)EYtiQHNZm)XSU|tt1|Nrt>WdiJrQ?P z4XT>eIWzTJfSGeNjOO}vF8g0gH}9y&v`*1QZYEb^BiG%v$aTMN0+>F1K#8 z{NvT^@704`?;l@acxvt5GUN?415GkrgJ`leN^6YPA>S0ku~a_)e1JH=L<8FoWm@!{ zxO4S3jX`Bg@o$F`DKp8y&Aa);Y_X9UwUKT0-80=8(Y{A*Boud&0q&#VOfw9F4AvqN zzWeYV&-Pcq1RTdR#yH_-*x@*h`2KXj+w&0?2Cw{pjdi#h23TWoz1d=n6Q1v`ahyg4 zTMVv-9lY^4O$US+u^V=HIG?j7>+N2AL-b&E)~SyO@MixE*P9*QA0KeL-GKqT*j~%NOa|A(rWm?j4;wu9TYLy3 zcFvc&j#-*&Z=J*-aWa)cYc;MY)rnwbAPXX#n?p%Wt(z`E)rC;egHW!tzfMn;D#FpAK4FSYA*@J_ zv)%P%?Jz^=q$TDGKnx5vsa%6??AKG&0YM^dyif|rVnP+87>ITH;7q{=wwmjiawBUh z4s4cOYu&W^uQAXso3b1Nz1=n1m$R|*i2%Lsd=)1(SUJjfW+`B zl_qyHI9N-F(O@%JZHAJX`k>HmB|53zi%hl7?@gwPTh#{VIGBu9hLGTl!S&S!(-d(Y zCoy>gVGI$WIQUBAd8=Jy?ze952GP$(T|>eXOx~w%g(T;fQBfJACoUYaFLD ztTA|gbA>;C`xcvFD9v{sXk`Pog|pQh_$nrN4Z1_<5`q?pAK{U*vzl+ceSg1%<`~O- zkB>!!d5u1ZWi(RslFI#`uei-yUIWHRi<9S>*OtUB% zT5=k@26gU?yhMN=p;*!^lBLrO&zn=~{4>a4Y24ES^M+QhjaJb4a*d{smBQ@%Y05o^KesY~&%Y*Nfo2mLiZL-36ay?{@ThA>Ni0wbYTiU# zs!U62waOmKR9$LZgqcMTT8h58jF;z)8n3K{+W0c9y>l%+mKVdN>JLF;45gAeQh8ZB zBgn%j))=Qk4R4LxsHfas>VZ>s0X1`)zW!6$NO_G?p*`nlJkvxcs>~212C2*q!)3PB zbu5FKOJ$prPBWUn-XCJ>(s%2+rA|v-MjBjA&r*S_TCWhQOqCV6M@e*9L~PT_;ZYe) z1QMU2YD&~gQ*_{$vx1{W1bTA9fZXme#(;&p3;~l;`K49+$d^;YovmO&O zu8hU)R?cK=EcU|=+hK#jd2LMPlfOS7Fol5Kut5Uj?(~4&W)BO(XSc8L`uZ83?{Dzk zhxhoKcYg#aJ^P#jPATH&yIZ`zdXC7F9~{&(gp2{m;2h3y&f&9Xufd!!c#kh$y~gjp zdkbqlp6zycKI}0#F*m%~?!}dQbA`b;yxBg(DFnRnl5=cTCgTVM?=yNP6Hem{!W6|l zn1K}0HHBFe9=YbfW%H<%#DpnMs;Sk?;NxM!cI!)VX~=*)iYPucy1N*Y7PIn@tx_>X z$C-^(OvzO}9>!9H8YR#>rQIDKChT?&+pU#&qSC}x`d{Z>$Bglu;0G&(6h!ZeB=^8L z{qKzI#hOQ%n>3>w{PQ`K-vg3sc2>XN8*mhqJQwJNuAC8e211mLz*s4^U198bCqh@s)Z;S#-^f%#!}}3on4x(qYeeI);345HoJRQ&Cro3dk?F6Kwd5TP0aIXD0t-R z#*}>H^CTIvG01>mP?NA2g*oD_fg2o7W5DfxgWtS6;A-o!@fL%(IFHi6S!e2=q3@pP zMyX`Yc2rraM7gY0EyhnK$6zH)N*$g;z-EyA+i%_<;T_@C?G<7Yb2sm7LFE8J^Z<}R zZ@+W=Q9EJo-L|D!Pff4|l zI%St<=yZ@I`LgN@PzEPzs0T*SsHt<9aJAducp6K@S=Er|B0ZJDj1`5<#$to>_|2DJ zU3N%rO{1QQK)F!Zb}=aQC{KFX5YvxrfLDK4mz-I(@iQ7Po+rnNa-Xf@(mfL3LHZ%GN~pce#5C7ow1}kHl#`2|o=cWD*C$j53N5>a(J(Bi zGb78(Mp!By48l1?UAIO70BtPE!WxG$1$_N*hu_?PhyUW`=lJd2cOWvj^;^7~P8h65 zVqkC{V+gq2USaDuMfT+zkk49&qt@0=zc0T>&UhTg5!ahNp8GAn8xK;)7>B?Sw|;{d zCm8E+jzI<(6`^t#)>Z9=HIe}=RADO7Ho0N! zXgS;(REq1=ZtKful%`fTz7S$@J~pTbL&?}wQ>+-Jc!xE`J-Z6+&inj9(OR%wR?>1NS%+C)f(I}HXPNL@x1+i6$p!kwG$ zGEvU=P7jJ{l6Z-{=Okvt5Rp{yISdxo$m=p05;Kan&}g!V z6W7DZAGh)4yLoB(nJ?+AcHHpb$Z^PNp?vUKWUpzEQb#k1+nfq&yaP^5^v_M1-nXFcBYB-NwqZYoDv(FTc)0QtC2vBo;{zY`F#{*HBWvs ztyR|D)?0D+2HTy>W9lM!U%yr%Wvtz5x*np7Jj8u#z-PdFS$ zTPqL?{h(h_a7uyhluD9L{6W%;zCQOwyP#5Lk7R_ZB)ykwAa zjm+WBI*Kpfaay=9EoT^ARva!M`F!E}ntbzaP{5j=u*SQmuJm5#rLVXt&9uGq#k5Gj zqCW5U$@_juIn(wr3r%s0_EAqWrIiMKP6uDp{ZcD;r}gVwn+B}LDz9kS(Z^Y}`gY=8 zkG#edpzi1O^!s)oMxdG7emygK6UP2)48@fjU++~&d1%inOQ{WVmaAk*S}~I=WwJyG zaV?rV30=Dnl-M6yCWtTpxsqK>U4Z=Tewj{jPgv7%&q_V?di8u=g(b){C+Au7yWEPF z8#tTVtIydkXb|XH%~4aiaAEQ#W{ff5G>u4Lyx8n<3ITsS9q?y2udw%9;P76|t;oW_ z;%YO9pMtSn&UZG)1j`Ve8`-viM+yQEVQ?NdPH4$q{a73fJXtuhRpwO;LrDyO+h?xX zqzMEv>TO~zydB`I7fBxsys^0QTii@%T=^~bep^)P(4o0wPGzd&22+fKydpnD(rg6H zzVs!-RrM0Kp@0~IA8bKg$PlJ6>Ezrf%`lcMd(O_h)|7nIh-%K#HVveiw#eGf#E2>C z_tt#Xq!iET9Cb>r21KfHv!=BR*J9q7j%T0I@Qi9XM|HZVbik}{ZX1C-1laEf$-+Db zp_4`~jk;@0#M&hr6BnZ-p`2#3fK=`Dl`?0nMDl>EVFD{QsDO8KeNsYmj#<|_P z62}ul){!RUh`plEXpBBs8~9u#ro?E?5V`rF z+R!N-Il>M=^aNr4zNnG!YBGN3YLH=7Gpy%87PNQ_)4-9j@LG|Z4ZXB1PWq&jywY%CEX znjE{<^iJt#%$d4V-#ceW%$brPY^K^T=so9>8=a@a-r0hrhLm7g=}kHbZZ*mw#6r<` zR=$Rc=~kr)2W3nEHP<#b*_mmP*Nd~caigMLhC26>SxbuSiyb8^^zj>6iwppHdacc~ z?3G5D6LQUk>5{d>LD&?Aq}k7Ae>u~+rO^@8^v%lNluXTh_D)R2;AMCj-#;L-!Qh-W zv$1NLb?OV%ZaX3*y>3ziipUH@3EmoPoWU4sZne|BIVMKZ*J|S(&ZF#Y+iNi+;{=>_ zaJKOlQ6*?tYCJcTkF-gZMz^Pkvh!O7}PDn`jbhs9su^!;L zDUT_2n$Hjq1=%%Ln&_gtw1oGEBM8883=PUI6t6KxoMXa^>pjj>z(0QZRe49ev0!b= zGv&Ryf!ET-W*S4 zwsef1t*w=T4>LD|L8^!@&5`L@^Q(^Mm4@9@p;NBevh1`)^ow4*i48;c!hjeaL5(Cq zdX7e%!U$&Iv;8gZ$1~o?GydfHCrDtN;)rLPEu0l_VK)r$&d=Vl8b>OCDQS(PJq)s( zR;c36T6k-Vr;kwX^@MCI|C@|~XTUgv zNuy?p4$oF3FR2S~Uc|9A;mPcNLmmC56evpB7&Yfu^qsNLh0o`J^SSDGTf>8DxWz~z zA&>Z5XWe|5y7pyLx?-m8W=C1*bJ1R{iN0VQ4`Xp7E;nZ8DpwbTyBEXn<0QTnaai8gg7JVMBJHFWEbTG2E{xulDC0pdNH9^_aoj)eBW)sp5i4y`@Bz2-JNf)7opCLPH^n zsrU!XwPK|KPSvsAX>;zB&)&u(m?o<@4@PY>1S=WZT;m+_TJ}~3W+?~Vs>gyKL}T5q zIcEu*!QuYlSQ_hG;8?0O+&ny}4VMfB5==EWYh2vP)VL>Vayj!oan8WAAijea+Eoa# zYS)@cv7wAOw;Dycha0bwlf>9qY4C4%8yv<7r!inm5kGlu0E+O=Au6&xQiOp~_KXw* z8)ut$UDtz7T|?yJ;_Z#Rq`6Qq95Ka+`_qIsx7XMY4oL?zN7b{NnN~A$&BY~+9V4Zz zi;a-f&9L>6d^4_5u;|$)(xvH-6r!bbd8lE>Jye5+U5qe-+ zB_J>9!fW}(^c1T5Lwn9y6Eg4nX*t|l_9C=|Zhr9fv>D>LIA600`hl8jdhE~XiEre` zsnrc|zUC3cmmW^*SVSMWSUN0r=?L5barCikV|HJ;A2xqf{_7(@pO-yg)mSze&_9$V z@Fe_Cy=0*JQ!Mkc`y6d5>yDLe1)AMy`mrMX`8;DqSNlV0{cV)iVvVeP5B13WlRtW< zneQc*ECj4&pIDak`5Dp5H}3u)26G9HeT=K0nI5EZ%O#CZ5H;dltr zgeE~Pq8d$kFU4Xz`I|31=Y%@lQaXzqEos4P)uc%yi*lnrj#8-H-%r|XR?24HBZt9u z4RDTf$+|$HD^^SX{QPosQA(y}&Y4;u=caZVBa%k;IKQbh;FuO1AkiyQ3BNX4JPF!u z0mPzRvrZbMag6f$8^erZfYCS>x5YqWF)#{&B_r2a&7|c*k2Jbzuue;vM0ZN9({U5F zUE*IFGY8W9lZzK=Fe~+!&^%XNKPsH3NTuOu ze@}jqlE!4^IE|clYfNjXrs%Li(Tm2UFJj3iq{VEzX14KcBknhA+YE!U3E7~gV)<5D zMQo{YQzm2N&$LM=mZroj-B?n1axTYCCB* zSufRTT9cx|E#)Yq_|(uup)oR&n#<=OwlKa$wo#7E5*Ml z2+AQ?H^jE7%TBVHqMzxuWgoDFs1%)z71Ok&gN5uSONund*sMb$-Fsx+#}pd_5GoT* z+c=>nH82as2+e@V32IKYna)||i2*;$dG&^1WO5?+jz#~%oIqBzyBX;a`jDc(igE_s zyNou^!Z6W&KdQ!F>FbFZLH7+b_e}K7vgd5Q!x*DTF{~*W>p31{H@IehNqW8{ICYFy zk7(KJbfA!P-vg&Yt>S`y>;4OQj$0?vl)(-#B*TP>BL-tJg{0447!^C`;ddLHrijxx zl>tJ&-YHfWeJa$g1Va|bpr(4pAZocVrG$smxfz7XH|2ta8<(6Ga3ZdSXNrvTIAQA@ z2Ip}&oK~8?S=GfAA^1VARC7NTx>Vag(!uZw`b<8&D_~n8}uJd z({8*Tc-f%0ir`1@JAYii*K^VN5gzyD#+sk#v3dS1iB=%T3l~oFLB-=Ak}zw6pIn-& z_T;@Ddwzc&MPZ+6-O-;H{{|-is${zzYP5OU3zUCd|2>!Ka_>cl|KD`@Elj_f`~4A2 zl6J(hqAyO|_H>Q;)>FUZ67hf@`FjF9+p2|bkn3WumUYEE%b@OtLG6Cs*uZy!_g<^r z>Xlo4c#8*1vu9?vO?w!we(lWthzTmsN!pmE^0VZ|CWtv@fEdgIlEM3KJm3%}+`0i{ z2nZ>vFNVS3eL-yl2N=n~DfK0>X&BjDKVxCj)mYIf223fG8aJcK=NP0(Z~Kd8-Erfj zjx60MV5I=4TeCrAVG|DIK`_|Z0awEo&N^7KdfwI(#C`htmFOlz#E1J4hx-xFUhd$% z!4w$Vtu5Nj?bhSny8}1@S2r6(rA22NqZ_oTilUdzZpF?SeE2Y8JV(5EwNpg4qCH8A z-h5O@107)j9ho@UmmQ!$#T2tVnxHV@9S?zvF!iHkcX zr<_-(d9jTyJ`X?gn#==@oVT6dm&B=Yp0Bb7(`eFTV34OW1W8=8%Z)lG}shWLRr7!2kIw!N`WOH!rl>V(*e|VnIkz8tg+&#nKU=<*kQYMVN?aRZSH?}L3^#-l zrW&p&PTmsZU=(pj=Nj7=XtPMBiw)G%{RVv$X?CS}Z?ivtEb_aiMV)7Vu0<6$To+!W z_g?rJKkE8sjdyE;_2eC2akgEBXdCy}HM;Rr*Vt-r=}G*UI-{@Ux|yvxbY09xG;%-G zB>WO|OPA@z{9~C1y}rH8+GYG3*D+u{Am9cX&&7BMsVtHd6UH%Nni674*zR3ftl4-g zHRG#H!3`lb^iFY+-fRrszdtMGyBZCoOtCen8Jf*R#ppIr;y)Y}I$_Z~XmjknDX4MI zw1XDh(-iTCfBgXKs%}#Bq^1iNPhc%Zr6CR>6+O5($M*ElHcNH8wL*_qN-Su_Gvc><8wf9<GnJt8j2+8n8=>dd@BoS56u!=&f;c#fDRkHLp3V+X0Up#lz> zZIOmM72;LRmLsPDSzRBfKJmRH#H8Uw&LITzl1doR~1+0M{Uf#?ljS z_Mo(xVpd6q08)pJIWW1gN{d+!nlN-+r60BLv7oX~zN;!@O-m;*5*l+&Du=Com7vaF z&Pd#*h_3S{)?U>LCaB)4@zqJLR$(AiFtQ#I% z{fq(dEGV>oR=M^|?G5yu?0YTGv2#%Bkfryzm;$0`l@LKavapnGuAR*&kUs2QU(Ir@ z`#?1{zV|{&I9^jQh<%~eMA*U}qq3rqWaCm~-Ckls$->4O8w_JXshtZM4i&A$lHrQ; zcCJNCY@O=PHEQ2Nteg%j7G;YiIEyfafWiyNI~jD@wP0a)kaKf5Y4@}$1q{5_`%~HH z!i>QY?#F=RvPg9pfYIz5o-rp?mvndJ7(!j!k^)(~mmL9;(Sl9Pr8 z0{4RloRbuK@30FJ_H{TfGQIY4EWHkwz(6b-W~i1D>ZR=@9q|&9Xetm@Y_L>Y6fW3S z(b%rZzGLtXZU`8>$9bOd<#56{_)9e?U5Nq7~*VA!`@f z)vIX!A{^{p*92|R60XfQRtDL32c1#(F72Xc;`dj3y|sdyZ)vx0|6n@>e4&H@UGZ3d z3ysZmiBi91gh6=QTGPci;ri9`Z37OXtLgH6GXrt&yX~x@9c$fXXszVsE+>39 z?O>TCA0O{A=e3l>y#N3p07*naRD>}M5{yhOLD~Rp3mxRuyu|`>?Riy{gc+PyAlgE@ zvR+z|HT>8jVu`w}piPIE?fKbKxTWzT_<*vU^)z^Cdtt)hN3j#aNI6$8=a7}LC>ktf zDX#_~#!;$f>}1C|!3WYVRSonRocdJ(P6g*&YM78ogq?noth9%jvn2$JYF&o0X*mf+ zPgjKggL*zAvoO92-f7OYBNil{I3ILlTw2d&?d^2yr;UAHa;}1AlY`FZgwsj7P5=Do z4+@S=sw;ItCi68JG6)z30qQvy4aSO?rlIzoipNuuLvxnlfz_*6#gh3BmnCDK6Fz^L zF$}I_NSkljn9Jfc$cJ3}Y$T@tB^FR*^}e+uTS~h@>=rkVrEMb3U%m23bUI)2R;-w7 zM;*{f`J8or}pI(38FB{iBf zy#*>!?Y4E!X%a23CdYQIl+sv42^KS^w*khSiw+bB>@6X>ZOWWy$qg zb%WJIgqs8l>jZoOggVJ|l1VVfTmdDIqc7a#Op=^qSj!1BPeD;2ri}YX80* zPMC&(K?AK^r}W9p;G_cxC*Rk4n2VRrVL}vNR)NUJLA6(20JC}I*1N3(3pSRT3ko&WrJehv{cNLcrjH22&E84x*(2Bmj?e;y zAUr=W`1m;C@o{X-xlTca#5~VkL*3Bw+ULNa&wdB6wTdd#n zVTSh}hvR~;hlmdk140m>YBee;3CN9_VjK$N%hy?bt8%WEDD3&uA>x3D^P{KmL5c=cl=O#Fa&N-5R$WLuc=3oxt|I z@!o@2x^%M+6+&ngT{#s@lShgP$J2rj_XEJCGPTZ$n!-E|HiU7`BY;=HYrhNl`ZQx2 zMQKr(5u?;uIR}w_DEKd>U{Me;c!&K?JA_qs>%CK^zw`1!lG9N=^Q;NoNu|Ioyy`s1 zT5%8+q?A(dcsJraN9l(p1B=`ZZ>ZJ;r2?dwB$$pFWAK>9fLZ;Q!ON{BovE@g4=Sxq zdU1Fnq+C>1>lB3ePGXH30Ac>+MLS`4ll08q?FT%c7I;ru&m&+)SXPm+hTy<@PmkVf zz!(s`^moTBavKS=k~*JxN!U$6hdmi{Ot{~T7=p)ni6E=~%2NAdY`a+zEGW-8kK*Kk zGz}hybHpD$Ld)GrL6$yTMoZZ@W?2Rxfe2ypI-sQ5;Vg`L!G4e$xuc%fA;>X2%~^id zk=R_k1X+09_#e(vTE81)tvxSMu48K6-&h z7EXwI+$B{K8loY`x~geRW#OD0`&h8k0L5MKn&_V}1dredv!>_gB7T7V5OCD%)&l0F zcUd+V)?rEHka7m=;AwDjE&lF)j{@N9@hn5s80+vic#pf?4ogfpoX+_E!y`UF9q_Q< zBgRF}y>nP%#Nl+p?>{_Zju9!#ef9b2h#!CXjQ{rcpRpUp3R+0>2`>2(y5c*0rzCot zB22ebyVuN)e4E*PcQ5fZpg_1nf?WV~evOjjcL32Fz?7~6l$Oozy#n_FQvdgbUm-cXa0YZMO9bt9dyb=80vI^WJbZNuD^NqX#e!r3eFJ6wV z+Hs}YYf7zFkJ$yAGJ(4&C{_@fl>u?lw>ltevk0}C~8AUI^ zbw(O@a%sgaVbC7vQbor(7Yu#?7hpdEjw1dPzK17={j`H4;J7UK{^1cZC7hN;hu~h- zOdD3Z8weOl7E{8)LO4j+Rndluf{)W3ep(Lo8RFUDe%K-9f|!%$tCTCY2CBWxqJ#=q zO4dD|!znJZMYjdiMSc8>HOL8`Cx!n#WH(9IIP}I7lQfdfP$?I-c z^3eQk8Y~%olfFk5X#wyh=i_x=~k>T`#`k z5FD21akpoD_i=}53N`4)q~zp)X$(lIXmYk3i(!xr@Zq@N$G;!&pZ<7X2MfZM%lCLb zL@+bKrkHOyU)bz5Z3+lo#uFCDwW*bjrlyc7(fRbs_l z)N9(htcRg7lXEO`47%avprL3IFr~h1OJF7fd>ed-Is!m2|EdOw?MC(HryAxnD5#dt z;I+<37xKa#Gunhs``svOPs**rNo^XdEr~M0g$tILkctdETsNJjE=+CrYC|odvfRu}R;!6z+bVL|~#~7rpEjWjm#cMprjQuo- zS4*dMQK!?a;bp!K=QIdJYr<+zd(5>@QS7^1L-C-h5(BpkU`aJf1l|$mIU^I`s5JYJ zZGnpX{8BPb3jD|5v2zZwFwSwt;2qBM3@&<&sH}eo9&=m-l(WOr^AQZjKYafkhT!l| zfBkpq=w`=0J%#0{1%XT z698`eXf?#rR4v;ElB-DbH~!il*DJ5fSW7(lHIVVD+;bD_d{HcncZ2*Fk3^pYOm6{u ze6>D^ZUkxZJF1&spb&4+Elpn6ZaBb*3Du8N$6ol z?TH5)Nyv&<+Bc)lyVANMbRXHJJliM9=6~zu(^sf{V`GJVwmb~kI}icv0V)pO1r2U& z`6&R9l04%*Ie7A_;>uckOHH=jYew|{i8t4BI7(FjI&!!lN3CV_^%9?R)X(7L+5Vap z<<<4Eedl)7Y&zhG5l{qB<}CildJ3pXOO0~&-eKCe1wi^tJ2`RZ33fHbSG~pA-X}K@ zRl6#wrE}F_9IQIUT%T-?R)bTog{r$3pl+SA)kD~U-<-K_$ZRufn?OlDEgIa@0!&J4 zBB{rxYU+EBrT6OnZPggudxE-N*?L2O5C#V%3HSko!LylOwlr;*r32PA5L!AAQGFlW zbTC4q^p;}uCfo5QL)W@R;l5r;8J>qW#e4?D~os)YakL4&S&x2 zF|XAlX4sFpU>F=8?x!k)EeamyMOd`&KJ4nlGV4bD>F|UlX8hwne872*BCGZe-+i1~ z-zI?XKkVAG#VVF8Y1nE)0$rE-7BNMKOr(~gl%{VbZDGq9wD<5@uFej4h4$YiA^ z)wLwp%97`T7~AH;7^%_?s*dM`;2mNv@KIj(xB%yvYEY4#CzG{TS+Y(yV>;v0^SP!j zbLT~GV4Yj9v1Qbf1Ubc2q+7a9ovDH|bm0}}Sn$`Mo+{Iqi!kG)GEu9R^1(}Bn_dgn zEpFFM(7-kW6vFO%JnRR=lLuPK>qJ(V z$E@dBs+e@pMGnrlIOmZ$BNbKD6q7LfDw`hEugoRu05Rgj{T-f9NBr*NBT7m5k58ZQ;eL;K zS@6%FzTn?}{uN8gxF1K%x!~z|7Pj6Gb=_FYNw3RK)Vi++Ej+SDGgu$a&J7pyb@(nt17Aokl%kD(+dp$1rUB4u&j+H z9SFt-PHutft5y1sQfNbnU((K>|(t06+)6F+2s3r21U1Z-?j1;bH z!l)isV?=sCoO2z6S`E|6ka?<`5dnEEa?Sp3&R7K+8Ov6>9z^;6)PiyB`&On|fYC)# zZ}l^Zgcuztm$d2vF1~evR#_-@a@w+*mhx(PxQ<1)2cmPpemBSlHV`;dO}wt%a~e?9 zntj#}6}C_!-Eh`ecWC8 z8u)5eVP2*C7JSu*K}qe`Za3n1n(J{}mW==Y^E1Z5;ddYRx_);`1t!FlF((;71qA^y zajoMq22KLIOWov@uEr{Ok%~T@BF4d2dAWgS?+G!M+C4pnHdwHS!*Nb^G0UCT)I7?` z9v*B=q%69nS0Gq%deTMumxD|zoeo^lCbQI45_qDukKMHda|83c!6CCu&WFL*Yt-!C zUe_K=ZJ(Dc?B0^<`K4~K#VWXPL=CXwDzgtNoplbF#-RogiqExdR0Q%qMSovp z*BO9E%4sF}6c2lo8_T)zqHrMs74f-xFO9(GnACRAhH)l;^3Ed{ssBku{LEn-a5oK_ z@+hxm2O#OdV^PrEJhN@6#t?hRZVa^&X&eM3&N1OMC!CY`i;czSj@>L(vc`2#P)flm zN|nPHd_7%-y5XpDe3O^RF_2{QMa6z|5R$`1uB->%o3<8qG ze(x|a0~nO`7Ar_mu%5CE6vn|L6*>N=`3wT%58r)2$q7GyeMTt-=NK^tk<~5pj5K&W z>~=V4>q6Z;X?+qCUxF9H6*Jp=SHc@r$X#?fH8GY@&c_VMX3SnOX)>{khRSuPJu&cDD(^_$S(?i~R)78eEFY#_)=kq28 z_*!TeZzw;y+N=B4^Sfduc@xw0tzEk}t9$5*+=AaQp4en3(=}znHx75UI^m1A|AuaH z#`Sur?z&z{(BH!_zfg8@hFQ&sqhFi`@W$fUJ8aIvVU%m8Hx zup(lWIreeB&pW{m2e$CUM z)+MaJLdjSK??5-}V}VCau${C$X+Pt$wJ~^jDk2*d|kgTax~7IkCcqSjT9+FkJK20S?|b&?Aylz_ZuD zC3H1BoeWq5SE90xtaUED>H%!*tX(8>OvL+aub)+CQL&X}ixII z$XTN958q9gMvsz8t7%boFL+^m~KTUA*&hx2Jc z%mRo#>G7_;w-O_tbHVwXzz|P&E{u6e@J?i<$ntt72~MTpbdGpFE%?*#?yBEp3;~0ToxN_C{77nhFbR0=URE~N6Za4cW4X(4rpPSSgN!+=y;Qg>aK(Nh0yQe16!x{jZ7#wlj(cN2mSh&f}434ZVxgLt`v z4h-!6kc|3SkjF1k}-|}Da(4Wn+D9wg5ZJsy9t3D ze*XLkfBEqz{CYg$_xF1Q=WvV>3q+N0Hx4LRCuPCd0SC)jiq~sAGo*QUh$LO z1Z8vu-v(@G)fvjLF6TG0?QfOoUOiR(n#uCTMCfaB z?bW~ge%T(R(F+oFzWzop<#W@!h9Wn-%B!F%{svj{wRf^K=x-d};0+RQ+Dw^NgO1zR zP!pF^o&EIdByX?rZ}|?qVZE7dgr0qan&Z~-W&^`-tQ6x{ZiQYf!0X%A1_z}|lBwE{ z>=Tvh=;zwDPzR3v`LZENC#AH|N3ogtZbm0ooZ{9YV9bpH?}2Ail3ZhhyZE+EE*z29 z=YpuV$O3{&0aI3ME9n5hDSpZdn0;4S+s_&`d=R|uIcBb~y%cGG@Z=E4BT+$sm#{2i z9OI1d=tHY{x;nt=QxqI-hTd(`_vuRPUXd0U+4Qlpe5{*9>s@tDa?Ah$!N!_6*E%6{ zEM6+e32p1-*8aY?njzbiSGl!HsOg%Nr|V_Sg)w@*7_g%zbjshi*JoxVt@j{uHMLaw zDjS1ml}4-3Me{jz;GysSsy^#(HrOmVP0C@NtG(jJ61gRv*aiko*1UayI`uMIjZ=&w z=`$H(u|#0MmJzwijwu5h2>1IzH`5v8;5F^Ky^eFjW^$cM6_nz!-woJJ%2cT&vPICB z+J9@Qt5PeKRbJi67ps(27oQEo)8F)MBG${irMgIx&|Q_y+TdpzLrqj>h7JIlpTv0w zD!KKob^q9Ln^>UtAnpeX0ouMGgl^MWtW3yyQfpMGcoP(-dJr>`V7&?H)_UV5n~ z3JSI6!(9!7I%^=7 zStU&IP-;rHr~=H#OFbi|$>v`U@>=)%QT`qa^A0e6|J@ya{(QiG8t`~GwGMFYn4XtZ zQxWTW*sTo)kWIR8zHO?C3JKeqHApPyf;nZRRPf)w9B@Al_`}DY1eQ66!Filxl=Nqq zU8U6ft`4>!^6t};>*ryX33LO&jMI{(J5TRI3y|Z&m~+7|hXtcbu*F+z)eEKGPes?W zud{fsT~mx{5Ssk=QW$rGfZcYDN+8x!{lBb<@UE`qgKs{lm}QOp*PlLjiSMqa%iHkE z)n|nae2#O(;KjRiSR(dAsMkyPh^@~jlM8>`jdGnH&$9kFsco2J#?xuW5X1vj*Aq>E z7Xa)q4o(NI;-w0~$uT0}VVr>4Jud6xFnEwFdTq_vP2vxy!uZ=SKjZ)SZ+}578TVsA zDj883{`)Z?7P)4RG50Yzo&Ws`5eK^Tscw5_-?)LLYh){3%W>t`H>lslw9*ZhZ2Nck zdSlXWDkpv;DC3rQLMw*uH-dv(G0tzTNxGIFb)y@2_1E~d<8@1N{c@t$mZ;=!RR?Ti zez%)rdI3AX^3_%jOTHz0p!XrXThkeOZP0P~n{)-JZW;cY=jWS3v2F?H-#FxX?HqTh z^=t6^4wb_TiO@_}4_dP-*q)rY?yGy{yd^`Ijs>l?d=^G%3c9Wa5{^Oafev4RsGCic zmGru?z}F8V^Yt?2UPj$pb}RF3gqr4_)b*mVsd$D%h2vXSURk-ioY&A%Xv}Bar{JRn|h3W^b zk=K?AY)qpb0F&}L^#f;R0Sv%-QV(u$>V4B>V5@teHn9dXxH5_T$)c6i*x1Lqra4*# zl2nk(%1-L0r2>Oofci7gUfu?RP1;-!W(}I@E@t(0Rtc?ZjA4^ND~JMWppqx+uoCGx zmjW(i8CUXJp5XN7ib@#0$EtMOeAsnKaC9LAAZ)xaUUoyo zK&h-ZSXBb00PO3H()+5Lw^+NG%|EOxl7VklS+!|DTCGnTF- z=Q&|FQSDU@gF~KWIFWO%wOa&X;Ut%UTJ?zQ26kvcD(2xGf~y`IV^~N(bFm&`Cm_-5 zcg&fUkrGzTtCYF+?&`pZNC2Z#CesE{s9qIepe69gUoMK=!hU>L`$aT?$YDPv1#Tko z384W)>&~8+)HO3)H!GDQQXadPvJS&aZYT(;~@_xnS^K!k=_V^67ZSpTB!RRB3lijHHtLU!KqS z;eM(Uak;KV8ntBc5qt08eZf5qm~%nSS(OmEO6XZT1Z^W?Uj0*){q^7R;ckx)_dEQb|MVC9@^r+*6!6D~JDipT<}5iGqF=`x6Yj=!FyXbF;maHJ zng9SG07*naREuO?-Y|Oa_3&->VAMZ5R{PF3Y>*kh4TsWpx5PIYNxE67!!K;``P==< zE0)QWN8jjQywwd{4{zHb?KC3~UzA^o|SpwY1_lBS(JuSZoCe zM^IZLwz=e`20R&aj>__8uqHvT&wu8vB7!n_S_9g) z@9#l8a~Ckjn{YWJYr5(sW*{T40$S~`8n!WuX(Rr(USjIzVwx`muB4S~DWOOuV+tew zuz$oUEwF6tfgBET#*!jH0U%fHvBX_S@k+63Yl&MAyk5H;I}$7W@*hX!R1gM-HALF+YCx2&hDJL@oo~bDFnF9#Nf}p>6WHclthlAhsV%Y74tmm_ zU#n%R;Z;SdLQ)n0Oy9%TYgqokY0TQnLtp=Ly0w)MSm9ta8 z*?!N#Ye#35D=>8h2rWnm>bsVzAgZ9cc!>ocg0K!b7sOnUty>sf_>u)+Dbq_@+tb)< z2O^O}7Y(B`$+B(Wjjf@YHrs9hW0}b5Jf)z5W&>6=#n+@3Iv9~o;&we&sk#S(CvcIf zAM<@W5Kvm**r3cy>yEA}4FNN3-om0u+T!(-x*AspIf+;YH?4auCkDdWQ{ucv+_Mfx zI%%^V_mVTRhIG;PUa|*!>6Hrtgoi=otRkCjp;)Z`h!!Fx=bJSdx_0@ZrFnY=#wm$+ z&}-V9-L7ocD~pw(unbZF#%}Z&2f{RXEdW z^W|Y(FpVQVJsG@8@gq^<^aOs9I?M2G(W)WJuhkY zxyS%3cli%C?63jZoTP3n=8P$L+>Ha~oDehPejM<_!#z?;ART6$=7=!_6rb_0KmCe7 zJlx@5evJ6_`H1iDCX~YXdYUl>kHQYa&;~rLn~GcN;wJTN!zbN116|0(99Ii+2SP-DIt7bG%}HuI%oq4|&8J*w}CPjJCIK6*_oP27TlB;EnHl z0p8x=3BCdf-kxo|0btjLC|$zsN`X*;Fuw|^_$`?0)^ViH)LU9kpZT)xKj6B z+}REQ#y5pQy=91S!S26}PVNIcum0G(!EdK~?*Tv4%>l=&Qspb3qc9`01Vu76Wu?Gi2mhY(onN2SyeP?Kt4$cn6P9mDmYHIK>%DTo6WAaaI#;+{%&Qi{TF6ddARY zg+ci9|NK!NqER+jK6s=h>w#+%fIiJQ#jdW%LXvEg4z8fp8D*{!G0HX zgC_$K>(-1Z*UwLS9=HH8ioY&J=`KB-5=s{K!;#0bB&{eBb$|+7>y77}lWtAzkoMz% zoO1Pe4#6X)jFhsncoG&P{yAa8K!hcUyxuz?Md?^}`ab(ftCxxf8^OSlQ@?d%wMqS) zb(3lSc9rTT^=a;Ql8zhI(=5Jl9l`{ssl!f&HL9-gfhxNz*SrjqEWpWok5V#*!Qp&P z)fWyhc0<6tNC4LuJe-$v-01zL%0I;R40JvcaGESd3eKVVskO#GZj zP8tfPjQ`yfaMBd*DR}MOmLUhBuR;UHU_lPz<#4cS8wQsQaw2zzUc+CXj`-c^h;tkF`EA?r#imOL-l zvnP?}rz|pF@13R>X8?px&u0Yh@SzM<1@Le;A*O`PMFY8jC1)I$1=HYhj2UC_SaPa; zy7LloH;tI1Fv=!bPB|f}XV??)e3WkP61KHQ58TUABpln{ak6Y%+X#z2HWeg7!mF=fm#N?lXT zxSK}&%TJ%+9q_;X;~#L25vL_eH*`w)*Z=qlA0KwIVa^4f8Q(qJbZJF|0AieYPUa2&GmuGS% zJ;&-CUGF-5fysK2ioXVw_+5jI*DG{4C#W}nGv8(zd&-P%XHH)Y1%4?t`9=BpM*4rN zmg|jPSh@m+4OH?9q`t!wORxOz-s?`=gzH=HL6_GV_m%;ERSy69kl~t*;YtGcJ9&K8 z`Z6Y|-FZ46@a25eASJ3fERWM3_v2pFbZ{^CW?x)4Eh$^pNmth+Q>Z$<@KVY8ESKXa zsmUu!RBT`@l|xm{sPh0HrhQmPE}L%Xi4!d8K;Ss+(Jf=45Z1EsE=4_d%D!;_IB9T|90VVvD0o$$LtWmT zhVop!Z}hJWqpNJi)ERvq;ob+QB*>u{8T26WO?YQ?+F>pFP zH)%3@Np|n4T|bceXL6{USgk(->ZXFUItl8HbzW|ElfoK1eqIvXlWrbL^C%Zc08+7Y zUI6ced2~9>BB9`grII}smMDQs`~9FGkkzZL%$+jU48}4Su$K+G(|?l(;*!CUtM#8q zK$mxtXln_~j#~W_^k#J`arZ<(033U*c`54g6$ae-UaH>M;T0RrU1%7Dk+X!p5G;_$ zt*W*>+R!7W+<7J|v8x@bl470IvspW$JNChQ*Pbt0L*(tEPzJew_}WdH3qYK!6qj7{ zuja%^u2er+(J`A-mee^{L&T=RYaJ2b^|%h9Q$Z2<~@;lmzVU#{n_P(GBE~ScY4ye)&Ax62Td|gyBC^$?`*g*5* z5`wbGa$UAzNzrf%sw`brzlNwlJAEE-n*e8J!|j~7>OGZIMY(>PRE^+NsX{fW-=tC&2$ymPe5ehj za?W@dWI!=55k7cKgATf~_?;hjBcA33|MBHX2BW4F&~^Ch>5M;qxW{2x@blMaJkN`& zH)PARq=@|#uxO|8@Nkd6|NII6&tLu>F(-VOM$ONl)+}JPbVN1PvhBZIx24-|e7UoR zZkRD#0mHnOwX*uQQqEXXl4GE2#heqC6cMx41PY68m3o-bg&)cqHFycbldfg2|7ge` zx^{gDbIz*mx)h;W%}+{0eFq@}M(pnqe1Q}t%kcF+EfWZRd>*sU!2HMaQ;A!OGj z(AWBfSIJB3hN2bCe670aQWDK?l?%Ui;Cs-=ugE0d`1;gs`kUuwJCJ!N3w#a4(MGnu zVzS=$9@I6Gv^Bc#GOic|D?+)jxD>1ttVtPVFkPr^xZCU%efM@*aaCb534qP$0<5$_!E!)|`V{@{c|+Ua zDPfMCWk9JcI+a{)potj-52Au4CoFlvqq~sLEZ3!l;B0&52*B~UTn-)Uf+qmhc~S;XPH&6_F0;x9+cTK7_lQU& z#6*Nq6Gex?#D><22M{+^i2@3t`DI@><_9&_I%_Jl z)9{=96y(n{n9AxV5y>j*z{64VP0J?RN%#)|We~6+QR)Rl zkj>Mg=iEG(a7}`m^_+XZiMl7M8*%fJTB%r-!$Xp{ohL9{D=T+UcAe{S3A!mJ?b@>` zS-Dei5d&~vp95GEE zu4Gk9X!@=BPm^rA$vf0Bmv`dh?_|$jK^;NvsZ34jK-ym&)u(ARd)8|&c(0)lyjp~W zZ9|p94#8{2ScnR%dl+rf* z!PdSYfl(=zT1#N^`6>f4e+)yft@Z%oYC*890#_@T#Ir5uN!Yux3XXExB^;9+vnhl+ zaLcJ^z>|b`P0H@o{^G*mS=Q*?7~mD$Rx1IuN{irJmlI$zu;!bUfn?{A*N{eX@_lMb zCO2^up#=ik(823Dk1-)L!@E**F1W}B!UlV?eir1+cwQoccNo3HK%vUCh#Yof(Cf!z z7zEIfK45=+J|X3d$7#a--GtMU@H{UVyvO5y0&|fxY)4pP!r%v-<^})XU;c)_{rVM$ zWx?b_WjXpVp!$5yrDGkstBbGcWtB+*ubH~5KB+Z}C-;T)9?vo1w9GgyQKj5Pz19-m zmse6WdHuhA`P%v5o7KUS3{8Xc7`^;6gn%IgjNZ$A?!4>;UXO};MNYcWwep%}Y=juy zHt+U5vf$g*E$;yen*?D~e7p<%-bxO>2nV}GiScdl*B`E%Z?pN+o&{mL4yG==-uwdc zx}6UFe!n=aJBhDKb-xW{(^eYYTaABaByHm)QFMsk=rA?Je02W8AjlcPmR=bWf2i*ELu zbG4a90Im*U$$`B5UHfpUWEtc#uP{kKH}x>42Cj`7vIa2O4>1acA%Iu19g~Zyq*qgJ z9XVx0sRH707jQntI;<)EEm(?d%vq|HV$3+7wJ$Rl45LRWz;cer!PNv*BC6@aM1;d( z>A*YIeoohdl-73~>PFC*y;3@Pb{D>75B3lQBv?I(h12ZrhKmP-$vQv+><@>CAAWa_ zKmEgF1=`+wMBRMV$-Q1cObpI)g5~%#N)o`y5{(Q8*>noKV+n2DjmYwSQe=GodcyG# z@%T7N=O!1#rGSXBEHc3g`n_T-Rl=Nd!Ew%A71UZ<$8`rQDZxb<8c$v4>J2LtseIhd zT~M%Rue;kVIGz(8?gmYY6_&X6RI}X3A)r>C#KidWyi~6!1GwLJ5(>1MC_t|Fv3B-y zm%zLpbPl6efG6*>EJ*@;)KFcD1g`~e0cF)#3r9t>5Zr13F~%pASbZ9H|9I8B5=m8x zu&%=9+2P+!gLcBY$`k>TOzXWc-XVDLLuv?BVZuF233`eV-Q(>%(b#Al~O&Nr*l$2aH}yX z#e($YINa?=EYZ5>8XKNfG2kuPSEpu-EM0ucC=a7IaRPks@PiBy$PrvIQGHg$f~(gx zIbpHUZThK8M9$ngeQg+I!F5^JfK&>mL3?X+sdBH-eTP*CUzhx93W%6ZnIh?x@-xk& zI%*z5(V>%fVjFPIVYeh<>XgkdNrrk^Q_cr3liA0+sSY>-1q`aN7=y=>a!r&>MMBYn zZ^457>TCVIj$EDewr5JK)@nV~s7aOml`k+PLV2o%+gVqSNFCfVDN;y zAz<)M>WftFo@Kg*!g8M#0>(ksk(7#V)B=9@a1T!o&!-u`9!^N5;C=!gxL`NRbKw*h zJRMJn@dSu3v_%TB&h=5xZpU? zILtE+27F7=uou2i0`rD}i6tZEbtqP|47EzKplRNt_n3UZZWyp1N9?Atvc)%hdR{~% zeGL+=C2BoMiES$_t@~Z;U*_u>UhnNL=1!tcb`CccHMchM^ak*>+FUbUE?a$>?jyd$ zw>KlJxYL*SiURNp0FQ1DE3On&_2lO}4|=C;xZLyMwi^3gSytA4+NACLdSEtQPzhg6 zf_~{D26ajG!_+Fsi%94&i;kYCv1btYQX_OR4h2BHn(0?dC>KbR=Np5 zO^AjU*h>0+orB{dg{Q0gZ5z|p-AOilGtz~d3et6-KbO3kv(zD`*Y%5)GdmhM1{gNXL8%|A`8gs!Kai#{k$j3Bk97%p(6TOs)S6Et#ySydrl#;c@hcNozogFh=@a z#6L;b&`&$imWaFG516I_%aW=bdmMcYKrzo{zn)rRYwuC!t{SMOv7$?zW^R6Aw4ZMv zX?=6srro-3?X!q<*kfLD>-}@6g9E1$iJTepoaOzU18dM#2m#K^29k7u;57M}yz~>fH06;r^OPLt!~GX-B@~K&9nT6KR)2Y;~pSjKF2mKw6G%r(vq+w zVboI2*i8Y`E?_qfU?-i?OG?rmZGKt}l`7h2T)5QLAomp=+=Z;wG+<$D97xzGl}qe* zBZ$uM!QpN{f?0+TDd##2k!q2wK$J9u%GLC{k_&$Qdaix}?g9adh9FsG7TLyUllE%B zb5ZZ}5M(gW%b?+8aPZ`?>V*}yk5=BgerWGBG!6>Ziw3G0Yh?o%li&}*N!qj{oM!P1 z^Rr%`zWD;lH7RURIdskpub%LvQ?-;?!yE|6QW@ogFf-Ec>_mRAQ@v6_jD&@mM;gSH zu_N^0 zMzxyDm|yJ(4m#Yh>$I*9%3$XtY3q_uEg;D`1!q~)mXiQgE{F|Iz)a*=f{5LFN( zY2Z#7ba^;rP3>P&)-GpO@J(2jj8jPnYPX2`Sp-lk3(r}z0$e2pV#>(D11#&+lCns| zw?ZmLO|_SSO9KVQ_NSa{Iwz}VT2$<`dI)nCuQniQ=^hr2*BtM=b z$!B~%%=r9t#9x2;f}frbxEluCPXi8fLKuCm0Yn#6QhjGDUWGRzwDqQQ4Sv_*Dwi8n zx&G!DahPX3olkf^FF5CfScfj%*t<*C<(~cFwXCwNlA*ahhjc)?OeKa zgqY!|)bHh-6Xlo2VLeVe{PFG)U*bu!VsyVSr-CtRN3yk zid~B0JBIALY8?|C9OtSS2&M2!(o)lVFD{}kb{m_48WDW=14(7MlC@KG|6n0LRO0*K!BSqvHAn-f-2nn!X;ZBjDV2Bzt=$UfZb5_cDu=Wbhhjq{qoT z=b0Hx6vnows^!Zew)AecfgNj}57j{kGrVs^MA?gFF zmIU#vIvpN(40?Zx*OfrYwO-o7$eKEQS~3bV?x(S)x|f_0ysK<>T~nwr?Q%{%=9tvi z%bVU{VciB*n3RI*8<#;@d%zDGdR3JldOZf;d8IaNqxgUc;}Eo_N|@^^ch;#eDf{Ql z^WS@js>+*c^@OWK1g^)t=vvu)vds?_ybNnXRr{!-CudEOm*<49-SF~SqgGBiM>sAK zgLlZfz7dJ!KPAS~dBLy8SqC$O(G%t+BbAKv@<~#)eZU{SyT>?qoaP0EOAVJxrQqLw z{uPHMV?PA!29fvsq!nt^cUNz^->KVpT!k>#!3}RIG{(xOnR3Q)KI8M@h_9zJjwyuKrsi=gS?a1zDdFHFp63}K_d7i9@30>S3_g7OhMX>C zw!~{yX&*|(H+8Z2?bR{1A?~!1~$=J4tT z!sXY9(D&%ZzXg=O<$7aYMXGK8?9Bt5mjbzH+doY2^V)Ljpa}6 zt)Al#F#oFUmMvK3(qQE3CMBm*&rBaA zX$!4v_;S6ma#2|=i&Ru3gWdE~CmT-h@7ZL}g%KCo%ng%lWT)ML<8i4H%a+{RPHMKa z;_c*1K}^;_nHY2Dud?623@2J23#t;#`pkE(SB>jL#MM=LTVm~l7*uUD8-lOiG$My3 z6_w>WOD?Zn$xw+l3Q!zCUMoXFlUj2wbrQnV$s+0|R7NJ+e1jN8;wQCI>f;On;4HFF zFCOmmoNzx(xVxKL2d4$?cnQ05UcgZGX#o^&!h5W_jnl zsHuleiecvV8W?1JICTxmjQz1Fk}TT-YLK|`?ys+Stqe7R7US{jw03(AHJ*)OAnQ!WPTYaTTMhq$rX9N#Fd(xz6u(N@1a1Bv15PK>1PD{F z9->U0$knFv6+G)8v&icm&I|6Q5#BqTW5V&6aE>zc`Td7`e9=E&wDURT0#6Q6S@Y8p z@#SF*t|E{U`%C@9}(&*bM>uDd6jA20+*aU+n^_GOJzky_}9#AwnPrxRwad1^w=%e^aidjqPBDzA;2%y==FW~I`l+CQJcI48zZ z)PtN7zPr1_l zNBK4rdP^P5R}TWO`vJH9Te>zxxSdM1#nOC5?fyE`N4QLeenUmiJJ|jUFL_U$e`8J0 z_KpN-FqRzg^>n~fI>WH21VT_t+zo?jFM;k7lkE?kZW?;~js4z*q<&L-)dH+|D{#F&_qjbE z>(*XcU0MQWgf-EBMg^8U}~`-GtL|xnkyM z&D56oI}8$r;T2FEo+ILt;RoM=&(=j;iflfH!PjW+ z#H^CeHgWOFq)KHFuOyocWy7>CgFEWcd+{qcRSbCbLdGoLGZ)sn8??^cq9HzT%3w?C zviY{XCQG-nZk9?FJ>Mk*>&|7j6hA*A>MQ-(=LOHxFq~v z|Mf@wPk;J=KmX}FJU^eY+m9e-9G3|12r(t>RElbVFUDL43xzEps?}Gq%9b+>Y>I;5 z`7q-Y6ZYeP505*fZt&GVEFg0w#F+5$agXy7TXMN~nCFOKm0YR<@v5X~GFNwza=)Y^ zYKbiBji>X1DafG9828dW2$&f^emUYC^BPDKuw>HoPusm!gAM_;-zd|r8=Bx;=k@J^zle~FbTXTV{gkjYf}G=mPtON@I-YP!X$AZ);a~$c zef0)kz_(r|{Q{7nHC}2$U>UL;N5aX(FI`(J0Ea8x%5PauT(P5IpTR)kd1mH zuEX${Gd}SVPw@;-vhNw4$M?HCjA5+xI)z$3^xFmwRQKp(rMMOrU@ZSOaKk z6-BsWudsd8OM-;nyK7-v+`W-K_e!TqUCkO->L9+*P{`i9G>Nu;1`8G~rBp>2)b?gI zMwV|=8wo&bm5;_TiGnQX^FCMEHmO1jT(BR4$A7$zKvl7OVO5;l8YhuCdsuQvO1t^3yeJyMFuHhKEo6cqx7`i-HkXr z&$xe>q`o6&3}Zk}1&<#m#F$pyg)KCo1GLc4tHIYrD#na)^4jgW5hy9kQhRs1&3;WY z?tnqg%519lPUM`{-|CysuJcQ@P$FJUwrs#zx<_VZyA(|Hay9RyztKo{VbpVFfDZ>@ z6)mjG{Eh~CMLydW(p<#z>pW_bwOyPw*i5fX-`@loczBouOyu_crg22%h-J=Ng9PNn zxVxJWm!i@^cQM+Mp&aBFK=@LqzuAe(7P*A@A54HIP+X0b2^l@UAgHKx7F z9fzQD2`Izr8dwajUc9+*1yX+SO={u@%MwKzKLn(5u9ZwSv9iJ&{#g2MX#{s7~ z0z?>wp-ec7k)2X!`}jHO^d4tP4w`1{u*{`6sw z5WM)eb!{n1xwZ!%*P)OdQv<5@K3V@^0_K!4n}-v|V#~ewl{10^ra@AZ=Ooz!xfDtH z&jq7*_0sv4#QyH^7fv+g{3IiPPs^6T@eX<^~iULwBIS~8UkTAN@wwTiI_%@ zPg;i;Gh;fkfSj}_c-e=a%7Ud3?tH-CmlOW$&ws=J{=fbgJe?zAPW73Tid65Ft~{z- zD3jKEp=(19XG2zocz{jf=SbwdXWi60um8A~@G6x?x$3X8Rsz}BHpYx;C%vyxlUtp# zNhw;Dk3G@F*ZC0M(fk`Yb-5s@Y*@OE9k`@g`NZ73;1&%~>#K_P zBs0}%7i(0-(|j zuAyKxozFV0)j!JAAAV|sZ9=V?S}oMwdzkcI{Fp5XxP7*^Y1v*oT(){Nz8L2B3xB`g zXn(N*KJ&WN#jE;F)wfHi3!WUu(oH+5%6M$)tx6B-E=_y*Sjx7BP4!h{wlSmCIFoTv z-nad(Xq9m-fI(%vR(MaX+t*YBpw_2n{d<=E?o3*$z}PWYk1>HXq_D|TX_L?3oXCLo z;|L@bL!4Zygl+<}5 zeAxg0ti5S(BuSDs_Lv_Lkx83;jKAHR9yJe4a;{}=!vd#guS9?BvEM!RO*htKANG>}B3pEyj9<&1(c%8%;|A^M*!|#r9f<_}P@R8|5!UVl@yAX0QMXLLr~l)2 zcGE`}_s5|1hgA0)n*Yc5m$0YG?nYXVP~_jew(ro+>Bm!=5_|OX=?P~))+b<&E50sQ zTzJFT4fr&S@J4ihWW*Hf>?bjY)yDN>#clWgcAE~}zk1Z8#C$WJsea9enu!i32nC$$ z>!RHp13=8Ni(uvEQBtI6{nv@QdQ&}O;s!DXR8Vr12XI$1;zb|nY{bwvV zC1q3`oL$x^6>+Y4TWRnF17tR1_CCG%BOjC>QI*n8_49VPBAOGs8khMW$UCygD&= zR6nD1yOii~FvJfZnGZg4}n_4n|gk8r>L*IhKJKb|r1ndQAxd#YE=DrNkcX&bxwahUI-$#4DT9@s*bnWx-K*uv@qxT`&*{X}NmjL} z?^Xap=UB2 zLm6xPcI+2vzqyyW7nkcuN@F3Cl~RTarFrfB2+(w!rLf%(c)FvV_YLZfQ(DS<;`@ab z=Yw|cfCcTQX^lEnbu}erk6SsNIaj$xF#;qu!pf*Lq=ruxUT7#icacSfE#r%WxDn&8B_xc4!_zoO25?NU zPBhe8VmOV2dS3#Z(|p0KbS-DG#)R*0Gpr%}`S}Fz#EfkkJTA*7dGtyVcgEsfY0am> zgETkt^LfPIzrSG#L0z-B5So~Vfg2`%EQMcK6?zfUwsoa?^%BeaTk z2(Vm>d0TN=LuXP4^!AZ-pf&SHAnq<+=r`*B?il_Yfw}UzL(D=WPYHvvULGM_xf?t) zGcN0j>$+l15k$b~9A2g~roq?YU)?tdovZ7;rtAY+^6zXE=pl3XING8g)0Xc}CGfys z>S#60kC9Kl?Vd6+5)Rit)CGkbq*c18!9{~;=iKmPgFV{skCm(Y5mJCv4gILLS~ODkGkE&yKM z?FVHXmr~Ror8DsChNv+ocpa#h8ay)#vr)ZbdSu$vMq18kZ@XM$t)VNRV;b|WAkyXc z7Hcd4&I)>6ez!`Fnvz(l>!tz{MtyePwhc=NIt<_{SuD!r+@rgdpR z9ykMIdwN1E0F!FnOeRD^jRDF=8g99gnmeY2rBl~sV_PO@dF?*dDJu$M>Sq-*9xCx3 zV7Y%EOzy0crq?YMG*6#4S%6$Ae9EzFp?D zkvK9jy{YK?ejrDc=5DHho~Wle^t5>H=o|WT7AV_6af&83!Z;w&={6vmRJW_{ytdDn zgT^f7{lKt(o@2l~-34OzhZNmBz(x`Ieg1Hxwig#$&7G+(cSR3Eol76>J>F4rME5zh z)S1MgZoE6AH>Ns(qPz6BE{OeJOf4U&DbyGmiuQvUT!tI9s5Tuq-ca=Fsc5=#y&|e+ zkCJht8l^6Km+!D6j*7Fs{31Hm9(SrvrW@T2Ss6A|?}QTfW?P&Ob%TVIGBUBm?io5D zy^x%|J0!+>t&?Pmm%|Ok`MnO8(oQ$jH@l^|%(_N;dTq&sCXGk4dd&+{r8tr2tot`X zDAGoThRh67*Y-1wSYpN!#VBt}5_=UhBkSSUU)i2zVGO~Og;iaWwFU^l7DZ2cI(c|! zumzbQha`*~HX@NJd!98G-V(gCHNThvoV?ctU50a8Oo+^Q_8$LyeXm*0-dc=4W7{G! z$$fcSR)iQqkoS|7)?M_r#i+58Kssh*-VN4t0H7A6#Q`39U$3{b;baH~S1fMkjmFm6~MYV-)eDAl#4p zvU{n*cew$4hYHVpprUs|M5ep6#r^vI0h8c&(a`T3qx~kt{lghV$I#)!4WNHv&Cm~S z#xc+Lhxf8LK{vSt@Ze|pHzLv>(Hw9T89z41E3{g&wk8Nv?en!Zu`o5Yf!l1m(KG5qSO*X5qw{IecFu zR2tptI;t75WsPhhRZVoUxF>2Z)DorAQD~Xl@tjdj19qG04KwUM9jIEhGl?NrhRO^| zM2cwv?WIvH24K6*bzkiENp#h|rsP;F3j{iqi;-H)PUfA?CG(LKNr$y2ReIU7GdhY) zhoZ|ynKkYv*S{OsE?cxaJJmV9Qf&?#>#|o`;o-eb(%dcdC@w<}09~YMlnU|99tHrSQX$?#H@k>j3RY z(PEb29U1W)DEo5daaSQwGtnw*uMWg?`kGR1dlRcOxfA_*h*_l|B5NoHhsIhsV}-t* z^3Ck19=2IrtSyT^zD4Sh#>k1@c=QhENlZ4@umP(MKr-Vz4S1hdT~-ox!~nc=_;e;L zF=5@r37N=}+qwVEv$l zsM%1J9LJuW!oV+YZ}@b6`T%6vH>G{Xed^>EH#AAQM-9f^;Zg(dZt0cxX|MRuVCU%k z+ea#8=G!7u|Ce&cb=&ak+Z(PSNVc5mmO)9;EtlZY@J}fyIwubZV~oP~b^FrbF|OiNyD>3O$tJ^lss_ z)2ZMf>@9LN-g(8`eGi@gL{fjnZ8@QlNY0#lPm5_CcfH zp{tAsROpYp>SDIElP};06#svf_D>H2v^rb5aD_y*v^XeYiaOyOOE4EIC-^7-zDJa+&U$>19qAeDT{br3e?;3VdebFSxq ziE?ES25XSjIIz6S!bGi1AC|~QOY;3N+*nCuD&v;OR%6S4Xp}oVwa*kbYC3DE7~3}H zSXP(OT+%eG!^?QW*L9Izpc-wpeLiG~e>ED57}5Z~yBj zxsaI=qcj7CL<^Zz6T3Sk6C)%+5sF4Q2Br9Fq|&!}2B+q)?YtF3D*~=>0n2>d@d!9~ z^hyFg|6#(mCA@uK@$J_cVM{yO=`flu16UCZtrANECJzH2%opTgi7{hc#HkvjxvB=> z1dtUS?nfJdbEY$>Qd*yCd2^PW+0rzbqSs7C$1ED!)5*f9X%z$Dq{vSe3Uf-$&?aYD z?5QBhS?G*{DD4emdV{D(4Sdp^=9n^KNSIC@!!T%8rnr2eR2c0*8A!p^RN7BPAdi(=83Tm)CW}Gp5zoU=%}`QGa$d0vAc!P;`h6AZaw()rq#H9Nc0-$ZXAM}J_GL?W zn**2_FXMoh(}b53@W21{&-n6s!Txkx!_ck`n9qWb;yqX`kG&wV_>xy44Zy*@_@#zUK)2T@>_!0FP_(*&E@NM5= zdEQruw{J>WT*p(+@YZ2)PE9rU&2QOfqUz=cMd*(#oZ|@^Q^c3cJFanqBZEIaJ>xWt z0D#Ln<9%C^Ipb*<@jRTWoa|=d-O#r1W4-mg!EnX(xuXLAKEtYh@Y>gQw~Z9<4L3fN zAMgWyd~2ks44U~t15Uqpfp>^7@!h(lt^KYxDS(BcDkE%kVnrLN^C@Dg&D_pW?`Cy! zW86?DU;6(S6LZX(?-JJ z96k*vIP(o9RDnR)QpTn=T2!}dTRx*{@;P*7R=wP_49&RTqd+MfIR*yvXD8<>(O?Di>@T0}HL>OxjLIRR#joAt^I?wS#$2vb) zL`5@58)9RnQB6@ANHOA}8mZJCWS#ODgZH-$tLB@EbXB1Kg635eN1H#PT}!lxS2L}o z+H5k=I3Y!R4%E-SVHgIBWeG@;alLNXH1>$0*V>v|(B#Y*yv4Q#L4MiD^=C$|F+Zp= zLs6Y&t8rP43u=XI$_!&97AGr>7eugRgn}K!q3LInqN!pqNu6<7H^rvLR4IoZm)Ra^v#KU$H3wN)A58nr-Z?9VXW9f4 zk>&`qAVgYF(1D(AIWMh%)IyDBna=@ zyvn3mo7hnumyIEW5CODY@1jvE)=VXO%37-fK~yd093!lk;oue{<~3jmLA)zmi%~Gf z;N>)mvwDe4ET~178H1F8uWwh0^HTqU_hm(h8Grx!ip#PBU`$SsY-d|B zz*?+Ggp<&6^G>hdF-p)pBaSFS?wd@a-5#=QPU$x^W4#eqnI7UG;HM-0dk+J>E#Qu4 zW_@pbXtM7V!uMiZNm&sm=Wy~83Lj#^#^MKID-%i_(nTg zhZOV>$lh^~dd5MB5x>3*I{)c(!k?a=cSdAY$Hy6$b-}m!3IgGe&(ApdQJUaG^FcfP zB;$3R@z42!!CJgb6DEvcX3X1$f4sjTRDZ};q_hK*KC`0v9OE%<`x+OvT z0b|epKeDR20TH2?NcP$f*1DzWo0Y8xfK=jPjj3jqqMepJ-aSH)TWqAF>Q!f#o3KIm=<3QPX=!>V2uHT z^@Y~h)y{U8MX);~ZywVw42t@vuKko-mrxJFl7;CtCT6Fim`-kjfg2@9F#Js+;LMx-umY@M+_$iZw;<T^dEm#_vZH?Hr;_xbFV@8GQoCVEo z^iRgy+uB@O8C9rW7HZK~rS0@Ky{+E&*gDkJ$}Ibj!u9SmSCk zSmKkqBFj|gQ&g8@FE*`$-stB_Nhr`;kOI{YrbeEWYxsWI#3^=*wQS5IKd2OSXZ9*{K$y6QEk+5IbO$z4-iwTq-k z6cxjmeY$9w-V`I5BO=YZk(a})(7mY^XU(R*r`mQhb67=zTA(@Azn23lS7QjSf2M?* zOIzHzX%{t9a%Jfe8T0;B{ouqxFb6Dviu2d)1fKxYIbu_FAMXyDLy=Ql$@}8Cf4!8N zo)?Vj?jBR2MRkQ^y-$4eQTi#XM375UzH)30GZ*iKhHe^-Y!fK}i$T>E;FaF|avr4- zbhaY$)><*ws%tNUinjrEgCirKKgKA?o(@?DFKcSs^V`K@0A@CO@gsjGr1$C!2vADB54D@tPy6E9QXh0AHO2Apu**Njj7W_X%p>?6%U9z zU~A2GRIiGBhhArxl!TQf$DVlCIDeS8`-rPsWE8zq{z1~v7?yj(oJZCM!-5pxU(>I5Bf#+H(} zppzgG-W3gS+uQce$NMqmePrvu(Ij+-0ClVjCOja~-tI$&EKb9KKb_A22q!<@I%N}X zg@ZqY-}zX&RzOSq3_y?05WkkU@}xH2|DN{ z`aYQNZz{*hP;K1M97L)VHcRcVU$$0(kW#0ZJsqN%61E{)*`wvW{*hUORyEXOj5NeT zjA{+RLgYf$7zJ!hOB-SJ0C5Fq%(d5W#)3Fk0Ek(cu!d^0Td3AmXvihGMedC=1`1z+ zV3FHswqxU{0yuR{9UWvQZH$v1kO|G{nE@<0iqWC-!hnr3#6;=m_zu&AI|I=>j^2ff zS3XnG<-0=Htp$*6F(Q0?kcv%r5aW_z5)D?^k|4Jbv!r!R+EB>P`O!;j#8eCH$}{Q; zXJ$f*8bySr^CP9rmjB)saUu1C1!u;lzt0W^IV#mKH>a{fGd4spjzyISEgLnAp=d%na^xkeEVeqz5g0*o-sWt4C zJkWFZlPth8NGQc_uWu-YXx1n!hMO&Bj#Oxu=w}?_lp?4J zF4t`rWn{X0RE*k+!OegC*EcvTx{Q>vHqNM&opOkn(kOH~sBYVD6l|qvzg-t>Ta@(% zLGD-!`!cnI4oZ6W<&-m~(Idu$(`k_Vp-hT#6n9={Exec4$}Gs#yadtbGmt66l7T5% z+`6AwGO7nTIwP0JCIi^wwBBg>F-0&4(;!hihJ>k+6Js1aIA@IB!+C>wN!86!*l~I= z=Zp+-?Oj3uL1;Wlv+JyGyoD9vy@PiKZ||#4%k^4mh_DQLazoX-d%Qz@2*%))oC9}F}CVi zV2se6MMq88LclZ*Sk{2STUcxGdR_7PX@au`^I9Uu2&ZX8j1g;yl|3+dk2Qvaq=`ks zp?BO{altRc6RX3Q!8^>$2IB}nJwG8u24`h<7-=G>!Q=h1;`wP3YIu;fxNZ^N$TO7n zGm2az$!lnYQi>VI5+?5jjRz|$Xv3eLMp%*rfh9zZ9?IBa!k>S7!ne0siacigbQ-a$ zoAuLqLQF}JmchyywndOJc$qxbm|y^WzphdQWii&ui7+aK(pw8xBFRjfa%T{%k-aSB zq!e_nCT1CZp0yycN%wQ?5LT-9$Y^mIbw68Tx}S-9*Yt>XX#Z|h*iV_d;+YOV`=KN8 zQI5E4tb3j#KaNML1#I!aFc_Q_WCMhG+wgr}u%sxlQ|ks0V+j$F87J@XmzPf%oWxya zmL@o6(RI(;hA-C{bKEM0F2sbO`y%z3rL;Gq;=3)_e?!;#sIj2q@T;`HkAEi-;WP}D zVIW3$55(0E^}qDnG|R_620!*5CZ#?8d_Lnm3^@Bi3i1bt>B$fHX*vNAJu{JcI3;No zg4A@yJ2^{-*a6K+_K3vw20*_-cl~>t;N5-w$h3-^*->KEPbz&hq^J!8-yr8Z9q{kW zF5U$q@drU%|I)JWB^n(Hd^1q)hte%V)9N`#T#R2!-E*WN^qK{}@p8X$_c5j>#J~!} zO=`7VX%0r2seSFvszq*M(@rv`O>7^zR(=OF<2=fF6I83-GgvZb#KLt_=^-imtHg_X zYxW84!~l{2Gz4%?wFjadR@(6bYdj~__ZyS+L!fOB0wa6L0KLM+Xd+5HT&=^TBVgX^sg zqBH>|ocq>PK@pymcXUc?rLg5B2xOr~^_%D0*vFk)aBlaS&* zJ|l)yo7z4lrorPs{_`ugE#lw) z?FHK!@qS$^4U?;`K@jF~^cW{E51#57_5hhQ*~^xy7SXxZ6tU_bwk^uqNL(j)L7Qps zZ0915sw3zR_EmG52W|3Vl+0_RMsUj#_7gyzESCFRsPpX*%oW`?mEI{6y_DqNt_#(* z!@w?Ix<0SEHCt`+<|W9`rQH9R;T+Wleccj=qmhk;;!TWVXh)D-5rqeOEBg7Wk zSf#nQ=SiMLTNxMBW$%hXovr%@Gvmv*Yc(7zF-p$bou;Ix&`Hi+LX27IkEGfb)MmLj zb>|$BlDG;QHGnD<)O}_g?bdQoBodub3f1owx^8hqHz`#|;W8v6A`ITcIa@>Soi#`) z;XDns!7CDtvO!JbAj1wdCo<~1@02Dihgw_n&RV5CsXo#f49+1Wi39S^%7MW${91#! zBxeRfO8EXJW_;t|V7<6AuOZgw`_YJB?x}5b#hd|>L7KG&vpyee_XZt_h2A_44k>1= zF~YG-u?OdnIU}q=nzBM<;BGHbmP?Mgx?vJ&{9bW0FCGodSk+DXJbJ7l;vcUUEFt2r zKR*jIMVang4u8F{F{V0HQ-=Y>obi{J6Q28 z&>YK`jXvAn7(``pgp@E1UW>3In*iRn1u?S@802gi-Hg#$JWVJ4bE>k0ry;-^gYUD! zdk9!lz?bU<&WOf%@P5aT*wwhWXYh51_2CD!+27ua>}sdm{N2asY)XtZMWiUt;?p=C z8pj_uwZA#<=Ldk`y?=AY;q&yQ{tvV_%-mr$gr{-B;5-1R2AJ-&*|!w>PSDq+EnPk< z*M|>3M!EEogtJO*R2 z=7_Ad&XzX3Z;NV}Tclk9k=k%8BWtfz>Ni`*5~hNK zk&!q%v%1sfrs1nb9fg|L8}77a62q|pyobExJ3;rQ!Lrrls8DwM-;p)GqBJj=o6$^4 zfX%l>FL!yN{eyNdz`VCTub;8|C@E&_AUV2`5$>b!_P#>3kQVecH+o#o$jPX7tSrQR z5mbz!`tDKM95e*+515SFu!fP3A#BTYXHNM zMNA2o%c`1SBk@@&3tFs$7^5P6R&+J0eo-DCSJz_+1~<-N7=8PUidN@t(D8g8E6YLX z-3i~m&qz7r^UJ9=h?$!+I_EYZvR3qz)(L%c+ZqMmd$%+4>hBAe?6fRRWTD#jhChNH zMtJYkyof6zpOa|3j3ot#bUhk$dK0R@(nRw}w_PJw;l(z!9yP9MdG zyxO+5igS0N;EQRJ>?dEpUmv>Vmgm~pZoL+YC^btO=S>}w5D5*oWQnE{Ba|C6;oIAc z%#8o%Z{OgZg?F;nNgJ!;jBS*a;hgInOPhW@t0CHH8Y+rkp4Xf+d=kxVh!I}ty(wl) zgM+gM+ZN!gMPgwokVo}yNGXAg!RW-;Zl~w&V#S~X2xF!&E#^5?=EI~Y zoOc$UoYJc_Dv3;`ZdqeMOfV+N+CX#Bu2EfJBug;Vu8%~w8Bi|H(gtsr4Nqr}lOHe) z4h9*2`}&6WHDYv-BJ8Xxsd55iX;O&{aA4Y-j$<@tnz~_$sZS|viFbP*0sz0faeNy9T?x( zzx0YvrcO0XT@1{wemtCwJu6hTXwFD1hPdvFHro(k8axJP@zd!f1&=YJ^$r2o07RwO z{`37^(BW;vQ*n77NBB-JH#!e`f5rE8#U%uMUuHa?COuc^ZiDk-E5XB zxTQ4cu)khC)6AEQ%IpT~T0E$Joy1wT4w0kSB1h| zl?CcFW;NynZ!E}<3RuXqpSYQy<=h6YM%jIq8iSHMPlZQnHg|NXB`}P$Eo*caPTmJV zH^UY{w2HYJ4&6@VSj|yj2*z?n9dA!TrOJ}sWw}zP7tUD_!J@siR`g)SjhTycuMomD z7pqO@n<*GqbAp2IR-;aB0MHdP`}%;J;Z3Kb>vKjQg>e%d|GRIjHTSo;vFUV#Y9d#FTKIH+*`TFb=LFpdesfgJ_3LYnGdNRb7@Ve|>882en`smjbzOrDM7*nx^F6?PEV+fsA4O9ilTvT38cZccB>Bco zxGy4O%&oxW9$_med)$FIN0-5n5?-$>Qeu318j*76l&=U>R9E4_$vyi#I$X4IT4T~I z-$olM#rzUtrH%JRZ)x&L52<;J^4i8=jj`&ctdZti^!Y+#U$zLw1%LncE@*#tm zCRLnS6G-*`JJt^lx-~SnbW4D-7C9w2&Cxv#9#IEUmI$x&ikLHg`Tkz_18WQh@36** zB{2T|@4r?=2mnswQ14wUPf7Jie3SiXH5>eW-7trMQ})Q&;Tktw)(tU6{QEDjc;6yk z#!0Si^;sYU%^IxDtTu$#B6neV?WnZxhDLE6Vm23TDeN@}LFywB$ZZd{@1J_5W?sK(xXb!RF|H+j0%M_?y4K#ej< z$cRZ};J~;dz|Jy=@LFJcXTigOQ;Zna71yv~i4j{)^8ahX`?g_?5q~CN@&l|9_K{UI z#elG>E3v7fbK32L%!hcNaurr1VdN~Z_9$r8Rucb)2I&SOox~1#(?yJLoMWN zdfhgWYlHxAy&C;7))?@toT~3-}Mp{rinD-v}Ox7fwSX`Isd0I~}1vg3|AgQ@CTG z5x6GvF{?1wJf3K`qnn#gO5Ll}$ED`1=h|oj;`ze{)q2xFy@1f-3c3E-e$HvFT83H0 zKd87klQGre(ZPTXiJ?`M9cHaRvf9bCn2Vc4a%x)SBNLOgm&z4<$z3cE@AT3o3ag@| z)SC$3t{c9!k~o;A_XefMdRp#5V#eeLd>&5ty3P{&N`x2VVE|0t={=NuBU{ZQ6-c61 zm)bHUCktrQ9cz8xG{|iLRpuY=NMl7YUMLHnxI;^e3v{SCyeVn(QQe$#o#gceW`B4T z^zFuNlCmOhdDmp4qaT*ZJ-XsCgoKm`&Ug8{WqNKp&4!>TXxSO_DR2SIYzfP~d zyHBMsYYkk{Nz(2BuFi?wK`aLi?cAq(k{9$xBp9ZX3sGr?CBSuAKq2@9y z$*?TbooVvBgVzv&T$|p7EwxRt1Z110(TK%jJp;@^V^oB0oz;o^?#Q=D0GtKKq;7pJ zhg#SW>4qcLyRFJ0$Z*(aZT1m7rXn&O4Y-T^s@If@)j%^WV3r0HEJ*Sel16Doc%|2t z^=J)Y-I7v1jYOLj1Ey}M!=3*rX#?6&_5Ht>x-q5 zRL3vO1k(**W%GMJPlz$${kj4Grpd!QE6B96P@FdS<#l^{`Au1JKm?pl9JxCv`X*%@ z9RB@3zhDg!|MRbZs10n03C=my)5uw_g`P( ztij9ER9%RRky6r(^Y`nDFRxd8dK&S38mqHoQd75eOZD{!F9_`59p0}CLKMpO;2mBs z3#=u)oF;6=BuksT-QeMH*p(Qi5)+)1n*aT>VhvGDrkHUWJ$`zcV2r_a*$|Z_0h0B0 z9z3E-J4$w-vj+e8{)SstZ=*sL#*ryQ*uuO~uYv#g`UZS`$8`(HS>!59N|>y{A74%| zB-(9T2OivD|)KYIV_v7PBLR>+C!ZqXF0M8J(s&!(K4yaIFF+kD)|B4 zIeb2!;hp#*j9y}S=%@+ZQ7&)X4<7S^kJPRs`kNW^7VwX^H^iLr=O@wN8^gQoUNKc` z8s$RyzX3Kh_ja@n1{-3^SX0CkXD}O^@u-#n03ZNKL_t(+DdOAZf?uxhcn__in!^Uh z3qGIDHDab9%5z-tzO2|%#LF<@<#cM_qxUwXgk{@AtL+|k~*6!ErP z@nyc?J#0wG7>vcI@q|B~pYSwH4LN?$eA8|j(+_CwhX|meYwg%i)u8J4C1%}ke5(%- z?xwEL0a@IqKO8>xqtCwsx(?GQ62^~6E|pbBTuq|Nr7b{-IRaXxwyd=Y-6|+>%wT9yUe$Q>9O;{Uw&@BmJ}W#M+pb0Wza&cw4hrYQsJ@3P#^M_x{LI zI~mOl&E@s#uz>2YxbL5ODr*^>w0YqEx*4ri5{p*((|Cft7%}6547=92BBqGJIc!|uF`%yM3t{RQ*0aX+;tK)Y;jgq2{ub zUGMLQC4-aqFbg5%q{fNzyROTMUoP)>4VxH5T8A|yd|hTi#l6RA7;4;HV#amd@Q?Rb ztSKX&5>DeF16pnr?s?nr%lj+H5PmwH@o73MT~Tt0f4#in*X4>e#fmsPYY`_7V}4- zE#GKK?@pJFP4FYCI~don;-A+y{4!q=S!%pX3gG#Qvl}ovIjf~4lV7ogYDG(UAA{`ihG|<`dcIyH{B!S0V2I0 zz8SNpv=`}L0iQnn{nV(zpuMT!PP)Nd&9{vHoPnt+cFd4+AMqn1OC8ug) zp{yr$My|0wStUFQGXu!T$hsFxiSJYb*uWm*&ZMjxf)TKbaVrv`+6?!xsG2cQ8cKA= zaN<|u+VH5$TW3b+_}ul0)G>W~_9h){5_TxCK}Zn0}|qlKAnicq6i)Mme!U@^qGHKlZC=G8rLhIUB6jux$ruhoXT zP$IHYDw_se4(y5)Vqesi<{XMP3x&EpC`FGzT`WQ|RHBY<+$NG-5B_ZnyNIKP)HkE1 ziWXGhw+#gbZl)RbJChLVH1kOv*?DJX zLrsTmnxR#FUc1jV4#hCaVjP^(+6}yu3~0$Bm-qq7O}kcppCN*`7K5`0fS`3+ygFy4 z!8E7`55Y9l$>7GFkWezG59Tv>rsJgh%`{jLiG%gBY?vkw8)TBZtXn;tiY#K$T=U|{ zT^cr_+^0H(=yl8mEgwvImU8`BX}rt-Gi;VPAD69~Iu*Kp+Ocj1XECoErom|h8Drf9 zmGM^eH(N}%I^YsteNpSPWb8cg!3@g0b(gkBqq_;L1nD_f5zJ~ z;}Ru~?9bjmG}1#e2r9h|Qj)}$KYkTaGT@V*&gco!>-X-~4axM54*rg8 zl|G=l>plFoT=8|8k-_-$bcP||+qz)YLC)u#Q9>8C6!Ess_`WUxfIpv}Fj|Z6>x}mp z@#}KM%P>ha6xp4oeBM@kTdsK9q;d9!@G_k6c{<}Xj4H3Wr`T>z<2!)^9dm(i_^xI5xYnm0(e{QqR}T(4%B|?8`M)vS*`dVLHNgcv%#|u@bi*k_u$ll;DbG#u zm-QSjEId)Z$g0X3B15JRNB=CH+uHNPh^#w6cbxhYVfi~K)9xW z7z0d6dQc-X&Yi}#^b`sX)gzM(Y&E?)fR9w78x`&T{_A|OfiHq?bv@=RQwu+3l)0MOprd81%UUTLea15t10mWYa8<*q4^X+kCAtVCNUKcWG=mpv zegwc;bi)FpuEEswsTly}YAkeYeW-h=vkr&EWt$b5-aBRAaN4P9JnArl_St2XAfXz{ z)agRdIW0&1y<82F2qe{cHIXUTr8eujCQ{Bd!b#kgWiyX4A?A!hC+ zS&o$Etf>J`%>c`KiQG;;#-7$pl|)<&>-70K7_4$vtPt*a#$7O>^i8StSw7}Fy+Z= z6=lu8WaYTrAG{S02!p{|ajm{wFo%GjPG`J~6GrbvH{7wjDr%!0TFe=jup#A)(=g)m zNhriIXS|0ETa+gI^TdE{D9z+OPR?VFVnlY?7EFG?phM2c374>8OGz7estw$i>pQ;8 z7o?o=hw+3zJwM~qbP|LK1e+{Q&ebTsm~$P-4%Ul{ZW%gfF~%C5182rB>5A8|;VlF_ z#eknqPZ-(PHAJ`t826Dme3LG6gfts^%=gWj`N7}mwgH~QhPQRWmJ>ev5q~-h>t;y_ zTUZfUQ!VsGwit1Z0g-{ve!ySO&p0{pZCEaESYpHy1>rTWu|<}!;g{funY>e*ce4IxJv3}_ERY>;w7NCBfOLpH8*(9A`v-3@HI zv!h0hvS_kN6D8dL_J8~lG31)XI!+Gj8u8_qE36^>^rthNH#O6jGwa`Tjbtf^ZP|QD zV?5CPTS{3c?X;)h(yfX*XT~x&D{iB~(;*~v`N}Q-*T_U*eOr-B;e>rwVXr?f)0>pI zpu}4rr{Wy4OB)q}4C0wr?hQVSQJ=QJkB7uxGHS#5AiLU`e%#pNk zj6o3YLT$9Bqp>ozMp~P>7&E-SM~ROS)2EaZ#&N*g`wFLxghLl0)0HVPBv@zQG-e^| z=3F$zIdgNWv}I!uO)j*Od118OQ zwc@&5Vl4_2p){|e+m!cYy8(%_1~F&Ekg!_O|6Z33z&iKq?fRt1(%USeW zR>RiU5W!I#h?iB+Oc}yRqfpF($~A~7!zM*Gy#-+5tVkBhGcFmo8qyBN`(>%F%JP{| zi=ZtDz1cehQd6&#g?T|B)$wI)LG+u=CAZPW%LZ^=Hh5>MmaSRq%g`XCg!jvWc@6mM zpMOGxyk?3S*LB0o^8^AgFXE~^jjlRDmwO2{`bsHd4N*R8S}|_{r_pP4lBrF5N^+=% z$auRf__sfPf?*(vLbwB@S7KlIn1&13A;_OXYcLZu?tIu!ivY`GlBab@CL(47EIO z5S$}~E#S!y(#RUXdxN)2z|;tr(pnN1RxaW_iaUFhGE7Q6PB>yeO>YE^@=qmybJ?=K0o1QJgMh_Bn_-# z!=jnCLUTQ^tr%byWGJ!Fxi8y_Yl<}*OQKI`N1wl!Jkm3Mm7G$i|cm`n8SuS z2CXfC!8nXgjJ%w)u$pfxu{t`as?9-G2k2axd4=szw5{lo;D_&xx=72W5qE|(k-~IC z-DdnGOcK*5z*%bOm=YpqJu^BM3js(*;^nlS+)_l+Y+hkED(dd#>kMEy3B2JWcYk!2&b&Kl%<#3 zV!-4EIAgHp1j_`g*Wt_c4gbr}e?rK>yv>*fF|gcHXr{pWo(#8p+mg??N{X5$yD^Oz z1Q#}G1fE_-rOahaCy(=!AS|WOHAeG+4QfMR_20;#L+7|o`%C8;W2()U9~@#xyZF4G z-dLK(*KZ5fE#Y)_7)LL(WTj7+2CJXQmIfuogkf~GIW$fkdSgP^WC~SGoQzeo9L;`= zK_0XaGKRs{0`m2Bf%7ua8Ap%HMVx-i;w`ztAqWC(EW!IiS(HY)WX{GIDspK|ZcV#s zpGS0~To-(Z86jr8y)7692j^R}QL}uzxSDklH0=GdVcU}A6KiyiIY#f)tXkSFP|;P( zAfwQGt5%{jF4+PjY!TjDOrsV3t1+EU{%4Oo^9DP@f3u`(x0BU=o|jCCl^_2oT)2;LSt zdWJDle1#BkokL|BIBQgI%yke`n$&GexXb~5aG0i18ftA+i|H5j2bQA1h02^WgRSPo zx&haD!`u5TCT6)AD-=%@LGP)?dGs!v#i%Z2aYoLa^ORAxLP7bLb;C3|F^O3Ni~&y5 z2t&Z4_sdRJ8g^}#w;TLUJp0MLq=VI&%*%k#J#!y9%l{Ceyr}?Q5gf)P-jHd+Vy-? z9I_0(=556qwmSTITNYen#AGf0`22+DX{z#|=1r#CYcoYfkvSE<)qh=Y7-c@3Z$f4{gxwtN{XWjRAjq zd(+HKi6ts>fTJHvF$yAOk9^w}&?{l`GC00$E3P5x;E(Zjxgz9<*LB5v*c3q`HGo=n zuF&t>if^lAID1G_eqC1t6KmlbvY3BK1G(WE0@f6(ZOLE^UIsCb3Mq>5m(u!ehwh>C z2q=2N-W2HG(CBrU@$2;+-dg61Fh+s@sMz$CwKOF>M)wDA$AU#i{}O9)s}f+NCc zg+aA!@>wjAG=qi8To5uNe_vCQ0WbkejEFhIqA&!Tv+Z>Y__EBn#E6r1_#fvNJP#9I z*BO7izF|&s%|DF+Zs^?3?;5fm9NYJnsUJjvaW#8dL%{2D#U+S`f((3tHO_e37EI3L z>_?&U7d9G-n{vmM>70z4i*Sp!?DuB;aIDcMsYq>D8a{AMk5(|jHkMcg^vqt$p-rlx zQuk;O5}|5vYhnxv^}fmWy1C|_L0ilfx$_??gAoof ztvE-#2$-{2HQ0$#t*c=L z8)D2DClPsDNAQD{tVkpIp2k>Alf14imi%uErqN*-#gyjfKb-4Sp*$2;k+-PVtLU7g zA`H%3n9_K4l>G8{(+v>(K-e~Md|bDHc@FsYW!BfFx&Z5KEcD1E*ZJv1C{d@gQ%7(? zYeUTHblP+SeV%M7Z09*>0WD};r734r`eBJxTGxc@Y^p|>bE7?nl=WUqgFL9Y(Y6tm zHP(ih2>AMS#=1p>kg%=^y7xeDt2CE`g>wd5P$U_s>r{|h*6i74@>C4mdb1p>bZ`dO z&9-Qsuh&i1l{!1OW{7M2OF?^OcplK4{`1CU1)5}TqlUx}Y-{0nnDBF%syRHGeFvh_t zEjg-do^0Gksj`!@3%M2ZptJ5H6ug-sC<(+!=M?$lQNfnzS+cF%Qiv6GA7ka9p` zNOl^AI(R;kk5e&x;pQ}Y_JcMjX{Rr?5LC4gjZ#i{yv>wk$5p+zkspO(T9jo$)k|u=<{Vnod|_#5G2I(`K|3<%1!(F8Z+{c`_g> z&2h|(U*;=lZt@Xp2%o(S?UoR54FOw9NO~V%w-rl@yTbqfBkkRm<4Be+z2$MpLlpp$ z-MwdPB-54NLGS+T!Z zeJg4HaV5$oX8f^U@EW%!v8=;@q=j%{S-)p5@1i9~6lT1~4f%~5*zz^3YGrU5ZPmoH z?RP89fBw+4i&Qe+w+(-9D<s)REf>g#{fNNOs$MqeoO^10HFjx<#5usZS`dJG= z7F&w=>yq);cEy@9PTs*8tLl-W1+c;FJRBKZW5Dm1H#lqXx~<5}cyb<-ca1}`rGURz z@pESs%yzcp~j#F@1Ajeq1Df^N9s`HxG+ppT9leYtAA%#JM8CHzWIcOj**e=ZJ_{8 zx@%P3s1YjqhmIDjpUJPLt`@mf=rsZRr48mn0ef3~d{3qdB> z=kti1N{cG${hrm>y<8LK6EKc*7pFr2k`y8>$F-lJ?;Vw=r%5Mv;y0|te27w@n&!VP za83%@oYkinsmV2scW_n;!_z$A`DMgucCglBSwoxpK@TtM60xjNJ(lQVndG_-qn8Qm zl5n{O^=ZrWxE^{!%s8J041-Z15+9@_T;yPGt!dLYZa(CFt@fZ}E_-$?f$*-ythKsH z7Ab-$1C$CeZ`XhrGX`()`FV!-;teLqI+C2( zg6k67@8Ntv2vH}b!=Y2UON{o|8BVEX9~&)=(3G>VB-ESBL_1~t;Q68^nXZKzx(RXxuT@*bu0+)Rw3FCo zwNqR|M#v0r2&d5_SKu6|$p=lBQ!ddA1+(`8t4kRn7s+fc`{AJG50_gANH=9{+=F&v z><$pkN7sSty62I*0+3ra<71A*!NdCizO!j&_UIixy2i6MFk{O2wk-I2U7OXy83RWKgEc5z@NL_qI3~tmEWVu1`0{kdY=^rX&Yy+h z8W_yOh+ofVFo5?EaES>^zQFeEhYF?9#?fe{!2P7}_ccjFtZ3xQXdTYOD6(xQXEkRD zpHC+_-)$?_BooOkClCukd+hV)D|~AUc;8lJE|{Fd%P=D5jIF4ejx0Wn6J93yESGJ= zB}wjbWQ{vw`HVGQ)9Up-JHN|BOG3y6YYr;0MDt`iLl}+4=j8JDo)J+87=$Fn@U>ZPCk zAQ6REZ4wtz(bf2@K0dRm0V{a0uFDEfGGq>cqd&}Iz5(mi?mgWQiud6ZAEfMDa*H{t z4NIfxAJTN!sc|XDCAat~W2jT=bVBAE(EMX7NDmz|`p0wwQRQyMkToZ18g)>+s-8t- zOVG^$Kr6q?$KDDfYzfN}!3_ApU>w~(q1J_2rNYcCpO>Y0 zan4{{Gr$|h*<&0XmTPKL+d8#oEgTxbHA}(mooyRxCE@OkQ0l18yO_a`9$`~C&l=U+ zSn&F7MF<(ElSg5>C&sD&H09>W+tw%&Zav_N?9ylikiojf9czyrzEoef%M!6}30w+> zQ9R5c$U&-tc2Z(&TS5qFpJ?ke=X@GqonUphO(d)9Dh-MhGoGJE_0w>h{1_66X-pY8 zn>L*$5>IVa0hsQBwWy?m)?DBs2b!m+0jJX-YbYkkCmgK&dCWrXb*436NQ0hJ7KyTC zl2z)lOk%f?)xWeiOF)DI14rDZoTrn=|N6gt!8Cc1-fKJ#sV`X$;To21aPapL0nSP; zrFSB?`ug>PzrHQlLIh=5XVwy4o<{uR*C#wbiKKVkVk;EJ!6AesF*Px@XH&euQJ+E6 z4*oSRsKzK&kQ?itU6z3B5+q|+CGx}IFetWCCh}6Grz~+9q?5MNO(_LZ1B}+d8YeQ| zDA#D&5(pXI7z`&>d|Xzf+7PgW_hmyajA@Yk&yoxNep^~m<($RqWxXpE4iX-F%)YCI zNJuhiHO3%jgK@B!A(ZO>`rrRlnLY4XJ;2Ty{FndhU-)(rrAlQ949;{1bUAD%y)WJx zc>g!1!C~@Fyw&=%M{lvlgn6*IE>YK9!pqZyagh6Q2_j#PIU`;(`1h~P1MaMCu)x|} zDur=fH!#Z~*gH`RRKIsAjK9BK5VJ^*oh6*+aerw6NSP6HgzG5VRTAy3PI2N6up>R- z))#H!(j?Nx5K>2C=jI{irlh%U;_;D)GLflQC5j~yvNi4J1=6Sp*y@K)n8;~Uj`8cM z=0vA1TC5)OS-bKw6?B6~h#A){B51z7S3iAGxxe_ey9s#@o9ZOVhiu>5336Y?R%n)z zJ()-k7LPbo3JYoeIO<}&j)&0@Bd+V(R2~v9Cih0xc;2B@>PyQ-ijl&23<=`L7T5b3 z()|&+kAmtfnftY=X5pT`T%m!rFiT?`e=M+|xKVrb*!)+#|q4+h97i=oy^Hr&*@yO9=S7 zEwTnQ@A{-t;!+A?$#~yZ{CRoDdkk<7$)2ZQi5PS~OV){=sc zO4-9xs%Ov{E6bH*Av{0)m4QIr|JXnj#dyMLVb;>gsti@>?a0vlhj^fb=FgcHx zVZ^7?8MF7Ts4|eIhEsX+_bP$Jd7s9T5iupv0|K%{3_be+r4%eV;hJ}TORF zO4cYagF*#s$q4IJNxb{Bld(VR)w5r;`F2SO@%jd14F8_G)`%z)kC#ukWBvNOk5_!8Q44#J*PQ%dbF7h7mco|P(S17rQy3!0+CYjL{ zZm=vO(=~(kLUk{gcZk$Sdlb6jvwcY}C{#LNl0dp*Enu-1w}qupw-tg&BC)ONL56x} zA%9-?P}_Y@Q3064yWw#Cba-@V&TyAAgsvfa2tmHBaQOgpWqgQNxO;aL%_j!qVLV=6 ztT%2r@8^Hh4X10f8aEI`Z#@bXc;k?*6C1-tRkskZWKwHkOEw_}krj_HwFKt&oQW-} z?2sk0s6*ar`b0^ z5E0@0EQhvj3(Z>z0LwMt{cRJdmO`k;(KeY}DL_scF_zYKn$I4Ww}5Y7SEN|l^!Tqo zuPC_)*hu3{rrG1^dBiw*c&9IqSeoIVbZTNqB3R_GW?FD{ZH(lyQ)~bF^MY+l*w%!c z8K<+yX?7B$5;8Vx_J|^q16fQAh?^11k}-^_Z79a5UrF=RO1Wi?;&5mqqfs^1spnce z0p1IUi%4_Mh@1YTmw#U_L1TXQNFmcfzP2q1HCKJnHTT#m6s*G3>H|QOk)ml6;S{sN zips&;BTW@AKi?X}D6-!W3&z1>oSdw$%Z71qm`10bSb#Zc0cCJ`-$Z7uaZ05WtRW$U z++?p!rK7q;>ZBik{&mH=Mx4(hPP247FV_vrCLLbsc8Z^T9Nivkq(yLmP<%!9L9O_d zEM0@i%VGZg5|HyNE=#~^9x#p$^E9*r`cPqI&Ng`w!1(+!@6)GBYlN3|YDIou)VB+0 z1HxHE&D&*d4p<|Fwjn}tMM=j_rUj~$sL-%!6xdAEW{gz`)fOUN-&ef8FIctdlX5|c ztPLc>;N|&wd7AO-m#3CL>r{T2Qf{ z<1`Hb7+XmA@^r#)UrzYr?+eD!;rTQnri{z7!E3&(F;Xy^t}2Q-;r%N2?=*SwL3@E` zt?C($QTCWQC4?x@vJf-=db?ta3FF{!ng%#4fVJ4xraT8!qBK z$VDciTA2TjKVPv%x%S>!7;}~DX&{j&B(Y011K8DRvibw77qvEq{(QZ3FP|D0v=bsf zoyK-ggzj7pB+1bmSTcv=D3t;(1>QOQ{dVmvI6FA6HX2fvsY=xQ|I2w6U|L~?;~A00 zrlQZ2>}gv{xL!AWTLRXqbm?CG)$0#Ni}5MEhZyzUe6F%(MkfxY!{n&u1|RZD5AkBh zHqL>VO!_lWI!RyOj21xA*JTmlh_RwTFs2>sM{DJN6E(?P${CkHxM~#Lt3As!b1Q4}^fN$5UDuzT7yTybx1*|b)Nx4lXtTC7ek-R%6 zq~Rq-1m3;6YfMr!=ZL{Mq_V4sp1j8|^NdfYv%>W<=MWpf5u0q3|n^`=oG2s#e z7FEHF*5T7M;WP{qHxvWjwhdpGD_+Bf0>bBEz^~_LJWW$KB}Rh?*8k>>!RUQEus(|E z+9%ul0OEP7g>ua^rNj3&#=$QH)6IEA2sbtlEFLt^Z6NH(h1HO zjLv@Go8RjS)?!$4$OV478|Y=a^gPdS*1#K!-S^Oec8jK|dSU)j!EhI~0pE} z02{brR6DaXRCeO87C`g>2Kb;K)#U$Ll=cnZQX6av@s!%zwA2Ea9WV)l27{^%?^4x4 zw6F6XU{#f#hUCOhT8Vm;{ly^pRI0No@F(EhHf zXRO+wFgN3M<{N8G_dT33h&quqWbiah?b?%Ox(D71;KXH@h@;%36>z7Vk~4f;k}?^C zlSqk1f#zLZB;R11Jm#}NLf&4tU1F;f+M*KIVRY&T<(8c)3)cXtpcLTpzBL^8I60io z9>yA6-ZsRT@$zMm52wXZVMYq6k)^Mf1Y>36;4NW3pYYpnXM~vW$M5ejhOinrY_~kt z%!n~zT@$YF0pldaP1poXB_2*$#OsnYJCfTX^;P0II$W=tbh5S#Ym>sSWU~yL!n8^U z@iB5e{NNf0$rhPl#4RD~;ZG#fMajV`O|<(2w^G)gpGQoy$De<^DM57YjD4C!|58qXy%!LFx24D-~-xGKl@6v)Mhn9su&#syMV%bqz|Im5Fi)8HPkcT;XJ-RO#zgUlfBt*GOYws#MXMD$yk@N=!Fe zbVEHCMGxP@U~ye#if(=*e0rI2Rr&oeSgf0T9qacQ26G5CKc7a#B-dbBA}-erTM((a zQ*Wa9mZj)emLP}1C=&OEImva&rDg?-LZM3Ass#UX-I{{H8p8ITa9uWB*VHD|R%OuBFyOKTIA`$r zc|uGDf4?sH`gVnL7GGXw^+t1>bjW10D53h8t<8Ld#0}merh@nDir34AHHhqSa0bpA zY%yaV9DaE@;dB~AR?Cce^w>he(|N?QivR58X~Hj`&qDfKgtA>F_En-2QUc+Em@;0T zCY(=Wg96TR!1H-R2oaYh;C&&iA>s4$jFby5%Z7CeSVIIen>z))KZL`vH+m>a8{-S?d>5X3S?9~B0c^=HIXz($&r(4xIgohjF!-Dn|ANs}tj z%2`}O0vE<;38Rwt4ap>BS%OS`Npg=Fz_JB=y>58hBBD0tochGqCe{jHB-PaUbbD75*Mg32X0B?9xNZS&%c@g8YF^O#^`kb% ztg(&LVL*sZ=-9D5nv&gyo7XcB-3j&xA4LA)cHsLcZ z=-^D9{(6~uTZd`zaNCAh_I&FCV96PASus0{pddMG2ruJ=FHcW64Wm#`v&HNOI9d^r zW!;*+smQy-bO-m`nN8>*(K}dAjDA1e%c>84a&jJ9l^#3~gGzcUSLJ8}{K$V@iZnz1 z?fn}Tl}iVeXHM4R%RJ+k`3c4tnfkuJ<4t|LwoZP}Pk5eYIAa^H;?4xGAVe!w!FN!> zIQTn3Pykf54g6?ar5%%8 zna`ShAeV5Lfh#akp$s8$7#<2uGjib7yKBE z#jPmAF%F5in+S7Dml)lkibpz3+*zYK$vRlJ0CkzOO;)@E?BpC z#^Td-f~Wn=WJuTsA|oH#HQfEXB9}hCl33S-PS{KJ{c66m#yu5AQ&zZ=Wx{hhdnqip zi23B(6lEOc@D$dB6htDIGw+kVDrHtrU@5@cvSON@LJ`A;7S*+ zQagnkP4j^FZ!7EwOq0Xl4X&3B48s5PKYzl1|9`$={dhXIMKaf?kJZ9M@+rbN|O-GE$Vz{uFv)S_t!z&v~LbgoG} zdRY&MNur{}v&-0))cg|T=-{0}-TTOz28gNw1k6CG*{emMWm||CoW=Y!fHUL$vLS{7 zHxS~Mv1(DBa}k>1Xm#2yg|D$Bhiz|FB3ZD7ERhVk;ORWb;XH`1MLgQMMFo`taAXAS zJQ*vU@I2%;&9201YmInBm6~0M@ciU4O&&1`Ash;E+d@Ka@k~`>$-p3_cbpgtzy#ETcpqyF(tgeuedB5axQp&p4w(t zbYfloriKVxosb!kRhz=C8~FORv@B`sq{vvK6eOq9fJ}s(Wh!-=ArtMK5n{#`GEx%S z`IqMzpFf>s8ap{GOTZQaw%`=#CLPom<-xEd>$?);S!i+tvn^Mr8_;Fom< zgST+b;{W)szhiuIIG+Z|p7aj?`5&KP3HUGn_wSeo*&j!5u!abqGyEX(<4RqwIiC{p zF8?hgDN5HZNb|&rALhEOc)td`T~}-hJp24|l7>OcK92ItM^SdHA;B78*)~{f8`-}4 ztgR&&vhqSN1z?%Di+oTTA%$zq1MoDD?J#v+B3_q(%O+FndGzx9Y7^=_k4*wuY1LKs zF5h$3buAO|!AtJ%+hxT!UR#WUwS;-_csWm)29I1aLP(efr@*(|Ce9(HUFKdRol;SW zB{xXX8WO%P8?HfKlTV}57oY4yAtn6leZliI;(2z6x!}*g-tf9b&4#BgYD{RqYmTsF z8!#{TIgyY(bI;}N@_nmL8V?vGom`E}V}&u_z(*K#03TKhNNY+_55LNYD*a8G5uOVn znsUg}bSA@F`MqP2H5yauV%okV6@5_g?z3OIDSqj$AZ`r3L%a$fi(rL=1(tWl5iGV> zVa^zt_!{}AW^WG8!I3>goV4Q-i?Sh?(#`0F20mEVpr2$IllS;Ejc`G%1~n4rZaDto z6#T;ssKcMfl<@cUic~Vb%x5@j@%4JuGmF9G2hfAO)_s0Nhc-z+z`sN(iVTtDlY56x zvrdfaIp5jK2VPC`#X|snx+^u|^KQe3{y?u8? zZDk8odE+t9`9?jVkxBIs#2@7Ll7b1(eh}$tw-bSygqnZMYNe27P)#w}>$-OSDMhc}{vIXT zCnB}EQkdaN$sjhkUP7a_o=-wu&5;pT@f}(#G|uf3F^sN(feHadAG(}mvQ;#i#N|B> zhd?MPqoi`6lX2>d+Zt+|Q^M)&TLjOxM$kOKdxP~F@bo+&sz3OzKQ1Uq4nWpYn^eY- z5tan9lBA{DxIi#tNLtC|T~IYm#TiX?8fDN08YY8>~cXE(K%_rcnwR&LV?MA*&pm+u_;jCQ+%G z;DWceEB^kvh-?)=Nx=E+@ar#U%#%#%-Yx+#$P^Y;TBQ*pV3BxNPnov{zkZq3!bw=J z0md*qG;21?@ANc#JiSbK{kGt`a?1{OmXM0Xjg$mr-hnxV8Q05(Y4SLq2UR@~&hvmg z6kL~p*VnCOL_25UodLASSr*AOokxelQmlV^9`W)#<21|fT4Q!b^+~6g^&k(-qk}UA zF(>4d;k@|!39P?cYKv#|7UOV|%xYucJV}OaNDUN~Qo-|ikY_U&ge_vUwl&HCU|R!T zFI#ICI4@M?oC?0ZuCPX?-1BI$MZz!|Oq0Yc{r>kGwk_k+lf!xTh#GJ7d>U~cJuYj& zzy0xwn2Hp!Pm_3OgM`U@Z;?WT)qLh$VsV_WTqbX&PHml$8 zvL!svqkylB!Ry6;nlL;d+BZP#nmn;79`Kfu!ttGrI8(vN-iC4Py?|;5w3lab6 zw--#K#kWgQW`M|oVosPRj~ENyE-Q)ywr^K_c|PH3656!aoMg$imF}{$0tSRq%d`64 zjUkLm7-x-Ysz>B?lY_C*DemXoS68?N!@NEfLB4h9c`5N%`D$y!3Jv8n7T=XCpQQ%Zw-INryWIHOYmyAgBiG+=E?=Pkf#!rW7bGu_ke5$`pGeVZk&B=T9?hY87lQl;Lvmfwz znn3{0!+?`!4BMI=dWfm|U_$$dFV!hF>AMWtTlQKVL3NX@!}q8A=%x=;2XtF)f4JUc zGW%&hVao}BELU7}L|ih4ZH3dKss8l>*|!4AS?|*zP_V(O1ls`5QMUA5VTTr@^iajoXK&wKs)vZH{;4~z@fy*_;+xG} z;K|6Oyt7^%?$^NsQN5zOpI73wR?JRfk~)ozTI32MGPWd9cPBq#@a}O(<6|&AG722b z0S;EwgI-k~M)!#N@ABT-o1dmDnp;z*9Ety3DYkPC+I2a2Npx-LPckCyE}c%M!cqJF zDjfiVQiS%9t$iAOjD)N?;C#bf=vflG3|b3#LkDIdbJ2Gex2$9C*O*@4YD*;}d-*tc zT-Gb5VZ`8^0Qmp}T|Wfi@5>v$oSw06D@HpYa)NEWH=X$2!UY>>7|kkFiq4KnAfA;L z?MfDsS1B@uBe28-m74EKVicEPwl)o}i;xTf8jVKGFvMD@NI}3w4n#zIWC|T}=Jw8I zFY>DX512i#w7x_Kl^Tze)5BfS39V9}Eg@`1Cm{yLQIiRm!jcoLk|*a(13e{^8U=b^ z&?2iHMvLc{2{{)`lW)?_!8trX%ioDnWZX|rlA|mDt<->8R-=6OSRS;lYbom0V}XUu zlhbKtR*;fR?CNxW^cLo{dlCBM$>p+P8l1?s)gxRK@BuA+Osr3HcO)ZjbWV{N$+b@? z8Ud3VVHJ(+YhbNLbugo(f?t1~F;5=DU?mT=`W>{ejam=~lbcVv-=uu?npUj2He0OC zI&soKb+?0jB-lla~rK zP4aCk_{~|uXb4X)Bc4tp)-B=nZNaiec@|Q^I9g;T@t$i&cBuqvhTL0g+5}dBPjZby zN*dfQnXjyQheap5G0LAy^Fj)s!B?o29OgnvBCYb>`r|#e$WLDV2cK`1^Im=rsegO08IY z^;`Ke=V(0KyEEA>3L_fwX zz9^WE9*;(YGCm$OR!4WH7#7r1Kc7j9=h0>w^CC)$^w`1iFmUCe6DCICG17k$+6uC8_m!<9@u*>$q>B zEA+tLdUI5M$c^QPweWH3_kck9Q2+-s5DRN#kCBLB1K~Urjn{f4qutLVmI%+ofG?*L zPUF;|Th;eCI0t7PPU;ujl>(0oEq)jy^8ko-<4YX9$8YB+TY3HA>gCVA_3CX-GA@~CVR(CCW(M01`bYDP?wtW1k$K*QwEaq5b zb05=J(-R0tC4&k{y=V+d$t`=h?plcj4)@pf4gVVmp2SvZi|N|I-5Msb4w;+i!Pclh zyoZ$y1EBFr!PQ$M1!b*`3Nv!z_Tei4sWx*=r@0sZ2LQ6LRu9*DNN?kK991g&Xu1!E z-i_0wQmmXZSS3)TPz3J`980ex8TB&O{9adDXuYq|Gv2g=e)S2O)*a^N>2!v$#eyxd zN{TfG#!J3s3{i?CJ#bC4!+dhc$;;t&%MFF$93koSnbiZiTtXvITf0Z&P=iZ|Pk0y@ zDM=9-LIkrEWHnpZyPbU9cl({QaviFy78%7@{TgbS%C(4Rg^#p%VbOc3lYzkl!;tl} z2n4J~sMzl7qyQ^8&4Xl?Y6HW2nS#~DSbfc@Xv|WTs2Kx7!TdalA6&mSXK4ydqjdN_ zzeq8YO2M~pE4D3S8a$90*EO~{su4O(h2$4Py;avG?qTT79#NXNZ199AZ7O&#q4p(< z?>oh;(m?|G>QRwHh4(VmKA#2*qZJx3Tkt&K8X{86`1UOwh}&c(K4x$R#4^>nUPGgc z8$%eqgCC3*+(to7I!PyyY8F;%Zq}Suo#J>W>y}KLp@C2oC}NPSH&*4`1<*n=HGlS` zv0y0|*jz0Swo!dSRk8%7jVpCLGnN=on~&BSe14i_x|T9f3P$~WP^q;cgY)b)s;jg{ zRsH=+VN9cG)7<(Fwc^2$0(5k3Q?3X4ZHsunmKK>M4I1>T8Vr>urqj#7` zhk5iUC2JEzyr?0_dahLN5QHFIN@=jLqFLz-jVm(TByBZ+xIm)nLK4sG&XCpfZr+OBwy0U>9VK6`wg zPI#W?4j8p-a$AyWwF)qQzwmwZY<~3RO+G-~zvII@bNgr1GMo9>Y@i+titi{qjtm20 zLLtKFJWhU;yx_~bs5(+a2ID;i@bV@WE;5+?sm0Twrw;$ja_!>PnfU?nxPu!q?(`Mh zHD4IF)&ShYr~1BE@6F{p8pUBBe^j`(T-&|&JJ?_7h8(tU;dK8*bOzsA1k)`@yVd7( z(}Su7KkmR|)cK?PU%SFcbIC$sHdf!o1yQayuMs>G`)1EOY)81XycfAIUb2$%aW!)Zt+^lsDFmEO!|J5OD+H@9nw*8cEsLXI93{dgCpq9d zZ!o~g^l{6uX>SBjQ3#fv=EzW6oHd`gKBP4!srh{?arw|8-#q|TQ%~C5kZfPuI-j-{ z+urEJm|&eucWVsPPUba-f3Jset9iOry1+V}ErwtjU@MXl+v28nk7J$gNnF(q)=43Q zHC82QW1y7MN!oUjY5VeAGzmF4Dy11< zOq0Vrd0dyEa3|Y3*hVF?QqYMM@O;vUD+NYX#Y3eF8)8slfC?-C(rZzOkc^OBgS2BC znb6iOXi|xL$uhmx;-~uYCC-N$l+0Pvni;cB22m#ndOwmD-qmYijl@=o2T}Hor~ss* z1Z1ess?v%p{3xZYQ%=)7ySbDq=z((v>lSxOo*oR{WLj=*&8;@3Hi6LMmL$%rVDS1g zv@y`+Pt16l2Pr~xL8_EPm6cT(Q&Wg^0FIIcQW9cn(Iqh@u~o#}Ky6Pa$ucGu7@I_d z8Js8Cdk5=4kfJa%WA+x#j+ll$zuP<74Vb=I4^4anAi>^1<_988YQ$*j$f2xL`R*O5*UnJSQ>|c@-gqXz$2dq{bfrFj zC=R82&)j~fqN79)AAqqQ6eq{P5&G^?x*uls(66IZbFYMqzT^7$8CN{YO(e^bBqFCj znD@VgAz*e=yf|x{2OKwE?T5xfK4!x<#qNXOm+&}Nq(7t8BDAd_s+3uN?2|oAvFH$~ z*q7q}p8wIK3W7hFK>l18$tskTot%diOV;#TOhr;OL9^ELtIqeuN4uUoJt7_bve6eOL7Dg&^5sgkfOWC{n zRAtw3?||1rJf(sUEv(k4(ikK2h zrd=u^FOv|eUT7jrqiq{U(c+a$Y0*-Ka)a3bH5-n*>G+Y_Tz#t2Tu{K5%8?njF+@6* z=e?P*%kB1}mrKoDEG>Y#`bukVuhBv&Y`X_U=*0Ej?iD6$wis(6VO6qdtr4?_tUcODx%B%MU!q<*~*|XO8_I~guDt(I0W$!ds`=? zJKfM)(a@{1u)k~}rLCC-{D81VmV00)qr-#A?l&OKL)~yyR zhxVqf4{4;PB-z=fNstZ3(PA2{5Zqf6D7SaAz|%zDn@U02a_6}L0FkUaXS%K_A(z~` zlUcJ>a~5!z(ZlfMEu7|qXO@P5NGlEC4PYeR3JNkQS)gs18ODhWSqRGor%6b#LFvS& z$t%q~s3Wh5jF)}UmdU~d{!4`;#gWn5#fxJr|C{~D~t z$#a|DCX%=)?xu8Q&vIx(6*3jOz>Vc&I=bkd_Hh#>*C!r5U?20%X}>3H@-0w^`%txB z>PXEpaI0o?um|t0GX|$&!0a7Xjl22Lu!#B^mu}>|#6Lb0ce2#`@`xWQGIwhu?^&bw zbK#?GkA85rRigZ0g|NG(v`>H^RA|f!qYD8H;5<&EuEGb?R>EWdYvl{@k?X+^C&5Pv z=y6cNcc*yfA6Gf;u-Ycv^uzo9U9j1Q^)7!rjs3y*rXRRWA69FJ2ZEkQp@d?wUuuDsQSJoDSt5S6t$LtH3Q&};GRc~hvO2UBV6p?dr!Xug8FzW{Np0H z-}mHT?uV{&>>g47LL?ZAJhhp zbs%#jL-%_aKQ4mrnwPZODLRfswL4WCz@Zrndbp1JYw4^g=u*k>)}nynt;5-Qe2pt6 zlH`{e9+xc9ZPPG`a_-SWzIE4>e$VaSK9!&?QAl9GwN01z7q1+uHKw6<&V|X|riR?8 zd%d5RZ$|1}@vGA^(YWuU>q>vzw`{2ImsD=1lR@5Ta&90k(6|||cP@iO|8SQpdz4!3 zaTbO4w?5wh1dQp5R8+sK6l|xn)yO7p)8oRxx=Qx3_oPKFu$6>u6D+)O2E$+)&Gfcg zXtj9Vw%np|N*1t=);+?i_EUS<}B*tJy)+HhtfThWz>ou504>nQ^7v7_DDlu?Sx_Bva zxUPB7F=cpXg*1%PXssgEMaywZI`J$WTE7yFMata~gj=3MX%AE>q>X`ckj>;m$Xi5C zn!T&jCFX*pE4)A^c1*kUvckojAy`k?mIx>Sj8zJ1tTY2)#FL-E;9}IGAkYS9EQ}LL zt#d{tzDgC;oL3=&TOb*YHInb^olGAKgtS-l8AX{}8qkXigEVKYN?!K{i`>r?8P~Ew zIZT+w0_)_ymcm%p2%05Jvrduw>3m8F0Srpmn*+M0B|6Nsd`MPrs(fA6TyL2qXC}AGVjePl!5=CMsU?f=bJIE{hCX8k`)5N{Ju$=e(<1eK zK1~xYF<_0mJoqCY2=^0?yCBNi#N`KByR`fD zXXi0mi!COFrrl1q_rFvizRWErOwQv!y#Oz#d4~vnD4+bG7$bTtVfVKKe(3() zQ0^w(H-CqZLwCYaxxw8e{GdANRD8AJz+D5O(X77%ck3G$AihZ~r$a&8@|lUdC9z9Y zh>ry_(f-zIli#jke8>dfEzV+V^8-E&6aL%s1_r?zU?z*#47_hE=5a#NCU-?*K7#I* z1`OMOR_-Ul`hKnF0Y@sAs@JAy1|oM0!rUPIdZJjnt~xoZzKC*@a^H$(LkG`OT`1IT zTAcaNU8oem_U*;|GP%OxJ9 z*wz_%OR&VqrsfG64T2|~5;7^m>B0Tia%D zPM3agpwpAA_aT>V-B1VRl0#vI%8=1!KxYc5hua>`Q1hHgfe%$~T;U=j)mEvmPUlh) z*>~HEYQqHvA?y(?%-jgJMyKms7}g1G)u`{=kVqy5r6BLfc{DjOvm73yPWW5rs&)WN zM`$%9o&>XZ-k7Dp3w461o_Q`ud7LEz=ZwTP@LWpYlmz534OR%+I=RhCUdGK>lFQbwhElf*tUerHNZRZWrGEpwO#{?O1lX_2>MK^DnO>GdnRC0 zI;fvE>jc^~8-qfzELss;V~uJQQESSyjO(%&Z>)GD5|6AgJ7IP&oB4J89>h&sWoYfdJ!F-haWk`&glt^Xoc2)pl3*JaaaF=;5oAU;R= zogJ>%0HYKAQX4+JH>gYxzN^KgK8RWWF$6r%zU7jxTabodBj!pMtWGrsXB0rixNcD+ zp|};CF=vES00TG&jg;WS`K#{VG4HO`X2;NsK*pX&Tz$lKN(?Yqa~6qY3fhib+KhF^ zAZnweDwdl1gO7ft!PkW8s{@L4gxNUo&@lmDSvbOnfiEbHFkM{PvHQIfH z{fWG{V^Cj^(}8z?w=W!Hh`7}cbi2n;FE77kE$}htw+-s)hE;JS8c;tSzIhEarmar( z3y#+->RGd@QcKD3!zi&~ubO}Wqt;W#>$>2dpMKFA!+a2#_P7nWH#5+7!NU8F<0Lv_ zkH5*OzzynyX&&EQ@@I&^4eqjc5zrF4XYUk3#Y4RH+AKvC_0H z6O!hd$^mS@$rT(#1&!v*I<~1rd(OOT;@b_Q!^)7CUsp6Jb(Hl*N+d;Blg=0Mnl(>I zmHg^~&RW|RVUdycA-k;>IdueCJqB4PH^U%)%aCMh;=OIYU30UTh15z7j#Y`3HG?-N zf_1Kwt?vqeQsjCb4*w6j>nW83RkQjriR4;2>sr`(m5JBfS?k2JyKV_7l|6^8-W%-S zzo^7FWrpn}!?`d<=h}l-p8cnu%+=FDg1jXJ{I{^3I5nz{vm%>1`2N=cQaFt~C zz}3A_BLa}M$P#^KS9}@_O}>xjkFOx6nvE(@DX|&|xxIkW-pJsaE@Yj;=PWY+TqHMh z(P^VK)QSshNhex3#*wvH8zJz;Ty{9y;M!EM!~&}YqBRD^qGd39C-SFl6EEt^lSfJ! z?^lULnkRw84K!liqQ>jUy~tc7o}(j;|P#ML^;X*^@W?Cz2t&KMP=+p2->N%pCJXP zZ!eGv9c*?FV=zw+L7`1nW!2KDv`R~qE@&iZmM>cDPs!Zm$h|b&DoMY-aAGRzAwR-Z z#1CBH2f6R_;94AzNaFVsKrRI3rmKHjBV4i&pSh4)Ef~pUgecK2&KbOJNoBmM#9#na zkhK5}F}LS;&o3-(zfGmUvT5(3Ty|fc#3B?0)rr(T>sFVFHVU+{V|1@A8l_MnEQh9I z=?(#vOVAa*-0NMr!XEd=Y_kO2OnHw}-@ZY=Q!~2(LMN5%By4p9DqVJC$2TyLxSlWP z6GASygiSGc_vg=#`-jvIIAFafly4uB&dYc#WF8g7A69y}59&I~)_+`0Jr?;tG&kRe-~O~DneN2l z)OpJv#0ve){e4u+(oJKl4yf)5y8DLJEzz53hYa?djG}&LLr0L?VVxfSp_Zk(001BW zNkl5#Yl=-wR*E!_Qnk3X0Cx=pw08%KZ+ zAE$b6MZsFS>wn|Jezd7@wXxu!KBC*->jQ;H`?~5we@KR=JFoK2yWqohPbtAU2V-oD zp>xJ!_9MJ?SaSlnfUHFUu*6NRfaZa?;<4I<_g7;#fxdx|5#UnN36|N%#3{)^q85)0 z7}ZTZ8+a$zB2hs?;3jV+D>)-cqmZg}aSOS<)apTpx=9;#2BZooss(mT>R(fjSe*u2 z7XKO-MVFIl@fmg6#wy<)N7v$WY9Z1VVFg1ajZiYwVtYgmP}=WYi_9v4u3e2%sc_5( z37pYLCTNt*JbM9PX*AI3B>!Iy_zDXvjF=?Wfz;PoCtRj4Y&7pzqFIV~fvYSv?J+lG zpgFL|14b=YfjypvX$N=J|6a1lsVh9F6c%}|m!?Tl-D};8s$3Cdzyzc$GDu$l?-|1& z8LTm7q$qyfl4Z(Qi~I&U)2Uw0MT>3I9>!rHZA_S!A8o80!q+XfzbnP2P^F8mQ}3)q z&BToJX+X;2U2J}H-lv>36S}U;m{QB6HO3&ugp=m|ivQ3yX=hU{R3I6489#Jdmy3L_ z5EIA{&a=n2w+&?Wm>BU@N~~5T9q-d9p}NWto|>0D#;jjgxv&P)B!1X+jTpSeJP&do zN@+F(>SU)PS2pmPwVhIp)ghf$LW*u}1BN6&^E8h#k<_?|oC~ILXw4_;Y!WFYgeXN$ ze@&*rOK#kFypKe=LnHQ8AzZZ&CcJ7^N z`$mO08Hf@E1jNjpBz?~~B_j1E8&ww&oR|BSGlOh_bMCI|P5`FCYXd=IhSnJ2mF!$t zsOU?Gm)$;-+}C&? z4$vNXL62U=V@B=mG?*XH{CK|?++F)S_gZ)Ty&;^O$252l5#HAgYl>~X9c+J$gGskn zt`2IkkmeBG+e#1jaaREKssIs3n1dAeBPR5t#=(yy(Fb2!Qw7||A@z`=-1r2QcyE8c zQ2Je* zJALEZ7m|I#j6xJc(1yu&@udl@5g1K@|HTacv9bT?*O0x53`H$gCDCvkz{1 z8F^Ks)oavWD}g8rAk|i79RqLc7619uFTnr%e`<{>_N!;(@YixdNELjlDhMLIPdb>< zNo1+ej=uQqo3h%3srSace3=oqj1VM~)jE$HCD_;y0K1c6N&T=nWc3B_le@w-0*M-x zn6f}~=Cc84klfQ)aC#bc<2mo1QTa$R1*u(wO8l&ZUr`7b&4M);G@>O0$x}5}3L_?f zY6dYFWQ`VXz_O&~VXcuD%;LAp8lw_YMqDB?i`4E#OOwP7gOh1zR^c)cmTPL!Kn*6P%PSWpa#v|&ExL(dHH@qlQ}gVxg^ZZw znpU5s(M=;Zi7;zntPghe=Gxr?AJcB`HwKNoohwL3smt4zF?a(vID{>CLEYTjMD~aZ zl6a~$WX;2sgZ(^v45KsxLdXCC^XTBbQmrB6>8#nlrOJJ~ht@iSWsLwc+~6=+1Eapo z_sfRUJP7DOAw1Ji)E59;)(8XECd01Ls!aiq$V_GZ6&P#a9kqhP7=x6QsBEPnR6WD> zo&rYc(x!pK>fuZzzSPL!ITvk|NCPR?!#POS;LQu86m6VP4=*t7J{SuOe!>=nJp6Qa zIL{Mm)MGA;Pv>#BDt7eE`hp2cbN0^SeDX+1e5zpJG>=%cNOjiA^N1ag#Z-gc#kX1S zTRlh?$l5j~OH`AQNom%c)2Gv@hc}h1lhALi)lJig#GR<@f}$$>WT#$k)1I-EbMv3i zqfDQ-EwyHywWf7xNhLX@NQ_g1kEtitS&M0KxGqu%4ug}!XHE_LF^H5(!12<}WeF>HT4MFn0?j^8>C=Ee~(x87a7;BMo zlA_;;45`Z9SuGj4$e!;VHCRxMDH=5*4L1b=Lx76JgqLTJ_p1=njS;XJ>C3#ZZHJSR z`Ov}54CI^|*sYWbmC--f*gb2N!dv(-U0{}IHAhC)9njoDJ=jEy60(Ekrho1sL)@CH z)IcOS$d6IqR}W;0_2AJ;qZ?@`YeBsORXQX|?Y^^6;EzDn{hKr@D>1xt;sxFUwv==t z{=vlJW)O5Yr8~%oA5|lK%=o3pBgD?was1PRM3xRy2EO^m)HSDO?=d-tQW$FrSaaOf z5Og2!)c+oIGpCJ7z;L46cs!pJ`$xhO1C|i9VJCo>$$8mJ)Vqie%Ps!U$NXqo$}Ekk zm@*RY?;}GJC*qwu60B>}rewUYE50sQSR%a4GoI&pKZyT(i>HoiE6-Tbz372gvc5h# z_0ln;mwvKr=A#j|r;^{A*gq_O`C$>v-?0+^e0@px<0tyyXaD4cpMLOPDk=V_B9VSZ zv3}6`-e_eXfZq;7`Kmf$gV76pK4i(SH{P@}GWz%&>TD16Xy!^f+%XNZR6mj>bI8P| z2fhLRXk2?UxoyR5i-YQ}Y3H}?l?nWvzCZ=;wVH#EW0C#$ zk~XBAVKmOs83!gE^i&2>2gb}T@#Xk1-zuyK1oyB1ag_z-RIbVbUuo1!mG_oXnnzM- zu$t}K>`x3*sEr*B!kTb-4_FtS(i-W)r^2nXZAciZp{2t>&TCC3}-{Nazs;M+wD-){g5@o76I*dlZRo2PmaoZBS zGj|>3q`umm8RwUwU358=WevBM8?ao17HLUhmIi~Dr%9R+X^$PLWK3p;YZ;bklb00L z!&m@gWhw_FMRnHrAM1=lcO>5vX)myw1i1HI8M(j4il-JToi2styrtK%l->VKEECHZ z3v4KDBH5xxYP1LK9?WHlc)x^3_5}f3%vg0o=bg}>6El`2ioCusoY!nisjeGUnzR$f z#MlmiIvbZe)74GAI2B+aXz z7wUH|41_HRSjsz@xNEUdzkj9o_Wv`MTB9eI4DTh}{jzN0ztueI`ohVE57HZ>-t)|i zvStBUbu!9Q$?X|1RIr7Dm;ypB;LIo}a$vKBukWj>J$5O7?FQDoS(%U*tedQPk;nq0 z=DuvFQLq_2+YgJ`hQ6FGC})tbpS(Wms#ltRks)nuS|RL5>BbB$=$% z{OeluH06pk9%70}DZ@D{#AIjH2Mv6CTQLq^_yI6GysbU!1@kz2xhzowbd@P*o`@5JG@=jCg1KY1huQ_jYMYE zpCx|k(tvW$-nGnP(i;9{&6*^+ku|+^71kQ`)?jcQI&0uVz-`;GhEN7R6*y;fh#M2_zV__Y0m!s% z`1N*$HG~%-w^dcs*Y*_L3>gJ2m*x8TLmgEA+YX zc%LS`&67A=S1~%DPAB~I{4Bxp)$fTh;=Zi-<@%26y5iGmgf&*PCFo;hk3VK@CbVhU zHr!T8JUI6~#;(h(lgJVGWr61aOBScT7jvZOv20!)@FjLCoA3QV{d>3=jV<^OsM4f|2k#rtVi>%^a#Fow|E4x{UF zT_?~g>Szqf}n3!#_-##y*dsB*582J>|8a>GSc_OEp&(}tb1Sf+@1 zTClA#pP+Sd&`NqLM+9({&B{6Q#C8VO5zZHh{g`em-oLGCmQ$OHP|M<=I^PNzb<3ZV z)MZzH|Lf-yj3q2f#JAt?n5Qkrp%_aNuVbplniIk+aPTq(HR39UQpO45X*+}0*AZS1 z;SdbKzdTpGX#rZNc`c&rfd*$`SojW2Tt^u|)IGuVJ zC$7fJ762GH3(QLZ96;8QsUubWhiF7U>5cGi6RRss?Nbo*C>p7w%D`rHmo?SQ#QP zU_!~EHL4@NFRK>&g1Amwz|cF@Q)>oxZe$FaW1X@Cmo}c7-)w;UtPBNXGny%M#mESVI1L>_tR}(Q{3g^nT}!)__@qA5Pt`O~jntUAIYdb{zGLU| zbL~UWJs9Y03cRn8dlnf@6a@F&cL*F2qc9@8<}WX+m>3D7oOEcEi;<|Ptj!uGs;H~T zY$T#K#wJD4#R~na%|ua|y);D=12}|Vl;NYzuOSZpjqONfc*Q%4G}XGWNQlHG12+{k zIoDBsXXhL)!+=ZIWApQ$(}dCk!iBWiKJ4B&RU^I z65+mWSiHv;<5AZ~KyM62MW!ad342qnzQtcd%(;J>ty6_YoTVmdKs9^69D z5!1FJGUH_!@a25Lsp~%sX8Hk8+Pu}sjQhIc?{D8>4Dj>wC-knS zaO!(u#)S>jwjpxF&~+GHC#HmdI!34}p6l2B&@KGWH^PmfT&06;bq4&;H^Gly|50|^ zkDQ@*npJBIM%UrGOql(KOOT;(T_0xoQrkfvFHe(nQaM3u(-yfpmuRQirP8o*Lx@g$ z47FNX)yfjp=7mS6wOq1&*VK;tQq7^BI*x3vGe^_mH(rDxNQ4dsSHI%A%ozJY zB%VQl!vHRI2BjD)pCBb=rd1vsniU`OeTZ;h|8_wH3z{`pL3TTt`U`r9Om{ZMz*seB z%C5bY)wP<=Y-^CXi>D6L)Z_ZLf)xcOjrahStX|t0%^;T7>w0OlrQsI3o6%x3MH|`9 z;QiZ-WrA9*QCgkXynxc`XZ^Ri)} zA2qUCi$Tq~y+@B_^;njmSz|(h-?W)Xd69`~oE})o39cfxbO4j4YgQY?ghn}Qak=y` z1YECcZmvbwF34kI4W!SlGRM3rCYyieh@p2_gU9v0;OnQeupa>Y=Ra<+c9TO!2Td`@ zi&3QsM}xE^LmkrWw&)rCU_l5vc#y$DY7Ev8OAfIqOWbpDt#*#olt9qNY|IcD{ovFT zPNThyfn_6cJc=}v5yxX>L^$T%eA_&pE~98o*G=w|F?pZq64S(%Xs@IfPc+1s^O4Iv zgrbH*txfmP;5=CLy%mS>;1#_rO(d%P?5OW$gV2=)Z5B%MG;NqS(W-R46(pIUF3sxd zZ4H(!=KaAqn~ei|OPOgBVo<@4FfoSSV1NvTpw89c+7VZtV`f?BLMM(n$0iFyn3waWnop$vxNX3 z8BUuhqmgW+g)wJjf*TjfL~-Uv(l~G)teTV|new2(nd#6Jh*YN0hG}m#((IeauCFcq ztz<;X;OHSjR?&tH5l(%N*Yg>(_gd)lhm6QQn|W{i%zMuA!OQoNBQ_rBp^um!HFC)^ zHV~?>YX9_}F5V-uxa&V^0+ZH#bPiumXY|fu-aOXeGwb5EZJ50;>#kJl4>g581l-mI zcfVq^4nJL<@HCzPVoa+b$k%1TZC>y+4sgy@mx{96dVNK5g2lc&(L>*>iP`(Iz#78W z^97f_*CC^<{YQ?t&KSI$PV&7*&~j_c|38;$F2nIDY_QQ}m;Dqq_&zWAzRZZk_-tIJ zTl*04zAlJhdfEi$sfG|K1``TXyj<;auu<9kAGaRnEZx+xjaiIy(O&PO(EJb z1|A`~NlI8rSYVo_@nO%;J8d$!y$8FWFjUui1w}V$RQrJmdpp?BSF2p2xpIzg1|X$_ zY`j)*K#5D8k4j!tU%#Yb_90|Y3oP~E$@R(_5vHdlgPRX{pC^2MdI6Eidp9ei(&*qK z>b{VDO)FYejaHlN>P>L}@qhn>xJhAPj1+gvv^6f2%obdE20CAIbKEe z)-avL>(?{R&wVy1GFEA`y5Sp}XmF`vfW};y63wJ2k3l4mM%P zG}?^@!_c806=}1Az*;9TJnOU~8QV?^$m06d*L^qs+d@>)dh+y*P= z#D0S4G=eHw-Up?aKAjxCd_Lp-E|lO!Y0;KU9*}gM#T2G0H&64C69TD?)_vMAE%Ls; zyqs`3_n6lW_j$v0+N2m(>bf%~?;TJCJ1Rw{c0Vp=QiRLcVf6v;w?*U=ol&DGrQ~yQ z=_ZK(M&Jl|iB?ImT+1q-(-7e_bm%*W@3#eJ_1SpJC<>eC+}<#p?t3vt%qwGAH(V7B z9y*In-IT{(h6zED*064kqqjA}0!ae_BEme2w4fs^O{kj2bg;$9d|{$K4gB<(fIlBJwG%f`Y3XS_hsv`Q>K(~P)TX@{t|#K2CPAt z0jaQj6eVVYw0A~!&eTUc68GYv-nviWVVD%mx zf%Dkoc|74X3~Hh!x__q#b$#Y0L$}p$Sp1g#O^lWA6)LxF3v8CUCq#Mw5sW9-=Aw8?2v9C00ZTa70&ue{%3kSK5DUP2VpVG=lee2;W^^D>+v!U>RVE`a+I%0 zTb6K>c0dnl&^xVdg9_6J5H>Z}WeFZr@bEEY7DrSH{1i5Ps%iB9t!Szp64z*#skS-) zr<&JRInAh@gMWxN{~1m2VU9&RV}L*LKDA$P*5Roe@vGl(TW9pnp|@Q%q&HPG&%shT zvxgWstg)o;a@7%7dpSf&pvAVe*rg({>yp^3x?GwEi{#l1i{rHWM9 z!-wmO*2uJ7?r}FH5RIt8k-mM*J3cPS)6|Te;L-eynDHM z-m>M#x=NGVbw+e~ft$FTTo`bP+ej!3>EAQ8Q4g(t=%i3{8XaN{mU+wFcyeZr%wXTz zrgP3-O>n#X`|VwHC&TFQ{ON>GU(bT7S%ZGCxV_K%da1ECx!I5mRrz!A8-gN`Ty0D~ zFxKmuO_ENR9+!+jOT#9U3~iv*z$S*0sl{B-XDCNKY*MTlBZ|jOb5;N2e|=VCD!^|6 z!{8*7aEb8Vi-_K+9?ueaJlqyEjFLESq%J#2G#-N(BX3F zF%Ax!4|sh&!TX4L2|4;FWfmv;AgM4fJA6{R3Fc{^vAA3uo}W+XI^o4{UR|D9?nel* zWWOo>mJU>-syNi5ukS3LE~6Ah26%lQaGN%L-@6}FQ=sx*xh3mR%-=W;7GFP~@%iy4&o3ZMUhp47*?xYIMENc%8{I9>h;D7wv7o3I} zzka{tmv0m9i_jJqA3k;h<;<|jO%lL3_YVK@muLLt%QK!YCw#w6`0eeE+q7ZvK`GJg zT78&|S}m`y&m;cl-#(%1IzaPkZ_|qFB+Q6)lenaLli!~VA-ZIGr(fofCt&b1;>^ zO=QR*O<4g}l+hwi z(uww6%&;=bXtd$2U7YKJPzR>RxW`g_g$mnD&f$jWF;K;YrD3qw+j22C*&$>}E8iGR zF*WGIUR^}01^)bek$ZE!;l8a150GH&*^iHvO1q|&jvww(+>Dt%UH||f07*naRLX-Z zLG_rly5x>bHt36x;R;fb)(my)L8BlCy_%)XK4OglzfTj^5b#&$a2W?3_%H@*@pKvy zLQr;s3>}@dNBu7sEqcnpHl}zE^v>b+d=i?m5hN_dV)@{4T^4-5-*Mkok>hCg_o?gg zGLCpYpE8pnMmhHcEfIsvdXQ4P&+g?6K6u>M6}M%^;)5`6jKQhzaT!L8U0?Z7e1Nrn z6rJ-?^o}MFT$cs6ZG$C)m+^$L6QsI9y|dqiM~FV7Lt+eQv#yidlQARBwULc5eDKgt zt98_^9Dk`DEVV{C?M0wU-tgz#rT27geqg7LKkWwn5py~>bPasY{~Q|pQ8qB|dC0rh z_<*FID72O&f^G8R#(rOCn8BbkE)Q+84**jGDyKNrB)ub*-ZkY83EG(qDEB(=!y+c2 z4kzPArJLeZO|_>|>r%jF2v(la9L-KF`_DS-DgBU{? z5ZoZ9N!#pkJ`ac*d9p5BHf%CB6^4`%1cCwCk|pXxipa5<7M?x>Rn2)|21TD2eux@1 zVg=Dyw}5T&Sl57L0mNPkx!@yCmmXlo?Y83e^(6W}0OwC9Y-_;SS-dir9^(9Aj&3HYH07bJK#9HEVSk zTsAFwdKvNQ^(^Z;-8ZCM;h0^f4HZK!Yi<7bO*4Pf+F@o)^M>=O!>89XUSCge&I0;* zHy?6*RBFgwC(Sw-SXQqzHO?B%M1PN-F|U$0`{nBeoklH9($b-~aUmr;`|bO!KNHUZUHTj9=EgUmZQECOZvh5}Ms$f}Z^QyGw zAjp$ye!I?~w^rT93765qTEeV3#^+OqZ`TRyD#N*-K40+p(*?u~&kVnMyiW_>ZVTqs z#};{AO>1moNLjA>bMn>MUIVD%9`KGN8! zDVVkR%cnD5p3XAVau%PT&-m%nvzYEMV_m(Pr-_U4+ik}0?=x=8CNBC_ZIPO$^U%Q> zi`%?{7`U7V7(@8iuP+TlCsdl_5E<|H1=G6a{Nia%t7QMA&Ni2CeFHRh@{)-N$^yee>vz!QZ|<;eY(~3zpSm*3ZC78C^GNhSSfkdPc2L9I}0)ma2Ik0eBf4 z{_X2CK0lqXdXM|E;oEIS)ID$B7(R-#bt=fxps>xM?5{7IBHS|5YuFFG}f~L|BDZnk|FI^9Vq3hARa&OZBXj(V?dVRi`nT z$#2sg^R|JB@tL0B)J69?Pl8@6ZQV!4;yu3KCoJ2hC>UYvx{N|^!Q=b=j$iLrrMj2v z?&cXU!-}s@Pq++Yj(hvlT^HfQ$fn4^7CpYtcWfczsqgSQjFp>qDaK`(;dL0WVGSP6 z7+i({FXLHa*K!jqNcOx*BQ1-3tzm*~m5X z3p;5AwT4c|q@Ep9mfC?gSH?-Z|4c^wBo64(kC^?2>pG*iQWK>?YW7>8N5gvu4=vcv7;DKQsA41~A2vY0m^U42trn7?Ph2x~6sU$T1|T5Uwb@F_ze#M1RZl0d?^D;rNTh z9gb8ZXsSib#CSJTJ_u{-E3*_Y!ze~H)7@iPyk`5RFz!UBr3V@Y`HSnQWvLt(8Zpz+ zV2XQcDpokumY!%Q_(~U8)>$440)GAVo=vlEw+)?Un%+PFmI`ewhgH7O5Do0+1z`#y z3PMdq_iU2+*BZe0O`9*(IxW5uy)zhIb+JZ~;-s0B!b_Pex-O(J_GoFj>9tdyVm*(Q z`w+4g!djz-HJm@=Y3%c$A;nLn3}k8G3DyGRXj_8;bpmG?bSd|Yk>gRofa{!U7|UdF z9Q%SgP;J#h=-jama!%HC%A~gXz!IboNoYR@aru3IaxjKr$cpgZx~zGvE##B#v`Y=9 zx+@Qz%Q03GY0|5wvc}!$=LlXx=t%DCmBF3YPQumhkyIar+kg?^D1t~xoL|^{g;=xoCihO zWb*fRTX8=1csdX0a-&)f2-kJ^I(GQ^r&o{>9d>$n({=PQWckGNWx%pUY}*23E#}q3 z#)$DWfDCZCobnJZMj0wh^O}c5At>QSr{c*ZYhJ~$Yb1lHdTU1p##oG_!!Q&L(C3#Y z{QJ*>x~`iv#+x?UP-D5F6NKt=>hcija_X`AklmMMuhwi&)tdjuU%zCp0H;%Q)v#V_Bl=$(^>!WbAt zkm)3z&3Fubhu3ErVl6(vYaVS35sUYjbTBY<4wtdVFW+u3&ZsWfYCHmPISV?}Yt&L; zHSpN!qBuq@n;2z0od=x9p^0+Ze83hHn?Beoz-3bPp86`Cej z6y1uNhtlwt=UPz>buP_lVk+<==}-|FWzVQ|EazV;aw12ZI%(kUI; z2$#OY=hF$FE*JD&hY)4G#VCW0&S^YbWu}*lN8|oUNba!)P;#(aC@nI>M{!EO^gW*X z0p~%CBk${qU+-7k{DN`raM7l5^&Y?7Zn$qk)m>TE>uuZM<5dSS(wKcYUoiAN2!wfE z@sHa(-q%$esrv!eO2+p4y5QIQEz`A!uKQ!O@F9v{zL1*Pd0TN`7hIPW#t>eH6UKfJ zIa>{Q5fB68zHPX!D>kKf`xx=I&e%f0U(5@P>&56XGOo*vf84HkUuH2r)L&)|;WGF5 za=GC1>5}!s^0_BRXSo>3a;WgXe0kb2ZS)j@tp>?l(++!yIHNeCc|8I$iKI z3_1*#edD+L4cBGH;5s}H1J)QYZ7V{I=&Z%_Fybwyv0!4sZ0VJa@es zu$iW56Enbw#jjX3=1UkYLZ2TxSI%TjvY59Gfg^e=rh|c_#>7Br`oi#o?oY`Uc_>ItdAsnACa z7j19#ZxJEqe{nUf$;Kf1@I=XV$>>N{s46X}AQ9BKlWnWlIGUh(;*`-!V6&5vM=A2{_BoehFT6MvjVfvmXruR8Dgh*MRl{+g4SmuzMd_wDJ zmxcr%z1K`iHI*l6?T%_>SW;LW^}|FZ%4SC=N}nPoWUiWYrLWTtK%kv&pv@ViJ$baP z!w9XOqMops9o>mqnqg)=90a`F7*MgISE4%&IYK4n?OGN4GogwkN+w!p3?ZoWlA801 zJQ+*5s%*pv%vX~(lQfnnPQJ-~7`q`6RgFR!j#8!HD8+SpUJL=`@;0moYfV*@A&XT4 zrv>F0ALmS)AD1+mry)n=M8?4>`29A)T7z-yKwQsFF=AU8Q;d!FIt>v{Lx^E?hk62cNGWR$WB)woL2Ew}wLjv&Li_()lwn@TKxeY*== zLZhmT5ubun-(l<>K)^Z2p%D76!{!&f-)003_SjG}8+eW4E^pK;LNkT?PU44J$`+=<-H*dN}?8Rfq~zz zvow=Ya#4fQo3jyGzJ>p$JKjf33ze#k5MEL>V{2*+5u^`&qpp%4qQ#MXYUKLP-RN8(j7-Yg z#B5X)pxVofF&MiZU!J8IzD_gVrWyCmtMh*wZ&7;=9qKpw$WW&?AllRYTBM*ZtGIH0 zO-(R8K!4Kjm#k32x$E%te8KDajPuxQo}|Us=Vy$4k1dD;`_loHY%)=PY(_*oYA6^- zhqU9xF$9cVkDs2NKm-h~*9>jh2WG$F?l-J{lg#i=%py;n_@}JFfHy{@081n^L7RTJ zW}@nzHf5lL71c2~5jOd+( z57IDyTV@1~7;J|prAcny^hGu9Y)&bejs5+Fl{CCJclivhFu zxNa-95a7%w8D8i!ds`R$W4UAW4wK)c@r)j82p}R{*BO6_jMwv7bm5UPZ3}+8-|&5z zFb9vIO;%?NE`5(Lr;9jb8=Jj8*5KtjEEDctWJLsEuntdsk5A(TmvPL_)ywAbkJ~%$ ze#Pr>f-?rwwqXtedYxU5*YPaqxshao@ADn+%PcIp^ApbfkQ*3|5%+CT2W{5Ieh@_Q zgL#S70l*dnz3;4rp;4HI;I?_G^}0mh2wc|%*KHOiPy$f^+_n|hbwaOh?M1)K``x6{ zu@+WOVonEU*vJ(VzNDR`L50zMx*+z-c1$#9BMqR_nrMxC`hB(1nEv45(sLg*i-r!{ zX+&Wp5o(?>@4ObO-dpRT5;aUnHesfYzZw&6s7FU3?yH_7CtDqqFQ?N%aV_;CY#(Cnp*M{4O3532;F(Di&UHh7r14G5 zy)@XZ8A4iLKE&eEj51w>YS~duSKF-U>UVk0d6Vfnr3U}^w@J}X3+Id$g2nLQfB)w< zIht&0K1_awQbuY*guF&T`9yP$oau7svBTvwVCY=VMkgYK5c2dpH@1fI1aw(8L^TQI zYOgeE*2Rj%Iz>6Hx>_fuOmb-klC*(}9#kpS>}A%RKA$lL;~+ox=|w1%&QgxKy3Y%) zw+X+!-}8ErtYyY&=yAQzxXo+6#`ETJUpMrff>?A0g>_{aCVAlS}{^S?1LBvC=u8k`0(NhB7x_7nkCT;Cl!VxTt=3yhM-oY^+9 zGU)OFoA>Y;U7?I>rWl80h!i271jj z8oM5+zQ@}<7|3ua`5%q08ntMzx5&>qLfPs(}YS&?^7Pn22|pypOb#yI({kXd64xvQOKJX_J5V zF>0Y)t&-*Y`#j-!81VDcGfsV9v2u)hbqJj}M#j*|Yf4=u43Qx> zl`^~mAIP*nMuBtAb-4x`N4zZyuImb~bk)up^v>a^7s?!pU`b8b>&55kf#eS^WKW6&A#{;N15TMHxJ9>k1zkpN0Yda(Tw+ zI`|myZN3A`^7sZT*uxz0)SodriCp`Ay5sj{#w40?!{yp7#E2CEqh(x%0nZq5 z-&Xv-%=mqo^DskD*nn@-9shNI#~K4JuE(fGm)CX0uhU)f(2c?Kc!tq<#qZM{|9N}E z)vxHtqPM~jy80D2zha3IeL z6Y%>oAw))}$a3xxg6=XttljnzMx>d&Q3v3r0&w;|RLo^h zGen4qQmjpuoTMgLl#3F+@u zG0ywxC6^|-9uL(T?6^Y^xk}*3?&RR3L`cuPq>;{kPZ^kzJ*+ntTMQVj!^|RQo1@2l zU2qvrr2!_YA_zIB|CrW>w?Ji0u9K`6ml{n}7UjlNQwu%@8B?V#C;;ZJ;owSNz=FII z7YAI<&Y1hTMTzSH1FS`C6aK9?!EhCCT{RHv8mj|N`T5Bt!H}+vHVdrioVh{#=%7QV z-83QDj~^X6AB~R*$fiBC&rc+@dh1L>C@!|72-8tMP+E4Y6j=FL)8ua%mIjs4BBZqy zI@QP_+V4$fQM}WVpqk(iVx>WCuU|Its3;go%9gL?10;h<-S>#3u%{9`B2z2c<&>e) zXmIP@K-oy|V1B{Xx~~JlGb+tM-`kA3=38wp{l_<-Ssl7v` z)s!a}yq>`(W@!Mm28xX?BC3vsD@46e^pEB`b?CcM1De_yn)e!x~tkOGMdw^=pGzkf*joQ zD%8@z($pt2DQl>?CfP7UU&K0ba}Oa(6Oo>WngszAdB|=r;B0_YxiC^{a-`@FTu@{MhDxpk?cK+=;Ap}8We2DmT8E{`VY$4)(T9jsa%X{W& zaM^twpnUzJQp7rIvze5{N(n{V8KW8`qo!!c`O3+-Dm`zp+Ez^EBAsO{&;do$2-1pUEUA>?^k(;4TXARCnkFj@f&Z1Xir0h@{fmLbfkz;N~ z)0%Kbj2MT`VH~=wom#zb88?;2o(Y)d^{}tj|NVNsD}pA?(;7qr|MK+oao4sN@>WAX zYb{P{{xtSIo|XmQ?-SmZ1#1Wob9H%di1pBEg+4a?*+q-yeDG%Ud~^v+*Vm}g)!21- z9tT0A#!-eg?$tdV;n z1B=g>r_AQ-)a~AYaOws)x?xoxgrwfzLcqomjtpLh6JExXybecrj`%juSX4ejJ0mqi z^4>n5E*QE#`*%!!!xAE9FNm$>*1?#F@OeDp<#dA8?~h>oI!}0Cg|<9Iht+$$FEi#4 z@Y)af`69!N$c*pvgumb3@qJzKvMe}t13C*qf9E^i*A=64_{;f%bEiMEO!#HGVY-vcZ z>mn}M{h)D%<}seB3{Rs}pp)xyaVNa3cih$qowG31t75ofp{brUaK!94EI~~A_(3lA z61=j5SbLVju*x7vlOV9`(zVeJMo`2^nUk79jcl%DissjV5ZNLmxmX%{a>YWTBZZWd z5fo!=3!noqQ-LRmIl~E|ZMQ3^6w7E`z{;>Sd1K|O`F-H0z5|<>j8cTTHI1ukV^f2d zH6y3VQ|4oY-oB5d%B2|$X8=bQy>Zw$VpN2FAd4k>+}0U?B~!h3ZV>GDysJ--T`XVq z`5mXqF>$%m@POKBEbk~Rhc{6vyQSU0Pk=U$$l&wvf0zX znE=PTiR%%`V4d`H#ed9`T|-$mz*Tfb%3X8RWr{N;eYg&y5>tXra^vHc7g|m7tTk|^ zzD%7wfd+LF;OzRm8}sX<2tX}kVpLM=V#2T z$EkOSA>uY~Vi;$Q{0X4igc2BSVEt*W)twxC|!@ zU1jX{U;s^f>U29^{kYNlArgFK6)}tceDM;4*1HZ)hc4f4cl_gag*66GeV_HJqWhn* z#c=4sGeB_!#y=Qzl!{Yg$zmDy>NES)b?B`_XDmRb7$hAG$@qg=N*jgJTZ=E}GoHo~ z*LlYJ{vB)dc%LRQBCE`4keWs&)dm-exNU~!#gr}W4~t(hZ$9gH8PXWeA(S(MlYA~k ztRVXnNHZqdXM1pgZk=(3qV6=xO3>TRI4p6MVO?avmTrV#&>Mqu-)GlrV+>yU5wi}w z5}RZ70c(`%<+&g5G>G#zYt8buO!&4gn8OCo5f)Y=-GVS$UWWmn#tX*2SGJACecSNe z&zRN~eh4ybtJp__b@)7_v>b=0bp$sF! z$aM@Jfg_9>;8BuleLzo1C@53Wx5_?bvZl4k=LTVyvQ$7Crd$K1XD!t1OWMf&gCW5_!Z?Z=}=wh+KI;B>d-GPnDt?p z=zdwp$_4T^uILrHvaut5g8Gw8?3n?Yg9yh^=+>srIHsdSiHMGiqdt6WrCZJun~G^p znTXI#AxxrRCewj5DX#N_Dip3wlqk-5iNeK&pCQF@;2dRCvNX6%>9oK~n$jj?yF%zv z;g%ZBx^xf??MDwV9jV0!+x%i+(uQd3^~*ZRDyksUZ%fR>11VW~o#e<@KNiF-RU41% z9%RIns(}_)f#da>tf;A^+F~}?LWD^NePS<|V&D+-RE`@vnA9v-TRmGy`NC$^$oPtx69IGCp(8$yJ5%|ZMG9@)x?`{Mk zHbu)!CZ;LS;{7V#HAG`FjKpMGxN^c5?nbTL@)4 zq_Q5<^OGp=iDI@_oviBGPJ8wSmC1U&o@(7`WMFh%$)c<#)M;p$2N0E!N;4rRL8h~T zBgvEUctI#RNu>^Th3mER=PA(5)TXO2TnC*)*93y(O5F82F97 z!?bR=uNx*WuG}H}ygnuumW^{~Y|%8{RO$6kgUTBQ3B@cn+r+dN6r@qB?b1D3GieVMU@h@Zw0 zKRrKVa2)_(@nYC^+qRv_GXFVE?haN@Mf=Hi`1$!+bi~PjB(h2;l%h=gyT^xT_TlSu zHLxcGXB`IDgGRz-p73p%@wUwPv|R*2d?+KP$DU%;nupEXieKK}@wSTA*Hn4f(sV>M zjH)$LHlpg(C$_b*YQchY!UW>d_BcLB`=WCb&4LA7_H9O9Oah$ zKSaaJIfX@>{>3q$h%i_uat$ry$4+Dnyb-n-3Pn|Kx*_0X9mYJPTaXplhMXmpBiErsHgop?imxbcjS6Fo&%^Ea0RYoEKwYqQ8&U8GbVL=VsRE1 zXsXT`6QvHy4pFgny>D1JM@?dOdAnQCN>j!^`5Y z^4omFruy}gN?36)^z|LpbtSi6K#+4SnRK}Z?aA(x=^n-A!*M<7Nk<8{8p7J3h?x&@ z_oxe?RNys+HIjGB2YRM%d29-`Gfz5|CaKN*G6#4YG0z@;i#ggx8gkC=ZB*Gx7Nl1u z_Q@t6v`0FM`)$cZ-t~P{ivL!u&5sAqJFDruq#tCoiNm360QG>uh)^cGoYkm!O_!i5 z{JU;UX|J1LYH>v>mhf3#@B!5=h6;wE*tlskiBZy?LdVr*NG^pfIj)WhKx2MZTtQ7^ z-A%Re3-7cYb@RV7XyN+aRwhbBwFaa9Ogbcr;)!WB5-in#N#Dtdqsfm4fnvTuVh~gS z{fY{U%#5gKG^H3XR2M!bwQEsXp_Vl;PxarcTt3r2!FA)^)-a`$4z=9`nS)Fr=N0Ga zdCBHu5S(+_y*jy!re@L_Q%tx}XzT`*r5N?#09CUpMTQ``b>m#;Qziu0n$}!`8q`BN z^W-EMQ&+;$z#BvLQ=HNLfS3n?<$0-}d->1D8i%Pib6q#WAfSvyvbv!<1ArX74_W7J z^k57zqE~0>bqhFY|Aq)YR1-_uLw3{*C}JHo0zQ}wS{Q;ys9pS1N7TO&He zv2z%^PH5`Ga#h8&%853T2-kJPxBHYG)~9vFZ?_w+ z>w?dxb8#4sWxyFYmIx2k$#0bepuHjwf@kSt0LhS`YDNoCr!;Y+#xk4t*aEcJHx(0w z{;0d&T`I0n{1U^`JBQctg!gU5?`=;{;r z!@$9e^Dv}mu29MXyd=bn_>hRLU$64g(?HIs1ZbCSMkFZq#dWy*4e#rWm+b^1F`kOd zcOsVe>(bp#)Hv?VU+1J;Yw zij)~8B2O&>DsJ}FozF^Safh!!0NSzQI3vh4h?%s^JJK-o;pvDCjl8OjDi$m>s;|gF zSl_vURS8!liD`8V_*m-f=i#JjM6s&t2zS52WZ7Un?Z5|y45%2nv}4uC&%1ivAl5f$ z-}Q5oIhNBstj}V9AG|l4UsoUnlG3Ctw6)M_h&7o~l=|Lu2^_E)%x0+Jv6ph%l z&TY-cN(C)rDH@}xvL(47?=_{B)ot#e7a%_7k@F*jn_Bm|G=WOUV3iZv=(!=tnlW*P>e z&}uzoNSa>55^+GG9JkIItbW7TcZeZiUIO|~&=VgcJ`bZhTMN^}s+QI2AR|3LMYr6- zZB*Rgvi`h&h7Cg-x6=F)8Q3avBBr64^2K@ANoJaB9m$sOb$n0)?($-@cDtpKCuEl& zNXF8jE5(IqXh3%1Pt$&`_hl+;*su%*44Et;Noz20%dT>nzL;p8l|v_hv2$<+SOaGV z?2Ib^K*Y+GvC46pc$Bn|AvkjCnBC(t3eA4mHf%vmy|xhHV}OsLIB^4-SsStUeW_C) z2*qH}l1%11YtgI0okY7>p%FV7%p`Lw`jdKH-YK10JaA8oe&kfWKL+G^csYs5(fd5( z*V_&Eb%7zm>;q=Mi8Jpwpm$E_<<4SsJ+AA9_hkX`8wMvPN?u)t<3a1Zcjc-#;A3x0 zBu&=Xbr`I}90Gp3-4K|w0n%+1UH7Hy@a6d#9b6f1S3t&(f+}g>g;$a?M;1@Rfb-qq zx~+JdXS|-yIGIP0H}r=HH$xEMC3ajg_pwx5?q%Gt=le_n;rq6lm9^*C6%YSQI? zMrv5}t#kd6CQWI7SwFnH50Mb}YOUQGAwsJ`;9ak8snEHBocS0vzwMAY)!bgr z_mV3Y^LpTr2QtjN4SolDP_fC6)xjL=#0^vF=NS#XobfeWhkTJ ztwBROAy4RVJ6z%2#Dj}2CJn-J?_%`*7fmzrM%d0|>Vh;GnYB|;&dq-FxogycYj!}T z*C#$mfcQX&qqfOR-Tbf}hwY!D_2H_L064I7JSeV;Q-jn-6i*VZ|Ik~kKHxO;7(0h$ z3s^%y@1#%&A;LL5Oe=kGJveL_vDF~0-akT~zQ&3&E^8ykHZIJP(Y!@2_-WSPQlpME-cQFB~B1TA!ov8ZU&R94y z*%94Ym-XmQb(<-3wWjg?Vb_Zt1$uWp*UDpV?WsTRqAP^@+zlf@U|>)+ltH z@~$*h`s{JcTIyPF-Aq;wj|e}MqZ+b!>PK8$hi~g5u~971PEW4Ksq0m{OI3kZ2G~T8 zN*U@k5QQ zrA3UE-q0{*cz#j4KQcvo>XCBJEvM`$$9RFF1M)d<&( z>QTIWpfhqAwj_hJM0es!gUpBdR?)!Mw49cfd7PBThSHQSGyR|hrHq!g zBn5h)>-#YY^wD=@7kfI!zBGy$u2>pv4_emO1Lhn)qC)MKec6l%AD)wsK#SeplU_Le z)4Bzm`wrvK!TX4pi^XkO3$3_{9I}S6Y-OUJOl2$)#$GhUvu4HSZ1t+hqJy@hI7ZTs zP@gKfufpvsCP~Xi;@x>x=V-S>$;P03#|n@F$5c{>`W-YT%=Dmq?tuApRsZ_wgg&7RI}C^T7%A<0hDd2K{_Gq3;})P0;XU zOgK1)q3h7)7$Bh~Kc7yp)?!*0G5Q3=oxJO?g@8^G@Xp#su$c6vr>@7RG2*NyOLdJ? zmubE0@b&2ly|ws0PvTmvj>wnsgy&(z^Ehfm5~|oh`ZME&%!ZL+6tY|-)i=&sJdY=Q zn2J0lou+9M9)nGN7s5xJE|4&T#104HnE>=g4Y@qyeO+*#Cxkxg&rFy$pEH5YE?c)44gO$Y#B~EKJ_wg?z}vdw+d5<8 z0FJ;*KZyAi?Tk*VLbH9&_QQJvOAK~KnuC&>dNaf* zF@`aqQX<{9o2*ODRNoV?qQIjr0LQE-XiOVqLFkIk$rSiZqP=6O= zSMu&OXemZW27@i?X=)#s+9Gu~4Cd;5iHX6K@__jfGR66H-1|Z`_1*(&4$%iEe~Q3O ze-dYg#L0B3_ZNPLBLfd{mY3cyHpqO8iRD=6TdDD~ zCO6rD2?gOyRZNb>)a0ESLkK>iQW!T*ps~v8Wct{lwnm5BGp%Uuh&F}8y;I?SXx<)FrMyicMx$*;Lt{S8Y>8oO8s(_)=Kq`Wl zqEjMQ)OW+#jkw-n4o%dfbwkCmjucTvh0@m(uy&GO)0(KY-98ea6)mdD_gaItAGXA* zS);Zs+@}*|)VA7;nLjEP>F~~qQGwE1iLxVNz7ExpseG-~#n;HzUKaS+0k&CF6CFZVFu^H>2qWW*hs+E_Kg?Oss73?JO#sX2&f-`^eu)z~vL+aVYo-e2ujPg#p3 znZ-URlDm4`SC6N$$1mS*7@UC*Vk#5#;OSk5@3%R>g`o7M2-bo*sy;`au@qX*%pkj? z5Hju7=Z+38pnni<+k|#RA{6S+PfvI`pJ7RUR~gQ!Z~TMow2*qqFU!It>6YhS)79*az9#8#%*^9$+)5ztkPDg8b;6p=CuFldwn$r*no$K&vJYfziZhphx zZ|@k51q1T=IO0=3;<+F7K1oCWI*hpa1#jyNfN@%S$qwEYOu^&gIz07~kzAjrN8$wi za8LxzdVlUm{60^3^9uqqo?M4ZKcF+NjT`z?!?)rwF4&Mr99~8lV%%0SP1}NpF$S}; zM@q>`ZJ-FHW|Xmn4Yzes_Cc4C;GKS@`uW0JNOCQ<^^{bo$5hj3Q6w-?xkp@S$at?4 zuJ;`Ns1~6Q|GsA@)OFAr0-UiqgFdpnBa=*IBjK?zLY0S0`=M+4*4QN4pk^}uV3@+x zI{8-$^dkuZdxApczY!UZ48Hbf{O58L76eHHyu^Tc+u*FrEZSs3287}OZzvA~V)G}O zKfwf7CapM77pPtk6-Bafa7MAB&H`>yF}pg@8B^SR3;I5rp{>SA0^zWInA#7F$1QZ$gEoe5AvY7MZXYk*8s&6V8HcIw*dT|X!` z>i8p)Qt8+{>`jZR0PXz(uADfT45;GL675iUD$Qw4OKs@yX&4f#@M$W|EbIG@`<|i6 z{oL)QTq@tKV_cJ~w`yHJE+O zhIF#$%6>CAaR%lcz1-xggwTY%H*}js%`Zd48sRs*(`F?KAfwWn@1oDBdiE+Re0?8l zGZRy>mm0%=dNBBCase}o+Ew~S;iw#x@abKX_h{D&{Y$93}~g!Yd*emc%vuoFW3pl<(RLqkL| z{P~CpQhiiQNfX+3=Z0CH#ScJdV+e!k@Njy>d>AGr-LbQ#@e-g$gLr%)-f?9C^cEF$DbT@d3ptHQAH~7 z?c*n8Fn)M_!SlZ3=k3}D4&R;5`1Ru_d^(?nhXnP3p?4263$}{+(18LTPiK5uPxyJe zA`tLtJ>xN+MZ}V*Ls+xf#(;Q$o4W-fY&ql0+iNjRYlC2#Gom?*n5zLakJwI7 z?>ClCmlG6Y)!-1T$w;C349+Plw!v^|E6ry{j-dpG3zViJ_)u-2bm~1mR|k!<#t!rb zL=>KJjbnPmL!;RqdSZ>v*^FbJ=nEEsW*^PqJdStLVbOCw;`rCx3pf@URk_Y|fgcEF zI;$IWNktYf-{2ck8YI*+zdmq|DeQe8yCy9;C#A4JCO8(fA97{v+f zro#SuQ<{3>>4A_afSPbxwg8Kuc**TlBTCZm0T1A0<@~8yQI>-0v#)8Qe010nWS{edw?} z$Slc1@CIaY(BpAx2Jmmc{RC#l>vh8tNmVO{aV&u@q!;FJ7F_pYB*T(2*fP=uq8fxl=$P8t{Z?!JM>Zf0RtyO`v32%kQfKn4UzBD{ zgT~&5d~6n=H1uOj@tg8f91=b|=ZAogS?w!rX>iw(&Dp(k<`s#;fNl^*AhyJ~R^Xab{#8e=bl!KyY8LHNp1Jml$iK&j*Nl)Mpo=Q{$I- zcJZ_2F$x((=qqgrMXUW-^Kv^nk3#Wnly$1Q>eF(G1?F6u zrVwk?S-@{TeS`1vQ)%Sl@-F)LX<6|7<0m|=Mf;tre!e)LpNi{#45Im<(qKP*gQw+G zGJ6IDpatyICOiNDAOJ~3K~#}qXA`|#uf-MJ=D1qVmip^kDlY0< zPMzIwqNT@E$|)u*%6suP=q8fFoJ4vWS(KNX?}mYZGL1&vvN9o9OVifLGqUYv2alAT zSPkG(9jH)eKa8P+vuBXE&xU%o2Jt>pw>`ULkjVj`ZioUobvef3i*)%;2L(%_#EF2< z=ZC6m9|N+K8Gm_u#vi`_4JZ+q6LKEe3#iX>%B&f=EQZa3X~hgIL1p#U3rX1|0q9-T z<#Y@CZ=i6pTRcyYRX&ZW1$l-8t-)X+s5Vv7Y|Cw?I!mb2)NCd1|70&}1t50N}DkJH;+nQ!cGxi4G zZG#94X%iWi4MWQbrYYSF=I@gOJWOud(9Y8YOWojugIhMJBn+-5zsP~uik(Qi2@6Db zLWBfKtkZo%Kj?<0J_yRSan2RHVAlYX&NhR{PG;r2Z0d56A1X7uRUT#-nr5xPAI--k z{Z*ODn$ZCBIelsmA5_fd5H+3x(s_PrN*S)MHrQAXo{?ca5SGX_^$pVxkP(W6sF%tm#R zsx5-k`??9t)B1^_ptIIApc(Hy2&n~$Cg<^Vn)&v379$~Gjf5pW;OQi`M>0lMa^MgP z3t(L$K4%t9J*tL2^jfFCYJRpYHYl=0lwQ>K1B?O$p;%Me`2g4rYg3qUO z(Q(tv5l~l!JPNrlGzcJY2w=`wW6?Pik;BihUN`!D-5Ge@c6_8fDy_wm!z_HLwB{th|;abip-2tEXE|t7}yL009N$p*5J!L*$hG;sTtJcte$b& zDfz5jI$2PriM((>6`IbH-bsGpWAhUmHBveN6XuOffxnZnnpi_Y`>V`xJc3q34wB~< zJ#rjk*R6~?U>%Vw?y=1k=L41k+xz4gO6b;`L^wOZ0X6_oBYC1b8mfV;Z?8e)8}%l5 zsM5ru(5(sMw)?Fz^#fts5;BUR)%Twtu`CgB-|^|;gczHN754~&oPpC4vDZJ_Qm#Sq zj8;PIDI;(x0QWs(4FQQtUSs)lR&LGSHbyKpr*7w3e=G9{NgIn04kC)gaL_U2;#*Fr ztb>x6gucu0qviBiq1s|i2K9VN>c1!PXJ`IM*upjsi)awjb7*>~0P6+)6 zn<;lguvdN<4}qm-`&I*eFQn&2^C^K`!o;2OKfI));8?=H*7&|*%SlbF)#X$S7}E^XNX5_+L!N542Kd3;a{1SqaiJR0 z6HK(u05J$6)>MIIbg*!bR&O4NbQe0xdsFJpLfuE#B@gh;&??Ps$l|(LAEN?ZX5|u1 zvdNXtt}}5ZGrZyMvaQeIT$3Aek1@+N-5?c%Ko(kEDw)p_gdZ<&y-6m8)T6WBa?N!n zA|wBhNhsBv@aI3hwNZ?&dM#~3I5X>_i)JDbLB;W~pe`zORjvhd21s;|pWau?HG|QVL^=08248Q~z$jXV7FFwagPlB*h6+eJUtRHY*l_)iHC`%zYKZ z8H>m&qpP~Hs2KNthE<;m&InbvsotK3(06zs3@80NfXfX~bhuEch`z6BeBam0!PvNI zlz_G4zs?*Z?K*ph8FBcf3GbK^kbk!RVmQ>)63(@3lCXI#jXc%k&i*6!;rKrC)X%ZA z8{K!y{@BFi4(q@fXw$oHIJ9Hw5z9`TCB6$Q-|q3hdN^XfKU?QLf5<^ZcVCeW#zhNP*yb=FKDX6$Ll^W_acU*GWM`ifL@)+c6w z{5?yiT=}nS2zXr9VyY>I^`ykS_JH4UKaH5?$Z9agDb-2c8zdJqBdvp&lXNG~35hFRU)8n1(BAFv%~)7!rIhQ9HOyb3ROtv?i5QL&w7BH(A(C82svyj5N%Gd}hpGT0iai za3HUYT;xVp7u(RAYI+8{XB@kSv0<9SLR(O z|J)rilKK13$fENN4mKv1Yuza*+MNyLD&y1uD%aN!Q|t#%6KWYOFr)&~6?7m%GFR^C zS?<}lA@_p1uPtM%D-~uQBBPM{UZC^LxDrufsxdT?Vfy)U{M0Mm%~F`Bp%cybhAkVx z{PzS#B2=wYR$5cpAci1+-u&9Ll}CZ5@RD5FGj$C;zg?@5OaSMSTNpywLQf$S{SXme zE_>J6e|g)n#(>9D$^AXA5qnNRig-G&00CRdc*TyKGfqp<1h@DIm#!|6J`#HTE-{b| zr*wyq42mi(2vlR3T4Y5v;R@6zz#u7W7=|U56+k45EbS#R86`2)d$v~&E$h3vVRO)a z7LD1I%l;k_6q~6bGDR{(0~u*%CEZt{JgJEXjf^>g7}!#pCjIX%zRh;%B`1FjalK2y zWz5!X4PoH}xYp``+bvoW|tM? zuH6rq-m&|u=KznM>7JPJ5F<`;!P9z1q-cx{Z@hn}-iou_3@!^(G~xO3h9BOZ@w#ou zEi1fdyTeHbAR$*8_3KIjYmI>lwMZ%mV-RPE9O#rizwey@uKNarsI<7l0a-r*+$qNu zna614JZ=dKfPvk;I=5URBMXv@Iqojk&7e{lVCFKwR|A8L12#eXXK?*;ja-vYd9L3l zSEu{X;%x_-Q|@K0S#sV-%{SH4t&110-_aiig@`xKczXDRQ(SP_H(c|M0N^nMJa1S0 z`1~_|_4!x$^7ewq^CMojD*^?amldzu2C6iD7ScZkW6vcf>5_7deqto_Rj6FlT_(WYl`CZ*8Db=fMtKWt@4HWk27VOHNZZp8ia4on+0u%=F1phwV_8 zaHAh%eG8v}d6bLBRUp2Da{Mr2Jkaw6_X z{ycYtT9{f9!4j6|ozX$`+33zC&!cT*q;(Ps4`)GnIp7K~lY2k9wLAGUV~t_jhK;2s zlUgsiQg?RFxJRsN#boYHQgtt9Gq4iKa-$EIjV^2UpS{%N7a<+(nwF)B>48U+pPSp6 zvV5d{C-78}uzL@VxgX`#w>qF=A9!^JNWjr33KP3-m>;oYDgiMB8c}1Ua;GItluczx z;u(7d@bb1{S&C!oIR^adVa4mV$0a^2PQxduWE0dLm?=8T86xIu3z)9E_*2y ze)IVOPY)*$0ngVRFI&P|IymfmLWsdUZw^ZY&KfZ+KzS`jZg`<-7pM85b#HE{*?2?% z#zYDb(M_X3<2)w*K`@Y1ek2V?N?jDEF(xEEi0#H#V*VFHR&iuaErHNf~dDX5Vm!MKJ&f5ci->8ZGO$*#u>>& zP&IU2c~eJDhn^O6Yt_&}hkTP|AnDo%)NlT}=X?5bMWS)8TGbSPdw#;xdcqQl<9D!w zb0(82LH>tjv&W&(Y*S9S>>Hl93tqNsZ&m|}%XmWa*=88YCZi4|?96!HE?8s46CSXH zFj4{9pkLTI7Yc4M!s-or2r7#g2o=y9b*1*!vEl@}$&9(ZZJ6hu99qVE769EFk{meZ z*=L-*W`neIxM1eC)P{_f=5R-UqlP5tKl^YxA%Ss@3$7_4Ry3LcBu;qQE_lj2Ql)dJ zs_D%Lpb)Wyr8;w$6pO^wkeRs~-=XTgm#GZoIoe|;E=ib^_3%nv+d21278Zxcs+QSR zqgHljdca^b>jD*y*3KtYO!O1jAt5PBhML~UNR{bBv!L;btEUe3xrt>%(UJ;609=u+1NDywhkj<3}sezL+D727aF>O2}v&ZxnyoiE`PVbC!4884o%M7 zOK}p8XH-AnL~YJ45A?To*?Tzdfh(|N&pU9j)Du0O@F3w$oBC93Bw!r-sXw`6RtT`i34j>4<74N#(hz5 zXv?2plNihyt=ByKwdWioWHNul4)f5NPzq6MueDh%8h3LMHxB7*Sk2Qp4%})nZWlT@ zFdvo_N*$z4eqftZmwdpi>&_*aZ7mNlv|pmLo!-#n^pYb%ri0edfd=`bi2SYLI;oFn z#2fd&M+_7C3C1oJTfJ^cWaW)K|bKk0{VGHK!CCC*>7EDsVwfIE!I_pia(Jk)6X z43ZTo;Pd%Gv6{U5e0#P+Q=!1QSWE4xAirNK?fIHht#P^kN{S^!;_UGMxu%34UY~K< zH+*~E@Xh%V=TmdI#ym)?+S$NM?IW_-`*ba?i@3%*3NtgrpbK=!*u?Fj3$wGRj_M=b zcktc8pzatvUx7??cO%Q{fI|28K?SjKgB&0xZS#)Cz(Ns^%UUz9fr#ok0xnt6L|AH$ zco3AyK!oeQRWy3&kEycYMo&_r(w3Xi%p}k-Ma7+I5O#>i!rX?*!8#d-Q$N*owt{LbNeJFGa@GGWkEkd}1Y@Fk9n37PM+_bG^*7D~6`>)}FlKv@ z^PJK~4A+>K?HFO8SB z0Y@FwXpZIp6P+iML80X2%Rwl6mZ<|mC}N77o=uOWHoPck?ocR3nS;&4VOC~66NRxo z3N~CDsz~jtjRWEN774>3Hs%x5snDA6Jly@1!@7|pc>8pa)HM#Orqm;;gW`~Ih#_KP zLFm_1j?%fxEl6h|Hv~}-Z3bn_83vMRe#hi}c2k$J8B3)KKb#h1241f_Hp)12z*3!? zQ*E5iG2qj~ip!o50+2HB)62CV9OotUVaY%J_>4X0{(d0@q#6OY#DJ93Ku!&NrLQou zaaWk0p^LZ;6CaFL`}GJMoNs0Q#p?aSeFRIAOJ1F zx~hhcpfV6`=C@2T!V)#CZQyU)^nm^2VEnIhs2JA>#0o5(zD=69_<^2{%WfRcTZ zf(8f2InjG|kB%X)hXFY4koEQQh9w3(#?z?2s;^0?2=o#nh!RpRhM&ZQJ(Vb|Ev3N> zsLt*&Mno#cSbI)oZ5IUiU<;EQvE=vitO=4K*h)`n3tee*MCNpqnW;(#{Eo4ca9NdW zhwb#q)f*mfB7ATIgHr^{7I(4$COPxgNaZk$GHXUOhD_|^@N8+7L&r9q(XKR>oJMgT zBpj=v$P82YV1v|}PL(k!Xn@LLwVSeF)=l-sQ5V>{L6yxl-#?hGQG=WW+Ge{l0%WEo zyWA5qFpzLCYh#MWBye>imW|#T>#PTc>buklCAA+6>};ysQ`VS@#hs0D0`(H7&RVRM7<+Nr&0YMJMKUy_mokr@sR>bM*LObj~F zs5UopB@GbqO3V;(TUD>x(O>;NNM~{h9XCy~ge=xh=7M@Q%5SXNsLa6Qx_}6{?zy19 zYrxZa#gpoHD9gw2GD{H~AO`Qa4Henh-hC z=4B*wQh{quvAv&xgv5c7xHP{Z1cXQ`GkqEwLK~h*=kGG*9{W8}P{R`4M-YVZq|%US zHV>(6`koXK&^4J%4Q;PVnWlek&)bm!-JODHirtdWHj&XozN0DO_ZH@CI4gSi&NqMF znibKV?3!v{WB3{s2;~xSz zai@vosk8Ez9K%s-HS(c*FhynESxfJvxU&Ho)L6q^hew91 zaw0=Bg0CJ!PlHs@yHD!__>u9jEx6Py-&6+!JZSgeJ`%j zxt!)pUw-23T>J*>T59`)>sOX++-AtOhuQf*(5a9xOJF6ENy4ew{(Ek zrebX9W!h|Ijx8JeXsX_f6vrwL)%BXKMi8Vji2+~^b3@lF;WlhJhb2%bCi175Cw7YQOX!?S??6W|AxmxyPuef{*A&51KP8 zfuY0hjSq{@4}Wdr7J9L)rWv#93rJ3T(AyE zdVd7sP{wacaC{FD*+?Ft!TfH7Ryv+LkQ?3k8XurRW0Dsfr=lFbk_SL~b)4y+LS^&> zCd5;~XeKJ&yQ#gIG8x&hzMBO~q(KX%MR zicujQN?Nn2TsV5THFR?}WuuO#C&YktS@7+{C!9;(SAV^QBK~rD#dY8NwTU$YnfR@u(S|Qisih?YtN{ zx+v8EzT*rXJ(oCmSWaTOJ2o=R=#LM~G!EaW{--Puan-rbjn<^khCm#6E{xb_WU|bp zJN9CjnQ%1SnNkSqNufNrA@YSuAcj3Bm_!3Gg_ww9iS0J~5jD zN?0Im_M9VK26WZ}TQ-Dje8m0ZF(f*&lNhe*o*OyO@e|^;a$SSUIg+)h)R3`^j5t<2 zdWMS=le3u{R!FEQYEEL{VW4BF@gEvs@zTW8^COQbW`l-x1G`3dFHP-r-wXN+#yLhr z0=9Y>tTEuSC!~~BetXybo)(;zfS=!XfEnLDo$-22{g5fH%%v#~L4*OZW&kl+>kXaj zPS#%m81DMsa9yF}WIZ`lp+YPhOu4nt~pnG85IYO|P2U(a*|-Z*)qq-8>? zmrp}yo>7tti0P0H_Yo5$O*XJ@xL?uudKs%XP+)mJv^{l1%&EY1mUG;KaaGEE3(oa*to%B3yUJ zA3PNw?wWL&ip1}!?>-0Q0~$O{wvV50y!RO=l^w4%_jlL*M}`?t1OJA;_R}{M zZRaVj)mVuk`H;g9JL^;cLt$?k&k#sxLdgZ=EV`WmPzZHfO~V{fRH=OnoE*+r18v~@ zjp_%TyH&42?_;BU6+1(1{4$15gB9x6oQrFBJ%sE&G;TI}KyVjXM% zdu1guGJd?gBISfNE{Gvu7jF46uGmv9o>9%zvNjW;ffM5XE|Lm36N>BK`B z#AGjbM36fDH3TY$iap~qkf(=$bzQ>NG!31;O`J#Lr4yIrZ<3KJn;u?7*-Xn84VoHJ?35QA+vqbGN zAWDBV`L@qz0QIdaBA(X&*0%8nU>U&@Jgi{Q7Tq9n$!rIt#M%oz6 z8J`~>aNW|#zeX#N(X)e@P40q3JtqUZdC;a(!$t8)YOqKODkQZl%Y0taYyj4HW4Rtd zfEU^rB(%&!h;>NG4nre==Gr57CIoDZhgNzta&~ zo<>9ehL7j(e!w=E4!CMiV3GBS@zL+{i0Jy)KS)IOx~>!HByIz9__q6%xXjRZ{XXbF?vsYl{f68n`@40 z!gc)leW13HtHS z!43nBu?`p$+jJOcnW#1O?6BNV*dT;9NV>dfwxe{`F4<(wv&cGn^38pjZ+Pw;(%K|a z)|{9r7H3IAjSiLtH9;7?k(NA?02J~?%dx)DeMu7`7S|B-B)i}+u!pG@E9Dx zx}3135|y*Y6#ya00WMD#==vHNnwyPeJsfnon*STui0?5qG{OClv2?T=#DB}Z&NBw5 z&7&kULZz8AHyU|q5<)N@I*?A@wI+#SWHp1ZryZAl!`pU6$~zvG6$=QbxPk!uykGGc zBNAss1ia-PFV{DmPmkErUUQKH0-}&H3A0T!Ka)z)PTX0si=BMJJ?Kam@~!zHyY6C{v1Mx|*FIZnz-_BCl+*%V!-;hSJoy5Wm4NVWz_mDww> z?0+?ogvdem1}ctQq!n=zYv)~Wmg!B`ZOA(XOcb4AU?%f0o7{J}2b0xhJ4M78nt0ak zQZGqjILc;fKWBq9JvFHatASL$d#WvNy=wm1=sRpvLyiuFtplhNMKBuCDgA_%GS!egEhR+VA>R-#y&i5)e+YH2-VMrDzZHtDJ&VSutRm)c*YD>nrR$>+**fADf|#~()ug1TMe%}Ul`H3K=) zGk&=FHAgkE6Cn4M(5Kr_kiy_?HPL-FJZi9UB-Iz~osmrk?al3G(|N0}bzW9KV`2$B z$rr(d!yV9}e+fcuK2tYz6ZIe6GR5Kpu>R(I9py3Kk&GS8tG)i;m5aeJOI*3s?QWEu zCY|rD|9h<}-FALH8Unn#8793?{}SZayT|Nw0`F+DyG<7f2}>W8LW5sl`P^}?Mu}6M zd1IJJcSs5!W+3oa$yNL{a3>GmExr6;?Pib~+6f(+LW3~?ZkYS%VVqE5H5#S_@vqR;8DTC!`QV+&xv{>q_9bb>(bj{`?j$s2C+Pxat@O&EEnv_Y$T#+22 zV7P1_vKqubC7VkU6B;iMo`vBYvx!?XsQ~OY&XZLZ07H=wdsjfqHYB(BQd8}A&>K3x zb?OZ8=Vwe~DAc~+5(6ld?BO*G6Ozop5`krj*x#-lv2Pi^Idi9o5mC)yuQcb{yai}F zd}et!siT{&X^x5QvO05X*+RFV2M3K51U2VfU8a33^!~xhC`3sG zCdrB%j?TD-+_S(ys_TuZpxp%EvS(~5;T+>}gYeG6*aZ!2Y!iI|-t*?EH?);Tz@yi{ z|DoW*>mSuH+B(vFMph?39iy#ZlfCQg9mic13C58gy7PpgiG|N!p_6uzU^e&WgSpUi zS#zL|y>TwyTW&l(<~M2sW5juj*&v6}69yBN6beiUnkUd)NX8O-~S@ zp7glpmV;OsV-SP#Samu%*XSu8&fO)Af;(+G)y!ui;1rkg^VP5{QfXoV;1pLpt*5#* z3@|Ma@Gq}l@bBV+E$<)-HRG3%QW@rF=EE)iVLc(`j6Ege0)2$Ku;&0T+cRkxqNf1t zOm)}*oIqp05RC;?a2$7Fsm9I&O4D4+APh$cv(j(fm=@Z%^tw^Z*{C;n4L;0VGOfJH zrZocra!|T)rn1)2_G`i#V6=uJq6Huvtq(GKU26a~4;y-sOxC#6F@tf8j>caXd9d1h z4pZXmkX?zu>z>$ZL zG>Nia2V$SHG7!w3KYU298Lzg)p;!(S`?PhdlhP#w?3ug!^Ilysv!E9T(!~KulRUEa zbhBojl%ASrL`x#MJd@y*X&$rN=rD)zZVpFO)2-VnKhTw-{l9_ih|Em&4I~vBwmH;U zeME45B6LK_{HN2I)M#%+JxcGoB?Q{a1l*x0#D)&Dmt}h7Mkxo(CJ5)FTHAL~@-&h; z*J)l8Ck_&3T~E9h33ueZuKC*057R*t{7aB=wsAd}WIL9P1I0PGg8+ZzGpfCRPCviV z>fOH1N1dXIx4IQ3><@eB4uf^NpDZIQZvZZ8IzoqE=rG!q5Bk`9i$S3Qmh$HtF}}9} zoJuhI3iGu0%qkc-s%-AQIO_q$QGJdN(#->g4@Zf~W209-_#B7Z0Rtb|8{ad;_?n2d zuib;1d$Q2AXC6jm%o%}VO=>ReqYy%kJ`5lv?E4OGDHOFC&CSTHqOeO`u*QfOV`r$P zn&S&*T=pwK0V@^iGxIXIvqbpw<+U)_D1dWDj)ch6&D5HnFCvKNzx>O#??_w*#>;$2 z(Sh~?envQ?LTYvto|Q9nINF+Ny9by(AB5B2!=tT(8?@OH-n0IWG4j z*wN%>k>7PWY6;zwLz%JIQ(YgF(r~LsZkS0FtL~NzWqcI1%8i+Mn#Rfkx_<`N=!*R^ zq!tvJVo2Mu>!FN5pO{Qh$@97-e$O^#8hg@bQ~Op5<>YA?-x~t*i{3`XBplF&%|lHh zU#-zeXNn<&41nk9R*O9I-qne&n|mFDdSQ+?v6osu^DN@jV4S~MQ>mH6V9Va|8xHF# zClfO**VJFS_#wlbKG3AK=OSZB4O(1~9FWLn*p#m0`BIY=pXrIFm!H`-OBn90EsHn? z!ZnR1Av0rHVwujSs->-mR}7(}z;ZnhGjgB#=f;I!1qhW{~mJk~t zrw>X9#Xe(fs7wQywzK#-3`-y+B#aoD^?oigi4`@7?m!f(f$=*;He2_QK5(bDgMk0^ z$3Nm<{rY>HmZj^fD^75c^@U;;aR^-cLohNuBMU% zs-f3cqSg2I0q^`h_{&_SEhKyalqU?`Xu{u3ZYX@AP)`fJx6c7mhp4_+b};T}pAR;S zuZ&isU-a{G@GWbhnp8oAC4}x9Lj5c)zfa6~+pfbDGZfiW46)Y{u~C#cW8e01cGmY= zmj!9xfyj7R*BTQ^L&C+3=lzP5GeQV>+Y**C?yvhssKX8F9v>2ke%miKGkU2m(CQ*M zxw5bU@wX@*JlE-WZQnJ1#78s$zL$r^9ySTd@7EcfW}O@~_}D|n1_;U3Y3(ni=ZF6ly|ccI#YVp zBn$SzK%DH9p0pDyH_8b6%p5vM2C$Q53j~t4!q}Zszh|6EQ+D6cxuZ2%BTB{O)yjU; z8gY^H+ANx9I@;UKCf++{?bv->uSnNy9+Y)QbK9bei1hmr<6syD(R504JxmY1Mf0sE z3tJT0Afcj5tUt$wJVB}!l{uTzM{ywWWU}+*YYk_9tz2h>nn{>at`pjtfk*ASad)U* z0@aD_pbqCMTL{ETq8?)~>)ft8RuM>pT29vDXj;2!M^rjlXtuf3BzDs&Gj^om0Rip% zJ)^}pOpZuqwtJPeWM6oW`-;VL%OHic&Q(60GS-e+8-Z2*)$29kAAb0;BKD-ryj0{Q z>BgAU@C#j2UEEp2U;wnDh?-j}_rIs9iPV9@@#i%Dz8L-xH7D57fK%o9ll(o6;i*d4 zpm7iTd(&VVG`|bk_)Bgq<@tI{TZ76uz0>@V(?os9{OcXa+Pw~h498qe#eazy*f?U6 z&8+p=bL@c#d^`|xn?E%ek~B~4L%A+b|V~ar8XZhhOCdv2cMJ9V&QT|2cnXpLFvv@O0IAAkrCTHrpr`p zD$~fXxbAnG?^{cmCl&^oVQ9*GF;?YV45jjp+-nKO5@Mw)mlTYmw=Mr(ynu3l+_+~x zmk$|0SmT0y-w-I^aXBH0;qabw(fR_oY*(C5CtOl4Il!Q5h&6VG#2K7(&yofMr)4dF zw`hP(3C!~(h%JCBi$O)hBrT4m<9mCwB`F!KGZYzFuOn`RZ#vJBo){fke9Q+QJHt=Q za@5R6;c6=mzbWF$ju%K{nKGC5a5G?5^RcE!>42=e5eyBFM*WV@po!wn+H~`Xu0la% z`Fea>@_KFZn9%bXESqBDLmAFQ6%`PXrAZzI*3-e@@@R@nb~l_Fdq+%8-XH_QA)K5h z<7n(l#wFY<(uPqGp=dis-m#GnNLOj+pdvah2B_+Y^~1|PogGHc+l?>yHf2%KYK=TX z(1=|&h6g?H9Dy?FnbjXj%`OdXbvDgK&p$`kIZqK9q|MNXXbz3<%@!Q0aZrwn?eKZa zLq}Q~;an%G)SD@wm{ZgQ#s29+y~gczV<4%i z^`XcZ?6k-Uc8KaM8al4mGpN0ug&d*keEBv^In{Q=t_Q9)(;KDxZGFXP<`2T@#QKb~ zkpqyJ@zdqnhgC-?5CfC^K;IVvqd3JcA=FqfbfykdrOI=zF(RZMZ)uK5V&Eo+4z~7f z)BVtN=duyEkGy~jh@7*Lqys2~6X@V1l+P$XOI^E6QzPA*{Pr$ktjR#6KL0rTb@MLI z`XJ6A{W(efs?m5-3|-j4fEZ%vf#V#GXcxqX5F<-PH3z9Ar?M{wQVcYJl?f^Cg5RuX zPhJy8GXaC>Htyy(__(20^@`Nn)@eAZ8~8m<5@{z^>p64OH^0*ei{%*v;VA*QvZD6@ zKX(!Od*9*cI*3R%K3W8K*R((s#jy9D0VfVuJHYR5!s#}ZmHsh%5=2oz#B2`y1ajFbveyT%2dP7ifZ zUtTAJdz|NAUZ3&%^BIvsX?&TG(PFI_DJSeH^}7^m1O9kABUR_`1+#^bs_WR%7RdcU zWqPvFDt0b5t4Nbk&#|)^8%yv2yJcK61pzY-YXJ}3$`8sH}Wa_<|t^XY}Nk<8aq4Ovip*6 zR77)SKiO0Zl`T73VG=tR(Zoy{&d5a*V;2CK@tR}+6#8*iW_lo<=iLVkL@ic+rlY80 zj$KJo|GT;55Qjh|_6jZDpdh_1hCoI>wLfptg-|I>tw2E4`;5sa3zSo|Q9PrhOq$-! z4A-Pr$seKR4&-#8GV8ZD9n8Y1emIB@3bx1v7WaLgY}w1oj*!osC1eLjzOS*y6YOz7 z&VL;jEKtDCxer-#&r6mOP0qm1$V<2n5REjZt{)J<8Q_STrwCOT<%*ne(D8I5v)Cld zC|{6*xMacDX=rn{4Q_qOmgyKE25ZK+H`glyG2pSD2}(oQnqcz6DFl_8U?SSeFewn# zIFQ`eUznO$e?YkD(MX5^gebxHt*PHQex@$!tv7!g9mb>D>9C`~2I z5=!)90s|@ap7RYRp}j!C9*8GLz~JoAB-c!k21M@Bm1Ll zHbSPE%m7Esf72q1=jLlnvU$=rIzuQh0fC3(=+LJc7ia~FSocy{5JTl2wa5X$P@zRU z&};-YhfK2vxyoXWteu56<-w$tVmCcNKf~kwV+f@P4|;kTLeU(?B@{hDAmp4!V@{aA zV=d6V_a>noY)vo`3-w6#t>tmA2y?Kat8-OA_N<|zpwV*c;4QU}WCXD6urS6mI4V3o zWCoBKLnRTL7HnO+evx{k9*fLjKW*`Turc-%PSGhIRm4f9M>9D3_sPD}t zE~b62jEp5LIIW?hgRy4xF$hZ}EU_41_2;c2&mnXptk@{i0M<&ACS*LU5wF)Bu})*% z230)*g5$ydKx6dF+7Wry`|3paKy`voE$T?HW15z^9_&ekziG9*cvhz8K<+nCD!(al)yPP=}Dp}pjh^hCSl1_HCKTBMW)nVC&bESbSQ+Ta= zYiDH2qe00T3W=NsA&My`h7nwgt@<68pdH6qG`=c}*`fB5FwtWKahB2~W$3jI58}KV z>Ch!eJV^p`E_)JD0Husn>2*Pv3m(!KMYk=_Yd%0R+j*z<7-tB|qA_RD1hUAepWOaM zQ>z+JW3on-iP0O~Dbj4zBcXK=WVGhcK2t-_Z-xM3Ed`@V*QohG+?BSy(c+mp8-aQQ zPyM`ZbszE?=Kas>Pt?laY~n?;-w{rE8PtY!1h)&!NnIAOTE|+HkfB5qDjHmMhE@{Kl`4ON6Z)rzn(#%EUcS1M5 zJoO=a&`mIVlsTSoz!+kWxobvJu*?r@vLu)#mu<%qW2w8DvF9DksJhqC&k9sDwY4kl zeV(tEy2mm0oXXF}fKyzs#8ZtLqUsL<>?vbM!nSQ7+Oez)kkL_FSz~jpDf*sLho?Cw z?3L_{obdU4!rOku!|8<2@r>uZ;S-jcd=T){enCzNk8uU3Tqw;kBJEfF<*m@Cmk@AX zmeQ#Avntg+g#pf3Ja~&8cXG8Kw+4ufBRtiESZXFds5y~_BMe!A+6R%3XXtcafA1B< zqyMBSb>Ac_e~Cu#+ZaxGxJLFbch!1 zzZt`N@1UpHcmo|%T_*RLD5nPhR3HGVmqK3H#o-f+PUdQ4RNbeRuz*1SYjxH zC_9*^iYQmT6xBmlqF?N|rm{eVSm8!Ct3(Wfq=b^Je!ZMC1;MVo9vbVmnZS{VZBamf zL%0|&_fo!HFlKCOL?eDQTTK|7Rz)&Itm88|>E+~%>PZi9Wt5rqq){K^O*E#=z_lq$%t0d^+KLT9GapIq!Je zcZ6kytBrd{vJ0sSG*{3C4$wD$z;;>oeJfO6033>wahI9|!oF=l2n8*r($sA!A?L!7 z`PKOm*Ie9~Git*bL#;~+GD=O_!azz1*PM`3!p<2>h@cR#hJa;RK)JAdVr2}*W=YnD z=@hWUh&2SnxZtO^*BWgU6gRW^uB5iV8FSVkp4)v-2n%6d7CbBqF4qJi!p0d(^}N_p zLf%t}G~z2V6##7w5kwIoP#v(!L5@JBi}wlj57rO;fZ08tC6yRTe1eB??+dODulO*V)&i3#znZSfrdY=Ofw@7D|g zWd7rf5?HufMK3W=7(=$3fQvNwZj@{zCPpe(Z>qAK@`~b~LQrnEwuP|62Nnxen?-TI z-bXVPMFZ4ln5LqM`&9I=WA$VeL!*mGI8<*57kiQxwv=sd#iLJ$l14r(CnQ!J&o(zy z3>J8Hs$ynvb{Mrh8oiD*i6MY|h{)IsSrluY<&r7Bt@SGV5y;b&+fZ!noL9P5;W&i*?gG_&zOS6xqi<^$2B9AnnS9pAFE zg*FlE(|d!o18(Bcjna)3(AtiGedk~@NyqX6q ziNUOdq{JydPwFQ<@JxL@#!tyzjzoI@okS>t3QToClrnXyvHfi18F7;4;)DiIiejG0 zW_roIPc06P;;LkZHZw9h;ZV`)AfC5jg(p^tIw zJJeHkmK&dcB9oHCYVDL?Gp6*)<{i*Y(#GB!C=`>Qjx>_eU?(Y*C1Uo`O zmToRgtz6E!Km<;?4;TXo=eS^5143MIxn2Mi#@9-*u(ZI~Q^uC|I`q#-HANujTpQ(x z$0e3?Dio6_DwLM8FXvMEFERG#E2V^-GZ>7$Ht4ZxZ9(PrQ)RrhCcYrZ<7^CKh)=bf zIH5rR03ZNKL_t)>c-t;@KoKiy&Dc3(%NdVxK_tSJi+BT&Q=B2ID9ECSa1 zePf|zW-OVlIRb+-sb+D;c@7)}V5wekQY=OzHTw&YX^2Q2&WDeJa9f?w*Z#!oum*BA zSY2q0>1(JrOBT9ad*e;EB#BaN?$Z?CPBjfxv^Pwukiv90km0OItux7Hi#$ydE7VI8 za>;cb6qIj?B)L^pd5r)d)d1_v)I+cd+OUgZQ3^Ig7OFJ7xnV|V5me=2AA^Be4n#uq zCACW$qp0^e;K0UmtSc2pG?_e+D&163ZwfLqf1VC4gAw_;1@%J_u__!W)QMbRq$Fx6 z^Q>0JYc)`6_f{bB-RMOYN?IMm#vCq4eO+*hRB}=jr>F#-SQrGE(%0jN|4PE_jr>-< zKVN}`R+V~@$7GpYLZd_a*~ob`2rP7avN>h}{z|7@^3p$gqc--oV2@*??<4?AjM&rI zuVeuhje!O8pfWby4>}@Msm6hNL{bc4MC}lZf**rEX6At84okG0hFM4HDcDw`vTS19 z>aeZsF_jJ$NxWFfs<~Uhj^; zw2K)xKQqzDq=_4wJxyxp zS$tL&_h>vndFCh^rhwCETzP8+R z!H5V5fe~s=8zLds=V#BQh)c-57U#^^`3lY%F$OFYk&&_Q3D>-r23j;3u}foF38L{#fX&ZcaE{v5Tu6SGI^l;JrS@l;CR*Zgn%6K0E}(6 zM44=MyUdyLs0c%v@WJ54L6F-BHD7r4&4{J7#3^#&Ftg4~z*A8H`%|9t^FynsO1LUE~yPs0>ra;TF1fj$x@g z<-kk5AgYv)EGTi4&e$j5_6c-T1J=oO{|d~)Tvx1h69=VecUiM}yRM1{*=2HxGuNgc zww`4*py+>cA}#;PL|PW$EpW;m$(7G{uA7?bLj{GXG=H^NlkUR|O<`BhIZs@t>9Me* zZJuCqQ|q!0o@RMpZkM)9S{2vW2+T(_$ zejpHdLhYD%xMFjg$c~H54j+byaasaW&h>e0MmNPAi>jVCH!fcj6)RUWNF6q`-lITe30>7XgFu`SP!9IYqj}dw?a%|F z@;?9)2(z`59kPrzdFQ&fWb1g$JQY%{G0O%??vER>^W4yIZ5Mhnavm@~(E74yeA`}S z(=PO|o0QQNkS9Ijlp2AN$k5_0q&6NRN~`U^cF!b31=qL1%Sb-p5wJrI62l`htSW05 z#zYe`HK?fi!j)mmcz#ENE6EnBtf@6ZqA?nXCaHF$o%<|g z2pYK@KjToCAW|dhI^7a0qps#0-++Q^PHNDk{U67lnn+hDZ6L4bcUbmRLq8Cs+gl0TcX_7fT7(pW@eOPq8-#B z^`Fh^3kV2Y_QzNqbivX~p*=o=$JZ%3c^JR8ZxoDCwP1}D03&u_rVLE7Y7P^ZGiwt= z?#zx)1sY-z`r%LO3d zbUNdAk59EhOh_5{`R%O?iI)ZIS~Jx_*mG$#jrK>M0nEZ7I~dtc10I4)cg<5ayxx3E z)0qI`ejV7vnO-a68Yo~5#f&N=W7{`uX~#kl*Sz615B+aJRtus_DEinnMo?HvBh9JA z1*L)n=k{~mEH#z-feEpJBw!$C_0K%`=pm2$?OQyDZ=bcH#AxiDu6 zc#sexVT}Rjb;T)0d|FrVOU7Q=CmS*%0nhstoD zuAvtN>`>K}J0`h8x@jxuxrLVRz&0Osnz1{`49w=)nmF+~HPWDI&Rx2`^9Q85{4h=H zXY(s|Y1fji#FlzOCYnK@36suC{StZ+%GUg6$}1%0G)y8tl);8?3CB8?44O_8j;47t zJone6O(3MEwNu184N>#|X`C*j8sDbk*&?4d?0qiJEq$WX zxOr$;kFug!(6WcA*_>3)HdctaKR-4EqQR3w#^Ig7$Sdk#0b8LeusA#p8)@mWKtB{~|2ur3iVZ(EzeDKnTr)N}C$I#{Sr z#rP&82Ewus_QXIMDBU$>EHQv6RC@Fe{D9b z5Y7B?Aq&=4WOWgHXyP)g#J(#5cW}JPY`vwHXVCwgTMFZ^U9>_+Avp5vk zbhZL5pV!cV3oHgIQ1&*Sm4-%-sx{qWuWyAeOUGu`gJWmEFdW7^Vx?2PPV-q z3=(9c>z_$1Jx-YWdOVKjrJhAz)YSBh=B+xL{Qv;Qzq$B7CBZ9?sZ7$@ksK|4H zs$g%LSP+^VpSj8pggM~Io)5wZll$K8InygIDxRYa%TO~QfU)JIhx`x`cO3A|a>l21#cv-! zQ7f_QE@b!shz@|_ZA%?-x^>O!+%MF~$N*wQC!G@4&=1`yks6JrTl zHdzJ^p%0kWih|~x>;F=3#m_M!b1qVZKzLjqa9&o#So(=QXQZ?jwo2ZCjIP&>OC6BM zfa_MxBSWmt`~`(wV#M3^0;*KzocAh=s3>h_#7dEtHBXbb5HhaYhSRbX78!sT38!TR z2uQiCk!4-+w4Skqh~GSZ#>3A);F1%F%V#`?fY-F)vTt~JC{EGDjGXuWG{ibY3q)$D z7!bgP42fkq5tLIcoFHENujJ6z>_$Bl#S;gWkDSgu%27k8C=7ZB86p(v!z(vb1CDDh z*@y}_TH!+gRcFc`m;liva3CF=Om2+!JW0f?(_o%zo9Wlg`PdS^-9I|^$Dg&p=HA_K z7-JN~tJ9_8puhzh(|?xkoyvlju&&xPr`fQuQj+O^lL%nwYgNRX0?kn|R}- zJ8EW8!S9GC3eh|rW|}wIu0bd21v=bRURhL+h>=D&JFO9W%C$+D-7eb)cvuO0F0QP( z6?&nVII%;ccdg;*p!-pdxPB(LZjk49&@n>}-NVZWR62f|MjiUV(N4bBt3z*#+>2`# z(Jm=vEcKeH2oiw^kT*&6TxiS&~>k_f&Ye(k_Z7GP{=b<5z zEo%@tsDVaQQUE%NdRhv~moilc=7K0AcrN?zjB}tt!#`rEwwF!fJH%>^ab5zpS`@G~ z`fKtP0zRD=rPpnHO*ZjZ_WxP)Ec=>fBiiXvQ5NT>xj1ySM07v`vU`QHKH@32UAkSf ziZQ(5PD6|>bIEArj3od&3m;`s4oIXvbkn+#7s+{0Nm*V8_J>OYrRGDIcFR(u145y< z|4P?gZ+Dl2djijH;A%;YCNqe(pC_r3LH$U#y(-sqjLK9TodypqP#~R!c^XX2S8B*R zNcu5|4WY|;$QmLvYAT5}#X?#oOcWvay&{Ze%1!dP~ zAAn{G%6)h@fQ3-+S8ARsWQRMXO{eU$N0Yc?AoBIkPch<>Qa99V8rI)FJmL@Ee20Ja z-EZ)4I^om#5uYB;2q7Y+gtzUAFK@3{Vnj|2IWD!y&KWrst?YN7zCmKfW#5ZjCkC9> zQ)$A|hBdCepSbL)m^p@sof&Ib%j-B;?XRI;ew5&L<=N@yIbH<)hG0v*@c3oC%`;NV8;{yl_iBU>QxLhxI zy#~4A z*wT*78T)p{o)a$nhJC-{ye{}L|Af=B;1mL$;)28pON{_LA>zk;#oKnlcl@a&2mnYq zBZfFK-7&K~LXRsASQ?tzXiB8AzuY8p;Uvfd(PFy+iH2HgPa1{AXyFE(O6W5L#Z+$M znE~q})aeI+cK@|f3^mDz$bqDR2C_z(RAi;9L)qnB@#Ip@4rQFRL6V>XkvruV_@tZi zG7iQVg7|DfZ-uIO_cl+S^o25hiYBuaixe|>Dc_+CB1q6U>2q+Vdh5Ri7Rkq@~JZfIx-QH&kiQ&rqA$7{ES ze<49^Cny6#=7Chi7z90dM!$xs9?k1GG~Km9Vu*(H!>OU9BX|}~nv0|gsY>6W*qJ%g zFrYHzNd)3<*o4eTR;@sK_a5g`Me1@^7B3{S#7TUBab6FzrsKsWbE zc9WO;$xCCP<(=Py?mUq4SO-P@z6TQe|Da_}wJ?;%O7c@@(HU9;d~N7v1st+rBULMm zYl`9%ISJ?Ws=cLqzvgNfSbopWU~U@7(V&AU*WZ4RPv#`JW^Ysk`#>?v){{DwQ;qQL> z5ik1{-<;0)_2Vb}+u!{GYal!>r{eOAVmkHH>oZ=iZ}{`;bNS!f3$D31HLM}x^LoOs zP7nAW|LcE?H7>}U@v>j>yj}5fdBYEH&-lyREB@~Jr#c)jd;U2@{O<7+{>}Hl!ymr= zzL@l-gv-9;$Ja0T`SOa_?TRng3!e8YUbZXN7;sGqpVt+SamDj~#WiJwm`W2L2zf7P zHkh&0`%O6kVI(}XJtwuHSuQ9)QPG$$h6m!Krfg`ML3f^H+?8`;P=unzK~S!`Hk2kM z*qAn1ttlS@2Hi|)gvvwFZa7;AcRcP=x7#-;9yF&ZBB_1PY_(x9krYmyCpEjG!IDu8 zO*hPTazT=G4aXgt)C7)ImZ3bz=GSKU1A<9$*?us?I8SKgcw~dN+)(iz0f=?;X5lj1 zsB&Y-FxEv*87i(RhEmvLL>H*!hWfhY^=M)VXH_7QHl$FK-BD7=1|j!M)p}6Z0-S;{ zixRJsSqy@xHWrfZ#AYy}T)A1tVF`yv>F_;i)*6 zK$65y7SR||=ft)6*!MyaF8O)X4W&4sHpk<82Ss6{jv!C+1M|B+Of=?K?J}H}fY;03 z3x>qNDF$TDIGtU2_Uum3rMilN}o8E$3?1He@F)22MVk z4K!S-`T-a-Ox9D3Is_2FhNP`CRLy2%bi0c%!O0U3odz!r!CKv#z*t)R4RVRJsoC2c zxPwTczD^7BVU`h??Z1rYHcKKt=bouiu1UoAr!&6nS3JfA4>97$?SkKb`Ue00Z~hh` z20XvLVp&!Iz?n{+jl*0T*Ce9Bw!9a&;SiC*$aUZ{=+u{zoWY#&>EQt{Z!h@% z(`Tf#;~)O~U+^D)`V&%0c-c4nczMN_eZwZA1_{9aU;ZAye|Wh9F8hYxef|ay>ly#oKmR@c`Q<0XifI3Iy;LK!gvS_h z*-O&I>%QT6-#XK4uV?F)69N(b^zsG&`nP|LxBZGG!~u?mYUb4pJi&F)Q8dpXmKDBu zWLxXvs>7GHOC}4dDOFd{OxYarazZ!|0h;`tB^ovpkbB!0Jl)fbj4(W;i>ImZ0HEq- z8fj|$N6lTc{(K8)!6;(?943vBlcll|N+Sg_bXgwyBw}BEqH2I)9%0eiAT)mxC78G@ zBtyFAVy`<(nKiVfYIYE$9sh8{z3xe5&58+ao!q39Va;bqP$XX-VxQnrVFi4UKpm(g=#R2q5vy1j3x~-lD4DBz3nlh!Fm##_^ZRCd+l>;O zM>J<}ye)$I^J*R?6HS;!{26uDNi#lcG~k&zJ9~kUF!UUMUo)hJ4Faiar3QI5Lp?k4 zlUOO#d)-%Zeg9ksRC{I7)Nz0q#&BgGMSOR9z~6lO27mp{xA@JcZ?LBw|I7dVpYXiz zHAbp7rL95a49zqiX9h%+9R1?w6S5V7UGpv(}i`BdZr8O?3GG`{~YVQ;!COOl+4eP$jJnR(7# ztE#KJx|+R^9L@|V&0t0&0n#Jr9rUmRL4f{Mm!?jBt0;58uq( zBWoHU*wuCKIk`l5n0@Q*azsSH)8zv1S9rhS-TepraQ6;hynBQD%Trz43BawJ(n6WL zdkMh3-|!SR{Qk|i`25vtytui=-@W}F-#^@8%L}iMDJ<&b(%-#dhi7iWyKTi+AKu~b zzx-!>{_11Af4sv@PK_o4F2MsXUz?z0d0+zE1P1Vjx8LKhfAKjUE|1v!2FHx^cELo1 z=hF?{IU9nr zzkmTuS-CJd&RHo+RT*2zF6WkuLOV=7i(ujt6r&2YrdDQ+TRLS80jcryvg7qpoRu-O zT3?X(X*`Vg%xN7F4E?+~6r)>Y=ujsWz8}cQ3aVc_M>Z7!}MO}8h zKa!IF7?$xea+4 zsy>LxhB{TIh}Cs$A8w+>x+aKaC(4Pgd2DwebLz^BMXE$gnWcx!DHGymFe!T`=&+L> zRWCj>A=@fx&XyesC+%IE)G@i#1(m6bNz0gajCbk!6ASj}j#?jMyM`!sdTyz`brE6$ z)hY~7{J1%sPowax59IvtC58%^(H?#5cv$xGzi3cm6tAv`W(-*QQ`2knKBoIY+Q?n- z`?k2Nu%?{SI^};C@{kxxO#+izz_g#RuQsC{{~LxN1q)Y7$0|4d%YP|j|Mtzs8$12sDJVrM6(KOkr6rNHTmkLSzlDxBPnSIWp=4lnDoEC z&tf1|1KI3NG}a#yso1 zVJhmBXgMj(JYrK&&>ys(0Ik$tGcC!C85w{b%QsuN=w`P$hC#Jil z#FgR80Xc@A8^hAEAugxjA$xg5)z5%Q5z}N?qv8!O*KU%*q}Njb8`;6*MSE`m%yarq zINwAywKldem?AsY!8Copvi4ouS(f^yE5)D46@R$mQTr-u9lf7n^CpoRD;R42C0iC$ zx^Cu?p;hSe?y%L{%U=yuQd+gIb!YJn(}`covv?HKO*7^pd&GVQD_fTICU&1k2?rpn?=h z6kI~Y>~uX)en>EnD$Oi2CML`i*ZW7&o?REK9==pkEsfr`syHy22&%_i`J8oNTYU~l zQom?is5HV=C&nT3x_n&e0bo^KrNCvEnV$}yZwtQau?O{GWQ4~ph3~`xz*ZrAB0nkB zZb5*$@0wuaGgF;O0lKtu$jH5QKRdAd{6NQ0_jafu%|Vg9b<9%Fa_K-hFS*twh^iSI zJ)?GQ)4pJR70NJ!7@J6^zA&Gy2|>+2-?V3__n)P{^>HFyT~EwQaLs3k_Pg!8!Jl{EnPFQa(x2I~0wH$2;!MSMK&ICo+3Q=>t}ZX>F-l5c z&I{JeDR<*$UTV!-3;~cvvs^{_Y)o2sli@ z8Ui8{VhHIO6{)HTJ+*w2BQw5y`v&jNk07MiwQyRNL$M!N5I{B!Fk&-IR_tn{<_l7j1rp}Gy;(V5S#Ti zN&*;6kvoH|H*zNuU$czFX$grDl@b$Gw?UjkWpWy79Vk?|i)Bjp>P@;u^ZK)6kjXT^ z%YG-?-hT=!D0EFuExoB|-EpT)HP? zmD;9j%CI=VAn!L|B*mRH+g9@IT=L)R0FpVQ>;)YSQQ*DcF!3M_wKj=1RB^3}|9Dn%Gn=3gi#@-lOcVFB$QY*(XQI*A_zVD9jUGh_T8e zbtiq#n8iVOZm)M9z!eP1IT4+V5w!a~>d>b#>UI2_YLBo^lhy%@+DmPIWM6VEBa0TH z>fWVxhezF0oM!}Iy@F|&WgYfstP?7fum`yGvZr1fm(Qiv#;LXd8mrH>*PIoYVbwP_ zqr(Jiisd+6em+zmvNY>Z^H)*@EecCj&Q3>+NU<_jq~+}pi+c1Pfnp1&l6HGWmCGbb zT4d%5?iBEzh7)UC()U(gyJ!LXb6?*zOU37l7R02B;%ntv4yYRd`{BRzg_^O3UNx-; zM!N%Ap-+{}Vn+h3>>Gs%v~uWZ068HmeP<%|^A$K(l^xQl6KtxEbXLPYD?v(Gkjl3( zc9y-WEum3E&{;!tcXGAQ6Pcj20#0)7ekWBmCn7ja6aL3fKEq#r`U{+v1-GXYX6N8T zz!p402$^c%LYVV+zKVkEM83+1(xe2o@D zi2Vu*YE~Uv`O>Z&Hg$yU&?`ibC<(by=$YPPfY<@#a(`_^hyjo14U*;KNj(VD)(Qe< zo^YBbEF_p6V`e}?L<}A?Gbk^~>70)_Pk`##_IzG&2_Co8jAuOIVY}e&{D{)EJ2_XC zel9C6qdI4N3^+Q65F@^P|09^vYu!SqOf&xZ{SR1p!a{^|*Z>ID zkm{|D&S3*EXPG_nk~~qWnhG)fUjZ2=5Qs7$Q;F9?3Z(C(5UR2CnN#XPeux_$&X4%` z*$b>8LP{SoVrAUx=~t|dsw#3Kb>S76Nz89V1GloT zo`bJf_f^LII+IrCBD-?B{Q-FAfxy;8*RLfcea{=2pW8l5G3kv>QKe(}NtHCyF!+#J zOvdP;y=|V%I@7a7EyRpypGPFxb65|L)yDjiy)vAmI)GIaF{I?!`C@+zn6ms|A@J3;a0YVId?73wF{tFJ~zUoVV%Ee-m=_1D+9 z!gRvjos6kA@R4~gGo@gYC~a7GMY@)M#1{J+zHFDm2zpU3Rjl&bI2b_mAlW>pR)=n$ zA7Xq_^^aBsNBO>)Oa_F{Y!jI-&*J6OuTci6v|e@5^?jrJyP|J!8LR1<>{muhN>yw~ z9TKIyZX*q|v%oORsAsE&WJ&P6bc+y3nZ&KOA{oleut>nOUfD`=G;{GgHk#SAT6*{ z={P#csS6ZiA{^LZjj^8DTdoadtuCy!XU!-3v*Qi^*MId_sY+2IF6)XXiJ9#K+`@1K zeERBT>fH?iTiCGq4F~6NvrM2T?=Nmh5(UF;$_xK#V}VZEe^p(uF?PWOkg4~f;8EzBvz>@WjFNp_vt_9IU@10sWlf?9V) z`Jb5xOU@9OidruJeJ_H!WGv)+g(>-o9e^!c2x3<9`6&2se#B?5K1K*3eMRj7Q3G{# zAafxaA_UX<-VZwzJB$@0>f~fcRgO;_suz+NaB~g_@{*)|Tx4q5m>y|NAdL1x_90q{ z1KD+xxkb}@Jszp@zs|3n2A6xGm_lc$8s=( zv_zU}D2om~V77x0Ghy9=C9;e3GlzDhf8Q3xG%M#we4?5<4VG4`q$)D}7L~t?Rj?W5 zgC=&}Tl*?s$WF5Fu3oDLiL~6*(xQUmqZvjSo0?2C-rJ&sGsFa3nMIwhK_ckH@W3@Q zgSiK~%8eqmgce>?Kc02QZFNmXy?|?Kjv7NCI%tTbTSP}43Xz4oNQLl7l&YQ~B7}^Y zhI%wK$-m3#77`UOlOS02IVdrpCMd8kuiJaEWD-DI-9u3M03qL#sb4Uk(=p$F+{mhO zkELpZY;oZ>2umI?3=pB}V%nT9Mkd`~MY2m2i+K%Fg@ZBgHz_9gdu zBPO^?jKxfYL_vck>OQAE1&Q=(E=x3+bzyi$b=tfxB#z0>iUDGxyTtqD6+?x93L)fC&swNq!n7NpzPC+#+NpPge9i`3+B( zGl&>IdH{rFc39j2oK>LbZN&i&m&+N>5oWi*fw0UIZs#TWQS*$n29GV*84;Rp!W6%CeEOO&Z3TaXR$3CQWOSAOE44XML8#3hy15%Fjs=@Lk!sRg70|3k!MWP zgn44TdiDZ7^!+|{*9sMo@Y9fIUPBLb2H zjF$$1R4J97`6-Nx)I3i)4W1E@GIlt5XAwnkV3&f`@;&K@FgsG#&Nizv0e9m=JaSR`c#Bfl8KUPY26b6@O&)aO&C}L6wq~N*fBOG(S7QO~B1YIK{(khtJ^`5=xA$cH52v%9*kB2FFtsOUc z3YSGcroe>3*dAz@gjBeK3@6mauNOPO#dWxiHVHn4>n6M z?0_fTWj@|7grB0K=!&?Tl+3KWKAYsC5L_)C%nQI|mgFXZYKhQobg5Ah#RRPCV|od1 z`CBncS_&xzKE*tgaRyo@o^bcHVGAi>>Nqpzh2dkwc@3DScIL*aXlcMx@GX&9tjb+6 z41J(b3rw1wYki&4m=elHb;_P!iB00iYO|X-)d!e~m)a1@**jLya~~3DS-&eI|C8?E zl6}u(|A`x9Q?q=65CdYha%9PMj5V!v%l^y7u?ii^EQQkyd}o$dp37cp8+EWDU*pu; z_Y+aPxO#amp2o&9NXr|{m#Ww-m!WFK#$8fme|X#BUFs%Wu8|`7FV>gz)>~4uDDZP^C+X;0h&jJ`pf0)yIk6qe z(kru66=RY5vhuK>+?;*mg2@p=AVdPTtll`f)Vn)z@sTE3t|P)4JPy;0WtwVu;(2>Q z6yP||I2{h@e2x*zJb{T2#6x6G^4@ws2-YZAH;>hCxm(@?#8{@u0Pcmgj7EjgS|luJ zjOu%B>MSuKf@1vqR0DMxsxMxED2d@s%dCeFp~``0H^Di^$sJR2@jR#NJ_Ou9+-IM4 zTE-LmONcofZ(_o>Mlid4b|QsmlI8uI%iadzQ%H+5K5?hS8oo3u^8zMyP#&R8V^wyKqJIe1_a1S=iF(1?$;Y;>$T?S*Y!s)ANj$O+r;#dc;8bTmn5#8`*X`M~RxH#D(^m z!(xjIJJOyK`q9s0(w({lTX-+y^K6uziS<(T4;IBk?x?0j z7WHgYot9u#Yay9Ab34qh#Oz3uR6``T+C~(BxcN}ubm}wBJ$9lOd1=VK*fi433qJbj zkR;vtK@vh*BD|OC`D|=VLsZJ)PlKeY)!Pi!TNsyML8?QD&}rFiAY<2)65v!IJ8#9R3&B=T_n3?scQj!DM}aqqh;*U zEiMJdh+RleS6E{aIaABk9A_*o(Y3se5`0z^F(KKS;33)5l9s}S9hg&5y?}6-9fCCZ zSqq73c?rzae8Q4^LO#a)prqWhUP~JqLn%rjL+LezB6*seI-k;K^>FcUNC}*mEnt}$ zYxXI}l;~DWr-|K=5^l(;@0XJVE5za>&K<={gztvxm^H8;i8gd1wD&IwIW0VKWxHBrIv0>4$qVP z!-R6s7B$wgbBwmvLu7sT)P+BorH%{(ueSx~7_0ZG6O$P}mXMpqCY2z#OP2U?R`0m% z<0?TzJ|^j4N&;`W5arA+ij-Cnq`(%~6lOxJo6(>kb>Dxf36l<_kXrH82avU}FHx_( zT5He(@X)0mv+9*Dth&n!UQ2^V4Mj}XcFu}W-LX2ru!w>+KcNH2VqFxavYYz2jU>wR z8|$K8R2#%(tW%;XLcRiGtDvSR)XBGZLY>rLi5z$nA}|xD`nUW%U8X7DcVjL?tI&c; z;-n4$?wbRMY&t?Z&ty>7Y1IfshnB(0vIS-{idhvCBvub(s{e@qO_+(W`T)md7H|i^ z12uu)fW_69Pgc6GDiYHiL+Eb)T9H&jxhj)ZgMLuEmJ5MZGEPKN;&aT)?elrYUw`^@ z{ORF+I?F|HJ3HL1DaT~y67ZSMyc86aUh9+JQc8B%aGDlewiO>ee}NxAe84hKcy>Bs z-L^yy5Q8NH!qWlk7O+f9dJjZ!a9Cr&=2P!3=W5g_0>7?UmL)ZAc%Ob2r4-}IP4GTo z^&94C!DU?$qTuzjXZY^zTfBb$0+(&WVV-f>HUx=yd2@q@%LV6c#k1oHtKR@3oR$T5 zPmgfy@VH%Ya5GlFV)X$&CRTQtX7~^=Ifvu201!-B()@Ti<7Y2l;mwElxH&BFA>i@+ zgyTG8;r_0vbVKVpD`w4 zoTJBSn$u?(Bc8bhciRINc0m3+ob7@WF+!G5iwG8%{|UG_vDR#l}F zG6d+i-x;yb&gdRdwUClrjZ}d(U|amxIOnAI_|h;U%D$HFs^obH66t7nQTjp=>HNnk zLckm4Qa3m6rM~Ynk!*knQAlC$t8Wrq)K2P5x(?@J4PE1s z8kru9(Rdu~Ipp#J%S>tbp!AR^|63rbHVhV5Z5g6`_s%≫!dj>)Vh9^^gNPYA7L~ z>Yv(c(Lg~NPj3zrX6LZ_<~b+jS+pvL@gk36yOwprv-Z~2g{ZrqqqM~+QoBZ2sQx>b z1HYmqiI~dMvLSma2c^B|I0BS|gjnjbp@V@+z)Y7?W6oYL(JpQsqR7`|p|w8~Lzis3 zZQ{tbci9SC2`|faej%sePn`T}AqMqaRTXVyLoT`4La3y2O1`$$YF898)Pi?J_M2~q zvraEagU*RY542#%8lhB1>AR(}7@G z?O^JthJJJX3>%{H%v_>9;QDzi`{E0AnTS!9>w7@1NWqAb2Rsy8mI*ww0b=vu{`0)4uCc?}vEpg0Pw+%i7EYn;Q z%bl_htv+O_Z??(h_a5^!;dnd&F-duU`T8UL_;3gB1D@U7 z;Cwk_S{B^Q2i!e9V%=7J|9->Ga=^rdb-Ta^kK=N{&9dNWTXES|tls0<@dV1MnGgat zNx@;q(~RebBYr$THg;6!6y*Np7D}1qz9BEI$H`&l1g@9s(v|u#Wf^0lbp8XNG>jxQ zOTGX(16WSYR6zsQ0<0MN(pw3NNV0h{by(|M|bD=7qVejw6o*VxJ=tWLzVp;lXA!8U@J-6 zxdgavK$IVt#0&`nc7*j3aJfV!PmnIwe&1JDB(jt@6o(urV02{KN36rH#QHC@-?8)- z69MZMn;*0Ix>$V@Wy%#*aS@ud$2m4kNb_z}W$1>%maZ7msV#W>*=^&3!K@{4t(Up< zz#f-5zt4~c(*XMbs2mt_2Disbr87f zlMj6eRu=;t_ELD&ABj=-B&Mgr=`drN85CtWKd1~?lsrc%3?qXr7(qKCmzHWy%9ZnF zYyH^jcrETCoxmHT;N~>tkgE3fm)SKoga})fSx;rz$aT?k~P3w)3E55u>4$QLH#F z8eKwUpT;e_RvatGx@wWxk$7rfDqXP&(qOID1{ou&uFFROYHLy_6K-TeKE7JuRtKOa zCRj|Ga>(dCD?mlEBB1#GYU(-j`01M<1ynUFK)Ehi#@=C_8!(!;lx>Cp`&%t| z+XclKE4~d;%;AAqO;(b3V>2ruAbDgR2oHWk!Qw-ejH1@A2}$R{ZwpE59Y$AhLRLYh zYo=_6lVMi+I#AM~tE%2HNUGkMbC76Zs3&4Te3Rwp#REz~@1tP#TU~x%KfA?OKmLH% z&z|GvaKL$8F;5ddJlx|jFO?hs5+KXOIL9p6m54109@a|=Qe?)&4v~O)VqCnh&(YN< zhIC>FVaDC#176)e$GUC^2-v&_^Maeh0S}imm?x}ZOC7V(=W3Vk#EkRhj8`|$YFOao<&4v^0F*+*0AO9Ww8Rw;vf4#TCvHRnu8XxQ z#E6gyB<7?mc?_W`U~1r-gBD;ETWbNWQ$<+`t;&+;eC@YTNU96(CHlp1 zS~~;sS;$BVk&qb{sWq!DQ9e~TwbR}q0_3u8Fo6I;1ee@!F%e;n0r!_PLJT;E74ObZ zh?M-~TMW2_B*~wcahztXK6%}l2-D>;jNUvVn_#Spp=;B|l-aO}5ZAUDGAD`Nu0gj0 z<#al-T~_NDHgwJPzAGCuep2 zwL2b`actjDXC9t+{p3hHe$C>asAwaNvS*!ExQoV<@>a#V6j5vjE0QD^4nD%UvV|D|uau+A}C7rajihL4ZF)<)?*+ZeBh z`jncjL4Gfz6nY35QV(`?OOY5)vmF49l-l%G%dXd0`U21Xdewt4oJG^ zBL+ndnYuj;j#2e9m3&k3nd6wT%r2=Ks3{g@$gdkKaP0IDR*3Q#B z)HoU*u{vby5@f86sl$>Ar_u$!d&(;H+2~6qitRElKI(EVM=i6q+Z3VzxARo(A_P%^ zU}7&{G#T{h(`>IZ)^_^t_|euZ{hk$g*aDAgQ*#bwDtQ_CCo_%8w~;5cS1Z|?!rr4F zM6fW`VY<9-SMwO!vG201QlzEI>pTY?GD%7zZf6@6a7rV|K|6r|Frkskj>v`s4e=+7 z0;^fBWkOSyYpx+wj9#)Wg><8mx;MK0wtF2))(x)L0jU7G*d@e6y6~>ODL}e;LcIWV#}fpCWu7p(30nx5nX!eC zo_~i0Ki=Qr?&%RPZf+nVIIkOS4+jtt{`QN1#>IR5r{DYzAqE`g8Ot=|ysbFS3kZb& z_m6+TahmY&fBWZnd-nnJG~@PgfR7QUdB)S_f`9n>OZXV^yH7sFVP5e4`*--^@eYsM z1rOVb_v@N_$qE1azx{Xkc2`)%_d!~h>z?A`q(9q zdbx*9PchP%zVA(IV5l1_Tf*y(42@clf*7)*^Si3hl5t}%3zd>Vqa&WA)3fdbLL2Ea zXh&_AYk#C&OkypgHo&UNxyA>aW)@^;fEtS#$ECVQf(2TBBRh$?WEBAFslFUiD3H6P zn_`+vdU66*!qx@?$wL%i{eN~7RPBJGpeVT#lFNHuX)<9HP-rzwXbErtrzBj*RZdxk zZSP}CNT=ip-7!jE32Vb3eFw?NDA5&7nqmLNGR%^0HfG>-nsHePj}&WyW$|I|2Gkf6 ze=2>4@_9pr!=wQS7`txTwkA=oc2i1D-)`p-_d>*$A0S-4rB_pW2mVVjc)XXoN0v=v z>Y`B?lxqb_?`_BdA@zDdlh`9abV@?FQ~3-;7Er1IA;pKuJY;DX3IE=IlFCl9ysAAo z<}CHm^**bQpzHyya6}iJtC#pFxt}?Eu_QnAA_+Kq@JgF-%`e(vPc05d-4cvW0YV)} zIVeNT6wfiq!>a_83Imy|&k9&2nn_kMGeb#$T#AV4v8m2ic)D zyQoHk#P5S+9Ro&U{!Fv1sPt@UzWER%vO>_LP_Q2DwQnYK)Npj1R)B} zIdh?Y?ZGN_`URh^E-*Xn9r~b4ZP94pX$8tjER9T-2PN1#s)#<+C@p@6>;q1|W-=-0 zoCj8l(mcjG1)1!1R6KUWJTllX8VJ}LB}Qd{78L0aQ}bj|=X?+P_wsxiR1En(iUOJv zM_Pg99D-GVlcG>@3aDe_W#1z(#Doq~r$f_5G5ZM=Mm{s+U3-R>!4Y<+Wjm-FhVElR$LjK z8~U3K0$jp|gPX904Ts5L4H0wBRADtwPfDp#CE+pR#mz1L?#nOmpa1j++&G7yKYNa! zzkG?~yx^A~e}r}2aM?CItrtwr<<#Veo8^Gx#5EDP+&A+yrHl+ofujMb#;#b~j`NZ$ z1tYd?!;706eEaq-7B?YG5f7J?fc*aP0nbi1czAlkmp}f1Z|>gXfBDT{;N|HCAx50$ z1#j*?;P>x-K!^hV(;x6Z|K=~SOcOT0;j*qNfGR}%Ki~ZeW+J>io$#yIALGOM2{SRC zwhKAO0HYi5A`I(jyC4fgB)gLT8YM^lgEHODlXF#}LHRxwA8W9aL?zLtAsJ0wR;0QU zEp-tEin-Iz<~$H`8}`~fuPc8>*_!T$jhbQUk#Nz~Gs;V&uX3g?Nl5l3uVzLn-$fcC zNcv9M7V*!zz*>MF}QJGPM-K;X_Q}hZ5^xElLh9yLOa*vM%g7b#1(QMi+g$SawBh zPqnlO-}Il?4C6?ZIVpX})z{xC3FKO>(azhji<%6|0h6p>a{Y*uyjualMfD=Z^G8Dx zGKuxT3zUD9`cg0+7bL}bmrI^iUt$Wd%!@AzD3)K}v_;IR3piw!y#x~#w`3XSCsweL zdI6RpBx{MPOD9YKhLMfauCTl<7g}LZ!c}TdJPny5(#;D_JsX|GoMDj{fXvD`BKXXt zv-%yBJGUuxnlrMJXQ9r_BzrT`P9kvz0IM%gXy2pTLm%pl8WNJx)x6cSU54TwC>vX> zpcl-!7nNjJb`zUNi`9-C%H%|7h@=Fxks97aqRA|&6<>Sh+&hXebdS6eyc9&RN=+HT zfnBP0@shx{;)z%CX~*0wTHUW4MlL(8n(k@s;jYzyBt~-DO9>e{Ww$ciR6U?6kvZV9 zdCWPbFQw=D)^H##&wv#SR<7~FvO1T3?lL*t9u9bMbAyQ;)-6@=l-}D@4u_hY!_DD< zIs17XagzSW6e<;CNZri@oR$N&94JK~+#HWsy@$_C@b~ZErw~6P{Nd|w@%HHv|L#}6 z#%W&QgU8c(!*}<0_|x47{PylWe)Z8OcyV)scMtdY{rBJEKod3&_^&^_!GHMGZ}H;x z7XB&VI4@Y7!&mR$V=KbhB{`mGseD?Aco*$0YrX%^B^d;P~Q#h4x zePYHXr$3iKONq$KUOStnta^wA_a{JvAq>nAI7uDvjtN@~inx+2%WkT+sP2cT8dgf^ zXrx#r_^f_!0@x+Xy0aonvSf<#vm+~q$|J7Xv0B#8B`cI72$Q&+D4xQ+Ue8P0A619& zlu>z~>z%e3AQFnqzoGW^!OrD?&<_e_FPpv2Hc8qF?c7rz0y zRuVMYX+^j~ifOh~dPb7O3c|YFj6DQ2R?mhg>GqB!T6eeZQ5G`R9LZjJpS0JfL#L$3 zFV)kYiho!-oi&YuZsP^c1C=NyRhNECZ!$A@t42?4$cFSddSM0fPw;C*;Ha1`%6fiI zk_}z+Lron}Z|12jC#`~@^WWJ-4_mcDsT^E})X4$>s2*cdpHaSx>;755A>)JSQ%lvg zrWQ(DPt{3)iTY34{7jRA9$}KZhwDnHMd?eDJNuvloj8S7zHs14oh~c z<=@LW+$%lc#K?OKLd-VaxcSa&2134nW#0>{<)mVjwQWHovc0z|y_=N;Q+u5_SNe#g zFs~9~J>{yh)IFH|M^SYXNBzr{YGe=vVN}&?2_rhcs0Ky*YWC^{73~vjNHS|n5NBpl zhtEm0wKl_`O=@`_7y2OWb4=Z+RFQrlUDFMOw)bCsj}cWK7cmB!RT<^v!0VQl0_{iC z0G9!*N+6L`ArFZ5uO$^gwO`*y-TRQ1b}Uy~%9Tg1-G5S(mJ6ffO4o0`_NiK83K&xY z!xRXNy`VC?$~Us1Q-n#E;h~p&;hLV^=Zuh+cX#$NqnPii)#X}1iUKG}v$Lg&F3VjT z3f5%2#dW6V4Rmc*rLm(=htq^a@y-?xbR*C;L(y1&oA@q@S;~`Lw~Kob6e&c^GWN1a zuEFErT#d~6)y*wFIo)7#jKehHZ@>K(_gNxc*tiO!szmVusBpDBPz#$SRmHe#XzQ2Y z=KH1IZl~(qm>3chW5~seNez^W0(ivwnjN=NEzTX!Puq%Rn((iG`70czBtQP_^+$Mh zdkdcvexEL99F`>k2r=UR{FHtNrzInxg?e2=fDdUYFX!~;J$&?4`uyjrB?VscB z@d5XjGoCFoK3oF4MEuR4{t5rnuYZTbJOhY0Zx?)j_a2|$KF8g9!55DY_%DC_J^sxv ze+@{)Kfim6`)$SVUcADQ8C&-IiU@9BkKKj4GEV z>9Nn=Po;R~3RvYU{w9-}@#p^(BkoDQp>}PYR_n+e*%#aGVI9RU)2($72Za2$=M? zYFhHTD10sTjUmy?!Ka!D%F<`fgIijHsarM`6RHz@rB_gj?EqCWvx->CPq=>64d0&D zGCmd+;yKvQhQ+#LXvF9j$3;Mz)N^3D`o+BOx|Jq9)ABm=Ui&}`Q`6J5`nm%Y=zP?o zy)b&Xk;;D34mQyyJMFQuZL#}*smWRAnNyGmx1}{zyCfsOtEjp@>{};w5WCEq@P}@3 zgzAPfo~N4lXk}13fUWXnruUX|k81Y+Obj=rYrYLvQC)ka?tx0F4dF7@A|Rc-k*@pO z_Y@GMJycENToZ|-O2(7~S|tgjRQZnd@#tz}ZFMZ70ymljOZ%0^WOh_3Pw#=hX7B7b z2pJuXR_)Q{3P`ugYIccwX?^aK+woo0e+~+XcfvIp+c6`$=2NRuXD-)`aYz4=(ffF{>iXUb=4%;<4rg(7Aq_^1Z#>ff@m=!r}16Lng< z!Zz*2%8y{D1s&U$62)$g(vdi#>)eW;j!IW#1Iy+q*H4uW$?A!w`n+N6=Qp+@C zF+Zz4^A-5ed9epwj8fZ_sic6?HME9O6;MkF&x5p3vvuj9N|Ohmt!E>EMxCchgT_Vm zBWg}0Gux_Uf3hjywQDy2uP5gaL~wEruZ{=2SPoz&{QZwV!b33U!)pr>fByP4{?lLn zB|@$X``d57$pJfT7SYm^UF*V%KhoTXZP}(P1d!-R0emB2g8fsGv>(nUdPU z5!a=@sN+npZuy%cxNIJ)_jq_Z;}@@9PkN5cg z?hc>5dX3XEXT?aaz5!wL0q1qa{nJwg&)+^i;`X>;nNqLt`@4Jmx9`5e`!%&1{Kwz? z4y*V0=G|LdyvMtB#eezwUog7~FK%w|&4+h**e;ML_`7%S@W;D5{PN{%yt%*2o?PG@ zB0f9Z;BS9?gFk!u8o&A3r|{mxhlqDikJ!A&>Jvlwe}DZYo*fRjT}}|mJ;?-|<{9^X z!@>+71DGA&UoJQ~SHH8T&0}H*9|If_Zrlte;L&ezOn5Obc(<;&Th9;(UL1~if4NkE zjmX+bi+U^he5r!DQ%<2=V`A59cd}YC3-{>M&+!s#PJvEruA!Qh42p<|$-kR@!l_v? zvD`u0^LHkvY?MVIl-XD!EN;dcJk}ViN4fw_TlAR7BO3L1k#Xozuai?Sw;`n{g~HY&HBGKw<&6wcgvRP@gAr ztA&ZGmojEKe9S>nT7D^#*v5HkUUHYy$3s=o5Gb^c$CMbF(h9Y2RofJ*JU&Z@X2*5G zNnO2+!|dv3%1n4Vhx%@($u+=IlE1q=ctd8oLX2r}krJRKhM6yaw`Zeurko7UM&%<{ zSra4mY8iQb(=fH}Pti<8mVa+PVwzm@=oVHHV{)?4=(UdK87TXn)ZJ$UY!CU6zAp;L zVhF{$WYz#c+IybtfwOy;|4s#fZsaCZmZ7Q?@&MG!+=@)RsJ57tIsrK_C9_4LG7{<9 zoxt@PimG3@9fxEW5oMtH+y}Txb$o{iFBdh`e>KaR((o^m? z_IcoH=ys}tx%l)MrhJIjgKU5!b-1)d;QS2G=zMn;rY_=`Z9&OLcgUeIMSd$9xgzRO zQuV88Ux3!bCOe3UXjy$`dL|^6$7f~9X-=J;2gi8WVkah(x*XSv7fEa-Wj}vmxBKTO z5p~v|&Mz&*ZhWVs(X2ZLOd(6{}pWoc#!-x0veMk-=|HGgC25%qk@%P_-jSy2c&2K;bSzcf~ z9LLc7PNBr@J?^E9~lE3uEIZOcl>o@;`XVZd@4o7^rT=2N9n22#;##8R` zm8_g9*BcYzG)-0hJ5z&O=bXg7hJe@0f;Uf3*+U=k;8RGM%f9j`V%lj=Zo(F0WrUCH z>a(}>y2lbKOJp(?L?SNP{**wPy!2-Ibg8!6VytzUob|{~9Oj8|aA}2JbK-PBgv3x~ z*CD2vg>n@|xh~I}#{;(f8Y4oUF-tAqadLP$FF1z)FCp!b05$|1-Gm8*hv2g(nsDPX z*-A;aePYIu9k$%zEFuUHY_auZFU&Qq!^fBzU>!5xO8`ly%HKK6_8KNPj ze@R7gi}hlPj9_jLPLh_uiFCmc-MU5l7_fBkICVlo>LwoQO~Rsa{(g5SEA%y8Lt>sg zJ!_zUejoNx-yOAqtt~}e&C6AZq3##nV`4g4wPDIoQB*R|JQF`<8A-AWlNfM9fA&(@ zYuYu{cu;p=5hV3hlN<+XaqxKKp@T z?B-UXK~mYnK_s?QvG;@)yP%8-Py_TsYS1GkL96dW_d9lSJA+Wl`0-RvGL3<6yPY{& znb84CE!}&+LZ>p*N&z8ziO{hB-bl#HLZr49bM425);lAWOiLN53O7aK8+oh1paU%h&)r90cZzkjt%-u@UH+PLFK+_5f-(cbPAuQ#vfDE83c(GD0R z)RA@4*&(iS>-M^-$FvP&+zBJD*G{P!lgf;>O};ES8ZVV@;WQFkxr#v=F%i*jW@Q7j z$<`N={vZ16i|(;dzsh10BI(X&*_Y@`14J35?n|30ttEzy#iYt)*Z0e?z`hi=QcPVU zM0Wk=wi>Hd9?f}3C4G)*KiTofRgQdB7Ae zPz7jojfxgORfg2a9@^|h*?%qqT!Kd=;GEf`Nr2Nd;dY+z`gp)cHz$1Z{26}s;yF%- zCCMyv2kl{=5he4X65v?nNo4=$8dB8P#14na;pn*fb*Z-k$zHe_^%tp8qS^OV6f))9 z&Gm5abBpeffN1vq5OgC8ODgC?5reC%VU%&kk z58H;#`xKBRg5Ur67O!ufb#;uuxA%AW$FIM_;-;#0TQUH0x2|xU*zSoLYm7Ls!-*Z< zp3gY17Z8AN-oLGY#Y~K2zOObPaW89L?wW<9{C9$``$VnuiD*EUfOFrv!<4I!bwqXRRJZUz8s z67qMG&XkFZ^qO*&Cgu0QI8BVjP03fA7xf^5B`4%Zk<>Sw<@d+b#8`WZefA#(DGB3} z!4|80Eo}?4V;q^WD!Zd5P`xBc{gSWk;?J+D(hRZ%N!QoHjH#y@NrfD754>u`k7`py z?p95q7-a$O(P~`lKI{TQOd@GlLP0VH)zFSqMoWoAb}WQD%&mE_Xk%KRV4`LdQNzC} z0a8*T>4$oSx(Z}K^mQ6)l~O9-i}b)m->C`g=&DCeX=Pmmh8?l8F<`fN61_)9uCo!M zBzI7Ln4X-MQ97LzUIe1pd_mXZldtuOVlOpRdu94TTl#(69J0D2hV+3acG?=G`hB{x z^iz!t!?9u4yCqqtdz0+$Rpz^HK6bT;Q9qFFEZnp=fKm5ytVKYiU&y=fwAU<;UncsU zM|S?Ku`hITMyqj_om6PqJ%0x99)o@^001BWNklE1 zX$f&?46S0<>}qR~1v1EFdp|mCNTOg~TuTzj%Z(2yyrhJ-6(wa5!JLu*;v-E!Kufc2 z))u7=D1~29OI=S)LT)*&!%*|n zlJ>eQl@V$Blt!hL(3RSsaTn~)qYf&ni=ONN0qSJ!EBuTcK*zI_@kE)rp)T*C$jM8d zNUQ-!Hu;ElN%;}hkS=$izxdknpCyEuRR?w9*28Q=JHMebdkO6}r0*`l1vI0SxJ>l` zvH^&sl920ZOu5#DX#hTAnfj=Gzoq_+nFggsNlq@$i6AinbNMy?_VzhGd3J+|8K1p+ ziJQZM|Jfn1Yggy8Y*hxaKU3&@KN;k)xAzJC7>EKLpQi16lo0loV%RWkYHWnP%D5MhlT zGZ?4Y0m3)~I5@_0nec8s-H=keI;NrE(1cZyxS&_Pzsrs4nxYLIovPqy}lse?)qQhZu1I;3*nwOX;yb zSUBZ8isEDUB2`*Fb-f8zemmIz zRLB4)DQgAf0}#D{TsqXKpZOK6)Gx;6xg#AwX?#@DxB0=O7}|+I?6ND^k6i9p-kR*( zFEF|qNezI*=O)$Sss6DS<|JgU2KDS9oc$C-I{avmjO)dWt`AT!iYQTg66w=@I5IZ!-24K=hOAl8ghA{;tT@%bLVt=^sipVR&a~ zP!Fu~x`dT?%hbE*w=P2;wJo_8SXCZ%MS`+F?eFi7qk5rhV*mDs|+LIkE9 z=+(Y#HXv5NSPkcVr>?Pq_GHdl-QcMeKYWFIYBJTGlr?H;&}A1dXtnD986N?|Vy=9O zby=mlFm^_sQJzeD(s~(yTG8o0q_Q7&x}7!CC4-|;&}RkLVT{OxI917MURa=0WlK|9 zuF)z8L~*e+NKR_+Ba;|bj}$mwK8)I#tmN0%*kCAzPgl?waI18~_h4lQFvdNJ_|TDz z@qqvu0dXDjrA~1TD(YdTGRCBHOPXAthO2mdx*GG#)tKibF()Q)EQ^57{T%@uF@TZ` z%V&K>vWpN6t67k=Ko#ee2FXkXyCjPc*ZUMkS{|iEk2z}aZ&lY2j2}E@Tja!X5kv(t zEj(p!VhXrAOb&nbi(jO=mU+U{dckG8;Ic_&LuZ#du$TFS&2NeMmBN-XeB5xeOfXqC zAe_BNhyfu+_!#Ti+5EZf*+|$wzlVjsv@2YB_Lr{bJ=r>$dr?|ny)+PW3lT4mE{7YI z1)88?RmCdI^_*A>i+ z7t4aTmrL%81=bkhV?c-@ha82<;QAPGw_b3XW<>E=*ujAj#p7{Xad0kKEtqg1#*@-bneMe0op!+m`*fX_*z|yoCsJ<`mX7Dt5Q{etqx=l4RVcX~yCh zF)|Q+_TjqhGj3jErUeASyY*71I!BC&fVqIkf|@C^h6GkHC&~QGF0t#u*E6~VP?ccA zAQ6k3>iZDcX~q#U0eex>(n?uYExzPL=sh!~J)hXMpvh9V1O{g-g;uXPB1{&Im#ZGS zVj_;BzS5W-(cB&s&NY7miIxxowiiMw6G|jQC=OJ@pY@E%5bDwY8R{vm9T}+w#i?5g zyOX7>PEzn^KJ;mC~vQ^lMoU`}Wew0ZF#<8xoz{++=5cl5+Vh zYxM|?vQ;g;E@4r9x-F6QRrV}qlaG^;G>TCN>?~S4-pD!jx9@H+hgkqN_Sm$U+AL@_c793quGP5t0y22fIA zJ)NXqd-;d^i%dmUW!W`=(5-O;M<-1UFrwsr+V(!VOhXmNo?TMswc>hwz|QrF+|NjQ z$gNUrxBVVR2!F`XCq8(+kvD~z}7N)`g@1L&HpN(>b`x5Dz z1v(XzE+JAuJvGR2$BLr8e>z1Oim|qU(2f>+r~A8AM#*j{nGE;3lIlq(o0J9&MX>>P zQL(gX@Dj`uTa_4gsZS8buH$Qu!S;+uOtc7%>J*U<^EDe7`tEYN3J#0LYRw}}Gi}TO zh33O7p+jXbV+eviRqGNgyB4#~JwGaRy1Ru*C1{s{lL7JL`!Z|%&OO@K@bnC>2`DA~ zEKZ?lUb1#J1Hsw;SD6jYEDTAT$3)t)=7?2ITnpeS0Xkl9Bi&ro4csV0vsVRz(G!nMLSV_cbnd`Zlf?5Mszu`E!8WhBoK5HLhl|zRR zfR6O2gZ!TJ=n5%Qm5#(U>AHkOmIXKFGrG-CQhR(u{%)fnElp$tN-x2;W&mlbA;;sm zSeB%6ot;C#m+#)70HBL%INypn*JnjV1MLz@=$R=3#>A}0h- zotCiFH&)<2rq+Tom&O30)F$z4SwQErX*r3A+0rCAJT@mYBM~o&9d0sU&H=o>p5IcH z8Vj>wBqDNHW{hyoVW!;EEUmt7V#W-vJ;E-3c56sU+7t+rBP^VHe2EBC3IEJLui7QZ zC8>6PZX^aft@M)$5Ok96d6HE5Rv6r+oD&KtPZdcK%)r79YxKGPC^6fK^^KVG&z{(2 zc3Ln&{Zff`7?vJf6_`wh+VxaFRDd0och!71XoBmo4#<^$PObf>kf`HsFHAq`;1wIST|%I zKwEk3``=i%tSUd*PXXr@o$Yf?WPgs>_;GGOz*hXcWbj>tJH4oz6rh={XHkF$z}ivxGI3)-zMB z^8#8TxbY!lFe$g|NvFaZ0E@ab?f!Px9F&#!_1EK2Gb9~{C!+!+O7o-E#ATJXyQiM8 z&jz|)!?KrHzGGme{`IlyELJKS{l{ciuW+sVQ*^q?jyt6}bfxcEMWcNSZdd+vO=;&+ zgOLHOpj0Z=-RzZX(4C@CKkCT zbB>Aipmz-R&@5c58#`tozZa$k_!N+WyEnll3`&6yRaK&rWI<9>lv6^nvl$EASYT1@ zA?Y(tz{t!gA=l*!nWqr&U0#6C+Xc%scMxLnrbe(Z z&N1eWVygaFM=6ZzZoSl>g{*R!D6d)+TRVwLvv*|Abjr$@l6YKd(=->cNkq(+qY_@p z(jt4k2v{1ykyzNY^p>#G(#?y!l$UGT#}r^SFPvFrk4L{^^{FR!VF$-a1#poq1K#QL ziCel>PSc#(SwWDb^sNmGB58@06mqx42rqzym_5UUpDoAgQ4TS^*HWQ4fp7_-23dgw zYeWm}DpGCfGdR-cFz3&;+3i{uLWq*j!;-~9$p^1|qeP4;SNBB#(=~=vs?V_tA<<8y z%t0{cFE5%_3Y9PBxF6#q&gS1w<=2QIc}a6jZlxD2C3^@bY;ihO{9{b?PsFdYQ=c> z7Z{CKJZYOmSAk^Up`GqXeMTsPb$8wWX;nF0@mP=jV$^$r>#OF`mjxx4R-VhAS|se~ zL{)*Oe%a2YMc2oKmOGbN7bb$2kRG;K#vNl~>$a%mZYn9cmr4|XjF?Oif|geH>I%(1 zSs=-NUi$tizgxOMD7{3mGOoi zdas+E!UCyX`$2#GiR_>~vJ3obulN9jN;}`Urr|dhmlY#o!qx}?G3HDkg&2)Rf$R;e z{Hx6tBxu_Et8B?+K_&{FH_g55gJTNWRzwG%%vu`E1(YpMs3DK&BIzlp~x(e zV~FRD7guCgfug~gA_TAgYZN7in52E`A%3NV655fu4d;LN`DympT(|jpVD5U$+8I{r z$6_lO%nb&ut|iqvu7{sSWwOWez@NeW5600W4#DCXMjsOf>aHAV#X=hdJ_G;oD-hb zr7{qT8*|PK&RJh;i8}Z=91+xkM~AiVUB(?9PM^CWPK?m~n;cldfb*9qBbs26g?4uC zk_`cF&ku{#tnM-8)7J?JjO+b|HLm#M=g;^*fBh?bf4|{rS@7TgoBtBqzSsQOHAbAy z7n~|$OSPU`%`0FB;mfw+6qo8+zC<7}VcB+V;`R-Z$o`JtHDz2(YaK#qn6w$b<*Y;C z+)Ja_F8&gWk*4}x6e>1IsrMV_s*7E0kX2l@gEh25J~X6BAmx<#s>rDa}1>jeWzS22u!$B9|Q8GVs`#tDrWJCY4k=zUVWQ*co^ghkmAtj($!% zA_3?AC8YYnoY_qd92<+=|-Xt!~Hp3n1^3j*+LnAkpub{X5Z4D?@YkGP-XdO+UJ9_aDLSAS< z@UCQ1C(F`%!?@R_d9yRfK@ZuxgZrWMOuByJK$r{=Xn^z4QJ^b2N;K7*lFJo7YPI_& zv(nb1(p&c75VLUUR>p`#)BM&l8~?r0Eu6u$b*(ShtZK;)B9 z$p?epF+!+=1SWsI3;!7koStIWBdIiOsB^;&nIpVMqJ%37$D(|6qITp?IdTtL?JAD0 z`9K6vC+Fpum*Y@?8cnh>H73R)By-npG-qELV#+GL+2!-`0S+A*s?_CBb`43@-%u~x zI3OgRdQ7#rt>}wmMigC3PC5mTW>%yb#e@wO#S}~6yfw4QOld;f&nZa9;HH5ALc)_4 zkva$BaV-k)fC@YmwQi3+P-F+0K;I{}VWhYMq$i9|o^LlJJT~e*(`NKK=mxQ#8|@hb zzE2(Ema4yllS1?z@0fcl#|t4n3QE$OzxgJnH6b)XjJ@VkTK;dL#Y-iP)_p*vxr6#> z8OU+C;1|=;y!~j5IU9;vz2)|eDMjfRtA$~I?ljGG7_5oc?{+9yi!|!LA5}yqn{#k4 z?-7V;0I4(9#9%&2?fj#Ov$z>SW!EE@U=OZLjv<}EX}oEm_O*ePyfF=QLmknza~O2l zSswvpa#xqAkGLXW%esolzRMZ{p4SCmUoQC3(-XdWdWZkxH~)-3-foKM#!$UVDmXGZ zBg^_e8MG*dC@lnhf4k%5cGHGq-;KGbMmW`I&(`JI{yl#D;XVHSx4+Zo)2PIs zZdc8j&DkRN6fvft+VEQf-mNQ6fpNB+tauycD3vnT?7PYJKo_WE;(IK@!W>kPrg zw7DBIe%Lm==7fa^&kGPm|ISlfG>S-A2Lr58S>@Rd1S~?>;SFJxf*(q4LPh%vkT9qs zvgTZG(u18DIcW2IUKVgR1lsLCL8vlAvKwBXjI9wWBc=_)OGMvd_dPSfq`##a;I0TC zj5J(lAS~4&heYYpaiMnOS+J}D=S#$P6AuB7p+mLW@Y7%_+LC}F8OA&7IORA`~eWLi2mmjREch#$GhaffdBoraEx!PskfXU*12_-Ldc4hUr@?Hx>>9+^7SdyxgSsD>Z}LQ3kU=b&U=Uy*87X)aM(q2GXod#zs= zr7{S~N4L&4J)Im3Le6cqQHSgBBv5nRW<}akW}TWvowQLGoir4515wGlIv^W;EOdrz{f!XQ(3I5_W1HeH3xOHfQ>sgD%NgH1T~vz| zf@-xwj~a&nY)Pw zGWFi=o}k1=+d3_=#nRN`Z@y2Fc*Fol_hK~1_lCZ>C@pWC=7&;#L?GxI=*lE=aa&Yq zsGZZd41>}Ut{%J^WQH8-|HwxFEV4}sp&|q#z`8`-Q?6N_ zgZ_N*5)jizmKdX8F4m=f2zC#}P_Zmr3FhJ`vRMR|sGsSy1Y{CKDuz6a_ooG4_Joh? zil4n(@$quPU%h|E$9GS7cRu0ie8T0tVvQ$cS@6d%U-0|e73a{}4QZZ<@DNph9D8u-B(pXOlgwtt7%8b*xfYLI8?5=NaOQFM@}zZEYSuDr6vhP-SePLnK^=+ z{#@y|s*7DfxS8(u#ZcNyjJR^bHM>+O!e zeE$_*uQ&YTx8LFtBL2s}`?rW8BIO5z|zPu_TtVTf(4eLZS}^7(~5uk{0ZOP?=S*3vH-i~+%9FHZbOpA;E zi36!qCSD%1Dc2M+C7B|q9p#mVMq1{7_kftWLc6EwjRW^KJ0|f|yyN6lYT-_ORnj3< zeXA~$Y(8Ifc_S(k88m`4xxjdnvOKaLM-5)@1~R!<>>xp!d@p(|%$nu}%Y(e%wovInR|91FdUFbW|F2N#dJf?b zEp~#LYgA^uv{wu6`67NyNgq*jag=s*RX8+3UJuqGJ=FlHAH>mtiYJG-v;%X4=-@0Z zGfFufDQI)rpH#$yJfah%bJ1o;L!xf>=AK&@-9pnns1124$RxvH#2LSoX4;!Ri4?hk zPDV=uTMDt@V@K%7`4&35Iv)p^MJHK2f!pT_KKNangQ&wbXwav5l&zDIIGtjcG;(83 za2WRX;I;1c^F4?hk~y2Gi%sg{><0bv5Vu#*UZJM-r;g>)Bgyp{9frFyTF#BpOXC^Q zg|i+Xf@6GMZG)<*QKxe1?vPX$b0PuKNYJ1*jHv@W>4)Baq~tv>=+Bb99uRAy;~6mq z0d%Hi(hrn<)Z7>q^rjp|=#a=z_MhJGpm_?E4B}biJ_}1Y<^}e`VVdc^>8P0>H#Zj7 zKvJkCrfNv!q*W|nm*|oG5Qdz28ilSTtOF85yp}*V^aH-WtoR@PtG~mtm@7SVby2?F zuK2`PT-JB^yxl=v_A;3<(V%PTBq!EvqGbVRQS>hh8~ss-qygqAYY|8!K#WGIWoxXB z|G#IpJmA)N28;1wW*c&7^ItRAJ1LtuMEp;zZznAaMBO=3_WEEFBzEU}qk8H9gK11s zPz@qn4?e&Z4sM~6O_5JsjC?NPZfAN?2;r5EtvQ}o_wr_aduE@ON zn-3qbM3qTq&Pb9KJ+${B==}*$?e+JsFZhqY`yD=9F8IZdzQJETy~7`_uOM3RVO{a< z-Q1r+*g4l4CfIimM0ia}8-Hl7aUkGAn)wS*JrCEQL+00g2LpJu{O7gQ(^0P-8}PJS zLn?Iu46LHG{NSPhg$0Qio4M_`M?E8FJ(tV9B{3bb)wL{wXalkr$=HRla04n9JJSLM zlVG4~zXJg$i2!Gd8DnrYm6M8^uO6c*d(OyK^L<>OkimG}lbTxv;JhqqWQXR!ydoeeJY&&i0T~)~Bt+dFA4sX#kpi}q zkwhB*R`i+G0iBrd30XWbhnRFAvzQLd&#)yVKIDn3nB*Ij`NJK}G z&cS$O)C&$Cl4CMQAI~|V+qtackzMBbC^8dXNrw_0k{L-RNuvYY>}>pdM`x#4l`$HL z4)m^mLOpTz@ea+SW2tyldqhHq_w4AY?X$^S@lrUtj)orMZiSRD^JJ8nQ(y22RXUkx z1OPIaO2~u!!tPX>KtO%B^r$hC2QfSztnvCJ5t#~1rLU~}V_c3@$fG439SXl5rS!J> zs=*$tAMv*;u`x!2@+gY2X1 zwz+f*4Voy+mIZ-3y_hB&HUI!107*naRAbC2OXz1f*Iz_OGt@pG1{p8rYEG)NRIj(R;RH9=>h3;d%IgEbhzi_ z4e30KfO?#GL`eE)LnazmzmGO;#&I1;9pe6}R*QxvwzG^I18VJg@Z%mC2ahyEDu+F9 zT-!p!{`1HrV6?xc$y9mV=g9_D+!?YJ#FmI=)6i<37R}L(DA(&+^>GJkaA-&;dY=L> zDQfi4xeeI3Z_-pF;Ww7{-!p}48Mb*(5+BSZkBll^W9wb6TVvGjU36}lG;z3p98X8n z1jECBmqwG>Oq)V$oZK26DY1(oAZ#>bL9hD{Rv?d?WBGG-9Z}}C&#|Gf#ptW}4Fn2E z_GeG)Nt@EU=E&x(&0~zVMp*FumoND4daX4>pK(46IyxV?7C}`~6QXH?BN5Kas!c}L z4mSwg;8M(hNvXLd#!4WjqZBOW9$fBopa3GlwyTCyU7#Bk-k+s`dLz${6*C-rEDjx> zu z+4qSExMgkBwzT8-pFZJ-+ZE5JRrSE6jc#Jic75f9kLv}1;GVgjn`dJbK1Wq`vKc1r ztb_GP0Z+?{o!R;n!4d;b5+R{57}y-m#Re`Kzq8oz=gt`$>lw=R>|z9o2v9?IfdW zsM4TS9U$a4+7XOYuR}rYazJr3@gU6jUcEGLG%q2d#}q3Ef%guL@>ohV(Az2!=BKs8 zdl$14uLq*hj%lU^b|<5V3`G**;1Dx5sxnT7Ct49Vxs;i>QKWP8ek7r-Y_38t5}8y; zjwV1YCZP`!o_#nOD2g=q%a&%mR`2^G*1^nlL?+{USIr#;ET!0~_4+VGq293T5a-cx z)Z|!z%+{~?qComiw$gX}z`!+mW-aKQau0@TnD?;L0k%0I*U7ic7#wbIk;#70IeLwL zuAU-0N*u~+4Lt-i(_Tr(z;8OeZY$PFd+b�{C`5brleDVQo;q&9SfK7YCBtjhGi zT*pv)?qag%K4+lG9@)k24W^df4C@ULxGxP#%EZw-qUN;EG_K@n`5p;=?10~Hp+HG= zJuSP+Ni42p`brPZP>eDGKP)~V!R>Qp(Z;5D6tr`8P?6d~^!ho^iYP9xNqRkbrCvi2 z4dPgALtxv8iY-^PYq5q1Ak}1~5}}f^Ix35>X+?`1B-DN8kDp)gnscS|qFWQv*h|3= z1*P7%QfxEY{`zi>cz3xdiWXw^3W(-T8;Gz(HL*&0uTh?WMjeHqf4&mAJZJqpyU}+u zv!Rf-zrhgKt_+ofU)^Ya7-$@%$zk2MSVC#*+lCrm&s^>dyCx!88o*^;@N!Kp!n8zH zRWK`Hspnt2AphMj{w4nM!w3AofBY@}@8ACxzqwwKCF9=_;eYvuf56A5C*1cP|9pML z^>+z>^A~@GcjpuS`SWM|_IAaGWkoa>Xr9;#_BcKpRd;7I(}!3 z@dB(ttgIskDAXKYfP*w-*36@dlqtKRV+e*4S7SY+{5!;Vp6;rYE&ko z!-~SF+F23U1kt_7r<6!zm32}n6dkcxR8=3Pmz%X8me8V-sAScLCfBG@W~Bih$51|I zLWaRR`2zfa#P|^IcAW8{QII(|XLM-x#u>v5RYvP*pNnc=qBT6yBM~A89q&j%dV_t{ zYjC@G3h50k2L9^QoVij|>DVMaXmW}ZXNRDB2QiUH(}qbA`Iyf;^@I{R($2>M>!EY$ z;J#XD5!qu#Rr@D{s!-nU0Ox5A&ai|)HY5dF0Q`fiv8ZH{djBNc3k#!ZypZ)!&-KlOX{A-V|J=^ z#D=4%06G`lquaam)16j~4;}nGzDK%AyqMue2tD;gSU}OU9NOThA}oE{a8H*eK(bCi z+kVkI&R3J7AtQ9LY)37=1WO>?Gq-D8J{!yt zQFJnK^mi0Z@54vOac1i^2Y-t0sqY)k`WrPnjXFsTKu3-2;Hl805^wCCBTK}gj^y$= z58iH7CoFihy~sGuqdu?q_%Y;Q8dn_h4777&QL zTx+7zTsR6nksEr+aE{~u-~Z7pHKIfVqLa(WfqgN{9>-ywVVGZ(tlP4c744uueoIkK z^hOsyvbZpt4@*VUM}tcuD~4mEVMfxdQdnYwmurn_5>ia9oc&VSoz|cp!fOn;r;J^4 z9gdtstSFoo_rd6#T6|uXh);W~mrmxc{IYL|F(6QcAR^_gG~%R9OJ+iH#zaC^_#POF zEvU37zWL~xwr-!|ipBKWEUYXUu%XXNNo6*(A5z1kGW=6rWNdXhJ7YK~!zNI)Sh=X; z5HiPVW7yPaBx<@;8m4?rhD6m>xOf&sLUq0^Zpn?s0OzJn)q|m9(NU9zWnoRKKcb`5XG2{E&9Y4C9@mC+ef{5U0 zRqC^vIGJZfZCH)IZIU6)>t*|_>Pr;u+7%NevWA+V+10h55r9He^fah`yUFo1E^7@+ z4PR^kND#1rHeyI_Hsn{Fem}ELftGQQX8Q((7C)%#I$#X}ml%-jEJ`*|Stx)dR}Tt( z_AJ86&gkL_y@?=L!%OB)eHX#mHHa*znYd-Jlg&xGGSz#1c7Qc?%-ob_3^XMsuglMiduD81^dWRCF8$3v;;tZwoD=#h~k*KuR1 zIZF@6RqKyvD4BX>htOA_GHG#q6iDy-+oP-vg>7!h;);U~Nt3>8K}Y*?jm5}pL!)KW zfY5a&LE>9!hsMwZ<)#NkN6AN|o@P9_I5~~eZCQ(eXcB32U8kfzFu$XZh2`S%KHWqr_Xgmx4}4iRs?%T=s;yp_4DXbq%@c> zn5$zAun&r=m^Dm|Ccr>LZF{srL8Y(@X8Kl8h~OG|shOQl!Z1cSk*B8j(M@BVa~JCo zCSJ`VUEBwTq@aUm>JY?sw_VSEj~N zh$$_(qG#R-c25bXWief=Y93=$8g6mGXIAqvJz8=#5o3X6kgCqGDGdmlMc0(S8vyJT zEw}xY(9wu-V2>2SmeefYay<);liZZKpz4Pq4(VASE_#rvF_u2ZB7)a#!xHqn=+6ty zHgr%{aL+od#rk}|;U9kUPoO~fw?FwAVj%qMAO9HF?S{Yp_yOPE@A&QOt6htL|Lc!` zz<1ZH)BG07e2WbG{g*Gunek=c@QV-cv8RO0j1T7ve)IVYHepT`WNF0>L%!sw2?L^qfoePyI)q~`DS9W| zBOEL}qR>(LCfeWbJzGIn@D-tfS}E@OKWZ>AQf;ZT(w`x~GbDO63Te%xOpHPLCQsfp zF=vi17)AP-xUWAk0;SoBD$H&**2 zBKi=ie>he+7QW&%tKP||D;CHN&E3;nm=Zi>xf)zTu9y{P*mkJhhfLN($w+e4QOHaq zDSZqo`up|vAOlkzXZ)dbnT(T&qA>itu3G{f5<`1kbJJ8RB7#5-w{vm;|L)~_muEOs ze}PaN3Tl%Khnia!pwZ@{IZncg)-qgS)(JD2OSFh{MlTm6Oy)o~&5m{Grr$qhJ(S5c zS7pBo&Sb|EbYv=+p{`*Sdhj^-y^S9H`G~fZGu^&RNqhoi=5=R}XVE~5m62HDcgD!Hv`{~5E>kct(SwGb%oI{h;n_qa$(Z?K;9ub7+Ew6COx)21oIkTGM4si_^S;RH78Xy!fOn;rEHRu07}|WBy;Iq zmPIx4j5frVBh|jJRKmEo3Asc9>IN#_n6Da14H}-~H@o_@96IpRt+lI5Fc_ zFJEv9!Oo?oZ-yw-;M><1+;YbE+lKt<2mFgqKj5}+_%2g;tH)zaL`!6Q0UjKMVR6c#tbG=Q!`6I;5xif z=j#A!kwIiB(ZQq|8DJ!1MyQcod5uDq&ZaFc|CBSK)HRU_9Mm13Bp`}5zlB|qSa4;= zyG3=(YY3pgH3Qu5uUyj-N*tfAcLdb;7e{AasHIa};!@A564zAYR_&gZd%F}0m=D^f zgIP!4T+J*Y^$leg8}Y_?nYspDTxQ%r zNglEV-=fU{s4jhlD#2rCD{f*=j|u^wt0;qAj7*J=-x{Lhpn0O9)flx%gS)eDq7MK) z)kBbFaMT`Ach{iQW|1C!g+p522%Y!61NpIOeQnRj) z-%B2&y7X|=`lc?)8*6no26f2y=Xgvzse%)cy!F;ZREM-uhE&`S0;`c4(b30*dWny} zUUl!Gfq7-cX`fZxmPXA;wMO?811@pV=$j?tvaXd0vFD5}C)_yWno}KO75PSD#=BCi zN<;mZpoTgm#VD!8C#n8d3-!|Qs3UX$L-65zspdPyT{(sp9rx|)y~emKh7_W{$5Wz_vkpUv z(X3f4PN|qD8J$?VAs6BOa#Ex%2K?swikmS0`TB}?OT>Iw{H$26N z#DW`VTqsskC*zc00pZiO;Y-@_B_;gL`*#3<|Ml0u#x*7U^xZqeKzP}9EFs_&V{OQS z#S}$z?`C0$Xak(NopXsbmJ1+!SWg;tm3IgUR*Kbx>Y9=cPy#ihu3%9}fe<+W#HJ%} z9uqpiQS+z7+LR_RAklQxp_)>ah@T>93P2q?Ro$*J4I=5^jMRK5ia}N=hM7cIqMds| zz@o8)QeL+*cXZIT3s-mSV%iqWG-?y6CV8ovV3~8YHO*N{(3n!A3v!%IZ(JOy0jY*t zWf7l)4%k*3{Ia=_?;`aX>7os#t=eGD(R+~>Ks5SGLUr5bVr+*d74QQ(5#SU976C$7 zR0HhwcK%Jr>bK;hmfT1Rgk>RY_k!>R+lYE25pspFr~ecbLIiWtlk|o_MdQNWjq~9C z>yV-Yhe`(O9o6YHOGAE+?FV%yxXf_ugL5Mt(PBCXdk`8txxkYvgBpZa)6Y1Fy1PAKrx{bhZK@$YfxW+R`9e?AqxdGP}Q~a><*c+cqgavY=9U<+?OY9gs>YdGaafz)^8P~DGb3lE7Huix!)3*`tA6PDX~FAl z2Ou@mIvQQ9(4PyXQ*~~P^QzO_J#lv*ea<$I^c3kG&dYrauA7u$P(>wj&)u7HaYe>B zAAG%|G^E$G>@0NY((W(@(ioOGBHw7FqsEu>ix zW3=L$aat7Z*|VD0_yFi5=VF*p&Gfx;VK{WFpZww}8 zG$v&LD7hLH1#ycmDRvYo)I)alw`o?kVHb^{(_? zA)La3WDz(&TyOa5>4qP@e~*2?Rvmn)L<0qlv#An6=``Gr9LHI>W(Oyu%$BTf?oK(W zYy&oMWkINsJp@7okc7abPRHNfZus&0_b5R3d5OrvHGelyp{@s9Xu)URumoMh-)%R1 zIIVbER{Z$+8K1TrzPsHJL%_GU8h$ah(tIo z3-+urSRa=Y0Kqp;&-jnO`z^jco$=M_RGp~bofbV0LD;xtyJ|C5BJjSX9bcc$IEP?) zwYnakm!&qgPs^%9ToK%vYZLpjX@+mIv%F}H$!Pz#lyNnUDH~;1Omlw<5z!8K9ZRC`TM*3h8Owf808T;8xSryI*RUH^LE2q%Wt412ab5`) zX{PzeiFF;{azdnlbBxMZi<%!DUwMdPH?wH^^_cYUe3 zM9wM3qib-Z0SomgWnVPWLz5Poe11957pP|@IAfBd&3CW&Qhkm`Q-_F{LT{6qw--57 zLx!SG9`di}YjY}-d=sB0p%L3ZuAKu4+~;Z^KPw#}U1_XJAk263@97 zq+|wG=OyA4by##>Vx=wy0zO=xsuS`U%s^(4)ER_!sU1^IgT1zq>+RnmaBWgFMk>_j zz6=TbbOPrVJIQTE=l%{Xv>-{=@9DIWk*G>W$Pj!HhS&ho=dc%gcLvwj-C3{km+KXu zZ}+*D#DC7gfxRt1*tF~Tm;(!!b;Il zM>Z7IOr{L4EcEf~zE>F%q0yboFlQlDBQ=2;g}RD!XYO2`6Bv@UxxMFH2gxnI$NP6s zrIs(|+fkx2H8;Ldd)d4;%J(a0IkPmQHwY4dSk^3Hkw}8z&OCH6rqCt zw`;Iaz$s`0SsGA~)PccjIomK60ugG)a4hxfB_Ue!LE6@Qf!{6;9H5k339I@09sC*>BY}rRKg-n9jNZn#2)3h=S_1jtPh#IaBgi@wUEq|W!UY^aAJNi(7l5803Rie}_@>Z#Ih4GSGnk8x2QtgsH>zo%E% z`NhyojwUgcLQC?-iX5IV`jafgF42r=kTrCXM&va8GAuKiQ}am<4;+pCFD4rLgNYs7 zraBr7ITV;iM7d482zw~;QP3OBV1ZLC2uwdV_ zPIn~Lc>n+)07*naRE8I-K-UsOjUW+$bF`IufRZ%khCmd8kmeLTxFpOq(IcmoX2mKU zVu1X7fttA**RIhuPLFomDzyrr(dc&nGxA8hm3yLo%RD_R3!CDSgK6 zR7F95(zTzQPEfp&hS2aFQ9rcs{)=*y!}z!4AttBjG+A0f>iJQJA&JX??BA_WJJ%=CF*xxV#Mb|yP4%~CUEHMA-8!&M@d*qW`cFBd$YR%QMe8|D4^ zgmqaUlCfBP2}Ep9i8!yZy2jRhR!}7o0il=D7;B4Nlh+ssT$w9%8W#XzvIamZONULT z$5JF0BPDz|4Tf`;EtW<^QSz59;j-BNm$P2;*mA5{lo6ASeOAtg7$Oh?mJlJb*V?2+ zg6)~{vhTR=+r(qer;{8HQ*wJGp&#Z}S9$4~GJRX%(ibW%H`Kq!&cQuEV|TWku_ZR< z5J6Z69H$Tg+Lf_nHBF#CWKfaY2!Y_`ey=_iDQDdF9p`1iE$vuD8`d=tc56njYf6Yg;edxDarTK@oIgNR)kjA^Xg>UYU&g>;{dYvO*M9-&vFfYi_`W z@l_0-iVirrbcssSmw*68#+RYC$SCI{o4J^PphJZciKZrMx!&vGz&a%n>i3m8T5oyD zVzEbM5Exm>7H5C7ve+#*OYOhfJ@Z(EbrHtMpBt`mk@Eb-$^2P3K5C3$|#Dq8jTvR&2>=i zH@8YoL@ECnf?LYCgs9HXBuHjZR%C2t%|{AYnhC`?F+OS`+X1HZ$u7%tG!>S9TvY_q zvnGKC;-VbF@H%c{rz!JJs2#FL^NHZBJ!zX0{aMYgy!8Vb^|_NSd6;k$9Z`qAcozsB zd1+(n>_Huq{_!`F5w@KaSltGTxyV}H`pdzM1~`^5oHFoaQ&44pxr>-!kdrj`*n z(!E{L(3h!xIOB7IddNQ1)$$Z8PHT22sCyW_iifBjDm)qo+L0;RR@ZKMwgnxAXwxO zK|C;8nha@1H`iLU_fcK54?~XrUs)TaN3m%Oc|QiBu2>Hwhdrajb`-5k&CWAN+OiMF zNXUi|w|&|nq2hHjQVXbpK8gGPy@?WG%1Lj}xE!hejVk|0V$qsZBFtm7eB{38^c#y(n_1ViZI zSw3FV>AviKn7MC3g3%-i+6X8z(j3_LAeBB`VsSKHMQOZG6cMAMLhsHCo=+zTfNx)} z`2MG8bVaZ7&aB@Zl<0&qHIV>0UP#J>c+Ej+-hf)WyP5KZBSP`nrJ3;@q z1k{}2a(0#6`Rbox`)FbylO-F7;iR@lWC(T^1mfQ4&Gp>H;k`VkxwuJBfW3miVn9P- zO>fCAUPPbMh@hHgw4oERG-vMej00FhwCezX)C4ZLK~_O)%RsM48j49-L>Z8^0Sc2~ zkpNlR(DcrXa}2oUtZXPdr}GfKTJ*i;dyj>(xSEq;T5g9lhl<`4mSdDmUr+{5uXLG_ z)%nmDX{PU_!|~!He`tyx`Uny~;hdqC=n9zzJ5oo7q0V4t-USZ%l+;gd6>%=8oPHr^c#1uMdMwc zgLBC&nkZkl^~Pco-Spv0QHN@G$gCWOvPO$@nAE_LeA9@9#*5YHOPyYI7%mCuKxY#2 z*NZx|VcN^lW4o;$j-RhspCR2B4)pi})AY>_@;#!l(4@n^sUN*`=<$qUKj#S3lvpj8 zQ>x((EOHIi?7o)b!yD%uvdL>hM91!1hKz2^vOry4>gY=E{ySJv+L24RZ*B@IRtJpI zjA}|_t^2YXVMQX`vve|!qR%0qIz);UeRWz*RR5gG&mCoo%lCa4^-Px`UaRflGEk~% zg3gUu;4OZxE){NPPb{bs1rW>Ok(dqmWFRke>zo~f!;dKa`#V}AW8!%wEjmaB6-vvl zXe}0l=FAzwTs!M@sE6nA5bRCI)CRvd+iL~q)u3}qg#gOhX^x?x8(?@uxUfF_M0$F* zJV=vx#6}sB+CB~wv=m}QwXK;r)FMd1GiILu&3M*VrW#-oqL8E zi8e|iwFR+r-x>*4H|h`T3D@*m_0UVzMH{m3{X85hR;~l1WPch;Z3Dk0M*BXC4r{Er zm#W!Te;Z+?Vr!kEZn@bC`fSK}Vm72KBWEr0L-33f-RJ7C(#dOtX3`^XsyawYhDb?~ ziI$#Pd>^`Dsm~Tx7F;rfub73YVN!0$qxc56A^v5JMN&uXs0PWnryZLO#;*HT>AgE= zylk6G63~5mu~@LpyeCRFZRU(UCq2K48Ix&*VGZCp#zyZgJqjAjaEYPTPf8bd%uz>i z&#c2Kstgu}Flpwj_o;CAVWAQkb(}L00SgEe2)pEZ{n~y}sOE%?4V@*zIa1ZKgHQ)) z(J^cCIFx3o=A2w)Np?6VX+9Q<#S|5Um7xgv5(sOESS>SGb^*(dtz--XxN*iU>3|}c zH-rdqt~tB)Ky<(Ic}gz()N-Mj zC!?R2Pv96Y)RRerBLh>E@D@R^I~5N|?a(^N1JwI1+UKJw@%Wi$5ZT&qKF92h_~72+Q8fSt`v)q{UukR{Ar6YQftz z-`yasoaj3q~pRw_GG=ERi_bs^$JGlF7onD<>zCw$f@_*{{7p8GK0~3KJLzg~_ zxPLtAZRZwy=D$a1=EmR&W22xIp+nAeH;k-K_XS^ zr=#i9gMmLDK)uIVK~K?l2(bU1HEJ^i5q$M@1`*-I<&5Xcs`OD2yxcZ?y4?^6I4uEd z3=qk9I-hV}7l`a|v8?q&I@}4tNR`WbH+hYVO6lTWr>4ZHq)`HHGH3a7P z$^-2`F*a8Mi%QHKR7w98=9_E_t{MwHTy`Xw3&3tyO8fWnX1OOtX`O ztYN7R!CliO(lbwn)z;ERq?({^n4|!KKD&Z(%UPSy(9mYoyzs&h;EoMcY6^9?HsqAluqvU+v`TZxE?+vjT{1|( zi3l43yXdp~k`gX^sDqp`7~d@um?1U{(U_!_TI^6kOLu#Yy*A(P`6{_GwA;?0#0(C; zmY_NWC~Gqk%rsUF<)S&Q2gmkOx-zQ49RF-hYY0(cHIp6r^ z6B^FYr}#V(I?~|B8hl3Zp^j24hv5TN7gp2(Z^c!wu{->Qtx7t(fbJ=aF?R&b__UiXR39)pNS$QGy;6ijNGj290wFen7c@rX_$-4t98s?Yrrz_c zHXYpS_^6g0-YwryVmg-OooQ7yKWQ-=%%1f7Eo>p?ev~F6 zRXYqv8}m_5LUT;sXRP<=oOd?2EMcd2^^%x_DWKrss@fST4HkHt%y_D{pC1mjv%X_7chAxf~GoJaK>p?N+n#5y~9|Bg#`neXrUAy3z9HLX3KPf>$ziE#H zLDL!%wY@huiujfjmV!EjfPeMlzrgFZ;pwy>#(-^CgzIuzK;euf1Uz3(rp;fFnQ>1W zzT7r!hQLI3v$LSnh&DRAUW-!*N*^YpUt4bX%YDOb-|^M+6Jj7dp9|Wn$jhEGUT!x{ z`7jzTj4B&q)YqfZ1h$&4xD|IpOx6Zn#&Xcid;~Q4$D&(u!u#_X3nG$a%LbN-j=o4m ze394U+{$qjiEzsqU$zZDdcGj#T&X_6vmb<26Ds;W#bTg{)KQoXK`xO@6to%1$eI&t z@3olT^I)Yi$fIOnZF#GtK9yn^6v^lX<{VFq78--HT}b@UN*c3=iY`=#179r*Hr5n^ zf>n-)*k#xulFeMJwZA)OBq*|*4e9niK2)->?b#^@IFwb=u;xT0*= zWC?^U4P7l+&0+(bbBMTeu56MlO1EC4D2>~e*T#%{$~FiIIK_aIrJa6M zE!t!RWw?eWA&Eu555gkTsb@QY#UZVwj$M`r3Ifj12V{*1F(F2BFy6#+Rc1CflM17Y zm2exIi+DM_^g&-ZHnh{kGn5%=xD$~Lx!u*sqJW_LxI~h6GQRmU$@b+WvUMwCmSsS zhz<^^kFSaJ3eosPz9KLvG9WKM4{oe?7`pw5 zgoG(MXeP&yJ`ed4m+HWLPZ>|A1zXy!xlrUW2Eu8Lc!yPEc#;|j<;*y(5%)dg^L4{* zOE$>rr{v@kJH#O+;tJA~wFy=Os|EKxfkp7!PoMF$F1V~Gjn*+I=EWjS_N2)0X$kHg z7UasVd37(=J%VIptw_}lcO+lpmeUE<%(7%NYtm*TG2wh#a9#-e4xEBU8WqzZ_KdU6 zVi1S(r8od57Top?fu60DHcht0KXrgeF@Hj+xuV4vV2!ce11Aq)%T{FyhAeku-N|z_ zA_LS>|Eb3YhoN>e6YAv)Eh;S()RzIGd|xFKCCPDoWY{H&N3)?ic78QZmcw6FzY~R-|+Fg;`6>&)3_4b z$acmkOF|hI{k*D0yA_7lm?R-a7f!+m5|q8v(TjCJa1NT4n~bs>spT$b5u9UC-MB>% zMa%4Eix4UU*FrxIqWbP^Yp&36ZDgSOabsDO+EBB0HJ&T8=G|_rAx(d2rcZiK#TJtW z1`v>hu~5MKB|u1U2@&tt)$)3ED7J=xr?6l>so`5gX$9YH_u4o=?dlHyl6I`5@lSyi zoi5LFsE94$WSPlp2sjtE85klNVB7mB*uYfuIxJRd|gz!X)hj^u_0)CLVBpQ%c}rEM<-c}+Xpluh$o)5ciVfaRo57bpAM z0Zkj#pWIE8chJbWiLM9Kwr;ZMTTTcNRT&{*{)O35K8VY7}GOP^_4m7v!x|GJX%le__EF}JZ zqMM|4MistrZ`>rCKK1%d{a)=mHs*goXYMvkx>~cX^c<3rfG$VR50ysaGy6os{X0|4mF)GsHfeBCcDJg69ji(%FrP=xgfI{qCoRdr8p}C z$XRe&V$A}M6hI-=0_wCzr9DOc^lbW=5CZm;t0OFVx)y1X-@ENah=?L9?+$H8XG|Pq>p-YOX7oq6MvSqh!N$GnVv+C*+V;9DrrDlaD zRzn(sYR{%-IF3@M%yj!>;CKyLD5bM5y|v*>SC;sCy5#7CE~hmh=Fi7 zx7~Zr_;kDCmJ;q+^LOtlnN}9K?FoAlygQxo;c~&Ltb@`fhT!6H#7b{nONj-ixYWJ4 z=$yrxgPb#hv?vla-%dUEIsw7tWt~f*jY37;PiV_mSeM?vy3H#q{vyxGpGXKR=PG z^{mISwDV;#J!tVT$Sl@)2ulB_THU7G4VHz@5Q}OuWX%vSk%Gbj*TmL@g^C6@&jdoC z2sTZpNE@oM72iL+zT)fC3E!O0Ku$>ej^_}t0C>G`SOL{xvmoV!ONiLAl2$j?=W;Ds zWM%;_G1PT=3RE4WSriqGwocDRosL!) zTw(+p#Xk_RMx_K-!zYxOGH)`~>J>(5n#E*BS~Kd*OyYflO107ob>M$12xqE zQ!gkJr%N1cGJMQ{q9m3LXSu+2+)76fw{t#r+DUq#y7kRK?`?b3?j3=VV}Ke}N|_uC zC8;(~9;~BYBRd9|<2d1?5kYw5R9Wabk5Ze+8?!X6TA!sn)HNkWl6s_}#J-G?F;nX4 zMUb8#`E{AB| z85Qw??$D2L-&09gh-G>bc+?cQM@A)RP2&1yZ!T|G^?AjK%*3-^OD{8Rd2{;qvYjQgv<9&PuMD7csKWO7LsJu) zC@XYC|0B`LXL0vb>^755%=maZ;b|>-wR*-WeG+&+FIb|+5OEf~Y&-t(51(qR$BacX zk$Csn$pjadS7yM9hKTf}_Rc*wpf(l7BqpV7?%Iq@q>(gx!pm*PZO_=U4o0h^zO>I1 zhFHHc&MEfJSx(SkEHsmJSmv!4Yd7Nxmb(;3Y;Dmogw zSk%iB)KIVBr%B{scj(P6o6u(ZKr|cksx9;n)^HN|8k3HZWKxYOb^oev)!cHKI~2V$ zKBPtiw_#DPYg~sK?@lMIA>)=4SQwXe#g-CIG3q|f&AC03AVZtlQqQqa^;rl*>SUEs zi9cthlM+l#A#F*QL0cKT%Mdv(T#U)Q^E;~Jb1^LnrZ6qbEs$zG?`cQQ85DqMQ9p|{ z@+S&de2%>R-7E~s0LO?+2)HXkhUD(o&a5&D+qVOiZ0%5)TWr@a2KyO8Wq>Gc8Qob5 zZVf9Nf=lEjaLS0vq-$DoLUInnG~|EhQd5^k*vT-c()o>|EsQ9k-~nC4obN>`JsCMIooq?}UCmX;~b;VCzj$knk)EeZ1fPh8y)g_nP))3cRY-(}_PDtE1 z4_wkNtk!zXSq%q6RH>3cf8V7RmCwEOv|aS$SpjCE(rV055n6$8aU4OHS~OBS#)d;6 zb5m%v2i|X{Z)p*6$W)ezM?5BGE5m;5!<~hWR@(>0IM2GuDTQim;$)r<(>JP>Ryt{R z+`{aK1+7q~aZ)96q{=zk@yN>S3n))poEPwZ@301Rni`kL z0~@ej#_CNE`gI~SvpsRVU%LSG=oU+_PnL;gm`n;>kv_KNpqHG{VttEEGA|-#1@!2! z^Kcf`$L#6$oy$2n=0%CX80m9#%yO=)tYl15bB82?8SS*Y{HwqjQ6TXO3n2;z&V0?vzhk) z3MEnMFmPCF2Y&zCjM!PbIdv58t*Z{hLYhc-x<@JunM-`!Zhicc%*J2xD4{@(@d)Mi zmnb80QV_THf)xPT=(dK>z#gQmnX81x;;F_yp0+(TjvFK;!8zg^@*hF?Z~x2mid6sr zAOJ~3K~&|>@wdPD34VBaLCP8LpUya+7hLZfmI$2Ih;>9oQgR#0uj%S06bBspr!&;)bs>WSE z!x|&DoGh{>Hbl-f#hGz1$7_-$Qi%zgbN+SD*mK5LmkaJ$jkapQU}&!4L9}VxEpjQC zCKp{wMO$Z;eMy)AFZ2CKJUgsLV?imzoxem|x4B4z{GiF2FS3bza5~^5taN=5bv-7f zhTl`}5VpXLb(ZZK3ju4+rt7urW!7gwsIBS3p<4Y!rmzhr$k-vr*f(pW ztseD2li1Krn{?E{iws)b0CmsKlbt@;xKHDqqD2I?;bMt|N{+QaMl)CRQ`dQ7%>gdi zw?4nUP`kY`T=jRcYZi#G8nCj8)StoYk!#umRiBk2EQ&5uJdpWuKomta29Pq|*v{5a zbGmoae}BS;B$~DS6jw#>;qXx^&tqdnEfm0HL~aHoq6ciKr4Wc207g)~7c2$IC)^A{ zUc-uDy=gHmT};PZ@>nU|K+XKpn$ysc4 zW|B^f-2Z!UyKAy8=i)1LfHKKX($G_;YjlV+_^NrYgd;_Bpr&>I3_qjx;+0N^g0VgwEZLW&_vG%YD*79&XEUg9KBz?x~4Pp(utSc52epR!jdX zkUdho2{^`W$=EKsLt}KOmE&MyR``?&uF$;6BFs=MPd%P23BVoB&Sg-%aX>%i74hNd zIA&FikI~R{bV-UNUDWbH3w!YKZUYo=+C?--1iU>}jdP-@VOHyzd$SNZ$`LUh|MD>2 ziDuq%!$fIpP##qkqeaQ^QgWPbl;*7zr*PWFLYbSHk5OTtxV?lN@w?Ylj#avJO?W9# zLui!oB@*^ssZ3dU*qzROHk>K(BLYQ$_G~mYbB>+eBY#!(UXYU^yivYYK`9OEwM7N> zX3?z}B~a%O?VHg`I}R>JO(y*IKhgZse4Cptvr!5|U2*Dlt@6-MK!(-P=Z<%piUBp6 zIpYHJ8%CJwW~?9$p_#iBdN3^GwA1%ebJGol9_?tVWxp@Zsa{#)9sjk!4AoeMDjL_H z%Lq2S7Qty*kTV0qpa5+54coTWrslTos=c+$AvQ`prbC1_loWZ?UQj39+H*!Q*+Zct zmp|l+HV|&si0+AzQU*jRu6Nva zr2rORfMj}Owg{lzGE`$AoRu;xkvJ zAxFjZ5Yp9)BHFA{D9?%vCZlw$O{M40jCEp>Fx9-{qD6KC`PM-8atJT6SV$?YH;a9G zUROhX_4ls(eyo$w8q8LYk@FoHYZ~g9IWowSx@fVwzfir7>?r0!(wx2BncUDtwk&U{ z@l?fF%16vp?*uc*ip59@MM9y>fjukrbW2K2_GVqR;VSvIF8?rqaES{pQ6rWLi-Ni^ z7ai1eK@ax%f;|`6Wl5JhScf|b#FW~L%RQF14;7c=I$SDRU2=w$;^Zo}mc0ryNE@<} zoj*$mSZwVSDFa!|)15OI%}6epyrUxBMhRa7VKpu5YTu_c-AAmi^F`4Ez8+(Im zzA$5FsT%y98F$XuID;pC3FTUZVkBkXEl~3t(E*1^UtC+cbMNR~K(8WO+Bh3^Uutwz z9bQ;Zc8;++trs&lHk90dud=?W`Y0XQkc*6*+p&-zSVChSo|;auPS60vm5`HbP7f$; z5#Twb%t7Ku2BI9OfHPlWLEHE%U5Wf89YYAY)X7byJ5hr&O0&t%iIk4H5;gjRh$a-y zSwYfhpEQf=p&p5*fezUbVaphUda#A4cghu?i!2ia%+qA0kI{KEclQyx=aE3t!N|U4 za14k`PYLahcr(|KKKzF8c5ynTNgl2%O$6uCIg`qaMNlJeO?HP0T{YiAxw~^8Pji|MR=#x!~LYr{q}&i9vo8P4BP0j$DHj2{<*3&e3A!* zZt2};;T@|VG_&ImrI$z0gInjlNvQj~6U(YW+(p40VhcS{P_>6N6hXc)sD8P=7;}^R zS##I>!$}=V?kOXlloEQ15ij@6#gCK%$DYB^qIbBucQ<_vT*JviQlD+KGYkNkws&*CU*$|=OrR#t_Db@DT|>* zZ_z<<(5!x(^C_A>88u9V>eT;-*DJ2KE6!_FBu@m)j30e`ry98!@Os;H9Wy$w1at#o z>Z$isrHhjxU)r~|IMFs&+>;FgDyERFYD`#S#I}n*w z)jem1N8+xQyW*3~^#A{h%w)DkNu~>TDU!?K%yd^(MgVX0dI`w^Ljy#b>MGg`i% ziQXvdG9TBmneOy}^vIvRHEfNI4z5;U$%fMz+zr|Q;7&Y%E75~0o2is$P-`RX9vr?8 zwa_FI1fQiU%K)$_rPK$%V)soV10x{H-uO%NL;z7H2J7$f+QI8Ych;8f%ABkL*5~zG zJy6YuV50<1+F`VM3S|6x-JAGo%Ncs zJ_^H?rcZ(&m%~ezD^7z6bl1Km07UmZH+!SWGj%G|WchPi?qFpMqF_?GEp$c z;6Y-bSYUOWM1?;Xu>B~2iBi1lI zE0+zi@6{BZ^?8hNdLef-x~DCEPRj#wvygPEkTp>wDId3^t2BzDH|dPLOtVsw7a&sLOT0 zRb7=0O=VJQ@}m?yj;h6=?J0V>EVZ&}>!$u~6g=fZ7Ib)@r8PyPOY1JVOoUs`_$&q7 z^Z?jW!pB}fS>tR3ikfU797T~kBEb#GhIrcOf4S)(4WW>37-*ZuqfN)4Aw*C;rKsUi zV*)BoQ7iNM<6h9}XV4~``0V{aa^B|s{A|(Wv<*qyKY#y@pWp7<7&(oT23XRjEa5;& z?g%gkR8i5VqgDL&c%VYvcg3{Bv^=+o2#3extaXkMgxX{}J1Ia6v8V33kM1tKq68ZT zMSC!Ee6TPEObtqnuAZ|i&i?WBH{?=@2zwh6iBcOf>$+2;JHkeUEvsB+O9}U!dd@Ia z{o$MvHr9wRzh^N=)~U?y(k)93&43vtwaJThqtxl#Iq2}4mDy!)SnA){;5O}{8al2&OJPe>>txLefE-89VA_S3F)C3J(WB*SD*tcEV4 z`h6DebZ=*@!k!z~Jr5JmA$hu4ws5aYCMvQv%>}d1t;Zrd=2n9S+6Qo&48izb4!q@L zF-DrxT$|v%6?Mq=ax`e1yTh{xZp`Ks9avGO6>m+jr96;t8*V2vP17AjCe|boEfIu+ zO`F$XqF#K31~f*(P;f|=CZJ6lVc%%#p`qccLCQJXba_1rbGs$byCX}XnO!qSSNhjE zW+URSi-*9XjiR8J?Ta8>M0vvVC7v@g=LQ}p_j?IbcSn-J&#|vzu(L#VEcum$n9M*I zCPFe#Nhg=$c~~&gaF!^krjA1W%kds~P94mH3w-yOhguv~QCtxOBc#q3T!XQ&b%lT*M>F0>iVb4{M zSb3=vq$8NkGd)+D>Ntsj zEvqSx>kFN_$TTP2vPTb1MsMqL7KS5qW&A+%_bf3MBX>3q{z7)9S9nBEs2VG$IqD8& z8oJ;@#z8;Gl&3Hax-xU?2=G~&c45r5vVY_+j2UUkRHo}^mR1(TwaHeetJ1iae)cgD z&iS}vaW*qOU5rxa6kBgzHHwPRmV!j4D4l?8EW#Wj!s93?M=^6I;Pt*iTEpkDE1GIF z-!139{+U)hZ9S_dW!)(Y9n)E?1tIe|zu5;utPQJD1=-?Dw2=d|MGj@uL@077;#5^* z&qOG-T@l!qt!Osr3BHD%xzW7%WJOb}G;Ex(8?x4#x;&SrPV>8IGbFEZ&6y*Sr+DI z3-qkvu56QmVD({tlX zzYe~1ASJE#b>UK}(ri#?y8Gf&95L-PhLKkjb@FEWkgt}TTYJEvxyGk>c z@$sBE`$b4PXjCmSDJ30}ZOqurG2W^BKIrlRmDqLZXus_Z*&YTy(KA;_NG_({WDiYu zDl}a{5$vB8tqxkTVCV)&)^cxb3WS-|PbbFKb^CQCUmbc&I-6h-%}v!Rq=+L-`(LC0 zOEg`Oiy|vbHQbeTifg)3VJ(@X=ja7%$T{d{qVCZI^tAr0T#<#AoLbB=6Y(!-ihBA> z&BD@@hr^5#fwMKh(G2J>wUx3WB+HkB6LKtq=@~~?X40nMNjPgLpamDu*ZO*~#}ZmN z;|n2q5ft_TM++!_kK{P7S-R3Yg>f;aAiPdG+hm_SK7P>~0h7&A;Yr7x#o0(~8G#Mt{@YGJwGd1f4 zEkQ0c*>XWqr?qFL#aD~^8em6119PJ5xadsUK5d1ol;P@a(?xRJT6UEeXfw^#L1_d*`QG+HQsw8IpyP!#n=OAoC?=KJy zb%q_r7&`MZ5TJl&xk>Q<`RniaU;dB(2Oy_Kvrg8(!|Y4`4(D`*Gl~ zqM0Vv#npKmhHi9`^oNMXwsU{HN)((%Z0COyjYNCBZFspQY@8HDtb$T|68CVq1BIVVOYQHIlr#clp(Pa8R$$rCtcSqGH2eegKY_~>!}`c;ylx{anLXdvbB}1K`Wwh zD5`;`j;cx^Do*cb8E~@cN!?Lgy%Mycx}}U?KOcBWTi=fb!y(&;wM;ziXilXjnyX%R z_hUoD#Iur}z2Yo2p@|yZU7TB_rc;oR$P@wCL>WVXvW0vdMKKG9l$0F}e;i7S&zz8% zt)b+Fsb>^oYjT=xQl(07kcY9P(O^BYPUbzYwx+RXwVF&oBXPFiPVTzX;*w0!0hAkG zZIrMP_Xr#xEW2a2yASLB#NljIy>hkK9o5Hb^jfjzv{|Nb1f!^TQr(a0dPped#&9l@S`@o+j&&%vupD!eL(t>7+-SkwDpSCnP>=S z%3*AR6b2sEoS@yX%ZDyBp}`U&4+rW!H-f;N<9Mpq1Sy*@uFgjt+A*-~TK+UF4@@$)wh3pSp8VNb+K&EC~{?d<4TB^zUk}#t! z;eC-ft#ZlEY!@h@n_>{1G;1WW(WXUrNz+D%&|pObdP3$ck2h-bAY`!)=W16XlII3E z_8xU@aHl5u!9irGQVXUwB$|;L8bIk3d^s`3y9GQ2@8Xz+`GmIa1>%p4*y26veWjba zn7v~+JaVnsX^iX-;LLfVg?AAHJw5YoE4i@~N-6i_>mO5eVqp#Q=*Wuz{9!Q)*RxUvL!n=C;^c`PhqITXWTXSSDu+ z1&15iNAh4?)CXG?^=3jXe&e#PTB@W=ZnI5Faww!T@7RXG5&%LM_ zPD*&a-%y&z6P2FL>E=wHvs{}Nq>4&;Ab}T}ZbCG!TWzNAG)LzI!e4!P#n;;nr3$|9 z#iEf4zU@1X+VI=^J6aRGZX14jx%Xki`{PhkEE%CLEy&=GR*g!k2z;?zXh0FaL`sY9 z&!=e^6;)(>90fUPGjhvGow7OgJ+FzHgejUtmZysD>h6x(ldl1WmQ|ZE9U^%F-=tx$ z6~BFc;>&$QHT|^@XpT~?(a}AF0&6HIMox_X>$iW!zaD!>4v*Gw&l{QmfA{OJ`1fDF z;D7w(=Ygi$`-w=Ch;&q{qrjq?)eJ%gcax~TkF#n&i&K!BU}GA{puKjnrXqnxWKcJ2 z3hu`Z{n?YCBj0OL^w6U8$k2ZuiUPowl=0{L3;z82YB4#wFSzbM5#uc<9JS*8I8@tf z)aQ?K^npLj>AN<~>piO8k9*1;#ow55H?%mDJ|kbZ4R70qY;~e%jC(fuYtCpy`2Bd` z+i~=*?=bVH$Vzz0ij*cAjqcZ+%_(}sV3F-nKAbC^hOKLGHwnN!>A?6f9r$6iUI6%B z3ci=aDBG&*-J53QY6h`3e5F-G%g3t8rky5I4}{NB@lg(q9kczXS~LD=6`!@@QI!Rg zK>hQ56#beb?~mH>QB+U6al%Vt9TYGlvF?{Q&Uj4;H)fM+=m29g1mCawi)91@`abSv zRCTn9zkPkf|NQ!fdt!Vm2M#k)yBk{Vk^uzpP=`fhaFdzwmNRa4k1HBJ?cDcfwixr~ z-Lj#55bDUkyHNNLAg_mRZ%8Q=@G>i~dch5*4WT^6M|Ovr|hf*JI>y`~@x6GPY|H&PN&XTGblmy)LFg0r|gg%&670sUfnYBl8j{hgZA{QTlLc4SOl7 zt4j%D&&I=Yh>zkSA{9h?cb?rcK`P;WKd_tG%wsPmgK$|~aD;{qZIjVmwJ}KDF}c@_ zhU$Xa31KC=-fErQ=FWGt?C+j^EHQ|q`4E+jN>5Wwu$O8y^Z7_{MnkDKd?*zu>H66c zR9LMmf$b1uCOxvKnXil^iOG@J zNS2gw2Kq~+E<+j=X3wte4Y6nR+V8HSPSA=dDtx6{+lq~q72wy4!?W>^-`|m#@V9^Z z34eTk8qMCQv=D4b_uxYsHX@{)jgGI*?lhj6u-Ddedi%3w3#dxN z_fqhhjk52tRZVc9KuEBW$`Hg*T{od(YZBi}!4Z~cYpOpL)@S}DC8MJ|BY=7X>KfMF z+^?Grli6suZV2et(VKU7%T|M`*7Ko#pK%&9ca(fa_Bos}x0H0aV>e8l>g`Ez`4ElQ zL%)6+^K=*zOqRM*6qoN zW}UT45{5?bX|CAp<2-l}nXmikQmHqeu_-6a=$*uM6^d8?eC*BLGVh@fpKzib{y>fF zd%$_Du}DRy_bY+ra2{?6onv;eI4mPH|IfM5i(@Hu9&k;5M!^2kLc1o4XEN zhO>Aq7AP{}<=Ig>v(bpgHLlR9K8ONg%5!ELYIWybHRYMLqzDeE^Sz5&?W>uq^tISJ zBPJFqB14D9L}r$y?3?$;fsf-rsj91Mn4@l5*fU#GwnvM|w#5JdAOJ~3K~!{~^&CyK znCh_1ZjGk?QGiD&UGpZ;=%$=T6iaPteDzUOJKaR^r_-?OL2^mCchk3jeE&dgf?>GN1FfuhaM>P)1_^FMz3 zhX3vRcf2v<-@U!!s15gQh_L7|!OWW{i`_wsI*ns!f&ahGnp#wpZ}$<}TWvgds4KZyv{VDv8G zaA)|SqpjiG?TFsScW;H*g2eOnZU35$>@jI8yq&L#zmvq zr%n{(eC)s#TqB}Fi*b;I;B!1jS|?p0ow=JbRpB_z(Wjr8ZTw}CN+Qq^hUJB0%ZUliopL-)Kf45sqJ7$sxQpO9rhA6xI>-JJ zu|LxbS9ESNypp+&n_Yn;5{NTWa!ux)48VqY(6#vu*2XP3qINp-q`i)_SD7_*Q#1Qy zm}2M|1y`7ePCesxkHOU0dHH0%HJL?mn1JGpMm^O^$M-~|nUBANFl8O$8F}M`*veB^ zZ(5q)nJhbEv_>IB)9~X;LeO(IFDcUt!iQlL6q_ZHnU)+W`Vvs%sXJYLis+O=HL=DC zbLQ(D!>k8B-xw&PaEKqdXXA-aR!w#gL+3G~E}A*8mO7eW360w2poOkD8FHD~i_5J+ zp0jH(59RvnP2K=Dk+qT|!FRgBl?xJ!A=$<%1Y zArhV5Z==yEtuaEwunx_(s}?nvrx z+bUl2tzR-;7#A%t0k{{*ejduqroqTh-R$0qH5=0qXgdGLek{u|KAI5yoYgd-u3>I1 z780rQ!QyhZu$R&-XiizTfcs{(CnXB%}JjZ8v;8cGX%E<3Q~QKE+~PWa?Zb z{F^q3GKHXYjdB>Qpyv=c`o7rt*jeVPT*IpktqfXotFsPkfX96yL-b58MLhX0e3XKJ z{MbS;;e4x1IkrKg1JHe_Shl#R8~PNC?*}-p>l!uQuX;J3^(ntz50N+ zaRS?Q%rpRyW({zs1h*Dw%&Im0EER0gOnpEA>UqEHO|lh4sZlT=)o7!5RrZ_UAq}9cH7(0XGb+J}JzqsX%oM?;njtxXn}U&(OEgCd zJZ+aG>H*^szUJ3HLtvKlaJdL97EB1J#LvdpuX%E5o@mNTbOl|Z7kCm(yI5dHR~5p! z8weR}L~=I0i3i_>+nsb;rcA~s6j6-80c%0u7_SKlZ z&U~nzhZ?AQLNk?nQD0wSsMo7U=B9Ku7Q6P)T1=v?h>3AFLDNnPmn*CX#xUz~U2MEB zF*tr)zlc&T;0Vc5kY5Z`;@Xc*yWpbuG@A7Vi;}LG;v;*dYv|USErMmmP=DV8#gh}w zRG!>L38F~3a66X>o5!7mXwui?*@~ejgOG?I1)KC}xj%}v*pSWUyhKYgAES{ldm7M6 zcc-}l@0dQJ8&wc#k>|Q!0;9D-MQk1!CCvkTZQ5?rZyxk$7D20WV#{15>8y+$xp>}r z9p3dOe)vVq+o(KMzzFF<8Bu31@1pxP8~mLD;yW`E=)8|a`f)fXDei6Ew?US@Z$@pS znc>ukP1|sYvztRmtyvBm{}N!mI=9P-*F-bAJRLRB1=&nQTA(loc8EK*-ZBTL?=j%X ziLf6&2pBgev&V$3C#y^+EKfBWL0BE8;|lPGsR*!_swhFJC{@uUX0c{n_4lQ!%kdhh z6s|L|^nn$hpJdRTmb+sJd zWVAy!H%sRJ+hh!ETR_I>ar5TtI7rO(MnsrbEAFadw_ za#!6h9GUg!58VkqC)Iub)8_-Wor#Lu+SJg-@w!(wQwM?;kuE7m+q8Qn;1W40k? zaL;$}4AQCIZoIdJJ@p7)03Iimtr>Ro-rv_)W4=F7*E^44x**xmFVWbio3x%WU+p<) z01`aPN>I=sqI7WeF44v~o`-q16r~d$H|Swu65Yc28wxOU|L%>~nZA6{Y==g>Pvd2! z!^kN{=~^U9Cn_3FW)@5H9JW5XyaG6jm}C7$SLO2(_ws};d)7lQQ-y%WP8zyEH6C#d zvldp20_{7+gf<4)Q5Ikz9-id)UCx7mD+lGt36;jWp~XpvK#L>p6{|gX^UCz8&iCk~ zFZLDDCY4rg|&UMw0+NfQ(mwcb zh~{=WHl#dG5FhHynr&H}na@ZO1$sklsN5KkCPTmB$T$W=_I2mPo!s%lvm&d}n;~LE z6DyZOcB1bWwFy)szX7}#B*YV$$TU{{(7D-; z8&{`_=XXjUB+{UU!J3H{uZ`gSu^Xk-O&z(r`gU&xM{tdsdF5k)&=9(u!QEFW#*vAd zTisH__hav~u0Orp@OczGit2iIsi;02@*Hp*gn98^n&7B)qC9$jY$EPD${3v#5#Sm) zj@kg%$(%1)aqI)KV*kD+o`;7rpKT)>nM0#F;*G{odepF`spgVeK&s7@lSrGjqgBh^ zCA{PfzkWOb5MH)Tjh!s=rVrFe5&o|?H9^{9*ktE3)69;#;=jc+-kvw6Ij151e+D#L zm}NvE8P&K3lf7owAb~rJ$)9UqJDC$oExnQJpp?KGKE3;i5)NyCz9h!~@b-qcTZUA@ zUU!WYf|;E5NGkmVpRJ*oMms^x-+aK6z~-ndodu+ETZ+n7qg7XbJ)dP;gX4Q;4!T;h z=%uWXZy1&v8qPp&q=+Gg0p!r|N?@xvtyr5}&#UgZ0*OUJnYmc2dIo#GGO=A7OvCOJ zSV7IcUpIu-Svjr07lCjJ$Z?d2(8Q*3^q*u%W5yaY76}X3IB=qNq*?2%9EhrC-t&m58q8y_l^!F2rzWiYg5X6rM}rX3obYMzxjzmwd}7 zMMP|df^pKBxqPm2WXT`B8UU=qowci8kd~nfpbL~zy8Usaa5E7o)1(EI=EWWb`~6W6 zG^lg4L`rMdam_iH1i|xLM0le3p4ZD2iv4URup;vpG+ml8ztwYmbV=etJpgk8d+JLPyw zFgX%g5YC~2o}L8tLOC@Bw0xicLOSa+=J{06qH=6SXuV%^gS+}o)6%XFitvSnozQhd zBWLINi0VuH%ZvO;AIeCe)KZ+b#C&R*nIK~Bv=hbGC`_d(%9G5dLFsJ-3D`$Bw2%>( z(Lz4o>}oukKna5+xnRj46r>`vjyX=>^46{ry<= zEflRpgEn<~EH4L-czX7nPNfTP0SbGtV0N@#uGqD-w-0qzHq0#ki2Zy#cHD4`WNr%8 znYe-5aDPussBYxrC_y}9;0>_8i^LR|GTe2Y=tCnyYpolW^gTx+rhk^A5HpB*G=N~h z5WZS1Ba<2TEiXIqVAc2<_=~CFGBZ;qtepotK&gX-y^NVcX$_B3r(sZ!OAN!T7E%Q~ zY7(6SsYN0OIx73#B;LqblbKA*$b_6T(g)+hr39^=3 z6p;8?UUWU7w@50I`FRwv+Q^y|)4<;>R?3mYo4Zvv>Bc-C0(_JMf1SQqhH-*m?+px| zav>>1TZY_sS+EhF!HJPe>}G*Voo>NMk(L^?`*{RFg8@`h*Io)|7}03B8q7d3Fh*{i z@N>@iDUZmpF_ayHFSdrR8in=FjAYOFjV*=1D940`qd}7gn8BjvOG?dc-K)Zp)4rdD zcuhu9IyebhrQZ09WfkkWHyeM>R4fW?1=GzslZap_vAtW?qShLZ88hh1~P+C z7$~jkunquN9Zf0Z$JfBSM9{~!z{U~FMrK2d+46!b)Ja?igtP2GhgR^!;J)v(nURS} z6D);!7*uf^8N24por%>H%;?t5uGL1|2j^zVw{%4zU$mm@?Uqo; zOuuk}24!*+#)KkD6ko_3dqSZXnlsL$Vcha**W#FFG`I;y6WA77`vs@6M+Z@8+(i~m z<^{v0OG2xh}HaAIU|P+pL`$I74$)TT|Rh*AuO&K6XVrH;X;t_)J7e1BTy##|ydq zfTEF_yBqh2Njd>4J^NUs>&S`bSj|)27gI~Z89L+4oIM@U`mzVV7aE&4X9wqu@5f~Y51%A4gcxu&#FJR`|~JO(N|Ks zF%xi0eqAdPF}|dPZdtC_=oEHMR{sW0yeKczA5IPzffZ6>CnfMmcRZ zSW~xY!Dp+eN9*px&D^R>YwG$Oka0HG;A)L#Gpf0pTLHBtfHUNF zY1mEl--2}!nMRd_26dds+S}IIY}KQ`4B>17P@>*o!=5W`9qaz__M~rBS1u7 zJ5y2(c3?$FkHKtc)_@<(T9bF&?P0k2x9OY9$-Et;y(&^XhmxTRMd&Xnh*rxLY zNia$PaD3KjS~BH?TtrLBGt8Gv_ht8v6QLYKKLh|NF-mQd(0@hyB$zr}vvcW5M9!)v zUD{NkE325x9(n3Ts(_O~&pzqKJF!|Dj-?CpAvjDT#2ry+04Ku?42>o&kBjt7zwx|` zvrf)PISo3if7;B1PkWx}!r17-kbKeg(rogy6g4DkCR&K;!iZ%cSw<{js1%!53Yyt- z3*~eky(iHD*#|$2t7zqGHo-&{r8E7!+JWPt?#=(Zwyy1IZIp~Du{98?LuyTsGo$Qf znmT*z5z@$8j}aI|bV|wf-%+a|WxbB2s)KW4Mn%PA*9q_As7Q&s)8n=&{f}8qknS6! zdVy3`mz5IPX9oJByP$Q|dD3@!a+lqQDqk>xbTnqK<+zwaSY@?wbPwA0FY zpI*m*k@icG*%cf@#omi%wUR~mD3-ogb0Q_nbF~qJ&Z#-nJV&54#EQ^|s+9CH!i7 zXf`)yMa>gFOTm^He|#R;Qo?=9_~qq}zkh$%{j})-;YN&)T8G@k1{cY4fPG)DHn?tP zT_sIWt$8S-5j1+O*mRFdV}D7hGZ1=4dZ6#dbKh%O-x=84(tX!(`nywtZ;AUkVs%x0FzMIJ0YCM7VQCYXy&{>tnO) zRz!@s5ZGl2kTUY8Z_K!{{hlz8+em5EWZ&cX*VmNsY0QgE?o5qN(M=5OwZ_)akX`9$ z_+bO?%`=$o+0RDxuE90Equo4)mzv3KO{eW+)fg9^H(g~6)T9Bs`I>B&?dyXB34_9p zQnxml-^~&b-9ZCqg6br z8pKJH5X(5u@x1DFS97bttX-U(=a{0E-nJGv9*ZMA4n{^{w3vdBTw55$A|q--PbK@( zC=1R7JDr5F6f#%kM5EP7A)Q>*R)=EH+ANL&My|}Ru*9!Oq?*h0yL#_LvpaXZH?mN% znb8p-B0@@pU;cdWhBz>q)lnL1X}vLy3wCjJnb3|EkCiK}v$F?;76g8VO$Wj`=!b6q z-=iO%{%R6bnQhTbj5n7DdJ^9f5`4(r`L|A0X+^bt_A6l8dHw1tf;$ z6*fa(2W4<>`NOrbMy9r*RW3zPX*3giX-H}4hu`iQDKYj^@%8nF`=*+{$5Ay-<#FI! zJ)q}uh)*NTx#FGzaG^5rgG8vEFSaauOx(XGxBRG~9w^W7EEq*zlpu(V*0sr%BxY!l zeD9si1%vrqAJWjp+MH4ls5?#0K~0$a>S+lXmJ2b*WrkmT2A=9n$7ow(DxdmMNHiRTSRLH z>K*__iTC)M*09&2CS{HLN_gM*!E&_0&P&cHt>XPS@Yi3z_MwBb8yW~VBK-RPf&cK! z&v@Ckq0;6S*fZyCPu6qQnpZW%q8@W5!LW&fQ}E%9O$*+pAR!%OtiqKac9k zsMsVSXKkX2HNuGq$#Zd~S4K@wA z6pg5=);%O}0<3gjsf!|Ed`}`@qr=g;34^JjioS1M12L6mXrxBqH-eQ~S zPHR+$Ox?qXVwiGQV=(=yh$Srg--wD#)_k`+NuLyKGG{`@BxQNP@VMTaWE7ddVd%_eQxMwX`Z8QDbhouLvD-lVl>mG1#9ye9BRXKATko%gd=7F zM$~!a$;s4!oSwn>Yc89d@XS1u&tc9AjRmf(lz1#Oe>&@{W$hDW6y4(cjI%i~%-ybB ztw%zUO!vs2uk&Z7;LV7hpT~wMM5%;a#SCJzO?XTK5u~xCsR6MR~LD zX+ncK?UYfv@OKI8-_Z^t;J5D|c>mm$>bND`a>B=P;PtlkXHXdq30q<`b3YcMr)nyM z^W-#;kZ~Zo+#sg}NS!1ojvn4v4VAuq?nVQp?!N(=HK1VH;n^{w$#i^K7_j$CU4W-?`Uq2rB%mrVzZ0^?#Hlr*>S}Xo^yG`sx zr(65A@O^!khKC5g?*(r;8ycIi89H5|l>V13L)ES>bpV>{hehzRZNprt>1*y?fA1_T zGl>+zqXC~y2Wy!^9R|`hWB>h{6B1>7l)Y2WoyFrQdbSRe)Qx`DA+y(P{E;frU~d%p9o$eSLZBk{hLQ^-B3>v z{e~M4`6md-LMJbdv+1$;CMQ_ii6%cli>Bf=K;cskT9^B*tL0pm$cpNWEGxP|7M~dn z&*nnIXk%m+(}WC+oL9!0%{Xa$0@>j zRggE01rY*Tt%mGS2vBcfBDokvU@orC)>1R(AOY6$Nip%Il5Ih5yeW@axIR;(g}w@$ z1Z@+#CRlukFBWZp*r(+&yM~Xvz9*|a2KdQ5r0}AVd1SM7^w-eOHBx1A(Op6wCc1t= znkj@sPQ`KG#+%Zt0~#3QFwmM>6EP< zT*#w00G-X-L~wRqTh{?C@mnN|F-aU)1l{E1p2X_Ln!>ZS!0Vokx|%%E$|jLjAwPyR z8a>+j^#1Kdsd>#PM#oXH<%}DEZ=bu$TZr)e^HA*#t2E*`EHZ>0!4*(D>*)K_E^?jw z{gh{czURtfR1??m=#e8kBkb2U@S3i7D4H|VB$oiq+>puKi{e_PiSj+^`7Wlk-QhQ& zQ@*97S_=Z(G7WCw#r%@ynMxZd*2u zYeTYJFV%bUX+^Fn5NpnO^hAil3yp+IYO^9^cb1MJYazS0){SFsl>55&=e)ET@rg8h zM^YNe#9*S3)C@Q>KBrnVk6hx=)Yul4nDIC&8i1F~I3A@_Bz?9~3@yv1+B$Deo7}<1Z1xO!GhJLx5!}Qj48w;}qD+ydj>h-sA!$9M zR(egV=<9pc&va+TPxlx6@%Y4Nt=O3Hwq--E&39S^pS9qgGfJ(f=B)f)3#tu=&}qVo z>D{Q|(@^`P`806J)G{Wc24psQ@eenVGO9 z;3Z2RykxLOf3=1knj@~UI?7Q=;K>_^guBd-AeNguGnL5Lm;i96)G^Bm$#n^pC&p9I z``UR_0VHd91>Bh!B!hLx4g<&_L%VrW7?Skz@VIOpo$!D*NhiPbLR;h%&ex{Y; z8Vdc0nw}81k#*DIBnq)TE5!^#CruY)W>?Kx%L^N6l5=6am=s+{$;>q3c3GO!^d{Ef z_Eh*znU7a71-LdKxk3jo3NW!Emx4xBR(Sp-$_z-gUFVbtDRa;8%$qu9f(V|QUaRU^ z{lTf#X-W*DIuHZ7!1Ow;n-)skLWQL>s(02f26Rs7#3<;x$P0P5TwZIMzDCamGFLih z3Xa-SsZK#Hc3!7+#e!c^to|8jirHp6;AGOAoORw&pMNrjyKrHfDXC7OqXj`YE9U#> zBsr6q*Jmzf2@_qs){+1mo*gnLKe;eOTQO*%(JT>U3a5|VO9K`&qqRw-lU|*|8la^C zmoR{!a9~7=z0*pVqF3`O;qY8*ouEz*r|vf()OBNGMNUqNV3t}IBwVIONQ?p}O(rCK zF9q$R;p3r1teo_Kf8Q&%MA(bzdvfYmC=rlS!d@y$wY6!KsWqzVwBi~qjTxrEc%h3W z>vUm>&Br=$u+J8{Pd{W?W(~gtji%c7H|b_XYmq{Ycf%M z*(h{k!&YYM1(}3F0edYW?gEU(0b05St%~6Hj|UFZm8Md)$u7DAUbhWjUT@e-MXEdo zuQKoP6Ve_h(KI_?OCZyCsw{$mJk=C_zZU%Krj4$^M@B5*Q3!{F*>JcWH)vH&r zbz#8ZO)iJ5+tfqf$d%fOv53T zd7))`COT<@e9x(yWIam7J?BmdclpOr8}80HuwuCunGAhMBKwJ^A=$~OBGe6TBlhjs z@n3%X18h3-`{qq=!p6Cy(|c<`EjVh$fBgCt|M|Dy@a^-V)JoH#I|6pk8xpjMe%aC( zaGP0GA(;$IyL={`&7BcsLxk4jY4mHw?t3L~de8?O?&six6*kRvmG!JbXGr-~@mQlK zf?9QGs%Z0Y9Zp8wZ2&&2uJOP5=_h>OANc+A6CdTkTTVuUwkD)B5Ox%)xTTD*w+(;$ z%P)98cGL>|{rB(q=f?wwNRL~3vCQtHnR%9F$})U7)R35POUWXU5{Ma}`vC}}26mkr z_KDOUQ~3QTI9kJDL!%0%IrnFx%VxE)z9nZss84~iGe*|1>uGmsJwmKEt)zxbxw(NC zVM|w1(Mijvv~^=m)b4o!+zlv6aP4rck>MJ>PHA@Olq2~}%1B90p*Pu(H=zLV(aMmg zb=iO!88Y=D@6nu!K9WHW)zzMuj}&yyd#gx|hEZJCChLDylIcuJv3l~FoCze13;wNU zJO?*#%@uBjvUB&W5)$ODh!qL?Je-iZ89<3nxH9X!)-mddw>%CpA)X7+h}`1@InL%m zBxg-jKzAilte4)1WQjzYf?ESxht-!$&XnM%uYG|<_aVrv1UuteH0Z#=eOpeZ90*hA zCDzi>+}5x^Oxv;9VN&(q(TfEjC5uWarVF4mj&O3FB`gi$92YZ1wIHJSb7MFe&o z-A>;hM5Yjh5p!uTG@YY%h>qPMCw6g$T;S(R((WqKBRPL9VhB$>efl-&Mioq;rCdIr z;S>fV7ekoE(RkFObfQ%e=GdT#u?a7PW>xRZ8JCr9y%dVi86iyJM5tybm)I0Flxd0(l3kREuao z1cYRwiFQ|ftTGKZEm6ego|VSuH$5|B%Z&HOfiEvNe19A`DsbBvM`{CXqr4OlL^)~5hs1q0Qe&da29a9ygvFE@ zNU;fpObFD}WXL&Zmlo7}-Mcvq{3yhO@ie>=i<{%Xy73`qU^zYcY%z;&))f9ol7wm4 z@{D#A<2WcrrI9R&exwLN->2oIJ05<`hGKSv!tNbo&WZ-WJQ65m9vk)VJ8YnvjNPEc zAX%2H(jh+{2Wo40d%anftQJKN+uTT=Whl>b4w@KGrXdeMS86*MKek{{T&r}muP^sI z9!2T4MH-GekO&FvfZEhZW+;WE4|5Q5k|jhP0@O9LV&^7XbJi)wor-7~-pz``q@QnN zM&g7U>(6|-Z6FXnOR-#Bb*O&S){UjyN!pjCJ6=mifDs~8TF>S5;yxKd4;};7%#55= zi^-&MIgU>9GJqVcBOg@G+3*swjcK7;j+ies_t{c11hIjdnsk*~0bv}q;op7ziog5U z-|#OV9~$@49IbPWVZ+;&)f6k!slf1Nqw7*>9@sm|+Yr19Xl4KfgKSizf+ivq(@0A2 zobcgNGkW=|v=dRXpfeT7;w04B+33G*cvoya5>7%akJ5Kvwhc!q_zHA%b zwyhgZvBhb<9|uZnxaV8X=*`x|-4)5^qQj9`$q{_%T55H(Cu0bdZaSr|$B7$~D3hYJ(g!{xA!S?>EXf}}tlKPud2bE1 z$2u{HW$L?%2(lfnBVG}gER9PMVoCz|Gow-a1cmztgy43NTv5JacuS3<;S ztpX}$^Dk+G%2*Ai!#h1mV8Qx zXM8OzSGAH>#fZCToPwAy=&Uj~e;vh@GnMUBZDY=3rY9}&IhbUPSGj;~Xl~A1EFOs$ zNAWf8O2T!Qi5SsG5rHa9K@Z+Fc5AyTYcw$hL%dVi>?JK;qO@R=63+*M54k3dAI)yx zr|vNzFL7ud2Ldwf53;y+T`)8{B9BwN%xn`ZlhbcU4qXw^qBehAMP{~X>3oe_r}VBG z7N_Pm_1+1TCW;Q@>y#w8!H;1P@UmsR+!8isP-5&y!C?-xZ}%JSw`7Jys)?$#^h56F zFLykSg4>ol5^+?)-hvyxp&G*nA?V~C&2i`Vp6g28{9{s&(Cmw#hHv^hU56B+h-OXk zBFmt`0P+UeTS>EoIzNGR-saCMssq)OaQ6wY(Y$@HtigCM zVd%7lyuK4NX9DibC^8)VMbyFEO)Brq%2Mryz-`D3%uW5O$sMdX?&$X<3%si2J%3GpN@0$A_^4vI7X+J4q*Rw;(}r-MO2zwb0M;g`9Qb zjV_5D3__(bYykuxwcwuaXrdAnm2>&p7L7p~Q4n*h;&BNg0SR3wxg-}YXrvg)Lg_pu zchF=B<&@3&d+9{0jm6d_jTLkeX%gl#@$i$Sd5R`DD~i*pim~X&$<#-He-k1W4=Lbm ziY0VK*ql8{XfY;AiBK%ofSGZ-5z@wJt>NQaMZOVk)&!SLAFjlN$6g^)I+fB7V8Yp* z_)=>fKF^cO-l<|9fEIDjk>@3Wnl~C$IYD+8$luDXq)3ps>PhKhkb(Z-(sMQUkN6DL3KO7#&DMxqBr|1P~2E@0GN6v?ayn)Iva z#kDNM_a-X~jXGVeOGmo7(?H9}7!JvbyGNtiBo? z(y|QM8ZtJjcY~l5XJurR))ZxQXIfs&t$cXETFiomsDft##N-MsAxg)M_+-s77j0NG zGd_<4lr-k#Xo6b;s#KIJc+`r|qu_>&YPAHQatx=>`Z=-@pAhsG*>e4yib$MT;si}$ z90I|eb+36G#ReQf)f}^_Pg0pN0EDw>tXZKjv?aR+U+*{Ea_Xj7(sWSsQ4ZX5?q)ee zN(X-&1%LDPEB@DCf5l5m+92CKOGNl=4fieM8X?bdwuc;3Z%J=-1* zs3GZSuwT^A16WT&U)E$8FiY2sQ}@9U({$fc>aDmV$=rvF8kyA~__pu(>$f*F zt~jcq`9I%ZL1_4=_iy-@&mH%quFL;Ezo;g-6ufR3r3sFzEQlt8jnup=6Y#M6u~)Xi zjMMs!nUFV)HcA;)G_#pxNIU+#-Bi0ygbeD}(wnNIX$k_vkX}2>?@ZWO2OF*hE&z(y zUJ`afHuSxPUWfr~Ox<)cb}cR3y}ea4MAIQ&rrFUYE#4tSBL=K?SLwZlfqB|wLwhMbAjoSxP&Nyk%$Ri2N z9w|R39at^2bxWQCcWY5 zET@TD*5%DUNqv&XLW}F?+|Qn!#$@fnSI1zPY3I_Blf|SbM&eim3j1IqX_M8O;P#@L zU@^L8DYj8=cCfjtU~Lx~%d^XKY%eE?@`YDQtAcH_2c@Z2P&0K~*9iRa$Bw)+e*W`a zjW+7gKTPi-c0X%NOXH!W6XZS)848wKgJBZ0Zc$^4n>W> z^Rp}^#%)VD3Zb>4k9cpm+-{_uG3PZemqyVVHSqBR`&6t=ktcN0DKmxWjRCb9C4OY$ zHVj<{On=tV)Yw2yljyj|TvhfoQ5~b^zdJhzbT2N4wUG6i?Br;_!s=0$5er-2zgP{S zqSU}Ojnb({`pG``Vg;?5%cPw8gtu81t;P>gcWmuZ8i`SBHQiS2O>xh@B)Zr_45=_v z7MOv_%a*`Qc)e-7(YN;p0N}P|Joc)Y?zQ2zC5@RdYGNt1>y8sMN~vH1_A0n#r7CX8 zrl^6++f4$agRhJzv&*RwNvA&3)sP2sbLMN3=torhv7WgP)AVOvZd))F3g<`@%n4$0 z+PpZzZlG=mHm%Pm1SVOKdx~l2R+Th=PJ$z<$ef?PV;D@*$h%$e;FbbFSm^M$AOod=Bu{Q6MM=`quG6Z z3<8{!#!qiAR@e@T-n{yT2~L!;$b~a6 z&>*C);(Mt^Ggj0zG2v*l9=2Jb|G)p^ulRD`@RF0dMBDj?CMJ0>9kE5ZKp0gjHe!5A zMYcx3b+TJhlb?+f2#2{6Qy*SsVmwMUcU$hcoJXy;|FD@{X*;_Yeb0|tk(u!i-@fDL z`whRmz2cRCm+gkVH2hb*BN5>s{oeon>la0}nxb63OXj4n?=^#>-c9$o2Hdl1^1j?Q zeBXC`xo_BO#p5VFR_Cu?U-7bKw0G&|U~GfO*wojif`U@VIg*S;P^g04e1~8~u{JwL zsd$uvmucz3@E@^!JrYs{d0(K_CVdpZNhExn`7egJt+jjS2 zpO1o0vNRnUZ7KDJ$Ol<_DVp(1+@rl-(}sU|f5+c{|Asfqjy{@TBVZ%o(JEe;@x2y{ z(#hBoo*sJO@zT0!cEX7enDypPxc}+#%|?zOk|>9hHAS4rDOC zl^r+JAUkb5<7CpZ6riy&3!&p8XXMila9IsH2#fP%oF2;w={#i&E|EA_=(^FS|SwuES_yR?>W~vHm5u}@a z7}F$8rZksY-LO;_a~4M+n1>6VNSidQ}1V|e}gvR|BBnlE_F-3*Lmrc%=tdn6#Ii>vPW!wh(Nb%N~->m}+b`XNMm z;EcKbh&QnMBZopf=uM^1_oeUE%mb0l%(!m}+m>+Kk`*~lcW#i4O?#*YDL8Ch@X}y~ z>B8D@(RvM^@gNqHyj5O6f%3B8m#3(#sL`g=P;ruM&xFF@wm!McPYHEP?l4{CxJQnG zEQ3Iqx(H(B4rL~?<3TvW=*9%e zjxYBc2*8*7i#F!}GUF94OA?Xp-aInTaZ2d$A*>G!8uS``9tGd`y(5}8bNNj)qzly= zic2}jmHYWRf0LodRd;McX{uZGXZNU1O=rzwZ-N6l#Mnz!3bg#%hrbpc1C?r6VRJ`NM1F@f7m#aF~l+k<3P4;u{^ zh=a7TMJP>I?`HU+5iBbwcsHY-)g~H;r}Tm{7--S3IXs>A0PPb@xa$@ z!`r3?V|%OkGg5YQ55hyt{H)ha6wY_D>(IJ1hB_EvuZ0mkBwaE!8ba5!Q#2^G+S3P8-V}OG z+RP|Uo!RZ2@7ig-9_s%w+`PMH))tyJ*=)n|kX;Tlw`%GxU2T0!kHKOvTgI^nzL(M) z*~4k)rnP?95H2FXSk}d{ai3EKmOV zN|h;}w%-TWhA@M((XiUM*0na21E8u-e`KNetu&28VNyH(cHP2Xp}Z2}GKPNXs&Scz zB688^wT4bV@`mknu`a?mY1Oe}u9!LLgS_1swFpY#?raJ%CeHd?Fd9~gu>1K|=J zt;4l&DkKHRFF*KG(oN`gPuMm_IT}QYHrSKX@SOeV2jrx;B{}(FVWkICmuSoj4KcTL zjrYQGUDu9LrbaoM%E;9<1JR`b03ZNKL_t(dztkhn#q?RXKj)L5`qhPE7o{8P3~!>7 zi=2fOS1fOmlW%}rM0brXYzzi{*bw7qqOlTP47WzcYKV7}XW8gB?QMdbJSS5#ep6!8 zLaR%{B2#Y5aOBPEO?JP{J7X?P(~PJKcdpA982Jp(hP-p&0k23N8SC%R5|80k@L zK`AzXgvG$Wuiqw7U%wRM@jXo@;-@%*`Tb!oSi@xaEc3U8n5;PA-XI4F&SH@<@`h*D zLNuD93#X1auv$E{o;Z&Buuo>kxN!f+YtS?(lXjK$PIKO{jDf13N15HsM3#XtEh2|; zAw7yiLMCN5ShCN`wpLhX*-9pLG5`L#cLqk!o(cl~EqHBN4S|?Z(QuR+9DL0k+BJM$*Muxwy$*`(9Zhi0+$CyVYiG^G(L^(d zYwHE@ACE_mM+1O|H6kxt#@G8DUtc!7-EMf_4{X`#$OraPW{ZNj$=!bJy0wTA!hTlFu)Rndr z(^obewRUG`*8q#T`WDMZy>mio#WHAXcgcN883MtVl<{6HT8=|q6DZSBg5lSMea(mU z4MQ$7>t0`}cGRrPnpo?tWGn<`?rU7vi3C$s>8zsFgW_lnuLOL56eQLL<}fCpqpeK9 zrws^RbHY!wfgpIz2`{Fn-t1m&DdV@tr@1RDDqL#Am)oYO;{V6kyY)ztBw2b#M9thi zA~Lh8y85=$Gt;xgf&eif!8h^;@GtWh5FoIe2Lg-b60_Xl&hE13(%p5-j0|@-Qx)NZ zh^mUJc{VvOHQgN*7jrW;xt#MI%iqL`9`BT#(tR(2+g`Awo>P_a_!fu~iWYEf zX}syF%bNspKea~+wc&#|uxeZDbK7)g>`I;)&$f9LX_>(dE~eU04Ddk~1zVF$Gx%yN zLRd~w@C}+XAaX3;AJv%r(}y8H)b1 zNYJf*G##+Q=$YHBTlh3e8^~gLvy-{hp49EmtBMOu` z^q((vg3Q4aE*c7L3jUd+U%kp%X{epMtXH-BItlTR^}nIA7tpD(v;TSKKs{Po#YP+W zlGiYv4uEJ>&|J&fPE_ zHrg~_s~{iI!MBQD?6)|hnUXwN9}eqiJ#6CQ`5A{#3dLnv@O(Kz>*g(s`ES@ZVS5%V ziSY7#f~er_Rvda_@~n~t;*x(33YZw%H99uNhfa0A+%$c>55{S#;~ey$8ki)`)N@uJ zS^0--^(cS9OrMXhsd4Bvek7wpY4q*t7Xc44{0Z7Nyzz94a(s#d2O5xooVkO5C?lQ4Zd-Z=FaR=O2i)ZzUQdc0%T!3kK+qnulcwy?j^wa&0Th2qqb(M~;1Ei-9q1j?_1xZ^^!?*@H(#wc#pDVX8(>jW82tS5gkQgZkK4YrufONr(5h0fH?O7BoTXL0RcdYR?~+7ylWD*6 zhtpz~vU(!A=Tu$dE->2@f$(x#&8o!a=dPY`4)4pkwy0Ku!5dC}U{IGj2|b0P)GaR* zCtiAktK0@V^?L*2{@{Klm)6sDCv_1CuQLtr4{8+Yy5%LV6nJiD!OCp>0arC7ua{G; zUz8g-to@FvZWT}vO4tAo=YEi+XF{0W;_wSwRMo@g zNaN;sk_CSW<}+-J0z|0GOQ}IR@L)+b#Gr;|s9Tl8RG;Xfd(A@7193JyTaBPPHI z7LD-_I>tXVFLc~I)rmPpSt5aw=OVjgnYl~rr4e+)^oZ^2iP7>E$&0s|aoMD3wE*G{&UEI!+H4JV*}%4TK_mDl}xIGKg!3sf0!X`lEt* zGQ&-(v$)xtw^;!v9b}W!CTjrL_My(SQk*l%2I)6uIMm~v(wEGmkHyI|0Z-=!HT+pR z0h(NzTzys=JXiEU5)rQ|F#(18aD(aK=_aN&jV@xJ&)4tIT^cIV&UE&og_*E;G}3(& zmxkI#q10zqfi=M=wcWQp4PbN#MDiNCYzGF-qCT>IAJ3ROtNQWluAU}tWk#9N! z(K=M47Or&*B+(zQ7~gM^#)~mph~hbWcwf>m4G!YT0yzuSU6w#&*iYgYG6cmba%5DHfYCh|fxI?Q_r~n$RwK7ZNuMyul z$H5yg!qW!mzbM|F7GxE??ShicJIicd*`7T~4_>2Vp`iaCQU_|qtPcEzp%zgu`iO8@ z7YE-E_R<^K)0%Ku7L(uRV%7yvqZ21)q&wrfXQwG!luJtykT4ygv4O7Ft*V$rfy>DW z#$p_RuTB^9z$R;OL<+W?&8J+O+-61OwpppgWtGK#-f!XkudVry0H#PbzGuPfeYf~E z=N2q%#OYk5QQTK{HA_d6VT{zJX}Qo}7V}(Dd|+VlHXcMq0*0x>lGBgL3Ds3{x=ZoD zpFCTc+SYRAgzS>t*KN0Lc-S11HwH`umN0ayB7^~k` zjsN1!aaDArI3%1Y)V>v0C~l=RkdcI8KD%^uQ`_Wz+BbZYGp>1W*MZp5Mv_M!J*{iI zCqclH65h70ZI?ISsteG_-L(QPUwtd?!t@+Ag z11hQM1f=5(&#B}o_04y?eZ+VBr#C07$KY8=ICIg;bRck-3r_09Mh;uY30% zGZRi85p|b>TM>MA3HK8-{ycGm&~0vqc;WzfK!?9hiDm`Jxd@4RjW~-btEz|Dy|rpX zF+h=}4~b35BDfcA=U#EO4>NG$)c!tEL6-i!t1EiM7d}}Lu7^MSu+B_>sJ-K6EjaK_3+n5sIS|kYkoyD#B)q z#$1#kaO4VfTjsy^nL zHh{|<`w}LVopsXXLj#HU5VF~GWGPwmm6FA;Fryu$L(V04vf$woJD5xz;$e-MpcJw^ z!=fX#Gi?2I@}(Z~odd1 zlcjBNBV7?whM*O8{xXX4`2ZF`GD3bS9U3>P5zn)@)!W}wQ@AbrtbxFA1cPj)+ zmz}~3hb1MCgaK|_ap`5XN4Nwn$hrt)gV|>Qet$~1N2sqoY0ipin|$gZ#6!$aoK{m~ zB=${YZMKu-<8VUL3e8#$@)_8J*^f6S5p9|dX$rOf-b)a-k$?3-Z*$~x- zgYK1mGb3W8={4c9tk|VAm{r}NF3HZdL&unc2>tkPLc6GygIx0XNMo3#o~t3xeuY3;&`H$zraRh7_|ZL0I_ z^D}Py)~>_Ly5h20zWi;^?dN7O`00rmcaPmdh%vgA5>$5`ewUKV>^%YrL|7NbIWaT? zuetOsU_HNTt~n`AWZvdA*|0?b&jz*zT{$7~ir1X+G4HJzE08`w9n@Fx?6ot7l~6(w zP9GM9?0R3c6*1a;O^x{KbaEJ&AgkhY$@nqv_?Qds-t-p$pQQj-#rw42L?%x^2OdbN zgA0EG3e+UP+G3U4vz*21V_rl4pS)xG9CXexQ$e}mZ#hYl>8o%T`B!3nb9JwLA z;fMeORwqK!P|MbgtJsLPYDn=XihK;2zU|ejbnu($T&!q-mqdwaj46u6@lYg^yDg03K!d&XZihj7osmk#MI^~nb&8{9LaSaO|3Z!ArA_`7GS`yaPsI%bir4msT zWw85j+SZ8)Ju*+*L{r-#=e3?s{!=3R8AuJYud2AcWh+tyLxfWY&rsXI);YUsz#qq( zF8XiLCE7A%G>$&dhZ)a0ZsNGzN7NAP_F$RMxi$If@^I;!e`b#@5k2^|rTsmaM zL{m}RYy?$STC{EHHEaze;NrWTz~ylZ``1WNVIcV6aoB(MqEL?!WDn|mowK?hxUKbQ zqMWVPgKt$Ie3!tf9b;QC+6f+_=d|BfgOBPQ&#dMEi;_-YI%;_K(WwoQ$#uqjJZjpXQ zv2T6>;Jyp)yEPZ5HQ~Bv5KH&V`R1a@XI(kaH}Ab+%0+-Bp@>O`Yr|VaoK|gr*3)Uh z`^y=p#Y({2p4+|idcXTX&3>+d^;T<4*~rtSHQg>(X0lWV8lT@-Lyc@b`~2Rv-C$i= zOxoybDk6{DaZjXufAOX~erObIa8-8f03+#dn@UfvO?wvepc1;>%LXr{L7F!A_g2|> z9iUi-uy5Up;Ih~swffdm0Goh=S{^C9I3FyB`q)Sjvt3cQTi$GNZ7rJGE4dWghM?BW z*rbGAVbB_Iw|*&4(cP#tVlm${`SuP5!rG;@-}1x>uXzW9aC1-n!lo+PveCo;_Pg)! zv@kxL&R95s+vjSu?5z}RyWn--aZU;EmlfhN{gqAOkYU4xA{HMd5Np5_H`;jxOJz|k z(tWtUxIE#+$@8P#2h8jP3Blq?2IpnLUNXc7IJ*dzWx-v^bUF7M`1aP4IBHY5RwBLE z&`nle&+$e+COjeqmB6<#(y}%gI}z~Xw%gFofA32IK6vwg6~S8(PwN1F*ad`wcSLwu z79_F(PJM52cw4KhVONhKP0k@uRc!voSQ8-!gjPJNOHFU7cNaNy&(8=ZBVZGGT#|pD z3AU6Iues~2HLKO>soE6ThD|4}gMPrnF}b}%z7ireN)@KqCslWoA14^od+87Q;k_U!U*bvcMnVMUzu z>^~yWfuh@smpVNIBH^{(IH{1!33Mz}!kg{=H)rfy!TnZ{H|d#w98fblXXxq}R{k6T+}F+mGj z6H7GvLHj_4h_J4Gx^J8AN}cxMi(n{lQ%bm;&5w1vJ6vYkNFrpgw)mAW9ao*eO%a?k z)YC^|?@W4D{eq9S`GOzy(K?7^+t;f;kZSfqw>=jDJ~ywNBUG;qU51cheZ1s-^e4BW z!2>hKoIN=ZJT=Ci2O9W;GiCCH&wzk9@az7uAamEAf2wL=iw`eneE-uG@19mHYckTa zf47`{Gqc!>`F79PbHTbKJf9P?0QYUTZB!{poI3areQ4&0(n)5kUTV3SKp$m`XJ8i(H37d-fh7m6Eem}p%R7pt4@JJ4ndL|0 zCYHlSO)+nmyDyL)G;V_gJ#3~A@@Jw0kggM?!`&*=qC9>-jXr4_oX2P$$NntT#*-8G z9NR}SZvUCd3(U4@uT4xna|O`UrR|thWuI>w-d!%OXnpm}>+17+)cg(}COvxb=>b`L zwpmn5P6!?VC&HrUQxq?LFQ*l+*E>Gm%xn34Ua*`UHdP6$(iH-n;N)2=+-e+D)eaDi z+rQ+4hJ~zIRhKar-{So2`30+k^2}4`q+>Np#NtY+Q(7Tv1LJKs&ns6_u^yaPO|3?Q zJ^a|^ZAG#8`S*F>p_|E4&z?`bNojzcEQ-sX2PH_iq{XwOuRQy(*{nZSpRf3SyB6n8lg!v{t?^n2p;H{LlB~J#y ziUn4{c8m1PLS`&0VJp@+F%!0}fENab!cj!Kd6_1^=rnb=uhF6SAnp2_o;z-AV(-Otj8qy57R)xbX{;HfH8H|LA8nn+^y0}kJ5Axkksf1pKtNgu>xq$8 z%k)(!_8m|)X;j?6=iD$XV;*wz*2b-+v;hbFFzs)U_y_f|s}%b_%W4$k+szJ+kO4Kw z@kDIPN?cnnaWFxz4pWFP>k$zt`4pYubZ&cT$`-OI+jQ6@PMn

6@CwKL>8N4G%E_13HWPw#vf@I&iEGV8Q@Ib(F~IBRG&AEf%%l z;u=vSI!tRUZ%>@O*$dv367E~Ux+d(o^dW|4FQ$ckJHxH9Z#Ha?jN#;4SRa;TQE=k+ zp%=ek3cyp~Nnb=6IB1R8~_CaCwO)3PZ zWS++*G4@=*%*cE0#Qj3Oi6d$Zh8m~gOq+IZRUnj-hTpPBE>%K4dx2P~wR^FxQ-xC< z!qX|*EYLF8Rdi69wI*Jv$HhP((l?A-E;ua<5+&>Mh~?w+%~gA~Z6IPtXm=C-UL2wv6&8%fJ0{&e3!Ot=-nX-Rn9 zGk$q~#x8;nmkVB=&PdF7z1_hqxU4H~*?g93TJUlObMlVjLke|5whs<`=xy7{ge4Kq?sZ*>@M*u}TYbhk zC0s@EZdoA;yyfhXmHvM8XK^pmk}Vb{>|%}giK$D}T_(={8MDjwS0=1r5K9{*StO3h zk?Y_<+P2&CqM3sYj$58|RrJIY;qDDEk>#pNDR@~|ll+FQuKSMjvKU=_S#VlceEaSNKfYb@y6?DVK~dl>+rZ(&YO>#t`;M>H6{TcsD)`SI zKOqs}S~AXw@qS&Am|Ipg6XO?87kt`xBN2OUeOiHQ$#~m$ygQxn$J?7lNx_=*Tggb< ztz|+lJLApe&O(e$43>m2TqiKDZV@p0w+}dc>6cZY#qOKxM<$mmXAy8R20&5UU>84o zPGpfr3bpGG#fcM2vElNC7wl!T4K*>oUQbXce#*I7i{34#W*OodWi?n>wAHZ-xzAqI z2-;L3VAepdDdFS3JKfy=@~6Z&FNgrD{eSIKxvi+$E9;p_-41F%W+ z115HbYEh%SLvb&z{_tnD(&@9m7wc)CD{#c=(YqAfN^WWtfL$JLpJ@Vc7s2=29bd02 ze#*NKh}?2SiUn~(ju9?IY^HEibl6lf@hCZ!NvhTwEYYb-(WCqosh3Bt&RTSd*AG38isktYL$7SYaLGF(Uo;0FRxrP9|3|=G?Tn%}yNsU?%BGtG!gp zHJhBEO^Jh_w-#ZOEboy_uhXFG$yogGo$`q(&{0W8$!-aQ*NK+~(2+Xq6C3$cD;y1E zQ;Y))3Z4qYMnywDB1i8LwJgFS3f)W3nyf`<1z5#PlzMZnEz8u4dFgWYx+Q6TT__}L z+cZ=kCW42qWX1iy_rJ#*{N@>L9!H-zviV!ql~ED_R3J_^jmxK~z^$2XJsN_hVNKtt zDUMLBbz$(rz+QTMlJ-2k+56vzNmkd@&8zcPwT}>cF zhr*PAL%upu{O8fJJNw6#0B(MJI&9$TdBa2OTnC%vQuEKze1$PjdE8ZNhw6kFs`y!} z&Jys^rQZMkX>dCzl|}+1`!h?TF$7_n=IS^J=Smc{B0I5Vmgds8J=!G6fC{zZegHM; zXgSc}->h3C3e;MyzArLKTelt9kSKeBRUs(ioe4RJs?ur`7tcuTGSTLj6wl9{6!IEg zwAQS2|9N8slpaI0q)uWUBI~N%g$@FHpReg5#4rwShQx33>p$=T#66Slje`vmez@KZ z$WlQ$$?mn!08Knc001BWNklOl6;D*5P*?F}J*%WIEgj z+LUzG%+`pX;#tI5#D{X!hBHM9RFmg>a$AzrSQe9IQ#-o^w$%gqHS*`8h=iXXSOHPp zSE;ly7ObiYB8*wOO{-FSB@lwi2T67K&OUTZ?i*g%B*#e^iOm1{&u^b?$nUj4(c%rS zw#KQ&W6^Bw(Xi|}RJ4H-Y=2vBsuu1;A%zd+Y+JpJdw{mxtAIiCQg^qM{C?Z)YXN*E3E!SCcz0Uyaoezo;CWfFtKfHUpYc<+=Kc4#S3K`0 zi?tGKD6b{s?w(L~mdu(Ga^CUjo-MwK8ShUQ6eXmzU{~PNzGE-@z?P{sG83Zq$vz{B zX)etG*v|SwAqUt0#+p~l&g?i5gTW|Tu%Up3tQk}wLv66-xybC2;+sbyB_?FS7&Ti5 zh|PQMwmOGvDf)os#J1BGAfGy4=$sreAaoPMh2@hH%sxj+@Iz{+&5XsDh)Q$4%@(`*a{7QQtnPKEj6_O`EecYJjYI(cPr=WE(doZG{m|2{^6XHaj;t?PPR=> zj&E$V=t{Va$l0I+lp=9X0P^iXtnw@aE=nhLn5J`}!S-9fDm&-#Jse@``kWP>u8iGN=96?mGfj6)Pw7tA~A=JlIwb6?n%svQ;kz-W= ztT2z7>KvEjf4>MoOZy#afklz3>_l8rYLQGT8glDgR_Q~eb+Ko!WNqM>)wjLLOTpsN zW;F|3L~+{+(wUG}lU?5K#i#j-^U6@)icqp{%>Fl1IMn8=A13y>FvZ;JnD$-=C&5ol zGy&|U6QjKsW?AaU0@M(K(zDV-;ERJ&M0JSsi8AAdc(SOTn6|mb2*6Oxw&OM$cVeh} zEkX}5cRHv~1jnfJ8Aw;*F%XY?pi1}k#<$92eJfN%u;&^9M`Ppr1;0PsH%eYqfK0to zFA@(xEc?ER&9@gn*Xlj5gwE;#7VT1b4aJSbhD&th=LIr&{axaFmclfoT2YL=TUGnf zS|x*&9BnT!Z~k>Ev}nw31{%z1T~e^1FjDhsDwf2^V%xZT5vL7P+r(@|QFL$LdtGc} zS&Cru{Mp_8s;Lcd(Kd`GowtAf>is+XH^2RJT>tj(@V4!?1r){Gw&QKz@K2vUzL&yr4J8^%>iX;^OeLl^MGee=C)~Dt(Foi$j32 zcdX(Um<#veu+V{wt~xFj25WGDEXLx0DF0ll907E3$x|QJ3_;1}x2*E$;*IupFTQ!> zwoS9Tf{{X;6~gD&>#H7t)VS!>&yW}gsotoI0Fr#mOMP%eE*I8nc>xXaie}%l&o4m#RZu(E;A2-fIHXe$43z2V)uf|(%#{PXpSk2zykYkp3=;N^U3gMoMJ z$t1T-O+v2%oYw_UWo>_V7K^g_yk{e+%gndvfG~EjQjJ#YwpE%>Y*KIdK%-Li1$NyF(4wvi2(LLq6nMX`c%N2#PQf1QWdOGY3a49OS83#F zh36*w&q_W(;c&33+F+AP{oI@eF0F}lRfu|)vi+*X~2Mv*_dC-kBB})4RXpyTwD4wOLp4qc8H3O z721?rq*FZ}H_)LG3Q}Svn`%p_4yANIY%QEdGgU9v9>G%Lwz-l_m4-b=`=&ZgQR8Gp zRe@X}OBx8=kyO<{Ebf6ibjT6TLY-0DSgP~&!pspr?JVYyLP&bdH>|r|-wr|m`8QNzwU3T0jQ`pjW-qA2*i%&d<-&aCL zaeWb~_Q@Tws|*4Jt-)?W6kE?aruJNn(N~j~)pJf0jm3J03fXog7eQLM#V$dpH5Hrt zrPhVp@4XkrbuVowQ#Y4;7FN(_^`(=#TrZ$|^Mx`I9 z9Zj)NZ4>=3uF~@yfQl5TW`a(C9?tq8bEk;;qsgl7m>?QBO4>HhMHHfKKc@rA5kabpf0s@rIx%Egr*wpJYAVr{CUv8Ntv7y zzRj8-d7viquR{wFK$8ylaa>~zgP~!(SEJ#&^q;9lNtu6qA=^0C#{6E2!#C?a(}(?= zOS)h4j-R$WPV|Pe_X)p#c|n2VpFX|fkNb}A_uaEw>i|UCwOg2xi{R$6!oBhU$Us~- z6?`s&KU{Bke_A}J)_#}ow>$2B4-`}?`N;<=J-%vTZUf@GW|t@vp3;JKNqD_)*h;~N z^Tl$AH5&<=>|L8B<1QI5e8Mb*>)^7eX!ZY|dZbiQ1*@98+`p(w*lh@5ma4>XiMI`x z&dj)Q0*T;N3U0+d`mBl!ptuxx7r}+e6kaZCE`E_u#8_CdyKS%WE&?^ZT5>8Kx_X(7 z3?29mZu?VoD)Z*G>2pf0hG!q6W{59+y#A4@1~EIHdYoKV#dAsn_;+A|`1>_gi-Jbb zuHCP@cKas-xJoe@x!7Q+S^#W2JT+zQQAxIeQUX#^Ea!ynrp?d{JvImtwnWi~G$E~q zLiXV#`Vc01@KoVwvl>Mu6#QG$rdD;XHXi&l=9;2QE2}(ixbp{j)}%+RWnQ2X>#Ug` ze8{8dn_`^6Wb_;)f%CP|V;sW}oLPyj53BD7o9@Z87Y7$Aa?ExeyN*flJ|}2I(3Uny zY}-gT!>dlv5j(uo!gjA#&rbBak(ux-e1hQ8v?{Aoqh>T&T-fr|X1PK{I=xjoNT)UT zz4=*JlirmgkdiHEUDd#l5Nx|N$XRVL5e5J)_Qoe-vOA%hZ99;&bQ!5)jEnMukD2=P zTQ&IU!-jXN32U*M665Kzpa^iK9or^N8PI%!9{W{1MV^xlB63j(Q|m#s%{jZhfM#G7 z^>_fmv5|^|&nnA8J4`hc!?B_0g|*Io_yg5O+#={Cv2WYn0H2E6g*JGQ9$8fVnF#(( zI$+Bw(sLS2WSH}ady4~!wyGBvm&eSYWC@-+`?1RS=5XxXRz=SBkaLxTBm{`Pxk!aKGbn8>`DDOthD#Q5s@ zj6eJOJwCj9!k#nkd%?f|hd<&j1tPw6qTZ+-dcr~FVDA-)aW=j^u3uQay@MS_yL3&} znw;Xw5U$%SZ;ax5Qd?siNZdjVDmREfsLS0b4%piJ#LoIs6xYpUy!DOI7!y%m72c~u zc5nOaVq~7q6ZEFsD54$-BW-}#VR&vI2;TCl7HS|G|9)9*DEi~=?w}p`23N44Z<{nc zGa~l$L;UB*L3BUf?&+xN<`h#a+3f??h^u(!ce@{!oUJ8nDpNbRAlf$D% zY8&+daj<@gH@Eed&7xRS-H3I5L>(NGOXCqpvnB!6Mz&=ghaw0=UI`h{ZCJ|V7_FTP z@mUNfNsUsh6ybRYOu@=WVQ@+z54Waf#OHO8$x>GYDaOv;FL>&#SuRwx|Zx%GeTIi^Vk6&#vAh3mB)=nJ%>I*D54%Awu!Qn??Ho;miqd z#r(lT#&uGZ-2dL)(@K_PgNmZ&&CV#eO9#yXzzQc*J7}$Dk7w#$cG3Q9rVvYO_i~h> z6Lz&c@4Bh~lnXvwPA#7}fw7SFvde@enO4AEaacDDZNeQS#Pfi)dM6QKPQz@3;>>k` zaE)fh`0MDvmpaC}&>+){FHn659Hr606*mmW1wbfPLhagU)??9HH%kNhbj||~5|=|I zUK^VLMu{HCVF4cjG}I5gaUV9ud1&x3)=ia$PM;1Suh=kX_jFF4=vaUb`OxseiRXx+ zObYe@k?vBUF2l3vmq4EN29;g9nzOhZ#Q{PkMrQbAu-Jyz;uffTcvUB=u>rX1>bnA- zN?)@Eh*&_EHs7-qrMVR3EFBtzo?T~?_72#pCm?&nR43YLF<^yV6OT4vVBgE6is%{C zGBk^QkU*Z@%8>|7Gj%uQ^blcL>~~1nd}s(X%AW6O-ymm!6g4nQrT&GDRHPVdD~{^F z-y=l^QKIT7we_tTMIvnQXdj0xIuK%q=IfAyJsNj=!Q0)D6O?xvzERJvbCm|C8b%>~ z0~|;54OF2M0B=0^TI7p%)yR@e?z}FHMIG*@-P3+b3D4&fQYyHdtmu8aU2)$nAJin4 zCbLYO49-ymyJz$@=uK4|gb1HO@j%D@1A^X#=m7SfVY=h2Gf+~SIt6H3w?1Rlut0f` z!(6HHGJ0D2_X=m8PPUb(2n3%4siUktO*g8EbVj9 z7IU;X{H#*%o2Ef~92~SOCMwvc#?-Ve_>}t4&iqLo7VkCYy1AmhBlH<*{X46_wGL#M zQuk>#c{4qLRzBK_kI*b~NhZ0cv_f9k&z0%}J@Epl*yhjAtD=f!)ONS~q2k`QQjo-+ zZ`;hG4Ir|Na(80ey4rTh{`)oA`{%bm{~7+x|MQPnUDC9R;LHgtTS0nG7MGNRuifO1 z-B&Gb7-$mTxprxOX6gg^HfN|g&+2)j+M9oa5!rhy`!=ZcgcLTU!Q#~M8x#g${fvlq zG79p1Xs-{u^~T<*63Z1oW9on)MqgEUV}FLduBfq1u)%K%Kc^l&+3)=nsKQD*M2eQG zXbZkT)wp4?+9?f=AnFq4iJ2o4@I5|LT<>4)(QF*3u^&z+d@8wt!&X-ssR~|BC%-Q$ z3`+1mEjT|V{ObLCFcIFi8~*Y26MnqivHNwrB!ePVYH$kJjzqC;0X{5Cd)%{RWIyXO z6hGcKd{`~7_x<_Q7zpf^0wv(P`wg22u9C5kHM&bm*vpPx%n!UU;r+5;bMiHL<5&Q^ zZrK`4N^M(c!!!y&wJzyp|9jN=Gqnb9OD`3dy7#kWVe{LHDmE4Dx!{}#Pn2*k*`(jh zX899fS?n3eQXy8_i23maBmnLngLG!bb6T(_`<{0ZY_-l#2JkDj!T08O)6x|ozC5WU z^V%#M@!O=;i3pp^;H!FIlVVbBwaBQdDA+_$pg1inR_@m#QGe!0N0L0EEkXMk)hz*j zU6oMW)@S5)Ap3K&xupISh7P6xWP@Nm)Cp^sEmgRL6b$jc(YF}kI^38aCns?MJ&GR>zpkzLSlvzS)0ylmrm-kngMyyl!=) zhX6&2(4Y=&h(6FT9rfW%d+~}*nza%=nTGU_zWk{T6*>VnNOi5yu)lU5sHk=T2l*zv zh+^AvlUO&OLd_y(!s29SqkH=85>_4kaWVR@5t+?5oO8&R^GlGp<^(HbmpQ@1i$&W= zwrS?j&4uUJ^N%aRSVad=Tmz2@HPFXtO?ZB?M71RguD5b%JauY&C=kCjY-Pw_^h4l0 zMUBUFK#tbt(FQ?%J2*f=Ch6JuaBBnP^?B4WgZ7bXMHE9mc#OPI8Hx8p^p`%601q^C z>KlE330+nr^XA?DmR!WQ!4}B{brp|ak6gr}k;qg8rD)&08$ASk55v7tN#DJHG!6`ElUjE^(l#LVrAY%{T*JMuA*y5; zJ`UN{UD~U9ymt=yLSc9?*igdJKc0sH7|CIC6TTsn$(k92|KYFyCH}=Peh$FS9U{W_ zKYqe*fAdSMYr_Bhzx^M0+jf(^Qi;iij}^gHb>wofDtWGc@+^X~OOrDfG3xGm^7x|q ztW`Okf5xKZwh%(eVGFMmls%JoUvW@^Rr`QA@ps@&3PD(#bdLj@f!aB24PNhgb)2!$ z7~|9HmdTQ(-2+>Wh_Py8q5$jCpf#(|F@hd! zT1Y6et&&yaZv=t znHSIGCER5Ll<>5eq_<|#76|UeuF+l0AG(tL?ms_Y@T=!1_#(7CiV!cahl4DWeWnG{SXk#MWoMis)WDz-_1%JF=@!kDq zqY^dJv-q=EspKUj*BtFxy(g;go@%43)bT3E)gtYkOaLzE1CgS@t?c;YzTrZITfp?y z0cZ0h6C1!Q7d$PB#TP}pQ^whCQsf(EG&rreOxP9x#kaBS4QzI~d)?lye*fIXPI{w_ zGcGB$dwKOmPj)&xIT62}%>~lV&nmxO*(B7p_N>|C;u(0wL5uZ#VLvZQg~s_niM8gH z+o0y;oQ9Tnt?DsIZ7PZ;TWO8QVsZ~@8?b3gJ?9UhPv+aab;|u|TUDnpkGX~up+k>k zyoX!s!Eft@bogEMkpwt;arC$m!{MCOAuqMpZ6i_nn1ZQ8&Zs`ZuRH>wQjCP^+qc$G z$OGV(9syl5xteBR&;)ED8Y$InBTe&mjmB$FLtxFY;X#ObL6H2!f@72f_Aqfk`WB2Z z0FsCZ>nY)MN+`L8rP~7=*L39D;dSBeMQ{&7yyw)3s_h~+KYZNu2YLMD`&7{mgW+D? zsu%0kzZcO{qa41Iy!RatDZOtR2($ggzAOo+)#6F=UdAnc7!cGZs%02YlZv*PWeVFb zv}lDWJ+GT;BEsde;Q4tq7*i75@1@K3J5td|;ELF)P(m`%p z795P24QAA*wFre=C5I0EzM>7EC{WuPJ^lD<@hn3&uv?QT(jZ+0k18R{*k-2?KlXaZ zzsHJ!E}Eoi?VD&M=_p2mt4=QvC3+mw7s%)IfHOe{4hA*vDM4{w5`OzZvV~6D;oi^~)ateu?4@t_m&6{G zghoCd2>o72)eV6L@Eb=pmf{eVMFEyqd(g|e*jXpi=Ufzs(*D77RN0_!_2UM5_KqJ+ zSA-ygY?z|ZE+NR0?Vsl*LFDezWK+kSeG4^M!N%AGS0zY$^s`I2_oD5c)IWchUOSOB zd2G#p`Ths|cVGP_R2BdD!;kp->5MXOxTd((VtbeRKC5ak!Cr?YROOvII1UDyad=`hWa(8dfKE!e z_P*6_HK??FiuRHS+L>xN4GMs$o?Co4zq_iYzW3|zT2ztU*C~_2vYV~lI>m#+;1yhJbalaHS@Dz>%SomB&w0mHcWZ7y*!?pp{=0uTU+}hN zylxwq3Ey3>_)nid;QjfGQ+md_Fh1U{02I&bikH&~d&#($Y|ofm04%<( zd;DwOc5!d&Ldogg0@2j&AE-TdXuequoBp6$grEo``*q)Pfv{o)8__x0kZ=>l$^H1d z2ug@{+|?m^eqB?|&~-U-@fg#^GqgVy!D-vD(Ak$JHpG&!kQ6aU)zbR3Uc=MCU$6{c z-7An5cwSbl#Q3;x*u?(s?EHvIaIaq7YC}1tWE=RZMvMTJV-D%47G( za|`0CixV+a87m3iaz+9nv%NPqkFi=1at_<;K`jT85{=Wf=6x`UpDq?|uObdEKr6Ca z!=~f(y8CW;JmA7&Plbk}xK-lRWv8+6Q^7&XG=dLiFw5)_jC4V5(z{{16S8J1e2U16 z8wVC?)SScuf@o+9djk<=pbB1sM=BDHNyk1pG+aGPW>SsoqmCC}h$hjodC>l18S<3% z_;aFPyKVDm98*O4DKUX^e|f;Cn6)^olINTi>$>2oyKSbj|3($MBIfr}QEYeHO4J&< z?9w(laZ}4=n^N+KqSlZ-Xzqw;n*JK1J^q~oCDbRLO?4@Hr^OLDol7pSfsQ#nwR6)dL;C)e2~|^qTQnx3<)F7D1xU zggLP0z89QN3qD^rn*e*TF1Ilx4KSkSFr;ye)R%QMNUb{up}&3WyT%5|-0r!0zKKVG zSVQO=xk`%-yod)Ut!?Ft$x-jQ;Nzz^JU>0*61*o)=P zjw7CNEwABG$XPlx(C=yNz*mTy*5?1_L<0%x&J5<1CWbX1Ncw;Pn!3_f7n0L=ruvS+EP;J^Or8~k{; z=$|*qO~zRtsFTMi#Zb?BujLAs3Y%ZsxP|{MnA%oP+pq%P&hSlaT$lX*uXetLfK3!a zRjO+DZSgJSLS!%|n1`H*@S1nrOU75{3%=jBwiVt=@ho^xY;aj=7Qrp=SX06u-`=pP z43MGPtZYT_k`{cmp73$s@%_HxyM0Fj@Ru(yIHd(|_Z!|XtG}$J(V0KyjJtn^s)WD) z^bsFR#@6J<-8WsW8i^R+U*B*_jC;;_e>vgvcE_jt9pX8==Ouamw8aQjxZjgYus?4b z-k&ZGxRh4M7Zos)D*+hKY?N>Xa4*)ZG1>O>?m)SPJg%_4-_V-hrS(bnEWhrSc`N|P zm3CRIb*|rM4KT89Q?WQU2?NF-@{T`$dBIjP0ER%C5=GS_SL=JOMlji+!|XCIoRC$p z6={Ryn!&sm#RgLyojJ8F^P9i#7EWz-l4~5Et9EuDUf!hOWm#}@l}z&cI}vc=WOhOk zTyt);YU3DCV?i`3x2rr7wRMX_)DrGeunOVZ<$|kbR3GM2BR^_0R-0Y(uk|Ta+>{(d z$U05r1dq*L8tIDT%#EU7kjA3|G{J0|Pj6s%w5-!Mb)jGskDviidr(Fjp#RKh_6k(m z>ha?`r6cZ5yH3S)98ZL4uSWDW*>vv0_w z-)G*4(WF5QF)?AkNkcati{ZH4!vI-Q@$Gi$jhjl}3R9qWTfU@ich!k^PtOasU66Nw z5qn@;-Ua(!u(*7*sT$@DfOL{+_X)=>e)oFC;ZeOVsSz$^81Pae59sDDFRhw&o@9RP z9$e-Y%|u=_i1eb>e0{+Ayh0VY-F8R`9&puxU>ha>tpN%fRY9k7N+T7T*{xDDn^a5G zB!>#*T(D=szRP4Q=svfg`yIF6!3Zk8EsFHYA)e+DvPh$Sg2oLpI`x*6nnZ2n;OQ{S zVd`>TjS>j|!%DV;l~eL&PJL(xY&nnqK{{l-HQ!`nhI%(xL|Rey;#r{5HfN?>AkSvH zZ5j7H5zft77SFIt*Ch5|x*@Hldpul+Co``_Y! z`XB#WynpxXlH`J)|Lh(1UGe+xe#Gy-|7d4v0u~y#EettO@4 zOF_!q{_NyxCDBN_))@^FJfOYf7SiyHA~e1dz&-t;&3;c-KfJLB1UYx&Fr-WB+Is>M z{Fw&i(HE^&Q~_^~TcXy`*TG=3k|2`o`<6}hC^e%H$yn>Kn!8FKVOEK<8YtS0+*+Yf zQ2gt^`+NM=w?9J>!D(4r97+=NbJpSgJr@+MjXO0-bfUVol(uE6|J+4pn>TrT7K=ARj3!e&tqT@PxVY3hi-4Ady$Ig+9lyU` z+d0id!y?KyzzKi-i(lf?{e~ZJSNyQu@tSuq$gbb*ifhTuE+D=QHqx<4Wq-Ke@I&5P zU8NM%0X{&v6~T|UD}LH;cINpz^z)uQuh@q6yA8g5&gi8tb@;JgoRb2Eir?o`=%hk z!392vip4#YLj$G{;n*Yb^GmspG=s%P>81wC>KDVMDiv`4*dO@eD7NPTbflAHnz%}L zD|E^3OWRNpkUWzt8HKo-;&Zkwi8z6Z{iTj7UGf5I0B484TVvH-Cz*P%%=WEh?C3q> zD?)qfi#|Cm8D0p~T(0BsOsL{gQ#J<12BaJEn%@(rk_kj2Yr*}Q`rHEfRrz_RpaG7`24459*>8b57vKy6v3 zi3yTIA0|E*iTdQG?8w)n|q^YGZUwuQ~3Zmyne5WXxpIGdxL{Q zq6m&QmOuenI5}z7zTdWsOYmuc9}T``j7```c*YP6uTio(F(xW|m7>QuP|ce+Qmm0( zJy$gsf%r5%O3Eq1f9ikVGI9z3@%KOCZ~pE7g}?ffzlQtvhva;Odgonvk;?^h8Rtu+*lLn?Ie#2(Ts-9p8PaeXbk~$id&u zH3K^!V*>7hb4Sq*) zWULPAa&<#hwK37O-9P@ox@D^8Ki9^T4fU$Q1~NQ@fpz@D^@b0hKeqvU zcEyV@ok*xXHY_BM_?A$JM*6(xmL`V{WshwlBqLzc6t$&Oa#-L>Y^C6w7QA0qT+)KA zWW41Ps#*7pGYz1yJw%vwIWo0q7wQI{o(1nJgF*IK8+p_%V#4N-I3VIy?)Wl z@Uy!L?6NLcG~xMn!>yPF>%JFUl3;hAXcj^ChU>cT_?-84UsW+or35G6xPLAgFP96J zl<>{-Gk%i`e)s7o=M&i9mG^?x6)SgDWEhy~<88yc(;1)lV!5#tnFHFFT3R>d316R| zpeneP9ZZbNlJLR{7GiA0X~e!2PYv(}e04e@aRSkT^LlD|xpf$!{x_EJZj(^wwWW&2 z&lN4lTf@duJ^Q%s+=zkqi?wBT?d|JQVw3_f3J{=reMpG546EP2J|sVEJH9=wcuK3C z+1$?+_8LyY07l)urw#sPDwMjZPBm_^sZqL;RmjfY=LD$#JN2=9p#)wQ`)u8ZOCk-9 zSiR8|(#O6zH${ZtO$y3xc9N>T(U8#`8?qrqPdjvMb02haN&Ye;O^FD!bSX@SJkbF< zb8wc@v<=pWyoW#We>4Q$MDZ6XO(8fBHkzbIf@7n^YVhz)Qe+)mknQC)S(m5gn5GR? zyoNI~V=T0wL1ixi7NKE-I!Cp}EqH)1jT7VtD3*yjI!w3cZkgy21~URoAIhPJ7#BJK zwLnV0h|H%TD;hti=^M7~Vu5EJ#;6L5FJi6@oyj?CbHHpMfPHtVX3fiVX&@&%WH}MI z=G&_KuBgTkP@^%|ri%T4i`l}pNfR+2W&QgjWm%(Kziv~rAoP+wPq_v>1X67-77bDF zxngvecF^*I=CR+l3^4cH=80}?jg(Gadum_X3$WVM*xo+Gg&QfOnUtMtHsLzDn`p~lFTE*T;lye57bj(klpOaEoQLNm-|Kz z4b-LKOce*~^a!80nQI$S?GbWutJnRUeUsUZZ~k7>3=!b6ny<1d))r=@LQMkvS6_XF z(`q1^Th4gBZFtK&_F}~91RCh=_4@B(O>~wryhF4N;QRSnfpnFaz0{y&9pinNc$$Pg za;Z@O<L21c}yx@R6xqDP-CLuvt4{ZE;_KM2LPxoKkTfR048kFVE$f8D5jpPHiDWr zTR)3oTWA|a1WES2WF%s|TUXpY58l`dRboomeS20!@%{aV*Sz({ZaQCTBYftBZ(d%o znnb|T^n$z4rGewXxFUYZ=RmqYSRm0jRb|L5P{5S~e=aZHacSLOhBHO)d68)TlV%(*Dm)MeFoE!eW)l(?z0 z>|F8uZV)J$^&@g#qF6*g}7b(D}--zs$}J=YDd_GXfmp<)t>Qn8}pVd&HJE{jMhG`9p$8Np>O zTB0Qp^a3<_gHyswxE3YDGZ4w6YTlYS1tzApakK2q;tRI0uXL}AePaeCm%wD3y4Ece zEB8<2pVtXgv&@R23yhPqy}=P;}Hj`fy=MCRI@-WY`vhlzm&*{oC7Cnxs5C_@%`x zq(IQ6Sm$)&=rG8sFXoV=c|1q@;HpA*#uy{*@>&W&kgz?DTwe_jYxQ`|J(Y$ne~r}Y z%!A$x7awpCCP0&p@>$!YS?tj2ECul?$FeMK!^_DsSGf*u7?9E@wpF$zQ-nwJ+(`Gw z-L?Y4j?>C`xvW?dgH8+XdqMUbPI6)>4XPOBvbVk9zU2lDVprKHjZ;((?-OniJRGxlYQf%Q z`h4Ib(Ap)VF~hyWaq7|c>9(1qj2T})ov|*d6YaybXXZ(CYxhwu)>Pec#@l`O0h@VF zfBW;V@pM{j5CULbJDHQH511mzvP)8};8+s3>n2d_rC9O9+%vAV(|gq+Kn(y`2OqwH z7Sv7PxD~ELJK;W#XnE7`_S`JDN zOv9oMwXAWKraIZBGzN$g3?}9T->lNqR7I^(BCypJ-l#B)fLwjQ!&cEZ!@CGnY=|We z1WGQE?Rn?jJKvHj+MBGj%-TK3!;6Liwg3K$)9~xY7it^1%aZVV-?5SbjW#ivGP@if zTAS+r4tc+x@RwhGg@69|5$Ckvb-Uwv-|=$3;5Xl#@vBnsoBIu0&iMD={{e3$V*$J5 zmhscRyIgh&O>(ya4oenQT=yL(PADpPUQc*`KI6x0!FAtS53*2l`MJTc$YbIVpnV|o zsb?*yIb9;g1X5Qr)N{VRcBB%=<8Y0vMpKAO>=o>=R1UF=1xA5hy%x0yGrz9>eu@Kn zYM)<7h=B&xkdc=6V!67zn3vk5_co;}4*S}Rd!50^Zp1e}16dObU={NkB*Tdd3cygk|7+H&9+11~D3hjH%G!&w`a*58v*rcX_M(nk zUsVAdHUl*Xk+_yc{A;8RY*bOi3i|quG<>!ix9+pIbBuNj(k3t35@9_jtZS!0@1C7o zP4>i2(A3`8q{WKHdO+6SRn@>q3l^+v&wdMo_CN6Q?tuJi{;>xN(HiY~e7bHZ;Yjzjj?y_x|m%sTr{@vgIBbLPY)i>{bvz3rb!PEJK z>$c&vEDq4K#;nDTfe#z_dKP}ZIXKLVoh30AG00x^g=$nd3^UMrKdBWNzSXS~=r5_2 z<`*Bi?-gM{xv*_4NNdX%8%H2eIQNLh?(t7YRrUCUx)yEDUH5PA>u{}`0jS~ zv#g#^44&TA9P;j~%v9q$KQ#Xouq6>NRl$&|Hk4@bm#+6zmxK;=!<0 zs24)nvyh7jc143)wRhbE#hz0rL$Uf`qbkryYt`Ea6Ln0v5IJEN?HRIMX&%Zv z2~ECjL#;VFEh|=9AY%9Cu8MPFEX3GtT#lCQ>hLcj;4eRXg@5tkYyAGxM|?Qj8JvsY zy4~H|$#{RcU>Ct(FTchgUSILu^)r6h46ybl1)uJBT$c6!CG1_FWI2*6zvB^+SylbG z7Z<>Q!{KsiT+VyHqaphhHb1r$q5mZ(6|PXDqft%Z3~ki>H$uHFe9`?q8^6|+{MWhZ-i4& zinoH<^p95Rv1erW=gqRxn;cMd#FXA_^5h8gpttDYqZkUQw@qx1SZOgmtx%v&{ypTG5J}r|G~qw(+ie=& zCLVJp#!ZOOZcrfaY;WJ>MIPi^$K;lyn~@GOK00h{X;{bZa+QB0PwB0+o%T|p-5z@I z8p_a!_{AEmkgfXO7i$oi$7!ZCnx}6^QX-@j+H|$rHcmEc7OS8TK!!fo0%^awFcq?W zKxzX}$q)BTP_f^$DN=(b866Qmndc#bz~9Ff1J$=A3K-&Dao2_r0S5R|C{Vu-Y|<)r zIQZ5sDe1Eg*~9HRryc{NubGDp6E#WGIP57V!_=RbJJH!J0`3Kj0hp4~g}K(|)26^Z z`o;hYdmn*c%e)h_HOnN){+@Ozp1kkR+mS#p=oa0BH>y@xz@FRq#*gZannx&VvjhMkYs8E|G560;E`_mj#j+JV-Bx^gT_F&BIM4XqhdU+hF1dYP zp!b;reqZNO5dvY$6|dLTB+CVX6k7H5<%;W;@pwKd37K_ZHl;XZyzU`oz6nk-;5;XU zHRE!cP}#E@6`r;Ys*Ca2;teJ+JI9_o!Nqt5o~;_%z_1To z_W~_xgJF0xMIE#R8^(m7pNCVzRfcWf+t?^oqyak}LR%sD*OwQ}F}CkD+1A$wT6c58 z!=_9LsioC?)!wKn5I)Q^?hL^8^K!)>pP%r<-5vhJkH5n|{`_a0Q_^i=jjawjPZMGY z_~G#b9xfL=Je}}(yW(YCfo;KSDfn=jyX9^yK8M4D?XyUv1n8wIEb>+gPBG%k?S|)~ z&F&lm&M~P%suXO(%A7&K9B0H>cJ;||)k6pHEP0IAFgUH!m6M6OxtxbRKMg}chfY>- zd%h?aR!_w#1`yj|!fGp539uEVRk$sV0BtJgO8Af>P6m*wrsArH1)WCR5eCssyN2QL zD5uby1;n4eZnKy|#6+a(04@+xAMPncyUq=?C;ImnW)xGkI^q0223%4^G>W|2DagQK zI1K;%qeupsrey$hv#v#2MkTUOKIsn<`xalq_MnvZy8Pd%V%h0XpqRk zK}NEFnCx6$LJhVRf;Iwqt2^0hjKJ>sZzy_!f}+YGYo-BQL?(fb!(+sA&xAXz*&%S# ztXrc}y1dop+0A$2MAp!^hYqOmri);b;4H|Q2LRgMlCF}bF0UeMHkkX?SGU#JO&{~d z$QvUiEi7|U?`Iu~j35L`1L1fmz=D}ZiRwVxRbqB2J;QVKXL(i|fsm$%`}+x(^Mox| z#3kcf<{jyhi2Alc_RZ`F!Wv_#sE#r;8Vcgk^M&F4=(4du`joR*)h!`g1KBdldw2BblOQVA`fiVOUV2 z^c=BlrM<>LN|U}jP5AJj4ONQjXG_VpZIywb)8Slsr;KoD2syB1Y)Gtfl3_>?NH^O) zj15f!);D>buq-yL>=~+nM#%WU<&S{SJ}GHkb}xc5F{ zfZ}-EnoH#Ze24)oTgA8AhOgHA-pv#K`iBp=%oEnEGHnDMVvQCb0W5Ig0000W07*na zR1P{Q;M;Y<rSA2Rn<6l2N)vFA|i%yi@GBQwGD6>P+@3MJAE!Tz_zXFl$|U? z3p8Uq2q00q)Q&ZOge{(?rzGj_udpd++UGe$Ar9iGU3?k#N-G+m8Z>~-d}|v0*}R)= z2MB5h*)>WQc5#tt12r4&+ItY1EUWg0UJiNep${E8R4+6FE)5h}w@G9k?MYo$8U~eB z3;}Z_Od%nKfLClf!17{y2xT9EZPbPh)WJg6EQ(v{iPPP)21Pe0bfHAr`HsO$?i)!J7wZY!ewi2W=XJ@FrWlC#~quc%_OW zqS@8f?1%urT(9jeJjZFLbPGhFs&Qv`X~L}(Ja1WnQXo9#jQ{& zGnQI#tFC+qMn7k)RSb9)S~^g*S6j>)lUW;LZ=yYZY>p8r1WX34T5ClXHn5@KRx4gh z!Cj1q&Jkg4sAszm0pMZC2fkGvs1!QDnL8 z5h3-wP?yvZxQG_aksw7Izj#ub9G5P8tak7+10kWwBQ z==sKd7&6LyCL$upIiO?Wr9>`w92D-8a ze#bT~(h|J7k0%hOSsRaKtqtrUEGln>HjrZe*;X7G*vKI#{^v@mZ4*hpP4;FwKr>8; zHFk@*#~g4kygb*~v);z{s4%0hY+Lev7=)6@#30l%$RkJAnxE9NX3-#fZw9v+J;*aibH6 zcEbnkz%>}W;^7@)`o8e>VU%yN$+oBKR`l1VfPnsfR|YPZ6JDBUtm=f|RY z9%oS(rzt^5P^)I*dDg3MK%EBpaGCMLcXznI%y_+Rs9c9_Q|%jARX_{BCrh1v3d55 zT8o>(H~{VEyZhB^GgcTc%L)SF z>9$$~mH|-=qaI^IOcBqw6)8ka7xf~~DWY)2oDxzb)LKE4Hpit@q!>FO3Io-CCs*Ak zMyOOiG{3Sp82+U6ZIzgh?J^}~W=s<`s0}0nkw%#NZXhoQ)l#IOueY%(yTw4Wwe|g~ zESOVlQdZdc=+ASmg7X})Rr|9s;L0+<6wzREE~yH^H|Zv?G=Dn}DiT)|7o(_76tO}F z6l*P@a8h|}ia3KIHR0~qq)02@tac8^fwIea{yah@jB7r|>Otzh>eTesx4k!F=} z76DEvwwRg6dB&H;t|x02PALuVc?X9<_G5fk4GOr$*?AK_W}!MaqJne?EDG;}5VAdo zSgJL?u@QlnlH0l9{%Vr`y@MRQt-~O{vS7=M=Phd#VJSPx^ky60ciS(lH=(0h}TX~RgR2GAJM$q3XK-}wynrE{Fo`= zr@K4+bpL?oWx=0rFZkz|7feCpXg+T%0Kre^JH$BY(7+y#33~rl1L39+akE9k z6qIc5BkYEl7S^b?W1~*} z0qS$tV1CJF@FzkH>XGsCz-7!a8N{Q-81*Tg%jO&S#)-O?zX}7!w5TBaxi$M(!6zab zhF;31o*9#b9|79F(ew}13$QwZxg@k=lLDzEbJcConi;jiq|s!NQ-W=)?X#Vxs2lVc znuNL)YwrKG*Z1MU9?(%AEHN4^Dum{lYg>}he`}w^X_}f`e0-26B7_vMETv6kUHT4d zoN{GnRlEzh1Atm;hbQeqsjb+phdaQ@xxU$}(jrxw1m_5h6aP9Uo34U&ExJ87mBJz! z%bIaICH(N|4pWMd#7HS(Hp$txRfug5;uJBZh*C7Zp%iN1p)D(gI|kD2ZQI=MXnjzJmoz{whI6ILw_q3rPihdcsNZs&q=R?TydV2+M6t!F~lw>*2WXqGGo~?=9KW^{)7nN zm**?4o03k0J!5|QbdS%^S1hIC@iJoy5lhK9O$ooV;fVjNDp?OL;;3+KwhXpLzEmr4 zSe;%~1WVCEXDf^_yYEutx`Ji!Jv&-mDKt%ta^(Kl|-my{xI>!xuYb{}10 z#Ij|Ot0x9|J&Wx9SuJW~P;L=$v4(oIS2!?`$j&%hA_N=yIQ-GKt)()qTfu6>DwQls zld_K@y}dfG`->xWVCiBRsyEL$Kg3&(uoyDn57;xg_ZygM9e9D}#0D{YpiV_o!xV(Se6WyqHu!S}J9=%}s3Awr=@+W1NNY zw5@pBHaz9cytcr7nsA?VbAFByYpwXau6WMbr0a~Fs|}*Ow(xitZgoVq0pAn}kMl|4 zbj>=VO;L65Au}G+WW%{ZK1=<1I;DighEcIK#QM5EOecId&$ypY_;9}9Z$EvH|By5O z`0Xn`->%r0@o7Hcm*s{(-CpsSX8drzV5tQGD?ZGpwuNSkR0zGl70|ZTitD!FX<2Z0n(+PoJ$`(6z(0Tef}gK1_;p>eaP1Jf z!B4w#!SlLej!_$ZVf_4j#h;fOK1>sCHuPl!{B2xO0sOeG_`8oE_JBm9Lz2BJB18%T z0jJO43s8|@IJk#FB@bKfkOM)&-I z8a)mV@Rl9%n@uu$gu-sCe#Bbt8flMf@MwegR=@AAo8Jhpj}jz0ki}&%Ig`5eMQ#m0!lAE|jEt;ux!Ox#%v0#skpTj@O7j-S6xy{L zV(0~uQGv6?A&{L6V}mUmv;iAD=2N0!u!BbCjIqnBwa^*;igqqUvOgP|9bi61eeX!x z5C8<^T(K;Kh< zu(TpBhAtTu7A(tV4KuZgKC_WLt8SZ})LSZ}Y_&nCLNKL@^oXN2V6gjWp3^?h)goH_ zI!h_E#*FRvxS;v_RsL&PuCiy)`Ddq_po%#~%u~f|+prV^{_wj;JiV^CtqS5;mV$r% zwYF2Pn?8BZO3rTpqoAHOA9`W^g!PBeC6&pCM z>cg~2n0+8(LGkAlbTt8B$wdp;!ua}hL$=J}6tw{Vt+Pgz3A~R?YlBkRA2+_q4}M+k zNd=)r_ITVG+9p(JZ_n666P2XBZUt-Bt)$0$HLCWY1~~c**&$So-W-M@ugcvWp4Yn^ zo#IzvoaPzxr1#O&b-~w{8*a52D24F-{TYAw@uT^*mDGHi6P{idJl>x)TXW0UEW`IS z1ts~$*yQNXw*~+9`5E7CYXjGLcE*4Hx6iG}h(!3q_hZ+$fDb=8qBM^gt$`#+X70lckY8Q6PXsK!ThAyyYQ^&xX zOs6yxsRiQ_6FyFp4q_Z)7UP?g#TJnk21Og~t4+;rMU&F{vnNpZ>dzK`l&dwKHZ*Bx zw?+O4q`}QPBv5~CjuFr6hUc=OSe9%F+%vubYyxcShOg_^pj!g>Pm9GG3T}DR*JbrX zCC1C>V;-gDL8aS&|KU+1VX6pEJCj?Z|fs!`AD!tdO^p5n=KyPdiIo~?bU~8NLK#0msF%=<;;7eICQ^Z|V z-~GmdlqTwFj&=~#+*78ShpXu7ndQ(A%FsMfpF(y{e1}SrgDwb=YTMOa`1zQ_C%XxJ z%kp&N+JMLN?ZPf$l(Ab+{x-DNv;D?+A~eEjA9RjE>hjK(bzBkX&*{@lks<#MbYubT zK{?08FUii8{f3%}w^_T#ud|01({2MTV|2{%^&B=NztvnHBSYG@e5BLK9yIhDi0g)S zC+V;zwu??g-ew%L!m=V8uu3{9a(r;MVmpq}Bq~crAVh^lBP1`j`{$WP0FE6%A-o*~ zh<*LP`td>K+7_uIc361)5e)^H%iTiU7bWmbvo(bwDh)bD!n&4z3&_#_P1rVVdP30G z7oryDB{OcZ%zbkeq?UHKH~o!f&!3vopowRhs>}L zu#5iCqN2ZE5p6KW3|a@JuuZ~iWs~O+N>&(9GO}`v!Q`Etcuf{O+q_Vvbj1csr<^uP zzGM7508Ivy-n!LxRyxk#3rAL^dJB{fM7Y09m{MvD_T6Q|`J7Oz;OTY2^|oQn>UF=Z zD$%_^PoO~fyk>1gs($Zjj(WY@pg|hg%C`go&%ZpkP43AE;gx}9%g9-ym;AGFp>eJi zxz^T9gb;9=fQQSB%Q@jJ$v$sx*TjyZH{1*{#n!Owcn$U^+&6rL_Yv)*b$0PrLnM+6 zn`5^}5cSWONOP14`tyrjLu)BGPs#kY)Kn-q_)=p``}}H)I|>Fn%(b6Iq2*57c|sA- z*99N$&v?D9_~qLx)>0f@qmpK3T$c^!Ie`LfyDr*TSHZu1ea6#uH6WSpcp!xD9!~iC zzy1-QzrEs*U!HK=O0(olCM&Pz^(E4c(C6ENFaNqAP{3b(yvN`C@B!C#Ly8e6i@>T} zk&;G&Op}tRL5wIpf*ebUT@?M3c-|lWo-7`tYIdU&lQW(g-0hq&tlc@<{+nJMXikSsZ+lsPs z`}MD;lJRD{Sk4p;(m-)a^k_K|K{8NYF!n)#ZrXW=`;(Si?^+t)&hjvr@(f_ly7iio zO=3F7gpcPF=A?rR|D5VU-rBt4L_01;=T;KLh_YluxG+S-c+*`fu(e?nkW7y34f|FgcKr}RhFKav1a}Em$G3gMWfhE%`u0FIYe6kj3jc#*LA^Cic;6@Hy_f3 zNP53+%y?cmm43S=O9py5v`(5SHny$UGr;44N@Zj-%NDB@s@)seo)OjBkpg7|B~ucJ z$;2y{h*bv3(!u!%_gxT(PYy{^~U88B$d> zv*)vDLleZ}qD1sFBp+m7+5ybj=bhl z4H|$$@8l8fqC8z<*pdc%jx7WSb2;|g&SBFwlG6m}ag1^xP*W$r;sDSZRXj&cZ=*~e zo32Bk(r#ZmPO5iaRT(0W-j^zm5{^Cs=ujwmXW@!NKCsZ+pYDxLkdB+z15``o9YUzQ z+2+3&btDI$UcVvtU$YhgO}#U3O%ipAc9ZB$5oz+MBmGj-9Q)Lsj<10b((rRrhe&y$ z+?8iTlV;VldX3B8YZ&}{>G#XIHc7iQ>Y~emLz4v)X@0TCkB|XNLNm4MgQylU%zlTI zxb!9jYDL$U3uKe-BL?2&6%v5E`xBNWBj<{`7Nasd9Z_Li4nxvyHskIxVU6mEjHhIT zz5cZ$Mcs@?^)0t;90pL9-Djs$8nSw&(a{|Ov*p|hX#cx97}xu5+p0CX?tip=TPtj0 z3dp(Y#*G#5LizfFeA1CZD|)9% zeY_rj0}yV@hL4YDoGue;t$4glD9rfz*Q;$;1?yH&iqf9%E(za#cTq`HsW?r7l%k%? zK$Ar?3+~P{Zrh5O5|UXxrl`KmzkCsFdBZ6gmDC!U6ocj;TJGrmdB*o2FDB8|=ECK& z*M)JOrvuQiHwn_35gg{Wedtt6?dQB_z)Bw&&QokL9^A6FyDZt|*h1Wj0}6y-#cs3# z2#db|rBuByt4efL#%=!GHu&H+1Pv4*?`W9Vu4(mP_iLm{xa~Rk{drcHS$@TuOOt&1 z|DKi&DMfsG`~k1G6-zFdQo!A5#+R2HzP#LQ`zRggCXAQchRrN@DANeL`K zw)fm4%mhlbcEwg@!ME21_vaZ?iYAc<)>5>Y%N4KdhFTeyIX0iRNYAzOO-v(-+W^l! zkf$j&8w(qJX-*0}ArZu3_&D}^PH1i;U!FA&p>u%*FHNu#!jwfDQ$2TLjak?c(g%z@y8 zvo1~UJcWQ5XH~s$ML@um60YltueSv+){ylK?{2H|GcQZEm>tVAmCi#6LrkG>LA|hU z&@>tDS-eS9Yzjc94g2c~c5Y8GnsCO!Wezk`I9s-Y_neY7sNE*#vg{D_Jo6?l(ttM| z zYLK8}5ikoFt8gFmqr=jwD8z3m+z7lh#7Al)7#ntopdM;5tY!>^b4bXdV7eO$lMRN~8-Bgr@GY;{ z?6YCW;hAGUuc?AuLEu+}%V|9$H`r&+mZdv|fX6iBV>3I zh0r(7A?)Ko#DQwrD5inlzzW(nk5!iK4Ygz2+dVJ>R!0&o0ng71LJXK^l_KU+Rpv4! z+~4cg*QLrOSFCGAh#94*e0&O_`A)0J$sI5f2*^ctiUw&ERxNm30^YZ$*tHT3yhpTa zNJkI#C~t#p`Oq31Eez%&!#MjYWm9^y0;?KOb^|fcw*o zrpWNB$_`fiwNcvny>mi&k$k!`RN5u%Zl&s zFL*f5-M#_+jHcwEs?amg$GX6-yEv$ZnmSC>|6D5LC9f^|?d7&1GvhL=*VDcGkm0AlAD1cd0Hjo_SSd|Z4Q#{d8z07*naRJmR8^8A8pDSFnl z2(f^tZG&AxZ_d{W!L61ZJGqr((&g?uAQ_I&eR+3??tsb3j8|tzMZU&n3YbFt4hBmaP7;v_ztP>Gd05|4#zs`YBMDR!v z=jb3IZD?jw1x)OnoO8 zvDC7kth8lBz%eAW4b&=wL)fW=`m@HAtz=oa6x9zej9blEx!cQLOTk}@AobZS<`{Qr zHUu38vYic{lN?8Dp({D8@!YuA1Do*P(rC3-2m*o9kTot#W#i0@yOeMW0YV8&gODEU z_kFED)<8aV@?{Sb@F`9B{pEt!yg{JsfvpzIq{^f$gly+{gbo=h^=L~Ukpy2n*>&*K z<$^gxthF>4t*RkHvqXqB@ZMwxSBpSeItdYQiUE;=#zK*__%3FcZ+#rbDIn}|+^C!N zB^rC+xn?|I!cBBAo!SG4sX+*YWf$M!gIU9V;TZ=7x(+qedw?(VL!@|Np)YeoGQ9yWC?Mw!RL$YEZN$M`QJ! zM=iKZQ3-El)(x=qA&6g746&#eG>}UOhX6;>4Q8jdc4b3|>Qh?R+9XoGT@pA&H(=W; zVuX3!*rw#R4Kk0_=$Re2>rz>Z88f>vbV4n|BJy-e5FwOO6+|^`z(TZ)(OvY)ep^); z3Z_Z{IkQR{?a$?6@9XfeZ}e&djtKYnu6xq^CBh>eJD@e_T8IRkFLCGHYLxk*;T);V zBTOOKH8Uj*ORp9}IZY8M>WZcswK@>t%eMvBTgJMneD`urczl>Uq-eA~_&LDXsSI{x z=uwv2WPCl(QbaJ%aYv#j0)j{DFi?t4%iVL%A~>Ie`mP9wOR!BpVPnA`e|}Og^AJli zl89ekR{SNt;BWut2mIZ?`vL#Yzx--2DSf;o^F%Vl;*c`L1|FxA78oHAKExBwbHv%` z(RU{$^%l|VrLy|K=c!L_?(a^PH(QWv#hME)^VG@6(uW}4q;Ty6x$!zZ()k;Cd{6-h zG}BrY2!lcZ{`>OgER=Cj7yRq$uU^;Oe2*%zls$)ljsqIspyG(!Z*~yj4rFw4f3S@q zi17LOhSz27Uixmz(nN)za{a$Ny<&pB9mUu% zR~|oGJC9>*lG`!+T88Kzg>KRT9YKS)l+40la_^u~U_XDm;`8f*%8aMmiXR{D?0nI! ze}5i?#t%V*&%hcG{du33wHM98?Y-M-!BQ&jrwR9%07}>_V(Jb_^Ir`p=GobiVpAJz zMWH##?lp42PB6KpO&RBunv%k;ETJt3D*o%|FIc$_O_^SM748wH9fcjkX1No=se5uA z*oe^!+X0csBGb7HKFD-(ZQqr+mtL^dj5~?O9-u~rUmJ)}l?z3fR1J}3f7NwkF-(f9$`C8a^L&zsECKvcmLM$VilBFH9pPDc4o&;iuO3arv>ZH==k z4xBO{>J);KfK5@u)|i4tK}a@inQU7>*;XzSA%Sov!VCd1>(YVTm#Oh}y4eGR`U*2`-uQxpBY};D(hX>mtwr2s!P#V*qFX}O0 z?4NH#_fX5p^^D$yOOs-+ToIr@#~dP_CAYta0L$HtU1Dj^=GU^B1iypEDClRM>}!Ll zHM1+hSTmtz;qqW^l+ZO(r=Vy1IRvaWWJxwKcDi^H1-@y^)Pa+Jj)p)Df)*QfTL1oR z*WF#ztlr7a#AqAmjA{!9w5_g6_T8s$iv%L1(8=qrnEZ9QDYcn3f{MkXhq?_&%+s9B zp3ss^>_TPpLcdrdg+stZ1m_U3Kp~7|Rsx3s`%q`8tkF>Ro=zdObcGvtNN5uGr;zlR z0Pj_dD=mXOsCTaN)_pnxRfoLUT~2MIejZas2=6A&4$Dd3nDS8}g?*Se2AVY?^xU}j z3a5sg#*x^95tT^IyD7Wn?@lT#vV$V_35@;>2z#Qa9H3aTN2$J%NsU=}bnr9FVbe%s z-ts|$j&Wl?4gq%N?w2v0y$Ym4sG{TckeeCem0YCnbw8a?vOt5dwwUfd<@fZY~P+2q72*DyrAfAJ|p5 z#JMnj{ma#4c|#te%fo7IWatqHq=lLOsi)_)0Y9Pv7@UAjkq+cb3^}$2@tPx+HRHBu z9_**@?(oBp4xNnc^C{r*;f%{=!pm#M*KaqhTkTs+`+v(? zhtDYV4Zgn?BE)IfHrjfRJziq~tVR1wVv{9t^YtLVDKj`X^UQK{&`iZk`dhEDZ< zr;x5JJHl-T+zxd4rab9BXSYNw7PZuV?tv6Nf^~`k`&?dr+j8?*yJvP!o*p4ZwA+RF zW$Pho4lD5&;IS?#n)+q1WaL`$$Ink~E3Az%4@#5nAM*yb3Io%Gc~Ur+vjJ)m1iS7r zkiES~jYIxzk20cWU250VPDM1v8*&>erVve*zv0i{baNhwu;hw0SIm=URYDwSWINe5 zXzgTh3wN#vAQeU6TI)gT?0B~EkW>3&7U( zk-SzTv3d-XFkaSz=Vimypg_(x(!_$$ynQhcrf3b!z`AJpK8<1v1Os<@(&kt? zrGx;%OHo?%OJ#hDR?oOB*BW0I#stFIELxi|CL+v%kSjFyr|Ns@8$HiIUjf{tAZM+I zPSEqO({lA(cK_=?zrG+lmj``7$*ZL>c_XgYIBjMNG0siJ{X1=z8@xQe$nbmZrRjL1|6dP zyuiusom(lWfp^>EAU#g03GM9~$jpf5SG9G9%5#Nr9w2UK^XI#^*;+-k)v!C-9C;>}T_jGk_NW}e0N@%M$73gQ@O}|>2~@*T(^~}pPI}$GotkD0&H z`C4)$D*;135PDp|0es%xH#!y#dt&#fnAkb3We?&wehs3->|;N_coT;*%C4tr*m_GR z5_S)`XF^)mEpM5|~XpOK+ zu=OyU&j}^ho}2l+Ax3Q!=P4fIbNV$XGFg+Uep>eu13*kFc`mu`uSb$yvNDV8Bq+yR z%UT7GAI_Lkz<>Wg{Cj--c#rcrSk#o@*Uwk{(|`FB{@;K8jJrF9Eu|Dus(PDKApO~7 zkoAVX!57gt^#n*HS?tU|lLcU$3aP>ByO}8HlT} zW`_z0aUp#9wzh4f>@^OEiSYPv!Vlly>tI5-6**4OCeba)7=Br*jyWkdC6zwRrhqML zgPoE#ekE5V^M49q7vGbs`J{xvHShhSviHp0mfC)Io)UiiG&QNa063p#%;s@iHwB($ z^RX^#!Jq%~f{!0AxGkIgo_ZENCdGZx(HkyS|7FevU$%-s`noTti1TT}lGWQerKm+o zwTGyCWr+}jzJGI!+F%~efbld6PT<9Hh}Q=~qoPL+p6hX7=fI5S)up}(ZjpO~;>P2@ zxH6W^ZE)1ze=!MaZ(uqhoZ7>D%orV;d;i=5SuRirsI@k)f3W|S0RZM3(e?nghA7&W z@49XK`73w3LHBU>ExXH9eMl1t1K1Dk3@j$go>MYLfnrmF(_wp0)VK&NjLVev}@vG&HA|DdB9{+$Y;+ zpVQQB24%XJv&%dY-$$*z0Q{aVW=({)uyw2iNmbjA%VvtX6l zH|-DvSprJcOt) zJSHrnu`ZJli&+F~F|~kQ3)FzM)Dk;qB9%W2<24IrB3wA)E+tGcAp^K_#WimV4D-!u z!`$s_Hy$buw!ubg^6gN z!017w-GWRGsxE5GBKPhFcW7{~z^xW+mGKOvSv&0MYTOT36QSsP`z32Oa~1vBlm@T& z|0P6hW>-o<7Z%k%O&>j+EF1hTP57Jn9-r46emtGA7$rOqVYYi*m5acq(+MwIMjf76 z?$_-BX*#s322FGINU*e(nbczUAUZJFTC|t6ZS1vHBzq4xt~kXBcX7hEyke=fr9HSD zx((PY2Y9iNVis8DvVzF8ndjY>-DBB;+b;keDhVSUNqe$a>o$_AzwnrbA8J>mQy^8j-!S_6wP}3s4`U2|N9s+- z?eLLIEijmo0$GAZG1O99A-Z030}sd`4-jVL3{=a7Yu8x}!-3JaK`yWMCS{B<3872r zrYZKnbJzq41eeRyr5^)b76PnWwu73D#%v8jBm+$W^I4}?UdZmNx&At&O*r-Ls~M=J zG}Frdz3oN3OMJoz(;gl?y6L^)KmMn`!{g%x=d(gXwp_8T8J|Ag;eYy%|9~__{L_E= z1s}h==vKV20b*3=!gkhX^6v`Z_EI;yzGoHbghV>zeWMno&x{b=ge9 zOTCy0Xmalu1I}|qj$H<=8OZw!ZJHwPVGN64nsiGWW5j%#)c3v> zKm_-9XZ-s01uxeXrzzre*2svMy1$Yv>y-Vqw7g~gp3kofPIEL0u?=jj<9d9&U@IQ; z-~*3-Knf9-wX|b-7b9faQ-iztG(NxqJZ7G-T=8IW6AWoq1efaS zu&MU#wnHOjU&v95v(uOj3=uIQs7LplQro1S=Lwhd+zQ@OrBUbo40H&llIN?z&rD() z144+17WuNUW}Q~29Z~mzF{A+i6^^AIXWweKNHQL{73PC=zc0%kW_o<=RADR|w{7xi zntG&?{l(9(H++A0!QDLLwrvflc1jVadBVEoomFO7e(LA%+>f9QOjkQd%REo!%Z(x6 zG$(A<>@^>Q3DzMTA;?8qZfP5j6YuCElfCbgj8=zzaLQdsZ7cJq=V#2X zDkJ@Hnwx5b0$LNv17Bp!VQ;l&QwCT@@*#e>k*J4e__#lCpjVuB86bm{oBEb9^v|#T z9Pllb477Y{9%g4$pw{L`1d-+9D&#Cuz?q~`ZNG@pcTWI5#E9=wf=D%rFr(O6m#pzg zqyxSnU0ovPZ7sI#y(4N~?<$N7CkPQm-59#o_M-Wt46 znq;1vcUBZ!1mGluIcP0)pQaX#b1~^_=4uU12Pqy4&2gA~EWc$q(_PMw1KFpmGWGKy z5#t_pi$IV7Cc)FX;yWUoVuwJ67FRaJLHct;0~#BYs<5ghO116zkQ7lHFwgR+iBLRu z8wxxttV)2`^1ZbwpM%Cf29QQ7NpA9Zl};ynX1QFv1L39+@M%8bhsy9J0z(Ey}cKSho}wsEwd7qYQn?wG^z?w#Fd> z);v2Hk4S5pA}XzSNjGf6>znjwP_EEu*8&zswatOUgh^>%vFiar?ckyvf=o} z9t07=rdHG-8$OtEiLq}PMRc1Tk~SbQCJe6m4i}>CN9mkxqu!x6UNU%8@>cM>A0P1F z{lo8ZyHzYV#`U&fo)d@yp1!TPyF1~p|N49U*Z=!hJU*P8>SI17EK45zx%yD6%viUK zQi?Wc)uyP4%v-F!yp*(IWf4qCCxKsI7Cb%Qn&dA8;QR0HaXwGTxi;C(vKFjMZt`(2 zteNzhsqWpi>pG=?`+EgQQBcoxDU6t?MTOwtOCwUSTC)fYF*;%qOVK9yKA@RW?D+nm zxr@yYDtn)+%G_lD`^g((e9H|7AuzX_l9@rQ_m;*ui$!-ZN)_C08$NuvG+(ehoRF(M zSh*UUNjE}`k{UeDg&R`2Fk_w~Sb(jlL@bwz>zex(c!UDVC^GDk68p_uKL~VSK!;{^ zU^2vH$IhT@kp3LFT&8A+XfUQeAK8)rItQR1R)yJ`x?tpZ8vM!wbPhH}8n)9y$_T*V zQAP7e%5I3_5j81BEZe3Hps6T))6gYtp=FLTOCydtLEa-cs>w}h|JHZKP9Q|+XQcAP zUi^&>T@jBIi}>_-hpjSRt_xy{t=Wc^Zo+zq`Goy6O?sXUq)BFl>6KE|S|A+;=9_97 zJ_B$3u4~4_WdcN16q-RzoiJ}3UmC!bGEV*kZ@`R?mot_%W1b?iZFI*@G8#Z`Ev4CA ze*Ex&H5a@r8){Ym7`#cbp;d|jr%CNIRlLcuMwQT1EFNvAndU0-+j1Q|mfT_?*`$_( zvjU@@XMDsvc^}sNYHo7a`J5n((`7vK9d zxxbQTJIT3&$@V;V5KJkAgB_Up1BHXyz0H8nH$7~F@ zWQ;&P3^ev?@mpQCkG9R`E(>gD`Uu$aEzwbq-z-~B5*FXe5+K16kI_?422X9RO5k<| z!F^0hR2P-73Iq$3HvN?=ehLX^ld%T(T?(*@HUNnR&?ZLOuWo`*qz%$)4M{<5e%jic z!M)iRW=jyEohq^|oW=!#IC+#6o8Yl-Z_H{rz=+%$H!vS)toMiQrJq47*Oo_ot)>0UR9)s=Aw&&gr_n|V zydoQTxG&o8hf|1{O!A*7C=Hqj!M4?|T8Pp&@THvxIz%lZ5F^05r7H@C$SY+INKgfa zW^G&kaxJJ_@pZZ3T1rzAs_a~M#w%BF28z^n==zxz1jwN;*z6Y8R9aj7WEQMY+BxiV zbSpt@XRW>tUo0h3%La=MOl0`P1L-vqOsVo(DxPb`s}E)!QfNcN2$fW4QRu5jNIC62 znW`d~&54PTYGJT7zm57#!!gL%1=8!87!VwxIY_;T-fkz4^kf;#_!y!s-eRxpCSdG0 z!h?I({mG+;xHpf7Z6xik`Th2phMS9yzF^tQGTMZU1`pHwL}YnS5`4@K5E+6OX0`BI} zd{8L`eE0nwVxrzO8X?jnb*f8es~{ktR`h(n(SJC+ho>p_i93sC?K!ZVHB0qK`*uGN zLxE7IQ$k7+%WbnfPAz&)=QM17wE$9CrZf}&WZRceJ9&=`WeI&*_DxqMCs1f9An z!}Obag^8T=8}-3>iC$k<{LlZ(KY$rnZYxfw2{EYOQvlrE>0|SU??0dx#y|aEzaS9d za+xrn67pJGW5Et6b3!!;rDT0i#9u!e1(#WigIX%Se!XGcbmMS2PbR~yIGrZkKb%l2 zBbSPKPKbf<{IaTt*ALfX#iJEsQ_`mQ<9GK+6WQO>!aC=I5F##@NxiE+Xrkequ!;QZ zt+b&N4jy;UJ8p9Prk0RKG^};s(2Y}Pkv)C!JxKaUQ9jxWny*bTC~EYs-Zq{1h7gds zG{Uz6inu|EO0D~B+$!zo2|*{@=hFlVz}?-+;&2okGfk5LM>-f~%+eOHZn$X&&TWGU zwnqLqz#Pw_xBP@LkNt4eqi-YXdyqXJDh|&mY5p~UEVGvlw$VOMk4@vF#2HP(jZyV8 zf(p4S5sHC3Zp+ph5~83WIV%)Hh;6vXCL<=>`g#MvEtZYCR*!mmfM36jtUdx%#t3`+ z{u(u>5x2vV&7bkk1DpCO`&?+{F}x8 z-Tl{-U7w8_KLW`htw?_h2^r3}b!#zDB7(IP16`TT!Q_~K{Q8WaKHTGF+3?Mz(B~=Q z>9!g*J7J1ZuYL12vR#0s<;V7t%;#Ey&K>x8o-xOS*KNa3kN4`IhjhrFJu5>{*?X`p zG>z&6jQ4(Toni<$CnZ6@ZW+ZI)=`yI$H5K=pxUkt7Qwf5#n*Ld1wI?)I|VHUVU2XP z!CPw#TpF@!fY)XvjCcmh3JI@n-pV?kn9lLUzgxj~RzM&1I zkSezOdZK`f75J+NUPbUPHRDW#I}q*z;bg!v5>dZtYtFTC$`Fk}LeEPM0M10nww)Fc zOaLw*EJHmhyCuhAT zRi9OCwElWfP|~s19L~BODQ8qt9&K#L#nq7g9mWUEsK>pUeMeIz2&WJfj=bGwQKN zZF;}kSRVSlp{b=ob0jS9@RFqY58p=kc+*TBZgm{`(BSzUikKZyc_5VTvEf~UEbkJX zs6B9YQ!3d@jE@rS9x2d0sV-&Xy3f~pn=#M}f_UqgST8S&4n<@SQS+Z~KV&-)^sK=N zyZe!MQfM+tuj4VvsEXcbO;b1&YvY7XoAh=+lDq}2vov{Z`@XT$RmtMD0;!iZkV-7S zetX5k{TbJrdZDM&tQ)Bq!CY-C9wFX{2%}nL&@|aZ+F})|Fo+2;Mci&1)?D>{NfEW^ z{lX&wsztF{G>06L=LU$Mv=3qknH$XZt-zLpyTi$^DGn}~ku*L0r6C@L42{%}=&;)z zxB`Hj3Q4=Er+wCa;K?#tcKSn*+NQeyd#M#mWo$Y3=b?M*B(!|+>QEv-k4gRhPTFku zvB{Lf5I={(8nTbI(ew0VhQ+O9yDxkzt7q8jZN>c^;c`0Rby*w7f+#e#4H+CKgorig zzNz+W1q4vkD51LSD6CEd=GdDv3ITU>!m<@5><$pDU%tHzLnpzZqTSukA_rTQHz-8N zRky!eDR`JCJTGg@ngj^9QgK@getf(K0DOME;?K`lEJhX$v}(kO)eGKabq}HJuTh}K zZCT-ZN(tZHo$<7;_;`2LBDM7R85y>iA*dH^wHj(vSoP0Tw~9psL~PqqYM+P8b^Bnh zD-`_4O(CMxVguCB0930D$rMf{Ds477CYc(*8=3uchTaE%49$td*Mbq7g8>!2s1C+r zxWr^bVI6KVy6pwtn0l=?M58s8m%k4?fu3#iJ+;8!6Vc39_pV}~3b%T|^F!KEr&~(( z_z$WT^lkE#q89L?a?xr-1>K148+Nj$M?yrj!Pi|PTuar683HQlmMVxKl14FB5nKU$ zt%6e(%oH$_AfeZGV#U9=GfrfFWo>qY`7~Js(HgW9X*McbM2xX<_ZGzg(XdNw8QCzy z2~h2#TB_iOaKbqRyjBG+eVix6pj*$o6!DS^7K>Rs*`T3vS9(;DL$f{B*~7M1-lm*1 z&D8)MCG(2W0VvpqPh!KnzP@2$yp)1JzrKPAm}0_Ovo(xyaM?;%yi%w2uYUcAdiOWK zj_q$0QTZ?1^&cEKYCm(ae~)c*8k)a8Su~JIL1t0e@k5H3qFn=A zQAIb!TlM%ieJQu9wy8*cXvV`pu!4I^0ryBSkEm=>mA-AYzdJ@!dfvJ)a5f+Ltup4I zuWuV9&}<5}Y(vN_z4?uSkc{*lZD14Z8^mFJ-6hxt-wOd}3P@bIdA&^3fH;MI;1~hi zr)tThtQa!(+RAk(`f-@njQ`#*oOd<>vU{}l2gsX7c-OnWDKL*<=iVp`Ki9jVw*4me z(Dt_*hcR|&m#Fu4Sv@q@G!#|4-2#pO-k-KYdq7u{Pf;&C$f#GlNG6eYx6Pv%yvJf3 z#sfn8k=OzEwXKBMXy^C{jsp5VUJJM6VQQ^a{y$72X zwufQp@EDOUzumGK_qlJ`#AWp$qbL_`xGUpwnQ%HMJU=Ze<1_ZVeoc#2Dgui7J*GKf z%$FuJJJBF@h(e;17W8%-!;;*XVUDw^h1&M`m*5v{6GGP|Mh?S4)+fy5D}JT zZA9Pu`wP}3<3Io7A9Xr8>BP?Cl73m9Ri0yn?e1~b_b+>nG4=P>|GhF}P6?;;j1&mB zCF6SCFwGH{^8|t5+tUr}mXR|zFczB>ST{`p%vJr+_P=TR|M+^h9!ZkqO7n=Qnwh&t zL}pf1S9h}q!^J$z|Nk+wz%0N8F$-S0*~O~Nj0nG&nX1S>i0I8diVYBKGBYCG?V_q8 za?W=m4u^>6mjl){)tp^&e=>0c>X3V9$R)BoTz`{@EA<``cq|b&LU=jEdE5oD$6k3h zax3)uXTNXKm8y>0VnMAwBLG6ux(gO!UDp7c#2Da;86fu;zMog*lsm{;foECgbW1h? zbRtfb8K$JTY#a7HVOb`C6sN<2I7RT9uqVqGwhYd;pxSbvEtkY6EIpLoVXK{2w*d=D z@0&D=;1gCIY$I*m*|Q8&ak_WK&{zzMKEnO<-tN*Z5(R#RE`n{8s~5GCSq(<<4%KLl z+nymgJ1|IVc6rkzHfehw!xOs9XzgNn2L3^b<>P94@ax&$H6~!0B7S^6K>_^scENSq z>@dX<^Ar&yW8D*O>+Xe*5y7>y*=Sc#5HsdwHrt3t2$ja2O|SEy0O~bDrM?%n@4QUW zCHA8q?ddSr2}ZxCR@=o#{0aS{aqDbo{o6C-tfNeSoefK zJ|FSzx?&Tj+V+{J_tG$`w{aK*l*9SNjDt(YErRKW<2+R;l8Bn6s!*}DG(f5FwX3wf z{IeF>AvO9(+454O%gS#lcT5tS#sqJ~Mz&{j&&g@LgfE8$Th7?m)%Q7%x9Oz77L^u2 z2-tJ>=b;tfL_FBDM%ZcBBcoPyk{{yh`L9?={J0;WRZ8Bb;HCZ3yR)cni}1L@qGoA5 zev>=~W&@&mLC>9}yeNyT5uk&#nw&7tOqe;;=To}OdeSDFUZ#NSX46db7`DhL>WIz7rsidfWflS*$L%!q6$JLds`S~!b+~Z zC~uklzD+ZBWds3kNFLGF2S04T3QM&@Cht((zg_!n5lcLJL z9(#>_;oF(use@saiGtn_0BE2V>p+XTJ17lAL5$H8^g)u3O9X44pmxUnGWDQws#{;Y z+KCv={)4Z!MB{*At;Z#%rY0p+9x#B@Yaem+M_17!rWy6;rPS%^7&9F@*&vtGiR-yTG3p+kSL_nz?kP?(@wF-oScCe89)AX z!fo3TIN(n|K4F>|@8=D-+m1a6&esk5mOU>Qm6(d+M{>z}2mw=MoK6c)rwQwB;4zDh z3H30MvOtrxBBlfFfPug@&kCYq;I0y1jut*b+x0yqT35Is)H3;oQU`bX<^QuVbB<|$uh zlk$|zZdE4^i@k&3$EZH0q1Q4rVyQxAvLE2QIpvdO0$TK$f3BZDJ>l#71^@h?U$JMw zkEbK17y+yU1-E$4dXQ3`w2_b62Bv`RGfOPd9Cn?L(}c#_?7Z})X)TI4v4g&}G6;%9 zxlESX;Y2lFBoJ&_Bs@(s&S`6qG;ba zDi03lD#KK+YAK&kNp1xUTBEDH>!E8-8@Q?E(eBx3wx!|0?xoB==|M+0E*Znr6|bd{ zO|@~*(SUs)UqW!plGHOCyDCLHxoL?(gcmSVFouUpD-1^UlK~G!#q3zBo+C>YB3GIk z1$3uka{97TN&R!Dw>uSkO-;HUx~(D-;Yh&j4TGX>NeX1QTwFBsRTOr=pF#w)NwEuCU}7@a zZlU27CIFGyeGe0ubS{XPoyP ze|>+)E*Y=;<^^+;5GTnHW!$pXh$^BkNy8(5v@|wKEM$?%mnMo|!(BXfvln3zgEEjI zI0kkadIkwqUPu|7l_qOxUuG4Y(~eu$xj2RrKW0*AD?W`N9=$ zNJJ#yCQ_e=?C)Mw`iD zjMm~-@u3;GKlc=AZ7Iycc&K8gnoHcG0=4tjwhn$Dhol{znCK2Z(<>s|xu<~(Iz|^@Hx{6=X6-dxdO4d(!TbG5pX>boo8h(EVUFd;TZGhaeBS{uZLibd(oxN`IMebTNX`~bw5~Ss z+EWIJd8LoX$#O;4WI2C6MSVIhevbuj=N)gaH(V|oQg%9Vi9#vGWbVoBR^WbmIUqWX z`F!56x^(e$oFGXnR3uIT^I=AqLS0YFzXL+`tXZ^@MFu7JA%%%C#ZWV(Re|@LP0KW7 z5C@9|VnE3yM?8YsD{Z=k5Xjnn3*xpGq#kG_6=!I5D`QTgY3Ob zST&$4>g~WWIk}zCa&O6{eAOCLWU20v6jyDDQZ2B?14Fwnhk3$n+d%=ixm^40wpH4w z`gdj~{O#Kt&f8umBv}Oi@p|?cDVIwtH>(t3%zv)ycJoUf9hmj!PbWMdW}H_8_pN)v z5+l}}8j;_#c_RhSROe11)H5EG%p`&F`83k*dBTb4AMMUA`cTq0+!0K>JW4?NmLiX~3?`EmAt8k&i8mlA*x(bmNS^o>p#Z4|v#4lT~7 zSCHPTP>*rKVVbe!3?{*!o?r0u(j~G!<2v0taSHC8kZqtquN+Wz>5eyl^e*h0WK|0GlwEw=$5)u(zy8 ze%*qS#~n4Q`8g#pl}V|=NO#GV@-3A~VAnSI;3hi^?UFNY{?2-W8U(;w&aO)ExIqPu zwgKUW1SZ8Mimm1%Tfu$cfQ18oIvntATk+i|^FZ+@3vRW+)b4)-@EoGc>un9VsbJF` z@rS=Wf&7q5R3Z`X4tQP^ET@RuB_YnS`nc2&nQ6-o1p-p)WNYZFISjs3CYx|Ngv?cd zRN+AACd4(TP+B*wCXZCv3HpE7U#2lHx0g}toqw`F&`RggDTf)o+l;9jfygi*8JTtc z6td4I>mwlsf9KFr?L8R%0?-x;?z}SE`80~umimK-F93n5#tjsmzg_zd`rxF$G9hLn zOiM(F9uzNHKbsZ33f)Kl?hr%eu_46dzEi?HF>bdVF$7GL6+7m?ZF205wYU_W@Ok+6NrS|&|pN)VW_Ol+jbJ^B4Hbw->c_C4c& z|L?!z-~aX#o)~XRGM=LgwwoWSp?gbak;FJ zthiq6IvoyH1jlGU`1-cmCM4DY-Zv|nRr+YEF@`AnBOhbHAAWqo>A2uwQ(YT;Y``}y z&%66YQ%X<~ynK1aa-6Yk3G2FJT{mp&jxaHnWyUfE%yY!s>lL@_TD^nCXU&CdEP`*} z&Y0%OWb46vV}~W;a=8K^9F7at)nIVTvKUkjLx|jiNTy0KS}^Unw%z{>_tUk}IP)+A zRCF1;El+fqMv)G78@HVe`Mdxg2>f@B0yO8o-lYN<2uD2+nC|4(C9{=FZf0*6@HMGS zA9~gShPWrs{Fcu0;TzICEl>_PZ$kd|lsFq!;JWXFxAuMA zyeUCd6;z%wvl6sS;HD6;eRU3uPs@xyetI?_VvJZ*!W<(Gal*cv%r}eTI0dXkSoe&R zOQh!zplbJ}s(jR9Q2L4K$s2jJV2g#ctl<__`I>4QlNDYpTt%Ck%uN$W+bUq2%gNS&$Sgl2K@Rwzw$ zCIl@pPh2PHH6BTgjBj%H5k%649$glfdMKuPKa57HL_UZl1}y_3H_&dMPdq zX2Scn;q&2W#iUrw%Hq#(Cb%Uf*G%WEikX2x0lQ?(6pRAx^3bVUX`sLY!UW)%7%#*) zc$4Us1%VU`5#~ZCCm;pFbFf0^IV#@7-VG4&ZBG!Nv|nsfX^=#;rb2LTfO%qFVNs_` z?F?$l0zm-dFQ;cK%KWUEp$HU^G}m`l+-i2DfL$|=F)XVMZE}N9@ z0E&H;u6NRD>h^*nA084#J6m1W7HxQ5cO=PJlQjVklQB0UfR|;#m&0O&ViBD84PUQk zyzMLge!F3zrt+K|I9HlmFJY>XvEUYyVi{m2Tv9SnQ^^=+#!UrRZ%+NZ7;7LBaLx$~ zV4;9(&e+Lfgmx8tnPz<7P0cg8_cfChgRx{7`*Z&4fU?w)ilYyeZmA)sf;Y-uLclQu z+dql$6eAW_@qOJ^L}Gl3j8FH{d6&XhK%O_j1d1(LgYR>kadm6K5HTQ7K<0;TehE_U5&tg2D_xt<*gIqc1^W_DQJ5Y8T9tr`B$JooUMx)COhmePXY7M zWEE-Chg`Tv6(BvHnq&kTlmoJeLiaMsAy9C_W*0=+1Ey4a|F4dkC4B^M+;Y5V1sH+gjiJw#oxLjg)sAXXG2d_?z zo_C+Ja`RnN=U3Kj9jOo)^U)@|*$a@{MaJ*{AXVug3LuRAS-4) zx?|6N0OFeI%hZ|$yVaB#++#H4_ z*(Tl3&kK%EvlW?}k%pIf!ZZcEzFqORfB%kcOSt*^QRt^B32xsn4cLM7 z733Rw$W{n{{^12V39gr$Z4fbFyKQ!WIT?T}#)x_|J}!Mn$~}D?2{3@{dz*A8lc2#6 zbJxhcQ?Zm;Y~M*AEHz!>+$5Xy037V?1lq;%^yJmVqUal( zj}GNtRBw~xz8Iz<4?M;RF-EZGz!uH{m@R`cWyHvs=1?bz#m~5}D?YtE+q&rozAS?4 z?S>Ge%kBMe5CSkOXj5!?!=92AC`?$l4JqeJ_Ka>LxUM^nNAy5dsmUR(P7-hA3L2|H{M{^4X@>cvi+{XKl+BhhdXTkO)^$;N zZCD_8@6f2$v2$L7kVx0C?AN2RLl@b0QpMzc*YYp!nk`?{59b9eB%(-;&1Vo2a)tc( zr$Eo<S(@u!5Gi2sqWb%m08&g;{tc5-D-_edViOr+t5RdxfR(oA+M267Tihk!5zpCU6b239;|+>&=cD)i+;%a0aK zRh>#ImDmb;^rWF3khO3C)h(Qj7L7YJOY0^! zc{UALb{(=6d+>lu;J6@aY2aRQ53}M?rqrz1UCwj1LO2AfvQQ8(9Rfc8aKL;Bc>liQ z{dH?of7M|!Q;6!ZyVd{zAOJ~3K~&+Dx?8+zg@R0kI5U<*!1;Z{JO!U_nS`*s1k8+S zDiEob>sKhLX2sMNCsgiJ_55{r-6yW42tr2|+_zNyqm9VjJlb)#%r|DjvP?J}OyXP? zi+M87WlD;)XKOl?sYB!&O zmOV!=a9!A@DY}GH@wvNV_#CC@?-hAMr1*7yCL;Ol%F2EoG$J1{KBY>jh7rPB_fg{K#2qb7yy(!lRb| zzc4%Fo}8kOv3U2t=8inu^FZ%aL^bccZzi^p!B}(C2jIm%_D!_|uxOhqYhyjYbH6Xw zTlLKL9<{QD7W#AjwS$hZ%kQOjxTk*c_wUX`1GI&RahT_NA6l%IDG)?#BD}eD*$UB! z)8T*^BCe}7FqpiVtcuIJTaLFF7zL_EZQplr3^*Jh(2U{BsF`AdQl73C) zqU@>;=^Clhu|c&FTH#YpCX+X+N9|-WuN)O>az|j*3^1F7cnXZuJYkLj-)=W-IlJt) z?X~5cqlzlEM8`|X5d3z%nm0Tz_~CT4ciUBYQ^`>8dP8O_@(CJig=qB+dv>YzF5>>% zrnq3HCU;h|(9keUC^jj7FMLhaQXG53BshH;KwZ~u>Pw@Hy@OD~i|J-Dp&_QNo)5Iq zqwBTolqUTuWRrEl$yER)5)LSNuw`OSorlxpt|%%rFZAyXcC!LUW<0Sq4R()x3Q#Oy z%+&nE1+*k3cU6Il3XZ~fPdoPH{@`uJ_ie{*H!6E&^Z}L~%`J9PKiICA$rv-vAPB_x zayTNpA5&B?`(84|h(Ll+JlG}53T%S~f4`2=R0(By{JL3`+-S?w(RD)|fOQzWjKd`T zpeb8C_vA{%a$YPeX<3V(g2hE{DLrH$2VzsR6*93Ti^6j`b7aEIz;TLL;)FjvzuKO)RHGCV4Q`{uHz1OWjxDT03JqhRT zp!g8a+f0EDQ=&VHbWfh91`aEoP|=})`!Hp*PT|TX#&z-J63OCH&!7L{sd`#3?>nxS zWS}NAYaO(a9<^KWl@-jvH(%>2?_0)izg+O+&nFy@6N)}f2M7$j5GHJEu4FChI{)m& zu2FF92ZYASP=RFBJjrNo=bF32$be_Ei zB4}L|?Vf>0un^VzTADCbo*M%`eL3QAoUmSZT+cV$*4?D8++njb;eF3o*ByCH^_(4; zk=BI&@BjS;SBFQ1V29-EdBu9!TGyU{!^D6x=I9_9w>M0|R0b&t)+7L#^f@|ABPT%y zFiipT9I$R4u|l>n6bb&&sR72QSOroNyuGir5x`8I``kcd+3Dh1B|K%*E(@zkx%$E> z`$6m&8UOtU19ZK=CtR}(()qniMI9{HV>grqw+kW6=9}aYkR&1Hj9h$uY<_cfXjUh< zZHKWg2B*sBf)1ENLfX|;!*i**kx?#Pp zn5P--qh@Oj$0qjP1*L)=O z|L((&j~C*5c0|vKWjzmU2h0#6($yk?mU%&( ztbpB8#=a-KU#?iD3Cm(qyeTkKlC`v#U;;a-^@>#EOR@hjN7_l&#AH* zwmoAl@10o4+qNY<9cKLSbi(oF3IF;19fx_s94GT7C);0+(~K`qQx{WY%A@G9WrfXf z-4ecEuE=WB-}3i&_qGRUZ3xVPU9Q`}DaUEXVY0$Ei{P^DxTS>M-`hW&o^Y6>0i1$I zy@+6*7;l%gvL>W^#+azu$oj}Q>Zqw@mQWkxOqEn$Y1`T|KFg*ky-kXMIY#_+T=0kI zC;WE4;QRH4OWN&x1ih1$b&L=#zC07TxRet`|KrxBoLXMhX|~U97b>)i8L|B-4wZ8j|NQBp^?h2@)0LZS5Lo=g>4^K zQ*weUeD9uWeP^HRm#Zwr1FTqlQm9>aI<+^q4qU9UK*%6$#rNyQa^FT^1wIGH3{y9h zsHRB3;>6){Xp&y;+nqrIOfG79toEF~ZJRZXLTIt2<+C~nNxEiDfrV8Vt2d?@cD_4? zfQgM5zN`3DQfgseB(CLmmou2KHGDR;$gS*WzMl?9eBUS%{`(vwNI+n%u9-y34(wqmT{e3wWq8|u*N4Ps>O8~W~r}n(I?8}%$7L@ z1H8GcdzI`SYasSTNTIM+=>auOiFLxIEl6zfHX(BL&Zv7W>37aK4UagazjXd5C;2kf zLYv5@7s0bJ8==z=hgI|9G8Q%pF6rH%qMG>2L53bk$)zo(+5qYH_k2O-E?25vcnr}e z>=IA?9ckdu9qtyd3U$))4enz=%ICEyy;f2v2S%6(oE14~{r!!SOl|leteDgRc3*~rr6{sXqDoe;`gC(sfe7LJzTwLc2iz{j=SB5d0<0Ld zxEZQG>IM;O%2PRTA&lP+pW8VORhEsAg7*8#Dw1+G!z7Gl7s zFAJu5LWltz-TRXS!epLZy>qE(i}x5BD;-#(NP7eshvylG;|x{BZ@;|Z`hG)NZF)N` z!O6@Hi}Q_lX2NosF$Ko?x?2RvE_gXj_`~Nz1zK4irTSD=s!X{2xro#`m?$7AS4n0l z^x!f*mmb0*xLos47}fhz8dgjrysQM5cq2okD-N8TP*Z}um~GjUeN1g>kXQ%59Csrb z7JnCe2EKkhW6Kt0#Fe;d8#}SZN=SDk>5HtK1qY7UvtpX;In-Uch!xTy-pqB~eY z57H~ww~|x4gBjhWDZ!}RP@4#^H_v>{W6Vt2Gj6K*q=#yM0D$9h!sWVQSpudopuyci4N&;u zf9Q7y#Un?X2C4H%?Z&qacEg&I0Fyv$zd@xOu%hlI&kBP)`F$Sf zya1MILWD&U6&P0*1EW9*Q;hgD9dKG^{Q7>ungr+V;zh4wjuZa-4`1--A3o##dchP3 z>z*28!{cmjn(AX*q_Sl?nKV~uow%ZhU1n~XtM*}2*+UBi(}}=bqIA5%w!GUScWq6x zDKP%+>v!xcg0P)boq?5ZDjin&&KH`EGJbm;_AR7Pl7#SFzljAH-} zz8@6{^+bdN5#~-oRo(;A%8x}-lUbitFsot#kRZ6EjB~Q+n<_;IbxKC9gyzBT+gV1$ zr&3IWKcAj#5a1RoQN0AJnj zsd^8!QhX=@Dt5~vr7i2pAq0G07P~(F%WOT(Zl~ZX|1AuizyXIyh!T)B;}9o2&$AWJ zu&nrNN?4OkO+U>GE@`*5i5SNiz|1(O9q;>&mwCcDC){$z=GSs!t`qY}0f8B}oFTX& zs{;iUxaEwOIAabGKgbB+dSe5~dJ9YR?;?HFe5Fzf7h@6^nb6cc=NIoA#G!@i7L>6zV=eWYh003tsbv zV~qF`>^^OhtdZi=;7d-JKpvl@h}t3wizojn+Wn18mQN-fQkn=4Q_JGVXi}_FvO{UfZ0UGSfR6@;f3%VO(bU#17)r~ay;F05zvPuf%y+}U9V@0G*l)6Gu z^{xx1kCT&P_gPK>&!1BI*=KK@fjxHZL()+ zPNksq?zV`ytdQz2)B)eGRsPz z6hW}>f~OeqGzqq31m(B$Y8$UMuT5Vd>75U;e56+6_LFRNg*{byG~A<{6YBHW=;qo$ zB-&mY?Io(EdE~dP_)dChpIgJN*wwaHjDiY3BvK=pvMbYGQ1I!3^a9lfzfUs3}eRYLk<^B#SyQg@A;&+=~X>3~w>eHjV5^^LQ_UA9sXbcqQ%kg%x|q)P_s93z%_!nWs{qulH| zu8wlqsfZv>Gay#b7Ao>#Ss>Gl>v~0GlhdkNwl4%A6$Ge(ulBtb`13U3^RnQ(nEd~` zCBztTnrFNm7Q9}(k*XzX%vTcW0EjTp*S-VCH3JJ{i6uUW@p@hH%bUfF)Wbb`ax1D2 z(>#_I;}e6$4}Cc;xU4qCT~kI3!E<;G@`R+g?K{3*S8v$cFI?h;!@S@yPY@9P_1CZX zbX*`(a>Zdv4-w2U3=x9V5s?Ekc3&S#(MjF2r`~KPwaGYhfqAxOKeQaIrTUA5vkFT5 z)1C!?e_stS)+bx^lfdpdFMv$Pz#ysO>2SbbV!*q5`|Fe%rP&gyf~ilvDC(wC=+^0A zPqNmQ zAg%YAw|3%lu$@v{Tk2WRywW+_*`!R=?fhKMFQ^jF0+}inDx2&ILdjV6f;y67Qo;to z;zZ~|ey*`?N<(6VdcmC(*cDh+5!8H|35ws=4eu#+0vC&;gi?)LDzfKlGSE>?*tb*y z%=)mVj7z%JeKio{vTq1XxJZKzebfh~PGOT>MS9B_q?Xnp)q+9~X#tA+Bm%_;yUg!5 zSP280Lrd%=8A$3+%L%uX@a=XrVmLD{`|eR)Huao+iVo+$lm=3Hj;yeCrI=bn@H7Q{ zIvm~pKzQG7xNN%@l-595g1t}YeaEgDONjXSIN=y0<`59s{@wR|M`ZIUFV{0xDV)y?>b8Msj4)ce2sCS&W_#{RKdH>DG72?Y?q8fBuuka zzdN$a!N!Od9U>^qR|nRb$EvJQn7MPJ0#h`aX;_K+2kz6nAw$lt7l}R?Y~uQ{5G6<$j1Ik=ay~ibM)kUZ@cBHM?wonj=m}#4TUlJX(s|f2Xm??dx&XN13301nU~_5&1e> zD+w`Mg6ye9-*KVQTP`SZnfzgh*=X@8TBkN6r$R9#RnqTKwQFp9wI`J=rYc;>Cckqu z8a$;9wSaq+BJhT_0hIOg)Xa&|(jNudK1{_*D@A*%TIU%z3? z89%%{feHBg*VlSanOp90R`F(sO^AM+CmfdZ z@IWZnRv5rNz=}QtxJ`q#v$>Q)FPBD(xV4J9{Ba0^1HcH3E!$-LmJ+V}HoRm+ipHGn zn)AL7GYBzQM(~<<{Oi|m_`}OHJ{=EGGC)=Vag_j_^(Q=cDX;7$RSrcKu}d;AUm`d6`H!Quy2&+ZcJL^Cbgwf2z6Ejj76Rn zHD3K*TF)7jEjwPUGl5+)k{C2|rR?xE9U8Of7&1u}tBB>h!X~=G8(f68&jwcnEv`t4 zzQks~Y7qr6WpeE7)3F4t65?;$hMj&6249;Z%@EirtQ-la`$vZMOQ)w|{O&Q0@isOrWvit1MVhi|T zKH!>jy;tAThQsZOQ;6=d^(k&A#ZTK><<2c(p$!QSAz~9Ty0X`KXW9Kdv<5-Q_8cTv zSBg^g|6g~5c2S8_MNuJ`Dq?0vPP+jeJyNWJ5D72SVzFDhS@Db{ZD+;g=?6D$P`OL7 zC4n3oX=|Y=1Ox1$Eyo0=x^VSlu~L+f!sA0294z`}eNQHZjs)FwBudd;R%BV2q%DBz z4^Yc;4ToYXQLML{m*QjfRkhQg!0tQ6Az9cvC?De0*AZ>f{Ww*M(zd+p`Q zcb%;!UGBYP&BKFi&;~_&US+ISyUgEGkTico+1N_^PKR!p6+|rpz#m>72P*)9g4T}x ze2m}#ynWk1frffp&;qu5-?iMcs%i9B)J??+hy}3l{4!zR8YpI-1C~=nh>V;p1GgS} zy2EUR!E~}iR7&)CLZrYScKwPRH$-j=@*tg}npaj8@k}Pkc3Hk;>)di6Nb;Pqi6Ih@ zWk=o=Q=G<$yN;6aCXe=`fObjDq&OTFT;H$Qt_kFe6TInhn1VH)+#43C!8*!!h<>oU zYC=%PTqNdIFy@{75Wy!UGJVqJnVgV2%E< z!^O5Glfjng7$@LnCnxSDA}fYUs-GvLc5izIMFY>B&o>;O4-l~q?9=BbaIl3YXHz%a z&Nsw4-kE#{o!iJJ?7!1p)P>%m3U%ZN{+C>>N6wZ$!l$Xb-+k9a@8UDYQ`RWyc0iq| zKqR-A8D`|1E4?^!#GbQd;Fdk8sH9Z7>%|{{(7Hmns`NS=h+3T3`#AlnBHETk4u!+^KL;UFGO`AP?Wp23#d^IGop<`=d+aIR{HVM`uah7gi^?(f^mcjCl53;dlVzCOQ} zD|-k5#~AU`a>O!Cc-wARJUT}OmXoWf133e0`XpahZ}v3E9f5F_ge_;>vK5z7tuR`F zmr{#zfJ;r``&00<&LwAjT{pbZUZuwjLgIk0`wfRJ;*zo}45WVdTS|x%<9yr9=Sogg zC&4Ni-}fB^!lw|dajV(X8PpmT*)L*I;uTV~2yjg+u3~V_=NNHtd3E;l=M_p05SD`hO#*rfXGkxNM| zjxN{VA+5644$B{-J(NYGU-Hz*iL6v;tYqEGybI=I^lVriy|vUlP!*Qa8K?8E&}8GC z7;LfxvV&i3=M<~&pkMQtkYo!yv^#jHKAdfc%qt55UVd7@O!)fOi}^|yM%t{qop*cq zM_&PT$2fX8+!rJEIIN{R>8Nj3(%ty3%Bzd-2uxO>CT68r!w-1 zzx~V~{`7?N+kPi?(%~MpHjoZk!%ddH93*tlwA$q(H;q)}yW)*D{ zB&I4;lVY0Z7BjRQVvWk^r{2uwNmrNVYuDAX=p_5Sk_8+sXVoa^Ku+0G`uTGwE9?4U)B)(= z_oOyze8LKkn<|vgl$+m|QG8Ear?aF@sUm2(&!G!K|0q_h_3QP=-BJjgc z7EDuDMdgwJ03ZNKL_t)*6eD;JE=y*F$z;XE{0Ln_#~!#J@e$q8<=K@IxlY7G2-vq< zulcn|0H5%ea|4qd&H}SN<7{5>v}Zh>7HoS$WNTb%%7|06Y~|aIlrokjdOTXFGIsU% zsLI@@*nD-rFFvafLBa>E{6f4Q`)c)hkqVW%8;qCSZ_?l8T;VSEsI0%mOWNBa9wMsz zCsW!HLp0!%?^)Zn`+IGLnn=mhmD5LF39)P;bBhhbiJPMg0BS z8z?t;>ji)O^n?%?@3#$a>t<3|X?e8EgIs!R;#fdBaZ=HJWW^q!W3fw%x9X$05E0Tcpu+1uV3yfN&JjXcQb)eAAfO$5hf z!h&et-2#wv+UuUPPcON~Cy5H+XCwC9-8n{ZAUo(*NMlpkmFA#$?k~H-1_0&|@yq)g z-qve3aqZ7nW463>#etRyjCA?hzTlPpFnhF05+D};rrxRlIv$JpUE5@*MnH1Y56J*0 zlv*5=*DZ^kX+p<95cgs!OobA;6^U9U#11o4d2DWXjYR1(h%4ovM!%s{3cNSX_*xR`^`qnj-GwEjGB&jUo#~XhMT^g;Lz2u?A+(|D9Md`*g8PdJ9`& z)84SeIBUkL=6~E(aMLaxsB4hdiLFz&C+eZzu)1?}V|JPD=F{t}#XnzSgczTuh)4l9*>U!) z-KwA%n!QbGUsRAufM3=Xk*KjNeEUkGh@7k6R-90sq4;&Z;+A&2r5#DdK!AR|CW<)c z9dCKVa$}rgblS8jFP>tAsNkFvu9983=xc^rblcR$0p29zIYb;{#F{hS(~g@+jjgNR z^8S7F_lmusCyX2=lR;YpwkY~a%u$JSs!@6bBn4LA+XEQ|J%Idkj#<|P%Uw6BG!S!^ zhl7KNAa#Xb@wVkG2nyt+AT~);b{TZq1nXr-T5}^u>sY_DnShW}iObp;X2Ku;@da_R3o99TomZqSo3A#j8~gRcBoA_uquzaf z{Q$hKG)8i3cPgWL;2lRAedcg=Dii~~D zSPl`(!5~rVmaK{5-T34=xKMSa0(mh}wYMgf^2ruDgnNi%FhSV_9^~kHC&|>-%`VhL z^wO?!xG&iKF2A@R84v&@1!pI)io@SX>(rAwLL&9@ib1qZkLfbC<4o1R)|#T)O>0~Xre!>QoWDz0euyB{xTXwz%*c=ngmul%6pYGF%R&$G4qfH z8E^3qPEKeg6>HqMuRXcPFiZ29qI8LUqiNUx{V=Yms#28vIePSB(FaG9TI2!46uf5$ z)NEhTAr`3jTs{z6p&#|%Fvi_coqi0!6m1PjRWB5CuCmMZwjn38hA?{$eTg`64=#ZQ zSeH1DqL<$h@E|fAvyHWD6c>r0{r^JX#xEEN7{m1+c??wZFzqOvy#ecK3jU7IF?i;p zNuW~}WGI#p@aN+RArLOR;JoiRuUnm1PBCEi{BOV_zz)-dFUJGsDd6k-6~Db-%zOX( z-mG_O{lO5TtIM^5_yQW-@qP~}MapGcLCiSK3u25_+R45_pEr{VrkstDK)qaxYm>f; zK06YuLFk&@tHG>VHoYkU+WOtpVI`G8+LY8S9EqqNjMtQ{z$2=lufC@&YRxNV;J3>e z=WTDP6oaK}e16ffMg-0@6zg0H%#Qlqm=}tV+b);L# z>Q;wR;0lHu<_bk4ec(8ZEE8&ck;-48+MFp(oU&n*|Nm*4u;qkncDl3X+Vsh)SeQH8 zo;_8YzaPgK@Dk_BSz&L8u#>r2sVX#b7xShaz%jYi;3)=tiL({3zFtops*QlbST*C9 z&1^0c6Ar{!*w)oglc`{CIpHnuLqN<^h&HA5|9kFgt%(>fVZtexJ%)&pRj`U+7i$8HtCYj{`wB+Z;m-$f^8-SbHKa={Odn`2LPNtPgd|%(ht=`dD-mx zg6AIYMRgkbSTK{07rlppU|5*6^NQTbZo1rMqUQIh(l|kv5m}_u)cJJVlu#S$u$xi< zhm#ecw{vP}?z^e6bp;Czk=l2^1Jm^NP0Piz&&h1fgS2P-_RFmv7O&H80o8l16VzBJ z(8Hi+j4cUav%J(SkknSwCcCz4#nDK19NGb5IYdOKmk%UvHb|P}y931l80#Q$!{Pak z*iED4zg-*cz)*Ux(RZq2E1YYeSG)t(y)X0;A@PBwdtV#VL))f@KJoA=?X{>rVNs8s zrn_Pr)z|nimebzTJ#NHGkD07=2Vc{!C=l;-#?ZxQ`Ryq(BKe&r+lPHLdXz@p|7H1L zzR2WCl~Mp{C&7J?-D9u_G@|=mzN;UMvbOy7vapLdFgJ_F{kuBYM2e&`H&|5Q zVBiafpTL__bu#a{$;0}iZG&;0We4Et8esG>eOW(j#q`-vBRQJxQUiL}Aq__x9eC0D z;XkeqpF@Xi_O1Yz26)u6@#l=36QoX3v?D`P&H8-!r?sEd(FaLS+B2~}&>t2afg*&6 zz`+WFyL>!-AlvqIvc7{UwOX2|h;`elQl=c5OX09EVa;}MS@WfK|I7OtnCm;V=WL7t z5%9KEW(I?>=2RPI$v+EIDOQ8!X+x37Gh=|@scV2n8og}PH4UX^=}M=d7%dUS z&L`S%V5DOXaXEyOPnnAC#pu>XL06`6?dZC!Mb?^JLDBO<1pW+B^RSW@#YJOtOjYLE zGJ1=ox?~|snFteMRp3SnR>lp$#m}R4e$kzY3t*M%_hc)wi)5T0XD_`vhyA;xu2o&$ zLCdF=aqa929;SNcmHP%@?@a0TVA39li{Z>FoxQHaYE6MLxe}wCoC38$L&+mHwF}qh zUJ9w$pWCF0w|(`AZ*IBBenA2eE;(W5VDFG*EEMo%o*@bxW5n0>ic|_*xNltB@M}1S zbkpr}SeuFQG)4UBaKxv1LCOi&w1X8`V#NEtRf+O3F8H=>NX}MZ4*2A>uei z{L9;SywcYB(Sgr#!KZn~E*X&-ONiJt`C2F4{wR0*OkpUHrwkYvb8;_Y^0h0*U~+Az zJ^PYS?_u)Pgy5OQb;vqoM~|S}vKB=S6}A=q-_7prWmn>$Q^ZKu=)Bvi5zpV&8VFq_ zB<|U?!orGONhV=EJWrSpp~cPk+dM678JU~Q7WK|UPZ&2ErMrr_dl9;7LRHPfO5?~) zh7>v785qLTk%X&k8-cmB6f0H*BkAo@s+CBf?nn zc-9KW%2{wYMWiH1$vr4ZuqUGp7A3?5Ou(W-XH7-U={4%7vgfLV5&>w zQ1m}$;XKycWn)t6m(&k3vAcJ##L1{jlDt76YS*N66I;W9svKYMfA7Z-dJHcx;e)~f zHA}I4>*R8wq*W{C#b{SZ>hB?v1E zb6o;Shbba+D{@Mllfa7ItTsul-t}_#i3XW3`N2kNbWoYTpiWuTGn=!d3b`uNdF}|L zQe=G{SW!fs%s0mGsdE;@y>AMO`U^xL5ueJKEHe;SHCky{Q?(^{0UzvusBZfzS(`H0 zAEfRPx3rIPUyM+n4>1OGr#b`OK^(1M6j7{c_kzj`$ube^j-Nfp#O4h$d?uoC{c3rt z+EfT#!>U5gs^X$+7IkGc!zkbCBv?%y8-cXOj`c-;c}~N_S)>__)fG*(Vczl~>sl<- zz*@m`(3wi6E_cy;+@4V;1ZhpAGT9c0H;=TDx%y`yumRfMBTO5)Kc2{!=Mg>&4jtd7| z#mLJ)P7D5ce8TI#;y-Q|yzZMzf1B@Dh`KeQn__0z#_hZ(kpRlPo{ldOAq1e46n7eQh+qj@i0k&Jlav&_Hh>6-Ep9l!b*mrH}1&69JIl+j4ea+^za;Stc)C4)(2aW?o zOFz)E>IYcbZqTB5fT1-ITH73{6E=9I*`$wR)&gw zKR6^X#;M9z*{1;^aQpENUc$w%CW_hfq>C?2#TXC6!BeyqGsSc6VfETI0-)4o_uWay z^-+}k(f@9+zwuz!AbQ#rmo!X#T9ZldCd-eW?_M|3f5sksh%VBls6A}zN*2>5x&GZ6 z{o+McsMCdR%GEaxOGHdpn6}uxyJxN!iu)hN5Iw6G72b&9aO#H&*5(D(?mKtvff~a{ zz_#!88LF9$()P4ll15PN)|9M3%DLaUoZ{Q{LZzdx7NS0*ibhL=QB>=wX-H7;;zYXl zBM&>NLQ?i(+NQk=2Y?I|mc^vcWt|d95NXd+^|z`%#QanXPl+okU@i(!&Q&Fr#qD2G zP(S{@sW-=hGmf2-TGmBoHv2($f9kxOig4Zv>?|4QZEcd{!cXW534GYk)JIo-ZH8JD zWKmb;t|_BpfWrrCLEl(|odE~>L*Ll*=ElVBa69<6yx9;vhNm_QJDF^01W30x7-8K( zSVaFnV{g_ZOOjma9aS^;h+OWiTUEVG4}e*O9P*9AAHe@F6ru2e4e#RTnbL`oCz@ZYmsSAC@$+cB20c-+X zP_S~qB7z@t#%}juZC1K`dvx{)bYon-&?03xEM*wqw#oPEh5mvdJLWWFx+lSigv4*x&zVExG9hBBYlG!O?jp)NTp2?CM9E?HUV9@Jc>TH2i zm6O_rH`zjzjSnMjkk`))Z#CO;Ztv^(^v$_#dF!E^cL5uXmuyUwwgO%V56=s>Yt{`S z2xae5PNgtoTJ!g)5AsFIL`WR`Zgm>$H^TR1jIFMrVIs=RD5-!M`1Jb+oE{eZ=l}gr zxV@>bYki1_D`USV+};xMUhw6|O$%67skB&gUy|Eq(n$5~>v=cvdS}x%6oPeP>fi_B zByYY!tk%MMT|1TDWntP&AlM2)C4eHlcL04iK!Y0|Svd}E; z04g;msEpIU5D4oUEea$80TxuQ~k~WAT!j^l?R-L4$_HwYo#r%-L{IJCJUM)x^Pfepv<|}Y7 z1k!jo()+61`<Y6&CXFr3$LMyj%fTSZ2IS&KiWZFL13O?7SmQi$=^os20MFey zC`p>0XHYJAZ@p04FLsMPjZ#C58@g@@(1y3d-uke+t8s#8v`B7&UDP0!08q?w<}-@L zAdodfc`sQ%Tic3ODSIwOy_S{0NEm$aqY}dd7txjlV-9<@i_u*xR~<|=e>n(+v+f{G zDqW?tF>p&ENn%L}b=&OP+2S^(ZToAHeWCJ3T74+P`!Ab1;|Aa=zys@2B<0ixg)|+{ zGOGt_EMsZ@NfBTk8MI-s^yi}(0e`$=qk2t7&UXKMnK&|jzc)u6k#ARQ2rHyfjKu~J zW&B|a(MZyePNJR_cNO#|PUYwQ(B1CXZ9dhuR4ttX!jHErzPr9Q=`9&8x!#l9_z-9f zcYPl&fv}R^N6GxiH&ex&Lcqd|M>{iCXsqQqsJHgNUS1KbN&kMo;mu^sY#Yy1H|a&C z#$PQ92q*JuCwwUh`{fJ1T26Sh%-l#7hNpv-VhW4wXzBGM3_VLkpIg_A0tL66)GKT( znL2Q&_J~bXCLNrWjtNZ)eJlB}dTX#es1LbilB0XG+0-QuF*FH&^(i-N3#o^=4zpue zy5F)+NjVg7=;6+F8dr*7yQU_s4S`3hz=?AO#H0p#9B$$2K*Auvc!XvBODKdx&W zoz$|~oVm+?`wQD5p6rDxG&Y1_jpA7!0+uD3&xV|OqMq{q| z!ZK4!+h(Q*)Q=D)D@|%nqc7y@lVe~qm2vhfQgTKV;8q08S_EBQ_I?0!e|f_BK5n7~;i(WSa_iYjgC-}RX6i*m%OjVuZao7rB0skrNlj!CZIUeH;@gXi1*RzVI- zJ-AjJMSU;=6X9PNU5pK!%f(h08W2NJ>GGmQz-_BJ)E0vS0Hx-s>lcbk#1bQ}*Ea+B z7>F%U&wVBgQ4Yls>N^n60YmBoNqxXEc;dVimv96$;V(({WgHWUhx^gs%^)`YvCdAjl-} zU3$-KAe0aegwsO~NVaXhgFBUap907wwV%6Q{79;N34!osOYZmX+rrkQS2!G59ncM% zN*H(Xa;5!$9k9T8jT_w1`KXD}w$masure4PxQAhbstE}IQ9-sHLd`xcQ`WO??}|#Q zk646h2Su(S9*4*^x~Wh`w9F2Lj1_k;_M&=OV%vT*jcq`;$>^4LuUj3ei)VTFTql<$ zBy@ZsG#R2j2hke3y3MPA$p-rJlxe~+SQM_&M8$zN$IYFRlHGZ28+Do%9K}h@Ww-`i z&YVRc)r;vP%E)~-gV|RGn+Z(^3_-z;)XlN_+}hxx+Szq80`_r^s!vG0WAQOx8!tx%)sl7u>D~O20?cohY1=}=Oe(M*g8Zh!V6vc(V)7P~(eCVUka#M~KAAD0y$))lvu+V^|i zH++aE)O`CQeQ3i>2#83?-Lq|qum-Y&W+w;AgZQH~`$I5*S5LpFL*1H9edgd6YdxR5 zo|BJJSv@parmAvw%Vm(jx7CxQj@OnVa^!ImZ>T#jvtlOj5`ZkjyuEJh{6L-=<^h)b zp1URm!+Sd0ftEK&G31>GRJ!UH19XaT{gM;{Q`t^+@lxrFOs+5K-dma16L;ilBjdEg zp=hD*0m*YOESQgHvaKfDc67hx=JyB@h>?-@(xYZ34U`lhZ5e6HZ2(cXpzyFC7L;O* zE@}MGPd{9mgji*EYy?3c9xfej=8B^36{az+=}z=gDFw)E->cIhOVQYlDhXyLENcL< zPMSmHR&R$uh)ckJ%Z^A{H{^YiEIxx(x}R&`v-*H@-4qummm-@kdYR$0*&hx^U%H26?JlRxibo-sle#NPFS%B!eT|NpyMd@9(^T#T6y(;~SGXTVLn9@&Msk4|fpTkx^hIyb8ynV^VtytTyxg|IUeLq%+!R$XY@-^9nU2G! z;U^<6e^wb;&9(&*wtd56j7T!dzh#o7j#xz}02MR}^kE}!nY1Mr1XBpd$n;$R03ZNK zL_t(hHC*^Sb2XH-!A#9p6jFI~$rdG2svcUuLnOw-x@o%jp#pZ;;*a$6d0ZAOA(+InM$J$wqU&4l%v#uAhR`{x9)~eXA-x}VgYW353J%wqE*M>QS8L%hlWtEh?%I1tvbWNUuy zuEvvz*Wq7RMLLwW7`=NTwQ8J92~Z!`N^JJKv(4_ox;b8n@e~%Eq7FBA5d(1Pru57T z_}WXxwPX~_^nK)rzkYZ|WX2^W^R8P{Y|_!EC1Nvp-KX`0Cy98BE57WvCWU^>3Fi>; zxU5RqPrGfXBSO^oMHYj38HK%C6$JXc7W;hJi1A%1(-{MS7^lE(aZt~C5k5dt`>dOE zcvpdE#7L5dzAKper*Z&RrnarAQG_+I0?=r=x=@L&St1}V0oyHsBVpeJabcVuBlcUy zdXBh!-f()*{MHazWp!2lC`yaY$$^%n7eXZch1EI*rL*SP0ItmEQ zgkHui{jt$-#TfPMWU&q?@A@-@5WsPB^DAAkJ8ZT6V8eUYYcz?GIS-;O zy37620vJFa_HJ^}k28W;^xe!|BF))!h-Dh7nK^4Bo0qyU( zpbkdMZJb>az_gI!|%VsbxX)na5}Gez1;BfhnouklDgEMjC}vBPZ=`tNXq+khaYk zec&=AC{IyuZkyLLflKr64yq!u@<@8NBpbLxnuoR0&$am3ZwVzQD`Xkxb-~kVF%PxM z8?XD0w_9pt?b1A-++P{&J!~>5ojP5&2=(m>0ZWX7=QkPqU|AQWtgYz*s3<+&1J2B(`x-sBg*wkwJbk9>SvXdiyfDf^vdJtkl7UNWmH+QpxymI^mbg z1)r~%R*Ki@X|_ThxnvSKrTH`}}B%k73AE?1^H*Eh2ZDK$5u$M)27KbZ|lYzzD*f?p(e zUN2G0wHDX721h+kBD$QS(NZwl7cfBL8rH`wNHyoyvm2TM#={w%jRLMg7N;f%Z7Z86 z+e1~#&Hn5>2Zz_r5J#INrttKtf09(Za0B5cV-13Bug1R0HR#idTplT^s)Q?*i z0JaT&;)vBa1qHw@XIyPi2wQZ0PCH)n-V#6n!5RXnMP2Do@+B8+DENv3zFtnqsBlF6 znNQIGQ+CZaqcqq2W`W?cZ$@p_=Jg@ylk&RVz#u%CQl-A<*eK5g;F_`y4$XQ>)KyNE zc)c`WoXO@zn|y$=CA~&#WJC();mr*yM>WZ#$Y6u2w`fMP_k}j~wI|D2rR2L5M6Pxg z{d&x7U_AXPZ#f|b#x-Y6cwZkQN>WKFkzl_HN-ns)?)dV<70kfXw`Wk$3wwTCG(O>$ zkoVlYnic&odCCkb&XiueWhiwM;+hQeiqz~qns9M4#QV^}bLuDJER&I*?m2S(3sFXR zl#Kdq@%|FWh?hx<<@0I$%W0S>`Hi(BiOOWgsNX)NjGPPNqR^_hmo&)31wEv+Nt*n& zLgc)RTQ@w-cFu*G>HkFX~>nC#~0j{yPZ;n{8@69VCM4tV{%>mZ@zHj1OBMl+qHbwxU zNXF%DLs%k6fZLX|L5>0E(_%0ED_&o3IzdetU+CJE8SZx?<3ZG-p+JBn$ONQq$Bh}+ z%MHtU#lyoH+n$kb$%^=Zl4}O)2>UC}OE@$oGzdj~nK1|u@9U=Juvx$Fy>W_*Mt+rP zbJW=i#fn^RzEax?7+I1{4k?SibVZy!#W&}T{z}tvLdSQHyJAP0*O=q#M_=Br?uSw& zJ!UCG1{p>rF+&8Yh|3E#YCiXXVI@wV+mjPgtx5!o`_U- zy6@OZk8Pmih`1nO_@&tq=UG^DqDiX_lxAn$z^kyq(4MoYL8Qsd1wqg25L(nwqxhSQy*9}& zX;&(A1y{ArnO?Dsg4ex3pn2OXGa}XDX15#FfoGDUgGp#4-plRQ>=oGtJ3|@V-c=dN zxB*+usdj_$ThU>Y%0q7%56=s}{i_dncshXt;n$xo`02YZ*k04nWTaa*s0}qx7S~3m z_$=UgnL;kPNE|VfnPH;V}KRVdx+OT(>ZB4J%ofD=z_dgL8eC90VFWWI>vx+ zwA?{Ju%1-Ho3og7nFb>{IlpD!U|9& z&X)~px}t%c>;0djjDD5@cgaU!YE5t54Anm$-1d~_kYD*%IZUVfcl<{*>ctwdv2S~2 zh;E%dVh-gyW_tQbt9c0P_4EJ|Y4W7W%Q=Pn=6j}I_qpBM_|6!C(c?6!801)d7KqWq zX&N$6Z?pxY+y4;sTF)SI9?gE;h7Ed^^k+9>V$ytcBCJc)NQYhH+^7`XwjFE{M}Y}n zUal$wO4+tOxsm*9vC}q1HEJXEBENKVm2FH5l5Hkp0Fgj$zhJ~=!EIAjMiH5 zBXV;7apOLG8g46G22V_V7r5&=d3gwePwNSP^XV%*o>u(*k3Zwb%NyR(-U`|tz+2sH8Kt(^|G(Jxsy3lep*m9VmUdL;bA90~6bwXUF)IeQ zn&0ttOPj4}O4%Zum`##zaZE+91KR9bW3v+L{p%xwnDCp22mHHFUt!O{%f8`i4Y~l6 zZ+SdN{lHgdkSDr2XBbP$SU36trxfSWrh5(-X=(>@GH`K2c(3Fbfn+LJXo^+9vMK&YNOZ)h_*@I&DQb20l!<%c#09{7@cxYmAIj$!i45|t?yoLe&k|y zjbq&o2)0tNB6JI$fx}>*Pwi*x*UbX#CfP2co9oD-4JvE4crevVV8SisM!c^oqE`H{4h~WY zjeg+J8gd!zWOtTtN1vZX7c_4Xfc0bzX;8^?2<%9&)FkpP%9MIsQjJDBor1+m^ggY) zVFjBCK}fgG94eJmu&xnt4Vw83g>uz{fO(XwlDqeOvr1mlIoISeZ*MRHutxJB+^R(j zu05kfw`3i{>_~= zmTnawa{fuzR+ht!c4weK@8SjiC0q2ASKbTxEL}q93w*#oy_X#ib`K{TnE?#LT_tW<+@ z8KIo*`)+&EcY$kCYg3ku+}RW{L}0L!8Ow0Q%?}s1pOJ?9tNt&TH>>7y5;_Z_DZorh zxDX?7wK|MXdGWDdG>K8%MzTYkOafX8(yidU1h7dGo2{i99PD*lrG7DRYvcm!fV2WZ zEAXp+?Z`Ypxhhx@RwULQYyP%w`?m?8*?)HFabfOudOofA&BFsgz~BA! zt41He#beq~C)s;HJ7^y@FZ>8pt(T_qEb8VcfN;&ZNh{wZH%a_n%3#OH0CrQkNZPS* zz+zz1Y`*BN^sQqO@@!%x5j>azgGk?H8#LQD65(n9t)9DV-uw!qW#CIr*g*&)h=K7O z7i>~&14sCOe)_>=^y+)eQlVVMvH7RDK4yj2vBsu*C#~5|99FE8mEB;nXLf6p7Ta)u zsmIdYs$Y}(v+cLJRIM)4eO@K=Wanm;e-}Rpaw@*sBo+7WVCq}mhY%WB_?8Q@jkSFK zb+Onc-Cj~l7P#cp{%mf^0KJyy!OkOXcuCI7Tn9TzfJB1OoI`@=u;6!(Pq?NXFZF_(8~zXok%Be228Ls5s*w^p zU=u`m*=_&?k(DQ{s*4Sg7|+X!Ei3d6BDj=<*OYL{8Oc7w2WBK|wqaZO= zWyFY0c*e~dQ0;BtKqyNZyXG}4jIzmb3&{2qyW)sYdQoH>K@la)khEGruf25N?%a?Zynpkg(U0C~%)^!A zZsuTqpZk_W{q1^pVkHUprqq!Msb&zmJqR69Ms?aac9?ovuqaMBM1Z4yBlVvu#D*7! zWWjdJnn_2ROL%&Wh)Y1u1!*_B+ujl9#FLE56&ZC1Ad}3wZ+l1TZTJ47ZK46)i2I4H z&xUM1IUgeia%&|b0H?-lA?0teOJ2qO6rS@tL6jm4FX0E zrbs_=Q1IOk&nI_kEN;)OHwjP#Dm=zYNh4 zI5hkxzKH%;NNH>A2~me`Q7r)HZKL?C=BeZNKTyx((d&JeEG~UFP5wnJts&pHj6G#M zKR-ZH21g#Y?PQIapl2`_D~;9iJ!U)3i}fMD+~5)=C2Y4H=ZBM$)1`oo@;I;H>$5bF zk6qN)`=aspn&0E}VoIo)0I$xYH)zn2m~Rj%l% z&coMp6<*^)Kn|7>ak>C$16&$keQt9;wAX zT1f^-S3SdcP;Be4vv~o@E3W{y4Jb;_6K&B$1B^$vI1$uja1D0TQVfh)Vb8^u%Q*UQ zpq_fJ6uSqndoo#L&^QtM30G#E7ln*%r69)W8h5oKy=^-l*0pUS#D)e)xv8hX2Fj`+ z7Hj&s4kWmk@=DJ&IIm&#&5hyNbE>sC#!9zRjjLxGrSs^--CQ)5X$=7|IVX&ms`s+i z2eEFYo01NrnhBk#-l35)0leHzJt>TOeIbY8)pbw!hwZa@l6|)(O_@=zw;#?j@$f7` zW;2F1Cbz76qlch{`*l|jdZjl98~#wkC~M|BiL`r01lX)`_R1C(QHd%uwoV~e|Z+P9zRmcOx#-X9NwapH(rfig{R-(Qd_Inq2+2SKDT0%f1C2E3LtXU|Zl# zQnSR2(e&f+wboJ$OmTMoE@kTRKehg58xC$c;gS@9$tD>W2tpwPxXmWfGAK(o7gH&- zR1UzW^`x2RHj1g+`YL7K&DV>Za4i5O!efZ|a5~{3E_lm3wp_5~jJ+hQkEq>?Uv_PF zPf_!MUvtJ=PFO7m`8Dk=Nu-EiA;xp;SO3A{nkpo6u^|NPbGQ}t?njf}t4o3!XJM0U z!wyE2qJH0;f$Qal4_`lFza^{>t2RtYfmi3p6COXD@ZEp;1?yw%UQIh3qzKY&M|#V{ zQp6uV?TjA6vSsky5eOfCd&2c|QW|o>A-fa~+H;uodrzLYh5@diA40q_NS*JQD*g2R z&;X8FT?1A{q$H3nH=k=6Ks!DhTbvXEgJ;MeP#o;C5<_x9Dg-VW%PHXL4{yA-z=FePCbQhRP1ed5X6C%&SKs_zzTTy7T2yB79@%~-((vgkMRvPwSeDSdu|1;4XC8Z)PcJ3NZI=7* zFYgRB-XlfN<4Zb9>4m1*k0(xw;{c4C>Qe0xQ1Df440iLX)+TO=N{LOjEh@!**7Imq z#=$N5&jZu8vw7mhy{r6VHA@|60EhICRvc-$^uMo>Gk%LVF$QYBWf5`2Z5n^TgOuGB zekLmgWAI6~Ed^0iFG+)48L){J&_bkMyS7`-cnn%_);T3}upD>Ia*yWePg1aifH`tYCN!O3Ogmp}{DdmmEe_M~W&ahTRfm%EBEy$vg+@^}yC$!C%4>sIY*Sj%6G8^dtPO92 zb#*mNG2$(QaEhvy_!uKTolkg(5tn^O${Eq(z25eOkP`mp>4g9E_1E}^pMJ)l-!>p; zJS+i}Gj@|kXFCb9`G|`Ugb8ORu&9#mvM0Qy7kt?^yrtA>;&n)g-Z&0&o?$*Q2a}ql zzYcQmoKN$|+7e{MF*|I;6QMCYL7+oYyHA3_!2saR-TQI}kl||_flTm?k&RFWy9OP8 zwz?T-V%^|cI>K8?=I5=OZC}sYCf8e@KuAzfYZ(>={RK#q2wMgrxwFg3@jmu;o_Z#~ z=8S*7ydn$maar)V29%@{WH2FsOtnx9Fr}cYWU*qX)NSeCpR3Q>29kfiUeuc#0#=hU z-%7?MCA{U_ml#agvo@lydB^9pAu?g**nnUe1tg3bHk3uMa8QejZ4Opq5J$^I?w&7;d>kF`+k!snk~5n{meR}XmnYQ^Q34X5V_j=JrXjj-Pe@?MZrF)x&>GE0wF3XySoiumfc z4|x4@!`tT_+neS}efu|0`0ZbPz@Pu&*A{6qrqS1lqf67R7mI0#1F4eP5tu9!=*HcS zbWinhl!6Q!isB|v74negNyB&v0pP@ z=;r<#1v)V+@QIm`vyzsX2-nx7KEMvkYg2oLkkPnBKX8v|*q96(KqqXJp_=O+;|fdj ze}fVJ69dpT~u4lBda zr%s0>TMjzQ5&c91&_TpbPqO$7%St8}tfmIyaC)-yk;FlFC0hr@_nk$eXKQ$GUHwll zMzpp>6TO$Mp*+g|#yFB8`}@9DcP!q#WXnMVVI)qK?@OCzH=U?GAEy{`%?Xk-q;4)<(r(%Q9XU=q<6Yf38HG%9t?Os*XmS?3{xOIO~x4#&(#OoIWu z<6|4I^EmvGX~uqtDe^))8CBREjK5$Tqn2^&U?cEk*{V{RZ7(wV+I$nk&H3A9n|6dZ ziLS8pI1fyS0{;yhZJsNTrFiw!)ZF$-byKNSNEQpJ_(G~LSIoCMZnHhB0nTqrJjO#w zU8WHuaEDMaLX*c9E7$&u6^ccD(I7(k^%_`yhi>@1!(U5nSUS_l6`jmcyO_ z$)dSK=2gq3$Cm6V@E0;4rs>xipnvXAcw- z&7D8w2CJj-mNSx6fue#YYX&NeVm?QfiET3g;rEBPx8@HeGQxAo zxTK6TGk){@jJPcLe%p~rP#CzBf@JUcmg!p|T6Tql^Hu-6ZTR7KYm1CZXm8uY3WciK z>UU;~^Bj zBGeOQs3ky12N}A7?qOviojfjHsn}wd3|d!=-0a@>p<4ecm#9HsS;xutKB|86(xO7O z<|gQ{;?T;wWCULuj7tooe~Y6lQy;$UMR410C=gsFBjbeK2;^iuirvoiV)tLNX1E)U z@}P;R;M{usvZ(+0u?Qpr8)e*b!CO}F+-iWhKq`L*f)1tZ&t7vHES!OL8w(Hu1S_-o zh3$GXA#l|D1A>PbAsCrSHKxjO)vBBO$+$t98_BCnkC}CDT*s& zIR`v^SaJEXAw))8^us_{5H zPxtm;GHm6KWa5+r>dHe7{tGXSOU{;(~n#%3iE+9og_L@O(4U zYN-wolV8|avq{eDPgwkBL0Y5XCn)4!uyUu-kWnh-YCi>3@%XW}rnnIn(cw!4AeI(~ z6DUoI}7886{=sy(6r2k9lq~At$l-`j z!@(}*XdD)~`MXRTdnW_;kd0xP#pC3tPU;E3XC{1l(zf+QKLl8&S z?b1Uh+j72Iuh@kCXdSSQl<$oK<`{%YVu}~U+~`3 z^S?vRnsZ@`1D4f>qbU7%iBWUPse^m`rdR;nQf}u#F2(4YcFxkBY}@M5byDayzZDt9 z+=IG=hDa~WwO_m4${_tH+0J;bTiHIpsx#v(dXERDgZkZ){ko3}P*CFITz=Tt%?c*NQwRofdYQ}@c|-&m)owjMIfNCZS2Hkip5=5J&2Mv(F~mYoSdym2Rd2rRRgy%Yb?pFBu7k^=5?-Fl?c~euUUfnz;8LX=VdQN)g$hVJq5-$&rf*W zHvG6Jd=&!Df!leTW%O($^8qgmEFs{n6k~0a_I7Q6Au?}s>1vK5e*AhTy<_kL%5;{D zyaIieLgUOxqpy#uC^pY(fNs7EfJLE0c(!P%fV#0vxS7|qK1anf2WC4BWTwpBRrr)i zhFk|R#WhvptcYaU+<8a^@B@_&YV|CWCS|7kfL=Q6QSMB@rOVC(!#uEPwEaW{LAX(? z3xL;@un=2wsn_{ZG8RWFX8;QVhzXHZ7GFVGwIg8i=R@x$DvS^yoC4!bjk&4`H?dzjY13iJOP47HA-qKT9oq36P?)A&%S{MGgg} zC=t2H2x;kbUm5xdX`AW$l4zM|7Ao^nsh83uOV5lm85nG@ljjViSs9`MFH*Y5Q07H*|L^R$G9v3*UJuGf^Ot& zr~?9)7;H$S+m5tlodg0l|z;b{$Y zl14SI@@=-};qC1TCeo(9s7i^rf}FJY&q={Lqg0$)fmEQZ3CSvNX0GyZeV%LaQ);#_ z;9{20Vl%u?n`wlyr6Tz1amDLx$Iq`<_4ksCG9pzP)COt|+^D;`XjXF!Yz5!MqaX!M zZKdG(ydb46pAM`rq}SVqB?f%|`PF>hmmyM}U;`!&SYuSVu84Yui4a53=)zK_!IgeK z=VdjiboB&xns2DXU^l?YqQfo{c4-_=gw~|iF6S1EY!66+lIx}A8wb2@H=Nc5r)9DC zm~N3^KH%r`NhQHX4^BDbH;>Qwr_Vp%CFIj2MV-%A45^lH0e^nIG;;Xwo*uw~@wY$x42EvUl9;#BB4%pypWG%aiJk#Z%YsYJgU+akfCWgV zaH#5(B4ya5is0^$y4fOV9KO`Xy*7~9;8kd|X>x=A{uP7yF5w1ICuV%Gg7CzQ4Wjwq zZP0_hsU$hLM#eHqhL};W5S;;XgGAq{n7q2ON7l`YGcRYZbsw5Hxod|;RgZ^HVM;)l z>t(Ox;kG^Q%>|>^n>CvrC`b@oa>ie+k62@Xh~PPAJj943S>OFO=yRH~rVu!&-|S_T ziZ!uz=GAR!ZK&&jlZkN(6)2_yjYg(MS1tPHCUA}EqjsJ*1)}uWGpBvG8foN_Xwx5> z6}H$Xx(057IJ6pef|Q~wcVj)(IY4F4MezUrZ{OkjKYYf&`H!FQ;nO2>Dfr=!zvBBp ze#V#Y-*9``74d#%gq7Qkm$qW^QU_GBH{7x^mdAkW7n3mU1)u+PMOYbeB_t-KYwlB- z7G*%L*pNdIwa9VtQzs6EyjR$1hR8`Iyl;?3Lffp{6psbCvq_VpS+=DV*R1uGEV%=@ zh#cp)HM+S97GaAJPZaNtH@Ez+rVfNiJS=FX-~^|vaxlkGNnO5exP z+yjH+Na|JDaR>wiAVzM@QVU`)eJetB>sGpix=l63c}NX6PQk+7-9|5%ygCH7g1rB* zD&ZJun(a{XxvtJf(BMvuk2!*An5N@ylIak+2Hty)idY6Eb$O8o&HN=9Gs)rQMh+@7 zc>(&DDYNISt2eUfG`TX8h}q)3+&xrbHsmEyz1t+A;j`#APi~a&2E!nm0a9p1yjMSX z_e{NZxd3;FP2(LXGw|d7y(@F%&J-0@C6{a6#=$lC=p=FSl5sgs9;Da-MU#KK0^&kw z_eUwpXVCMdbV_^u_hNo%=QsTPa&23+0Y)=;^Jt*E`xm*0V2uIiQ^fPb0%pef6p?em zvP9$}NV#B31wxGNw&U%#W7{%#2`EH}YgC`I4S6KVG`XjPA9o@zO!`i`BNvx{VIzja z7U$@D#~=suzIQ2NK%xvib?8`QwBPt`i!{dy10#uOpQJ{ zd3W9H)%PY_R8NtDElJz@Gdgv>M(dSf0`H{j+(9fc1k0DUMw>$$)Mg|bT5|*Zb*h)N zVx^#GEBl9l0Q`F0?CXy|6Xo8X2!e_VF{Xj`>-xp zc-75wlV>v4B_K;d%6bhejXjG#7nj=x2(Sbm-oFeWgos}+S8bG8VUJv;y6BW>9Y7VQ z1?a%687Fmk2TS7y_TQem(xd*F>$cJF+n*0$MkIRa;ZJ&Y- zWME_*tR{rRK;>95STkH|R7u|=i91SrF)LHt5)PT+1R1t}^HZPfYq7~WliiuOoDhT3 zos;-P62jZA8S76=#L4U>spvrc%mEx2+n(?cBmVujUxOg{t8c%-b-Ut^KmLM$`1xm? zW5l0+{estBl?^Xhw^pkSgOxU}o9raOPW@fX0-OQdO2KZ@Zg#(|Kc`A@bI(%iW2)!!+W2dBxO-{;;W!3Fl$-tNI-|+dn3&JU2IWf{E z*j{&(z2Nj=K|C|o$B6wkA@9=SdZZ+jgeD~m3%Bjie$7e?<-X0$dr_LPJy-Fhf1S4M znp+u)o*F9eCq?Ze^r-%Ig6C_rZD2lWHSPoLW1D*{Pq@{pNoN{2y~43a)3 zATHdCFz+|>uaur3UyH?}$W{XxbqNbGwOq&UJ?#Z% zl{k)K_+uAJK6XQ9dZOSsT9rZaLaZXjt?$xeB&bQONY|p*4MqhTRbRI+(8U{zTx=6aKv6eFDNRvfVi6;CSX9a|JQ|zU zxv`&q$>aRxz90C`dFcxVqJFTeAGB^Pc&yM|fjVTpx=h7lad6}%&HkFQbnqEaos ztgBHxuu57~Pn92w$%MdUFUA?MF&3o+;IuM+^XU-)aN9HX+m0`n8{Tfp4yXf=VkF*& z(+ZCLnclX9u*CNKv}Y-oE|--%R2AQbyD5dt_qE_oQ6a}aKVwht^0TN@N$s0SrS(#W zy7`DOIDZ(Cw>C(F>|pfLMX=0ELwx z&Td=6M9u?Gjp9p_qM<4~SQvpBd(p--TJ~jW<(PR?SI47&bEGlx6L6v&ZI}hcli|3)UFgbDPa;ys)aPavc;DrI1o! z%ezFA#}-Q_SVBM%!Im?&+t!}z=1sHhcI1eK=}rr0gHW=2VZ>id15X<$y8LbD_Crs@ z1g`tm&T0%WW%cvS&kMzdCuF`+ zakb*XCU*UP3?1Sm+wy0#xt&G#PrK1*YsAZ9 zaXP6e9eRxcZ`Uh+eY@bg@A$Z`_;=rYjsNdYKj1ADtfWn1A>hOTFP4wJOFHFud14I5lgUHx>Y&N>I4Bu}?k|g)?QI~N zyU#@Yu!aV!2u(^=cE5L#-mVw(DSucZzCNu=pf;&Jn+?bNdaIv&l=_XMLGpEGwWd{> zJB=j{+ybYbw?1Q@y~XSBmY$;vX`WIy)y^;`@QYFRxf5YftaeKd?m)%5Euel*QJYqi z;~AXjcmLrdF0UKXEw?6xL&oVjVm(Kcl(9TS5P)AYT2sM+n)e_q1d616T7 zP6gO6+2pMH=WefQVxY(rx#J5)iyuNSNHl|Y#N*#FsgAy~Mq`#qibsQ8&r@14$7qnB zV$%d=8qVM0KpNdNS}2P9E;Iu*gr>*~_quEiV=?*P*!Z@{qhg>InxuI`N5yPA-~4N1 zq{7ZN)5AW_5n-!8x|j@@YF;lZ;kkWv88`1Ouh(Q!J=3(yGbO7(T|{NJhVRr(g-zTC zpllXL=+@y%2;*S|+)0}~rqm20E}?rt$^D)cM>zKXn(Vr8dojoaMxujp*CQ1KW3KR+ z1@umI*p%}n;SwXg_P0Z<`IzFUnd1aQ2X2c>22p(-ALALKn!*S$GQ>D7NgLl?{duJnxdyNY~g3aRag858~8!B{3n$L=K5hK*SadogVG%`|DisqUZ zU);$Q`v+@2nf2Q1&ubfGhysdC`j!ijsWia8Ow)}b;=a$M3mEdvwNMw=UMXHpMfGrig?i;rK z6-6>~E|dMLD!9lED?L1u2l;-P9u0AjoeG)~Xw&z$?P|R+^;a(D3v85QlNM*eo-;n4 zPdJB&iwy*3$?U8l6~{O6prLVgJAQs_UsSU`3p{dEIs}YfQ^e+ZF%UKYxdpea9_lL?V1zSN!$I zC;anIzv8=>SG?weNAn_ULz{6Em8)lHHtK)!oQG~(esW4?$31Xf`Yl!qa-5WsJ_qF zd_~%`^OU-msmqzIO>_|qF9c>%Ppbz&nYWZpwor##!er7oG^tSc&J*6ic(}LbJ@_Q2 z8u*WlK{FIVK$yRD~(1s@+zF1~=V#HgO% zJ>j;g2lTWqxTU0Ce+Ow%eGpQTcL)|fH{i6t(VFDi4hyY+u30o>y1LL%W4!Eif zKNA86Z2N9+8}-%(v*)C&@l1OuSeU_;UM#@nw&N}BxbC|(!{fGS-crFh4D7*ye@Rp3 z8p^ggXQU{9r!nB7pOORafmAjEBh zdh^xeCo#aO8o%7g#^n&4#{f*#)iUB|$TH84J`CqyRv^mcW{x6+HF z4LAruV2%FVBw1_>wYjC?CLMuhAag?yuD$|`YbF=_~Ydb|LygH6IV*IHru-Z zI|Q*179ng<8nnRCoV$}wAwZ3yD?=Swruu*;E5_m0!Q5bW)r!9x>#(my-BE8+y$3h| zhytv(^(B*^84C7Z%Y^-;VLuk?>d75LNo_j1WzI2+w+4CL$(_Tq}GL!ks&z8 z*oe%bP??J1?*5Wo7bpTKRkT1ZT&&bTc!=-R<5^_^p8a9yP8Ydd^7!OB} z^6Z;5E?c8jvwry$22Vi0?gsI4F-TIZF{zuwV&#ZxL7S%lB3Rad(>daHO+(RJ$*5d4 ziFE z5Nt+04uKG2&>X^)Aq1QjMqDE}GGLpH(uN#jIn|S8!RI|XXa)xc2SzGD-gA?vGgE8O z8YIlepn-;>w%m20-}64xxEYf7r?`G$UMHao(*WU+8iP;^0wGYaxDtAY&+7cQ<7%=O zhGaBF6dexAp6oNMF=1UHK~vmLU?3I!>yJ-*j$gMO>k{zm%e8HiW)BcrA>O^CLnP^n$ z;NZ$RNE6$HP`X?`SD&4e&w9Bexp&PF%}1RQTI0X62yXW5pOythcI-LdN%3^s1-HSk zya)1=zCY>Z=4ig&60W;$ z^scrEt~t-S2+nK7r^g5N#BsoGS-F8!0C-*&Ek5^zRDjpp4VP`lE=2*A)H9^3(xttx z0VGMyhwE>K9$_F zH{DyTO75$2MH^f71&{R$LGEq5>e<&mwbFBx(X9na|CVWEcP9fL9r)+dFNhiE5b?Y$ zcs`u~5`6dN1(&3s^(h!(Run)LgJ#77z-8ZY%LN~nrKum1D7T}^PtQT4k=}~nV#A=C z|JAA5z!R%?k^n3$*o(e50ugq1y|U*v*z;07v&C%+$JN6zen+U&o<)xDK5zNC=OUun z*5m#o53yqqPNah~ZF&XKo=dXr^=9wwHyDL`)P`Y*Fr$%AG1Qff9pdc{bZ12{+wyAD zC&QMReUD4WjqDhmhauc>y!(!YIyqQx8+hFbEMPyX5>a2h=-C@vEeHJ9&%ff+>4dM& zXKXox3t4m=(d}9h_4PKQmo#t%G2!8(QU>>Hgg;tO001BWNkl(3cCGphG`e6)bnjgXpa7!4g`CtznAUc(~yVT zK$>p}qJC$6X}CC`Y?-RWs1VU{mPix0=*~PxT-SC_rhja^+Sl14gOXrjMu^;GSk)eJ ze$p*q2;2yY10$^x#W9hm?f?>sC|KomUa$v7-nFsHZfGNwWVi&4hAQ@=vf?OPbM_*@ z^S#Xo-81t{v9foz!E+kX*)~BO7{9*Wz)RH4v~&vZ16r6r3_O#3Gsj%3u_>Ep#e)x$bzm z?p-1Xm4k;6bgN0aAxry?4_`kcZ3%Q*5f|NtQJu`U;lMq-rwnmd$3pHPvpC3`+pc$# zb$4Iz0CnyK$({8uGox4#t{gneW^)j~&!izn;_wa#5 zq$EbB+-Uf|&Fgj;FTH0siuS_>0=4{LZBvUi*=%&>>%JidwzE{9U*0*G=O zri{u5pZ(p^QV$R%rcM>E!_X$@_Jbs-eXIF6{isHOTiKf&x(w@^I}~_VRtIZvzdq|b z?6Pmza>lJ>FcCg1CoF-n<&4*T*Jv>3$&SnjrQnhhPLUgfq;9i|z&7a`?~}x~{}xRY z0W1a?E6@hJS|WmhuC4_Tiw$jYoh>W84gmVgbj+`pVaP}%ZJ^cje%+u>MxQi|f}3{r z#^^p8Hi#ZHRxJV%K&)`a$^med@@9WF+i;@RuhpHFr{&PS8mABUF-7Ib(Umq^(rfeU zITLfzhD;VFnpH_M;9W+yFHOzxltM7o7&K^}Xi`SmCfDp%3{@W7%7(4vrqZj)47ZZ; zdEf9T5o=KWP8RLcd~-TGxc#=jNyvxgLASxQG_-oI*!@;4T1n`2hab#cB|i6 zJ`%Rb2$-cwdHZhK$6UkP%ORFD64*cm>!T7s^V_c5-&JXM$bxj;K};IUVe&{#E>SOB zSQ&XM;7EA<=7hK~e)*@@K4mwc4l(fbX~o-Tl>t3`T=4em4nXkdzxx6JSRMmD{hJ3o ze|y4yEk>Xfq+5mv;pyuIAu?WmzTumH`-rc;eZYVDKYna7?74X*Uxgp;-oMcFPO~JU zW5%TS;}UmSt9WC)FO*~~$UT8}te-vb27UW3N9r04lQDD&tV~xyrXt<@J<9!fkKqbT z)A_rEA(=O!qc@SLW|LMWfll{}0P7kMVnMJ%EA82!FpRjcHpsCn5>hG#@?eXj(nh%y zMj(@ArHr^ltfvLrmTW*XElrA>%n7wE5)O-Shdd^^FDjU`I;Xcwu_n*B^7iHWOaAa@ zJTUDrSv$_Sh8)`|+xBoa+1EaOI^*Tn8=lt%59feu(vWyzRe&sy3%>g15nq0O122{X zp2fp(yM$*7EG0&Z`~c~nm6Wkt@e~7rp^-T{K{MD)F6s?mA|wD*I>w$!DbQ>gpX+(W zcD-T9ou%1aIr`S-ArE1siq-@5aS8bFbi!*cc)jk3xnMc1AiL-4b0Z{o)wMLlDmd`O zq=&CXc7?QsX*g4;Z}rJfcb&H*kiuGO+($|Y47A7=ai*&oC{hQQAwYqC_Val8U^EyH zndeKSU|AM?czggw#+U1ck`tuZrmh-STa=3oBu*L#%>J39o`I_=k?vr;&-Weu`Lk8U zs}M?mMr16Wl^3AbKO$%pNiO3G(JMNl*T*MlBgHjC}U7sY$ zk?p?Yj)=&t>Ym5Wy(?0b#FxoT|NlS3FEo?H6)%^UcjwWMs?3Z4-1Wg7fCG`W9J95Z z>8`G>j0XUBKj(Ksf!Si@qQ6sH#&%5mn1=@Bd9$Cl4gc`+fHW1k7 zZN()e98&OpWW48$L`KMFCLG1Ug0JghkyuRlo)24VRq&ElEVSZF-myCvt!~1H6l_Fz zsABci_om`gQfrDIYX0A<4CIz!ZACT@X$qq;uX(CfCs0~1g!>>=@iZYqqf}jO&5o<> z;`ddgtD8F0CpP@44TkCj(q<4c_M2PeVkk#X zUU3dKq$hlc`7ozl{V8D@uRh^W(z6DTMvuD=(Tt-tYqtH6H&H6}xi*Mu4BbME?>XcB z?Hm5IZus-d3y3VD>s|^jiII2(R8XYC#@IdLA#46bY_?}x`H2$1KWAcY&mzGrU9AB* zdx}qXGkuE3Y8JeOALxm5e>hKsOqVB7seln_5I|>2DmQTAt z4lx?^=hYqppTeyd#v>7MP5Auh4YDh~{P!ECGk|{|f@a13otQ@A%`N?|=f^3uC_*Z&2viX&_7^_ZR}Iqi?Ux-2*Ts zno2-}bgFK=%;BLAHpcBpe42u+g@p%EBE+9axkY4AehXQ_~@ z_(EDZD^(K73raO|aPW<%{y8enDGi-I;ukC&>BbtOInbOzcWvORS0 zK(R~KRHR9MRDkW8kQO_cl{`ib;K}5d@HCdOEmnQ9(id<&F@y;6x?+7<@gvBZ5wML| z#AN3`{QQD+P5Ao94M(xG#r3)ZBG6)!_9kboZ4sI_Oei1>P6{oGi+4^<~9nNd`Yz z7y_Sg`li4x?Gz21N8$+(9h*?yQ`2H^NWp1A-ZNg-3vPS^Ds;xzj^fvP*$De#*^^ny zkj?mb98e~_UN`*n-@f5-99R+~7pK|!dzYGA%9=%5jOP2xpRRblF8J-sJMKrp;-3)! zF4x5+p@hqtpv3t4?G5*wA)?kCuo0#=q>^>~rFizNv~`9C$W4c9jBQ*VHU1wq;evlR z60RwB?P>Hrg^YDYjYdPu;8|0GG9&wEtH}Ot3+u|JI%T8kGbP1@1Gi?LuW=Ad;H|lc zy{F_WZi`~7@eS;gU}|0`(k|;NeGF{=!~};b*=I~nBtG2pNr8?mp|{XgDYgzLe?A^Z z#${b_-3+$!_SmuKjBQ!$I=LL4L-EC=14N~+x#HAVKegcVWohqa&D{QaKRT?+pY8Aa zfuF7$ZihJ~E12%GE;ytFY2MUGcD0gawOSz)_s{zN)@Py8RbQB}l1W$DCsiVL2D&Z_ zm=kVAuq-PSz(4)*y)_jQCCd!j;ao86L~~c$rW6dJd0Y|gsU{VZjh-{h)VJa zOrkV3^W3-Tjq?`WrsgMo9NFWL1lQFva1%3%7Az?trK-y65shb?Pj6@yDczIV=N=C6 z5%+tO%f&4Ms;KnX%t>*Ek4#2kD%a0glU3BSL4VpdEK9<-eQ%J)tm2b-#p|}=o->n!1tW-X<53g-0xp@wbD`5R_K1) zL$xVGvSI)!9$~`1xe7zn4*H_n6cIziaAILbFRMg&oSusmovnirP(~r$!68j4g5mtn zKeVPuXn=RsU+cE6x-PKxe^;{qmN<TpO0cs=&*S3-Uv7|8f}Vx5+TH`N;C2`2DZ%CM~%zwwHw4*DArXi69a1{^f!F zmI2B*9vPen|N8&>89)4d#jk(;j$i-3H)v7ZzS*?;^-mifZ+Y-!oxDX)hr2p-%53+F zbT~VhaSHyl0B>p!N0ku=Xjh+XP=ioW>IVFh6*;o*$28(odS0y8Pno-3XzK~jK4I8( zs#d8}_Xenu7vL&%CI{n5tS8P=lDYf9O#&O_Sz*#;9;I3*o3FCfoeHEip&SZ14tp87 zN2`#>7?f<&!)Y4xZ4FTbH6z!8)(cX2OY@HEwMfkusJ~6NwwN!ONg7oi4 zsyL1k4v>lrgG$ho?%7i@^r=G|n1uRV@bJI<`3L;<*Y8bQmUlsxf|MB7>)IMp`N)uL zJ~kBT5h*@dRD3{NZqG<@YH?RcR6p#{*`w(HvI#4=+S|;$i*d)EELbHM|z^9j`!RADc4w_&)3?6PHSs1uI>^lDO?Hzx7BkZ}j z44tqr;hqaP=O)p=`GL3;@dz@)x-M8ZQ|RPJM#AcbpN-^=EoM3-z+jaQ%$I~6F49f&nE*{Mer#bbx|X?M~DqmK(@ zda>s7{Xh}(z!sHuz;50>B5;H^1*HgugZ1l{a1_Os2-~{g*Y9^bo>7#cTH$&W39uNO zLKNu|(7{s=0&_hKTYOW?-Skm>a_{e+OFug(wR9`?i7*3Li7kiKCoMS_e7;=pPrrS^ z{jh?{AWNoz$Rf=@93hDs<@Bv-QxBCHqGt=qjIgI6vW7Y~spcxyHMvFmacC~qTvcn1 zEcLmWy`#<^Ag^Q($z#*CKKo83D_)rQeUG{v8x^z`vfW)4sg3J!PAi}(#-}AFgq|49 zjsP;5`!Clke!N_4x>^c$$w-v&wi~1j3cM@}9;M*@IPhs*TVXCP3tt$36lCquUWEi{ zNjN+fZ)LV?2w)e%LJ5N6?_%}Ocf}H^O%u5`i{pehL>?beS@uqMdt)g5XSjpf+T?;P1uI?(pp zGp-cIOc7kw-!*+QsZb3~o%x!aypxgppy3p#gKDESbz1^}4wpQ{qq_>EBOIhz?WpZ_ z(kAN;-t&U`kbVINGKntYyykvRAN_g@zk%)R<4ZuwkD5&a!1O(>s- zU=khbt-AI}y-rWgWlZ#UY^w7bNl!KKvG!R;`a}fH#2l^hB6KiH!plz^)+^(Oe|WJ@ z+K(Hy*A@GI;P!P#J_;a&_b(58|Jwt{Zqs5_#c}gaOCs~_IwhK5(Mv>x$M@`#J$uNm zKW<=V{FndqEB1Hq$g|O;myPX^{wNJBMD$$Z5r^h9_{6M;N{h)gsmi>oY&UW9)-}nT z7gyqow>)}O1Zjw_u$GL)^c0hBkQaEAM|1iB%{j+ePM_ri9sNh-V4Ya*H2R90d{t?f zlIpYSZi))}yZlDbG3ohG&_0?pUk3BbN5e?F3Z3Vrk;_hHVW}DXJQgGjGZ97V6c=*PYmefAm zwk}9%agYxhXecq`qmuRP-iz!8ed_H#N&D#|RNOO_iVi%==JV81+{`|aBcE*MMmmle zvtu(#gCw!3z=!B04SBmB>E;gBMlr1-6D{Vfq!cEcVXvk!od6Z0=3gi70VB41xo4NO z`r68@p=hPSBULB0b~rT_x*{cxZY_W2@0&3a8P}x5}IgAU0S~oV#0qagZ)j*EQRtJpE&5(*D8|NfpiYHem7|WQ3v#J!o$%K8 zG$TxWgU4H%$J7gAo${O89J2eDN1v+jY3Yj09y@8#N?qX@vXHB4V2a#~zSGDf zP|R@4ancwuJ<%7y+RX~N(dRLom1-9Gz?LT zla2=_qCVO6n^eQ+A&&6Y4pk{4F2fGfTOI)=Ja|%g$oVr8E*Sw>XBz6$^7Z3T?xzlB z8YjF1-4ytE37JC4Po7k?sa=c&IV=4Uzzp@9NjnfR07@Kkn9<12RAXOyWggJ%aYU}| zFfL-zA4n$Yw#^-lxcL;nK166xC$om`6j2EJQJtpp_HfK$EB&P&r#oXr$moZ(d{x4} z7x1#+$Iln&QE+=dn&c4}bD+;G!V$_7ea#g>7r!>|g4>?0kSf+qZXi2VgHL4#8L<@s zOx2%6gRfOJ5JJObl!8~aXZnuu7o-4-U}JLdLwJ8<1A=<=k9q3}m&<~;4<@;B3X36I zd)VinrHX`2p-KkHCp`AcP8o@|y8mEE2+BZZ|Z(Nc!KBbZ3ZDVF8{KMy0 zydN3g-tQnLOHmt;GHg~@7|JT+pq*mUzK1;2j3;pf*YK3z6^d+d0W(u!cX zQcMGDgDLp>?6vXYI!a^}x81y#VcWIm4&0v2y$F81Y#q9yB zdzWke@^x8oI7~7nC(A={FBzruGec`SQG9*%v=CE};?i{3ZkDaKPV{DuD7;wJ4s!zl zD!7+W;4$#)?FJFW&({lH*QHH)b8X>>`M+0(g-J25>wC#qz)+v`GpJcbSjnsQzX2`5@87RoJG^twyF{n2`V}J2`MNsDVATI z2-YT(W6)@b$Ml6u|o*2ljU>b}v6HHi5l)UfqSUZVX-x zNJKCR<#8|g^vj0JrxmwvJHTu?#GbKNDZ!kqcrm$qm!Ouz9_q9uy#BP}c$nwtxMhn{ zic+_TzneUNXIN7p?s-wIDk$RN&Q)S14j6ISXUo)u41g_tI++i&=oIMB1&zYVL_Q(M z8Qz8GQ4vFsHqqR>>BPH0)eJ!D_99XB=c-N;zJ6GqCpQtDe?Ht#=^|PW%)Lu#g_e1} znXMrZn*iz!i0X+TBJ7W12e{3mkBZCK>XbNA#rH=>aakLCas8AE7dl3RtW0C%=$|bkd*E;b~QEAW(4K&c2|Jl7d)ibU|TW1@C zch`L;DFGf+0y3I3K5Ps=gFS>S>>#%p8a)#`t8Gxl6ep;Fleu|T55A*XZF?h^kxjaM;wc_IoK z&OzE3u8$ONRQW`;f)m+a>METh&#X;s@T=nFt??ofzCIGRiGl^AXF6vU z$ZKVIvx5s*p-ORDi!s8sK*yl2U^B}xE5EUlN!Q)?SAa|>ALks<)BkRlaoejt-;Cc zi7KU^504@pE=2@R1~{_y>3l92_WSu*Hhu8(42}i}4xm_=2c=aa!sh3#qUHgw(%R(j z*ZZDPASgG*>$(~}p9o7b;NRibc5{MvBEnlfjHj^LvvPQmp)FU|;sjrqak%&Sv)d?k z1s>jjsL!joB7uUc%1=j@Xn`(+t!r~_IGJV9EHyn>nuf;0S=>Y;j^df@^x3jZqra&F ziX{5(E7v3pXa@w~#sikNES^$=nk73;kysC%S=BO*|-eG=7CU>59Yd z8pEXSRTMZtlMJ{If-u3y`p~2f3l;}awH)(scGOt(wzH-+-q@qFR%YC#U>Adk*7&JP z8~^46bEx97RO=HrG2Q>{pQ6z%eO5a&)8Co)J}H#x*-?z1Fg)}$=&KQ>qJ20vt90~S z%Ke?QAVP#2un^HC64OB%OhFb+u!7_9z2K*R^Ww#tcgPBG|8iKoz>jO26iHF*`oSj0 zh5LP7w8p4>%C-lHFip zKuh(0^}}H8sH^l?k^!WJ(OVd}V#AcDL4167s!^v@y~V1b``hy#F+`U{Z_fl9it;m! z2TmusQI}X!3dJY`CHKXo*CbF2Qvd)U07*naRQAuf!=pBPprE#xCvMu7+70I>9Y>=f zbF%0k^Q?6_sgjYu?K!iU2+4}|x*#PW@0G0DBczVKU|AdC#y)qlqWIWL1G`0ul}>)> z2n4J5iAiQhL-zsdC@`D-HKWo;hoJTe4*)=rk{$B?*blHOUQcP>rl93v!&CbT0xYa{8JaY{s<~ z1=G&}l?tie5Ok7qV;&TU-yU+6!62ubI>c?Ip(j)MXOz9U{v|vWLH940p6iG({q@sF z$))*2waL{+=U6LZDTIVn*pS1lG)&!kJdq6vS9Zr zNKQ=Ru@4w2_f-xos<`LkYeb&{s6(#mRFxb)R#b81f^A92kC6V^Vm9csI&zz&S<&-2 zGKvtc%Nhm;uHhhR#B(YZZ=(tnB3y}Z7mK)gFUA{Lz;)=hai1;M|-#!9TUH5i|`Ripb!}FN&tn5izN9RQ5DznliE~zC7-(5J-4w zFe`@+5~GkMQrt>G(bCpF4Km}pm#m%>{a)lch*|qJZQ2?4JICf>i!_Uj zKoDM;n;K%5{#lx3WNH8+KzvDK=@yD#r^Hv#W-Ghu*HizVOf&o`S_w{;~bj<7}tCXU|n+Q{L0)i#(%tQAQ0a3j^B^nR1VJRAy~8STei&c&tgiM*Tr(l zcjq4fAMcR~ky#Q7RGzJ2&rYJ40W{N1*Qc7r9^qxGi^>J4O`bM1^1JnN}h{xOs& zg#uu~tPm#cpoD>ZOCGOaH?r)-CV4(Bl`PTOzv8(B99u1%!n!0HA_gKePK^E`Q;GHz7A%o18`*iYYtSe!lsL}@~NP^|KCck&Y z=9^M2VHy!Sd_k%)OQDBG?L@cJfH02Bag~TiAVXW2wC5;GuM1jZz@h>=l&y#ZuGHl> zhgi3?FqVa|ZVQMCI5ARUJS024j_Q1JniXjyC{Li|Q93+(uSr`so`a-A2We)YMz`)r zeOY@!CO%!0Vv~(!y7+P9!X(dm-gxVBNs4}2|9KVyE=$6mtwB{MLEIuF`WleEfx+Kdv4)QvcB5?X7n#ldB)f^Fs1k(d-E{*g)7-abd!W1S!B>3JTTYgBtO*f#(JIrMqFT8J@eEKK>+}pSO;3P<7}IW|w};>!VAbJ3OmmoV?QGYo zd1Ji)et;Obye6!d#V(iwqRQc6>kGAzc=fL8vNA3&3*Nu(KIzj&+iieL9%V627AaBc z0PMHY@?krW&QwHwus5HiW8myafo7y;|TEibWnhOR_kU0YqtYZ)D9NXyMW6OPOD><$VuAt zEF-wf42QQv`$X?{WPHA?c;wQOs<=ijlqv}+z zwM^7{0xW()qA-c9-fwaUA19xscma6d3tof5;qM*zhIwjiPU#uw&?BLHj_4ZHApKpZ z>HdU_?`bSa6tp(sFlJ2@m$GSZ6tw^Ldy?JPc42mVK#u}jnDNNDO|Yupb4?3={(Qyz zZO8AoJ5p^;kT%&9b(2Xm zCp@E;fBetrcn8gxt1*xF@9VtOEe5s0<_CKx#Y@$4P7gtS7H#w8K~g=?e8*s4_hPgA zOm#RkMqhjNPm)IfYL$>z`n0RBggV@c!rpZ1!u$S|IN{~8ATeP-4oHPHYG+x9nC!2= z*xH&)HuY32QX`ABLHg?7nu{POgR$irSs}5>IYLbK(&@h@t(W$!)_ts?cI&B4&Mzt9 zo^yNVvIvq?@Dh4;?VBRCX>dPyhXfxhYJ9!TY7s>O%Lx|PnpcGIx-DQTP%>ZgT?;l& zcz86KRNx=Gw40)bzTOkI16^y>2!>?Awm*ad#n%u94MbPPEMrD%jw^J?${i%op_pz> z$>6=qQUI(i{iD(LwH}9T^7zxs760=6yD4zmrwim_ggB!b)kP;`*5u2_pisk1w^p55 ztvx!eFVe#rQ1h_4-lL#kDNSN;ur3i;quHZb#JLI?z^aUwW$~tr`tJ!gW+)PFIh)iv znH?*+#D4)}7r~Y*Ht2c(2o8IKwm~mmoTglkHn5z)5 zgi4gwFa)BpDH7~yJVf91RE5YjVDw>MT+{`Ko8{CI5m+kVHljFItav);R zj^hTXOQ7qNP67P%k5~Nok5_#E{oxX6X&zXW9-E{g(jdwt>UH4MyU4QZFly8%xknMe z0h%+sI(crrrdF7%Yk;*-e<@l~&V3rk+H!}ZbdsX9QLC4kZF1z1J=L?SYLOMy_Z0@e zr%i#IY^TQ$U}$idTzI`Oe)`83eE;Kt_isn*LWxIY)B}ckXFV}P>(&;gu}~VbQv;xb zaE200V(?ZxvvK1h1;;>n_>C70gCsht#Ny`$UO6PF&I1L~DFw!P! z+m^6Da*H^~IuUw;7m`i{+K=mr-o@-w?gmvN!u==?Q|fugxd01!G#aF35{gfQ8^lI^ zS_pv43>=uG{AJuFN`+?-khD%vv_%?Jawd6?EHbjWN}m5lCUkwNu-P= z+2=NY8aFR*h3rTxdg>J2o{_`lcjEbg96U)9C#&_HZhM(WH~7J$w8uWcMTlAiPV&aq zx-IQ_+m9Twcl{8p`uFnbah~Ij`RE9q+kgoTJsl12b+?Uho8(#(K}Gy$w!CmvlYkC{ z>DrJ&fhr@VdcXK2?TMX!gMcZPl`I$4BZu@E*)a6Bs+2J$>)44HYofmQr4+2|+NM~E zlfN4ne4wfYi$6c&-oSN9cR~LZllNm5?;3z3N_NS z_L!X}IRy_5Dhl^b2AQM#H(ioj8)uef+ok4IW1aSFe@|6ExhFR^z5AqDu@~vjm9Mj{ zzo~ZpYil>F;^&tOa#?W{<4+{tV;;q%+lQ2v=dCnO_pRYjJPyi0n)TG-$G2dQ$oEMW#$KH<}KfGM+Yz1H!lR01AzkigBEhV!m zQQ&5r{Ptn|(1_A8N+Ce#o~dq>PJ5@uCT?YtRA5l}N^TQy4n$@2dsb=7>{Nd(7-{F` zBd)7Hb^unz4pq#EaVoUO3@PIq+2#8CDR1 z$fJVPi*v;gmZ%v28-05C)3)J{#~p_%Uee-cMo+^~JxS|jXvAqFx7WQqPR=wyj>KVG z5;CQAvJl2Ty#_C{v@(Yp@UHs#ul`r+v|q0+8Z(W-wENE}qDr!e^6 zcNYvf!Q_Co&+D~G@9RIW>xTb*Ss+mSx9{KaH6IYN2A)E(6kCV>v{{P8B_+J=JGN!P zEgv4KWozCp(k0j-CmvydG4ja9kmM*piAfRE_FlnLK+s%0OnYQyIxr~kS~_b2n27ox zh`i%5ad~$5STD=qE+#fmh7`sAZU#|a*rdV??DvBGRz`0teH=#d{kI3!mt@gJSHk1F zc|6tqhFyA~fy!L7<}lFh@Dykj{a|#K#ZB^sL7M9YnnoWQ!oe!oeJPSnga%2`Frbea zh!n8?Eg!?11pYP@5zK?SQ4|%`@11!X7XYe0$*g$XviZLffog%{vXxe#Q-vAzAocD6 zqd?)K6NqN`Tzw=xGy&4WmLa+4E@`eZ9IjJgZ9d*s*!w^=`7>Dmr!J$ryewD}TL!HE z+PWnufc@cN*9&94*ynGiW^Jz6CdIYe7;yh>60U>CM61U&7Nw-Ed&(3Kmvkz)TIO%` zLTIO*)Gl$0BEd6re-D4B=l!^GMljpX!uy@tVa%j#a8LEZHQ$e1;z!Y@Wd7{>_p$ce zh+%UaSidHDu3mZ?4|Ea%CEBwD`}=IqrbW?I*{}JMMS-`(_*B&sI(0QqtI?FSt&<;5 zXF2tm1~NB*bnu^OAj*!1y!LE!o$~h#K6208wk=pdIPN==dl4%!y{a4PmYSw*i0a{| zO3VGQcx1sNmv#W1Z14R*O5LlCAP0+y*OAl+vLd8Hv{*3}x!OACq#cT&0q!+7a`luo z*vS+^BfWYwY=?u}(_3fKb+p1>`jmYrMkCojxQ7MZYMa z`gM!eDj~zx;Z+o*=*?fkN)<=3Mo>!BrgTtSH^kqQTmzn4YYG zA&39xai9j9#1gk%d7+R`+C#qL3+2 z(76XCL5G1+n&vmi$e<-Mtn@k4pNWz zjN9)=E1XnJP9{bAfPF&+6uTT4WYyh68x( z1}DP0CM+xC<*X{3BQjEjH^n}@tfCD<4HVxG(WUQ8;H z7~92;|NE`raXZWt{&0U}QAidnD`UfA5@{IGw8qm64hnz;X>8>P8}M zkS6_jsuvD))hH2~j=^n>8VeIN7)Rz$zWJ{{4x`a-ti}I?9+R96mY?wfljsocFt_PW_!ul(rPk6|5m#k?YS=y ze7k3Rh7_IX->d|E@~jv)`Dzn++NP!1MuZ_UMJL!5hB(2#SE?6WA_9R$JVg%~X@|3_ zua#>`t+>ek=h$PqxJgpoTj?I+jgy1Xj5?i3T2np|;a&<35&W1I{E!&8objb(Yid^i zp?^>TaQMMrFsRap;w`MCZGTjlnq^~#2;or#S;Rkw;wk-^I++-O%|XFK-R+VgPAM$S zC0=_Vv_|{)7N7KXeUqIbR^LAsS7lpJ9?+$|)8zus@SGUJCe z<5O91xXk{N5`Ne=T+)I+zrNznc*Q@zzv0)%1BVK3MZq|*GUMB0hd}Y?>kEFkT=8w+ zaX${cED1|WJ$qLtzkGy@4KS>r@{w_09y5{0$pirdNGrk$%afWWXd8q4#BZo#M9%J@wk}``2G8i$KB+Gk`?=2?D`QINm+oh_rQ$W z+0~)@r+xC+Jhy3T1$cJv^+`HW-UH zq9!#;iQWInZHlACrrYHxxWDHyUs<_5H+9o2xprwgXrlSwDFQpD|E4EYIUTna4H>2y zyHHaWHR-u06N%C4V-nS#=lK-tK7Y@XH$$H+0wXk~OOO$YPBf2Hox-sfR;5b52?M}j z7@$70XiO7u35AM)g8z36xo??%sv+)Ul*5t&+nR7)8G75tKG(AX_awWS=2@iC&N4M8 zwH0_WQEnRneNtf4lj$Z=^>w<(&aZV{9~UObQBV%c&0VW>R{O!eQq|d~CdL0fF?+UT z-$57dkvug@Mo&}IbN2YUB>eK@72n<-_s{r?pru3e1UcfNQ;7>Qjrb_bPgfAO{r%qfaX%lntIIPF^^GfZLwkSDt;pu|2^6 zMYX|FRSsy8J0T9Y-4`NyDyLtV@#(tYWn1y({eedjpaR@{BD5{Z=;uW&cbf=nV%(1m zRmGavC+mVr?3R|J+>4JoOF3zf~7XN9J0Br+$DpNXf;@E zb-4Z%owcM--14y*7h!{fqhzGQIC5$5ry8HOdTiRGSk83eLaJ!$DIO zPE-{kMSz8Sj`Bkbu3j``0GCAA#s1tCj2i^II%tqW%UqwCGbyNEwCy{8Ja*r+rA^Oz zPV7jB@B3#|8EC`$rFb;0&CoEtk`~valf_9JFf&RGiZ`^4W=u{qrslDJl;U3B?gh@O zrvw@uctj58qzJ6ibG9~m_Hk36g`DQvY$#PeJ-MM8{1D@iL!FT{&f2E5;0B_70@qYO zQ%w_V8q2!Dnwfk}uF+#R$#}b$0qFb7y5V1a`i%EuxBZ*VANFS?~46h#!< zOTtgTT=Dg{2MEA%2o|CcKT_de(l$e*ea$W2iYz9q|>Kl?VRsu9_ z*!4b!PGbmuX;ZkK?dva^OxbiNPXl4LsfKUc{~U)1CDo59o`G~mPiA`05%CdPVE?x+ z+@l2A1lRuVM5X`{t=(J-U^5D_4caKHbMZ^9{P2Rh0cmPCj-t^cG-XxFM!vvbrqH z$R#)1%}8{aH_b4Gig8gK-Yu7nrDVlQJ&u*)Qt1X(p(X`AkycNAYK)(u7T*%0l$_hGrPLb*yKy(^C!4B{4p27p#do z)foo)%Bm(=u5w)W)w3Ab?3xmG_q?mrSk12Ysf5~H3Kn8)i{<~;2*ptB7hVpM5Ppw4lIz_oYIrnqRi7DH=B?X7#prlWiKQ(MvnE z`lm@ZX^$47s;-#;S~cybM>Nuqet?kI+pdJtkaEgl4W&s1p{M-f(BOyK-i>JS8P{fo zd5o*tOUTyK#DtBL@i;gkd*soD7yPhYa7hX4o$>pA$G2R3?{&$7W1m9Zbr$F?Za4eHo!5AQ77MVs~sb`JRZKHXIPtmy?IU%B+<`erRwijYA12MnPCIKc+f+W zhUl=VMZZ$X;!#s{&QF?Un{@%JQv-@n9yI9Gx^SC9E{Pn-qeIRfO~>F?cT<1~_&n)>L zq|`#nXwOot!}jTi3$pvvMS7!VG(OJVAqvV%nkKaq*;|7=AauDaFif9#`#XGON{J8m zc44$b?zmu1OAS3!B}8wYK3$*?3Na9{rDXmaaX1C_(0J?O_QOYfPMG_Y3Za8K_y$`C zIp8v?_jQ0VYxi)4MYaRsdQl}JrU-DU+mUTb^NoPd2tEU;;r7qKSI?kJ0UR^QcZkNR z%dqH%-kp5({ezE3gTDsNyI+f6h8~AS2f2r6ko$U3p;Kf~Oa)k%ghd4p2y*FP&+er^ z$^`eo=%cMuN&#?tWZxM5&?W+|aUU_d2aE;4x~A@XnryhL7@%`>;%7u(;uwE)ItIpM z105SWBLhv_Bsr?8Eh0kt&(t77I`o`r48&;gD4x`te)h0pX&>YbQUaRlYk;Nx$9#2&Z+y4s? z*yPynaq;)Kh(L3oQ+orl=E)Y7L1K*H;a0*2dxQ1{3!T^g+B9!7tI0RlK%nXl;4T=SVMj5M)@2TR0{pqYQ(2{~j4WW@U0k&&3OCY$mm0KeVt zr@hexX=deHnLt7~rUfXfkwg?FDT<(hi;S?Fu}eWJ<_(4dE6mmv&mRv}mpxY>C}j=0*wy5E%^_^BR;% z_3s#4nCG{0Xn4j%h{3zA!A{Z7x8jBI;NhQSW5f@Ni-m2kN(C|U8- zFE9At{{2tLE@OXvU6EGC_iuMxt_!ZOEB?n{zu}+%>AQir7HU%ionmH4Xz(~yc#m?J zG=^?>+ce^#)+2F5fXCy&@z}zP+r1iX!b;RN0W?l@=p^i5U+8?H4!Lp~`7wcDiVyv# zAJCdryRRx{Z|YgDcpmBMc>#z1-FmNn#rHj9U5u&-LEA(@)nnJRD}J!Y;P@Gap7UB5 z3X%4A%D6}k9^m%9X^3rWo0V38mNpni$?Bdyt-9bq_0}gqrH^`-n7}bQNBi~XM8m`* z0$(CTE#y8E#m%ucz@)8*?C)5XjN`s{fJcR;cxGu-L&j@YCu&91U>CdqBv6mT5E1N0 z@#0q-AWJ3sBtw*g-|M34jS6bhkTxBt1-Y0Hj(99`2kPrIpTiXBf)9GWe-9L`Pv}|t zAq(?j?#XL z`p;2Ru@{rbi(4joo-C}%CV6yy2c4_+Jjs2;)K~!>G}w#V-hb5519(!zcjB!owYVk~ z;E&s*@ekIN@cFW0&jk&HaaX4|eo+CbAR{Z>W!Il8Ab zIkN!;-R6^3P%;LpvG|5%H1~Q939p;Q)D@?K(`@v3a>#2o(i$&ejp5>xtLw6O zCae9Nl`0Q4)(f|+-0>eCp6q^XuHZYR*}o@)J*+EfbdCiN(?AL$!4PxfLy+YEFJ4Hj z#JDUA5;Gp9;C**55QUnjptuazmT6Ey86flhZ ze4(BovNGeX6jKb;SQyyZxVQo!$~kEk=`=M|w<|S<3k7IekPT~Vc&KN4%TR1kQ0tU% zeJV&X`snM|OH(seOIZ)>yigPOHNaH$yGxj~N>D{rRY}oqZBiN|TroF3(V?1%MK6ao zic+(OG_W5c+d-$<@i=L(4bb90BMoQ#^BJK^o2S<;%-X2umV1YaNjt>fiz2g{bOL^~ zkL)lt*h;`k)|@+3ahHPklCd)32Ol7AiLoVys6C_ieBeL+_%lQW|M}}5_}hNRua5`* zkJnH5)AfqC+ckpj?M@OTpk769pLd9Xv~j`HABNG=16dAQ>#YRp5`0Dg)d zU>RadW>^c26j)Uh$PgHQ8WoZ}q2`6m;Ra;!>2K~GV53QDqY=kLU5~Qq@CmpF%OuCZ z{wP?tgqN2Uzy9}k?1wZozcvcs_+pcrT9^sj#)jw5f@MqCt_fd%zvFQ$xV>jAtHlc~ zTf*nhSLCDMho4{Y{`LSb3;y)y&-ml_2OhWLL~l$)*^#Ur>3Z-iGh!ViV9*h?L?P5Y zF*i>~VhY8Sb@!!svnEQpF-o$Ycg;)i0s1o}Bbjov8JzXu6o3kSdNs#pph@s_wy}Sd z;TJbYd`w?I@1^z(ErbD=p^@tlS8xGJeKB+d&5y8(aqKtxlXd9oX^dJ3o1TU-o3u8~J3 z>-9Wo(q^w|12OSTOdUa56mm0@NAsA1#XiGjGm2oYJPU7DN+3AvsdG-@fKg#*{aLIu z2Lms86R|}FcCgp$WkpH}MY2nFU7lX#6n{PDpX-<_5TtkU=f8WbRfH#1rI8Ut{T$%M zL-mgmlLLtCJTXiFVw0d=%X(1s2}mT-M$3T&U=C;^jACp&)#su{gb}#y-RjMZHQBUK z9yYaBsKqQ5%YA*6;>B=p1aX)e*GBebNmx_D;gK#2F*2J?pn{SzDURZngjU@2XL(za zHyi9u{lljhe7QaF<^E_d?@;8^+_39_k;o~~;As@+Y(NytVi76#qnMJ)@AH}%D&+hD zYl>+r5JF@i^|%`jDkE{MJbBYnw1X50WYeu-$ry7lc=G!jlOi@Et2Uw^xc6M3?9y1Z zhF4KVWYX5}D1MES^FekI%hyc{pv7`n)o9KLg--TZz|Eh9?C08*36G;7131Krfz`dI zh{ZtQ<*4?_yu{Q5n$GfJq8``2BJ!0fZKcC)K?zC z2Vap^V)Iy6FqUeQ0BJy$zhqM@ZXKMZIw}{Gnv!fG_Rl7+IP2NZv*=CRT4;V!YB6eE zb;C}EuF+B&a^D-=59pc4)L~k2{}Irs&Wjp53I_z{9$^$cZrW^58V-71oCp7bh9;~~ zoZyFZvZDFUvnoVOv#M=m>)HQ!+##y?^UEtftsCwoW+KbvmjDP$31Bwd%^7ajX zxqZj4`yKz`^G_~Y?~Qj2PF<>NY-eEi56!(foI-?>MA*Ph-rLu`|Jaq9m z`d5M>_YeF13dDlv#ByTrM!0T9hmFN0(PAx6;+1Sye;+j z++VC^YDEq>`R3m92&H+Zp^fSpoZhV)2Cg}e;ox6e@ddcNSai?hy|moNIsu?i?4-r> z5Vr-#VT9!UVUzl_GSZeHQWqw2`Jy!~v>mR^pT)_uOg)qM_AWzVcI?t9#ovB=;O)!b zuxyO=lJMI77D};Cm*RBvwkndRg#z*r6pYM%K* z7(_5iO1> z&+^;`e{wx`Dkya?tVo~gYn<+g9rS6MI8z9$uY^Z#u@dNN5XEG7(EmyC`^1Z>quRVE zA*F=F3r${(m{=JFMlt24N)Z~#0}6uVjLXXg-Lp-B#SU-_fZN033Lusnx-#Jq+w7D( z)U7>NE}K3|vBRvI-%{SiWq-D)e7-K&vp|IHfJ?zESXCv;&Ho^R1`9d-U1ZSE$v(VL zN#q1*-~5iffEMeHCiVte5%6lH(&`C{Q{rf0QOvPC{jTVQ)zm4%xqe;ZgmUCY5>+XE zjh5oij>mSmWrUdAf~$9wH}?trTEDw z-9sc2MJrCX5A9i7L$b=>D*2f`^E76hEzE7Go&8WFaqS#{T-q7|Sqhevz*Wxd*}AR3 zi@L?w=&isi*yL?t&l48KbzShwYeIRm*swp|@A&2QVqLjn|4T#;gR+RSl^J&_xGt;5 zP}RIkv$AYU!uQ9~8%0`W^ZlKs&=lv^Q~(YY{BXJWHKI|ANnuj1)c6YXnpPWdP13I} zZ8k%@$=jC&P;1;=I5o>yDRvc?WHL#er(YF_uK3}y;kVn(*D5{HuzQh+!Sh^1@U915*A-k8yVK2A z<~F>jYuKX{zh72DWdW{fL00uCy2+`q(8X%G?9JHw|?R(`hGtSmCrG_IC zAH-!pGdViIih6(UD>sR$TTCcS&Dom{mgO9$r*hPP7ipT7K#fA{(cpSKk+%j)^ox$O^? z^}zw|H*33=aQ2Ojkb2#itXWc=`d$@>j9{Ijx-RLdDON2QqZ6>I0QDl*Q+9 zX^U>9^Allz6r|NAuBsM)gy0)1N62Z_XI(1KRHOL8`&6&EH%V2^;#t2(vbfC+umw%U2@ldirve<<8l-s55)%9o_Vw;-!18?MTJ8akIi^&e~O#<8|p&e$y zp{H52N9pJ^!OgoME!~5nHKHSC?|A3D1>bu?KH3nd=RUh%y*5cw%W*tIsbUTt5rqkh z4su_rGHo)1yabK0E$~$?q2t(#fiaMHl$@S3JLyS4cweBZR888?5dD_DOWB}168Bmf z;73ErP5qEWK7mL~gToT?jLF=AZWIn$5n>T#0Nk#3o&3@O_)&ZW!@^U4m4*WALBVfR zx+k$h|5}za_Ww7m5EDAt^a`v#4yh1N)jky4RH zOZ^MZ($ziztWB#mRhbV5zaJW9ww`f`jYmP$z5Z4Dd~)XpiB)YhHx_|QJB8%b+Nv=^ z)pw>oMF(sW+`f(&dnvf(3})cx*H`?TCE+i>eL*RL-Raqf%YPFQE-5vca+PtutO@tS z@^T-@*j!Tl$k_@@PwlQY7YTNYs2NFQjoR$rdr6G9$KE8Ta%vRmbik(**W+AR*5_B+ z(4rRaF``d=e<@;~WjeVXM~IJ*c*K%#>n=-O{rkSIi>U(w<3haX3RW45~?PNr!n!kJlRMd6hR~*U5S_0_rLy!U@Sa_R?vjL8F80 zYQw9zBwIoud^l}7wZa#nh@S>U!Kzqqs)VPf0;hISAD9L5Z#-9X-(RdbH|3Ov0(+oH zj0s6$AQ$qwCxgybGrrsOR%=gvSerOBO`=PtG4p8Oq#8Ppq*^(d*4PTwg1mw{=mQc^aHMG!S`dw=76l4UHyH3VC97W@cBo4 zJ0AF+Gk)J6cu2uDH5WWKkF9F7@u1wC8*Ygr92FhL80=buz^UDbE(ag*(=-}!bfDRH z8FW2^*o3&l+)fOiaJLtgegLh~TFnY1tBgwNp4fWxm$g!OlVz(;5UIHV*+DkwMOe^_ zHp#M2DVM8#Ulqb}&p7T*iKHhd>p)9x2bmmeQJuPdW7j~Zu0Dm#-=P4EhYlKLdpyuI zr!GS?b7`a0l$e~BXqeeI`sj*d}Lk${TO@uVUD&nGxa zNbK=6>c9Xfjzh4D%jhZO)t<6+F)ZRkG+d;U*L$g*m{@!&U!=zmRH^e2!7+JwG!$_< zIj5opqM`9)z6VY6e8N+qv!0GR&r@$>k=};REeJ-3C>bJZ@nl)1LwwACjMlT+3p!Ig zNxR=i`o~h23J=^<9;b2i!~+<+Llj{k9|6!lFpW@n7A~rIL^#|Kj}ySAL-Qn@LSh~! z15!#0oF@QU3i6SgY`^w4O7P)`4nqsPysX%kg!|)YM~UjYWM6kJ%hL3+hY0pkI?P4G zwXE{|uy9e8OY!)bd+ug%KzWma)xK9E!r||>hV?>88l^EdF-Azp+>{u9CeVn;q0p#_ z62T_D4X=r)dR*LOQ4a96lu~=AH57V1)(R~{z^-V*%xEX^LMIv-i-=ECb(qrE>C?Bn zfnk36{L%>QZ~NW~LX$KnEDq*8-1{sL{Qmy11^@vs+ltrAhWn9mII>%y2GNx>lnyRRt)lPoM0@f<)|CG1b#j^po-|Jmd+;pErl5HWQ{RgT3xizbV=XH066 z2GwQ&T4TE^h_1}|mJhra!M9__mQuSnxhP(j1q#6}7ZlC-ylvRhE4KY%a%%UrXD6b= zxeTV7=@j+2mRB1~U4uD#*6X0v**OgI<`!BcTy1#aS$fB_rf@v1ahD3nplHBEfDSbT z5f+W6Jr`&*v-$lTbhT^=+m9&`ee_ zyQ+F_=gEo7Y!E$~KKZELKr3LVPse7!SO_84GA^(GKVxsVBT15E=^arubC1ZZs-Bwd zndL5avE&sm`Tj4#3xWU%5(G#B1ZQ`qyXz-2!re?&gcl;BDx&5!%bttruF8yXw;xp% zk#oM2WRMbM$g<>sk_=3~z%#>aGY<%R03#Q;%w%oU8VYg3#Wj$8tP*^a^FDMKI{TVKeJ$oNsN)88-5qmT;7G}};e0D7`gN&_rm2-N;2Us_lA~ZV0 z>oj~-<%%p2z#zFU!HYR_Hz&M~?YmUjk^+UC@NVWpqE#s%|2pF>V&g?F)nO!?Qiv>? zGHdf$cV&J@>mocMZ$~rG<32UAna5x-lmwU&N zO_ASWHhQ(kpACt+O3D!9i(+A&R4gH<+0!Xr^u?2Jady^*24`%%6l@;59TdMm4{WvI zZQIa}Q}1bz;vLe^PQtV4bF@`0iW(TL2_D}&e))djzE}MCw&SPwH+&uqA4jwIkjH&o z8}$@q{r>1a&W2~}Lk2BLrW({Wa;4C&OoG|8vnDRj>FH@|y3QKsGjgZxAsjigz>ADi zvT2cemwnRoW#93`?S{r3Uv9UNO2PKKya4n`qd5F4D5dnhDcC?bM%0)^OKsW|_;wt4 zSX2G2R(!I`VBLq>~jvPKOh zSjqqZAOJ~3K~yG%?^nnRN1FCg3Zgcr zt#hRlK8-=tL?^S}03hThJ_jXD3830o7x*NX!Pfp_KUE*m#;yX!P$h>B*r2r-83aH`VJDtgA!WjGQ!zscRe2VG03`kv|pkG+X`Ku0Dpcz z(VL)#MTi&!no)OcLKJ(Tg$1+`_N{7$vT%~_ZM%6?>`+u`m5TTT+N5?ULx&@*els$u z4TAK$i~~%lqa!3vmL=Dxcv41e!W!BrG_R-M9xXcN^`oM<$>XQuF^Ejc>x-io9ItuV`kvNGkj56F($HF;5;UHfd%R4x3EH6C zw@syECr=r!9h8P>q0DJ3b2w^8+3WJGrskaexjjTDMFGVM)$+g7TLW+#%AZ07VwBv= zxKS>=tEKRxECh*S{uhcM3h(340I?Cb&Ep%3P>B#FqwvcB-D!iRP)QaMSb;DPz7z1I zP?iRg60+iR;ZaNmnC#522$q)|Rth~I3XP+phhS)iVzoT3^Xx{u&VaDAR77>frRbDF z%12c61i++Vp+yF=0EhisljWAQaj z1Yh?ZTh*ylYm6V?ZaBMAp{EK##`h$fxcv0?hSnM1j}r&$HE+T=kB)E0fxmt{M%Jc3 zPjgKyvzZ1AqOf0>qjwzKAq@QfJZ6IpOD6CO*+ANNauAXmenyj63En&!J8TdE zKblIA33bxv;&HYi>8<46)P|#VY^CDSHD>ec?S?OQPHfDh=&fFWo61OSAkL>CNQV`d zy4F3LMjgSHygIkJ(n4i6d7@x0o!)>d0nk4>hE8{FuDP(Cc8oHR5 zT~JX9+f;f^(W~8%j`iovM&g+AQl6}?I3ThqwF8k7;%iw?=%PNE1BEp@CdQ!o+rJT| zGYE^QJUXY=PU%u=LgziSV8Ux#XjU@3VG$vdlpEWUV8h4(8DX4Y=a^OD7oXos1#g!9 zd@E(jt4ZHo2jjihie}G|6b$I&PPPY|B-oQHdMJ!bRB>5(R!HNCntPzB40{0E&vUh9 zj>4Qxhi;AaItbcx2qhq#qPfp`_AuyBr5N!%|5hsL zEaw&|2P9J58%0tMJI5Ji*n8so7A-w&F-%z%m6O%q=d6h2DGMu=%7F^ z-4YT)sllh>F(VuBw(q!o+3@|BC(fn?CT%lPBSEHVfrL{$NG`vVro1a}^m!8-kF!l- z#=)>UMG}Yx8TkVc*or8R6O9ouKob&k@w`a*oW4wuq)i*4bBZzx|7smjmvS-7pxScR zOx+VIeb&uDUrIlSj&2)U4jm>SYrhqoO|x&iO@T{MNKHTV<*Aj1{ET^acBIUei%i>H z28={IuquJ|Vl=NP?2}6;z?G!}@+L|6Ged$jU=DgHZBMg(lI_Bz4T2X9R8xpVZ7Kri7R+PtR-A5-V&yo2(dEpq%X5Q`XPdM zqL<|7i~Kiqb%iA1udb7}3A#z$I0-p=7IBjnI?TMf>BF-Hr{uyKFE*bzF(s2FYM~Ok zgmi_K%;H!eR8V*zfe>?(DD93kC-AGWl>TVu_(Ek8{%I*R+SUreqM#&^Ame@wFB{TW z2HM#mU52!AL`4NUH;ovail2UZ!a0e}EFbZ@}bQY_+$P%EL9DSxeQWF?mo z(@@uWF+8V{G>YbCb}u%m_`Oyzp=C01jdNyx+0cf`9q^ z#Ipl?skjx!5!7xXP7dgmojwUP5>2_xrdOq;8n&$>+e z!Dvpewvyl(^+{%Gz{lcOqDR@Juh!5gMdhoT{ZNm1l9X%`6o`RpEhNKNKK=PfH4TmLFRQ4_&eA{B4L{s=o&HiaZ@J*t=Mx8yBxUTS;D=K6{WMZ| zGxfq=l``G{PKL_=ldO>XTx5L72<-^AZ}!ZxxcfC^WgR&vt*{;vwt@Uh5!;2rnE+- zA*oE=qlC<0wseU!&zF=!J>p(d>z${ibmTHeFe|y{KoxcVxNIykX{iWBq|7`(_2cT# z+3s~rC;=r!Q}wB@nDkag6aj@p_?YgoA4+uMNkS+}oDcHTi1!TsSwGA1%&e%p{`7Xr zJbbSX_!ovMij!O0+$naRB^TioJW*JE!DEstxL6Yw+dw;b9DbAUw9m;gp`aFnK)8h3 zrehmH33u*W3a!508T+lEH6C6!2kw-+`j*<0lk`kpsA#^cPifTH_=D%eH_BoJ?QRh`rTE4+z3gnCvlKl513cw5fxeoK z+(Z5;qv$OGRyA^%Y4+q|;S6@4stid;_SP%{HKCk|o4HQud3}iBt*o`$0#sWe%t9{? zsf23}JNuBmSv49v6!P?VHX%HUE46+k1AWrwJ`QL!;3%Dq{s>8uK7V`jYg$;?|Rs* zR8TAMY&}qeuhCmwSiywVP2FV{EoHvzWzpiSmoPPo*5@+-$LF@Dl;M`{%>@F40zH&_=Ow7=;5g(N&oI%H~5R zrQt{&4%Q8q#~$qaUM(I<*P#2R`O81u_c1MHo8;D_)7n}J8aJeu zvk*4gM)CDH8g5&`8|@IVK^y1P>FgapzP;i1=O@lb$Cquxvv-wbS655u`q&0}8FPZ% z8NWQ9`1$<}Z)HP=7T|6jBbkjcG6u}-eUjxo&sZQLLPd<}$*?wv#;r{?iM?+W10{;d zTxYQt{iAN{h&{c*Y>7>_ED1=mehC@%7yFSl{5m?&=YXh0CX4OIE-@z#EP!#89;;VY zbVjCzWU%bT2JvFce%UOgyl?v-`f=NFuPXQ5OF?Jg6!qz!+|5d;UUq+X?^XZ%eN+1N z$9bT!O6}jb9bcL8(GEO&$J@49Dg&_DJ$SIH2^v&o;YwKdr=MX@{8Bf3^aeJMv_pj} zfFHIUe?5<&TC$0ASPnUOadmpH&(8PO@Lsn;g|0=@Ql6}ic$a|p(1_g%4WBlg;2wO{ zOtFbGGpB=qFtH7W3c%yoBumIky6f}@29A7R=dd-}-m*G3>UT?M+-)UHG5yIe7DFS= z{9B%NXou8qe|_Nmv?z&6c|o|Tyr_`r!Z9hGKDklCv})_A5V+}K>3rJnq>w?1*`*w^ z$!+Q&{^7s<0YCrSulT3`^EdqZPoHC=#3cD;7QFC{(RgAcwLCH;Cgv1EBy^jQNT373 zJ-TluC$`V-g&~8r`%2lzMOH|Hl-k-JDxrBRgwFb4-V5RMJjqbph!w-1AA)VSpxdI8 zd?CHYy$gEN{)(y0jfxy7yXJWy3SE*EUK?Lh_DN62@Ma5#jmg6BIH09Nz;aQQs-OV=v#(h4nF`!e^mg zkinFfwwBBoo-~2xY(0sDV-DZFSbp)xdE)PH?|2+aR^MvH54GUg8lJ|K*l5Zci$#yX z*dcqZ`2Kj}Pw#K|)B79#^8FLPKacS|7W3uFl6{RZRgO|aU+*%}Irv0r$%&7xqTttE z3HR~&_XY}4!M&K@kw?4QEEyf8`&3DW{D0L8cpmbIKC!t=vjVNL;QKM@vhJl9k}>JM zZ<}TlTQRA{gi5k|2kyHb;#=>Q#a(bSiE8T|_kF|Bnv(KM#iO_JIoNV)Yf*3Sz7=%V z%yY5(*-489{`|z>y}jYl8-6>q=(thAlX*6KX%=~6{!ZyLS)vE)0NFsJ1*yAFxyQR) zEMCqfsW#;;PQwn(tI>S+IEJAJaeu9{{5jC8FN(;;@-RvZg9#BbHQb+hk3Jkf`^5`B zcpwQXE4lxS7*`hSb3fn8E0Mu+wwy331VGbJdt|qbQkRQH*PSeh&@ z-=3e^j0>=jU0_mc73 zdEig?8$Qkx{ZtQdHUDlmOP||mJZD{mlTxw<_}x_P&iB7i9oJn}`gQO}S8}cZPnZ~$ z!91ZCa0&w9ple(v}cDr2zA%bbUp<87xh_;m>QQdhiu+hh?bj1JpBBRU(pWMV#!|~ z(!y%>$S04clN7`68fJU;?q@!@$nfwQ3g71bV>-4fQ!#%w2jew`(0YH--4`pYg+@QM zJG$9av-;)m7@&};r_oI`WXSED`M_lZp5L2xQLA=yxUtgqB+IC3Aw^7OL!^urZ)ASCL1wnk{rEh!^Y>oSPr?0G@c1~f-AX$6jTepPl_biN zPLVMANQXh03W)&HV0#DjV6B_p7Bb{M0hL2TUe{|cb_-c2W%e(hmdooPGlwJ%T6GAh z_J|TH(0pu6a2YKLV6xJq7is~EW@&O4F!zOJv3vnj9#FcbWIl1SJZ`xZP7^vLA%co> zxI6vVL0C^B+o)(rfV#={jy|7jNSItjZ^e>hC!(@t065Q<8V?)k0m$jBl;(%J=A5nL z{r!etfB(?Pqb~UVao~dpdnG*Y9rvwZ-z#orf#A>v`2N{6!_+>gf1lU@Wj0kuA^iNq zJO1Uj&mpJvEJAN2SXe~Jv;>~d6LsGq%;?gz6)pfb19y4hJD^H)4QQR$w4=P1BTDO# zd`AP0WDT|R>{djYG~73DLyn7Nk*u%jEE8rH_r_W)T5Gxnv*3BQQ7n7z?cf4n(qm`; zP`^O$Vu$}?G~i&D5lMyWt2i6y8~Eotdj~A0X0HX$qZyBZM#r71NA`r-`^e4CQ}~!( z;_vIAC}Ktt9I`nJ)W;b!!`=7hQra;Yi1x7{ zyUc|%29140``OL%NzPU@*RvMoUr2=2*`IAjmOg9>|9PDF>C4-g>TM-9$|SkFIGUda zeOyv=J;(p%s3;23=GknNOeIQ1Dh-&Q%i;?G&)x=EyD^oK-+QAnqM0fcbTB?z2jqc&^W`09XMFUAd##xr8m9D= zGSz9weG!=h7%JwR1#Ii!(dYW*lQz!|<{;g9tD9!7cTQ5~DbqJT3C(?&Uo@QPvI)vm zh{QHCWtT02jfF_Uvp2y^Q+oOWJUM0n*@hzP1a}FLh!p!|u^fXbUQ~AS5(XCY!~x*5 zDUp1)xF3Pwm*)e`ppthh{Jxzh?pwvt8Bb#aeD5btdv0!ZLm>sSv4~ENZExJDU@H~B ze12jpgnxMd3bEh)=nVyG8SrP}E)^%M9kQ4-|76BnQ7ZgK1yV51PYjv4!ls@ZQgPP* zeKOXI%Q(dRHC)T-4tE2r-f5!_EHhCc<@&i7oj949PS`2S{K|8wAifxM9pA2p? zAXfLUPGXi()KPf(VX+EcPj0t@`+LD>P8#u}7yY-te&Qeg!=Lc+?a)7B zMT6L17K?PCun?rfX?PiDKzNQe$y)SeHJl=UqRYZDK6eRl4w@S_jlxS?D^uXl=>`~npt@B&EMIlR!worVD6?rAo`)3Ip>9^3 zx!+w6)CzqWv{hs)q!6LEj^lX_$yt;DGYg&!JkO3=2)Df$&_@+Lr%Ra&sju7INRnVs zk*ceO@aN++nI7=#=Q$%uOclU{&;Zf}|LjeHA(p#K9MT_43Gr2{ctMh^ZoMxeIYws` zRe_R<1=8i^>9pd^cY_>W7Y7*D@bmlW1v)uoYGjT2J|kly{Oa>Y0F^h3VqvDmy0sCk z;GoEsa63lubat9WQ8PeODHRo@)(@Elu1CjcZM_!#i_b2u*0k0(S&kbI^-=OhVe%8> zPliHDUM<+OxiuNbGxlnDEdRi(T*CWZZ5kLS<&oT9Xytjtqya7Zj@Cz(^XUUc=xHhjj3K0Z*X;J#J7 z@7qwyY_$$K^Q~@Z+;Mb9Kef4ESl8wEeb-5u`6gS7HaBaW*pNjtNNG$t8G_H=@y{Qh z`1$RQFS6m=d8T_$ROT6muG%UJ$`KmzHZ!hgs0~9QSSd6#gdmp6;p?7z&$}!H>Cq%A zDh+qrL};L_fX2~CBuZ~!h7`(Lw~lk?m?~4YvT*?GrM!<2t2Ok}B+2L?3lx!rR=s6V zQ>`IK+2S?jx)Z4Ty`9*4(LJ(s?4^u%!Uq7)*73;=B!s=H{QkiW-`k15JRds6cXf)W zGGHq~B`AzUh(m3(@vjx;!r3IPPBrn zJB?zSMezGy9{6wm)6W4!GKUEq8FFDak4)UCoptl28f&8c&g1AhX>^|uD=`fY294?a~=;&rv= zjsL8&QHCoaMxRXDaVfgNxjYUO33!3LwKS0^O2oW~V;RLTb3TE3@yj+%=Q6Xgi@gKx zZTI(?7vIi9fVb5gL%`)E6WqGV6H#bd9)i~^W^*`L2j^AJBo5ZB@@Kxm$oX@`yX0A7kJA2rf&N8B7t;jxT^!?>`I_OOWqmyuvI;p z9^7p*+LehyJ5HOvxxC+_p_s+|x&^fewori)*LQg*Infm^VM@PbXxAg7?1qDY9IB2@Q*?hUN9Wf6#3dwW zm?YlHHr9HF$~9qaJh6JlCqD!DY#oo@@a=4Pa-S06;7@U78eCZ9vDcT@>@Uvn&{IiTTeFtT!RY5*nU5NXd;k3Bwr{pvr=BkoZPkLpmYR{zbu} zlp$Td?r7?S$+uUbq$r%PndM<4Tu)haD|R?5iJV5}YrKD{;5XCueyUckR`*v4w!H$< z(a%7+VHllO!NQy4b!{1k2O3n#9;!!lQ&`+{oS-Xsq8BP9qX^l{s zG{Sy%>~GufP1DT4NYmmmS&yLM|#;BVfFr_mXUVz)Q2eTp_2TLc+rs9S5SvPoCEHY$F@Ks34Mnz$shjcxNOoE_- zIxu2dC@e&e@L0WH!NW^5(JVHK!9Sj_$`VHz@|2y>Hh=hlsImW%7;Pp~??t)E1o49< zvPn3#sA!O>GtpJst}%j%UdZX=19#ecf48Q_!GZ??_t?C!|M%UXBi3&j5ffXfxZPC# z1Qn8h zH6EM;dI7qb(_BKf%15Z45>hGgGzgTgx8JiNiK9tM9{=286$kU-m8^2uo3yyCmViM; z*Y;-%F+5~$Od=f)u;6F{!2w@DJI`phqjw0?$T!#J(3N&d)7`C*eYTE?EACsx<5WU>HK}m(JmoNgj2w&dBBw3sbqFEBGv?LOXaKP_i?yKRQNWM^otP$( zHo~2JS|3|o#o%r*%h}0-COr^v^}YQ(=lkGNW+&-xr40FYla9tZB|Dh$IGcV(=sI== zg+w^J4>g`Y>$O&FA_~xA!EeWbpKiCI{EBDHvBpF=&!Xaq9UBUMp^R$FR)+{!l_BF4 zn?PoNB@e23c>YtAUYJrxg`d3imjp%6I{6QEbMW3{4ILF)j)dXeUr6z5v)d)m*}C*9 zdpfMI5y%&b#G#-rh`GkoGrg0lMi{_v$AKmqN92*Cn@wZCZku_?)w0=~Mjjdrjz(ds zDvPIL3XeLH2xin%8A_x1EY6GZRtoC2kEkWLCitX&1MprnyZjs}3z6L$lTTdfq3>&r zGv=C~v>E9%0}iHQZiPV&+m@ZlB}8E2$q8V&hYwC>*_?O{Goa*g#GnB5uE zCn0;uhcPTOb9_rN41>O4i?mO~!c)dwaQjjrB6xn+O_NVfxsA07I?aRGP(Kxu%(V#C zQQzfxuz4T#z)cQ_&m1WX|ASFcl+O3JpPTJ|mpgFv9 z*<%o~-@{;!+#QKsZ;G9)MfL`pI;KSwz12=3!)i zE9gzY&7_TIx2_WEI? zEGWDr^3R--nlNMf(1yg+p80AqSYrso5Gvz7MoDiZsg?ky;xt7MIld+#eOmD&Y{f9; zKDA$_51A7p*8mL)c6e_nJS)BN$OXnCUzJb@jcHtojb{B>on;hS*{7U~>sPAo*sPNf z@$-P-jjpj!Pn-T0QtvI1;PL3XafQVh<5``%k&{~YTJd(@04n(SJSVA~Wy1W#3O$OG z%SpZW$5b;E$>{ns&$19w7N}3l_FB>VX+N`!SgK-^$=+f-4dpDp_?#r)>{K({YQfQX zPWY|hC~i5CsmvHO*(_9sA1}p>xCdz|U@1g^yacql>O?|0##JyNl>Qn^4LN+G;wpPgtW!R=o(a1eC%AYwXDvABJQx^9s76 zuo}?xGP`1^fR>&UU9D*-nWA4Q7yrv*TETl zB-3Y_@|@#dsw+W0>xA#_mVlVg3<=l8z<5ms<0I~9`h97vdCHV9^^nWlZ5?KBWY}4x z7Q$94o=v$4TcH8)8t<*;tRF?a+mF`p&k%XX*2S)?_PvapwU=o*W&15 z9`E=vNw5tJqH=sU7KPGChmff7pma}$VUZ#gS(_ZqCe!Qj)tiugY&5yKD0CGIC$5f5 z<80@c zH7()Unrm)ku9WcpH4*G3?uR)>(%MEdA<#~Pp@c%Hn;q{?YqBy*|mr)xa6)P;ddyf#w=(uiSVC96O)USIUY1G zQmerR$j}2foBM=E67o*wL{fnJt>V5Djz^9Ya)^e|3Jb6WwIe$zB$(GjLFkg|S_xDt zdhY`wvhM}Q$)lL21oWBGUdA%XkkqFfgF{(10m^5pX?N)IZOs4e?yl;VcYQVIMoRdPPDrE&i37!<0 zvLBz=d4q+6e=Xu}nPO36FMXMlY%fp>sT!c!=lBHSJsx5+vP2=Ey4LN$aLdU5QKG(>m+}0&}5oEG%p5D#3Vzq(O{iV^+x)%NTxMd=;F1LMKUJ~i_p6^teWPRNmye- z8Zf(*piQpWk>K$oJx0?j(r%Wb&Jh8!Du#n+mU5XGr7bR~aCzLI^$PG%S%OXieHsm) z)=2`4__{d=!?eazT^mk5*)0LEl7>bh_E6UCSh5{r+8%<>Y`l9iGEJizA{X#3!^5IL z@algOs3|mW(*#^X^G19voKU;s%}ir!xc9PIW_jRNO)0glBg4m6B>8zI(%&$Bha(k1 zLQGt^4t`^ZfG^vIb{t^Q3Fq05+<;%EeX=krF3%RtB0DOiE)kNW8AHp@lJJboP{GbJ zB5}&4@eiZ#h6by>r^5&B5^B+=JkyjcyFW5|{i9U2^$tkU{9%i6c{ZKoZX+&n+G)h^ zO(_35+nVO|aFH-QpK)C?W`|O)w&Xs4QkL>UUtdYJX)_*-2^!~%GGdryOuPsg95j0K zJc-pA1(|s!gHsV%3PfR#nhGnr%6Dc%;6+Z6dprp*8rrxB5?-jdLP59MSc1~(Wqx`p z#03X;Y*dFXikqH?z3SP`Ua%Dcps5!wk$wu}t!~yt;t^x#LpZ zrH_{qctbM4bR{&2G``4yti}>>+ryh#t)6yRr~?Ly9Ay{j+r$|XiIMqO}5jHD7VwMtP#q^fkTz3MTQG!))o^2)r zWdp6{B0!tc@%(a4KZtJp?E3YM@b&NN@W7Qq*zSaWXjh{Up=>%OX>A_P#TPr%zjGH* z6_gDDC_^Yk=2X)_IUYpqu|mK7eERdNnvt{^$>0K9V?n~3{K0B7nGNDaD+I*3dk14wZP?V$`oScoEC-a7^GtdR0_nFBHqES@G zn60$KESVubVH@4Y#rHit2>}ptuY}e*dh0lkZh5Qv+$UmTJjEAeyFii%THKLQ)2R^p zsk5H_R&l#+=DpX>)OmLEl&KF>y8g3y*p;D*AXAe{8_8EN4jgVoV_NLctSfoG6n^EQs0+JkIY3g%EbiNZg7f8lHk`DxyUgcLZTA{%Sc3- z+0qeQbSdU2rzWDT3qdD0?;dl`i;oBhA3J z-@_7S0+HT2?DECbHGF-3IX|Epabtw_GQk*qkqIk>znWKU^_|WGX+Ida5x&0fxNQ}` ze;ha)>(5)n8BwMhn+!EjCLPiivxPO+T{rn=xu;_CQr)o2av-GS8_U`2$oGG3p82yy z$W!7#eN0kx_uG?Po2B;PkO6T-rn*3)R1DSu2p3CpclGyH%5ptflwMSWif2{-$MrEr zc|_|j3+i66ZxepfI&0I=8zAl*Bvn(1#Di>(e)Fn&%I}t4M3%_$Xa|bZU|PJ7h$#aR zu5)d4j6}i^@0a#7nP8xjCQNgUA}X`-DwG;;lO#Y-KiCT@lb}%FpYt zHwCqdeHgYQS+R97#@uk=T_8;RWi$vV`Nk zEfh1CDdCHz6d^`-tyEzdl1PbE*--#-K#sqtNcJJy^G-x#LS3y;=%djl=65!=7Nu~( zkcllgSf$!J?NfjDp)Z2PP}w0p#@7*nq>MG(J=;AHZ#ScKBP3zaa=(}YTMFC71XzTOgvB)_0tV>vD>4o%|VDxKRCb#np zEQ=!R>hfHrt&9JYmP05>Y7`fm>X3A1rHJ>Nvip+-OnqF}2Eqm{c)cKMqSgm7@^~eO zPnAS;A97%t;I^~#mnlq3hKr{A*B~Ll@{ie)4Ybw zmO}=9|LYU~_#Y(|W0in5I^anD)F=%Ux>o<>#SFV2$|hcgrbj6_W{9v9pNok({9dAb z)-7RkM45$tfBzr9;(YcgQK*vqSt6Mhn!0#o_uMCyVZtld6UZWcn9!lR6_j&QOA8UC7%A0> zb07$rEZU`tQR-xq#!_{vECyaf39?~f5Eso(?TqKMqkTK9(PNT$*3Ct8He)lC7E(MCWE%#5ZFmGtjX0k&HWO@}EVc3tP_M#^0H z2bpzDq1ITPfv$V{&&mSzniHSrv*BzVU*C4T-#7gDrQrMH#BugX&y5>S;2pSkKdpOy z0QM%K<^6V<761if@sJNDyX#WWlw}ez|LmG6C}{v80sMmiEfSZ{*aJ~wq{!l}^-gjb zS(qVn)gtORjy%0EOSJF&fg~|SXy$%LrN|2&Ou@< z3jsUGH6?iTahy>3W5I9{rcip>*PtjFK2b?Z9a$=5ktNqSO`u*MXo(~g z@-v((Qjoo&;d}j~^>Hsp{8%`~_0bR#G!itn%yPToTO|VxWt+k9_S3Y2y;p@LwV;Z5 zbfpHb0Yak)iWTW1<&-cr)@GFCT~3g!78#I*k;^)tdzqFHqRZ$k?$u+xjzgI^Ty=$F zvOZ*)aT<7#OJ-#bchZr!Bw*bIc@W8tFZBtRbgfK<4$)IyX!6oXT(3U`opf2ZXPk^zQWU+#9GR0_of`eg#NHc4I8MJ4RRZXpcOd;`chzXwZK@GyW4 zizhQ8^|=9M)6;)D8T(FIkl#P6bgym& ztqHchpza0dK{y|*vg%G+c*)ZBfq>X1l{{M=kKa%H^`E}s_rE^oVpLT!h!wsO;wb!p zPBvB+@R+_xNA@EUB`?f;3F9;(NQf>Pku2G8Ic$_=D9mg6$5f7J8>fX?*W&(38h%li zuoGQlYe1yXp6^PG{r1Z-H-V-vL+ikHvm&w(j@I$-|Krbi`*O$s{y+aS&PTJN%}rW% z(1{Nz(WRqDHdY-(bHTM(PA$HYWI+8CJRfazm$(a_A1Ah5u^PvtW7~{89psD>pQ^)h@Ly9#K_3FE4^Lv$|pt)wA z=O%slt-L@w+5N54uNHHA@#5A*Kk_Ip*sKZ-?moIk!9<@JT^f3eWNAhq4UbqX7Gx?& zVa4k6?D!z1`|TF5^f;PEM-kz6E1;9{czzlnM*r@&CxG9NbuY%zd+5%pl-C8uCXGUd`|3vmZQhN?bkUY7S+ zU=_?F2E(_-2|$<$F_P>g#mJ|+wnw6Bm_$)f`HUhhG$}%1U7~-90%VY*=ih%fg=P&6 zBuAX`r|ER~45`pZu0JJ62t@>6?mJ2?BNtU{TDFl`hBA$RSIw_1Hmy)!C~)5u$a0$U zqlY*+S|;k3^;@!DP1Z>s8{eY8IMl}55 z6sgp5rJPPiMDDu{;^-<(Ewc(NwgyX}>KD(P<$01|nGInHhv->6U;W8$UGV9#y^Z5q zc_hfC8M_u3f+dP37fO_(V-|0_WCg;j!SF1AEOI5uymFMK1b9t{J&-VNOaP&}A2Nb% z$RKZ(LW9-!Lz#QE+c_NW)4lN}G$j1~+`F{1P#J195hYN^SQUpQ1%P_6*KufqN5zv| zfKO3U{YvSwD4#D-QIUn4LBSRkY5<+NXd2?vAVac?nQGEOFDO3gzWq8u z?!MDZl;_a!^JTA!7PoI!D6%~p2L$(GRxF+k-S53wv09WOZCTgNzUyw+;jn+luGSL9 z%|Ml(tY^DhDY%!4-}?z}2X3X}Yuz9q>{LfWK}U#Ia@8PB?u_pT)sh)JTfMk~h-S7s zg>|}w-h#sCLiTf5-~GBtxNluLPM)BwYMqN{&2dk^E*k|59{=7gkDEQCUDys{CL=Fo zKs_zWTj#OY7n;vu2?L$&k_l)91+2Iti5s(IpMkxd?9&j|gF!oI*SI%9-ECU46|@t0 ze)IqtF>uzVCvEN~6Np(;6maX9R8AMI|Ly<%j`QGg!0N{gR$dM9f}9Ho0_rwBrZUd) zeiWcgFLpv=!9`v)Xv9$lO4t6E7Ds}a+Cy{NXbV>FcA&+aKK9V5r*3f%?%i|BvCJr^ zx>to7L6j{0b+f6wc3xZ4gSQ;o`sx<3^7V&1{{HX3;J^PL{t^Gn|M;JcqzsFnqA}$u zHra&d+H#injLFXnFj2IWq)AM%$RHt5DcCpL*f%YEwyolPHq=^hyH_L1@?1FCrlgp% zaJ>d5sVBnQmtC_>S>u+Tj|OJe_$^G$`7GXLc*(TjV|;!G&b?r_si!LvMnJng-?eJP zhe*llzPXg53;VX~RG!G>H^s>2Y&o}Hgzd!^>IqsxIQT_byNEVTIy4iO-79Qwz{x7H zdGjn@nVs(n(&AVoK)e=cndE8g>J^eW6-d-u3Xg%{rDj2bS1n&vYcbN+#_BfjTUB8U1jTq05l@ig_8CEkc7d6S!G+tsm%VNJgj6 zwc7{V$Z5V2C=(;Nyjqln1bVe8MoRXjELy4f5*Px5?oL6T9CA%1!h0f&kHWo`RJbKk zsVo!hG3mQjaFM*L68PG@Dtc`YfVTDg%VY?A?;4RX&-Y4cGCxIRj4}5;&|t;XQwWd&3Hz zjq3#^vh*iX;-^x4S+F_CYX+_?Pp*-_9pCD!7+|N(&2vu0X>&U{^fuX%_)?_1VvwP^LAL1jVQNW%`0O z-i&9K_R_GCK(+TfT1!a{&l~}XK#ArNp$5yh_ungV;}2XBklIJ}yg!>N(FP!)QN69> z@+qywm<&rWd83l4!}v z(CL%CQbrd+7@3Cp?v~^7V2e{!XahV(OAJ9CLB#K`4Nxi`G zE@CNDPEgEq^yA;{czfUQ@%_YB3%-2a!Chl{)Q`%z-E|`P@p0ng`-$UZRQC$n0ZT;i z`MnSIUpv$%c7Lzt`|Xz3M|k_8;`!OpI^(xrKfx^c{5UnnL>yj5ll)7;D=LyF2xvDSY@tn{9>c5DWLZ!|Ymt&tDpb@il9u_0v?Mvyy(0OcMM&8;DqYCvA~xg>kPe=2V@+&yAW0+2<3_t%490rT=+*+pzBi-#;79qmSb;8?ias z4=&|y2~=AN)2H|a?fow+T)_@yT@<`FsllPc9V2J##U)s=V+E>8r-0+=h#9v2b8fc{ z2PRea)v2`>QhzZi;Eh?=^VWNi22PMrjObvR)9o=mjoBE%mzl`S?%xU{b0JQOoS|x3 z6ahmT#SvPDlnWWRIiYKUsdukSfmtase2XMQ-;#wR94OrXSzHm5ArP}MgGIW%Mvtsn z*OXjX4#r67tREMNYH4*yv(YY95X0l634v ztb4CS9Q>CF!z{8$9T!1~dH=Se5AlWgly zOh2d8nHgk_#t@enL$_qAC1_Fh#HM7D{swuvTq1p#g$Q5(03ZNKL_t)iN^_?nlkQG5+3+up>2?UX-JD-909Vmb}#ePqORe00VA#sD#5(1sPjT3qP zu4utxL0_~d3ruTH-@3ma;j1}tZl{8uZZ~{CPdudCL^PgXXiocA=2oyvAhj^`|3A&6 z&OsK@rC)eC-WaIls3vX3Zd8r_tbg(p&BAuJ#-le?kN`MEGr#Yp;0{6~#_z4+**mHg z%4AH9W@~?q4e6y4A%ae$$=4Jf4YFG3MXvHxmyE~25^_hpp84{NM=pW0mF8mNmGOR# z$!&Uv=U*4O?m3K{9zqCqGQpUFI|PeEK|k#?`p36bV@(K#7n%`BbMcZSIiirs=~IE* zJJ9x`(vv3G?^O?1hoGM(|FADt?14QGld|?K_rS83d5p-qDN*q#i87)z>ZTIdxxwdT z^$_n-Ns_%6KBh2y8L=2I(p%}pv*QJjH`OHQ@tkDIviN!^JKstzmUTBt z+5T*?=yUbMk8fakqWOf}b~L73%dG~a2ysb^{J8%-HfafPpCLoD4W(08Pbt}bd!;R` zgsl>&?x_15Ig|m~YGZm=i+XJ3Vxh@;N0B)pIHjj%XO|qog!DagGbDLEm;7c`gJ@1F z1sF?aBREuXrOnFB%N=KsGWQ^VuPFV-`QKzhS@MA@lo&jHwu2!V8ar5|$H+Uz;1BqS zM4}YidA`xN`~u!4@}e&hnuPRnE*vPy)_@+9LwkXWsR73I8|n^^aH4Ve@n@v7T%swm zW>)?Xj)fpa%nkXOi|n%FNVxWi=!mY7)FGSjFQGun#c)xM`%c*}DX-v75mtpzFTtuZN=;<(&PydO~xSXu5h?4L7U@NU?lV0vlpdX!`aCJj7HAwoB~zWQk^ zPG%i4*yr!U78x}6S4qz=B{fbYZVx+)Uv<*wVJn2UQn8nUFZ%}S+E`$Sk(sBN08mB1 z*S#jSgnL_&6mI%m4q^T7y^UihOG0>zYb-X5x~muyhwafEJkw9+k*YGDADZvOPK2Lt zckHFA74JCktu>u06XE8ppk!g2jk+;c3D7VyTU8k`Wun({iFWNWW6yd7`S!``;Xp`? z!vG5CXIhQi;WNfeZWds-3Grxr+j`)3h{kL#6bgHatkjrsmX9bzmq&ZgevAH&NF^k5 zm5+tT$zh4wXD?NUQl8nSQEwIO(K<$$?F}Q6hUz>rbprTMf0m4p?6`r-ashoxYdOUX z(5(y!cHMyXv_9}q4w^~)ptS3HxOl5F+zZb`~|6dnx;{5ofb#= zFPY?=55fQTKmALZyZ}fYsEa(w+JmaT;3+ZmqMikO2yje@Qp}03N zF7xH57;O@KXve{LJQ}zwKC;@%DJ?~ggv%)N zWYVRyROSS;Zu;HNr~SHW(6TN;)*dEVq<@A&4319=yy+o`poc6?i-(sU4yBhq-2zLI zW#b;*?=oR$B+HaHH^v7MG94FQWOAnEaVRlmfQCjs?hCvVR&PIa3NF!H2*KxP!`B~_ zLEsuTGI#|oxvsIZY`Qt~mHGE%#G@HP@nK6;konH5%-zY0^asL11z_E7n;l+FUM;}B z>EvF*#O}6NKngydrxMJKOiWQ8^g^MLirS#i$fS~2<3PzUKCFlE8Erx#V&|%o$k#<; zUTECPX*N?z@C1$nV(=+Z2xyv99$YhkhZp#z)y)XC7dD+dRB#V z7DkckOc&qs@W2zL=OjuR(~K%~?Mi?TH4&V|>3J9_Abw5R;^ORCWywIMZh7zGz#jLD zdL!99pzeq5ejW%?^R@`dEXKQ%$mK!=kc;74D>xR9QkrC|g z9Tl^TxToJewj%iCmL{u2@m-tMuom7Uq|Ks}^ml8vFw`3xEIJOBy48Xo>W;k>JbObS z!q4|Rez@)U)e7N*8GBLwg_U7r3O@#fA_FklMJeg0bX1kJX%n;<)Jmp@en75hzW+Ow z(bxcmRERh zXg()d8yhuOLiR5D%wB}47(51W9F{l(E&L$QeGP1%8_$dfnek6^Vq|~UJEomNA!$WX zS0I|gwXE>rKDz#eXuOjM5Ug~23AvsKzSfb8OQG{fF>4z*PsZot93D!5LkE}A zpfbu061?TH2AyL|KKmu`md#s-Rv#h`_<>k0+#q=q$!0 zWhdr(ZZYZEBw#CqCVI$gKIP!NrP6XJC%h=glgJsMWfmWa&m!~iKBn}NeXTG278wK< zE=2FS0rN86Xc<)x?_>HbFVrV?u$Cd*M2b0iWqWd2-lW^R54#MXTx^0cM`hU>5hK`& zf^S@E+n5cQhcqo2Q_DqSTatGe!_ywyC+t^%W28Yambndh6aNf2UwOrBkdaGGslKCqTm?Gs}x%^X=ou(Glk zkQ=0z$a*WBBIM4LLS862+hVhy)IAz#d(-0baaP8uhHqONI4NeL#YDs+ zjzoc}`qy-WYr<&VBFAKcqPi(Z_M(kBaUZbIkWa_vOdlE5fv6q_W*(i0Vi2tP9=Q_j zbRV_2_t@ieX$&{q%0RM6NloGAo*Ob~TC7VV;BkwN3qkItPKuispamd?oH3|ggQWXu zF%ea7R&Vp2ZwF()mk<_i4+sZ%0IyZlTwzj?9(SGMUGUj5Z)eB07yS6=H~jSHcl`Fh z{xSefKA}#MD3kO0bUO)MS%GGG&Rap}+2tXVfQ$p2%n6cT7dVA4 z%KligH)v3`;b%>) z(0ZI+CmgiMTLN?%!~F_flQ1NURT?EBk>Z7*Q%LAEA&Dsd5_Qn=QXOZz3?b9nLm zuuefSlAuac4lFc*V%!x{R|>H2MSX=Vvmp}>hP^Rpuh?sutbon~(iD{=B|x)coZIlq z(hC<>n07`8^K3B8qn47M`<%uHWekN$ZuEr5>6$jy<|Yo*y}CdzNt&~=4kkj0`%I)hCH?TRCb#U+Qi ztV!&Pd6y?sL6!!#9%_?(+M~@ROP#OJ_iUEZCH*0@L9#qp(mq4ZNAN~*f*%DbbGKM3 ziVe2_#T$`Hy4Lxlrqpbo?od#R$Y|J&2{zAz^(Z_>5krYS?rSE6d&we;0c2@@#dVYN z6djiaBL=`}m54_Aa|;cXCIq5B?&pFMxcFKJkjm)TT>Sl+k#2GDGo33khAw{G9`t2v z%6B&3HB{}vX-=jUwBIKs7O9*2S4p{xk=P?PfA}w-$W1rdBUz+-k)J!eKzA z)Z!y8K0WS8ELv!#Y&Tb7SONL1HQY+U58IC4d&3!&k&%?|O~K*~bGsHl?K|$Z;?XsB zkce=cn$YrIH#}K6H#(gKj;^2mWX5}~_+j7i{XFrs;ZLQ4W|2vEs^%%S#y)7+CWy3n zSM@_tyH+Ta3+1d{cAvFf=Za#(HW?8_qX>P`l$XV)E&*~^$l|1eT2_R?Jk=duU`slW z1w}++hge+^;u%R~0CA%qIIgu@Z>xri$7HTIo^-}81FFIe6M*9jg-rjM7GyfvD$pFY zx)Y=kr1LPBh-*)Rgh~Vn!ZO}!bN5d=%?uP&Te|?(^QWuN) zq2ZOI5(~|6xS&KI!VH6@)WwlC_*&-SUl@3NDyi8QGvS3Wx`w&S%n?n?xbP@0OYosh zP?9PYPz2jv(K}}t4b84mBzL>Saz-Ld&+0(V=U}~YZ;Qn#cwVK$1*p<^X?r|;2M;NQ z%|L>JVq{H=9tfC#w=X+BK8}=*E=VaJOA(PbC?NQ|uJ-pl!C4N#v}Bn2!GYHv?+aD} zCCPl0b;@XvjH^MClTE^5$`vaVGL40l{(ZaG@y`;*rCiGN(8QX*s3enkoJdR%j95;g z1XzsP5R~Mq>rt35hQ6Ca-Ok2V2$Ol=tszEp^4;x_e7|q{KBG_Micly%bdwo}I+_ym z`2OYlxfGj1FU6kM4Kmd=K%Ax5%K`oSgdx()8xta1ZE^`@a zrs=65Nk84+L_$}4z><}Cjl)}PJTdY^mb}kZH6cL;9`xKRv~u`|8FOv6!lM?#zE!l| z$F=HyFe>qoj4Pj=aG6$3I<^cymMl~9D3y;B`BHohq9Dg!V0WwKBIZ%snlvJeY4zdH z<&KwvUV@q=v#%#fH=l+={;o`q?;I3_rilHHZtE*B0dFqKt6qtZ?#^PMUTGKE>nzYy=KTzc>hQ0qlrMFew4$I2~Tud?8Gv z{Fl`BR39R{yfWOM=-HlcQfOYpT-dcqYlr$86c&t0k#5+C8R?IPt8*46wcO|Gpa`^6OLX5_QgUEtag@Xcx zoos$#2GMFn(uPg2pUli^Q)W#H;*`G*U$M(>^BDw}8jD6gbhZ7qNgs^{JrZlFbO_^c zrJzj+tCd$f>BP8*sQlS}(P?K;r)v8@?;^Mr&A8u2#FR$*2tq_7&F9+x8H?b_9slLQ z_`BPVFLlFhFC*Or0DjnXXhG1(BQhU#@2rIB^Tb{XK6_ITpmAZm>D3q8kSu$!`iPf4 z<#IhY(i!=aHLZt(6gozIRdVPRW>bY;CS=b=oCLQ43PiB#Ii;O(E5)E#4iU?qdwHq* zqGY%8ilazYu!^lQ{u4HZBjrg66`KUsO%GZE;OtdfI>N}sU;lj9Zpx#hJs8~j z5bo0k@Trp1T6B7nwCQ+tz)yC!bXZzrB496cL^lrKYm*H6VhP|9D`=~Rb?b&>atR`x z@R%Ret1YTltcLJ*6lh$Jz`W3+ByR7{+3zS@1@(rH-w*ux-+jf8|K^Um6`UudJv(k+ zcbt!g&u`}-7R!)MN<6KFi73xWSm+jwhJ-N0kaDwYtj|Py_AFAQ0ILaJON7`XpQw4p zm{OMuzWsKfH^$o!H={Do3_S=?c7k-+b8mUod)4V6tI|M=5Q}!9;8!D>vZw&m7{HBM z{y)CnZP$`4$*waq_Z^qIbV(#3@JY~{{zH%Y9|8J8fp7W)0fGdO6q-$TXJtm5vwfNA zVcBJxxkrNlyE`i?@|?Zh*Rso;V^(}ZBHg!Ig3W`$eOe{4jh6DIR=mOf`OYu&owi2$Mf_;{15(d#_uJcLA!Mpk6uCReQQw4RBW_U5Kh zd1$(;s>)3Dm^d?L6uS@pVXIW2Cu#Qps#@}s6>4@H;rmYBtb1rj=u`bZkkdNzPHTgV zFcH4pw2TG2{cCjO`kgcO=x%5m|A>j_Jp<~CI0K8mjGqm_sBq9KyU06FNd?ch^Vk49 zkb)ZcJ=v&7Ah`^!(Y`_P6f#>e+|+Q(7Ek{kcfJ6__D#IWAS903xE~X6w!fc4n672J z0A-BggA8hTw`#a!|H`uyg`MSKUqnbeRw{F=F%@DA6jd#~c=h?R%Lrp5!E_?*3C_;< z@?;BGNBQmzg2c{tJ}E1k?iq23$pEVs2Vl_t+Rr8 z*RhKCRnqROel_dhi|#i^evVZf6d~ZNZ^@XgMNoy!sHGnG5K3qc1AoOE%{{)KbA1xH z28%$cKG&{`>5cHR?*7ZwkS&+Gr*yo8vR%oy==>%qnQt?wGQau;nM8G-`vaV zn!ldMhJo3R%@e9&*2$GMzg!V;@Iik$rC2k5=4_9&wmK=l13%MN62CVvESFNow~TSc zym;b@d(GOC%aY~_7NMT)n|ac}Xp*!nXk&m}s!8TQ&o468K>a1>gPl7cq-7@6MeiR< z>jQjSD;|uU0U1q3o|Tv8_ANZXW>zCw?%=65gytC%U{f7YJ{M-Xek}UG{?C7;^XneV z$SL&Y`$^yb{G#{gqJRGXe$xA=H;TMXXUyt8Ol_V+wue|IzHcr%`4T3yTK*P-0~aAp zojP3H)6Orl^cOAd-&0KEDGFGD!j<;rZEE!X)QX@=>57M1ACh!!Ri*#@|NKV(GR{Fr9>Y;-gNnZ!_?^Gk1Lxri#38NEQ8AHHAKmkcM85t=HWTxm{4-_aP17T9`C|K zes?$}HwfG7+EaNmqW911rY|(7IZED&=NQXr1ux=ljWm~;`2u@^w5*e6t)x5( zlVZ|zs)?Ld0lF! zEtK*}&S}uxjs_C^IYKn4LtuTf2OqI}M-zuI(HS7Wcgw@)$7#Ky-e|T58tYmGlHrm@vT-VJ-fvBxF?&Q%o7d{fN~Gu`58sHtH`qlTOKLH1?%cWg)yY`(gavJV|bxyCFV zn8wz2B80;L>)cZ_yFp*B1ZbZF)el=?aFaUBPU_j`dV4*>d?v^P_5RfKG++jcgSR&*qxSLZ9{M_8) z1(EQ2V<`kE37_eFpTUaJpmh#-dq|?@2QxLdDJFoDxi`P$e#fdI4T#fz{=|L7=o{{A zhq&(}-6FtH^V#6}+)`!WwsXP8J;eTm4eMneyEZLNK^txU&s2BHJdLQv+~? zSC(hYH1}b1C`aMUAd);Ig!^|NLtwOTyme(%+UNCd43EQzz8pvMij!5vum+fRQqs4& zrX=u(M-*L=)e8VVfgEWAdi6mi`~Sl?g37j)s8Vm+->*0Qc3D$@9Ng#hj1HCfj^x7aW!_<515T4>AP!e>06I!2|`K!lQrk}sP>Eri{{`|Ku zJI=@ZHM=YUFrCK4_|ErZFE1%APt^br*lYl-7mc;GG6-kx8?kPEf;>3CXcKVu>6sQzN}Jf5)G{zv=gX`JmU=BR7ZZ+FQ|Y|L~Q5{{Hr`d3b&_ zwuO+VNSI6a>>*|FJ*KaQXH(3zF~bePD1bSl1C~F-p1G^OrBWRgY*cIvjfhc(vuAur zzUX%uWo-Uz>w5U|XVT^_#WTWU@|W_sJ+kA!cqe3+%;$tWxQ2!*NHU$cN!Iz{Ncib`Kw8gD-N z#>9d+CiJQeqB&DSZOR@}?|KX^2+E)U03ZNKL_t*iX>3WU(Wy~>Jz_ct`vz?QJ_CAl z4A*o5-SX${q~#!{{}KwuCCO*lq#|{dZiWM-J6lR^6J`c;$%CWN=k&BLEua7C0~#Wi zj#_P<+Uo%1Y$k}#1UaMH9u#wK2suB?ikU*r#J$cU@cRY0tbj{R#j{qdZrz=0l|z+xEr2b*CW+{IsVCeUrn1TS)vst`Ddt zm>Qri6f(Q!QZg1gSm=xFdwHp$BQYEI9&Z<_hpMd(h_ejF?A1VH4DiGoI<4Ip^{l%6 zg_yn{$A*omI&_NwCRJyNw7SOb&!yFN8*O2z@C=(;3@4&l#v!}KRyO^*PGfgfQt9sv zY(M*F_^E5UiyG|m2tqV#vjtRjCUnpqIGkU;9VI7>X-a06y0kb8tR3AF#AFdBK6!(D z&b=*Omu0j>LJlI=&Wx@&DRt`5`zG`@s`DHLQyNeu053#=v6x=p4qEU1DSr8W(#LOa zT5qGj{@btgxBuZg{mVc8q@TaN$^Gk{=toaf54*uWcnV8ehUn<@Bn}80fDpRr=6m)e zN^7_xssJJr#e2$cU3*pDXP1pf5dmJ4lzXE>+wpAw>Hq#0`uG3S-*)k_l#Y`(IOvPe z-~Z_ct&f#*H=>F&3NmqSK8^07*oHD9XcNpH@9W!6?}5QF(n!<08l>nA3-S%CJstSm za(H2dPdH}0A3nbMv|ix$H`Chx%B3HLC0R!jqKb~Een7GG`H?aRCMX8xRU@(O53yLrrA3f`tS4lnLLDZIGWY~Tb1`5O&r9uWJawwO>8?>?sXgq5)|en zj27j*KvUf88{EKJv$PcOK;n0Hiy{nN{+CZ_MlNq>F4h?(ez zDt$YTtbr!OqGG(Da#6ZHzc>V|vt>i;7Ajo=4rm4z9d4>WZs#q-=R!lPvwwz{LnK-b zJp3G|NEe`^WS&b2XKZv<@lymSRo&B#6&{@^@NGvPeR?Df6VX+EH8FNrQWZx5&k9+m zF1vwu+KWdITc7ZIDoj2fVo~DRxjyvf1PdwmmH<0pm>`olyllcfqT?1cPknk%u$@bP zZ(T-{uo@45+0z7vT6Ug|Z7n&KjpFAd9*qnGGkELfAjDELjU;x{zkU6ZP08)L>c5G-)^G;G9s5V zv_QtY^{{W#kWvZ@mGG^y?xswH>Fc+HmKnW27jSL@*^ZCiJ_Kz+o~*(FoPIa>+O?!W zZf8lkQVs&B`>jf*UUKJYepXAU{QB%GkG4#46f z`?fB^9{iR#6)hMZas8I5 z9cq4zRjM8Nj(MAM+aYJYg`|$GO5k+jcGYf7B1UA82lzL%0NqAy<$ zqMsL?nRQKs1m#%#F-uWlsiNu(=ARyQ{V*3 zkIGx^Mz>T&sv52l~(L23vy8$g?I-CX3*c#qhJwF+XpV7a$ z7d5! zjD*}am1kC5{dM?u?*4A$4kAt2u-#wSazj6k9v*T<T>Q$F)#LuoioSRF!zf4gr1p5POe_wb>YV$ zbbQ?~p!cVeek=!K)GpT$kb^Qx@Lm~!0x%lX#z(-ywBAI=YyamTe_T10{cr#A$M&E2 zEE&9qmU6F|6qk8fB3`^&mmy(+ zXR#$wB<=a7DzMb^PZ=b*~LhwvU2z40=8ObU1+kg2WVxiZUO-D8*I!~eh?%(}| z{_B7K-6MPaTRfbiz|^wtP|^|;M8lXXfVkM$H@2u1A5!3i%uI)@sSh3EaueBJa9uiv z_tr?F*tppMdR2TvC36dcTBtV)Jl~Izp^H{Uyw?P!i^Q}Ta$Tvs?hFwL=OLs*>C={D z|E|}k`jDsl*8>C1P_=UVq6p$!1fE@c_7Tm%bF;BgvIoNF#{PO!+@jsqnH7aXWTM#)wGKWhRF1b?CUm2t|wf> zqT;YJ9#UEbfK&*jL^x&2O3x&GhPsQRMtV@1aV+rscTlevzKYPFkCQ&MfTF;6uk9l) zt$g@q1B1bSZ{|SUN`)Xo^zvL?CCH&}qgF_z0Wct@6kGG-gB1;Y(Y^hxMWNarvVS>* zzPZZhhdC6<=;%`e-DXvogoI%W_Hup z*zKP!vzxL3|IBeiuD9ZEe+T}|pSVp;1bdL7&zKBx6_TD!m4{tOUtwag^98kMfGVe~ zfltX?Sz3Ibf8F#!&wBUpmGZERk(fid0zU|uu-Bf}#|seCY61umjVLC}jUz*F3CbF) zf%SWyo&|#39@r?{w66C}hD4Qg#8fC{Y(yzz81_1lw}_KZCHnZoqqr3gWdZ3q(lEVv zIgW#_>!LsY`GfxSx3BwW^5*L6x0C+*-+!Zj{x3hsmZr~e%{*tb+a%iB1ZhaPya1lM zegwTdeV$p2Si#pq3Qnr@ejBa7e{TA+-~iO13B(8*E*f`qVmuk(+h6~I-qh`X5 zs&y4dTlk_%!|vwcx#Hrn#-NHc!tKW)MG3@hUtOvG{I=05*QOb^v8qOy3J_5i0QGA2 z3;<|_cX2{tXuy}I*DOD;@M|ZrbrE>av$UgqY{wC79$Z;-_OvDJ7A7sQF1ilVA-2FYmrz?k+%Ms47L zzB$H%1M|jnqd5kw`wMAOx6UVkEdWH-7*(8ENVzYqxGEdlLFOLY08K!$zt%Oab=Wf; zkOr0kd>12DQsSa_H`IlL;y0sEFwAICXC|yhncS9+YCB-|$Kq`&IWyvU{22X4oc^t@b4X(Hk*BL0Yi^~6< zOM-;zaC_xA<85NVtDdt9YF9shA_`{mOPWKv{fs!g;#S8nL>J*VY=g3AOs&4-v6-XW z@ymCH4C5wvM04|$YY+R;!w~K{6{XI|#T^_utKblUuJ|mp{!aA`FpY&678+^Y+?q2Hs6XS{d|MRnJ!L_S}m4~-2ypLA-W(L8M#^B)yAd3yjG#oaPMIS~0m7D~Ai-~RNX z-~Q>xmQ=V@%vL-bx+T8zf|!tCzI*kMk{Cq<{vFdFnBpRonrRFbs(@x@I*v*8g`ztS z2H|X?Y}K}diD)f{3?Y0%A)^=KpZHO(RMNO<0SG0=VFl~5+lJX-QYSgIdV99>_wO%y zeW~xz04P>*IQ3a6PUr_)str|!{2!&s_bu?#lFz2{y~$_n;bAc9U6zPTOmT0(y_v8f zZ)Sk}L7w=XCC{Cwx;HQ*a~Zm3rxZR0mMaSEi|y28qN1l zTC-;m0dbra^+Bj40(vp7fL-}X!;8?ps&M`VaTX=KhEO|WvbFr#l8o_T+7WI+0$qyP ztKDPEc9-Y1LWH?u=#aDx-oyk2H9HP@rXy#(3b#VP*W#*so5LizB~l6qnB{OKVrpV5 z^$*{_($CL}KG$-&v#VrW_T8W?UKC>MEzFf=YQ5Ib{H{H)#pNIh1B%ScY+Zxzb_`=( z+HUw^e%5Kx*r492&44^&98F+N)3axds}XBAtaU(BOHH<3uQ8@yXnlE2p<{H<+jhT2 zr~_*D$u~tvEjl1E7|%!<;vWOgxASOfu&f(s2~~xeIdDkZ@C+?N*8JBDmJU>l!Py_@ zSbVlme2JXnD(Q$;@BUxDN&@ z48|eXpdB>)YL*Gu-vx7~n&5U{rIm=9rHNVEzcb#Zvo`6rUsy9b&V#PcMRNEiZ0XL3 z6{3&dcZ^K@-*WDTOy9fFl#^(E?(8KwgybO7%YT?-U%c0QXVIz-5P&sxb(_5OYoee; z%s8ppFfLuY5nu1!%)I|_>gC~CpJBpNkZ}b%o5c^fy%LHhMio7|UVaPz;@shEN>d64 z*_le#HgX7|-i1j)FOHMF=m*F|XfT!gg-P90l%{jpe}^10JeJ%=G^p@%iJCBzDbwd4 zpY)f1^OcyC4k5aJzDae*?7TlZn82*lFZ{AwKDL_|RHgrgsQG3V*l9*e@vQ z((y^iX7GK^&eM$O^JCGAj_u+1haWbz})?oKYHgM_!2x1Rlw-c@AQMZMuS*leVX?K1jlSJIZFaFqkRO&+X^Xh2phIT#T zK<3V;l}6hQ>T^l;IH7olNGqn(?&9B>P?2;Y!2=+4(3eL}w$^8Ys!ikjRvFO zuP1$6Yv+I((V2-9ol6l}y^AbAn*g|l)DbsBhivPD#C9XWVC)Jmh}&YmGXp$sB-C6) zy>YT%YKDyHF0ksB1N4sIKpD>~IPsY=B5C+h=b{grLr|Z!VwNrMnE?M-4ok|n8~4o8 z&Y@sD&p6J!i5oSL-`^#$DUr5XN*LjbydD?Z29kF6`%?XgI1+5 zG<}lWS@UTbjGb|ZoG@U*W^y^u_+Uu@p+)vM>8%-GFqvtVr3RJ`?U*n8PhB z%3VsKzI4bI&WbM20QZgh{OwQL=|9)<0hstNc4nQ4vc45kC3dViXyxJ;2lLeVRp^rK zVO#J0F@%qC7Yv$30xhGp&~sv_M}Y!=%5cU>KUVU)C-4oVSxqCkVn1vyj%PdqNmu#2 zx1^$Z`B7GvdRg%2U>BNG#C}K%m!Ylv#76Sky9ISf#~xasKglP|SFS5_bK7D#2jMrj z$qoTg)yx8HQYjtu1>yu|=~7ysi$00S^v8y3G1Kw#7^PJjklflv@p*QL#BHO1epry= zqKCHZk^CflzC$~xq9pRdX2|qJ+G=c2e0#mOap0>~uVd0)sJY95Qq3^ZUjB4-qMxaWqA| zmHhV@y?r=yE}`RK`hFh7%=BJLpG!C8mv$Z}5y3WizhhXWMxJ~E7TJqA;`Zz_{JxEv ztI*WC-|gTYyQJ;B<>vp(jO4U$aHqF|?7uxb2F()>3x?hOzb#k$zVhtn+>M!Kr3L_y zUxMM7Q>w}Im<~yV8S-$aAiDw;2=t33sKJuqE^zJTqzwm7G(CrN3lw5#48G?B^MO5P zCTZag{K3I!GDXm753xojwOz+3EF3m;4g~mn<4uNNQO&5iDXjpQ_K;v;?Q3nsww9lL zZ;+`?_-t;7?P*w#ipZV~%6uRF@UtAWCA^!K_mz>zE;T$s&$vN7ZBRF~XDsQ5P4AnT zy0x?88yf|E@@%ED5QBdwFn4nJ7=x180$EX)D~)FVfs>eCA|x!1GOEZiybF-#ErV1( zGpXNNHY77CC4%*q%phmHfja?}S8CT%&zvmjxetYq;BdzO`vKESl^k!ev=S^o0Mr>O zBw?=aTj>@*x~OF^$Or zMwyOde^;MB_h$O~RQmG$aId#*6+p-XSs={Y8f`f~*Vq8%^E*)L?LYyA!0pdAr5N8J zM5Ss*GIecXE7up~^=70Oe3l-ul1-jeB`R^rjen^9FOTG zUf?{@9^w;1_bwhsU0lb(`7WqK`qcV%Iia~&q~j$6QcN1tM4X)O-(Bi>=Q~iQ-P3%} zfyEn~XCAa)(684)@3&8j;XKslhh`vX?%!cGz2W{BeE;?$Q>9ODkj_(5nw@T`0Jhoi z_+kGB*`5hD(nh9ww2O#zU|DX41gWuiscp6aFbrsZLc(I9B3#T3eMa3FW&Dho2(%LN zNO8cL31Tt|(MeC|YX33&^GVjv;~BK+wQR*$r=D+0`ToNP?*_@5oXwi@0fj0|T0Y|z z!30q-QF;t|aq}jJq}wVANqZV-4t0X2OGyI-PJ?GMCWYSzoPrxlD^i(B6@d}Y!A#pS z!oGQdc4s%d?lC*|0Oxh=?M2fw zHq$e-2wZ;X%nqfnE&1{V?`VQL0^3Qnpbc&W4-z!b7tSgItM_{wsMVY3A`c%xb~l?S zd##8hyi?$$GgcV#chA4h7&Jq3o{WuV#1Z9tT=hUGewZrPc;6+VaN;(M%Gg#yNN&|*9c+s4dFPWTYm>8w zUF{?cOpljL_vCV^X7*WnzWjebP9|M@Lul9DHt4k_cDl^vx!%l0vFA6NUPg1!)T(2f zDXWvPBmHI$k{Ftfy&pk-001BWNklZ#S8rH@5Rxf5~_E)VU}9{L|&)?3etKJQH->EjIVvtW;KfsKs;qoYPktzVBD=Q6qzT+%g@LLWCo{GHhe!IgT^bg1(@%oK!xXmGS{rz7dD6$#8G%86uR-RVZBg<@KYNAqI#3o;2)Z!Aai8wWkZCG_RokzK0h(1`L$ zf@~2rc#t*Q5-NUjXixB2vwMxdphU{B+ z^SH)~st*$K`XicjWf9Kiw>4%Ux4LuR8KloaEgt#jW1=|Hm2Yi|U>sVUrGte)iO(BD zVzl5`-w@B-+#FD~dZZ-&nW1g4ktZPaV><+c_1O65lg*uEjgs87H$g!lFklL$gK%u? zVQGOwEg>#AKdXyt99^#Y$qZz@a_c&Yk@nQq^wTvW`3Gc0S0|O`o!iCy{&e z%huT^#YyP*&pme>BNzFR!4aR+$rap~-x#l~@Sb&>S4&5M2r|xCEvwoJH=!WQGWWyAz}+BEI~ul= z1%@FwwP;Az-divZa(vO`|J9F8$K3;N?B`mt%HTB&I1~;|E+RS%GKWDy8X!v^%Y?Q* zu7e(_5`1ML%|?LCZr0c7LQs{7Z(j@zN0-HIL%lIo3Sas<^;CH<#?R3 z!Ba8m^oJz3nA8#R`FNsn!2MOY!Ss>SS?dL$Rl2u+IjrBs2 z6(&?c*EIaj>OhQWi||)y^EP#HGx}0-BNzV;Q@k7>riRv2V`gx76+HcAx$!LrjkxH5 zpq994<4dZ}@SQReg-1hgDfIvZX6nHzs)%nC6iS%SaY$aIQDmwUP6>R#HOQ&U+n}&Q zwZRdQE*zdwZvG=1`H`J|3B_DYP0eo2sTnmJM9bd=NvV#a$kZBS&XDA6bdR^e(iAd{ zQkvGLw*>H_swPV{V02@W{9GD$DJ~Z--_Vi!w#&xaoI`789<$fNqqqc!cV(M{ne9wzzhUNw3 zzTD}NRf>BJj@^_j ztF1S#7BC773&XsNvh$Be0JdB0SFHCkqdO!Ta*(J2=Kvo{Y>w|1V@tdq^CbROr$4h} znQ_q7)dLx5=fe&01PPR3=VPBJopM|CvuzZ}X-lcW)xrCovVgjI4kd%!Zo}^Sze}_YuP8N?9Q#dc~N~6Xg7_s;E02dVYV0mTFx2+~J;g)unnPdKD_}y#Bi5oI( zPa@~G2HTShQujE17TwW+-AuAjye_)@^O3X+fTE$LMA)cjW~TL+E;=wpt1`TOmwle% z))j0VLp{hH?xWr;EKnasu`i5G(PS5Ji%UF)aw^W`Nzg^h4Ey`bY?+~oN=(I4O?tD+ zS+Lt}TWAOiD&G##p7&LNVIhS!b zI1q0@4fH#y1d0|Ip=%84vqvv=oh{K+CY!)5tEUh6glqVvXi74=Svna>>b#p@j0Uqo!0vIJ{gZB1 z9^j_L$9WJZW!-+EkGquUkfGuT`zNS55!%?tERyo+DA6^A)k2r9JwRZl^Eh1gX;lqm z%#`kJt}dm|-xq!S{c^Z*+Xy}ol!%yk*bi^(qZ4OT8&#)Axp zC&C|$M?xmwHR#IyJb(ca1^O)>GG!xHgYbFO6b-sG$Yn83zSm>i0Sz|@032La|BgKY z*$u45O<~-p%HW;gu{6hNb3|ms8*u$xw44$Uxw$cPNR?&(m2(3lRMT9e4Zk_0;6%)u z@Vzd}Vm_{E<*^+fy>=&ebiXT79hs=-!@_NB10q94qbMKS;#TxP_Hy&WMb@juU~Wt0 zBP&}=N?)rbSn|TfZN|CXv8JxHU*xQppP94aeb+LSG%sJ;0>T!b`0LGIQPQK6FPa8J75LH9u-r?x4+wrZo@eI-nc~uR+vTri;@a#<)@VxGO>_| z7O;n(G!Yp7iK38(lx>r1wA>gxoN6QTtwlq@odIzxtr))jSlqKxrzD;G*>&^kK~OVh zGUkv)IyIDAE-q{fK=K{Wr#9bO!|?Q>ufO15PoYc0&mPbib_iDut`ucxW=)bG&xZ)n zDaVG6#SjPvvbn7_$gUkGK82P9^_jUXWZuJpxF@&@bM)RC3~QTbbI|vd>@F9inz2w_ zNm+>F=NW(*fxn}gFm78p^L%du;hjF%K`{ET6cP+MuD&^pvz+=c6FYson}0e!<7RPy z!CLyN2R{aq9@tmeA$!ioDdm&6b+YDVU{GnbxF{I5>)bo!P-f8XX$aZ4E7EQQ@F=H+ zbI=$yo^j98CDXJ$re9X+reHviZCD4Je|bb9-Wp!y99T8{jfq1T?>D}A_hWacs%1Qo z!e%M%o39?JVE8T@=Ki{(lShyo)a}C3tqdXEWl0aIfh8j9c|9njfc9s7G`Vm{q%j*; zKnh}QNLkX~_Bc_&3-@Ptc(TEUP91I7NhH>b>9z#2h&7b#D-Jn&cCS!76U)K^{{1|g zG!+dYiH@zVn7Dq%~v)-YA=1fe|HRc9#_m z&wn+l{zZQNQ2!1Lk+P4n=N-TellIX(;#O)GAmQhJmi0~ZtUt%;r_BF(8PTC+Z`j~2 zYf_^Y-6rAx5||j%fB?*>1ind16OuA>0Ac%MkeD}z5Lmo1aZZ5|ZqcS!X3baCb|Th8 zyBQ13+@Nz8n?PiiqF~6Zks^aa-Nk-fn;ET$uTIg1skDIX$BZtmqBC;71!E^5R@X#` zFk{#r3S}sgYj{)fHPe$t1JqGC`%yqZagg7nDyg3fsZ22=UUqvwWD(J#dx0>XbHm1i z`rpAAZxwMqJfyS`pMdcu+@{Mk{Kn`rvksh@;E;H)64vG*I@2PmQx%h$U+n*UnbBc8 z^5t#U{&T`mfVd|-iuPkSzv2z_v1DK3qo_0^dcPN)hoo8{VxZ6`NH-u;e~je8BisKB zw`V*5Q{mfSgC6VzLG-UP7v=T=x9K!z$zp?B3x~=biHZj>AN8|ZNfMhvr$bBTVUA5n z95RAi$i@!~+2oNlOx$C|d3qiP6nZ7swI>Tk3s|b=y0$9PSQnh)P@KG48(!*z8$x_T zvCP^%jZtycv<)Ai;Wi61Q)}pg&Hc3?C)k2sTGGsFG9Xiz`lc)j+w*YWGS}bn63&>p z%G0drnzV>6_qH;3Ww%RFl?up4FkJ5uMAS3mW#%u>ZT3T^HNXamZ6Hi6A&OO>SZ+n* zZWq|L>ur6$$3ezKr$sCdC1)k##AL$Zz(0Q&Z2#GQVX7HEeS&zu1l1J}Ly#>3F+2-N z)xtSQ&tY8*@hzTsZYsH8E|k=}erDkZ>njw8N(PNJH1tg44Dtn&QOWt)ZZV8AAmxIl zQ=tit#M9m6$gSu*WHTNPpy@t{QT^X55Y1$*{$_9b?#3u?>bX#TibIi<01g$O19(W| zD_`&|?g1BgM4OyGXzf+As~LS^fGm-87!_JG7}XJypXtf6p<90DhD9k9LZ?WRRJP%v zTNs$R?S1kS6mB(2zIXX9S%LT9eb``KA*Dqp9k9y}nt4A@x|ds^q~L@C;*zrdIn$RN zCDHEQ7^odAImkNV`y#hwvXb!8*d~fg6lhvwfTLb8Yee*#<)6zKzmTQtK1-Hq+FUdi zWj>@7HaeMUc_V!O_8))tgm*2Ule(drV_U&Y=L=}cFQ159O4on?56Cy6Kb-z~ChGuRkt&@5Y8{rY~;dMmv@ zR?7JvtjTb2x*za@TS<1~sHR2o_Rs4$g^sTWks5vazXKx;THbJOm8*bakeM?>#1&x# zP$yj)N>Zg~-BMn*1CG@K=<7wdMct1bLkg-bvq|}1_SXUO8?HQJO;Wor=HKW{ld>hu-V{JLd8bUhONWKw4DQj zBKh7^$Q#W7C$MFeCL73yd5ry8t0*+P%QqYjYL)mmUdB$=UT%5&9opPSslt)Z@ z{S5(=c2iMw4Uti2s&ivU5HK*p-~Ez(c3(m7H>IcP6Z4k#AMVo=hwWXyNj^mAgn7V9 zi8aFpB4Bhg%CUaN11Zu*Sx6JesAJF~AZfSOv_rGZJ!t@AtY#C25kw_%p3yG#E*ccT z(d=W4PALTT=T`L$Zl?Enuh!KrH7u%_N4)cu9z;S8X~Kd&h_p>#>nR0>kgsU$;j>&h zyhCU000P1bDBiH;@6);%OWJyiZ_I16Pm|*rSc8KR`RoRpLfi_*f=JJ9W zd2P0(@2Bh-vJFoXAYJdq_c*x4)RmM~{2VSY(VcHlEDD8YA8*)r$221I8oP&@bi9Mz zfDvc}Q$xxD6hF{3QB9!HprQ{nhCyM^BDYakfc?r+Xg++8XX?mEGfjKfna__v)_f;S zAI!r?+lS+a97&#!`yuWc@n-VnndWa@o?*S0TwiyH5h-lPg`!r2*gMQDY414E>2;6g zkezd5t1v!|Xt1uwpsek-O={D0i{pQ2-Nr6mtj+L0CZP?orB8 zz8G%$Bg>yl$Co`s(j#(!A7U){wxu@PpP9ISNtcZa0U%gXg>p(_JX(Z>>H0LEfI86N z^uu0wQ=qkb37#S+`RFo1f^BWe?PB|N?4ny_4?g7F6SehL`uw@bREb5>M^}FbcL)yk zTsdZTB?4^AX6{^d<1Qbk%CUcfa~?6ZZmn7q4I=_ zX9mB{!zHkq&H7ytJX~K4g^NoqW81c4-0nX+vLu+nKl(ruot{rreh~PV>w>LeP)0%x z2+EX6oWeOcQqOd6XOVEK2kf?wg%vPCgq!VXVTN~MaHldk$Jl?_p3yK$KM4QCCWA`eaOd%zqrA}}+Kr~?lY`7)moYES%Hh2ON zm0|3Pb;&GBi19xC#Z5pU3>&$*(SSsjd#NJ&z0m2&%+}s93r}#{WLX>JEH#L&93IC9 zd;Ii>b@%I?T@bdP3u1treV8 zV^}&Q`-2Mt+QGpe`;|w)8qeivVSq!WzMbu+KBZzVVA4SR^CK+|b>^AffmI5CA@n<^ z8fcJz3HT84{xc5-kN+3m7EKy7I>9@Lr3_UaXhYD$h9ON(_67UP+bt1-HVFeI4&<)c zgH?(#wvMNZu9{b~JK>w*p4tl{PO3NI{r)IFu$$!GY~J3X`tW2Nwj9`qb2vVA6`{H# zyYrpVTqrOM*R8QvkeL#7pW5DV()ihsSrD9x$ZXCA+7)FRB1nxRftkBU62-ZDnEl_i z)aY`*E^`fJ;<;tt6cd{dC2Lsy*3FpuavXb`>}x z#_NK&E&9SoeOB*3mKXNwFS!59-XKNh@~RLK$w?JPY($?ww#QV2>G&q3pH>-|hc9_Q zcL=d5gwQK){a}En-SfeQ%^=A0sK+B&PJ<05($-@uZfBT1cPm_*Z!3C3Zf<+z0YA#9 zFj}B#3Yc!OFqXgfoN?bZT5U>(orY3wPh&fBU0r_52Q<^%n_9Nv+f#ieVAL z+&AyxH)e3S*(kv9V!XW%Aoa3}QHY_2ph5xT1`PguzVBTo6%Z-2K?`j%_@@vFIB&*3 zw#G4m&y)tY0G`+Mcg3`xArfo&D0yzP5r>K~0E|+y+I;ZXvqt2Z$cCcr*t#l4e}(WP zZ>)s54H^5^8?#ZZLCXS)!yws6DI53U8+M~OCAXzLn>wIyu@z&8-tf2*%rdkQ$wTS= zd;oVKYc8_Swb#HMI`4M^PdR3(OO^s?j(c$(NC3>Gx0au1nboZiX`kh%+r$qY`~C=N3$1F&qH#Urs+|fXPb340N9=^ ziKL)JA?h)9Gv$GY?cos%kG|ZZ6*q1`)9OHG=uI~g&!S6K+4}PjciiC{FtSc(=9B?$ z4gk9v$bgD#v^Xa%+T!JRfi5 zY-=iK0Yb3>TxhGHG^P25mJBc67qKf`?mP)(K1>g-qMvtfJhs%9__pW}gpe7W=OKj# z%ZpLBQ!T}fpWTO=!x_;t&-Y*aMe1(J=YfIl&PaHcM@|Yq{2#^@<%ZT49F$~o!I*}| zZ~|d-9bDI3xm73eT}`8dc%D2QzY|XGbtjD>jIli|%IY&XL&DAV-m2Fciz)}YY$-ex zkG2(@@lIPA7Iw0R-tN8`6UA(F0B;1H`=6%yObIcq&H=BVFB;;w6PSqUsNuwT#|{d( zXT_J1S+@}RcPy@o5U(3vzFB#h(Az3l@!U`=b6*Ka$^d92ST#g^1~irH@^9cM6Q8Z} z2HDy`mr~0GcuOA+XKGLh8p@VxUd9#yOz=rK)VY<7dx?9L2A3Qo3IJ|0MT4xeXEXV2 zQv8c-(Dc`i1-7aAWH*Wlh&X_zt6$PX%8`H|+c-m)K<5Rf=HJU)dhvr17(i3h&I8xn z160B_>Sm=Yo@+HBGmV>)gnOHtU)bTLDPw-ZSH8WG&?)ap50 znLDr^-_mFCp@n$mMQrA#w~KCeILJi{xP@P`aq&BA~7SxI{h?(PhYr#xMbP%*ABz zB2&DBP3fjsCOt6sd~%G-!uvVmTdq_J)z7(19L|I}?5LD$?fV|^lS52F&2%6r3cmXw zG%MW;eC7^*EGXeaoz$LNyR%fJsM9fM7MBo<)g~<(w5`%R^Ru`VWZanS=}RiLV%A6r z%Ja-#gyj1oHe2IN3{^)<(cm0x7)+BCv$Vhnp_GRbt(5$sByG)l=tHVB^s#r zIHJxn<%U}Hl+GM;*nAp-TY{ymK&(7NmsbAH9Wh-T6x zapx(w!?4L&gEHAkB)7c6R_HYsCEnafNcRI%Ajj?@JQ@9JC`809O8)#uy>r?4El_QE z0j`xtMfNbJomq5!Sq^bEp`&pWj{4Vb;7rGKl5fEz50#I^{<=G>d2FdK-{{4JPsrzYFgya<&50Btrq? z)kZOz1;6{>X~^Jki+}`Zvu;hO!Iwdnde2KH?&Q&o7sE-s_4RJcm&MHzzTffuNfaQ` zhQgahY_0ia8rxiqKbc5c8nGJjjUxGXh&(zB#UUk1edo>qmg0)J!7YP8vvlYOn(=M^@Q z7~@pA_Q)5>0Z3E*?e~`b{f$~OYC&UE+|41h-I62P!*!F|-+f624;F*sMJ+bYR6r-v z%14Eq=1O%gy9qTS^gg2&&WtB$bPWF|U782LHN4%z{asBDf5hVZxG6NW5e{KrCkr1gfS001BWNkl9ha+T~>}a(if*<$33p$4F_toSs&i3FFi{}9m<~aX~%nx?|*EM zB`tm%T-fTo93bxh&e!9}bb>Q460L@n-H*#hky+G+4&{UTSi{>YTV;awwyuCm196ym*2* zo207e6hAD{f{Ymrqd+5raxvWxY`+(PFV@_mk^=S;UxRBjy`9g?yGi_!WAC!}E+^7h2w92^&AgT*+Xe(+$ z9!CEMc{)aev2Yj9dtpE*!xI3O8F!x))$hOmX;go!#mObbnG0GDw)>BGc=rU;~QQIYiQ$d)zRiq`vE zXg8Vj__7Tw>Q4pELsQcQ`dxyU8%ry?_a?F#2~}Os5paWn#Y9}?D3&Q(N4Q&y=^+mph8expkj9_krQm%$6JcKa2d>HTa=H8HyPo+>G zuyf43&V$E2RSu}T(dBXA(L1fN_H6)LNWDx${QX;%; zesYTnUzs%3TOYPyWL;xB;J_?4%*0!6$sJ;;$T%31SAzj+X6PD|@yho@NAn&wk<+s| z(+UAK&W>xVC+0}G>~1bZV4Lp~P_zeN9M!=`-=a)Qkv)aFm*me(%Wpk1RiUYmxTP9O zQ1kUBF<+Q)U+YTUW9>6?n5i1^i7sZ z#Cna;@im3Zp)xjj5Y-!sLPbVQ3PjT=HNmzr)u{F zh_#^8=FOf%O<_>P2n^70FfllbIKW~a8@FMzIUD<-;(tQTvrKP3AHK=04_BD%Y;%Dj z^JNv|bWo)c#OHzk{-&_aTHEFL;-E~bSW=k}u!uJs?8A_4fW2pG3gQ zC~S0Y4;{E^>PE>;xy!!K$TY-k!kk_%yFGBZMPtaj^Gw3^sVVWz^>fR_DfkNuyue8D z6z-~b>oUEYObNC2LXku_dck^lo4WLiT{h;RXvY)?S zNt&+jHR9*GHz-M{krSygZ~<|KFdZkCTIQ=H^>S_tHDbPk-0WZ!h$^y$l91gXWXlHL+oE+^&vP_uQ6M+JYHL(@*aaxPx8(VM zGIq{pD2w85F8vmCfG(cy=w zfVb2`?i-r`!9(a-UjYV*V#_D!;=fo*EV+Ld(d*EILVy`P`naL9qVTr!(t$==?m@~y zNL?n1@DJJ`7#ujJ1KZD`1p}zqeq=_fO+X-z%=>t6cMX&_1@ItKHUS1cO3O2ZZ`KQI z>Qe@}rCI+F&CCHf;R*M=bB^aD&J+eK#y%ZmuAFpnzfU*NN7{Q-u~jgbf*2$M&|ST& zAcs%85s%c8hyWTsbEzv*EVWjKa;P5#2Mc^tPt3zB2MreK z5>5$6I|dH4!(%Gqa{KXpT4i-xYjXR5X4$a23~6~n?EHCM;B6LHun-JNGSPtg`CS8I z0SUXo5C#a@u~=zcpUOxiZO-YRtl*6!3`6Z_DMl}J#b07Zp?KAAI%o-?W_S-r6 zgUQ)VLNgj}Jf-rPIrc-MYTfe$MIhUxNn4{!&ql4w~P_+feg5?x$W_km7- z{|usOs`0Sfc7w++xYZzB5*cy;hFW`G5uvXJLTC7Qc9LUjO<|H~dB_CG(jAQn?TJ6O z-m41FD1ZX=H1QE*Ys=i_n|90dh(o`PZ_|?#8=Rq0f0UXxRR99J!252u)FD^R31IFL!nzCmOj zt0Vt>1e%v%A_l|`6#;D%7?+{)@N@SKVsRp@e4bQv)k6v$%h2rN1)UP^IR9dJ`9S$^ z^mC^)&nuE)5G!8L8ApM_Bi5_nU&7UtTND<;!zWFRo$>9E)?6zLyqIf(Up+hw(kz-3 zNs@IG_~F%FS|8pazWMPYLa>1wCxVTQg*F;sQ5p{zoS0MFQ$~)w41d>i&<@Jtl-8TI zSRSUkB)j>N#)DrT<+7k8_rYIKJ`9E!OfNpFCNb(!B1|N#{tQHuTB-nMqHpKXo*#y1 z7p2v0=EBn6k&4Lk$Qtm^C)>k+xW(3_l*cay26!Wz1C!#j4xkr>$2f}p+&}_z#HiI{ z3`5(HdRBZ5B|{(_&$W0F4fz1hya9jU_;DZCZ5D%YoWm9ugqe`BD}OI zJq+v&mu&*?vSPNF!-e9j62XYi?b!~AJDz8ans2yTFo2A~@W+NC4?wAJ)h>eun)0M+ zqwh2DG6#?3hOsft2E}SCXYhRSgP%~Q0-Cy{ys12a2gERdoT)4{6cup}9#iE6OxihfreOx<-G%^m92Pu84Zcj-x&H8vpH zgG|q84{=p!=L`m5#KAJ`1ndXk@E4f@^r_@j_78pyqV4|M#+#^MY{a zt=$j(r^_veSr9#h&AKX)U8_ynve@&_l(p0sM%|a#&jAF{l*drGwt^sb%;|FYhWMD- z7ShP--3vOlxwx2{N^qc>*PnrB)4%T)gTqfE-rBMdWI4tS&o>|i#^=7(-G+H#H+(xB*s(N~%V&%P6gu zv^U2ZUL>5?=iy+g@MYN`g{aM4L4A>HX;b+iq{eJV6XgIEEI(l>h$1Hj$^4yx>HBf3m!DS6I3l9wwZwx4}vgJYqc`zf_u zHaLF@nz!Ltl>z~;hgtefW1EW6UgdnUl(y!XZXIfg-xSJ*)FXv#u0Vc0|l$HvND zxYV9z>5bumY?2VU#@{QpsHI$j%vhl=FyL9>JNI*~mhqh%@8MOahep|M^#1xm8WsWu zoR}A{rVnG@=@#o}yUiUR-zC&(-GYo~WUrvALK-zZ&Y7Y#Thq2+l-7P8!lEGQJ*HIV zD%8Bv;|=otSn;ksTAk|r=ozMz2|NsDa+`<-+c8^F-{bGgu>Chsu*RYS?f3j zd(iH^>prD%387bKz#-^ivh)0ZTNdd7WH{WIZ$l=O83fBjU3p)20}karb7%geOUP z>M|$TRIv0MGrUmL-$<676KqPPOKW=rkrFJn5C2g-T5jTHM=x0gt>>jBoZHygl4(;z z!=asW(hkjuk5Ql-FzStTYvk+~xuQ`hE-xI9Y7R-YJk2N#Z}Qmq-fIZa)Vnk$plu8G zltm46Tr`wU+X30lB&d@bY&a~TTTD*K?{-1gvwm|Eicwh6NOG5Y#RnXjqy$-mHK4gm z-q9B@^Y5C3o5p(qO>h1@%{NvcJVrVE@Dqn?K^!2%Ob$bHiBTvSgz3647u9^jSBeos zB}&^OgB&NnS8MT=0cyj1;qp~m7T7v$9{Ap=+M{ndA`9lmkfq11FiP#60377-XsGJ+ z=3B-yxK{BF5F&-!aoj*k8*Fk?-1~Yv_!s~MVJK9F z#B!N6NgEANnM525+ij5BttsS_aEuqT7U_WyG}VgoxG8u?0T|6}VHWtl(X$3h9&u1! zU;#i2W8m;iY`_!l^knt((lEz49E|XTFE4}=1;qD!-2;X#0#PFo_0iB1-0KhYik@f5 zun!*wh7BNcP-%W`avw5xv*SKNDWLIDdpFc^4YePc(B!f(8}vDxUZF zdDNc3A=d3O+|Ncc8e6;i`y_Ke339ea8prL#wVNu-lE%!>e+JL$aaV<}l=1V=Lb-CQ zMXG`0LfGV4Rl}K{;k@b7gZzKnmz1P*6LcY35%?wX^I`m>Wb#Sa{~#6(w%h@+d+T!6 z$^J9f(#lIUCS`3kvH-}+jOg@XiusmV0Zhgqq`nEw?g}egGL7>qoN52E4E6x<^ zga=OvsabAWFF%7`J{-P+QqLnbggUiettiIvU6+l++)AOK+Nnq+ZGz^1#RkJwn;A%e z)pL?-FyXRt2W_2lbnou?U#%&5P=y8yH15%klRl3MNEGWn<=oQ~P7|%9FocS(NXhc~ z6Yy3W1x3D};T8k>wt=1D&G16h1es^lzi2{f#CZ~)_FvY)hx%J0FP7xA~gLAWIX7v7PwIWh>IezRL733oZn}>id$=HZmdKv3LD;W)sh>p3C=}3HIt(crtDELfHs9mf@ z4yLp@pa>=Zt->vTxVuTY%a3e0cHkMvVubhx4Z~YQXT6EK61Oa%%oqLOON&$RzO-#9 z#mVR*C|4Ge1N`4K8f;@SagRNX+*Fi+Sj9i#rX#nzR%C58t0p3zrkIjL4y>Sx0-o=L zNaS$TWy>z%Z+>trexy8dmJ{bWjg}pcz~yTU)dtJ{-xrmHZaJWF>9!cGW%Ti0t#}vd zF&HPKodyS((=Q182gz^CX|r?s0y%W_X;3wE+PsqnU3* z0Rr=uw7m7)_z{E^%VIPmK(5u=;_5U*sj&s7Bkq* z3Su_pHP(huc=kiWefl7mpLY_6T;9R*z@G@Ui{)@Hwx(+DO&OG&~0V$ih1_IcLV)WM%YO^p+zHg8+6&4K5cJ3jr%% z8xZA(Z%w^{=T=4%2I&xd%l2qeLVgP5Q#WsO?_W|X%nyBsxy6sTsZ0#m4>oeR@tnkW z2NMFqqiY(=u=t#M&0E{E*Dorf$n1V74+tQ4L3 zu*bGG{W`7L>)Q+)F#wy_VL9Q&Ne*s#8;lHKYvx6}X>GqO57d&bnLcduF@h>{WxFpS zWXS~NSPXqf$#J7u0uhSN_~UHEn3N}XmpWlj*+5S#5>J2t%C@P@B_XNnoFjh z=PB1`$tJrA1_@(}-Z6&H!@9&cpippN)g+Z$!NMb|1IKL&V}OIYOeqS5#WbU}{ES@+f1V2(lh08b4b{8RKx4I0$P$Sqx7ksM zC{V`Z8(^dTD~v{6b&#~jHbiO|6$O-mJn&$g_I3LLrzemzSCu?3GMhq(S;2-k z3a_rJ>Ym90v(&cvc}>*Gui+ymotuX`pAKSrUllMEnY%?0o=wR9>moN*O*bUV3*z(G z66hTE_>eodv{VH>kB$6X7Bqn%+b=x-A?@vOg;Z_~mrF0Pt=rZ>n#lv4t%h@7X^=az z`uvPL^iln}UcMw@F^IdNc22f~1*-i?^Nt2XiGVf?3KfHQimHxMDrnS}H#6x0%LW+6 zV@h?(qzgu(iw|EQneH& z4k(;Q2T7i#y2F~&T5ubP84RY4+ET8TP0xPg)@))n_Jc1OrR6Av61H*GLInk>5GzV7 zNYH7lsNn+VI61ha#Vz9)sMkz<%iuQZj0*)oV;kc85QO2*5{RSYIZ?v}4GjI1N}+QG zaTsGkY>V3feu zmMVrU{-7)5_EFa!r<@iX_E}&{c>tn3HQv5t1dp^hl=M#kaEmZkP{^RTi-8JT`GxLT zuT-9TD@l0s8YPI}Hp8j?WBhxjc>p9@!1!u$E+X;V3a!higKg~Qzwf;&P(Gue*ZygjhLn{c0syC_ru%)7HrT|N zySi93E=O{yvH;!K-&FxmaBOH#frQjiWanqixZSR;)dbHO3`Ij8XDw^E8pY!*sotSS z@f(=P4#{^JISv@llX>J`sBnwh1Bq82;RU*5-V)X<<|(|{RV+vR-?enhw}DPw5=2Wi ziUPv{rM>?@XYZDzNs?pf0Z{urvbsjdU6Bvm5Ghof6PgSP7DZmHCJSjPz@}l)LpP^Nfb*!o>+)j2XWcjCR?J>XYO82d_Pn}-%tRob+{Xs*3Ec5hIl@l z^Z%!?DNn@}y>JUq=4%t6)&02Jw{Q=MKZ4g>@|_u}IC!rPvoCUKue_tClnic ze?yl`fib>j!#e-UVH&|!x>SdT_}FYhsd+9_;)?eG0vKteML>+`5+(%|Liamb4hX~j z9>d32wk)n~X4qTuS@iJOQr4;~DDdF{XKQf6VgS^0K0}DOWWPnvr>?dONaZ76*hVUa zp<4eWheX~jAw+BXhD$A}L?fpTSr*-rfT|!SBE@Ya_pIke_-(%Tx{x=Oq?`%Wap&)@ zxW-r9w~`4dp+r)8{tj%C;mOn0f^Z$OA!5a?4L(5U(Fdy4&_u$fguq@@U@%5`PUm{( zhFmR2E}D=xe=cl|bVEFhKNwq~Z?7E8CO69MAI<2H4w5p+Ar!1y@fX$s_v2^L%s{Luuf4mOPVE?F-ra2-&; z{&|u9=eH}zfj(>Tkf1C|h0oL!i^K#eq^6ih97p%p5xqr74Zm>PT=Fxs5I*Z@ej}qX zY1P~hiTrN~f9TjgVt~7qpA=qJ|Da)#OKcAE;u?@dQOq(KYq(4p8@|#|GJ-;6`iQv| z0W&JvaKr^NV6u?`3paaB7kDJu!LN#qPQA8kD>FK%QI>bWvE52J0!R+GR{8?neM zZ@IZdbnkPIwwz;_#4vnjoW%-y0gD<>4Wn`j)!a}R4)DN-&ovzufoMGrxuYhSPC>?+ zerHpsfonfQsV5o4HiKEyi)qSLw1tWT2Dl2(u7If(O(`Z z+L{vW*!qD=MYV5j(4&|eMdOrC8)+{Fjp&NZ_4zoI-jJPh738^ohQ)xYU9%u#j^@TD zP2I+ycfgfd71ih&;{oJ}=XW)@9ULPgOV$9mR4oGx46zuNuwxhzsLi7MQtcG!5FO#M z9iPePx{g259<1Cv_f^B!R`AC3``(+v8h#pFEhPXOeb&m~x2WE8OSa&OBf;i1DbVnM zx3Rw00LYN~Ceqp#Q=HmYgORfOBp+n*Nrq>8#wSUtEUdPS0K#IhX{eR*M!?6J8+}+& z8xHP<47AJKd!(3UmS};C0!k$?NHegPqkPYBmD7$-PglI;K{xTdhhErkLO0wGQ`7nH&2EKchuuBTzd^2=+5EL0CK%?+im6SOQ)mu23Sncs(VCZGI!v zArbua0ct$Vj9$Mjo`(wN(iuIZHhBOyAQyaaPC+(?-z(HJbBQ0|c6>#(aRH6E9~v}* zq#40kjZ})cRpoKf@7*9n3{4$NQ2^EWY$JmL=l|ZerIcm*8lxB>j!mMC=&G|J>3VX% z34HU8;Hqfu845Yt^dtwPEJiUS6*jOj91sMXm3%3#gmp2g4U#5!zID6jhoA5iqweA1 z*GAjgh`W}nmK#`_CC_N;(Jvj5Nh}fV;hSVT!wa@T=*RYrKxWEL-Hk&Sec3d(^SMQq ze(#_u4iCU75u|giDbR)4sL3Fuhugt6l+oFh5^7Wq8)Ez0_`jrrcH03!3De%1vQ!bG zug_{Ze#3auHdU=jMcF3g%64D+xrf$>@3N4LF~)0iW;Eya8C4MBt1%wx2zCi>#V6!; z(bWp;;xqih=nOTwH`-yVVTcATd@R=`)gA~YaCAri#B{M478(IP$GH2uV`I9pyq8Pik!iEzI2+M3#GR>l7`k~fZ z4|Ro6#R+MH5<(!CPZ2_5mxBQSDv4iL7c=padgzzjG7F@)H2;|3s~k=@In z7Bhy~40Zn*UjI43#b=^7_$}K!Hwfi_k9%h^7K+o-L#5n-jHIS+NL7Q;(ty7KScr(e z@4LzP+QyBEZX$sHofmLx*)NvVcaxDg?)_#uF(DlFRr>N7>5BXP$7(GxaGZJ7_#`t`89~-F?;g@kGgkA z0#$1(7_rjEZI{q@FRhX^`r=i&yP)_$o*Lf?Le}rQZM!t?1eCy%$c}ua(EuT#aV@6(6V0s$)i0@m1Wg)e zmoA6zLk8|pyb92nbqAj>%kdvs^V**GFRKHl4A!Ivh-?)G4nBeO*tCt4Jt(SpvseV1 zsh{A=&DMkT-h7-&@1PCjhayrGSnZ8LTyx)hx5;qIg6#<(U?8c*12Q(mv9&JnS{u1E zx-n8`RY#LZ(i)L$rv2d@9`FfSimcHVl;Ybev_VM5iPSyCu@x_+f8{9tTC`RbEpZL5 zC5_fPC1?%AmN}Z}ednIxo!?yxnCl)co=`pbOi>}th%a#;FZgoM79i={GbonMgL#|M z{i6+tVrF#T>cA$Y&qq$Hgpz3j`Z60a>B%;R>1-77Keqy@*&e>Ob*((l6!;1Zq|~@j zII@x9x|0!U65t~BZO&R=+<~lZX6#i{-zl24Wi1F^(1gW?q&=}OLXNy*wYJqJ4%4p+RzJy^Q2SXZvANP;CEr;@-= zi_Nd@9ws(aLX@OGDBmdQo;mK3*(*)rIwilbG8q2XYCi=>%0*X&qtMz04h9s-_wUcS zu-PX$*_9?sYH+<8FtbC{n7vsR} zr1fk-wOcF^;yVd|+g8o|R&!;-ifv59pmz+=ZENF7x*mkpW_cK?!(p`RB6v= zXY{dxQih$n8=v8To<{SVlcIE%_z}&m-R` z#yzs&EPso;se}k?9&Up?(j`5f@-&o5kPG zp+J{povlV4XT#2IqsT9{d|s`&#bg*{XaT|S$;ll|%eWxIA)-0B%O^S;u7TBtqyQ*mO?z8CYVOA(nh6xCA9ctm4a<^3`Y?jny~tq)e~Ic#M^ z`)8>$W^%I`WTNmnAKvwW zb$);jtOf@?Z>Gd6`Q!MIDmuz zY(kX-_?WxH1RjPiYYxauX3}bTVn2)18!O;1#?ZkecgAKMB%b%y5lX;>;b|;A)+Cz( zzk5VlV3C;hk^%fo7%TYKGI-6%uc4Kda)q(E2H0kc&~>eTG!o|khp9_vr=`anlu_!R`h3nAiF^vqSeHvAbI%R^ zF$CG_q42`A4k=h$Sb-=h!c^C1KCg_fwHg!#y`iZD^y0S-Q$Q(_v|>&3LP?V2o*B_| zHBc|+1H!a2MobNIV+mk$=37ecJeRi5b-H$()yyroRH-;BFKM^XpkO2oHQM*qLn@E! z#`0VSh@DdY?`OhA40$MRCedh_5q(69q8-mj0L5*;c4a)AGsa6i`YR;0-i&F0gm_Y{ ztwl%1v%0J&eKUzQwOYXd#p|K(t#pYbcSNm#n*n{xEw?HxDGFFf+3avq_qUxv(GsQ+ z`;h4$21zf?&kSezCa_Q$Wj=Do>M^QXO7Y^l*<4GycqB`?jy}7p3=y-$RQs0EIY&$Yp>rpx>Nl=@K zIm4@Jqp+psKUMc&*~}us#>C#EpQbr)PSBqJlKg!}#7dWn^Dt7Q2SKiLb(~CLqhJ4A zE-@DR_kaG-=Z{6}OXwf}{LugS@89(E*FG7Ei|E&XT=dt!HvRgKbsGAQ(Ei@;ab^1X z^FB0chKGXH)a&{Z`uy^|!d-Bmq6t#-5lx^*lTS8zkUd}yJ?6(CM#dhwV0kksHM2fK zvQRqN7Vav$El9cI?%HW1@eIyHB%1Eo4k{N6lP{RBE z=ys3LwifNf{2a(7)mbY_g|i{-*EK`FrVfuxdbOazh(;Jq%wfKewHw`q78Fle}1$Bahh*`*aMK(&9LFo%6`5)H)HE*BJex30=!q@ zE+dksdBLEdZaJ4VGlYo%rRz_l>rV$a@r_#_+;?;4wdklo~4DCv|Pa zwou)!6pDm6r(;O6d>d@0bQ{s$=loCp0($+V8fc6NGWSXemm1-Yjim#naMO1rxVr%# zm(=&0_asb;lk|_7nv7obvFSH$O;x;}s?5NtL=eqID=+*VeDW}ac3X%2DkSog1^ zO!c$mypPO2uIp}_(Pv3Juhn{yd98J_L`NElSx-WUk}a~Ug@u;5M-`)ztfvrM!r2%b z@G!!A9+~BPYZ0}fNr-%0;!+?O_*%C&Wn}hyUixDq}br{b{ zB>`1PBw!j5kKM7{m>!=cU0zK+)-6DQ@wxZhW6ma`TOeM_T%Mj(_6_!Fq5`xRsyi7Q zP+U~u`>!CA{(F@M!-Y}f(l?>``sW^!pLN8vXG$*;n>_%B;Rr^=A;~2@ea3h!$;Rtz zp941AK<`eg%iQH5aLND9ofe~>7axBl1}Hr)uKw(8yOt~5nHcdLZlCmw`@_1OM%>zi z3k8czG8pXG7*HeQ%U&QT@Aa_RQyQKN8)AoGO%E0tz_?@~IdKJwF|ZA~L@GR)HVn-+ zmcS5KQH_W}5HwE)0LdaUMUNzUGA8E!C12M@Q=2zcG#`DcfNsLve!tvYbCO_>vx0M@ z7gGg8f^qZ=-oP>BC8tj+Cn_~n=j9t)mnmd!*g9K7xtP`^XRG<66y)#gk^|egjd*EF zbHD$6R|BL5%>ZSCy)O5MtABR_vLV5Uack3S8k>Yv`KrM@j2{~!++HzbqyxQ}AI@jO zbbt!@-E$5&iX?~-6e1^chfgfM5S$PpS=^}6wmF*{8epp)N`4xyU%#$=2(0DJ4pp{w z7GO@>4F7d32Kvwj`pUhS;>Q+E0j2@X z4W1JyRTXlItF@_i^Rt*w`Mp_Pi&MI;dZfmfv(4ziaU}`rTl(;mi)hw$ zsm$j*5G_(Qvk}W8UGA}h>3>L&#D|m-^MzJ&%Qzm~Bz5U^+GY&>&GOi^%ktPKFV1e= zK3q7(&WrBJ`;DRq&;2e;YEcbA zp$+RrIxb)58pD<@+{5S_@F=8#mPoHnTcw3K|VYL4c z(f5y*!l97{&rUD@8An8*jc72fXB%u!lpbovKJxaBCyRSV8RWY8y=!aMbEpB0l(d*9 zOLjnUIhb_oi{})#eRgatS;JbBG*Xk{3N#t^Xs)o>VIsQs20&adc@3K`S6!WFbZxaR zp%x#8K%unQ`4&oH)6t`SEaRbi<8T^njY>m0ahJ>HLrdHYBh?88^sA#tmO=$C5|g z!U;};-D2zju2ebc#gEu~@GPh+%i>cGXV=eG;3jZcv;nHd8e%GBtQC{&=un zE|CdzdVYS-ZuB>eDgb)@44Z$y9XOJ;53p3M zJ?xqeX~eHs`Fn;^v)#C(iGsYC_7<7lgRNdIr*FZ%c`}9wN+h3Z0;52F5>ebLGtq_uGbKhYZLsw9Ot?4@H5zjWRXn;d*oo1xlVN1<0)p+8#*qpk0)MNJiu1tSC7X@52BwT;gf8+oOAWWp)5JqY;2T!Bv*Ko zwQZ!)7Z&n7Rh!n~dWOLknhCRgo&HSPNYSUy&X6CM_45Nj!)$GXM(wPF!qkZV{nzcJ z*SZbG2TLMEzdlzPg_e90I^ z$+KUlXG5VQ&(tYC?z0ZLFeg+c1%XvuN{_-R#zWXjif)`SWe+I0=+2f!$X;j*wKm@}b|FHwl*>>v^G*wxrVGI;hSqvPUP6Ec1E zjpgkgezD?o)JEB3(sIk+x8!Bfc}w!(ml_|JbNnE38@~-pXFQSpW}V4BAO-m_6QqR< zpJrhi3?v(LZNs{pQh`&;Lw?u|+c*ZudNz0hon{#4L!5X$rZZO}k7$_!zA-Fj9`0!v zCJ2BPo08<^)4k4lX8nUg51!2r*l#dB?r&uRpGa(jMuC9xM(X!Y%W5>HDe^FL;0}O^ z%m9F(=l5}%;(&MtdQl!(zEd9270zXL&*2t$s?L+J{406TGqp_FF8^h~@LLOB$8F)s z+vP)y#i{heG)x^hmUoKMx)#yDH~sma-(>&%(E2R;=l}dgfBx&H?|<)-7;r?|**9T7WY)AbJlVn{I8Ox_nI%8vuJzhn7NM zT~b>olRRtk0A3*Axu{9lF#-qUD55bJIX>gu3Q&PU%&7f0aGBbRt?wJ8KaD6{;3oE5 z@*_t+*LrMM(d%6g5u&xO93EVY>H0{LcS%n}8V}=&I}3o76QrSm4=Lbd&@g_1HL0sXIojmA{zD#8Q5INZ#BCtpW^) zII}Kv2STGO`eZ#|uDiNvGKPDZ$Cx0ZF2!t<^|3UN>YtDNG2ue|kRwJFZ%*o&5m+s9 zE}kI_etqgP`aixdnhQ#7*v#l9Z=3-T1`%1-uP3!ZmyY)(hhmFybz$k*c9aVtP+e=$ zBOU`HV6Y-lB;G>drS5A#g>J8>1*MGWtMdWI!X3#QF&>^xiRgiAeQSZ2+8|dr<(nC^ z;sbV(@<0A;&5hw1=tvGYFk&iwd)zO!8*-cDrZ@P^gZ>K{TB#OWNi43x%TycKE2Y1h z18qfa)i-h8?HpVlzqTrnfSKrP$pMvtH&L}3(A%LQSdR)c_|cq)60T&RTpnE*jBSvP zSEjE2Z!O<5gJj=)*kHK02+A}99`Qc*20mo33=-F1I1mwit<@c%hOs)>RG!y!fl7vr zoJsPw1hkZ}&*s2@tiKiDNj?OzU0&5C1?-ZRFG~;QPYiENt*b0wkkYUn7Sk`5fm|qD@im68;@Ms2N%q{M^`pT6=!-E17vp zd*}bcj?A=O7PakuX>t=1)%<<{REx(AL;WfUgyZM7pJ=2a*T1l#?xp$NgFK!qgSOtA zfg1B|p_@|mqC(r-@^{F?s>>K+bXw-~L2E}+3;>;)JBP*cg#5bGrM;gA=}=>`FQwpgSc^JQVB4AQ6UczF z8)l;G3U7y`96mFs`7K(&9~?S7B~c7+DCCq#u9DoD)CYS!xEq=hP*wyei-)f_>DA0*5)1jINZa*cPspfp8oUW0QJkK+bI+qF4*dIsGG`cS zSiz)XxFaCvV-=!dg0z{TOM zS$c2JwXP@2oa21AR=}XdzaT4PY7HJ9Q!86c$h>F(nXG_Z>ARzI-v?yw)imX2=?@HG zaMK*G`SK8G<}P*-Sve@F)ul(s`Hx%aCFVJIs_$w_@VJfqvXG8Q_O+HjM=UeQks=y? zN1@to8f+MFXh@ zEhS3TQqq6(`YZ-9LXsKGYGoaYrzkVVUo}~pZ+6c&choqZriHvSlr|G zdiHez7?BEHZVLw3E5CgGT?L9ZuHKlzpNrw0Vg1>aJlxhKGiq)ays?i0RRlM_kzg(z zJ@@R?;ARnT$y4 zR41QTeAVF%8&NHoO>V>#e&3?jB*vCZHfL0Z*_%=R4Fmu~3Vaa3q@OY#!heR)$yn4= zKpx-IncTm-bNhrOHzQ*^jge2y)-a?r%>0->a5LQWH!s}mD_vF+bTa@{gj`d%l@|jl zE^JOiBA8nuA-cbJ%0+b(?VnBG|GLk1Prx&(?t>qC|{kxH-;XErU(c&I#NPN5TI5o}rvA-3W}7nC%>B(c#5 zfl^x&ZS%scOD$1)A0FvB?KK0_{ z(|z<0;<pol}7 zthoeJ6PRepNWjAV(A3YpUz23t_?a3B%*9xD(lo$MQRVDSN=q| zHEUhmx7z)>L6kQJ8LWTsw_pDVcuMKD9PWZreGaowzT+8~gLpZEPKoasfpF82gqt?W zIz?gVmIE9b3a$i#Dm1t_EO;bX7F)vyt2-m5QtvhX64&)*5K+T?gqI;vrYCTQ0a7=g zPr^k$zPQV9TM7h1hVpyXM)<}}BRMS~Vm_J9{UZR*Q%I^<^2{#A#I+Xm<3DhLXE!PG z!nCYtw?XHSK&{O7U_c-w51B(<1B^CnjIG|Ew2z@UZ;Q%X9eg$6kI04%)4$Q-+;$kd!cgmmt0jJ@)L;fIZ6AUb4H9 z0Y6^w*crwQS;Kl5=OxDmn5+0}hZ`P1j4D@u!K?qdv(@Bx3gXMf#Bkvo5P8X?MKVuT zEk_XnK4D?9i%EWB5-yd^_Ueo7nV^zJdLBXCR#~Bhd1c(EE_ktH^u)Lj8^|QQ7!c_b zFc{rc%$gZpUqbh{(%wh`wDj6Wa}m-(S#?pxup)e|vQ9qm{iC$+Q#9;5Q0So9iub!a zoSo$~zw0CAKUHdtuT5{jkbM*kjUNxB$OJ^=Kjzy-gKTVlO5~S?hE(4Vc^O%;k+n-! z9+(P5$M*u%INTUnSDtIcczAPeu%Td%9AzG#hZMyeq_hEUi(-h0Qx0wRd`Q%HO_)WH z8&_s2l5zOu+9}W#=@LnD&QqWk`e9TyQf39M-sZz_D?OwEX6jKzlrz z!T^B=Ak6H!fg~hiHeZodRxel-Rh1}wW+9Std7qlPNBIfvqomZXF_ge4*B*)|#2t7d zoZm&8S}7&redSi4Qx=H4hbyAD>?kPWSrgm4fP~FNL-J^jGUCpI6Si)IPX3$c5l9aPKy)x zgYjZaeZv^)35z0rYcFP@<}f5n$Iv0Kft!ZGfId=jRwA4rNJ$AgKO~nJqa`gD8wO&2 zJ}TClF)V=Z>HFRlkK|jqwU};&Tu<=n1|y=R@Gd!@z#oDJv`M}!{nH0hDK%J`n_4PZ z+SyJ^@?laIDXVQJG1btB=-1~f)duRym_}6Q&Yh{{h>^S!dwSuXuTSdQjiDfex@}VR z1C}Y&mo;?`o;MLW*<}l~!EE5#+~$uu-fS6wr96dNt=HB~=XoRw+hwpIS`8oOCfwS> zB$OYTEDW31phqsPDE7Gf)8n4nYLKwPHME4B;|>JwUX+xp{~hp-xHE*NpJl??_%|}% zwkCWIJ6it%a^;Y?n{|#^D%z5@+$;CDzGXZjy$`8qt8z<~)uQis=O;53gw#ou>~&53 zN1BDKK#lMF-&78VDuL>|=7@;$v;3O&UCcTppu+~VA)|V@HX*hP|H!}b*3K{42%$jL zwB^bay|-L?@X(X65G`Z+k>kM=i?|Bw;5zPlcJ_u^xsxI(^CY{Bk0H`$N=oMHi$_m9 zh9bFXM*zY4NP0uen;Y9i@+BmhN&1-0=J?eZ9g${VDf z^@~yV8r223>7b65o=#2M8T9#c42GY8xwn{1!3cmnCJfs8XiGx56Pdp>X` zru8K`@h+QAT@x|vhhV0Pcm|`eX9k`(5joB!3#FW(huk=5^e4Al>U;7+apmUw#FPA} zZ>d*#StBax__L*=zqdln7kBRg3s1k&UR}@!(h#(5zVixYTEjy z-!~Xr6M7~MyX@O<)pn*D$RtG|wh4T6lm#k#WR(E`ueQVFIl&w`mXHw~X9fC)ACd7? zu@!L*FOTU_GGcX!J_nQv0A5Mp?fJ(F1+SGy#+wrXk}2l^;Z3#~jfwzyv4$U5BuW0e zZ0w}CAhanx-x+}g(60a$Rz|wimdz>N2=9swjapwF2q-ZXh7cLYMoK?^PH$HXN2FW~ z5m!b;9W_TFv&2xf0Rs@N2qYy<@EKG26xB3ili2;M$`D%=fG#+b7fD{9w<>+sazC;9 zM$?P^NJNroypFPE!TIo6l95O_gcN_s{%p+wt^klKyqFQw7S|X`rk}-IL}b^@n)Dq8L|3+#wHBoR9zc?FH{Hm>bZmlTF>=$Z%w;J)JdpFd5e&W zGfiRr8hW;fy^4eaWhl?c5W#zWwC3IUhqHhA3RL;8?Hg-{uqXXVuJm9!oHmfL$__ul#@hZ~u=qI0+6K zjG!n5tG%8AA>lk;jHwyfZFGO@P|A;i*Cd4w%Yl53OfaSFT7@>rfp5hZubXJ!;d3Lp zeo1+DtcYo?IfVko-nH4_Yk~Nf0rp~+F^JS!+3?WyoRn&vJdZB4b(u^PCW& zDZcri9+{bzo;p=JjW&}LLLvK3x}@A_eisnQMLpXyVoMrfn9hb+B34Ksa}?>*8S`>( z(wIdO+gZWozAUW zfgF1Z*;s6tm(e}nZ8mMz!7~S_yyF)af%<#J-C)?hqxjEEo>sp->Tsf>(b_tLjYDDm8jj6X8Wak#) zblN%+zC};ZTotoAd9-aq&gjR(72fpz!0-56;yz}jC5O$RYNu#4qwA9M-J@*)HnHcL zDNkyg-|5Ly^#+P-kO8G;;XmnK**wewUmqbM%mU?pdmTeQ#<8sjv{!8_Q6{4?J!9}ge8 znw_DBF#U3MQkTlkoIrZ$@yt4xVlti(;`)FLje7Nwqs~1pk*AWBIyFAOYpd%PhnNaY zs_<{}Z%1v2-*Mg_Hk&^NhQUxFK_pddA0H<YJ;ub)}`ugkVUaqTbQ;ucHPb5dP{EYun^mP6F5ErC5qaBXsd7DXCOl`u$O z3tw;QF*}@0q4`_za_M?zDuq z6~+Ucp-ALGUWo@i8PoO4BaPB4qQv(Ae1v?||DK`p5TZxcbv6^SFd~YFG@hF&-neFH zZ6do3Y3AN^*+XKVGH8n_IDiDg!v)_nhR0`p7sS;l*c5Vzfs$!V#5$lAAk|z~3Z#Gn zK4kP3+rqQaS|arMT6BFD{nvl}m75uhc${5PIs(mN!R8JiMK}A;qio7hl07}PX59X} ze2^s#J^uOlyt?nMM+v!P1R8kp2q=kdl`!2uJ5QP%K5X#sae&~tH9mP==xMlu7_8m^ zovr2~Sq9)42Fcg?d$IA&(!XaBG{D1S1e!%IXM+??4tgyOJbt7?6_3O8{jn=gY67(7&NGh)c@09in$zX^hDNR>j)+2zR{7(+m+7U{|plqy%VQ&C0g#byl< zdIJ&agIE(SK=bmbl$p#W=olMT-mh#NR{Z!?Uag3a3I7Gtv*-XN3?95li@nvVNhAfi zX~dkg-HpeR!7>^oXbBGtubkC}wW{+oYh95_!&mN*`*dE_EyiU z_96N9A<6f)ynoW#Y({3g^4zH!nS@z#ZK+i7Erz0?56TRliMaXAh(0^RF!SNX67JDX zgQ8;)6>4o5P&Tbp_9!SR8BZZ@gLQlS9`xpS?g4U|=7w&zZ5Y|aL;XYGna*gKo-q|oin42nvE!#Vvz<$i(I@q(%A9dBSnP}%)NyVW|0W){dHKz^RQUOe@b4b) z)L~Pz2o^4s|UXt^Q?wecc*U&80jR zuWvo*X@Fs~l#&*$+q_55aBG+XT*J|Q43yBzOP;qF{hV znK81*r`VrGEcfx~`sOJL7z58KZ7M%CG!Hd(my~hpUvwmY0$we%Be$k%lET*+o83;w zpawTL8hn>chM~ zE=jEYZ2C5$fBld9d^g)OveAd=o2K7+jAI37PK5-i3{Sa|4< z)DtB@QR0hlsZ9~8Q$J^2;>`_s+;#w=j-R{$a{Zt^Dgrpf*`PS=MDUcYE9KC`z&j@X zv-Wl=ulInRP6|qnL7b<@!3Z?E+$wNJt&n>|)+Im>hfHYpMliNfP((9G{`E{BvY;z3 zX-Ud6Yk9*aHZ}y?+CvwiN+d*o{Q5vPa9LNjDcQGgXW1Tb3(HP`mJHP!PbyZR%dpNh|5gmYbuK6K$RXAQ`RWRnz0# zwMWjCH9$~cq;Na*qk67MP~0%ruu&EauISlBzEa##uLcrsY!-yi8M>W#$|R*X&+S@C z^%!}c&~^95pcEJp8O-8*qVYu%%j@uWti4l!x|EgXV24% z)vui{hGY8K$dk243z3w3Dr?IDSP5MS%k!NaNoptw0x+x=|B|O zG;IvV$X+Cdx{s-e#Bu7*8cn;;O0gnq(WMF;@1jB8R?(ijsh*8F+GOt%{SS_1rr z=NZIf;LKyi-j*})nQ+p#r-EhRhvI%5TeymaajLm=9-?KG78@?g`_z_76J{74P!Hv zj}#{)3)AQix<>U>dap@&se&z_G|Sea3GZuDg|jjWbQK9B9_Z!voY}eW0ZjA@S+WXE$s%{ zFs1Ir2P$H|e=6wGkv%&(8vJCsnKoG)KBkg}tMFB|+Ln*}w)8t@f#OO4c<_8at9d?A z>deHs4PID!aF^P)vMFbpoVnd+yJWqvj^P)ngR?Z_sj42}M%S$ea(O-#w%jy4=+l{l zwYeA+`U*2Xd)gz=^OffaRn9BJfN=XpS@{eVG$3twX1KJSDG1i`zF~7IOAZu4Eu>Bs z4v0_`?-79!!>Yf&cZ8iiQ09Y$=g$MQKQfZ557_cTp z4X8zfe8h=G;)3DU001!&t3<8wTjc;+ao=WugtkoQ~^V=^YT)!(m1zB6pNGlSai zXvOcOw!IRH08}T12Y=A1^LzC3V27QBt1*lF;KwKLM(-P1@%I%w|m9z9*) z%^AsmW9_JW$=<_itq*=0RO6+q&S$D`G@G}VncB*~!Lz6-M1J1^5B~u=v$@F7eLD^{ zgVh4C%gtvm)rSr!Ur&A)kD7%Miuk*B`Yo1fCO`3ue4uK%%OE7Vd5-3)9|E&dVHVLe z<0Z)D`CQwZY3mcmBmx6zIg5Ya0^zjYw&g>tAC=Y`Q^NewJZ!N+g*?CcJcH?;F?gEB zO`s7VZ*O{ZgU`SPCARBp7id~D0w}gt-w|IB zvgF$QN`t6WR6<6^I7JYPkMzK?CS91or)x-;OgbofDPBCrQrGLw!&TG&NJPXNWn$F* zhYQ|6ANeSed5n@58D@+j>cp5cdOhHov{V7tjA8p) zes7WkL85@7xZOjvh9CP@B5`SI_{id(AkA1NN=YiWgS|naP$~@OsT?)|$uRsa$ESAe z9zULF8Q<5Evmt$xFX6B%0f$JuOkZt*p~yFGc>R7|QcXdN* zK}EIZFXl(>QC=m^c(15X)lfU<)M%?g#wLAwqyTw+8s+jTW3Q3mBdq~#OM(T}4dXQ5 z&(Gi<2+z!CZjm4)8czj)_`m?Axc)_bTs9vb>1J$vkZ$Ye z<3LP!rdV*Z+mz(voX5-vNrVAFi;x?+q4xW|3^$YB^bropXOgKk<+@Dfpv$t*2*mUl z7Ps)L*GUY2?Ae&9$^V;2ucz%mW;j)>l@)s;XxPW?Q}I6l;{}kKBridMhA*Rd?=0)S zEyEmC{7yBu=$5`+_QB^Rbp~s?Dq>4~hCo6LTW`yOB71y6MT>Rj0>d^7nFPTMiOk-K*I(Pm2J-kaS7NcBFoxp47K|N($6^S{^|I9;jD}^+N|?U5I#g?v``ts^t8J{ z9mo(t7nZZ0xehzR!C5faf?>aT2Gd;6@T6LEIykB2!_RHAbt5zX&DQt-&wt*mzYnOn zH8@s`2Mn85lsx~&Z~Xe}-`3d1!{keUxA%r$ZJ&iZ4|cq){J*)gE8SN zJ%Xqevy&mZT6ipYc&rWRO5zQ~XQCpAat*QaJ`C8IZ&MEl3kO`aTPQhSS(r@{HG{|BQpB(7##xzL3nXh%_+9jqBlx;%D^1J z&3?~#tkIMIJ%R1To_`O|H>>L({8UY537+-%%!~YakVTRCtYoxg0R_4SxNLmvJAoV? zlJ(|oMiMF+L>qRWRb>1!Q{_(Q7EK*;wUR_0&!p>;+&abpqbxdB#D99>fX#wx%}mkC zGfx7t#1Igne{iDYV_Jd9fu7%fCfRdrKDIjC^6A?J&_K}!$}bI}QK?{v8x*ol*%@iD z+|DF?wC{}Bi|41}i2g_jp^;^XL+dq8jW^gE(rqYdwvod+fnD~@3n!)}V3**ylO&%F zcRsKwH`4Vpb@UL=MM6Y!o+zMF^aK|{^?Bx?T1g0b1>7XChH~AYt$RIIu)R}^2aGBQ zNZvdyD2$re!If+aa^f5N!vFX78ZA;8)X&I^DN_+z{RJBi%O%NErx^D5iqaowQ z-2DXaMFK9!N_#47q1=$p-H6)A1*?_-8;9rmLJT}+$>}22#4Yc!bJG@eH#`xYm z*IZzrXk;M8H?ZUJ)8qTQo^YIZGWA3JbVK9ypNkgiHFc_RaKH>3?%t%o`Ra|{Vu!40=tsK#l0nM~N;zZtxd0<5GPrYc|>r#?Y{8&%cBhzLjy^p7PALCbU zFlxhZ^q@U6#GN%y9YSSbS;KRd2;RvYlY4B9$K-5KI;6baH!K9i+>BMQ`fS7{;x=+S z(;LukRWK&jhKlT)W_goHaziY2MHDU`h8kirA65)cseJnokWq0h z5eeujmN(g7o;~pT8zgT0{0~*XSgXZV_wn>VAv*N7u!9>Ov`LIf1I)g~J*tbv?^!|K^Ee6>$zJ`f@U02;J0`Bh^ zr~!Y+oPN#F^eXtM?ZLn(pqm-gC7830td`#qmhtRuA`X6?X>qHuVZ+^&R&bY`8hp7J9z7ne&L7 zSX1|#$9chZ&}S|umrqPlegj1d#})(L!+-qa@<>^I-$V5Z>?*bA*&c>U#(@y&7)#Zo;j#F z=?@Hz01k%@f(!xx5>(^+bQGse9JhtNA zX*FvD7Xo50DP_Es&5P`z$ny8W))Zm5i5KQZeU4Z-1OH_E4#88;#Jzc-+9nyuc9#G& zj5G(-xB-hz=OTw#`n9@zQ-ju=J5+ctD5AW+hZH)b$+t&rZM9@+TrnWaVT4sp3CR{^ zu{+IsGF7Ax^c5*Z5SRE&=*3vCsO22yH~>@zTluv3JqAzy`=KAeXE}lhbHo5C5|K;K zj_5ZU@J|lQ&T0>r@@<1i-iBkAi(aMld_eKvq@~Ud&)$5|qtSc~sGvH7t@S6vCWJax zw3@W2!50}0+jAKE@JaFerW<-o^uvczDSoH6j^<@i&s8_o*1c1tAEu#{Ysk|l-L)fQ z!a4@5V-rk7#Q(?t`hRCn8d-E9MLb&;e$YG-PT2}hnEVh!!^$>_mQFcNe*siUZNkc3 zVoeafY;(iUbLn3|sG^_3BsD#1^!M_Cc+g=P4dmeTs)1*=ym0+yFz~eGjqH9z%o8*s zy1$k74`_f5DBEO-kfH4-%5HL-9HDY^XtD|ukfIhy9jlB zyFs%H1x{@I+YH#yyHJvoeB|LKf`I1i5eqEbbM-<7>H|RCegKCI595;J(A9z|ef?T= z-{!PkdytZGONbfR3CZ5oJTyN%UM^DEzvO<(ltjmtAOJJ&+g{h%h#widWMgjD?;lzG zV&SN3D{@75BKiT`dYz!==;nPu$RjSIHjq*GECe%AUO0ZNjLwoz|B{6P=d zu}apm^~|G(h)z~-3=cxW|Lbe{=Vg6DAMzmg8#AZ^a5Ek`>p^2}kxQ+Jrt z*|>ovCe;PtyQRL!AZ@TWXcC&omcfVIi)OQ@unZeCY4qe#w1Pz3I*QhZC+8YWc`0=Y zZstmZRNue@EV;evbw_+p;W5WEY6>6}J%m=&@oNb!dD|Xy+x~8&mXjO9vwTjOJv{!n zr8lr^*i3kE0`{aE(5Q`=OCz9}i+uk1wSbyErOdfy#2p#sfU~@w@g7HB@bDnl*0tWB zLZqTKODWo)-Z$z0ADo9dgsGi$ZT}0r$b;fu)b*q)&kXF$gD)ZZ1i0aQ@BD|;XSLMa@~)Q8nXmvE~WbD#=Vy1$hZ8#S4|*#Rhu1qhWr zQ(W`+T`aUo>3eH6$Z$RLu%9qA`Wi8mzAzD<&whK|C_e<Y+i#H3`Nehr6djnu zMsu1C84-QH)@eW_ zWQ`TcRV3-i4ivJhXuq*tB-$r-2(ME=h*+bgF^vG5^6Z_O>9xmWpC z1OHKNzt_kKuRK=+*hl?uZ1Rkle*XMv^0G6PR4>fjf4(gbdydA@ulaRFb1rA=ud%_e zm>$bvA~!m*7|~zf+H4Umz}*{q-kN-=@M`BEgmG)7bZ;)z1$9a)Dzx_Y1`EDp&G2mW z`3~|qg4njwf{f!nOn&hMPhzAI?bw!u_tt7)6!*+)UZPLwr4e?!*_o|>SD^}tO`aNT z+CZg&4_V1brK0c|_iXZr-BEj%x!01)F9$n`J>-H?TX;Hj+9j5Qvl;i%2-4u6o@4WV zw%Q~2_rzpP&z_fGey=wx43*`h4+=3^fLexomJG<@Fo4+Eh^m94h~V$06qbR8`Vx*3 z0~F(KDKhF&g>MA?tU>(wtBSre8y@=F)|ow@Z>ncohY>$T;15c52gj8ko3imVY$D=X z04Ni|HaMSORD%@UV9zV`#^2A>(>kav2YXW%-|?u``~&G*&csH^it0f;r+ z3vZJ!roYpn*FU`V1;GtID1me5Dy=^C>@;k4W{B`;i1}%??M<%@KRI^;I5T$=Gk1a; zf$uNZ&rP$8-dP|_8b7u2?jpx%mKOgr*Ope>198u#wOF7MHYs^DjDSUs3<(1zw}Yok zUU0@h4P=lg4kY`C5!yeR5182eqLDs}f!uz(Bx?ElFeTeY@^OmhkEXOO$+}z{)p$S) z^Ya{l??$*l7>HI!SuRtD#V{QTqQQhdOn^wnQ!U@Htm-CoL#E|o;)UR-HkQdlctneImcyQ| z&#LVV6RBjPjT7Xhe5oMlw&CzH>Qdt`ZL!J#3m z1_V6UjDu@`A(}(Qx*3|=-v6vX9hc`GywH&QwrAXE#xcvdXMbB+h0Ah^*A1N4l`!e&SNShLtT3U(Z<$i2tdV(dPViU%d9Oc%J zA?f$gHZ`#)$oFSb$(uBIG-e1Lwc(Y-6b7_pQdoTgxTz(RCm$4xah+#W(sY6$K`44U zYoUhlW<9_CwFM3@eD-7G{#R?+?J-DaucvT;g#h_T6j-2EV+VP%xP`->SC$PM=)=EJ+Fg2cj{y^8li_B& zJ!GI53*RBYwou+gMvo0j++cF~dssqiZQ3{VnXrkfaVtPA z>WsbF*im%2oZ74pYFa40UvI4Lfh2})B5eJp$B0TsjQ0&U5_~ne!9j>z(}*ToR~2YW zQ0~YnaVyuc|zj0wHE96UZ>lW1%iWOTK;2#As#aB;hEg}dkfK`uqPurm_4 z_p9h-WKF-vEBz-DO=|TD(gUQ)cMgg)iZ=_o^zJE5&^#uBt z_r04vC9+fn8s^-pzN}hW25FE|A+y;O&r)vrkp)H!ofL8LwUO84h5kbN$pdMLpV~qD#8s$5J7{!z%_P{W$NT3E&(&P#a3TWujJt zAt^01kQ&~*yM1s_AY^UJjRV9u=XS+cybCqWn%7woa@GEhJ(w+&Q9tyu{i4p7q!@xz z8{#YVO>t8o!DTWf=;hz%w^!MwHI2j5^!r`%oL+F^Ol4a#$;Flp?vd!nfERqS+?&4U zLw;;Nd8pIq0o&q{fj(6WECNk_x7v-bA4v750aAoE4Pta$$Cw_HZJ!{Zke;i^F^u>< zG5sxZhhGe>?`zVE;~N;brYMD&JL7il<#1;v!^DtGifAux;|%4D2-6+XLAVlI$=Vv> zsbl8Cs=qHFN!u*M8lQ!8V3n!xU{TOE&{DFtK_=?9g0_>7p>|6zp>chd74PqN!2P?I zwr#hroI!p<&Zap5w$@^%PJz0ZL2BRC!3eJ8$CCty;YJ-kJQPc_&cON#n(T{U;+}E} z26q#L*h&_tPW@JHgfH|LTBe@E&g0h^*v}MCAw)*$Pb0BFipgR2d)Pm~c=H3Vm z0uBvs)fwgIDBC0=i6+XY^rW>(T@I>>>nyenvF!^2Ed!7Q{UdNHgEZaR6gd7qQl`-_ zB`+RY)gqzqJfNxaSjp0}w+D^V2857@m#ESM1VCm1yssetm4D z8Ng_G=+)M)nya<^JJyiY6+rDoTv=s41}0zi*$52cu!P4+KqhY`Ro_Dn*1#9BS)l-K zjp#CeZ9QnR?#0QF&#N2>e8aqE)U-NDo1_8ZD-7izz#e zwSvEKc(}Yik~E@HAIK6~p7V^#ghW7aA6A5%4Hq>ha~(iAA{o~c=h@e_n%5|wfnJ2} zee&d(j}l~S$(vxPg&UM6{PMY_&yBgCWoj@K)ix}sEdW(g0#`w8I-`*~LD;PA zmB5#ZXH6+c;uh1GVeIofWgI9Gs1c}OH|PV$EaOY~47&!~AO=4Ck`0QcxE-HS_$-wg z%V3zg=3SE4PYy<1-dsaB0|{nt=mY?C_Ck*3k)D#z39Jv$`zM3vQ|4YTWUb;~Y5?ML9M6aZ zvJo3P>QkG5&*yA0-aA7iz>Lj$J>SRuW-lPnQOp`k7)2P zhq^A5T?Gzxh*mz&gnp-IHlpW75M%V-B9bf=3`B2}Gkd_9_;pNh`viokt7#SGJfL2Q zvq>SCfd^s!QJnt`)*3y>#{JpbWb0h2JS7-3FarOcI_5wSid!XuKtJQPCfyz5Jl+>? z(BN_@oBMs&WziRDf6#@W1ECi@OyP{w=B(-i3UyZXnT^ZC@6mGEpeb#h;%Z(?83GjM zJ(uI$*I~Hvk`8>xhZRa&TuzyVGYfG7an8m&B138;Wz8nh;a)tT2K{MlUKQ{-Fix>! z{ef&PQ@Z(~OQwa>dv20tdpVpAF`9BykP~p(Gr6faQA;g%o#nU;$ zne&CA^(DD<8LE~v0-I|5X-T9+xS0~sI@jD*){+y#peAJgB~L#jQS{Rm3B9)G!}58K(S1x>bvV1_K_MGhBoEDK^vu7< z8W4zsFz8TC*~ZpXbE~{>h2nXT{h7;K%jI_$JI49G6{BqY3ATVZ1&u9jz=>oL?G3UZ zltSwCbwabD<6*(^Jsw|AK+R)2JN{_?j0C3}$lFm(6S>i^5+aZ|=aja879qzjX{2QH z88#6eDh6o8BjP`>xt+e+kRWd#)TQh@{N*O38*+bNw!*?Ro>1`p zG{Dkm9{gVYT$jsTk>-hTA*pSolpIjRa~?N*-vqgptSJ{q_fIQW$l5mg1r~6wd3$+p zL_q8Y5t&O?ONB%9L{tEnCz=hJvu%DrMjNQhN+K9!`^+g%j)o*zJS4~XS<$d-#kN=B zyA0244qjj47ShN1QtNL$i2`Cg^|$WVH!tK306i00HWfu60roR#g5Juis{ov;hCAeu z2t|5E+&m#?iV6%OTB}riik?257B_oHCmC!dIBY}<(n?QYy*_~rs=~K5Qb7+Rfti%g z$w*ct@FvG(YTsexnAUSxTXSL1056g5jfE|04nz3GSkR&^(FV}HqvyHs?szn?0p_r2 z$cI_F+$<&Nr0)dwM-y(pwNa*rmivO@)gwF2o^y1>ZyscjzfxHWwE^3#`N6jtY`jO~ zOZB{qf3kuqhs{~ld)8xTJ z!5zBxHZ8<2N${Q*;>ZPv_>NrD!-X*KdMkfTY+*4D$6%Ce+Ow2$)}kfxtqHBwnE7#G zbonpR@QyaLXpE{)uK`<^0kA`POo#ry`3+A~awe$f{XT-*7njmpP_^b-eYMfw4bqr& zZ6y;-QzApf-r)nr{(911+kN0E`<9up##yxA-&*lR%ilmypl@ir_+&ZM)BzY0bfK0di?0o@B?cd6PVdyChty6U zaO2v0)QJC{tzI#f0fj{B`iKS-K`#W^#5*@`$Nwx=@iQ#ca*h4QtA0mXvP0hg?s#vn3_D9^Tu#%Ya<5%jhH4Nkq=Z0UA#W z*poxvPzpp;Kg*~hXyWr!7}>{nLoT7G5xH|b?}vb_kgXSRGI~iiGkS@Di_A_Dy13gH zVhCU`2q%bg3cv*q|L=^s=rTtnCHG<~zt1WE&6ip}LOfJAIp_~FM!6IW%zd+ne)((= z*oFj)D`rUsc@!-<=8&Tq`Gl4V1_>7vEvfX-$mBi17fQ>(EHvoFXPcjo&?w;Tx%t7P z82qFHU2Ts(#7xhc!nf}W?@6g=ZBvRcSQx@v480*MSWofjlIQdo#%t0h%?yez z+EJ0OF`SWk5k&LZv(k%_fvUdpHCRjez^*``d9&u{t#~CKmKp*@b@2bMOj4W9+G`j0yB%cju;1Y|C zDna0?CHMw287=d0OMVrgmtwety4u+ zu=0taStq-eeS>AVUD#(kbo&4RAOJ~3K~zdVip1Ts`826RR|pIKfEIuhCJ2~*Bgsc{ z(#Jz~XfGjY*0vGH18Gpam2~Vpc&;z$G8~iKY_QOYsR6$u1GH>!J=c>G&`=>BMhmo+ z_YPp`jnB`(i7k9UXM%Sf2vbD^aY~UTr=m)#lCL?prm;oMNRx7tKn_smw4Nrvtd8+r zV5ar`V-+*vlG=_KP&!#mS45Gx=DU~;<`9}vPDIu* zfka*-9*c{45XFcP5VQUM4QW>pbboza)p*OyMGT4Z`AoK+`yeFf0$?_!KCu?Ahkl#z zL-b5oR?^4LMB=eX+hzZk52%=WW1e8WW*e|xKrq3;g_?&j1_wp=ujbEOwQx8Enle~= zk%+F4t}8MRV8aog5o}-ufPAXfH{`!5c0z5D7LO?0mFXP_YY$J&gK}$HXEwx6Oz$9q zFlV2-wWcN;WOuQtmxQ!&%L>O>BWto^0ho)xJse)ap$2$FD_T?Be3sBQ1={=Y*ukdc z35v9Y2^pi0lylp+p*amPS8T!5h(0TZjA4r@FLGpd=a9h4HyO2B0~7*`CkebEp~uZN zaT4=Sd>%$Uf<&8~UorBt$%*@KvPEqaY8WmJbt>|RgwSdc-=oteq#9Xa1g{>z;*uH` zRIv?;EJX8zfFl~n8dN?ZJt44dU@v|FsxdZMeOPI1ZE)Uv@nlezLjw}5W8RCx!ECrLt&|lKM z)muP}SemO{VDz+sR3C250W8t)B=8RbJPA{egyM=0y+O`@M7QeAn`FAci5aq#8^_NJ zm6h5vg0c@y?ByEd_sy?8?OF^GuCav`MqJ+*w1UMT`V9Sl346O_TbA5N4?yO=MYeio zGLs%O9?7KX>1QsIvDqZ=UJ2Jj1ONvj*CEY{R(0<=d#{y05dpyc^W8X(xFb=Kf3jO1 zDJ#>ZmGkXkvD9}6p^mW{PvN@~mlP;7#fohv|Lgzwe}W|kqH9VPkraGz0=TFh9qkQP zjm?+AL2#!-cVu1i&!ghPQB+)!>?BK&4^xgv@QA&bd1kv34uJAGC1g{yqSYGH>7ph3 z?LjbF2gK=kYw~U$Ko$?UY9oZ3C?LN5?5tM_y^lcDU2t3cvwzu_!c*07_^@ zNy~Beu7v2Ix@d4|t_Rq_q3c?Q-hcb|U%79S>$1$-6_Uj}A?W$NNX#V9p123pMrwzUu1q5@A+PDd`AQQ^>fo2q;2vS#Z5F9l=D-`;*bp?YJiGr zdU&(&+YQLDEtskNetc)Tb_#5KH`9~i!iOY`oLH{fSW3^HLH_wX`G_LcMH)pu;dgY% z3<~yTmc_qn9g>z8+*3ithg*ohsqn@`LldUPz%P*w4O&@_=*3T4XU|BAK1~DV6zh?z zJF|QFd*%Iod&|jcpd8AnTmW|j`zUng-uDV_32*A)g3P;Gmvyxd|FYb z6;ssXgkHJ2=aexrL-nZ4+OVQ|x7#)A2pd*vq{PxB3*(RrJ_Z`Zgy!6tfubaVtp_?fYrASi7<(*1A{| z!$X?M3Ob>$F#zi!FcrgDYnn?-LOYOawliyU??FWirG-aja6E8YEO-uu^O+3)vP)$- zU#c19Gc#|&Kk2vs{ctNLgi&C)t4THKyFY$8wb`noRvgPtAI(*&GuQc9%KMe zQZ7Q0qXgGnl--vyto`#Jl3DCYT;#b%p=`JAqD7_g!m?qqPQ(TwjQ6^`*QG_YRuE}8 zCRXIr%X?iq_;x^2p($Qn45v`wxlrT_WJNFJKF2D4%LNy?V0KwHD5^QICr!6zJciim z^W(?Y9}rPWGmA6-tZRb4GIG|GVV|@qY0C{=T}kT_%ssg=>h#2SYJ^oCQo#|`!;5!< zmq@Z?Ow3Yg*|*q|+_>JCs$4q-tWDAYdZ^LmZChHSiiSQp4N0rsi)88BgDfI6eO_vW=$%}bB`D$VUl%;S>AOpcjgjZfeR~&DbZDsv z%$A2NWoD@EeMQMc2e==GInIhFnoCx`*0A3Po#&$c*m+%+5~wO@iFE%)t)VPGmgwez zfeK<_#=(rEC=_cpOc?mrVpq1?Ofw!x(nGdX1fE636}AU~{Kqfk^E@km*~%iZyyZmRWl#>w?9l;%8UQNQ9<#1W z5nE#yej#qwvqbCzx)u)5e!Q(H}uEUO$b{&iy+A37cWc#C+@Ovp2uA20l$|P$GN%61C+C+?HmP!zqd_4&ivl>O` zsfpibC+MHP6$BU!K|W}sPaHeMQJd7L}cO8y4T0d~+w4j^a}0 z^o0H&sDNjt1i!O*@sb{e6irP1acTg^wA_j_a%6S8Xs!kC{#(9=Pnz0$R;i;Cx*4Dv z1qp&us{}#gW-@A8C*wIgrlc78x{8;{+<16oO1k^|c21PfLfF_Mbv)`X8QLn}8!s(| zw3mR>vL)K(;&1f{$fA*~6$v0xsMCVLj^^!+7x;<2(?{mQljfe z;%&i>q9>acoTCh)B}NJh<1vR!7F29!nGkz=C|67(fT1LlX9V*^Qh~Suo%#HmC&M zkXJ13@8+hHq zNG+RxjuzX(%`Kj=q|Z!)-&||7qf{XnnVsw-RPBdCj=jR}hC6h~xVQ9=o*s8JiDje3 zwulVLCcsiflsn8+IqSNGF_F^PPe-j2`5xs>VcB@#N3mQ6v^K_W6|@uwMWciA{h zHy!NEW5IC{g!U-N3Bh5;$&|{MVn~`tPhZqxJxr8~U~R>dzMer}V0SVaBU+J_yz|HX z;rriH8k0!0?Q zmzdnD;&?`UQ`i%5_JtttYM^6E{0drlr*+d`zFDhR8BAyo{)E7-e{Z5f1~&5jlw@ep z5*I{A(qntwn&ePG1Q*1f>MG>|Xys#sQCDtHp8cW2npM5@``8>*82zlxC1 z-9-_Kn<$V#6&_J5YOCy2dZ!jRAIxlLN+-cWdxTHIJ=!AOv)=5dH2B!XlJ994o+4i> zM=ptzv`}k7A#$M-bf}Z2F{Z!$^W<@|fT5J(`r3ACV7+|`I8&Meoohlttow0T?OK_)@#dw z);jDu5o^)qRIi=(?Imq_oIn8vMw~`^zWCD9L62<*Asb#3Q17SuwwTrTvApG)-LSTv zvmbH42fx2h9*QFqH{`IQBESFPIo-AGU0s>ZQWH8Q!njgYi7pwPZQDj3qx}HC3&|)cF2<|a8rg3JSHWdpKbVPRG1k_9EXgaVoM<5B@SPvH`#Ez z=UTK=Ff$iSWG*H{Hb&fcSQ(tfZ8NZ%y^1 zx=QkX9G2H!xo!)NM#q8ApA>DXAQY7;%P9ms)damrpN*GkksFPZ_vdf^0apCH$Y(*j z4}tnPkyXbpRiR0eSkbTvtSWe`gV1gu`e@-?PmXClXkSLlxo2{BSY2GD1K65PoFeY% zx;U+am3NFjIo^NAx4q>ywl9>@5_nN*K!+)IL_U34IdojUnj&*L2QxgDeES!A$oh4F z;FQ%DElp@h{dtlMeYaM;h$NhT);&X&<8a24Eq>rYJD+>kuV>MF#n0$5JD7*vc!8{B zb&b^g&_&QXe1rrz&EHo{0g8Qis%(rY{pd z)je)T8$bxB#rDE#{n^tNe?W4t?Olih1&zV**b~x9w}dld$XFQ&cY+DHjQP4Jo%( zw&02sk`~P+L>QKcx!`VW&z{&_;99d>Izd$r0y=l&>s`pnkC$}(8`OroWrs&fZA%+t z5)l*dy~sW_N4iW!FUuTb^(sPh=?Q3#v5F;Rx*%`CMQDC`soLNKhD!e3W-f0dw4O z1aZIWIIUV7hAm;gZ_Rz>d3+ykYx=Dl;uD~+Mrog)Wyf^I#b6MguhXskT$g;FXH5qm zq&moS%g*-f92oNReJw(o#z9PRXuNbY9<=0iJ^n;8r*|Y3*961)aKx9pI`zKySKHQ? zxZcm^7U(PhP=}Be)s2h~ayZ#4BoFvG3TQwd{7DU&DusOZ?Ko4FtW>M%B2k}%J$#x% z!MNu}P)&HBRK@#O`-0K5x|*ui#I3Qwt^e`CUx9IAc&4UDb&4clnb~ACf z6|6e4ap|lHI-h(s$xbSGJCIf+)GWyIS!8hA@KQ#@>Ek_y2P z^s=*d2YQ4GH;iiQTxEC&hmM8*3BsMH$EkpP~A7$%Sb^5G%kh? z>a2l%#)My&nW@`13YM!XS^!f6mf=MYYR#)(@-pM;s?4FZo<+|9kl9e{^yTNJW2Sv@ zRA0*Tc|%LbSZBV6OZ|y!c$3^y!PHl6j7AB0T~j_;H1nE%>U#%}C--wataUy$Nw9(KrKJhIrH~#xwDkrTe>TUHXsT?G=n7qoS zeCgEA2jI#3%NCI$csv&nH55sC|K0l^meXPki4DgaG?q^uicEg~`AYJv8ZC_bfNZz6 z1z5>I&$3?Cvwg`ATlji*FlsC4FcvuQZo-5;)fWDwQz}g!oygPdH<2=hH;CwY@Xndg zGh3Ph&$6flDxyaTg~RR!8I}SlC(O@0Zmdb)7wE3jqIdR$Ew+&w2b+v?AG)j zY;Uw`_uSQnFU{kwA^Fa(vZj|iqHZ!SU~Cq1aR5t~7{#@_)z}d~e3&;Y=meB~@LST` z4lfkt5dYsqP6T{?z9=ZmZ-bU?YC?Iu^ql6i;dN*UnluQ;Gb3{lj$8CfJV=`f@zV7^ z-uET9t%FBp4kHgZpGOxwEMaY&Lwd=NmPfjLj+T#8NK`Z416@KO;t$nz>OiLz5ML+00Zw>Uf1I3thGVCqnedSxRjCInH zhZ$@TCAj8oeHxC@fZ*6Jldjl^J@Yc{kxV?1mB_M2#vQGOQRPAUQ}23EPn8a-35bKf zd!4x%C1lA6%gxJlrJb>bNPIeriZ}Uoe^!B6h}9kis@Q=DCs`9hO%3olP1mo!(=%%k z9Ldx6V$>O6x(jJZRO#ol1SRdp#bw&|#18)2--CdaCS8hiDcTX5CAUtKr{o;SjAC2X zKQ5f!dlfpSRp=3sQ~j1uMDJ@acS8j&2Ju}Yzy)(vjWaSw$G?*yC6^ajd|6zyGZ65C z#+nK>dS5hX!ivN5Y`|#V^so1{&Ia04RYu|5Q{!Il6GqGt-Ua5MVU!+W<|ukM2Ekie zUc+vpYRZ+B**JSJ_-rygwC&>aabH89ap17gge+q=fKW{ifPe(BzZ!eU?FTM1wlhpe)sj03M%;j7Za(kqP~ z;?D?|XUQiZ`qOhu5wUWMyHf?%)en%SB7Uh{7fCXawp+P=Ozal<{D)_ECyFH#TQX;3 z3VJ733KvW&uW*1BzX$On3POij<4vT}L0w2-5Eu9*YmUVdDXrd87SZ(?q)%2O;y`(t z$~nVQQ8pk?p^oWzIy+Unf09~Vv7sk6KB5)K2f`EoxsG%Go*5m~q|23AWy_GIxiPTx zC@8oy=H%=IGHl@m`HzzMmcn5lYzfrR)I$`35z;axooaeqkLeRnSoX$!RoqH`d>dpK zc_(W!=^R3b-iRsj`TMn`{^4Vn-pTjvX*RVk4n^HR74lbWMgO>aN;5yd&ni$all$jt zxrq?SKmMr>FwX{gMLbZd0QPr#B7b-v-dw!pN`i8~J8VXCI$beqweeP#W-Y!62UE={ zYQ&Ib2m2ZaIPK7?XuRkqP;&-rK&>K|8((H{36Y6ss%_SsnN3YhE~0FJC&tnhhqZ{B z;ZZnZA`u69<%p4>Y)X@PR^zUsMk;4Fno{fNX=b8Vc`pNA1?)9smaC{Z9F3{m%Q=EMQI1U}0;53XvvhgUD0RtjOmE$<9-W;E?;$v=|t9`br*d zZ7#<-y(>m(9nwv&$NtnZcOhg=|Q(1TAMm8^192j9Pjyza};WNw<3&eEsrb~htRc& zoj0O)MA3+@j&r{ZFE>==f}bEQj&(3@hU?U!wh5@d)>6~QkE7&f21Tl?T$Z&3M^Bdy z`kX_{!w)^;Vvb!(Bf{!sWt(XcEODi&f9#*dHX$pO78e23^i#@LN2El=xvEWuou;dU zjt^D{ICbMG@<>lWT0vyLrU!p1G;k0Y6mNwox+RYRl_?MIzs-&U2yM@U53eh|_z8=I z9F31WD=0)?wee)k;sP%QN|c=PdX2z`^KtbiSB!vNY#(aV)Dg=DoHl$Nd*qbex8f^n z^75Z^TfoGM!$Jzj*h75Zx5vGEmbL7TpxP|=&p&x&<$EDGm9DMX9!1sb#I?V(4W<_7 zf8t~~fkVxZ3cGJgqb8VQjs{*N0B>N;|Kv5}_2*X8PGb_4&u`3iy?b1Cx81JlTw~v? zlqISXP$>Ml#Pj)3W)5C%jbiYU;CFfA8bsPb$d+pvOHuy zzh2T|p^0E?+oCnIxN|jSM=jtxWJSgkhA390p(3eI-=iX^6OX}#fY<#|i%wBBD~_Uw z^J^mC3U z9#~?_Jn5)B>+2SIJ_Q5{=v-XSEPN~`@6^ml3N6vi z(bWsTzn=HHKqYlZxc|D^ihb7HvmY1RU)1ak+0xIc>$`AmgX*@W}6+Pp9r+ zJ`*6HW$*y@dlXH0inbWhp5~(<0Zs%In@1lkxqj;nNgN(WJ17 z(=w+(i=CC4{vV*Iybb1XMzO@?!$*FBk{tp>fsVLP_UTUPvhVEOHPM7zdZmoFQQvj# zD=Wf8M12*0rh|@)#gGs{KHK{pQD(AZPdKisS#wn7zO+SiC8#8PkfLHgIy{OLV}!l0 zkgBCkWz$O0U(S-9*0eX8Jnlq6-(KyA{CZq>WZSy#mk?>5*c%|mL+qK{9Eb( z7L^(2vHOfie_}F7JO96hVbn`e;`?G)1i*YFyjOQ5g`dXn4Qd%sG#p?o*SV zO7r&TQ;M@W^G0?*1T`;zLHmnw{4~XqKpPd^QP?U9(#OLRkKE%}UoYm#RBWS5@e9fm z-v?kSLoN79f_hPsb&Rz%2p@q?S>(sIHdbmaW$!*q1*mY>JI2jbbZgMpV*Rz?{Q_(| z$M#1N9795Zi2iT?)BlB#_oa`_7jf9`<>&H8+7!$#Iea>i1qvqmQbuepa72TIpa{}d z2b|bkU+7x{`hr(EzFpR&dq`3pns;x$v>CJTV=bPEJ6J(}JtaW&MV;9O=&d)-&U)$) z5kA@|{x!VPaoD|fark&bup+($!x*a9?2wI?7LQcf&qrOwkP|P7x0AQBd|(+fU@U>$ z*GbD`nv$7_su|H36JBALw$O0y_-D+9RU`}uwq;hN=aTNO$x6ya2gLu|qL)&r;rsBX z$0K!0vSb55y|XU>03ZNKL_t(09$|lqd~TP@^FdyV$Yl0?shy<=VvbgbMAo!6KKmk?csW7wiMdbkxBVP}NnKOJy!A zDIDJAiG7{BE+hXCzBEWthf`%+2GYLJoRGv2Fai;23z4632u`%{@wqi(F);1{#I9>K zOXrJvvW@S zL0(E}!Lvz_%vVRWTK!$;Hd^|dBMUnUKFW zbHC7Wuko@UzE*Hu)sYQCl^%ooS|$<`2h&r^_w!|S(%wuj8TaWqZWHuD7H8JcSDSoB zNtUuA*CVsGUW4+$=WrqFg`*6S`8oT~yY1T1e<$?JRON%GS;&N^^*W~6FJiyPx5b61 z*g4!HvoGj+&OCO=$y``~H)~c<)_4GDLs*Wni=X+B0+oM3t8RtDWAz^EsyQ~7L?&oi(n1U*B;VS+?FS7Q(e~3&QatXi+ zkfZbPA?2yvQ>E$nUCE9A%Cwd&Dp_ z{)9I*==pRKWywKDB%A)6dO2l!#Ly{=cCD=C()U9^-Fve^oCZxyRfgY?rFdw?Ra{qA zTX{&=Gv`Z%Rl%m@%}uD2b_reNtXlWrG(QnK2@nGDUkm_FiAY!qmXns477$z`vp#3X zlZ!j;J4-HP&i*%wg!1xPM|=l=Jl~Tof6#Ioqrs3qx0}3aQc$Jv6q*c=Ps(u1`>0R% z0zt4PY@iK{A_!>nw3iJ-n|!t=k_~($Bw43tAsZ0;*>VEEXrg+1)?_h1kWpl2Dst&= z{NQMipiQ28wxUj%xZvHV1*m{GUIkjmUBOoB&ir3c>NRL^BbU9z^e`~C{H5IC+ zZ?PP7R7VI1)b4LJ>TOrMvd%GcjaSo@*k_Ua_6!B< zM-Ce+z58QwJbpUA`+sVV@R*?qU=s3$GkaR6v=%i*u9<0{sOy*lVyK*24NZ2Bmx;V? z{zMho6KqN6_bEFM5BIKDBFC7U0*kR>?aj~oEZ>v~{elKX9aL2Fuun(h;qV{6e6QYf z(`Jw1Zuyx(-zYgfB|_LXP#l^|c0$kO#cMUsnp<&%`a&w!E*eeoC!S#);A_A@;n+;> zCRw9*#X;GN(s@NZ9qQz;4FcJeX0<)3i<{zg@nPqxm0f%n@e9>a^&tOR5|#8N(oe?= zZ;D<7zXr4u=`XX$yoyts*=qT-ykP^(8H zO9{itom(Vn|hOSc5$acOZi%6YE&B5Lz5L@}4$Z;;Ges#~fy02gsMr$q;b z&Uzwg9(V9kDnBLTm+mIYKD;oE5$!y2Or-Psjx5`_QSR&Aa5~iHd$d1~XZ#ZKS@b;( z{`tqXCAE7>V@t;RZ0>zbpwN^{CCRBw#K(uAVv+kQ9&>##m_WqHZ^JjQd2sB>Z|vb;;wTn~2yZfbrf$9`z)`&+nss zPasH%=hVK(&cMrKt1Tz9rjJYdSyQjx=n#q?)7ObN%g#ztTPKn$F{{;yU20T&>9{K~ z21fi-iz1;W6-7)n01tJfg&KP$Db=*Va8?tM{-&w#nJsg8z{o;W3tA6m-d z5lzd42+iEq;CrYZO1OSd?R%_K&U`?y^17!hQ|Rx!ruPhWM9+HVe{b0^WPn>8(lD$y znvHueKbq7b?V-4;m=G&Ba2Ny`N(Pl7rA0QBCfh;Ub1Y3&aTLA!&CqtfXM7&0gQA_X zW2W0uLIIl3yQtNwUxcEdII>oFqcxO*q#xJ%vbvVlER~~(eHRqrdyx#COIh;@YQ_^r z$|@T1_N(2L$w?&lY#H&ikG21uQATAe3h%eb1_EKcbJ|r7mymQ8lgv)>V$yj8bxhTh z9=l6$A&Ie0eJE>N^SkIb1F0|~^ow3$h*d#9dZ>DM513Hh_5}ENJbT+6lBGKaLlPTOB@&YE=+01SFLaJ=8lK-+6ucT$0FK_;2Awy^$p%B z(BqC~?!9mXDRPSIW1mX}#D+M2gqni%4u_TK+R6q{!$ZR=Bh@qzeS7Z87UQ6{6e&}$ zboIWm2D+m6zHY0`)z*mybxUj)EgcWvGjEaRXKkcABq_NL?L~F13@F0Z?~CBHDI>n! zk&H}nJwh_kvq|`l_Xb*2LB!BR_9tiQX6IRZM+r?Pusmujcd^TCgK})mciAiHn6~jm zMOk8DG6 zH3^HLkB|jI(z}|zKhq{LNr~Li0EEa>H0|_~HVLQKk;Wy7(apD}L2^fCZojRGYpTPW z;wc$BGibGZeIxQ?cHyhb>buMBd+Ns#U3$1!thM+1JDvaD7B~phIA9qvM>?!^t>^pl zR90y+!oVce5$3-ApX-{&;fo;>F;RTam{B7-G} z-j@zvc>QjTf9?=m+DNO%`8hn~+j@~cx2qz$zU2%dX#8>Y_01BcRY*W6`b7)MCS^Io z=23X81AHARqL1%|vS+n#AMJ_Kj7O<>Dux!BzFSVz-VS~wuWl7Fnq|q%dd!QJBvn-8 z_v1s9u+9=@CgPsOiRMIN`Haj~f#PXxux3LD3E#7u6fhoXQvNn(K{jjxuj1c#h~~F3 zc}$+UF?i6EOOh~36iIk#fo9kyp)b<fT-99pc9qdI zxj1`Y&9!xqy88g^h4ae9Xta1PnI}Rf9Ty6wNcrf6sx}^icA5JC^Suow7&`JfUIh&(E|_*R zo_Xq_a$t|km~l4+!Q87rGIS*cP=2l;QPW&j8N|0Cexcs6T)#Fmi3f~hA{}qpN~zdu z`tY$cT$Yt0vpF5#@78N?%v7YUL&PfJG^UMP0tz|(nYyLEQCMAfyj3pl1z5|EZxO2!5hU9Etx_>JMU0e|RV;!H;wm}FY&6+H z3Vk_L>Nh#b=AR@}%@EUxY0XoVaH}$r-5^fyB_dr#Rql zNwqYNvXab^t>%6dfxRJ}#7mmwJHLVq34t_xe>JaqT42g!bSfNp`_TjPk^h_}xL(Ui zO1!r}1{D3&*8Ize;@l;C2>^QW%VdZ2stw(J-RFk@AwRdR!U`hSau4I;7H@}124kX{ z1*R-$LyInody>ouj^z=f_pSE5^4i*&)5}pAaTuaqpkPp2eiv$%>q^L7PB)bObluy} zwB%?qn07@&7Xj8@a}GMAS!yp05WF1F-#e4&1A7_ts5Ze>x|eyt1x^(o*a)B|8hti-9jxK&uuIXcV{r{{v#m_h^hI7f&MQ5R zB-;Z=>1iF*yAO37Ux%85n~NWJ!nG8U7f||B7+&D|j*dZGmmc|fg?H}omm+~tZGiHk zMAEO&D5YV~mnL<@XEJ=2wk=~zB2L97#z&S>=TAyL*tkpe%Q0EeZ@lB46@MHUxp;5w z5IvfI>;BZ_KG(spD-7NNhH^R2WIJKQwG>y;*prd&!BYq3C%vJuG$*?88PNtJ=KxrMQ8z5+n-k5B5Mz`_EAYmc|g)M@+6kmb`SU!ZECsp zPA`$&Id2r&^ayULyjhT$*MT-B6`-j?#tdX9vln$)$c2pdlAX0l2YiQowl~{M z1y@L-t$Bu$LTYu$a+ESaX4W-?Xv)1K*Z?am0nfrVCXO2wIu224T|dTCvU4LLJHdVy z$3D9-{vT9cF;UW^!QoDwJ3T#LddQ%9ZmOfJ6X4x%gf;FVtyI`iD%At+Q8}@0IY|y(Xqu1KDo6@7_wAWp&xMXN6*!xx z)=O$;Eoh$%M)r0+oBC2XXz~IeBYyr61yr+GwE$8^{`HU)M*$RjaLy*R?uBa6d{uj! zAg)Vs2}|AM%0;v#Vf7(tf6$_x8`=zmLqG5V>~ki%bIWOE6`JhrU6C!_z zGU=sfT8~=0q3>fI{3cD1nN)LXdY76*N!s5wnrbEw)Dfup!=m?C1Dj_uz5N|PBwu=R zDX|2Gzn8K_Uu}->B%yhq6{osFH*e)Z{XWzB-&2N;Xetk2n`(q2fVIzr#yS><&jpfO&o0^Jg3wk%Z+bx7*1tt~nl zQZCrhY^!*D==`qwB0<;QU>TxaI``}0MbXx%e2HF@9=&*29JJRLs#W~v6F~_%sg%Gt zi>Ne37H8Ny>K?N%y1_SNZO#kidjh=wdv&-gB1S1oZT&Ove!okzb6N*!lAzIrMC-~+ z8+>w%L}B7=gyXv$3OAsi+_hV==Rd< z@v`QFWGa`IJjOlr^VHo4^0A{)cri$tE!X#D+M_g+M^y)nYpQfFO_nFk3QH(N32{J(P0?eT z%9~m!9AcV27D@rZq((zmUpwH^y@)YkbxG22c?zV+^{#CSt-`+GC4o)%fSKHvh;ImB z2P{c?g>x#R1Sz5>k&++w$N&U;SMUNn=@QIOP{0Q?Ha%=Mz*>a#3{QATUbU?=If0u; z^cl?}l_zYg)6=Ns7kk@>ggWA#cxrv=>WS)xRHY1lvj`HB15#t44$r)6KaPr!Qt2Q? zYNoo^0o%w+WqCNNNTUvgk}L)b9*Bw^B7JuQhRnkq#dla1G#&KFUddRHJtfYbEq!hV zTLdn{9r4DO!XBS6v@RH(mBN>GtId#KLcpf>9eDTbMULLVhs64gKmUYoIpgjXDPU0 z6{J=^msPZiDvn5}Cb#8hMHwVX;Ln}eUC>Bvkq>$}Me$PRpUqd^XEN(`CNv4j7)SHC z8dH#2MYa`S@5MVzWIcaAiaV(U2aKm7>GGpk0yRSLVx16qIk%t#w}$e#OFdJHZvE1l za+Le21wMdh7$|9dkL!4Qs+B8#jCWJ0_zi)I9-J4nX)MKC?jpz31_m0fGuIm4qtPrw zI^RELS9|1{8r+`fDzw9zAiV&@P*pT*&!|?F-p9j&q%z=6H9Wgu1WZGEYSBVpC>l{- z-oZhQht#``789#wYmFv0UF!#sD<{D_=8yWMSMoZ@84gw|t?rS^H#UM4_zLW2Kx25w zuyksB^XiA)@Sx91O{8f0t0Tq$r4u4)rELFL`8|-{lTL5!3TE!ckc1iGjogK|SKg;K zf0!PUAW_YDp+=D9mxFRF+a^bmKur?qO~iEM@C{|`k|@*U*->QSD2R=xZ44!PQiHH_ zRQGhAB9Y+z)CrOhhHP)vmrWvk$AitAKvin~Ncu^2cvLe6B4ouI?A3{>N(cXKdfze# ze>S~(bai<9yY6d2V{+nZ)TyQ}{JzYN-r)<#kZlq);F-0V5lJ#=+4MZwKdg2BTcMSi zbw#4Ji1N9t;;T>xA>ZWTQWgZ5i)f^qXs!ZLn8Z?5ay&+<&X(072itK_ce*9*c6I6V%U@HCQoZPWCzb%0k+T&0V!f|!&(_?dOpvN>=@02M)97aD3bm~;T`KAeEGc{ow{yzZNj#ue>>># zEAD2LV@x+5aeY(G0YLuxv4U8TL^Znko)fg-Hq=X?e0_&x*r(i*z&^!Ysr$~-c_TS% ze>{U4tcm~ zqS89tAYYh}WWM$s@{A_aVfxwk&jAxrwMqMf2CkPj?=}B9P=OeSg6pD*95sj zt&t+hoROvk?1kQI^c1oWx}vZ4e}5d}TG3Hy-3ZS!*{#w-u~lo3QY>kc$G<_&DxaDs zT-hL1E3oT<27GyrhQ}Cu_Cy4#r)}feQz^`*U41z~%V0LNd~5o=Jh9bN zClMn8<8u-<&)%ZLFoZBB((=#~LiWN@HkUZg8WL5Vv3{PBp&rDZGnYyjk~CfBtrOR46I zD9SC3=2#?jDAlOD`qTrh;KiwG!q}(6oBx0OFaHnlK|W+za7iy#8HvtNCX75V9bKz_ zaNwAMcM0U%)}X^CoEg1Ixm?8C1;6PN+P%VW8Ly`|&+dKj+|$Tav=YIZ5AnQ(jPKS8 zguPHfwTf5lV#jOZ_B>058-s);nO>A@yUKGK`M)8%L>}~Kixz1h)`%ey%BYRS*b7P% zpqY@GG@7xnH}@pR@j_}kCfZ|h!SHLz_6X~jOv9E(omsI~Ve@a9q(ht$GH38c9M{n~ z^NEwu@|M<5LMLu;psa=`{1j{-T-M&fCaTti^j_UHx+f|S*CF%Q$H?n-L z!Y6500qWvlp75;!Fwb{!KZ`;hNAYneFm1lLxQ|hU&xjNEI@IevFsDy^3D_4H~vIj+`*J#7;b@kfR@K3S= zR`V_>>=C{2`1&)UoVYKOzkW>a`>M=nsT|xE3RbXSTISmj&xQ zxRTXolGJ&-HU88}$uW6g` zYNe0R$f)IP%eXe3CPpZxye z^HPx2qQ~elv&mnW2Jz?q`28UAkh#t19Y>yr3T$`#-<7c|j~tsfcYb5q8;~iMVe$rT z*GX<`5?!On3t=y4ml0JENv(;t}qhC<(?1NOnxeCI5J zz+jY`ZKF#ce(<#?r z{QR{F-Rv-f;+ah4`FQK5hGik79oK!e>(9^YVM#SzwB(rDXytIm_cN+Yb0U3o;_C4G z`}ecuadCi+WA2L8nN+w^Q|LL)P) z*YEuN?G@janb;-Qy??G1s$ryh*y|67m>rEcXnz*7F%oiYDOeLAB2Oh!f~Jx!;)CQ< zDpE}))cfFVjVT38PN;KIrA2k8;y7sQ0kVki8a`}}6o`qoA*O2U%VAI^`F;3e_rNUV%wislNQ-H5GyvP*e63Tx~E_=WiZ z03ZNKL_t(70X;)kBBU%;*M#@&U?jBvbQL1nJ&GJ~4aJ35u@Z9oK27b#D8qE(JHW{hnjZ zgUNCs%3>yu4e(_eKEys;VRLFre$rR{I^HM3BnNli6GcyGnDpik zzFbRQf7r7+E4jEnzU8`=sxr{VfA~L}iXc|O#rcBXWK&BP4GW@q-I=IO88L@l0imz2 z=~Vii)MGuOE2dRVNeR{(uj~MUez9iEqMp!s^Bvw<$z|WPT)T$$tD+v|kA11SKN<7- zW}ojdWVe5IW@`LO$Cg}mp^BK&OE?zK*DhTZIQ%|pj#CIS#BE{f+Fk_EeP1E``5TdRHA>rJ&=|Kp5yA`BLUpip8f4OVOPRzwU96QhN4ut0Z0i@z-6_(*sf_ zjS#J@KGMpjH{#`=;1XJ5n{Fq4ogn2`Sd+Wjw!Sy-5>HmQ;Edp?Vg55sRq(%o;)?1VB3RoOHwmWcsiYeffoog(cfW@Zl_P3-VRRg0srpG4ymILdU_`Lh@v>lCkvZvx-+NNT5w zg0D5hG6&R8UN{Ugta3xTiqWv?F~HPFSL={e>6&oG35;wpo?i=vUcf8KjGHA4IHtiM zIRP>pGD_wi@o8)=$^G$wjm@WlOyUWS*f@2v20 z$f!9SV@p-?7UzpOCghnqu+oE z@Sd>I`|KJMG+@g<3xFb&6(da+{odl!lAEG3!15+VY!sXMp;;IUx;a~D@}}vt^cF25 zJCiVEyU(XfoV8g9wyQ030=s8Kd2<0t-x_!UjSSgjK??c(_osOeWp;_!@J#Vb(U^$h zF}-L&%UxGsKn|MSveC&E6t>M11n<&1mdHl!WJ2Z=l7PzLQ<9c#?9ZO=lWbg=l0IWA zVsYM6MBfvIYs;5Y9T0_yBT1HA1cyJjA70r9h}|IZwwvqAIhb{C@f`v(lz!!wzdcGE zrjTx^Ff5=))ya+bSo5Bs9e5c=vEsA1k+R6S>DkrF-9t|8`BAg<5=TQMKXojwUe~Pq zb|}_nzvGWbOfEOO_5o{Wqh}WzbqFYrWj`S)9pZb2Cz3=T9XTDKff4l40C7!D1QBzg zOuXsaC5Rpf3V{w#X`R=7u^x|Ue)l9Z9D`|`@xniklM2NMK&Ck%eDrBkB3L!QV%=No zUHSb#&w?x1wK818WAyKTKP#F_J%L#!BA*ZBzyJNW{QO)++}DUFs%fR{=0Fy}-aGD> z^jI4~2dDVh$3iB*fdQ2Tn$JT&(1gVNPx3X@KK7dh@$sw>&mtHyy)kX{`F&6NP3rAM zMM9BWyk*4i`~7*It69r7_XGrFh86W0K*a)h5eaCoyyf z&^h7ci8rRzYqkwve26&VU3tEgV-(z24m!oV&{Vz1oPy0sJg?2*f^UXTzj)25kW}OH z42NR#goDx0Czu^a0f>ql)nULqBrDov-q>oAClAQ>@^|Tbd{qd5?gNh8Y4*a~Rv+|9 z`PbQU4U4{nC8<<_>@?*r>(e;X)6+{CYF}H}--pxM&AvSG|1u>7`ntkuA&0L<1bWv8 z{42iZnZGpDH5(_4=5zwf{7lsgD} ziBNztlA5I?{IjK%?$ue(Mo0W!22v2VU&quzb;y?n?cKbH3|&%ue2_?gB_cIXV3U4I3-Qdjy#jtl?~|y=r6zhW$kUw0 z1h6I*9Pu4xO~TYnU_=Hs$}1d)=;!0~zHS&+sB2#BD?Gv7kf@;M;#88w^Xc0wrA*rN z<$+26FmQJ9wEULS1GZ|A@H6%M7#`irD*7W2ZM$LOw05agp#%aO{_YsG%3Br6)voFJ zS{_S|8eo|OC1J^+V=DO{pGWLb`RnHve;}X7*R$va9V~x!Z;poN{Nv^AuG~iC6wy?h zq}Qe#QWyBg`8Y=O@5^}dv7vwDnReoI&S^5|d>4!l3j${)V>f~ps&KKwQ4`$UGn8}XYm;7_c)L}`#E!{JRUJ!1vu1wC{NIiN&5Eng$LxKXMB&GRMqCYYjI{ql_{En z7BsoWm^NEnOMUZcVjNzrc|2RxiJ;`+K?u|Chz>0Tsu;ryH-~@@oU~An-n!qH>1z=T0t@Tg zwWCRY0S4<_%98Eao2@naM$542)5l7tdvZ*+wxEy!OSv2C)B{g=*JZXPE1@$Ny! zACv`B(M>v82Po@=(nY<48Vtwrz<@WSh)9y4EKlhn%b+xKst&C@7PYo*bVQK~0GFDH z7#iGzC!S0|osj_^9o{piy3_!9u+usOCfHzVA1FG-iIBP_Rpnh9Yr!UJ=LBoRx)$(C zQ}KDgaRg1y+h7+i@^cx<$|9OVGd_v)4d(!;$*at`{@xS7oms0aC#4RQ>Y_;NDHliN z?%r2+!hjw#0e*2*+qJeqoR%>muK!-GR0hJ-p@F8x`)6_t4R?T>d^CSniRO7D2l`|& z$;Do%e(0v|SYW{DkBwsZaU~PmRKP3pwcoc!-w7p zdH{E3ec2*ZzvSKNDP!AzFYOA-;|OgbE03eeWT$Lw(avewHJTVXSrTRoggjE@-~at1 z*b-I`$k>K=-9ILO{dJM_txx-P$PeSBl<%dk18jPOIl9OyifcW~whzvA@T=$ym2pID z(lnvp*VJn24ymC4*}Y9eK!ie3!zBDtVvy8~>H!=CU7NpuUG5*rLiMkoC8GL$9{IUV{^ei)mY@5QpWEbd$me_q{)~gdo~V589SskLH<1Kb?UEdrX{dvX*AX+ z$=m_TcH~1%sEBZ=KAH+@WxQYT-!H?IHU-fy{Qoz%?VWyow*l1x)T8~ z=u7da=$C_U>_wR^yRV9A_uzHK5vjDIP$xg2jCD+RqdH{Q5;@LV-b`)^8Lu4*qT9!M zqOE>Xl~kAkZ|Cf-ph@M!TdpUIUH2j0TggQh1*Xl@v(O}iXafyZJrB0P(a;9-A29Ck z%LJ0+TXH`hdjLvXH2H%} zxMIGJT&z=9Hm1~cCTaPyXTh*?v`cB>EFOLF@lJ_1SgxyQxp=l*PiDa(kk|7$Yw~eA zqvbhQv^7+EV&?F;vP>1sGLRNM$W6PK`9GbJ`Z^#pgBCS_s>rAHzi(>nH~IEH^w|~* zSA>2WMu zx+FOni_L^KMd<*qDC*+D)$kNP<*BY|U#*H2J+u!|L)P;7fTW4kQM4Wc?^vBsZYbvO zG#!+{E$y@F?y;ntL+Hm?v`t8%tHIH_!9$Fc2PaqD2W)8pa%{m5r2BNtCW{wblJ z09-($zavLUX_E-YJLCeh;PN=dh@sQn_swOX z#1YM*+76t2u+MBo6xG}!lQv}ig8Uv)T4#pnZV>(TW8zpe><3rn_5Q3W-0yFM1`H`2 z#U#~1QRVaTeD;Dq{3vDj#*_YpRDFpY*L%<@pnCYsUZg`NN7k#+2aN-c5ok?mN*}-~ zpAZev?!6{PLo_#8_Q=mkNM3k34{)bB`dYC8s$bplD|j;u4J zUpZ9>0bh%HH~rN6{dG{}R?#PeJeR8IjcdL!q@1dx3L5YLBg_eTFV&TDbYCd!s3u8Q za#s}x10z{ zx2mY1HCCn_fcM@0?$t1_Vz0EjbXDb|_adK$D017`K-+yvuB&P(#QroLR~&L}D5?3- z*9A@-iv1xzkI#2yny+6HQ#=PSN)X0KbnYs%xDHF9L9$FPVM z1#bj_ieiGf-LDvhEiopzk!)}wM+N{-vRL4n;u$bGdw>W-+Q zCSTHe*{|D@92;>sg7UsC52|#ZsS#}HiEavYZ>~ZmYDk=5Bg69S(3@FCG{u3^A1@R9 zp1#irqC>V2;vNkzDB5&1Cp|Q&*Bwg|o@~7|J;3FP$^MF(Swr)i|PoiLu-s1yN0`@ zD;rp5)j>C@zaQ5lA@U*gO^+6n>p@H;y}@FbfSas3{g zlC$eRNeF8}-Bak0(_SXmmc1H9qx|C^mgf@A&yH*G`FQ>v2jutfM}9v~gYRJ24|oHN zh$M~}Fw<;Kn9WQ&+AI|<=eU{)XPi!~y|c)M7ia+E`b9GUo5F3n_zz01NIV6x5Q#k6 zlDM}V!U`PTRtUr#^56dTk$?KfE&uhO zZ;}7lWK(Y$=<;A19o zUpoe9XY*!L$NG8LNVb2OZM)9Soz2g%uJdKKrTUQ1v*XZY#0Rp1ov8H@!^09%(Ddvl zaYxF8i2v`)7L#2b@=ONe#-G}f6M-FW&9fp@HPStU*OKz_%Dy*W0cv6G(>NXiK z9ptZT`|N|2QR30^slP?6O3|(>VW3+mm`odLpN!I&{zfGqB_CS7B$Xi%cdBGdp+kHD z>b8RlP367)Xo^UC@A>elPQ}LF1N}iM6{ZY+0F-bv9csR(B;m%yy{NP`r>5`TCUV~N z<1B3+p)h)ab|zav@V1uXsQNJa3$1B0z;!rcKseE(cTSly-5LnPldBkNE0F{0Q+xTO zU5%(I>Cr|)+?AD2J!TZjdBVfrd{`BG5zo};oB$&=_1S>NOvo8eR7?RYQqNl^Z-D2V zUiN;esgA&@x=u*cUh9PHni!sTN1ZLqbkDT3d#t}HQFO#q(Z8^4fKEwu6=ENMx}aht z1wGClIxk6hT%%19c#{leagVM_c*L`KwFsno zZo^uVXOU>u8FK30g68zS1Ms`!MdEC0n=mcImn|Zq|NH;^e?wSC2Z-KZ;;}iuItu2; zSD~xjCm_QYmltklIws}UhSyf27)Sb5-BEsmP4~>{8B;Cy=Bj%NkY1%HG8;{0vaI`M2lSqX1i?XnMyXn;`xgrC8SX+ zMn^N!AQ}&9`m*VE@4jq*IIAua@@5q=6^G}Tf~pnyoEv&v1aiDZbUFQ{dG{b*eFccW34P>k?N!>I#tpga^Kh5%zvLI|8+!xf~JIF&vjA9t2pKZsCz;9u2>^O z9(Iv@R=ectaqNg30{%*uA0wY%P+q3=yuf&#P2{ipT2g3VP0Q=LmZbXejN}SS%Hzv( zd|y|~W{rr4BQZe9ubDtD%dFX0Z)5swWC1D_&6x{q0otPLRx&>%g}EKhrLJjq|G!Aw zY=BenQb4?ng4*@{N_1s~5aV`L&S^7QhgO`JS57r6Oi)d#mC-Gy$LXb@ZT=-pQ=7&8z!Enq99IEv}Ak@dRsh7*n`NMB-WC_=kjRJxVoz3kMt zq2{3!S^J}tg<`KooA%2VrZ;EjTMA6Yz`W{gM3@HOyCf2k zQ$0a~`ldpuLOC*j9g87xn{_q{71k|78C{tjzvhwYbEk=ZrdIUAbSYct{{QLu)gHsu zoxO`w@p%8;&)z_KGQ(Nt9B{>PZdq%Q$!8DeGLm$^6SrCZZf!a4vHn(Rj@HPn-wXMwQ*MXe_jT2{YjG!`O(=AL(+Dui8<3e*M!;fg^nsUiZ^%Ot>hp}<}}mM@bIQGJiCw& zPn#{GOss9#wT{l*jRkV6Xfc``P$Q^=(pJN%CL=+TmXUTxazerAWYScaJ>dI-T&ZO6 zI`D)SQ`W@J^Hp>Ce3sPxqiKCV8#p14-C=#LMlj;Js>6Hhc}u!cEyjfR@jE$*CJ+zR3skcNE5l!dp+d-4Vbqk zu=iCFPn?0SQ29vZRO(YelQmkC9hf2P*yXCwuF0of3{>T(F`w zHqdw!Xf_a!WCJ=F6#cn(u)F49-)F;NV`^0e!Ehe;%)D-i28Xv#PnOSna>jr>bvYe4 zN#Nydm@rFjMjJ&iQ4X%DjKHq*l*oSCUP$R7htx8&lLT^jUdLb3HpP>SHOlyoT^~M< zw5jCJZqTd#9w(jGYaSVj1wV?z+B?yRu^oa6<^2Gs*77APH=0Em{YZY~>nJev)!q8a znnIf;_Af;F%KD{fjr~X{Hl_tTL0c~LpNfb#oi@HGEoY#}uY*)E5LXeiY6v#NEBEbX za3R2n9q-101^har(?AnRP$g-i1oouqEqS{?jt#=XXc&amLk5(Ze6keV6?%f29`C8t3C-FZ`>hu)~=mgY^AfWb%w*2jZR2T&D} z=hrK;Vi!H)D!TJia6^iiNB$RoN!JP9$as2q}TuP%t@!0!imBZyctjp(BNuCS7%3a|D)VQ&)^uLlK&%z10ckH5^oiwS`5YNg$>7H=AOe0Z?bOsh?-!{P1~G z0r%?lLKRy2Qq(o*6!~=v+WA{mFuE6!D> z0~orC{EMs_4@uXvu!U0lY|z^YNSAztH=lnfl$^K+4A#}r=pY40^QDhQ8^;i0Y*R|j zko1C=>$+sl@%?xGk#cfCetpf+j_a?2B2N19uJS1@lpDxL6bniA9 zp~N9NXc&4e%5duo5`2px7iK#nbQ{qELJ|kq$V`oN`t(P7d4O8fJ8zKJ z^qyHA{!G^dRLK8iC9JmyiXN0?gy%F9=;Nid%z}&W-=d^OY(w~2bMdwCu2mBv{Dy9O zs&b3mKbP3O?&rV#%kOF|>_R%_aZGM@Gy=X}<$JR&@rVsIy5PW;o;gL^V0AZZ0zr#` zck@uV5bwV%<$LJ?C$i-XY{x6!AJXy8lHhlLR*qJOLXZcTrd2Y?draBRyz8-~#sRO% zn}=jh*%LQb3A>HFIWocol<40SaYWCnJF*}#P4lt1FMsQlq~ye;c>*IFaczb!l{@A~ zt{xpojl>3Qt&obE(3YhgO&TZ5A#IMrRMrmF;NK}kHhngzOY>cj2D+#TWcOUI>GgW< zE~AZV`?T#ck~h{SI!^^S79zbS)#B-c+>{dy2kYL+m; zG#YHT=sx6V&+#RXnLFg!HU@6QsL^Rh38duRHx|TM^(QyHY9y>R367Q7N~fOK1XWf3 zeh(EYBh(THmJ8BOal;>!4dzV8Xe zPXSxbtvfDj%f(Wvg1o?y zWPphvuOkuEqyf7?KonYsUz^Y+I^*$1U7#2(FG`-^!VDbSq%XFBn56o)d}x!K#&lOn zD$^1QLUwPG6YiR%7ifwlvY-q;XqP~De>OoNF;y_VAqxz7V>BJPS~C(>QbW(8`IzMQ zJ{4>^xGgH@rRvpLcu2+Ud>`N*=^*cxP`p7-+9X@JI7f#Z=}5NdGK89nCXdB2>_lkI zobtNkMajUE=gmaO|LvsF)Fwke!*$nBy zZGd^JzXQ&6cnlKUrD*nfUK_y@Q{a0M{4)IQQ(QD}vXc5F0loUIk#x$4bxgC@6GsuK zI_@)VcP=%w%Ku>9R|lG>Jf^hF2bg~G$w)|^$u!anU(e15iFYQgk9=09BGxQ8Z(`dfQlQp|2=uL9@C_olLEhg6Mo#<}NX}Z_;qVm3G{w}{WXh28Bj5Ri zQNZLS$RK+%TGK&w`SS|JEk+BQ~cRzb1?rY*>*OKVkRel;l#18Fl$&WN0MiGbC zA`ucs?1N3ltUOC1>>reWI85Xyje7bVy^2!|=vVlK^@R%9_ndAJh+-rpiTZSeh7d73 zd_UI}(!yG#Oe+VEH_)V1NA|J>w8vT|GZz4d4`y06T*yDHO*Smsvz9rS`@@5sr!7NQ zL@zWbLS|#Kj>MHFvdW0@q?@(|GJ)NE7f_ayd74H?M(~c=Y0`cbYGIe;nWs4=rqy=&^2&IYugm13H7_ZgrYjqmvKX4Am|JHno+79gVPk*vi9@&wNwfV*{*JO(MMAM~YsYfynoZ2zRikCG;+eA|TH2r-W=<_vI$n0ge7T60!jgvS) zT`48YML!*CPUl-<&1hgpsEN_AAya0w-oJ*m1HapRhUxKnrI00}{g|YOqJhcya}f3W zJo&tNyzh?mg)7~kAv4}yb?5$J3fx^(Y+I6ID*TsS@?(~h;J+R6Uw?kK{ZdvAut@m~ zW1?5#*!_-z!|4I(G@)&bf5udHz=y)`kOp;!_{Xy&<_(6#vaH>5h^QGInj$Y5MizHc zg+NN7VXfuu1hA2eyolfDNy!XpozgUcANZI=wfDuxN})wUP5IW`&?wI<{k~H9wGoq% z*N!?&v0(}Ug#155o{x***@Rh5dAEoEh!*eb+Sf#SUhFFLJw4JTqs8fm+MSr#yeV~N zbayo22l>2t@!rb+Ty}$$%(iar$OGnlMk|E_zqlYMPjAB5K3qpb0VKOv|L&m-Jhk%W zhU`*Dc5t26FbLM=tFS%ZN$Aq<3D=0M33>5y7>8udj)wC>HMBOze8(z89p& zV5KIok^p}`NQtZO0+30j)yP0@Fh=e5)zI)NMulmwC^#4`;;T^>;pft&!*r;aa2|&q zN`Ug>X&&xWmIqk#9gbp_b)2E6j z>GGibRjh09JT5=?MbM5dPaE+(V9BtL0vV@gE7#AINcQ+Eq?DH?j5w!$;eHZQV$`#> zrQ{eMy9Uha6EbMYL_segXAdi>KO~#pUrv{F5K~OpN=!0zfcvSC#RfN`ALe;11C}7C zcCs$e$MGUE9X?7SHic>Xtlnr{=+ji)`RXH#P8vi^vyp|tL$aMi-a(F>sGyCN5k-v) z$)@?R4Z&2P>m0F+ZK4W`#)t_o7P&IFHOtI@QfS z|8`!m6SY`8imCk)e6+lp;FzV7khkg5aIvdZ4k372L^3qnB;n_`0=9)VLD_OoH1h9~ zyJz%IY*tK|r09s0L(NPv?LKmajtS=QV-c+8|EKP~VgYmn1jjBjV|Se9IO?E|I*vbeM#qATjtvy0D+tmBqzi#SDAIck0RrTC&b`4`ekZvobrtX}MhSV-QSA?v;*M zFqC4O`A9|z)JuobwpICInGS&wTXOe*IrOlZT#zN6TEZ|DZAy9AZQMcGet5Va%WUKX zh0vw5L5g;XZJq=L0=3aC+Km7VGJuL3M$I|t_jCfHGAZ+nW@@oCg#gK0hRS~Z&`jq0 z1xACA>Gq}A(5Z!xa9tBNa%bYn=6++_H6$GcN2ooAa^7l`tYULmX(;*iEde_DEzR>G zoqO_kW}!ffT{v*b+$(aQJT*@K&~BWbYo?jC!N$-ORf>g?*=pAophLp%m?OAVSrL_M zl7USEVrd9A6VrNrIZuU)mzj*ZBrk@&h7gJJL?ab|y&h$na-#5hnI}8q?!*cGvKGf^ z8i1S%laX;fj15v&TGOL4m7q2)hU3QtCqE4q2`vCGRvC8`C5}KG8*Su8@btZcktoi3Bqcs7T3al=`lZs8gWl;4-S%aBf<}mY+#q<>pz4ic22D(Nt zB5^vR&(6idOUctfNNfR?`^#2#3ip{32 zA$V@($ntxGM}^Iw7e)iSKd<)$C^aO@ePV_qF*8FHRFxrA9F-FRN@^I^Yd%cWK)Dd0 zKd;_5(rarCV&mY8<^2b?F-&mlwd{cfOh^F%U2DHvv?aPS7d^#o%jDLiUQR(^W(+tL zIUzSz`XId2j-km-;2xW4i24jD%XVWaAQCxM$#rhxC>^h8Vgnjvsb!uR$w4jvXsH}3 ziL{GdJRhyM5p&XqWT^ocC{ZbZvz(Zt6zGzZt0c1$X)<6mBf&4Pk=-Ol5A#$&RZ?IG z{HPtoR7?x4SSHSG%(&ULedb_mjzKD z1nXghO33r!CV-sJwD@@Jl%IVAS!GG;YbQgIDc4hA9T~nO-)-pWp92GN&?f0-iFq;w zu-rcuaA09Bq@8<$_g`Dmu^g;2)TlC)7{ zW!dSTv{|yDF*$ewn26Ce^Fp4uw=EmGCZp*!=;fc?j>Q!9 zrWNT#R65X{*Qr2ha?DwZSMq8@Zd#e{H`|=bkc-a9sriwg?_htv@oSR9iRL)FfC8Md zXZj-L%ac?2Tp>rOQCU-fUavuDRPG!Cq2F&nlEjGloodk%)mr_I1iOHgTIUP1tNNA&oLBc95#S z#qJrijm&IWGK`gon5V*;`OF;ED;qgqKX}rA27z+f_t{Jxjf|O5BJd;tL)MU9AoN1O zrm?}0scz9QnR~yxMwM9tx!~ z*D0_LZM?!k4ieG*X#z3@ImHdJX(EJ>5PmJ|bwD};H6fsvuiGF-wAnfv^AM;!qo05C z4Q%tiU?we+kt?7nb{aFrp2}LGnr?>?`)$Hf7e^%C(AbV3+VFhRQh@U-syY-&_cLD{NgJWwH1bLAJ(f-;(z zB~vJvhC<09hRT^G|Jq((>oQ4&r$RJ>`F-{o4>^WNWAI{{3P2;|Lde8t@=dVx%!i_! zV2E5f76@aEY08}9ojAwxXalfqxw1jz21owzZ5#6xMl)nNcZi!N9l&hfpo{I`M6n+m zL=1AF+cf6hf+_*VjRZ^_KZ3}NjW3R3tc@uL+Nn?c1RvRxEccK+dlDBVOa~uvq3fib zYDv=|UBlR&keFgvnz5lli3DULZyb7ftBR8%J2mk)wn-%)ajcEn;4M)-ROI5LgElEL zGEmGmeC;U!M7~)n*^aruiAJ>$a`Q9f^-b1}SkyT~8bsAV)KabSXR}_pnq0$WzN<>e zq$HR%vuViKu;4tisrM5rSC#ikw9I9aCM=a+Im=-PdZ*&l27g`a9)IMX>D||Z^M2bqu*;V*2YAZH=aa!pY0&3U998~>-%ehHtw0(XAzrx zT83?4>3K&Q6J*XHoY&nb&{wjJ&Dy(4Wxuo@OfrLyu5_4faMhp9C#FD^MBSk63|-x> zku3L>mF-eRI>^HlVMvrSlfd_w)l+PWLpBs}BF{{UQ17S2;ia6|w!|&ob>r&c(dQC{*kEpHkz0=D6PMQ+Y)J;xXb22-ymOVl? zfy%z&T(Xwi0+B$Ij@J_%tCFX}_!DGjWD4q#5EF1q<-NY^3Jk_#Aw45nJw?K)H%$P{ z0#N{2K|R1_fsupy7#EssGZIXJr*Q=d{zYY)nLKu6x-*8ZJ%lSCa)&OMZm;FkOHr6; z(dbBl*bWugP>3sOGP~?U3%f`Y=-8TkG^!3Z95TR+Jluo39!R!R_WV#XSvoV^q*E60 z5P~D6+Ksyqf@8`X)dQovr`BwI>R~A=OVX$S4ho2hkxFx^&2dn1z-IbwCg=?n`EF?a zCXzhWNokM@g6O9IF(%4zFk@S#M+O(0k|eNHvhbuSBT~PPVTh|9HE({#r~0ckyV!2~WENXYM2W~ggJ2;OSIOl4DK_8_{!ptT_nCij-m z1kRr?PgJAIq-FZjYeJr4sePjyDp}WIHjE0n#0~ig>7VW4_Jr zYitk`h%RTNQ=oD%Ve6NKfw~z9p-3YcDu0jM9Y5J)w(LI+JX!b-lnZwlYfO@fGYD<# zD=E5;S zVus0fHFAS>nob@r$c*k(e8{gxD7sdmLVA_h6h>+_iRAz~){-lF*rXf8Y*F!kX*Qrn zlt>hIM{rf*4A056lxn|7b9KQ4cF5J@$vlZXE>C{_ZNKh`bGgi(TJ?<47T| zM1+3OGi=+MEMJ$KYsTHy)fzGloX~d3zRMwa7;;#Y$VSx zx!Tqb?3+PDY!)W!iky-_UH+lq4?|f$sR}%35VG}0pN%p|3T$ka)L8}6gxC&btW`&u zIMJl5NMxJrjiO9#Y+fIcDZcZ1R^Fc~oqE9*gEe0wsU(&x7!!Cf&kB>L-5My8?Vj4gs*DfJ zA%Tp<%|{<)nW~Xu$Cy7~?i#T%`dfmt7}_db(B0H+Ue0eM zQ=ni97PIxnP{29rxAQ>AG+&;hf_saB*~vDHJjO6;ktm9kJ5 z({1KL-PA%k1UB$AjjbZ%F->yy{Jpc!FSHP}@gt#7o`rN9bGI|}$P?4y^W9wdFKdIx z0VB<|D3;}5i%tb@X-;!0@?*A5aLooNl7FAc3qvU+vzZBn8^&i`4qu@}9*Cr!tlnT&}5+XL5birLO$b__@mAIdVrLm?i+hO}aYsB=ep_ zCSV7W%}(UsO1D*DN&5M{elYT30eMNHfKJ3!s_mn|^!&!EtPyW)rlPy4q5;#itbTl( zUSRVi+X3fB8s*|m3o~h8NrAp;bWj=!GI90?Ulcf8ImZREcmZW=WVyLHu?}Um3{qz`Gbm9#rg7AKp3X!5P2k+ZpJVlE(~ zc+ZP{u9i)UD?ibN(_ z#+L|oq;PO>YpW<;?5B-pebb3adaY)-)6aSIY#betk2Z;H9Uv9dM<#@3a$olfxYgJ( zDNcZx4((#GssY_*BUc}!^(@*9RxbCSPWhF%jLL6a1`%d8VUAV%w_bNOLs##$RA9+8 ztDIGB6jvcDo2f7Ba6kZW#a3v`Jg2Bqn@xR(MhlQ4w;*;tN5@-DiDzKt?HFc>w$-ee zD3l66ximwJYa4y2}i3-0h?N~089q_1u6G}=qUrj8zM z+tgr5y*x8rj%LcKX$bkNGe1iw?OHggoh?S&+_t$9Rp=#Gv0P>7U^3;IhOARuY@cQN zEe)nn-S-t&g@zD9L$vic?mW!Yv1`{tUpO=y;| z{#^!AA@A+2tiwd;5n*8VtsW1|<{*cbo}XUY&V&PCR8RyxMCGnn#88vEPCJwU3xdKy z$V)lo21;IHQVCuYyl0CUF&CJ@$Xo3UodASBm3SkOmbpUAW=vIupcd-v79}>>q9qzG zXJ1MfeCbZ~smRU6Zzv!vj)bO9hOlbl1KiqU^tWNW001BWNkl zg9dYzaVIw-aYri2hBoXn3+)pUstIt*hO(^DKE+QJTUZcdO~g}UJ(o?`+N^AB4om@A z8Z@CAF`DlvN{m*tzAMu~H}(*jk_MR=8kKw#^sl5L8>MLUH| z6%L^ac4rII$T{0QEGmS`1dAwWgMoYpTRceWd$iLYh17)H7|#RX&t9tTq`~4|XE)560GD`4G+tEcXlpp=r{Tn$om=koOvmG=oEiCu*?qn^hUQ z>R?E3e&&=h9RAw1c=++z_}$f8dn>J#zOvb4E6|p0xS(X1+o=m1;wPN!v@SYPaTT;U z@#ptAY8xbqL6cE3)V>t!7EoRye?R6ZzLT>*{SzX11NbL z1MyHFs$IVav8@z1{`(B(T*MtOr-{`EhZM$VbDRDq0vp|6u9!)=2`RuqA~a;oT^6)x(A>EIYsNK(*`4XPfbspJ5Q2+OHkgrjtRfd!Awb~N zfe+d5TRKwW$SljtL|+=UfzW)^%GYY;TgmQ0o?8_l{^ z0z~$K^DfGUy$nv`l*$7b$Z-i!CW+*lEoE9}iHXrf%hbiRkq!&}8YUpSz|ZnEZA>JP zmyzao(-632?M_L|t~p?`AwL&swM3N|u#|KSEevZg$djuOGwR!3YZWh6-$5=l6JaPBocfgs_yqt>K-;!nm}ntj4D?5WLbzDv>TK3R1pW8A{^S^!L0f=t!K|^hq|yDoF!yxg zUeAY_jc2%$1yV(PA*AO>;ZG>pe8v0E?C9z+@lZN_CioyYa%*qM>!-Cxh;qOdMJmT8 zjE)Y}pPUa1A3N?sEM5F{{Kq#>$Agbe$PEw%QB1FAa#MRf@9PTEtcjGV{7K0Z7#JYG zPUXR~(p)zksVqhky(IUX6jwz4NXq6~HVvsfT$DxmU@2sKGN}o&w@UO|+j{m!QLeB~ zYyig8Nt>|OuK$WV?*1swmjYO#(}*(R)sW(aL15rugy8doO#vMOK@zp1iTVl@3aE*V zRf(!v$phIeBsr*KsT8#%>I2?w1B$RJKVe*sGX60iM~sOs=9+EVWn^H_-sU z$TVQZPZxr9N-GaSWR-2<3t$M$sp78K^CDOAZ)^^@hI5;|x$5~;-*hJHi?t=nex&-% z0p$KcX)e}9pa5B|!UuWkDxOE%plyiYa;0kk5otrN6wFdoSMpLw%1E&hQN{IT^qQu4 z#iC&ubV%Ot*DV^EI#&uEg46xF&t8l0d~sm_vr;}W8DLp02kruzd_j{35EV_0j1|WL zOeYgFA8V|4Opbk+>RYYwKtR3hfe z)~ZfrEnbx>c&sRRtt&H6OUjz_{0G) z`qIEbmdg1*A7q4jEnYqWfmtt|l_=ej`kpu8YUKIL&0}4%VD{gq;ivj1u{j2#N9N6fq{Sp zf$G#`RG3`iiV{P{(<*-2kT>Nb;|L~Ox-~MBJmC?q5+cx&9{)Kqnw}Qu@*Z85si=g! zK2^_p4Z*3Q1v+FJEDcp%c=DEq0ObX6o`hB7ZOp%Zvbll~ajTZi-~->&Qq?4-A%C z3^E?;P#|*hSpi^Vq>l;Xdl(x_ndzZPvg1V3H$>6aTL)A@qw-zxBexWtvBa(y>_9v= z)0rH9fJ`h?0georVaP4lwv0Bm7;8hGXrx0yWTXs6JCPK{PwXDh*&ZsZ9LWwH%d<=! z(2(qWhV6bVk%4;DFCJ9(7M!l<)!=>1drk!i6iUXGRNoY&KJ(S-q)W51SV5}zpjYw& zH+g-x61`aD{%{aSvBD?-ipwB^jhwI>&BRqC5t*r^@rm08SuWnnw;Hu5wOZE_%VI1q zTup5J6rS^;m#Sul@zIy`5Y04=MlK*^+jO`!3Lze1g)`pbl5}Ae2Bz_7&xD+dztFLT zv2X5H8Si?p=w7aHE!%*OrSNjYn<2R}Tn~WZr|aY<9zMddA#xI5HXH^I6tN|+k%vdQ zad_yI+20S2MJfSIs*1ot*-JA_A)X*xu|y7bEEe#x46>4S^BLGd%{1$r zTno4IS#tt7X-rl`8x82p#Ehjio?YsNP|j<0IS(1A7b`?(4dLoDT7_X$i=S&eXoFS; z7;5uTGIq>GVO`W%a=)8ovy_FY?u)GU20}M&kqZUgC^pzY(XtAqG>!!6h-ErmGIaA4urw*07lX^-o`Yj&k04Q zB8z0G;vwDP7eJ)gG4L~4nvE{p)*B>$8yjs^HmW%m803X96))s`%TzT4N(Tv4L6xXz z|I0#?LuVEIV;(cZI+6dMnj%(-;M;j|fD(tL8*)CVHW`FdVp~)K5Akc122wx>QK{$o zwZ=_o$~2m7VVM{9(M_pfvl-GwqFp-e(Ge`k!0wtRltbu#y06$U8KthOA|xd4Sab+v&}QI_Frhc4i-5Bv6}RRcwgutsFv{kSQ(-FrpG|p$8c6ol9|UwiyzPw_M`^L{d$qF(suHRo=yD zXq2Te4%9hx_~{sx#u332Ke@_WWMh^i+w`)8O4M1G%03`FA$HMnY$TDow?r@ogIK0i zmi7$*)Kbb3mhUiwjp9I?R?+-f@o%w_5>1#e;tpnMlGEtk!wNnNL48!n1lfUj!wZN| zC2M4C1f2KP#zFV|VTJ4|xQvudlOl+83nfRoc<=MGR+`f?bt{whpph9!O|7O!MTW}; ze5g`GRK<{yYL~;2mo$oXKz;fi+!J7BxQ6v%xgIXYiR^jXm+JxqW2y>5(=4hWM^t6L z1_YNRQJI~6vBDs(Ogd6rbr@q8(e3=ZzRyRO<16mY$mydis==M);eLw!PbLsCz!s-q@m)2 zPLbq%!T$DkDjmy%fp6|R&=ixG+g3L~M}b8{sy>;ZT5y-x5lMLrHoWsEb{(S=s{Kf;4Tcli1H%>ARbIBeMTqG@Csu@a&n`jO1gbfGm+&X1Zye z+GOf0$~Tc0MCal%*e;7nxC!K%&6JySPJ(UDz4p*gq^bm4v~s3wm-lK5aih$#^mBIB z3xR{efW6f25lxgHnW(P1(Ngw~$W@A-jlShl*q63(C70-h9{Kx~jXuF>8916wQBZ)9 zm9Ldfwk*POp(HA($ad`%rP~^@kqtwFsnVa1th8S;aVGf8;c^J>1HExsksj)z#}}|MGSE_%lCTR0 z52kkz2%el*flA7T5Y)pWvhc1{LnpC~iITD&0Gk0ggp1>SUs4ERsr(F^$xJZu0NjzNbq-b*a#2YpizLxNi%O$~ z)B6Ywm7-E2C+w6)Aa;fLY}7{_4QRq_&@rax1Jcc}>>-;r4Gj;C;dD8v+6XI;7BG?(?aWQeJ1j zKZXh87^7pscxlg2$iMWbMIiT%NwlL}zo^3NsJ;nT{6xc15W+o2_GI5O zX%N7r*d~0TRthB2CZR;!h*Hf)x&(8mcM%Y-$vgae70=`#w%vFzTR7Rm5t`>Lm9VI! zw1Du647;AXGD0ZCwgW(H`raz_ChArU)S1R=waX5qs7foUAV|`5wSYO0R|+^N8GN8e zv`sc!v=J%fiBY__ArtlG{tvFyv?>Rif2P z(M7k9Q3D9#|GOL@1cb+(@%(L#ld`oLsuq&BTN8$r%WgfI6rGLfD5gC>o7pCFQC>7`BTg z9pO?4!gO`>8r<~Lp|F4|+0fK1fqMrPxTG@>qN#Aq?TF1UL_1$eDIlqLL<~saX`V#JNbn zjeJ}FoMvd-L!hCmhpMY)b0sTinKG2Jgro`RFoI3qWMuAu%cS8YF%i*854~k!Fo;>E zs}MK`Q;@wWNWq@wk%AHTY}Q^s>L6IY-)5a{rtu z%2mr;z3mE}E5ts(s+V}Le*=( zo3d3kd?|9XoHF!KFNC0M9v(@G&{VHd>B!;T+b}|m)9 z#f7X4uC7qvrpvH7r1Ep}0S@reRPHzcs|lqkQtkIs=Vb#q=h(!5QKb?OW+KhDc+KB+ z6N1l5WdqU7jUgsKnXJz=c3*afa8c!DdxH|BVHyqH=qI}eS%qLV3(Njo_V6q7=K~Q& z+qM)V!Nm7P8|TAHoDEGVzDpmy;|l{fDXuUSrO67DKN@_rVzE5MI=|NzZ8p=ETwI&{ zuwD}|h>0W|Nb}AMA=pUc8$uy=C;4i~GFS3@68scB6nGpvxx1(B`sTmXiSoYW&$le> zir@w)RnU-$TuRm*nO;WYPSUd+n?l(wmad^={qd1&j*HOLGut5Z9i)*e>p-ueJV}n_ zH5yV+yinO?e{LgJd#YEvsR5mRArCFc-M&nQVoj-lR?0GWPEUYP z95Il4Bm4Ycj%~@#ZgciDWyXlaG)W_7(5B{3Lxhp0DbF8UMIW?D&=6rDd%8KZ+54{v zp+uMsIF|iFa-4fXV_Ze9Eo&H9Eh3RQeKzI-)`~EB(bAB}F-d^}`c1%C+vcdZpv3)L zC^sP~a0Ff%k**<&0*WX@!gBF_{9!jRI32p>E)Tt6rY;^p1tU{%2yyA;6TGz4njoY| zimcs@P_^8Ms>35WFJtM7MH9&){yQ;*M@xrbe`tjD3!1|jtIoa2F-g%D8O+|y2JS^h_EXtXsD*5*7f_bTgcC?kDQ;!D;x=+FlV_FDnge`D1w@g&mjhF` zP^e=MOMx^JeAqcEuQZ{vaJm$_2sZ&I(tKnmWpL336)v8t*bQcU{=~mRuXh^&;Lh7V zgCBh5ptP~~Y#t!6$AJ&v&Bq=K0QmNo&%)hnrlke8N9c_-uK1LN*&b*^Q0gp;L{%&( z#2x9m{fucs8FKEKdPjU?#uhVQ=aN@UqhP&2IGu=nV@n8Q!dac|41wZ)aLHZrG?4S~Lfq@|w1$P%1)IM8E zy6F@X`HHm(zWdi^8DX#uGqO_{;7xB`i~V2oQ2@YKzHknncx*gsN6hEH`bA8fcrot0 z{h#su|2{B17Y(_FK+0W4fhV*r7y7czt+Zs#V2WBBMdXku!)t(X%AqT>(17Uok)si^BuB5zQ01wphjOVd;l_+A^?P-9iLSRidxq!<}r zvmE$h%!fS5PL~J@fiN1yO`$s%53*_C244kG0AAkIV(0BQ;OMuUikUOc7}{`%aVuQ+ z#}D9Vr@yL9bLz{%G{h5K6(3+5)n!#KhI=62NiGoG;nS_fT%cPed$E@0u}&aul!;6p zwRx*RCA$Mh)DiM(Ct11ZS-j_{6EJbYmH2bNUf#3^uDS8eIP;?AcxklB&s8jYZyRGS zj8Uf7Tv!HPD(GT5sj}q2KNE2GQ74=WuNdA-!1q4&kLdO80syT0)5q|CPF^+ivmLn0 zBRJ-b9|i#Ypa1^2-DNQ8n;)1(I%G#4@6BxXA9GysxVV=YP{`hw> zb@Iiy^S-~vzx`}=ks#V84}tK$gKxvC-HrzUeEh#o!qXf3uAyt;PtjzIHk23|o5lu4 z?x;nHJf*!Rl+4>;6+tfMM^`B#xR^5+uVn3MB2$hv0l6Axz&F>($ZutWUT<$Ap>`^U zK*jE%etm7~6jczbQi{wyZoU3(lxVeNmyiP3K~$>((yR9ES+|OXGYKEXio@Re0(cp& zr3yPubeWk{jW=^m`ypt-Qw;+OQYq9TQ+>c&vHO@TYp{h`cG=B^^l!}#rt%k?kQB$L zzFT`k4G9}HDy{CCN#u3S7D!9O_Km8b6o#T1=4O&No99av24ngDDAQn4^0$;`jqr|2 z^AZMx%Em4TeLgA0cnu8YeAur6GkVf2DnlvCBxfpr-VGz5_Kjjwl;#Vx;Fo)_W$x4x zWw;_SV|AFL#Gx81A7?LlX{MQ>T?C^ivV)Y(R_j*tO%)l@&}-F!oUeFm{53Y`R}APb z6NNPI$xLaTBbrr0BALUmKn?ML$^j`;0n1|93#PM2#^;(J*p@p%>*e8ZxlCot0XZaC zl~9*sn@(Wj^w$pqgdKxW|YFmg9*gzCDPqgBo_%Tbd!itVtFJ2hnyr^paEoZ zjcwEr1?Ys)988H6u9d)4zDN*Rv7^OR*U$n#>H`eB!=+iMi5!8mOuq}^mUO|EmpF<_;~xHW0We zh{LV8crOaoQ^QLCE4~!cNN(#jlmg%Sb;T2(q|Y0i0svv~iz{tf-!Ism}48xBL$yo@Q6e}_jN zeGfLD^LsSS-Pm)tPh;YQf5C|-?`J5`=9;k0MIQo#v4m9`zLrpa-(t;aoX^w(8Q~_r z+ITf0T@#-cg2G=YGcMAkM(@Bvm}iak0<#0p$g?$T>L}iQ=H3nHWFrTCCdZTI{xR6)wH%aQyoB3%$P+XLiDY@q`ikCYowTt#fS=acJ(lK1(_uf_Av!<6y+;rW*u53;GuXas-2(@BZ(|GD?W z4on9(Pw=oIc^#!GlRLOF{6te0=vr3!3VITaoMfc4ohSt=$vYJ&TgcPKylzCj-r~<} z6S1*S(Z%u;5DV#fsLa@U{z`F3M$%h*db@{nE3r=nrrFEL?n^3zb)L+0^@`Vjh)NgH zVm)Ooh8boQvT{18H$nG7&Z+#5=BaLeW@EB5^a5qrT+Go(T8c(yL%e2Z_ml>Ru0@0% zYz&G_siJmFxTt9_O;caKG_7(D8UL%qq79yYCStn>ER zs4Bbwq#{!%PiL8kGlK&=U$z|AV{S_I|P##J!&^vkW0gSj{!J`LD7O#V7&l zT&@|LkabiNS9>C6v-Vhtw1Bm`@DTGX$kc<)M8mqO%J*k%8|GkSiM?zgEB4tOrW`fC zyQq&MNNBjs1{Rka2}q66aui^-uQ)lP$>OBo@9T1}#7wgmSQPmwmz#iw;E329W6FQG z78N7cER*Ui{X6>2XQAI)hqhgW-<=s=l*r+@Tg|crt z-KMz_LY7FgU3+3@s%9tFGNpRMs@sd?`K;f41?T@^Tg;!k5r+ZTv}q#F|HFKof7N_! zK4UK&zWR^YY5N~z(!|T~hSis0pIzUC-(7wvF1~)NG6hDZmMG_dsM(49gYou7&;H9az2FgE;H*1^Pf$;x7yV;>iDP!6xUHN~WdwC7Td@OrX1W zguOs|ejswsGkfBknSyGQjH*eNzlb(+MArx!GWOwe?uw449?ev+F>Hy*z(|iu;zasx zgHF1#L>v{^ip=>l5!cCaCQ2-ffwKBSMb#0~1mwyqDrY)9KbnGtxsIbZ=QEfPEN#}* z&m9?`Jh^C>X!EL6{#)4_Y%wSeSsF5lKf*ao4RoHIO0O5F(lc88ER%{%5DNOp?p@C}e0am11;LJ4iOVBEwM6IE9pPAE0o@wlt%t zsAn8tv0~OpQ>S4MOJtIoh0L`k!sHGSC|_%OYD)Agd3umFNa8n_V-m#RtCrBh1gY&qKF%ee3`*s}g z#`gdK*52^%`1zMtnOw@;(dj9vT7AT+6PqU38h;dRyC#y#ETc}ZHCiPvc5R-pkvud2 z#?qf*LpjyyB~_u=>1N#R;0Lkw{Kt}X?8b$-Y6znDn z29ZHMn5c#>!Dr(OhhWm=^RVMC*W%*SmYBE%R2{k`wH>q(O(6980U-p8j<&9_s0Od- zjWpR8A03fW;zybu4IzB$1dc)|l5?(%Xny$OJtUp?LS2~?0PL{a)0n^TYw0^#`!{&# zo+%nT052o1bUCHSD+hTpfRrj<)63APOtkIkluc7ZoTCbdAqP*B-@`^`XCui11#qxM z7{7u9yDN*7<%x`!o=6FgB84tw&tw=)#b5{?2-xw!)guX&nY#70Wb=_dL7PIy+dLeZ zwZu`d^LP1ZAe7wIk~yw{ehSFw5qQ`E0@{i07{HCJJD8{tnOv1R-pCYVL`Py-)_Slz zKMEyTC6>dD(3~N3Mp1xfd2}u@Y}N=xQuo^j*xD{%ailre9;#o^QuFm#oIMx6SG_N?iaK*OOV_v=NCb z8M!AD99O&cxzjMH7g2{w{KCdnm^|q+?6UohIO~c9RnxXREp`kA9VKQcS8%f1$O0d0 zopjxxB0A5nHckv#-6-KSa)f zjIcEgdp1*0u8#_AO4n*_A+}LF)V0OsvqUi8MJN+aDhdMyJSu=o-s_u|KaK^PKM4T1 z?(S`H)q~UY&zH{;%lE6)aFwmw!GSwV*@BPk4(s8eO`z(GN}aXF%G+I$maoOQhVk!* z{{~ymJ`Is);Dq1(3og2ECSsoOs%5|=GO%AOY&25=bpZ&}KmA^~b08(CY;> z{RCi;!07Vx&18#2Ha%eQAjzxSv50n%{$0#u-*p&XB zCOH+UR64l9IR63Mw{AAJ-}x-;y3fb(*7tr5bLPM30b6Bd>jp9NM8%8qB3g2R`Wo$-bO4=1Z@b%{>$;)6l&N*$5R9wUq z-xSOD?(Z2Z;}`m}$c(5-je#a#3m9-ifPD{HN>DPzEv-|tmWwAgP@-Gsg*OC3udkWo zP2)+fO+)DQYDt$pfT(PWs^}%B?J+PuLYKr?5D@u2H5t5>?h|ueNdHbob_%GU(SdL} z)0JT|EI7M|HlaGbayRa_6OWL+bxnDnL5rnO&-LrVTm8kKwGu z`O9_KNuM!GNQPCMk?{n>*k8y_fYRrkI6mU7Z@LXf9CCZPH$$dQE}ZvddF>l+nx6{M z7~M1^1i~YaPsGEI9+=jV&DWaAL@3ybK>EJQ?N9!2MTWR7?{xCSftfm0S=|{z87OC2 zlfR$YOxOdN!qrIlr%oDl$gc_|A*IziX>KUbKIMC-DQiH$)X5J30G@hg9(b7K!%h@I zxc{-q_{L8U#CLxBKX~Yo)a;!*=On!E==b82?>iUEw|UO_&1G*X)Mh!!P$#FuHKSBb zWG03gtXEITLl_nzXY?Ur`BhoZc z+I;QWn2W;J!rM$9tL&YTvvgSP9&$aRybh;xKpRuRo&M5)*G93iX&$jcvdFAK)S=;b zy_)Fw%*ou3RBM1`3Jq=6m6e>yHLnt_IuvQPlse^mw#=ETXX-3vh)Mvq3LxgL3!c=g zOh)1y9ZRH&o-+z57Au^a?hr!vs9)Aa+a-#^rZMXeWimaCq}M&-`N!kW>`|7lz-lYP(|#LD@HLSiqRIfG-Roef-|xL%p%)KrjHs^ zLUb(O$)*q2-%<6{|21? zj^l9pJB~xoN(AFrnvRAMx%gF3a^93eZcDSx2xKb72oZq?@&JZB5%!&iB+XUSx*)3~ zF?hf(k54X+Kt715$eYriA*}k#JF)#PpG*bN9sh#!PFR_zX;up8Nu3?9QGPH7W3fym zQ$&x%`$*2KW*$e@4VNV2S0)H!2#%ll72)BFi9Zaf6P_|Iby2Th*Nh2raM z$70Gq9ZNsNMy7Dqa&T0DSDmrNG3LUZiszXPAvzVgY$VcVS|6xDz{G<@6{yUT7@xd< zBF_5RM{v~p@4)y8m*SWYF2r{}`(BK`7(A%M_z$TW4kfYhsM7hfF+1kFkJf`%b>N+;+GNfq4*B*4*gL(I1Ie^~SrfY=;{$Yxd0;8Fx3v z#qpE%h@5=gll1c6+pdYpLOY_93v+2I)r&P7n}Gc1Jytz} zEw=hz>e&79-FW<=@fm<&q_oBhX#G=5ci4c{hhB-zw^)mb6W8M9mzUtthj+(i7ww64 zYi62FpQ|aWm$L2q+6S?6=UcG(+%*_K;ZDRj4bMNf9QWV1GcG-UCp_}dgjDzwAp|3> z1q=b4u+4&vSbgxdn78e9m@si2MmNpJ(@(F!AFkL3*Il_q<+V_oKs0i_y3Sj&-Fm$4 zpsTR?+_jiE@m9S2@*+I)@b0+u;ytkTmKi3e6t1uZfYk@xjYD34901^i@1Bka?w^8# z4_${9JNyAtr>+AKv0=lGxa-bc@!NBEz>6Du9iu#TL3IJ6Ww`(TopA9*EAhyq6JXOw<)eM%Bfr6d1^%L@SmeP9~KYmoFeJ_qHw8&vn*lTat9gjXX4}(EtCOm8i zvnEOd_TTBXX7dd1uy{QVdF|zR>Y2It;V*Y9Q(NmtbpFU_`;hG8qo7`JIh;z9@q_O; z7tcLE6TiM_S3L08cn6*<79%;J#$YTqgz+1PG-a$rn4Gu%raPzM#ybzk z{;%2_hrZ@~%$|7~7R);VAAj#DSa;WZar*D}z=Kat=uC@fnB>JAhuHld)+RA@8lp|E zD#xCO04(ya$Df{s^&8%R8Pm?e0lWPP_nxzIh}0Y%DKTmv8i0|0#GCfL7dtMw6|-mF zhH)eJV{EVm)^AvfJMUY8vo2qTXI|(RWd}@=7HWv6^=upOM)FlEQ$$s5)S;8xcO8(v z_h9A6AP;ntBDngVh3L(Bnsms@2eIpR>#)UUw_(Egd(gI1@%+ZcxaYy`@$0L%$9+$a zM{uAcJxiSMw_n2ek(=;~^S^)?wq zce6iWpXeJwCStcRa8i zPQ7|5p4!+mQ8K2X0i7s=fC+uZTXwk@D;KZBW;4udJuk1d1kb$Gca-FM)BnK-zJ*B> zuEuTm{tdox#(vmm!3Mlx_v^9MW;bD^|1e$}U5JMsUx{b}ImFxD%kSmB=X&_TB}d~!hdh8u;6bNme&JP5R1!!!TX;$Um;tUO@Rs$imK#}csQ)lcJq!@mFk*s%UcobuJz zI|^p0pYlbC1GWKPCYB==xH7Ght2t;j(uJym#ZDj7R2dk_(`AtwkBvoymT}DAor5XU z&O&S#;;jGu2-ZJ5-W=pqf~L~)I<7T6d;<8E>mNxMj3QSGDK0AJsV~M*is_g=IK@gS z@4rXrj|*rA6#<#OuT!YG@^IgB`5bIH|9I?w&~cbO=ZARPN9N%tUpd$`9aN%6xf2hr zplar>9F&+QkuK#$Go)gl1v77^Rdy9@dIQ{c^D6AU>X%r)!+#9=%yCTz?O?%z+e8&k1=D`&)pt0ZrsC|v-vfcvpKNSuCKvqKl=#QUNa}lqGbUQ z%5Oe&nuT6sd`bkA#f=by#N$jTy*S^5fQvgSbZP<)IQTaJfYH%ian5PG=BZRLhcorW z!^lwf*#B`H`SwpBgh$Gcm^kr4ELw6g7A~HNi_ZTt&O2i%mjbnanT>hzju9ac_FnZc4tvWN(Cgh% zUT4C@`>^%IOL5dO-^RRczm83t#&rry7Yh;moO6%8AIDK|`}@vkT)gB`ELuDh7hdph zIQQ(O4v0pqbaJaQXqt!*edN^6XPrI!7R;V~GFGfO3_tvz_u;_@Cv^Zx6bKxE8Phi5 zeaD@FEw??o8SB$F28&mo%H0+?$b}t!GZC@My1-^GH=5GE19(rhEnH0~NvjG6G{+Y#P9oSGds)J?-SR&G0G?U@7r6b-8EIl~8kLaA9?1F5YJZ;CTO?aFG-Q|$(G_H+2+Zpm z0T>y_IOMf=W8b~c!sJO8mh1TZ3lr14PDy{*xN&0n@AJ3WfQKKO(2<|Fxd6ZP>McRM zWdVYfK`~h`kv4Cl1-Temwhh*<`%9d4!7h08sqsaYPS$c##cM^@goXl1;)o5; zEx^PHS7M9Jufw!S2V=v=-q1;^E4Ep^aDTXYF0Q)eT{vW)RXAYpvoU4zh1h=S*RgET zwzzTaJ8}AjJK?G4dI}SQ$#7?{JQkm4VNkG7b#NYZv0!l5Q?3mSGXCEdH{ZS&R_%EX zc3gHP=$w@*mF|>=Y#K!5_VGn?H{ydwpN3h}&-Q!A$P<{m*&58<>{r-r$Gz~gv)_X& z*3B$x0T^{zFwr8m*)5dwID}S1rW?bo`I}UC{Ec4~R(@YibY-Gyi+fQ+&6zxkzdiC? zY(4iRzkY>>F>Crd%$ojd?7ZDFoOa;{aNe4&OkomLHI#o%p0Ejrzvd)N7$xYp z_qW;1JFwZzv#@f>>+sFfj>X+iO)TEYDB0q<09d-&Mtt~)(=luMIo0?0B<9Y(8FOa? zyKlcce)QXS;)=UxfskTaN)U+|!YrHpB0hTf>6kVB-0o}5x*2n40lROvJAQQjJMjBI z%`CkP3;E&dM8102Q+V&8UqlG1Y?(CvNi5s;8Z4bR73W_44>I!#4ph_uY; zH;ub*N+71G1AOsgCHPb%_z24HVN#iTQ5;CSm$LPVO|)e`TVM9Tbj-i*uW;C&pTjnr z{R$u1cOL%hiX93?+>$*k-MPGm92J8S%DGUP(>YD#(lK*8LO#322(-bwom+5@amocH z0~^K~eEZ_V@X0sai+=BJym!^FaNpVQ#)BLCDMEBGz#zvPNd%HaIt?PHC_++b*@S+B zO>M+jY_rVS7w>j260nvj&$XzHsxE}{PRroc#DN`nRK;WU#$Z z{KO*dOGPpjILowcHsLQm@Le?dpnS`<2V>dZPr@>|*_bVeJg|u@x}0kn1nZ_kLptNY zE8ygP2ZfojVEF^sc5&*SU3%_kaog`_fkK*?w-HG~ZFXh~>^zX1V|qeEIp&C*w9ZPt zgy>0wJY}Oq5oQpY!07cuw%JfA5(?6Ozz*_4;<=95BhX78XUXQJ4EH(zCo8bUHlM|k z?LLpCD~`uOZ{Hf{pR`?WXxN;|6r&WygKR-*`Oa<{c789JydzH+n!ZYmJe^ew;HxiM zj=lDug(*|d#EL!Ngs=A9^ol%}$4r8?Jc=o?kzrnUj@~kZf^CEe?C@x3S^beR0c8 z2jI!ar(*oXQ7qej9TqS7CWxNF>Vy9o4?pmI-15iS>0UF1eh5In&-m+){S>n|`)MkQ zo<0V*-m)*Aerg)}{Q(v%x(~~@KN0=@EqLP*pTSd4d>yy1onb&xddQl$Xd@1L^OsBU z```l~!tJ;1gy)`{gsD?s#PaQL!e0A)0&8zR-h78Bxcepq4B9r)B+F-H9RB9-V8e#J zvF65Ac=E|?>#pL-G3 z-f}$dy=NXajrOqFX3t{Bl~-Wq%wJ;aw6pQf_l(1TeEB$xjRmJi<;dvuB0liJlQ4U; zlk)XC8f$Oe2TwmW4ZYqN7A<}N%a{KT`u(*y?6BkU)Kg!@x^**^2uXw|o=9HG>C+!n z$|8U{b6&>C$PIYrnYUu*%u_LM-UdAK=;V~AJcyVz>!~DXeR?k1HX;O|*9*Annw40w zBEA0Jd)Xechh_GV2XE1GAI^5rS>pmU|XEWWq;<7T~>d@;sZN9xGBxw|* zNTbLf}ICS+@SiJDN0Mg|7i6`HNHES1@ ztjyFw59l>g2uJkt0gbJ$m~*CM$0JQSu|Ri&HMj1BnKSmpgz?v4rxjnu4$Bwf=G%_N z*%!VFk3Kbmrb$x7)@563oJ+9H-F&azarX|`Vsl{J$RF{}Bd)@?ezsrXcUTaPWp*gv ziHZX0+$$I0;%h&Q!w+~J_TKeZ7(ecM?6%Vvu;X?+;hH}lg;OtFj+Y0$JQ+_EP>D#A z#>!aRq8FrDU?am!4tZ`|A+S=EbN;s;s=BbCrfG4}wF|NT9@8*&$_3bKyQ6T`x>>FP z-0?QT$Lvj;Fu+HS`8lRfJr4lzz$5R%+BP;%Mz3wrG?w_)yPSK_8SK7qR)+6tRS8*Dyf19n+{9cE4c z4W>`I03SMH1fTlRUt%;i3E;#rk(wEpHhzGQzU3E~G4%of!2OTD9k<-QJ)V7G5+;p* z8Oyi556kDDjEUo~!~5U(BG#YsaoqaYB+WOLdxn4+<6C_E&A-5msTZ2p+79bCPQ>K# zqgX!gUM!#g^YmJ;e-Z0{`Ejg!Vp8E5Q0CD@XH8=qyZR)oe|~pdfBWuu{Mjj(G;Rzl z=HH9u^Zyq_&*AlZeHHgT@nu|j?=%BkO5ebxw`_-m)?lfo#PLkiHf7HQpRDn<#Yacim zuUhyWOrCHxKCyZ${Qa*F!DxQDe$X;C<{G96Nj^$>uW9pidDEcH6X;ZMn=P(nlUvt| zz`Wc!BAQTjXtGH#QCZRQUW`g|&H5==vwkunfL_xNfOz|g2P(M9PB1zEZrKIdlUbl) z!8I_>e)5n2-+9hc$tGn;Mh-|fyq;TT`tXj2frWxH~(69 z(-(2cPnRpDG9MB`kIFPW`F?{EWg~`!i7TBCW)q}FDVXjCrW&rK{x9pb161N(02V>> zA-h(_EMW+QQ~vuPeDHJkV&?3hV!zk@D;~N3+qmV5IVjoB`GFmb#&U;~gOKTltp%Ft zU{eA}A%hO=dq#DLdE)-@c=&<8#MayX7k1g_X54!9fi6SQz5hTi&^!dfL2q4Cir=d) z`xegl;ZEiHH@$$%e!UobuHFHMzwN^q8Mz*BIPwyF_lt+81-_xQQJF%Mjg&h^Mhzkr zGN~NY+}FlKP9_#U2S(Fp?El*IeqVa=0GxOFj-`=7Zn01A-!?h6+WI;?`NS^x{@0Jd z=q8nyT=v_AIOy;tIPlQF0?{)#;GipT>rHP?g#k=vX#`&P#@kEryXM9(;%DFA$J~I7 zOL;za-)}jNJoZCqn)`6j>o36_x4uOqKa7p)z(cM_uYW55;18F71E>FDC+AuE-ES7- zFOFS=U3NR!)5%~qfyyVf)z;VI$tQNkx4&^1Mn{{n0bTsNg*fC5i}AWc{w96K)t6)K zO-Cp-o5@i&<=29RzsFC0^d+pld6p@%x597F*&cuOfvvD`;WsdI=C5$Tfva)exr+;d zRy1Y@A9`mges8+z3;4;8_BIr7#wC|5#GZRD!%;_n6istK4nFwzxc&AcjNPB{$RpDM z08^&ifL>sNoE36I;;j?9Y~8B zyEiZ^J#%EF!E5%tAN%ij4rWY0tGti9?|CmSxO6Y9yK8b*jMxo4eV_L~JQWW;avZka z@vIomXMf#LKW^*;nzY zzkU{fT)R_6GNO`$&g^~TC7ut!^Di+@I(I3~z5JhWSxREv3f3G#zb>;r} z{WXWo;5wTSTO$4KjAvIdmIOuY7sc1RLWGSTCLH_qGx0C3(-+u(EWco(+a^2gYI!BzP9cR!4=wg<*m==I)?eRuv6Hk)xd zMw%lr8ma0{vUv`##`n(nB(A<^hWUAOaq^W*@GnPhfyMJq#H{J(;ElWPi&L*%1j_`b zKsaWmQnqr=EQyW>5bORxJ2E`t&M{n%qhY z#@lbZd-Ag~6~DV>TXfPPf=1glm3=~dveHoN8xDv(7X$OT#@eOi*Gkuv`PU_z&9@017Du@7`B*o2Hvpy=kb60udQdFhEMFf1-^3m&Z?{-FK0)k z#wn^-vVKAcebQXnkO^#*w34w+ ze%u%%_J7YheCUL4;Zwgl2A?|h132!RCt>+(pL83EemW|DJ`NH!TK>5mFvebLG1$~* znRJp1_eKJmaRI&YJx~KBg|XK;u*t1G;uBY4%X#0x|6hJR@#t|l^*;|Na1;4o`2wVjP zGsxw&C1{kY4%!UwN?@=2cQfq^Aw3Oiu1h|7w6ef9|pszt1w! zqjBbsc23HLCi$bKNWSipdHB;EA55Fkf}dl`i~$-8t_hOq476=N$VcQnXrRleV4ZYv z2YKXvO`7~Hc6s%q*!9(qV%Pm2!&}~YJ*G@eji76;JQ^=P-*f7Wau1cpgOOm3zX!kk z@f$Ft+qK685oSrg;@kd=(Z})r zJChL%I+odvpkIHrv_!Y8 zSnYQmRs{y-DC@jhyhinGT)Y_L3$s>nRmLpD7*MDvIE(Zzbw|u(!V+VONT6J~#V?H< zdJa$i>TjC3k?<7fHR9pBSK*Nd9za>Azk-lPRG+^G z?=E-?zj@&{e7z%2qm}?iR$3e4V?!E^Ro90D?dC(flp!;v4KYz(ggIIG{n>ck!Gfg~ zc=FFrV9mxq;(GINxb89Z?e%Ls`N%JD&&=&eOAKmLGiGVRlj}o5DW^6 zKvBW#xOvjU;(kiLfNmvqf!cZJ=T*y=|{(?#nuU ziTEhb%1$Tk8h@Ngg2Y|K^!D~y0!}AIO z06j_%i)T`zhYGRl-sxLO`Mm*i*L2hSvu(VzY;P_Oo}44DWBwAP#tQbNh^{gJbT8c| zUu!b{xwbrv6C>`bFAwD6P?dSDl$em^eGoxHLQK>dys~TrT0*9|Qa}IU_9C3QcpCtq zFnb-MDEBpUs=L^BSPUIvqqku~#f7kA66e-+fbwD?cR77=3Qk{*)eQ}?qUJotB_c{J z%C_H?SDinV@9I)wJr@)%q1ta}V16TR9wOvH^$lb2moG<(=EtgM{Vvx8>9GvC@j*+A zENIaVHIBlzvoHXpM0v=IZv%%hSz|UULeA1uuCB)!qNs<0gjU2jT<6n`RGIk^ry1aP#X*QTp0 z!#H9mDarfCFP#{XTaV`_t-$;_&*S6oJcYS4KE(HXodAu@2}GpfCx@$0DKF$VhENLv z2Qd4Y)u`zEgiq6#*I_GWJ^U|hsdyG&zR=sESqcLtpOBkzc$TxA8|5FzXlrUNGQOxI zn+G2k-&)`ZNBc}G zoIR9;rdn4c0U!jqof~oUy`LesP|#e0ZGEwS=Nv3~ZLn~k_WV^V0T7!U!t@8$p;N_s zaNUF8EC**#-G!y|C!qRJqEGjt7O6lC!}JHfMtSA?2n2S4^CX-uMrcw7I!>cf>)lt7r|!7qH7TC`p2#AMY~IS0CylV%M?2R%Cx{j zhjrF*JFzJQ_;zInjGHlQ2REjvp#R=dJ*y)#%u@9tXB%!3!B~HW^%)0C4h()QXaUxcI)-_^SV(w=mOxCQgnv3I6b1vFXpJ&Fw_T-6f$jJj-_b}2j zn{nZ6T!bEZ-alu+b*H1X)iI`R#Ex+ipYzP=4k+maPcq# z5X_T(EE$0*a5)$ENY%-{s7zfH>F@$S~ zTwFxVlzo#OKGadukir}iP*)p;OP9tVH}?e6(vJB22-G_C_M6i|=`^&qQZPnru-u~Y zahy3*gwj%g(zD3OY{u!+@%p_Qn_M(C4MzZuKykl({3_(;HlV619-MPzW?m8#^W$k~ zY#ffXw6lVBKnYxzBOzg{qzWfP`6wxk(T0ts7&)R0u6qQ1dhf#eO$8ARS)a;_h?AMH zyY}?59j?29wi z@zS|iWH;M>KsslY8LHK>syMv$-eZ_E>n$WCtVDeLDt!eXy7yfiJ~9w%zwUx_7vt<` zT78bZu;AaKyz7`cVJo_Jc@vZfdim+I-^a>z!?1l{x^EieQjXhh+JW9Z7b9Bg9WT|~ zjt>`4#eoyaCRW6fE(BZ;-{NvK&iRcfpOLreSd3&k-NL76W=egWlb{Vf&t0_+&+Q)HMW5A!!W+K}{jK z^*|~b-ugLizv(OF<;>IXnU%2sKfHSZuGCJ)>aWM)%gy=5WQG}(MBM`pwTEy7)itqr z@%`cG+MyS29=8!4i(W%&^5>W}<#UV}cspL0Hyt(gQE;S5CJBp$BNoalVm7F+IfXxI zPBPjo2V~}VIbjS~xh)q{##ACYY8S@zIg2G<7onY0Jgd(XZ;}+}Y4rER>F$;`l~_xN zVAvfzQG)VLqM0i%x{57F(zWK7S@Aj){`+oNyN(AV2#9+&%4s z|51^vXo`M~H}KNOli@m(&=RBwdct@{5)XX{VAZ(-lobP%UO-+-D~?=>k=KZWa>FML zh8#UmWsHkI0|?|) zDe4S~(H>F~my7Fow!4q1Vr1$BAPfhpicwJlP+Eg-`PZ=Nc$!aFnHc41B>DK+3hCl& z(>pbF2K%duQQlF!Rz==5Y&@Q-!GN&zZ=Am}1l899(s^FSh=S=s^0Dd?6lMdiQ-%Db z7M!e))w*tVKP}x|gmL|f;W||q*mXa?*q>)nX$5_CRHsXbj}b$iZAW^TlH8I2Fkg-` z_vw`h1zb3Kh(*H^8Ynf{f`kB+5oE`CxNpLTaGfeK%sA5XlfRa}eN z!&e|9c^LrUdP_g7-7^_)N@U-jB`R9>!2GMYbHplSrwJ)fOIuHDJ}?QdeqAn!#N4n3 zWt3JHF}^SHrYhymEf_xTgml5q*k1wFz~ zjG>ZrQc8ToF>bL{)qj~MUiX1s$1$PT>!9R{@nA_hhsl*IQIvi!o?1QFj{qxqjx?p? z;BcRk)RlEh-ov`Nmc7td z$J40hpkmS`L`4U+8=-BxZ;GNwqj8%qy=Qs8sUPG8D5|`M@wfgKKnv=x+==j5_9;-d*|NInb zne)I{0T#dYBV0TdBk5d%GEAwpIa{z*W`}0n_2Vf3fREo;gk3B0Wb+G@4ZMO`4?c~k zsI6$eJ_bz<#YoRuh{}PFBCo^uG51$@q4}z-hX@=%T7C=edE)O#N?8cc>x`NU_aixV z4+=Z}33orb3Gco5G*0bH)~?IU6AknCpL!daId6cmeAHaL4@t>~kXQ5~?*Gp%c>k5B zacp~vKIh;Z(Fq>z{n>ov7MR8?BkN7v_HYNh`=<#$-6^wURIE%s$ham>mA1`ny0)Q$ zFog5p*x_AJ+jWZLho+#S*Ei_VcPrL@QWD_~%o)>q0`<4a8Lg(FY)H_K-mn)CCa7gs z>&ze^A+g0r9VbN4+9|a(@Fe{65M7*W4IMRFQ9U4_|3&p>PlaPoAUVAiHRoc4Srrf0 z^Fiv9P4Us#kY=F<#IGBs1WgU`mJyX_<@9mgY}Q|i@o9}U1A$lp@tmWzC18s6XwNF% z#afV%Xk2<58sdH0eDQcPIIg%~8XFV+D43=!^N3=#u_0YgtnF-JWwO_3ea5)hkh~sT z=1p@S!U+IZ>*F*!$ZeQZZj_CUsSK+)dR=@2#}tB4$aLY(9jTt`ubvIXK&FPTTx*%C_RtH#))XU?f_uI zI7xn%YuBQ1;>2{6mHh!lMIRwEeLQL|NBbO?dsiM1MQgAJHf|~m%civ=%&Y;XN&+yB zm{ND|BRz@YBqSqZAPsjql{DavS@+69xg7iU&%)B>y>Rw?j4bwphTQ<6XC^+AB4U6+ zd9ZsAr{UMnKY}R}#-m5YMx>+&_C#vRVhkFv82$SeW6QP&v0!m8od>O5Qsugu7$3r% z8Jkhjbsi|K1`vg+(?7uSHAAuIaH<}_+BEvRXMKap9>4ZIvssz*@!;LtFn`g{@$H^$ zGce;oOccXU9{LEm+3#9~em#l`UdR8rXD42H=P?{Pm8kdl0X;5Z&Mkt?@W+=I;M9c# z032;02cN9yiZ9ldVe*Kn7}Ea>B*d@ApuSI|Pmf;Mx@#8RUD7kc7)Gtjs!%_CIuXyl zd<%MY9)rPs4xzI9@AYw|r+tE3r+k7gB~Rd$e+`YuNY|1lZq{~tHQsw70sH@#}5%TFFg>oT71a%LoTb3(JkS2IoXaJo@|*K+qC& zaQOH*^eo?nzTJ0Y$>t7{a@~%rvXbY3AR{TH|NdH2d;|tzJlQzv8)D4f5!a&Kl|i6m zO^D-Id9_Sy56wDDvH)|En*eH0xJvv@G1I$R;IWDI8SG<09Srg)Ay8H9|5+G}}Gw+FWUf)vrf5(S94M0cvOx zPPWm3OR$}CDIv&A2%3H9dYn9NVtLA#1FUN_F6K#btq9MP&G5ZeJbeA19O$Yx-F~g) z*j5Xe;1Q;E4cAjF6x1+oTI`MQfErzfGJL%F4HQOUa8vfFDm@iKO_o z`uGP`{26J9_uvBL0vvkYLe%$Qbmbm&%((;4ESZQ_Ml|gf0Ls&v@c2#hkPy4sVvJOyN5@z3yUZi_ z^{2PvY-5zT&p>cjzatpc>knvY>w&x9dJLW%UJ3M*=<5`cAc;dMHdUvic>4pmvH!18 zUNjdE4NAu=o4fjuRBAc+DvCO@}+cBI~SUx z@WP5&_{ro(#76JLL!&>z&z9Vd+E!OI=vD(es-O zho}{NOOWL8GtU?#a6%J2LUGBu?&I0P5{8_MYGXd=Sm8MwC@dj=BHg1Gmm^N^YII%eJXcg(GQ47F$FChu_E z`PgFQ7Q77Rm0-h)pCcjp8u|`?6lL9>#+X_8SpI&wrsLYQMoPuxTA}lbB1IheU^*uE zQ@I9$lQ6m$j}*xhVEd*XsOSac70yF;@l;$o9UoS_nD|;kC>Y_0j_Huak6oI~!@L~693Sw;#zs;<$@>jKRejp6@@M-D!TH~%_87xg1=Jb}s6?t$YR z#rUb)@xiOZ3>k&_joK)sau}f&JjbWOw$T#ELeVypG6f`Ll!m`-Hu?_}s}XK8=OcjW zMxXi3Q8R!-9yH8#TW4@vPUW5zm%+mf z@c>*npO4tshY$)y$#sNT8ZHuXD!3)d7ig{0m)czf(a!8je_C6cgMtDeJ-tq6=%%I~ z!j;-?;2gMeB?pCt+mVyqf{T~pke4U)!L@ZIviK5r33bfmcgxnEC@TXfJ&$2SkKvQW z-SmAH9nDZ$@|8rGZ$MMCj1eN#D&*jwSjnc}?>E4WzLnUqHRgoc1&cs~x?s)P2eEQp z8QR(?0xpkaNXQAXc7Bw+QAaWxI59~5^=23IKk1J57Iwqq|ED85m%fPlt5cAY{5c$_ z3PT3|6v;_H!$000u7@hhNQMB&&u&8Zir>NWigD=3Pq1oz9~?TKtfk)J{tJl-9(r~Y zl)i(9pTN5x4@FjbGw!_QV`QX%gjv&G#+ACqap+{c9@ac~*DBfkMq%C8&td7BGIZ*2 z4RdC^j<~q>_}=Yrqr`Euk}P_ccA+GU8uQIfbLyCg6keb&x$|d zJ5zs!w-)!ZaE@EI>$RK?_12iErgK!nIP)A zl6KZjChCXq?|GiZ03pVAL%sUB%h`yFxeFmLN-nQlQ|JX1K78#W+W6D<^h@al}NoqHoztC@XvmkB?2n-&XgNtP)^U$2!ay_76}}hYMG3#y?k0 zLQPWu_YB*OKAm4g_l~dOk&#Jwaa}jvTn#I#!|W0NfJ4utrhYPBUwH%0UW>(@0}o+H zx93ru^8tQ3DGt9`HXI>&J&KcB@EwhWMTY>D1s#4XI#Jk10e(FNMXNAXbek>%P6UarpS#nYeTK zPNXEP#hl)~@!~h#t;jjC*55Vc3#25h{r}wmc`Ofm7d(Z_ZLWTHzF&C^6M8)l062SP z27a?*5-zofeQDY8EWA4F7-FOLpiki`*#NiinE?Rq;<+L=<=o*ELkqM}iTI>z0`aKr zji#D_E;g^%Ie7P%ci_9f4I(@LW%M6?KQi*3!r|?eh>C4NpW(~kI7h%)E-qBhir6^p z!{PtMAsV+Ya}ic9n1nfxeFfLui?K83Xvn(B+DSoGH_PTlrRdi0Q4|#a1)VGYfu3V4v2Se-NC2RA%!=xJtnm8! zb8%>H8jslc73eo&EiRpzj->SKn0U*Zh>cr?`r6y@&hKWSwNcPY_O8i><2-|z_x=F+ z9sY*zV{YIg#t16;ijg}49oJv{hS4H6PR$9Er# z#aAESf$P^|FnZcr6n1fu!P>_EcZcEpK2v^4|ub+eI@x&VX59>J2gdWSW@Lgz;v;zK{}9gBO+=Mih@^kLVZ zgv;#hP<4qwFyS)EIl>yoc0&I&^T3#)MP%E`$==#8I%3Gkn~|LIDZ2HXhqUazs5uv{ z8+D@4eXc$$4{I#jLu=Y`kX8c-iw33^+LA6;F=Ffv6cp}6e8K@Z&e8u4UeJ^jqMH-{ zbb=Ctj2gq92rC+6bfb}U;(I+ApDhbtqbD{xoPTpW+1?3ZZzzh=upc0z0A0MO2RM3I zx;%wA8_`2ar)wBJdM64BcOfC+AROn|e@94+5KUaJ5es~3>Lq{xsi{H4#C(k_m&b!M zhMF1?vC^UFDlS}%L1w0yl3%XL6>&fE@Y_65csdTj_8pnHY06DVPF{i@-M_{si@Rw} z;?P0Wh>8-!$8FoYS=WV`+;MIUo?s6SRTdwmV2nw`$`rb`X`5~Q)7;`96zUAuJ&XzC z9>IuV1F>h{6s%m^4HquO371LE;V~w%1c}VOgvgnugj>xM4>L4V`M6?0^2!@`BR}U> z%$zn43Gu7Yt^9WwH=+YpZYa{txX1jI6G|^5Huf5llbYf2WLao)D~d<_Ta?!bN`=mD z!#AQ)O-{{|m^AxwC7 z9!N!V%Y%6EJ6DmE_!S<$Cmu__ya`Rs0SxKCNl*ZrhvIxqwCE0C9Fz$a&ueRt;BsvO z8X7Z@krt(?k=l))8`>#g1}>0BG2A)@jZGio_`IK2JS_Ny#0ue zJEG~T|J4`OdTAa}J&A01z@_fLS&hSnq0l~OR+3_5HAyTdSVa@&Ab4|4IVdUDnX1Y` zoFg3N^{?|(K0~}6b3{7`y?vi?Sx1I#x>oQ{gg)4ghzU0!)$1(;tIJWQqQ+VhzA3HB z0+;bjgb-{vk%3zq#v?Ux1u8pj12&fF-x=5S9HIh(@$>b8GV`w7uZeZ zg`Sz%p^&bpETF*WgQD)mSRB2Og{6nG@woS63@HB_23EX?ACt$ha&IY0v#w#r(0Op^ zSzNg~5x@R)3Tj)$TK4>k{&<2sjLJ@b$FLrMg6lknmHSIknpuaLL*IkzoI+j0Sp4SG z>8NfBfPmm{>v|yQ{S+g6{vKV6=HjUvJ$$mYH`3$VaLbS{kesjxMc`_B8 zb9{O*8!f>Hao_mgBPVSMel;zC4>u1-l*`bgSkT5VUo{464ux(m#eT(n&z{FP?&x_I zIg*yWVgJK8To>n$A(PTNP6!;Gt1a(eP>t?W^?lgrF!9l$iI$a#qwp-0)rZ(x`xrP- zp4yD*{e=PL)us`czpIO0tL*IIvsX&d(465niV9z3NPE^~Kz{m4IP@HzoxB*Ej`qj( zwg7q;oj^t5TS5X*cN3oaY7**Ni7*0`kxiGcbwXjr9wfy!wpT)7>49==KlNL&*6jH_ zFJf&G8eZiHtu(;_P?u^n$#DomEm8R3H{ZdnPsE|1kVl08dt5hbpeBYxp~<>|vzk`lC1rpYBzXT1)A7J>&Lbs#K5qK% zUvcTwGq`dl9z@1KF;B)8D4skMlw3+Fwyd3lQ8%tYcHXPF=O?e4sebctEO_}IT(5Jj z-(%SERW4F8|9~-5e~!GO7x3Nxc)@B|I$-I7CsDO4#Y84>p!RGmKA!tS%zB^+$*CV< z)_osYn_LdoE`1Jr*Jg=`D$YPTLH)%5KAQUwZhxpg@^^R=HY|T0UoR=f_&X1xyr-BV zZ(Q*-4s6PTiQgrXhAtO=OXsVp!{EMbQ!YDGU9?-8S`!0d6%%?&4jkM)0)s~_Kt-=L zSj;N@h$giVMb6uOKJ8$1L)9S>sGrURrSJ_-l;N$?eya_S+32VBT6V!gtT>(!d{&;i zIfN(foo_Zw!NjSb!gUW};DdQ)cz{E+vH5nj;;KBt##m>vC14Rxp(uKjzg^Otjg&15{_%#IEgf1*PTA1+d**g-$ zhb=)`+DE7;zXb;lrh-zSXHQ}3)z~-z+jnOB7&1iBN@;t;PRM8qfv|-_)M$~oJ;2}= z4N4e}RGo>zGr#>KCXG9UK9$Q57q=b*`fWkKJ~=pk;ts6a)E7JVrit^TibYN%>r&;t zU}uI?z;%d5U>r)YeQy>nU495Z{=qo}+=CcCcqLYDxL4A`L=gi3)feNjcmFTYt^93t zD0~ea3SYy7(RX0=`q5awH7`P!=up68qLFTCrJ7rIr9K*O|LbntcV{!=2uf{;aqKykh-ItiV8#twKs^uzuJs1*$9G_X185q8_rW^9o??7?x2beSZbzHjk zW4y4YFP@6OA6<%G$H4MGV_>;W1s{RGeK8Z4TLL2cRz~3bVR>IX6MYZ56uyN)6@SK{ z3j244;)SKR;CxfGMZpDL-dK*kB9=cnkko@Rr5;>WB~4J%shA(&19`ysbQu ziOg?)glU6bLP6%oc=V=^t=c;sU+m~-7*9|V1uo-|#J%Op>ozd=DsCDeSwg4p#Y>wi zgl1N<1vmp3HN!5=3gCtXpjH-F#!`Wc2qK44xRfHqn6Keu(n+4T!>HsIHBjx!nK429 z$Am?QkJ*ZlSB%%z&cx-GDE-*Fgn&ub%2O)`NoIu9`&#=tw}yH)jG%tzfj*@e*Ygj^ zN?C^K{g?Uf=TkMe;P

+ +
+ +
+
+
+
+
+
+
+

README du projet

+

+ Cette fenêtre d'accueil charge automatiquement le fichier README depuis le backend Rust. +

+
Chargement du README...
+
+
+
+
+
+
+
+ +
+
+
+
+ © 2026 SASEDEV +
+
+
+
+ + + + + \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_app.scss b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_app.scss new file mode 100644 index 0000000..fc42eb2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_app.scss @@ -0,0 +1,385 @@ +// file: kb_app_demo/frontend/sass/_app.scss +// version: 5 + +$app-header-height: 48px; +$app-footer-height: 48px; +$sidenav-base-width: 260px; + +html, +body { + height: 100%; + overflow: hidden; +} + +body { + min-height: 100vh; +} + +#ibackground { + position: fixed; + top: 0; + left: 0; + min-width: 100%; + min-height: 100%; + width: auto; + height: auto; + z-index: 1; + object-fit: cover; +} + +#vbackground { + position: fixed; + top: 0; + left: 0; + min-width: 100%; + min-height: 100%; + width: auto; + height: auto; + z-index: 2; + object-fit: cover; +} + +video#vbackground:not([playsinline]) { + display: none; +} + +.app-header { + position: fixed; + top: 0; + left: 0; + right: 0; + height: $app-header-height; + z-index: 1030; + + & nav { + border-top: none !important; + border-left: none !important; + border-right: none !important; + border-bottom: 3px $primary solid !important; + border-top-left-radius: 0 !important; + border-top-right-radius: 0 !important; + border-bottom-left-radius: 15px 150px; + border-bottom-right-radius: 75px 25px; + } + +} + +.app-footer { + position: fixed; + bottom: 0; + left: 0; + right: 0; + height: $app-footer-height; + z-index: 1020; + border-style: solid; + border-width: 2px 0 0; + border-top-left-radius: 255px 25px; + border-top-right-radius: 25px 225px; +} + +.app-main { + position: relative; + height: calc(100vh - $app-header-height - $app-footer-height); // plein viewport + margin-top: $app-header-height; + margin-bottom: $app-footer-height; + box-sizing: border-box; + overflow: hidden; // pas de scroll ici + z-index: 500; +} + +.app-main-error { + position: relative; + height: 100vh; // plein viewport + padding-top: $app-header-height; + padding-bottom: $app-footer-height; + box-sizing: border-box; + overflow: hidden; // pas de scroll ici + background: linear-gradient(135deg, #9945ff 0%, #764ba2 100%); + color: white; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 100vh; + z-index: 501; +} + +.app-main-error-content { + width: 100%; + + & h1 { + font-size: 4rem; + margin: 0; + text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3); + } + + & p { + font-size: 1.5rem; + margin: 1rem 0 2rem; + } +} + +.app-logo { + height: 42px; + width: auto; + display: block; +} + +#app-dashbord { + display: flex; + height: 100%; //height: calc(100vh - var(--app-header-height) - var(--app-footer-height)); +} + +#app-dashbord-sidenav { + flex-basis: $sidenav-base-width; + flex-shrink: 0; + transition: transform 0.15s ease-in-out; + + &[dir="ltr"] { + transform: translateX(-$sidenav-base-width); + } + + &[dir="rtl"] { + transform: translateX($sidenav-base-width); + } +} + + +#app-dashbord-main { + position: relative; + display: flex; + flex-direction: column; + min-width: 0; + flex-grow: 1; + min-height: 100%; + + &[dir="ltr"] { + margin-left: -$sidenav-base-width; + } + + &[dir="rtl"] { + margin-right: -$sidenav-base-width; + } +} + + +// quand body a la classe .sidenav-toggled +.sidenav-toggled { + #app-dashbord-sidenav { + transform: translateX(0); + } + + #app-dashbord-main { + &:before { + content: ""; + display: block; + position: absolute; + inset: 0; + background: #000; + opacity: 0.5; + transition: opacity 0.3s ease-in-out; + } + } +} + +// breakpoint desktop : sidebar visible par défaut +@include media-breakpoint-up(lg) { + #app-dashbord-sidenav { + transform: translateX(0); + } + + #app-dashbord-main { + margin-left: 0; + margin-right: 0; + transition: margin 0.15s ease-in-out; + } + + .sidenav-toggled { + #app-dashbord-sidenav { + &[dir="ltr"] { + transform: translateX(-$sidenav-base-width); + } + + &[dir="rtl"] { + transform: translateX($sidenav-base-width); + } + } + + #app-dashbord-main { + &[dir="ltr"] { + margin-left: -$sidenav-base-width; + } + + &[dir="rtl"] { + margin-right: -$sidenav-base-width; + } + + &:before { + display: none; + } + } + } +} + +// ------------------------------------------------------------------ +// Sidebar (style SBAdmin-ish) +// ------------------------------------------------------------------ + +.app-sidebar.sidenav { + display: flex; + flex-direction: column; + height: 100%; + flex-wrap: nowrap; + background-color: rgb(97, 53, 131, 0.7); + color: #fff; +} + +.sidenav-menu { + flex-grow: 1; + + .nav { + flex-direction: column; + flex-wrap: nowrap; + } + + .sidenav-menu-heading { + padding: 1.5rem 1rem 0.75rem; + font-size: 0.75rem; + font-weight: bold; + text-transform: uppercase; + color: rgba(255, 255, 255, 0.5); + } + + .nav-link { + display: flex; + align-items: center; + padding: 0.75rem 1.25rem; + color: rgba(255, 255, 255, 0.8); + position: relative; + + &:hover, + &.active { + color: #fff; + background: rgba(255, 255, 255, 0.08); + } + + .nav-link-icon { + font-size: 0.9rem; + margin-right: 0.5rem; + } + + .sidenav-collapse-arrow { + margin-left: auto; + transition: transform 0.15s ease; + } + + &.collapsed { + .sidenav-collapse-arrow { + transform: rotate(-90deg); + } + } + } + + .sidenav-menu-nested { + margin-left: 1.5rem; + flex-direction: column; + } +} + + + +.osb-scrollable { + height: 100%; // occupe toute la hauteur de .app-main + max-height: 100%; // évite de dépasser + // Pour vérifier sans OverlayScrollbars : + // +} + +.hero { + padding: 2rem; + background: white; +} + +.modal { + z-index: 1080; +} + +.modal-backdrop { + z-index: -1; +} + +.voice-output-textarea { + min-height: 320px; + resize: vertical; + font-size: 1.05rem; + line-height: 1.5; +} + +#logs-card { + transition: opacity 0.15s ease-in-out; +} + +.app-readme-box, +.app-config-box { + max-height: 60vh; + overflow: auto; + word-break: break-word; + border: 1px solid var(--bs-border-color); + border-radius: 0.5rem; + background: var(--bs-body-bg); + padding: 1rem; +} + +.app-config-box { + font-size: 0.875rem; + white-space: pre-wrap; +} + +.app-json-viewer { + white-space: normal; +} + +.app-json-viewer andypf-json-viewer { + display: block; + min-width: 100%; +} + +.app-markdown-body { + line-height: 1.6; + + & h1, + & h2, + & h3, + & h4, + & h5, + & h6 { + margin-top: 1.5rem; + margin-bottom: 0.75rem; + } + + & h1:first-child, + & h2:first-child, + & h3:first-child { + margin-top: 0; + } + + & pre { + padding: 0.75rem; + overflow: auto; + border: 1px solid var(--bs-border-color); + border-radius: 0.5rem; + background: var(--bs-tertiary-bg); + } + + & code { + color: var(--bs-code-color); + } +} +.app-json-box { + max-height: 420px; + overflow: auto; + padding: 1rem; + border-radius: 0.5rem; + background-color: rgba(0, 0, 0, 0.04); + white-space: pre-wrap; +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_bootswatch.scss b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_bootswatch.scss new file mode 100644 index 0000000..90c26e9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_bootswatch.scss @@ -0,0 +1,160 @@ +// file: kb_demo_app/frontend/sass/_bootswatch.scss +// version: 1 + +// Pulse 5.3.8 +// Bootswatch + + +// Variables + +// Buttons + +.btn { + + &:focus, + &:active, + &:active:focus, + &.active:focus { + outline: none; + } + + &-secondary { + color: $gray-900; + background-color: $white; + border-color: #ccc; + + &:hover { + color: $gray-900; + background-color: $gray-300; + border-color: $gray-500; + } + + &.disabled { + color: tint-color($gray-900, 5%); + background-color: $white; + border-color: tint-color(#ccc, 5%); + } + } + + &-warning { + color: $white; + } + + &-primary:focus { + box-shadow: 0 0 5px tint-color($primary, 10%); + } + + &-secondary:focus { + box-shadow: 0 0 5px $gray-400; + } + + &-success:focus { + box-shadow: 0 0 5px tint-color($success, 10%); + } + + &-info:focus { + box-shadow: 0 0 5px tint-color($info, 10%); + } + + &-warning:focus { + box-shadow: 0 0 5px tint-color($warning, 10%); + } + + &-danger:focus { + box-shadow: 0 0 5px tint-color($danger, 10%); + } + + &.disabled:focus { + box-shadow: none; + } +} + +// Tables + +.table .thead-dark th { + background-color: $secondary; + border-color: $table-border-color; +} + +.table-primary, +.table-secondary, +.table-success, +.table-warning, +.table-danger, +.table-info, +.table-light { + --#{$prefix}table-color: #{$body-color}; +} + +// Forms + +.form-control:focus { + box-shadow: 0 0 5px rgba(100, 65, 164, .4); +} + +// Navs + +.nav-tabs { + + .nav-link, + .nav-link.active { + border-width: 0 0 1px; + } + + .nav-link:hover, + .nav-link.active, + .nav-link.active:hover, + .nav-link.active:focus { + border-bottom: 1px solid $primary; + } + + .nav-item+.nav-item { + margin-left: 0; + } +} + +.breadcrumb { + &-item.active { + color: $gray-700; + } +} + +// Indicators + +.badge { + &.bg-light { + color: $dark; + } +} + +// Progress bars + +.progress { + height: 8px; +} + +// Containers + +.list-group { + &-item { + color: rgba(255, 255, 255, .8); + + &.active, + &:hover, + &:focus { + color: $white; + } + + &.active { + font-weight: 700; + + &:hover { + background-color: $list-group-hover-bg; + } + } + + &.disabled:hover { + color: $list-group-disabled-color; + } + } +} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_fontawesome.scss b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_fontawesome.scss new file mode 100644 index 0000000..a47e750 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_fontawesome.scss @@ -0,0 +1,19 @@ +// file: kb_demo_app/frontend/sass/_fontawesome.scss +// version: 1 + +//@use '@fortawesome/fontawesome-free/scss/variables' with ( +// // customizing $font-path - make sure it points to where your webfonts are stored in your project +// $font-path: '../webfonts', +//); +@use '@fortawesome/fontawesome-free/scss/variables' with ( + // use fonts from @fortawesome/fontawesome-free + $font-path: '@fortawesome/fontawesome-free/webfonts', +); +// load Font Awesome core +@use '@fortawesome/fontawesome-free/scss/fontawesome'; + +// load and make available Font Awesome helpers (mixins, functions, and variables) +@use '@fortawesome/fontawesome-free/scss/fa' as fa; +@use '@fortawesome/fontawesome-free/scss/brands' as fa-brands; +@use '@fortawesome/fontawesome-free/scss/regular' as fa-regular; +@use '@fortawesome/fontawesome-free/scss/solid' as fa-solid; \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_simplebar.scss b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_simplebar.scss new file mode 100644 index 0000000..63b6a48 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_simplebar.scss @@ -0,0 +1,248 @@ +// file: kb_demo_app/frontend/sass/_simplebar.scss +// version: 1 + +/* Rtl support */ +[data-simplebar] { + position: relative; + flex-direction: column; + flex-wrap: wrap; + justify-content: flex-start; + align-content: flex-start; + align-items: flex-start; +} + +.simplebar-wrapper { + overflow: hidden; + width: inherit; + height: inherit; + max-width: inherit; + max-height: inherit; +} + +.simplebar-mask { + direction: inherit; + position: absolute; + overflow: hidden; + padding: 0; + margin: 0; + left: 0; + top: 0; + bottom: 0; + right: 0; + width: auto !important; + height: auto !important; +// z-index: 0; + inset: 0; +} + +.simplebar-offset { + direction: inherit !important; + box-sizing: inherit !important; + resize: none !important; + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + padding: 0; + margin: 0; + -webkit-overflow-scrolling: touch; + inset: 0; +} + +.simplebar-content-wrapper { + direction: inherit; + box-sizing: border-box !important; + position: relative; + display: block; + height: 100%; + width: auto; + max-width: 100%; + max-height: 100%; + overflow: auto; + scrollbar-width: none; + -ms-overflow-style: none; + + &::-webkit-scrollbar { + display: none; + width: 0; + height: 0; + } +} + +.simplebar-hide-scrollbar { + &::-webkit-scrollbar { + display: none; + width: 0; + height: 0; + } + + position: fixed; + left: 0; + visibility: hidden; + overflow-y: scroll; + scrollbar-width: none; + -ms-overflow-style: none; +} + +.simplebar-content { + &:before { + content: ' '; + display: table; + } + + &:after { + content: ' '; + display: table; + } +} + +.simplebar-placeholder { + max-height: 100%; + max-width: 100%; + width: 100%; + pointer-events: none; +} + +.simplebar-height-auto-observer-wrapper { + box-sizing: inherit !important; + height: 100%; + width: 100%; + max-width: 1px; + position: relative; + float: left; + max-height: 1px; + overflow: hidden; +// z-index: -1; + padding: 0; + margin: 0; + pointer-events: none; + flex-grow: inherit; + flex-shrink: 0; + flex-basis: 0; +} + +.simplebar-height-auto-observer { + box-sizing: inherit; + display: block; + opacity: 0; + position: absolute; + top: 0; + left: 0; + height: 1000%; + width: 1000%; + min-height: 1px; + min-width: 1px; + overflow: hidden; + pointer-events: none; +// z-index: -1; +} + +.simplebar-track { +// z-index: 1; + position: absolute; + right: 0; + bottom: 0; + pointer-events: none; + overflow: hidden; +} + +[data-simplebar].simplebar-dragging { + pointer-events: none; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + .simplebar-content { + pointer-events: none; + -webkit-touch-callout: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + } + + .simplebar-track { + pointer-events: all; + } +} + +.simplebar-scrollbar { + position: absolute; + left: 0; + right: 0; + min-height: 10px; + + + &:before { + position: absolute; + content: ''; + background: black; + border-radius: 7px; + left: 2px; + right: 2px; + opacity: 0; + transition: opacity 0.2s 0.5s linear; + top: 2px; + bottom: 2px; + } +} + +.simplebar-scrollbar.simplebar-visible { + &:before { + opacity: 0.5; + transition-delay: 0s; + transition-duration: 0s; + } +} + +.simplebar-track.simplebar-vertical { + top: 0; + width: 11px; +} + +.simplebar-track.simplebar-horizontal { + left: 0; + height: 11px; + + .simplebar-scrollbar { + right: auto; + left: 0; + top: 0; + bottom: 0; + min-height: 0; + min-width: 10px; + width: auto; + } +} + +[data-simplebar-direction='rtl'] { + .simplebar-track.simplebar-vertical { + right: auto; + left: 0; + } +} + +.simplebar-dummy-scrollbar-size { + direction: rtl; + position: fixed; + opacity: 0; + visibility: hidden; + height: 500px; + width: 500px; + overflow-y: hidden; + overflow-x: scroll; + -ms-overflow-style: scrollbar !important; + + >div { + width: 200%; + height: 200%; + margin: 10px 0; + } +} + +.simplebar-hover { + cursor: pointer; +} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_variables.scss b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_variables.scss new file mode 100644 index 0000000..97db729 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/_variables.scss @@ -0,0 +1,95 @@ +// file: kb_demo_app/frontend/sass/_variables.scss +// version: 1 + +// Pulse 5.3.8 +// Bootswatch + +$theme: "pulse" !default; + +// +// Color system +// + +$white: #fff !default; +$gray-100: #fafafa !default; +$gray-200: #f9f8fc !default; +$gray-300: #ededed !default; +$gray-400: #cbc8d0 !default; +$gray-500: #adb5bd !default; +$gray-600: #868e96 !default; +$gray-700: #444 !default; +$gray-800: #343a40 !default; +$gray-900: #17141f !default; +$black: #000 !default; + +$blue: #007bff !default; +$indigo: #6610f2 !default; +$purple: #593196 !default; +$pink: #e83e8c !default; +$red: #fc3939 !default; +$orange: #fd7e14 !default; +$yellow: #efa31d !default; +$green: #13b955 !default; +$teal: #20c997 !default; +$cyan: #009cdc !default; + +$primary: $purple !default; +$secondary: #a991d4 !default; +$success: $green !default; +$info: $cyan !default; +$warning: $yellow !default; +$danger: $red !default; +$light: $gray-200 !default; +$dark: $gray-900 !default; + +$min-contrast-ratio: 2.1 !default; + +// Options + +$enable-rounded: false !default; + +// Body + +$body-color: $gray-700 !default; + +// Links + +$link-hover-color: $primary !default; + +// Tables + +$table-color: initial !default; + +$table-border-color: rgba(0, 0, 0, .05) !default; + +// Forms + +$input-focus-border-color: $primary !default; + +// Dropdowns + +$dropdown-link-hover-color: $white !default; +$dropdown-link-hover-bg: $primary !default; + +// Navs + +$nav-tabs-border-color: $gray-300 !default; +$nav-tabs-link-hover-border-color: $primary !default; + +// Navbar + +$navbar-padding-y: 1.2rem !default; + +// Progress bars + +$progress-bg: $gray-300 !default; +$progress-bar-bg: $primary !default; + +// List group + +$list-group-bg: $gray-900 !default; +$list-group-border-color: transparent !default; +$list-group-hover-bg: lighten($list-group-bg, 10%) !default; +$list-group-active-color: $white !default; +$list-group-active-bg: $list-group-bg !default; +$list-group-disabled-color: lighten($list-group-bg, 30%) !default; \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/main.scss b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/main.scss new file mode 100644 index 0000000..cebeba2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/main.scss @@ -0,0 +1,10 @@ +// file: kb_demo_app/frontend/sass/main.scss +// version: 1 + +@import "bootstrap/scss/functions"; +@import "variables"; +@import "fontawesome"; +@import "simplebar"; +@import "bootstrap/scss/bootstrap"; +@import "bootswatch"; +@import "app"; diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/splash.scss b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/splash.scss new file mode 100644 index 0000000..aad12f5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/sass/splash.scss @@ -0,0 +1,126 @@ +// file: kb_app_demo/frontend/sass/splash.scss +// version: 3 + +@font-face { + font-family: 'Dos Amazigh'; + src: url('../fonts/DOS_Amazigh.ttf') format('truetype'); + font-weight: normal; + font-style: normal; + font-display: swap; + /* Assure un bon rendu pendant le chargement de la police */ +} + +body { + margin: 0; + padding: 0; + background-color: transparent; + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + overflow: hidden; + font-family: Arial, sans-serif; +} + +#splash-container { + position: relative; + width: 960px; + /* Largeur exacte de votre image */ + height: 637px; + /* Hauteur exacte de votre image */ + opacity: 0; + /* Sera animé par JavaScript. */ + will-change: opacity; +} + +#splash-image { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + z-index: 1; +} + +#app-name { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + font-family: 'Dos Amazigh', sans-serif; + /* Application de la police */ + font-size: 96px; + font-weight: bold; + color: #fff; + /* Blanc pour contraste */ + text-shadow: 0 0 10px rgba(0, 0, 0, 0.5); + /* Ombre pour meilleure lisibilité */ + z-index: 2; + text-align: center; +} + +#debug-info { + position: absolute; + top: 0; + left: 0; + width: 50%; + max-height: 30%; + overflow-y: auto; + color: white; + font-family: monospace; + font-size: 12px; + background-color: rgba(0, 0, 0, 0.1); + padding: 8px; + z-index: 3; + box-sizing: border-box; +} + +#messages-container { + position: absolute; + bottom: 0; + left: 0; + width: 100%; + max-height: 30%; + overflow-y: auto; + background-color: rgba(0, 0, 0, 0.1); + padding: 10px 10px 40px; + z-index: 2; + box-sizing: border-box; +} + +.splash-message { + margin-bottom: 8px; + padding: 6px 10px; + border-radius: 4px; + animation: fadeIn 0.3s ease-in-out; + font-weight: bold; + color: #fff; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +.splash-message.info { + background-color: rgba(33, 150, 243, 0.2); +} + +.splash-message.warning { + background-color: rgba(255, 152, 0, 0.2); +} + +.splash-message.error { + background-color: rgba(244, 67, 54, 0.2); +} + +.splash-message.success { + background-color: rgba(76, 175, 80, 0.2); +} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/splash.html b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/splash.html new file mode 100644 index 0000000..27e98f9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/splash.html @@ -0,0 +1,23 @@ + + + + + + + + + Loading ... Khadhroony Bot2 + + + + +
+ Application Loading +
Khadhroony
+
+
+
+ + + + \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_backfill.ts b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_backfill.ts new file mode 100644 index 0000000..852dc9d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_backfill.ts @@ -0,0 +1,267 @@ +// file: kb_app_demo/frontend/ts/demo_backfill.ts +// version: 5 + +import * as bootstrap from "bootstrap"; +import "simplebar"; +import ResizeObserver from "resize-observer-polyfill"; +import { invoke } from "@tauri-apps/api/core"; +import { listen } from "@tauri-apps/api/event"; +import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log"; +import type { DemoBackfillOptionsPayload } from "./bindings/kb_app_demo/demo_backfill/DemoBackfillOptionsPayload"; +import type { DemoBackfillProgressPayload } from "./bindings/kb_app_demo/demo_backfill/DemoBackfillProgressPayload"; +import type { DemoBackfillRequest } from "./bindings/kb_app_demo/demo_backfill/DemoBackfillRequest"; +import type { DemoBackfillSummaryPayload } from "./bindings/kb_app_demo/demo_backfill/DemoBackfillSummaryPayload"; + +(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap; +(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver; + +const logLines: string[] = []; +const maximumLogLines = 1000; +let running = false; + +function element(selector: string): T { + const value = document.querySelector(selector); + if (!value) { + throw new Error(`Missing UI element: ${selector}`); + } + return value; +} + +function inputValue(selector: string): string { + return element(selector).value.trim(); +} + +function integerValue(selector: string): number { + const parsed = Number.parseInt(inputValue(selector), 10); + if (!Number.isFinite(parsed)) { + throw new Error(`Valeur numérique invalide pour ${selector}`); + } + return parsed; +} + +const base58Alphabet = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; + +function decodesToSolanaAddress(value: string): boolean { + if (value.length === 0) { + return false; + } + let numericValue = 0n; + for (const character of value) { + const digit = base58Alphabet.indexOf(character); + if (digit < 0) { + return false; + } + numericValue = numericValue * 58n + BigInt(digit); + } + let decodedNonZeroBytes = 0; + let remaining = numericValue; + while (remaining > 0n) { + decodedNonZeroBytes += 1; + remaining >>= 8n; + } + let leadingZeroBytes = 0; + for (const character of value) { + if (character !== "1") { + break; + } + leadingZeroBytes += 1; + } + return leadingZeroBytes + decodedNonZeroBytes === 32; +} + +function appendLog(payload: DemoBackfillProgressPayload | { timestamp: string; level: string; message: string }): void { + const progress = "completed" in payload && payload.completed !== null && payload.total !== null + ? ` [${payload.completed}/${payload.total}]` + : ""; + logLines.push(`${payload.timestamp} ${payload.level.toUpperCase()}${progress} ${payload.message}`); + while (logLines.length > maximumLogLines) { + logLines.shift(); + } + element("#backfillLogOutput").value = logLines.join("\n"); + element("#backfillLogOutput").scrollTop = element("#backfillLogOutput").scrollHeight; +} + +function setRunning(value: boolean): void { + running = value; + document.querySelectorAll(".backfill-start-button").forEach(button => { + button.disabled = value; + }); + element("#cancelBackfillButton").disabled = !value; + const badge = element("#backfillStatusBadge"); + badge.textContent = value ? "Backfill en cours" : "Prêt"; + badge.className = value ? "badge text-bg-warning" : "badge text-bg-success"; +} + +function commonRequest(mode: string): DemoBackfillRequest { + return { + role: inputValue("#backfillRoleSelect"), + commitment: inputValue("#backfillCommitmentSelect"), + mode, + signaturesText: null, + address: null, + anchorSignature: null, + direction: null, + limit: 1, + pageSize: integerValue("#backfillPageSizeInput"), + maxPages: integerValue("#backfillMaxPagesInput"), + maxConcurrentRequests: integerValue("#backfillConcurrencyInput"), + maxRetries: integerValue("#backfillRetriesInput"), + }; +} + +function buildRequest(mode: string): DemoBackfillRequest { + const request = commonRequest(mode); + if (mode === "explicit_signatures") { + request.signaturesText = element("#explicitSignaturesTextarea").value; + return request; + } + request.address = inputValue(`#${mode}AddressInput`); + const anchorSignature = inputValue(`#${mode}AnchorInput`); + request.anchorSignature = anchorSignature.length > 0 ? anchorSignature : null; + request.direction = inputValue(`#${mode}DirectionSelect`); + request.limit = integerValue(`#${mode}LimitInput`); + return request; +} + +function validationMessage(request: DemoBackfillRequest): string | null { + if (request.mode === "explicit_signatures") { + const signatureCount = request.signaturesText + ? request.signaturesText.split(/\r?\n/).filter(value => value.trim().length > 0).length + : 0; + if (signatureCount === 0) { + return "Ajouter au moins une signature explicite avant de lancer le backfill."; + } + return null; + } + if (!request.address || request.address.trim().length === 0) { + return "L’adresse est obligatoire pour ce mode de backfill."; + } + if (!decodesToSolanaAddress(request.address.trim())) { + return "L’adresse Solana doit être une valeur Base58 décodant exactement sur 32 octets."; + } + if (request.direction === "after" && (!request.anchorSignature || request.anchorSignature.trim().length === 0)) { + return "La signature d’ancrage est obligatoire pour rechercher des transactions plus récentes."; + } + return null; +} + +function reportValidationWarning(message: string): void { + element("#backfillSummaryOutput").value = JSON.stringify({ validation: message }, null, 2); + appendLog({ timestamp: new Date().toISOString(), level: "warn", message }); + frontendDebug("kb_app_demo.frontend.demo_backfill", `Backfill request rejected locally: ${message}`); +} + + +function synchronizeAnchorRequirement(mode: "program" | "token" | "pool"): void { + const direction = element(`#${mode}DirectionSelect`).value; + const anchor = element(`#${mode}AnchorInput`); + const required = direction === "after"; + anchor.required = required; + anchor.placeholder = required + ? "Signature obligatoire pour rechercher après cette transaction" + : "Vide : commencer depuis les transactions les plus récentes"; +} + +async function executeBackfill(mode: string): Promise { + if (running) { + return; + } + try { + const request = buildRequest(mode); + const invalid = validationMessage(request); + if (invalid) { + reportValidationWarning(invalid); + return; + } + setRunning(true); + element("#backfillSummaryOutput").value = "Exécution en cours..."; + appendLog({ timestamp: new Date().toISOString(), level: "info", message: `Démarrage du mode ${mode}` }); + const summary = await invoke("demo_backfill_execute", { request }); + element("#backfillSummaryOutput").value = JSON.stringify(summary, null, 2); + appendLog({ + timestamp: new Date().toISOString(), + level: summary.cancelled ? "warn" : "info", + message: `Campagne terminée : completed=${summary.candidatesCompleted}, cancelled=${summary.candidatesCancelled}, notStarted=${summary.candidatesNotStarted}, inserted=${summary.canonicalInserted}, existing=${summary.existingSkipped}, missing=${summary.missing}, failed=${summary.failed}`, + }); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + element("#backfillSummaryOutput").value = JSON.stringify({ error: message }, null, 2); + appendLog({ timestamp: new Date().toISOString(), level: "error", message }); + frontendError("kb_app_demo.frontend.demo_backfill", `Backfill failed: ${message}`); + } finally { + setRunning(false); + } +} + +async function cancelBackfill(): Promise { + try { + const accepted = await invoke("demo_backfill_cancel"); + appendLog({ + timestamp: new Date().toISOString(), + level: accepted ? "warn" : "info", + message: accepted ? "Demande d'arrêt envoyée." : "Aucune campagne active.", + }); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + frontendError("kb_app_demo.frontend.demo_backfill", `Cancellation failed: ${message}`); + } +} + +async function loadOptions(): Promise { + const options = await invoke("demo_backfill_options"); + const roleSelect = element("#backfillRoleSelect"); + roleSelect.replaceChildren(); + for (const role of options.roles) { + const option = document.createElement("option"); + option.value = role.role; + option.textContent = `${role.role} — ${role.providers.join(", ")}`; + option.selected = role.role === options.defaultRole; + roleSelect.append(option); + } + if (options.roles.length === 0) { + const option = document.createElement("option"); + option.value = ""; + option.textContent = "Aucun rôle compatible"; + roleSelect.append(option); + } + element("#backfillCommitmentSelect").value = options.defaultCommitment; + element("#backfillPageSizeInput").value = String(options.defaultPageSize); + element("#backfillMaxPagesInput").value = String(options.defaultMaxPages); + element("#backfillConcurrencyInput").value = String(options.defaultMaxConcurrentRequests); + element("#backfillRetriesInput").value = String(options.defaultMaxRetries); + setRunning(options.running); +} + +document.addEventListener("DOMContentLoaded", () => { + installFrontendConsoleBridge("kb_app_demo.frontend.demo_backfill"); + frontendDebug("kb_app_demo.frontend.demo_backfill", "backfill demo window loaded"); + document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(item => new bootstrap.Tooltip(item)); + for (const mode of ["program", "token", "pool"] as const) { + const direction = element(`#${mode}DirectionSelect`); + direction.addEventListener("change", () => synchronizeAnchorRequirement(mode)); + synchronizeAnchorRequirement(mode); + } + document.querySelectorAll(".backfill-start-button").forEach(button => { + button.addEventListener("click", () => { + const mode = button.dataset.backfillMode; + if (mode) { + void executeBackfill(mode); + } + }); + }); + element("#cancelBackfillButton").addEventListener("click", () => { + void cancelBackfill(); + }); + element("#clearBackfillLogButton").addEventListener("click", () => { + logLines.length = 0; + element("#backfillLogOutput").value = ""; + }); + void listen("demo-backfill-progress", event => { + appendLog(event.payload); + }); + void loadOptions().catch(caughtError => { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + appendLog({ timestamp: new Date().toISOString(), level: "error", message }); + frontendError("kb_app_demo.frontend.demo_backfill", `Options loading failed: ${message}`); + }); +}); diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_config.ts b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_config.ts new file mode 100644 index 0000000..4c0cee9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_config.ts @@ -0,0 +1,84 @@ +// file: kb_app_demo/frontend/ts/demo_config.ts +// version: 4 + +import * as bootstrap from "bootstrap"; +import "simplebar"; +import "@andypf/json-viewer"; +import ResizeObserver from "resize-observer-polyfill"; +import { invoke } from "@tauri-apps/api/core"; +import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log"; +import type { DemoConfigPayload } from "./bindings/kb_app_demo/demo_config/DemoConfigPayload"; + +(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap; +(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver; + +function setText(selector: string, value: string): void { + const element = document.querySelector(selector); + if (element) { + element.textContent = value; + } +} + +function renderJsonViewer(selector: string, value: unknown): void { + const container = document.querySelector(selector); + if (!container) { + return; + } + const serialized = JSON.stringify(value, null, 2) ?? "null"; + const viewer = document.createElement("andypf-json-viewer"); + viewer.setAttribute("indent", "2"); + viewer.setAttribute("expanded", "1"); + viewer.setAttribute("theme", "default-light"); + viewer.setAttribute("show-data-types", "true"); + viewer.setAttribute("show-toolbar", "false"); + viewer.setAttribute("expand-icon-type", "arrow"); + viewer.setAttribute("show-copy", "true"); + viewer.setAttribute("show-size", "true"); + viewer.setAttribute("expand-empty", "false"); + viewer.setAttribute("data", serialized); + container.textContent = ""; + container.appendChild(viewer); +} + +function parseSchemaJson(schemaJson: string): unknown { + try { + return JSON.parse(schemaJson) as unknown; + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + return { + error: `Schema JSON parsing failed: ${message}`, + raw: schemaJson, + }; + } +} + +async function loadDemoConfig(): Promise { + try { + const payload = await invoke("load_demo_config"); + setText("#configPath", payload.config_path); + setText("#activeProfileName", payload.active_profile_name); + setText("#activeEnvironment", payload.environment); + renderJsonViewer("#activeProfileJson", payload.active_profile); + renderJsonViewer("#fullConfigJson", payload.app_config); + renderJsonViewer("#schemaJson", parseSchemaJson(payload.schema_json)); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + setText("#activeProfileJson", `Erreur pendant le chargement de la configuration : ${message}`); + frontendError("kb_app_demo.frontend.demo_config", `Configuration loading failed: ${message}`); + } +} + +document.addEventListener("DOMContentLoaded", async () => { + installFrontendConsoleBridge("kb_app_demo.frontend.demo_config"); + + frontendDebug("kb_app_demo.frontend.demo_config", "configuration demo window loaded"); + + const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); + Array.from(tooltipTriggerList).map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)); + const toastElList = document.querySelectorAll('.toast'); + Array.from(toastElList).map(toastEl => new bootstrap.Toast(toastEl)); + const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]'); + Array.from(popoverTriggerList).map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl)); + + await loadDemoConfig(); +}); diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_core_extraction.ts b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_core_extraction.ts new file mode 100644 index 0000000..45fca21 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_core_extraction.ts @@ -0,0 +1,185 @@ +// file: kb_app_demo/frontend/ts/demo_core_extraction.ts +// version: 4 + +import * as bootstrap from "bootstrap"; +import "simplebar"; +import ResizeObserver from "resize-observer-polyfill"; +import { invoke } from "@tauri-apps/api/core"; +import { listen } from "@tauri-apps/api/event"; +import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log"; +import type { DemoCoreExtractionOptionsPayload } from "./bindings/kb_app_demo/demo_core_extraction/DemoCoreExtractionOptionsPayload"; +import type { DemoCoreExtractionProgressPayload } from "./bindings/kb_app_demo/demo_core_extraction/DemoCoreExtractionProgressPayload"; +import type { DemoCoreExtractionRequest } from "./bindings/kb_app_demo/demo_core_extraction/DemoCoreExtractionRequest"; +import type { DemoCoreExtractionSummaryPayload } from "./bindings/kb_app_demo/demo_core_extraction/DemoCoreExtractionSummaryPayload"; + +(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap; +(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver; + +const logLines: string[] = []; +const maximumLogLines = 1000; +let running = false; + +function element(selector: string): T { + const value = document.querySelector(selector); + if (!value) { + throw new Error(`Missing UI element: ${selector}`); + } + return value; +} + +function inputValue(selector: string): string { + return element(selector).value.trim(); +} + +function integerValue(selector: string): number { + const parsed = Number.parseInt(inputValue(selector), 10); + if (!Number.isFinite(parsed)) { + throw new Error(`Valeur numérique invalide pour ${selector}`); + } + return parsed; +} + +function nullableIntegerValue(selector: string): number | null { + const value = inputValue(selector); + if (value.length === 0) { + return null; + } + const parsed = Number.parseInt(value, 10); + if (!Number.isFinite(parsed)) { + throw new Error(`Valeur numérique invalide pour ${selector}`); + } + return parsed; +} + +function appendLog(payload: DemoCoreExtractionProgressPayload | { timestamp: string; level: string; message: string }): void { + const progress = "completed" in payload ? ` [${payload.completed}/${payload.total}]` : ""; + logLines.push(`${payload.timestamp} ${payload.level.toUpperCase()}${progress} ${payload.message}`); + while (logLines.length > maximumLogLines) { + logLines.shift(); + } + const output = element("#coreExtractionLogOutput"); + output.value = logLines.join("\n"); + output.scrollTop = output.scrollHeight; +} + +function setRunning(value: boolean): void { + running = value; + document.querySelectorAll(".core-extraction-start-button").forEach(button => { + button.disabled = value; + }); + element("#cancelCoreExtractionButton").disabled = !value; + const badge = element("#coreExtractionStatusBadge"); + badge.textContent = value ? "Extraction en cours" : "Prêt"; + badge.className = value ? "badge text-bg-warning" : "badge text-bg-success"; +} + +function buildRequest(mode: string): DemoCoreExtractionRequest { + return { + mode, + signaturesText: mode === "signatures" ? element("#coreExtractionSignaturesTextarea").value : null, + programId: mode === "program_id" ? inputValue("#coreExtractionProgramIdInput") : null, + minSlot: mode === "slot_range" ? nullableIntegerValue("#coreExtractionMinSlotInput") : null, + maxSlot: mode === "slot_range" ? nullableIntegerValue("#coreExtractionMaxSlotInput") : null, + limit: integerValue("#coreExtractionLimitInput"), + maxConcurrentExtractions: integerValue("#coreExtractionConcurrencyInput"), + forceReplay: element("#coreExtractionForceReplayInput").checked, + }; +} + +async function executeCoreExtraction(mode: string): Promise { + if (running) { + return; + } + try { + const request = buildRequest(mode); + setRunning(true); + element("#coreExtractionSummaryOutput").value = "Exécution en cours..."; + appendLog({ timestamp: new Date().toISOString(), level: "info", message: `Démarrage du mode ${mode}` }); + const summary = await invoke("demo_core_extraction_execute", { request }); + element("#coreExtractionSummaryOutput").value = JSON.stringify(summary, null, 2); + appendLog({ + timestamp: new Date().toISOString(), + level: summary.cancelled || Number(summary.failed) > 0 ? "warn" : "info", + message: `Campagne terminée : extracted=${summary.extracted}, skipped=${summary.skipped}, failed=${summary.failed}, cancelled=${summary.cancelledCandidates}, notStarted=${summary.notStarted}`, + }); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + element("#coreExtractionSummaryOutput").value = JSON.stringify({ error: message }, null, 2); + appendLog({ timestamp: new Date().toISOString(), level: "error", message }); + frontendError("kb_app_demo.frontend.demo_core_extraction", `Core extraction failed: ${message}`); + } finally { + setRunning(false); + } +} + +async function cancelCoreExtraction(): Promise { + try { + const accepted = await invoke("demo_core_extraction_cancel"); + appendLog({ + timestamp: new Date().toISOString(), + level: accepted ? "warn" : "info", + message: accepted ? "Demande d'arrêt envoyée." : "Aucune campagne active.", + }); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + frontendError("kb_app_demo.frontend.demo_core_extraction", `Cancellation failed: ${message}`); + } +} + + +async function openDiagnostics(command: "open_demo_sql_pg_raw_window" | "open_demo_sql_pg_core_window" | "open_demo_sql_replay_candidates_window", label: string): Promise { + try { + await invoke(command); + appendLog({ timestamp: new Date().toISOString(), level: "info", message: `${label} ouvert.` }); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + appendLog({ timestamp: new Date().toISOString(), level: "error", message }); + frontendError("kb_app_demo.frontend.demo_core_extraction", `${label} opening failed: ${message}`); + } +} + +async function loadOptions(): Promise { + const options = await invoke("demo_core_extraction_options"); + element("#coreExtractionVersionBadge").textContent = `Version ${options.processorVersion}`; + element("#coreExtractionLimitInput").value = String(options.defaultLimit); + element("#coreExtractionConcurrencyInput").value = String(options.defaultMaxConcurrentExtractions); + setRunning(options.running); +} + +document.addEventListener("DOMContentLoaded", () => { + installFrontendConsoleBridge("kb_app_demo.frontend.demo_core_extraction"); + frontendDebug("kb_app_demo.frontend.demo_core_extraction", "core extraction demo window loaded"); + document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(item => new bootstrap.Tooltip(item)); + document.querySelectorAll(".core-extraction-start-button").forEach(button => { + button.addEventListener("click", () => { + const mode = button.dataset.coreExtractionMode; + if (mode) { + void executeCoreExtraction(mode); + } + }); + }); + element("#openRawDiagnosticsButton").addEventListener("click", () => { + void openDiagnostics("open_demo_sql_pg_raw_window", "Diagnostic raw"); + }); + element("#openCoreDiagnosticsButton").addEventListener("click", () => { + void openDiagnostics("open_demo_sql_pg_core_window", "Diagnostic core"); + }); + element("#openReplayCandidatesButton").addEventListener("click", () => { + void openDiagnostics("open_demo_sql_replay_candidates_window", "Sélecteur de candidats replay"); + }); + element("#cancelCoreExtractionButton").addEventListener("click", () => { + void cancelCoreExtraction(); + }); + element("#clearCoreExtractionLogButton").addEventListener("click", () => { + logLines.length = 0; + element("#coreExtractionLogOutput").value = ""; + }); + void listen("demo-core-extraction-progress", event => { + appendLog(event.payload); + }); + void loadOptions().catch(caughtError => { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + appendLog({ timestamp: new Date().toISOString(), level: "error", message }); + frontendError("kb_app_demo.frontend.demo_core_extraction", `Options loading failed: ${message}`); + }); +}); diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_decode_replay.ts b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_decode_replay.ts new file mode 100644 index 0000000..9f70c0c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_decode_replay.ts @@ -0,0 +1,340 @@ +// file: kb_app_demo/frontend/ts/demo_decode_replay.ts +// version: 5 + +import * as bootstrap from "bootstrap"; +import "simplebar"; +import ResizeObserver from "resize-observer-polyfill"; +import { invoke } from "@tauri-apps/api/core"; +import { listen } from "@tauri-apps/api/event"; +import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log"; +import type { DemoDecodeDiagnosticsPayload } from "./bindings/kb_app_demo/demo_decode_replay/DemoDecodeDiagnosticsPayload"; +import type { DemoDecodeReplayOptionsPayload } from "./bindings/kb_app_demo/demo_decode_replay/DemoDecodeReplayOptionsPayload"; +import type { DemoDecodeReplayProgressPayload } from "./bindings/kb_app_demo/demo_decode_replay/DemoDecodeReplayProgressPayload"; +import type { DemoDecodeReplayRequest } from "./bindings/kb_app_demo/demo_decode_replay/DemoDecodeReplayRequest"; +import type { DemoDecodeReplaySummaryPayload } from "./bindings/kb_app_demo/demo_decode_replay/DemoDecodeReplaySummaryPayload"; +import type { DemoTransactionAnnotationRequest } from "./bindings/kb_app_demo/demo_decode_replay/DemoTransactionAnnotationRequest"; +import type { DemoTransactionAnnotationRow } from "./bindings/kb_app_demo/demo_decode_replay/DemoTransactionAnnotationRow"; + +(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap; +(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver; + +const logLines: string[] = []; +let running = false; +let materializationAvailable = false; + +function element(selector: string): T { + const value = document.querySelector(selector); + if (!value) { + throw new Error(`Missing UI element: ${selector}`); + } + return value; +} + +function inputValue(selector: string): string { + return element(selector).value.trim(); +} + +function integerValue(selector: string): number { + const value = Number.parseInt(inputValue(selector), 10); + if (!Number.isFinite(value) || value < 1) { + throw new Error(`Valeur numérique invalide pour ${selector}`); + } + return value; +} + +function appendLog(payload: DemoDecodeReplayProgressPayload | { timestamp: string; level: string; message: string }): void { + const progress = "completed" in payload ? ` [${payload.completed}/${payload.total}]` : ""; + const campaign = "campaignId" in payload ? ` [${payload.campaignId}]` : ""; + logLines.push(`${payload.timestamp} ${payload.level.toUpperCase()}${campaign}${progress} ${payload.message}`); + while (logLines.length > 1000) { + logLines.shift(); + } + const output = element("#decodeReplayLogOutput"); + output.value = logLines.join("\n"); + output.scrollTop = output.scrollHeight; +} + +function explicitSignatureCount(): number { + return element("#decodeReplaySignaturesTextarea") + .value + .split(/\r?\n/) + .filter(value => value.trim().length > 0) + .length; +} + +function updateReplayScopeControls(): void { + const signaturesPresent = explicitSignatureCount() > 0; + const incompleteSignatures = inputValue("#decodeReplayStateSelect") === "incomplete_signatures"; + const allMatchingInput = element("#decodeReplayAllSignaturesInput"); + const forceInput = element("#decodeReplayForceInput"); + const materializeInput = element("#decodeReplayMaterializeInput"); + if (!running && (signaturesPresent || incompleteSignatures) && allMatchingInput.checked) { + allMatchingInput.checked = false; + } + allMatchingInput.disabled = running || signaturesPresent || incompleteSignatures; + const forceScopeMissing = !signaturesPresent && !allMatchingInput.checked; + forceInput.disabled = running || forceScopeMissing || incompleteSignatures; + if (!running && (forceScopeMissing || incompleteSignatures)) { + forceInput.checked = false; + } + materializeInput.disabled = running || !materializationAvailable; + if (!running && !materializationAvailable) { + materializeInput.checked = false; + } +} + +function setRunning(value: boolean): void { + running = value; + element("#startDecodeReplayButton").disabled = value; + element("#cancelDecodeReplayButton").disabled = !value; + const badge = element("#decodeReplayStatusBadge"); + badge.textContent = value ? "Replay en cours" : "Prêt"; + badge.className = value ? "badge text-bg-warning" : "badge text-bg-success"; + updateReplayScopeControls(); +} + +function selectedDecoderNames(): string[] { + return Array.from(document.querySelectorAll(".decode-replay-decoder-input:checked")) + .map(input => input.value); +} + +function buildRequest(): DemoDecodeReplayRequest { + return { + signaturesText: element("#decodeReplaySignaturesTextarea").value || null, + programId: inputValue("#decodeReplayProgramIdInput") || null, + instructionState: inputValue("#decodeReplayStateSelect"), + instructionPathsText: element("#decodeReplayPathsTextarea").value || null, + decoderNames: selectedDecoderNames(), + limit: integerValue("#decodeReplayLimitInput"), + maxConcurrentInputs: integerValue("#decodeReplayConcurrencyInput"), + allCompatible: element("#decodeReplayAllCompatibleInput").checked, + forceReplay: element("#decodeReplayForceInput").checked, + forceReplayAllMatching: element("#decodeReplayAllSignaturesInput").checked, + materializeAfterDecode: element("#decodeReplayMaterializeInput").checked, + }; +} + +function validationMessage(request: DemoDecodeReplayRequest): string | null { + const signatureCount = request.signaturesText + ? request.signaturesText.split(/\r?\n/).filter(value => value.trim().length > 0).length + : 0; + if (request.decoderNames.length === 0) { + return "Sélectionner au moins un décodeur."; + } + if (request.instructionState === "incomplete_signatures" && request.forceReplay) { + return "Le mode signatures incomplètes rejoue son périmètre sans Force replay global."; + } + if (request.forceReplay && signatureCount === 0 && !request.forceReplayAllMatching) { + return "Le force replay nécessite des signatures explicites ou l’autorisation Toutes les signatures."; + } + if (request.forceReplayAllMatching && signatureCount > 0) { + return "Le mode « Toutes les signatures » ne peut pas être combiné avec une liste de signatures explicites."; + } + if (request.forceReplayAllMatching && !request.forceReplay) { + return "Le mode « Toutes les signatures » est uniquement disponible avec Force replay."; + } + if (request.materializeAfterDecode && !materializationAvailable) { + return "Aucun matérialiseur n’est actuellement enregistré dans cette application."; + } + return null; +} + +function reportValidationWarning(message: string): void { + element("#decodeReplaySummaryOutput").value = JSON.stringify({ validation: message }, null, 2); + appendLog({ timestamp: new Date().toISOString(), level: "warn", message }); + frontendDebug("kb_app_demo.frontend.demo_decode_replay", `Decode replay request rejected locally: ${message}`); +} + +async function executeDecodeReplay(): Promise { + if (running) { + return; + } + try { + const request = buildRequest(); + const invalid = validationMessage(request); + if (invalid) { + reportValidationWarning(invalid); + return; + } + setRunning(true); + element("#decodeReplaySummaryOutput").value = "Exécution en cours..."; + const signatureCount = request.signaturesText + ? request.signaturesText.split(/\r?\n/).filter(value => value.trim().length > 0).length + : 0; + const requestSummary = `signatures=${signatureCount}, programId=${request.programId ?? "auto"}, state=${request.instructionState}, decoders=${request.decoderNames.join(",")}, limit=${request.limit}, concurrency=${request.maxConcurrentInputs}, allCompatible=${request.allCompatible}, forceReplay=${request.forceReplay}, allSignatures=${request.forceReplayAllMatching}, materialize=${request.materializeAfterDecode}`; + appendLog({ timestamp: new Date().toISOString(), level: "info", message: `Démarrage du replay de décodage contextualisé : ${requestSummary}` }); + frontendDebug("kb_app_demo.frontend.demo_decode_replay", `Invoke demo_decode_replay_execute ${requestSummary}`); + const summary = await invoke("demo_decode_replay_execute", { request }); + element("#decodeReplaySummaryOutput").value = JSON.stringify(summary, null, 2); + frontendDebug("kb_app_demo.frontend.demo_decode_replay", `Invoke demo_decode_replay_execute completed summary=${JSON.stringify(summary)}`); + appendLog({ + timestamp: new Date().toISOString(), + level: summary.cancelled || summary.failedInputs > 0 ? "warn" : "info", + message: `Campagne ${summary.campaignId} terminée : completed=${summary.completed}, unmatched=${summary.unmatched}, failed=${summary.failedInputs}, notStarted=${summary.notStarted}`, + }); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + element("#decodeReplaySummaryOutput").value = JSON.stringify({ error: message }, null, 2); + appendLog({ timestamp: new Date().toISOString(), level: "error", message }); + frontendError("kb_app_demo.frontend.demo_decode_replay", `Decode replay failed: ${message}`); + } finally { + setRunning(false); + } +} + +async function cancelDecodeReplay(): Promise { + try { + frontendDebug("kb_app_demo.frontend.demo_decode_replay", "Invoke demo_decode_replay_cancel"); + const accepted = await invoke("demo_decode_replay_cancel"); + frontendDebug("kb_app_demo.frontend.demo_decode_replay", `Invoke demo_decode_replay_cancel completed accepted=${accepted}`); + appendLog({ + timestamp: new Date().toISOString(), + level: accepted ? "warn" : "info", + message: accepted ? "Demande d'arrêt envoyée." : "Aucune campagne active.", + }); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + frontendError("kb_app_demo.frontend.demo_decode_replay", `Cancellation failed: ${message}`); + } +} + +async function loadDiagnostics(): Promise { + try { + frontendDebug("kb_app_demo.frontend.demo_decode_replay", "Invoke demo_decode_replay_diagnostics"); + const diagnostics = await invoke("demo_decode_replay_diagnostics"); + frontendDebug("kb_app_demo.frontend.demo_decode_replay", `Invoke demo_decode_replay_diagnostics completed tables=${diagnostics.tables.length} coverage=${diagnostics.coverage.length}`); + element("#decodeReplayDiagnosticsOutput").value = JSON.stringify(diagnostics, null, 2); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + element("#decodeReplayDiagnosticsOutput").value = JSON.stringify({ error: message }, null, 2); + frontendError("kb_app_demo.frontend.demo_decode_replay", `Diagnostics loading failed: ${message}`); + } +} + +function tableCell(value: string, title?: string): HTMLTableCellElement { + const cell = document.createElement("td"); + cell.textContent = value; + if (title) { + cell.title = title; + } + return cell; +} + +function shortened(value: string, visible: number): string { + return value.length > visible ? `${value.slice(0, visible)}…` : value; +} + +function renderAnnotations(rows: DemoTransactionAnnotationRow[]): void { + const body = element("#annotationJournalBody"); + body.replaceChildren(); + for (const row of rows) { + const tableRow = document.createElement("tr"); + tableRow.append( + tableCell(row.slot), + tableCell(shortened(row.signature, 18), row.signature), + tableCell(row.instructionPath), + tableCell(row.generation, row.programId), + tableCell(row.text), + tableCell(String(row.payloadLengthBytes)), + tableCell(row.verifiedSigners.join(", ") || "—"), + tableCell(`${shortened(row.payloadSha256, 12)} · ${row.decoder}`, `${row.payloadSha256}\n${row.idempotenceKey}\ntransaction_annotations@${row.processorVersion}`), + ); + body.append(tableRow); + } + element("#annotationJournalStatus").textContent = `${rows.length} annotation(s) committed chargée(s).`; +} + +async function loadAnnotations(): Promise { + const request: DemoTransactionAnnotationRequest = { + signatureContains: inputValue("#annotationSignatureInput") || null, + limit: integerValue("#annotationLimitInput"), + }; + try { + frontendDebug("kb_app_demo.frontend.demo_decode_replay", `Invoke demo_decode_replay_annotations limit=${request.limit}`); + const rows = await invoke("demo_decode_replay_annotations", { request }); + renderAnnotations(rows); + frontendDebug("kb_app_demo.frontend.demo_decode_replay", `Invoke demo_decode_replay_annotations completed rows=${rows.length}`); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + element("#annotationJournalStatus").textContent = `Erreur : ${message}`; + frontendError("kb_app_demo.frontend.demo_decode_replay", `Annotation journal loading failed: ${message}`); + } +} + +async function openWindow(command: "open_demo_sql_pg_core_window" | "open_demo_sql_replay_candidates_window"): Promise { + try { + frontendDebug("kb_app_demo.frontend.demo_decode_replay", `Invoke ${command}`); + await invoke(command); + frontendDebug("kb_app_demo.frontend.demo_decode_replay", `Invoke ${command} completed`); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + frontendError("kb_app_demo.frontend.demo_decode_replay", `Window opening failed: ${message}`); + } +} + +async function loadOptions(): Promise { + frontendDebug("kb_app_demo.frontend.demo_decode_replay", "Invoke demo_decode_replay_options"); + const options = await invoke("demo_decode_replay_options"); + frontendDebug("kb_app_demo.frontend.demo_decode_replay", `Invoke demo_decode_replay_options completed options=${JSON.stringify(options)}`); + element("#decodeReplayVersionBadge").textContent = `Pipeline ${options.pipelineVersion}`; + element("#decodeReplayLimitInput").value = String(options.defaultLimit); + element("#decodeReplayConcurrencyInput").value = String(options.defaultMaxConcurrentInputs); + const container = element("#decodeReplayDecoderList"); + container.innerHTML = ""; + for (const decoder of options.decoders) { + const wrapper = document.createElement("div"); + wrapper.className = "form-check border rounded p-3 ps-5 mb-2"; + const input = document.createElement("input"); + input.className = "form-check-input decode-replay-decoder-input"; + input.type = "checkbox"; + input.value = decoder.name; + input.id = `decode-${decoder.name}`; + input.checked = true; + const label = document.createElement("label"); + label.className = "form-check-label"; + label.htmlFor = input.id; + label.textContent = `${decoder.name} v${decoder.version} — ${decoder.programIds.length} programmes`; + wrapper.append(input, label); + container.append(wrapper); + } + materializationAvailable = options.materializerNames.length > 0; + const materializeHelp = element("#decodeReplayMaterializeHelp"); + materializeHelp.textContent = materializationAvailable + ? `Matérialiseurs disponibles : ${options.materializerNames.join(", ")}` + : "Aucun matérialiseur enregistré : option désactivée."; + setRunning(options.running); +} + +document.addEventListener("DOMContentLoaded", () => { + installFrontendConsoleBridge("kb_app_demo.frontend.demo_decode_replay"); + frontendDebug("kb_app_demo.frontend.demo_decode_replay", "decode replay demo window loaded"); + document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(item => new bootstrap.Tooltip(item)); + element("#decodeReplaySignaturesTextarea").addEventListener("input", () => updateReplayScopeControls()); + element("#decodeReplayStateSelect").addEventListener("change", () => updateReplayScopeControls()); + element("#decodeReplayAllSignaturesInput").addEventListener("change", event => { + const input = event.currentTarget as HTMLInputElement; + if (input.checked) { + element("#decodeReplayForceInput").checked = true; + } + updateReplayScopeControls(); + }); + element("#decodeReplayForceInput").addEventListener("change", event => { + const input = event.currentTarget as HTMLInputElement; + if (!input.checked) { + element("#decodeReplayAllSignaturesInput").checked = false; + } + updateReplayScopeControls(); + }); + element("#startDecodeReplayButton").addEventListener("click", () => void executeDecodeReplay()); + element("#cancelDecodeReplayButton").addEventListener("click", () => void cancelDecodeReplay()); + element("#loadDecodeDiagnosticsButton").addEventListener("click", () => void loadDiagnostics()); + element("#loadAnnotationsButton").addEventListener("click", () => void loadAnnotations()); + element("#openReplayCandidatesFromDecodeButton").addEventListener("click", () => void openWindow("open_demo_sql_replay_candidates_window")); + element("#openCoreDiagnosticsFromDecodeButton").addEventListener("click", () => void openWindow("open_demo_sql_pg_core_window")); + void listen("demo-decode-replay-progress", event => appendLog(event.payload)); + void loadOptions().catch(caughtError => { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + appendLog({ timestamp: new Date().toISOString(), level: "error", message }); + frontendError("kb_app_demo.frontend.demo_decode_replay", `Options loading failed: ${message}`); + }); +}); diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_execution_solana_core.ts b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_execution_solana_core.ts new file mode 100644 index 0000000..842c897 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_execution_solana_core.ts @@ -0,0 +1,31 @@ +// file: kb_app_demo/frontend/ts/demo_execution_solana_core.ts +// version: 6 + +import * as bootstrap from "bootstrap"; +import "simplebar"; +import ResizeObserver from "resize-observer-polyfill"; +import { invoke } from "@tauri-apps/api/core"; +import { listen } from "@tauri-apps/api/event"; +import { frontendDebug, frontendError, frontendWarn, installFrontendConsoleBridge } from "./frontend_log"; +import type { DemoExecutionSolanaCoreGeneratedRecipientPayload } from "./bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionSolanaCoreGeneratedRecipientPayload"; +import type { DemoExecutionSolanaCoreOptionsPayload } from "./bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionSolanaCoreOptionsPayload"; +import type { DemoExecutionSolanaCoreProfileOption } from "./bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionSolanaCoreProfileOption"; +import type { DemoExecutionSolanaCoreProgressPayload } from "./bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionSolanaCoreProgressPayload"; +import type { DemoExecutionSolanaCoreRequest } from "./bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionSolanaCoreRequest"; +import type { DemoExecutionSolanaCoreSummaryPayload } from "./bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionSolanaCoreSummaryPayload"; +(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap; +(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver; +const logLines: string[] = []; +let running = false; +let profileOptions: DemoExecutionSolanaCoreProfileOption[] = []; +function element(selector: string): T { const value=document.querySelector(selector); if(!value){throw new Error(`Missing UI element: ${selector}`);} return value; } +function integerValue(selector: string): number { const value=Number.parseInt(element(selector).value,10); if(!Number.isFinite(value)||value<0){throw new Error(`Valeur numérique invalide pour ${selector}`);} return value; } +function selectedProfile(): DemoExecutionSolanaCoreProfileOption|null { const name=element("#executionProfileSelect").value; return profileOptions.find(profile=>profile.name===name)??null; } +function appendLog(payload: DemoExecutionSolanaCoreProgressPayload|{timestamp:string;level:string;stage:string;message:string;signature?:string|null}):void { const signature=payload.signature?` signature=${payload.signature}`:""; logLines.push(`${payload.timestamp} ${payload.level.toUpperCase()} [${payload.stage}] ${payload.message}${signature}`); const output=element("#executionLogOutput"); output.value=logLines.join("\n"); output.scrollTop=output.scrollHeight; } +function setRunning(value:boolean):void { running=value; const profile=selectedProfile(); element("#simulateExecutionButton").disabled=value||!profile; element("#submitExecutionButton").disabled=value||!profile||!profile.sendEnabled; element("#generateRecipientButton").disabled=value; element("#cancelExecutionButton").disabled=!value; const badge=element("#executionStatusBadge"); badge.textContent=value?"Exécution en cours":"Prêt"; badge.className=value?"badge text-bg-warning":"badge text-bg-success"; } +function updateProfileHelp():void { const profile=selectedProfile(); const help=element("#executionProfileHelp"); if(!profile){help.textContent="Aucun profil Devnet compatible.";return;} help.textContent=`wallet=${profile.walletAlias}, spendMax=${profile.maxSpendLamports}, airdropMax=${profile.maxAirdropLamports}, send=${profile.sendEnabled?"enabled":"disabled"}`; element("#executionAirdropInput").max=String(profile.maxAirdropLamports); element("#executionLamportsInput").max=String(profile.maxSpendLamports); setRunning(running); } +function buildRequest(submit:boolean):DemoExecutionSolanaCoreRequest { const recipient=element("#executionRecipientInput").value.trim(); if(recipient.length===0){throw new Error("Le destinataire est obligatoire.");} return {profileName:element("#executionProfileSelect").value,recipient,lamports:integerValue("#executionLamportsInput"),airdropLamports:integerValue("#executionAirdropInput"),submit,operatorConfirmed:element("#executionOperatorConfirmedInput").checked,forcePostValidationReplay:element("#executionForceReplayInput").checked,materializeAfterDecode:element("#executionMaterializeInput").checked}; } +function displaySummary(summary:DemoExecutionSolanaCoreSummaryPayload):void { element("#executionSummaryOutput").value=JSON.stringify(summary,null,2); element("#executionPlanOutput").value=summary.planJson; element("#executionSimulationOutput").value=summary.simulationJson; element("#executionDiagnosticsOutput").value=summary.diagnosticsJson; } +async function execute(submit:boolean):Promise{if(running){return;}try{const request=buildRequest(submit);if(submit&&!request.operatorConfirmed){frontendWarn("kb_app_demo.frontend.demo_execution_solana_core","Submission blocked: operator confirmation missing");return;}setRunning(true);const summary=await invoke("demo_execution_solana_core_execute",{request});displaySummary(summary);}catch(caughtError){const message=caughtError instanceof Error?caughtError.message:String(caughtError);frontendError("kb_app_demo.frontend.demo_execution_solana_core",`Execution failed: ${message}`);}finally{setRunning(false);}} +async function loadOptions():Promise{const options=await invoke("demo_execution_solana_core_options");profileOptions=options.profiles;const select=element("#executionProfileSelect");select.replaceChildren();for(const profile of profileOptions){const option=document.createElement("option");option.value=profile.name;option.textContent=profile.name;option.selected=profile.name===options.defaultProfileName;select.append(option);}element("#executionLamportsInput").value=String(options.defaultTransferLamports);updateProfileHelp();setRunning(options.running);} +document.addEventListener("DOMContentLoaded",async()=>{installFrontendConsoleBridge("kb_app_demo.frontend.demo_execution_solana_core");frontendDebug("kb_app_demo.frontend.demo_execution_solana_core","Solana Core execution window loaded");await listen("demo-execution-solana-core-progress",event=>appendLog(event.payload));element("#executionProfileSelect").addEventListener("change",updateProfileHelp);element("#generateRecipientButton").addEventListener("click",async()=>{const payload=await invoke("demo_execution_solana_core_generate_recipient");element("#executionRecipientInput").value=payload.publicKey;});element("#simulateExecutionButton").addEventListener("click",()=>void execute(false));element("#submitExecutionButton").addEventListener("click",()=>void execute(true));element("#cancelExecutionButton").addEventListener("click",()=>void invoke("demo_execution_solana_core_cancel"));element("#clearExecutionLogButton").addEventListener("click",()=>{logLines.length=0;element("#executionLogOutput").value="";});try{await loadOptions();}catch(caughtError){const message=caughtError instanceof Error?caughtError.message:String(caughtError);frontendError("kb_app_demo.frontend.demo_execution_solana_core",`Options loading failed: ${message}`);}}); diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_execution_spl.ts b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_execution_spl.ts new file mode 100644 index 0000000..a9f8701 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_execution_spl.ts @@ -0,0 +1,881 @@ +// file: kb_app_demo/frontend/ts/demo_execution_spl.ts +// version: 2 + +import * as bootstrap from "bootstrap"; +import "simplebar"; +import ResizeObserver from "resize-observer-polyfill"; +import { invoke } from "@tauri-apps/api/core"; +import { listen } from "@tauri-apps/api/event"; +import { frontendDebug, frontendError, frontendWarn, installFrontendConsoleBridge } from "./frontend_log"; +import type { DemoExecutionSolanaCoreGeneratedRecipientPayload } from "./bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionSolanaCoreGeneratedRecipientPayload"; +import type { DemoExecutionSolanaCoreOptionsPayload } from "./bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionSolanaCoreOptionsPayload"; +import type { DemoExecutionSolanaCoreProfileOption } from "./bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionSolanaCoreProfileOption"; +import type { DemoExecutionSolanaCoreProgressPayload } from "./bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionSolanaCoreProgressPayload"; +import type { DemoExecutionSolanaCoreRequest } from "./bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionSolanaCoreRequest"; +import type { DemoExecutionSolanaCoreSummaryPayload } from "./bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionSolanaCoreSummaryPayload"; +import type { DemoExecutionMemoRequest } from "./bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionMemoRequest"; +import type { DemoExecutionMemoSummaryPayload } from "./bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionMemoSummaryPayload"; +import type { DemoExecutionSplTokenRequest } from "./bindings/kb_app_demo/demo_spl_token/DemoExecutionSplTokenRequest"; +import type { DemoExecutionSplTokenSummaryPayload } from "./bindings/kb_app_demo/demo_spl_token/DemoExecutionSplTokenSummaryPayload"; +import type { DemoSplTokenJournalRequest } from "./bindings/kb_app_demo/demo_spl_token/DemoSplTokenJournalRequest"; +import type { DemoSplTokenJournalRow } from "./bindings/kb_app_demo/demo_spl_token/DemoSplTokenJournalRow"; +import type { DemoExecutionSplAtaRequest } from "./bindings/kb_app_demo/demo_spl_ata/DemoExecutionSplAtaRequest"; +import type { DemoExecutionSplAtaSummaryPayload } from "./bindings/kb_app_demo/demo_spl_ata/DemoExecutionSplAtaSummaryPayload"; +import type { DemoSplAtaDerivationPayload } from "./bindings/kb_app_demo/demo_spl_ata/DemoSplAtaDerivationPayload"; +import type { DemoSplAtaDerivationRequest } from "./bindings/kb_app_demo/demo_spl_ata/DemoSplAtaDerivationRequest"; +import type { DemoSplAtaJournalRequest } from "./bindings/kb_app_demo/demo_spl_ata/DemoSplAtaJournalRequest"; +import type { DemoSplAtaJournalRow } from "./bindings/kb_app_demo/demo_spl_ata/DemoSplAtaJournalRow"; +import type { DemoExecutionSplToken2022Request } from "./bindings/kb_app_demo/demo_spl_token_2022/DemoExecutionSplToken2022Request"; +import type { DemoExecutionSplToken2022SummaryPayload } from "./bindings/kb_app_demo/demo_spl_token_2022/DemoExecutionSplToken2022SummaryPayload"; +import type { DemoSplToken2022FixturePayload } from "./bindings/kb_app_demo/demo_spl_token_2022/DemoSplToken2022FixturePayload"; + + +(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap; +(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver; + +const logLines: string[] = []; +const maximumLogLines = 1200; +let running = false; +let profileOptions: DemoExecutionSolanaCoreProfileOption[] = []; +let token2022Fixture: DemoSplToken2022FixturePayload | null = null; + +function element(selector: string): T { + const value = document.querySelector(selector); + if (!value) { + throw new Error(`Missing UI element: ${selector}`); + } + return value; +} + +function integerValue(selector: string): number { + const value = Number.parseInt(element(selector).value, 10); + if (!Number.isFinite(value) || value < 0) { + throw new Error(`Valeur numérique invalide pour ${selector}`); + } + return value; +} + +function appendLog(payload: DemoExecutionSolanaCoreProgressPayload | { timestamp: string; level: string; stage: string; message: string; signature?: string | null }): void { + const signature = payload.signature ? ` signature=${payload.signature}` : ""; + logLines.push(`${payload.timestamp} ${payload.level.toUpperCase()} [${payload.stage}] ${payload.message}${signature}`); + while (logLines.length > maximumLogLines) { + logLines.shift(); + } + const output = element("#executionLogOutput"); + output.value = logLines.join("\n"); + output.scrollTop = output.scrollHeight; +} + +function setRunning(value: boolean): void { + running = value; + const profile = selectedProfile(); + element("#simulateExecutionButton").disabled = value || !profile; + element("#submitExecutionButton").disabled = value || !profile || !profile.sendEnabled; + element("#simulateMemoButton").disabled = value || !profile; + element("#submitMemoButton").disabled = value || !profile || !profile.sendEnabled; + element("#simulateTokenButton").disabled = value || !profile; + element("#submitTokenButton").disabled = value || !profile || !profile.sendEnabled; + element("#deriveAtaButton").disabled = value || !profile; + element("#simulateAtaButton").disabled = value || !profile; + element("#submitAtaButton").disabled = value || !profile || !profile.sendEnabled; + element("#simulateToken2022Button").disabled = value || !profile; + element("#submitToken2022Button").disabled = value || !profile || !profile.sendEnabled; + element("#loadToken2022FixtureButton").disabled = value || !profile; + element("#generateRecipientButton").disabled = value; + element("#cancelExecutionButton").disabled = !value; + const badge = element("#executionStatusBadge"); + badge.textContent = value ? "Exécution en cours" : "Prêt"; + badge.className = value ? "badge text-bg-warning" : "badge text-bg-success"; +} + +function selectedProfile(): DemoExecutionSolanaCoreProfileOption | null { + const name = element("#executionProfileSelect").value; + return profileOptions.find(profile => profile.name === name) ?? null; +} + +function updateProfileHelp(): void { + const profile = selectedProfile(); + const help = element("#executionProfileHelp"); + if (!profile) { + help.textContent = "Aucun profil Devnet compatible."; + return; + } + help.textContent = `wallet=${profile.walletAlias}, spendMax=${profile.maxSpendLamports}, airdropMax=${profile.maxAirdropLamports}, send=${profile.sendEnabled ? "enabled" : "disabled"}`; + element("#executionAirdropInput").max = String(profile.maxAirdropLamports); + element("#executionLamportsInput").max = String(profile.maxSpendLamports); + setRunning(running); +} + +function buildRequest(submit: boolean): DemoExecutionSolanaCoreRequest { + const recipient = element("#executionRecipientInput").value.trim(); + if (recipient.length === 0) { + throw new Error("Le destinataire est obligatoire."); + } + return { + profileName: element("#executionProfileSelect").value, + recipient, + lamports: integerValue("#executionLamportsInput"), + airdropLamports: integerValue("#executionAirdropInput"), + submit, + operatorConfirmed: element("#executionOperatorConfirmedInput").checked, + forcePostValidationReplay: element("#executionForceReplayInput").checked, + materializeAfterDecode: element("#executionMaterializeInput").checked, + }; +} + +function displaySummary(summary: DemoExecutionSolanaCoreSummaryPayload): void { + const concise = { + profileName: summary.profileName, + cluster: summary.cluster, + genesisHash: summary.genesisHash, + walletPublicKey: summary.walletPublicKey, + recipient: summary.recipient, + recipientExistedBefore: summary.recipientExistedBefore, + recipientBalanceBeforeLamports: summary.recipientBalanceBeforeLamports, + recipientMinimumBalanceLamports: summary.recipientMinimumBalanceLamports, + balanceBeforeLamports: summary.balanceBeforeLamports, + balanceAfterFundingLamports: summary.balanceAfterFundingLamports, + feeLamports: summary.feeLamports, + simulationSuccess: summary.simulationSuccess, + simulationError: summary.simulationError, + airdropSignature: summary.airdropSignature, + transactionSignature: summary.transactionSignature, + confirmationStatus: summary.confirmationStatus, + canonicalInserted: summary.canonicalInserted, + coreExtracted: summary.coreExtracted, + decodeCompleted: summary.decodeCompleted, + decodeFailedInputs: summary.decodeFailedInputs, + }; + element("#executionSummaryOutput").value = JSON.stringify(concise, null, 2); + element("#executionPlanOutput").value = summary.planJson; + element("#executionSimulationOutput").value = summary.simulationJson; + element("#executionDiagnosticsOutput").value = summary.diagnosticsJson; +} + +function buildMemoRequest(submit: boolean): DemoExecutionMemoRequest { + return { + profileName: element("#executionProfileSelect").value, + message: element("#memoMessageInput").value, + includeWalletAsMemoSigner: element("#memoWalletSignerInput").checked, + submit, + operatorConfirmed: element("#executionOperatorConfirmedInput").checked, + forcePostValidationReplay: element("#executionForceReplayInput").checked, + }; +} + +function displayMemoSummary(summary: DemoExecutionMemoSummaryPayload): void { + const concise = { + operation: "spl_memo_v4.add_memo", + profileName: summary.profileName, + cluster: summary.cluster, + genesisHash: summary.genesisHash, + walletPublicKey: summary.walletPublicKey, + balanceLamports: summary.balanceLamports, + feeLamports: summary.feeLamports, + simulationSuccess: summary.simulationSuccess, + simulationError: summary.simulationError, + transactionSignature: summary.transactionSignature, + confirmationStatus: summary.confirmationStatus, + canonicalInserted: summary.canonicalInserted, + coreExtracted: summary.coreExtracted, + decodeReplayed: summary.decodeReplayed, + materialized: summary.materialized, + annotationCount: summary.annotationCount, + idempotenceValidated: summary.idempotenceValidated, + }; + element("#executionSummaryOutput").value = JSON.stringify(concise, null, 2); + element("#executionPlanOutput").value = summary.planJson; + element("#executionSimulationOutput").value = summary.simulationJson; + element("#executionDiagnosticsOutput").value = summary.diagnosticsJson; +} + +function requiredText(selector: string, label: string): string { + const value = element(selector).value.trim(); + if (value.length === 0) { + throw new Error(`${label} est obligatoire.`); + } + return value; +} + +function optionalText(selector: string): string | null { + const value = element(selector).value.trim(); + return value.length === 0 ? null : value; +} + +function buildTokenRequest(submit: boolean): DemoExecutionSplTokenRequest { + const amountRaw = requiredText("#tokenAmountRawInput", "Le montant brut"); + if (!/^[0-9]+$/.test(amountRaw)) { + throw new Error("Le montant brut doit être un entier décimal non signé."); + } + const decimals = integerValue("#tokenDecimalsInput"); + if (decimals > 255) { + throw new Error("Les decimals doivent être compris entre 0 et 255."); + } + return { + profileName: element("#executionProfileSelect").value, + source: requiredText("#tokenSourceInput", "Le compte source"), + mint: requiredText("#tokenMintInput", "Le mint"), + destination: requiredText("#tokenDestinationInput", "Le compte destination"), + authority: requiredText("#tokenAuthorityInput", "L’autorité"), + amountRaw, + decimals, + submit, + operatorConfirmed: element("#executionOperatorConfirmedInput").checked, + forcePostValidationReplay: element("#executionForceReplayInput").checked, + }; +} + +function displayTokenSummary(summary: DemoExecutionSplTokenSummaryPayload): void { + const concise = { + operation: summary.operation, + profileName: summary.profileName, + cluster: summary.cluster, + genesisHash: summary.genesisHash, + walletPublicKey: summary.walletPublicKey, + balanceLamports: summary.balanceLamports, + feeLamports: summary.feeLamports, + amountRaw: summary.amountRaw, + decimals: summary.decimals, + readinessStatus: summary.readinessStatus, + simulationSuccess: summary.simulationSuccess, + simulationError: summary.simulationError, + transactionSignature: summary.transactionSignature, + confirmationStatus: summary.confirmationStatus, + canonicalInserted: summary.canonicalInserted, + coreExtracted: summary.coreExtracted, + decodeReplayed: summary.decodeReplayed, + materializationCount: summary.materializationCount, + idempotenceValidated: summary.idempotenceValidated, + }; + element("#executionSummaryOutput").value = JSON.stringify(concise, null, 2); + element("#executionPlanOutput").value = `PRÉFLIGHT\n${summary.readinessJson}\n\nPLAN\n${summary.planJson}`; + element("#executionSimulationOutput").value = summary.simulationJson; + element("#executionDiagnosticsOutput").value = summary.diagnosticsJson; +} + +function ataDerivationRequest(): DemoSplAtaDerivationRequest { + return { + profileName: element("#executionProfileSelect").value, + mint: requiredText("#ataMintInput", "Le mint ATA"), + tokenProgram: element("#ataTokenProgramSelect").value, + }; +} + +async function deriveAta(): Promise { + const payload = await invoke("demo_spl_ata_derive", { + request: ataDerivationRequest(), + }); + element("#ataPayerInput").value = payload.payer; + element("#ataWalletOwnerInput").value = payload.walletOwner; + element("#ataDerivedInput").value = payload.associatedTokenAccount; + return payload; +} + +function displayAtaSummary(summary: DemoExecutionSplAtaSummaryPayload): void { + element("#executionSummaryOutput").value = JSON.stringify({ + operation: summary.operation, + profileName: summary.profileName, + walletPublicKey: summary.walletPublicKey, + tokenProgramId: summary.tokenProgramId, + associatedTokenAccount: summary.associatedTokenAccount, + balanceLamports: summary.balanceLamports, + feeLamports: summary.feeLamports, + readinessStatus: summary.readinessStatus, + simulationSuccess: summary.simulationSuccess, + simulationError: summary.simulationError, + transactionSignature: summary.transactionSignature, + confirmationStatus: summary.confirmationStatus, + materializationCount: summary.materializationCount, + idempotenceValidated: summary.idempotenceValidated, + }, null, 2); + element("#executionPlanOutput").value = `PRÉFLIGHT\n${summary.readinessJson}\n\nPLAN\n${summary.planJson}`; + element("#executionSimulationOutput").value = summary.simulationJson; + element("#executionDiagnosticsOutput").value = summary.diagnosticsJson; +} + +async function executeAta(submit: boolean): Promise { + if (running) { + return; + } + try { + if (submit && !element("#executionOperatorConfirmedInput").checked) { + throw new Error("La confirmation opérateur est obligatoire avant signature ATA."); + } + const derivation = await deriveAta(); + const request: DemoExecutionSplAtaRequest = { + profileName: element("#executionProfileSelect").value, + walletOwner: derivation.walletOwner, + mint: requiredText("#ataMintInput", "Le mint ATA"), + tokenProgram: element("#ataTokenProgramSelect").value, + mode: element("#ataModeSelect").value, + submit, + operatorConfirmed: element("#executionOperatorConfirmedInput").checked, + forcePostValidationReplay: element("#executionForceReplayInput").checked, + }; + setRunning(true); + appendLog({ + timestamp: new Date().toISOString(), + level: "info", + stage: submit ? "ata_submit" : "ata_simulation", + message: `${request.mode} ${request.tokenProgram}, ATA=${derivation.associatedTokenAccount}`, + }); + const summary = await invoke("demo_execution_spl_ata_execute", { request }); + displayAtaSummary(summary); + appendLog({ + timestamp: new Date().toISOString(), + level: summary.simulationSuccess && (!submit || summary.idempotenceValidated) ? "info" : "error", + stage: "ata_completed", + message: `preflight=${summary.readinessStatus}, simulation=${summary.simulationSuccess}, confirmation=${summary.confirmationStatus ?? "none"}, materializations=${summary.materializationCount}, idempotence=${summary.idempotenceValidated}`, + signature: summary.transactionSignature, + }); + if (summary.transactionSignature) { + element("#ataJournalSignatureInput").value = summary.transactionSignature; + element("#ataJournalMintInput").value = request.mint; + element("#ataJournalAddressInput").value = summary.associatedTokenAccount; + await loadAtaJournal(); + } + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + appendLog({ timestamp: new Date().toISOString(), level: "error", stage: "ata_failed", message }); + frontendError("kb_app_demo.frontend.demo_execution_spl", `ATA execution failed: ${message}`); + } finally { + setRunning(false); + } +} + +function ataJournalRequest(): DemoSplAtaJournalRequest { + return { + profileName: element("#executionProfileSelect").value, + signatureContains: optionalText("#ataJournalSignatureInput"), + mint: optionalText("#ataJournalMintInput"), + associatedTokenAccount: optionalText("#ataJournalAddressInput"), + operation: optionalText("#ataJournalOperationInput"), + limit: integerValue("#ataJournalLimitInput"), + }; +} + +async function loadAtaJournal(): Promise { + const button = element("#refreshAtaJournalButton"); + button.disabled = true; + try { + const rows = await invoke("demo_spl_ata_journal", { request: ataJournalRequest() }); + element("#ataJournalOutput").value = JSON.stringify(rows.map(row => ({ + ...row, + payloadJson: JSON.parse(row.payloadJson) as unknown, + })), null, 2); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + element("#ataJournalOutput").value = JSON.stringify({ error: message }, null, 2); + } finally { + button.disabled = false; + } +} + +async function execute(submit: boolean): Promise { + if (running) { + return; + } + try { + const request = buildRequest(submit); + if (submit && !request.operatorConfirmed) { + const message = "La confirmation opérateur doit être cochée avant signature et envoi."; + element("#executionSummaryOutput").value = JSON.stringify({ warning: message }, null, 2); + appendLog({ + timestamp: new Date().toISOString(), + level: "warn", + stage: "policy", + message, + }); + frontendWarn("kb_app_demo.frontend.demo_execution_spl", `Execution blocked by policy: ${message}`); + return; + } + setRunning(true); + element("#executionSummaryOutput").value = "Exécution en cours..."; + appendLog({ + timestamp: new Date().toISOString(), + level: "info", + stage: submit ? "submit" : "simulation", + message: submit ? "Démarrage du parcours Devnet complet." : "Démarrage de la simulation exacte.", + }); + const summary = await invoke("demo_execution_solana_core_execute", { request }); + displaySummary(summary); + appendLog({ + timestamp: new Date().toISOString(), + level: summary.simulationSuccess ? "info" : "error", + stage: "completed", + message: `simulation=${summary.simulationSuccess}, confirmation=${summary.confirmationStatus ?? "none"}, canonical=${summary.canonicalInserted ?? 0}, core=${summary.coreExtracted ?? 0}, decode=${summary.decodeCompleted ?? 0}`, + signature: summary.transactionSignature, + }); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + element("#executionSummaryOutput").value = JSON.stringify({ error: message }, null, 2); + appendLog({ timestamp: new Date().toISOString(), level: "error", stage: "failed", message }); + frontendError("kb_app_demo.frontend.demo_execution_spl", `Execution failed: ${message}`); + } finally { + setRunning(false); + } +} + +async function executeMemo(submit: boolean): Promise { + if (running) { + return; + } + try { + const request = buildMemoRequest(submit); + if (submit && !request.operatorConfirmed) { + const message = "La confirmation opérateur doit être cochée avant signature et envoi du Memo."; + appendLog({ timestamp: new Date().toISOString(), level: "warn", stage: "memo_policy", message }); + frontendWarn("kb_app_demo.frontend.demo_execution_spl", `Memo execution blocked by policy: ${message}`); + return; + } + setRunning(true); + element("#executionSummaryOutput").value = "Exécution Memo en cours..."; + appendLog({ + timestamp: new Date().toISOString(), + level: "info", + stage: submit ? "memo_submit" : "memo_simulation", + message: submit ? "Démarrage du parcours Memo v4 Devnet complet." : "Démarrage de la simulation Memo v4 exacte.", + }); + const summary = await invoke("demo_execution_spl_memo_execute", { request }); + displayMemoSummary(summary); + appendLog({ + timestamp: new Date().toISOString(), + level: summary.simulationSuccess && (!submit || summary.materialized) ? "info" : "error", + stage: "memo_completed", + message: `simulation=${summary.simulationSuccess}, confirmation=${summary.confirmationStatus ?? "none"}, annotation=${summary.annotationCount}, idempotence=${summary.idempotenceValidated}`, + signature: summary.transactionSignature, + }); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + element("#executionSummaryOutput").value = JSON.stringify({ error: message }, null, 2); + appendLog({ timestamp: new Date().toISOString(), level: "error", stage: "memo_failed", message }); + frontendError("kb_app_demo.frontend.demo_execution_spl", `Memo execution failed: ${message}`); + } finally { + setRunning(false); + } +} + +async function executeToken(submit: boolean): Promise { + if (running) { + return; + } + try { + const request = buildTokenRequest(submit); + if (submit && !request.operatorConfirmed) { + const message = "La confirmation opérateur doit être cochée avant signature et envoi Token."; + appendLog({ timestamp: new Date().toISOString(), level: "warn", stage: "spl_token_policy", message }); + frontendWarn("kb_app_demo.frontend.demo_execution_spl", `Token execution blocked by policy: ${message}`); + return; + } + setRunning(true); + element("#executionSummaryOutput").value = "Préflight et exécution SPL Token en cours..."; + appendLog({ + timestamp: new Date().toISOString(), + level: "info", + stage: submit ? "spl_token_submit" : "spl_token_simulation", + message: submit ? "Démarrage du TransferChecked Devnet complet." : "Démarrage du préflight et de la simulation TransferChecked.", + }); + const summary = await invoke("demo_execution_spl_token_execute", { request }); + displayTokenSummary(summary); + appendLog({ + timestamp: new Date().toISOString(), + level: summary.simulationSuccess && (!submit || summary.idempotenceValidated) ? "info" : "error", + stage: "spl_token_completed", + message: `preflight=${summary.readinessStatus}, simulation=${summary.simulationSuccess}, confirmation=${summary.confirmationStatus ?? "none"}, materializations=${summary.materializationCount}, idempotence=${summary.idempotenceValidated}`, + signature: summary.transactionSignature, + }); + if (summary.transactionSignature) { + element("#tokenJournalSignatureInput").value = summary.transactionSignature; + await loadTokenJournal(); + } + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + element("#executionSummaryOutput").value = JSON.stringify({ error: message }, null, 2); + appendLog({ timestamp: new Date().toISOString(), level: "error", stage: "spl_token_failed", message }); + frontendError("kb_app_demo.frontend.demo_execution_spl", `Token execution failed: ${message}`); + } finally { + setRunning(false); + } +} + +function tokenJournalRequest(): DemoSplTokenJournalRequest { + return { + profileName: element("#executionProfileSelect").value, + signatureContains: optionalText("#tokenJournalSignatureInput"), + mint: optionalText("#tokenJournalMintInput"), + account: optionalText("#tokenJournalAccountInput"), + family: element("#tokenJournalFamilySelect").value || null, + operation: optionalText("#tokenJournalOperationInput"), + limit: integerValue("#tokenJournalLimitInput"), + }; +} + +function renderTokenJournal(rows: DemoSplTokenJournalRow[]): void { + const body = element("#tokenJournalRows"); + body.replaceChildren(); + if (rows.length === 0) { + const row = document.createElement("tr"); + const cell = document.createElement("td"); + cell.colSpan = 5; + cell.className = "text-body-secondary"; + cell.textContent = "Aucun événement correspondant."; + row.append(cell); + body.append(row); + element("#tokenJournalDetailOutput").value = "Aucun événement sélectionné."; + return; + } + for (const item of rows) { + const row = document.createElement("tr"); + row.tabIndex = 0; + row.setAttribute("role", "button"); + const values = [ + item.slot, + item.operation ?? "—", + item.family, + item.amountRaw ?? "—", + item.signature.length > 20 ? `${item.signature.slice(0, 10)}…${item.signature.slice(-8)}` : item.signature, + ]; + for (const value of values) { + const cell = document.createElement("td"); + cell.textContent = value; + row.append(cell); + } + row.title = item.signature; + const select = (): void => { + element("#tokenJournalDetailOutput").value = item.payloadJson; + }; + row.addEventListener("click", select); + row.addEventListener("keydown", event => { + if (event.key === "Enter" || event.key === " ") { + event.preventDefault(); + select(); + } + }); + body.append(row); + } + element("#tokenJournalDetailOutput").value = rows[0].payloadJson; +} + + + +function token2022ScenarioAmount(fixture: DemoSplToken2022FixturePayload, scenarioId: string): string { + if (scenarioId === "token_2022_mint_to_checked") { + return "1"; + } + if (scenarioId === "token_2022_approve_checked") { + return "2"; + } + if (scenarioId === "token_2022_burn_checked") { + return "1"; + } + return fixture.defaultAmountRaw; +} + +function applyToken2022Fixture(): void { + if (!token2022Fixture) { + return; + } + const scenarioId = element("#token2022ScenarioSelect").value; + element("#token2022SourceInput").value = scenarioId === "token_2022_close_destination" + ? token2022Fixture.closeAccount + : token2022Fixture.source; + element("#token2022MintInput").value = token2022Fixture.mint; + element("#token2022DestinationInput").value = scenarioId === "token_2022_close_destination" + ? token2022Fixture.authority + : token2022Fixture.destination; + element("#token2022DelegateInput").value = token2022Fixture.delegate; + element("#token2022AuthorityInput").value = token2022Fixture.authority; + element("#token2022FreezeAuthorityInput").value = token2022Fixture.freezeAuthority; + element("#token2022AmountInput").value = token2022ScenarioAmount(token2022Fixture, scenarioId); + element("#token2022DecimalsInput").value = String(token2022Fixture.decimals); +} + +async function loadToken2022Fixture(): Promise { + try { + token2022Fixture = await invoke("demo_spl_token_2022_fixture", { + profileName: element("#executionProfileSelect").value, + }); + applyToken2022Fixture(); + element("#token2022FixtureHelp").textContent = `Fixture: ${token2022Fixture.fixturePath} — program=${token2022Fixture.programId}`; + appendLog({ + timestamp: new Date().toISOString(), + level: "info", + stage: "token_2022_fixture", + message: `fixture chargée: mint=${token2022Fixture.mint}, source=${token2022Fixture.source}, destination=${token2022Fixture.destination}`, + }); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + token2022Fixture = null; + element("#token2022FixtureHelp").textContent = message; + appendLog({ timestamp: new Date().toISOString(), level: "error", stage: "token_2022_fixture", message }); + } +} + +function buildToken2022Request(submit: boolean): DemoExecutionSplToken2022Request { + const scenarioId = element("#token2022ScenarioSelect").value; + const source = element("#token2022SourceInput").value.trim(); + const mint = element("#token2022MintInput").value.trim(); + const destination = element("#token2022DestinationInput").value.trim(); + const delegate = element("#token2022DelegateInput").value.trim(); + const authority = element("#token2022AuthorityInput").value.trim(); + const freezeAuthority = element("#token2022FreezeAuthorityInput").value.trim(); + if (source.length === 0) { + throw new Error("Le compte source ou cible Token-2022 est obligatoire."); + } + if (scenarioId !== "token_2022_revoke" && scenarioId !== "token_2022_close_destination" && mint.length === 0) { + throw new Error("Le mint Token-2022 est obligatoire pour ce scénario."); + } + if (scenarioId === "token_2022_transfer_checked" && destination.length === 0) { + throw new Error("Le compte destination Token-2022 est obligatoire."); + } + if (scenarioId === "token_2022_close_destination" && destination.length === 0) { + throw new Error("La destination lamports est obligatoire pour CloseAccount."); + } + if (scenarioId === "token_2022_approve_checked" && delegate.length === 0) { + throw new Error("Le delegate est obligatoire pour ApproveChecked."); + } + if (["token_2022_freeze_account", "token_2022_thaw_account"].includes(scenarioId) && freezeAuthority.length === 0) { + throw new Error("La freeze authority est obligatoire pour FreezeAccount et ThawAccount."); + } + if (!["token_2022_freeze_account", "token_2022_thaw_account"].includes(scenarioId) && authority.length === 0) { + throw new Error("L’autorité Token-2022 est obligatoire."); + } + return { + profileName: element("#executionProfileSelect").value, + scenarioId, + source, + mint, + destination, + delegate, + authority, + freezeAuthority, + amountRaw: element("#token2022AmountInput").value.trim(), + decimals: integerValue("#token2022DecimalsInput"), + submit, + operatorConfirmed: element("#executionOperatorConfirmedInput").checked, + forcePostValidationReplay: element("#executionForceReplayInput").checked, + }; +} + +function displayToken2022Summary(summary: DemoExecutionSplToken2022SummaryPayload): void { + element("#executionSummaryOutput").value = JSON.stringify({ + scenarioId: summary.scenarioId, + operation: summary.operation, + profileName: summary.profileName, + cluster: summary.cluster, + genesisHash: summary.genesisHash, + walletPublicKey: summary.walletPublicKey, + balanceLamports: summary.balanceLamports, + feeLamports: summary.feeLamports, + simulationSuccess: summary.simulationSuccess, + simulationError: summary.simulationError, + transactionSignature: summary.transactionSignature, + confirmationStatus: summary.confirmationStatus, + canonicalInserted: summary.canonicalInserted, + coreExtracted: summary.coreExtracted, + decodeReplayed: summary.decodeReplayed, + materializationCount: summary.materializationCount, + idempotenceValidated: summary.idempotenceValidated, + }, null, 2); + element("#executionPlanOutput").value = `PRÉFLIGHT\n${summary.preflightJson}\n\nPLAN\n${summary.planJson}`; + element("#executionSimulationOutput").value = summary.simulationJson; + element("#executionDiagnosticsOutput").value = summary.diagnosticsJson; +} + +async function executeToken2022(submit: boolean): Promise { + if (running) { + return; + } + let request: DemoExecutionSplToken2022Request; + try { + request = buildToken2022Request(submit); + if (submit && !request.operatorConfirmed) { + const message = "La confirmation opérateur est obligatoire avant signature Token-2022."; + element("#executionSummaryOutput").value = JSON.stringify({ warning: message }, null, 2); + appendLog({ timestamp: new Date().toISOString(), level: "error", stage: "token_2022_failed", message }); + return; + } + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + appendLog({ timestamp: new Date().toISOString(), level: "error", stage: "token_2022_failed", message }); + return; + } + setRunning(true); + element("#executionSummaryOutput").value = "Préflight et exécution Token-2022 en cours..."; + appendLog({ + timestamp: new Date().toISOString(), + level: "info", + stage: submit ? "token_2022_submit" : "token_2022_simulation", + message: `${request.scenarioId}, source=${request.source}`, + }); + try { + const summary = await invoke("demo_execution_spl_token_2022_execute", { request }); + displayToken2022Summary(summary); + appendLog({ + timestamp: new Date().toISOString(), + level: summary.simulationSuccess ? "info" : "error", + stage: "token_2022_completed", + message: `scenario=${summary.scenarioId}, simulation=${summary.simulationSuccess}, confirmation=${summary.confirmationStatus ?? "none"}, materializations=${summary.materializationCount}, idempotence=${summary.idempotenceValidated}`, + signature: summary.transactionSignature, + }); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + element("#executionSummaryOutput").value = JSON.stringify({ error: message }, null, 2); + appendLog({ timestamp: new Date().toISOString(), level: "error", stage: "token_2022_failed", message }); + frontendError("kb_app_demo.frontend.demo_execution_spl", `Token-2022 execution failed: ${message}`); + } finally { + setRunning(false); + } +} + +async function loadTokenJournal(): Promise { + const button = element("#refreshTokenJournalButton"); + button.disabled = true; + try { + const rows = await invoke("demo_spl_token_journal", { request: tokenJournalRequest() }); + renderTokenJournal(rows); + appendLog({ + timestamp: new Date().toISOString(), + level: "info", + stage: "spl_token_journal", + message: `${rows.length} événement(s) matérialisé(s) chargé(s).`, + }); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + element("#tokenJournalDetailOutput").value = JSON.stringify({ error: message }, null, 2); + frontendError("kb_app_demo.frontend.demo_execution_spl", `Token journal loading failed: ${message}`); + } finally { + button.disabled = false; + } +} + +async function generateRecipient(): Promise { + try { + const payload = await invoke("demo_execution_solana_core_generate_recipient"); + element("#executionRecipientInput").value = payload.publicKey; + appendLog({ + timestamp: new Date().toISOString(), + level: "info", + stage: "recipient", + message: `Adresse destinataire jetable générée : ${payload.publicKey}`, + }); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + frontendError("kb_app_demo.frontend.demo_execution_spl", `Recipient generation failed: ${message}`); + } +} + +async function cancelExecution(): Promise { + try { + const accepted = await invoke("demo_execution_solana_core_cancel"); + appendLog({ + timestamp: new Date().toISOString(), + level: accepted ? "warn" : "info", + stage: "cancel", + message: accepted ? "Demande d’annulation enregistrée." : "Aucune exécution active.", + }); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + frontendError("kb_app_demo.frontend.demo_execution_spl", `Cancellation failed: ${message}`); + } +} + +async function loadOptions(): Promise { + const options = await invoke("demo_execution_solana_core_options"); + profileOptions = options.profiles; + const select = element("#executionProfileSelect"); + select.replaceChildren(); + for (const profile of profileOptions) { + const option = document.createElement("option"); + option.value = profile.name; + option.textContent = profile.name; + option.selected = profile.name === options.defaultProfileName; + select.append(option); + } + element("#executionLamportsInput").value = String(options.defaultTransferLamports); + updateProfileHelp(); + setRunning(options.running); + if (profileOptions.length === 0) { + element("#simulateExecutionButton").disabled = true; + element("#submitExecutionButton").disabled = true; + element("#simulateMemoButton").disabled = true; + element("#submitMemoButton").disabled = true; + element("#simulateTokenButton").disabled = true; + element("#submitTokenButton").disabled = true; + element("#deriveAtaButton").disabled = true; + element("#simulateAtaButton").disabled = true; + element("#submitAtaButton").disabled = true; + element("#simulateToken2022Button").disabled = true; + element("#submitToken2022Button").disabled = true; + element("#loadToken2022FixtureButton").disabled = true; + } +} + + +document.addEventListener("DOMContentLoaded", () => { + installFrontendConsoleBridge("kb_app_demo.frontend.demo_execution_spl"); + frontendDebug("kb_app_demo.frontend.demo_execution_spl", "execution demo window loaded"); + document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(item => new bootstrap.Tooltip(item)); + element("#executionProfileSelect").addEventListener("change", updateProfileHelp); + element("#generateRecipientButton").addEventListener("click", () => { + void generateRecipient(); + }); + element("#simulateExecutionButton").addEventListener("click", () => { + void execute(false); + }); + element("#submitExecutionButton").addEventListener("click", () => { + void execute(true); + }); + element("#simulateMemoButton").addEventListener("click", () => { + void executeMemo(false); + }); + element("#submitMemoButton").addEventListener("click", () => { + void executeMemo(true); + }); + element("#simulateTokenButton").addEventListener("click", () => { + void executeToken(false); + }); + element("#submitTokenButton").addEventListener("click", () => { + void executeToken(true); + }); + element("#loadToken2022FixtureButton").addEventListener("click", () => { + void loadToken2022Fixture(); + }); + element("#token2022ScenarioSelect").addEventListener("change", applyToken2022Fixture); + element("#simulateToken2022Button").addEventListener("click", () => { + void executeToken2022(false); + }); + element("#submitToken2022Button").addEventListener("click", () => { + void executeToken2022(true); + }); + element("#refreshTokenJournalButton").addEventListener("click", () => { + void loadTokenJournal(); + }); + element("#deriveAtaButton").addEventListener("click", () => { + void deriveAta(); + }); + element("#simulateAtaButton").addEventListener("click", () => { + void executeAta(false); + }); + element("#submitAtaButton").addEventListener("click", () => { + void executeAta(true); + }); + element("#refreshAtaJournalButton").addEventListener("click", () => { + void loadAtaJournal(); + }); + element("#cancelExecutionButton").addEventListener("click", () => { + void cancelExecution(); + }); + element("#clearExecutionLogButton").addEventListener("click", () => { + logLines.length = 0; + element("#executionLogOutput").value = ""; + }); + void listen("demo-execution-solana-core-progress", event => { + appendLog(event.payload); + }); + void loadOptions().catch(caughtError => { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + appendLog({ timestamp: new Date().toISOString(), level: "error", stage: "options", message }); + frontendError("kb_app_demo.frontend.demo_execution_spl", `Options loading failed: ${message}`); + }); +}); diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_http.ts b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_http.ts new file mode 100644 index 0000000..74d119c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_http.ts @@ -0,0 +1,181 @@ +// file: kb_app_demo/frontend/ts/demo_http.ts +// version: 3 + +import * as bootstrap from "bootstrap"; +import "simplebar"; +import ResizeObserver from "resize-observer-polyfill"; +import { invoke } from "@tauri-apps/api/core"; +import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log"; +import type { DemoHttpExecutionPayload } from "./bindings/kb_app_demo/demo_http/DemoHttpExecutionPayload"; +import type { DemoHttpMethodOption } from "./bindings/kb_app_demo/demo_http/DemoHttpMethodOption"; +import type { DemoHttpOptionsPayload } from "./bindings/kb_app_demo/demo_http/DemoHttpOptionsPayload"; +import type { DemoHttpRequest } from "./bindings/kb_app_demo/demo_http/DemoHttpRequest"; +import type { DemoHttpRoleOption } from "./bindings/kb_app_demo/demo_http/DemoHttpRoleOption"; + +(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap; +(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver; + +let roleOptions: DemoHttpRoleOption[] = []; +let methodOptions: DemoHttpMethodOption[] = []; + +function textInputValue(selector: string): string { + const element = document.querySelector(selector); + return element ? element.value.trim() : ""; +} + +function writeTextarea(selector: string, value: string): void { + const element = document.querySelector(selector); + if (element) { + element.value = value; + } +} + +function jsonText(value: unknown): string { + if (typeof value === "string") { + return value; + } + return JSON.stringify(value, null, 2); +} + +function roleSupportsMethod(role: DemoHttpRoleOption, method: DemoHttpMethodOption): boolean { + return role.requestKinds.includes("*") || role.requestKinds.includes(method.requestKind); +} + +function selectedRole(): DemoHttpRoleOption | null { + const role = textInputValue("#httpRoleSelect"); + return roleOptions.find(option => option.role === role) ?? null; +} + +function populateSelect(selector: string, values: Array<{ value: string; label: string }>): void { + const select = document.querySelector(selector); + if (!select) { + return; + } + select.textContent = ""; + for (const item of values) { + const option = document.createElement("option"); + option.value = item.value; + option.textContent = item.label; + select.appendChild(option); + } +} + +function refreshMethodList(): void { + const role = selectedRole(); + const filtered = role ? methodOptions.filter(method => roleSupportsMethod(role, method)) : methodOptions; + populateSelect("#httpMethodSelect", filtered.map(method => ({ + value: method.method, + label: `${method.method} — ${method.label}`, + }))); + refreshMethodFields(); +} + +function refreshMethodFields(): void { + const methodName = textInputValue("#httpMethodSelect"); + const method = methodOptions.find(option => option.method === methodName) ?? null; + const firstArg = document.querySelector("#httpFirstArgInput"); + const config = document.querySelector("#httpConfigInput"); + const params = document.querySelector("#httpParamsInput"); + if (firstArg) { + firstArg.disabled = method ? !method.requiresFirstArg : false; + firstArg.placeholder = method && method.requiresFirstArg ? "Argument requis" : "Non requis pour cette méthode"; + } + if (config) { + config.disabled = method ? !method.supportsConfigJson : false; + } + if (params) { + params.disabled = false; + } +} + +function formatHttpExecutionPayload(payload: DemoHttpExecutionPayload): string { + const header = { + endpointName: payload.endpointName, + provider: payload.provider, + endpointUrl: payload.endpointUrl, + role: payload.role, + method: payload.method, + requestKind: payload.requestKind, + methodClass: payload.methodClass, + }; + return `${JSON.stringify(header, null, 2)}\n\n--- response ---\n${payload.responseJson}`; +} + +async function copyTextarea(selector: string): Promise { + const element = document.querySelector(selector); + if (!element) { + return; + } + await navigator.clipboard.writeText(element.value); +} + +async function refreshHttpOptions(): Promise { + try { + const options = await invoke("demo_http_options"); + roleOptions = options.roles; + methodOptions = options.methods; + populateSelect("#httpRoleSelect", roleOptions.map(role => ({ + value: role.role, + label: role.role, + }))); + refreshMethodList(); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + writeTextarea("#httpResultOutput", `Erreur options HTTP : ${message}`); + frontendError("kb_app_demo.frontend.demo_http", `HTTP options loading failed: ${message}`); + } +} + +async function refreshHttpPool(): Promise { + try { + const snapshots = await invoke("demo_http_list_pool_clients"); + writeTextarea("#httpPoolOutput", jsonText(snapshots)); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + writeTextarea("#httpPoolOutput", `Erreur : ${message}`); + frontendError("kb_app_demo.frontend.demo_http", `HTTP pool refresh failed: ${message}`); + } +} + +async function executeHttpRequest(): Promise { + const request: DemoHttpRequest = { + role: textInputValue("#httpRoleSelect"), + method: textInputValue("#httpMethodSelect"), + firstArg: textInputValue("#httpFirstArgInput") || null, + configJson: textInputValue("#httpConfigInput") || null, + paramsJson: textInputValue("#httpParamsInput") || null, + }; + writeTextarea("#httpResultOutput", "Exécution en cours..."); + try { + const response = await invoke("demo_http_execute_request", { request }); + writeTextarea("#httpResultOutput", formatHttpExecutionPayload(response)); + frontendDebug("kb_app_demo.frontend.demo_http", `HTTP request completed: ${response.method}`); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + writeTextarea("#httpResultOutput", `Erreur : ${message}`); + frontendError("kb_app_demo.frontend.demo_http", `HTTP request failed: ${message}`); + } +} + +document.addEventListener("DOMContentLoaded", () => { + installFrontendConsoleBridge("kb_app_demo.frontend.demo_http"); + frontendDebug("kb_app_demo.frontend.demo_http", "HTTP demo window loaded"); + const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); + Array.from(tooltipTriggerList).map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)); + document.querySelector("#httpRoleSelect")?.addEventListener("change", refreshMethodList); + document.querySelector("#httpMethodSelect")?.addEventListener("change", refreshMethodFields); + document.querySelector("#executeHttpButton")?.addEventListener("click", () => { + void executeHttpRequest(); + }); + document.querySelector("#refreshHttpPoolButton")?.addEventListener("click", () => { + void refreshHttpPool(); + }); + document.querySelector("#copyHttpPoolButton")?.addEventListener("click", () => { + void copyTextarea("#httpPoolOutput"); + }); + document.querySelector("#copyHttpResultButton")?.addEventListener("click", () => { + void copyTextarea("#httpResultOutput"); + }); + void refreshHttpOptions(); + void refreshHttpPool(); +}); diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_diag.ts b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_diag.ts new file mode 100644 index 0000000..d368ac8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_diag.ts @@ -0,0 +1,60 @@ +// file: kb_app_demo/frontend/ts/demo_sql_diag.ts +// version: 2 + +import * as bootstrap from "bootstrap"; +import "simplebar"; +import ResizeObserver from "resize-observer-polyfill"; +import { invoke } from "@tauri-apps/api/core"; +import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log"; +import { jsonText, renderJsonViewer, renderTables, setText } from "./demo_sql_tables"; +import type { DemoSqlDiagPayload } from "./bindings/kb_app_demo/demo_sql_diag/DemoSqlDiagPayload"; + +(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap; +(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver; + +let latestPayload: DemoSqlDiagPayload | null = null; + +async function refreshDiagnostics(): Promise { + setText("#sqlDiagStatus", "Chargement..."); + try { + const payload = await invoke("load_demo_sql_diag"); + setText("#sqlDiagConfigPath", payload.configPath); + setText("#sqlDiagProfile", payload.activeProfileName); + setText("#sqlDiagBackend", payload.backend); + setText("#sqlDiagDsn", payload.maskedDsn); + setText("#sqlDiagSchema", payload.currentSchema ?? "—"); + setText("#sqlDiagHealth", payload.healthStatus); + setText("#sqlDiagMigration", payload.migrationStatus); + setText("#sqlDiagStatus", "OK"); + renderTables("#sqlDiagTableBody", payload.tables); + latestPayload = payload; + renderJsonViewer("#sqlDiagJson", payload); + frontendDebug("kb_app_demo.frontend.demo_sql_diag", "SQL diagnostics refreshed"); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + setText("#sqlDiagStatus", `Erreur : ${message}`); + latestPayload = null; + renderJsonViewer("#sqlDiagJson", { error: message }); + frontendError("kb_app_demo.frontend.demo_sql_diag", `SQL diagnostics loading failed: ${message}`); + } +} + +async function copyDiagnostics(): Promise { + if (latestPayload) { + await navigator.clipboard.writeText(jsonText(latestPayload)); + } +} + +document.addEventListener("DOMContentLoaded", () => { + installFrontendConsoleBridge("kb_app_demo.frontend.demo_sql_diag"); + frontendDebug("kb_app_demo.frontend.demo_sql_diag", "SQL diagnostics demo window loaded"); + const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); + Array.from(tooltipTriggerList).map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)); + document.querySelector("#refreshSqlDiagButton")?.addEventListener("click", () => { + void refreshDiagnostics(); + }); + document.querySelector("#copySqlDiagButton")?.addEventListener("click", () => { + void copyDiagnostics(); + }); + void refreshDiagnostics(); +}); diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_pg_core.ts b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_pg_core.ts new file mode 100644 index 0000000..9b41822 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_pg_core.ts @@ -0,0 +1,42 @@ +// file: kb_app_demo/frontend/ts/demo_sql_pg_core.ts +// version: 3 + +import * as bootstrap from "bootstrap"; +import "simplebar"; +import ResizeObserver from "resize-observer-polyfill"; +import { invoke } from "@tauri-apps/api/core"; +import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log"; +import { renderJsonViewer, renderTables, setText } from "./demo_sql_tables"; +import type { DemoSqlPgCorePayload } from "./bindings/kb_app_demo/demo_sql_pg_core/DemoSqlPgCorePayload"; + +(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap; +(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver; + +async function refreshCore(): Promise { + setText("#sqlCoreStatus", "Chargement..."); + try { + const payload = await invoke("load_demo_sql_pg_core"); + setText("#sqlCoreProfile", payload.activeProfileName); + setText("#sqlCoreDsn", payload.maskedDsn); + setText("#sqlCoreStatus", "OK"); + renderTables("#sqlCoreTableBody", payload.tables); + renderJsonViewer("#sqlCoreJson", payload); + frontendDebug("kb_app_demo.frontend.demo_sql_pg_core", "PostgreSQL core diagnostics refreshed"); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + setText("#sqlCoreStatus", `Erreur : ${message}`); + renderJsonViewer("#sqlCoreJson", { error: message }); + frontendError("kb_app_demo.frontend.demo_sql_pg_core", `PostgreSQL core diagnostics loading failed: ${message}`); + } +} + +document.addEventListener("DOMContentLoaded", () => { + installFrontendConsoleBridge("kb_app_demo.frontend.demo_sql_pg_core"); + frontendDebug("kb_app_demo.frontend.demo_sql_pg_core", "PostgreSQL core demo window loaded"); + const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); + Array.from(tooltipTriggerList).map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)); + document.querySelector("#refreshSqlCoreButton")?.addEventListener("click", () => { + void refreshCore(); + }); + void refreshCore(); +}); diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_pg_raw.ts b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_pg_raw.ts new file mode 100644 index 0000000..aed2d52 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_pg_raw.ts @@ -0,0 +1,42 @@ +// file: kb_app_demo/frontend/ts/demo_sql_pg_raw.ts +// version: 3 + +import * as bootstrap from "bootstrap"; +import "simplebar"; +import ResizeObserver from "resize-observer-polyfill"; +import { invoke } from "@tauri-apps/api/core"; +import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log"; +import { renderJsonViewer, renderTables, setText } from "./demo_sql_tables"; +import type { DemoSqlPgRawPayload } from "./bindings/kb_app_demo/demo_sql_pg_raw/DemoSqlPgRawPayload"; + +(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap; +(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver; + +async function refreshRaw(): Promise { + setText("#sqlRawStatus", "Chargement..."); + try { + const payload = await invoke("load_demo_sql_pg_raw"); + setText("#sqlRawProfile", payload.activeProfileName); + setText("#sqlRawDsn", payload.maskedDsn); + setText("#sqlRawStatus", "OK"); + renderTables("#sqlRawTableBody", payload.tables); + renderJsonViewer("#sqlRawJson", payload); + frontendDebug("kb_app_demo.frontend.demo_sql_pg_raw", "PostgreSQL raw diagnostics refreshed"); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + setText("#sqlRawStatus", `Erreur : ${message}`); + renderJsonViewer("#sqlRawJson", { error: message }); + frontendError("kb_app_demo.frontend.demo_sql_pg_raw", `PostgreSQL raw diagnostics loading failed: ${message}`); + } +} + +document.addEventListener("DOMContentLoaded", () => { + installFrontendConsoleBridge("kb_app_demo.frontend.demo_sql_pg_raw"); + frontendDebug("kb_app_demo.frontend.demo_sql_pg_raw", "PostgreSQL raw demo window loaded"); + const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); + Array.from(tooltipTriggerList).map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)); + document.querySelector("#refreshSqlRawButton")?.addEventListener("click", () => { + void refreshRaw(); + }); + void refreshRaw(); +}); diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_replay_candidates.ts b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_replay_candidates.ts new file mode 100644 index 0000000..4f751fe --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_replay_candidates.ts @@ -0,0 +1,800 @@ +// file: kb_app_demo/frontend/ts/demo_sql_replay_candidates.ts +// version: 7 + +import * as bootstrap from "bootstrap"; +import "simplebar"; +import ResizeObserver from "resize-observer-polyfill"; +import DataTable, { type Api } from "datatables.net-bs5"; +import "datatables.net-bs5/css/dataTables.bootstrap5.css"; +import "datatables.net-select-bs5"; +import "datatables.net-select-bs5/css/select.bootstrap5.css"; +import { invoke } from "@tauri-apps/api/core"; +import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log"; +import type { DemoSqlReplayEntityRequest } from "./bindings/kb_app_demo/demo_sql_replay_candidates/DemoSqlReplayEntityRequest"; +import type { DemoSqlReplayEntityRow } from "./bindings/kb_app_demo/demo_sql_replay_candidates/DemoSqlReplayEntityRow"; +import type { DemoSqlReplayKnownProgramOption } from "./bindings/kb_app_demo/demo_sql_replay_candidates/DemoSqlReplayKnownProgramOption"; +import type { DemoSqlReplayOptionsPayload } from "./bindings/kb_app_demo/demo_sql_replay_candidates/DemoSqlReplayOptionsPayload"; +import type { DemoSqlReplayProgramRequest } from "./bindings/kb_app_demo/demo_sql_replay_candidates/DemoSqlReplayProgramRequest"; +import type { DemoSqlReplayProgramRow } from "./bindings/kb_app_demo/demo_sql_replay_candidates/DemoSqlReplayProgramRow"; +import type { DemoSqlReplayTransactionRequest } from "./bindings/kb_app_demo/demo_sql_replay_candidates/DemoSqlReplayTransactionRequest"; +import type { DemoSqlReplayTransactionRow } from "./bindings/kb_app_demo/demo_sql_replay_candidates/DemoSqlReplayTransactionRow"; + +(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap; +(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver; + +type EntityKindCode = "mint" | "owner" | "account_key"; + +type EntityViewConfig = { + kind: EntityKindCode; + label: string; + pluralLabel: string; + tableSelector: string; + containsSelector: string; + limitSelector: string; + summarySelector: string; + loadButtonSelector: string; + resetButtonSelector: string; + useButtonSelector: string; + copyButtonSelector: string; + exportButtonSelector: string; + fileName: string; +}; + +const tracingTarget = "kb_app_demo.frontend.demo_sql_replay_candidates"; +const entityViewConfigs: EntityViewConfig[] = [ + { + kind: "mint", + label: "mint", + pluralLabel: "mints", + tableSelector: "#mintCandidateTable", + containsSelector: "#mintContainsInput", + limitSelector: "#mintLimitInput", + summarySelector: "#mintResultSummary", + loadButtonSelector: "#loadMintsButton", + resetButtonSelector: "#resetMintsButton", + useButtonSelector: "#useSelectedMintsButton", + copyButtonSelector: "#copyMintsButton", + exportButtonSelector: "#exportMintsCsvButton", + fileName: "replay_mints.csv", + }, + { + kind: "owner", + label: "owner", + pluralLabel: "owners", + tableSelector: "#ownerCandidateTable", + containsSelector: "#ownerContainsInput", + limitSelector: "#ownerLimitInput", + summarySelector: "#ownerResultSummary", + loadButtonSelector: "#loadOwnersButton", + resetButtonSelector: "#resetOwnersButton", + useButtonSelector: "#useSelectedOwnersButton", + copyButtonSelector: "#copyOwnersButton", + exportButtonSelector: "#exportOwnersCsvButton", + fileName: "replay_owners.csv", + }, + { + kind: "account_key", + label: "compte", + pluralLabel: "comptes", + tableSelector: "#accountCandidateTable", + containsSelector: "#accountContainsInput", + limitSelector: "#accountLimitInput", + summarySelector: "#accountResultSummary", + loadButtonSelector: "#loadAccountsButton", + resetButtonSelector: "#resetAccountsButton", + useButtonSelector: "#useSelectedAccountsButton", + copyButtonSelector: "#copyAccountsButton", + exportButtonSelector: "#exportAccountsCsvButton", + fileName: "replay_account_keys.csv", + }, +]; +let transactionTable: Api | null = null; +let programTable: Api | null = null; +let mintTable: Api | null = null; +let ownerTable: Api | null = null; +let accountTable: Api | null = null; +let busyOperationCount = 0; +let maximumReplayCandidateLimit = 5_000; + +function element(selector: string): T { + const value = document.querySelector(selector); + if (!value) { + throw new Error(`Missing UI element: ${selector}`); + } + return value; +} + +function optionalInputValue(selector: string): string | null { + const value = element(selector).value.trim(); + return value.length === 0 ? null : value; +} + +function requiredInputValue(selector: string): string { + const value = optionalInputValue(selector); + if (value === null) { + throw new Error(`Valeur obligatoire manquante pour ${selector}`); + } + return value; +} + +function positiveIntegerValue(selector: string): number { + const value = Number.parseInt(requiredInputValue(selector), 10); + if (!Number.isSafeInteger(value) || value <= 0) { + throw new Error(`Valeur entière positive invalide pour ${selector}`); + } + return value; +} + +function boundedPositiveIntegerValue(selector: string): number { + const value = positiveIntegerValue(selector); + if (value > maximumReplayCandidateLimit) { + throw new Error( + `La limite doit être comprise entre 1 et ${maximumReplayCandidateLimit} pour ${selector}`, + ); + } + return value; +} + +function optionalNonNegativeIntegerValue(selector: string): number | null { + const raw = optionalInputValue(selector); + if (raw === null) { + return null; + } + const value = Number.parseInt(raw, 10); + if (!Number.isSafeInteger(value) || value < 0) { + throw new Error(`Valeur entière positive ou nulle invalide pour ${selector}`); + } + return value; +} + +function escapeHtml(value: unknown): string { + const text = String(value ?? ""); + return text + .split("&").join("&") + .split("<").join("<") + .split(">").join(">") + .split('"').join(""") + .split("'").join("'"); +} + +function codeDisplay(value: unknown, type: string): string { + if (type !== "display") { + return String(value ?? ""); + } + return `${escapeHtml(value)}`; +} + +function compactIdentifier(value: string, leadingLength: number = 12, trailingLength: number = 10): string { + if (value.length <= leadingLength + trailingLength + 1) { + return value; + } + return `${value.slice(0, leadingLength)}…${value.slice(-trailingLength)}`; +} + +function copyableCodeDisplay(label: string): (value: unknown, type: string) => string { + return (value: unknown, type: string): string => { + const text = String(value ?? ""); + if (type !== "display") { + return text; + } + const escapedText = escapeHtml(text); + const compactText = escapeHtml(compactIdentifier(text)); + const escapedLabel = escapeHtml(label); + return `${compactText}`; + }; +} + +function badgeDisplay(value: unknown, type: string): string { + if (type !== "display") { + return String(value ?? ""); + } + const text = String(value ?? "—"); + const className = text === "succeeded" || text === "core_extracted" + ? "text-bg-success" + : text === "failed" + ? "text-bg-danger" + : text === "running" || text === "received" + ? "text-bg-warning" + : "text-bg-secondary"; + return `${escapeHtml(text)}`; +} + +function coreStatusText(row: DemoSqlReplayTransactionRow): string { + if (!row.hasCoreTransaction) { + return "absent"; + } + if (row.transactionFailed === true) { + return "échec on-chain"; + } + return "présent"; +} + +function coreDisplay(_value: unknown, type: string, row: DemoSqlReplayTransactionRow): string | number { + if (type === "display") { + if (!row.hasCoreTransaction) { + return 'absent'; + } + if (row.transactionFailed === true) { + return 'échec on-chain'; + } + return 'présent'; + } + if (type === "filter") { + return coreStatusText(row); + } + if (row.transactionFailed === true) { + return 0; + } + return row.hasCoreTransaction ? 1 : 2; +} + +function occurrenceRatioDisplay(_value: unknown, type: string, row: DemoSqlReplayEntityRow): string | number { + const ratio = row.transactionCount > 0 ? row.occurrenceCount / row.transactionCount : 0; + if (type === "display" || type === "filter") { + return ratio.toFixed(2); + } + return ratio; +} + +function slotSpanDisplay(_value: unknown, type: string, row: DemoSqlReplayEntityRow): string | number { + const span = Math.max(0, row.maxSlot - row.minSlot); + if (type === "display" || type === "filter") { + return String(span); + } + return span; +} + +function commonLanguage(emptyTable: string, infoLabel: string, zeroRecords: string): object { + return { + emptyTable, + info: `_START_ à _END_ sur _TOTAL_ ${infoLabel}`, + infoEmpty: `0 ${infoLabel}`, + infoFiltered: "(filtré depuis _MAX_)", + lengthMenu: "Afficher _MENU_", + search: "Filtre local :", + zeroRecords, + paginate: { first: "Premier", last: "Dernier", next: "Suivant", previous: "Précédent" }, + select: { rows: { _: "%d lignes sélectionnées", 0: "", 1: "1 ligne sélectionnée" } }, + }; +} + +function createTransactionTable(): Api { + return new DataTable("#transactionCandidateTable", { + data: [], + columns: [ + { + data: null, + className: "replay-select-column text-center", + width: "1%", + orderable: false, + searchable: false, + render: DataTable.render.select(), + }, + { data: "signature", render: copyableCodeDisplay("la signature") }, + { data: "slot" }, + { data: "rawProcessingState", render: badgeDisplay }, + { data: "hasCoreTransaction", render: coreDisplay }, + { data: "ledgerStatus", render: badgeDisplay }, + { data: "processorVersion", defaultContent: "—", render: codeDisplay }, + { data: "attemptCount" }, + { data: "outerInstructionCount" }, + { data: "innerInstructionCount" }, + { data: "outerProgramCount" }, + { data: "innerProgramCount" }, + { data: "updatedAt" }, + ], + order: [[2, "desc"]], + pageLength: 10, + lengthMenu: [10, 25, 50, 100, 250], + select: { style: "multi", selector: "td:first-child", headerCheckbox: "select-all" }, + language: commonLanguage( + "Aucune transaction chargée", + "transactions", + "Aucune transaction ne correspond au filtre local", + ), + drawCallback: () => initializeTooltips(), + }); +} + +function createProgramTable(): Api { + return new DataTable("#programCandidateTable", { + data: [], + columns: [ + { + data: null, + className: "replay-select-column text-center", + width: "1%", + orderable: false, + searchable: false, + render: DataTable.render.select(), + }, + { data: "programCode", defaultContent: "—", render: codeDisplay }, + { data: "programId", render: copyableCodeDisplay("le program ID") }, + { data: "transactionCount" }, + { data: "outerInstructionCount" }, + { data: "innerInstructionCount" }, + { data: "logCount" }, + { data: "minSlot" }, + { data: "maxSlot" }, + ], + order: [[3, "desc"]], + pageLength: 10, + lengthMenu: [10, 25, 50, 100, 250], + select: { style: "multi", selector: "td:first-child", headerCheckbox: "select-all" }, + language: commonLanguage( + "Aucun programme chargé", + "programmes", + "Aucun programme ne correspond au filtre local", + ), + drawCallback: () => initializeTooltips(), + }); +} + +function createEntityTable(config: EntityViewConfig): Api { + return new DataTable(config.tableSelector, { + data: [], + columns: [ + { + data: null, + className: "replay-select-column text-center", + width: "1%", + orderable: false, + searchable: false, + render: DataTable.render.select(), + }, + { data: "entityValue", render: copyableCodeDisplay(config.label) }, + { data: "transactionCount" }, + { data: "occurrenceCount" }, + { data: null, render: occurrenceRatioDisplay }, + { data: "minSlot" }, + { data: "maxSlot" }, + { data: null, render: slotSpanDisplay }, + ], + order: [[2, "desc"]], + pageLength: 10, + lengthMenu: [10, 25, 50, 100, 250], + select: { style: "multi", selector: "td:first-child", headerCheckbox: "select-all" }, + language: commonLanguage( + `Aucun ${config.label} chargé`, + config.pluralLabel, + `Aucun ${config.label} ne correspond au filtre local`, + ), + drawCallback: () => initializeTooltips(), + }); +} + +function entityTable(kind: EntityKindCode): Api | null { + if (kind === "mint") { + return mintTable; + } + if (kind === "owner") { + return ownerTable; + } + return accountTable; +} + +function replaceRows(table: Api, rows: T[]): void { + table.clear(); + table.rows.add(rows); + table.draw(); +} + +function resetTableState(table: Api | null): void { + if (table === null) { + return; + } + table.search(""); + table.columns().search(""); + table.rows().deselect(); + table.draw(); +} + +function selectedOrFilteredRows(table: Api): T[] { + const selected = table.rows({ selected: true, search: "applied" }).data().toArray(); + if (selected.length > 0) { + return selected; + } + return table.rows({ search: "applied" }).data().toArray(); +} + +function selectedOrSingleFilteredRow(table: Api, label: string): T { + const selected = table.rows({ selected: true }).data().toArray(); + if (selected.length === 1) { + return selected[0]; + } + if (selected.length > 1) { + throw new Error(`Plusieurs ${label} sont cochés. Conservez une seule sélection.`); + } + const filtered = table.rows({ search: "applied" }).data().toArray(); + if (filtered.length === 1) { + return filtered[0]; + } + throw new Error(`Cochez exactement un ${label}, ou réduisez le filtre local à une seule ligne.`); +} + +function setBusy(busy: boolean, message: string): void { + busyOperationCount = busy ? busyOperationCount + 1 : Math.max(0, busyOperationCount - 1); + const operationInProgress = busyOperationCount > 0; + const badge = element("#replayCandidateStatusBadge"); + badge.textContent = operationInProgress ? (busy ? message : "Chargement en cours") : "Prêt"; + badge.className = operationInProgress ? "badge text-bg-warning" : "badge text-bg-success"; + document.querySelectorAll("button").forEach(button => { + if (button.id !== "openCoreExtractionButton") { + button.disabled = operationInProgress; + } + }); +} + +function buildTransactionRequest(): DemoSqlReplayTransactionRequest { + const entityKind = optionalInputValue("#transactionEntityKindSelect"); + const entityValue = optionalInputValue("#transactionEntityValueInput"); + if ((entityKind === null) !== (entityValue === null)) { + throw new Error("Le type d’entité et sa valeur doivent être renseignés ensemble."); + } + return { + signatureContains: optionalInputValue("#transactionSignatureContainsInput"), + minSlot: optionalNonNegativeIntegerValue("#transactionMinSlotInput"), + maxSlot: optionalNonNegativeIntegerValue("#transactionMaxSlotInput"), + rawProcessingState: optionalInputValue("#transactionRawStateSelect"), + ledgerStatus: optionalInputValue("#transactionLedgerStatusSelect"), + programId: optionalInputValue("#transactionProgramIdInput"), + programScope: requiredInputValue("#transactionProgramScopeSelect"), + entityKind, + entityValue, + limit: boundedPositiveIntegerValue("#transactionLimitInput"), + newestFirst: element("#transactionNewestFirstInput").checked, + }; +} + +async function resetTransactionFilters(): Promise { + element("#transactionSignatureContainsInput").value = ""; + element("#transactionMinSlotInput").value = ""; + element("#transactionMaxSlotInput").value = ""; + element("#transactionRawStateSelect").value = ""; + element("#transactionLedgerStatusSelect").value = ""; + element("#transactionProgramIdInput").value = ""; + element("#transactionProgramScopeSelect").value = "any"; + element("#transactionEntityKindSelect").value = ""; + element("#transactionEntityValueInput").value = ""; + element("#transactionLimitInput").value = "500"; + element("#transactionNewestFirstInput").checked = true; + resetTableState(transactionTable); + await loadTransactions(); +} + +async function resetProgramFilters(): Promise { + element("#knownProgramSelect").value = ""; + element("#programContainsInput").value = ""; + element("#programLimitInput").value = "500"; + resetTableState(programTable); + await loadPrograms(); +} + +async function resetEntityFilters(config: EntityViewConfig): Promise { + element(config.containsSelector).value = ""; + element(config.limitSelector).value = "500"; + resetTableState(entityTable(config.kind)); + await loadEntity(config); +} + +async function loadTransactions(): Promise { + if (transactionTable === null) { + return; + } + try { + setBusy(true, "Chargement transactions"); + const request = buildTransactionRequest(); + const rows = await invoke("load_demo_sql_replay_transactions", { request }); + replaceRows(transactionTable, rows); + element("#transactionResultSummary").textContent = `${rows.length} transaction(s) chargée(s) depuis PostgreSQL.`; + frontendDebug(tracingTarget, `loaded ${rows.length} replay transaction candidates`); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + element("#transactionResultSummary").textContent = `Erreur : ${message}`; + frontendError(tracingTarget, `transaction candidate loading failed: ${message}`); + } finally { + setBusy(false, "Prêt"); + } +} + +async function loadPrograms(): Promise { + if (programTable === null) { + return; + } + try { + setBusy(true, "Chargement programmes"); + const request: DemoSqlReplayProgramRequest = { + programIdContains: optionalInputValue("#programContainsInput"), + limit: boundedPositiveIntegerValue("#programLimitInput"), + }; + const rows = await invoke("load_demo_sql_replay_programs", { request }); + replaceRows(programTable, rows); + element("#programResultSummary").textContent = `${rows.length} programme(s) chargé(s), avec occurrences outer, inner et logs liés.`; + frontendDebug(tracingTarget, `loaded ${rows.length} replay program summaries`); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + element("#programResultSummary").textContent = `Erreur : ${message}`; + frontendError(tracingTarget, `program summary loading failed: ${message}`); + } finally { + setBusy(false, "Prêt"); + } +} + +async function loadEntity(config: EntityViewConfig): Promise { + const table = entityTable(config.kind); + if (table === null) { + return; + } + try { + setBusy(true, `Chargement ${config.pluralLabel}`); + const request: DemoSqlReplayEntityRequest = { + entityKind: config.kind, + entityValueContains: optionalInputValue(config.containsSelector), + limit: boundedPositiveIntegerValue(config.limitSelector), + }; + const rows = await invoke("load_demo_sql_replay_entities", { request }); + replaceRows(table, rows); + element(config.summarySelector).textContent = `${rows.length} ${config.pluralLabel} chargé(s) depuis PostgreSQL.`; + frontendDebug(tracingTarget, `loaded ${rows.length} replay ${config.kind} summaries`); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + element(config.summarySelector).textContent = `Erreur : ${message}`; + frontendError(tracingTarget, `${config.kind} summary loading failed: ${message}`); + } finally { + setBusy(false, "Prêt"); + } +} + +async function copyLines(lines: string[], label: string): Promise { + if (lines.length === 0) { + throw new Error(`Aucune ${label} à copier.`); + } + await navigator.clipboard.writeText(lines.join("\n")); + element("#replayCandidateStatusBadge").textContent = `${lines.length} ${label} copiée(s)`; +} + +function csvCell(value: unknown): string { + const text = value === null || value === undefined ? "" : String(value); + return `"${text.split('"').join('""')}"`; +} + +function csvContent(headers: string[], rows: unknown[][]): string { + const lines = [headers, ...rows].map(row => row.map(csvCell).join(";")); + return `${lines.join("\r\n")}\r\n`; +} + +async function saveCsv(fileName: string, content: string, rowCount: number): Promise { + if (rowCount === 0) { + throw new Error("Aucune ligne à exporter."); + } + const path = await invoke("export_demo_sql_replay_csv", { fileName, content }); + const badge = element("#replayCandidateStatusBadge"); + badge.textContent = `${rowCount} ligne(s) exportée(s) : ${path}`; + badge.className = "badge text-bg-success"; + frontendDebug(tracingTarget, `CSV export written: ${path}`); +} + +async function exportTransactionsCsv(): Promise { + const rows = transactionRowsForExport(); + const content = csvContent( + ["signature", "slot", "raw_processing_state", "retention_state", "core_status", "ledger_status", "processor_version", "attempt_count", "outer_instruction_count", "inner_instruction_count", "outer_program_count", "inner_program_count", "updated_at"], + rows.map(row => [row.signature, row.slot, row.rawProcessingState, row.retentionState, coreStatusText(row), row.ledgerStatus, row.processorVersion, row.attemptCount, row.outerInstructionCount, row.innerInstructionCount, row.outerProgramCount, row.innerProgramCount, row.updatedAt]), + ); + await saveCsv("replay_transactions.csv", content, rows.length); +} + +async function exportProgramsCsv(): Promise { + const rows = programRowsForExport(); + const content = csvContent( + ["program_code", "program_id", "transaction_count", "outer_instruction_count", "inner_instruction_count", "log_count", "min_slot", "max_slot"], + rows.map(row => [row.programCode, row.programId, row.transactionCount, row.outerInstructionCount, row.innerInstructionCount, row.logCount, row.minSlot, row.maxSlot]), + ); + await saveCsv("replay_programs.csv", content, rows.length); +} + +async function exportEntityCsv(config: EntityViewConfig): Promise { + const rows = entityRowsForExport(config.kind); + const content = csvContent( + ["entity_kind", "entity_value", "transaction_count", "occurrence_count", "occurrences_per_transaction", "min_slot", "max_slot", "slot_span"], + rows.map(row => [row.entityKind, row.entityValue, row.transactionCount, row.occurrenceCount, row.transactionCount > 0 ? row.occurrenceCount / row.transactionCount : 0, row.minSlot, row.maxSlot, Math.max(0, row.maxSlot - row.minSlot)]), + ); + await saveCsv(config.fileName, content, rows.length); +} + +function transactionRowsForExport(): DemoSqlReplayTransactionRow[] { + if (transactionTable === null) { + return []; + } + return selectedOrFilteredRows(transactionTable); +} + +function programRowsForExport(): DemoSqlReplayProgramRow[] { + if (programTable === null) { + return []; + } + return selectedOrFilteredRows(programTable); +} + +function entityRowsForExport(kind: EntityKindCode): DemoSqlReplayEntityRow[] { + const table = entityTable(kind); + if (table === null) { + return []; + } + return selectedOrFilteredRows(table); +} + +function showTransactionsTab(): void { + bootstrap.Tab.getOrCreateInstance(element("#transactionsTabButton")).show(); +} + +async function useSelectedProgram(): Promise { + if (programTable === null) { + return; + } + const row = selectedOrSingleFilteredRow(programTable, "programme"); + element("#transactionProgramIdInput").value = row.programId; + element("#transactionProgramScopeSelect").value = "any"; + showTransactionsTab(); + await loadTransactions(); +} + +async function useSelectedEntity(config: EntityViewConfig): Promise { + const table = entityTable(config.kind); + if (table === null) { + return; + } + const row = selectedOrSingleFilteredRow(table, config.label); + element("#transactionEntityKindSelect").value = config.kind; + element("#transactionEntityValueInput").value = row.entityValue; + showTransactionsTab(); + await loadTransactions(); +} + +async function openCoreExtraction(): Promise { + try { + await invoke("open_demo_core_extraction_window"); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + frontendError(tracingTarget, `core extraction window opening failed: ${message}`); + } +} + +async function loadOptions(): Promise { + const options = await invoke("demo_sql_replay_options"); + if (!Number.isSafeInteger(options.maximumLimit) || options.maximumLimit <= 0) { + throw new Error("La limite maximale des candidats replay retournée par le backend est invalide."); + } + maximumReplayCandidateLimit = options.maximumLimit; + element("#replayCandidateProfileBadge").textContent = `Profil ${options.activeProfileName}`; + element("#replayCandidateDsn").textContent = options.maskedDsn; + element("#replayCandidateMaximumLimit").textContent = String(options.maximumLimit); + document.querySelectorAll("input[type=number][max]").forEach(input => { + input.max = String(options.maximumLimit); + }); + populateKnownPrograms(options.knownPrograms); +} + +function populateKnownPrograms(programs: DemoSqlReplayKnownProgramOption[]): void { + const select = element("#knownProgramSelect"); + const datalist = element("#knownProgramDatalist"); + select.replaceChildren(new Option("Tous les programmes connus", "")); + datalist.replaceChildren(); + for (const program of programs) { + const label = `${program.code} — ${program.programId}`; + select.add(new Option(label, program.programId)); + const option = document.createElement("option"); + option.value = program.programId; + option.label = program.code; + datalist.append(option); + } +} + +function initializeTooltips(): void { + document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(target => { + bootstrap.Tooltip.getOrCreateInstance(target); + }); +} + +function adjustVisibleTables(): void { + transactionTable?.columns.adjust(); + programTable?.columns.adjust(); + mintTable?.columns.adjust(); + ownerTable?.columns.adjust(); + accountTable?.columns.adjust(); +} + +function installInlineCopyHandler(): void { + document.addEventListener("click", event => { + const source = event.target; + if (!(source instanceof Element)) { + return; + } + const button = source.closest("button[data-copy-value]"); + if (button === null) { + return; + } + event.preventDefault(); + event.stopPropagation(); + const value = button.dataset.copyValue ?? ""; + const label = button.dataset.copyLabel ?? "valeur"; + void copyLines([value], label).catch(error => reportUiError(`copy ${label}`, error)); + }); +} + +function reportUiError(action: string, caughtError: unknown): void { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + element("#replayCandidateStatusBadge").textContent = `Erreur : ${message}`; + element("#replayCandidateStatusBadge").className = "badge text-bg-danger"; + frontendError(tracingTarget, `${action} failed: ${message}`); +} + +function installHandlers(): void { + element("#loadTransactionsButton").addEventListener("click", () => void loadTransactions()); + element("#resetTransactionsButton").addEventListener("click", () => { + void resetTransactionFilters().catch(error => reportUiError("reset transaction filters", error)); + }); + element("#loadProgramsButton").addEventListener("click", () => void loadPrograms()); + element("#resetProgramsButton").addEventListener("click", () => { + void resetProgramFilters().catch(error => reportUiError("reset program filters", error)); + }); + element("#openCoreExtractionButton").addEventListener("click", () => void openCoreExtraction()); + element("#copySignaturesButton").addEventListener("click", () => { + void copyLines(transactionRowsForExport().map(row => row.signature), "signature").catch(error => reportUiError("copy signatures", error)); + }); + element("#exportTransactionsCsvButton").addEventListener("click", () => { + void exportTransactionsCsv().catch(error => reportUiError("export transactions CSV", error)); + }); + element("#copyProgramsButton").addEventListener("click", () => { + void copyLines(programRowsForExport().map(row => row.programId), "program ID").catch(error => reportUiError("copy programs", error)); + }); + element("#exportProgramsCsvButton").addEventListener("click", () => { + void exportProgramsCsv().catch(error => reportUiError("export programs CSV", error)); + }); + element("#useSelectedProgramButton").addEventListener("click", () => { + void useSelectedProgram().catch(error => reportUiError("use selected program", error)); + }); + for (const config of entityViewConfigs) { + element(config.loadButtonSelector).addEventListener("click", () => void loadEntity(config)); + element(config.resetButtonSelector).addEventListener("click", () => { + void resetEntityFilters(config).catch(error => reportUiError(`reset ${config.pluralLabel} filters`, error)); + }); + element(config.copyButtonSelector).addEventListener("click", () => { + void copyLines(entityRowsForExport(config.kind).map(row => row.entityValue), config.label).catch(error => reportUiError(`copy ${config.pluralLabel}`, error)); + }); + element(config.exportButtonSelector).addEventListener("click", () => { + void exportEntityCsv(config).catch(error => reportUiError(`export ${config.pluralLabel} CSV`, error)); + }); + element(config.useButtonSelector).addEventListener("click", () => { + void useSelectedEntity(config).catch(error => reportUiError(`use selected ${config.label}`, error)); + }); + } + element("#knownProgramSelect").addEventListener("change", event => { + const target = event.currentTarget as HTMLSelectElement; + element("#programContainsInput").value = target.value; + }); + document.querySelectorAll('[data-bs-toggle="tab"]').forEach(button => { + button.addEventListener("shown.bs.tab", adjustVisibleTables); + }); +} + +document.addEventListener("DOMContentLoaded", () => { + installFrontendConsoleBridge(tracingTarget); + frontendDebug(tracingTarget, "SQL replay candidate browser loaded"); + transactionTable = createTransactionTable(); + programTable = createProgramTable(); + mintTable = createEntityTable(entityViewConfigs[0]); + ownerTable = createEntityTable(entityViewConfigs[1]); + accountTable = createEntityTable(entityViewConfigs[2]); + initializeTooltips(); + installHandlers(); + installInlineCopyHandler(); + void loadOptions() + .then(() => Promise.all([ + loadTransactions(), + loadPrograms(), + loadEntity(entityViewConfigs[0]), + loadEntity(entityViewConfigs[1]), + loadEntity(entityViewConfigs[2]), + ])) + .catch(caughtError => reportUiError("initial loading", caughtError)); +}); diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_tables.ts b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_tables.ts new file mode 100644 index 0000000..3736fc3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_sql_tables.ts @@ -0,0 +1,68 @@ +// file: kb_app_demo/frontend/ts/demo_sql_tables.ts +// version: 2 + +import "@andypf/json-viewer"; +import type { DemoSqlTableSnapshot } from "./bindings/kb_app_demo/demo_sql/DemoSqlTableSnapshot"; + +export function setText(selector: string, value: string): void { + const element = document.querySelector(selector); + if (element) { + element.textContent = value; + } +} + +export function renderTables(selector: string, tables: DemoSqlTableSnapshot[]): void { + const tbody = document.querySelector(selector); + if (!tbody) { + return; + } + tbody.textContent = ""; + for (const table of tables) { + const row = document.createElement("tr"); + row.appendChild(cell(table.tableName, true)); + row.appendChild(cell(table.domain, false)); + row.appendChild(cell(table.role, false)); + row.appendChild(cell(table.exists ? "oui" : "non", false)); + row.appendChild(cell(table.rowCount === null ? "—" : String(table.rowCount), false)); + row.appendChild(cell(table.minSlot === null ? "—" : String(table.minSlot), false)); + row.appendChild(cell(table.maxSlot === null ? "—" : String(table.maxSlot), false)); + row.appendChild(cell(table.latestCreatedAt ?? "—", false)); + tbody.appendChild(row); + } +} + +export function jsonText(value: unknown): string { + return JSON.stringify(value, null, 2); +} + +export function renderJsonViewer(selector: string, value: unknown): void { + const container = document.querySelector(selector); + if (!container) { + return; + } + const viewer = document.createElement("andypf-json-viewer"); + viewer.setAttribute("indent", "2"); + viewer.setAttribute("expanded", "1"); + viewer.setAttribute("theme", "default-light"); + viewer.setAttribute("show-data-types", "true"); + viewer.setAttribute("show-toolbar", "false"); + viewer.setAttribute("expand-icon-type", "arrow"); + viewer.setAttribute("show-copy", "true"); + viewer.setAttribute("show-size", "true"); + viewer.setAttribute("expand-empty", "false"); + viewer.setAttribute("data", jsonText(value)); + container.textContent = ""; + container.appendChild(viewer); +} + +function cell(value: string, code: boolean): HTMLTableCellElement { + const td = document.createElement("td"); + if (code) { + const codeElement = document.createElement("code"); + codeElement.textContent = value; + td.appendChild(codeElement); + } else { + td.textContent = value; + } + return td; +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_ws.ts b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_ws.ts new file mode 100644 index 0000000..ae9a10d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/demo_ws.ts @@ -0,0 +1,325 @@ +// file: kb_app_demo/frontend/ts/demo_ws.ts +// version: 6 + +import * as bootstrap from "bootstrap"; +import "simplebar"; +import ResizeObserver from "resize-observer-polyfill"; +import {invoke} from "@tauri-apps/api/core"; +import {listen} from "@tauri-apps/api/event"; +import {frontendDebug, frontendError, installFrontendConsoleBridge} from "./frontend_log"; +import type {DemoWsExecutionPayload} from "./bindings/kb_app_demo/demo_ws/DemoWsExecutionPayload"; +import type {DemoWsMessagePayload} from "./bindings/kb_app_demo/demo_ws/DemoWsMessagePayload"; +import type {DemoWsMethodOption} from "./bindings/kb_app_demo/demo_ws/DemoWsMethodOption"; +import type {DemoWsOptionsPayload} from "./bindings/kb_app_demo/demo_ws/DemoWsOptionsPayload"; +import type {DemoWsRequest} from "./bindings/kb_app_demo/demo_ws/DemoWsRequest"; +import type {DemoWsRoleOption} from "./bindings/kb_app_demo/demo_ws/DemoWsRoleOption"; +import type {DemoWsStatusPayload} from "./bindings/kb_app_demo/demo_ws/DemoWsStatusPayload"; +import type {DemoWsUnsubscribeRequest} from "./bindings/kb_app_demo/demo_ws/DemoWsUnsubscribeRequest"; + +(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap; +(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver; + +let roleOptions: DemoWsRoleOption[] = []; +let methodOptions: DemoWsMethodOption[] = []; + +const maxResultOutputCharacters = 200_000; +const maxIncomingMessageCharacters = 25_000; + +function textInputValue(selector: string): string { + const element = document.querySelector(selector); + return element ? element.value.trim() : ""; +} + +function writeTextarea(selector: string, value: string): void { + const element = document.querySelector(selector); + if (element) { + element.value = value; + } +} + +function truncateIncomingMessage(value: string): string { + if (value.length <= maxIncomingMessageCharacters) { + return value; + } + return `${value.slice(0, maxIncomingMessageCharacters)}\n... message tronqué côté UI ...`; +} + +function capTextareaValue(value: string): string { + if (value.length <= maxResultOutputCharacters) { + return value; + } + return `... historique tronqué côté UI ...\n${value.slice(value.length - maxResultOutputCharacters)}`; +} + +function appendTextarea(selector: string, value: string): void { + const element = document.querySelector(selector); + if (!element) { + return; + } + const previous = element.value.trim(); + const safeValue = truncateIncomingMessage(value); + const nextValue = previous.length > 0 ? `${previous}\n\n${safeValue}` : safeValue; + element.value = capTextareaValue(nextValue); + element.scrollTop = element.scrollHeight; +} + +function jsonText(value: unknown): string { + if (typeof value === "string") { + return value; + } + return JSON.stringify(value, null, 2); +} + +function roleSupportsMethod(role: DemoWsRoleOption, method: DemoWsMethodOption): boolean { + return role.requestKinds.includes("*") || role.requestKinds.includes(method.requestKind); +} + +function selectedRole(): DemoWsRoleOption | null { + const role = textInputValue("#wsRoleSelect"); + return roleOptions.find(option => option.role === role) ?? null; +} + +function populateSelect(selector: string, values: Array<{ value: string; label: string }>): void { + const select = document.querySelector(selector); + if (!select) { + return; + } + select.textContent = ""; + for (const item of values) { + const option = document.createElement("option"); + option.value = item.value; + option.textContent = item.label; + select.appendChild(option); + } +} + +function refreshMethodList(): void { + const role = selectedRole(); + const filtered = role ? methodOptions.filter(method => roleSupportsMethod(role, method)) : methodOptions; + populateSelect("#wsMethodSelect", filtered.map(method => ({ + value: method.method, + label: `${method.method} — ${method.label}`, + }))); + refreshMethodFields(); +} + +function refreshMethodFields(): void { + const methodName = textInputValue("#wsMethodSelect"); + const method = methodOptions.find(option => option.method === methodName) ?? null; + const target = document.querySelector("#wsTargetInput"); + const filter = document.querySelector("#wsFilterInput"); + const config = document.querySelector("#wsConfigInput"); + if (target) { + target.disabled = method ? !method.requiresTarget : false; + target.placeholder = method && method.requiresTarget ? "Target requis" : "Non requis pour cette méthode"; + } + if (filter) { + filter.disabled = method ? !method.requiresFilterJson : false; + } + if (config) { + config.disabled = method ? !method.supportsConfigJson : false; + } +} + +function refreshSubscriptionSelect(status: DemoWsStatusPayload): void { + const select = document.querySelector("#wsSubscriptionSelect"); + const unsubscribeButton = document.querySelector("#unsubscribeWsButton"); + if (!select) { + return; + } + const previous = select.value; + select.textContent = ""; + for (const subscription of status.subscriptions) { + const option = document.createElement("option"); + option.value = String(subscription.subscriptionId); + option.textContent = `${subscription.subscriptionId} · ${subscription.method}`; + select.appendChild(option); + } + if (previous.length > 0) { + select.value = previous; + } + if (unsubscribeButton) { + unsubscribeButton.disabled = !status.connected || status.subscriptions.length === 0; + } +} + +function setConnectedUi(status: DemoWsStatusPayload): void { + const badge = document.querySelector("#wsStatusBadge"); + const connectButton = document.querySelector("#connectWsButton"); + const disconnectButton = document.querySelector("#disconnectWsButton"); + if (badge) { + badge.textContent = status.connected ? `Connecté · ${status.subscriptionCount} subscription(s)` : "Déconnecté"; + badge.className = status.connected ? "badge text-bg-success" : "badge text-bg-secondary"; + } + if (connectButton) { + connectButton.disabled = false; + } + if (disconnectButton) { + disconnectButton.disabled = !status.connected; + } + refreshSubscriptionSelect(status); + writeTextarea("#wsStatusOutput", jsonText(status)); +} + +function formatWsExecutionPayload(payload: DemoWsExecutionPayload): string { + const header = { + endpointName: payload.endpointName, + provider: payload.provider, + endpointUrl: payload.endpointUrl, + role: payload.role, + method: payload.method, + requestKind: payload.requestKind, + responseKind: payload.responseKind, + subscriptionId: payload.subscriptionId, + }; + return `${JSON.stringify(header, null, 2)}\n\n--- subscription response ---\n${payload.responseJson}`; +} + +async function copyTextarea(selector: string): Promise { + const element = document.querySelector(selector); + if (!element) { + return; + } + await navigator.clipboard.writeText(element.value); +} + +async function refreshWsOptions(): Promise { + try { + const options = await invoke("demo_ws_options"); + roleOptions = options.roles; + methodOptions = options.methods; + populateSelect("#wsRoleSelect", roleOptions.map(role => ({ + value: role.role, + label: role.role, + }))); + refreshMethodList(); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + writeTextarea("#wsResultOutput", `Erreur options WebSocket : ${message}`); + frontendError("kb_app_demo.frontend.demo_ws", `WebSocket options loading failed: ${message}`); + } +} + +async function refreshWsPool(): Promise { + try { + const snapshots = await invoke("demo_ws_list_pool_clients"); + writeTextarea("#wsPoolOutput", jsonText(snapshots)); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + writeTextarea("#wsPoolOutput", `Erreur : ${message}`); + frontendError("kb_app_demo.frontend.demo_ws", `WebSocket pool refresh failed: ${message}`); + } +} + +async function refreshWsStatus(): Promise { + try { + const status = await invoke("demo_ws_status"); + setConnectedUi(status); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + writeTextarea("#wsStatusOutput", `Erreur statut WebSocket : ${message}`); + frontendError("kb_app_demo.frontend.demo_ws", `WebSocket status failed: ${message}`); + } +} + +async function connectWsRequest(): Promise { + const request: DemoWsRequest = { + role: textInputValue("#wsRoleSelect"), + method: textInputValue("#wsMethodSelect"), + target: textInputValue("#wsTargetInput") || null, + filterJson: textInputValue("#wsFilterInput") || null, + configJson: textInputValue("#wsConfigInput") || null, + }; + writeTextarea("#wsResultOutput", "Souscription en cours sur la connexion WebSocket..."); + try { + const response = await invoke("demo_ws_connect", { request }); + writeTextarea("#wsResultOutput", formatWsExecutionPayload(response)); + frontendDebug("kb_app_demo.frontend.demo_ws", `WebSocket subscribed: ${response.method}`); + await refreshWsStatus(); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + writeTextarea("#wsResultOutput", `Erreur : ${message}`); + frontendError("kb_app_demo.frontend.demo_ws", `WebSocket subscription failed: ${message}`); + await refreshWsStatus(); + } +} + +async function unsubscribeWsRequest(): Promise { + const subscriptionIdText = textInputValue("#wsSubscriptionSelect"); + if (subscriptionIdText.length === 0) { + appendTextarea("#wsResultOutput", "Aucune subscription sélectionnée."); + return; + } + const subscriptionId = Number(subscriptionIdText); + if (!Number.isSafeInteger(subscriptionId) || subscriptionId < 0) { + appendTextarea("#wsResultOutput", "Identifiant de subscription invalide."); + return; + } + const request: DemoWsUnsubscribeRequest = { + subscriptionId, + }; + try { + const status = await invoke("demo_ws_unsubscribe", { request }); + setConnectedUi(status); + appendTextarea("#wsResultOutput", `--- unsubscribe ---\nSubscription ${request.subscriptionId} désinscrite.`); + frontendDebug("kb_app_demo.frontend.demo_ws", `WebSocket unsubscribed: ${request.subscriptionId}`); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + appendTextarea("#wsResultOutput", `Erreur unsubscribe : ${message}`); + frontendError("kb_app_demo.frontend.demo_ws", `WebSocket unsubscribe failed: ${message}`); + await refreshWsStatus(); + } +} + +async function disconnectWsRequest(): Promise { + try { + const status = await invoke("demo_ws_disconnect"); + setConnectedUi(status); + appendTextarea("#wsResultOutput", "--- disconnect ---\nDéconnexion demandée."); + frontendDebug("kb_app_demo.frontend.demo_ws", "WebSocket disconnected"); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + appendTextarea("#wsResultOutput", `Erreur déconnexion : ${message}`); + frontendError("kb_app_demo.frontend.demo_ws", `WebSocket disconnection failed: ${message}`); + } +} + +async function installWsEventListeners(): Promise { + await listen("demo-ws-status", event => { + setConnectedUi(event.payload); + }); + await listen("demo-ws-message", event => { + appendTextarea("#wsResultOutput", `--- ${event.payload.kind} ---\n${event.payload.payloadJson}`); + }); +} + +document.addEventListener("DOMContentLoaded", () => { + installFrontendConsoleBridge("kb_app_demo.frontend.demo_ws"); + frontendDebug("kb_app_demo.frontend.demo_ws", "WebSocket demo window loaded"); + const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); + Array.from(tooltipTriggerList).map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)); + document.querySelector("#wsRoleSelect")?.addEventListener("change", refreshMethodList); + document.querySelector("#wsMethodSelect")?.addEventListener("change", refreshMethodFields); + document.querySelector("#connectWsButton")?.addEventListener("click", () => { + void connectWsRequest(); + }); + document.querySelector("#unsubscribeWsButton")?.addEventListener("click", () => { + void unsubscribeWsRequest(); + }); + document.querySelector("#disconnectWsButton")?.addEventListener("click", () => { + void disconnectWsRequest(); + }); + document.querySelector("#refreshWsPoolButton")?.addEventListener("click", () => { + void refreshWsPool(); + }); + document.querySelector("#copyWsPoolButton")?.addEventListener("click", () => { + void copyTextarea("#wsPoolOutput"); + }); + document.querySelector("#copyWsResultButton")?.addEventListener("click", () => { + void copyTextarea("#wsResultOutput"); + }); + void installWsEventListeners(); + void refreshWsOptions(); + void refreshWsPool(); + void refreshWsStatus(); +}); diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/frontend_log.ts b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/frontend_log.ts new file mode 100644 index 0000000..653c052 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/frontend_log.ts @@ -0,0 +1,97 @@ +// file: kb_app_demo/frontend/ts/frontend_log.ts +// version: 1 + +//! Frontend logging helpers that preserve explicit tracing targets. + +import { invoke } from "@tauri-apps/api/core"; +import type { FrontendLogPayload } from "./bindings/kb_app_demo/frontend_log/FrontendLogPayload"; + +type FrontendLogLevel = "trace" | "debug" | "info" | "warn" | "error"; + +type ConsoleMethod = (...items: unknown[]) => void; + +const originalConsole = { + trace: console.trace.bind(console), + debug: console.debug.bind(console), + log: console.log.bind(console), + info: console.info.bind(console), + warn: console.warn.bind(console), + error: console.error.bind(console), +}; + +function stringifyItem(item: unknown): string { + if (item instanceof Error) { + return item.stack ?? item.message; + } + if (typeof item === "string") { + return item; + } + try { + return JSON.stringify(item); + } catch (caughtError) { + return String(item); + } +} + +function formatMessage(items: unknown[]): string { + return items.map(item => stringifyItem(item)).join(" "); +} + +async function sendFrontendLog(level: FrontendLogLevel, target: string, message: string): Promise { + const payload: FrontendLogPayload = { + level, + target, + message, + }; + try { + await invoke("emit_frontend_log", { payload }); + } catch (caughtError) { + originalConsole.error("frontend tracing bridge failed", caughtError); + } +} + +export function frontendTrace(target: string, ...items: unknown[]): void { + const message = formatMessage(items); + originalConsole.trace(message); + void sendFrontendLog("trace", target, message); +} + +export function frontendDebug(target: string, ...items: unknown[]): void { + const message = formatMessage(items); + originalConsole.debug(message); + void sendFrontendLog("debug", target, message); +} + +export function frontendInfo(target: string, ...items: unknown[]): void { + const message = formatMessage(items); + originalConsole.info(message); + void sendFrontendLog("info", target, message); +} + +export function frontendWarn(target: string, ...items: unknown[]): void { + const message = formatMessage(items); + originalConsole.warn(message); + void sendFrontendLog("warn", target, message); +} + +export function frontendError(target: string, ...items: unknown[]): void { + const message = formatMessage(items); + originalConsole.error(message); + void sendFrontendLog("error", target, message); +} + +function buildConsoleBridge(level: FrontendLogLevel, target: string, original: ConsoleMethod): ConsoleMethod { + return (...items: unknown[]) => { + original(...items); + void sendFrontendLog(level, target, formatMessage(items)); + }; +} + +export function installFrontendConsoleBridge(target: string): void { + console.trace = buildConsoleBridge("trace", target, originalConsole.trace); + console.debug = buildConsoleBridge("debug", target, originalConsole.debug); + console.log = buildConsoleBridge("info", target, originalConsole.log); + console.info = buildConsoleBridge("info", target, originalConsole.info); + console.warn = buildConsoleBridge("warn", target, originalConsole.warn); + console.error = buildConsoleBridge("error", target, originalConsole.error); +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/main.ts b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/main.ts new file mode 100644 index 0000000..9ce0e2d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/main.ts @@ -0,0 +1,256 @@ +// file: kb_app_demo/frontend/ts/main.ts +// version: 14 + +import * as bootstrap from "bootstrap"; +import "simplebar"; +import ResizeObserver from "resize-observer-polyfill"; +import MarkdownIt from "markdown-it"; +import { invoke } from "@tauri-apps/api/core"; +import { frontendDebug, frontendError, installFrontendConsoleBridge } from "./frontend_log"; + +(window as Window & typeof globalThis & { bootstrap?: typeof bootstrap }).bootstrap = bootstrap; +(window as Window & typeof globalThis & { ResizeObserver?: typeof ResizeObserver }).ResizeObserver = ResizeObserver; + +const markdownRenderer = new MarkdownIt({ + html: false, + linkify: true, + typographer: true, +}); + +function renderMarkdown(markdownText: string): string { + return markdownRenderer.render(markdownText); +} + +async function loadReadme(readmeContent: HTMLElement): Promise { + try { + const readme = await invoke("load_project_readme"); + readmeContent.innerHTML = renderMarkdown(readme); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + readmeContent.textContent = `Erreur pendant le chargement du README : ${message}`; + frontendError("kb_app_demo.frontend.main", `README loading failed: ${message}`); + } +} + +async function openDemoConfigWindow(): Promise { + try { + await invoke("open_demo_config_window"); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + frontendError("kb_app_demo.frontend.main", `Configuration demo window opening failed: ${message}`); + } +} + +async function openDemoHttpWindow(): Promise { + try { + await invoke("open_demo_http_window"); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + frontendError("kb_app_demo.frontend.main", `HTTP demo window opening failed: ${message}`); + } +} + + +async function openDemoSqlDiagWindow(): Promise { + try { + await invoke("open_demo_sql_diag_window"); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + frontendError("kb_app_demo.frontend.main", `SQL diagnostics demo window opening failed: ${message}`); + } +} + +async function openDemoSqlPgRawWindow(): Promise { + try { + await invoke("open_demo_sql_pg_raw_window"); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + frontendError("kb_app_demo.frontend.main", `PostgreSQL raw demo window opening failed: ${message}`); + } +} + +async function openDemoSqlPgCoreWindow(): Promise { + try { + await invoke("open_demo_sql_pg_core_window"); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + frontendError("kb_app_demo.frontend.main", `PostgreSQL core demo window opening failed: ${message}`); + } +} + +async function openDemoSqlReplayCandidatesWindow(): Promise { + try { + await invoke("open_demo_sql_replay_candidates_window"); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + frontendError("kb_app_demo.frontend.main", `SQL replay candidates window opening failed: ${message}`); + } +} + +async function openDemoWsWindow(): Promise { + try { + await invoke("open_demo_ws_window"); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + frontendError("kb_app_demo.frontend.main", `WebSocket demo window opening failed: ${message}`); + } +} + +async function openDemoBackfillWindow(): Promise { + try { + await invoke("open_demo_backfill_window"); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + frontendError("kb_app_demo.frontend.main", `Backfill demo window opening failed: ${message}`); + } +} + +async function openDemoCoreExtractionWindow(): Promise { + try { + await invoke("open_demo_core_extraction_window"); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + frontendError("kb_app_demo.frontend.main", `Core extraction demo window opening failed: ${message}`); + } +} + +async function openDemoDecodeReplayWindow(): Promise { + try { + await invoke("open_demo_decode_replay_window"); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + frontendError("kb_app_demo.frontend.main", `Decode replay demo window opening failed: ${message}`); + } +} + +async function openDemoExecutionSolanaCoreWindow(): Promise { + try { + await invoke("open_demo_execution_solana_core_window"); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + frontendError("kb_app_demo.frontend.main", `Solana Devnet execution window opening failed: ${message}`); + } +} + +async function openDemoExecutionSplWindow(): Promise { + try { + await invoke("open_demo_execution_spl_window"); + } catch (caughtError) { + const message = caughtError instanceof Error ? caughtError.message : String(caughtError); + frontendError("kb_app_demo.frontend.main", `SPL Devnet execution window opening failed: ${message}`); + } +} + +document.addEventListener("DOMContentLoaded", async () => { + installFrontendConsoleBridge("kb_app_demo.frontend.main"); + + frontendDebug("kb_app_demo.frontend.main", "main window loaded"); + + const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); + Array.from(tooltipTriggerList).map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl)); + const toastElList = document.querySelectorAll('.toast'); + Array.from(toastElList).map(toastEl => new bootstrap.Toast(toastEl)); + const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]'); + Array.from(popoverTriggerList).map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl)); + + const readmeContent = document.querySelector("#readmeContent"); + if (readmeContent) { + await loadReadme(readmeContent); + } + + const openDemoConfigLink = document.querySelector("#openDemoConfigLink"); + if (openDemoConfigLink) { + openDemoConfigLink.addEventListener("click", event => { + event.preventDefault(); + void openDemoConfigWindow(); + }); + } + + const openDemoHttpLink = document.querySelector("#openDemoHttpLink"); + if (openDemoHttpLink) { + openDemoHttpLink.addEventListener("click", event => { + event.preventDefault(); + void openDemoHttpWindow(); + }); + } + + const openDemoWsLink = document.querySelector("#openDemoWsLink"); + if (openDemoWsLink) { + openDemoWsLink.addEventListener("click", event => { + event.preventDefault(); + void openDemoWsWindow(); + }); + } + + const openDemoBackfillLink = document.querySelector("#openDemoBackfillLink"); + if (openDemoBackfillLink) { + openDemoBackfillLink.addEventListener("click", event => { + event.preventDefault(); + void openDemoBackfillWindow(); + }); + } + + const openDemoCoreExtractionLink = document.querySelector("#openDemoCoreExtractionLink"); + if (openDemoCoreExtractionLink) { + openDemoCoreExtractionLink.addEventListener("click", event => { + event.preventDefault(); + void openDemoCoreExtractionWindow(); + }); + } + + const openDemoDecodeReplayLink = document.querySelector("#openDemoDecodeReplayLink"); + if (openDemoDecodeReplayLink) { + openDemoDecodeReplayLink.addEventListener("click", event => { + event.preventDefault(); + void openDemoDecodeReplayWindow(); + }); + } + + const openDemoExecutionSolanaCoreLink = document.querySelector("#openDemoExecutionSolanaCoreLink"); + if (openDemoExecutionSolanaCoreLink) { + openDemoExecutionSolanaCoreLink.addEventListener("click", event => { + event.preventDefault(); + void openDemoExecutionSolanaCoreWindow(); + }); + } + + const openDemoExecutionSplLink = document.querySelector("#openDemoExecutionSplLink"); + if (openDemoExecutionSplLink) { + openDemoExecutionSplLink.addEventListener("click", event => { + event.preventDefault(); + void openDemoExecutionSplWindow(); + }); + } + + const openDemoSqlDiagLink = document.querySelector("#openDemoSqlDiagLink"); + if (openDemoSqlDiagLink) { + openDemoSqlDiagLink.addEventListener("click", event => { + event.preventDefault(); + void openDemoSqlDiagWindow(); + }); + } + + const openDemoSqlPgRawLink = document.querySelector("#openDemoSqlPgRawLink"); + if (openDemoSqlPgRawLink) { + openDemoSqlPgRawLink.addEventListener("click", event => { + event.preventDefault(); + void openDemoSqlPgRawWindow(); + }); + } + + const openDemoSqlPgCoreLink = document.querySelector("#openDemoSqlPgCoreLink"); + if (openDemoSqlPgCoreLink) { + openDemoSqlPgCoreLink.addEventListener("click", event => { + event.preventDefault(); + void openDemoSqlPgCoreWindow(); + }); + } + + const openDemoSqlReplayCandidatesLink = document.querySelector("#openDemoSqlReplayCandidatesLink"); + if (openDemoSqlReplayCandidatesLink) { + openDemoSqlReplayCandidatesLink.addEventListener("click", event => { + event.preventDefault(); + void openDemoSqlReplayCandidatesWindow(); + }); + } +}); diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/splash.ts b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/splash.ts new file mode 100644 index 0000000..ae98ee9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/frontend/ts/splash.ts @@ -0,0 +1,130 @@ +// file: kb_app_demo/frontend/ts/splash.ts +// version: 4 + +//! Splash-screen event handling for the Tauri startup window. + +import { listen } from "@tauri-apps/api/event"; +import type { SplashOrder } from "./bindings/kb_app_demo/splash/SplashOrder"; +import { frontendError, frontendDebug, installFrontendConsoleBridge } from "./frontend_log"; + + +const splashTarget = "kb_app_demo.frontend.splash"; +const defaultFadeDurationMs = 3000; +let activeOpacityFrame: number | null = null; + +function normalizeDurationMs(value: number | null | undefined): number { + if (typeof value !== "number") { + return defaultFadeDurationMs; + } + if (!Number.isFinite(value) || value <= 0) { + return defaultFadeDurationMs; + } + return value; +} + +function easeInOut(progress: number): number { + if (progress < 0.5) { + return 2 * progress * progress; + } + return 1 - Math.pow(-2 * progress + 2, 2) / 2; +} + +async function animateOpacity(element: HTMLElement, fromOpacity: number, toOpacity: number, durationMs: number): Promise { + frontendDebug(splashTarget, `Animating from ${fromOpacity} to ${toOpacity} over ${durationMs}ms`); + if (activeOpacityFrame !== null) { + cancelAnimationFrame(activeOpacityFrame); + activeOpacityFrame = null; + } + element.style.opacity = fromOpacity.toString(); + element.style.willChange = "opacity"; + await new Promise(resolve => requestAnimationFrame(() => resolve())); + await new Promise(resolve => { + const startedAt = performance.now(); + const opacityDelta = toOpacity - fromOpacity; + const updateOpacity = (currentTime: number): void => { + const elapsedMs = currentTime - startedAt; + const rawProgress = Math.min(elapsedMs / durationMs, 1); + const easedProgress = easeInOut(rawProgress); + const nextOpacity = fromOpacity + opacityDelta * easedProgress; + element.style.opacity = nextOpacity.toString(); + if (rawProgress >= 1) { + element.style.opacity = toOpacity.toString(); + element.style.willChange = "auto"; + activeOpacityFrame = null; + resolve(); + return; + } + activeOpacityFrame = requestAnimationFrame(updateOpacity); + }; + activeOpacityFrame = requestAnimationFrame(updateOpacity); + }); +} + +function addLogMessage(message: string): void { + const debugInfo = document.getElementById("debug-info"); + if (!debugInfo) { + return; + } + const time = new Date().toLocaleTimeString(); + debugInfo.innerHTML += `${time}: ${message}
`; + frontendDebug(splashTarget, `addLogMessage: ${message}`); +} + +function addMessage(message: string, status: string): void { + const messagesContainer = document.getElementById("messages-container"); + if (!messagesContainer) { + return; + } + const messageElement = document.createElement("div"); + messageElement.className = `splash-message ${status}`; + messageElement.textContent = message; + messagesContainer.appendChild(messageElement); + messagesContainer.scrollTop = messagesContainer.scrollHeight; + frontendDebug(splashTarget, `addMessage: ${message} - ${status}`); +} + +async function handleSplashOrder(order: SplashOrder): Promise { + const durationMs = normalizeDurationMs(order.duration_ms); + const container = document.getElementById("splash-container"); + if (order.order === "fadein" && container) { + frontendDebug(splashTarget, `fadein`); + await animateOpacity(container, 0, 1, durationMs); + return; + } + if (order.order === "fadeout" && container) { + frontendDebug(splashTarget, `fadeout`); + await animateOpacity(container, 1, 0, durationMs); + return; + } + if (order.order === "add_msg" && order.msg && order.status) { + addMessage(order.msg, order.status); + return; + } + if (order.order === "add_log" && order.msg) { + addLogMessage(order.msg); + return; + } + frontendError(splashTarget, `unknown splash order: ${order.order}`); +} + +function initializeSplashDom(): void { + const container = document.getElementById("splash-container"); + if (container) { + container.style.opacity = "0"; + container.style.willChange = "opacity"; + } + installFrontendConsoleBridge(splashTarget); +} + +async function initializeSplashListeners(): Promise { + await listen("splash", event => { + void handleSplashOrder(event.payload); + }); +} + +document.addEventListener("DOMContentLoaded", () => { + initializeSplashDom(); + void initializeSplashListeners(); + frontendDebug(splashTarget, "splash window loaded"); +}); + diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/icons/favicon.ico b/migration/khadhroony-bot2-reference/kb_app_demo/icons/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9dfa172f89d8511207a622ccdfac2adc03cc460c GIT binary patch literal 458870 zcmeEP2YeJo7e7Shvmi(ku%N--EBbx*AS3}S*ih`)d%=d?L{UMJA|#i)gr=Z?y(e9u3Jq6FXcXPRpJvMNCkjr&Ow#(J6 zoB#UI?k?AgT$ig?uR!^}C_mWcI{$qC^{Sguexb{C%{77YMK0G9JzBX63j^i<)5>-5 z{8lddBYF<)vc0QM#dfYkagC;N$A2G@?SC(?I^o7ypWk-hyGPyLqOIcU%vNvz({t)_ z?~_Nh$PC<={#!o}%kwgX(?g$e*JNz>_mqbz&l{S@uZ8mO@q0W*`v)wV{D`IU4d<88 zbBFP=6r=ntt4BUV_Xmpi#WZh!Wzo~WtKG(Y)m6-q5KrhNK zWx1uTt<(@&-cyx*FlZp&uZ*TVm09%aBX6PnY<5mioQ73}woebfS&@6nmTQZM23UFc zKj3~ao_m7jHdpb@w~V?0_pb|II=WoOT&|Vhxm?QvVF}_TkXQwztp)T|6$LKW(j0$S zhIkq7tsL)iQCU)EbbT&#Z(9Eo9ToPJg6)vqTB@8sYxQ}mbnLn#>Sp|USp{SRM8f}} z-jeqgp2L=oEx0QDlTEpJp85dNoiACh7n(A3QdED{lSAp*wL00lw}kqs+<$c+3fx5f zV`8lTwOg}JGi(C&Pv3CwNTTB(!-qmLLg=qY+5JNv(JoPU+p%kp*x1-h!}1HgXY}sz z`l}xq)`+@^wqm{KQ2*UCf4qkBDLke3jNUz7e^ueoY3SdH{*4G3;*HI|JdDIiKFeX@BK%S4W<08jJN8S9jQd?a;y7 zQ2!c)olx)T2#db|`X&~@{a@U-UO8Il^>4U;#9OHA#PVORoQ^u!uhZ`O23$K)l*!ZbzJ2$+PF>|Cq`N~Js+8U_Ij%Yvg#rZTs%wSN24N<` zGK3`v)d)U>Itm3^om4L=aTkPc2=pGP{vtyFeFD8}U5{|p=uOtfjJySu*)>p>%12f> zPzlPNiuY&s-M2Tebwh?c%<{(Z>P=&m?J{QFVOm-I20sN`ikJp!|HJv`iN8~(?<<@z zYJc+b)5WqU@I0%rPWzvL4@<>Sc7Ge%r{9L7tq_3rkD0RU43Yn?R?3Ob= zS>j{5X_UR}?SFcS?MM8}Z_xf0(9l`8QQ;=aju*zi^_b__S;)4O3~j&u5;x~3?28Y| zwMVE~Jn=F9{2Z%#BN7(c{(?6L@-dDN_36%iY=^l%lzV#I+N0RE%ZqvYgT#8RrbFBR zZ0;q4H{J0fuk!)DdQj$ggu359xR*a?_zt93Uc49DKHf?hQlO`u<<%GC@9Dk)JQt7il`Q{V!%; zaSiUBURHTX*~Yv5JWl`h(k(BoK90?Q`vKN?Zt(qt+P>UMKM{_m(DvtkbPt;`=Q8%u zpBFI9fob|xck?J#-!~|0y0;1;_mJ(Iy|RXD#xO6meblS9X%Lg1Pe6$1ebTUTWazX{ zeP0s|{`V2OUWjmDxVoarYteqLHyiEjj?i)Sh-cg5{hjL}y|?Jjb41fFFQe1`zC4d& z=;7TP@7CsL2!kRqPe$6TedM=Ae?;&3)cyv9*p*Ho+^l_ibmGeYKC|T-Z7!E+cuXiB zbJ9xtcxI13-@Tva)T#Z|2>1n(^Jk@f`n`L7N%yk)i-LW?jdEJou|M?xdUb=&L;rc@ z(S`s9^D$4bo!)bi-m}6#)#>(ia(%F%&pVC1gZ(eQ)W!a1wS8&{&z|J6UK^Eglb$~g zp)10krUldk8UmOr(EjQR>I&*}>T>F{1EC#a3T-I(3hG-^s3@p!RnF>L6|#C)D}>ev zZ4lZa%lJcmM+)^){TIlnSF8hdYV^O4 z@1l6$W?0`*jc-L*J5Uz$Z#t*{+wZQ|>aKcf===Dcc!1?cJsmHSuBAEs)CTGnL+=(3 zM|gwNzpSiKtoPSA(LaOr91$VK%^y!N{WLy|cn(tJ0Q4V&z!HQ%$$T<9!}=VsNIRuL zd$$DBPw&rBzo&xsYl(h*rxW%6XVe+WYSh=LU+aH#?3cGWl68-+J=8tAxH^Mj9*x&U z@y9f7mGx(V{%VZlBYsbIS7o$9-SbP=@6Wbg6`a>3`h(wrW$<8uerofPOEYN1X#EfM zo`}%+-rr|4@Gs(jll5B6pVXxv>*Y4x9-Nb;wz2M^)be|Jpw97#*}SHCU8;w^M?^fY zAC3zn`hP0FmFDn8+^#>rz2UOWW=){ZLlCxL?VUI`OLhIldX0(scU^2umww>%@gEmX zUGu_Vk^Xu3-P~R*tCudS&D;)keuBX8{a!3j>4HOA%NbkowvFgtQ9S6BPv>4z8CV0s z>CpX#&g&{+t*I}&n`p;+QMU1(;9MP*--NI?e^yp(L_ciqFwB!Yv1Zt_oOX)8L};bg zc5#i?51)nlwh;Xb-h5CjN9ob5cl5u7dTr9ZaP%Xu{ipLTUMI+a?C+$|x}ImdQ$6P* zY(!vR{nVccb3;TseV33nPc62O_Vj+g8VP@T4(rAq!`!imcatp5 zUWIu{&a;R0l0H=(WE*XV#h)I83?9KcY)%iWeQ@|YDE9_}hWYq4$1u!iGJ!|_?1xwz z*6f>Guwh9FUHZ}QPyg-G5y-m<;W~teKbn8Rw!j)bw(U|kdwEq?R#tg1`|+!r8P+Cq zdd2ty$SLA)z>%_LL_gLEGWsU#soF<-FNO?QVC}v}BdwdNyMDAtFHh5Z!)TTt{hGW; zc+jOk@E%F)59@9it^I~>zI~MVeVNl58q>G9zO4VqNJxn3*>LoOmQebPu4(;RF@pI* zhnT2sSvW@eNmrgl$Y$M3T2>y1J7MT2x`rZn5yl~uZtY*>UOeeB{@sMj;*vO!Y1o7T z{lVWav0y322f>iYmpJVde;{*CDiU5JrN8|F+S-xJ|9t0uk$zfBaAYLy%6c|Z`l($v)}%~Xe)>Py zA13lJE)g<_hW_>kYBCrgtk)twYr+1;c~GJJJYe!|1@OaUBBNu*k2^lPx}$# zGNy=?{(uZl|LMzH=slhDKxxZsw_}k{fc|`ZBN;BVzZqjP%k%zNOVa9%B|XkTd0NlJ zdH96&^29<~L}k$~?Nj*$VF*HJ%-2R%KEH(4qTGu>d&MY_4(YC6il}0X4+C$P7J8If zEYb3@U>&@!kUhvPOQ<^v0s9c}FBqad=JY4TGa18{C>P^#Ib*HqxY+%9n4uHJvY4Q;(by*zI1Yo}eeuJFf$3bVEAC$SQWo+}T;E?0qejeGvMbs%mN zh_eH6jz6voT!+T>?fm&XZVZ&?G1$@S55`Xhu6YbZG+dg;OPH^K?rVR>F|E_;hERy0 z)@cC*^`{U}2q*+3Ab@f6H3(k_VQNuLrs?1P`hSt08t4GImXr2}c1wtV?5!5t z;c3f$*pBAdS7KhWtqeNQ=3WR} ziT}T+J}mN0Y5L7MSVlWm>WF}UqU}~Ib(%ecHg6@~u`ZJZ{P>9&_*hR*8?!oO zpRumTLVL8X_b1|i(#rp_hCa={+Y$~m=92z#1bV;Le)su0PSnTi|I?~}T`Rm< zc_tkH6)!!034AGS3|WUvw7!isvJu%R+Uba}lz1oEhpdI>{13d^b+r|;r9Bgle~e4v z+a_!D!q*<+^zoS1K3m@ZQS8g0wcT$M?-a^sU8T3ixC-+PiCA}OH2$Ntf8^U|U#xN3 zAozgd7uh(1X z0Qo)HD_lE)ao%HSL$i5LRc8iY4Iwo7i_GRsex<_)}-PN7gAMf6$_pV33saM;vr^E^dR`?&~>p(g%;;%Pa?1iCq z5;#$y>94ppgZlY>5wzbq#M(FyzF-;i`#k0{=kc2?wLm4E%8l(_MT~bh?;yhiDhZNQ|?Bt2%!LtVeOw+S6rDzIxvj( z#)t5~aN+9@?SgvFL!fc3X6uL<@WSsL_M5bv-=I2ZFVYdb{zS!~(-QwgAD-Ro)qhW^ z*>+iT9l+fAN|A5c>+$_JH?l4I`#Oks{MLC-ll49w3wDP5LuT+NKMnb`|7K7iQ2H;N zh0sg~Xb%gGX*^Z?vd?E{ z1A~5z))3b|Fv3XJIbMH)|HGWZJK=B0Kl(WGJx=c^%f=RHek*7XFa_!ZG+vwcgPcdb zn116XBJA!2nTs@p;~(_u@e=7h`j*xY^9B*UFNieSnYNLCnlpV4p#$p^|8cEV%xvI) z0eF54Vb4VE#j?sfJ!ir{jmv3I9PLY?_kJGg@%~${GdDuEF@oj9Z@p|Jyy`t0hJSkh zI~jXfb_P$Kkv<6Eu2Vh@kmEd=k_X|}R?5zv%8L1_$S&>{&>t0)`#qYBmT)hmTl{2WZ@Dk|5z)A`K_#JIERIJ76Xm> z&Xz@|UG>DuKhcFc^56aYtj3T%c!b3a=zt@~eH>3I{MU49jc<8BuNw8VD1S=Rc;Uai zEhok@_$R(l&lT{Kqc^WT1!I3FvaZo2kN=D|CBD5E!M9{~-zE^ZqZkNf2jZMSTo8y6Q9c!;m>#D%ClF_AaT_0x{rPR`cudWp zy}CdQe-zp^kKw06yKW@%2tOVZ`E*^#;{yM6IgfJ!*MYbq+kdUaa4Vq^(3tIyk!9tN z{6zFZAg`m!PXs_ve+mJGfI=Wm5Wssp&Asi8Kxg9yLs4yqxV~!!=%BNpgER!v30)Hq zpzBw};(EqLtkXX=o+=WcFU6WYT5_FZf`Kx_vBYEw1_R& z0XoV<=@?zJ%fmzih1EG>k0Q5|E)Abwr`^SB2M*W5hWkcr!+J1U%YF&V8)Hk`Vx=NJ zKV$zer(?_YMYN`z*54)42eF2a&bc8p|Me5T1=~LFBjPjf!+XSeH=drBKp)NF@FDv0 z+PLhU9!_$6K4rb17R^C=I?3@NAHZFugCbOj-%A>2Xy?5op?#5!y=15TV;w-yzeAGZ zL+6@j3nG`AgznJZz<&@z&s9r?{+ZJ_9N#vi;=(I~{?IwNn+c!!Z$H4>6XTB{AO3jU zV$ef&Z}$7e=_DWaw(RW#4RqcV#>*Mnz9Y25+vM>pNq`U0`hXpK;DF5?etbHxHN&6f zn27NUtY>6?wPh$7Xo^ICh~6cvhu89XjaFz3(C|Lt6crkCp1AKVyau^_6R%A53_J zh8gVKmtvx)-^e!9_tDvun(j{x#Z1Td z(Ps412SnQDQ}eVlUk^pw+HV$O_Ot%_W5VcOoO>K;6Vjfyg3ri}t~S_9@`r&Ay@+*F z5zzym9J`|39|;!<7{6Onu$m2+Gu z#@;~f{9K_tuZQAyWUek7_vA;6-sK1%`UY?*JLzpZ*^Rhv&>tS3T<>)F+;o-rGF?T!POedi zv5y2gw8xy4MT5B9{c*mx5w;W57s3W(Z}$0|%Nf4MHh;FM-=h2EyYWWWJu&Z~;XpIP z2Y#k}nDeB$tFZ{95sDCALipvw`RB97vzz)8MIr?eNrxugXg8A<}zoy_hsOJK-pmgOw2q73MOPw_AF>LBx$sF}Rr}y@y*_CX z%Z=Z;0g>`<<#`MEOlLhjF7hb@ELuYsc0kO-_Uw$(*6>$(_xpdHt2e($?;Mxo+l&=b z9QlkTd`y0W-PIZGFgLWuV1DrjU~2pNJiEY^fzPP*Xn60$0M})*TeB0YpcO`tO*R~xF_46EPRM>e77MTdV9`icZTW@UE{r?G;U|w z%aVgkd^C)(Z)3umV;i;-L+F4684b~8@L*JA%ep&L>KGGWi!p+A1yGuq-i(DD{@`2Mlu zFBoSh#XE;x_yqjsJ@%>ox)^$I?U^v8h@fG+@F5cLT{#oqcYIY(wKy}5Y-}3qS?2ip zGCT1ps@au(%SL{MZ>GQ_G|;@Ft$TtZP!q#P?Zk&Zp`-o-OV{te+2HJsSJ;UY3~6&h zhOPL3!RLIM|IW#@r&LFW=v&7APQby42D|YI=ulUjd(cpKgYKG*VNM<|+KvwdgLS-g z#^K%*Fm7OWy z-y`a!vsh>^AhpB8CrF=^X#UOGCq3c8?Ad7Zp(nvMcw0PQXQ&C&bb;^;(k@;$#~__8+!xbuTNy0dj*)XsjZS#W z6w~#fiQjKZw}xL#eqT(tVm|QWf4&tXhn!lvm5Y_b`z22o2GfNF(JmcbwPexG0pL)#&7eq z=72PCzgaqnA;~RJC+Tl_fxmt>NJt}}{-_@Q7e$)|p&DzPJ7XTyHT-~!qNq8xBF*s&(nPckCYrR6pZIm? zB7O^}KFnwx9S@D?c<9^2ES6Um^BG8yqRH{2y?o@ufYXHY);EJD`i8U&Yx@$uc1^@@ z@x;gY_X#nkwHtkpYTY_g2x59Zh#&knuYX#1y+jT0OZay<3-}S1-(Zh-T=)E>7=H9^ zF2{RtznF9?lENv5AL-z`tl!W`Ac*N%;CD8H=6gK_?3eJPedR~R1WE*jf!zfuh96;A z%DNXvfI{?-0y|m*kxKeU_|+inbK1_?(F0?#B(Qso<5%@$i}S$!v?R~(FVV}xAl7}H zMHHfYthie9t22S{gWnHU^YAdQMDUH3nrR}3b##$+?)u;F0 z*@RzN<-u&z9lCx3X`gXI&tf5NKx3r%5r%i#;WL?|3*TNHLDvSt@8wk|82R-2jh!<# z;p>f~#P2Woh{OSU^8b!HcRP~l`Gg$>tZ`##E7S()`aQdRr$@q7l=#s*-K8!Yrf0Cm zo$w=nJ{o>MeR+!>c64tr>zR1}MN#5M^uxbFiS(CZ?3v62P3t&*%gPFk@VofnwrwL( z6<51B-buIWg+cQeOs?7G8P-Hp0yoM|i{v7c6%wDP)4@dI`%it&BB zY5XFlKV&^$<5k5RgO;5T=5hQ!pAG*3`tAM>*~N7}!-zR6_?7>1rID}UE#U9id-puk zsCvuy1N#oH4n6<%H@cVn}!}pI}cf{6>_voI}fH~^YE+_3A{Z{zm zTJ3#f2*-}%e_nZ14?DW|E9+4v{dX%`9kqgAt* z;U5HTyhr{kLwyvYZjR+j{BE?f1BYnq1l?xuZ;gcC3!6I8*kbaJU)^lvKl)ylZ{6qQ z#UpT*f=~PQ-0Td^Z@;}>&+ik*?;$7nU>32qiXWB1vqz7qJan^x4laV8SUM-C?+e;F z6uY3lcMvqaqxg*_J=oU%x_;pazr_fS(!BsCmh_LFEv-6+Fnr~QnYubi?{rtvb7p^- zA83zy-W9Qf4-)1p(Dj8x*iqe=JJA;jc(9CL?wzd(zaufHRA=B{cNWVx{%!R7{blcm zI)Y~r5QcP?g@IoHZny6_)jvQa_>o?X@cSE?sQJ-v>eo4+v`1Tapa1CE-CLopix5^3 zW;~PuyMIdz$H`PL`3=P2h*sn}?+<)f);~@?${bEQXt0MHegKMt^Kx!>6!{I<6J^FD z)FTA3dwJD~Y_d&2PaJN119TX za|_-Y$bOi46Z`D9e(ddkda>7*c4JePpRT<#e1vlcHr+l-#3Iu4Vqn+9>Aw|vVzh#v zs8`yzuuU&|*hS7Aqv+wr??sHCV57}&jr{&UDBqX$aJPPLKia95 zoPPF63fDm@F?KmTsMWYtemm-!k7!|IWqN+TJ>(!&JG#z!C!qC_Rs0* zAf4soxbJAz5o`XWZ2S&9Q(HfJAlCnETw^>pf5&&nKHHp-B)yW2AHh&kt@&_$WyY_U z>76M`I*D_oN^M#H8*ee#fgkaJdQbFJ?X%V3n=XyXFJWgm``d;US=fc2+ozp%{HZp# zsy{dL9o~Z-vyRFNR(j9ag&(zrx_jc=c*Dj4x@(ST?l1O*mwHUa@t(c-(YY0wKmRiQ z4!z%Dy7wMCt0<}F>qA9jCw^Cdq3PfiFRebFZMj}|9XNd-KRT4AWV*H!Kf(dNuy@9I z`tLt+#xStb3u9RC#QTAWR%UU)kNUv@^sUL@9C-3ku}`%0C%T;7_z{0-qXTqs)#8_R z{Zo)mKFjjPCK`&U+K&f*qt|O^L_JIA`!t-V`%bq8-$N3<7d09HjeCd@j& z0r2q=4GRe^6EFPSHQK(wG1GqUi}lUAI`}tq(9-(fgaQ`Qc)al28?%0B>!=dn-dku* zht6-Ye~qrg*Qn6#KZEe}`a#cs5BfuOLz?~(hFC|q9oo9T;^oI_ z-Aia&bo~+Qm0WAExNd;<2_Zaf#PzHAZnhVHMple^+au80@K6kC{qJ$I+KFd9pxce$ zLm<9}BOJ(j*fXZUZ!P)odjf&l3k{=L_et?&T}oTfvt1D~5Zbc*7hSz~Ib2G;MwI}! z9RuxAn__@&Cf>J!5#8#(LO>y)5Kssx1QY_XA;6Cqp#w)^OVgJigaEx+%| zK^iycnP%y1;l5ECr?dpiifObNyf3D4VoUHo(*A>W&@PCpL(pgt)1b+J$c{fxO#2Vq zq5B1_0(EI+k@g?HL)SUY(uDyWT&`^9KYRzzbjfb?AHGBBcJ;yZvF+;cn7__r+xUX% zliHLMMp|1ZwJt<+fiyOyu|WDXNMA@3?ta=_E|3{agPdSGTS(^!>4K&-Rl}dBqCB0g zo6aex^K<;qeNFY`h-vLyoj_SJUEf4QPO~(z8+e`{7=-H_I$DX}r{kA&(}g)r z_r-L1cGG<^tudqFnZv}i#z5eHvoy!NB8MLm#8?Gmjr{&HA&n-rKTUv9r)gL5H3;Ph z>YN{dp#BsB3ITy)5Kssx1X2zGe18h8sma^}>Cn)=s5UBBaJJ(FTFF0YD82le zzT??WgNz1VYW}<&mir`PY ze2cXbj_}2mrudT$3wMvvC_5dP#3It>0dvEI)0@f0Ke z#P?6^OmEzLkGN~J^H^Wt_gq_8D#K&i`dwqx2p!qM3 z|Af`Yv2B+Z8|gpo!E>~aI0^U@?;o@L`1s!ey{8~-y@&7v%pvpyw`KKDQp z{^S$*SnO4d!15x0w8*FRJtZ7_>ig+z0JLos-auaD4aKoUB=|R+uj>br^ZqL9ZC@XB z+{N3uifXdpdn41w@yGsiW^ir|r|F(JLK#(4mhs1#3X3N_#v0Go^;barVt;GtK~WK5 zp-_O%0**b!A1;7jM&olZ$R8jyXn~@1!yR ziYENC7|!z4qk-RB%z8QJd#RC-E#r?qcIJ=QRFlsRe1qgXhQ?Ja*OPI6`=gASi|T&y zhCOM%q1l1m3wa+1!r|eTx$<4Hy$2`&8%iqLOz!3q^uIeBk!O^W~q;yCj?qEW-EhU1jia z{AKvftA&O$fn$=dFKwGL!S>@VlElzM~?=|HTa*={Ms{x35z}n7L|t?>{G6{d4=@vyMPJ zYenq6Rb5yZKh|9LZ)JU6j<)Hzmx~mCBCw>UBl+$5_#>R**VxN&Ec7Y*tBC!v!pA1w zvoiNX{R>6x>CC{n#^-r(?Ek_$$S!dQF@^@C!JnE#xttPT=dG(=e1`KL8j}s2#7?o^ z506{Dw+nO2T~PP@=GbSlPv>4@bPfY=m*&FHPnLe4i!56Fr`EXWxA8M={>#_~rm&vY z&Y5vnYTwjPK%Et#*uS|rk8Qcm@GNhR|GQ2-SD9n27e|Xfe3Fw5oaFZHQNMEZ(;S1) z80#cT4|sY%y`k5Y=iq%`oYC*n`% zVO`03yd;vAG->b}HU6X5Yh%Pyi>o^}tR89jJ5Yr_c~23L!Sma>%KYcIcSrkkIo@LY z?%!uK17Gzb_LTOrbB9L!J1^3c$aF0D6WyNmne;pGu69PuS%!0?53$?{8k+;zktA;# z-_RbFtq8%fJ*Cl~u`g#`#kO7*c5a8uU}*#Kkyv2{&Rj&$EhX0y|%0@ z9>j48jhEva;V#8B83*FoVuWghX1LS!*mZ|#bDCk^>%@BJVy>?vry*Hm@EtS$*xkZ~mVZ@S~fYw-Ne zZeQm1p307`%2sB(M&Rsq{0{Gc_okC^&eFDjOPH)JG$w^1?TDemCyAu zI>QOiX!=fPZZCZ8A-3%bOZ_~T!LJx+9*A|1M(iZrec)lO{{jAA!dJB)zb5_FyIxtO z|2{yz!fCDe3infy=sU+AYb-pOf1f-k=`apQux0%5{fqjE&*xmurY_52%#ypJ= zH{3UZRXsHn--MrIYljZjz71DBIaK={t?%oO1Y@IT>3qXJ%=L=vk-hSh5??c-BgWr& zt&I_e(cGuGyQv6!vGeR((_)5AJn|igKjx6VXXE=#YnD?K4+s+4hJ)}YKks*AT)C?g z_P54MC<(@&=InpgaL@DZ#?FlWn{m8wlhsZF@!t&GyJ6if`OSA7vqM6*GRcB5>Utu? ze?Bl!-?VlE?gv6| zyCR&1^qGNhI&eP$^I`{;&${|#tW()K?}K~8trekpoc}l&9@|Na5dUW1U&0ahQf&s! zOTRX0<^Rmq30_0}PrClug+Jkmy3fGe=C(zX9|`vk@B{EqyhDiuDS(~$4`1DmY~Y(W zEAq^K2YRqOzfBMViEY+S{6}x-L^f~^zE84cV+zCBz;t%fSBV88L9N?~Ke2@R+aS!* z)@m8=m7ueq|C=BP656cY_>cBw(VD7TuwKz@@5Tft`w6p4NOt4j{(x34cTE<~?ao|2 zW6ova`k*@4zC}4d#-I4cdQR$x9`vr?pV4O|ayU+vB#n6CKf>=b=|%cxw)yr^M)=eHf$8)+5HI|DJhBV* zLHi?YH1QX+1mD=&N>VS!R}(M%35cTgyV3jDtJ<7I|FG+s`@8ORJakeBj3@pjRoWW! z6X8Fp{;xM44A%$E#+*dle5yzRGRjKgi9eC(uGQ9yemrH_8R7PE(VX_VcKefv5;Cd9 z;*Eb`KW{I0b;tT8jgXTx(GFo>VX73Ey#%p+2a3E^(NJ<3$L_PMs(454A&wlF{&IaI9#Y@LBrtDv$ z@b^?{`-tx^!TLpCLD)HoVJug^PZg&KG*S2;lG_?{5*c(((b}0mUE^QZs6Q{{!@kBW zr{E=JeFij)8^87_w&nWK;bd?f%TK+vxQWJ}YyjWK4hJ1u=6!HaxV1vR!v<1r zorDwcch_qB-`}CVU?#p)o$t_!6e@y0C*bd?ZBKpBnMKtd8p+2Z^xP_p$J`K)-eR{D=4-(5o-nd&NF&Uher%{3N28a&kn%WBOzhgiH$>ep`%ac+zz%d1Hq4<-& z{fclc*3@Fnt^H>$U_Cm~KE?9=895DTlveNFksQ zPzWdl6aoqXg@8gpA)tojc_07M*a*KXr89Zn9qg#^{zk~jEU(wejm)#xpMr^Bkc>O z3s{AaF4q`BHtz9sE2IlGFqp3u>T~gQVRoZS0}}TO+tmlt(i~^p=giQ33iKD6uH*7T zK2J9y-58K*Bh|;#)P5jMb=mhv3t|dMQ!oqIO4Bg9P=5*mg@8gpA)pXY2q**;0tx|z zfI>hapb$_9CwKuLMxpKhk0|<>KJkyy!ZIX+#$f*U7R^(rg=WZxnpBnC! zz>AW9KvgWtpZbQMN7rQR9Jz)$uY{6+lT1S8@2SdWzfQZ0&HZRVvoP<&d$!d)Jj{dj z+yw~du%4w!J01v?Kb?C;=QE2u&wu-YRSg%E3<~f|Jl_J}W+^uw2$g?XTf5WRghC(tv+8dXEc{0A z2-$xn%Kz7CcZWMyhL3a9{BP5kk7FXnG$(uu{EIod4}ibMogn*VP4drTzrT-jUc>W7 z`E=H_n*R@v(`@tsyF~Vj9N(vlt2448`%PT_Zr>j4uh$>4BLB}J|D>DiGD8&&;h{{w2Q7Z0XnnPJg)gP4Jdf^Bh({mMnk5ZwBUr9NABf zUf+Sva4J9u&i#0+y0Ep+4+$rCp67;dyQFx>wIX-^j1BUy15VvocjwIqVGSd#b^RD2 zIQLUlc@W!l`-@!0;o`^HIm!2}i_6(4wn6@c;Z)Ycd2_<+*(s&HP<9^iGgD1T4YzHHU#r;TJT-k$@0966?q2^18zQU2%yJcS4epAQ;crS<){ zf!796{x7XQo^8D*>{+f{2gskcWBRgKWiFPFTK?vJCSkXP_4Fis(+JKf`VaX0R+K%Z z%V%B58qe`?85_m)ZQ~Z!GxdCylO9ARf2@_>aNh{l*xM3zcfb#Q!uo)sYVEwpT=2O? zlz)kDZ?<@1*z-!bKH$s%?~$U-q*6L6`S<3{R?itB33K#!|y#l)?3N!FW-+Mk3 zWl!met4?HfH-&qI>H=Xk%y;y+#4UYbv8LixTxhUn| zu;Jbj^c`ivgf+)%V?kgQ&WSG zcRr-GZ$6*=aS_A5UMuq70Qn#Jp6fl^=}lw%1n(rfC-UF?1>93Mi{v{iRY+nY^GTkrl=JJ1e)d^PmDBg#C!+LvC z_MKl;zF6fysy2(p+3=NxKH$q+tn>lPU{@VQ8f2s~&qwci;}Dw7@#C6#s`kMezXut^ zyan{nEYKRZzO1+X;}jWu$65#QiOK${eRp+MJJj?0__gQ*9vp6lFU_Y9VEJxobHZqw z*7X!3H2b!XYy1XuWaYnH$r>)SH0C4Pzr=h`TG&3x4j9EOe^2!u=mWBDqCP;IS2G_6 z{DJXfTWUYnKhRIIr~Q1*=J#=}{r*o~mcup{a$4XYxlm|{R%b%Dn zs>#?5bM_9#~KkZB)4bHZ1!Zqbjg9$&3-tkW$E`h*!B-r|)hMPp;a=D?7!k&Jzr#2` zt0UTZ1A+WfhP>y|cyC|Lmz#xe`AMkD@{cUk|hwbn^?|-2ACxjMrd!!2_d-7k{aGvGy9_he)EVm@8z9@mBlhU+I z`4d5IUq)Nh`RmxVN3iNgP5XdN!2U#bwmU2@#kCnUzMwTcH0Qh?p@q%UJ@Q>S=hKK~ zUxE3E16Ysr8S{x?12<{*PxTg6Y3psS#rwdv@4vaxN*^#5>qHIbgiB!apz&~o_Yrj7 z@BF$z`|WNir6MYTDJU&XLnWRuBdm;*mZ}q>c>p` zfGWt~pwRCuUig~EeBSQp@9EucErPDi(|wxTTUJ(RWy~qco__Nm#(Sut?j?i#Vxs(ef=y@yt8${XD9F&LUj!U`yypieyu#_%;O$U5hA26Hx0N>*;h}i3;S8VNHw3oXk zb9c~l9l|UG!@eGQ+8Q2OgNAZ?^@Y1f`t~05tu5<4DpI&o?s;Hoi~L9W`vBN**0yiI zyWXr1X!z%qM`yTwohm?&?mHdHoC4YWn-#fih5IQhYqvx7fMowA`ut8#+@G2V<+4lu z#EaWMM>A{8y2F_7iST^@eK(+a11{&5aTcR*jO43j%XP4MYx@sG`IGFwK-e>#=6B6> zDk}NsdmAy{nuxMz75g%ear-i}OE4!h+LyT-(z}hW&dkJqt1iet3gJQY0rlU0cU@R} zrMUun2G54*Ko%Q=vHdsSU&m@58D?djktlPXCfR?Ex&1w0`(}`&Ren_Rr}f|Ex6-(b z=3Z%?4f#{q7!v5-=0F}Bv+hvlD;&z@p&M5|^BjKH4-EG6WbZV7ta@^2Slxkab?Xb< zC%;5*;~l>v{P(AoY^^X0`9%Kv@0Wjwof^NNp$*Nh$%ezt$u^wl(Z1DwH2-{t^EcSW zyGNRh8^Xz*^5|Rsi`a*}tFn82ZHQL>n;o1|aO1%Io*_rE2|dRPslEOFlDbOh>+xe*fTJMst1Bf9uEI{-+n4 z{O_sSoXpsDM=+WPrhT?kmY>evTAa^j!`GsPb%LR?jO3br>*pcluw0Myb!OI)O^zp{ zl|S%pk_l{;_JY#fE3K_2-Kf9Vt(_xOe+gnrlU-vC4#iQ7-J9AH+ijVq{(d6Dp6r6~ z-xQKVI`&g3MgA@2U{zkSo+F!If^apyCG2SSJ;{Dllc$9gkUyP4F$N)vcqXE1l$6>%HZLw&Q8saHuL|3A^k?NljiVO8GZn zKAq-P+u)nZE(rS|eJldz+`PRJpO0`c!X*fO5za&Cg>Wjuku&C8ei^<8ZFzHX9@~0l zv6VT1nV2(5%k{bPB&}al4D!EAzee-BUcW!SZ-PG9%=>^Xkbl1VZf}llq)#M5{^K_9 zMt-@-H_x{B7oKay|2Fjz@1SoCX0qMEEMw7-nj{Hz;d z=?oUyD{x@wUIE#dq*HwfmA@~8_Vx_In!iTPH;=h5W_s@&j6Ohp*O#QEiIhLFfcE!8 zsHA-Y7WNF&IPhPDPLkM9`)U#`|Dx)w*6`y{f;D7~7X1j)-kp1s;jGzYP_9JFf9R@C z)CU}cu;Jy^UD>8PES<%LIpI<@CoDn15-opX0e&ROU*~JIw`U&K62cFI*_`r|^Uxcl+hP=L?+w3=h^54feK+Orqpp{OMKe13; z+llrf%_e`H%f=L1k^d%q7y4f;e&k|+E+vXON&arX@9vvHQ+)(}+Q;LpSv4mdr8YWA z{=`DDuR{mW^bh&(TQmGwD}8_u`+N?refZ%h5lHW1PLqF0mF7>HeEu|iIqNDb{@Q6y zcqrD0sX5^&RM~0rCl>H7&>3{CE~`A4`3l1OjezW_k64Ls!+S=-e%cptr2K2N??NN! zyO2d+vNR_=ko7D{JO7=rDzFpfPvH_rk|F#sMdTzRjI{bQR4t>8sK#Kmu}vR`hflj z^&c$g!y3qcjJdy&i5LfLZw-O;e#+_cC+Xna2Rc_|zW?lAOXqji;JeUatXpAv3)ceo zNg;n7IhR@S@lSKYBcKCnPS`{Tl0p8&0>%NIKSD^CQ=dAn&tcK6e zy-nCp`|My#j-2PWT)0C2W4S5X}ic!g_ktoNyQ|PZs&Vhhx~?H5mth-u2_) zQ_{jYqtr(%g5Qwc!?2(BS;-=Q5){sWY7Kfzao#xlWybARe1X^FyWmX-X)k9Z0Fp`m zqbfVlyWsH%8z-(hk!`+hl$CM7EXaRnBmUFAc^!Xs^H@@84%-uK>*n zUx4|5bQiTQ|H&r*l3Hz@a4zWHLT5W~?QdyL_%rB$niJO10N_aTt6wAhOaWu7&)~zE z_Sb~#Tgm@V)c#*|$cX3`&K#k=0+TQ&JZpJr;c^pNi4XEkbq4FDz6*r{4IJq=mGIQU za_^Jvffb#L7*75)uDU(6u6SJ+`Dh%Fu>;0|ZE*hAe=X2Uyi6uPvGJlYS=#|lyC7^p z(33x{v8cd1RU!7^J7T>ZXxRZYy=6eJ$U`mW1CLZ^zJ=FJ;HW2m`aSp+;WmT}mgk&x zcC-f+H02`DelStqq3K4*f0UXNHkJQQz>)lF{~QP(BEA@Nsk^WviuwSrUcm&crm*z?k+5|(McL&yOs-r<6AK;Mv4ndkl`a{!sYChP}!c@d51QY@a z0fm4K#z7^-$b8AXqf9(RKJjKcI~3)>ze8hT>Eha z9vUEZF5~MQl=lhwq1V|V`E*_3(%5EHKczyi%R}}fbToXVc*Nv{A zez;r!q`u{K0ZQSo+|>;L)cLs%Ui(}cD%$lm0Hh-1x|}N2{>ohgkYCvBItSzg)C=`L z>6x{5rd-KCOIC$ThzVJRfW>a2;$ea2>2Za2>2Ja2>49 zf8A8vf8A7E|2oKxhDT!%Y*Y(8SWp;7*G=SW&liSX2hsJ{SKi{fAjkl2_p%DA_jcT)_^uR<15|rT!EG3ITy)5Kssx1QY@a0fm4< zKp~(IPzWdl6aoqXg@8gpA)pXY2q**;0tx|zfI>hapb$_9Cy)5Kssx z1QY@a0fm4hapb$_9Cy)5Kssx1QY@a0fm4_F*Fv{b~r)`F{%<<5P*DF z#VBL0Bk$NkpsF-s2*}WjB45V1gfXBRHbp?$zbQm%`+yYtug=^udSm8ZX$zvm8CUl2 zZ~-NdJQ?;+brjWP4M`vys_8HYDEkisPMT-QvVYX$8@o2MZJNXBNcNTeJ5nr;AWEYB z(;U$~j$ld=(F_4)|IOe~7Xkv3?SE+@b8RnFuaXWxK-s?ogy2vTB-=lY0j_W;t7%J? zvj4Ody(!~n_RsRXEXL5tdX}p70j83$?*TLWue)h4UQW1OcA} z&Bo9Ey*}1wtXdzC0G&|wp8(L4q{%qiKV$h`wLTyLdZFw;0iY*IlX12GJnvH0tx&BG zh_h}e`;Rl=Nz`1N?H^-+QnfxH&ibM3KhA(BQFC#(|2*&PB!VX;NLBWq5@NK4i3Hd` zW4WcdwlJeA)kQ$rzb-85CJ;!7{pWe7De&WmfU^JiVV!I(CCL66>*+Zr*|17hdX@br ztK{sbJ7M;p=bdOjcd7+KK-s_210w_yX#d+-e(^p=bgBFD5K#6n56e`mDS`G+`+{Cb zH9Vcj4`jSPoSfDEKPO5pJ~V~X3(AXe?{E0>x!xL<>unbwPSdv)$gwt@oR$5jwQg|V z?z*w9_V4#CFfe`5j}Pat{W@j;@xeOTS_!8Ylotp4|Ce?3?vQM_B@4Z;F;{Cd8LQv_ zWRa$PI?Z$=4)#y`g8IwnOw~vT6xFoNFq5&e|Mb-rGu?=b{pWe-rmqC-<$RQ{;{j$e zR`zeN44hoMnQp|%{%K!OZztoFthBj(nO)6ftn5Eor5iilX1WnK`_J`$9y>RxhEN2G zYckI@ld-b@P}I`r+Dtd%X8-gpAiu0@`XC=4PK$k+H=D_LYyToS^SsLcZ+wI*u@y7j zh^zhQc_&L`O;rU1Am69VWW4QCx3d2<)fO||h_n6IvtA`#(o__-uw7i8^^%#48!jkO z_HPUN&ac!=H{xvnq=zp$AGPG65c2&#oQz9+o#og*y}#vp-%cK66s3efINhMU1la!u zcD8$WB^@~k6jf(dhLiDx)yK$`@yl|kR<*t z)z~e`6w0CYzl!x9)h5ZHbR4;EUq-H(eBb^1Y=MbA8!at zxXLs|ptvSupqYGU%(;x?Eg>$w+mtBvfTIvF(~kt&|DVjoS~&`=BqPRMojKV|zCX;o zNrK(;Gon40B^g9%Pp+AMB+&k`9>{ZU+6yQK0E(+K7Msa;-iP3HJUkjl?01 zRmk-+Gucvp{A1v_Rsl5qRa^ER;V-UHG?7!sMpx}POxvX#2_C*S+6MDkTtaYDdMM;u`PSXb&P zjuRXy(Tu0I!{KIfrTl60E|xd;&o6x`CBREFCd~B25%!M{Gm|UjFCAAP&;D~uFG(_( z6uCgaOivtR|CnE5ClB9A2`xMV#g!fQflM3Cf?#Q{>rxo_8ZFEZojXI4D}IAz-E}&a!{+LZxTn!sR)?qio@)mz6D(9R4fxsS4nlof6e5{^A}BiM56tdmL!^Y z)qZpcaD6a}9cTY}p6{aLH1$gr`!dEF$uac)rn_F0XaBjLt5YA{)Z{Q!FG8=KXaBU< ztmM?xlt%PiVZHxukllt*`RQH1S&=Kx_Gyi_TK6Bl;1XO?Pd~(aj7#_QZ)hlDu4^fnt= zQ&roHK&XC%Udyuo+6RZrU2m4>-OPHFb+Q?rq^RCqo$*Pi+>EYgeSVv~KA7J9^E?M7 z1xSiWT?CBugx{BG|8FemA$JUrTRKRWsWiG-RGslZE-NdsuVAQ5`>#kNs8fze<+>@-8VU7>i5+(~rz#$n)P{DACui|GW>cMKaZ64n=_LhEXir{$F2m zCTl!P;yggE=T3)0Ydcw(^FPr@ZhH6sne?d4zE_>uZo7o-Z$M8^#Cx*spX#o7NutlW zJnv$5@oTN^N6L9Ez`yVNqO8KD$F4h!)%TNh&ZqykzXrJP=sZ*uMUfy7PG=}jn*9$P z9^8}m+R|>4#{lWK_cYSq{vW@(MVc%%oyhf0iA2xTv(7{yUiQEFmQht=TT=bb=6Qc| zCc5@e#h9xVunc>yr-(JBeFZPbwf}riUwgPywZ;>HP(2B~mi7DJaIR+&e9~><^-o=f zd4O|d&I6QmizjZ0Yzw|UZ{Rpui9ej*N3QMXd3~(g`2Qpl1J$-11kChDmi@zj&wR91 zBDS}9qRjcI(hubz;_O=7zIM9?{?ccxm8^PVm2Od2^wKizpJ;+_ zx?-^%$={^LS>-z!pDXfZ9uMpqMEuOsD~q0%ZU2kZxs1uEJ^D18>5pvtFWHo_H`=V@ z?M_*K25aaeZ606?%Nx6wKBvijw~)EE2WCHU+^od!Ej&lI?V~==gUOAgqFzM6Opj#S zKeb&{omC{ZyXaM^^8iCc{!%6FuE}^t#LP;1Me$Rz?SBjFJuWL%FjaiS(f=>z|2W>G zH)iey{oTxUc=GboWsU)=Sf8~r^iDQ7-k)z?95&>V{!SNBt$Shts^Uaoj}7zPRA=NPGSugNSgHg{%MVS=Ex&9c%cH2LJgm-`!#P+3*0eX6lNeV%L zNUZVM4mi9gVqrD?`}_CWVf!@a-#=OKQ*=g-fSG>9!~RQZJ9Pvd!LutSt;9UQh0?|V z6C+0@F(uFs4mFd9Uj9daUSQ9-KhOJz`t47QF2t)CbyR2n#}t|o+C+r``z_&@M-p&uYEIYRBQd@%Gs&4ndw`6 z?Z0gX=&9v2OLh) z4j2PGE7COoElFd5JnwSW)w@F?u@>8gACv(ipOK_#ofP~jhT5#G>ngR!Pn1+@r_J;( z-u6GHdbbXssfN?_@~RVA{l${}Ry*KZ7JcJ`?GNMpMH-J;cc^?{p3TSqx2LwoEAB)D z%=9nb_D{57U%=BMUGv|5K-O>DT#@&9r-!cUvYZ`K7%Rfq1~@!7R>GH1VJB+%MRww6*=b&N`M` zc32{?QEes;0W&>Jg#8m5pzA4-Hfg$_>-{|5Xct#y9gBJXWg`gnUfLO!sEH@L2W^Akxtd%DfGkMmPf#a}|a z5bMoQ%|NE0%kgxNc$(O@f-StNTbAkZZFD~rT^Pr)?=V|n@Dps=~-XI z!S?gKZ_36%)t6idnCWJs?SE8dhuuKqdQRtrHOI1TmlezSUEILVcJD4rTE(>)hv7H> z8%}4mF@58k|F*O>PvLO-i}fD0n=JlSJ;{lHnVu%v{s~o_7eK$MjECPpkha(TkyxeX zWv(4S*W(DmGfg<1k;Y>x4`cs&B%jsS^qw#F1)ErpvZG_=U6oIM1k7~Q3HCpBZDw1n znXBZql8?)6S4bHHtio?#n`ot2;>+j`n*QW;#S)XxgQ_QoiZ*IrTKV&eR^%yH&P->W zVE+VqkuU2Jk=A)1-XqICZirN<#wJekGIakCaG(DVk-YKw8e~s9F zEa`D;k7P^7+2p%J@0}n1JYsp*3B^QRrv?IMI_xO>*Tw*uYdOtiLtC$wFa|L2b>km% zXnXWm@HS<{A1vs@HVzosvLDj3TRHa@u%2T&q=v>SE@UELrq52Yf8x8iCS#yT^KT!^ z8Ur+)ZRbCuT-$>f{O%~W8Eu-r?fv@6U99mOY3H3==|k4C?%o4r>Wr!@br3MqWvAJH zU=M9@96om4VQg#v*w4FaJj?T@yE?0Pu`lZ_U{Y^}OQiX4ROGUCLxxzfSDDXjWIa45 zr;fHNPGlosrpHdRf8rnS;`fVm({F05bT;<#vZ>2*f@=)K`eR9Zs`h2SeR8+VGfk}W zx&i)sb7bp_sxP$=Fw^Z=?J8J4*E?Spa^4kt?F_nCaoWeOJA!SwCiXD^ojbzolo5aO z`^Bv0q2X5TRpxWGEZ=j6EWA}csf&P_o(JtW)_Ew2_D|Bl8Cmy=wEsFS_AvnY8l`>4 zV!hF%$vxcBVeS<{Jt#v$M-H;AO9iV-94CNdlY+Rt}d?1 zJhP-K>p1+DAA-HU`_dU8G`~mt=xAL%jq}M52%Qm1&$z2QoP;$%y^!uxuf4NAurF-Dm4=7|lv6_m8L#pyz3yK9!Y)SH$&@?dO-ClG@s;xRZ^5Xg_-C zi1pCG#%doN9+6Gy(V}xNcy2J|X=8xvV;=+jF!Lr$v{HMtM}PCJw)x_=+Mld~504XN z>xQZ?^%2mqsb;$GaQkmYEAb-3SJsC6M=<(UZ-fnKz8GREQMzg*F*?vkp#*!15nWy`&V3~L3f{w~}6oaYm@ zhu;pXNS`{nog2l;X8**;FB1G^Rt|oSd8_tkpU%6O(RV=^eTpnBwR-ZsZgnR8QEHl3X~1}YfL;2a>P;^MxSbotvh1JU zsc8&t)Q;@?aq*qmWRo_J^Mn1vI+o`-E4?&Sacd_6W_Bmb{;3{XUsL(ab8OM%N7&Es zZ#fNPYg#MguHIXEzd+yQY&;_~UiL1uQ~Cw#Ub45Hx}fS#KLp}w|3-ZYjkBvCe}Szm zdYb(`(SQZ>Ri6`v(0#L>;(I_%a){_8yx>}KL6cc zqSo@p!wS;3ja0UOyZV**)a!XmrhoGE)o{gmI0Tga2j|xKZ*HRF^(^1pKb*`|o5I?O64mAWFw&LKcV)IxqZ(_)@N*6 z*>Y9&DFmz_pzJ?k_Fv9=dXBLoBlV0zz%B%o{m0$@|AB9=zIMq|)vFMQ1Oa9LakKx` znAg7xdvv-_=ezs=5^d(IBAg-!}VShPl0aS^mqr zL?ahfMj;Rn1eE=mUjv3R=9mUGIDytBP zHv-E3W3m6z5|&@qHQq8)?I{FgA)xF(7W?-;Crb{h9)&<65K#6Xll}XY|II{bt!h&i z0?PhlvVYnuc%Lj;sCpCv@kSty_P>c@sj zIV)74dl~EM-67s`Q|&1PWFQbX``0=Dll)h+-esL>4h{XnSvbGy3fR@{STFMszM&05 zJRE&SG47QiOhA~3HFU4wT89)Hi3GGw9ZPzb~ufkfJWym&3D$vj8olYE12k#J5J`W=5ilJEUBUc9Jw z6aq345bZ)Q9n^8I>(RzKzyC7%2v-+lu2x0WnZJwt(K)};c=KbDAC3lf<$k%y63_C?^-OY_d=)(k zfp7?j_Mn$eCHwcT-@P4ptP%NKR`#UKF~Bx_lTc@Ihf_F}=O_e3ThL3VlKm6#mdu60jmhArJ-u(H8X5sb>G9Dm!!rpIb#<*A5*luMf!e*065l+lPT% zWhn$4gMerUdg)ZNe*)fJo%yoJ>x^G7muLUlw?NN>juEgTL?I9c0nrZh(y3_wqc?Oq zO5~N!AFg?1n9M$4h4S+kM(I?RV-OJSKrfw&_D|rut1~_k`Th3W>t)(M<^ep{I!4Hf z5QTsN0-_D*rBl`Zi+mY9M1H+h`?Jlrjgr|1%wsOr$^dqCTOr^G1VkIqOQ)*+6ZqhF zmdNw$&+d?E|ESAzmLr6$h)@U^ARy|$UOJWSAKwBm6?q=F_DHtvvSNvSKc3t(mF*vEf?F3?XDk-^p8xhhiS|$X13f1=LePo`g@6tMqVDUZQ`!Crj-u+U zyF|WUS#b($=p%DYuy=|M?CO?6z!3s?#ob52zG*uXyQknPY%@_#xTP z5yDnPCfe+Iusqe5U;mkI$#ARy|!UOFA@zofQP zNASK`|AWDQgNQ-ZlS5_p0jqFUKpRI0T@j%W2p}NpyIwk7?4RH$_GP{; zV(`^Z*T}Sgi~&k-REnn%a1sKdzU!sa$^MIKI-D$GP~z*%HVzmmuMdC^G_@w!N!qVy z5%pa!olf>o+JL>mUyC^WTz;!O``7jcm-bOwrx0)o0-~<#rPIy+i)u2?6LA<*d6?|; zf@V5J_KFULfT-(w>2$MyIx7G;%onlv{eyeujsbGZPEwku5O4wlqMqxe)6M=#CYT4j zQN-fqRb5%bg(dR(fPC*8PLRE#K_MXOxn4RQ?SB+Iunn+SCE~GY@+0!>KhN8M^*{$I z?NbOO8Uazq_0s8R|0I)QU)G}{9jXXWv|*+2#ETv zmriHB&*{gOH0-|o~rPJO1ORBPt6LF!l0%{)|E^`d9kQEkgr!-L^kO%}s-PTK|yZw_) zfXinhHs61JlT7=kvjQ$mgzQzD3IS2C_0r1z>04ljd=VS(`Yx>QrqL4n0MB_1@t)frcc_>5n3 zG~0HmTV@~dZhC8?;yw`wh&rv8R`&0gN+EM?4}6x27%g~H>RADete5+cMCgEOGkp;d z^;s{i?7vAj@D6aVh|%Qbr?bX$B%T$J?at#1*?)8Vi)*q5i#WZrsFzIp$5{d1r<=E^t`q|H zAt36qURv3IbL_nvGxx-KL0d$uR*!r}W*<<=@?YMid8_J5Az%*zq8{s|mHoHGz1WvA zR>bPlxtGYae_9W8XUkUA9fg2=1Vla7ODp?tiFt8t#$mv!QN+tv@Pf=fU@^P+wbm_L zR(BKvauE=9STC*YUmtH^^`3~=*FQ);Dabo~*}pN?!0USvvss_tCe!{g4^Yz0xOJ7I5QrTC zQGfN)%KnWp9_{ORk%-xtb%(O8R~Jj{14=(MZe8Ul1Y$=()L*@{vVUWYu_kysVD^`Y z+aK@UC(-`BjjU(s5ylj#9ECt^2#C6?msa*49$&XF^A-`eNh|-$8qSk=RzRM2TzCpp zu0kMs1Vr7{ODp>ikL`HY^d4 znW0aJ*u7Da%UDnB>DSKtlo6K>F{eU3p%9210a0)D($aJoXJ1E7U2>Ets%e`6>^6${ zt#Cgj(f++Y)@N+nzzcF@tIhY4LOT@8s{ei6R z2AO9Cc&F*LtL`ZTqCr5^RlT&b|Hx2f%+;#cmo-RAC3!?myS6arQe5cO0qt?b__l1WV&xNl&By$X~mE{-j)6}9$DFmWJK-5vaw6g!`a4o9Id_cr=$}*f4aF)cg0`j~s zL`Rt_r4VRBK-5vaw6gyutRhK`s_d{E@T?XwT{>3cSpm7;8k`x}E)ts5vkHMw1VsJR zODp>iMa=p&-T{V+n7;S-*)rb&sIGz5sZmcV1X>~>>Ze{>*?&vStllZvl(9E3-74a` zX4qhveZX?o)w_dLden0YflvfQ-PB7f`wvAdvTLjdm>}Xh{kMKH?Vt7q^^c4q^|V5u z2?0?z_0r1zo3M%|RZ_eAks_|{>b+U@W5ZZO(a$F3*9^&=76 zZ@#}yru}0KP})0oDpd{Xh=8b(fRpyx@ww|he+2-3vN$dkWGh(Mw)sT(| zhvJv)t92-#qVRIh3ke6aq~MNYl?0_W##}R%9ub?-?o$-#l+U>s8VvG78kw z3W4wlNYhVc|1E%K-Anccz7od(o{=pYQ8yFTm?2(9KQw ztuJADo`W3Dnq$ZV#vet2v!3nKdcc*e_oz0GVNMZ})CfeNmuv8QU%y)5Kssx1QY@a0fm4ha zpb$_9Cy)5Kssx1QY@a0fm4hapb$_9Cy)5Kssx1QY@a0fm4nh4M>ZVH_LBhQr;}TwMlui{8lFA&GH2WAa%%Ts*aI;hzb&2enGHC{qkn{ zIVR=J^0Q6Ko8>ntfIj_A`E|PZH7P)J`ArI-U*3AYPnW(X1+kJ}5JVFRO+T6xh{}h{ zuh7pA7Kt)H2b5@k<@)vK1hSbyezus;(^~B3FPLA5`lHAXDhP_}l^64ygh37Y+Yc%b ziigXm?pZIND~MS>wZ@ZwJ#KuBf#H2FJEDlU*MiU z_kuB}&cAwllt-;PAL5T+YJ!#-Z>vwKAFaJI2eoQ^Dc#yz@MGKB^z3+he&VgX9bf0q zZTsHpQpM?})hFJTv3u}ya67&p&)klu^Y-W{i5-vSJL+!~r_U7d9B_+k{h8lw-*C5m z!`=1_--G%o;Q5;S_H}*VzUIDt&39K`bKjokK3s9op5mZA#X)C%#|BKVy#c;`}E#yJUUhOFuGwkxsyfLjlJA=y-pZLqSG<`iwXfXvCMFr>prF zUglueh+hdJd9uND&-}oGNWNdiKd~Sje}f}|IR3-}Nsa_zz7R-qBnb0`K$0T?nA1hX zkpLXO5ac#r2*UAqc=7_wcr1|OK%mL7Ac_M)CdYy(4g{GT3v!=hL1z374g{I;bQN(R z(2U0dX`bB!o*WCJdAf>+f{6HtLqSCR4ekXJ@h3WAH1`4t4h7QOpD=<$K{WRQ2u_!8 zaVUt$A8{y%h=;uV01fMC08pU2oKo$-cD?3M7Mvd{Z4e2e*ASc6!Jz%m@9zfTJEoI~W-=j(g8(aNf2SZQ?opbSE}63f5IM%=b*|mEIl`@`PmX zh=1_?nSO^)bWawl=C!SNvJ zR=M{(;){JRr=&m;V;!jr$HojD!)Zn&$OdI11G+rJ;kzeLs-KS=DYrm0N43BC!1aiaOT@k#epJ)x96eFT!W1Cz3*-2;F*dMZo#cFQhQB^71KQG(z61!%<+tq}G2K`xl|J8C! ziUAP?Qy^je|Ng07Ai~dky!3rWl&KtadWPuF{1p*{cPB+XCm(1!4-5K8QC~hk7L|Bx z>j1b2qeL)2Mz9hn&Pay6zC0S$ds&0jTbCeI-3xL*|F5ECW%skX3JGa#1yR}Oi0ppN z>9En9svED1Q^jy1nD0v?6x33kS$UB01$Kp`XhgP2Zt0Ncq&JMifmM`Q=$o;$+3x2& zE*@xp5sAK4BM{@Ie>83D5Xi>SEIu@OwHE6F;#bdBL6+y5HHBOD=isxK=f(noorM;}P-Z9hr=Ss0KT@gb& z0JGU}xAb{>-|&cHr~16-*5LaY^Pk#Ko^zp{r5-VP^Y4=)>ct+p?Zz%jNjaOCX~P{M z!=7^qv!CLrrJxUKIG+iU5>GJ!OaXs!|L%lMh1=)8nGU?0>U)HSzhEfwx^%YDkBM@L z1JE6=iovT43x4X>?V=`P+Vd(tWKynVu7FeJhN@F6ujMVG&Z^S6-m960UpzVn1pOtG z+aMUH=ouS%tTo#(0RkwbGu!N4LSH$1g{A?0*$zL@?hS3b?1H(fB z>)%Y$>Eqo*R@MDfeaF^jiOoStpPaukAuVaE)3)J!$g1; z)myyvZo2ZTkMroEvwlwi*a-CoJEs6c^`39$f25Zbx5j_Pt=cw2Er(^7=0D51OC#cIV)csWNkn zZSeq)R_@!rC;u=f@1_X?p?X!Yx7&AO-b9E;Vs7#c^1vwmsg9vEtrM|im33+PGwWCV zz*M>UAr)`=wUe+0AyAaq!ez;TYA7bAb15;K-c6O`n#m(ohJ|$F6CTfYF?el)q3$k0 znvMe;@IcTGI)xJC?~AozD~l$aLXatjRoQi#qt}!0Oj~(x`6*4Y5J9+6L*1`J>!+;I zcpHdc88G7%fdm10O?wh~TsmGll|Fn| z*H^(v586<3EYsu`U7OUsl|i9eTO-xTMRdcBgT4wF+8IdUxyG7pKiD^l=egLt=+*p| zhLNsaz`n>o%!msMgl@{k*GN?fkxPpniuwh2#tAQ`d0j`k--%sw!FgP1mg~2Na6LP| znf6)pV|9GCu(#dW!FFi;y*Us~TtwhXwAV~TF+jcxy z8Y_eKQw6i)GqlS-&&FS{LUu#82l~@bu2*z_n|XB%o{b`EOhnjF@!{d^*9632pey zgf2vZgstG)($BrHV1KvgZQK8gFQu&Y^SsOb1`j`U{^kL0dJQ=CV!EuI8&sbAG=6g_l&L zG>cq$jZ{l9@hj20Sqv99b1%+}xUbFqun~5=?@Ev}O3RNfCs!~)K+oFt;ON*>eh%`0FV2 zUq1rR)S4d{8M2TWsG!&}F{x1FK#W1AG`?mPpVco$&U9MXjBdI7FY#_68@+i@y=5V~ zjSP`UCdYcTwov0!;eWWOKybfg&zsNQCzD9P9bLC;u0!}G``PCyHX_LpWAbpT(=6kx zs+FyeCRNwC$bGUj{%elwi$SbJs(MYZ_&c5lzAc-yARZ;~rd&a%9}*l^m(D%P96qWk zC)-D)KYmXvm+j9w{}l^F1h4I{Um*AeI&G+VL!TgQuT$cYvzn_2LO9En!Px!X0FM+v zqyMiettfx^^d2Naeyn1^jyOxD^1_7m%*F>x$p70o95!04D03FQr~LfrM~*1s{#P&9 zjcBm9QMqlNd9x&kri)|dL=l&!b3YOP9PLvMqc*(g#K+hU1GMsoZ7umVHY%@lGO*He zFD?dka}i`e1i#rF;<%n={nXv8zBvOr4fpY@sJtWy?1;0ex%2&2cf}nDS{2l?DTa%R zMbIs3%r_%yl~_sr^X*ajAh}53h^L%Tr&@Fnc-_kPx-+hjc4$V*m#>iQC=Cjz;(k6M z%n?lALegU$fS>icLQ?2XZ zt@B^q&RzX*+x#5_@W8J{{3SH3WeS?1@AdT}8a{Q10GY zO*VniZ=BFipMoKB1c?WdVpnqX_5i!!Ku)_U4;L7UB18}k$zt^xMCIsv?B*`TaPdgT9D%n5$1zwNXe z(mx`uj@dUYNxxN2HG%I|% zzo0eG;6A--+D!AMGM})gobgn(`mFF zO}k5EmCnZ}T-va^GSYRt{l5tI=5in2xBX6q2^xLqnR)L&$oOVCvte`}OVHc<_siPP zS{0wcJo!EhZIpG`3`HwSI0TMuyVU=84?PA*8iRcuTIVl4a;?bBgg^I>cR%gES$kj1 zFszfNhZ$%l*(B~ooDL<4G8H*LzTs)t9)G{=;%P@2HtE$2Lnks?e0j?h#y6-r(^)t1 z(GS0|9-yay{wDT71wosUJyDkRA_tbD&j(gPyG7p?F1RA5_%x&>NEH1jluu@Y6yU|r z+RLrA)acIznw86h1z2N%pJt`ZbW^`nEuS>`Fz2&F3fI4kk ztYYG}%6!M{%P&igBnwCx!x}rUC0Iu@`4hWokUv92iuGzS_D>j*QAW#$*)5OhsAkFB0N|d0RY}N3GrvuJe*nTpd+!*GZg*-D{ zY$8AFqG+&REXQQ4X!`k@8~{(T3kga~{bT&wi`t({#&tujyOg<3TcB^7b8B4mf{5-D~rr+U>7X z`?kHn0@HgOS~iTooJK&4uOm*orYD0XXFE2AAep8%dML&Lu*l4{^Xw`(AL_kwQ{;lw zFwg#44Sj%B&nCf)|5{~C;8H$jVn}ZH=dI{9Xl#{6n4|6M(h4% zZjh!U>xbV?ZI`(OM6a$izmUJ(Z;c|ov=4xX_Vp@(Hw@URWEkE?5C^a2`YhAg-#yQ( z^ja=m>YO>(W6xQ1y~~N zO~p*D?F7K6ZNC4CE1GXFHE7zW3zZtF$=EW;5O3?!o+SjwgNZY|XI>_k6&kC*QQbA! zYS~xUta5qJ>5a?s8b^~ol^Gqh z@!b*;P{`1C)!Nr-xFQ5Qs113ZyX{o7t4o)97V+&3(D{~Ex;JynZ+R7YK26wr?7sT6 zQ4()&EPv2lnvRe*&&5oh$ z@o&LK8Ms`ZO4tN3phTZ|)sbhnHt_4^sh0Bmj`V89+gy^RBfKv4l%~}m6z#_|+%Noz z#%yi5=Om%|tFb&tO3sd8WHVBe{rnx=o@Y>cB6;zT`AJL#x{%b3_;7&t?2|E7WvQFO zn%@xi%Z(1d!0m3vMg_QB4CS2$3WC_fRJVl(#iNDPDl9=xm zxk@@rqQkRMEuc_!<*U|p*9oOYCAeJc?SIE$!=0nlnu?b~wAgSC`itUD?!^YKzy<5z z6sP=Yd~NIbN__BC<(y;0L?ejvO90=o}YSy}F!Td@1D9%-UvP&w+zdf)%Kk7kuqb4=Fe}KCTHdI{0&~anx5Ebn*8=HEZ|Yw>xcV6 zw92;pQI{ND9!~xtr=aZSAptdIr9a1c_XqhKS>6$|zgV|kZ$1b}uzk0-mIWj=UN94R(D6P-a<#}$l zcMGKFz84NWdv1!J>YV0!V+QzSX^T{JwAZxiTR0bp0PQPBvm=GLVy4vu%oFu@FLTbF z=7qyQ$AFxj|E&8SUf#+Z!w$2QiBx@H^m`vY30yv!jP4moY9E%Ss6xvxPGdEhWZaI8 zB)&LwC-Is6-g~CHH5M=u<)HQ~bjjIC#@OvU*Xcfa7#R^H?e$8YJN5xo3n4&PlK?X{E*`_-XEo~OVS8IH#RVzcYQuv?S zIi^`F8xp&5A3{xc%*5JbUdSAkYTN0I<($euQFLqtJWVkGecM6O&AJUtOWxbAJaPTR zh8&Bc&*j6kK$X*o-Ds4->iF2@E2W+m-OuDcO*-|hX26#WNeKmD+N6l zT6qq3-<=KT7N>}xfLEKrt`uL8q>}&{B@)A@%qn}58e`mBbOL`i(Y`>$w~20{R>{TL z0W6Kbgm=rr?9W9awxmz#@j9B5pvOf3d{Hk*KfHN^!fGwv#j{kMl+%>C{P~CHT(z8= z?4<2sU$-5A_XPVyMIr#Wo7(Qg$o|q@KHSE3r6?*vY7hWfNJr_x(+I@ET9A5%M_1 zd}>4XX)E6Fr7npxJs8&$8s+CQH}iWoQNBmwu9C_2-WGya;dtVB&cUhESBaHFx0CBj z6Pr=Rpft=QCF+k&4_MBuf3H8+?(CJF1XP_`5)ziLO8=}PpuL5-{Wm7gBq9=VMZ&Vc z&ylnp9I{LHh1jUm0o7ILd=4Dnw0mc5V|q*PYi8V4#dRwQQkX`Hbx}XLM^@3ZX{P-~ zTQBhsI@-CHl`Te`K|-srsuR(wh4GLnW!fXMO3wf4IR zombBHq+=Uj{M9|#adx;fQAAn7m1DzijC!Yvx7b&i&~14Bj!L=GZ50EHg~<@@gp0wY zXMjdNey*o7*ku|g2qd0p15Xa+4);0)G>vjTT4$0MgOZLNHac=8=4?1q?X}b{FG;7< z*5sw=f@m7LhA~>ht37`B-ZX|0HGwO{dQ|qvQp@;7Hap9wEkWRb14WzoLr{(Gi*`+q z!|W3fqw_#p8S$H;tilc-JWkB>xM)*TYyJP!j|hI2q;30tn7NVC@N)aN06G54O-gy1 zc28x{-C-MIflDy9%p7Y?EqJcf@Yb;_<=6@aMV$&?3Ru*dIdXNEaQ`S|?Dm zt}bw%Ild4C3)98-skjwyV)ULI|J_)T>AAbbh(C;8IgF#%g-MHJAAfMCLsVKkGguzbgR&F?S((;OX;G(XY&a!^>a+h4#`!?@7_bMJ zWR2G9kvOX%dbAwaT{0{;l{1SwQWjoa7SA&dFfw0B9G}oWNK*#qgTyn1xq5j9msoh% zQtSZ{K;^GNuZ%-J+0}0g%@cy9dPg`4tZSfvzq7j}ho(W6`s;89@xk0>FcAPQx3s)Z zl3PgjM?sncFDcAh?;#zO!xPY7CJV1Z-xlA( z%klrxisE=3J`ND1qzX6Qe?X0wd54FFMh`MM5L0WB5_RA_B~h@;j~+E2xVu{J6ig#m zAFhb8q{=x19|Qe8;-*xF{Q;p0U=ke~`Fim&ZM=L{iQkG6|K7r5V}DU`j4nyZ;-xY_ zso`IflHIKT$lb}|8rCzo;(Fqln^#K=OFC(f`+NESy, + http_pool: kb_rpc::HttpEndpointPool, + ws_pool: kb_rpc::WsEndpointPool, + demo_ws_session: tokio::sync::Mutex>>, + demo_backfill_running: std::sync::atomic::AtomicBool, + demo_backfill_cancel_requested: std::sync::atomic::AtomicBool, + demo_core_extraction_running: std::sync::atomic::AtomicBool, + demo_core_extraction_cancel_requested: std::sync::atomic::AtomicBool, + demo_decode_replay_running: std::sync::atomic::AtomicBool, + demo_decode_replay_cancel_requested: std::sync::atomic::AtomicBool, + demo_decode_replay_campaign_id: std::sync::Mutex>, + demo_execution_solana_core_running: std::sync::atomic::AtomicBool, + demo_execution_solana_core_cancel_requested: std::sync::atomic::AtomicBool, +} + +impl crate::AppState { + /// Initializes configuration, logging and shared runtime state. + pub(crate) fn initialize() -> kb_core::Result { + let config_path = resolve_config_path(); + let app_config = match kb_config::read_config_json_file(&config_path) { + std::result::Result::Ok(config) => config, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let active_profile = match kb_config::active_profile(&app_config) { + std::result::Result::Ok(profile) => profile.clone(), + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let logging_config = convert_logging_config(&active_profile.logging); + let logging_guard = match kb_logging::init_logging(&logging_config) { + std::result::Result::Ok(guard) => guard, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let http_pool = match kb_rpc::HttpEndpointPool::from_profile(&active_profile) { + std::result::Result::Ok(pool) => pool, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let ws_pool = match kb_rpc::WsEndpointPool::from_profile(&active_profile) { + std::result::Result::Ok(pool) => pool, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(crate::AppState { + config_path: config_path.display().to_string(), + app_config, + active_profile, + logging_guard: std::sync::Mutex::new(logging_guard), + http_pool, + ws_pool, + demo_ws_session: tokio::sync::Mutex::new(std::option::Option::None), + demo_backfill_running: std::sync::atomic::AtomicBool::new(false), + demo_backfill_cancel_requested: std::sync::atomic::AtomicBool::new(false), + demo_core_extraction_running: std::sync::atomic::AtomicBool::new(false), + demo_core_extraction_cancel_requested: std::sync::atomic::AtomicBool::new(false), + demo_decode_replay_running: std::sync::atomic::AtomicBool::new(false), + demo_decode_replay_cancel_requested: std::sync::atomic::AtomicBool::new(false), + demo_decode_replay_campaign_id: std::sync::Mutex::new(std::option::Option::None), + demo_execution_solana_core_running: std::sync::atomic::AtomicBool::new(false), + demo_execution_solana_core_cancel_requested: std::sync::atomic::AtomicBool::new(false), + }); + } + + /// Returns the path used to load the configuration file. + pub(crate) fn config_path(&self) -> &str { + return self.config_path.as_str(); + } + + /// Returns the complete parsed application configuration. + pub(crate) fn app_config(&self) -> &kb_config::AppConfig { + return &self.app_config; + } + + /// Returns the active profile selected from the configuration. + pub(crate) fn active_profile(&self) -> &kb_config::ProfileConfig { + return &self.active_profile; + } + + /// Returns the configured HTTP endpoint pool. + pub(crate) fn http_pool(&self) -> &kb_rpc::HttpEndpointPool { + return &self.http_pool; + } + + /// Returns the configured WebSocket endpoint pool. + pub(crate) fn ws_pool(&self) -> &kb_rpc::WsEndpointPool { + return &self.ws_pool; + } + + /// Returns the demo WebSocket runtime session slot. + pub(crate) fn demo_ws_session( + &self, + ) -> &tokio::sync::Mutex>> { + return &self.demo_ws_session; + } + + /// Returns the single-campaign execution flag used by the backfill demo. + pub(crate) fn demo_backfill_running(&self) -> &std::sync::atomic::AtomicBool { + return &self.demo_backfill_running; + } + + /// Returns the cooperative cancellation flag used by the backfill demo. + pub(crate) fn demo_backfill_cancel_requested(&self) -> &std::sync::atomic::AtomicBool { + return &self.demo_backfill_cancel_requested; + } + + /// Returns the single-campaign execution flag used by the core extraction demo. + pub(crate) fn demo_core_extraction_running(&self) -> &std::sync::atomic::AtomicBool { + return &self.demo_core_extraction_running; + } + + /// Returns the cooperative cancellation flag used by the core extraction demo. + pub(crate) fn demo_core_extraction_cancel_requested(&self) -> &std::sync::atomic::AtomicBool { + return &self.demo_core_extraction_cancel_requested; + } + + /// Returns the single-campaign execution flag used by the decode replay demo. + pub(crate) fn demo_decode_replay_running(&self) -> &std::sync::atomic::AtomicBool { + return &self.demo_decode_replay_running; + } + + /// Returns the cooperative cancellation flag used by the decode replay demo. + pub(crate) fn demo_decode_replay_cancel_requested(&self) -> &std::sync::atomic::AtomicBool { + return &self.demo_decode_replay_cancel_requested; + } + + /// Returns the active contextual decode campaign identifier slot. + pub(crate) fn demo_decode_replay_campaign_id( + &self, + ) -> &std::sync::Mutex> { + return &self.demo_decode_replay_campaign_id; + } + + /// Returns the single-execution flag used by the Solana Core execution demo. + pub(crate) fn demo_execution_solana_core_running(&self) -> &std::sync::atomic::AtomicBool { + return &self.demo_execution_solana_core_running; + } + + /// Returns the cooperative cancellation flag used by the execution demo. + pub(crate) fn demo_execution_solana_core_cancel_requested( + &self, + ) -> &std::sync::atomic::AtomicBool { + return &self.demo_execution_solana_core_cancel_requested; + } + + /// Returns the number of logging routes held by the logging guard. + pub(crate) fn logging_route_count(&self) -> usize { + let lock_result = self.logging_guard.lock(); + let guard = match lock_result { + std::result::Result::Ok(guard) => guard, + std::result::Result::Err(_) => return 0, + }; + return guard.route_count(); + } +} + +fn convert_logging_config(config: &kb_config::LoggingConfig) -> kb_logging::LoggingConfig { + let mut targets = std::vec::Vec::::new(); + for target in &config.targets { + targets.push(kb_logging::LogTargetConfig { + name: target.name.clone(), + enabled: target.enabled, + sink: target.sink.clone(), + level: target.level.clone(), + path: target.path.clone(), + rotation: target.rotation.clone(), + format: target.format.clone(), + ansi: target.ansi, + targets: target.targets.clone(), + }); + } + let mut target_filters = std::vec::Vec::::new(); + for filter in &config.target_filters { + target_filters.push(kb_logging::LogTargetFilterConfig { + target: filter.target.clone(), + level: filter.level.clone(), + }); + } + return kb_logging::LoggingConfig { + default_level: config.default_level.clone(), + targets, + target_filters, + }; +} + +fn resolve_config_path() -> std::path::PathBuf { + let configured = std::env::var("KB_CONFIG_PATH").ok(); + return resolve_config_path_from_value(configured.as_deref()); +} + +fn resolve_config_path_from_value(value: std::option::Option<&str>) -> std::path::PathBuf { + if let std::option::Option::Some(configured) = value { + let trimmed = configured.trim(); + if !trimmed.is_empty() { + return std::path::PathBuf::from(trimmed); + } + } + return crate::workspace_root_dir().join("config/example.config.json"); +} + +#[cfg(test)] +mod tests { + #[test] + fn resolve_config_path_uses_workspace_example_by_default() { + let path = super::resolve_config_path_from_value(std::option::Option::None); + assert_eq!( + path.file_name().and_then(std::ffi::OsStr::to_str), + std::option::Option::Some("example.config.json") + ); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/constants.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/constants.rs new file mode 100644 index 0000000..02167e9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_app_demo/src/constants.rs +// version: 1 + +//! Local constants for the `kb_app_demo` crate. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_app_demo"; diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_backfill.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_backfill.rs new file mode 100644 index 0000000..1419ade --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_backfill.rs @@ -0,0 +1,457 @@ +// file: kb_app_demo/src/demo_backfill.rs +// version: 9 + +//! Tauri commands and UI payloads for bounded HTTP transaction backfills. + +use tauri::Emitter; // rust-rules: trait-import +use ts_rs::TS; // rust-rules: derive-import + +/// One endpoint role capable of discovering and hydrating transaction history. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_backfill/DemoBackfillRoleOption.ts" +)] +pub(crate) struct DemoBackfillRoleOption { + /// Stable endpoint role code. + pub(crate) role: std::string::String, + /// Providers exposing this role. + pub(crate) providers: std::vec::Vec, +} + +/// Initial options shown by the backfill demo. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_backfill/DemoBackfillOptionsPayload.ts" +)] +pub(crate) struct DemoBackfillOptionsPayload { + /// Selectable endpoint roles. + pub(crate) roles: std::vec::Vec, + /// Preferred role when configured. + pub(crate) default_role: std::option::Option, + /// Default transaction commitment. + pub(crate) default_commitment: std::string::String, + /// Default history page size. + pub(crate) default_page_size: u16, + /// Default maximum number of history pages. + pub(crate) default_max_pages: u32, + /// Default hydration concurrency requested by the operator. + pub(crate) default_max_concurrent_requests: u32, + /// Default retries after the initial attempt. + pub(crate) default_max_retries: u32, + /// Whether one campaign is currently running. + pub(crate) running: bool, +} + +/// UI request for one bounded backfill campaign. +#[derive(Clone, Debug, serde::Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_backfill/DemoBackfillRequest.ts" +)] +pub(crate) struct DemoBackfillRequest { + /// Endpoint role used for history and transaction requests. + pub(crate) role: std::string::String, + /// Commitment used by standard Solana HTTP requests. + pub(crate) commitment: std::string::String, + /// Source mode: explicit_signatures, program, token or pool. + pub(crate) mode: std::string::String, + /// Newline-separated signatures for explicit mode. + pub(crate) signatures_text: std::option::Option, + /// Program, token mint or pool address for address history modes. + pub(crate) address: std::option::Option, + /// Optional anchor transaction signature. It is required only for newer-history scans. + pub(crate) anchor_signature: std::option::Option, + /// Direction relative to the anchor, or from the latest entry when `before` has no anchor. + pub(crate) direction: std::option::Option, + /// Maximum number of address-history signatures to hydrate. + pub(crate) limit: u32, + /// Maximum RPC page size. + pub(crate) page_size: u16, + /// Maximum number of history pages inspected. + pub(crate) max_pages: u32, + /// Operator concurrency cap. + pub(crate) max_concurrent_requests: u32, + /// Retries after the initial HTTP attempt. + pub(crate) max_retries: u32, +} + +/// One progress event emitted to the backfill window. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_backfill/DemoBackfillProgressPayload.ts" +)] +pub(crate) struct DemoBackfillProgressPayload { + /// RFC 3339 timestamp. + pub(crate) timestamp: std::string::String, + /// Stable severity code. + pub(crate) level: std::string::String, + /// Human-readable message. + pub(crate) message: std::string::String, + /// Completed candidate count when known. + #[ts(type = "number | null")] + pub(crate) completed: std::option::Option, + /// Total candidate count when known. + #[ts(type = "number | null")] + pub(crate) total: std::option::Option, +} + +/// Final UI-safe summary for one backfill campaign. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_backfill/DemoBackfillSummaryPayload.ts" +)] +pub(crate) struct DemoBackfillSummaryPayload { + /// Unique capture session identifier. + pub(crate) capture_session_id: std::string::String, + /// Stable filter code. + pub(crate) filter_code: std::string::String, + /// Endpoint role. + pub(crate) role: std::string::String, + /// Provider used for hydration. + pub(crate) provider: std::string::String, + /// Endpoint code used for hydration. + pub(crate) endpoint_code: std::string::String, + /// Number of history pages fetched. + #[ts(type = "number")] + pub(crate) pages_fetched: u64, + /// Number of unique candidates selected. + #[ts(type = "number")] + pub(crate) candidates_selected: u64, + /// Number of candidates admitted to the bounded execution queue. + #[ts(type = "number")] + pub(crate) candidates_started: u64, + /// Number of candidates that reached a terminal outcome. + #[ts(type = "number")] + pub(crate) candidates_completed: u64, + /// Number of admitted candidates interrupted before a terminal outcome. + #[ts(type = "number")] + pub(crate) candidates_cancelled: u64, + /// Number of selected candidates never admitted after cancellation. + #[ts(type = "number")] + pub(crate) candidates_not_started: u64, + /// Number of transactions received and normalized. + #[ts(type = "number")] + pub(crate) transactions_received: u64, + /// Number of canonical rows inserted. + #[ts(type = "number")] + pub(crate) canonical_inserted: u64, + /// Number of canonical inserts skipped by idempotence. + #[ts(type = "number")] + pub(crate) canonical_skipped: u64, + /// Number of signatures skipped before hydration because they already existed. + #[ts(type = "number")] + pub(crate) existing_skipped: u64, + /// Number of missing transactions after retries. + #[ts(type = "number")] + pub(crate) missing: u64, + /// Number of failed candidates. + #[ts(type = "number")] + pub(crate) failed: u64, + /// Number of observation rows inserted. + #[ts(type = "number")] + pub(crate) observations_inserted: u64, + /// Number of source attempts. + #[ts(type = "number")] + pub(crate) attempts: u64, + /// Whether the operator cancelled the campaign. + pub(crate) cancelled: bool, + /// Optional cursor for continuing an older-history scan. + pub(crate) resume_before_signature: std::option::Option, + /// Campaign start time. + pub(crate) started_at: std::string::String, + /// Campaign completion time. + pub(crate) finished_at: std::string::String, +} + +pub(crate) struct DemoBackfillObserver<'a> { + pub(crate) app_handle: tauri::AppHandle, + pub(crate) cancel_requested: &'a std::sync::atomic::AtomicBool, +} + +impl kb_pipeline::BackfillObserver for crate::DemoBackfillObserver<'_> { + fn on_progress(&self, event: &kb_pipeline::BackfillProgressEvent) { + let payload = crate::DemoBackfillProgressPayload { + timestamp: event.timestamp.clone(), + level: event.level.code().to_string(), + message: event.message.clone(), + completed: event.completed, + total: event.total, + }; + let emit_result = + self.app_handle.emit_to("demo_backfill", "demo-backfill-progress", payload); + if let std::result::Result::Err(error) = emit_result { + tracing::warn!( + target: crate::TRACING_TARGET, + "cannot emit backfill progress: {error}" + ); + } + } + + fn is_cancelled(&self) -> bool { + return self.cancel_requested.load(std::sync::atomic::Ordering::Relaxed); + } +} + +pub(crate) struct DemoBackfillRunGuard<'a> { + pub(crate) running: &'a std::sync::atomic::AtomicBool, +} + +impl std::ops::Drop for crate::DemoBackfillRunGuard<'_> { + fn drop(&mut self) { + self.running.store(false, std::sync::atomic::Ordering::Release); + } +} + +pub(crate) fn build_role_options( + snapshots: std::vec::Vec, +) -> std::vec::Vec { + let mut role_methods = std::collections::BTreeMap::< + std::string::String, + ( + std::collections::BTreeSet, + std::collections::BTreeSet, + ), + >::new(); + for snapshot in snapshots { + for role in snapshot.roles { + if !role.enabled { + continue; + } + let entry = role_methods.entry(role.role).or_default(); + for request_kind in role.request_kinds { + entry.0.insert(request_kind); + } + entry.1.insert(snapshot.provider.clone()); + } + } + let mut output = std::vec::Vec::new(); + for (role, (request_kinds, providers)) in role_methods { + let supports_all = request_kinds.contains("*"); + if !supports_all + && (!request_kinds.contains("get_signatures_for_address") + || !request_kinds.contains("get_transaction")) + { + continue; + } + output.push(DemoBackfillRoleOption { + role, + providers: providers.into_iter().collect(), + }); + } + return output; +} + +pub(crate) fn build_demo_backfill_pipeline_request( + request: DemoBackfillRequest, +) -> std::result::Result { + let source_result = match request.mode.trim() { + "explicit_signatures" => explicit_source(request.signatures_text.as_deref()), + "program" => address_source( + kb_pipeline::BackfillAddressKind::Program, + request.address.as_deref(), + request.anchor_signature.as_deref(), + request.direction.as_deref(), + request.limit, + ), + "token" => address_source( + kb_pipeline::BackfillAddressKind::Token, + request.address.as_deref(), + request.anchor_signature.as_deref(), + request.direction.as_deref(), + request.limit, + ), + "pool" => address_source( + kb_pipeline::BackfillAddressKind::Pool, + request.address.as_deref(), + request.anchor_signature.as_deref(), + request.direction.as_deref(), + request.limit, + ), + _ => std::result::Result::Err("unsupported backfill mode".to_string()), + }; + let source = match source_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let pipeline_request = kb_pipeline::BackfillRequest { + role: request.role, + commitment: request.commitment, + source, + page_size: request.page_size, + max_pages: request.max_pages, + max_concurrent_requests: request.max_concurrent_requests, + max_retries: request.max_retries, + }; + let validation_result = pipeline_request.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error.to_string()); + } + return std::result::Result::Ok(pipeline_request); +} + +fn explicit_source( + signatures_text: std::option::Option<&str>, +) -> std::result::Result { + let text = match signatures_text { + std::option::Option::Some(value) => value, + std::option::Option::None => "", + }; + let signatures = text + .lines() + .map(str::trim) + .filter(|value| return !value.is_empty()) + .map(str::to_string) + .collect::>(); + if signatures.is_empty() { + return std::result::Result::Err( + "the signatures textarea must contain at least one signature".to_string(), + ); + } + return std::result::Result::Ok(kb_pipeline::BackfillSource::ExplicitSignatures(signatures)); +} + +fn address_source( + kind: kb_pipeline::BackfillAddressKind, + address: std::option::Option<&str>, + anchor_signature: std::option::Option<&str>, + direction: std::option::Option<&str>, + limit: u32, +) -> std::result::Result { + let address_text = match address { + std::option::Option::Some(value) => value, + std::option::Option::None => "", + }; + let address_value = address_text.trim(); + if address_value.is_empty() { + return std::result::Result::Err("address is required".to_string()); + } + let direction_text = match direction { + std::option::Option::Some(value) => value, + std::option::Option::None => "", + }; + let direction_value = match direction_text.trim() { + "before" => kb_pipeline::BackfillDirection::Before, + "after" => kb_pipeline::BackfillDirection::After, + _ => { + return std::result::Result::Err("direction must be before or after".to_string()); + }, + }; + let anchor_value = anchor_signature + .map(str::trim) + .filter(|value| return !value.is_empty()) + .map(str::to_string); + if direction_value == kb_pipeline::BackfillDirection::After && anchor_value.is_none() { + return std::result::Result::Err( + "anchor signature is required for newer-history backfill".to_string(), + ); + } + let limit_value = match usize::try_from(limit) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!("signature limit conversion failed: {error}")); + }, + }; + return std::result::Result::Ok(kb_pipeline::BackfillSource::AddressHistory { + kind, + address: address_value.to_string(), + anchor_signature: anchor_value, + direction: direction_value, + limit: limit_value, + }); +} + +pub(crate) fn demo_backfill_summary_payload( + summary: kb_pipeline::BackfillSummary, +) -> DemoBackfillSummaryPayload { + return crate::DemoBackfillSummaryPayload { + capture_session_id: summary.capture_session_id, + filter_code: summary.filter_code, + role: summary.role, + provider: summary.provider, + endpoint_code: summary.endpoint_code, + pages_fetched: summary.pages_fetched, + candidates_selected: summary.candidates_selected, + candidates_started: summary.candidates_started, + candidates_completed: summary.candidates_completed, + candidates_cancelled: summary.candidates_cancelled, + candidates_not_started: summary.candidates_not_started, + transactions_received: summary.transactions_received, + canonical_inserted: summary.canonical_inserted, + canonical_skipped: summary.canonical_skipped, + existing_skipped: summary.existing_skipped, + missing: summary.missing, + failed: summary.failed, + observations_inserted: summary.observations_inserted, + attempts: summary.attempts, + cancelled: summary.cancelled, + resume_before_signature: summary.resume_before_signature, + started_at: summary.started_at, + finished_at: summary.finished_at, + }; +} + +#[cfg(test)] +mod tests { + #[test] + fn explicit_source_splits_lines_and_ignores_empty_rows() { + let result = super::explicit_source(std::option::Option::Some(" first \n\n second\n")); + let source = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("source failed: {error}"), + }; + assert_eq!( + source, + kb_pipeline::BackfillSource::ExplicitSignatures(std::vec![ + "first".to_string(), + "second".to_string(), + ]) + ); + } + + #[test] + fn older_history_accepts_missing_anchor_and_uses_latest_filter() { + let source_result = super::address_source( + kb_pipeline::BackfillAddressKind::Program, + std::option::Option::Some("11111111111111111111111111111111"), + std::option::Option::Some(" "), + std::option::Option::Some("before"), + 100, + ); + let source = match source_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("source failed: {error}"), + }; + let request = kb_pipeline::BackfillRequest { + role: "history_backfill".to_string(), + commitment: "confirmed".to_string(), + source, + page_size: 100, + max_pages: 1, + max_concurrent_requests: 1, + max_retries: 0, + }; + assert!(request.validate().is_ok()); + assert_eq!(request.filter_code(), "program_latest"); + } + + #[test] + fn newer_history_rejects_missing_anchor() { + let source_result = super::address_source( + kb_pipeline::BackfillAddressKind::Program, + std::option::Option::Some("11111111111111111111111111111111"), + std::option::Option::None, + std::option::Option::Some("after"), + 100, + ); + assert!(source_result.is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_config.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_config.rs new file mode 100644 index 0000000..1ce1c02 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_config.rs @@ -0,0 +1,27 @@ +// file: kb_app_demo/src/demo_config.rs +// version: 8 + +//! Configuration demo commands. + +use ts_rs::TS; // rust-rules: derive-import + +/// Serializable payload shown by the configuration demo page. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_config/DemoConfigPayload.ts" +)] +pub(crate) struct DemoConfigPayload { + /// Path used to load the configuration file. + pub(crate) config_path: std::string::String, + /// Active profile name. + pub(crate) active_profile_name: std::string::String, + /// Active profile environment. + pub(crate) environment: std::string::String, + /// Entire parsed configuration. + pub(crate) app_config: kb_config::AppConfig, + /// Active profile configuration. + pub(crate) active_profile: kb_config::ProfileConfig, + /// Embedded JSON Schema text used during loading. + pub(crate) schema_json: std::string::String, +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_core_extraction.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_core_extraction.rs new file mode 100644 index 0000000..6989b5a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_core_extraction.rs @@ -0,0 +1,267 @@ +// file: kb_app_demo/src/demo_core_extraction.rs +// version: 9 + +//! Tauri commands and UI payloads for canonical transaction to core extraction. + +use tauri::Emitter; // rust-rules: trait-import +use ts_rs::TS; // rust-rules: derive-import + +/// Initial options shown by the core extraction demo. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_core_extraction/DemoCoreExtractionOptionsPayload.ts" +)] +pub(crate) struct DemoCoreExtractionOptionsPayload { + /// Current extractor implementation version. + pub(crate) processor_version: std::string::String, + /// Default maximum selected canonical transactions. + pub(crate) default_limit: u32, + /// Default maximum concurrent extractions. + pub(crate) default_max_concurrent_extractions: u32, + /// Whether one extraction campaign is currently running. + pub(crate) running: bool, +} + +/// UI request for one bounded core extraction campaign. +#[derive(Clone, Debug, serde::Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_core_extraction/DemoCoreExtractionRequest.ts" +)] +pub(crate) struct DemoCoreExtractionRequest { + /// Source mode: signatures, pending or slot_range. + pub(crate) mode: std::string::String, + /// Newline-separated signatures for exact selection. + pub(crate) signatures_text: std::option::Option, + /// Optional program id already present in core instructions. + pub(crate) program_id: std::option::Option, + /// Optional inclusive minimum slot. + #[ts(type = "number | null")] + pub(crate) min_slot: std::option::Option, + /// Optional inclusive maximum slot. + #[ts(type = "number | null")] + pub(crate) max_slot: std::option::Option, + /// Maximum selected canonical transactions. + pub(crate) limit: u32, + /// Maximum concurrent extraction operations. + pub(crate) max_concurrent_extractions: u32, + /// Forces replacement of already current core rows. + pub(crate) force_replay: bool, +} + +/// One progress event emitted to the core extraction window. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_core_extraction/DemoCoreExtractionProgressPayload.ts" +)] +pub(crate) struct DemoCoreExtractionProgressPayload { + /// RFC 3339 timestamp. + pub(crate) timestamp: std::string::String, + /// Stable severity code. + pub(crate) level: std::string::String, + /// Human-readable message. + pub(crate) message: std::string::String, + /// Number of terminal candidates. + #[ts(type = "number")] + pub(crate) completed: u64, + /// Total selected candidates. + #[ts(type = "number")] + pub(crate) total: u64, +} + +/// Final UI-safe summary for one extraction campaign. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_core_extraction/DemoCoreExtractionSummaryPayload.ts" +)] +pub(crate) struct DemoCoreExtractionSummaryPayload { + /// Current extractor implementation version. + pub(crate) processor_version: std::string::String, + /// Number of selected canonical transactions. + #[ts(type = "number")] + pub(crate) selected: u64, + /// Number admitted to the bounded execution queue. + #[ts(type = "number")] + pub(crate) started: u64, + /// Number reaching a terminal result. + #[ts(type = "number")] + pub(crate) completed: u64, + /// Number skipped by version/hash idempotence. + #[ts(type = "number")] + pub(crate) skipped: u64, + /// Number extracted and committed. + #[ts(type = "number")] + pub(crate) extracted: u64, + /// Number failed. + #[ts(type = "number")] + pub(crate) failed: u64, + /// Number admitted but cancelled before a terminal result. + #[ts(type = "number")] + pub(crate) cancelled_candidates: u64, + /// Number selected but not started after cancellation. + #[ts(type = "number")] + pub(crate) not_started: u64, + /// Whether the campaign was cancelled. + pub(crate) cancelled: bool, + /// Campaign start timestamp. + pub(crate) started_at: std::string::String, + /// Campaign finish timestamp. + pub(crate) finished_at: std::string::String, +} + +pub(crate) struct DemoCoreExtractionObserver<'a> { + pub(crate) app_handle: tauri::AppHandle, + pub(crate) cancel_requested: &'a std::sync::atomic::AtomicBool, +} + +impl kb_pipeline::CoreExtractionObserver for crate::DemoCoreExtractionObserver<'_> { + fn on_progress(&self, event: &kb_pipeline::CoreExtractionProgressEvent) { + let payload = crate::DemoCoreExtractionProgressPayload { + timestamp: event.timestamp.clone(), + level: event.level.code().to_string(), + message: event.message.clone(), + completed: event.completed, + total: event.total, + }; + let emit_result = self.app_handle.emit_to( + "demo_core_extraction", + "demo-core-extraction-progress", + payload, + ); + if let std::result::Result::Err(error) = emit_result { + tracing::warn!( + target: crate::TRACING_TARGET, + "cannot emit core extraction progress: {error}" + ); + } + } + + fn is_cancelled(&self) -> bool { + return self.cancel_requested.load(std::sync::atomic::Ordering::Relaxed); + } +} + +pub(crate) struct DemoCoreExtractionRunGuard<'a> { + pub(crate) running: &'a std::sync::atomic::AtomicBool, +} + +impl std::ops::Drop for crate::DemoCoreExtractionRunGuard<'_> { + fn drop(&mut self) { + self.running.store(false, std::sync::atomic::Ordering::Release); + } +} + +pub(crate) fn build_demo_core_extraction_pipeline_request( + request: crate::DemoCoreExtractionRequest, +) -> std::result::Result { + let source_result = match request.mode.trim() { + "signatures" => { + let signatures = split_signatures(request.signatures_text.as_deref()); + std::result::Result::Ok(kb_pipeline::CoreExtractionSource::Signatures(signatures)) + }, + "pending" => std::result::Result::Ok(kb_pipeline::CoreExtractionSource::Pending), + "program_id" => { + let program_id = match request.program_id { + std::option::Option::Some(value) if !value.trim().is_empty() => value, + _ => { + return std::result::Result::Err( + "program id is required for program extraction".to_string(), + ); + }, + }; + std::result::Result::Ok(kb_pipeline::CoreExtractionSource::ProgramId { program_id }) + }, + "slot_range" => { + let min_slot = match request.min_slot { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "minimum slot is required for slot range extraction".to_string(), + ); + }, + }; + let max_slot = match request.max_slot { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "maximum slot is required for slot range extraction".to_string(), + ); + }, + }; + std::result::Result::Ok(kb_pipeline::CoreExtractionSource::SlotRange { + min_slot, + max_slot, + }) + }, + _ => std::result::Result::Err("unsupported core extraction mode".to_string()), + }; + let source = match source_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let pipeline_request = kb_pipeline::CoreExtractionRequest { + source, + limit: request.limit, + max_concurrent_extractions: request.max_concurrent_extractions, + force_replay: request.force_replay, + }; + let validation_result = pipeline_request.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error.to_string()); + } + return std::result::Result::Ok(pipeline_request); +} + +fn split_signatures(text: std::option::Option<&str>) -> std::vec::Vec { + let source = match text { + std::option::Option::Some(value) => value, + std::option::Option::None => "", + }; + let mut unique = std::collections::BTreeSet::::new(); + let mut output = std::vec::Vec::new(); + for line in source.lines() { + let signature = line.trim(); + if signature.is_empty() { + continue; + } + if unique.insert(signature.to_string()) { + output.push(signature.to_string()); + } + } + return output; +} + +pub(crate) fn demo_core_extraction_summary_payload( + summary: kb_pipeline::CoreExtractionSummary, +) -> crate::DemoCoreExtractionSummaryPayload { + return crate::DemoCoreExtractionSummaryPayload { + processor_version: summary.processor_version, + selected: summary.selected, + started: summary.started, + completed: summary.completed, + skipped: summary.skipped, + extracted: summary.extracted, + failed: summary.failed, + cancelled_candidates: summary.cancelled_candidates, + not_started: summary.not_started, + cancelled: summary.cancelled, + started_at: summary.started_at, + finished_at: summary.finished_at, + }; +} + +#[cfg(test)] +mod tests { + #[test] + fn signature_source_splits_deduplicates_and_ignores_empty_rows() { + let values = super::split_signatures(std::option::Option::Some("alpha\n\n beta \nalpha\n")); + assert_eq!(values, std::vec!["alpha".to_string(), "beta".to_string()]); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_decode_replay.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_decode_replay.rs new file mode 100644 index 0000000..81fef3e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_decode_replay.rs @@ -0,0 +1,953 @@ +// file: kb_app_demo/src/demo_decode_replay.rs +// version: 24 + +//! Tauri commands and UI payloads for contextual instruction decode replay. + +use tauri::Emitter; // rust-rules: trait-import +use ts_rs::TS; // rust-rules: derive-import + +/// One decoder selectable by the decode replay demo. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_decode_replay/DemoDecodeReplayDecoderOption.ts" +)] +pub(crate) struct DemoDecodeReplayDecoderOption { + /// Stable decoder name. + pub(crate) name: std::string::String, + /// Stable decoder version. + pub(crate) version: std::string::String, + /// Exact supported program identifiers. + pub(crate) program_ids: std::vec::Vec, +} + +/// Initial options shown by the contextual decode replay demo. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_decode_replay/DemoDecodeReplayOptionsPayload.ts" +)] +pub(crate) struct DemoDecodeReplayOptionsPayload { + /// Common orchestration version. + pub(crate) pipeline_version: std::string::String, + /// Available contextual decoders. + pub(crate) decoders: std::vec::Vec, + /// Stable names of materializers currently registered by the demo. + pub(crate) materializer_names: std::vec::Vec, + /// Default bounded selection limit. + pub(crate) default_limit: u32, + /// Default maximum concurrent contextual inputs. + pub(crate) default_max_concurrent_inputs: u32, + /// Whether one replay campaign is running. + pub(crate) running: bool, +} + +/// UI request for one bounded contextual decode replay campaign. +#[derive(Clone, Debug, serde::Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_decode_replay/DemoDecodeReplayRequest.ts" +)] +pub(crate) struct DemoDecodeReplayRequest { + /// Newline-separated exact transaction signatures. + pub(crate) signatures_text: std::option::Option, + /// Optional exact program identifier. + pub(crate) program_id: std::option::Option, + /// Instruction processing state code, actionable or incomplete_signatures. + pub(crate) instruction_state: std::string::String, + /// Newline-separated exact stable instruction paths. + pub(crate) instruction_paths_text: std::option::Option, + /// Explicit selected decoder names. + pub(crate) decoder_names: std::vec::Vec, + /// Maximum selected contextual inputs. + pub(crate) limit: u32, + /// Maximum concurrent contextual inputs. + pub(crate) max_concurrent_inputs: u32, + /// Whether every compatible decoder must run. + pub(crate) all_compatible: bool, + /// Replaces only processor-owned outputs for the same version and input. + pub(crate) force_replay: bool, + /// Explicitly authorizes a bounded force replay without exact signatures. + pub(crate) force_replay_all_matching: bool, + /// Runs compatible materializers after decoded event persistence. + pub(crate) materialize_after_decode: bool, +} + +/// One progress event emitted to the decode replay window. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_decode_replay/DemoDecodeReplayProgressPayload.ts" +)] +pub(crate) struct DemoDecodeReplayProgressPayload { + /// Stable process-local campaign identifier. + pub(crate) campaign_id: std::string::String, + /// RFC 3339 timestamp. + pub(crate) timestamp: std::string::String, + /// Stable severity code. + pub(crate) level: std::string::String, + /// Human-readable message. + pub(crate) message: std::string::String, + /// Number of terminal contextual inputs. + #[ts(type = "number")] + pub(crate) completed: u64, + /// Total selected contextual inputs. + #[ts(type = "number")] + pub(crate) total: u64, +} + +/// One processor counter row returned to the UI. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_decode_replay/DemoDecodeProcessorSummaryPayload.ts" +)] +pub(crate) struct DemoDecodeProcessorSummaryPayload { + /// Stable processor name. + pub(crate) processor_name: std::string::String, + /// Stable processor version. + pub(crate) processor_version: std::string::String, + /// Compatible dispatch count. + #[ts(type = "number")] + pub(crate) dispatched: u64, + /// Version and hash skip count. + #[ts(type = "number")] + pub(crate) skipped: u64, + /// Decoded input count. + #[ts(type = "number")] + pub(crate) decoded: u64, + /// Ignored input count. + #[ts(type = "number")] + pub(crate) ignored: u64, + /// Unsupported input count. + #[ts(type = "number")] + pub(crate) unsupported: u64, + /// Failed input count. + #[ts(type = "number")] + pub(crate) failed: u64, + /// Materialized output count. + #[ts(type = "number")] + pub(crate) materialized_outputs: u64, + /// Materialization refusal count. + #[ts(type = "number")] + pub(crate) materialization_refused: u64, +} + +/// Final UI-safe summary for one contextual decode replay campaign. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_decode_replay/DemoDecodeReplaySummaryPayload.ts" +)] +pub(crate) struct DemoDecodeReplaySummaryPayload { + /// Stable process-local campaign identifier. + pub(crate) campaign_id: std::string::String, + /// Common pipeline version. + pub(crate) pipeline_version: std::string::String, + /// Number of selected contextual inputs. + #[ts(type = "number")] + pub(crate) selected: u64, + /// Number admitted to execution. + #[ts(type = "number")] + pub(crate) started: u64, + /// Number reaching a terminal state. + #[ts(type = "number")] + pub(crate) completed: u64, + /// Number with no compatible enabled decoder. + #[ts(type = "number")] + pub(crate) unmatched: u64, + /// Number never started after cancellation. + #[ts(type = "number")] + pub(crate) not_started: u64, + /// Number of failed contextual inputs. + #[ts(type = "number")] + pub(crate) failed_inputs: u64, + /// Whether cancellation was observed. + pub(crate) cancelled: bool, + /// Per-processor counters. + pub(crate) processors: std::vec::Vec, + /// Campaign start timestamp. + pub(crate) started_at: std::string::String, + /// Campaign finish timestamp. + pub(crate) finished_at: std::string::String, +} + +/// One aggregated coverage row returned to the UI. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_decode_replay/DemoDecodeCoverageSummaryPayload.ts" +)] +pub(crate) struct DemoDecodeCoverageSummaryPayload { + /// Stable processor name. + pub(crate) processor_name: std::string::String, + /// Stable processor version. + pub(crate) processor_version: std::string::String, + /// Exact program identifier. + pub(crate) program_id: std::string::String, + /// Optional stable surface code. + pub(crate) surface_code: std::option::Option, + /// Stable entry classifier. + pub(crate) entry_code: std::string::String, + /// Declared count. + #[ts(type = "number")] + pub(crate) declared_count: i64, + /// Observed count. + #[ts(type = "number")] + pub(crate) observed_count: i64, + /// Recognized count. + #[ts(type = "number")] + pub(crate) recognized_count: i64, + /// Decoded observation count. + #[ts(type = "number")] + pub(crate) decoded_count: i64, + /// Materialized output count. + #[ts(type = "number")] + pub(crate) materialized_count: i64, + /// Error count. + #[ts(type = "number")] + pub(crate) error_count: i64, + /// Unknown or unsupported observation count. + #[ts(type = "number")] + pub(crate) unknown_count: i64, + /// Successful source transaction count. + #[ts(type = "number")] + pub(crate) successful_transaction_count: i64, + /// Failed source transaction count. + #[ts(type = "number")] + pub(crate) failed_transaction_count: i64, +} + +/// Read-only decode, ledger and coverage diagnostics. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_decode_replay/DemoDecodeDiagnosticsPayload.ts" +)] +pub(crate) struct DemoDecodeDiagnosticsPayload { + /// Decode, materialization and ledger table diagnostics. + pub(crate) tables: std::vec::Vec, + /// Aggregated declared and observed coverage. + pub(crate) coverage: std::vec::Vec, +} + +/// Bounded read request for committed transaction annotations. +#[derive(Clone, Debug, serde::Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_decode_replay/DemoTransactionAnnotationRequest.ts" +)] +pub(crate) struct DemoTransactionAnnotationRequest { + /// Optional partial transaction signature. + pub(crate) signature_contains: std::option::Option, + /// Maximum returned rows. + pub(crate) limit: u32, +} + +/// UI-safe committed transaction annotation row. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_decode_replay/DemoTransactionAnnotationRow.ts" +)] +pub(crate) struct DemoTransactionAnnotationRow { + /// Materializer processor version. + pub(crate) processor_version: std::string::String, + /// Source decoder and version. + pub(crate) decoder: std::string::String, + /// Transaction signature. + pub(crate) signature: std::string::String, + /// Decimal slot rendered as text to preserve JSON precision. + pub(crate) slot: std::string::String, + /// Stable outer or inner instruction path. + pub(crate) instruction_path: std::string::String, + /// Exact Memo generation. + pub(crate) generation: std::string::String, + /// Exact Memo Program ID. + pub(crate) program_id: std::string::String, + /// Complete bounded UTF-8 Memo text. + pub(crate) text: std::string::String, + /// Exact payload byte length. + pub(crate) payload_length_bytes: u32, + /// Canonical payload SHA-256. + pub(crate) payload_sha256: std::string::String, + /// Runtime-verified ordered signer keys. + pub(crate) verified_signers: std::vec::Vec, + /// Stable materializer idempotence key. + pub(crate) idempotence_key: std::string::String, + /// Database creation timestamp. + pub(crate) created_at: std::string::String, + /// Database replacement timestamp. + pub(crate) updated_at: std::string::String, +} + +pub(crate) struct DemoDecodeReplayObserver<'a> { + pub(crate) app_handle: tauri::AppHandle, + pub(crate) campaign_id: std::string::String, + pub(crate) cancel_requested: &'a std::sync::atomic::AtomicBool, +} + +impl kb_pipeline::DecodeReplayObserver for crate::DemoDecodeReplayObserver<'_> { + fn on_progress(&self, event: &kb_pipeline::DecodeReplayProgressEvent) { + let payload = DemoDecodeReplayProgressPayload { + campaign_id: self.campaign_id.clone(), + timestamp: event.timestamp.clone(), + level: event.level.code().to_string(), + message: event.message.clone(), + completed: event.completed, + total: event.total, + }; + let emit_result = + self.app_handle + .emit_to("demo_decode_replay", "demo-decode-replay-progress", payload); + if let std::result::Result::Err(error) = emit_result { + tracing::warn!(target: crate::TRACING_TARGET, action = "emit_progress", campaign_id = %self.campaign_id, error = %error, "cannot emit decode replay progress"); + } + } + + fn is_cancelled(&self) -> bool { + return self.cancel_requested.load(std::sync::atomic::Ordering::Relaxed); + } +} + +pub(crate) struct DemoDecodeReplayRunGuard<'a> { + pub(crate) running: &'a std::sync::atomic::AtomicBool, + pub(crate) campaign_id: &'a std::sync::Mutex>, +} + +impl std::ops::Drop for crate::DemoDecodeReplayRunGuard<'_> { + fn drop(&mut self) { + self.running.store(false, std::sync::atomic::Ordering::Release); + let lock_result = self.campaign_id.lock(); + if let std::result::Result::Ok(mut active_campaign_id) = lock_result { + *active_campaign_id = std::option::Option::None; + } + } +} + +pub(crate) fn register_active_campaign( + campaign_slot: &std::sync::Mutex>, + campaign_id: &str, +) -> std::result::Result<(), std::string::String> { + let lock_result = campaign_slot.lock(); + let mut active_campaign_id = match lock_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err( + "cannot register active contextual decode campaign".to_string(), + ); + }, + }; + *active_campaign_id = std::option::Option::Some(campaign_id.to_string()); + return std::result::Result::Ok(()); +} + +pub(crate) fn available_materializers() +-> std::vec::Vec> { + return std::vec![ + std::sync::Arc::new(kb_materializer_admin::AdminMaterializer), + std::sync::Arc::new(kb_materializer_compliance_audit::ComplianceAuditMaterializer), + std::sync::Arc::new(kb_materializer_fees::FeesMaterializer), + std::sync::Arc::new(kb_materializer_lifecycle::LifecycleMaterializer), + std::sync::Arc::new(kb_materializer_staking::StakingMaterializer), + std::sync::Arc::new(kb_materializer_token_accounts::TokenAccountsMaterializer), + std::sync::Arc::new(kb_materializer_risk::RiskMaterializer), + std::sync::Arc::new( + kb_materializer_transaction_annotations::TransactionAnnotationMaterializer, + ), + ]; +} + +pub(crate) fn available_decoders() +-> std::vec::Vec> { + return std::vec![ + std::sync::Arc::new(kb_decoder_solana_core::SolanaCoreDecoder), + std::sync::Arc::new( + kb_decoder_spl_associated_token_account::SplAssociatedTokenAccountDecoder, + ), + std::sync::Arc::new(kb_decoder_spl_elgamal_registry::SplElgamalRegistryDecoder), + std::sync::Arc::new(kb_decoder_spl_memo::SplMemoDecoder), + std::sync::Arc::new(kb_decoder_spl_token::SplTokenDecoder), + std::sync::Arc::new(kb_decoder_spl_token_2022::SplToken2022Decoder), + ]; +} + +pub(crate) fn build_demo_decode_replay_pipeline_request( + request: crate::DemoDecodeReplayRequest, + campaign_id: std::string::String, +) -> std::result::Result { + let states_result = processing_states(request.instruction_state.as_str()); + let states = match states_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let signatures = split_lines(request.signatures_text.as_deref()); + let instruction_paths = split_lines(request.instruction_paths_text.as_deref()); + let incomplete_signatures = request.instruction_state.trim() == "incomplete_signatures"; + let program_ids = match request.program_id { + std::option::Option::Some(value) if !value.trim().is_empty() => { + std::vec![value.trim().to_string()] + }, + _ => std::vec::Vec::new(), + }; + let selection_result = kb_store_core::DecodeSelectionFilter::new( + signatures, + states, + std::option::Option::None, + std::option::Option::None, + program_ids, + instruction_paths, + incomplete_signatures, + request.limit, + ); + let selection = match selection_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let dispatch_policy = if request.all_compatible { + kb_pipeline::DecodeDispatchPolicy::AllCompatible + } else { + kb_pipeline::DecodeDispatchPolicy::HighestPriority + }; + let pipeline_request = kb_pipeline::DecodeReplayRequest { + campaign_id, + selection, + decoder_names: request.decoder_names, + dispatch_policy, + max_concurrent_inputs: request.max_concurrent_inputs, + force_replay: request.force_replay, + force_replay_all_matching: request.force_replay_all_matching, + materialize_after_decode: request.materialize_after_decode, + }; + let validation_result = pipeline_request.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error.to_string()); + } + tracing::debug!( + target: crate::TRACING_TARGET, + action = "build_request", + campaign_id = %pipeline_request.campaign_id, + signature_count = pipeline_request.selection.signatures.len(), + signature_sample = ?text_sample(pipeline_request.selection.signatures.as_slice(), 5), + processing_states = ?pipeline_request.selection.processing_states, + program_ids = ?pipeline_request.selection.program_ids, + instruction_paths = ?pipeline_request.selection.instruction_paths, + incomplete_signatures = pipeline_request.selection.incomplete_signatures, + limit = pipeline_request.selection.limit, + decoder_names = ?pipeline_request.decoder_names, + dispatch_policy = ?pipeline_request.dispatch_policy, + max_concurrent_inputs = pipeline_request.max_concurrent_inputs, + force_replay = pipeline_request.force_replay, + force_replay_all_matching = pipeline_request.force_replay_all_matching, + materialize_after_decode = pipeline_request.materialize_after_decode, + "built normalized contextual decode replay request" + ); + return std::result::Result::Ok(pipeline_request); +} + +pub(crate) fn optional_line_count(value: &std::option::Option) -> usize { + return match value { + std::option::Option::Some(text) => { + text.lines().filter(|line| return !line.trim().is_empty()).count() + }, + std::option::Option::None => 0, + }; +} + +pub(crate) fn text_sample(values: &[std::string::String], limit: usize) -> std::vec::Vec<&str> { + return values.iter().take(limit).map(std::string::String::as_str).collect(); +} + +fn processing_states( + value: &str, +) -> std::result::Result< + std::vec::Vec, + std::string::String, +> { + return match value.trim() { + "incomplete_signatures" | "actionable" => std::result::Result::Ok(std::vec![ + kb_store_core::CoreInstructionProcessingState::Pending, + kb_store_core::CoreInstructionProcessingState::Failed, + kb_store_core::CoreInstructionProcessingState::ReplayRequested, + ]), + "pending" => std::result::Result::Ok(std::vec![ + kb_store_core::CoreInstructionProcessingState::Pending + ]), + "failed" => std::result::Result::Ok(std::vec![ + kb_store_core::CoreInstructionProcessingState::Failed + ]), + "replay_requested" => std::result::Result::Ok(std::vec![ + kb_store_core::CoreInstructionProcessingState::ReplayRequested + ]), + "decoded" => std::result::Result::Ok(std::vec![ + kb_store_core::CoreInstructionProcessingState::Decoded + ]), + "ignored" => std::result::Result::Ok(std::vec![ + kb_store_core::CoreInstructionProcessingState::Ignored + ]), + "materialized" => std::result::Result::Ok(std::vec![ + kb_store_core::CoreInstructionProcessingState::Materialized + ]), + _ => std::result::Result::Err("unsupported instruction processing state".to_string()), + }; +} + +fn split_lines(text: std::option::Option<&str>) -> std::vec::Vec { + let source = match text { + std::option::Option::Some(value) => value, + std::option::Option::None => "", + }; + let mut unique = std::collections::BTreeSet::::new(); + let mut output = std::vec::Vec::new(); + for line in source.lines() { + let value = line.trim(); + if value.is_empty() { + continue; + } + if unique.insert(value.to_string()) { + output.push(value.to_string()); + } + } + return output; +} + +pub(crate) fn demo_decode_replay_summary_payload( + summary: kb_pipeline::DecodeReplaySummary, +) -> crate::DemoDecodeReplaySummaryPayload { + return crate::DemoDecodeReplaySummaryPayload { + campaign_id: summary.campaign_id, + pipeline_version: summary.pipeline_version, + selected: summary.selected, + started: summary.started, + completed: summary.completed, + unmatched: summary.unmatched, + not_started: summary.not_started, + failed_inputs: summary.failed_inputs, + cancelled: summary.cancelled, + processors: summary + .processors + .into_iter() + .map(|processor| { + return crate::DemoDecodeProcessorSummaryPayload { + processor_name: processor.processor_name, + processor_version: processor.processor_version, + dispatched: processor.dispatched, + skipped: processor.skipped, + decoded: processor.decoded, + ignored: processor.ignored, + unsupported: processor.unsupported, + failed: processor.failed, + materialized_outputs: processor.materialized_outputs, + materialization_refused: processor.materialization_refused, + }; + }) + .collect(), + started_at: summary.started_at, + finished_at: summary.finished_at, + }; +} + +pub(crate) fn coverage_payload( + value: kb_store_core::DecodeCoverageSummaryRow, +) -> crate::DemoDecodeCoverageSummaryPayload { + return crate::DemoDecodeCoverageSummaryPayload { + processor_name: value.processor_name, + processor_version: value.processor_version, + program_id: value.program_id, + surface_code: value.surface_code, + entry_code: value.entry_code, + declared_count: value.declared_count, + observed_count: value.observed_count, + recognized_count: value.recognized_count, + decoded_count: value.decoded_count, + materialized_count: value.materialized_count, + error_count: value.error_count, + unknown_count: value.unknown_count, + successful_transaction_count: value.successful_transaction_count, + failed_transaction_count: value.failed_transaction_count, + }; +} + +pub(crate) fn annotation_payload( + row: kb_store_core::MaterializedEventQueryRow, +) -> std::result::Result { + if row.processor_name != "transaction_annotations" + || row.materialized_family != "transaction_annotation" + { + return std::result::Result::Err( + "materialized row is not a transaction annotation".to_string(), + ); + } + let payload = &row.payload_json; + let byte_length = match payload.get("payloadLengthBytes").and_then(serde_json::Value::as_u64) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "transaction annotation payloadLengthBytes is absent".to_string(), + ); + }, + }; + let byte_length_result = u32::try_from(byte_length); + let payload_length_bytes = match byte_length_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "transaction annotation payloadLengthBytes is not UI-safe: {error}" + )); + }, + }; + let signers = match payload.get("signers") { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "transaction annotation signers are absent".to_string(), + ); + }, + }; + let verified_values = match signers.get("verified").and_then(serde_json::Value::as_array) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "transaction annotation verified signers are invalid".to_string(), + ); + }, + }; + let mut verified_signers = std::vec::Vec::with_capacity(verified_values.len()); + for value in verified_values { + match value.as_str() { + std::option::Option::Some(signer) if !signer.trim().is_empty() => { + verified_signers.push(signer.to_string()); + }, + _ => { + return std::result::Result::Err( + "transaction annotation verified signer is invalid".to_string(), + ); + }, + } + } + let instruction_path = match required_annotation_text(payload, "instructionPath") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let generation = match required_annotation_text(payload, "generation") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let program_id = match required_annotation_text(payload, "programId") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let text = match required_annotation_text(payload, "text") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let payload_sha256 = match required_annotation_text(payload, "payloadSha256") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let idempotence_key = match required_annotation_text(payload, "idempotenceKey") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(crate::DemoTransactionAnnotationRow { + processor_version: row.processor_version, + decoder: format!("{}@{}", row.source_decoder_name, row.source_decoder_version), + signature: row.signature, + slot: row.slot.to_string(), + instruction_path, + generation, + program_id, + text, + payload_length_bytes, + payload_sha256, + verified_signers, + idempotence_key, + created_at: row.created_at, + updated_at: row.updated_at, + }); +} + +fn required_annotation_text( + payload: &serde_json::Value, + field: &str, +) -> std::result::Result { + return match payload.get(field).and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) => std::result::Result::Ok(value.to_string()), + std::option::Option::None => { + std::result::Result::Err(format!("transaction annotation {field} is absent")) + }, + }; +} + +#[cfg(test)] +mod tests { + fn ata_observation(entry: &str) -> kb_decoder_api::DecodedObservation { + return kb_decoder_api::DecodedObservation { + event_key: format!("ata:{entry}:0"), + event: kb_model::DecodedProtocolEvent { + signature: kb_model::Signature("signature".to_string()), + slot: kb_model::Slot(1), + instruction_path: kb_model::InstructionPath("0".to_string()), + program_id: kb_model::ProgramId( + kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID.to_string(), + ), + protocol_code: kb_model::ProtocolCode("spl_associated_token_account".to_string()), + surface_code: kb_model::SurfaceCode("spl_associated_token_account".to_string()), + event_code: kb_model::EventCode(format!("spl_associated_token_account.{entry}")), + event_name: kb_model::EventName(entry.to_string()), + event_family: kb_model::EventFamily::Lifecycle, + source_kind: kb_model::EventSourceKind::Instruction, + confidence: kb_model::DecoderConfidence::ManualExact, + }, + payload_json: serde_json::json!({}), + transaction_failed: false, + transaction_error: std::option::Option::None, + observation_committed: true, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::Manual, + confidence: kb_model::DecoderConfidence::ManualExact, + evidence: std::vec!["fixture".to_string()], + }, + }; + } + + #[test] + fn native_memo_token_2022_elgamal_classic_token_and_ata_decoders_are_registered() { + let decoders = crate::available_decoders(); + assert_eq!(decoders.len(), 6); + let names = decoders + .iter() + .map(|decoder| return decoder.identity().name) + .collect::>(); + assert_eq!( + names, + std::vec![ + "solana_native_classifier".to_string(), + "spl_associated_token_account".to_string(), + "spl_elgamal_registry".to_string(), + "spl_memo".to_string(), + "spl_token".to_string(), + "spl_token_2022".to_string(), + ] + ); + } + + #[test] + fn runtime_materializer_registry_is_complete_for_current_instructional_surfaces() { + let materializers = crate::available_materializers(); + assert_eq!(materializers.len(), 8); + let names = materializers + .iter() + .map(|materializer| return materializer.identity().name) + .collect::>(); + assert_eq!( + names, + std::vec![ + "solana_native_admin".to_string(), + "solana_native_compliance_audit".to_string(), + "fees".to_string(), + "solana_native_lifecycle".to_string(), + "solana_native_staking".to_string(), + "spl_token_accounts".to_string(), + "spl_token_risk".to_string(), + "transaction_annotations".to_string(), + ] + ); + } + + #[test] + fn ata_materializer_ownership_is_exact_in_the_runtime_registry() { + for (entry, expected) in [ + ("create", std::vec!["spl_token_accounts".to_string()]), + ( + "recover_nested", + std::vec!["spl_token_accounts".to_string(), "spl_token_risk".to_string(),], + ), + ] { + let observation = ata_observation(entry); + let owners = crate::available_materializers() + .iter() + .filter(|materializer| return materializer.accepts_observation(&observation)) + .map(|materializer| return materializer.identity().name) + .collect::>(); + assert_eq!(owners, expected); + } + } + + #[test] + fn line_selection_is_trimmed_and_deduplicated() { + let values = super::split_lines(std::option::Option::Some(" a \n\na\nb\n")); + assert_eq!(values, std::vec!["a".to_string(), "b".to_string()]); + } + + fn request( + signatures_text: std::option::Option<&str>, + force_replay: bool, + force_replay_all_matching: bool, + materialize_after_decode: bool, + ) -> crate::DemoDecodeReplayRequest { + return crate::DemoDecodeReplayRequest { + signatures_text: signatures_text.map(|value| return value.to_string()), + program_id: std::option::Option::None, + instruction_state: "actionable".to_string(), + instruction_paths_text: std::option::Option::None, + decoder_names: std::vec!["solana_native_classifier".to_string()], + limit: 10, + max_concurrent_inputs: 2, + all_compatible: false, + force_replay, + force_replay_all_matching, + materialize_after_decode, + }; + } + + #[test] + fn force_replay_requires_signatures_or_all_matching_authorization() { + let result = crate::build_demo_decode_replay_pipeline_request( + request(std::option::Option::None, true, false, false), + "decode-test".to_string(), + ); + assert!(result.is_err()); + } + + #[test] + fn force_replay_all_matching_is_forwarded() { + let result = crate::build_demo_decode_replay_pipeline_request( + request(std::option::Option::None, true, true, false), + "decode-test".to_string(), + ); + let request = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("request mapping failed: {error}"), + }; + assert!(request.force_replay_all_matching); + assert!(request.selection.signatures.is_empty()); + } + + #[test] + fn run_guard_clears_active_campaign_state() { + let running = std::sync::atomic::AtomicBool::new(true); + let campaign_id = + std::sync::Mutex::new(std::option::Option::Some("decode-test".to_string())); + { + let _guard = crate::DemoDecodeReplayRunGuard { + running: &running, + campaign_id: &campaign_id, + }; + } + assert!(!running.load(std::sync::atomic::Ordering::Acquire)); + let lock_result = campaign_id.lock(); + let active_campaign_id = match lock_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => panic!("campaign id mutex must not be poisoned"), + }; + assert!(active_campaign_id.is_none()); + } + + #[test] + fn register_active_campaign_sets_slot_without_exposing_guard() { + let campaign_id = std::sync::Mutex::new(std::option::Option::None); + let result = crate::register_active_campaign(&campaign_id, "decode-test"); + assert!(result.is_ok()); + let lock_result = campaign_id.lock(); + let active_campaign_id = match lock_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => panic!("campaign id mutex must not be poisoned"), + }; + assert_eq!(active_campaign_id.as_deref(), std::option::Option::Some("decode-test")); + } + + #[test] + fn incomplete_signature_state_uses_actionable_states_and_expansion() { + let mut request = request(std::option::Option::None, false, false, true); + request.instruction_state = "incomplete_signatures".to_string(); + let result = crate::build_demo_decode_replay_pipeline_request( + request, + "decode-incomplete".to_string(), + ); + let pipeline_request = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("request mapping failed: {error}"), + }; + assert!(pipeline_request.selection.incomplete_signatures); + assert_eq!(pipeline_request.selection.processing_states.len(), 3); + assert!(!pipeline_request.force_replay); + assert!(pipeline_request.materialize_after_decode); + } + + #[test] + fn actionable_state_contains_pending_failed_and_replay_requested() { + let result = super::processing_states("actionable"); + let states = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("state mapping failed: {error}"), + }; + assert_eq!(states.len(), 3); + } + + #[test] + fn committed_annotation_row_is_mapped_to_ui_safe_contract() { + let row = kb_store_core::MaterializedEventQueryRow { + processor_name: "transaction_annotations".to_string(), + processor_version: "0.4.3".to_string(), + input_key: "input".to_string(), + output_key: "output".to_string(), + source_event_key: "memo:0".to_string(), + source_decoder_name: "spl_memo".to_string(), + source_decoder_version: "0.4.3".to_string(), + signature: "signature".to_string(), + slot: u64::MAX, + materialized_family: "transaction_annotation".to_string(), + payload_json: serde_json::json!({ + "instructionPath": "1/0", + "generation": "v4", + "programId": kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, + "text": "annotation", + "payloadLengthBytes": 10, + "payloadSha256": "11", + "signers": {"verified": ["signer"]}, + "idempotenceKey": "stable" + }), + created_at: "created".to_string(), + updated_at: "updated".to_string(), + }; + let result = crate::annotation_payload(row); + let mapped = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("annotation mapping failed: {error}"), + }; + assert_eq!(mapped.slot, u64::MAX.to_string()); + assert_eq!(mapped.text, "annotation"); + assert_eq!(mapped.verified_signers, std::vec!["signer".to_string()]); + } + + #[test] + fn malformed_annotation_payload_fails_closed() { + let row = kb_store_core::MaterializedEventQueryRow { + processor_name: "transaction_annotations".to_string(), + processor_version: "0.4.3".to_string(), + input_key: "input".to_string(), + output_key: "output".to_string(), + source_event_key: "memo:0".to_string(), + source_decoder_name: "spl_memo".to_string(), + source_decoder_version: "0.4.3".to_string(), + signature: "signature".to_string(), + slot: 1, + materialized_family: "transaction_annotation".to_string(), + payload_json: serde_json::json!({}), + created_at: "created".to_string(), + updated_at: "updated".to_string(), + }; + assert!(crate::annotation_payload(row).is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_execution_solana_core.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_execution_solana_core.rs new file mode 100644 index 0000000..fe4d51a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_execution_solana_core.rs @@ -0,0 +1,664 @@ +// file: kb_app_demo/src/demo_execution_solana_core.rs +// version: 5 + +//! Tauri adapter for bounded Solana Core execution on Devnet. + +use tauri::Emitter; // rust-rules: trait-import +use ts_rs::TS; // rust-rules: derive-import + +/// One Devnet execution profile exposed to the demo window. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionSolanaCoreProfileOption.ts" +)] +pub(crate) struct DemoExecutionSolanaCoreProfileOption { + /// Stable profile name. + pub(crate) name: std::string::String, + /// Non-secret temporary wallet alias. + pub(crate) wallet_alias: std::string::String, + /// Maximum Devnet spend allowed by the profile. + #[ts(type = "number")] + pub(crate) max_spend_lamports: u64, + /// Maximum faucet request allowed by the profile. + #[ts(type = "number")] + pub(crate) max_airdrop_lamports: u64, + /// Whether signed Devnet submission is enabled. + pub(crate) send_enabled: bool, + /// Whether explicit operator confirmation is required. + pub(crate) require_operator_confirmation: bool, +} + +/// Initial options shown by the Solana Core execution demo. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionSolanaCoreOptionsPayload.ts" +)] +pub(crate) struct DemoExecutionSolanaCoreOptionsPayload { + /// Devnet profiles compatible with the execution laboratory. + pub(crate) profiles: std::vec::Vec, + /// Suggested profile name. + pub(crate) default_profile_name: std::option::Option, + /// Default transfer amount suitable for a new zero-data account on Devnet. + #[ts(type = "number")] + pub(crate) default_transfer_lamports: u64, + /// Whether one execution is currently running. + pub(crate) running: bool, +} + +/// Request sent by the execution demo. +#[derive(Clone, Debug, serde::Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionSolanaCoreRequest.ts" +)] +pub(crate) struct DemoExecutionSolanaCoreRequest { + /// Selected Devnet profile. + pub(crate) profile_name: std::string::String, + /// Recipient public key. + pub(crate) recipient: std::string::String, + /// Lamports transferred by the System instruction. + #[ts(type = "number")] + pub(crate) lamports: u64, + /// Optional bounded faucet request when the source wallet is underfunded. + #[ts(type = "number")] + pub(crate) airdrop_lamports: u64, + /// Whether the transaction should be signed and submitted after simulation. + pub(crate) submit: bool, + /// Explicit operator confirmation for signed submission. + pub(crate) operator_confirmed: bool, + /// Whether post-execution core/decode outputs should be force-replayed. + pub(crate) force_post_validation_replay: bool, + /// Whether compatible materializers should run after decode replay. + pub(crate) materialize_after_decode: bool, +} + +/// Request sent by the Memo v4 Devnet execution panel. +#[derive(Clone, Debug, serde::Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionMemoRequest.ts" +)] +pub(crate) struct DemoExecutionMemoRequest { + /// Selected Devnet profile. + pub(crate) profile_name: std::string::String, + /// Exact UTF-8 Memo payload. + pub(crate) message: std::string::String, + /// Whether the wallet is supplied as a readonly Memo signer account. + pub(crate) include_wallet_as_memo_signer: bool, + /// Whether the transaction should be signed and submitted after simulation. + pub(crate) submit: bool, + /// Explicit operator confirmation for signed submission. + pub(crate) operator_confirmed: bool, + /// Whether post-execution core and first decode outputs should be force-replayed. + pub(crate) force_post_validation_replay: bool, +} + +/// Public key generated for a disposable Devnet recipient. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionSolanaCoreGeneratedRecipientPayload.ts" +)] +pub(crate) struct DemoExecutionSolanaCoreGeneratedRecipientPayload { + /// Base58 public key. The private key is not persisted or exposed. + pub(crate) public_key: std::string::String, +} + +/// Progress event emitted during execution and post-validation. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionSolanaCoreProgressPayload.ts" +)] +pub(crate) struct DemoExecutionSolanaCoreProgressPayload { + /// RFC 3339 timestamp. + pub(crate) timestamp: std::string::String, + /// Stable severity code. + pub(crate) level: std::string::String, + /// Stable stage code. + pub(crate) stage: std::string::String, + /// Human-readable message. + pub(crate) message: std::string::String, + /// Transaction signature when already available. + pub(crate) signature: std::option::Option, +} + +/// UI-safe result of one execution orchestration. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionSolanaCoreSummaryPayload.ts" +)] +pub(crate) struct DemoExecutionSolanaCoreSummaryPayload { + /// Profile used by the execution. + pub(crate) profile_name: std::string::String, + /// Classified cluster code. + pub(crate) cluster: std::string::String, + /// Genesis hash returned by the endpoint. + pub(crate) genesis_hash: std::string::String, + /// Source wallet public key. + pub(crate) wallet_public_key: std::string::String, + /// Recipient public key. + pub(crate) recipient: std::string::String, + /// Whether the recipient account existed before execution. + pub(crate) recipient_existed_before: bool, + /// Recipient balance before execution when present. + #[ts(type = "number | null")] + pub(crate) recipient_balance_before_lamports: std::option::Option, + /// Rent-exempt minimum required for a new zero-data account. + #[ts(type = "number")] + pub(crate) recipient_minimum_balance_lamports: u64, + /// Source balance before optional funding. + #[ts(type = "number")] + pub(crate) balance_before_lamports: u64, + /// Source balance after optional funding. + #[ts(type = "number")] + pub(crate) balance_after_funding_lamports: u64, + /// Estimated transaction fee. + #[ts(type = "number | null")] + pub(crate) fee_lamports: std::option::Option, + /// Whether exact simulation succeeded. + pub(crate) simulation_success: bool, + /// Runtime simulation error when present. + pub(crate) simulation_error: std::option::Option, + /// Runtime simulation logs. + pub(crate) simulation_logs: std::vec::Vec, + /// Faucet signature when funding was requested. + pub(crate) airdrop_signature: std::option::Option, + /// Submitted transaction signature. + pub(crate) transaction_signature: std::option::Option, + /// Terminal confirmation status. + pub(crate) confirmation_status: std::option::Option, + /// Canonical rows inserted after submission. + #[ts(type = "number | null")] + pub(crate) canonical_inserted: std::option::Option, + /// Core transactions extracted after submission. + #[ts(type = "number | null")] + pub(crate) core_extracted: std::option::Option, + /// Contextual decode inputs completed after submission. + #[ts(type = "number | null")] + pub(crate) decode_completed: std::option::Option, + /// Contextual decode input failures after submission. + #[ts(type = "number | null")] + pub(crate) decode_failed_inputs: std::option::Option, + /// Pretty JSON representation of the exact execution plan. + pub(crate) plan_json: std::string::String, + /// Pretty JSON representation of the simulation result. + pub(crate) simulation_json: std::string::String, + /// Pretty JSON representation of confirmation and post-validation diagnostics. + pub(crate) diagnostics_json: std::string::String, +} + +/// UI-safe result of one Memo v4 Devnet execution orchestration. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_execution_solana_core/DemoExecutionMemoSummaryPayload.ts" +)] +pub(crate) struct DemoExecutionMemoSummaryPayload { + /// Profile used by the execution. + pub(crate) profile_name: std::string::String, + /// Classified cluster code. + pub(crate) cluster: std::string::String, + /// Genesis hash returned by the endpoint. + pub(crate) genesis_hash: std::string::String, + /// Source wallet public key. + pub(crate) wallet_public_key: std::string::String, + /// Exact wallet balance rendered without JSON integer precision loss. + pub(crate) balance_lamports: std::string::String, + /// Estimated transaction fee rendered without JSON integer precision loss. + pub(crate) fee_lamports: std::option::Option, + /// Whether exact simulation succeeded. + pub(crate) simulation_success: bool, + /// Runtime simulation error when present. + pub(crate) simulation_error: std::option::Option, + /// Submitted transaction signature. + pub(crate) transaction_signature: std::option::Option, + /// Terminal confirmation status. + pub(crate) confirmation_status: std::option::Option, + /// Whether canonical hydration was validated. + pub(crate) canonical_inserted: bool, + /// Whether core extraction was validated. + pub(crate) core_extracted: bool, + /// Whether Memo decode replay was validated. + pub(crate) decode_replayed: bool, + /// Whether the committed transaction annotation was validated. + pub(crate) materialized: bool, + /// Number of exact annotation rows returned by the bounded query. + pub(crate) annotation_count: u32, + /// Whether the second replay produced no failures or new materialized output. + pub(crate) idempotence_validated: bool, + /// Pretty JSON representation of the exact execution plan. + pub(crate) plan_json: std::string::String, + /// Pretty JSON representation of the simulation result. + pub(crate) simulation_json: std::string::String, + /// Pretty JSON representation of annotations and post-validation diagnostics. + pub(crate) diagnostics_json: std::string::String, +} + +pub(crate) struct DemoExecutionSolanaCoreObserver<'a> { + pub(crate) app_handle: tauri::AppHandle, + pub(crate) cancel_requested: &'a std::sync::atomic::AtomicBool, +} + +impl crate::DemoExecutionSolanaCoreObserver<'_> { + fn emit( + &self, + timestamp: std::string::String, + level: &str, + stage: &str, + message: std::string::String, + signature: std::option::Option, + ) { + let payload = crate::DemoExecutionSolanaCoreProgressPayload { + timestamp, + level: level.to_string(), + stage: stage.to_string(), + message, + signature, + }; + let emit_result = self.app_handle.emit_to( + "demo_execution_solana_core", + "demo-execution-solana-core-progress", + payload, + ); + if let std::result::Result::Err(error) = emit_result { + tracing::warn!( + target: crate::TRACING_TARGET, + action = "emit_execution_progress", + error = %error, + "cannot emit Solana Core execution progress" + ); + } + } + + fn cancelled(&self) -> bool { + return self.cancel_requested.load(std::sync::atomic::Ordering::Relaxed); + } +} + +impl kb_pipeline::BackfillObserver for crate::DemoExecutionSolanaCoreObserver<'_> { + fn on_progress(&self, event: &kb_pipeline::BackfillProgressEvent) { + self.emit( + event.timestamp.clone(), + event.level.code(), + "canonical_insert", + event.message.clone(), + std::option::Option::None, + ); + } + + fn is_cancelled(&self) -> bool { + return self.cancelled(); + } +} + +impl kb_pipeline::CoreExtractionObserver for crate::DemoExecutionSolanaCoreObserver<'_> { + fn on_progress(&self, event: &kb_pipeline::CoreExtractionProgressEvent) { + self.emit( + event.timestamp.clone(), + event.level.code(), + "core_extraction", + event.message.clone(), + std::option::Option::None, + ); + } + + fn is_cancelled(&self) -> bool { + return self.cancelled(); + } +} + +impl kb_pipeline::DecodeReplayObserver for crate::DemoExecutionSolanaCoreObserver<'_> { + fn on_progress(&self, event: &kb_pipeline::DecodeReplayProgressEvent) { + self.emit( + event.timestamp.clone(), + event.level.code(), + "decode_replay", + event.message.clone(), + std::option::Option::None, + ); + } + + fn is_cancelled(&self) -> bool { + return self.cancelled(); + } +} + +impl kb_pipeline::SolanaExecutionObserver for crate::DemoExecutionSolanaCoreObserver<'_> { + fn on_execution_progress(&self, event: &kb_pipeline::SolanaExecutionProgressEvent) { + self.emit( + event.timestamp.clone(), + event.level.code(), + event.stage.as_str(), + event.message.clone(), + event.signature.clone(), + ); + } + + fn is_execution_cancelled(&self) -> bool { + return self.cancelled(); + } +} + +pub(crate) struct DemoExecutionSolanaCoreRunGuard<'a> { + pub(crate) running: &'a std::sync::atomic::AtomicBool, +} +impl std::ops::Drop for crate::DemoExecutionSolanaCoreRunGuard<'_> { + fn drop(&mut self) { + self.running.store(false, std::sync::atomic::Ordering::Release); + } +} + +pub(crate) fn devnet_profile_options( + config: &kb_config::AppConfig, +) -> std::vec::Vec { + let mut output = std::vec::Vec::new(); + for profile in &config.profiles { + if profile.wallet.cluster != "devnet" + || !profile.wallet.temporary_wallet_enabled + || !profile.wallet.temporary_wallet_persist + { + continue; + } + output.push(crate::DemoExecutionSolanaCoreProfileOption { + name: profile.name.clone(), + wallet_alias: profile.wallet.temporary_wallet_alias.clone(), + max_spend_lamports: profile.execution.devnet_max_spend_lamports, + max_airdrop_lamports: profile.execution.devnet_airdrop_max_lamports, + send_enabled: profile.wallet.devnet_send_enabled, + require_operator_confirmation: profile.execution.require_operator_confirmation, + }); + } + return output; +} + +pub(crate) fn select_devnet_profile( + config: &kb_config::AppConfig, + profile_name: &str, +) -> std::result::Result { + for profile in &config.profiles { + if profile.name == profile_name + && profile.wallet.cluster == "devnet" + && profile.wallet.temporary_wallet_enabled + && profile.wallet.temporary_wallet_persist + { + return std::result::Result::Ok(profile.clone()); + } + } + return std::result::Result::Err(format!( + "Devnet execution profile '{profile_name}' is unavailable" + )); +} + +pub(crate) fn demo_execution_solana_core_summary_payload( + summary: kb_pipeline::DevnetSystemTransferSummary, +) -> crate::DemoExecutionSolanaCoreSummaryPayload { + let plan_json = crate::pretty_json(&summary.plan); + let simulation_json = crate::pretty_json(&summary.simulation); + let transaction_signature = + summary.send_result.as_ref().map(|value| return value.signature.0.clone()); + let confirmation_status = summary.confirmation.as_ref().map(|value| { + return crate::confirmation_status_code(value.status); + }); + let canonical_inserted = summary.backfill.as_ref().map(|value| return value.canonical_inserted); + let core_extracted = summary.core_extraction.as_ref().map(|value| return value.extracted); + let decode_completed = summary.decode_replay.as_ref().map(|value| return value.completed); + let decode_failed_inputs = + summary.decode_replay.as_ref().map(|value| return value.failed_inputs); + let diagnostics_value = serde_json::json!({ + "airdropConfirmation": summary.airdrop_confirmation.as_ref(), + "confirmation": summary.confirmation.as_ref(), + "postExecution": summary.post_execution.as_ref(), + "backfill": summary.backfill.as_ref().map(|value| { + return serde_json::json!({ + "canonicalInserted": value.canonical_inserted, + "canonicalSkipped": value.canonical_skipped, + "existingSkipped": value.existing_skipped, + "missing": value.missing, + "failed": value.failed + }); + }), + "coreExtraction": summary.core_extraction.as_ref().map(|value| { + return serde_json::json!({ + "selected": value.selected, + "extracted": value.extracted, + "skipped": value.skipped, + "failed": value.failed + }); + }), + "decodeReplay": summary.decode_replay.as_ref().map(|value| { + return serde_json::json!({ + "campaignId": value.campaign_id, + "selected": value.selected, + "completed": value.completed, + "unmatched": value.unmatched, + "failedInputs": value.failed_inputs, + "cancelled": value.cancelled + }); + }) + }); + let diagnostics_json = crate::pretty_json(&diagnostics_value); + let cluster = crate::execution_cluster_code(summary.cluster); + return crate::DemoExecutionSolanaCoreSummaryPayload { + profile_name: summary.profile_name, + cluster, + genesis_hash: summary.genesis_hash, + wallet_public_key: summary.wallet.public_key, + recipient: summary.recipient.0, + recipient_existed_before: summary.recipient_existed_before, + recipient_balance_before_lamports: summary.recipient_balance_before_lamports, + recipient_minimum_balance_lamports: summary.recipient_minimum_balance_lamports, + balance_before_lamports: summary.balance_before_lamports, + balance_after_funding_lamports: summary.balance_after_funding_lamports, + fee_lamports: summary.fee.fee_lamports, + simulation_success: summary.simulation.success, + simulation_error: summary.simulation.error, + simulation_logs: summary.simulation.logs, + airdrop_signature: summary.airdrop_signature.map(|value| return value.0), + transaction_signature, + confirmation_status, + canonical_inserted, + core_extracted, + decode_completed, + decode_failed_inputs, + plan_json, + simulation_json, + diagnostics_json, + }; +} + +pub(crate) fn demo_execution_solana_core_memo_summary_payload( + summary: kb_pipeline::DevnetMemoExecutionSummary, +) -> crate::DemoExecutionMemoSummaryPayload { + let plan_json = crate::pretty_json(&summary.plan); + let simulation_json = crate::pretty_json(&summary.simulation); + let transaction_signature = + summary.send_result.as_ref().map(|value| return value.signature.0.clone()); + let confirmation_status = summary.confirmation.as_ref().map(|value| { + return crate::confirmation_status_code(value.status); + }); + let canonical_inserted = summary + .post_execution + .as_ref() + .is_some_and(|value| return value.canonical_inserted); + let core_extracted = + summary.post_execution.as_ref().is_some_and(|value| return value.core_extracted); + let decode_replayed = summary + .post_execution + .as_ref() + .is_some_and(|value| return value.decode_replayed); + let materialized = + summary.post_execution.as_ref().is_some_and(|value| return value.materialized); + let idempotence_validated = summary.idempotence_replay.as_ref().is_some_and(|replay| { + return replay.failed_inputs == 0 + && !replay.cancelled + && replay.processors.iter().all(|processor| { + return processor.failed == 0 + && processor.materialized_outputs == 0 + && processor.materialization_refused == 0; + }) + && replay.processors.iter().map(|processor| return processor.skipped).sum::() + >= 1; + }); + let annotation_count = match u32::try_from(summary.annotations.len()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => u32::MAX, + }; + let diagnostics_value = serde_json::json!({ + "confirmation": summary.confirmation.as_ref(), + "postExecution": summary.post_execution.as_ref(), + "backfill": summary.backfill.as_ref().map(|value| { + return serde_json::json!({ + "canonicalInserted": value.canonical_inserted, + "canonicalSkipped": value.canonical_skipped, + "existingSkipped": value.existing_skipped, + "missing": value.missing, + "failed": value.failed + }); + }), + "coreExtraction": summary.core_extraction.as_ref().map(|value| { + return serde_json::json!({ + "selected": value.selected, + "extracted": value.extracted, + "skipped": value.skipped, + "failed": value.failed + }); + }), + "decodeReplay": summary.decode_replay.as_ref().map(|value| { + return serde_json::json!({ + "campaignId": value.campaign_id, + "selected": value.selected, + "completed": value.completed, + "failedInputs": value.failed_inputs, + "processors": value.processors.iter().map(|processor| { + return serde_json::json!({ + "name": processor.processor_name, + "version": processor.processor_version, + "decoded": processor.decoded, + "skipped": processor.skipped, + "failed": processor.failed, + "materializedOutputs": processor.materialized_outputs, + "materializationRefused": processor.materialization_refused + }); + }).collect::>() + }); + }), + "idempotenceReplay": summary.idempotence_replay.as_ref().map(|value| { + return serde_json::json!({ + "campaignId": value.campaign_id, + "selected": value.selected, + "completed": value.completed, + "failedInputs": value.failed_inputs, + "processors": value.processors.iter().map(|processor| { + return serde_json::json!({ + "name": processor.processor_name, + "version": processor.processor_version, + "decoded": processor.decoded, + "skipped": processor.skipped, + "failed": processor.failed, + "materializedOutputs": processor.materialized_outputs, + "materializationRefused": processor.materialization_refused + }); + }).collect::>() + }); + }), + "annotations": summary.annotations + }); + let diagnostics_json = crate::pretty_json(&diagnostics_value); + return crate::DemoExecutionMemoSummaryPayload { + profile_name: summary.profile_name, + cluster: crate::execution_cluster_code(summary.cluster), + genesis_hash: summary.genesis_hash, + wallet_public_key: summary.wallet.public_key, + balance_lamports: summary.balance_lamports.to_string(), + fee_lamports: summary.fee.fee_lamports.map(|value| return value.to_string()), + simulation_success: summary.simulation.success, + simulation_error: summary.simulation.error, + transaction_signature, + confirmation_status, + canonical_inserted, + core_extracted, + decode_replayed, + materialized, + annotation_count, + idempotence_validated, + plan_json, + simulation_json, + diagnostics_json, + }; +} + +pub(crate) fn execution_cluster_code( + cluster: kb_execution_api::ExecutionCluster, +) -> std::string::String { + return match cluster { + kb_execution_api::ExecutionCluster::Localnet => "localnet".to_string(), + kb_execution_api::ExecutionCluster::Devnet => "devnet".to_string(), + kb_execution_api::ExecutionCluster::Testnet => "testnet".to_string(), + kb_execution_api::ExecutionCluster::Mainnet => "mainnet".to_string(), + }; +} + +pub(crate) fn confirmation_status_code( + status: kb_execution_api::ExecutionConfirmationStatus, +) -> std::string::String { + return match status { + kb_execution_api::ExecutionConfirmationStatus::Processed => "processed".to_string(), + kb_execution_api::ExecutionConfirmationStatus::Confirmed => "confirmed".to_string(), + kb_execution_api::ExecutionConfirmationStatus::Finalized => "finalized".to_string(), + kb_execution_api::ExecutionConfirmationStatus::Failed => "failed".to_string(), + kb_execution_api::ExecutionConfirmationStatus::Expired => "expired".to_string(), + kb_execution_api::ExecutionConfirmationStatus::TimedOut => "timed_out".to_string(), + }; +} + +pub(crate) fn pretty_json(value: &T) -> std::string::String +where + T: serde::Serialize, +{ + return match serde_json::to_string_pretty(value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + serde_json::json!({"serializationError": error.to_string()}).to_string() + }, + }; +} + +#[cfg(test)] +mod tests { + #[test] + fn example_config_exposes_one_devnet_execution_profile() { + let config = + match kb_config::parse_config_json(include_str!("../../config/example.config.json")) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("example config parse failed: {error}"), + }; + let profiles = crate::devnet_profile_options(&config); + assert_eq!(profiles.len(), 1); + assert_eq!(profiles[0].name, "local_devnet"); + assert!(profiles[0].send_enabled); + assert!(profiles[0].max_spend_lamports >= 1_000_000); + } + + #[test] + fn profile_selection_rejects_mainnet_profiles() { + let config = + match kb_config::parse_config_json(include_str!("../../config/example.config.json")) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("example config parse failed: {error}"), + }; + assert!(crate::select_devnet_profile(&config, "local_devnet").is_ok()); + assert!(crate::select_devnet_profile(&config, "mainnet").is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_execution_spl.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_execution_spl.rs new file mode 100644 index 0000000..4660c19 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_execution_spl.rs @@ -0,0 +1,71 @@ +// file: kb_app_demo/src/demo_execution_spl.rs +// version: 3 + +//! Dedicated Tauri window for bounded SPL execution on Devnet. + +/// Frontend payload for one independent Devnet SPL validation scenario. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, ts_rs::TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_execution_spl/DevnetSplValidationScenarioPayload.ts" +)] +pub(crate) struct DevnetSplValidationScenarioPayload { + /// Stable scenario identifier. + pub id: std::string::String, + /// Operator-visible label. + pub label: std::string::String, + /// Stable family code. + pub family: std::string::String, + /// Stable executor operation code. + pub operation_code: std::string::String, + /// Current implementation status. + pub implementation_status: std::string::String, + /// Whether the scenario requires proof material. + pub proof_required: bool, + /// Ordered required fixture variables. + pub required_fixture_variables: std::vec::Vec, +} + +#[cfg(test)] +mod tests { + + #[test] + fn memo_summary_keeps_lamports_as_json_strings() { + let payload = crate::DemoExecutionMemoSummaryPayload { + profile_name: "local_devnet".to_string(), + cluster: "devnet".to_string(), + genesis_hash: "EtWTRABZaYq6iMfeYKouRu166VU2xqa1".to_string(), + wallet_public_key: kb_program_ids::SYSTEM_PROGRAM_ID.to_string(), + balance_lamports: u64::MAX.to_string(), + fee_lamports: std::option::Option::Some(u64::MAX.to_string()), + simulation_success: true, + simulation_error: std::option::Option::None, + transaction_signature: std::option::Option::None, + confirmation_status: std::option::Option::None, + canonical_inserted: false, + core_extracted: false, + decode_replayed: false, + materialized: false, + annotation_count: 0, + idempotence_validated: false, + plan_json: "{}".to_string(), + simulation_json: "{}".to_string(), + diagnostics_json: "{}".to_string(), + }; + let json = match serde_json::to_value(payload) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + assert_eq!( + std::option::Option::Some(error.to_string()), + std::option::Option::None, + "Memo summary serialization failed" + ); + return; + }, + }; + assert!(json["balanceLamports"].is_string()); + assert!(json["feeLamports"].is_string()); + assert_eq!(json["balanceLamports"], u64::MAX.to_string()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_http.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_http.rs new file mode 100644 index 0000000..e02f6e8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_http.rs @@ -0,0 +1,361 @@ +// file: kb_app_demo/src/demo_http.rs +// version: 8 + +//! HTTP JSON-RPC demo commands. + +use ts_rs::TS; // rust-rules: derive-import + +/// One selectable role shown by the HTTP demo. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_http/DemoHttpRoleOption.ts" +)] +pub(crate) struct DemoHttpRoleOption { + /// Endpoint role code. + pub(crate) role: std::string::String, + /// Request kinds accepted by this role. + pub(crate) request_kinds: std::vec::Vec, +} + +/// One selectable HTTP JSON-RPC method shown by the HTTP demo. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_http/DemoHttpMethodOption.ts" +)] +pub(crate) struct DemoHttpMethodOption { + /// JSON-RPC method name. + pub(crate) method: std::string::String, + /// Derived request kind used for endpoint routing. + pub(crate) request_kind: std::string::String, + /// Human-readable method label. + pub(crate) label: std::string::String, + /// Whether this method needs the first argument field. + pub(crate) requires_first_arg: bool, + /// Whether this method supports an optional configuration object. + pub(crate) supports_config_json: bool, +} + +/// HTTP demo options derived from configuration and local method presets. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_http/DemoHttpOptionsPayload.ts" +)] +pub(crate) struct DemoHttpOptionsPayload { + /// Selectable roles. + pub(crate) roles: std::vec::Vec, + /// Selectable methods. + pub(crate) methods: std::vec::Vec, +} + +/// Request payload for one HTTP JSON-RPC demo execution. +#[derive(Clone, Debug, serde::Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_http/DemoHttpRequest.ts" +)] +pub(crate) struct DemoHttpRequest { + /// Required endpoint role used by the HTTP pool. + pub(crate) role: std::string::String, + /// JSON-RPC method name. + pub(crate) method: std::string::String, + /// Optional first argument string. + pub(crate) first_arg: std::option::Option, + /// Optional JSON configuration string appended after the first argument. + pub(crate) config_json: std::option::Option, + /// Optional raw JSON-RPC params array. When present, it overrides firstArg and configJson. + pub(crate) params_json: std::option::Option, +} + +/// Response payload for one HTTP JSON-RPC demo execution. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_http/DemoHttpExecutionPayload.ts" +)] +pub(crate) struct DemoHttpExecutionPayload { + /// Selected endpoint name. + pub(crate) endpoint_name: std::string::String, + /// Selected provider name. + pub(crate) provider: std::string::String, + /// Selected endpoint URL. + pub(crate) endpoint_url: std::string::String, + /// Required role used by the selection. + pub(crate) role: std::string::String, + /// JSON-RPC method name. + pub(crate) method: std::string::String, + /// Derived request kind. + pub(crate) request_kind: std::string::String, + /// Classified method family. + pub(crate) method_class: std::string::String, + /// Pretty JSON response text. + pub(crate) response_json: std::string::String, +} + +pub(crate) async fn demo_http_execute_request_inner( + state: tauri::State<'_, crate::AppState>, + request: crate::DemoHttpRequest, +) -> std::result::Result { + let role = request.role.trim().to_string(); + if role.is_empty() { + return std::result::Result::Err("demo HTTP role must not be empty".to_string()); + } + let method = request.method.trim().to_string(); + if method.is_empty() { + return std::result::Result::Err("demo HTTP method must not be empty".to_string()); + } + let params_json_value = match parse_optional_params_json(request.params_json) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let config_json_value = match parse_optional_json(request.config_json) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let params = match build_demo_http_params( + &method, + request.first_arg.as_deref(), + config_json_value, + params_json_value, + ) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let selected_client = match state.http_pool().select_client_for_role_and_method(&role, &method) + { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let response_value = + match selected_client.execute_json_rpc_result_raw(method.clone(), params).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let response_json = match serde_json::to_string_pretty(&response_value) { + std::result::Result::Ok(text) => text, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let method_class = kb_rpc::HttpClient::classify_method(&method); + return std::result::Result::Ok(crate::DemoHttpExecutionPayload { + endpoint_name: selected_client.endpoint_name().to_string(), + provider: selected_client.provider().to_string(), + endpoint_url: selected_client.endpoint_url().to_string(), + role, + request_kind: kb_rpc::request_kind_from_method(&method), + method, + method_class: method_class_to_string(method_class).to_string(), + response_json, + }); +} + +pub(crate) fn build_http_role_options( + snapshots: std::vec::Vec, +) -> std::vec::Vec { + let mut roles = std::collections::BTreeMap::< + std::string::String, + std::collections::BTreeSet, + >::new(); + for snapshot in snapshots { + for role in snapshot.roles { + if !role.enabled { + continue; + } + let role_entry = roles.entry(role.role).or_default(); + for request_kind in role.request_kinds { + role_entry.insert(request_kind); + } + } + } + let mut options = std::vec::Vec::new(); + for (role, request_kinds) in roles { + options.push(crate::DemoHttpRoleOption { + role, + request_kinds: request_kinds.into_iter().collect(), + }); + } + return options; +} + +pub(crate) fn build_http_method_options() -> std::vec::Vec { + let methods = [ + ("getAccountInfo", "Compte Solana", true, true), + ("getBalance", "Balance SOL", true, true), + ("getBlock", "Bloc par slot", true, true), + ("getBlockCommitment", "Commitment d’un bloc", true, false), + ("getBlockHeight", "Hauteur de bloc", false, true), + ("getBlockProduction", "Production de blocs", false, true), + ("getBlocks", "Liste de blocs", true, true), + ("getBlocksWithLimit", "Liste de blocs avec limite", true, true), + ("getClusterNodes", "Nœuds du cluster", false, false), + ("getEpochInfo", "Époque courante", false, true), + ("getEpochSchedule", "Planning des époques", false, false), + ("getFeeForMessage", "Frais pour message", true, true), + ("getFirstAvailableBlock", "Premier bloc disponible", false, false), + ("getGenesisHash", "Genesis hash", false, false), + ("getHealth", "Santé du nœud", false, false), + ("getHighestSnapshotSlot", "Dernier snapshot", false, false), + ("getIdentity", "Identité du nœud", false, false), + ("getInflationGovernor", "Gouverneur inflation", false, true), + ("getInflationRate", "Taux d’inflation", false, false), + ("getInflationReward", "Récompense inflation", true, true), + ("getLargestAccounts", "Plus gros comptes", false, true), + ("getLatestBlockhash", "Dernier blockhash", false, true), + ("getLeaderSchedule", "Planning leaders", false, true), + ("getMaxRetransmitSlot", "Slot retransmis maximum", false, false), + ("getMaxShredInsertSlot", "Shred insert slot maximum", false, false), + ("getMinimumBalanceForRentExemption", "Rent exemption", true, true), + ("getMultipleAccounts", "Comptes multiples", true, true), + ("getProgramAccounts", "Comptes de programme", true, true), + ("getRecentPerformanceSamples", "Samples performance", false, true), + ("getRecentPrioritizationFees", "Frais de priorité récents", false, true), + ("getSignaturesForAddress", "Signatures par adresse", true, true), + ("getSignatureStatuses", "Statuts signatures", true, true), + ("getSlot", "Slot courant", false, true), + ("getSlotLeader", "Leader du slot", false, true), + ("getSlotLeaders", "Leaders de slots", true, true), + ("getStakeActivation", "Activation stake", true, true), + ("getStakeMinimumDelegation", "Minimum delegation", false, true), + ("getSupply", "Supply SOL", false, true), + ("getTokenAccountBalance", "Balance token account", true, true), + ("getTokenAccountsByDelegate", "Token accounts par delegate", true, true), + ("getTokenAccountsByOwner", "Token accounts par owner", true, true), + ("getTokenLargestAccounts", "Plus gros comptes token", true, true), + ("getTokenSupply", "Supply token", true, true), + ("getTransaction", "Transaction", true, true), + ("getTransactionCount", "Nombre de transactions", false, true), + ("getVersion", "Version du nœud", false, false), + ("getVoteAccounts", "Vote accounts", false, true), + ("isBlockhashValid", "Validité blockhash", true, true), + ("requestAirdrop", "Airdrop devnet", true, true), + ("sendTransaction", "Envoi de transaction", true, true), + ("simulateTransaction", "Simulation transaction", true, true), + ]; + let mut options = std::vec::Vec::new(); + for (method, label, requires_first_arg, supports_config_json) in methods { + options.push(crate::DemoHttpMethodOption { + method: method.to_string(), + request_kind: kb_rpc::request_kind_from_method(method), + label: label.to_string(), + requires_first_arg, + supports_config_json, + }); + } + return options; +} + +fn parse_optional_json( + config_json: std::option::Option, +) -> std::result::Result, std::string::String> { + let config_text = match config_json { + std::option::Option::Some(value) => value.trim().to_string(), + std::option::Option::None => return std::result::Result::Ok(std::option::Option::None), + }; + if config_text.is_empty() { + return std::result::Result::Ok(std::option::Option::None); + } + let parse_result = serde_json::from_str::(&config_text); + return match parse_result { + std::result::Result::Ok(value) => std::result::Result::Ok(std::option::Option::Some(value)), + std::result::Result::Err(error) => { + std::result::Result::Err(format!("invalid configJson: {error}")) + }, + }; +} + +fn parse_optional_params_json( + params_json: std::option::Option, +) -> std::result::Result>, std::string::String> +{ + let params_text = match params_json { + std::option::Option::Some(value) => value.trim().to_string(), + std::option::Option::None => return std::result::Result::Ok(std::option::Option::None), + }; + if params_text.is_empty() { + return std::result::Result::Ok(std::option::Option::None); + } + let parse_result = serde_json::from_str::(¶ms_text); + let value = match parse_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!("invalid paramsJson: {error}")); + }, + }; + let array = match value.as_array() { + std::option::Option::Some(array) => array.clone(), + std::option::Option::None => { + return std::result::Result::Err("paramsJson must be a JSON array".to_string()); + }, + }; + return std::result::Result::Ok(std::option::Option::Some(array)); +} + +fn build_demo_http_params( + method: &str, + first_arg: std::option::Option<&str>, + config_json: std::option::Option, + params_json: std::option::Option>, +) -> std::result::Result, std::string::String> { + if let std::option::Option::Some(params) = params_json { + return std::result::Result::Ok(params); + } + let needs_first_arg = method == "getBalance" + || method == "getAccountInfo" + || method == "getBlock" + || method == "getBlockCommitment" + || method == "getBlocks" + || method == "getBlocksWithLimit" + || method == "getFeeForMessage" + || method == "getInflationReward" + || method == "getMinimumBalanceForRentExemption" + || method == "getMultipleAccounts" + || method == "getProgramAccounts" + || method == "getSignaturesForAddress" + || method == "getSignatureStatuses" + || method == "getSlotLeaders" + || method == "getStakeActivation" + || method == "getTokenAccountBalance" + || method == "getTokenAccountsByDelegate" + || method == "getTokenAccountsByOwner" + || method == "getTokenLargestAccounts" + || method == "getTokenSupply" + || method == "getTransaction" + || method == "isBlockhashValid" + || method == "requestAirdrop" + || method == "sendTransaction" + || method == "simulateTransaction"; + if needs_first_arg { + let first_arg_value = match first_arg { + std::option::Option::Some(value) => value.trim(), + std::option::Option::None => "", + }; + if first_arg_value.is_empty() { + return std::result::Result::Err(format!("method '{method}' requires firstArg")); + } + let mut params = std::vec::Vec::new(); + params.push(serde_json::Value::String(first_arg_value.to_string())); + if let std::option::Option::Some(config_value) = config_json { + params.push(config_value); + } + return std::result::Result::Ok(params); + } + let mut params = std::vec::Vec::new(); + if let std::option::Option::Some(config_value) = config_json { + params.push(config_value); + } + return std::result::Result::Ok(params); +} + +fn method_class_to_string(method_class: kb_rpc::HttpMethodClass) -> &'static str { + return match method_class { + kb_rpc::HttpMethodClass::GeneralRpc => "GeneralRpc", + kb_rpc::HttpMethodClass::SendTransaction => "SendTransaction", + kb_rpc::HttpMethodClass::HeavyRead => "HeavyRead", + }; +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_spl_ata.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_spl_ata.rs new file mode 100644 index 0000000..4558f6d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_spl_ata.rs @@ -0,0 +1,447 @@ +// file: kb_app_demo/src/demo_spl_ata.rs +// version: 3 + +//! Thin Tauri adapter for ATA execution, derivation and lifecycle journal reads. + +use ts_rs::TS; // rust-rules: derive-import + +/// Request for one representative ATA creation on Devnet. +#[derive(Clone, Debug, serde::Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_spl_ata/DemoExecutionSplAtaRequest.ts" +)] +pub(crate) struct DemoExecutionSplAtaRequest { + /// Selected Devnet profile. + pub(crate) profile_name: std::string::String, + /// Wallet owner used by canonical derivation. + pub(crate) wallet_owner: std::string::String, + /// Mint account. + pub(crate) mint: std::string::String, + /// `classic` or `token_2022`. + pub(crate) token_program: std::string::String, + /// `create` or `create_idempotent`. + pub(crate) mode: std::string::String, + /// Whether to sign and submit after simulation. + pub(crate) submit: bool, + /// Explicit operator confirmation. + pub(crate) operator_confirmed: bool, + /// Whether post-execution outputs should be force-replayed. + pub(crate) force_post_validation_replay: bool, +} + +/// Request used to derive the representative wallet ATA before execution. +#[derive(Clone, Debug, serde::Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_spl_ata/DemoSplAtaDerivationRequest.ts" +)] +pub(crate) struct DemoSplAtaDerivationRequest { + /// Selected Devnet profile. + pub(crate) profile_name: std::string::String, + /// Mint account. + pub(crate) mint: std::string::String, + /// `classic` or `token_2022`. + pub(crate) token_program: std::string::String, +} + +/// Readonly payer, wallet, Token Program and derived ATA values. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_spl_ata/DemoSplAtaDerivationPayload.ts" +)] +pub(crate) struct DemoSplAtaDerivationPayload { + /// Profile wallet used as payer. + pub(crate) payer: std::string::String, + /// Wallet owner selected by the representative panel. + pub(crate) wallet_owner: std::string::String, + /// Exact Token Program ID. + pub(crate) token_program_id: std::string::String, + /// Canonically derived ATA. + pub(crate) associated_token_account: std::string::String, +} + +/// UI-safe ATA execution result. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_spl_ata/DemoExecutionSplAtaSummaryPayload.ts" +)] +pub(crate) struct DemoExecutionSplAtaSummaryPayload { + /// Stable operation code. + pub(crate) operation: std::string::String, + /// Profile name. + pub(crate) profile_name: std::string::String, + /// Profile wallet public key. + pub(crate) wallet_public_key: std::string::String, + /// Target Token Program ID. + pub(crate) token_program_id: std::string::String, + /// Derived ATA from the exact plan. + pub(crate) associated_token_account: std::string::String, + /// Wallet balance as lossless JSON text. + pub(crate) balance_lamports: std::string::String, + /// Fee estimate as lossless JSON text. + pub(crate) fee_lamports: std::option::Option, + /// Aggregate stateful readiness. + pub(crate) readiness_status: std::string::String, + /// Exact simulation outcome. + pub(crate) simulation_success: bool, + /// Runtime simulation error. + pub(crate) simulation_error: std::option::Option, + /// Submitted signature. + pub(crate) transaction_signature: std::option::Option, + /// Terminal confirmation status. + pub(crate) confirmation_status: std::option::Option, + /// Number of ATA-owned materialized facts. + pub(crate) materialization_count: u32, + /// Whether the second replay created no output. + pub(crate) idempotence_validated: bool, + /// Exact plan JSON. + pub(crate) plan_json: std::string::String, + /// Stateful readiness JSON. + pub(crate) readiness_json: std::string::String, + /// Simulation JSON. + pub(crate) simulation_json: std::string::String, + /// Confirmation and replay diagnostics JSON. + pub(crate) diagnostics_json: std::string::String, +} + +/// Bounded ATA lifecycle journal request. +#[derive(Clone, Debug, serde::Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_spl_ata/DemoSplAtaJournalRequest.ts" +)] +pub(crate) struct DemoSplAtaJournalRequest { + /// Selected Devnet profile. + pub(crate) profile_name: std::string::String, + /// Optional partial signature. + pub(crate) signature_contains: std::option::Option, + /// Optional exact mint. + pub(crate) mint: std::option::Option, + /// Optional exact ATA. + pub(crate) associated_token_account: std::option::Option, + /// Optional exact materialized operation. + pub(crate) operation: std::option::Option, + /// Maximum returned rows. + pub(crate) limit: u32, +} + +/// One UI-safe ATA lifecycle journal row. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_spl_ata/DemoSplAtaJournalRow.ts" +)] +pub(crate) struct DemoSplAtaJournalRow { + /// Transaction signature. + pub(crate) signature: std::string::String, + /// Slot as lossless JSON text. + pub(crate) slot: std::string::String, + /// Materialized family. + pub(crate) family: std::string::String, + /// Lifecycle operation. + pub(crate) operation: std::option::Option, + /// Exact mint when present. + pub(crate) mint: std::option::Option, + /// Exact ATA when present. + pub(crate) associated_token_account: std::option::Option, + /// Full bounded payload JSON. + pub(crate) payload_json: std::string::String, +} + +pub(crate) async fn load_profile_wallet( + profile: &kb_config::ProfileConfig, +) -> std::result::Result { + let configured = std::path::PathBuf::from(profile.wallet.wallet_dir.as_str()); + let directory = if configured.is_absolute() { + configured + } else { + crate::workspace_root_dir().join(configured) + }; + let store = match kb_wallet::TemporaryWalletStore::new(directory) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let alias = match kb_wallet::WalletAlias::parse(profile.wallet.temporary_wallet_alias.clone()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + return match store.load_or_create(alias).await { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(error.to_string()), + }; +} + +pub(crate) fn parse_token_program( + value: &str, +) -> std::result::Result< + kb_executor_spl_associated_token_account::SplAssociatedTokenProgram, + std::string::String, +> { + return match value.trim() { + "classic" => std::result::Result::Ok( + kb_executor_spl_associated_token_account::SplAssociatedTokenProgram::Classic, + ), + "token_2022" => std::result::Result::Ok( + kb_executor_spl_associated_token_account::SplAssociatedTokenProgram::Token2022, + ), + _ => std::result::Result::Err("Token Program must be classic or token_2022".to_string()), + }; +} + +pub(crate) fn derive_ata( + wallet: &str, + mint: &str, + token_program: &str, +) -> std::result::Result { + let wallet: solana_pubkey::Pubkey = match wallet.parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!("invalid wallet: {error}")); + }, + }; + let mint: solana_pubkey::Pubkey = match mint.parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!("invalid mint: {error}")); + }, + }; + let token_program: solana_pubkey::Pubkey = match token_program.parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!("invalid Token Program: {error}")); + }, + }; + return std::result::Result::Ok( + spl_associated_token_account_interface::address::get_associated_token_address_with_program_id( + &wallet, + &mint, + &token_program, + ) + .to_string(), + ); +} + +pub(crate) fn demo_spl_ata_summary_payload( + summary: kb_pipeline::DevnetSplAssociatedTokenAccountExecutionSummary, +) -> crate::DemoExecutionSplAtaSummaryPayload { + let (associated_token_account, token_program_id) = match summary.plan.instructions.first() { + std::option::Option::Some(instruction) => { + let ata = match instruction.accounts.get(1) { + std::option::Option::Some(account) => account.pubkey.0.clone(), + std::option::Option::None => std::string::String::new(), + }; + let token_program = match instruction.accounts.last() { + std::option::Option::Some(account) => account.pubkey.0.clone(), + std::option::Option::None => std::string::String::new(), + }; + (ata, token_program) + }, + std::option::Option::None => (std::string::String::new(), std::string::String::new()), + }; + let transaction_signature = + summary.send_result.as_ref().map(|value| return value.signature.0.clone()); + let confirmation_status = summary.confirmation.as_ref().map(|value| { + return crate::confirmation_status_code(value.status); + }); + let idempotence_validated = summary.idempotence_replay.as_ref().is_some_and(|replay| { + return replay.failed_inputs == 0 + && replay.processors.iter().all(|processor| { + return processor.failed == 0 + && processor.materialized_outputs == 0 + && processor.materialization_refused == 0; + }); + }); + let materialization_count = match u32::try_from(summary.materializations.len()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => u32::MAX, + }; + let diagnostics = serde_json::json!({ + "confirmation": summary.confirmation.as_ref(), + "postStateValidation": summary.post_state_validation.as_ref(), + "postExecution": summary.post_execution.as_ref(), + "backfill": summary.backfill.as_ref().map(|value| { + return serde_json::json!({ + "candidatesCompleted": value.candidates_completed, + "candidatesCancelled": value.candidates_cancelled, + "candidatesNotStarted": value.candidates_not_started, + "canonicalInserted": value.canonical_inserted, + "canonicalSkipped": value.canonical_skipped, + "existingSkipped": value.existing_skipped, + "missing": value.missing, + "failed": value.failed + }); + }), + "coreExtraction": summary.core_extraction.as_ref().map(|value| { + return serde_json::json!({ + "selected": value.selected, + "extracted": value.extracted, + "skipped": value.skipped, + "failed": value.failed, + "cancelled": value.cancelled + }); + }), + "decodeReplay": summary.decode_replay.as_ref().map(|value| { + return serde_json::json!({ + "campaignId": value.campaign_id, + "selected": value.selected, + "completed": value.completed, + "unmatched": value.unmatched, + "failedInputs": value.failed_inputs, + "cancelled": value.cancelled + }); + }), + "idempotenceReplay": summary.idempotence_replay.as_ref().map(|value| { + return serde_json::json!({ + "campaignId": value.campaign_id, + "selected": value.selected, + "completed": value.completed, + "unmatched": value.unmatched, + "failedInputs": value.failed_inputs, + "cancelled": value.cancelled, + "materializedOutputs": value.processors.iter().map(|processor| { + return processor.materialized_outputs; + }).sum::(), + "materializationRefused": value.processors.iter().map(|processor| { + return processor.materialization_refused; + }).sum::() + }); + }), + "materializations": summary.materializations.iter().map(|value| { + return serde_json::json!({ + "processorName": value.processor_name, + "outputKey": value.output_key, + "family": value.materialized_family, + "signature": value.signature, + "slot": value.slot.to_string(), + "payload": value.payload_json + }); + }).collect::>() + }); + let plan_json = crate::pretty_json(&summary.plan); + let readiness_json = crate::pretty_json(&summary.stateful_readiness); + let simulation_json = crate::pretty_json(&summary.simulation); + let diagnostics_json = crate::pretty_json(&diagnostics); + return crate::DemoExecutionSplAtaSummaryPayload { + operation: summary.stateful_readiness.operation_code, + profile_name: summary.profile_name, + wallet_public_key: summary.wallet.public_key, + token_program_id, + associated_token_account, + balance_lamports: summary.balance_lamports.to_string(), + fee_lamports: summary.fee.fee_lamports.map(|value| return value.to_string()), + readiness_status: match summary.stateful_readiness.status { + kb_pipeline::SplAssociatedTokenAccountStatefulReadinessStatus::Ready => { + "ready".to_string() + }, + kb_pipeline::SplAssociatedTokenAccountStatefulReadinessStatus::Blocked => { + "blocked".to_string() + }, + }, + simulation_success: summary.simulation.success, + simulation_error: summary.simulation.error, + transaction_signature, + confirmation_status, + materialization_count, + idempotence_validated, + plan_json, + readiness_json, + simulation_json, + diagnostics_json, + }; +} + +pub(crate) fn journal_matches( + payload: &serde_json::Value, + request: &crate::DemoSplAtaJournalRequest, +) -> bool { + for (expected, key) in [ + (&request.operation, "operation"), + (&request.mint, "mint"), + (&request.associated_token_account, "associatedTokenAccount"), + ] { + if expected.as_ref().is_some_and(|value| { + return payload.get(key).and_then(serde_json::Value::as_str) + != std::option::Option::Some(value.as_str()); + }) { + return false; + } + } + return true; +} + +pub(crate) fn journal_row( + row: kb_store_core::MaterializedEventQueryRow, +) -> crate::DemoSplAtaJournalRow { + let text = |key: &str| { + return row + .payload_json + .get(key) + .and_then(serde_json::Value::as_str) + .map(std::string::ToString::to_string); + }; + let operation = text("operation"); + let mint = text("mint"); + let associated_token_account = text("associatedTokenAccount"); + let payload_json = crate::pretty_json(&row.payload_json); + return crate::DemoSplAtaJournalRow { + signature: row.signature, + slot: row.slot.to_string(), + family: row.materialized_family, + operation, + mint, + associated_token_account, + payload_json, + }; +} + +#[cfg(test)] +mod tests { + #[test] + fn classic_and_token_2022_derivations_are_distinct_and_canonical() { + let wallet = kb_program_ids::SYSTEM_PROGRAM_ID; + let mint = "So11111111111111111111111111111111111111112"; + let classic = crate::derive_ata(wallet, mint, kb_program_ids::SPL_TOKEN_PROGRAM_ID) + .unwrap_or_else(|error| panic!("classic derivation failed: {error}")); + let token_2022 = crate::derive_ata(wallet, mint, kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID) + .unwrap_or_else(|error| panic!("Token-2022 derivation failed: {error}")); + assert_eq!(classic, "aqxoAhCwpy3oB1BpNw9hL1HdLYLgPpbPjzxDrrQj3Fs"); + assert_eq!(token_2022, "2sZUUBGq1i6aE47ZoxCaCW89jmYm2EXLPPmNMgMDXHMS"); + } + + #[test] + fn lifecycle_journal_filters_are_exact() { + let request = crate::DemoSplAtaJournalRequest { + profile_name: "local_devnet".to_string(), + signature_contains: std::option::Option::None, + mint: std::option::Option::Some("mint111".to_string()), + associated_token_account: std::option::Option::Some("ata111".to_string()), + operation: std::option::Option::Some("create_idempotent".to_string()), + limit: 100, + }; + assert!(crate::journal_matches( + &serde_json::json!({ + "operation": "create_idempotent", + "mint": "mint111", + "associatedTokenAccount": "ata111" + }), + &request, + )); + assert!(!crate::journal_matches( + &serde_json::json!({ + "operation": "create", + "mint": "mint111", + "associatedTokenAccount": "ata111" + }), + &request, + )); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_spl_token.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_spl_token.rs new file mode 100644 index 0000000..bf608c5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_spl_token.rs @@ -0,0 +1,493 @@ +// file: kb_app_demo/src/demo_spl_token.rs +// version: 2 + +//! Thin Tauri adapter for classic SPL Token execution and materialized journals. + +use ts_rs::TS; // rust-rules: derive-import + +/// Request for one checked classic SPL Token transfer on Devnet. +#[derive(Clone, Debug, serde::Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_spl_token/DemoExecutionSplTokenRequest.ts" +)] +pub(crate) struct DemoExecutionSplTokenRequest { + /// Selected Devnet profile. + pub(crate) profile_name: std::string::String, + /// Source token account. + pub(crate) source: std::string::String, + /// Exact mint account carried by `TransferChecked`. + pub(crate) mint: std::string::String, + /// Destination token account. + pub(crate) destination: std::string::String, + /// Simple authority resolved by the selected profile wallet. + pub(crate) authority: std::string::String, + /// Exact raw amount represented as a decimal string. + pub(crate) amount_raw: std::string::String, + /// Expected mint decimals carried by the wire. + pub(crate) decimals: u8, + /// Whether the transaction should be signed and submitted after simulation. + pub(crate) submit: bool, + /// Explicit operator confirmation for signed submission. + pub(crate) operator_confirmed: bool, + /// Whether post-execution core and decode outputs should be force-replayed. + pub(crate) force_post_validation_replay: bool, +} + +/// UI-safe result of one checked SPL Token transfer orchestration. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_spl_token/DemoExecutionSplTokenSummaryPayload.ts" +)] +pub(crate) struct DemoExecutionSplTokenSummaryPayload { + /// Stable operation code. + pub(crate) operation: std::string::String, + /// Profile used by the orchestration. + pub(crate) profile_name: std::string::String, + /// Classified cluster code. + pub(crate) cluster: std::string::String, + /// Genesis hash returned by the endpoint. + pub(crate) genesis_hash: std::string::String, + /// Source wallet public key. + pub(crate) wallet_public_key: std::string::String, + /// Exact wallet balance rendered without JSON integer precision loss. + pub(crate) balance_lamports: std::string::String, + /// Estimated fee rendered without JSON integer precision loss. + pub(crate) fee_lamports: std::option::Option, + /// Exact raw token amount. + pub(crate) amount_raw: std::string::String, + /// Expected mint decimals. + pub(crate) decimals: u8, + /// Aggregate stateful preflight status. + pub(crate) readiness_status: std::string::String, + /// Whether exact simulation succeeded. + pub(crate) simulation_success: bool, + /// Runtime simulation error when present. + pub(crate) simulation_error: std::option::Option, + /// Submitted transaction signature. + pub(crate) transaction_signature: std::option::Option, + /// Terminal confirmation status. + pub(crate) confirmation_status: std::option::Option, + /// Whether canonical hydration was validated. + pub(crate) canonical_inserted: bool, + /// Whether core extraction was validated. + pub(crate) core_extracted: bool, + /// Whether SPL Token decode replay was validated. + pub(crate) decode_replayed: bool, + /// Number of materialized rows for the submitted instruction. + pub(crate) materialization_count: u32, + /// Whether the second replay produced no failure or new output. + pub(crate) idempotence_validated: bool, + /// Pretty JSON representation of the exact execution plan. + pub(crate) plan_json: std::string::String, + /// Pretty JSON representation of stateful readiness. + pub(crate) readiness_json: std::string::String, + /// Pretty JSON representation of the exact simulation result. + pub(crate) simulation_json: std::string::String, + /// Pretty JSON representation of confirmation and replay diagnostics. + pub(crate) diagnostics_json: std::string::String, +} + +/// Bounded exact filters for the classic SPL Token materialized journal. +#[derive(Clone, Debug, serde::Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_spl_token/DemoSplTokenJournalRequest.ts" +)] +pub(crate) struct DemoSplTokenJournalRequest { + /// Devnet profile whose PostgreSQL store is queried. + pub(crate) profile_name: std::string::String, + /// Optional partial signature handled by the bounded store query. + pub(crate) signature_contains: std::option::Option, + /// Optional exact mint account. + pub(crate) mint: std::option::Option, + /// Optional exact account occurring in the ordered account list. + pub(crate) account: std::option::Option, + /// Optional exact materialized family. + pub(crate) family: std::option::Option, + /// Optional exact operation code without the `spl_token.` prefix. + pub(crate) operation: std::option::Option, + /// Maximum number of rows returned after typed filtering. + pub(crate) limit: u32, +} + +/// UI-safe materialized classic SPL Token journal row. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_spl_token/DemoSplTokenJournalRow.ts" +)] +pub(crate) struct DemoSplTokenJournalRow { + /// Materializer processor name. + pub(crate) processor_name: std::string::String, + /// Materializer processor version. + pub(crate) processor_version: std::string::String, + /// Stable processor-owned output key. + pub(crate) output_key: std::string::String, + /// Transaction signature. + pub(crate) signature: std::string::String, + /// Decimal slot rendered as text to preserve JSON precision. + pub(crate) slot: std::string::String, + /// Materialized family code. + pub(crate) family: std::string::String, + /// Exact operation when present. + pub(crate) operation: std::option::Option, + /// Exact mint account when explicitly available. + pub(crate) mint: std::option::Option, + /// Exact raw amount when carried by the materialized fact. + pub(crate) amount_raw: std::option::Option, + /// Stable outer or inner instruction path when present. + pub(crate) instruction_path: std::option::Option, + /// Ordered account keys preserved by the decoder. + pub(crate) account_keys: std::vec::Vec, + /// Complete bounded typed payload rendered as JSON text. + pub(crate) payload_json: std::string::String, + /// Database creation timestamp. + pub(crate) created_at: std::string::String, + /// Database replacement timestamp. + pub(crate) updated_at: std::string::String, +} + +pub(crate) fn demo_spl_token_summary_payload( + summary: kb_pipeline::DevnetSplTokenExecutionSummary, + amount_raw: std::string::String, + decimals: u8, +) -> crate::DemoExecutionSplTokenSummaryPayload { + let plan_json = crate::pretty_json(&summary.plan); + let readiness_json = crate::pretty_json(&summary.stateful_readiness); + let simulation_json = crate::pretty_json(&summary.simulation); + let transaction_signature = + summary.send_result.as_ref().map(|value| return value.signature.0.clone()); + let confirmation_status = summary.confirmation.as_ref().map(|value| { + return crate::confirmation_status_code(value.status); + }); + let post_execution = summary.post_execution.as_ref(); + let canonical_inserted = post_execution.is_some_and(|value| return value.canonical_inserted); + let core_extracted = post_execution.is_some_and(|value| return value.core_extracted); + let decode_replayed = post_execution.is_some_and(|value| return value.decode_replayed); + let idempotence_validated = summary.idempotence_replay.as_ref().is_some_and(|replay| { + return replay.failed_inputs == 0 + && !replay.cancelled + && replay.processors.iter().all(|processor| { + return processor.failed == 0 + && processor.materialized_outputs == 0 + && processor.materialization_refused == 0; + }); + }); + let materialization_count = match u32::try_from(summary.materializations.len()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => u32::MAX, + }; + let diagnostics = serde_json::json!({ + "confirmation": summary.confirmation, + "postExecution": summary.post_execution, + "backfill": summary.backfill.as_ref().map(|value| { + return serde_json::json!({ + "canonicalInserted": value.canonical_inserted, + "canonicalSkipped": value.canonical_skipped, + "existingSkipped": value.existing_skipped, + "missing": value.missing, + "failed": value.failed + }); + }), + "coreExtraction": summary.core_extraction.as_ref().map(|value| { + return serde_json::json!({ + "selected": value.selected, + "extracted": value.extracted, + "skipped": value.skipped, + "failed": value.failed, + "cancelled": value.cancelled + }); + }), + "decodeReplay": summary.decode_replay.as_ref().map(|value| { + return replay_diagnostics(value); + }), + "idempotenceReplay": summary.idempotence_replay.as_ref().map(|value| { + return replay_diagnostics(value); + }), + "materializations": summary.materializations + }); + return crate::DemoExecutionSplTokenSummaryPayload { + operation: summary.stateful_readiness.operation_code, + profile_name: summary.profile_name, + cluster: crate::execution_cluster_code(summary.cluster), + genesis_hash: summary.genesis_hash, + wallet_public_key: summary.wallet.public_key, + balance_lamports: summary.balance_lamports.to_string(), + fee_lamports: summary.fee.fee_lamports.map(|value| return value.to_string()), + amount_raw, + decimals, + readiness_status: match summary.stateful_readiness.status { + kb_pipeline::SplTokenStatefulReadinessStatus::Ready => "ready".to_string(), + kb_pipeline::SplTokenStatefulReadinessStatus::Blocked => "blocked".to_string(), + }, + simulation_success: summary.simulation.success, + simulation_error: summary.simulation.error, + transaction_signature, + confirmation_status, + canonical_inserted, + core_extracted, + decode_replayed, + materialization_count, + idempotence_validated, + plan_json, + readiness_json, + simulation_json, + diagnostics_json: crate::pretty_json(&diagnostics), + }; +} + +fn replay_diagnostics(summary: &kb_pipeline::DecodeReplaySummary) -> serde_json::Value { + return serde_json::json!({ + "campaignId": summary.campaign_id, + "selected": summary.selected, + "completed": summary.completed, + "failedInputs": summary.failed_inputs, + "cancelled": summary.cancelled, + "processors": summary.processors.iter().map(|processor| { + return serde_json::json!({ + "name": processor.processor_name, + "version": processor.processor_version, + "decoded": processor.decoded, + "skipped": processor.skipped, + "failed": processor.failed, + "materializedOutputs": processor.materialized_outputs, + "materializationRefused": processor.materialization_refused + }); + }).collect::>() + }); +} + +pub(crate) fn validate_journal_request( + request: crate::DemoSplTokenJournalRequest, +) -> std::result::Result { + if request.limit == 0 || request.limit > kb_store_core::MAX_MATERIALIZED_EVENT_QUERY_ROWS { + return std::result::Result::Err(format!( + "journal limit must be between 1 and {}", + kb_store_core::MAX_MATERIALIZED_EVENT_QUERY_ROWS + )); + } + if request.profile_name.trim().is_empty() { + return std::result::Result::Err("journal profile name must not be empty".to_string()); + } + let signature_contains = + match bounded_optional(request.signature_contains, "signature filter", 128) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mint = match bounded_optional(request.mint, "mint filter", 64) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let account = match bounded_optional(request.account, "account filter", 64) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let operation = match bounded_optional(request.operation, "operation filter", 64) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let family = match bounded_optional(request.family, "family filter", 32) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if family.as_ref().is_some_and(|value| { + return !matches!(value.as_str(), "token_account" | "admin" | "risk"); + }) { + return std::result::Result::Err( + "journal family must be token_account, admin or risk".to_string(), + ); + } + return std::result::Result::Ok(crate::DemoSplTokenJournalRequest { + profile_name: request.profile_name.trim().to_string(), + signature_contains, + mint, + account, + family, + operation, + limit: request.limit, + }); +} + +fn bounded_optional( + value: std::option::Option, + label: &str, + maximum_length: usize, +) -> std::result::Result, std::string::String> { + let trimmed = value.map(|text| return text.trim().to_string()); + let trimmed = trimmed.filter(|text| return !text.is_empty()); + if trimmed.as_ref().is_some_and(|text| return text.len() > maximum_length) { + return std::result::Result::Err(format!("{label} must not exceed {maximum_length} bytes")); + } + return std::result::Result::Ok(trimmed); +} + +pub(crate) fn payload_matches( + payload: &serde_json::Value, + request: &crate::DemoSplTokenJournalRequest, +) -> bool { + if request.operation.as_ref().is_some_and(|expected| { + return payload.get("operation").and_then(serde_json::Value::as_str) + != std::option::Option::Some(expected.as_str()); + }) { + return false; + } + if request.mint.as_ref().is_some_and(|expected| { + return payload_mint(payload).as_deref() != std::option::Option::Some(expected.as_str()); + }) { + return false; + } + if request.account.as_ref().is_some_and(|expected| { + return !payload_account_keys(payload).iter().any(|value| return value == expected); + }) { + return false; + } + return true; +} + +pub(crate) fn demo_spl_token_journal_row( + row: kb_store_core::MaterializedEventQueryRow, +) -> DemoSplTokenJournalRow { + let operation = row + .payload_json + .get("operation") + .and_then(serde_json::Value::as_str) + .map(std::string::ToString::to_string); + let amount_raw = row + .payload_json + .get("amountRaw") + .or_else(|| { + return row + .payload_json + .get("parameters") + .and_then(|value| return value.get("amountRaw")); + }) + .and_then(serde_json::Value::as_str) + .map(std::string::ToString::to_string); + let instruction_path = row + .payload_json + .get("instructionPath") + .and_then(serde_json::Value::as_str) + .map(std::string::ToString::to_string); + let payload_json = crate::pretty_json(&row.payload_json); + return DemoSplTokenJournalRow { + processor_name: row.processor_name, + processor_version: row.processor_version, + output_key: row.output_key, + signature: row.signature, + slot: row.slot.to_string(), + family: row.materialized_family, + operation, + mint: payload_mint(&row.payload_json), + amount_raw, + instruction_path, + account_keys: payload_account_keys(&row.payload_json), + payload_json, + created_at: row.created_at, + updated_at: row.updated_at, + }; +} + +fn payload_mint(payload: &serde_json::Value) -> std::option::Option { + let direct = payload.get("mint").and_then(serde_json::Value::as_str); + if let std::option::Option::Some(value) = direct { + return std::option::Option::Some(value.to_string()); + } + return payload.get("accounts").and_then(serde_json::Value::as_array).and_then(|rows| { + return rows.iter().find_map(|row| { + if row.get("role").and_then(serde_json::Value::as_str) + != std::option::Option::Some("mint") + { + return std::option::Option::None; + } + return row + .get("accountKey") + .and_then(serde_json::Value::as_str) + .map(std::string::ToString::to_string); + }); + }); +} + +fn payload_account_keys(payload: &serde_json::Value) -> std::vec::Vec { + let values = payload.get("accounts").and_then(serde_json::Value::as_array).map(|rows| { + return rows + .iter() + .filter_map(|row| { + return row + .get("accountKey") + .and_then(serde_json::Value::as_str) + .map(std::string::ToString::to_string); + }) + .collect(); + }); + return match values { + std::option::Option::Some(values) => values, + std::option::Option::None => std::vec::Vec::new(), + }; +} + +#[cfg(test)] +mod tests { + #[test] + fn journal_filters_are_exact_bounded_and_preserve_raw_amounts() { + let request = crate::DemoSplTokenJournalRequest { + profile_name: "local_devnet".to_string(), + signature_contains: std::option::Option::None, + mint: std::option::Option::Some("mint111".to_string()), + account: std::option::Option::Some("source111".to_string()), + family: std::option::Option::Some("token_account".to_string()), + operation: std::option::Option::Some("transfer_checked".to_string()), + limit: 25, + }; + let payload = serde_json::json!({ + "operation": "transfer_checked", + "mint": "mint111", + "amountRaw": "18446744073709551615", + "accounts": [ + {"role": "source", "accountKey": "source111"}, + {"role": "mint", "accountKey": "mint111"} + ] + }); + assert!(crate::payload_matches(&payload, &request)); + assert_eq!( + payload.get("amountRaw").and_then(serde_json::Value::as_str), + std::option::Option::Some("18446744073709551615") + ); + let row = crate::demo_spl_token_journal_row(kb_store_core::MaterializedEventQueryRow { + processor_name: "spl_token_accounts".to_string(), + processor_version: "0.4.4".to_string(), + input_key: "input".to_string(), + output_key: "output".to_string(), + source_event_key: "event".to_string(), + source_decoder_name: "spl_token".to_string(), + source_decoder_version: "0.4.4".to_string(), + signature: "signature".to_string(), + slot: u64::MAX, + materialized_family: "token_account".to_string(), + payload_json: payload, + created_at: "created".to_string(), + updated_at: "updated".to_string(), + }); + assert_eq!(row.slot, u64::MAX.to_string()); + assert_eq!(row.amount_raw, std::option::Option::Some(u64::MAX.to_string())); + } + + #[test] + fn journal_rejects_unbounded_or_unknown_family_requests() { + let request = crate::DemoSplTokenJournalRequest { + profile_name: "local_devnet".to_string(), + signature_contains: std::option::Option::None, + mint: std::option::Option::None, + account: std::option::Option::None, + family: std::option::Option::Some("fee".to_string()), + operation: std::option::Option::None, + limit: 501, + }; + assert!(crate::validate_journal_request(request).is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_spl_token_2022.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_spl_token_2022.rs new file mode 100644 index 0000000..537066f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_spl_token_2022.rs @@ -0,0 +1,423 @@ +// file: kb_app_demo/src/demo_spl_token_2022.rs +// version: 3 + +//! Thin Tauri adapter for independent public Token-2022 Devnet validation scenarios. + +use ts_rs::TS; // rust-rules: derive-import + +/// Public values loaded from one persisted Token-2022 validation fixture. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_spl_token_2022/DemoSplToken2022FixturePayload.ts" +)] +pub(crate) struct DemoSplToken2022FixturePayload { + /// Fixture file used by the application. + pub(crate) fixture_path: std::string::String, + /// Token-2022 program ID. + pub(crate) program_id: std::string::String, + /// Mint account. + pub(crate) mint: std::string::String, + /// Source token account. + pub(crate) source: std::string::String, + /// Destination token account. + pub(crate) destination: std::string::String, + /// Dedicated empty account reserved for CloseAccount validation. + pub(crate) close_account: std::string::String, + /// Delegate account. + pub(crate) delegate: std::string::String, + /// Profile-wallet authority. + pub(crate) authority: std::string::String, + /// Freeze authority when configured on the mint. + pub(crate) freeze_authority: std::string::String, + /// Mint decimals. + pub(crate) decimals: u8, + /// Default raw amount for the selected scenario. + pub(crate) default_amount_raw: std::string::String, +} + +/// Request for one independent public Token-2022 Devnet scenario. +#[derive(Clone, Debug, serde::Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_spl_token_2022/DemoExecutionSplToken2022Request.ts" +)] +pub(crate) struct DemoExecutionSplToken2022Request { + /// Selected Devnet profile. + pub(crate) profile_name: std::string::String, + /// Stable scenario identifier. + pub(crate) scenario_id: std::string::String, + /// Source or target token account depending on the selected operation. + pub(crate) source: std::string::String, + /// Exact mint account. + pub(crate) mint: std::string::String, + /// Destination token account or close-account lamport destination. + pub(crate) destination: std::string::String, + /// Delegate account used by `ApproveChecked`. + pub(crate) delegate: std::string::String, + /// Owner or mint authority resolved by the selected profile wallet. + pub(crate) authority: std::string::String, + /// Freeze authority used by freeze and thaw scenarios. + pub(crate) freeze_authority: std::string::String, + /// Exact raw amount represented as a decimal string. + pub(crate) amount_raw: std::string::String, + /// Expected mint decimals carried by checked instructions. + pub(crate) decimals: u8, + /// Whether the transaction should be signed and submitted after simulation. + pub(crate) submit: bool, + /// Explicit operator confirmation for signed submission. + pub(crate) operator_confirmed: bool, + /// Whether post-execution core and decode outputs should be force-replayed. + pub(crate) force_post_validation_replay: bool, +} + +/// UI-safe result of one public Token-2022 Devnet orchestration. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_spl_token_2022/DemoExecutionSplToken2022SummaryPayload.ts" +)] +pub(crate) struct DemoExecutionSplToken2022SummaryPayload { + /// Stable scenario identifier. + pub(crate) scenario_id: std::string::String, + /// Stable operation code. + pub(crate) operation: std::string::String, + /// Profile used by the orchestration. + pub(crate) profile_name: std::string::String, + /// Classified cluster code. + pub(crate) cluster: std::string::String, + /// Genesis hash returned by the endpoint. + pub(crate) genesis_hash: std::string::String, + /// Source wallet public key. + pub(crate) wallet_public_key: std::string::String, + /// Exact wallet balance rendered without JSON integer precision loss. + pub(crate) balance_lamports: std::string::String, + /// Estimated fee rendered without JSON integer precision loss. + pub(crate) fee_lamports: std::option::Option, + /// Whether exact simulation succeeded. + pub(crate) simulation_success: bool, + /// Runtime simulation error when present. + pub(crate) simulation_error: std::option::Option, + /// Submitted transaction signature. + pub(crate) transaction_signature: std::option::Option, + /// Terminal confirmation status. + pub(crate) confirmation_status: std::option::Option, + /// Whether canonical hydration was validated. + pub(crate) canonical_inserted: bool, + /// Whether core extraction was validated. + pub(crate) core_extracted: bool, + /// Whether Token-2022 decode replay was validated. + pub(crate) decode_replayed: bool, + /// Number of materialized rows for the submitted instruction. + pub(crate) materialization_count: u32, + /// Whether the second replay produced no failure or new output. + pub(crate) idempotence_validated: bool, + /// Pretty JSON representation of the exact execution plan. + pub(crate) plan_json: std::string::String, + /// Pretty JSON representation of stateful preflight. + pub(crate) preflight_json: std::string::String, + /// Pretty JSON representation of the exact simulation result. + pub(crate) simulation_json: std::string::String, + /// Pretty JSON representation of confirmation and replay diagnostics. + pub(crate) diagnostics_json: std::string::String, +} + +pub(crate) fn operation_from_request( + request: &crate::DemoExecutionSplToken2022Request, +) -> std::result::Result { + let authority = kb_executor_spl_token_2022::SplTokenAuthority { + authority: kb_model::Pubkey(request.authority.trim().to_string()), + multisig_signers: std::vec::Vec::new(), + }; + let freeze_authority = kb_executor_spl_token_2022::SplTokenAuthority { + authority: kb_model::Pubkey(request.freeze_authority.trim().to_string()), + multisig_signers: std::vec::Vec::new(), + }; + let value = match request.scenario_id.trim() { + "token_2022_mint_to_checked" => { + kb_executor_spl_token_2022::SplTokenSingleOperation::MintToChecked { + mint: kb_model::Pubkey(request.mint.trim().to_string()), + destination: kb_model::Pubkey(request.source.trim().to_string()), + authority, + amount: kb_executor_spl_token_2022::SplTokenAmount( + request.amount_raw.trim().to_string(), + ), + decimals: request.decimals, + } + }, + "token_2022_transfer_checked" => { + kb_executor_spl_token_2022::SplTokenSingleOperation::TransferChecked { + source: kb_model::Pubkey(request.source.trim().to_string()), + mint: kb_model::Pubkey(request.mint.trim().to_string()), + destination: kb_model::Pubkey(request.destination.trim().to_string()), + authority, + amount: kb_executor_spl_token_2022::SplTokenAmount( + request.amount_raw.trim().to_string(), + ), + decimals: request.decimals, + } + }, + "token_2022_approve_checked" => { + kb_executor_spl_token_2022::SplTokenSingleOperation::ApproveChecked { + source: kb_model::Pubkey(request.source.trim().to_string()), + mint: kb_model::Pubkey(request.mint.trim().to_string()), + delegate: kb_model::Pubkey(request.delegate.trim().to_string()), + authority, + amount: kb_executor_spl_token_2022::SplTokenAmount( + request.amount_raw.trim().to_string(), + ), + decimals: request.decimals, + } + }, + "token_2022_revoke" => kb_executor_spl_token_2022::SplTokenSingleOperation::Revoke { + source: kb_model::Pubkey(request.source.trim().to_string()), + authority, + }, + "token_2022_burn_checked" => { + kb_executor_spl_token_2022::SplTokenSingleOperation::BurnChecked { + source: kb_model::Pubkey(request.source.trim().to_string()), + mint: kb_model::Pubkey(request.mint.trim().to_string()), + authority, + amount: kb_executor_spl_token_2022::SplTokenAmount( + request.amount_raw.trim().to_string(), + ), + decimals: request.decimals, + } + }, + "token_2022_freeze_account" => { + kb_executor_spl_token_2022::SplTokenSingleOperation::FreezeAccount { + account: kb_model::Pubkey(request.source.trim().to_string()), + mint: kb_model::Pubkey(request.mint.trim().to_string()), + authority: freeze_authority, + } + }, + "token_2022_thaw_account" => { + kb_executor_spl_token_2022::SplTokenSingleOperation::ThawAccount { + account: kb_model::Pubkey(request.source.trim().to_string()), + mint: kb_model::Pubkey(request.mint.trim().to_string()), + authority: freeze_authority, + } + }, + "token_2022_close_destination" => { + kb_executor_spl_token_2022::SplTokenSingleOperation::CloseAccount { + account: kb_model::Pubkey(request.source.trim().to_string()), + destination: kb_model::Pubkey(request.destination.trim().to_string()), + authority, + } + }, + other => { + return std::result::Result::Err(format!( + "unsupported public Token-2022 Devnet scenario {other}" + )); + }, + }; + return std::result::Result::Ok( + kb_executor_spl_token_2022::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(value), + }, + ); +} + +pub(crate) fn demo_spl_token_2022_summary_payload( + scenario_id: std::string::String, + operation: std::string::String, + summary: kb_pipeline::DevnetSplToken2022ExecutionSummary, +) -> crate::DemoExecutionSplToken2022SummaryPayload { + let plan_json = crate::pretty_json(&summary.plan); + let preflight_json = crate::pretty_json(&summary.stateful_preflight); + let simulation_json = crate::pretty_json(&summary.simulation); + let transaction_signature = + summary.send_result.as_ref().map(|value| return value.signature.0.clone()); + let confirmation_status = summary.confirmation.as_ref().map(|value| { + return crate::confirmation_status_code(value.status); + }); + let post_execution = summary.post_execution.as_ref(); + let canonical_inserted = post_execution.is_some_and(|value| return value.canonical_inserted); + let core_extracted = post_execution.is_some_and(|value| return value.core_extracted); + let decode_replayed = post_execution.is_some_and(|value| return value.decode_replayed); + let idempotence_validated = summary.idempotence_replay.as_ref().is_some_and(|replay| { + return replay.failed_inputs == 0 + && !replay.cancelled + && replay.processors.iter().all(|processor| { + return processor.failed == 0 + && processor.materialized_outputs == 0 + && processor.materialization_refused == 0; + }); + }); + let materialization_count = match u32::try_from(summary.materializations.len()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => u32::MAX, + }; + let diagnostics = serde_json::json!({ + "confirmation": summary.confirmation, + "postExecution": summary.post_execution, + "backfill": summary.backfill.as_ref().map(backfill_summary_json), + "coreExtraction": summary.core_extraction.as_ref().map(core_extraction_summary_json), + "decodeReplay": summary.decode_replay.as_ref().map(decode_replay_summary_json), + "idempotenceReplay": summary.idempotence_replay.as_ref().map(decode_replay_summary_json), + "materializations": summary.materializations + }); + return crate::DemoExecutionSplToken2022SummaryPayload { + scenario_id, + operation, + profile_name: summary.profile_name, + cluster: crate::execution_cluster_code(summary.cluster), + genesis_hash: summary.genesis_hash, + wallet_public_key: summary.wallet.public_key, + balance_lamports: summary.balance_lamports.to_string(), + fee_lamports: summary.fee.fee_lamports.map(|value| return value.to_string()), + simulation_success: summary.simulation.success, + simulation_error: summary.simulation.error, + transaction_signature, + confirmation_status, + canonical_inserted, + core_extracted, + decode_replayed, + materialization_count, + idempotence_validated, + plan_json, + preflight_json, + simulation_json, + diagnostics_json: crate::pretty_json(&diagnostics), + }; +} + +pub(crate) fn parse_fixture( + contents: &str, +) -> std::collections::BTreeMap { + let mut values = std::collections::BTreeMap::new(); + for line in contents.lines() { + let trimmed = line.trim(); + let assignment = match trimmed.strip_prefix("export ") { + std::option::Option::Some(value) => value, + std::option::Option::None => continue, + }; + let (name, raw_value) = match assignment.split_once('=') { + std::option::Option::Some(value) => value, + std::option::Option::None => continue, + }; + let value = raw_value.trim().trim_matches('\'').trim_matches('"').to_string(); + values.insert(name.trim().to_string(), value); + } + return values; +} + +fn backfill_summary_json(summary: &kb_pipeline::BackfillSummary) -> serde_json::Value { + return serde_json::json!({ + "captureSessionId": summary.capture_session_id, + "filterCode": summary.filter_code, + "role": summary.role, + "provider": summary.provider, + "endpointCode": summary.endpoint_code, + "pagesFetched": summary.pages_fetched, + "candidatesSelected": summary.candidates_selected, + "candidatesStarted": summary.candidates_started, + "candidatesCompleted": summary.candidates_completed, + "candidatesCancelled": summary.candidates_cancelled, + "candidatesNotStarted": summary.candidates_not_started, + "transactionsReceived": summary.transactions_received, + "canonicalInserted": summary.canonical_inserted, + "canonicalSkipped": summary.canonical_skipped, + "existingSkipped": summary.existing_skipped, + "missing": summary.missing, + "failed": summary.failed, + "observationsInserted": summary.observations_inserted, + "attempts": summary.attempts, + "cancelled": summary.cancelled, + "resumeBeforeSignature": summary.resume_before_signature, + "startedAt": summary.started_at, + "finishedAt": summary.finished_at + }); +} + +fn core_extraction_summary_json(summary: &kb_pipeline::CoreExtractionSummary) -> serde_json::Value { + return serde_json::json!({ + "processorVersion": summary.processor_version, + "selected": summary.selected, + "started": summary.started, + "completed": summary.completed, + "skipped": summary.skipped, + "extracted": summary.extracted, + "failed": summary.failed, + "cancelledCandidates": summary.cancelled_candidates, + "notStarted": summary.not_started, + "cancelled": summary.cancelled, + "startedAt": summary.started_at, + "finishedAt": summary.finished_at + }); +} + +fn decode_replay_summary_json(summary: &kb_pipeline::DecodeReplaySummary) -> serde_json::Value { + let processors = summary + .processors + .iter() + .map(|processor| { + return serde_json::json!({ + "name": processor.processor_name, + "version": processor.processor_version, + "dispatched": processor.dispatched, + "skipped": processor.skipped, + "decoded": processor.decoded, + "ignored": processor.ignored, + "unsupported": processor.unsupported, + "failed": processor.failed, + "materializedOutputs": processor.materialized_outputs, + "materializationRefused": processor.materialization_refused + }); + }) + .collect::>(); + return serde_json::json!({ + "campaignId": summary.campaign_id, + "pipelineVersion": summary.pipeline_version, + "selected": summary.selected, + "started": summary.started, + "completed": summary.completed, + "unmatched": summary.unmatched, + "notStarted": summary.not_started, + "failedInputs": summary.failed_inputs, + "cancelled": summary.cancelled, + "processors": processors, + "startedAt": summary.started_at, + "finishedAt": summary.finished_at + }); +} + +#[cfg(test)] +mod tests { + fn request(scenario_id: &str) -> crate::DemoExecutionSplToken2022Request { + return crate::DemoExecutionSplToken2022Request { + profile_name: "local_devnet".to_string(), + scenario_id: scenario_id.to_string(), + source: "source".to_string(), + mint: "mint".to_string(), + destination: "destination".to_string(), + delegate: "delegate".to_string(), + authority: "authority".to_string(), + freeze_authority: "freeze".to_string(), + amount_raw: "1".to_string(), + decimals: 9, + submit: false, + operator_confirmed: false, + force_post_validation_replay: true, + }; + } + + #[test] + fn every_public_scenario_builds_one_typed_operation() { + for scenario in [ + "token_2022_mint_to_checked", + "token_2022_transfer_checked", + "token_2022_approve_checked", + "token_2022_revoke", + "token_2022_burn_checked", + "token_2022_freeze_account", + "token_2022_thaw_account", + "token_2022_close_destination", + ] { + assert!(crate::operation_from_request(&request(scenario)).is_ok()); + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_common.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_common.rs new file mode 100644 index 0000000..3cd55ca --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_common.rs @@ -0,0 +1,349 @@ +// file: kb_app_demo/src/demo_sql_common.rs +// version: 7 + +//! Shared SQL demo helpers and serializable payloads. + +use tauri::Manager; // rust-rules: trait-import +use ts_rs::TS; // rust-rules: derive-import + +/// UI-safe table diagnostics shown by SQL demo windows. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_sql/DemoSqlTableSnapshot.ts" +)] +pub(crate) struct DemoSqlTableSnapshot { + /// Table name. + pub(crate) table_name: std::string::String, + /// Logical Solana domain encoded in the table name. + pub(crate) domain: std::string::String, + /// Human-readable table role. + pub(crate) role: std::string::String, + /// Whether the table currently exists. + pub(crate) exists: bool, + /// Row count when the table exists. + #[ts(type = "number | null")] + pub(crate) row_count: std::option::Option, + /// Minimum slot when available. + #[ts(type = "number | null")] + pub(crate) min_slot: std::option::Option, + /// Maximum slot when available. + #[ts(type = "number | null")] + pub(crate) max_slot: std::option::Option, + /// Latest insertion timestamp rendered by PostgreSQL when available. + pub(crate) latest_created_at: std::option::Option, +} + +/// Opens or focuses a SQL demo window. +pub(crate) fn open_sql_demo_window( + app_handle: tauri::AppHandle, + label: &str, + html_path: &str, + title: &str, +) -> std::result::Result<(), std::string::String> { + tracing::info!(target: crate::TRACING_TARGET, window = label, "open SQL demo window"); + let existing_window = app_handle.get_webview_window(label); + if let std::option::Option::Some(window) = existing_window { + let show_result = window.show(); + if let std::result::Result::Err(error) = show_result { + return std::result::Result::Err( + kb_core::Error::tauri(format!("cannot show {label} window: {error}")).to_string(), + ); + } + let focus_result = window.set_focus(); + if let std::result::Result::Err(error) = focus_result { + return std::result::Result::Err( + kb_core::Error::tauri(format!("cannot focus {label} window: {error}")).to_string(), + ); + } + return std::result::Result::Ok(()); + } + let builder = tauri::WebviewWindowBuilder::new( + &app_handle, + label, + tauri::WebviewUrl::App(html_path.into()), + ) + .title(title) + .inner_size(1220.0, 780.0) + .min_inner_size(920.0, 560.0) + .resizable(true) + .visible(true); + let build_result = builder.build(); + return match build_result { + std::result::Result::Ok(window) => { + let focus_result = window.set_focus(); + if let std::result::Result::Err(error) = focus_result { + return std::result::Result::Err( + kb_core::Error::tauri(format!("cannot focus created {label} window: {error}")) + .to_string(), + ); + } + std::result::Result::Ok(()) + }, + std::result::Result::Err(error) => std::result::Result::Err( + kb_core::Error::tauri(format!("cannot create {label} window: {error}")).to_string(), + ), + }; +} + +/// Connects to PostgreSQL without reapplying schemas initialized at application startup. +pub(crate) async fn connect_postgres_store( + profile: &kb_config::ProfileConfig, +) -> std::result::Result { + if !profile.database.enabled { + return std::result::Result::Err(std::string::String::from( + "database configuration is disabled in the active profile", + )); + } + let options_result = kb_store_pg::PostgresStoreOptions::from_profile_config(profile); + let mut options = match options_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + options.auto_initialize_schema = false; + let store_result = kb_store_pg::PostgresStore::connect(options).await; + return match store_result { + std::result::Result::Ok(store) => std::result::Result::Ok(store), + std::result::Result::Err(error) => std::result::Result::Err(error.to_string()), + }; +} + +/// Converts store table diagnostics to the UI payload shape. +pub(crate) fn table_snapshot_from_pg( + value: &kb_store_pg::PostgresTableDiagnostics, +) -> crate::DemoSqlTableSnapshot { + let row_count = match &value.statistics { + std::option::Option::Some(statistics) => std::option::Option::Some(statistics.row_count), + std::option::Option::None => std::option::Option::None, + }; + let min_slot = match &value.statistics { + std::option::Option::Some(statistics) => statistics.min_slot, + std::option::Option::None => std::option::Option::None, + }; + let max_slot = match &value.statistics { + std::option::Option::Some(statistics) => statistics.max_slot, + std::option::Option::None => std::option::Option::None, + }; + let latest_created_at = match &value.statistics { + std::option::Option::Some(statistics) => statistics.latest_created_at.clone(), + std::option::Option::None => std::option::Option::None, + }; + return crate::DemoSqlTableSnapshot { + table_name: value.table_name.clone(), + domain: value.domain.clone(), + role: value.role.clone(), + exists: value.exists, + row_count, + min_slot, + max_slot, + latest_created_at, + }; +} + +/// Converts many table diagnostics to UI payloads. +pub(crate) fn table_snapshots_from_pg( + values: &[kb_store_pg::PostgresTableDiagnostics], +) -> std::vec::Vec { + let mut output = std::vec::Vec::new(); + for value in values { + output.push(table_snapshot_from_pg(value)); + } + return output; +} + +/// Initializes PostgreSQL raw, core, decode and materialization schemas during Tauri startup. +pub(crate) async fn initialize_postgres_schema_for_startup( + state: &crate::AppState, + splash_window: &tauri::WebviewWindow, +) { + let profile = state.active_profile(); + if !profile.database.enabled { + emit_sql_startup_splash( + splash_window, + "SQL store disabled; schema initialization skipped.", + "info", + false, + ); + tracing::debug!(target: crate::TRACING_TARGET, "database is disabled; skip SQL schema initialization"); + return; + } + if profile.database.backend != "postgres" { + emit_sql_startup_splash( + splash_window, + "SQL backend is not PostgreSQL; schema initialization skipped.", + "info", + false, + ); + tracing::debug!(target: crate::TRACING_TARGET, backend = profile.database.backend.as_str(), "database backend is not postgres; skip SQL schema initialization"); + return; + } + let options_result = kb_store_pg::PostgresStoreOptions::from_profile_config(profile); + let mut options = match options_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + let error_message = error.to_string(); + emit_sql_startup_error(splash_window, error_message.as_str()); + tracing::error!(target: crate::TRACING_TARGET, error = error_message.as_str(), "cannot build PostgreSQL store options"); + return; + }, + }; + if !options.auto_initialize_schema { + emit_sql_startup_splash( + splash_window, + "PostgreSQL schema auto-initialization disabled.", + "info", + false, + ); + let masked_dsn = options.masked_dsn(); + tracing::debug!(target: crate::TRACING_TARGET, dsn = masked_dsn.as_str(), "PostgreSQL schema auto-initialization disabled"); + return; + } + let masked_dsn = options.masked_dsn(); + options.auto_initialize_schema = false; + emit_sql_startup_splash( + splash_window, + "Checking PostgreSQL raw/core/decode tables...", + "info", + true, + ); + tracing::debug!(target: crate::TRACING_TARGET, dsn = masked_dsn.as_str(), "start PostgreSQL schema initialization"); + let store_result = kb_store_pg::PostgresStore::connect(options).await; + let store = match store_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + let error_message = error.to_string(); + emit_sql_startup_error(splash_window, error_message.as_str()); + tracing::error!(target: crate::TRACING_TARGET, error = error_message.as_str(), "PostgreSQL connection failed during startup schema initialization"); + return; + }, + }; + let before_result = store.known_table_diagnostics().await; + let before_tables = match before_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + let error_message = error.to_string(); + emit_sql_startup_error(splash_window, error_message.as_str()); + tracing::error!(target: crate::TRACING_TARGET, error = error_message.as_str(), "cannot read table diagnostics before schema initialization"); + return; + }, + }; + let initialize_result = store.initialize_store_schema().await; + if let std::result::Result::Err(error) = initialize_result { + let error_message = error.to_string(); + emit_sql_startup_error(splash_window, error_message.as_str()); + tracing::error!(target: crate::TRACING_TARGET, error = error_message.as_str(), "PostgreSQL store schema initialization failed"); + return; + } + let after_result = store.known_table_diagnostics().await; + let after_tables = match after_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + let error_message = error.to_string(); + emit_sql_startup_error(splash_window, error_message.as_str()); + tracing::error!(target: crate::TRACING_TARGET, error = error_message.as_str(), "cannot read table diagnostics after schema initialization"); + return; + }, + }; + emit_sql_startup_table_report(splash_window, before_tables.as_slice(), after_tables.as_slice()); +} + +fn emit_sql_startup_table_report( + splash_window: &tauri::WebviewWindow, + before_tables: &[kb_store_pg::PostgresTableDiagnostics], + after_tables: &[kb_store_pg::PostgresTableDiagnostics], +) { + let mut created_count = 0_u32; + let mut existing_count = 0_u32; + let mut missing_count = 0_u32; + for table in after_tables { + let before_exists = table_exists_in(before_tables, table.table_name.as_str()); + if table.exists && before_exists == std::option::Option::Some(false) { + created_count += 1; + emit_sql_startup_splash( + splash_window, + format!("Created table {}", table.table_name).as_str(), + "success", + true, + ); + tracing::debug!(target: crate::TRACING_TARGET, table = table.table_name.as_str(), "PostgreSQL table created"); + } else if table.exists { + existing_count += 1; + emit_sql_startup_splash( + splash_window, + format!("Table {} already exists", table.table_name).as_str(), + "info", + true, + ); + tracing::debug!(target: crate::TRACING_TARGET, table = table.table_name.as_str(), "PostgreSQL table already exists"); + } else { + missing_count += 1; + emit_sql_startup_splash( + splash_window, + format!("Table {} is missing after initialization", table.table_name).as_str(), + "warning", + true, + ); + tracing::error!(target: crate::TRACING_TARGET, table = table.table_name.as_str(), "PostgreSQL table missing after initialization"); + } + } + emit_sql_startup_splash( + splash_window, + format!( + "PostgreSQL schema ready: {created_count} created, {existing_count} already present, {missing_count} missing." + ) + .as_str(), + schema_report_status(missing_count), + false, + ); + tracing::debug!(target: crate::TRACING_TARGET, created = created_count, existing = existing_count, missing = missing_count, "PostgreSQL schema initialization completed"); +} + +fn table_exists_in( + tables: &[kb_store_pg::PostgresTableDiagnostics], + table_name: &str, +) -> std::option::Option { + for table in tables { + if table.table_name.as_str() == table_name { + return std::option::Option::Some(table.exists); + } + } + return std::option::Option::None; +} + +fn schema_report_status(missing_count: u32) -> &'static str { + if missing_count == 0 { + return "success"; + } + return "warning"; +} + +fn emit_sql_startup_error(splash_window: &tauri::WebviewWindow, message: &str) { + emit_sql_startup_splash( + splash_window, + format!("PostgreSQL schema initialization error: {message}").as_str(), + "danger", + false, + ); +} + +fn emit_sql_startup_splash( + splash_window: &tauri::WebviewWindow, + message: &str, + status: &str, + log_only: bool, +) { + let order = if log_only { "add_log" } else { "add_msg" }; + crate::emit_splash_order( + splash_window, + order, + std::option::Option::Some(message), + std::option::Option::Some(status), + std::option::Option::None, + ); +} + +/// Formats debug enums for UI display. +pub(crate) fn debug_status(value: T) -> std::string::String { + return format!("{value:?}"); +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_diag.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_diag.rs new file mode 100644 index 0000000..dabad29 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_diag.rs @@ -0,0 +1,38 @@ +// file: kb_app_demo/src/demo_sql_diag.rs +// version: 2 + +//! SQL diagnostic demo commands. + +use ts_rs::TS; // rust-rules: derive-import + +/// Complete SQL diagnostic payload shown by `demo_sql_diag`. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_sql_diag/DemoSqlDiagPayload.ts" +)] +pub(crate) struct DemoSqlDiagPayload { + /// Configuration file path. + pub(crate) config_path: std::string::String, + /// Active profile name. + pub(crate) active_profile_name: std::string::String, + /// Configured database backend. + pub(crate) backend: std::string::String, + /// Masked PostgreSQL DSN. + pub(crate) masked_dsn: std::string::String, + /// PostgreSQL current schema. + pub(crate) current_schema: std::option::Option, + /// PostgreSQL health status. + pub(crate) health_status: std::string::String, + /// PostgreSQL health message. + pub(crate) health_message: std::option::Option, + /// Migration status. + pub(crate) migration_status: std::string::String, + /// Migration diagnostic message. + pub(crate) migration_message: std::option::Option, + /// PostgreSQL server version when available. + pub(crate) server_version: std::option::Option, + /// Known raw/core table diagnostics. + pub(crate) tables: std::vec::Vec, +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_pg_core.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_pg_core.rs new file mode 100644 index 0000000..4a9fd31 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_pg_core.rs @@ -0,0 +1,22 @@ +// file: kb_app_demo/src/demo_sql_pg_core.rs +// version: 3 + +//! PostgreSQL core store demo commands. + +use ts_rs::TS; // rust-rules: derive-import + +/// PostgreSQL core store payload shown by `demo_sql_pg_core`. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_sql_pg_core/DemoSqlPgCorePayload.ts" +)] +pub(crate) struct DemoSqlPgCorePayload { + /// Active profile name. + pub(crate) active_profile_name: std::string::String, + /// Masked PostgreSQL DSN. + pub(crate) masked_dsn: std::string::String, + /// Core table diagnostics. + pub(crate) tables: std::vec::Vec, +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_pg_raw.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_pg_raw.rs new file mode 100644 index 0000000..4455b4d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_pg_raw.rs @@ -0,0 +1,22 @@ +// file: kb_app_demo/src/demo_sql_pg_raw.rs +// version: 4 + +//! PostgreSQL canonical acquisition store demo commands. + +use ts_rs::TS; // rust-rules: derive-import + +/// PostgreSQL canonical acquisition payload shown by `demo_sql_pg_raw`. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_sql_pg_raw/DemoSqlPgRawPayload.ts" +)] +pub(crate) struct DemoSqlPgRawPayload { + /// Active profile name. + pub(crate) active_profile_name: std::string::String, + /// Masked PostgreSQL DSN. + pub(crate) masked_dsn: std::string::String, + /// Raw table diagnostics. + pub(crate) tables: std::vec::Vec, +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_replay_candidates.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_replay_candidates.rs new file mode 100644 index 0000000..1adab5e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_sql_replay_candidates.rs @@ -0,0 +1,409 @@ +// file: kb_app_demo/src/demo_sql_replay_candidates.rs +// version: 6 + +//! Read-only SQL replay candidate browser commands. + +use ts_rs::TS; // rust-rules: derive-import + +/// One program identifier exposed by the runtime `kb_program_ids` registry. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_sql_replay_candidates/DemoSqlReplayKnownProgramOption.ts" +)] +pub(crate) struct DemoSqlReplayKnownProgramOption { + /// Stable lower snake case program code. + pub(crate) code: std::string::String, + /// Base58 Solana program identifier. + pub(crate) program_id: std::string::String, +} + +/// Static options for the replay candidate browser. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_sql_replay_candidates/DemoSqlReplayOptionsPayload.ts" +)] +pub(crate) struct DemoSqlReplayOptionsPayload { + /// Active profile name. + pub(crate) active_profile_name: std::string::String, + /// Masked PostgreSQL DSN. + pub(crate) masked_dsn: std::string::String, + /// Maximum rows accepted by one query. + pub(crate) maximum_limit: u32, + /// Program identifiers enumerable from `kb_program_ids`. + pub(crate) known_programs: std::vec::Vec, +} + +/// UI request for bounded transaction replay candidates. +#[derive(Clone, Debug, serde::Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_sql_replay_candidates/DemoSqlReplayTransactionRequest.ts" +)] +pub(crate) struct DemoSqlReplayTransactionRequest { + /// Optional partial signature search. + pub(crate) signature_contains: std::option::Option, + /// Optional inclusive minimum slot. + #[ts(type = "number | null")] + pub(crate) min_slot: std::option::Option, + /// Optional inclusive maximum slot. + #[ts(type = "number | null")] + pub(crate) max_slot: std::option::Option, + /// Optional raw processing state. + pub(crate) raw_processing_state: std::option::Option, + /// Optional latest ledger status. + pub(crate) ledger_status: std::option::Option, + /// Optional exact program id. + pub(crate) program_id: std::option::Option, + /// Program scope code: any, outer, inner or logs. + pub(crate) program_scope: std::string::String, + /// Optional entity kind: mint, owner or account_key. + pub(crate) entity_kind: std::option::Option, + /// Optional exact entity value. + pub(crate) entity_value: std::option::Option, + /// Maximum returned rows. + pub(crate) limit: u32, + /// Orders newest slots first when true. + pub(crate) newest_first: bool, +} + +/// One transaction row shown by the replay candidate browser. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_sql_replay_candidates/DemoSqlReplayTransactionRow.ts" +)] +pub(crate) struct DemoSqlReplayTransactionRow { + /// Canonical transaction signature. + pub(crate) signature: std::string::String, + /// Transaction slot. + #[ts(type = "number")] + pub(crate) slot: i64, + /// Current raw processing state. + pub(crate) raw_processing_state: std::string::String, + /// Current raw retention state. + pub(crate) retention_state: std::string::String, + /// Whether a core transaction exists. + pub(crate) has_core_transaction: bool, + /// Core transaction failure flag when available. + pub(crate) transaction_failed: std::option::Option, + /// Latest core extraction ledger status. + pub(crate) ledger_status: std::string::String, + /// Latest processor version when available. + pub(crate) processor_version: std::option::Option, + /// Latest attempt count. + pub(crate) attempt_count: i32, + /// Number of top-level instructions. + #[ts(type = "number")] + pub(crate) outer_instruction_count: i64, + /// Number of inner instructions. + #[ts(type = "number")] + pub(crate) inner_instruction_count: i64, + /// Number of distinct top-level programs. + #[ts(type = "number")] + pub(crate) outer_program_count: i64, + /// Number of distinct inner programs. + #[ts(type = "number")] + pub(crate) inner_program_count: i64, + /// Raw row update timestamp. + pub(crate) updated_at: std::string::String, +} + +/// UI request for bounded program summaries. +#[derive(Clone, Debug, serde::Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_sql_replay_candidates/DemoSqlReplayProgramRequest.ts" +)] +pub(crate) struct DemoSqlReplayProgramRequest { + /// Optional partial program id search. + pub(crate) program_id_contains: std::option::Option, + /// Maximum returned rows. + pub(crate) limit: u32, +} + +/// Aggregated program row shown by the replay candidate browser. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_sql_replay_candidates/DemoSqlReplayProgramRow.ts" +)] +pub(crate) struct DemoSqlReplayProgramRow { + /// Optional stable code from `kb_program_ids`. + pub(crate) program_code: std::option::Option, + /// Program id. + pub(crate) program_id: std::string::String, + /// Number of distinct transactions. + #[ts(type = "number")] + pub(crate) transaction_count: i64, + /// Number of top-level instruction occurrences. + #[ts(type = "number")] + pub(crate) outer_instruction_count: i64, + /// Number of inner instruction occurrences. + #[ts(type = "number")] + pub(crate) inner_instruction_count: i64, + /// Number of reliably linked log occurrences. + #[ts(type = "number")] + pub(crate) log_count: i64, + /// Lowest observed slot. + #[ts(type = "number")] + pub(crate) min_slot: i64, + /// Highest observed slot. + #[ts(type = "number")] + pub(crate) max_slot: i64, +} + +/// UI request for bounded core entity summaries. +#[derive(Clone, Debug, serde::Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_sql_replay_candidates/DemoSqlReplayEntityRequest.ts" +)] +pub(crate) struct DemoSqlReplayEntityRequest { + /// Entity kind: mint, owner or account_key. + pub(crate) entity_kind: std::string::String, + /// Optional partial entity value search. + pub(crate) entity_value_contains: std::option::Option, + /// Maximum returned rows. + pub(crate) limit: u32, +} + +/// Aggregated core entity row shown by the replay candidate browser. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_sql_replay_candidates/DemoSqlReplayEntityRow.ts" +)] +pub(crate) struct DemoSqlReplayEntityRow { + /// Stable entity kind code. + pub(crate) entity_kind: std::string::String, + /// Mint, owner or account-key address. + pub(crate) entity_value: std::string::String, + /// Number of distinct transactions. + #[ts(type = "number")] + pub(crate) transaction_count: i64, + /// Total number of core-table occurrences. + #[ts(type = "number")] + pub(crate) occurrence_count: i64, + /// Lowest observed slot. + #[ts(type = "number")] + pub(crate) min_slot: i64, + /// Highest observed slot. + #[ts(type = "number")] + pub(crate) max_slot: i64, +} + +pub(crate) fn transaction_row_from_pg( + row: kb_store_pg::PostgresReplayTransactionCandidate, +) -> crate::DemoSqlReplayTransactionRow { + return crate::DemoSqlReplayTransactionRow { + signature: row.signature, + slot: row.slot, + raw_processing_state: row.raw_processing_state, + retention_state: row.retention_state, + has_core_transaction: row.has_core_transaction, + transaction_failed: row.transaction_failed, + ledger_status: row.ledger_status, + processor_version: row.processor_version, + attempt_count: row.attempt_count, + outer_instruction_count: row.outer_instruction_count, + inner_instruction_count: row.inner_instruction_count, + outer_program_count: row.outer_program_count, + inner_program_count: row.inner_program_count, + updated_at: row.updated_at, + }; +} + +pub(crate) fn program_row_from_pg( + row: kb_store_pg::PostgresReplayProgramSummary, +) -> crate::DemoSqlReplayProgramRow { + let program_code = kb_program_ids::find_registered_program_id(&row.program_id) + .map(|entry| return entry.code().to_owned()); + return crate::DemoSqlReplayProgramRow { + program_code, + program_id: row.program_id, + transaction_count: row.transaction_count, + outer_instruction_count: row.outer_instruction_count, + inner_instruction_count: row.inner_instruction_count, + log_count: row.log_count, + min_slot: row.min_slot, + max_slot: row.max_slot, + }; +} + +pub(crate) fn entity_row_from_pg( + row: kb_store_pg::PostgresReplayEntitySummary, +) -> crate::DemoSqlReplayEntityRow { + return crate::DemoSqlReplayEntityRow { + entity_kind: row.entity_kind, + entity_value: row.entity_value, + transaction_count: row.transaction_count, + occurrence_count: row.occurrence_count, + min_slot: row.min_slot, + max_slot: row.max_slot, + }; +} + +pub(crate) fn validated_csv_file_name( + file_name: &str, +) -> std::result::Result<&'static str, std::string::String> { + return match file_name { + "replay_transactions.csv" => std::result::Result::Ok("replay_transactions.csv"), + "replay_programs.csv" => std::result::Result::Ok("replay_programs.csv"), + "replay_mints.csv" => std::result::Result::Ok("replay_mints.csv"), + "replay_owners.csv" => std::result::Result::Ok("replay_owners.csv"), + "replay_account_keys.csv" => std::result::Result::Ok("replay_account_keys.csv"), + _ => std::result::Result::Err(format!("unsupported replay CSV file name: {file_name}")), + }; +} + +pub(crate) fn csv_export_directory_from_current_dir( + current_dir: &std::path::Path, +) -> std::path::PathBuf { + let current_name = current_dir.file_name().and_then(std::ffi::OsStr::to_str); + if let (std::option::Option::Some("kb_app_demo"), std::option::Option::Some(parent)) = + (current_name, current_dir.parent()) + { + return parent.join("data").join("exports_csv"); + } + return current_dir.join("data").join("exports_csv"); +} + +pub(crate) async fn available_csv_export_path( + export_dir: &std::path::Path, + file_name: &str, +) -> std::result::Result { + let direct_path = export_dir.join(file_name); + let direct_exists_result = tokio::fs::try_exists(&direct_path).await; + let direct_exists = match direct_exists_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!("cannot inspect CSV export path: {error}")); + }, + }; + if !direct_exists { + return std::result::Result::Ok(direct_path); + } + let stem = file_name.trim_end_matches(".csv"); + for suffix in 1_u16..=999_u16 { + let candidate = export_dir.join(format!("{stem}_{suffix}.csv")); + let exists_result = tokio::fs::try_exists(&candidate).await; + let exists = match exists_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "cannot inspect CSV export path: {error}" + )); + }, + }; + if !exists { + return std::result::Result::Ok(candidate); + } + } + return std::result::Result::Err("cannot allocate a unique CSV export path".to_owned()); +} + +pub(crate) fn program_scope_from_code( + code: &str, +) -> std::result::Result { + return match code { + "any" => std::result::Result::Ok(kb_store_pg::PostgresReplayProgramScope::Any), + "outer" => std::result::Result::Ok(kb_store_pg::PostgresReplayProgramScope::Outer), + "inner" => std::result::Result::Ok(kb_store_pg::PostgresReplayProgramScope::Inner), + "logs" => std::result::Result::Ok(kb_store_pg::PostgresReplayProgramScope::Logs), + _ => std::result::Result::Err(format!("unsupported replay program scope: {code}")), + }; +} + +pub(crate) fn optional_entity_kind_from_code( + code: std::option::Option<&str>, +) -> std::result::Result< + std::option::Option, + std::string::String, +> { + return match code { + std::option::Option::Some(value) => { + let result = entity_kind_from_code(value); + match result { + std::result::Result::Ok(kind) => { + std::result::Result::Ok(std::option::Option::Some(kind)) + }, + std::result::Result::Err(error) => std::result::Result::Err(error), + } + }, + std::option::Option::None => std::result::Result::Ok(std::option::Option::None), + }; +} + +pub(crate) fn entity_kind_from_code( + code: &str, +) -> std::result::Result { + return match code { + "mint" => std::result::Result::Ok(kb_store_pg::PostgresReplayEntityKind::Mint), + "owner" => std::result::Result::Ok(kb_store_pg::PostgresReplayEntityKind::Owner), + "account_key" => std::result::Result::Ok(kb_store_pg::PostgresReplayEntityKind::AccountKey), + _ => std::result::Result::Err(format!("unsupported replay entity kind: {code}")), + }; +} + +#[cfg(test)] +mod tests { + #[test] + fn program_scope_parser_accepts_logs() { + let result = crate::program_scope_from_code("logs"); + assert_eq!(result, std::result::Result::Ok(kb_store_pg::PostgresReplayProgramScope::Logs)); + } + + #[test] + fn entity_kind_parser_rejects_unknown_code() { + let result = crate::entity_kind_from_code("unknown"); + assert!(result.is_err()); + } + + #[test] + fn csv_file_name_rejects_unknown_name() { + let result = crate::validated_csv_file_name("arbitrary.csv"); + assert!(result.is_err()); + } + + #[test] + fn csv_export_directory_uses_workspace_root_from_demo_crate() { + let current_dir = std::path::Path::new("/tmp/khadhroony-bot2/kb_app_demo"); + let result = crate::csv_export_directory_from_current_dir(current_dir); + assert_eq!(result, std::path::Path::new("/tmp/khadhroony-bot2/data/exports_csv")); + } + + #[test] + fn csv_file_name_accepts_split_entity_exports() { + let mint_result = crate::validated_csv_file_name("replay_mints.csv"); + let owner_result = crate::validated_csv_file_name("replay_owners.csv"); + let account_result = crate::validated_csv_file_name("replay_account_keys.csv"); + assert!(mint_result.is_ok()); + assert!(owner_result.is_ok()); + assert!(account_result.is_ok()); + } + + #[test] + fn program_row_exposes_registered_code() { + let row = kb_store_pg::PostgresReplayProgramSummary { + program_id: kb_program_ids::SYSTEM_PROGRAM_ID.to_owned(), + transaction_count: 1, + outer_instruction_count: 1, + inner_instruction_count: 0, + log_count: 1, + min_slot: 1, + max_slot: 1, + }; + let converted = crate::program_row_from_pg(row); + assert_eq!(converted.program_code.as_deref(), std::option::Option::Some("system")); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_ws.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_ws.rs new file mode 100644 index 0000000..7995b62 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/demo_ws.rs @@ -0,0 +1,869 @@ +// file: kb_app_demo/src/demo_ws.rs +// version: 13 + +//! Standard Solana WebSocket demo commands backed by `kb_rpc::WsSession`. + +use tauri::Emitter; // rust-rules: trait-import +use tauri::Manager; // rust-rules: trait-import +use ts_rs::TS; // rust-rules: derive-import + +/// One selectable role shown by the WebSocket demo. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_ws/DemoWsRoleOption.ts" +)] +pub(crate) struct DemoWsRoleOption { + /// Endpoint role code. + pub(crate) role: std::string::String, + /// Request kinds accepted by this role. + pub(crate) request_kinds: std::vec::Vec, +} + +/// One selectable WebSocket JSON-RPC method shown by the WebSocket demo. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_ws/DemoWsMethodOption.ts" +)] +pub(crate) struct DemoWsMethodOption { + /// JSON-RPC subscribe method name. + pub(crate) method: std::string::String, + /// Matching unsubscribe method name. + pub(crate) unsubscribe_method: std::string::String, + /// Derived request kind used for endpoint routing. + pub(crate) request_kind: std::string::String, + /// Human-readable method label. + pub(crate) label: std::string::String, + /// Whether this method needs the target field. + pub(crate) requires_target: bool, + /// Whether this method needs the filter JSON field. + pub(crate) requires_filter_json: bool, + /// Whether this method supports an optional configuration object. + pub(crate) supports_config_json: bool, +} + +/// WebSocket demo options derived from configuration and the standard registry. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_ws/DemoWsOptionsPayload.ts" +)] +pub(crate) struct DemoWsOptionsPayload { + /// Selectable roles. + pub(crate) roles: std::vec::Vec, + /// Selectable methods. + pub(crate) methods: std::vec::Vec, +} + +/// Request payload for one WebSocket subscription demo command. +#[derive(Clone, Debug, serde::Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_ws/DemoWsRequest.ts" +)] +pub(crate) struct DemoWsRequest { + /// Required endpoint role used by the WebSocket pool. + pub(crate) role: std::string::String, + /// JSON-RPC WebSocket subscribe method name. + pub(crate) method: std::string::String, + /// Optional target string used by account/program/signature subscriptions. + pub(crate) target: std::option::Option, + /// Optional JSON filter string used by logsSubscribe or blockSubscribe. + pub(crate) filter_json: std::option::Option, + /// Optional JSON configuration string. + pub(crate) config_json: std::option::Option, +} + +/// Response payload for one WebSocket subscription response. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_ws/DemoWsExecutionPayload.ts" +)] +pub(crate) struct DemoWsExecutionPayload { + /// Selected endpoint name. + pub(crate) endpoint_name: std::string::String, + /// Selected provider name. + pub(crate) provider: std::string::String, + /// Selected endpoint URL. + pub(crate) endpoint_url: std::string::String, + /// Required role used by the selection. + pub(crate) role: std::string::String, + /// JSON-RPC method name. + pub(crate) method: std::string::String, + /// Derived request kind. + pub(crate) request_kind: std::string::String, + /// Parsed response kind. + pub(crate) response_kind: std::string::String, + /// Remote subscription id returned by the node. + #[ts(type = "number | null")] + pub(crate) subscription_id: std::option::Option, + /// Pretty JSON response text. + pub(crate) response_json: std::string::String, +} + +/// Current WebSocket demo session status. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_ws/DemoWsStatusPayload.ts" +)] +pub(crate) struct DemoWsStatusPayload { + /// Whether a session is currently connected. + pub(crate) connected: bool, + /// Selected endpoint name. + pub(crate) endpoint_name: std::option::Option, + /// Selected endpoint URL. + pub(crate) endpoint_url: std::option::Option, + /// Last subscription method. + pub(crate) method: std::option::Option, + /// Last unsubscribe method. + pub(crate) unsubscribe_method: std::option::Option, + /// Last remote subscription id. + #[ts(type = "number | null")] + pub(crate) subscription_id: std::option::Option, + /// Number of active subscriptions on the current connection. + pub(crate) subscription_count: u32, + /// Active subscriptions kept by the session. + pub(crate) subscriptions: std::vec::Vec, +} + +/// One active WebSocket subscription shown by the demo UI. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_ws/DemoWsSubscriptionStatusPayload.ts" +)] +pub(crate) struct DemoWsSubscriptionStatusPayload { + /// Remote subscription id. + #[ts(type = "number")] + pub(crate) subscription_id: u64, + /// Subscribe method used to create this subscription. + pub(crate) method: std::string::String, + /// Matching unsubscribe method. + pub(crate) unsubscribe_method: std::string::String, +} + +/// Request payload for one explicit WebSocket unsubscribe command. +#[derive(Clone, Debug, serde::Deserialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_ws/DemoWsUnsubscribeRequest.ts" +)] +pub(crate) struct DemoWsUnsubscribeRequest { + /// Remote subscription id to unsubscribe. + #[ts(type = "number")] + pub(crate) subscription_id: u64, +} + +/// WebSocket event payload emitted to the demo window. +#[derive(Clone, Debug, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/demo_ws/DemoWsMessagePayload.ts" +)] +pub(crate) struct DemoWsMessagePayload { + /// Message kind. + pub(crate) kind: std::string::String, + /// Pretty JSON or text payload. + pub(crate) payload_json: std::string::String, +} + +const DEMO_WS_UI_RATE_WINDOW_MS: u64 = 1000; +const DEMO_WS_UI_MAX_MESSAGES_PER_WINDOW: u32 = 12; +const DEMO_WS_UI_MAX_PAYLOAD_CHARS: usize = 8_000; +const DEMO_WS_RECONNECT_ATTEMPTS: u32 = 3; +const DEMO_WS_RECONNECT_INITIAL_DELAY_MS: u64 = 250; +const DEMO_WS_RECONNECT_MAX_DELAY_MS: u64 = 2_000; + +struct DemoWsUiRateLimiter { + window_started_at: std::time::Instant, + emitted_in_window: u32, + dropped_in_window: u32, +} + +impl DemoWsUiRateLimiter { + fn new() -> Self { + return Self { + window_started_at: std::time::Instant::now(), + emitted_in_window: 0, + dropped_in_window: 0, + }; + } + + fn should_emit(&mut self, app_handle: &tauri::AppHandle) -> bool { + let now = std::time::Instant::now(); + if now.duration_since(self.window_started_at) + >= std::time::Duration::from_millis(DEMO_WS_UI_RATE_WINDOW_MS) + { + if self.dropped_in_window > 0 { + emit_demo_ws_message( + app_handle, + "throttled", + format!( + "{} WebSocket notification(s) masquée(s) pendant la dernière fenêtre UI.", + self.dropped_in_window + ), + ); + } + self.window_started_at = now; + self.emitted_in_window = 0; + self.dropped_in_window = 0; + } + if self.emitted_in_window >= DEMO_WS_UI_MAX_MESSAGES_PER_WINDOW { + self.dropped_in_window = self.dropped_in_window.saturating_add(1); + return false; + } + self.emitted_in_window = self.emitted_in_window.saturating_add(1); + return true; + } +} + +pub(crate) async fn demo_ws_status_inner( + state: &crate::AppState, +) -> std::result::Result { + let session = { + let guard = state.demo_ws_session().lock().await; + guard.clone() + }; + return match session { + std::option::Option::Some(session) => { + std::result::Result::Ok(status_from_snapshot(session.snapshot().await)) + }, + std::option::Option::None => std::result::Result::Ok(disconnected_status()), + }; +} + +pub(crate) async fn demo_ws_connect_inner( + app_handle: tauri::AppHandle, + state: &crate::AppState, + request: crate::DemoWsRequest, +) -> std::result::Result { + let role = request.role.trim().to_string(); + if role.is_empty() { + return std::result::Result::Err("demo WebSocket role must not be empty".to_string()); + } + let method = request.method.trim().to_string(); + if method.is_empty() { + return std::result::Result::Err("demo WebSocket method must not be empty".to_string()); + } + let standard_request = match build_standard_ws_request( + &method, + request.target, + request.filter_json, + request.config_json, + ) { + std::result::Result::Ok(request) => request, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let selected_client = match state.ws_pool().select_client_for_role_and_method(&role, &method) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let session = match ensure_demo_ws_session(&app_handle, state, selected_client.clone()).await { + std::result::Result::Ok(session) => session, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let acknowledgement = match session.subscribe(standard_request).await { + std::result::Result::Ok(acknowledgement) => acknowledgement, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let response_value = match acknowledgement.response.to_value() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let response_json = match serde_json::to_string_pretty(&response_value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + emit_demo_ws_status(&app_handle, session.snapshot().await); + return std::result::Result::Ok(crate::DemoWsExecutionPayload { + endpoint_name: selected_client.endpoint_name().to_string(), + provider: selected_client.provider().to_string(), + endpoint_url: selected_client.endpoint_url().to_string(), + role, + method: method.clone(), + request_kind: kb_rpc::request_kind_from_method(&method), + response_kind: acknowledgement.response.kind_name().to_string(), + subscription_id: acknowledgement.subscription.remote_subscription_id, + response_json, + }); +} + +async fn ensure_demo_ws_session( + app_handle: &tauri::AppHandle, + state: &crate::AppState, + selected_client: kb_rpc::WsClient, +) -> std::result::Result, std::string::String> { + let existing = { + let guard = state.demo_ws_session().lock().await; + guard.clone() + }; + if let std::option::Option::Some(session) = existing { + let snapshot = session.snapshot().await; + if snapshot.state != kb_rpc::WsSessionState::Disconnected { + if snapshot.endpoint_url != selected_client.endpoint_url() { + return std::result::Result::Err(format!( + "demo WebSocket session already uses endpoint '{}'; disconnect before selecting '{}'", + snapshot.endpoint_name, + selected_client.endpoint_name() + )); + } + return std::result::Result::Ok(session); + } + } + let reconnect_policy = if selected_client.endpoint_config().auto_reconnect { + match kb_rpc::WsReconnectPolicy::bounded( + DEMO_WS_RECONNECT_ATTEMPTS, + DEMO_WS_RECONNECT_INITIAL_DELAY_MS, + DEMO_WS_RECONNECT_MAX_DELAY_MS, + ) { + std::result::Result::Ok(policy) => policy, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + } + } else { + kb_rpc::WsReconnectPolicy::disabled() + }; + let capabilities = + kb_rpc::StandardWsCapabilities::from_endpoint(selected_client.endpoint_config()); + let session = + match kb_rpc::WsSession::connect(selected_client, capabilities, reconnect_policy).await { + std::result::Result::Ok(session) => std::sync::Arc::new(session), + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + spawn_demo_ws_event_bridge(app_handle.clone(), session.clone()); + { + let mut guard = state.demo_ws_session().lock().await; + *guard = std::option::Option::Some(session.clone()); + } + return std::result::Result::Ok(session); +} + +pub(crate) async fn demo_ws_unsubscribe_inner( + app_handle: tauri::AppHandle, + state: &crate::AppState, + remote_subscription_id: u64, +) -> std::result::Result { + let session = { + let guard = state.demo_ws_session().lock().await; + guard.clone() + }; + let session = match session { + std::option::Option::Some(session) => session, + std::option::Option::None => { + return std::result::Result::Err("demo WebSocket session is not connected".to_string()); + }, + }; + let unsubscribe_result = session.unsubscribe(remote_subscription_id).await; + if let std::result::Result::Err(error) = unsubscribe_result { + return std::result::Result::Err(error.to_string()); + } + let snapshot = session.snapshot().await; + emit_demo_ws_status(&app_handle, snapshot.clone()); + return std::result::Result::Ok(status_from_snapshot(snapshot)); +} + +pub(crate) async fn demo_ws_disconnect_inner( + state: &crate::AppState, +) -> std::result::Result { + let session = { + let mut guard = state.demo_ws_session().lock().await; + guard.take() + }; + if let std::option::Option::Some(session) = session { + if let std::result::Result::Err(error) = session.disconnect().await { + return std::result::Result::Err(error.to_string()); + } + } + return std::result::Result::Ok(disconnected_status()); +} + +/// Disconnects the demo WebSocket session from application lifecycle hooks. +pub(crate) async fn disconnect_demo_ws_app_state(state: &crate::AppState, _wait_for_relay: bool) { + let disconnect_result = demo_ws_disconnect_inner(state).await; + if let std::result::Result::Err(error) = disconnect_result { + tracing::debug!(target: crate::TRACING_TARGET, "demo ws lifecycle disconnect failed: {error}"); + } +} + +fn spawn_demo_ws_event_bridge( + app_handle: tauri::AppHandle, + session: std::sync::Arc, +) { + let mut receiver = session.subscribe_events(); + tauri::async_runtime::spawn(async move { + let mut rate_limiter = DemoWsUiRateLimiter::new(); + loop { + let event = receiver.recv().await; + let event = match event { + std::result::Result::Ok(event) => event, + std::result::Result::Err(tokio::sync::broadcast::error::RecvError::Lagged( + count, + )) => { + emit_demo_ws_message( + &app_handle, + "lagged", + format!("{count} événement(s) WebSocket interne(s) perdu(s)"), + ); + continue; + }, + std::result::Result::Err(tokio::sync::broadcast::error::RecvError::Closed) => break, + }; + match event { + kb_rpc::WsSessionEvent::Notification { notification, .. } => { + if rate_limiter.should_emit(&app_handle) { + let payload = match serde_json::to_string_pretty(¬ification) { + std::result::Result::Ok(payload) => payload, + std::result::Result::Err(error) => { + format!("cannot serialize typed WebSocket notification: {error}") + }, + }; + emit_demo_ws_message( + &app_handle, + "notification", + truncate_payload(payload), + ); + } + }, + kb_rpc::WsSessionEvent::Diagnostic { code, message } => { + emit_demo_ws_message(&app_handle, &code, truncate_payload(message)); + }, + kb_rpc::WsSessionEvent::Reconnecting { attempt, maximum_attempts } => { + emit_demo_ws_message( + &app_handle, + "reconnecting", + format!("tentative {attempt}/{maximum_attempts}"), + ); + emit_demo_ws_status(&app_handle, session.snapshot().await); + }, + kb_rpc::WsSessionEvent::Reconnected { reconnect_count } => { + emit_demo_ws_message( + &app_handle, + "reconnected", + format!("reconnexion numéro {reconnect_count}"), + ); + emit_demo_ws_status(&app_handle, session.snapshot().await); + }, + kb_rpc::WsSessionEvent::Connected + | kb_rpc::WsSessionEvent::SubscriptionAdded(_) + | kb_rpc::WsSessionEvent::SubscriptionRemapped { .. } + | kb_rpc::WsSessionEvent::SubscriptionRemoved(_) => { + emit_demo_ws_status(&app_handle, session.snapshot().await); + }, + kb_rpc::WsSessionEvent::Disconnected => { + emit_demo_ws_status(&app_handle, session.snapshot().await); + break; + }, + } + } + }); +} + +fn emit_demo_ws_status(app_handle: &tauri::AppHandle, snapshot: kb_rpc::WsSessionSnapshot) { + let window = match app_handle.get_webview_window("demo_ws") { + std::option::Option::Some(window) => window, + std::option::Option::None => return, + }; + let status = status_from_snapshot(snapshot); + if let std::result::Result::Err(error) = window.emit("demo-ws-status", status) { + tracing::debug!(target: crate::TRACING_TARGET, "cannot emit demo ws status: {error}"); + } +} + +fn emit_demo_ws_message( + app_handle: &tauri::AppHandle, + kind: &str, + payload_json: std::string::String, +) { + let window = match app_handle.get_webview_window("demo_ws") { + std::option::Option::Some(window) => window, + std::option::Option::None => return, + }; + if let std::result::Result::Err(error) = window.emit( + "demo-ws-message", + crate::DemoWsMessagePayload { kind: kind.to_string(), payload_json }, + ) { + tracing::debug!(target: crate::TRACING_TARGET, "cannot emit demo ws message: {error}"); + } +} + +fn status_from_snapshot(snapshot: kb_rpc::WsSessionSnapshot) -> crate::DemoWsStatusPayload { + let mut subscriptions = std::vec::Vec::new(); + for subscription in &snapshot.subscriptions { + if let std::option::Option::Some(remote_subscription_id) = + subscription.remote_subscription_id + { + subscriptions.push(crate::DemoWsSubscriptionStatusPayload { + subscription_id: remote_subscription_id, + method: subscription.subscribe_method.clone(), + unsubscribe_method: subscription.unsubscribe_method.clone(), + }); + } + } + let (method, unsubscribe_method, subscription_id) = match subscriptions.last() { + std::option::Option::Some(subscription) => ( + std::option::Option::Some(subscription.method.clone()), + std::option::Option::Some(subscription.unsubscribe_method.clone()), + std::option::Option::Some(subscription.subscription_id), + ), + std::option::Option::None => { + (std::option::Option::None, std::option::Option::None, std::option::Option::None) + }, + }; + return crate::DemoWsStatusPayload { + connected: snapshot.state != kb_rpc::WsSessionState::Disconnected, + endpoint_name: std::option::Option::Some(snapshot.endpoint_name), + endpoint_url: std::option::Option::Some(snapshot.endpoint_url), + method, + unsubscribe_method, + subscription_id, + subscription_count: match u32::try_from(subscriptions.len()) { + std::result::Result::Ok(count) => count, + std::result::Result::Err(_) => u32::MAX, + }, + subscriptions, + }; +} + +fn disconnected_status() -> crate::DemoWsStatusPayload { + return crate::DemoWsStatusPayload { + connected: false, + endpoint_name: std::option::Option::None, + endpoint_url: std::option::Option::None, + method: std::option::Option::None, + unsubscribe_method: std::option::Option::None, + subscription_id: std::option::Option::None, + subscription_count: 0, + subscriptions: std::vec::Vec::new(), + }; +} + +pub(crate) fn build_ws_role_options( + snapshots: std::vec::Vec, +) -> std::vec::Vec { + let mut roles = std::collections::BTreeMap::< + std::string::String, + std::collections::BTreeSet, + >::new(); + for snapshot in snapshots { + for role in snapshot.roles { + if !role.enabled { + continue; + } + let role_entry = roles.entry(role.role).or_default(); + for request_kind in role.request_kinds { + role_entry.insert(request_kind); + } + } + } + let mut options = std::vec::Vec::new(); + for (role, request_kinds) in roles { + options.push(crate::DemoWsRoleOption { + role, + request_kinds: request_kinds.into_iter().collect(), + }); + } + return options; +} + +pub(crate) fn build_ws_method_options() -> std::vec::Vec { + let labels = std::collections::BTreeMap::from([ + ("accountSubscribe", ("Compte", true, false, true)), + ("blockSubscribe", ("Bloc", false, true, true)), + ("logsSubscribe", ("Logs", false, true, true)), + ("programSubscribe", ("Programme", true, false, true)), + ("rootSubscribe", ("Roots", false, false, false)), + ("signatureSubscribe", ("Signature", true, false, true)), + ("slotSubscribe", ("Slots", false, false, false)), + ("slotsUpdatesSubscribe", ("Mises à jour slots", false, false, false)), + ("voteSubscribe", ("Votes", false, false, false)), + ]); + let mut options = std::vec::Vec::new(); + for specification in &kb_rpc::STANDARD_WS_SUBSCRIPTIONS { + let metadata = labels.get(specification.subscribe_method); + let (label, requires_target, requires_filter_json, supports_config_json) = match metadata { + std::option::Option::Some(metadata) => *metadata, + std::option::Option::None => continue, + }; + options.push(crate::DemoWsMethodOption { + method: specification.subscribe_method.to_string(), + unsubscribe_method: specification.unsubscribe_method.to_string(), + request_kind: kb_rpc::request_kind_from_method(specification.subscribe_method), + label: label.to_string(), + requires_target, + requires_filter_json, + supports_config_json, + }); + } + return options; +} + +fn build_standard_ws_request( + method: &str, + target: std::option::Option, + filter_json: std::option::Option, + config_json: std::option::Option, +) -> std::result::Result { + return match method { + "accountSubscribe" => { + let target = match required_target(target, method) { + std::result::Result::Ok(target) => target, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let config = match parse_optional_json_as::( + config_json, + "configJson", + ) { + std::result::Result::Ok(config) => config, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if let std::result::Result::Err(error) = + reject_present(filter_json, "filterJson", method) + { + return std::result::Result::Err(error); + } + std::result::Result::Ok(kb_rpc::StandardWsRequest::Account( + kb_rpc::AccountSubscribeRequest { pubkey: target, config }, + )) + }, + "blockSubscribe" => { + if let std::result::Result::Err(error) = reject_present(target, "target", method) { + return std::result::Result::Err(error); + } + let filter = + match parse_required_json_as::(filter_json, "filterJson") { + std::result::Result::Ok(filter) => filter, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let config = match parse_optional_json_as::( + config_json, + "configJson", + ) { + std::result::Result::Ok(config) => config, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + std::result::Result::Ok(kb_rpc::StandardWsRequest::Block( + kb_rpc::BlockSubscribeRequest { filter, config }, + )) + }, + "logsSubscribe" => { + if let std::result::Result::Err(error) = reject_present(target, "target", method) { + return std::result::Result::Err(error); + } + let filter = + match parse_required_json_as::(filter_json, "filterJson") { + std::result::Result::Ok(filter) => filter, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let config = match parse_optional_json_as::( + config_json, + "configJson", + ) { + std::result::Result::Ok(config) => config, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + std::result::Result::Ok(kb_rpc::StandardWsRequest::Logs(kb_rpc::LogsSubscribeRequest { + filter, + config, + })) + }, + "programSubscribe" => { + let target = match required_target(target, method) { + std::result::Result::Ok(target) => target, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if let std::result::Result::Err(error) = + reject_present(filter_json, "filterJson", method) + { + return std::result::Result::Err(error); + } + let config = match parse_optional_json_as::( + config_json, + "configJson", + ) { + std::result::Result::Ok(config) => config, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + std::result::Result::Ok(kb_rpc::StandardWsRequest::Program( + kb_rpc::ProgramSubscribeRequest { program_id: target, config }, + )) + }, + "signatureSubscribe" => { + let target = match required_target(target, method) { + std::result::Result::Ok(target) => target, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if let std::result::Result::Err(error) = + reject_present(filter_json, "filterJson", method) + { + return std::result::Result::Err(error); + } + let config = match parse_optional_json_as::( + config_json, + "configJson", + ) { + std::result::Result::Ok(config) => config, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + std::result::Result::Ok(kb_rpc::StandardWsRequest::Signature( + kb_rpc::SignatureSubscribeRequest { signature: target, config }, + )) + }, + "rootSubscribe" => { + if let std::result::Result::Err(error) = + reject_parameterless_inputs(target, filter_json, config_json, method) + { + return std::result::Result::Err(error); + } + std::result::Result::Ok(kb_rpc::StandardWsRequest::Root(kb_rpc::RootSubscribeRequest)) + }, + "slotSubscribe" => { + if let std::result::Result::Err(error) = + reject_parameterless_inputs(target, filter_json, config_json, method) + { + return std::result::Result::Err(error); + } + std::result::Result::Ok(kb_rpc::StandardWsRequest::Slot(kb_rpc::SlotSubscribeRequest)) + }, + "slotsUpdatesSubscribe" => { + if let std::result::Result::Err(error) = + reject_parameterless_inputs(target, filter_json, config_json, method) + { + return std::result::Result::Err(error); + } + std::result::Result::Ok(kb_rpc::StandardWsRequest::SlotsUpdates( + kb_rpc::SlotsUpdatesSubscribeRequest, + )) + }, + "voteSubscribe" => { + if let std::result::Result::Err(error) = + reject_parameterless_inputs(target, filter_json, config_json, method) + { + return std::result::Result::Err(error); + } + std::result::Result::Ok(kb_rpc::StandardWsRequest::Vote(kb_rpc::VoteSubscribeRequest)) + }, + _ => std::result::Result::Err(format!("unsupported standard WebSocket method '{method}'")), + }; +} + +fn required_target( + target: std::option::Option, + method: &str, +) -> std::result::Result { + let target = match target { + std::option::Option::Some(target) => target.trim().to_string(), + std::option::Option::None => std::string::String::new(), + }; + if target.is_empty() { + return std::result::Result::Err(format!("method '{method}' requires target")); + } + return std::result::Result::Ok(target); +} + +fn reject_parameterless_inputs( + target: std::option::Option, + filter_json: std::option::Option, + config_json: std::option::Option, + method: &str, +) -> std::result::Result<(), std::string::String> { + if let std::result::Result::Err(error) = reject_present(target, "target", method) { + return std::result::Result::Err(error); + } + if let std::result::Result::Err(error) = reject_present(filter_json, "filterJson", method) { + return std::result::Result::Err(error); + } + if let std::result::Result::Err(error) = reject_present(config_json, "configJson", method) { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(()); +} + +fn reject_present( + value: std::option::Option, + field: &str, + method: &str, +) -> std::result::Result<(), std::string::String> { + if let std::option::Option::Some(value) = value { + if !value.trim().is_empty() { + return std::result::Result::Err(format!("method '{method}' does not accept {field}")); + } + } + return std::result::Result::Ok(()); +} + +fn parse_required_json_as( + text: std::option::Option, + field: &str, +) -> std::result::Result { + let text = match text { + std::option::Option::Some(text) if !text.trim().is_empty() => text, + _ => return std::result::Result::Err(format!("{field} is required")), + }; + return match serde_json::from_str::(&text) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => { + std::result::Result::Err(format!("invalid {field}: {error}")) + }, + }; +} + +fn parse_optional_json_as( + text: std::option::Option, + field: &str, +) -> std::result::Result, std::string::String> { + let text = match text { + std::option::Option::Some(text) if !text.trim().is_empty() => text, + _ => return std::result::Result::Ok(std::option::Option::None), + }; + return match serde_json::from_str::(&text) { + std::result::Result::Ok(value) => std::result::Result::Ok(std::option::Option::Some(value)), + std::result::Result::Err(error) => { + std::result::Result::Err(format!("invalid {field}: {error}")) + }, + }; +} + +fn truncate_payload(payload: std::string::String) -> std::string::String { + if payload.chars().count() <= DEMO_WS_UI_MAX_PAYLOAD_CHARS { + return payload; + } + let mut truncated = payload + .chars() + .take(DEMO_WS_UI_MAX_PAYLOAD_CHARS) + .collect::(); + truncated.push_str("\n… payload tronqué côté Rust …"); + return truncated; +} + +#[cfg(test)] +mod tests { + use ts_rs::TS; // rust-rules: derive-import + + #[test] + fn tauri_numeric_bindings_use_json_compatible_numbers() { + let config = ts_rs::Config::default(); + let declarations = [ + ::decl(&config), + ::decl(&config), + ::decl(&config), + ::decl(&config), + ::decl(&config), + ::decl(&config), + ::decl(&config), + ]; + for declaration in declarations { + assert!(!declaration.contains("bigint")); + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/frontend_log.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/frontend_log.rs new file mode 100644 index 0000000..173100b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/frontend_log.rs @@ -0,0 +1,191 @@ +// file: kb_app_demo/src/frontend_log.rs +// version: 10 + +//! Frontend logging bridge commands for Tauri WebView scripts. + +use ts_rs::TS; // rust-rules: derive-import + +/// Log payload sent by frontend scripts when a typed tracing target is required. +#[derive(Clone, Debug, serde::Deserialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_app_demo/frontend_log/FrontendLogPayload.ts" +)] +pub(crate) struct FrontendLogPayload { + /// Lowercase tracing level: `trace`, `debug`, `info`, `warn`, or `error`. + pub(crate) level: std::string::String, + /// Explicit tracing target used for route-based filtering. + pub(crate) target: std::string::String, + /// Message rendered by the frontend before forwarding it to Rust. + pub(crate) message: std::string::String, +} + +pub(crate) fn emit_trace(target: &str, message: &str) { + tracing::trace!( + target: crate::TRACING_TARGET, + action = "frontend_log", + frontend_target = target, + "{}", + message + ); +} + +pub(crate) fn emit_debug(target: &str, message: &str) { + tracing::debug!( + target: crate::TRACING_TARGET, + action = "frontend_log", + frontend_target = target, + "{}", + message + ); +} + +pub(crate) fn emit_info(target: &str, message: &str) { + tracing::info!( + target: crate::TRACING_TARGET, + action = "frontend_log", + frontend_target = target, + "{}", + message + ); +} + +pub(crate) fn emit_warn(target: &str, message: &str) { + tracing::warn!( + target: crate::TRACING_TARGET, + action = "frontend_log", + frontend_target = target, + "{}", + message + ); +} + +pub(crate) fn emit_error(target: &str, message: &str) { + tracing::error!( + target: crate::TRACING_TARGET, + action = "frontend_log", + frontend_target = target, + "{}", + message + ); +} + +pub(crate) fn normalize_frontend_target(target: &str) -> std::string::String { + let trimmed = target.trim(); + if trimmed.is_empty() { + return "kb_app_demo.frontend".to_string(); + } + if trimmed == "kb_app_demo.frontend.main" { + return trimmed.to_string(); + } + if trimmed == "kb_app_demo.frontend.demo_config" { + return trimmed.to_string(); + } + if trimmed == "kb_app_demo.frontend.demo_http" { + return trimmed.to_string(); + } + if trimmed == "kb_app_demo.frontend.demo_ws" { + return trimmed.to_string(); + } + if trimmed == "kb_app_demo.frontend.demo_backfill" { + return trimmed.to_string(); + } + if trimmed == "kb_app_demo.frontend.demo_core_extraction" { + return trimmed.to_string(); + } + if trimmed == "kb_app_demo.frontend.demo_decode_replay" { + return trimmed.to_string(); + } + if trimmed == "kb_app_demo.frontend.demo_execution_solana_core" { + return trimmed.to_string(); + } + if trimmed == "kb_app_demo.frontend.demo_execution_spl" { + return trimmed.to_string(); + } + if trimmed == "kb_app_demo.frontend.demo_sql_replay_candidates" { + return trimmed.to_string(); + } + if trimmed == "kb_app_demo.frontend.splash" { + return trimmed.to_string(); + } + return "kb_app_demo.frontend".to_string(); +} + +#[cfg(test)] +mod tests { + #[test] + fn normalize_frontend_target_uses_default_for_empty_target() { + let normalized = crate::normalize_frontend_target(" "); + assert_eq!(normalized, "kb_app_demo.frontend"); + } + + #[test] + fn normalize_frontend_target_keeps_main_target() { + let normalized = crate::normalize_frontend_target("kb_app_demo.frontend.main"); + assert_eq!(normalized, "kb_app_demo.frontend.main"); + } + + #[test] + fn normalize_frontend_target_keeps_demo_config_target() { + let normalized = crate::normalize_frontend_target("kb_app_demo.frontend.demo_config"); + assert_eq!(normalized, "kb_app_demo.frontend.demo_config"); + } + + #[test] + fn normalize_frontend_target_keeps_demo_http_target() { + let normalized = crate::normalize_frontend_target("kb_app_demo.frontend.demo_http"); + assert_eq!(normalized, "kb_app_demo.frontend.demo_http"); + } + + #[test] + fn normalize_frontend_target_keeps_demo_ws_target() { + let normalized = crate::normalize_frontend_target("kb_app_demo.frontend.demo_ws"); + assert_eq!(normalized, "kb_app_demo.frontend.demo_ws"); + } + + #[test] + fn normalize_frontend_target_keeps_demo_backfill_target() { + let normalized = crate::normalize_frontend_target("kb_app_demo.frontend.demo_backfill"); + assert_eq!(normalized, "kb_app_demo.frontend.demo_backfill"); + } + + #[test] + fn normalize_frontend_target_keeps_demo_core_extraction_target() { + let normalized = + crate::normalize_frontend_target("kb_app_demo.frontend.demo_core_extraction"); + assert_eq!(normalized, "kb_app_demo.frontend.demo_core_extraction"); + } + + #[test] + fn normalize_frontend_target_keeps_demo_decode_replay_target() { + let normalized = + crate::normalize_frontend_target("kb_app_demo.frontend.demo_decode_replay"); + assert_eq!(normalized, "kb_app_demo.frontend.demo_decode_replay"); + } + + #[test] + fn normalize_frontend_target_keeps_demo_execution_solana_core_target() { + let normalized = + crate::normalize_frontend_target("kb_app_demo.frontend.demo_execution_solana_core"); + assert_eq!(normalized, "kb_app_demo.frontend.demo_execution_solana_core"); + } + + #[test] + fn normalize_frontend_target_keeps_demo_sql_replay_candidates_target() { + let normalized = + crate::normalize_frontend_target("kb_app_demo.frontend.demo_sql_replay_candidates"); + assert_eq!(normalized, "kb_app_demo.frontend.demo_sql_replay_candidates"); + } + + #[test] + fn normalize_frontend_target_keeps_splash_target() { + let normalized = crate::normalize_frontend_target("kb_app_demo.frontend.splash"); + assert_eq!(normalized, "kb_app_demo.frontend.splash"); + } + + #[test] + fn normalize_frontend_target_rejects_unknown_dynamic_targets() { + let normalized = crate::normalize_frontend_target("other.frontend.target"); + assert_eq!(normalized, "kb_app_demo.frontend"); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/lib.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/lib.rs new file mode 100644 index 0000000..7424d2f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/lib.rs @@ -0,0 +1,338 @@ +// file: kb_app_demo/src/lib.rs +// version: 30 + +//! Tauri application demo library for `khadhroony-bot2`. +//! +//! This crate loads the shared configuration, initializes workspace logging and +//! exposes focused demo windows for early runtime validation. + +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod app_state; +mod constants; +mod demo_backfill; +mod demo_config; +mod demo_core_extraction; +mod demo_decode_replay; +mod demo_execution_solana_core; +mod demo_execution_spl; +mod demo_http; +mod demo_spl_ata; +mod demo_spl_token; +mod demo_spl_token_2022; +mod demo_sql_common; +mod demo_sql_diag; +mod demo_sql_pg_core; +mod demo_sql_pg_raw; +mod demo_sql_replay_candidates; +mod demo_ws; +mod frontend_log; +mod main_window; +mod splash; +mod tauri; + +/// Shared state managed by Tauri for the demo application. +pub(crate) use crate::app_state::AppState; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; +/// ???? +pub(crate) use crate::demo_backfill::DemoBackfillObserver; +/// Initial options shown by the backfill demo. +pub(crate) use crate::demo_backfill::DemoBackfillOptionsPayload; +/// One progress event emitted to the backfill window. +pub(crate) use crate::demo_backfill::DemoBackfillProgressPayload; +/// UI request for one bounded backfill campaign. +pub(crate) use crate::demo_backfill::DemoBackfillRequest; +/// ???? +pub(crate) use crate::demo_backfill::DemoBackfillRunGuard; +/// Final UI-safe summary for one backfill campaign. +pub(crate) use crate::demo_backfill::DemoBackfillSummaryPayload; +/// ???? +pub(crate) use crate::demo_backfill::build_demo_backfill_pipeline_request; +/// ???? +pub(crate) use crate::demo_backfill::build_role_options; +/// ???? +pub(crate) use crate::demo_backfill::demo_backfill_summary_payload; +/// Serializable payload shown by the configuration demo page. +pub(crate) use crate::demo_config::DemoConfigPayload; +/// ???? +pub(crate) use crate::demo_core_extraction::DemoCoreExtractionObserver; +/// Initial options shown by the core extraction demo. +pub(crate) use crate::demo_core_extraction::DemoCoreExtractionOptionsPayload; +/// One progress event emitted to the core extraction window. +pub(crate) use crate::demo_core_extraction::DemoCoreExtractionProgressPayload; +/// UI request for one bounded core extraction campaign. +pub(crate) use crate::demo_core_extraction::DemoCoreExtractionRequest; +/// ???? +pub(crate) use crate::demo_core_extraction::DemoCoreExtractionRunGuard; +/// Final UI-safe summary for one extraction campaign. +pub(crate) use crate::demo_core_extraction::DemoCoreExtractionSummaryPayload; +/// ???? +pub(crate) use crate::demo_core_extraction::build_demo_core_extraction_pipeline_request; +/// ???? +pub(crate) use crate::demo_core_extraction::demo_core_extraction_summary_payload; +/// One aggregated coverage row returned to the UI. +pub(crate) use crate::demo_decode_replay::DemoDecodeCoverageSummaryPayload; +/// Read-only decode, ledger and coverage diagnostics. +pub(crate) use crate::demo_decode_replay::DemoDecodeDiagnosticsPayload; +/// One processor counter row returned to the UI. +pub(crate) use crate::demo_decode_replay::DemoDecodeProcessorSummaryPayload; +/// One decoder selectable by the decode replay demo. +pub(crate) use crate::demo_decode_replay::DemoDecodeReplayDecoderOption; +/// ???? +pub(crate) use crate::demo_decode_replay::DemoDecodeReplayObserver; +/// Initial options shown by the contextual decode replay demo. +pub(crate) use crate::demo_decode_replay::DemoDecodeReplayOptionsPayload; +/// UI request for one bounded contextual decode replay campaign. +pub(crate) use crate::demo_decode_replay::DemoDecodeReplayRequest; +/// ???? +pub(crate) use crate::demo_decode_replay::DemoDecodeReplayRunGuard; +/// Final UI-safe summary for one contextual decode replay campaign. +pub(crate) use crate::demo_decode_replay::DemoDecodeReplaySummaryPayload; +/// Bounded read request for committed transaction annotations. +pub(crate) use crate::demo_decode_replay::DemoTransactionAnnotationRequest; +/// UI-safe committed transaction annotation row. +pub(crate) use crate::demo_decode_replay::DemoTransactionAnnotationRow; +/// ???? +pub(crate) use crate::demo_decode_replay::annotation_payload; +/// ???? +pub(crate) use crate::demo_decode_replay::available_decoders; +/// ???? +pub(crate) use crate::demo_decode_replay::available_materializers; +/// ???? +pub(crate) use crate::demo_decode_replay::build_demo_decode_replay_pipeline_request; +/// ???? +pub(crate) use crate::demo_decode_replay::coverage_payload; +/// ???? +pub(crate) use crate::demo_decode_replay::demo_decode_replay_summary_payload; +/// ???? +pub(crate) use crate::demo_decode_replay::optional_line_count; +/// ???? +pub(crate) use crate::demo_decode_replay::register_active_campaign; +/// ???? +pub(crate) use crate::demo_decode_replay::text_sample; +/// Request sent by the Memo v4 Devnet execution panel. +pub(crate) use crate::demo_execution_solana_core::DemoExecutionMemoRequest; +/// UI-safe result of one Memo v4 Devnet execution orchestration. +pub(crate) use crate::demo_execution_solana_core::DemoExecutionMemoSummaryPayload; +/// Public key generated for a disposable Devnet recipient. +pub(crate) use crate::demo_execution_solana_core::DemoExecutionSolanaCoreGeneratedRecipientPayload; +/// ???? +pub(crate) use crate::demo_execution_solana_core::DemoExecutionSolanaCoreObserver; +/// Initial options shown by the Solana Core execution demo. +pub(crate) use crate::demo_execution_solana_core::DemoExecutionSolanaCoreOptionsPayload; +/// One Devnet execution profile exposed to the demo window. +pub(crate) use crate::demo_execution_solana_core::DemoExecutionSolanaCoreProfileOption; +/// Progress event emitted during execution and post-validation. +pub(crate) use crate::demo_execution_solana_core::DemoExecutionSolanaCoreProgressPayload; +/// Request sent by the execution demo. +pub(crate) use crate::demo_execution_solana_core::DemoExecutionSolanaCoreRequest; +/// ???? +pub(crate) use crate::demo_execution_solana_core::DemoExecutionSolanaCoreRunGuard; +/// UI-safe result of one execution orchestration. +pub(crate) use crate::demo_execution_solana_core::DemoExecutionSolanaCoreSummaryPayload; +/// ???? +pub(crate) use crate::demo_execution_solana_core::confirmation_status_code; +/// ???? +pub(crate) use crate::demo_execution_solana_core::demo_execution_solana_core_memo_summary_payload; +/// ???? +pub(crate) use crate::demo_execution_solana_core::demo_execution_solana_core_summary_payload; +/// ???? +pub(crate) use crate::demo_execution_solana_core::devnet_profile_options; +/// ???? +pub(crate) use crate::demo_execution_solana_core::execution_cluster_code; +/// ???? +pub(crate) use crate::demo_execution_solana_core::pretty_json; +/// ???? +pub(crate) use crate::demo_execution_solana_core::select_devnet_profile; +/// Frontend payload for one independent Devnet SPL validation scenario. +pub(crate) use crate::demo_execution_spl::DevnetSplValidationScenarioPayload; +/// Response payload for one HTTP JSON-RPC demo execution. +pub(crate) use crate::demo_http::DemoHttpExecutionPayload; +/// One selectable HTTP JSON-RPC method shown by the HTTP demo. +pub(crate) use crate::demo_http::DemoHttpMethodOption; +/// HTTP demo options derived from configuration and local method presets. +pub(crate) use crate::demo_http::DemoHttpOptionsPayload; +/// Request payload for one HTTP JSON-RPC demo execution. +pub(crate) use crate::demo_http::DemoHttpRequest; +/// One selectable role shown by the HTTP demo. +pub(crate) use crate::demo_http::DemoHttpRoleOption; +/// ???? +pub(crate) use crate::demo_http::build_http_method_options; +/// ???? +pub(crate) use crate::demo_http::build_http_role_options; +/// ???? +pub(crate) use crate::demo_http::demo_http_execute_request_inner; +/// Request for one representative ATA creation on Devnet. +pub(crate) use crate::demo_spl_ata::DemoExecutionSplAtaRequest; +/// UI-safe ATA execution result. +pub(crate) use crate::demo_spl_ata::DemoExecutionSplAtaSummaryPayload; +/// Readonly payer, wallet, Token Program and derived ATA values. +pub(crate) use crate::demo_spl_ata::DemoSplAtaDerivationPayload; +/// Request used to derive the representative wallet ATA before execution. +pub(crate) use crate::demo_spl_ata::DemoSplAtaDerivationRequest; +/// Bounded ATA lifecycle journal request. +pub(crate) use crate::demo_spl_ata::DemoSplAtaJournalRequest; +/// One UI-safe ATA lifecycle journal row. +pub(crate) use crate::demo_spl_ata::DemoSplAtaJournalRow; +/// ???? +pub(crate) use crate::demo_spl_ata::demo_spl_ata_summary_payload; +/// ???? +pub(crate) use crate::demo_spl_ata::derive_ata; +/// ???? +pub(crate) use crate::demo_spl_ata::journal_matches; +/// ???? +pub(crate) use crate::demo_spl_ata::journal_row; +/// ???? +pub(crate) use crate::demo_spl_ata::load_profile_wallet; +/// ???? +pub(crate) use crate::demo_spl_ata::parse_token_program; +/// Request for one checked classic SPL Token transfer on Devnet. +pub(crate) use crate::demo_spl_token::DemoExecutionSplTokenRequest; +/// UI-safe result of one checked SPL Token transfer orchestration. +pub(crate) use crate::demo_spl_token::DemoExecutionSplTokenSummaryPayload; +/// Bounded exact filters for the classic SPL Token materialized journal. +pub(crate) use crate::demo_spl_token::DemoSplTokenJournalRequest; +/// UI-safe materialized classic SPL Token journal row. +pub(crate) use crate::demo_spl_token::DemoSplTokenJournalRow; +/// ???? +pub(crate) use crate::demo_spl_token::demo_spl_token_journal_row; +/// Executes one checked SPL Token simulation or explicitly authorized Devnet submission. +pub(crate) use crate::demo_spl_token::demo_spl_token_summary_payload; +/// ???? +pub(crate) use crate::demo_spl_token::payload_matches; +/// Loads a bounded, typed journal of committed classic SPL Token projections. +pub(crate) use crate::demo_spl_token::validate_journal_request; +/// Request for one independent public Token-2022 Devnet scenario. +pub(crate) use crate::demo_spl_token_2022::DemoExecutionSplToken2022Request; +/// UI-safe result of one public Token-2022 Devnet orchestration. +pub(crate) use crate::demo_spl_token_2022::DemoExecutionSplToken2022SummaryPayload; +/// Public values loaded from one persisted Token-2022 validation fixture. +pub(crate) use crate::demo_spl_token_2022::DemoSplToken2022FixturePayload; +/// ???? +pub(crate) use crate::demo_spl_token_2022::demo_spl_token_2022_summary_payload; +/// ???? +pub(crate) use crate::demo_spl_token_2022::operation_from_request; +/// ???? +pub(crate) use crate::demo_spl_token_2022::parse_fixture; +/// UI-safe table diagnostics shown by SQL demo windows. +pub(crate) use crate::demo_sql_common::DemoSqlTableSnapshot; +/// Connects to PostgreSQL without reapplying schemas initialized at application startup. +pub(crate) use crate::demo_sql_common::connect_postgres_store; +/// Formats debug enums for UI display. +pub(crate) use crate::demo_sql_common::debug_status; +/// Initializes PostgreSQL raw, core, decode and materialization schemas during Tauri startup. +pub(crate) use crate::demo_sql_common::initialize_postgres_schema_for_startup; +/// Opens or focuses a SQL demo window. +pub(crate) use crate::demo_sql_common::open_sql_demo_window; +/// Converts store table diagnostics to the UI payload shape. +pub(crate) use crate::demo_sql_common::table_snapshot_from_pg; +/// Converts many table diagnostics to UI payloads. +pub(crate) use crate::demo_sql_common::table_snapshots_from_pg; +/// Complete SQL diagnostic payload shown by `demo_sql_diag`. +pub(crate) use crate::demo_sql_diag::DemoSqlDiagPayload; +/// PostgreSQL core store payload shown by `demo_sql_pg_core`. +pub(crate) use crate::demo_sql_pg_core::DemoSqlPgCorePayload; +/// PostgreSQL canonical acquisition payload shown by `demo_sql_pg_raw`. +pub(crate) use crate::demo_sql_pg_raw::DemoSqlPgRawPayload; +/// UI request for bounded core entity summaries. +pub(crate) use crate::demo_sql_replay_candidates::DemoSqlReplayEntityRequest; +/// Aggregated core entity row shown by the replay candidate browser. +pub(crate) use crate::demo_sql_replay_candidates::DemoSqlReplayEntityRow; +/// One program identifier exposed by the runtime `kb_program_ids` registry. +pub(crate) use crate::demo_sql_replay_candidates::DemoSqlReplayKnownProgramOption; +/// Static options for the replay candidate browser. +pub(crate) use crate::demo_sql_replay_candidates::DemoSqlReplayOptionsPayload; +/// UI request for bounded program summaries. +pub(crate) use crate::demo_sql_replay_candidates::DemoSqlReplayProgramRequest; +/// Aggregated program row shown by the replay candidate browser. +pub(crate) use crate::demo_sql_replay_candidates::DemoSqlReplayProgramRow; +/// UI request for bounded transaction replay candidates. +pub(crate) use crate::demo_sql_replay_candidates::DemoSqlReplayTransactionRequest; +/// One transaction row shown by the replay candidate browser. +pub(crate) use crate::demo_sql_replay_candidates::DemoSqlReplayTransactionRow; +/// ???? +pub(crate) use crate::demo_sql_replay_candidates::available_csv_export_path; +/// ???? +pub(crate) use crate::demo_sql_replay_candidates::csv_export_directory_from_current_dir; +/// ???? +pub(crate) use crate::demo_sql_replay_candidates::entity_kind_from_code; +/// ???? +pub(crate) use crate::demo_sql_replay_candidates::entity_row_from_pg; +/// ???? +pub(crate) use crate::demo_sql_replay_candidates::optional_entity_kind_from_code; +/// ???? +pub(crate) use crate::demo_sql_replay_candidates::program_row_from_pg; +/// ???? +pub(crate) use crate::demo_sql_replay_candidates::program_scope_from_code; +/// ???? +pub(crate) use crate::demo_sql_replay_candidates::transaction_row_from_pg; +/// ???? +pub(crate) use crate::demo_sql_replay_candidates::validated_csv_file_name; +/// Response payload for one WebSocket subscription response. +pub(crate) use crate::demo_ws::DemoWsExecutionPayload; +/// WebSocket event payload emitted to the demo window. +pub(crate) use crate::demo_ws::DemoWsMessagePayload; +/// One selectable WebSocket JSON-RPC method shown by the WebSocket demo. +pub(crate) use crate::demo_ws::DemoWsMethodOption; +/// WebSocket demo options derived from configuration and the standard registry. +pub(crate) use crate::demo_ws::DemoWsOptionsPayload; +/// Request payload for one WebSocket subscription demo command. +pub(crate) use crate::demo_ws::DemoWsRequest; +/// One selectable role shown by the WebSocket demo. +pub(crate) use crate::demo_ws::DemoWsRoleOption; +/// Current WebSocket demo session status. +pub(crate) use crate::demo_ws::DemoWsStatusPayload; +/// One active WebSocket subscription shown by the demo UI. +pub(crate) use crate::demo_ws::DemoWsSubscriptionStatusPayload; +/// Request payload for one explicit WebSocket unsubscribe command. +pub(crate) use crate::demo_ws::DemoWsUnsubscribeRequest; +/// ???? +pub(crate) use crate::demo_ws::build_ws_method_options; +/// ???? +pub(crate) use crate::demo_ws::build_ws_role_options; +/// ???? +pub(crate) use crate::demo_ws::demo_ws_connect_inner; +/// ???? +pub(crate) use crate::demo_ws::demo_ws_disconnect_inner; +/// ???? +pub(crate) use crate::demo_ws::demo_ws_status_inner; +/// ???? +pub(crate) use crate::demo_ws::demo_ws_unsubscribe_inner; +/// Disconnects the demo WebSocket session from application lifecycle hooks. +pub(crate) use crate::demo_ws::disconnect_demo_ws_app_state; +/// Log payload sent by frontend scripts when a typed tracing target is required. +pub(crate) use crate::frontend_log::FrontendLogPayload; +/// ???? +pub(crate) use crate::frontend_log::emit_debug; +/// ???? +pub(crate) use crate::frontend_log::emit_error; +/// ???? +pub(crate) use crate::frontend_log::emit_info; +/// ???? +pub(crate) use crate::frontend_log::emit_trace; +/// ???? +pub(crate) use crate::frontend_log::emit_warn; +/// ???? +pub(crate) use crate::frontend_log::normalize_frontend_target; +/// ???? +pub(crate) use crate::main_window::strip_readme_metadata_comments; +/// Resolves the workspace root directory used by the demo application. +pub(crate) use crate::main_window::workspace_root_dir; +/// Delay after the fade-out command before destroying the splash window. +pub(crate) use crate::splash::SPLASH_CLOSE_WAIT_MS; +/// Splash fade-in and fade-out animation duration. +pub(crate) use crate::splash::SPLASH_FADE_MS; +/// Minimum startup splash duration before fade-out starts. +pub(crate) use crate::splash::SPLASH_MINIMUM_MS; +/// Command payload sent from Rust to the splash frontend. +pub(crate) use crate::splash::SplashOrder; +/// Emits a splash-screen order to the splash WebView. +pub(crate) use crate::splash::emit_splash_order; +/// Waits until the minimum splash duration has elapsed. +pub(crate) use crate::splash::wait_until_minimum; + +/// Runs the Tauri application. +pub use crate::tauri::run; diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/main.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/main.rs new file mode 100644 index 0000000..89fb6bd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/main.rs @@ -0,0 +1,43 @@ +// file: kb_app_demo/src/main.rs +// version: 6 + +//! Binary entry point for the `kb_app_demo` crate. + +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] +// Prevents additional console window on Windows in release, DO NOT REMOVE!! +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +use fs2::FileExt; // rust-rules: trait-import + +/// Entrypoint of the kb app binary. +#[tokio::main] +async fn main() -> std::process::ExitCode { + let mut lock_path = std::env::temp_dir(); + lock_path.push("com_khadhroony_bot2_kb_app_demo.lock"); + let lock_file = match std::fs::File::create(&lock_path) { + std::result::Result::Ok(lock) => lock, + std::result::Result::Err(error) => { + eprintln!("cannot create application lock '{}': {error}", lock_path.display()); + return std::process::ExitCode::FAILURE; + }, + }; + if let std::result::Result::Err(error) = lock_file.try_lock_exclusive() { + if error.kind() == std::io::ErrorKind::WouldBlock { + eprintln!("another kb_app_demo instance is already running"); + return std::process::ExitCode::FAILURE; + } + eprintln!("cannot acquire application lock '{}': {error}", lock_path.display()); + return std::process::ExitCode::FAILURE; + } + let _lock_file = lock_file; + let run_result = kb_app_demo_lib::run().await; + return match run_result { + std::result::Result::Ok(()) => std::process::ExitCode::SUCCESS, + std::result::Result::Err(error) => { + eprintln!("application error: {error}"); + std::process::ExitCode::FAILURE + }, + }; +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/main_window.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/main_window.rs new file mode 100644 index 0000000..2225904 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/main_window.rs @@ -0,0 +1,54 @@ +// file: kb_app_demo/src/main_window.rs +// version: 3 + +//! Main window commands and workspace README loading helpers. + +pub(crate) fn workspace_root_dir() -> std::path::PathBuf { + let manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + return match manifest_dir.parent() { + std::option::Option::Some(parent) => parent.to_path_buf(), + std::option::Option::None => manifest_dir, + }; +} + +pub(crate) fn strip_readme_metadata_comments(content: &str) -> std::string::String { + let mut output = std::string::String::new(); + let mut leading_metadata = true; + for line in content.lines() { + let trimmed = line.trim(); + if leading_metadata && is_readme_metadata_comment(trimmed) { + continue; + } + leading_metadata = false; + output.push_str(line); + output.push('\n'); + } + return output; +} + +fn is_readme_metadata_comment(line: &str) -> bool { + if line.starts_with("\n\n# Title\nBody\n"; + let cleaned = crate::strip_readme_metadata_comments(content); + assert_eq!(cleaned, "# Title\nBody\n"); + } + + #[test] + fn strip_readme_metadata_comments_keeps_later_html_comments() { + let content = "\n# Title\n\nBody\n"; + let cleaned = crate::strip_readme_metadata_comments(content); + assert_eq!(cleaned, "# Title\n\nBody\n"); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/splash.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/splash.rs new file mode 100644 index 0000000..dc92bd7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/splash.rs @@ -0,0 +1,80 @@ +// file: kb_app_demo/src/splash.rs +// version: 5 + +//! Shared splash-screen payload types and helpers. +//! +//! The startup sequence is intentionally wired in `lib.rs` so it stays close to +//! the Tauri builder setup pattern used by the previous demo application. + +use tauri::Emitter; // rust-rules: trait-import +use ts_rs::TS; // rust-rules: derive-import + +/// Minimum startup splash duration before fade-out starts. +pub(crate) const SPLASH_MINIMUM_MS: u64 = 3100; +/// Splash fade-in and fade-out animation duration. +pub(crate) const SPLASH_FADE_MS: u32 = 3000; +/// Delay after the fade-out command before destroying the splash window. +pub(crate) const SPLASH_CLOSE_WAIT_MS: u64 = 3100; + +/// Command payload sent from Rust to the splash frontend. +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, TS)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_app_demo/splash/SplashOrder.ts")] +pub(crate) struct SplashOrder { + /// Splash command name such as `fadein`, `fadeout`, `add_msg`, or `add_log`. + pub(crate) order: std::string::String, + /// Optional message payload attached to the command. + pub(crate) msg: std::option::Option, + /// Optional status payload attached to the command. + pub(crate) status: std::option::Option, + /// Optional animation duration in milliseconds. + pub(crate) duration_ms: std::option::Option, +} + +/// Emits a splash-screen order to the splash WebView. +pub(crate) fn emit_splash_order( + splash_window: &tauri::WebviewWindow, + order: &str, + msg: std::option::Option<&str>, + status: std::option::Option<&str>, + duration_ms: std::option::Option, +) { + let payload = crate::SplashOrder { + order: order.to_string(), + msg: msg.map(std::string::ToString::to_string), + status: status.map(std::string::ToString::to_string), + duration_ms, + }; + let emit_result = splash_window.emit("splash", payload); + if let std::result::Result::Err(error) = emit_result { + tracing::error!(target: crate::TRACING_TARGET, "error emitting splash event '{order}': {error:?}"); + } +} + +/// Waits until the minimum splash duration has elapsed. +pub(crate) async fn wait_until_minimum(start: tokio::time::Instant, minimum_ms: u64) { + let minimum = std::time::Duration::from_millis(minimum_ms); + let elapsed = start.elapsed(); + if elapsed >= minimum { + return; + } + tokio::time::sleep(minimum - elapsed).await; +} + +#[cfg(test)] +mod tests { + #[test] + fn splash_order_serializes_duration_ms() { + let order = crate::SplashOrder { + order: "fadein".to_string(), + msg: std::option::Option::None, + status: std::option::Option::None, + duration_ms: std::option::Option::Some(3000), + }; + let value_result = serde_json::to_value(order); + let value = match value_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("cannot serialize splash order: {error}"), + }; + assert_eq!(value["duration_ms"], serde_json::json!(3000)); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/src/tauri.rs b/migration/khadhroony-bot2-reference/kb_app_demo/src/tauri.rs new file mode 100644 index 0000000..78ede8e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/src/tauri.rs @@ -0,0 +1,2258 @@ +// file: kb_app_demo/src/tauri.rs +// version: 1 + +use tauri::Emitter; // rust-rules: trait-import +use tauri::Manager; // rust-rules: trait-import + +/// Runs the Tauri application. +/// +/// Desktop builds apply their single-instance lock in `main.rs`. Mobile builds +/// enter here directly and rely on the mobile operating system lifecycle. +#[allow(clippy::question_mark_used)] +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub async fn run() -> kb_core::Result<()> { + let rustls_result = install_default_rustls_provider(); + if let std::result::Result::Err(error) = rustls_result { + return std::result::Result::Err(error); + } + let app_state = match crate::AppState::initialize() { + std::result::Result::Ok(state) => state, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + tracing::info!( + target: crate::TRACING_TARGET, + logging_routes = app_state.logging_route_count(), + "starting tauri application" + ); + let tracing_builder = tauri_plugin_tracing::Builder::new(); + let mut tauri_builder = tauri::Builder::default(); + tauri_builder = tauri_builder.manage(app_state); + tauri_builder = tauri_builder.invoke_handler(tauri::generate_handler![ + emit_frontend_log, + load_project_readme, + open_demo_backfill_window, + demo_backfill_options, + demo_backfill_execute, + demo_backfill_cancel, + open_demo_core_extraction_window, + demo_core_extraction_options, + demo_core_extraction_execute, + demo_core_extraction_cancel, + open_demo_decode_replay_window, + demo_decode_replay_options, + demo_decode_replay_execute, + demo_decode_replay_cancel, + demo_decode_replay_diagnostics, + demo_decode_replay_annotations, + demo_execution_solana_core_options, + demo_execution_solana_core_generate_recipient, + demo_execution_solana_core_execute, + open_demo_execution_solana_core_window, + demo_execution_spl_memo_execute, + demo_execution_solana_core_cancel, + open_demo_execution_spl_window, + demo_execution_spl_validation_scenarios, + demo_execution_spl_token_execute, + demo_spl_token_journal, + demo_execution_spl_token_2022_execute, + demo_spl_token_2022_fixture, + demo_spl_ata_derive, + demo_execution_spl_ata_execute, + demo_spl_ata_journal, + open_demo_config_window, + load_demo_config, + open_demo_http_window, + demo_http_list_pool_clients, + demo_http_options, + demo_http_execute_request, + open_demo_ws_window, + demo_ws_list_pool_clients, + demo_ws_options, + demo_ws_status, + demo_ws_connect, + demo_ws_unsubscribe, + demo_ws_disconnect, + open_demo_sql_diag_window, + load_demo_sql_diag, + open_demo_sql_pg_raw_window, + load_demo_sql_pg_raw, + open_demo_sql_pg_core_window, + load_demo_sql_pg_core, + open_demo_sql_replay_candidates_window, + demo_sql_replay_options, + load_demo_sql_replay_transactions, + load_demo_sql_replay_programs, + load_demo_sql_replay_entities, + export_demo_sql_replay_csv, + ]); + tauri_builder = tauri_builder.plugin(tracing_builder.build::()); + tauri_builder = tauri_builder.on_window_event(|window, event| { + if window.label() != "main" { + return; + } + match event { + tauri::WindowEvent::CloseRequested { .. } => { + let app_handle = window.app_handle().clone(); + tauri::async_runtime::spawn(async move { + let state = app_handle.state::(); + crate::disconnect_demo_ws_app_state(state.inner(), false).await; + }); + }, + _ => (), + } + }); + tauri_builder = tauri_builder.setup(|app| { + let app_handle = app.handle().clone(); + tauri::async_runtime::spawn(async move { + let splash_window_option = app_handle.get_webview_window("splash"); + let splash_window = match splash_window_option { + std::option::Option::Some(window) => window, + std::option::Option::None => { + tracing::error!(target: crate::TRACING_TARGET, "splash window not found"); + return; + }, + }; + let main_window_option = app_handle.get_webview_window("main"); + let main_window = match main_window_option { + std::option::Option::Some(window) => window, + std::option::Option::None => { + tracing::error!(target: crate::TRACING_TARGET, "main window not found"); + return; + }, + }; + let start = tokio::time::Instant::now(); + let is_debug = cfg!(debug_assertions); + tokio::time::sleep(std::time::Duration::from_millis(1500)).await; + if is_debug { + crate::emit_splash_order( + &splash_window, + "add_log", + std::option::Option::Some("Start Fade-In"), + std::option::Option::None, + std::option::Option::None, + ); + } + crate::emit_splash_order( + &splash_window, + "fadein", + std::option::Option::None, + std::option::Option::None, + std::option::Option::Some(crate::SPLASH_FADE_MS), + ); + crate::emit_splash_order( + &splash_window, + "add_msg", + std::option::Option::Some("Initialisation..."), + std::option::Option::Some("info"), + std::option::Option::None, + ); + tokio::time::sleep(std::time::Duration::from_millis(500)).await; + crate::emit_splash_order( + &splash_window, + "add_msg", + std::option::Option::Some("Loading configuration and logging..."), + std::option::Option::Some("info"), + std::option::Option::None, + ); + tokio::time::sleep(std::time::Duration::from_millis(500)).await; + let state = app_handle.state::(); + crate::initialize_postgres_schema_for_startup( + state.inner(), + &splash_window, + ) + .await; + tokio::time::sleep(std::time::Duration::from_millis(500)).await; + crate::emit_splash_order( + &splash_window, + "add_msg", + std::option::Option::Some("Loading complete..."), + std::option::Option::Some("success"), + std::option::Option::None, + ); + crate::wait_until_minimum(start, crate::SPLASH_MINIMUM_MS).await; + tokio::time::sleep(std::time::Duration::from_millis(500)).await; + tracing::trace!(target: crate::TRACING_TARGET, "start splash fadeout"); + if is_debug { + crate::emit_splash_order( + &splash_window, + "add_log", + std::option::Option::Some("Start Fade-out"), + std::option::Option::None, + std::option::Option::None, + ); + } + crate::emit_splash_order( + &splash_window, + "fadeout", + std::option::Option::None, + std::option::Option::None, + std::option::Option::Some(crate::SPLASH_FADE_MS), + ); + tracing::trace!(target: crate::TRACING_TARGET, "end splash fadeout"); + tokio::time::sleep(std::time::Duration::from_millis(crate::SPLASH_CLOSE_WAIT_MS)).await; + let close_result = splash_window.destroy(); + if let std::result::Result::Err(error) = close_result { + tracing::error!(target: crate::TRACING_TARGET, "error closing splash window: {error:?}"); + } + let show_result = main_window.show(); + if let std::result::Result::Err(error) = show_result { + tracing::error!(target: crate::TRACING_TARGET, "error showing main window: {error:?}"); + return; + } + let emit_result = main_window.emit("setupTray", ()); + if let std::result::Result::Err(error) = emit_result { + tracing::error!(target: crate::TRACING_TARGET, "error emitting setupTray event: {error:?}"); + } + }); + return std::result::Result::Ok(()); + }); + let run_result = tauri_builder.run(tauri::generate_context!()); + return match run_result { + std::result::Result::Ok(()) => std::result::Result::Ok(()), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::tauri( + format!("error while running tauri application: {error:?}"), + )), + }; +} + +fn install_default_rustls_provider() -> kb_core::Result<()> { + if rustls::crypto::CryptoProvider::get_default().is_some() { + return std::result::Result::Ok(()); + } + let provider_result = rustls::crypto::aws_lc_rs::default_provider().install_default(); + return match provider_result { + std::result::Result::Ok(()) => std::result::Result::Ok(()), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::invalid_state( + format!("cannot install default rustls crypto provider: {error:?}"), + )), + }; +} + +/// Requests cooperative cancellation of the current backfill campaign. +#[tauri::command] +fn demo_backfill_cancel(state: tauri::State<'_, crate::AppState>) -> bool { + let running = state.demo_backfill_running().load(std::sync::atomic::Ordering::Acquire); + state + .demo_backfill_cancel_requested() + .store(true, std::sync::atomic::Ordering::Release); + return running; +} + +/// Opens or focuses the HTTP backfill demo window. +#[tauri::command] +fn open_demo_backfill_window( + app_handle: tauri::AppHandle, +) -> std::result::Result<(), std::string::String> { + tracing::info!(target: crate::TRACING_TARGET, "open demo backfill window"); + let existing_window = app_handle.get_webview_window("demo_backfill"); + if let std::option::Option::Some(window) = existing_window { + let show_result = window.show(); + if let std::result::Result::Err(error) = show_result { + return std::result::Result::Err(error.to_string()); + } + let focus_result = window.set_focus(); + if let std::result::Result::Err(error) = focus_result { + return std::result::Result::Err(error.to_string()); + } + return std::result::Result::Ok(()); + } + let build_result = tauri::WebviewWindowBuilder::new( + &app_handle, + "demo_backfill", + tauri::WebviewUrl::App("demo_backfill.html".into()), + ) + .title("Khadhroony Bot2 - Backfill HTTP") + .inner_size(1280.0, 860.0) + .min_inner_size(960.0, 620.0) + .resizable(true) + .visible(true) + .build(); + return match build_result { + std::result::Result::Ok(window) => { + let focus_result = window.set_focus(); + if let std::result::Result::Err(error) = focus_result { + return std::result::Result::Err(error.to_string()); + } + std::result::Result::Ok(()) + }, + std::result::Result::Err(error) => std::result::Result::Err(error.to_string()), + }; +} + +/// Returns endpoint roles and bounded defaults for the backfill demo. +#[tauri::command] +fn demo_backfill_options( + state: tauri::State<'_, crate::AppState>, +) -> crate::DemoBackfillOptionsPayload { + let roles = crate::build_role_options(state.http_pool().snapshot()); + let default_role = if roles.iter().any(|item| return item.role == "history_backfill") { + std::option::Option::Some("history_backfill".to_string()) + } else { + roles.first().map(|item| return item.role.clone()) + }; + return crate::DemoBackfillOptionsPayload { + roles, + default_role, + default_commitment: "confirmed".to_string(), + default_page_size: 100, + default_max_pages: 20, + default_max_concurrent_requests: 4, + default_max_retries: 2, + running: state.demo_backfill_running().load(std::sync::atomic::Ordering::Acquire), + }; +} + +/// Executes one bounded backfill campaign and persists canonical transactions. +#[tauri::command] +async fn demo_backfill_execute( + app_handle: tauri::AppHandle, + state: tauri::State<'_, crate::AppState>, + request: crate::DemoBackfillRequest, +) -> std::result::Result { + let acquire_result = state.demo_backfill_running().compare_exchange( + false, + true, + std::sync::atomic::Ordering::AcqRel, + std::sync::atomic::Ordering::Acquire, + ); + if acquire_result.is_err() { + return std::result::Result::Err("a backfill campaign is already running".to_string()); + } + let _run_guard = crate::DemoBackfillRunGuard { running: state.demo_backfill_running() }; + state + .demo_backfill_cancel_requested() + .store(false, std::sync::atomic::Ordering::Release); + let pipeline_request_result = crate::build_demo_backfill_pipeline_request(request); + let pipeline_request = match pipeline_request_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let store_result = crate::connect_postgres_store(state.active_profile()).await; + let store = match store_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let observer = crate::DemoBackfillObserver { + app_handle, + cancel_requested: state.demo_backfill_cancel_requested(), + }; + let summary_result = + kb_pipeline::execute_http_backfill(state.http_pool(), &store, &pipeline_request, &observer) + .await; + let summary = match summary_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + return std::result::Result::Ok(crate::demo_backfill_summary_payload(summary)); +} + +/// Opens or focuses the canonical to core extraction demo window. +#[tauri::command] +fn open_demo_core_extraction_window( + app_handle: tauri::AppHandle, +) -> std::result::Result<(), std::string::String> { + tracing::info!(target: crate::TRACING_TARGET, "open core extraction demo window"); + let existing_window = app_handle.get_webview_window("demo_core_extraction"); + if let std::option::Option::Some(window) = existing_window { + let show_result = window.show(); + if let std::result::Result::Err(error) = show_result { + return std::result::Result::Err(error.to_string()); + } + let focus_result = window.set_focus(); + if let std::result::Result::Err(error) = focus_result { + return std::result::Result::Err(error.to_string()); + } + return std::result::Result::Ok(()); + } + let build_result = tauri::WebviewWindowBuilder::new( + &app_handle, + "demo_core_extraction", + tauri::WebviewUrl::App("demo_core_extraction.html".into()), + ) + .title("Khadhroony Bot2 - Extraction canonical vers core") + .inner_size(1280.0, 860.0) + .min_inner_size(960.0, 620.0) + .resizable(true) + .visible(true) + .build(); + return match build_result { + std::result::Result::Ok(window) => { + let focus_result = window.set_focus(); + if let std::result::Result::Err(error) = focus_result { + return std::result::Result::Err(error.to_string()); + } + std::result::Result::Ok(()) + }, + std::result::Result::Err(error) => std::result::Result::Err(error.to_string()), + }; +} + +/// Returns the initial core extraction demo options. +#[tauri::command] +fn demo_core_extraction_options( + state: tauri::State<'_, crate::AppState>, +) -> crate::DemoCoreExtractionOptionsPayload { + return crate::DemoCoreExtractionOptionsPayload { + processor_version: kb_pipeline::CORE_EXTRACTION_PROCESSOR_VERSION.to_string(), + default_limit: 100, + default_max_concurrent_extractions: 4, + running: state.demo_core_extraction_running().load(std::sync::atomic::Ordering::Acquire), + }; +} + +/// Executes one bounded canonical transaction to core extraction campaign. +#[tauri::command] +async fn demo_core_extraction_execute( + app_handle: tauri::AppHandle, + state: tauri::State<'_, crate::AppState>, + request: crate::DemoCoreExtractionRequest, +) -> std::result::Result { + let acquire_result = state.demo_core_extraction_running().compare_exchange( + false, + true, + std::sync::atomic::Ordering::AcqRel, + std::sync::atomic::Ordering::Acquire, + ); + if acquire_result.is_err() { + return std::result::Result::Err( + "a core extraction campaign is already running".to_string(), + ); + } + let _run_guard = crate::DemoCoreExtractionRunGuard { + running: state.demo_core_extraction_running(), + }; + state + .demo_core_extraction_cancel_requested() + .store(false, std::sync::atomic::Ordering::Release); + let pipeline_request_result = crate::build_demo_core_extraction_pipeline_request(request); + let pipeline_request = match pipeline_request_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let store_result = crate::connect_postgres_store(state.active_profile()).await; + let store = match store_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let observer = crate::DemoCoreExtractionObserver { + app_handle, + cancel_requested: state.demo_core_extraction_cancel_requested(), + }; + let summary_result = + kb_pipeline::execute_core_extraction(&store, &pipeline_request, &observer).await; + let summary = match summary_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + return std::result::Result::Ok(crate::demo_core_extraction_summary_payload(summary)); +} + +/// Requests cooperative cancellation of the current core extraction campaign. +#[tauri::command] +fn demo_core_extraction_cancel(state: tauri::State<'_, crate::AppState>) -> bool { + let running = state.demo_core_extraction_running().load(std::sync::atomic::Ordering::Acquire); + state + .demo_core_extraction_cancel_requested() + .store(true, std::sync::atomic::Ordering::Release); + return running; +} + +/// Opens or focuses the contextual decode replay window. +#[tauri::command] +fn open_demo_decode_replay_window( + app_handle: tauri::AppHandle, +) -> std::result::Result<(), std::string::String> { + tracing::info!(target: crate::TRACING_TARGET, action = "open_window", window = "demo_decode_replay", "open contextual decode replay window"); + let existing_window = app_handle.get_webview_window("demo_decode_replay"); + if let std::option::Option::Some(window) = existing_window { + let show_result = window.show(); + if let std::result::Result::Err(error) = show_result { + return std::result::Result::Err(error.to_string()); + } + let focus_result = window.set_focus(); + if let std::result::Result::Err(error) = focus_result { + return std::result::Result::Err(error.to_string()); + } + return std::result::Result::Ok(()); + } + let build_result = tauri::WebviewWindowBuilder::new( + &app_handle, + "demo_decode_replay", + tauri::WebviewUrl::App("demo_decode_replay.html".into()), + ) + .title("Khadhroony Bot2 - Décodage et matérialisation") + .inner_size(1320.0, 900.0) + .min_inner_size(980.0, 660.0) + .resizable(true) + .visible(true) + .build(); + return match build_result { + std::result::Result::Ok(window) => { + let focus_result = window.set_focus(); + if let std::result::Result::Err(error) = focus_result { + return std::result::Result::Err(error.to_string()); + } + std::result::Result::Ok(()) + }, + std::result::Result::Err(error) => std::result::Result::Err(error.to_string()), + }; +} + +/// Returns available decoders and default replay bounds. +#[tauri::command] +fn demo_decode_replay_options( + state: tauri::State<'_, crate::AppState>, +) -> crate::DemoDecodeReplayOptionsPayload { + let decoders = crate::available_decoders(); + let decoder_options = decoders + .iter() + .map(|decoder| { + let identity = decoder.identity(); + return crate::DemoDecodeReplayDecoderOption { + name: identity.name, + version: identity.version, + program_ids: decoder + .surfaces() + .iter() + .map(|surface| return surface.program_id.to_string()) + .collect(), + }; + }) + .collect::>(); + let materializers = crate::available_materializers(); + let materializer_names = materializers + .iter() + .map(|materializer| { + let identity = materializer.identity(); + return format!("{}@{}", identity.name, identity.version); + }) + .collect::>(); + let running = state.demo_decode_replay_running().load(std::sync::atomic::Ordering::Acquire); + tracing::debug!( + target: crate::TRACING_TARGET, + action = "load_options", + pipeline_version = kb_pipeline::DECODE_PIPELINE_VERSION, + decoder_count = decoder_options.len(), + materializer_names = ?materializer_names, + default_limit = 100_u32, + default_max_concurrent_inputs = 4_u32, + running, + "return contextual decode replay options" + ); + return crate::DemoDecodeReplayOptionsPayload { + pipeline_version: kb_pipeline::DECODE_PIPELINE_VERSION.to_string(), + decoders: decoder_options, + materializer_names, + default_limit: 100, + default_max_concurrent_inputs: 4, + running, + }; +} + +/// Executes one bounded contextual decode and optional materialization campaign. +#[tauri::command] +async fn demo_decode_replay_execute( + app_handle: tauri::AppHandle, + state: tauri::State<'_, crate::AppState>, + request: crate::DemoDecodeReplayRequest, +) -> std::result::Result { + let campaign_id = kb_pipeline::new_decode_campaign_id(); + tracing::debug!( + target: crate::TRACING_TARGET, + action = "execute", + campaign_id = %campaign_id, + instruction_state = %request.instruction_state, + signature_line_count = crate::optional_line_count(&request.signatures_text), + program_id = ?request.program_id, + instruction_paths_text = ?request.instruction_paths_text, + decoder_names = ?request.decoder_names, + limit = request.limit, + max_concurrent_inputs = request.max_concurrent_inputs, + all_compatible = request.all_compatible, + force_replay = request.force_replay, + force_replay_all_matching = request.force_replay_all_matching, + materialize_after_decode = request.materialize_after_decode, + "received contextual decode replay command" + ); + let acquire_result = state.demo_decode_replay_running().compare_exchange( + false, + true, + std::sync::atomic::Ordering::AcqRel, + std::sync::atomic::Ordering::Acquire, + ); + if acquire_result.is_err() { + tracing::warn!(target: crate::TRACING_TARGET, action = "execute", campaign_id = %campaign_id, accepted = false, reason = "already_running", "reject contextual decode replay command"); + return std::result::Result::Err( + "a contextual decode replay campaign is already running".to_string(), + ); + } + let _run_guard = crate::DemoDecodeReplayRunGuard { + running: state.demo_decode_replay_running(), + campaign_id: state.demo_decode_replay_campaign_id(), + }; + let register_result = crate::register_active_campaign( + state.demo_decode_replay_campaign_id(), + campaign_id.as_str(), + ); + if let std::result::Result::Err(error) = register_result { + tracing::error!(target: crate::TRACING_TARGET, action = "register_campaign", campaign_id = %campaign_id, error = %error, "cannot lock active contextual decode campaign slot"); + return std::result::Result::Err(error); + } + state + .demo_decode_replay_cancel_requested() + .store(false, std::sync::atomic::Ordering::Release); + let pipeline_request_result = + crate::build_demo_decode_replay_pipeline_request(request, campaign_id.clone()); + let pipeline_request = match pipeline_request_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "build_request", campaign_id = %campaign_id, error = %error, "cannot build contextual decode replay request"); + return std::result::Result::Err(error); + }, + }; + tracing::debug!( + target: crate::TRACING_TARGET, + action = "execute_pipeline", + campaign_id = %pipeline_request.campaign_id, + signature_count = pipeline_request.selection.signatures.len(), + signature_sample = ?crate::text_sample(pipeline_request.selection.signatures.as_slice(), 5), + processing_states = ?pipeline_request.selection.processing_states, + min_slot = ?pipeline_request.selection.min_slot, + max_slot = ?pipeline_request.selection.max_slot, + program_ids = ?pipeline_request.selection.program_ids, + instruction_paths = ?pipeline_request.selection.instruction_paths, + incomplete_signatures = pipeline_request.selection.incomplete_signatures, + limit = pipeline_request.selection.limit, + decoder_names = ?pipeline_request.decoder_names, + dispatch_policy = ?pipeline_request.dispatch_policy, + max_concurrent_inputs = pipeline_request.max_concurrent_inputs, + force_replay = pipeline_request.force_replay, + force_replay_all_matching = pipeline_request.force_replay_all_matching, + materialize_after_decode = pipeline_request.materialize_after_decode, + "start contextual decode replay pipeline" + ); + let store_result = crate::connect_postgres_store(state.active_profile()).await; + let store = match store_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "connect_store", campaign_id = %pipeline_request.campaign_id, error = %error, "cannot connect contextual decode replay store"); + return std::result::Result::Err(error); + }, + }; + let decoders = crate::available_decoders(); + let materializers = crate::available_materializers(); + let observer = crate::DemoDecodeReplayObserver { + app_handle, + campaign_id: pipeline_request.campaign_id.clone(), + cancel_requested: state.demo_decode_replay_cancel_requested(), + }; + let summary_result = kb_pipeline::execute_decode_replay( + &store, + &pipeline_request, + decoders.as_slice(), + materializers.as_slice(), + &observer, + ) + .await; + let summary = match summary_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "execute_pipeline", campaign_id = %pipeline_request.campaign_id, error = %error, "contextual decode replay pipeline failed"); + return std::result::Result::Err(error.to_string()); + }, + }; + tracing::debug!( + target: crate::TRACING_TARGET, + action = "execute_pipeline", + campaign_id = %summary.campaign_id, + selected = summary.selected, + started = summary.started, + completed = summary.completed, + unmatched = summary.unmatched, + not_started = summary.not_started, + failed_inputs = summary.failed_inputs, + cancelled = summary.cancelled, + processors = ?summary.processors, + "contextual decode replay command completed" + ); + return std::result::Result::Ok(crate::demo_decode_replay_summary_payload(summary)); +} + +/// Requests cooperative cancellation of the current decode replay campaign. +#[tauri::command] +fn demo_decode_replay_cancel(state: tauri::State<'_, crate::AppState>) -> bool { + let running = state.demo_decode_replay_running().load(std::sync::atomic::Ordering::Acquire); + state + .demo_decode_replay_cancel_requested() + .store(true, std::sync::atomic::Ordering::Release); + let campaign_lock_result = state.demo_decode_replay_campaign_id().lock(); + let campaign_id = match campaign_lock_result { + std::result::Result::Ok(active_campaign_id) => active_campaign_id.clone(), + std::result::Result::Err(_) => std::option::Option::None, + }; + tracing::debug!(target: crate::TRACING_TARGET, action = "cancel", campaign_id = ?campaign_id, running, cancellation_requested = true, "contextual decode replay cancellation command handled"); + return running; +} + +/// Loads read-only decode, materialization, ledger and coverage diagnostics. +#[tauri::command] +async fn demo_decode_replay_diagnostics( + state: tauri::State<'_, crate::AppState>, +) -> std::result::Result { + tracing::debug!(target: crate::TRACING_TARGET, action = "load_diagnostics", coverage_limit = 500_u32, "load contextual decode replay diagnostics"); + let store_result = crate::connect_postgres_store(state.active_profile()).await; + let store = match store_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let table_result = store.known_table_diagnostics().await; + let all_tables = match table_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let selected_tables: std::vec::Vec = all_tables + .iter() + .filter(|table| { + return table.table_name.starts_with("kb_sol_decode_") + || table.table_name.starts_with("kb_sol_mat_") + || table.table_name == "kb_sol_ops_processing_ledger"; + }) + .map(crate::table_snapshot_from_pg) + .collect(); + let coverage_result = kb_store_core::DecodePipelineStore::list_decode_coverage_summary( + &store, + std::option::Option::None, + std::option::Option::None, + 500, + ) + .await; + let coverage = match coverage_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + tracing::debug!(target: crate::TRACING_TARGET, action = "load_diagnostics", table_count = selected_tables.len(), coverage_count = coverage.len(), "contextual decode replay diagnostics loaded"); + return std::result::Result::Ok(crate::DemoDecodeDiagnosticsPayload { + tables: selected_tables, + coverage: coverage.into_iter().map(crate::coverage_payload).collect(), + }); +} + +/// Loads a bounded journal of committed SPL Memo transaction annotations. +#[tauri::command] +async fn demo_decode_replay_annotations( + state: tauri::State<'_, crate::AppState>, + request: crate::DemoTransactionAnnotationRequest, +) -> std::result::Result, std::string::String> { + let filter_result = kb_store_core::MaterializedEventFilter::new( + std::option::Option::Some("transaction_annotations".to_string()), + std::option::Option::Some("transaction_annotation".to_string()), + request.signature_contains, + request.limit, + ); + let filter = match filter_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + tracing::debug!(target: crate::TRACING_TARGET, action = "load_transaction_annotations", signature_contains = ?filter.signature_contains, limit = filter.limit, "load bounded committed transaction annotation journal"); + let store_result = crate::connect_postgres_store(state.active_profile()).await; + let store = match store_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let rows_result = + kb_store_core::DecodePipelineStore::list_materialized_events(&store, &filter).await; + let rows = match rows_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let mut output = std::vec::Vec::with_capacity(rows.len()); + for row in rows { + let mapped = crate::annotation_payload(row); + match mapped { + std::result::Result::Ok(value) => output.push(value), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + tracing::debug!(target: crate::TRACING_TARGET, action = "load_transaction_annotations", row_count = output.len(), "bounded committed transaction annotation journal loaded"); + return std::result::Result::Ok(output); +} + +/// Opens or focuses the Solana Core execution demo window. +#[tauri::command] +fn open_demo_execution_solana_core_window( + app_handle: tauri::AppHandle, +) -> std::result::Result<(), std::string::String> { + let existing_window = app_handle.get_webview_window("demo_execution_solana_core"); + if let std::option::Option::Some(window) = existing_window { + let show_result = window.show(); + if let std::result::Result::Err(error) = show_result { + return std::result::Result::Err(error.to_string()); + } + let focus_result = window.set_focus(); + if let std::result::Result::Err(error) = focus_result { + return std::result::Result::Err(error.to_string()); + } + return std::result::Result::Ok(()); + } + let build_result = tauri::WebviewWindowBuilder::new( + &app_handle, + "demo_execution_solana_core", + tauri::WebviewUrl::App("demo_execution_solana_core.html".into()), + ) + .title("Khadhroony Bot2 - Exécution Solana Devnet") + .inner_size(1360.0, 920.0) + .min_inner_size(1040.0, 700.0) + .resizable(true) + .visible(true) + .build(); + return match build_result { + std::result::Result::Ok(window) => { + let focus_result = window.set_focus(); + if let std::result::Result::Err(error) = focus_result { + return std::result::Result::Err(error.to_string()); + } + std::result::Result::Ok(()) + }, + std::result::Result::Err(error) => std::result::Result::Err(error.to_string()), + }; +} + +/// Returns compatible Devnet profiles and conservative defaults. +#[tauri::command] +fn demo_execution_solana_core_options( + state: tauri::State<'_, crate::AppState>, +) -> crate::DemoExecutionSolanaCoreOptionsPayload { + let profiles = crate::devnet_profile_options(state.app_config()); + let default_profile_name = profiles.first().map(|profile| return profile.name.clone()); + return crate::DemoExecutionSolanaCoreOptionsPayload { + profiles, + default_profile_name, + default_transfer_lamports: 1_000_000, + running: state + .demo_execution_solana_core_running() + .load(std::sync::atomic::Ordering::Acquire), + }; +} + +/// Generates a disposable recipient public key without exposing its private key. +#[tauri::command] +fn demo_execution_solana_core_generate_recipient() +-> std::result::Result +{ + let alias = match kb_wallet::WalletAlias::parse("demo-devnet-recipient") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let wallet = kb_wallet::TemporaryWallet::generate(alias); + return std::result::Result::Ok(crate::DemoExecutionSolanaCoreGeneratedRecipientPayload { + public_key: wallet.public_key(), + }); +} + +/// Executes one bounded System transfer simulation or Devnet submission. +#[tauri::command] +async fn demo_execution_solana_core_execute( + app_handle: tauri::AppHandle, + state: tauri::State<'_, crate::AppState>, + request: crate::DemoExecutionSolanaCoreRequest, +) -> std::result::Result { + let acquire_result = state.demo_execution_solana_core_running().compare_exchange( + false, + true, + std::sync::atomic::Ordering::AcqRel, + std::sync::atomic::Ordering::Acquire, + ); + if acquire_result.is_err() { + return std::result::Result::Err("a Solana Core execution is already running".to_string()); + } + let _run_guard = crate::DemoExecutionSolanaCoreRunGuard { + running: state.demo_execution_solana_core_running(), + }; + state + .demo_execution_solana_core_cancel_requested() + .store(false, std::sync::atomic::Ordering::Release); + let profile = + match crate::select_devnet_profile(state.app_config(), request.profile_name.as_str()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let http_pool = match kb_rpc::HttpEndpointPool::from_profile(&profile) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let store = match kb_store_pg::PostgresStore::connect_from_profile_config(&profile).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let initialize_result = store.initialize_store_schema().await; + if let std::result::Result::Err(error) = initialize_result { + return std::result::Result::Err(error.to_string()); + } + let mut pipeline_request = kb_pipeline::DevnetSystemTransferRequest::new( + format!("demo-execution-{}", chrono::Utc::now().timestamp_micros()), + kb_model::Pubkey(request.recipient.trim().to_string()), + request.lamports, + ); + pipeline_request.airdrop_lamports = request.airdrop_lamports; + pipeline_request.submit = request.submit; + pipeline_request.operator_confirmed = request.operator_confirmed; + pipeline_request.post_validation_max_retries = 20; + pipeline_request.force_post_validation_replay = request.force_post_validation_replay; + pipeline_request.materialize_after_decode = request.materialize_after_decode; + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(kb_decoder_solana_core::SolanaCoreDecoder)]; + let materializers: std::vec::Vec> = + if request.materialize_after_decode { + std::vec![ + std::sync::Arc::new(kb_materializer_admin::AdminMaterializer), + std::sync::Arc::new(kb_materializer_compliance_audit::ComplianceAuditMaterializer,), + std::sync::Arc::new(kb_materializer_lifecycle::LifecycleMaterializer), + std::sync::Arc::new(kb_materializer_staking::StakingMaterializer), + ] + } else { + std::vec::Vec::new() + }; + let observer = crate::DemoExecutionSolanaCoreObserver { + app_handle, + cancel_requested: state.demo_execution_solana_core_cancel_requested(), + }; + let workspace_root = crate::workspace_root_dir(); + let summary = match kb_pipeline::execute_devnet_system_transfer( + &http_pool, + &store, + &profile, + workspace_root.as_path(), + &pipeline_request, + decoders.as_slice(), + materializers.as_slice(), + &observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + return std::result::Result::Ok(crate::demo_execution_solana_core_summary_payload(summary)); +} + +/// Executes one Memo v4 simulation or explicitly authorized Devnet submission. +#[tauri::command] +async fn demo_execution_spl_memo_execute( + app_handle: tauri::AppHandle, + state: tauri::State<'_, crate::AppState>, + request: crate::DemoExecutionMemoRequest, +) -> std::result::Result { + let acquire_result = state.demo_execution_solana_core_running().compare_exchange( + false, + true, + std::sync::atomic::Ordering::AcqRel, + std::sync::atomic::Ordering::Acquire, + ); + if acquire_result.is_err() { + return std::result::Result::Err("a Devnet execution is already running".to_string()); + } + let _run_guard = crate::DemoExecutionSolanaCoreRunGuard { + running: state.demo_execution_solana_core_running(), + }; + state + .demo_execution_solana_core_cancel_requested() + .store(false, std::sync::atomic::Ordering::Release); + let profile = + match crate::select_devnet_profile(state.app_config(), request.profile_name.as_str()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let http_pool = match kb_rpc::HttpEndpointPool::from_profile(&profile) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let store = match kb_store_pg::PostgresStore::connect_from_profile_config(&profile).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + if let std::result::Result::Err(error) = store.initialize_store_schema().await { + return std::result::Result::Err(error.to_string()); + } + let mut pipeline_request = kb_pipeline::DevnetMemoExecutionRequest::new( + format!("demo-memo-execution-{}", chrono::Utc::now().timestamp_micros()), + request.message, + ); + pipeline_request.include_wallet_as_memo_signer = request.include_wallet_as_memo_signer; + pipeline_request.submit = request.submit; + pipeline_request.operator_confirmed = request.operator_confirmed; + pipeline_request.post_validation_max_retries = 20; + pipeline_request.force_post_validation_replay = request.force_post_validation_replay; + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(kb_decoder_spl_memo::SplMemoDecoder)]; + let materializers: std::vec::Vec> = + std::vec![std::sync::Arc::new( + kb_materializer_transaction_annotations::TransactionAnnotationMaterializer, + )]; + let observer = crate::DemoExecutionSolanaCoreObserver { + app_handle, + cancel_requested: state.demo_execution_solana_core_cancel_requested(), + }; + let workspace_root = crate::workspace_root_dir(); + let summary = match kb_pipeline::execute_devnet_memo( + &http_pool, + &store, + &profile, + workspace_root.as_path(), + &pipeline_request, + decoders.as_slice(), + materializers.as_slice(), + &observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + return std::result::Result::Ok(crate::demo_execution_solana_core_memo_summary_payload( + summary, + )); +} + +/// Requests cooperative cancellation before the next execution stage. +#[tauri::command] +fn demo_execution_solana_core_cancel(state: tauri::State<'_, crate::AppState>) -> bool { + let running = state + .demo_execution_solana_core_running() + .load(std::sync::atomic::Ordering::Acquire); + if !running { + return false; + } + state + .demo_execution_solana_core_cancel_requested() + .store(true, std::sync::atomic::Ordering::Release); + return true; +} + +/// Returns the complete ordered Devnet SPL validation inventory for milestone 0.4.6. +#[tauri::command] +fn demo_execution_spl_validation_scenarios() +-> std::vec::Vec { + return kb_pipeline::devnet_spl_validation_scenarios() + .into_iter() + .map(|scenario| { + return crate::DevnetSplValidationScenarioPayload { + id: scenario.id, + label: scenario.label, + family: match scenario.family { + kb_pipeline::DevnetSplValidationFamily::Token2022Public => { + "token_2022_public".to_string() + }, + kb_pipeline::DevnetSplValidationFamily::ElGamalRegistry => { + "elgamal_registry".to_string() + }, + kb_pipeline::DevnetSplValidationFamily::Token2022Confidential => { + "token_2022_confidential".to_string() + }, + }, + operation_code: scenario.operation_code, + implementation_status: match scenario.implementation_status { + kb_pipeline::DevnetSplValidationImplementationStatus::Executable => { + "executable".to_string() + }, + kb_pipeline::DevnetSplValidationImplementationStatus::BackendReady => { + "backend_ready".to_string() + }, + kb_pipeline::DevnetSplValidationImplementationStatus::ProofFixtureRequired => { + "proof_fixture_required".to_string() + }, + }, + proof_required: scenario.proof_required, + required_fixture_variables: scenario.required_fixture_variables, + }; + }) + .collect(); +} + +/// Opens or focuses the dedicated SPL execution window. +#[tauri::command] +fn open_demo_execution_spl_window( + app_handle: tauri::AppHandle, +) -> std::result::Result<(), std::string::String> { + tracing::info!(target: crate::TRACING_TARGET, "open SPL Devnet execution window"); + let existing_window = app_handle.get_webview_window("demo_execution_spl"); + if let std::option::Option::Some(window) = existing_window { + let show_result = window.show(); + if let std::result::Result::Err(error) = show_result { + return std::result::Result::Err(error.to_string()); + } + let focus_result = window.set_focus(); + if let std::result::Result::Err(error) = focus_result { + return std::result::Result::Err(error.to_string()); + } + return std::result::Result::Ok(()); + } + let build_result = tauri::WebviewWindowBuilder::new( + &app_handle, + "demo_execution_spl", + tauri::WebviewUrl::App("demo_execution_spl.html".into()), + ) + .title("Khadhroony Bot2 - Exécution SPL Devnet") + .inner_size(1500.0, 960.0) + .min_inner_size(1120.0, 720.0) + .resizable(true) + .visible(true) + .build(); + return match build_result { + std::result::Result::Ok(window) => { + let focus_result = window.set_focus(); + if let std::result::Result::Err(error) = focus_result { + return std::result::Result::Err(error.to_string()); + } + std::result::Result::Ok(()) + }, + std::result::Result::Err(error) => std::result::Result::Err(error.to_string()), + }; +} + +/// Executes one checked SPL Token simulation or explicitly authorized Devnet submission. +#[tauri::command] +async fn demo_execution_spl_token_execute( + app_handle: tauri::AppHandle, + state: tauri::State<'_, crate::AppState>, + request: crate::DemoExecutionSplTokenRequest, +) -> std::result::Result { + let acquire_result = state.demo_execution_solana_core_running().compare_exchange( + false, + true, + std::sync::atomic::Ordering::AcqRel, + std::sync::atomic::Ordering::Acquire, + ); + if acquire_result.is_err() { + return std::result::Result::Err("a Devnet execution is already running".to_string()); + } + let _run_guard = crate::DemoExecutionSolanaCoreRunGuard { + running: state.demo_execution_solana_core_running(), + }; + state + .demo_execution_solana_core_cancel_requested() + .store(false, std::sync::atomic::Ordering::Release); + let profile = + match crate::select_devnet_profile(state.app_config(), request.profile_name.as_str()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let http_pool = match kb_rpc::HttpEndpointPool::from_profile(&profile) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let store = match kb_store_pg::PostgresStore::connect_from_profile_config(&profile).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + if let std::result::Result::Err(error) = store.initialize_store_schema().await { + return std::result::Result::Err(error.to_string()); + } + let amount_raw = request.amount_raw.trim().to_string(); + let operation = kb_executor_spl_token::SplTokenOperation::Instruction { + value: kb_executor_spl_token::SplTokenSingleOperation::TransferChecked { + source: kb_model::Pubkey(request.source.trim().to_string()), + mint: kb_model::Pubkey(request.mint.trim().to_string()), + destination: kb_model::Pubkey(request.destination.trim().to_string()), + authority: kb_executor_spl_token::SplTokenAuthority { + authority: kb_model::Pubkey(request.authority.trim().to_string()), + multisig_signers: std::vec::Vec::new(), + }, + amount: kb_executor_spl_token::SplTokenAmount(amount_raw.clone()), + decimals: request.decimals, + }, + }; + let mut pipeline_request = kb_pipeline::DevnetSplTokenExecutionRequest::new( + format!("demo-spl-token-execution-{}", chrono::Utc::now().timestamp_micros()), + operation, + ); + pipeline_request.submit = request.submit; + pipeline_request.operator_confirmed = request.operator_confirmed; + pipeline_request.post_validation_max_retries = 20; + pipeline_request.force_post_validation_replay = request.force_post_validation_replay; + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(kb_decoder_spl_token::SplTokenDecoder)]; + let materializers: std::vec::Vec> = std::vec![ + std::sync::Arc::new(kb_materializer_token_accounts::TokenAccountsMaterializer), + std::sync::Arc::new(kb_materializer_admin::AdminMaterializer), + std::sync::Arc::new(kb_materializer_risk::RiskMaterializer), + ]; + let observer = crate::DemoExecutionSolanaCoreObserver { + app_handle, + cancel_requested: state.demo_execution_solana_core_cancel_requested(), + }; + let workspace_root = crate::workspace_root_dir(); + let summary = match kb_pipeline::execute_devnet_spl_token( + &http_pool, + &store, + &profile, + workspace_root.as_path(), + &pipeline_request, + decoders.as_slice(), + materializers.as_slice(), + &observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + return std::result::Result::Ok(crate::demo_spl_token_summary_payload( + summary, + amount_raw, + request.decimals, + )); +} + +/// Loads a bounded, typed journal of committed classic SPL Token projections. +#[tauri::command] +async fn demo_spl_token_journal( + state: tauri::State<'_, crate::AppState>, + request: crate::DemoSplTokenJournalRequest, +) -> std::result::Result, std::string::String> { + let validated = match crate::validate_journal_request(request) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let profile = + match crate::select_devnet_profile(state.app_config(), validated.profile_name.as_str()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let store = match kb_store_pg::PostgresStore::connect_from_profile_config(&profile).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + if let std::result::Result::Err(error) = store.initialize_store_schema().await { + return std::result::Result::Err(error.to_string()); + } + let needs_payload_filter = + validated.mint.is_some() || validated.account.is_some() || validated.operation.is_some(); + let query_limit = if needs_payload_filter { + kb_store_core::MAX_MATERIALIZED_EVENT_QUERY_ROWS + } else { + validated.limit + }; + let filter = match kb_store_core::MaterializedEventFilter::new( + std::option::Option::None, + validated.family.clone(), + validated.signature_contains.clone(), + query_limit, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let rows = + match kb_store_core::DecodePipelineStore::list_materialized_events(&store, &filter).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let mut output = std::vec::Vec::new(); + for row in rows { + if row.source_decoder_name != "spl_token" + || !crate::payload_matches(&row.payload_json, &validated) + { + continue; + } + output.push(crate::demo_spl_token_journal_row(row)); + if output.len() >= validated.limit as usize { + break; + } + } + return std::result::Result::Ok(output); +} + +/// Derives the representative profile wallet ATA without exposing private key material. +#[tauri::command] +async fn demo_spl_ata_derive( + state: tauri::State<'_, crate::AppState>, + request: crate::DemoSplAtaDerivationRequest, +) -> std::result::Result { + let profile = + match crate::select_devnet_profile(state.app_config(), request.profile_name.as_str()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let wallet = match crate::load_profile_wallet(&profile).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let wallet_owner = wallet.public_key(); + let token_program = match crate::parse_token_program(&request.token_program) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let associated_token_account = match crate::derive_ata( + wallet_owner.as_str(), + request.mint.trim(), + token_program.program_id(), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(crate::DemoSplAtaDerivationPayload { + payer: wallet_owner.clone(), + wallet_owner, + token_program_id: token_program.program_id().to_string(), + associated_token_account, + }); +} + +/// Executes one representative Create or CreateIdempotent ATA flow. +#[tauri::command] +async fn demo_execution_spl_ata_execute( + app_handle: tauri::AppHandle, + state: tauri::State<'_, crate::AppState>, + request: crate::DemoExecutionSplAtaRequest, +) -> std::result::Result { + let acquired = state.demo_execution_solana_core_running().compare_exchange( + false, + true, + std::sync::atomic::Ordering::AcqRel, + std::sync::atomic::Ordering::Acquire, + ); + if acquired.is_err() { + return std::result::Result::Err("a Devnet execution is already running".to_string()); + } + let _run_guard = crate::DemoExecutionSolanaCoreRunGuard { + running: state.demo_execution_solana_core_running(), + }; + state + .demo_execution_solana_core_cancel_requested() + .store(false, std::sync::atomic::Ordering::Release); + let profile = + match crate::select_devnet_profile(state.app_config(), request.profile_name.as_str()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let profile_wallet = match crate::load_profile_wallet(&profile).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if request.wallet_owner.trim() != profile_wallet.public_key() { + return std::result::Result::Err( + "the representative ATA panel requires the profile wallet as wallet owner".to_string(), + ); + } + let token_program = match crate::parse_token_program(&request.token_program) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let wallet_owner = kb_model::Pubkey(request.wallet_owner.trim().to_string()); + let mint = kb_model::Pubkey(request.mint.trim().to_string()); + let operation = match request.mode.as_str() { + "create" => kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::Create { + wallet_owner, + mint, + token_program, + }, + "create_idempotent" => kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::CreateIdempotent { + wallet_owner, + mint, + token_program, + }, + _ => return std::result::Result::Err("ATA mode must be create or create_idempotent".to_string()), + }; + let mut pipeline_request = kb_pipeline::DevnetSplAssociatedTokenAccountExecutionRequest::new( + format!("demo-spl-ata-execution-{}", chrono::Utc::now().timestamp_micros()), + operation, + ); + pipeline_request.submit = request.submit; + pipeline_request.operator_confirmed = request.operator_confirmed; + pipeline_request.post_validation_max_retries = 20; + pipeline_request.force_post_validation_replay = request.force_post_validation_replay; + let http_pool = match kb_rpc::HttpEndpointPool::from_profile(&profile) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let store = match kb_store_pg::PostgresStore::connect_from_profile_config(&profile).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + if let std::result::Result::Err(error) = store.initialize_store_schema().await { + return std::result::Result::Err(error.to_string()); + } + let decoders: std::vec::Vec> = std::vec![ + std::sync::Arc::new( + kb_decoder_spl_associated_token_account::SplAssociatedTokenAccountDecoder + ), + std::sync::Arc::new(kb_decoder_spl_token::SplTokenDecoder), + ]; + let materializers: std::vec::Vec> = std::vec![ + std::sync::Arc::new(kb_materializer_token_accounts::TokenAccountsMaterializer), + std::sync::Arc::new(kb_materializer_risk::RiskMaterializer), + ]; + let observer = crate::DemoExecutionSolanaCoreObserver { + app_handle, + cancel_requested: state.demo_execution_solana_core_cancel_requested(), + }; + let summary = match kb_pipeline::execute_devnet_spl_associated_token_account( + &http_pool, + &store, + &profile, + crate::workspace_root_dir().as_path(), + &pipeline_request, + decoders.as_slice(), + materializers.as_slice(), + &observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + return std::result::Result::Ok(crate::demo_spl_ata_summary_payload(summary)); +} + +/// Loads a bounded journal of ATA-owned lifecycle facts. +#[tauri::command] +async fn demo_spl_ata_journal( + state: tauri::State<'_, crate::AppState>, + request: crate::DemoSplAtaJournalRequest, +) -> std::result::Result, std::string::String> { + if request.limit == 0 || request.limit > 500 { + return std::result::Result::Err("ATA journal limit must be between 1 and 500".to_string()); + } + let profile = + match crate::select_devnet_profile(state.app_config(), request.profile_name.as_str()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let store = match kb_store_pg::PostgresStore::connect_from_profile_config(&profile).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let filter = match kb_store_core::MaterializedEventFilter::new( + std::option::Option::Some("spl_token_accounts".to_string()), + std::option::Option::None, + request.signature_contains.clone(), + 500, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let rows = + match kb_store_core::DecodePipelineStore::list_materialized_events(&store, &filter).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let mut output = std::vec::Vec::new(); + for row in rows { + if row.source_decoder_name != "spl_associated_token_account" + || !crate::journal_matches(&row.payload_json, &request) + { + continue; + } + output.push(crate::journal_row(row)); + if output.len() >= request.limit as usize { + break; + } + } + return std::result::Result::Ok(output); +} + +/// Executes one public Token-2022 simulation or explicitly authorized Devnet submission. +#[tauri::command] +async fn demo_execution_spl_token_2022_execute( + app_handle: tauri::AppHandle, + state: tauri::State<'_, crate::AppState>, + request: crate::DemoExecutionSplToken2022Request, +) -> std::result::Result { + let acquire_result = state.demo_execution_solana_core_running().compare_exchange( + false, + true, + std::sync::atomic::Ordering::AcqRel, + std::sync::atomic::Ordering::Acquire, + ); + if acquire_result.is_err() { + return std::result::Result::Err("a Devnet execution is already running".to_string()); + } + let _run_guard = crate::DemoExecutionSolanaCoreRunGuard { + running: state.demo_execution_solana_core_running(), + }; + state + .demo_execution_solana_core_cancel_requested() + .store(false, std::sync::atomic::Ordering::Release); + let profile = + match crate::select_devnet_profile(state.app_config(), request.profile_name.as_str()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let http_pool = match kb_rpc::HttpEndpointPool::from_profile(&profile) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let store = match kb_store_pg::PostgresStore::connect_from_profile_config(&profile).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + if let std::result::Result::Err(error) = store.initialize_store_schema().await { + return std::result::Result::Err(error.to_string()); + } + let operation = match crate::operation_from_request(&request) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let operation_code = operation.operation_code().to_string(); + let mut pipeline_request = kb_pipeline::DevnetSplToken2022ExecutionRequest::new( + format!("demo-spl-token-2022-execution-{}", chrono::Utc::now().timestamp_micros()), + operation, + ); + pipeline_request.submit = request.submit; + pipeline_request.operator_confirmed = request.operator_confirmed; + pipeline_request.post_validation_max_retries = 20; + pipeline_request.force_post_validation_replay = request.force_post_validation_replay; + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(kb_decoder_spl_token_2022::SplToken2022Decoder)]; + let materializers: std::vec::Vec> = std::vec![ + std::sync::Arc::new(kb_materializer_token_accounts::TokenAccountsMaterializer), + std::sync::Arc::new(kb_materializer_admin::AdminMaterializer), + std::sync::Arc::new(kb_materializer_risk::RiskMaterializer), + std::sync::Arc::new(kb_materializer_fees::FeesMaterializer), + std::sync::Arc::new(kb_materializer_compliance_audit::ComplianceAuditMaterializer), + ]; + let observer = crate::DemoExecutionSolanaCoreObserver { + app_handle, + cancel_requested: state.demo_execution_solana_core_cancel_requested(), + }; + let workspace_root = crate::workspace_root_dir(); + let summary = match kb_pipeline::execute_devnet_spl_token_2022( + &http_pool, + &store, + &profile, + workspace_root.as_path(), + &pipeline_request, + decoders.as_slice(), + materializers.as_slice(), + &observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + return std::result::Result::Ok(crate::demo_spl_token_2022_summary_payload( + request.scenario_id, + operation_code, + summary, + )); +} + +/// Loads public Token-2022 fixture values without reading private key bytes. +#[tauri::command] +fn demo_spl_token_2022_fixture( + state: tauri::State<'_, crate::AppState>, + profile_name: std::string::String, +) -> std::result::Result { + let profile = match crate::select_devnet_profile(state.app_config(), profile_name.as_str()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let configured = std::path::PathBuf::from(profile.wallet.wallet_dir.as_str()); + let wallet_dir = if configured.is_absolute() { + configured + } else { + crate::workspace_root_dir().join(configured) + }; + let fixture_path = wallet_dir.join("spl_token_2022_validation").join("fixture.env"); + let contents = match std::fs::read_to_string(&fixture_path) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "unable to read Token-2022 fixture {}: {error}", + fixture_path.display() + )); + }, + }; + let values = crate::parse_fixture(contents.as_str()); + let required = |name: &str| -> std::result::Result { + return match values.get(name) { + std::option::Option::Some(value) if !value.trim().is_empty() => { + std::result::Result::Ok(value.clone()) + }, + _ => std::result::Result::Err(format!("Token-2022 fixture is missing {name}")), + }; + }; + let program_id = match required("TOKEN_2022_PROGRAM") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mint = match required("TOKEN_2022_MINT") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let source = match required("TOKEN_2022_SOURCE") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let destination = match required("TOKEN_2022_DESTINATION") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let close_account = match required("TOKEN_2022_CLOSE_ACCOUNT") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let delegate = match required("TOKEN_2022_DELEGATE") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority = match required("TOKEN_2022_AUTHORITY") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let freeze_authority = match required("TOKEN_2022_FREEZE_AUTHORITY") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let decimals_text = match required("TOKEN_2022_DECIMALS") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let decimals = match decimals_text.parse::() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!("invalid TOKEN_2022_DECIMALS value: {error}")); + }, + }; + return std::result::Result::Ok(crate::DemoSplToken2022FixturePayload { + fixture_path: fixture_path.display().to_string(), + program_id, + mint, + source, + destination, + close_account, + delegate, + authority, + freeze_authority, + decimals, + default_amount_raw: values + .get("TOKEN_2022_TRANSFER_AMOUNT_RAW") + .cloned() + .unwrap_or_else(|| return "1".to_string()), + }); +} + +/// Opens or focuses the configuration demo window. +#[tauri::command] +fn open_demo_config_window( + app_handle: tauri::AppHandle, +) -> std::result::Result<(), std::string::String> { + tracing::info!(target: crate::TRACING_TARGET, "open_demo_config_window"); + let existing_window = app_handle.get_webview_window("demo_config"); + if let std::option::Option::Some(window) = existing_window { + let show_result = window.show(); + if let std::result::Result::Err(error) = show_result { + return std::result::Result::Err( + kb_core::Error::tauri(format!("cannot show demo_config window: {error}")) + .to_string(), + ); + } + let focus_result = window.set_focus(); + if let std::result::Result::Err(error) = focus_result { + return std::result::Result::Err( + kb_core::Error::tauri(format!("cannot focus demo_config window: {error}")) + .to_string(), + ); + } + return std::result::Result::Ok(()); + } + let builder = tauri::WebviewWindowBuilder::new( + &app_handle, + "demo_config", + tauri::WebviewUrl::App("demo_config.html".into()), + ) + .title("Khadhroony Bot2 - Configuration") + .inner_size(1100.0, 760.0) + .min_inner_size(860.0, 560.0) + .resizable(true) + .visible(true); + let build_result = builder.build(); + return match build_result { + std::result::Result::Ok(window) => { + let focus_result = window.set_focus(); + if let std::result::Result::Err(error) = focus_result { + return std::result::Result::Err( + kb_core::Error::tauri(format!( + "cannot focus created demo_config window: {error}" + )) + .to_string(), + ); + } + std::result::Result::Ok(()) + }, + std::result::Result::Err(error) => std::result::Result::Err( + kb_core::Error::tauri(format!("cannot create demo_config window: {error}")).to_string(), + ), + }; +} + +/// Loads the active configuration payload for the configuration demo page. +#[tauri::command] +fn load_demo_config( + state: tauri::State<'_, crate::AppState>, +) -> std::result::Result { + return std::result::Result::Ok(crate::DemoConfigPayload { + config_path: state.config_path().to_string(), + active_profile_name: state.active_profile().name.clone(), + environment: state.active_profile().app.environment.clone(), + app_config: state.app_config().clone(), + active_profile: state.active_profile().clone(), + schema_json: kb_config::config_json_schema_text().to_string(), + }); +} + +/// Opens or focuses the HTTP JSON-RPC demo window. +#[tauri::command] +fn open_demo_http_window( + app_handle: tauri::AppHandle, +) -> std::result::Result<(), std::string::String> { + tracing::info!(target: crate::TRACING_TARGET, "open_demo_http_window"); + let existing_window = app_handle.get_webview_window("demo_http"); + if let std::option::Option::Some(window) = existing_window { + let show_result = window.show(); + if let std::result::Result::Err(error) = show_result { + return std::result::Result::Err( + kb_core::Error::tauri(format!("cannot show demo_http window: {error}")).to_string(), + ); + } + let focus_result = window.set_focus(); + if let std::result::Result::Err(error) = focus_result { + return std::result::Result::Err( + kb_core::Error::tauri(format!("cannot focus demo_http window: {error}")) + .to_string(), + ); + } + return std::result::Result::Ok(()); + } + let builder = tauri::WebviewWindowBuilder::new( + &app_handle, + "demo_http", + tauri::WebviewUrl::App("demo_http.html".into()), + ) + .title("Khadhroony Bot2 - HTTP JSON-RPC") + .inner_size(1200.0, 760.0) + .min_inner_size(920.0, 560.0) + .resizable(true) + .visible(true); + let build_result = builder.build(); + return match build_result { + std::result::Result::Ok(window) => { + let focus_result = window.set_focus(); + if let std::result::Result::Err(error) = focus_result { + return std::result::Result::Err( + kb_core::Error::tauri(format!( + "cannot focus created demo_http window: {error}" + )) + .to_string(), + ); + } + std::result::Result::Ok(()) + }, + std::result::Result::Err(error) => std::result::Result::Err( + kb_core::Error::tauri(format!("cannot create demo_http window: {error}")).to_string(), + ), + }; +} + +/// Lists HTTP endpoints available through the configured pool. +#[tauri::command] +fn demo_http_list_pool_clients( + state: tauri::State<'_, crate::AppState>, +) -> std::result::Result, std::string::String> { + return std::result::Result::Ok(state.http_pool().snapshot()); +} + +/// Lists selectable HTTP roles and methods for the demo UI. +#[tauri::command] +fn demo_http_options( + state: tauri::State<'_, crate::AppState>, +) -> std::result::Result { + return std::result::Result::Ok(crate::DemoHttpOptionsPayload { + roles: crate::build_http_role_options(state.http_pool().snapshot()), + methods: crate::build_http_method_options(), + }); +} + +/// Executes one manual HTTP JSON-RPC request through the role pool. +#[tauri::command] +async fn demo_http_execute_request( + state: tauri::State<'_, crate::AppState>, + request: crate::DemoHttpRequest, +) -> std::result::Result { + return crate::demo_http_execute_request_inner(state, request).await; +} + +/// Opens or focuses the WebSocket demo window. +#[tauri::command] +fn open_demo_ws_window( + app_handle: tauri::AppHandle, +) -> std::result::Result<(), std::string::String> { + tracing::info!(target: crate::TRACING_TARGET, "open_demo_ws_window"); + if let std::option::Option::Some(window) = app_handle.get_webview_window("demo_ws") { + if let std::result::Result::Err(error) = window.show() { + return std::result::Result::Err( + kb_core::Error::tauri(format!("cannot show demo_ws window: {error}")).to_string(), + ); + } + if let std::result::Result::Err(error) = window.set_focus() { + return std::result::Result::Err( + kb_core::Error::tauri(format!("cannot focus demo_ws window: {error}")).to_string(), + ); + } + return std::result::Result::Ok(()); + } + let build_result = tauri::WebviewWindowBuilder::new( + &app_handle, + "demo_ws", + tauri::WebviewUrl::App("demo_ws.html".into()), + ) + .title("Khadhroony Bot2 - WebSocket standard") + .inner_size(1200.0, 760.0) + .min_inner_size(920.0, 560.0) + .resizable(true) + .visible(true) + .build(); + return match build_result { + std::result::Result::Ok(window) => match window.set_focus() { + std::result::Result::Ok(()) => std::result::Result::Ok(()), + std::result::Result::Err(error) => std::result::Result::Err( + kb_core::Error::tauri(format!("cannot focus created demo_ws window: {error}")) + .to_string(), + ), + }, + std::result::Result::Err(error) => std::result::Result::Err( + kb_core::Error::tauri(format!("cannot create demo_ws window: {error}")).to_string(), + ), + }; +} + +/// Lists WebSocket endpoints available through the configured pool. +#[tauri::command] +fn demo_ws_list_pool_clients( + state: tauri::State<'_, crate::AppState>, +) -> std::result::Result, std::string::String> { + return std::result::Result::Ok(state.ws_pool().snapshot()); +} + +/// Lists selectable WebSocket roles and methods for the demo UI. +#[tauri::command] +fn demo_ws_options( + state: tauri::State<'_, crate::AppState>, +) -> std::result::Result { + return std::result::Result::Ok(crate::DemoWsOptionsPayload { + roles: crate::build_ws_role_options(state.ws_pool().snapshot()), + methods: crate::build_ws_method_options(), + }); +} + +/// Returns the current WebSocket demo session status. +#[tauri::command] +async fn demo_ws_status( + state: tauri::State<'_, crate::AppState>, +) -> std::result::Result { + return crate::demo_ws_status_inner(state.inner()).await; +} + +/// Connects if needed, then subscribes through `kb_rpc::WsSession`. +#[tauri::command] +async fn demo_ws_connect( + app_handle: tauri::AppHandle, + state: tauri::State<'_, crate::AppState>, + request: crate::DemoWsRequest, +) -> std::result::Result { + return crate::demo_ws_connect_inner(app_handle, state.inner(), request).await; +} + +/// Unsubscribes one subscription while keeping the WebSocket connection open. +#[tauri::command] +async fn demo_ws_unsubscribe( + app_handle: tauri::AppHandle, + state: tauri::State<'_, crate::AppState>, + request: crate::DemoWsUnsubscribeRequest, +) -> std::result::Result { + return crate::demo_ws_unsubscribe_inner(app_handle, state.inner(), request.subscription_id) + .await; +} + +/// Disconnects the current persistent WebSocket demo session. +#[tauri::command] +async fn demo_ws_disconnect( + state: tauri::State<'_, crate::AppState>, +) -> std::result::Result { + return crate::demo_ws_disconnect_inner(state.inner()).await; +} + +/// Emits a frontend log record through Rust tracing with an explicit target. +#[tauri::command] +fn emit_frontend_log( + payload: crate::FrontendLogPayload, +) -> std::result::Result<(), std::string::String> { + let level = payload.level.trim().to_ascii_lowercase(); + let target = crate::normalize_frontend_target(&payload.target); + match level.as_str() { + "trace" => crate::emit_trace(&target, &payload.message), + "debug" => crate::emit_debug(&target, &payload.message), + "info" => crate::emit_info(&target, &payload.message), + "warn" => crate::emit_warn(&target, &payload.message), + "error" => crate::emit_error(&target, &payload.message), + _ => { + return std::result::Result::Err( + kb_core::Error::config(format!("unsupported frontend log level '{level}'")) + .to_string(), + ); + }, + } + return std::result::Result::Ok(()); +} + +/// Loads the workspace README text for the main window. +#[tauri::command] +fn load_project_readme() -> std::result::Result { + let readme_path = crate::workspace_root_dir().join("README.md"); + let content_result = std::fs::read_to_string(&readme_path); + return match content_result { + std::result::Result::Ok(content) => { + std::result::Result::Ok(crate::strip_readme_metadata_comments(&content)) + }, + std::result::Result::Err(error) => std::result::Result::Err( + kb_core::Error::io(format!("cannot read '{}': {error}", readme_path.display())) + .to_string(), + ), + }; +} + +/// Opens or focuses the SQL diagnostic demo window. +#[tauri::command] +fn open_demo_sql_diag_window( + app_handle: tauri::AppHandle, +) -> std::result::Result<(), std::string::String> { + return crate::open_sql_demo_window( + app_handle, + "demo_sql_diag", + "demo_sql_diag.html", + "Khadhroony Bot2 - SQL diagnostics", + ); +} + +/// Loads read-only SQL diagnostics from the active profile. +#[tauri::command] +async fn load_demo_sql_diag( + state: tauri::State<'_, crate::AppState>, +) -> std::result::Result { + let profile = state.active_profile().clone(); + let store_result = crate::connect_postgres_store(&profile).await; + let store = match store_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let diagnostics_result = store.backend_diagnostics().await; + let diagnostics = match diagnostics_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let tables_result = store.known_table_diagnostics().await; + let tables = match tables_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + return std::result::Result::Ok(crate::DemoSqlDiagPayload { + config_path: state.config_path().to_string(), + active_profile_name: profile.name.clone(), + backend: profile.database.backend.clone(), + masked_dsn: store.options().masked_dsn(), + current_schema: diagnostics.descriptor.current_schema, + health_status: crate::debug_status(diagnostics.health.status), + health_message: diagnostics.health.message, + migration_status: crate::debug_status(diagnostics.migrations.status), + migration_message: diagnostics.migrations.message, + server_version: diagnostics.server_version, + tables: crate::table_snapshots_from_pg(tables.as_slice()), + }); +} + +/// Opens or focuses the PostgreSQL canonical acquisition demo window. +#[tauri::command] +fn open_demo_sql_pg_raw_window( + app_handle: tauri::AppHandle, +) -> std::result::Result<(), std::string::String> { + return crate::open_sql_demo_window( + app_handle, + "demo_sql_pg_raw", + "demo_sql_pg_raw.html", + "Khadhroony Bot2 - PostgreSQL canonical acquisition", + ); +} + +/// Loads read-only raw table diagnostics from PostgreSQL. +#[tauri::command] +async fn load_demo_sql_pg_raw( + state: tauri::State<'_, crate::AppState>, +) -> std::result::Result { + let profile = state.active_profile().clone(); + let store_result = crate::connect_postgres_store(&profile).await; + let store = match store_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let tables_result = store.raw_table_diagnostics().await; + let tables = match tables_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + return std::result::Result::Ok(crate::DemoSqlPgRawPayload { + active_profile_name: profile.name.clone(), + masked_dsn: store.options().masked_dsn(), + tables: crate::table_snapshots_from_pg(tables.as_slice()), + }); +} + +/// Opens or focuses the PostgreSQL core store demo window. +#[tauri::command] +fn open_demo_sql_pg_core_window( + app_handle: tauri::AppHandle, +) -> std::result::Result<(), std::string::String> { + return crate::open_sql_demo_window( + app_handle, + "demo_sql_pg_core", + "demo_sql_pg_core.html", + "Khadhroony Bot2 - PostgreSQL core store", + ); +} + +/// Loads read-only core table diagnostics from PostgreSQL. +#[tauri::command] +async fn load_demo_sql_pg_core( + state: tauri::State<'_, crate::AppState>, +) -> std::result::Result { + let profile = state.active_profile().clone(); + let store_result = crate::connect_postgres_store(&profile).await; + let store = match store_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let tables_result = store.core_table_diagnostics().await; + let tables = match tables_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + return std::result::Result::Ok(crate::DemoSqlPgCorePayload { + active_profile_name: profile.name.clone(), + masked_dsn: store.options().masked_dsn(), + tables: crate::table_snapshots_from_pg(tables.as_slice()), + }); +} + +/// Opens or focuses the SQL replay candidate browser. +#[tauri::command] +fn open_demo_sql_replay_candidates_window( + app_handle: tauri::AppHandle, +) -> std::result::Result<(), std::string::String> { + return crate::open_sql_demo_window( + app_handle, + "demo_sql_replay_candidates", + "demo_sql_replay_candidates.html", + "Khadhroony Bot2 - SQL replay candidates", + ); +} + +/// Writes a bounded replay-candidate CSV file into `./data/exports_csv/`. +#[tauri::command] +async fn export_demo_sql_replay_csv( + file_name: std::string::String, + content: std::string::String, +) -> std::result::Result { + if content.is_empty() { + return std::result::Result::Err("CSV export content must not be empty".to_owned()); + } + if content.len() > 16 * 1024 * 1024 { + return std::result::Result::Err("CSV export content exceeds 16 MiB".to_owned()); + } + let normalized_name_result = crate::validated_csv_file_name(&file_name); + let normalized_name = match normalized_name_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let current_dir_result = std::env::current_dir(); + let current_dir = match current_dir_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!("cannot resolve current directory: {error}")); + }, + }; + let export_dir = crate::csv_export_directory_from_current_dir(¤t_dir); + let create_result = tokio::fs::create_dir_all(&export_dir).await; + if let std::result::Result::Err(error) = create_result { + return std::result::Result::Err(format!("cannot create CSV export directory: {error}")); + } + let export_path_result = crate::available_csv_export_path(&export_dir, normalized_name).await; + let export_path = match export_path_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut bytes = std::vec::Vec::with_capacity(content.len() + 3); + bytes.extend_from_slice(&[0xEF, 0xBB, 0xBF]); + bytes.extend_from_slice(content.as_bytes()); + let write_result = tokio::fs::write(&export_path, bytes).await; + if let std::result::Result::Err(error) = write_result { + return std::result::Result::Err(format!("cannot write CSV export: {error}")); + } + let display_path = export_path.to_string_lossy().into_owned(); + tracing::info!(target: crate::TRACING_TARGET, path = %display_path, "CSV replay candidate export written"); + return std::result::Result::Ok(display_path); +} + +/// Loads static browser options from the active PostgreSQL profile. +#[tauri::command] +async fn demo_sql_replay_options( + state: tauri::State<'_, crate::AppState>, +) -> std::result::Result { + let profile = state.active_profile().clone(); + let options_result = kb_store_pg::PostgresStoreOptions::from_profile_config(&profile); + let options = match options_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let mut known_programs = + std::vec::Vec::with_capacity(kb_program_ids::registered_program_ids().len()); + for entry in kb_program_ids::registered_program_ids() { + known_programs.push(crate::DemoSqlReplayKnownProgramOption { + code: entry.code().to_owned(), + program_id: entry.program_id().to_owned(), + }); + } + return std::result::Result::Ok(crate::DemoSqlReplayOptionsPayload { + active_profile_name: profile.name, + masked_dsn: options.masked_dsn(), + maximum_limit: kb_store_pg::MAX_REPLAY_CANDIDATE_ROWS, + known_programs, + }); +} + +/// Loads bounded transaction replay candidates from PostgreSQL. +#[tauri::command] +async fn load_demo_sql_replay_transactions( + state: tauri::State<'_, crate::AppState>, + request: crate::DemoSqlReplayTransactionRequest, +) -> std::result::Result, std::string::String> { + let program_scope_result = crate::program_scope_from_code(request.program_scope.as_str()); + let program_scope = match program_scope_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let entity_kind_result = crate::optional_entity_kind_from_code(request.entity_kind.as_deref()); + let entity_kind = match entity_kind_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let filter_result = kb_store_pg::PostgresReplayTransactionFilter::new( + request.signature_contains, + request.min_slot, + request.max_slot, + request.raw_processing_state, + request.ledger_status, + request.program_id, + program_scope, + entity_kind, + request.entity_value, + request.limit, + request.newest_first, + ); + let filter = match filter_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let store_result = crate::connect_postgres_store(state.active_profile()).await; + let store = match store_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let rows_result = store.replay_transaction_candidates(&filter).await; + let rows = match rows_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + tracing::debug!(target: crate::TRACING_TARGET, rows = rows.len(), "loaded replay transaction candidates"); + let mut output = std::vec::Vec::with_capacity(rows.len()); + for row in rows { + output.push(crate::transaction_row_from_pg(row)); + } + return std::result::Result::Ok(output); +} + +/// Loads bounded program summaries from PostgreSQL. +#[tauri::command] +async fn load_demo_sql_replay_programs( + state: tauri::State<'_, crate::AppState>, + request: crate::DemoSqlReplayProgramRequest, +) -> std::result::Result, std::string::String> { + let filter_result = + kb_store_pg::PostgresReplayProgramFilter::new(request.program_id_contains, request.limit); + let filter = match filter_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let store_result = crate::connect_postgres_store(state.active_profile()).await; + let store = match store_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let rows_result = store.replay_program_summaries(&filter).await; + let rows = match rows_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + tracing::debug!(target: crate::TRACING_TARGET, rows = rows.len(), "loaded replay program summaries"); + let mut output = std::vec::Vec::with_capacity(rows.len()); + for row in rows { + output.push(crate::program_row_from_pg(row)); + } + return std::result::Result::Ok(output); +} + +/// Loads bounded mint, owner or account-key summaries from PostgreSQL core tables. +#[tauri::command] +async fn load_demo_sql_replay_entities( + state: tauri::State<'_, crate::AppState>, + request: crate::DemoSqlReplayEntityRequest, +) -> std::result::Result, std::string::String> { + let entity_kind_result = crate::entity_kind_from_code(request.entity_kind.as_str()); + let entity_kind = match entity_kind_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let filter_result = kb_store_pg::PostgresReplayEntityFilter::new( + entity_kind, + request.entity_value_contains, + request.limit, + ); + let filter = match filter_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + let store_result = crate::connect_postgres_store(state.active_profile()).await; + let store = match store_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let rows_result = store.replay_entity_summaries(&filter).await; + let rows = match rows_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error.to_string()), + }; + tracing::debug!(target: crate::TRACING_TARGET, rows = rows.len(), "loaded replay entity summaries"); + let mut output = std::vec::Vec::with_capacity(rows.len()); + for row in rows { + output.push(crate::entity_row_from_pg(row)); + } + return std::result::Result::Ok(output); +} + +#[cfg(test)] +mod tests { + #[test] + fn validation_scenario_payload_keeps_registry_separate() { + let scenarios = super::demo_execution_spl_validation_scenarios(); + assert_eq!(scenarios.len(), 11); + assert!(scenarios.iter().any(|scenario| return scenario.family == "elgamal_registry")); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/tauri.conf.json b/migration/khadhroony-bot2-reference/kb_app_demo/tauri.conf.json new file mode 100644 index 0000000..6fc72d7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/tauri.conf.json @@ -0,0 +1,53 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "productName": "kb-app-demo", + "version": "0.4.6", + "identifier": "com.sasedev.kb-app-demo", + "build": { + "beforeDevCommand": "npm run dev", + "devUrl": "http://localhost:1420", + "beforeBuildCommand": "npm run build", + "frontendDist": "./dist" + }, + "app": { + "withGlobalTauri": true, + "windows": [ + { + "label": "splash", + "url": "splash.html", + "title": "Loading ... Khadhroony Bot2", + "width": 960, + "height": 637, + "resizable": false, + "decorations": false, + "transparent": true, + "center": true, + "alwaysOnTop": true + }, + { + "label": "main", + "url": "main.html", + "title": "Khadhroony-Bot2", + "width": 1024, + "height": 768, + "minWidth": 800, + "minHeight": 600, + "center": true, + "visible": false, + "transparent": false, + "decorations": true + } + ], + "security": { + "csp": null + } + }, + "bundle": { + "active": true, + "targets": "all", + "icon": [ + "icons/favicon.png", + "icons/favicon.ico" + ] + } +} diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/tsconfig.json b/migration/khadhroony-bot2-reference/kb_app_demo/tsconfig.json new file mode 100644 index 0000000..016ec89 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/tsconfig.json @@ -0,0 +1,31 @@ +{ + "compilerOptions": { + "target": "ES2020", + "useDefineForClassFields": true, + "module": "ESNext", + "lib": [ + "ES2020", + "DOM", + "DOM.Iterable" + ], + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "allowSyntheticDefaultImports": true, + "types": [ + "vite/client", + "node" + ] + }, + "include": [ + "frontend", + "vite.config.ts" + ] +} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_app_demo/vite.config.ts b/migration/khadhroony-bot2-reference/kb_app_demo/vite.config.ts new file mode 100644 index 0000000..487ae7f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_app_demo/vite.config.ts @@ -0,0 +1,97 @@ +// file: kb_app_demo/vite.config.ts +// version: 13 + +import { defineConfig, normalizePath } from "vite"; +import { NodePackageImporter } from "sass-embedded"; +import { resolve } from 'path'; + +const host = process.env.TAURI_DEV_HOST; + +// https://vite.dev/config/ +export default defineConfig(() => ({ + + envPrefix: ['VITE_', 'TAURI_ENV_*'], + // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` + // + // 1. prevent Vite from obscuring rust errors + clearScreen: false, + root: 'frontend', // Set this to your frontend directory + publicDir: 'public', + build: { + outDir: './dist', // Output directory for the build + emptyOutDir: true, + rollupOptions: { + input: { + "splash": normalizePath(resolve(__dirname, 'frontend/splash.html')), + "main": normalizePath(resolve(__dirname, 'frontend/main.html')), + "demo_config": normalizePath(resolve(__dirname, 'frontend/demo_config.html')), + "demo_http": normalizePath(resolve(__dirname, 'frontend/demo_http.html')), + "demo_ws": normalizePath(resolve(__dirname, 'frontend/demo_ws.html')), + "demo_backfill": normalizePath(resolve(__dirname, 'frontend/demo_backfill.html')), + "demo_core_extraction": normalizePath(resolve(__dirname, 'frontend/demo_core_extraction.html')), + "demo_decode_replay": normalizePath(resolve(__dirname, 'frontend/demo_decode_replay.html')), + "demo_execution_solana_core": normalizePath(resolve(__dirname, 'frontend/demo_execution_solana_core.html')), + "demo_sql_diag": normalizePath(resolve(__dirname, 'frontend/demo_sql_diag.html')), + "demo_sql_pg_raw": normalizePath(resolve(__dirname, 'frontend/demo_sql_pg_raw.html')), + "demo_sql_pg_core": normalizePath(resolve(__dirname, 'frontend/demo_sql_pg_core.html')), + "demo_sql_replay_candidates": normalizePath(resolve(__dirname, 'frontend/demo_sql_replay_candidates.html')), + "demo_execution_spl": normalizePath(resolve(__dirname, 'frontend/demo_execution_spl.html')) + }, + output: { + entryFileNames: 'js/[name]-[hash].js', + chunkFileNames: 'js/chunks/[name]-[hash].js', + assetFileNames: (assetInfo) => { + const originalName = assetInfo.name ?? ''; + const ext = originalName.substring(originalName.lastIndexOf('.') + 1).toLowerCase(); + if (ext === 'js') { + return 'js/[name]-[hash][extname]'; + } + // CSS + if (ext === 'css') { + return 'css/[name]-[hash][extname]'; + } + if (['eot', 'otf', 'ttf', 'woff', 'woff2'].includes(ext)) { + return 'fonts/[name]-[hash][extname]'; + } + if (['png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'ico'].includes(ext)) { + return 'imgs/[name][extname]'; + } + if (['mp4', 'webm'].includes(ext)) { + return 'videos/[name][extname]'; + } + return 'otherassets/[name][extname]'; + }, + }, + }, + minify: true, + sourcemap: false, + cssCodeSplit: true + }, + css: { + preprocessorOptions: { + scss: { + quietDeps: true, + silenceDeprecations: ["import", "color-functions", "global-builtin",], + verbose: false, + api: 'modern', + importers: [new NodePackageImporter()], + } + } + }, + // 2. tauri expects a fixed port, fail if that port is not available + server: { + port: 1420, + strictPort: false, + host: host || false, + hmr: host + ? { + protocol: "ws", + host, + port: 1421, + } + : undefined, + watch: { + ignored: ["**/src/**"], + }, + }, +})); diff --git a/migration/khadhroony-bot2-reference/kb_config/Cargo.toml b/migration/khadhroony-bot2-reference/kb_config/Cargo.toml new file mode 100644 index 0000000..3ad0fb3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_config/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_config/Cargo.toml +# version: 3 + +[package] +name = "kb_config" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +jsonschema.workspace = true +serde.workspace = true +serde_json.workspace = true +ts-rs.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_config/README.md b/migration/khadhroony-bot2-reference/kb_config/README.md new file mode 100644 index 0000000..11d7f88 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_config/README.md @@ -0,0 +1,68 @@ + + + +# kb_config + +Ce crate définit le contrat de configuration commun du workspace. + +## Rôle dans l'écosystème + +`kb_config` charge, valide et sérialise un fichier JSON contenant plusieurs profils. Un seul profil est actif à l'exécution grâce au champ `active_profile`. + +Le crate fournit les structures typées runtime pour : + +- l'application et l'environnement ; +- les sorties de logs console, fichier humain, fichier JSON et fichier erreurs ; +- les filtres de targets `tracing` ; +- les stores PostgreSQL et SQLite ; +- les endpoints HTTP JSON-RPC et WebSocket Solana ; +- les rôles et limites par endpoint ; +- les listeners Solana standards ; +- le wallet temporaire géré par profil ; +- les permissions d'envoi localnet/devnet/testnet/mainnet ; +- les plafonds de dépense, frais, priorité et fraîcheur du blockhash ; +- les pages de démonstration Tauri. + +## Wallet temporaire + +Le profil `local_devnet` active un wallet persistant de laboratoire sous : + +```text +wallets/temporary/local_devnet/local-devnet-operator.json +``` + +Les profils mainnet conservent `temporary_wallet_enabled = false`. La validation typée refuse un wallet temporaire actif sur `mainnet-beta`, une persistance sans activation et les alias pouvant produire un chemin arbitraire. + +## Exécution + +La configuration sépare : + +- le dry-run par défaut ; +- l'obligation de simulation ; +- la confirmation opérateur ; +- les plafonds de dépense localnet, devnet, testnet et mainnet ; +- le plafond de frais ; +- le plafond de prix par compute unit ; +- l'âge maximal du recent blockhash. + +Un cluster dont l'envoi est activé doit disposer d'un plafond de dépense positif. Toute dépense configurée exige la simulation et toute dépense mainnet exige la confirmation opérateur. Les paramètres de confirmation sont bornés et le plafond d’airdrop Devnet est interdit dans les profils d’un autre cluster. + +## Validation + +La validation se fait en deux étapes : + +1. validation JSON brute via `config/schema.config.json` et `jsonschema` ; +2. validation typée après désérialisation Rust. + +## Secrets et placeholders + +Le fichier versionné doit rester sans secret. Les placeholders restent directement dans les URLs. Les keypairs générés par `kb_wallet` vivent hors du fichier de configuration et sous un répertoire ignoré par Git. + +## Exports TS-rs + +Les structures exposées à Tauri utilisent `TS` avec un chemin `export_to` explicite. Les fichiers générés servent uniquement au partage des types Rust/TypeScript et ne remplacent pas le schéma JSON runtime. + +```bash +cargo test export_bindings -p kb_config +cargo test -p kb_config settings::tests:: +``` diff --git a/migration/khadhroony-bot2-reference/kb_config/src/lib.rs b/migration/khadhroony-bot2-reference/kb_config/src/lib.rs new file mode 100644 index 0000000..b9c7a31 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_config/src/lib.rs @@ -0,0 +1,70 @@ +// file: kb_config/src/lib.rs +// version: 2 + +//! Workspace configuration contract and loading helpers. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod settings; + +/// Exposes the account listener configuration type. +pub use crate::settings::AccountListenerConfig; +/// Exposes the root application configuration type. +pub use crate::settings::AppConfig; +/// Exposes the application section configuration type. +pub use crate::settings::AppSectionConfig; +/// Exposes the local data configuration type. +pub use crate::settings::DataConfig; +/// Exposes the database configuration type. +pub use crate::settings::DatabaseConfig; +/// Exposes the demo application configuration type. +pub use crate::settings::DemoConfig; +/// Exposes the endpoint role configuration type. +pub use crate::settings::EndpointRoleConfig; +/// Exposes the execution configuration type. +pub use crate::settings::ExecutionConfig; +/// Exposes the HTTP endpoint configuration type. +pub use crate::settings::HttpEndpointConfig; +/// Exposes the listener configuration type. +pub use crate::settings::ListenerConfig; +/// Exposes the log listener configuration type. +pub use crate::settings::LogListenerConfig; +/// Exposes the logging target configuration type. +pub use crate::settings::LogTargetConfig; +/// Exposes the logging target filter configuration type. +pub use crate::settings::LogTargetFilterConfig; +/// Exposes the logging configuration type. +pub use crate::settings::LoggingConfig; +/// Exposes the PostgreSQL configuration type. +pub use crate::settings::PostgresConfig; +/// Exposes the profile configuration type. +pub use crate::settings::ProfileConfig; +/// Exposes the program listener configuration type. +pub use crate::settings::ProgramListenerConfig; +/// Exposes the Solana configuration type. +pub use crate::settings::SolanaConfig; +/// Exposes the SQLite configuration type. +pub use crate::settings::SqliteConfig; +/// Exposes the wallet configuration type. +pub use crate::settings::WalletConfig; +/// Exposes the WebSocket endpoint configuration type. +pub use crate::settings::WsEndpointConfig; +/// Exposes the active profile resolver. +pub use crate::settings::active_profile; +/// Exposes the embedded JSON Schema text. +pub use crate::settings::config_json_schema_text; +/// Exposes the embedded JSON Schema value parser. +pub use crate::settings::config_json_schema_value; +/// Exposes the configuration parser from a JSON string. +pub use crate::settings::parse_config_json; +/// Exposes the configuration loader from a filesystem path. +pub use crate::settings::read_config_json_file; +/// Exposes the compact JSON serializer for configuration values. +pub use crate::settings::serialize_config_json; +/// Exposes the pretty JSON serializer for configuration values. +pub use crate::settings::serialize_config_json_pretty; +/// Exposes the typed configuration validator. +pub use crate::settings::validate_config; +/// Exposes the JSON Schema validator for raw JSON configuration. +pub use crate::settings::validate_config_json_schema; diff --git a/migration/khadhroony-bot2-reference/kb_config/src/settings.rs b/migration/khadhroony-bot2-reference/kb_config/src/settings.rs new file mode 100644 index 0000000..6317b5f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_config/src/settings.rs @@ -0,0 +1,1530 @@ +// file: kb_config/src/settings.rs +// version: 13 + +//! Typed configuration models shared by applications and workers. + +use ts_rs::TS; // rust-rules: derive-import + +const CONFIG_JSON_SCHEMA: &str = include_str!("../../config/schema.config.json"); + +/// Root configuration containing every named profile. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_config/settings/AppConfig.ts")] +pub struct AppConfig { + /// Active profile name. + pub active_profile: std::string::String, + /// Named profiles available in this configuration file. + pub profiles: std::vec::Vec, +} + +/// Configuration profile selected by the root active profile name. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_config/settings/ProfileConfig.ts" +)] +pub struct ProfileConfig { + /// Profile code. + pub name: std::string::String, + /// Application metadata. + pub app: AppSectionConfig, + /// Logging configuration. + pub logging: LoggingConfig, + /// Database configuration. + pub database: DatabaseConfig, + /// Local data directories. + pub data: DataConfig, + /// Solana endpoint and listener configuration. + pub solana: SolanaConfig, + /// Wallet configuration. + pub wallet: WalletConfig, + /// Execution safety configuration. + pub execution: ExecutionConfig, + /// Demo application configuration. + pub demo: DemoConfig, +} + +/// Application metadata for a profile. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_config/settings/AppSectionConfig.ts" +)] +pub struct AppSectionConfig { + /// Application name. + pub name: std::string::String, + /// Environment name. + pub environment: std::string::String, + /// Default auto reconnect flag used when a transport does not override it. + pub auto_reconnect_default: bool, +} + +/// Logging configuration shared by apps and worker processes. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_config/settings/LoggingConfig.ts" +)] +pub struct LoggingConfig { + /// Default log level. + pub default_level: std::string::String, + /// Output targets. + pub targets: std::vec::Vec, + /// Target-specific filters. + pub target_filters: std::vec::Vec, +} + +/// Single logging output target. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_config/settings/LogTargetConfig.ts" +)] +pub struct LogTargetConfig { + /// Output target name. + pub name: std::string::String, + /// Enables this target. + pub enabled: bool, + /// Sink kind, such as console or file. + pub sink: std::string::String, + /// Minimum level for this target. + pub level: std::string::String, + /// File path for file sinks or an empty string for console sinks. + pub path: std::string::String, + /// Rotation mode for file sinks. + pub rotation: std::string::String, + /// Message format, such as human or json. + pub format: std::string::String, + /// Enables ANSI escape sequences for this target. + pub ansi: bool, + /// Included tracing targets or globs. + pub targets: std::vec::Vec, +} + +/// Per-target logging filter. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_config/settings/LogTargetFilterConfig.ts" +)] +pub struct LogTargetFilterConfig { + /// Tracing target or crate prefix. + pub target: std::string::String, + /// Level assigned to the target. + pub level: std::string::String, +} + +/// Database backend configuration. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_config/settings/DatabaseConfig.ts" +)] +pub struct DatabaseConfig { + /// Enables database storage. + pub enabled: bool, + /// Selected backend code. + pub backend: std::string::String, + /// PostgreSQL configuration. + pub postgres: PostgresConfig, + /// SQLite configuration for tests or imports. + pub sqlite: SqliteConfig, +} + +/// PostgreSQL backend configuration. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_config/settings/PostgresConfig.ts" +)] +pub struct PostgresConfig { + /// PostgreSQL URL or local placeholder. + pub url: std::string::String, + /// Maximum connection count. + pub max_connections: u32, + /// Connection timeout in milliseconds. + #[ts(type = "number")] + pub connect_timeout_ms: u64, + /// Enables schema initialization at startup. + pub auto_initialize_schema: bool, +} + +/// SQLite backend configuration. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_config/settings/SqliteConfig.ts")] +pub struct SqliteConfig { + /// SQLite database path. + pub path: std::string::String, + /// Creates the file when missing. + pub create_if_missing: bool, + /// Busy timeout in milliseconds. + #[ts(type = "number")] + pub busy_timeout_ms: u64, + /// Maximum connection count. + pub max_connections: u32, + /// Enables schema initialization at startup. + pub auto_initialize_schema: bool, + /// Enables WAL mode. + pub use_wal: bool, +} + +/// Local data directory configuration. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_config/settings/DataConfig.ts")] +pub struct DataConfig { + /// Wallet directory path. + pub wallets_directory: std::string::String, + /// Logs directory path. + pub logs_directory: std::string::String, +} + +/// Solana endpoints and listener configuration. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_config/settings/SolanaConfig.ts")] +pub struct SolanaConfig { + /// HTTP JSON-RPC endpoints. + pub http_endpoints: std::vec::Vec, + /// Standard Solana WebSocket endpoints. + pub ws_endpoints: std::vec::Vec, + /// Listener declarations. + pub listeners: ListenerConfig, +} + +/// HTTP JSON-RPC endpoint configuration. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_config/settings/HttpEndpointConfig.ts" +)] +pub struct HttpEndpointConfig { + /// Endpoint name. + pub name: std::string::String, + /// Enables this endpoint. + pub enabled: bool, + /// Provider code. + pub provider: std::string::String, + /// Cluster code. + pub cluster: std::string::String, + /// Full endpoint URL. + pub url: std::string::String, + /// Connection timeout in milliseconds. + #[ts(type = "number")] + pub connect_timeout_ms: u64, + /// Request timeout in milliseconds. + #[ts(type = "number")] + pub request_timeout_ms: u64, + /// Maximum idle connections per host. + pub max_idle_connections_per_host: u32, + /// Role-specific limits for this URL. + pub roles: std::vec::Vec, +} + +/// Standard Solana WebSocket endpoint configuration. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_config/settings/WsEndpointConfig.ts" +)] +pub struct WsEndpointConfig { + /// Endpoint name. + pub name: std::string::String, + /// Enables this endpoint. + pub enabled: bool, + /// Provider code. + pub provider: std::string::String, + /// Cluster code. + pub cluster: std::string::String, + /// Full endpoint URL. + pub url: std::string::String, + /// Connection timeout in milliseconds. + #[ts(type = "number")] + pub connect_timeout_ms: u64, + /// Request timeout in milliseconds. + #[ts(type = "number")] + pub request_timeout_ms: u64, + /// Unsubscribe timeout in milliseconds. + #[ts(type = "number")] + pub unsubscribe_timeout_ms: u64, + /// Writer channel capacity. + pub write_channel_capacity: u32, + /// Event channel capacity. + pub event_channel_capacity: u32, + /// Enables automatic reconnect for this endpoint. + pub auto_reconnect: bool, + /// Role-specific limits for this URL. + pub roles: std::vec::Vec, +} + +/// Role-specific endpoint limits. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_config/settings/EndpointRoleConfig.ts" +)] +pub struct EndpointRoleConfig { + /// Role code used by endpoint pools. + pub role: std::string::String, + /// Enables this role on the endpoint. + pub enabled: bool, + /// Request or subscription kinds handled by this role. + pub request_kinds: std::vec::Vec, + /// Role priority where lower values are preferred. + pub priority: u32, + /// Requests per second allowed for this role on this URL. + pub requests_per_second: u32, + /// Burst capacity allowed for this role on this URL. + pub burst_capacity: u32, + /// Maximum concurrent requests allowed for this role on this URL. + pub max_concurrent_requests: u32, + /// Maximum subscriptions allowed for this role on this URL. + pub max_subscriptions: u32, + /// Pause after a rate limit response in milliseconds. + #[ts(type = "number")] + pub pause_after_rate_limit_ms: u64, +} + +/// Listener configuration used by standard WebSocket subscriptions. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_config/settings/ListenerConfig.ts" +)] +pub struct ListenerConfig { + /// Enables listener creation. + pub enabled: bool, + /// Default commitment used by listener subscriptions. + pub default_commitment: std::string::String, + /// Log listeners filtered by program mentions. + pub log_listeners: std::vec::Vec, + /// Program account listeners. + pub program_listeners: std::vec::Vec, + /// Account listeners. + pub account_listeners: std::vec::Vec, +} + +/// Log listener filtered by a program identifier mention. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_config/settings/LogListenerConfig.ts" +)] +pub struct LogListenerConfig { + /// Listener name. + pub name: std::string::String, + /// Enables this listener. + pub enabled: bool, + /// Required endpoint role. + pub endpoint_role: std::string::String, + /// Program identifier used as logsSubscribe mention. + pub program_id: std::string::String, + /// Human-readable listener purpose. + pub purpose: std::string::String, +} + +/// Program account listener. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_config/settings/ProgramListenerConfig.ts" +)] +pub struct ProgramListenerConfig { + /// Listener name. + pub name: std::string::String, + /// Enables this listener. + pub enabled: bool, + /// Required endpoint role. + pub endpoint_role: std::string::String, + /// Program identifier used by programSubscribe. + pub program_id: std::string::String, + /// Human-readable listener purpose. + pub purpose: std::string::String, +} + +/// Account listener. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_config/settings/AccountListenerConfig.ts" +)] +pub struct AccountListenerConfig { + /// Listener name. + pub name: std::string::String, + /// Enables this listener. + pub enabled: bool, + /// Required endpoint role. + pub endpoint_role: std::string::String, + /// Account public key used by accountSubscribe. + pub account_pubkey: std::string::String, + /// Human-readable listener purpose. + pub purpose: std::string::String, +} + +/// Wallet configuration. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_config/settings/WalletConfig.ts")] +pub struct WalletConfig { + /// Wallet directory path. + pub wallet_dir: std::string::String, + /// Selected cluster name. + pub cluster: std::string::String, + /// Enables the managed temporary wallet for this profile. + pub temporary_wallet_enabled: bool, + /// Alias used for the managed temporary wallet. + pub temporary_wallet_alias: std::string::String, + /// Persists the managed temporary wallet between process restarts. + pub temporary_wallet_persist: bool, + /// Enables local validator transaction sending. + pub localnet_send_enabled: bool, + /// Enables devnet transaction sending. + pub devnet_send_enabled: bool, + /// Enables testnet transaction sending. + pub testnet_send_enabled: bool, + /// Enables mainnet transaction sending. + pub mainnet_send_enabled: bool, +} + +/// Execution safety configuration. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_config/settings/ExecutionConfig.ts" +)] +pub struct ExecutionConfig { + /// Enables dry-run for newly prepared execution policies. + pub dry_run_default: bool, + /// Requires simulation before send. + pub require_simulation: bool, + /// Requires explicit operator confirmation before send. + pub require_operator_confirmation: bool, + /// Maximum spend in lamports for local validator tests. + #[ts(type = "number")] + pub localnet_max_spend_lamports: u64, + /// Maximum spend in lamports for devnet tests. + #[ts(type = "number")] + pub devnet_max_spend_lamports: u64, + /// Maximum spend in lamports for testnet tests. + #[ts(type = "number")] + pub testnet_max_spend_lamports: u64, + /// Maximum spend in lamports for mainnet operations. + #[ts(type = "number")] + pub mainnet_max_spend_lamports: u64, + /// Maximum estimated transaction fee in lamports. + #[ts(type = "number")] + pub max_fee_lamports: u64, + /// Maximum compute-unit price in micro-lamports. + #[ts(type = "number")] + pub max_compute_unit_price_micro_lamports: u64, + /// Maximum accepted age for a recent blockhash in slots. + #[ts(type = "number")] + pub recent_blockhash_max_age_slots: u64, + /// Maximum node retransmission retries requested by `sendTransaction`. + pub send_max_retries: u32, + /// Delay between transaction confirmation polls. + #[ts(type = "number")] + pub confirmation_poll_interval_ms: u64, + /// Maximum number of transaction confirmation polls. + pub confirmation_max_attempts: u32, + /// Maximum devnet faucet airdrop allowed for a temporary wallet. + #[ts(type = "number")] + pub devnet_airdrop_max_lamports: u64, +} + +/// Demo application configuration. +#[derive(Clone, Debug, serde::Deserialize, Eq, PartialEq, serde::Serialize, TS)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_config/settings/DemoConfig.ts")] +pub struct DemoConfig { + /// Enables live demo pages. + pub live_demo_enabled: bool, + /// Enables trading demo pages. + pub trading_demo_enabled: bool, +} + +/// Returns the embedded JSON Schema text used for configuration validation. +pub fn config_json_schema_text() -> &'static str { + return CONFIG_JSON_SCHEMA; +} + +/// Parses the embedded JSON Schema into a JSON value. +pub fn config_json_schema_value() -> kb_core::Result { + let schema_result = serde_json::from_str::(CONFIG_JSON_SCHEMA); + return match schema_result { + std::result::Result::Ok(schema) => std::result::Result::Ok(schema), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "config_schema_parse_failed", + error.to_string(), + )), + }; +} + +/// Validates a raw JSON configuration string against the embedded JSON Schema. +pub fn validate_config_json_schema(raw_json: &str) -> kb_core::Result<()> { + let schema = match config_json_schema_value() { + std::result::Result::Ok(schema) => schema, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instance = match serde_json::from_str::(raw_json) { + std::result::Result::Ok(instance) => instance, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "config_json_parse_failed", + error.to_string(), + )); + }, + }; + let validator = match jsonschema::validator_for(&schema) { + std::result::Result::Ok(validator) => validator, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "config_schema_compile_failed", + error.to_string(), + )); + }, + }; + let validation_result = validator.validate(&instance); + return match validation_result { + std::result::Result::Ok(()) => std::result::Result::Ok(()), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "config_json_schema_validation_failed", + error.to_string(), + )), + }; +} + +/// Parses the application configuration from a JSON string and validates it. +pub fn parse_config_json(raw_json: &str) -> kb_core::Result { + match validate_config_json_schema(raw_json) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let config = match serde_json::from_str::(raw_json) { + std::result::Result::Ok(config) => config, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "config_json_decode_failed", + error.to_string(), + )); + }, + }; + return match validate_config(&config) { + std::result::Result::Ok(()) => std::result::Result::Ok(config), + std::result::Result::Err(error) => std::result::Result::Err(error), + }; +} + +/// Reads and parses the application configuration from a filesystem path. +pub fn read_config_json_file(path: &std::path::Path) -> kb_core::Result { + let raw_json = match std::fs::read_to_string(path) { + std::result::Result::Ok(content) => content, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "config_file_read_failed", + error.to_string(), + )); + }, + }; + return parse_config_json(&raw_json); +} + +/// Serializes a configuration value to compact JSON. +pub fn serialize_config_json(config: &AppConfig) -> kb_core::Result { + match validate_config(config) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + return match serde_json::to_string(config) { + std::result::Result::Ok(serialized) => std::result::Result::Ok(serialized), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "config_json_serialize_failed", + error.to_string(), + )), + }; +} + +/// Serializes a configuration value to pretty JSON. +pub fn serialize_config_json_pretty(config: &AppConfig) -> kb_core::Result { + match validate_config(config) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + return match serde_json::to_string_pretty(config) { + std::result::Result::Ok(serialized) => std::result::Result::Ok(serialized), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "config_json_pretty_serialize_failed", + error.to_string(), + )), + }; +} + +/// Returns the active profile declared by the root configuration. +pub fn active_profile(config: &AppConfig) -> kb_core::Result<&ProfileConfig> { + for profile in &config.profiles { + if profile.name == config.active_profile { + return std::result::Result::Ok(profile); + } + } + return std::result::Result::Err(kb_core::Error::new( + "active_profile_not_found", + config.active_profile.clone(), + )); +} + +/// Validates a typed configuration after JSON Schema validation and deserialization. +pub fn validate_config(config: &AppConfig) -> kb_core::Result<()> { + match validate_root_config(config) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + for profile in &config.profiles { + match validate_profile(profile) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + return match active_profile(config) { + std::result::Result::Ok(_) => std::result::Result::Ok(()), + std::result::Result::Err(error) => std::result::Result::Err(error), + }; +} + +fn validate_root_config(config: &AppConfig) -> kb_core::Result<()> { + match require_non_empty(&config.active_profile, "active_profile") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if config.profiles.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "config_profiles_empty", + "at least one profile is required", + )); + } + let mut names = std::collections::BTreeSet::::new(); + let mut active_count = 0_u32; + for profile in &config.profiles { + match require_non_empty(&profile.name, "profile.name") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if !names.insert(profile.name.clone()) { + return std::result::Result::Err(kb_core::Error::new( + "profile_name_duplicate", + profile.name.clone(), + )); + } + if profile.name == config.active_profile { + active_count += 1; + } + } + if active_count != 1 { + return std::result::Result::Err(kb_core::Error::new( + "active_profile_count_invalid", + format!("active profile '{}' must match exactly one profile", config.active_profile), + )); + } + return std::result::Result::Ok(()); +} + +fn validate_profile(profile: &ProfileConfig) -> kb_core::Result<()> { + match validate_app_section(&profile.app) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match validate_logging(&profile.logging) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match validate_database(&profile.database) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match validate_data(&profile.data) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match validate_solana(&profile.solana) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match validate_wallet(&profile.wallet) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match validate_execution(&profile.execution) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + return validate_wallet_execution_pair(&profile.wallet, &profile.execution); +} + +fn validate_app_section(config: &AppSectionConfig) -> kb_core::Result<()> { + match require_non_empty(&config.name, "app.name") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + return require_non_empty(&config.environment, "app.environment"); +} + +fn validate_logging(config: &LoggingConfig) -> kb_core::Result<()> { + match validate_log_level(&config.default_level, "logging.default_level") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if config.targets.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "logging_targets_empty", + "at least one logging target is required", + )); + } + let mut enabled_count = 0_u32; + let mut names = std::collections::BTreeSet::::new(); + for target in &config.targets { + if target.enabled { + enabled_count += 1; + } + if !names.insert(target.name.clone()) { + return std::result::Result::Err(kb_core::Error::new( + "logging_target_duplicate", + target.name.clone(), + )); + } + match validate_log_target(target) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + for filter in &config.target_filters { + match require_non_empty(&filter.target, "logging.target_filters.target") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match validate_log_level(&filter.level, "logging.target_filters.level") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + if enabled_count == 0 { + return std::result::Result::Err(kb_core::Error::new( + "logging_no_enabled_targets", + "at least one logging target must be enabled", + )); + } + return std::result::Result::Ok(()); +} + +fn validate_log_target(config: &LogTargetConfig) -> kb_core::Result<()> { + match require_non_empty(&config.name, "logging.targets.name") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match validate_log_level(&config.level, "logging.targets.level") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if config.sink != "console" && config.sink != "file" { + return std::result::Result::Err(kb_core::Error::new( + "logging_sink_invalid", + config.sink.clone(), + )); + } + if config.rotation != "none" + && config.rotation != "never" + && config.rotation != "daily" + && config.rotation != "hourly" + { + return std::result::Result::Err(kb_core::Error::new( + "logging_rotation_invalid", + config.rotation.clone(), + )); + } + if config.format != "human" + && config.format != "compact" + && config.format != "pretty" + && config.format != "json" + { + return std::result::Result::Err(kb_core::Error::new( + "logging_format_invalid", + config.format.clone(), + )); + } + if config.sink == "file" && config.path.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "logging_file_path_empty", + config.name.clone(), + )); + } + if config.sink == "console" && !config.path.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "logging_console_path_not_empty", + config.name.clone(), + )); + } + for target in &config.targets { + match require_non_empty(target, "logging.targets.targets") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + return std::result::Result::Ok(()); +} + +fn validate_database(config: &DatabaseConfig) -> kb_core::Result<()> { + if config.backend != "postgres" && config.backend != "sqlite" { + return std::result::Result::Err(kb_core::Error::new( + "database_backend_invalid", + config.backend.clone(), + )); + } + match require_non_empty(&config.postgres.url, "database.postgres.url") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if config.postgres.max_connections == 0 || config.sqlite.max_connections == 0 { + return std::result::Result::Err(kb_core::Error::new( + "database_max_connections_zero", + "database connection limits must be greater than zero", + )); + } + if config.postgres.connect_timeout_ms == 0 || config.sqlite.busy_timeout_ms == 0 { + return std::result::Result::Err(kb_core::Error::new( + "database_timeout_zero", + "database timeouts must be greater than zero", + )); + } + return require_non_empty(&config.sqlite.path, "database.sqlite.path"); +} + +fn validate_data(config: &DataConfig) -> kb_core::Result<()> { + match require_non_empty(&config.wallets_directory, "data.wallets_directory") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + return require_non_empty(&config.logs_directory, "data.logs_directory"); +} + +fn validate_solana(config: &SolanaConfig) -> kb_core::Result<()> { + if config.http_endpoints.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "http_endpoints_empty", + "at least one HTTP JSON-RPC endpoint is required", + )); + } + if config.ws_endpoints.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "ws_endpoints_empty", + "at least one standard Solana WebSocket endpoint is required", + )); + } + let mut http_names = std::collections::BTreeSet::::new(); + for endpoint in &config.http_endpoints { + if !http_names.insert(endpoint.name.clone()) { + return std::result::Result::Err(kb_core::Error::new( + "http_endpoint_duplicate", + endpoint.name.clone(), + )); + } + match validate_http_endpoint(endpoint) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + let mut ws_names = std::collections::BTreeSet::::new(); + for endpoint in &config.ws_endpoints { + if !ws_names.insert(endpoint.name.clone()) { + return std::result::Result::Err(kb_core::Error::new( + "ws_endpoint_duplicate", + endpoint.name.clone(), + )); + } + match validate_ws_endpoint(endpoint) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + return validate_listeners(&config.listeners); +} + +fn validate_http_endpoint(config: &HttpEndpointConfig) -> kb_core::Result<()> { + match validate_endpoint_common(&config.name, &config.provider, &config.cluster, &config.roles) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if !config.url.starts_with("http://") && !config.url.starts_with("https://") { + return std::result::Result::Err(kb_core::Error::new( + "http_endpoint_url_invalid", + config.url.clone(), + )); + } + if config.connect_timeout_ms == 0 + || config.request_timeout_ms == 0 + || config.max_idle_connections_per_host == 0 + { + return std::result::Result::Err(kb_core::Error::new( + "http_endpoint_limit_zero", + config.name.clone(), + )); + } + for role in &config.roles { + if role.max_subscriptions != 0 { + return std::result::Result::Err(kb_core::Error::new( + "http_endpoint_subscriptions_invalid", + role.role.clone(), + )); + } + } + return std::result::Result::Ok(()); +} + +fn validate_ws_endpoint(config: &WsEndpointConfig) -> kb_core::Result<()> { + match validate_endpoint_common(&config.name, &config.provider, &config.cluster, &config.roles) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if !config.url.starts_with("ws://") && !config.url.starts_with("wss://") { + return std::result::Result::Err(kb_core::Error::new( + "ws_endpoint_url_invalid", + config.url.clone(), + )); + } + if config.connect_timeout_ms == 0 + || config.request_timeout_ms == 0 + || config.unsubscribe_timeout_ms == 0 + || config.write_channel_capacity == 0 + || config.event_channel_capacity == 0 + { + return std::result::Result::Err(kb_core::Error::new( + "ws_endpoint_limit_zero", + config.name.clone(), + )); + } + return std::result::Result::Ok(()); +} + +fn validate_endpoint_common( + name: &str, + provider: &str, + cluster: &str, + roles: &[EndpointRoleConfig], +) -> kb_core::Result<()> { + match require_non_empty(name, "endpoint.name") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match require_non_empty(provider, "endpoint.provider") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match require_non_empty(cluster, "endpoint.cluster") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if roles.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "endpoint_roles_empty", + name.to_string(), + )); + } + let mut role_names = std::collections::BTreeSet::::new(); + for role in roles { + if !role_names.insert(role.role.clone()) { + return std::result::Result::Err(kb_core::Error::new( + "endpoint_role_duplicate", + role.role.clone(), + )); + } + match validate_endpoint_role(role) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + return std::result::Result::Ok(()); +} + +fn validate_endpoint_role(config: &EndpointRoleConfig) -> kb_core::Result<()> { + match require_non_empty(&config.role, "endpoint.roles.role") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if config.request_kinds.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "endpoint_role_request_kinds_empty", + config.role.clone(), + )); + } + for request_kind in &config.request_kinds { + match require_non_empty(request_kind, "endpoint.roles.request_kinds") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + if config.requests_per_second == 0 + || config.burst_capacity == 0 + || config.max_concurrent_requests == 0 + || config.pause_after_rate_limit_ms == 0 + { + return std::result::Result::Err(kb_core::Error::new( + "endpoint_role_limit_zero", + config.role.clone(), + )); + } + return std::result::Result::Ok(()); +} + +fn validate_listeners(config: &ListenerConfig) -> kb_core::Result<()> { + match require_non_empty(&config.default_commitment, "solana.listeners.default_commitment") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + for listener in &config.log_listeners { + match validate_log_listener(listener) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + for listener in &config.program_listeners { + match validate_program_listener(listener) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + for listener in &config.account_listeners { + match validate_account_listener(listener) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + return std::result::Result::Ok(()); +} + +fn validate_log_listener(config: &LogListenerConfig) -> kb_core::Result<()> { + match require_non_empty(&config.name, "log_listener.name") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match require_non_empty(&config.endpoint_role, "log_listener.endpoint_role") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + return require_non_empty(&config.program_id, "log_listener.program_id"); +} + +fn validate_program_listener(config: &ProgramListenerConfig) -> kb_core::Result<()> { + match require_non_empty(&config.name, "program_listener.name") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match require_non_empty(&config.endpoint_role, "program_listener.endpoint_role") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + return require_non_empty(&config.program_id, "program_listener.program_id"); +} + +fn validate_account_listener(config: &AccountListenerConfig) -> kb_core::Result<()> { + match require_non_empty(&config.name, "account_listener.name") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match require_non_empty(&config.endpoint_role, "account_listener.endpoint_role") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + return require_non_empty(&config.account_pubkey, "account_listener.account_pubkey"); +} + +fn validate_wallet(config: &WalletConfig) -> kb_core::Result<()> { + match require_non_empty(&config.wallet_dir, "wallet.wallet_dir") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match require_non_empty(&config.cluster, "wallet.cluster") { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match validate_wallet_alias(&config.temporary_wallet_alias) { + std::result::Result::Ok(()) => (), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if config.temporary_wallet_persist && !config.temporary_wallet_enabled { + return std::result::Result::Err(kb_core::Error::new( + "wallet_temporary_persist_without_enablement", + "temporary wallet persistence requires temporary_wallet_enabled", + )); + } + if config.temporary_wallet_enabled && config.cluster == "mainnet-beta" { + return std::result::Result::Err(kb_core::Error::new( + "wallet_temporary_mainnet_forbidden", + "the managed temporary wallet is forbidden on mainnet-beta profiles", + )); + } + return std::result::Result::Ok(()); +} + +fn validate_wallet_alias(value: &str) -> kb_core::Result<()> { + if value.is_empty() || value.len() > 64 { + return std::result::Result::Err(kb_core::Error::new( + "wallet_alias_length_invalid", + "temporary wallet alias length must be between 1 and 64 bytes", + )); + } + if !value.bytes().enumerate().all(|(index, byte)| { + return byte.is_ascii_alphanumeric() || (index > 0 && (byte == b'_' || byte == b'-')); + }) { + return std::result::Result::Err(kb_core::Error::new( + "wallet_alias_invalid", + "temporary wallet alias contains invalid characters", + )); + } + return std::result::Result::Ok(()); +} + +fn validate_execution(config: &ExecutionConfig) -> kb_core::Result<()> { + let configured_spend = config + .localnet_max_spend_lamports + .saturating_add(config.devnet_max_spend_lamports) + .saturating_add(config.testnet_max_spend_lamports) + .saturating_add(config.mainnet_max_spend_lamports); + if configured_spend > 0 && !config.require_simulation { + return std::result::Result::Err(kb_core::Error::new( + "execution_spend_without_simulation", + "any configured spend requires simulation", + )); + } + if config.mainnet_max_spend_lamports > 0 && !config.require_operator_confirmation { + return std::result::Result::Err(kb_core::Error::new( + "execution_mainnet_without_confirmation", + "mainnet spend requires operator confirmation", + )); + } + if config.max_fee_lamports == 0 { + return std::result::Result::Err(kb_core::Error::new( + "execution_fee_limit_zero", + "max_fee_lamports must be greater than zero", + )); + } + if config.recent_blockhash_max_age_slots == 0 { + return std::result::Result::Err(kb_core::Error::new( + "execution_blockhash_age_zero", + "recent_blockhash_max_age_slots must be greater than zero", + )); + } + if config.confirmation_poll_interval_ms == 0 || config.confirmation_poll_interval_ms > 60_000 { + return std::result::Result::Err(kb_core::Error::new( + "execution_confirmation_poll_interval_invalid", + "confirmation_poll_interval_ms must be between 1 and 60000", + )); + } + if config.confirmation_max_attempts == 0 || config.confirmation_max_attempts > 10_000 { + return std::result::Result::Err(kb_core::Error::new( + "execution_confirmation_attempts_invalid", + "confirmation_max_attempts must be between 1 and 10000", + )); + } + return std::result::Result::Ok(()); +} + +fn validate_wallet_execution_pair( + wallet: &WalletConfig, + execution: &ExecutionConfig, +) -> kb_core::Result<()> { + for (enabled, limit, cluster) in [ + (wallet.localnet_send_enabled, execution.localnet_max_spend_lamports, "localnet"), + (wallet.devnet_send_enabled, execution.devnet_max_spend_lamports, "devnet"), + (wallet.testnet_send_enabled, execution.testnet_max_spend_lamports, "testnet"), + ( + wallet.mainnet_send_enabled, + execution.mainnet_max_spend_lamports, + "mainnet-beta", + ), + ] { + if enabled && limit == 0 { + return std::result::Result::Err(kb_core::Error::new( + "execution_enabled_cluster_without_spend_limit", + format!("{cluster} sending requires a positive spend limit"), + )); + } + } + if wallet.mainnet_send_enabled && !execution.require_operator_confirmation { + return std::result::Result::Err(kb_core::Error::new( + "execution_mainnet_send_without_confirmation", + "mainnet sending requires operator confirmation", + )); + } + if wallet.cluster != "devnet" && execution.devnet_airdrop_max_lamports > 0 { + return std::result::Result::Err(kb_core::Error::new( + "execution_devnet_airdrop_on_non_devnet_profile", + "devnet_airdrop_max_lamports must be zero outside devnet profiles", + )); + } + return std::result::Result::Ok(()); +} + +fn validate_log_level(value: &str, field_name: &str) -> kb_core::Result<()> { + if value == "trace" + || value == "debug" + || value == "info" + || value == "warn" + || value == "error" + || value == "off" + { + return std::result::Result::Ok(()); + } + return std::result::Result::Err(kb_core::Error::new( + "log_level_invalid", + format!("{field_name}: {value}"), + )); +} + +fn require_non_empty(value: &str, field_name: &str) -> kb_core::Result<()> { + if value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "config_field_empty", + field_name.to_string(), + )); + } + return std::result::Result::Ok(()); +} + +#[cfg(test)] +mod tests { + use ts_rs::TS; // rust-rules: derive-import + + #[test] + fn exported_json_configuration_numbers_do_not_use_bigint() { + let config = ts_rs::Config::default(); + let declarations = [ + ::decl(&config), + ::decl(&config), + ::decl(&config), + ::decl(&config), + ::decl(&config), + ::decl(&config), + ]; + for declaration in declarations { + assert!(!declaration.contains("bigint")); + } + } + + const EXAMPLE_CONFIG: &str = include_str!("../../config/example.config.json"); + + fn parse_example_value() -> serde_json::Value { + let result = serde_json::from_str::(EXAMPLE_CONFIG); + match result { + std::result::Result::Ok(value) => return value, + std::result::Result::Err(error) => panic!("example config must be valid JSON: {error}"), + } + } + + fn value_to_json(value: &serde_json::Value) -> std::string::String { + let result = serde_json::to_string(value); + match result { + std::result::Result::Ok(raw_json) => return raw_json, + std::result::Result::Err(error) => panic!("test value must serialize: {error}"), + } + } + + fn tracing_crate_names() -> std::vec::Vec { + let manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let workspace_root = match manifest_dir.parent() { + std::option::Option::Some(path) => path, + std::option::Option::None => panic!("workspace root must exist"), + }; + let read_result = std::fs::read_dir(workspace_root); + let entries = match read_result { + std::result::Result::Ok(entries) => entries, + std::result::Result::Err(error) => panic!("workspace must be readable: {error}"), + }; + let mut names = std::vec::Vec::::new(); + for entry_result in entries { + let entry = match entry_result { + std::result::Result::Ok(entry) => entry, + std::result::Result::Err(error) => { + panic!("workspace entry must be readable: {error}") + }, + }; + let cargo_path = entry.path().join("Cargo.toml"); + if !cargo_path.is_file() { + continue; + } + let cargo_result = std::fs::read_to_string(&cargo_path); + let cargo_toml = match cargo_result { + std::result::Result::Ok(content) => content, + std::result::Result::Err(error) => panic!("Cargo.toml must be readable: {error}"), + }; + if !cargo_toml.lines().any(|line| return line.trim() == "tracing.workspace = true") { + continue; + } + let mut in_package = false; + for line in cargo_toml.lines() { + let trimmed = line.trim(); + if trimmed == "[package]" { + in_package = true; + continue; + } + if in_package && trimmed.starts_with('[') { + break; + } + if in_package && trimmed.starts_with("name = ") { + names.push(trimmed.trim_start_matches("name = ").trim_matches('"').to_string()); + break; + } + } + } + names.sort(); + return names; + } + + #[test] + fn schema_text_is_valid_json() { + let result = super::config_json_schema_value(); + assert!(result.is_ok()); + } + + #[test] + fn example_config_validates_against_schema() { + let result = super::validate_config_json_schema(EXAMPLE_CONFIG); + assert!(result.is_ok()); + } + + #[test] + fn example_config_routes_global_and_operational_crate_files() { + let config_result = super::parse_config_json(EXAMPLE_CONFIG); + let config = match config_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("example config must parse: {error}"), + }; + let operational_crates = tracing_crate_names(); + assert!(!operational_crates.is_empty()); + for profile in config.profiles { + let directory = match profile.name.as_str() { + "local_devnet" => "devnet", + "mainnet_research" => "mainnet_research", + "mainnet" => "mainnet", + value => panic!("unexpected example profile: {value}"), + }; + for (suffix, level, format) in [ + ("debug.log", "debug", "human"), + ("info.log", "info", "human"), + ("error.jsonl", "error", "json"), + ] { + let expected_path = format!("logs/{directory}/{suffix}"); + assert!(profile.logging.targets.iter().any(|target| { + return target.enabled + && target.path == expected_path + && target.level == level + && target.format == format + && target.targets == std::vec!["*".to_string()]; + })); + } + assert!(profile.logging.targets.iter().any(|target| { + return target.enabled + && target.path == format!("logs/{directory}/app.log") + && target.level == "debug" + && target.format == "human" + && target.targets == std::vec!["kb_app_demo".to_string()]; + })); + for crate_name in &operational_crates { + for (suffix, level, format) in [ + ("debug.log", "debug", "human"), + ("info.log", "info", "human"), + ("error.jsonl", "error", "json"), + ] { + let expected_path = format!("logs/{directory}/{crate_name}/{suffix}"); + let route_exists = profile.logging.targets.iter().any(|target| { + return target.enabled + && target.path == expected_path + && target.level == level + && target.format == format + && target.targets == std::vec![crate_name.clone()]; + }); + assert!( + route_exists, + "profile {} is missing the canonical {} route for {} at {}", + profile.name, level, crate_name, expected_path + ); + } + } + } + } + + #[test] + fn example_config_parses_and_resolves_active_profile() { + let config_result = super::parse_config_json(EXAMPLE_CONFIG); + assert!(config_result.is_ok()); + let config = match config_result { + std::result::Result::Ok(config) => config, + std::result::Result::Err(error) => panic!("example config must parse: {error}"), + }; + let active_result = super::active_profile(&config); + assert!(active_result.is_ok()); + //let active = + match active_result { + std::result::Result::Ok(active) => active, + std::result::Result::Err(error) => panic!("active profile must resolve: {error}"), + }; + //assert_eq!(active.name, "local_devnet"); + } + + #[test] + fn example_config_serializes_and_roundtrips() { + let config_result = super::parse_config_json(EXAMPLE_CONFIG); + assert!(config_result.is_ok()); + let config = match config_result { + std::result::Result::Ok(config) => config, + std::result::Result::Err(error) => panic!("example config must parse: {error}"), + }; + let serialized_result = super::serialize_config_json_pretty(&config); + assert!(serialized_result.is_ok()); + let serialized = match serialized_result { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => panic!("example config must serialize: {error}"), + }; + let reparsed_result = super::parse_config_json(&serialized); + assert!(reparsed_result.is_ok()); + let reparsed = match reparsed_result { + std::result::Result::Ok(reparsed) => reparsed, + std::result::Result::Err(error) => panic!("serialized config must parse: {error}"), + }; + assert_eq!(config, reparsed); + } + + #[test] + fn parser_rejects_missing_active_profile() { + let mut value = parse_example_value(); + value["active_profile"] = serde_json::Value::String("missing_profile".to_string()); + let raw_json = value_to_json(&value); + let result = super::parse_config_json(&raw_json); + assert!(result.is_err()); + } + + #[test] + fn schema_rejects_profile_enabled_field() { + let mut value = parse_example_value(); + value["profiles"][0]["enabled"] = serde_json::Value::Bool(true); + let raw_json = value_to_json(&value); + let result = super::validate_config_json_schema(&raw_json); + assert!(result.is_err()); + } + + #[test] + fn parser_rejects_duplicate_profile_names() { + let mut value = parse_example_value(); + value["profiles"][1]["name"] = serde_json::Value::String("local_devnet".to_string()); + let raw_json = value_to_json(&value); + let result = super::parse_config_json(&raw_json); + assert!(result.is_err()); + } + + #[test] + fn schema_rejects_unknown_endpoint_secret_field() { + let mut value = parse_example_value(); + value["profiles"][0]["solana"]["http_endpoints"][0]["secret_env_field"] = + serde_json::Value::String("HELIUS_API_KEY".to_string()); + let raw_json = value_to_json(&value); + let result = super::validate_config_json_schema(&raw_json); + assert!(result.is_err()); + } + + #[test] + fn schema_rejects_runtime_idls_directory_field() { + let mut value = parse_example_value(); + value["profiles"][0]["data"]["idls_directory"] = + serde_json::Value::String("idls".to_string()); + let raw_json = value_to_json(&value); + let result = super::validate_config_json_schema(&raw_json); + assert!(result.is_err()); + } + + #[test] + fn schema_rejects_unsupported_transport_surface() { + let mut value = parse_example_value(); + value["profiles"][0]["solana"]["advanced_streams"] = serde_json::json!([]); + let raw_json = value_to_json(&value); + let result = super::validate_config_json_schema(&raw_json); + assert!(result.is_err()); + } + + #[test] + fn parser_rejects_invalid_http_endpoint_url() { + let mut value = parse_example_value(); + value["profiles"][0]["solana"]["http_endpoints"][0]["url"] = + serde_json::Value::String("wss://api.devnet.solana.com".to_string()); + let raw_json = value_to_json(&value); + let result = super::parse_config_json(&raw_json); + assert!(result.is_err()); + } + + #[test] + fn parser_rejects_invalid_ws_endpoint_url() { + let mut value = parse_example_value(); + value["profiles"][0]["solana"]["ws_endpoints"][0]["url"] = + serde_json::Value::String("https://api.devnet.solana.com".to_string()); + let raw_json = value_to_json(&value); + let result = super::parse_config_json(&raw_json); + assert!(result.is_err()); + } + + #[test] + fn parser_rejects_file_logging_target_without_path() { + let mut value = parse_example_value(); + value["profiles"][0]["logging"]["targets"][1]["path"] = + serde_json::Value::String("".to_string()); + let raw_json = value_to_json(&value); + let result = super::parse_config_json(&raw_json); + assert!(result.is_err()); + } + + #[test] + fn parser_rejects_invalid_temporary_wallet_alias() { + let mut value = parse_example_value(); + value["profiles"][0]["wallet"]["temporary_wallet_alias"] = + serde_json::Value::String("../wallet".to_string()); + let raw_json = value_to_json(&value); + let result = super::parse_config_json(&raw_json); + assert!(result.is_err()); + } + + #[test] + fn parser_rejects_persistent_disabled_temporary_wallet() { + let mut value = parse_example_value(); + value["profiles"][0]["wallet"]["temporary_wallet_enabled"] = serde_json::Value::Bool(false); + let raw_json = value_to_json(&value); + let result = super::parse_config_json(&raw_json); + assert!(result.is_err()); + } + + #[test] + fn parser_rejects_enabled_cluster_without_spend_limit() { + let mut value = parse_example_value(); + value["profiles"][0]["execution"]["devnet_max_spend_lamports"] = + serde_json::Value::Number(serde_json::Number::from(0)); + let raw_json = value_to_json(&value); + let result = super::parse_config_json(&raw_json); + assert!(result.is_err()); + } + + #[test] + fn parser_rejects_zero_fee_or_blockhash_limits() { + for field in [ + "max_fee_lamports", + "recent_blockhash_max_age_slots", + "confirmation_poll_interval_ms", + "confirmation_max_attempts", + ] { + let mut value = parse_example_value(); + value["profiles"][0]["execution"][field] = + serde_json::Value::Number(serde_json::Number::from(0)); + let raw_json = value_to_json(&value); + let result = super::parse_config_json(&raw_json); + assert!(result.is_err()); + } + } + + #[test] + fn parser_rejects_http_role_with_subscriptions() { + let mut value = parse_example_value(); + value["profiles"][0]["solana"]["http_endpoints"][0]["roles"][0]["max_subscriptions"] = + serde_json::Value::Number(serde_json::Number::from(1)); + let raw_json = value_to_json(&value); + let result = super::parse_config_json(&raw_json); + assert!(result.is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_core/Cargo.toml b/migration/khadhroony-bot2-reference/kb_core/Cargo.toml new file mode 100644 index 0000000..c27765e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_core/Cargo.toml @@ -0,0 +1,14 @@ +# file: kb_core/Cargo.toml +# version: 2 + +[package] +name = "kb_core" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_core/README.md b/migration/khadhroony-bot2-reference/kb_core/README.md new file mode 100644 index 0000000..9c9b8dc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_core/README.md @@ -0,0 +1,23 @@ + + + +# kb_core + +Ce crate fournit les types minimaux partagés, les erreurs communes et l'identité des modules. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. + +## Erreurs communes + +`kb_core::Error` est l'enum d'erreur commune du workspace. Elle fournit des familles stables pour configuration, I/O, JSON, tracing, Tauri, HTTP, WebSocket, base de données, état invalide, client non connecté, fonctionnalité non implémentée et erreurs personnalisées. + +`kb_core::Error::new(code, message)` reste disponible comme compatibilité courte pendant que les domaines sont progressivement spécialisés. diff --git a/migration/khadhroony-bot2-reference/kb_core/src/error.rs b/migration/khadhroony-bot2-reference/kb_core/src/error.rs new file mode 100644 index 0000000..f4f67da --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_core/src/error.rs @@ -0,0 +1,197 @@ +// file: kb_core/src/error.rs +// version: 6 + +//! Error and result primitives shared by the workspace. + +/// Workspace-wide result alias. +pub type Result = std::result::Result; + +/// Workspace-wide explicit error type. +/// +/// The project avoids generic catch-all error crates, so this enum centralizes +/// the first stable error families used by core, configuration, logging, +/// applications, RPC, stores and execution modules. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Error { + /// Configuration or profile validation error. + Config(std::string::String), + /// Filesystem or standard I/O error. + Io(std::string::String), + /// JSON serialization, deserialization or schema validation error. + Json(std::string::String), + /// Tracing initialization or logging runtime error. + Tracing(std::string::String), + /// Tauri application or WebView runtime error. + Tauri(std::string::String), + /// HTTP transport error. + Http(std::string::String), + /// WebSocket transport error. + Ws(std::string::String), + /// Database or storage backend error. + Db(std::string::String), + /// Invalid internal state error. + InvalidState(std::string::String), + /// Operation requested while a client or subsystem is not connected. + NotConnected(std::string::String), + /// Feature intentionally scheduled for a later version. + NotImplemented(std::string::String), + /// Stable custom error code used while domains are still being split. + Custom { + /// Stable custom error code. + code: std::string::String, + /// Human-readable custom error message. + message: std::string::String, + }, +} + +impl crate::Error { + /// Creates a custom error value with a stable code. + pub fn new( + code: impl std::convert::Into, + message: impl std::convert::Into, + ) -> Self { + return Self::Custom { + code: code.into(), + message: message.into(), + }; + } + + /// Creates a configuration error value. + pub fn config(message: impl std::convert::Into) -> Self { + return Self::Config(message.into()); + } + + /// Creates an I/O error value. + pub fn io(message: impl std::convert::Into) -> Self { + return Self::Io(message.into()); + } + + /// Creates a JSON error value. + pub fn json(message: impl std::convert::Into) -> Self { + return Self::Json(message.into()); + } + + /// Creates a tracing error value. + pub fn tracing(message: impl std::convert::Into) -> Self { + return Self::Tracing(message.into()); + } + + /// Creates a Tauri runtime error value. + pub fn tauri(message: impl std::convert::Into) -> Self { + return Self::Tauri(message.into()); + } + + /// Creates an HTTP transport error value. + pub fn http(message: impl std::convert::Into) -> Self { + return Self::Http(message.into()); + } + + /// Creates a WebSocket transport error value. + pub fn ws(message: impl std::convert::Into) -> Self { + return Self::Ws(message.into()); + } + + /// Creates a database error value. + pub fn db(message: impl std::convert::Into) -> Self { + return Self::Db(message.into()); + } + + /// Creates an invalid state error value. + pub fn invalid_state(message: impl std::convert::Into) -> Self { + return Self::InvalidState(message.into()); + } + + /// Creates a not-connected error value. + pub fn not_connected(message: impl std::convert::Into) -> Self { + return Self::NotConnected(message.into()); + } + + /// Creates a not-implemented error value. + pub fn not_implemented(message: impl std::convert::Into) -> Self { + return Self::NotImplemented(message.into()); + } + + /// Returns a stable code for logs and UI diagnostics. + pub fn code(&self) -> &str { + return match self { + Self::Config(_) => "config", + Self::Io(_) => "io", + Self::Json(_) => "json", + Self::Tracing(_) => "tracing", + Self::Tauri(_) => "tauri", + Self::Http(_) => "http", + Self::Ws(_) => "ws", + Self::Db(_) => "db", + Self::InvalidState(_) => "invalid_state", + Self::NotConnected(_) => "not_connected", + Self::NotImplemented(_) => "not_implemented", + Self::Custom { code, message: _ } => code.as_str(), + }; + } + + /// Returns the human-readable message without the family prefix. + pub fn message(&self) -> &str { + return match self { + Self::Config(message) => message, + Self::Io(message) => message, + Self::Json(message) => message, + Self::Tracing(message) => message, + Self::Tauri(message) => message, + Self::Http(message) => message, + Self::Ws(message) => message, + Self::Db(message) => message, + Self::InvalidState(message) => message, + Self::NotConnected(message) => message, + Self::NotImplemented(message) => message, + Self::Custom { code: _, message } => message, + }; + } +} + +impl std::fmt::Display for crate::Error { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + return match self { + Self::Config(message) => write!(formatter, "configuration error: {message}"), + Self::Io(message) => write!(formatter, "io error: {message}"), + Self::Json(message) => write!(formatter, "json error: {message}"), + Self::Tracing(message) => write!(formatter, "tracing error: {message}"), + Self::Tauri(message) => write!(formatter, "tauri error: {message}"), + Self::Http(message) => write!(formatter, "http error: {message}"), + Self::Ws(message) => write!(formatter, "websocket error: {message}"), + Self::Db(message) => write!(formatter, "database error: {message}"), + Self::InvalidState(message) => write!(formatter, "invalid state: {message}"), + Self::NotConnected(message) => write!(formatter, "not connected: {message}"), + Self::NotImplemented(message) => { + write!(formatter, "not implemented: {message}") + }, + Self::Custom { code, message } => write!(formatter, "{code}: {message}"), + }; + } +} + +impl std::error::Error for crate::Error {} + +impl std::convert::From for crate::Error { + fn from(error: std::io::Error) -> Self { + return Self::Io(error.to_string()); + } +} + +#[cfg(test)] +mod tests { + #[test] + fn custom_error_preserves_code_and_message() { + let error = super::Error::new("sample_code", "sample message"); + assert_eq!(error.code(), "sample_code"); + assert_eq!(error.message(), "sample message"); + assert_eq!(error.to_string(), "sample_code: sample message"); + } + + #[test] + fn family_error_formats_with_family_prefix() { + let error = super::Error::config("missing profile"); + assert_eq!(error.code(), "config"); + assert_eq!(error.message(), "missing profile"); + assert_eq!(error.to_string(), "configuration error: missing profile"); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_core/src/lib.rs b/migration/khadhroony-bot2-reference/kb_core/src/lib.rs new file mode 100644 index 0000000..046b279 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_core/src/lib.rs @@ -0,0 +1,21 @@ +// file: kb_core/src/lib.rs +// version: 3 + +//! Core primitives, module identity and shared errors for the workspace. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod error; +mod module; + +/// Workspace-wide explicit error type. +pub use crate::error::Error; +/// Workspace-wide result alias. +pub use crate::error::Result; +/// Processing module category. +pub use crate::module::ModuleKind; +/// Stable module name. +pub use crate::module::ModuleName; +/// Stable semantic module version. +pub use crate::module::ModuleVersion; diff --git a/migration/khadhroony-bot2-reference/kb_core/src/module.rs b/migration/khadhroony-bot2-reference/kb_core/src/module.rs new file mode 100644 index 0000000..e466b6c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_core/src/module.rs @@ -0,0 +1,31 @@ +// file: kb_core/src/module.rs +// version: 1 + +//! Module identity and module-kind primitives. + +/// Stable module name. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ModuleName(pub std::string::String); + +/// Stable semantic module version. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ModuleVersion(pub std::string::String); + +/// Processing module category. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ModuleKind { + /// Raw transaction ingestion. + Ingestor, + /// Generic Solana extractor. + Extractor, + /// Program observation builder. + Observer, + /// Protocol decoder. + Decoder, + /// Business materializer. + Materializer, + /// Aggregation stage. + Aggregator, + /// Validation stage. + Validator, +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/Cargo.toml new file mode 100644 index 0000000..05e49f0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_adapter_saber_decimal_wrapper/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_adapter_saber_decimal_wrapper" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/README.md b/migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/README.md new file mode 100644 index 0000000..188061d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_adapter_saber_decimal_wrapper + +Ce crate réserve la surface de décodage `adapter_saber_decimal_wrapper`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `adapter_saber_decimal_wrapper` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/src/constants.rs new file mode 100644 index 0000000..1b6770f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_adapter_saber_decimal_wrapper/src/constants.rs +// version: 3 + +//! Local constants for the `kb_decoder_adapter_saber_decimal_wrapper` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_adapter_saber_decimal_wrapper"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/src/decoder.rs new file mode 100644 index 0000000..e9fdc3d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_adapter_saber_decimal_wrapper/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_adapter_saber_decimal_wrapper` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AdapterSaberDecimalWrapperDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AdapterSaberDecimalWrapperDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_adapter_saber_decimal_wrapper"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ADAPTER_SABER_DECIMAL_WRAPPER_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/src/lib.rs new file mode 100644 index 0000000..69f5aba --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_adapter_saber_decimal_wrapper/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_adapter_saber_decimal_wrapper/src/lib.rs +// version: 2 + +//! Decoder crate for `adapter_saber_decimal_wrapper`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AdapterSaberDecimalWrapperDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/Cargo.toml new file mode 100644 index 0000000..1750c1a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_admin_jupiter_lock/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_admin_jupiter_lock" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/README.md b/migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/README.md new file mode 100644 index 0000000..f8226fa --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_admin_jupiter_lock + +Ce crate réserve la surface de décodage `admin_jupiter_lock`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `admin_jupiter_lock` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/src/constants.rs new file mode 100644 index 0000000..7802e9e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_admin_jupiter_lock/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_admin_jupiter_lock` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_admin_jupiter_lock"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/src/decoder.rs new file mode 100644 index 0000000..ab570ab --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_admin_jupiter_lock/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_admin_jupiter_lock` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AdminJupiterLockDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AdminJupiterLockDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_admin_jupiter_lock"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ADMIN_JUPITER_LOCK_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/src/lib.rs new file mode 100644 index 0000000..aba4fa4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_admin_jupiter_lock/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_admin_jupiter_lock/src/lib.rs +// version: 2 + +//! Decoder crate for `admin_jupiter_lock`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AdminJupiterLockDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/Cargo.toml new file mode 100644 index 0000000..f9e871b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_admin_pump_fees/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_admin_pump_fees" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/README.md b/migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/README.md new file mode 100644 index 0000000..d8e1dad --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/README.md @@ -0,0 +1,17 @@ + + + +# kb_decoder_admin_pump_fees + +Ce crate décode la surface protocolaire `pump_fees` en événements protocolaires normalisés. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/src/constants.rs new file mode 100644 index 0000000..f824bad --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_admin_pump_fees/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_admin_pump_fees` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_admin_pump_fees"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/src/decoder.rs new file mode 100644 index 0000000..ceeb480 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/src/decoder.rs @@ -0,0 +1,42 @@ +// file: kb_decoder_admin_pump_fees/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_admin_pump_fees` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AdminPumpFeesDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AdminPumpFeesDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_admin_pump_fees"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ADMIN_PUMP_FEES_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/src/lib.rs new file mode 100644 index 0000000..b299f43 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_admin_pump_fees/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_admin_pump_fees/src/lib.rs +// version: 2 + +//! Decoder crate for `admin_pump_fees`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Initial decoder implementation placeholder. +pub use crate::decoder::AdminPumpFeesDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/Cargo.toml new file mode 100644 index 0000000..4c036bd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_aldrin_v1/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_aldrin_v1" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/README.md new file mode 100644 index 0000000..c99427f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_aldrin_v1 + +Ce crate réserve la surface de décodage `amm_aldrin_v1`. Aucun `program_id` local n'est encore fixé dans ce squelette. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_aldrin_v1` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/src/constants.rs new file mode 100644 index 0000000..6574f6c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_aldrin_v1/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_aldrin_v1` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_aldrin_v1"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/src/decoder.rs new file mode 100644 index 0000000..be5ca8b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_aldrin_v1/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_aldrin_v1` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AldrinDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AldrinDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_aldrin_v1"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_ALDRIN_V1_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/src/lib.rs new file mode 100644 index 0000000..667be0e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v1/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_aldrin_v1/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_aldrin_v1`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AldrinDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/Cargo.toml new file mode 100644 index 0000000..4f0a1f8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_aldrin_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_aldrin_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/README.md new file mode 100644 index 0000000..bd9b178 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_aldrin_v2 + +Ce crate réserve la surface de décodage `amm_aldrin_v2`. Aucun `program_id` local n'est encore fixé dans ce squelette. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_aldrin_v2` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/src/constants.rs new file mode 100644 index 0000000..02c6b4c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_aldrin_v2/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_aldrin_v2` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_aldrin_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/src/decoder.rs new file mode 100644 index 0000000..6bb1257 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_aldrin_v2/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_aldrin_v2` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AldrinV2Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AldrinV2Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_aldrin_v2"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_ALDRIN_V2_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/src/lib.rs new file mode 100644 index 0000000..4c3021c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_aldrin_v2/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_aldrin_v2/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_aldrin_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AldrinV2Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/Cargo.toml new file mode 100644 index 0000000..3aba9a4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_alphaq/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_alphaq" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/README.md new file mode 100644 index 0000000..94235ea --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_alphaq + +Ce crate réserve la surface de décodage `amm_alphaq`. Aucun `program_id` local n'est encore fixé dans ce squelette. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_alphaq` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/src/constants.rs new file mode 100644 index 0000000..d9f85fc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_alphaq/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_alphaq` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_alphaq"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/src/decoder.rs new file mode 100644 index 0000000..4f6f147 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_alphaq/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_alphaq` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmAlphaqDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmAlphaqDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_alphaq"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_ALPHAQ_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/src/lib.rs new file mode 100644 index 0000000..9c96c1c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_alphaq/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_alphaq/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_alphaq`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmAlphaqDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_believe/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_believe/Cargo.toml new file mode 100644 index 0000000..2fc6fb6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_believe/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_believe/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_believe" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_believe/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_believe/README.md new file mode 100644 index 0000000..e8754d5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_believe/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_believe + +Ce crate réserve la surface de décodage `amm_believe`. Aucun `program_id` local n'est encore fixé dans ce squelette. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_believe` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_believe/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_believe/src/constants.rs new file mode 100644 index 0000000..1a41cb1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_believe/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_believe/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_believe` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_believe"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_believe/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_believe/src/decoder.rs new file mode 100644 index 0000000..cc57889 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_believe/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_believe/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_believe` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmBelieveDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmBelieveDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_believe"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_BELIEVE_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_believe/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_believe/src/lib.rs new file mode 100644 index 0000000..e27d368 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_believe/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_believe/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_believe`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmBelieveDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/Cargo.toml new file mode 100644 index 0000000..74784a0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_bonk_swap/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_bonk_swap" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/README.md new file mode 100644 index 0000000..08fd98b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_bonk_swap + +Ce crate réserve la surface de décodage `amm_bonk_swap`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_bonk_swap` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/src/constants.rs new file mode 100644 index 0000000..2b4b3cf --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_bonk_swap/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_bonk_swap` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_bonk_swap"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/src/decoder.rs new file mode 100644 index 0000000..0478876 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_bonk_swap/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_bonk_swap` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmBonkSwapDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmBonkSwapDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_bonk_swap"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_BONK_SWAP_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/src/lib.rs new file mode 100644 index 0000000..6257005 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_bonk_swap/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_bonk_swap/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_bonk_swap`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmBonkSwapDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/Cargo.toml new file mode 100644 index 0000000..e44f363 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_fluxbeam/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_fluxbeam" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/README.md new file mode 100644 index 0000000..5f7d303 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_fluxbeam + +Ce crate réserve la surface de décodage `amm_fluxbeam`. Aucun `program_id` local n'est encore fixé dans ce squelette. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_fluxbeam` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/src/constants.rs new file mode 100644 index 0000000..2c122b6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_fluxbeam/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_fluxbeam` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_fluxbeam"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/src/decoder.rs new file mode 100644 index 0000000..06c4490 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_fluxbeam/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_fluxbeam` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmFluxbeamDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmFluxbeamDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_fluxbeam"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_FLUXBEAM_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/src/lib.rs new file mode 100644 index 0000000..80a8919 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_fluxbeam/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_fluxbeam/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_fluxbeam`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmFluxbeamDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/Cargo.toml new file mode 100644 index 0000000..635e671 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_goon_fi/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_goon_fi" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/README.md new file mode 100644 index 0000000..9e9bf11 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_goon_fi + +Ce crate réserve la surface de décodage `amm_goon_fi`. Aucun `program_id` local n'est encore fixé dans ce squelette. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_goon_fi` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/src/constants.rs new file mode 100644 index 0000000..5cc0c0d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_goon_fi/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_goon_fi` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_goon_fi"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/src/decoder.rs new file mode 100644 index 0000000..745ab7d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_goon_fi/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_goon_fi` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmGoonFiDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmGoonFiDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_goon_fi"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_GOON_FI_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/src/lib.rs new file mode 100644 index 0000000..9d51aca --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_goon_fi/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_goon_fi/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_goon_fi`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmGoonFiDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/Cargo.toml new file mode 100644 index 0000000..8829974 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_goosefx_gamma/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_goosefx_gamma" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/README.md new file mode 100644 index 0000000..2562270 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_goosefx_gamma + +Ce crate réserve la surface de décodage `amm_goosefx_gamma`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_goosefx_gamma` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/src/constants.rs new file mode 100644 index 0000000..9c2ad90 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_goosefx_gamma/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_goosefx_gamma` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_goosefx_gamma"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/src/decoder.rs new file mode 100644 index 0000000..f44383d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_goosefx_gamma/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_goosefx_gamma` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmGoosefxGammaDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmGoosefxGammaDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_goosefx_gamma"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_GOOSEFX_GAMMA_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/src/lib.rs new file mode 100644 index 0000000..7f7aeff --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_gamma/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_goosefx_gamma/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_goosefx_gamma`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmGoosefxGammaDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/Cargo.toml new file mode 100644 index 0000000..fc67373 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_goosefx_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_goosefx_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/README.md new file mode 100644 index 0000000..4145f28 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_goosefx_v2 + +Ce crate réserve la surface de décodage `amm_goosefx_v2`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_goosefx_v2` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/src/constants.rs new file mode 100644 index 0000000..443d3cd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_goosefx_v2/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_goosefx_v2` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_goosefx_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/src/decoder.rs new file mode 100644 index 0000000..1189344 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_goosefx_v2/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_goosefx_v2` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmGoosefxV2Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmGoosefxV2Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_goosefx_v2"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_GOOSEFX_V2_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/src/lib.rs new file mode 100644 index 0000000..f94ebd4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_goosefx_v2/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_goosefx_v2/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_goosefx_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmGoosefxV2Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/Cargo.toml new file mode 100644 index 0000000..94671b9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_guac_swap/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_guac_swap" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/README.md new file mode 100644 index 0000000..b158c3f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_guac_swap + +Ce crate réserve la surface de décodage `amm_guac_swap`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_guac_swap` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/src/constants.rs new file mode 100644 index 0000000..5fac7e1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_guac_swap/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_guac_swap` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_guac_swap"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/src/decoder.rs new file mode 100644 index 0000000..ea1030a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_guac_swap/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_guac_swap` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmGuacSwapDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmGuacSwapDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_guac_swap"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_GUAC_SWAP_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/src/lib.rs new file mode 100644 index 0000000..1c741df --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_guac_swap/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_guac_swap/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_guac_swap`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmGuacSwapDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/Cargo.toml new file mode 100644 index 0000000..c6bbcd5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_lifinity_swap_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_lifinity_swap_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/README.md new file mode 100644 index 0000000..b1b3018 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_lifinity_swap_v2 + +Ce crate réserve la surface de décodage `amm_lifinity_swap_v2`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_lifinity_swap_v2` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/src/constants.rs new file mode 100644 index 0000000..c0447eb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_lifinity_swap_v2/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_lifinity_swap_v2` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_lifinity_swap_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/src/decoder.rs new file mode 100644 index 0000000..e65d4fc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_lifinity_swap_v2/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_lifinity_swap_v2` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmLifinitySwapV2Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmLifinitySwapV2Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_lifinity_swap_v2"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_LIFINITY_SWAP_V2_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/src/lib.rs new file mode 100644 index 0000000..e9b49ca --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_lifinity_swap_v2/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_lifinity_swap_v2/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_lifinity_swap_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmLifinitySwapV2Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/Cargo.toml new file mode 100644 index 0000000..4043f7b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_metadao_futarchy_amm/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_metadao_futarchy_amm" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/README.md new file mode 100644 index 0000000..61a0323 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_metadao_futarchy_amm + +Ce crate réserve la surface de décodage `amm_metadao_futarchy_amm`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_metadao_futarchy_amm` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/src/constants.rs new file mode 100644 index 0000000..2398b1e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_metadao_futarchy_amm/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_metadao_futarchy_amm` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_metadao_futarchy_amm"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/src/decoder.rs new file mode 100644 index 0000000..f641774 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_metadao_futarchy_amm/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_metadao_futarchy_amm` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmMetadaoFutarchyAmmDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmMetadaoFutarchyAmmDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_metadao_futarchy_amm"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_METADAO_FUTARCHY_AMM_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/src/lib.rs new file mode 100644 index 0000000..13ecd11 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_futarchy_amm/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_metadao_futarchy_amm/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_metadao_futarchy_amm`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmMetadaoFutarchyAmmDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/Cargo.toml new file mode 100644 index 0000000..5870374 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_metadao_v0_5/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_metadao_v0_5" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/README.md new file mode 100644 index 0000000..de729e7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_metadao_v0_5 + +Ce crate réserve la surface de décodage `amm_metadao_v0_5`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_metadao_v0_5` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/src/constants.rs new file mode 100644 index 0000000..5faed4e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_metadao_v0_5/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_metadao_v0_5` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_metadao_v0_5"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/src/decoder.rs new file mode 100644 index 0000000..cdbfca5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_metadao_v0_5/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_metadao_v0_5` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmMetadaoV05Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmMetadaoV05Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_metadao_v0_5"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_METADAO_V0_5_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/src/lib.rs new file mode 100644 index 0000000..35722ab --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_metadao_v0_5/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_metadao_v0_5/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_metadao_v0_5`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmMetadaoV05Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/Cargo.toml new file mode 100644 index 0000000..0cc02af --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_meteora_damm_v1/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_meteora_damm_v1" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/README.md new file mode 100644 index 0000000..c07d854 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/README.md @@ -0,0 +1,17 @@ + + + +# kb_decoder_amm_meteora_damm_v1 + +Ce crate décode la surface protocolaire `meteora_damm_v1` en événements protocolaires normalisés. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/src/constants.rs new file mode 100644 index 0000000..ebc48d4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_meteora_damm_v1/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_meteora_damm_v1` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_meteora_damm_v1"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/src/decoder.rs new file mode 100644 index 0000000..ee59399 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/src/decoder.rs @@ -0,0 +1,42 @@ +// file: kb_decoder_amm_meteora_damm_v1/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_meteora_damm_v1` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmMeteoraDammV1Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmMeteoraDammV1Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_meteora_damm_v1"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_METEORA_DAMM_V1_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/src/lib.rs new file mode 100644 index 0000000..bee2acf --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v1/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_meteora_damm_v1/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_meteora_damm_v1`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Initial decoder implementation placeholder. +pub use crate::decoder::AmmMeteoraDammV1Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/Cargo.toml new file mode 100644 index 0000000..efbae88 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_meteora_damm_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_meteora_damm_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/README.md new file mode 100644 index 0000000..eabb345 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/README.md @@ -0,0 +1,17 @@ + + + +# kb_decoder_amm_meteora_damm_v2 + +Ce crate décode la surface protocolaire `meteora_damm_v2` en événements protocolaires normalisés. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/src/constants.rs new file mode 100644 index 0000000..9b9fc43 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_meteora_damm_v2/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_meteora_damm_v2` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_meteora_damm_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/src/decoder.rs new file mode 100644 index 0000000..fd1ae57 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/src/decoder.rs @@ -0,0 +1,42 @@ +// file: kb_decoder_amm_meteora_damm_v2/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_meteora_damm_v2` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmMeteoraDammV2Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmMeteoraDammV2Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_meteora_damm_v2"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_METEORA_DAMM_V2_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/src/lib.rs new file mode 100644 index 0000000..26ff3f4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_meteora_damm_v2/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_meteora_damm_v2/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_meteora_damm_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Initial decoder implementation placeholder. +pub use crate::decoder::AmmMeteoraDammV2Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/Cargo.toml new file mode 100644 index 0000000..a4eba5a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_obric_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_obric_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/README.md new file mode 100644 index 0000000..4e0196a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_obric_v2 + +Ce crate réserve la surface de décodage `amm_obric_v2`. Aucun `program_id` local n'est encore fixé dans ce squelette. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_obric_v2` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/src/constants.rs new file mode 100644 index 0000000..866985b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_obric_v2/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_obric_v2` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_obric_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/src/decoder.rs new file mode 100644 index 0000000..77c8de1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_obric_v2/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_obric_v2` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmObricV2Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmObricV2Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_obric_v2"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_OBRIC_V2_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/src/lib.rs new file mode 100644 index 0000000..0f7735f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_obric_v2/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_obric_v2/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_obric_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmObricV2Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/Cargo.toml new file mode 100644 index 0000000..91bd946 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_one_dex/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_one_dex" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/README.md new file mode 100644 index 0000000..08a44d5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_one_dex + +Ce crate réserve la surface de décodage `amm_one_dex`. Aucun `program_id` local n'est encore fixé dans ce squelette. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_one_dex` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/src/constants.rs new file mode 100644 index 0000000..779ea17 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_one_dex/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_one_dex` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_one_dex"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/src/decoder.rs new file mode 100644 index 0000000..504eb50 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_one_dex/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_one_dex` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmOneDexDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmOneDexDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_one_dex"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_ONE_DEX_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/src/lib.rs new file mode 100644 index 0000000..f3b06c9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_one_dex/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_one_dex/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_one_dex`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmOneDexDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/Cargo.toml new file mode 100644 index 0000000..d8d5498 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_pump_swap/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_pump_swap" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/README.md new file mode 100644 index 0000000..f0c35a5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/README.md @@ -0,0 +1,17 @@ + + + +# kb_decoder_amm_pump_swap + +Ce crate décode la surface protocolaire `pump_swap` en événements protocolaires normalisés. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/src/constants.rs new file mode 100644 index 0000000..2881366 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_pump_swap/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_pump_swap` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_pump_swap"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/src/decoder.rs new file mode 100644 index 0000000..259cfa9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/src/decoder.rs @@ -0,0 +1,42 @@ +// file: kb_decoder_amm_pump_swap/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_pump_swap` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmPumpSwapDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmPumpSwapDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_pump_swap"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_PUMP_SWAP_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/src/lib.rs new file mode 100644 index 0000000..547db6f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_pump_swap/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_pump_swap/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_pump_swap`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Initial decoder implementation placeholder. +pub use crate::decoder::AmmPumpSwapDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/Cargo.toml new file mode 100644 index 0000000..ed1d4e6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_raydium_lp_v4/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_raydium_lp_v4" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/README.md new file mode 100644 index 0000000..a4535cf --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/README.md @@ -0,0 +1,17 @@ + + + +# kb_decoder_amm_raydium_lp_v4 + +Ce crate décode la surface protocolaire `raydium_amm_v4` en événements protocolaires normalisés. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/src/constants.rs new file mode 100644 index 0000000..60ad50d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_raydium_lp_v4/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_raydium_lp_v4` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_raydium_lp_v4"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/src/decoder.rs new file mode 100644 index 0000000..b0a9542 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/src/decoder.rs @@ -0,0 +1,42 @@ +// file: kb_decoder_amm_raydium_lp_v4/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_raydium_lp_v4` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmRaydiumLpV4Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmRaydiumLpV4Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_raydium_lp_v4"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_RAYDIUM_LP_V4_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/src/lib.rs new file mode 100644 index 0000000..e8aa132 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_raydium_lp_v4/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_raydium_lp_v4/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_raydium_lp_v4`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Initial decoder implementation placeholder. +pub use crate::decoder::AmmRaydiumLpV4Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/Cargo.toml new file mode 100644 index 0000000..7ed0bf3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_solfi/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_solfi" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/README.md new file mode 100644 index 0000000..4346f7c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_solfi + +Ce crate réserve la surface de décodage `amm_solfi`. Aucun `program_id` local n'est encore fixé dans ce squelette. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_solfi` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/src/constants.rs new file mode 100644 index 0000000..5390d33 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_solfi/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_solfi` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_solfi"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/src/decoder.rs new file mode 100644 index 0000000..a9372b4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_solfi/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_solfi` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmSolfiDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmSolfiDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_solfi"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_SOLFI_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/src/lib.rs new file mode 100644 index 0000000..1eb4f7f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_solfi/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_solfi`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmSolfiDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/Cargo.toml new file mode 100644 index 0000000..aa102a3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_solfi_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_solfi_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/README.md new file mode 100644 index 0000000..a389767 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_solfi_v2 + +Ce crate réserve la surface de décodage `amm_solfi_v2`. Aucun `program_id` local n'est encore fixé dans ce squelette. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_solfi_v2` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/src/constants.rs new file mode 100644 index 0000000..cf90e66 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_solfi_v2/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_solfi_v2` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_solfi_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/src/decoder.rs new file mode 100644 index 0000000..5d517fb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_solfi_v2/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_solfi_v2` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmSolfiV2Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmSolfiV2Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_solfi_v2"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_SOLFI_V2_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/src/lib.rs new file mode 100644 index 0000000..3c004ad --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_solfi_v2/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_solfi_v2/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_solfi_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmSolfiV2Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/Cargo.toml new file mode 100644 index 0000000..202c4ad --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_vertigo/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_vertigo" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/README.md new file mode 100644 index 0000000..f882a22 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_vertigo + +Ce crate réserve la surface de décodage `amm_vertigo`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_vertigo` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/src/constants.rs new file mode 100644 index 0000000..10e52b7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_vertigo/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_vertigo` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_vertigo"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/src/decoder.rs new file mode 100644 index 0000000..a629664 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_vertigo/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_vertigo` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmVertigoDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmVertigoDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_vertigo"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_VERTIGO_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/src/lib.rs new file mode 100644 index 0000000..97c9ca9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_vertigo/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_vertigo/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_vertigo`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmVertigoDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/Cargo.toml new file mode 100644 index 0000000..0b7e05b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_virtuals/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_virtuals" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/README.md new file mode 100644 index 0000000..e95c84a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_virtuals + +Ce crate réserve la surface de décodage `amm_virtuals`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_virtuals` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/src/constants.rs new file mode 100644 index 0000000..0488d2c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_virtuals/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_virtuals` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_virtuals"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/src/decoder.rs new file mode 100644 index 0000000..3b61f67 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_virtuals/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_virtuals` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmVirtualsDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmVirtualsDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_virtuals"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_VIRTUALS_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/src/lib.rs new file mode 100644 index 0000000..11f80ba --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_virtuals/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_virtuals/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_virtuals`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmVirtualsDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/Cargo.toml new file mode 100644 index 0000000..f2f8116 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_woofi/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_woofi" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/README.md new file mode 100644 index 0000000..40ca3d8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_woofi + +Ce crate réserve la surface de décodage `amm_woofi`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_woofi` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/src/constants.rs new file mode 100644 index 0000000..6fe4ce2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_woofi/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_woofi` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_woofi"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/src/decoder.rs new file mode 100644 index 0000000..d45e2ee --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_woofi/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_woofi` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmWoofiDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmWoofiDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_woofi"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_WOOFI_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/src/lib.rs new file mode 100644 index 0000000..68c4ed5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_woofi/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_woofi/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_woofi`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmWoofiDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/Cargo.toml new file mode 100644 index 0000000..1c75477 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_zero_fi/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_zero_fi" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/README.md new file mode 100644 index 0000000..4637e64 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_zero_fi + +Ce crate réserve la surface de décodage `amm_zero_fi`. Aucun `program_id` local n'est encore fixé dans ce squelette. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_zero_fi` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/src/constants.rs new file mode 100644 index 0000000..638918b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_zero_fi/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_zero_fi` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_zero_fi"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/src/decoder.rs new file mode 100644 index 0000000..099f36e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_zero_fi/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_zero_fi` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmZeroFiDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmZeroFiDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_zero_fi"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_ZERO_FI_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/src/lib.rs new file mode 100644 index 0000000..6896b1b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_zero_fi/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_zero_fi/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_zero_fi`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmZeroFiDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_zora/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_amm_zora/Cargo.toml new file mode 100644 index 0000000..c404bbb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_zora/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_amm_zora/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_amm_zora" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_zora/README.md b/migration/khadhroony-bot2-reference/kb_decoder_amm_zora/README.md new file mode 100644 index 0000000..cfd4689 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_zora/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_amm_zora + +Ce crate réserve la surface de décodage `amm_zora`. Aucun `program_id` local n'est encore fixé dans ce squelette. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `amm_zora` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_zora/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_zora/src/constants.rs new file mode 100644 index 0000000..2c7cf86 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_zora/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_amm_zora/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_amm_zora` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_amm_zora"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_zora/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_zora/src/decoder.rs new file mode 100644 index 0000000..9409894 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_zora/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_amm_zora/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_amm_zora` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmZoraDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AmmZoraDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_amm_zora"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_ZORA_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_amm_zora/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_amm_zora/src/lib.rs new file mode 100644 index 0000000..9e52cd0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_amm_zora/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_amm_zora/src/lib.rs +// version: 2 + +//! Decoder crate for `amm_zora`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::AmmZoraDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_anchor/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_anchor/Cargo.toml new file mode 100644 index 0000000..6fb6bd2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_anchor/Cargo.toml @@ -0,0 +1,17 @@ +# file: kb_decoder_anchor/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_anchor" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_anchor/README.md b/migration/khadhroony-bot2-reference/kb_decoder_anchor/README.md new file mode 100644 index 0000000..48191a7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_anchor/README.md @@ -0,0 +1,17 @@ + + + +# kb_decoder_anchor + +Ce crate fournit les outils génériques Anchor : IDL, discriminators, Borsh et événements. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_anchor/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_anchor/src/constants.rs new file mode 100644 index 0000000..beffee7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_anchor/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_decoder_anchor/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_anchor"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_anchor/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_anchor/src/decoder.rs new file mode 100644 index 0000000..c9f9d31 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_anchor/src/decoder.rs @@ -0,0 +1,42 @@ +// file: kb_decoder_anchor/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_anchor` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AnchorDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::AnchorDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_anchor"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_anchor/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_anchor/src/lib.rs new file mode 100644 index 0000000..35934ab --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_anchor/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_decoder_anchor/src/lib.rs +// version: 2 + +//! Decoder crate for `anchor`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Initial decoder implementation placeholder. +pub use crate::decoder::AnchorDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_api/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_api/Cargo.toml new file mode 100644 index 0000000..4bb12f5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_api/Cargo.toml @@ -0,0 +1,23 @@ +# file: kb_decoder_api/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_api" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +base64.workspace = true +kb_core = { path = "../kb_core" } +kb_model = { path = "../kb_model" } +kb_store_core = { path = "../kb_store_core" } +serde.workspace = true +serde_json.workspace = true +sha2.workspace = true + +[dev-dependencies] + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_api/README.md b/migration/khadhroony-bot2-reference/kb_decoder_api/README.md new file mode 100644 index 0000000..072f5dd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_api/README.md @@ -0,0 +1,25 @@ + + + +# kb_decoder_api + +Ce crate déclare l'interface commune des décodeurs protocolaires. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. + +## Contrats `0.4.0` et contexte core `2` + +Le trait `InstructionDecoder` reçoit exclusivement `CoreInstructionReplayInput`. Il expose une identité et une version stables, les surfaces supportées, la couverture déclarée, la reconnaissance déterministe et un résultat explicite `decoded`, `ignored`, `unsupported` ou `failed`. Les observations produites conservent la preuve, la confiance, le statut on-chain et le caractère commité ou annulé. + +Depuis `0.4.1-pre.014`, le même contrat inclut les payloads ordonnés des instructions outer. `contextual_input_hash` sérialise l’ensemble du contexte, trie récursivement les clés d’objets et conserve l’ordre des tableaux. Un changement de payload outer modifie donc le hash, tandis qu’un contexte identique produit un hash stable. + +Le crate reste indépendant de PostgreSQL, Tauri et des fournisseurs RPC. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_api/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_api/src/constants.rs new file mode 100644 index 0000000..ff48ea8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_api/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_decoder_api/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_api"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_api/src/contracts.rs b/migration/khadhroony-bot2-reference/kb_decoder_api/src/contracts.rs new file mode 100644 index 0000000..d6dedcd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_api/src/contracts.rs @@ -0,0 +1,577 @@ +// file: kb_decoder_api/src/contracts.rs +// version: 7 + +//! Backend-neutral contextual instruction decoder contracts. + +use sha2::Digest; // rust-rules: trait-import + +/// Current contextual core instruction input contract version. +pub const CORE_INSTRUCTION_INPUT_CONTRACT_VERSION: u32 = + kb_store_core::CORE_REPLAY_INPUT_CONTRACT_VERSION; + +/// Stable identity of one contextual instruction decoder. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecoderIdentity { + /// Stable lower snake case processor name. + pub name: std::string::String, + /// Semantic or deterministic implementation version. + pub version: std::string::String, +} + +impl DecoderIdentity { + /// Builds a validated decoder identity. + pub fn new( + name: impl std::convert::Into, + version: impl std::convert::Into, + ) -> kb_core::Result { + let value = Self { + name: name.into(), + version: version.into(), + }; + if value.name.trim().is_empty() || value.version.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "decoder identity fields must not be empty", + )); + } + return std::result::Result::Ok(value); + } +} + +/// One statically declared Solana surface supported by a decoder. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct DecoderSurface { + /// Exact Solana program identifier. + pub program_id: &'static str, + /// Stable surface code. + pub surface_code: &'static str, + /// Explicit dispatch priority, where larger values win. + pub priority: u16, +} + +/// Declared decoder coverage entry kind. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum DecoderCoverageEntryKind { + /// Program instruction entry. + Instruction, + /// Program event entry. + Event, + /// Raw discriminator entry. + Discriminator, +} + +/// Machine-readable decoder coverage declaration. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecoderCoverageDeclaration { + /// Solana program identifier. + pub program_id: std::string::String, + /// Optional stable surface code. + pub surface_code: std::option::Option, + /// Entry kind. + pub entry_kind: DecoderCoverageEntryKind, + /// Stable instruction, event or discriminator code. + pub entry_code: std::string::String, + /// Optional normalized lowercase hexadecimal discriminator. + pub discriminator_hex: std::option::Option, + /// Whether the entry is historical or deprecated but still supported. + pub historical: bool, +} + +impl DecoderCoverageDeclaration { + /// Builds a validated coverage declaration. + pub fn new( + program_id: impl std::convert::Into, + surface_code: std::option::Option, + entry_kind: DecoderCoverageEntryKind, + entry_code: impl std::convert::Into, + discriminator_hex: std::option::Option, + historical: bool, + ) -> kb_core::Result { + let value = Self { + program_id: program_id.into(), + surface_code, + entry_kind, + entry_code: entry_code.into(), + discriminator_hex, + historical, + }; + if value.program_id.trim().is_empty() || value.entry_code.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "decoder coverage program id and entry code must not be empty", + )); + } + if value + .surface_code + .as_deref() + .is_some_and(|surface| return surface.trim().is_empty()) + { + return std::result::Result::Err(kb_core::Error::invalid_state( + "decoder coverage surface code must not be empty when present", + )); + } + if value + .discriminator_hex + .as_deref() + .is_some_and(|discriminator| return discriminator.trim().is_empty()) + { + return std::result::Result::Err(kb_core::Error::invalid_state( + "decoder coverage discriminator must not be empty when present", + )); + } + return std::result::Result::Ok(value); + } +} + +/// Deterministic recognition result used by dispatch. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecoderRecognition { + /// Whether the decoder is compatible with the input. + pub compatible: bool, + /// Whether recognition is exact rather than program-only. + pub exact: bool, + /// Explicit priority used after program and surface matching. + pub priority: u16, + /// Optional recognized surface code. + pub surface_code: std::option::Option, + /// Optional recognized instruction or discriminator code. + pub entry_code: std::option::Option, + /// Optional normalized lowercase hexadecimal discriminator. + pub discriminator_hex: std::option::Option, +} + +impl DecoderRecognition { + /// Builds a non-compatible recognition result. + pub fn incompatible() -> Self { + return Self { + compatible: false, + exact: false, + priority: 0, + surface_code: std::option::Option::None, + entry_code: std::option::Option::None, + discriminator_hex: std::option::Option::None, + }; + } + + /// Builds a compatible recognition result. + pub fn compatible( + exact: bool, + priority: u16, + surface_code: std::option::Option, + entry_code: std::option::Option, + discriminator_hex: std::option::Option, + ) -> Self { + return Self { + compatible: true, + exact, + priority, + surface_code, + entry_code, + discriminator_hex, + }; + } +} + +/// Decoder terminal result status. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum DecoderOutcomeStatus { + /// At least one stable decoded observation was produced. + Decoded, + /// The decoder intentionally ignored a recognized input. + Ignored, + /// The input belongs to the program but its entry is unsupported. + Unsupported, + /// Decoding failed and diagnostics were produced. + Failed, +} + +/// Proof category attached to one decoded observation. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum DecoderProofKind { + /// Exact instruction discriminator match. + ExactDiscriminator, + /// Exact binary layout decode. + ExactLayout, + /// Exact IDL-based decode. + Idl, + /// Manually verified exact decode. + Manual, + /// Correlation with runtime logs. + LogCorrelation, + /// Balance delta evidence. + BalanceDelta, + /// Heuristic evidence that must not be treated as exact. + Heuristic, + /// Audit-only classification. + Audit, + /// Unknown or unclassified evidence. + Unknown, +} + +/// Explicit proof attached to one decoded observation. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecoderProof { + /// Proof category. + pub kind: DecoderProofKind, + /// Decoder confidence. + pub confidence: kb_model::DecoderConfidence, + /// Stable evidence strings, hashes or classifier codes. + pub evidence: std::vec::Vec, +} + +/// Structured decoder diagnostic without third-party error wrappers. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecoderDiagnostic { + /// Stable machine-readable diagnostic code. + pub code: std::string::String, + /// Human-readable diagnostic message. + pub message: std::string::String, + /// Whether retrying the same version and input may succeed. + pub retriable: bool, +} + +impl DecoderDiagnostic { + /// Builds a validated decoder diagnostic. + pub fn new( + code: impl std::convert::Into, + message: impl std::convert::Into, + retriable: bool, + ) -> kb_core::Result { + let value = Self { + code: code.into(), + message: message.into(), + retriable, + }; + if value.code.trim().is_empty() || value.message.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "decoder diagnostic code and message must not be empty", + )); + } + return std::result::Result::Ok(value); + } +} + +/// Stable contextual decoded observation persisted by the decode pipeline. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecodedObservation { + /// Stable processor-owned event key within one input. + pub event_key: std::string::String, + /// Shared typed protocol event identity. + pub event: kb_model::DecodedProtocolEvent, + /// Typed event payload serialized as deterministic JSON. + pub payload_json: serde_json::Value, + /// Whether the source transaction failed on-chain. + pub transaction_failed: bool, + /// Optional transaction error JSON. + pub transaction_error: std::option::Option, + /// Whether the observed state mutation was committed on-chain. + pub observation_committed: bool, + /// Explicit decoding proof. + pub proof: DecoderProof, +} + +impl DecodedObservation { + /// Validates failed transaction commit semantics and stable identity fields. + pub fn validate(&self) -> kb_core::Result<()> { + if self.event_key.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "decoded observation event key must not be empty", + )); + } + if self.transaction_failed && self.observation_committed { + return std::result::Result::Err(kb_core::Error::invalid_state( + "failed transaction observations must not be marked committed", + )); + } + if self.event.signature.0.trim().is_empty() + || self.event.instruction_path.0.trim().is_empty() + || self.event.program_id.0.trim().is_empty() + || self.event.event_code.0.trim().is_empty() + { + return std::result::Result::Err(kb_core::Error::invalid_state( + "decoded observation event identity fields must not be empty", + )); + } + return std::result::Result::Ok(()); + } +} + +/// Complete explicit result produced by one decoder invocation. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecoderExecutionResult { + /// Terminal decoder status. + pub status: DecoderOutcomeStatus, + /// Optional recognized instruction or discriminator code. + pub recognized_entry_code: std::option::Option, + /// Decoded observations. + pub observations: std::vec::Vec, + /// Structured diagnostics. + pub diagnostics: std::vec::Vec, +} + +impl DecoderExecutionResult { + /// Builds an unsupported result with no false decoded observation. + pub fn unsupported(entry_code: std::option::Option) -> Self { + return Self { + status: DecoderOutcomeStatus::Unsupported, + recognized_entry_code: entry_code, + observations: std::vec::Vec::new(), + diagnostics: std::vec::Vec::new(), + }; + } + + /// Builds an ignored result. + pub fn ignored(entry_code: std::option::Option) -> Self { + return Self { + status: DecoderOutcomeStatus::Ignored, + recognized_entry_code: entry_code, + observations: std::vec::Vec::new(), + diagnostics: std::vec::Vec::new(), + }; + } + + /// Validates status and observation consistency. + pub fn validate(&self) -> kb_core::Result<()> { + if self.status == DecoderOutcomeStatus::Decoded && self.observations.is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "decoded status requires at least one observation", + )); + } + if self.status != DecoderOutcomeStatus::Decoded && !self.observations.is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "non-decoded status must not contain observations", + )); + } + if self.status == DecoderOutcomeStatus::Failed && self.diagnostics.is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "failed decoder status requires at least one diagnostic", + )); + } + for observation in &self.observations { + let validation_result = observation.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + } + return std::result::Result::Ok(()); + } +} + +/// Contextual instruction decoder contract used by the common pipeline. +pub trait InstructionDecoder: std::marker::Send + std::marker::Sync { + /// Returns the stable decoder identity. + fn identity(&self) -> DecoderIdentity; + /// Returns every exact Solana surface supported by the decoder. + fn surfaces(&self) -> &'static [DecoderSurface]; + /// Returns the machine-readable declared coverage matrix. + fn coverage(&self) -> std::vec::Vec; + /// Recognizes one contextual core instruction for deterministic dispatch. + fn recognize(&self, input: &kb_store_core::CoreInstructionReplayInput) -> DecoderRecognition; + /// Decodes one recognized contextual core instruction. + fn decode(&self, input: &kb_store_core::CoreInstructionReplayInput) -> DecoderExecutionResult; +} + +/// Returns true when a decoder declares the exact program id. +pub fn decoder_handles_program_id(decoder: &dyn InstructionDecoder, program_id: &str) -> bool { + for surface in decoder.surfaces() { + if surface.program_id == program_id { + return true; + } + } + return false; +} + +/// Computes a deterministic lowercase SHA-256 hash for one contextual input. +pub fn contextual_input_hash( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_core::Result { + let serialization_result = serde_json::to_value(input); + let mut value = match serialization_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot serialize contextual decoder input: {error}" + ))); + }, + }; + return crate::deterministic_json_hash(&mut value); +} + +/// Computes a deterministic lowercase SHA-256 hash for one JSON value. +pub fn deterministic_json_hash( + value: &mut serde_json::Value, +) -> kb_core::Result { + sort_json_value(value); + let bytes_result = serde_json::to_vec(value); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot serialize deterministic JSON bytes: {error}" + ))); + }, + }; + let digest = sha2::Sha256::digest(bytes); + let mut output = std::string::String::with_capacity(64); + for byte in digest { + output.push_str(format!("{byte:02x}").as_str()); + } + return std::result::Result::Ok(output); +} + +/// Extracts the first eight payload bytes as normalized lowercase hexadecimal when available. +pub fn discriminator_8_hex( + input: &kb_store_core::CoreInstructionReplayInput, +) -> std::option::Option { + let payload = match &input.instruction_payload_json { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::option::Option::None, + }; + let encoded = match payload.get("dataBase64").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::option::Option::None, + }; + let decoded_result = + base64::Engine::decode(&base64::engine::general_purpose::STANDARD, encoded.as_bytes()); + let decoded = match decoded_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => return std::option::Option::None, + }; + if decoded.len() < 8 { + return std::option::Option::None; + } + let mut output = std::string::String::with_capacity(16); + for byte in &decoded[..8] { + output.push_str(format!("{byte:02x}").as_str()); + } + return std::option::Option::Some(output); +} + +fn sort_json_value(value: &mut serde_json::Value) { + match value { + serde_json::Value::Object(map) => { + let mut entries = std::mem::take(map).into_iter().collect::>(); + entries.sort_by(|left, right| return left.0.cmp(&right.0)); + for (key, mut child) in entries { + sort_json_value(&mut child); + map.insert(key, child); + } + }, + serde_json::Value::Array(values) => { + for child in values { + sort_json_value(child); + } + }, + _ => (), + } +} + +#[cfg(test)] +mod tests { + fn replay_input() -> kb_store_core::CoreInstructionReplayInput { + let result = kb_store_core::CoreInstructionReplayInput::new( + "signature:0", + "signature", + 42, + "0", + "program", + false, + std::option::Option::None, + serde_json::json!([{"accountIndex": 0, "accountKey": "account"}]), + serde_json::json!([{"accountIndex": 0, "accountKey": "account"}]), + std::option::Option::Some(serde_json::json!({"dataBase64": "AQIDBAUGBwg="})), + std::option::Option::Some("payload-hash".to_string()), + serde_json::json!([{ + "instructionIndex": 0, + "instructionPath": "0", + "programId": "program", + "payloadJson": {"dataBase64": "AQIDBAUGBwg="}, + "payloadHash": "payload-hash" + }]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected replay input error: {error}"), + }; + } + + #[test] + fn contextual_input_hash_is_stable() { + let first = crate::contextual_input_hash(&replay_input()); + let second = crate::contextual_input_hash(&replay_input()); + assert!(first.is_ok()); + assert_eq!(first, second); + } + + #[test] + fn contextual_input_hash_changes_when_outer_context_changes() { + let first = replay_input(); + let mut second = replay_input(); + second.outer_instructions_json = serde_json::json!([{ + "instructionIndex": 0, + "instructionPath": "0", + "programId": "program", + "payloadJson": {"dataBase64": "CAcGBQQDAgE="}, + "payloadHash": "different-payload-hash" + }]); + let first_hash = crate::contextual_input_hash(&first); + let second_hash = crate::contextual_input_hash(&second); + assert!(first_hash.is_ok()); + assert!(second_hash.is_ok()); + assert_ne!(first_hash, second_hash); + } + + #[test] + fn contextual_input_serialization_is_deterministic_with_outer_context() { + let first_result = serde_json::to_string(&replay_input()); + let first = match first_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected serialization error: {error}"), + }; + let second_result = serde_json::to_string(&replay_input()); + let second = match second_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected serialization error: {error}"), + }; + assert_eq!(first, second); + } + + #[test] + fn discriminator_uses_first_eight_bytes() { + let value = crate::discriminator_8_hex(&replay_input()); + assert_eq!(value.as_deref(), std::option::Option::Some("0102030405060708")); + } + + #[test] + fn failed_observation_cannot_be_committed() { + let observation = crate::DecodedObservation { + event_key: "event".to_string(), + event: kb_model::DecodedProtocolEvent { + signature: kb_model::Signature("signature".to_string()), + slot: kb_model::Slot(42), + instruction_path: kb_model::InstructionPath("0".to_string()), + program_id: kb_model::ProgramId("program".to_string()), + protocol_code: kb_model::ProtocolCode("solana".to_string()), + surface_code: kb_model::SurfaceCode("system_program".to_string()), + event_code: kb_model::EventCode("system_program.attempt".to_string()), + event_name: kb_model::EventName("attempt".to_string()), + event_family: kb_model::EventFamily::Audit, + source_kind: kb_model::EventSourceKind::Instruction, + confidence: kb_model::DecoderConfidence::Exact, + }, + payload_json: serde_json::json!({}), + transaction_failed: true, + transaction_error: std::option::Option::Some( + serde_json::json!({"InstructionError": [0, "Custom"]}), + ), + observation_committed: true, + proof: crate::DecoderProof { + kind: crate::DecoderProofKind::ExactLayout, + confidence: kb_model::DecoderConfidence::Exact, + evidence: std::vec!["layout".to_string()], + }, + }; + assert!(observation.validate().is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_api/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_api/src/decoder.rs new file mode 100644 index 0000000..1d9333c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_api/src/decoder.rs @@ -0,0 +1,74 @@ +// file: kb_decoder_api/src/decoder.rs +// version: 4 + +//! Shared decoder contracts for protocol decoder crates. + +/// Decoder support level for a program observation. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum DecoderSupport { + /// The decoder does not support the observation. + No, + /// The decoder may support the observation after deeper inspection. + Maybe, + /// The decoder explicitly supports the observation. + Yes, +} + +/// Common trait implemented by every protocol decoder crate. +pub trait ProtocolDecoder { + /// Returns the stable decoder name. + fn decoder_name(&self) -> &'static str; + /// Returns the decoder implementation version. + fn decoder_version(&self) -> &'static str; + /// Returns the Solana program ids handled by this decoder. + fn program_ids(&self) -> &'static [&'static str]; + /// Checks whether the decoder handles a program id. + fn handles_program_id(&self, program_id: &kb_model::ProgramId) -> bool { + for candidate in self.program_ids() { + if program_id.0.as_str() == *candidate { + return true; + } + } + return false; + } + /// Checks whether the decoder supports a generic program observation. + fn supports_observation(&self, observation: &kb_model::ProgramObservation) -> DecoderSupport; + /// Decodes a generic program observation into protocol events. + fn decode_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_core::Result>; +} + +/// Initial decoder implementation placeholder for API-level tests. +#[derive(Clone, Debug, Default)] +pub struct InitialDecoder; + +impl ProtocolDecoder for InitialDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_api"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[]; + } + + fn supports_observation(&self, observation: &kb_model::ProgramObservation) -> DecoderSupport { + if ProtocolDecoder::handles_program_id(self, &observation.program_id) { + return DecoderSupport::Maybe; + } + return DecoderSupport::No; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_api/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_api/src/lib.rs new file mode 100644 index 0000000..64e8650 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_api/src/lib.rs @@ -0,0 +1,56 @@ +// file: kb_decoder_api/src/lib.rs +// version: 5 + +//! Decoder API shared by protocol decoder crates. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod contracts; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Current contextual core instruction input contract version. +pub use crate::contracts::CORE_INSTRUCTION_INPUT_CONTRACT_VERSION; +/// Stable contextual decoded observation. +pub use crate::contracts::DecodedObservation; +/// Machine-readable decoder coverage declaration. +pub use crate::contracts::DecoderCoverageDeclaration; +/// Declared decoder coverage entry kind. +pub use crate::contracts::DecoderCoverageEntryKind; +/// Structured decoder diagnostic. +pub use crate::contracts::DecoderDiagnostic; +/// Complete decoder execution result. +pub use crate::contracts::DecoderExecutionResult; +/// Stable decoder identity. +pub use crate::contracts::DecoderIdentity; +/// Decoder terminal result status. +pub use crate::contracts::DecoderOutcomeStatus; +/// Explicit decoding proof. +pub use crate::contracts::DecoderProof; +/// Decoding proof category. +pub use crate::contracts::DecoderProofKind; +/// Deterministic decoder recognition result. +pub use crate::contracts::DecoderRecognition; +/// One statically declared decoder surface. +pub use crate::contracts::DecoderSurface; +/// Contextual instruction decoder contract. +pub use crate::contracts::InstructionDecoder; +/// Computes the deterministic contextual input hash. +pub use crate::contracts::contextual_input_hash; +/// Returns true when a decoder declares the exact program id. +pub use crate::contracts::decoder_handles_program_id; +/// Computes a deterministic lowercase SHA-256 hash for one JSON value. +pub use crate::contracts::deterministic_json_hash; +/// Extracts the first eight payload bytes as hexadecimal when available. +pub use crate::contracts::discriminator_8_hex; +/// Exposes the decoder support level. +pub use crate::decoder::DecoderSupport; +/// Initial decoder implementation placeholder for API-level tests. +pub use crate::decoder::InitialDecoder; +/// Exposes the common protocol decoder trait. +pub use crate::decoder::ProtocolDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/Cargo.toml new file mode 100644 index 0000000..bdf35d4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_bridge_circle_cctp_token_messenger_minter/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_bridge_circle_cctp_token_messenger_minter" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/README.md b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/README.md new file mode 100644 index 0000000..6ecd638 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_bridge_circle_cctp_token_messenger_minter + +Ce crate réserve la surface de décodage `bridge_circle_cctp_token_messenger_minter`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `bridge_circle_cctp_token_messenger_minter` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/src/constants.rs new file mode 100644 index 0000000..c110546 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_bridge_circle_cctp_token_messenger_minter/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_bridge_circle_cctp_token_messenger_minter` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_bridge_circle_cctp_token_messenger_minter"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/src/decoder.rs new file mode 100644 index 0000000..7adb394 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_bridge_circle_cctp_token_messenger_minter/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_bridge_circle_cctp_token_messenger_minter` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct BridgeCircleCctpTokenMessengerMinterDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::BridgeCircleCctpTokenMessengerMinterDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_bridge_circle_cctp_token_messenger_minter"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::BRIDGE_CIRCLE_CCTP_CCTP_TOKEN_MESSENGER_MINTER_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/src/lib.rs new file mode 100644 index 0000000..813e206 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_bridge_circle_cctp_token_messenger_minter/src/lib.rs +// version: 2 + +//! Decoder crate for `bridge_circle_cctp_token_messenger_minter`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::BridgeCircleCctpTokenMessengerMinterDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/Cargo.toml new file mode 100644 index 0000000..e285898 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_bridge_circle_cctp_token_messenger_minter_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/README.md b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/README.md new file mode 100644 index 0000000..19cbef0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_bridge_circle_cctp_token_messenger_minter_v2 + +Ce crate réserve la surface de décodage `bridge_circle_cctp_token_messenger_minter_v2`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `bridge_circle_cctp_token_messenger_minter_v2` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/src/constants.rs new file mode 100644 index 0000000..06a29e6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_bridge_circle_cctp_token_messenger_minter_v2` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_bridge_circle_cctp_token_messenger_minter_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/src/decoder.rs new file mode 100644 index 0000000..0a39702 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_bridge_circle_cctp_token_messenger_minter_v2` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct BridgeCircleCctpTokenMessengerMinterV2Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::BridgeCircleCctpTokenMessengerMinterV2Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_bridge_circle_cctp_token_messenger_minter_v2"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::BRIDGE_CIRCLE_CCTP_CCTP_TOKEN_MESSENGER_MINTER_V2_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/src/lib.rs new file mode 100644 index 0000000..924c1e4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_bridge_circle_cctp_token_messenger_minter_v2/src/lib.rs +// version: 2 + +//! Decoder crate for `bridge_circle_cctp_token_messenger_minter_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::BridgeCircleCctpTokenMessengerMinterV2Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/Cargo.toml new file mode 100644 index 0000000..676de22 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_bridge_layer_zero_endpoint/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_bridge_layer_zero_endpoint" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/README.md b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/README.md new file mode 100644 index 0000000..69a0a00 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_bridge_layer_zero_endpoint + +Ce crate réserve la surface de décodage `bridge_layer_zero_endpoint`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `bridge_layer_zero_endpoint` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/src/constants.rs new file mode 100644 index 0000000..b1775d9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_bridge_layer_zero_endpoint/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_bridge_layer_zero_endpoint` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_bridge_layer_zero_endpoint"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/src/decoder.rs new file mode 100644 index 0000000..862cc45 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_bridge_layer_zero_endpoint/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_bridge_layer_zero_endpoint` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct BridgeLayerZeroEndpointDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::BridgeLayerZeroEndpointDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_bridge_layer_zero_endpoint"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::BRIDGE_LAYER_ZERO_ENDPOINT_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/src/lib.rs new file mode 100644 index 0000000..b7edf4e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_endpoint/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_bridge_layer_zero_endpoint/src/lib.rs +// version: 2 + +//! Decoder crate for `bridge_layer_zero_endpoint`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::BridgeLayerZeroEndpointDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/Cargo.toml new file mode 100644 index 0000000..0126fae --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_bridge_layer_zero_executor/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_bridge_layer_zero_executor" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/README.md b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/README.md new file mode 100644 index 0000000..8fd40e2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_bridge_layer_zero_executor + +Ce crate réserve la surface de décodage `bridge_layer_zero_executor`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `bridge_layer_zero_executor` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/src/constants.rs new file mode 100644 index 0000000..e65f9dc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_bridge_layer_zero_executor/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_bridge_layer_zero_executor` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_bridge_layer_zero_executor"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/src/decoder.rs new file mode 100644 index 0000000..6b05cf0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_bridge_layer_zero_executor/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_bridge_layer_zero_executor` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct BridgeLayerZeroExecutorDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::BridgeLayerZeroExecutorDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_bridge_layer_zero_executor"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::BRIDGE_LAYER_ZERO_EXECUTOR_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/src/lib.rs new file mode 100644 index 0000000..9632b87 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_bridge_layer_zero_executor/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_bridge_layer_zero_executor/src/lib.rs +// version: 2 + +//! Decoder crate for `bridge_layer_zero_executor`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::BridgeLayerZeroExecutorDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/Cargo.toml new file mode 100644 index 0000000..d161c2c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_clmm_byreal/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_clmm_byreal" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/README.md b/migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/README.md new file mode 100644 index 0000000..90d4afd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_clmm_byreal + +Ce crate réserve la surface de décodage `clmm_byreal`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `clmm_byreal` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/src/constants.rs new file mode 100644 index 0000000..116bb1b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_clmm_byreal/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_clmm_byreal` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_clmm_byreal"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/src/decoder.rs new file mode 100644 index 0000000..7a2780f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_clmm_byreal/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_clmm_byreal` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct ClmmByrealDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::ClmmByrealDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_clmm_byreal"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::CLMM_BYREAL_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/src/lib.rs new file mode 100644 index 0000000..3ba5098 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_byreal/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_clmm_byreal/src/lib.rs +// version: 2 + +//! Decoder crate for `clmm_byreal`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::ClmmByrealDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/Cargo.toml new file mode 100644 index 0000000..945ee30 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_clmm_fusion/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_clmm_fusion" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/README.md b/migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/README.md new file mode 100644 index 0000000..a9bc73b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_clmm_fusion + +Ce crate réserve la surface de décodage `clmm_fusion`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `clmm_fusion` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/src/constants.rs new file mode 100644 index 0000000..e2060ce --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_clmm_fusion/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_clmm_fusion` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_clmm_fusion"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/src/decoder.rs new file mode 100644 index 0000000..8f9a5ec --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_clmm_fusion/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_clmm_fusion` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct ClmmFusionDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::ClmmFusionDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_clmm_fusion"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::CLMM_FUSION_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/src/lib.rs new file mode 100644 index 0000000..8e194e6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_fusion/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_clmm_fusion/src/lib.rs +// version: 2 + +//! Decoder crate for `clmm_fusion`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::ClmmFusionDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/Cargo.toml new file mode 100644 index 0000000..8d325bb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_clmm_orca_whirlpool/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_clmm_orca_whirlpool" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/README.md b/migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/README.md new file mode 100644 index 0000000..81013ce --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/README.md @@ -0,0 +1,17 @@ + + + +# kb_decoder_clmm_orca_whirlpool + +Ce crate décode la surface protocolaire `orca_whirlpool` en événements protocolaires normalisés. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/src/constants.rs new file mode 100644 index 0000000..1a583f2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_clmm_orca_whirlpool/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_clmm_orca_whirlpool` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_clmm_orca_whirlpool"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/src/decoder.rs new file mode 100644 index 0000000..47df1a4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/src/decoder.rs @@ -0,0 +1,42 @@ +// file: kb_decoder_clmm_orca_whirlpool/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_clmm_orca_whirlpool` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct ClmmOrcaWhirlpoolDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::ClmmOrcaWhirlpoolDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_clmm_orca_whirlpool"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::CLMM_ORCA_WHIRLPOOL_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/src/lib.rs new file mode 100644 index 0000000..5d84f7a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_orca_whirlpool/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_clmm_orca_whirlpool/src/lib.rs +// version: 2 + +//! Decoder crate for `clmm_orca_whirlpool`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Initial decoder implementation placeholder. +pub use crate::decoder::ClmmOrcaWhirlpoolDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/Cargo.toml new file mode 100644 index 0000000..36d63c1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_clmm_pancake_swap/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_clmm_pancake_swap" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/README.md b/migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/README.md new file mode 100644 index 0000000..a5254c9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_clmm_pancake_swap + +Ce crate réserve la surface de décodage `clmm_pancake_swap`. Aucun `program_id` local n'est encore fixé dans ce squelette. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `clmm_pancake_swap` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/src/constants.rs new file mode 100644 index 0000000..554e222 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_clmm_pancake_swap/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_clmm_pancake_swap` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_clmm_pancake_swap"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/src/decoder.rs new file mode 100644 index 0000000..505819e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_clmm_pancake_swap/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_clmm_pancake_swap` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct ClmmPancakeSwapDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::ClmmPancakeSwapDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_clmm_pancake_swap"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::CLMM_PANCAKE_SWAP_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/src/lib.rs new file mode 100644 index 0000000..bd282c0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_pancake_swap/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_clmm_pancake_swap/src/lib.rs +// version: 2 + +//! Decoder crate for `clmm_pancake_swap`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::ClmmPancakeSwapDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/Cargo.toml new file mode 100644 index 0000000..4a00ab4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_clmm_raydium/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_clmm_raydium" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/README.md b/migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/README.md new file mode 100644 index 0000000..bde5200 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/README.md @@ -0,0 +1,17 @@ + + + +# kb_decoder_clmm_raydium + +Ce crate décode la surface protocolaire `raydium_clmm` en événements protocolaires normalisés. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/src/constants.rs new file mode 100644 index 0000000..7ef5a65 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_clmm_raydium/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_clmm_raydium` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_clmm_raydium"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/src/decoder.rs new file mode 100644 index 0000000..d2ec47e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/src/decoder.rs @@ -0,0 +1,42 @@ +// file: kb_decoder_clmm_raydium/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_clmm_raydium` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct ClmmRaydiumDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::ClmmRaydiumDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_clmm_raydium"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::CLMM_RAYDIUM_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/src/lib.rs new file mode 100644 index 0000000..dc2aa9c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_raydium/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_clmm_raydium/src/lib.rs +// version: 2 + +//! Decoder crate for `clmm_raydium`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Initial decoder implementation placeholder. +pub use crate::decoder::ClmmRaydiumDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/Cargo.toml new file mode 100644 index 0000000..3883b2e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_clmm_stabble/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_clmm_stabble" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/README.md b/migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/README.md new file mode 100644 index 0000000..c947948 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_clmm_stabble + +Ce crate réserve la surface de décodage `clmm_stabble`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `clmm_stabble` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/src/constants.rs new file mode 100644 index 0000000..6cdbb63 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_clmm_stabble/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_clmm_stabble` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_clmm_stabble"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/src/decoder.rs new file mode 100644 index 0000000..a80f5d3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_clmm_stabble/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_clmm_stabble` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct ClmmStabbleDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::ClmmStabbleDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_clmm_stabble"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::CLMM_STABBLE_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/src/lib.rs new file mode 100644 index 0000000..fdd6726 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_clmm_stabble/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_clmm_stabble/src/lib.rs +// version: 2 + +//! Decoder crate for `clmm_stabble`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::ClmmStabbleDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/Cargo.toml new file mode 100644 index 0000000..6555f14 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_cpmm_raydium/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_cpmm_raydium" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/README.md b/migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/README.md new file mode 100644 index 0000000..40098c8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/README.md @@ -0,0 +1,17 @@ + + + +# kb_decoder_cpmm_raydium + +Ce crate décode la surface protocolaire `raydium_cpmm` en événements protocolaires normalisés. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/src/constants.rs new file mode 100644 index 0000000..300ae5b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_cpmm_raydium/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_cpmm_raydium` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_cpmm_raydium"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/src/decoder.rs new file mode 100644 index 0000000..519d6ef --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/src/decoder.rs @@ -0,0 +1,42 @@ +// file: kb_decoder_cpmm_raydium/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_cpmm_raydium` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct CpmmRaydiumDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::CpmmRaydiumDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_cpmm_raydium"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::CPMM_RAYDIUM_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/src/lib.rs new file mode 100644 index 0000000..d95148c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_cpmm_raydium/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_cpmm_raydium/src/lib.rs +// version: 2 + +//! Decoder crate for `cpmm_raydium`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Initial decoder implementation placeholder. +pub use crate::decoder::CpmmRaydiumDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/Cargo.toml new file mode 100644 index 0000000..c572099 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_dlmm_meteora/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_dlmm_meteora" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/README.md b/migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/README.md new file mode 100644 index 0000000..729f1e1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/README.md @@ -0,0 +1,17 @@ + + + +# kb_decoder_dlmm_meteora + +Ce crate décode la surface protocolaire `meteora_dlmm` en événements protocolaires normalisés. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/src/constants.rs new file mode 100644 index 0000000..b374835 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_dlmm_meteora/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_dlmm_meteora` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_dlmm_meteora"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/src/decoder.rs new file mode 100644 index 0000000..2fe2aac --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/src/decoder.rs @@ -0,0 +1,42 @@ +// file: kb_decoder_dlmm_meteora/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_dlmm_meteora` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct DlmmMeteoraDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::DlmmMeteoraDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_dlmm_meteora"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::DLMM_METEORA_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/src/lib.rs new file mode 100644 index 0000000..df985be --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_dlmm_meteora/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_dlmm_meteora/src/lib.rs +// version: 2 + +//! Decoder crate for `dlmm_meteora`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Initial decoder implementation placeholder. +pub use crate::decoder::DlmmMeteoraDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/Cargo.toml new file mode 100644 index 0000000..b7abf94 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_fees_bags_fee_share_v1/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_fees_bags_fee_share_v1" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/README.md b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/README.md new file mode 100644 index 0000000..71680a2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_fees_bags_fee_share_v1 + +Ce crate réserve le décodeur de la surface canonique `fees_bags_fee_share_v1`. + +## Rôle dans l'écosystème + +Il servira à décoder les instructions et événements Bags Fee Share liés à la configuration et au claim de frais. + +## Statut + +Le crate est un squelette. Les layouts d'instructions, discriminants et corpus de validation restent à ajouter. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/src/constants.rs new file mode 100644 index 0000000..888b2a0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_fees_bags_fee_share_v1/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_fees_bags_fee_share_v1` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_fees_bags_fee_share_v1"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/src/decoder.rs new file mode 100644 index 0000000..ed9b0a2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/src/decoder.rs @@ -0,0 +1,42 @@ +// file: kb_decoder_fees_bags_fee_share_v1/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_fees_bags_fee_share_v1` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct FeesBagsFeeShareV1Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::FeesBagsFeeShareV1Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_fees_bags_fee_share_v1"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::FEES_BAGS_FEE_SHARE_V1_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/src/lib.rs new file mode 100644 index 0000000..557a4af --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v1/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_fees_bags_fee_share_v1/src/lib.rs +// version: 2 + +//! Decoder crate for `fees_bags_fee_share_v1`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::FeesBagsFeeShareV1Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/Cargo.toml new file mode 100644 index 0000000..2b8cfb5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_fees_bags_fee_share_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_fees_bags_fee_share_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/README.md b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/README.md new file mode 100644 index 0000000..cf5563c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_fees_bags_fee_share_v2 + +Ce crate réserve le décodeur de la surface canonique `fees_bags_fee_share_v2`. + +## Rôle dans l'écosystème + +Il servira à décoder les instructions et événements Bags Fee Share liés à la configuration et au claim de frais. + +## Statut + +Le crate est un squelette. Les layouts d'instructions, discriminants et corpus de validation restent à ajouter. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/src/constants.rs new file mode 100644 index 0000000..d378904 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_fees_bags_fee_share_v2/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_fees_bags_fee_share_v2` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_fees_bags_fee_share_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/src/decoder.rs new file mode 100644 index 0000000..8792ce2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/src/decoder.rs @@ -0,0 +1,42 @@ +// file: kb_decoder_fees_bags_fee_share_v2/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_fees_bags_fee_share_v2` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct FeesBagsFeeShareV2Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::FeesBagsFeeShareV2Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_fees_bags_fee_share_v2"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::FEES_BAGS_FEE_SHARE_V2_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/src/lib.rs new file mode 100644 index 0000000..9cb5491 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_fees_bags_fee_share_v2/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_fees_bags_fee_share_v2/src/lib.rs +// version: 2 + +//! Decoder crate for `fees_bags_fee_share_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::FeesBagsFeeShareV2Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/Cargo.toml new file mode 100644 index 0000000..762e5ac --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_governance_metadao_bid_wall/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_governance_metadao_bid_wall" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/README.md b/migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/README.md new file mode 100644 index 0000000..5cae264 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_governance_metadao_bid_wall + +Ce crate réserve la surface de décodage `governance_metadao_bid_wall`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `governance_metadao_bid_wall` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/src/constants.rs new file mode 100644 index 0000000..de9dfd3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_governance_metadao_bid_wall/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_governance_metadao_bid_wall` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_governance_metadao_bid_wall"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/src/decoder.rs new file mode 100644 index 0000000..4a54954 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_governance_metadao_bid_wall/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_governance_metadao_bid_wall` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct GovernanceMetadaoBidWallDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::GovernanceMetadaoBidWallDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_governance_metadao_bid_wall"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::GOVERNANCE_METADAO_BID_WALL_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/src/lib.rs new file mode 100644 index 0000000..4be8ee8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_governance_metadao_bid_wall/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_governance_metadao_bid_wall/src/lib.rs +// version: 2 + +//! Decoder crate for `governance_metadao_bid_wall`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::GovernanceMetadaoBidWallDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/Cargo.toml new file mode 100644 index 0000000..f0df0cd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_launchpad_boop_fun/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_launchpad_boop_fun" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/README.md b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/README.md new file mode 100644 index 0000000..85ad24a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_launchpad_boop_fun + +Ce crate réserve la surface de décodage `launchpad_boop_fun`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `launchpad_boop_fun` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/src/constants.rs new file mode 100644 index 0000000..12cc150 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_launchpad_boop_fun/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_launchpad_boop_fun` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_launchpad_boop_fun"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/src/decoder.rs new file mode 100644 index 0000000..648e883 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_launchpad_boop_fun/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_launchpad_boop_fun` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LaunchpadBoopFunDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::LaunchpadBoopFunDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_launchpad_boop_fun"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LAUNCHPAD_BOOP_FUN_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/src/lib.rs new file mode 100644 index 0000000..635afb0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_boop_fun/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_launchpad_boop_fun/src/lib.rs +// version: 2 + +//! Decoder crate for `launchpad_boop_fun`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::LaunchpadBoopFunDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/Cargo.toml new file mode 100644 index 0000000..c0aee31 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_launchpad_metadao_ico/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_launchpad_metadao_ico" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/README.md b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/README.md new file mode 100644 index 0000000..a67a7c4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_launchpad_metadao_ico + +Ce crate réserve la surface de décodage `launchpad_metadao_ico`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `launchpad_metadao_ico` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/src/constants.rs new file mode 100644 index 0000000..a3b17bb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_launchpad_metadao_ico/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_launchpad_metadao_ico` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_launchpad_metadao_ico"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/src/decoder.rs new file mode 100644 index 0000000..157df00 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_launchpad_metadao_ico/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_launchpad_metadao_ico` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LaunchpadMetadaoIcoDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::LaunchpadMetadaoIcoDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_launchpad_metadao_ico"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LAUNCHPAD_METADAO_ICO_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/src/lib.rs new file mode 100644 index 0000000..7bc37f6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_metadao_ico/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_launchpad_metadao_ico/src/lib.rs +// version: 2 + +//! Decoder crate for `launchpad_metadao_ico`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::LaunchpadMetadaoIcoDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/Cargo.toml new file mode 100644 index 0000000..8d34c1c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_launchpad_meteora_dbc/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_launchpad_meteora_dbc" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/README.md b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/README.md new file mode 100644 index 0000000..5e45e8a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/README.md @@ -0,0 +1,17 @@ + + + +# kb_decoder_launchpad_meteora_dbc + +Ce crate décode la surface protocolaire `meteora_dbc` en événements protocolaires normalisés. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/src/constants.rs new file mode 100644 index 0000000..d666a96 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_launchpad_meteora_dbc/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_launchpad_meteora_dbc` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_launchpad_meteora_dbc"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/src/decoder.rs new file mode 100644 index 0000000..e52396c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/src/decoder.rs @@ -0,0 +1,42 @@ +// file: kb_decoder_launchpad_meteora_dbc/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_launchpad_meteora_dbc` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LaunchpadMeteoraDbcDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::LaunchpadMeteoraDbcDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_launchpad_meteora_dbc"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LAUNCHPAD_METEORA_DBC_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/src/lib.rs new file mode 100644 index 0000000..b8965ec --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_meteora_dbc/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_launchpad_meteora_dbc/src/lib.rs +// version: 2 + +//! Decoder crate for `launchpad_meteora_dbc`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Initial decoder implementation placeholder. +pub use crate::decoder::LaunchpadMeteoraDbcDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/Cargo.toml new file mode 100644 index 0000000..9c172ef --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_launchpad_moonit/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_launchpad_moonit" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/README.md b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/README.md new file mode 100644 index 0000000..11817a6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_launchpad_moonit + +Ce crate réserve la surface de décodage `launchpad_moonit`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `launchpad_moonit` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/src/constants.rs new file mode 100644 index 0000000..7d2bd1a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_launchpad_moonit/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_launchpad_moonit` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_launchpad_moonit"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/src/decoder.rs new file mode 100644 index 0000000..b511670 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_launchpad_moonit/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_launchpad_moonit` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LaunchpadMoonitDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::LaunchpadMoonitDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_launchpad_moonit"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LAUNCHPAD_MOONIT_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/src/lib.rs new file mode 100644 index 0000000..01fc5bc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_moonit/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_launchpad_moonit/src/lib.rs +// version: 2 + +//! Decoder crate for `launchpad_moonit`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::LaunchpadMoonitDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/Cargo.toml new file mode 100644 index 0000000..72a1647 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_launchpad_orca_wavebreak/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_launchpad_orca_wavebreak" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/README.md b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/README.md new file mode 100644 index 0000000..8a03622 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_launchpad_orca_wavebreak + +Ce crate réserve la surface de décodage `launchpad_orca_wavebreak`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `launchpad_orca_wavebreak` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/src/constants.rs new file mode 100644 index 0000000..a0221cb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_launchpad_orca_wavebreak/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_launchpad_orca_wavebreak` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_launchpad_orca_wavebreak"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/src/decoder.rs new file mode 100644 index 0000000..2e71fbd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_launchpad_orca_wavebreak/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_launchpad_orca_wavebreak` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LaunchpadOrcaWavebreakDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::LaunchpadOrcaWavebreakDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_launchpad_orca_wavebreak"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LAUNCHPAD_ORCA_WAVEBREAK_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/src/lib.rs new file mode 100644 index 0000000..9a79100 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_orca_wavebreak/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_launchpad_orca_wavebreak/src/lib.rs +// version: 2 + +//! Decoder crate for `launchpad_orca_wavebreak`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::LaunchpadOrcaWavebreakDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/Cargo.toml new file mode 100644 index 0000000..8a23e64 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_launchpad_printr/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_launchpad_printr" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/README.md b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/README.md new file mode 100644 index 0000000..0d6e2f0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_launchpad_printr + +Ce crate réserve la surface de décodage `launchpad_printr`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `launchpad_printr` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/src/constants.rs new file mode 100644 index 0000000..2b382e0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_launchpad_printr/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_launchpad_printr` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_launchpad_printr"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/src/decoder.rs new file mode 100644 index 0000000..4a8e83a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_launchpad_printr/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_launchpad_printr` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LaunchpadPrintrDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::LaunchpadPrintrDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_launchpad_printr"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LAUNCHPAD_PRINTR_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/src/lib.rs new file mode 100644 index 0000000..bc9fca0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_printr/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_launchpad_printr/src/lib.rs +// version: 2 + +//! Decoder crate for `launchpad_printr`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::LaunchpadPrintrDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/Cargo.toml new file mode 100644 index 0000000..f5f2702 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_launchpad_pump_fun/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_launchpad_pump_fun" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/README.md b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/README.md new file mode 100644 index 0000000..e98d13b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/README.md @@ -0,0 +1,17 @@ + + + +# kb_decoder_launchpad_pump_fun + +Ce crate décode la surface protocolaire `pump_fun` en événements protocolaires normalisés. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/src/constants.rs new file mode 100644 index 0000000..b5eebc5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_launchpad_pump_fun/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_launchpad_pump_fun` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_launchpad_pump_fun"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/src/decoder.rs new file mode 100644 index 0000000..55d0162 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/src/decoder.rs @@ -0,0 +1,42 @@ +// file: kb_decoder_launchpad_pump_fun/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_launchpad_pump_fun` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LaunchpadPumpFunDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::LaunchpadPumpFunDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_launchpad_pump_fun"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LAUNCHPAD_PUMP_FUN_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/src/lib.rs new file mode 100644 index 0000000..76272d4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_fun/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_launchpad_pump_fun/src/lib.rs +// version: 2 + +//! Decoder crate for `launchpad_pump_fun`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Initial decoder implementation placeholder. +pub use crate::decoder::LaunchpadPumpFunDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/Cargo.toml new file mode 100644 index 0000000..a13773b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_launchpad_pump_pumpup_ai/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_launchpad_pump_pumpup_ai" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/README.md b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/README.md new file mode 100644 index 0000000..f38df38 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_launchpad_pump_pumpup_ai + +Ce crate réserve la surface de décodage `launchpad_pump_pumpup_ai`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `launchpad_pump_pumpup_ai` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/src/constants.rs new file mode 100644 index 0000000..aedaca2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_launchpad_pump_pumpup_ai/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_launchpad_pump_pumpup_ai` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_launchpad_pump_pumpup_ai"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/src/decoder.rs new file mode 100644 index 0000000..174816f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_launchpad_pump_pumpup_ai/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_launchpad_pump_pumpup_ai` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LaunchpadPumpPumpupAiDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::LaunchpadPumpPumpupAiDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_launchpad_pump_pumpup_ai"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LAUNCHPAD_PUMP_PUMPUP_AI_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/src/lib.rs new file mode 100644 index 0000000..ddbea2e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_pump_pumpup_ai/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_launchpad_pump_pumpup_ai/src/lib.rs +// version: 2 + +//! Decoder crate for `launchpad_pump_pumpup_ai`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::LaunchpadPumpPumpupAiDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/Cargo.toml new file mode 100644 index 0000000..5e52240 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_launchpad_raydium_launchlab/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_launchpad_raydium_launchlab" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/README.md b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/README.md new file mode 100644 index 0000000..e41ac40 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/README.md @@ -0,0 +1,17 @@ + + + +# kb_decoder_launchpad_raydium_launchlab + +Ce crate décode la surface protocolaire `raydium_launchpad` en événements protocolaires normalisés. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/src/constants.rs new file mode 100644 index 0000000..80698c8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_launchpad_raydium_launchlab/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_launchpad_raydium_launchlab` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_launchpad_raydium_launchlab"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/src/decoder.rs new file mode 100644 index 0000000..08a292f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/src/decoder.rs @@ -0,0 +1,42 @@ +// file: kb_decoder_launchpad_raydium_launchlab/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_launchpad_raydium_launchlab` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LaunchpadRaydiumLaunchlabDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::LaunchpadRaydiumLaunchlabDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_launchpad_raydium_launchlab"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LAUNCHPAD_RAYDIUM_LAUNCHLAB_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/src/lib.rs new file mode 100644 index 0000000..5ed7f2c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_launchpad_raydium_launchlab/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_launchpad_raydium_launchlab/src/lib.rs +// version: 2 + +//! Decoder crate for `launchpad_raydium_launchlab`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Initial decoder implementation placeholder. +pub use crate::decoder::LaunchpadRaydiumLaunchlabDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_clone/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_lending_clone/Cargo.toml new file mode 100644 index 0000000..b23db27 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_clone/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_lending_clone/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_lending_clone" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_clone/README.md b/migration/khadhroony-bot2-reference/kb_decoder_lending_clone/README.md new file mode 100644 index 0000000..02bf6fd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_clone/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_lending_clone + +Ce crate réserve la surface de décodage `lending_clone`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `lending_clone` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_clone/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_clone/src/constants.rs new file mode 100644 index 0000000..266ac77 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_clone/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_lending_clone/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_lending_clone` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_lending_clone"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_clone/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_clone/src/decoder.rs new file mode 100644 index 0000000..df48c2f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_clone/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_lending_clone/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_lending_clone` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LendingCloneDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::LendingCloneDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_lending_clone"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LENDING_CLONE_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_clone/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_clone/src/lib.rs new file mode 100644 index 0000000..a5b0715 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_clone/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_lending_clone/src/lib.rs +// version: 2 + +//! Decoder crate for `lending_clone`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::LendingCloneDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/Cargo.toml new file mode 100644 index 0000000..9399d1f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_lending_jupiter_lend_borrow/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_lending_jupiter_lend_borrow" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/README.md b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/README.md new file mode 100644 index 0000000..9b4a6b5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_lending_jupiter_lend_borrow + +Ce crate réserve la surface de décodage `lending_jupiter_lend_borrow`. Aucun `program_id` local n'est encore fixé dans ce squelette. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `lending_jupiter_lend_borrow` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/src/constants.rs new file mode 100644 index 0000000..056e88e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_lending_jupiter_lend_borrow/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_lending_jupiter_lend_borrow` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_lending_jupiter_lend_borrow"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/src/decoder.rs new file mode 100644 index 0000000..fd268ee --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_lending_jupiter_lend_borrow/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_lending_jupiter_lend_borrow` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LendingJupiterLendBorrowDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::LendingJupiterLendBorrowDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_lending_jupiter_lend_borrow"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LENDING_JUPITER_LEND_BORROW_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/src/lib.rs new file mode 100644 index 0000000..6f66869 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_borrow/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_lending_jupiter_lend_borrow/src/lib.rs +// version: 2 + +//! Decoder crate for `lending_jupiter_lend_borrow`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::LendingJupiterLendBorrowDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/Cargo.toml new file mode 100644 index 0000000..d4121d7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_lending_jupiter_lend_earn/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_lending_jupiter_lend_earn" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/README.md b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/README.md new file mode 100644 index 0000000..2b3270a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_lending_jupiter_lend_earn + +Ce crate réserve la surface de décodage `lending_jupiter_lend_earn`. Aucun `program_id` local n'est encore fixé dans ce squelette. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `lending_jupiter_lend_earn` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/src/constants.rs new file mode 100644 index 0000000..79a1b91 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_lending_jupiter_lend_earn/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_lending_jupiter_lend_earn` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_lending_jupiter_lend_earn"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/src/decoder.rs new file mode 100644 index 0000000..ad98eb3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_lending_jupiter_lend_earn/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_lending_jupiter_lend_earn` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LendingJupiterLendEarnDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::LendingJupiterLendEarnDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_lending_jupiter_lend_earn"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LENDING_JUPITER_LEND_EARN_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/src/lib.rs new file mode 100644 index 0000000..9d8fd7a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_earn/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_lending_jupiter_lend_earn/src/lib.rs +// version: 2 + +//! Decoder crate for `lending_jupiter_lend_earn`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::LendingJupiterLendEarnDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/Cargo.toml new file mode 100644 index 0000000..91970d0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_lending_jupiter_lend_flash_loan/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_lending_jupiter_lend_flash_loan" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/README.md b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/README.md new file mode 100644 index 0000000..6554bc0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_lending_jupiter_lend_flash_loan + +Ce crate réserve la surface de décodage `lending_jupiter_lend_flash_loan`. Aucun `program_id` local n'est encore fixé dans ce squelette. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `lending_jupiter_lend_flash_loan` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/src/constants.rs new file mode 100644 index 0000000..115e0fe --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_lending_jupiter_lend_flash_loan/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_lending_jupiter_lend_flash_loan` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_lending_jupiter_lend_flash_loan"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/src/decoder.rs new file mode 100644 index 0000000..a111851 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_lending_jupiter_lend_flash_loan/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_lending_jupiter_lend_flash_loan` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LendingJupiterLendFlashLoanDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::LendingJupiterLendFlashLoanDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_lending_jupiter_lend_flash_loan"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LENDING_JUPITER_LEND_FLASH_LOAN_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/src/lib.rs new file mode 100644 index 0000000..ee81e58 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_flash_loan/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_lending_jupiter_lend_flash_loan/src/lib.rs +// version: 2 + +//! Decoder crate for `lending_jupiter_lend_flash_loan`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::LendingJupiterLendFlashLoanDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/Cargo.toml new file mode 100644 index 0000000..a921c61 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_lending_jupiter_lend_liquidity/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_lending_jupiter_lend_liquidity" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/README.md b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/README.md new file mode 100644 index 0000000..b3f976f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_lending_jupiter_lend_liquidity + +Ce crate réserve la surface de décodage `lending_jupiter_lend_liquidity`. Aucun `program_id` local n'est encore fixé dans ce squelette. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `lending_jupiter_lend_liquidity` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/src/constants.rs new file mode 100644 index 0000000..25c6995 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_lending_jupiter_lend_liquidity/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_lending_jupiter_lend_liquidity` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_lending_jupiter_lend_liquidity"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/src/decoder.rs new file mode 100644 index 0000000..205fe34 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_lending_jupiter_lend_liquidity/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_lending_jupiter_lend_liquidity` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LendingJupiterLendLiquidityDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::LendingJupiterLendLiquidityDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_lending_jupiter_lend_liquidity"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LENDING_JUPITER_LEND_LIQUIDITY_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/src/lib.rs new file mode 100644 index 0000000..04ea9ea --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_jupiter_lend_liquidity/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_lending_jupiter_lend_liquidity/src/lib.rs +// version: 2 + +//! Decoder crate for `lending_jupiter_lend_liquidity`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::LendingJupiterLendLiquidityDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/Cargo.toml new file mode 100644 index 0000000..adb0001 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_lending_kamino/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_lending_kamino" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/README.md b/migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/README.md new file mode 100644 index 0000000..20ab5e7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_lending_kamino + +Ce crate réserve la surface de décodage `lending_kamino`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `lending_kamino` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/src/constants.rs new file mode 100644 index 0000000..f705c21 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_lending_kamino/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_lending_kamino` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_lending_kamino"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/src/decoder.rs new file mode 100644 index 0000000..c5e7e50 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_lending_kamino/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_lending_kamino` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LendingKaminoDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::LendingKaminoDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_lending_kamino"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LENDING_KAMINO_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/src/lib.rs new file mode 100644 index 0000000..83ac84d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_kamino/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_lending_kamino/src/lib.rs +// version: 2 + +//! Decoder crate for `lending_kamino`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::LendingKaminoDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/Cargo.toml new file mode 100644 index 0000000..346b261 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_lending_marginfi_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_lending_marginfi_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/README.md b/migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/README.md new file mode 100644 index 0000000..6995ab1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_lending_marginfi_v2 + +Ce crate réserve la surface de décodage `lending_marginfi_v2`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `lending_marginfi_v2` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/src/constants.rs new file mode 100644 index 0000000..df77e83 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_lending_marginfi_v2/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_lending_marginfi_v2` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_lending_marginfi_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/src/decoder.rs new file mode 100644 index 0000000..fbd9a74 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_lending_marginfi_v2/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_lending_marginfi_v2` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LendingMarginfiV2Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::LendingMarginfiV2Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_lending_marginfi_v2"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LENDING_MARGINFI_V2_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/src/lib.rs new file mode 100644 index 0000000..5347dee --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lending_marginfi_v2/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_lending_marginfi_v2/src/lib.rs +// version: 2 + +//! Decoder crate for `lending_marginfi_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::LendingMarginfiV2Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/Cargo.toml new file mode 100644 index 0000000..3b23e46 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_lock_raydium_lp/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_lock_raydium_lp" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/README.md b/migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/README.md new file mode 100644 index 0000000..046da6e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_lock_raydium_lp + +Ce crate réserve la surface de décodage `lock_raydium_lp`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `lock_raydium_lp` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/src/constants.rs new file mode 100644 index 0000000..95afc04 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_lock_raydium_lp/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_lock_raydium_lp` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_lock_raydium_lp"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/src/decoder.rs new file mode 100644 index 0000000..112f226 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_lock_raydium_lp/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_lock_raydium_lp` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LockRaydiumLpDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::LockRaydiumLpDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_lock_raydium_lp"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LOCK_RAYDIUM_LP_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/src/lib.rs new file mode 100644 index 0000000..e837e8d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_lock_raydium_lp/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_lock_raydium_lp/src/lib.rs +// version: 2 + +//! Decoder crate for `lock_raydium_lp`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::LockRaydiumLpDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/Cargo.toml new file mode 100644 index 0000000..f621708 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/Cargo.toml @@ -0,0 +1,26 @@ +# file: kb_decoder_metadata_metaplex_token_metadata/Cargo.toml +# version: 4 + +[package] +name = "kb_decoder_metadata_metaplex_token_metadata" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +base64.workspace = true +borsh_0_10.workspace = true +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +kb_store_core = { path = "../kb_store_core" } +mpl-token-metadata.workspace = true +serde_json.workspace = true +sha2.workspace = true +solana-pubkey.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/README.md b/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/README.md new file mode 100644 index 0000000..cb96b4c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/README.md @@ -0,0 +1,178 @@ + + + +# `kb_decoder_metadata_metaplex_token_metadata` + +Décodeur contextualisé dédié au programme Metaplex Token Metadata. + +La crate suit la frontière commune des décodeurs opérationnels du workspace : + +- `constants.rs` contient la surface, les discriminateurs, les bornes et la cible de tracing ; +- `decoder.rs` implémente `ProtocolDecoder` et `InstructionDecoder` ; +- `instruction.rs` contient le décodage Borsh borné, la résolution des comptes et les observations ; +- `lib.rs` réexporte le décodeur d’instructions et les types publics du parseur de comptes ; +- le Program ID provient exclusivement de `kb_program_ids`. + +`0.4.7-pre.002` active le décodage exact de `CreateMetadataAccountV3` et `UpdateMetadataAccountV2`. `0.4.7-pre.003` ajoute `VerifyCollection`, `UnverifyCollection` et `SetAndVerifyCollection`. `0.4.7-pre.004` ajoute le groupe sized `VerifySizedCollectionItem`, `UnverifySizedCollectionItem` et `SetAndVerifySizedCollectionItem`, avec la metadata de collection writable et le `collection_authority_record` optionnel. `0.4.7-pre.005` ajoute l’administration historique/courante des autorités de collection avec `ApproveCollectionAuthority` et `RevokeCollectionAuthority`, y compris le record PDA, les autorités distinctes et les comptes système/rent optionnels. `0.4.7-pre.006` ajoute les usages et autorités d’usage avec `ApproveUseAuthority`, `RevokeUseAuthority` et `Utilize`, leurs compteurs `u64`, le `use_authority_record`, le burner, les comptes Token/ATA et les comptes système/rent. `0.4.7-pre.007` ajoute `SignMetadata` et `UpdatePrimarySaleHappenedViaToken`, avec creator/owner signataires, metadata writable et token account readonly. Les layouts proviennent de `mpl-token-metadata 5.1.1`. Le décodeur préserve outer/CPI, statut de transaction, comptes ordonnés, paramètres Borsh, provenance et hash du wire. Il refuse les payloads tronqués, suffixes, comptes invalides et discriminateurs inconnus. + +Depuis `pre.025`, la crate traite le premier compte on-chain `MetadataV1`. Elle ne traite pas encore les autres comptes Metaplex Token Metadata, les metadata Token-2022 incorporées, Metaplex Core, Bubblegum ou les contenus JSON distants. + +## Groupe `0.4.7-pre.009` + +- `PuffMetadata` (`14`) : normalisation/padding de la metadata externe. +- `RemoveCreatorVerification` (`28`) : retrait explicite de la vérification d’un creator. +- `SetTokenStandard` (`35`) : classification du standard de token, avec edition optionnelle. + +Le décodage conserve les comptes ordonnés, les flags signer/writable officiels, outer/CPI, les échecs non committés et le rejet des suffixes. + +## 0.4.7-pre.009 — création metadata historique V2 + +- `CreateMetadataAccountV2` (discriminant `16`) est décodé comme surface historique. +- Le wire conserve `DataV2` et `is_mutable` avec les mêmes bornes que V3. +- Le contrat de comptes reprend metadata, mint, mint authority, payer, update authority, System Program et rent optionnel. +- Cette instruction est décodable et matérialisable ultérieurement, mais restera non exécutable. + +## 0.4.7-pre.010 + +Ajout du décodage historique non exécutable de `CreateMetadataAccount` (0) et `UpdateMetadataAccount` (1), avec layouts `Data` V1 bornés et comptes exacts. + +## 0.4.7-pre.011 + +Ajout de `CreateMasterEditionV3` (17) et `ConvertMasterEditionV1ToV2` (12). Le premier conserve `max_supply: Option` et les autorités/payer/programmes exacts ; le second est classé historique et conserve les trois comptes writable de conversion. Le mint d’éditions imprimées et les edition markers restent planifiés pour la tranche suivante. + +## 0.4.7-pre.012 + +Ajout de `MintNewEditionFromMasterEditionViaToken` (11) et de la variante historique `MintNewEditionFromMasterEditionViaVaultProxy` (13). Les deux wires conservent l’édition `u64`, le master edition, le nouvel edition account et l’edition marker. La variante Token conserve le propriétaire et le token account du master ; la variante Vault conserve l’autorité, le safety deposit et le Token Vault Program. La variante Vault reste définitivement non exécutable. + +## 0.4.7-pre.013 + +Ajout de `SetCollectionSize` (34) et `BubblegumSetCollectionSize` (36). Les deux wires conservent `size: u64`. La variante directe exige une collection authority writable/signataire ; la variante Bubblegum exige une collection authority readonly/signataire et le `bubblegum_signer` readonly/signataire. Le record d’autorité de collection reste optionnel. Le bridge Bubblegum est décodé avec provenance Metaplex Token Metadata mais reste distinct de la future couverture complète Bubblegum de `0.4.9`. + +## 0.4.7-pre.014 — Delegate / Revoke programmables + +- `Delegate` (44) et `Revoke` (45) décodés avec leurs enums Borsh exactes. +- Contrat positionnel de 14 comptes conservé, y compris les placeholders Metaplex des comptes optionnels. +- `AuthorizationData` est projeté lorsqu’il est présent ; validation sémantique différée au preflight stateful. + +## 0.4.7-pre.015 — lifecycle programmable + +Ajout de `Transfer` (49), `Lock` (46), `Unlock` (47), `Burn` (41) et `CloseAccounts` (57). Les enums Borsh modernes, comptes positionnels, placeholders optionnels, montants `u64`, outer/CPI et transactions échouées non committées sont conservés. La validation stateful des TokenRecord, rule sets, owners et PDA reste différée. + +## `0.4.7-pre.016` — audit exhaustif et freeze/thaw historiques + +- inventaire officiel recalé sur `clients/rust/src/generated/instructions/mod.rs` ; +- ajout de `FreezeDelegatedAccount` (`26`) et `ThawDelegatedAccount` (`27`) ; +- contrat exact de cinq comptes : delegate writable/signataire, token account writable, edition, mint et Token Program readonly ; +- ces deux surfaces restent historiques, decode-only et non exécutables ; +- les wrappers modernes et les opérations escrow/collect/resize/migrate encore absents restent explicitement planifiés avant le décodage des comptes. + +## `0.4.7-pre.017` — burns NFT historiques et printing token + +- `BurnNft` (`29`) conserve metadata, owner signataire, mint, token account, master edition, Token Program et collection metadata optionnelle. +- `BurnEditionNft` (`37`) conserve le print mint, le master mint, leurs token accounts, les comptes edition/master edition et l’edition marker. +- `DeprecatedMintNewEditionFromMasterEditionViaPrintingToken` (`3`) conserve les quinze comptes obligatoires et la reservation list optionnelle. +- Ces trois surfaces sont historiques, decode-only et définitivement non exécutables. + +## `0.4.7-pre.018` — escrow et collecte + +- `CreateEscrowAccount` (`38`) et `CloseEscrowAccount` (`39`) conservent escrow, metadata, mint, token account, edition, payer et comptes système/sysvar exacts. +- `TransferOutOfEscrow` (`40`) conserve `amount: u64`, les comptes source/destination de l’attribut, le mint et le token account liés à l’escrow, ainsi que l’autorité optionnelle. +- `Collect` (`54`) conserve l’autorité signataire et le destinataire readonly. +- Les quatre surfaces préservent outer/CPI et les transactions échouées non committées ; les owners, PDA et états escrow restent à valider lors du décodage stateful. + +## `0.4.7-pre.019` — wrapper moderne Create + +- `Create` (`42`) décode `CreateArgs::V1`, y compris metadata textuelle, royalties, creators, collection, uses, token standard, rule set, decimals et print supply. +- Le contrat de neuf positions conserve metadata, master edition optionnelle, mint signataire/writable, authority, payer, update authority, programmes système/sysvar et SPL Token Program optionnel. +- Les wrappers modernes `Create`, `Mint`, `Print`, `Update`, `Use`, `Verify`, `Unverify`, `Resize` et `Migrate` sont désormais décodés ; leurs validations owner/PDA restent stateful. + +## 0.4.7-pre.020 + +Cette préversion ajoute le wrapper moderne `Mint` (`43`). Le décodeur conserve `MintArgs::V1`, le montant brut `u64`, `AuthorizationData` optionnelle et les quinze positions officielles, y compris les placeholders Metaplex utilisés pour les comptes optionnels absents. L’activation runtime, la validation stateful des PDA/rule sets et l’exécution restent différées. + +## `0.4.7-pre.021` — wrapper moderne Print + +- `Print` (`55`) décode les variantes `PrintArgs::V1` et `PrintArgs::V2`, chacune conservant le numéro d’édition `u64`. +- Le contrat de dix-huit positions conserve les comptes edition metadata, edition, edition mint, token account, Token Record optionnel, master edition, edition marker, payer, propriétaire du master token account et programmes Token/ATA/System/Sysvar. +- `PrintV1` et `PrintV2` sont des builders spécialisés du même discriminant `55` et non deux discriminateurs supplémentaires. +- Les PDA edition/edition marker et les owners restent à valider lors du décodage stateful des comptes. + +## Update moderne + +Le discriminant `50` décode `UpdateArgs` et toutes ses variantes publiées : `V1`, `AsUpdateAuthorityV2`, `AsAuthorityItemDelegateV2`, `AsCollectionDelegateV2`, `AsDataDelegateV2`, `AsProgrammableConfigDelegateV2`, `AsDataItemDelegateV2`, `AsCollectionItemDelegateV2` et `AsProgrammableConfigItemDelegateV2`. Le contrat conserve onze positions, avec placeholders Metaplex readonly pour les comptes optionnels absents. La validation sémantique des PDA, delegates, owners et rule sets reste stateful. + +## Wrapper Use et vérifications modernes + +`Use` (`51`) décode `UseArgs::V1` et ses douze positions. `Verify` (`52`) et `Unverify` (`53`) décodent `VerificationArgs::{CreatorV1,CollectionV1}` avec huit positions et placeholders optionnels explicites. Les validations owner/PDA restent stateful. + +## `0.4.7-pre.024` — inventaire d’instructions exhaustif + +`Resize` (`56`) et `Migrate` (`48`) sont décodés avec leurs contrats positionnels exacts. L’audit de l’IDL `1.14.0` a également identifié puis couvert les six discriminateurs historiques manquants `2`, `5`, `6`, `8`, `9` et `10`. Le registre contient désormais les 58 discriminateurs `0..=57` sans trou. Ces six surfaces historiques restent decode-only et non exécutables. + +## `0.4.7-pre.025` — premier compte on-chain + +La crate expose désormais `decode_metadata_account` pour le layout officiel `Metadata`. Le parseur refuse un owner étranger avant désérialisation, exige `Key::MetadataV1`, borne les données et les champs textuels, limite les creators, valide le PDA canonique `[metadata, program_id, mint]` et conserve le bump. Cette tranche ne couvre pas encore MasterEdition, Edition, EditionMarker, TokenRecord ni les records de délégation. + +## `0.4.7-pre.026` — comptes edition et master edition + +La crate expose `decode_edition_account` pour `DeprecatedMasterEditionV1`, `MasterEditionV2` et `EditionV1`. Le décodeur vérifie l’owner avant parsing, exige le PDA commun `[metadata, program_id, mint, edition]`, conserve le bump et projette supply/max supply, printing mints historiques, parent et numéro d’édition. `MasterEditionV1` reste historique et decode-only ; `MasterEditionV2` et `EditionV1` alimenteront les futures projections stateful. + +## `0.4.7-pre.027` — Edition Marker V1/V2 + +- `EditionMarker` conserve un ledger fixe de 31 octets, soit 248 éditions par groupe. +- Le groupe V1 est `edition / 248` et le PDA utilise les seeds `[metadata, program_id, mint, edition, groupe_decimal]`. +- `EditionMarkerV2` conserve un `Vec` extensible et utilise un PDA unique `[metadata, program_id, mint, edition, marker]`. +- Le décodeur projette l'index d'octet, le masque big-endian et l'état `edition_taken` pour l'édition demandée. +- V1 exige exactement 32 octets ; V2 exige une longueur Borsh exacte et borne le ledger à 1 048 576 octets. +- Owner étranger, mauvais PDA, discriminant inconnu, troncature et suffixe sont refusés avant toute projection stateful. + +## `0.4.7-pre.028` — Token Record programmable + +- `TokenRecord` exige le layout alloué exact de 80 octets et `Key::TokenRecord`. +- Le PDA est dérivé par `[metadata, program_id, mint, token_record, token_account]` et le bump stocké doit être identique au bump canonique. +- La projection conserve `TokenState::{Unlocked,Locked,Listed}`, la révision optionnelle du rule set, le delegate, son rôle et la destination optionnelle de locked transfer. +- Les rôles officiels `Sale`, `Transfer`, `Utility`, `Staking`, `Standard`, `LockedTransfer` et `Migration` sont conservés sans normalisation destructive. +- Owner étranger, mauvais PDA, bump incohérent, mauvais discriminant, troncature et suffixe sont refusés. + +## `0.4.7-pre.029` — records de délégation et d’autorité + +- `MetadataDelegateRecord` et `HolderDelegateRecord` exigent exactement 98 octets, leur discriminant `Key`, leur bump stocké et leur PDA canonique. +- Les huit seeds de rôles Metadata sont conservées sans regroupement : `authority_item_delegate`, `collection_delegate`, `use_delegate`, `data_delegate`, `programmable_config_delegate`, `data_item_delegate`, `collection_item_delegate` et `prog_config_item_delegate`. +- `HolderDelegateRecord` conserve le rôle `print_delegate`, le holder utilisé comme seed, le delegate et l’update authority stockée. +- `CollectionAuthorityRecord` valide `[metadata, program_id, mint, collection_authority, authority]` et conserve l’update authority optionnelle. +- `UseAuthorityRecord` valide `[metadata, program_id, mint, user, authority]`, exige exactement 10 octets et conserve `allowed_uses: u64`. +- Owner étranger, longueur incorrecte, discriminant erroné, PDA ou bump incohérents sont refusés avant projection. + +## `0.4.7-pre.030` — Token Owned Escrow stateful + +- `TokenOwnedEscrow` conserve le base token mint, l’autorité `TokenOwner` ou `Creator(Pubkey)` et le bump stocké. +- Le PDA `TokenOwner` utilise `[metadata, program_id, base_token, 0, escrow]`. +- Le PDA `Creator` utilise `[metadata, program_id, base_token, 1, creator, escrow]`. +- Les longueurs Borsh exactes sont 35 octets pour `TokenOwner` et 67 octets pour `Creator`. +- Owner étranger, discriminant, longueur, PDA ou bump incohérents sont refusés. +- Les effets de `CreateEscrowAccount`, `CloseEscrowAccount`, `TransferOutOfEscrow`, `Migrate` et `Resize` sont décrits dans la matrice pour la future corrélation stateful ; ils ne sont pas encore matérialisés. + +## `0.4.7-pre.031` — Reservation Lists historiques + +- `ReservationListV1` conserve le Master Edition, le supply snapshot optionnel et jusqu’à 200 entrées avec compteurs `u8`. +- `ReservationListV2` conserve les mêmes identités, des compteurs `u64` et les caches `total_reservation_spots`/`current_reservation_spots`. +- Le PDA exige le contexte `resource` et suit `[metadata, program_id, master_edition, reservation, resource]`. +- Les tailles maximales historiques sont 6 949 octets pour V1 et 9 749 octets pour V2 ; seuls les suffixes de padding nuls sont tolérés. +- Ces comptes sont historiques et ne seront jamais exposés par l’exécuteur. Ils restent toutefois matérialisables comme état historique lors des backfills et replays, avec provenance explicite et sans écraser l’état actif. + +## `0.4.7-pre.032` — audit exhaustif des comptes + +- Les quinze variantes `Key` publiées, de `Uninitialized` (`0`) à `HolderDelegate` (`14`), sont inventoriées dans le code et la matrice. +- Les quatorze variantes représentant un compte réel sont reliées à un décodeur borné ; `Uninitialized` est une sentinelle explicitement rejetée. +- Les layouts historiques restent `decode-only`, `EditionMarkerV2` reste classé actif/expérimental, et les comptes actifs restent éligibles aux futures matérialisations. +- Un test compare l’ordre et les discriminateurs Borsh du SDK épinglé à l’inventaire interne et à `docs/METAPLEX_TOKEN_METADATA_MATRIX.json`. +- Toute variante future absente de l’inventaire doit faire échouer l’audit avant toute activation runtime. + +## `0.4.7-pre.033` — modèle canonique et provenance + +- Chaque snapshot de compte est enveloppé dans une identité stable `program_id:account_address`, un nom de layout, son discriminant `Key` exact et une version canonique interne. +- Les layouts sont classés `Active`, `ActiveExperimental` ou `Historical` sans confondre cette classification avec leur politique d’exécution. +- Les états actifs et expérimentaux sont matérialisables comme état courant ; les états historiques sont matérialisables comme état historique lors d’un backfill ou replay. +- Les layouts historiques restent définitivement non exécutables, même lorsqu’ils sont matérialisés. +- La provenance conserve source, slot, write version, signature, index outer/inner et statut committed lorsque ces données sont disponibles. +- Les sources `Live`, `Replay`, `Backfill`, `Repair` et `Synthetic` restent distinctes afin d’éviter toute fusion silencieuse des observations. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/account.rs b/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/account.rs new file mode 100644 index 0000000..a882dbe --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/account.rs @@ -0,0 +1,2595 @@ +// file: kb_decoder_metadata_metaplex_token_metadata/src/account.rs +// version: 10 + +//! Bounded decoding and PDA validation for Metaplex Token Metadata accounts. + +const MAX_METADATA_ACCOUNT_BYTES: usize = 1_024; +const MAX_NAME_BYTES: usize = 32; +const MAX_SYMBOL_BYTES: usize = 10; +const MAX_URI_BYTES: usize = 200; +const MAX_CREATORS: usize = 5; +const EDITION_MARKER_V1_BITS: u64 = 248; +const MAX_EDITION_MARKER_V2_LEDGER_BYTES: usize = 1_048_576; + +/// Bounded error returned while decoding a Metadata account. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum MetadataAccountDecodeError { + /// The account key is not a valid Solana public key. + InvalidAccountKey, + /// The account owner is not a valid Solana public key. + InvalidOwner, + /// The account is owned by another program. + ForeignOwner, + /// The account data is empty or exceeds the explicit bound. + InvalidDataLength, + /// The account payload is not a supported Metadata layout. + InvalidMetadataLayout, + /// One bounded string or vector exceeds the official maximum. + MetadataBoundsExceeded, + /// The supplied account key is not the canonical Metadata PDA for the parsed mint. + InvalidMetadataPda, + /// The parsed account key discriminant is not Metadata. + InvalidMetadataKey, + /// The supplied account key is not the canonical Edition Marker PDA. + InvalidEditionMarkerPda, + /// The supplied account key is not the canonical Token Record PDA. + InvalidTokenRecordPda, + /// The parsed account key discriminant is not Token Record. + InvalidTokenRecordKey, + /// The supplied account key is not the canonical delegate or authority record PDA. + InvalidAuthorityRecordPda, + /// The supplied account key is not the canonical Token Owned Escrow PDA. + InvalidTokenOwnedEscrowPda, + /// The parsed account key discriminant is not Token Owned Escrow. + InvalidTokenOwnedEscrowKey, + /// The parsed account key discriminant is not the expected delegate or authority record. + InvalidAuthorityRecordKey, + /// The supplied account key is not the canonical historical Reservation List PDA. + InvalidReservationListPda, + /// The parsed account key discriminant is not Reservation List V1 or V2. + InvalidReservationListKey, + /// The historical reservation vector or padded payload exceeds official bounds. + ReservationListBoundsExceeded, + /// The Edition Marker ledger exceeds the explicit decoder bound. + EditionMarkerBoundsExceeded, + /// The parsed account could not be projected to bounded JSON. + ProjectionFailed, +} + +impl std::fmt::Display for MetadataAccountDecodeError { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let message = match self { + Self::InvalidAccountKey => "metadata account key is invalid", + Self::InvalidOwner => "metadata account owner is invalid", + Self::ForeignOwner => "metadata account is owned by another program", + Self::InvalidDataLength => "metadata account data length is invalid", + Self::InvalidMetadataLayout => "metadata account layout is invalid", + Self::MetadataBoundsExceeded => "metadata account exceeds bounded fields", + Self::InvalidMetadataPda => "metadata account PDA is invalid", + Self::InvalidMetadataKey => "metadata account key discriminant is invalid", + Self::InvalidEditionMarkerPda => "edition marker account PDA is invalid", + Self::InvalidTokenRecordPda => "token record account PDA is invalid", + Self::InvalidTokenRecordKey => "token record account key discriminant is invalid", + Self::InvalidAuthorityRecordPda => "delegate or authority record PDA is invalid", + Self::InvalidTokenOwnedEscrowPda => "token owned escrow PDA is invalid", + Self::InvalidTokenOwnedEscrowKey => "token owned escrow key discriminant is invalid", + Self::InvalidAuthorityRecordKey => { + "delegate or authority record key discriminant is invalid" + }, + Self::InvalidReservationListPda => "reservation list account PDA is invalid", + Self::InvalidReservationListKey => { + "reservation list account key discriminant is invalid" + }, + Self::ReservationListBoundsExceeded => { + "reservation list account exceeds official bounds" + }, + Self::EditionMarkerBoundsExceeded => "edition marker ledger exceeds bounded fields", + Self::ProjectionFailed => "metadata account JSON projection failed", + }; + return formatter.write_str(message); + } +} + +impl std::error::Error for MetadataAccountDecodeError {} + +/// One audited Metaplex account-key layout. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct MetadataAccountLayoutAuditEntry { + /// Published `Key` variant name. + pub key_name: &'static str, + /// Exact Borsh discriminant byte. + pub discriminant: u8, + /// Decoder entry point or explicit rejection policy. + pub decoder: &'static str, + /// Lifecycle classification used by later materialization and execution layers. + pub lifecycle: &'static str, +} + +/// Exhaustive inventory of every published Metaplex Token Metadata `Key` variant. +pub const METADATA_ACCOUNT_LAYOUT_AUDIT: &[MetadataAccountLayoutAuditEntry] = &[ + MetadataAccountLayoutAuditEntry { + key_name: "Uninitialized", + discriminant: 0, + decoder: "reject_uninitialized", + lifecycle: "sentinel", + }, + MetadataAccountLayoutAuditEntry { + key_name: "EditionV1", + discriminant: 1, + decoder: "decode_edition_account", + lifecycle: "active", + }, + MetadataAccountLayoutAuditEntry { + key_name: "MasterEditionV1", + discriminant: 2, + decoder: "decode_edition_account", + lifecycle: "historical_materializable", + }, + MetadataAccountLayoutAuditEntry { + key_name: "ReservationListV1", + discriminant: 3, + decoder: "decode_reservation_list_account", + lifecycle: "historical_materializable", + }, + MetadataAccountLayoutAuditEntry { + key_name: "MetadataV1", + discriminant: 4, + decoder: "decode_metadata_account", + lifecycle: "active", + }, + MetadataAccountLayoutAuditEntry { + key_name: "ReservationListV2", + discriminant: 5, + decoder: "decode_reservation_list_account", + lifecycle: "historical_materializable", + }, + MetadataAccountLayoutAuditEntry { + key_name: "MasterEditionV2", + discriminant: 6, + decoder: "decode_edition_account", + lifecycle: "active", + }, + MetadataAccountLayoutAuditEntry { + key_name: "EditionMarker", + discriminant: 7, + decoder: "decode_edition_marker_account", + lifecycle: "active", + }, + MetadataAccountLayoutAuditEntry { + key_name: "UseAuthorityRecord", + discriminant: 8, + decoder: "decode_use_authority_record_account", + lifecycle: "active", + }, + MetadataAccountLayoutAuditEntry { + key_name: "CollectionAuthorityRecord", + discriminant: 9, + decoder: "decode_collection_authority_record_account", + lifecycle: "active", + }, + MetadataAccountLayoutAuditEntry { + key_name: "TokenOwnedEscrow", + discriminant: 10, + decoder: "decode_token_owned_escrow_account", + lifecycle: "active", + }, + MetadataAccountLayoutAuditEntry { + key_name: "TokenRecord", + discriminant: 11, + decoder: "decode_token_record_account", + lifecycle: "active", + }, + MetadataAccountLayoutAuditEntry { + key_name: "MetadataDelegate", + discriminant: 12, + decoder: "decode_metadata_delegate_record_account", + lifecycle: "active", + }, + MetadataAccountLayoutAuditEntry { + key_name: "EditionMarkerV2", + discriminant: 13, + decoder: "decode_edition_marker_account", + lifecycle: "active_experimental", + }, + MetadataAccountLayoutAuditEntry { + key_name: "HolderDelegate", + discriminant: 14, + decoder: "decode_holder_delegate_record_account", + lifecycle: "active", + }, +]; + +/// Canonical bounded projection of one Metadata account. +#[derive(Clone, Debug, PartialEq)] +pub struct MetadataAccountSnapshot { + /// Canonical account address. + pub account: String, + /// Canonical Metadata PDA bump. + pub bump: u8, + /// Mint carried by the account. + pub mint: String, + /// Current update authority. + pub update_authority: String, + /// Trimmed metadata name. + pub name: String, + /// Trimmed metadata symbol. + pub symbol: String, + /// Trimmed metadata URI. + pub uri: String, + /// Seller fee in basis points. + pub seller_fee_basis_points: u16, + /// Whether primary sale happened. + pub primary_sale_happened: bool, + /// Whether the metadata is mutable. + pub is_mutable: bool, + /// Complete bounded account projection. + pub payload_json: serde_json::Value, +} + +/// Decodes one Metadata account after validating owner, bounds and canonical PDA. +pub fn decode_metadata_account( + account: &str, + owner: &str, + data: &[u8], +) -> std::result::Result { + let account_key = match ::from_str(account) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAccountKey); + }, + }; + let owner_key = match ::from_str(owner) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidOwner); + }, + }; + let expected_owner = match ::from_str( + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidOwner); + }, + }; + if owner_key != expected_owner { + return std::result::Result::Err(MetadataAccountDecodeError::ForeignOwner); + } + if data.is_empty() || data.len() > MAX_METADATA_ACCOUNT_BYTES { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidDataLength); + } + let metadata = match mpl_token_metadata::accounts::Metadata::from_bytes(data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + if metadata.key != mpl_token_metadata::types::Key::MetadataV1 { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataKey); + } + let name = metadata.name.trim_end_matches('\0').to_string(); + let symbol = metadata.symbol.trim_end_matches('\0').to_string(); + let uri = metadata.uri.trim_end_matches('\0').to_string(); + if name.len() > MAX_NAME_BYTES || symbol.len() > MAX_SYMBOL_BYTES || uri.len() > MAX_URI_BYTES { + return std::result::Result::Err(MetadataAccountDecodeError::MetadataBoundsExceeded); + } + if metadata + .creators + .as_ref() + .is_some_and(|creators| return creators.len() > MAX_CREATORS) + { + return std::result::Result::Err(MetadataAccountDecodeError::MetadataBoundsExceeded); + } + let (expected_pda, bump) = mpl_token_metadata::accounts::Metadata::find_pda(&metadata.mint); + if account_key.to_string() != expected_pda.to_string() { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataPda); + } + let payload_json = match serde_json::to_value(&metadata) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::ProjectionFailed); + }, + }; + return std::result::Result::Ok(MetadataAccountSnapshot { + account: account_key.to_string(), + bump, + mint: metadata.mint.to_string(), + update_authority: metadata.update_authority.to_string(), + name, + symbol, + uri, + seller_fee_basis_points: metadata.seller_fee_basis_points, + primary_sale_happened: metadata.primary_sale_happened, + is_mutable: metadata.is_mutable, + payload_json, + }); +} + +/// Supported edition account layout. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum EditionAccountKind { + /// Historical Master Edition V1 layout. + MasterEditionV1, + /// Current Master Edition V2 layout. + MasterEditionV2, + /// Printed Edition V1 layout. + EditionV1, +} + +/// Canonical bounded projection of one edition or master-edition account. +#[derive(Clone, Debug, PartialEq)] +pub struct EditionAccountSnapshot { + /// Canonical account address. + pub account: String, + /// Canonical edition PDA bump. + pub bump: u8, + /// Mint used to derive the edition PDA. + pub mint: String, + /// Decoded account layout. + pub kind: EditionAccountKind, + /// Current printed supply for a master edition. + pub supply: std::option::Option, + /// Optional maximum supply for a master edition. + pub max_supply: std::option::Option, + /// Parent master edition for a printed edition. + pub parent: std::option::Option, + /// Printed edition number. + pub edition: std::option::Option, + /// Historical printing mint carried by Master Edition V1. + pub printing_mint: std::option::Option, + /// Historical one-time printing authorization mint carried by Master Edition V1. + pub one_time_printing_authorization_mint: std::option::Option, + /// Complete bounded account projection. + pub payload_json: serde_json::Value, +} + +/// Decodes one MasterEditionV1, MasterEditionV2 or Edition account with exact owner and PDA validation. +pub fn decode_edition_account( + account: &str, + owner: &str, + mint: &str, + data: &[u8], +) -> std::result::Result { + let account_key = match ::from_str(account) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAccountKey); + }, + }; + let owner_key = match ::from_str(owner) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidOwner); + }, + }; + let expected_owner = match ::from_str( + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidOwner); + }, + }; + if owner_key != expected_owner { + return std::result::Result::Err(MetadataAccountDecodeError::ForeignOwner); + } + if data.is_empty() || data.len() > 128 { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidDataLength); + } + let (expected_pda, bump) = match mint.parse() { + std::result::Result::Ok(mint_key) => { + mpl_token_metadata::accounts::MasterEdition::find_pda(&mint_key) + }, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAccountKey); + }, + }; + if account_key.to_string() != expected_pda.to_string() { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataPda); + } + let key = data[0]; + if key == mpl_token_metadata::types::Key::MasterEditionV1 as u8 { + let value = match mpl_token_metadata::accounts::DeprecatedMasterEditionV1::from_bytes(data) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + let payload_json = match serde_json::to_value(&value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::ProjectionFailed); + }, + }; + return std::result::Result::Ok(EditionAccountSnapshot { + account: account_key.to_string(), + bump, + mint: mint.to_string(), + kind: EditionAccountKind::MasterEditionV1, + supply: std::option::Option::Some(value.supply), + max_supply: value.max_supply, + parent: std::option::Option::None, + edition: std::option::Option::None, + printing_mint: std::option::Option::Some(value.printing_mint.to_string()), + one_time_printing_authorization_mint: std::option::Option::Some( + value.one_time_printing_authorization_mint.to_string(), + ), + payload_json, + }); + } + if key == mpl_token_metadata::types::Key::MasterEditionV2 as u8 { + let value = match mpl_token_metadata::accounts::MasterEdition::from_bytes(data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + let payload_json = match serde_json::to_value(&value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::ProjectionFailed); + }, + }; + return std::result::Result::Ok(EditionAccountSnapshot { + account: account_key.to_string(), + bump, + mint: mint.to_string(), + kind: EditionAccountKind::MasterEditionV2, + supply: std::option::Option::Some(value.supply), + max_supply: value.max_supply, + parent: std::option::Option::None, + edition: std::option::Option::None, + printing_mint: std::option::Option::None, + one_time_printing_authorization_mint: std::option::Option::None, + payload_json, + }); + } + if key == mpl_token_metadata::types::Key::EditionV1 as u8 { + if data.len() != mpl_token_metadata::accounts::Edition::LEN { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidDataLength); + } + let value = match mpl_token_metadata::accounts::Edition::from_bytes(data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + let payload_json = match serde_json::to_value(&value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::ProjectionFailed); + }, + }; + return std::result::Result::Ok(EditionAccountSnapshot { + account: account_key.to_string(), + bump, + mint: mint.to_string(), + kind: EditionAccountKind::EditionV1, + supply: std::option::Option::None, + max_supply: std::option::Option::None, + parent: std::option::Option::Some(value.parent.to_string()), + edition: std::option::Option::Some(value.edition), + printing_mint: std::option::Option::None, + one_time_printing_authorization_mint: std::option::Option::None, + payload_json, + }); + } + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataKey); +} + +/// Supported Edition Marker account layout. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum EditionMarkerAccountKind { + /// Fixed 248-edition historical/current marker layout. + EditionMarkerV1, + /// Extensible marker layout with one PDA per mint. + EditionMarkerV2, +} + +/// Canonical bounded projection of one Edition Marker account. +#[derive(Clone, Debug, PartialEq)] +pub struct EditionMarkerAccountSnapshot { + /// Canonical account address. + pub account: String, + /// Canonical PDA bump. + pub bump: u8, + /// Mint used to derive the marker PDA. + pub mint: String, + /// Decoded marker layout. + pub kind: EditionMarkerAccountKind, + /// V1 group number (`edition / 248`), absent for V2. + pub marker_group: std::option::Option, + /// Requested edition used to inspect the bitmap. + pub edition: u64, + /// Byte index inside the ledger. + pub byte_index: usize, + /// Big-endian bit mask inside the selected byte. + pub bit_mask: u8, + /// Whether the requested edition is already marked. + pub edition_taken: bool, + /// Exact bounded ledger bytes. + pub ledger: std::vec::Vec, + /// Complete bounded account projection. + pub payload_json: serde_json::Value, +} + +/// Decodes EditionMarker or EditionMarkerV2 with exact owner, bounds, bitmap and PDA validation. +pub fn decode_edition_marker_account( + account: &str, + owner: &str, + mint: &str, + edition: u64, + data: &[u8], +) -> std::result::Result { + let account_key = match ::from_str(account) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAccountKey); + }, + }; + let owner_key = match ::from_str(owner) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidOwner); + }, + }; + let program_key = match ::from_str( + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidOwner); + }, + }; + if owner_key != program_key { + return std::result::Result::Err(MetadataAccountDecodeError::ForeignOwner); + } + let mint_key = match ::from_str(mint) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAccountKey); + }, + }; + if data.is_empty() { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidDataLength); + } + if data[0] == mpl_token_metadata::types::Key::EditionMarker as u8 { + if data.len() != mpl_token_metadata::accounts::EditionMarker::LEN { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidDataLength); + } + let marker_group = edition / EDITION_MARKER_V1_BITS; + let marker_seed = marker_group.to_string(); + let (expected_pda, bump) = solana_pubkey::Pubkey::find_program_address( + &[ + b"metadata", + program_key.as_ref(), + mint_key.as_ref(), + b"edition", + marker_seed.as_bytes(), + ], + &program_key, + ); + if account_key != expected_pda { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidEditionMarkerPda); + } + let value = match mpl_token_metadata::accounts::EditionMarker::from_bytes(data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + let offset = (edition % EDITION_MARKER_V1_BITS) as usize; + let byte_index = offset / 8; + let bit_mask = 1u8 << (7 - (offset % 8)); + let payload_json = match serde_json::to_value(&value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::ProjectionFailed); + }, + }; + return std::result::Result::Ok(EditionMarkerAccountSnapshot { + account: account_key.to_string(), + bump, + mint: mint_key.to_string(), + kind: EditionMarkerAccountKind::EditionMarkerV1, + marker_group: std::option::Option::Some(marker_group), + edition, + byte_index, + bit_mask, + edition_taken: value.ledger[byte_index] & bit_mask != 0, + ledger: value.ledger.to_vec(), + payload_json, + }); + } + if data[0] == mpl_token_metadata::types::Key::EditionMarkerV2 as u8 { + if data.len() < 5 || data.len() > MAX_EDITION_MARKER_V2_LEDGER_BYTES + 5 { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidDataLength); + } + let (expected_pda, bump) = solana_pubkey::Pubkey::find_program_address( + &[b"metadata", program_key.as_ref(), mint_key.as_ref(), b"edition", b"marker"], + &program_key, + ); + if account_key != expected_pda { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidEditionMarkerPda); + } + let value = match mpl_token_metadata::accounts::EditionMarkerV2::from_bytes(data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + if value.ledger.len() > MAX_EDITION_MARKER_V2_LEDGER_BYTES { + return std::result::Result::Err( + MetadataAccountDecodeError::EditionMarkerBoundsExceeded, + ); + } + if data.len() != value.ledger.len() + 5 { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidDataLength); + } + let byte_index_u64 = edition / 8; + let byte_index = match usize::try_from(byte_index_u64) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err( + MetadataAccountDecodeError::EditionMarkerBoundsExceeded, + ); + }, + }; + let bit_mask = 1u8 << (7 - (edition % 8) as usize); + let edition_taken = + value.ledger.get(byte_index).is_some_and(|entry| return entry & bit_mask != 0); + let payload_json = match serde_json::to_value(&value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::ProjectionFailed); + }, + }; + return std::result::Result::Ok(EditionMarkerAccountSnapshot { + account: account_key.to_string(), + bump, + mint: mint_key.to_string(), + kind: EditionMarkerAccountKind::EditionMarkerV2, + marker_group: std::option::Option::None, + edition, + byte_index, + bit_mask, + edition_taken, + ledger: value.ledger, + payload_json, + }); + } + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataKey); +} + +/// Canonical bounded projection of one programmable Token Record account. +#[derive(Clone, Debug, PartialEq)] +pub struct TokenRecordAccountSnapshot { + /// Canonical Token Record account address. + pub account: String, + /// Canonical Token Record PDA bump computed from mint and token account. + pub bump: u8, + /// Mint used to derive the Token Record PDA. + pub mint: String, + /// Token account used to derive the Token Record PDA. + pub token: String, + /// Bump stored by the on-chain Token Record layout. + pub stored_bump: u8, + /// Current programmable token state. + pub state: String, + /// Optional Token Authorization Rules revision. + pub rule_set_revision: std::option::Option, + /// Optional active delegate. + pub delegate: std::option::Option, + /// Optional active delegate role. + pub delegate_role: std::option::Option, + /// Optional locked-transfer destination. + pub locked_transfer: std::option::Option, + /// Whether the token is currently locked. + pub locked: bool, + /// Complete bounded account projection. + pub payload_json: serde_json::Value, +} + +/// Decodes one Token Record account after exact owner, length, discriminant and PDA validation. +pub fn decode_token_record_account( + account: &str, + owner: &str, + mint: &str, + token: &str, + data: &[u8], +) -> std::result::Result { + let account_key = match ::from_str(account) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAccountKey); + }, + }; + let owner_key = match ::from_str(owner) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidOwner); + }, + }; + let expected_owner = match ::from_str( + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidOwner); + }, + }; + if owner_key != expected_owner { + return std::result::Result::Err(MetadataAccountDecodeError::ForeignOwner); + } + if data.len() != mpl_token_metadata::accounts::TokenRecord::LEN { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidDataLength); + } + let mint_key = match ::from_str(mint) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAccountKey); + }, + }; + let token_key = match ::from_str(token) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAccountKey); + }, + }; + let (expected_pda, bump) = solana_pubkey::Pubkey::find_program_address( + &[ + b"metadata", + expected_owner.as_ref(), + mint_key.as_ref(), + b"token_record", + token_key.as_ref(), + ], + &expected_owner, + ); + if account_key != expected_pda { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidTokenRecordPda); + } + let value = match mpl_token_metadata::accounts::TokenRecord::from_bytes(data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + if value.key != mpl_token_metadata::types::Key::TokenRecord { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidTokenRecordKey); + } + if value.bump != bump { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidTokenRecordPda); + } + let payload_json = match serde_json::to_value(&value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::ProjectionFailed); + }, + }; + let state = format!("{:?}", value.state); + let delegate_role = value.delegate_role.as_ref().map(|role| return format!("{role:?}")); + let locked = value.state == mpl_token_metadata::types::TokenState::Locked; + return std::result::Result::Ok(TokenRecordAccountSnapshot { + account: account_key.to_string(), + bump, + mint: mint_key.to_string(), + token: token_key.to_string(), + stored_bump: value.bump, + state, + rule_set_revision: value.rule_set_revision, + delegate: value.delegate.map(|delegate| return delegate.to_string()), + delegate_role, + locked_transfer: value + .locked_transfer + .map(|locked_transfer| return locked_transfer.to_string()), + locked, + payload_json, + }); +} + +/// Canonical projection shared by Metadata and Holder delegate records. +#[derive(Clone, Debug, PartialEq)] +pub struct DelegateRecordAccountSnapshot { + /// Canonical delegate-record account address. + pub account: String, + /// Canonical PDA bump. + pub bump: u8, + /// Delegate-record family. + pub kind: String, + /// Mint carried by the record. + pub mint: String, + /// Role seed used by the PDA. + pub role: String, + /// Authority or holder used by the PDA. + pub authority: String, + /// Delegate carried by the record. + pub delegate: String, + /// Update authority stored by the record. + pub update_authority: String, + /// Bump stored by the account. + pub stored_bump: u8, + /// Complete bounded account projection. + pub payload_json: serde_json::Value, +} + +fn parse_record_boundary( + account: &str, + owner: &str, +) -> std::result::Result<(solana_pubkey::Pubkey, solana_pubkey::Pubkey), MetadataAccountDecodeError> +{ + let account_key = match ::from_str(account) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAccountKey); + }, + }; + let owner_key = match ::from_str(owner) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidOwner); + }, + }; + let expected_owner = match ::from_str( + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidOwner); + }, + }; + if owner_key != expected_owner { + return std::result::Result::Err(MetadataAccountDecodeError::ForeignOwner); + } + return std::result::Result::Ok((account_key, expected_owner)); +} + +/// Decodes a Metadata Delegate Record with exact role, authority, delegate and bump validation. +pub fn decode_metadata_delegate_record_account( + account: &str, + owner: &str, + role: mpl_token_metadata::types::MetadataDelegateRole, + data: &[u8], +) -> std::result::Result { + let (account_key, program) = match parse_record_boundary(account, owner) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if data.len() != mpl_token_metadata::accounts::MetadataDelegateRecord::LEN { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidDataLength); + } + let value = match mpl_token_metadata::accounts::MetadataDelegateRecord::from_bytes(data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + if value.key != mpl_token_metadata::types::Key::MetadataDelegate { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAuthorityRecordKey); + } + let role_seed = role.to_string(); + let mint = match ::from_str(&value.mint.to_string()) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + let authority = match ::from_str( + &value.update_authority.to_string(), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + let delegate = + match ::from_str(&value.delegate.to_string()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + let (expected_pda, bump) = solana_pubkey::Pubkey::find_program_address( + &[ + b"metadata", + program.as_ref(), + mint.as_ref(), + role_seed.as_bytes(), + authority.as_ref(), + delegate.as_ref(), + ], + &program, + ); + if account_key != expected_pda || value.bump != bump { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAuthorityRecordPda); + } + let payload_json = match serde_json::to_value(&value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::ProjectionFailed); + }, + }; + return std::result::Result::Ok(DelegateRecordAccountSnapshot { + account: account_key.to_string(), + bump, + kind: "MetadataDelegateRecord".to_string(), + mint: mint.to_string(), + role: role_seed, + authority: authority.to_string(), + delegate: delegate.to_string(), + update_authority: value.update_authority.to_string(), + stored_bump: value.bump, + payload_json, + }); +} + +/// Decodes a Holder Delegate Record with exact role, holder, delegate and bump validation. +pub fn decode_holder_delegate_record_account( + account: &str, + owner: &str, + holder: &str, + role: mpl_token_metadata::types::HolderDelegateRole, + data: &[u8], +) -> std::result::Result { + let (account_key, program) = match parse_record_boundary(account, owner) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if data.len() != mpl_token_metadata::accounts::HolderDelegateRecord::LEN { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidDataLength); + } + let value = match mpl_token_metadata::accounts::HolderDelegateRecord::from_bytes(data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + if value.key != mpl_token_metadata::types::Key::HolderDelegate { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAuthorityRecordKey); + } + let holder_key = match ::from_str(holder) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAccountKey); + }, + }; + let role_seed = role.to_string(); + let mint = match ::from_str(&value.mint.to_string()) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + let delegate = + match ::from_str(&value.delegate.to_string()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + let (expected_pda, bump) = solana_pubkey::Pubkey::find_program_address( + &[ + b"metadata", + program.as_ref(), + mint.as_ref(), + role_seed.as_bytes(), + holder_key.as_ref(), + delegate.as_ref(), + ], + &program, + ); + if account_key != expected_pda || value.bump != bump { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAuthorityRecordPda); + } + let payload_json = match serde_json::to_value(&value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::ProjectionFailed); + }, + }; + return std::result::Result::Ok(DelegateRecordAccountSnapshot { + account: account_key.to_string(), + bump, + kind: "HolderDelegateRecord".to_string(), + mint: mint.to_string(), + role: role_seed, + authority: holder_key.to_string(), + delegate: delegate.to_string(), + update_authority: value.update_authority.to_string(), + stored_bump: value.bump, + payload_json, + }); +} + +/// Canonical projection of Collection Authority and Use Authority records. +#[derive(Clone, Debug, PartialEq)] +pub struct AuthorityRecordAccountSnapshot { + /// Canonical record account address. + pub account: String, + /// Canonical PDA bump. + pub bump: u8, + /// Record family. + pub kind: String, + /// Mint used by the PDA. + pub mint: String, + /// Authority used by the PDA. + pub authority: String, + /// Optional update authority stored by Collection Authority Record. + pub update_authority: std::option::Option, + /// Optional remaining use allowance stored by Use Authority Record. + pub allowed_uses: std::option::Option, + /// Bump stored by the account. + pub stored_bump: u8, + /// Complete bounded account projection. + pub payload_json: serde_json::Value, +} + +/// Decodes a Collection Authority Record with exact PDA and bump validation. +pub fn decode_collection_authority_record_account( + account: &str, + owner: &str, + mint: &str, + authority: &str, + data: &[u8], +) -> std::result::Result { + let (account_key, program) = match parse_record_boundary(account, owner) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if data.len() != 3 && data.len() != 35 { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidDataLength); + } + let mint_key = match ::from_str(mint) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAccountKey); + }, + }; + let authority_key = match ::from_str(authority) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAccountKey); + }, + }; + let (expected_pda, bump) = solana_pubkey::Pubkey::find_program_address( + &[ + b"metadata", + program.as_ref(), + mint_key.as_ref(), + b"collection_authority", + authority_key.as_ref(), + ], + &program, + ); + if account_key != expected_pda { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAuthorityRecordPda); + } + let value = match mpl_token_metadata::accounts::CollectionAuthorityRecord::from_bytes(data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + if value.key != mpl_token_metadata::types::Key::CollectionAuthorityRecord { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAuthorityRecordKey); + } + if value.bump != bump { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAuthorityRecordPda); + } + let payload_json = match serde_json::to_value(&value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::ProjectionFailed); + }, + }; + return std::result::Result::Ok(AuthorityRecordAccountSnapshot { + account: account_key.to_string(), + bump, + kind: "CollectionAuthorityRecord".to_string(), + mint: mint_key.to_string(), + authority: authority_key.to_string(), + update_authority: value.update_authority.map(|key| return key.to_string()), + allowed_uses: std::option::Option::None, + stored_bump: value.bump, + payload_json, + }); +} + +/// Decodes a Use Authority Record with exact length, PDA and bump validation. +pub fn decode_use_authority_record_account( + account: &str, + owner: &str, + mint: &str, + authority: &str, + data: &[u8], +) -> std::result::Result { + let (account_key, program) = match parse_record_boundary(account, owner) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if data.len() != mpl_token_metadata::accounts::UseAuthorityRecord::LEN { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidDataLength); + } + let mint_key = match ::from_str(mint) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAccountKey); + }, + }; + let authority_key = match ::from_str(authority) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAccountKey); + }, + }; + let (expected_pda, bump) = solana_pubkey::Pubkey::find_program_address( + &[ + b"metadata", + program.as_ref(), + mint_key.as_ref(), + b"user", + authority_key.as_ref(), + ], + &program, + ); + if account_key != expected_pda { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAuthorityRecordPda); + } + let value = match mpl_token_metadata::accounts::UseAuthorityRecord::from_bytes(data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + if value.key != mpl_token_metadata::types::Key::UseAuthorityRecord { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAuthorityRecordKey); + } + if value.bump != bump { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAuthorityRecordPda); + } + let payload_json = match serde_json::to_value(&value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::ProjectionFailed); + }, + }; + return std::result::Result::Ok(AuthorityRecordAccountSnapshot { + account: account_key.to_string(), + bump, + kind: "UseAuthorityRecord".to_string(), + mint: mint_key.to_string(), + authority: authority_key.to_string(), + update_authority: std::option::Option::None, + allowed_uses: std::option::Option::Some(value.allowed_uses), + stored_bump: value.bump, + payload_json, + }); +} + +/// Authority form used by one Token Owned Escrow account. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum TokenOwnedEscrowAuthorityKind { + /// Escrow controlled by the owner of the base token. + TokenOwner, + /// Escrow controlled by an explicit creator key. + Creator, +} + +/// Canonical bounded projection of one Token Owned Escrow account. +#[derive(Clone, Debug, PartialEq)] +pub struct TokenOwnedEscrowAccountSnapshot { + /// Canonical escrow account address. + pub account: String, + /// Canonical PDA bump. + pub bump: u8, + /// Base token mint stored by the account. + pub base_token: String, + /// Authority form encoded by the account. + pub authority_kind: TokenOwnedEscrowAuthorityKind, + /// Optional creator authority. + pub creator: std::option::Option, + /// Bump stored by the account. + pub stored_bump: u8, + /// Complete bounded account projection. + pub payload_json: serde_json::Value, +} + +/// Decodes one Token Owned Escrow account with exact owner, layout, authority seeds and bump validation. +pub fn decode_token_owned_escrow_account( + account: &str, + owner: &str, + data: &[u8], +) -> std::result::Result { + let (account_key, program) = match parse_record_boundary(account, owner) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if data.len() != 35 && data.len() != 67 { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidDataLength); + } + let value = match mpl_token_metadata::accounts::TokenOwnedEscrow::from_bytes(data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + if value.key != mpl_token_metadata::types::Key::TokenOwnedEscrow { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidTokenOwnedEscrowKey); + } + let base_token = + match ::from_str(&value.base_token.to_string()) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAccountKey); + }, + }; + let (authority_kind, creator, expected_pda, bump) = match &value.authority { + mpl_token_metadata::types::EscrowAuthority::TokenOwner => { + let (expected_pda, bump) = solana_pubkey::Pubkey::find_program_address( + &[b"metadata", program.as_ref(), base_token.as_ref(), &[0], b"escrow"], + &program, + ); + ( + TokenOwnedEscrowAuthorityKind::TokenOwner, + std::option::Option::None, + expected_pda, + bump, + ) + }, + mpl_token_metadata::types::EscrowAuthority::Creator(creator_key) => { + let creator_text = creator_key.to_string(); + let creator_key = + match ::from_str(&creator_text) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err( + MetadataAccountDecodeError::InvalidAccountKey, + ); + }, + }; + let (expected_pda, bump) = solana_pubkey::Pubkey::find_program_address( + &[ + b"metadata", + program.as_ref(), + base_token.as_ref(), + &[1], + creator_key.as_ref(), + b"escrow", + ], + &program, + ); + ( + TokenOwnedEscrowAuthorityKind::Creator, + std::option::Option::Some(creator_text), + expected_pda, + bump, + ) + }, + }; + if account_key != expected_pda || value.bump != bump { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidTokenOwnedEscrowPda); + } + let payload_json = match serde_json::to_value(&value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::ProjectionFailed); + }, + }; + return std::result::Result::Ok(TokenOwnedEscrowAccountSnapshot { + account: account_key.to_string(), + bump, + base_token: value.base_token.to_string(), + authority_kind, + creator, + stored_bump: value.bump, + payload_json, + }); +} + +/// Historical Reservation List account generation. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ReservationListAccountKind { + /// Legacy layout with `u8` spot counters. + V1, + /// Later historical layout with `u64` spot counters and cached totals. + V2, +} + +/// One bounded historical reservation entry. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ReservationEntrySnapshot { + /// Reserved address. + pub address: String, + /// Spots still available for the address. + pub spots_remaining: u64, + /// Total spots assigned to the address. + pub total_spots: u64, +} + +/// Canonical decode-only projection of a historical Reservation List account. +#[derive(Clone, Debug, PartialEq)] +pub struct ReservationListAccountSnapshot { + /// Canonical account address. + pub account: String, + /// Canonical Reservation List PDA bump. + pub bump: u8, + /// Historical account generation. + pub kind: ReservationListAccountKind, + /// Master Edition stored by the account. + pub master_edition: String, + /// Resource key used by the PDA derivation. + pub resource: String, + /// Master Edition supply captured when reservations were created. + pub supply_snapshot: std::option::Option, + /// Bounded reservation entries. + pub reservations: std::vec::Vec, + /// Total reservation spots. V1 derives this value from entries. + pub total_reservation_spots: u64, + /// Current reservation spots. V1 derives this value from entries. + pub current_reservation_spots: u64, + /// Complete bounded JSON projection. + pub payload_json: serde_json::Value, +} + +fn read_reservation_u64(data: &[u8], offset: &mut usize) -> std::option::Option { + let end = match offset.checked_add(8) { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::option::Option::None, + }; + let bytes = match data.get(*offset..end) { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::option::Option::None, + }; + let array = match <[u8; 8]>::try_from(bytes) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::option::Option::None, + }; + *offset = end; + return std::option::Option::Some(u64::from_le_bytes(array)); +} + +fn read_reservation_pubkey( + data: &[u8], + offset: &mut usize, +) -> std::option::Option { + let end = match offset.checked_add(32) { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::option::Option::None, + }; + let bytes = match data.get(*offset..end) { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::option::Option::None, + }; + let array = match <[u8; 32]>::try_from(bytes) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::option::Option::None, + }; + *offset = end; + return std::option::Option::Some(solana_pubkey::Pubkey::new_from_array(array)); +} + +/// Decodes a historical Reservation List V1 or V2 account as decode-only state. +pub fn decode_reservation_list_account( + account: &str, + owner: &str, + resource: &str, + data: &[u8], +) -> std::result::Result { + const MAX_RESERVATIONS: usize = 200; + const MAX_V1_BYTES: usize = 6_949; + const MAX_V2_BYTES: usize = 9_749; + let account_key = match ::from_str(account) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAccountKey); + }, + }; + let owner_key = match ::from_str(owner) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidOwner); + }, + }; + let resource_key = match ::from_str(resource) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidAccountKey); + }, + }; + let program_key = match ::from_str( + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidOwner); + }, + }; + if owner_key != program_key { + return std::result::Result::Err(MetadataAccountDecodeError::ForeignOwner); + } + let key = match data.first() { + std::option::Option::Some(value) => *value, + std::option::Option::None => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidDataLength); + }, + }; + let (kind, maximum, entry_bytes) = match key { + 3 => (ReservationListAccountKind::V1, MAX_V1_BYTES, 34usize), + 5 => (ReservationListAccountKind::V2, MAX_V2_BYTES, 48usize), + _ => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidReservationListKey); + }, + }; + if data.len() > maximum { + return std::result::Result::Err(MetadataAccountDecodeError::ReservationListBoundsExceeded); + } + let mut offset = 1usize; + let master_edition = match read_reservation_pubkey(data, &mut offset) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + let option_tag = match data.get(offset) { + std::option::Option::Some(value) => *value, + std::option::Option::None => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + offset += 1; + let supply_snapshot = match option_tag { + 0 => std::option::Option::None, + 1 => match read_reservation_u64(data, &mut offset) { + std::option::Option::Some(value) => std::option::Option::Some(value), + std::option::Option::None => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }, + _ => return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout), + }; + let count_end = match offset.checked_add(4) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + let count_bytes = match data.get(offset..count_end) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + let count_array = match <[u8; 4]>::try_from(count_bytes) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + let count = u32::from_le_bytes(count_array) as usize; + offset = count_end; + if count > MAX_RESERVATIONS { + return std::result::Result::Err(MetadataAccountDecodeError::ReservationListBoundsExceeded); + } + let required_entries = match count.checked_mul(entry_bytes) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + MetadataAccountDecodeError::ReservationListBoundsExceeded, + ); + }, + }; + if data.len().saturating_sub(offset) < required_entries { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + } + let mut reservations = std::vec::Vec::with_capacity(count); + for _ in 0..count { + let address = match read_reservation_pubkey(data, &mut offset) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + }, + }; + let (spots_remaining, total_spots) = match kind { + ReservationListAccountKind::V1 => { + let remaining = match data.get(offset) { + std::option::Option::Some(value) => *value as u64, + std::option::Option::None => { + return std::result::Result::Err( + MetadataAccountDecodeError::InvalidMetadataLayout, + ); + }, + }; + let total = match data.get(offset + 1) { + std::option::Option::Some(value) => *value as u64, + std::option::Option::None => { + return std::result::Result::Err( + MetadataAccountDecodeError::InvalidMetadataLayout, + ); + }, + }; + offset += 2; + (remaining, total) + }, + ReservationListAccountKind::V2 => { + let remaining = match read_reservation_u64(data, &mut offset) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + MetadataAccountDecodeError::InvalidMetadataLayout, + ); + }, + }; + let total = match read_reservation_u64(data, &mut offset) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + MetadataAccountDecodeError::InvalidMetadataLayout, + ); + }, + }; + (remaining, total) + }, + }; + reservations.push(ReservationEntrySnapshot { + address: address.to_string(), + spots_remaining, + total_spots, + }); + } + let derived_total = reservations + .iter() + .fold(0u64, |sum, entry| return sum.saturating_add(entry.total_spots)); + let (total_reservation_spots, current_reservation_spots) = match kind { + ReservationListAccountKind::V1 => (derived_total, derived_total), + ReservationListAccountKind::V2 => { + let total = match read_reservation_u64(data, &mut offset) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + MetadataAccountDecodeError::InvalidMetadataLayout, + ); + }, + }; + let current = match read_reservation_u64(data, &mut offset) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + MetadataAccountDecodeError::InvalidMetadataLayout, + ); + }, + }; + (total, current) + }, + }; + if data + .get(offset..) + .is_some_and(|suffix| return suffix.iter().any(|byte| return *byte != 0)) + { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidMetadataLayout); + } + let (expected_pda, bump) = solana_pubkey::Pubkey::find_program_address( + &[ + b"metadata", + program_key.as_ref(), + master_edition.as_ref(), + b"reservation", + resource_key.as_ref(), + ], + &program_key, + ); + if account_key != expected_pda { + return std::result::Result::Err(MetadataAccountDecodeError::InvalidReservationListPda); + } + let payload_json = serde_json::json!({ + "kind": match kind { ReservationListAccountKind::V1 => "reservation_list_v1", ReservationListAccountKind::V2 => "reservation_list_v2" }, + "master_edition": master_edition.to_string(), + "resource": resource_key.to_string(), + "supply_snapshot": supply_snapshot, + "reservations": reservations.iter().map(|entry| return serde_json::json!({"address": entry.address.clone(), "spots_remaining": entry.spots_remaining, "total_spots": entry.total_spots})).collect::>(), + "total_reservation_spots": total_reservation_spots, + "current_reservation_spots": current_reservation_spots, + }); + return std::result::Result::Ok(ReservationListAccountSnapshot { + account: account_key.to_string(), + bump, + kind, + master_edition: master_edition.to_string(), + resource: resource_key.to_string(), + supply_snapshot, + reservations, + total_reservation_spots, + current_reservation_spots, + payload_json, + }); +} + +#[cfg(test)] +mod tests { + fn metadata() -> mpl_token_metadata::accounts::Metadata { + return mpl_token_metadata::accounts::Metadata { + key: mpl_token_metadata::types::Key::MetadataV1, + update_authority: mpl_token_metadata::ID, + mint: mpl_token_metadata::ID, + name: "Khadhroony".to_string(), + symbol: "KB".to_string(), + uri: "https://example.invalid/metadata.json".to_string(), + seller_fee_basis_points: 500, + creators: std::option::Option::None, + primary_sale_happened: false, + is_mutable: true, + edition_nonce: std::option::Option::None, + token_standard: std::option::Option::None, + collection: std::option::Option::None, + uses: std::option::Option::None, + collection_details: std::option::Option::None, + programmable_config: std::option::Option::None, + }; + } + + #[test] + fn metadata_account_decodes_with_exact_owner_and_pda() { + let metadata = metadata(); + let (account, expected_bump) = + mpl_token_metadata::accounts::Metadata::find_pda(&metadata.mint); + let data = match borsh_0_10::to_vec(&metadata) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("metadata serialization failed: {error}"), + }; + let snapshot = match crate::decode_metadata_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &data, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("metadata decode failed: {error}"), + }; + assert_eq!(snapshot.bump, expected_bump); + assert_eq!(snapshot.mint, metadata.mint.to_string()); + assert_eq!(snapshot.name, "Khadhroony"); + assert_eq!(snapshot.symbol, "KB"); + assert_eq!(snapshot.seller_fee_basis_points, 500); + assert!(snapshot.is_mutable); + assert!(!snapshot.primary_sale_happened); + } + + #[test] + fn metadata_account_rejects_foreign_owner_wrong_pda_and_bounds() { + let metadata = metadata(); + let (account, _) = mpl_token_metadata::accounts::Metadata::find_pda(&metadata.mint); + let data = match borsh_0_10::to_vec(&metadata) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("metadata serialization failed: {error}"), + }; + assert_eq!( + crate::decode_metadata_account( + &account.to_string(), + &solana_pubkey::Pubkey::new_unique().to_string(), + &data + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::ForeignOwner), + ); + assert_eq!( + crate::decode_metadata_account( + &solana_pubkey::Pubkey::new_unique().to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &data + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::InvalidMetadataPda), + ); + assert_eq!( + crate::decode_metadata_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &[] + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::InvalidDataLength), + ); + } + + #[test] + fn edition_and_master_edition_accounts_decode_with_exact_pda() { + let mint = mpl_token_metadata::ID; + let (account, expected_bump) = mpl_token_metadata::accounts::MasterEdition::find_pda(&mint); + let master_v2 = mpl_token_metadata::accounts::MasterEdition { + key: mpl_token_metadata::types::Key::MasterEditionV2, + supply: 7, + max_supply: std::option::Option::Some(100), + }; + let master_v2_data = match borsh_0_10::to_vec(&master_v2) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("master edition serialization failed: {error}") + }, + }; + let master_v2_snapshot = match crate::decode_edition_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &mint.to_string(), + &master_v2_data, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("master edition decode failed: {error}"), + }; + assert_eq!(master_v2_snapshot.bump, expected_bump); + assert_eq!(master_v2_snapshot.kind, crate::EditionAccountKind::MasterEditionV2); + assert_eq!(master_v2_snapshot.supply, std::option::Option::Some(7)); + assert_eq!(master_v2_snapshot.max_supply, std::option::Option::Some(100)); + let edition = mpl_token_metadata::accounts::Edition { + key: mpl_token_metadata::types::Key::EditionV1, + parent: account, + edition: u64::MAX, + }; + let edition_data = match borsh_0_10::to_vec(&edition) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("edition serialization failed: {error}"), + }; + let edition_snapshot = match crate::decode_edition_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &mint.to_string(), + &edition_data, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("edition decode failed: {error}"), + }; + assert_eq!(edition_snapshot.kind, crate::EditionAccountKind::EditionV1); + assert_eq!(edition_snapshot.parent, std::option::Option::Some(account.to_string())); + assert_eq!(edition_snapshot.edition, std::option::Option::Some(u64::MAX)); + } + + #[test] + fn historical_master_edition_v1_and_invalid_boundaries_are_explicit() { + let mint = mpl_token_metadata::ID; + let (account, _) = mpl_token_metadata::accounts::MasterEdition::find_pda(&mint); + let historical = mpl_token_metadata::accounts::DeprecatedMasterEditionV1 { + key: mpl_token_metadata::types::Key::MasterEditionV1, + supply: 1, + max_supply: std::option::Option::None, + printing_mint: mpl_token_metadata::ID, + one_time_printing_authorization_mint: mpl_token_metadata::ID, + }; + let data = match borsh_0_10::to_vec(&historical) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("historical master edition serialization failed: {error}") + }, + }; + let snapshot = match crate::decode_edition_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &mint.to_string(), + &data, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("historical master edition decode failed: {error}") + }, + }; + assert_eq!(snapshot.kind, crate::EditionAccountKind::MasterEditionV1); + assert_eq!( + snapshot.printing_mint, + std::option::Option::Some(mpl_token_metadata::ID.to_string()) + ); + assert_eq!( + crate::decode_edition_account( + &solana_pubkey::Pubkey::new_unique().to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &mint.to_string(), + &data + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::InvalidMetadataPda), + ); + assert_eq!( + crate::decode_edition_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &mint.to_string(), + &[255] + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::InvalidMetadataKey), + ); + } + + #[test] + fn edition_marker_v1_decodes_group_bitmap_and_exact_pda() { + let mint = mpl_token_metadata::ID.to_string(); + let program = match ::from_str( + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("program parse failed: {error}"), + }; + let mint_key = match ::from_str(&mint) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("mint parse failed: {error}"), + }; + let edition = 249u64; + let group = edition / 248; + let group_seed = group.to_string(); + let (account, expected_bump) = solana_pubkey::Pubkey::find_program_address( + &[ + b"metadata", + program.as_ref(), + mint_key.as_ref(), + b"edition", + group_seed.as_bytes(), + ], + &program, + ); + let mut ledger = [0u8; 31]; + ledger[0] = 64; + let marker = mpl_token_metadata::accounts::EditionMarker { + key: mpl_token_metadata::types::Key::EditionMarker, + ledger, + }; + let data = match borsh_0_10::to_vec(&marker) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("edition marker serialization failed: {error}") + }, + }; + let snapshot = match crate::decode_edition_marker_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &mint, + edition, + &data, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("edition marker decode failed: {error}"), + }; + assert_eq!(snapshot.kind, crate::EditionMarkerAccountKind::EditionMarkerV1); + assert_eq!(snapshot.bump, expected_bump); + assert_eq!(snapshot.marker_group, std::option::Option::Some(1)); + assert_eq!(snapshot.byte_index, 0); + assert_eq!(snapshot.bit_mask, 64); + assert!(snapshot.edition_taken); + } + + #[test] + fn edition_marker_v2_decodes_extensible_bitmap_and_rejects_boundaries() { + let mint = mpl_token_metadata::ID.to_string(); + let program = match ::from_str( + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("program parse failed: {error}"), + }; + let mint_key = match ::from_str(&mint) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("mint parse failed: {error}"), + }; + let (account, expected_bump) = solana_pubkey::Pubkey::find_program_address( + &[b"metadata", program.as_ref(), mint_key.as_ref(), b"edition", b"marker"], + &program, + ); + let marker = mpl_token_metadata::accounts::EditionMarkerV2 { + key: mpl_token_metadata::types::Key::EditionMarkerV2, + ledger: vec![0, 64], + }; + let data = match borsh_0_10::to_vec(&marker) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("edition marker v2 serialization failed: {error}") + }, + }; + let snapshot = match crate::decode_edition_marker_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &mint, + 9, + &data, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("edition marker v2 decode failed: {error}"), + }; + assert_eq!(snapshot.kind, crate::EditionMarkerAccountKind::EditionMarkerV2); + assert_eq!(snapshot.bump, expected_bump); + assert_eq!(snapshot.marker_group, std::option::Option::None); + assert_eq!(snapshot.byte_index, 1); + assert_eq!(snapshot.bit_mask, 64); + assert!(snapshot.edition_taken); + let mut suffixed = data.clone(); + suffixed.push(0); + assert_eq!( + crate::decode_edition_marker_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &mint, + 9, + &suffixed + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::InvalidDataLength), + ); + assert_eq!( + crate::decode_edition_marker_account( + &solana_pubkey::Pubkey::new_unique().to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &mint, + 9, + &data + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::InvalidEditionMarkerPda), + ); + } + + #[test] + fn token_record_decodes_programmable_state_delegate_and_exact_pda() { + let mint = solana_pubkey::Pubkey::new_unique(); + let token = solana_pubkey::Pubkey::new_unique(); + let program = match ::from_str( + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("program parse failed: {error}"), + }; + let (account, bump) = solana_pubkey::Pubkey::find_program_address( + &[b"metadata", program.as_ref(), mint.as_ref(), b"token_record", token.as_ref()], + &program, + ); + let value = mpl_token_metadata::accounts::TokenRecord { + key: mpl_token_metadata::types::Key::TokenRecord, + bump, + state: mpl_token_metadata::types::TokenState::Locked, + rule_set_revision: std::option::Option::Some(u64::MAX), + delegate: std::option::Option::Some(mpl_token_metadata::ID), + delegate_role: std::option::Option::Some( + mpl_token_metadata::types::TokenDelegateRole::Transfer, + ), + locked_transfer: std::option::Option::Some(mpl_token_metadata::ID), + }; + let mut data = match borsh_0_10::to_vec(&value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("token record serialization failed: {error}"), + }; + data.resize(mpl_token_metadata::accounts::TokenRecord::LEN, 0); + let snapshot = match crate::decode_token_record_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &mint.to_string(), + &token.to_string(), + &data, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("token record decode failed: {error}"), + }; + assert_eq!(snapshot.bump, bump); + assert_eq!(snapshot.stored_bump, bump); + assert_eq!(snapshot.state, "Locked"); + assert!(snapshot.locked); + assert_eq!(snapshot.rule_set_revision, std::option::Option::Some(u64::MAX)); + assert_eq!(snapshot.delegate_role, std::option::Option::Some("Transfer".to_string())); + assert_eq!( + snapshot.delegate, + std::option::Option::Some(mpl_token_metadata::ID.to_string()) + ); + assert_eq!( + snapshot.locked_transfer, + std::option::Option::Some(mpl_token_metadata::ID.to_string()) + ); + } + + #[test] + fn token_record_rejects_foreign_owner_wrong_pda_bump_key_and_length() { + let mint = solana_pubkey::Pubkey::new_unique(); + let token = solana_pubkey::Pubkey::new_unique(); + let program = match ::from_str( + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("program parse failed: {error}"), + }; + let (account, bump) = solana_pubkey::Pubkey::find_program_address( + &[b"metadata", program.as_ref(), mint.as_ref(), b"token_record", token.as_ref()], + &program, + ); + let make_data = |key, stored_bump| { + let value = mpl_token_metadata::accounts::TokenRecord { + key, + bump: stored_bump, + state: mpl_token_metadata::types::TokenState::Unlocked, + rule_set_revision: std::option::Option::None, + delegate: std::option::Option::None, + delegate_role: std::option::Option::None, + locked_transfer: std::option::Option::None, + }; + let mut bytes = match borsh_0_10::to_vec(&value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("token record serialization failed: {error}") + }, + }; + bytes.resize(mpl_token_metadata::accounts::TokenRecord::LEN, 0); + return bytes; + }; + let data = make_data(mpl_token_metadata::types::Key::TokenRecord, bump); + assert_eq!( + crate::decode_token_record_account( + &account.to_string(), + &solana_pubkey::Pubkey::new_unique().to_string(), + &mint.to_string(), + &token.to_string(), + &data + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::ForeignOwner), + ); + assert_eq!( + crate::decode_token_record_account( + &solana_pubkey::Pubkey::new_unique().to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &mint.to_string(), + &token.to_string(), + &data + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::InvalidTokenRecordPda), + ); + let wrong_bump = + make_data(mpl_token_metadata::types::Key::TokenRecord, bump.wrapping_add(1)); + assert_eq!( + crate::decode_token_record_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &mint.to_string(), + &token.to_string(), + &wrong_bump + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::InvalidTokenRecordPda), + ); + let wrong_key = make_data(mpl_token_metadata::types::Key::MetadataV1, bump); + assert_eq!( + crate::decode_token_record_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &mint.to_string(), + &token.to_string(), + &wrong_key + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::InvalidTokenRecordKey), + ); + assert_eq!( + crate::decode_token_record_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &mint.to_string(), + &token.to_string(), + &data[..data.len() - 1] + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::InvalidDataLength), + ); + } + + #[test] + fn delegate_records_decode_exact_role_seeds_and_pdas() { + let program = match ::from_str( + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("program parse failed: {error}"), + }; + let mint = mpl_token_metadata::ID; + let authority = mpl_token_metadata::ID; + let delegate = mpl_token_metadata::ID; + let role = mpl_token_metadata::types::MetadataDelegateRole::ProgrammableConfigItem; + let role_seed = role.to_string(); + let (account, bump) = solana_pubkey::Pubkey::find_program_address( + &[ + b"metadata", + program.as_ref(), + program.as_ref(), + role_seed.as_bytes(), + program.as_ref(), + program.as_ref(), + ], + &program, + ); + let record = mpl_token_metadata::accounts::MetadataDelegateRecord { + key: mpl_token_metadata::types::Key::MetadataDelegate, + bump, + mint, + delegate, + update_authority: authority, + }; + let data = match borsh_0_10::to_vec(&record) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("metadata delegate serialization failed: {error}") + }, + }; + assert_eq!(data.len(), mpl_token_metadata::accounts::MetadataDelegateRecord::LEN); + let snapshot = match crate::decode_metadata_delegate_record_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + role, + &data, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("metadata delegate decode failed: {error}"), + }; + assert_eq!(snapshot.role, "prog_config_item_delegate"); + assert_eq!(snapshot.stored_bump, bump); + let holder_role = mpl_token_metadata::types::HolderDelegateRole::PrintDelegate; + let holder_seed = holder_role.to_string(); + let (holder_account, holder_bump) = solana_pubkey::Pubkey::find_program_address( + &[ + b"metadata", + program.as_ref(), + program.as_ref(), + holder_seed.as_bytes(), + program.as_ref(), + program.as_ref(), + ], + &program, + ); + let holder_record = mpl_token_metadata::accounts::HolderDelegateRecord { + key: mpl_token_metadata::types::Key::HolderDelegate, + bump: holder_bump, + mint, + delegate, + update_authority: authority, + }; + let holder_data = match borsh_0_10::to_vec(&holder_record) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("holder delegate serialization failed: {error}") + }, + }; + let holder_snapshot = match crate::decode_holder_delegate_record_account( + &holder_account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &authority.to_string(), + holder_role, + &holder_data, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("holder delegate decode failed: {error}"), + }; + assert_eq!(holder_snapshot.role, "print_delegate"); + assert_eq!(holder_snapshot.stored_bump, holder_bump); + } + + #[test] + fn authority_records_decode_exact_values_and_reject_invalid_boundaries() { + let program = match ::from_str( + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("program parse failed: {error}"), + }; + let mint = mpl_token_metadata::ID; + let authority = mpl_token_metadata::ID; + let (collection_account, collection_bump) = solana_pubkey::Pubkey::find_program_address( + &[ + b"metadata", + program.as_ref(), + program.as_ref(), + b"collection_authority", + program.as_ref(), + ], + &program, + ); + let collection = mpl_token_metadata::accounts::CollectionAuthorityRecord { + key: mpl_token_metadata::types::Key::CollectionAuthorityRecord, + bump: collection_bump, + update_authority: std::option::Option::Some(mpl_token_metadata::ID), + }; + let collection_data = match borsh_0_10::to_vec(&collection) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("collection authority serialization failed: {error}") + }, + }; + let collection_snapshot = match crate::decode_collection_authority_record_account( + &collection_account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &mint.to_string(), + &authority.to_string(), + &collection_data, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("collection authority decode failed: {error}") + }, + }; + assert_eq!( + collection_snapshot.update_authority, + std::option::Option::Some(mpl_token_metadata::ID.to_string()) + ); + let (use_account, use_bump) = solana_pubkey::Pubkey::find_program_address( + &[b"metadata", program.as_ref(), program.as_ref(), b"user", program.as_ref()], + &program, + ); + let use_record = mpl_token_metadata::accounts::UseAuthorityRecord { + key: mpl_token_metadata::types::Key::UseAuthorityRecord, + allowed_uses: u64::MAX, + bump: use_bump, + }; + let use_data = match borsh_0_10::to_vec(&use_record) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("use authority serialization failed: {error}") + }, + }; + let use_snapshot = match crate::decode_use_authority_record_account( + &use_account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &mint.to_string(), + &authority.to_string(), + &use_data, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("use authority decode failed: {error}"), + }; + assert_eq!(use_snapshot.allowed_uses, std::option::Option::Some(u64::MAX)); + assert_eq!( + crate::decode_use_authority_record_account( + &solana_pubkey::Pubkey::new_unique().to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &mint.to_string(), + &authority.to_string(), + &use_data + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::InvalidAuthorityRecordPda) + ); + assert_eq!( + crate::decode_use_authority_record_account( + &use_account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &mint.to_string(), + &authority.to_string(), + &use_data[..9] + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::InvalidDataLength) + ); + } + + #[test] + fn token_owned_escrow_decodes_token_owner_and_creator_authorities() { + let program = match ::from_str( + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("program parse failed: {error}"), + }; + let base_token = mpl_token_metadata::ID; + let base_key = program; + let (owner_account, owner_bump) = solana_pubkey::Pubkey::find_program_address( + &[b"metadata", program.as_ref(), base_key.as_ref(), &[0], b"escrow"], + &program, + ); + let owner_value = mpl_token_metadata::accounts::TokenOwnedEscrow { + key: mpl_token_metadata::types::Key::TokenOwnedEscrow, + base_token, + authority: mpl_token_metadata::types::EscrowAuthority::TokenOwner, + bump: owner_bump, + }; + let owner_data = match borsh_0_10::to_vec(&owner_value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("escrow serialization failed: {error}"), + }; + assert_eq!(owner_data.len(), 35); + let owner_snapshot = match crate::decode_token_owned_escrow_account( + &owner_account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &owner_data, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("escrow decode failed: {error}"), + }; + assert_eq!(owner_snapshot.authority_kind, crate::TokenOwnedEscrowAuthorityKind::TokenOwner); + assert_eq!(owner_snapshot.creator, std::option::Option::None); + let creator = mpl_token_metadata::ID; + let (creator_account, creator_bump) = solana_pubkey::Pubkey::find_program_address( + &[ + b"metadata", + program.as_ref(), + base_key.as_ref(), + &[1], + program.as_ref(), + b"escrow", + ], + &program, + ); + let creator_value = mpl_token_metadata::accounts::TokenOwnedEscrow { + key: mpl_token_metadata::types::Key::TokenOwnedEscrow, + base_token, + authority: mpl_token_metadata::types::EscrowAuthority::Creator(creator), + bump: creator_bump, + }; + let creator_data = match borsh_0_10::to_vec(&creator_value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("creator escrow serialization failed: {error}") + }, + }; + assert_eq!(creator_data.len(), 67); + let creator_snapshot = match crate::decode_token_owned_escrow_account( + &creator_account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &creator_data, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("creator escrow decode failed: {error}"), + }; + assert_eq!(creator_snapshot.authority_kind, crate::TokenOwnedEscrowAuthorityKind::Creator); + assert_eq!( + creator_snapshot.creator, + std::option::Option::Some(mpl_token_metadata::ID.to_string()) + ); + } + + #[test] + fn token_owned_escrow_rejects_wrong_pda_bump_key_and_length() { + let program = match ::from_str( + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("program parse failed: {error}"), + }; + let (account, bump) = solana_pubkey::Pubkey::find_program_address( + &[b"metadata", program.as_ref(), program.as_ref(), &[0], b"escrow"], + &program, + ); + let make = |key, stored_bump| { + let value = mpl_token_metadata::accounts::TokenOwnedEscrow { + key, + base_token: mpl_token_metadata::ID, + authority: mpl_token_metadata::types::EscrowAuthority::TokenOwner, + bump: stored_bump, + }; + return match borsh_0_10::to_vec(&value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("escrow serialization failed: {error}"), + }; + }; + let data = make(mpl_token_metadata::types::Key::TokenOwnedEscrow, bump); + assert_eq!( + crate::decode_token_owned_escrow_account( + &solana_pubkey::Pubkey::new_unique().to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &data + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::InvalidTokenOwnedEscrowPda) + ); + let wrong_bump = + make(mpl_token_metadata::types::Key::TokenOwnedEscrow, bump.wrapping_add(1)); + assert_eq!( + crate::decode_token_owned_escrow_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &wrong_bump + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::InvalidTokenOwnedEscrowPda) + ); + let wrong_key = make(mpl_token_metadata::types::Key::MetadataV1, bump); + assert_eq!( + crate::decode_token_owned_escrow_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &wrong_key + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::InvalidTokenOwnedEscrowKey) + ); + assert_eq!( + crate::decode_token_owned_escrow_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &data[..34] + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::InvalidDataLength) + ); + } + + #[test] + fn historical_reservation_lists_decode_v1_and_v2_exactly() { + let program = match ::from_str( + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("program parse failed: {error}"), + }; + let master = solana_pubkey::Pubkey::new_unique(); + let resource = solana_pubkey::Pubkey::new_unique(); + let reserved = solana_pubkey::Pubkey::new_unique(); + let (account, _) = solana_pubkey::Pubkey::find_program_address( + &[ + b"metadata", + program.as_ref(), + master.as_ref(), + b"reservation", + resource.as_ref(), + ], + &program, + ); + let mut v1 = vec![3u8]; + v1.extend_from_slice(master.as_ref()); + v1.push(1); + v1.extend_from_slice(&9u64.to_le_bytes()); + v1.extend_from_slice(&1u32.to_le_bytes()); + v1.extend_from_slice(reserved.as_ref()); + v1.push(2); + v1.push(3); + let snapshot = match crate::decode_reservation_list_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &resource.to_string(), + &v1, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("reservation v1 decode failed: {error}"), + }; + assert_eq!(snapshot.kind, crate::ReservationListAccountKind::V1); + assert_eq!(snapshot.supply_snapshot, std::option::Option::Some(9)); + assert_eq!(snapshot.total_reservation_spots, 3); + let mut v2 = vec![5u8]; + v2.extend_from_slice(master.as_ref()); + v2.push(0); + v2.extend_from_slice(&1u32.to_le_bytes()); + v2.extend_from_slice(reserved.as_ref()); + v2.extend_from_slice(&u64::MAX.to_le_bytes()); + v2.extend_from_slice(&7u64.to_le_bytes()); + v2.extend_from_slice(&11u64.to_le_bytes()); + v2.extend_from_slice(&5u64.to_le_bytes()); + let snapshot = match crate::decode_reservation_list_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &resource.to_string(), + &v2, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("reservation v2 decode failed: {error}"), + }; + assert_eq!(snapshot.kind, crate::ReservationListAccountKind::V2); + assert_eq!(snapshot.reservations[0].spots_remaining, u64::MAX); + assert_eq!(snapshot.total_reservation_spots, 11); + assert_eq!(snapshot.current_reservation_spots, 5); + } + + #[test] + fn published_account_key_inventory_is_exact_and_exhaustive() { + let variants = [ + mpl_token_metadata::types::Key::Uninitialized, + mpl_token_metadata::types::Key::EditionV1, + mpl_token_metadata::types::Key::MasterEditionV1, + mpl_token_metadata::types::Key::ReservationListV1, + mpl_token_metadata::types::Key::MetadataV1, + mpl_token_metadata::types::Key::ReservationListV2, + mpl_token_metadata::types::Key::MasterEditionV2, + mpl_token_metadata::types::Key::EditionMarker, + mpl_token_metadata::types::Key::UseAuthorityRecord, + mpl_token_metadata::types::Key::CollectionAuthorityRecord, + mpl_token_metadata::types::Key::TokenOwnedEscrow, + mpl_token_metadata::types::Key::TokenRecord, + mpl_token_metadata::types::Key::MetadataDelegate, + mpl_token_metadata::types::Key::EditionMarkerV2, + mpl_token_metadata::types::Key::HolderDelegate, + ]; + assert_eq!(crate::METADATA_ACCOUNT_LAYOUT_AUDIT.len(), variants.len()); + for (index, variant) in variants.iter().enumerate() { + let bytes = match borsh_0_10::to_vec(variant) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("key serialization failed: {error}"), + }; + assert_eq!(bytes, vec![index as u8]); + let entry = crate::METADATA_ACCOUNT_LAYOUT_AUDIT[index]; + assert_eq!(entry.discriminant, index as u8); + assert!(!entry.key_name.is_empty()); + assert!(!entry.decoder.is_empty()); + assert!(!entry.lifecycle.is_empty()); + } + } + + #[test] + fn account_audit_matrix_covers_every_key_without_duplicate_discriminants() { + let mut discriminants = std::collections::BTreeSet::new(); + let mut names = std::collections::BTreeSet::new(); + for entry in crate::METADATA_ACCOUNT_LAYOUT_AUDIT { + assert!(discriminants.insert(entry.discriminant)); + assert!(names.insert(entry.key_name)); + if entry.discriminant == 0 { + assert_eq!(entry.decoder, "reject_uninitialized"); + } else { + assert_ne!(entry.decoder, "reject_uninitialized"); + } + } + let matrix: serde_json::Value = match serde_json::from_str(include_str!( + "../../docs/METAPLEX_TOKEN_METADATA_MATRIX.json" + )) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("matrix parse failed: {error}"), + }; + let inventory = + match matrix.get("account_key_inventory").and_then(serde_json::Value::as_array) { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("account_key_inventory missing"), + }; + assert_eq!(inventory.len(), crate::METADATA_ACCOUNT_LAYOUT_AUDIT.len()); + for (index, item) in inventory.iter().enumerate() { + assert_eq!( + item.get("discriminant").and_then(serde_json::Value::as_u64), + std::option::Option::Some(index as u64) + ); + assert_eq!( + item.get("key").and_then(serde_json::Value::as_str), + std::option::Option::Some(crate::METADATA_ACCOUNT_LAYOUT_AUDIT[index].key_name) + ); + assert_eq!( + item.get("decoder").and_then(serde_json::Value::as_str), + std::option::Option::Some(crate::METADATA_ACCOUNT_LAYOUT_AUDIT[index].decoder) + ); + } + } + + #[test] + fn historical_reservation_lists_reject_wrong_pda_key_bounds_and_suffix() { + let program = match ::from_str( + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("program parse failed: {error}"), + }; + let master = solana_pubkey::Pubkey::new_unique(); + let resource = solana_pubkey::Pubkey::new_unique(); + let (account, _) = solana_pubkey::Pubkey::find_program_address( + &[ + b"metadata", + program.as_ref(), + master.as_ref(), + b"reservation", + resource.as_ref(), + ], + &program, + ); + let mut data = vec![3u8]; + data.extend_from_slice(master.as_ref()); + data.push(0); + data.extend_from_slice(&0u32.to_le_bytes()); + assert_eq!( + crate::decode_reservation_list_account( + &solana_pubkey::Pubkey::new_unique().to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &resource.to_string(), + &data + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::InvalidReservationListPda) + ); + let mut wrong_key = data.clone(); + wrong_key[0] = 4; + assert_eq!( + crate::decode_reservation_list_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &resource.to_string(), + &wrong_key + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::InvalidReservationListKey) + ); + let mut too_many = data.clone(); + too_many[34..38].copy_from_slice(&201u32.to_le_bytes()); + assert_eq!( + crate::decode_reservation_list_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &resource.to_string(), + &too_many + ), + std::result::Result::Err( + crate::MetadataAccountDecodeError::ReservationListBoundsExceeded + ) + ); + let mut suffix = data.clone(); + suffix.push(1); + assert_eq!( + crate::decode_reservation_list_account( + &account.to_string(), + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + &resource.to_string(), + &suffix + ), + std::result::Result::Err(crate::MetadataAccountDecodeError::InvalidMetadataLayout) + ); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/canonical.rs b/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/canonical.rs new file mode 100644 index 0000000..8cf7abe --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/canonical.rs @@ -0,0 +1,417 @@ +// file: kb_decoder_metadata_metaplex_token_metadata/src/canonical.rs +// version: 2 + +//! Canonical Metaplex account identities, provenance and materialization policy. + +/// Lifecycle class of one decoded Metaplex account layout. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum CanonicalAccountLifecycle { + /// Current account layout used by supported runtime flows. + Active, + /// Published account layout whose runtime surface is still experimental. + ActiveExperimental, + /// Historical account layout retained for ledger replay and backfill. + Historical, +} + +/// Materialization policy attached to one canonical account snapshot. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum CanonicalMaterializationPolicy { + /// Materialize the latest committed state of an active account. + ActiveState, + /// Materialize historical state with explicit lifecycle provenance. + HistoricalState, +} + +/// Execution policy attached to one canonical account layout. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum CanonicalExecutionPolicy { + /// The layout belongs to current operations that may later be executable. + CurrentCandidate, + /// The layout belongs to an experimental operation requiring explicit activation. + ExperimentalCandidate, + /// Historical state must never be exposed by an executor. + NeverHistorical, +} + +/// Origin of one canonical account observation. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum CanonicalObservationSource { + /// Account observed from a live subscription or direct live read. + Live, + /// Account reconstructed while replaying already stored transactions. + Replay, + /// Account discovered while backfilling historical ledger data. + Backfill, + /// Account read to repair a detected ingestion gap. + Repair, + /// Synthetic account used only by bounded validation tests. + Synthetic, +} + +/// Provenance retained for one canonical account observation. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct CanonicalAccountProvenance { + /// Observation source. + pub source: CanonicalObservationSource, + /// Slot associated with the account state when known. + pub slot: std::option::Option, + /// Provider write version when available. + pub write_version: std::option::Option, + /// Transaction signature responsible for the state when known. + pub signature: std::option::Option, + /// Instruction index associated with the mutation when known. + pub instruction_index: std::option::Option, + /// Inner-instruction index associated with the mutation when known. + pub inner_instruction_index: std::option::Option, + /// Whether the source state is committed according to the caller's boundary. + pub committed: bool, +} + +/// Stable identity and policy of one canonical Metaplex account snapshot. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct CanonicalAccountIdentity { + /// Stable identity formed from program id and canonical account address. + pub stable_id: String, + /// Canonical Metaplex Token Metadata program id. + pub program_id: String, + /// Canonical account address. + pub account: String, + /// Published layout name. + pub layout: String, + /// Exact published `Key` discriminant. + pub discriminant: u8, + /// Internal canonical-model version. + pub canonical_version: u16, + /// Published lifecycle class. + pub lifecycle: CanonicalAccountLifecycle, + /// Materialization policy, including historical backfill state. + pub materialization: CanonicalMaterializationPolicy, + /// Execution policy separated from materialization. + pub execution: CanonicalExecutionPolicy, +} + +/// Typed state carried by one canonical Metaplex account snapshot. +#[derive(Clone, Debug, PartialEq)] +pub enum CanonicalMetadataAccountState { + /// Metadata account state. + Metadata(crate::MetadataAccountSnapshot), + /// Master Edition or printed Edition state. + Edition(crate::EditionAccountSnapshot), + /// Edition Marker state. + EditionMarker(crate::EditionMarkerAccountSnapshot), + /// Programmable Token Record state. + TokenRecord(crate::TokenRecordAccountSnapshot), + /// Metadata or holder delegate state. + DelegateRecord(crate::DelegateRecordAccountSnapshot), + /// Collection or use authority state. + AuthorityRecord(crate::AuthorityRecordAccountSnapshot), + /// Token-owned escrow state. + TokenOwnedEscrow(crate::TokenOwnedEscrowAccountSnapshot), + /// Historical Reservation List state. + ReservationList(crate::ReservationListAccountSnapshot), +} + +/// Canonical envelope used by future materializers and stateful replay. +#[derive(Clone, Debug, PartialEq)] +pub struct CanonicalMetadataAccountSnapshot { + /// Stable identity and lifecycle policy. + pub identity: CanonicalAccountIdentity, + /// Observation provenance. + pub provenance: CanonicalAccountProvenance, + /// Typed bounded state. + pub state: CanonicalMetadataAccountState, +} + +fn canonical_identity( + account: &str, + layout: &str, + discriminant: u8, + lifecycle: CanonicalAccountLifecycle, +) -> CanonicalAccountIdentity { + let program_id = kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID.to_string(); + let materialization = match lifecycle { + CanonicalAccountLifecycle::Historical => CanonicalMaterializationPolicy::HistoricalState, + CanonicalAccountLifecycle::Active | CanonicalAccountLifecycle::ActiveExperimental => { + CanonicalMaterializationPolicy::ActiveState + }, + }; + let execution = match lifecycle { + CanonicalAccountLifecycle::Historical => CanonicalExecutionPolicy::NeverHistorical, + CanonicalAccountLifecycle::Active => CanonicalExecutionPolicy::CurrentCandidate, + CanonicalAccountLifecycle::ActiveExperimental => { + CanonicalExecutionPolicy::ExperimentalCandidate + }, + }; + return CanonicalAccountIdentity { + stable_id: format!("{program_id}:{account}"), + program_id, + account: account.to_string(), + layout: layout.to_string(), + discriminant, + canonical_version: 1, + lifecycle, + materialization, + execution, + }; +} + +impl CanonicalMetadataAccountSnapshot { + /// Wraps one Metadata account snapshot with stable identity and provenance. + pub fn from_metadata( + snapshot: crate::MetadataAccountSnapshot, + provenance: CanonicalAccountProvenance, + ) -> Self { + let identity = canonical_identity( + &snapshot.account, + "MetadataV1", + mpl_token_metadata::types::Key::MetadataV1 as u8, + CanonicalAccountLifecycle::Active, + ); + return Self { + identity, + provenance, + state: CanonicalMetadataAccountState::Metadata(snapshot), + }; + } + + /// Wraps one Edition or Master Edition snapshot with exact lifecycle policy. + pub fn from_edition( + snapshot: crate::EditionAccountSnapshot, + provenance: CanonicalAccountProvenance, + ) -> Self { + let (layout, discriminant, lifecycle) = match snapshot.kind { + crate::EditionAccountKind::MasterEditionV1 => ( + "MasterEditionV1", + mpl_token_metadata::types::Key::MasterEditionV1 as u8, + CanonicalAccountLifecycle::Historical, + ), + crate::EditionAccountKind::MasterEditionV2 => ( + "MasterEditionV2", + mpl_token_metadata::types::Key::MasterEditionV2 as u8, + CanonicalAccountLifecycle::Active, + ), + crate::EditionAccountKind::EditionV1 => ( + "EditionV1", + mpl_token_metadata::types::Key::EditionV1 as u8, + CanonicalAccountLifecycle::Active, + ), + }; + let identity = canonical_identity(&snapshot.account, layout, discriminant, lifecycle); + return Self { + identity, + provenance, + state: CanonicalMetadataAccountState::Edition(snapshot), + }; + } + + /// Wraps one Edition Marker snapshot with exact lifecycle policy. + pub fn from_edition_marker( + snapshot: crate::EditionMarkerAccountSnapshot, + provenance: CanonicalAccountProvenance, + ) -> Self { + let (layout, discriminant, lifecycle) = match snapshot.kind { + crate::EditionMarkerAccountKind::EditionMarkerV1 => ( + "EditionMarker", + mpl_token_metadata::types::Key::EditionMarker as u8, + CanonicalAccountLifecycle::Active, + ), + crate::EditionMarkerAccountKind::EditionMarkerV2 => ( + "EditionMarkerV2", + mpl_token_metadata::types::Key::EditionMarkerV2 as u8, + CanonicalAccountLifecycle::ActiveExperimental, + ), + }; + let identity = canonical_identity(&snapshot.account, layout, discriminant, lifecycle); + return Self { + identity, + provenance, + state: CanonicalMetadataAccountState::EditionMarker(snapshot), + }; + } + + /// Wraps one Token Record snapshot. + pub fn from_token_record( + snapshot: crate::TokenRecordAccountSnapshot, + provenance: CanonicalAccountProvenance, + ) -> Self { + let identity = canonical_identity( + &snapshot.account, + "TokenRecord", + mpl_token_metadata::types::Key::TokenRecord as u8, + CanonicalAccountLifecycle::Active, + ); + return Self { + identity, + provenance, + state: CanonicalMetadataAccountState::TokenRecord(snapshot), + }; + } + + /// Wraps one Metadata or Holder Delegate snapshot. + pub fn from_delegate_record( + snapshot: crate::DelegateRecordAccountSnapshot, + provenance: CanonicalAccountProvenance, + ) -> Self { + let (layout, discriminant) = if snapshot.kind == "MetadataDelegateRecord" { + ("MetadataDelegate", mpl_token_metadata::types::Key::MetadataDelegate as u8) + } else { + ("HolderDelegate", mpl_token_metadata::types::Key::HolderDelegate as u8) + }; + let identity = canonical_identity( + &snapshot.account, + layout, + discriminant, + CanonicalAccountLifecycle::Active, + ); + return Self { + identity, + provenance, + state: CanonicalMetadataAccountState::DelegateRecord(snapshot), + }; + } + + /// Wraps one Collection or Use Authority snapshot. + pub fn from_authority_record( + snapshot: crate::AuthorityRecordAccountSnapshot, + provenance: CanonicalAccountProvenance, + ) -> Self { + let (layout, discriminant) = if snapshot.kind == "CollectionAuthorityRecord" { + ( + "CollectionAuthorityRecord", + mpl_token_metadata::types::Key::CollectionAuthorityRecord as u8, + ) + } else { + ("UseAuthorityRecord", mpl_token_metadata::types::Key::UseAuthorityRecord as u8) + }; + let identity = canonical_identity( + &snapshot.account, + layout, + discriminant, + CanonicalAccountLifecycle::Active, + ); + return Self { + identity, + provenance, + state: CanonicalMetadataAccountState::AuthorityRecord(snapshot), + }; + } + + /// Wraps one Token Owned Escrow snapshot. + pub fn from_token_owned_escrow( + snapshot: crate::TokenOwnedEscrowAccountSnapshot, + provenance: CanonicalAccountProvenance, + ) -> Self { + let identity = canonical_identity( + &snapshot.account, + "TokenOwnedEscrow", + mpl_token_metadata::types::Key::TokenOwnedEscrow as u8, + CanonicalAccountLifecycle::Active, + ); + return Self { + identity, + provenance, + state: CanonicalMetadataAccountState::TokenOwnedEscrow(snapshot), + }; + } + + /// Wraps one historical Reservation List snapshot as materializable historical state. + pub fn from_reservation_list( + snapshot: crate::ReservationListAccountSnapshot, + provenance: CanonicalAccountProvenance, + ) -> Self { + let (layout, discriminant) = match snapshot.kind { + crate::ReservationListAccountKind::V1 => { + ("ReservationListV1", mpl_token_metadata::types::Key::ReservationListV1 as u8) + }, + crate::ReservationListAccountKind::V2 => { + ("ReservationListV2", mpl_token_metadata::types::Key::ReservationListV2 as u8) + }, + }; + let identity = canonical_identity( + &snapshot.account, + layout, + discriminant, + CanonicalAccountLifecycle::Historical, + ); + return Self { + identity, + provenance, + state: CanonicalMetadataAccountState::ReservationList(snapshot), + }; + } +} + +#[cfg(test)] +mod tests { + #[test] + fn historical_state_is_materializable_but_never_executable() { + let snapshot = crate::ReservationListAccountSnapshot { + account: "reservation-account".to_string(), + bump: 1, + kind: crate::ReservationListAccountKind::V1, + master_edition: "master-edition".to_string(), + resource: "resource".to_string(), + supply_snapshot: std::option::Option::Some(7), + reservations: std::vec::Vec::new(), + total_reservation_spots: 0, + current_reservation_spots: 0, + payload_json: serde_json::json!({}), + }; + let provenance = super::CanonicalAccountProvenance { + source: super::CanonicalObservationSource::Backfill, + slot: std::option::Option::Some(42), + write_version: std::option::Option::None, + signature: std::option::Option::Some("historical-signature".to_string()), + instruction_index: std::option::Option::Some(3), + inner_instruction_index: std::option::Option::None, + committed: true, + }; + let canonical = + super::CanonicalMetadataAccountSnapshot::from_reservation_list(snapshot, provenance); + assert_eq!(canonical.identity.lifecycle, super::CanonicalAccountLifecycle::Historical); + assert_eq!( + canonical.identity.materialization, + super::CanonicalMaterializationPolicy::HistoricalState + ); + assert_eq!(canonical.identity.execution, super::CanonicalExecutionPolicy::NeverHistorical); + assert_eq!(canonical.provenance.source, super::CanonicalObservationSource::Backfill); + assert!(canonical.identity.stable_id.ends_with(":reservation-account")); + } + + #[test] + fn active_and_experimental_layouts_keep_distinct_policies() { + let metadata = crate::MetadataAccountSnapshot { + account: "metadata-account".to_string(), + bump: 1, + mint: "mint".to_string(), + update_authority: "authority".to_string(), + name: "name".to_string(), + symbol: "symbol".to_string(), + uri: "uri".to_string(), + seller_fee_basis_points: 0, + primary_sale_happened: false, + is_mutable: true, + payload_json: serde_json::json!({}), + }; + let provenance = super::CanonicalAccountProvenance { + source: super::CanonicalObservationSource::Live, + slot: std::option::Option::Some(1), + write_version: std::option::Option::Some(2), + signature: std::option::Option::None, + instruction_index: std::option::Option::None, + inner_instruction_index: std::option::Option::None, + committed: true, + }; + let canonical = + super::CanonicalMetadataAccountSnapshot::from_metadata(metadata, provenance); + assert_eq!(canonical.identity.lifecycle, super::CanonicalAccountLifecycle::Active); + assert_eq!( + canonical.identity.materialization, + super::CanonicalMaterializationPolicy::ActiveState + ); + assert_eq!(canonical.identity.execution, super::CanonicalExecutionPolicy::CurrentCandidate); + assert_eq!(canonical.identity.discriminant, 4); + assert_eq!(canonical.identity.canonical_version, 1); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/constants.rs new file mode 100644 index 0000000..b69a23f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/constants.rs @@ -0,0 +1,258 @@ +// file: kb_decoder_metadata_metaplex_token_metadata/src/constants.rs +// version: 28 + +//! Internal constants for Metaplex Token Metadata decoding. + +/// Stable decoder surface code. +pub(crate) const SURFACE_CODE: &str = "metadata_metaplex_token_metadata"; +/// Tracing target for this decoder. +/// Maximum retained instruction payload size. +pub(crate) const MAX_INSTRUCTION_BYTES: usize = 16_384; +/// Maximum encoded base64 instruction payload size. +pub(crate) const MAX_BASE64_BYTES: usize = 21_852; +/// Maximum number of instruction accounts accepted by this decoder. +pub(crate) const MAX_INSTRUCTION_ACCOUNTS: usize = 64; +/// Number of bytes retained in malformed-wire diagnostics. +pub(crate) const DIAGNOSTIC_PREFIX_BYTES: usize = 32; +/// Stable event payload contract version. +pub(crate) const EVENT_VERSION: u32 = 1; +/// Deprecated Create Master Edition discriminator. +pub(crate) const DEPRECATED_CREATE_MASTER_EDITION_DISCRIMINATOR: u8 = 2; +/// Deprecated Set Reservation List discriminator. +pub(crate) const DEPRECATED_SET_RESERVATION_LIST_DISCRIMINATOR: u8 = 5; +/// Deprecated Create Reservation List discriminator. +pub(crate) const DEPRECATED_CREATE_RESERVATION_LIST_DISCRIMINATOR: u8 = 6; +/// Deprecated Mint Printing Tokens Via Token discriminator. +pub(crate) const DEPRECATED_MINT_PRINTING_TOKENS_VIA_TOKEN_DISCRIMINATOR: u8 = 8; +/// Deprecated Mint Printing Tokens discriminator. +pub(crate) const DEPRECATED_MINT_PRINTING_TOKENS_DISCRIMINATOR: u8 = 9; +/// Historical Create Master Edition discriminator. +pub(crate) const CREATE_MASTER_EDITION_DISCRIMINATOR: u8 = 10; +/// Deprecated printing-token mint-new-edition discriminator. +pub(crate) const DEPRECATED_MINT_NEW_EDITION_VIA_PRINTING_TOKEN_DISCRIMINATOR: u8 = 3; +/// Historical Create Metadata Account discriminator. +pub(crate) const CREATE_METADATA_ACCOUNT_DISCRIMINATOR: u8 = 0; +/// Historical Update Metadata Account discriminator. +pub(crate) const UPDATE_METADATA_ACCOUNT_DISCRIMINATOR: u8 = 1; +/// Historical Create Metadata Account V2 discriminator. +pub(crate) const CREATE_METADATA_ACCOUNT_V2_DISCRIMINATOR: u8 = 16; +/// Create Metadata Account V3 discriminator. +pub(crate) const CREATE_METADATA_ACCOUNT_V3_DISCRIMINATOR: u8 = 33; +/// Update Metadata Account V2 discriminator. +pub(crate) const UPDATE_METADATA_ACCOUNT_V2_DISCRIMINATOR: u8 = 15; +/// Verify Collection discriminator. +pub(crate) const VERIFY_COLLECTION_DISCRIMINATOR: u8 = 18; +/// Unverify Collection discriminator. +pub(crate) const UNVERIFY_COLLECTION_DISCRIMINATOR: u8 = 22; +/// Set And Verify Collection discriminator. +pub(crate) const SET_AND_VERIFY_COLLECTION_DISCRIMINATOR: u8 = 25; +/// Approve Collection Authority discriminator. +pub(crate) const APPROVE_COLLECTION_AUTHORITY_DISCRIMINATOR: u8 = 23; +/// Revoke Collection Authority discriminator. +pub(crate) const REVOKE_COLLECTION_AUTHORITY_DISCRIMINATOR: u8 = 24; +/// Verify Sized Collection Item discriminator. +pub(crate) const VERIFY_SIZED_COLLECTION_ITEM_DISCRIMINATOR: u8 = 30; +/// Unverify Sized Collection Item discriminator. +pub(crate) const UNVERIFY_SIZED_COLLECTION_ITEM_DISCRIMINATOR: u8 = 31; +/// Set And Verify Sized Collection Item discriminator. +pub(crate) const SET_AND_VERIFY_SIZED_COLLECTION_ITEM_DISCRIMINATOR: u8 = 32; +/// Sign Metadata discriminator. +pub(crate) const SIGN_METADATA_DISCRIMINATOR: u8 = 7; +/// Update Primary Sale Happened Via Token discriminator. +pub(crate) const UPDATE_PRIMARY_SALE_HAPPENED_VIA_TOKEN_DISCRIMINATOR: u8 = 4; +/// Utilize discriminator. +pub(crate) const UTILIZE_DISCRIMINATOR: u8 = 19; +/// Approve Use Authority discriminator. +pub(crate) const APPROVE_USE_AUTHORITY_DISCRIMINATOR: u8 = 20; +/// Revoke Use Authority discriminator. +pub(crate) const REVOKE_USE_AUTHORITY_DISCRIMINATOR: u8 = 21; +/// Puff Metadata discriminator. +pub(crate) const PUFF_METADATA_DISCRIMINATOR: u8 = 14; +/// Freeze Delegated Account discriminator. +pub(crate) const FREEZE_DELEGATED_ACCOUNT_DISCRIMINATOR: u8 = 26; +/// Thaw Delegated Account discriminator. +pub(crate) const THAW_DELEGATED_ACCOUNT_DISCRIMINATOR: u8 = 27; +/// Remove Creator Verification discriminator. +pub(crate) const REMOVE_CREATOR_VERIFICATION_DISCRIMINATOR: u8 = 28; +/// Set Token Standard discriminator. +pub(crate) const SET_TOKEN_STANDARD_DISCRIMINATOR: u8 = 35; +/// Mint New Edition From Master Edition Via Token discriminator. +pub(crate) const MINT_NEW_EDITION_FROM_MASTER_EDITION_VIA_TOKEN_DISCRIMINATOR: u8 = 11; +/// Mint New Edition From Master Edition Via Vault Proxy discriminator. +pub(crate) const MINT_NEW_EDITION_FROM_MASTER_EDITION_VIA_VAULT_PROXY_DISCRIMINATOR: u8 = 13; +/// Convert Master Edition V1 to V2 discriminator. +pub(crate) const CONVERT_MASTER_EDITION_V1_TO_V2_DISCRIMINATOR: u8 = 12; +/// Create Master Edition V3 discriminator. +pub(crate) const CREATE_MASTER_EDITION_V3_DISCRIMINATOR: u8 = 17; +/// Set Collection Size discriminator. +pub(crate) const SET_COLLECTION_SIZE_DISCRIMINATOR: u8 = 34; +/// Bubblegum Set Collection Size discriminator. +pub(crate) const BUBBLEGUM_SET_COLLECTION_SIZE_DISCRIMINATOR: u8 = 36; +/// Create Escrow Account discriminator. +pub(crate) const CREATE_ESCROW_ACCOUNT_DISCRIMINATOR: u8 = 38; +/// Close Escrow Account discriminator. +pub(crate) const CLOSE_ESCROW_ACCOUNT_DISCRIMINATOR: u8 = 39; +/// Transfer Out Of Escrow discriminator. +pub(crate) const TRANSFER_OUT_OF_ESCROW_DISCRIMINATOR: u8 = 40; +/// Collect discriminator. +pub(crate) const COLLECT_DISCRIMINATOR: u8 = 54; +/// Historical Burn NFT discriminator. +pub(crate) const BURN_NFT_DISCRIMINATOR: u8 = 29; +/// Historical Burn Edition NFT discriminator. +pub(crate) const BURN_EDITION_NFT_DISCRIMINATOR: u8 = 37; +/// Burn discriminator. +pub(crate) const BURN_DISCRIMINATOR: u8 = 41; +/// Lock discriminator. +pub(crate) const LOCK_DISCRIMINATOR: u8 = 46; +/// Unlock discriminator. +pub(crate) const UNLOCK_DISCRIMINATOR: u8 = 47; +/// Transfer discriminator. +pub(crate) const TRANSFER_DISCRIMINATOR: u8 = 49; +/// Close Accounts discriminator. +pub(crate) const CLOSE_ACCOUNTS_DISCRIMINATOR: u8 = 57; +/// Modern Create discriminator. +pub(crate) const CREATE_DISCRIMINATOR: u8 = 42; +/// Modern Mint discriminator. +pub(crate) const MINT_DISCRIMINATOR: u8 = 43; +/// Modern Print discriminator. +pub(crate) const PRINT_DISCRIMINATOR: u8 = 55; +/// Modern Update discriminator. +pub(crate) const UPDATE_DISCRIMINATOR: u8 = 50; +/// Modern Use discriminator. +pub(crate) const USE_DISCRIMINATOR: u8 = 51; +/// Modern Verify discriminator. +pub(crate) const VERIFY_DISCRIMINATOR: u8 = 52; +/// Modern Unverify discriminator. +pub(crate) const UNVERIFY_DISCRIMINATOR: u8 = 53; +/// Migrate discriminator. +pub(crate) const MIGRATE_DISCRIMINATOR: u8 = 48; +/// Resize discriminator. +pub(crate) const RESIZE_DISCRIMINATOR: u8 = 56; +/// Delegate discriminator. +pub(crate) const DELEGATE_DISCRIMINATOR: u8 = 44; +/// Revoke discriminator. +pub(crate) const REVOKE_DISCRIMINATOR: u8 = 45; +/// Current pre-release instruction inventory. +pub(crate) const INSTRUCTION_ENTRIES: &[(u8, &str, bool)] = &[ + (CREATE_METADATA_ACCOUNT_DISCRIMINATOR, "create_metadata_account", true), + ( + DEPRECATED_CREATE_MASTER_EDITION_DISCRIMINATOR, + "deprecated_create_master_edition", + true, + ), + ( + DEPRECATED_MINT_NEW_EDITION_VIA_PRINTING_TOKEN_DISCRIMINATOR, + "deprecated_mint_new_edition_from_master_edition_via_printing_token", + true, + ), + (UPDATE_METADATA_ACCOUNT_DISCRIMINATOR, "update_metadata_account", true), + ( + UPDATE_PRIMARY_SALE_HAPPENED_VIA_TOKEN_DISCRIMINATOR, + "update_primary_sale_happened_via_token", + false, + ), + ( + DEPRECATED_SET_RESERVATION_LIST_DISCRIMINATOR, + "deprecated_set_reservation_list", + true, + ), + ( + DEPRECATED_CREATE_RESERVATION_LIST_DISCRIMINATOR, + "deprecated_create_reservation_list", + true, + ), + (SIGN_METADATA_DISCRIMINATOR, "sign_metadata", false), + ( + DEPRECATED_MINT_PRINTING_TOKENS_VIA_TOKEN_DISCRIMINATOR, + "deprecated_mint_printing_tokens_via_token", + true, + ), + ( + DEPRECATED_MINT_PRINTING_TOKENS_DISCRIMINATOR, + "deprecated_mint_printing_tokens", + true, + ), + (CREATE_MASTER_EDITION_DISCRIMINATOR, "create_master_edition", true), + (PUFF_METADATA_DISCRIMINATOR, "puff_metadata", false), + ( + MINT_NEW_EDITION_FROM_MASTER_EDITION_VIA_TOKEN_DISCRIMINATOR, + "mint_new_edition_from_master_edition_via_token", + false, + ), + ( + MINT_NEW_EDITION_FROM_MASTER_EDITION_VIA_VAULT_PROXY_DISCRIMINATOR, + "mint_new_edition_from_master_edition_via_vault_proxy", + true, + ), + ( + CONVERT_MASTER_EDITION_V1_TO_V2_DISCRIMINATOR, + "convert_master_edition_v1_to_v2", + true, + ), + (UPDATE_METADATA_ACCOUNT_V2_DISCRIMINATOR, "update_metadata_account_v2", false), + (CREATE_METADATA_ACCOUNT_V2_DISCRIMINATOR, "create_metadata_account_v2", true), + (CREATE_MASTER_EDITION_V3_DISCRIMINATOR, "create_master_edition_v3", false), + (CREATE_METADATA_ACCOUNT_V3_DISCRIMINATOR, "create_metadata_account_v3", false), + (VERIFY_COLLECTION_DISCRIMINATOR, "verify_collection", false), + (UNVERIFY_COLLECTION_DISCRIMINATOR, "unverify_collection", false), + (SET_AND_VERIFY_COLLECTION_DISCRIMINATOR, "set_and_verify_collection", false), + ( + APPROVE_COLLECTION_AUTHORITY_DISCRIMINATOR, + "approve_collection_authority", + false, + ), + (REVOKE_COLLECTION_AUTHORITY_DISCRIMINATOR, "revoke_collection_authority", false), + ( + VERIFY_SIZED_COLLECTION_ITEM_DISCRIMINATOR, + "verify_sized_collection_item", + false, + ), + ( + UNVERIFY_SIZED_COLLECTION_ITEM_DISCRIMINATOR, + "unverify_sized_collection_item", + false, + ), + ( + SET_AND_VERIFY_SIZED_COLLECTION_ITEM_DISCRIMINATOR, + "set_and_verify_sized_collection_item", + false, + ), + (UTILIZE_DISCRIMINATOR, "utilize", false), + (APPROVE_USE_AUTHORITY_DISCRIMINATOR, "approve_use_authority", false), + (REVOKE_USE_AUTHORITY_DISCRIMINATOR, "revoke_use_authority", false), + (FREEZE_DELEGATED_ACCOUNT_DISCRIMINATOR, "freeze_delegated_account", true), + (THAW_DELEGATED_ACCOUNT_DISCRIMINATOR, "thaw_delegated_account", true), + (REMOVE_CREATOR_VERIFICATION_DISCRIMINATOR, "remove_creator_verification", false), + (SET_TOKEN_STANDARD_DISCRIMINATOR, "set_token_standard", false), + (SET_COLLECTION_SIZE_DISCRIMINATOR, "set_collection_size", false), + ( + BUBBLEGUM_SET_COLLECTION_SIZE_DISCRIMINATOR, + "bubblegum_set_collection_size", + false, + ), + (CREATE_ESCROW_ACCOUNT_DISCRIMINATOR, "create_escrow_account", false), + (CLOSE_ESCROW_ACCOUNT_DISCRIMINATOR, "close_escrow_account", false), + (TRANSFER_OUT_OF_ESCROW_DISCRIMINATOR, "transfer_out_of_escrow", false), + (BURN_NFT_DISCRIMINATOR, "burn_nft", true), + (BURN_EDITION_NFT_DISCRIMINATOR, "burn_edition_nft", true), + (BURN_DISCRIMINATOR, "burn", false), + (CREATE_DISCRIMINATOR, "create", false), + (MINT_DISCRIMINATOR, "mint", false), + (LOCK_DISCRIMINATOR, "lock", false), + (UNLOCK_DISCRIMINATOR, "unlock", false), + (TRANSFER_DISCRIMINATOR, "transfer", false), + (COLLECT_DISCRIMINATOR, "collect", false), + (PRINT_DISCRIMINATOR, "print", false), + (UPDATE_DISCRIMINATOR, "update", false), + (USE_DISCRIMINATOR, "use", false), + (VERIFY_DISCRIMINATOR, "verify", false), + (UNVERIFY_DISCRIMINATOR, "unverify", false), + (MIGRATE_DISCRIMINATOR, "migrate", false), + (RESIZE_DISCRIMINATOR, "resize", false), + (CLOSE_ACCOUNTS_DISCRIMINATOR, "close_accounts", false), + (DELEGATE_DISCRIMINATOR, "delegate", false), + (REVOKE_DISCRIMINATOR, "revoke", false), +]; + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_metadata_metaplex_token_metadata"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/decoder.rs new file mode 100644 index 0000000..b857ad4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/decoder.rs @@ -0,0 +1,3055 @@ +// file: kb_decoder_metadata_metaplex_token_metadata/src/decoder.rs +// version: 27 + +//! Exact contextual dispatch for Metaplex Token Metadata. + +const SURFACES: &[kb_decoder_api::DecoderSurface] = &[kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + surface_code: crate::SURFACE_CODE, + priority: 100, +}]; + +const PROGRAM_IDS: &[&str] = &[kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID]; + +/// Exact decoder for the Metaplex Token Metadata program. +#[derive(Clone, Debug, Default)] +pub struct MetadataMetaplexTokenMetadataDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::MetadataMetaplexTokenMetadataDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_metadata_metaplex_token_metadata"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return PROGRAM_IDS; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Yes + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +impl kb_decoder_api::InstructionDecoder for crate::MetadataMetaplexTokenMetadataDecoder { + fn identity(&self) -> kb_decoder_api::DecoderIdentity { + return kb_decoder_api::DecoderIdentity { + name: crate::SURFACE_CODE.to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + }; + } + + fn surfaces(&self) -> &'static [kb_decoder_api::DecoderSurface] { + return SURFACES; + } + + fn coverage(&self) -> std::vec::Vec { + return crate::INSTRUCTION_ENTRIES + .iter() + .map(|(discriminator, code, historical)| { + return kb_decoder_api::DecoderCoverageDeclaration { + program_id: kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID + .to_string(), + surface_code: std::option::Option::Some(crate::SURFACE_CODE.to_string()), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: (*code).to_string(), + discriminator_hex: std::option::Option::Some(format!("{discriminator:02x}")), + historical: *historical, + }; + }) + .collect(); + } + + fn recognize( + &self, + input: &kb_store_core::CoreInstructionReplayInput, + ) -> kb_decoder_api::DecoderRecognition { + if input.program_id != kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID { + return kb_decoder_api::DecoderRecognition::incompatible(); + } + let discriminator = crate::payload_discriminator(input); + let entry = discriminator.and_then(crate::entry_for_discriminator); + return kb_decoder_api::DecoderRecognition::compatible( + entry.is_some(), + 100, + std::option::Option::Some(crate::SURFACE_CODE.to_string()), + entry.map(|(_, code, _)| return code.to_string()), + discriminator.map(|value| return format!("{value:02x}")), + ); + } + + fn decode( + &self, + input: &kb_store_core::CoreInstructionReplayInput, + ) -> kb_decoder_api::DecoderExecutionResult { + if input.program_id != kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID { + return kb_decoder_api::DecoderExecutionResult::unsupported(std::option::Option::None); + } + let result = crate::decode(input); + if matches!( + result.status, + kb_decoder_api::DecoderOutcomeStatus::Failed + | kb_decoder_api::DecoderOutcomeStatus::Unsupported + ) { + tracing::error!( + target: crate::TRACING_TARGET, + action = "decode_failure", + signature = %input.signature, + slot = input.slot, + instruction_path = %input.instruction_path, + program_id = %input.program_id, + processor_name = crate::SURFACE_CODE, + processor_version = env!("CARGO_PKG_VERSION"), + input_key = %input.replay_input_key, + result_status = ?result.status, + diagnostics = ?result.diagnostics, + "Metaplex Token Metadata instruction was not decoded successfully" + ); + } + tracing::debug!( + target: crate::TRACING_TARGET, + action = "decode", + signature = %input.signature, + instruction_path = %input.instruction_path, + transaction_failed = input.transaction_failed, + result_status = ?result.status, + observation_count = result.observations.len(), + "Metaplex Token Metadata instruction decode completed" + ); + return result; + } +} + +#[cfg(test)] +mod tests { + use base64::Engine; // rust-rules: trait-import + + const AUDIT_MATRIX_JSON: &str = include_str!("../../docs/METAPLEX_TOKEN_METADATA_MATRIX.json"); + const MAX_SDK_INSTRUCTION_MODULES: usize = 128; + const MIN_SDK_INSTRUCTION_MODULES: usize = 90; + const MIN_PDA_FAMILIES: usize = 8; + const MIN_ACCOUNT_LAYOUTS: usize = 9; + + fn input( + payload: &[u8], + account_count: usize, + failed: bool, + path: &str, + ) -> kb_store_core::CoreInstructionReplayInput { + let keys = (0..account_count).map(|position| return serde_json::json!({ + "accountIndex": position, + "accountKey": format!("account{position}"), + "source": "static", + "signer": match position { 2 | 3 => true, 1 if account_count == 2 => true, _ => false }, + "writable": match position { 0 | 3 => true, _ => false }, + })).collect::>(); + let accounts = (0..account_count) + .map(|position| { + return serde_json::json!({ + "accountIndex": position, + "accountKey": format!("account{position}"), + }); + }) + .collect::>(); + let result = kb_store_core::CoreInstructionReplayInput::new( + format!("signature:{path}"), + "signature", + 42, + path, + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + failed, + if failed { + std::option::Option::Some(serde_json::json!({"InstructionError":[0,"Custom"]})) + } else { + std::option::Option::None + }, + serde_json::Value::Array(keys), + serde_json::Value::Array(accounts), + std::option::Option::Some( + serde_json::json!({"dataBase64": base64::engine::general_purpose::STANDARD.encode(payload)}), + ), + std::option::Option::None, + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Metaplex replay input failed: {error}"), + }; + } + + fn input_with_flags( + payload: &[u8], + flags: &[(bool, bool)], + failed: bool, + path: &str, + ) -> kb_store_core::CoreInstructionReplayInput { + let keys = flags + .iter() + .enumerate() + .map(|(position, (signer, writable))| { + return serde_json::json!({ + "accountIndex": position, + "accountKey": format!("account{position}"), + "source": "static", + "signer": signer, + "writable": writable, + }); + }) + .collect::>(); + let accounts = flags + .iter() + .enumerate() + .map(|(position, _)| { + return serde_json::json!({ + "accountIndex": position, + "accountKey": format!("account{position}"), + }); + }) + .collect::>(); + let result = kb_store_core::CoreInstructionReplayInput::new( + format!("signature:{path}"), + "signature", + 42, + path, + kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + failed, + if failed { + std::option::Option::Some(serde_json::json!({ + "InstructionError": [0, "Custom"] + })) + } else { + std::option::Option::None + }, + serde_json::Value::Array(keys), + serde_json::Value::Array(accounts), + std::option::Option::Some(serde_json::json!({ + "dataBase64": base64::engine::general_purpose::STANDARD.encode(payload) + })), + std::option::Option::None, + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("Metaplex replay input failed: {error}") + }, + }; + } + + fn data_v2() -> mpl_token_metadata::types::DataV2 { + return mpl_token_metadata::types::DataV2 { + name: "Khadhroony".to_string(), + symbol: "KB".to_string(), + uri: "https://example.invalid/metadata.json".to_string(), + seller_fee_basis_points: 500, + creators: std::option::Option::None, + collection: std::option::Option::None, + uses: std::option::Option::None, + }; + } + + fn create_wire() -> std::vec::Vec { + let args = mpl_token_metadata::instructions::CreateMetadataAccountV3InstructionArgs { + data: data_v2(), + is_mutable: true, + collection_details: std::option::Option::None, + }; + let mut wire = std::vec![crate::CREATE_METADATA_ACCOUNT_V3_DISCRIMINATOR]; + let encoded = borsh_0_10::to_vec(&args); + match encoded { + std::result::Result::Ok(mut value) => wire.append(&mut value), + std::result::Result::Err(error) => { + panic!("CreateMetadataAccountV3 encode failed: {error}") + }, + } + return wire; + } + + fn update_wire() -> std::vec::Vec { + let args = mpl_token_metadata::instructions::UpdateMetadataAccountV2InstructionArgs { + data: std::option::Option::Some(data_v2()), + new_update_authority: std::option::Option::None, + primary_sale_happened: std::option::Option::Some(true), + is_mutable: std::option::Option::Some(false), + }; + let mut wire = std::vec![crate::UPDATE_METADATA_ACCOUNT_V2_DISCRIMINATOR]; + let encoded = borsh_0_10::to_vec(&args); + match encoded { + std::result::Result::Ok(mut value) => wire.append(&mut value), + std::result::Result::Err(error) => { + panic!("UpdateMetadataAccountV2 encode failed: {error}") + }, + } + return wire; + } + + fn approve_use_authority_wire(number_of_uses: u64) -> std::vec::Vec { + let args = + mpl_token_metadata::instructions::ApproveUseAuthorityInstructionArgs { number_of_uses }; + let mut wire = std::vec![crate::APPROVE_USE_AUTHORITY_DISCRIMINATOR]; + let encoded = borsh_0_10::to_vec(&args); + match encoded { + std::result::Result::Ok(mut value) => wire.append(&mut value), + std::result::Result::Err(error) => panic!("ApproveUseAuthority encode failed: {error}"), + } + return wire; + } + + fn utilize_wire(number_of_uses: u64) -> std::vec::Vec { + let args = mpl_token_metadata::instructions::UtilizeInstructionArgs { number_of_uses }; + let mut wire = std::vec![crate::UTILIZE_DISCRIMINATOR]; + let encoded = borsh_0_10::to_vec(&args); + match encoded { + std::result::Result::Ok(mut value) => wire.append(&mut value), + std::result::Result::Err(error) => panic!("Utilize encode failed: {error}"), + } + return wire; + } + + fn historical_data() -> mpl_token_metadata::types::Data { + return mpl_token_metadata::types::Data { + name: "Legacy V1".to_string(), + symbol: "KV1".to_string(), + uri: "https://example.invalid/v1.json".to_string(), + seller_fee_basis_points: 125, + creators: std::option::Option::None, + }; + } + + fn historical_create_v1_wire() -> std::vec::Vec { + let mut wire = std::vec![crate::CREATE_METADATA_ACCOUNT_DISCRIMINATOR]; + match borsh_0_10::to_vec(&historical_data()) { + std::result::Result::Ok(mut value) => wire.append(&mut value), + std::result::Result::Err(error) => { + panic!("CreateMetadataAccount data encode failed: {error}") + }, + } + match borsh_0_10::to_vec(&true) { + std::result::Result::Ok(mut value) => wire.append(&mut value), + std::result::Result::Err(error) => { + panic!("CreateMetadataAccount mutable encode failed: {error}") + }, + } + return wire; + } + + fn historical_update_v1_wire() -> std::vec::Vec { + let mut wire = std::vec![crate::UPDATE_METADATA_ACCOUNT_DISCRIMINATOR]; + let data = std::option::Option::Some(historical_data()); + let authority = std::option::Option::Some([7_u8; 32]); + let primary_sale = std::option::Option::Some(true); + for encoded in [ + borsh_0_10::to_vec(&data), + borsh_0_10::to_vec(&authority), + borsh_0_10::to_vec(&primary_sale), + ] { + match encoded { + std::result::Result::Ok(mut value) => wire.append(&mut value), + std::result::Result::Err(error) => { + panic!("UpdateMetadataAccount encode failed: {error}") + }, + } + } + return wire; + } + + fn historical_create_v2_wire() -> std::vec::Vec { + let data = mpl_token_metadata::types::DataV2 { + name: "Legacy Khadhroony".to_string(), + symbol: "KLEG".to_string(), + uri: "https://example.invalid/legacy.json".to_string(), + seller_fee_basis_points: 250, + creators: std::option::Option::None, + collection: std::option::Option::None, + uses: std::option::Option::None, + }; + let mut wire = std::vec![crate::CREATE_METADATA_ACCOUNT_V2_DISCRIMINATOR]; + let encoded_data = borsh_0_10::to_vec(&data); + match encoded_data { + std::result::Result::Ok(mut value) => wire.append(&mut value), + std::result::Result::Err(error) => { + panic!("CreateMetadataAccountV2 data encode failed: {error}") + }, + } + let encoded_is_mutable = borsh_0_10::to_vec(&true); + match encoded_is_mutable { + std::result::Result::Ok(mut value) => wire.append(&mut value), + std::result::Result::Err(error) => { + panic!("CreateMetadataAccountV2 is_mutable encode failed: {error}") + }, + } + return wire; + } + + fn mint_new_edition_wire(discriminator: u8, edition: u64) -> std::vec::Vec { + let mut wire = std::vec![discriminator]; + let args = + mpl_token_metadata::types::MintNewEditionFromMasterEditionViaTokenArgs { edition }; + let encoded = borsh_0_10::to_vec(&args); + match encoded { + std::result::Result::Ok(mut value) => wire.append(&mut value), + std::result::Result::Err(error) => { + panic!("MintNewEditionFromMasterEdition args encode failed: {error}") + }, + } + return wire; + } + + fn create_master_edition_v3_wire(max_supply: std::option::Option) -> std::vec::Vec { + let mut wire = std::vec![crate::CREATE_MASTER_EDITION_V3_DISCRIMINATOR]; + let encoded = borsh_0_10::to_vec(&max_supply); + match encoded { + std::result::Result::Ok(mut value) => wire.append(&mut value), + std::result::Result::Err(error) => { + panic!("CreateMasterEditionV3 max_supply encode failed: {error}") + }, + } + return wire; + } + + fn set_collection_size_wire(discriminator: u8, size: u64) -> std::vec::Vec { + let mut wire = std::vec![discriminator]; + let args = mpl_token_metadata::types::SetCollectionSizeArgs { size }; + let encoded = borsh_0_10::to_vec(&args); + match encoded { + std::result::Result::Ok(mut value) => wire.append(&mut value), + std::result::Result::Err(error) => { + panic!("SetCollectionSize args encode failed: {error}") + }, + } + return wire; + } + + fn programmable_wire( + discriminator: u8, + args: &T, + ) -> std::vec::Vec { + let mut wire = std::vec![discriminator]; + let encoded = match borsh_0_10::BorshSerialize::try_to_vec(args) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("programmable fixture serialization failed: {error}") + }, + }; + wire.extend_from_slice(encoded.as_slice()); + return wire; + } + + fn delegate_wire(args: &mpl_token_metadata::types::DelegateArgs) -> std::vec::Vec { + let mut wire = std::vec![crate::DELEGATE_DISCRIMINATOR]; + let encoded = match borsh_0_10::BorshSerialize::try_to_vec(args) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("delegate fixture serialization failed: {error}") + }, + }; + wire.extend_from_slice(encoded.as_slice()); + return wire; + } + + fn revoke_wire(args: &mpl_token_metadata::types::RevokeArgs) -> std::vec::Vec { + let mut wire = std::vec![crate::REVOKE_DISCRIMINATOR]; + let encoded = match borsh_0_10::BorshSerialize::try_to_vec(args) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("revoke fixture serialization failed: {error}") + }, + }; + wire.extend_from_slice(encoded.as_slice()); + return wire; + } + + #[test] + fn decoder_identity_program_boundary_and_coverage_are_exact() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + assert_eq!( + kb_decoder_api::ProtocolDecoder::decoder_name(&decoder), + "kb_decoder_metadata_metaplex_token_metadata" + ); + assert_eq!( + kb_decoder_api::ProtocolDecoder::program_ids(&decoder), + &[kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID] + ); + assert_eq!(crate::TRACING_TARGET, "kb_decoder_metadata_metaplex_token_metadata"); + assert_eq!(kb_decoder_api::InstructionDecoder::coverage(&decoder).len(), 58); + } + + #[test] + fn printed_edition_mint_variants_decode_exact_contracts() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let token = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + mint_new_edition_wire( + crate::MINT_NEW_EDITION_FROM_MASTER_EDITION_VIA_TOKEN_DISCRIMINATOR, + u64::MAX, + ) + .as_slice(), + &[ + (false, true), + (false, true), + (false, true), + (false, true), + (false, true), + (true, false), + (true, true), + (true, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ], + false, + "printed_edition_token", + ), + ); + assert_eq!(token.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + token.recognized_entry_code.as_deref(), + std::option::Option::Some("mint_new_edition_from_master_edition_via_token") + ); + assert_eq!(token.observations[0].payload_json["parameters"]["edition"], u64::MAX); + assert_eq!(token.observations[0].payload_json["accounts"][4]["role"], "edition_marker"); + let vault = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + mint_new_edition_wire( + crate::MINT_NEW_EDITION_FROM_MASTER_EDITION_VIA_VAULT_PROXY_DISCRIMINATOR, + 248, + ) + .as_slice(), + &[ + (false, true), + (false, true), + (false, true), + (false, true), + (false, true), + (true, false), + (true, true), + (true, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ], + true, + "printed_edition_vault", + ), + ); + assert_eq!(vault.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + vault.recognized_entry_code.as_deref(), + std::option::Option::Some("mint_new_edition_from_master_edition_via_vault_proxy") + ); + assert_eq!(vault.observations[0].payload_json["parameters"]["edition"], 248); + assert_eq!( + vault.observations[0].payload_json["accounts"][14]["role"], + "token_vault_program" + ); + assert!(!vault.observations[0].observation_committed); + } + + fn modern_create_wire() -> std::vec::Vec { + let args = mpl_token_metadata::types::CreateArgs::V1 { + name: "Modern Create".to_string(), + symbol: "MCR".to_string(), + uri: "https://example.invalid/modern-create.json".to_string(), + seller_fee_basis_points: 500, + creators: std::option::Option::None, + primary_sale_happened: false, + is_mutable: true, + token_standard: mpl_token_metadata::types::TokenStandard::NonFungible, + collection: std::option::Option::None, + uses: std::option::Option::None, + collection_details: std::option::Option::None, + rule_set: std::option::Option::None, + decimals: std::option::Option::None, + print_supply: std::option::Option::None, + }; + return programmable_wire(crate::CREATE_DISCRIMINATOR, &args); + } + + #[test] + fn modern_create_decodes_exact_contract() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let result = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + modern_create_wire().as_slice(), + &[ + (false, true), + (false, true), + (true, true), + (true, false), + (true, true), + (true, false), + (false, false), + (false, false), + (false, false), + ], + false, + "modern_create", + ), + ); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(result.recognized_entry_code.as_deref(), std::option::Option::Some("create")); + assert_eq!(result.observations[0].payload_json["accounts"][0]["role"], "metadata"); + assert_eq!( + result.observations[0].payload_json["parameters"]["V1"]["name"], + "Modern Create" + ); + } + + #[test] + fn modern_create_rejects_truncated_invalid_and_suffix_wires() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let truncated = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::CREATE_DISCRIMINATOR], + &[ + (false, true), + (false, true), + (true, true), + (true, false), + (true, true), + (true, false), + (false, false), + (false, false), + (false, false), + ], + false, + "modern_create_truncated", + ), + ); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let invalid = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + modern_create_wire().as_slice(), + &[ + (false, true), + (false, true), + (false, true), + (true, false), + (true, true), + (true, false), + (false, false), + (false, false), + (false, false), + ], + false, + "modern_create_invalid", + ), + ); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let mut suffix_wire = modern_create_wire(); + suffix_wire.push(1); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + suffix_wire.as_slice(), + &[ + (false, true), + (false, true), + (true, true), + (true, false), + (true, true), + (true, false), + (false, false), + (false, false), + (false, false), + ], + false, + "modern_create_suffix", + ), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + fn modern_mint_wire(amount: u64) -> std::vec::Vec { + let args = mpl_token_metadata::types::MintArgs::V1 { + amount, + authorization_data: std::option::Option::None, + }; + return programmable_wire(crate::MINT_DISCRIMINATOR, &args); + } + + #[test] + fn modern_mint_decodes_exact_contract() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let result = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + modern_mint_wire(u64::MAX).as_slice(), + &[ + (false, true), + (false, false), + (false, false), + (false, true), + (false, true), + (false, true), + (true, false), + (false, false), + (true, true), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ], + false, + "modern_mint", + ), + ); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(result.recognized_entry_code.as_deref(), std::option::Option::Some("mint")); + assert_eq!(result.observations[0].payload_json["parameters"]["V1"]["amount"], u64::MAX); + assert_eq!(result.observations[0].payload_json["accounts"][4]["role"], "token_record"); + assert_eq!( + result.observations[0].payload_json["accounts"][14]["role"], + "authorization_rules" + ); + } + + #[test] + fn modern_mint_rejects_truncated_invalid_and_suffix_wires() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let flags = [ + (false, true), + (false, false), + (false, false), + (false, true), + (false, true), + (false, true), + (true, false), + (false, false), + (true, true), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ]; + let truncated = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags(&[crate::MINT_DISCRIMINATOR], &flags, false, "modern_mint_truncated"), + ); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let mut invalid_flags = flags; + invalid_flags[6] = (false, false); + let invalid = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + modern_mint_wire(1).as_slice(), + &invalid_flags, + false, + "modern_mint_invalid", + ), + ); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let mut suffix_wire = modern_mint_wire(2); + suffix_wire.push(1); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags(suffix_wire.as_slice(), &flags, false, "modern_mint_suffix"), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + fn modern_print_wire_v1(edition: u64) -> std::vec::Vec { + let args = mpl_token_metadata::types::PrintArgs::V1 { edition }; + return programmable_wire(crate::PRINT_DISCRIMINATOR, &args); + } + + fn modern_print_wire_v2(edition: u64) -> std::vec::Vec { + let args = mpl_token_metadata::types::PrintArgs::V2 { edition }; + return programmable_wire(crate::PRINT_DISCRIMINATOR, &args); + } + + #[test] + fn modern_print_v1_and_v2_decode_exact_contracts() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let flags = [ + (false, true), + (false, true), + (true, true), + (false, false), + (false, true), + (true, false), + (false, true), + (false, true), + (false, true), + (true, true), + (true, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ]; + let v1 = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + modern_print_wire_v1(u64::MAX).as_slice(), + &flags, + false, + "modern_print_v1", + ), + ); + assert_eq!(v1.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(v1.recognized_entry_code.as_deref(), std::option::Option::Some("print")); + assert_eq!(v1.observations[0].payload_json["parameters"]["V1"]["edition"], u64::MAX); + assert_eq!(v1.observations[0].payload_json["accounts"][8]["role"], "edition_marker_pda"); + let v2 = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags(modern_print_wire_v2(42).as_slice(), &flags, true, "modern_print_v2"), + ); + assert_eq!(v2.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(v2.observations[0].payload_json["parameters"]["V2"]["edition"], 42); + assert!(!v2.observations[0].observation_committed); + } + + #[test] + fn modern_print_rejects_truncated_invalid_and_suffix_wires() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let flags = [ + (false, true), + (false, true), + (true, true), + (false, false), + (false, true), + (true, false), + (false, true), + (false, true), + (false, true), + (true, true), + (true, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ]; + let truncated = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::PRINT_DISCRIMINATOR], + &flags, + false, + "modern_print_truncated", + ), + ); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let mut invalid_flags = flags; + invalid_flags[10] = (false, false); + let invalid = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + modern_print_wire_v1(1).as_slice(), + &invalid_flags, + false, + "modern_print_invalid", + ), + ); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let mut suffix_wire = modern_print_wire_v2(2); + suffix_wire.push(1); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags(suffix_wire.as_slice(), &flags, false, "modern_print_suffix"), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + fn modern_update_wire() -> std::vec::Vec { + let args = mpl_token_metadata::types::UpdateArgs::AsCollectionDelegateV2 { + collection: mpl_token_metadata::types::CollectionToggle::None, + authorization_data: std::option::Option::None, + }; + return programmable_wire(crate::UPDATE_DISCRIMINATOR, &args); + } + + #[test] + fn modern_update_variants_decode_exact_contract() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let flags = [ + (true, false), + (false, false), + (false, false), + (false, false), + (false, true), + (false, false), + (true, true), + (false, false), + (false, false), + (false, false), + (false, false), + ]; + let result = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags(modern_update_wire().as_slice(), &flags, false, "modern_update"), + ); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(result.recognized_entry_code.as_deref(), std::option::Option::Some("update")); + assert_eq!( + result.observations[0].payload_json["parameters"]["AsCollectionDelegateV2"]["collection"], + "None" + ); + assert_eq!(result.observations[0].payload_json["accounts"][4]["role"], "metadata"); + assert_eq!( + result.observations[0].payload_json["accounts"][10]["role"], + "authorization_rules" + ); + } + + #[test] + fn modern_update_rejects_truncated_invalid_and_suffix_wires() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let flags = [ + (true, false), + (false, false), + (false, false), + (false, false), + (false, true), + (false, false), + (true, true), + (false, false), + (false, false), + (false, false), + (false, false), + ]; + let truncated = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::UPDATE_DISCRIMINATOR], + &flags, + false, + "modern_update_truncated", + ), + ); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let mut invalid_flags = flags; + invalid_flags[0] = (false, false); + let invalid = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + modern_update_wire().as_slice(), + &invalid_flags, + false, + "modern_update_invalid", + ), + ); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let mut suffix_wire = modern_update_wire(); + suffix_wire.push(1); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags(suffix_wire.as_slice(), &flags, false, "modern_update_suffix"), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + fn modern_use_wire() -> std::vec::Vec { + let args = mpl_token_metadata::types::UseArgs::V1 { + authorization_data: std::option::Option::None, + }; + return programmable_wire(crate::USE_DISCRIMINATOR, &args); + } + + fn modern_verification_wire( + discriminator: u8, + args: mpl_token_metadata::types::VerificationArgs, + ) -> std::vec::Vec { + return programmable_wire(discriminator, &args); + } + + #[test] + fn modern_use_verify_and_unverify_decode_exact_contracts() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let use_flags = [ + (true, false), + (false, true), + (false, true), + (false, false), + (false, true), + (false, true), + (true, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ]; + let use_result = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags(modern_use_wire().as_slice(), &use_flags, false, "modern_use"), + ); + assert_eq!(use_result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(use_result.recognized_entry_code.as_deref(), std::option::Option::Some("use")); + assert_eq!( + use_result.observations[0].payload_json["parameters"]["V1"]["authorization_data"], + serde_json::Value::Null + ); + let verify_flags = [ + (true, false), + (false, false), + (false, true), + (false, false), + (false, true), + (false, false), + (false, false), + (false, false), + ]; + let verify = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + modern_verification_wire( + crate::VERIFY_DISCRIMINATOR, + mpl_token_metadata::types::VerificationArgs::CollectionV1, + ) + .as_slice(), + &verify_flags, + false, + "modern_verify", + ), + ); + assert_eq!(verify.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(verify.observations[0].payload_json["parameters"], "CollectionV1"); + let unverify = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + modern_verification_wire( + crate::UNVERIFY_DISCRIMINATOR, + mpl_token_metadata::types::VerificationArgs::CreatorV1, + ) + .as_slice(), + &verify_flags, + true, + "modern_unverify", + ), + ); + assert_eq!(unverify.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + unverify.recognized_entry_code.as_deref(), + std::option::Option::Some("unverify") + ); + assert!(!unverify.observations[0].observation_committed); + } + + #[test] + fn modern_use_verify_and_unverify_reject_invalid_wires() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let use_flags = [ + (true, false), + (false, true), + (false, true), + (false, false), + (false, true), + (false, true), + (true, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ]; + let truncated = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::USE_DISCRIMINATOR], + &use_flags, + false, + "modern_use_truncated", + ), + ); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let mut invalid_flags = use_flags; + invalid_flags[0] = (false, false); + let invalid = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + modern_use_wire().as_slice(), + &invalid_flags, + false, + "modern_use_invalid", + ), + ); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let verify_flags = [ + (true, false), + (false, false), + (false, true), + (false, false), + (false, true), + (false, false), + (false, false), + (false, false), + ]; + let mut suffix_wire = modern_verification_wire( + crate::VERIFY_DISCRIMINATOR, + mpl_token_metadata::types::VerificationArgs::CreatorV1, + ); + suffix_wire.push(1); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags(suffix_wire.as_slice(), &verify_flags, false, "modern_verify_suffix"), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn printed_edition_mint_variants_reject_truncated_wire_invalid_flags_and_suffixes() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let truncated = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::MINT_NEW_EDITION_FROM_MASTER_EDITION_VIA_TOKEN_DISCRIMINATOR], + &[(false, true); 13], + false, + "printed_edition_truncated", + ), + ); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let invalid = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + mint_new_edition_wire( + crate::MINT_NEW_EDITION_FROM_MASTER_EDITION_VIA_TOKEN_DISCRIMINATOR, + 1, + ) + .as_slice(), + &[ + (false, true), + (false, true), + (false, true), + (false, true), + (false, true), + (false, false), + (true, true), + (true, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ], + false, + "printed_edition_invalid", + ), + ); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let mut suffix_wire = mint_new_edition_wire( + crate::MINT_NEW_EDITION_FROM_MASTER_EDITION_VIA_VAULT_PROXY_DISCRIMINATOR, + 2, + ); + suffix_wire.push(1); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + suffix_wire.as_slice(), + &[ + (false, true), + (false, true), + (false, true), + (false, true), + (false, true), + (true, false), + (true, true), + (true, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ], + false, + "printed_edition_suffix", + ), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn master_edition_creation_and_conversion_decode_exact_contracts() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let create = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + create_master_edition_v3_wire(std::option::Option::Some(u64::MAX)).as_slice(), + &[ + (false, true), + (false, true), + (true, false), + (true, false), + (true, true), + (false, true), + (false, false), + (false, false), + ], + false, + "master_edition_create", + ), + ); + assert_eq!(create.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + create.recognized_entry_code.as_deref(), + std::option::Option::Some("create_master_edition_v3") + ); + assert_eq!(create.observations[0].payload_json["parameters"]["max_supply"], u64::MAX); + assert_eq!(create.observations[0].payload_json["accounts"][0]["role"], "master_edition"); + let convert = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::CONVERT_MASTER_EDITION_V1_TO_V2_DISCRIMINATOR], + &[(false, true), (false, true), (false, true)], + true, + "master_edition_convert", + ), + ); + assert_eq!(convert.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + convert.recognized_entry_code.as_deref(), + std::option::Option::Some("convert_master_edition_v1_to_v2") + ); + assert!(!convert.observations[0].observation_committed); + } + + #[test] + fn master_edition_group_rejects_truncated_wire_invalid_flags_and_suffixes() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let truncated = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::CREATE_MASTER_EDITION_V3_DISCRIMINATOR], + &[ + (false, true), + (false, true), + (true, false), + (true, false), + (true, true), + (false, true), + (false, false), + (false, false), + ], + false, + "master_edition_truncated", + ), + ); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let invalid = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + create_master_edition_v3_wire(std::option::Option::None).as_slice(), + &[ + (false, true), + (false, true), + (false, false), + (true, false), + (true, true), + (false, true), + (false, false), + (false, false), + ], + false, + "master_edition_invalid", + ), + ); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::CONVERT_MASTER_EDITION_V1_TO_V2_DISCRIMINATOR, 1], + &[(false, true), (false, true), (false, true)], + false, + "master_edition_suffix", + ), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn historical_v1_create_and_update_decode_exact_wires() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let create = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + historical_create_v1_wire().as_slice(), + &[ + (false, true), + (false, false), + (true, false), + (true, true), + (false, false), + (false, false), + (false, false), + ], + false, + "legacy_create_v1", + ), + ); + assert_eq!(create.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + create.recognized_entry_code.as_deref(), + std::option::Option::Some("create_metadata_account") + ); + assert_eq!(create.observations[0].payload_json["parameters"]["data"]["name"], "Legacy V1"); + let update = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + historical_update_v1_wire().as_slice(), + &[(false, true), (true, false)], + true, + "legacy_update_v1", + ), + ); + assert_eq!(update.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + update.recognized_entry_code.as_deref(), + std::option::Option::Some("update_metadata_account") + ); + assert_eq!( + update.observations[0].payload_json["parameters"]["primary_sale_happened"], + true + ); + assert!(!update.observations[0].observation_committed); + } + + #[test] + fn historical_v1_wires_reject_truncation_and_invalid_accounts() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let truncated = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::UPDATE_METADATA_ACCOUNT_DISCRIMINATOR], + &[(false, true), (true, false)], + false, + "legacy_update_v1_truncated", + ), + ); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let invalid = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + historical_create_v1_wire().as_slice(), + &[ + (false, true), + (false, false), + (false, false), + (true, true), + (false, false), + (false, false), + (false, false), + ], + false, + "legacy_create_v1_invalid", + ), + ); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn historical_create_metadata_account_v2_decodes_bounded_data_v2() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let result = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + historical_create_v2_wire().as_slice(), + &[ + (false, true), + (false, false), + (true, false), + (true, true), + (false, false), + (false, false), + (false, false), + ], + false, + "legacy_create_v2", + ), + ); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some("create_metadata_account_v2") + ); + assert_eq!( + result.observations[0].payload_json["parameters"]["data"]["name"], + "Legacy Khadhroony" + ); + assert_eq!(result.observations[0].payload_json["accounts"][2]["role"], "mint_authority"); + } + + #[test] + fn historical_create_metadata_account_v2_rejects_truncated_wire_and_invalid_flags() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let truncated = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::CREATE_METADATA_ACCOUNT_V2_DISCRIMINATOR], + &[ + (false, true), + (false, false), + (true, false), + (true, true), + (false, false), + (false, false), + ], + false, + "legacy_create_v2_truncated", + ), + ); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let invalid = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + historical_create_v2_wire().as_slice(), + &[ + (false, true), + (false, false), + (false, false), + (true, true), + (false, false), + (false, false), + ], + false, + "legacy_create_v2_invalid", + ), + ); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn create_metadata_account_v3_decodes_official_borsh_wire() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let result = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input(create_wire().as_slice(), 6, false, "0"), + ); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some("create_metadata_account_v3") + ); + assert_eq!(result.observations.len(), 1); + assert_eq!(result.observations[0].payload_json["parameters"]["data"]["name"], "Khadhroony"); + assert!(result.observations[0].observation_committed); + } + + #[test] + fn update_metadata_account_v2_preserves_failed_transaction_as_uncommitted() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let result = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input(update_wire().as_slice(), 2, true, "1/0"), + ); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.observations[0].event.source_kind, + kb_model::EventSourceKind::InnerInstruction + ); + assert!(!result.observations[0].observation_committed); + assert_eq!( + result.observations[0].payload_json["parameters"]["primary_sale_happened"], + true + ); + } + + #[test] + fn collection_verification_family_decodes_exact_account_contracts() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let verify = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::VERIFY_COLLECTION_DISCRIMINATOR], + &[ + (false, true), + (true, true), + (true, true), + (false, false), + (false, false), + (false, false), + (false, false), + ], + false, + "2/0", + ), + ); + assert_eq!(verify.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + verify.recognized_entry_code.as_deref(), + std::option::Option::Some("verify_collection") + ); + assert_eq!( + verify.observations[0].payload_json["accounts"][6]["role"], + "collection_authority_record" + ); + let unverify = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::UNVERIFY_COLLECTION_DISCRIMINATOR], + &[(false, true), (true, true), (false, false), (false, false), (false, false)], + true, + "2/1", + ), + ); + assert_eq!(unverify.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!unverify.observations[0].observation_committed); + let set_and_verify = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::SET_AND_VERIFY_COLLECTION_DISCRIMINATOR], + &[ + (false, true), + (true, true), + (true, true), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ], + false, + "3", + ), + ); + assert_eq!(set_and_verify.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + set_and_verify.observations[0].payload_json["accounts"][3]["role"], + "update_authority" + ); + } + + #[test] + fn collection_authority_administration_decodes_exact_account_contracts() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let approve = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::APPROVE_COLLECTION_AUTHORITY_DISCRIMINATOR], + &[ + (false, true), + (false, false), + (true, true), + (true, true), + (false, false), + (false, false), + (false, false), + (false, false), + ], + false, + "approve_collection_authority", + ), + ); + assert_eq!(approve.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + approve.recognized_entry_code.as_deref(), + std::option::Option::Some("approve_collection_authority") + ); + assert_eq!( + approve.observations[0].payload_json["accounts"][0]["role"], + "collection_authority_record" + ); + assert_eq!( + approve.observations[0].payload_json["accounts"][1]["role"], + "new_collection_authority" + ); + assert_eq!(approve.observations[0].payload_json["accounts"][7]["role"], "rent"); + let revoke = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::REVOKE_COLLECTION_AUTHORITY_DISCRIMINATOR], + &[(false, true), (false, true), (true, true), (false, false), (false, false)], + true, + "4/0", + ), + ); + assert_eq!(revoke.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!revoke.observations[0].observation_committed); + assert_eq!( + revoke.observations[0].payload_json["accounts"][1]["role"], + "delegate_authority" + ); + assert_eq!(revoke.observations[0].payload_json["accounts"][2]["role"], "revoke_authority"); + } + + #[test] + fn collection_authority_administration_rejects_invalid_flags_and_suffixes() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let invalid = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::APPROVE_COLLECTION_AUTHORITY_DISCRIMINATOR], + &[ + (false, true), + (false, false), + (true, false), + (true, true), + (false, false), + (false, false), + (false, false), + ], + false, + "approve-invalid-flags", + ), + ); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::REVOKE_COLLECTION_AUTHORITY_DISCRIMINATOR, 0], + &[(false, true), (false, true), (true, true), (false, false), (false, false)], + false, + "revoke-suffix", + ), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn use_authority_family_decodes_parameters_and_exact_account_contracts() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let approve = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + approve_use_authority_wire(u64::MAX).as_slice(), + &[ + (false, true), + (true, true), + (true, true), + (false, false), + (false, true), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ], + false, + "approve_use_authority", + ), + ); + assert_eq!(approve.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(approve.observations[0].payload_json["parameters"]["number_of_uses"], u64::MAX); + assert_eq!( + approve.observations[0].payload_json["accounts"][0]["role"], + "use_authority_record" + ); + assert_eq!(approve.observations[0].payload_json["accounts"][7]["role"], "burner"); + assert_eq!(approve.observations[0].payload_json["accounts"][10]["role"], "rent"); + let revoke = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::REVOKE_USE_AUTHORITY_DISCRIMINATOR], + &[ + (false, true), + (true, true), + (false, false), + (false, true), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ], + true, + "revoke_use_authority/0", + ), + ); + assert_eq!(revoke.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!revoke.observations[0].observation_committed); + assert_eq!( + revoke.observations[0].payload_json["accounts"][3]["role"], + "owner_token_account" + ); + let utilize = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + utilize_wire(3).as_slice(), + &[ + (false, true), + (false, true), + (false, true), + (true, true), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, true), + (false, false), + ], + false, + "utilize", + ), + ); + assert_eq!(utilize.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(utilize.observations[0].payload_json["parameters"]["number_of_uses"], 3); + assert_eq!( + utilize.observations[0].payload_json["accounts"][9]["role"], + "use_authority_record" + ); + assert_eq!(utilize.observations[0].payload_json["accounts"][10]["role"], "burner"); + } + + #[test] + fn use_authority_family_rejects_truncated_wire_invalid_flags_and_suffixes() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let truncated = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::APPROVE_USE_AUTHORITY_DISCRIMINATOR], + &[(false, true); 10], + false, + "approve_use_authority_truncated", + ), + ); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let invalid_flags = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + utilize_wire(1).as_slice(), + &[ + (false, true), + (false, true), + (false, true), + (false, true), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ], + false, + "utilize_invalid_flags", + ), + ); + assert_eq!(invalid_flags.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::REVOKE_USE_AUTHORITY_DISCRIMINATOR, 0], + &[ + (false, true), + (true, true), + (false, false), + (false, true), + (false, false), + (false, false), + (false, false), + (false, false), + ], + false, + "revoke_use_authority_suffix", + ), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn creator_signature_and_primary_sale_family_decode_exact_contracts() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let sign = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::SIGN_METADATA_DISCRIMINATOR], + &[(false, true), (true, false)], + false, + "sign_metadata", + ), + ); + assert_eq!(sign.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(sign.observations[0].payload_json["accounts"][0]["role"], "metadata"); + assert_eq!(sign.observations[0].payload_json["accounts"][1]["role"], "creator"); + let primary_sale = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::UPDATE_PRIMARY_SALE_HAPPENED_VIA_TOKEN_DISCRIMINATOR], + &[(false, true), (true, false), (false, false)], + true, + "update_primary_sale_happened_via_token/0", + ), + ); + assert_eq!(primary_sale.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!primary_sale.observations[0].observation_committed); + assert_eq!(primary_sale.observations[0].payload_json["accounts"][1]["role"], "owner"); + assert_eq!( + primary_sale.observations[0].payload_json["accounts"][2]["role"], + "token_account" + ); + } + + #[test] + fn creator_signature_and_primary_sale_family_reject_invalid_flags_and_suffixes() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let invalid = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::SIGN_METADATA_DISCRIMINATOR], + &[(false, true), (false, false)], + false, + "sign_metadata_invalid", + ), + ); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::UPDATE_PRIMARY_SALE_HAPPENED_VIA_TOKEN_DISCRIMINATOR, 0], + &[(false, true), (true, false), (false, false)], + false, + "primary_sale_suffix", + ), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn sized_collection_verification_family_preserves_writable_collection_contracts() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let flags = &[ + (false, true), + (true, false), + (true, true), + (false, false), + (false, true), + (false, false), + (false, false), + ]; + for (discriminator, code) in [ + ( + crate::VERIFY_SIZED_COLLECTION_ITEM_DISCRIMINATOR, + "verify_sized_collection_item", + ), + ( + crate::UNVERIFY_SIZED_COLLECTION_ITEM_DISCRIMINATOR, + "unverify_sized_collection_item", + ), + ] { + let result = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags(&[discriminator], flags, false, code), + ); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(result.recognized_entry_code.as_deref(), std::option::Option::Some(code)); + assert_eq!( + result.observations[0].payload_json["accounts"][4]["role"], + "collection_metadata" + ); + assert_eq!(result.observations[0].payload_json["accounts"][4]["writable"], true); + assert_eq!( + result.observations[0].payload_json["accounts"][6]["role"], + "collection_authority_record" + ); + } + let set_and_verify = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::SET_AND_VERIFY_SIZED_COLLECTION_ITEM_DISCRIMINATOR], + &[ + (false, true), + (true, false), + (true, true), + (false, false), + (false, false), + (false, true), + (false, false), + (false, false), + ], + true, + "set_and_verify_sized_collection_item", + ), + ); + assert_eq!(set_and_verify.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!set_and_verify.observations[0].observation_committed); + assert_eq!( + set_and_verify.observations[0].payload_json["accounts"][3]["role"], + "update_authority" + ); + assert_eq!(set_and_verify.observations[0].payload_json["accounts"][5]["writable"], true); + } + + #[test] + fn sized_collection_verification_rejects_non_writable_collection_and_suffix() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let invalid_collection = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::VERIFY_SIZED_COLLECTION_ITEM_DISCRIMINATOR], + &[ + (false, true), + (true, false), + (true, true), + (false, false), + (false, false), + (false, false), + ], + false, + "sized-invalid-flags", + ), + ); + assert_eq!(invalid_collection.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::UNVERIFY_SIZED_COLLECTION_ITEM_DISCRIMINATOR, 0], + &[ + (false, true), + (true, false), + (true, true), + (false, false), + (false, true), + (false, false), + ], + false, + "sized-suffix", + ), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn collection_verification_rejects_invalid_flags_and_suffixes() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let invalid_flags = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::VERIFY_COLLECTION_DISCRIMINATOR], + &[ + (false, true), + (false, true), + (true, true), + (false, false), + (false, false), + (false, false), + ], + false, + "4", + ), + ); + assert_eq!(invalid_flags.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::UNVERIFY_COLLECTION_DISCRIMINATOR, 0], + &[(false, true), (true, true), (false, false), (false, false), (false, false)], + false, + "5", + ), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn unknown_truncated_suffix_and_invalid_accounts_fail_closed() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let unknown = + kb_decoder_api::InstructionDecoder::decode(&decoder, &input(&[254], 0, false, "0")); + assert_eq!(unknown.status, kb_decoder_api::DecoderOutcomeStatus::Unsupported); + let truncated = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input(&[crate::CREATE_METADATA_ACCOUNT_V3_DISCRIMINATOR], 6, false, "0"), + ); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let mut suffix = update_wire(); + suffix.push(7); + let suffix_result = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input(suffix.as_slice(), 2, false, "0"), + ); + assert_eq!(suffix_result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let accounts = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input(create_wire().as_slice(), 2, false, "0"), + ); + assert_eq!(accounts.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn audit_matrix_remains_machine_readable_and_bounded() { + let parsed = serde_json::from_str::(AUDIT_MATRIX_JSON); + assert!(parsed.is_ok()); + let matrix = match parsed { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let entries = matrix + .get("current_sdk_instruction_modules") + .and_then(serde_json::Value::as_array); + assert!(entries.is_some_and(|value| return value.len() >= MIN_SDK_INSTRUCTION_MODULES + && value.len() <= MAX_SDK_INSTRUCTION_MODULES)); + assert!( + matrix + .get("pda_families") + .and_then(serde_json::Value::as_array) + .is_some_and(|value| return value.len() >= MIN_PDA_FAMILIES) + ); + assert!( + matrix + .get("account_layouts") + .and_then(serde_json::Value::as_array) + .is_some_and(|value| return value.len() >= MIN_ACCOUNT_LAYOUTS) + ); + } + + #[test] + fn metadata_normalization_and_creator_unverification_decode_exact_contracts() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let puff = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::PUFF_METADATA_DISCRIMINATOR], + &[(false, true)], + false, + "puff_metadata", + ), + ); + assert_eq!(puff.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(puff.observations[0].payload_json["accounts"][0]["role"], "metadata"); + let remove = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::REMOVE_CREATOR_VERIFICATION_DISCRIMINATOR], + &[(false, true), (true, false)], + true, + "remove_creator_verification/0", + ), + ); + assert_eq!(remove.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!remove.observations[0].observation_committed); + assert_eq!(remove.observations[0].payload_json["accounts"][1]["role"], "creator"); + let token_standard = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::SET_TOKEN_STANDARD_DISCRIMINATOR], + &[(false, true), (true, false), (false, false), (false, false)], + false, + "set_token_standard", + ), + ); + assert_eq!(token_standard.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(token_standard.observations[0].payload_json["accounts"][3]["role"], "edition"); + } + + #[test] + fn metadata_normalization_family_rejects_invalid_flags_and_suffixes() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let invalid = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::REMOVE_CREATOR_VERIFICATION_DISCRIMINATOR], + &[(false, true), (false, false)], + false, + "remove_creator_verification_invalid", + ), + ); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::PUFF_METADATA_DISCRIMINATOR, 0], + &[(false, true)], + false, + "puff_metadata_suffix", + ), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let invalid_standard = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::SET_TOKEN_STANDARD_DISCRIMINATOR], + &[(false, true), (false, false), (false, false)], + false, + "set_token_standard_invalid", + ), + ); + assert_eq!(invalid_standard.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn collection_size_variants_decode_exact_contracts() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let direct = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + set_collection_size_wire(crate::SET_COLLECTION_SIZE_DISCRIMINATOR, u64::MAX) + .as_slice(), + &[(false, true), (true, true), (false, false), (false, false)], + false, + "set_collection_size", + ), + ); + assert_eq!(direct.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(direct.observations[0].payload_json["parameters"]["size"], u64::MAX); + assert_eq!( + direct.observations[0].payload_json["accounts"][3]["role"], + "collection_authority_record" + ); + let bubblegum = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + set_collection_size_wire(crate::BUBBLEGUM_SET_COLLECTION_SIZE_DISCRIMINATOR, 42) + .as_slice(), + &[(false, true), (true, false), (false, false), (true, false), (false, false)], + true, + "bubblegum_set_collection_size/0", + ), + ); + assert_eq!(bubblegum.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(bubblegum.observations[0].payload_json["parameters"]["size"], 42); + assert_eq!( + bubblegum.observations[0].payload_json["accounts"][3]["role"], + "bubblegum_signer" + ); + assert!(!bubblegum.observations[0].observation_committed); + } + + #[test] + fn collection_size_variants_reject_truncated_wire_invalid_flags_and_suffixes() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let truncated = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::SET_COLLECTION_SIZE_DISCRIMINATOR], + &[(false, true), (true, true), (false, false)], + false, + "set_collection_size_truncated", + ), + ); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let invalid = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + set_collection_size_wire(crate::BUBBLEGUM_SET_COLLECTION_SIZE_DISCRIMINATOR, 1) + .as_slice(), + &[(false, true), (true, true), (false, false), (true, false)], + false, + "bubblegum_set_collection_size_invalid", + ), + ); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let mut suffix_wire = set_collection_size_wire(crate::SET_COLLECTION_SIZE_DISCRIMINATOR, 2); + suffix_wire.push(0); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + suffix_wire.as_slice(), + &[(false, true), (true, true), (false, false)], + false, + "set_collection_size_suffix", + ), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn programmable_delegate_and_revoke_decode_exact_contracts() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let flags = &[ + (false, true), + (false, false), + (false, true), + (false, false), + (false, true), + (false, false), + (false, true), + (true, false), + (true, true), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ]; + let delegate = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + delegate_wire(&mpl_token_metadata::types::DelegateArgs::StandardV1 { + amount: u64::MAX, + }) + .as_slice(), + flags, + false, + "delegate", + ), + ); + assert_eq!(delegate.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + delegate.observations[0].payload_json["parameters"]["StandardV1"]["amount"], + u64::MAX + ); + assert_eq!( + delegate.observations[0].payload_json["accounts"][10]["role"], + "sysvar_instructions" + ); + let revoke = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + revoke_wire(&mpl_token_metadata::types::RevokeArgs::StandardV1).as_slice(), + flags, + true, + "revoke/0", + ), + ); + assert_eq!(revoke.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!revoke.observations[0].observation_committed); + assert_eq!(revoke.observations[0].payload_json["parameters"], "StandardV1"); + } + + #[test] + fn programmable_delegate_and_revoke_reject_truncated_invalid_and_suffix_wires() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let flags = &[ + (false, false), + (false, false), + (false, true), + (false, false), + (false, false), + (false, false), + (false, false), + (true, false), + (true, true), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ]; + let truncated = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags(&[crate::DELEGATE_DISCRIMINATOR], flags, false, "delegate_truncated"), + ); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let mut invalid_flags = flags.to_vec(); + invalid_flags[7] = (false, false); + let invalid = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + revoke_wire(&mpl_token_metadata::types::RevokeArgs::TransferV1).as_slice(), + invalid_flags.as_slice(), + false, + "revoke_invalid", + ), + ); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let mut suffix_wire = + delegate_wire(&mpl_token_metadata::types::DelegateArgs::CollectionV1 { + authorization_data: std::option::Option::None, + }); + suffix_wire.push(0); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags(suffix_wire.as_slice(), flags, false, "delegate_suffix"), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn programmable_lifecycle_transfer_lock_unlock_burn_and_close_decode_exact_contracts() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let transfer_flags = &[ + (false, true), + (false, false), + (false, true), + (false, false), + (false, false), + (false, true), + (false, false), + (false, true), + (false, true), + (true, false), + (true, true), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ]; + let transfer = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + programmable_wire( + crate::TRANSFER_DISCRIMINATOR, + &mpl_token_metadata::types::TransferArgs::V1 { + amount: u64::MAX, + authorization_data: std::option::Option::None, + }, + ) + .as_slice(), + transfer_flags, + false, + "transfer", + ), + ); + assert_eq!(transfer.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(transfer.observations[0].payload_json["parameters"]["V1"]["amount"], u64::MAX); + assert_eq!( + transfer.observations[0].payload_json["accounts"][8]["role"], + "destination_token_record" + ); + let lock_flags = &[ + (true, false), + (false, false), + (false, true), + (false, false), + (false, true), + (false, false), + (false, true), + (true, true), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ]; + let lock = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + programmable_wire( + crate::LOCK_DISCRIMINATOR, + &mpl_token_metadata::types::LockArgs::V1 { + authorization_data: std::option::Option::None, + }, + ) + .as_slice(), + lock_flags, + false, + "lock", + ), + ); + assert_eq!(lock.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + let unlock = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + programmable_wire( + crate::UNLOCK_DISCRIMINATOR, + &mpl_token_metadata::types::UnlockArgs::V1 { + authorization_data: std::option::Option::None, + }, + ) + .as_slice(), + lock_flags, + true, + "unlock/0", + ), + ); + assert_eq!(unlock.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!unlock.observations[0].observation_committed); + let burn_flags = &[ + (true, true), + (false, true), + (false, true), + (false, true), + (false, true), + (false, true), + (false, true), + (false, false), + (false, false), + (false, true), + (false, true), + (false, false), + (false, false), + (false, false), + ]; + let burn = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + programmable_wire( + crate::BURN_DISCRIMINATOR, + &mpl_token_metadata::types::BurnArgs::V1 { amount: u64::MAX }, + ) + .as_slice(), + burn_flags, + false, + "burn", + ), + ); + assert_eq!(burn.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(burn.observations[0].payload_json["parameters"]["V1"]["amount"], u64::MAX); + let close = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::CLOSE_ACCOUNTS_DISCRIMINATOR], + &[(false, true), (false, true), (false, true), (true, false), (false, true)], + false, + "close_accounts", + ), + ); + assert_eq!(close.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(close.observations[0].payload_json["accounts"][4]["role"], "destination"); + } + + #[test] + fn programmable_lifecycle_rejects_truncated_invalid_and_suffix_wires() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let truncated = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::TRANSFER_DISCRIMINATOR], + &[(false, true); 17], + false, + "transfer_truncated", + ), + ); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let invalid = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + programmable_wire( + crate::BURN_DISCRIMINATOR, + &mpl_token_metadata::types::BurnArgs::V1 { amount: 1 }, + ) + .as_slice(), + &[ + (false, true), + (false, true), + (false, true), + (false, true), + (false, true), + (false, true), + (false, true), + (false, false), + (false, false), + (false, true), + (false, true), + (false, false), + (false, false), + (false, false), + ], + false, + "burn_invalid", + ), + ); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::CLOSE_ACCOUNTS_DISCRIMINATOR, 0], + &[(false, true), (false, true), (false, true), (true, false), (false, true)], + false, + "close_suffix", + ), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn legacy_delegated_freeze_and_thaw_decode_exact_contracts() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let flags = &[(true, true), (false, true), (false, false), (false, false), (false, false)]; + let freeze = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::FREEZE_DELEGATED_ACCOUNT_DISCRIMINATOR], + flags, + false, + "freeze_delegated_account", + ), + ); + assert_eq!(freeze.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(freeze.observations[0].payload_json["accounts"][0]["role"], "delegate"); + assert_eq!(freeze.observations[0].payload_json["accounts"][4]["role"], "token_program"); + let thaw = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::THAW_DELEGATED_ACCOUNT_DISCRIMINATOR], + flags, + true, + "thaw_delegated_account/0", + ), + ); + assert_eq!(thaw.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!thaw.observations[0].observation_committed); + } + + #[test] + fn legacy_delegated_freeze_and_thaw_reject_invalid_flags_and_suffixes() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let invalid = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::FREEZE_DELEGATED_ACCOUNT_DISCRIMINATOR], + &[(false, true), (false, true), (false, false), (false, false), (false, false)], + false, + "freeze_delegated_account_invalid", + ), + ); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::THAW_DELEGATED_ACCOUNT_DISCRIMINATOR, 0], + &[(true, true), (false, true), (false, false), (false, false), (false, false)], + false, + "thaw_delegated_account_suffix", + ), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn legacy_nft_burn_and_printing_token_mint_decode_exact_contracts() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let burn_nft = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::BURN_NFT_DISCRIMINATOR], + &[ + (false, true), + (true, true), + (false, true), + (false, true), + (false, true), + (false, false), + (false, true), + ], + false, + "burn_nft", + ), + ); + assert_eq!(burn_nft.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + burn_nft.observations[0].payload_json["accounts"][6]["role"], + "collection_metadata" + ); + let burn_edition = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::BURN_EDITION_NFT_DISCRIMINATOR], + &[ + (false, true), + (true, true), + (false, true), + (false, false), + (false, true), + (false, false), + (false, true), + (false, true), + (false, true), + (false, false), + ], + true, + "burn_edition_nft/0", + ), + ); + assert_eq!(burn_edition.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!burn_edition.observations[0].observation_committed); + assert_eq!( + burn_edition.observations[0].payload_json["accounts"][8]["role"], + "edition_marker_account" + ); + let printing = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::DEPRECATED_MINT_NEW_EDITION_VIA_PRINTING_TOKEN_DISCRIMINATOR], + &[ + (false, true), + (false, true), + (false, true), + (false, true), + (true, false), + (false, true), + (false, true), + (false, true), + (true, false), + (true, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, true), + ], + false, + "deprecated_printing_token", + ), + ); + assert_eq!(printing.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + printing.observations[0].payload_json["accounts"][15]["role"], + "reservation_list" + ); + } + + #[test] + fn legacy_nft_burn_and_printing_token_mint_reject_invalid_flags_and_suffixes() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let invalid = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::BURN_NFT_DISCRIMINATOR], + &[ + (false, true), + (false, true), + (false, true), + (false, true), + (false, true), + (false, false), + ], + false, + "burn_nft_invalid", + ), + ); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::BURN_EDITION_NFT_DISCRIMINATOR, 0], + &[ + (false, true), + (true, true), + (false, true), + (false, false), + (false, true), + (false, false), + (false, true), + (false, true), + (false, true), + (false, false), + ], + false, + "burn_edition_suffix", + ), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let truncated_accounts = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::DEPRECATED_MINT_NEW_EDITION_VIA_PRINTING_TOKEN_DISCRIMINATOR], + &[(false, true); 14], + false, + "deprecated_printing_token_truncated", + ), + ); + assert_eq!(truncated_accounts.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn escrow_and_collect_family_decode_exact_contracts() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let create = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::CREATE_ESCROW_ACCOUNT_DISCRIMINATOR], + &[ + (false, true), + (false, true), + (false, false), + (false, false), + (false, false), + (true, true), + (false, false), + (false, false), + (true, false), + ], + false, + "create_escrow_account", + ), + ); + assert_eq!(create.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(create.observations[0].payload_json["accounts"][8]["role"], "authority"); + let close = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::CLOSE_ESCROW_ACCOUNT_DISCRIMINATOR], + &[ + (false, true), + (false, true), + (false, false), + (false, false), + (false, false), + (true, true), + (false, false), + (false, false), + ], + true, + "close_escrow_account/0", + ), + ); + assert_eq!(close.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!close.observations[0].observation_committed); + let mut transfer_wire = vec![crate::TRANSFER_OUT_OF_ESCROW_DISCRIMINATOR]; + transfer_wire.extend_from_slice(&u64::MAX.to_le_bytes()); + let transfer = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + transfer_wire.as_slice(), + &[ + (false, false), + (false, true), + (true, true), + (false, false), + (false, true), + (false, true), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + (true, false), + ], + false, + "transfer_out_of_escrow", + ), + ); + assert_eq!(transfer.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(transfer.observations[0].payload_json["parameters"]["amount"], u64::MAX); + assert_eq!(transfer.observations[0].payload_json["accounts"][12]["role"], "authority"); + let collect = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::COLLECT_DISCRIMINATOR], + &[(true, false), (false, false)], + false, + "collect", + ), + ); + assert_eq!(collect.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(collect.observations[0].payload_json["accounts"][1]["role"], "recipient"); + } + + #[test] + fn escrow_and_collect_family_reject_truncated_invalid_and_suffix_wires() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let truncated = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::TRANSFER_OUT_OF_ESCROW_DISCRIMINATOR], + &[ + (false, false), + (false, true), + (true, true), + (false, false), + (false, true), + (false, true), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ], + false, + "transfer_out_of_escrow_truncated", + ), + ); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let invalid = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::CREATE_ESCROW_ACCOUNT_DISCRIMINATOR], + &[ + (false, true), + (false, true), + (false, false), + (false, false), + (false, false), + (false, true), + (false, false), + (false, false), + ], + false, + "create_escrow_account_invalid", + ), + ); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::COLLECT_DISCRIMINATOR, 0], + &[(true, false), (false, false)], + false, + "collect_suffix", + ), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn resize_and_migrate_decode_exact_contracts() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let resize = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::RESIZE_DISCRIMINATOR], + &[ + (false, true), + (false, true), + (false, false), + (true, true), + (true, false), + (false, false), + (false, false), + ], + false, + "resize", + ), + ); + assert_eq!(resize.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(resize.observations[0].payload_json["accounts"][4]["role"], "authority"); + let migrate = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::MIGRATE_DISCRIMINATOR], + &[ + (false, true), + (false, true), + (false, true), + (false, false), + (false, false), + (true, true), + (true, false), + (false, false), + (false, false), + (false, true), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ], + true, + "migrate/0", + ), + ); + assert_eq!(migrate.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!migrate.observations[0].observation_committed); + assert_eq!(migrate.observations[0].payload_json["accounts"][9]["role"], "token_record"); + } + + #[test] + fn resize_and_migrate_reject_invalid_wires() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let invalid_resize = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::RESIZE_DISCRIMINATOR], + &[ + (false, true), + (false, true), + (false, false), + (false, true), + (true, false), + (false, false), + (false, false), + ], + false, + "resize_invalid", + ), + ); + assert_eq!(invalid_resize.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::MIGRATE_DISCRIMINATOR, 0], + &[ + (false, true), + (false, true), + (false, true), + (false, false), + (false, false), + (true, true), + (true, false), + (false, false), + (false, false), + (false, true), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ], + false, + "migrate_suffix", + ), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn omitted_historical_discriminators_decode_exact_contracts() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + type HistoricalCase<'a> = (&'a [u8], &'a [(bool, bool)], &'a str, usize, &'a str); + let cases: &[HistoricalCase<'_>] = &[ + ( + &[crate::DEPRECATED_CREATE_MASTER_EDITION_DISCRIMINATOR], + &[ + (false, true), + (false, true), + (false, true), + (false, true), + (true, false), + (true, false), + (true, false), + (false, false), + (true, false), + (false, false), + (false, false), + (false, false), + (true, false), + ], + "deprecated_create_master_edition", + 12, + "one_time_printing_authorization_mint_authority", + ), + ( + &[crate::DEPRECATED_SET_RESERVATION_LIST_DISCRIMINATOR], + &[(false, true), (false, true), (true, false)], + "deprecated_set_reservation_list", + 2, + "resource", + ), + ( + &[crate::DEPRECATED_CREATE_RESERVATION_LIST_DISCRIMINATOR], + &[ + (false, true), + (true, false), + (true, false), + (false, false), + (false, false), + (false, false), + (false, false), + (false, false), + ], + "deprecated_create_reservation_list", + 4, + "resource", + ), + ( + &[crate::DEPRECATED_MINT_PRINTING_TOKENS_VIA_TOKEN_DISCRIMINATOR], + &[ + (false, true), + (false, true), + (false, true), + (false, true), + (true, false), + (false, false), + (false, false), + (false, false), + (false, false), + ], + "deprecated_mint_printing_tokens_via_token", + 4, + "burn_authority", + ), + ( + &[crate::DEPRECATED_MINT_PRINTING_TOKENS_DISCRIMINATOR], + &[ + (false, true), + (false, true), + (true, false), + (false, false), + (false, false), + (false, false), + (false, false), + ], + "deprecated_mint_printing_tokens", + 2, + "update_authority", + ), + ( + &[crate::CREATE_MASTER_EDITION_DISCRIMINATOR], + &[ + (false, true), + (false, true), + (true, false), + (true, false), + (true, true), + (false, false), + (false, false), + (false, false), + (false, false), + ], + "create_master_edition", + 4, + "payer", + ), + ]; + for (wire, flags, path, role_index, role) in cases { + let decoded = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags(wire, flags, false, path), + ); + assert_eq!(decoded.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + decoded.observations[0].payload_json["accounts"][*role_index]["role"], + *role + ); + } + } + + #[test] + fn omitted_historical_discriminators_reject_invalid_wires() { + let decoder = crate::MetadataMetaplexTokenMetadataDecoder; + let invalid = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::DEPRECATED_SET_RESERVATION_LIST_DISCRIMINATOR], + &[(false, true), (false, true), (false, false)], + false, + "deprecated_set_reservation_list_invalid", + ), + ); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let suffix = kb_decoder_api::InstructionDecoder::decode( + &decoder, + &input_with_flags( + &[crate::CREATE_MASTER_EDITION_DISCRIMINATOR, 0], + &[ + (false, true), + (false, true), + (true, false), + (true, false), + (true, true), + (false, false), + (false, false), + (false, false), + (false, false), + ], + false, + "create_master_edition_suffix", + ), + ); + assert_eq!(suffix.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/instruction.rs b/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/instruction.rs new file mode 100644 index 0000000..9afbc56 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/instruction.rs @@ -0,0 +1,2732 @@ +// file: kb_decoder_metadata_metaplex_token_metadata/src/instruction.rs +// version: 26 + +//! Bounded Metaplex Token Metadata instruction wire decoding. + +use base64::Engine; // rust-rules: trait-import +use sha2::Digest; // rust-rules: trait-import + +pub(crate) struct ParsedInstruction { + pub(crate) code: &'static str, + pub(crate) discriminator: u8, + pub(crate) parameters: serde_json::Value, + pub(crate) consumed: usize, +} + +#[derive(Clone, Debug)] +struct ResolvedAccount { + position: usize, + account_index: u64, + account_key: std::string::String, + signer: bool, + writable: bool, + source: serde_json::Value, +} + +pub(crate) fn entry_for_discriminator( + discriminator: u8, +) -> std::option::Option<(u8, &'static str, bool)> { + return crate::INSTRUCTION_ENTRIES + .iter() + .copied() + .find(|(candidate, _, _)| return *candidate == discriminator); +} + +pub(crate) fn payload_discriminator( + input: &kb_store_core::CoreInstructionReplayInput, +) -> std::option::Option { + return decode_payload(input).ok().and_then(|value| return value.first().copied()); +} + +pub(crate) fn decode( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_decoder_api::DecoderExecutionResult { + let bytes = match decode_payload(input) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return failed( + std::option::Option::None, + "metaplex_token_metadata_payload_unavailable", + error.to_string(), + ); + }, + }; + let discriminator = match bytes.first().copied() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return failed( + std::option::Option::None, + "metaplex_token_metadata_payload_empty", + "Metaplex Token Metadata instruction payload is empty".to_string(), + ); + }, + }; + let entry = match crate::entry_for_discriminator(discriminator) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Unsupported, + recognized_entry_code: std::option::Option::None, + observations: std::vec::Vec::new(), + diagnostics: std::vec![kb_decoder_api::DecoderDiagnostic { + code: "unknown_metaplex_token_metadata_discriminator".to_string(), + message: format!( + "unknown Metaplex Token Metadata discriminator {discriminator}; payloadPrefixHex={}", + hex_prefix(bytes.as_slice()) + ), + retriable: false, + }], + }; + }, + }; + let parsed = match parse_instruction(bytes.as_slice()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => { + return failed( + std::option::Option::Some(entry.1), + "malformed_metaplex_token_metadata_instruction", + format!("{message}; payloadPrefixHex={}", hex_prefix(bytes.as_slice())), + ); + }, + }; + let accounts = match resolve_accounts(input) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return failed( + std::option::Option::Some(parsed.code), + "metaplex_token_metadata_accounts_malformed", + error.to_string(), + ); + }, + }; + let validation = validate_accounts(parsed.code, accounts.as_slice()); + if let std::result::Result::Err(message) = validation { + return failed( + std::option::Option::Some(parsed.code), + "metaplex_token_metadata_accounts_invalid", + message, + ); + } + let suffix_length = bytes.len().saturating_sub(parsed.consumed); + if suffix_length != 0 { + return failed( + std::option::Option::Some(parsed.code), + "metaplex_token_metadata_suffix_forbidden", + format!("{} unexpected suffix bytes", suffix_length), + ); + } + let observation = observation( + input, + &parsed, + project_accounts(parsed.code, accounts.as_slice()), + bytes.as_slice(), + ); + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Decoded, + recognized_entry_code: std::option::Option::Some(parsed.code.to_string()), + observations: std::vec![observation], + diagnostics: std::vec::Vec::new(), + }; +} + +fn decode_payload( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_core::Result> { + let payload = match input.instruction_payload_json.as_ref() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Metaplex Token Metadata instruction payload is not retained", + )); + }, + }; + let encoded = match payload.get("dataBase64").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Metaplex Token Metadata payload does not contain dataBase64", + )); + }, + }; + if encoded.len() > crate::MAX_BASE64_BYTES { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Metaplex Token Metadata base64 payload exceeds {} bytes", + crate::MAX_BASE64_BYTES + ))); + } + let decoded = match base64::engine::general_purpose::STANDARD.decode(encoded.as_bytes()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Metaplex Token Metadata payload is not valid base64: {error}" + ))); + }, + }; + if decoded.len() > crate::MAX_INSTRUCTION_BYTES { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Metaplex Token Metadata payload exceeds {} decoded bytes", + crate::MAX_INSTRUCTION_BYTES + ))); + } + return std::result::Result::Ok(decoded); +} + +fn parse_instruction( + bytes: &[u8], +) -> std::result::Result { + let discriminator = match bytes.first().copied() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err("empty instruction".to_string()); + }, + }; + return match discriminator { + crate::DEPRECATED_CREATE_MASTER_EDITION_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "deprecated_create_master_edition", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::DEPRECATED_SET_RESERVATION_LIST_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "deprecated_set_reservation_list", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::DEPRECATED_CREATE_RESERVATION_LIST_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "deprecated_create_reservation_list", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::DEPRECATED_MINT_PRINTING_TOKENS_VIA_TOKEN_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "deprecated_mint_printing_tokens_via_token", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::DEPRECATED_MINT_PRINTING_TOKENS_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "deprecated_mint_printing_tokens", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::CREATE_MASTER_EDITION_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "create_master_edition", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::DEPRECATED_MINT_NEW_EDITION_VIA_PRINTING_TOKEN_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "deprecated_mint_new_edition_from_master_edition_via_printing_token", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::MINT_NEW_EDITION_FROM_MASTER_EDITION_VIA_TOKEN_DISCRIMINATOR + | crate::MINT_NEW_EDITION_FROM_MASTER_EDITION_VIA_VAULT_PROXY_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let args = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("MintNewEditionFromMasterEdition edition Borsh decode failed: {error}")), + }; + let code = if discriminator + == crate::MINT_NEW_EDITION_FROM_MASTER_EDITION_VIA_TOKEN_DISCRIMINATOR + { + "mint_new_edition_from_master_edition_via_token" + } else { + "mint_new_edition_from_master_edition_via_vault_proxy" + }; + std::result::Result::Ok(crate::ParsedInstruction { + code, + discriminator, + parameters: serde_json::json!({"edition": args.edition}), + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::CONVERT_MASTER_EDITION_V1_TO_V2_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "convert_master_edition_v1_to_v2", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::CREATE_MASTER_EDITION_V3_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let max_supply = + match as borsh_0_10::BorshDeserialize>::deserialize( + &mut remaining, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "CreateMasterEditionV3 max_supply Borsh decode failed: {error}" + )); + }, + }; + std::result::Result::Ok(crate::ParsedInstruction { + code: "create_master_edition_v3", + discriminator, + parameters: serde_json::json!({"max_supply": max_supply}), + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::UPDATE_PRIMARY_SALE_HAPPENED_VIA_TOKEN_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "update_primary_sale_happened_via_token", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::SIGN_METADATA_DISCRIMINATOR => std::result::Result::Ok(crate::ParsedInstruction { + code: "sign_metadata", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }), + crate::PUFF_METADATA_DISCRIMINATOR => std::result::Result::Ok(crate::ParsedInstruction { + code: "puff_metadata", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }), + crate::REMOVE_CREATOR_VERIFICATION_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "remove_creator_verification", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::SET_TOKEN_STANDARD_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "set_token_standard", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::CREATE_METADATA_ACCOUNT_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let data = + match ::deserialize( + &mut remaining, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "CreateMetadataAccount data Borsh decode failed: {error}" + )); + }, + }; + let is_mutable = + match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "CreateMetadataAccount is_mutable Borsh decode failed: {error}" + )); + }, + }; + match validate_data(&data) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let data_json = match serde_json::to_value(&data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "CreateMetadataAccount data JSON projection failed: {error}" + )); + }, + }; + std::result::Result::Ok(crate::ParsedInstruction { + code: "create_metadata_account", + discriminator, + parameters: serde_json::json!({"data": data_json, "is_mutable": is_mutable}), + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::UPDATE_METADATA_ACCOUNT_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let data = match as borsh_0_10::BorshDeserialize>::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("UpdateMetadataAccount data Borsh decode failed: {error}")), + }; + let new_update_authority = + match as borsh_0_10::BorshDeserialize>::deserialize( + &mut remaining, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "UpdateMetadataAccount authority Borsh decode failed: {error}" + )); + }, + }; + let primary_sale_happened = + match as borsh_0_10::BorshDeserialize>::deserialize( + &mut remaining, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "UpdateMetadataAccount primary sale Borsh decode failed: {error}" + )); + }, + }; + if let std::option::Option::Some(value) = data.as_ref() { + match validate_data(value) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + let data_json = match serde_json::to_value(&data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "UpdateMetadataAccount data JSON projection failed: {error}" + )); + }, + }; + let authority_json = new_update_authority.map(|value| return hex_bytes(&value)); + std::result::Result::Ok(crate::ParsedInstruction { + code: "update_metadata_account", + discriminator, + parameters: serde_json::json!({"data": data_json, "new_update_authority_hex": authority_json, "primary_sale_happened": primary_sale_happened}), + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::CREATE_METADATA_ACCOUNT_V2_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let data = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("CreateMetadataAccountV2 data Borsh decode failed: {error}")), + }; + let is_mutable = + match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "CreateMetadataAccountV2 is_mutable Borsh decode failed: {error}" + )); + }, + }; + match validate_data_v2(&data) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let data_json = match serde_json::to_value(&data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "CreateMetadataAccountV2 data JSON projection failed: {error}" + )); + }, + }; + std::result::Result::Ok(crate::ParsedInstruction { + code: "create_metadata_account_v2", + discriminator, + parameters: serde_json::json!({ + "data": data_json, + "is_mutable": is_mutable, + }), + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::CREATE_METADATA_ACCOUNT_V3_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let args = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("CreateMetadataAccountV3 Borsh decode failed: {error}")), + }; + match validate_data_v2(&args.data) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let parameters = match serde_json::to_value(&args) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "CreateMetadataAccountV3 JSON projection failed: {error}" + )); + }, + }; + std::result::Result::Ok(crate::ParsedInstruction { + code: "create_metadata_account_v3", + discriminator, + parameters, + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::TRANSFER_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let args = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("TransferArgs Borsh decode failed: {error}")), + }; + let parameters = match serde_json::to_value(&args) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "TransferArgs JSON projection failed: {error}" + )); + }, + }; + std::result::Result::Ok(crate::ParsedInstruction { + code: "transfer", + discriminator, + parameters, + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::LOCK_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let args = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("LockArgs Borsh decode failed: {error}")), + }; + let parameters = match serde_json::to_value(&args) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "LockArgs JSON projection failed: {error}" + )); + }, + }; + std::result::Result::Ok(crate::ParsedInstruction { + code: "lock", + discriminator, + parameters, + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::UNLOCK_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let args = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("UnlockArgs Borsh decode failed: {error}")), + }; + let parameters = match serde_json::to_value(&args) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "UnlockArgs JSON projection failed: {error}" + )); + }, + }; + std::result::Result::Ok(crate::ParsedInstruction { + code: "unlock", + discriminator, + parameters, + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::CREATE_ESCROW_ACCOUNT_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "create_escrow_account", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::CLOSE_ESCROW_ACCOUNT_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "close_escrow_account", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::TRANSFER_OUT_OF_ESCROW_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let amount = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "TransferOutOfEscrow amount Borsh decode failed: {error}" + )); + }, + }; + std::result::Result::Ok(crate::ParsedInstruction { + code: "transfer_out_of_escrow", + discriminator, + parameters: serde_json::json!({"amount": amount}), + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::COLLECT_DISCRIMINATOR => std::result::Result::Ok(crate::ParsedInstruction { + code: "collect", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }), + crate::BURN_NFT_DISCRIMINATOR => std::result::Result::Ok(crate::ParsedInstruction { + code: "burn_nft", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }), + crate::BURN_EDITION_NFT_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "burn_edition_nft", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::CREATE_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let args = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("CreateArgs Borsh decode failed: {error}")), + }; + let parameters = match serde_json::to_value(&args) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "CreateArgs JSON projection failed: {error}" + )); + }, + }; + std::result::Result::Ok(crate::ParsedInstruction { + code: "create", + discriminator, + parameters, + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::MINT_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let args = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("MintArgs Borsh decode failed: {error}")), + }; + let parameters = match serde_json::to_value(&args) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "MintArgs JSON projection failed: {error}" + )); + }, + }; + std::result::Result::Ok(crate::ParsedInstruction { + code: "mint", + discriminator, + parameters, + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::PRINT_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let args = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("PrintArgs Borsh decode failed: {error}")), + }; + let parameters = match serde_json::to_value(&args) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "PrintArgs JSON projection failed: {error}" + )); + }, + }; + std::result::Result::Ok(crate::ParsedInstruction { + code: "print", + discriminator, + parameters, + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::MIGRATE_DISCRIMINATOR => std::result::Result::Ok(crate::ParsedInstruction { + code: "migrate", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }), + crate::RESIZE_DISCRIMINATOR => std::result::Result::Ok(crate::ParsedInstruction { + code: "resize", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }), + crate::USE_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let args = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("UseArgs Borsh decode failed: {error}")), + }; + let parameters = match serde_json::to_value(&args) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "UseArgs JSON projection failed: {error}" + )); + }, + }; + std::result::Result::Ok(crate::ParsedInstruction { + code: "use", + discriminator, + parameters, + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::VERIFY_DISCRIMINATOR | crate::UNVERIFY_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let args = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("VerificationArgs Borsh decode failed: {error}")), + }; + let parameters = match serde_json::to_value(&args) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "VerificationArgs JSON projection failed: {error}" + )); + }, + }; + let code = + if discriminator == crate::VERIFY_DISCRIMINATOR { "verify" } else { "unverify" }; + std::result::Result::Ok(crate::ParsedInstruction { + code, + discriminator, + parameters, + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::UPDATE_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let args = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("UpdateArgs Borsh decode failed: {error}")), + }; + let parameters = match serde_json::to_value(&args) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "UpdateArgs JSON projection failed: {error}" + )); + }, + }; + std::result::Result::Ok(crate::ParsedInstruction { + code: "update", + discriminator, + parameters, + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::BURN_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let args = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("BurnArgs Borsh decode failed: {error}")), + }; + let parameters = match serde_json::to_value(&args) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "BurnArgs JSON projection failed: {error}" + )); + }, + }; + std::result::Result::Ok(crate::ParsedInstruction { + code: "burn", + discriminator, + parameters, + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::CLOSE_ACCOUNTS_DISCRIMINATOR => std::result::Result::Ok(crate::ParsedInstruction { + code: "close_accounts", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }), + crate::DELEGATE_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let args = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("DelegateArgs Borsh decode failed: {error}")), + }; + let parameters = match serde_json::to_value(&args) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "DelegateArgs JSON projection failed: {error}" + )); + }, + }; + std::result::Result::Ok(crate::ParsedInstruction { + code: "delegate", + discriminator, + parameters, + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::REVOKE_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let args = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("RevokeArgs Borsh decode failed: {error}")), + }; + let parameters = match serde_json::to_value(&args) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "RevokeArgs JSON projection failed: {error}" + )); + }, + }; + std::result::Result::Ok(crate::ParsedInstruction { + code: "revoke", + discriminator, + parameters, + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::SET_COLLECTION_SIZE_DISCRIMINATOR + | crate::BUBBLEGUM_SET_COLLECTION_SIZE_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let args = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("SetCollectionSize Borsh decode failed: {error}")), + }; + let code = if discriminator == crate::SET_COLLECTION_SIZE_DISCRIMINATOR { + "set_collection_size" + } else { + "bubblegum_set_collection_size" + }; + std::result::Result::Ok(crate::ParsedInstruction { + code, + discriminator, + parameters: serde_json::json!({"size": args.size}), + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::VERIFY_COLLECTION_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "verify_collection", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::UNVERIFY_COLLECTION_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "unverify_collection", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::SET_AND_VERIFY_COLLECTION_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "set_and_verify_collection", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::APPROVE_COLLECTION_AUTHORITY_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "approve_collection_authority", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::REVOKE_COLLECTION_AUTHORITY_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "revoke_collection_authority", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::VERIFY_SIZED_COLLECTION_ITEM_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "verify_sized_collection_item", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::UNVERIFY_SIZED_COLLECTION_ITEM_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "unverify_sized_collection_item", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::SET_AND_VERIFY_SIZED_COLLECTION_ITEM_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "set_and_verify_sized_collection_item", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::UTILIZE_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let args = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("Utilize Borsh decode failed: {error}")), + }; + let parameters = match serde_json::to_value(&args) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "Utilize JSON projection failed: {error}" + )); + }, + }; + std::result::Result::Ok(crate::ParsedInstruction { + code: "utilize", + discriminator, + parameters, + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::APPROVE_USE_AUTHORITY_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let args = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("ApproveUseAuthority Borsh decode failed: {error}")), + }; + let parameters = match serde_json::to_value(&args) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "ApproveUseAuthority JSON projection failed: {error}" + )); + }, + }; + std::result::Result::Ok(crate::ParsedInstruction { + code: "approve_use_authority", + discriminator, + parameters, + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + crate::REVOKE_USE_AUTHORITY_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "revoke_use_authority", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::FREEZE_DELEGATED_ACCOUNT_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "freeze_delegated_account", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::THAW_DELEGATED_ACCOUNT_DISCRIMINATOR => { + std::result::Result::Ok(crate::ParsedInstruction { + code: "thaw_delegated_account", + discriminator, + parameters: serde_json::json!({}), + consumed: 1, + }) + }, + crate::UPDATE_METADATA_ACCOUNT_V2_DISCRIMINATOR => { + let mut remaining = &bytes[1..]; + let args = match ::deserialize(&mut remaining) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(format!("UpdateMetadataAccountV2 Borsh decode failed: {error}")), + }; + if let std::option::Option::Some(data) = args.data.as_ref() { + match validate_data_v2(data) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + let parameters = match serde_json::to_value(&args) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "UpdateMetadataAccountV2 JSON projection failed: {error}" + )); + }, + }; + std::result::Result::Ok(crate::ParsedInstruction { + code: "update_metadata_account_v2", + discriminator, + parameters, + consumed: bytes.len().saturating_sub(remaining.len()), + }) + }, + _ => std::result::Result::Err(format!("unsupported discriminator {discriminator}")), + }; +} + +fn validate_data( + data: &mpl_token_metadata::types::Data, +) -> std::result::Result<(), std::string::String> { + if data.name.len() > 32 { + return std::result::Result::Err("metadata name exceeds 32 bytes".to_string()); + } + if data.symbol.len() > 10 { + return std::result::Result::Err("metadata symbol exceeds 10 bytes".to_string()); + } + if data.uri.len() > 200 { + return std::result::Result::Err("metadata URI exceeds 200 bytes".to_string()); + } + if data.seller_fee_basis_points > 10_000 { + return std::result::Result::Err("metadata seller fee exceeds 10000".to_string()); + } + if data.creators.as_ref().is_some_and(|creators| return creators.len() > 5) { + return std::result::Result::Err("metadata creators exceed 5 entries".to_string()); + } + return std::result::Result::Ok(()); +} + +fn hex_bytes(bytes: &[u8]) -> std::string::String { + return bytes + .iter() + .map(|value| return format!("{value:02x}")) + .collect::>() + .join(""); +} + +fn validate_data_v2( + data: &mpl_token_metadata::types::DataV2, +) -> std::result::Result<(), std::string::String> { + if data.name.len() > 32 { + return std::result::Result::Err("metadata name exceeds 32 bytes".to_string()); + } + if data.symbol.len() > 10 { + return std::result::Result::Err("metadata symbol exceeds 10 bytes".to_string()); + } + if data.uri.len() > 200 { + return std::result::Result::Err("metadata URI exceeds 200 bytes".to_string()); + } + if data.seller_fee_basis_points > 10_000 { + return std::result::Result::Err("seller fee basis points exceed 10000".to_string()); + } + if data.creators.as_ref().is_some_and(|creators| return creators.len() > 5) { + return std::result::Result::Err("metadata creators exceed 5 entries".to_string()); + } + return std::result::Result::Ok(()); +} + +fn resolve_accounts( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_core::Result> { + let instruction_accounts = match input.instruction_accounts_json.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Metaplex Token Metadata instruction accounts must be a JSON array", + )); + }, + }; + if instruction_accounts.len() > crate::MAX_INSTRUCTION_ACCOUNTS { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Metaplex Token Metadata instruction account count exceeds bound", + )); + } + let account_keys = match input.account_keys_json.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Metaplex Token Metadata transaction account keys must be a JSON array", + )); + }, + }; + let mut output = std::vec::Vec::with_capacity(instruction_accounts.len()); + for (position, account) in instruction_accounts.iter().enumerate() { + let account_index = match account.get("accountIndex").and_then(serde_json::Value::as_u64) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Metaplex Token Metadata instruction account {position} has no accountIndex" + ))); + }, + }; + let account_key = match account.get("accountKey").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) if !value.trim().is_empty() => value, + _ => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Metaplex Token Metadata instruction account {position} has no accountKey" + ))); + }, + }; + let resolved = match account_keys.iter().find(|candidate| { + return candidate.get("accountIndex").and_then(serde_json::Value::as_u64) + == std::option::Option::Some(account_index); + }) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Metaplex Token Metadata account index {account_index} is not resolved" + ))); + }, + }; + if resolved.get("accountKey").and_then(serde_json::Value::as_str) + != std::option::Option::Some(account_key) + { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Metaplex Token Metadata account index {account_index} resolves to a different key" + ))); + } + let signer = match resolved.get("signer").and_then(serde_json::Value::as_bool) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Metaplex Token Metadata account index {account_index} has no signer flag" + ))); + }, + }; + let writable = match resolved.get("writable").and_then(serde_json::Value::as_bool) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Metaplex Token Metadata account index {account_index} has no writable flag" + ))); + }, + }; + output.push(ResolvedAccount { + position, + account_index, + account_key: account_key.to_string(), + signer, + writable, + source: resolved.get("source").cloned().unwrap_or(serde_json::Value::Null), + }); + } + return std::result::Result::Ok(output); +} + +fn validate_accounts( + code: &str, + accounts: &[ResolvedAccount], +) -> std::result::Result<(), std::string::String> { + if code == "create_escrow_account" { + if accounts.len() < 8 { + return std::result::Result::Err(format!( + "create_escrow_account requires at least 8 accounts, received {}", + accounts.len() + )); + } + if let std::option::Option::Some(authority) = accounts.get(8) { + if !authority.signer || authority.writable { + return std::result::Result::Err( + "create_escrow_account optional authority must be readonly signer".to_string(), + ); + } + } + } + if code == "transfer_out_of_escrow" { + if accounts.len() < 12 { + return std::result::Result::Err(format!( + "transfer_out_of_escrow requires at least 12 accounts, received {}", + accounts.len() + )); + } + if let std::option::Option::Some(authority) = accounts.get(12) { + if !authority.signer || authority.writable { + return std::result::Result::Err( + "transfer_out_of_escrow optional authority must be readonly signer".to_string(), + ); + } + } + } + if code == "create" { + if accounts.len() < 9 { + return std::result::Result::Err(format!( + "create requires 9 positional accounts, received {}", + accounts.len() + )); + } + let mandatory: &[(usize, bool, bool)] = &[ + (0, false, true), + (2, true, true), + (3, true, false), + (4, true, true), + (5, true, false), + (6, false, false), + (7, false, false), + (8, false, false), + ]; + for (index, signer, writable) in mandatory { + let account = match accounts.get(*index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!("create is missing account {index}")); + }, + }; + if account.signer != *signer || account.writable != *writable { + return std::result::Result::Err(format!( + "create account {index} has invalid signer/writable flags" + )); + } + } + let master_edition = match accounts.get(1) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "create is missing master edition placeholder".to_string(), + ); + }, + }; + if master_edition.signer { + return std::result::Result::Err( + "create master edition account must not sign".to_string(), + ); + } + return std::result::Result::Ok(()); + } + if code == "mint" { + if accounts.len() < 15 { + return std::result::Result::Err(format!( + "mint requires 15 positional accounts, received {}", + accounts.len() + )); + } + let mandatory: &[(usize, bool, bool)] = &[ + (0, false, true), + (2, false, false), + (5, false, true), + (6, true, false), + (8, true, true), + (9, false, false), + (10, false, false), + (11, false, false), + (12, false, false), + ]; + for (index, signer, writable) in mandatory { + let account = match accounts.get(*index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!("mint is missing account {index}")); + }, + }; + if account.signer != *signer || account.writable != *writable { + return std::result::Result::Err(format!( + "mint account {index} has invalid signer/writable flags" + )); + } + } + for index in [1_usize, 3_usize, 4_usize, 7_usize, 13_usize, 14_usize] { + let account = match accounts.get(index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!( + "mint is missing optional placeholder account {index}" + )); + }, + }; + if account.signer { + return std::result::Result::Err(format!( + "mint optional account {index} must not sign" + )); + } + } + return std::result::Result::Ok(()); + } + if code == "print" { + if accounts.len() < 18 { + return std::result::Result::Err(format!( + "print requires 18 positional accounts, received {}", + accounts.len() + )); + } + let mandatory: &[(usize, bool, bool)] = &[ + (0, false, true), + (1, false, true), + (2, true, true), + (3, false, false), + (4, false, true), + (5, true, false), + (7, false, true), + (8, false, true), + (9, true, true), + (10, true, false), + (11, false, false), + (12, false, false), + (13, false, false), + (14, false, false), + (15, false, false), + (16, false, false), + (17, false, false), + ]; + for (index, signer, writable) in mandatory { + let account = match accounts.get(*index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!("print is missing account {index}")); + }, + }; + if account.signer != *signer || account.writable != *writable { + return std::result::Result::Err(format!( + "print account {index} has invalid signer/writable flags" + )); + } + } + let edition_token_record = match accounts.get(6) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "print is missing edition token record placeholder".to_string(), + ); + }, + }; + if edition_token_record.signer { + return std::result::Result::Err( + "print edition token record must not sign".to_string(), + ); + } + return std::result::Result::Ok(()); + } + if code == "update" { + if accounts.len() < 11 { + return std::result::Result::Err(format!( + "update requires 11 positional accounts, received {}", + accounts.len() + )); + } + let mandatory: &[(usize, bool, bool)] = &[ + (0, true, false), + (3, false, false), + (4, false, true), + (6, true, true), + (7, false, false), + (8, false, false), + ]; + for (index, signer, writable) in mandatory { + let account = match accounts.get(*index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!("update is missing account {index}")); + }, + }; + if account.signer != *signer || account.writable != *writable { + return std::result::Result::Err(format!( + "update account {index} has invalid signer/writable flags" + )); + } + } + for index in [1_usize, 2_usize, 5_usize, 9_usize, 10_usize] { + let account = match accounts.get(index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!( + "update is missing optional placeholder account {index}" + )); + }, + }; + if account.signer || account.writable { + return std::result::Result::Err(format!( + "update optional account {index} must be readonly and non-signer" + )); + } + } + return std::result::Result::Ok(()); + } + if code == "resize" { + if accounts.len() < 7 { + return std::result::Result::Err(format!( + "resize requires 7 positional accounts, received {}", + accounts.len() + )); + } + let mandatory: &[(usize, bool, bool)] = &[ + (0, false, true), + (1, false, true), + (2, false, false), + (3, true, true), + (6, false, false), + ]; + for (index, signer, writable) in mandatory { + let account = match accounts.get(*index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!("resize is missing account {index}")); + }, + }; + if account.signer != *signer || account.writable != *writable { + return std::result::Result::Err(format!( + "resize account {index} has invalid signer/writable flags" + )); + } + } + for index in [4_usize, 5_usize] { + let account = match accounts.get(index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!( + "resize is missing optional placeholder account {index}" + )); + }, + }; + if account.writable { + return std::result::Result::Err(format!( + "resize optional account {index} must be readonly" + )); + } + if index == 5 && account.signer { + return std::result::Result::Err("resize optional token must not sign".to_string()); + } + } + return std::result::Result::Ok(()); + } + if code == "migrate" { + if accounts.len() < 15 { + return std::result::Result::Err(format!( + "migrate requires 15 positional accounts, received {}", + accounts.len() + )); + } + let mandatory: &[(usize, bool, bool)] = &[ + (0, false, true), + (1, false, true), + (2, false, true), + (3, false, false), + (4, false, false), + (5, true, true), + (6, true, false), + (7, false, false), + (8, false, false), + (9, false, true), + (10, false, false), + (11, false, false), + (12, false, false), + ]; + for (index, signer, writable) in mandatory { + let account = match accounts.get(*index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!("migrate is missing account {index}")); + }, + }; + if account.signer != *signer || account.writable != *writable { + return std::result::Result::Err(format!( + "migrate account {index} has invalid signer/writable flags" + )); + } + } + for index in [13_usize, 14_usize] { + let account = match accounts.get(index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!( + "migrate is missing optional placeholder account {index}" + )); + }, + }; + if account.signer || account.writable { + return std::result::Result::Err(format!( + "migrate optional account {index} must be readonly and non-signer" + )); + } + } + return std::result::Result::Ok(()); + } + if code == "transfer" { + if accounts.len() < 17 { + return std::result::Result::Err(format!( + "transfer requires 17 positional accounts, received {}", + accounts.len() + )); + } + let mandatory: &[(usize, bool, bool)] = &[ + (0, false, true), + (1, false, false), + (2, false, true), + (3, false, false), + (4, false, false), + (5, false, true), + (9, true, false), + (10, true, true), + (11, false, false), + (12, false, false), + (13, false, false), + (14, false, false), + ]; + for (index, signer, writable) in mandatory { + let account = match accounts.get(*index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!( + "transfer is missing account {index}" + )); + }, + }; + if account.signer != *signer || account.writable != *writable { + return std::result::Result::Err(format!( + "transfer account {index} has invalid signer/writable flags" + )); + } + } + for index in [6_usize, 7_usize, 8_usize, 15_usize, 16_usize] { + let account = match accounts.get(index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!( + "transfer is missing optional placeholder account {index}" + )); + }, + }; + if account.signer { + return std::result::Result::Err(format!( + "transfer optional account {index} must not sign" + )); + } + } + return std::result::Result::Ok(()); + } + if code == "lock" || code == "unlock" { + if accounts.len() < 13 { + return std::result::Result::Err(format!( + "{code} requires 13 positional accounts, received {}", + accounts.len() + )); + } + let mandatory: &[(usize, bool, bool)] = &[ + (0, true, false), + (2, false, true), + (3, false, false), + (4, false, true), + (7, true, true), + (8, false, false), + (9, false, false), + ]; + for (index, signer, writable) in mandatory { + let account = match accounts.get(*index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!("{code} is missing account {index}")); + }, + }; + if account.signer != *signer || account.writable != *writable { + return std::result::Result::Err(format!( + "{code} account {index} has invalid signer/writable flags" + )); + } + } + for index in [1_usize, 5_usize, 6_usize, 10_usize, 11_usize, 12_usize] { + let account = match accounts.get(index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!( + "{code} is missing optional placeholder account {index}" + )); + }, + }; + if account.signer { + return std::result::Result::Err(format!( + "{code} optional account {index} must not sign" + )); + } + } + return std::result::Result::Ok(()); + } + if code == "use" { + if accounts.len() < 12 { + return std::result::Result::Err(format!( + "use requires 12 positional accounts, received {}", + accounts.len() + )); + } + let mandatory: &[(usize, bool, bool)] = &[ + (0, true, false), + (3, false, false), + (4, false, true), + (6, true, false), + (7, false, false), + (8, false, false), + (9, false, false), + (10, false, false), + (11, false, false), + ]; + for (index, signer, writable) in mandatory { + let account = match accounts.get(*index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!("use is missing account {index}")); + }, + }; + if account.signer != *signer || account.writable != *writable { + return std::result::Result::Err(format!( + "use account {index} has invalid signer/writable flags" + )); + } + } + for index in [1_usize, 2_usize, 5_usize] { + let account = match accounts.get(index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!( + "use is missing optional placeholder account {index}" + )); + }, + }; + if account.signer { + return std::result::Result::Err(format!( + "use optional account {index} must not sign" + )); + } + } + return std::result::Result::Ok(()); + } + if code == "verify" || code == "unverify" { + if accounts.len() < 8 { + return std::result::Result::Err(format!( + "{code} requires 8 positional accounts, received {}", + accounts.len() + )); + } + let mandatory: &[(usize, bool, bool)] = + &[(0, true, false), (2, false, true), (6, false, false), (7, false, false)]; + for (index, signer, writable) in mandatory { + let account = match accounts.get(*index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!("{code} is missing account {index}")); + }, + }; + if account.signer != *signer || account.writable != *writable { + return std::result::Result::Err(format!( + "{code} account {index} has invalid signer/writable flags" + )); + } + } + for index in [1_usize, 3_usize, 4_usize, 5_usize] { + let account = match accounts.get(index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!( + "{code} is missing optional placeholder account {index}" + )); + }, + }; + if account.signer { + return std::result::Result::Err(format!( + "{code} optional account {index} must not sign" + )); + } + } + return std::result::Result::Ok(()); + } + if code == "burn" { + if accounts.len() < 14 { + return std::result::Result::Err(format!( + "burn requires 14 positional accounts, received {}", + accounts.len() + )); + } + let mandatory: &[(usize, bool, bool)] = &[ + (0, true, true), + (2, false, true), + (4, false, true), + (5, false, true), + (11, false, false), + (12, false, false), + (13, false, false), + ]; + for (index, signer, writable) in mandatory { + let account = match accounts.get(*index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!("burn is missing account {index}")); + }, + }; + if account.signer != *signer || account.writable != *writable { + return std::result::Result::Err(format!( + "burn account {index} has invalid signer/writable flags" + )); + } + } + for index in [1_usize, 3_usize, 6_usize, 7_usize, 8_usize, 9_usize, 10_usize] { + let account = match accounts.get(index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!( + "burn is missing optional placeholder account {index}" + )); + }, + }; + if account.signer { + return std::result::Result::Err(format!( + "burn optional account {index} must not sign" + )); + } + } + return std::result::Result::Ok(()); + } + if code == "delegate" || code == "revoke" { + if accounts.len() < 14 { + return std::result::Result::Err(format!( + "{code} requires 14 positional accounts, received {}", + accounts.len() + )); + } + let mandatory: &[(usize, bool, bool)] = &[ + (1, false, false), + (2, false, true), + (3, false, false), + (5, false, false), + (7, true, false), + (8, true, true), + (9, false, false), + (10, false, false), + (11, false, false), + (12, false, false), + (13, false, false), + ]; + for (index, signer, writable) in mandatory { + let account = match accounts.get(*index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!("{code} is missing account {index}")); + }, + }; + if account.signer != *signer || account.writable != *writable { + return std::result::Result::Err(format!( + "{code} account {index} has invalid signer/writable flags" + )); + } + } + for index in [0_usize, 4_usize, 6_usize] { + let account = match accounts.get(index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!( + "{code} is missing optional placeholder account {index}" + )); + }, + }; + if account.signer { + return std::result::Result::Err(format!( + "{code} optional account {index} must not sign" + )); + } + } + return std::result::Result::Ok(()); + } + let (required, exact_flags): (usize, &[(usize, bool, bool)]) = match code { + "close_accounts" => ( + 5, + &[ + (0, false, true), + (1, false, true), + (2, false, true), + (3, true, false), + (4, false, true), + ], + ), + "create_escrow_account" => ( + 8, + &[ + (0, false, true), + (1, false, true), + (2, false, false), + (3, false, false), + (4, false, false), + (5, true, true), + (6, false, false), + (7, false, false), + ], + ), + "close_escrow_account" => ( + 8, + &[ + (0, false, true), + (1, false, true), + (2, false, false), + (3, false, false), + (4, false, false), + (5, true, true), + (6, false, false), + (7, false, false), + ], + ), + "transfer_out_of_escrow" => ( + 12, + &[ + (0, false, false), + (1, false, true), + (2, true, true), + (3, false, false), + (4, false, true), + (5, false, true), + (6, false, false), + (7, false, false), + (8, false, false), + (9, false, false), + (10, false, false), + (11, false, false), + ], + ), + "collect" => (2, &[(0, true, false), (1, false, false)]), + "deprecated_create_master_edition" => ( + 13, + &[ + (0, false, true), + (1, false, true), + (2, false, true), + (3, false, true), + (4, true, false), + (5, true, false), + (6, true, false), + (7, false, false), + (8, true, false), + (9, false, false), + (10, false, false), + (11, false, false), + (12, true, false), + ], + ), + "deprecated_set_reservation_list" => { + (3, &[(0, false, true), (1, false, true), (2, true, false)]) + }, + "deprecated_create_reservation_list" => ( + 8, + &[ + (0, false, true), + (1, true, false), + (2, true, false), + (3, false, false), + (4, false, false), + (5, false, false), + (6, false, false), + (7, false, false), + ], + ), + "deprecated_mint_printing_tokens_via_token" => ( + 9, + &[ + (0, false, true), + (1, false, true), + (2, false, true), + (3, false, true), + (4, true, false), + (5, false, false), + (6, false, false), + (7, false, false), + (8, false, false), + ], + ), + "deprecated_mint_printing_tokens" => ( + 7, + &[ + (0, false, true), + (1, false, true), + (2, true, false), + (3, false, false), + (4, false, false), + (5, false, false), + (6, false, false), + ], + ), + "create_master_edition" => ( + 9, + &[ + (0, false, true), + (1, false, true), + (2, true, false), + (3, true, false), + (4, true, true), + (5, false, false), + (6, false, false), + (7, false, false), + (8, false, false), + ], + ), + "deprecated_mint_new_edition_from_master_edition_via_printing_token" => ( + 15, + &[ + (0, false, true), + (1, false, true), + (2, false, true), + (3, false, true), + (4, true, false), + (5, false, true), + (6, false, true), + (7, false, true), + (8, true, false), + (9, true, false), + (10, false, false), + (11, false, false), + (12, false, false), + (13, false, false), + (14, false, false), + ], + ), + "burn_nft" => ( + 6, + &[ + (0, false, true), + (1, true, true), + (2, false, true), + (3, false, true), + (4, false, true), + (5, false, false), + ], + ), + "burn_edition_nft" => ( + 10, + &[ + (0, false, true), + (1, true, true), + (2, false, true), + (3, false, false), + (4, false, true), + (5, false, false), + (6, false, true), + (7, false, true), + (8, false, true), + (9, false, false), + ], + ), + "freeze_delegated_account" | "thaw_delegated_account" => ( + 5, + &[ + (0, true, true), + (1, false, true), + (2, false, false), + (3, false, false), + (4, false, false), + ], + ), + "update_primary_sale_happened_via_token" => { + (3, &[(0, false, true), (1, true, false), (2, false, false)]) + }, + "sign_metadata" => (2, &[(0, false, true), (1, true, false)]), + "puff_metadata" => (1, &[(0, false, true)]), + "mint_new_edition_from_master_edition_via_token" => ( + 13, + &[ + (0, false, true), + (1, false, true), + (2, false, true), + (3, false, true), + (4, false, true), + (5, true, false), + (6, true, true), + (7, true, false), + (8, false, false), + (9, false, false), + (10, false, false), + (11, false, false), + (12, false, false), + ], + ), + "mint_new_edition_from_master_edition_via_vault_proxy" => ( + 16, + &[ + (0, false, true), + (1, false, true), + (2, false, true), + (3, false, true), + (4, false, true), + (5, true, false), + (6, true, true), + (7, true, false), + (8, false, false), + (9, false, false), + (10, false, false), + (11, false, false), + (12, false, false), + (13, false, false), + (14, false, false), + (15, false, false), + ], + ), + "convert_master_edition_v1_to_v2" => { + (3, &[(0, false, true), (1, false, true), (2, false, true)]) + }, + "create_master_edition_v3" => ( + 8, + &[ + (0, false, true), + (1, false, true), + (2, true, false), + (3, true, false), + (4, true, true), + (5, false, true), + (6, false, false), + (7, false, false), + ], + ), + "remove_creator_verification" => (2, &[(0, false, true), (1, true, false)]), + "set_token_standard" => (3, &[(0, false, true), (1, true, false), (2, false, false)]), + "create_metadata_account" | "create_metadata_account_v2" | "create_metadata_account_v3" => { + (6, &[(0, false, true), (1, false, false), (2, true, false), (3, true, true)]) + }, + "update_metadata_account" | "update_metadata_account_v2" => { + (2, &[(0, false, true), (1, true, false)]) + }, + "set_collection_size" => (3, &[(0, false, true), (1, true, true), (2, false, false)]), + "bubblegum_set_collection_size" => { + (4, &[(0, false, true), (1, true, false), (2, false, false), (3, true, false)]) + }, + "verify_collection" => ( + 6, + &[ + (0, false, true), + (1, true, true), + (2, true, true), + (3, false, false), + (4, false, false), + (5, false, false), + ], + ), + "unverify_collection" => ( + 5, + &[ + (0, false, true), + (1, true, true), + (2, false, false), + (3, false, false), + (4, false, false), + ], + ), + "set_and_verify_collection" => ( + 7, + &[ + (0, false, true), + (1, true, true), + (2, true, true), + (3, false, false), + (4, false, false), + (5, false, false), + (6, false, false), + ], + ), + "approve_collection_authority" => ( + 7, + &[ + (0, false, true), + (1, false, false), + (2, true, true), + (3, true, true), + (4, false, false), + (5, false, false), + (6, false, false), + ], + ), + "revoke_collection_authority" => ( + 5, + &[ + (0, false, true), + (1, false, true), + (2, true, true), + (3, false, false), + (4, false, false), + ], + ), + "approve_use_authority" => ( + 10, + &[ + (0, false, true), + (1, true, true), + (2, true, true), + (3, false, false), + (4, false, true), + (5, false, false), + (6, false, false), + (7, false, false), + (8, false, false), + (9, false, false), + ], + ), + "revoke_use_authority" => ( + 8, + &[ + (0, false, true), + (1, true, true), + (2, false, false), + (3, false, true), + (4, false, false), + (5, false, false), + (6, false, false), + (7, false, false), + ], + ), + "utilize" => ( + 9, + &[ + (0, false, true), + (1, false, true), + (2, false, true), + (3, true, true), + (4, false, false), + (5, false, false), + (6, false, false), + (7, false, false), + (8, false, false), + ], + ), + "verify_sized_collection_item" | "unverify_sized_collection_item" => ( + 6, + &[ + (0, false, true), + (1, true, false), + (2, true, true), + (3, false, false), + (4, false, true), + (5, false, false), + ], + ), + "set_and_verify_sized_collection_item" => ( + 7, + &[ + (0, false, true), + (1, true, false), + (2, true, true), + (3, false, false), + (4, false, false), + (5, false, true), + (6, false, false), + ], + ), + _ => return std::result::Result::Err(format!("unknown account contract for {code}")), + }; + if accounts.len() < required { + return std::result::Result::Err(format!( + "{code} requires at least {required} accounts, received {}", + accounts.len() + )); + } + for (index, signer, writable) in exact_flags { + let account = match accounts.get(*index) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!("{code} is missing account {index}")); + }, + }; + if account.signer != *signer || account.writable != *writable { + return std::result::Result::Err(format!( + "{code} account {index} has invalid signer/writable flags" + )); + } + } + return std::result::Result::Ok(()); +} + +fn project_accounts(code: &str, accounts: &[ResolvedAccount]) -> serde_json::Value { + let roles: &[&str] = match code { + "create" => &[ + "metadata", + "master_edition", + "mint", + "authority", + "payer", + "update_authority", + "system_program", + "sysvar_instructions", + "spl_token_program", + ], + "mint" => &[ + "token", + "token_owner", + "metadata", + "master_edition", + "token_record", + "mint", + "authority", + "delegate_record", + "payer", + "system_program", + "sysvar_instructions", + "spl_token_program", + "spl_ata_program", + "authorization_rules_program", + "authorization_rules", + ], + "print" => &[ + "edition_metadata", + "edition", + "edition_mint", + "edition_token_account_owner", + "edition_token_account", + "edition_mint_authority", + "edition_token_record", + "master_edition", + "edition_marker_pda", + "payer", + "master_token_account_owner", + "master_token_account", + "master_metadata", + "update_authority", + "spl_token_program", + "spl_ata_program", + "sysvar_instructions", + "system_program", + ], + "update" => &[ + "authority", + "delegate_record", + "token", + "mint", + "metadata", + "edition", + "payer", + "system_program", + "sysvar_instructions", + "authorization_rules_program", + "authorization_rules", + ], + "use" => &[ + "authority", + "delegate_record", + "token", + "mint", + "metadata", + "edition", + "payer", + "system_program", + "sysvar_instructions", + "spl_token_program", + "authorization_rules_program", + "authorization_rules", + ], + "verify" | "unverify" => &[ + "authority", + "delegate_record", + "metadata", + "collection_mint", + "collection_metadata", + "collection_master_edition", + "system_program", + "sysvar_instructions", + ], + "resize" => { + &["metadata", "edition", "mint", "payer", "authority", "token", "system_program"] + }, + "migrate" => &[ + "metadata", + "edition", + "token", + "token_owner", + "mint", + "payer", + "authority", + "collection_metadata", + "delegate_record", + "token_record", + "system_program", + "sysvar_instructions", + "spl_token_program", + "authorization_rules_program", + "authorization_rules", + ], + "transfer" => &[ + "token", + "token_owner", + "destination_token", + "destination_owner", + "mint", + "metadata", + "edition", + "token_record", + "destination_token_record", + "authority", + "payer", + "system_program", + "sysvar_instructions", + "spl_token_program", + "spl_ata_program", + "authorization_rules_program", + "authorization_rules", + ], + "lock" | "unlock" => &[ + "authority", + "token_owner", + "token", + "mint", + "metadata", + "edition", + "token_record", + "payer", + "system_program", + "sysvar_instructions", + "spl_token_program", + "authorization_rules_program", + "authorization_rules", + ], + "burn" => &[ + "authority", + "collection_metadata", + "metadata", + "edition", + "mint", + "token", + "master_edition", + "master_edition_mint", + "master_edition_token", + "edition_marker", + "token_record", + "system_program", + "sysvar_instructions", + "spl_token_program", + ], + "close_accounts" => &["metadata", "edition", "mint", "authority", "destination"], + "create_escrow_account" => &[ + "escrow", + "metadata", + "mint", + "token_account", + "edition", + "payer", + "system_program", + "sysvar_instructions", + "authority", + ], + "close_escrow_account" => &[ + "escrow", + "metadata", + "mint", + "token_account", + "edition", + "payer", + "system_program", + "sysvar_instructions", + ], + "transfer_out_of_escrow" => &[ + "escrow", + "metadata", + "payer", + "attribute_mint", + "attribute_src", + "attribute_dst", + "escrow_mint", + "escrow_account", + "system_program", + "ata_program", + "token_program", + "sysvar_instructions", + "authority", + ], + "collect" => &["authority", "recipient"], + "deprecated_create_master_edition" => &[ + "edition", + "mint", + "printing_mint", + "one_time_printing_authorization_mint", + "update_authority", + "printing_mint_authority", + "mint_authority", + "metadata", + "payer", + "token_program", + "system_program", + "rent", + "one_time_printing_authorization_mint_authority", + ], + "deprecated_set_reservation_list" => &["master_edition", "reservation_list", "resource"], + "deprecated_create_reservation_list" => &[ + "reservation_list", + "payer", + "update_authority", + "master_edition", + "resource", + "metadata", + "system_program", + "rent", + ], + "deprecated_mint_printing_tokens_via_token" => &[ + "destination", + "token", + "one_time_printing_authorization_mint", + "printing_mint", + "burn_authority", + "metadata", + "master_edition", + "token_program", + "rent", + ], + "deprecated_mint_printing_tokens" => &[ + "destination", + "printing_mint", + "update_authority", + "metadata", + "master_edition", + "token_program", + "rent", + ], + "create_master_edition" => &[ + "edition", + "mint", + "update_authority", + "mint_authority", + "payer", + "metadata", + "token_program", + "system_program", + "rent", + ], + "deprecated_mint_new_edition_from_master_edition_via_printing_token" => &[ + "metadata", + "edition", + "master_edition", + "mint", + "mint_authority", + "printing_mint", + "master_token_account", + "edition_marker", + "burn_authority", + "payer", + "master_update_authority", + "master_metadata", + "token_program", + "system_program", + "rent", + "reservation_list", + ], + "burn_nft" => &[ + "metadata", + "owner", + "mint", + "token_account", + "master_edition_account", + "spl_token_program", + "collection_metadata", + ], + "burn_edition_nft" => &[ + "metadata", + "owner", + "print_edition_mint", + "master_edition_mint", + "print_edition_token_account", + "master_edition_token_account", + "master_edition_account", + "print_edition_account", + "edition_marker_account", + "spl_token_program", + ], + "freeze_delegated_account" | "thaw_delegated_account" => { + &["delegate", "token_account", "edition", "mint", "token_program"] + }, + "delegate" | "revoke" => &[ + "delegate_record", + "delegate", + "metadata", + "master_edition", + "token_record", + "mint", + "token", + "authority", + "payer", + "system_program", + "sysvar_instructions", + "spl_token_program", + "authorization_rules_program", + "authorization_rules", + ], + "update_primary_sale_happened_via_token" => &["metadata", "owner", "token_account"], + "sign_metadata" => &["metadata", "creator"], + "puff_metadata" => &["metadata"], + "mint_new_edition_from_master_edition_via_token" => &[ + "new_metadata", + "new_edition", + "master_edition", + "new_mint", + "edition_marker", + "new_mint_authority", + "payer", + "token_account_owner", + "token_account", + "new_metadata_update_authority", + "master_metadata", + "token_program", + "system_program", + "rent", + ], + "mint_new_edition_from_master_edition_via_vault_proxy" => &[ + "new_metadata", + "new_edition", + "master_edition", + "new_mint", + "edition_marker", + "new_mint_authority", + "payer", + "vault_authority", + "safety_deposit_store", + "safety_deposit_box", + "vault", + "new_metadata_update_authority", + "master_metadata", + "token_program", + "token_vault_program", + "system_program", + "rent", + ], + "convert_master_edition_v1_to_v2" => { + &["master_edition", "one_time_authorization_mint", "printing_mint"] + }, + "create_master_edition_v3" => &[ + "master_edition", + "mint", + "update_authority", + "mint_authority", + "payer", + "metadata", + "token_program", + "system_program", + "rent", + ], + "remove_creator_verification" => &["metadata", "creator"], + "set_token_standard" => &["metadata", "update_authority", "mint", "edition"], + "create_metadata_account" | "create_metadata_account_v2" | "create_metadata_account_v3" => { + &[ + "metadata", + "mint", + "mint_authority", + "payer", + "update_authority", + "system_program", + "rent", + ] + }, + "update_metadata_account" | "update_metadata_account_v2" => { + &["metadata", "update_authority"] + }, + "set_collection_size" => &[ + "collection_metadata", + "collection_authority", + "collection_mint", + "collection_authority_record", + ], + "bubblegum_set_collection_size" => &[ + "collection_metadata", + "collection_authority", + "collection_mint", + "bubblegum_signer", + "collection_authority_record", + ], + "verify_collection" => &[ + "metadata", + "collection_authority", + "payer", + "collection_mint", + "collection_metadata", + "collection_master_edition", + "collection_authority_record", + ], + "unverify_collection" => &[ + "metadata", + "collection_authority", + "collection_mint", + "collection_metadata", + "collection_master_edition", + "collection_authority_record", + ], + "set_and_verify_collection" => &[ + "metadata", + "collection_authority", + "payer", + "update_authority", + "collection_mint", + "collection_metadata", + "collection_master_edition", + "collection_authority_record", + ], + "approve_collection_authority" => &[ + "collection_authority_record", + "new_collection_authority", + "update_authority", + "payer", + "collection_metadata", + "collection_mint", + "system_program", + "rent", + ], + "revoke_collection_authority" => &[ + "collection_authority_record", + "delegate_authority", + "revoke_authority", + "collection_metadata", + "collection_mint", + ], + "approve_use_authority" => &[ + "use_authority_record", + "owner", + "payer", + "user", + "owner_token_account", + "metadata", + "mint", + "burner", + "token_program", + "system_program", + "rent", + ], + "revoke_use_authority" => &[ + "use_authority_record", + "owner", + "user", + "owner_token_account", + "mint", + "metadata", + "token_program", + "system_program", + "rent", + ], + "utilize" => &[ + "metadata", + "token_account", + "mint", + "use_authority", + "owner", + "token_program", + "ata_program", + "system_program", + "rent", + "use_authority_record", + "burner", + ], + "verify_sized_collection_item" | "unverify_sized_collection_item" => &[ + "metadata", + "collection_authority", + "payer", + "collection_mint", + "collection_metadata", + "collection_master_edition", + "collection_authority_record", + ], + "set_and_verify_sized_collection_item" => &[ + "metadata", + "collection_authority", + "payer", + "update_authority", + "collection_mint", + "collection_metadata", + "collection_master_edition", + "collection_authority_record", + ], + _ => &[], + }; + return serde_json::Value::Array( + accounts + .iter() + .enumerate() + .map(|(position, account)| { + return serde_json::json!({ + "position": account.position, + "accountIndex": account.account_index, + "accountKey": account.account_key, + "signer": account.signer, + "writable": account.writable, + "source": account.source, + "role": roles.get(position).copied().unwrap_or("remaining_account"), + }); + }) + .collect(), + ); +} + +fn observation( + input: &kb_store_core::CoreInstructionReplayInput, + parsed: &crate::ParsedInstruction, + accounts: serde_json::Value, + wire: &[u8], +) -> kb_decoder_api::DecodedObservation { + let wire_hash = hash(wire); + let committed = !input.transaction_failed; + let event = kb_model::DecodedProtocolEvent { + signature: kb_model::Signature(input.signature.clone()), + slot: kb_model::Slot(input.slot), + instruction_path: kb_model::InstructionPath(input.instruction_path.clone()), + program_id: kb_model::ProgramId(input.program_id.clone()), + protocol_code: kb_model::ProtocolCode(crate::SURFACE_CODE.to_string()), + surface_code: kb_model::SurfaceCode(crate::SURFACE_CODE.to_string()), + event_code: kb_model::EventCode(format!("{}.{}", crate::SURFACE_CODE, parsed.code)), + event_name: kb_model::EventName(parsed.code.to_string()), + event_family: kb_model::EventFamily::Metadata, + source_kind: if input.instruction_path.contains('/') { + kb_model::EventSourceKind::InnerInstruction + } else { + kb_model::EventSourceKind::Instruction + }, + confidence: kb_model::DecoderConfidence::ManualExact, + }; + return kb_decoder_api::DecodedObservation { + event_key: format!("metaplex_token_metadata:{}:0", parsed.code), + event, + payload_json: serde_json::json!({ + "eventVersion": crate::EVENT_VERSION, + "programId": input.program_id, + "instruction": parsed.code, + "wireDiscriminator": parsed.discriminator, + "wireDiscriminatorHex": format!("{:02x}", parsed.discriminator), + "instructionPath": input.instruction_path, + "instructionLocation": if input.instruction_path.contains('/') { "inner" } else { "outer" }, + "transactionSucceeded": !input.transaction_failed, + "committed": committed, + "parameters": parsed.parameters, + "accounts": accounts, + "wire": { "lengthBytes": wire.len(), "sha256": wire_hash, "prefixHex": hex_prefix(wire), "suffixLengthBytes": 0, "complete": true }, + "provenance": { "sdk": "mpl-token-metadata", "sdkVersion": "5.1.1", "layout": "official_borsh", "externalMetadata": true, "token2022EmbeddedMetadata": false }, + }), + transaction_failed: input.transaction_failed, + transaction_error: input.transaction_err_json.clone(), + observation_committed: committed, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::Manual, + confidence: kb_model::DecoderConfidence::ManualExact, + evidence: std::vec![ + "mpl_token_metadata_5_1_1_generated_wire".to_string(), + format!("wire_discriminator:{}", parsed.discriminator), + format!("wire_sha256:{wire_hash}") + ], + }, + }; +} + +fn hash(bytes: &[u8]) -> std::string::String { + let digest = sha2::Sha256::digest(bytes); + let mut output = std::string::String::with_capacity(64); + for byte in digest { + output.push_str(format!("{byte:02x}").as_str()); + } + return output; +} + +fn hex_prefix(bytes: &[u8]) -> std::string::String { + let length = std::cmp::min(bytes.len(), crate::DIAGNOSTIC_PREFIX_BYTES); + let mut output = std::string::String::with_capacity(length.saturating_mul(2)); + for byte in &bytes[..length] { + output.push_str(format!("{byte:02x}").as_str()); + } + return output; +} + +fn failed( + entry_code: std::option::Option<&str>, + code: &str, + message: std::string::String, +) -> kb_decoder_api::DecoderExecutionResult { + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Failed, + recognized_entry_code: entry_code.map(str::to_string), + observations: std::vec::Vec::new(), + diagnostics: std::vec![kb_decoder_api::DecoderDiagnostic { + code: code.to_string(), + message, + retriable: false + }], + }; +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/lib.rs new file mode 100644 index 0000000..0fa8cec --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_metadata_metaplex_token_metadata/src/lib.rs @@ -0,0 +1,226 @@ +// file: kb_decoder_metadata_metaplex_token_metadata/src/lib.rs +// version: 17 + +//! Contextual instruction and bounded account decoder for Metaplex Token Metadata. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod account; +mod canonical; +mod constants; +mod decoder; +mod instruction; + +/// Approve Collection Authority discriminator. +pub(crate) use crate::constants::APPROVE_COLLECTION_AUTHORITY_DISCRIMINATOR; +/// Approve Use Authority discriminator. +pub(crate) use crate::constants::APPROVE_USE_AUTHORITY_DISCRIMINATOR; +/// Bubblegum Set Collection Size discriminator. +pub(crate) use crate::constants::BUBBLEGUM_SET_COLLECTION_SIZE_DISCRIMINATOR; +/// Burn discriminator. +pub(crate) use crate::constants::BURN_DISCRIMINATOR; +/// Historical Burn Edition NFT discriminator. +pub(crate) use crate::constants::BURN_EDITION_NFT_DISCRIMINATOR; +/// Historical Burn NFT discriminator. +pub(crate) use crate::constants::BURN_NFT_DISCRIMINATOR; +/// Close Accounts discriminator. +pub(crate) use crate::constants::CLOSE_ACCOUNTS_DISCRIMINATOR; +/// Close Escrow Account discriminator. +pub(crate) use crate::constants::CLOSE_ESCROW_ACCOUNT_DISCRIMINATOR; +/// Collect discriminator. +pub(crate) use crate::constants::COLLECT_DISCRIMINATOR; +/// Convert Master Edition V1 to V2 discriminator. +pub(crate) use crate::constants::CONVERT_MASTER_EDITION_V1_TO_V2_DISCRIMINATOR; +/// Modern Create discriminator. +pub(crate) use crate::constants::CREATE_DISCRIMINATOR; +/// Create Escrow Account discriminator. +pub(crate) use crate::constants::CREATE_ESCROW_ACCOUNT_DISCRIMINATOR; +/// Historical Create Master Edition discriminator. +pub(crate) use crate::constants::CREATE_MASTER_EDITION_DISCRIMINATOR; +/// Create Master Edition V3 discriminator. +pub(crate) use crate::constants::CREATE_MASTER_EDITION_V3_DISCRIMINATOR; +/// Historical Create Metadata Account discriminator. +pub(crate) use crate::constants::CREATE_METADATA_ACCOUNT_DISCRIMINATOR; +/// Historical Create Metadata Account V2 discriminator. +pub(crate) use crate::constants::CREATE_METADATA_ACCOUNT_V2_DISCRIMINATOR; +/// Create Metadata Account V3 discriminator. +pub(crate) use crate::constants::CREATE_METADATA_ACCOUNT_V3_DISCRIMINATOR; +/// Delegate discriminator. +pub(crate) use crate::constants::DELEGATE_DISCRIMINATOR; +/// Deprecated Create Master Edition discriminator. +pub(crate) use crate::constants::DEPRECATED_CREATE_MASTER_EDITION_DISCRIMINATOR; +/// Deprecated Create Reservation List discriminator. +pub(crate) use crate::constants::DEPRECATED_CREATE_RESERVATION_LIST_DISCRIMINATOR; +/// Deprecated printing-token mint-new-edition discriminator. +pub(crate) use crate::constants::DEPRECATED_MINT_NEW_EDITION_VIA_PRINTING_TOKEN_DISCRIMINATOR; +/// Deprecated Mint Printing Tokens discriminator. +pub(crate) use crate::constants::DEPRECATED_MINT_PRINTING_TOKENS_DISCRIMINATOR; +/// Deprecated Mint Printing Tokens Via Token discriminator. +pub(crate) use crate::constants::DEPRECATED_MINT_PRINTING_TOKENS_VIA_TOKEN_DISCRIMINATOR; +/// Deprecated Set Reservation List discriminator. +pub(crate) use crate::constants::DEPRECATED_SET_RESERVATION_LIST_DISCRIMINATOR; +/// Number of bytes retained in malformed-wire diagnostics. +pub(crate) use crate::constants::DIAGNOSTIC_PREFIX_BYTES; +/// Stable event payload contract version. +pub(crate) use crate::constants::EVENT_VERSION; +/// Freeze Delegated Account discriminator. +pub(crate) use crate::constants::FREEZE_DELEGATED_ACCOUNT_DISCRIMINATOR; +/// Current pre-release instruction inventory. +pub(crate) use crate::constants::INSTRUCTION_ENTRIES; +/// Lock discriminator. +pub(crate) use crate::constants::LOCK_DISCRIMINATOR; +/// Maximum encoded base64 instruction payload size. +pub(crate) use crate::constants::MAX_BASE64_BYTES; +/// Maximum number of instruction accounts accepted by this decoder. +pub(crate) use crate::constants::MAX_INSTRUCTION_ACCOUNTS; +/// Tracing target for this decoder. +/// Maximum retained instruction payload size. +pub(crate) use crate::constants::MAX_INSTRUCTION_BYTES; +/// Migrate discriminator. +pub(crate) use crate::constants::MIGRATE_DISCRIMINATOR; +/// Modern Mint discriminator. +pub(crate) use crate::constants::MINT_DISCRIMINATOR; +/// Mint New Edition From Master Edition Via Token discriminator. +pub(crate) use crate::constants::MINT_NEW_EDITION_FROM_MASTER_EDITION_VIA_TOKEN_DISCRIMINATOR; +/// Mint New Edition From Master Edition Via Vault Proxy discriminator. +pub(crate) use crate::constants::MINT_NEW_EDITION_FROM_MASTER_EDITION_VIA_VAULT_PROXY_DISCRIMINATOR; +/// Modern Print discriminator. +pub(crate) use crate::constants::PRINT_DISCRIMINATOR; +/// Puff Metadata discriminator. +pub(crate) use crate::constants::PUFF_METADATA_DISCRIMINATOR; +/// Remove Creator Verification discriminator. +pub(crate) use crate::constants::REMOVE_CREATOR_VERIFICATION_DISCRIMINATOR; +/// Resize discriminator. +pub(crate) use crate::constants::RESIZE_DISCRIMINATOR; +/// Revoke Collection Authority discriminator. +pub(crate) use crate::constants::REVOKE_COLLECTION_AUTHORITY_DISCRIMINATOR; +/// Revoke discriminator. +pub(crate) use crate::constants::REVOKE_DISCRIMINATOR; +/// Revoke Use Authority discriminator. +pub(crate) use crate::constants::REVOKE_USE_AUTHORITY_DISCRIMINATOR; +/// Set And Verify Collection discriminator. +pub(crate) use crate::constants::SET_AND_VERIFY_COLLECTION_DISCRIMINATOR; +/// Set And Verify Sized Collection Item discriminator. +pub(crate) use crate::constants::SET_AND_VERIFY_SIZED_COLLECTION_ITEM_DISCRIMINATOR; +/// Set Collection Size discriminator. +pub(crate) use crate::constants::SET_COLLECTION_SIZE_DISCRIMINATOR; +/// Set Token Standard discriminator. +pub(crate) use crate::constants::SET_TOKEN_STANDARD_DISCRIMINATOR; +/// Sign Metadata discriminator. +pub(crate) use crate::constants::SIGN_METADATA_DISCRIMINATOR; +/// Stable decoder surface code. +pub(crate) use crate::constants::SURFACE_CODE; +/// Thaw Delegated Account discriminator. +pub(crate) use crate::constants::THAW_DELEGATED_ACCOUNT_DISCRIMINATOR; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; +/// Transfer discriminator. +pub(crate) use crate::constants::TRANSFER_DISCRIMINATOR; +/// Transfer Out Of Escrow discriminator. +pub(crate) use crate::constants::TRANSFER_OUT_OF_ESCROW_DISCRIMINATOR; +/// Unlock discriminator. +pub(crate) use crate::constants::UNLOCK_DISCRIMINATOR; +/// Unverify Collection discriminator. +pub(crate) use crate::constants::UNVERIFY_COLLECTION_DISCRIMINATOR; +/// Modern Unverify discriminator. +pub(crate) use crate::constants::UNVERIFY_DISCRIMINATOR; +/// Unverify Sized Collection Item discriminator. +pub(crate) use crate::constants::UNVERIFY_SIZED_COLLECTION_ITEM_DISCRIMINATOR; +/// Modern Update discriminator. +pub(crate) use crate::constants::UPDATE_DISCRIMINATOR; +/// Historical Update Metadata Account discriminator. +pub(crate) use crate::constants::UPDATE_METADATA_ACCOUNT_DISCRIMINATOR; +/// Update Metadata Account V2 discriminator. +pub(crate) use crate::constants::UPDATE_METADATA_ACCOUNT_V2_DISCRIMINATOR; +/// Update Primary Sale Happened Via Token discriminator. +pub(crate) use crate::constants::UPDATE_PRIMARY_SALE_HAPPENED_VIA_TOKEN_DISCRIMINATOR; +/// Modern Use discriminator. +pub(crate) use crate::constants::USE_DISCRIMINATOR; +/// Utilize discriminator. +pub(crate) use crate::constants::UTILIZE_DISCRIMINATOR; +/// Verify Collection discriminator. +pub(crate) use crate::constants::VERIFY_COLLECTION_DISCRIMINATOR; +/// Modern Verify discriminator. +pub(crate) use crate::constants::VERIFY_DISCRIMINATOR; +/// Verify Sized Collection Item discriminator. +pub(crate) use crate::constants::VERIFY_SIZED_COLLECTION_ITEM_DISCRIMINATOR; +/// Crate-root access to `ParsedInstruction` from `instruction`. +pub(crate) use crate::instruction::ParsedInstruction; +/// Crate-root access to `decode` from `instruction`. +pub(crate) use crate::instruction::decode; +/// Crate-root access to `entry_for_discriminator` from `instruction`. +pub(crate) use crate::instruction::entry_for_discriminator; +/// Crate-root access to `payload_discriminator` from `instruction`. +pub(crate) use crate::instruction::payload_discriminator; + +/// Canonical projection shared by Collection and Use Authority records. +pub use crate::account::AuthorityRecordAccountSnapshot; +/// Canonical projection shared by Metadata and Holder delegate records. +pub use crate::account::DelegateRecordAccountSnapshot; +/// Supported edition account layout. +pub use crate::account::EditionAccountKind; +/// Canonical bounded projection of one edition or master-edition account. +pub use crate::account::EditionAccountSnapshot; +/// Supported Edition Marker account layout. +pub use crate::account::EditionMarkerAccountKind; +/// Canonical bounded projection of one Edition Marker account. +pub use crate::account::EditionMarkerAccountSnapshot; +/// Exhaustive published Metaplex account-key inventory. +pub use crate::account::METADATA_ACCOUNT_LAYOUT_AUDIT; +/// Bounded error returned while decoding a Metadata account. +pub use crate::account::MetadataAccountDecodeError; +/// One entry in the exhaustive published account-key audit. +pub use crate::account::MetadataAccountLayoutAuditEntry; +/// Canonical bounded projection of one Metadata account. +pub use crate::account::MetadataAccountSnapshot; +/// One bounded historical reservation entry. +pub use crate::account::ReservationEntrySnapshot; +/// Historical Reservation List account generation. +pub use crate::account::ReservationListAccountKind; +/// Canonical decode-only projection of a historical Reservation List account. +pub use crate::account::ReservationListAccountSnapshot; +/// Canonical projection of one Token Owned Escrow account. +pub use crate::account::TokenOwnedEscrowAccountSnapshot; +/// Authority form used by one Token Owned Escrow account. +pub use crate::account::TokenOwnedEscrowAuthorityKind; +/// Canonical bounded projection of one programmable Token Record account. +pub use crate::account::TokenRecordAccountSnapshot; +/// Decodes one Collection Authority Record account. +pub use crate::account::decode_collection_authority_record_account; +/// Decodes and validates one edition or master-edition account snapshot. +pub use crate::account::decode_edition_account; +/// Decodes and validates one Edition Marker account snapshot. +pub use crate::account::decode_edition_marker_account; +/// Decodes one Holder Delegate Record account. +pub use crate::account::decode_holder_delegate_record_account; +/// Decodes and validates one canonical Metadata account snapshot. +pub use crate::account::decode_metadata_account; +/// Decodes one Metadata Delegate Record account. +pub use crate::account::decode_metadata_delegate_record_account; +/// Decodes one historical Reservation List V1 or V2 account. +pub use crate::account::decode_reservation_list_account; +/// Decodes one Token Owned Escrow account. +pub use crate::account::decode_token_owned_escrow_account; +/// Decodes and validates one programmable Token Record account snapshot. +pub use crate::account::decode_token_record_account; +/// Decodes one Use Authority Record account. +pub use crate::account::decode_use_authority_record_account; +/// Stable identity and policy of one canonical account snapshot. +pub use crate::canonical::CanonicalAccountIdentity; +/// Lifecycle class of one canonical account layout. +pub use crate::canonical::CanonicalAccountLifecycle; +/// Provenance retained for one canonical account observation. +pub use crate::canonical::CanonicalAccountProvenance; +/// Execution policy separated from historical materialization. +pub use crate::canonical::CanonicalExecutionPolicy; +/// Materialization policy for active or historical state. +pub use crate::canonical::CanonicalMaterializationPolicy; +/// Canonical account envelope used by materializers and replay. +pub use crate::canonical::CanonicalMetadataAccountSnapshot; +/// Typed canonical Metaplex account state. +pub use crate::canonical::CanonicalMetadataAccountState; +/// Origin of one canonical account observation. +pub use crate::canonical::CanonicalObservationSource; +/// Exact Metaplex Token Metadata decoder. +pub use crate::decoder::MetadataMetaplexTokenMetadataDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/Cargo.toml new file mode 100644 index 0000000..c17442f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_metadata_spl_name_service/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_metadata_spl_name_service" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/README.md b/migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/README.md new file mode 100644 index 0000000..26bcdb4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/README.md @@ -0,0 +1,17 @@ + + + +# kb_decoder_metadata_spl_name_service + +Ce crate réserve la frontière de décodage pour le programme SPL Name Service. + +## État + +Le program ID et l'identité du crate sont enregistrés, mais aucun payload n'est encore déclaré `decoded`. L'implémentation maximale sera réalisée dans la version dédiée indiquée par le `ROADMAP.md`, à partir des interfaces et fixtures officielles. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs`. +- Le scaffold ne doit produire aucun faux événement avant l'implémentation officielle. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/src/constants.rs new file mode 100644 index 0000000..eb1fd31 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_metadata_spl_name_service/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_metadata_spl_name_service` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_metadata_spl_name_service"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/src/decoder.rs new file mode 100644 index 0000000..a280df0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/src/decoder.rs @@ -0,0 +1,55 @@ +// file: kb_decoder_metadata_spl_name_service/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_metadata_spl_name_service` crate. + +/// Reserved decoder implementation for the verified program surface. +#[derive(Clone, Debug, Default)] +pub struct MetadataSplNameServiceDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::MetadataSplNameServiceDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_metadata_spl_name_service"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::SPL_NAME_SERVICE_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +#[cfg(test)] +mod tests { + #[test] + fn exposes_every_verified_program_id() { + let decoder = crate::MetadataSplNameServiceDecoder; + let program_ids = kb_decoder_api::ProtocolDecoder::program_ids(&decoder); + assert_eq!(program_ids.len(), 1); + for program_id in program_ids { + assert!(kb_program_ids::find_registered_program_id(program_id).is_some()); + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/src/lib.rs new file mode 100644 index 0000000..1ba111a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_metadata_spl_name_service/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_metadata_spl_name_service/src/lib.rs +// version: 2 + +//! Decoder crate for `metadata_spl_name_service`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::MetadataSplNameServiceDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/Cargo.toml new file mode 100644 index 0000000..76f7409 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_nft_metaplex_bubblegum/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_nft_metaplex_bubblegum" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/README.md b/migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/README.md new file mode 100644 index 0000000..24445ca --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_nft_metaplex_bubblegum + +Ce crate réserve la surface de décodage `nft_metaplex_bubblegum`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `nft_metaplex_bubblegum` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/src/constants.rs new file mode 100644 index 0000000..cbd7471 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_nft_metaplex_bubblegum/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_nft_metaplex_bubblegum` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_nft_metaplex_bubblegum"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/src/decoder.rs new file mode 100644 index 0000000..f8495e3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_nft_metaplex_bubblegum/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_nft_metaplex_bubblegum` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct NftMetaplexBubblegumDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::NftMetaplexBubblegumDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_nft_metaplex_bubblegum"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::NFT_METAPLEX_BUBBLEGUM_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/src/lib.rs new file mode 100644 index 0000000..ded2549 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_nft_metaplex_bubblegum/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_nft_metaplex_bubblegum/src/lib.rs +// version: 2 + +//! Decoder crate for `nft_metaplex_bubblegum`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::NftMetaplexBubblegumDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/Cargo.toml new file mode 100644 index 0000000..deb2e0b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_orderbook_jupiter_limit_order/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_orderbook_jupiter_limit_order" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/README.md b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/README.md new file mode 100644 index 0000000..7862c51 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_orderbook_jupiter_limit_order + +Ce crate réserve la surface de décodage `orderbook_jupiter_limit_order`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `orderbook_jupiter_limit_order` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/src/constants.rs new file mode 100644 index 0000000..949ef6c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_orderbook_jupiter_limit_order/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_orderbook_jupiter_limit_order` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_orderbook_jupiter_limit_order"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/src/decoder.rs new file mode 100644 index 0000000..87b5209 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_orderbook_jupiter_limit_order/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_orderbook_jupiter_limit_order` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct OrderbookJupiterLimitOrderDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::OrderbookJupiterLimitOrderDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_orderbook_jupiter_limit_order"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ORDERBOOK_JUPITER_LIMIT_ORDER_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/src/lib.rs new file mode 100644 index 0000000..2bfc937 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_orderbook_jupiter_limit_order/src/lib.rs +// version: 2 + +//! Decoder crate for `orderbook_jupiter_limit_order`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::OrderbookJupiterLimitOrderDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/Cargo.toml new file mode 100644 index 0000000..c1e1a19 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_orderbook_jupiter_limit_order_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_orderbook_jupiter_limit_order_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/README.md b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/README.md new file mode 100644 index 0000000..86c5154 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_orderbook_jupiter_limit_order_v2 + +Ce crate réserve la surface de décodage `orderbook_jupiter_limit_order_v2`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `orderbook_jupiter_limit_order_v2` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/src/constants.rs new file mode 100644 index 0000000..0376036 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_orderbook_jupiter_limit_order_v2/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_orderbook_jupiter_limit_order_v2` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_orderbook_jupiter_limit_order_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/src/decoder.rs new file mode 100644 index 0000000..7c2e357 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_orderbook_jupiter_limit_order_v2/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_orderbook_jupiter_limit_order_v2` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct OrderbookJupiterLimitOrderV2Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::OrderbookJupiterLimitOrderV2Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_orderbook_jupiter_limit_order_v2"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ORDERBOOK_JUPITER_LIMIT_ORDER_V2_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/src/lib.rs new file mode 100644 index 0000000..3834ebb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_jupiter_limit_order_v2/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_orderbook_jupiter_limit_order_v2/src/lib.rs +// version: 2 + +//! Decoder crate for `orderbook_jupiter_limit_order_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::OrderbookJupiterLimitOrderV2Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/Cargo.toml new file mode 100644 index 0000000..842eaf1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_orderbook_openbook_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_orderbook_openbook_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/README.md b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/README.md new file mode 100644 index 0000000..49b5a12 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/README.md @@ -0,0 +1,17 @@ + + + +# kb_decoder_orderbook_openbook_v2 + +Ce crate décode la surface protocolaire `openbook_v2` en événements protocolaires normalisés. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/src/constants.rs new file mode 100644 index 0000000..2410307 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_orderbook_openbook_v2/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_orderbook_openbook_v2` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_orderbook_openbook_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/src/decoder.rs new file mode 100644 index 0000000..8b156ae --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/src/decoder.rs @@ -0,0 +1,42 @@ +// file: kb_decoder_orderbook_openbook_v2/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_orderbook_openbook_v2` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct OrderbookOpenbookV2Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::OrderbookOpenbookV2Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_orderbook_openbook_v2"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ORDERBOOK_OPENBOOK_V2_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/src/lib.rs new file mode 100644 index 0000000..782cf4d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_orderbook_openbook_v2/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_orderbook_openbook_v2/src/lib.rs +// version: 2 + +//! Decoder crate for `orderbook_openbook_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Initial decoder implementation placeholder. +pub use crate::decoder::OrderbookOpenbookV2Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/Cargo.toml new file mode 100644 index 0000000..7321141 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_perpetuals_drift_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_perpetuals_drift_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/README.md b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/README.md new file mode 100644 index 0000000..413ba1c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_perpetuals_drift_v2 + +Ce crate réserve la surface de décodage `perpetuals_drift_v2`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `perpetuals_drift_v2` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/src/constants.rs new file mode 100644 index 0000000..0ad9790 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_perpetuals_drift_v2/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_perpetuals_drift_v2` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_perpetuals_drift_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/src/decoder.rs new file mode 100644 index 0000000..ea580c8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_perpetuals_drift_v2/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_perpetuals_drift_v2` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct PerpetualsDriftV2Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::PerpetualsDriftV2Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_perpetuals_drift_v2"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::PERPETUALS_DRIFT_V2_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/src/lib.rs new file mode 100644 index 0000000..9521d03 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_drift_v2/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_perpetuals_drift_v2/src/lib.rs +// version: 2 + +//! Decoder crate for `perpetuals_drift_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::PerpetualsDriftV2Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/Cargo.toml new file mode 100644 index 0000000..7cf859e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_perpetuals_jupiter/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_perpetuals_jupiter" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/README.md b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/README.md new file mode 100644 index 0000000..eb6e24e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_perpetuals_jupiter + +Ce crate réserve la surface de décodage `perpetuals_jupiter`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `perpetuals_jupiter` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/src/constants.rs new file mode 100644 index 0000000..6c7bd12 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_perpetuals_jupiter/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_perpetuals_jupiter` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_perpetuals_jupiter"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/src/decoder.rs new file mode 100644 index 0000000..272de4b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_perpetuals_jupiter/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_perpetuals_jupiter` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct PerpetualsJupiterDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::PerpetualsJupiterDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_perpetuals_jupiter"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::PERPETUALS_JUPITER_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/src/lib.rs new file mode 100644 index 0000000..852f712 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_jupiter/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_perpetuals_jupiter/src/lib.rs +// version: 2 + +//! Decoder crate for `perpetuals_jupiter`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::PerpetualsJupiterDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/Cargo.toml new file mode 100644 index 0000000..a3d6ed7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_perpetuals_zeta/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_perpetuals_zeta" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/README.md b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/README.md new file mode 100644 index 0000000..11ad077 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_perpetuals_zeta + +Ce crate réserve la surface de décodage `perpetuals_zeta`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `perpetuals_zeta` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/src/constants.rs new file mode 100644 index 0000000..bb5c8c4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_perpetuals_zeta/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_perpetuals_zeta` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_perpetuals_zeta"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/src/decoder.rs new file mode 100644 index 0000000..ffee18c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_perpetuals_zeta/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_perpetuals_zeta` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct PerpetualsZetaDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::PerpetualsZetaDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_perpetuals_zeta"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::PERPETUALS_ZETA_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/src/lib.rs new file mode 100644 index 0000000..ae931ef --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_perpetuals_zeta/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_perpetuals_zeta/src/lib.rs +// version: 2 + +//! Decoder crate for `perpetuals_zeta`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::PerpetualsZetaDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/Cargo.toml new file mode 100644 index 0000000..05c0f14 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_router_dflow_aggregator_v4/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_router_dflow_aggregator_v4" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/README.md b/migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/README.md new file mode 100644 index 0000000..7c309dd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_router_dflow_aggregator_v4 + +Ce crate réserve la surface de décodage `router_dflow_aggregator_v4`. Aucun `program_id` local n'est encore fixé dans ce squelette. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `router_dflow_aggregator_v4` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/src/constants.rs new file mode 100644 index 0000000..e46de0a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_router_dflow_aggregator_v4/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_router_dflow_aggregator_v4` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_router_dflow_aggregator_v4"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/src/decoder.rs new file mode 100644 index 0000000..c81d06b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_router_dflow_aggregator_v4/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_router_dflow_aggregator_v4` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct RouterDflowAggregatorV4Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::RouterDflowAggregatorV4Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_router_dflow_aggregator_v4"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ROUTER_DFLOW_AGGREGATOR_V4_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/src/lib.rs new file mode 100644 index 0000000..abc2674 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_dflow_aggregator_v4/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_router_dflow_aggregator_v4/src/lib.rs +// version: 2 + +//! Decoder crate for `router_dflow_aggregator_v4`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::RouterDflowAggregatorV4Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/Cargo.toml new file mode 100644 index 0000000..a99490d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_router_jupiter_aggregator_v4/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_router_jupiter_aggregator_v4" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/README.md b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/README.md new file mode 100644 index 0000000..1530756 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_router_jupiter_aggregator_v4 + +Ce crate réserve la surface de décodage `router_jupiter_aggregator_v4`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `router_jupiter_aggregator_v4` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/src/constants.rs new file mode 100644 index 0000000..1c098cb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_router_jupiter_aggregator_v4/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_router_jupiter_aggregator_v4` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_router_jupiter_aggregator_v4"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/src/decoder.rs new file mode 100644 index 0000000..152ad94 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_router_jupiter_aggregator_v4/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_router_jupiter_aggregator_v4` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct RouterJupiterAggregatorV4Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::RouterJupiterAggregatorV4Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_router_jupiter_aggregator_v4"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ROUTER_JUPITER_AGGREGATOR_V4_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/src/lib.rs new file mode 100644 index 0000000..7a7fe14 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v4/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_router_jupiter_aggregator_v4/src/lib.rs +// version: 2 + +//! Decoder crate for `router_jupiter_aggregator_v4`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::RouterJupiterAggregatorV4Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/Cargo.toml new file mode 100644 index 0000000..944e3ce --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_router_jupiter_aggregator_v6/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_router_jupiter_aggregator_v6" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/README.md b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/README.md new file mode 100644 index 0000000..6d37769 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_router_jupiter_aggregator_v6 + +Ce crate réserve la surface de décodage `router_jupiter_aggregator_v6`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `router_jupiter_aggregator_v6` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/src/constants.rs new file mode 100644 index 0000000..9f89b8b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_router_jupiter_aggregator_v6/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_router_jupiter_aggregator_v6` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_router_jupiter_aggregator_v6"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/src/decoder.rs new file mode 100644 index 0000000..24856db --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_router_jupiter_aggregator_v6/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_router_jupiter_aggregator_v6` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct RouterJupiterAggregatorV6Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::RouterJupiterAggregatorV6Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_router_jupiter_aggregator_v6"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ROUTER_JUPITER_AGGREGATOR_V6_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/src/lib.rs new file mode 100644 index 0000000..4918c08 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_aggregator_v6/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_router_jupiter_aggregator_v6/src/lib.rs +// version: 2 + +//! Decoder crate for `router_jupiter_aggregator_v6`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::RouterJupiterAggregatorV6Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/Cargo.toml new file mode 100644 index 0000000..2443b4c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_router_jupiter_dca/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_router_jupiter_dca" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/README.md b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/README.md new file mode 100644 index 0000000..c74e8bc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_router_jupiter_dca + +Ce crate réserve la surface de décodage `router_jupiter_dca`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `router_jupiter_dca` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/src/constants.rs new file mode 100644 index 0000000..77ce55a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_router_jupiter_dca/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_router_jupiter_dca` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_router_jupiter_dca"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/src/decoder.rs new file mode 100644 index 0000000..70712c9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_router_jupiter_dca/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_router_jupiter_dca` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct RouterJupiterDcaDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::RouterJupiterDcaDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_router_jupiter_dca"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ROUTER_JUPITER_DCA_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/src/lib.rs new file mode 100644 index 0000000..5edfdfb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_jupiter_dca/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_router_jupiter_dca/src/lib.rs +// version: 2 + +//! Decoder crate for `router_jupiter_dca`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::RouterJupiterDcaDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/Cargo.toml new file mode 100644 index 0000000..0150289 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_router_okx_labs_v1/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_router_okx_labs_v1" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/README.md b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/README.md new file mode 100644 index 0000000..921ee68 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_router_okx_labs_v1 + +Ce crate réserve la surface de décodage `router_okx_labs_v1`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `router_okx_labs_v1` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/src/constants.rs new file mode 100644 index 0000000..59109f5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_router_okx_labs_v1/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_router_okx_labs_v1` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_router_okx_labs_v1"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/src/decoder.rs new file mode 100644 index 0000000..c66f77d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_router_okx_labs_v1/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_router_okx_labs_v1` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct RouterOkxLabsV1Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::RouterOkxLabsV1Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_router_okx_labs_v1"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ROUTER_OKX_LABS_V1_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/src/lib.rs new file mode 100644 index 0000000..b783dfd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v1/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_router_okx_labs_v1/src/lib.rs +// version: 2 + +//! Decoder crate for `router_okx_labs_v1`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::RouterOkxLabsV1Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/Cargo.toml new file mode 100644 index 0000000..3480e69 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_router_okx_labs_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_router_okx_labs_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/README.md b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/README.md new file mode 100644 index 0000000..288285c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_router_okx_labs_v2 + +Ce crate réserve la surface de décodage `router_okx_labs_v2`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `router_okx_labs_v2` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/src/constants.rs new file mode 100644 index 0000000..d6e7ed0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_router_okx_labs_v2/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_router_okx_labs_v2` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_router_okx_labs_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/src/decoder.rs new file mode 100644 index 0000000..14356fc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_router_okx_labs_v2/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_router_okx_labs_v2` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct RouterOkxLabsV2Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::RouterOkxLabsV2Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_router_okx_labs_v2"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ROUTER_OKX_LABS_V2_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/src/lib.rs new file mode 100644 index 0000000..f24da33 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_router_okx_labs_v2/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_router_okx_labs_v2/src/lib.rs +// version: 2 + +//! Decoder crate for `router_okx_labs_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::RouterOkxLabsV2Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/Cargo.toml new file mode 100644 index 0000000..ccbc3d4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_rwa_ondo_global_markets/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_rwa_ondo_global_markets" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/README.md b/migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/README.md new file mode 100644 index 0000000..3945841 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_rwa_ondo_global_markets + +Ce crate réserve la surface de décodage `rwa_ondo_global_markets`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `rwa_ondo_global_markets` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/src/constants.rs new file mode 100644 index 0000000..6b84930 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_rwa_ondo_global_markets/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_rwa_ondo_global_markets` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_rwa_ondo_global_markets"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/src/decoder.rs new file mode 100644 index 0000000..48f3c8f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_rwa_ondo_global_markets/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_rwa_ondo_global_markets` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct RwaOndoGlobalMarketsDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::RwaOndoGlobalMarketsDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_rwa_ondo_global_markets"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::RWA_ONDO_GLOBAL_MARKETS_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/src/lib.rs new file mode 100644 index 0000000..cf17c57 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_rwa_ondo_global_markets/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_rwa_ondo_global_markets/src/lib.rs +// version: 2 + +//! Decoder crate for `rwa_ondo_global_markets`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::RwaOndoGlobalMarketsDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/Cargo.toml new file mode 100644 index 0000000..27bdd3b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/Cargo.toml @@ -0,0 +1,34 @@ +# file: kb_decoder_solana_core/Cargo.toml +# version: 15 + +[package] +name = "kb_decoder_solana_core" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +bs58.workspace = true +base64.workspace = true +borsh.workspace = true +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +kb_store_core = { path = "../kb_store_core" } +serde_json.workspace = true +sha2.workspace = true +solana-address-lookup-table-interface.workspace = true +solana-compute-budget-interface.workspace = true +solana-loader-v3-interface.workspace = true +solana-sdk-ids.workspace = true +solana-system-interface.workspace = true +solana-stake-interface.workspace = true +solana-vote-interface.workspace = true +solana-zk-elgamal-proof-interface.workspace = true +tracing.workspace = true +wincode.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/README.md b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/README.md new file mode 100644 index 0000000..43a9620 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/README.md @@ -0,0 +1,116 @@ + + + +# kb_decoder_solana_core + +Ce crate décode les programmes Solana natifs/runtime, les programmes historiques encore observables, les loaders et les précompiles. `memo` est explicitement exclu de cette crate et appartient à la série SPL. + +## Rôle dans l'écosystème + +`SolanaCoreDecoder` implémente le contrat commun `InstructionDecoder` et consomme uniquement les inputs contextualisés produits par l’extraction core. Il ne dépend ni de PostgreSQL, ni de Tauri, ni d’un fournisseur RPC. + +Le processor conserve l’identité stable `solana_native_classifier` introduite en `0.4.0`. Cette continuité permet au ledger processor/version/hash de remplacer les anciens résultats `unsupported` lors d’un force replay. La version du processor reste `0.4.1` pendant les deltas préparatoires. + +## Couverture `0.4.1-pre.018` + +Les deltas préparatoires de `0.4.1` livrent les phases A à E et poursuivent l’audit final : + +- utilitaires de lecture base64 bornée et de lecture little-endian ; +- validation des comptes résolus et attribution de rôles officiels ; +- événements natifs versionnés, déterministes et explicites sur le succès on-chain ; +- décodage des quatorze variantes actuelles de `SystemInstruction` depuis `solana-system-interface 3.2.0` ; +- décodage des variantes Compute Budget actuelles depuis `solana-compute-budget-interface 3.0.0`, avec compatibilité runtime Borsh unchecked : valeurs minimales décodées et suffixes conservés par taille, préfixe et SHA-256 ; +- prise en charge historique de `RequestUnitsDeprecated` ; +- distinction entre `decoded`, `ignored`, `unsupported` et `failed` ; +- absence de mutation commitée pour une transaction source échouée ; +- décodage exact des cinq variantes Address Lookup Table depuis `solana-address-lookup-table-interface 3.1.0` ; +- gestion de la paire optionnelle payer/System lors d’un extend et de la règle signer historique de l’autorité lors d’un create ; +- projection lifecycle ALT séparée dans `kb_materializer_lifecycle` pour les observations réussies et commitées ; +- conservation des comptes System additionnels correctement résolus avec le rôle `additional_account`, après validation sur un transfert mainnet réussi ; +- décodage exact des loaders BPF immuables, upgradeable et v4 avec tailles canoniques, champs historiques optionnels et hashes des blocs écrits ; +- classification de l’invocation directe du Native Loader comme `ignored`, car aucune enum d’instruction autonome stable n’est publiée ; +- projection lifecycle des finalize/deploy/upgrade/close/extend/retract/set length réussis ; `pre.020` projette séparément les changements d’autorité dans `kb_materializer_admin` et les writes/copies de bytecode dans `kb_materializer_compliance_audit`, avec hash et préfixe borné ; +- décodage de `FeatureGateInstruction::RevokePendingActivation` avec validation des comptes Feature, Incinerator et System ; +- décodage générique du Config Program : clés compactes, signataires et payload opaque borné/hashé ; +- exclusion de Stake Config des surfaces, car son adresse désigne un compte historique non exécutable ; +- décodage exact des vingt variantes `VoteInstruction` de `solana-vote-interface ^6.0` via `wincode::deserialize_exact`, y compris les formes checked/seed, compactes, tower sync, initialisation V2 avec BLS, commissions différenciées et dépôt de récompenses délégateurs ; +- validation des sysvars Rent, Clock et Slot Hashes aux positions officielles, tout en conservant les comptes additionnels résolus et les privilèges attendus/observés ; +- aucune matérialisation Vote ajoutée : les événements restent des observations structurées et les transactions échouées restent non commitées. +- validation mainnet Vote sur 500 instructions réelles `tower_sync`, sans échec Vote ; +- correction du seul échec de replay observé, un `set_compute_unit_limit` de 12 octets accepté par Agave mais auparavant rejeté par le décodeur strict ; +- décodage des dix-huit variantes `StakeInstruction` depuis `solana-stake-interface ^4.3`, au moyen d’un miroir wire privé compatible avec le format historique et décodé par `wincode::deserialize_exact` ; +- distinction entre les layouts de comptes historiques avec sysvars et les layouts BPF modernes, avec `accountLayout`, comptes additionnels et privilèges attendus/observés ; +- classification de `Redelegate` comme instruction historique officiellement désactivée et décodage des transactions échouées comme intentions non commitées ; +- absence de matérialisation Stake tant que l’état préalable du compte, le lockup et la délégation ne sont pas disponibles dans un contrat de projection stable. +- contrat core contextualisé `2`, avec accès stable aux payloads de toutes les instructions outer de la transaction ; +- décodage structurel exact des précompiles Ed25519, secp256k1 et secp256r1, y compris références courantes/externes, signatures multiples, clé ou adresse, recovery ID et message ; +- conservation bornée des composants par longueur, SHA-256 et préfixe hexadécimal, sans recopier les messages et sans recalculer les signatures ; +- interprétation prudente du runtime : acceptation affirmée uniquement pour une transaction réussie, aucune validité cryptographique affirmée pour une transaction échouée ; +- aucune matérialisation des précompiles, avec `materializedOutputs = 0` ; +- décodage des treize instructions ZK ElGamal Proof depuis `solana-zk-elgamal-proof-interface ^0.1` et le runtime Agave ; +- distinction entre preuve inline et preuve référencée par compte, offset `u32 LE`, création optionnelle d’un compte de contexte et fermeture du contexte ; +- séparation bornée contexte/corps pour les preuves inline selon les tailles `Pod` officielles, avec longueurs, SHA-256 et préfixes limités, sans conserver ni vérifier cryptographiquement la preuve ; +- marquage explicite du contenu historique indisponible pour les preuves stockées dans un compte, sans lecture RPC actuelle susceptible de diverger ; +- aucune matérialisation ZK dans `pre.015`, avec `materializedOutputs = 0`. + +`pre.016` décode l’ancien ZK Token Proof Program comme surface historique et couvre ses dix-sept discriminants officiels. Les layouts inline utilisent les tailles `Pod` de un miroir wire local audité contre `solana-zk-token-sdk 3.1.14`; les instructions exactes de cinq octets conservent l’offset vers un compte de preuve, sans lire un état RPC actuel. Les gates historiques `enable_zk_proof_from_account` et `enable_zk_transfer_with_fee`, les arités de contexte et l’interdiction historique des vérifications inner sont exposées explicitement. + +Le runtime Agave `v4.1.1` est un stub sans effet. Un payload qui ne correspond pas exactement à un layout historique est donc classé `current_runtime_noop_invocation`, pas `unsupported` ou `failed`. Aucun corpus mainnet n’ayant été trouvé, la validation repose sur les sources officielles et des fixtures synthétiques. Le décodeur ne vérifie aucune preuve, ne lie pas une transaction à une version runtime supposée et ne produit aucune matérialisation. + +## Matérialisations natives + +Le stateless Slashing Program est couvert depuis `pre.018` par deux layouts exacts. La preuve duplicate block externe n’est pas relue depuis un état RPC actuel ; le décodeur conserve uniquement les données transactionnelles et la relation avec l’instruction Ed25519 précédente. + +La couverture du décodeur est volontairement plus large que celle de chaque matérialiseur spécialisé. ALT, loaders et Feature possèdent des projections lifecycle stables. Les durable nonce accounts System, les comptes de contexte ZK et les rapports Slashing possèdent désormais leurs projections stables. Stake et Vote nécessitent un matérialiseur d’état dédié, `kb_materializer_staking`, car une instruction seule ne suffit pas toujours à reconstruire l’état antérieur, le lockup, la délégation ou l’autorité effective. + +Compute Budget, les précompiles de signature et les preuves ZK sans contexte restent en audit : ils n’introduisent pas à eux seuls un état métier durable, et une projection partielle serait trompeuse. Cette distinction est maintenant suivie explicitement dans le ROADMAP et dans `docs/DECODER_MATERIALIZATION_CONTRACTS.md`. + +## Contrat des événements + +Chaque observation décodée contient notamment : + +- `eventVersion` ; +- program ID, surface et entry code ; +- signature, slot et chemin outer/inner via `DecodedProtocolEvent` ; +- paramètres numériques sans perte ; +- comptes résolus avec rôle, index, clé et privilèges attendus/observés ; +- `transactionSucceeded` ; +- hash du payload ; +- preuve indiquant l’interface officielle utilisée. + +Une transaction échouée peut produire un événement d’intention décodé, mais `observation_committed` reste obligatoirement faux. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs`. +- Aucun discriminant, offset ou ordre de compte ne doit être ajouté sans source officielle vérifiable. +- Les payloads inconnus ou invalides ne doivent jamais provoquer de panic en production. + + +## Précompiles de signature + +Ed25519 et secp256r1 utilisent la sentinelle officielle `u16::MAX` pour référencer le payload de l’instruction cible. secp256k1 n’a pas de sentinelle : chaque index `u8` désigne explicitement une instruction outer, et la provenance `current_instruction` est déduite lorsque cet index égale celui de la cible. Les données après la table d’offsets restent autorisées, car elles peuvent contenir les signatures, clés et messages référencés. + +Les règles zéro signature suivent le runtime : Ed25519 accepte exactement deux octets, secp256k1 exactement un octet, et secp256r1 refuse zéro entrée et limite le compteur à huit. Le décodeur ne dépend d’aucune nouvelle crate officielle : les structures publiées documentent le layout, mais le comportement runtime est reproduit par un parseur local borné et aucune opération cryptographique n’est exécutée. + +## Interfaces et tracing + +Le crate privilégie les interfaces officielles étroites : ALT, System, Vote et Loader v3 utilisent leurs enums officielles avec `wincode`; Stake consomme son interface officielle et un miroir wire privé `wincode` parce que `StakeInstruction` ne dérive pas directement les traits Wincode ; Compute Budget s’aligne sur l’interface Borsh et le comportement unchecked du runtime. Config et Loader v2 conservent des parseurs locaux bornés parce que leurs helpers officiels sont uniquement exposés derrière `bincode`. Loader v4 conserve également un parser local borné : son interface publie l’enum et les comptes, mais ses helpers sont conditionnés par `bincode` et aucun schéma `wincode` n’est exposé. + +Depuis `0.4.2-pre.020`, Loader v3 est désérialisé par `wincode::deserialize_exact`. Les tags, champs, booléens optionnels historiques, payloads suffixés et comptes restent couverts par les fixtures de la crate officielle et par les tests négatifs du décodeur. + +Le target canonique est `kb_decoder_solana_core`. Tout statut `failed` ou `unsupported` après dispatch émet un événement `error` contenant signature, slot, chemin, program ID, processor/version, clé d’input, longueur et hash du payload, statut transactionnel et diagnostics bornés. Une transaction on-chain échouée correctement décodée reste un événement valide non commité. + +## Compatibilité ZK Token historique sans SDK déprécié + +Le crate ne dépend plus de `solana-zk-token-sdk`. Les tags `0..16`, tailles totales de preuve/contexte et règles de forme sont conservés dans une table locale bornée, auditée contre la dernière interface historique et le runtime Agave `v2.0.0`. Aucun helper `bincode` n’est utilisé et le fallback no-op Agave `v4.1.1` reste explicite. +## Matrice de complétude native — `0.4.2-pre.023` + +`docs/NATIVE_SOLANA_DECODER_MATRIX.json` inventorie les 18 surfaces de `kb_program_ids::native_program_ids()` et leurs 121 déclarations de couverture. Le test de matrice impose l’égalité exacte avec `SolanaCoreDecoder::surfaces()` et `SolanaCoreDecoder::coverage()`, ainsi que le nombre de déclarations attendu pour chaque Program ID. + +La matrice complète les tests spécialisés qui parcourent les enums officielles ou tables wire auditées. Elle empêche qu’une surface soit ajoutée au registre partagé sans décodeur, qu’une famille disparaisse de l’agrégateur ou que son nombre de variantes change sans mise à jour explicite de l’audit. + +L’audit consolidé exécuteur/décodeur/RPC est disponible dans `docs/SOLANA_NATIVE_RPC_CLOSURE_AUDIT.md`. + diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/accounts.rs b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/accounts.rs new file mode 100644 index 0000000..8c31595 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/accounts.rs @@ -0,0 +1,137 @@ +// file: kb_decoder_solana_core/src/accounts.rs +// version: 3 + +//! Resolved account-role validation for native instructions. + +/// Expected role and privileges for one positional instruction account. +#[derive(Clone, Copy, Debug)] +pub(crate) struct AccountRole { + /// Stable role code. + pub(crate) role: &'static str, + /// Whether the official instruction contract marks the account writable. + pub(crate) expected_writable: bool, + /// Whether the official instruction contract marks the account as a signer. + pub(crate) expected_signer: std::option::Option, +} + +impl AccountRole { + /// Builds one account role declaration. + pub(crate) const fn new( + role: &'static str, + expected_writable: bool, + expected_signer: bool, + ) -> Self { + return Self { + role, + expected_writable, + expected_signer: std::option::Option::Some(expected_signer), + }; + } + + /// Builds one role whose signer requirement differs across supported runtime generations. + pub(crate) const fn optional_signer(role: &'static str, expected_writable: bool) -> Self { + return Self { + role, + expected_writable, + expected_signer: std::option::Option::None, + }; + } +} + +/// Resolves positional instruction accounts and validates their core indexes. +pub(crate) fn resolve_accounts( + input: &kb_store_core::CoreInstructionReplayInput, + roles: &[crate::AccountRole], + minimum_count: usize, + maximum_count: std::option::Option, +) -> kb_core::Result { + let instruction_accounts = match input.instruction_accounts_json.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "native instruction accounts must be a JSON array", + )); + }, + }; + if instruction_accounts.len() < minimum_count { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "native instruction requires at least {minimum_count} accounts but received {}", + instruction_accounts.len() + ))); + } + if maximum_count.is_some_and(|value| return instruction_accounts.len() > value) { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "native instruction accepts at most {} accounts but received {}", + maximum_count.unwrap_or(0), + instruction_accounts.len() + ))); + } + let account_keys = match input.account_keys_json.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "native transaction account keys must be a JSON array", + )); + }, + }; + let mut output = std::vec::Vec::with_capacity(instruction_accounts.len()); + for (position, instruction_account) in instruction_accounts.iter().enumerate() { + let account_index = + match instruction_account.get("accountIndex").and_then(serde_json::Value::as_u64) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "native instruction account {position} has no valid accountIndex" + ))); + }, + }; + let instruction_key = + match instruction_account.get("accountKey").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "native instruction account {position} has no accountKey" + ))); + }, + }; + let resolved = account_keys.iter().find(|candidate| { + return candidate.get("accountIndex").and_then(serde_json::Value::as_u64) + == std::option::Option::Some(account_index); + }); + let resolved = match resolved { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "native instruction account index {account_index} is absent from resolved keys" + ))); + }, + }; + let resolved_key = match resolved.get("accountKey").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "resolved native account index {account_index} has no accountKey" + ))); + }, + }; + if resolved_key != instruction_key { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "native instruction account index {account_index} resolves to a different key" + ))); + } + let role = roles.get(position); + output.push(serde_json::json!({ + "position": position, + "accountIndex": account_index, + "accountKey": instruction_key, + "role": role.map(|value| return value.role).unwrap_or("additional_account"), + "expectedWritable": role.map(|value| return value.expected_writable), + "expectedSigner": role.map(|value| return value.expected_signer), + "writable": resolved.get("writable").and_then(serde_json::Value::as_bool), + "signer": resolved.get("signer").and_then(serde_json::Value::as_bool), + "executable": resolved.get("executable").and_then(serde_json::Value::as_bool), + "source": resolved.get("source").cloned().unwrap_or(serde_json::Value::Null), + })); + } + return std::result::Result::Ok(serde_json::Value::Array(output)); +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/address_lookup_table.rs b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/address_lookup_table.rs new file mode 100644 index 0000000..9c65cc1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/address_lookup_table.rs @@ -0,0 +1,488 @@ +// file: kb_decoder_solana_core/src/address_lookup_table.rs +// version: 4 + +//! Exact Address Lookup Table instruction decoding through the official interface schema. + +const SOURCE: &str = + "solana-address-lookup-table-interface@3.1.0 ProgramInstruction wincode contract"; + +const CREATE_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("lookup_table", true, false), + crate::AccountRole::optional_signer("authority", false), + crate::AccountRole::new("payer", true, true), + crate::AccountRole::new("system_program", false, false), +]; +const AUTHORITY_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("lookup_table", true, false), + crate::AccountRole::new("authority", false, true), +]; +const EXTEND_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("lookup_table", true, false), + crate::AccountRole::new("authority", false, true), + crate::AccountRole::new("payer", true, true), + crate::AccountRole::new("system_program", false, false), +]; +const CLOSE_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("lookup_table", true, false), + crate::AccountRole::new("authority", false, true), + crate::AccountRole::new("recipient", true, false), +]; + +/// Returns declared Address Lookup Table instruction coverage. +pub(crate) fn address_lookup_table_coverage() +-> std::vec::Vec { + return vec![ + coverage_entry( + "create_lookup_table", + solana_address_lookup_table_interface::instruction::ProgramInstruction::CreateLookupTable { + recent_slot: 0, + bump_seed: 0, + }, + ), + coverage_entry( + "freeze_lookup_table", + solana_address_lookup_table_interface::instruction::ProgramInstruction::FreezeLookupTable, + ), + coverage_entry( + "extend_lookup_table", + solana_address_lookup_table_interface::instruction::ProgramInstruction::ExtendLookupTable { + new_addresses: std::vec::Vec::new(), + }, + ), + coverage_entry( + "deactivate_lookup_table", + solana_address_lookup_table_interface::instruction::ProgramInstruction::DeactivateLookupTable, + ), + coverage_entry( + "close_lookup_table", + solana_address_lookup_table_interface::instruction::ProgramInstruction::CloseLookupTable, + ), + ]; +} + +/// Recognizes one Address Lookup Table instruction without producing an event. +pub(crate) fn address_lookup_table_recognize( + input: &kb_store_core::CoreInstructionReplayInput, + priority: u16, +) -> kb_decoder_api::DecoderRecognition { + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return kb_decoder_api::DecoderRecognition::compatible( + false, + priority, + std::option::Option::Some(crate::ADDRESS_LOOKUP_TABLE_SURFACE_CODE.to_string()), + std::option::Option::Some("malformed_address_lookup_table_instruction".to_string()), + std::option::Option::None, + ); + }, + }; + let tag_result = crate::read_u32_le(bytes.as_slice(), 0); + let tag = match tag_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return kb_decoder_api::DecoderRecognition::compatible( + false, + priority, + std::option::Option::Some(crate::ADDRESS_LOOKUP_TABLE_SURFACE_CODE.to_string()), + std::option::Option::Some("malformed_address_lookup_table_instruction".to_string()), + crate::hexadecimal_prefix(bytes.as_slice(), bytes.len().min(4)), + ); + }, + }; + let entry_code = entry_code(tag); + let exact = if entry_code.is_some() { + wincode::deserialize_exact::< + solana_address_lookup_table_interface::instruction::ProgramInstruction, + >(bytes.as_slice()) + .is_ok() + } else { + false + }; + return kb_decoder_api::DecoderRecognition::compatible( + exact, + priority, + std::option::Option::Some(crate::ADDRESS_LOOKUP_TABLE_SURFACE_CODE.to_string()), + entry_code.map(str::to_string).or_else(|| { + return std::option::Option::Some( + "unknown_address_lookup_table_instruction".to_string(), + ); + }), + crate::hexadecimal_prefix(bytes.as_slice(), 4), + ); +} + +/// Decodes one Address Lookup Table instruction. +pub(crate) fn address_lookup_table_decode( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_decoder_api::DecoderExecutionResult { + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("malformed_address_lookup_table_instruction"), + "address_lookup_table_payload_invalid", + error.to_string(), + ); + }, + }; + let tag_result = crate::read_u32_le(bytes.as_slice(), 0); + let tag = match tag_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("malformed_address_lookup_table_instruction"), + "address_lookup_table_tag_truncated", + error.to_string(), + ); + }, + }; + let entry_code = match entry_code(tag) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return crate::unsupported_result( + "unknown_address_lookup_table_instruction", + "address_lookup_table_tag_unknown", + format!( + "unknown Address Lookup Table instruction tag {tag}; payload_sha256={}", + crate::payload_hash(input) + ), + ); + }, + }; + let instruction_result = wincode::deserialize_exact::< + solana_address_lookup_table_interface::instruction::ProgramInstruction, + >(bytes.as_slice()); + let instruction = match instruction_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some(entry_code), + "address_lookup_table_wincode_invalid", + format!("cannot decode exact Address Lookup Table {entry_code}: {error}"), + ); + }, + }; + return decoded_instruction(input, instruction); +} + +fn decoded_instruction( + input: &kb_store_core::CoreInstructionReplayInput, + instruction: solana_address_lookup_table_interface::instruction::ProgramInstruction, +) -> kb_decoder_api::DecoderExecutionResult { + return match instruction { + solana_address_lookup_table_interface::instruction::ProgramInstruction::CreateLookupTable { + recent_slot, + bump_seed, + } => build( + input, + "create_lookup_table", + CREATE_ROLES, + 4, + std::option::Option::Some(4), + serde_json::json!({ + "recentSlot": recent_slot, + "bumpSeed": bump_seed, + "authoritySignerPolicy": "optional_current_historical_required", + }), + ), + solana_address_lookup_table_interface::instruction::ProgramInstruction::FreezeLookupTable => { + build( + input, + "freeze_lookup_table", + AUTHORITY_ROLES, + 2, + std::option::Option::Some(2), + serde_json::json!({}), + ) + }, + solana_address_lookup_table_interface::instruction::ProgramInstruction::ExtendLookupTable { + new_addresses, + } => { + let account_count = input + .instruction_accounts_json + .as_array() + .map(std::vec::Vec::len) + .unwrap_or(0); + if account_count != 2 && account_count != 4 { + return crate::failed_result( + std::option::Option::Some("extend_lookup_table"), + "address_lookup_table_extend_accounts_invalid", + format!( + "Address Lookup Table extend requires 2 accounts or the complete optional payer/system pair of 4 accounts but received {account_count}" + ), + ); + } + let addresses = new_addresses + .iter() + .map(std::string::ToString::to_string) + .collect::>(); + build( + input, + "extend_lookup_table", + EXTEND_ROLES, + 2, + std::option::Option::Some(4), + serde_json::json!({ + "newAddresses": addresses, + "newAddressCount": new_addresses.len(), + "fundingAccountsPresent": account_count == 4, + }), + ) + }, + solana_address_lookup_table_interface::instruction::ProgramInstruction::DeactivateLookupTable => { + build( + input, + "deactivate_lookup_table", + AUTHORITY_ROLES, + 2, + std::option::Option::Some(2), + serde_json::json!({}), + ) + }, + solana_address_lookup_table_interface::instruction::ProgramInstruction::CloseLookupTable => { + build( + input, + "close_lookup_table", + CLOSE_ROLES, + 3, + std::option::Option::Some(3), + serde_json::json!({}), + ) + }, + }; +} + +fn build( + input: &kb_store_core::CoreInstructionReplayInput, + entry_code: &str, + roles: &[crate::AccountRole], + minimum_count: usize, + maximum_count: std::option::Option, + parameters: serde_json::Value, +) -> kb_decoder_api::DecoderExecutionResult { + let accounts_result = crate::resolve_accounts(input, roles, minimum_count, maximum_count); + let accounts = match accounts_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some(entry_code), + "address_lookup_table_accounts_invalid", + error.to_string(), + ); + }, + }; + return crate::decoded_result( + input, + crate::ADDRESS_LOOKUP_TABLE_SURFACE_CODE, + entry_code, + kb_model::EventFamily::Lifecycle, + false, + accounts, + parameters, + SOURCE, + ); +} + +fn entry_code(tag: u32) -> std::option::Option<&'static str> { + return match tag { + 0 => std::option::Option::Some("create_lookup_table"), + 1 => std::option::Option::Some("freeze_lookup_table"), + 2 => std::option::Option::Some("extend_lookup_table"), + 3 => std::option::Option::Some("deactivate_lookup_table"), + 4 => std::option::Option::Some("close_lookup_table"), + _ => std::option::Option::None, + }; +} + +fn coverage_entry( + entry_code: &str, + instruction: solana_address_lookup_table_interface::instruction::ProgramInstruction, +) -> kb_decoder_api::DecoderCoverageDeclaration { + let discriminator_hex = match wincode::serialize(&instruction) { + std::result::Result::Ok(bytes) => crate::hexadecimal_prefix(bytes.as_slice(), 4), + std::result::Result::Err(_error) => std::option::Option::None, + }; + return kb_decoder_api::DecoderCoverageDeclaration { + program_id: kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID.to_string(), + surface_code: std::option::Option::Some( + crate::ADDRESS_LOOKUP_TABLE_SURFACE_CODE.to_string(), + ), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: entry_code.to_string(), + discriminator_hex, + historical: false, + }; +} + +#[cfg(test)] +mod tests { + use base64::Engine; // rust-rules: trait-import + + fn replay_input( + instruction: &solana_address_lookup_table_interface::instruction::ProgramInstruction, + account_count: usize, + transaction_failed: bool, + ) -> kb_store_core::CoreInstructionReplayInput { + let bytes_result = wincode::serialize(instruction); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("ALT fixture serialization failed: {error}"), + }; + return replay_input_bytes(bytes.as_slice(), account_count, transaction_failed); + } + + fn replay_input_bytes( + bytes: &[u8], + account_count: usize, + transaction_failed: bool, + ) -> kb_store_core::CoreInstructionReplayInput { + let encoded = base64::engine::general_purpose::STANDARD.encode(bytes); + let account_keys = (0..account_count) + .map(|index| { + return serde_json::json!({ + "accountIndex": index, + "accountKey": format!("account{index}"), + "source": "static", + "writable": true, + "signer": true, + "executable": false, + }); + }) + .collect::>(); + let instruction_accounts = (0..account_count) + .map(|index| { + return serde_json::json!({ + "accountIndex": index, + "accountKey": format!("account{index}"), + }); + }) + .collect::>(); + let result = kb_store_core::CoreInstructionReplayInput::new( + "signature:0", + "signature", + 42, + "0", + kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID, + transaction_failed, + if transaction_failed { + std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]})) + } else { + std::option::Option::None + }, + serde_json::Value::Array(account_keys), + serde_json::Value::Array(instruction_accounts), + std::option::Option::Some(serde_json::json!({"dataBase64": encoded})), + std::option::Option::Some("payload-hash".to_string()), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("ALT replay input failed: {error}"), + }; + } + + #[test] + fn every_official_variant_decodes() { + let address = solana_sdk_ids::address_lookup_table::id(); + let fixtures = [ + ( + solana_address_lookup_table_interface::instruction::ProgramInstruction::CreateLookupTable { + recent_slot: u64::MAX, + bump_seed: u8::MAX, + }, + 4, + "create_lookup_table", + ), + ( + solana_address_lookup_table_interface::instruction::ProgramInstruction::FreezeLookupTable, + 2, + "freeze_lookup_table", + ), + ( + solana_address_lookup_table_interface::instruction::ProgramInstruction::ExtendLookupTable { + new_addresses: vec![address, address], + }, + 4, + "extend_lookup_table", + ), + ( + solana_address_lookup_table_interface::instruction::ProgramInstruction::DeactivateLookupTable, + 2, + "deactivate_lookup_table", + ), + ( + solana_address_lookup_table_interface::instruction::ProgramInstruction::CloseLookupTable, + 3, + "close_lookup_table", + ), + ]; + for (instruction, account_count, expected) in fixtures { + let result = crate::address_lookup_table_decode(&replay_input( + &instruction, + account_count, + false, + )); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some(expected) + ); + assert_eq!(result.observations.len(), 1); + } + } + + #[test] + fn extend_accepts_only_absent_or_complete_funding_pair() { + let instruction = solana_address_lookup_table_interface::instruction::ProgramInstruction::ExtendLookupTable { + new_addresses: std::vec::Vec::new(), + }; + let without_funding = + crate::address_lookup_table_decode(&replay_input(&instruction, 2, false)); + assert_eq!(without_funding.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + let incomplete = crate::address_lookup_table_decode(&replay_input(&instruction, 3, false)); + assert_eq!(incomplete.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn truncated_unknown_and_trailing_payloads_are_distinguished() { + let truncated = + crate::address_lookup_table_decode(&replay_input_bytes(&[0, 0, 0], 4, false)); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let unknown = + crate::address_lookup_table_decode(&replay_input_bytes(&[255, 0, 0, 0], 0, false)); + assert_eq!(unknown.status, kb_decoder_api::DecoderOutcomeStatus::Unsupported); + let mut valid = match wincode::serialize( + &solana_address_lookup_table_interface::instruction::ProgramInstruction::FreezeLookupTable, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("ALT fixture serialization failed: {error}"), + }; + valid.push(1); + let trailing = + crate::address_lookup_table_decode(&replay_input_bytes(valid.as_slice(), 2, false)); + assert_eq!(trailing.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn failed_transaction_is_decoded_as_uncommitted_lifecycle_intent() { + let instruction = solana_address_lookup_table_interface::instruction::ProgramInstruction::DeactivateLookupTable; + let result = crate::address_lookup_table_decode(&replay_input(&instruction, 2, true)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!result.observations[0].observation_committed); + assert_eq!(result.observations[0].payload_json["transactionSucceeded"], false); + } + + #[test] + fn coverage_uses_official_encoder_discriminants() { + let coverage = crate::address_lookup_table_coverage(); + assert_eq!(coverage.len(), 5); + assert_eq!(coverage[0].discriminator_hex.as_deref(), std::option::Option::Some("00000000")); + assert_eq!(coverage[4].discriminator_hex.as_deref(), std::option::Option::Some("04000000")); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/compute_budget.rs b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/compute_budget.rs new file mode 100644 index 0000000..778c316 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/compute_budget.rs @@ -0,0 +1,883 @@ +// file: kb_decoder_solana_core/src/compute_budget.rs +// version: 5 + +//! Maximal current and historical Compute Budget instruction decoding. + +use base64::Engine; // rust-rules: trait-import + +const SOURCE_CURRENT: &str = "solana-compute-budget-interface@3.0.0; solana-compute-budget-instruction@4.1.1::try_from_slice_unchecked"; +const SOURCE_HISTORICAL: &str = + "solana-sdk compute_budget::ComputeBudgetInstruction RequestUnitsDeprecated"; + +/// Returns declared Compute Budget instruction coverage. +pub(crate) fn compute_budget_coverage() -> std::vec::Vec +{ + return vec![ + coverage_entry("unused_reserved", "00", false), + coverage_entry("request_units_deprecated", "00", true), + coverage_entry("request_heap_frame", "01", false), + coverage_entry("set_compute_unit_limit", "02", false), + coverage_entry("set_compute_unit_price", "03", false), + coverage_entry("set_loaded_accounts_data_size_limit", "04", false), + ]; +} + +/// Recognizes one Compute Budget instruction without producing an event. +pub(crate) fn compute_budget_recognize( + input: &kb_store_core::CoreInstructionReplayInput, + priority: u16, +) -> kb_decoder_api::DecoderRecognition { + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return kb_decoder_api::DecoderRecognition::compatible( + false, + priority, + std::option::Option::Some(crate::COMPUTE_BUDGET_SURFACE_CODE.to_string()), + std::option::Option::Some("malformed_compute_budget_instruction".to_string()), + std::option::Option::None, + ); + }, + }; + let entry_code = entry_code(bytes.as_slice()); + return kb_decoder_api::DecoderRecognition::compatible( + entry_code.is_some(), + priority, + std::option::Option::Some(crate::COMPUTE_BUDGET_SURFACE_CODE.to_string()), + entry_code.map(str::to_string).or_else(|| { + return std::option::Option::Some("unknown_compute_budget_instruction".to_string()); + }), + crate::hexadecimal_prefix(bytes.as_slice(), 1), + ); +} + +/// Decodes one Compute Budget instruction. +pub(crate) fn compute_budget_decode( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_decoder_api::DecoderExecutionResult { + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("malformed_compute_budget_instruction"), + "compute_budget_payload_invalid", + error.to_string(), + ); + }, + }; + if bytes.is_empty() { + return crate::failed_result( + std::option::Option::Some("malformed_compute_budget_instruction"), + "compute_budget_payload_empty", + "Compute Budget instruction payload is empty", + ); + } + let accounts_result = crate::resolve_accounts(input, &[], 0, std::option::Option::None); + let accounts = match accounts_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + entry_code(bytes.as_slice()), + "compute_budget_accounts_invalid", + error.to_string(), + ); + }, + }; + return match bytes[0] { + 0 if bytes.len() == 1 => kb_decoder_api::DecoderExecutionResult::ignored( + std::option::Option::Some("unused_reserved".to_string()), + ), + 0 if bytes.len() == 9 => { + let units_result = crate::read_u32_le(bytes.as_slice(), 1); + let units = match units_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("request_units_deprecated"), + "compute_budget_request_units_truncated", + error.to_string(), + ); + }, + }; + let additional_fee_result = crate::read_u32_le(bytes.as_slice(), 5); + let additional_fee = match additional_fee_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("request_units_deprecated"), + "compute_budget_request_units_truncated", + error.to_string(), + ); + }, + }; + let mut parameters = serde_json::Map::new(); + parameters.insert("units".to_string(), serde_json::json!(units)); + parameters.insert("additionalFee".to_string(), serde_json::json!(additional_fee)); + append_transaction_profile(input, &mut parameters); + crate::decoded_result( + input, + crate::COMPUTE_BUDGET_SURFACE_CODE, + "request_units_deprecated", + kb_model::EventFamily::Audit, + true, + accounts, + serde_json::Value::Object(parameters), + SOURCE_HISTORICAL, + ) + }, + 0 => crate::failed_result( + std::option::Option::Some("request_units_deprecated"), + "compute_budget_request_units_size_invalid", + format!( + "historical RequestUnitsDeprecated requires 9 bytes but received {}", + bytes.len() + ), + ), + 1 => { + decode_u32_instruction(input, bytes.as_slice(), accounts, "request_heap_frame", "bytes") + }, + 2 => decode_u32_instruction( + input, + bytes.as_slice(), + accounts, + "set_compute_unit_limit", + "computeUnitLimit", + ), + 3 => decode_u64_instruction( + input, + bytes.as_slice(), + accounts, + "set_compute_unit_price", + "microLamports", + ), + 4 => decode_u32_instruction( + input, + bytes.as_slice(), + accounts, + "set_loaded_accounts_data_size_limit", + "bytes", + ), + tag => crate::unsupported_result( + "unknown_compute_budget_instruction", + "compute_budget_tag_unknown", + format!( + "unknown Compute Budget instruction tag {tag}; payload_sha256={}", + crate::payload_hash(input) + ), + ), + }; +} + +fn decode_u32_instruction( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], + accounts: serde_json::Value, + entry_code: &str, + field_name: &str, +) -> kb_decoder_api::DecoderExecutionResult { + if bytes.len() < 5 { + return crate::failed_result( + std::option::Option::Some(entry_code), + "compute_budget_instruction_size_invalid", + format!( + "Compute Budget {entry_code} requires at least 5 bytes but received {}", + bytes.len() + ), + ); + } + let value_result = crate::read_u32_le(bytes, 1); + let value = match value_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some(entry_code), + "compute_budget_instruction_truncated", + error.to_string(), + ); + }, + }; + let mut parameters = serde_json::Map::new(); + parameters.insert(field_name.to_string(), serde_json::json!(value)); + append_runtime_ignored_trailing_data(&mut parameters, bytes, 5); + append_transaction_profile(input, &mut parameters); + return crate::decoded_result( + input, + crate::COMPUTE_BUDGET_SURFACE_CODE, + entry_code, + kb_model::EventFamily::Audit, + false, + accounts, + serde_json::Value::Object(parameters), + SOURCE_CURRENT, + ); +} + +fn decode_u64_instruction( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], + accounts: serde_json::Value, + entry_code: &str, + field_name: &str, +) -> kb_decoder_api::DecoderExecutionResult { + if bytes.len() < 9 { + return crate::failed_result( + std::option::Option::Some(entry_code), + "compute_budget_instruction_size_invalid", + format!( + "Compute Budget {entry_code} requires at least 9 bytes but received {}", + bytes.len() + ), + ); + } + let value_result = crate::read_u64_le(bytes, 1); + let value = match value_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some(entry_code), + "compute_budget_instruction_truncated", + error.to_string(), + ); + }, + }; + let mut parameters = serde_json::Map::new(); + parameters.insert(field_name.to_string(), serde_json::json!(value)); + append_runtime_ignored_trailing_data(&mut parameters, bytes, 9); + append_transaction_profile(input, &mut parameters); + return crate::decoded_result( + input, + crate::COMPUTE_BUDGET_SURFACE_CODE, + entry_code, + kb_model::EventFamily::Audit, + false, + accounts, + serde_json::Value::Object(parameters), + SOURCE_CURRENT, + ); +} + +fn append_transaction_profile( + input: &kb_store_core::CoreInstructionReplayInput, + parameters: &mut serde_json::Map, +) { + parameters.insert("transactionProfile".to_string(), transaction_profile(input)); +} + +fn transaction_profile(input: &kb_store_core::CoreInstructionReplayInput) -> serde_json::Value { + let target_path = input.instruction_path.as_str(); + let mut instruction_rows = std::vec::Vec::new(); + let mut effective = serde_json::Map::new(); + let mut decoded_count = 0_u64; + let mut unsupported_count = 0_u64; + let mut failed_count = 0_u64; + let mut trailing_count = 0_u64; + let outer_rows = compute_budget_outer_rows(input); + for row in outer_rows { + if row.status == "decoded" { + decoded_count = decoded_count.saturating_add(1); + } else if row.status == "unsupported" { + unsupported_count = unsupported_count.saturating_add(1); + } else if row.status == "failed" { + failed_count = failed_count.saturating_add(1); + } + if row.trailing_data_present { + trailing_count = trailing_count.saturating_add(1); + } + apply_effective_value(&mut effective, &row); + instruction_rows.push(row.to_json()); + } + let emitter_path = profile_emitter_path(instruction_rows.as_slice()); + let current_is_emitter = emitter_path.as_deref() == std::option::Option::Some(target_path); + return serde_json::json!({ + "profileVersion": 1, + "profileSemantics": "transaction_compute_budget_last_write_wins", + "currentInstructionIsProfileEmitter": current_is_emitter, + "profileEmitterInstructionPath": emitter_path, + "computeBudgetInstructionCount": instruction_rows.len(), + "decodedInstructionCount": decoded_count, + "unsupportedInstructionCount": unsupported_count, + "failedInstructionCount": failed_count, + "runtimeIgnoredTrailingDataInstructionCount": trailing_count, + "effectiveValues": serde_json::Value::Object(effective), + "instructions": instruction_rows, + "transactionFinalRuntimeMetricsCaptured": false, + "computeUnitsConsumedCaptured": false, + }); +} + +#[derive(Clone, Debug)] +struct ComputeBudgetProfileRow { + instruction_index: u64, + instruction_path: std::string::String, + entry_code: std::string::String, + status: &'static str, + parameters: serde_json::Value, + payload_hash: serde_json::Value, + trailing_data_present: bool, + diagnostic: serde_json::Value, +} + +impl ComputeBudgetProfileRow { + fn to_json(&self) -> serde_json::Value { + return serde_json::json!({ + "instructionIndex": self.instruction_index, + "instructionPath": self.instruction_path, + "entryCode": self.entry_code, + "status": self.status, + "parameters": self.parameters, + "payloadHash": self.payload_hash, + "trailingDataPresent": self.trailing_data_present, + "diagnostic": self.diagnostic, + }); + } +} + +fn compute_budget_outer_rows( + input: &kb_store_core::CoreInstructionReplayInput, +) -> std::vec::Vec { + let mut rows = std::vec::Vec::new(); + let outer_values = match input.outer_instructions_json.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => return rows, + }; + for value in outer_values { + if value.get("programId").and_then(serde_json::Value::as_str) + != std::option::Option::Some(kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID) + { + continue; + } + let instruction_index = + value.get("instructionIndex").and_then(serde_json::Value::as_u64).unwrap_or(0); + let instruction_path = value + .get("instructionPath") + .and_then(serde_json::Value::as_str) + .unwrap_or("") + .to_string(); + let payload_hash = value.get("payloadHash").cloned().unwrap_or(serde_json::Value::Null); + let payload_json = match value.get("payloadJson") { + std::option::Option::Some(payload) if !payload.is_null() => payload, + _ => { + rows.push(profile_failure_row( + instruction_index, + instruction_path, + payload_hash, + "payload_missing", + "outer Compute Budget instruction payload is not retained", + )); + continue; + }, + }; + let bytes_result = decode_payload_json_for_profile(payload_json); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + rows.push(profile_failure_row( + instruction_index, + instruction_path, + payload_hash, + "payload_invalid", + error.as_str(), + )); + continue; + }, + }; + rows.push(profile_row_from_bytes( + instruction_index, + instruction_path, + payload_hash, + bytes.as_slice(), + )); + } + if rows.is_empty() && input.program_id == kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID { + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + rows.push(profile_failure_row( + input.instruction_path.parse::().unwrap_or(0), + input.instruction_path.clone(), + serde_json::Value::String(crate::payload_hash(input)), + "payload_invalid", + error.to_string().as_str(), + )); + return rows; + }, + }; + rows.push(profile_row_from_bytes( + input.instruction_path.parse::().unwrap_or(0), + input.instruction_path.clone(), + serde_json::Value::String(crate::payload_hash(input)), + bytes.as_slice(), + )); + } + return rows; +} + +fn profile_row_from_bytes( + instruction_index: u64, + instruction_path: std::string::String, + payload_hash: serde_json::Value, + bytes: &[u8], +) -> ComputeBudgetProfileRow { + let entry = entry_code(bytes).unwrap_or("unknown_compute_budget_instruction"); + let mut diagnostic = serde_json::Value::Null; + let mut status = "decoded"; + let mut parameters = serde_json::Map::new(); + let mut trailing_data_present = false; + match entry { + "unused_reserved" => {}, + "request_units_deprecated" => { + if bytes.len() == 9 { + if let std::result::Result::Ok(value) = crate::read_u32_le(bytes, 1) { + parameters.insert("units".to_string(), serde_json::json!(value)); + } + if let std::result::Result::Ok(value) = crate::read_u32_le(bytes, 5) { + parameters.insert("additionalFee".to_string(), serde_json::json!(value)); + } + } else { + status = "failed"; + diagnostic = serde_json::json!("historical request units payload size is invalid"); + } + }, + "request_heap_frame" | "set_compute_unit_limit" | "set_loaded_accounts_data_size_limit" => { + if bytes.len() >= 5 { + if let std::result::Result::Ok(value) = crate::read_u32_le(bytes, 1) { + let field_name = match entry { + "request_heap_frame" => "bytes", + "set_compute_unit_limit" => "computeUnitLimit", + _ => "bytes", + }; + parameters.insert(field_name.to_string(), serde_json::json!(value)); + } + trailing_data_present = bytes.len() > 5; + } else { + status = "failed"; + diagnostic = serde_json::json!("current u32 Compute Budget payload is truncated"); + } + }, + "set_compute_unit_price" => { + if bytes.len() >= 9 { + if let std::result::Result::Ok(value) = crate::read_u64_le(bytes, 1) { + parameters.insert("microLamports".to_string(), serde_json::json!(value)); + } + trailing_data_present = bytes.len() > 9; + } else { + status = "failed"; + diagnostic = serde_json::json!("current u64 Compute Budget payload is truncated"); + } + }, + _ => { + status = "unsupported"; + diagnostic = serde_json::json!("unknown Compute Budget instruction tag"); + }, + } + return ComputeBudgetProfileRow { + instruction_index, + instruction_path, + entry_code: entry.to_string(), + status, + parameters: serde_json::Value::Object(parameters), + payload_hash, + trailing_data_present, + diagnostic, + }; +} + +fn profile_failure_row( + instruction_index: u64, + instruction_path: std::string::String, + payload_hash: serde_json::Value, + code: &str, + message: &str, +) -> ComputeBudgetProfileRow { + return ComputeBudgetProfileRow { + instruction_index, + instruction_path, + entry_code: "malformed_compute_budget_instruction".to_string(), + status: "failed", + parameters: serde_json::Value::Null, + payload_hash, + trailing_data_present: false, + diagnostic: serde_json::json!({"code": code, "message": message}), + }; +} + +fn decode_payload_json_for_profile( + payload: &serde_json::Value, +) -> std::result::Result, std::string::String> { + let encoded = match payload.get("dataBase64").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err("payload does not contain dataBase64".to_string()); + }, + }; + let maximum_encoded_length = crate::MAX_NATIVE_INSTRUCTION_PAYLOAD_BYTES + .saturating_mul(4) + .saturating_div(3) + .saturating_add(4); + if encoded.len() > maximum_encoded_length { + return std::result::Result::Err(format!( + "payload base64 exceeds {maximum_encoded_length} bytes" + )); + } + let decoded_result = base64::engine::general_purpose::STANDARD.decode(encoded.as_bytes()); + let decoded = match decoded_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!("payload is not valid base64: {error}")); + }, + }; + if decoded.len() > crate::MAX_NATIVE_INSTRUCTION_PAYLOAD_BYTES { + return std::result::Result::Err(format!( + "payload exceeds {} decoded bytes", + crate::MAX_NATIVE_INSTRUCTION_PAYLOAD_BYTES + )); + } + return std::result::Result::Ok(decoded); +} + +fn apply_effective_value( + effective: &mut serde_json::Map, + row: &ComputeBudgetProfileRow, +) { + if row.status != "decoded" { + return; + } + let value = match row.entry_code.as_str() { + "request_units_deprecated" => serde_json::json!({ + "units": row.parameters.get("units").cloned().unwrap_or(serde_json::Value::Null), + "additionalFee": row.parameters.get("additionalFee").cloned().unwrap_or(serde_json::Value::Null), + }), + "request_heap_frame" => { + row.parameters.get("bytes").cloned().unwrap_or(serde_json::Value::Null) + }, + "set_compute_unit_limit" => row + .parameters + .get("computeUnitLimit") + .cloned() + .unwrap_or(serde_json::Value::Null), + "set_compute_unit_price" => { + row.parameters.get("microLamports").cloned().unwrap_or(serde_json::Value::Null) + }, + "set_loaded_accounts_data_size_limit" => { + row.parameters.get("bytes").cloned().unwrap_or(serde_json::Value::Null) + }, + _ => return, + }; + effective.insert( + row.entry_code.clone(), + serde_json::json!({ + "value": value, + "sourceInstructionIndex": row.instruction_index, + "sourceInstructionPath": row.instruction_path, + }), + ); +} + +fn profile_emitter_path( + instruction_rows: &[serde_json::Value], +) -> std::option::Option { + for row in instruction_rows { + if row.get("status").and_then(serde_json::Value::as_str) + != std::option::Option::Some("decoded") + { + continue; + } + if row.get("entryCode").and_then(serde_json::Value::as_str) + == std::option::Option::Some("unused_reserved") + { + continue; + } + return row + .get("instructionPath") + .and_then(serde_json::Value::as_str) + .map(|value| return value.to_string()); + } + return std::option::Option::None; +} + +fn append_runtime_ignored_trailing_data( + parameters: &mut serde_json::Map, + bytes: &[u8], + decoded_byte_length: usize, +) { + let trailing_data = match bytes.get(decoded_byte_length..) { + std::option::Option::Some(value) if !value.is_empty() => value, + _ => return, + }; + parameters.insert("trailingDataByteLength".to_string(), serde_json::json!(trailing_data.len())); + parameters.insert( + "trailingDataSha256".to_string(), + serde_json::json!(crate::hash_bytes(trailing_data)), + ); + parameters.insert( + "trailingDataPrefixHex".to_string(), + serde_json::json!(crate::hexadecimal_prefix(trailing_data, trailing_data.len().min(16),)), + ); + parameters.insert( + "trailingDataSemantics".to_string(), + serde_json::json!("ignored_by_runtime_borsh_unchecked"), + ); +} + +fn entry_code(bytes: &[u8]) -> std::option::Option<&'static str> { + let tag = match bytes.first() { + std::option::Option::Some(value) => *value, + std::option::Option::None => return std::option::Option::None, + }; + return match tag { + 0 if bytes.len() == 1 => std::option::Option::Some("unused_reserved"), + 0 if bytes.len() == 9 => std::option::Option::Some("request_units_deprecated"), + 1 => std::option::Option::Some("request_heap_frame"), + 2 => std::option::Option::Some("set_compute_unit_limit"), + 3 => std::option::Option::Some("set_compute_unit_price"), + 4 => std::option::Option::Some("set_loaded_accounts_data_size_limit"), + _ => std::option::Option::None, + }; +} + +fn coverage_entry( + entry_code: &str, + discriminator_hex: &str, + historical: bool, +) -> kb_decoder_api::DecoderCoverageDeclaration { + return kb_decoder_api::DecoderCoverageDeclaration { + program_id: kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID.to_string(), + surface_code: std::option::Option::Some(crate::COMPUTE_BUDGET_SURFACE_CODE.to_string()), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: entry_code.to_string(), + discriminator_hex: std::option::Option::Some(discriminator_hex.to_string()), + historical, + }; +} + +#[cfg(test)] +mod tests { + use base64::Engine; // rust-rules: trait-import + + fn replay_input( + bytes: &[u8], + transaction_failed: bool, + ) -> kb_store_core::CoreInstructionReplayInput { + let encoded = base64::engine::general_purpose::STANDARD.encode(bytes); + let result = kb_store_core::CoreInstructionReplayInput::new( + "signature:0", + "signature", + 42, + "0", + kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID, + transaction_failed, + if transaction_failed { + std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]})) + } else { + std::option::Option::None + }, + serde_json::json!([]), + serde_json::json!([]), + std::option::Option::Some(serde_json::json!({"dataBase64": encoded})), + std::option::Option::Some("payload-hash".to_string()), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("replay input failed: {error}"), + }; + } + + fn replay_input_with_outer( + bytes: &[u8], + instruction_path: &str, + outer_instructions_json: serde_json::Value, + ) -> kb_store_core::CoreInstructionReplayInput { + let encoded = base64::engine::general_purpose::STANDARD.encode(bytes); + let result = kb_store_core::CoreInstructionReplayInput::new( + "signature:0", + "signature", + 42, + instruction_path, + kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID, + false, + std::option::Option::None, + serde_json::json!([]), + serde_json::json!([]), + std::option::Option::Some(serde_json::json!({"dataBase64": encoded})), + std::option::Option::Some("payload-hash".to_string()), + outer_instructions_json, + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("replay input failed: {error}"), + }; + } + + fn outer_instruction( + index: u64, + path: &str, + program_id: &str, + bytes: &[u8], + ) -> serde_json::Value { + let encoded = base64::engine::general_purpose::STANDARD.encode(bytes); + return serde_json::json!({ + "instructionIndex": index, + "instructionPath": path, + "programId": program_id, + "payloadJson": {"dataBase64": encoded}, + "payloadHash": format!("hash-{path}"), + }); + } + + #[test] + fn transaction_profile_aggregates_outer_compute_budget_instructions_once() { + let outer = serde_json::json!([ + outer_instruction( + 0, + "0", + kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID, + &[2, 64, 66, 15, 0], + ), + outer_instruction(1, "1", kb_program_ids::SYSTEM_PROGRAM_ID, &[2, 1, 0, 0, 0],), + outer_instruction( + 2, + "2", + kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID, + &[3, 7, 0, 0, 0, 0, 0, 0, 0], + ), + outer_instruction( + 3, + "3", + kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID, + &[2, 128, 132, 30, 0], + ), + ]); + let result = + crate::compute_budget_decode(&replay_input_with_outer(&[2, 64, 66, 15, 0], "0", outer)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + let profile = &result.observations[0].payload_json["parameters"]["transactionProfile"]; + assert_eq!(profile["currentInstructionIsProfileEmitter"], true); + assert_eq!(profile["computeBudgetInstructionCount"], 3); + assert_eq!(profile["effectiveValues"]["set_compute_unit_limit"]["value"], 2_000_000); + assert_eq!( + profile["effectiveValues"]["set_compute_unit_limit"]["sourceInstructionPath"], + "3" + ); + assert_eq!(profile["effectiveValues"]["set_compute_unit_price"]["value"], 7); + } + + #[test] + fn official_current_encoder_matches_documented_layout() { + let instruction = + solana_compute_budget_interface::ComputeBudgetInstruction::SetComputeUnitPrice(7); + let bytes_result = borsh::to_vec(&instruction); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("compute budget fixture serialization failed: {error}") + }, + }; + assert_eq!(bytes, [3, 7, 0, 0, 0, 0, 0, 0, 0]); + let result = crate::compute_budget_decode(&replay_input(bytes.as_slice(), false)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some("set_compute_unit_price") + ); + } + + #[test] + fn all_current_variants_decode_with_exact_numeric_values() { + let fixtures = [ + (vec![1, 0, 0, 4, 0], "request_heap_frame"), + (vec![2, 255, 255, 255, 255], "set_compute_unit_limit"), + (vec![3, 255, 255, 255, 255, 255, 255, 255, 255], "set_compute_unit_price"), + (vec![4, 0, 0, 0, 1], "set_loaded_accounts_data_size_limit"), + ]; + for (bytes, expected) in fixtures { + let result = crate::compute_budget_decode(&replay_input(bytes.as_slice(), false)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some(expected) + ); + assert_eq!(result.observations.len(), 1); + } + } + + #[test] + fn historical_request_units_is_decoded() { + let result = + crate::compute_budget_decode(&replay_input(&[0, 64, 13, 3, 0, 7, 0, 0, 0], false)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some("request_units_deprecated") + ); + assert_eq!(result.observations[0].payload_json["parameters"]["units"], 200_000); + assert_eq!(result.observations[0].payload_json["parameters"]["additionalFee"], 7); + } + + #[test] + fn reserved_unused_tag_is_ignored_without_false_event() { + let result = crate::compute_budget_decode(&replay_input(&[0], false)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Ignored); + assert!(result.observations.is_empty()); + } + + #[test] + fn truncated_and_unknown_payloads_are_distinguished() { + let truncated = crate::compute_budget_decode(&replay_input(&[2, 1, 2], false)); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let unknown = crate::compute_budget_decode(&replay_input(&[255, 1, 2, 3], false)); + assert_eq!(unknown.status, kb_decoder_api::DecoderOutcomeStatus::Unsupported); + } + + #[test] + fn current_variants_accept_and_preserve_runtime_ignored_trailing_bytes() { + let result = crate::compute_budget_decode(&replay_input( + &[2, 64, 13, 3, 0, 1, 2, 3, 4, 5, 6, 7], + true, + )); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some("set_compute_unit_limit") + ); + assert_eq!(result.observations[0].payload_json["parameters"]["computeUnitLimit"], 200_000); + assert_eq!(result.observations[0].payload_json["parameters"]["trailingDataByteLength"], 7); + assert_eq!( + result.observations[0].payload_json["parameters"]["trailingDataPrefixHex"], + "01020304050607" + ); + assert_eq!( + result.observations[0].payload_json["parameters"]["trailingDataSemantics"], + "ignored_by_runtime_borsh_unchecked" + ); + assert!(result.observations[0].transaction_failed); + assert!(!result.observations[0].observation_committed); + } + + #[test] + fn failed_transaction_event_is_not_committed() { + let result = crate::compute_budget_decode(&replay_input(&[2, 64, 13, 3, 0], true)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(result.observations[0].transaction_failed); + assert!(!result.observations[0].observation_committed); + assert_eq!(result.observations[0].payload_json["transactionSucceeded"], false); + } + + #[test] + fn coverage_declares_current_and_historical_entries() { + let coverage = crate::compute_budget_coverage(); + assert_eq!(coverage.len(), 6); + assert!(coverage.iter().any(|entry| { + return entry.entry_code == "request_units_deprecated" && entry.historical; + })); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/config.rs b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/config.rs new file mode 100644 index 0000000..c9059ca --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/config.rs @@ -0,0 +1,487 @@ +// file: kb_decoder_solana_core/src/config.rs +// version: 3 + +//! Generic bounded Config Program store decoding without inventing payload semantics. + +const SOURCE: &str = + "solana-config-interface@2.0.0 plus solana-config-program@2.2.20 processor contract"; +const MAX_CONFIG_KEYS: usize = 128; + +#[derive(Clone, Debug, Eq, PartialEq)] +struct ParsedConfigPayload { + keys: std::vec::Vec<(std::string::String, bool)>, + data_offset: usize, +} + +/// Returns declared Config Program instruction coverage. +pub(crate) fn config_coverage() -> std::vec::Vec { + return vec![kb_decoder_api::DecoderCoverageDeclaration { + program_id: kb_program_ids::CONFIG_PROGRAM_ID.to_string(), + surface_code: std::option::Option::Some(crate::CONFIG_SURFACE_CODE.to_string()), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: "store".to_string(), + discriminator_hex: std::option::Option::None, + historical: false, + }]; +} + +/// Recognizes one generic Config Program store instruction. +pub(crate) fn config_recognize( + input: &kb_store_core::CoreInstructionReplayInput, + priority: u16, +) -> kb_decoder_api::DecoderRecognition { + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return kb_decoder_api::DecoderRecognition::compatible( + false, + priority, + std::option::Option::Some(crate::CONFIG_SURFACE_CODE.to_string()), + std::option::Option::Some("malformed_config_store".to_string()), + std::option::Option::None, + ); + }, + }; + let exact = parse_payload(bytes.as_slice()).is_ok(); + return kb_decoder_api::DecoderRecognition::compatible( + exact, + priority, + std::option::Option::Some(crate::CONFIG_SURFACE_CODE.to_string()), + std::option::Option::Some(if exact { + "store".to_string() + } else { + "malformed_config_store".to_string() + }), + crate::hexadecimal_prefix(bytes.as_slice(), bytes.len().min(3)), + ); +} + +/// Decodes one generic Config Program store instruction. +pub(crate) fn config_decode( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_decoder_api::DecoderExecutionResult { + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("malformed_config_store"), + "config_payload_invalid", + error.to_string(), + ); + }, + }; + let parsed_result = parse_payload(bytes.as_slice()); + let parsed = match parsed_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("store"), + "config_keys_invalid", + error.to_string(), + ); + }, + }; + let accounts_result = resolve_and_validate_accounts(input, &parsed); + let accounts = match accounts_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("store"), + "config_accounts_invalid", + error.to_string(), + ); + }, + }; + let data = match bytes.get(parsed.data_offset..) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return crate::failed_result( + std::option::Option::Some("store"), + "config_data_offset_invalid", + "Config data offset exceeds retained payload", + ); + }, + }; + let keys_json = parsed + .keys + .iter() + .map(|(pubkey, signer)| { + return serde_json::json!({"pubkey": pubkey, "signer": signer}); + }) + .collect::>(); + let signer_count = parsed.keys.iter().filter(|(_, signer)| return *signer).count(); + return crate::decoded_result( + input, + crate::CONFIG_SURFACE_CODE, + "store", + kb_model::EventFamily::Admin, + false, + accounts, + serde_json::json!({ + "configuredKeys": keys_json, + "configuredKeyCount": parsed.keys.len(), + "configuredSignerCount": signer_count, + "configDataByteLength": data.len(), + "configDataSha256": crate::hash_bytes(data), + "configDataPrefixHex": crate::hexadecimal_prefix(data, data.len().min(16)), + "configDataSemantics": "opaque_program_specific", + }), + SOURCE, + ); +} + +fn parse_payload(bytes: &[u8]) -> kb_core::Result { + let length_result = read_compact_u16(bytes); + let (key_count, mut offset) = match length_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let key_count_usize = usize::from(key_count); + if key_count_usize > MAX_CONFIG_KEYS { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Config key count {key_count_usize} exceeds bounded maximum {}", + MAX_CONFIG_KEYS + ))); + } + let required = offset.saturating_add(key_count_usize.saturating_mul(33)); + if required > bytes.len() { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Config key vector requires {required} bytes but payload contains {}", + bytes.len() + ))); + } + let mut keys = std::vec::Vec::with_capacity(key_count_usize); + for index in 0..key_count_usize { + let pubkey_end = offset.saturating_add(32); + let pubkey_bytes = match bytes.get(offset..pubkey_end) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Config key {index} pubkey is truncated" + ))); + }, + }; + let signer_byte = match bytes.get(pubkey_end) { + std::option::Option::Some(value) => *value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Config key {index} signer flag is truncated" + ))); + }, + }; + let signer = match signer_byte { + 0 => false, + 1 => true, + value => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Config key {index} signer flag {value} is not a canonical bool" + ))); + }, + }; + let pubkey = bs58::encode(pubkey_bytes).into_string(); + if keys.iter().any(|(existing_pubkey, existing_signer)| { + return existing_pubkey == &pubkey && *existing_signer == signer; + }) { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Config key {index} duplicates an earlier key/signature pair" + ))); + } + keys.push((pubkey, signer)); + offset = pubkey_end.saturating_add(1); + } + return std::result::Result::Ok(ParsedConfigPayload { keys, data_offset: offset }); +} + +fn read_compact_u16(bytes: &[u8]) -> kb_core::Result<(u16, usize)> { + let first = match bytes.first() { + std::option::Option::Some(value) => *value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Config compact-u16 key count is missing", + )); + }, + }; + let mut value = u16::from(first & 0x7f); + if first & 0x80 == 0 { + return std::result::Result::Ok((value, 1)); + } + let second = match bytes.get(1) { + std::option::Option::Some(value) => *value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Config compact-u16 key count is truncated after one byte", + )); + }, + }; + value |= u16::from(second & 0x7f) << 7; + if second & 0x80 == 0 { + if value < 128 { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Config compact-u16 key count uses a non-canonical two-byte encoding", + )); + } + return std::result::Result::Ok((value, 2)); + } + let third = match bytes.get(2) { + std::option::Option::Some(value) => *value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Config compact-u16 key count is truncated after two bytes", + )); + }, + }; + if third & 0xfc != 0 { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Config compact-u16 key count exceeds u16 or has a continuation bit", + )); + } + value |= u16::from(third) << 14; + if value < 16_384 { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Config compact-u16 key count uses a non-canonical three-byte encoding", + )); + } + return std::result::Result::Ok((value, 3)); +} + +fn resolve_and_validate_accounts( + input: &kb_store_core::CoreInstructionReplayInput, + parsed: &ParsedConfigPayload, +) -> kb_core::Result { + let instruction_accounts = match input.instruction_accounts_json.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Config instruction accounts must be an array", + )); + }, + }; + let config_key = match instruction_accounts + .first() + .and_then(|value| return value.get("accountKey")) + .and_then(serde_json::Value::as_str) + { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Config instruction requires a config account at position zero", + )); + }, + }; + let required_signers = parsed + .keys + .iter() + .filter(|(pubkey, signer)| return *signer && pubkey != config_key) + .map(|(pubkey, _signer)| return pubkey.as_str()) + .collect::>(); + let mut roles = std::vec::Vec::with_capacity(required_signers.len().saturating_add(1)); + roles.push(crate::AccountRole::optional_signer("config_account", true)); + for _signer in &required_signers { + roles.push(crate::AccountRole::new("configured_signer", true, true)); + } + let minimum_count = required_signers.len().saturating_add(1); + let accounts = match crate::resolve_accounts( + input, + roles.as_slice(), + minimum_count, + std::option::Option::None, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let resolved = match accounts.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "resolved Config accounts must be an array", + )); + }, + }; + for (index, expected) in required_signers.iter().enumerate() { + let position = index.saturating_add(1); + let actual = resolved + .get(position) + .and_then(|value| return value.get("accountKey")) + .and_then(serde_json::Value::as_str); + if actual != std::option::Option::Some(*expected) { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Config signer account {position} must match encoded key {expected}" + ))); + } + } + return std::result::Result::Ok(accounts); +} + +#[cfg(test)] +mod tests { + use base64::Engine; // rust-rules: trait-import + + fn encode_payload(keys: &[([u8; 32], bool)], data: &[u8]) -> std::vec::Vec { + let mut output = std::vec::Vec::new(); + let count = keys.len(); + if count < 128 { + output.push(count as u8); + } else { + panic!("test fixture key count exceeds one-byte compact encoding"); + } + for (pubkey, signer) in keys { + output.extend_from_slice(pubkey); + output.push(u8::from(*signer)); + } + output.extend_from_slice(data); + return output; + } + + fn replay_input( + bytes: &[u8], + extra_accounts: &[std::string::String], + transaction_failed: bool, + ) -> kb_store_core::CoreInstructionReplayInput { + let mut keys = vec!["ConfigAccount1111111111111111111111111111".to_string()]; + keys.extend_from_slice(extra_accounts); + let account_keys = keys + .iter() + .enumerate() + .map(|(index, key)| { + return serde_json::json!({ + "accountIndex": index, + "accountKey": key, + "source": "static", + "writable": true, + "signer": true, + "executable": false, + }); + }) + .collect::>(); + let instruction_accounts = keys + .iter() + .enumerate() + .map(|(index, key)| { + return serde_json::json!({"accountIndex": index, "accountKey": key}); + }) + .collect::>(); + let result = kb_store_core::CoreInstructionReplayInput::new( + "signature:0", + "signature", + 42, + "0", + kb_program_ids::CONFIG_PROGRAM_ID, + transaction_failed, + if transaction_failed { + std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]})) + } else { + std::option::Option::None + }, + serde_json::Value::Array(account_keys), + serde_json::Value::Array(instruction_accounts), + std::option::Option::Some(serde_json::json!({ + "dataBase64": base64::engine::general_purpose::STANDARD.encode(bytes), + })), + std::option::Option::Some("payload-hash".to_string()), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Config replay input failed: {error}"), + }; + } + + #[test] + fn valid_generic_store_is_recognized_exactly() { + let payload = encode_payload(&[], &[1, 2, 3]); + let input = replay_input(payload.as_slice(), &[], false); + let recognition = crate::config_recognize(&input, 100); + assert!(recognition.compatible); + assert!(recognition.exact); + assert_eq!(recognition.entry_code.as_deref(), std::option::Option::Some("store")); + let malformed = crate::config_recognize(&replay_input(&[], &[], false), 100); + assert!(malformed.compatible); + assert!(!malformed.exact); + } + + #[test] + fn empty_key_set_and_opaque_data_decode_without_invented_semantics() { + let payload = encode_payload(&[], &[1, 2, 3, 4]); + let result = crate::config_decode(&replay_input(payload.as_slice(), &[], false)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(result.observations[0].payload_json["parameters"]["configuredKeyCount"], 0); + assert_eq!(result.observations[0].payload_json["parameters"]["configDataByteLength"], 4); + assert_eq!( + result.observations[0].payload_json["parameters"]["configDataSemantics"], + "opaque_program_specific" + ); + } + + #[test] + fn encoded_signer_keys_are_resolved_in_official_account_order() { + let signer_bytes = [7_u8; 32]; + let signer = bs58::encode(signer_bytes).into_string(); + let payload = encode_payload(&[([3_u8; 32], false), (signer_bytes, true)], &[9]); + let result = crate::config_decode(&replay_input(payload.as_slice(), &[signer], false)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(result.observations[0].payload_json["parameters"]["configuredSignerCount"], 1); + assert_eq!(result.observations[0].payload_json["accounts"][1]["role"], "configured_signer"); + } + + #[test] + fn malformed_compact_length_keys_and_bool_fail_safely() { + for payload in [ + vec![], + vec![0x80], + vec![1, 2, 3], + { + let mut value = vec![1]; + value.extend_from_slice(&[0_u8; 32]); + value.push(2); + value + }, + vec![0x80, 0], + { + let mut value = vec![2]; + value.extend_from_slice(&[4_u8; 32]); + value.push(1); + value.extend_from_slice(&[4_u8; 32]); + value.push(1); + value + }, + ] { + let result = crate::config_decode(&replay_input(payload.as_slice(), &[], false)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + } + + #[test] + fn missing_or_mismatched_signer_accounts_fail() { + let signer_bytes = [8_u8; 32]; + let payload = encode_payload(&[(signer_bytes, true)], &[]); + let missing = crate::config_decode(&replay_input(payload.as_slice(), &[], false)); + assert_eq!(missing.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let mismatch = crate::config_decode(&replay_input( + payload.as_slice(), + &[bs58::encode([9_u8; 32]).into_string()], + false, + )); + assert_eq!(mismatch.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn failed_config_store_is_decoded_as_uncommitted_intent() { + let payload = encode_payload(&[], &[5]); + let result = crate::config_decode(&replay_input(payload.as_slice(), &[], true)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!result.observations[0].observation_committed); + } + + #[test] + fn coverage_declares_one_generic_store_surface() { + let coverage = crate::config_coverage(); + assert_eq!(coverage.len(), 1); + assert_eq!(coverage[0].entry_code, "store"); + assert!(coverage[0].discriminator_hex.is_none()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/constants.rs new file mode 100644 index 0000000..a8d3ef4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/constants.rs @@ -0,0 +1,67 @@ +// file: kb_decoder_solana_core/src/constants.rs +// version: 12 + +//! Local constants for the `kb_decoder_solana_core` crate. Program identifiers live in `kb_program_ids`. + +/// Tracing target for the native decoder. +/// Stable protocol code shared by native Solana events. +pub(crate) const PROTOCOL_CODE: &str = "solana_native"; +/// Stable System Program surface code. +pub(crate) const SYSTEM_SURFACE_CODE: &str = "solana_native_system"; +/// Stable deprecated immutable BPF Loader surface code. +pub(crate) const BPF_LOADER_DEPRECATED_SURFACE_CODE: &str = "solana_native_bpf_loader_deprecated"; +/// Stable immutable BPF Loader v2 surface code. +pub(crate) const BPF_LOADER_SURFACE_CODE: &str = "solana_native_bpf_loader"; +/// Stable upgradeable BPF Loader surface code. +pub(crate) const BPF_LOADER_UPGRADEABLE_SURFACE_CODE: &str = "solana_native_bpf_loader_upgradeable"; +/// Stable Loader v4 surface code. +pub(crate) const LOADER_V4_SURFACE_CODE: &str = "solana_native_loader_v4"; +/// Stable Native Loader surface code. +pub(crate) const NATIVE_LOADER_SURFACE_CODE: &str = "solana_native_loader"; +/// Stable Compute Budget surface code. +pub(crate) const COMPUTE_BUDGET_SURFACE_CODE: &str = "solana_native_compute_budget"; +/// Stable Address Lookup Table surface code. +pub(crate) const ADDRESS_LOOKUP_TABLE_SURFACE_CODE: &str = "solana_native_address_lookup_table"; +/// Stable Vote Program surface code. +pub(crate) const VOTE_SURFACE_CODE: &str = "solana_native_vote"; +/// Stable Stake Program surface code. +pub(crate) const STAKE_SURFACE_CODE: &str = "solana_native_stake"; +/// Stable Config Program surface code. +pub(crate) const CONFIG_SURFACE_CODE: &str = "solana_native_config"; +/// Stable Feature Gate surface code. +pub(crate) const FEATURE_SURFACE_CODE: &str = "solana_native_feature"; +/// Stable Ed25519 signature precompile surface code. +pub(crate) const ED25519_SURFACE_CODE: &str = "solana_native_ed25519"; +/// Stable secp256k1 signature precompile surface code. +pub(crate) const SECP256K1_SURFACE_CODE: &str = "solana_native_secp256k1"; +/// Stable secp256r1 signature precompile surface code. +pub(crate) const SECP256R1_SURFACE_CODE: &str = "solana_native_secp256r1"; +/// Stable native ZK ElGamal Proof surface code. +pub(crate) const ZK_ELGAMAL_PROOF_SURFACE_CODE: &str = "solana_native_zk_elgamal_proof"; +/// Stable historical ZK Token Proof surface code. +pub(crate) const ZK_TOKEN_PROOF_SURFACE_CODE: &str = "solana_native_zk_token_proof"; +/// Byte length shared by compact Ed25519, secp256k1 and secp256r1 signatures. +pub(crate) const SIGNATURE_BYTES: usize = 64; +/// Byte length of an Ed25519 public key. +pub(crate) const ED25519_PUBLIC_KEY_BYTES: usize = 32; +/// Byte length of a compressed secp256r1 public key. +pub(crate) const SECP256R1_PUBLIC_KEY_BYTES: usize = 33; +/// Byte length of a secp256k1 Ethereum address. +pub(crate) const SECP256K1_ETHEREUM_ADDRESS_BYTES: usize = 20; +/// Byte length of one Ed25519 or secp256r1 offsets entry. +pub(crate) const U16_OFFSETS_BYTES: usize = 14; +/// Byte length of one secp256k1 offsets entry. +pub(crate) const U8_OFFSETS_BYTES: usize = 11; +/// Stable Slashing Program decoder surface code. +pub(crate) const SLASHING_SURFACE_CODE: &str = "solana_native_slashing"; +/// Runtime maximum number of secp256r1 signatures in one precompile instruction. +pub(crate) const SECP256R1_MAX_SIGNATURES: usize = 8; +/// Maximum number of component bytes retained as a hexadecimal event prefix. +pub(crate) const PRECOMPILE_COMPONENT_PREFIX_BYTES: usize = 16; +/// Maximum retained native instruction payload accepted by the first decoder phase. +pub(crate) const MAX_NATIVE_INSTRUCTION_PAYLOAD_BYTES: usize = 4_096; +/// Current native event payload contract version. +pub(crate) const NATIVE_EVENT_VERSION: u32 = 1; + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_solana_core"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/decoder.rs new file mode 100644 index 0000000..0ad1a67 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/decoder.rs @@ -0,0 +1,613 @@ +// file: kb_decoder_solana_core/src/decoder.rs +// version: 22 + +//! Runtime-native Solana program classifier for the common decode pipeline. + +const NATIVE_SURFACES: &[kb_decoder_api::DecoderSurface] = &[ + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID, + surface_code: "solana_native_address_lookup_table", + priority: 100, + }, + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::BPF_LOADER_DEPRECATED_PROGRAM_ID, + surface_code: "solana_native_bpf_loader_deprecated", + priority: 100, + }, + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::BPF_LOADER_PROGRAM_ID, + surface_code: "solana_native_bpf_loader", + priority: 100, + }, + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID, + surface_code: "solana_native_bpf_loader_upgradeable", + priority: 100, + }, + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID, + surface_code: "solana_native_compute_budget", + priority: 100, + }, + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::CONFIG_PROGRAM_ID, + surface_code: "solana_native_config", + priority: 100, + }, + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::ED25519_PROGRAM_ID, + surface_code: "solana_native_ed25519", + priority: 100, + }, + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::FEATURE_PROGRAM_ID, + surface_code: "solana_native_feature", + priority: 100, + }, + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::LOADER_V4_PROGRAM_ID, + surface_code: "solana_native_loader_v4", + priority: 100, + }, + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::NATIVE_LOADER_PROGRAM_ID, + surface_code: "solana_native_loader", + priority: 100, + }, + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::SECP256K1_PROGRAM_ID, + surface_code: "solana_native_secp256k1", + priority: 100, + }, + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::SECP256R1_PROGRAM_ID, + surface_code: "solana_native_secp256r1", + priority: 100, + }, + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::SLASHING_PROGRAM_ID, + surface_code: "solana_native_slashing", + priority: 100, + }, + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::STAKE_PROGRAM_ID, + surface_code: "solana_native_stake", + priority: 100, + }, + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::SYSTEM_PROGRAM_ID, + surface_code: "solana_native_system", + priority: 100, + }, + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::VOTE_PROGRAM_ID, + surface_code: "solana_native_vote", + priority: 100, + }, + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID, + surface_code: "solana_native_zk_elgamal_proof", + priority: 100, + }, + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::ZK_TOKEN_PROOF_PROGRAM_ID, + surface_code: "solana_native_zk_token_proof", + priority: 100, + }, +]; + +const LEGACY_PROGRAM_IDS: &[&str] = &[ + kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID, + kb_program_ids::BPF_LOADER_DEPRECATED_PROGRAM_ID, + kb_program_ids::BPF_LOADER_PROGRAM_ID, + kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID, + kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID, + kb_program_ids::CONFIG_PROGRAM_ID, + kb_program_ids::ED25519_PROGRAM_ID, + kb_program_ids::FEATURE_PROGRAM_ID, + kb_program_ids::LOADER_V4_PROGRAM_ID, + kb_program_ids::NATIVE_LOADER_PROGRAM_ID, + kb_program_ids::SECP256K1_PROGRAM_ID, + kb_program_ids::SECP256R1_PROGRAM_ID, + kb_program_ids::SLASHING_PROGRAM_ID, + kb_program_ids::STAKE_PROGRAM_ID, + kb_program_ids::SYSTEM_PROGRAM_ID, + kb_program_ids::VOTE_PROGRAM_ID, + kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID, + kb_program_ids::ZK_TOKEN_PROOF_PROGRAM_ID, +]; + +/// Runtime-native Solana decoder with phased maximal instruction coverage. +#[derive(Clone, Debug, Default)] +pub struct SolanaCoreDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::SolanaCoreDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_solana_core"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return LEGACY_PROGRAM_IDS; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + tracing::debug!(target: crate::TRACING_TARGET, "legacy native decoder adapter classified an observation without maximal decoding"); + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +impl kb_decoder_api::InstructionDecoder for crate::SolanaCoreDecoder { + fn identity(&self) -> kb_decoder_api::DecoderIdentity { + return kb_decoder_api::DecoderIdentity { + name: "solana_native_classifier".to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + }; + } + + fn surfaces(&self) -> &'static [kb_decoder_api::DecoderSurface] { + return NATIVE_SURFACES; + } + + fn coverage(&self) -> std::vec::Vec { + let mut coverage = crate::system_coverage(); + coverage.extend(crate::compute_budget_coverage()); + coverage.extend(crate::config_coverage()); + coverage.extend(crate::feature_coverage()); + coverage.extend(crate::vote_coverage()); + coverage.extend(crate::stake_coverage()); + coverage.extend(crate::address_lookup_table_coverage()); + coverage.extend(crate::loaders_coverage()); + coverage.extend(crate::precompiles_coverage()); + coverage.extend(crate::slashing_coverage()); + coverage.extend(crate::zk_elgamal_coverage()); + coverage.extend(crate::zk_token_proof_coverage()); + for surface in NATIVE_SURFACES { + if surface.program_id == kb_program_ids::SYSTEM_PROGRAM_ID + || surface.program_id == kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID + || surface.program_id == kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID + || surface.program_id == kb_program_ids::CONFIG_PROGRAM_ID + || surface.program_id == kb_program_ids::FEATURE_PROGRAM_ID + || surface.program_id == kb_program_ids::VOTE_PROGRAM_ID + || surface.program_id == kb_program_ids::STAKE_PROGRAM_ID + || is_signature_precompile(surface.program_id) + || surface.program_id == kb_program_ids::SLASHING_PROGRAM_ID + || surface.program_id == kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID + || surface.program_id == kb_program_ids::ZK_TOKEN_PROOF_PROGRAM_ID + || is_loader_program(surface.program_id) + { + continue; + } + coverage.push(kb_decoder_api::DecoderCoverageDeclaration { + program_id: surface.program_id.to_string(), + surface_code: std::option::Option::Some(surface.surface_code.to_string()), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: "unclassified_native_instruction".to_string(), + discriminator_hex: std::option::Option::None, + historical: is_historical_native_program(surface.program_id), + }); + } + return coverage; + } + + fn recognize( + &self, + input: &kb_store_core::CoreInstructionReplayInput, + ) -> kb_decoder_api::DecoderRecognition { + tracing::debug!(target: crate::TRACING_TARGET, action = "recognize", signature = %input.signature, slot = input.slot, instruction_path = %input.instruction_path, program_id = %input.program_id, input_key = %input.replay_input_key, transaction_failed = input.transaction_failed, "recognize native Solana contextual instruction"); + let surface = NATIVE_SURFACES + .iter() + .find(|surface| return surface.program_id == input.program_id); + let surface = match surface { + std::option::Option::Some(value) => value, + std::option::Option::None => { + tracing::debug!(target: crate::TRACING_TARGET, action = "recognize", signature = %input.signature, instruction_path = %input.instruction_path, program_id = %input.program_id, compatible = false, "native Solana decoder does not support program id"); + return kb_decoder_api::DecoderRecognition::incompatible(); + }, + }; + let recognition = if input.program_id == kb_program_ids::SYSTEM_PROGRAM_ID { + crate::system_recognize(input, surface.priority) + } else if input.program_id == kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID { + crate::compute_budget_recognize(input, surface.priority) + } else if input.program_id == kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID { + crate::address_lookup_table_recognize(input, surface.priority) + } else if input.program_id == kb_program_ids::CONFIG_PROGRAM_ID { + crate::config_recognize(input, surface.priority) + } else if input.program_id == kb_program_ids::FEATURE_PROGRAM_ID { + crate::feature_recognize(input, surface.priority) + } else if input.program_id == kb_program_ids::VOTE_PROGRAM_ID { + crate::vote_recognize(input, surface.priority) + } else if input.program_id == kb_program_ids::STAKE_PROGRAM_ID { + crate::stake_recognize(input, surface.priority) + } else if input.program_id == kb_program_ids::SLASHING_PROGRAM_ID { + crate::slashing_recognize(input, surface.priority) + } else if is_signature_precompile(input.program_id.as_str()) { + crate::precompiles_recognize(input, surface.priority) + } else if input.program_id == kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID { + crate::zk_elgamal_recognize(input, surface.priority) + } else if input.program_id == kb_program_ids::ZK_TOKEN_PROOF_PROGRAM_ID { + crate::zk_token_proof_recognize(input, surface.priority) + } else if is_loader_program(input.program_id.as_str()) { + crate::loaders_recognize(input, surface.priority) + } else { + kb_decoder_api::DecoderRecognition::compatible( + false, + surface.priority, + std::option::Option::Some(surface.surface_code.to_string()), + std::option::Option::Some("unclassified_native_instruction".to_string()), + kb_decoder_api::discriminator_8_hex(input), + ) + }; + tracing::debug!(target: crate::TRACING_TARGET, action = "recognize", signature = %input.signature, instruction_path = %input.instruction_path, program_id = %input.program_id, surface_code = surface.surface_code, recognition = ?recognition, compatible = true, "native Solana decoder recognized contextual instruction"); + return recognition; + } + + fn decode( + &self, + input: &kb_store_core::CoreInstructionReplayInput, + ) -> kb_decoder_api::DecoderExecutionResult { + let result = if input.program_id == kb_program_ids::SYSTEM_PROGRAM_ID { + crate::system_decode(input) + } else if input.program_id == kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID { + crate::compute_budget_decode(input) + } else if input.program_id == kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID { + crate::address_lookup_table_decode(input) + } else if input.program_id == kb_program_ids::CONFIG_PROGRAM_ID { + crate::config_decode(input) + } else if input.program_id == kb_program_ids::FEATURE_PROGRAM_ID { + crate::feature_decode(input) + } else if input.program_id == kb_program_ids::VOTE_PROGRAM_ID { + crate::vote_decode(input) + } else if input.program_id == kb_program_ids::STAKE_PROGRAM_ID { + crate::stake_decode(input) + } else if input.program_id == kb_program_ids::SLASHING_PROGRAM_ID { + crate::slashing_decode(input) + } else if is_signature_precompile(input.program_id.as_str()) { + crate::precompiles_decode(input) + } else if input.program_id == kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID { + crate::zk_elgamal_decode(input) + } else if input.program_id == kb_program_ids::ZK_TOKEN_PROOF_PROGRAM_ID { + crate::zk_token_proof_decode(input) + } else if is_loader_program(input.program_id.as_str()) { + crate::loaders_decode(input) + } else { + kb_decoder_api::DecoderExecutionResult::unsupported(std::option::Option::Some( + "unclassified_native_instruction".to_string(), + )) + }; + if matches!( + result.status, + kb_decoder_api::DecoderOutcomeStatus::Failed + | kb_decoder_api::DecoderOutcomeStatus::Unsupported + ) { + tracing::error!( + target: crate::TRACING_TARGET, + action = "decode_failure", + signature = %input.signature, + slot = input.slot, + instruction_path = %input.instruction_path, + program_id = %input.program_id, + processor_name = "solana_native_classifier", + processor_version = env!("CARGO_PKG_VERSION"), + input_key = %input.replay_input_key, + payload_hash = ?input.instruction_payload_hash, + payload_length = ?crate::decoded_payload_length(input), + transaction_failed = input.transaction_failed, + transaction_error = ?input.transaction_err_json, + result_status = ?result.status, + recognized_entry_code = ?result.recognized_entry_code, + diagnostics = ?result.diagnostics, + "native instruction was not decoded successfully" + ); + } + tracing::debug!(target: crate::TRACING_TARGET, action = "decode", signature = %input.signature, slot = input.slot, instruction_path = %input.instruction_path, program_id = %input.program_id, input_key = %input.replay_input_key, payload_hash = ?input.instruction_payload_hash, transaction_failed = input.transaction_failed, transaction_error = ?input.transaction_err_json, result_status = ?result.status, recognized_entry_code = ?result.recognized_entry_code, observation_count = result.observations.len(), diagnostic_count = result.diagnostics.len(), "native instruction decode completed"); + return result; + } +} + +fn is_signature_precompile(program_id: &str) -> bool { + return program_id == kb_program_ids::ED25519_PROGRAM_ID + || program_id == kb_program_ids::SECP256K1_PROGRAM_ID + || program_id == kb_program_ids::SECP256R1_PROGRAM_ID; +} + +fn is_loader_program(program_id: &str) -> bool { + return program_id == kb_program_ids::BPF_LOADER_DEPRECATED_PROGRAM_ID + || program_id == kb_program_ids::BPF_LOADER_PROGRAM_ID + || program_id == kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID + || program_id == kb_program_ids::LOADER_V4_PROGRAM_ID + || program_id == kb_program_ids::NATIVE_LOADER_PROGRAM_ID; +} + +fn is_historical_native_program(program_id: &str) -> bool { + return program_id == kb_program_ids::BPF_LOADER_DEPRECATED_PROGRAM_ID + || program_id == kb_program_ids::BPF_LOADER_PROGRAM_ID + || program_id == kb_program_ids::ZK_TOKEN_PROOF_PROGRAM_ID; +} + +#[cfg(test)] +mod tests { + fn replay_input(program_id: &str) -> kb_store_core::CoreInstructionReplayInput { + let result = kb_store_core::CoreInstructionReplayInput::new( + "signature:0", + "signature", + 42, + "0", + program_id, + false, + std::option::Option::None, + serde_json::json!([]), + serde_json::json!([]), + std::option::Option::Some(serde_json::json!({"dataBase64": "AQIDBAUGBwg="})), + std::option::Option::Some("payload-hash".to_string()), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("replay input failed: {error}"), + }; + } + + #[test] + fn vote_program_is_dispatched_without_false_decode_for_unknown_tag() { + let decoder = crate::SolanaCoreDecoder; + let input = replay_input(kb_program_ids::VOTE_PROGRAM_ID); + let recognition = kb_decoder_api::InstructionDecoder::recognize(&decoder, &input); + assert!(recognition.compatible); + assert_eq!( + recognition.surface_code.as_deref(), + std::option::Option::Some("solana_native_vote") + ); + assert_eq!( + recognition.entry_code.as_deref(), + std::option::Option::Some("unknown_vote_instruction") + ); + let result = kb_decoder_api::InstructionDecoder::decode(&decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Unsupported); + assert!(result.observations.is_empty()); + } + + #[test] + fn stake_program_is_dispatched_without_false_decode_for_unknown_tag() { + let decoder = crate::SolanaCoreDecoder; + let input = replay_input(kb_program_ids::STAKE_PROGRAM_ID); + let recognition = kb_decoder_api::InstructionDecoder::recognize(&decoder, &input); + assert!(recognition.compatible); + assert_eq!( + recognition.surface_code.as_deref(), + std::option::Option::Some("solana_native_stake") + ); + assert_eq!( + recognition.entry_code.as_deref(), + std::option::Option::Some("unknown_stake_instruction") + ); + let result = kb_decoder_api::InstructionDecoder::decode(&decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Unsupported); + assert!(result.observations.is_empty()); + } + + #[test] + fn zk_token_proof_program_dispatches_to_current_noop_fallback() { + let decoder = crate::SolanaCoreDecoder; + let input = replay_input(kb_program_ids::ZK_TOKEN_PROOF_PROGRAM_ID); + let recognition = kb_decoder_api::InstructionDecoder::recognize(&decoder, &input); + assert!(recognition.compatible); + assert_eq!( + recognition.surface_code.as_deref(), + std::option::Option::Some("solana_native_zk_token_proof") + ); + assert_eq!( + recognition.entry_code.as_deref(), + std::option::Option::Some("current_runtime_noop_invocation") + ); + let result = kb_decoder_api::InstructionDecoder::decode(&decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some("current_runtime_noop_invocation") + ); + assert_eq!(result.observations.len(), 1); + } + + #[test] + fn every_native_registry_program_has_one_surface() { + let decoder = crate::SolanaCoreDecoder; + let surfaces = kb_decoder_api::InstructionDecoder::surfaces(&decoder); + assert_eq!(surfaces.len(), kb_program_ids::native_program_ids().len()); + for entry in kb_program_ids::native_program_ids() { + assert!(surfaces.iter().any(|surface| return surface.program_id == entry.program_id())); + } + } + + #[test] + fn every_native_surface_has_declared_coverage_without_fallback_entries() { + let decoder = crate::SolanaCoreDecoder; + let surfaces = kb_decoder_api::InstructionDecoder::surfaces(&decoder); + let coverage = kb_decoder_api::InstructionDecoder::coverage(&decoder); + for surface in surfaces { + assert!(coverage.iter().any(|entry| { + return entry.program_id == surface.program_id + && entry.surface_code.as_deref() + == std::option::Option::Some(surface.surface_code); + })); + } + assert!(!coverage.iter().any(|entry| { + return entry.entry_code == "unclassified_native_instruction"; + })); + } + + #[test] + fn coverage_entries_are_unique_and_reference_declared_surfaces() { + let decoder = crate::SolanaCoreDecoder; + let surfaces = kb_decoder_api::InstructionDecoder::surfaces(&decoder); + let coverage = kb_decoder_api::InstructionDecoder::coverage(&decoder); + let mut identities = std::collections::BTreeSet::new(); + for entry in coverage { + let surface_code = match entry.surface_code.as_deref() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("native coverage entry has no surface code"), + }; + assert!(surfaces.iter().any(|surface| { + return surface.program_id == entry.program_id + && surface.surface_code == surface_code; + })); + assert!(identities.insert(format!( + "{}:{surface_code}:{}:{:?}", + entry.program_id, entry.entry_code, entry.entry_kind + ))); + } + } + + #[test] + fn slashing_program_is_dispatched_to_exact_decoder() { + let decoder = crate::SolanaCoreDecoder; + let input = replay_input(kb_program_ids::SLASHING_PROGRAM_ID); + let recognition = kb_decoder_api::InstructionDecoder::recognize(&decoder, &input); + assert!(recognition.compatible); + assert_eq!( + recognition.surface_code.as_deref(), + std::option::Option::Some("solana_native_slashing") + ); + assert_eq!( + recognition.entry_code.as_deref(), + std::option::Option::Some("duplicate_block_proof") + ); + } + + #[test] + fn deprecated_stake_config_account_is_not_an_instruction_surface() { + let decoder = crate::SolanaCoreDecoder; + let surfaces = kb_decoder_api::InstructionDecoder::surfaces(&decoder); + assert!(!surfaces.iter().any(|surface| { + return surface.program_id == kb_program_ids::STAKE_CONFIG_ACCOUNT_ID; + })); + let input = replay_input(kb_program_ids::STAKE_CONFIG_ACCOUNT_ID); + let recognition = kb_decoder_api::InstructionDecoder::recognize(&decoder, &input); + assert!(!recognition.compatible); + } + + #[test] + fn native_decoder_matrix_matches_registry_surfaces_and_coverage() { + let raw = include_str!("../../docs/NATIVE_SOLANA_DECODER_MATRIX.json"); + let parsed = match serde_json::from_str::(raw) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("native decoder matrix parsing failed: {error}") + }, + }; + let matrix_surfaces = match parsed.get("surfaces").and_then(serde_json::Value::as_array) { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("native decoder matrix surfaces missing"), + }; + assert_eq!(matrix_surfaces.len(), 18); + let declared_surface_count = + parsed.get("surface_count").and_then(serde_json::Value::as_u64); + assert_eq!(declared_surface_count, std::option::Option::Some(18)); + + let decoder = crate::SolanaCoreDecoder; + let compiled_surfaces = kb_decoder_api::InstructionDecoder::surfaces(&decoder); + let compiled_surface_map = compiled_surfaces + .iter() + .map(|surface| { + return (surface.program_id.to_string(), surface.surface_code.to_string()); + }) + .collect::>(); + assert_eq!(compiled_surface_map.len(), 18); + + let native_registry = kb_program_ids::native_program_ids() + .iter() + .map(|entry| return entry.program_id().to_string()) + .collect::>(); + assert_eq!(native_registry.len(), 18); + + let coverage = kb_decoder_api::InstructionDecoder::coverage(&decoder); + let mut compiled_coverage_counts = std::collections::BTreeMap::new(); + for entry in &coverage { + let count = compiled_coverage_counts.entry(entry.program_id.clone()).or_insert(0_usize); + *count = count.saturating_add(1); + } + + let mut matrix_surface_map = std::collections::BTreeMap::new(); + let mut matrix_program_ids = std::collections::BTreeSet::new(); + let mut matrix_coverage_total = 0_usize; + for surface in matrix_surfaces { + let surface_code = match surface.get("surface_code").and_then(serde_json::Value::as_str) + { + std::option::Option::Some(value) if !value.is_empty() => value, + _ => panic!("native decoder matrix surface_code missing"), + }; + let program_id = match surface.get("program_id").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) if !value.is_empty() => value, + _ => panic!("native decoder matrix program_id missing"), + }; + for field in ["runtime_status", "source_contract", "completeness_test"] { + let value = surface.get(field).and_then(serde_json::Value::as_str); + assert!(matches!(value, std::option::Option::Some(text) if !text.is_empty())); + } + let expected_count_u64 = match surface + .get("expected_coverage_entries") + .and_then(serde_json::Value::as_u64) + { + std::option::Option::Some(value) => value, + std::option::Option::None => { + panic!("native decoder matrix expected coverage count missing") + }, + }; + let expected_count = match usize::try_from(expected_count_u64) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("native decoder matrix coverage count invalid: {error}") + }, + }; + assert!(expected_count > 0); + assert!(matrix_program_ids.insert(program_id.to_string())); + assert!( + matrix_surface_map + .insert(program_id.to_string(), surface_code.to_string()) + .is_none() + ); + assert_eq!( + compiled_coverage_counts.get(program_id).copied(), + std::option::Option::Some(expected_count) + ); + matrix_coverage_total = matrix_coverage_total.saturating_add(expected_count); + } + + let declared_coverage_count = + parsed.get("coverage_entry_count").and_then(serde_json::Value::as_u64); + assert_eq!(declared_coverage_count, std::option::Option::Some(121)); + assert_eq!(matrix_coverage_total, 121); + assert_eq!(coverage.len(), matrix_coverage_total); + assert_eq!(matrix_surface_map, compiled_surface_map); + assert_eq!(matrix_program_ids, native_registry); + } + + #[test] + fn unrelated_program_is_not_dispatched() { + let decoder = crate::SolanaCoreDecoder; + let input = replay_input(kb_program_ids::SPL_TOKEN_PROGRAM_ID); + let recognition = kb_decoder_api::InstructionDecoder::recognize(&decoder, &input); + assert!(!recognition.compatible); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/event.rs b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/event.rs new file mode 100644 index 0000000..111b93b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/event.rs @@ -0,0 +1,101 @@ +// file: kb_decoder_solana_core/src/event.rs +// version: 2 + +//! Stable native event and decoder result builders. + +/// Builds one exact decoded native observation. +pub(crate) fn decoded_result( + input: &kb_store_core::CoreInstructionReplayInput, + surface_code: &str, + entry_code: &str, + event_family: kb_model::EventFamily, + historical: bool, + accounts: serde_json::Value, + parameters: serde_json::Value, + source_evidence: &str, +) -> kb_decoder_api::DecoderExecutionResult { + let event = kb_model::DecodedProtocolEvent { + signature: kb_model::Signature(input.signature.clone()), + slot: kb_model::Slot(input.slot), + instruction_path: kb_model::InstructionPath(input.instruction_path.clone()), + program_id: kb_model::ProgramId(input.program_id.clone()), + protocol_code: kb_model::ProtocolCode(crate::PROTOCOL_CODE.to_string()), + surface_code: kb_model::SurfaceCode(surface_code.to_string()), + event_code: kb_model::EventCode(format!("{surface_code}.{entry_code}")), + event_name: kb_model::EventName(entry_code.to_string()), + event_family, + source_kind: if input.instruction_path.contains('/') { + kb_model::EventSourceKind::InnerInstruction + } else { + kb_model::EventSourceKind::Instruction + }, + confidence: kb_model::DecoderConfidence::ManualExact, + }; + let payload_hash = crate::payload_hash(input); + let observation = kb_decoder_api::DecodedObservation { + event_key: format!("{entry_code}:0"), + event, + payload_json: serde_json::json!({ + "eventVersion": crate::NATIVE_EVENT_VERSION, + "programId": input.program_id, + "surfaceCode": surface_code, + "entryCode": entry_code, + "historical": historical, + "instructionPath": input.instruction_path, + "transactionSucceeded": !input.transaction_failed, + "payloadHash": payload_hash, + "accounts": accounts, + "parameters": parameters, + }), + transaction_failed: input.transaction_failed, + transaction_error: input.transaction_err_json.clone(), + observation_committed: !input.transaction_failed, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::Manual, + confidence: kb_model::DecoderConfidence::ManualExact, + evidence: vec![source_evidence.to_string(), format!("payload_sha256:{payload_hash}")], + }, + }; + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Decoded, + recognized_entry_code: std::option::Option::Some(entry_code.to_string()), + observations: vec![observation], + diagnostics: std::vec::Vec::new(), + }; +} + +/// Builds one failed native decode result. +pub(crate) fn failed_result( + entry_code: std::option::Option<&str>, + code: &str, + message: impl std::convert::Into, +) -> kb_decoder_api::DecoderExecutionResult { + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Failed, + recognized_entry_code: entry_code.map(str::to_string), + observations: std::vec::Vec::new(), + diagnostics: vec![kb_decoder_api::DecoderDiagnostic { + code: code.to_string(), + message: message.into(), + retriable: false, + }], + }; +} + +/// Builds one unsupported native decode result with a bounded diagnostic. +pub(crate) fn unsupported_result( + entry_code: &str, + code: &str, + message: impl std::convert::Into, +) -> kb_decoder_api::DecoderExecutionResult { + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Unsupported, + recognized_entry_code: std::option::Option::Some(entry_code.to_string()), + observations: std::vec::Vec::new(), + diagnostics: vec![kb_decoder_api::DecoderDiagnostic { + code: code.to_string(), + message: message.into(), + retriable: false, + }], + }; +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/feature.rs b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/feature.rs new file mode 100644 index 0000000..70a0347 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/feature.rs @@ -0,0 +1,279 @@ +// file: kb_decoder_solana_core/src/feature.rs +// version: 3 + +//! Exact Feature Gate instruction decoding from the official interface contract. + +const SOURCE: &str = "solana-feature-gate-interface@4.0.0 revoke_pending_activation contract"; +const REVOKE_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("feature_account", true, true), + crate::AccountRole::new("incinerator", true, false), + crate::AccountRole::new("system_program", false, false), +]; + +/// Returns declared Feature Gate instruction coverage. +pub(crate) fn feature_coverage() -> std::vec::Vec { + return vec![kb_decoder_api::DecoderCoverageDeclaration { + program_id: kb_program_ids::FEATURE_PROGRAM_ID.to_string(), + surface_code: std::option::Option::Some(crate::FEATURE_SURFACE_CODE.to_string()), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: "revoke_pending_activation".to_string(), + discriminator_hex: std::option::Option::Some("00".to_string()), + historical: false, + }]; +} + +/// Recognizes one Feature Gate instruction without producing an event. +pub(crate) fn feature_recognize( + input: &kb_store_core::CoreInstructionReplayInput, + priority: u16, +) -> kb_decoder_api::DecoderRecognition { + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return kb_decoder_api::DecoderRecognition::compatible( + false, + priority, + std::option::Option::Some(crate::FEATURE_SURFACE_CODE.to_string()), + std::option::Option::Some("malformed_feature_instruction".to_string()), + std::option::Option::None, + ); + }, + }; + let entry_code = match bytes.first() { + std::option::Option::Some(0) => "revoke_pending_activation", + std::option::Option::Some(_value) => "unknown_feature_instruction", + std::option::Option::None => "malformed_feature_instruction", + }; + return kb_decoder_api::DecoderRecognition::compatible( + bytes.as_slice() == [0], + priority, + std::option::Option::Some(crate::FEATURE_SURFACE_CODE.to_string()), + std::option::Option::Some(entry_code.to_string()), + crate::hexadecimal_prefix(bytes.as_slice(), bytes.len().min(1)), + ); +} + +/// Decodes one Feature Gate instruction. +pub(crate) fn feature_decode( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_decoder_api::DecoderExecutionResult { + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("malformed_feature_instruction"), + "feature_payload_invalid", + error.to_string(), + ); + }, + }; + if bytes.is_empty() { + return crate::failed_result( + std::option::Option::Some("malformed_feature_instruction"), + "feature_payload_empty", + "Feature Gate instruction payload is empty", + ); + } + if bytes[0] != 0 { + return crate::unsupported_result( + "unknown_feature_instruction", + "feature_tag_unknown", + format!( + "unknown Feature Gate instruction tag {}; payload_sha256={}", + bytes[0], + crate::payload_hash(input) + ), + ); + } + if bytes.len() != 1 { + return crate::failed_result( + std::option::Option::Some("revoke_pending_activation"), + "feature_instruction_size_invalid", + format!( + "Feature Gate revoke_pending_activation requires 1 byte but received {}", + bytes.len() + ), + ); + } + let accounts_result = + crate::resolve_accounts(input, REVOKE_ROLES, 3, std::option::Option::Some(3)); + let accounts = match accounts_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("revoke_pending_activation"), + "feature_accounts_invalid", + error.to_string(), + ); + }, + }; + let fixed_accounts_result = validate_fixed_accounts(&accounts); + if let std::result::Result::Err(error) = fixed_accounts_result { + return crate::failed_result( + std::option::Option::Some("revoke_pending_activation"), + "feature_fixed_account_invalid", + error.to_string(), + ); + } + return crate::decoded_result( + input, + crate::FEATURE_SURFACE_CODE, + "revoke_pending_activation", + kb_model::EventFamily::Lifecycle, + false, + accounts, + serde_json::json!({ + "burnsFeatureLamports": true, + "requiresPendingActivation": true, + }), + SOURCE, + ); +} + +fn validate_fixed_accounts(accounts: &serde_json::Value) -> kb_core::Result<()> { + let array = match accounts.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "resolved Feature Gate accounts must be an array", + )); + }, + }; + for (position, expected) in [ + (1_usize, kb_program_ids::INCINERATOR_PROGRAM_ID), + (2_usize, kb_program_ids::SYSTEM_PROGRAM_ID), + ] { + let actual = array + .get(position) + .and_then(|value| return value.get("accountKey")) + .and_then(serde_json::Value::as_str); + if actual != std::option::Option::Some(expected) { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Feature Gate account {position} must be {expected}" + ))); + } + } + return std::result::Result::Ok(()); +} + +#[cfg(test)] +mod tests { + use base64::Engine; // rust-rules: trait-import + + fn replay_input( + bytes: &[u8], + transaction_failed: bool, + ) -> kb_store_core::CoreInstructionReplayInput { + let keys = [ + "FeatureAccount11111111111111111111111111111", + kb_program_ids::INCINERATOR_PROGRAM_ID, + kb_program_ids::SYSTEM_PROGRAM_ID, + ]; + let account_keys = keys + .iter() + .enumerate() + .map(|(index, key)| { + return serde_json::json!({ + "accountIndex": index, + "accountKey": key, + "source": "static", + "writable": index != 2, + "signer": index == 0, + "executable": index == 2, + }); + }) + .collect::>(); + let instruction_accounts = keys + .iter() + .enumerate() + .map(|(index, key)| { + return serde_json::json!({"accountIndex": index, "accountKey": key}); + }) + .collect::>(); + let result = kb_store_core::CoreInstructionReplayInput::new( + "signature:0", + "signature", + 42, + "0", + kb_program_ids::FEATURE_PROGRAM_ID, + transaction_failed, + if transaction_failed { + std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]})) + } else { + std::option::Option::None + }, + serde_json::Value::Array(account_keys), + serde_json::Value::Array(instruction_accounts), + std::option::Option::Some(serde_json::json!({ + "dataBase64": base64::engine::general_purpose::STANDARD.encode(bytes), + })), + std::option::Option::Some("payload-hash".to_string()), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Feature Gate replay input failed: {error}"), + }; + } + + #[test] + fn official_payload_is_recognized_exactly() { + let input = replay_input(&[0], false); + let recognition = crate::feature_recognize(&input, 100); + assert!(recognition.compatible); + assert!(recognition.exact); + assert_eq!( + recognition.entry_code.as_deref(), + std::option::Option::Some("revoke_pending_activation") + ); + let unknown = crate::feature_recognize(&replay_input(&[7], false), 100); + assert!(unknown.compatible); + assert!(!unknown.exact); + } + + #[test] + fn revoke_pending_activation_decodes_exact_official_layout() { + let result = crate::feature_decode(&replay_input(&[0], false)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some("revoke_pending_activation") + ); + assert_eq!(result.observations[0].payload_json["parameters"]["burnsFeatureLamports"], true); + } + + #[test] + fn unknown_truncated_trailing_and_wrong_fixed_accounts_are_safe() { + let empty = crate::feature_decode(&replay_input(&[], false)); + assert_eq!(empty.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let unknown = crate::feature_decode(&replay_input(&[7], false)); + assert_eq!(unknown.status, kb_decoder_api::DecoderOutcomeStatus::Unsupported); + let trailing = crate::feature_decode(&replay_input(&[0, 1], false)); + assert_eq!(trailing.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let mut wrong = replay_input(&[0], false); + wrong.instruction_accounts_json[1]["accountKey"] = serde_json::json!("wrong"); + wrong.account_keys_json[1]["accountKey"] = serde_json::json!("wrong"); + let invalid = crate::feature_decode(&wrong); + assert_eq!(invalid.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn failed_feature_revoke_is_uncommitted() { + let result = crate::feature_decode(&replay_input(&[0], true)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!result.observations[0].observation_committed); + } + + #[test] + fn coverage_declares_the_single_official_instruction() { + let coverage = crate::feature_coverage(); + assert_eq!(coverage.len(), 1); + assert_eq!(coverage[0].entry_code, "revoke_pending_activation"); + assert_eq!(coverage[0].discriminator_hex.as_deref(), std::option::Option::Some("00")); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/lib.rs new file mode 100644 index 0000000..72d5f45 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/lib.rs @@ -0,0 +1,201 @@ +// file: kb_decoder_solana_core/src/lib.rs +// version: 16 + +//! Decoder crate for `solana_core`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod accounts; +mod address_lookup_table; +mod compute_budget; +mod config; +mod constants; +mod decoder; +mod event; +mod feature; +mod loaders; +mod payload; +mod precompiles; +mod slashing; +mod stake; +mod system; +mod vote; +mod zk_elgamal; +mod zk_token_proof; + +/// Expected role and privileges for one positional instruction account. +pub(crate) use crate::accounts::AccountRole; +/// Resolves positional instruction accounts and validates their core indexes. +pub(crate) use crate::accounts::resolve_accounts; +/// Returns declared Address Lookup Table instruction coverage. +pub(crate) use crate::address_lookup_table::address_lookup_table_coverage; +/// Decodes one Address Lookup Table instruction. +pub(crate) use crate::address_lookup_table::address_lookup_table_decode; +/// Recognizes one Address Lookup Table instruction without producing an event. +pub(crate) use crate::address_lookup_table::address_lookup_table_recognize; +/// Returns declared Compute Budget instruction coverage. +pub(crate) use crate::compute_budget::compute_budget_coverage; +/// Decodes one Compute Budget instruction. +pub(crate) use crate::compute_budget::compute_budget_decode; +/// Recognizes one Compute Budget instruction without producing an event. +pub(crate) use crate::compute_budget::compute_budget_recognize; +/// Returns declared Config Program instruction coverage. +pub(crate) use crate::config::config_coverage; +/// Decodes one generic Config Program store instruction. +pub(crate) use crate::config::config_decode; +/// Recognizes one generic Config Program store instruction. +pub(crate) use crate::config::config_recognize; +/// Stable Address Lookup Table surface code. +pub(crate) use crate::constants::ADDRESS_LOOKUP_TABLE_SURFACE_CODE; +/// Stable deprecated immutable BPF Loader surface code. +pub(crate) use crate::constants::BPF_LOADER_DEPRECATED_SURFACE_CODE; +/// Stable immutable BPF Loader v2 surface code. +pub(crate) use crate::constants::BPF_LOADER_SURFACE_CODE; +/// Stable upgradeable BPF Loader surface code. +pub(crate) use crate::constants::BPF_LOADER_UPGRADEABLE_SURFACE_CODE; +/// Stable Compute Budget surface code. +pub(crate) use crate::constants::COMPUTE_BUDGET_SURFACE_CODE; +/// Stable Config Program surface code. +pub(crate) use crate::constants::CONFIG_SURFACE_CODE; +/// Byte length of an Ed25519 public key. +pub(crate) use crate::constants::ED25519_PUBLIC_KEY_BYTES; +/// Stable Ed25519 signature precompile surface code. +pub(crate) use crate::constants::ED25519_SURFACE_CODE; +/// Stable Feature Gate surface code. +pub(crate) use crate::constants::FEATURE_SURFACE_CODE; +/// Stable Loader v4 surface code. +pub(crate) use crate::constants::LOADER_V4_SURFACE_CODE; +/// Maximum retained native instruction payload accepted by the first decoder phase. +pub(crate) use crate::constants::MAX_NATIVE_INSTRUCTION_PAYLOAD_BYTES; +/// Current native event payload contract version. +pub(crate) use crate::constants::NATIVE_EVENT_VERSION; +/// Stable Native Loader surface code. +pub(crate) use crate::constants::NATIVE_LOADER_SURFACE_CODE; +/// Maximum number of component bytes retained as a hexadecimal event prefix. +pub(crate) use crate::constants::PRECOMPILE_COMPONENT_PREFIX_BYTES; +/// Tracing target for the native decoder. +/// Stable protocol code shared by native Solana events. +pub(crate) use crate::constants::PROTOCOL_CODE; +/// Byte length of a secp256k1 Ethereum address. +pub(crate) use crate::constants::SECP256K1_ETHEREUM_ADDRESS_BYTES; +/// Stable secp256k1 signature precompile surface code. +pub(crate) use crate::constants::SECP256K1_SURFACE_CODE; +/// Runtime maximum number of secp256r1 signatures in one precompile instruction. +pub(crate) use crate::constants::SECP256R1_MAX_SIGNATURES; +/// Byte length of a compressed secp256r1 public key. +pub(crate) use crate::constants::SECP256R1_PUBLIC_KEY_BYTES; +/// Stable secp256r1 signature precompile surface code. +pub(crate) use crate::constants::SECP256R1_SURFACE_CODE; +/// Byte length shared by compact Ed25519, secp256k1 and secp256r1 signatures. +pub(crate) use crate::constants::SIGNATURE_BYTES; +/// Stable Slashing Program decoder surface code. +pub(crate) use crate::constants::SLASHING_SURFACE_CODE; +/// Stable Stake Program surface code. +pub(crate) use crate::constants::STAKE_SURFACE_CODE; +/// Stable System Program surface code. +pub(crate) use crate::constants::SYSTEM_SURFACE_CODE; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; +/// Byte length of one secp256k1 offsets entry. +pub(crate) use crate::constants::U8_OFFSETS_BYTES; +/// Byte length of one Ed25519 or secp256r1 offsets entry. +pub(crate) use crate::constants::U16_OFFSETS_BYTES; +/// Stable Vote Program surface code. +pub(crate) use crate::constants::VOTE_SURFACE_CODE; +/// Stable native ZK ElGamal Proof surface code. +pub(crate) use crate::constants::ZK_ELGAMAL_PROOF_SURFACE_CODE; +/// Stable historical ZK Token Proof surface code. +pub(crate) use crate::constants::ZK_TOKEN_PROOF_SURFACE_CODE; +/// Builds one exact decoded native observation. +pub(crate) use crate::event::decoded_result; +/// Builds one failed native decode result. +pub(crate) use crate::event::failed_result; +/// Builds one unsupported native decode result with a bounded diagnostic. +pub(crate) use crate::event::unsupported_result; +/// Returns declared Feature Gate instruction coverage. +pub(crate) use crate::feature::feature_coverage; +/// Decodes one Feature Gate instruction. +pub(crate) use crate::feature::feature_decode; +/// Recognizes one Feature Gate instruction without producing an event. +pub(crate) use crate::feature::feature_recognize; +/// Returns declared loader instruction coverage. +pub(crate) use crate::loaders::loaders_coverage; +/// Decodes one loader instruction. +pub(crate) use crate::loaders::loaders_decode; +/// Recognizes one loader instruction without producing an event. +pub(crate) use crate::loaders::loaders_recognize; +/// One resolved outer instruction payload and its provenance relative to the target instruction. +pub(crate) use crate::payload::ResolvedInstructionPayload; +/// Returns a hexadecimal prefix no longer than the available byte slice. +pub(crate) use crate::payload::bounded_hexadecimal_prefix; +/// Extracts one exact bounded byte slice with checked arithmetic. +pub(crate) use crate::payload::bounded_slice; +/// Decodes one retained base64 instruction payload with an explicit byte limit. +pub(crate) use crate::payload::decode_instruction_data; +/// Returns the decoded target payload length when retained and valid. +pub(crate) use crate::payload::decoded_payload_length; +/// Returns the SHA-256 of the decoded target instruction data when available. +pub(crate) use crate::payload::decoded_payload_sha256; +/// Returns the lowercase SHA-256 of one bounded byte slice. +pub(crate) use crate::payload::hash_bytes; +/// Returns a normalized lowercase hexadecimal prefix. +pub(crate) use crate::payload::hexadecimal_prefix; +/// Returns a stable payload hash from core or computes it from retained JSON. +pub(crate) use crate::payload::payload_hash; +/// Reads one little-endian `u32` from an exact byte range. +pub(crate) use crate::payload::read_u32_le; +/// Reads one little-endian `u64` from an exact byte range. +pub(crate) use crate::payload::read_u64_le; +/// Resolves a secp256k1 instruction reference. The runtime format has no current-instruction +/// sentinel: every `u8` value is an explicit outer instruction index. +pub(crate) use crate::payload::resolve_u8_instruction_payload; +/// Resolves an Ed25519 or secp256r1 instruction reference using the official `u16::MAX` sentinel. +pub(crate) use crate::payload::resolve_u16_instruction_payload; +/// Returns the numeric outer index of the target instruction. +pub(crate) use crate::payload::target_outer_instruction_index; +/// Returns declared signature precompile coverage. +pub(crate) use crate::precompiles::precompiles_coverage; +/// Decodes one signature precompile instruction structurally without cryptographic recomputation. +pub(crate) use crate::precompiles::precompiles_decode; +/// Recognizes one signature precompile instruction without resolving referenced data. +pub(crate) use crate::precompiles::precompiles_recognize; +/// Returns declared Slashing Program instruction coverage. +pub(crate) use crate::slashing::slashing_coverage; +/// Decodes one Slashing Program instruction. +pub(crate) use crate::slashing::slashing_decode; +/// Recognizes one Slashing Program instruction without producing an event. +pub(crate) use crate::slashing::slashing_recognize; +/// Returns declared Stake Program instruction coverage. +pub(crate) use crate::stake::stake_coverage; +/// Decodes one Stake Program instruction. +pub(crate) use crate::stake::stake_decode; +/// Recognizes one Stake Program instruction without producing an event. +pub(crate) use crate::stake::stake_recognize; +/// Returns declared System Program instruction coverage. +pub(crate) use crate::system::system_coverage; +/// Decodes one System Program instruction. +pub(crate) use crate::system::system_decode; +/// Recognizes one System Program instruction without producing an event. +pub(crate) use crate::system::system_recognize; +/// Returns declared Vote Program instruction coverage. +pub(crate) use crate::vote::vote_coverage; +/// Decodes one Vote Program instruction. +pub(crate) use crate::vote::vote_decode; +/// Recognizes one Vote Program instruction without producing an event. +pub(crate) use crate::vote::vote_recognize; +/// Returns declared ZK ElGamal Proof instruction coverage. +pub(crate) use crate::zk_elgamal::zk_elgamal_coverage; +/// Decodes one native ZK ElGamal Proof instruction without recomputing the proof. +pub(crate) use crate::zk_elgamal::zk_elgamal_decode; +/// Recognizes one native ZK ElGamal Proof instruction. +pub(crate) use crate::zk_elgamal::zk_elgamal_recognize; +/// Returns declared historical and current ZK Token Proof coverage. +pub(crate) use crate::zk_token_proof::zk_token_proof_coverage; +/// Decodes one historical ZK Token Proof layout without recomputing any proof. +pub(crate) use crate::zk_token_proof::zk_token_proof_decode; +/// Recognizes one historical ZK Token Proof layout or the current no-op runtime fallback. +pub(crate) use crate::zk_token_proof::zk_token_proof_recognize; + +/// Runtime-native Solana decoder with phased maximal native coverage. +pub use crate::decoder::SolanaCoreDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/loaders.rs b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/loaders.rs new file mode 100644 index 0000000..a7be2dd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/loaders.rs @@ -0,0 +1,1322 @@ +// file: kb_decoder_solana_core/src/loaders.rs +// version: 8 + +//! Exact bounded decoding for Solana native loader generations. + +const SOURCE_BPF_LOADER_V2: &str = "solana-loader-v2-interface@3.0.0"; +const SOURCE_BPF_LOADER_V3: &str = "solana-loader-v3-interface@8.0.1"; +const SOURCE_LOADER_V4: &str = "solana-loader-v4-interface@3.1.0"; +const SOURCE_NATIVE_LOADER: &str = "Agave native_loader runtime dispatch"; + +const IMMUTABLE_WRITE_ROLES: &[crate::AccountRole] = + &[crate::AccountRole::new("program_account", true, true)]; +const IMMUTABLE_FINALIZE_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("program_account", true, true), + crate::AccountRole::new("rent_sysvar", false, false), +]; +const UPGRADEABLE_INITIALIZE_BUFFER_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("buffer_account", true, false), + crate::AccountRole::new("buffer_authority", false, false), +]; +const UPGRADEABLE_WRITE_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("buffer_account", true, false), + crate::AccountRole::new("buffer_authority", false, true), +]; +const UPGRADEABLE_DEPLOY_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("payer", true, true), + crate::AccountRole::new("programdata_account", true, false), + crate::AccountRole::new("program_account", true, false), + crate::AccountRole::new("buffer_account", true, false), + crate::AccountRole::new("rent_sysvar", false, false), + crate::AccountRole::new("clock_sysvar", false, false), + crate::AccountRole::new("system_program", false, false), + crate::AccountRole::new("upgrade_authority", false, true), +]; +const UPGRADEABLE_UPGRADE_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("programdata_account", true, false), + crate::AccountRole::new("program_account", true, false), + crate::AccountRole::new("buffer_account", true, false), + crate::AccountRole::new("spill_account", true, false), + crate::AccountRole::new("rent_sysvar", false, false), + crate::AccountRole::new("clock_sysvar", false, false), + crate::AccountRole::new("upgrade_authority", false, true), +]; +const UPGRADEABLE_SET_AUTHORITY_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("authority_target", true, false), + crate::AccountRole::new("current_authority", false, true), + crate::AccountRole::new("new_authority", false, false), +]; +const UPGRADEABLE_CLOSE_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("close_target", true, false), + crate::AccountRole::new("recipient", true, false), + crate::AccountRole::new("authority", false, true), + crate::AccountRole::new("associated_program", true, false), +]; +const UPGRADEABLE_EXTEND_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("programdata_account", true, false), + crate::AccountRole::new("program_account", true, false), + crate::AccountRole::new("system_program", false, false), + crate::AccountRole::new("payer", true, true), +]; +const UPGRADEABLE_SET_AUTHORITY_CHECKED_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("authority_target", true, false), + crate::AccountRole::new("current_authority", false, true), + crate::AccountRole::new("new_authority", false, true), +]; +const LOADER_V4_WRITE_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("program_account", true, false), + crate::AccountRole::new("authority", false, true), +]; +const LOADER_V4_COPY_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("program_account", true, false), + crate::AccountRole::new("authority", false, true), + crate::AccountRole::new("source_program_account", false, false), +]; +const LOADER_V4_SET_LENGTH_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("program_account", true, false), + crate::AccountRole::new("authority", false, true), + crate::AccountRole::new("recipient", true, false), +]; +const LOADER_V4_DEPLOY_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("program_account", true, false), + crate::AccountRole::new("authority", false, true), + crate::AccountRole::new("source_program_account", true, false), +]; +const LOADER_V4_AUTHORITY_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("program_account", true, false), + crate::AccountRole::new("authority", false, true), +]; +const LOADER_V4_TRANSFER_AUTHORITY_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("program_account", true, false), + crate::AccountRole::new("current_authority", false, true), + crate::AccountRole::new("new_authority", false, true), +]; +const LOADER_V4_FINALIZE_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("program_account", true, false), + crate::AccountRole::new("authority", false, true), + crate::AccountRole::new("next_version_program", false, false), +]; + +/// Returns declared loader instruction coverage. +pub(crate) fn loaders_coverage() -> std::vec::Vec { + let mut output = std::vec::Vec::new(); + output.push(coverage_entry( + kb_program_ids::NATIVE_LOADER_PROGRAM_ID, + crate::NATIVE_LOADER_SURFACE_CODE, + "direct_invocation_opaque", + std::option::Option::None, + true, + )); + for (program_id, surface_code, historical) in [ + ( + kb_program_ids::BPF_LOADER_DEPRECATED_PROGRAM_ID, + crate::BPF_LOADER_DEPRECATED_SURFACE_CODE, + true, + ), + (kb_program_ids::BPF_LOADER_PROGRAM_ID, crate::BPF_LOADER_SURFACE_CODE, true), + ] { + output.push(coverage_entry( + program_id, + surface_code, + "write", + std::option::Option::Some("00000000".to_string()), + historical, + )); + output.push(coverage_entry( + program_id, + surface_code, + "finalize", + std::option::Option::Some("01000000".to_string()), + historical, + )); + } + for (entry_code, tag) in [ + ("initialize_buffer", 0_u32), + ("write", 1_u32), + ("deploy_with_max_data_len", 2_u32), + ("upgrade", 3_u32), + ("set_authority", 4_u32), + ("close", 5_u32), + ("extend_program", 6_u32), + ("set_authority_checked", 7_u32), + ] { + output.push(coverage_entry( + kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID, + crate::BPF_LOADER_UPGRADEABLE_SURFACE_CODE, + entry_code, + std::option::Option::Some(tag_hex(tag)), + matches!(tag, 2 | 3 | 5), + )); + } + for (entry_code, tag) in [ + ("write", 0_u32), + ("copy", 1_u32), + ("set_program_length", 2_u32), + ("deploy", 3_u32), + ("retract", 4_u32), + ("transfer_authority", 5_u32), + ("finalize", 6_u32), + ] { + output.push(coverage_entry( + kb_program_ids::LOADER_V4_PROGRAM_ID, + crate::LOADER_V4_SURFACE_CODE, + entry_code, + std::option::Option::Some(tag_hex(tag)), + false, + )); + } + return output; +} + +/// Recognizes one loader instruction without producing an event. +pub(crate) fn loaders_recognize( + input: &kb_store_core::CoreInstructionReplayInput, + priority: u16, +) -> kb_decoder_api::DecoderRecognition { + let surface_code = match surface_code(input.program_id.as_str()) { + std::option::Option::Some(value) => value, + std::option::Option::None => return kb_decoder_api::DecoderRecognition::incompatible(), + }; + if input.program_id == kb_program_ids::NATIVE_LOADER_PROGRAM_ID { + return kb_decoder_api::DecoderRecognition::compatible( + true, + priority, + std::option::Option::Some(surface_code.to_string()), + std::option::Option::Some("direct_invocation_opaque".to_string()), + std::option::Option::None, + ); + } + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return kb_decoder_api::DecoderRecognition::compatible( + false, + priority, + std::option::Option::Some(surface_code.to_string()), + std::option::Option::Some("malformed_loader_instruction".to_string()), + std::option::Option::None, + ); + }, + }; + let entry_code = entry_code(input.program_id.as_str(), bytes.as_slice()); + return kb_decoder_api::DecoderRecognition::compatible( + entry_code.is_some(), + priority, + std::option::Option::Some(surface_code.to_string()), + entry_code.map(str::to_string).or_else(|| { + return std::option::Option::Some("unknown_loader_instruction".to_string()); + }), + crate::hexadecimal_prefix(bytes.as_slice(), 4), + ); +} + +/// Decodes one loader instruction. +pub(crate) fn loaders_decode( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_decoder_api::DecoderExecutionResult { + if input.program_id == kb_program_ids::NATIVE_LOADER_PROGRAM_ID { + tracing::debug!(target: crate::TRACING_TARGET, action = "decode_native_loader", signature = %input.signature, instruction_path = %input.instruction_path, source = SOURCE_NATIVE_LOADER, "ignore opaque direct Native Loader invocation without inventing an instruction schema"); + return kb_decoder_api::DecoderExecutionResult::ignored(std::option::Option::Some( + "direct_invocation_opaque".to_string(), + )); + } + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("malformed_loader_instruction"), + "loader_payload_invalid", + error.to_string(), + ); + }, + }; + if input.program_id == kb_program_ids::BPF_LOADER_DEPRECATED_PROGRAM_ID + || input.program_id == kb_program_ids::BPF_LOADER_PROGRAM_ID + { + return decode_immutable(input, bytes.as_slice()); + } + if input.program_id == kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID { + return decode_upgradeable(input, bytes.as_slice()); + } + if input.program_id == kb_program_ids::LOADER_V4_PROGRAM_ID { + return decode_v4(input, bytes.as_slice()); + } + return crate::unsupported_result( + "unknown_loader_instruction", + "loader_program_unknown", + format!("unsupported loader program id {}", input.program_id), + ); +} + +fn decode_immutable( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], +) -> kb_decoder_api::DecoderExecutionResult { + let tag = match read_tag(bytes, "immutable_loader_tag_truncated") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(result) => return result, + }; + let surface_code = if input.program_id == kb_program_ids::BPF_LOADER_DEPRECATED_PROGRAM_ID { + crate::BPF_LOADER_DEPRECATED_SURFACE_CODE + } else { + crate::BPF_LOADER_SURFACE_CODE + }; + return match tag { + 0 => decode_immutable_write(input, bytes, surface_code), + 1 => { + if bytes.len() != 4 { + return size_failure("finalize", 4, bytes.len()); + } + build( + input, + surface_code, + "finalize", + kb_model::EventFamily::Lifecycle, + true, + IMMUTABLE_FINALIZE_ROLES, + 2, + serde_json::json!({}), + SOURCE_BPF_LOADER_V2, + ) + }, + _ => unknown_tag(input, "unknown_immutable_loader_instruction", tag), + }; +} + +fn decode_immutable_write( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], + surface_code: &str, +) -> kb_decoder_api::DecoderExecutionResult { + if bytes.len() < 12 { + return crate::failed_result( + std::option::Option::Some("write"), + "loader_write_truncated", + format!( + "immutable loader write requires at least 12 bytes but received {}", + bytes.len() + ), + ); + } + let offset = match crate::read_u32_le(bytes, 4) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("write"), + "loader_write_offset_truncated", + error.to_string(), + ); + }, + }; + let byte_length = match exact_u32_vector_length(bytes, 8, 12, "write") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(result) => return result, + }; + let written_bytes = match bytes.get(12..) { + std::option::Option::Some(value) => value, + std::option::Option::None => &[], + }; + return build( + input, + surface_code, + "write", + kb_model::EventFamily::Audit, + true, + IMMUTABLE_WRITE_ROLES, + 1, + serde_json::json!({ + "offset": offset, + "byteLength": byte_length, + "bytesSha256": crate::hash_bytes(written_bytes), + "bytesPrefixHex": crate::hexadecimal_prefix( + written_bytes, + written_bytes.len().min(16), + ), + }), + SOURCE_BPF_LOADER_V2, + ); +} + +fn decode_upgradeable( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], +) -> kb_decoder_api::DecoderExecutionResult { + let tag = match read_tag(bytes, "upgradeable_loader_tag_truncated") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(result) => return result, + }; + let entry_code = match tag { + 0 => "initialize_buffer", + 1 => "write", + 2 => "deploy_with_max_data_len", + 3 => "upgrade", + 4 => "set_authority", + 5 => "close", + 6 => "extend_program", + 7 => "set_authority_checked", + _ => { + return unknown_tag(input, "unknown_upgradeable_loader_instruction", tag); + }, + }; + let instruction = match wincode::deserialize_exact::< + solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction, + >(bytes) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some(entry_code), + "upgradeable_loader_wincode_invalid", + format!("cannot decode exact Loader v3 {entry_code}: {error}"), + ); + }, + }; + return match instruction { + solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction::InitializeBuffer => { + build( + input, + crate::BPF_LOADER_UPGRADEABLE_SURFACE_CODE, + "initialize_buffer", + kb_model::EventFamily::Lifecycle, + false, + UPGRADEABLE_INITIALIZE_BUFFER_ROLES, + 1, + serde_json::json!({}), + SOURCE_BPF_LOADER_V3, + ) + }, + solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction::Write { + offset, + bytes: written_bytes, + } => build( + input, + crate::BPF_LOADER_UPGRADEABLE_SURFACE_CODE, + "write", + kb_model::EventFamily::Audit, + false, + UPGRADEABLE_WRITE_ROLES, + 2, + serde_json::json!({ + "offset": offset, + "byteLength": written_bytes.len(), + "bytesSha256": crate::hash_bytes(written_bytes.as_slice()), + "bytesPrefixHex": crate::hexadecimal_prefix( + written_bytes.as_slice(), + written_bytes.len().min(16), + ), + }), + SOURCE_BPF_LOADER_V3, + ), + solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction::DeployWithMaxDataLen { + max_data_len, + close_buffer, + } => build( + input, + crate::BPF_LOADER_UPGRADEABLE_SURFACE_CODE, + "deploy_with_max_data_len", + kb_model::EventFamily::Lifecycle, + bytes.len() == 12, + UPGRADEABLE_DEPLOY_ROLES, + 8, + serde_json::json!({ + "maxDataLength": max_data_len, + "closeBuffer": close_buffer, + "legacyDefaultedCloseBuffer": bytes.len() == 12, + }), + SOURCE_BPF_LOADER_V3, + ), + solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction::Upgrade { + close_buffer, + } => build( + input, + crate::BPF_LOADER_UPGRADEABLE_SURFACE_CODE, + "upgrade", + kb_model::EventFamily::Lifecycle, + bytes.len() == 4, + UPGRADEABLE_UPGRADE_ROLES, + 7, + serde_json::json!({ + "closeBuffer": close_buffer, + "legacyDefaulted": bytes.len() == 4, + }), + SOURCE_BPF_LOADER_V3, + ), + solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction::SetAuthority => { + build( + input, + crate::BPF_LOADER_UPGRADEABLE_SURFACE_CODE, + "set_authority", + kb_model::EventFamily::Admin, + false, + UPGRADEABLE_SET_AUTHORITY_ROLES, + 2, + serde_json::json!({}), + SOURCE_BPF_LOADER_V3, + ) + }, + solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction::Close { + tombstone, + } => build( + input, + crate::BPF_LOADER_UPGRADEABLE_SURFACE_CODE, + "close", + kb_model::EventFamily::Lifecycle, + bytes.len() == 4, + UPGRADEABLE_CLOSE_ROLES, + 2, + serde_json::json!({ + "tombstone": tombstone, + "legacyDefaulted": bytes.len() == 4, + }), + SOURCE_BPF_LOADER_V3, + ), + solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction::ExtendProgram { + additional_bytes, + } => { + let account_count = account_count(input); + if account_count == 3 { + return crate::failed_result( + std::option::Option::Some("extend_program"), + "upgradeable_loader_extend_accounts_invalid", + "extend_program requires either two accounts or the complete optional system/payer pair", + ); + } + build( + input, + crate::BPF_LOADER_UPGRADEABLE_SURFACE_CODE, + "extend_program", + kb_model::EventFamily::Lifecycle, + false, + UPGRADEABLE_EXTEND_ROLES, + 2, + serde_json::json!({ + "additionalBytes": additional_bytes, + "fundingAccountsPresent": account_count >= 4, + }), + SOURCE_BPF_LOADER_V3, + ) + }, + solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction::SetAuthorityChecked => { + build( + input, + crate::BPF_LOADER_UPGRADEABLE_SURFACE_CODE, + "set_authority_checked", + kb_model::EventFamily::Admin, + false, + UPGRADEABLE_SET_AUTHORITY_CHECKED_ROLES, + 3, + serde_json::json!({}), + SOURCE_BPF_LOADER_V3, + ) + }, + }; +} + +fn decode_v4( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], +) -> kb_decoder_api::DecoderExecutionResult { + let tag = match read_tag(bytes, "loader_v4_tag_truncated") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(result) => return result, + }; + return match tag { + 0 => decode_write( + input, + bytes, + crate::LOADER_V4_SURFACE_CODE, + false, + LOADER_V4_WRITE_ROLES, + 2, + SOURCE_LOADER_V4, + ), + 1 => decode_copy(input, bytes), + 2 => decode_set_program_length(input, bytes), + 3 => decode_unit( + input, + bytes, + crate::LOADER_V4_SURFACE_CODE, + "deploy", + kb_model::EventFamily::Lifecycle, + LOADER_V4_DEPLOY_ROLES, + 2, + SOURCE_LOADER_V4, + ), + 4 => decode_unit( + input, + bytes, + crate::LOADER_V4_SURFACE_CODE, + "retract", + kb_model::EventFamily::Lifecycle, + LOADER_V4_AUTHORITY_ROLES, + 2, + SOURCE_LOADER_V4, + ), + 5 => decode_unit( + input, + bytes, + crate::LOADER_V4_SURFACE_CODE, + "transfer_authority", + kb_model::EventFamily::Admin, + LOADER_V4_TRANSFER_AUTHORITY_ROLES, + 3, + SOURCE_LOADER_V4, + ), + 6 => decode_unit( + input, + bytes, + crate::LOADER_V4_SURFACE_CODE, + "finalize", + kb_model::EventFamily::Lifecycle, + LOADER_V4_FINALIZE_ROLES, + 3, + SOURCE_LOADER_V4, + ), + _ => unknown_tag(input, "unknown_loader_v4_instruction", tag), + }; +} + +fn decode_write( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], + surface_code: &str, + historical: bool, + roles: &[crate::AccountRole], + minimum_count: usize, + source: &str, +) -> kb_decoder_api::DecoderExecutionResult { + if bytes.len() < 16 { + return crate::failed_result( + std::option::Option::Some("write"), + "loader_write_truncated", + format!("loader write requires at least 16 bytes but received {}", bytes.len()), + ); + } + let offset = match crate::read_u32_le(bytes, 4) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("write"), + "loader_write_offset_truncated", + error.to_string(), + ); + }, + }; + let byte_length = match exact_vector_length(bytes, 8, 16, "write") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(result) => return result, + }; + let written_bytes = match bytes.get(16..) { + std::option::Option::Some(value) => value, + std::option::Option::None => &[], + }; + return build( + input, + surface_code, + "write", + kb_model::EventFamily::Audit, + historical, + roles, + minimum_count, + serde_json::json!({ + "offset": offset, + "byteLength": byte_length, + "bytesSha256": crate::hash_bytes(written_bytes), + "bytesPrefixHex": crate::hexadecimal_prefix( + written_bytes, + written_bytes.len().min(16), + ), + }), + source, + ); +} + +fn decode_copy( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], +) -> kb_decoder_api::DecoderExecutionResult { + if bytes.len() != 16 { + return size_failure("copy", 16, bytes.len()); + } + let destination_offset = match crate::read_u32_le(bytes, 4) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("copy"), + "loader_v4_copy_truncated", + error.to_string(), + ); + }, + }; + let source_offset = match crate::read_u32_le(bytes, 8) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("copy"), + "loader_v4_copy_truncated", + error.to_string(), + ); + }, + }; + let length = match crate::read_u32_le(bytes, 12) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("copy"), + "loader_v4_copy_truncated", + error.to_string(), + ); + }, + }; + return build( + input, + crate::LOADER_V4_SURFACE_CODE, + "copy", + kb_model::EventFamily::Audit, + false, + LOADER_V4_COPY_ROLES, + 3, + serde_json::json!({ + "destinationOffset": destination_offset, + "sourceOffset": source_offset, + "length": length, + }), + SOURCE_LOADER_V4, + ); +} + +fn decode_set_program_length( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], +) -> kb_decoder_api::DecoderExecutionResult { + if bytes.len() != 8 { + return size_failure("set_program_length", 8, bytes.len()); + } + let new_size = match crate::read_u32_le(bytes, 4) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("set_program_length"), + "loader_v4_set_length_truncated", + error.to_string(), + ); + }, + }; + return build( + input, + crate::LOADER_V4_SURFACE_CODE, + "set_program_length", + kb_model::EventFamily::Lifecycle, + false, + LOADER_V4_SET_LENGTH_ROLES, + 2, + serde_json::json!({ + "newSize": new_size, + "recipientPresent": account_count(input) >= 3, + }), + SOURCE_LOADER_V4, + ); +} + +fn decode_unit( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], + surface_code: &str, + entry_code: &str, + family: kb_model::EventFamily, + roles: &[crate::AccountRole], + minimum_count: usize, + source: &str, +) -> kb_decoder_api::DecoderExecutionResult { + if bytes.len() != 4 { + return size_failure(entry_code, 4, bytes.len()); + } + return build( + input, + surface_code, + entry_code, + family, + false, + roles, + minimum_count, + serde_json::json!({}), + source, + ); +} + +fn build( + input: &kb_store_core::CoreInstructionReplayInput, + surface_code: &str, + entry_code: &str, + family: kb_model::EventFamily, + historical: bool, + roles: &[crate::AccountRole], + minimum_count: usize, + parameters: serde_json::Value, + source: &str, +) -> kb_decoder_api::DecoderExecutionResult { + let accounts_result = + crate::resolve_accounts(input, roles, minimum_count, std::option::Option::None); + let accounts = match accounts_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some(entry_code), + "loader_accounts_invalid", + error.to_string(), + ); + }, + }; + return crate::decoded_result( + input, + surface_code, + entry_code, + family, + historical, + accounts, + parameters, + source, + ); +} + +fn read_tag( + bytes: &[u8], + code: &str, +) -> std::result::Result { + let result = crate::read_u32_le(bytes, 0); + return match result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(crate::failed_result( + std::option::Option::Some("malformed_loader_instruction"), + code, + error.to_string(), + )), + }; +} + +fn exact_u32_vector_length( + bytes: &[u8], + length_offset: usize, + data_offset: usize, + entry_code: &str, +) -> std::result::Result { + let encoded_length = match crate::read_u32_le(bytes, length_offset) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(crate::failed_result( + std::option::Option::Some(entry_code), + "loader_vector_length_truncated", + error.to_string(), + )); + }, + }; + let expected_length = encoded_length as usize; + let actual_length = bytes.len().saturating_sub(data_offset); + if bytes.len() < data_offset || actual_length != expected_length { + return std::result::Result::Err(crate::failed_result( + std::option::Option::Some(entry_code), + "loader_vector_length_mismatch", + format!( + "loader {entry_code} declares {expected_length} bytes but contains {actual_length}" + ), + )); + } + return std::result::Result::Ok(expected_length); +} + +fn exact_vector_length( + bytes: &[u8], + length_offset: usize, + data_offset: usize, + entry_code: &str, +) -> std::result::Result { + let encoded_length = match crate::read_u64_le(bytes, length_offset) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(crate::failed_result( + std::option::Option::Some(entry_code), + "loader_vector_length_truncated", + error.to_string(), + )); + }, + }; + let expected_length = match usize::try_from(encoded_length) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return std::result::Result::Err(crate::failed_result( + std::option::Option::Some(entry_code), + "loader_vector_length_overflow", + format!("loader vector length {encoded_length} does not fit usize"), + )); + }, + }; + let actual_length = bytes.len().saturating_sub(data_offset); + if bytes.len() < data_offset || actual_length != expected_length { + return std::result::Result::Err(crate::failed_result( + std::option::Option::Some(entry_code), + "loader_vector_length_mismatch", + format!( + "loader {entry_code} declares {expected_length} bytes but contains {actual_length}" + ), + )); + } + return std::result::Result::Ok(expected_length); +} + +fn size_failure( + entry_code: &str, + expected: usize, + actual: usize, +) -> kb_decoder_api::DecoderExecutionResult { + return crate::failed_result( + std::option::Option::Some(entry_code), + "loader_instruction_size_invalid", + format!("loader {entry_code} requires exactly {expected} bytes but received {actual}"), + ); +} + +fn unknown_tag( + input: &kb_store_core::CoreInstructionReplayInput, + entry_code: &str, + tag: u32, +) -> kb_decoder_api::DecoderExecutionResult { + return crate::unsupported_result( + entry_code, + "loader_tag_unknown", + format!( + "unknown loader instruction tag {tag}; payload_sha256={}", + crate::payload_hash(input) + ), + ); +} + +fn surface_code(program_id: &str) -> std::option::Option<&'static str> { + return if program_id == kb_program_ids::BPF_LOADER_DEPRECATED_PROGRAM_ID { + std::option::Option::Some(crate::BPF_LOADER_DEPRECATED_SURFACE_CODE) + } else if program_id == kb_program_ids::BPF_LOADER_PROGRAM_ID { + std::option::Option::Some(crate::BPF_LOADER_SURFACE_CODE) + } else if program_id == kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID { + std::option::Option::Some(crate::BPF_LOADER_UPGRADEABLE_SURFACE_CODE) + } else if program_id == kb_program_ids::LOADER_V4_PROGRAM_ID { + std::option::Option::Some(crate::LOADER_V4_SURFACE_CODE) + } else if program_id == kb_program_ids::NATIVE_LOADER_PROGRAM_ID { + std::option::Option::Some(crate::NATIVE_LOADER_SURFACE_CODE) + } else { + std::option::Option::None + }; +} + +fn entry_code(program_id: &str, bytes: &[u8]) -> std::option::Option<&'static str> { + if program_id == kb_program_ids::NATIVE_LOADER_PROGRAM_ID { + return std::option::Option::Some("direct_invocation_opaque"); + } + let tag = match crate::read_u32_le(bytes, 0) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => return std::option::Option::None, + }; + if program_id == kb_program_ids::BPF_LOADER_DEPRECATED_PROGRAM_ID + || program_id == kb_program_ids::BPF_LOADER_PROGRAM_ID + { + return match tag { + 0 => std::option::Option::Some("write"), + 1 => std::option::Option::Some("finalize"), + _ => std::option::Option::None, + }; + } + if program_id == kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID { + return match tag { + 0 => std::option::Option::Some("initialize_buffer"), + 1 => std::option::Option::Some("write"), + 2 => std::option::Option::Some("deploy_with_max_data_len"), + 3 => std::option::Option::Some("upgrade"), + 4 => std::option::Option::Some("set_authority"), + 5 => std::option::Option::Some("close"), + 6 => std::option::Option::Some("extend_program"), + 7 => std::option::Option::Some("set_authority_checked"), + _ => std::option::Option::None, + }; + } + if program_id == kb_program_ids::LOADER_V4_PROGRAM_ID { + return match tag { + 0 => std::option::Option::Some("write"), + 1 => std::option::Option::Some("copy"), + 2 => std::option::Option::Some("set_program_length"), + 3 => std::option::Option::Some("deploy"), + 4 => std::option::Option::Some("retract"), + 5 => std::option::Option::Some("transfer_authority"), + 6 => std::option::Option::Some("finalize"), + _ => std::option::Option::None, + }; + } + return std::option::Option::None; +} + +fn account_count(input: &kb_store_core::CoreInstructionReplayInput) -> usize { + return input.instruction_accounts_json.as_array().map(std::vec::Vec::len).unwrap_or(0); +} + +fn coverage_entry( + program_id: &str, + surface_code: &str, + entry_code: &str, + discriminator_hex: std::option::Option, + historical: bool, +) -> kb_decoder_api::DecoderCoverageDeclaration { + return kb_decoder_api::DecoderCoverageDeclaration { + program_id: program_id.to_string(), + surface_code: std::option::Option::Some(surface_code.to_string()), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: entry_code.to_string(), + discriminator_hex, + historical, + }; +} + +fn tag_hex(tag: u32) -> std::string::String { + let bytes = tag.to_le_bytes(); + return format!("{:02x}{:02x}{:02x}{:02x}", bytes[0], bytes[1], bytes[2], bytes[3]); +} + +#[cfg(test)] +mod tests { + use base64::Engine; // rust-rules: trait-import + + fn replay_input( + program_id: &str, + bytes: &[u8], + account_count: usize, + transaction_failed: bool, + ) -> kb_store_core::CoreInstructionReplayInput { + let encoded = base64::engine::general_purpose::STANDARD.encode(bytes); + let account_keys = (0..account_count) + .map(|index| { + return serde_json::json!({ + "accountIndex": index, + "accountKey": format!("account{index}"), + "source": "static", + "writable": true, + "signer": true, + "executable": false, + }); + }) + .collect::>(); + let instruction_accounts = (0..account_count) + .map(|index| { + return serde_json::json!({ + "accountIndex": index, + "accountKey": format!("account{index}"), + }); + }) + .collect::>(); + let result = kb_store_core::CoreInstructionReplayInput::new( + "signature:0", + "signature", + 42, + "0", + program_id, + transaction_failed, + if transaction_failed { + std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]})) + } else { + std::option::Option::None + }, + serde_json::Value::Array(account_keys), + serde_json::Value::Array(instruction_accounts), + std::option::Option::Some(serde_json::json!({"dataBase64": encoded})), + std::option::Option::None, + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("loader replay input failed: {error}"), + }; + } + + fn unit(tag: u32) -> std::vec::Vec { + return tag.to_le_bytes().to_vec(); + } + + fn immutable_write(tag: u32, offset: u32, data: &[u8]) -> std::vec::Vec { + let mut bytes = tag.to_le_bytes().to_vec(); + bytes.extend_from_slice(&offset.to_le_bytes()); + bytes.extend_from_slice(&(data.len() as u32).to_le_bytes()); + bytes.extend_from_slice(data); + return bytes; + } + + fn write(tag: u32, offset: u32, data: &[u8]) -> std::vec::Vec { + let mut bytes = tag.to_le_bytes().to_vec(); + bytes.extend_from_slice(&offset.to_le_bytes()); + bytes.extend_from_slice(&(data.len() as u64).to_le_bytes()); + bytes.extend_from_slice(data); + return bytes; + } + + fn decode( + program_id: &str, + bytes: &[u8], + account_count: usize, + ) -> kb_decoder_api::DecoderExecutionResult { + let input = replay_input(program_id, bytes, account_count, false); + return crate::loaders_decode(&input); + } + + fn upgradeable( + instruction: &solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction, + ) -> std::vec::Vec { + return match wincode::serialize(instruction) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("Loader v3 fixture serialization failed: {error}"); + }, + }; + } + + #[test] + fn immutable_generations_decode_write_and_finalize() { + for program_id in [ + kb_program_ids::BPF_LOADER_DEPRECATED_PROGRAM_ID, + kb_program_ids::BPF_LOADER_PROGRAM_ID, + ] { + let write_result = decode(program_id, immutable_write(0, 7, &[1, 2, 3]).as_slice(), 1); + assert_eq!(write_result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(write_result.observations[0].payload_json["parameters"]["offset"], 7); + assert_eq!(write_result.observations[0].payload_json["parameters"]["byteLength"], 3); + assert_eq!( + write_result.observations[0].payload_json["parameters"]["bytesPrefixHex"], + "010203" + ); + assert!(write_result.observations[0].payload_json["parameters"].get("bytes").is_none()); + let finalize_result = decode(program_id, unit(1).as_slice(), 2); + assert_eq!(finalize_result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + } + } + + #[test] + fn immutable_write_accepts_mainnet_u32_vector_wire() { + let mut bytes = unit(0); + bytes.extend_from_slice(&13_600_u32.to_le_bytes()); + bytes.extend_from_slice(&800_u32.to_le_bytes()); + bytes.extend_from_slice(vec![0x5a_u8; 800].as_slice()); + let result = decode(kb_program_ids::BPF_LOADER_PROGRAM_ID, bytes.as_slice(), 1); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(result.observations[0].payload_json["parameters"]["offset"], 13_600); + assert_eq!(result.observations[0].payload_json["parameters"]["byteLength"], 800); + } + + #[test] + fn upgradeable_loader_decodes_every_official_variant() { + let fixtures = [ + ( + solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction::InitializeBuffer, + 1, + "initialize_buffer", + ), + ( + solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction::Write { + offset: 9, + bytes: vec![4, 5], + }, + 2, + "write", + ), + ( + solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction::DeployWithMaxDataLen { + max_data_len: 4_096, + close_buffer: true, + }, + 8, + "deploy_with_max_data_len", + ), + ( + solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction::Upgrade { + close_buffer: false, + }, + 7, + "upgrade", + ), + ( + solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction::SetAuthority, + 2, + "set_authority", + ), + ( + solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction::Close { + tombstone: true, + }, + 2, + "close", + ), + ( + solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction::ExtendProgram { + additional_bytes: solana_loader_v3_interface::instruction::MINIMUM_EXTEND_PROGRAM_BYTES, + }, + 2, + "extend_program", + ), + ( + solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction::SetAuthorityChecked, + 3, + "set_authority_checked", + ), + ]; + for (instruction, account_count, entry_code) in fixtures { + let bytes = upgradeable(&instruction); + let result = decode( + kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID, + bytes.as_slice(), + account_count, + ); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some(entry_code) + ); + } + } + + #[test] + fn upgradeable_wincode_rejects_trailing_bytes() { + let mut bytes = upgradeable( + &solana_loader_v3_interface::instruction::UpgradeableLoaderInstruction::InitializeBuffer, + ); + bytes.push(0); + let result = decode(kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID, bytes.as_slice(), 1); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some("initialize_buffer") + ); + } + + #[test] + fn upgradeable_legacy_optional_fields_are_defaulted() { + let mut deploy = unit(2); + deploy.extend_from_slice(&2048_u64.to_le_bytes()); + let deploy_result = + decode(kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID, deploy.as_slice(), 8); + assert_eq!(deploy_result.observations[0].payload_json["parameters"]["closeBuffer"], true); + assert_eq!(deploy_result.observations[0].payload_json["historical"], true); + let upgrade_result = + decode(kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID, unit(3).as_slice(), 7); + assert_eq!(upgrade_result.observations[0].payload_json["parameters"]["closeBuffer"], true); + assert_eq!(upgrade_result.observations[0].payload_json["historical"], true); + let close_result = + decode(kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID, unit(5).as_slice(), 2); + assert_eq!(close_result.observations[0].payload_json["parameters"]["tombstone"], false); + assert_eq!(close_result.observations[0].payload_json["historical"], true); + } + + #[test] + fn loader_v4_decodes_every_official_variant() { + let fixtures = [ + (write(0, 3, &[8, 9]), 2, "write"), + ( + { + let mut value = unit(1); + value.extend_from_slice(&1_u32.to_le_bytes()); + value.extend_from_slice(&2_u32.to_le_bytes()); + value.extend_from_slice(&3_u32.to_le_bytes()); + value + }, + 3, + "copy", + ), + ( + { + let mut value = unit(2); + value.extend_from_slice(&4096_u32.to_le_bytes()); + value + }, + 2, + "set_program_length", + ), + (unit(3), 2, "deploy"), + (unit(4), 2, "retract"), + (unit(5), 3, "transfer_authority"), + (unit(6), 3, "finalize"), + ]; + for (bytes, account_count, entry_code) in fixtures { + let result = + decode(kb_program_ids::LOADER_V4_PROGRAM_ID, bytes.as_slice(), account_count); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some(entry_code) + ); + } + } + + #[test] + fn malformed_lengths_unknown_tags_and_invalid_boole_are_safe() { + let truncated = decode(kb_program_ids::BPF_LOADER_PROGRAM_ID, &[0, 0, 0], 1); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let mut mismatched = immutable_write(0, 0, &[1, 2]); + mismatched[8] = 3; + let mismatched_result = + decode(kb_program_ids::BPF_LOADER_PROGRAM_ID, mismatched.as_slice(), 1); + assert_eq!(mismatched_result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let unknown = decode(kb_program_ids::LOADER_V4_PROGRAM_ID, unit(99).as_slice(), 0); + assert_eq!(unknown.status, kb_decoder_api::DecoderOutcomeStatus::Unsupported); + let mut invalid_bool = unit(3); + invalid_bool.push(2); + let invalid_bool_result = + decode(kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID, invalid_bool.as_slice(), 7); + assert_eq!(invalid_bool_result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn extend_rejects_incomplete_optional_funding_pair() { + let mut bytes = unit(6); + bytes.extend_from_slice(&128_u32.to_le_bytes()); + let incomplete = + decode(kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID, bytes.as_slice(), 3); + assert_eq!(incomplete.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let complete = + decode(kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID, bytes.as_slice(), 4); + assert_eq!(complete.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + } + + #[test] + fn failed_loader_lifecycle_is_uncommitted() { + let bytes = unit(3); + let input = replay_input(kb_program_ids::LOADER_V4_PROGRAM_ID, bytes.as_slice(), 2, true); + let result = crate::loaders_decode(&input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(result.observations[0].transaction_failed); + assert!(!result.observations[0].observation_committed); + } + + #[test] + fn native_loader_direct_invocation_is_ignored_without_false_event() { + let input = replay_input(kb_program_ids::NATIVE_LOADER_PROGRAM_ID, &[], 0, false); + let result = crate::loaders_decode(&input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Ignored); + assert!(result.observations.is_empty()); + } + + #[test] + fn coverage_declares_every_loader_entry() { + let coverage = crate::loaders_coverage(); + assert_eq!(coverage.len(), 20); + assert!( + coverage + .iter() + .any(|entry| return entry.entry_code == "deploy_with_max_data_len") + ); + assert!(coverage.iter().any(|entry| { + return entry.entry_code == "upgrade" && entry.historical; + })); + assert!(coverage.iter().any(|entry| return entry.entry_code == "transfer_authority")); + assert!( + coverage + .iter() + .any(|entry| return entry.entry_code == "direct_invocation_opaque") + ); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/payload.rs b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/payload.rs new file mode 100644 index 0000000..1fb325d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/payload.rs @@ -0,0 +1,328 @@ +// file: kb_decoder_solana_core/src/payload.rs +// version: 5 + +//! Bounded native instruction payload helpers. + +use base64::Engine; // rust-rules: trait-import +use sha2::Digest; // rust-rules: trait-import + +/// One resolved outer instruction payload and its provenance relative to the target instruction. +pub(crate) struct ResolvedInstructionPayload { + /// Numeric outer instruction index in the transaction message. + pub(crate) instruction_index: usize, + /// Stable outer instruction path retained by the core store. + pub(crate) instruction_path: std::string::String, + /// Decoded instruction data. + pub(crate) bytes: std::vec::Vec, + /// Stable provenance code used by decoded precompile events. + pub(crate) provenance: &'static str, +} + +/// Decodes one retained base64 instruction payload with an explicit byte limit. +pub(crate) fn decode_instruction_data( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_core::Result> { + let payload = match input.instruction_payload_json.as_ref() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "native instruction payload is not retained", + )); + }, + }; + return decode_payload_json(payload, "native instruction"); +} + +/// Resolves an Ed25519 or secp256r1 instruction reference using the official `u16::MAX` sentinel. +pub(crate) fn resolve_u16_instruction_payload( + input: &kb_store_core::CoreInstructionReplayInput, + raw_instruction_index: u16, +) -> kb_core::Result { + if raw_instruction_index == u16::MAX { + let target_index_result = crate::target_outer_instruction_index(input); + let target_index = match target_index_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(crate::ResolvedInstructionPayload { + instruction_index: target_index, + instruction_path: input.instruction_path.clone(), + bytes, + provenance: "current_instruction", + }); + } + return resolve_explicit_outer_instruction(input, usize::from(raw_instruction_index)); +} + +/// Resolves a secp256k1 instruction reference. The runtime format has no current-instruction +/// sentinel: every `u8` value is an explicit outer instruction index. +pub(crate) fn resolve_u8_instruction_payload( + input: &kb_store_core::CoreInstructionReplayInput, + raw_instruction_index: u8, +) -> kb_core::Result { + return resolve_explicit_outer_instruction(input, usize::from(raw_instruction_index)); +} + +/// Returns the numeric outer index of the target instruction. +pub(crate) fn target_outer_instruction_index( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_core::Result { + if input.instruction_path.contains('/') { + return std::result::Result::Err(kb_core::Error::invalid_state( + "signature precompiles can only resolve outer instruction paths", + )); + } + let parse_result = input.instruction_path.parse::(); + return match parse_result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::invalid_state( + format!("target outer instruction path is not numeric: {error}"), + )), + }; +} + +/// Extracts one exact bounded byte slice with checked arithmetic. +pub(crate) fn bounded_slice<'a>( + bytes: &'a [u8], + offset: usize, + length: usize, + label: &str, +) -> kb_core::Result<&'a [u8]> { + let end = match offset.checked_add(length) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "{label} range overflows usize: offset={offset}, length={length}" + ))); + }, + }; + return match bytes.get(offset..end) { + std::option::Option::Some(value) => std::result::Result::Ok(value), + std::option::Option::None => { + std::result::Result::Err(kb_core::Error::invalid_state(format!( + "{label} range is outside referenced instruction: offset={offset}, length={length}, instructionLength={}", + bytes.len() + ))) + }, + }; +} + +/// Returns the decoded target payload length when retained and valid. +pub(crate) fn decoded_payload_length( + input: &kb_store_core::CoreInstructionReplayInput, +) -> std::option::Option { + return crate::decode_instruction_data(input).ok().map(|bytes| return bytes.len()); +} + +/// Returns the SHA-256 of the decoded target instruction data when available. +pub(crate) fn decoded_payload_sha256( + input: &kb_store_core::CoreInstructionReplayInput, +) -> std::option::Option { + let bytes_result = crate::decode_instruction_data(input); + return match bytes_result { + std::result::Result::Ok(bytes) => { + std::option::Option::Some(crate::hash_bytes(bytes.as_slice())) + }, + std::result::Result::Err(_error) => std::option::Option::None, + }; +} + +/// Returns a stable payload hash from core or computes it from retained JSON. +pub(crate) fn payload_hash( + input: &kb_store_core::CoreInstructionReplayInput, +) -> std::string::String { + if let std::option::Option::Some(value) = input.instruction_payload_hash.as_ref() { + return value.clone(); + } + let mut value = match input.instruction_payload_json.clone() { + std::option::Option::Some(value) => value, + std::option::Option::None => serde_json::Value::Null, + }; + let hash_result = kb_decoder_api::deterministic_json_hash(&mut value); + return match hash_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => "payload_hash_unavailable".to_string(), + }; +} + +/// Reads one little-endian `u32` from an exact byte range. +pub(crate) fn read_u32_le(bytes: &[u8], offset: usize) -> kb_core::Result { + let slice_result = crate::bounded_slice(bytes, offset, 4, "native instruction u32"); + let slice = match slice_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let array_result = <[u8; 4]>::try_from(slice); + return match array_result { + std::result::Result::Ok(value) => std::result::Result::Ok(u32::from_le_bytes(value)), + std::result::Result::Err(_error) => std::result::Result::Err( + kb_core::Error::invalid_state("native instruction u32 slice has an invalid length"), + ), + }; +} + +/// Reads one little-endian `u64` from an exact byte range. +pub(crate) fn read_u64_le(bytes: &[u8], offset: usize) -> kb_core::Result { + let slice_result = crate::bounded_slice(bytes, offset, 8, "native instruction u64"); + let slice = match slice_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let array_result = <[u8; 8]>::try_from(slice); + return match array_result { + std::result::Result::Ok(value) => std::result::Result::Ok(u64::from_le_bytes(value)), + std::result::Result::Err(_error) => std::result::Result::Err( + kb_core::Error::invalid_state("native instruction u64 slice has an invalid length"), + ), + }; +} + +/// Returns the lowercase SHA-256 of one bounded byte slice. +pub(crate) fn hash_bytes(bytes: &[u8]) -> std::string::String { + let digest = sha2::Sha256::digest(bytes); + let mut output = std::string::String::with_capacity(digest.len().saturating_mul(2)); + for byte in digest { + output.push_str(format!("{byte:02x}").as_str()); + } + return output; +} + +/// Returns a normalized lowercase hexadecimal prefix. +pub(crate) fn hexadecimal_prefix( + bytes: &[u8], + count: usize, +) -> std::option::Option { + let selected = match bytes.get(..count) { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::option::Option::None, + }; + let mut output = std::string::String::with_capacity(count.saturating_mul(2)); + for byte in selected { + output.push_str(format!("{byte:02x}").as_str()); + } + return std::option::Option::Some(output); +} + +/// Returns a hexadecimal prefix no longer than the available byte slice. +pub(crate) fn bounded_hexadecimal_prefix( + bytes: &[u8], + maximum_count: usize, +) -> std::string::String { + let count = std::cmp::min(bytes.len(), maximum_count); + return match crate::hexadecimal_prefix(bytes, count) { + std::option::Option::Some(value) => value, + std::option::Option::None => std::string::String::new(), + }; +} + +fn resolve_explicit_outer_instruction( + input: &kb_store_core::CoreInstructionReplayInput, + instruction_index: usize, +) -> kb_core::Result { + let entries = match input.outer_instructions_json.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "outer instruction context is not a JSON array", + )); + }, + }; + let target_index_result = crate::target_outer_instruction_index(input); + let target_index = match target_index_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let entry = entries.iter().find(|entry| { + let value = entry + .get("instructionIndex") + .and_then(serde_json::Value::as_u64) + .and_then(|value| return usize::try_from(value).ok()); + return value == std::option::Option::Some(instruction_index); + }); + let entry = match entry { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "referenced outer instruction index is absent: {instruction_index}" + ))); + }, + }; + let instruction_path = match entry.get("instructionPath").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) if !value.trim().is_empty() => value.to_string(), + _ => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "referenced outer instruction {instruction_index} has no instructionPath" + ))); + }, + }; + let payload_json = match entry.get("payloadJson") { + std::option::Option::Some(value) if !value.is_null() => value, + _ => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "referenced outer instruction {instruction_index} has no retained payloadJson" + ))); + }, + }; + let bytes_result = decode_payload_json( + payload_json, + format!("referenced outer instruction {instruction_index}").as_str(), + ); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(crate::ResolvedInstructionPayload { + instruction_index, + instruction_path, + bytes, + provenance: if instruction_index == target_index { + "current_instruction" + } else { + "outer_instruction" + }, + }); +} + +fn decode_payload_json( + payload: &serde_json::Value, + label: &str, +) -> kb_core::Result> { + let encoded = match payload.get("dataBase64").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "{label} payload does not contain dataBase64" + ))); + }, + }; + let maximum_encoded_length = crate::MAX_NATIVE_INSTRUCTION_PAYLOAD_BYTES + .saturating_mul(4) + .saturating_div(3) + .saturating_add(4); + if encoded.len() > maximum_encoded_length { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "{label} base64 payload exceeds {maximum_encoded_length} bytes" + ))); + } + let decoded_result = base64::engine::general_purpose::STANDARD.decode(encoded.as_bytes()); + let decoded = match decoded_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "{label} payload is not valid base64: {error}" + ))); + }, + }; + if decoded.len() > crate::MAX_NATIVE_INSTRUCTION_PAYLOAD_BYTES { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "{label} payload exceeds {} decoded bytes", + crate::MAX_NATIVE_INSTRUCTION_PAYLOAD_BYTES + ))); + } + return std::result::Result::Ok(decoded); +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/precompiles.rs b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/precompiles.rs new file mode 100644 index 0000000..2ba579b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/precompiles.rs @@ -0,0 +1,1608 @@ +// file: kb_decoder_solana_core/src/precompiles.rs +// version: 3 + +//! Bounded structural decoding for Solana signature verification precompiles. + +const ENTRY_CODE: &str = "signature_verification"; +const SOURCE_ED25519: &str = + "agave precompiles/src/ed25519.rs; solana-ed25519-program@3.0.0 exact offset layout"; +const SOURCE_SECP256K1: &str = + "agave precompiles/src/secp256k1.rs; solana-secp256k1-program@3.0.0 exact offset layout"; +const SOURCE_SECP256R1: &str = + "agave precompiles/src/secp256r1.rs; solana-secp256r1-program@3.0.0 exact offset layout"; + +struct U16SignatureOffsets { + signature_offset: u16, + signature_instruction_index: u16, + verification_key_offset: u16, + verification_key_instruction_index: u16, + message_data_offset: u16, + message_data_size: u16, + message_instruction_index: u16, +} + +struct U8SignatureOffsets { + signature_offset: u16, + signature_instruction_index: u8, + ethereum_address_offset: u16, + ethereum_address_instruction_index: u8, + message_data_offset: u16, + message_data_size: u16, + message_instruction_index: u8, +} + +struct U16PrecompileSpec { + algorithm: &'static str, + surface_code: &'static str, + verification_key_size: usize, + zero_signature_allowed: bool, + maximum_signatures: usize, + source: &'static str, +} + +/// Returns declared signature precompile coverage. +pub(crate) fn precompiles_coverage() -> std::vec::Vec { + return vec![ + coverage_entry(kb_program_ids::ED25519_PROGRAM_ID, crate::ED25519_SURFACE_CODE), + coverage_entry(kb_program_ids::SECP256K1_PROGRAM_ID, crate::SECP256K1_SURFACE_CODE), + coverage_entry(kb_program_ids::SECP256R1_PROGRAM_ID, crate::SECP256R1_SURFACE_CODE), + ]; +} + +/// Recognizes one signature precompile instruction without resolving referenced data. +pub(crate) fn precompiles_recognize( + input: &kb_store_core::CoreInstructionReplayInput, + priority: u16, +) -> kb_decoder_api::DecoderRecognition { + let surface_code = match surface_code(input.program_id.as_str()) { + std::option::Option::Some(value) => value, + std::option::Option::None => return kb_decoder_api::DecoderRecognition::incompatible(), + }; + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return kb_decoder_api::DecoderRecognition::compatible( + false, + priority, + std::option::Option::Some(surface_code.to_string()), + std::option::Option::Some(ENTRY_CODE.to_string()), + std::option::Option::None, + ); + }, + }; + let prefix_count = std::cmp::min(bytes.len(), 2); + let discriminator_hex = if prefix_count == 0 { + std::option::Option::None + } else { + crate::hexadecimal_prefix(bytes.as_slice(), prefix_count) + }; + return kb_decoder_api::DecoderRecognition::compatible( + true, + priority, + std::option::Option::Some(surface_code.to_string()), + std::option::Option::Some(ENTRY_CODE.to_string()), + discriminator_hex, + ); +} + +/// Decodes one signature precompile instruction structurally without cryptographic recomputation. +pub(crate) fn precompiles_decode( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_decoder_api::DecoderExecutionResult { + if input.program_id == kb_program_ids::ED25519_PROGRAM_ID { + return decode_u16_precompile( + input, + &U16PrecompileSpec { + algorithm: "ed25519", + surface_code: crate::ED25519_SURFACE_CODE, + verification_key_size: crate::ED25519_PUBLIC_KEY_BYTES, + zero_signature_allowed: true, + maximum_signatures: usize::from(u8::MAX), + source: SOURCE_ED25519, + }, + ); + } + if input.program_id == kb_program_ids::SECP256K1_PROGRAM_ID { + return decode_secp256k1(input); + } + if input.program_id == kb_program_ids::SECP256R1_PROGRAM_ID { + return decode_u16_precompile( + input, + &U16PrecompileSpec { + algorithm: "secp256r1", + surface_code: crate::SECP256R1_SURFACE_CODE, + verification_key_size: crate::SECP256R1_PUBLIC_KEY_BYTES, + zero_signature_allowed: false, + maximum_signatures: crate::SECP256R1_MAX_SIGNATURES, + source: SOURCE_SECP256R1, + }, + ); + } + return crate::unsupported_result( + ENTRY_CODE, + "signature_precompile_program_unsupported", + "program id is not one of the three supported signature precompiles", + ); +} + +fn decode_u16_precompile( + input: &kb_store_core::CoreInstructionReplayInput, + spec: &U16PrecompileSpec, +) -> kb_decoder_api::DecoderExecutionResult { + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return failed( + input, + spec.algorithm, + "signature_precompile_payload_invalid", + std::option::Option::None, + error.to_string(), + ); + }, + }; + if bytes.len() < 2 { + return failed( + input, + spec.algorithm, + "signature_precompile_header_truncated", + std::option::Option::None, + format!("header requires 2 bytes but payload has {}", bytes.len()), + ); + } + let signature_count = match bytes.first() { + std::option::Option::Some(value) => usize::from(*value), + std::option::Option::None => { + return failed( + input, + spec.algorithm, + "signature_precompile_header_truncated", + std::option::Option::None, + "header does not contain a signature-count byte", + ); + }, + }; + let padding = match bytes.get(1) { + std::option::Option::Some(value) => *value, + std::option::Option::None => { + return failed( + input, + spec.algorithm, + "signature_precompile_header_truncated", + std::option::Option::None, + "header does not contain a padding byte", + ); + }, + }; + if signature_count == 0 { + if !spec.zero_signature_allowed { + return failed( + input, + spec.algorithm, + "signature_precompile_zero_signatures_rejected", + std::option::Option::None, + "runtime rejects zero signatures for this precompile", + ); + } + if bytes.len() != 2 { + return failed( + input, + spec.algorithm, + "signature_precompile_zero_signature_size_invalid", + std::option::Option::None, + format!( + "zero-signature payload must contain exactly 2 bytes but has {}", + bytes.len() + ), + ); + } + return decoded_result( + input, + spec.surface_code, + spec.algorithm, + spec.source, + vec![zero_signature_parameters( + input, + spec.algorithm, + std::option::Option::Some(padding), + )], + ); + } + if signature_count > spec.maximum_signatures { + return failed( + input, + spec.algorithm, + "signature_precompile_count_exceeds_runtime_limit", + std::option::Option::None, + format!("signatureCount={signature_count} exceeds maximum={}", spec.maximum_signatures), + ); + } + let table_bytes = match signature_count.checked_mul(crate::U16_OFFSETS_BYTES) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return failed( + input, + spec.algorithm, + "signature_precompile_table_size_overflow", + std::option::Option::None, + "offset table size overflows usize", + ); + }, + }; + let table_end = match 2usize.checked_add(table_bytes) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return failed( + input, + spec.algorithm, + "signature_precompile_table_end_overflow", + std::option::Option::None, + "offset table end overflows usize", + ); + }, + }; + if bytes.len() < table_end { + return failed( + input, + spec.algorithm, + "signature_precompile_offsets_truncated", + std::option::Option::None, + format!("offset table requires {table_end} bytes but payload has {}", bytes.len()), + ); + } + let mut parameters = std::vec::Vec::with_capacity(signature_count); + for entry_index in 0..signature_count { + let relative_offset = match entry_index.checked_mul(crate::U16_OFFSETS_BYTES) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return failed( + input, + spec.algorithm, + "signature_precompile_entry_offset_overflow", + std::option::Option::Some(entry_index), + "entry offset multiplication overflows usize", + ); + }, + }; + let entry_offset = match 2usize.checked_add(relative_offset) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return failed( + input, + spec.algorithm, + "signature_precompile_entry_offset_overflow", + std::option::Option::Some(entry_index), + "entry offset addition overflows usize", + ); + }, + }; + let offsets_result = read_u16_offsets(bytes.as_slice(), entry_offset); + let offsets = match offsets_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return failed( + input, + spec.algorithm, + "signature_precompile_offsets_invalid", + std::option::Option::Some(entry_index), + error.to_string(), + ); + }, + }; + let entry_result = + resolve_u16_entry(input, spec, signature_count, padding, entry_index, &offsets); + match entry_result { + std::result::Result::Ok(value) => parameters.push(value), + std::result::Result::Err(error) => { + return failed( + input, + spec.algorithm, + "signature_precompile_reference_invalid", + std::option::Option::Some(entry_index), + error.to_string(), + ); + }, + } + } + return decoded_result(input, spec.surface_code, spec.algorithm, spec.source, parameters); +} + +fn decode_secp256k1( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_decoder_api::DecoderExecutionResult { + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return failed( + input, + "secp256k1", + "secp256k1_payload_invalid", + std::option::Option::None, + error.to_string(), + ); + }, + }; + if bytes.is_empty() { + return failed( + input, + "secp256k1", + "secp256k1_header_truncated", + std::option::Option::None, + "header requires one signature-count byte", + ); + } + let signature_count = match bytes.first() { + std::option::Option::Some(value) => usize::from(*value), + std::option::Option::None => { + return failed( + input, + "secp256k1", + "secp256k1_header_truncated", + std::option::Option::None, + "header does not contain a signature-count byte", + ); + }, + }; + if signature_count == 0 { + if bytes.len() != 1 { + return failed( + input, + "secp256k1", + "secp256k1_zero_signature_size_invalid", + std::option::Option::None, + format!( + "zero-signature payload must contain exactly 1 byte but has {}", + bytes.len() + ), + ); + } + return decoded_result( + input, + crate::SECP256K1_SURFACE_CODE, + "secp256k1", + SOURCE_SECP256K1, + vec![zero_signature_parameters(input, "secp256k1", std::option::Option::None)], + ); + } + let table_bytes = match signature_count.checked_mul(crate::U8_OFFSETS_BYTES) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return failed( + input, + "secp256k1", + "secp256k1_table_size_overflow", + std::option::Option::None, + "offset table size overflows usize", + ); + }, + }; + let table_end = match 1usize.checked_add(table_bytes) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return failed( + input, + "secp256k1", + "secp256k1_table_end_overflow", + std::option::Option::None, + "offset table end overflows usize", + ); + }, + }; + if bytes.len() < table_end { + return failed( + input, + "secp256k1", + "secp256k1_offsets_truncated", + std::option::Option::None, + format!("offset table requires {table_end} bytes but payload has {}", bytes.len()), + ); + } + let mut parameters = std::vec::Vec::with_capacity(signature_count); + for entry_index in 0..signature_count { + let relative_offset = match entry_index.checked_mul(crate::U8_OFFSETS_BYTES) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return failed( + input, + "secp256k1", + "secp256k1_entry_offset_overflow", + std::option::Option::Some(entry_index), + "entry offset multiplication overflows usize", + ); + }, + }; + let entry_offset = match 1usize.checked_add(relative_offset) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return failed( + input, + "secp256k1", + "secp256k1_entry_offset_overflow", + std::option::Option::Some(entry_index), + "entry offset addition overflows usize", + ); + }, + }; + let offsets_result = read_u8_offsets(bytes.as_slice(), entry_offset); + let offsets = match offsets_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return failed( + input, + "secp256k1", + "secp256k1_offsets_invalid", + std::option::Option::Some(entry_index), + error.to_string(), + ); + }, + }; + let entry_result = resolve_u8_entry(input, signature_count, entry_index, &offsets); + match entry_result { + std::result::Result::Ok(value) => parameters.push(value), + std::result::Result::Err(error) => { + return failed( + input, + "secp256k1", + "secp256k1_reference_invalid", + std::option::Option::Some(entry_index), + error.to_string(), + ); + }, + } + } + return decoded_result( + input, + crate::SECP256K1_SURFACE_CODE, + "secp256k1", + SOURCE_SECP256K1, + parameters, + ); +} + +fn resolve_u16_entry( + input: &kb_store_core::CoreInstructionReplayInput, + spec: &U16PrecompileSpec, + signature_count: usize, + padding: u8, + entry_index: usize, + offsets: &U16SignatureOffsets, +) -> kb_core::Result { + let signature_payload_result = + crate::resolve_u16_instruction_payload(input, offsets.signature_instruction_index); + let signature_payload = match signature_payload_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let verification_key_payload_result = + crate::resolve_u16_instruction_payload(input, offsets.verification_key_instruction_index); + let verification_key_payload = match verification_key_payload_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let message_payload_result = + crate::resolve_u16_instruction_payload(input, offsets.message_instruction_index); + let message_payload = match message_payload_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let signature_result = crate::bounded_slice( + signature_payload.bytes.as_slice(), + usize::from(offsets.signature_offset), + crate::SIGNATURE_BYTES, + "signature", + ); + let signature = match signature_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let verification_key_result = crate::bounded_slice( + verification_key_payload.bytes.as_slice(), + usize::from(offsets.verification_key_offset), + spec.verification_key_size, + "verification key", + ); + let verification_key = match verification_key_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let message_result = crate::bounded_slice( + message_payload.bytes.as_slice(), + usize::from(offsets.message_data_offset), + usize::from(offsets.message_data_size), + "message", + ); + let message = match message_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(serde_json::json!({ + "algorithm": spec.algorithm, + "entryCount": signature_count, + "entryIndex": entry_index, + "padding": padding, + "offsets": { + "signatureOffset": offsets.signature_offset, + "verificationKeyOffset": offsets.verification_key_offset, + "messageDataOffset": offsets.message_data_offset, + "messageDataSize": offsets.message_data_size, + }, + "rawInstructionIndexes": { + "signatureInstructionIndex": offsets.signature_instruction_index, + "verificationKeyInstructionIndex": offsets.verification_key_instruction_index, + "messageInstructionIndex": offsets.message_instruction_index, + }, + "resolvedInstructionIndexes": { + "signatureInstructionIndex": signature_payload.instruction_index, + "verificationKeyInstructionIndex": verification_key_payload.instruction_index, + "messageInstructionIndex": message_payload.instruction_index, + }, + "resolvedInstructionPaths": { + "signatureInstructionPath": signature_payload.instruction_path, + "verificationKeyInstructionPath": verification_key_payload.instruction_path, + "messageInstructionPath": message_payload.instruction_path, + }, + "provenance": { + "signature": signature_payload.provenance, + "verificationKey": verification_key_payload.provenance, + "message": message_payload.provenance, + }, + "signature": component_summary(signature), + "publicKey": component_summary(verification_key), + "message": component_summary(message), + "runtimeVerification": runtime_verification(input), + "transactionSucceeded": !input.transaction_failed, + "observationCommitted": !input.transaction_failed, + "cryptographicReverificationPerformed": false, + "materializedOutputs": 0, + })); +} + +fn resolve_u8_entry( + input: &kb_store_core::CoreInstructionReplayInput, + signature_count: usize, + entry_index: usize, + offsets: &U8SignatureOffsets, +) -> kb_core::Result { + let signature_payload_result = + crate::resolve_u8_instruction_payload(input, offsets.signature_instruction_index); + let signature_payload = match signature_payload_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let address_payload_result = + crate::resolve_u8_instruction_payload(input, offsets.ethereum_address_instruction_index); + let address_payload = match address_payload_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let message_payload_result = + crate::resolve_u8_instruction_payload(input, offsets.message_instruction_index); + let message_payload = match message_payload_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let signature_result = crate::bounded_slice( + signature_payload.bytes.as_slice(), + usize::from(offsets.signature_offset), + crate::SIGNATURE_BYTES, + "secp256k1 compact signature", + ); + let signature = match signature_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let recovery_offset = + match usize::from(offsets.signature_offset).checked_add(crate::SIGNATURE_BYTES) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "secp256k1 recovery id offset overflows usize", + )); + }, + }; + let recovery_result = crate::bounded_slice( + signature_payload.bytes.as_slice(), + recovery_offset, + 1, + "secp256k1 recovery id", + ); + let recovery_slice = match recovery_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let recovery = match recovery_slice.first() { + std::option::Option::Some(value) => *value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "secp256k1 recovery id slice is unexpectedly empty", + )); + }, + }; + let address_result = crate::bounded_slice( + address_payload.bytes.as_slice(), + usize::from(offsets.ethereum_address_offset), + crate::SECP256K1_ETHEREUM_ADDRESS_BYTES, + "secp256k1 Ethereum address", + ); + let address = match address_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let message_result = crate::bounded_slice( + message_payload.bytes.as_slice(), + usize::from(offsets.message_data_offset), + usize::from(offsets.message_data_size), + "secp256k1 message", + ); + let message = match message_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(serde_json::json!({ + "algorithm": "secp256k1", + "entryCount": signature_count, + "entryIndex": entry_index, + "offsets": { + "signatureOffset": offsets.signature_offset, + "ethereumAddressOffset": offsets.ethereum_address_offset, + "messageDataOffset": offsets.message_data_offset, + "messageDataSize": offsets.message_data_size, + }, + "rawInstructionIndexes": { + "signatureInstructionIndex": offsets.signature_instruction_index, + "ethereumAddressInstructionIndex": offsets.ethereum_address_instruction_index, + "messageInstructionIndex": offsets.message_instruction_index, + }, + "resolvedInstructionIndexes": { + "signatureInstructionIndex": signature_payload.instruction_index, + "ethereumAddressInstructionIndex": address_payload.instruction_index, + "messageInstructionIndex": message_payload.instruction_index, + }, + "resolvedInstructionPaths": { + "signatureInstructionPath": signature_payload.instruction_path, + "ethereumAddressInstructionPath": address_payload.instruction_path, + "messageInstructionPath": message_payload.instruction_path, + }, + "provenance": { + "signature": signature_payload.provenance, + "ethereumAddress": address_payload.provenance, + "message": message_payload.provenance, + }, + "signature": component_summary(signature), + "recoveryId": recovery, + "ethereumAddress": component_summary(address), + "message": component_summary(message), + "runtimeVerification": runtime_verification(input), + "transactionSucceeded": !input.transaction_failed, + "observationCommitted": !input.transaction_failed, + "cryptographicReverificationPerformed": false, + "materializedOutputs": 0, + })); +} + +fn read_u16_offsets(bytes: &[u8], offset: usize) -> kb_core::Result { + let table_result = crate::bounded_slice( + bytes, + offset, + crate::U16_OFFSETS_BYTES, + "Ed25519 or secp256r1 offsets", + ); + let table = match table_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let array_result = <[u8; crate::U16_OFFSETS_BYTES]>::try_from(table); + let array = match array_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Ed25519 or secp256r1 offsets have an invalid byte length", + )); + }, + }; + let [ + signature_offset_0, + signature_offset_1, + signature_instruction_index_0, + signature_instruction_index_1, + verification_key_offset_0, + verification_key_offset_1, + verification_key_instruction_index_0, + verification_key_instruction_index_1, + message_data_offset_0, + message_data_offset_1, + message_data_size_0, + message_data_size_1, + message_instruction_index_0, + message_instruction_index_1, + ] = array; + return std::result::Result::Ok(U16SignatureOffsets { + signature_offset: u16::from_le_bytes([signature_offset_0, signature_offset_1]), + signature_instruction_index: u16::from_le_bytes([ + signature_instruction_index_0, + signature_instruction_index_1, + ]), + verification_key_offset: u16::from_le_bytes([ + verification_key_offset_0, + verification_key_offset_1, + ]), + verification_key_instruction_index: u16::from_le_bytes([ + verification_key_instruction_index_0, + verification_key_instruction_index_1, + ]), + message_data_offset: u16::from_le_bytes([message_data_offset_0, message_data_offset_1]), + message_data_size: u16::from_le_bytes([message_data_size_0, message_data_size_1]), + message_instruction_index: u16::from_le_bytes([ + message_instruction_index_0, + message_instruction_index_1, + ]), + }); +} + +fn read_u8_offsets(bytes: &[u8], offset: usize) -> kb_core::Result { + let table_result = + crate::bounded_slice(bytes, offset, crate::U8_OFFSETS_BYTES, "secp256k1 offsets"); + let table = match table_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let array_result = <[u8; crate::U8_OFFSETS_BYTES]>::try_from(table); + let array = match array_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "secp256k1 offsets have an invalid byte length", + )); + }, + }; + let [ + signature_offset_0, + signature_offset_1, + signature_instruction_index, + ethereum_address_offset_0, + ethereum_address_offset_1, + ethereum_address_instruction_index, + message_data_offset_0, + message_data_offset_1, + message_data_size_0, + message_data_size_1, + message_instruction_index, + ] = array; + return std::result::Result::Ok(U8SignatureOffsets { + signature_offset: u16::from_le_bytes([signature_offset_0, signature_offset_1]), + signature_instruction_index, + ethereum_address_offset: u16::from_le_bytes([ + ethereum_address_offset_0, + ethereum_address_offset_1, + ]), + ethereum_address_instruction_index, + message_data_offset: u16::from_le_bytes([message_data_offset_0, message_data_offset_1]), + message_data_size: u16::from_le_bytes([message_data_size_0, message_data_size_1]), + message_instruction_index, + }); +} + +fn decoded_result( + input: &kb_store_core::CoreInstructionReplayInput, + surface_code: &str, + algorithm: &str, + source: &str, + parameters: std::vec::Vec, +) -> kb_decoder_api::DecoderExecutionResult { + let payload_hash = crate::payload_hash(input); + let instruction_data_sha256 = match crate::decoded_payload_sha256(input) { + std::option::Option::Some(value) => value, + std::option::Option::None => "instruction_data_sha256_unavailable".to_string(), + }; + let mut observations = std::vec::Vec::with_capacity(parameters.len()); + for parameter in parameters { + let entry_key = match parameter.get("entryIndex").and_then(serde_json::Value::as_u64) { + std::option::Option::Some(value) => value.to_string(), + std::option::Option::None => "batch".to_string(), + }; + let event = kb_model::DecodedProtocolEvent { + signature: kb_model::Signature(input.signature.clone()), + slot: kb_model::Slot(input.slot), + instruction_path: kb_model::InstructionPath(input.instruction_path.clone()), + program_id: kb_model::ProgramId(input.program_id.clone()), + protocol_code: kb_model::ProtocolCode(crate::PROTOCOL_CODE.to_string()), + surface_code: kb_model::SurfaceCode(surface_code.to_string()), + event_code: kb_model::EventCode(format!("{surface_code}.{ENTRY_CODE}")), + event_name: kb_model::EventName(ENTRY_CODE.to_string()), + event_family: kb_model::EventFamily::Audit, + source_kind: kb_model::EventSourceKind::Instruction, + confidence: kb_model::DecoderConfidence::ManualExact, + }; + observations.push(kb_decoder_api::DecodedObservation { + event_key: format!("{ENTRY_CODE}:{entry_key}"), + event, + payload_json: serde_json::json!({ + "eventVersion": crate::NATIVE_EVENT_VERSION, + "programId": input.program_id, + "surfaceCode": surface_code, + "entryCode": ENTRY_CODE, + "algorithm": algorithm, + "historical": false, + "instructionPath": input.instruction_path, + "transactionSucceeded": !input.transaction_failed, + "observationCommitted": !input.transaction_failed, + "payloadHash": payload_hash, + "instructionDataSha256": instruction_data_sha256, + "parameters": parameter, + "materializedOutputs": 0, + }), + transaction_failed: input.transaction_failed, + transaction_error: input.transaction_err_json.clone(), + observation_committed: !input.transaction_failed, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::ExactLayout, + confidence: kb_model::DecoderConfidence::ManualExact, + evidence: vec![ + source.to_string(), + "structural_decode_only:no_cryptographic_reverification".to_string(), + format!("instruction_data_sha256:{instruction_data_sha256}"), + ], + }, + }); + } + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Decoded, + recognized_entry_code: std::option::Option::Some(ENTRY_CODE.to_string()), + observations, + diagnostics: std::vec::Vec::new(), + }; +} + +fn zero_signature_parameters( + input: &kb_store_core::CoreInstructionReplayInput, + algorithm: &str, + padding: std::option::Option, +) -> serde_json::Value { + let mut parameters = serde_json::json!({ + "algorithm": algorithm, + "entryCount": 0, + "entryIndex": serde_json::Value::Null, + "zeroSignatureNoop": true, + "runtimeVerification": runtime_verification(input), + "transactionSucceeded": !input.transaction_failed, + "observationCommitted": !input.transaction_failed, + "cryptographicReverificationPerformed": false, + "materializedOutputs": 0, + }); + if let std::option::Option::Some(value) = padding + && let std::option::Option::Some(object) = parameters.as_object_mut() + { + object.insert("padding".to_string(), serde_json::json!(value)); + } + return parameters; +} + +fn component_summary(bytes: &[u8]) -> serde_json::Value { + return serde_json::json!({ + "length": bytes.len(), + "sha256": crate::hash_bytes(bytes), + "hexPrefix": crate::bounded_hexadecimal_prefix( + bytes, + crate::PRECOMPILE_COMPONENT_PREFIX_BYTES, + ), + }); +} + +fn runtime_verification(input: &kb_store_core::CoreInstructionReplayInput) -> &'static str { + return if input.transaction_failed { + "not_asserted_transaction_failed" + } else { + "accepted_in_successful_transaction" + }; +} + +fn failed( + input: &kb_store_core::CoreInstructionReplayInput, + algorithm: &str, + code: &str, + entry_index: std::option::Option, + detail: impl std::convert::Into, +) -> kb_decoder_api::DecoderExecutionResult { + let detail_value = detail.into(); + let bounded_detail = detail_value.chars().take(512).collect::(); + let decoded_result = crate::decode_instruction_data(input); + let (payload_length, signature_count, payload_sha256) = match decoded_result { + std::result::Result::Ok(bytes) => { + let count = match bytes.first() { + std::option::Option::Some(value) => value.to_string(), + std::option::Option::None => "absent".to_string(), + }; + (bytes.len().to_string(), count, crate::hash_bytes(bytes.as_slice())) + }, + std::result::Result::Err(_error) => { + ("unknown".to_string(), "unknown".to_string(), "unavailable".to_string()) + }, + }; + let entry = match entry_index { + std::option::Option::Some(value) => value.to_string(), + std::option::Option::None => "none".to_string(), + }; + return crate::failed_result( + std::option::Option::Some(ENTRY_CODE), + code, + format!( + "algorithm={algorithm}; signatureCount={signature_count}; entryIndex={entry}; payloadLength={payload_length}; payloadSha256={payload_sha256}; payloadContextHash={}; detail={bounded_detail}", + crate::payload_hash(input) + ), + ); +} + +fn coverage_entry( + program_id: &str, + surface_code: &str, +) -> kb_decoder_api::DecoderCoverageDeclaration { + return kb_decoder_api::DecoderCoverageDeclaration { + program_id: program_id.to_string(), + surface_code: std::option::Option::Some(surface_code.to_string()), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: ENTRY_CODE.to_string(), + discriminator_hex: std::option::Option::None, + historical: false, + }; +} + +fn surface_code(program_id: &str) -> std::option::Option<&'static str> { + if program_id == kb_program_ids::ED25519_PROGRAM_ID { + return std::option::Option::Some(crate::ED25519_SURFACE_CODE); + } + if program_id == kb_program_ids::SECP256K1_PROGRAM_ID { + return std::option::Option::Some(crate::SECP256K1_SURFACE_CODE); + } + if program_id == kb_program_ids::SECP256R1_PROGRAM_ID { + return std::option::Option::Some(crate::SECP256R1_SURFACE_CODE); + } + return std::option::Option::None; +} + +#[cfg(test)] +mod tests { + fn replay_input( + program_id: &str, + target_bytes: std::vec::Vec, + mut external: std::vec::Vec<(usize, std::vec::Vec)>, + transaction_failed: bool, + ) -> kb_store_core::CoreInstructionReplayInput { + external.push((1, target_bytes.clone())); + external.sort_by_key(|entry| return entry.0); + let outer = external + .iter() + .map(|(index, bytes)| { + return serde_json::json!({ + "instructionIndex": index, + "instructionPath": index.to_string(), + "programId": if *index == 1 { program_id } else { "external_program" }, + "payloadJson": { + "dataBase64": base64::Engine::encode( + &base64::engine::general_purpose::STANDARD, + bytes, + ) + }, + "payloadHash": crate::hash_bytes(bytes), + }); + }) + .collect::>(); + let result = kb_store_core::CoreInstructionReplayInput::new( + "signature:1", + "signature", + 42, + "1", + program_id, + transaction_failed, + if transaction_failed { + std::option::Option::Some(serde_json::json!({ + "InstructionError": [1, "Custom"] + })) + } else { + std::option::Option::None + }, + serde_json::json!([]), + serde_json::json!([]), + std::option::Option::Some(serde_json::json!({ + "dataBase64": base64::Engine::encode( + &base64::engine::general_purpose::STANDARD, + target_bytes.as_slice(), + ) + })), + std::option::Option::Some(crate::hash_bytes(target_bytes.as_slice())), + serde_json::Value::Array(outer), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected replay input error: {error}"), + }; + } + + fn u16_current_payload(key_size: usize, message: &[u8], padding: u8) -> std::vec::Vec { + let table_end = 2 + crate::U16_OFFSETS_BYTES; + let signature_offset = table_end; + let key_offset = signature_offset + crate::SIGNATURE_BYTES; + let message_offset = key_offset + key_size; + let mut bytes = vec![1, padding]; + push_u16(&mut bytes, u16_value(signature_offset)); + push_u16(&mut bytes, u16::MAX); + push_u16(&mut bytes, u16_value(key_offset)); + push_u16(&mut bytes, u16::MAX); + push_u16(&mut bytes, u16_value(message_offset)); + push_u16(&mut bytes, u16_value(message.len())); + push_u16(&mut bytes, u16::MAX); + bytes.extend(std::iter::repeat_n(0x11, crate::SIGNATURE_BYTES)); + bytes.extend(std::iter::repeat_n(0x22, key_size)); + bytes.extend_from_slice(message); + return bytes; + } + + fn u16_external_payload( + key_size: usize, + message: &[u8], + ) -> (std::vec::Vec, std::vec::Vec) { + let signature_offset = 0; + let key_offset = crate::SIGNATURE_BYTES; + let message_offset = key_offset + key_size; + let mut target = vec![1, 0]; + push_u16(&mut target, u16_value(signature_offset)); + push_u16(&mut target, 0); + push_u16(&mut target, u16_value(key_offset)); + push_u16(&mut target, 0); + push_u16(&mut target, u16_value(message_offset)); + push_u16(&mut target, u16_value(message.len())); + push_u16(&mut target, 0); + let mut external = vec![0x31; crate::SIGNATURE_BYTES]; + external.extend(std::iter::repeat_n(0x32, key_size)); + external.extend_from_slice(message); + return (target, external); + } + + fn u16_mixed_payload( + key_size: usize, + ) -> (std::vec::Vec, std::vec::Vec<(usize, std::vec::Vec)>) { + let signature_offset = 2 + crate::U16_OFFSETS_BYTES; + let mut target = vec![1, 0]; + push_u16(&mut target, u16_value(signature_offset)); + push_u16(&mut target, u16::MAX); + push_u16(&mut target, 0); + push_u16(&mut target, 0); + push_u16(&mut target, 0); + push_u16(&mut target, 4); + push_u16(&mut target, 2); + target.extend(std::iter::repeat_n(0x41, crate::SIGNATURE_BYTES)); + return (target, vec![(0, vec![0x42; key_size]), (2, vec![0x43, 0x44, 0x45, 0x46])]); + } + + fn secp256k1_current_payload(message: &[u8]) -> std::vec::Vec { + let table_end = 1 + crate::U8_OFFSETS_BYTES; + let signature_offset = table_end; + let address_offset = signature_offset + crate::SIGNATURE_BYTES + 1; + let message_offset = address_offset + crate::SECP256K1_ETHEREUM_ADDRESS_BYTES; + let mut bytes = vec![1]; + push_u16(&mut bytes, u16_value(signature_offset)); + bytes.push(1); + push_u16(&mut bytes, u16_value(address_offset)); + bytes.push(1); + push_u16(&mut bytes, u16_value(message_offset)); + push_u16(&mut bytes, u16_value(message.len())); + bytes.push(1); + bytes.extend(std::iter::repeat_n(0x51, crate::SIGNATURE_BYTES)); + bytes.push(2); + bytes.extend(std::iter::repeat_n(0x52, crate::SECP256K1_ETHEREUM_ADDRESS_BYTES)); + bytes.extend_from_slice(message); + return bytes; + } + + fn secp256k1_external_payload(message: &[u8]) -> (std::vec::Vec, std::vec::Vec) { + let signature_offset = 0; + let address_offset = crate::SIGNATURE_BYTES + 1; + let message_offset = address_offset + crate::SECP256K1_ETHEREUM_ADDRESS_BYTES; + let mut target = vec![1]; + push_u16(&mut target, u16_value(signature_offset)); + target.push(0); + push_u16(&mut target, u16_value(address_offset)); + target.push(0); + push_u16(&mut target, u16_value(message_offset)); + push_u16(&mut target, u16_value(message.len())); + target.push(0); + let mut external = vec![0x61; crate::SIGNATURE_BYTES]; + external.push(3); + external.extend(std::iter::repeat_n(0x62, crate::SECP256K1_ETHEREUM_ADDRESS_BYTES)); + external.extend_from_slice(message); + return (target, external); + } + + fn secp256k1_mixed_payload() -> (std::vec::Vec, std::vec::Vec<(usize, std::vec::Vec)>) { + let signature_offset = 1 + crate::U8_OFFSETS_BYTES; + let mut target = vec![1]; + push_u16(&mut target, u16_value(signature_offset)); + target.push(1); + push_u16(&mut target, 0); + target.push(0); + push_u16(&mut target, 0); + push_u16(&mut target, 4); + target.push(2); + target.extend(std::iter::repeat_n(0x71, crate::SIGNATURE_BYTES)); + target.push(1); + return ( + target, + vec![ + (0, vec![0x72; crate::SECP256K1_ETHEREUM_ADDRESS_BYTES]), + (2, vec![0x73, 0x74, 0x75, 0x76]), + ], + ); + } + + fn two_u16_entries(key_size: usize) -> std::vec::Vec { + let table_end = 2 + 2 * crate::U16_OFFSETS_BYTES; + let entry_size = crate::SIGNATURE_BYTES + key_size + 1; + let mut bytes = vec![2, 0]; + for index in 0..2 { + let signature_offset = table_end + index * entry_size; + let key_offset = signature_offset + crate::SIGNATURE_BYTES; + let message_offset = key_offset + key_size; + push_u16(&mut bytes, u16_value(signature_offset)); + push_u16(&mut bytes, u16::MAX); + push_u16(&mut bytes, u16_value(key_offset)); + push_u16(&mut bytes, u16::MAX); + push_u16(&mut bytes, u16_value(message_offset)); + push_u16(&mut bytes, 1); + push_u16(&mut bytes, u16::MAX); + } + for index in 0..2 { + bytes.extend(std::iter::repeat_n(0x80 + index as u8, crate::SIGNATURE_BYTES)); + bytes.extend(std::iter::repeat_n(0x90 + index as u8, key_size)); + bytes.push(index as u8); + } + return bytes; + } + + fn two_secp256k1_entries() -> std::vec::Vec { + let table_end = 1 + 2 * crate::U8_OFFSETS_BYTES; + let entry_size = crate::SIGNATURE_BYTES + 1 + crate::SECP256K1_ETHEREUM_ADDRESS_BYTES + 1; + let mut bytes = vec![2]; + for index in 0..2 { + let signature_offset = table_end + index * entry_size; + let address_offset = signature_offset + crate::SIGNATURE_BYTES + 1; + let message_offset = address_offset + crate::SECP256K1_ETHEREUM_ADDRESS_BYTES; + push_u16(&mut bytes, u16_value(signature_offset)); + bytes.push(1); + push_u16(&mut bytes, u16_value(address_offset)); + bytes.push(1); + push_u16(&mut bytes, u16_value(message_offset)); + push_u16(&mut bytes, 1); + bytes.push(1); + } + for index in 0..2 { + bytes.extend(std::iter::repeat_n(0xA0 + index as u8, crate::SIGNATURE_BYTES)); + bytes.push(index as u8); + bytes.extend(std::iter::repeat_n( + 0xB0 + index as u8, + crate::SECP256K1_ETHEREUM_ADDRESS_BYTES, + )); + bytes.push(index as u8); + } + return bytes; + } + + fn malformed_u16_index() -> std::vec::Vec { + let mut bytes = vec![1, 0]; + push_u16(&mut bytes, 0); + push_u16(&mut bytes, 9); + push_u16(&mut bytes, 0); + push_u16(&mut bytes, 9); + push_u16(&mut bytes, 0); + push_u16(&mut bytes, 0); + push_u16(&mut bytes, 9); + return bytes; + } + + fn malformed_k1_index() -> std::vec::Vec { + let mut bytes = vec![1]; + push_u16(&mut bytes, 0); + bytes.push(9); + push_u16(&mut bytes, 0); + bytes.push(9); + push_u16(&mut bytes, 0); + push_u16(&mut bytes, 0); + bytes.push(9); + return bytes; + } + + fn push_u16(bytes: &mut std::vec::Vec, value: u16) { + bytes.extend_from_slice(value.to_le_bytes().as_slice()); + } + + fn u16_value(value: usize) -> u16 { + return match u16::try_from(value) { + std::result::Result::Ok(converted) => converted, + std::result::Result::Err(error) => panic!("fixture offset does not fit u16: {error}"), + }; + } + + fn parameters(result: &kb_decoder_api::DecoderExecutionResult) -> &serde_json::Value { + return &result.observations[0].payload_json["parameters"]; + } + + #[test] + fn ed25519_current_reference_decodes_exact_component_sizes_and_padding() { + let input = replay_input( + kb_program_ids::ED25519_PROGRAM_ID, + u16_current_payload(crate::ED25519_PUBLIC_KEY_BYTES, b"message", 7), + vec![], + false, + ); + let result = crate::precompiles_decode(&input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(parameters(&result)["padding"], 7); + assert_eq!(parameters(&result)["signature"]["length"], 64); + assert_eq!(parameters(&result)["publicKey"]["length"], 32); + assert_eq!(parameters(&result)["provenance"]["message"], "current_instruction"); + } + + #[test] + fn secp256r1_current_reference_decodes_compressed_public_key() { + let input = replay_input( + kb_program_ids::SECP256R1_PROGRAM_ID, + u16_current_payload(crate::SECP256R1_PUBLIC_KEY_BYTES, b"r1", 11), + vec![], + false, + ); + let result = crate::precompiles_decode(&input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(parameters(&result)["padding"], 11); + assert_eq!(parameters(&result)["publicKey"]["length"], 33); + assert_eq!(parameters(&result)["signature"]["length"], 64); + } + + #[test] + fn secp256k1_current_reference_is_explicit_and_retains_recovery_id() { + let input = replay_input( + kb_program_ids::SECP256K1_PROGRAM_ID, + secp256k1_current_payload(b"keccak message"), + vec![], + false, + ); + let result = crate::precompiles_decode(&input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(parameters(&result)["recoveryId"], 2); + assert_eq!(parameters(&result)["ethereumAddress"]["length"], 20); + assert_eq!(parameters(&result)["rawInstructionIndexes"]["signatureInstructionIndex"], 1); + assert_eq!(parameters(&result)["provenance"]["signature"], "current_instruction"); + } + + #[test] + fn all_precompiles_resolve_external_outer_instruction_data() { + let (ed_target, ed_external) = + u16_external_payload(crate::ED25519_PUBLIC_KEY_BYTES, b"external"); + let (k1_target, k1_external) = secp256k1_external_payload(b"external"); + let (r1_target, r1_external) = + u16_external_payload(crate::SECP256R1_PUBLIC_KEY_BYTES, b"external"); + let cases = [ + (kb_program_ids::ED25519_PROGRAM_ID, ed_target, ed_external), + (kb_program_ids::SECP256K1_PROGRAM_ID, k1_target, k1_external), + (kb_program_ids::SECP256R1_PROGRAM_ID, r1_target, r1_external), + ]; + for (program_id, target, external) in cases { + let input = replay_input(program_id, target, vec![(0, external)], false); + let result = crate::precompiles_decode(&input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(parameters(&result)["provenance"]["message"], "outer_instruction"); + assert_eq!( + parameters(&result)["resolvedInstructionIndexes"]["messageInstructionIndex"], + 0 + ); + } + } + + #[test] + fn all_precompiles_support_mixed_component_references() { + let (ed_target, ed_external) = u16_mixed_payload(crate::ED25519_PUBLIC_KEY_BYTES); + let (k1_target, k1_external) = secp256k1_mixed_payload(); + let (r1_target, r1_external) = u16_mixed_payload(crate::SECP256R1_PUBLIC_KEY_BYTES); + let cases = [ + (kb_program_ids::ED25519_PROGRAM_ID, ed_target, ed_external), + (kb_program_ids::SECP256K1_PROGRAM_ID, k1_target, k1_external), + (kb_program_ids::SECP256R1_PROGRAM_ID, r1_target, r1_external), + ]; + for (program_id, target, external) in cases { + let input = replay_input(program_id, target, external, false); + let result = crate::precompiles_decode(&input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(parameters(&result)["provenance"]["signature"], "current_instruction"); + assert_eq!(parameters(&result)["provenance"]["message"], "outer_instruction"); + } + } + + #[test] + fn ed25519_and_secp256r1_decode_multiple_signatures() { + let cases = [ + ( + kb_program_ids::ED25519_PROGRAM_ID, + two_u16_entries(crate::ED25519_PUBLIC_KEY_BYTES), + ), + ( + kb_program_ids::SECP256R1_PROGRAM_ID, + two_u16_entries(crate::SECP256R1_PUBLIC_KEY_BYTES), + ), + ]; + for (program_id, bytes) in cases { + let input = replay_input(program_id, bytes, vec![], false); + let result = crate::precompiles_decode(&input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(result.observations.len(), 2); + assert_eq!(result.observations[1].event_key, "signature_verification:1"); + } + } + + #[test] + fn secp256k1_decodes_multiple_signatures() { + let input = replay_input( + kb_program_ids::SECP256K1_PROGRAM_ID, + two_secp256k1_entries(), + vec![], + false, + ); + let result = crate::precompiles_decode(&input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(result.observations.len(), 2); + assert_eq!(parameters(&result)["entryCount"], 2); + assert_eq!(result.observations[1].payload_json["parameters"]["recoveryId"], 1); + } + + #[test] + fn empty_and_large_messages_are_hashed_without_full_message_retention() { + let empty_input = replay_input( + kb_program_ids::ED25519_PROGRAM_ID, + u16_current_payload(crate::ED25519_PUBLIC_KEY_BYTES, b"", 0), + vec![], + false, + ); + let empty_result = crate::precompiles_decode(&empty_input); + assert_eq!(parameters(&empty_result)["message"]["length"], 0); + let large_message = vec![0xA5; 1_024]; + let large_input = replay_input( + kb_program_ids::SECP256K1_PROGRAM_ID, + secp256k1_current_payload(large_message.as_slice()), + vec![], + false, + ); + let large_result = crate::precompiles_decode(&large_input); + assert_eq!(parameters(&large_result)["message"]["length"], 1_024); + assert!(parameters(&large_result)["message"].get("bytes").is_none()); + assert_eq!( + parameters(&large_result)["message"]["sha256"], + crate::hash_bytes(large_message.as_slice()) + ); + } + + #[test] + fn official_zero_signature_rules_are_preserved() { + let ed_result = crate::precompiles_decode(&replay_input( + kb_program_ids::ED25519_PROGRAM_ID, + vec![0, 9], + vec![], + false, + )); + let k1_result = crate::precompiles_decode(&replay_input( + kb_program_ids::SECP256K1_PROGRAM_ID, + vec![0], + vec![], + false, + )); + let r1_result = crate::precompiles_decode(&replay_input( + kb_program_ids::SECP256R1_PROGRAM_ID, + vec![0, 0], + vec![], + false, + )); + assert_eq!(ed_result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(k1_result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(r1_result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + assert_eq!(parameters(&ed_result)["entryCount"], 0); + assert!(parameters(&k1_result).get("padding").is_none()); + } + + #[test] + fn header_and_offsets_truncation_fail_deterministically() { + let cases = [ + (kb_program_ids::ED25519_PROGRAM_ID, vec![1]), + (kb_program_ids::ED25519_PROGRAM_ID, vec![1, 0, 1]), + (kb_program_ids::SECP256K1_PROGRAM_ID, vec![]), + (kb_program_ids::SECP256K1_PROGRAM_ID, vec![1, 1]), + (kb_program_ids::SECP256R1_PROGRAM_ID, vec![1]), + (kb_program_ids::SECP256R1_PROGRAM_ID, vec![1, 0, 1]), + ]; + for (program_id, bytes) in cases { + let result = crate::precompiles_decode(&replay_input(program_id, bytes, vec![], false)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + assert_eq!(result.diagnostics.len(), 1); + } + } + + #[test] + fn inconsistent_zero_count_and_runtime_r1_count_limit_fail() { + let cases = [ + (kb_program_ids::ED25519_PROGRAM_ID, vec![0, 0, 1]), + (kb_program_ids::SECP256K1_PROGRAM_ID, vec![0, 1]), + (kb_program_ids::SECP256R1_PROGRAM_ID, vec![9, 0]), + ]; + for (program_id, bytes) in cases { + let result = crate::precompiles_decode(&replay_input(program_id, bytes, vec![], false)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + } + + #[test] + fn absent_instruction_indexes_fail_for_all_precompiles() { + let cases = [ + (kb_program_ids::ED25519_PROGRAM_ID, malformed_u16_index()), + (kb_program_ids::SECP256K1_PROGRAM_ID, malformed_k1_index()), + (kb_program_ids::SECP256R1_PROGRAM_ID, malformed_u16_index()), + ]; + for (program_id, bytes) in cases { + let result = crate::precompiles_decode(&replay_input(program_id, bytes, vec![], false)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + assert!(result.diagnostics[0].message.contains("signatureCount=1")); + assert!(result.diagnostics[0].message.contains("entryIndex=0")); + assert!(result.diagnostics[0].message.contains("payloadSha256=")); + } + } + + #[test] + fn out_of_bounds_offsets_fail_for_all_precompiles() { + let mut ed = u16_current_payload(crate::ED25519_PUBLIC_KEY_BYTES, b"x", 0); + ed[2] = 0xFF; + ed[3] = 0xFF; + let mut k1 = secp256k1_current_payload(b"x"); + k1[1] = 0xFF; + k1[2] = 0xFF; + let mut r1 = u16_current_payload(crate::SECP256R1_PUBLIC_KEY_BYTES, b"x", 0); + r1[10] = 0xFF; + r1[11] = 0xFF; + let cases = [ + (kb_program_ids::ED25519_PROGRAM_ID, ed), + (kb_program_ids::SECP256K1_PROGRAM_ID, k1), + (kb_program_ids::SECP256R1_PROGRAM_ID, r1), + ]; + for (program_id, bytes) in cases { + let result = crate::precompiles_decode(&replay_input(program_id, bytes, vec![], false)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + } + + #[test] + fn out_of_bounds_message_lengths_fail_for_all_precompiles() { + let mut ed = u16_current_payload(crate::ED25519_PUBLIC_KEY_BYTES, b"x", 0); + ed[12] = 0xFF; + ed[13] = 0xFF; + let mut k1 = secp256k1_current_payload(b"x"); + k1[9] = 0xFF; + k1[10] = 0xFF; + let mut r1 = u16_current_payload(crate::SECP256R1_PUBLIC_KEY_BYTES, b"x", 0); + r1[12] = 0xFF; + r1[13] = 0xFF; + let cases = [ + (kb_program_ids::ED25519_PROGRAM_ID, ed), + (kb_program_ids::SECP256K1_PROGRAM_ID, k1), + (kb_program_ids::SECP256R1_PROGRAM_ID, r1), + ]; + for (program_id, bytes) in cases { + let result = crate::precompiles_decode(&replay_input(program_id, bytes, vec![], false)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + } + + #[test] + fn checked_slice_rejects_offset_length_addition_overflow() { + let result = crate::bounded_slice(&[1, 2, 3], usize::MAX, 1, "overflow test"); + assert!(result.is_err()); + } + + #[test] + fn missing_or_invalid_external_base64_fails_for_all_precompiles() { + let (ed_target, ed_external) = + u16_external_payload(crate::ED25519_PUBLIC_KEY_BYTES, b"external"); + let (k1_target, k1_external) = secp256k1_external_payload(b"external"); + let (r1_target, r1_external) = + u16_external_payload(crate::SECP256R1_PUBLIC_KEY_BYTES, b"external"); + let cases = [ + (kb_program_ids::ED25519_PROGRAM_ID, ed_target, ed_external), + (kb_program_ids::SECP256K1_PROGRAM_ID, k1_target, k1_external), + (kb_program_ids::SECP256R1_PROGRAM_ID, r1_target, r1_external), + ]; + for (program_id, target, external) in cases { + let mut missing = + replay_input(program_id, target.clone(), vec![(0, external.clone())], false); + missing.outer_instructions_json[0]["payloadJson"] = serde_json::Value::Null; + let missing_result = crate::precompiles_decode(&missing); + assert_eq!(missing_result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let mut invalid = replay_input(program_id, target, vec![(0, external)], false); + invalid.outer_instructions_json[0]["payloadJson"] = + serde_json::json!({"dataBase64": "%%%"}); + let invalid_result = crate::precompiles_decode(&invalid); + assert_eq!(invalid_result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + } + + #[test] + fn secp256k1_treats_u8_max_as_an_explicit_outer_index_not_a_sentinel() { + let mut bytes = secp256k1_current_payload(b"explicit-index"); + bytes[3] = u8::MAX; + bytes[6] = u8::MAX; + bytes[11] = u8::MAX; + let result = crate::precompiles_decode(&replay_input( + kb_program_ids::SECP256K1_PROGRAM_ID, + bytes, + vec![], + false, + )); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + assert!(result.diagnostics[0].message.contains("outer instruction index is absent: 255")); + } + + #[test] + fn failed_transaction_is_decoded_without_commit_or_runtime_validity_claim() { + let input = replay_input( + kb_program_ids::ED25519_PROGRAM_ID, + u16_current_payload(crate::ED25519_PUBLIC_KEY_BYTES, b"failed", 0), + vec![], + true, + ); + let result = crate::precompiles_decode(&input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!result.observations[0].observation_committed); + assert_eq!(parameters(&result)["runtimeVerification"], "not_asserted_transaction_failed"); + assert_eq!(parameters(&result)["cryptographicReverificationPerformed"], false); + } + + #[test] + fn event_kind_and_serialization_are_stable() { + let input = replay_input( + kb_program_ids::SECP256R1_PROGRAM_ID, + u16_current_payload(crate::SECP256R1_PUBLIC_KEY_BYTES, b"stable", 0), + vec![], + false, + ); + let first = crate::precompiles_decode(&input); + let second = crate::precompiles_decode(&input); + assert_eq!(first, second); + assert_eq!( + first.observations[0].event.event_code.0, + "solana_native_secp256r1.signature_verification" + ); + assert_eq!(parameters(&first)["runtimeVerification"], "accepted_in_successful_transaction"); + let decoded_bytes = match crate::decode_instruction_data(&input) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected payload error: {error}"), + }; + assert_eq!( + first.observations[0].payload_json["instructionDataSha256"], + crate::hash_bytes(decoded_bytes.as_slice()) + ); + let first_json_result = serde_json::to_string(&first); + let first_json = match first_json_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected serialization error: {error}"), + }; + let second_json_result = serde_json::to_string(&second); + let second_json = match second_json_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected serialization error: {error}"), + }; + assert_eq!(first_json, second_json); + } + + #[test] + fn coverage_declares_exactly_three_signature_precompile_surfaces() { + let coverage = crate::precompiles_coverage(); + assert_eq!(coverage.len(), 3); + assert!(coverage.iter().all(|entry| return entry.entry_code == "signature_verification")); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/slashing.rs b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/slashing.rs new file mode 100644 index 0000000..2753362 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/slashing.rs @@ -0,0 +1,654 @@ +// file: kb_decoder_solana_core/src/slashing.rs +// version: 2 + +//! Exact structural decoding for the enshrined stateless Slashing Program. + +const SOURCE: &str = "solana-program/slashing@fe8da3a instruction contract and Agave v4.1.1 stateless builtin verified build"; +const DUPLICATE_BLOCK_PROOF_DATA_BYTES: usize = 304; +const DUPLICATE_BLOCK_PROOF_INSTRUCTION_BYTES: usize = 305; +const SIGNATURE_BYTES: usize = 64; +const HASH_BYTES: usize = 32; +const PUBKEY_BYTES: usize = 32; +const CLOSE_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("violation_report", true, false), + crate::AccountRole::new("destination", true, false), +]; +const DUPLICATE_BLOCK_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("proof_account", false, false), + crate::AccountRole::new("violation_report", true, false), + crate::AccountRole::new("instructions_sysvar", false, false), + crate::AccountRole::new("system_program", false, false), +]; + +/// Returns declared Slashing Program instruction coverage. +pub(crate) fn slashing_coverage() -> std::vec::Vec { + return vec![ + kb_decoder_api::DecoderCoverageDeclaration { + program_id: kb_program_ids::SLASHING_PROGRAM_ID.to_string(), + surface_code: std::option::Option::Some(crate::SLASHING_SURFACE_CODE.to_string()), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: "close_violation_report".to_string(), + discriminator_hex: std::option::Option::Some("00".to_string()), + historical: false, + }, + kb_decoder_api::DecoderCoverageDeclaration { + program_id: kb_program_ids::SLASHING_PROGRAM_ID.to_string(), + surface_code: std::option::Option::Some(crate::SLASHING_SURFACE_CODE.to_string()), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: "duplicate_block_proof".to_string(), + discriminator_hex: std::option::Option::Some("01".to_string()), + historical: false, + }, + ]; +} + +/// Recognizes one Slashing Program instruction without producing an event. +pub(crate) fn slashing_recognize( + input: &kb_store_core::CoreInstructionReplayInput, + priority: u16, +) -> kb_decoder_api::DecoderRecognition { + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return kb_decoder_api::DecoderRecognition::compatible( + false, + priority, + std::option::Option::Some(crate::SLASHING_SURFACE_CODE.to_string()), + std::option::Option::Some("malformed_slashing_instruction".to_string()), + std::option::Option::None, + ); + }, + }; + let (entry_code, exact) = match bytes.first() { + std::option::Option::Some(0) => ("close_violation_report", bytes.len() == 1), + std::option::Option::Some(1) => { + ("duplicate_block_proof", bytes.len() == DUPLICATE_BLOCK_PROOF_INSTRUCTION_BYTES) + }, + std::option::Option::Some(_value) => ("unknown_slashing_instruction", false), + std::option::Option::None => ("malformed_slashing_instruction", false), + }; + return kb_decoder_api::DecoderRecognition::compatible( + exact, + priority, + std::option::Option::Some(crate::SLASHING_SURFACE_CODE.to_string()), + std::option::Option::Some(entry_code.to_string()), + crate::hexadecimal_prefix(bytes.as_slice(), bytes.len().min(1)), + ); +} + +/// Decodes one Slashing Program instruction. +pub(crate) fn slashing_decode( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_decoder_api::DecoderExecutionResult { + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("malformed_slashing_instruction"), + "slashing_payload_invalid", + error.to_string(), + ); + }, + }; + let tag = match bytes.first() { + std::option::Option::Some(value) => *value, + std::option::Option::None => { + return crate::failed_result( + std::option::Option::Some("malformed_slashing_instruction"), + "slashing_payload_empty", + "Slashing Program instruction payload is empty", + ); + }, + }; + return match tag { + 0 => decode_close_violation_report(input, bytes.as_slice()), + 1 => decode_duplicate_block_proof(input, bytes.as_slice()), + _ => crate::unsupported_result( + "unknown_slashing_instruction", + "slashing_tag_unknown", + format!( + "unknown Slashing Program instruction tag {tag}; payload_sha256={}", + crate::payload_hash(input) + ), + ), + }; +} + +fn decode_close_violation_report( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], +) -> kb_decoder_api::DecoderExecutionResult { + if bytes.len() != 1 { + return crate::failed_result( + std::option::Option::Some("close_violation_report"), + "slashing_close_size_invalid", + format!("close_violation_report requires exactly 1 byte but received {}", bytes.len()), + ); + } + let accounts_result = crate::resolve_accounts(input, CLOSE_ROLES, 2, std::option::Option::None); + let accounts = match accounts_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("close_violation_report"), + "slashing_close_accounts_invalid", + error.to_string(), + ); + }, + }; + return crate::decoded_result( + input, + crate::SLASHING_SURFACE_CODE, + "close_violation_report", + kb_model::EventFamily::Lifecycle, + false, + accounts, + serde_json::json!({ + "proofType": "duplicate_block", + "minimumRetentionEpochs": 3, + "runtimeMutation": runtime_mutation( + input, + "violation_report_closed_lamports_transferred_owner_reset_to_system_program", + ), + "transactionFinalAccountStateCaptured": false, + }), + SOURCE, + ); +} + +fn decode_duplicate_block_proof( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], +) -> kb_decoder_api::DecoderExecutionResult { + if bytes.len() != DUPLICATE_BLOCK_PROOF_INSTRUCTION_BYTES { + return crate::failed_result( + std::option::Option::Some("duplicate_block_proof"), + "slashing_duplicate_block_size_invalid", + format!( + "duplicate_block_proof requires {} bytes but received {}", + DUPLICATE_BLOCK_PROOF_INSTRUCTION_BYTES, + bytes.len() + ), + ); + } + let accounts_result = + crate::resolve_accounts(input, DUPLICATE_BLOCK_ROLES, 4, std::option::Option::None); + let accounts = match accounts_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("duplicate_block_proof"), + "slashing_duplicate_block_accounts_invalid", + error.to_string(), + ); + }, + }; + let fixed_accounts_result = validate_duplicate_block_fixed_accounts(&accounts); + if let std::result::Result::Err(error) = fixed_accounts_result { + return crate::failed_result( + std::option::Option::Some("duplicate_block_proof"), + "slashing_duplicate_block_fixed_account_invalid", + error.to_string(), + ); + } + let parsed_result = parse_duplicate_block_parameters(input, bytes); + let parameters = match parsed_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("duplicate_block_proof"), + "slashing_duplicate_block_data_invalid", + error.to_string(), + ); + }, + }; + return crate::decoded_result( + input, + crate::SLASHING_SURFACE_CODE, + "duplicate_block_proof", + kb_model::EventFamily::Audit, + false, + accounts, + parameters, + SOURCE, + ); +} + +fn parse_duplicate_block_parameters( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], +) -> kb_core::Result { + let offset_result = crate::read_u64_le(bytes, 1); + let offset = match offset_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let slot_result = crate::read_u64_le(bytes, 9); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let node_pubkey_result = read_pubkey(bytes, 17, "slashing node pubkey"); + let node_pubkey = match node_pubkey_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let reporter_result = read_pubkey(bytes, 49, "slashing reporter"); + let reporter = match reporter_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let destination_result = read_pubkey(bytes, 81, "slashing destination"); + let destination = match destination_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let shred_1_root_result = + crate::bounded_slice(bytes, 113, HASH_BYTES, "slashing first shred merkle root"); + let shred_1_root = match shred_1_root_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let shred_1_signature_result = + crate::bounded_slice(bytes, 145, SIGNATURE_BYTES, "slashing first shred signature"); + let shred_1_signature = match shred_1_signature_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let shred_2_root_result = + crate::bounded_slice(bytes, 209, HASH_BYTES, "slashing second shred merkle root"); + let shred_2_root = match shred_2_root_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let shred_2_signature_result = + crate::bounded_slice(bytes, 241, SIGNATURE_BYTES, "slashing second shred signature"); + let shred_2_signature = match shred_2_signature_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(serde_json::json!({ + "proofType": "duplicate_block", + "proofAccountOffset": offset, + "violationSlot": slot, + "nodePubkey": node_pubkey, + "reporter": reporter, + "destination": destination, + "shred1MerkleRoot": hash_summary(shred_1_root), + "shred1Signature": signature_summary(shred_1_signature), + "shred2MerkleRoot": hash_summary(shred_2_root), + "shred2Signature": signature_summary(shred_2_signature), + "proofAccountDataCaptured": false, + "proofAccountDataSemantics": "external_account_not_captured_by_transaction_core", + "reportAccountMustBePrefunded": true, + "prefundingProvenanceInspectedByDecoder": false, + "violationReportPdaDerivationVerifiedByDecoder": false, + "precedingEd25519Instruction": preceding_ed25519_instruction(input), + "cryptographicVerificationPerformedByDecoder": false, + "runtimeVerification": if input.transaction_failed { + "not_asserted_transaction_failed" + } else { + "accepted_in_successful_transaction" + }, + "runtimeMutation": runtime_mutation( + input, + "violation_report_pda_assigned_allocated_and_data_stored_after_duplicate_block_proof_acceptance", + ), + "penaltyAppliedByProgram": false, + "programSemantics": "records_verified_violation_report_for_external_consensus_enforcement", + "instructionDataBytes": DUPLICATE_BLOCK_PROOF_DATA_BYTES, + })); +} + +fn validate_duplicate_block_fixed_accounts(accounts: &serde_json::Value) -> kb_core::Result<()> { + let array = match accounts.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "resolved Slashing Program accounts must be an array", + )); + }, + }; + for (position, expected) in [ + (2_usize, kb_program_ids::SYSVAR_INSTRUCTIONS_PROGRAM_ID), + (3_usize, kb_program_ids::SYSTEM_PROGRAM_ID), + ] { + let actual = array + .get(position) + .and_then(|value| return value.get("accountKey")) + .and_then(serde_json::Value::as_str); + if actual != std::option::Option::Some(expected) { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Slashing Program account {position} must be {expected}" + ))); + } + } + return std::result::Result::Ok(()); +} + +fn read_pubkey(bytes: &[u8], offset: usize, label: &str) -> kb_core::Result { + let slice_result = crate::bounded_slice(bytes, offset, PUBKEY_BYTES, label); + return match slice_result { + std::result::Result::Ok(value) => { + std::result::Result::Ok(bs58::encode(value).into_string()) + }, + std::result::Result::Err(error) => std::result::Result::Err(error), + }; +} + +fn hash_summary(bytes: &[u8]) -> serde_json::Value { + return serde_json::json!({ + "length": bytes.len(), + "hex": crate::bounded_hexadecimal_prefix(bytes, bytes.len()), + "sha256": crate::hash_bytes(bytes), + }); +} + +fn signature_summary(bytes: &[u8]) -> serde_json::Value { + return serde_json::json!({ + "length": bytes.len(), + "prefixHex": crate::bounded_hexadecimal_prefix( + bytes, + crate::PRECOMPILE_COMPONENT_PREFIX_BYTES, + ), + "sha256": crate::hash_bytes(bytes), + }); +} + +fn preceding_ed25519_instruction( + input: &kb_store_core::CoreInstructionReplayInput, +) -> serde_json::Value { + let target_index = match crate::target_outer_instruction_index(input) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return serde_json::json!({ + "requiredRelativeInstructionOffset": -1, + "expectedProgramId": kb_program_ids::ED25519_PROGRAM_ID, + "resolved": false, + "matchesExpectedProgram": false, + "diagnostic": error.to_string(), + }); + }, + }; + let previous_index = match target_index.checked_sub(1) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return serde_json::json!({ + "requiredRelativeInstructionOffset": -1, + "currentInstructionIndex": target_index, + "expectedProgramId": kb_program_ids::ED25519_PROGRAM_ID, + "resolved": false, + "matchesExpectedProgram": false, + "diagnostic": "target instruction has no preceding outer instruction", + }); + }, + }; + let previous = input.outer_instructions_json.as_array().and_then(|instructions| { + return instructions.iter().find(|instruction| { + return instruction + .get("instructionIndex") + .and_then(serde_json::Value::as_u64) + .and_then(|value| return usize::try_from(value).ok()) + == std::option::Option::Some(previous_index); + }); + }); + let program_id = previous + .and_then(|instruction| return instruction.get("programId")) + .and_then(serde_json::Value::as_str); + let instruction_path = previous + .and_then(|instruction| return instruction.get("instructionPath")) + .and_then(serde_json::Value::as_str); + return serde_json::json!({ + "requiredRelativeInstructionOffset": -1, + "currentInstructionIndex": target_index, + "resolvedInstructionIndex": previous_index, + "resolvedInstructionPath": instruction_path, + "expectedProgramId": kb_program_ids::ED25519_PROGRAM_ID, + "resolvedProgramId": program_id, + "resolved": previous.is_some(), + "matchesExpectedProgram": program_id == std::option::Option::Some(kb_program_ids::ED25519_PROGRAM_ID), + "expectedSignatureCount": 2, + "signatureTableInspectedByDecoder": false, + }); +} + +fn runtime_mutation( + input: &kb_store_core::CoreInstructionReplayInput, + successful_value: &'static str, +) -> &'static str { + return if input.transaction_failed { + "not_asserted_transaction_failed" + } else { + successful_value + }; +} + +#[cfg(test)] +mod tests { + use base64::Engine; // rust-rules: trait-import + + fn duplicate_payload() -> std::vec::Vec { + let mut bytes = vec![1_u8]; + bytes.extend_from_slice(&34_u64.to_le_bytes()); + bytes.extend_from_slice(&42_u64.to_le_bytes()); + bytes.extend_from_slice(&[1_u8; 32]); + bytes.extend_from_slice(&[2_u8; 32]); + bytes.extend_from_slice(&[3_u8; 32]); + bytes.extend_from_slice(&[4_u8; 32]); + bytes.extend_from_slice(&[5_u8; 64]); + bytes.extend_from_slice(&[6_u8; 32]); + bytes.extend_from_slice(&[7_u8; 64]); + return bytes; + } + + fn replay_input( + bytes: &[u8], + transaction_failed: bool, + instruction_path: &str, + ) -> kb_store_core::CoreInstructionReplayInput { + let keys = [ + "Proof1111111111111111111111111111111111111", + "Report111111111111111111111111111111111111", + kb_program_ids::SYSVAR_INSTRUCTIONS_PROGRAM_ID, + kb_program_ids::SYSTEM_PROGRAM_ID, + ]; + let account_keys = keys + .iter() + .enumerate() + .map(|(index, key)| { + return serde_json::json!({ + "accountIndex": index, + "accountKey": key, + "source": "static", + "writable": index == 1, + "signer": false, + "executable": index == 3, + }); + }) + .collect::>(); + let instruction_accounts = keys + .iter() + .enumerate() + .map(|(index, key)| { + return serde_json::json!({"accountIndex": index, "accountKey": key}); + }) + .collect::>(); + let current_index = instruction_path.parse::().unwrap_or(2); + let previous_index = current_index.saturating_sub(1); + let outer = serde_json::json!([ + { + "instructionIndex": previous_index, + "instructionPath": previous_index.to_string(), + "programId": kb_program_ids::ED25519_PROGRAM_ID, + "payloadJson": {"dataBase64": ""}, + "payloadHash": "ed25519-hash" + }, + { + "instructionIndex": current_index, + "instructionPath": instruction_path, + "programId": kb_program_ids::SLASHING_PROGRAM_ID, + "payloadJson": { + "dataBase64": base64::engine::general_purpose::STANDARD.encode(bytes), + }, + "payloadHash": "slashing-hash" + } + ]); + let result = kb_store_core::CoreInstructionReplayInput::new( + format!("signature:{instruction_path}"), + "signature", + 42, + instruction_path, + kb_program_ids::SLASHING_PROGRAM_ID, + transaction_failed, + if transaction_failed { + std::option::Option::Some(serde_json::json!({"InstructionError": [2, "Custom"]})) + } else { + std::option::Option::None + }, + serde_json::Value::Array(account_keys), + serde_json::Value::Array(instruction_accounts), + std::option::Option::Some(serde_json::json!({ + "dataBase64": base64::engine::general_purpose::STANDARD.encode(bytes), + })), + std::option::Option::Some("payload-hash".to_string()), + outer, + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Slashing replay input failed: {error}"), + }; + } + + fn close_replay_input(transaction_failed: bool) -> kb_store_core::CoreInstructionReplayInput { + let mut input = replay_input(&[0], transaction_failed, "0"); + input.account_keys_json = serde_json::json!([ + { + "accountIndex": 0, + "accountKey": "Report111111111111111111111111111111111111", + "source": "static", + "writable": true, + "signer": false, + "executable": false + }, + { + "accountIndex": 1, + "accountKey": "Destination11111111111111111111111111111111", + "source": "static", + "writable": true, + "signer": false, + "executable": false + } + ]); + input.instruction_accounts_json = serde_json::json!([ + {"accountIndex": 0, "accountKey": "Report111111111111111111111111111111111111"}, + {"accountIndex": 1, "accountKey": "Destination11111111111111111111111111111111"} + ]); + return input; + } + + #[test] + fn coverage_declares_exactly_two_official_instructions() { + let coverage = crate::slashing_coverage(); + assert_eq!(coverage.len(), 2); + assert_eq!(coverage[0].entry_code, "close_violation_report"); + assert_eq!(coverage[1].entry_code, "duplicate_block_proof"); + } + + #[test] + fn close_violation_report_decodes_exact_runtime_contract() { + let result = crate::slashing_decode(&close_replay_input(false)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some("close_violation_report") + ); + let parameters = &result.observations[0].payload_json["parameters"]; + assert_eq!(parameters["minimumRetentionEpochs"], 3); + assert_eq!( + parameters["runtimeMutation"], + "violation_report_closed_lamports_transferred_owner_reset_to_system_program" + ); + } + + #[test] + fn duplicate_block_proof_decodes_exact_official_layout() { + let payload = duplicate_payload(); + assert_eq!(payload.len(), super::DUPLICATE_BLOCK_PROOF_INSTRUCTION_BYTES); + let result = crate::slashing_decode(&replay_input(payload.as_slice(), false, "2")); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + let parameters = &result.observations[0].payload_json["parameters"]; + assert_eq!(parameters["proofAccountOffset"], 34); + assert_eq!(parameters["violationSlot"], 42); + assert_eq!(parameters["shred1Signature"]["length"], 64); + assert_eq!(parameters["shred2MerkleRoot"]["length"], 32); + assert_eq!(parameters["proofAccountDataCaptured"], false); + assert_eq!(parameters["reportAccountMustBePrefunded"], true); + assert_eq!(parameters["prefundingProvenanceInspectedByDecoder"], false); + assert_eq!(parameters["violationReportPdaDerivationVerifiedByDecoder"], false); + assert_eq!(parameters["cryptographicVerificationPerformedByDecoder"], false); + assert_eq!(parameters["penaltyAppliedByProgram"], false); + } + + #[test] + fn duplicate_block_proof_reports_preceding_ed25519_context() { + let result = + crate::slashing_decode(&replay_input(duplicate_payload().as_slice(), false, "2")); + let preceding = + &result.observations[0].payload_json["parameters"]["precedingEd25519Instruction"]; + assert_eq!(preceding["resolvedInstructionIndex"], 1); + assert_eq!(preceding["matchesExpectedProgram"], true); + assert_eq!(preceding["expectedSignatureCount"], 2); + assert_eq!(preceding["signatureTableInspectedByDecoder"], false); + } + + #[test] + fn malformed_unknown_and_trailing_payloads_fail_safely() { + for bytes in [vec![], vec![0, 7], vec![1, 2, 3]] { + let result = crate::slashing_decode(&replay_input(bytes.as_slice(), false, "2")); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + let unknown = crate::slashing_decode(&replay_input(&[9], false, "2")); + assert_eq!(unknown.status, kb_decoder_api::DecoderOutcomeStatus::Unsupported); + } + + #[test] + fn wrong_fixed_accounts_fail_safely() { + let mut input = replay_input(duplicate_payload().as_slice(), false, "2"); + input.instruction_accounts_json[2]["accountKey"] = serde_json::json!("wrong"); + input.account_keys_json[2]["accountKey"] = serde_json::json!("wrong"); + let result = crate::slashing_decode(&input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn failed_transaction_is_decoded_without_runtime_or_mutation_claim() { + let result = + crate::slashing_decode(&replay_input(duplicate_payload().as_slice(), true, "2")); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!result.observations[0].observation_committed); + let parameters = &result.observations[0].payload_json["parameters"]; + assert_eq!(parameters["runtimeVerification"], "not_asserted_transaction_failed"); + assert_eq!(parameters["runtimeMutation"], "not_asserted_transaction_failed"); + } + + #[test] + fn serialization_is_deterministic() { + let input = replay_input(duplicate_payload().as_slice(), false, "2"); + let first = crate::slashing_decode(&input); + let second = crate::slashing_decode(&input); + let first_json = match serde_json::to_string(&first) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("first serialization failed: {error}"), + }; + let second_json = match serde_json::to_string(&second) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("second serialization failed: {error}"), + }; + assert_eq!(first_json, second_json); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/stake.rs b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/stake.rs new file mode 100644 index 0000000..9b2fe04 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/stake.rs @@ -0,0 +1,1327 @@ +// file: kb_decoder_solana_core/src/stake.rs +// version: 4 + +//! Exact Stake Program instruction decoding through a bounded wincode-compatible wire mirror. + +const SOURCE: &str = "solana-stake-interface@4.3.x StakeInstruction contract plus current BPF Stake dual-layout processor"; + +#[derive(Clone, Debug, Eq, PartialEq, wincode::SchemaRead, wincode::SchemaWrite)] +enum StakeInstructionWire { + Initialize(solana_stake_interface::state::Authorized, solana_stake_interface::state::Lockup), + Authorize([u8; 32], solana_stake_interface::state::StakeAuthorize), + DelegateStake, + Split(u64), + Withdraw(u64), + Deactivate, + SetLockup(LockupArgsWire), + Merge, + AuthorizeWithSeed(AuthorizeWithSeedArgsWire), + InitializeChecked, + AuthorizeChecked(solana_stake_interface::state::StakeAuthorize), + AuthorizeCheckedWithSeed(AuthorizeCheckedWithSeedArgsWire), + SetLockupChecked(LockupCheckedArgsWire), + GetMinimumDelegation, + DeactivateDelinquent, + Redelegate, + MoveStake(u64), + MoveLamports(u64), +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, wincode::SchemaRead, wincode::SchemaWrite)] +struct LockupArgsWire { + unix_timestamp: std::option::Option, + epoch: std::option::Option, + custodian: std::option::Option<[u8; 32]>, +} + +#[derive(Clone, Debug, Eq, PartialEq, wincode::SchemaRead, wincode::SchemaWrite)] +struct AuthorizeWithSeedArgsWire { + new_authorized_pubkey: [u8; 32], + stake_authorize: solana_stake_interface::state::StakeAuthorize, + authority_seed: std::string::String, + authority_owner: [u8; 32], +} + +#[derive(Clone, Debug, Eq, PartialEq, wincode::SchemaRead, wincode::SchemaWrite)] +struct AuthorizeCheckedWithSeedArgsWire { + stake_authorize: solana_stake_interface::state::StakeAuthorize, + authority_seed: std::string::String, + authority_owner: [u8; 32], +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, wincode::SchemaRead, wincode::SchemaWrite)] +struct LockupCheckedArgsWire { + unix_timestamp: std::option::Option, + epoch: std::option::Option, +} + +struct AccountContract { + roles: std::vec::Vec, + minimum_count: usize, + account_layout: &'static str, +} + +/// Returns declared Stake Program instruction coverage. +pub(crate) fn stake_coverage() -> std::vec::Vec { + let entries = [ + ("initialize", 0_u32, false), + ("authorize", 1_u32, false), + ("delegate_stake", 2_u32, false), + ("split", 3_u32, false), + ("withdraw", 4_u32, false), + ("deactivate", 5_u32, false), + ("set_lockup", 6_u32, false), + ("merge", 7_u32, false), + ("authorize_with_seed", 8_u32, false), + ("initialize_checked", 9_u32, false), + ("authorize_checked", 10_u32, false), + ("authorize_checked_with_seed", 11_u32, false), + ("set_lockup_checked", 12_u32, false), + ("get_minimum_delegation", 13_u32, false), + ("deactivate_delinquent", 14_u32, false), + ("redelegate", 15_u32, true), + ("move_stake", 16_u32, false), + ("move_lamports", 17_u32, false), + ]; + return entries + .into_iter() + .map(|(entry_code, tag, historical)| { + return kb_decoder_api::DecoderCoverageDeclaration { + program_id: kb_program_ids::STAKE_PROGRAM_ID.to_string(), + surface_code: std::option::Option::Some(crate::STAKE_SURFACE_CODE.to_string()), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: entry_code.to_string(), + discriminator_hex: std::option::Option::Some(tag_hex(tag)), + historical, + }; + }) + .collect(); +} + +/// Recognizes one Stake Program instruction without producing an event. +pub(crate) fn stake_recognize( + input: &kb_store_core::CoreInstructionReplayInput, + priority: u16, +) -> kb_decoder_api::DecoderRecognition { + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return kb_decoder_api::DecoderRecognition::compatible( + false, + priority, + std::option::Option::Some(crate::STAKE_SURFACE_CODE.to_string()), + std::option::Option::Some("malformed_stake_instruction".to_string()), + std::option::Option::None, + ); + }, + }; + let tag_result = crate::read_u32_le(bytes.as_slice(), 0); + let tag = match tag_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return kb_decoder_api::DecoderRecognition::compatible( + false, + priority, + std::option::Option::Some(crate::STAKE_SURFACE_CODE.to_string()), + std::option::Option::Some("malformed_stake_instruction".to_string()), + crate::hexadecimal_prefix(bytes.as_slice(), bytes.len().min(4)), + ); + }, + }; + let entry_code = entry_code(tag); + return kb_decoder_api::DecoderRecognition::compatible( + entry_code.is_some(), + priority, + std::option::Option::Some(crate::STAKE_SURFACE_CODE.to_string()), + entry_code.map(str::to_string).or_else(|| { + return std::option::Option::Some("unknown_stake_instruction".to_string()); + }), + std::option::Option::Some(tag_hex(tag)), + ); +} + +/// Decodes one Stake Program instruction. +pub(crate) fn stake_decode( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_decoder_api::DecoderExecutionResult { + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("malformed_stake_instruction"), + "stake_payload_invalid", + error.to_string(), + ); + }, + }; + let tag_result = crate::read_u32_le(bytes.as_slice(), 0); + let tag = match tag_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("malformed_stake_instruction"), + "stake_tag_truncated", + error.to_string(), + ); + }, + }; + let entry_code = match entry_code(tag) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return crate::unsupported_result( + "unknown_stake_instruction", + "stake_tag_unknown", + format!( + "unknown Stake Program instruction tag {tag}; payload_sha256={}", + crate::payload_hash(input) + ), + ); + }, + }; + let instruction_result = wincode::deserialize_exact::(bytes.as_slice()); + let instruction = match instruction_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some(entry_code), + "stake_wincode_invalid", + format!("cannot decode exact Stake Program {entry_code}: {error}"), + ); + }, + }; + return decoded_instruction(input, instruction); +} + +fn decoded_instruction( + input: &kb_store_core::CoreInstructionReplayInput, + instruction: StakeInstructionWire, +) -> kb_decoder_api::DecoderExecutionResult { + return match instruction { + StakeInstructionWire::Initialize(authorized, lockup) => build( + input, + "initialize", + kb_model::EventFamily::Lifecycle, + false, + contract(input, "initialize"), + serde_json::json!({ + "authorized": authorized_json(authorized), + "lockup": lockup_json(lockup), + }), + ), + StakeInstructionWire::Authorize(new_authority, authorization_type) => build( + input, + "authorize", + kb_model::EventFamily::Admin, + false, + contract(input, "authorize"), + serde_json::json!({ + "newAuthority": pubkey_string(new_authority), + "authorizationType": stake_authorize_code(authorization_type), + }), + ), + StakeInstructionWire::DelegateStake => build( + input, + "delegate_stake", + kb_model::EventFamily::Staking, + false, + contract(input, "delegate_stake"), + serde_json::json!({}), + ), + StakeInstructionWire::Split(lamports) => build( + input, + "split", + kb_model::EventFamily::Staking, + false, + contract(input, "split"), + serde_json::json!({"lamports": lamports}), + ), + StakeInstructionWire::Withdraw(lamports) => build( + input, + "withdraw", + kb_model::EventFamily::Staking, + false, + contract(input, "withdraw"), + serde_json::json!({"lamports": lamports}), + ), + StakeInstructionWire::Deactivate => build( + input, + "deactivate", + kb_model::EventFamily::Staking, + false, + contract(input, "deactivate"), + serde_json::json!({}), + ), + StakeInstructionWire::SetLockup(value) => build( + input, + "set_lockup", + kb_model::EventFamily::Admin, + false, + contract(input, "set_lockup"), + lockup_args_json(value), + ), + StakeInstructionWire::Merge => build( + input, + "merge", + kb_model::EventFamily::Staking, + false, + contract(input, "merge"), + serde_json::json!({}), + ), + StakeInstructionWire::AuthorizeWithSeed(value) => build( + input, + "authorize_with_seed", + kb_model::EventFamily::Admin, + false, + contract(input, "authorize_with_seed"), + serde_json::json!({ + "newAuthorizedPubkey": pubkey_string(value.new_authorized_pubkey), + "authorizationType": stake_authorize_code(value.stake_authorize), + "authoritySeed": value.authority_seed, + "authorityOwner": pubkey_string(value.authority_owner), + }), + ), + StakeInstructionWire::InitializeChecked => build( + input, + "initialize_checked", + kb_model::EventFamily::Lifecycle, + false, + contract(input, "initialize_checked"), + serde_json::json!({ + "authorizedFromAccounts": true, + "lockup": "default", + }), + ), + StakeInstructionWire::AuthorizeChecked(authorization_type) => build( + input, + "authorize_checked", + kb_model::EventFamily::Admin, + false, + contract(input, "authorize_checked"), + serde_json::json!({ + "authorizationType": stake_authorize_code(authorization_type), + "newAuthorityFromAccount": true, + }), + ), + StakeInstructionWire::AuthorizeCheckedWithSeed(value) => build( + input, + "authorize_checked_with_seed", + kb_model::EventFamily::Admin, + false, + contract(input, "authorize_checked_with_seed"), + serde_json::json!({ + "authorizationType": stake_authorize_code(value.stake_authorize), + "authoritySeed": value.authority_seed, + "authorityOwner": pubkey_string(value.authority_owner), + "newAuthorityFromAccount": true, + }), + ), + StakeInstructionWire::SetLockupChecked(value) => build( + input, + "set_lockup_checked", + kb_model::EventFamily::Admin, + false, + contract(input, "set_lockup_checked"), + lockup_checked_args_json(value), + ), + StakeInstructionWire::GetMinimumDelegation => build( + input, + "get_minimum_delegation", + kb_model::EventFamily::Audit, + false, + contract(input, "get_minimum_delegation"), + serde_json::json!({ + "returnDataEncoding": "u64_le", + "returnDataSemantics": "minimum_delegation_lamports", + }), + ), + StakeInstructionWire::DeactivateDelinquent => build( + input, + "deactivate_delinquent", + kb_model::EventFamily::Staking, + false, + contract(input, "deactivate_delinquent"), + serde_json::json!({}), + ), + StakeInstructionWire::Redelegate => build( + input, + "redelegate", + kb_model::EventFamily::Staking, + true, + contract(input, "redelegate"), + serde_json::json!({ + "officiallyDisabled": true, + "processorOutcome": "invalid_instruction_data", + }), + ), + StakeInstructionWire::MoveStake(lamports) => build( + input, + "move_stake", + kb_model::EventFamily::Staking, + false, + contract(input, "move_stake"), + serde_json::json!({"lamports": lamports}), + ), + StakeInstructionWire::MoveLamports(lamports) => build( + input, + "move_lamports", + kb_model::EventFamily::Staking, + false, + contract(input, "move_lamports"), + serde_json::json!({"lamports": lamports}), + ), + }; +} + +fn build( + input: &kb_store_core::CoreInstructionReplayInput, + entry_code: &str, + event_family: kb_model::EventFamily, + historical: bool, + contract: AccountContract, + mut parameters: serde_json::Value, +) -> kb_decoder_api::DecoderExecutionResult { + let accounts_result = crate::resolve_accounts( + input, + contract.roles.as_slice(), + contract.minimum_count, + std::option::Option::None, + ); + let accounts = match accounts_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some(entry_code), + "stake_accounts_invalid", + error.to_string(), + ); + }, + }; + if let std::option::Option::Some(object) = parameters.as_object_mut() { + object.insert( + "accountLayout".to_string(), + serde_json::Value::String(contract.account_layout.to_string()), + ); + } + return crate::decoded_result( + input, + crate::STAKE_SURFACE_CODE, + entry_code, + event_family, + historical, + accounts, + parameters, + SOURCE, + ); +} + +fn contract( + input: &kb_store_core::CoreInstructionReplayInput, + entry_code: &str, +) -> AccountContract { + return match entry_code { + "initialize" => { + if account_key(input, 1) + == std::option::Option::Some(kb_program_ids::SYSVAR_RENT_PROGRAM_ID) + { + AccountContract { + roles: vec![ + crate::AccountRole::new("stake_account", true, false), + crate::AccountRole::new("rent_sysvar", false, false), + ], + minimum_count: 1, + account_layout: "legacy_rent_compatibility", + } + } else { + AccountContract { + roles: vec![crate::AccountRole::new("stake_account", true, false)], + minimum_count: 1, + account_layout: "runtime_minimal", + } + } + }, + "authorize" => clock_branch_contract( + input, + 1, + vec![ + crate::AccountRole::new("stake_account", true, false), + crate::AccountRole::new("clock_sysvar", false, false), + crate::AccountRole::new("current_authority", false, true), + crate::AccountRole::new("lockup_authority", false, true), + ], + 3, + vec![ + crate::AccountRole::new("stake_account", true, false), + crate::AccountRole::new("current_authority", false, true), + crate::AccountRole::new("lockup_authority", false, true), + ], + 2, + ), + "delegate_stake" => clock_branch_contract( + input, + 2, + vec![ + crate::AccountRole::new("stake_account", true, false), + crate::AccountRole::new("vote_account", false, false), + crate::AccountRole::new("clock_sysvar", false, false), + crate::AccountRole::new("stake_history_sysvar", false, false), + crate::AccountRole::new("unused_stake_config_compatibility_account", false, false), + crate::AccountRole::new("stake_authority", false, true), + ], + 5, + vec![ + crate::AccountRole::new("stake_account", true, false), + crate::AccountRole::new("vote_account", false, false), + crate::AccountRole::new("stake_authority", false, true), + ], + 3, + ), + "split" => AccountContract { + roles: vec![ + crate::AccountRole::new("source_stake_account", true, false), + crate::AccountRole::new("destination_stake_account", true, false), + crate::AccountRole::new("stake_authority", false, true), + ], + minimum_count: 2, + account_layout: "runtime_legacy_lax", + }, + "withdraw" => clock_branch_contract( + input, + 2, + vec![ + crate::AccountRole::new("source_stake_account", true, false), + crate::AccountRole::new("recipient_account", true, false), + crate::AccountRole::new("clock_sysvar", false, false), + crate::AccountRole::new("stake_history_sysvar", false, false), + crate::AccountRole::new("withdraw_authority", false, true), + crate::AccountRole::new("lockup_authority", false, true), + ], + 5, + vec![ + crate::AccountRole::new("source_stake_account", true, false), + crate::AccountRole::new("recipient_account", true, false), + crate::AccountRole::new("withdraw_authority", false, true), + crate::AccountRole::new("lockup_authority", false, true), + ], + 3, + ), + "deactivate" => clock_branch_contract( + input, + 1, + vec![ + crate::AccountRole::new("stake_account", true, false), + crate::AccountRole::new("clock_sysvar", false, false), + crate::AccountRole::new("stake_authority", false, true), + ], + 2, + vec![ + crate::AccountRole::new("stake_account", true, false), + crate::AccountRole::new("stake_authority", false, true), + ], + 2, + ), + "set_lockup" => AccountContract { + roles: vec![ + crate::AccountRole::new("stake_account", true, false), + crate::AccountRole::new("lockup_or_withdraw_authority", false, true), + ], + minimum_count: 1, + account_layout: "runtime_legacy_lax", + }, + "merge" => clock_branch_contract( + input, + 2, + vec![ + crate::AccountRole::new("destination_stake_account", true, false), + crate::AccountRole::new("source_stake_account", true, false), + crate::AccountRole::new("clock_sysvar", false, false), + crate::AccountRole::new("stake_history_sysvar", false, false), + crate::AccountRole::new("stake_authority", false, true), + ], + 4, + vec![ + crate::AccountRole::new("destination_stake_account", true, false), + crate::AccountRole::new("source_stake_account", true, false), + crate::AccountRole::new("stake_authority", false, true), + ], + 3, + ), + "authorize_with_seed" => clock_branch_contract( + input, + 2, + vec![ + crate::AccountRole::new("stake_account", true, false), + crate::AccountRole::new("authority_base", false, true), + crate::AccountRole::new("clock_sysvar", false, false), + crate::AccountRole::new("lockup_authority", false, true), + ], + 3, + vec![ + crate::AccountRole::new("stake_account", true, false), + crate::AccountRole::new("authority_base", false, true), + crate::AccountRole::new("lockup_authority", false, true), + ], + 2, + ), + "initialize_checked" => { + if account_key(input, 1) + == std::option::Option::Some(kb_program_ids::SYSVAR_RENT_PROGRAM_ID) + { + AccountContract { + roles: vec![ + crate::AccountRole::new("stake_account", true, false), + crate::AccountRole::new("rent_sysvar", false, false), + crate::AccountRole::new("stake_authority", false, false), + crate::AccountRole::new("withdraw_authority", false, true), + ], + minimum_count: 4, + account_layout: "legacy_sysvar", + } + } else { + AccountContract { + roles: vec![ + crate::AccountRole::new("stake_account", true, false), + crate::AccountRole::new("stake_authority", false, false), + crate::AccountRole::new("withdraw_authority", false, true), + ], + minimum_count: 3, + account_layout: "runtime_strict", + } + } + }, + "authorize_checked" => clock_branch_contract( + input, + 1, + vec![ + crate::AccountRole::new("stake_account", true, false), + crate::AccountRole::new("clock_sysvar", false, false), + crate::AccountRole::new("current_authority", false, true), + crate::AccountRole::new("new_authority", false, true), + crate::AccountRole::new("lockup_authority", false, true), + ], + 4, + vec![ + crate::AccountRole::new("stake_account", true, false), + crate::AccountRole::new("current_authority", false, true), + crate::AccountRole::new("new_authority", false, true), + crate::AccountRole::new("lockup_authority", false, true), + ], + 3, + ), + "authorize_checked_with_seed" => clock_branch_contract( + input, + 2, + vec![ + crate::AccountRole::new("stake_account", true, false), + crate::AccountRole::new("authority_base", false, true), + crate::AccountRole::new("clock_sysvar", false, false), + crate::AccountRole::new("new_authority", false, true), + crate::AccountRole::new("lockup_authority", false, true), + ], + 4, + vec![ + crate::AccountRole::new("stake_account", true, false), + crate::AccountRole::new("authority_base", false, true), + crate::AccountRole::new("new_authority", false, true), + crate::AccountRole::new("lockup_authority", false, true), + ], + 3, + ), + "set_lockup_checked" => AccountContract { + roles: vec![ + crate::AccountRole::new("stake_account", true, false), + crate::AccountRole::new("lockup_or_withdraw_authority", false, true), + crate::AccountRole::new("new_lockup_authority", false, true), + ], + minimum_count: 1, + account_layout: "runtime_legacy_lax", + }, + "get_minimum_delegation" => AccountContract { + roles: std::vec::Vec::new(), + minimum_count: 0, + account_layout: "no_accounts", + }, + "deactivate_delinquent" => AccountContract { + roles: vec![ + crate::AccountRole::new("stake_account", true, false), + crate::AccountRole::new("delinquent_vote_account", false, false), + crate::AccountRole::new("reference_vote_account", false, false), + ], + minimum_count: 3, + account_layout: "runtime_strict", + }, + "redelegate" => AccountContract { + roles: vec![ + crate::AccountRole::new("source_stake_account", true, false), + crate::AccountRole::new("destination_stake_account", true, false), + crate::AccountRole::new("vote_account", false, false), + crate::AccountRole::new("unused_stake_config_compatibility_account", false, false), + crate::AccountRole::new("stake_authority", false, true), + ], + minimum_count: 0, + account_layout: "disabled_before_account_processing", + }, + "move_stake" | "move_lamports" => AccountContract { + roles: vec![ + crate::AccountRole::new("source_stake_account", true, false), + crate::AccountRole::new("destination_stake_account", true, false), + crate::AccountRole::new("stake_authority", false, true), + ], + minimum_count: 3, + account_layout: "runtime_strict", + }, + _ => AccountContract { + roles: std::vec::Vec::new(), + minimum_count: 0, + account_layout: "unknown", + }, + }; +} + +fn clock_branch_contract( + input: &kb_store_core::CoreInstructionReplayInput, + branch_position: usize, + legacy_roles: std::vec::Vec, + legacy_minimum_count: usize, + current_roles: std::vec::Vec, + current_minimum_count: usize, +) -> AccountContract { + if account_key(input, branch_position) + == std::option::Option::Some(kb_program_ids::SYSVAR_CLOCK_PROGRAM_ID) + { + return AccountContract { + roles: legacy_roles, + minimum_count: legacy_minimum_count, + account_layout: "legacy_sysvar", + }; + } + return AccountContract { + roles: current_roles, + minimum_count: current_minimum_count, + account_layout: "runtime_strict", + }; +} + +fn account_key( + input: &kb_store_core::CoreInstructionReplayInput, + position: usize, +) -> std::option::Option<&str> { + return input + .instruction_accounts_json + .as_array() + .and_then(|accounts| return accounts.get(position)) + .and_then(|account| return account.get("accountKey")) + .and_then(serde_json::Value::as_str); +} + +fn authorized_json(value: solana_stake_interface::state::Authorized) -> serde_json::Value { + return serde_json::json!({ + "staker": value.staker.to_string(), + "withdrawer": value.withdrawer.to_string(), + }); +} + +fn lockup_json(value: solana_stake_interface::state::Lockup) -> serde_json::Value { + return serde_json::json!({ + "unixTimestamp": value.unix_timestamp, + "epoch": value.epoch, + "custodian": value.custodian.to_string(), + }); +} + +fn lockup_args_json(value: LockupArgsWire) -> serde_json::Value { + return serde_json::json!({ + "unixTimestamp": value.unix_timestamp, + "epoch": value.epoch, + "custodian": value.custodian.map(pubkey_string), + }); +} + +fn lockup_checked_args_json(value: LockupCheckedArgsWire) -> serde_json::Value { + return serde_json::json!({ + "unixTimestamp": value.unix_timestamp, + "epoch": value.epoch, + "custodianFromAccount": true, + }); +} + +fn stake_authorize_code(value: solana_stake_interface::state::StakeAuthorize) -> &'static str { + return match value { + solana_stake_interface::state::StakeAuthorize::Staker => "staker", + solana_stake_interface::state::StakeAuthorize::Withdrawer => "withdrawer", + }; +} + +fn pubkey_string(value: [u8; 32]) -> std::string::String { + return bs58::encode(value).into_string(); +} + +fn entry_code(tag: u32) -> std::option::Option<&'static str> { + return match tag { + 0 => std::option::Option::Some("initialize"), + 1 => std::option::Option::Some("authorize"), + 2 => std::option::Option::Some("delegate_stake"), + 3 => std::option::Option::Some("split"), + 4 => std::option::Option::Some("withdraw"), + 5 => std::option::Option::Some("deactivate"), + 6 => std::option::Option::Some("set_lockup"), + 7 => std::option::Option::Some("merge"), + 8 => std::option::Option::Some("authorize_with_seed"), + 9 => std::option::Option::Some("initialize_checked"), + 10 => std::option::Option::Some("authorize_checked"), + 11 => std::option::Option::Some("authorize_checked_with_seed"), + 12 => std::option::Option::Some("set_lockup_checked"), + 13 => std::option::Option::Some("get_minimum_delegation"), + 14 => std::option::Option::Some("deactivate_delinquent"), + 15 => std::option::Option::Some("redelegate"), + 16 => std::option::Option::Some("move_stake"), + 17 => std::option::Option::Some("move_lamports"), + _ => std::option::Option::None, + }; +} + +fn tag_hex(tag: u32) -> std::string::String { + return tag.to_le_bytes().iter().map(|byte| return format!("{byte:02x}")).collect(); +} + +#[cfg(test)] +mod tests { + use base64::Engine; // rust-rules: trait-import + + struct Fixture { + instruction: super::StakeInstructionWire, + entry_code: &'static str, + account_keys: std::vec::Vec, + transaction_failed: bool, + } + + fn pubkey(byte: u8) -> [u8; 32] { + return [byte; 32]; + } + + fn fixtures() -> std::vec::Vec { + let authorized = solana_stake_interface::state::Authorized { + staker: solana_sdk_ids::stake::id(), + withdrawer: solana_sdk_ids::stake::id(), + }; + let lockup = solana_stake_interface::state::Lockup { + unix_timestamp: -1, + epoch: u64::MAX, + custodian: solana_sdk_ids::stake::id(), + }; + let lockup_args = super::LockupArgsWire { + unix_timestamp: std::option::Option::Some(i64::MIN), + epoch: std::option::Option::Some(u64::MAX), + custodian: std::option::Option::Some(pubkey(4)), + }; + let lockup_checked_args = super::LockupCheckedArgsWire { + unix_timestamp: std::option::Option::Some(i64::MAX), + epoch: std::option::Option::Some(42), + }; + return vec![ + fixture( + super::StakeInstructionWire::Initialize(authorized, lockup), + "initialize", + vec!["Stake0", kb_program_ids::SYSVAR_RENT_PROGRAM_ID], + false, + ), + fixture( + super::StakeInstructionWire::Authorize( + pubkey(5), + solana_stake_interface::state::StakeAuthorize::Withdrawer, + ), + "authorize", + vec!["Stake0", kb_program_ids::SYSVAR_CLOCK_PROGRAM_ID, "Authority2", "Custodian3"], + false, + ), + fixture( + super::StakeInstructionWire::DelegateStake, + "delegate_stake", + vec![ + "Stake0", + "Vote1", + kb_program_ids::SYSVAR_CLOCK_PROGRAM_ID, + kb_program_ids::SYSVAR_STAKE_HISTORY_PROGRAM_ID, + kb_program_ids::STAKE_CONFIG_ACCOUNT_ID, + "Authority5", + ], + false, + ), + fixture( + super::StakeInstructionWire::Split(u64::MAX), + "split", + vec!["Stake0", "Stake1", "Authority2"], + false, + ), + fixture( + super::StakeInstructionWire::Withdraw(u64::MAX), + "withdraw", + vec![ + "Stake0", + "Recipient1", + kb_program_ids::SYSVAR_CLOCK_PROGRAM_ID, + kb_program_ids::SYSVAR_STAKE_HISTORY_PROGRAM_ID, + "Authority4", + "Custodian5", + ], + false, + ), + fixture( + super::StakeInstructionWire::Deactivate, + "deactivate", + vec!["Stake0", kb_program_ids::SYSVAR_CLOCK_PROGRAM_ID, "Authority2"], + false, + ), + fixture( + super::StakeInstructionWire::SetLockup(lockup_args), + "set_lockup", + vec!["Stake0", "Authority1"], + false, + ), + fixture( + super::StakeInstructionWire::Merge, + "merge", + vec![ + "Stake0", + "Stake1", + kb_program_ids::SYSVAR_CLOCK_PROGRAM_ID, + kb_program_ids::SYSVAR_STAKE_HISTORY_PROGRAM_ID, + "Authority4", + ], + false, + ), + fixture( + super::StakeInstructionWire::AuthorizeWithSeed(super::AuthorizeWithSeedArgsWire { + new_authorized_pubkey: pubkey(6), + stake_authorize: solana_stake_interface::state::StakeAuthorize::Staker, + authority_seed: "seed".to_string(), + authority_owner: pubkey(7), + }), + "authorize_with_seed", + vec!["Stake0", "Base1", kb_program_ids::SYSVAR_CLOCK_PROGRAM_ID, "Custodian3"], + false, + ), + fixture( + super::StakeInstructionWire::InitializeChecked, + "initialize_checked", + vec!["Stake0", kb_program_ids::SYSVAR_RENT_PROGRAM_ID, "Authority2", "Authority3"], + false, + ), + fixture( + super::StakeInstructionWire::AuthorizeChecked( + solana_stake_interface::state::StakeAuthorize::Staker, + ), + "authorize_checked", + vec![ + "Stake0", + kb_program_ids::SYSVAR_CLOCK_PROGRAM_ID, + "Authority2", + "Authority3", + "Custodian4", + ], + false, + ), + fixture( + super::StakeInstructionWire::AuthorizeCheckedWithSeed( + super::AuthorizeCheckedWithSeedArgsWire { + stake_authorize: solana_stake_interface::state::StakeAuthorize::Withdrawer, + authority_seed: "seed".to_string(), + authority_owner: pubkey(8), + }, + ), + "authorize_checked_with_seed", + vec![ + "Stake0", + "Base1", + kb_program_ids::SYSVAR_CLOCK_PROGRAM_ID, + "Authority3", + "Custodian4", + ], + false, + ), + fixture( + super::StakeInstructionWire::SetLockupChecked(lockup_checked_args), + "set_lockup_checked", + vec!["Stake0", "Authority1", "Authority2"], + false, + ), + fixture( + super::StakeInstructionWire::GetMinimumDelegation, + "get_minimum_delegation", + std::vec::Vec::new(), + false, + ), + fixture( + super::StakeInstructionWire::DeactivateDelinquent, + "deactivate_delinquent", + vec!["Stake0", "Vote1", "Vote2"], + false, + ), + fixture( + super::StakeInstructionWire::Redelegate, + "redelegate", + vec![ + "Stake0", + "Stake1", + "Vote2", + kb_program_ids::STAKE_CONFIG_ACCOUNT_ID, + "Authority4", + ], + true, + ), + fixture( + super::StakeInstructionWire::MoveStake(u64::MAX), + "move_stake", + vec!["Stake0", "Stake1", "Authority2"], + false, + ), + fixture( + super::StakeInstructionWire::MoveLamports(u64::MAX), + "move_lamports", + vec!["Stake0", "Stake1", "Authority2"], + false, + ), + ]; + } + + fn fixture( + instruction: super::StakeInstructionWire, + entry_code: &'static str, + account_keys: std::vec::Vec<&str>, + transaction_failed: bool, + ) -> Fixture { + return Fixture { + instruction, + entry_code, + account_keys: account_keys.into_iter().map(str::to_string).collect(), + transaction_failed, + }; + } + + fn replay_input( + instruction: &super::StakeInstructionWire, + account_keys: &[std::string::String], + transaction_failed: bool, + ) -> kb_store_core::CoreInstructionReplayInput { + let bytes_result = wincode::serialize(instruction); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("Stake fixture serialization failed: {error}") + }, + }; + return replay_input_bytes(bytes.as_slice(), account_keys, transaction_failed); + } + + fn replay_input_bytes( + bytes: &[u8], + account_keys: &[std::string::String], + transaction_failed: bool, + ) -> kb_store_core::CoreInstructionReplayInput { + let resolved_keys = account_keys + .iter() + .enumerate() + .map(|(index, key)| { + return serde_json::json!({ + "accountIndex": index, + "accountKey": key, + "source": "static", + "writable": true, + "signer": true, + "executable": false, + }); + }) + .collect::>(); + let instruction_accounts = account_keys + .iter() + .enumerate() + .map(|(index, key)| { + return serde_json::json!({ + "accountIndex": index, + "accountKey": key, + }); + }) + .collect::>(); + let result = kb_store_core::CoreInstructionReplayInput::new( + "signature:0", + "signature", + 42, + "0", + kb_program_ids::STAKE_PROGRAM_ID, + transaction_failed, + if transaction_failed { + std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]})) + } else { + std::option::Option::None + }, + serde_json::Value::Array(resolved_keys), + serde_json::Value::Array(instruction_accounts), + std::option::Option::Some(serde_json::json!({ + "dataBase64": base64::engine::general_purpose::STANDARD.encode(bytes), + })), + std::option::Option::Some("payload-hash".to_string()), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Stake replay input failed: {error}"), + }; + } + + #[test] + fn coverage_declares_every_official_stake_instruction() { + let coverage = crate::stake_coverage(); + assert_eq!(coverage.len(), 18); + for tag in 0_u32..18_u32 { + let expected = super::tag_hex(tag); + assert!(coverage.iter().any(|entry| { + return entry.discriminator_hex.as_deref() + == std::option::Option::Some(expected.as_str()); + })); + } + assert_eq!(solana_sdk_ids::stake::id().to_string(), kb_program_ids::STAKE_PROGRAM_ID); + } + + #[test] + fn wire_mirror_matches_the_official_legacy_layout() { + let initialize = super::StakeInstructionWire::Initialize( + solana_stake_interface::state::Authorized { + staker: solana_sdk_ids::stake::id(), + withdrawer: solana_sdk_ids::stake::id(), + }, + solana_stake_interface::state::Lockup { + unix_timestamp: -2, + epoch: 3, + custodian: solana_sdk_ids::stake::id(), + }, + ); + let initialize_result = wincode::serialize(&initialize); + let initialize_bytes = match initialize_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("Stake fixture serialization failed: {error}") + }, + }; + let program_id_bytes = solana_sdk_ids::stake::id().to_bytes(); + let mut expected_initialize = 0_u32.to_le_bytes().to_vec(); + expected_initialize.extend_from_slice(&program_id_bytes); + expected_initialize.extend_from_slice(&program_id_bytes); + expected_initialize.extend_from_slice(&(-2_i64).to_le_bytes()); + expected_initialize.extend_from_slice(&3_u64.to_le_bytes()); + expected_initialize.extend_from_slice(&program_id_bytes); + assert_eq!(initialize_bytes, expected_initialize); + let authorize_with_seed = + super::StakeInstructionWire::AuthorizeWithSeed(super::AuthorizeWithSeedArgsWire { + new_authorized_pubkey: [5; 32], + stake_authorize: solana_stake_interface::state::StakeAuthorize::Withdrawer, + authority_seed: "seed".to_string(), + authority_owner: [6; 32], + }); + let seed_result = wincode::serialize(&authorize_with_seed); + let seed_bytes = match seed_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("Stake fixture serialization failed: {error}") + }, + }; + let mut expected_seed = 8_u32.to_le_bytes().to_vec(); + expected_seed.extend_from_slice(&[5; 32]); + expected_seed.extend_from_slice(&1_u32.to_le_bytes()); + expected_seed.extend_from_slice(&4_u64.to_le_bytes()); + expected_seed.extend_from_slice(b"seed"); + expected_seed.extend_from_slice(&[6; 32]); + assert_eq!(seed_bytes, expected_seed); + } + + #[test] + fn every_official_stake_variant_decodes() { + for fixture in fixtures() { + let input = replay_input( + &fixture.instruction, + fixture.account_keys.as_slice(), + fixture.transaction_failed, + ); + let result = crate::stake_decode(&input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some(fixture.entry_code) + ); + assert_eq!(result.observations.len(), 1); + assert_eq!(result.observations[0].observation_committed, !fixture.transaction_failed); + } + } + + #[test] + fn legacy_and_current_account_layouts_are_preserved() { + let instruction = super::StakeInstructionWire::DelegateStake; + let legacy_keys = [ + "Stake0".to_string(), + "Vote1".to_string(), + kb_program_ids::SYSVAR_CLOCK_PROGRAM_ID.to_string(), + kb_program_ids::SYSVAR_STAKE_HISTORY_PROGRAM_ID.to_string(), + kb_program_ids::STAKE_CONFIG_ACCOUNT_ID.to_string(), + "Authority5".to_string(), + ]; + let current_keys = ["Stake0".to_string(), "Vote1".to_string(), "Authority2".to_string()]; + let legacy = + crate::stake_decode(&replay_input(&instruction, legacy_keys.as_slice(), false)); + let current = + crate::stake_decode(&replay_input(&instruction, current_keys.as_slice(), false)); + assert_eq!( + legacy.observations[0].payload_json["parameters"]["accountLayout"], + "legacy_sysvar" + ); + assert_eq!( + current.observations[0].payload_json["parameters"]["accountLayout"], + "runtime_strict" + ); + assert_eq!( + legacy.observations[0].payload_json["accounts"][4]["role"], + "unused_stake_config_compatibility_account" + ); + assert_eq!(current.observations[0].payload_json["accounts"][2]["role"], "stake_authority"); + } + + #[test] + fn current_bpf_account_layouts_decode_without_legacy_sysvars() { + let authorized = solana_stake_interface::state::Authorized { + staker: solana_sdk_ids::stake::id(), + withdrawer: solana_sdk_ids::stake::id(), + }; + let lockup = solana_stake_interface::state::Lockup::default(); + let fixtures = vec![ + (super::StakeInstructionWire::Initialize(authorized, lockup), vec!["Stake0"]), + ( + super::StakeInstructionWire::Authorize( + pubkey(1), + solana_stake_interface::state::StakeAuthorize::Staker, + ), + vec!["Stake0", "Authority1"], + ), + ( + super::StakeInstructionWire::DelegateStake, + vec!["Stake0", "Vote1", "Authority2"], + ), + ( + super::StakeInstructionWire::Withdraw(1), + vec!["Stake0", "Recipient1", "Authority2"], + ), + (super::StakeInstructionWire::Deactivate, vec!["Stake0", "Authority1"]), + (super::StakeInstructionWire::Merge, vec!["Stake0", "Stake1", "Authority2"]), + ( + super::StakeInstructionWire::AuthorizeWithSeed(super::AuthorizeWithSeedArgsWire { + new_authorized_pubkey: pubkey(2), + stake_authorize: solana_stake_interface::state::StakeAuthorize::Withdrawer, + authority_seed: "seed".to_string(), + authority_owner: pubkey(3), + }), + vec!["Stake0", "Base1"], + ), + ( + super::StakeInstructionWire::InitializeChecked, + vec!["Stake0", "Authority1", "Authority2"], + ), + ( + super::StakeInstructionWire::AuthorizeChecked( + solana_stake_interface::state::StakeAuthorize::Staker, + ), + vec!["Stake0", "Authority1", "Authority2"], + ), + ( + super::StakeInstructionWire::AuthorizeCheckedWithSeed( + super::AuthorizeCheckedWithSeedArgsWire { + stake_authorize: solana_stake_interface::state::StakeAuthorize::Withdrawer, + authority_seed: "seed".to_string(), + authority_owner: pubkey(4), + }, + ), + vec!["Stake0", "Base1", "Authority2"], + ), + ]; + for (instruction, keys) in fixtures { + let account_keys = keys.into_iter().map(str::to_string).collect::>(); + let result = + crate::stake_decode(&replay_input(&instruction, account_keys.as_slice(), false)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.observations[0].payload_json["parameters"]["accountLayout"], + if matches!(instruction, super::StakeInstructionWire::Initialize(_, _)) { + "runtime_minimal" + } else { + "runtime_strict" + } + ); + } + } + + #[test] + fn deprecated_redelegate_is_an_uncommitted_historical_intent() { + let instruction = super::StakeInstructionWire::Redelegate; + let input = replay_input(&instruction, &[], true); + let result = crate::stake_decode(&input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!result.observations[0].observation_committed); + assert_eq!(result.observations[0].payload_json["historical"], true); + assert_eq!(result.observations[0].payload_json["parameters"]["officiallyDisabled"], true); + assert_eq!( + result.observations[0].payload_json["parameters"]["accountLayout"], + "disabled_before_account_processing" + ); + } + + #[test] + fn failed_stake_instruction_is_decoded_as_uncommitted_intent() { + let instruction = super::StakeInstructionWire::MoveStake(42); + let keys = ["Stake0".to_string(), "Stake1".to_string(), "Authority2".to_string()]; + let result = crate::stake_decode(&replay_input(&instruction, keys.as_slice(), true)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!result.observations[0].observation_committed); + assert_eq!(result.observations[0].payload_json["transactionSucceeded"], false); + } + + #[test] + fn truncated_unknown_trailing_and_missing_accounts_fail_safely() { + let no_accounts = std::vec::Vec::new(); + let truncated = + crate::stake_decode(&replay_input_bytes(&[0, 0, 0], no_accounts.as_slice(), false)); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let unknown = crate::stake_decode(&replay_input_bytes( + &99_u32.to_le_bytes(), + no_accounts.as_slice(), + false, + )); + assert_eq!(unknown.status, kb_decoder_api::DecoderOutcomeStatus::Unsupported); + let bytes_result = wincode::serialize(&super::StakeInstructionWire::MoveLamports(1)); + let mut bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("Stake fixture serialization failed: {error}") + }, + }; + bytes.push(1); + let trailing = crate::stake_decode(&replay_input_bytes( + bytes.as_slice(), + no_accounts.as_slice(), + false, + )); + assert_eq!(trailing.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let exact_result = wincode::serialize(&super::StakeInstructionWire::MoveLamports(1)); + let exact = match exact_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("Stake fixture serialization failed: {error}") + }, + }; + let missing_accounts = crate::stake_decode(&replay_input_bytes( + exact.as_slice(), + no_accounts.as_slice(), + false, + )); + assert_eq!(missing_accounts.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/system.rs b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/system.rs new file mode 100644 index 0000000..c7405ec --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/system.rs @@ -0,0 +1,685 @@ +// file: kb_decoder_solana_core/src/system.rs +// version: 5 + +//! Maximal current System Program instruction decoding through the official interface enum and wincode schema. + +const SOURCE: &str = "solana-system-interface@3.2.0 SystemInstruction wincode-compatible contract"; + +const CREATE_ACCOUNT_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("funding_account", true, true), + crate::AccountRole::new("new_account", true, true), +]; +const ASSIGN_ROLES: &[crate::AccountRole] = + &[crate::AccountRole::new("assigned_account", true, true)]; +const TRANSFER_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("funding_account", true, true), + crate::AccountRole::new("recipient_account", true, false), +]; +const CREATE_ACCOUNT_WITH_SEED_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("funding_account", true, true), + crate::AccountRole::new("new_account", true, false), + crate::AccountRole::new("base_account", false, true), +]; +const ADVANCE_NONCE_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("nonce_account", true, false), + crate::AccountRole::new("recent_blockhashes_sysvar", false, false), + crate::AccountRole::new("nonce_authority", false, true), +]; +const WITHDRAW_NONCE_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("nonce_account", true, false), + crate::AccountRole::new("recipient_account", true, false), + crate::AccountRole::new("recent_blockhashes_sysvar", false, false), + crate::AccountRole::new("rent_sysvar", false, false), + crate::AccountRole::new("nonce_authority", false, true), +]; +const INITIALIZE_NONCE_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("nonce_account", true, false), + crate::AccountRole::new("recent_blockhashes_sysvar", false, false), + crate::AccountRole::new("rent_sysvar", false, false), +]; +const AUTHORIZE_NONCE_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("nonce_account", true, false), + crate::AccountRole::new("nonce_authority", false, true), +]; +const ALLOCATE_ROLES: &[crate::AccountRole] = + &[crate::AccountRole::new("allocated_account", true, true)]; +const SEEDED_ACCOUNT_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("derived_account", true, false), + crate::AccountRole::new("base_account", false, true), +]; +const TRANSFER_WITH_SEED_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("funding_account", true, false), + crate::AccountRole::new("base_account", false, true), + crate::AccountRole::new("recipient_account", true, false), +]; +const UPGRADE_NONCE_ROLES: &[crate::AccountRole] = + &[crate::AccountRole::new("nonce_account", true, false)]; +const CREATE_ACCOUNT_ALLOW_PREFUND_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("new_account", true, true), + crate::AccountRole::new("funding_account", true, true), +]; + +/// Returns declared System Program instruction coverage. +pub(crate) fn system_coverage() -> std::vec::Vec { + let entries = [ + ("create_account", 0_u32), + ("assign", 1_u32), + ("transfer", 2_u32), + ("create_account_with_seed", 3_u32), + ("advance_nonce_account", 4_u32), + ("withdraw_nonce_account", 5_u32), + ("initialize_nonce_account", 6_u32), + ("authorize_nonce_account", 7_u32), + ("allocate", 8_u32), + ("allocate_with_seed", 9_u32), + ("assign_with_seed", 10_u32), + ("transfer_with_seed", 11_u32), + ("upgrade_nonce_account", 12_u32), + ("create_account_allow_prefund", 13_u32), + ]; + return entries + .into_iter() + .map(|(entry_code, tag)| { + return kb_decoder_api::DecoderCoverageDeclaration { + program_id: kb_program_ids::SYSTEM_PROGRAM_ID.to_string(), + surface_code: std::option::Option::Some(crate::SYSTEM_SURFACE_CODE.to_string()), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: entry_code.to_string(), + discriminator_hex: std::option::Option::Some(tag_hex(tag).to_string()), + historical: false, + }; + }) + .collect(); +} + +/// Recognizes one System Program instruction without producing an event. +pub(crate) fn system_recognize( + input: &kb_store_core::CoreInstructionReplayInput, + priority: u16, +) -> kb_decoder_api::DecoderRecognition { + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return kb_decoder_api::DecoderRecognition::compatible( + false, + priority, + std::option::Option::Some(crate::SYSTEM_SURFACE_CODE.to_string()), + std::option::Option::Some("malformed_system_instruction".to_string()), + std::option::Option::None, + ); + }, + }; + let tag_result = crate::read_u32_le(bytes.as_slice(), 0); + let tag = match tag_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return kb_decoder_api::DecoderRecognition::compatible( + false, + priority, + std::option::Option::Some(crate::SYSTEM_SURFACE_CODE.to_string()), + std::option::Option::Some("malformed_system_instruction".to_string()), + crate::hexadecimal_prefix(bytes.as_slice(), bytes.len().min(4)), + ); + }, + }; + let entry_code = entry_code(tag); + return kb_decoder_api::DecoderRecognition::compatible( + entry_code.is_some(), + priority, + std::option::Option::Some(crate::SYSTEM_SURFACE_CODE.to_string()), + entry_code.map(str::to_string).or_else(|| { + return std::option::Option::Some("unknown_system_instruction".to_string()); + }), + std::option::Option::Some(tag_hex(tag).to_string()), + ); +} + +/// Decodes one System Program instruction. +pub(crate) fn system_decode( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_decoder_api::DecoderExecutionResult { + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("malformed_system_instruction"), + "system_payload_invalid", + error.to_string(), + ); + }, + }; + let tag_result = crate::read_u32_le(bytes.as_slice(), 0); + let tag = match tag_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("malformed_system_instruction"), + "system_tag_truncated", + error.to_string(), + ); + }, + }; + let entry_code = match entry_code(tag) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return crate::unsupported_result( + "unknown_system_instruction", + "system_tag_unknown", + format!( + "unknown System Program instruction tag {tag}; payload_sha256={}", + crate::payload_hash(input) + ), + ); + }, + }; + let instruction_result = wincode::deserialize_exact::< + solana_system_interface::instruction::SystemInstruction, + >(bytes.as_slice()); + let instruction = match instruction_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some(entry_code), + "system_wincode_invalid", + format!("cannot decode exact System Program {entry_code}: {error}"), + ); + }, + }; + return decoded_instruction(input, instruction); +} + +fn decoded_instruction( + input: &kb_store_core::CoreInstructionReplayInput, + instruction: solana_system_interface::instruction::SystemInstruction, +) -> kb_decoder_api::DecoderExecutionResult { + return match instruction { + solana_system_interface::instruction::SystemInstruction::CreateAccount { + lamports, + space, + owner, + } => build( + input, + "create_account", + kb_model::EventFamily::Lifecycle, + CREATE_ACCOUNT_ROLES, + 2, + serde_json::json!({"lamports": lamports, "space": space, "owner": owner.to_string()}), + ), + solana_system_interface::instruction::SystemInstruction::Assign { owner } => build( + input, + "assign", + kb_model::EventFamily::Admin, + ASSIGN_ROLES, + 1, + serde_json::json!({"owner": owner.to_string()}), + ), + solana_system_interface::instruction::SystemInstruction::Transfer { lamports } => build( + input, + "transfer", + kb_model::EventFamily::Audit, + TRANSFER_ROLES, + 2, + serde_json::json!({"lamports": lamports}), + ), + solana_system_interface::instruction::SystemInstruction::CreateAccountWithSeed { + base, + seed, + lamports, + space, + owner, + } => build( + input, + "create_account_with_seed", + kb_model::EventFamily::Lifecycle, + CREATE_ACCOUNT_WITH_SEED_ROLES, + 2, + serde_json::json!({ + "base": base.to_string(), + "seed": seed, + "lamports": lamports, + "space": space, + "owner": owner.to_string(), + }), + ), + solana_system_interface::instruction::SystemInstruction::AdvanceNonceAccount => build( + input, + "advance_nonce_account", + kb_model::EventFamily::Lifecycle, + ADVANCE_NONCE_ROLES, + 3, + serde_json::json!({}), + ), + solana_system_interface::instruction::SystemInstruction::WithdrawNonceAccount(lamports) => { + build( + input, + "withdraw_nonce_account", + kb_model::EventFamily::Lifecycle, + WITHDRAW_NONCE_ROLES, + 5, + serde_json::json!({"lamports": lamports}), + ) + }, + solana_system_interface::instruction::SystemInstruction::InitializeNonceAccount( + authority, + ) => build( + input, + "initialize_nonce_account", + kb_model::EventFamily::Lifecycle, + INITIALIZE_NONCE_ROLES, + 3, + serde_json::json!({"authority": authority.to_string()}), + ), + solana_system_interface::instruction::SystemInstruction::AuthorizeNonceAccount( + authority, + ) => build( + input, + "authorize_nonce_account", + kb_model::EventFamily::Admin, + AUTHORIZE_NONCE_ROLES, + 2, + serde_json::json!({"authority": authority.to_string()}), + ), + solana_system_interface::instruction::SystemInstruction::Allocate { space } => build( + input, + "allocate", + kb_model::EventFamily::Lifecycle, + ALLOCATE_ROLES, + 1, + serde_json::json!({"space": space}), + ), + solana_system_interface::instruction::SystemInstruction::AllocateWithSeed { + base, + seed, + space, + owner, + } => build( + input, + "allocate_with_seed", + kb_model::EventFamily::Lifecycle, + SEEDED_ACCOUNT_ROLES, + 2, + serde_json::json!({ + "base": base.to_string(), + "seed": seed, + "space": space, + "owner": owner.to_string(), + }), + ), + solana_system_interface::instruction::SystemInstruction::AssignWithSeed { + base, + seed, + owner, + } => build( + input, + "assign_with_seed", + kb_model::EventFamily::Admin, + SEEDED_ACCOUNT_ROLES, + 2, + serde_json::json!({ + "base": base.to_string(), + "seed": seed, + "owner": owner.to_string(), + }), + ), + solana_system_interface::instruction::SystemInstruction::TransferWithSeed { + lamports, + from_seed, + from_owner, + } => build( + input, + "transfer_with_seed", + kb_model::EventFamily::Audit, + TRANSFER_WITH_SEED_ROLES, + 3, + serde_json::json!({ + "lamports": lamports, + "fromSeed": from_seed, + "fromOwner": from_owner.to_string(), + }), + ), + solana_system_interface::instruction::SystemInstruction::UpgradeNonceAccount => build( + input, + "upgrade_nonce_account", + kb_model::EventFamily::Lifecycle, + UPGRADE_NONCE_ROLES, + 1, + serde_json::json!({}), + ), + solana_system_interface::instruction::SystemInstruction::CreateAccountAllowPrefund { + lamports, + space, + owner, + } => build( + input, + "create_account_allow_prefund", + kb_model::EventFamily::Lifecycle, + CREATE_ACCOUNT_ALLOW_PREFUND_ROLES, + if lamports == 0 { 1 } else { 2 }, + serde_json::json!({"lamports": lamports, "space": space, "owner": owner.to_string()}), + ), + }; +} + +fn build( + input: &kb_store_core::CoreInstructionReplayInput, + entry_code: &str, + event_family: kb_model::EventFamily, + roles: &[crate::AccountRole], + minimum_count: usize, + parameters: serde_json::Value, +) -> kb_decoder_api::DecoderExecutionResult { + let accounts_result = + crate::resolve_accounts(input, roles, minimum_count, std::option::Option::None); + let accounts = match accounts_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some(entry_code), + "system_accounts_invalid", + error.to_string(), + ); + }, + }; + return crate::decoded_result( + input, + crate::SYSTEM_SURFACE_CODE, + entry_code, + event_family, + false, + accounts, + parameters, + SOURCE, + ); +} + +fn entry_code(tag: u32) -> std::option::Option<&'static str> { + return match tag { + 0 => std::option::Option::Some("create_account"), + 1 => std::option::Option::Some("assign"), + 2 => std::option::Option::Some("transfer"), + 3 => std::option::Option::Some("create_account_with_seed"), + 4 => std::option::Option::Some("advance_nonce_account"), + 5 => std::option::Option::Some("withdraw_nonce_account"), + 6 => std::option::Option::Some("initialize_nonce_account"), + 7 => std::option::Option::Some("authorize_nonce_account"), + 8 => std::option::Option::Some("allocate"), + 9 => std::option::Option::Some("allocate_with_seed"), + 10 => std::option::Option::Some("assign_with_seed"), + 11 => std::option::Option::Some("transfer_with_seed"), + 12 => std::option::Option::Some("upgrade_nonce_account"), + 13 => std::option::Option::Some("create_account_allow_prefund"), + _ => std::option::Option::None, + }; +} + +fn tag_hex(tag: u32) -> std::string::String { + let bytes = tag.to_le_bytes(); + return format!("{:02x}{:02x}{:02x}{:02x}", bytes[0], bytes[1], bytes[2], bytes[3]); +} + +#[cfg(test)] +mod tests { + use base64::Engine; // rust-rules: trait-import + + fn replay_input( + instruction: &solana_system_interface::instruction::SystemInstruction, + account_count: usize, + transaction_failed: bool, + ) -> kb_store_core::CoreInstructionReplayInput { + let bytes_result = wincode::serialize(instruction); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("system fixture serialization failed: {error}") + }, + }; + return replay_input_bytes(bytes.as_slice(), account_count, transaction_failed); + } + + fn replay_input_bytes( + bytes: &[u8], + account_count: usize, + transaction_failed: bool, + ) -> kb_store_core::CoreInstructionReplayInput { + let encoded = base64::engine::general_purpose::STANDARD.encode(bytes); + let account_keys = (0..account_count) + .map(|index| { + return serde_json::json!({ + "accountIndex": index, + "accountKey": format!("account{index}"), + "source": "static", + "writable": true, + "signer": true, + "executable": false, + }); + }) + .collect::>(); + let instruction_accounts = (0..account_count) + .map(|index| { + return serde_json::json!({ + "accountIndex": index, + "accountKey": format!("account{index}"), + }); + }) + .collect::>(); + let result = kb_store_core::CoreInstructionReplayInput::new( + "signature:0", + "signature", + 42, + "0", + kb_program_ids::SYSTEM_PROGRAM_ID, + transaction_failed, + if transaction_failed { + std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]})) + } else { + std::option::Option::None + }, + serde_json::Value::Array(account_keys), + serde_json::Value::Array(instruction_accounts), + std::option::Option::Some(serde_json::json!({"dataBase64": encoded})), + std::option::Option::Some("payload-hash".to_string()), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("replay input failed: {error}"), + }; + } + + #[test] + fn every_official_variant_decodes() { + let address = solana_sdk_ids::system_program::id(); + let fixtures = [ + ( + solana_system_interface::instruction::SystemInstruction::CreateAccount { + lamports: u64::MAX, + space: u64::MAX, + owner: address, + }, + 2, + "create_account", + ), + ( + solana_system_interface::instruction::SystemInstruction::Assign { owner: address }, + 1, + "assign", + ), + ( + solana_system_interface::instruction::SystemInstruction::Transfer { + lamports: u64::MAX, + }, + 2, + "transfer", + ), + ( + solana_system_interface::instruction::SystemInstruction::CreateAccountWithSeed { + base: address, + seed: "seed".to_string(), + lamports: 1, + space: 2, + owner: address, + }, + 3, + "create_account_with_seed", + ), + ( + solana_system_interface::instruction::SystemInstruction::AdvanceNonceAccount, + 3, + "advance_nonce_account", + ), + ( + solana_system_interface::instruction::SystemInstruction::WithdrawNonceAccount(1), + 5, + "withdraw_nonce_account", + ), + ( + solana_system_interface::instruction::SystemInstruction::InitializeNonceAccount( + address, + ), + 3, + "initialize_nonce_account", + ), + ( + solana_system_interface::instruction::SystemInstruction::AuthorizeNonceAccount( + address, + ), + 2, + "authorize_nonce_account", + ), + ( + solana_system_interface::instruction::SystemInstruction::Allocate { space: 2 }, + 1, + "allocate", + ), + ( + solana_system_interface::instruction::SystemInstruction::AllocateWithSeed { + base: address, + seed: "seed".to_string(), + space: 2, + owner: address, + }, + 2, + "allocate_with_seed", + ), + ( + solana_system_interface::instruction::SystemInstruction::AssignWithSeed { + base: address, + seed: "seed".to_string(), + owner: address, + }, + 2, + "assign_with_seed", + ), + ( + solana_system_interface::instruction::SystemInstruction::TransferWithSeed { + lamports: 1, + from_seed: "seed".to_string(), + from_owner: address, + }, + 3, + "transfer_with_seed", + ), + ( + solana_system_interface::instruction::SystemInstruction::UpgradeNonceAccount, + 1, + "upgrade_nonce_account", + ), + ( + solana_system_interface::instruction::SystemInstruction::CreateAccountAllowPrefund { + lamports: 1, + space: 2, + owner: address, + }, + 2, + "create_account_allow_prefund", + ), + ]; + for (instruction, account_count, expected) in fixtures { + let input = replay_input(&instruction, account_count, false); + let result = crate::system_decode(&input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some(expected) + ); + assert_eq!(result.observations.len(), 1); + } + } + + #[test] + fn additional_accounts_are_preserved_without_rejecting_valid_system_instructions() { + let instruction = + solana_system_interface::instruction::SystemInstruction::Transfer { lamports: 1 }; + let result = crate::system_decode(&replay_input(&instruction, 3, false)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + let accounts = match result.observations[0].payload_json["accounts"].as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("decoded System accounts are not an array"), + }; + assert_eq!(accounts.len(), 3); + assert_eq!(accounts[2]["role"], "additional_account"); + } + + #[test] + fn missing_accounts_and_truncated_payload_fail_without_panic() { + let instruction = + solana_system_interface::instruction::SystemInstruction::Transfer { lamports: 1 }; + let missing = crate::system_decode(&replay_input(&instruction, 1, false)); + assert_eq!(missing.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let truncated = crate::system_decode(&replay_input_bytes(&[2, 0, 0], 2, false)); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn unknown_tag_is_unsupported_and_hashable_for_replay() { + let result = crate::system_decode(&replay_input_bytes(&[255, 0, 0, 0], 0, false)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Unsupported); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some("unknown_system_instruction") + ); + } + + #[test] + fn failed_transaction_is_decoded_as_uncommitted_intent() { + let instruction = + solana_system_interface::instruction::SystemInstruction::Transfer { lamports: 55 }; + let result = crate::system_decode(&replay_input(&instruction, 2, true)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!result.observations[0].observation_committed); + assert_eq!(result.observations[0].payload_json["transactionSucceeded"], false); + } + + #[test] + fn event_serialization_is_deterministic() { + let instruction = + solana_system_interface::instruction::SystemInstruction::Transfer { lamports: 55 }; + let input = replay_input(&instruction, 2, false); + let first = crate::system_decode(&input); + let second = crate::system_decode(&input); + let first_json = match serde_json::to_string(&first) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("first event serialization failed: {error}"), + }; + let second_json = match serde_json::to_string(&second) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("second event serialization failed: {error}"), + }; + assert_eq!(first_json, second_json); + } + + #[test] + fn coverage_declares_all_current_system_variants() { + let coverage = crate::system_coverage(); + assert_eq!(coverage.len(), 14); + assert!(coverage.iter().any(|entry| { + return entry.entry_code == "create_account_allow_prefund" + && entry.discriminator_hex.as_deref() == std::option::Option::Some("0d000000"); + })); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/vote.rs b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/vote.rs new file mode 100644 index 0000000..cf720a3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/vote.rs @@ -0,0 +1,1058 @@ +// file: kb_decoder_solana_core/src/vote.rs +// version: 5 + +//! Exact Vote Program instruction decoding through the official interface wincode contract. + +const SOURCE: &str = "solana-vote-interface@6.x VoteInstruction wincode contract"; + +const INITIALIZE_ACCOUNT_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("vote_account", true, false), + crate::AccountRole::new("rent_sysvar", false, false), + crate::AccountRole::new("clock_sysvar", false, false), + crate::AccountRole::new("validator_identity", false, true), +]; +const AUTHORIZE_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("vote_account", true, false), + crate::AccountRole::new("clock_sysvar", false, false), + crate::AccountRole::new("current_authority", false, true), +]; +const VOTE_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("vote_account", true, false), + crate::AccountRole::new("slot_hashes_sysvar", false, false), + crate::AccountRole::new("clock_sysvar", false, false), + crate::AccountRole::new("vote_authority", false, true), +]; +const WITHDRAW_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("vote_account", true, false), + crate::AccountRole::new("recipient_account", true, false), + crate::AccountRole::new("withdraw_authority", false, true), +]; +const UPDATE_VALIDATOR_IDENTITY_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("vote_account", true, false), + crate::AccountRole::new("new_validator_identity", false, true), + crate::AccountRole::new("withdraw_authority", false, true), +]; +const UPDATE_COMMISSION_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("vote_account", true, false), + crate::AccountRole::new("withdraw_authority", false, true), +]; +const AUTHORIZE_CHECKED_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("vote_account", true, false), + crate::AccountRole::new("clock_sysvar", false, false), + crate::AccountRole::new("current_authority", false, true), + crate::AccountRole::new("new_authority", false, true), +]; +const VOTE_STATE_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("vote_account", true, false), + crate::AccountRole::new("vote_authority", false, true), +]; +const AUTHORIZE_WITH_SEED_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("vote_account", true, false), + crate::AccountRole::new("clock_sysvar", false, false), + crate::AccountRole::new("current_authority_base", false, true), +]; +const AUTHORIZE_CHECKED_WITH_SEED_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("vote_account", true, false), + crate::AccountRole::new("clock_sysvar", false, false), + crate::AccountRole::new("current_authority_base", false, true), + crate::AccountRole::new("new_authority", false, true), +]; +const INITIALIZE_ACCOUNT_V2_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("vote_account", true, false), + crate::AccountRole::new("validator_identity", false, true), + crate::AccountRole::new("inflation_rewards_collector", true, false), + crate::AccountRole::new("block_revenue_collector", true, false), +]; +const UPDATE_COMMISSION_COLLECTOR_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("vote_account", true, false), + crate::AccountRole::new("new_commission_collector", true, false), + crate::AccountRole::new("withdraw_authority", false, true), +]; +const DEPOSIT_DELEGATOR_REWARDS_ROLES: &[crate::AccountRole] = &[ + crate::AccountRole::new("vote_account", true, false), + crate::AccountRole::new("deposit_source", true, true), +]; + +/// Returns declared Vote Program instruction coverage. +pub(crate) fn vote_coverage() -> std::vec::Vec { + let entries = [ + ("initialize_account", 0_u32), + ("authorize", 1_u32), + ("vote", 2_u32), + ("withdraw", 3_u32), + ("update_validator_identity", 4_u32), + ("update_commission", 5_u32), + ("vote_switch", 6_u32), + ("authorize_checked", 7_u32), + ("update_vote_state", 8_u32), + ("update_vote_state_switch", 9_u32), + ("authorize_with_seed", 10_u32), + ("authorize_checked_with_seed", 11_u32), + ("compact_update_vote_state", 12_u32), + ("compact_update_vote_state_switch", 13_u32), + ("tower_sync", 14_u32), + ("tower_sync_switch", 15_u32), + ("initialize_account_v2", 16_u32), + ("update_commission_collector", 17_u32), + ("update_commission_bps", 18_u32), + ("deposit_delegator_rewards", 19_u32), + ]; + return entries + .into_iter() + .map(|(entry_code, tag)| { + return kb_decoder_api::DecoderCoverageDeclaration { + program_id: kb_program_ids::VOTE_PROGRAM_ID.to_string(), + surface_code: std::option::Option::Some(crate::VOTE_SURFACE_CODE.to_string()), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: entry_code.to_string(), + discriminator_hex: std::option::Option::Some(tag_hex(tag)), + historical: false, + }; + }) + .collect(); +} + +/// Recognizes one Vote Program instruction without producing an event. +pub(crate) fn vote_recognize( + input: &kb_store_core::CoreInstructionReplayInput, + priority: u16, +) -> kb_decoder_api::DecoderRecognition { + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return kb_decoder_api::DecoderRecognition::compatible( + false, + priority, + std::option::Option::Some(crate::VOTE_SURFACE_CODE.to_string()), + std::option::Option::Some("malformed_vote_instruction".to_string()), + std::option::Option::None, + ); + }, + }; + let tag_result = crate::read_u32_le(bytes.as_slice(), 0); + let tag = match tag_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return kb_decoder_api::DecoderRecognition::compatible( + false, + priority, + std::option::Option::Some(crate::VOTE_SURFACE_CODE.to_string()), + std::option::Option::Some("malformed_vote_instruction".to_string()), + crate::hexadecimal_prefix(bytes.as_slice(), bytes.len().min(4)), + ); + }, + }; + let entry_code = entry_code(tag); + return kb_decoder_api::DecoderRecognition::compatible( + entry_code.is_some(), + priority, + std::option::Option::Some(crate::VOTE_SURFACE_CODE.to_string()), + entry_code.map(str::to_string).or_else(|| { + return std::option::Option::Some("unknown_vote_instruction".to_string()); + }), + std::option::Option::Some(tag_hex(tag)), + ); +} + +/// Decodes one Vote Program instruction. +pub(crate) fn vote_decode( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_decoder_api::DecoderExecutionResult { + let bytes_result = crate::decode_instruction_data(input); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("malformed_vote_instruction"), + "vote_payload_invalid", + error.to_string(), + ); + }, + }; + let tag_result = crate::read_u32_le(bytes.as_slice(), 0); + let tag = match tag_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("malformed_vote_instruction"), + "vote_tag_truncated", + error.to_string(), + ); + }, + }; + let entry_code = match entry_code(tag) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return crate::unsupported_result( + "unknown_vote_instruction", + "vote_tag_unknown", + format!( + "unknown Vote Program instruction tag {tag}; payload_sha256={}", + crate::payload_hash(input) + ), + ); + }, + }; + let instruction_result = wincode::deserialize_exact::< + solana_vote_interface::instruction::VoteInstruction, + >(bytes.as_slice()); + let instruction = match instruction_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some(entry_code), + "vote_wincode_invalid", + format!("cannot decode exact Vote Program {entry_code}: {error}"), + ); + }, + }; + return decoded_instruction(input, instruction); +} + +fn decoded_instruction( + input: &kb_store_core::CoreInstructionReplayInput, + instruction: solana_vote_interface::instruction::VoteInstruction, +) -> kb_decoder_api::DecoderExecutionResult { + return match instruction { + solana_vote_interface::instruction::VoteInstruction::InitializeAccount(value) => build( + input, + "initialize_account", + kb_model::EventFamily::Lifecycle, + INITIALIZE_ACCOUNT_ROLES, + 4, + &[ + (1, kb_program_ids::SYSVAR_RENT_PROGRAM_ID), + (2, kb_program_ids::SYSVAR_CLOCK_PROGRAM_ID), + ], + vote_init_json(value), + ), + solana_vote_interface::instruction::VoteInstruction::Authorize( + new_authority, + authorization_type, + ) => build( + input, + "authorize", + kb_model::EventFamily::Admin, + AUTHORIZE_ROLES, + 3, + &[(1, kb_program_ids::SYSVAR_CLOCK_PROGRAM_ID)], + serde_json::json!({ + "newAuthority": new_authority.to_string(), + "authorizationType": vote_authorize_json(authorization_type), + }), + ), + solana_vote_interface::instruction::VoteInstruction::Vote(value) => build( + input, + "vote", + kb_model::EventFamily::Staking, + VOTE_ROLES, + 4, + &[ + (1, kb_program_ids::SYSVAR_SLOT_HASHES_PROGRAM_ID), + (2, kb_program_ids::SYSVAR_CLOCK_PROGRAM_ID), + ], + vote_json(value), + ), + solana_vote_interface::instruction::VoteInstruction::Withdraw(lamports) => build( + input, + "withdraw", + kb_model::EventFamily::Staking, + WITHDRAW_ROLES, + 3, + &[], + serde_json::json!({"lamports": lamports}), + ), + solana_vote_interface::instruction::VoteInstruction::UpdateValidatorIdentity => build( + input, + "update_validator_identity", + kb_model::EventFamily::Admin, + UPDATE_VALIDATOR_IDENTITY_ROLES, + 3, + &[], + serde_json::json!({}), + ), + solana_vote_interface::instruction::VoteInstruction::UpdateCommission(commission) => build( + input, + "update_commission", + kb_model::EventFamily::Admin, + UPDATE_COMMISSION_ROLES, + 2, + &[], + serde_json::json!({"commissionPercent": commission}), + ), + solana_vote_interface::instruction::VoteInstruction::VoteSwitch( + value, + switch_proof_hash, + ) => { + let mut parameters = vote_json(value); + parameters["switchProofHash"] = + serde_json::Value::String(switch_proof_hash.to_string()); + build( + input, + "vote_switch", + kb_model::EventFamily::Staking, + VOTE_ROLES, + 4, + &[ + (1, kb_program_ids::SYSVAR_SLOT_HASHES_PROGRAM_ID), + (2, kb_program_ids::SYSVAR_CLOCK_PROGRAM_ID), + ], + parameters, + ) + }, + solana_vote_interface::instruction::VoteInstruction::AuthorizeChecked( + authorization_type, + ) => build( + input, + "authorize_checked", + kb_model::EventFamily::Admin, + AUTHORIZE_CHECKED_ROLES, + 4, + &[(1, kb_program_ids::SYSVAR_CLOCK_PROGRAM_ID)], + serde_json::json!({ + "authorizationType": vote_authorize_json(authorization_type), + "newAuthorityFromAccount": true, + }), + ), + solana_vote_interface::instruction::VoteInstruction::UpdateVoteState(value) => build( + input, + "update_vote_state", + kb_model::EventFamily::Staking, + VOTE_STATE_ROLES, + 2, + &[], + vote_state_update_json(value, false), + ), + solana_vote_interface::instruction::VoteInstruction::UpdateVoteStateSwitch( + value, + switch_proof_hash, + ) => { + let mut parameters = vote_state_update_json(value, false); + parameters["switchProofHash"] = + serde_json::Value::String(switch_proof_hash.to_string()); + build( + input, + "update_vote_state_switch", + kb_model::EventFamily::Staking, + VOTE_STATE_ROLES, + 2, + &[], + parameters, + ) + }, + solana_vote_interface::instruction::VoteInstruction::AuthorizeWithSeed(value) => build( + input, + "authorize_with_seed", + kb_model::EventFamily::Admin, + AUTHORIZE_WITH_SEED_ROLES, + 3, + &[(1, kb_program_ids::SYSVAR_CLOCK_PROGRAM_ID)], + serde_json::json!({ + "authorizationType": vote_authorize_json(value.authorization_type), + "currentAuthorityDerivedKeyOwner": value.current_authority_derived_key_owner.to_string(), + "currentAuthorityDerivedKeySeed": value.current_authority_derived_key_seed, + "newAuthority": value.new_authority.to_string(), + }), + ), + solana_vote_interface::instruction::VoteInstruction::AuthorizeCheckedWithSeed(value) => { + build( + input, + "authorize_checked_with_seed", + kb_model::EventFamily::Admin, + AUTHORIZE_CHECKED_WITH_SEED_ROLES, + 4, + &[(1, kb_program_ids::SYSVAR_CLOCK_PROGRAM_ID)], + serde_json::json!({ + "authorizationType": vote_authorize_json(value.authorization_type), + "currentAuthorityDerivedKeyOwner": value.current_authority_derived_key_owner.to_string(), + "currentAuthorityDerivedKeySeed": value.current_authority_derived_key_seed, + "newAuthorityFromAccount": true, + }), + ) + }, + solana_vote_interface::instruction::VoteInstruction::CompactUpdateVoteState(value) => { + build( + input, + "compact_update_vote_state", + kb_model::EventFamily::Staking, + VOTE_STATE_ROLES, + 2, + &[], + vote_state_update_json(value, true), + ) + }, + solana_vote_interface::instruction::VoteInstruction::CompactUpdateVoteStateSwitch( + value, + switch_proof_hash, + ) => { + let mut parameters = vote_state_update_json(value, true); + parameters["switchProofHash"] = + serde_json::Value::String(switch_proof_hash.to_string()); + build( + input, + "compact_update_vote_state_switch", + kb_model::EventFamily::Staking, + VOTE_STATE_ROLES, + 2, + &[], + parameters, + ) + }, + solana_vote_interface::instruction::VoteInstruction::TowerSync(value) => build( + input, + "tower_sync", + kb_model::EventFamily::Staking, + VOTE_STATE_ROLES, + 2, + &[], + tower_sync_json(value), + ), + solana_vote_interface::instruction::VoteInstruction::TowerSyncSwitch( + value, + switch_proof_hash, + ) => { + let mut parameters = tower_sync_json(value); + parameters["switchProofHash"] = + serde_json::Value::String(switch_proof_hash.to_string()); + build( + input, + "tower_sync_switch", + kb_model::EventFamily::Staking, + VOTE_STATE_ROLES, + 2, + &[], + parameters, + ) + }, + solana_vote_interface::instruction::VoteInstruction::InitializeAccountV2(value) => build( + input, + "initialize_account_v2", + kb_model::EventFamily::Lifecycle, + INITIALIZE_ACCOUNT_V2_ROLES, + 4, + &[], + vote_init_v2_json(value), + ), + solana_vote_interface::instruction::VoteInstruction::UpdateCommissionCollector(kind) => { + build( + input, + "update_commission_collector", + kb_model::EventFamily::Admin, + UPDATE_COMMISSION_COLLECTOR_ROLES, + 3, + &[], + serde_json::json!({"commissionKind": commission_kind(kind)}), + ) + }, + solana_vote_interface::instruction::VoteInstruction::UpdateCommissionBps { + commission_bps, + kind, + } => build( + input, + "update_commission_bps", + kb_model::EventFamily::Admin, + UPDATE_COMMISSION_ROLES, + 2, + &[], + serde_json::json!({ + "commissionBasisPoints": commission_bps, + "commissionKind": commission_kind(kind), + }), + ), + solana_vote_interface::instruction::VoteInstruction::DepositDelegatorRewards { + deposit, + } => build( + input, + "deposit_delegator_rewards", + kb_model::EventFamily::Reward, + DEPOSIT_DELEGATOR_REWARDS_ROLES, + 2, + &[], + serde_json::json!({"depositLamports": deposit}), + ), + }; +} + +fn build( + input: &kb_store_core::CoreInstructionReplayInput, + entry_code: &str, + event_family: kb_model::EventFamily, + roles: &[crate::AccountRole], + minimum_count: usize, + fixed_accounts: &[(usize, &str)], + parameters: serde_json::Value, +) -> kb_decoder_api::DecoderExecutionResult { + let accounts_result = + crate::resolve_accounts(input, roles, minimum_count, std::option::Option::None); + let accounts = match accounts_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some(entry_code), + "vote_accounts_invalid", + error.to_string(), + ); + }, + }; + let fixed_result = validate_fixed_accounts(&accounts, fixed_accounts); + if let std::result::Result::Err(error) = fixed_result { + return crate::failed_result( + std::option::Option::Some(entry_code), + "vote_fixed_account_invalid", + error.to_string(), + ); + } + return crate::decoded_result( + input, + crate::VOTE_SURFACE_CODE, + entry_code, + event_family, + false, + accounts, + parameters, + SOURCE, + ); +} + +fn validate_fixed_accounts( + accounts: &serde_json::Value, + fixed_accounts: &[(usize, &str)], +) -> kb_core::Result<()> { + let array = match accounts.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "resolved Vote Program accounts must be an array", + )); + }, + }; + for (position, expected) in fixed_accounts { + let actual = array + .get(*position) + .and_then(|value| return value.get("accountKey")) + .and_then(serde_json::Value::as_str); + if actual != std::option::Option::Some(*expected) { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Vote Program account {position} must be {expected}" + ))); + } + } + return std::result::Result::Ok(()); +} + +fn vote_init_json(value: solana_vote_interface::state::VoteInit) -> serde_json::Value { + return serde_json::json!({ + "nodePubkey": value.node_pubkey.to_string(), + "authorizedVoter": value.authorized_voter.to_string(), + "authorizedWithdrawer": value.authorized_withdrawer.to_string(), + "commissionPercent": value.commission, + }); +} + +fn vote_init_v2_json(value: solana_vote_interface::state::VoteInitV2) -> serde_json::Value { + return serde_json::json!({ + "nodePubkey": value.node_pubkey.to_string(), + "authorizedVoter": value.authorized_voter.to_string(), + "authorizedVoterBlsPubkeyHex": hex_bytes(&value.authorized_voter_bls_pubkey), + "authorizedVoterBlsProofOfPossessionHex": hex_bytes(&value.authorized_voter_bls_proof_of_possession), + "authorizedWithdrawer": value.authorized_withdrawer.to_string(), + "inflationRewardsCommissionBasisPoints": value.inflation_rewards_commission_bps, + "blockRevenueCommissionBasisPoints": value.block_revenue_commission_bps, + }); +} + +fn vote_authorize_json(value: solana_vote_interface::state::VoteAuthorize) -> serde_json::Value { + return match value { + solana_vote_interface::state::VoteAuthorize::Voter => { + serde_json::json!({"kind": "voter"}) + }, + solana_vote_interface::state::VoteAuthorize::Withdrawer => { + serde_json::json!({"kind": "withdrawer"}) + }, + solana_vote_interface::state::VoteAuthorize::VoterWithBLS(arguments) => { + serde_json::json!({ + "kind": "voter_with_bls", + "blsPubkeyHex": hex_bytes(&arguments.bls_pubkey), + "blsProofOfPossessionHex": hex_bytes(&arguments.bls_proof_of_possession), + }) + }, + }; +} + +fn vote_json(value: solana_vote_interface::state::Vote) -> serde_json::Value { + let last_voted_slot = value.last_voted_slot(); + let bank_hash = value.hash.to_string(); + let timestamp = value.timestamp; + return serde_json::json!({ + "slots": value.slots, + "lastVotedSlot": last_voted_slot, + "bankHash": bank_hash, + "timestamp": timestamp, + }); +} + +fn vote_state_update_json( + value: solana_vote_interface::state::VoteStateUpdate, + compact_encoding: bool, +) -> serde_json::Value { + let lockouts = value + .lockouts + .iter() + .map(|lockout| { + return serde_json::json!({ + "slot": lockout.slot(), + "confirmationCount": lockout.confirmation_count(), + }); + }) + .collect::>(); + return serde_json::json!({ + "lockouts": lockouts, + "root": value.root, + "lastVotedSlot": value.last_voted_slot(), + "bankHash": value.hash.to_string(), + "timestamp": value.timestamp, + "compactEncoding": compact_encoding, + }); +} + +fn tower_sync_json(value: solana_vote_interface::state::TowerSync) -> serde_json::Value { + let lockouts = value + .lockouts + .iter() + .map(|lockout| { + return serde_json::json!({ + "slot": lockout.slot(), + "confirmationCount": lockout.confirmation_count(), + }); + }) + .collect::>(); + return serde_json::json!({ + "lockouts": lockouts, + "root": value.root, + "lastVotedSlot": value.last_voted_slot(), + "bankHash": value.hash.to_string(), + "timestamp": value.timestamp, + "blockId": value.block_id.to_string(), + "compactEncoding": true, + }); +} + +fn commission_kind(kind: solana_vote_interface::instruction::CommissionKind) -> &'static str { + return match kind { + solana_vote_interface::instruction::CommissionKind::InflationRewards => "inflation_rewards", + solana_vote_interface::instruction::CommissionKind::BlockRevenue => "block_revenue", + }; +} + +fn entry_code(tag: u32) -> std::option::Option<&'static str> { + return match tag { + 0 => std::option::Option::Some("initialize_account"), + 1 => std::option::Option::Some("authorize"), + 2 => std::option::Option::Some("vote"), + 3 => std::option::Option::Some("withdraw"), + 4 => std::option::Option::Some("update_validator_identity"), + 5 => std::option::Option::Some("update_commission"), + 6 => std::option::Option::Some("vote_switch"), + 7 => std::option::Option::Some("authorize_checked"), + 8 => std::option::Option::Some("update_vote_state"), + 9 => std::option::Option::Some("update_vote_state_switch"), + 10 => std::option::Option::Some("authorize_with_seed"), + 11 => std::option::Option::Some("authorize_checked_with_seed"), + 12 => std::option::Option::Some("compact_update_vote_state"), + 13 => std::option::Option::Some("compact_update_vote_state_switch"), + 14 => std::option::Option::Some("tower_sync"), + 15 => std::option::Option::Some("tower_sync_switch"), + 16 => std::option::Option::Some("initialize_account_v2"), + 17 => std::option::Option::Some("update_commission_collector"), + 18 => std::option::Option::Some("update_commission_bps"), + 19 => std::option::Option::Some("deposit_delegator_rewards"), + _ => std::option::Option::None, + }; +} + +fn tag_hex(tag: u32) -> std::string::String { + let bytes = tag.to_le_bytes(); + return format!("{:02x}{:02x}{:02x}{:02x}", bytes[0], bytes[1], bytes[2], bytes[3]); +} + +fn hex_bytes(bytes: &[u8]) -> std::string::String { + let mut output = std::string::String::with_capacity(bytes.len().saturating_mul(2)); + for byte in bytes { + output.push_str(format!("{byte:02x}").as_str()); + } + return output; +} + +#[cfg(test)] +mod tests { + use base64::Engine; // rust-rules: trait-import + + fn fixtures() + -> std::vec::Vec<(solana_vote_interface::instruction::VoteInstruction, usize, &'static str)> + { + let pubkey = solana_sdk_ids::system_program::id(); + let vote = solana_vote_interface::state::Vote { + slots: vec![1, 2], + ..solana_vote_interface::state::Vote::default() + }; + let update = solana_vote_interface::state::VoteStateUpdate::from(vec![(1, 2), (2, 1)]); + let tower = solana_vote_interface::state::TowerSync::from(vec![(1, 2), (2, 1)]); + return vec![ + ( + solana_vote_interface::instruction::VoteInstruction::InitializeAccount( + solana_vote_interface::state::VoteInit::default(), + ), + 4, + "initialize_account", + ), + ( + solana_vote_interface::instruction::VoteInstruction::Authorize( + pubkey, + solana_vote_interface::state::VoteAuthorize::Voter, + ), + 3, + "authorize", + ), + ( + solana_vote_interface::instruction::VoteInstruction::Vote(vote.clone()), + 4, + "vote", + ), + ( + solana_vote_interface::instruction::VoteInstruction::Withdraw(u64::MAX), + 3, + "withdraw", + ), + ( + solana_vote_interface::instruction::VoteInstruction::UpdateValidatorIdentity, + 3, + "update_validator_identity", + ), + ( + solana_vote_interface::instruction::VoteInstruction::UpdateCommission(u8::MAX), + 2, + "update_commission", + ), + ( + solana_vote_interface::instruction::VoteInstruction::VoteSwitch( + vote, + std::default::Default::default(), + ), + 4, + "vote_switch", + ), + ( + solana_vote_interface::instruction::VoteInstruction::AuthorizeChecked( + solana_vote_interface::state::VoteAuthorize::Withdrawer, + ), + 4, + "authorize_checked", + ), + ( + solana_vote_interface::instruction::VoteInstruction::UpdateVoteState( + update.clone(), + ), + 2, + "update_vote_state", + ), + ( + solana_vote_interface::instruction::VoteInstruction::UpdateVoteStateSwitch( + update.clone(), + std::default::Default::default(), + ), + 2, + "update_vote_state_switch", + ), + ( + solana_vote_interface::instruction::VoteInstruction::AuthorizeWithSeed( + solana_vote_interface::state::VoteAuthorizeWithSeedArgs { + authorization_type: solana_vote_interface::state::VoteAuthorize::Voter, + current_authority_derived_key_owner: pubkey, + current_authority_derived_key_seed: "seed".to_string(), + new_authority: pubkey, + }, + ), + 3, + "authorize_with_seed", + ), + ( + solana_vote_interface::instruction::VoteInstruction::AuthorizeCheckedWithSeed( + solana_vote_interface::state::VoteAuthorizeCheckedWithSeedArgs { + authorization_type: solana_vote_interface::state::VoteAuthorize::Withdrawer, + current_authority_derived_key_owner: pubkey, + current_authority_derived_key_seed: "seed".to_string(), + }, + ), + 4, + "authorize_checked_with_seed", + ), + ( + solana_vote_interface::instruction::VoteInstruction::CompactUpdateVoteState( + update.clone(), + ), + 2, + "compact_update_vote_state", + ), + ( + solana_vote_interface::instruction::VoteInstruction::CompactUpdateVoteStateSwitch( + update, + std::default::Default::default(), + ), + 2, + "compact_update_vote_state_switch", + ), + ( + solana_vote_interface::instruction::VoteInstruction::TowerSync(tower.clone()), + 2, + "tower_sync", + ), + ( + solana_vote_interface::instruction::VoteInstruction::TowerSyncSwitch( + tower, + std::default::Default::default(), + ), + 2, + "tower_sync_switch", + ), + ( + solana_vote_interface::instruction::VoteInstruction::InitializeAccountV2( + solana_vote_interface::state::VoteInitV2::default(), + ), + 4, + "initialize_account_v2", + ), + ( + solana_vote_interface::instruction::VoteInstruction::UpdateCommissionCollector( + solana_vote_interface::instruction::CommissionKind::InflationRewards, + ), + 3, + "update_commission_collector", + ), + ( + solana_vote_interface::instruction::VoteInstruction::UpdateCommissionBps { + commission_bps: u16::MAX, + kind: solana_vote_interface::instruction::CommissionKind::BlockRevenue, + }, + 2, + "update_commission_bps", + ), + ( + solana_vote_interface::instruction::VoteInstruction::DepositDelegatorRewards { + deposit: u64::MAX, + }, + 2, + "deposit_delegator_rewards", + ), + ]; + } + + fn replay_input( + instruction: &solana_vote_interface::instruction::VoteInstruction, + account_count: usize, + entry_code: &str, + transaction_failed: bool, + ) -> kb_store_core::CoreInstructionReplayInput { + let bytes_result = wincode::serialize(instruction); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Vote fixture serialization failed: {error}"), + }; + return replay_input_bytes(bytes.as_slice(), account_count, entry_code, transaction_failed); + } + + fn replay_input_bytes( + bytes: &[u8], + account_count: usize, + entry_code: &str, + transaction_failed: bool, + ) -> kb_store_core::CoreInstructionReplayInput { + let account_keys = (0..account_count) + .map(|index| { + let key = account_key(entry_code, index); + return serde_json::json!({ + "accountIndex": index, + "accountKey": key, + "source": "static", + "writable": true, + "signer": true, + "executable": false, + }); + }) + .collect::>(); + let instruction_accounts = (0..account_count) + .map(|index| { + return serde_json::json!({ + "accountIndex": index, + "accountKey": account_key(entry_code, index), + }); + }) + .collect::>(); + let result = kb_store_core::CoreInstructionReplayInput::new( + "signature:0", + "signature", + 42, + "0", + kb_program_ids::VOTE_PROGRAM_ID, + transaction_failed, + if transaction_failed { + std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]})) + } else { + std::option::Option::None + }, + serde_json::Value::Array(account_keys), + serde_json::Value::Array(instruction_accounts), + std::option::Option::Some(serde_json::json!({ + "dataBase64": base64::engine::general_purpose::STANDARD.encode(bytes), + })), + std::option::Option::Some("payload-hash".to_string()), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Vote replay input failed: {error}"), + }; + } + + fn account_key(entry_code: &str, index: usize) -> std::string::String { + if entry_code == "initialize_account" && index == 1 { + return kb_program_ids::SYSVAR_RENT_PROGRAM_ID.to_string(); + } + if matches!(entry_code, "vote" | "vote_switch") && index == 1 { + return kb_program_ids::SYSVAR_SLOT_HASHES_PROGRAM_ID.to_string(); + } + if (matches!(entry_code, "vote" | "vote_switch") || entry_code == "initialize_account") + && index == 2 + || (matches!( + entry_code, + "authorize" + | "authorize_checked" + | "authorize_with_seed" + | "authorize_checked_with_seed" + ) && index == 1) + { + return kb_program_ids::SYSVAR_CLOCK_PROGRAM_ID.to_string(); + } + return format!("VoteAccount{index}111111111111111111111111111"); + } + + #[test] + fn coverage_declares_every_official_vote_instruction() { + let coverage = crate::vote_coverage(); + assert_eq!(coverage.len(), 20); + for tag in 0_u32..20_u32 { + let expected = super::tag_hex(tag); + assert!(coverage.iter().any(|entry| { + return entry.discriminator_hex.as_deref() + == std::option::Option::Some(expected.as_str()); + })); + } + } + + #[test] + fn every_official_vote_variant_decodes() { + for (instruction, account_count, entry_code) in fixtures() { + let input = replay_input(&instruction, account_count, entry_code, false); + let result = crate::vote_decode(&input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some(entry_code) + ); + assert_eq!(result.observations.len(), 1); + assert!(result.observations[0].observation_committed); + } + } + + #[test] + fn voter_with_bls_arguments_are_preserved_exactly() { + let instruction = solana_vote_interface::instruction::VoteInstruction::Authorize( + solana_sdk_ids::system_program::id(), + solana_vote_interface::state::VoteAuthorize::VoterWithBLS( + solana_vote_interface::state::VoterWithBLSArgs::default(), + ), + ); + let input = replay_input(&instruction, 3, "authorize", false); + let result = crate::vote_decode(&input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.observations[0].payload_json["parameters"]["authorizationType"]["kind"], + "voter_with_bls" + ); + assert_eq!( + result.observations[0].payload_json["parameters"]["authorizationType"]["blsPubkeyHex"] + .as_str() + .map(str::len), + std::option::Option::Some(96) + ); + } + + #[test] + fn initialize_account_v2_collectors_are_instruction_accounts() { + let instruction = solana_vote_interface::instruction::VoteInstruction::InitializeAccountV2( + solana_vote_interface::state::VoteInitV2::default(), + ); + let input = replay_input(&instruction, 4, "initialize_account_v2", false); + let result = crate::vote_decode(&input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + let payload = &result.observations[0].payload_json; + assert_eq!(payload["accounts"][2]["role"], "inflation_rewards_collector"); + assert_eq!(payload["accounts"][3]["role"], "block_revenue_collector"); + assert!(payload["parameters"].get("inflationRewardsCollector").is_none()); + assert!(payload["parameters"].get("blockRevenueCollector").is_none()); + } + + #[test] + fn truncated_unknown_and_trailing_payloads_are_distinguished() { + let truncated = + crate::vote_decode(&replay_input_bytes(&[0, 0, 0], 4, "initialize_account", false)); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let unknown = crate::vote_decode(&replay_input_bytes( + &[99, 0, 0, 0], + 1, + "unknown_vote_instruction", + false, + )); + assert_eq!(unknown.status, kb_decoder_api::DecoderOutcomeStatus::Unsupported); + let fixture = solana_vote_interface::instruction::VoteInstruction::Withdraw(1); + let bytes_result = wincode::serialize(&fixture); + let mut bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Vote fixture serialization failed: {error}"), + }; + bytes.push(0); + let trailing = + crate::vote_decode(&replay_input_bytes(bytes.as_slice(), 3, "withdraw", false)); + assert_eq!(trailing.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn missing_or_wrong_fixed_accounts_fail_safely() { + let instruction = solana_vote_interface::instruction::VoteInstruction::Vote( + solana_vote_interface::state::Vote::default(), + ); + let missing = crate::vote_decode(&replay_input(&instruction, 3, "vote", false)); + assert_eq!(missing.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let mut wrong = replay_input(&instruction, 4, "vote", false); + wrong.instruction_accounts_json[1]["accountKey"] = + serde_json::Value::String("WrongSysvar1111111111111111111111111111".to_string()); + wrong.account_keys_json[1]["accountKey"] = + serde_json::Value::String("WrongSysvar1111111111111111111111111111".to_string()); + let wrong_result = crate::vote_decode(&wrong); + assert_eq!(wrong_result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn failed_vote_instruction_is_decoded_as_uncommitted_intent() { + let instruction = solana_vote_interface::instruction::VoteInstruction::Withdraw(1); + let input = replay_input(&instruction, 3, "withdraw", true); + let first = crate::vote_decode(&input); + let second = crate::vote_decode(&input); + assert_eq!(first.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!first.observations[0].observation_committed); + assert_eq!(first.observations[0].payload_json, second.observations[0].payload_json); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/zk_elgamal.rs b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/zk_elgamal.rs new file mode 100644 index 0000000..7b7e50a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/zk_elgamal.rs @@ -0,0 +1,708 @@ +// file: kb_decoder_solana_core/src/zk_elgamal.rs +// version: 3 + +//! Exact structural decoding for the native ZK ElGamal Proof program. + +const SOURCE: &str = "solana-zk-elgamal-proof-interface@^0.1 ProofInstruction and Pod layouts; Agave native runtime proof-account mode"; +const PROOF_ACCOUNT_INSTRUCTION_BYTES: usize = 5; +const PROOF_COMPONENT_PREFIX_BYTES: usize = 16; + +#[derive(Clone, Copy, Debug)] +struct ProofDescriptor { + entry_code: &'static str, + proof_data_size: usize, + context_data_size: usize, +} + +/// Returns declared ZK ElGamal Proof instruction coverage. +pub(crate) fn zk_elgamal_coverage() -> std::vec::Vec { + return instruction_entries() + .iter() + .map(|(tag, entry_code)| { + return kb_decoder_api::DecoderCoverageDeclaration { + program_id: kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID.to_string(), + surface_code: std::option::Option::Some( + crate::ZK_ELGAMAL_PROOF_SURFACE_CODE.to_string(), + ), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: (*entry_code).to_string(), + discriminator_hex: std::option::Option::Some(format!("{tag:02x}")), + historical: false, + }; + }) + .collect(); +} + +/// Recognizes one native ZK ElGamal Proof instruction. +pub(crate) fn zk_elgamal_recognize( + input: &kb_store_core::CoreInstructionReplayInput, + priority: u16, +) -> kb_decoder_api::DecoderRecognition { + let bytes = match crate::decode_instruction_data(input) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return kb_decoder_api::DecoderRecognition::compatible( + false, + priority, + std::option::Option::Some(crate::ZK_ELGAMAL_PROOF_SURFACE_CODE.to_string()), + std::option::Option::Some("malformed_zk_elgamal_proof_instruction".to_string()), + std::option::Option::None, + ); + }, + }; + let instruction = + match solana_zk_elgamal_proof_interface::instruction::ProofInstruction::instruction_type( + bytes.as_slice(), + ) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + let entry_code = if bytes.is_empty() { + "malformed_zk_elgamal_proof_instruction" + } else { + "unknown_zk_elgamal_proof_instruction" + }; + return kb_decoder_api::DecoderRecognition::compatible( + false, + priority, + std::option::Option::Some(crate::ZK_ELGAMAL_PROOF_SURFACE_CODE.to_string()), + std::option::Option::Some(entry_code.to_string()), + crate::hexadecimal_prefix(bytes.as_slice(), bytes.len().min(1)), + ); + }, + }; + let (entry_code, exact) = match instruction { + solana_zk_elgamal_proof_interface::instruction::ProofInstruction::CloseContextState => { + ("close_context_state", true) + }, + _ => { + let descriptor = proof_descriptor(instruction); + let descriptor = match descriptor { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return kb_decoder_api::DecoderRecognition::compatible( + false, + priority, + std::option::Option::Some(crate::ZK_ELGAMAL_PROOF_SURFACE_CODE.to_string()), + std::option::Option::Some( + "unknown_zk_elgamal_proof_instruction".to_string(), + ), + crate::hexadecimal_prefix(bytes.as_slice(), 1), + ); + }, + }; + let inline_size = descriptor.proof_data_size.saturating_add(1); + ( + descriptor.entry_code, + bytes.len() == PROOF_ACCOUNT_INSTRUCTION_BYTES || bytes.len() == inline_size, + ) + }, + }; + return kb_decoder_api::DecoderRecognition::compatible( + exact, + priority, + std::option::Option::Some(crate::ZK_ELGAMAL_PROOF_SURFACE_CODE.to_string()), + std::option::Option::Some(entry_code.to_string()), + crate::hexadecimal_prefix(bytes.as_slice(), 1), + ); +} + +/// Decodes one native ZK ElGamal Proof instruction without recomputing the proof. +pub(crate) fn zk_elgamal_decode( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_decoder_api::DecoderExecutionResult { + let bytes = match crate::decode_instruction_data(input) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("malformed_zk_elgamal_proof_instruction"), + "zk_elgamal_payload_invalid", + error.to_string(), + ); + }, + }; + let instruction = + match solana_zk_elgamal_proof_interface::instruction::ProofInstruction::instruction_type( + bytes.as_slice(), + ) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + if bytes.is_empty() { + return crate::failed_result( + std::option::Option::Some("malformed_zk_elgamal_proof_instruction"), + "zk_elgamal_payload_empty", + "ZK ElGamal Proof instruction payload is empty", + ); + } + let tag = match bytes.first() { + std::option::Option::Some(value) => *value, + std::option::Option::None => { + return crate::failed_result( + std::option::Option::Some("malformed_zk_elgamal_proof_instruction"), + "zk_elgamal_payload_empty", + "ZK ElGamal Proof instruction payload is empty", + ); + }, + }; + return crate::unsupported_result( + "unknown_zk_elgamal_proof_instruction", + "zk_elgamal_tag_unknown", + format!( + "unknown ZK ElGamal Proof instruction tag {tag}; payload_sha256={}", + crate::payload_hash(input) + ), + ); + }, + }; + if instruction + == solana_zk_elgamal_proof_interface::instruction::ProofInstruction::CloseContextState + { + return decode_close_context_state(input, bytes.as_slice()); + } + let descriptor = match proof_descriptor(instruction) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return crate::unsupported_result( + "unknown_zk_elgamal_proof_instruction", + "zk_elgamal_instruction_unmapped", + "official ZK ElGamal Proof instruction has no local descriptor", + ); + }, + }; + return decode_verify_proof(input, bytes.as_slice(), descriptor); +} + +fn decode_close_context_state( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], +) -> kb_decoder_api::DecoderExecutionResult { + let roles = [ + crate::AccountRole::new("proof_context_state", true, false), + crate::AccountRole::new("lamport_destination", true, false), + crate::AccountRole::new("context_state_authority", false, true), + ]; + let accounts = + match crate::resolve_accounts(input, roles.as_slice(), 3, std::option::Option::None) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("close_context_state"), + "zk_elgamal_close_accounts_invalid", + error.to_string(), + ); + }, + }; + let account_array = match accounts.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return crate::failed_result( + std::option::Option::Some("close_context_state"), + "zk_elgamal_close_accounts_not_array", + "resolved ZK ElGamal close accounts are not an array", + ); + }, + }; + let context_key = account_array + .first() + .and_then(|value| return value.get("accountKey")) + .and_then(serde_json::Value::as_str); + let destination_key = account_array + .get(1) + .and_then(|value| return value.get("accountKey")) + .and_then(serde_json::Value::as_str); + if context_key.is_some() && context_key == destination_key { + return crate::failed_result( + std::option::Option::Some("close_context_state"), + "zk_elgamal_close_destination_matches_context", + "ZK ElGamal proof context account and lamport destination must differ", + ); + } + let trailing = match bytes.get(1..) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return crate::failed_result( + std::option::Option::Some("close_context_state"), + "zk_elgamal_close_payload_missing_tag", + "ZK ElGamal close-context instruction has no discriminator byte", + ); + }, + }; + return crate::decoded_result( + input, + crate::ZK_ELGAMAL_PROOF_SURFACE_CODE, + "close_context_state", + kb_model::EventFamily::Lifecycle, + false, + accounts, + serde_json::json!({ + "instructionMode": "close_context_state", + "runtimeAcceptsTrailingBytes": true, + "trailingByteLength": trailing.len(), + "trailingSha256": crate::hash_bytes(trailing), + "trailingPrefixHex": crate::bounded_hexadecimal_prefix( + trailing, + PROOF_COMPONENT_PREFIX_BYTES, + ), + "reclaimsLamports": true, + "resetsOwnerToSystemProgram": true, + "runtimeVerification": runtime_verification(input), + }), + SOURCE, + ); +} + +fn decode_verify_proof( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], + descriptor: ProofDescriptor, +) -> kb_decoder_api::DecoderExecutionResult { + let external_proof = bytes.len() == PROOF_ACCOUNT_INSTRUCTION_BYTES; + let expected_inline_size = descriptor.proof_data_size.saturating_add(1); + if !external_proof && bytes.len() != expected_inline_size { + return crate::failed_result( + std::option::Option::Some(descriptor.entry_code), + "zk_elgamal_proof_data_size_invalid", + format!( + "{} requires either {} bytes for proof-account mode or {} bytes for inline proof data but received {}", + descriptor.entry_code, + PROOF_ACCOUNT_INSTRUCTION_BYTES, + expected_inline_size, + bytes.len() + ), + ); + } + let account_count = match input.instruction_accounts_json.as_array() { + std::option::Option::Some(value) => value.len(), + std::option::Option::None => { + return crate::failed_result( + std::option::Option::Some(descriptor.entry_code), + "zk_elgamal_accounts_not_array", + "ZK ElGamal Proof instruction accounts are not an array", + ); + }, + }; + let context_state_requested = + if external_proof { account_count >= 3 } else { account_count >= 2 }; + let mut roles = std::vec::Vec::new(); + if external_proof { + roles.push(crate::AccountRole::new("proof_data_account", false, false)); + } + if context_state_requested { + roles.push(crate::AccountRole::new("proof_context_state", true, false)); + roles.push(crate::AccountRole::new("context_state_authority", false, false)); + } + let minimum_accounts = if external_proof { 1 } else { 0 }; + let accounts = match crate::resolve_accounts( + input, + roles.as_slice(), + minimum_accounts, + std::option::Option::None, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some(descriptor.entry_code), + "zk_elgamal_accounts_invalid", + error.to_string(), + ); + }, + }; + let proof_parameters = if external_proof { + let offset = match crate::read_u32_le(bytes, 1) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some(descriptor.entry_code), + "zk_elgamal_proof_account_offset_invalid", + error.to_string(), + ); + }, + }; + serde_json::json!({ + "proofDataSource": "account", + "proofAccountOffset": offset, + "proofDataAvailability": "external_account_not_captured_by_transaction_core", + "proofDataByteLength": descriptor.proof_data_size, + "contextDataByteLength": descriptor.context_data_size, + "proofBodyByteLength": descriptor.proof_data_size.saturating_sub(descriptor.context_data_size), + "proofDataSha256": serde_json::Value::Null, + "contextDataSha256": serde_json::Value::Null, + "proofBodySha256": serde_json::Value::Null, + }) + } else { + let proof_data = match bytes.get(1..) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return crate::failed_result( + std::option::Option::Some(descriptor.entry_code), + "zk_elgamal_inline_proof_missing", + "inline ZK ElGamal proof data is missing", + ); + }, + }; + let context_data = match proof_data.get(..descriptor.context_data_size) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return crate::failed_result( + std::option::Option::Some(descriptor.entry_code), + "zk_elgamal_inline_context_truncated", + "inline ZK ElGamal proof context data is truncated", + ); + }, + }; + let proof_body = match proof_data.get(descriptor.context_data_size..) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return crate::failed_result( + std::option::Option::Some(descriptor.entry_code), + "zk_elgamal_inline_proof_body_truncated", + "inline ZK ElGamal proof body is truncated", + ); + }, + }; + serde_json::json!({ + "proofDataSource": "instruction_data", + "proofAccountOffset": serde_json::Value::Null, + "proofDataAvailability": "inline_hashed_not_retained", + "proofDataByteLength": proof_data.len(), + "contextDataByteLength": context_data.len(), + "proofBodyByteLength": proof_body.len(), + "proofDataSha256": crate::hash_bytes(proof_data), + "proofDataPrefixHex": crate::bounded_hexadecimal_prefix( + proof_data, + PROOF_COMPONENT_PREFIX_BYTES, + ), + "contextDataSha256": crate::hash_bytes(context_data), + "contextDataPrefixHex": crate::bounded_hexadecimal_prefix( + context_data, + PROOF_COMPONENT_PREFIX_BYTES, + ), + "proofBodySha256": crate::hash_bytes(proof_body), + "proofBodyPrefixHex": crate::bounded_hexadecimal_prefix( + proof_body, + PROOF_COMPONENT_PREFIX_BYTES, + ), + }) + }; + return crate::decoded_result( + input, + crate::ZK_ELGAMAL_PROOF_SURFACE_CODE, + descriptor.entry_code, + kb_model::EventFamily::Audit, + false, + accounts, + serde_json::json!({ + "proofType": descriptor.entry_code, + "proof": proof_parameters, + "contextStateRequested": context_state_requested, + "contextStateMutation": if context_state_requested { + "initialize_after_successful_verification" + } else { + "none" + }, + "cryptographicVerificationPerformedByDecoder": false, + "runtimeVerification": runtime_verification(input), + }), + SOURCE, + ); +} + +fn proof_descriptor( + instruction: solana_zk_elgamal_proof_interface::instruction::ProofInstruction, +) -> std::option::Option { + let descriptor = match instruction { + solana_zk_elgamal_proof_interface::instruction::ProofInstruction::CloseContextState => { + return std::option::Option::None; + }, + solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyZeroCiphertext => { + ProofDescriptor { + entry_code: "verify_zero_ciphertext", + proof_data_size: std::mem::size_of::(), + context_data_size: std::mem::size_of::(), + } + }, + solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyCiphertextCiphertextEquality => ProofDescriptor { + entry_code: "verify_ciphertext_ciphertext_equality", + proof_data_size: std::mem::size_of::(), + context_data_size: std::mem::size_of::(), + }, + solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyCiphertextCommitmentEquality => ProofDescriptor { + entry_code: "verify_ciphertext_commitment_equality", + proof_data_size: std::mem::size_of::(), + context_data_size: std::mem::size_of::(), + }, + solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyPubkeyValidity => { + ProofDescriptor { + entry_code: "verify_pubkey_validity", + proof_data_size: std::mem::size_of::(), + context_data_size: std::mem::size_of::(), + } + }, + solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyPercentageWithCap => ProofDescriptor { + entry_code: "verify_percentage_with_cap", + proof_data_size: std::mem::size_of::(), + context_data_size: std::mem::size_of::(), + }, + solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyBatchedRangeProofU64 => ProofDescriptor { + entry_code: "verify_batched_range_proof_u64", + proof_data_size: std::mem::size_of::(), + context_data_size: std::mem::size_of::(), + }, + solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyBatchedRangeProofU128 => ProofDescriptor { + entry_code: "verify_batched_range_proof_u128", + proof_data_size: std::mem::size_of::(), + context_data_size: std::mem::size_of::(), + }, + solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyBatchedRangeProofU256 => ProofDescriptor { + entry_code: "verify_batched_range_proof_u256", + proof_data_size: std::mem::size_of::(), + context_data_size: std::mem::size_of::(), + }, + solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyGroupedCiphertext2HandlesValidity => ProofDescriptor { + entry_code: "verify_grouped_ciphertext_2_handles_validity", + proof_data_size: std::mem::size_of::(), + context_data_size: std::mem::size_of::(), + }, + solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity => ProofDescriptor { + entry_code: "verify_batched_grouped_ciphertext_2_handles_validity", + proof_data_size: std::mem::size_of::(), + context_data_size: std::mem::size_of::(), + }, + solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyGroupedCiphertext3HandlesValidity => ProofDescriptor { + entry_code: "verify_grouped_ciphertext_3_handles_validity", + proof_data_size: std::mem::size_of::(), + context_data_size: std::mem::size_of::(), + }, + solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity => ProofDescriptor { + entry_code: "verify_batched_grouped_ciphertext_3_handles_validity", + proof_data_size: std::mem::size_of::(), + context_data_size: std::mem::size_of::(), + }, + }; + return std::option::Option::Some(descriptor); +} + +fn runtime_verification(input: &kb_store_core::CoreInstructionReplayInput) -> &'static str { + if input.transaction_failed { + return "not_asserted_transaction_failed"; + } + return "accepted_in_successful_transaction"; +} + +fn instruction_entries() -> &'static [(u8, &'static str)] { + return &[ + (0, "close_context_state"), + (1, "verify_zero_ciphertext"), + (2, "verify_ciphertext_ciphertext_equality"), + (3, "verify_ciphertext_commitment_equality"), + (4, "verify_pubkey_validity"), + (5, "verify_percentage_with_cap"), + (6, "verify_batched_range_proof_u64"), + (7, "verify_batched_range_proof_u128"), + (8, "verify_batched_range_proof_u256"), + (9, "verify_grouped_ciphertext_2_handles_validity"), + (10, "verify_batched_grouped_ciphertext_2_handles_validity"), + (11, "verify_grouped_ciphertext_3_handles_validity"), + (12, "verify_batched_grouped_ciphertext_3_handles_validity"), + ]; +} + +#[cfg(test)] +mod tests { + use base64::Engine; // rust-rules: trait-import + + fn replay_input( + bytes: &[u8], + account_count: usize, + transaction_failed: bool, + ) -> kb_store_core::CoreInstructionReplayInput { + let account_keys = (0..account_count) + .map(|index| { + return serde_json::json!({ + "accountIndex": index, + "accountKey": format!("ZkAccount{index:02}111111111111111111111111111"), + "source": "static", + "writable": true, + "signer": index == 2, + "executable": false, + }); + }) + .collect::>(); + let instruction_accounts = account_keys + .iter() + .map(|account| { + return serde_json::json!({ + "accountIndex": account["accountIndex"], + "accountKey": account["accountKey"], + }); + }) + .collect::>(); + let result = kb_store_core::CoreInstructionReplayInput::new( + "signature:0", + "signature", + 42, + "0", + kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID, + transaction_failed, + if transaction_failed { + std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]})) + } else { + std::option::Option::None + }, + serde_json::Value::Array(account_keys), + serde_json::Value::Array(instruction_accounts), + std::option::Option::Some(serde_json::json!({ + "dataBase64": base64::engine::general_purpose::STANDARD.encode(bytes), + })), + std::option::Option::Some("payload-hash".to_string()), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("ZK ElGamal replay input failed: {error}"), + }; + } + + fn inline_fixture( + tag: u8, + instruction: solana_zk_elgamal_proof_interface::instruction::ProofInstruction, + ) -> std::vec::Vec { + let descriptor = match super::proof_descriptor(instruction) { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("proof descriptor missing"), + }; + let mut bytes = vec![tag]; + bytes.resize(descriptor.proof_data_size.saturating_add(1), tag); + return bytes; + } + + #[test] + fn coverage_declares_all_thirteen_official_instructions() { + let coverage = crate::zk_elgamal_coverage(); + assert_eq!(coverage.len(), 13); + for (tag, entry_code) in super::instruction_entries() { + let expected_discriminator = format!("{tag:02x}"); + assert!(coverage.iter().any(|entry| { + return entry.entry_code == *entry_code + && entry.discriminator_hex.as_deref() + == std::option::Option::Some(expected_discriminator.as_str()); + })); + } + } + + #[test] + fn official_discriminants_map_to_stable_entry_codes() { + for (tag, entry_code) in super::instruction_entries() { + let recognition = crate::zk_elgamal_recognize(&replay_input(&[*tag], 3, false), 100); + assert!(recognition.compatible); + assert_eq!(recognition.entry_code.as_deref(), std::option::Option::Some(*entry_code)); + } + } + + #[test] + fn proof_account_mode_decodes_offset_and_context_request() { + let bytes = [1_u8, 44, 0, 0, 0]; + let result = crate::zk_elgamal_decode(&replay_input(&bytes, 3, false)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + let parameters = &result.observations[0].payload_json["parameters"]; + assert_eq!(parameters["proof"]["proofDataSource"], "account"); + assert_eq!(parameters["proof"]["proofAccountOffset"], 44); + assert_eq!(parameters["contextStateRequested"], true); + assert_eq!(parameters["cryptographicVerificationPerformedByDecoder"], false); + assert_eq!(parameters["runtimeVerification"], "accepted_in_successful_transaction"); + } + + #[test] + fn proof_account_mode_requires_the_proof_account() { + let result = crate::zk_elgamal_decode(&replay_input(&[1, 0, 0, 0, 0], 0, false)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn every_inline_proof_layout_is_bounded_by_official_pod_sizes() { + let variants = [ + (1, solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyZeroCiphertext), + (2, solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyCiphertextCiphertextEquality), + (3, solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyCiphertextCommitmentEquality), + (4, solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyPubkeyValidity), + (5, solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyPercentageWithCap), + (6, solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyBatchedRangeProofU64), + (7, solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyBatchedRangeProofU128), + (8, solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyBatchedRangeProofU256), + (9, solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyGroupedCiphertext2HandlesValidity), + (10, solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity), + (11, solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyGroupedCiphertext3HandlesValidity), + (12, solana_zk_elgamal_proof_interface::instruction::ProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity), + ]; + for (tag, instruction) in variants { + let bytes = inline_fixture(tag, instruction); + let result = crate::zk_elgamal_decode(&replay_input(bytes.as_slice(), 0, true)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(result.observations.len(), 1); + assert!(!result.observations[0].observation_committed); + assert_eq!( + result.observations[0].payload_json["parameters"]["proof"]["proofDataByteLength"], + bytes.len().saturating_sub(1) + ); + } + } + + #[test] + fn invalid_inline_sizes_unknown_tags_and_empty_payload_fail_safely() { + let truncated = crate::zk_elgamal_decode(&replay_input(&[1, 2, 3], 0, false)); + assert_eq!(truncated.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let unknown = crate::zk_elgamal_decode(&replay_input(&[99], 0, false)); + assert_eq!(unknown.status, kb_decoder_api::DecoderOutcomeStatus::Unsupported); + let empty = crate::zk_elgamal_decode(&replay_input(&[], 0, false)); + assert_eq!(empty.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn close_context_state_accepts_runtime_trailing_bytes_and_rejects_same_destination() { + let decoded = crate::zk_elgamal_decode(&replay_input(&[0, 7, 8], 3, false)); + assert_eq!(decoded.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(decoded.observations[0].payload_json["parameters"]["trailingByteLength"], 2); + let mut same = replay_input(&[0], 3, false); + let accounts = match same.instruction_accounts_json.as_array_mut() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("instruction accounts missing"), + }; + accounts[1]["accountKey"] = accounts[0]["accountKey"].clone(); + let keys = match same.account_keys_json.as_array_mut() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("account keys missing"), + }; + keys[1]["accountKey"] = keys[0]["accountKey"].clone(); + let failed = crate::zk_elgamal_decode(&same); + assert_eq!(failed.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn failed_transaction_never_claims_runtime_verification_or_commit() { + let result = crate::zk_elgamal_decode(&replay_input(&[4, 0, 0, 0, 0], 1, true)); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!result.observations[0].observation_committed); + assert_eq!( + result.observations[0].payload_json["parameters"]["runtimeVerification"], + "not_asserted_transaction_failed" + ); + } + + #[test] + fn serialization_is_deterministic() { + let input = replay_input(&[1, 0, 0, 0, 0], 1, false); + let first = crate::zk_elgamal_decode(&input); + let second = crate::zk_elgamal_decode(&input); + let first_json = match serde_json::to_string(&first.observations[0].payload_json) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("first serialization failed: {error}"), + }; + let second_json = match serde_json::to_string(&second.observations[0].payload_json) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("second serialization failed: {error}"), + }; + assert_eq!(first_json, second_json); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/zk_token_proof.rs b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/zk_token_proof.rs new file mode 100644 index 0000000..ba8d5b7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_solana_core/src/zk_token_proof.rs @@ -0,0 +1,932 @@ +// file: kb_decoder_solana_core/src/zk_token_proof.rs +// version: 3 + +//! Historical ZK Token Proof interface decoding with explicit current no-op runtime semantics. + +const SOURCE: &str = "local bounded mirror of the historical ZK Token Proof wire contract audited against solana-zk-token-sdk 3.1.14 and Agave v2.0.0; Agave v4.1.1 no-op runtime stub"; +const PROOF_ACCOUNT_INSTRUCTION_BYTES: usize = 5; +const PROOF_COMPONENT_PREFIX_BYTES: usize = 16; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u8)] +enum HistoricalProofInstruction { + CloseContextState = 0, + VerifyZeroBalance = 1, + VerifyWithdraw = 2, + VerifyCiphertextCiphertextEquality = 3, + VerifyTransfer = 4, + VerifyTransferWithFee = 5, + VerifyPubkeyValidity = 6, + VerifyRangeProofU64 = 7, + VerifyBatchedRangeProofU64 = 8, + VerifyBatchedRangeProofU128 = 9, + VerifyBatchedRangeProofU256 = 10, + VerifyCiphertextCommitmentEquality = 11, + VerifyGroupedCiphertext2HandlesValidity = 12, + VerifyBatchedGroupedCiphertext2HandlesValidity = 13, + VerifyFeeSigma = 14, + VerifyGroupedCiphertext3HandlesValidity = 15, + VerifyBatchedGroupedCiphertext3HandlesValidity = 16, +} + +impl HistoricalProofInstruction { + fn from_tag(tag: u8) -> std::option::Option { + return match tag { + 0 => std::option::Option::Some(Self::CloseContextState), + 1 => std::option::Option::Some(Self::VerifyZeroBalance), + 2 => std::option::Option::Some(Self::VerifyWithdraw), + 3 => std::option::Option::Some(Self::VerifyCiphertextCiphertextEquality), + 4 => std::option::Option::Some(Self::VerifyTransfer), + 5 => std::option::Option::Some(Self::VerifyTransferWithFee), + 6 => std::option::Option::Some(Self::VerifyPubkeyValidity), + 7 => std::option::Option::Some(Self::VerifyRangeProofU64), + 8 => std::option::Option::Some(Self::VerifyBatchedRangeProofU64), + 9 => std::option::Option::Some(Self::VerifyBatchedRangeProofU128), + 10 => std::option::Option::Some(Self::VerifyBatchedRangeProofU256), + 11 => std::option::Option::Some(Self::VerifyCiphertextCommitmentEquality), + 12 => std::option::Option::Some(Self::VerifyGroupedCiphertext2HandlesValidity), + 13 => std::option::Option::Some(Self::VerifyBatchedGroupedCiphertext2HandlesValidity), + 14 => std::option::Option::Some(Self::VerifyFeeSigma), + 15 => std::option::Option::Some(Self::VerifyGroupedCiphertext3HandlesValidity), + 16 => std::option::Option::Some(Self::VerifyBatchedGroupedCiphertext3HandlesValidity), + _ => std::option::Option::None, + }; + } +} + +fn historical_instruction(bytes: &[u8]) -> std::option::Option { + return bytes.first().copied().and_then(HistoricalProofInstruction::from_tag); +} + +#[derive(Clone, Copy, Debug)] +struct ProofDescriptor { + entry_code: &'static str, + proof_data_size: usize, + context_data_size: usize, + historical_inline_availability: &'static str, +} + +/// Returns declared historical and current ZK Token Proof coverage. +pub(crate) fn zk_token_proof_coverage() -> std::vec::Vec +{ + let mut coverage = instruction_entries() + .iter() + .map(|(tag, entry_code)| { + return kb_decoder_api::DecoderCoverageDeclaration { + program_id: kb_program_ids::ZK_TOKEN_PROOF_PROGRAM_ID.to_string(), + surface_code: std::option::Option::Some( + crate::ZK_TOKEN_PROOF_SURFACE_CODE.to_string(), + ), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: (*entry_code).to_string(), + discriminator_hex: std::option::Option::Some(format!("{tag:02x}")), + historical: true, + }; + }) + .collect::>(); + coverage.push(kb_decoder_api::DecoderCoverageDeclaration { + program_id: kb_program_ids::ZK_TOKEN_PROOF_PROGRAM_ID.to_string(), + surface_code: std::option::Option::Some(crate::ZK_TOKEN_PROOF_SURFACE_CODE.to_string()), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: "current_runtime_noop_invocation".to_string(), + discriminator_hex: std::option::Option::None, + historical: false, + }); + return coverage; +} + +/// Recognizes one historical ZK Token Proof layout or the current no-op runtime fallback. +pub(crate) fn zk_token_proof_recognize( + input: &kb_store_core::CoreInstructionReplayInput, + priority: u16, +) -> kb_decoder_api::DecoderRecognition { + let bytes = match crate::decode_instruction_data(input) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return kb_decoder_api::DecoderRecognition::compatible( + false, + priority, + std::option::Option::Some(crate::ZK_TOKEN_PROOF_SURFACE_CODE.to_string()), + std::option::Option::Some("malformed_zk_token_proof_instruction".to_string()), + std::option::Option::None, + ); + }, + }; + let instruction = historical_instruction(bytes.as_slice()); + let entry_code = match instruction { + std::option::Option::Some(HistoricalProofInstruction::CloseContextState) => { + "close_context_state" + }, + std::option::Option::Some(value) => { + let descriptor = proof_descriptor(value); + match descriptor { + std::option::Option::Some(value) => { + let inline_size = value.proof_data_size.checked_add(1); + if bytes.len() == PROOF_ACCOUNT_INSTRUCTION_BYTES + || inline_size.is_some_and(|size| return bytes.len() == size) + { + value.entry_code + } else { + "current_runtime_noop_invocation" + } + }, + std::option::Option::None => "current_runtime_noop_invocation", + } + }, + std::option::Option::None => "current_runtime_noop_invocation", + }; + return kb_decoder_api::DecoderRecognition::compatible( + true, + priority, + std::option::Option::Some(crate::ZK_TOKEN_PROOF_SURFACE_CODE.to_string()), + std::option::Option::Some(entry_code.to_string()), + crate::hexadecimal_prefix(bytes.as_slice(), bytes.len().min(1)), + ); +} + +/// Decodes one historical ZK Token Proof layout without recomputing any proof. +pub(crate) fn zk_token_proof_decode( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_decoder_api::DecoderExecutionResult { + let bytes = match crate::decode_instruction_data(input) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("malformed_zk_token_proof_instruction"), + "zk_token_proof_payload_invalid", + error.to_string(), + ); + }, + }; + let instruction = historical_instruction(bytes.as_slice()); + let instruction = match instruction { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return decode_current_noop( + input, + bytes.as_slice(), + "empty_or_unknown_historical_discriminator", + ); + }, + }; + if instruction == HistoricalProofInstruction::CloseContextState { + return decode_close_context_state(input, bytes.as_slice()); + } + let descriptor = match proof_descriptor(instruction) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return decode_current_noop(input, bytes.as_slice(), "unmapped_historical_instruction"); + }, + }; + let inline_size = match descriptor.proof_data_size.checked_add(1) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return crate::failed_result( + std::option::Option::Some(descriptor.entry_code), + "zk_token_proof_inline_size_overflow", + "historical ZK Token Proof inline size overflowed usize", + ); + }, + }; + if bytes.len() != PROOF_ACCOUNT_INSTRUCTION_BYTES && bytes.len() != inline_size { + return decode_current_noop(input, bytes.as_slice(), "historical_proof_size_mismatch"); + } + return decode_verify_proof(input, bytes.as_slice(), descriptor); +} + +fn decode_current_noop( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], + historical_parse_status: &str, +) -> kb_decoder_api::DecoderExecutionResult { + let accounts = match crate::resolve_accounts(input, &[], 0, std::option::Option::None) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("current_runtime_noop_invocation"), + "zk_token_proof_accounts_invalid", + error.to_string(), + ); + }, + }; + return crate::decoded_result( + input, + crate::ZK_TOKEN_PROOF_SURFACE_CODE, + "current_runtime_noop_invocation", + kb_model::EventFamily::Audit, + false, + accounts, + serde_json::json!({ + "instructionMode": "opaque_current_runtime_noop", + "historicalParseStatus": historical_parse_status, + "historicalDiscriminator": bytes.first().copied(), + "payloadByteLength": bytes.len(), + "payloadSha256": crate::hash_bytes(bytes), + "payloadPrefixHex": crate::bounded_hexadecimal_prefix( + bytes, + PROOF_COMPONENT_PREFIX_BYTES, + ), + "historicalActivationEvidence": "no_public_activation_epoch_documented_in_feature_gate_issue", + "decoderValidationBasis": "official_interface_runtime_sources_and_synthetic_fixtures", + "historicalRuntimeReference": "agave_v2_0_0_gated_verifier", + "currentRuntimeReference": "agave_v4_1_1_no_op_success_stub", + "currentRuntimeBehavior": "no_op_success_stub", + "cryptographicVerificationPerformedByDecoder": false, + "cryptographicVerificationClaim": verification_claim(input), + "runtimeExecutionOutcome": runtime_execution_outcome(input), + }), + SOURCE, + ); +} + +fn decode_close_context_state( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], +) -> kb_decoder_api::DecoderExecutionResult { + let roles = [ + crate::AccountRole::new("proof_context_state", true, false), + crate::AccountRole::new("lamport_destination", true, false), + crate::AccountRole::new("context_state_authority", false, true), + ]; + let accounts = + match crate::resolve_accounts(input, roles.as_slice(), 0, std::option::Option::None) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some("close_context_state"), + "zk_token_proof_close_accounts_invalid", + error.to_string(), + ); + }, + }; + let account_array = match accounts.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return crate::failed_result( + std::option::Option::Some("close_context_state"), + "zk_token_proof_close_accounts_not_array", + "resolved ZK Token Proof close accounts are not an array", + ); + }, + }; + let context_key = account_array + .first() + .and_then(|value| return value.get("accountKey")) + .and_then(serde_json::Value::as_str); + let destination_key = account_array + .get(1) + .and_then(|value| return value.get("accountKey")) + .and_then(serde_json::Value::as_str); + let historical_account_layout_valid = account_array.len() >= 3 + && context_key.is_some() + && destination_key.is_some() + && context_key != destination_key; + let trailing = match bytes.get(1..) { + std::option::Option::Some(value) => value, + std::option::Option::None => &[], + }; + return crate::decoded_result( + input, + crate::ZK_TOKEN_PROOF_SURFACE_CODE, + "close_context_state", + kb_model::EventFamily::Audit, + true, + accounts, + serde_json::json!({ + "instructionMode": "historical_close_context_state", + "historicalAccountLayoutValid": historical_account_layout_valid, + "historicalRuntimeAcceptsTrailingBytes": true, + "trailingByteLength": trailing.len(), + "trailingSha256": crate::hash_bytes(trailing), + "trailingPrefixHex": crate::bounded_hexadecimal_prefix( + trailing, + PROOF_COMPONENT_PREFIX_BYTES, + ), + "historicalIntendedMutation": "close_context_and_reclaim_lamports", + "historicalInnerInstructionSupport": true, + "historicalActivationEvidence": "no_public_activation_epoch_documented_in_feature_gate_issue", + "decoderValidationBasis": "official_interface_runtime_sources_and_synthetic_fixtures", + "historicalRuntimeReference": "agave_v2_0_0_gated_verifier", + "currentRuntimeReference": "agave_v4_1_1_no_op_success_stub", + "currentRuntimeBehavior": "no_op_success_stub", + "currentRuntimeMutation": "none", + "stateMutationClaim": "not_asserted", + "runtimeExecutionOutcome": runtime_execution_outcome(input), + }), + SOURCE, + ); +} + +fn decode_verify_proof( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], + descriptor: ProofDescriptor, +) -> kb_decoder_api::DecoderExecutionResult { + let external_proof = bytes.len() == PROOF_ACCOUNT_INSTRUCTION_BYTES; + let account_count = match input.instruction_accounts_json.as_array() { + std::option::Option::Some(value) => value.len(), + std::option::Option::None => { + return crate::failed_result( + std::option::Option::Some(descriptor.entry_code), + "zk_token_proof_accounts_not_array", + "ZK Token Proof instruction accounts are not an array", + ); + }, + }; + let historical_account_layout = historical_account_layout(external_proof, account_count); + let context_state_requested = + if external_proof { account_count >= 3 } else { account_count >= 2 }; + let mut roles = std::vec::Vec::new(); + if external_proof { + roles.push(crate::AccountRole::new("proof_data_account", false, false)); + } + if context_state_requested { + roles.push(crate::AccountRole::new("proof_context_state", true, false)); + roles.push(crate::AccountRole::new("context_state_authority", false, false)); + } + let accounts = + match crate::resolve_accounts(input, roles.as_slice(), 0, std::option::Option::None) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some(descriptor.entry_code), + "zk_token_proof_accounts_invalid", + error.to_string(), + ); + }, + }; + let proof_body_size = match descriptor.proof_data_size.checked_sub(descriptor.context_data_size) + { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return crate::failed_result( + std::option::Option::Some(descriptor.entry_code), + "zk_token_proof_official_size_inconsistent", + "historical ZK Token Proof context size exceeds proof data size", + ); + }, + }; + let proof_parameters = if external_proof { + let offset = match crate::read_u32_le(bytes, 1) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::failed_result( + std::option::Option::Some(descriptor.entry_code), + "zk_token_proof_account_offset_invalid", + error.to_string(), + ); + }, + }; + serde_json::json!({ + "proofDataSource": "account", + "proofAccountOffset": offset, + "proofDataAvailability": "external_account_not_captured_by_transaction_core", + "proofDataByteLength": descriptor.proof_data_size, + "contextDataByteLength": descriptor.context_data_size, + "proofBodyByteLength": proof_body_size, + "proofDataSha256": serde_json::Value::Null, + "contextDataSha256": serde_json::Value::Null, + "proofBodySha256": serde_json::Value::Null, + }) + } else { + let proof_data = match bytes.get(1..) { + std::option::Option::Some(value) => value, + std::option::Option::None => &[], + }; + let context_data = match proof_data.get(..descriptor.context_data_size) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return crate::failed_result( + std::option::Option::Some(descriptor.entry_code), + "zk_token_proof_inline_context_truncated", + "inline historical ZK Token Proof context data is truncated", + ); + }, + }; + let proof_body = match proof_data.get(descriptor.context_data_size..) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return crate::failed_result( + std::option::Option::Some(descriptor.entry_code), + "zk_token_proof_inline_body_truncated", + "inline historical ZK Token Proof body is truncated", + ); + }, + }; + serde_json::json!({ + "proofDataSource": "instruction_data", + "proofAccountOffset": serde_json::Value::Null, + "proofDataAvailability": "inline_hashed_not_retained", + "proofDataByteLength": proof_data.len(), + "contextDataByteLength": context_data.len(), + "proofBodyByteLength": proof_body.len(), + "proofDataSha256": crate::hash_bytes(proof_data), + "proofDataPrefixHex": crate::bounded_hexadecimal_prefix( + proof_data, + PROOF_COMPONENT_PREFIX_BYTES, + ), + "contextDataSha256": crate::hash_bytes(context_data), + "contextDataPrefixHex": crate::bounded_hexadecimal_prefix( + context_data, + PROOF_COMPONENT_PREFIX_BYTES, + ), + "proofBodySha256": crate::hash_bytes(proof_body), + "proofBodyPrefixHex": crate::bounded_hexadecimal_prefix( + proof_body, + PROOF_COMPONENT_PREFIX_BYTES, + ), + }) + }; + let historical_variant_availability = if external_proof { + "gated_by_enable_zk_proof_from_account" + } else { + descriptor.historical_inline_availability + }; + return crate::decoded_result( + input, + crate::ZK_TOKEN_PROOF_SURFACE_CODE, + descriptor.entry_code, + kb_model::EventFamily::Audit, + true, + accounts, + serde_json::json!({ + "proofType": descriptor.entry_code, + "proof": proof_parameters, + "historicalAccountLayout": historical_account_layout, + "historicalAccountLayoutValid": historical_account_layout != "invalid_for_historical_interface", + "contextStateRequested": context_state_requested, + "historicalContextStateMutation": if context_state_requested { + "initialize_after_successful_verification" + } else { + "none" + }, + "historicalVariantAvailability": historical_variant_availability, + "historicalInnerInstructionSupport": false, + "historicalActivationEvidence": "no_public_activation_epoch_documented_in_feature_gate_issue", + "decoderValidationBasis": "official_interface_runtime_sources_and_synthetic_fixtures", + "historicalRuntimeReference": "agave_v2_0_0_gated_verifier", + "currentRuntimeReference": "agave_v4_1_1_no_op_success_stub", + "currentRuntimeBehavior": "no_op_success_stub", + "cryptographicVerificationPerformedByDecoder": false, + "cryptographicVerificationClaim": verification_claim(input), + "runtimeExecutionOutcome": runtime_execution_outcome(input), + }), + SOURCE, + ); +} + +fn historical_account_layout(external_proof: bool, account_count: usize) -> &'static str { + if external_proof { + return match account_count { + 1 => "proof_account_without_context", + 3.. => "proof_account_with_context", + _ => "invalid_for_historical_interface", + }; + } + return match account_count { + 0 => "inline_without_context", + 2.. => "inline_with_context", + _ => "invalid_for_historical_interface", + }; +} + +fn runtime_execution_outcome(input: &kb_store_core::CoreInstructionReplayInput) -> &'static str { + if input.transaction_failed { + return "not_asserted_transaction_failed"; + } + return "accepted_in_successful_transaction"; +} + +fn verification_claim(input: &kb_store_core::CoreInstructionReplayInput) -> &'static str { + if input.transaction_failed { + return "not_asserted_transaction_failed"; + } + return "not_asserted_decoder_does_not_bind_transaction_to_runtime_version"; +} + +fn proof_descriptor( + instruction: HistoricalProofInstruction, +) -> std::option::Option { + let descriptor = match instruction { + HistoricalProofInstruction::CloseContextState => { + return std::option::Option::None; + }, + HistoricalProofInstruction::VerifyZeroBalance => ProofDescriptor { + entry_code: "verify_zero_balance", + proof_data_size: 192, + context_data_size: 96, + historical_inline_availability: "historical_runtime_implementation_present_activation_unconfirmed", + }, + HistoricalProofInstruction::VerifyWithdraw => ProofDescriptor { + entry_code: "verify_withdraw", + proof_data_size: 992, + context_data_size: 96, + historical_inline_availability: "historical_runtime_implementation_present_activation_unconfirmed", + }, + HistoricalProofInstruction::VerifyCiphertextCiphertextEquality => ProofDescriptor { + entry_code: "verify_ciphertext_ciphertext_equality", + proof_data_size: 416, + context_data_size: 192, + historical_inline_availability: "historical_runtime_implementation_present_activation_unconfirmed", + }, + HistoricalProofInstruction::VerifyTransfer => ProofDescriptor { + entry_code: "verify_transfer", + proof_data_size: 1536, + context_data_size: 416, + historical_inline_availability: "historical_runtime_implementation_present_activation_unconfirmed", + }, + HistoricalProofInstruction::VerifyTransferWithFee => ProofDescriptor { + entry_code: "verify_transfer_with_fee", + proof_data_size: 2282, + context_data_size: 650, + historical_inline_availability: "gated_by_enable_zk_transfer_with_fee", + }, + HistoricalProofInstruction::VerifyPubkeyValidity => ProofDescriptor { + entry_code: "verify_pubkey_validity", + proof_data_size: 96, + context_data_size: 32, + historical_inline_availability: "historical_runtime_implementation_present_activation_unconfirmed", + }, + HistoricalProofInstruction::VerifyRangeProofU64 => ProofDescriptor { + entry_code: "verify_range_proof_u64", + proof_data_size: 704, + context_data_size: 32, + historical_inline_availability: "historical_runtime_implementation_present_activation_unconfirmed", + }, + HistoricalProofInstruction::VerifyBatchedRangeProofU64 => ProofDescriptor { + entry_code: "verify_batched_range_proof_u64", + proof_data_size: 936, + context_data_size: 264, + historical_inline_availability: "historical_runtime_implementation_present_activation_unconfirmed", + }, + HistoricalProofInstruction::VerifyBatchedRangeProofU128 => ProofDescriptor { + entry_code: "verify_batched_range_proof_u128", + proof_data_size: 1000, + context_data_size: 264, + historical_inline_availability: "historical_runtime_implementation_present_activation_unconfirmed", + }, + HistoricalProofInstruction::VerifyBatchedRangeProofU256 => ProofDescriptor { + entry_code: "verify_batched_range_proof_u256", + proof_data_size: 1064, + context_data_size: 264, + historical_inline_availability: "gated_by_enable_zk_transfer_with_fee", + }, + HistoricalProofInstruction::VerifyCiphertextCommitmentEquality => ProofDescriptor { + entry_code: "verify_ciphertext_commitment_equality", + proof_data_size: 320, + context_data_size: 128, + historical_inline_availability: "historical_runtime_implementation_present_activation_unconfirmed", + }, + HistoricalProofInstruction::VerifyGroupedCiphertext2HandlesValidity => ProofDescriptor { + entry_code: "verify_grouped_ciphertext_2_handles_validity", + proof_data_size: 320, + context_data_size: 160, + historical_inline_availability: "historical_runtime_implementation_present_activation_unconfirmed", + }, + HistoricalProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity => { + ProofDescriptor { + entry_code: "verify_batched_grouped_ciphertext_2_handles_validity", + proof_data_size: 416, + context_data_size: 256, + historical_inline_availability: "historical_runtime_implementation_present_activation_unconfirmed", + } + }, + HistoricalProofInstruction::VerifyFeeSigma => ProofDescriptor { + entry_code: "verify_fee_sigma", + proof_data_size: 360, + context_data_size: 104, + historical_inline_availability: "gated_by_enable_zk_transfer_with_fee", + }, + HistoricalProofInstruction::VerifyGroupedCiphertext3HandlesValidity => ProofDescriptor { + entry_code: "verify_grouped_ciphertext_3_handles_validity", + proof_data_size: 416, + context_data_size: 224, + historical_inline_availability: "historical_runtime_implementation_present_activation_unconfirmed", + }, + HistoricalProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity => { + ProofDescriptor { + entry_code: "verify_batched_grouped_ciphertext_3_handles_validity", + proof_data_size: 544, + context_data_size: 352, + historical_inline_availability: "historical_runtime_implementation_present_activation_unconfirmed", + } + }, + }; + return std::option::Option::Some(descriptor); +} + +fn instruction_entries() -> &'static [(u8, &'static str)] { + return &[ + (0, "close_context_state"), + (1, "verify_zero_balance"), + (2, "verify_withdraw"), + (3, "verify_ciphertext_ciphertext_equality"), + (4, "verify_transfer"), + (5, "verify_transfer_with_fee"), + (6, "verify_pubkey_validity"), + (7, "verify_range_proof_u64"), + (8, "verify_batched_range_proof_u64"), + (9, "verify_batched_range_proof_u128"), + (10, "verify_batched_range_proof_u256"), + (11, "verify_ciphertext_commitment_equality"), + (12, "verify_grouped_ciphertext_2_handles_validity"), + (13, "verify_batched_grouped_ciphertext_2_handles_validity"), + (14, "verify_fee_sigma"), + (15, "verify_grouped_ciphertext_3_handles_validity"), + (16, "verify_batched_grouped_ciphertext_3_handles_validity"), + ]; +} + +#[cfg(test)] +mod tests { + use base64::Engine; // rust-rules: trait-import + + fn replay_input( + bytes: &[u8], + account_count: usize, + transaction_failed: bool, + instruction_path: &str, + ) -> kb_store_core::CoreInstructionReplayInput { + let account_keys = (0..account_count) + .map(|index| { + return serde_json::json!({ + "accountIndex": index, + "accountKey": format!("ZkToken{index:02}1111111111111111111111111"), + "source": "static", + "writable": index < 2, + "signer": index == 2, + "executable": false, + }); + }) + .collect::>(); + let instruction_accounts = account_keys + .iter() + .map(|account| { + return serde_json::json!({ + "accountIndex": account["accountIndex"], + "accountKey": account["accountKey"], + }); + }) + .collect::>(); + let result = kb_store_core::CoreInstructionReplayInput::new( + format!("signature:{instruction_path}"), + "signature", + 42, + instruction_path, + kb_program_ids::ZK_TOKEN_PROOF_PROGRAM_ID, + transaction_failed, + if transaction_failed { + std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]})) + } else { + std::option::Option::None + }, + serde_json::Value::Array(account_keys), + serde_json::Value::Array(instruction_accounts), + std::option::Option::Some(serde_json::json!({ + "dataBase64": base64::engine::general_purpose::STANDARD.encode(bytes), + })), + std::option::Option::Some("payload-hash".to_string()), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("ZK Token Proof replay input failed: {error}") + }, + }; + } + + fn inline_fixture(instruction: super::HistoricalProofInstruction) -> std::vec::Vec { + let descriptor = match super::proof_descriptor(instruction) { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("proof descriptor missing"), + }; + let inline_size = match descriptor.proof_data_size.checked_add(1) { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("inline fixture size overflow"), + }; + let tag = instruction as u8; + let mut bytes = vec![tag]; + bytes.resize(inline_size, tag); + return bytes; + } + + #[test] + fn coverage_declares_seventeen_historical_entries_and_current_noop_fallback() { + let coverage = crate::zk_token_proof_coverage(); + assert_eq!(coverage.len(), 18); + assert_eq!(coverage.iter().filter(|entry| return entry.historical).count(), 17); + assert!(coverage.iter().any(|entry| { + return entry.entry_code == "current_runtime_noop_invocation" + && !entry.historical + && entry.discriminator_hex.is_none(); + })); + } + + #[test] + fn official_discriminants_map_to_stable_entry_codes() { + for (tag, entry_code) in super::instruction_entries() { + let bytes = if *tag == 0 { vec![*tag] } else { vec![*tag, 0, 0, 0, 0] }; + let recognition = crate::zk_token_proof_recognize( + &replay_input(bytes.as_slice(), 3, false, "0"), + 100, + ); + assert!(recognition.compatible); + assert_eq!(recognition.entry_code.as_deref(), std::option::Option::Some(*entry_code)); + } + } + + #[test] + fn every_historical_inline_layout_matches_the_audited_wire_table() { + let variants = [ + (super::HistoricalProofInstruction::VerifyZeroBalance, 192, 96), + (super::HistoricalProofInstruction::VerifyWithdraw, 992, 96), + (super::HistoricalProofInstruction::VerifyCiphertextCiphertextEquality, 416, 192), + (super::HistoricalProofInstruction::VerifyTransfer, 1536, 416), + (super::HistoricalProofInstruction::VerifyTransferWithFee, 2282, 650), + (super::HistoricalProofInstruction::VerifyPubkeyValidity, 96, 32), + (super::HistoricalProofInstruction::VerifyRangeProofU64, 704, 32), + (super::HistoricalProofInstruction::VerifyBatchedRangeProofU64, 936, 264), + (super::HistoricalProofInstruction::VerifyBatchedRangeProofU128, 1000, 264), + (super::HistoricalProofInstruction::VerifyBatchedRangeProofU256, 1064, 264), + (super::HistoricalProofInstruction::VerifyCiphertextCommitmentEquality, 320, 128), + ( + super::HistoricalProofInstruction::VerifyGroupedCiphertext2HandlesValidity, + 320, + 160, + ), + ( + super::HistoricalProofInstruction::VerifyBatchedGroupedCiphertext2HandlesValidity, + 416, + 256, + ), + (super::HistoricalProofInstruction::VerifyFeeSigma, 360, 104), + ( + super::HistoricalProofInstruction::VerifyGroupedCiphertext3HandlesValidity, + 416, + 224, + ), + ( + super::HistoricalProofInstruction::VerifyBatchedGroupedCiphertext3HandlesValidity, + 544, + 352, + ), + ]; + for (instruction, proof_data_size, context_data_size) in variants { + let descriptor = match super::proof_descriptor(instruction) { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("proof descriptor missing"), + }; + assert_eq!(descriptor.proof_data_size, proof_data_size); + assert_eq!(descriptor.context_data_size, context_data_size); + let bytes = inline_fixture(instruction); + let result = + crate::zk_token_proof_decode(&replay_input(bytes.as_slice(), 0, true, "0")); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(result.observations.len(), 1); + assert!(!result.observations[0].observation_committed); + assert_eq!( + result.observations[0].payload_json["parameters"]["proof"]["proofDataByteLength"], + bytes.len() - 1 + ); + } + } + + #[test] + fn proof_account_mode_is_structured_and_historical_feature_gate_is_explicit() { + let result = crate::zk_token_proof_decode(&replay_input(&[1, 44, 0, 0, 0], 3, false, "0")); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + let parameters = &result.observations[0].payload_json["parameters"]; + assert_eq!(parameters["proof"]["proofDataSource"], "account"); + assert_eq!(parameters["proof"]["proofAccountOffset"], 44); + assert_eq!( + parameters["historicalVariantAvailability"], + "gated_by_enable_zk_proof_from_account" + ); + assert!(parameters["contextStateRequested"].as_bool().is_some_and(|value| return value)); + } + + #[test] + fn historical_transfer_fee_gates_are_explicit() { + let variants = [ + super::HistoricalProofInstruction::VerifyTransferWithFee, + super::HistoricalProofInstruction::VerifyBatchedRangeProofU256, + super::HistoricalProofInstruction::VerifyFeeSigma, + ]; + for instruction in variants { + let bytes = inline_fixture(instruction); + let result = + crate::zk_token_proof_decode(&replay_input(bytes.as_slice(), 0, false, "0")); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.observations[0].payload_json["parameters"]["historicalVariantAvailability"], + "gated_by_enable_zk_transfer_with_fee" + ); + } + } + + #[test] + fn malformed_historical_shapes_decode_as_current_noop_instead_of_false_failure() { + let fixtures: &[&[u8]] = &[&[], &[99], &[1, 2, 3]]; + for bytes in fixtures { + let result = crate::zk_token_proof_decode(&replay_input(bytes, 1, false, "0")); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some("current_runtime_noop_invocation") + ); + assert_eq!( + result.observations[0].payload_json["parameters"]["currentRuntimeReference"], + "agave_v4_1_1_no_op_success_stub" + ); + assert_eq!( + result.observations[0].payload_json["parameters"]["currentRuntimeBehavior"], + "no_op_success_stub" + ); + } + } + + #[test] + fn historical_account_arity_is_reported_without_overriding_current_noop_acceptance() { + let inline = inline_fixture(super::HistoricalProofInstruction::VerifyZeroBalance); + let inline_result = + crate::zk_token_proof_decode(&replay_input(inline.as_slice(), 1, false, "0")); + assert_eq!(inline_result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(inline_result.observations[0].payload_json["parameters"]["historicalAccountLayoutValid"] + .as_bool() + .is_some_and(|value| return !value)); + let account_result = + crate::zk_token_proof_decode(&replay_input(&[1, 0, 0, 0, 0], 2, false, "0")); + assert_eq!(account_result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(account_result.observations[0].payload_json["parameters"]["historicalAccountLayoutValid"] + .as_bool() + .is_some_and(|value| return !value)); + } + + #[test] + fn close_context_state_preserves_historical_intent_without_claiming_mutation() { + let result = crate::zk_token_proof_decode(&replay_input(&[0, 7, 8], 3, false, "0")); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + let parameters = &result.observations[0].payload_json["parameters"]; + assert!( + parameters["historicalAccountLayoutValid"] + .as_bool() + .is_some_and(|value| return value) + ); + assert_eq!(parameters["trailingByteLength"], 2); + assert!( + parameters["historicalInnerInstructionSupport"] + .as_bool() + .is_some_and(|value| return value) + ); + assert_eq!(parameters["currentRuntimeMutation"], "none"); + assert_eq!(parameters["stateMutationClaim"], "not_asserted"); + } + + #[test] + fn inner_invocation_never_claims_historical_verification() { + let bytes = inline_fixture(super::HistoricalProofInstruction::VerifyPubkeyValidity); + let result = crate::zk_token_proof_decode(&replay_input(bytes.as_slice(), 0, false, "0/1")); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!( + result.observations[0].payload_json["parameters"]["historicalInnerInstructionSupport"] + .as_bool() + .is_some_and(|value| return !value) + ); + assert_eq!( + result.observations[0].payload_json["parameters"]["cryptographicVerificationClaim"], + "not_asserted_decoder_does_not_bind_transaction_to_runtime_version" + ); + } + + #[test] + fn failed_transaction_is_non_committed_and_never_claims_verification() { + let bytes = inline_fixture(super::HistoricalProofInstruction::VerifyZeroBalance); + let result = crate::zk_token_proof_decode(&replay_input(bytes.as_slice(), 0, true, "0")); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!result.observations[0].observation_committed); + assert_eq!( + result.observations[0].payload_json["parameters"]["cryptographicVerificationClaim"], + "not_asserted_transaction_failed" + ); + } + + #[test] + fn serialization_is_deterministic() { + let input = replay_input(&[1, 0, 0, 0, 0], 1, false, "0"); + let first = crate::zk_token_proof_decode(&input); + let second = crate::zk_token_proof_decode(&input); + let first_json = match serde_json::to_string(&first.observations[0].payload_json) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("first serialization failed: {error}"), + }; + let second_json = match serde_json::to_string(&second.observations[0].payload_json) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("second serialization failed: {error}"), + }; + assert_eq!(first_json, second_json); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/Cargo.toml new file mode 100644 index 0000000..2be2643 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_spl_account_compression/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_spl_account_compression" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/README.md b/migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/README.md new file mode 100644 index 0000000..a8efb03 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/README.md @@ -0,0 +1,17 @@ + + + +# kb_decoder_spl_account_compression + +Ce crate réserve la frontière de décodage pour le programme SPL Account Compression. + +## État + +Le program ID et l'identité du crate sont enregistrés, mais aucun payload n'est encore déclaré `decoded`. L'implémentation maximale sera réalisée dans la version dédiée indiquée par le `ROADMAP.md`, à partir des interfaces et fixtures officielles. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs`. +- Le scaffold ne doit produire aucun faux événement avant l'implémentation officielle. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/src/constants.rs new file mode 100644 index 0000000..03c90dc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_spl_account_compression/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_spl_account_compression` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_spl_account_compression"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/src/decoder.rs new file mode 100644 index 0000000..5eae241 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/src/decoder.rs @@ -0,0 +1,55 @@ +// file: kb_decoder_spl_account_compression/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_spl_account_compression` crate. + +/// Reserved decoder implementation for the verified program surface. +#[derive(Clone, Debug, Default)] +pub struct SplAccountCompressionDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::SplAccountCompressionDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_spl_account_compression"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::SPL_ACCOUNT_COMPRESSION_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +#[cfg(test)] +mod tests { + #[test] + fn exposes_every_verified_program_id() { + let decoder = crate::SplAccountCompressionDecoder; + let program_ids = kb_decoder_api::ProtocolDecoder::program_ids(&decoder); + assert_eq!(program_ids.len(), 1); + for program_id in program_ids { + assert!(kb_program_ids::find_registered_program_id(program_id).is_some()); + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/src/lib.rs new file mode 100644 index 0000000..da9a1f6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_account_compression/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_spl_account_compression/src/lib.rs +// version: 2 + +//! Decoder crate for `spl_account_compression`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::SplAccountCompressionDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/Cargo.toml new file mode 100644 index 0000000..80cb46e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/Cargo.toml @@ -0,0 +1,25 @@ +# file: kb_decoder_spl_associated_token_account/Cargo.toml +# version: 4 + +[package] +name = "kb_decoder_spl_associated_token_account" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +base64.workspace = true +borsh.workspace = true +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +kb_store_core = { path = "../kb_store_core" } +serde_json.workspace = true +sha2.workspace = true +spl-associated-token-account-interface.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/README.md b/migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/README.md new file mode 100644 index 0000000..9020bbc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/README.md @@ -0,0 +1,40 @@ + + + +# kb_decoder_spl_associated_token_account + +Ce crate décode exactement le programme SPL Associated Token Account +`ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL`. + +## Surface décodée + +- `Create`, discriminant Borsh `0`, ainsi que sa forme historique vide ; +- `CreateIdempotent`, discriminant `1` ; +- `RecoverNested`, discriminant `2` ; +- cibles SPL Token classique et Token-2022, sans décoder les extensions Token-2022. + +Le décodeur conserve le chemin outer/inner, le statut committed, les comptes ordonnés avec leurs +flags et doublons, les rôles ATA, les adresses observées et les PDA canoniques attendus. Une adresse +incorrecte ou une forme de comptes incomplète reste une intention structurée accompagnée de +diagnostics bornés ; elle n'est jamais corrigée silencieusement. + +La matrice `docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json` et les tests différentiels imposent +l'égalité avec `spl-associated-token-account-interface 2.0.0`, ses builders et son helper PDA. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Matérialisation + +Le décodeur ne reconstruit aucun solde ni snapshot final. Les faits commités sont possédés par +`kb_materializer_token_accounts`, tandis que `kb_materializer_risk` possède uniquement le constat +distinct de récupération d'un anti-pattern nested. Les CPI SPL Token restent propriétaires des +initialisations, transferts et fermetures qu'elles effectuent. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/src/constants.rs new file mode 100644 index 0000000..88a59ec --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/src/constants.rs @@ -0,0 +1,20 @@ +// file: kb_decoder_spl_associated_token_account/src/constants.rs +// version: 3 + +//! Local constants for the `kb_decoder_spl_associated_token_account` crate. Program identifiers live in `kb_program_ids`. + +pub(crate) const SURFACE_CODE: &str = "spl_associated_token_account"; +pub(crate) const EVENT_VERSION: u32 = 1; +pub(crate) const MAX_INSTRUCTION_BYTES: usize = 64; +pub(crate) const MAX_ACCOUNTS: usize = 64; +pub(crate) const MAX_TRANSACTION_KEYS: usize = 4_096; +pub(crate) const MAX_ACCOUNT_KEY_BYTES: usize = 128; +pub(crate) const MAX_DIAGNOSTICS: usize = 32; +pub(crate) const INSTRUCTION_ENTRIES: &[(u8, &str, bool)] = &[ + (0, "create", false), + (1, "create_idempotent", false), + (2, "recover_nested", false), +]; + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_spl_associated_token_account"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/src/decoder.rs new file mode 100644 index 0000000..36cebf0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/src/decoder.rs @@ -0,0 +1,619 @@ +// file: kb_decoder_spl_associated_token_account/src/decoder.rs +// version: 10 + +//! Exact SPL Associated Token Account dispatch for the common decode pipeline. + +const SURFACES: &[kb_decoder_api::DecoderSurface] = &[kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID, + surface_code: crate::SURFACE_CODE, + priority: 100, +}]; + +const PROGRAM_IDS: &[&str] = &[kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID]; + +/// Exact decoder for the SPL Associated Token Account program. +#[derive(Clone, Debug, Default)] +pub struct SplAssociatedTokenAccountDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::SplAssociatedTokenAccountDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_spl_associated_token_account"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return PROGRAM_IDS; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Yes + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +impl kb_decoder_api::InstructionDecoder for crate::SplAssociatedTokenAccountDecoder { + fn identity(&self) -> kb_decoder_api::DecoderIdentity { + return kb_decoder_api::DecoderIdentity { + name: crate::SURFACE_CODE.to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + }; + } + + fn surfaces(&self) -> &'static [kb_decoder_api::DecoderSurface] { + return SURFACES; + } + + fn coverage(&self) -> std::vec::Vec { + return crate::INSTRUCTION_ENTRIES + .iter() + .map(|(tag, code, historical)| { + return kb_decoder_api::DecoderCoverageDeclaration { + program_id: kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID.to_string(), + surface_code: std::option::Option::Some(crate::SURFACE_CODE.to_string()), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: (*code).to_string(), + discriminator_hex: std::option::Option::Some(format!("{tag:02x}")), + historical: *historical, + }; + }) + .collect(); + } + + fn recognize( + &self, + input: &kb_store_core::CoreInstructionReplayInput, + ) -> kb_decoder_api::DecoderRecognition { + if input.program_id != kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID { + return kb_decoder_api::DecoderRecognition::incompatible(); + } + let wire = crate::payload(input).ok(); + let entry = wire.as_ref().and_then(|bytes| return crate::entry(bytes)); + return kb_decoder_api::DecoderRecognition::compatible( + entry.is_some(), + 100, + std::option::Option::Some(crate::SURFACE_CODE.to_string()), + entry.map(|(_, code, _)| return code.to_string()), + wire.and_then(|bytes| return bytes.first().map(|tag| return format!("{tag:02x}"))), + ); + } + + fn decode( + &self, + input: &kb_store_core::CoreInstructionReplayInput, + ) -> kb_decoder_api::DecoderExecutionResult { + if input.program_id != kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID { + return kb_decoder_api::DecoderExecutionResult::unsupported(std::option::Option::None); + } + let result = crate::decode(input); + if matches!( + result.status, + kb_decoder_api::DecoderOutcomeStatus::Failed + | kb_decoder_api::DecoderOutcomeStatus::Unsupported + ) { + tracing::error!( + target: crate::TRACING_TARGET, + action = "decode_failure", + signature = %input.signature, + slot = input.slot, + instruction_path = %input.instruction_path, + program_id = %input.program_id, + processor_name = crate::SURFACE_CODE, + processor_version = env!("CARGO_PKG_VERSION"), + input_key = %input.replay_input_key, + result_status = ?result.status, + diagnostics = ?result.diagnostics, + "SPL Associated Token Account instruction was not decoded successfully" + ); + } + tracing::debug!( + target: crate::TRACING_TARGET, + action = "decode", + signature = %input.signature, + instruction_path = %input.instruction_path, + transaction_failed = input.transaction_failed, + result_status = ?result.status, + observation_count = result.observations.len(), + "SPL Associated Token Account instruction decode completed" + ); + return result; + } +} + +#[cfg(test)] +mod tests { + fn matrix() -> serde_json::Value { + return match serde_json::from_str(include_str!( + "../../docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json" + )) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("SPL ATA matrix is invalid: {error}"), + }; + } + + #[test] + fn published_interface_and_matrix_variants_are_equal() { + let matrix = matrix(); + let instructions = match matrix.get("instructions").and_then(serde_json::Value::as_array) { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("SPL ATA matrix has no instructions"), + }; + let official = [ + ( + "create", + spl_associated_token_account_interface::instruction::AssociatedTokenAccountInstruction::Create, + ), + ( + "create_idempotent", + spl_associated_token_account_interface::instruction::AssociatedTokenAccountInstruction::CreateIdempotent, + ), + ( + "recover_nested", + spl_associated_token_account_interface::instruction::AssociatedTokenAccountInstruction::RecoverNested, + ), + ]; + assert_eq!(instructions.len(), official.len()); + for (position, (name, variant)) in official.iter().enumerate() { + let encoded = match borsh::to_vec(variant) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("cannot encode official ATA variant: {error}") + }, + }; + assert_eq!(encoded, vec![position as u8]); + assert_eq!(instructions[position]["name"], *name); + assert_eq!(instructions[position]["discriminant"], position as u64); + assert_eq!(instructions[position]["canonicalEncodingHex"], format!("{position:02x}")); + assert_eq!(instructions[position]["officialBuilder"]["available"], true); + } + assert_eq!(matrix["surfaceEquality"]["matrixVariantCount"], 3); + assert_eq!(matrix["surfaceEquality"]["officialInterfaceVariantCount"], 3); + let compiled = crate::INSTRUCTION_ENTRIES + .iter() + .map(|(tag, code, _)| return (std::option::Option::Some(*tag), (*code).to_string())) + .collect::>(); + let declared = + kb_decoder_api::InstructionDecoder::coverage(&crate::SplAssociatedTokenAccountDecoder) + .iter() + .map(|entry| { + let tag = entry + .discriminator_hex + .as_deref() + .and_then(|value| return u8::from_str_radix(value, 16).ok()); + return (tag, entry.entry_code.clone()); + }) + .collect::>(); + assert_eq!(declared, compiled); + } + + #[test] + fn program_ids_accounts_and_pda_seed_order_are_explicit() { + let matrix = matrix(); + assert_eq!(matrix["programId"], kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID); + assert_eq!( + spl_associated_token_account_interface::program::ID.to_string(), + kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID + ); + assert_eq!(matrix["limits"]["createRequiredAccountCount"], 6); + assert_eq!(matrix["limits"]["recoverNestedRequiredAccountCount"], 7); + assert_eq!( + matrix["pdaDerivation"]["seedOrder"], + serde_json::json!([ + "wallet_address_bytes[32]", + "token_program_id_bytes[32]", + "mint_address_bytes[32]" + ]) + ); + assert_eq!( + matrix["supportedTokenPrograms"].as_array().map(std::vec::Vec::len), + std::option::Option::Some(2) + ); + } + + #[test] + fn official_builders_match_matrix_wire_and_account_contracts() { + let funding = match "11111111111111111111111111111111".parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("invalid funding fixture: {error}"), + }; + let wallet = match "Vote111111111111111111111111111111111111111".parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("invalid wallet fixture: {error}"), + }; + let owner_mint = match "So11111111111111111111111111111111111111112".parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("invalid owner mint fixture: {error}"), + }; + let nested_mint = match "SysvarRent111111111111111111111111111111111".parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("invalid nested mint fixture: {error}"), + }; + let token_program = match kb_program_ids::SPL_TOKEN_PROGRAM_ID.parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("invalid Token fixture: {error}"), + }; + let create = + spl_associated_token_account_interface::instruction::create_associated_token_account( + &funding, + &wallet, + &owner_mint, + &token_program, + ); + let idempotent = spl_associated_token_account_interface::instruction::create_associated_token_account_idempotent( + &funding, + &wallet, + &owner_mint, + &token_program, + ); + assert_eq!(create.data, vec![0]); + assert_eq!(idempotent.data, vec![1]); + assert_eq!(create.accounts.len(), 6); + assert_eq!(idempotent.accounts, create.accounts); + assert_eq!(create.accounts[0].pubkey, funding); + assert!(create.accounts[0].is_signer); + assert!(create.accounts[0].is_writable); + assert!(!create.accounts[1].is_signer); + assert!(create.accounts[1].is_writable); + assert_eq!(create.accounts[2].pubkey, wallet); + assert_eq!(create.accounts[3].pubkey, owner_mint); + assert_eq!(create.accounts[4].pubkey.to_string(), "11111111111111111111111111111111"); + assert_eq!(create.accounts[5].pubkey, token_program); + let recover = spl_associated_token_account_interface::instruction::recover_nested( + &wallet, + &owner_mint, + &nested_mint, + &token_program, + ); + assert_eq!(recover.data, vec![2]); + assert_eq!(recover.accounts.len(), 7); + assert!(recover.accounts[0].is_writable); + assert!(recover.accounts[2].is_writable); + assert_eq!(recover.accounts[5].pubkey, wallet); + assert!(recover.accounts[5].is_signer); + assert!(recover.accounts[5].is_writable); + assert_eq!(recover.accounts[6].pubkey, token_program); + } + + #[test] + fn matrix_pda_vectors_match_the_official_helper() { + let matrix = matrix(); + let vectors = match matrix["pdaDerivation"]["differentialVectors"].as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("SPL ATA matrix has no PDA vectors"), + }; + for vector in vectors { + let wallet = match vector["wallet"].as_str().and_then(|value| return value.parse().ok()) + { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("invalid wallet PDA fixture"), + }; + let mint = match vector["mint"].as_str().and_then(|value| return value.parse().ok()) { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("invalid mint PDA fixture"), + }; + let token_program = + match vector["tokenProgramId"].as_str().and_then(|value| return value.parse().ok()) + { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("invalid token program PDA fixture"), + }; + let (address, bump) = spl_associated_token_account_interface::address::get_associated_token_address_and_bump_seed( + &wallet, + &mint, + &spl_associated_token_account_interface::program::ID, + &token_program, + ); + assert_eq!(address.to_string(), vector["expectedAta"]); + assert_eq!(u64::from(bump), vector["expectedBump"]); + } + } + + fn replay( + wire: &[u8], + metas: &[(std::string::String, bool, bool)], + failed: bool, + path: &str, + ) -> kb_store_core::CoreInstructionReplayInput { + use base64::Engine; // rust-rules: trait-import + + let keys = metas + .iter() + .enumerate() + .map(|(position, (key, signer, writable))| { + return serde_json::json!({ + "accountIndex": position, + "accountKey": key, + "signer": signer, + "writable": writable, + "source": "static", + }); + }) + .collect::>(); + let accounts = metas + .iter() + .enumerate() + .map(|(position, (key, _, _))| { + return serde_json::json!({ + "position": position, + "accountIndex": position, + "accountKey": key, + }); + }) + .collect::>(); + let result = kb_store_core::CoreInstructionReplayInput::new( + format!("signature:{path}"), + "signature", + 42, + path, + kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID, + failed, + if failed { + std::option::Option::Some(serde_json::json!({"InstructionError":[0,"Custom"]})) + } else { + std::option::Option::None + }, + serde_json::Value::Array(keys), + serde_json::Value::Array(accounts), + std::option::Option::Some(serde_json::json!({ + "dataBase64": base64::engine::general_purpose::STANDARD.encode(wire), + })), + std::option::Option::None, + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("ATA replay fixture failed: {error}"), + }; + } + + fn create_fixture( + token_program_text: &str, + idempotent: bool, + ) -> (std::vec::Vec, std::vec::Vec<(std::string::String, bool, bool)>) { + let funding = match "Vote111111111111111111111111111111111111111".parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("invalid funding fixture: {error}"), + }; + let wallet = match "Stake11111111111111111111111111111111111111".parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("invalid wallet fixture: {error}"), + }; + let mint = match "So11111111111111111111111111111111111111112".parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("invalid mint fixture: {error}"), + }; + let token_program = match token_program_text.parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("invalid token program fixture: {error}"), + }; + let instruction = if idempotent { + spl_associated_token_account_interface::instruction::create_associated_token_account_idempotent( + &funding, + &wallet, + &mint, + &token_program, + ) + } else { + spl_associated_token_account_interface::instruction::create_associated_token_account( + &funding, + &wallet, + &mint, + &token_program, + ) + }; + let metas = instruction + .accounts + .iter() + .map(|meta| return (meta.pubkey.to_string(), meta.is_signer, meta.is_writable)) + .collect(); + return (instruction.data, metas); + } + + #[test] + fn exact_dispatch_and_coverage_are_compiled() { + assert_eq!( + kb_decoder_api::InstructionDecoder::coverage(&crate::SplAssociatedTokenAccountDecoder) + .len(), + 3 + ); + let observation = kb_model::ProgramObservation { + signature: kb_model::Signature("signature".to_string()), + slot: kb_model::Slot(1), + instruction_path: kb_model::InstructionPath("0".to_string()), + program_id: kb_model::ProgramId( + kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID.to_string(), + ), + discriminator_8: std::option::Option::None, + data_len: 1, + accounts_len: 6, + failed: false, + }; + assert_eq!( + kb_decoder_api::ProtocolDecoder::supports_observation( + &crate::SplAssociatedTokenAccountDecoder, + &observation, + ), + kb_decoder_api::DecoderSupport::Yes + ); + } + + #[test] + fn create_and_legacy_empty_create_decode_with_canonical_address() { + let (wire, metas) = create_fixture(kb_program_ids::SPL_TOKEN_PROGRAM_ID, false); + for candidate in [wire.as_slice(), &[]] { + let input = replay(candidate, metas.as_slice(), false, "0"); + let result = kb_decoder_api::InstructionDecoder::decode( + &crate::SplAssociatedTokenAccountDecoder, + &input, + ); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some("create") + ); + assert_eq!( + result.observations[0].payload_json["addresses"]["associatedTokenAccount"]["valid"], + true + ); + assert_eq!( + result.observations[0].payload_json["semanticDiagnostics"], + serde_json::json!([]) + ); + let expected_hash = if candidate.is_empty() { + "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + } else { + "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d" + }; + assert_eq!(result.observations[0].payload_json["wire"]["sha256"], expected_hash); + } + } + + #[test] + fn token_2022_inner_failed_idempotent_remains_an_uncommitted_intent() { + let (wire, metas) = create_fixture(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, true); + let input = replay(wire.as_slice(), metas.as_slice(), true, "2/1"); + let result = kb_decoder_api::InstructionDecoder::decode( + &crate::SplAssociatedTokenAccountDecoder, + &input, + ); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!result.observations[0].observation_committed); + assert_eq!( + result.observations[0].event.source_kind, + kb_model::EventSourceKind::InnerInstruction + ); + assert_eq!( + result.observations[0].payload_json["addresses"]["tokenProgram"]["kind"], + "token_2022" + ); + assert_eq!(result.observations[0].payload_json["tokenProgramExtensionsDecoded"], false); + } + + #[test] + fn recover_nested_preserves_mint_roles_and_validates_all_three_pdas() { + let wallet = match "Vote111111111111111111111111111111111111111".parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("invalid wallet fixture: {error}"), + }; + let owner_mint = match "So11111111111111111111111111111111111111112".parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("invalid owner mint fixture: {error}"), + }; + let nested_mint = match "SysvarRent111111111111111111111111111111111".parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("invalid nested mint fixture: {error}"), + }; + let token_program = match kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("invalid token program fixture: {error}"), + }; + let instruction = spl_associated_token_account_interface::instruction::recover_nested( + &wallet, + &owner_mint, + &nested_mint, + &token_program, + ); + let metas = instruction + .accounts + .iter() + .map(|meta| return (meta.pubkey.to_string(), meta.is_signer, meta.is_writable)) + .collect::>(); + let input = replay(instruction.data.as_slice(), metas.as_slice(), false, "1/0"); + let result = kb_decoder_api::InstructionDecoder::decode( + &crate::SplAssociatedTokenAccountDecoder, + &input, + ); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + let addresses = &result.observations[0].payload_json["addresses"]; + assert_eq!(addresses["ownerMint"], owner_mint.to_string()); + assert_eq!(addresses["nestedMint"], nested_mint.to_string()); + for field in [ + "ownerAssociatedTokenAccount", + "nestedAssociatedTokenAccount", + "walletNestedMintAssociatedTokenAccount", + ] { + assert_eq!(addresses[field]["valid"], true); + } + assert_eq!( + result.observations[0].payload_json["semanticDiagnostics"], + serde_json::json!([]) + ); + } + + #[test] + fn wrong_address_flags_duplicates_extra_and_missing_accounts_stay_decodable() { + let (wire, mut metas) = create_fixture(kb_program_ids::SPL_TOKEN_PROGRAM_ID, false); + metas[1].0 = metas[0].0.clone(); + metas[0].1 = false; + metas.push(("SysvarC1ock11111111111111111111111111111111".to_string(), false, false)); + let input = replay(wire.as_slice(), metas.as_slice(), false, "0"); + let result = kb_decoder_api::InstructionDecoder::decode( + &crate::SplAssociatedTokenAccountDecoder, + &input, + ); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + let diagnostics = result.observations[0].payload_json["semanticDiagnostics"].as_array(); + let diagnostics = match diagnostics { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("ATA diagnostics are not an array"), + }; + for code in [ + "unexpected_account_count", + "account_flags_mismatch", + "duplicate_account_key", + "associated_address_mismatch", + ] { + assert!(diagnostics.iter().any(|value| return value["code"] == code)); + } + + metas.truncate(3); + let input = replay(wire.as_slice(), metas.as_slice(), false, "0"); + let result = kb_decoder_api::InstructionDecoder::decode( + &crate::SplAssociatedTokenAccountDecoder, + &input, + ); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!( + result.observations[0].payload_json["semanticDiagnostics"] + .as_array() + .is_some_and(|values| return values + .iter() + .any(|value| return value["code"] == "missing_required_account")) + ); + } + + #[test] + fn unknown_and_suffixed_wire_fail_with_bounded_diagnostics() { + let (_, metas) = create_fixture(kb_program_ids::SPL_TOKEN_PROGRAM_ID, false); + for wire in [std::vec![9], std::vec![0, 0]] { + let input = replay(wire.as_slice(), metas.as_slice(), false, "0"); + let result = kb_decoder_api::InstructionDecoder::decode( + &crate::SplAssociatedTokenAccountDecoder, + &input, + ); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + assert_eq!(result.observations.len(), 0); + assert_eq!(result.diagnostics.len(), 1); + assert!(result.diagnostics[0].message.len() < 256); + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/src/instruction.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/src/instruction.rs new file mode 100644 index 0000000..7ef542f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/src/instruction.rs @@ -0,0 +1,460 @@ +// file: kb_decoder_spl_associated_token_account/src/instruction.rs +// version: 3 + +//! Bounded ATA wire, ordered account and canonical PDA decoding. + +use base64::Engine; // rust-rules: trait-import +use sha2::Digest; // rust-rules: trait-import + +#[derive(Clone, Debug)] +struct Account { + position: usize, + account_index: u64, + key: std::string::String, + signer: bool, + writable: bool, + source: serde_json::Value, +} + +pub(crate) fn entry(bytes: &[u8]) -> std::option::Option<(u8, &'static str, bool)> { + if bytes.is_empty() { + return std::option::Option::Some((0, "create", true)); + } + if bytes.len() != 1 { + return std::option::Option::None; + } + return crate::INSTRUCTION_ENTRIES + .iter() + .copied() + .find(|(tag, _, _)| return *tag == bytes[0]); +} + +pub(crate) fn payload( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_core::Result> { + let value = match input.instruction_payload_json.as_ref() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "ATA instruction payload is not retained", + )); + }, + }; + let encoded = match value.get("dataBase64").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "ATA payload does not contain dataBase64", + )); + }, + }; + if encoded.len() > 92 { + return std::result::Result::Err(kb_core::Error::invalid_state( + "ATA base64 payload exceeds the bounded input limit", + )); + } + let bytes = match base64::engine::general_purpose::STANDARD.decode(encoded.as_bytes()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "ATA payload is not valid base64: {error}" + ))); + }, + }; + if bytes.len() > crate::MAX_INSTRUCTION_BYTES { + return std::result::Result::Err(kb_core::Error::invalid_state( + "ATA decoded payload exceeds 64 bytes", + )); + } + return std::result::Result::Ok(bytes); +} + +pub(crate) fn decode( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_decoder_api::DecoderExecutionResult { + let bytes = match crate::payload(input) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return failed(std::option::Option::None, "ata_payload_unavailable", error.to_string()); + }, + }; + let instruction = match crate::entry(bytes.as_slice()) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + let code = if bytes.len() > 1 { + "malformed_ata_instruction" + } else { + "unknown_ata_instruction_tag" + }; + return failed( + std::option::Option::None, + code, + format!("unrecognized ATA wire; payloadPrefixHex={}", hex(bytes.as_slice())), + ); + }, + }; + let accounts = match accounts(input) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return failed( + std::option::Option::Some(instruction.1), + "ata_accounts_malformed", + error.to_string(), + ); + }, + }; + let diagnostics = diagnostics(instruction.1, accounts.as_slice()); + let projected_accounts = project_accounts(instruction.1, accounts.as_slice()); + let addresses = addresses(instruction.1, accounts.as_slice()); + let hash = hash(bytes.as_slice()); + let committed = !input.transaction_failed; + let confidence = kb_model::DecoderConfidence::ManualExact; + let event = kb_model::DecodedProtocolEvent { + signature: kb_model::Signature(input.signature.clone()), + slot: kb_model::Slot(input.slot), + instruction_path: kb_model::InstructionPath(input.instruction_path.clone()), + program_id: kb_model::ProgramId(input.program_id.clone()), + protocol_code: kb_model::ProtocolCode(crate::SURFACE_CODE.to_string()), + surface_code: kb_model::SurfaceCode(crate::SURFACE_CODE.to_string()), + event_code: kb_model::EventCode(format!("{}.{}", crate::SURFACE_CODE, instruction.1)), + event_name: kb_model::EventName(instruction.1.to_string()), + event_family: kb_model::EventFamily::Lifecycle, + source_kind: if input.instruction_path.contains('/') { + kb_model::EventSourceKind::InnerInstruction + } else { + kb_model::EventSourceKind::Instruction + }, + confidence, + }; + let observation = kb_decoder_api::DecodedObservation { + event_key: format!("ata:{}:0", instruction.1), + event, + payload_json: serde_json::json!({ + "eventVersion": crate::EVENT_VERSION, + "programId": input.program_id, + "instruction": instruction.1, + "wireTag": instruction.0, + "wireTagHex": format!("{:02x}", instruction.0), + "wireEncoding": if bytes.is_empty() { "legacy_empty_create" } else { "borsh_enum_discriminant" }, + "instructionPath": input.instruction_path, + "instructionLocation": if input.instruction_path.contains('/') { "inner" } else { "outer" }, + "transactionSucceeded": !input.transaction_failed, + "committed": committed, + "accounts": projected_accounts, + "addresses": addresses, + "semanticDiagnostics": diagnostics, + "wire": { + "lengthBytes": bytes.len(), + "sha256": hash, + "prefixHex": hex(bytes.as_slice()), + "complete": true, + }, + "tokenProgramExtensionsDecoded": false, + "stateSnapshotReconstructed": false, + }), + transaction_failed: input.transaction_failed, + transaction_error: input.transaction_err_json.clone(), + observation_committed: committed, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::Manual, + confidence, + evidence: std::vec![ + "spl_associated_token_account_interface_2_0_0_and_processor_audit".to_string(), + format!("wire_sha256:{hash}"), + ], + }, + }; + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Decoded, + recognized_entry_code: std::option::Option::Some(instruction.1.to_string()), + observations: std::vec![observation], + diagnostics: std::vec::Vec::new(), + }; +} + +fn accounts( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_core::Result> { + let instruction_accounts = match input.instruction_accounts_json.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "ATA instruction accounts must be a JSON array", + )); + }, + }; + let keys = match input.account_keys_json.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "ATA transaction account keys must be a JSON array", + )); + }, + }; + if instruction_accounts.len() > crate::MAX_ACCOUNTS { + return std::result::Result::Err(kb_core::Error::invalid_state( + "ATA instruction account count exceeds 64", + )); + } + if keys.len() > crate::MAX_TRANSACTION_KEYS { + return std::result::Result::Err(kb_core::Error::invalid_state( + "ATA transaction account key count exceeds 4096", + )); + } + let mut output = std::vec::Vec::with_capacity(instruction_accounts.len()); + for (position, value) in instruction_accounts.iter().enumerate() { + let index = match value.get("accountIndex").and_then(serde_json::Value::as_u64) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "ATA instruction account {position} has no accountIndex" + ))); + }, + }; + let key = match value.get("accountKey").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) + if !value.is_empty() && value.len() <= crate::MAX_ACCOUNT_KEY_BYTES => + { + value + }, + _ => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "ATA instruction account {position} has no accountKey" + ))); + }, + }; + let resolved = match keys.iter().find(|candidate| { + return candidate.get("accountIndex").and_then(serde_json::Value::as_u64) + == std::option::Option::Some(index); + }) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "ATA account index {index} is not resolved" + ))); + }, + }; + if resolved.get("accountKey").and_then(serde_json::Value::as_str) + != std::option::Option::Some(key) + { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "ATA account index {index} resolves to a different key" + ))); + } + let signer = match resolved.get("signer").and_then(serde_json::Value::as_bool) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "ATA account index {index} has no signer flag" + ))); + }, + }; + let writable = match resolved.get("writable").and_then(serde_json::Value::as_bool) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "ATA account index {index} has no writable flag" + ))); + }, + }; + output.push(Account { + position, + account_index: index, + key: key.to_string(), + signer, + writable, + source: resolved.get("source").cloned().unwrap_or(serde_json::Value::Null), + }); + } + return std::result::Result::Ok(output); +} + +fn roles(code: &str) -> &'static [(&'static str, bool, bool)] { + return if code == "recover_nested" { + &[ + ("nested_associated_token_account", false, true), + ("nested_mint", false, false), + ("wallet_nested_mint_associated_token_account", false, true), + ("owner_associated_token_account", false, false), + ("owner_mint", false, false), + ("wallet_owner", true, true), + ("token_program", false, false), + ] + } else { + &[ + ("funding_account", true, true), + ("associated_token_account", false, true), + ("wallet_owner", false, false), + ("mint", false, false), + ("system_program", false, false), + ("token_program", false, false), + ] + }; +} + +fn project_accounts(code: &str, accounts: &[Account]) -> serde_json::Value { + let roles = roles(code); + return serde_json::Value::Array(accounts.iter().map(|account| return serde_json::json!({ + "position": account.position, + "accountIndex": account.account_index, + "accountKey": account.key, + "signer": account.signer, + "writable": account.writable, + "source": account.source, + "role": roles.get(account.position).map(|value| return value.0).unwrap_or("extra_account"), + })).collect()); +} + +fn diagnostics(code: &str, accounts: &[Account]) -> serde_json::Value { + let roles = roles(code); + let mut values = std::vec::Vec::new(); + if accounts.len() != roles.len() { + values.push(serde_json::json!({"code":"unexpected_account_count","expected":roles.len(),"observed":accounts.len()})); + } + for (position, (role, signer, writable)) in roles.iter().enumerate() { + if let std::option::Option::Some(account) = accounts.get(position) { + if account.signer != *signer || account.writable != *writable { + values.push(serde_json::json!({"code":"account_flags_mismatch","position":position,"role":role,"expectedSigner":signer,"observedSigner":account.signer,"expectedWritable":writable,"observedWritable":account.writable})); + } + } else { + values.push(serde_json::json!({"code":"missing_required_account","position":position,"role":role})); + } + } + for left in 0..accounts.len() { + for right in left.saturating_add(1)..accounts.len() { + if accounts[left].key == accounts[right].key { + values.push(serde_json::json!({"code":"duplicate_account_key","positions":[left,right],"accountKey":accounts[left].key})); + } + } + } + let expected = addresses(code, accounts); + for field in [ + "associatedTokenAccount", + "ownerAssociatedTokenAccount", + "nestedAssociatedTokenAccount", + "walletNestedMintAssociatedTokenAccount", + ] { + if expected + .get(field) + .and_then(|value| return value.get("valid")) + .and_then(serde_json::Value::as_bool) + == std::option::Option::Some(false) + { + values.push(serde_json::json!({"code":"associated_address_mismatch","field":field,"observed":expected[field]["observed"],"expected":expected[field]["expected"]})); + } + } + let token_position = if code == "recover_nested" { 6 } else { 5 }; + if accounts.get(token_position).is_some_and(|account| { + return account.key != kb_program_ids::SPL_TOKEN_PROGRAM_ID + && account.key != kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID; + }) { + values.push(serde_json::json!({"code":"unsupported_token_program","observed":accounts.get(token_position).map(|value| return value.key.clone())})); + } + if code != "recover_nested" + && accounts + .get(4) + .is_some_and(|account| return account.key != "11111111111111111111111111111111") + { + values + .push(serde_json::json!({"code":"system_program_mismatch","observed":accounts[4].key})); + } + values.truncate(crate::MAX_DIAGNOSTICS); + return serde_json::Value::Array(values); +} + +fn addresses(code: &str, accounts: &[Account]) -> serde_json::Value { + let token_position = if code == "recover_nested" { 6 } else { 5 }; + let token = accounts.get(token_position).map(|value| return value.key.as_str()); + if code == "recover_nested" { + return serde_json::json!({ + "tokenProgram": token_kind(token), + "ownerAssociatedTokenAccount": derived(accounts.get(5), accounts.get(4), token, accounts.get(3)), + "nestedAssociatedTokenAccount": derived(accounts.get(3), accounts.get(1), token, accounts.first()), + "walletNestedMintAssociatedTokenAccount": derived(accounts.get(5), accounts.get(1), token, accounts.get(2)), + "walletOwner": accounts.get(5).map(|value| return value.key.clone()), + "ownerMint": accounts.get(4).map(|value| return value.key.clone()), + "nestedMint": accounts.get(1).map(|value| return value.key.clone()), + }); + } + return serde_json::json!({ + "tokenProgram": token_kind(token), + "associatedTokenAccount": derived(accounts.get(2), accounts.get(3), token, accounts.get(1)), + "fundingAccount": accounts.first().map(|value| return value.key.clone()), + "walletOwner": accounts.get(2).map(|value| return value.key.clone()), + "mint": accounts.get(3).map(|value| return value.key.clone()), + }); +} + +fn derived( + wallet: std::option::Option<&Account>, + mint: std::option::Option<&Account>, + token: std::option::Option<&str>, + observed: std::option::Option<&Account>, +) -> serde_json::Value { + let expected = match (wallet, mint, token) { + ( + std::option::Option::Some(wallet), + std::option::Option::Some(mint), + std::option::Option::Some(token), + ) => { + let wallet_key = wallet.key.parse(); + let mint_key = mint.key.parse(); + let token_key = token.parse(); + match (wallet_key, mint_key, token_key) { + (std::result::Result::Ok(wallet_key), std::result::Result::Ok(mint_key), std::result::Result::Ok(token_key)) => std::option::Option::Some(spl_associated_token_account_interface::address::get_associated_token_address_with_program_id(&wallet_key, &mint_key, &token_key).to_string()), + _ => std::option::Option::None, + } + }, + _ => std::option::Option::None, + }; + let observed = observed.map(|value| return value.key.clone()); + let valid = expected.is_some() && expected == observed; + return serde_json::json!({"expected":expected,"observed":observed,"valid":valid}); +} + +fn token_kind(token: std::option::Option<&str>) -> serde_json::Value { + return match token { + std::option::Option::Some(kb_program_ids::SPL_TOKEN_PROGRAM_ID) => { + serde_json::json!({"programId":kb_program_ids::SPL_TOKEN_PROGRAM_ID,"kind":"spl_token_classic","supported":true}) + }, + std::option::Option::Some(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID) => { + serde_json::json!({"programId":kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID,"kind":"token_2022","supported":true}) + }, + std::option::Option::Some(value) => { + serde_json::json!({"programId":value,"kind":"unsupported","supported":false}) + }, + std::option::Option::None => { + serde_json::json!({"programId":serde_json::Value::Null,"kind":"missing","supported":false}) + }, + }; +} + +fn failed( + entry: std::option::Option<&str>, + code: &str, + message: std::string::String, +) -> kb_decoder_api::DecoderExecutionResult { + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Failed, + recognized_entry_code: entry.map(|value| return value.to_string()), + observations: std::vec::Vec::new(), + diagnostics: std::vec![kb_decoder_api::DecoderDiagnostic { + code: code.to_string(), + message, + retriable: false + }], + }; +} + +fn hash(bytes: &[u8]) -> std::string::String { + let digest = sha2::Sha256::digest(bytes); + let mut output = std::string::String::with_capacity(64); + for byte in digest { + output.push_str(format!("{byte:02x}").as_str()); + } + return output; +} + +fn hex(bytes: &[u8]) -> std::string::String { + return bytes.iter().take(16).map(|value| return format!("{value:02x}")).collect(); +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/src/lib.rs new file mode 100644 index 0000000..a81022b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_associated_token_account/src/lib.rs @@ -0,0 +1,39 @@ +// file: kb_decoder_spl_associated_token_account/src/lib.rs +// version: 7 + +//! Decoder crate for `spl_associated_token_account`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; +mod instruction; + +/// Crate-root access to `EVENT_VERSION` from `constants`. +pub(crate) use crate::constants::EVENT_VERSION; +/// Crate-root access to `INSTRUCTION_ENTRIES` from `constants`. +pub(crate) use crate::constants::INSTRUCTION_ENTRIES; +/// Crate-root access to `MAX_ACCOUNT_KEY_BYTES` from `constants`. +pub(crate) use crate::constants::MAX_ACCOUNT_KEY_BYTES; +/// Crate-root access to `MAX_ACCOUNTS` from `constants`. +pub(crate) use crate::constants::MAX_ACCOUNTS; +/// Crate-root access to `MAX_DIAGNOSTICS` from `constants`. +pub(crate) use crate::constants::MAX_DIAGNOSTICS; +/// Crate-root access to `MAX_INSTRUCTION_BYTES` from `constants`. +pub(crate) use crate::constants::MAX_INSTRUCTION_BYTES; +/// Crate-root access to `MAX_TRANSACTION_KEYS` from `constants`. +pub(crate) use crate::constants::MAX_TRANSACTION_KEYS; +/// Crate-root access to `SURFACE_CODE` from `constants`. +pub(crate) use crate::constants::SURFACE_CODE; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; +/// Crate-root access to `decode` from `instruction`. +pub(crate) use crate::instruction::decode; +/// Crate-root access to `entry` from `instruction`. +pub(crate) use crate::instruction::entry; +/// Crate-root access to `payload` from `instruction`. +pub(crate) use crate::instruction::payload; + +/// Exact SPL Associated Token Account decoder. +pub use crate::decoder::SplAssociatedTokenAccountDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/Cargo.toml new file mode 100644 index 0000000..4b0063c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/Cargo.toml @@ -0,0 +1,25 @@ +# file: kb_decoder_spl_elgamal_registry/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_spl_elgamal_registry" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +base64.workspace = true +bs58.workspace = true +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +kb_store_core = { path = "../kb_store_core" } +serde_json.workspace = true +sha2.workspace = true +spl-elgamal-registry-interface.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/README.md b/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/README.md new file mode 100644 index 0000000..8631169 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/README.md @@ -0,0 +1,13 @@ + + + +# Décodeur SPL ElGamal Registry + +Décodeur strict du programme `regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg`. + +Il couvre les deux instructions publiées par `spl-elgamal-registry-interface 0.2.1` : + +- `CreateRegistry` (`00 `) ; +- `UpdateRegistry` (`01 `). + +Un offset nul désigne un compte de contexte de preuve prévalidé. Un offset non nul désigne une instruction relative du programme natif ZK ElGamal Proof. Le décodeur ne prétend pas décoder la preuve elle-même et ne mélange jamais ce programme avec Token-2022. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/constants.rs new file mode 100644 index 0000000..7bae929 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/constants.rs @@ -0,0 +1,11 @@ +// file: kb_decoder_spl_elgamal_registry/src/constants.rs +// version: 2 + +//! Constants for the SPL ElGamal registry decoder. + +pub(crate) const SURFACE_CODE: &str = "spl_elgamal_registry"; +pub(crate) const EVENT_VERSION: u32 = 1; +pub(crate) const MAX_INSTRUCTION_BYTES: usize = 16; + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_spl_elgamal_registry"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/decoder.rs new file mode 100644 index 0000000..85502fb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/decoder.rs @@ -0,0 +1,256 @@ +// file: kb_decoder_spl_elgamal_registry/src/decoder.rs +// version: 6 + +//! Exact dispatch for the SPL ElGamal registry program. + +const SURFACES: &[kb_decoder_api::DecoderSurface] = &[kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID, + surface_code: crate::SURFACE_CODE, + priority: 100, +}]; + +const PROGRAM_IDS: &[&str] = &[kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID]; + +/// Decoder for the dedicated Token-2022 ElGamal public-key registry program. +#[derive(Clone, Debug, Default)] +pub struct SplElgamalRegistryDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::SplElgamalRegistryDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_spl_elgamal_registry"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return PROGRAM_IDS; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Yes + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +impl kb_decoder_api::InstructionDecoder for crate::SplElgamalRegistryDecoder { + fn identity(&self) -> kb_decoder_api::DecoderIdentity { + return kb_decoder_api::DecoderIdentity { + name: "spl_elgamal_registry".to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + }; + } + + fn surfaces(&self) -> &'static [kb_decoder_api::DecoderSurface] { + return SURFACES; + } + + fn coverage(&self) -> std::vec::Vec { + return [(0_u8, "create_registry"), (1_u8, "update_registry")] + .into_iter() + .map(|(tag, code)| { + return kb_decoder_api::DecoderCoverageDeclaration { + program_id: kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID + .to_string(), + surface_code: std::option::Option::Some(crate::SURFACE_CODE.to_string()), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: code.to_string(), + discriminator_hex: std::option::Option::Some(format!("{tag:02x}")), + historical: false, + }; + }) + .collect(); + } + + fn recognize( + &self, + input: &kb_store_core::CoreInstructionReplayInput, + ) -> kb_decoder_api::DecoderRecognition { + if input.program_id != kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID { + return kb_decoder_api::DecoderRecognition::incompatible(); + } + let entry = input + .instruction_payload_json + .as_ref() + .and_then(|payload| return payload.get("dataBase64")) + .and_then(serde_json::Value::as_str) + .and_then(|encoded| { + use base64::Engine; // rust-rules: trait-import + return base64::engine::general_purpose::STANDARD.decode(encoded.as_bytes()).ok(); + }) + .and_then(|bytes| return bytes.first().copied()) + .and_then(|tag| { + return match tag { + 0 => std::option::Option::Some("create_registry".to_string()), + 1 => std::option::Option::Some("update_registry".to_string()), + _ => std::option::Option::None, + }; + }); + return kb_decoder_api::DecoderRecognition::compatible( + entry.is_some(), + 100, + std::option::Option::Some(crate::SURFACE_CODE.to_string()), + entry, + std::option::Option::None, + ); + } + + fn decode( + &self, + input: &kb_store_core::CoreInstructionReplayInput, + ) -> kb_decoder_api::DecoderExecutionResult { + if input.program_id != kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID { + return kb_decoder_api::DecoderExecutionResult::unsupported(std::option::Option::None); + } + let result = crate::decode(input); + if matches!( + result.status, + kb_decoder_api::DecoderOutcomeStatus::Failed + | kb_decoder_api::DecoderOutcomeStatus::Unsupported + ) { + tracing::error!( + target: crate::TRACING_TARGET, + action = "decode_failure", + signature = %input.signature, + slot = input.slot, + instruction_path = %input.instruction_path, + program_id = %input.program_id, + processor_name = "spl_elgamal_registry", + processor_version = env!("CARGO_PKG_VERSION"), + input_key = %input.replay_input_key, + payload_hash = ?input.instruction_payload_hash, + transaction_failed = input.transaction_failed, + result_status = ?result.status, + diagnostics = ?result.diagnostics, + "SPL ElGamal registry instruction was not decoded successfully" + ); + } + tracing::debug!( + target: crate::TRACING_TARGET, + action = "decode", + signature = %input.signature, + instruction_path = %input.instruction_path, + program_id = %input.program_id, + transaction_failed = input.transaction_failed, + result_status = ?result.status, + observation_count = result.observations.len(), + "SPL ElGamal registry instruction decode completed" + ); + return result; + } +} + +#[cfg(test)] +mod tests { + use base64::Engine; // rust-rules: trait-import + + fn input( + program_id: &str, + payload: &[u8], + failed: bool, + path: &str, + ) -> kb_store_core::CoreInstructionReplayInput { + let result = kb_store_core::CoreInstructionReplayInput::new( + format!("signature:{path}"), + "signature", + 42, + path, + program_id, + failed, + if failed { + std::option::Option::Some(serde_json::json!({"InstructionError":[0,"Custom"]})) + } else { + std::option::Option::None + }, + serde_json::json!([]), + serde_json::json!([]), + std::option::Option::Some(serde_json::json!({ + "dataBase64": base64::engine::general_purpose::STANDARD.encode(payload) + })), + std::option::Option::None, + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("registry replay input failed: {error}"), + }; + } + + #[test] + fn exact_program_boundary_and_interface_id_are_preserved() { + assert_eq!( + spl_elgamal_registry_interface::id().to_string(), + kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID + ); + let decoder = crate::SplElgamalRegistryDecoder; + assert_eq!(kb_decoder_api::InstructionDecoder::coverage(&decoder).len(), 2); + let foreign = input(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, &[0, 1], false, "0"); + assert_eq!( + kb_decoder_api::InstructionDecoder::decode(&decoder, &foreign).status, + kb_decoder_api::DecoderOutcomeStatus::Unsupported + ); + } + + #[test] + fn create_and_update_decode_exact_offsets_and_commit_state() { + for (tag, code, offset, failed, path) in [ + (0_u8, "create_registry", 1_i8, false, "0"), + (1_u8, "update_registry", -1_i8, true, "1/0"), + (1_u8, "update_registry", 0_i8, false, "2"), + ] { + let input = input( + kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID, + &[tag, offset as u8], + failed, + path, + ); + let result = kb_decoder_api::InstructionDecoder::decode( + &crate::SplElgamalRegistryDecoder, + &input, + ); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(result.recognized_entry_code.as_deref(), std::option::Option::Some(code)); + assert_eq!( + result.observations[0].payload_json["proofInstructionOffset"], + serde_json::json!(offset) + ); + assert_eq!(result.observations[0].observation_committed, !failed); + } + } + + #[test] + fn malformed_unknown_and_suffixed_wire_fail_closed() { + for payload in [std::vec::Vec::new(), vec![0], vec![2, 0], vec![0, 1, 2]] { + let input = input( + kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID, + payload.as_slice(), + false, + "0", + ); + let result = kb_decoder_api::InstructionDecoder::decode( + &crate::SplElgamalRegistryDecoder, + &input, + ); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + assert!(result.observations.is_empty()); + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/lib.rs new file mode 100644 index 0000000..6095917 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/lib.rs @@ -0,0 +1,38 @@ +// file: kb_decoder_spl_elgamal_registry/src/lib.rs +// version: 5 + +//! Decoder for the SPL Token-2022 ElGamal public-key registry program. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; +mod registry; +pub mod state; + +/// Crate-root access to `EVENT_VERSION` from `constants`. +pub(crate) use crate::constants::EVENT_VERSION; +/// Crate-root access to `MAX_INSTRUCTION_BYTES` from `constants`. +pub(crate) use crate::constants::MAX_INSTRUCTION_BYTES; +/// Crate-root access to `SURFACE_CODE` from `constants`. +pub(crate) use crate::constants::SURFACE_CODE; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; +/// Crate-root access to `ParsedRegistryInstruction` from `registry`. +pub(crate) use crate::registry::ParsedRegistryInstruction; +/// Crate-root access to `decode` from `registry`. +pub(crate) use crate::registry::decode; +/// Crate-root access to `decode_payload` from `registry`. +pub(crate) use crate::registry::decode_payload; +/// Crate-root access to `parse` from `registry`. +pub(crate) use crate::registry::parse; + +/// Exact decoder for the SPL ElGamal registry program. +pub use crate::decoder::SplElgamalRegistryDecoder; +/// Exact byte length of one SPL ElGamal registry account. +pub use crate::state::ELGAMAL_REGISTRY_ACCOUNT_LEN; +/// Crate-root access to `ElGamalRegistryState` from `state`. +pub use crate::state::ElGamalRegistryState; +/// Parse one exact SPL ElGamal registry account. +pub use crate::state::parse_elgamal_registry_state; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/registry.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/registry.rs new file mode 100644 index 0000000..8bbaa66 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/registry.rs @@ -0,0 +1,191 @@ +// file: kb_decoder_spl_elgamal_registry/src/registry.rs +// version: 3 + +//! Exact two-byte SPL ElGamal registry instruction decoding. + +use base64::Engine; // rust-rules: trait-import +use sha2::Digest; // rust-rules: trait-import + +#[derive(Clone, Debug)] +pub(crate) struct ParsedRegistryInstruction { + pub(crate) code: &'static str, + pub(crate) tag: u8, + pub(crate) proof_instruction_offset: i8, +} + +pub(crate) fn decode_payload( + input: &kb_store_core::CoreInstructionReplayInput, +) -> std::result::Result, std::string::String> { + let payload = match input.instruction_payload_json.as_ref() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "ElGamal registry instruction payload is not retained".to_string(), + ); + }, + }; + let encoded = match payload.get("dataBase64").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "ElGamal registry payload does not contain dataBase64".to_string(), + ); + }, + }; + if encoded.len() > 32 { + return std::result::Result::Err( + "ElGamal registry base64 payload is too large".to_string(), + ); + } + let bytes = match base64::engine::general_purpose::STANDARD.decode(encoded.as_bytes()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "ElGamal registry payload is not valid base64: {error}" + )); + }, + }; + if bytes.len() > crate::MAX_INSTRUCTION_BYTES { + return std::result::Result::Err(format!( + "ElGamal registry payload exceeds {} bytes", + crate::MAX_INSTRUCTION_BYTES + )); + } + return std::result::Result::Ok(bytes); +} + +pub(crate) fn parse( + bytes: &[u8], +) -> std::result::Result { + if bytes.len() != 2 { + return std::result::Result::Err(format!( + "ElGamal registry instruction requires exactly 2 bytes; received {}", + bytes.len() + )); + } + let code = match bytes[0] { + 0 => "create_registry", + 1 => "update_registry", + tag => return std::result::Result::Err(format!("unknown ElGamal registry tag {tag}")), + }; + return std::result::Result::Ok(crate::ParsedRegistryInstruction { + code, + tag: bytes[0], + proof_instruction_offset: bytes[1] as i8, + }); +} + +pub(crate) fn decode( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_decoder_api::DecoderExecutionResult { + let bytes = match crate::decode_payload(input) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => { + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Failed, + recognized_entry_code: std::option::Option::None, + observations: std::vec::Vec::new(), + diagnostics: std::vec![kb_decoder_api::DecoderDiagnostic { + code: "elgamal_registry_payload_invalid".to_string(), + message, + retriable: false, + }], + }; + }, + }; + let parsed = match crate::parse(bytes.as_slice()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => { + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Failed, + recognized_entry_code: bytes.first().and_then(|tag| { + return match tag { + 0 => std::option::Option::Some("create_registry".to_string()), + 1 => std::option::Option::Some("update_registry".to_string()), + _ => std::option::Option::None, + }; + }), + observations: std::vec::Vec::new(), + diagnostics: std::vec![kb_decoder_api::DecoderDiagnostic { + code: "elgamal_registry_wire_invalid".to_string(), + message, + retriable: false, + }], + }; + }, + }; + let committed = !input.transaction_failed; + let wire_hash = hash(bytes.as_slice()); + let event = kb_model::DecodedProtocolEvent { + signature: kb_model::Signature(input.signature.clone()), + slot: kb_model::Slot(input.slot), + instruction_path: kb_model::InstructionPath(input.instruction_path.clone()), + program_id: kb_model::ProgramId(input.program_id.clone()), + protocol_code: kb_model::ProtocolCode(crate::SURFACE_CODE.to_string()), + surface_code: kb_model::SurfaceCode(crate::SURFACE_CODE.to_string()), + event_code: kb_model::EventCode(format!("{}.{}", crate::SURFACE_CODE, parsed.code)), + event_name: kb_model::EventName(parsed.code.to_string()), + event_family: kb_model::EventFamily::Admin, + source_kind: if input.instruction_path.contains('/') { + kb_model::EventSourceKind::InnerInstruction + } else { + kb_model::EventSourceKind::Instruction + }, + confidence: kb_model::DecoderConfidence::ManualExact, + }; + let observation = kb_decoder_api::DecodedObservation { + event_key: format!("elgamal_registry:{}:0", parsed.code), + event, + payload_json: serde_json::json!({ + "eventVersion": crate::EVENT_VERSION, + "instruction": parsed.code, + "wireTag": parsed.tag, + "proofInstructionOffset": parsed.proof_instruction_offset, + "proofLocationKind": if parsed.proof_instruction_offset == 0 { "context_state_account" } else { "relative_instruction_offset" }, + "instructionPath": input.instruction_path, + "instructionLocation": if input.instruction_path.contains('/') { "inner" } else { "outer" }, + "transactionSucceeded": !input.transaction_failed, + "committed": committed, + "accounts": input.instruction_accounts_json, + "wire": { + "lengthBytes": bytes.len(), + "sha256": wire_hash, + "prefixHex": hex(bytes.as_slice()), + "complete": true, + }, + "programBoundary": { + "registryProgramId": kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID, + "token2022ProgramId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + "zkProofProgramId": kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID, + "proofDecodedHere": false, + } + }), + transaction_failed: input.transaction_failed, + transaction_error: input.transaction_err_json.clone(), + observation_committed: committed, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::Manual, + confidence: kb_model::DecoderConfidence::ManualExact, + evidence: std::vec![ + "spl_elgamal_registry_interface_0_2_1_registry_instruction_unpack".to_string(), + format!("wire_tag:{}", parsed.tag), + format!("wire_sha256:{wire_hash}"), + ], + }, + }; + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Decoded, + recognized_entry_code: std::option::Option::Some(parsed.code.to_string()), + observations: std::vec![observation], + diagnostics: std::vec::Vec::new(), + }; +} + +fn hash(bytes: &[u8]) -> std::string::String { + let digest = sha2::Sha256::digest(bytes); + return digest.iter().map(|byte| return format!("{byte:02x}")).collect(); +} + +fn hex(bytes: &[u8]) -> std::string::String { + return bytes.iter().map(|byte| return format!("{byte:02x}")).collect(); +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/state.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/state.rs new file mode 100644 index 0000000..25e2026 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_elgamal_registry/src/state.rs @@ -0,0 +1,76 @@ +// file: kb_decoder_spl_elgamal_registry/src/state.rs +// version: 2 + +//! Strict SPL ElGamal registry account-state parsing. + +use base64::Engine; // rust-rules: trait-import + +/// Exact byte length of one SPL ElGamal registry account. +pub const ELGAMAL_REGISTRY_ACCOUNT_LEN: usize = 64; + +/// Public state retained by one SPL ElGamal registry account. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ElGamalRegistryState { + /// Wallet address associated with this registry. + pub owner: String, + /// Exact 32-byte ElGamal public key encoded as standard base64. + pub elgamal_pubkey_base64: String, + /// Exact account bytes encoded as lowercase hexadecimal. + pub wire_hex: String, +} + +/// Parse one exact SPL ElGamal registry account. +pub fn parse_elgamal_registry_state( + data: &[u8], +) -> std::result::Result { + if data.len() != ELGAMAL_REGISTRY_ACCOUNT_LEN { + return std::result::Result::Err(format!( + "SPL ElGamal registry account requires exactly {ELGAMAL_REGISTRY_ACCOUNT_LEN} bytes; received {}", + data.len() + )); + } + return std::result::Result::Ok(ElGamalRegistryState { + owner: bs58::encode(&data[..32]).into_string(), + elgamal_pubkey_base64: base64::engine::general_purpose::STANDARD.encode(&data[32..64]), + wire_hex: lower_hex(data), + }); +} + +fn lower_hex(bytes: &[u8]) -> String { + let mut output = String::with_capacity(bytes.len() * 2); + for byte in bytes { + output.push_str(&format!("{byte:02x}")); + } + return output; +} + +#[cfg(test)] +mod tests { + use base64::Engine; // rust-rules: trait-import + + #[test] + fn exact_registry_state_preserves_owner_key_and_wire() { + let mut data = [0u8; crate::ELGAMAL_REGISTRY_ACCOUNT_LEN]; + data[..32].fill(7); + data[32..].fill(9); + let parsed = crate::parse_elgamal_registry_state(&data); + assert_eq!( + parsed.as_ref().map(|state| return state.owner.clone()), + std::result::Result::Ok(bs58::encode([7u8; 32]).into_string()) + ); + assert_eq!( + parsed.as_ref().map(|state| return state.elgamal_pubkey_base64.clone()), + std::result::Result::Ok(base64::engine::general_purpose::STANDARD.encode([9u8; 32])) + ); + assert_eq!( + parsed.as_ref().map(|state| return state.wire_hex.len()), + std::result::Result::Ok(128) + ); + } + + #[test] + fn registry_state_rejects_every_non_exact_length() { + assert!(crate::parse_elgamal_registry_state(&[0; 63]).is_err()); + assert!(crate::parse_elgamal_registry_state(&[0; 65]).is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_memo/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_spl_memo/Cargo.toml new file mode 100644 index 0000000..ccf9966 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_memo/Cargo.toml @@ -0,0 +1,24 @@ +# file: kb_decoder_spl_memo/Cargo.toml +# version: 4 + +[package] +name = "kb_decoder_spl_memo" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +base64.workspace = true +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +kb_store_core = { path = "../kb_store_core" } +serde_json.workspace = true +sha2.workspace = true +spl-memo-interface.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_memo/README.md b/migration/khadhroony-bot2-reference/kb_decoder_spl_memo/README.md new file mode 100644 index 0000000..1ec6305 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_memo/README.md @@ -0,0 +1,55 @@ + + + +# kb_decoder_spl_memo + +Ce crate décode les programmes SPL Memo v1, v3 et v4 depuis l’instruction core contextualisée. + +## Surface exacte + +- v1 : `Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo` ; +- v3 : `MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr` ; +- v4 : `Memo4c2pN8afCj432Lb7RMVKi9PbQnnW7ewFFaV3oAH`. + +`SplMemoDecoder` implémente le contrat contextualisé `InstructionDecoder`. La reconnaissance est exacte pour ces trois IDs et incompatible avec tout autre programme. L’adaptateur historique `ProtocolDecoder` déclare `Yes` pour ces IDs, mais ne fabrique pas d’événement sans instruction contextualisée. + +## Contrat décodé + +Le payload Memo est la totalité des octets de l’instruction, sans discriminator ni préfixe de longueur. Le décodeur conserve : + +- la génération et le Program ID exacts ; +- le texte complet lorsque l’UTF‑8 est valide ; +- la longueur en octets, le SHA-256 et un préfixe hexadécimal borné à 32 octets ; +- tous les comptes dans leur ordre, y compris les doublons, avec position, index, pubkey, signer et writable ; +- les signataires observés et les comptes non signers ; +- le chemin outer/inner, le succès de transaction et l’état committed ; +- les validations syntaxiques et le statut de preuve runtime. + +Le décodeur accepte au maximum 4 096 octets décodés. Cette limite protège le replay contre un payload core anormal tout en dépassant la taille pratique d’une instruction contenue dans une transaction Solana actuelle. Dans cette borne, le texte valide est conservé sans troncature. + +Les événements sont : + +- `add_memo` pour une transaction réussie dont le contrat est valide ; +- `memo_intent` pour un payload lisible dans une transaction échouée ; +- `invalid_memo_attempt` pour un UTF‑8 invalide ou un compte non signer lorsque la génération l’exige. + +Une transaction échouée n’est jamais marquée committed et son statut runtime reste `not_proven_transaction_failed`, même lorsque l’analyse statique des octets et comptes est valide. + +## Différences de génération + +Memo v1 valide l’UTF‑8 et ignore les comptes. Memo v3 et v4 parcourent tous les comptes fournis et échouent si l’un d’eux n’est pas signer ; zéro compte reste valide. Les trois générations partagent le même wire de données brutes, mais le décodeur conserve des surfaces et règles séparées. + +La matrice normative et les références officielles sont dans `docs/SPL_MEMO_MATRIX.json`. + +Le corpus réel couvre les trois générations sur Mainnet. La validation opérateur ajoute trois signatures v4 Devnet confirmées, toutes décodées comme `add_memo` outer au path `0`, avec compte wallet signer readonly, sans échec de décodage. v1 et v3 ne sont pas prétendues déployées sur Devnet : leur couverture réelle reste assurée par le replay Mainnet et leur construction reste soumise à simulation avant tout envoi. + +## Projection + +`kb_materializer_transaction_annotations` projette uniquement les `add_memo` réussis et commités dans la famille dédiée `TransactionAnnotation`. Les intentions échouées et tentatives invalides restent consultables dans le decode sans devenir des annotations réussies. Les comptes complets, flags writable, diagnostics, préfixes hexadécimaux et preuves restent volontairement dans l’événement décodé ; la projection conserve le texte, la longueur, le hash et les signataires utiles à la recherche et à la corrélation. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs`. +- Les erreurs de rétention core, de base64, de taille ou de résolution de comptes produisent un résultat `Failed` borné, jamais un faux Memo. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_memo/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_memo/src/constants.rs new file mode 100644 index 0000000..28a2a73 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_memo/src/constants.rs @@ -0,0 +1,21 @@ +// file: kb_decoder_spl_memo/src/constants.rs +// version: 3 + +//! Local constants for the `kb_decoder_spl_memo` crate. Program identifiers live in `kb_program_ids`. + +/// Stable protocol code shared by Memo generations. +pub(crate) const PROTOCOL_CODE: &str = "spl_memo"; +/// Stable Memo v1 surface code. +pub(crate) const V1_SURFACE_CODE: &str = "spl_memo_v1"; +/// Stable Memo v3 surface code. +pub(crate) const V3_SURFACE_CODE: &str = "spl_memo_v3"; +/// Stable Memo v4 surface code. +pub(crate) const V4_SURFACE_CODE: &str = "spl_memo_v4"; +/// Maximum decoded instruction data retained as a complete Memo payload. +pub(crate) const MAX_MEMO_PAYLOAD_BYTES: usize = 4_096; +/// Maximum payload prefix retained for bounded diagnostics. +pub(crate) const MEMO_DIAGNOSTIC_PREFIX_BYTES: usize = 32; +/// Current stable Memo decoded event contract version. +pub(crate) const MEMO_EVENT_VERSION: u32 = 1; +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_spl_memo"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_memo/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_memo/src/decoder.rs new file mode 100644 index 0000000..0f8ae1a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_memo/src/decoder.rs @@ -0,0 +1,438 @@ +// file: kb_decoder_spl_memo/src/decoder.rs +// version: 10 + +//! Exact SPL Memo v1, v3 and v4 dispatch for the common decode pipeline. + +const MEMO_SURFACES: &[kb_decoder_api::DecoderSurface] = &[ + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::SPL_MEMO_V1_PROGRAM_ID, + surface_code: crate::V1_SURFACE_CODE, + priority: 100, + }, + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::SPL_MEMO_V3_PROGRAM_ID, + surface_code: crate::V3_SURFACE_CODE, + priority: 100, + }, + kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, + surface_code: crate::V4_SURFACE_CODE, + priority: 100, + }, +]; + +const LEGACY_PROGRAM_IDS: &[&str] = &[ + kb_program_ids::SPL_MEMO_V1_PROGRAM_ID, + kb_program_ids::SPL_MEMO_V3_PROGRAM_ID, + kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, +]; + +/// Exact decoder for the three registered SPL Memo generations. +#[derive(Clone, Debug, Default)] +pub struct SplMemoDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::SplMemoDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_spl_memo"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return LEGACY_PROGRAM_IDS; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) { + return kb_decoder_api::DecoderSupport::Yes; + } + return kb_decoder_api::DecoderSupport::No; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +impl kb_decoder_api::InstructionDecoder for crate::SplMemoDecoder { + fn identity(&self) -> kb_decoder_api::DecoderIdentity { + return kb_decoder_api::DecoderIdentity { + name: "spl_memo".to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + }; + } + + fn surfaces(&self) -> &'static [kb_decoder_api::DecoderSurface] { + return MEMO_SURFACES; + } + + fn coverage(&self) -> std::vec::Vec { + return MEMO_SURFACES + .iter() + .map(|surface| { + return kb_decoder_api::DecoderCoverageDeclaration { + program_id: surface.program_id.to_string(), + surface_code: std::option::Option::Some(surface.surface_code.to_string()), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: "add_memo".to_string(), + discriminator_hex: std::option::Option::None, + historical: surface.program_id != kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, + }; + }) + .collect(); + } + + fn recognize( + &self, + input: &kb_store_core::CoreInstructionReplayInput, + ) -> kb_decoder_api::DecoderRecognition { + let surface = MEMO_SURFACES + .iter() + .find(|surface| return surface.program_id == input.program_id); + let surface = match surface { + std::option::Option::Some(value) => value, + std::option::Option::None => return kb_decoder_api::DecoderRecognition::incompatible(), + }; + return kb_decoder_api::DecoderRecognition::compatible( + true, + surface.priority, + std::option::Option::Some(surface.surface_code.to_string()), + std::option::Option::Some("add_memo".to_string()), + std::option::Option::None, + ); + } + + fn decode( + &self, + input: &kb_store_core::CoreInstructionReplayInput, + ) -> kb_decoder_api::DecoderExecutionResult { + if !LEGACY_PROGRAM_IDS.contains(&input.program_id.as_str()) { + return kb_decoder_api::DecoderExecutionResult::unsupported(std::option::Option::None); + } + let result = crate::decode(input); + if matches!( + result.status, + kb_decoder_api::DecoderOutcomeStatus::Failed + | kb_decoder_api::DecoderOutcomeStatus::Unsupported + ) { + tracing::error!( + target: crate::TRACING_TARGET, + action = "decode_failure", + signature = %input.signature, + slot = input.slot, + instruction_path = %input.instruction_path, + program_id = %input.program_id, + processor_name = "spl_memo", + processor_version = env!("CARGO_PKG_VERSION"), + input_key = %input.replay_input_key, + payload_hash = ?input.instruction_payload_hash, + transaction_failed = input.transaction_failed, + result_status = ?result.status, + diagnostics = ?result.diagnostics, + "SPL Memo instruction was not decoded successfully" + ); + } + tracing::debug!( + target: crate::TRACING_TARGET, + action = "decode", + signature = %input.signature, + instruction_path = %input.instruction_path, + program_id = %input.program_id, + transaction_failed = input.transaction_failed, + result_status = ?result.status, + observation_count = result.observations.len(), + "SPL Memo instruction decode completed" + ); + return result; + } +} + +#[cfg(test)] +mod tests { + use base64::Engine; // rust-rules: trait-import + + fn input( + program_id: &str, + payload: &[u8], + accounts: serde_json::Value, + keys: serde_json::Value, + failed: bool, + path: &str, + ) -> kb_store_core::CoreInstructionReplayInput { + let result = kb_store_core::CoreInstructionReplayInput::new( + format!("signature:{path}"), + "signature", + 42, + path, + program_id, + failed, + if failed { + std::option::Option::Some(serde_json::json!({"InstructionError":[0,"Custom"]})) + } else { + std::option::Option::None + }, + keys, + accounts, + std::option::Option::Some(serde_json::json!({ + "dataBase64": base64::engine::general_purpose::STANDARD.encode(payload) + })), + std::option::Option::None, + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("memo replay input failed: {error}"), + }; + } + + fn one_account(signer: bool) -> (serde_json::Value, serde_json::Value) { + return ( + serde_json::json!([{"position":0,"accountIndex":1,"accountKey":"signer"}]), + serde_json::json!([{"accountIndex":1,"accountKey":"signer","signer":signer,"writable":false,"source":"static"}]), + ); + } + + #[test] + fn exact_support_and_interface_ids_cover_all_generations() { + let decoder = crate::SplMemoDecoder; + assert_eq!(spl_memo_interface::v1::ID.to_string(), kb_program_ids::SPL_MEMO_V1_PROGRAM_ID); + assert_eq!(spl_memo_interface::v3::ID.to_string(), kb_program_ids::SPL_MEMO_V3_PROGRAM_ID); + assert_eq!(spl_memo_interface::v4::ID.to_string(), kb_program_ids::SPL_MEMO_V4_PROGRAM_ID); + assert_eq!(kb_decoder_api::InstructionDecoder::surfaces(&decoder).len(), 3); + assert!( + kb_decoder_api::InstructionDecoder::coverage(&decoder) + .iter() + .all(|entry| return entry.entry_code == "add_memo") + ); + } + + #[test] + fn decodes_empty_ascii_unicode_outer_and_inner_payloads() { + for (index, payload) in [b"".as_slice(), b"payment:42".as_slice(), "memo ".as_bytes()] + .iter() + .enumerate() + { + let path = if index == 2 { "1/0" } else { "0" }; + let input = input( + kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, + payload, + serde_json::json!([]), + serde_json::json!([]), + false, + path, + ); + let result = kb_decoder_api::InstructionDecoder::decode(&crate::SplMemoDecoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.observations[0].payload_json["payloadLengthBytes"], + serde_json::json!(payload.len()) + ); + assert!(result.observations[0].observation_committed); + assert_eq!( + result.observations[0].event.source_kind, + if path.contains('/') { + kb_model::EventSourceKind::InnerInstruction + } else { + kb_model::EventSourceKind::Instruction + } + ); + } + } + + #[test] + fn v1_ignores_accounts_while_v3_and_v4_require_every_account_to_sign() { + let (accounts, keys) = one_account(false); + let v1 = input( + kb_program_ids::SPL_MEMO_V1_PROGRAM_ID, + b"legacy", + accounts.clone(), + keys.clone(), + false, + "0", + ); + let v1_result = kb_decoder_api::InstructionDecoder::decode(&crate::SplMemoDecoder, &v1); + assert_eq!(v1_result.observations[0].event.event_name.0, "add_memo"); + assert_eq!( + v1_result.observations[0].payload_json["signerValidation"]["status"], + "not_applicable" + ); + for program_id in + [kb_program_ids::SPL_MEMO_V3_PROGRAM_ID, kb_program_ids::SPL_MEMO_V4_PROGRAM_ID] + { + let input = input(program_id, b"signed", accounts.clone(), keys.clone(), false, "0"); + let result = kb_decoder_api::InstructionDecoder::decode(&crate::SplMemoDecoder, &input); + assert_eq!(result.observations[0].event.event_name.0, "invalid_memo_attempt"); + assert_eq!( + result.observations[0].payload_json["signerValidation"]["status"], + "invalid" + ); + assert!(!result.observations[0].observation_committed); + } + } + + #[test] + fn invalid_utf8_and_failed_transactions_are_explicit_uncommitted_intents() { + let invalid = input( + kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, + &[0xf0, 0x9f, 0x90, 0xff], + serde_json::json!([]), + serde_json::json!([]), + true, + "0", + ); + let invalid_result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplMemoDecoder, &invalid); + assert_eq!(invalid_result.observations[0].event.event_name.0, "invalid_memo_attempt"); + assert_eq!( + invalid_result.observations[0].payload_json["utf8Validation"]["status"], + "invalid" + ); + assert!(!invalid_result.observations[0].observation_committed); + let valid_failed = input( + kb_program_ids::SPL_MEMO_V3_PROGRAM_ID, + b"rolled back", + serde_json::json!([]), + serde_json::json!([]), + true, + "0", + ); + let failed_result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplMemoDecoder, &valid_failed); + assert_eq!(failed_result.observations[0].event.event_name.0, "memo_intent"); + assert!(!failed_result.observations[0].observation_committed); + assert_eq!( + failed_result.observations[0].payload_json["runtimeValidation"], + "not_proven_transaction_failed" + ); + } + + #[test] + fn preserves_duplicate_writable_and_multiple_signer_accounts_in_order() { + let accounts = serde_json::json!([ + {"position":0,"accountIndex":1,"accountKey":"first"}, + {"position":1,"accountIndex":2,"accountKey":"second"}, + {"position":2,"accountIndex":1,"accountKey":"first"} + ]); + let keys = serde_json::json!([ + {"accountIndex":1,"accountKey":"first","signer":true,"writable":true,"source":"static"}, + {"accountIndex":2,"accountKey":"second","signer":true,"writable":false,"source":"static"} + ]); + let input = + input(kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, b"ordered", accounts, keys, false, "0"); + let result = kb_decoder_api::InstructionDecoder::decode(&crate::SplMemoDecoder, &input); + assert_eq!(result.observations[0].payload_json["accounts"][0]["pubkey"], "first"); + assert_eq!(result.observations[0].payload_json["accounts"][0]["writable"], true); + assert_eq!(result.observations[0].payload_json["accounts"][2]["pubkey"], "first"); + assert_eq!( + result.observations[0].payload_json["signerValidation"]["orderedRequiredSigners"], + serde_json::json!(["first", "second", "first"]) + ); + assert_eq!( + result.observations[0].payload_json["signerValidation"]["orderedObservedSigners"], + serde_json::json!(["first", "second", "first"]) + ); + } + + #[test] + fn payload_hash_is_canonical_sha256() { + let input = input( + kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, + b"abc", + serde_json::json!([]), + serde_json::json!([]), + false, + "0", + ); + let result = kb_decoder_api::InstructionDecoder::decode(&crate::SplMemoDecoder, &input); + assert_eq!( + result.observations[0].payload_json["payloadSha256"], + "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" + ); + } + + #[test] + fn payload_bound_accepts_limit_and_rejects_oversize_and_malformed_base64() { + let at_limit = std::vec![b'x'; crate::MAX_MEMO_PAYLOAD_BYTES]; + let cinput = input( + kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, + at_limit.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + "0", + ); + let result = kb_decoder_api::InstructionDecoder::decode(&crate::SplMemoDecoder, &cinput); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + let oversize = std::vec![b'x'; crate::MAX_MEMO_PAYLOAD_BYTES + 1]; + let cinput = input( + kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, + oversize.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + "0", + ); + let result = kb_decoder_api::InstructionDecoder::decode(&crate::SplMemoDecoder, &cinput); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + let mut malformed = input( + kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, + b"valid", + serde_json::json!([]), + serde_json::json!([]), + false, + "0", + ); + malformed.instruction_payload_json = + std::option::Option::Some(serde_json::json!({"dataBase64":"%%%"})); + let result = kb_decoder_api::InstructionDecoder::decode(&crate::SplMemoDecoder, &malformed); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + assert!(result.observations.is_empty()); + } + + #[test] + fn unknown_program_is_incompatible_and_never_emits_an_observation() { + let input = input( + kb_program_ids::SYSTEM_PROGRAM_ID, + b"not memo", + serde_json::json!([]), + serde_json::json!([]), + false, + "0", + ); + let recognition = + kb_decoder_api::InstructionDecoder::recognize(&crate::SplMemoDecoder, &input); + assert!(!recognition.compatible); + let result = kb_decoder_api::InstructionDecoder::decode(&crate::SplMemoDecoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Unsupported); + assert!(result.observations.is_empty()); + } + + #[test] + fn matrix_is_machine_readable_and_covers_required_cases() { + let parsed = serde_json::from_str::(include_str!( + "../../docs/SPL_MEMO_MATRIX.json" + )); + let matrix = match parsed { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("memo matrix is invalid JSON: {error}"), + }; + assert_eq!(matrix["matrixVersion"], 4); + assert_eq!( + matrix["generations"].as_array().map(std::vec::Vec::len), + std::option::Option::Some(3) + ); + let cases = matrix["requiredCorpusCases"].as_array(); + assert!(cases.is_some_and(|values| return values.len() >= 12)); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_memo/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_memo/src/lib.rs new file mode 100644 index 0000000..4a1ad20 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_memo/src/lib.rs @@ -0,0 +1,33 @@ +// file: kb_decoder_spl_memo/src/lib.rs +// version: 5 + +//! Decoder crate for `spl_memo`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; +mod memo; + +/// Maximum decoded instruction data retained as a complete Memo payload. +pub(crate) use crate::constants::MAX_MEMO_PAYLOAD_BYTES; +/// Maximum payload prefix retained for bounded diagnostics. +pub(crate) use crate::constants::MEMO_DIAGNOSTIC_PREFIX_BYTES; +/// Current stable Memo decoded event contract version. +pub(crate) use crate::constants::MEMO_EVENT_VERSION; +/// Stable protocol code shared by Memo generations. +pub(crate) use crate::constants::PROTOCOL_CODE; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; +/// Stable Memo v1 surface code. +pub(crate) use crate::constants::V1_SURFACE_CODE; +/// Stable Memo v3 surface code. +pub(crate) use crate::constants::V3_SURFACE_CODE; +/// Stable Memo v4 surface code. +pub(crate) use crate::constants::V4_SURFACE_CODE; +/// Crate-root access to `decode` from `memo`. +pub(crate) use crate::memo::decode; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::SplMemoDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_memo/src/memo.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_memo/src/memo.rs new file mode 100644 index 0000000..b9f4e99 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_memo/src/memo.rs @@ -0,0 +1,375 @@ +// file: kb_decoder_spl_memo/src/memo.rs +// version: 2 + +//! Bounded Memo payload, account and validation projection. + +use base64::Engine; // rust-rules: trait-import +use sha2::Digest; // rust-rules: trait-import + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum MemoGeneration { + V1, + V3, + V4, +} + +impl MemoGeneration { + fn from_program_id(program_id: &str) -> std::option::Option { + if program_id == kb_program_ids::SPL_MEMO_V1_PROGRAM_ID { + return std::option::Option::Some(Self::V1); + } + if program_id == kb_program_ids::SPL_MEMO_V3_PROGRAM_ID { + return std::option::Option::Some(Self::V3); + } + if program_id == kb_program_ids::SPL_MEMO_V4_PROGRAM_ID { + return std::option::Option::Some(Self::V4); + } + return std::option::Option::None; + } + + fn code(self) -> &'static str { + return match self { + Self::V1 => "v1", + Self::V3 => "v3", + Self::V4 => "v4", + }; + } + + fn surface_code(self) -> &'static str { + return match self { + Self::V1 => crate::V1_SURFACE_CODE, + Self::V3 => crate::V3_SURFACE_CODE, + Self::V4 => crate::V4_SURFACE_CODE, + }; + } + + fn validates_signers(self) -> bool { + return self != Self::V1; + } +} + +struct ResolvedAccounts { + values: serde_json::Value, + ordered_accounts: std::vec::Vec, + ordered_signers: std::vec::Vec, + missing_signers: std::vec::Vec, +} + +pub(crate) fn decode( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_decoder_api::DecoderExecutionResult { + let generation = match MemoGeneration::from_program_id(input.program_id.as_str()) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return kb_decoder_api::DecoderExecutionResult::unsupported(std::option::Option::None); + }, + }; + let bytes = match decode_payload(input) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return failed("memo_payload_unavailable", error.to_string()); + }, + }; + let accounts = match resolve_accounts(input) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return failed("memo_accounts_malformed", error.to_string()); + }, + }; + let text_result = std::str::from_utf8(bytes.as_slice()); + let utf8_valid = text_result.is_ok(); + let signer_valid = !generation.validates_signers() || accounts.missing_signers.is_empty(); + let structurally_valid = utf8_valid && signer_valid; + let event_name = if !structurally_valid { + "invalid_memo_attempt" + } else if input.transaction_failed { + "memo_intent" + } else { + "add_memo" + }; + let memo_text = match text_result { + std::result::Result::Ok(value) => serde_json::Value::String(value.to_string()), + std::result::Result::Err(_error) => serde_json::Value::Null, + }; + let utf8_error_offset = std::str::from_utf8(bytes.as_slice()) + .err() + .map(|error| return error.valid_up_to()); + let signer_status = if !generation.validates_signers() { + "not_applicable" + } else if signer_valid { + "valid" + } else { + "invalid" + }; + let runtime_validation = if input.transaction_failed { + "not_proven_transaction_failed" + } else if structurally_valid { + "proven_by_successful_transaction" + } else { + "inconsistent_with_successful_transaction" + }; + let missing_required_signers = if generation.validates_signers() { + accounts.missing_signers.clone() + } else { + std::vec::Vec::new() + }; + let ordered_required_signers = if generation.validates_signers() { + accounts.ordered_accounts.clone() + } else { + std::vec::Vec::new() + }; + let payload_hash = hash(bytes.as_slice()); + let event = kb_model::DecodedProtocolEvent { + signature: kb_model::Signature(input.signature.clone()), + slot: kb_model::Slot(input.slot), + instruction_path: kb_model::InstructionPath(input.instruction_path.clone()), + program_id: kb_model::ProgramId(input.program_id.clone()), + protocol_code: kb_model::ProtocolCode(crate::PROTOCOL_CODE.to_string()), + surface_code: kb_model::SurfaceCode(generation.surface_code().to_string()), + event_code: kb_model::EventCode(format!("{}.{event_name}", generation.surface_code())), + event_name: kb_model::EventName(event_name.to_string()), + event_family: kb_model::EventFamily::Audit, + source_kind: if input.instruction_path.contains('/') { + kb_model::EventSourceKind::InnerInstruction + } else { + kb_model::EventSourceKind::Instruction + }, + confidence: kb_model::DecoderConfidence::ManualExact, + }; + let observation = kb_decoder_api::DecodedObservation { + event_key: "memo:0".to_string(), + event, + payload_json: serde_json::json!({ + "eventVersion": crate::MEMO_EVENT_VERSION, + "generation": generation.code(), + "programId": input.program_id, + "instructionPath": input.instruction_path, + "instructionLocation": if input.instruction_path.contains('/') { "inner" } else { "outer" }, + "transactionSucceeded": !input.transaction_failed, + "committed": !input.transaction_failed && structurally_valid, + "memoText": memo_text, + "payloadLengthBytes": bytes.len(), + "payloadSha256": payload_hash, + "payloadHexPrefix": hex_prefix(bytes.as_slice()), + "payloadComplete": true, + "utf8Validation": { + "status": if utf8_valid { "valid" } else { "invalid" }, + "invalidFromByte": utf8_error_offset + }, + "signerValidation": { + "requiredForGeneration": generation.validates_signers(), + "accountsIgnoredByRuntime": !generation.validates_signers(), + "status": signer_status, + "orderedRequiredSigners": ordered_required_signers, + "orderedObservedSigners": accounts.ordered_signers, + "missingRequiredSigners": missing_required_signers + }, + "runtimeValidation": runtime_validation, + "accounts": accounts.values, + "diagnostic": if structurally_valid { + serde_json::Value::Null + } else { + serde_json::json!({ + "code": if !utf8_valid { "invalid_utf8" } else { "missing_required_signature" }, + "payloadHexPrefix": hex_prefix(bytes.as_slice()) + }) + } + }), + transaction_failed: input.transaction_failed, + transaction_error: input.transaction_err_json.clone(), + observation_committed: !input.transaction_failed && structurally_valid, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::Manual, + confidence: kb_model::DecoderConfidence::ManualExact, + evidence: std::vec![ + "spl_memo_exact_raw_instruction_data".to_string(), + format!("payload_sha256:{payload_hash}"), + format!("generation:{}", generation.code()), + ], + }, + }; + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Decoded, + recognized_entry_code: std::option::Option::Some("add_memo".to_string()), + observations: std::vec![observation], + diagnostics: std::vec::Vec::new(), + }; +} + +fn decode_payload( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_core::Result> { + let payload = match input.instruction_payload_json.as_ref() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Memo instruction payload is not retained", + )); + }, + }; + let encoded = match payload.get("dataBase64").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Memo instruction payload does not contain dataBase64", + )); + }, + }; + let maximum_encoded = crate::MAX_MEMO_PAYLOAD_BYTES + .saturating_mul(4) + .saturating_div(3) + .saturating_add(4); + if encoded.len() > maximum_encoded { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Memo base64 payload exceeds {maximum_encoded} bytes" + ))); + } + let decoded = match base64::engine::general_purpose::STANDARD.decode(encoded.as_bytes()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Memo payload is not valid base64: {error}" + ))); + }, + }; + if decoded.len() > crate::MAX_MEMO_PAYLOAD_BYTES { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Memo payload exceeds {} decoded bytes", + crate::MAX_MEMO_PAYLOAD_BYTES + ))); + } + return std::result::Result::Ok(decoded); +} + +fn resolve_accounts( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_core::Result { + let instruction_accounts = match input.instruction_accounts_json.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Memo instruction accounts must be a JSON array", + )); + }, + }; + let account_keys = match input.account_keys_json.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Memo transaction account keys must be a JSON array", + )); + }, + }; + let mut values = std::vec::Vec::with_capacity(instruction_accounts.len()); + let mut ordered_accounts = std::vec::Vec::with_capacity(instruction_accounts.len()); + let mut ordered_signers = std::vec::Vec::new(); + let mut missing_signers = std::vec::Vec::new(); + for (position, account) in instruction_accounts.iter().enumerate() { + let account_index = match account.get("accountIndex").and_then(serde_json::Value::as_u64) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Memo instruction account {position} has no accountIndex" + ))); + }, + }; + let account_key = match account.get("accountKey").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) if !value.trim().is_empty() => value, + _ => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Memo instruction account {position} has no accountKey" + ))); + }, + }; + let resolved = account_keys.iter().find(|candidate| { + return candidate.get("accountIndex").and_then(serde_json::Value::as_u64) + == std::option::Option::Some(account_index); + }); + let resolved = match resolved { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Memo account index {account_index} is absent from resolved keys" + ))); + }, + }; + if resolved.get("accountKey").and_then(serde_json::Value::as_str) + != std::option::Option::Some(account_key) + { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Memo account index {account_index} resolves to a different key" + ))); + } + let signer = match resolved.get("signer").and_then(serde_json::Value::as_bool) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Memo account index {account_index} has no signer flag" + ))); + }, + }; + let writable = match resolved.get("writable").and_then(serde_json::Value::as_bool) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Memo account index {account_index} has no writable flag" + ))); + }, + }; + ordered_accounts.push(account_key.to_string()); + if signer { + ordered_signers.push(account_key.to_string()); + } else { + missing_signers.push(account_key.to_string()); + } + let source = match resolved.get("source") { + std::option::Option::Some(value) => value.clone(), + std::option::Option::None => serde_json::Value::Null, + }; + values.push(serde_json::json!({ + "position": position, + "accountIndex": account_index, + "pubkey": account_key, + "signer": signer, + "writable": writable, + "source": source + })); + } + return std::result::Result::Ok(ResolvedAccounts { + values: serde_json::Value::Array(values), + ordered_accounts, + ordered_signers, + missing_signers, + }); +} + +fn hash(bytes: &[u8]) -> std::string::String { + let digest = sha2::Sha256::digest(bytes); + let mut output = std::string::String::with_capacity(64); + for byte in digest { + output.push_str(format!("{byte:02x}").as_str()); + } + return output; +} + +fn hex_prefix(bytes: &[u8]) -> std::string::String { + let length = std::cmp::min(bytes.len(), crate::MEMO_DIAGNOSTIC_PREFIX_BYTES); + let mut output = std::string::String::with_capacity(length.saturating_mul(2)); + for byte in &bytes[..length] { + output.push_str(format!("{byte:02x}").as_str()); + } + return output; +} + +fn failed(code: &str, message: std::string::String) -> kb_decoder_api::DecoderExecutionResult { + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Failed, + recognized_entry_code: std::option::Option::Some("add_memo".to_string()), + observations: std::vec::Vec::new(), + diagnostics: std::vec![kb_decoder_api::DecoderDiagnostic { + code: code.to_string(), + message, + retriable: false, + }], + }; +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_noop/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_spl_noop/Cargo.toml new file mode 100644 index 0000000..f3f53fe --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_noop/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_spl_noop/Cargo.toml +# version: 1 + +[package] +name = "kb_decoder_spl_noop" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_noop/README.md b/migration/khadhroony-bot2-reference/kb_decoder_spl_noop/README.md new file mode 100644 index 0000000..720cca3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_noop/README.md @@ -0,0 +1,17 @@ + + + +# kb_decoder_spl_noop + +Ce crate réserve la frontière de décodage pour le programme SPL Noop. + +## État + +Le program ID et l'identité du crate sont enregistrés, mais aucun payload n'est encore déclaré `decoded`. L'implémentation maximale sera réalisée dans la version dédiée indiquée par le `ROADMAP.md`, à partir des interfaces et fixtures officielles. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs`. +- Le scaffold ne doit produire aucun faux événement avant l'implémentation officielle. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_noop/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_noop/src/constants.rs new file mode 100644 index 0000000..66bd213 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_noop/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_spl_noop/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_spl_noop` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_spl_noop"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_noop/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_noop/src/decoder.rs new file mode 100644 index 0000000..000772d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_noop/src/decoder.rs @@ -0,0 +1,55 @@ +// file: kb_decoder_spl_noop/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_spl_noop` crate. + +/// Reserved decoder implementation for the verified program surface. +#[derive(Clone, Debug, Default)] +pub struct SplNoopDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::SplNoopDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_spl_noop"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::SPL_NOOP_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +#[cfg(test)] +mod tests { + #[test] + fn exposes_every_verified_program_id() { + let decoder = crate::SplNoopDecoder; + let program_ids = kb_decoder_api::ProtocolDecoder::program_ids(&decoder); + assert_eq!(program_ids.len(), 1); + for program_id in program_ids { + assert!(kb_program_ids::find_registered_program_id(program_id).is_some()); + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_noop/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_noop/src/lib.rs new file mode 100644 index 0000000..ffe50ba --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_noop/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_spl_noop/src/lib.rs +// version: 2 + +//! Decoder crate for `spl_noop`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::SplNoopDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/Cargo.toml new file mode 100644 index 0000000..85983a3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_spl_single_pool/Cargo.toml +# version: 1 + +[package] +name = "kb_decoder_spl_single_pool" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/README.md b/migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/README.md new file mode 100644 index 0000000..fa6e2dd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/README.md @@ -0,0 +1,17 @@ + + + +# kb_decoder_spl_single_pool + +Ce crate réserve la frontière de décodage pour le programme SPL Single Pool. + +## État + +Le program ID et l'identité du crate sont enregistrés, mais aucun payload n'est encore déclaré `decoded`. L'implémentation maximale sera réalisée dans la version dédiée indiquée par le `ROADMAP.md`, à partir des interfaces et fixtures officielles. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs`. +- Le scaffold ne doit produire aucun faux événement avant l'implémentation officielle. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/src/constants.rs new file mode 100644 index 0000000..178261a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_spl_single_pool/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_spl_single_pool` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_spl_single_pool"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/src/decoder.rs new file mode 100644 index 0000000..6b3bd90 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/src/decoder.rs @@ -0,0 +1,55 @@ +// file: kb_decoder_spl_single_pool/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_spl_single_pool` crate. + +/// Reserved decoder implementation for the verified program surface. +#[derive(Clone, Debug, Default)] +pub struct SplSinglePoolDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::SplSinglePoolDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_spl_single_pool"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::SPL_SINGLE_POOL_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +#[cfg(test)] +mod tests { + #[test] + fn exposes_every_verified_program_id() { + let decoder = crate::SplSinglePoolDecoder; + let program_ids = kb_decoder_api::ProtocolDecoder::program_ids(&decoder); + assert_eq!(program_ids.len(), 1); + for program_id in program_ids { + assert!(kb_program_ids::find_registered_program_id(program_id).is_some()); + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/src/lib.rs new file mode 100644 index 0000000..ad096e0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_single_pool/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_spl_single_pool/src/lib.rs +// version: 2 + +//! Decoder crate for `spl_single_pool`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::SplSinglePoolDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/Cargo.toml new file mode 100644 index 0000000..379dab5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_spl_stake_pool/Cargo.toml +# version: 1 + +[package] +name = "kb_decoder_spl_stake_pool" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/README.md b/migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/README.md new file mode 100644 index 0000000..8be7af6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_spl_stake_pool + +Ce crate réserve la surface de décodage `spl_stake_pool`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `spl_stake_pool` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/src/constants.rs new file mode 100644 index 0000000..6dc6f5e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_spl_stake_pool/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_spl_stake_pool` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_spl_stake_pool"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/src/decoder.rs new file mode 100644 index 0000000..421db5b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_spl_stake_pool/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_spl_stake_pool` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct SplStakePoolDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::SplStakePoolDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_spl_stake_pool"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::SPL_STAKE_POOL_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/src/lib.rs new file mode 100644 index 0000000..34bb205 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_stake_pool/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_spl_stake_pool/src/lib.rs +// version: 2 + +//! Decoder crate for `spl_stake_pool`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::SplStakePoolDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_token/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_spl_token/Cargo.toml new file mode 100644 index 0000000..94efe97 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_token/Cargo.toml @@ -0,0 +1,25 @@ +# file: kb_decoder_spl_token/Cargo.toml +# version: 3 + +[package] +name = "kb_decoder_spl_token" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +base64.workspace = true +bs58.workspace = true +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +kb_store_core = { path = "../kb_store_core" } +serde_json.workspace = true +sha2.workspace = true +spl-token-interface.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_token/README.md b/migration/khadhroony-bot2-reference/kb_decoder_spl_token/README.md new file mode 100644 index 0000000..4b52d58 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_token/README.md @@ -0,0 +1,89 @@ + + + +# kb_decoder_spl_token + +Ce crate décode exclusivement le programme SPL Token classique +`TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA`. Token-2022, ATA, les interfaces +de métadonnées et les programmes qui invoquent Token par CPI ne sont pas absorbés dans sa +surface. Une instruction Token inner reste néanmoins décodée grâce à son Program ID exact et à +son chemin core stable. + +## Surface couverte + +La couverture compilée contient les 28 tags publiés par `spl-token-interface 3.0.0` : `0..24`, +`38`, `45` et `255`. Elle inclut les variantes historiques, checked, les deux générations sans +Rent, les conversions montant/UI, `WithdrawExcessLamports`, `UnwrapLamports` et `Batch`. + +La publication d'un tag et son déploiement sont traités séparément. `UnwrapLamports` et `Batch` +sont constructibles dans l'interface `3.0.0` et implémentés par p-token `1.0.0`. Leur déploiement +sur le programme classique Devnet est observé par deux simulations réussies le 16 juillet 2026 ; +Localnet et Mainnet restent non testés pour ces tags récents. Les opérations classiques observées +sur Mainnet ou exécutées sur Devnet portent séparément leurs preuves réelles. La matrice normative +est `docs/SPL_TOKEN_MATRIX.json`. + +## Contrat de décodage + +`SplTokenDecoder` implémente les deux contrats communs : + +- `ProtocolDecoder` annonce `Yes` uniquement pour le Program ID Token classique exact ; +- `InstructionDecoder` fournit une surface, 28 déclarations de couverture, une reconnaissance par + tag et un résultat contextualisé pour le replay commun. + +Chaque observation conserve le tag, le wire borné et son SHA-256, le chemin outer/inner, les +comptes dans leur ordre d'origine avec doublons et flags, les rôles prouvables, les paramètres bruts, +la forme d'autorité structurelle et les diagnostics. Les montants `u64` sont écrits en chaînes +décimales dans le JSON pour éviter toute perte à la frontière Tauri. + +Un `Transfer` non checked ne reçoit jamais de mint ou de decimals inventés. Le champ d'inférence +reste explicitement vide tant qu'aucune corrélation core prouvée n'est implémentée. Le décodeur +n'effectue aucune lecture RPC. + +## Transactions échouées et autorités + +Une transaction échouée mais lisible produit une intention structurée avec `committed=false`. Elle +ne prouve aucune mutation de compte Token. Les formes simple et multisig sont distinguées depuis +les metas fournies ; la validation de `M/N` et des membres d'un multisig existant exige un snapshot +stateful et n'est jamais inventée par le décodeur. + +Pour `InitializeMultisig` et `InitializeMultisig2`, le wire `M`, le nombre `N` déduit des comptes et +les bornes officielles `1 <= M <= N <= 11` sont contrôlés structurellement. + +## Batch + +`Batch` est analysé avec des bornes locales de 64 sous-instructions, 512 comptes cumulés et +16 384 octets de payload. Chaque entrée conserve son slice de comptes, son offset de données, son +ordre et un chemin dérivé `/batch/`. Un batch vide, tronqué, avec longueur nulle, +comptes insuffisants ou batch imbriqué échoue avec un diagnostic borné. + +## Limites actuelles + +- Le contrat `CoreInstructionReplayInput` ne transporte pas encore les return data canoniques ; les + trois conversions concernées restent décodées comme intentions, avec validation de return data + explicitement indisponible. +- Le corpus Mainnet prouve les opérations réellement observées, pas l'exhaustivité des 28 tags sur + ce cluster. Les initialisations normalisées par la matérialisation ne sont pas attribuées à une + variante wire sans preuve supplémentaire. +- Localnet et Mainnet restent sans preuve de déploiement de `UnwrapLamports` et `Batch` ; la preuve + Devnet n'est pas extrapolée aux autres clusters. + +## Corpus réel + +Le replay Mainnet du 15 juillet 2026 a décodé 370 instructions sans entrée unsupported, failed ou +unmatched. Il a produit 371 sorties commitées, huit refus correspondant exactement aux huit +transactions échouées et un second replay avec zéro sélection. Les signatures représentatives de +`Transfer`, `Approve`, `CloseAccount`, `TransferChecked`, `BurnChecked` et `SyncNative` sont +conservées par instruction dans la matrice. Le lifecycle Devnet y conserve séparément les +signatures exactes de ses opérations courantes. + +Le probe Devnet du 16 juillet 2026 a simulé sans signature ni envoi un `Batch` contenant un +`TransferChecked` de montant nul, puis un `UnwrapLamports` d'un lamport depuis un compte wrapped SOL +auxiliaire classique. Les deux simulations ont réussi sans erreur runtime, avec respectivement 270 +et 140 compute units. + +## Exemple + +```rust +let decoder = kb_decoder_spl_token::SplTokenDecoder; +let result = kb_decoder_api::InstructionDecoder::decode(&decoder, &input); +``` diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_token/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_token/src/constants.rs new file mode 100644 index 0000000..13198b7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_token/src/constants.rs @@ -0,0 +1,50 @@ +// file: kb_decoder_spl_token/src/constants.rs +// version: 3 + +//! Local constants for the `kb_decoder_spl_token` crate. Program identifiers live in `kb_program_ids`. + +/// Stable protocol and surface code for the classic SPL Token program. +pub(crate) const SURFACE_CODE: &str = "spl_token"; +/// Current stable decoded event contract version. +pub(crate) const TOKEN_EVENT_VERSION: u32 = 1; +/// Maximum retained instruction payload size. +pub(crate) const MAX_INSTRUCTION_BYTES: usize = 16_384; +/// Maximum prefix retained in diagnostics. +pub(crate) const DIAGNOSTIC_PREFIX_BYTES: usize = 32; +/// Maximum decoded sub-instructions in one batch. +pub(crate) const MAX_BATCH_INSTRUCTIONS: usize = 64; +/// Maximum total account metas consumed by one batch. +pub(crate) const MAX_BATCH_ACCOUNTS: usize = 512; +/// One exact instruction entry published by `spl-token-interface` 3.0.0. +pub(crate) const INSTRUCTION_ENTRIES: &[(u8, &str, bool)] = &[ + (0, "initialize_mint", true), + (1, "initialize_account", true), + (2, "initialize_multisig", true), + (3, "transfer", true), + (4, "approve", true), + (5, "revoke", true), + (6, "set_authority", true), + (7, "mint_to", true), + (8, "burn", true), + (9, "close_account", true), + (10, "freeze_account", true), + (11, "thaw_account", true), + (12, "transfer_checked", false), + (13, "approve_checked", false), + (14, "mint_to_checked", false), + (15, "burn_checked", false), + (16, "initialize_account2", true), + (17, "sync_native", false), + (18, "initialize_account3", false), + (19, "initialize_multisig2", false), + (20, "initialize_mint2", false), + (21, "get_account_data_size", false), + (22, "initialize_immutable_owner", false), + (23, "amount_to_ui_amount", false), + (24, "ui_amount_to_amount", false), + (38, "withdraw_excess_lamports", false), + (45, "unwrap_lamports", false), + (255, "batch", false), +]; +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_spl_token"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_token/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_token/src/decoder.rs new file mode 100644 index 0000000..c70deff --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_token/src/decoder.rs @@ -0,0 +1,512 @@ +// file: kb_decoder_spl_token/src/decoder.rs +// version: 12 + +//! Exact classic SPL Token dispatch for the common decode pipeline. + +const TOKEN_SURFACES: &[kb_decoder_api::DecoderSurface] = &[kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::SPL_TOKEN_PROGRAM_ID, + surface_code: crate::SURFACE_CODE, + priority: 100, +}]; + +const PROGRAM_IDS: &[&str] = &[kb_program_ids::SPL_TOKEN_PROGRAM_ID]; + +/// Exact decoder for the classic SPL Token program. +#[derive(Clone, Debug, Default)] +pub struct SplTokenDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::SplTokenDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_spl_token"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return PROGRAM_IDS; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Yes + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +impl kb_decoder_api::InstructionDecoder for crate::SplTokenDecoder { + fn identity(&self) -> kb_decoder_api::DecoderIdentity { + return kb_decoder_api::DecoderIdentity { + name: "spl_token".to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + }; + } + + fn surfaces(&self) -> &'static [kb_decoder_api::DecoderSurface] { + return TOKEN_SURFACES; + } + + fn coverage(&self) -> std::vec::Vec { + return crate::INSTRUCTION_ENTRIES + .iter() + .map(|(tag, code, historical)| { + return kb_decoder_api::DecoderCoverageDeclaration { + program_id: kb_program_ids::SPL_TOKEN_PROGRAM_ID.to_string(), + surface_code: std::option::Option::Some(crate::SURFACE_CODE.to_string()), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: (*code).to_string(), + discriminator_hex: std::option::Option::Some(format!("{tag:02x}")), + historical: *historical, + }; + }) + .collect(); + } + + fn recognize( + &self, + input: &kb_store_core::CoreInstructionReplayInput, + ) -> kb_decoder_api::DecoderRecognition { + if input.program_id != kb_program_ids::SPL_TOKEN_PROGRAM_ID { + return kb_decoder_api::DecoderRecognition::incompatible(); + } + let tag = crate::payload_tag(input); + let entry = tag.and_then(crate::entry_for_tag); + return kb_decoder_api::DecoderRecognition::compatible( + entry.is_some(), + 100, + std::option::Option::Some(crate::SURFACE_CODE.to_string()), + entry.map(|(_, code, _)| return code.to_string()), + tag.map(|value| return format!("{value:02x}")), + ); + } + + fn decode( + &self, + input: &kb_store_core::CoreInstructionReplayInput, + ) -> kb_decoder_api::DecoderExecutionResult { + if input.program_id != kb_program_ids::SPL_TOKEN_PROGRAM_ID { + return kb_decoder_api::DecoderExecutionResult::unsupported(std::option::Option::None); + } + let result = crate::decode(input); + if matches!( + result.status, + kb_decoder_api::DecoderOutcomeStatus::Failed + | kb_decoder_api::DecoderOutcomeStatus::Unsupported + ) { + tracing::error!( + target: crate::TRACING_TARGET, + action = "decode_failure", + signature = %input.signature, + slot = input.slot, + instruction_path = %input.instruction_path, + program_id = %input.program_id, + processor_name = "spl_token", + processor_version = env!("CARGO_PKG_VERSION"), + input_key = %input.replay_input_key, + payload_hash = ?input.instruction_payload_hash, + result_status = ?result.status, + diagnostics = ?result.diagnostics, + "classic SPL Token instruction was not decoded successfully" + ); + } + tracing::debug!( + target: crate::TRACING_TARGET, + action = "decode", + signature = %input.signature, + instruction_path = %input.instruction_path, + transaction_failed = input.transaction_failed, + result_status = ?result.status, + observation_count = result.observations.len(), + "classic SPL Token instruction decode completed" + ); + return result; + } +} + +#[cfg(test)] +mod tests { + use base64::Engine; // rust-rules: trait-import + + fn input( + program_id: &str, + payload: &[u8], + accounts: serde_json::Value, + keys: serde_json::Value, + failed: bool, + path: &str, + ) -> kb_store_core::CoreInstructionReplayInput { + let result = kb_store_core::CoreInstructionReplayInput::new( + format!("signature:{path}"), + "signature", + 42, + path, + program_id, + failed, + if failed { + std::option::Option::Some(serde_json::json!({"InstructionError":[0,"Custom"]})) + } else { + std::option::Option::None + }, + keys, + accounts, + std::option::Option::Some(serde_json::json!({ + "dataBase64": base64::engine::general_purpose::STANDARD.encode(payload) + })), + std::option::Option::None, + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Token replay input failed: {error}"), + }; + } + + fn minimal_wire(tag: u8) -> std::vec::Vec { + return match tag { + 0 | 20 => { + let mut value = std::vec![tag, 9]; + value.extend_from_slice(&[1; 32]); + value.push(0); + value + }, + 2 | 19 => std::vec![tag, 1], + 3 | 4 | 7 | 8 | 23 => { + let mut value = std::vec![tag]; + value.extend_from_slice(&1_u64.to_le_bytes()); + value + }, + 6 => std::vec![tag, 0, 0], + 12 | 13 | 14 | 15 => { + let mut value = std::vec![tag]; + value.extend_from_slice(&1_u64.to_le_bytes()); + value.push(9); + value + }, + 16 | 18 => { + let mut value = std::vec![tag]; + value.extend_from_slice(&[2; 32]); + value + }, + 24 => std::vec![tag, b'1'], + 45 => std::vec![tag, 0], + 255 => std::vec![tag, 0, 1, 1], + _ => std::vec![tag], + }; + } + + fn account(position: usize, signer: bool, writable: bool) -> serde_json::Value { + return serde_json::json!({ + "position": position, + "accountIndex": position, + "accountKey": format!("account{position}"), + "signer": signer, + "writable": writable, + "source": "static", + }); + } + + #[test] + fn exact_support_program_id_and_interface_match() { + assert_eq!(spl_token_interface::ID.to_string(), kb_program_ids::SPL_TOKEN_PROGRAM_ID); + let decoder = crate::SplTokenDecoder; + assert_eq!(kb_decoder_api::InstructionDecoder::surfaces(&decoder).len(), 1); + let observation = kb_model::ProgramObservation { + signature: kb_model::Signature("signature".to_string()), + slot: kb_model::Slot(1), + instruction_path: kb_model::InstructionPath("0".to_string()), + program_id: kb_model::ProgramId(kb_program_ids::SPL_TOKEN_PROGRAM_ID.to_string()), + discriminator_8: std::option::Option::None, + data_len: 1, + accounts_len: 0, + failed: false, + }; + assert_eq!( + kb_decoder_api::ProtocolDecoder::supports_observation(&decoder, &observation), + kb_decoder_api::DecoderSupport::Yes + ); + } + + #[test] + fn coverage_matrix_and_compiled_entries_are_equal() { + let matrix: serde_json::Value = + match serde_json::from_str(include_str!("../../docs/SPL_TOKEN_MATRIX.json")) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("SPL Token matrix is invalid: {error}"), + }; + let rows = match matrix.get("instructions").and_then(serde_json::Value::as_array) { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("SPL Token matrix has no instructions"), + }; + let matrix_entries = rows + .iter() + .map(|row| { + return ( + row.get("tag").and_then(serde_json::Value::as_u64).unwrap_or(u64::MAX) as u8, + row.get("name").and_then(serde_json::Value::as_str).unwrap_or(""), + ); + }) + .collect::>(); + let compiled_entries = crate::INSTRUCTION_ENTRIES + .iter() + .map(|(tag, code, _)| return (*tag, *code)) + .collect::>(); + assert_eq!(matrix_entries, compiled_entries); + assert_eq!(kb_decoder_api::InstructionDecoder::coverage(&crate::SplTokenDecoder).len(), 28); + } + + #[test] + fn cluster_evidence_scopes_recent_instruction_deployment_exactly() { + let matrix: serde_json::Value = + match serde_json::from_str(include_str!("../../docs/SPL_TOKEN_MATRIX.json")) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("SPL Token matrix is invalid: {error}"), + }; + assert_eq!( + matrix["deploymentAudit"]["mainnet"]["status"], + "real_corpus_decode_and_materialization_validated" + ); + assert_eq!(matrix["corpusAudit"]["mainnet"]["invariants"]["uncommittedOutputs"], 0); + assert_eq!(matrix["corpusAudit"]["mainnet"]["invariants"]["secondReplaySelected"], 0); + assert_eq!(matrix["corpusAudit"]["devnet"]["recentInstructionProbe"]["submission"], false); + assert_eq!( + matrix["corpusAudit"]["devnet"]["recentInstructionProbe"]["batch"]["success"], + true + ); + assert_eq!( + matrix["corpusAudit"]["devnet"]["recentInstructionProbe"]["batch"]["computeUnits"], + 270 + ); + assert_eq!( + matrix["corpusAudit"]["devnet"]["recentInstructionProbe"]["unwrapLamports"]["success"], + true + ); + assert_eq!( + matrix["corpusAudit"]["devnet"]["recentInstructionProbe"]["unwrapLamports"]["computeUnits"], + 140 + ); + + let instructions = match matrix.get("instructions").and_then(serde_json::Value::as_array) { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("SPL Token matrix has no instructions"), + }; + for recent_name in ["unwrap_lamports", "batch"] { + let recent = match instructions.iter().find(|row| { + return row.get("name").and_then(serde_json::Value::as_str) + == std::option::Option::Some(recent_name); + }) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + panic!("recent SPL Token instruction {recent_name} is absent") + }, + }; + assert_eq!(recent["executorSupport"]["constructibility"], "official_builder_proven"); + assert_eq!( + recent["executorSupport"]["clusterDeployment"], + "devnet_simulation_succeeded_2026-07-16" + ); + assert_eq!(recent["clusterStatus"]["localnet"], "not_tested"); + assert_eq!(recent["clusterStatus"]["devnet"], "simulation_succeeded_2026-07-16"); + assert_eq!(recent["clusterStatus"]["mainnet"], "not_tested"); + assert_eq!(recent["realSignatures"].as_array().map(std::vec::Vec::len), Some(0)); + } + } + + #[test] + fn every_published_tag_decodes_and_matches_official_unpack() { + for (tag, code, _) in crate::INSTRUCTION_ENTRIES { + let wire = minimal_wire(*tag); + let official = spl_token_interface::instruction::TokenInstruction::unpack(&wire); + assert!(official.is_ok(), "official unpack rejected tag {tag}"); + let input = input( + kb_program_ids::SPL_TOKEN_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + "0", + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplTokenDecoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(result.recognized_entry_code.as_deref(), std::option::Option::Some(*code)); + } + } + + #[test] + fn failed_transfer_is_an_exact_uncommitted_inner_intent_without_invented_mint() { + let mut wire = std::vec![3]; + wire.extend_from_slice(&u64::MAX.to_le_bytes()); + let accounts = serde_json::json!([ + {"position":0,"accountIndex":0,"accountKey":"source"}, + {"position":1,"accountIndex":1,"accountKey":"destination"}, + {"position":2,"accountIndex":2,"accountKey":"authority"} + ]); + let keys = serde_json::json!([ + {"accountIndex":0,"accountKey":"source","signer":false,"writable":true,"source":"static"}, + {"accountIndex":1,"accountKey":"destination","signer":false,"writable":true,"source":"static"}, + {"accountIndex":2,"accountKey":"authority","signer":true,"writable":false,"source":"static"} + ]); + let input = input( + kb_program_ids::SPL_TOKEN_PROGRAM_ID, + wire.as_slice(), + accounts, + keys, + true, + "2/1", + ); + let result = kb_decoder_api::InstructionDecoder::decode(&crate::SplTokenDecoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert!(!result.observations[0].observation_committed); + assert_eq!( + result.observations[0].payload_json["parameters"]["amountRaw"], + u64::MAX.to_string() + ); + assert_eq!(result.observations[0].payload_json["inference"]["mintInvented"], false); + assert_eq!( + result.observations[0].event.source_kind, + kb_model::EventSourceKind::InnerInstruction + ); + } + + #[test] + fn suffix_unknown_empty_truncated_and_invalid_utf8_are_bounded() { + let mut transfer = minimal_wire(3); + transfer.extend_from_slice(&[0xaa, 0xbb]); + let suffix = input( + kb_program_ids::SPL_TOKEN_PROGRAM_ID, + transfer.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + "0", + ); + let suffix_result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplTokenDecoder, &suffix); + assert_eq!(suffix_result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(suffix_result.observations[0].payload_json["wire"]["suffixLengthBytes"], 2); + for wire in [std::vec::Vec::new(), std::vec![3, 1], std::vec![24, 0xff], std::vec![255]] { + let input = input( + kb_program_ids::SPL_TOKEN_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + "0", + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplTokenDecoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + assert!(result.observations.is_empty()); + } + let unknown = input( + kb_program_ids::SPL_TOKEN_PROGRAM_ID, + &[44], + serde_json::json!([]), + serde_json::json!([]), + false, + "0", + ); + let result = kb_decoder_api::InstructionDecoder::decode(&crate::SplTokenDecoder, &unknown); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Unsupported); + assert!(!result.diagnostics.is_empty()); + } + + #[test] + fn batch_preserves_order_slices_paths_and_rejects_nested_batch() { + let mut batch = std::vec![255, 0, 9, 3]; + batch.extend_from_slice(&1_u64.to_le_bytes()); + batch.extend_from_slice(&[0, 1, 17]); + let cinput = input( + kb_program_ids::SPL_TOKEN_PROGRAM_ID, + batch.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + "4/2", + ); + let result = kb_decoder_api::InstructionDecoder::decode(&crate::SplTokenDecoder, &cinput); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(result.observations.len(), 3); + assert_eq!(result.observations[1].event.instruction_path.0, "4/2/batch/0"); + assert_eq!(result.observations[2].event.instruction_path.0, "4/2/batch/1"); + assert_eq!(result.observations[0].payload_json["parameters"]["subInstructionCount"], 2); + let nested = input( + kb_program_ids::SPL_TOKEN_PROGRAM_ID, + &[255, 0, 1, 255], + serde_json::json!([]), + serde_json::json!([]), + false, + "0", + ); + let nested_result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplTokenDecoder, &nested); + assert_eq!(nested_result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + + #[test] + fn authority_forms_and_multisig_threshold_diagnostics_are_explicit() { + let wire = minimal_wire(3); + let simple_accounts = serde_json::json!([ + {"position":0,"accountIndex":0,"accountKey":"account0"}, + {"position":1,"accountIndex":1,"accountKey":"account1"}, + {"position":2,"accountIndex":2,"accountKey":"account2"} + ]); + let simple_keys = serde_json::json!([ + account(0, false, true), + account(1, false, true), + account(2, true, false) + ]); + let simple = input( + kb_program_ids::SPL_TOKEN_PROGRAM_ID, + wire.as_slice(), + simple_accounts.clone(), + simple_keys, + false, + "0", + ); + let simple_result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplTokenDecoder, &simple); + assert_eq!(simple_result.observations[0].payload_json["authority"]["form"], "single"); + let mut multisig_accounts = simple_accounts.as_array().cloned().unwrap_or_default(); + multisig_accounts + .push(serde_json::json!({"position":3,"accountIndex":3,"accountKey":"account3"})); + let multisig_keys = serde_json::json!([ + account(0, false, true), + account(1, false, true), + account(2, false, false), + account(3, true, false) + ]); + let multisig = input( + kb_program_ids::SPL_TOKEN_PROGRAM_ID, + wire.as_slice(), + serde_json::Value::Array(multisig_accounts), + multisig_keys, + false, + "0", + ); + let multisig_result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplTokenDecoder, &multisig); + assert_eq!(multisig_result.observations[0].payload_json["authority"]["form"], "multisig"); + assert_eq!( + multisig_result.observations[0].payload_json["authority"]["statefulMultisigValidation"], + "requires_multisig_account_snapshot" + ); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_token/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_token/src/lib.rs new file mode 100644 index 0000000..0272fd0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_token/src/lib.rs @@ -0,0 +1,37 @@ +// file: kb_decoder_spl_token/src/lib.rs +// version: 5 + +//! Decoder crate for `spl_token`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; +mod token; + +/// Maximum prefix retained in diagnostics. +pub(crate) use crate::constants::DIAGNOSTIC_PREFIX_BYTES; +/// One exact instruction entry published by `spl-token-interface` 3.0.0. +pub(crate) use crate::constants::INSTRUCTION_ENTRIES; +/// Maximum total account metas consumed by one batch. +pub(crate) use crate::constants::MAX_BATCH_ACCOUNTS; +/// Maximum decoded sub-instructions in one batch. +pub(crate) use crate::constants::MAX_BATCH_INSTRUCTIONS; +/// Maximum retained instruction payload size. +pub(crate) use crate::constants::MAX_INSTRUCTION_BYTES; +/// Stable protocol and surface code for the classic SPL Token program. +pub(crate) use crate::constants::SURFACE_CODE; +/// Current stable decoded event contract version. +pub(crate) use crate::constants::TOKEN_EVENT_VERSION; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; +/// Crate-root access to `decode` from `token`. +pub(crate) use crate::token::decode; +/// Crate-root access to `entry_for_tag` from `token`. +pub(crate) use crate::token::entry_for_tag; +/// Crate-root access to `payload_tag` from `token`. +pub(crate) use crate::token::payload_tag; + +/// Initial decoder implementation placeholder. +pub use crate::decoder::SplTokenDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_token/src/token.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_token/src/token.rs new file mode 100644 index 0000000..6386c4c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_token/src/token.rs @@ -0,0 +1,1005 @@ +// file: kb_decoder_spl_token/src/token.rs +// version: 4 + +//! Bounded classic SPL Token wire, account and authority decoding. + +use base64::Engine; // rust-rules: trait-import +use sha2::Digest; // rust-rules: trait-import + +#[derive(Clone, Debug)] +struct ParsedInstruction { + code: &'static str, + tag: u8, + parameters: serde_json::Value, + consumed: usize, +} + +#[derive(Clone, Debug)] +struct ResolvedAccount { + position: usize, + account_index: u64, + account_key: std::string::String, + signer: bool, + writable: bool, + source: serde_json::Value, +} + +pub(crate) fn entry_for_tag(tag: u8) -> std::option::Option<(u8, &'static str, bool)> { + return crate::INSTRUCTION_ENTRIES + .iter() + .copied() + .find(|(candidate, _, _)| return *candidate == tag); +} + +pub(crate) fn payload_tag( + input: &kb_store_core::CoreInstructionReplayInput, +) -> std::option::Option { + let bytes = decode_payload(input).ok(); + return bytes.and_then(|value| return value.first().copied()); +} + +pub(crate) fn decode( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_decoder_api::DecoderExecutionResult { + let bytes = match decode_payload(input) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return failed( + std::option::Option::None, + "token_payload_unavailable", + error.to_string(), + ); + }, + }; + let tag = match bytes.first().copied() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return failed( + std::option::Option::None, + "token_payload_empty", + "classic SPL Token instruction payload is empty".to_string(), + ); + }, + }; + let entry = match crate::entry_for_tag(tag) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Unsupported, + recognized_entry_code: std::option::Option::None, + observations: std::vec::Vec::new(), + diagnostics: std::vec![kb_decoder_api::DecoderDiagnostic { + code: "unknown_token_instruction_tag".to_string(), + message: format!( + "unknown classic SPL Token tag {tag}; payloadPrefixHex={}", + hex_prefix(bytes.as_slice()) + ), + retriable: false, + }], + }; + }, + }; + let parsed = match parse_instruction(bytes.as_slice(), false) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => { + return failed( + std::option::Option::Some(entry.1), + "malformed_token_instruction", + format!("{message}; payloadPrefixHex={}", hex_prefix(bytes.as_slice())), + ); + }, + }; + let accounts = match resolve_accounts(input) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return failed( + std::option::Option::Some(parsed.code), + "token_accounts_malformed", + error.to_string(), + ); + }, + }; + let (parameters, batch_children) = if parsed.code == "batch" { + match batch_observations(input, bytes.as_slice(), accounts.as_slice()) { + std::result::Result::Ok((children, manifest)) => (manifest, children), + std::result::Result::Err(message) => { + return failed( + std::option::Option::Some("batch"), + "malformed_token_batch", + message, + ); + }, + } + } else { + (parsed.parameters.clone(), std::vec::Vec::new()) + }; + let account_projection = project_accounts(parsed.code, accounts.as_slice()); + let suffix_length = bytes.len().saturating_sub(parsed.consumed); + let authority = authority_projection(parsed.code, accounts.as_slice()); + let semantic_diagnostics = + semantic_diagnostics(parsed.code, parameters.clone(), accounts.as_slice(), suffix_length); + let committed = !input.transaction_failed; + let parent = observation( + input, + parsed.code, + parsed.tag, + parameters, + account_projection, + authority, + semantic_diagnostics, + bytes.as_slice(), + suffix_length, + committed, + format!("token:{}:0", parsed.code), + input.instruction_path.clone(), + event_family(parsed.code), + ); + let mut observations = std::vec![parent]; + observations.extend(batch_children); + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Decoded, + recognized_entry_code: std::option::Option::Some(parsed.code.to_string()), + observations, + diagnostics: std::vec::Vec::new(), + }; +} + +fn decode_payload( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_core::Result> { + let payload = match input.instruction_payload_json.as_ref() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "classic SPL Token instruction payload is not retained", + )); + }, + }; + let encoded = match payload.get("dataBase64").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "classic SPL Token payload does not contain dataBase64", + )); + }, + }; + let maximum_encoded = crate::MAX_INSTRUCTION_BYTES + .saturating_mul(4) + .saturating_div(3) + .saturating_add(4); + if encoded.len() > maximum_encoded { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "classic SPL Token base64 payload exceeds {maximum_encoded} bytes" + ))); + } + let decoded = match base64::engine::general_purpose::STANDARD.decode(encoded.as_bytes()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "classic SPL Token payload is not valid base64: {error}" + ))); + }, + }; + if decoded.len() > crate::MAX_INSTRUCTION_BYTES { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "classic SPL Token payload exceeds {} decoded bytes", + crate::MAX_INSTRUCTION_BYTES + ))); + } + return std::result::Result::Ok(decoded); +} + +fn parse_instruction( + bytes: &[u8], + nested_in_batch: bool, +) -> std::result::Result { + let tag = match bytes.first().copied() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err("empty instruction".to_string()); + }, + }; + let code = match crate::entry_for_tag(tag) { + std::option::Option::Some((_, value, _)) => value, + std::option::Option::None => { + return std::result::Result::Err(format!("unknown instruction tag {tag}")); + }, + }; + if nested_in_batch && tag == 255 { + return std::result::Result::Err("nested Batch instruction is forbidden".to_string()); + } + let result = match tag { + 0 | 20 => parse_initialize_mint(bytes, code, tag), + 1 | 5 | 9 | 10 | 11 | 17 | 21 | 22 | 38 => std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::json!({}), + consumed: 1, + }), + 255 => std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::json!({}), + consumed: bytes.len(), + }), + 2 | 19 => parse_one_u8(bytes, code, tag, "m"), + 3 | 4 | 7 | 8 | 23 => parse_one_u64(bytes, code, tag, "amountRaw"), + 6 => parse_set_authority(bytes, code, tag), + 12 | 13 | 14 | 15 => parse_amount_decimals(bytes, code, tag), + 16 | 18 => parse_pubkey_parameter(bytes, code, tag, "owner"), + 24 => parse_ui_amount(bytes, code, tag), + 45 => parse_unwrap_lamports(bytes, code, tag), + _ => std::result::Result::Err(format!("unsupported instruction tag {tag}")), + }; + return result; +} + +fn parse_initialize_mint( + bytes: &[u8], + code: &'static str, + tag: u8, +) -> std::result::Result { + if bytes.len() < 35 { + return std::result::Result::Err(format!("{code} requires at least 35 bytes")); + } + let mint_authority = pubkey_text(&bytes[2..34]); + let (freeze_authority, option_length) = match pubkey_option(&bytes[34..]) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::json!({ + "decimals": bytes[1], + "mintAuthority": mint_authority, + "freezeAuthority": freeze_authority, + }), + consumed: 34 + option_length, + }); +} + +fn parse_one_u8( + bytes: &[u8], + code: &'static str, + tag: u8, + field: &str, +) -> std::result::Result { + if bytes.len() < 2 { + return std::result::Result::Err(format!("{code} requires 2 bytes")); + } + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: one_parameter(field, serde_json::Value::from(bytes[1])), + consumed: 2, + }); +} + +fn parse_one_u64( + bytes: &[u8], + code: &'static str, + tag: u8, + field: &str, +) -> std::result::Result { + let amount = match read_u64(bytes, 1) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: one_parameter(field, serde_json::Value::String(amount.to_string())), + consumed: 9, + }); +} + +fn parse_set_authority( + bytes: &[u8], + code: &'static str, + tag: u8, +) -> std::result::Result { + if bytes.len() < 3 { + return std::result::Result::Err("set_authority requires at least 3 bytes".to_string()); + } + let authority_type = match bytes[1] { + 0 => "mint_tokens", + 1 => "freeze_account", + 2 => "account_owner", + 3 => "close_account", + value => return std::result::Result::Err(format!("invalid authority type {value}")), + }; + let (new_authority, option_length) = match pubkey_option(&bytes[2..]) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::json!({ + "authorityType": authority_type, + "newAuthority": new_authority, + }), + consumed: 2 + option_length, + }); +} + +fn parse_amount_decimals( + bytes: &[u8], + code: &'static str, + tag: u8, +) -> std::result::Result { + if bytes.len() < 10 { + return std::result::Result::Err(format!("{code} requires 10 bytes")); + } + let amount = match read_u64(bytes, 1) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::json!({ + "amountRaw": amount.to_string(), + "decimals": bytes[9], + }), + consumed: 10, + }); +} + +fn parse_pubkey_parameter( + bytes: &[u8], + code: &'static str, + tag: u8, + field: &str, +) -> std::result::Result { + if bytes.len() < 33 { + return std::result::Result::Err(format!("{code} requires 33 bytes")); + } + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: one_parameter(field, serde_json::Value::String(pubkey_text(&bytes[1..33]))), + consumed: 33, + }); +} + +fn parse_ui_amount( + bytes: &[u8], + code: &'static str, + tag: u8, +) -> std::result::Result { + let text = match std::str::from_utf8(&bytes[1..]) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "ui amount is not UTF-8 at byte {}", + error.valid_up_to() + )); + }, + }; + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::json!({"uiAmount": text}), + consumed: bytes.len(), + }); +} + +fn parse_unwrap_lamports( + bytes: &[u8], + code: &'static str, + tag: u8, +) -> std::result::Result { + if bytes.len() < 2 { + return std::result::Result::Err("unwrap_lamports requires an option tag".to_string()); + } + let (amount, consumed) = match bytes[1] { + 0 => (serde_json::Value::Null, 2), + 1 => { + let value = match read_u64(bytes, 2) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + (serde_json::Value::String(value.to_string()), 10) + }, + value => return std::result::Result::Err(format!("invalid unwrap amount option {value}")), + }; + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::json!({"amountLamports": amount}), + consumed, + }); +} + +fn read_u64(bytes: &[u8], offset: usize) -> std::result::Result { + let slice = match bytes.get(offset..offset.saturating_add(8)) { + std::option::Option::Some(value) if value.len() == 8 => value, + _ => return std::result::Result::Err("truncated little-endian u64".to_string()), + }; + let array = match <[u8; 8]>::try_from(slice) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return std::result::Result::Err("invalid little-endian u64".to_string()); + }, + }; + return std::result::Result::Ok(u64::from_le_bytes(array)); +} + +fn pubkey_option( + bytes: &[u8], +) -> std::result::Result<(serde_json::Value, usize), std::string::String> { + return match bytes.first().copied() { + std::option::Option::Some(0) => std::result::Result::Ok((serde_json::Value::Null, 1)), + std::option::Option::Some(1) if bytes.len() >= 33 => { + std::result::Result::Ok((serde_json::Value::String(pubkey_text(&bytes[1..33])), 33)) + }, + std::option::Option::Some(1) => { + std::result::Result::Err("truncated optional pubkey".to_string()) + }, + std::option::Option::Some(value) => { + std::result::Result::Err(format!("invalid optional pubkey tag {value}")) + }, + std::option::Option::None => { + std::result::Result::Err("missing optional pubkey tag".to_string()) + }, + }; +} + +fn pubkey_text(bytes: &[u8]) -> std::string::String { + return bs58::encode(bytes).into_string(); +} + +fn one_parameter(field: &str, value: serde_json::Value) -> serde_json::Value { + let mut parameters = serde_json::Map::new(); + parameters.insert(field.to_string(), value); + return serde_json::Value::Object(parameters); +} + +fn resolve_accounts( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_core::Result> { + let instruction_accounts = match input.instruction_accounts_json.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "classic SPL Token instruction accounts must be a JSON array", + )); + }, + }; + let account_keys = match input.account_keys_json.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "classic SPL Token transaction account keys must be a JSON array", + )); + }, + }; + let mut output = std::vec::Vec::with_capacity(instruction_accounts.len()); + for (position, account) in instruction_accounts.iter().enumerate() { + let account_index = match account.get("accountIndex").and_then(serde_json::Value::as_u64) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "classic SPL Token instruction account {position} has no accountIndex" + ))); + }, + }; + let account_key = match account.get("accountKey").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) if !value.trim().is_empty() => value, + _ => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "classic SPL Token instruction account {position} has no accountKey" + ))); + }, + }; + let resolved = account_keys.iter().find(|candidate| { + return candidate.get("accountIndex").and_then(serde_json::Value::as_u64) + == std::option::Option::Some(account_index); + }); + let resolved = match resolved { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "classic SPL Token account index {account_index} is not resolved" + ))); + }, + }; + if resolved.get("accountKey").and_then(serde_json::Value::as_str) + != std::option::Option::Some(account_key) + { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "classic SPL Token account index {account_index} resolves to a different key" + ))); + } + let signer = match resolved.get("signer").and_then(serde_json::Value::as_bool) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "classic SPL Token account index {account_index} has no signer flag" + ))); + }, + }; + let writable = match resolved.get("writable").and_then(serde_json::Value::as_bool) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "classic SPL Token account index {account_index} has no writable flag" + ))); + }, + }; + output.push(ResolvedAccount { + position, + account_index, + account_key: account_key.to_string(), + signer, + writable, + source: resolved.get("source").cloned().unwrap_or(serde_json::Value::Null), + }); + } + return std::result::Result::Ok(output); +} + +fn project_accounts(code: &str, accounts: &[ResolvedAccount]) -> serde_json::Value { + let authority_index = authority_index(code); + let values = accounts + .iter() + .enumerate() + .map(|(role_position, account)| { + return serde_json::json!({ + "position": account.position, + "rolePosition": role_position, + "accountIndex": account.account_index, + "accountKey": account.account_key, + "signer": account.signer, + "writable": account.writable, + "source": account.source, + "role": account_role(code, role_position, authority_index), + }); + }) + .collect::>(); + return serde_json::Value::Array(values); +} + +fn authority_projection(code: &str, accounts: &[ResolvedAccount]) -> serde_json::Value { + let authority_index = authority_index(code); + let index = match authority_index { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return serde_json::json!({ + "form": "not_applicable", + "declaredAuthority": serde_json::Value::Null, + "providedSigners": [], + "statefulMultisigValidation": "not_applicable", + }); + }, + }; + let authority = accounts.get(index); + let provided_signers = accounts + .iter() + .skip(index.saturating_add(1)) + .filter(|account| return account.signer) + .map(|account| return account.account_key.clone()) + .collect::>(); + let form = if accounts.len() > index.saturating_add(1) { "multisig" } else { "single" }; + return serde_json::json!({ + "form": form, + "declaredAuthority": authority.map(|value| return value.account_key.clone()), + "declaredAuthoritySigner": authority.map(|value| return value.signer), + "providedSigners": provided_signers, + "providedSignerCount": provided_signers.len(), + "statefulMultisigValidation": if form == "multisig" { + "requires_multisig_account_snapshot" + } else { + "not_applicable" + }, + "membersClaimed": false, + }); +} + +fn authority_index(code: &str) -> std::option::Option { + return match code { + "transfer" + | "approve" + | "mint_to" + | "burn" + | "close_account" + | "freeze_account" + | "thaw_account" + | "withdraw_excess_lamports" + | "unwrap_lamports" => std::option::Option::Some(2), + "revoke" | "set_authority" => std::option::Option::Some(1), + "transfer_checked" | "approve_checked" => std::option::Option::Some(3), + "mint_to_checked" | "burn_checked" => std::option::Option::Some(2), + _ => std::option::Option::None, + }; +} + +fn account_role( + code: &str, + position: usize, + authority_index: std::option::Option, +) -> std::string::String { + if authority_index.is_some_and(|index| return position > index) { + return "multisig_signer".to_string(); + } + let role = match (code, position) { + ("initialize_mint", 0) | ("initialize_mint2", 0) => "mint", + ("initialize_mint", 1) => "rent_sysvar", + ("initialize_account", 0) | ("initialize_account2", 0) | ("initialize_account3", 0) => { + "token_account" + }, + ("initialize_account", 1) | ("initialize_account2", 1) | ("initialize_account3", 1) => { + "mint" + }, + ("initialize_account", 2) => "owner", + ("initialize_account", 3) | ("initialize_account2", 2) => "rent_sysvar", + ("initialize_multisig", 0) | ("initialize_multisig2", 0) => "multisig_account", + ("initialize_multisig", 1) => "rent_sysvar", + ("initialize_multisig", _) | ("initialize_multisig2", _) => "multisig_member", + ("transfer", 0) | ("transfer_checked", 0) => "source_token_account", + ("transfer", 1) => "destination_token_account", + ("transfer_checked", 1) => "mint", + ("transfer_checked", 2) => "destination_token_account", + ("approve", 0) | ("approve_checked", 0) | ("revoke", 0) => "source_token_account", + ("approve", 1) | ("approve_checked", 2) => "delegate", + ("approve_checked", 1) => "mint", + ("mint_to", 0) | ("mint_to_checked", 0) => "mint", + ("mint_to", 1) | ("mint_to_checked", 1) => "destination_token_account", + ("burn", 0) | ("burn_checked", 0) => "source_token_account", + ("burn", 1) | ("burn_checked", 1) => "mint", + ("close_account", 0) | ("withdraw_excess_lamports", 0) | ("unwrap_lamports", 0) => { + "source_token_account" + }, + ("close_account", 1) | ("withdraw_excess_lamports", 1) | ("unwrap_lamports", 1) => { + "destination_account" + }, + ("freeze_account", 0) | ("thaw_account", 0) => "token_account", + ("freeze_account", 1) | ("thaw_account", 1) => "mint", + ("set_authority", 0) => "authority_target", + ("sync_native", 0) | ("initialize_immutable_owner", 0) => "token_account", + ("sync_native", 1) => "rent_sysvar", + ("get_account_data_size", 0) | ("amount_to_ui_amount", 0) | ("ui_amount_to_amount", 0) => { + "mint" + }, + (_, value) if authority_index == std::option::Option::Some(value) => "authority", + _ => "unresolved", + }; + return role.to_string(); +} + +fn semantic_diagnostics( + code: &str, + parameters: serde_json::Value, + accounts: &[ResolvedAccount], + suffix_length: usize, +) -> serde_json::Value { + let mut values = std::vec::Vec::new(); + if suffix_length > 0 { + values.push(serde_json::json!({ + "code": "ignored_wire_suffix", + "lengthBytes": suffix_length, + "runtimeSemantics": "official_interface_unpack_ignores_suffix_for_this_variant", + })); + } + let valid_count = valid_account_count(code, accounts.len()); + if !valid_count { + values.push(serde_json::json!({ + "code": "unexpected_account_count", + "provided": accounts.len(), + })); + } + if matches!(code, "initialize_multisig" | "initialize_multisig2") { + let member_start = if code == "initialize_multisig" { 2 } else { 1 }; + let n = accounts.len().saturating_sub(member_start); + let m = parameters.get("m").and_then(serde_json::Value::as_u64).unwrap_or(0); + if !(1..=11).contains(&n) || m == 0 || m > n as u64 { + values.push(serde_json::json!({ + "code": "invalid_multisig_threshold", + "m": m, + "n": n, + "minSigners": 1, + "maxSigners": 11, + })); + } + } + let authority_index = authority_index(code); + if let std::option::Option::Some(index) = authority_index { + if let std::option::Option::Some(authority) = accounts.get(index) { + let multisig_form = accounts.len() > index.saturating_add(1); + if !multisig_form && !authority.signer { + values.push(serde_json::json!({"code":"missing_single_authority_signature"})); + } + if multisig_form { + let invalid_signers = accounts + .iter() + .skip(index.saturating_add(1)) + .filter(|account| return !account.signer) + .map(|account| return account.position) + .collect::>(); + if !invalid_signers.is_empty() { + values.push(serde_json::json!({ + "code": "multisig_meta_missing_signature", + "positions": invalid_signers, + })); + } + } + } + } + return serde_json::Value::Array(values); +} + +fn valid_account_count(code: &str, count: usize) -> bool { + return match code { + "initialize_mint" => count == 2, + "initialize_mint2" => count == 1, + "initialize_account" => count == 4, + "initialize_account2" => count == 3, + "initialize_account3" => count == 2, + "initialize_multisig" => (3..=13).contains(&count), + "initialize_multisig2" => (2..=12).contains(&count), + "transfer" + | "approve" + | "mint_to" + | "burn" + | "close_account" + | "freeze_account" + | "thaw_account" + | "withdraw_excess_lamports" + | "unwrap_lamports" => count >= 3, + "revoke" | "set_authority" => count >= 2, + "transfer_checked" | "approve_checked" => count >= 4, + "mint_to_checked" | "burn_checked" => count >= 3, + "sync_native" => count == 1 || count == 2, + "get_account_data_size" + | "initialize_immutable_owner" + | "amount_to_ui_amount" + | "ui_amount_to_amount" => count == 1, + "batch" => true, + _ => false, + }; +} + +#[allow(clippy::too_many_arguments)] +fn observation( + input: &kb_store_core::CoreInstructionReplayInput, + code: &str, + tag: u8, + parameters: serde_json::Value, + accounts: serde_json::Value, + authority: serde_json::Value, + semantic_diagnostics: serde_json::Value, + wire: &[u8], + suffix_length: usize, + committed: bool, + event_key: std::string::String, + instruction_path: std::string::String, + family: kb_model::EventFamily, +) -> kb_decoder_api::DecodedObservation { + let wire_hash = hash(wire); + let event = kb_model::DecodedProtocolEvent { + signature: kb_model::Signature(input.signature.clone()), + slot: kb_model::Slot(input.slot), + instruction_path: kb_model::InstructionPath(instruction_path.clone()), + program_id: kb_model::ProgramId(input.program_id.clone()), + protocol_code: kb_model::ProtocolCode(crate::SURFACE_CODE.to_string()), + surface_code: kb_model::SurfaceCode(crate::SURFACE_CODE.to_string()), + event_code: kb_model::EventCode(format!("{}.{}", crate::SURFACE_CODE, code)), + event_name: kb_model::EventName(code.to_string()), + event_family: family, + source_kind: if input.instruction_path.contains('/') { + kb_model::EventSourceKind::InnerInstruction + } else { + kb_model::EventSourceKind::Instruction + }, + confidence: kb_model::DecoderConfidence::ManualExact, + }; + return kb_decoder_api::DecodedObservation { + event_key, + event, + payload_json: serde_json::json!({ + "eventVersion": crate::TOKEN_EVENT_VERSION, + "programId": input.program_id, + "instruction": code, + "wireTag": tag, + "wireTagHex": format!("{tag:02x}"), + "instructionPath": instruction_path, + "instructionLocation": if input.instruction_path.contains('/') { "inner" } else { "outer" }, + "transactionSucceeded": !input.transaction_failed, + "committed": committed, + "parameters": parameters, + "accounts": accounts, + "authority": authority, + "semanticDiagnostics": semantic_diagnostics, + "wire": { + "lengthBytes": wire.len(), + "sha256": wire_hash, + "prefixHex": hex_prefix(wire), + "suffixLengthBytes": suffix_length, + "complete": true, + }, + "inference": { + "mintFromCoreBalanceChanges": serde_json::Value::Null, + "mintInvented": false, + "rpcAccountReadUsed": false, + }, + "returnData": { + "availableInCoreReplayInput": false, + "validated": false, + }, + }), + transaction_failed: input.transaction_failed, + transaction_error: input.transaction_err_json.clone(), + observation_committed: committed, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::Manual, + confidence: kb_model::DecoderConfidence::ManualExact, + evidence: std::vec![ + "spl_token_interface_3_0_0_wire_audit".to_string(), + format!("wire_tag:{tag}"), + format!("wire_sha256:{wire_hash}"), + ], + }, + }; +} + +fn batch_observations( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], + accounts: &[ResolvedAccount], +) -> std::result::Result< + (std::vec::Vec, serde_json::Value), + std::string::String, +> { + if bytes.len() == 1 { + return std::result::Result::Err("Batch requires at least one sub-instruction".to_string()); + } + let mut cursor = 1_usize; + let mut account_cursor = 0_usize; + let mut index = 0_usize; + let mut observations = std::vec::Vec::new(); + let mut manifest = std::vec::Vec::new(); + while cursor < bytes.len() { + if index >= crate::MAX_BATCH_INSTRUCTIONS { + return std::result::Result::Err(format!( + "Batch exceeds {} sub-instructions", + crate::MAX_BATCH_INSTRUCTIONS + )); + } + let account_count = match bytes.get(cursor).copied() { + std::option::Option::Some(value) => value as usize, + std::option::Option::None => { + return std::result::Result::Err(format!( + "Batch entry {index} has no account count" + )); + }, + }; + let data_length = match bytes.get(cursor.saturating_add(1)).copied() { + std::option::Option::Some(value) => value as usize, + std::option::Option::None => { + return std::result::Result::Err(format!("Batch entry {index} has no data length")); + }, + }; + if data_length == 0 { + return std::result::Result::Err(format!("Batch entry {index} has empty data")); + } + let data_start = cursor.saturating_add(2); + let data_end = data_start.saturating_add(data_length); + let child_wire = match bytes.get(data_start..data_end) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!( + "Batch entry {index} data length exceeds payload" + )); + }, + }; + let account_end = account_cursor.saturating_add(account_count); + if account_end > crate::MAX_BATCH_ACCOUNTS || account_end > accounts.len() { + return std::result::Result::Err(format!( + "Batch entry {index} account slice exceeds provided accounts or bound" + )); + } + let parsed = match parse_instruction(child_wire, true) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => { + return std::result::Result::Err(format!("Batch entry {index}: {message}")); + }, + }; + let child_accounts = &accounts[account_cursor..account_end]; + let suffix_length = child_wire.len().saturating_sub(parsed.consumed); + observations.push(observation( + input, + parsed.code, + parsed.tag, + parsed.parameters.clone(), + project_accounts(parsed.code, child_accounts), + authority_projection(parsed.code, child_accounts), + semantic_diagnostics(parsed.code, parsed.parameters, child_accounts, suffix_length), + child_wire, + suffix_length, + !input.transaction_failed, + format!("token:batch:{index}"), + format!("{}/batch/{index}", input.instruction_path), + event_family(parsed.code), + )); + manifest.push(serde_json::json!({ + "position": index, + "instruction": parsed.code, + "wireTag": parsed.tag, + "accountStart": account_cursor, + "accountCount": account_count, + "dataOffset": data_start, + "dataLength": data_length, + "derivedInstructionPath": format!("{}/batch/{index}", input.instruction_path), + })); + cursor = data_end; + account_cursor = account_end; + index = index.saturating_add(1); + } + if account_cursor != accounts.len() { + return std::result::Result::Err(format!( + "Batch consumed {account_cursor} of {} provided accounts", + accounts.len() + )); + } + return std::result::Result::Ok(( + observations, + serde_json::json!({ + "subInstructionCount": manifest.len(), + "subInstructions": manifest, + "nestedBatchAllowed": false, + }), + )); +} + +fn event_family(code: &str) -> kb_model::EventFamily { + return match code { + "set_authority" | "initialize_multisig" | "initialize_multisig2" => { + kb_model::EventFamily::Admin + }, + "initialize_mint" | "initialize_mint2" | "mint_to" | "mint_to_checked" => { + kb_model::EventFamily::TokenMint + }, + "burn" | "burn_checked" => kb_model::EventFamily::TokenBurn, + "batch" | "get_account_data_size" | "amount_to_ui_amount" | "ui_amount_to_amount" => { + kb_model::EventFamily::Audit + }, + _ => kb_model::EventFamily::TokenAccount, + }; +} + +fn hash(bytes: &[u8]) -> std::string::String { + let digest = sha2::Sha256::digest(bytes); + let mut output = std::string::String::with_capacity(64); + for byte in digest { + output.push_str(format!("{byte:02x}").as_str()); + } + return output; +} + +fn hex_prefix(bytes: &[u8]) -> std::string::String { + let length = std::cmp::min(bytes.len(), crate::DIAGNOSTIC_PREFIX_BYTES); + let mut output = std::string::String::with_capacity(length.saturating_mul(2)); + for byte in &bytes[..length] { + output.push_str(format!("{byte:02x}").as_str()); + } + return output; +} + +fn failed( + entry_code: std::option::Option<&str>, + code: &str, + message: std::string::String, +) -> kb_decoder_api::DecoderExecutionResult { + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Failed, + recognized_entry_code: entry_code.map(str::to_string), + observations: std::vec::Vec::new(), + diagnostics: std::vec![kb_decoder_api::DecoderDiagnostic { + code: code.to_string(), + message, + retriable: false, + }], + }; +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/Cargo.toml new file mode 100644 index 0000000..f120340 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/Cargo.toml @@ -0,0 +1,26 @@ +# file: kb_decoder_spl_token_2022/Cargo.toml +# version: 7 + +[package] +name = "kb_decoder_spl_token_2022" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +base64.workspace = true +bs58.workspace = true +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +kb_store_core = { path = "../kb_store_core" } +serde_json.workspace = true +sha2.workspace = true +spl-elgamal-registry-interface.workspace = true +spl-token-2022-interface.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/README.md b/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/README.md new file mode 100644 index 0000000..294f836 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/README.md @@ -0,0 +1,30 @@ + + + +# kb_decoder_spl_token_2022 + +Ce crate décode la surface `spl_token_2022` afin de normaliser les événements token fondamentaux. + +La première tranche d'audit publie `docs/SPL_TOKEN_2022_MATRIX.json`. Elle inventorie les 48 tags +de premier niveau et les 29 types TLV de production. La résolution opérateur confirme +`spl-token-2022-interface 3.1.1` avec `default + serde` et +`spl-elgamal-registry-interface 0.2.1` avec `default`. Le scaffold reste volontairement inchangé +tant que les sous-discriminants, comptes exacts et processors ne sont pas confirmés. + +La crate opérationnelle utilise la cible de tracing canonique `kb_decoder_spl_token_2022`. Pendant +la phase d'audit, l'appel du scaffold émet seulement un diagnostic `debug` structuré indiquant +qu'aucun événement métier n'a encore été produit ; il ne revendique aucun décodage effectif. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. +- Le seul Program ID accepté sera `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb`. +- Le registre ElGamal, le programme ZK ElGamal natif et les implémentations externes de Transfer + Hook, Token Group ou Token Metadata conservent des dispatchs et matrices séparés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/constants.rs new file mode 100644 index 0000000..f94355b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/constants.rs @@ -0,0 +1,72 @@ +// file: kb_decoder_spl_token_2022/src/constants.rs +// version: 5 + +//! Local constants for the exact Token-2022 decoder. Program identifiers live in `kb_program_ids`. + +/// Stable Token-2022 surface code. +pub(crate) const SURFACE_CODE: &str = "spl_token_2022"; +/// Current decoded event contract version. +pub(crate) const TOKEN_EVENT_VERSION: u32 = 1; +/// Maximum retained instruction payload size. +pub(crate) const MAX_INSTRUCTION_BYTES: usize = 16_384; +/// Maximum retained UTF-8 string size for embedded Token Metadata instructions. +pub(crate) const MAX_METADATA_STRING_BYTES: usize = 4_096; +/// Maximum prefix retained in diagnostics. +pub(crate) const DIAGNOSTIC_PREFIX_BYTES: usize = 32; +/// Maximum decoded sub-instructions in one batch. +pub(crate) const MAX_BATCH_INSTRUCTIONS: usize = 64; +/// Maximum total account metas consumed by one batch. +pub(crate) const MAX_BATCH_ACCOUNTS: usize = 512; +/// Exact top-level instruction inventory published by the resolved Token-2022 interface. +pub(crate) const INSTRUCTION_ENTRIES: &[(u8, &str, bool)] = &[ + (0, "initialize_mint", false), + (1, "initialize_account", false), + (2, "initialize_multisig", false), + (3, "transfer", false), + (4, "approve", false), + (5, "revoke", false), + (6, "set_authority", false), + (7, "mint_to", false), + (8, "burn", false), + (9, "close_account", false), + (10, "freeze_account", false), + (11, "thaw_account", false), + (12, "transfer_checked", false), + (13, "approve_checked", false), + (14, "mint_to_checked", false), + (15, "burn_checked", false), + (16, "initialize_account2", false), + (17, "sync_native", false), + (18, "initialize_account3", false), + (19, "initialize_multisig2", false), + (20, "initialize_mint2", false), + (21, "get_account_data_size", false), + (22, "initialize_immutable_owner", false), + (23, "amount_to_ui_amount", false), + (24, "ui_amount_to_amount", false), + (25, "initialize_mint_close_authority", false), + (26, "transfer_fee_extension", false), + (27, "confidential_transfer_extension", false), + (28, "default_account_state_extension", false), + (29, "reallocate", false), + (30, "memo_transfer_extension", false), + (31, "create_native_mint", false), + (32, "initialize_non_transferable_mint", false), + (33, "interest_bearing_mint_extension", false), + (34, "cpi_guard_extension", false), + (35, "initialize_permanent_delegate", false), + (36, "transfer_hook_extension", false), + (37, "confidential_transfer_fee_extension", false), + (38, "withdraw_excess_lamports", false), + (39, "metadata_pointer_extension", false), + (40, "group_pointer_extension", false), + (41, "group_member_pointer_extension", false), + (42, "confidential_mint_burn_extension", false), + (43, "scaled_ui_amount_extension", false), + (44, "pausable_extension", false), + (45, "unwrap_lamports", false), + (46, "permissioned_burn_extension", false), + (255, "batch", false), +]; +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_spl_token_2022"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/decoder.rs new file mode 100644 index 0000000..cd6a9af --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/decoder.rs @@ -0,0 +1,1268 @@ +// file: kb_decoder_spl_token_2022/src/decoder.rs +// version: 17 + +//! Exact Token-2022 dispatch for the common decode pipeline. + +const TOKEN_SURFACES: &[kb_decoder_api::DecoderSurface] = &[kb_decoder_api::DecoderSurface { + program_id: kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + surface_code: crate::SURFACE_CODE, + priority: 100, +}]; + +const PROGRAM_IDS: &[&str] = &[kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID]; + +/// Exact decoder for the Token-2022 program. +#[derive(Clone, Debug, Default)] +pub struct SplToken2022Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::SplToken2022Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_spl_token_2022"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return PROGRAM_IDS; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Yes + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +impl kb_decoder_api::InstructionDecoder for crate::SplToken2022Decoder { + fn identity(&self) -> kb_decoder_api::DecoderIdentity { + return kb_decoder_api::DecoderIdentity { + name: "spl_token_2022".to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + }; + } + + fn surfaces(&self) -> &'static [kb_decoder_api::DecoderSurface] { + return TOKEN_SURFACES; + } + + fn coverage(&self) -> std::vec::Vec { + return crate::INSTRUCTION_ENTRIES + .iter() + .map(|(tag, code, historical)| { + return kb_decoder_api::DecoderCoverageDeclaration { + program_id: kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string(), + surface_code: std::option::Option::Some(crate::SURFACE_CODE.to_string()), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: (*code).to_string(), + discriminator_hex: std::option::Option::Some(format!("{tag:02x}")), + historical: *historical, + }; + }) + .collect(); + } + + fn recognize( + &self, + input: &kb_store_core::CoreInstructionReplayInput, + ) -> kb_decoder_api::DecoderRecognition { + if input.program_id != kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID { + return kb_decoder_api::DecoderRecognition::incompatible(); + } + let tag = crate::payload_tag(input); + let entry = tag.and_then(crate::entry_for_tag); + return kb_decoder_api::DecoderRecognition::compatible( + entry.is_some(), + 100, + std::option::Option::Some(crate::SURFACE_CODE.to_string()), + entry.map(|(_, code, _)| return code.to_string()), + tag.map(|value| return format!("{value:02x}")), + ); + } + + fn decode( + &self, + input: &kb_store_core::CoreInstructionReplayInput, + ) -> kb_decoder_api::DecoderExecutionResult { + if input.program_id != kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID { + return kb_decoder_api::DecoderExecutionResult::unsupported(std::option::Option::None); + } + let result = crate::decode(input); + if matches!( + result.status, + kb_decoder_api::DecoderOutcomeStatus::Failed + | kb_decoder_api::DecoderOutcomeStatus::Unsupported + ) { + tracing::error!( + target: crate::TRACING_TARGET, + action = "decode_failure", + signature = %input.signature, + slot = input.slot, + instruction_path = %input.instruction_path, + program_id = %input.program_id, + processor_name = "spl_token_2022", + processor_version = env!("CARGO_PKG_VERSION"), + input_key = %input.replay_input_key, + payload_hash = ?input.instruction_payload_hash, + result_status = ?result.status, + diagnostics = ?result.diagnostics, + "Token-2022 instruction was not decoded successfully" + ); + } + tracing::debug!( + target: crate::TRACING_TARGET, + action = "decode", + signature = %input.signature, + instruction_path = %input.instruction_path, + transaction_failed = input.transaction_failed, + result_status = ?result.status, + observation_count = result.observations.len(), + "Token-2022 instruction decode completed" + ); + return result; + } +} + +#[cfg(test)] +mod tests { + use base64::Engine; // rust-rules: trait-import + + fn input( + program_id: &str, + payload: &[u8], + accounts: serde_json::Value, + keys: serde_json::Value, + failed: bool, + path: &str, + ) -> kb_store_core::CoreInstructionReplayInput { + let result = kb_store_core::CoreInstructionReplayInput::new( + format!("signature:{path}"), + "signature", + 42, + path, + program_id, + failed, + if failed { + std::option::Option::Some(serde_json::json!({"InstructionError":[0,"Custom"]})) + } else { + std::option::Option::None + }, + keys, + accounts, + std::option::Option::Some(serde_json::json!({ + "dataBase64": base64::engine::general_purpose::STANDARD.encode(payload) + })), + std::option::Option::None, + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Token-2022 replay input failed: {error}"), + }; + } + + fn account(position: usize, signer: bool, writable: bool) -> serde_json::Value { + return serde_json::json!({ + "position": position, + "accountIndex": position, + "accountKey": format!("account{position}"), + "signer": signer, + "writable": writable, + "source": "static", + }); + } + + #[test] + fn exact_token_2022_dispatch_decodes_checked_transfer() { + let mut wire = std::vec![12]; + wire.extend_from_slice(&u64::MAX.to_le_bytes()); + wire.push(9); + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([ + {"position":0,"accountIndex":0,"accountKey":"account0"}, + {"position":1,"accountIndex":1,"accountKey":"account1"}, + {"position":2,"accountIndex":2,"accountKey":"account2"}, + {"position":3,"accountIndex":3,"accountKey":"account3"} + ]), + serde_json::json!([ + account(0, false, true), + account(1, false, false), + account(2, false, true), + account(3, true, false), + ]), + false, + "0", + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(result.recognized_entry_code.as_deref(), Some("transfer_checked")); + assert_eq!(result.observations.len(), 1); + assert!(result.observations[0].observation_committed); + assert_eq!( + result.observations[0].event.program_id.0, + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID + ); + assert_eq!( + result.observations[0].payload_json["parameters"]["amountRaw"], + u64::MAX.to_string() + ); + assert_eq!(result.observations[0].payload_json["parameters"]["decimals"], 9); + } + + #[test] + fn failed_inner_token_2022_instruction_remains_uncommitted() { + let mut wire = std::vec![7]; + wire.extend_from_slice(&42_u64.to_le_bytes()); + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([ + {"position":0,"accountIndex":0,"accountKey":"account0"}, + {"position":1,"accountIndex":1,"accountKey":"account1"}, + {"position":2,"accountIndex":2,"accountKey":"account2"} + ]), + serde_json::json!([ + account(0, false, true), + account(1, false, true), + account(2, true, false), + ]), + true, + "1/0", + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!(result.recognized_entry_code.as_deref(), Some("mint_to")); + assert_eq!(result.observations.len(), 1); + assert!(!result.observations[0].observation_committed); + assert!(result.observations[0].transaction_failed); + assert_eq!(result.observations[0].event.instruction_path.0, "1/0"); + } + + #[test] + fn interest_bearing_non_transferable_permanent_delegate_and_reallocate_decode_exactly() { + let mut initialize_interest = vec![33, 0]; + initialize_interest.extend_from_slice(&[7_u8; 32]); + initialize_interest.extend_from_slice(&(-125_i16).to_le_bytes()); + let cases = vec![ + (initialize_interest, "initialize_interest_bearing_mint", serde_json::json!(-125)), + (vec![33, 1, 250, 0], "update_interest_bearing_rate", serde_json::json!(250)), + (vec![32], "initialize_non_transferable_mint", serde_json::Value::Null), + ( + { + let mut value = vec![35]; + value.extend_from_slice(&[9_u8; 32]); + value + }, + "initialize_permanent_delegate", + serde_json::Value::Null, + ), + (vec![29, 9, 0, 10, 0, 12, 0], "reallocate", serde_json::Value::Null), + ]; + for (wire, expected, expected_rate) in cases { + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([{"position":0,"accountIndex":0,"accountKey":"account0"}]), + serde_json::json!([account(0, true, true)]), + false, + "0", + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded, "{expected}"); + assert_eq!(result.recognized_entry_code.as_deref(), Some(expected)); + assert_eq!(result.observations[0].event.event_name.0, expected); + if !expected_rate.is_null() { + assert_eq!( + result.observations[0].payload_json["parameters"]["rateBasisPoints"], + expected_rate + ); + } + } + } + + #[test] + fn interest_bearing_wire_rejects_unknown_truncated_and_suffixed_forms() { + for wire in [vec![33], vec![33, 0], vec![33, 1, 0], vec![33, 1, 0, 0, 0], vec![33, 2]] { + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + "0", + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + } + + #[test] + fn embedded_metadata_and_group_interfaces_decode_as_token_2022_leaves() { + fn string(value: &str) -> std::vec::Vec { + let mut output = std::vec::Vec::new(); + output.extend_from_slice(&(value.len() as u32).to_le_bytes()); + output.extend_from_slice(value.as_bytes()); + return output; + } + let mut metadata = vec![0xd2, 0xe1, 0x1e, 0xa2, 0x58, 0xb8, 0x4d, 0x8d]; + metadata.extend_from_slice(string("Token").as_slice()); + metadata.extend_from_slice(string("TKN").as_slice()); + metadata.extend_from_slice(string("https://example.invalid/token.json").as_slice()); + let mut group = vec![0x79, 0x71, 0x6c, 0x27, 0x36, 0x33, 0x00, 0x04]; + group.extend_from_slice(&[7_u8; 32]); + group.extend_from_slice(&u64::MAX.to_le_bytes()); + for (wire, expected, account_count) in [ + (metadata, "initialize_token_metadata", 4_usize), + (group, "initialize_token_group", 3_usize), + ( + vec![0x98, 0x20, 0xde, 0xb0, 0xdf, 0xed, 0x74, 0x86], + "initialize_token_group_member", + 5_usize, + ), + ] { + let instruction_accounts = (0..account_count) + .map(|position| return serde_json::json!({"position":position,"accountIndex":position,"accountKey":format!("account{position}")})) + .collect::>(); + let resolved_accounts = (0..account_count) + .map(|position| { + return account(position, position + 1 == account_count, true); + }) + .collect::>(); + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::Value::Array(instruction_accounts), + serde_json::Value::Array(resolved_accounts), + false, + "0", + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded, "{expected}"); + assert_eq!(result.recognized_entry_code.as_deref(), Some(expected)); + assert_eq!( + result.observations[0].event.program_id.0, + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID + ); + } + } + + #[test] + fn embedded_interfaces_reject_truncation_suffix_and_unbounded_metadata_strings() { + let malformed = [ + vec![0xd2, 0xe1, 0x1e, 0xa2, 0x58, 0xb8, 0x4d, 0x8d], + vec![0x98, 0x20, 0xde, 0xb0, 0xdf, 0xed, 0x74, 0x86, 1], + { + let mut value = vec![0xd2, 0xe1, 0x1e, 0xa2, 0x58, 0xb8, 0x4d, 0x8d]; + value.extend_from_slice( + &((crate::MAX_METADATA_STRING_BYTES + 1) as u32).to_le_bytes(), + ); + value + }, + ]; + for wire in malformed { + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + "0", + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + } + + #[test] + fn pointer_and_transfer_hook_extensions_decode_all_published_leaves() { + for (tag, initialize_code, update_code, address_field) in [ + ( + 36_u8, + "initialize_transfer_hook", + "update_transfer_hook_program_id", + "programId", + ), + ( + 39_u8, + "initialize_metadata_pointer", + "update_metadata_pointer", + "metadataAddress", + ), + (40_u8, "initialize_group_pointer", "update_group_pointer", "groupAddress"), + ( + 41_u8, + "initialize_group_member_pointer", + "update_group_member_pointer", + "memberAddress", + ), + ] { + let mut initialize = vec![tag, 0]; + initialize.extend_from_slice(&[7_u8; 32]); + initialize.extend_from_slice(&[9_u8; 32]); + let mut update = vec![tag, 1]; + update.extend_from_slice(&[11_u8; 32]); + for (wire, expected) in [(initialize, initialize_code), (update, update_code)] { + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([{"position":0,"accountIndex":0,"accountKey":"account0"}]), + serde_json::json!([account(0, true, true)]), + false, + "0", + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!( + result.status, + kb_decoder_api::DecoderOutcomeStatus::Decoded, + "{expected}" + ); + assert_eq!(result.recognized_entry_code.as_deref(), Some(expected)); + assert!( + result.observations[0].payload_json["parameters"][address_field].is_string() + ); + } + } + } + + #[test] + fn pointer_and_transfer_hook_extensions_reject_unknown_truncated_and_suffixed_wire() { + for wire in [ + vec![36], + vec![36, 2], + vec![39, 0, 1], + { + let mut value = vec![40, 1]; + value.extend_from_slice(&[3_u8; 33]); + value + }, + { + let mut value = vec![41, 0]; + value.extend_from_slice(&[4_u8; 63]); + value + }, + ] { + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + "0", + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + } + + #[test] + fn classic_token_and_unknown_programs_never_emit_token_2022_events() { + for program_id in [kb_program_ids::SPL_TOKEN_PROGRAM_ID, kb_program_ids::SYSTEM_PROGRAM_ID] + { + let input = + input(program_id, &[17], serde_json::json!([]), serde_json::json!([]), false, "0"); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Unsupported); + assert!(result.observations.is_empty()); + } + } + + fn matrix() -> serde_json::Value { + return match serde_json::from_str(include_str!("../../docs/SPL_TOKEN_2022_MATRIX.json")) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("invalid Token-2022 matrix JSON: {error}"), + }; + } + + #[test] + fn initial_matrix_has_exact_program_boundary_and_candidate_inventory() { + let matrix = matrix(); + assert_eq!(matrix["programId"], kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID); + assert_eq!(matrix["surfaceEquality"]["topLevelInstructionCount"], 48); + assert_eq!(matrix["surfaceEquality"]["productionExtensionTypeCount"], 29); + assert_eq!(matrix["instructions"].as_array().map(std::vec::Vec::len), Some(48)); + assert_eq!(matrix["extensionTypes"].as_array().map(std::vec::Vec::len), Some(29)); + } + + #[test] + fn initial_matrix_tags_are_unique_and_include_recent_surface() { + let matrix = matrix(); + let rows = match matrix["instructions"].as_array() { + std::option::Option::Some(rows) => rows, + std::option::Option::None => panic!("Token-2022 instruction rows missing"), + }; + let mut tags = std::collections::BTreeSet::new(); + for row in rows { + let tag = match row["tag"].as_u64() { + std::option::Option::Some(tag) => tag, + std::option::Option::None => panic!("Token-2022 instruction tag missing"), + }; + assert!(tags.insert(tag), "duplicate Token-2022 instruction tag {tag}"); + } + assert_eq!( + rows.iter().find(|row| return row["tag"] == 46).map(|row| return &row["name"]), + Some(&serde_json::json!("permissioned_burn_extension")), + ); + assert_eq!( + rows.iter().find(|row| return row["tag"] == 255).map(|row| return &row["name"]), + Some(&serde_json::json!("batch")), + ); + } + + #[test] + fn extension_types_are_contiguous_and_external_interfaces_stay_explicit() { + let matrix = matrix(); + let rows = match matrix["extensionTypes"].as_array() { + std::option::Option::Some(rows) => rows, + std::option::Option::None => panic!("Token-2022 extension rows missing"), + }; + for (expected, row) in rows.iter().enumerate() { + assert_eq!(row["type"].as_u64(), Some(expected as u64)); + } + assert_eq!(rows[19]["name"], "token_metadata"); + assert_eq!(rows[21]["name"], "token_group"); + assert_eq!(rows[23]["name"], "token_group_member"); + assert!(matrix["strictProgramBoundary"]["externalInterfaceRule"].is_string()); + } + + #[test] + fn matrix_records_operator_resolution_and_keeps_elgamal_technically_separate() { + let matrix = matrix(); + assert_eq!( + spl_token_2022_interface::ID.to_string(), + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + ); + assert_eq!( + spl_elgamal_registry_interface::ID.to_string(), + "regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg", + ); + assert_eq!(spl_elgamal_registry_interface::REGISTRY_ADDRESS_SEED, b"elgamal-registry",); + assert_eq!(matrix["interfaceAudit"]["resolvedVersion"], "3.1.1"); + assert_eq!( + matrix["interfaceAudit"]["resolvedFeatures"], + serde_json::json!(["default", "serde"]) + ); + assert_eq!(matrix["interfaceAudit"]["workspaceCargoLockPresentInProvidedArchive"], true); + assert_eq!(matrix["elgamalRegistryBoundary"]["resolvedVersion"], "0.2.1"); + assert_eq!( + matrix["elgamalRegistryBoundary"]["programId"], + "regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg" + ); + assert_eq!(matrix["elgamalRegistryBoundary"]["sharedDecoder"], false); + assert_eq!(matrix["elgamalRegistryBoundary"]["sharedExecutor"], false); + } + + #[test] + fn matrix_matches_the_pinned_processor_dispatch_shape() { + let matrix = matrix(); + assert_eq!(matrix["processorAudit"]["auditedVersion"], "11.0.0"); + assert_eq!( + matrix["processorAudit"]["sourceCommit"], + "e18f9c6f9bf6044b934f48e3090e8e59e4820f02", + ); + assert_eq!(matrix["surfaceEquality"]["extensionDispatchFamilyCount"], 15); + assert_eq!(matrix["surfaceEquality"]["extensionSubInstructionCount"], 58); + assert_eq!(matrix["surfaceEquality"]["embeddedInterfaceFallbackCount"], 9); + assert_eq!(matrix["surfaceEquality"]["processorRoutableLeafInstructionCount"], 100); + assert_eq!( + matrix["processorDispatch"]["embeddedInterfaceFallbacks"] + .as_array() + .map(std::vec::Vec::len), + Some(9), + ); + } + + #[test] + fn matrix_leaf_inventory_equals_compiled_decoder_coverage() { + let matrix = matrix(); + let base_rows = match matrix["baseInstructionWireAudit"]["rows"].as_array() { + std::option::Option::Some(rows) => rows, + std::option::Option::None => panic!("Token-2022 base wire rows missing"), + }; + let extension_families = match matrix["processorDispatch"]["extensionFamilies"].as_array() { + std::option::Option::Some(rows) => rows, + std::option::Option::None => panic!("Token-2022 extension families missing"), + }; + let embedded_fallbacks = match matrix["processorDispatch"]["embeddedInterfaceFallbacks"] + .as_array() + { + std::option::Option::Some(rows) => rows, + std::option::Option::None => panic!("Token-2022 embedded interface fallbacks missing"), + }; + let mut leaf_keys = std::collections::BTreeSet::new(); + for row in base_rows { + let tag = match row["tag"].as_u64() { + std::option::Option::Some(tag) => tag, + std::option::Option::None => panic!("Token-2022 base leaf tag missing"), + }; + assert!( + leaf_keys.insert(format!("base:{tag}")), + "duplicate Token-2022 base leaf {tag}" + ); + } + let mut extension_leaf_count = 0usize; + for family in extension_families { + let tag = match family["tag"].as_u64() { + std::option::Option::Some(tag) => tag, + std::option::Option::None => panic!("Token-2022 extension family tag missing"), + }; + let leaves = match family["subInstructions"].as_array() { + std::option::Option::Some(leaves) => leaves, + std::option::Option::None => panic!("Token-2022 extension family leaves missing"), + }; + assert_eq!(family["subInstructionCount"].as_u64(), Some(leaves.len() as u64)); + extension_leaf_count += leaves.len(); + for leaf in leaves { + let name = match leaf.as_str() { + std::option::Option::Some(name) => name, + std::option::Option::None => panic!("Token-2022 extension leaf name missing"), + }; + assert!( + leaf_keys.insert(format!("extension:{tag}:{name}")), + "duplicate Token-2022 extension leaf {tag}:{name}", + ); + } + } + for fallback in embedded_fallbacks { + let interface = match fallback["interface"].as_str() { + std::option::Option::Some(interface) => interface, + std::option::Option::None => panic!("Token-2022 embedded interface name missing"), + }; + let name = match fallback["name"].as_str() { + std::option::Option::Some(name) => name, + std::option::Option::None => panic!("Token-2022 embedded fallback name missing"), + }; + assert!( + leaf_keys.insert(format!("embedded:{interface}:{name}")), + "duplicate Token-2022 embedded leaf {interface}:{name}", + ); + } + assert_eq!(base_rows.len(), 33); + assert_eq!(extension_leaf_count, 58); + assert_eq!(embedded_fallbacks.len(), 9); + assert_eq!(leaf_keys.len(), 100); + assert_eq!( + matrix["surfaceEquality"]["compiledDecoderCoverage"], + "exact_100_of_100_routable_leaves" + ); + assert_eq!(matrix["extensionImplementationStatus"]["decodedLeafCount"], 58); + assert_eq!(matrix["extensionImplementationStatus"]["embeddedInterfaceDecodedLeafCount"], 9); + } + + #[test] + fn matrix_requires_bounded_token_metadata_string_decoding() { + let matrix = matrix(); + let audit = &matrix["tokenMetadataVersionDriftAudit"]; + assert_eq!(audit["processorSourceVersion"], "1.0.0"); + assert_eq!(audit["operatorWorkspaceVersion"], "1.0.1"); + assert_eq!(audit["wireDiscriminantsChanged"], false); + assert_eq!(audit["buildersChanged"], false); + assert_eq!(audit["affectedInstructions"].as_object().map(serde_json::Map::len), Some(3),); + assert!(audit["decoderRequirement"].is_string()); + } + + #[test] + fn matrix_records_exact_public_wire_for_all_non_extension_envelopes() { + let matrix = matrix(); + let audit = &matrix["baseInstructionWireAudit"]; + let rows = match audit["rows"].as_array() { + std::option::Option::Some(rows) => rows, + std::option::Option::None => panic!("Token-2022 base wire rows missing"), + }; + assert_eq!(audit["rowCount"], 33); + assert_eq!(rows.len(), 33); + assert_eq!(matrix["operatorValidation"]["cargoTestPackage"]["passed"], 8); + assert_eq!(rows[0]["tag"], 0); + assert_eq!(rows[0]["sizeRule"], "35_or_67"); + assert_eq!(rows[25]["tag"], 25); + assert_eq!(rows[25]["sizeRule"], "2_or_34"); + assert_eq!(rows[31]["tag"], 45); + assert_eq!(rows[31]["sizeRule"], "2_or_10"); + assert_eq!(rows[32]["tag"], 255); + assert_eq!(audit["publicUnpackContract"]["runtimeClaim"], "none_from_public_unpack_alone",); + assert!(rows.iter().all(|row| return row["officialBuilder"].is_string())); + } + + #[test] + fn matrix_partitions_every_base_tag_by_exact_processor_suffix_policy() { + let matrix = matrix(); + let audit = &matrix["baseInstructionProcessorSuffixAudit"]; + let partitions = match audit["partition"].as_array() { + std::option::Option::Some(partitions) => partitions, + std::option::Option::None => panic!("Token-2022 processor suffix partition missing"), + }; + assert_eq!(partitions.len(), 5); + assert_eq!(matrix["operatorValidation"]["cargoTestPackage"]["passed"], 8); + + let mut processor_tags = std::collections::BTreeSet::new(); + for partition in partitions { + let tags = match partition["tags"].as_array() { + std::option::Option::Some(tags) => tags, + std::option::Option::None => panic!("processor suffix policy tags missing"), + }; + assert_eq!(partition["count"].as_u64(), Some(tags.len() as u64)); + for tag in tags { + let tag = match tag.as_u64() { + std::option::Option::Some(tag) => tag, + std::option::Option::None => panic!("processor suffix policy tag invalid"), + }; + assert!(processor_tags.insert(tag), "duplicate processor suffix tag {tag}"); + } + } + + let base_tags = match matrix["baseInstructionWireAudit"]["rows"].as_array() { + std::option::Option::Some(rows) => rows + .iter() + .filter_map(|row| return row["tag"].as_u64()) + .collect::>(), + std::option::Option::None => panic!("Token-2022 base wire rows missing"), + }; + assert_eq!(processor_tags, base_tags); + assert_eq!(audit["partitionTagCount"], 33); + assert_eq!(partitions[0]["suffix"], "rejected"); + assert_eq!(partitions[1]["suffix"], "accepted_after_complete_none_or_some_prefix"); + assert_eq!(partitions[2]["suffix"], "accepted_without_inspection"); + assert_eq!(partitions[4]["record"]["nestedBatch"], "rejected"); + } + + #[test] + fn matrix_maps_every_base_tag_to_exact_official_builder_accounts() { + let matrix = matrix(); + let audit = &matrix["baseInstructionBuilderAccountAudit"]; + let rows = match audit["rows"].as_array() { + std::option::Option::Some(rows) => rows, + std::option::Option::None => panic!("Token-2022 builder account rows missing"), + }; + assert_eq!(audit["rowCount"], 33); + assert_eq!(rows.len(), 33); + + let builder_tags = rows + .iter() + .filter_map(|row| return row["tag"].as_u64()) + .collect::>(); + let wire_tags = match matrix["baseInstructionWireAudit"]["rows"].as_array() { + std::option::Option::Some(rows) => rows + .iter() + .filter_map(|row| return row["tag"].as_u64()) + .collect::>(), + std::option::Option::None => panic!("Token-2022 base wire rows missing"), + }; + assert_eq!(builder_tags, wire_tags); + assert_eq!(rows.iter().filter(|row| return row["builder"].is_string()).count(), 32,); + + let transfer = &rows[3]; + assert_eq!(transfer["tag"], 3); + assert_eq!(transfer["accounts"][0]["role"], "source"); + assert_eq!(transfer["accounts"][0]["writable"], true); + assert_eq!(transfer["accounts"][2]["signer"], "conditional_authority",); + assert_eq!(rows[17]["optionalAccounts"][0]["role"], "rent_sysvar"); + assert_eq!( + rows[26]["processorAccountProof"], + "pending_exact_reallocate_module_reconfirmation", + ); + assert!(rows[32]["builder"].is_null()); + assert_eq!( + audit["processorFactsAlreadyConfirmed"]["initializeMultisig"], + "all accounts after multisig and optional rent become configuration keys; runtime validates m and n independently in 1..11 but does not enforce builder rule m <= n", + ); + } + #[test] + fn transfer_fee_extension_decodes_all_six_published_subinstructions() { + let cases = [ + ( + std::vec![26, 0, 0, 0, 5, 0, 9, 0, 0, 0, 0, 0, 0, 0], + "initialize_transfer_fee_config", + ), + ( + { + let mut value = std::vec![26, 1]; + value.extend_from_slice(&7_u64.to_le_bytes()); + value.push(6); + value.extend_from_slice(&1_u64.to_le_bytes()); + value + }, + "transfer_checked_with_fee", + ), + (std::vec![26, 2], "withdraw_withheld_tokens_from_mint"), + (std::vec![26, 3, 1], "withdraw_withheld_tokens_from_accounts"), + (std::vec![26, 4], "harvest_withheld_tokens_to_mint"), + ( + { + let mut value = std::vec![26, 5]; + value.extend_from_slice(&25_u16.to_le_bytes()); + value.extend_from_slice(&99_u64.to_le_bytes()); + value + }, + "set_transfer_fee", + ), + ]; + for (wire, expected) in cases { + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + expected, + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some(expected) + ); + assert_eq!(result.observations[0].payload_json["instruction"], expected); + } + } + + #[test] + fn compliance_extensions_decode_exact_toggle_and_state_contracts() { + let cases = [ + (std::vec![28, 0, 2], "initialize_default_account_state"), + (std::vec![28, 1, 1], "update_default_account_state"), + (std::vec![30, 0], "enable_required_transfer_memos"), + (std::vec![30, 1], "disable_required_transfer_memos"), + (std::vec![34, 0], "enable_cpi_guard"), + (std::vec![34, 1], "disable_cpi_guard"), + ]; + for (wire, expected) in cases { + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + expected, + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some(expected) + ); + } + } + + #[test] + fn audited_extension_families_reject_unknown_truncated_and_suffixed_wire() { + for wire in [ + std::vec![26], + std::vec![26, 9], + std::vec![28, 0], + std::vec![28, 0, 1, 0], + std::vec![30, 0, 0], + std::vec![34, 2], + ] { + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + "invalid", + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + assert!(result.observations.is_empty()); + } + } + + #[test] + fn scaled_ui_pausable_and_permissioned_burn_decode_exact_non_confidential_leaves() { + let mut scaled_initialize = std::vec![43, 0]; + scaled_initialize.extend_from_slice(&[7_u8; 32]); + scaled_initialize.extend_from_slice(&1.5_f64.to_bits().to_le_bytes()); + let mut scaled_update = std::vec![43, 1]; + scaled_update.extend_from_slice(&2.25_f64.to_bits().to_le_bytes()); + scaled_update.extend_from_slice(&(-5_i64).to_le_bytes()); + let mut pause_initialize = std::vec![44, 0]; + pause_initialize.extend_from_slice(&[9_u8; 32]); + let mut burn_initialize = std::vec![46, 0]; + burn_initialize.extend_from_slice(&[11_u8; 32]); + let mut burn = std::vec![46, 1]; + burn.extend_from_slice(&u64::MAX.to_le_bytes()); + let mut burn_checked = std::vec![46, 2]; + burn_checked.extend_from_slice(&17_u64.to_le_bytes()); + burn_checked.push(9); + let cases = [ + (scaled_initialize, "initialize_scaled_ui_amount"), + (scaled_update, "update_scaled_ui_amount_multiplier"), + (pause_initialize, "initialize_pausable_config"), + (std::vec![44, 1], "pause_token_mint"), + (std::vec![44, 2], "resume_token_mint"), + (burn_initialize, "initialize_permissioned_burn"), + (burn, "permissioned_burn"), + (burn_checked, "permissioned_burn_checked"), + ]; + for (wire, expected) in cases { + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + expected, + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded, "{expected}"); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some(expected) + ); + } + } + + #[test] + fn scaled_ui_pausable_and_permissioned_burn_reject_invalid_exact_wire() { + for wire in [ + std::vec![43], + std::vec![43, 2], + { + let mut value = std::vec![43, 0]; + value.extend_from_slice(&[1_u8; 39]); + value + }, + std::vec![44, 3], + std::vec![44, 1, 0], + std::vec![46, 4], + std::vec![46, 1, 0], + { + let mut value = std::vec![46, 2]; + value.extend_from_slice(&[0_u8; 10]); + value.push(0); + value + }, + ] { + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + "invalid", + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + } + + #[test] + fn confidential_transfer_simple_public_leaves_decode_exactly() { + let mut initialize = std::vec![27, 0]; + initialize.extend_from_slice(&[7_u8; 32]); + initialize.push(1); + initialize.extend_from_slice(&[9_u8; 32]); + let mut update = std::vec![27, 1, 0]; + update.extend_from_slice(&[0_u8; 32]); + let mut deposit = std::vec![27, 5]; + deposit.extend_from_slice(&u64::MAX.to_le_bytes()); + deposit.push(9); + let mut apply = std::vec![27, 8]; + apply.extend_from_slice(&17_u64.to_le_bytes()); + apply.extend_from_slice(&[3_u8; 36]); + for (wire, expected) in [ + (initialize, "initialize_confidential_transfer_mint"), + (update, "update_confidential_transfer_mint"), + (std::vec![27, 3], "approve_confidential_transfer_account"), + (std::vec![27, 4, 255], "empty_confidential_transfer_account"), + (deposit, "deposit_confidential_tokens"), + (apply, "apply_confidential_pending_balance"), + (std::vec![27, 9], "enable_confidential_credits"), + (std::vec![27, 10], "disable_confidential_credits"), + (std::vec![27, 11], "enable_non_confidential_credits"), + (std::vec![27, 12], "disable_non_confidential_credits"), + ] { + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + expected, + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded, "{expected}"); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some(expected) + ); + } + } + + #[test] + fn confidential_transfer_simple_public_leaves_reject_invalid_wire() { + for wire in [ + std::vec![27], + std::vec![27, 0], + { + let mut value = std::vec![27, 0]; + value.extend_from_slice(&[0_u8; 32]); + value.push(2); + value.extend_from_slice(&[0_u8; 32]); + value + }, + std::vec![27, 3, 0], + std::vec![27, 4], + std::vec![27, 5, 0], + std::vec![27, 8, 0], + std::vec![27, 9, 0], + std::vec![27, 15], + ] { + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + "invalid", + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + } + + #[test] + fn confidential_transfer_complex_public_leaves_decode_exactly() { + let mut configure = std::vec![27, 2]; + configure.extend_from_slice(&[1_u8; 36]); + configure.extend_from_slice(&19_u64.to_le_bytes()); + configure.push(255); + let mut withdraw = std::vec![27, 6]; + withdraw.extend_from_slice(&23_u64.to_le_bytes()); + withdraw.push(9); + withdraw.extend_from_slice(&[2_u8; 36]); + withdraw.extend_from_slice(&[1_u8, 0_u8]); + let mut transfer = std::vec![27, 7]; + transfer.extend_from_slice(&[3_u8; 36]); + transfer.extend_from_slice(&[4_u8; 64]); + transfer.extend_from_slice(&[5_u8; 64]); + transfer.extend_from_slice(&[1_u8, 2_u8, 0_u8]); + let mut transfer_with_fee = std::vec![27, 13]; + transfer_with_fee.extend_from_slice(&[6_u8; 36]); + transfer_with_fee.extend_from_slice(&[7_u8; 64]); + transfer_with_fee.extend_from_slice(&[8_u8; 64]); + transfer_with_fee.extend_from_slice(&[1_u8, 2_u8, 3_u8, 4_u8, 0_u8]); + for (wire, expected) in [ + (configure, "configure_confidential_transfer_account"), + (withdraw, "withdraw_confidential_tokens"), + (transfer, "transfer_confidential_tokens"), + (transfer_with_fee, "transfer_confidential_tokens_with_fee"), + (std::vec![27, 14], "configure_confidential_transfer_account_with_registry"), + ] { + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + expected, + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded, "{expected}"); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some(expected) + ); + } + } + + #[test] + fn confidential_transfer_complex_public_leaves_reject_invalid_wire() { + for wire in [ + std::vec![27, 2], + std::vec![27, 6, 0], + { + let mut value = std::vec![27, 7]; + value.extend_from_slice(&[0_u8; 168]); + value + }, + { + let mut value = std::vec![27, 13]; + value.extend_from_slice(&[0_u8; 168]); + value + }, + std::vec![27, 14, 0], + ] { + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + "invalid", + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + } + + #[test] + fn confidential_transfer_fee_leaves_decode_exactly() { + let mut initialize = std::vec![37, 0]; + initialize.extend_from_slice(&[7_u8; 32]); + initialize.extend_from_slice(&[8_u8; 32]); + let mut withdraw_from_mint = std::vec![37, 1, 255]; + withdraw_from_mint.extend_from_slice(&[3_u8; 36]); + let mut withdraw_from_accounts = std::vec![37, 2, 11, 0]; + withdraw_from_accounts.extend_from_slice(&[4_u8; 36]); + for (wire, expected) in [ + (initialize, "initialize_confidential_transfer_fee_config"), + (withdraw_from_mint, "withdraw_confidential_withheld_tokens_from_mint"), + (withdraw_from_accounts, "withdraw_confidential_withheld_tokens_from_accounts"), + (std::vec![37, 3], "harvest_confidential_withheld_tokens_to_mint"), + (std::vec![37, 4], "enable_confidential_harvest_to_mint"), + (std::vec![37, 5], "disable_confidential_harvest_to_mint"), + ] { + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + expected, + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded, "{expected}"); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some(expected) + ); + assert_eq!(result.observations.len(), 1); + assert_eq!(result.observations[0].event.event_family, kb_model::EventFamily::Fee); + } + } + + #[test] + fn confidential_transfer_fee_leaves_reject_invalid_wire() { + for wire in [ + std::vec![37], + std::vec![37, 0], + std::vec![37, 1, 0], + std::vec![37, 2, 1, 0], + std::vec![37, 3, 0], + std::vec![37, 6], + ] { + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + "invalid", + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + } + + #[test] + fn confidential_mint_burn_and_permissioned_confidential_burn_decode_exactly() { + let mut initialize = std::vec![42, 0]; + initialize.extend_from_slice(&[1_u8; 32]); + initialize.extend_from_slice(&[2_u8; 36]); + let mut rotate = std::vec![42, 1]; + rotate.extend_from_slice(&[3_u8; 32]); + rotate.push(255); + let mut update = std::vec![42, 2]; + update.extend_from_slice(&[4_u8; 36]); + let mut mint = std::vec![42, 3]; + mint.extend_from_slice(&[5_u8; 36]); + mint.extend_from_slice(&[6_u8; 64]); + mint.extend_from_slice(&[7_u8; 64]); + mint.extend_from_slice(&[1_u8, 2_u8, 3_u8]); + let mut burn = mint.clone(); + burn[1] = 4; + let mut permissioned = mint.clone(); + permissioned[0] = 46; + permissioned[1] = 3; + for (wire, expected) in [ + (initialize, "initialize_confidential_mint_burn"), + (rotate, "rotate_confidential_supply_elgamal_pubkey"), + (update, "update_confidential_decryptable_supply"), + (mint, "confidential_mint"), + (burn, "confidential_burn"), + (std::vec![42, 5], "apply_pending_confidential_burn"), + (permissioned, "permissioned_confidential_burn"), + ] { + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + expected, + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Decoded, "{expected}"); + assert_eq!( + result.recognized_entry_code.as_deref(), + std::option::Option::Some(expected) + ); + } + } + + #[test] + fn confidential_mint_burn_and_permissioned_confidential_burn_reject_invalid_wire() { + for wire in [ + std::vec![42], + std::vec![42, 0], + std::vec![42, 1, 0], + std::vec![42, 2, 0], + std::vec![42, 3, 0], + std::vec![42, 5, 0], + std::vec![42, 6], + std::vec![46, 3, 0], + ] { + let input = input( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + wire.as_slice(), + serde_json::json!([]), + serde_json::json!([]), + false, + "invalid", + ); + let result = + kb_decoder_api::InstructionDecoder::decode(&crate::SplToken2022Decoder, &input); + assert_eq!(result.status, kb_decoder_api::DecoderOutcomeStatus::Failed); + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/lib.rs new file mode 100644 index 0000000..40d6fbc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/lib.rs @@ -0,0 +1,48 @@ +// file: kb_decoder_spl_token_2022/src/lib.rs +// version: 6 + +//! Decoder crate for `spl_token_2022`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; +pub mod state; +mod token; + +/// Maximum prefix retained in diagnostics. +pub(crate) use crate::constants::DIAGNOSTIC_PREFIX_BYTES; +/// Exact top-level instruction inventory published by the resolved Token-2022 interface. +pub(crate) use crate::constants::INSTRUCTION_ENTRIES; +/// Maximum total account metas consumed by one batch. +pub(crate) use crate::constants::MAX_BATCH_ACCOUNTS; +/// Maximum decoded sub-instructions in one batch. +pub(crate) use crate::constants::MAX_BATCH_INSTRUCTIONS; +/// Maximum retained instruction payload size. +pub(crate) use crate::constants::MAX_INSTRUCTION_BYTES; +/// Maximum retained UTF-8 string size for embedded Token Metadata instructions. +pub(crate) use crate::constants::MAX_METADATA_STRING_BYTES; +/// Stable Token-2022 surface code. +pub(crate) use crate::constants::SURFACE_CODE; +/// Current decoded event contract version. +pub(crate) use crate::constants::TOKEN_EVENT_VERSION; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; +/// Crate-root access to `decode` from `token`. +pub(crate) use crate::token::decode; +/// Crate-root access to `entry_for_tag` from `token`. +pub(crate) use crate::token::entry_for_tag; +/// Crate-root access to `payload_tag` from `token`. +pub(crate) use crate::token::payload_tag; + +/// Initial decoder implementation placeholder. +pub use crate::decoder::SplToken2022Decoder; +/// Crate-root access to `Token2022State` from `state`. +pub use crate::state::Token2022State; +/// Crate-root access to `Token2022StateKind` from `state`. +pub use crate::state::Token2022StateKind; +/// Crate-root access to `Token2022TlvEntry` from `state`. +pub use crate::state::Token2022TlvEntry; +/// Parse one Token-2022 Mint, Account, or Multisig state without interpreting extension payloads. +pub use crate::state::parse_token_2022_state; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/state.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/state.rs new file mode 100644 index 0000000..e7090fb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/state.rs @@ -0,0 +1,1647 @@ +// file: kb_decoder_spl_token_2022/src/state.rs +// version: 10 + +//! Strict Token-2022 base-state and TLV parsing. + +use base64::Engine as _; // rust-rules: trait-import + +const MINT_BASE_LEN: usize = 82; +const ACCOUNT_BASE_LEN: usize = 165; +const MULTISIG_BASE_LEN: usize = 355; +const ACCOUNT_TYPE_OFFSET: usize = 165; +const TLV_START_OFFSET: usize = 166; +const MAX_ACCOUNT_DATA_LEN: usize = 1_048_576; +const MAX_TLV_ENTRIES: usize = 64; +const MAX_METADATA_STRING_LEN: usize = 1_024; +const MAX_METADATA_PAIRS: usize = 64; + +/// Token-2022 account state category selected by the caller. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Token2022StateKind { + /// Mint base state, optionally followed by Token-2022 TLV extensions. + Mint, + /// Token account base state, optionally followed by Token-2022 TLV extensions. + Account, + /// Multisignature base state. Token-2022 does not append TLV extensions. + Multisig, +} + +/// One bounded Token-2022 TLV extension entry. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Token2022TlvEntry { + /// Numeric `ExtensionType` discriminant retained exactly from the wire. + pub extension_type: u16, + /// Stable name for published types, or `unknown` for a future type. + pub extension_name: &'static str, + /// Exact extension bytes encoded as lowercase hexadecimal. + pub value_hex: String, + /// Structured public fields for audited fixed-size layouts, or `null` when still opaque. + pub value_fields: serde_json::Value, +} + +/// Strictly parsed Token-2022 account state. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Token2022State { + /// Caller-selected base state category. + pub kind: Token2022StateKind, + /// Structured fields decoded from the exact base layout. + pub base_fields: serde_json::Value, + /// Base layout bytes encoded as lowercase hexadecimal. + pub base_hex: String, + /// Account-type discriminator for extended Mint or Account states. + pub account_type: Option, + /// Ordered TLV entries. + pub extensions: Vec, +} + +/// Parse one Token-2022 Mint, Account, or Multisig state without interpreting extension payloads. +pub fn parse_token_2022_state( + kind: Token2022StateKind, + data: &[u8], +) -> Result { + if data.len() > MAX_ACCOUNT_DATA_LEN { + return Err(format!( + "Token-2022 account data length {} exceeds bound {}", + data.len(), + MAX_ACCOUNT_DATA_LEN + )); + } + return match kind { + Token2022StateKind::Mint => parse_mint(data), + Token2022StateKind::Account => parse_account(data), + Token2022StateKind::Multisig => parse_multisig(data), + }; +} + +fn parse_mint(data: &[u8]) -> Result { + if data.len() == MINT_BASE_LEN { + return Ok(Token2022State { + kind: Token2022StateKind::Mint, + base_fields: match parse_mint_base(data) { + Ok(value) => value, + Err(error) => return Err(error), + }, + base_hex: lower_hex(data), + account_type: None, + extensions: Vec::new(), + }); + } + if data.len() < TLV_START_OFFSET { + return Err(format!( + "extended Token-2022 mint requires at least {TLV_START_OFFSET} bytes, got {}", + data.len() + )); + } + if data[MINT_BASE_LEN..ACCOUNT_TYPE_OFFSET].iter().any(|byte| return *byte != 0) { + return Err("extended Token-2022 mint padding must be zero".to_string()); + } + if data[ACCOUNT_TYPE_OFFSET] != 1 { + return Err(format!( + "extended Token-2022 mint account type must be 1, got {}", + data[ACCOUNT_TYPE_OFFSET] + )); + } + let extensions = match parse_tlv(&data[TLV_START_OFFSET..], 1) { + Ok(extensions) => extensions, + Err(error) => return Err(error), + }; + return Ok(Token2022State { + kind: Token2022StateKind::Mint, + base_fields: match parse_mint_base(&data[..MINT_BASE_LEN]) { + Ok(value) => value, + Err(error) => return Err(error), + }, + base_hex: lower_hex(&data[..MINT_BASE_LEN]), + account_type: Some(1), + extensions, + }); +} + +fn parse_account(data: &[u8]) -> Result { + if data.len() == ACCOUNT_BASE_LEN { + return Ok(Token2022State { + kind: Token2022StateKind::Account, + base_fields: match parse_account_base(data) { + Ok(value) => value, + Err(error) => return Err(error), + }, + base_hex: lower_hex(data), + account_type: None, + extensions: Vec::new(), + }); + } + if data.len() < TLV_START_OFFSET { + return Err(format!( + "extended Token-2022 account requires at least {TLV_START_OFFSET} bytes, got {}", + data.len() + )); + } + if data[ACCOUNT_TYPE_OFFSET] != 2 { + return Err(format!( + "extended Token-2022 token account type must be 2, got {}", + data[ACCOUNT_TYPE_OFFSET] + )); + } + let extensions = match parse_tlv(&data[TLV_START_OFFSET..], 2) { + Ok(extensions) => extensions, + Err(error) => return Err(error), + }; + return Ok(Token2022State { + kind: Token2022StateKind::Account, + base_fields: match parse_account_base(&data[..ACCOUNT_BASE_LEN]) { + Ok(value) => value, + Err(error) => return Err(error), + }, + base_hex: lower_hex(&data[..ACCOUNT_BASE_LEN]), + account_type: Some(2), + extensions, + }); +} + +fn parse_multisig(data: &[u8]) -> Result { + if data.len() != MULTISIG_BASE_LEN { + return Err(format!( + "Token-2022 multisig requires exactly {MULTISIG_BASE_LEN} bytes, got {}", + data.len() + )); + } + return Ok(Token2022State { + kind: Token2022StateKind::Multisig, + base_fields: match parse_multisig_base(data) { + Ok(value) => value, + Err(error) => return Err(error), + }, + base_hex: lower_hex(data), + account_type: None, + extensions: Vec::new(), + }); +} + +fn parse_mint_base(data: &[u8]) -> Result { + let mint_authority = match parse_coption_pubkey(&data[0..36], "mint authority") { + Ok(value) => value, + Err(error) => return Err(error), + }; + let freeze_authority = match parse_coption_pubkey(&data[46..82], "freeze authority") { + Ok(value) => value, + Err(error) => return Err(error), + }; + let initialized = match data[45] { + 0 => false, + 1 => true, + value => { + return Err(format!("Token-2022 mint initialized flag must be 0 or 1, got {value}")); + }, + }; + return Ok(serde_json::json!({ + "mintAuthority": mint_authority, + "supply": read_u64(&data[36..44]).to_string(), + "decimals": data[44], + "initialized": initialized, + "freezeAuthority": freeze_authority, + })); +} + +fn parse_account_base(data: &[u8]) -> Result { + let delegate = match parse_coption_pubkey(&data[72..108], "delegate") { + Ok(value) => value, + Err(error) => return Err(error), + }; + let native_reserve = match parse_coption_u64(&data[109..121], "native reserve") { + Ok(value) => value, + Err(error) => return Err(error), + }; + let close_authority = match parse_coption_pubkey(&data[129..165], "close authority") { + Ok(value) => value, + Err(error) => return Err(error), + }; + let state = match data[108] { + 0 => "uninitialized", + 1 => "initialized", + 2 => "frozen", + value => return Err(format!("Token-2022 account state must be 0, 1, or 2, got {value}")), + }; + return Ok(serde_json::json!({ + "mint": bs58::encode(&data[0..32]).into_string(), + "owner": bs58::encode(&data[32..64]).into_string(), + "amount": read_u64(&data[64..72]).to_string(), + "delegate": delegate, + "state": state, + "nativeReserve": native_reserve, + "delegatedAmount": read_u64(&data[121..129]).to_string(), + "closeAuthority": close_authority, + })); +} + +fn parse_multisig_base(data: &[u8]) -> Result { + let initialized = match data[2] { + 0 => false, + 1 => true, + value => { + return Err(format!( + "Token-2022 multisig initialized flag must be 0 or 1, got {value}" + )); + }, + }; + let m = data[0]; + let n = data[1]; + if initialized && (m == 0 || n == 0 || m > n || n > 11) { + return Err(format!("Token-2022 multisig threshold is invalid: m={m}, n={n}")); + } + let mut signers = Vec::with_capacity(11); + for index in 0..11usize { + let start = 3 + index * 32; + signers + .push(serde_json::Value::String(bs58::encode(&data[start..start + 32]).into_string())); + } + return Ok(serde_json::json!({ + "m": m, + "n": n, + "initialized": initialized, + "signers": signers, + })); +} + +fn parse_coption_pubkey(data: &[u8], field: &str) -> Result { + let tag = read_u32(&data[0..4]); + return match tag { + 0 => Ok(serde_json::Value::Null), + 1 => Ok(serde_json::Value::String(bs58::encode(&data[4..36]).into_string())), + value => Err(format!("Token-2022 {field} option tag must be 0 or 1, got {value}")), + }; +} + +fn parse_coption_u64(data: &[u8], field: &str) -> Result { + let tag = read_u32(&data[0..4]); + return match tag { + 0 => Ok(serde_json::Value::Null), + 1 => Ok(serde_json::Value::String(read_u64(&data[4..12]).to_string())), + value => Err(format!("Token-2022 {field} option tag must be 0 or 1, got {value}")), + }; +} + +fn read_u32(data: &[u8]) -> u32 { + return u32::from_le_bytes([data[0], data[1], data[2], data[3]]); +} + +fn read_u64(data: &[u8]) -> u64 { + return u64::from_le_bytes([ + data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], + ]); +} + +fn read_i16(data: &[u8]) -> i16 { + return i16::from_le_bytes([data[0], data[1]]); +} + +fn read_i64(data: &[u8]) -> i64 { + return i64::from_le_bytes([ + data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7], + ]); +} + +fn parse_tlv(data: &[u8], kind_from_account_type: u8) -> Result, String> { + let mut entries = Vec::new(); + let mut offset = 0usize; + let mut seen = std::collections::BTreeSet::new(); + while offset < data.len() { + if data[offset..].iter().all(|byte| return *byte == 0) { + return Ok(entries); + } + if entries.len() >= MAX_TLV_ENTRIES { + return Err(format!("Token-2022 TLV entry count exceeds bound {MAX_TLV_ENTRIES}")); + } + if data.len() - offset < 4 { + return Err(format!("Token-2022 TLV header is truncated at offset {offset}")); + } + let extension_type = u16::from_le_bytes([data[offset], data[offset + 1]]); + let value_len = usize::from(u16::from_le_bytes([data[offset + 2], data[offset + 3]])); + offset += 4; + if extension_type == 0 { + return Err("Token-2022 TLV type 0 is only valid as zero padding".to_string()); + } + let value_end = match offset.checked_add(value_len) { + Some(value_end) => value_end, + None => return Err("Token-2022 TLV length overflow".to_string()), + }; + if value_end > data.len() { + return Err(format!("Token-2022 TLV value for type {extension_type} is truncated")); + } + if !seen.insert(extension_type) { + return Err(format!("Token-2022 TLV extension type {extension_type} is duplicated")); + } + let value = &data[offset..value_end]; + if !extension_kind_is_compatible(extension_type, kind_from_account_type) { + return Err(format!( + "Token-2022 TLV extension type {extension_type} is incompatible with account type {kind_from_account_type}" + )); + } + let value_fields = match decode_fixed_extension(extension_type, value) { + Ok(value_fields) => value_fields, + Err(error) => return Err(error), + }; + entries.push(Token2022TlvEntry { + extension_type, + extension_name: extension_name(extension_type), + value_hex: lower_hex(value), + value_fields, + }); + offset = value_end; + } + return Ok(entries); +} + +fn extension_kind_is_compatible(extension_type: u16, account_type: u8) -> bool { + return match extension_type { + 1 | 3 | 4 | 6 | 9 | 10 | 12 | 14 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 => { + account_type == 1 + }, + 2 | 5 | 7 | 8 | 11 | 13 | 15 | 17 | 27 => account_type == 2, + _ => true, + }; +} + +fn decode_fixed_extension(extension_type: u16, value: &[u8]) -> Result { + return match extension_type { + 1 => decode_transfer_fee_config(value), + 2 => decode_transfer_fee_amount(value), + 3 => decode_optional_pubkey_extension("mint close authority", value), + 4 => decode_confidential_transfer_mint(value), + 5 => decode_confidential_transfer_account(value), + 6 => decode_account_state_extension(value), + 7 => decode_empty_extension("immutable owner", value), + 8 => decode_bool_extension("memo transfer", value), + 9 => decode_empty_extension("non-transferable mint", value), + 10 => decode_interest_bearing_config(value), + 11 => decode_bool_extension("CPI guard", value), + 12 => decode_optional_pubkey_extension("permanent delegate", value), + 13 => decode_empty_extension("non-transferable account", value), + 14 => decode_authority_address_extension("transfer hook", value), + 15 => decode_bool_extension("transfer hook account", value), + 16 => decode_confidential_transfer_fee_config(value), + 17 => decode_confidential_transfer_fee_amount(value), + 18 => decode_authority_address_extension("metadata pointer", value), + 19 => decode_token_metadata(value), + 20 => decode_authority_address_extension("group pointer", value), + 21 => decode_token_group(value), + 22 => decode_authority_address_extension("group member pointer", value), + 23 => decode_token_group_member(value), + 24 => decode_confidential_mint_burn(value), + 25 => decode_scaled_ui_amount_config(value), + 26 => decode_pausable_config(value), + 27 => decode_empty_extension("pausable account", value), + 28 => decode_required_pubkey_extension("permissioned burn authority", value), + _ => Ok(serde_json::Value::Null), + }; +} + +fn decode_token_group(value: &[u8]) -> Result { + if value.len() != 80 { + return Err(format!( + "Token-2022 token group extension requires exactly 80 bytes, got {}", + value.len() + )); + } + let size = read_u64(&value[64..72]); + let max_size = read_u64(&value[72..80]); + if size > max_size { + return Err(format!("Token-2022 token group size {size} exceeds max size {max_size}")); + } + return Ok(serde_json::json!({ + "updateAuthority": optional_nonzero_pubkey(&value[0..32]), + "mint": bs58::encode(&value[32..64]).into_string(), + "size": size.to_string(), + "maxSize": max_size.to_string(), + })); +} + +fn decode_token_group_member(value: &[u8]) -> Result { + if value.len() != 72 { + return Err(format!( + "Token-2022 token group member extension requires exactly 72 bytes, got {}", + value.len() + )); + } + return Ok(serde_json::json!({ + "mint": bs58::encode(&value[0..32]).into_string(), + "group": bs58::encode(&value[32..64]).into_string(), + "memberNumber": read_u64(&value[64..72]).to_string(), + })); +} + +fn decode_token_metadata(value: &[u8]) -> Result { + if value.len() < 76 { + return Err(format!( + "Token-2022 token metadata extension requires at least 76 bytes, got {}", + value.len() + )); + } + let mut offset = 64usize; + let name = match decode_borsh_string("token metadata name", value, &mut offset) { + Ok(value) => value, + Err(error) => return Err(error), + }; + let symbol = match decode_borsh_string("token metadata symbol", value, &mut offset) { + Ok(value) => value, + Err(error) => return Err(error), + }; + let uri = match decode_borsh_string("token metadata URI", value, &mut offset) { + Ok(value) => value, + Err(error) => return Err(error), + }; + let pair_count = + match read_bounded_u32("token metadata additional metadata count", value, &mut offset) { + Ok(value) => value, + Err(error) => return Err(error), + }; + if pair_count > MAX_METADATA_PAIRS { + return Err(format!( + "Token-2022 token metadata pair count {pair_count} exceeds bound {MAX_METADATA_PAIRS}" + )); + } + let mut additional_metadata = Vec::with_capacity(pair_count); + let mut keys = std::collections::BTreeSet::new(); + for _ in 0..pair_count { + let key = match decode_borsh_string("token metadata additional key", value, &mut offset) { + Ok(value) => value, + Err(error) => return Err(error), + }; + if !keys.insert(key.clone()) { + return Err(format!("Token-2022 token metadata key is duplicated: {key}")); + } + let field_value = + match decode_borsh_string("token metadata additional value", value, &mut offset) { + Ok(value) => value, + Err(error) => return Err(error), + }; + additional_metadata.push(serde_json::json!({"key": key, "value": field_value})); + } + if offset != value.len() { + return Err(format!( + "Token-2022 token metadata extension has {} trailing bytes", + value.len() - offset + )); + } + return Ok(serde_json::json!({ + "updateAuthority": optional_nonzero_pubkey(&value[0..32]), + "mint": bs58::encode(&value[32..64]).into_string(), + "name": name, + "symbol": symbol, + "uri": uri, + "additionalMetadata": additional_metadata, + })); +} + +fn read_bounded_u32(name: &str, value: &[u8], offset: &mut usize) -> Result { + let end = match offset.checked_add(4) { + Some(end) => end, + None => return Err(format!("Token-2022 {name} offset overflow")), + }; + if end > value.len() { + return Err(format!("Token-2022 {name} is truncated")); + } + let decoded = usize::try_from(read_u32(&value[*offset..end])); + *offset = end; + return match decoded { + Ok(decoded) => Ok(decoded), + Err(_) => Err(format!("Token-2022 {name} does not fit usize")), + }; +} + +fn decode_borsh_string(name: &str, value: &[u8], offset: &mut usize) -> Result { + let length = match read_bounded_u32(name, value, offset) { + Ok(length) => length, + Err(error) => return Err(error), + }; + if length > MAX_METADATA_STRING_LEN { + return Err(format!( + "Token-2022 {name} length {length} exceeds bound {MAX_METADATA_STRING_LEN}" + )); + } + let end = match offset.checked_add(length) { + Some(end) => end, + None => return Err(format!("Token-2022 {name} length overflow")), + }; + if end > value.len() { + return Err(format!("Token-2022 {name} bytes are truncated")); + } + let decoded = match std::str::from_utf8(&value[*offset..end]) { + Ok(decoded) => decoded.to_string(), + Err(_) => return Err(format!("Token-2022 {name} is not valid UTF-8")), + }; + *offset = end; + return Ok(decoded); +} + +fn decode_confidential_transfer_fee_config(value: &[u8]) -> Result { + if value.len() != 129 { + return Err(format!( + "Token-2022 confidential transfer fee config extension requires exactly 129 bytes, got {}", + value.len() + )); + } + let harvest_to_mint_enabled = + match decode_pod_bool("confidential transfer fee harvest-to-mint flag", value[64]) { + Ok(value) => value, + Err(error) => return Err(error), + }; + return Ok(serde_json::json!({ + "authority": optional_nonzero_pubkey(&value[0..32]), + "withdrawWithheldAuthorityElgamalPubkey": encode_base64(&value[32..64]), + "harvestToMintEnabled": harvest_to_mint_enabled, + "withheldAmount": encode_base64(&value[65..129]), + })); +} + +fn decode_confidential_transfer_fee_amount(value: &[u8]) -> Result { + if value.len() != 64 { + return Err(format!( + "Token-2022 confidential transfer fee amount extension requires exactly 64 bytes, got {}", + value.len() + )); + } + return Ok(serde_json::json!({ + "withheldAmount": encode_base64(value), + })); +} + +fn decode_confidential_mint_burn(value: &[u8]) -> Result { + if value.len() != 196 { + return Err(format!( + "Token-2022 confidential mint-burn extension requires exactly 196 bytes, got {}", + value.len() + )); + } + return Ok(serde_json::json!({ + "confidentialSupply": encode_base64(&value[0..64]), + "decryptableSupply": encode_base64(&value[64..100]), + "supplyElgamalPubkey": encode_base64(&value[100..132]), + "pendingBurn": encode_base64(&value[132..196]), + })); +} + +fn decode_interest_bearing_config(value: &[u8]) -> Result { + if value.len() != 52 { + return Err(format!( + "Token-2022 interest-bearing config extension requires exactly 52 bytes, got {}", + value.len() + )); + } + return Ok(serde_json::json!({ + "rateAuthority": optional_nonzero_pubkey(&value[0..32]), + "initializationTimestamp": read_i64(&value[32..40]).to_string(), + "preUpdateAverageRateBasisPoints": read_i16(&value[40..42]), + "lastUpdateTimestamp": read_i64(&value[42..50]).to_string(), + "currentRateBasisPoints": read_i16(&value[50..52]), + })); +} + +fn decode_scaled_ui_amount_config(value: &[u8]) -> Result { + if value.len() != 56 { + return Err(format!( + "Token-2022 scaled UI amount extension requires exactly 56 bytes, got {}", + value.len() + )); + } + let multiplier = f64::from_le_bytes(match value[32..40].try_into() { + Ok(bytes) => bytes, + Err(_) => return Err("Token-2022 scaled UI multiplier has invalid width".to_string()), + }); + let new_multiplier = f64::from_le_bytes(match value[48..56].try_into() { + Ok(bytes) => bytes, + Err(_) => return Err("Token-2022 scaled UI new multiplier has invalid width".to_string()), + }); + if !multiplier.is_finite() || multiplier <= 0.0 { + return Err(format!( + "Token-2022 scaled UI multiplier must be finite and positive, got {multiplier}" + )); + } + if !new_multiplier.is_finite() || new_multiplier <= 0.0 { + return Err(format!( + "Token-2022 scaled UI new multiplier must be finite and positive, got {new_multiplier}" + )); + } + return Ok(serde_json::json!({ + "authority": optional_nonzero_pubkey(&value[0..32]), + "multiplier": multiplier.to_string(), + "newMultiplierEffectiveTimestamp": read_i64(&value[40..48]).to_string(), + "newMultiplier": new_multiplier.to_string(), + })); +} + +fn decode_pausable_config(value: &[u8]) -> Result { + if value.len() != 33 { + return Err(format!( + "Token-2022 pausable config extension requires exactly 33 bytes, got {}", + value.len() + )); + } + let paused = match decode_pod_bool("pausable config paused flag", value[32]) { + Ok(value) => value, + Err(error) => return Err(error), + }; + return Ok(serde_json::json!({ + "authority": optional_nonzero_pubkey(&value[0..32]), + "paused": paused, + })); +} + +fn decode_confidential_transfer_mint(value: &[u8]) -> Result { + if value.len() != 65 { + return Err(format!( + "Token-2022 confidential transfer mint extension requires exactly 65 bytes, got {}", + value.len() + )); + } + let auto_approve_new_accounts = match value[32] { + 0 => false, + 1 => true, + invalid => { + return Err(format!( + "Token-2022 confidential transfer mint auto-approve flag must be 0 or 1, got {invalid}" + )); + }, + }; + return Ok(serde_json::json!({ + "authority": optional_nonzero_pubkey(&value[0..32]), + "autoApproveNewAccounts": auto_approve_new_accounts, + "auditorElgamalPubkey": optional_nonzero_base64(&value[33..65]), + })); +} + +fn decode_confidential_transfer_account(value: &[u8]) -> Result { + if value.len() != 295 { + return Err(format!( + "Token-2022 confidential transfer account extension requires exactly 295 bytes, got {}", + value.len() + )); + } + let approved = match decode_pod_bool("confidential transfer account approved", value[0]) { + Ok(value) => value, + Err(error) => return Err(error), + }; + let allow_confidential_credits = match decode_pod_bool( + "confidential transfer account allow confidential credits", + value[261], + ) { + Ok(value) => value, + Err(error) => return Err(error), + }; + let allow_non_confidential_credits = match decode_pod_bool( + "confidential transfer account allow non-confidential credits", + value[262], + ) { + Ok(value) => value, + Err(error) => return Err(error), + }; + return Ok(serde_json::json!({ + "approved": approved, + "elgamalPubkey": encode_base64(&value[1..33]), + "pendingBalanceLo": encode_base64(&value[33..97]), + "pendingBalanceHi": encode_base64(&value[97..161]), + "availableBalance": encode_base64(&value[161..225]), + "decryptableAvailableBalance": encode_base64(&value[225..261]), + "allowConfidentialCredits": allow_confidential_credits, + "allowNonConfidentialCredits": allow_non_confidential_credits, + "pendingBalanceCreditCounter": read_u64(&value[263..271]).to_string(), + "maximumPendingBalanceCreditCounter": read_u64(&value[271..279]).to_string(), + "expectedPendingBalanceCreditCounter": read_u64(&value[279..287]).to_string(), + "actualPendingBalanceCreditCounter": read_u64(&value[287..295]).to_string(), + })); +} + +fn decode_pod_bool(name: &str, value: u8) -> Result { + return match value { + 0 => Ok(false), + 1 => Ok(true), + invalid => Err(format!("Token-2022 {name} must be 0 or 1, got {invalid}")), + }; +} + +fn encode_base64(value: &[u8]) -> String { + return base64::engine::general_purpose::STANDARD.encode(value); +} + +fn optional_nonzero_base64(value: &[u8]) -> serde_json::Value { + if value.iter().all(|byte| return *byte == 0) { + return serde_json::Value::Null; + } + return serde_json::Value::String(encode_base64(value)); +} + +fn decode_transfer_fee_config(value: &[u8]) -> Result { + if value.len() != 108 { + return Err(format!( + "Token-2022 transfer fee config extension requires exactly 108 bytes, got {}", + value.len() + )); + } + let older_transfer_fee = match decode_transfer_fee("older transfer fee", &value[72..90]) { + Ok(value) => value, + Err(error) => return Err(error), + }; + let newer_transfer_fee = match decode_transfer_fee("newer transfer fee", &value[90..108]) { + Ok(value) => value, + Err(error) => return Err(error), + }; + return Ok(serde_json::json!({ + "transferFeeConfigAuthority": optional_nonzero_pubkey(&value[0..32]), + "withdrawWithheldAuthority": optional_nonzero_pubkey(&value[32..64]), + "withheldAmount": read_u64(&value[64..72]).to_string(), + "olderTransferFee": older_transfer_fee, + "newerTransferFee": newer_transfer_fee, + })); +} + +fn decode_transfer_fee(name: &str, value: &[u8]) -> Result { + if value.len() != 18 { + return Err(format!("Token-2022 {name} requires exactly 18 bytes, got {}", value.len())); + } + let basis_points = u16::from_le_bytes([value[16], value[17]]); + if basis_points > 10_000 { + return Err(format!("Token-2022 {name} basis points exceed 10000: {basis_points}")); + } + return Ok(serde_json::json!({ + "epoch": read_u64(&value[0..8]).to_string(), + "maximumFee": read_u64(&value[8..16]).to_string(), + "transferFeeBasisPoints": basis_points, + })); +} + +fn decode_transfer_fee_amount(value: &[u8]) -> Result { + if value.len() != 8 { + return Err(format!( + "Token-2022 transfer fee amount extension requires exactly 8 bytes, got {}", + value.len() + )); + } + return Ok(serde_json::json!({ + "withheldAmount": read_u64(value).to_string(), + })); +} + +fn decode_empty_extension(name: &str, value: &[u8]) -> Result { + if !value.is_empty() { + return Err(format!( + "Token-2022 {name} extension requires zero bytes, got {}", + value.len() + )); + } + return Ok(serde_json::json!({})); +} + +fn decode_bool_extension(name: &str, value: &[u8]) -> Result { + if value.len() != 1 { + return Err(format!( + "Token-2022 {name} extension requires exactly 1 byte, got {}", + value.len() + )); + } + let enabled = match value[0] { + 0 => false, + 1 => true, + invalid => return Err(format!("Token-2022 {name} bool must be 0 or 1, got {invalid}")), + }; + return Ok(serde_json::json!({"enabled": enabled})); +} + +fn decode_account_state_extension(value: &[u8]) -> Result { + if value.len() != 1 { + return Err(format!( + "Token-2022 default account state requires exactly 1 byte, got {}", + value.len() + )); + } + let state = match value[0] { + 0 => "uninitialized", + 1 => "initialized", + 2 => "frozen", + invalid => { + return Err(format!( + "Token-2022 default account state must be 0, 1, or 2, got {invalid}" + )); + }, + }; + return Ok(serde_json::json!({"state": state})); +} + +fn decode_optional_pubkey_extension(name: &str, value: &[u8]) -> Result { + if value.len() != 32 { + return Err(format!( + "Token-2022 {name} extension requires exactly 32 bytes, got {}", + value.len() + )); + } + let address = optional_nonzero_pubkey(value); + return Ok(serde_json::json!({"address": address})); +} + +fn decode_required_pubkey_extension(name: &str, value: &[u8]) -> Result { + if value.len() != 32 { + return Err(format!( + "Token-2022 {name} extension requires exactly 32 bytes, got {}", + value.len() + )); + } + return Ok(serde_json::json!({"address": bs58::encode(value).into_string()})); +} + +fn decode_authority_address_extension( + name: &str, + value: &[u8], +) -> Result { + if value.len() != 64 { + return Err(format!( + "Token-2022 {name} extension requires exactly 64 bytes, got {}", + value.len() + )); + } + return Ok(serde_json::json!({ + "authority": optional_nonzero_pubkey(&value[0..32]), + "address": optional_nonzero_pubkey(&value[32..64]), + })); +} + +fn optional_nonzero_pubkey(value: &[u8]) -> serde_json::Value { + if value.iter().all(|byte| return *byte == 0) { + return serde_json::Value::Null; + } + return serde_json::Value::String(bs58::encode(value).into_string()); +} + +fn extension_name(extension_type: u16) -> &'static str { + return match extension_type { + 1 => "transfer_fee_config", + 2 => "transfer_fee_amount", + 3 => "mint_close_authority", + 4 => "confidential_transfer_mint", + 5 => "confidential_transfer_account", + 6 => "default_account_state", + 7 => "immutable_owner", + 8 => "memo_transfer", + 9 => "non_transferable", + 10 => "interest_bearing_config", + 11 => "cpi_guard", + 12 => "permanent_delegate", + 13 => "non_transferable_account", + 14 => "transfer_hook", + 15 => "transfer_hook_account", + 16 => "confidential_transfer_fee_config", + 17 => "confidential_transfer_fee_amount", + 18 => "metadata_pointer", + 19 => "token_metadata", + 20 => "group_pointer", + 21 => "token_group", + 22 => "group_member_pointer", + 23 => "token_group_member", + 24 => "confidential_mint_burn", + 25 => "scaled_ui_amount", + 26 => "pausable", + 27 => "pausable_account", + 28 => "permissioned_burn", + _ => "unknown", + }; +} + +fn lower_hex(bytes: &[u8]) -> String { + let mut output = String::with_capacity(bytes.len() * 2); + for byte in bytes { + output.push_str(&format!("{byte:02x}")); + } + return output; +} + +#[cfg(test)] +mod tests { + + #[test] + fn exact_base_mint_account_and_multisig_layouts_are_preserved() { + let mint = super::parse_token_2022_state(super::Token2022StateKind::Mint, &[0; 82]); + let account = super::parse_token_2022_state(super::Token2022StateKind::Account, &[0; 165]); + let multisig = + super::parse_token_2022_state(super::Token2022StateKind::Multisig, &[0; 355]); + assert_eq!(mint.as_ref().map(|state| return state.base_hex.len()), Ok(164)); + assert_eq!(account.as_ref().map(|state| return state.base_hex.len()), Ok(330)); + assert_eq!(multisig.as_ref().map(|state| return state.base_hex.len()), Ok(710)); + assert_eq!( + mint.as_ref().map(|state| return state.base_fields["supply"].as_str()), + Ok(Some("0")) + ); + assert_eq!( + account.as_ref().map(|state| return state.base_fields["amount"].as_str()), + Ok(Some("0")) + ); + assert_eq!( + multisig.as_ref().map(|state| return state.base_fields["m"].as_u64()), + Ok(Some(0)) + ); + } + + #[test] + fn extended_mint_and_account_preserve_ordered_known_and_unknown_tlv() { + let mut mint = vec![0; 166]; + mint[165] = 1; + mint.extend_from_slice(&9u16.to_le_bytes()); + mint.extend_from_slice(&0u16.to_le_bytes()); + mint.extend_from_slice(&77u16.to_le_bytes()); + mint.extend_from_slice(&1u16.to_le_bytes()); + mint.push(0xcc); + mint.extend_from_slice(&[0; 8]); + let parsed_mint = super::parse_token_2022_state(super::Token2022StateKind::Mint, &mint); + assert_eq!(parsed_mint.as_ref().map(|state| return state.extensions.len()), Ok(2)); + assert_eq!( + parsed_mint.as_ref().map(|state| return state.extensions[0].extension_name), + Ok("non_transferable") + ); + assert_eq!( + parsed_mint.as_ref().map(|state| return state.extensions[1].extension_name), + Ok("unknown") + ); + let mut account = vec![0; 166]; + account[165] = 2; + account.extend_from_slice(&8u16.to_le_bytes()); + account.extend_from_slice(&1u16.to_le_bytes()); + account.push(1); + let parsed_account = + super::parse_token_2022_state(super::Token2022StateKind::Account, &account); + assert_eq!( + parsed_account.as_ref().map(|state| return state.extensions[0].extension_name), + Ok("memo_transfer") + ); + assert_eq!( + parsed_account + .as_ref() + .map(|state| return state.extensions[0].value_fields["enabled"].as_bool()), + Ok(Some(true)) + ); + } + + #[test] + fn malformed_padding_account_type_tlv_and_duplicates_fail_closed() { + let mut mint_padding = vec![0; 166]; + mint_padding[82] = 1; + mint_padding[165] = 1; + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Mint, &mint_padding).is_err() + ); + let mut wrong_type = vec![0; 166]; + wrong_type[165] = 2; + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Mint, &wrong_type).is_err() + ); + let mut truncated = vec![0; 166]; + truncated[165] = 2; + truncated.extend_from_slice(&1u16.to_le_bytes()); + truncated.extend_from_slice(&3u16.to_le_bytes()); + truncated.push(9); + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Account, &truncated).is_err() + ); + let mut duplicate = vec![0; 166]; + duplicate[165] = 2; + for _ in 0..2 { + duplicate.extend_from_slice(&8u16.to_le_bytes()); + duplicate.extend_from_slice(&1u16.to_le_bytes()); + duplicate.push(1); + } + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Account, &duplicate).is_err() + ); + } + + #[test] + fn state_lengths_and_tlv_count_are_bounded() { + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Multisig, &[0; 354]).is_err() + ); + let oversized = vec![0; 1_048_577]; + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Account, &oversized).is_err() + ); + let mut account = vec![0; 166]; + account[165] = 2; + for extension_type in 1u16..=65 { + account.extend_from_slice(&extension_type.to_le_bytes()); + account.extend_from_slice(&0u16.to_le_bytes()); + } + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Account, &account).is_err() + ); + } + #[test] + fn fixed_extension_payloads_are_structured_and_kind_checked() { + let mut mint = vec![0; 166]; + mint[165] = 1; + mint.extend_from_slice(&6u16.to_le_bytes()); + mint.extend_from_slice(&1u16.to_le_bytes()); + mint.push(2); + mint.extend_from_slice(&18u16.to_le_bytes()); + mint.extend_from_slice(&64u16.to_le_bytes()); + mint.extend_from_slice(&[0; 64]); + let parsed = super::parse_token_2022_state(super::Token2022StateKind::Mint, &mint); + assert_eq!( + parsed + .as_ref() + .map(|state| return state.extensions[0].value_fields["state"].as_str()), + Ok(Some("frozen")) + ); + assert_eq!( + parsed + .as_ref() + .map(|state| return state.extensions[1].value_fields["authority"].is_null()), + Ok(true) + ); + let mut incompatible = vec![0; 166]; + incompatible[165] = 2; + incompatible.extend_from_slice(&6u16.to_le_bytes()); + incompatible.extend_from_slice(&1u16.to_le_bytes()); + incompatible.push(1); + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Account, &incompatible) + .is_err() + ); + } + + #[test] + fn audited_fixed_extension_lengths_and_values_fail_closed() { + let mut invalid_bool = vec![0; 166]; + invalid_bool[165] = 2; + invalid_bool.extend_from_slice(&8u16.to_le_bytes()); + invalid_bool.extend_from_slice(&1u16.to_le_bytes()); + invalid_bool.push(2); + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Account, &invalid_bool) + .is_err() + ); + let mut invalid_pointer = vec![0; 166]; + invalid_pointer[165] = 1; + invalid_pointer.extend_from_slice(&18u16.to_le_bytes()); + invalid_pointer.extend_from_slice(&63u16.to_le_bytes()); + invalid_pointer.extend_from_slice(&[0; 63]); + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Mint, &invalid_pointer) + .is_err() + ); + } + + #[test] + fn transfer_fee_state_extensions_decode_exact_public_fields() { + let mut mint = vec![0; 166]; + mint[165] = 1; + let mut config = vec![0; 108]; + config[0..32].copy_from_slice(&[1; 32]); + config[32..64].copy_from_slice(&[2; 32]); + config[64..72].copy_from_slice(&7u64.to_le_bytes()); + config[72..80].copy_from_slice(&3u64.to_le_bytes()); + config[80..88].copy_from_slice(&11u64.to_le_bytes()); + config[88..90].copy_from_slice(&25u16.to_le_bytes()); + config[90..98].copy_from_slice(&4u64.to_le_bytes()); + config[98..106].copy_from_slice(&13u64.to_le_bytes()); + config[106..108].copy_from_slice(&50u16.to_le_bytes()); + mint.extend_from_slice(&1u16.to_le_bytes()); + mint.extend_from_slice(&108u16.to_le_bytes()); + mint.extend_from_slice(&config); + let parsed_mint = super::parse_token_2022_state(super::Token2022StateKind::Mint, &mint); + assert_eq!( + parsed_mint + .as_ref() + .map(|state| return state.extensions[0].value_fields["withheldAmount"].as_str()), + Ok(Some("7")) + ); + assert_eq!( + parsed_mint.as_ref().map(|state| return state.extensions[0].value_fields["newerTransferFee"]["transferFeeBasisPoints"].as_u64()), + Ok(Some(50)) + ); + let mut account = vec![0; 166]; + account[165] = 2; + account.extend_from_slice(&2u16.to_le_bytes()); + account.extend_from_slice(&8u16.to_le_bytes()); + account.extend_from_slice(&19u64.to_le_bytes()); + let parsed_account = + super::parse_token_2022_state(super::Token2022StateKind::Account, &account); + assert_eq!( + parsed_account + .as_ref() + .map(|state| return state.extensions[0].value_fields["withheldAmount"].as_str()), + Ok(Some("19")) + ); + } + + #[test] + fn transfer_fee_state_extensions_reject_invalid_lengths_and_basis_points() { + let mut short_config = vec![0; 166]; + short_config[165] = 1; + short_config.extend_from_slice(&1u16.to_le_bytes()); + short_config.extend_from_slice(&107u16.to_le_bytes()); + short_config.extend_from_slice(&[0; 107]); + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Mint, &short_config).is_err() + ); + let mut invalid_basis_points = vec![0; 166]; + invalid_basis_points[165] = 1; + let mut config = vec![0; 108]; + config[88..90].copy_from_slice(&10_001u16.to_le_bytes()); + invalid_basis_points.extend_from_slice(&1u16.to_le_bytes()); + invalid_basis_points.extend_from_slice(&108u16.to_le_bytes()); + invalid_basis_points.extend_from_slice(&config); + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Mint, &invalid_basis_points) + .is_err() + ); + let mut short_amount = vec![0; 166]; + short_amount[165] = 2; + short_amount.extend_from_slice(&2u16.to_le_bytes()); + short_amount.extend_from_slice(&7u16.to_le_bytes()); + short_amount.extend_from_slice(&[0; 7]); + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Account, &short_amount) + .is_err() + ); + } + + #[test] + fn confidential_transfer_state_extensions_decode_exact_public_fields() { + let mut mint = vec![0; 166]; + mint[165] = 1; + let mut mint_extension = vec![0; 65]; + mint_extension[0..32].copy_from_slice(&[1; 32]); + mint_extension[32] = 1; + mint_extension[33..65].copy_from_slice(&[2; 32]); + mint.extend_from_slice(&4u16.to_le_bytes()); + mint.extend_from_slice(&65u16.to_le_bytes()); + mint.extend_from_slice(&mint_extension); + let parsed_mint = super::parse_token_2022_state(super::Token2022StateKind::Mint, &mint); + assert_eq!( + parsed_mint + .as_ref() + .map(|state| return state.extensions[0].value_fields["autoApproveNewAccounts"] + .as_bool()), + Ok(Some(true)) + ); + assert_eq!( + parsed_mint + .as_ref() + .map(|state| return state.extensions[0].value_fields["auditorElgamalPubkey"] + .is_string()), + Ok(true) + ); + let mut account = vec![0; 166]; + account[165] = 2; + let mut account_extension = vec![0; 295]; + account_extension[0] = 1; + account_extension[1..33].copy_from_slice(&[3; 32]); + account_extension[33..97].copy_from_slice(&[4; 64]); + account_extension[97..161].copy_from_slice(&[5; 64]); + account_extension[161..225].copy_from_slice(&[6; 64]); + account_extension[225..261].copy_from_slice(&[7; 36]); + account_extension[261] = 1; + account_extension[262] = 0; + account_extension[263..271].copy_from_slice(&8u64.to_le_bytes()); + account_extension[271..279].copy_from_slice(&9u64.to_le_bytes()); + account_extension[279..287].copy_from_slice(&10u64.to_le_bytes()); + account_extension[287..295].copy_from_slice(&11u64.to_le_bytes()); + account.extend_from_slice(&5u16.to_le_bytes()); + account.extend_from_slice(&295u16.to_le_bytes()); + account.extend_from_slice(&account_extension); + let parsed_account = + super::parse_token_2022_state(super::Token2022StateKind::Account, &account); + assert_eq!( + parsed_account + .as_ref() + .map(|state| return state.extensions[0].value_fields["approved"].as_bool()), + Ok(Some(true)) + ); + assert_eq!( + parsed_account.as_ref().map(|state| { + return state.extensions[0].value_fields["maximumPendingBalanceCreditCounter"] + .as_str(); + }), + Ok(Some("9")) + ); + assert_eq!( + parsed_account.as_ref().map(|state| { + return state.extensions[0].value_fields["allowNonConfidentialCredits"].as_bool(); + }), + Ok(Some(false)) + ); + } + + #[test] + fn confidential_transfer_state_extensions_reject_invalid_lengths_and_flags() { + let mut short_mint = vec![0; 166]; + short_mint[165] = 1; + short_mint.extend_from_slice(&4u16.to_le_bytes()); + short_mint.extend_from_slice(&64u16.to_le_bytes()); + short_mint.extend_from_slice(&[0; 64]); + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Mint, &short_mint).is_err() + ); + let mut invalid_mint_flag = vec![0; 166]; + invalid_mint_flag[165] = 1; + let mut mint_extension = vec![0; 65]; + mint_extension[32] = 2; + invalid_mint_flag.extend_from_slice(&4u16.to_le_bytes()); + invalid_mint_flag.extend_from_slice(&65u16.to_le_bytes()); + invalid_mint_flag.extend_from_slice(&mint_extension); + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Mint, &invalid_mint_flag) + .is_err() + ); + let mut invalid_account_flag = vec![0; 166]; + invalid_account_flag[165] = 2; + let mut account_extension = vec![0; 295]; + account_extension[261] = 2; + invalid_account_flag.extend_from_slice(&5u16.to_le_bytes()); + invalid_account_flag.extend_from_slice(&295u16.to_le_bytes()); + invalid_account_flag.extend_from_slice(&account_extension); + assert!( + super::parse_token_2022_state( + super::Token2022StateKind::Account, + &invalid_account_flag + ) + .is_err() + ); + } + + #[test] + fn interest_scaled_ui_and_pausable_states_decode_exact_public_fields() { + let mut interest = Vec::new(); + interest.extend_from_slice(&[0; 32]); + interest.extend_from_slice(&(-10i64).to_le_bytes()); + interest.extend_from_slice(&(-250i16).to_le_bytes()); + interest.extend_from_slice(&20i64.to_le_bytes()); + interest.extend_from_slice(&500i16.to_le_bytes()); + let mut mint = vec![0; 166]; + mint[165] = 1; + mint.extend_from_slice(&10u16.to_le_bytes()); + mint.extend_from_slice(&(interest.len() as u16).to_le_bytes()); + mint.extend_from_slice(&interest); + let parsed = super::parse_token_2022_state(super::Token2022StateKind::Mint, &mint); + assert_eq!( + parsed + .as_ref() + .map(|state| return state.extensions[0].value_fields["currentRateBasisPoints"] + .as_i64()), + Ok(Some(500)) + ); + + let mut scaled = Vec::new(); + scaled.extend_from_slice(&[0; 32]); + scaled.extend_from_slice(&1.5f64.to_le_bytes()); + scaled.extend_from_slice(&123i64.to_le_bytes()); + scaled.extend_from_slice(&2.5f64.to_le_bytes()); + let mut scaled_mint = vec![0; 166]; + scaled_mint[165] = 1; + scaled_mint.extend_from_slice(&25u16.to_le_bytes()); + scaled_mint.extend_from_slice(&(scaled.len() as u16).to_le_bytes()); + scaled_mint.extend_from_slice(&scaled); + let parsed_scaled = + super::parse_token_2022_state(super::Token2022StateKind::Mint, &scaled_mint); + assert_eq!( + parsed_scaled + .as_ref() + .map(|state| return state.extensions[0].value_fields["multiplier"].as_str()), + Ok(Some("1.5")) + ); + + let mut pausable_mint = vec![0; 166]; + pausable_mint[165] = 1; + pausable_mint.extend_from_slice(&26u16.to_le_bytes()); + pausable_mint.extend_from_slice(&33u16.to_le_bytes()); + pausable_mint.extend_from_slice(&[0; 32]); + pausable_mint.push(1); + let parsed_pausable = + super::parse_token_2022_state(super::Token2022StateKind::Mint, &pausable_mint); + assert_eq!( + parsed_pausable + .as_ref() + .map(|state| return state.extensions[0].value_fields["paused"].as_bool()), + Ok(Some(true)) + ); + } + + #[test] + fn interest_scaled_ui_and_pausable_states_fail_closed() { + let mut short_interest = vec![0; 166]; + short_interest[165] = 1; + short_interest.extend_from_slice(&10u16.to_le_bytes()); + short_interest.extend_from_slice(&51u16.to_le_bytes()); + short_interest.extend_from_slice(&[0; 51]); + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Mint, &short_interest) + .is_err() + ); + + let mut invalid_scaled = vec![0; 166]; + invalid_scaled[165] = 1; + invalid_scaled.extend_from_slice(&25u16.to_le_bytes()); + invalid_scaled.extend_from_slice(&56u16.to_le_bytes()); + invalid_scaled.extend_from_slice(&[0; 32]); + invalid_scaled.extend_from_slice(&f64::NAN.to_le_bytes()); + invalid_scaled.extend_from_slice(&0i64.to_le_bytes()); + invalid_scaled.extend_from_slice(&1.0f64.to_le_bytes()); + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Mint, &invalid_scaled) + .is_err() + ); + + let mut invalid_pausable = vec![0; 166]; + invalid_pausable[165] = 1; + invalid_pausable.extend_from_slice(&26u16.to_le_bytes()); + invalid_pausable.extend_from_slice(&33u16.to_le_bytes()); + invalid_pausable.extend_from_slice(&[0; 32]); + invalid_pausable.push(2); + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Mint, &invalid_pausable) + .is_err() + ); + + let mut account_marker = vec![0; 166]; + account_marker[165] = 2; + account_marker.extend_from_slice(&27u16.to_le_bytes()); + account_marker.extend_from_slice(&0u16.to_le_bytes()); + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Account, &account_marker) + .is_ok() + ); + } + + #[test] + fn confidential_fee_and_mint_burn_states_decode_exact_public_fields() { + let mut fee_mint = vec![0; 166]; + fee_mint[165] = 1; + let mut fee_config = vec![0; 129]; + fee_config[0..32].copy_from_slice(&[1; 32]); + fee_config[32..64].copy_from_slice(&[2; 32]); + fee_config[64] = 1; + fee_config[65..129].copy_from_slice(&[3; 64]); + fee_mint.extend_from_slice(&16u16.to_le_bytes()); + fee_mint.extend_from_slice(&129u16.to_le_bytes()); + fee_mint.extend_from_slice(&fee_config); + let parsed_fee_mint = + super::parse_token_2022_state(super::Token2022StateKind::Mint, &fee_mint); + assert_eq!( + parsed_fee_mint + .as_ref() + .map(|state| return state.extensions[0].value_fields["harvestToMintEnabled"] + .as_bool()), + Ok(Some(true)) + ); + assert_eq!( + parsed_fee_mint + .as_ref() + .map(|state| return state.extensions[0].value_fields["withheldAmount"].is_string()), + Ok(true) + ); + + let mut fee_account = vec![0; 166]; + fee_account[165] = 2; + fee_account.extend_from_slice(&17u16.to_le_bytes()); + fee_account.extend_from_slice(&64u16.to_le_bytes()); + fee_account.extend_from_slice(&[4; 64]); + let parsed_fee_account = + super::parse_token_2022_state(super::Token2022StateKind::Account, &fee_account); + assert_eq!( + parsed_fee_account + .as_ref() + .map(|state| return state.extensions[0].value_fields["withheldAmount"].is_string()), + Ok(true) + ); + + let mut mint_burn = vec![0; 166]; + mint_burn[165] = 1; + let mut mint_burn_extension = vec![0; 196]; + mint_burn_extension[0..64].copy_from_slice(&[5; 64]); + mint_burn_extension[64..100].copy_from_slice(&[6; 36]); + mint_burn_extension[100..132].copy_from_slice(&[7; 32]); + mint_burn_extension[132..196].copy_from_slice(&[8; 64]); + mint_burn.extend_from_slice(&24u16.to_le_bytes()); + mint_burn.extend_from_slice(&196u16.to_le_bytes()); + mint_burn.extend_from_slice(&mint_burn_extension); + let parsed_mint_burn = + super::parse_token_2022_state(super::Token2022StateKind::Mint, &mint_burn); + assert_eq!( + parsed_mint_burn + .as_ref() + .map(|state| return state.extensions[0].value_fields["decryptableSupply"] + .is_string()), + Ok(true) + ); + assert_eq!( + parsed_mint_burn + .as_ref() + .map(|state| return state.extensions[0].value_fields["pendingBurn"].is_string()), + Ok(true) + ); + } + + #[test] + fn confidential_fee_and_mint_burn_states_fail_closed() { + let mut invalid_fee_flag = vec![0; 166]; + invalid_fee_flag[165] = 1; + let mut fee_config = vec![0; 129]; + fee_config[64] = 2; + invalid_fee_flag.extend_from_slice(&16u16.to_le_bytes()); + invalid_fee_flag.extend_from_slice(&129u16.to_le_bytes()); + invalid_fee_flag.extend_from_slice(&fee_config); + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Mint, &invalid_fee_flag) + .is_err() + ); + + let mut short_fee_amount = vec![0; 166]; + short_fee_amount[165] = 2; + short_fee_amount.extend_from_slice(&17u16.to_le_bytes()); + short_fee_amount.extend_from_slice(&63u16.to_le_bytes()); + short_fee_amount.extend_from_slice(&[0; 63]); + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Account, &short_fee_amount) + .is_err() + ); + + let mut short_mint_burn = vec![0; 166]; + short_mint_burn[165] = 1; + short_mint_burn.extend_from_slice(&24u16.to_le_bytes()); + short_mint_burn.extend_from_slice(&195u16.to_le_bytes()); + short_mint_burn.extend_from_slice(&[0; 195]); + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Mint, &short_mint_burn) + .is_err() + ); + } + + #[test] + fn token_group_and_member_states_decode_exact_public_fields() { + let mut mint = vec![0; 166]; + mint[165] = 1; + let mut group = vec![0; 80]; + group[0..32].copy_from_slice(&[1; 32]); + group[32..64].copy_from_slice(&[2; 32]); + group[64..72].copy_from_slice(&3u64.to_le_bytes()); + group[72..80].copy_from_slice(&5u64.to_le_bytes()); + mint.extend_from_slice(&21u16.to_le_bytes()); + mint.extend_from_slice(&80u16.to_le_bytes()); + mint.extend_from_slice(&group); + let parsed_group = super::parse_token_2022_state(super::Token2022StateKind::Mint, &mint); + assert_eq!( + parsed_group + .as_ref() + .map(|state| return state.extensions[0].value_fields["size"].as_str()), + Ok(Some("3")) + ); + assert_eq!( + parsed_group + .as_ref() + .map(|state| return state.extensions[0].value_fields["maxSize"].as_str()), + Ok(Some("5")) + ); + let mut member_mint = vec![0; 166]; + member_mint[165] = 1; + let mut member = vec![0; 72]; + member[0..32].copy_from_slice(&[3; 32]); + member[32..64].copy_from_slice(&[4; 32]); + member[64..72].copy_from_slice(&7u64.to_le_bytes()); + member_mint.extend_from_slice(&23u16.to_le_bytes()); + member_mint.extend_from_slice(&72u16.to_le_bytes()); + member_mint.extend_from_slice(&member); + let parsed_member = + super::parse_token_2022_state(super::Token2022StateKind::Mint, &member_mint); + assert_eq!( + parsed_member + .as_ref() + .map(|state| return state.extensions[0].value_fields["memberNumber"].as_str()), + Ok(Some("7")) + ); + assert_eq!( + parsed_member + .as_ref() + .map(|state| return state.extensions[0].value_fields["group"].is_string()), + Ok(true) + ); + } + + #[test] + fn token_group_and_member_states_reject_invalid_lengths_and_size() { + let mut short_group = vec![0; 166]; + short_group[165] = 1; + short_group.extend_from_slice(&21u16.to_le_bytes()); + short_group.extend_from_slice(&79u16.to_le_bytes()); + short_group.extend_from_slice(&[0; 79]); + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Mint, &short_group).is_err() + ); + let mut invalid_size = vec![0; 166]; + invalid_size[165] = 1; + let mut group = vec![0; 80]; + group[64..72].copy_from_slice(&6u64.to_le_bytes()); + group[72..80].copy_from_slice(&5u64.to_le_bytes()); + invalid_size.extend_from_slice(&21u16.to_le_bytes()); + invalid_size.extend_from_slice(&80u16.to_le_bytes()); + invalid_size.extend_from_slice(&group); + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Mint, &invalid_size).is_err() + ); + let mut short_member = vec![0; 166]; + short_member[165] = 1; + short_member.extend_from_slice(&23u16.to_le_bytes()); + short_member.extend_from_slice(&71u16.to_le_bytes()); + short_member.extend_from_slice(&[0; 71]); + assert!( + super::parse_token_2022_state(super::Token2022StateKind::Mint, &short_member).is_err() + ); + } + + #[test] + fn token_metadata_state_decodes_name_symbol_uri_and_additional_fields() { + let mut mint = vec![0; 166]; + mint[165] = 1; + let mut metadata = Vec::new(); + metadata.extend_from_slice(&[1; 32]); + metadata.extend_from_slice(&[2; 32]); + append_borsh_string(&mut metadata, "Khadhroony Token"); + append_borsh_string(&mut metadata, "KHD"); + append_borsh_string(&mut metadata, "https://example.invalid/token.json"); + metadata.extend_from_slice(&2u32.to_le_bytes()); + append_borsh_string(&mut metadata, "category"); + append_borsh_string(&mut metadata, "utility"); + append_borsh_string(&mut metadata, "website"); + append_borsh_string(&mut metadata, "https://example.invalid"); + mint.extend_from_slice(&19u16.to_le_bytes()); + mint.extend_from_slice(&(metadata.len() as u16).to_le_bytes()); + mint.extend_from_slice(&metadata); + let parsed = super::parse_token_2022_state(super::Token2022StateKind::Mint, &mint); + assert_eq!( + parsed + .as_ref() + .map(|state| return state.extensions[0].value_fields["name"].as_str()), + Ok(Some("Khadhroony Token")) + ); + assert_eq!( + parsed + .as_ref() + .map(|state| return state.extensions[0].value_fields["symbol"].as_str()), + Ok(Some("KHD")) + ); + assert_eq!( + parsed.as_ref().map(|state| { + return state.extensions[0].value_fields["additionalMetadata"] + .as_array() + .map(|values| return values.len()); + }), + Ok(Some(2)) + ); + } + + #[test] + fn token_metadata_state_rejects_truncation_invalid_utf8_duplicates_and_trailing_bytes() { + let mut truncated = token_metadata_fixture("name", "SYM", "uri", &[]); + truncated.pop(); + assert!(parse_metadata_extension(&truncated).is_err()); + let mut invalid_utf8 = token_metadata_fixture("name", "SYM", "uri", &[]); + invalid_utf8[68] = 0xff; + assert!(parse_metadata_extension(&invalid_utf8).is_err()); + let duplicate = + token_metadata_fixture("name", "SYM", "uri", &[("key", "one"), ("key", "two")]); + assert!(parse_metadata_extension(&duplicate).is_err()); + let mut trailing = token_metadata_fixture("name", "SYM", "uri", &[]); + trailing.push(1); + assert!(parse_metadata_extension(&trailing).is_err()); + let oversized = "x".repeat(1_025); + let oversized_value = token_metadata_fixture(&oversized, "SYM", "uri", &[]); + assert!(parse_metadata_extension(&oversized_value).is_err()); + } + + fn parse_metadata_extension(value: &[u8]) -> Result { + let mut mint = vec![0; 166]; + mint[165] = 1; + mint.extend_from_slice(&19u16.to_le_bytes()); + mint.extend_from_slice(&(value.len() as u16).to_le_bytes()); + mint.extend_from_slice(value); + return super::parse_token_2022_state(super::Token2022StateKind::Mint, &mint); + } + + fn token_metadata_fixture( + name: &str, + symbol: &str, + uri: &str, + fields: &[(&str, &str)], + ) -> Vec { + let mut value = Vec::new(); + value.extend_from_slice(&[0; 32]); + value.extend_from_slice(&[2; 32]); + append_borsh_string(&mut value, name); + append_borsh_string(&mut value, symbol); + append_borsh_string(&mut value, uri); + value.extend_from_slice(&(fields.len() as u32).to_le_bytes()); + for (key, field_value) in fields { + append_borsh_string(&mut value, key); + append_borsh_string(&mut value, field_value); + } + return value; + } + + fn append_borsh_string(output: &mut Vec, value: &str) { + output.extend_from_slice(&(value.len() as u32).to_le_bytes()); + output.extend_from_slice(value.as_bytes()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/token.rs b/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/token.rs new file mode 100644 index 0000000..e90aa86 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_spl_token_2022/src/token.rs @@ -0,0 +1,2603 @@ +// file: kb_decoder_spl_token_2022/src/token.rs +// version: 13 + +//! Bounded Token-2022 wire, account and authority decoding. + +use base64::Engine; // rust-rules: trait-import +use sha2::Digest; // rust-rules: trait-import + +#[derive(Clone, Debug)] +struct ParsedInstruction { + code: &'static str, + tag: u8, + parameters: serde_json::Value, + consumed: usize, +} + +#[derive(Clone, Debug)] +struct ResolvedAccount { + position: usize, + account_index: u64, + account_key: std::string::String, + signer: bool, + writable: bool, + source: serde_json::Value, +} + +pub(crate) fn entry_for_tag(tag: u8) -> std::option::Option<(u8, &'static str, bool)> { + return crate::INSTRUCTION_ENTRIES + .iter() + .copied() + .find(|(candidate, _, _)| return *candidate == tag); +} + +pub(crate) fn payload_tag( + input: &kb_store_core::CoreInstructionReplayInput, +) -> std::option::Option { + let bytes = decode_payload(input).ok(); + return bytes.and_then(|value| return value.first().copied()); +} + +pub(crate) fn decode( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_decoder_api::DecoderExecutionResult { + let bytes = match decode_payload(input) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return failed( + std::option::Option::None, + "token_payload_unavailable", + error.to_string(), + ); + }, + }; + let tag = match bytes.first().copied() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return failed( + std::option::Option::None, + "token_payload_empty", + "Token-2022 instruction payload is empty".to_string(), + ); + }, + }; + let recognized_code = crate::entry_for_tag(tag) + .map(|(_, code, _)| return code) + .or_else(|| return embedded_interface_code(bytes.as_slice())); + if recognized_code.is_none() { + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Unsupported, + recognized_entry_code: std::option::Option::None, + observations: std::vec::Vec::new(), + diagnostics: std::vec![kb_decoder_api::DecoderDiagnostic { + code: "unknown_token_instruction_tag_or_interface_discriminator".to_string(), + message: format!( + "unknown Token-2022 tag or embedded interface discriminator; payloadPrefixHex={}", + hex_prefix(bytes.as_slice()) + ), + retriable: false, + }], + }; + } + let parsed = match parse_instruction(bytes.as_slice(), false) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => { + return failed( + recognized_code, + "malformed_token_instruction", + format!("{message}; payloadPrefixHex={}", hex_prefix(bytes.as_slice())), + ); + }, + }; + let accounts = match resolve_accounts(input) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return failed( + std::option::Option::Some(parsed.code), + "token_accounts_malformed", + error.to_string(), + ); + }, + }; + let (parameters, batch_children) = if parsed.code == "batch" { + match batch_observations(input, bytes.as_slice(), accounts.as_slice()) { + std::result::Result::Ok((children, manifest)) => (manifest, children), + std::result::Result::Err(message) => { + return failed( + std::option::Option::Some("batch"), + "malformed_token_batch", + message, + ); + }, + } + } else { + (parsed.parameters.clone(), std::vec::Vec::new()) + }; + let account_projection = project_accounts(parsed.code, accounts.as_slice()); + let suffix_length = bytes.len().saturating_sub(parsed.consumed); + let authority = authority_projection(parsed.code, accounts.as_slice()); + let semantic_diagnostics = + semantic_diagnostics(parsed.code, parameters.clone(), accounts.as_slice(), suffix_length); + let committed = !input.transaction_failed; + let parent = observation( + input, + parsed.code, + parsed.tag, + parameters, + account_projection, + authority, + semantic_diagnostics, + bytes.as_slice(), + suffix_length, + committed, + format!("token_2022:{}:0", parsed.code), + input.instruction_path.clone(), + event_family(parsed.code), + ); + let mut observations = std::vec![parent]; + observations.extend(batch_children); + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Decoded, + recognized_entry_code: std::option::Option::Some(parsed.code.to_string()), + observations, + diagnostics: std::vec::Vec::new(), + }; +} + +fn decode_payload( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_core::Result> { + let payload = match input.instruction_payload_json.as_ref() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Token-2022 instruction payload is not retained", + )); + }, + }; + let encoded = match payload.get("dataBase64").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Token-2022 payload does not contain dataBase64", + )); + }, + }; + let maximum_encoded = crate::MAX_INSTRUCTION_BYTES + .saturating_mul(4) + .saturating_div(3) + .saturating_add(4); + if encoded.len() > maximum_encoded { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Token-2022 base64 payload exceeds {maximum_encoded} bytes" + ))); + } + let decoded = match base64::engine::general_purpose::STANDARD.decode(encoded.as_bytes()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Token-2022 payload is not valid base64: {error}" + ))); + }, + }; + if decoded.len() > crate::MAX_INSTRUCTION_BYTES { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Token-2022 payload exceeds {} decoded bytes", + crate::MAX_INSTRUCTION_BYTES + ))); + } + return std::result::Result::Ok(decoded); +} + +fn parse_instruction( + bytes: &[u8], + nested_in_batch: bool, +) -> std::result::Result { + if embedded_interface_code(bytes).is_some() { + return parse_embedded_interface_instruction(bytes); + } + let tag = match bytes.first().copied() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err("empty instruction".to_string()); + }, + }; + let code = match crate::entry_for_tag(tag) { + std::option::Option::Some((_, value, _)) => value, + std::option::Option::None => { + return std::result::Result::Err(format!("unknown instruction tag {tag}")); + }, + }; + if nested_in_batch && tag == 255 { + return std::result::Result::Err("nested Batch instruction is forbidden".to_string()); + } + let result = match tag { + 0 | 20 => parse_initialize_mint(bytes, code, tag), + 1 | 5 | 9 | 10 | 11 | 17 | 21 | 22 | 38 => std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::json!({}), + consumed: 1, + }), + 255 => std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::json!({}), + consumed: bytes.len(), + }), + 2 | 19 => parse_one_u8(bytes, code, tag, "m"), + 3 | 4 | 7 | 8 | 23 => parse_one_u64(bytes, code, tag, "amountRaw"), + 6 => parse_set_authority(bytes, code, tag), + 12 | 13 | 14 | 15 => parse_amount_decimals(bytes, code, tag), + 16 | 18 => parse_pubkey_parameter(bytes, code, tag, "owner"), + 24 => parse_ui_amount(bytes, code, tag), + 29 => parse_reallocate(bytes, code, tag), + 31 | 32 => std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::json!({}), + consumed: 1, + }), + 35 => parse_pubkey_parameter(bytes, code, tag, "delegate"), + 45 => parse_unwrap_lamports(bytes, code, tag), + 26 | 27 | 28 | 30 | 33 | 34 | 36 | 37 | 39 | 40 | 41 | 42 | 43 | 44 | 46 => { + parse_extension_envelope(bytes, code, tag) + }, + _ => std::result::Result::Err(format!("unsupported instruction tag {tag}")), + }; + return result; +} + +const TOKEN_METADATA_INITIALIZE_DISCRIMINATOR: [u8; 8] = + [0xd2, 0xe1, 0x1e, 0xa2, 0x58, 0xb8, 0x4d, 0x8d]; +const TOKEN_METADATA_UPDATE_FIELD_DISCRIMINATOR: [u8; 8] = + [0xdd, 0xe9, 0x31, 0x2d, 0xb5, 0xca, 0xdc, 0xc8]; +const TOKEN_METADATA_REMOVE_KEY_DISCRIMINATOR: [u8; 8] = + [0xea, 0x12, 0x20, 0x38, 0x59, 0x8d, 0x25, 0xb5]; +const TOKEN_METADATA_UPDATE_AUTHORITY_DISCRIMINATOR: [u8; 8] = + [0xd7, 0xe4, 0xa6, 0xe4, 0x54, 0x64, 0x56, 0x7b]; +const TOKEN_METADATA_EMIT_DISCRIMINATOR: [u8; 8] = [0xfa, 0xa6, 0xb4, 0xfa, 0x0d, 0x0c, 0xb8, 0x46]; +const TOKEN_GROUP_INITIALIZE_DISCRIMINATOR: [u8; 8] = + [0x79, 0x71, 0x6c, 0x27, 0x36, 0x33, 0x00, 0x04]; +const TOKEN_GROUP_UPDATE_MAX_SIZE_DISCRIMINATOR: [u8; 8] = + [0x6c, 0x25, 0xab, 0x8f, 0xf8, 0x1e, 0x12, 0x6e]; +const TOKEN_GROUP_UPDATE_AUTHORITY_DISCRIMINATOR: [u8; 8] = + [0xa1, 0x69, 0x58, 0x01, 0xed, 0xdd, 0xd8, 0xcb]; +const TOKEN_GROUP_INITIALIZE_MEMBER_DISCRIMINATOR: [u8; 8] = + [0x98, 0x20, 0xde, 0xb0, 0xdf, 0xed, 0x74, 0x86]; + +fn embedded_interface_code(bytes: &[u8]) -> std::option::Option<&'static str> { + let discriminator = match bytes.get(..8) { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::option::Option::None, + }; + return match discriminator { + value if value == TOKEN_METADATA_INITIALIZE_DISCRIMINATOR => { + std::option::Option::Some("initialize_token_metadata") + }, + value if value == TOKEN_METADATA_UPDATE_FIELD_DISCRIMINATOR => { + std::option::Option::Some("update_token_metadata_field") + }, + value if value == TOKEN_METADATA_REMOVE_KEY_DISCRIMINATOR => { + std::option::Option::Some("remove_token_metadata_key") + }, + value if value == TOKEN_METADATA_UPDATE_AUTHORITY_DISCRIMINATOR => { + std::option::Option::Some("update_token_metadata_authority") + }, + value if value == TOKEN_METADATA_EMIT_DISCRIMINATOR => { + std::option::Option::Some("emit_token_metadata") + }, + value if value == TOKEN_GROUP_INITIALIZE_DISCRIMINATOR => { + std::option::Option::Some("initialize_token_group") + }, + value if value == TOKEN_GROUP_UPDATE_MAX_SIZE_DISCRIMINATOR => { + std::option::Option::Some("update_token_group_max_size") + }, + value if value == TOKEN_GROUP_UPDATE_AUTHORITY_DISCRIMINATOR => { + std::option::Option::Some("update_token_group_authority") + }, + value if value == TOKEN_GROUP_INITIALIZE_MEMBER_DISCRIMINATOR => { + std::option::Option::Some("initialize_token_group_member") + }, + _ => std::option::Option::None, + }; +} + +macro_rules! token_value_or_return { + ($expression:expr) => { + match $expression { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + }; +} + +fn parse_embedded_interface_instruction( + bytes: &[u8], +) -> std::result::Result { + let code = match embedded_interface_code(bytes) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "unknown embedded interface discriminator".to_string(), + ); + }, + }; + let mut offset = 8_usize; + let parameters = match code { + "initialize_token_metadata" => { + let name = token_value_or_return!(read_borsh_string(bytes, &mut offset, "name")); + let symbol = token_value_or_return!(read_borsh_string(bytes, &mut offset, "symbol")); + let uri = token_value_or_return!(read_borsh_string(bytes, &mut offset, "uri")); + serde_json::json!({"name":name,"symbol":symbol,"uri":uri}) + }, + "update_token_metadata_field" => { + let field_tag = token_value_or_return!(read_u8(bytes, &mut offset, "metadata field")); + let field = match field_tag { + 0 => serde_json::json!({"kind":"name"}), + 1 => serde_json::json!({"kind":"symbol"}), + 2 => serde_json::json!({"kind":"uri"}), + 3 => { + serde_json::json!({"kind":"key","key":token_value_or_return!(read_borsh_string(bytes, &mut offset, "field key"))}) + }, + _ => { + return std::result::Result::Err(format!( + "unknown Token Metadata field discriminant {field_tag}" + )); + }, + }; + let value = token_value_or_return!(read_borsh_string(bytes, &mut offset, "value")); + serde_json::json!({"field":field,"value":value}) + }, + "remove_token_metadata_key" => { + let idempotent = match token_value_or_return!(read_u8(bytes, &mut offset, "idempotent")) + { + 0 => false, + 1 => true, + value => return std::result::Result::Err(format!("invalid Borsh bool {value}")), + }; + let key = token_value_or_return!(read_borsh_string(bytes, &mut offset, "key")); + serde_json::json!({"idempotent":idempotent,"key":key}) + }, + "update_token_metadata_authority" => { + let authority = + token_value_or_return!(read_nullable_pubkey(bytes, &mut offset, "new authority")); + serde_json::json!({"newAuthority":authority}) + }, + "emit_token_metadata" => { + let start = token_value_or_return!(read_borsh_option_u64(bytes, &mut offset, "start")); + let end = token_value_or_return!(read_borsh_option_u64(bytes, &mut offset, "end")); + serde_json::json!({"start":start,"end":end}) + }, + "initialize_token_group" => { + let update_authority = token_value_or_return!(read_nullable_pubkey( + bytes, + &mut offset, + "update authority" + )); + let max_size = token_value_or_return!(read_u64(bytes, &mut offset, "max size")); + serde_json::json!({"updateAuthority":update_authority,"maxSize":max_size.to_string()}) + }, + "update_token_group_max_size" => { + let max_size = token_value_or_return!(read_u64(bytes, &mut offset, "max size")); + serde_json::json!({"maxSize":max_size.to_string()}) + }, + "update_token_group_authority" => { + let authority = + token_value_or_return!(read_nullable_pubkey(bytes, &mut offset, "new authority")); + serde_json::json!({"newAuthority":authority}) + }, + "initialize_token_group_member" => serde_json::json!({}), + _ => { + return std::result::Result::Err(format!( + "unsupported embedded interface instruction {code}" + )); + }, + }; + if offset != bytes.len() { + return std::result::Result::Err(format!( + "{code} requires exact wire length; trailingBytes={}", + bytes.len().saturating_sub(offset) + )); + } + return std::result::Result::Ok(ParsedInstruction { + code, + tag: bytes[0], + parameters, + consumed: offset, + }); +} + +fn read_u8( + bytes: &[u8], + offset: &mut usize, + label: &str, +) -> std::result::Result { + let value = match bytes.get(*offset).copied() { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::result::Result::Err(format!("truncated {label}")), + }; + *offset = offset.saturating_add(1); + return std::result::Result::Ok(value); +} + +fn read_u64( + bytes: &[u8], + offset: &mut usize, + label: &str, +) -> std::result::Result { + let end = offset.saturating_add(8); + let slice = match bytes.get(*offset..end) { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::result::Result::Err(format!("truncated {label}")), + }; + *offset = end; + return std::result::Result::Ok(u64::from_le_bytes([ + slice[0], slice[1], slice[2], slice[3], slice[4], slice[5], slice[6], slice[7], + ])); +} + +fn read_borsh_string( + bytes: &[u8], + offset: &mut usize, + label: &str, +) -> std::result::Result { + let length_end = offset.saturating_add(4); + let length_bytes = match bytes.get(*offset..length_end) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!("truncated {label} length")); + }, + }; + let length = + u32::from_le_bytes([length_bytes[0], length_bytes[1], length_bytes[2], length_bytes[3]]) + as usize; + if length > crate::MAX_METADATA_STRING_BYTES { + return std::result::Result::Err(format!( + "{label} exceeds {} bytes", + crate::MAX_METADATA_STRING_BYTES + )); + } + let start = length_end; + let end = start.saturating_add(length); + let value = match bytes.get(start..end) { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::result::Result::Err(format!("truncated {label}")), + }; + let text = match std::str::from_utf8(value) { + std::result::Result::Ok(value) => value.to_string(), + std::result::Result::Err(_) => { + return std::result::Result::Err(format!("{label} is not valid UTF-8")); + }, + }; + *offset = end; + return std::result::Result::Ok(text); +} + +fn read_nullable_pubkey( + bytes: &[u8], + offset: &mut usize, + label: &str, +) -> std::result::Result { + let end = offset.saturating_add(32); + let value = match bytes.get(*offset..end) { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::result::Result::Err(format!("truncated {label}")), + }; + *offset = end; + if value.iter().all(|byte| return *byte == 0) { + return std::result::Result::Ok(serde_json::Value::Null); + } + return std::result::Result::Ok(serde_json::Value::String(bs58::encode(value).into_string())); +} + +fn read_borsh_option_u64( + bytes: &[u8], + offset: &mut usize, + label: &str, +) -> std::result::Result { + let tag = match read_u8(bytes, offset, label) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return match tag { + 0 => std::result::Result::Ok(serde_json::Value::Null), + 1 => { + let value = match read_u64(bytes, offset, label) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + std::result::Result::Ok(serde_json::Value::String(value.to_string())) + }, + value => std::result::Result::Err(format!("invalid Borsh option tag {value} for {label}")), + }; +} + +fn parse_reallocate( + bytes: &[u8], + code: &'static str, + tag: u8, +) -> std::result::Result { + if bytes.is_empty() || bytes.len().saturating_sub(1) % 2 != 0 { + return std::result::Result::Err(format!( + "{code} requires a complete u16 extension type sequence" + )); + } + let mut extension_types = std::vec::Vec::new(); + for chunk in bytes[1..].chunks_exact(2) { + extension_types.push(u16::from_le_bytes([chunk[0], chunk[1]])); + } + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::json!({"extensionTypes": extension_types}), + consumed: bytes.len(), + }); +} + +fn parse_extension_envelope( + bytes: &[u8], + code: &'static str, + tag: u8, +) -> std::result::Result { + return match tag { + 26 => parse_transfer_fee_instruction(bytes, tag), + 27 => parse_confidential_transfer_instruction(bytes, tag), + 28 => parse_default_account_state_instruction(bytes, tag), + 30 => parse_toggle_extension_instruction( + bytes, + tag, + "memo_transfer", + "enable_required_transfer_memos", + "disable_required_transfer_memos", + ), + 33 => parse_interest_bearing_instruction(bytes, tag), + 34 => parse_toggle_extension_instruction( + bytes, + tag, + "cpi_guard", + "enable_cpi_guard", + "disable_cpi_guard", + ), + 36 => parse_pointer_extension_instruction( + bytes, + tag, + "transfer_hook", + "initialize_transfer_hook", + "update_transfer_hook_program_id", + "authority", + "programId", + ), + 37 => parse_confidential_transfer_fee_instruction(bytes, tag), + 39 => parse_pointer_extension_instruction( + bytes, + tag, + "metadata_pointer", + "initialize_metadata_pointer", + "update_metadata_pointer", + "authority", + "metadataAddress", + ), + 40 => parse_pointer_extension_instruction( + bytes, + tag, + "group_pointer", + "initialize_group_pointer", + "update_group_pointer", + "authority", + "groupAddress", + ), + 41 => parse_pointer_extension_instruction( + bytes, + tag, + "group_member_pointer", + "initialize_group_member_pointer", + "update_group_member_pointer", + "authority", + "memberAddress", + ), + 42 => parse_confidential_mint_burn_instruction(bytes, tag), + 43 => parse_scaled_ui_amount_instruction(bytes, tag), + 44 => parse_pausable_instruction(bytes, tag), + 46 => parse_permissioned_burn_instruction(bytes, tag), + _ => parse_opaque_extension_envelope(bytes, code, tag), + }; +} + +fn parse_confidential_transfer_instruction( + bytes: &[u8], + tag: u8, +) -> std::result::Result { + let sub = match bytes.get(1).copied() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "confidential transfer instruction requires a sub-discriminant".to_string(), + ); + }, + }; + let parsed = match sub { + 0 => { + if bytes.len() != 67 { + return std::result::Result::Err(format!( + "initialize_confidential_transfer_mint requires exactly 67 bytes; received {}", + bytes.len() + )); + } + let auto_approve = match bytes[34] { + 0 => false, + 1 => true, + value => { + return std::result::Result::Err(format!( + "initialize_confidential_transfer_mint has invalid Bool value {value}" + )); + }, + }; + ParsedInstruction { + code: "initialize_confidential_transfer_mint", + tag, + parameters: serde_json::json!({ + "extension": "confidential_transfer", + "subDiscriminant": sub, + "authority": optional_nonzero_pubkey(&bytes[2..34]), + "autoApproveNewAccounts": auto_approve, + "auditorElgamalPubkeyBase64": base64::engine::general_purpose::STANDARD.encode(&bytes[35..67]), + "auditorElgamalPubkeyPresent": bytes[35..67].iter().any(|value| return *value != 0), + }), + consumed: 67, + } + }, + 1 => { + if bytes.len() != 35 { + return std::result::Result::Err(format!( + "update_confidential_transfer_mint requires exactly 35 bytes; received {}", + bytes.len() + )); + } + let auto_approve = match bytes[2] { + 0 => false, + 1 => true, + value => { + return std::result::Result::Err(format!( + "update_confidential_transfer_mint has invalid Bool value {value}" + )); + }, + }; + ParsedInstruction { + code: "update_confidential_transfer_mint", + tag, + parameters: serde_json::json!({ + "extension": "confidential_transfer", + "subDiscriminant": sub, + "autoApproveNewAccounts": auto_approve, + "auditorElgamalPubkeyBase64": base64::engine::general_purpose::STANDARD.encode(&bytes[3..35]), + "auditorElgamalPubkeyPresent": bytes[3..35].iter().any(|value| return *value != 0), + }), + consumed: 35, + } + }, + 3 => ParsedInstruction { + code: "approve_confidential_transfer_account", + tag, + parameters: serde_json::json!({"extension":"confidential_transfer","subDiscriminant":sub}), + consumed: 2, + }, + 4 => { + if bytes.len() != 3 { + return std::result::Result::Err(format!( + "empty_confidential_transfer_account requires exactly 3 bytes; received {}", + bytes.len() + )); + } + ParsedInstruction { + code: "empty_confidential_transfer_account", + tag, + parameters: serde_json::json!({ + "extension":"confidential_transfer", + "subDiscriminant":sub, + "proofInstructionOffset": i8::from_le_bytes([bytes[2]]), + "proofLocation": if bytes[2] == 0 { "context_state_account" } else { "instruction_offset" }, + }), + consumed: 3, + } + }, + 5 => { + if bytes.len() != 11 { + return std::result::Result::Err(format!( + "deposit_confidential_tokens requires exactly 11 bytes; received {}", + bytes.len() + )); + } + let amount = u64::from_le_bytes([ + bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], + ]); + ParsedInstruction { + code: "deposit_confidential_tokens", + tag, + parameters: serde_json::json!({ + "extension":"confidential_transfer", + "subDiscriminant":sub, + "amountRaw": amount.to_string(), + "decimals": bytes[10], + }), + consumed: 11, + } + }, + 8 => { + if bytes.len() != 46 { + return std::result::Result::Err(format!( + "apply_confidential_pending_balance requires exactly 46 bytes; received {}", + bytes.len() + )); + } + let counter = u64::from_le_bytes([ + bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], + ]); + ParsedInstruction { + code: "apply_confidential_pending_balance", + tag, + parameters: serde_json::json!({ + "extension":"confidential_transfer", + "subDiscriminant":sub, + "expectedPendingBalanceCreditCounter": counter.to_string(), + "newDecryptableAvailableBalanceBase64": base64::engine::general_purpose::STANDARD.encode(&bytes[10..46]), + }), + consumed: 46, + } + }, + 9 | 10 | 11 | 12 => { + if bytes.len() != 2 { + return std::result::Result::Err(format!( + "confidential credit toggle requires exactly 2 bytes; received {}", + bytes.len() + )); + } + let (code, credit_kind, enabled) = match sub { + 9 => ("enable_confidential_credits", "confidential", true), + 10 => ("disable_confidential_credits", "confidential", false), + 11 => ("enable_non_confidential_credits", "non_confidential", true), + 12 => ("disable_non_confidential_credits", "non_confidential", false), + _ => { + return std::result::Result::Err("unreachable confidential toggle".to_string()); + }, + }; + ParsedInstruction { + code, + tag, + parameters: serde_json::json!({ + "extension":"confidential_transfer", + "subDiscriminant":sub, + "creditKind": credit_kind, + "enabled": enabled, + }), + consumed: 2, + } + }, + 2 => { + if bytes.len() != 47 { + return std::result::Result::Err(format!( + "configure_confidential_transfer_account requires exactly 47 bytes; received {}", + bytes.len() + )); + } + let maximum_pending_balance_credit_counter = u64::from_le_bytes([ + bytes[38], bytes[39], bytes[40], bytes[41], bytes[42], bytes[43], bytes[44], + bytes[45], + ]); + ParsedInstruction { + code: "configure_confidential_transfer_account", + tag, + parameters: serde_json::json!({ + "extension":"confidential_transfer", + "subDiscriminant":sub, + "decryptableZeroBalanceBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[2..38]), + "maximumPendingBalanceCreditCounter":maximum_pending_balance_credit_counter.to_string(), + "proofInstructionOffset":i8::from_le_bytes([bytes[46]]), + "proofLocation":if bytes[46] == 0 { "context_state_account" } else { "instruction_offset" }, + }), + consumed: 47, + } + }, + 6 => { + if bytes.len() != 49 { + return std::result::Result::Err(format!( + "withdraw_confidential_tokens requires exactly 49 bytes; received {}", + bytes.len() + )); + } + let amount = u64::from_le_bytes([ + bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], + ]); + ParsedInstruction { + code: "withdraw_confidential_tokens", + tag, + parameters: serde_json::json!({ + "extension":"confidential_transfer", + "subDiscriminant":sub, + "amountRaw":amount.to_string(), + "decimals":bytes[10], + "newDecryptableAvailableBalanceBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[11..47]), + "equalityProofInstructionOffset":i8::from_le_bytes([bytes[47]]), + "rangeProofInstructionOffset":i8::from_le_bytes([bytes[48]]), + }), + consumed: 49, + } + }, + 7 => { + if bytes.len() != 169 { + return std::result::Result::Err(format!( + "transfer_confidential_tokens requires exactly 169 bytes; received {}", + bytes.len() + )); + } + ParsedInstruction { + code: "transfer_confidential_tokens", + tag, + parameters: serde_json::json!({ + "extension":"confidential_transfer", + "subDiscriminant":sub, + "newSourceDecryptableAvailableBalanceBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[2..38]), + "transferAmountAuditorCiphertextLoBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[38..102]), + "transferAmountAuditorCiphertextHiBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[102..166]), + "equalityProofInstructionOffset":i8::from_le_bytes([bytes[166]]), + "ciphertextValidityProofInstructionOffset":i8::from_le_bytes([bytes[167]]), + "rangeProofInstructionOffset":i8::from_le_bytes([bytes[168]]), + }), + consumed: 169, + } + }, + 13 => { + if bytes.len() != 171 { + return std::result::Result::Err(format!( + "transfer_confidential_tokens_with_fee requires exactly 171 bytes; received {}", + bytes.len() + )); + } + ParsedInstruction { + code: "transfer_confidential_tokens_with_fee", + tag, + parameters: serde_json::json!({ + "extension":"confidential_transfer", + "subDiscriminant":sub, + "newSourceDecryptableAvailableBalanceBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[2..38]), + "transferAmountAuditorCiphertextLoBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[38..102]), + "transferAmountAuditorCiphertextHiBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[102..166]), + "equalityProofInstructionOffset":i8::from_le_bytes([bytes[166]]), + "transferAmountCiphertextValidityProofInstructionOffset":i8::from_le_bytes([bytes[167]]), + "feeSigmaProofInstructionOffset":i8::from_le_bytes([bytes[168]]), + "feeCiphertextValidityProofInstructionOffset":i8::from_le_bytes([bytes[169]]), + "rangeProofInstructionOffset":i8::from_le_bytes([bytes[170]]), + }), + consumed: 171, + } + }, + 14 => { + if bytes.len() != 2 { + return std::result::Result::Err(format!( + "configure_confidential_transfer_account_with_registry requires exactly 2 bytes; received {}", + bytes.len() + )); + } + ParsedInstruction { + code: "configure_confidential_transfer_account_with_registry", + tag, + parameters: serde_json::json!({ + "extension":"confidential_transfer", + "subDiscriminant":sub, + "proofLocation":"elgamal_registry_account", + "ownerSignatureRequired":false, + }), + consumed: 2, + } + }, + value => { + return std::result::Result::Err(format!( + "unknown confidential transfer sub-discriminant {value}" + )); + }, + }; + if bytes.len() != parsed.consumed { + return std::result::Result::Err(format!( + "{} requires exactly {} bytes; received {}", + parsed.code, + parsed.consumed, + bytes.len() + )); + } + return std::result::Result::Ok(parsed); +} + +fn parse_confidential_transfer_fee_instruction( + bytes: &[u8], + tag: u8, +) -> std::result::Result { + let sub = match bytes.get(1).copied() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "confidential transfer fee instruction requires a sub-discriminant".to_string(), + ); + }, + }; + let parsed = match sub { + 0 => { + if bytes.len() != 66 { + return std::result::Result::Err(format!( + "initialize_confidential_transfer_fee_config requires exactly 66 bytes; received {}", + bytes.len() + )); + } + ParsedInstruction { + code: "initialize_confidential_transfer_fee_config", + tag, + parameters: serde_json::json!({ + "extension":"confidential_transfer_fee", + "subDiscriminant":sub, + "authority":optional_nonzero_pubkey(&bytes[2..34]), + "withdrawWithheldAuthorityElgamalPubkeyBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[34..66]), + }), + consumed: 66, + } + }, + 1 => { + if bytes.len() != 39 { + return std::result::Result::Err(format!( + "withdraw_confidential_withheld_tokens_from_mint requires exactly 39 bytes; received {}", + bytes.len() + )); + } + ParsedInstruction { + code: "withdraw_confidential_withheld_tokens_from_mint", + tag, + parameters: serde_json::json!({ + "extension":"confidential_transfer_fee", + "subDiscriminant":sub, + "proofInstructionOffset":i8::from_le_bytes([bytes[2]]), + "proofLocation":if bytes[2] == 0 { "context_state_account" } else { "instruction_offset" }, + "newDecryptableAvailableBalanceBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[3..39]), + }), + consumed: 39, + } + }, + 2 => { + if bytes.len() != 40 { + return std::result::Result::Err(format!( + "withdraw_confidential_withheld_tokens_from_accounts requires exactly 40 bytes; received {}", + bytes.len() + )); + } + ParsedInstruction { + code: "withdraw_confidential_withheld_tokens_from_accounts", + tag, + parameters: serde_json::json!({ + "extension":"confidential_transfer_fee", + "subDiscriminant":sub, + "numTokenAccounts":bytes[2], + "proofInstructionOffset":i8::from_le_bytes([bytes[3]]), + "proofLocation":if bytes[3] == 0 { "context_state_account" } else { "instruction_offset" }, + "newDecryptableAvailableBalanceBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[4..40]), + }), + consumed: 40, + } + }, + 3 | 4 | 5 => { + if bytes.len() != 2 { + return std::result::Result::Err(format!( + "confidential transfer fee toggle or harvest instruction requires exactly 2 bytes; received {}", + bytes.len() + )); + } + let code = match sub { + 3 => "harvest_confidential_withheld_tokens_to_mint", + 4 => "enable_confidential_harvest_to_mint", + 5 => "disable_confidential_harvest_to_mint", + _ => { + return std::result::Result::Err( + "unreachable confidential transfer fee leaf".to_string(), + ); + }, + }; + ParsedInstruction { + code, + tag, + parameters: serde_json::json!({ + "extension":"confidential_transfer_fee", + "subDiscriminant":sub, + }), + consumed: 2, + } + }, + value => { + return std::result::Result::Err(format!( + "unknown confidential transfer fee sub-discriminant {value}" + )); + }, + }; + if bytes.len() != parsed.consumed { + return std::result::Result::Err(format!( + "{} requires exactly {} bytes; received {}", + parsed.code, + parsed.consumed, + bytes.len() + )); + } + return std::result::Result::Ok(parsed); +} + +fn parse_confidential_mint_burn_instruction( + bytes: &[u8], + tag: u8, +) -> std::result::Result { + let sub = match bytes.get(1).copied() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "confidential mint/burn instruction requires a sub-discriminant".to_string(), + ); + }, + }; + let expected = match sub { + 0 => 70, + 1 => 35, + 2 => 38, + 3 | 4 => 169, + 5 => 2, + value => { + return std::result::Result::Err(format!( + "unknown confidential mint/burn sub-discriminant {value}" + )); + }, + }; + if bytes.len() != expected { + return std::result::Result::Err(format!( + "confidential mint/burn sub-discriminant {sub} requires exactly {expected} bytes; received {}", + bytes.len() + )); + } + return match sub { + 0 => std::result::Result::Ok(ParsedInstruction { + code: "initialize_confidential_mint_burn", + tag, + parameters: serde_json::json!({ + "extension":"confidential_mint_burn", + "subDiscriminant":sub, + "supplyElgamalPubkeyBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[2..34]), + "decryptableSupplyBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[34..70]), + }), + consumed: 70, + }), + 1 => std::result::Result::Ok(ParsedInstruction { + code: "rotate_confidential_supply_elgamal_pubkey", + tag, + parameters: serde_json::json!({ + "extension":"confidential_mint_burn", + "subDiscriminant":sub, + "newSupplyElgamalPubkeyBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[2..34]), + "proofInstructionOffset":i8::from_le_bytes([bytes[34]]), + "proofLocation":if bytes[34] == 0 { "context_state_account" } else { "instruction_offset" }, + }), + consumed: 35, + }), + 2 => std::result::Result::Ok(ParsedInstruction { + code: "update_confidential_decryptable_supply", + tag, + parameters: serde_json::json!({ + "extension":"confidential_mint_burn", + "subDiscriminant":sub, + "newDecryptableSupplyBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[2..38]), + }), + consumed: 38, + }), + 3 | 4 => std::result::Result::Ok(ParsedInstruction { + code: if sub == 3 { "confidential_mint" } else { "confidential_burn" }, + tag, + parameters: serde_json::json!({ + "extension":"confidential_mint_burn", + "subDiscriminant":sub, + "newDecryptableBalanceBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[2..38]), + "amountAuditorCiphertextLoBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[38..102]), + "amountAuditorCiphertextHiBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[102..166]), + "equalityProofInstructionOffset":i8::from_le_bytes([bytes[166]]), + "ciphertextValidityProofInstructionOffset":i8::from_le_bytes([bytes[167]]), + "rangeProofInstructionOffset":i8::from_le_bytes([bytes[168]]), + }), + consumed: 169, + }), + 5 => std::result::Result::Ok(ParsedInstruction { + code: "apply_pending_confidential_burn", + tag, + parameters: serde_json::json!({ + "extension":"confidential_mint_burn", + "subDiscriminant":sub, + }), + consumed: 2, + }), + _ => std::result::Result::Err( + "unreachable confidential mint/burn sub-discriminant".to_string(), + ), + }; +} + +fn parse_scaled_ui_amount_instruction( + bytes: &[u8], + tag: u8, +) -> std::result::Result { + let sub = match bytes.get(1).copied() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "scaled UI amount instruction requires a sub-discriminant".to_string(), + ); + }, + }; + return match sub { + 0 => { + if bytes.len() != 42 { + return std::result::Result::Err(format!( + "initialize_scaled_ui_amount requires exactly 42 bytes; received {}", + bytes.len() + )); + } + let multiplier_bits = u64::from_le_bytes([ + bytes[34], bytes[35], bytes[36], bytes[37], bytes[38], bytes[39], bytes[40], + bytes[41], + ]); + std::result::Result::Ok(ParsedInstruction { + code: "initialize_scaled_ui_amount", + tag, + parameters: serde_json::json!({ + "extension": "scaled_ui_amount", + "subDiscriminant": sub, + "authority": optional_nonzero_pubkey(&bytes[2..34]), + "multiplierBitsHex": format!("{multiplier_bits:016x}"), + "multiplierFiniteDecimal": finite_f64_decimal(multiplier_bits), + }), + consumed: 42, + }) + }, + 1 => { + if bytes.len() != 18 { + return std::result::Result::Err(format!( + "update_scaled_ui_amount_multiplier requires exactly 18 bytes; received {}", + bytes.len() + )); + } + let multiplier_bits = u64::from_le_bytes([ + bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], + ]); + let effective_timestamp = i64::from_le_bytes([ + bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], bytes[16], + bytes[17], + ]); + std::result::Result::Ok(ParsedInstruction { + code: "update_scaled_ui_amount_multiplier", + tag, + parameters: serde_json::json!({ + "extension": "scaled_ui_amount", + "subDiscriminant": sub, + "multiplierBitsHex": format!("{multiplier_bits:016x}"), + "multiplierFiniteDecimal": finite_f64_decimal(multiplier_bits), + "effectiveTimestamp": effective_timestamp.to_string(), + }), + consumed: 18, + }) + }, + value => { + std::result::Result::Err(format!("unknown scaled UI amount sub-discriminant {value}")) + }, + }; +} + +fn parse_pausable_instruction( + bytes: &[u8], + tag: u8, +) -> std::result::Result { + let sub = match bytes.get(1).copied() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "pausable instruction requires a sub-discriminant".to_string(), + ); + }, + }; + return match sub { + 0 => { + if bytes.len() != 34 { + return std::result::Result::Err(format!( + "initialize_pausable_config requires exactly 34 bytes; received {}", + bytes.len() + )); + } + std::result::Result::Ok(ParsedInstruction { + code: "initialize_pausable_config", + tag, + parameters: serde_json::json!({ + "extension": "pausable", + "subDiscriminant": sub, + "pauseAuthority": bs58::encode(&bytes[2..34]).into_string(), + }), + consumed: 34, + }) + }, + 1 | 2 => { + if bytes.len() != 2 { + return std::result::Result::Err(format!( + "{} requires exactly 2 bytes; received {}", + if sub == 1 { "pause_token_mint" } else { "resume_token_mint" }, + bytes.len() + )); + } + std::result::Result::Ok(ParsedInstruction { + code: if sub == 1 { "pause_token_mint" } else { "resume_token_mint" }, + tag, + parameters: serde_json::json!({ + "extension": "pausable", + "subDiscriminant": sub, + "paused": sub == 1, + }), + consumed: 2, + }) + }, + value => std::result::Result::Err(format!("unknown pausable sub-discriminant {value}")), + }; +} + +fn parse_permissioned_burn_instruction( + bytes: &[u8], + tag: u8, +) -> std::result::Result { + let sub = match bytes.get(1).copied() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "permissioned burn instruction requires a sub-discriminant".to_string(), + ); + }, + }; + return match sub { + 0 => { + if bytes.len() != 34 { + return std::result::Result::Err(format!( + "initialize_permissioned_burn requires exactly 34 bytes; received {}", + bytes.len() + )); + } + std::result::Result::Ok(ParsedInstruction { + code: "initialize_permissioned_burn", + tag, + parameters: serde_json::json!({ + "extension": "permissioned_burn", + "subDiscriminant": sub, + "permissionedBurnAuthority": bs58::encode(&bytes[2..34]).into_string(), + }), + consumed: 34, + }) + }, + 1 => { + if bytes.len() != 10 { + return std::result::Result::Err(format!( + "permissioned_burn requires exactly 10 bytes; received {}", + bytes.len() + )); + } + let amount = match read_u64_at(bytes, 2) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + std::result::Result::Ok(ParsedInstruction { + code: "permissioned_burn", + tag, + parameters: serde_json::json!({ + "extension": "permissioned_burn", + "subDiscriminant": sub, + "amountRaw": amount.to_string(), + }), + consumed: 10, + }) + }, + 2 => { + if bytes.len() != 11 { + return std::result::Result::Err(format!( + "permissioned_burn_checked requires exactly 11 bytes; received {}", + bytes.len() + )); + } + let amount = match read_u64_at(bytes, 2) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + std::result::Result::Ok(ParsedInstruction { + code: "permissioned_burn_checked", + tag, + parameters: serde_json::json!({ + "extension": "permissioned_burn", + "subDiscriminant": sub, + "amountRaw": amount.to_string(), + "decimals": bytes[10], + }), + consumed: 11, + }) + }, + 3 => { + if bytes.len() != 169 { + return std::result::Result::Err(format!( + "permissioned_confidential_burn requires exactly 169 bytes; received {}", + bytes.len() + )); + } + std::result::Result::Ok(ParsedInstruction { + code: "permissioned_confidential_burn", + tag, + parameters: serde_json::json!({ + "extension":"permissioned_burn", + "subDiscriminant":sub, + "newDecryptableAvailableBalanceBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[2..38]), + "burnAmountAuditorCiphertextLoBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[38..102]), + "burnAmountAuditorCiphertextHiBase64":base64::engine::general_purpose::STANDARD.encode(&bytes[102..166]), + "equalityProofInstructionOffset":i8::from_le_bytes([bytes[166]]), + "ciphertextValidityProofInstructionOffset":i8::from_le_bytes([bytes[167]]), + "rangeProofInstructionOffset":i8::from_le_bytes([bytes[168]]), + }), + consumed: 169, + }) + }, + value => { + std::result::Result::Err(format!("unknown permissioned burn sub-discriminant {value}")) + }, + }; +} + +fn finite_f64_decimal(bits: u64) -> serde_json::Value { + let value = f64::from_bits(bits); + if value.is_finite() { + return serde_json::Value::String(value.to_string()); + } + return serde_json::Value::Null; +} + +fn parse_interest_bearing_instruction( + bytes: &[u8], + tag: u8, +) -> std::result::Result { + let sub = match bytes.get(1).copied() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "interest-bearing instruction requires a sub-discriminant".to_string(), + ); + }, + }; + return match sub { + 0 => { + if bytes.len() != 36 { + return std::result::Result::Err(format!( + "initialize_interest_bearing_mint requires exactly 36 bytes; received {}", + bytes.len() + )); + } + let authority_bytes = &bytes[2..34]; + let authority = if authority_bytes.iter().all(|value| return *value == 0) { + serde_json::Value::Null + } else { + serde_json::Value::String(bs58::encode(authority_bytes).into_string()) + }; + let rate = i16::from_le_bytes([bytes[34], bytes[35]]); + std::result::Result::Ok(ParsedInstruction { + code: "initialize_interest_bearing_mint", + tag, + parameters: serde_json::json!({ + "subDiscriminant": sub, + "rateAuthority": authority, + "rateBasisPoints": rate, + }), + consumed: 36, + }) + }, + 1 => { + if bytes.len() != 4 { + return std::result::Result::Err(format!( + "update_interest_bearing_rate requires exactly 4 bytes; received {}", + bytes.len() + )); + } + let rate = i16::from_le_bytes([bytes[2], bytes[3]]); + std::result::Result::Ok(ParsedInstruction { + code: "update_interest_bearing_rate", + tag, + parameters: serde_json::json!({ + "subDiscriminant": sub, + "rateBasisPoints": rate, + }), + consumed: 4, + }) + }, + _ => std::result::Result::Err(format!("unknown interest-bearing sub-discriminant {sub}")), + }; +} + +fn parse_transfer_fee_instruction( + bytes: &[u8], + tag: u8, +) -> std::result::Result { + let sub = match bytes.get(1).copied() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "transfer fee instruction requires a sub-discriminant".to_string(), + ); + }, + }; + let parsed = match sub { + 0 => { + let (config_authority, first_length) = match pubkey_option(&bytes[2..]) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + let second_offset = 2_usize.saturating_add(first_length); + let (withdraw_authority, second_length) = match pubkey_option(&bytes[second_offset..]) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + let basis_offset = second_offset.saturating_add(second_length); + let basis_points = match read_u16(bytes, basis_offset) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + let maximum_fee = match read_u64_at(bytes, basis_offset.saturating_add(2)) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + ParsedInstruction { + code: "initialize_transfer_fee_config", + tag, + parameters: serde_json::json!({ + "extension": "transfer_fee", + "subDiscriminant": sub, + "transferFeeConfigAuthority": config_authority, + "withdrawWithheldAuthority": withdraw_authority, + "transferFeeBasisPoints": basis_points, + "maximumFeeRaw": maximum_fee.to_string(), + }), + consumed: basis_offset.saturating_add(10), + } + }, + 1 => { + let amount = match read_u64_at(bytes, 2) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + let decimals = match bytes.get(10).copied() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "transfer_checked_with_fee requires decimals".to_string(), + ); + }, + }; + let fee = match read_u64_at(bytes, 11) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + ParsedInstruction { + code: "transfer_checked_with_fee", + tag, + parameters: serde_json::json!({ + "extension": "transfer_fee", + "subDiscriminant": sub, + "amountRaw": amount.to_string(), + "decimals": decimals, + "feeRaw": fee.to_string(), + }), + consumed: 19, + } + }, + 2 => ParsedInstruction { + code: "withdraw_withheld_tokens_from_mint", + tag, + parameters: serde_json::json!({"extension":"transfer_fee","subDiscriminant":sub}), + consumed: 2, + }, + 3 => { + let count = match bytes.get(2).copied() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "withdraw_withheld_tokens_from_accounts requires a count".to_string(), + ); + }, + }; + ParsedInstruction { + code: "withdraw_withheld_tokens_from_accounts", + tag, + parameters: serde_json::json!({ + "extension": "transfer_fee", + "subDiscriminant": sub, + "tokenAccountCount": count, + }), + consumed: 3, + } + }, + 4 => ParsedInstruction { + code: "harvest_withheld_tokens_to_mint", + tag, + parameters: serde_json::json!({"extension":"transfer_fee","subDiscriminant":sub}), + consumed: 2, + }, + 5 => { + let basis_points = match read_u16(bytes, 2) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + let maximum_fee = match read_u64_at(bytes, 4) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + ParsedInstruction { + code: "set_transfer_fee", + tag, + parameters: serde_json::json!({ + "extension": "transfer_fee", + "subDiscriminant": sub, + "transferFeeBasisPoints": basis_points, + "maximumFeeRaw": maximum_fee.to_string(), + }), + consumed: 12, + } + }, + value => { + return std::result::Result::Err(format!( + "unknown transfer fee sub-discriminant {value}" + )); + }, + }; + if bytes.len() < parsed.consumed { + return std::result::Result::Err(format!("{} payload is truncated", parsed.code)); + } + return std::result::Result::Ok(parsed); +} + +fn parse_default_account_state_instruction( + bytes: &[u8], + tag: u8, +) -> std::result::Result { + if bytes.len() < 3 { + return std::result::Result::Err( + "default account state instruction requires 3 bytes".to_string(), + ); + } + let code = match bytes[1] { + 0 => "initialize_default_account_state", + 1 => "update_default_account_state", + value => { + return std::result::Result::Err(format!( + "unknown default account state sub-discriminant {value}" + )); + }, + }; + let state = match bytes[2] { + 0 => "uninitialized", + 1 => "initialized", + 2 => "frozen", + value => return std::result::Result::Err(format!("unknown token account state {value}")), + }; + if bytes.len() != 3 { + return std::result::Result::Err(format!("{code} requires exactly 3 bytes")); + } + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::json!({ + "extension": "default_account_state", + "subDiscriminant": bytes[1], + "accountState": state, + "accountStateCode": bytes[2], + }), + consumed: 3, + }); +} + +fn parse_toggle_extension_instruction( + bytes: &[u8], + tag: u8, + extension: &str, + enable_code: &'static str, + disable_code: &'static str, +) -> std::result::Result { + if bytes.len() != 2 { + return std::result::Result::Err(format!( + "{extension} instruction requires exactly 2 bytes" + )); + } + let code = match bytes[1] { + 0 => enable_code, + 1 => disable_code, + value => { + return std::result::Result::Err(format!( + "unknown {extension} sub-discriminant {value}" + )); + }, + }; + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::json!({ + "extension": extension, + "subDiscriminant": bytes[1], + "enabled": bytes[1] == 0, + }), + consumed: 2, + }); +} + +fn parse_pointer_extension_instruction( + bytes: &[u8], + tag: u8, + extension: &str, + initialize_code: &'static str, + update_code: &'static str, + authority_field: &str, + address_field: &str, +) -> std::result::Result { + let sub = match bytes.get(1).copied() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!( + "{extension} instruction requires a sub-discriminant" + )); + }, + }; + let (code, authority, address, consumed) = match sub { + 0 => { + if bytes.len() != 66 { + return std::result::Result::Err(format!( + "{initialize_code} requires exactly 66 bytes; received {}", + bytes.len() + )); + } + ( + initialize_code, + optional_nonzero_pubkey(&bytes[2..34]), + optional_nonzero_pubkey(&bytes[34..66]), + 66_usize, + ) + }, + 1 => { + if bytes.len() != 34 { + return std::result::Result::Err(format!( + "{update_code} requires exactly 34 bytes; received {}", + bytes.len() + )); + } + ( + update_code, + serde_json::Value::Null, + optional_nonzero_pubkey(&bytes[2..34]), + 34_usize, + ) + }, + value => { + return std::result::Result::Err(format!( + "unknown {extension} sub-discriminant {value}" + )); + }, + }; + let mut parameters = serde_json::Map::new(); + parameters.insert("extension".to_string(), serde_json::Value::String(extension.to_string())); + parameters.insert("subDiscriminant".to_string(), serde_json::json!(sub)); + parameters.insert(address_field.to_string(), address); + if sub == 0 { + parameters.insert(authority_field.to_string(), authority); + } + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::Value::Object(parameters), + consumed, + }); +} + +fn optional_nonzero_pubkey(bytes: &[u8]) -> serde_json::Value { + if bytes.len() != 32 || bytes.iter().all(|value| return *value == 0) { + return serde_json::Value::Null; + } + return serde_json::Value::String(bs58::encode(bytes).into_string()); +} + +fn parse_opaque_extension_envelope( + bytes: &[u8], + code: &'static str, + tag: u8, +) -> std::result::Result { + let sub_discriminant = match bytes.get(1).copied() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!( + "{code} requires an extension sub-discriminant" + )); + }, + }; + let opaque = &bytes[2..]; + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::json!({ + "subDiscriminant": sub_discriminant, + "opaquePayloadLength": opaque.len(), + "opaquePayloadPrefixHex": hex_prefix(opaque), + "semanticStatus": "opaque_until_extension_layout_audit" + }), + consumed: bytes.len(), + }); +} + +fn parse_initialize_mint( + bytes: &[u8], + code: &'static str, + tag: u8, +) -> std::result::Result { + if bytes.len() < 35 { + return std::result::Result::Err(format!("{code} requires at least 35 bytes")); + } + let mint_authority = pubkey_text(&bytes[2..34]); + let (freeze_authority, option_length) = match pubkey_option(&bytes[34..]) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::json!({ + "decimals": bytes[1], + "mintAuthority": mint_authority, + "freezeAuthority": freeze_authority, + }), + consumed: 34 + option_length, + }); +} + +fn parse_one_u8( + bytes: &[u8], + code: &'static str, + tag: u8, + field: &str, +) -> std::result::Result { + if bytes.len() < 2 { + return std::result::Result::Err(format!("{code} requires 2 bytes")); + } + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: one_parameter(field, serde_json::Value::from(bytes[1])), + consumed: 2, + }); +} + +fn parse_one_u64( + bytes: &[u8], + code: &'static str, + tag: u8, + field: &str, +) -> std::result::Result { + let amount = match read_u64_at(bytes, 1) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: one_parameter(field, serde_json::Value::String(amount.to_string())), + consumed: 9, + }); +} + +fn parse_set_authority( + bytes: &[u8], + code: &'static str, + tag: u8, +) -> std::result::Result { + if bytes.len() < 3 { + return std::result::Result::Err("set_authority requires at least 3 bytes".to_string()); + } + let authority_type = match bytes[1] { + 0 => "mint_tokens", + 1 => "freeze_account", + 2 => "account_owner", + 3 => "close_account", + value => return std::result::Result::Err(format!("invalid authority type {value}")), + }; + let (new_authority, option_length) = match pubkey_option(&bytes[2..]) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::json!({ + "authorityType": authority_type, + "newAuthority": new_authority, + }), + consumed: 2 + option_length, + }); +} + +fn parse_amount_decimals( + bytes: &[u8], + code: &'static str, + tag: u8, +) -> std::result::Result { + if bytes.len() < 10 { + return std::result::Result::Err(format!("{code} requires 10 bytes")); + } + let amount = match read_u64_at(bytes, 1) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::json!({ + "amountRaw": amount.to_string(), + "decimals": bytes[9], + }), + consumed: 10, + }); +} + +fn parse_pubkey_parameter( + bytes: &[u8], + code: &'static str, + tag: u8, + field: &str, +) -> std::result::Result { + if bytes.len() < 33 { + return std::result::Result::Err(format!("{code} requires 33 bytes")); + } + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: one_parameter(field, serde_json::Value::String(pubkey_text(&bytes[1..33]))), + consumed: 33, + }); +} + +fn parse_ui_amount( + bytes: &[u8], + code: &'static str, + tag: u8, +) -> std::result::Result { + let text = match std::str::from_utf8(&bytes[1..]) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "ui amount is not UTF-8 at byte {}", + error.valid_up_to() + )); + }, + }; + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::json!({"uiAmount": text}), + consumed: bytes.len(), + }); +} + +fn parse_unwrap_lamports( + bytes: &[u8], + code: &'static str, + tag: u8, +) -> std::result::Result { + if bytes.len() < 2 { + return std::result::Result::Err("unwrap_lamports requires an option tag".to_string()); + } + let (amount, consumed) = match bytes[1] { + 0 => (serde_json::Value::Null, 2), + 1 => { + let value = match read_u64_at(bytes, 2) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + (serde_json::Value::String(value.to_string()), 10) + }, + value => return std::result::Result::Err(format!("invalid unwrap amount option {value}")), + }; + return std::result::Result::Ok(ParsedInstruction { + code, + tag, + parameters: serde_json::json!({"amountLamports": amount}), + consumed, + }); +} + +fn read_u16(bytes: &[u8], offset: usize) -> std::result::Result { + let slice = match bytes.get(offset..offset.saturating_add(2)) { + std::option::Option::Some(value) if value.len() == 2 => value, + _ => return std::result::Result::Err("truncated little-endian u16".to_string()), + }; + return std::result::Result::Ok(u16::from_le_bytes([slice[0], slice[1]])); +} + +fn read_u64_at(bytes: &[u8], offset: usize) -> std::result::Result { + let slice = match bytes.get(offset..offset.saturating_add(8)) { + std::option::Option::Some(value) if value.len() == 8 => value, + _ => return std::result::Result::Err("truncated little-endian u64".to_string()), + }; + let array = match <[u8; 8]>::try_from(slice) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return std::result::Result::Err("invalid little-endian u64".to_string()); + }, + }; + return std::result::Result::Ok(u64::from_le_bytes(array)); +} + +fn pubkey_option( + bytes: &[u8], +) -> std::result::Result<(serde_json::Value, usize), std::string::String> { + return match bytes.first().copied() { + std::option::Option::Some(0) => std::result::Result::Ok((serde_json::Value::Null, 1)), + std::option::Option::Some(1) if bytes.len() >= 33 => { + std::result::Result::Ok((serde_json::Value::String(pubkey_text(&bytes[1..33])), 33)) + }, + std::option::Option::Some(1) => { + std::result::Result::Err("truncated optional pubkey".to_string()) + }, + std::option::Option::Some(value) => { + std::result::Result::Err(format!("invalid optional pubkey tag {value}")) + }, + std::option::Option::None => { + std::result::Result::Err("missing optional pubkey tag".to_string()) + }, + }; +} + +fn pubkey_text(bytes: &[u8]) -> std::string::String { + return bs58::encode(bytes).into_string(); +} + +fn one_parameter(field: &str, value: serde_json::Value) -> serde_json::Value { + let mut parameters = serde_json::Map::new(); + parameters.insert(field.to_string(), value); + return serde_json::Value::Object(parameters); +} + +fn resolve_accounts( + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_core::Result> { + let instruction_accounts = match input.instruction_accounts_json.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Token-2022 instruction accounts must be a JSON array", + )); + }, + }; + let account_keys = match input.account_keys_json.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Token-2022 transaction account keys must be a JSON array", + )); + }, + }; + let mut output = std::vec::Vec::with_capacity(instruction_accounts.len()); + for (position, account) in instruction_accounts.iter().enumerate() { + let account_index = match account.get("accountIndex").and_then(serde_json::Value::as_u64) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Token-2022 instruction account {position} has no accountIndex" + ))); + }, + }; + let account_key = match account.get("accountKey").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) if !value.trim().is_empty() => value, + _ => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Token-2022 instruction account {position} has no accountKey" + ))); + }, + }; + let resolved = account_keys.iter().find(|candidate| { + return candidate.get("accountIndex").and_then(serde_json::Value::as_u64) + == std::option::Option::Some(account_index); + }); + let resolved = match resolved { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Token-2022 account index {account_index} is not resolved" + ))); + }, + }; + if resolved.get("accountKey").and_then(serde_json::Value::as_str) + != std::option::Option::Some(account_key) + { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Token-2022 account index {account_index} resolves to a different key" + ))); + } + let signer = match resolved.get("signer").and_then(serde_json::Value::as_bool) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Token-2022 account index {account_index} has no signer flag" + ))); + }, + }; + let writable = match resolved.get("writable").and_then(serde_json::Value::as_bool) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "Token-2022 account index {account_index} has no writable flag" + ))); + }, + }; + output.push(ResolvedAccount { + position, + account_index, + account_key: account_key.to_string(), + signer, + writable, + source: resolved.get("source").cloned().unwrap_or(serde_json::Value::Null), + }); + } + return std::result::Result::Ok(output); +} + +fn project_accounts(code: &str, accounts: &[ResolvedAccount]) -> serde_json::Value { + let authority_index = authority_index(code); + let values = accounts + .iter() + .enumerate() + .map(|(role_position, account)| { + return serde_json::json!({ + "position": account.position, + "rolePosition": role_position, + "accountIndex": account.account_index, + "accountKey": account.account_key, + "signer": account.signer, + "writable": account.writable, + "source": account.source, + "role": account_role(code, role_position, authority_index), + }); + }) + .collect::>(); + return serde_json::Value::Array(values); +} + +fn authority_projection(code: &str, accounts: &[ResolvedAccount]) -> serde_json::Value { + let authority_index = authority_index(code); + let index = match authority_index { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return serde_json::json!({ + "form": "not_applicable", + "declaredAuthority": serde_json::Value::Null, + "providedSigners": [], + "statefulMultisigValidation": "not_applicable", + }); + }, + }; + let authority = accounts.get(index); + let provided_signers = accounts + .iter() + .skip(index.saturating_add(1)) + .filter(|account| return account.signer) + .map(|account| return account.account_key.clone()) + .collect::>(); + let form = if accounts.len() > index.saturating_add(1) { "multisig" } else { "single" }; + return serde_json::json!({ + "form": form, + "declaredAuthority": authority.map(|value| return value.account_key.clone()), + "declaredAuthoritySigner": authority.map(|value| return value.signer), + "providedSigners": provided_signers, + "providedSignerCount": provided_signers.len(), + "statefulMultisigValidation": if form == "multisig" { + "requires_multisig_account_snapshot" + } else { + "not_applicable" + }, + "membersClaimed": false, + }); +} + +fn authority_index(code: &str) -> std::option::Option { + return match code { + "transfer" + | "approve" + | "mint_to" + | "burn" + | "close_account" + | "freeze_account" + | "thaw_account" + | "withdraw_excess_lamports" + | "unwrap_lamports" => std::option::Option::Some(2), + "revoke" | "set_authority" => std::option::Option::Some(1), + "transfer_checked" | "approve_checked" => std::option::Option::Some(3), + "mint_to_checked" | "burn_checked" => std::option::Option::Some(2), + "transfer_checked_with_fee" => std::option::Option::Some(3), + "withdraw_withheld_tokens_from_mint" | "withdraw_withheld_tokens_from_accounts" => { + std::option::Option::Some(2) + }, + "set_transfer_fee" | "update_default_account_state" => std::option::Option::Some(1), + "enable_required_transfer_memos" + | "disable_required_transfer_memos" + | "enable_cpi_guard" + | "disable_cpi_guard" => std::option::Option::Some(1), + "update_token_metadata_field" + | "remove_token_metadata_key" + | "update_token_metadata_authority" + | "update_token_group_max_size" + | "update_token_group_authority" => std::option::Option::Some(1), + _ => std::option::Option::None, + }; +} + +fn account_role( + code: &str, + position: usize, + authority_index: std::option::Option, +) -> std::string::String { + if authority_index.is_some_and(|index| return position > index) { + return "multisig_signer".to_string(); + } + let role = match (code, position) { + ("initialize_mint", 0) | ("initialize_mint2", 0) => "mint", + ("initialize_mint", 1) => "rent_sysvar", + ("initialize_account", 0) | ("initialize_account2", 0) | ("initialize_account3", 0) => { + "token_account" + }, + ("initialize_account", 1) | ("initialize_account2", 1) | ("initialize_account3", 1) => { + "mint" + }, + ("initialize_account", 2) => "owner", + ("initialize_account", 3) | ("initialize_account2", 2) => "rent_sysvar", + ("initialize_multisig", 0) | ("initialize_multisig2", 0) => "multisig_account", + ("initialize_multisig", 1) => "rent_sysvar", + ("initialize_multisig", _) | ("initialize_multisig2", _) => "multisig_member", + ("transfer", 0) | ("transfer_checked", 0) => "source_token_account", + ("transfer", 1) => "destination_token_account", + ("transfer_checked", 1) => "mint", + ("transfer_checked", 2) => "destination_token_account", + ("approve", 0) | ("approve_checked", 0) | ("revoke", 0) => "source_token_account", + ("approve", 1) | ("approve_checked", 2) => "delegate", + ("approve_checked", 1) => "mint", + ("mint_to", 0) | ("mint_to_checked", 0) => "mint", + ("mint_to", 1) | ("mint_to_checked", 1) => "destination_token_account", + ("burn", 0) | ("burn_checked", 0) => "source_token_account", + ("burn", 1) | ("burn_checked", 1) => "mint", + ("close_account", 0) | ("withdraw_excess_lamports", 0) | ("unwrap_lamports", 0) => { + "source_token_account" + }, + ("close_account", 1) | ("withdraw_excess_lamports", 1) | ("unwrap_lamports", 1) => { + "destination_account" + }, + ("freeze_account", 0) | ("thaw_account", 0) => "token_account", + ("freeze_account", 1) | ("thaw_account", 1) => "mint", + ("set_authority", 0) => "authority_target", + ("sync_native", 0) | ("initialize_immutable_owner", 0) => "token_account", + ("sync_native", 1) => "rent_sysvar", + ("get_account_data_size", 0) | ("amount_to_ui_amount", 0) | ("ui_amount_to_amount", 0) => { + "mint" + }, + ("initialize_transfer_fee_config", 0) + | ("initialize_default_account_state", 0) + | ("update_default_account_state", 0) + | ("set_transfer_fee", 0) + | ("harvest_withheld_tokens_to_mint", 0) => "mint", + ("transfer_checked_with_fee", 0) => "source_token_account", + ("transfer_checked_with_fee", 1) => "mint", + ("transfer_checked_with_fee", 2) => "destination_token_account", + ("withdraw_withheld_tokens_from_mint", 0) => "mint", + ("withdraw_withheld_tokens_from_mint", 1) => "destination_token_account", + ("withdraw_withheld_tokens_from_accounts", 0) => "mint", + ("withdraw_withheld_tokens_from_accounts", 1) => "destination_token_account", + ("withdraw_withheld_tokens_from_accounts", _) => "source_or_multisig_account", + ("harvest_withheld_tokens_to_mint", _) => "source_token_account", + ("enable_required_transfer_memos", 0) + | ("disable_required_transfer_memos", 0) + | ("enable_cpi_guard", 0) + | ("disable_cpi_guard", 0) => "token_account", + ("initialize_token_metadata", 0) + | ("update_token_metadata_field", 0) + | ("remove_token_metadata_key", 0) + | ("update_token_metadata_authority", 0) + | ("emit_token_metadata", 0) => "metadata", + ("initialize_token_metadata", 1) => "update_authority", + ("initialize_token_metadata", 2) => "mint", + ("initialize_token_metadata", 3) => "mint_authority", + ("initialize_token_group", 0) + | ("update_token_group_max_size", 0) + | ("update_token_group_authority", 0) + | ("initialize_token_group_member", 3) => "group", + ("initialize_token_group", 1) => "mint", + ("initialize_token_group", 2) => "mint_authority", + ("initialize_token_group_member", 0) => "member", + ("initialize_token_group_member", 1) => "member_mint", + ("initialize_token_group_member", 2) => "member_mint_authority", + ("initialize_token_group_member", 4) => "group_update_authority", + (_, value) if authority_index == std::option::Option::Some(value) => "authority", + _ => "unresolved", + }; + return role.to_string(); +} + +fn semantic_diagnostics( + code: &str, + parameters: serde_json::Value, + accounts: &[ResolvedAccount], + suffix_length: usize, +) -> serde_json::Value { + let mut values = std::vec::Vec::new(); + if suffix_length > 0 { + values.push(serde_json::json!({ + "code": "ignored_wire_suffix", + "lengthBytes": suffix_length, + "runtimeSemantics": "official_interface_unpack_ignores_suffix_for_this_variant", + })); + } + let valid_count = valid_account_count(code, accounts.len()); + if !valid_count { + values.push(serde_json::json!({ + "code": "unexpected_account_count", + "provided": accounts.len(), + })); + } + if matches!(code, "initialize_multisig" | "initialize_multisig2") { + let member_start = if code == "initialize_multisig" { 2 } else { 1 }; + let n = accounts.len().saturating_sub(member_start); + let m = parameters.get("m").and_then(serde_json::Value::as_u64).unwrap_or(0); + if !(1..=11).contains(&n) || m == 0 || m > n as u64 { + values.push(serde_json::json!({ + "code": "invalid_multisig_threshold", + "m": m, + "n": n, + "minSigners": 1, + "maxSigners": 11, + })); + } + } + let authority_index = authority_index(code); + if let std::option::Option::Some(index) = authority_index { + if let std::option::Option::Some(authority) = accounts.get(index) { + let multisig_form = accounts.len() > index.saturating_add(1); + if !multisig_form && !authority.signer { + values.push(serde_json::json!({"code":"missing_single_authority_signature"})); + } + if multisig_form { + let invalid_signers = accounts + .iter() + .skip(index.saturating_add(1)) + .filter(|account| return !account.signer) + .map(|account| return account.position) + .collect::>(); + if !invalid_signers.is_empty() { + values.push(serde_json::json!({ + "code": "multisig_meta_missing_signature", + "positions": invalid_signers, + })); + } + } + } + } + return serde_json::Value::Array(values); +} + +fn valid_account_count(code: &str, count: usize) -> bool { + return match code { + "initialize_mint" => count == 2, + "initialize_mint2" => count == 1, + "initialize_account" => count == 4, + "initialize_account2" => count == 3, + "initialize_account3" => count == 2, + "initialize_multisig" => (3..=13).contains(&count), + "initialize_multisig2" => (2..=12).contains(&count), + "transfer" + | "approve" + | "mint_to" + | "burn" + | "close_account" + | "freeze_account" + | "thaw_account" + | "withdraw_excess_lamports" + | "unwrap_lamports" => count >= 3, + "revoke" | "set_authority" => count >= 2, + "transfer_checked" | "approve_checked" => count >= 4, + "mint_to_checked" | "burn_checked" => count >= 3, + "sync_native" => count == 1 || count == 2, + "get_account_data_size" + | "initialize_immutable_owner" + | "amount_to_ui_amount" + | "ui_amount_to_amount" => count == 1, + "initialize_transfer_fee_config" | "initialize_default_account_state" => count == 1, + "transfer_checked_with_fee" => count >= 4, + "withdraw_withheld_tokens_from_mint" => count >= 3, + "withdraw_withheld_tokens_from_accounts" => count >= 4, + "harvest_withheld_tokens_to_mint" => count >= 2, + "set_transfer_fee" + | "update_default_account_state" + | "enable_required_transfer_memos" + | "disable_required_transfer_memos" + | "enable_cpi_guard" + | "disable_cpi_guard" => count >= 2, + "batch" => true, + _ => false, + }; +} + +#[allow(clippy::too_many_arguments)] +fn observation( + input: &kb_store_core::CoreInstructionReplayInput, + code: &str, + tag: u8, + parameters: serde_json::Value, + accounts: serde_json::Value, + authority: serde_json::Value, + semantic_diagnostics: serde_json::Value, + wire: &[u8], + suffix_length: usize, + committed: bool, + event_key: std::string::String, + instruction_path: std::string::String, + family: kb_model::EventFamily, +) -> kb_decoder_api::DecodedObservation { + let wire_hash = hash(wire); + let event = kb_model::DecodedProtocolEvent { + signature: kb_model::Signature(input.signature.clone()), + slot: kb_model::Slot(input.slot), + instruction_path: kb_model::InstructionPath(instruction_path.clone()), + program_id: kb_model::ProgramId(input.program_id.clone()), + protocol_code: kb_model::ProtocolCode(crate::SURFACE_CODE.to_string()), + surface_code: kb_model::SurfaceCode(crate::SURFACE_CODE.to_string()), + event_code: kb_model::EventCode(format!("{}.{}", crate::SURFACE_CODE, code)), + event_name: kb_model::EventName(code.to_string()), + event_family: family, + source_kind: if input.instruction_path.contains('/') { + kb_model::EventSourceKind::InnerInstruction + } else { + kb_model::EventSourceKind::Instruction + }, + confidence: kb_model::DecoderConfidence::ManualExact, + }; + return kb_decoder_api::DecodedObservation { + event_key, + event, + payload_json: serde_json::json!({ + "eventVersion": crate::TOKEN_EVENT_VERSION, + "programId": input.program_id, + "instruction": code, + "wireTag": tag, + "wireTagHex": format!("{tag:02x}"), + "instructionPath": instruction_path, + "instructionLocation": if input.instruction_path.contains('/') { "inner" } else { "outer" }, + "transactionSucceeded": !input.transaction_failed, + "committed": committed, + "parameters": parameters, + "accounts": accounts, + "authority": authority, + "semanticDiagnostics": semantic_diagnostics, + "wire": { + "lengthBytes": wire.len(), + "sha256": wire_hash, + "prefixHex": hex_prefix(wire), + "suffixLengthBytes": suffix_length, + "complete": true, + }, + "inference": { + "mintFromCoreBalanceChanges": serde_json::Value::Null, + "mintInvented": false, + "rpcAccountReadUsed": false, + }, + "returnData": { + "availableInCoreReplayInput": false, + "validated": false, + }, + }), + transaction_failed: input.transaction_failed, + transaction_error: input.transaction_err_json.clone(), + observation_committed: committed, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::Manual, + confidence: kb_model::DecoderConfidence::ManualExact, + evidence: std::vec![ + "spl_token_interface_3_0_0_wire_audit".to_string(), + format!("wire_tag:{tag}"), + format!("wire_sha256:{wire_hash}"), + ], + }, + }; +} + +fn batch_observations( + input: &kb_store_core::CoreInstructionReplayInput, + bytes: &[u8], + accounts: &[ResolvedAccount], +) -> std::result::Result< + (std::vec::Vec, serde_json::Value), + std::string::String, +> { + if bytes.len() == 1 { + return std::result::Result::Err("Batch requires at least one sub-instruction".to_string()); + } + let mut cursor = 1_usize; + let mut account_cursor = 0_usize; + let mut index = 0_usize; + let mut observations = std::vec::Vec::new(); + let mut manifest = std::vec::Vec::new(); + while cursor < bytes.len() { + if index >= crate::MAX_BATCH_INSTRUCTIONS { + return std::result::Result::Err(format!( + "Batch exceeds {} sub-instructions", + crate::MAX_BATCH_INSTRUCTIONS + )); + } + let account_count = match bytes.get(cursor).copied() { + std::option::Option::Some(value) => value as usize, + std::option::Option::None => { + return std::result::Result::Err(format!( + "Batch entry {index} has no account count" + )); + }, + }; + let data_length = match bytes.get(cursor.saturating_add(1)).copied() { + std::option::Option::Some(value) => value as usize, + std::option::Option::None => { + return std::result::Result::Err(format!("Batch entry {index} has no data length")); + }, + }; + if data_length == 0 { + return std::result::Result::Err(format!("Batch entry {index} has empty data")); + } + let data_start = cursor.saturating_add(2); + let data_end = data_start.saturating_add(data_length); + let child_wire = match bytes.get(data_start..data_end) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!( + "Batch entry {index} data length exceeds payload" + )); + }, + }; + let account_end = account_cursor.saturating_add(account_count); + if account_end > crate::MAX_BATCH_ACCOUNTS || account_end > accounts.len() { + return std::result::Result::Err(format!( + "Batch entry {index} account slice exceeds provided accounts or bound" + )); + } + let parsed = match parse_instruction(child_wire, true) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => { + return std::result::Result::Err(format!("Batch entry {index}: {message}")); + }, + }; + let child_accounts = &accounts[account_cursor..account_end]; + let suffix_length = child_wire.len().saturating_sub(parsed.consumed); + observations.push(observation( + input, + parsed.code, + parsed.tag, + parsed.parameters.clone(), + project_accounts(parsed.code, child_accounts), + authority_projection(parsed.code, child_accounts), + semantic_diagnostics(parsed.code, parsed.parameters, child_accounts, suffix_length), + child_wire, + suffix_length, + !input.transaction_failed, + format!("token_2022:batch:{index}"), + format!("{}/batch/{index}", input.instruction_path), + event_family(parsed.code), + )); + manifest.push(serde_json::json!({ + "position": index, + "instruction": parsed.code, + "wireTag": parsed.tag, + "accountStart": account_cursor, + "accountCount": account_count, + "dataOffset": data_start, + "dataLength": data_length, + "derivedInstructionPath": format!("{}/batch/{index}", input.instruction_path), + })); + cursor = data_end; + account_cursor = account_end; + index = index.saturating_add(1); + } + if account_cursor != accounts.len() { + return std::result::Result::Err(format!( + "Batch consumed {account_cursor} of {} provided accounts", + accounts.len() + )); + } + return std::result::Result::Ok(( + observations, + serde_json::json!({ + "subInstructionCount": manifest.len(), + "subInstructions": manifest, + "nestedBatchAllowed": false, + }), + )); +} + +fn event_family(code: &str) -> kb_model::EventFamily { + return match code { + "set_authority" + | "initialize_multisig" + | "initialize_multisig2" + | "initialize_permanent_delegate" + | "initialize_interest_bearing_mint" + | "update_interest_bearing_rate" + | "update_token_metadata_authority" + | "update_token_group_authority" + | "update_token_group_max_size" => kb_model::EventFamily::Admin, + "initialize_mint" + | "initialize_mint2" + | "mint_to" + | "mint_to_checked" + | "initialize_non_transferable_mint" => kb_model::EventFamily::TokenMint, + "burn" | "burn_checked" => kb_model::EventFamily::TokenBurn, + "initialize_transfer_fee_config" + | "set_transfer_fee" + | "withdraw_withheld_tokens_from_mint" + | "withdraw_withheld_tokens_from_accounts" + | "harvest_withheld_tokens_to_mint" + | "transfer_checked_with_fee" + | "initialize_confidential_transfer_fee_config" + | "withdraw_confidential_withheld_tokens_from_mint" + | "withdraw_confidential_withheld_tokens_from_accounts" + | "harvest_confidential_withheld_tokens_to_mint" + | "enable_confidential_harvest_to_mint" + | "disable_confidential_harvest_to_mint" + | "transfer_confidential_tokens_with_fee" => kb_model::EventFamily::Fee, + "initialize_default_account_state" + | "update_default_account_state" + | "enable_required_transfer_memos" + | "disable_required_transfer_memos" + | "enable_cpi_guard" + | "disable_cpi_guard" => kb_model::EventFamily::Audit, + "initialize_token_metadata" + | "update_token_metadata_field" + | "remove_token_metadata_key" + | "emit_token_metadata" + | "initialize_token_group" + | "initialize_token_group_member" => kb_model::EventFamily::Metadata, + "batch" | "get_account_data_size" | "amount_to_ui_amount" | "ui_amount_to_amount" => { + kb_model::EventFamily::Audit + }, + _ => kb_model::EventFamily::TokenAccount, + }; +} + +fn hash(bytes: &[u8]) -> std::string::String { + let digest = sha2::Sha256::digest(bytes); + let mut output = std::string::String::with_capacity(64); + for byte in digest { + output.push_str(format!("{byte:02x}").as_str()); + } + return output; +} + +fn hex_prefix(bytes: &[u8]) -> std::string::String { + let length = std::cmp::min(bytes.len(), crate::DIAGNOSTIC_PREFIX_BYTES); + let mut output = std::string::String::with_capacity(length.saturating_mul(2)); + for byte in &bytes[..length] { + output.push_str(format!("{byte:02x}").as_str()); + } + return output; +} + +fn failed( + entry_code: std::option::Option<&str>, + code: &str, + message: std::string::String, +) -> kb_decoder_api::DecoderExecutionResult { + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Failed, + recognized_entry_code: entry_code.map(str::to_string), + observations: std::vec::Vec::new(), + diagnostics: std::vec![kb_decoder_api::DecoderDiagnostic { + code: code.to_string(), + message, + retriable: false, + }], + }; +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/Cargo.toml new file mode 100644 index 0000000..adcfd86 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_stable_swap_hylo_exchange/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_stable_swap_hylo_exchange" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/README.md b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/README.md new file mode 100644 index 0000000..10776f6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_stable_swap_hylo_exchange + +Ce crate réserve la surface de décodage `stable_swap_hylo_exchange`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `stable_swap_hylo_exchange` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/src/constants.rs new file mode 100644 index 0000000..a0c4228 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_stable_swap_hylo_exchange/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_stable_swap_hylo_exchange` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_stable_swap_hylo_exchange"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/src/decoder.rs new file mode 100644 index 0000000..8e8dbc1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_stable_swap_hylo_exchange/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_stable_swap_hylo_exchange` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct StableSwapHyloExchangeDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::StableSwapHyloExchangeDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_stable_swap_hylo_exchange"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::STABLE_SWAP_HYLO_EXCHANGE_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/src/lib.rs new file mode 100644 index 0000000..ba5c63e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_hylo_exchange/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_stable_swap_hylo_exchange/src/lib.rs +// version: 2 + +//! Decoder crate for `stable_swap_hylo_exchange`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::StableSwapHyloExchangeDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/Cargo.toml new file mode 100644 index 0000000..fb8ae97 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_stable_swap_jupiter_stable/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_stable_swap_jupiter_stable" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/README.md b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/README.md new file mode 100644 index 0000000..bd90a32 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_stable_swap_jupiter_stable + +Ce crate réserve la surface de décodage `stable_swap_jupiter_stable`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `stable_swap_jupiter_stable` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/src/constants.rs new file mode 100644 index 0000000..b76102a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_stable_swap_jupiter_stable/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_stable_swap_jupiter_stable` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_stable_swap_jupiter_stable"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/src/decoder.rs new file mode 100644 index 0000000..dadd4c7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_stable_swap_jupiter_stable/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_stable_swap_jupiter_stable` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct StableSwapJupiterStableDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::StableSwapJupiterStableDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_stable_swap_jupiter_stable"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::STABLE_SWAP_JUPITER_STABLE_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/src/lib.rs new file mode 100644 index 0000000..b1854bb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_jupiter_stable/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_stable_swap_jupiter_stable/src/lib.rs +// version: 2 + +//! Decoder crate for `stable_swap_jupiter_stable`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::StableSwapJupiterStableDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/Cargo.toml new file mode 100644 index 0000000..d9ff2f7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_stable_swap_numeraire/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_stable_swap_numeraire" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/README.md b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/README.md new file mode 100644 index 0000000..bbd7795 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_stable_swap_numeraire + +Ce crate réserve la surface de décodage `stable_swap_numeraire`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `stable_swap_numeraire` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/src/constants.rs new file mode 100644 index 0000000..eee38dd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_stable_swap_numeraire/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_stable_swap_numeraire` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_stable_swap_numeraire"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/src/decoder.rs new file mode 100644 index 0000000..95d2247 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_stable_swap_numeraire/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_stable_swap_numeraire` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct StableSwapNumeraireDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::StableSwapNumeraireDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_stable_swap_numeraire"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::STABLE_SWAP_NUMERAIRE_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/src/lib.rs new file mode 100644 index 0000000..3eff862 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_numeraire/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_stable_swap_numeraire/src/lib.rs +// version: 2 + +//! Decoder crate for `stable_swap_numeraire`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::StableSwapNumeraireDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/Cargo.toml new file mode 100644 index 0000000..1652f2c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_stable_swap_stabble/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_stable_swap_stabble" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/README.md b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/README.md new file mode 100644 index 0000000..f56473c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_stable_swap_stabble + +Ce crate réserve la surface de décodage `stable_swap_stabble`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `stable_swap_stabble` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/src/constants.rs new file mode 100644 index 0000000..2279cf9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_stable_swap_stabble/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_stable_swap_stabble` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_stable_swap_stabble"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/src/decoder.rs new file mode 100644 index 0000000..576ff23 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_stable_swap_stabble/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_stable_swap_stabble` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct StableSwapStabbleDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::StableSwapStabbleDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_stable_swap_stabble"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::STABLE_SWAP_STABBLE_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/src/lib.rs new file mode 100644 index 0000000..34afa85 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_stable_swap_stabble/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_stable_swap_stabble/src/lib.rs +// version: 2 + +//! Decoder crate for `stable_swap_stabble`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::StableSwapStabbleDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/Cargo.toml new file mode 100644 index 0000000..5fcd291 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_staking_kamino_farm/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_staking_kamino_farm" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/README.md b/migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/README.md new file mode 100644 index 0000000..c079589 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_staking_kamino_farm + +Ce crate réserve la surface de décodage `staking_kamino_farm`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `staking_kamino_farm` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/src/constants.rs new file mode 100644 index 0000000..67d3aac --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_staking_kamino_farm/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_staking_kamino_farm` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_staking_kamino_farm"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/src/decoder.rs new file mode 100644 index 0000000..489e1ee --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_staking_kamino_farm/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_staking_kamino_farm` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct StakingKaminoFarmDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::StakingKaminoFarmDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_staking_kamino_farm"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::STAKING_KAMINO_FARM_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/src/lib.rs new file mode 100644 index 0000000..c66d96a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_staking_kamino_farm/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_staking_kamino_farm/src/lib.rs +// version: 2 + +//! Decoder crate for `staking_kamino_farm`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::StakingKaminoFarmDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/Cargo.toml new file mode 100644 index 0000000..65a65d4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_staking_marinade_finance/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_staking_marinade_finance" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/README.md b/migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/README.md new file mode 100644 index 0000000..4b71eec --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_staking_marinade_finance + +Ce crate réserve la surface de décodage `staking_marinade_finance`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `staking_marinade_finance` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/src/constants.rs new file mode 100644 index 0000000..0a85f69 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_staking_marinade_finance/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_staking_marinade_finance` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_staking_marinade_finance"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/src/decoder.rs new file mode 100644 index 0000000..3f286de --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_staking_marinade_finance/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_staking_marinade_finance` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct StakingMarinadeFinanceDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::StakingMarinadeFinanceDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_staking_marinade_finance"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::STAKING_MARINADE_FINANCE_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/src/lib.rs new file mode 100644 index 0000000..6a68f33 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_staking_marinade_finance/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_staking_marinade_finance/src/lib.rs +// version: 2 + +//! Decoder crate for `staking_marinade_finance`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::StakingMarinadeFinanceDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/Cargo.toml new file mode 100644 index 0000000..de64928 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_staking_solayer/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_staking_solayer" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/README.md b/migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/README.md new file mode 100644 index 0000000..9a7eef0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_staking_solayer + +Ce crate réserve la surface de décodage `staking_solayer`. Aucun `program_id` local n'est encore fixé dans ce squelette. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `staking_solayer` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/src/constants.rs new file mode 100644 index 0000000..f4452ad --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_staking_solayer/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_staking_solayer` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_staking_solayer"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/src/decoder.rs new file mode 100644 index 0000000..fc867a8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_staking_solayer/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_staking_solayer` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct StakingSolayerDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::StakingSolayerDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_staking_solayer"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::STAKING_SOLAYER_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/src/lib.rs new file mode 100644 index 0000000..c5ad1c9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_staking_solayer/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_staking_solayer/src/lib.rs +// version: 2 + +//! Decoder crate for `staking_solayer`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::StakingSolayerDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/Cargo.toml new file mode 100644 index 0000000..edb9e46 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_treasury_helium_treasury_management/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_treasury_helium_treasury_management" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/README.md b/migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/README.md new file mode 100644 index 0000000..6e748f1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_treasury_helium_treasury_management + +Ce crate réserve la surface de décodage `treasury_helium_treasury_management`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `treasury_helium_treasury_management` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/src/constants.rs new file mode 100644 index 0000000..6b9407e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_treasury_helium_treasury_management/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_treasury_helium_treasury_management` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_treasury_helium_treasury_management"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/src/decoder.rs new file mode 100644 index 0000000..7aac790 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_treasury_helium_treasury_management/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_treasury_helium_treasury_management` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct TreasuryHeliumTreasuryManagementDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::TreasuryHeliumTreasuryManagementDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_treasury_helium_treasury_management"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::TREASURY_HELIUM_TREASURY_MANAGEMENT_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/src/lib.rs new file mode 100644 index 0000000..6bfe0d7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_treasury_helium_treasury_management/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_treasury_helium_treasury_management/src/lib.rs +// version: 2 + +//! Decoder crate for `treasury_helium_treasury_management`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::TreasuryHeliumTreasuryManagementDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/Cargo.toml new file mode 100644 index 0000000..6f1c26e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_vault_carrot_defi/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_vault_carrot_defi" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/README.md b/migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/README.md new file mode 100644 index 0000000..b81ef0c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_vault_carrot_defi + +Ce crate réserve la surface de décodage `vault_carrot_defi`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `vault_carrot_defi` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/src/constants.rs new file mode 100644 index 0000000..cf48280 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_vault_carrot_defi/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_vault_carrot_defi` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_vault_carrot_defi"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/src/decoder.rs new file mode 100644 index 0000000..66114cd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_vault_carrot_defi/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_vault_carrot_defi` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct VaultCarrotDefiDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::VaultCarrotDefiDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_vault_carrot_defi"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::VAULT_CARROT_DEFI_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/src/lib.rs new file mode 100644 index 0000000..df2d62e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_carrot_defi/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_vault_carrot_defi/src/lib.rs +// version: 2 + +//! Decoder crate for `vault_carrot_defi`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::VaultCarrotDefiDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/Cargo.toml new file mode 100644 index 0000000..f1ca1ca --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_vault_hylo_stability_pool/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_vault_hylo_stability_pool" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/README.md b/migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/README.md new file mode 100644 index 0000000..e0e4f98 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_vault_hylo_stability_pool + +Ce crate réserve la surface de décodage `vault_hylo_stability_pool`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `vault_hylo_stability_pool` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/src/constants.rs new file mode 100644 index 0000000..3061fed --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_vault_hylo_stability_pool/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_vault_hylo_stability_pool` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_vault_hylo_stability_pool"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/src/decoder.rs new file mode 100644 index 0000000..c5e149e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_vault_hylo_stability_pool/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_vault_hylo_stability_pool` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct VaultHyloStabilityPoolDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::VaultHyloStabilityPoolDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_vault_hylo_stability_pool"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::VAULT_HYLO_STABILITY_POOL_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/src/lib.rs new file mode 100644 index 0000000..c84de99 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_hylo_stability_pool/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_vault_hylo_stability_pool/src/lib.rs +// version: 2 + +//! Decoder crate for `vault_hylo_stability_pool`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::VaultHyloStabilityPoolDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/Cargo.toml new file mode 100644 index 0000000..812498e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_vault_kamino/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_vault_kamino" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/README.md b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/README.md new file mode 100644 index 0000000..617aed0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_vault_kamino + +Ce crate réserve la surface de décodage `vault_kamino`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `vault_kamino` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/src/constants.rs new file mode 100644 index 0000000..d979067 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_vault_kamino/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_vault_kamino` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_vault_kamino"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/src/decoder.rs new file mode 100644 index 0000000..0223be9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_vault_kamino/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_vault_kamino` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct VaultKaminoDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::VaultKaminoDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_vault_kamino"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::VAULT_KAMINO_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/src/lib.rs new file mode 100644 index 0000000..ccf9456 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_vault_kamino/src/lib.rs +// version: 2 + +//! Decoder crate for `vault_kamino`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::VaultKaminoDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/Cargo.toml new file mode 100644 index 0000000..30cf535 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_vault_kamino_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_vault_kamino_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/README.md b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/README.md new file mode 100644 index 0000000..390f155 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_vault_kamino_v2 + +Ce crate réserve la surface de décodage `vault_kamino_v2`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `vault_kamino_v2` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/src/constants.rs new file mode 100644 index 0000000..5880cfe --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_vault_kamino_v2/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_vault_kamino_v2` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_vault_kamino_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/src/decoder.rs new file mode 100644 index 0000000..74a4a93 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_vault_kamino_v2/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_vault_kamino_v2` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct VaultKaminoV2Decoder; + +impl kb_decoder_api::ProtocolDecoder for crate::VaultKaminoV2Decoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_vault_kamino_v2"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::VAULT_KAMINO_V2_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/src/lib.rs new file mode 100644 index 0000000..f0fea2f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_v2/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_vault_kamino_v2/src/lib.rs +// version: 2 + +//! Decoder crate for `vault_kamino_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::VaultKaminoV2Decoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/Cargo.toml new file mode 100644 index 0000000..4ae3301 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_vault_kamino_yvaults/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_vault_kamino_yvaults" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/README.md b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/README.md new file mode 100644 index 0000000..86b8c54 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_vault_kamino_yvaults + +Ce crate réserve la surface de décodage `vault_kamino_yvaults`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `vault_kamino_yvaults` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/src/constants.rs new file mode 100644 index 0000000..e38076a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_vault_kamino_yvaults/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_vault_kamino_yvaults` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_vault_kamino_yvaults"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/src/decoder.rs new file mode 100644 index 0000000..3187aa1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_vault_kamino_yvaults/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_vault_kamino_yvaults` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct VaultKaminoYvaultsDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::VaultKaminoYvaultsDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_vault_kamino_yvaults"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::VAULT_KAMINO_YVAULTS_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/src/lib.rs new file mode 100644 index 0000000..62937b5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_kamino_yvaults/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_vault_kamino_yvaults/src/lib.rs +// version: 2 + +//! Decoder crate for `vault_kamino_yvaults`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::VaultKaminoYvaultsDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/Cargo.toml new file mode 100644 index 0000000..5402cd7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_vault_meteora/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_vault_meteora" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/README.md b/migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/README.md new file mode 100644 index 0000000..2e48422 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/README.md @@ -0,0 +1,17 @@ + + + +# kb_decoder_vault_meteora + +Ce crate décode la surface protocolaire `meteora_vault` en événements protocolaires normalisés. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/src/constants.rs new file mode 100644 index 0000000..76ef8da --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_vault_meteora/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_vault_meteora` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_vault_meteora"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/src/decoder.rs new file mode 100644 index 0000000..ee3d7f5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/src/decoder.rs @@ -0,0 +1,42 @@ +// file: kb_decoder_vault_meteora/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_vault_meteora` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct VaultMeteoraDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::VaultMeteoraDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_vault_meteora"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::VAULT_METEORA_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if kb_decoder_api::ProtocolDecoder::handles_program_id(self, &observation.program_id) + { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/src/lib.rs new file mode 100644 index 0000000..a07f697 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vault_meteora/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_vault_meteora/src/lib.rs +// version: 2 + +//! Decoder crate for `vault_meteora`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Initial decoder implementation placeholder. +pub use crate::decoder::VaultMeteoraDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/Cargo.toml new file mode 100644 index 0000000..c649ba7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_vesting_streamflow/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_vesting_streamflow" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/README.md b/migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/README.md new file mode 100644 index 0000000..d74f5dc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_vesting_streamflow + +Ce crate réserve la surface de décodage `vesting_streamflow`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `vesting_streamflow` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/src/constants.rs new file mode 100644 index 0000000..db762d6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_vesting_streamflow/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_vesting_streamflow` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_vesting_streamflow"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/src/decoder.rs new file mode 100644 index 0000000..df81013 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_vesting_streamflow/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_vesting_streamflow` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct VestingStreamflowDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::VestingStreamflowDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_vesting_streamflow"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::VESTING_STREAMFLOW_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/src/lib.rs new file mode 100644 index 0000000..577e737 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_vesting_streamflow/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_vesting_streamflow/src/lib.rs +// version: 2 + +//! Decoder crate for `vesting_streamflow`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::VestingStreamflowDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/Cargo.toml new file mode 100644 index 0000000..8a550a4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_wallet_jupiter_apepro_smart_wallet/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_wallet_jupiter_apepro_smart_wallet" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/README.md b/migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/README.md new file mode 100644 index 0000000..d2a7195 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_wallet_jupiter_apepro_smart_wallet + +Ce crate réserve la surface de décodage `wallet_jupiter_apepro_smart_wallet`. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `wallet_jupiter_apepro_smart_wallet` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/src/constants.rs new file mode 100644 index 0000000..de48c7e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_wallet_jupiter_apepro_smart_wallet/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_wallet_jupiter_apepro_smart_wallet` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_wallet_jupiter_apepro_smart_wallet"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/src/decoder.rs new file mode 100644 index 0000000..0d6031e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_wallet_jupiter_apepro_smart_wallet/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_wallet_jupiter_apepro_smart_wallet` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct WalletJupiterApeproSmartWalletDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::WalletJupiterApeproSmartWalletDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_wallet_jupiter_apepro_smart_wallet"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::WALLET_JUPITER_APEPRO_SMART_WALLET_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/src/lib.rs new file mode 100644 index 0000000..5140f64 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_wallet_jupiter_apepro_smart_wallet/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_wallet_jupiter_apepro_smart_wallet/src/lib.rs +// version: 2 + +//! Decoder crate for `wallet_jupiter_apepro_smart_wallet`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::WalletJupiterApeproSmartWalletDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/Cargo.toml b/migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/Cargo.toml new file mode 100644 index 0000000..576a1dc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_decoder_weighted_swap_stabble/Cargo.toml +# version: 2 + +[package] +name = "kb_decoder_weighted_swap_stabble" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/README.md b/migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/README.md new file mode 100644 index 0000000..78a0469 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/README.md @@ -0,0 +1,14 @@ + + + +# kb_decoder_weighted_swap_stabble + +Ce crate réserve la surface de décodage `weighted_swap_stabble`. Le ou les `program_id` initiaux proviennent des IDL locales. + +## Rôle dans l'écosystème + +Ce module transforme les observations Solana liées à `weighted_swap_stabble` en événements protocolaires normalisés. Il ne doit pas accéder directement à PostgreSQL, au RPC, au wallet, à Tauri ou aux matérialisateurs. + +## État initial + +Le crate est créé comme point d'ancrage. Le décodage effectif, les discriminants, les IDL et les tests de couverture seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/src/constants.rs b/migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/src/constants.rs new file mode 100644 index 0000000..69ecc2f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_decoder_weighted_swap_stabble/src/constants.rs +// version: 2 + +//! Local constants for the `kb_decoder_weighted_swap_stabble` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_decoder_weighted_swap_stabble"; diff --git a/migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/src/decoder.rs b/migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/src/decoder.rs new file mode 100644 index 0000000..10874b3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/src/decoder.rs @@ -0,0 +1,41 @@ +// file: kb_decoder_weighted_swap_stabble/src/decoder.rs +// version: 5 + +//! Decoder scaffold for the `kb_decoder_weighted_swap_stabble` crate. + +/// Initial decoder implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct WeightedSwapStabbleDecoder; + +impl kb_decoder_api::ProtocolDecoder for crate::WeightedSwapStabbleDecoder { + fn decoder_name(&self) -> &'static str { + return "kb_decoder_weighted_swap_stabble"; + } + + fn decoder_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::WEIGHTED_SWAP_STABBLE_PROGRAM_ID]; + } + + fn supports_observation( + &self, + observation: &kb_model::ProgramObservation, + ) -> kb_decoder_api::DecoderSupport { + return if self.handles_program_id(&observation.program_id) { + kb_decoder_api::DecoderSupport::Maybe + } else { + kb_decoder_api::DecoderSupport::No + }; + } + + fn decode_observation( + &self, + _observation: &kb_model::ProgramObservation, + ) -> kb_core::Result> { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(std::vec::Vec::new()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/src/lib.rs b/migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/src/lib.rs new file mode 100644 index 0000000..21592e5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_decoder_weighted_swap_stabble/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_decoder_weighted_swap_stabble/src/lib.rs +// version: 2 + +//! Decoder crate for `weighted_swap_stabble`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod decoder; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the decoder type implemented by this crate. +pub use crate::decoder::WeightedSwapStabbleDecoder; diff --git a/migration/khadhroony-bot2-reference/kb_execution_api/Cargo.toml b/migration/khadhroony-bot2-reference/kb_execution_api/Cargo.toml new file mode 100644 index 0000000..b37dcae --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_execution_api/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_execution_api/Cargo.toml +# version: 2 + +[package] +name = "kb_execution_api" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_model = { path = "../kb_model" } +serde.workspace = true +serde_json.workspace = true +ts-rs.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_execution_api/README.md b/migration/khadhroony-bot2-reference/kb_execution_api/README.md new file mode 100644 index 0000000..577f13a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_execution_api/README.md @@ -0,0 +1,101 @@ + + + +# kb_execution_api + +Cette crate définit les contrats communs de la couche d’exécution. Elle ne dépend ni du wallet, ni du RPC, ni du stockage, ni des décodeurs et ne construit aucune instruction Solana. + +## API publique + +### Capacités et traits + +| Export | Usage | +|-----------------------------------------|----------------------------------------------------------------------------------------------------------------------------| +| `ExecutionCapability` | Réponse exacte `Supported { operation_code }` ou `Unsupported { reason_code, reason }` pour un couple programme/opération. | +| `ExecutionCapability::supported(...)` | Construit une capacité supportée avec un code stable. | +| `ExecutionCapability::unsupported(...)` | Construit un refus documenté avec code et message. | +| `ExecutionCapability::is_supported()` | Teste la capacité sans perdre le diagnostic du variant complet. | +| `TypedInstructionExecutor` | Contrat actuel : expose `capability(...)` et `build_prepared_plan(...)` sans I/O, signature ou envoi. | +| `InstructionExecutor` | Pont historique fondé sur `ExecutionRequest`/`ExecutionPlan`; il reste disponible pour les crates réservées. | +| `ExecutionSupport` | Résultat historique `No`, `Maybe` ou `Yes`; les exécuteurs opérationnels doivent éviter `Maybe`. | + +### Politiques + +| Export | Usage | +|---------------------------------|-----------------------------------------------------------------------------------------------------------| +| `ExecutionPolicy` | Agrège cluster, simulation, blockhash/nonce, plafonds, signataires autorisés, dry-run et post-validation. | +| `ExecutionCluster` | Cluster attendu : Localnet, Devnet, Testnet ou Mainnet. | +| `ExecutionClusterPolicy` | Autorisation du cluster et double confirmation Mainnet. | +| `ExecutionSimulationPolicy` | Indique si la simulation est obligatoire. | +| `ExecutionBlockhashKind` | Sélectionne un recent blockhash ou un durable nonce. | +| `ExecutionBlockhashPolicy` | Porte l’âge maximal du blockhash ou le compte et l’autorité nonce. | +| `ExecutionCostLimit` | Plafonds de dépense, frais totaux et prix par compute unit. | +| `PostExecutionValidationPolicy` | Étapes exigées après confirmation : canonical insert, core extraction, decode replay et matérialisation. | + +Tous les types de politique implémentent des valeurs par défaut conservatrices : Devnet, simulation obligatoire, recent blockhash borné, dry-run actif et Mainnet désactivé. + +### Plan préparé + +| Export | Usage | +|-------------------------|-------------------------------------------------------------------------------------| +| `PreparedExecutionPlan` | Contrat immutable transmis de l’exécuteur à la sécurité puis à l’assembleur Solana. | +| `PlannedInstruction` | Program ID, code d’opération, comptes ordonnés et payload exact. | +| `PlannedAccount` | Public key et flags signer/writable d’un compte d’instruction. | +| `RequiredSigner` | Public key et rôle stable d’un signataire requis. | + +Un plan contient également l’exécuteur/version, l’identifiant d’intent, le fee payer, la politique, les lamports dépensés ou verrouillés et le prix Compute Budget demandé. + +### Résultats d’orchestration + +| Export | Usage | +|-------------------------------|-------------------------------------------------------------------------------------------------------------------| +| `ExecutionSimulationResult` | Preuve provider-neutral d’une simulation, avec contexte cluster/blockhash, frais, unités, logs et erreur runtime. | +| `ExecutionSendResult` | Signature acceptée par le RPC et slot de soumission éventuel. | +| `ExecutionConfirmationStatus` | État final ou intermédiaire de confirmation. | +| `ExecutionConfirmationResult` | Résultat borné des polls de confirmation. | +| `PostExecutionDiagnostic` | Résumé des étapes canonical/core/decode/materialization après exécution. | + +### Compatibilité et helpers JSON + +| Export | Usage | +|--------------------------------------|------------------------------------------------------------------------| +| `ExecutionRequest` | Requête historique `program_id + operation_code + payload_json`. | +| `ExecutionPlan` | Plan historique sérialisé, sans garantie d’être directement envoyable. | +| `serialize_payload_json(...)` | Sérialise un `serde_json::Value` compact avec `kb_core::Error`. | +| `serialize_payload_json_pretty(...)` | Sérialise le même payload en forme lisible. | + +## Exemple d’implémentation d’un exécuteur + +```rust +impl kb_execution_api::TypedInstructionExecutor for MyExecutor { + type Intent = MyIntent; + + fn capability( + &self, + program_id: &kb_model::ProgramId, + operation_code: &str, + ) -> kb_execution_api::ExecutionCapability { + // Return an exact Supported or Unsupported result. + } + + fn build_prepared_plan( + &self, + intent: &Self::Intent, + ) -> kb_core::Result { + // Validate and encode only. Do not access RPC or sign here. + } +} +``` + +## Frontières + +`kb_execution_api` ne décide pas qu’un plan est sûr, ne compile pas de message Solana et ne contacte aucun endpoint. Le flux attendu est : + +```text +executor intent + -> PreparedExecutionPlan + -> kb_execution_safety + -> kb_execution_solana + -> kb_rpc + -> post-execution orchestration +``` diff --git a/migration/khadhroony-bot2-reference/kb_execution_api/src/execution.rs b/migration/khadhroony-bot2-reference/kb_execution_api/src/execution.rs new file mode 100644 index 0000000..3776e71 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_execution_api/src/execution.rs @@ -0,0 +1,496 @@ +// file: kb_execution_api/src/execution.rs +// version: 7 + +//! Typed execution plans, policies and result contracts. + +use ts_rs::TS; // rust-rules: derive-import + +/// Exact capability returned for one program operation. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(tag = "status", rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionCapability.ts" +)] +pub enum ExecutionCapability { + /// The operation is implemented by the executor. + Supported { + /// Stable operation code implemented by the executor. + operation_code: std::string::String, + }, + /// The operation is not implemented or is rejected by design. + Unsupported { + /// Stable reason code. + reason_code: std::string::String, + /// Human-readable reason. + reason: std::string::String, + }, +} + +impl crate::ExecutionCapability { + /// Creates a supported capability value. + pub fn supported(operation_code: impl std::convert::Into) -> Self { + return Self::Supported { operation_code: operation_code.into() }; + } + + /// Creates an unsupported capability value. + pub fn unsupported( + reason_code: impl std::convert::Into, + reason: impl std::convert::Into, + ) -> Self { + return Self::Unsupported { + reason_code: reason_code.into(), + reason: reason.into(), + }; + } + + /// Returns whether the capability is explicitly supported. + pub fn is_supported(&self) -> bool { + return matches!(self, Self::Supported { operation_code: _ }); + } +} + +/// Solana cluster expected by an execution plan. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionCluster.ts" +)] +pub enum ExecutionCluster { + /// Local validator cluster. + Localnet, + /// Public development cluster. + Devnet, + /// Public test cluster. + Testnet, + /// Public production cluster. + /// + /// RPC URLs and CLI aliases may still use `mainnet-beta`. + Mainnet, +} + +#[derive(serde::Deserialize)] +#[serde(rename_all = "snake_case")] +enum ExecutionClusterWire { + Localnet, + Devnet, + Testnet, + #[serde(alias = "mainnet_beta")] + Mainnet, +} + +impl<'de> serde::Deserialize<'de> for crate::ExecutionCluster { + fn deserialize( + deserializer: Deserializer, + ) -> std::result::Result + where + Deserializer: serde::Deserializer<'de>, + { + let wire = match ::deserialize(deserializer) { + std::result::Result::Ok(wire) => wire, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(match wire { + ExecutionClusterWire::Localnet => Self::Localnet, + ExecutionClusterWire::Devnet => Self::Devnet, + ExecutionClusterWire::Testnet => Self::Testnet, + ExecutionClusterWire::Mainnet => Self::Mainnet, + }); + } +} + +/// Cluster restrictions attached to an execution plan. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionClusterPolicy.ts" +)] +pub struct ExecutionClusterPolicy { + /// Cluster that the RPC endpoint must report. + pub expected_cluster: crate::ExecutionCluster, + /// Whether production execution may proceed after all other checks. + pub allow_mainnet: bool, + /// Whether the operator explicitly confirmed this production execution. + pub mainnet_confirmation: bool, +} + +impl std::default::Default for crate::ExecutionClusterPolicy { + fn default() -> Self { + return Self { + expected_cluster: crate::ExecutionCluster::Devnet, + allow_mainnet: false, + mainnet_confirmation: false, + }; + } +} + +/// Simulation requirement attached to an execution plan. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionSimulationPolicy.ts" +)] +pub enum ExecutionSimulationPolicy { + /// Simulation must succeed before signing or sending. + Required, + /// Simulation is not required. Safety policy rejects this mode by default. + Optional, +} + +/// Blockhash source required when a transaction is assembled. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionBlockhashKind.ts" +)] +pub enum ExecutionBlockhashKind { + /// Fetch a recent blockhash and enforce a bounded age. + Latest, + /// Use a durable nonce account and authority. + DurableNonce, +} + +/// Blockhash or durable nonce policy attached to an execution plan. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionBlockhashPolicy.ts" +)] +pub struct ExecutionBlockhashPolicy { + /// Selected blockhash source. + pub kind: crate::ExecutionBlockhashKind, + /// Maximum accepted age for a recent blockhash, in slots. + pub max_age_slots: std::option::Option, + /// Durable nonce account when `kind` is `DurableNonce`. + pub nonce_account: std::option::Option, + /// Durable nonce authority when `kind` is `DurableNonce`. + pub nonce_authority: std::option::Option, +} + +impl std::default::Default for crate::ExecutionBlockhashPolicy { + fn default() -> Self { + return Self { + kind: crate::ExecutionBlockhashKind::Latest, + max_age_slots: std::option::Option::Some(150), + nonce_account: std::option::Option::None, + nonce_authority: std::option::Option::None, + }; + } +} + +/// Explicit cost ceilings attached to an execution plan. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionCostLimit.ts" +)] +pub struct ExecutionCostLimit { + /// Maximum lamports that operation instructions may transfer or lock. + pub max_spend_lamports: std::option::Option, + /// Maximum total transaction fee accepted after simulation. + pub max_fee_lamports: std::option::Option, + /// Maximum compute-unit price accepted, in micro-lamports per compute unit. + pub max_compute_unit_price_micro_lamports: std::option::Option, +} + +impl std::default::Default for crate::ExecutionCostLimit { + fn default() -> Self { + return Self { + max_spend_lamports: std::option::Option::None, + max_fee_lamports: std::option::Option::None, + max_compute_unit_price_micro_lamports: std::option::Option::None, + }; + } +} + +/// Post-execution replay validation policy. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/PostExecutionValidationPolicy.ts" +)] +pub struct PostExecutionValidationPolicy { + /// Whether the sent signature must be inserted into canonical storage. + pub canonical_insert_required: bool, + /// Whether core extraction must complete. + pub core_extraction_required: bool, + /// Whether contextual decode replay must complete. + pub decode_replay_required: bool, + /// Whether compatible materializers must run. + pub materialization_required: bool, +} + +impl std::default::Default for crate::PostExecutionValidationPolicy { + fn default() -> Self { + return Self { + canonical_insert_required: true, + core_extraction_required: true, + decode_replay_required: true, + materialization_required: false, + }; + } +} + +/// Conservative policy carried from intent creation to transaction assembly. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionPolicy.ts" +)] +pub struct ExecutionPolicy { + /// Cluster restrictions. + pub cluster: crate::ExecutionClusterPolicy, + /// Simulation requirement. + pub simulation: crate::ExecutionSimulationPolicy, + /// Blockhash source restrictions. + pub blockhash: crate::ExecutionBlockhashPolicy, + /// Explicit operation and fee ceilings. + pub cost_limit: crate::ExecutionCostLimit, + /// Public keys authorized to sign this plan. + pub authorized_signers: std::vec::Vec, + /// Whether the operation is restricted to plan construction and simulation. + pub dry_run: bool, + /// Required post-execution validation steps. + pub post_execution_validation: crate::PostExecutionValidationPolicy, +} + +impl std::default::Default for crate::ExecutionPolicy { + fn default() -> Self { + return Self { + cluster: crate::ExecutionClusterPolicy::default(), + simulation: crate::ExecutionSimulationPolicy::Required, + blockhash: crate::ExecutionBlockhashPolicy::default(), + cost_limit: crate::ExecutionCostLimit::default(), + authorized_signers: std::vec::Vec::new(), + dry_run: true, + post_execution_validation: crate::PostExecutionValidationPolicy::default(), + }; + } +} + +/// One account required by a planned instruction. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/PlannedAccount.ts" +)] +pub struct PlannedAccount { + /// Account public key. + pub pubkey: kb_model::Pubkey, + /// Whether the account must sign the transaction. + pub is_signer: bool, + /// Whether the account must be writable. + pub is_writable: bool, +} + +/// One fully encoded instruction in an execution plan. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/PlannedInstruction.ts" +)] +pub struct PlannedInstruction { + /// Target program id. + pub program_id: kb_model::ProgramId, + /// Stable operation code represented by the instruction. + pub operation_code: std::string::String, + /// Ordered account metadata. + pub accounts: std::vec::Vec, + /// Exact encoded instruction payload. + pub data: std::vec::Vec, +} + +/// Signer required before transaction assembly. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/RequiredSigner.ts" +)] +pub struct RequiredSigner { + /// Signer public key. + pub pubkey: kb_model::Pubkey, + /// Stable signer role such as `fee_payer`, `funding_account` or `new_account`. + pub role: std::string::String, +} + +/// Typed execution plan produced before transaction assembly. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/PreparedExecutionPlan.ts" +)] +pub struct PreparedExecutionPlan { + /// Stable executor crate name. + pub executor_name: std::string::String, + /// Executor package version. + pub executor_version: std::string::String, + /// Caller-provided intent identifier. + pub intent_id: std::string::String, + /// Stable operation code. + pub operation_code: std::string::String, + /// Transaction fee payer. + pub fee_payer: kb_model::Pubkey, + /// Ordered instructions to place in the transaction message. + pub instructions: std::vec::Vec, + /// Deduplicated signers required by the fee payer and instruction metas. + pub required_signers: std::vec::Vec, + /// Explicit policy to evaluate before simulation, signing and sending. + pub policy: crate::ExecutionPolicy, + /// Lamports explicitly transferred or locked by planned instructions. + pub requested_spend_lamports: u64, + /// Requested compute-unit price, when present. + pub requested_compute_unit_price_micro_lamports: std::option::Option, +} + +/// Result returned by a transaction simulation adapter. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionSimulationResult.ts" +)] +pub struct ExecutionSimulationResult { + /// Whether an RPC simulation call was actually performed. + pub simulated: bool, + /// Whether the simulated transaction completed successfully. + pub success: bool, + /// Cluster reported by the simulation adapter. + pub cluster: crate::ExecutionCluster, + /// Blockhash source used by the simulated transaction. + pub blockhash_kind: crate::ExecutionBlockhashKind, + /// Observed recent blockhash age, in slots. + pub blockhash_age_slots: std::option::Option, + /// Replacement blockhash returned by the node, if replacement was requested. + #[serde(default)] + pub replacement_blockhash: std::option::Option, + /// Last valid block height associated with the replacement blockhash. + #[serde(default)] + pub replacement_last_valid_block_height: std::option::Option, + /// Durable nonce account used by the simulated transaction. + pub nonce_account: std::option::Option, + /// Durable nonce authority used by the simulated transaction. + pub nonce_authority: std::option::Option, + /// Compute units consumed when reported by the RPC endpoint. + pub units_consumed: std::option::Option, + /// Estimated transaction fee in lamports when available. + pub estimated_fee_lamports: std::option::Option, + /// Simulation logs in runtime order. + pub logs: std::vec::Vec, + /// Stable or provider error text when simulation failed. + pub error: std::option::Option, +} + +/// Result returned after an adapter submits a signed transaction. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionSendResult.ts" +)] +pub struct ExecutionSendResult { + /// Cluster used for submission. + pub cluster: crate::ExecutionCluster, + /// Signature returned by the RPC endpoint. + pub signature: kb_model::Signature, + /// Whether submission was accepted by the RPC endpoint. + pub submitted: bool, +} + +/// Confirmation state returned by an execution confirmation adapter. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionConfirmationStatus.ts" +)] +pub enum ExecutionConfirmationStatus { + /// The transaction was observed at processed commitment. + Processed, + /// The transaction reached confirmed commitment. + Confirmed, + /// The transaction reached finalized commitment. + Finalized, + /// The transaction was confirmed with a runtime error. + Failed, + /// The transaction was not confirmed before its blockhash expired. + Expired, + /// The bounded confirmation poll ended before reaching the requested commitment. + TimedOut, +} + +/// Result returned after waiting for transaction confirmation. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/ExecutionConfirmationResult.ts" +)] +pub struct ExecutionConfirmationResult { + /// Cluster queried for confirmation. + pub cluster: crate::ExecutionCluster, + /// Submitted transaction signature. + pub signature: kb_model::Signature, + /// Highest confirmation state reached. + pub status: crate::ExecutionConfirmationStatus, + /// Slot that reported the transaction when available. + pub slot: std::option::Option, + /// Number of status polling attempts performed. + pub attempts: u32, + /// Last block height observed while checking recent-blockhash expiration. + pub last_observed_block_height: std::option::Option, + /// Stable or provider error text when confirmation failed. + pub error: std::option::Option, +} + +/// Diagnostic produced by post-execution canonical replay validation. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/execution/PostExecutionDiagnostic.ts" +)] +pub struct PostExecutionDiagnostic { + /// Executed transaction signature. + pub signature: kb_model::Signature, + /// Whether canonical insertion completed. + pub canonical_inserted: bool, + /// Whether core extraction completed. + pub core_extracted: bool, + /// Whether contextual decode replay completed. + pub decode_replayed: bool, + /// Whether requested materialization completed. + pub materialized: bool, + /// Diagnostic messages from orchestration layers. + pub diagnostics: std::vec::Vec, +} + +/// Contract implemented by executors that expose typed intents and exact plans. +pub trait TypedInstructionExecutor { + /// Protocol-specific typed intent. + type Intent; + + /// Returns the exact capability for one program and operation code. + fn capability( + &self, + program_id: &kb_model::ProgramId, + operation_code: &str, + ) -> crate::ExecutionCapability; + + /// Builds a typed execution plan without signing, sending or performing I/O. + fn build_prepared_plan( + &self, + intent: &Self::Intent, + ) -> kb_core::Result; +} + +#[cfg(test)] +mod tests { + #[test] + fn mainnet_cluster_serializes_with_current_name() { + let serialized = serde_json::to_string(&crate::ExecutionCluster::Mainnet) + .unwrap_or_else(|error| panic!("unexpected serialization error: {error}")); + assert_eq!(serialized, "\"mainnet\""); + let legacy = serde_json::from_str::("\"mainnet_beta\"") + .unwrap_or_else(|error| panic!("unexpected deserialization error: {error}")); + assert_eq!(legacy, crate::ExecutionCluster::Mainnet); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_execution_api/src/executor.rs b/migration/khadhroony-bot2-reference/kb_execution_api/src/executor.rs new file mode 100644 index 0000000..350fb8e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_execution_api/src/executor.rs @@ -0,0 +1,104 @@ +// file: kb_execution_api/src/executor.rs +// version: 3 + +//! Shared execution contract and neutral request models. + +use ts_rs::TS; // rust-rules: derive-import + +/// Support level returned by an executor for a requested operation. +#[derive(Clone, Copy, Debug, Eq, PartialEq, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/executor/ExecutionSupport.ts" +)] +pub enum ExecutionSupport { + /// The executor cannot handle the request. + No, + /// The executor may handle the request after account and payload validation. + Maybe, + /// The executor explicitly supports the request. + Yes, +} + +/// Generic request used before protocol-specific execution models are introduced. +#[derive(Clone, Debug, Eq, PartialEq, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/executor/ExecutionRequest.ts" +)] +pub struct ExecutionRequest { + /// Target program id. + pub program_id: kb_model::ProgramId, + /// Stable operation code such as buy, sell, transfer or route. + pub operation_code: std::string::String, + /// Serialized JSON payload reserved for protocol-specific arguments. + pub payload_json: std::string::String, +} + +/// Neutral execution plan placeholder produced by executor crates. +#[derive(Clone, Debug, Eq, PartialEq, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_api/executor/ExecutionPlan.ts" +)] +pub struct ExecutionPlan { + /// Stable executor crate name. + pub executor_name: std::string::String, + /// Number of instructions that would be produced by the final implementation. + pub instruction_count: usize, + /// Serialized JSON payload reserved for future instruction data and account metadata. + pub payload_json: std::string::String, +} + +/// Serializes a JSON payload to its compact string representation. +pub fn serialize_payload_json(value: &serde_json::Value) -> kb_core::Result { + return match serde_json::to_string(value) { + std::result::Result::Ok(serialized) => std::result::Result::Ok(serialized), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "execution_payload_json_serialize_failed", + error.to_string(), + )), + }; +} + +/// Serializes a JSON payload to its pretty string representation. +pub fn serialize_payload_json_pretty( + value: &serde_json::Value, +) -> kb_core::Result { + return match serde_json::to_string_pretty(value) { + std::result::Result::Ok(serialized) => std::result::Result::Ok(serialized), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "execution_payload_json_pretty_serialize_failed", + error.to_string(), + )), + }; +} + +/// Common contract implemented by all execution crates. +pub trait InstructionExecutor { + /// Returns the stable executor name. + fn executor_name(&self) -> &'static str; + + /// Returns the executor version. + fn executor_version(&self) -> &'static str; + + /// Returns the program ids handled by this executor. + fn program_ids(&self) -> &'static [&'static str]; + + /// Tests whether this executor owns a program id. + fn handles_program_id(&self, program_id: &kb_model::ProgramId) -> bool { + return self + .program_ids() + .iter() + .any(|candidate| return *candidate == program_id.0.as_str()); + } + + /// Tests whether this executor supports a request. + fn supports_request(&self, request: &crate::ExecutionRequest) -> crate::ExecutionSupport; + + /// Builds a neutral execution plan placeholder. + fn build_plan( + &self, + request: &crate::ExecutionRequest, + ) -> kb_core::Result; +} diff --git a/migration/khadhroony-bot2-reference/kb_execution_api/src/lib.rs b/migration/khadhroony-bot2-reference/kb_execution_api/src/lib.rs new file mode 100644 index 0000000..618415d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_execution_api/src/lib.rs @@ -0,0 +1,61 @@ +// file: kb_execution_api/src/lib.rs +// version: 2 + +//! Execution API contract shared by all executor crates. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod execution; +mod executor; + +/// Exposes the blockhash policy kind. +pub use crate::execution::ExecutionBlockhashKind; +/// Exposes the blockhash policy. +pub use crate::execution::ExecutionBlockhashPolicy; +/// Exposes the exact execution capability result. +pub use crate::execution::ExecutionCapability; +/// Exposes supported Solana clusters. +pub use crate::execution::ExecutionCluster; +/// Exposes cluster restrictions. +pub use crate::execution::ExecutionClusterPolicy; +/// Exposes the result of waiting for transaction confirmation. +pub use crate::execution::ExecutionConfirmationResult; +/// Exposes transaction confirmation states. +pub use crate::execution::ExecutionConfirmationStatus; +/// Exposes explicit execution cost ceilings. +pub use crate::execution::ExecutionCostLimit; +/// Exposes the complete execution policy. +pub use crate::execution::ExecutionPolicy; +/// Exposes the result of a transaction send adapter. +pub use crate::execution::ExecutionSendResult; +/// Exposes the simulation policy. +pub use crate::execution::ExecutionSimulationPolicy; +/// Exposes the simulation result contract. +pub use crate::execution::ExecutionSimulationResult; +/// Exposes one planned instruction account. +pub use crate::execution::PlannedAccount; +/// Exposes one encoded planned instruction. +pub use crate::execution::PlannedInstruction; +/// Exposes post-execution replay diagnostics. +pub use crate::execution::PostExecutionDiagnostic; +/// Exposes post-execution validation requirements. +pub use crate::execution::PostExecutionValidationPolicy; +/// Exposes a typed execution plan. +pub use crate::execution::PreparedExecutionPlan; +/// Exposes one required signer. +pub use crate::execution::RequiredSigner; +/// Exposes the typed executor contract. +pub use crate::execution::TypedInstructionExecutor; +/// Exposes the legacy common execution plan type. +pub use crate::executor::ExecutionPlan; +/// Exposes the legacy common execution request type. +pub use crate::executor::ExecutionRequest; +/// Exposes the legacy common execution support enum. +pub use crate::executor::ExecutionSupport; +/// Exposes the legacy common instruction executor trait. +pub use crate::executor::InstructionExecutor; +/// Exposes the compact JSON payload serializer. +pub use crate::executor::serialize_payload_json; +/// Exposes the pretty JSON payload serializer. +pub use crate::executor::serialize_payload_json_pretty; diff --git a/migration/khadhroony-bot2-reference/kb_execution_safety/Cargo.toml b/migration/khadhroony-bot2-reference/kb_execution_safety/Cargo.toml new file mode 100644 index 0000000..47eaa57 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_execution_safety/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_execution_safety/Cargo.toml +# version: 3 + +[package] +name = "kb_execution_safety" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +serde.workspace = true +ts-rs.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_execution_safety/README.md b/migration/khadhroony-bot2-reference/kb_execution_safety/README.md new file mode 100644 index 0000000..4f37c19 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_execution_safety/README.md @@ -0,0 +1,55 @@ + + + +# kb_execution_safety + +Cette crate applique des contrôles stateless communs aux plans préparés avant simulation, puis avant signature et envoi. Elle ne construit pas de transaction et n’effectue aucun I/O. + +## API publique + +| Export | Usage | +|-----------------------------|------------------------------------------------------------| +| `ExecutionSafetyChecker` | Point d’entrée stateless des évaluations de sécurité. | +| `ExecutionSafetyDecision` | Décision agrégée `Deny`, `RequireConfirmation` ou `Allow`. | +| `ExecutionSafetyEvaluation` | Décision accompagnée de toutes les violations stables. | +| `ExecutionSafetyViolation` | Code machine-readable et message humain d’un refus. | + +### `ExecutionSafetyChecker::evaluate_plan(plan)` + +Évalue l’ancien `ExecutionPlan`. Ce contrat historique ne contient pas assez d’informations pour autoriser directement une exécution ; la décision reste donc conservatrice et exige une confirmation. + +### `ExecutionSafetyChecker::evaluate_prepared_plan(plan)` + +Décide si un `PreparedExecutionPlan` peut atteindre la simulation RPC. La fonction vérifie notamment : + +- présence d’instructions et de signataires ; +- simulation obligatoire ; +- recent blockhash borné ou durable nonce complet ; +- cohérence entre signataires d’instruction, signataires déclarés et liste autorisée ; +- plafond de frais présent ; +- dépense et prix de compute unit sous leurs plafonds ; +- Mainnet explicitement autorisé et confirmé. + +### `ExecutionSafetyChecker::evaluate_send(plan, simulation)` + +Réévalue le plan puis décide si la simulation exacte permet la signature et l’envoi. La fonction refuse : + +- dry-run encore actif ; +- simulation absente ou échouée ; +- blockhash remplacé pendant la simulation ; +- cluster, blockhash ou nonce observé différent du plan ; +- frais simulés supérieurs au plafond ; +- confirmation Mainnet manquante. + +## Exemple + +```rust +let checker = kb_execution_safety::ExecutionSafetyChecker; +let before_simulation = checker.evaluate_prepared_plan(&plan)?; +if before_simulation.decision != kb_execution_safety::ExecutionSafetyDecision::Allow { + // Surface the stable violations and stop. +} +let before_send = checker.evaluate_send(&plan, &simulation)?; +``` + +Une décision `Allow` ne remplace pas la validation cryptographique du message ou des signatures. Cette responsabilité appartient à `kb_execution_solana`. diff --git a/migration/khadhroony-bot2-reference/kb_execution_safety/src/lib.rs b/migration/khadhroony-bot2-reference/kb_execution_safety/src/lib.rs new file mode 100644 index 0000000..86c1608 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_execution_safety/src/lib.rs @@ -0,0 +1,18 @@ +// file: kb_execution_safety/src/lib.rs +// version: 2 + +//! Shared safety checks for execution plans. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod safety; + +/// Exposes the execution safety checker. +pub use crate::safety::ExecutionSafetyChecker; +/// Exposes the execution safety decision. +pub use crate::safety::ExecutionSafetyDecision; +/// Exposes a complete execution safety evaluation. +pub use crate::safety::ExecutionSafetyEvaluation; +/// Exposes one safety policy violation. +pub use crate::safety::ExecutionSafetyViolation; diff --git a/migration/khadhroony-bot2-reference/kb_execution_safety/src/safety.rs b/migration/khadhroony-bot2-reference/kb_execution_safety/src/safety.rs new file mode 100644 index 0000000..4932902 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_execution_safety/src/safety.rs @@ -0,0 +1,698 @@ +// file: kb_execution_safety/src/safety.rs +// version: 6 + +//! Safety checks applied before simulation, signing or sending. + +use ts_rs::TS; // rust-rules: derive-import + +/// Safety decision returned before continuing an execution stage. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_safety/safety/ExecutionSafetyDecision.ts" +)] +pub enum ExecutionSafetyDecision { + /// The plan is not allowed to continue. + Deny, + /// The plan requires explicit operator confirmation. + RequireConfirmation, + /// The plan may continue to the requested stage. + Allow, +} + +/// One stable safety policy violation. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_safety/safety/ExecutionSafetyViolation.ts" +)] +pub struct ExecutionSafetyViolation { + /// Stable violation code. + pub code: std::string::String, + /// Human-readable violation message. + pub message: std::string::String, +} + +/// Complete safety evaluation for one execution stage. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_execution_safety/safety/ExecutionSafetyEvaluation.ts" +)] +pub struct ExecutionSafetyEvaluation { + /// Aggregate decision. + pub decision: crate::ExecutionSafetyDecision, + /// Violations that produced a denial or confirmation requirement. + pub violations: std::vec::Vec, +} + +/// Stateless execution safety checker. +#[derive(Clone, Debug, Default)] +pub struct ExecutionSafetyChecker; + +impl crate::ExecutionSafetyChecker { + /// Evaluates a legacy reserved plan conservatively. + pub fn evaluate_plan( + &self, + _plan: &kb_execution_api::ExecutionPlan, + ) -> kb_core::Result { + return std::result::Result::Ok(crate::ExecutionSafetyDecision::RequireConfirmation); + } + + /// Evaluates whether a typed plan may proceed to RPC simulation. + pub fn evaluate_prepared_plan( + &self, + plan: &kb_execution_api::PreparedExecutionPlan, + ) -> kb_core::Result { + let mut violations = std::vec::Vec::new(); + if plan.instructions.is_empty() { + push_violation( + &mut violations, + "execution_plan_has_no_instructions", + "the prepared execution plan contains no instruction", + ); + } + if plan.required_signers.is_empty() { + push_violation( + &mut violations, + "execution_plan_has_no_signers", + "the prepared execution plan declares no required signer", + ); + } + if plan.policy.simulation != kb_execution_api::ExecutionSimulationPolicy::Required { + push_violation( + &mut violations, + "execution_simulation_not_required", + "simulation must be required before signing or sending", + ); + } + validate_blockhash_policy(plan, &mut violations); + validate_signer_contract(plan, &mut violations); + validate_authorized_signers(plan, &mut violations); + validate_cost_limits(plan, &mut violations); + if !violations.is_empty() { + return std::result::Result::Ok(crate::ExecutionSafetyEvaluation { + decision: crate::ExecutionSafetyDecision::Deny, + violations, + }); + } + if plan.policy.cluster.expected_cluster == kb_execution_api::ExecutionCluster::Mainnet { + if !plan.policy.cluster.allow_mainnet { + push_violation( + &mut violations, + "execution_mainnet_disabled", + "mainnet execution is disabled by the cluster policy", + ); + return std::result::Result::Ok(crate::ExecutionSafetyEvaluation { + decision: crate::ExecutionSafetyDecision::Deny, + violations, + }); + } + if !plan.policy.cluster.mainnet_confirmation { + push_violation( + &mut violations, + "execution_mainnet_confirmation_required", + "mainnet execution requires explicit operator confirmation", + ); + return std::result::Result::Ok(crate::ExecutionSafetyEvaluation { + decision: crate::ExecutionSafetyDecision::RequireConfirmation, + violations, + }); + } + } + return std::result::Result::Ok(crate::ExecutionSafetyEvaluation { + decision: crate::ExecutionSafetyDecision::Allow, + violations, + }); + } + + /// Evaluates whether a simulated plan may proceed to signing and sending. + pub fn evaluate_send( + &self, + plan: &kb_execution_api::PreparedExecutionPlan, + simulation: &kb_execution_api::ExecutionSimulationResult, + ) -> kb_core::Result { + let plan_evaluation = match self.evaluate_prepared_plan(plan) { + std::result::Result::Ok(evaluation) => evaluation, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if plan_evaluation.decision == crate::ExecutionSafetyDecision::Deny { + return std::result::Result::Ok(plan_evaluation); + } + let mut violations = plan_evaluation.violations; + if plan.policy.dry_run { + push_violation( + &mut violations, + "execution_dry_run_enabled", + "dry-run mode forbids transaction signing and sending", + ); + } + if !simulation.simulated { + push_violation( + &mut violations, + "execution_simulation_missing", + "an RPC simulation result is required before sending", + ); + } else { + if !simulation.success { + push_violation( + &mut violations, + "execution_simulation_failed", + "the simulated transaction did not complete successfully", + ); + } + if simulation.replacement_blockhash.is_some() + || simulation.replacement_last_valid_block_height.is_some() + { + push_violation( + &mut violations, + "execution_simulation_replaced_blockhash", + "a simulation with a replacement blockhash cannot authorize signing or sending the original message", + ); + } + validate_simulation_context(plan, simulation, &mut violations); + } + validate_simulated_fee(plan, simulation, &mut violations); + if !violations.is_empty() { + let decision = if violations.iter().all(|violation| { + return violation.code == "execution_mainnet_confirmation_required"; + }) { + crate::ExecutionSafetyDecision::RequireConfirmation + } else { + crate::ExecutionSafetyDecision::Deny + }; + return std::result::Result::Ok(crate::ExecutionSafetyEvaluation { + decision, + violations, + }); + } + return std::result::Result::Ok(crate::ExecutionSafetyEvaluation { + decision: crate::ExecutionSafetyDecision::Allow, + violations, + }); + } +} + +fn validate_blockhash_policy( + plan: &kb_execution_api::PreparedExecutionPlan, + violations: &mut std::vec::Vec, +) { + match plan.policy.blockhash.kind { + kb_execution_api::ExecutionBlockhashKind::Latest => { + if plan.policy.blockhash.max_age_slots == std::option::Option::Some(0) + || plan.policy.blockhash.max_age_slots.is_none() + { + push_violation( + violations, + "execution_blockhash_age_missing", + "a positive maximum blockhash age is required", + ); + } + if plan.policy.blockhash.nonce_account.is_some() + || plan.policy.blockhash.nonce_authority.is_some() + { + push_violation( + violations, + "execution_latest_blockhash_has_nonce_fields", + "latest blockhash policy must not carry durable nonce fields", + ); + } + }, + kb_execution_api::ExecutionBlockhashKind::DurableNonce => { + if plan.policy.blockhash.nonce_account.is_none() + || plan.policy.blockhash.nonce_authority.is_none() + { + push_violation( + violations, + "execution_durable_nonce_context_missing", + "durable nonce policy requires both account and authority", + ); + } + if plan.policy.blockhash.max_age_slots.is_some() { + push_violation( + violations, + "execution_durable_nonce_has_blockhash_age", + "durable nonce policy must not carry a recent blockhash age", + ); + } + }, + } +} + +fn validate_signer_contract( + plan: &kb_execution_api::PreparedExecutionPlan, + violations: &mut std::vec::Vec, +) { + let mut unique_signers = std::collections::BTreeSet::new(); + for required in &plan.required_signers { + if !unique_signers.insert(required.pubkey.0.as_str()) { + push_violation( + violations, + "execution_required_signer_duplicate", + format!("required signer {} is declared more than once", required.pubkey.0), + ); + } + } + if !plan + .required_signers + .iter() + .any(|required| return required.pubkey == plan.fee_payer) + { + push_violation( + violations, + "execution_fee_payer_not_declared", + "the transaction fee payer is missing from required signers", + ); + } + for instruction in &plan.instructions { + for account in &instruction.accounts { + if account.is_signer + && !plan + .required_signers + .iter() + .any(|required| return required.pubkey == account.pubkey) + { + push_violation( + violations, + "execution_instruction_signer_not_declared", + format!( + "instruction signer {} is missing from required signers", + account.pubkey.0 + ), + ); + } + } + } +} + +fn validate_authorized_signers( + plan: &kb_execution_api::PreparedExecutionPlan, + violations: &mut std::vec::Vec, +) { + for required in &plan.required_signers { + if !plan + .policy + .authorized_signers + .iter() + .any(|authorized| return authorized == &required.pubkey) + { + push_violation( + violations, + "execution_signer_not_authorized", + format!( + "required signer {} with role {} is not authorized", + required.pubkey.0, required.role + ), + ); + } + } +} + +fn validate_cost_limits( + plan: &kb_execution_api::PreparedExecutionPlan, + violations: &mut std::vec::Vec, +) { + match plan.policy.cost_limit.max_fee_lamports { + std::option::Option::Some(max_fee_lamports) if max_fee_lamports > 0 => {}, + std::option::Option::Some(_) | std::option::Option::None => { + push_violation( + violations, + "execution_fee_limit_missing", + "a positive transaction fee ceiling is required", + ); + }, + } + if plan.requested_spend_lamports > 0 { + match plan.policy.cost_limit.max_spend_lamports { + std::option::Option::Some(limit) if plan.requested_spend_lamports <= limit => {}, + std::option::Option::Some(_) => push_violation( + violations, + "execution_spend_limit_exceeded", + "the requested lamport spend exceeds the configured ceiling", + ), + std::option::Option::None => push_violation( + violations, + "execution_spend_limit_missing", + "a lamport spend ceiling is required for this operation", + ), + } + } + if let std::option::Option::Some(requested_price) = + plan.requested_compute_unit_price_micro_lamports + { + match plan.policy.cost_limit.max_compute_unit_price_micro_lamports { + std::option::Option::Some(limit) if requested_price <= limit => {}, + std::option::Option::Some(_) => push_violation( + violations, + "execution_compute_unit_price_limit_exceeded", + "the requested compute-unit price exceeds the configured ceiling", + ), + std::option::Option::None => push_violation( + violations, + "execution_compute_unit_price_limit_missing", + "a compute-unit price ceiling is required for this operation", + ), + } + } +} + +fn validate_simulation_context( + plan: &kb_execution_api::PreparedExecutionPlan, + simulation: &kb_execution_api::ExecutionSimulationResult, + violations: &mut std::vec::Vec, +) { + if simulation.cluster != plan.policy.cluster.expected_cluster { + push_violation( + violations, + "execution_simulation_cluster_mismatch", + "the simulation cluster does not match the execution policy", + ); + } + if simulation.blockhash_kind != plan.policy.blockhash.kind { + push_violation( + violations, + "execution_simulation_blockhash_kind_mismatch", + "the simulated blockhash source does not match the execution policy", + ); + return; + } + match plan.policy.blockhash.kind { + kb_execution_api::ExecutionBlockhashKind::Latest => { + let max_age_slots = match plan.policy.blockhash.max_age_slots { + std::option::Option::Some(max_age_slots) => max_age_slots, + std::option::Option::None => return, + }; + match simulation.blockhash_age_slots { + std::option::Option::Some(age_slots) if age_slots <= max_age_slots => {}, + std::option::Option::Some(_) => push_violation( + violations, + "execution_simulation_blockhash_too_old", + "the simulated recent blockhash exceeds the configured maximum age", + ), + std::option::Option::None => push_violation( + violations, + "execution_simulation_blockhash_age_missing", + "the simulation adapter did not report the recent blockhash age", + ), + } + if simulation.nonce_account.is_some() || simulation.nonce_authority.is_some() { + push_violation( + violations, + "execution_simulation_latest_has_nonce_fields", + "a latest-blockhash simulation must not report durable nonce fields", + ); + } + }, + kb_execution_api::ExecutionBlockhashKind::DurableNonce => { + if simulation.blockhash_age_slots.is_some() { + push_violation( + violations, + "execution_simulation_nonce_has_blockhash_age", + "a durable nonce simulation must not report recent blockhash age", + ); + } + if simulation.nonce_account != plan.policy.blockhash.nonce_account { + push_violation( + violations, + "execution_simulation_nonce_account_mismatch", + "the simulated durable nonce account does not match the execution policy", + ); + } + if simulation.nonce_authority != plan.policy.blockhash.nonce_authority { + push_violation( + violations, + "execution_simulation_nonce_authority_mismatch", + "the simulated durable nonce authority does not match the execution policy", + ); + } + }, + } +} + +fn validate_simulated_fee( + plan: &kb_execution_api::PreparedExecutionPlan, + simulation: &kb_execution_api::ExecutionSimulationResult, + violations: &mut std::vec::Vec, +) { + let limit = match plan.policy.cost_limit.max_fee_lamports { + std::option::Option::Some(limit) => limit, + std::option::Option::None => return, + }; + match simulation.estimated_fee_lamports { + std::option::Option::Some(fee) if fee <= limit => {}, + std::option::Option::Some(_) => push_violation( + violations, + "execution_simulated_fee_limit_exceeded", + "the simulated transaction fee exceeds the configured ceiling", + ), + std::option::Option::None => push_violation( + violations, + "execution_simulated_fee_missing", + "the simulation did not report an estimated transaction fee", + ), + } +} + +fn push_violation( + violations: &mut std::vec::Vec, + code: impl std::convert::Into, + message: impl std::convert::Into, +) { + violations.push(crate::ExecutionSafetyViolation { + code: code.into(), + message: message.into(), + }); +} + +#[cfg(test)] +mod tests { + fn sample_plan() -> kb_execution_api::PreparedExecutionPlan { + let signer = + kb_model::Pubkey(std::string::String::from("11111111111111111111111111111111")); + return kb_execution_api::PreparedExecutionPlan { + executor_name: std::string::String::from("sample_executor"), + executor_version: std::string::String::from("0.4.2"), + intent_id: std::string::String::from("intent-1"), + operation_code: std::string::String::from("sample.operation"), + fee_payer: signer.clone(), + instructions: vec![kb_execution_api::PlannedInstruction { + program_id: kb_model::ProgramId(std::string::String::from( + "11111111111111111111111111111111", + )), + operation_code: std::string::String::from("sample.operation"), + accounts: vec![kb_execution_api::PlannedAccount { + pubkey: signer.clone(), + is_signer: true, + is_writable: true, + }], + data: vec![1], + }], + required_signers: vec![kb_execution_api::RequiredSigner { + pubkey: signer.clone(), + role: std::string::String::from("fee_payer"), + }], + policy: kb_execution_api::ExecutionPolicy { + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(100), + max_fee_lamports: std::option::Option::Some(10_000), + max_compute_unit_price_micro_lamports: std::option::Option::Some(5), + }, + authorized_signers: vec![signer], + ..kb_execution_api::ExecutionPolicy::default() + }, + requested_spend_lamports: 100, + requested_compute_unit_price_micro_lamports: std::option::Option::None, + }; + } + + fn successful_simulation() -> kb_execution_api::ExecutionSimulationResult { + return kb_execution_api::ExecutionSimulationResult { + simulated: true, + success: true, + cluster: kb_execution_api::ExecutionCluster::Devnet, + blockhash_kind: kb_execution_api::ExecutionBlockhashKind::Latest, + blockhash_age_slots: std::option::Option::Some(3), + replacement_blockhash: std::option::Option::None, + replacement_last_valid_block_height: std::option::Option::None, + nonce_account: std::option::Option::None, + nonce_authority: std::option::Option::None, + units_consumed: std::option::Option::Some(500), + estimated_fee_lamports: std::option::Option::Some(5_000), + logs: std::vec::Vec::new(), + error: std::option::Option::None, + }; + } + + #[test] + fn conservative_plan_is_allowed_for_simulation() { + let plan = sample_plan(); + let evaluation = crate::ExecutionSafetyChecker + .evaluate_prepared_plan(&plan) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(evaluation.decision, crate::ExecutionSafetyDecision::Allow); + assert!(evaluation.violations.is_empty()); + } + + #[test] + fn dry_run_blocks_send_after_successful_simulation() { + let plan = sample_plan(); + let simulation = successful_simulation(); + let evaluation = crate::ExecutionSafetyChecker + .evaluate_send(&plan, &simulation) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(evaluation.decision, crate::ExecutionSafetyDecision::Deny); + assert!(evaluation.violations.iter().any(|violation| { + return violation.code == "execution_dry_run_enabled"; + })); + } + + #[test] + fn successful_simulation_allows_devnet_send_when_dry_run_is_disabled() { + let mut plan = sample_plan(); + plan.policy.dry_run = false; + let simulation = successful_simulation(); + let evaluation = crate::ExecutionSafetyChecker + .evaluate_send(&plan, &simulation) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(evaluation.decision, crate::ExecutionSafetyDecision::Allow); + } + + #[test] + fn undeclared_fee_payer_and_instruction_signer_are_denied() { + let mut plan = sample_plan(); + plan.required_signers.clear(); + let evaluation = crate::ExecutionSafetyChecker + .evaluate_prepared_plan(&plan) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(evaluation.decision, crate::ExecutionSafetyDecision::Deny); + assert!(evaluation.violations.iter().any(|violation| { + return violation.code == "execution_fee_payer_not_declared"; + })); + assert!(evaluation.violations.iter().any(|violation| { + return violation.code == "execution_instruction_signer_not_declared"; + })); + } + + #[test] + fn duplicate_required_signer_is_denied() { + let mut plan = sample_plan(); + let duplicate = plan.required_signers[0].clone(); + plan.required_signers.push(duplicate); + let evaluation = crate::ExecutionSafetyChecker + .evaluate_prepared_plan(&plan) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(evaluation.decision, crate::ExecutionSafetyDecision::Deny); + assert!(evaluation.violations.iter().any(|violation| { + return violation.code == "execution_required_signer_duplicate"; + })); + } + + #[test] + fn unauthorized_signer_is_denied() { + let mut plan = sample_plan(); + plan.policy.authorized_signers.clear(); + let evaluation = crate::ExecutionSafetyChecker + .evaluate_prepared_plan(&plan) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(evaluation.decision, crate::ExecutionSafetyDecision::Deny); + assert!(evaluation.violations.iter().any(|violation| { + return violation.code == "execution_signer_not_authorized"; + })); + } + + #[test] + fn spend_above_ceiling_is_denied() { + let mut plan = sample_plan(); + plan.policy.cost_limit.max_spend_lamports = std::option::Option::Some(99); + let evaluation = crate::ExecutionSafetyChecker + .evaluate_prepared_plan(&plan) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(evaluation.decision, crate::ExecutionSafetyDecision::Deny); + assert!(evaluation.violations.iter().any(|violation| { + return violation.code == "execution_spend_limit_exceeded"; + })); + } + + #[test] + fn mainnet_requires_enablement_and_confirmation() { + let mut plan = sample_plan(); + plan.policy.cluster.expected_cluster = kb_execution_api::ExecutionCluster::Mainnet; + let disabled = crate::ExecutionSafetyChecker + .evaluate_prepared_plan(&plan) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(disabled.decision, crate::ExecutionSafetyDecision::Deny); + plan.policy.cluster.allow_mainnet = true; + let unconfirmed = crate::ExecutionSafetyChecker + .evaluate_prepared_plan(&plan) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(unconfirmed.decision, crate::ExecutionSafetyDecision::RequireConfirmation); + plan.policy.cluster.mainnet_confirmation = true; + let confirmed = crate::ExecutionSafetyChecker + .evaluate_prepared_plan(&plan) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(confirmed.decision, crate::ExecutionSafetyDecision::Allow); + } + + #[test] + fn simulation_cluster_mismatch_is_denied() { + let mut plan = sample_plan(); + plan.policy.dry_run = false; + let mut simulation = successful_simulation(); + simulation.cluster = kb_execution_api::ExecutionCluster::Testnet; + let evaluation = crate::ExecutionSafetyChecker + .evaluate_send(&plan, &simulation) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(evaluation.decision, crate::ExecutionSafetyDecision::Deny); + assert!(evaluation.violations.iter().any(|violation| { + return violation.code == "execution_simulation_cluster_mismatch"; + })); + } + + #[test] + fn stale_simulation_blockhash_is_denied() { + let mut plan = sample_plan(); + plan.policy.dry_run = false; + let mut simulation = successful_simulation(); + simulation.blockhash_age_slots = std::option::Option::Some(151); + let evaluation = crate::ExecutionSafetyChecker + .evaluate_send(&plan, &simulation) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(evaluation.decision, crate::ExecutionSafetyDecision::Deny); + assert!(evaluation.violations.iter().any(|violation| { + return violation.code == "execution_simulation_blockhash_too_old"; + })); + } + + #[test] + fn replacement_blockhash_simulation_cannot_authorize_send() { + let mut plan = sample_plan(); + plan.policy.dry_run = false; + let mut simulation = successful_simulation(); + simulation.replacement_blockhash = + std::option::Option::Some(std::string::String::from("replacement-blockhash")); + simulation.replacement_last_valid_block_height = std::option::Option::Some(200); + let evaluation = crate::ExecutionSafetyChecker + .evaluate_send(&plan, &simulation) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(evaluation.decision, crate::ExecutionSafetyDecision::Deny); + assert!(evaluation.violations.iter().any(|violation| { + return violation.code == "execution_simulation_replaced_blockhash"; + })); + } + + #[test] + fn simulated_fee_above_ceiling_is_denied() { + let mut plan = sample_plan(); + plan.policy.dry_run = false; + let mut simulation = successful_simulation(); + simulation.estimated_fee_lamports = std::option::Option::Some(10_001); + let evaluation = crate::ExecutionSafetyChecker + .evaluate_send(&plan, &simulation) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(evaluation.decision, crate::ExecutionSafetyDecision::Deny); + assert!(evaluation.violations.iter().any(|violation| { + return violation.code == "execution_simulated_fee_limit_exceeded"; + })); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_execution_solana/Cargo.toml b/migration/khadhroony-bot2-reference/kb_execution_solana/Cargo.toml new file mode 100644 index 0000000..e969b2d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_execution_solana/Cargo.toml @@ -0,0 +1,35 @@ +# file: kb_execution_solana/Cargo.toml +# version: 3 + +[package] +name = "kb_execution_solana" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +base64.workspace = true +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_execution_safety = { path = "../kb_execution_safety" } +kb_model = { path = "../kb_model" } +solana-hash.workspace = true +solana-instruction.workspace = true +solana-keypair.workspace = true +solana-message.workspace = true +solana-nonce.workspace = true +solana-pubkey.workspace = true +solana-sdk-ids.workspace = true +solana-signer.workspace = true +solana-system-interface.workspace = true +solana-transaction.workspace = true +tracing.workspace = true +wincode.workspace = true + +[dev-dependencies] +kb_executor_solana_core = { path = "../kb_executor_solana_core" } +kb_wallet = { path = "../kb_wallet" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_execution_solana/README.md b/migration/khadhroony-bot2-reference/kb_execution_solana/README.md new file mode 100644 index 0000000..1ab6f35 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_execution_solana/README.md @@ -0,0 +1,151 @@ + + + +# kb_execution_solana + +`kb_execution_solana` transforme un `PreparedExecutionPlan` commun en message et transaction Solana, puis résout les signataires via l’interface Solana `Signer` et signe uniquement après une simulation autorisée par `kb_execution_safety`. + +La crate est indépendante des exécuteurs concrets et des transports RPC. Elle assemble aussi bien les plans à recent blockhash que les transactions consommant réellement un durable nonce. + +## API publique + +| Export | Usage | +|----------------------------------------|-------------------------------------------------------------------------------------------------------| +| `build_legacy_transaction(...)` | Compile un plan `Latest` avec un recent blockhash explicite. | +| `build_durable_nonce_transaction(...)` | Compile un plan `DurableNonce` avec un état nonce validé et injecte l’avance en première instruction. | +| `durable_nonce_account_data_length()` | Fournit la longueur exacte à demander au RPC pour un compte nonce. | +| `parse_durable_nonce_account(...)` | Valide et décode un snapshot complet de compte nonce System. | +| `DurableNonceAccountState` | État validé exposant compte, autorité, nonce/blockhash et coût par signature. | +| `UnsignedSolanaTransaction` | Message compilé non signé, destiné aux frais, à la simulation exacte et à la signature contrôlée. | +| `SolanaSimulationEvidence` | Simulation liée au hash et au blockhash/nonce exacts du message. | +| `SignedSolanaTransaction` | Transaction signée, vérifiable localement et sérialisable pour `sendTransaction`. | + +### Méthodes des types + +| Type | Méthodes publiques | +|-----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `DurableNonceAccountState` | `account()`, `authority()`, `blockhash()`, `lamports_per_signature()`. | +| `UnsignedSolanaTransaction` | `plan()`, `message_hash()`, `blockhash()`, `required_signer_pubkeys()`, `message_base64()`, `transaction_base64()`, `bind_simulation(...)`, `sign_after_simulation(...)`. | +| `SolanaSimulationEvidence` | `message_hash()`, `blockhash()`, `simulation()`. | +| `SignedSolanaTransaction` | `message_hash()`, `primary_signature()`, `signer_pubkeys()`, `wire_length()`, `transaction_base64()`, `verify_signatures()`. | + +## Exemple de flux + +```rust +let unsigned = kb_execution_solana::build_legacy_transaction(&plan, recent_blockhash)?; +let simulation = rpc_simulation.to_execution_result( + unsigned.message_hash(), + unsigned.blockhash(), + observed_cluster, +)?; +let evidence = unsigned.bind_simulation(simulation)?; +let signed = unsigned.sign_after_simulation(&evidence, signers.as_slice())?; +signed.verify_signatures()?; +``` + +Le transport RPC reste extérieur : cette crate ne récupère pas le blockhash, ne charge pas le compte nonce et n’envoie pas le payload signé. + +## Assemblage à recent blockhash + +### `build_legacy_transaction(plan, recent_blockhash)` + +Construit une transaction legacy non signée à partir d’un blockhash base58 obtenu par `getLatestBlockhash`. + +La fonction vérifie : + +- la politique `ExecutionBlockhashKind::Latest` ; +- les public keys et le blockhash ; +- la présence d’au moins une instruction ; +- l’égalité exacte entre signataires déclarés et signataires compilés ; +- la limite wire Solana de 1 232 octets. + +Un plan `DurableNonce` est refusé et doit utiliser le chemin dédié. + +## Lecture et validation d’un compte nonce + +### `durable_nonce_account_data_length()` + +Retourne la taille wire officielle d’un compte nonce, actuellement 80 octets. Cette valeur doit être utilisée comme limite exacte d’un appel `getAccountInfo` avec données complètes. + +### `parse_durable_nonce_account(account, owner, executable, space, data)` + +Décode l’état officiel `solana_nonce::versions::Versions` avec `wincode::deserialize_exact` et produit un `DurableNonceAccountState` uniquement lorsque toutes les conditions suivantes sont satisfaites : + +- adresse du compte valide ; +- owner égal au System Program ; +- compte non exécutable ; +- `space` et longueur décodée égaux à la taille officielle ; +- version `Current` ; +- état `Initialized` ; +- autorité et valeur nonce extraites sans approximation. + +Les états `Legacy`, `Uninitialized`, tronqués, suffixés ou possédés par un autre programme sont refusés. `DurableNonceAccountState` expose uniquement l’adresse, l’autorité, la valeur nonce utilisée comme blockhash et le coût par signature stocké dans l’état. + +Le chargement réseau reste extérieur à cette crate. Le contrat RPC prévu est : + +```rust +let config = kb_rpc::GetAccountInfoConfig::confirmed_with_data( + kb_execution_solana::durable_nonce_account_data_length(), +)?; +``` + +## Assemblage durable nonce + +### `build_durable_nonce_transaction(plan, nonce_state)` + +Construit une transaction non signée qui consomme l’état nonce validé. + +La fonction exige : + +- une politique `ExecutionBlockhashKind::DurableNonce` sans âge de blockhash récent ; +- le même compte nonce dans la politique et dans l’état validé ; +- la même autorité dans la politique et dans l’état validé ; +- l’autorité nonce dans `required_signers` ; +- au moins une instruction métier à exécuter après l’avance nonce. + +L’assemblage utilise `Message::new_with_nonce`, place l’instruction officielle `AdvanceNonceAccount` en position zéro, puis remplace le champ `recent_blockhash` par la valeur nonce extraite de l’état. Le plan d’origine n’est pas muté : `UnsignedSolanaTransaction::plan()` expose un plan effectif cloné contenant l’instruction injectée. + +Après compilation, la crate vérifie : + +- la reconnaissance de la transaction par `solana_transaction::uses_durable_nonce` ; +- l’égalité exacte entre signataires déclarés et signataires compilés ; +- la taille wire ; +- le hash du message et la valeur nonce effectivement compilée. + +Les opérations de gestion du compte nonce elles-mêmes, telles que `initialize`, `authorize`, `withdraw` ou `upgrade`, restent des transactions ordinaires à recent blockhash. Elles ne passent pas par ce chemin. + +## Simulation et signature + +### `UnsignedSolanaTransaction` + +Expose : + +- le plan effectif ; +- le hash exact du message ; +- le recent blockhash ou la valeur nonce compilée ; +- l’ordre exact des signataires ; +- le message base64 pour `getFeeForMessage` ; +- la transaction non signée base64 pour `simulateTransaction` avec `sigVerify = false` et `replaceRecentBlockhash = false`. + +### `bind_simulation(result)` + +Lie le résultat typé de simulation au hash du message **et** au blockhash/nonce compilé. Une simulation utilisant un autre message, une autre source de blockhash ou un blockhash de remplacement ne peut pas autoriser la signature. + +### `sign_after_simulation(evidence, signers)` + +Réévalue `kb_execution_safety`, exige une décision `Allow`, résout exactement tous les signataires via `&dyn solana_signer::Signer`, signe puis vérifie que la transaction est complète. + +Les signataires manquants, supplémentaires ou dupliqués sont refusés. La fonction ne contacte aucun endpoint RPC et n’envoie rien. + +### `SignedSolanaTransaction` + +Expose uniquement des données non secrètes : + +- hash du message ; +- signature primaire ; +- public keys des signataires ; +- taille wire ; +- payload base64 signé ; +- vérification locale des signatures. + +Le payload signé reste côté backend. Il ne doit pas être transféré automatiquement à Tauri. `kb_rpc::HttpClient::send_transaction` et sa variante pool imposent le preflight et refusent une signature RPC différente de la signature primaire locale. diff --git a/migration/khadhroony-bot2-reference/kb_execution_solana/src/constants.rs b/migration/khadhroony-bot2-reference/kb_execution_solana/src/constants.rs new file mode 100644 index 0000000..fa82078 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_execution_solana/src/constants.rs @@ -0,0 +1,11 @@ +// file: kb_execution_solana/src/constants.rs +// version: 5 + +//! Local constants for the `kb_execution_solana` crate. + +/// Maximum over-the-wire size of a Solana transaction packet. +pub(crate) const MAX_TRANSACTION_WIRE_BYTES: usize = 1_232; +/// Stable operation code for the nonce advance injected by durable transaction assembly. +pub(crate) const DURABLE_NONCE_ADVANCE_OPERATION: &str = "solana_core.system.nonce.advance"; +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_execution_solana"; diff --git a/migration/khadhroony-bot2-reference/kb_execution_solana/src/lib.rs b/migration/khadhroony-bot2-reference/kb_execution_solana/src/lib.rs new file mode 100644 index 0000000..12b6a7c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_execution_solana/src/lib.rs @@ -0,0 +1,35 @@ +// file: kb_execution_solana/src/lib.rs +// version: 5 + +//! Solana transaction assembly and signing for prepared execution plans. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod nonce; +mod transaction; + +/// Stable operation code for the nonce advance injected by durable transaction assembly. +pub(crate) use crate::constants::DURABLE_NONCE_ADVANCE_OPERATION; +/// Maximum over-the-wire size of a Solana transaction packet. +pub(crate) use crate::constants::MAX_TRANSACTION_WIRE_BYTES; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Validated current durable nonce account state used for transaction assembly. +pub use crate::nonce::DurableNonceAccountState; +/// Returns the exact serialized byte length of a Solana nonce account state. +pub use crate::nonce::durable_nonce_account_data_length; +/// Parses and validates a complete System Program-owned durable nonce account snapshot. +pub use crate::nonce::parse_durable_nonce_account; +/// Signed Solana transaction ready for RPC submission. +pub use crate::transaction::SignedSolanaTransaction; +/// Simulation result bound to the exact assembled message that was simulated. +pub use crate::transaction::SolanaSimulationEvidence; +/// Unsigned assembled Solana transaction used for fee estimation and simulation. +pub use crate::transaction::UnsignedSolanaTransaction; +/// Builds an unsigned durable nonce transaction from a prepared execution plan and validated nonce state. +pub use crate::transaction::build_durable_nonce_transaction; +/// Builds an unsigned legacy Solana transaction from a prepared execution plan. +pub use crate::transaction::build_legacy_transaction; diff --git a/migration/khadhroony-bot2-reference/kb_execution_solana/src/nonce.rs b/migration/khadhroony-bot2-reference/kb_execution_solana/src/nonce.rs new file mode 100644 index 0000000..38e3a85 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_execution_solana/src/nonce.rs @@ -0,0 +1,276 @@ +// file: kb_execution_solana/src/nonce.rs +// version: 5 + +//! Durable nonce account state parsing and validation. + +use std::str::FromStr; // rust-rules: trait-import + +/// Validated current durable nonce account state used for transaction assembly. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DurableNonceAccountState { + account: kb_model::Pubkey, + authority: kb_model::Pubkey, + blockhash: std::string::String, + lamports_per_signature: u64, +} + +impl crate::DurableNonceAccountState { + /// Returns the nonce account address. + pub fn account(&self) -> &kb_model::Pubkey { + return &self.account; + } + + /// Returns the authority that must sign the nonce advance instruction. + pub fn authority(&self) -> &kb_model::Pubkey { + return &self.authority; + } + + /// Returns the durable nonce value used as the transaction message blockhash. + pub fn blockhash(&self) -> &str { + return self.blockhash.as_str(); + } + + /// Returns the fee rate stored when the nonce was last advanced. + pub fn lamports_per_signature(&self) -> u64 { + return self.lamports_per_signature; + } +} + +/// Returns the exact serialized byte length of a Solana nonce account state. +pub const fn durable_nonce_account_data_length() -> usize { + return solana_nonce::state::State::size(); +} + +/// Parses and validates a complete System Program-owned durable nonce account snapshot. +pub fn parse_durable_nonce_account( + account: &kb_model::Pubkey, + owner: &kb_model::ProgramId, + executable: bool, + space: u64, + data: &[u8], +) -> kb_core::Result { + let account_address = match solana_pubkey::Pubkey::from_str(account.0.as_str()) { + std::result::Result::Ok(account_address) => account_address, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_nonce_account_address_invalid", + format!("invalid durable nonce account address {}: {error}", account.0), + )); + }, + }; + let owner_address = match solana_pubkey::Pubkey::from_str(owner.0.as_str()) { + std::result::Result::Ok(owner_address) => owner_address, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_nonce_account_owner_invalid", + format!("invalid durable nonce account owner {}: {error}", owner.0), + )); + }, + }; + if owner_address != solana_sdk_ids::system_program::id() { + return std::result::Result::Err(kb_core::Error::new( + "execution_nonce_account_owner_mismatch", + format!( + "durable nonce account {account_address} is owned by {owner_address} instead of the System Program" + ), + )); + } + if executable { + return std::result::Result::Err(kb_core::Error::new( + "execution_nonce_account_executable", + format!("durable nonce account {account_address} must not be executable"), + )); + } + let expected_length = crate::durable_nonce_account_data_length(); + if space != expected_length as u64 { + return std::result::Result::Err(kb_core::Error::new( + "execution_nonce_account_space_invalid", + format!( + "durable nonce account {account_address} reports {space} bytes instead of {expected_length}" + ), + )); + } + if data.len() != expected_length { + return std::result::Result::Err(kb_core::Error::new( + "execution_nonce_account_data_length_invalid", + format!( + "durable nonce account {account_address} contains {} decoded bytes instead of {expected_length}", + data.len() + ), + )); + } + let versions = match wincode::deserialize_exact::(data) { + std::result::Result::Ok(versions) => versions, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_nonce_account_state_invalid", + format!("cannot decode durable nonce account {account_address}: {error}"), + )); + }, + }; + let state = match versions { + solana_nonce::versions::Versions::Legacy(_) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_nonce_account_legacy_version", + format!( + "durable nonce account {account_address} uses the legacy nonce domain and must be upgraded before use" + ), + )); + }, + solana_nonce::versions::Versions::Current(state) => state, + }; + let initialized = match *state { + solana_nonce::state::State::Uninitialized => { + return std::result::Result::Err(kb_core::Error::new( + "execution_nonce_account_uninitialized", + format!("durable nonce account {account_address} is not initialized"), + )); + }, + solana_nonce::state::State::Initialized(initialized) => initialized, + }; + let authority = kb_model::Pubkey(initialized.authority.to_string()); + let blockhash = initialized.blockhash().to_string(); + let lamports_per_signature = initialized.get_lamports_per_signature(); + return std::result::Result::Ok(crate::DurableNonceAccountState { + account: account.clone(), + authority, + blockhash, + lamports_per_signature, + }); +} + +#[cfg(test)] +mod tests { + fn initialized_bytes( + authority: solana_pubkey::Pubkey, + ) -> (std::vec::Vec, std::string::String) { + let source_blockhash = solana_hash::Hash::new_unique(); + let durable_nonce = solana_nonce::state::DurableNonce::from_blockhash(&source_blockhash); + let expected_blockhash = durable_nonce.as_hash().to_string(); + let state = solana_nonce::versions::Versions::new( + solana_nonce::state::State::new_initialized(&authority, durable_nonce, 5_000), + ); + let bytes = wincode::serialize(&state) + .unwrap_or_else(|error| panic!("unexpected nonce serialization error: {error}")); + return (bytes, expected_blockhash); + } + + fn account(value: solana_pubkey::Pubkey) -> kb_model::Pubkey { + return kb_model::Pubkey(value.to_string()); + } + + #[test] + fn current_initialized_nonce_state_is_decoded_exactly() { + let account = account(solana_pubkey::Pubkey::new_unique()); + let authority = solana_pubkey::Pubkey::new_unique(); + let (bytes, expected_blockhash) = initialized_bytes(authority); + assert_eq!(bytes.len(), crate::durable_nonce_account_data_length()); + let parsed = crate::parse_durable_nonce_account( + &account, + &kb_model::ProgramId(solana_sdk_ids::system_program::id().to_string()), + false, + bytes.len() as u64, + bytes.as_slice(), + ) + .unwrap_or_else(|error| panic!("unexpected nonce parse error: {error}")); + assert_eq!(parsed.account(), &account); + assert_eq!(parsed.authority().0, authority.to_string()); + assert_eq!(parsed.blockhash(), expected_blockhash); + assert_eq!(parsed.lamports_per_signature(), 5_000); + } + + #[test] + fn malformed_owner_shape_and_state_are_rejected() { + let saccount = account(solana_pubkey::Pubkey::new_unique()); + let authority = solana_pubkey::Pubkey::new_unique(); + let (bytes, _blockhash) = initialized_bytes(authority); + let owner = kb_model::ProgramId(solana_sdk_ids::system_program::id().to_string()); + assert!( + crate::parse_durable_nonce_account( + &saccount, + &kb_model::ProgramId(solana_pubkey::Pubkey::new_unique().to_string()), + false, + bytes.len() as u64, + bytes.as_slice(), + ) + .is_err() + ); + assert!( + crate::parse_durable_nonce_account( + &saccount, + &owner, + true, + bytes.len() as u64, + bytes.as_slice(), + ) + .is_err() + ); + assert!( + crate::parse_durable_nonce_account( + &saccount, + &owner, + false, + (bytes.len() + 1) as u64, + bytes.as_slice(), + ) + .is_err() + ); + assert!( + crate::parse_durable_nonce_account( + &saccount, + &owner, + false, + bytes.len() as u64, + &bytes[..bytes.len() - 1], + ) + .is_err() + ); + let mut malformed = bytes.clone(); + malformed[0] = 255; + assert!( + crate::parse_durable_nonce_account( + &saccount, + &owner, + false, + malformed.len() as u64, + malformed.as_slice(), + ) + .is_err() + ); + } + + #[test] + fn legacy_and_uninitialized_nonce_states_are_rejected() { + let saccount = account(solana_pubkey::Pubkey::new_unique()); + let owner = kb_model::ProgramId(solana_sdk_ids::system_program::id().to_string()); + let legacy = solana_nonce::versions::Versions::Legacy(std::boxed::Box::new( + solana_nonce::state::State::Initialized(solana_nonce::state::Data::default()), + )); + let legacy_bytes = wincode::serialize(&legacy) + .unwrap_or_else(|error| panic!("unexpected legacy serialization error: {error}")); + assert!( + crate::parse_durable_nonce_account( + &saccount, + &owner, + false, + legacy_bytes.len() as u64, + legacy_bytes.as_slice(), + ) + .is_err() + ); + let uninitialized = + solana_nonce::versions::Versions::new(solana_nonce::state::State::Uninitialized); + let uninitialized_bytes = wincode::serialize(&uninitialized) + .unwrap_or_else(|error| panic!("unexpected state serialization error: {error}")); + assert!( + crate::parse_durable_nonce_account( + &saccount, + &owner, + false, + uninitialized_bytes.len() as u64, + uninitialized_bytes.as_slice(), + ) + .is_err() + ); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_execution_solana/src/transaction.rs b/migration/khadhroony-bot2-reference/kb_execution_solana/src/transaction.rs new file mode 100644 index 0000000..e07d0c1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_execution_solana/src/transaction.rs @@ -0,0 +1,982 @@ +// file: kb_execution_solana/src/transaction.rs +// version: 7 + +//! Legacy and durable nonce Solana transaction assembly, simulation binding and signing. + +use base64::Engine; // rust-rules: trait-import +use std::str::FromStr; // rust-rules: trait-import + +/// Unsigned assembled Solana transaction used for fee estimation and simulation. +#[derive(Clone, Debug)] +pub struct UnsignedSolanaTransaction { + plan: kb_execution_api::PreparedExecutionPlan, + transaction: solana_transaction::Transaction, + message_hash: std::string::String, + blockhash: std::string::String, + required_signer_pubkeys: std::vec::Vec, +} + +impl crate::UnsignedSolanaTransaction { + /// Returns the effective immutable plan represented by this transaction. + /// + /// Durable nonce transactions include the injected nonce-advance instruction. + pub fn plan(&self) -> &kb_execution_api::PreparedExecutionPlan { + return &self.plan; + } + + /// Returns the base58 hash of the exact compiled transaction message. + pub fn message_hash(&self) -> &str { + return self.message_hash.as_str(); + } + + /// Returns the recent blockhash or durable nonce value compiled into the message. + pub fn blockhash(&self) -> &str { + return self.blockhash.as_str(); + } + + /// Returns required signer public keys in compiled message order. + pub fn required_signer_pubkeys(&self) -> &[std::string::String] { + return self.required_signer_pubkeys.as_slice(); + } + + /// Returns the serialized message in base64 for `getFeeForMessage`. + pub fn message_base64(&self) -> std::string::String { + return base64::engine::general_purpose::STANDARD.encode(self.transaction.message_data()); + } + + /// Returns the unsigned serialized transaction in base64 for `simulateTransaction` with `sigVerify = false`. + pub fn transaction_base64(&self) -> kb_core::Result { + let wire_bytes = match serialize_transaction(&self.transaction) { + std::result::Result::Ok(wire_bytes) => wire_bytes, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok( + base64::engine::general_purpose::STANDARD.encode(wire_bytes), + ); + } + + /// Binds a typed RPC simulation result to this exact compiled message. + pub fn bind_simulation( + &self, + simulation: kb_execution_api::ExecutionSimulationResult, + ) -> crate::SolanaSimulationEvidence { + return crate::SolanaSimulationEvidence { + message_hash: self.message_hash.clone(), + blockhash: self.blockhash.clone(), + simulation, + }; + } + + /// Signs the transaction after safety validation of a matching simulation result. + pub fn sign_after_simulation( + mut self, + evidence: &crate::SolanaSimulationEvidence, + signers: &[&dyn solana_signer::Signer], + ) -> kb_core::Result { + if evidence.message_hash != self.message_hash { + return std::result::Result::Err(kb_core::Error::new( + "execution_simulation_message_mismatch", + "the simulation result is not bound to the transaction message being signed", + )); + } + if evidence.blockhash != self.blockhash { + return std::result::Result::Err(kb_core::Error::new( + "execution_simulation_blockhash_mismatch", + "the simulation result is not bound to the blockhash or durable nonce value being signed", + )); + } + let safety_evaluation = match kb_execution_safety::ExecutionSafetyChecker + .evaluate_send(&self.plan, &evidence.simulation) + { + std::result::Result::Ok(evaluation) => evaluation, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if safety_evaluation.decision != kb_execution_safety::ExecutionSafetyDecision::Allow { + return std::result::Result::Err(kb_core::Error::new( + "execution_signing_not_authorized", + format!( + "execution safety decision {:?} forbids signing: {}", + safety_evaluation.decision, + violation_codes(&safety_evaluation.violations) + ), + )); + } + let signers = match resolve_signers(self.required_signer_pubkeys.as_slice(), signers) { + std::result::Result::Ok(signers) => signers, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let recent_blockhash = self.transaction.message.recent_blockhash; + if let std::result::Result::Err(error) = + self.transaction.try_sign(signers.as_slice(), recent_blockhash) + { + return std::result::Result::Err(kb_core::Error::new( + "execution_transaction_sign_failed", + error.to_string(), + )); + } + if !self.transaction.is_signed() { + return std::result::Result::Err(kb_core::Error::new( + "execution_transaction_not_fully_signed", + "the transaction is not fully signed after signer resolution", + )); + } + let wire_bytes = match serialize_transaction(&self.transaction) { + std::result::Result::Ok(wire_bytes) => wire_bytes, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let primary_signature = match self.transaction.signatures.first() { + std::option::Option::Some(signature) => signature.to_string(), + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_primary_signature_missing", + "the signed transaction contains no primary signature", + )); + }, + }; + tracing::info!( + target: crate::TRACING_TARGET, + action = "sign_transaction", + intent_id = %self.plan.intent_id, + operation_code = %self.plan.operation_code, + message_hash = %self.message_hash, + signer_count = self.required_signer_pubkeys.len(), + wire_length = wire_bytes.len(), + "signed prepared Solana transaction" + ); + return std::result::Result::Ok(crate::SignedSolanaTransaction { + transaction: self.transaction, + message_hash: self.message_hash, + primary_signature: kb_model::Signature(primary_signature), + signer_pubkeys: self.required_signer_pubkeys, + wire_bytes, + }); + } +} + +/// Simulation result bound to the exact assembled message that was simulated. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct SolanaSimulationEvidence { + message_hash: std::string::String, + blockhash: std::string::String, + simulation: kb_execution_api::ExecutionSimulationResult, +} + +impl crate::SolanaSimulationEvidence { + /// Returns the base58 hash of the simulated compiled message. + pub fn message_hash(&self) -> &str { + return self.message_hash.as_str(); + } + + /// Returns the recent blockhash or durable nonce value used by the simulated message. + pub fn blockhash(&self) -> &str { + return self.blockhash.as_str(); + } + + /// Returns the typed simulation result. + pub fn simulation(&self) -> &kb_execution_api::ExecutionSimulationResult { + return &self.simulation; + } +} + +/// Signed Solana transaction ready for RPC submission. +#[derive(Clone, Debug)] +pub struct SignedSolanaTransaction { + transaction: solana_transaction::Transaction, + message_hash: std::string::String, + primary_signature: kb_model::Signature, + signer_pubkeys: std::vec::Vec, + wire_bytes: std::vec::Vec, +} + +impl crate::SignedSolanaTransaction { + /// Returns the base58 hash of the signed compiled message. + pub fn message_hash(&self) -> &str { + return self.message_hash.as_str(); + } + + /// Returns the transaction's primary signature. + pub fn primary_signature(&self) -> &kb_model::Signature { + return &self.primary_signature; + } + + /// Returns signer public keys in signature order. + pub fn signer_pubkeys(&self) -> &[std::string::String] { + return self.signer_pubkeys.as_slice(); + } + + /// Returns the signed transaction wire length. + pub fn wire_length(&self) -> usize { + return self.wire_bytes.len(); + } + + /// Returns the signed transaction in base64 for future RPC submission. + pub fn transaction_base64(&self) -> std::string::String { + return base64::engine::general_purpose::STANDARD.encode(self.wire_bytes.as_slice()); + } + + /// Verifies every signature against the compiled message. + pub fn verify_signatures(&self) -> kb_core::Result<()> { + return match self.transaction.verify() { + std::result::Result::Ok(()) => std::result::Result::Ok(()), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "execution_transaction_signature_verification_failed", + error.to_string(), + )), + }; + } +} + +/// Builds an unsigned legacy Solana transaction from a prepared execution plan. +pub fn build_legacy_transaction( + plan: &kb_execution_api::PreparedExecutionPlan, + recent_blockhash: &str, +) -> kb_core::Result { + if plan.policy.blockhash.kind != kb_execution_api::ExecutionBlockhashKind::Latest { + return std::result::Result::Err(kb_core::Error::new( + "execution_durable_nonce_requires_dedicated_assembly", + "durable nonce plans require build_durable_nonce_transaction", + )); + } + if plan.instructions.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_plan_has_no_instructions", + "cannot assemble a Solana transaction without instructions", + )); + } + let fee_payer = match parse_pubkey(plan.fee_payer.0.as_str(), "execution fee payer") { + std::result::Result::Ok(fee_payer) => fee_payer, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let blockhash = match solana_hash::Hash::from_str(recent_blockhash) { + std::result::Result::Ok(blockhash) => blockhash, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_recent_blockhash_invalid", + format!("invalid recent blockhash: {error}"), + )); + }, + }; + let mut instructions = std::vec::Vec::with_capacity(plan.instructions.len()); + for planned_instruction in &plan.instructions { + let instruction = match convert_instruction(planned_instruction) { + std::result::Result::Ok(instruction) => instruction, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + instructions.push(instruction); + } + let message = solana_message::Message::new_with_blockhash( + instructions.as_slice(), + std::option::Option::Some(&fee_payer), + &blockhash, + ); + let compiled_signers = message + .signer_keys() + .iter() + .map(|pubkey| return pubkey.to_string()) + .collect::>(); + match validate_compiled_signers(plan, compiled_signers.as_slice()) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let transaction = solana_transaction::Transaction::new_unsigned(message); + let wire_bytes = match serialize_transaction(&transaction) { + std::result::Result::Ok(wire_bytes) => wire_bytes, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let message_hash = transaction.message.hash().to_string(); + tracing::debug!( + target: crate::TRACING_TARGET, + action = "assemble_transaction", + intent_id = %plan.intent_id, + operation_code = %plan.operation_code, + instruction_count = plan.instructions.len(), + signer_count = compiled_signers.len(), + message_hash = %message_hash, + wire_length = wire_bytes.len(), + "assembled unsigned legacy Solana transaction" + ); + return std::result::Result::Ok(crate::UnsignedSolanaTransaction { + plan: plan.clone(), + transaction, + message_hash, + blockhash: blockhash.to_string(), + required_signer_pubkeys: compiled_signers, + }); +} + +/// Builds an unsigned transaction that consumes a validated durable nonce state. +pub fn build_durable_nonce_transaction( + plan: &kb_execution_api::PreparedExecutionPlan, + nonce_state: &crate::DurableNonceAccountState, +) -> kb_core::Result { + if plan.policy.blockhash.kind != kb_execution_api::ExecutionBlockhashKind::DurableNonce { + return std::result::Result::Err(kb_core::Error::new( + "execution_durable_nonce_policy_required", + "the dedicated durable nonce assembly path requires a DurableNonce blockhash policy", + )); + } + if plan.policy.blockhash.max_age_slots.is_some() { + return std::result::Result::Err(kb_core::Error::new( + "execution_durable_nonce_has_blockhash_age", + "durable nonce assembly cannot use a recent blockhash age limit", + )); + } + if plan.instructions.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_plan_has_no_instructions", + "cannot assemble a durable nonce transaction without operation instructions", + )); + } + let policy_nonce_account = match &plan.policy.blockhash.nonce_account { + std::option::Option::Some(account) => account, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_durable_nonce_account_missing", + "durable nonce assembly requires the nonce account in the execution policy", + )); + }, + }; + let policy_nonce_authority = match &plan.policy.blockhash.nonce_authority { + std::option::Option::Some(authority) => authority, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_durable_nonce_authority_missing", + "durable nonce assembly requires the nonce authority in the execution policy", + )); + }, + }; + if policy_nonce_account != nonce_state.account() { + return std::result::Result::Err(kb_core::Error::new( + "execution_durable_nonce_account_mismatch", + format!( + "execution policy nonce account {} differs from validated account {}", + policy_nonce_account.0, + nonce_state.account().0 + ), + )); + } + if policy_nonce_authority != nonce_state.authority() { + return std::result::Result::Err(kb_core::Error::new( + "execution_durable_nonce_authority_mismatch", + format!( + "execution policy nonce authority {} differs from validated authority {}", + policy_nonce_authority.0, + nonce_state.authority().0 + ), + )); + } + if !plan + .required_signers + .iter() + .any(|required| return &required.pubkey == policy_nonce_authority) + { + return std::result::Result::Err(kb_core::Error::new( + "execution_durable_nonce_authority_not_declared", + format!( + "durable nonce authority {} is missing from required signers", + policy_nonce_authority.0 + ), + )); + } + let fee_payer = match parse_pubkey(plan.fee_payer.0.as_str(), "execution fee payer") { + std::result::Result::Ok(fee_payer) => fee_payer, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let nonce_account = match parse_pubkey(policy_nonce_account.0.as_str(), "durable nonce account") + { + std::result::Result::Ok(nonce_account) => nonce_account, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let nonce_authority = + match parse_pubkey(policy_nonce_authority.0.as_str(), "durable nonce authority") { + std::result::Result::Ok(nonce_authority) => nonce_authority, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let nonce_blockhash = match solana_hash::Hash::from_str(nonce_state.blockhash()) { + std::result::Result::Ok(blockhash) => blockhash, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_durable_nonce_blockhash_invalid", + format!("invalid durable nonce value: {error}"), + )); + }, + }; + let advance_instruction = solana_system_interface::instruction::advance_nonce_account( + &nonce_account, + &nonce_authority, + ); + let mut effective_plan = plan.clone(); + effective_plan.instructions.insert( + 0, + planned_instruction(crate::DURABLE_NONCE_ADVANCE_OPERATION, &advance_instruction), + ); + let mut operation_instructions = std::vec::Vec::with_capacity(plan.instructions.len()); + for planned_instruction in &plan.instructions { + let instruction = match convert_instruction(planned_instruction) { + std::result::Result::Ok(instruction) => instruction, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + operation_instructions.push(instruction); + } + let mut message = solana_message::Message::new_with_nonce( + operation_instructions, + std::option::Option::Some(&fee_payer), + &nonce_account, + &nonce_authority, + ); + message.recent_blockhash = nonce_blockhash; + let compiled_signers = message + .signer_keys() + .iter() + .map(|pubkey| return pubkey.to_string()) + .collect::>(); + match validate_compiled_signers(&effective_plan, compiled_signers.as_slice()) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let transaction = solana_transaction::Transaction::new_unsigned(message); + if solana_transaction::uses_durable_nonce(&transaction).is_none() { + return std::result::Result::Err(kb_core::Error::new( + "execution_durable_nonce_marker_missing", + "the assembled transaction is not recognized as a durable nonce transaction", + )); + } + let wire_bytes = match serialize_transaction(&transaction) { + std::result::Result::Ok(wire_bytes) => wire_bytes, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let message_hash = transaction.message.hash().to_string(); + tracing::debug!( + target: crate::TRACING_TARGET, + action = "assemble_durable_nonce_transaction", + intent_id = %effective_plan.intent_id, + operation_code = %effective_plan.operation_code, + nonce_account = %policy_nonce_account.0, + nonce_authority = %policy_nonce_authority.0, + nonce_blockhash = %nonce_state.blockhash(), + instruction_count = effective_plan.instructions.len(), + signer_count = compiled_signers.len(), + message_hash = %message_hash, + wire_length = wire_bytes.len(), + "assembled unsigned durable nonce Solana transaction" + ); + return std::result::Result::Ok(crate::UnsignedSolanaTransaction { + plan: effective_plan, + transaction, + message_hash, + blockhash: nonce_state.blockhash().to_string(), + required_signer_pubkeys: compiled_signers, + }); +} + +fn convert_instruction( + planned: &kb_execution_api::PlannedInstruction, +) -> kb_core::Result { + let program_id = + match parse_pubkey(planned.program_id.0.as_str(), "planned instruction program id") { + std::result::Result::Ok(program_id) => program_id, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut accounts = std::vec::Vec::with_capacity(planned.accounts.len()); + for planned_account in &planned.accounts { + let pubkey = + match parse_pubkey(planned_account.pubkey.0.as_str(), "planned instruction account") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + accounts.push(solana_instruction::AccountMeta { + pubkey, + is_signer: planned_account.is_signer, + is_writable: planned_account.is_writable, + }); + } + return std::result::Result::Ok(solana_instruction::Instruction { + program_id, + accounts, + data: planned.data.clone(), + }); +} + +fn planned_instruction( + operation_code: &str, + instruction: &solana_instruction::Instruction, +) -> kb_execution_api::PlannedInstruction { + return kb_execution_api::PlannedInstruction { + program_id: kb_model::ProgramId(instruction.program_id.to_string()), + operation_code: operation_code.to_string(), + accounts: instruction + .accounts + .iter() + .map(|account| { + return kb_execution_api::PlannedAccount { + pubkey: kb_model::Pubkey(account.pubkey.to_string()), + is_signer: account.is_signer, + is_writable: account.is_writable, + }; + }) + .collect(), + data: instruction.data.clone(), + }; +} + +fn parse_pubkey(value: &str, field_name: &str) -> kb_core::Result { + return match solana_pubkey::Pubkey::from_str(value) { + std::result::Result::Ok(pubkey) => std::result::Result::Ok(pubkey), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "execution_pubkey_invalid", + format!("invalid {field_name} {value}: {error}"), + )), + }; +} + +fn validate_compiled_signers( + plan: &kb_execution_api::PreparedExecutionPlan, + compiled_signers: &[std::string::String], +) -> kb_core::Result<()> { + let declared_signers = plan + .required_signers + .iter() + .map(|required| return required.pubkey.0.as_str()) + .collect::>(); + let compiled_signer_set = compiled_signers + .iter() + .map(std::string::String::as_str) + .collect::>(); + if declared_signers != compiled_signer_set { + return std::result::Result::Err(kb_core::Error::new( + "execution_compiled_signer_contract_mismatch", + format!( + "declared signers [{}] differ from compiled signers [{}]", + declared_signers.into_iter().collect::>().join(","), + compiled_signer_set.into_iter().collect::>().join(",") + ), + )); + } + return std::result::Result::Ok(()); +} + +fn resolve_signers<'a>( + required_signers: &[std::string::String], + signers: &'a [&'a dyn solana_signer::Signer], +) -> kb_core::Result> { + let mut signer_by_pubkey = std::collections::BTreeMap::new(); + for signer in signers { + let public_key = signer.pubkey().to_string(); + if signer_by_pubkey.insert(public_key.clone(), *signer).is_some() { + return std::result::Result::Err(kb_core::Error::new( + "execution_signer_duplicate", + format!("signer {public_key} was supplied more than once"), + )); + } + } + if signer_by_pubkey.len() != required_signers.len() { + return std::result::Result::Err(kb_core::Error::new( + "execution_signer_count_mismatch", + format!( + "{} signers were supplied for {} required signers", + signer_by_pubkey.len(), + required_signers.len() + ), + )); + } + let mut resolved = std::vec::Vec::with_capacity(required_signers.len()); + for required_signer in required_signers { + let signer = match signer_by_pubkey.get(required_signer) { + std::option::Option::Some(signer) => *signer, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_required_signer_missing", + format!("required signer {required_signer} was not supplied"), + )); + }, + }; + resolved.push(signer); + } + return std::result::Result::Ok(resolved); +} + +fn serialize_transaction( + transaction: &solana_transaction::Transaction, +) -> kb_core::Result> { + let wire_bytes = match wincode::serialize(transaction) { + std::result::Result::Ok(wire_bytes) => wire_bytes, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_transaction_serialize_failed", + error.to_string(), + )); + }, + }; + if wire_bytes.len() > crate::MAX_TRANSACTION_WIRE_BYTES { + return std::result::Result::Err(kb_core::Error::new( + "execution_transaction_wire_size_exceeded", + format!( + "serialized transaction length {} exceeds the Solana packet limit {}", + wire_bytes.len(), + crate::MAX_TRANSACTION_WIRE_BYTES + ), + )); + } + return std::result::Result::Ok(wire_bytes); +} + +fn violation_codes( + violations: &[kb_execution_safety::ExecutionSafetyViolation], +) -> std::string::String { + return violations + .iter() + .map(|violation| return violation.code.as_str()) + .collect::>() + .join(","); +} + +#[cfg(test)] +mod tests { + use solana_signer::Signer; // rust-rules: trait-import + use std::str::FromStr; // rust-rules: trait-import + + fn wallet(alias: &str) -> kb_wallet::TemporaryWallet { + let alias = kb_wallet::WalletAlias::parse(alias) + .unwrap_or_else(|error| panic!("unexpected alias error: {error}")); + return kb_wallet::TemporaryWallet::generate(alias); + } + + fn transfer_plan( + wallet: &kb_wallet::TemporaryWallet, + dry_run: bool, + lamports: u64, + ) -> kb_execution_api::PreparedExecutionPlan { + let payer = kb_model::Pubkey(wallet.public_key()); + let destination = kb_model::Pubkey(solana_keypair::Keypair::new().pubkey().to_string()); + let intent = kb_executor_solana_core::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("transfer-test"), + fee_payer: payer.clone(), + operation: kb_executor_solana_core::SolanaCoreOperation::SystemTransfer { + from: payer.clone(), + to: destination, + lamports, + }, + policy: kb_execution_api::ExecutionPolicy { + dry_run, + authorized_signers: vec![payer], + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(lamports), + max_fee_lamports: std::option::Option::Some(10_000), + max_compute_unit_price_micro_lamports: std::option::Option::Some(100), + }, + ..kb_execution_api::ExecutionPolicy::default() + }, + }; + return kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &kb_executor_solana_core::SolanaCoreExecutor, + &intent, + ) + .unwrap_or_else(|error| panic!("unexpected plan error: {error}")); + } + + fn durable_transfer_plan( + payer: &kb_wallet::TemporaryWallet, + nonce_authority: &kb_wallet::TemporaryWallet, + nonce_account: &kb_model::Pubkey, + ) -> kb_execution_api::PreparedExecutionPlan { + let payer_pubkey = kb_model::Pubkey(payer.public_key()); + let authority_pubkey = kb_model::Pubkey(nonce_authority.public_key()); + let destination = kb_model::Pubkey(solana_keypair::Keypair::new().pubkey().to_string()); + let intent = kb_executor_solana_core::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("durable-transfer-test"), + fee_payer: payer_pubkey.clone(), + operation: kb_executor_solana_core::SolanaCoreOperation::SystemTransfer { + from: payer_pubkey.clone(), + to: destination, + lamports: 100, + }, + policy: kb_execution_api::ExecutionPolicy { + dry_run: false, + authorized_signers: vec![payer_pubkey, authority_pubkey.clone()], + blockhash: kb_execution_api::ExecutionBlockhashPolicy { + kind: kb_execution_api::ExecutionBlockhashKind::DurableNonce, + max_age_slots: std::option::Option::None, + nonce_account: std::option::Option::Some(nonce_account.clone()), + nonce_authority: std::option::Option::Some(authority_pubkey), + }, + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(100), + max_fee_lamports: std::option::Option::Some(10_000), + max_compute_unit_price_micro_lamports: std::option::Option::Some(100), + }, + ..kb_execution_api::ExecutionPolicy::default() + }, + }; + return kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &kb_executor_solana_core::SolanaCoreExecutor, + &intent, + ) + .unwrap_or_else(|error| panic!("unexpected durable plan error: {error}")); + } + + fn nonce_state( + nonce_account: &kb_model::Pubkey, + nonce_authority: &kb_wallet::TemporaryWallet, + ) -> crate::DurableNonceAccountState { + let source_blockhash = solana_hash::Hash::new_unique(); + let durable_nonce = solana_nonce::state::DurableNonce::from_blockhash(&source_blockhash); + let authority = solana_pubkey::Pubkey::from_str(nonce_authority.public_key().as_str()) + .unwrap_or_else(|error| panic!("invalid authority fixture: {error}")); + let versions = solana_nonce::versions::Versions::new( + solana_nonce::state::State::new_initialized(&authority, durable_nonce, 5_000), + ); + let bytes = wincode::serialize(&versions) + .unwrap_or_else(|error| panic!("unexpected nonce serialization error: {error}")); + return crate::parse_durable_nonce_account( + nonce_account, + &kb_model::ProgramId(solana_sdk_ids::system_program::id().to_string()), + false, + bytes.len() as u64, + bytes.as_slice(), + ) + .unwrap_or_else(|error| panic!("unexpected nonce parse error: {error}")); + } + + fn successful_simulation() -> kb_execution_api::ExecutionSimulationResult { + return kb_execution_api::ExecutionSimulationResult { + simulated: true, + success: true, + cluster: kb_execution_api::ExecutionCluster::Devnet, + blockhash_kind: kb_execution_api::ExecutionBlockhashKind::Latest, + blockhash_age_slots: std::option::Option::Some(1), + replacement_blockhash: std::option::Option::None, + replacement_last_valid_block_height: std::option::Option::None, + nonce_account: std::option::Option::None, + nonce_authority: std::option::Option::None, + units_consumed: std::option::Option::Some(500), + estimated_fee_lamports: std::option::Option::Some(5_000), + logs: std::vec::Vec::new(), + error: std::option::Option::None, + }; + } + + fn successful_nonce_simulation( + nonce_account: &kb_model::Pubkey, + nonce_authority: &kb_wallet::TemporaryWallet, + ) -> kb_execution_api::ExecutionSimulationResult { + return kb_execution_api::ExecutionSimulationResult { + simulated: true, + success: true, + cluster: kb_execution_api::ExecutionCluster::Devnet, + blockhash_kind: kb_execution_api::ExecutionBlockhashKind::DurableNonce, + blockhash_age_slots: std::option::Option::None, + replacement_blockhash: std::option::Option::None, + replacement_last_valid_block_height: std::option::Option::None, + nonce_account: std::option::Option::Some(nonce_account.clone()), + nonce_authority: std::option::Option::Some(kb_model::Pubkey( + nonce_authority.public_key(), + )), + units_consumed: std::option::Option::Some(700), + estimated_fee_lamports: std::option::Option::Some(10_000), + logs: std::vec::Vec::new(), + error: std::option::Option::None, + }; + } + + #[test] + fn legacy_transaction_preserves_plan_and_serializes_for_rpc() { + let wallet = wallet("payer"); + let plan = transfer_plan(&wallet, false, 100); + let blockhash = solana_hash::Hash::new_unique().to_string(); + let unsigned = crate::build_legacy_transaction(&plan, blockhash.as_str()) + .unwrap_or_else(|error| panic!("unexpected assembly error: {error}")); + assert_eq!(unsigned.plan().intent_id, plan.intent_id); + assert_eq!(unsigned.required_signer_pubkeys(), &[wallet.public_key()]); + assert!(!unsigned.message_hash().is_empty()); + assert!(!unsigned.message_base64().is_empty()); + assert!( + !unsigned + .transaction_base64() + .unwrap_or_else(|error| panic!("unexpected serialization error: {error}")) + .is_empty() + ); + } + + #[test] + fn matching_simulation_and_signer_produce_verified_signature() { + let wallet = wallet("payer"); + let plan = transfer_plan(&wallet, false, 100); + let blockhash = solana_hash::Hash::new_unique().to_string(); + let unsigned = crate::build_legacy_transaction(&plan, blockhash.as_str()) + .unwrap_or_else(|error| panic!("unexpected assembly error: {error}")); + let evidence = unsigned.bind_simulation(successful_simulation()); + let signed = unsigned + .sign_after_simulation(&evidence, &[wallet.as_signer()]) + .unwrap_or_else(|error| panic!("unexpected signing error: {error}")); + signed + .verify_signatures() + .unwrap_or_else(|error| panic!("unexpected verification error: {error}")); + assert!(!signed.primary_signature().0.is_empty()); + assert_eq!(signed.signer_pubkeys(), &[wallet.public_key()]); + assert!(signed.wire_length() <= crate::MAX_TRANSACTION_WIRE_BYTES); + assert!(!signed.transaction_base64().is_empty()); + } + + #[test] + fn dry_run_policy_blocks_signing_after_successful_simulation() { + let wallet = wallet("payer"); + let plan = transfer_plan(&wallet, true, 100); + let blockhash = solana_hash::Hash::new_unique().to_string(); + let unsigned = crate::build_legacy_transaction(&plan, blockhash.as_str()) + .unwrap_or_else(|error| panic!("unexpected assembly error: {error}")); + let evidence = unsigned.bind_simulation(successful_simulation()); + let result = unsigned.sign_after_simulation(&evidence, &[wallet.as_signer()]); + assert!(result.is_err()); + } + + #[test] + fn simulation_evidence_from_another_message_is_rejected() { + let wallet = wallet("payer"); + let plan = transfer_plan(&wallet, false, 100); + let first_hash = solana_hash::Hash::new_unique().to_string(); + let second_hash = solana_hash::Hash::new_unique().to_string(); + let first = crate::build_legacy_transaction(&plan, first_hash.as_str()) + .unwrap_or_else(|error| panic!("unexpected first assembly error: {error}")); + let second = crate::build_legacy_transaction(&plan, second_hash.as_str()) + .unwrap_or_else(|error| panic!("unexpected second assembly error: {error}")); + let evidence = first.bind_simulation(successful_simulation()); + let result = second.sign_after_simulation(&evidence, &[wallet.as_signer()]); + assert!(result.is_err()); + } + + #[test] + fn missing_extra_and_duplicate_signers_are_rejected() { + let tmpwallet = wallet("payer"); + let extra = wallet("extra"); + let plan = transfer_plan(&tmpwallet, false, 100); + let blockhash = solana_hash::Hash::new_unique().to_string(); + let unsigned = crate::build_legacy_transaction(&plan, blockhash.as_str()) + .unwrap_or_else(|error| panic!("unexpected assembly error: {error}")); + let evidence = unsigned.bind_simulation(successful_simulation()); + assert!(unsigned.clone().sign_after_simulation(&evidence, &[]).is_err()); + assert!( + unsigned + .clone() + .sign_after_simulation(&evidence, &[tmpwallet.as_signer(), extra.as_signer()],) + .is_err() + ); + assert!( + unsigned + .sign_after_simulation(&evidence, &[tmpwallet.as_signer(), tmpwallet.as_signer()],) + .is_err() + ); + } + + #[test] + fn durable_nonce_transaction_injects_exact_advance_and_signs() { + let payer = wallet("durable-payer"); + let authority = wallet("durable-authority"); + let nonce_account = kb_model::Pubkey(solana_pubkey::Pubkey::new_unique().to_string()); + let plan = durable_transfer_plan(&payer, &authority, &nonce_account); + assert_eq!(plan.instructions.len(), 1); + assert_eq!(plan.required_signers.len(), 2); + let nonce_state = nonce_state(&nonce_account, &authority); + let unsigned = crate::build_durable_nonce_transaction(&plan, &nonce_state) + .unwrap_or_else(|error| panic!("unexpected durable assembly error: {error}")); + assert_eq!(plan.instructions.len(), 1); + assert_eq!(unsigned.plan().instructions.len(), 2); + assert_eq!(unsigned.blockhash(), nonce_state.blockhash()); + let authority_address = solana_pubkey::Pubkey::from_str(authority.public_key().as_str()) + .unwrap_or_else(|error| panic!("invalid authority fixture: {error}")); + let nonce_address = solana_pubkey::Pubkey::from_str(nonce_account.0.as_str()) + .unwrap_or_else(|error| panic!("invalid nonce fixture: {error}")); + let expected_advance = solana_system_interface::instruction::advance_nonce_account( + &nonce_address, + &authority_address, + ); + let actual_advance = &unsigned.plan().instructions[0]; + assert_eq!(actual_advance.operation_code, crate::DURABLE_NONCE_ADVANCE_OPERATION); + assert_eq!(actual_advance.program_id.0, expected_advance.program_id.to_string()); + assert_eq!(actual_advance.data, expected_advance.data); + assert_eq!(actual_advance.accounts.len(), expected_advance.accounts.len()); + for (actual, expected) in + actual_advance.accounts.iter().zip(expected_advance.accounts.iter()) + { + assert_eq!(actual.pubkey.0, expected.pubkey.to_string()); + assert_eq!(actual.is_signer, expected.is_signer); + assert_eq!(actual.is_writable, expected.is_writable); + } + assert!(solana_transaction::uses_durable_nonce(&unsigned.transaction).is_some()); + let compiled_first = &unsigned.transaction.message.instructions[0]; + assert_eq!( + unsigned.transaction.message.account_keys[compiled_first.program_id_index as usize], + expected_advance.program_id + ); + assert_eq!(compiled_first.data, expected_advance.data); + let signer_set = unsigned + .required_signer_pubkeys() + .iter() + .cloned() + .collect::>(); + assert_eq!( + signer_set, + std::collections::BTreeSet::from([payer.public_key(), authority.public_key()]) + ); + let evidence = + unsigned.bind_simulation(successful_nonce_simulation(&nonce_account, &authority)); + assert_eq!(evidence.blockhash(), nonce_state.blockhash()); + let signed = unsigned + .sign_after_simulation(&evidence, &[payer.as_signer(), authority.as_signer()]) + .unwrap_or_else(|error| panic!("unexpected durable signing error: {error}")); + signed + .verify_signatures() + .unwrap_or_else(|error| panic!("unexpected signature verification error: {error}")); + } + + #[test] + fn durable_nonce_assembly_rejects_mismatched_state_and_simulation() { + let payer = wallet("mismatch-payer"); + let authority = wallet("mismatch-authority"); + let wrong_authority = wallet("wrong-authority"); + let nonce_account = kb_model::Pubkey(solana_pubkey::Pubkey::new_unique().to_string()); + let plan = durable_transfer_plan(&payer, &authority, &nonce_account); + let wrong_state = nonce_state(&nonce_account, &wrong_authority); + assert!(crate::build_durable_nonce_transaction(&plan, &wrong_state).is_err()); + let nonce_state = nonce_state(&nonce_account, &authority); + let unsigned = crate::build_durable_nonce_transaction(&plan, &nonce_state) + .unwrap_or_else(|error| panic!("unexpected durable assembly error: {error}")); + let latest_evidence = unsigned.bind_simulation(successful_simulation()); + assert!( + unsigned + .sign_after_simulation( + &latest_evidence, + &[payer.as_signer(), authority.as_signer()], + ) + .is_err() + ); + let mut undeclared = plan.clone(); + undeclared + .required_signers + .retain(|required| return required.pubkey.0 != authority.public_key()); + assert!(crate::build_durable_nonce_transaction(&undeclared, &nonce_state).is_err()); + } + + #[test] + fn durable_nonce_plan_requires_dedicated_assembly() { + let wallet = wallet("payer"); + let mut plan = transfer_plan(&wallet, false, 100); + plan.policy.blockhash.kind = kb_execution_api::ExecutionBlockhashKind::DurableNonce; + plan.policy.blockhash.max_age_slots = std::option::Option::None; + plan.policy.blockhash.nonce_account = + std::option::Option::Some(kb_model::Pubkey(wallet.public_key())); + plan.policy.blockhash.nonce_authority = + std::option::Option::Some(kb_model::Pubkey(wallet.public_key())); + let nonce = solana_hash::Hash::new_unique().to_string(); + assert!(crate::build_legacy_transaction(&plan, nonce.as_str()).is_err()); + } + + #[test] + fn invalid_blockhash_and_signer_contract_are_rejected() { + let wallet = wallet("payer"); + let mut plan = transfer_plan(&wallet, false, 100); + assert!(crate::build_legacy_transaction(&plan, "invalid").is_err()); + plan.required_signers.clear(); + let blockhash = solana_hash::Hash::new_unique().to_string(); + assert!(crate::build_legacy_transaction(&plan, blockhash.as_str()).is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/Cargo.toml new file mode 100644 index 0000000..870c41c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_adapter_saber_decimal_wrapper/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_adapter_saber_decimal_wrapper" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/README.md b/migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/README.md new file mode 100644 index 0000000..41694cd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_adapter_saber_decimal_wrapper + +Ce crate réserve l'exécuteur de la surface `adapter_saber_decimal_wrapper`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/src/constants.rs new file mode 100644 index 0000000..8b7d832 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_adapter_saber_decimal_wrapper/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_adapter_saber_decimal_wrapper"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/src/executor.rs new file mode 100644 index 0000000..4466b77 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_adapter_saber_decimal_wrapper/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_adapter_saber_decimal_wrapper` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AdapterSaberDecimalWrapperExecutor; + +impl kb_execution_api::InstructionExecutor for crate::AdapterSaberDecimalWrapperExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_adapter_saber_decimal_wrapper"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ADAPTER_SABER_DECIMAL_WRAPPER_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_adapter_saber_decimal_wrapper"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/src/lib.rs new file mode 100644 index 0000000..f0c5ba2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_adapter_saber_decimal_wrapper/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_adapter_saber_decimal_wrapper/src/lib.rs +// version: 2 + +//! Executor crate for `adapter_saber_decimal_wrapper`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AdapterSaberDecimalWrapperExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/Cargo.toml new file mode 100644 index 0000000..f10f457 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_admin_jupiter_lock/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_admin_jupiter_lock" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/README.md b/migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/README.md new file mode 100644 index 0000000..e481d94 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_admin_jupiter_lock + +Ce crate réserve l'exécuteur de la surface `admin_jupiter_lock`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/src/constants.rs new file mode 100644 index 0000000..8a03e76 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_admin_jupiter_lock/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_admin_jupiter_lock"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/src/executor.rs new file mode 100644 index 0000000..b491e5c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_admin_jupiter_lock/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_admin_jupiter_lock` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AdminJupiterLockExecutor; + +impl kb_execution_api::InstructionExecutor for crate::AdminJupiterLockExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_admin_jupiter_lock"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ADMIN_JUPITER_LOCK_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_admin_jupiter_lock"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/src/lib.rs new file mode 100644 index 0000000..bf5d751 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_admin_jupiter_lock/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_admin_jupiter_lock/src/lib.rs +// version: 2 + +//! Executor crate for `admin_jupiter_lock`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AdminJupiterLockExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/Cargo.toml new file mode 100644 index 0000000..53caef0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_admin_pump_fees/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_admin_pump_fees" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/README.md b/migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/README.md new file mode 100644 index 0000000..ec315f6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_admin_pump_fees + +Ce crate réserve l'exécuteur de la surface `admin_pump_fees`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/src/constants.rs new file mode 100644 index 0000000..251c788 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_admin_pump_fees/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_admin_pump_fees"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/src/executor.rs new file mode 100644 index 0000000..22e02d3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_admin_pump_fees/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_admin_pump_fees` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AdminPumpFeesExecutor; + +impl kb_execution_api::InstructionExecutor for crate::AdminPumpFeesExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_admin_pump_fees"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ADMIN_PUMP_FEES_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_admin_pump_fees"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/src/lib.rs new file mode 100644 index 0000000..f964265 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_admin_pump_fees/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_admin_pump_fees/src/lib.rs +// version: 2 + +//! Executor crate for `admin_pump_fees`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AdminPumpFeesExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/Cargo.toml new file mode 100644 index 0000000..3083669 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_aldrin_v1/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_aldrin_v1" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/README.md new file mode 100644 index 0000000..ac2b2f5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_aldrin_v1 + +Ce crate réserve l'exécuteur de la surface `amm_aldrin_v1`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/src/constants.rs new file mode 100644 index 0000000..325373c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_aldrin_v1/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_aldrin_v1"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/src/executor.rs new file mode 100644 index 0000000..71a9f34 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_aldrin_v1/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_aldrin_v1` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmAldrinV1Executor; + +impl kb_execution_api::InstructionExecutor for crate::AmmAldrinV1Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_aldrin_v1"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_aldrin_v1"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/src/lib.rs new file mode 100644 index 0000000..0fc4ff3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v1/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_aldrin_v1/src/lib.rs +// version: 2 + +//! Executor crate for `amm_aldrin_v1`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmAldrinV1Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/Cargo.toml new file mode 100644 index 0000000..d2400fe --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_aldrin_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_aldrin_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/README.md new file mode 100644 index 0000000..4691508 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_aldrin_v2 + +Ce crate réserve l'exécuteur de la surface `amm_aldrin_v2`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/src/constants.rs new file mode 100644 index 0000000..f7ca694 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_aldrin_v2/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_aldrin_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/src/executor.rs new file mode 100644 index 0000000..a6eb044 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_aldrin_v2/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_aldrin_v2` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmAldrinV2Executor; + +impl kb_execution_api::InstructionExecutor for crate::AmmAldrinV2Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_aldrin_v2"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_aldrin_v2"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/src/lib.rs new file mode 100644 index 0000000..8d9a890 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_aldrin_v2/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_aldrin_v2/src/lib.rs +// version: 2 + +//! Executor crate for `amm_aldrin_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmAldrinV2Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/Cargo.toml new file mode 100644 index 0000000..10be7a0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_alphaq/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_alphaq" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/README.md new file mode 100644 index 0000000..515635e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_alphaq + +Ce crate réserve l'exécuteur de la surface `amm_alphaq`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/src/constants.rs new file mode 100644 index 0000000..4006812 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_alphaq/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_alphaq"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/src/executor.rs new file mode 100644 index 0000000..0324834 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_alphaq/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_alphaq` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmAlphaqExecutor; + +impl kb_execution_api::InstructionExecutor for crate::AmmAlphaqExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_alphaq"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_ALPHAQ_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_alphaq"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/src/lib.rs new file mode 100644 index 0000000..8a50f6c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_alphaq/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_alphaq/src/lib.rs +// version: 2 + +//! Executor crate for `amm_alphaq`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmAlphaqExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_believe/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_believe/Cargo.toml new file mode 100644 index 0000000..80bf501 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_believe/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_believe/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_believe" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_believe/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_believe/README.md new file mode 100644 index 0000000..7ce4aba --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_believe/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_believe + +Ce crate réserve l'exécuteur de la surface `amm_believe`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_believe/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_believe/src/constants.rs new file mode 100644 index 0000000..5d8dc39 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_believe/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_believe/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_believe"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_believe/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_believe/src/executor.rs new file mode 100644 index 0000000..87cfb49 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_believe/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_believe/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_believe` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmBelieveExecutor; + +impl kb_execution_api::InstructionExecutor for crate::AmmBelieveExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_believe"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_BELIEVE_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_believe"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_believe/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_believe/src/lib.rs new file mode 100644 index 0000000..b970730 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_believe/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_believe/src/lib.rs +// version: 2 + +//! Executor crate for `amm_believe`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmBelieveExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/Cargo.toml new file mode 100644 index 0000000..b527628 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_bonk_swap/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_bonk_swap" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/README.md new file mode 100644 index 0000000..6bb0c93 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_bonk_swap + +Ce crate réserve l'exécuteur de la surface `amm_bonk_swap`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/src/constants.rs new file mode 100644 index 0000000..c2eb63c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_bonk_swap/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_bonk_swap"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/src/executor.rs new file mode 100644 index 0000000..b125dbe --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_bonk_swap/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_bonk_swap` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmBonkSwapExecutor; + +impl kb_execution_api::InstructionExecutor for crate::AmmBonkSwapExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_bonk_swap"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_BONK_SWAP_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_bonk_swap"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/src/lib.rs new file mode 100644 index 0000000..722cdee --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_bonk_swap/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_bonk_swap/src/lib.rs +// version: 2 + +//! Executor crate for `amm_bonk_swap`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmBonkSwapExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/Cargo.toml new file mode 100644 index 0000000..b37fcfd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_fluxbeam/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_fluxbeam" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/README.md new file mode 100644 index 0000000..c001614 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_fluxbeam + +Ce crate réserve l'exécuteur de la surface `amm_fluxbeam`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/src/constants.rs new file mode 100644 index 0000000..d1814a5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_fluxbeam/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_fluxbeam"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/src/executor.rs new file mode 100644 index 0000000..802a3d6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_fluxbeam/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_fluxbeam` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmFluxbeamExecutor; + +impl kb_execution_api::InstructionExecutor for crate::AmmFluxbeamExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_fluxbeam"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_FLUXBEAM_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_fluxbeam"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/src/lib.rs new file mode 100644 index 0000000..f42df5d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_fluxbeam/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_fluxbeam/src/lib.rs +// version: 2 + +//! Executor crate for `amm_fluxbeam`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmFluxbeamExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/Cargo.toml new file mode 100644 index 0000000..d9bbd4c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_goon_fi/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_goon_fi" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/README.md new file mode 100644 index 0000000..d8ebb6f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_goon_fi + +Ce crate réserve l'exécuteur de la surface `amm_goon_fi`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/src/constants.rs new file mode 100644 index 0000000..fec0c11 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_goon_fi/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_goon_fi"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/src/executor.rs new file mode 100644 index 0000000..e1a9519 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_goon_fi/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_goon_fi` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmGoonFiExecutor; + +impl kb_execution_api::InstructionExecutor for crate::AmmGoonFiExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_goon_fi"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_GOON_FI_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_goon_fi"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/src/lib.rs new file mode 100644 index 0000000..68ce4a7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_goon_fi/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_goon_fi/src/lib.rs +// version: 2 + +//! Executor crate for `amm_goon_fi`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmGoonFiExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/Cargo.toml new file mode 100644 index 0000000..40b2ceb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_goosefx_gamma/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_goosefx_gamma" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/README.md new file mode 100644 index 0000000..b80b23c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_goosefx_gamma + +Ce crate réserve l'exécuteur de la surface `amm_goosefx_gamma`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/src/constants.rs new file mode 100644 index 0000000..94a895e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_goosefx_gamma/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_goosefx_gamma"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/src/executor.rs new file mode 100644 index 0000000..9d1bc30 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_goosefx_gamma/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_goosefx_gamma` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmGoosefxGammaExecutor; + +impl kb_execution_api::InstructionExecutor for crate::AmmGoosefxGammaExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_goosefx_gamma"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_GOOSEFX_GAMMA_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_goosefx_gamma"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/src/lib.rs new file mode 100644 index 0000000..3c6506f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_gamma/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_goosefx_gamma/src/lib.rs +// version: 2 + +//! Executor crate for `amm_goosefx_gamma`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmGoosefxGammaExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/Cargo.toml new file mode 100644 index 0000000..ff9f7a7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_goosefx_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_goosefx_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/README.md new file mode 100644 index 0000000..8b11a1c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_goosefx_v2 + +Ce crate réserve l'exécuteur de la surface `amm_goosefx_v2`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/src/constants.rs new file mode 100644 index 0000000..6af0118 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_goosefx_v2/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_goosefx_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/src/executor.rs new file mode 100644 index 0000000..49e97d0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_goosefx_v2/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_goosefx_v2` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmGoosefxV2Executor; + +impl kb_execution_api::InstructionExecutor for crate::AmmGoosefxV2Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_goosefx_v2"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_GOOSEFX_V2_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_goosefx_v2"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/src/lib.rs new file mode 100644 index 0000000..a46fd5c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_goosefx_v2/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_goosefx_v2/src/lib.rs +// version: 2 + +//! Executor crate for `amm_goosefx_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmGoosefxV2Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/Cargo.toml new file mode 100644 index 0000000..1c5dd50 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_guac_swap/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_guac_swap" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/README.md new file mode 100644 index 0000000..e90adbf --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_guac_swap + +Ce crate réserve l'exécuteur de la surface `amm_guac_swap`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/src/constants.rs new file mode 100644 index 0000000..b6b5190 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_guac_swap/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_guac_swap"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/src/executor.rs new file mode 100644 index 0000000..d707499 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_guac_swap/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_guac_swap` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmGuacSwapExecutor; + +impl kb_execution_api::InstructionExecutor for crate::AmmGuacSwapExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_guac_swap"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_GUAC_SWAP_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_guac_swap"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/src/lib.rs new file mode 100644 index 0000000..402796c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_guac_swap/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_guac_swap/src/lib.rs +// version: 2 + +//! Executor crate for `amm_guac_swap`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmGuacSwapExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/Cargo.toml new file mode 100644 index 0000000..07ba748 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_lifinity_swap_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_lifinity_swap_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/README.md new file mode 100644 index 0000000..651344f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_lifinity_swap_v2 + +Ce crate réserve l'exécuteur de la surface `amm_lifinity_swap_v2`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/src/constants.rs new file mode 100644 index 0000000..f090f71 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_lifinity_swap_v2/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_lifinity_swap_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/src/executor.rs new file mode 100644 index 0000000..cc05f25 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_lifinity_swap_v2/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_lifinity_swap_v2` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmLifinitySwapV2Executor; + +impl kb_execution_api::InstructionExecutor for crate::AmmLifinitySwapV2Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_lifinity_swap_v2"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_LIFINITY_SWAP_V2_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_lifinity_swap_v2"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/src/lib.rs new file mode 100644 index 0000000..33dfd40 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_lifinity_swap_v2/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_lifinity_swap_v2/src/lib.rs +// version: 2 + +//! Executor crate for `amm_lifinity_swap_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmLifinitySwapV2Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/Cargo.toml new file mode 100644 index 0000000..75e59c4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_metadao_futarchy_amm/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_metadao_futarchy_amm" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/README.md new file mode 100644 index 0000000..03e2ef9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_metadao_futarchy_amm + +Ce crate réserve l'exécuteur de la surface `amm_metadao_futarchy_amm`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/src/constants.rs new file mode 100644 index 0000000..43c082d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_metadao_futarchy_amm/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_metadao_futarchy_amm"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/src/executor.rs new file mode 100644 index 0000000..125fb5b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_metadao_futarchy_amm/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_metadao_futarchy_amm` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmMetadaoFutarchyAmmExecutor; + +impl kb_execution_api::InstructionExecutor for crate::AmmMetadaoFutarchyAmmExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_metadao_futarchy_amm"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_METADAO_FUTARCHY_AMM_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_metadao_futarchy_amm"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/src/lib.rs new file mode 100644 index 0000000..e57b020 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_futarchy_amm/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_metadao_futarchy_amm/src/lib.rs +// version: 2 + +//! Executor crate for `amm_metadao_futarchy_amm`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmMetadaoFutarchyAmmExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/Cargo.toml new file mode 100644 index 0000000..d7d110c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_metadao_v0_5/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_metadao_v0_5" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/README.md new file mode 100644 index 0000000..835035a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_metadao_v0_5 + +Ce crate réserve l'exécuteur de la surface `amm_metadao_v0_5`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/src/constants.rs new file mode 100644 index 0000000..1c56c2b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_metadao_v0_5/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_metadao_v0_5"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/src/executor.rs new file mode 100644 index 0000000..050026f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_metadao_v0_5/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_metadao_v0_5` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmMetadaoV05Executor; + +impl kb_execution_api::InstructionExecutor for crate::AmmMetadaoV05Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_metadao_v0_5"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_METADAO_V0_5_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_metadao_v0_5"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/src/lib.rs new file mode 100644 index 0000000..44872b9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_metadao_v0_5/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_metadao_v0_5/src/lib.rs +// version: 2 + +//! Executor crate for `amm_metadao_v0_5`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmMetadaoV05Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/Cargo.toml new file mode 100644 index 0000000..8bd3c53 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_meteora_damm_v1/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_meteora_damm_v1" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/README.md new file mode 100644 index 0000000..eeab38f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_meteora_damm_v1 + +Ce crate réserve l'exécuteur de la surface `amm_meteora_damm_v1`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/src/constants.rs new file mode 100644 index 0000000..b24337d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_meteora_damm_v1/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_meteora_damm_v1"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/src/executor.rs new file mode 100644 index 0000000..125ce02 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_meteora_damm_v1/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_meteora_damm_v1` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmMeteoraDammV1Executor; + +impl kb_execution_api::InstructionExecutor for crate::AmmMeteoraDammV1Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_meteora_damm_v1"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_METEORA_DAMM_V1_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_meteora_damm_v1"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/src/lib.rs new file mode 100644 index 0000000..00715c0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v1/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_meteora_damm_v1/src/lib.rs +// version: 2 + +//! Executor crate for `amm_meteora_damm_v1`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmMeteoraDammV1Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/Cargo.toml new file mode 100644 index 0000000..aa0d1f5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_meteora_damm_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_meteora_damm_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/README.md new file mode 100644 index 0000000..1fa749d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_meteora_damm_v2 + +Ce crate réserve l'exécuteur de la surface `amm_meteora_damm_v2`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/src/constants.rs new file mode 100644 index 0000000..b0173f3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_meteora_damm_v2/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_meteora_damm_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/src/executor.rs new file mode 100644 index 0000000..e00cccc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_meteora_damm_v2/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_meteora_damm_v2` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmMeteoraDammV2Executor; + +impl kb_execution_api::InstructionExecutor for crate::AmmMeteoraDammV2Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_meteora_damm_v2"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_METEORA_DAMM_V2_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_meteora_damm_v2"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/src/lib.rs new file mode 100644 index 0000000..77368ed --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_meteora_damm_v2/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_meteora_damm_v2/src/lib.rs +// version: 2 + +//! Executor crate for `amm_meteora_damm_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmMeteoraDammV2Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/Cargo.toml new file mode 100644 index 0000000..feb8769 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_obric_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_obric_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/README.md new file mode 100644 index 0000000..06a1a75 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_obric_v2 + +Ce crate réserve l'exécuteur de la surface `amm_obric_v2`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/src/constants.rs new file mode 100644 index 0000000..e81eb5d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_obric_v2/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_obric_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/src/executor.rs new file mode 100644 index 0000000..8a649c0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_obric_v2/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_obric_v2` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmObricV2Executor; + +impl kb_execution_api::InstructionExecutor for crate::AmmObricV2Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_obric_v2"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_OBRIC_V2_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_obric_v2"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/src/lib.rs new file mode 100644 index 0000000..61b7ec4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_obric_v2/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_obric_v2/src/lib.rs +// version: 2 + +//! Executor crate for `amm_obric_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmObricV2Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/Cargo.toml new file mode 100644 index 0000000..df2bd1f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_one_dex/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_one_dex" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/README.md new file mode 100644 index 0000000..c960f20 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_one_dex + +Ce crate réserve l'exécuteur de la surface `amm_one_dex`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/src/constants.rs new file mode 100644 index 0000000..5474b2f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_one_dex/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_one_dex"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/src/executor.rs new file mode 100644 index 0000000..027050b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_one_dex/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_one_dex` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmOneDexExecutor; + +impl kb_execution_api::InstructionExecutor for crate::AmmOneDexExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_one_dex"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_ONE_DEX_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_one_dex"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/src/lib.rs new file mode 100644 index 0000000..dd54237 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_one_dex/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_one_dex/src/lib.rs +// version: 2 + +//! Executor crate for `amm_one_dex`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmOneDexExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/Cargo.toml new file mode 100644 index 0000000..3e291db --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_pump_swap/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_pump_swap" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/README.md new file mode 100644 index 0000000..54c6f11 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_pump_swap + +Ce crate réserve l'exécuteur de la surface `amm_pump_swap`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/src/constants.rs new file mode 100644 index 0000000..a906dc4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_pump_swap/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_pump_swap"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/src/executor.rs new file mode 100644 index 0000000..427523b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_pump_swap/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_pump_swap` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmPumpSwapExecutor; + +impl kb_execution_api::InstructionExecutor for crate::AmmPumpSwapExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_pump_swap"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_PUMP_SWAP_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_pump_swap"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/src/lib.rs new file mode 100644 index 0000000..92c414b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_pump_swap/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_pump_swap/src/lib.rs +// version: 2 + +//! Executor crate for `amm_pump_swap`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmPumpSwapExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/Cargo.toml new file mode 100644 index 0000000..fa82dd9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_raydium_lp_v4/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_raydium_lp_v4" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/README.md new file mode 100644 index 0000000..ebabd4a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_raydium_lp_v4 + +Ce crate réserve l'exécuteur de la surface `amm_raydium_lp_v4`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/src/constants.rs new file mode 100644 index 0000000..bd4cffd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_raydium_lp_v4/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_raydium_lp_v4"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/src/executor.rs new file mode 100644 index 0000000..a39f640 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_raydium_lp_v4/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_raydium_lp_v4` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmRaydiumLpV4Executor; + +impl kb_execution_api::InstructionExecutor for crate::AmmRaydiumLpV4Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_raydium_lp_v4"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_RAYDIUM_LP_V4_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_raydium_lp_v4"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/src/lib.rs new file mode 100644 index 0000000..c66ca69 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_raydium_lp_v4/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_raydium_lp_v4/src/lib.rs +// version: 2 + +//! Executor crate for `amm_raydium_lp_v4`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmRaydiumLpV4Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_solfi/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi/Cargo.toml new file mode 100644 index 0000000..37e5bd0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_solfi/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_solfi" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_solfi/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi/README.md new file mode 100644 index 0000000..dde2cb1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_solfi + +Ce crate réserve l'exécuteur de la surface `amm_solfi`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_solfi/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi/src/constants.rs new file mode 100644 index 0000000..014245e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_solfi/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_solfi"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_solfi/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi/src/executor.rs new file mode 100644 index 0000000..0b651a1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_solfi/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_solfi` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmSolfiExecutor; + +impl kb_execution_api::InstructionExecutor for crate::AmmSolfiExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_solfi"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_SOLFI_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_solfi"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_solfi/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi/src/lib.rs new file mode 100644 index 0000000..33ace64 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_solfi/src/lib.rs +// version: 2 + +//! Executor crate for `amm_solfi`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmSolfiExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/Cargo.toml new file mode 100644 index 0000000..18f817c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_solfi_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_solfi_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/README.md new file mode 100644 index 0000000..442135a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_solfi_v2 + +Ce crate réserve l'exécuteur de la surface `amm_solfi_v2`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/src/constants.rs new file mode 100644 index 0000000..58908a2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_solfi_v2/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_solfi_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/src/executor.rs new file mode 100644 index 0000000..e1abf4a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_solfi_v2/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_solfi_v2` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmSolfiV2Executor; + +impl kb_execution_api::InstructionExecutor for crate::AmmSolfiV2Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_solfi_v2"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_SOLFI_V2_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_solfi_v2"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/src/lib.rs new file mode 100644 index 0000000..f9b152f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_solfi_v2/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_solfi_v2/src/lib.rs +// version: 2 + +//! Executor crate for `amm_solfi_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmSolfiV2Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/Cargo.toml new file mode 100644 index 0000000..b2cae75 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_vertigo/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_vertigo" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/README.md new file mode 100644 index 0000000..fba391c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_vertigo + +Ce crate réserve l'exécuteur de la surface `amm_vertigo`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/src/constants.rs new file mode 100644 index 0000000..205f0b5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_vertigo/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_vertigo"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/src/executor.rs new file mode 100644 index 0000000..b058b79 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_vertigo/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_vertigo` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmVertigoExecutor; + +impl kb_execution_api::InstructionExecutor for crate::AmmVertigoExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_vertigo"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_VERTIGO_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_vertigo"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/src/lib.rs new file mode 100644 index 0000000..673f49d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_vertigo/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_vertigo/src/lib.rs +// version: 2 + +//! Executor crate for `amm_vertigo`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmVertigoExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/Cargo.toml new file mode 100644 index 0000000..caf2451 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_virtuals/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_virtuals" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/README.md new file mode 100644 index 0000000..5584f91 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_virtuals + +Ce crate réserve l'exécuteur de la surface `amm_virtuals`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/src/constants.rs new file mode 100644 index 0000000..52ec09d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_virtuals/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_virtuals"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/src/executor.rs new file mode 100644 index 0000000..8613b8c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_virtuals/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_virtuals` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmVirtualsExecutor; + +impl kb_execution_api::InstructionExecutor for crate::AmmVirtualsExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_virtuals"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_VIRTUALS_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_virtuals"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/src/lib.rs new file mode 100644 index 0000000..5e67dd8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_virtuals/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_virtuals/src/lib.rs +// version: 2 + +//! Executor crate for `amm_virtuals`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmVirtualsExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_woofi/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_woofi/Cargo.toml new file mode 100644 index 0000000..4c9b136 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_woofi/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_woofi/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_woofi" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_woofi/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_woofi/README.md new file mode 100644 index 0000000..a6afd55 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_woofi/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_woofi + +Ce crate réserve l'exécuteur de la surface `amm_woofi`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_woofi/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_woofi/src/constants.rs new file mode 100644 index 0000000..dc622be --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_woofi/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_woofi/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_woofi"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_woofi/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_woofi/src/executor.rs new file mode 100644 index 0000000..68d437d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_woofi/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_woofi/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_woofi` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmWoofiExecutor; + +impl kb_execution_api::InstructionExecutor for crate::AmmWoofiExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_woofi"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_WOOFI_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_woofi"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_woofi/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_woofi/src/lib.rs new file mode 100644 index 0000000..5c69953 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_woofi/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_woofi/src/lib.rs +// version: 2 + +//! Executor crate for `amm_woofi`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmWoofiExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/Cargo.toml new file mode 100644 index 0000000..e7df4c3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_zero_fi/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_zero_fi" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/README.md new file mode 100644 index 0000000..f22b74d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_zero_fi + +Ce crate réserve l'exécuteur de la surface `amm_zero_fi`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/src/constants.rs new file mode 100644 index 0000000..5823af7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_zero_fi/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_zero_fi"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/src/executor.rs new file mode 100644 index 0000000..3f66b7d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_zero_fi/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_zero_fi` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmZeroFiExecutor; + +impl kb_execution_api::InstructionExecutor for crate::AmmZeroFiExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_zero_fi"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_ZERO_FI_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_zero_fi"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/src/lib.rs new file mode 100644 index 0000000..b601153 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_zero_fi/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_zero_fi/src/lib.rs +// version: 2 + +//! Executor crate for `amm_zero_fi`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmZeroFiExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_zora/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_amm_zora/Cargo.toml new file mode 100644 index 0000000..27370db --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_zora/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_amm_zora/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_amm_zora" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_zora/README.md b/migration/khadhroony-bot2-reference/kb_executor_amm_zora/README.md new file mode 100644 index 0000000..e862587 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_zora/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_amm_zora + +Ce crate réserve l'exécuteur de la surface `amm_zora`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_zora/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_zora/src/constants.rs new file mode 100644 index 0000000..7f4a73e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_zora/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_amm_zora/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_amm_zora"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_zora/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_zora/src/executor.rs new file mode 100644 index 0000000..94787c6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_zora/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_amm_zora/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_amm_zora` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct AmmZoraExecutor; + +impl kb_execution_api::InstructionExecutor for crate::AmmZoraExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_amm_zora"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::AMM_ZORA_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_amm_zora"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_amm_zora/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_amm_zora/src/lib.rs new file mode 100644 index 0000000..6e65c2e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_amm_zora/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_amm_zora/src/lib.rs +// version: 2 + +//! Executor crate for `amm_zora`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::AmmZoraExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/Cargo.toml new file mode 100644 index 0000000..43df9d3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_bridge_circle_cctp_token_messenger_minter/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_bridge_circle_cctp_token_messenger_minter" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/README.md b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/README.md new file mode 100644 index 0000000..0fbea15 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_bridge_circle_cctp_token_messenger_minter + +Ce crate réserve l'exécuteur de la surface `bridge_circle_cctp_token_messenger_minter`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/src/constants.rs new file mode 100644 index 0000000..9131984 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_bridge_circle_cctp_token_messenger_minter/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_bridge_circle_cctp_token_messenger_minter"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/src/executor.rs new file mode 100644 index 0000000..2295caf --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/src/executor.rs @@ -0,0 +1,51 @@ +// file: kb_executor_bridge_circle_cctp_token_messenger_minter/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_bridge_circle_cctp_token_messenger_minter` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct BridgeCircleCctpTokenMessengerMinterExecutor; + +impl kb_execution_api::InstructionExecutor for crate::BridgeCircleCctpTokenMessengerMinterExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_bridge_circle_cctp_token_messenger_minter"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::BRIDGE_CIRCLE_CCTP_CCTP_TOKEN_MESSENGER_MINTER_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from( + "kb_executor_bridge_circle_cctp_token_messenger_minter", + ), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/src/lib.rs new file mode 100644 index 0000000..7703a94 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_bridge_circle_cctp_token_messenger_minter/src/lib.rs +// version: 2 + +//! Executor crate for `bridge_circle_cctp_token_messenger_minter`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::BridgeCircleCctpTokenMessengerMinterExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/Cargo.toml new file mode 100644 index 0000000..357ba9d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_bridge_circle_cctp_token_messenger_minter_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_bridge_circle_cctp_token_messenger_minter_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/README.md b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/README.md new file mode 100644 index 0000000..305623a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_bridge_circle_cctp_token_messenger_minter_v2 + +Ce crate réserve l'exécuteur de la surface `bridge_circle_cctp_token_messenger_minter_v2`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/src/constants.rs new file mode 100644 index 0000000..772b288 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_bridge_circle_cctp_token_messenger_minter_v2/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_bridge_circle_cctp_token_messenger_minter_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/src/executor.rs new file mode 100644 index 0000000..f6b2cd5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/src/executor.rs @@ -0,0 +1,53 @@ +// file: kb_executor_bridge_circle_cctp_token_messenger_minter_v2/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_bridge_circle_cctp_token_messenger_minter_v2` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct BridgeCircleCctpTokenMessengerMinterV2Executor; + +impl kb_execution_api::InstructionExecutor + for crate::BridgeCircleCctpTokenMessengerMinterV2Executor +{ + fn executor_name(&self) -> &'static str { + return "kb_executor_bridge_circle_cctp_token_messenger_minter_v2"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::BRIDGE_CIRCLE_CCTP_CCTP_TOKEN_MESSENGER_MINTER_V2_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from( + "kb_executor_bridge_circle_cctp_token_messenger_minter_v2", + ), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/src/lib.rs new file mode 100644 index 0000000..20a0ab3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_circle_cctp_token_messenger_minter_v2/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_bridge_circle_cctp_token_messenger_minter_v2/src/lib.rs +// version: 2 + +//! Executor crate for `bridge_circle_cctp_token_messenger_minter_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::BridgeCircleCctpTokenMessengerMinterV2Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/Cargo.toml new file mode 100644 index 0000000..326d3ea --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_bridge_layer_zero_endpoint/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_bridge_layer_zero_endpoint" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/README.md b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/README.md new file mode 100644 index 0000000..f536410 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_bridge_layer_zero_endpoint + +Ce crate réserve l'exécuteur de la surface `bridge_layer_zero_endpoint`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/src/constants.rs new file mode 100644 index 0000000..116e80c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_bridge_layer_zero_endpoint/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_bridge_layer_zero_endpoint"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/src/executor.rs new file mode 100644 index 0000000..e034664 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_bridge_layer_zero_endpoint/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_bridge_layer_zero_endpoint` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct BridgeLayerZeroEndpointExecutor; + +impl kb_execution_api::InstructionExecutor for crate::BridgeLayerZeroEndpointExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_bridge_layer_zero_endpoint"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::BRIDGE_LAYER_ZERO_ENDPOINT_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_bridge_layer_zero_endpoint"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/src/lib.rs new file mode 100644 index 0000000..c6a507b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_endpoint/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_bridge_layer_zero_endpoint/src/lib.rs +// version: 2 + +//! Executor crate for `bridge_layer_zero_endpoint`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::BridgeLayerZeroEndpointExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/Cargo.toml new file mode 100644 index 0000000..a42cfb2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_bridge_layer_zero_executor/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_bridge_layer_zero_executor" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/README.md b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/README.md new file mode 100644 index 0000000..c44c9cf --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_bridge_layer_zero_executor + +Ce crate réserve l'exécuteur de la surface `bridge_layer_zero_executor`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/src/constants.rs new file mode 100644 index 0000000..37619b6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_bridge_layer_zero_executor/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_bridge_layer_zero_executor"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/src/executor.rs new file mode 100644 index 0000000..b33dba9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_bridge_layer_zero_executor/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_bridge_layer_zero_executor` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct BridgeLayerZeroExecutorExecutor; + +impl kb_execution_api::InstructionExecutor for crate::BridgeLayerZeroExecutorExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_bridge_layer_zero_executor"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::BRIDGE_LAYER_ZERO_EXECUTOR_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_bridge_layer_zero_executor"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/src/lib.rs new file mode 100644 index 0000000..71270c6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_bridge_layer_zero_executor/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_bridge_layer_zero_executor/src/lib.rs +// version: 2 + +//! Executor crate for `bridge_layer_zero_executor`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::BridgeLayerZeroExecutorExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/Cargo.toml new file mode 100644 index 0000000..00f7e30 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_clmm_byreal/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_clmm_byreal" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/README.md b/migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/README.md new file mode 100644 index 0000000..494cd66 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_clmm_byreal + +Ce crate réserve l'exécuteur de la surface `clmm_byreal`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/src/constants.rs new file mode 100644 index 0000000..3207890 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_clmm_byreal/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_clmm_byreal"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/src/executor.rs new file mode 100644 index 0000000..150f189 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_clmm_byreal/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_clmm_byreal` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct ClmmByrealExecutor; + +impl kb_execution_api::InstructionExecutor for crate::ClmmByrealExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_clmm_byreal"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::CLMM_BYREAL_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_clmm_byreal"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/src/lib.rs new file mode 100644 index 0000000..31731ab --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_byreal/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_clmm_byreal/src/lib.rs +// version: 2 + +//! Executor crate for `clmm_byreal`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::ClmmByrealExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/Cargo.toml new file mode 100644 index 0000000..78dd192 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_clmm_fusion/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_clmm_fusion" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/README.md b/migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/README.md new file mode 100644 index 0000000..b5fbb99 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_clmm_fusion + +Ce crate réserve l'exécuteur de la surface `clmm_fusion`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/src/constants.rs new file mode 100644 index 0000000..98b511b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_clmm_fusion/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_clmm_fusion"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/src/executor.rs new file mode 100644 index 0000000..e1c434b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_clmm_fusion/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_clmm_fusion` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct ClmmFusionExecutor; + +impl kb_execution_api::InstructionExecutor for crate::ClmmFusionExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_clmm_fusion"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::CLMM_FUSION_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_clmm_fusion"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/src/lib.rs new file mode 100644 index 0000000..19c1c24 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_fusion/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_clmm_fusion/src/lib.rs +// version: 2 + +//! Executor crate for `clmm_fusion`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::ClmmFusionExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/Cargo.toml new file mode 100644 index 0000000..a2573ae --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_clmm_orca_whirlpool/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_clmm_orca_whirlpool" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/README.md b/migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/README.md new file mode 100644 index 0000000..728d047 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_clmm_orca_whirlpool + +Ce crate réserve l'exécuteur de la surface `clmm_orca_whirlpool`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/src/constants.rs new file mode 100644 index 0000000..68f1ea1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_clmm_orca_whirlpool/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_clmm_orca_whirlpool"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/src/executor.rs new file mode 100644 index 0000000..83b2ff6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_clmm_orca_whirlpool/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_clmm_orca_whirlpool` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct ClmmOrcaWhirlpoolExecutor; + +impl kb_execution_api::InstructionExecutor for crate::ClmmOrcaWhirlpoolExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_clmm_orca_whirlpool"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::CLMM_ORCA_WHIRLPOOL_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_clmm_orca_whirlpool"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/src/lib.rs new file mode 100644 index 0000000..14d6b17 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_orca_whirlpool/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_clmm_orca_whirlpool/src/lib.rs +// version: 2 + +//! Executor crate for `clmm_orca_whirlpool`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::ClmmOrcaWhirlpoolExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/Cargo.toml new file mode 100644 index 0000000..73dcaeb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_clmm_pancake_swap/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_clmm_pancake_swap" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/README.md b/migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/README.md new file mode 100644 index 0000000..4f953bc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_clmm_pancake_swap + +Ce crate réserve l'exécuteur de la surface `clmm_pancake_swap`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/src/constants.rs new file mode 100644 index 0000000..c5d478b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_clmm_pancake_swap/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_clmm_pancake_swap"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/src/executor.rs new file mode 100644 index 0000000..459a6c1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_clmm_pancake_swap/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_clmm_pancake_swap` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct ClmmPancakeSwapExecutor; + +impl kb_execution_api::InstructionExecutor for crate::ClmmPancakeSwapExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_clmm_pancake_swap"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::CLMM_PANCAKE_SWAP_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_clmm_pancake_swap"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/src/lib.rs new file mode 100644 index 0000000..cc45fbe --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_pancake_swap/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_clmm_pancake_swap/src/lib.rs +// version: 2 + +//! Executor crate for `clmm_pancake_swap`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::ClmmPancakeSwapExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/Cargo.toml new file mode 100644 index 0000000..03fe782 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_clmm_raydium/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_clmm_raydium" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/README.md b/migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/README.md new file mode 100644 index 0000000..75b2076 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_clmm_raydium + +Ce crate réserve l'exécuteur de la surface `clmm_raydium`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/src/constants.rs new file mode 100644 index 0000000..ac89f7f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_clmm_raydium/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_clmm_raydium"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/src/executor.rs new file mode 100644 index 0000000..e5b9c96 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_clmm_raydium/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_clmm_raydium` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct ClmmRaydiumExecutor; + +impl kb_execution_api::InstructionExecutor for crate::ClmmRaydiumExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_clmm_raydium"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::CLMM_RAYDIUM_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_clmm_raydium"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/src/lib.rs new file mode 100644 index 0000000..4725078 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_raydium/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_clmm_raydium/src/lib.rs +// version: 2 + +//! Executor crate for `clmm_raydium`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::ClmmRaydiumExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/Cargo.toml new file mode 100644 index 0000000..87dbc0d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_clmm_stabble/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_clmm_stabble" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/README.md b/migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/README.md new file mode 100644 index 0000000..b573d12 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_clmm_stabble + +Ce crate réserve l'exécuteur de la surface `clmm_stabble`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/src/constants.rs new file mode 100644 index 0000000..b416eb8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_clmm_stabble/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_clmm_stabble"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/src/executor.rs new file mode 100644 index 0000000..0a0b35a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_clmm_stabble/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_clmm_stabble` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct ClmmStabbleExecutor; + +impl kb_execution_api::InstructionExecutor for crate::ClmmStabbleExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_clmm_stabble"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::CLMM_STABBLE_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_clmm_stabble"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/src/lib.rs new file mode 100644 index 0000000..68055cc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_clmm_stabble/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_clmm_stabble/src/lib.rs +// version: 2 + +//! Executor crate for `clmm_stabble`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::ClmmStabbleExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/Cargo.toml new file mode 100644 index 0000000..a720d1e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_cpmm_raydium/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_cpmm_raydium" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/README.md b/migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/README.md new file mode 100644 index 0000000..b5998c8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_cpmm_raydium + +Ce crate réserve l'exécuteur de la surface `cpmm_raydium`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/src/constants.rs new file mode 100644 index 0000000..108918e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_cpmm_raydium/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_cpmm_raydium"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/src/executor.rs new file mode 100644 index 0000000..cd24d6e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_cpmm_raydium/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_cpmm_raydium` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct CpmmRaydiumExecutor; + +impl kb_execution_api::InstructionExecutor for crate::CpmmRaydiumExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_cpmm_raydium"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::CPMM_RAYDIUM_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_cpmm_raydium"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/src/lib.rs new file mode 100644 index 0000000..967a7ec --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_cpmm_raydium/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_cpmm_raydium/src/lib.rs +// version: 2 + +//! Executor crate for `cpmm_raydium`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::CpmmRaydiumExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/Cargo.toml new file mode 100644 index 0000000..5679208 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_dlmm_meteora/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_dlmm_meteora" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/README.md b/migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/README.md new file mode 100644 index 0000000..3d37343 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_dlmm_meteora + +Ce crate réserve l'exécuteur de la surface `dlmm_meteora`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/src/constants.rs new file mode 100644 index 0000000..8621964 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_dlmm_meteora/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_dlmm_meteora"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/src/executor.rs new file mode 100644 index 0000000..eccc031 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_dlmm_meteora/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_dlmm_meteora` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct DlmmMeteoraExecutor; + +impl kb_execution_api::InstructionExecutor for crate::DlmmMeteoraExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_dlmm_meteora"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::DLMM_METEORA_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_dlmm_meteora"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/src/lib.rs new file mode 100644 index 0000000..03c0d90 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_dlmm_meteora/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_dlmm_meteora/src/lib.rs +// version: 2 + +//! Executor crate for `dlmm_meteora`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::DlmmMeteoraExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/Cargo.toml new file mode 100644 index 0000000..88ddd24 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_fees_bags_fee_share_v1/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_fees_bags_fee_share_v1" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/README.md b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/README.md new file mode 100644 index 0000000..e243c22 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/README.md @@ -0,0 +1,14 @@ + + + +# kb_executor_fees_bags_fee_share_v1 + +Ce crate réserve l'exécuteur de la surface `fees_bags_fee_share_v1`. + +## Rôle dans l'écosystème + +Il construira les plans d'exécution liés à Bags Fee Share lorsque les builders, simulations et garde-fous seront validés. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders et simulations dédiés ne sont pas validés. diff --git a/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/src/constants.rs new file mode 100644 index 0000000..68e258f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_fees_bags_fee_share_v1/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_fees_bags_fee_share_v1"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/src/executor.rs new file mode 100644 index 0000000..aad1c7f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_fees_bags_fee_share_v1/src/executor.rs +// version: 3 + +//! Executor scaffold for the `kb_executor_fees_bags_fee_share_v1` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct FeesBagsFeeShareV1Executor; + +impl kb_execution_api::InstructionExecutor for crate::FeesBagsFeeShareV1Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_fees_bags_fee_share_v1"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::FEES_BAGS_FEE_SHARE_V1_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_fees_bags_fee_share_v1"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/src/lib.rs new file mode 100644 index 0000000..02ee98c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v1/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_fees_bags_fee_share_v1/src/lib.rs +// version: 2 + +//! Executor crate for `fees_bags_fee_share_v1`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::FeesBagsFeeShareV1Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/Cargo.toml new file mode 100644 index 0000000..0672a84 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_fees_bags_fee_share_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_fees_bags_fee_share_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/README.md b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/README.md new file mode 100644 index 0000000..cdb1524 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/README.md @@ -0,0 +1,14 @@ + + + +# kb_executor_fees_bags_fee_share_v2 + +Ce crate réserve l'exécuteur de la surface `fees_bags_fee_share_v2`. + +## Rôle dans l'écosystème + +Il construira les plans d'exécution liés à Bags Fee Share lorsque les builders, simulations et garde-fous seront validés. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders et simulations dédiés ne sont pas validés. diff --git a/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/src/constants.rs new file mode 100644 index 0000000..2e27f99 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_fees_bags_fee_share_v2/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_fees_bags_fee_share_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/src/executor.rs new file mode 100644 index 0000000..8661d52 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_fees_bags_fee_share_v2/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_fees_bags_fee_share_v2` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct FeesBagsFeeShareV2Executor; + +impl kb_execution_api::InstructionExecutor for crate::FeesBagsFeeShareV2Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_fees_bags_fee_share_v2"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::FEES_BAGS_FEE_SHARE_V2_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_fees_bags_fee_share_v2"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/src/lib.rs new file mode 100644 index 0000000..1883442 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_fees_bags_fee_share_v2/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_fees_bags_fee_share_v2/src/lib.rs +// version: 2 + +//! Executor crate for `fees_bags_fee_share_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::FeesBagsFeeShareV2Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/Cargo.toml new file mode 100644 index 0000000..24b7347 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_governance_metadao_bid_wall/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_governance_metadao_bid_wall" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/README.md b/migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/README.md new file mode 100644 index 0000000..3507c22 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_governance_metadao_bid_wall + +Ce crate réserve l'exécuteur de la surface `governance_metadao_bid_wall`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/src/constants.rs new file mode 100644 index 0000000..43f277e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_governance_metadao_bid_wall/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_governance_metadao_bid_wall"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/src/executor.rs new file mode 100644 index 0000000..23c2e49 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_governance_metadao_bid_wall/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_governance_metadao_bid_wall` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct GovernanceMetadaoBidWallExecutor; + +impl kb_execution_api::InstructionExecutor for crate::GovernanceMetadaoBidWallExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_governance_metadao_bid_wall"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::GOVERNANCE_METADAO_BID_WALL_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_governance_metadao_bid_wall"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/src/lib.rs new file mode 100644 index 0000000..fed0c38 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_governance_metadao_bid_wall/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_governance_metadao_bid_wall/src/lib.rs +// version: 2 + +//! Executor crate for `governance_metadao_bid_wall`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::GovernanceMetadaoBidWallExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/Cargo.toml new file mode 100644 index 0000000..d0583bc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_launchpad_boop_fun/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_launchpad_boop_fun" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/README.md b/migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/README.md new file mode 100644 index 0000000..ee941f5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_launchpad_boop_fun + +Ce crate réserve l'exécuteur de la surface `launchpad_boop_fun`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/src/constants.rs new file mode 100644 index 0000000..09310d9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_launchpad_boop_fun/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_launchpad_boop_fun"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/src/executor.rs new file mode 100644 index 0000000..87a14fc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_launchpad_boop_fun/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_launchpad_boop_fun` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LaunchpadBoopFunExecutor; + +impl kb_execution_api::InstructionExecutor for crate::LaunchpadBoopFunExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_launchpad_boop_fun"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LAUNCHPAD_BOOP_FUN_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_launchpad_boop_fun"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/src/lib.rs new file mode 100644 index 0000000..7a4388e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_boop_fun/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_launchpad_boop_fun/src/lib.rs +// version: 2 + +//! Executor crate for `launchpad_boop_fun`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::LaunchpadBoopFunExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/Cargo.toml new file mode 100644 index 0000000..22fb7a0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_launchpad_metadao_ico/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_launchpad_metadao_ico" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/README.md b/migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/README.md new file mode 100644 index 0000000..b4b9956 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_launchpad_metadao_ico + +Ce crate réserve l'exécuteur de la surface `launchpad_metadao_ico`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/src/constants.rs new file mode 100644 index 0000000..7fdbf95 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_launchpad_metadao_ico/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_launchpad_metadao_ico"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/src/executor.rs new file mode 100644 index 0000000..536b7ad --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_launchpad_metadao_ico/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_launchpad_metadao_ico` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LaunchpadMetadaoIcoExecutor; + +impl kb_execution_api::InstructionExecutor for crate::LaunchpadMetadaoIcoExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_launchpad_metadao_ico"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LAUNCHPAD_METADAO_ICO_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_launchpad_metadao_ico"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/src/lib.rs new file mode 100644 index 0000000..cf8df3b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_metadao_ico/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_launchpad_metadao_ico/src/lib.rs +// version: 2 + +//! Executor crate for `launchpad_metadao_ico`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::LaunchpadMetadaoIcoExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/Cargo.toml new file mode 100644 index 0000000..bdc70e8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_launchpad_meteora_dbc/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_launchpad_meteora_dbc" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/README.md b/migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/README.md new file mode 100644 index 0000000..84ad365 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_launchpad_meteora_dbc + +Ce crate réserve l'exécuteur de la surface `launchpad_meteora_dbc`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/src/constants.rs new file mode 100644 index 0000000..b7e654f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_launchpad_meteora_dbc/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_launchpad_meteora_dbc"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/src/executor.rs new file mode 100644 index 0000000..c0ce1ac --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_launchpad_meteora_dbc/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_launchpad_meteora_dbc` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LaunchpadMeteoraDbcExecutor; + +impl kb_execution_api::InstructionExecutor for crate::LaunchpadMeteoraDbcExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_launchpad_meteora_dbc"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LAUNCHPAD_METEORA_DBC_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_launchpad_meteora_dbc"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/src/lib.rs new file mode 100644 index 0000000..b0bfb84 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_meteora_dbc/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_launchpad_meteora_dbc/src/lib.rs +// version: 2 + +//! Executor crate for `launchpad_meteora_dbc`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::LaunchpadMeteoraDbcExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/Cargo.toml new file mode 100644 index 0000000..a65613a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_launchpad_moonit/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_launchpad_moonit" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/README.md b/migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/README.md new file mode 100644 index 0000000..e8afa22 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_launchpad_moonit + +Ce crate réserve l'exécuteur de la surface `launchpad_moonit`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/src/constants.rs new file mode 100644 index 0000000..5cbee67 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_launchpad_moonit/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_launchpad_moonit"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/src/executor.rs new file mode 100644 index 0000000..4e3f090 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_launchpad_moonit/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_launchpad_moonit` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LaunchpadMoonitExecutor; + +impl kb_execution_api::InstructionExecutor for crate::LaunchpadMoonitExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_launchpad_moonit"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LAUNCHPAD_MOONIT_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_launchpad_moonit"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/src/lib.rs new file mode 100644 index 0000000..dc38a3e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_moonit/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_launchpad_moonit/src/lib.rs +// version: 2 + +//! Executor crate for `launchpad_moonit`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::LaunchpadMoonitExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/Cargo.toml new file mode 100644 index 0000000..5c0664c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_launchpad_orca_wavebreak/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_launchpad_orca_wavebreak" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/README.md b/migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/README.md new file mode 100644 index 0000000..a8a049f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_launchpad_orca_wavebreak + +Ce crate réserve l'exécuteur de la surface `launchpad_orca_wavebreak`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/src/constants.rs new file mode 100644 index 0000000..959e797 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_launchpad_orca_wavebreak/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_launchpad_orca_wavebreak"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/src/executor.rs new file mode 100644 index 0000000..fe0b65b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_launchpad_orca_wavebreak/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_launchpad_orca_wavebreak` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LaunchpadOrcaWavebreakExecutor; + +impl kb_execution_api::InstructionExecutor for crate::LaunchpadOrcaWavebreakExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_launchpad_orca_wavebreak"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LAUNCHPAD_ORCA_WAVEBREAK_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_launchpad_orca_wavebreak"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/src/lib.rs new file mode 100644 index 0000000..9619613 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_orca_wavebreak/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_launchpad_orca_wavebreak/src/lib.rs +// version: 2 + +//! Executor crate for `launchpad_orca_wavebreak`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::LaunchpadOrcaWavebreakExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/Cargo.toml new file mode 100644 index 0000000..3a1fa1e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_launchpad_printr/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_launchpad_printr" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/README.md b/migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/README.md new file mode 100644 index 0000000..76f3a39 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_launchpad_printr + +Ce crate réserve l'exécuteur de la surface `launchpad_printr`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/src/constants.rs new file mode 100644 index 0000000..5bf6948 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_launchpad_printr/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_launchpad_printr"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/src/executor.rs new file mode 100644 index 0000000..49da5d4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_launchpad_printr/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_launchpad_printr` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LaunchpadPrintrExecutor; + +impl kb_execution_api::InstructionExecutor for crate::LaunchpadPrintrExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_launchpad_printr"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LAUNCHPAD_PRINTR_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_launchpad_printr"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/src/lib.rs new file mode 100644 index 0000000..83f4ba0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_printr/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_launchpad_printr/src/lib.rs +// version: 2 + +//! Executor crate for `launchpad_printr`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::LaunchpadPrintrExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/Cargo.toml new file mode 100644 index 0000000..32c6178 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_launchpad_pump_fun/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_launchpad_pump_fun" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/README.md b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/README.md new file mode 100644 index 0000000..28b7500 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_launchpad_pump_fun + +Ce crate réserve l'exécuteur de la surface `launchpad_pump_fun`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/src/constants.rs new file mode 100644 index 0000000..6cfe125 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_launchpad_pump_fun/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_launchpad_pump_fun"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/src/executor.rs new file mode 100644 index 0000000..7449133 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_launchpad_pump_fun/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_launchpad_pump_fun` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LaunchpadPumpFunExecutor; + +impl kb_execution_api::InstructionExecutor for crate::LaunchpadPumpFunExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_launchpad_pump_fun"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LAUNCHPAD_PUMP_FUN_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_launchpad_pump_fun"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/src/lib.rs new file mode 100644 index 0000000..4e4bbde --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_fun/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_launchpad_pump_fun/src/lib.rs +// version: 2 + +//! Executor crate for `launchpad_pump_fun`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::LaunchpadPumpFunExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/Cargo.toml new file mode 100644 index 0000000..47d8847 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_launchpad_pump_pumpup_ai/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_launchpad_pump_pumpup_ai" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/README.md b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/README.md new file mode 100644 index 0000000..c8257b2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_launchpad_pump_pumpup_ai + +Ce crate réserve l'exécuteur de la surface `launchpad_pump_pumpup_ai`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/src/constants.rs new file mode 100644 index 0000000..022a976 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_launchpad_pump_pumpup_ai/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_launchpad_pump_pumpup_ai"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/src/executor.rs new file mode 100644 index 0000000..f65ff97 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_launchpad_pump_pumpup_ai/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_launchpad_pump_pumpup_ai` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LaunchpadPumpPumpupAiExecutor; + +impl kb_execution_api::InstructionExecutor for crate::LaunchpadPumpPumpupAiExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_launchpad_pump_pumpup_ai"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LAUNCHPAD_PUMP_PUMPUP_AI_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_launchpad_pump_pumpup_ai"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/src/lib.rs new file mode 100644 index 0000000..418a0f2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_pump_pumpup_ai/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_launchpad_pump_pumpup_ai/src/lib.rs +// version: 2 + +//! Executor crate for `launchpad_pump_pumpup_ai`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::LaunchpadPumpPumpupAiExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/Cargo.toml new file mode 100644 index 0000000..3c83c43 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_launchpad_raydium_launchlab/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_launchpad_raydium_launchlab" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/README.md b/migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/README.md new file mode 100644 index 0000000..ea1bf8c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_launchpad_raydium_launchlab + +Ce crate réserve l'exécuteur de la surface `launchpad_raydium_launchlab`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/src/constants.rs new file mode 100644 index 0000000..267e955 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_launchpad_raydium_launchlab/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_launchpad_raydium_launchlab"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/src/executor.rs new file mode 100644 index 0000000..e28c6c3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_launchpad_raydium_launchlab/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_launchpad_raydium_launchlab` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LaunchpadRaydiumLaunchlabExecutor; + +impl kb_execution_api::InstructionExecutor for crate::LaunchpadRaydiumLaunchlabExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_launchpad_raydium_launchlab"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LAUNCHPAD_RAYDIUM_LAUNCHLAB_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_launchpad_raydium_launchlab"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/src/lib.rs new file mode 100644 index 0000000..7021b41 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_launchpad_raydium_launchlab/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_launchpad_raydium_launchlab/src/lib.rs +// version: 2 + +//! Executor crate for `launchpad_raydium_launchlab`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::LaunchpadRaydiumLaunchlabExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_clone/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_lending_clone/Cargo.toml new file mode 100644 index 0000000..5e75e7a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_clone/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_lending_clone/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_lending_clone" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_clone/README.md b/migration/khadhroony-bot2-reference/kb_executor_lending_clone/README.md new file mode 100644 index 0000000..74ef499 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_clone/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_lending_clone + +Ce crate réserve l'exécuteur de la surface `lending_clone`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_clone/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_clone/src/constants.rs new file mode 100644 index 0000000..694494f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_clone/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_lending_clone/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_lending_clone"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_clone/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_clone/src/executor.rs new file mode 100644 index 0000000..ac954e7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_clone/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_lending_clone/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_lending_clone` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LendingCloneExecutor; + +impl kb_execution_api::InstructionExecutor for crate::LendingCloneExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_lending_clone"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LENDING_CLONE_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_lending_clone"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_clone/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_clone/src/lib.rs new file mode 100644 index 0000000..1ebe5f1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_clone/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_lending_clone/src/lib.rs +// version: 2 + +//! Executor crate for `lending_clone`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::LendingCloneExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/Cargo.toml new file mode 100644 index 0000000..f4bfd65 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_lending_jupiter_lend_borrow/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_lending_jupiter_lend_borrow" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/README.md b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/README.md new file mode 100644 index 0000000..07f0f8f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_lending_jupiter_lend_borrow + +Ce crate réserve l'exécuteur de la surface `lending_jupiter_lend_borrow`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/src/constants.rs new file mode 100644 index 0000000..5538704 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_lending_jupiter_lend_borrow/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_lending_jupiter_lend_borrow"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/src/executor.rs new file mode 100644 index 0000000..a160c30 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_lending_jupiter_lend_borrow/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_lending_jupiter_lend_borrow` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LendingJupiterLendBorrowExecutor; + +impl kb_execution_api::InstructionExecutor for crate::LendingJupiterLendBorrowExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_lending_jupiter_lend_borrow"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LENDING_JUPITER_LEND_BORROW_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_lending_jupiter_lend_borrow"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/src/lib.rs new file mode 100644 index 0000000..9d396a9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_borrow/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_lending_jupiter_lend_borrow/src/lib.rs +// version: 2 + +//! Executor crate for `lending_jupiter_lend_borrow`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::LendingJupiterLendBorrowExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/Cargo.toml new file mode 100644 index 0000000..2529d20 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_lending_jupiter_lend_earn/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_lending_jupiter_lend_earn" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/README.md b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/README.md new file mode 100644 index 0000000..f64140c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_lending_jupiter_lend_earn + +Ce crate réserve l'exécuteur de la surface `lending_jupiter_lend_earn`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/src/constants.rs new file mode 100644 index 0000000..a8ff800 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_lending_jupiter_lend_earn/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_lending_jupiter_lend_earn"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/src/executor.rs new file mode 100644 index 0000000..b04d55d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_lending_jupiter_lend_earn/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_lending_jupiter_lend_earn` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LendingJupiterLendEarnExecutor; + +impl kb_execution_api::InstructionExecutor for crate::LendingJupiterLendEarnExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_lending_jupiter_lend_earn"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LENDING_JUPITER_LEND_EARN_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_lending_jupiter_lend_earn"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/src/lib.rs new file mode 100644 index 0000000..6d205b6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_earn/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_lending_jupiter_lend_earn/src/lib.rs +// version: 2 + +//! Executor crate for `lending_jupiter_lend_earn`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::LendingJupiterLendEarnExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/Cargo.toml new file mode 100644 index 0000000..c17d2f1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_lending_jupiter_lend_flash_loan/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_lending_jupiter_lend_flash_loan" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/README.md b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/README.md new file mode 100644 index 0000000..4fe0d61 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_lending_jupiter_lend_flash_loan + +Ce crate réserve l'exécuteur de la surface `lending_jupiter_lend_flash_loan`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/src/constants.rs new file mode 100644 index 0000000..e9fe8e1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_lending_jupiter_lend_flash_loan/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_lending_jupiter_lend_flash_loan"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/src/executor.rs new file mode 100644 index 0000000..47d8065 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_lending_jupiter_lend_flash_loan/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_lending_jupiter_lend_flash_loan` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LendingJupiterLendFlashLoanExecutor; + +impl kb_execution_api::InstructionExecutor for crate::LendingJupiterLendFlashLoanExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_lending_jupiter_lend_flash_loan"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LENDING_JUPITER_LEND_FLASH_LOAN_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_lending_jupiter_lend_flash_loan"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/src/lib.rs new file mode 100644 index 0000000..3427424 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_flash_loan/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_lending_jupiter_lend_flash_loan/src/lib.rs +// version: 2 + +//! Executor crate for `lending_jupiter_lend_flash_loan`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::LendingJupiterLendFlashLoanExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/Cargo.toml new file mode 100644 index 0000000..46de755 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_lending_jupiter_lend_liquidity/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_lending_jupiter_lend_liquidity" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/README.md b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/README.md new file mode 100644 index 0000000..0577d4b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_lending_jupiter_lend_liquidity + +Ce crate réserve l'exécuteur de la surface `lending_jupiter_lend_liquidity`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/src/constants.rs new file mode 100644 index 0000000..7f8a52c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_lending_jupiter_lend_liquidity/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_lending_jupiter_lend_liquidity"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/src/executor.rs new file mode 100644 index 0000000..7fae5fb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_lending_jupiter_lend_liquidity/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_lending_jupiter_lend_liquidity` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LendingJupiterLendLiquidityExecutor; + +impl kb_execution_api::InstructionExecutor for crate::LendingJupiterLendLiquidityExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_lending_jupiter_lend_liquidity"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LENDING_JUPITER_LEND_LIQUIDITY_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_lending_jupiter_lend_liquidity"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/src/lib.rs new file mode 100644 index 0000000..86143a8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_jupiter_lend_liquidity/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_lending_jupiter_lend_liquidity/src/lib.rs +// version: 2 + +//! Executor crate for `lending_jupiter_lend_liquidity`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::LendingJupiterLendLiquidityExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_kamino/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_lending_kamino/Cargo.toml new file mode 100644 index 0000000..1fca466 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_kamino/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_lending_kamino/Cargo.toml +# version: 1 + +[package] +name = "kb_executor_lending_kamino" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_program_ids = { path = "../kb_program_ids" } +kb_core = { path = "../kb_core" } +kb_model = { path = "../kb_model" } +kb_execution_api = { path = "../kb_execution_api" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_kamino/README.md b/migration/khadhroony-bot2-reference/kb_executor_lending_kamino/README.md new file mode 100644 index 0000000..b7f9879 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_kamino/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_lending_kamino + +Ce crate réserve l'exécuteur de la surface `lending_kamino`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_kamino/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_kamino/src/constants.rs new file mode 100644 index 0000000..cf2faa7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_kamino/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_lending_kamino/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_lending_kamino"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_kamino/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_kamino/src/executor.rs new file mode 100644 index 0000000..ab845f9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_kamino/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_lending_kamino/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_lending_kamino` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LendingKaminoExecutor; + +impl kb_execution_api::InstructionExecutor for crate::LendingKaminoExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_lending_kamino"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LENDING_KAMINO_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_lending_kamino"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_kamino/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_kamino/src/lib.rs new file mode 100644 index 0000000..cb43758 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_kamino/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_lending_kamino/src/lib.rs +// version: 2 + +//! Executor crate for `lending_kamino`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::LendingKaminoExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/Cargo.toml new file mode 100644 index 0000000..b124085 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_lending_marginfi_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_lending_marginfi_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/README.md b/migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/README.md new file mode 100644 index 0000000..86f8706 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_lending_marginfi_v2 + +Ce crate réserve l'exécuteur de la surface `lending_marginfi_v2`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/src/constants.rs new file mode 100644 index 0000000..3e39ad2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_lending_marginfi_v2/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_lending_marginfi_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/src/executor.rs new file mode 100644 index 0000000..02722a3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_lending_marginfi_v2/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_lending_marginfi_v2` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LendingMarginfiV2Executor; + +impl kb_execution_api::InstructionExecutor for crate::LendingMarginfiV2Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_lending_marginfi_v2"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LENDING_MARGINFI_V2_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_lending_marginfi_v2"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/src/lib.rs new file mode 100644 index 0000000..9de3262 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lending_marginfi_v2/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_lending_marginfi_v2/src/lib.rs +// version: 2 + +//! Executor crate for `lending_marginfi_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::LendingMarginfiV2Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/Cargo.toml new file mode 100644 index 0000000..4ebae55 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_lock_raydium_lp/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_lock_raydium_lp" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/README.md b/migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/README.md new file mode 100644 index 0000000..7b25181 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_lock_raydium_lp + +Ce crate réserve l'exécuteur de la surface `lock_raydium_lp`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/src/constants.rs new file mode 100644 index 0000000..c6bfbd2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_lock_raydium_lp/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_lock_raydium_lp"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/src/executor.rs new file mode 100644 index 0000000..406d45f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_lock_raydium_lp/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_lock_raydium_lp` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LockRaydiumLpExecutor; + +impl kb_execution_api::InstructionExecutor for crate::LockRaydiumLpExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_lock_raydium_lp"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::LOCK_RAYDIUM_LP_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_lock_raydium_lp"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/src/lib.rs new file mode 100644 index 0000000..333ec8d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_lock_raydium_lp/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_lock_raydium_lp/src/lib.rs +// version: 2 + +//! Executor crate for `lock_raydium_lp`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::LockRaydiumLpExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/Cargo.toml new file mode 100644 index 0000000..4e32e4e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/Cargo.toml @@ -0,0 +1,21 @@ +# file: kb_executor_metadata_metaplex_token_metadata/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_metadata_metaplex_token_metadata" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +mpl-token-metadata.workspace = true +serde_json.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/README.md b/migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/README.md new file mode 100644 index 0000000..d6eb75d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/README.md @@ -0,0 +1,15 @@ + + + +# `kb_executor_metadata_metaplex_token_metadata` + +Exécuteur dédié au programme Metaplex Token Metadata. + +La crate suit la frontière commune des exécuteurs du workspace : + +- `constants.rs` contient les constantes locales et la cible de tracing ; +- `executor.rs` expose `MetadataMetaplexTokenMetadataExecutor` et implémente `kb_execution_api::InstructionExecutor` ; +- `lib.rs` réexporte uniquement le type public ; +- le Program ID provient exclusivement de `kb_program_ids`. + +Dans `0.4.7-pre.001`, le support exact du Program ID retourne encore `Maybe`. `build_plan` produit uniquement un plan réservé sans instruction afin qu'aucune opération Metaplex ne puisse être exécutée avant validation complète des builders officiels, comptes ordonnés, autorités, signataires, préconditions, coûts, confirmations et postconditions stateful. diff --git a/migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/src/constants.rs new file mode 100644 index 0000000..0b5079c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/src/constants.rs @@ -0,0 +1,9 @@ +// file: kb_executor_metadata_metaplex_token_metadata/src/constants.rs +// version: 4 + +//! Local constants for the Metaplex Token Metadata executor. + +/// Status emitted by the reserved execution plan until exact builders are validated. +pub(crate) const RESERVED_EXECUTION_STATUS: &str = "reserved_executor"; +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_metadata_metaplex_token_metadata"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/src/executor.rs new file mode 100644 index 0000000..9c2674e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/src/executor.rs @@ -0,0 +1,72 @@ +// file: kb_executor_metadata_metaplex_token_metadata/src/executor.rs +// version: 5 + +//! Executor scaffold for Metaplex Token Metadata. + +/// Initial Metaplex Token Metadata executor implementation. +#[derive(Clone, Debug, Default)] +pub struct MetadataMetaplexTokenMetadataExecutor; + +impl kb_execution_api::InstructionExecutor for crate::MetadataMetaplexTokenMetadataExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_metadata_metaplex_token_metadata"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + tracing::debug!( + target: crate::TRACING_TARGET, + "Metaplex Token Metadata execution remains reserved pending builder validation" + ); + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from( + "kb_executor_metadata_metaplex_token_metadata", + ), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json(&serde_json::json!({ + "status": crate::RESERVED_EXECUTION_STATUS, + })) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} + +#[cfg(test)] +mod tests { + #[test] + fn executor_identity_and_program_boundary_are_exact() { + let executor = crate::MetadataMetaplexTokenMetadataExecutor; + assert_eq!( + kb_execution_api::InstructionExecutor::executor_name(&executor), + "kb_executor_metadata_metaplex_token_metadata" + ); + assert_eq!( + kb_execution_api::InstructionExecutor::program_ids(&executor), + &["metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"] + ); + assert_eq!(crate::TRACING_TARGET, "kb_executor_metadata_metaplex_token_metadata"); + assert_eq!(crate::RESERVED_EXECUTION_STATUS, "reserved_executor"); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/src/lib.rs new file mode 100644 index 0000000..0934c91 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_metadata_metaplex_token_metadata/src/lib.rs @@ -0,0 +1,18 @@ +// file: kb_executor_metadata_metaplex_token_metadata/src/lib.rs +// version: 4 + +//! Executor crate for Metaplex Token Metadata. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod executor; + +/// Status emitted by the reserved execution plan until exact builders are validated. +pub(crate) use crate::constants::RESERVED_EXECUTION_STATUS; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the Metaplex Token Metadata executor implemented by this crate. +pub use crate::executor::MetadataMetaplexTokenMetadataExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/Cargo.toml new file mode 100644 index 0000000..131d2e6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/Cargo.toml @@ -0,0 +1,20 @@ +# file: kb_executor_metadata_spl_name_service/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_metadata_spl_name_service" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/README.md b/migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/README.md new file mode 100644 index 0000000..d80e0c7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/README.md @@ -0,0 +1,14 @@ + + + +# kb_executor_metadata_spl_name_service + +Ce crate réserve l’exécuteur pour le programme SPL Name Service. + +## Rôle + +Il construira des instructions ou plans d’exécution indépendamment du décodeur correspondant. Le scaffold actuel ne signe, ne simule et n’envoie aucune transaction. + +## Statut + +L’exécuteur reconnaît uniquement ses program IDs vérifiés et retourne un plan réservé contenant zéro instruction. Les builders, contrôles de comptes, simulations et garde-fous seront ajoutés dans la version SPL dédiée du `ROADMAP.md`. diff --git a/migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/src/constants.rs new file mode 100644 index 0000000..7ef8ff0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_executor_metadata_spl_name_service/src/constants.rs +// version: 1 + +//! Local constants for the `kb_executor_metadata_spl_name_service` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_metadata_spl_name_service"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/src/executor.rs new file mode 100644 index 0000000..e9df61a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/src/executor.rs @@ -0,0 +1,92 @@ +// file: kb_executor_metadata_spl_name_service/src/executor.rs +// version: 5 + +//! Reserved executor for the `spl_name_service` surface. + +/// Reserved executor implementation for the verified program surface. +#[derive(Clone, Debug, Default)] +pub struct SplNameServiceExecutor; + +impl kb_execution_api::InstructionExecutor for crate::SplNameServiceExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_metadata_spl_name_service"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::SPL_NAME_SERVICE_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + tracing::debug!( + target: crate::TRACING_TARGET, + action = "build_reserved_execution_plan", + executor_name = "kb_executor_metadata_spl_name_service", + program_id = %request.program_id.0.as_str(), + operation_code = %request.operation_code, + instruction_count = 0_usize, + "build reserved SPL execution plan" + ); + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_metadata_spl_name_service"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor","surface":"spl_name_service"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} + +#[cfg(test)] +mod tests { + fn request(program_id: &str) -> kb_execution_api::ExecutionRequest { + return kb_execution_api::ExecutionRequest { + program_id: kb_model::ProgramId(program_id.to_string()), + operation_code: "reserved".to_string(), + payload_json: "{}".to_string(), + }; + } + + #[test] + fn exposes_only_registered_program_ids() { + let executor = crate::SplNameServiceExecutor; + let program_ids = kb_execution_api::InstructionExecutor::program_ids(&executor); + assert_eq!(program_ids.len(), 1); + for program_id in program_ids { + assert!(kb_program_ids::find_registered_program_id(program_id).is_some()); + } + } + + #[test] + fn reserved_plan_never_contains_instructions() { + let executor = crate::SplNameServiceExecutor; + let program_id = kb_execution_api::InstructionExecutor::program_ids(&executor)[0]; + let result = + kb_execution_api::InstructionExecutor::build_plan(&executor, &request(program_id)); + let plan = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("reserved plan failed: {error}"), + }; + assert_eq!(plan.instruction_count, 0); + assert!(plan.payload_json.contains("reserved_executor")); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/src/lib.rs new file mode 100644 index 0000000..c7344b0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_metadata_spl_name_service/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_executor_metadata_spl_name_service/src/lib.rs +// version: 3 + +//! Executor crate for `spl_name_service`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the reserved executor type implemented by this crate. +pub use crate::executor::SplNameServiceExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/Cargo.toml new file mode 100644 index 0000000..58ff124 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_nft_metaplex_bubblegum/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_nft_metaplex_bubblegum" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/README.md b/migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/README.md new file mode 100644 index 0000000..ed7c946 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_nft_metaplex_bubblegum + +Ce crate réserve l'exécuteur de la surface `nft_metaplex_bubblegum`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/src/constants.rs new file mode 100644 index 0000000..71f2856 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_nft_metaplex_bubblegum/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_nft_metaplex_bubblegum"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/src/executor.rs new file mode 100644 index 0000000..c936c6f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_nft_metaplex_bubblegum/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_nft_metaplex_bubblegum` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct NftMetaplexBubblegumExecutor; + +impl kb_execution_api::InstructionExecutor for crate::NftMetaplexBubblegumExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_nft_metaplex_bubblegum"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::NFT_METAPLEX_BUBBLEGUM_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_nft_metaplex_bubblegum"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/src/lib.rs new file mode 100644 index 0000000..fc0e49b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_nft_metaplex_bubblegum/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_nft_metaplex_bubblegum/src/lib.rs +// version: 2 + +//! Executor crate for `nft_metaplex_bubblegum`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::NftMetaplexBubblegumExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/Cargo.toml new file mode 100644 index 0000000..4e3551d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_orderbook_jupiter_limit_order/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_orderbook_jupiter_limit_order" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/README.md b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/README.md new file mode 100644 index 0000000..f638ce0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_orderbook_jupiter_limit_order + +Ce crate réserve l'exécuteur de la surface `orderbook_jupiter_limit_order`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/src/constants.rs new file mode 100644 index 0000000..1932c3c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_orderbook_jupiter_limit_order/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_orderbook_jupiter_limit_order"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/src/executor.rs new file mode 100644 index 0000000..114876d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_orderbook_jupiter_limit_order/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_orderbook_jupiter_limit_order` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct OrderbookJupiterLimitOrderExecutor; + +impl kb_execution_api::InstructionExecutor for crate::OrderbookJupiterLimitOrderExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_orderbook_jupiter_limit_order"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ORDERBOOK_JUPITER_LIMIT_ORDER_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_orderbook_jupiter_limit_order"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/src/lib.rs new file mode 100644 index 0000000..5a3a87e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_orderbook_jupiter_limit_order/src/lib.rs +// version: 2 + +//! Executor crate for `orderbook_jupiter_limit_order`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::OrderbookJupiterLimitOrderExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/Cargo.toml new file mode 100644 index 0000000..e1c11bd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_orderbook_jupiter_limit_order_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_orderbook_jupiter_limit_order_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/README.md b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/README.md new file mode 100644 index 0000000..4eb7016 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_orderbook_jupiter_limit_order_v2 + +Ce crate réserve l'exécuteur de la surface `orderbook_jupiter_limit_order_v2`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/src/constants.rs new file mode 100644 index 0000000..5faea3e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_orderbook_jupiter_limit_order_v2/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_orderbook_jupiter_limit_order_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/src/executor.rs new file mode 100644 index 0000000..4a17d33 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/src/executor.rs @@ -0,0 +1,51 @@ +// file: kb_executor_orderbook_jupiter_limit_order_v2/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_orderbook_jupiter_limit_order_v2` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct OrderbookJupiterLimitOrderV2Executor; + +impl kb_execution_api::InstructionExecutor for crate::OrderbookJupiterLimitOrderV2Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_orderbook_jupiter_limit_order_v2"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ORDERBOOK_JUPITER_LIMIT_ORDER_V2_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from( + "kb_executor_orderbook_jupiter_limit_order_v2", + ), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/src/lib.rs new file mode 100644 index 0000000..7ff19fb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_orderbook_jupiter_limit_order_v2/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_orderbook_jupiter_limit_order_v2/src/lib.rs +// version: 2 + +//! Executor crate for `orderbook_jupiter_limit_order_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::OrderbookJupiterLimitOrderV2Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/Cargo.toml new file mode 100644 index 0000000..ff7f14a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_orderbook_openbook_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_orderbook_openbook_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/README.md b/migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/README.md new file mode 100644 index 0000000..73f5933 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_orderbook_openbook_v2 + +Ce crate réserve l'exécuteur de la surface `orderbook_openbook_v2`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/src/constants.rs new file mode 100644 index 0000000..fbb631b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_orderbook_openbook_v2/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_orderbook_openbook_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/src/executor.rs new file mode 100644 index 0000000..8b6762d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_orderbook_openbook_v2/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_orderbook_openbook_v2` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct OrderbookOpenbookV2Executor; + +impl kb_execution_api::InstructionExecutor for crate::OrderbookOpenbookV2Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_orderbook_openbook_v2"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ORDERBOOK_OPENBOOK_V2_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_orderbook_openbook_v2"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/src/lib.rs new file mode 100644 index 0000000..3f174d1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_orderbook_openbook_v2/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_orderbook_openbook_v2/src/lib.rs +// version: 2 + +//! Executor crate for `orderbook_openbook_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::OrderbookOpenbookV2Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/Cargo.toml new file mode 100644 index 0000000..310ca0d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_perpetuals_drift_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_perpetuals_drift_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/README.md b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/README.md new file mode 100644 index 0000000..edd88bc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_perpetuals_drift_v2 + +Ce crate réserve l'exécuteur de la surface `perpetuals_drift_v2`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/src/constants.rs new file mode 100644 index 0000000..eb313ea --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_perpetuals_drift_v2/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_perpetuals_drift_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/src/executor.rs new file mode 100644 index 0000000..324f1ed --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_perpetuals_drift_v2/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_perpetuals_drift_v2` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct PerpetualsDriftV2Executor; + +impl kb_execution_api::InstructionExecutor for crate::PerpetualsDriftV2Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_perpetuals_drift_v2"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::PERPETUALS_DRIFT_V2_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_perpetuals_drift_v2"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/src/lib.rs new file mode 100644 index 0000000..28c43e1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_drift_v2/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_perpetuals_drift_v2/src/lib.rs +// version: 2 + +//! Executor crate for `perpetuals_drift_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::PerpetualsDriftV2Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/Cargo.toml new file mode 100644 index 0000000..cfbe3a0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_perpetuals_jupiter/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_perpetuals_jupiter" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/README.md b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/README.md new file mode 100644 index 0000000..90de274 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_perpetuals_jupiter + +Ce crate réserve l'exécuteur de la surface `perpetuals_jupiter`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/src/constants.rs new file mode 100644 index 0000000..9df54fb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_perpetuals_jupiter/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_perpetuals_jupiter"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/src/executor.rs new file mode 100644 index 0000000..a1cbc2f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_perpetuals_jupiter/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_perpetuals_jupiter` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct PerpetualsJupiterExecutor; + +impl kb_execution_api::InstructionExecutor for crate::PerpetualsJupiterExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_perpetuals_jupiter"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::PERPETUALS_JUPITER_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_perpetuals_jupiter"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/src/lib.rs new file mode 100644 index 0000000..1959724 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_jupiter/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_perpetuals_jupiter/src/lib.rs +// version: 2 + +//! Executor crate for `perpetuals_jupiter`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::PerpetualsJupiterExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/Cargo.toml new file mode 100644 index 0000000..af9fc8d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_perpetuals_zeta/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_perpetuals_zeta" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/README.md b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/README.md new file mode 100644 index 0000000..6e44392 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_perpetuals_zeta + +Ce crate réserve l'exécuteur de la surface `perpetuals_zeta`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/src/constants.rs new file mode 100644 index 0000000..4e7cc2d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_perpetuals_zeta/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_perpetuals_zeta"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/src/executor.rs new file mode 100644 index 0000000..96931a1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_perpetuals_zeta/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_perpetuals_zeta` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct PerpetualsZetaExecutor; + +impl kb_execution_api::InstructionExecutor for crate::PerpetualsZetaExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_perpetuals_zeta"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::PERPETUALS_ZETA_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_perpetuals_zeta"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/src/lib.rs new file mode 100644 index 0000000..46c355c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_perpetuals_zeta/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_perpetuals_zeta/src/lib.rs +// version: 2 + +//! Executor crate for `perpetuals_zeta`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::PerpetualsZetaExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/Cargo.toml new file mode 100644 index 0000000..7005b54 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_router_dflow_aggregator_v4/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_router_dflow_aggregator_v4" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/README.md b/migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/README.md new file mode 100644 index 0000000..34080fd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_router_dflow_aggregator_v4 + +Ce crate réserve l'exécuteur de la surface `router_dflow_aggregator_v4`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/src/constants.rs new file mode 100644 index 0000000..8e387d4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_router_dflow_aggregator_v4/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_router_dflow_aggregator_v4"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/src/executor.rs new file mode 100644 index 0000000..3e4be9a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_router_dflow_aggregator_v4/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_router_dflow_aggregator_v4` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct RouterDflowAggregatorV4Executor; + +impl kb_execution_api::InstructionExecutor for crate::RouterDflowAggregatorV4Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_router_dflow_aggregator_v4"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ROUTER_DFLOW_AGGREGATOR_V4_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_router_dflow_aggregator_v4"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/src/lib.rs new file mode 100644 index 0000000..6508847 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_dflow_aggregator_v4/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_router_dflow_aggregator_v4/src/lib.rs +// version: 2 + +//! Executor crate for `router_dflow_aggregator_v4`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::RouterDflowAggregatorV4Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/Cargo.toml new file mode 100644 index 0000000..8e46096 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_router_jupiter_aggregator_v4/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_router_jupiter_aggregator_v4" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/README.md b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/README.md new file mode 100644 index 0000000..412695e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_router_jupiter_aggregator_v4 + +Ce crate réserve l'exécuteur de la surface `router_jupiter_aggregator_v4`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/src/constants.rs new file mode 100644 index 0000000..216d690 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_router_jupiter_aggregator_v4/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_router_jupiter_aggregator_v4"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/src/executor.rs new file mode 100644 index 0000000..bb240f9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_router_jupiter_aggregator_v4/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_router_jupiter_aggregator_v4` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct RouterJupiterAggregatorV4Executor; + +impl kb_execution_api::InstructionExecutor for crate::RouterJupiterAggregatorV4Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_router_jupiter_aggregator_v4"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ROUTER_JUPITER_AGGREGATOR_V4_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_router_jupiter_aggregator_v4"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/src/lib.rs new file mode 100644 index 0000000..44e7459 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v4/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_router_jupiter_aggregator_v4/src/lib.rs +// version: 2 + +//! Executor crate for `router_jupiter_aggregator_v4`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::RouterJupiterAggregatorV4Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/Cargo.toml new file mode 100644 index 0000000..c199858 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_router_jupiter_aggregator_v6/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_router_jupiter_aggregator_v6" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/README.md b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/README.md new file mode 100644 index 0000000..f67e99a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_router_jupiter_aggregator_v6 + +Ce crate réserve l'exécuteur de la surface `router_jupiter_aggregator_v6`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/src/constants.rs new file mode 100644 index 0000000..968f802 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_router_jupiter_aggregator_v6/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_router_jupiter_aggregator_v6"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/src/executor.rs new file mode 100644 index 0000000..786b482 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_router_jupiter_aggregator_v6/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_router_jupiter_aggregator_v6` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct RouterJupiterAggregatorV6Executor; + +impl kb_execution_api::InstructionExecutor for crate::RouterJupiterAggregatorV6Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_router_jupiter_aggregator_v6"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ROUTER_JUPITER_AGGREGATOR_V6_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_router_jupiter_aggregator_v6"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/src/lib.rs new file mode 100644 index 0000000..5342216 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_aggregator_v6/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_router_jupiter_aggregator_v6/src/lib.rs +// version: 2 + +//! Executor crate for `router_jupiter_aggregator_v6`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::RouterJupiterAggregatorV6Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/Cargo.toml new file mode 100644 index 0000000..eee03e3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_router_jupiter_dca/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_router_jupiter_dca" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/README.md b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/README.md new file mode 100644 index 0000000..225c87e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_router_jupiter_dca + +Ce crate réserve l'exécuteur de la surface `router_jupiter_dca`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/src/constants.rs new file mode 100644 index 0000000..5940fa8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_router_jupiter_dca/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_router_jupiter_dca"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/src/executor.rs new file mode 100644 index 0000000..eb26d00 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_router_jupiter_dca/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_router_jupiter_dca` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct RouterJupiterDcaExecutor; + +impl kb_execution_api::InstructionExecutor for crate::RouterJupiterDcaExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_router_jupiter_dca"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ROUTER_JUPITER_DCA_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_router_jupiter_dca"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/src/lib.rs new file mode 100644 index 0000000..4962b80 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_jupiter_dca/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_router_jupiter_dca/src/lib.rs +// version: 2 + +//! Executor crate for `router_jupiter_dca`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::RouterJupiterDcaExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/Cargo.toml new file mode 100644 index 0000000..47b57f8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_router_okx_labs_v1/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_router_okx_labs_v1" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/README.md b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/README.md new file mode 100644 index 0000000..fef2613 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_router_okx_labs_v1 + +Ce crate réserve l'exécuteur de la surface `router_okx_labs_v1`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/src/constants.rs new file mode 100644 index 0000000..74ef316 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_router_okx_labs_v1/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_router_okx_labs_v1"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/src/executor.rs new file mode 100644 index 0000000..036fec3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_router_okx_labs_v1/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_router_okx_labs_v1` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct RouterOkxLabsV1Executor; + +impl kb_execution_api::InstructionExecutor for crate::RouterOkxLabsV1Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_router_okx_labs_v1"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ROUTER_OKX_LABS_V1_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_router_okx_labs_v1"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/src/lib.rs new file mode 100644 index 0000000..41ea911 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v1/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_router_okx_labs_v1/src/lib.rs +// version: 2 + +//! Executor crate for `router_okx_labs_v1`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::RouterOkxLabsV1Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/Cargo.toml new file mode 100644 index 0000000..3c961ba --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_router_okx_labs_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_router_okx_labs_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/README.md b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/README.md new file mode 100644 index 0000000..e7536ba --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_router_okx_labs_v2 + +Ce crate réserve l'exécuteur de la surface `router_okx_labs_v2`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/src/constants.rs new file mode 100644 index 0000000..537394f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_router_okx_labs_v2/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_router_okx_labs_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/src/executor.rs new file mode 100644 index 0000000..40eae89 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_router_okx_labs_v2/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_router_okx_labs_v2` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct RouterOkxLabsV2Executor; + +impl kb_execution_api::InstructionExecutor for crate::RouterOkxLabsV2Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_router_okx_labs_v2"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ROUTER_OKX_LABS_V2_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_router_okx_labs_v2"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/src/lib.rs new file mode 100644 index 0000000..26e68a6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_router_okx_labs_v2/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_router_okx_labs_v2/src/lib.rs +// version: 2 + +//! Executor crate for `router_okx_labs_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::RouterOkxLabsV2Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/Cargo.toml new file mode 100644 index 0000000..40d8bb3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_rwa_ondo_global_markets/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_rwa_ondo_global_markets" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/README.md b/migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/README.md new file mode 100644 index 0000000..61d3c64 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_rwa_ondo_global_markets + +Ce crate réserve l'exécuteur de la surface `rwa_ondo_global_markets`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/src/constants.rs new file mode 100644 index 0000000..4a217f6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_rwa_ondo_global_markets/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_rwa_ondo_global_markets"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/src/executor.rs new file mode 100644 index 0000000..fc8aed0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_rwa_ondo_global_markets/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_rwa_ondo_global_markets` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct RwaOndoGlobalMarketsExecutor; + +impl kb_execution_api::InstructionExecutor for crate::RwaOndoGlobalMarketsExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_rwa_ondo_global_markets"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::RWA_ONDO_GLOBAL_MARKETS_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_rwa_ondo_global_markets"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/src/lib.rs new file mode 100644 index 0000000..54359a0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_rwa_ondo_global_markets/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_rwa_ondo_global_markets/src/lib.rs +// version: 2 + +//! Executor crate for `rwa_ondo_global_markets`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::RwaOndoGlobalMarketsExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_solana_core/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_solana_core/Cargo.toml new file mode 100644 index 0000000..e95a398 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_solana_core/Cargo.toml @@ -0,0 +1,40 @@ +# file: kb_executor_solana_core/Cargo.toml +# version: 11 + +[package] +name = "kb_executor_solana_core" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde.workspace = true +serde_json.workspace = true +solana-address-lookup-table-interface.workspace = true +solana-compute-budget-interface.workspace = true +solana-feature-gate-interface.workspace = true +solana-hash.workspace = true +solana-instruction.workspace = true +solana-loader-v3-interface.workspace = true +solana-pubkey.workspace = true +solana-sdk-ids.workspace = true +solana-system-interface.workspace = true +solana-stake-interface.workspace = true +solana-vote-interface.workspace = true +solana-zk-elgamal-proof-interface.workspace = true +tracing.workspace = true +ts-rs.workspace = true +wincode.workspace = true + +[dev-dependencies] +solana-ed25519-program.workspace = true +solana-secp256k1-program.workspace = true +solana-secp256r1-program.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_solana_core/README.md b/migration/khadhroony-bot2-reference/kb_executor_solana_core/README.md new file mode 100644 index 0000000..20dc88b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_solana_core/README.md @@ -0,0 +1,915 @@ + + + +# kb_executor_solana_core + +Cette crate construit des plans d’instructions déterministes pour les dix-huit surfaces du registre natif `solana_core`. Elle expose actuellement 109 opérations : dix-sept System, quatre Compute Budget, cinq Address Lookup Table, six formes de vérification par précompile, deux opérations Config, deux opérations Feature, deux opérations Slashing, trois opérations ZK ElGamal, vingt-quatre opérations Stake, vingt-quatre opérations Vote, onze opérations Loader v3 et neuf opérations Loader v4. Les comptes natifs non exécutables, notamment Stake Config, en sont exclus. L’ancien ZK Token Proof, BPF Loader v1/v2 et Stake Redelegate sont conservés comme surfaces historiques de décodage seulement; Native Loader n’expose pas de builder transactionnel client. + +Elle ne charge aucun wallet, ne signe rien, n’appelle aucun RPC et n’envoie aucune transaction. Les opérations dangereuses restent des capacités de bibliothèque : leur exposition éventuelle relève de l’orchestrateur ou de l’application appelante. + +## API publique + +### `SolanaCoreExecutor` + +`SolanaCoreExecutor` implémente deux contrats : + +- `kb_execution_api::TypedInstructionExecutor` pour l’API typée actuelle ; +- `kb_execution_api::InstructionExecutor` pour le pont historique fondé sur un payload JSON. + +| Fonction | Paramètres | Résultat | Rôle | +|-------------------------------------------------|--------------------------------------------------|------------------------------------------|------------------------------------------------------------------------------------------------------------------| +| `TypedInstructionExecutor::capability` | `program_id: &ProgramId`, `operation_code: &str` | `ExecutionCapability` | Indique exactement si le couple programme/opération possède un builder. | +| `TypedInstructionExecutor::build_prepared_plan` | `intent: &SolanaCoreExecutionIntent` | `kb_core::Result` | Valide l’intent, construit l’instruction officielle et déclare comptes, signataires, coût et politique. | +| `InstructionExecutor::program_ids` | aucun | `&'static [&'static str]` | Retourne les dix-huit program IDs natifs classifiés par la crate. | +| `InstructionExecutor::supports_request` | `request: &ExecutionRequest` | `ExecutionSupport` | Pont historique exact `Yes/No`, sans état `Maybe`. | +| `InstructionExecutor::build_plan` | `request: &ExecutionRequest` | `kb_core::Result` | Désérialise un `SolanaCoreExecutionIntent`, construit le plan typé puis le sérialise dans le contrat historique. | + +### Types et constantes réexportés + +| Export | Usage | +|-----------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------| +| `SolanaCoreExecutor` | Point d’entrée stateless pour interroger une capacité et construire un plan. | +| `SolanaCoreExecutionIntent` | Enveloppe complète contenant identifiant, fee payer, politique et opération typée. | +| `SolanaCoreOperation` | Enum des opérations System, Compute Budget, Address Lookup Table, précompiles, Config, Feature, Slashing, ZK ElGamal, Stake, Vote et Loaders disponibles. | +| `SystemTransferRecipient` | Destination ordonnée d’un `SystemTransferMany`. | +| `Ed25519VerificationOffsets` | Table d’offsets Ed25519, avec `u16::MAX` comme référence aux données de l’instruction courante. | +| `Secp256k1VerificationOffsets` | Table d’offsets secp256k1 utilisant des index d’instructions `u8` explicites. | +| `Secp256r1VerificationOffsets` | Table d’offsets secp256r1, avec `u16::MAX` comme référence aux données de l’instruction courante. | +| `SYSTEM_*_OPERATION` | Codes stables des dix-sept opérations System, utilisables dans les registries, logs et ponts historiques. | +| `COMPUTE_BUDGET_*_OPERATION` | Codes stables des quatre opérations Compute Budget actuelles. | +| `ADDRESS_LOOKUP_TABLE_*_OPERATION` | Codes stables des cinq opérations Address Lookup Table. | +| `ED25519_*_OPERATION`, `SECP256K1_*_OPERATION`, `SECP256R1_*_OPERATION` | Codes stables des six formes inline/offsets des précompiles de signature. | +| `CONFIG_*_OPERATION`, `FEATURE_*_OPERATION`, `SLASHING_*_OPERATION`, `ZK_ELGAMAL_*_OPERATION` | Codes stables des neuf opérations natives ajoutées dans `pre.017`. | +| `ConfigKey` | Entrée ordonnée de la liste `ConfigKeys`, avec rôle de signataire explicite. | +| `ZkElGamalProofType` | Douze discriminants de preuve ZK ElGamal et tailles POD officielles. | +| `ZkElGamalContextState` | Compte de contexte préalloué et autorité enregistrée par la preuve. | +| `StakeAuthorizationKind` | Sélectionne l’autorité `staker` ou `withdrawer` dans les builders Stake authorize. | +| `STAKE_*_OPERATION` | Codes stables des vingt-quatre helpers clients Stake actuels. | +| `VoteAuthorizationKind` | Sélectionne l’autorité Vote `voter`, `withdrawer` ou `voter_with_bls` avec tailles BLS bornées. | +| `VoteCommissionKind` | Sélectionne la commission Vote v2 d’inflation ou de revenu de bloc. | +| `VoteLockout` | Entrée de tour typée contenant slot et confirmation count. | +| `VOTE_*_OPERATION` | Codes stables des vingt-quatre opérations Vote actuelles. | +| `LOADER_V3_*_OPERATION` | Codes stables des onze plans Loader v3. | +| `LOADER_V4_*_OPERATION` | Codes stables des neuf plans Loader v4. | + +Les modules internes ne sont pas publics. Les appelants utilisent les réexports de `lib.rs` et les traits de `kb_execution_api`. Le module privé `validation` centralise le parsing des `Pubkey`, les IDs natifs, les contrôles de longueur, de montants positifs, de comptes distincts, de dérivation seedée et la construction d’erreurs partagées ; les modules de programme conservent uniquement leurs règles et libellés métier. + +## Contrat d’intent et résultat + +Toutes les opérations utilisent : + +```rust +SolanaCoreExecutionIntent { + intent_id, + fee_payer, + policy, + operation, +} +``` + +- `intent_id` : identifiant stable fourni par l’appelant pour les logs et la corrélation ; +- `fee_payer` : compte qui paiera les frais de transaction ; +- `policy` : `ExecutionPolicy` évaluée séparément par `kb_execution_safety` ; +- `operation` : variante typée de `SolanaCoreOperation`. + +Le résultat `PreparedExecutionPlan` contient : + +- l’exécuteur et sa version ; +- le code d’opération ; +- le fee payer ; +- les instructions encodées dans leur ordre final ; +- les comptes ordonnés avec flags signer/writable ; +- les signataires requis dédupliqués et leur rôle ; +- la politique de cluster, simulation, blockhash/nonce et plafonds ; +- le nombre de lamports explicitement dépensés ou verrouillés ; +- le prix Compute Budget demandé, lorsqu’il existe. + +### Exemple minimal + +```rust +let executor = kb_executor_solana_core::SolanaCoreExecutor; +let intent = kb_executor_solana_core::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("compute-budget-limit-1"), + fee_payer: payer.clone(), + policy, + operation: kb_executor_solana_core::SolanaCoreOperation::ComputeBudgetSetUnitLimit { + units: 500_000, + }, +}; +let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &executor, + &intent, +)?; +``` + +Le plan doit ensuite passer par `kb_execution_safety`, `kb_execution_solana` et le RPC. Cet exemple ne signe et n’envoie rien. + +## Opérations actuellement constructibles + +Cent neuf opérations disposent d’un builder : dix-sept System, quatre Compute Budget, cinq Address Lookup Table, six précompiles de signature, neuf Config/Feature/Slashing/ZK ElGamal, vingt-quatre Stake, vingt-quatre Vote, onze Loader v3 et neuf Loader v4. + +### `solana_core.system.transfer` + +```rust +SolanaCoreOperation::SystemTransfer { from, to, lamports } +``` + +| Paramètre | Type | Description | +|------------|----------|-----------------------------------------| +| `from` | `Pubkey` | Compte source writable et signataire. | +| `to` | `Pubkey` | Compte destinataire writable. | +| `lamports` | `u64` | Montant transféré, strictement positif. | + +**Résultat et effet :** une instruction System `Transfer`. Le plan exige `from`, ajoute le fee payer si nécessaire et reporte `lamports` dans `requested_spend_lamports`. + +### `solana_core.system.transfer_many` + +```rust +SolanaCoreOperation::SystemTransferMany { + from, + recipients: Vec, +} +``` + +**Résultat et effet :** un vecteur ordonné d’instructions officielles System `Transfer`, une par destinataire. La liste doit être non vide, chaque montant est strictement positif, chaque destination diffère de `from` et la somme est calculée avec détection de dépassement `u64`. Le plan déclare un seul signataire source et reporte la somme dans `requested_spend_lamports`. La taille wire finale reste contrôlée lors de l’assemblage de transaction. + +### `solana_core.system.transfer_with_seed` + +```rust +SolanaCoreOperation::SystemTransferWithSeed { + from, + from_base, + from_seed, + from_owner, + to, + lamports, +} +``` + +| Paramètre | Type | Description | +|--------------|----------|------------------------------------------------------------| +| `from` | `Pubkey` | Compte source dérivé, writable mais non signataire direct. | +| `from_base` | `Pubkey` | Base de dérivation et signataire requis. | +| `from_seed` | `String` | Seed utilisé pour dériver `from`. | +| `from_owner` | `Pubkey` | Owner utilisé dans la dérivation de `from`. | +| `to` | `Pubkey` | Destinataire writable. | +| `lamports` | `u64` | Montant strictement positif. | + +**Résultat et effet :** une instruction System `TransferWithSeed`. Le builder recalcule `Pubkey::create_with_seed(from_base, from_seed, from_owner)` et refuse l’intent si l’adresse obtenue diffère de `from`. + +### `solana_core.system.create_account` + +```rust +SolanaCoreOperation::SystemCreateAccount { + from, + new_account, + lamports, + space, + owner, +} +``` + +| Paramètre | Type | Description | +|---------------|----------|---------------------------------------------------------------------------------| +| `from` | `Pubkey` | Compte de financement writable et signataire. | +| `new_account` | `Pubkey` | Nouveau compte writable et signataire. | +| `lamports` | `u64` | Financement initial, strictement positif. | +| `space` | `u64` | Taille de données allouée ; zéro est valide pour un compte System sans données. | +| `owner` | `Pubkey` | Programme propriétaire après création. | + +**Résultat et effet :** une instruction officielle `CreateAccount` qui finance, alloue et assigne le compte. Le calcul du minimum rent-exempt appartient à la couche RPC/orchestration. + +### `solana_core.system.create_account_with_seed` + +```rust +SolanaCoreOperation::SystemCreateAccountWithSeed { + from, + new_account, + base, + seed, + lamports, + space, + owner, +} +``` + +| Paramètre | Type | Description | +|---------------|----------|------------------------------------------------------| +| `from` | `Pubkey` | Compte de financement writable et signataire. | +| `new_account` | `Pubkey` | Adresse créée à partir de `base`, `seed` et `owner`. | +| `base` | `Pubkey` | Base signataire ; elle peut être identique à `from`. | +| `seed` | `String` | Seed borné par l’interface Solana. | +| `lamports` | `u64` | Financement initial strictement positif. | +| `space` | `u64` | Taille allouée, zéro compris. | +| `owner` | `Pubkey` | Owner du compte et composant de sa dérivation. | + +**Résultat et effet :** une instruction `CreateAccountWithSeed`. Le builder recalcule l’adresse dérivée avant de construire le plan et déduplique `from`/`base` lorsqu’ils désignent le même signataire. + +### `solana_core.system.create_account_allow_prefund` + +```rust +SolanaCoreOperation::SystemCreateAccountAllowPrefund { + new_account, + payer, + lamports, + space, + owner, +} +``` + +| Paramètre | Type | Description | +|---------------|------------------|------------------------------------------------------------------| +| `new_account` | `Pubkey` | Compte préfinancé à créer, writable et toujours signataire. | +| `payer` | `Option` | Payer writable/signataire pour des lamports additionnels. | +| `lamports` | `u64` | Montant additionnel ; doit être zéro lorsque `payer` est absent. | +| `space` | `u64` | Taille de données à allouer, zéro compris. | +| `owner` | `Pubkey` | Programme propriétaire après création. | + +**Résultat et effet :** une instruction `CreateAccountAllowPrefund`, qui n’impose pas un solde initial nul. Cette opération peut verrouiller tout le solde existant dans le compte assigné ; elle doit être protégée par une politique appelante stricte et peut dépendre de l’activation runtime correspondante sur le cluster ciblé. + +### `solana_core.system.allocate` + +```rust +SolanaCoreOperation::SystemAllocate { account, space } +``` + +| Paramètre | Type | Description | +|-----------|----------|---------------------------------------| +| `account` | `Pubkey` | Compte System writable et signataire. | +| `space` | `u64` | Nouvelle taille strictement positive. | + +**Résultat et effet :** une instruction `Allocate`. Elle ne finance pas le compte et ne change pas son propriétaire. + +### `solana_core.system.allocate_with_seed` + +```rust +SolanaCoreOperation::SystemAllocateWithSeed { + account, + base, + seed, + space, + owner, +} +``` + +| Paramètre | Type | Description | +|-----------|----------|--------------------------------------------------------| +| `account` | `Pubkey` | Compte dérivé writable. | +| `base` | `Pubkey` | Base de dérivation et signataire requis. | +| `seed` | `String` | Seed de dérivation. | +| `space` | `u64` | Taille strictement positive. | +| `owner` | `Pubkey` | Owner utilisé dans la dérivation et assigné au compte. | + +**Résultat et effet :** une instruction `AllocateWithSeed`. L’adresse `account` est recalculée et validée avant construction. + +### `solana_core.system.assign` + +```rust +SolanaCoreOperation::SystemAssign { account, owner } +``` + +| Paramètre | Type | Description | +|-----------|----------|---------------------------------------| +| `account` | `Pubkey` | Compte System writable et signataire. | +| `owner` | `Pubkey` | Nouveau programme propriétaire. | + +**Résultat et effet :** une instruction `Assign`. Elle ne transfère aucun lamport et n’alloue aucune donnée. + +### `solana_core.system.assign_with_seed` + +```rust +SolanaCoreOperation::SystemAssignWithSeed { + account, + base, + seed, + owner, +} +``` + +| Paramètre | Type | Description | +|-----------|----------|------------------------------------------------------| +| `account` | `Pubkey` | Compte dérivé writable. | +| `base` | `Pubkey` | Base de dérivation et signataire requis. | +| `seed` | `String` | Seed de dérivation. | +| `owner` | `Pubkey` | Nouvel owner et composant de la dérivation attendue. | + +**Résultat et effet :** une instruction `AssignWithSeed`. Le builder refuse une adresse qui ne correspond pas exactement à `base + seed + owner`. + +### `solana_core.system.nonce.create` + +```rust +SolanaCoreOperation::SystemCreateNonceAccount { + from, + nonce_account, + authority, + lamports, +} +``` + +**Résultat et effet :** deux instructions officielles ordonnées : `CreateAccount` avec l’espace nonce officiel, puis `InitializeNonceAccount`. `from` et `nonce_account` signent ; `authority` est stockée dans l’état mais ne signe pas nécessairement la création. `lamports` doit être strictement positif et être calculé par l’orchestrateur avec `getMinimumBalanceForRentExemption`. + +### `solana_core.system.nonce.create_with_seed` + +```rust +SolanaCoreOperation::SystemCreateNonceAccountWithSeed { + from, + nonce_account, + base, + seed, + authority, + lamports, +} +``` + +**Résultat et effet :** deux instructions officielles `CreateAccountWithSeed` puis `InitializeNonceAccount`. Le builder recalcule `nonce_account = create_with_seed(base, seed, System Program)` et exige `from` ainsi que `base` lorsqu’ils diffèrent. + +### `solana_core.system.nonce.initialize` + +```rust +SolanaCoreOperation::SystemInitializeNonceAccount { + nonce_account, + authority, +} +``` + +**Résultat et effet :** une instruction wire officielle `InitializeNonceAccount` pour un compte System déjà créé, rent-exempt et non initialisé. L’interface 3.2 n’expose pas de helper public autonome ; la crate encode donc le type officiel `SystemInstruction` avec `wincode` et reproduit exactement les comptes sysvar attendus. Aucun signataire d’instruction n’est requis, hors fee payer du message. + +### `solana_core.system.nonce.advance` + +```rust +SolanaCoreOperation::SystemAdvanceNonceAccount { + nonce_account, + authority, +} +``` + +**Résultat et effet :** une instruction officielle `AdvanceNonceAccount`. L’autorité courante signe. Dans cette tranche, elle est une opération de lifecycle envoyée avec un recent blockhash normal ; elle ne constitue pas encore l’assemblage spécial d’une transaction utilisant la valeur nonce comme blockhash. + +### `solana_core.system.nonce.authorize` + +```rust +SolanaCoreOperation::SystemAuthorizeNonceAccount { + nonce_account, + authority, + new_authority, +} +``` + +**Résultat et effet :** une instruction officielle `AuthorizeNonceAccount`. L’autorité courante signe et la nouvelle autorité est enregistrée dans l’état. + +### `solana_core.system.nonce.withdraw` + +```rust +SolanaCoreOperation::SystemWithdrawNonceAccount { + nonce_account, + authority, + to, + lamports, +} +``` + +**Résultat et effet :** une instruction officielle `WithdrawNonceAccount`. L’autorité signe, le montant est reporté comme dépense explicite et doit laisser le compte rent-exempt ou le vider complètement ; cette contrainte d’état est validée par simulation/runtime. + +### `solana_core.system.nonce.upgrade` + +```rust +SolanaCoreOperation::SystemUpgradeNonceAccount { nonce_account } +``` + +**Résultat et effet :** une instruction officielle idempotente `UpgradeNonceAccount` sur un ancien état nonce. Aucun signataire d’instruction n’est requis, hors fee payer. + +Toutes les opérations de gestion nonce ci-dessus exigent `ExecutionBlockhashKind::Latest`. Elles administrent le compte avec une transaction ordinaire. Pour une opération métier utilisant réellement un durable nonce, l’exécuteur accepte une politique `DurableNonce`, exige compte et autorité, puis ajoute cette autorité aux signataires requis ; `kb_execution_solana` valide ensuite l’état on-chain et réalise l’assemblage spécial. + +### `solana_core.compute_budget.request_heap_frame` + +```rust +SolanaCoreOperation::ComputeBudgetRequestHeapFrame { bytes } +``` + +| Paramètre | Type | Description | +|-----------|-------|----------------------------------------------------------------------------------------------| +| `bytes` | `u32` | Taille demandée pour chaque heap de programme, entre 32 KiB et 256 KiB et multiple de 1 024. | + +**Résultat et effet :** une instruction officielle `RequestHeapFrame` sans compte ni signataire propre. La taille s’applique à chaque programme exécuté par la transaction, y compris les CPI. Le builder refuse les valeurs que le runtime classerait `InvalidInstructionData`. + +### `solana_core.compute_budget.set_compute_unit_limit` + +```rust +SolanaCoreOperation::ComputeBudgetSetUnitLimit { units } +``` + +| Paramètre | Type | Description | +|-----------|-------|-------------------------------------------------| +| `units` | `u32` | Limite demandée, comprise entre 1 et 1 400 000. | + +**Résultat et effet :** une instruction Compute Budget sans compte ni signataire propre. Elle doit être assemblée dans la transaction dont elle modifie la limite. + +### `solana_core.compute_budget.set_compute_unit_price` + +```rust +SolanaCoreOperation::ComputeBudgetSetUnitPrice { micro_lamports } +``` + +| Paramètre | Type | Description | +|------------------|-------|--------------------------------| +| `micro_lamports` | `u64` | Prix demandé par compute unit. | + +**Résultat et effet :** une instruction Compute Budget sans compte propre. Le plan reporte la valeur dans `requested_compute_unit_price_micro_lamports` afin que `kb_execution_safety` applique le plafond configuré. + +### `solana_core.compute_budget.set_loaded_accounts_data_size_limit` + +```rust +SolanaCoreOperation::ComputeBudgetSetLoadedAccountsDataSizeLimit { bytes } +``` + +| Paramètre | Type | Description | +|-----------|-------|----------------------------------------------------------------------------------| +| `bytes` | `u32` | Limite transactionnelle de données de comptes chargées, entre 1 octet et 64 MiB. | + +**Résultat et effet :** une instruction officielle `SetLoadedAccountsDataSizeLimit` sans compte ni signataire propre. Le runtime accepte une valeur non nulle puis borne la limite effective à 64 MiB ; l’exécuteur applique directement cette borne afin que le plan reflète exactement la limite utile. + +### Variantes non émises + +- `Unused` est un tag réservé et n’est pas une opération client. +- `RequestUnitsDeprecated` reste couvert par `kb_decoder_solana_core` pour l’historique, mais n’appartient plus à `solana-compute-budget-interface ^3.0` et n’est pas construit par l’exécuteur. +- Une transaction ne doit contenir qu’une instruction de chaque variante Compute Budget ; la détection des doublons appartient au futur assembleur de plans multi-opérations ou au runtime. + + +## Address Lookup Table + +Les cinq fonctions publiques de `solana-address-lookup-table-interface` sont couvertes. Les builders reproduisent exactement le program ID, l’ordre des comptes, les flags signer/writable et le payload officiels. Ils ne lisent pas l’état on-chain et ne calculent pas le rent top-up dynamique. + +### `solana_core.address_lookup_table.create` + +```rust +SolanaCoreOperation::AddressLookupTableCreate { + authority, + payer, + recent_slot, +} +``` + +**Résultat et effet :** une instruction officielle `CreateLookupTable` et l’adresse PDA déterministe dérivée de `authority + recent_slot`. `payer` est writable et signataire ; l’autorité est enregistrée dans la table mais n’est pas signataire de cette instruction dans l’interface actuelle. Le builder déclare un coût explicite nul, car le runtime calcule le financement rent-exempt pendant l’exécution. L’orchestrateur doit fournir un slot récent et vérifier le coût simulé contre la politique avant signature. + +### `solana_core.address_lookup_table.extend` + +```rust +SolanaCoreOperation::AddressLookupTableExtend { + lookup_table, + authority, + payer, + new_addresses, +} +``` + +**Résultat et effet :** une instruction officielle `ExtendLookupTable` qui conserve l’ordre des adresses. `authority` signe toujours. `payer`, lorsqu’il est présent, signe et active le couple de comptes payer/System Program nécessaire au rent top-up. La liste doit être non vide et ne peut pas dépasser la capacité maximale officielle de 256 adresses. La capacité restante de la table et le montant exact du top-up exigent une lecture stateful avant simulation. + +### `solana_core.address_lookup_table.freeze` + +```rust +SolanaCoreOperation::AddressLookupTableFreeze { + lookup_table, + authority, +} +``` + +**Résultat et effet :** une instruction officielle `FreezeLookupTable`. L’autorité signe. Le gel supprime définitivement l’autorité de la table ; l’orchestrateur doit donc relire l’état et exiger une confirmation renforcée. Une table gelée ne peut plus être étendue, désactivée ou fermée. + +### `solana_core.address_lookup_table.deactivate` + +```rust +SolanaCoreOperation::AddressLookupTableDeactivate { + lookup_table, + authority, +} +``` + +**Résultat et effet :** une instruction officielle `DeactivateLookupTable`. L’autorité signe. La désactivation démarre un cooldown dépendant de l’historique des slots ; elle ne permet pas une fermeture immédiate. + +### `solana_core.address_lookup_table.close` + +```rust +SolanaCoreOperation::AddressLookupTableClose { + lookup_table, + authority, + recipient, +} +``` + +**Résultat et effet :** une instruction officielle `CloseLookupTable`. L’autorité signe et les lamports du compte fermé sont transférés vers `recipient`. Le builder refuse que la table soit son propre destinataire. L’orchestrateur doit vérifier que la table est désactivée, que son cooldown est terminé et que l’autorité on-chain correspond avant simulation et signature. + +### Frontière stateful et coût + +`requested_spend_lamports` reste à zéro pour les cinq plans ALT : ce champ représente les lamports encodés explicitement dans les instructions, alors que le rent de création/extension est calculé par le programme au runtime. Avant tout envoi réel, la couche d’orchestration doit : + +- charger et décoder le compte ALT lorsqu’il existe ; +- vérifier owner, autorité, statut actif/gelé/désactivé, capacité et cooldown ; +- obtenir un slot récent pour la création ; +- calculer le top-up maximal depuis la taille, le solde et le minimum rent-exempt, le comparer au plafond de dépense, puis confirmer le plan par simulation exacte ; +- refuser la fermeture avant la fin du cooldown. + +Ces contrôles cluster ne sont pas simulés par le builder stateless. + + +## Précompiles de signature + +Les précompiles Ed25519, secp256k1 et secp256r1 sont des instructions natives sans compte applicatif et sans signataire propre. Le fee payer reste le seul signataire transactionnel ajouté par défaut. Une vérification réussie prouve uniquement que le runtime a validé le tuple demandé ; elle ne marque pas la clé vérifiée comme `is_signer` et ne remplace pas les contrôles d’autorisation du programme consommateur. + +Chaque précompile expose deux contrats : + +- une forme `Inline`, destinée à une signature, qui place clé/adresse, signature et message dans la même instruction ; +- une forme `Offsets`, destinée aux lots ou références croisées, qui encode une table officielle et peut ajouter un buffer local après cette table. + +Les tables sont limitées à 255 entrées et la donnée totale doit rester adressable par les offsets `u16`, soit au plus 65 535 octets. Les références locales sont vérifiées avant simulation ; les références vers d’autres instructions restent validées par le runtime sur la transaction finale. + +### `solana_core.ed25519.verify_inline` + +```rust +SolanaCoreOperation::Ed25519VerifyInline { + message, + signature, + public_key, +} +``` + +| Paramètre | Contrat | +|--------------|----------------------------------------------------------------------------------| +| `message` | Octets exacts signés, longueur bornée par la taille adressable de l’instruction. | +| `signature` | Signature Ed25519 de 64 octets. | +| `public_key` | Clé publique Ed25519 de 32 octets. | + +**Résultat et effet :** une instruction officielle contenant une entrée, avec clé, signature puis message. Les trois index utilisent le sentinelle `u16::MAX` pour référencer l’instruction courante. + +### `solana_core.ed25519.verify_offsets` + +```rust +SolanaCoreOperation::Ed25519VerifyOffsets { + offsets, + data, +} +``` + +`offsets` contient des `Ed25519VerificationOffsets`. `data` est ajouté après la table et peut porter les clés, signatures ou messages référencés localement. Les index différents de `u16::MAX` désignent d’autres instructions de la transaction. + +### `solana_core.secp256k1.verify_inline` + +```rust +SolanaCoreOperation::Secp256k1VerifyInline { + message, + signature, + recovery_id, + ethereum_address, +} +``` + +| Paramètre | Contrat | +|--------------------|------------------------------------------------------------| +| `message` | Message brut attendu par le précompile secp256k1. | +| `signature` | Signature compacte de 64 octets. | +| `recovery_id` | Identifiant de récupération compris entre 0 et 3. | +| `ethereum_address` | Adresse Ethereum de 20 octets attendue après récupération. | + +**Résultat et effet :** une instruction conforme au layout officiel de 11 octets par entrée, suivie de l’adresse, de la signature, du recovery ID et du message. Le layout secp256k1 ne possède pas de sentinelle « instruction courante » : la forme inline utilise explicitement l’index transactionnel `0`. Elle doit donc rester la première instruction de la transaction. Aucun helper `bincode` n’est activé ; le wire little-endian est construit directement et comparé aux constantes/structures officielles. Le runtime secp256k1 n’impose pas lui-même la forme low-S ; le programme consommateur doit ajouter ce contrôle lorsque l’unicité canonique des signatures fait partie de son modèle de sécurité. + +### `solana_core.secp256k1.verify_offsets` + +```rust +SolanaCoreOperation::Secp256k1VerifyOffsets { + offsets, + data, +} +``` + +`offsets` contient des `Secp256k1VerificationOffsets` avec des index `u8`. Le builder conserve les références externes. L’index `0` désigne les données de cette instruction selon le contrat de positionnement imposé ; ses plages sont contrôlées localement. + +### `solana_core.secp256r1.verify_inline` + +```rust +SolanaCoreOperation::Secp256r1VerifyInline { + message, + signature, + public_key, +} +``` + +| Paramètre | Contrat | +|--------------|--------------------------------------------------------------------------------| +| `message` | Octets exacts signés. | +| `signature` | Signature compacte `rs` de 64 octets, avec composants non nuls et forme low-S. | +| `public_key` | Clé publique compressée de 33 octets commençant par `0x02` ou `0x03`. | + +**Résultat et effet :** une instruction officielle contenant une entrée et utilisant `u16::MAX` pour les trois références locales. Le builder refuse une signature high-S avant simulation afin de reproduire la contrainte de malléabilité du runtime. + +### `solana_core.secp256r1.verify_offsets` + +```rust +SolanaCoreOperation::Secp256r1VerifyOffsets { + offsets, + data, +} +``` + +`offsets` contient des `Secp256r1VerificationOffsets`. Les références locales utilisent `u16::MAX`; les références externes conservent leur index d’instruction. Dans cette forme avancée, le contenu cryptographique référencé dans une autre instruction est contrôlé par le runtime et par le programme consommateur. + +### Exemple minimal Ed25519 + +```rust +let intent = kb_executor_solana_core::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("verify-ed25519-1"), + fee_payer: payer, + policy, + operation: kb_executor_solana_core::SolanaCoreOperation::Ed25519VerifyInline { + message: b"authorization-v1".to_vec(), + signature: signature_bytes.to_vec(), + public_key: public_key_bytes.to_vec(), + }, +}; +let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &kb_executor_solana_core::SolanaCoreExecutor, + &intent, +)?; +``` + +L’appelant doit ensuite ajouter, lorsqu’il existe, le programme qui consomme cette preuve et vérifie l’instruction via le sysvar d’instructions. La seule présence de la précompile dans une transaction n’accorde aucune autorisation métier. + + +## Config Program + +La crate expose deux opérations Config. Les helpers officiels actuels activent leur sérialisation via une feature `bincode`; le workspace ne l’active pas. Le builder reproduit donc uniquement le wire vérifié : longueur compact-u16 de `ConfigKeys`, couples `Pubkey + bool`, puis données applicatives déjà sérialisées. + +### `solana_core.config.create_account` + +Produit deux instructions ordonnées : + +1. création System du compte, avec `space = max_config_data_space + taille(ConfigKeys(max_keys))` ; +2. initialisation Config avec une liste de clés vide suivie de `initial_data`. + +Paramètres principaux : + +| Champ | Rôle | +|-------------------------|------------------------------------------------------------------| +| `from` | Compte de financement et signataire. | +| `config_account` | Nouveau compte Config et signataire. | +| `lamports` | Montant rent-exempt déjà mesuré par l’orchestrateur. | +| `max_config_data_space` | Réserve maximale pour les données métier sérialisées. | +| `max_keys` | Liste utilisée pour calculer la taille maximale de `ConfigKeys`. | +| `initial_data` | Octets déjà sérialisés de la valeur initiale. | + +Le plan comptabilise `lamports` dans `requested_spend_lamports`. Il ne calcule pas le rent et ne valide pas l’absence préalable du compte. + +### `solana_core.config.store` + +Construit une instruction Config unique. `config_account_is_signer` et les flags `is_signer` de `ConfigKey` déterminent exactement les comptes signataires ajoutés. La liste est ordonnée, dédupliquée et bornée. Au moins une autorité doit signer. + +L’exécuteur ne connaît pas le type applicatif `T`; l’appelant doit fournir `data` dans le format attendu par le programme Config concerné. + +## Feature Program + +### `solana_core.feature.activate` + +Reproduit la séquence officielle : transfert rent-exempt, allocation à `Feature::size_of()`, puis assignation au Feature Program. Le compte de financement et le nouveau compte feature signent. Le montant est fourni par l’orchestrateur après mesure du rent. + +### `solana_core.feature.revoke_pending_activation` + +Produit l’instruction d’un octet `0x00` avec les comptes ordonnés : feature writable/signataire, incinerator writable, System Program readonly. La révocation ne doit être envoyée qu’après lecture de l’état du compte et confirmation qu’une activation est encore en attente. + +## Slashing Program + +### `solana_core.slashing.submit_duplicate_block_proof` + +Produit un plan atomique de trois instructions : + +```text +System transfer vers le report PDA + -> Ed25519SigVerify avec deux tables d’offsets + -> Slashing DuplicateBlockProof (305 octets) +``` + +Le builder : + +- dérive le report PDA depuis `node_pubkey`, `slot` little-endian et le type de violation `1` ; +- exige le montant rent-exempt exact fourni dans `report_lamports` ; +- conserve l’ordre et les offsets officiels vers la troisième instruction ; +- refuse `destination == report_account` ; +- exige `ExecutionBlockhashKind::Latest`, car l’injection d’une instruction durable nonce déplacerait les index attendus ; +- comptabilise le préfinancement dans `requested_spend_lamports`. + +L’orchestrateur reste responsable de créer/remplir le compte de preuve, de vérifier la fenêtre d’un epoch, de mesurer le rent du rapport et de relire les comptes avant envoi. + +### `solana_core.slashing.close_violation_report` + +Construit l’instruction `0x00` avec le report et sa destination writable. Aucun signataire d’instruction n’est requis, mais le runtime vérifie l’owner, la destination stockée et une rétention minimale de trois epochs. Ces contrôles stateful doivent être reproduits avant simulation. + +## ZK ElGamal Proof Program + +### `solana_core.zk_elgamal.verify_inline` + +Accepte l’un des douze `ZkElGamalProofType` et exige exactement la taille POD officielle associée. Sans contexte, aucun compte n’est ajouté. Avec contexte, le plan ajoute le compte writable préalloué puis son autorité readonly. + +### `solana_core.zk_elgamal.verify_from_account` + +Encode le discriminant suivi d’un offset `u32` little-endian. Le compte contenant la preuve est readonly ; les deux comptes de contexte optionnels suivent dans l’ordre officiel. + +### `solana_core.zk_elgamal.close_context_state` + +Encode le discriminant `0`, rend le contexte et la destination writable et exige la signature de l’autorité du contexte. + +La crate ne crée ni n’alloue le compte de contexte. L’orchestrateur doit vérifier son owner, sa taille, son financement et son état avant la vérification ou la fermeture. L’ancien programme ZK Token Proof ne reçoit aucun builder : il est retiré du runtime actif et reste uniquement décodable pour l’historique. + +### Exemple minimal ZK ElGamal + +```rust +let operation = kb_executor_solana_core::SolanaCoreOperation::ZkElGamalVerifyFromAccount { + proof_type: kb_executor_solana_core::ZkElGamalProofType::PubkeyValidity, + proof_account, + proof_offset: 0, + context_state: None, +}; +``` + +## Stake Program + +La crate couvre les **24 helpers clients actuels** de `solana-stake-interface 4.3.1` sans activer sa feature `bincode`. Le payload est produit par un miroir privé `wincode` identique à celui du décodeur, puis les comptes et signataires sont reproduits selon les builders officiels. + +| Groupe | Opérations exposées | Effet du plan | +|----------------------------|---------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------| +| Initialisation | `STAKE_INITIALIZE_OPERATION`, `STAKE_INITIALIZE_CHECKED_OPERATION` | Initialise un compte préalloué ; la forme checked exige la signature du withdrawer. | +| Création | `STAKE_CREATE_ACCOUNT_OPERATION`, `STAKE_CREATE_ACCOUNT_WITH_SEED_OPERATION`, variantes checked | Construit `System create_account[_with_seed]` puis l’initialisation Stake. | +| Création + délégation | `STAKE_CREATE_ACCOUNT_AND_DELEGATE_OPERATION`, variante seedée | Ajoute `DelegateStake` après création et initialisation. | +| Split / merge | `STAKE_SPLIT_OPERATION`, `STAKE_SPLIT_WITH_SEED_OPERATION`, `STAKE_MERGE_OPERATION` | Prépare le compte de destination puis split, ou fusionne deux états compatibles. | +| Autorités | `STAKE_AUTHORIZE_OPERATION`, checked, seed et checked-with-seed | Change le staker ou le withdrawer via `StakeAuthorizationKind`. | +| Délégation / désactivation | `STAKE_DELEGATE_OPERATION`, `STAKE_DEACTIVATE_OPERATION`, `STAKE_DEACTIVATE_DELINQUENT_OPERATION` | Administre la délégation et les transitions d’activation. | +| Retrait / mouvement | `STAKE_WITHDRAW_OPERATION`, `STAKE_MOVE_STAKE_OPERATION`, `STAKE_MOVE_LAMPORTS_OPERATION` | Déplace des lamports ou du stake entre comptes compatibles. | +| Lockup | `STAKE_SET_LOCKUP_OPERATION`, `STAKE_SET_LOCKUP_CHECKED_OPERATION` | Modifie timestamp, epoch et éventuellement custodian. | +| Lecture runtime | `STAKE_GET_MINIMUM_DELEGATION_OPERATION` | Produit l’instruction sans comptes dont le résultat est renvoyé en return data. | + +`StakeAuthorizationKind::{Staker, Withdrawer}` sélectionne le rôle modifié par les quatre opérations d’autorisation. Les plans déclarent tous les signataires avant simulation, conservent l’ordre officiel des instructions composées et comptabilisent conservativement les montants de création, split, retrait ou mouvement dans `requested_spend_lamports`. + +### Frontière stateful Stake + +Le builder ne prétend pas connaître l’état on-chain. Avant envoi, l’orchestrateur doit notamment vérifier : propriétaire et taille du compte, état initialisé/delegated, autorités courantes, lockup et custodian, rent-exemption, compatibilité merge/split/move, activation/deactivation epoch, vote account, minimum delegation et solde réellement retirable. Ces contrôles et les parcours localnet/devnet restent dans `pre.022`. + +`Redelegate` et `redelegate_with_seed` ne sont pas exposés : l’interface officielle les marque dépréciés avec la mention « Redelegate will not be enabled ». Ils restent décodables historiquement mais sont classés `decode-only` dans la matrice d’exécution. + +### Exemple minimal Stake + +```rust +use kb_execution_api::TypedInstructionExecutor; + +let executor = kb_executor_solana_core::SolanaCoreExecutor; +let intent = kb_executor_solana_core::SolanaCoreExecutionIntent { + intent_id: String::from("delegate-stake"), + fee_payer: payer.clone(), + policy, + operation: kb_executor_solana_core::SolanaCoreOperation::StakeDelegate { + stake_account, + stake_authority, + vote_account, + }, +}; +let plan = executor.build_prepared_plan(&intent)?; +``` + +Le plan est seulement construit. La lecture des comptes, la simulation exacte, la signature, l’envoi, la confirmation et le replay post-exécution restent des étapes distinctes. + +## Vote Program + +La crate couvre **24 opérations Vote** : les vingt variantes `VoteInstruction` actuelles de `solana-vote-interface 6.0.3` et quatre plans composés de création. Le payload est sérialisé directement avec le schéma officiel `wincode`; aucune feature ni helper `bincode` n’est activé. Les comptes sysvar utilisent le registre canonique `solana_sdk_ids::sysvar`, conformément à l’interface officielle. + +| Groupe | Opérations exposées | Contrat principal | +|----------------|-----------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------| +| Initialisation | `VOTE_INITIALIZE_ACCOUNT_OPERATION`, `VOTE_INITIALIZE_ACCOUNT_V2_OPERATION` | Legacy avec Rent/Clock; V2 avec identité, clé/preuve BLS 48/96 octets et deux collectors writable. | +| Création | `VOTE_CREATE_ACCOUNT_OPERATION`, variante seedée, et deux variantes V2 | `System create_account[_with_seed]` avec `VoteStateV4::size_of()`, puis initialisation Vote dans l’ordre officiel. | +| Autorités | `VOTE_AUTHORIZE_OPERATION`, checked, seed et checked-with-seed | Autorités voter/withdrawer, nouvelle autorité signer dans les formes checked, BLS borné pour `VoterWithBLS`. | +| Vote simple | `VOTE_SUBMIT_OPERATION`, `VOTE_SWITCH_OPERATION` | Slots strictement croissants, hash de banque, timestamp optionnel, SlotHashes/Clock et autorité signer. | +| État/tour | update, compact update, TowerSync et variantes switch | Lockouts bornés à `MAX_LOCKOUT_HISTORY`, root cohérente, hashes et block ID validés avant sérialisation compacte. | +| Administration | retrait, identité, commission legacy, collector et commission BPS | Comptes/signataires officiels; pourcentage `0..=100`, BPS `0..=10_000`. | +| Récompenses | `VOTE_DEPOSIT_DELEGATOR_REWARDS_OPERATION` | Source writable signer et montant positif. | + +Les créations, retraits et dépôts reportent leurs lamports explicitement transférés dans `requested_spend_lamports`; ils exigent donc un plafond de dépense avant simulation. Les autres opérations reportent zéro. Les plans ne lisent pas le compte Vote : version/state, autorités courantes, ownership, rent, collectors autorisés, fenêtre de commission, validité cryptographique BLS et admissibilité runtime de la tour restent des validations stateful de l’orchestrateur. + +## Loaders v3 et v4 + +La crate couvre **20 plans Loader**. Les onze plans Loader v3 appellent `solana-loader-v3-interface 8.x` avec son schéma `wincode`; les neuf plans Loader v4 reproduisent le wire et les comptes publiés par `solana-loader-v4-interface 3.1`, sans activer ses helpers conditionnés par `bincode`. + +| Famille | Opérations | Contrat | +|----------------------|-------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------| +| Loader v3 buffer | création, écriture, autorité/checked, fermeture | création composée System + InitializeBuffer, écritures bornées à 1 024 octets et signataires officiels | +| Loader v3 programme | deploy-with-max-len, upgrade, autorité/checked, close-any, extend | ordre officiel, comptes Program/ProgramData/buffer, sysvars via `solana_sdk_ids` et extension minimale officielle | +| Loader v4 allocation | create-buffer, set-program-length | création System de taille zéro puis `SetProgramLength`; zéro ferme/réinitialise dans l’opération directe | +| Loader v4 bytecode | write, copy | tag `u32 LE`, offset, longueur de vecteur `u64 LE`, plages bornées et source read-only pour copy | +| Loader v4 lifecycle | deploy, deploy-from-source, retract, transfer-authority, finalize | comptes et flags signer/writable identiques aux helpers officiels; le programme next-version peut être identique au programme finalisé | + +Les créations Loader déclarent uniquement les lamports explicitement transférés par l’instruction System dans `requested_spend_lamports`. Les rent top-ups calculés à partir de l’état réel ne sont pas devinés par le builder. Avant simulation, l’orchestrateur doit vérifier owner, état, autorité, taille/capacité, rent, ProgramData, relations de comptes, source de déploiement et admissibilité de fermeture/extension. + +BPF Loader v1/v2 restent historiques `decode-only` malgré l’existence d’anciens helpers dépréciés. Native Loader appartient au chemin de mise à niveau du logiciel validator et ne possède pas d’instruction client autonome. Ces surfaces sont classées précisément par `ExecutionCapability::Unsupported` et aucune instruction fictive n’est construite. + +## Contrat avec l’assemblage durable nonce + +Les builders de création et lifecycle nonce restent indépendants de la consommation d’un durable nonce. Pour un plan métier configuré avec `ExecutionBlockhashKind::DurableNonce`, `finalize_prepared_plan` impose désormais : + +- un compte nonce ; +- une autorité nonce ; +- aucune limite d’âge de recent blockhash ; +- l’autorité nonce dans les signataires requis, avec le rôle `durable_nonce_authority`. + +`kb_execution_solana` prend ensuite la responsabilité stateful : lecture complète et bornée du compte, validation System Program/`Current`/`Initialized`, concordance compte-autorité, injection de `AdvanceNonceAccount` en première instruction, compilation avec la valeur nonce et liaison exacte de la simulation avant signature. Le plan produit ici ne lit toujours aucun RPC et ne contient aucun secret. + +## Erreurs et garde-fous de construction + +Les erreurs utilisent `kb_core::Error` avec un code stable. Les contrôles couvrent notamment : + +- program ID et code d’opération incohérents ; +- clé publique invalide ; +- lamports nuls lorsque l’opération exige un montant positif ; +- comptes source/destination identiques ; +- taille d’allocation nulle ou supérieure à la limite officielle selon l’opération ; +- seed invalide ou adresse dérivée incohérente ; +- payer absent avec un montant additionnel non nul pour `create_account_allow_prefund` ; +- liste `transfer_many` vide, montant nul ou dépassement de la somme ; +- gestion nonce demandée avec une politique durable nonce au lieu du recent-blockhash de lifecycle ; +- politique durable nonce sans compte, sans autorité ou avec une limite d’âge de recent blockhash ; +- heap frame hors intervalle ou non alignée sur 1 024 octets ; +- limite de données de comptes chargées nulle ou supérieure à 64 MiB ; +- signature, clé publique, adresse Ethereum ou recovery ID de taille/valeur invalide ; +- signature secp256r1 avec composant nul, forme high-S ou préfixe de clé compressée invalide ; +- table d’offsets vide, supérieure à 255 entrées, trop grande ou plage locale hors bornes ; +- dépassement numérique lors du calcul du coût ; +- extension ALT vide ou supérieure à 256 adresses ; +- table ALT confondue avec son autorité, son payer ou son destinataire de fermeture ; +- Config sans autorité, clé dupliquée, données trop grandes ou espace incohérent ; +- Feature sans rent positif ou confondue avec son compte de financement ; +- report Slashing mal dérivé, montant nul, destination égale au PDA, preuve/signature de mauvaise taille ou politique durable nonce ; +- preuve ZK ElGamal de taille incorrecte, comptes preuve/contexte confondus ou fermeture sans autorité distincte ; +- commission Vote hors plage, BLS de taille incorrecte, seed/adresse dérivée incohérente, hash invalide, slots ou lockouts non strictement croissants, root au-dessus de la première lockout ou montant nul ; +- création Loader sans lamports/taille, écriture vide ou supérieure à 1 024 octets, offset/plage en dépassement, comptes source/destination confondus, autorité v4 inchangée ou extension v3 sous le minimum officiel ; +- opération déclarée mais pas encore implémentée. + +`ExecutionCapability::Unsupported { reason_code, reason }` représente uniquement une dette temporaire ou une instruction non constructible par un client. L’objectif de clôture est de couvrir toutes les instructions natives officiellement appelables. + +## Builders officiels et tests + +Les builders proviennent de : + +- `solana-system-interface` avec la feature `wincode` ; +- `solana-compute-budget-interface` ; +- `solana-address-lookup-table-interface` avec les helpers `create`, `extend`, `freeze`, `deactivate` et `close` ; +- `solana-ed25519-program` et `solana-secp256r1-program` pour les builders inline et structures d’offsets officiels ; +- `solana-secp256k1-program` sans feature `bincode`, pour les constantes et la structure wire officielle reproduite explicitement en little-endian ; +- `solana-config-interface` pour le contrat `ConfigKeys`, reproduit sans activer son helper `bincode` ; +- `solana-feature-gate-interface` pour `Feature::size_of()` et les séquences officielles reproduites sans helper `bincode` ; +- `solana-zk-elgamal-proof-interface` pour les discriminants, tailles POD, comptes de contexte et fermeture ; +- `solana-vote-interface` pour les vingt variantes `VoteInstruction`, les types d’état Vote/BLS et le schéma officiel `wincode`, avec `solana-sdk-ids` pour les sysvar IDs ; +- `solana-loader-v3-interface` pour les onze helpers Loader v3 et leur sérialisation `wincode` ; +- `solana-loader-v4-interface` comme contrat normatif des sept discriminants et comptes, reproduits localement sans activer sa feature `bincode` ; +- SIMD-0204 et le contrat publié du Slashing Program pour le payload de 305 octets, le PDA et l’introspection Ed25519. + +Les tests comparent : + +- le program ID ; +- le code d’opération ; +- l’ordre exact des comptes ; +- les flags signer/writable ; +- le payload exact ; +- les signataires requis et leur déduplication ; +- les coûts demandés ; +- la validation des adresses dérivées ; +- les erreurs de valeurs ou contrats invalides ; +- les tables d’offsets, références externes, sentinelles locales et contraintes de position des précompiles ; +- le wire Config compact-u16, le calcul d’espace et les signataires exacts ; +- les séquences System officielles d’activation et de révocation Feature ; +- l’ordre atomique, le PDA, les offsets Ed25519 et le payload Slashing de 305 octets ; +- les discriminants, tailles POD, modes inline/account et fermeture des contextes ZK ElGamal ; +- les vingt variantes wire Vote, les quatre créations composées, comptes/signataires, BLS, commissions, tours et plafonds de lamports ; +- les onze helpers Loader v3 contre les builders officiels, puis les neuf plans Loader v4 contre les tags, champs, comptes, signataires, ordre composé et plafonds de lamports publiés. + +## État final `0.4.2` + +System, Compute Budget, Address Lookup Table, les trois précompiles de signature, Config, Feature, Slashing, ZK ElGamal, Stake, Vote, Loader v3 et Loader v4 sont couverts pour leurs opérations client retenues. L’ancien ZK Token Proof, Stake Redelegate et BPF Loader v1/v2 restent explicitement `decode-only`; Native Loader est classé sans instruction client. + +La bibliothèque est complète au niveau des plans et du wire connus, mais elle ne donne aucune autorisation implicite d’envoi. Les opérations administratives ou dangereuses restent hors de `kb_app_demo`, derrière les politiques et préflights stateful, et sans activation Mainnet. Une validation cluster spécifique reste obligatoire avant toute future exposition mutable. + +## Tracing + +Les événements opérationnels utilisent le target canonique `kb_executor_solana_core` défini dans `src/constants.rs`. + + +## Contrat avec le préflight stateful `pre.022` + +`ZkElGamalProofType::context_state_size()` expose la taille exacte de `ProofContextState` pour chaque contexte officiel. Cette donnée permet à `kb_pipeline` de contrôler l’allocation et le rent sans dupliquer une table numérique fragile. L’exécuteur reste sans I/O : ALT, Config, Feature, Slashing et ZK ElGamal sont inspectés par `inspect_solana_core_stateful_readiness` avant simulation. + +La surface complète est inventoriée dans `docs/NATIVE_SOLANA_EXECUTION_MATRIX.json`. Le validateur associé impose la correspondance exacte des 109 codes `solana_core.*` et les classifications sans builder client. +## Matrice compilée de complétude — `0.4.2-pre.023` + +`SOLANA_CORE_OPERATION_CODES` expose le registre compilé des 109 opérations appelables. Un test Rust charge `docs/NATIVE_SOLANA_EXECUTION_MATRIX.json` et impose : + +- 18 surfaces exactement ; +- 14 surfaces appelables et 4 surfaces non invocables ou historiques ; +- égalité exacte entre la matrice et les 109 codes compilés ; +- Program IDs enregistrés, surfaces et opérations uniques ; +- justification des surfaces non invocables ; +- contrats de construction, politique, tests, cluster et exposition UI ; +- distribution exacte des préflights `27 none / 14 required / 68 future`. + +Cette validation remplace le script Python fondé sur des expressions régulières. La matrice prouve l’exhaustivité théorique des builders connus ; les entrées `future` conservent explicitement la dette de validation stateful Localnet/Devnet. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/address_lookup_table.rs b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/address_lookup_table.rs new file mode 100644 index 0000000..ef0a5a9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/address_lookup_table.rs @@ -0,0 +1,579 @@ +// file: kb_executor_solana_core/src/address_lookup_table.rs +// version: 4 + +//! Official Address Lookup Table instruction builders. + +pub(crate) fn build_prepared_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, +) -> kb_core::Result { + return match &intent.operation { + crate::SolanaCoreOperation::AddressLookupTableCreate { authority, payer, recent_slot } => { + build_create_plan(intent, fee_payer, authority, payer, *recent_slot) + }, + crate::SolanaCoreOperation::AddressLookupTableExtend { + lookup_table, + authority, + payer, + new_addresses, + } => build_extend_plan( + intent, + fee_payer, + lookup_table, + authority, + payer.as_ref(), + new_addresses.as_slice(), + ), + crate::SolanaCoreOperation::AddressLookupTableFreeze { lookup_table, authority } => { + build_freeze_plan(intent, fee_payer, lookup_table, authority) + }, + crate::SolanaCoreOperation::AddressLookupTableDeactivate { lookup_table, authority } => { + build_deactivate_plan(intent, fee_payer, lookup_table, authority) + }, + crate::SolanaCoreOperation::AddressLookupTableClose { + lookup_table, + authority, + recipient, + } => build_close_plan(intent, fee_payer, lookup_table, authority, recipient), + _ => std::result::Result::Err(kb_core::Error::new( + "execution_address_lookup_table_operation_mismatch", + "Address Lookup Table builder received a non-ALT operation", + )), + }; +} + +fn build_create_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + authority: &kb_model::Pubkey, + payer: &kb_model::Pubkey, + recent_slot: u64, +) -> kb_core::Result { + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let payer_address = match crate::parse_pubkey(payer, "payer") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let (instruction, lookup_table_address) = + solana_address_lookup_table_interface::instruction::create_lookup_table( + authority_address, + payer_address, + recent_slot, + ); + if lookup_table_address == authority_address || lookup_table_address == payer_address { + return std::result::Result::Err(kb_core::Error::new( + "execution_address_lookup_table_derived_address_conflict", + "derived lookup table address must differ from its authority and payer", + )); + } + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::ADDRESS_LOOKUP_TABLE_CREATE_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + vec![(payer.clone(), std::string::String::from("lookup_table_payer"))], + ); +} + +fn build_extend_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + lookup_table: &kb_model::Pubkey, + authority: &kb_model::Pubkey, + payer: std::option::Option<&kb_model::Pubkey>, + new_addresses: &[kb_model::Pubkey], +) -> kb_core::Result { + if new_addresses.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_address_lookup_table_extend_empty", + "Address Lookup Table extension requires at least one address", + )); + } + if new_addresses.len() + > solana_address_lookup_table_interface::state::LOOKUP_TABLE_MAX_ADDRESSES + { + return std::result::Result::Err(kb_core::Error::new( + "execution_address_lookup_table_extend_too_many_addresses", + format!( + "Address Lookup Table extension cannot contain more than {} addresses", + solana_address_lookup_table_interface::state::LOOKUP_TABLE_MAX_ADDRESSES + ), + )); + } + let lookup_table_address = match crate::parse_pubkey(lookup_table, "lookup_table") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + match crate::validate_distinct_pubkeys( + lookup_table, + authority, + "execution_address_lookup_table_authority_is_table", + "lookup table account and authority must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let payer_address = match payer { + std::option::Option::Some(value) => { + match crate::validate_distinct_pubkeys( + lookup_table, + value, + "execution_address_lookup_table_payer_is_table", + "lookup table account and rent payer must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::parse_pubkey(value, "payer") { + std::result::Result::Ok(pubkey) => std::option::Option::Some(pubkey), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + }, + std::option::Option::None => std::option::Option::None, + }; + let mut parsed_addresses = std::vec::Vec::with_capacity(new_addresses.len()); + for (index, address) in new_addresses.iter().enumerate() { + let parsed = match crate::parse_pubkey(address, format!("new_addresses[{index}]").as_str()) + { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + parsed_addresses.push(parsed); + } + let instruction = solana_address_lookup_table_interface::instruction::extend_lookup_table( + lookup_table_address, + authority_address, + payer_address, + parsed_addresses, + ); + let mut signer_roles = + vec![(authority.clone(), std::string::String::from("lookup_table_authority"))]; + if let std::option::Option::Some(payer_pubkey) = payer { + signer_roles.push((payer_pubkey.clone(), std::string::String::from("lookup_table_payer"))); + } + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::ADDRESS_LOOKUP_TABLE_EXTEND_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + signer_roles, + ); +} + +fn build_freeze_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + lookup_table: &kb_model::Pubkey, + authority: &kb_model::Pubkey, +) -> kb_core::Result { + let (lookup_table_address, authority_address) = + match parse_authority_pair(lookup_table, authority) { + std::result::Result::Ok(pair) => pair, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = solana_address_lookup_table_interface::instruction::freeze_lookup_table( + lookup_table_address, + authority_address, + ); + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::ADDRESS_LOOKUP_TABLE_FREEZE_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + vec![(authority.clone(), std::string::String::from("lookup_table_authority"))], + ); +} + +fn build_deactivate_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + lookup_table: &kb_model::Pubkey, + authority: &kb_model::Pubkey, +) -> kb_core::Result { + let (lookup_table_address, authority_address) = + match parse_authority_pair(lookup_table, authority) { + std::result::Result::Ok(pair) => pair, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = solana_address_lookup_table_interface::instruction::deactivate_lookup_table( + lookup_table_address, + authority_address, + ); + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::ADDRESS_LOOKUP_TABLE_DEACTIVATE_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + vec![(authority.clone(), std::string::String::from("lookup_table_authority"))], + ); +} + +fn build_close_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + lookup_table: &kb_model::Pubkey, + authority: &kb_model::Pubkey, + recipient: &kb_model::Pubkey, +) -> kb_core::Result { + let (lookup_table_address, authority_address) = + match parse_authority_pair(lookup_table, authority) { + std::result::Result::Ok(pair) => pair, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + match crate::validate_distinct_pubkeys( + lookup_table, + recipient, + "execution_address_lookup_table_close_recipient_is_table", + "lookup table account and close recipient must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let recipient_address = match crate::parse_pubkey(recipient, "recipient") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = solana_address_lookup_table_interface::instruction::close_lookup_table( + lookup_table_address, + authority_address, + recipient_address, + ); + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::ADDRESS_LOOKUP_TABLE_CLOSE_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + vec![(authority.clone(), std::string::String::from("lookup_table_authority"))], + ); +} + +fn parse_authority_pair( + lookup_table: &kb_model::Pubkey, + authority: &kb_model::Pubkey, +) -> kb_core::Result<(solana_pubkey::Pubkey, solana_pubkey::Pubkey)> { + match crate::validate_distinct_pubkeys( + lookup_table, + authority, + "execution_address_lookup_table_authority_is_table", + "lookup table account and authority must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let lookup_table_address = match crate::parse_pubkey(lookup_table, "lookup_table") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok((lookup_table_address, authority_address)); +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; // rust-rules: trait-import + + fn pubkey(value: &str) -> kb_model::Pubkey { + return kb_model::Pubkey(std::string::String::from(value)); + } + + fn parsed(value: &str) -> solana_pubkey::Pubkey { + return solana_pubkey::Pubkey::from_str(value) + .unwrap_or_else(|error| panic!("invalid fixture: {error}")); + } + + fn policy(signers: &[&str]) -> kb_execution_api::ExecutionPolicy { + return kb_execution_api::ExecutionPolicy { + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(10_000_000), + max_fee_lamports: std::option::Option::Some(10_000), + max_compute_unit_price_micro_lamports: std::option::Option::Some(100), + }, + authorized_signers: signers.iter().map(|value| return pubkey(value)).collect(), + ..kb_execution_api::ExecutionPolicy::default() + }; + } + + fn assert_matches_official( + plan: &kb_execution_api::PreparedExecutionPlan, + expected: &solana_instruction::Instruction, + ) { + assert_eq!(plan.instructions.len(), 1); + let actual = &plan.instructions[0]; + assert_eq!(actual.program_id.0, expected.program_id.to_string()); + assert_eq!(actual.data, expected.data); + assert_eq!(actual.accounts.len(), expected.accounts.len()); + for (actual_account, expected_account) in + actual.accounts.iter().zip(expected.accounts.iter()) + { + assert_eq!(actual_account.pubkey.0, expected_account.pubkey.to_string()); + assert_eq!(actual_account.is_signer, expected_account.is_signer); + assert_eq!(actual_account.is_writable, expected_account.is_writable); + } + } + + #[test] + fn create_lookup_table_matches_official_builder_and_signers() { + let authority = kb_program_ids::VOTE_PROGRAM_ID; + let payer = kb_program_ids::STAKE_PROGRAM_ID; + let recent_slot = 42; + let intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("alt-create-1"), + fee_payer: pubkey(payer), + policy: policy(&[payer]), + operation: crate::SolanaCoreOperation::AddressLookupTableCreate { + authority: pubkey(authority), + payer: pubkey(payer), + recent_slot, + }, + }; + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let (expected, expected_address) = + solana_address_lookup_table_interface::instruction::create_lookup_table( + parsed(authority), + parsed(payer), + recent_slot, + ); + assert_matches_official(&plan, &expected); + assert_eq!(plan.operation_code, crate::ADDRESS_LOOKUP_TABLE_CREATE_OPERATION); + assert_eq!(plan.instructions[0].accounts[0].pubkey.0, expected_address.to_string()); + assert_eq!(plan.required_signers.len(), 1); + assert_eq!(plan.required_signers[0].pubkey.0, payer); + assert!(!plan.instructions[0].accounts[1].is_signer); + assert_eq!(plan.requested_spend_lamports, 0); + } + + #[test] + fn extend_lookup_table_matches_official_builder_with_optional_payer() { + let authority = parsed(kb_program_ids::STAKE_PROGRAM_ID); + let payer = parsed(kb_program_ids::VOTE_PROGRAM_ID); + let (lookup_table, _bump) = + solana_address_lookup_table_interface::instruction::derive_lookup_table_address( + &authority, 100, + ); + let addresses = vec![ + parsed(kb_program_ids::SYSTEM_PROGRAM_ID), + parsed(kb_program_ids::CONFIG_PROGRAM_ID), + ]; + let authority_text = authority.to_string(); + let payer_text = payer.to_string(); + let lookup_table_text = lookup_table.to_string(); + let new_addresses = addresses + .iter() + .map(|address| { + return pubkey(address.to_string().as_str()); + }) + .collect::>(); + let funded_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("alt-extend-funded"), + fee_payer: pubkey(payer_text.as_str()), + policy: policy(&[payer_text.as_str(), authority_text.as_str()]), + operation: crate::SolanaCoreOperation::AddressLookupTableExtend { + lookup_table: pubkey(lookup_table_text.as_str()), + authority: pubkey(authority_text.as_str()), + payer: std::option::Option::Some(pubkey(payer_text.as_str())), + new_addresses: new_addresses.clone(), + }, + }; + let funded_plan = crate::builder_build_prepared_plan(&funded_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let funded_expected = + solana_address_lookup_table_interface::instruction::extend_lookup_table( + lookup_table, + authority, + std::option::Option::Some(payer), + addresses.clone(), + ); + assert_matches_official(&funded_plan, &funded_expected); + assert_eq!(funded_plan.required_signers.len(), 2); + assert_eq!(funded_plan.operation_code, crate::ADDRESS_LOOKUP_TABLE_EXTEND_OPERATION); + + let unfunded_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("alt-extend-prefunded"), + fee_payer: pubkey(authority_text.as_str()), + policy: policy(&[authority_text.as_str()]), + operation: crate::SolanaCoreOperation::AddressLookupTableExtend { + lookup_table: pubkey(lookup_table_text.as_str()), + authority: pubkey(authority_text.as_str()), + payer: std::option::Option::None, + new_addresses, + }, + }; + let unfunded_plan = crate::builder_build_prepared_plan(&unfunded_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let unfunded_expected = + solana_address_lookup_table_interface::instruction::extend_lookup_table( + lookup_table, + authority, + std::option::Option::None, + addresses, + ); + assert_matches_official(&unfunded_plan, &unfunded_expected); + assert_eq!(unfunded_plan.required_signers.len(), 1); + } + + #[test] + fn lookup_table_authority_lifecycle_matches_official_builders() { + let authority = parsed(kb_program_ids::VOTE_PROGRAM_ID); + let recipient = parsed(kb_program_ids::CONFIG_PROGRAM_ID); + let (lookup_table, _bump) = + solana_address_lookup_table_interface::instruction::derive_lookup_table_address( + &authority, 200, + ); + let authority_text = authority.to_string(); + let recipient_text = recipient.to_string(); + let lookup_table_text = lookup_table.to_string(); + let variants = [ + ( + crate::SolanaCoreOperation::AddressLookupTableFreeze { + lookup_table: pubkey(lookup_table_text.as_str()), + authority: pubkey(authority_text.as_str()), + }, + crate::ADDRESS_LOOKUP_TABLE_FREEZE_OPERATION, + solana_address_lookup_table_interface::instruction::freeze_lookup_table( + lookup_table, + authority, + ), + ), + ( + crate::SolanaCoreOperation::AddressLookupTableDeactivate { + lookup_table: pubkey(lookup_table_text.as_str()), + authority: pubkey(authority_text.as_str()), + }, + crate::ADDRESS_LOOKUP_TABLE_DEACTIVATE_OPERATION, + solana_address_lookup_table_interface::instruction::deactivate_lookup_table( + lookup_table, + authority, + ), + ), + ( + crate::SolanaCoreOperation::AddressLookupTableClose { + lookup_table: pubkey(lookup_table_text.as_str()), + authority: pubkey(authority_text.as_str()), + recipient: pubkey(recipient_text.as_str()), + }, + crate::ADDRESS_LOOKUP_TABLE_CLOSE_OPERATION, + solana_address_lookup_table_interface::instruction::close_lookup_table( + lookup_table, + authority, + recipient, + ), + ), + ]; + for (operation, operation_code, expected) in variants { + let intent = crate::SolanaCoreExecutionIntent { + intent_id: format!("alt-{operation_code}"), + fee_payer: pubkey(authority_text.as_str()), + policy: policy(&[authority_text.as_str()]), + operation, + }; + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_matches_official(&plan, &expected); + assert_eq!(plan.operation_code, operation_code); + assert_eq!(plan.required_signers.len(), 1); + } + } + + #[test] + fn lookup_table_invalid_contracts_are_rejected() { + let authority = parsed(kb_program_ids::VOTE_PROGRAM_ID); + let (lookup_table, _bump) = + solana_address_lookup_table_interface::instruction::derive_lookup_table_address( + &authority, 300, + ); + let authority_text = authority.to_string(); + let lookup_table_text = lookup_table.to_string(); + let empty = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("alt-empty"), + fee_payer: pubkey(authority_text.as_str()), + policy: policy(&[authority_text.as_str()]), + operation: crate::SolanaCoreOperation::AddressLookupTableExtend { + lookup_table: pubkey(lookup_table_text.as_str()), + authority: pubkey(authority_text.as_str()), + payer: std::option::Option::None, + new_addresses: std::vec::Vec::new(), + }, + }; + let empty_error = + crate::builder_build_prepared_plan(&empty).expect_err("empty extension must fail"); + assert_eq!(empty_error.code(), "execution_address_lookup_table_extend_empty"); + + let too_many_addresses = (0 + ..=solana_address_lookup_table_interface::state::LOOKUP_TABLE_MAX_ADDRESSES) + .map(|_| { + return pubkey(kb_program_ids::SYSTEM_PROGRAM_ID); + }) + .collect::>(); + let too_many = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("alt-too-many"), + fee_payer: pubkey(authority_text.as_str()), + policy: policy(&[authority_text.as_str()]), + operation: crate::SolanaCoreOperation::AddressLookupTableExtend { + lookup_table: pubkey(lookup_table_text.as_str()), + authority: pubkey(authority_text.as_str()), + payer: std::option::Option::None, + new_addresses: too_many_addresses, + }, + }; + let too_many_error = crate::builder_build_prepared_plan(&too_many) + .expect_err("oversized extension must fail"); + assert_eq!( + too_many_error.code(), + "execution_address_lookup_table_extend_too_many_addresses" + ); + + let same_authority = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("alt-same-authority"), + fee_payer: pubkey(lookup_table_text.as_str()), + policy: policy(&[lookup_table_text.as_str()]), + operation: crate::SolanaCoreOperation::AddressLookupTableFreeze { + lookup_table: pubkey(lookup_table_text.as_str()), + authority: pubkey(lookup_table_text.as_str()), + }, + }; + let same_authority_error = crate::builder_build_prepared_plan(&same_authority) + .expect_err("table cannot be its own authority signer"); + assert_eq!( + same_authority_error.code(), + "execution_address_lookup_table_authority_is_table" + ); + + let same_recipient = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("alt-same-recipient"), + fee_payer: pubkey(authority_text.as_str()), + policy: policy(&[authority_text.as_str()]), + operation: crate::SolanaCoreOperation::AddressLookupTableClose { + lookup_table: pubkey(lookup_table_text.as_str()), + authority: pubkey(authority_text.as_str()), + recipient: pubkey(lookup_table_text.as_str()), + }, + }; + let same_recipient_error = crate::builder_build_prepared_plan(&same_recipient) + .expect_err("table cannot receive its own closed lamports"); + assert_eq!( + same_recipient_error.code(), + "execution_address_lookup_table_close_recipient_is_table" + ); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/builder.rs b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/builder.rs new file mode 100644 index 0000000..5ecec82 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/builder.rs @@ -0,0 +1,2161 @@ +// file: kb_executor_solana_core/src/builder.rs +// version: 14 + +//! Official Solana core instruction builders and shared plan finalization. + +use std::str::FromStr; // rust-rules: trait-import + +/// Conservative maximum Compute Budget unit limit accepted by this executor. +const MAX_COMPUTE_UNIT_LIMIT: u32 = 1_400_000; +/// Minimum heap-frame size accepted by the runtime. +const MIN_HEAP_FRAME_BYTES: u32 = 32 * 1_024; +/// Maximum heap-frame size accepted by the runtime. +const MAX_HEAP_FRAME_BYTES: u32 = 256 * 1_024; +/// Heap-frame alignment required by the runtime. +const HEAP_FRAME_BYTES_GRANULARITY: u32 = 1_024; +/// Conservative maximum loaded-account data-size limit accepted by this executor. +const MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES: u32 = 64 * 1_024 * 1_024; + +pub(crate) fn build_prepared_plan( + intent: &crate::SolanaCoreExecutionIntent, +) -> kb_core::Result { + if intent.intent_id.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_intent_id_empty", + "Solana core execution intent id must not be empty", + )); + } + let fee_payer = match crate::parse_pubkey(&intent.fee_payer, "fee_payer") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let operation_code = intent.operation.operation_code(); + if intent.operation.program_id() == kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID { + return crate::loader_v3_build_prepared_plan(intent, &fee_payer); + } + if intent.operation.program_id() == kb_program_ids::LOADER_V4_PROGRAM_ID { + return crate::loader_v4_build_prepared_plan(intent, &fee_payer); + } + if intent.operation.program_id() == kb_program_ids::STAKE_PROGRAM_ID { + return crate::stake_build_prepared_plan(intent, &fee_payer); + } + if intent.operation.program_id() == kb_program_ids::VOTE_PROGRAM_ID { + return crate::vote_build_prepared_plan(intent, &fee_payer); + } + match &intent.operation { + crate::SolanaCoreOperation::SystemTransferMany { from, recipients } => { + return build_transfer_many_plan(intent, &fee_payer, from, recipients.as_slice()); + }, + crate::SolanaCoreOperation::SystemCreateNonceAccount { + from, + nonce_account, + authority, + lamports, + } => { + return build_create_nonce_account_plan( + intent, + &fee_payer, + from, + nonce_account, + authority, + *lamports, + ); + }, + crate::SolanaCoreOperation::SystemCreateNonceAccountWithSeed { .. } => { + return build_create_nonce_account_with_seed_plan( + intent, + &fee_payer, + &intent.operation, + ); + }, + crate::SolanaCoreOperation::SystemInitializeNonceAccount { nonce_account, authority } => { + return build_initialize_nonce_account_plan( + intent, + &fee_payer, + nonce_account, + authority, + ); + }, + crate::SolanaCoreOperation::SystemAdvanceNonceAccount { nonce_account, authority } => { + return build_advance_nonce_account_plan(intent, &fee_payer, nonce_account, authority); + }, + crate::SolanaCoreOperation::SystemAuthorizeNonceAccount { + nonce_account, + authority, + new_authority, + } => { + return build_authorize_nonce_account_plan( + intent, + &fee_payer, + nonce_account, + authority, + new_authority, + ); + }, + crate::SolanaCoreOperation::SystemWithdrawNonceAccount { + nonce_account, + authority, + to, + lamports, + } => { + return build_withdraw_nonce_account_plan( + intent, + &fee_payer, + nonce_account, + authority, + to, + *lamports, + ); + }, + crate::SolanaCoreOperation::SystemUpgradeNonceAccount { nonce_account } => { + return build_upgrade_nonce_account_plan(intent, &fee_payer, nonce_account); + }, + crate::SolanaCoreOperation::AddressLookupTableCreate { .. } + | crate::SolanaCoreOperation::AddressLookupTableExtend { .. } + | crate::SolanaCoreOperation::AddressLookupTableFreeze { .. } + | crate::SolanaCoreOperation::AddressLookupTableDeactivate { .. } + | crate::SolanaCoreOperation::AddressLookupTableClose { .. } => { + return crate::address_lookup_table_build_prepared_plan(intent, &fee_payer); + }, + crate::SolanaCoreOperation::Ed25519VerifyInline { .. } + | crate::SolanaCoreOperation::Ed25519VerifyOffsets { .. } + | crate::SolanaCoreOperation::Secp256k1VerifyInline { .. } + | crate::SolanaCoreOperation::Secp256k1VerifyOffsets { .. } + | crate::SolanaCoreOperation::Secp256r1VerifyInline { .. } + | crate::SolanaCoreOperation::Secp256r1VerifyOffsets { .. } => { + return crate::precompiles_build_prepared_plan(intent, &fee_payer); + }, + crate::SolanaCoreOperation::ConfigCreateAccount { .. } + | crate::SolanaCoreOperation::ConfigStore { .. } + | crate::SolanaCoreOperation::FeatureActivate { .. } + | crate::SolanaCoreOperation::FeatureRevokePendingActivation { .. } => { + return crate::native_admin_build_prepared_plan(intent, &fee_payer); + }, + crate::SolanaCoreOperation::SlashingCloseViolationReport { .. } + | crate::SolanaCoreOperation::SlashingSubmitDuplicateBlockProof { .. } => { + return crate::slashing_build_prepared_plan(intent, &fee_payer); + }, + crate::SolanaCoreOperation::ZkElGamalVerifyInline { .. } + | crate::SolanaCoreOperation::ZkElGamalVerifyFromAccount { .. } + | crate::SolanaCoreOperation::ZkElGamalCloseContextState { .. } => { + return crate::zk_elgamal_build_prepared_plan(intent, &fee_payer); + }, + _ => {}, + } + let (instruction, requested_spend_lamports, requested_compute_unit_price, signer_roles) = + match &intent.operation { + crate::SolanaCoreOperation::SystemTransfer { from, to, lamports } => { + match crate::validate_positive_u64( + *lamports, + "execution_transfer_lamports_zero", + "System transfer requires a positive lamport amount", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + match crate::validate_distinct_pubkeys( + from, + to, + "execution_transfer_same_account", + "System transfer source and destination must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + let from_address = match crate::parse_pubkey(from, "from") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let to_address = match crate::parse_pubkey(to, "to") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + ( + solana_system_interface::instruction::transfer( + &from_address, + &to_address, + *lamports, + ), + *lamports, + std::option::Option::None, + vec![(from.clone(), std::string::String::from("funding_account"))], + ) + }, + crate::SolanaCoreOperation::SystemTransferWithSeed { + from, + from_base, + from_seed, + from_owner, + to, + lamports, + } => { + match crate::validate_positive_u64( + *lamports, + "execution_transfer_lamports_zero", + "System seeded transfer requires a positive lamport amount", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + match crate::validate_distinct_pubkeys( + from, + to, + "execution_transfer_same_account", + "System seeded transfer source and destination must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + let from_address = match crate::parse_pubkey(from, "from") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let from_base_address = match crate::parse_pubkey(from_base, "from_base") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + }; + let from_owner_address = match crate::parse_pubkey(from_owner, "from_owner") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + }; + let to_address = match crate::parse_pubkey(to, "to") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + match crate::validate_seeded_address( + &from_address, + &from_base_address, + from_seed, + &from_owner_address, + "from", + "execution_seed_invalid", + "execution_seeded_address_mismatch", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + ( + solana_system_interface::instruction::transfer_with_seed( + &from_address, + &from_base_address, + from_seed.clone(), + &from_owner_address, + &to_address, + *lamports, + ), + *lamports, + std::option::Option::None, + vec![(from_base.clone(), std::string::String::from("seed_base_account"))], + ) + }, + crate::SolanaCoreOperation::SystemCreateAccount { + from, + new_account, + lamports, + space, + owner, + } => { + match crate::validate_positive_u64( + *lamports, + "execution_create_account_lamports_zero", + "System account creation requires a positive lamport amount", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + match validate_create_space(*space) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + match crate::validate_distinct_pubkeys( + from, + new_account, + "execution_create_account_same_account", + "funding and new account public keys must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + let from_address = match crate::parse_pubkey(from, "from") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let new_account_address = match crate::parse_pubkey(new_account, "new_account") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + }; + let owner_address = match crate::parse_pubkey(owner, "owner") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + ( + solana_system_interface::instruction::create_account( + &from_address, + &new_account_address, + *lamports, + *space, + &owner_address, + ), + *lamports, + std::option::Option::None, + vec![ + (from.clone(), std::string::String::from("funding_account")), + (new_account.clone(), std::string::String::from("new_account")), + ], + ) + }, + crate::SolanaCoreOperation::SystemCreateAccountWithSeed { + from, + new_account, + base, + seed, + lamports, + space, + owner, + } => { + match crate::validate_positive_u64( + *lamports, + "execution_create_account_lamports_zero", + "System seeded account creation requires a positive lamport amount", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + match validate_create_space(*space) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + match crate::validate_distinct_pubkeys( + from, + new_account, + "execution_create_account_same_account", + "funding and new seeded account public keys must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + let from_address = match crate::parse_pubkey(from, "from") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let new_account_address = match crate::parse_pubkey(new_account, "new_account") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + }; + let base_address = match crate::parse_pubkey(base, "base") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let owner_address = match crate::parse_pubkey(owner, "owner") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + match crate::validate_seeded_address( + &new_account_address, + &base_address, + seed, + &owner_address, + "new_account", + "execution_seed_invalid", + "execution_seeded_address_mismatch", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + ( + solana_system_interface::instruction::create_account_with_seed( + &from_address, + &new_account_address, + &base_address, + seed, + *lamports, + *space, + &owner_address, + ), + *lamports, + std::option::Option::None, + vec![ + (from.clone(), std::string::String::from("funding_account")), + (base.clone(), std::string::String::from("seed_base_account")), + ], + ) + }, + crate::SolanaCoreOperation::SystemCreateAccountAllowPrefund { + new_account, + payer, + lamports, + space, + owner, + } => { + match validate_create_space(*space) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + if payer.is_none() && *lamports != 0 { + return std::result::Result::Err(kb_core::Error::new( + "execution_prefund_payer_missing", + "create-account-allow-prefund requires a payer when additional lamports are requested", + )); + } + if let std::option::Option::Some(payer_pubkey) = payer { + match crate::validate_distinct_pubkeys( + payer_pubkey, + new_account, + "execution_prefund_payer_is_new_account", + "prefund payer and new account public keys must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + } + let new_account_address = match crate::parse_pubkey(new_account, "new_account") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + }; + let owner_address = match crate::parse_pubkey(owner, "owner") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let payer_address = match payer { + std::option::Option::Some(payer_pubkey) => { + match crate::parse_pubkey(payer_pubkey, "payer") { + std::result::Result::Ok(pubkey) => std::option::Option::Some(pubkey), + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + }, + std::option::Option::None => std::option::Option::None, + }; + let instruction = match payer_address.as_ref() { + std::option::Option::Some(address) => { + solana_system_interface::instruction::create_account_allow_prefund( + &new_account_address, + std::option::Option::Some((address, *lamports)), + *space, + &owner_address, + ) + }, + std::option::Option::None => { + solana_system_interface::instruction::create_account_allow_prefund( + &new_account_address, + std::option::Option::None, + *space, + &owner_address, + ) + }, + }; + let mut roles = + vec![(new_account.clone(), std::string::String::from("new_account"))]; + if let std::option::Option::Some(payer_pubkey) = payer { + roles + .push((payer_pubkey.clone(), std::string::String::from("funding_account"))); + } + ( + instruction, + if payer.is_some() { *lamports } else { 0 }, + std::option::Option::None, + roles, + ) + }, + crate::SolanaCoreOperation::SystemAllocate { account, space } => { + match validate_allocate_space(*space) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + let account_address = match crate::parse_pubkey(account, "account") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + ( + solana_system_interface::instruction::allocate(&account_address, *space), + 0, + std::option::Option::None, + vec![(account.clone(), std::string::String::from("allocated_account"))], + ) + }, + crate::SolanaCoreOperation::SystemAllocateWithSeed { + account, + base, + seed, + space, + owner, + } => { + match validate_allocate_space(*space) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + let account_address = match crate::parse_pubkey(account, "account") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let base_address = match crate::parse_pubkey(base, "base") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let owner_address = match crate::parse_pubkey(owner, "owner") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + match crate::validate_seeded_address( + &account_address, + &base_address, + seed, + &owner_address, + "account", + "execution_seed_invalid", + "execution_seeded_address_mismatch", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + ( + solana_system_interface::instruction::allocate_with_seed( + &account_address, + &base_address, + seed, + *space, + &owner_address, + ), + 0, + std::option::Option::None, + vec![(base.clone(), std::string::String::from("seed_base_account"))], + ) + }, + crate::SolanaCoreOperation::SystemAssign { account, owner } => { + let account_address = match crate::parse_pubkey(account, "account") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let owner_address = match crate::parse_pubkey(owner, "owner") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + ( + solana_system_interface::instruction::assign(&account_address, &owner_address), + 0, + std::option::Option::None, + vec![(account.clone(), std::string::String::from("assigned_account"))], + ) + }, + crate::SolanaCoreOperation::SystemAssignWithSeed { account, base, seed, owner } => { + let account_address = match crate::parse_pubkey(account, "account") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let base_address = match crate::parse_pubkey(base, "base") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let owner_address = match crate::parse_pubkey(owner, "owner") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + match crate::validate_seeded_address( + &account_address, + &base_address, + seed, + &owner_address, + "account", + "execution_seed_invalid", + "execution_seeded_address_mismatch", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + ( + solana_system_interface::instruction::assign_with_seed( + &account_address, + &base_address, + seed, + &owner_address, + ), + 0, + std::option::Option::None, + vec![(base.clone(), std::string::String::from("seed_base_account"))], + ) + }, + crate::SolanaCoreOperation::ComputeBudgetRequestHeapFrame { bytes } => { + if !(MIN_HEAP_FRAME_BYTES..=MAX_HEAP_FRAME_BYTES).contains(bytes) + || *bytes % HEAP_FRAME_BYTES_GRANULARITY != 0 + { + return std::result::Result::Err(kb_core::Error::new( + "execution_compute_budget_heap_frame_invalid", + format!( + "heap frame must be between {} and {} bytes and aligned to {} bytes", + MIN_HEAP_FRAME_BYTES, + MAX_HEAP_FRAME_BYTES, + HEAP_FRAME_BYTES_GRANULARITY + ), + )); + } + ( + solana_compute_budget_interface::ComputeBudgetInstruction::request_heap_frame( + *bytes, + ), + 0, + std::option::Option::None, + std::vec::Vec::new(), + ) + }, + crate::SolanaCoreOperation::ComputeBudgetSetUnitLimit { units } => { + if *units == 0 || *units > MAX_COMPUTE_UNIT_LIMIT { + return std::result::Result::Err(kb_core::Error::new( + "execution_compute_unit_limit_out_of_range", + format!( + "compute-unit limit must be between 1 and {}", + MAX_COMPUTE_UNIT_LIMIT + ), + )); + } + ( + solana_compute_budget_interface::ComputeBudgetInstruction::set_compute_unit_limit( + *units, + ), + 0, + std::option::Option::None, + std::vec::Vec::new(), + ) + }, + crate::SolanaCoreOperation::ComputeBudgetSetUnitPrice { micro_lamports } => ( + solana_compute_budget_interface::ComputeBudgetInstruction::set_compute_unit_price( + *micro_lamports, + ), + 0, + std::option::Option::Some(*micro_lamports), + std::vec::Vec::new(), + ), + crate::SolanaCoreOperation::ComputeBudgetSetLoadedAccountsDataSizeLimit { bytes } => { + if !(1..=MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES).contains(bytes) { + return std::result::Result::Err(kb_core::Error::new( + "execution_compute_budget_loaded_accounts_data_size_limit_invalid", + format!( + "loaded-account data-size limit must be between 1 and {} bytes", + MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES + ), + )); + } + ( + solana_compute_budget_interface::ComputeBudgetInstruction::set_loaded_accounts_data_size_limit( + *bytes, + ), + 0, + std::option::Option::None, + std::vec::Vec::new(), + ) + }, + _ => { + return std::result::Result::Err(kb_core::Error::new( + "execution_solana_core_internal_dispatch_failed", + format!("operation {operation_code} was not routed to its dedicated builder"), + )); + }, + }; + return crate::finalize_prepared_plan( + intent, + &fee_payer, + operation_code, + vec![instruction], + requested_spend_lamports, + requested_compute_unit_price, + signer_roles, + ); +} + +fn build_transfer_many_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + from: &kb_model::Pubkey, + recipients: &[crate::SystemTransferRecipient], +) -> kb_core::Result { + if recipients.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_transfer_many_empty", + "System transfer-many requires at least one recipient", + )); + } + let from_address = match crate::parse_pubkey(from, "from") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut to_lamports = std::vec::Vec::with_capacity(recipients.len()); + let mut requested_spend_lamports = 0_u64; + for recipient in recipients { + match crate::validate_positive_u64( + recipient.lamports, + "execution_transfer_many_lamports_zero", + "every System transfer-many recipient requires a positive lamport amount", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_distinct_pubkeys( + from, + &recipient.to, + "execution_transfer_many_same_account", + "System transfer-many source and every destination must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let to_address = match crate::parse_pubkey(&recipient.to, "recipient") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + requested_spend_lamports = match requested_spend_lamports.checked_add(recipient.lamports) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_transfer_many_lamports_overflow", + "System transfer-many total lamports exceed u64", + )); + }, + }; + to_lamports.push((to_address, recipient.lamports)); + } + let instructions = + solana_system_interface::instruction::transfer_many(&from_address, to_lamports.as_slice()); + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::SYSTEM_TRANSFER_MANY_OPERATION, + instructions, + requested_spend_lamports, + std::option::Option::None, + vec![(from.clone(), std::string::String::from("funding_account"))], + ); +} + +fn build_create_nonce_account_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + from: &kb_model::Pubkey, + nonce_account: &kb_model::Pubkey, + authority: &kb_model::Pubkey, + lamports: u64, +) -> kb_core::Result { + match validate_nonce_management_policy(intent) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_positive_u64( + lamports, + "execution_nonce_create_lamports_zero", + "durable nonce account creation requires a positive rent-exempt lamport amount", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_distinct_pubkeys( + from, + nonce_account, + "execution_nonce_create_same_account", + "nonce funding account and nonce account must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let from_address = match crate::parse_pubkey(from, "from") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let nonce_address = match crate::parse_pubkey(nonce_account, "nonce_account") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instructions = solana_system_interface::instruction::create_nonce_account( + &from_address, + &nonce_address, + &authority_address, + lamports, + ); + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::SYSTEM_NONCE_CREATE_OPERATION, + instructions, + lamports, + std::option::Option::None, + vec![ + (from.clone(), std::string::String::from("funding_account")), + (nonce_account.clone(), std::string::String::from("new_nonce_account")), + ], + ); +} + +fn build_create_nonce_account_with_seed_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + operation: &crate::SolanaCoreOperation, +) -> kb_core::Result { + let (from, nonce_account, base, seed, authority, lamports) = match operation { + crate::SolanaCoreOperation::SystemCreateNonceAccountWithSeed { + from, + nonce_account, + base, + seed, + authority, + lamports, + } => (from, nonce_account, base, seed, authority, *lamports), + _ => { + return std::result::Result::Err(kb_core::Error::new( + "execution_solana_core_internal_dispatch_failed", + "seeded nonce builder received an incompatible operation", + )); + }, + }; + match validate_nonce_management_policy(intent) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_positive_u64( + lamports, + "execution_nonce_create_lamports_zero", + "seeded durable nonce account creation requires a positive rent-exempt lamport amount", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_distinct_pubkeys( + from, + nonce_account, + "execution_nonce_create_same_account", + "nonce funding account and nonce account must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let from_address = match crate::parse_pubkey(from, "from") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let nonce_address = match crate::parse_pubkey(nonce_account, "nonce_account") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let base_address = match crate::parse_pubkey(base, "base") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let system_program = match solana_pubkey::Pubkey::from_str(kb_program_ids::SYSTEM_PROGRAM_ID) { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_system_program_id_invalid", + error.to_string(), + )); + }, + }; + match crate::validate_seeded_address( + &nonce_address, + &base_address, + seed, + &system_program, + "nonce_account", + "execution_seed_invalid", + "execution_seeded_address_mismatch", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let instructions = solana_system_interface::instruction::create_nonce_account_with_seed( + &from_address, + &nonce_address, + &base_address, + seed, + &authority_address, + lamports, + ); + let mut signer_roles = vec![(from.clone(), std::string::String::from("funding_account"))]; + if base != from { + signer_roles.push((base.clone(), std::string::String::from("seed_base_account"))); + } + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::SYSTEM_NONCE_CREATE_WITH_SEED_OPERATION, + instructions, + lamports, + std::option::Option::None, + signer_roles, + ); +} + +fn build_initialize_nonce_account_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + nonce_account: &kb_model::Pubkey, + authority: &kb_model::Pubkey, +) -> kb_core::Result { + match validate_nonce_management_policy(intent) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let nonce_address = match crate::parse_pubkey(nonce_account, "nonce_account") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = match initialize_nonce_account_instruction(&nonce_address, &authority_address) + { + std::result::Result::Ok(instruction) => instruction, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::SYSTEM_NONCE_INITIALIZE_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + std::vec::Vec::new(), + ); +} + +fn build_advance_nonce_account_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + nonce_account: &kb_model::Pubkey, + authority: &kb_model::Pubkey, +) -> kb_core::Result { + match validate_nonce_management_policy(intent) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let nonce_address = match crate::parse_pubkey(nonce_account, "nonce_account") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::SYSTEM_NONCE_ADVANCE_OPERATION, + vec![solana_system_interface::instruction::advance_nonce_account( + &nonce_address, + &authority_address, + )], + 0, + std::option::Option::None, + vec![(authority.clone(), std::string::String::from("nonce_authority"))], + ); +} + +fn build_authorize_nonce_account_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + nonce_account: &kb_model::Pubkey, + authority: &kb_model::Pubkey, + new_authority: &kb_model::Pubkey, +) -> kb_core::Result { + match validate_nonce_management_policy(intent) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let nonce_address = match crate::parse_pubkey(nonce_account, "nonce_account") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let new_authority_address = match crate::parse_pubkey(new_authority, "new_authority") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::SYSTEM_NONCE_AUTHORIZE_OPERATION, + vec![solana_system_interface::instruction::authorize_nonce_account( + &nonce_address, + &authority_address, + &new_authority_address, + )], + 0, + std::option::Option::None, + vec![(authority.clone(), std::string::String::from("nonce_authority"))], + ); +} + +fn build_withdraw_nonce_account_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + nonce_account: &kb_model::Pubkey, + authority: &kb_model::Pubkey, + to: &kb_model::Pubkey, + lamports: u64, +) -> kb_core::Result { + match validate_nonce_management_policy(intent) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_positive_u64( + lamports, + "execution_nonce_withdraw_lamports_zero", + "durable nonce withdrawal requires a positive lamport amount", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_distinct_pubkeys( + nonce_account, + to, + "execution_nonce_withdraw_same_account", + "nonce account and withdrawal recipient must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let nonce_address = match crate::parse_pubkey(nonce_account, "nonce_account") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let to_address = match crate::parse_pubkey(to, "to") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::SYSTEM_NONCE_WITHDRAW_OPERATION, + vec![solana_system_interface::instruction::withdraw_nonce_account( + &nonce_address, + &authority_address, + &to_address, + lamports, + )], + lamports, + std::option::Option::None, + vec![(authority.clone(), std::string::String::from("nonce_authority"))], + ); +} + +fn build_upgrade_nonce_account_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + nonce_account: &kb_model::Pubkey, +) -> kb_core::Result { + match validate_nonce_management_policy(intent) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let nonce_address = match crate::parse_pubkey(nonce_account, "nonce_account") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::SYSTEM_NONCE_UPGRADE_OPERATION, + vec![solana_system_interface::instruction::upgrade_nonce_account(nonce_address)], + 0, + std::option::Option::None, + std::vec::Vec::new(), + ); +} + +fn validate_nonce_management_policy( + intent: &crate::SolanaCoreExecutionIntent, +) -> kb_core::Result<()> { + if intent.policy.blockhash.kind != kb_execution_api::ExecutionBlockhashKind::Latest { + return std::result::Result::Err(kb_core::Error::new( + "execution_nonce_management_requires_latest_blockhash", + "nonce account lifecycle operations use a recent blockhash; durable nonce transaction assembly is a separate execution path", + )); + } + return std::result::Result::Ok(()); +} + +#[allow(deprecated)] +fn initialize_nonce_account_instruction( + nonce_account: &solana_pubkey::Pubkey, + authority: &solana_pubkey::Pubkey, +) -> kb_core::Result { + let data = match wincode::serialize( + &solana_system_interface::instruction::SystemInstruction::InitializeNonceAccount( + *authority, + ), + ) { + std::result::Result::Ok(data) => data, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_nonce_initialize_serialize_failed", + error.to_string(), + )); + }, + }; + return std::result::Result::Ok(solana_instruction::Instruction { + program_id: solana_sdk_ids::system_program::id(), + accounts: vec![ + solana_instruction::AccountMeta::new(*nonce_account, false), + solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::sysvar::recent_blockhashes::id(), + false, + ), + solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::sysvar::rent::id(), + false, + ), + ], + data, + }); +} + +pub(crate) fn finalize_prepared_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + operation_code: &str, + instructions: std::vec::Vec, + requested_spend_lamports: u64, + requested_compute_unit_price: std::option::Option, + signer_roles: std::vec::Vec<(kb_model::Pubkey, std::string::String)>, +) -> kb_core::Result { + if instructions.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_operation_has_no_instructions", + format!("operation {operation_code} produced no instructions"), + )); + } + let planned_instructions = instructions + .iter() + .map(|instruction| return planned_instruction(operation_code, instruction)) + .collect::>(); + let mut signer_roles = signer_roles; + if intent.policy.blockhash.kind == kb_execution_api::ExecutionBlockhashKind::DurableNonce { + if intent.policy.blockhash.max_age_slots.is_some() { + return std::result::Result::Err(kb_core::Error::new( + "execution_durable_nonce_has_blockhash_age", + "durable nonce plans must not declare a recent blockhash age", + )); + } + if intent.policy.blockhash.nonce_account.is_none() { + return std::result::Result::Err(kb_core::Error::new( + "execution_durable_nonce_account_missing", + "durable nonce plans require a nonce account", + )); + } + let nonce_authority = match &intent.policy.blockhash.nonce_authority { + std::option::Option::Some(authority) => authority.clone(), + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_durable_nonce_authority_missing", + "durable nonce plans require a nonce authority", + )); + }, + }; + signer_roles.push((nonce_authority, std::string::String::from("durable_nonce_authority"))); + } + let required_signers = required_signers(&intent.fee_payer, signer_roles.as_slice()); + match validate_signer_contract(planned_instructions.as_slice(), &required_signers) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + tracing::debug!( + target: crate::TRACING_TARGET, + action = "build_prepared_plan", + intent_id = %intent.intent_id, + operation_code, + instruction_count = planned_instructions.len(), + signer_count = required_signers.len(), + "built Solana core execution plan" + ); + return std::result::Result::Ok(kb_execution_api::PreparedExecutionPlan { + executor_name: std::string::String::from("kb_executor_solana_core"), + executor_version: std::string::String::from(env!("CARGO_PKG_VERSION")), + intent_id: intent.intent_id.clone(), + operation_code: std::string::String::from(operation_code), + fee_payer: kb_model::Pubkey(fee_payer.to_string()), + instructions: planned_instructions, + required_signers, + policy: intent.policy.clone(), + requested_spend_lamports, + requested_compute_unit_price_micro_lamports: requested_compute_unit_price, + }); +} + +fn validate_create_space(space: u64) -> kb_core::Result<()> { + if space > solana_system_interface::MAX_PERMITTED_DATA_LENGTH { + return std::result::Result::Err(kb_core::Error::new( + "execution_system_space_out_of_range", + format!( + "System account space must not exceed {} bytes", + solana_system_interface::MAX_PERMITTED_DATA_LENGTH + ), + )); + } + return std::result::Result::Ok(()); +} + +fn validate_allocate_space(space: u64) -> kb_core::Result<()> { + if space == 0 || space > solana_system_interface::MAX_PERMITTED_DATA_LENGTH { + return std::result::Result::Err(kb_core::Error::new( + "execution_system_space_out_of_range", + format!( + "System allocation space must be between 1 and {} bytes", + solana_system_interface::MAX_PERMITTED_DATA_LENGTH + ), + )); + } + return std::result::Result::Ok(()); +} + +fn planned_instruction( + operation_code: &str, + instruction: &solana_instruction::Instruction, +) -> kb_execution_api::PlannedInstruction { + return kb_execution_api::PlannedInstruction { + program_id: kb_model::ProgramId(instruction.program_id.to_string()), + operation_code: std::string::String::from(operation_code), + accounts: instruction + .accounts + .iter() + .map(|account| { + return kb_execution_api::PlannedAccount { + pubkey: kb_model::Pubkey(account.pubkey.to_string()), + is_signer: account.is_signer, + is_writable: account.is_writable, + }; + }) + .collect(), + data: instruction.data.clone(), + }; +} + +fn required_signers( + fee_payer: &kb_model::Pubkey, + signer_roles: &[(kb_model::Pubkey, std::string::String)], +) -> std::vec::Vec { + let mut required = vec![kb_execution_api::RequiredSigner { + pubkey: fee_payer.clone(), + role: std::string::String::from("fee_payer"), + }]; + for (pubkey, role) in signer_roles { + if required.iter().any(|candidate| return candidate.pubkey == *pubkey) { + continue; + } + required.push(kb_execution_api::RequiredSigner { + pubkey: pubkey.clone(), + role: role.clone(), + }); + } + return required; +} + +fn validate_signer_contract( + instructions: &[kb_execution_api::PlannedInstruction], + required_signers: &[kb_execution_api::RequiredSigner], +) -> kb_core::Result<()> { + for instruction in instructions { + for account in &instruction.accounts { + if account.is_signer + && !required_signers.iter().any(|signer| return signer.pubkey == account.pubkey) + { + return std::result::Result::Err(kb_core::Error::new( + "execution_instruction_signer_not_declared", + format!( + "instruction signer {} is missing from required signers", + account.pubkey.0 + ), + )); + } + } + } + return std::result::Result::Ok(()); +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; // rust-rules: trait-import + + fn pubkey(value: &str) -> kb_model::Pubkey { + return kb_model::Pubkey(std::string::String::from(value)); + } + + fn parsed(value: &str) -> solana_pubkey::Pubkey { + return solana_pubkey::Pubkey::from_str(value) + .unwrap_or_else(|error| panic!("invalid fixture: {error}")); + } + + fn policy(signers: &[&str]) -> kb_execution_api::ExecutionPolicy { + return kb_execution_api::ExecutionPolicy { + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(10_000), + max_fee_lamports: std::option::Option::Some(10_000), + max_compute_unit_price_micro_lamports: std::option::Option::Some(100), + }, + authorized_signers: signers.iter().map(|value| return pubkey(value)).collect(), + ..kb_execution_api::ExecutionPolicy::default() + }; + } + + fn assert_matches_official( + plan: &kb_execution_api::PreparedExecutionPlan, + expected: &solana_instruction::Instruction, + ) { + assert_eq!(plan.instructions.len(), 1); + let actual = &plan.instructions[0]; + assert_eq!(actual.program_id.0, expected.program_id.to_string()); + assert_eq!(actual.data, expected.data); + assert_eq!(actual.accounts.len(), expected.accounts.len()); + for (actual_account, expected_account) in + actual.accounts.iter().zip(expected.accounts.iter()) + { + assert_eq!(actual_account.pubkey.0, expected_account.pubkey.to_string()); + assert_eq!(actual_account.is_signer, expected_account.is_signer); + assert_eq!(actual_account.is_writable, expected_account.is_writable); + } + } + + fn assert_many_match_official( + plan: &kb_execution_api::PreparedExecutionPlan, + expected: &[solana_instruction::Instruction], + ) { + assert_eq!(plan.instructions.len(), expected.len()); + for (actual, expected_instruction) in plan.instructions.iter().zip(expected.iter()) { + assert_eq!(actual.program_id.0, expected_instruction.program_id.to_string()); + assert_eq!(actual.data, expected_instruction.data); + assert_eq!(actual.accounts.len(), expected_instruction.accounts.len()); + for (actual_account, expected_account) in + actual.accounts.iter().zip(expected_instruction.accounts.iter()) + { + assert_eq!(actual_account.pubkey.0, expected_account.pubkey.to_string()); + assert_eq!(actual_account.is_signer, expected_account.is_signer); + assert_eq!(actual_account.is_writable, expected_account.is_writable); + } + } + } + + #[test] + fn transfer_matches_official_builder_and_signers() { + let from = kb_program_ids::VOTE_PROGRAM_ID; + let to = kb_program_ids::STAKE_PROGRAM_ID; + let intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("transfer-1"), + fee_payer: pubkey(from), + policy: policy(&[from]), + operation: crate::SolanaCoreOperation::SystemTransfer { + from: pubkey(from), + to: pubkey(to), + lamports: 500, + }, + }; + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let expected = + solana_system_interface::instruction::transfer(&parsed(from), &parsed(to), 500); + assert_matches_official(&plan, &expected); + assert_eq!(plan.operation_code, crate::SYSTEM_TRANSFER_OPERATION); + assert_eq!(plan.requested_spend_lamports, 500); + assert_eq!(plan.required_signers.len(), 1); + assert_eq!(plan.required_signers[0].pubkey.0, from); + } + + #[test] + fn create_account_matches_official_builder_and_allows_zero_space() { + let from = kb_program_ids::VOTE_PROGRAM_ID; + let new_account = kb_program_ids::STAKE_PROGRAM_ID; + let owner = kb_program_ids::SYSTEM_PROGRAM_ID; + let intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("create-1"), + fee_payer: pubkey(from), + policy: policy(&[from, new_account]), + operation: crate::SolanaCoreOperation::SystemCreateAccount { + from: pubkey(from), + new_account: pubkey(new_account), + lamports: 5_000, + space: 0, + owner: pubkey(owner), + }, + }; + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let expected = solana_system_interface::instruction::create_account( + &parsed(from), + &parsed(new_account), + 5_000, + 0, + &parsed(owner), + ); + assert_matches_official(&plan, &expected); + assert_eq!(plan.operation_code, crate::SYSTEM_CREATE_ACCOUNT_OPERATION); + assert_eq!(plan.required_signers.len(), 2); + } + + #[test] + fn allocate_and_assign_match_official_builders() { + let account = kb_program_ids::VOTE_PROGRAM_ID; + let owner = kb_program_ids::STAKE_PROGRAM_ID; + let allocate_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("allocate-1"), + fee_payer: pubkey(account), + policy: policy(&[account]), + operation: crate::SolanaCoreOperation::SystemAllocate { + account: pubkey(account), + space: 256, + }, + }; + let allocate_plan = crate::builder_build_prepared_plan(&allocate_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let account_address = parsed(account); + let allocate_expected = + solana_system_interface::instruction::allocate(&account_address, 256); + assert_matches_official(&allocate_plan, &allocate_expected); + assert_eq!(allocate_plan.operation_code, crate::SYSTEM_ALLOCATE_OPERATION); + let assign_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("assign-1"), + fee_payer: pubkey(account), + policy: policy(&[account]), + operation: crate::SolanaCoreOperation::SystemAssign { + account: pubkey(account), + owner: pubkey(owner), + }, + }; + let assign_plan = crate::builder_build_prepared_plan(&assign_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let owner_address = parsed(owner); + let assign_expected = + solana_system_interface::instruction::assign(&account_address, &owner_address); + assert_matches_official(&assign_plan, &assign_expected); + assert_eq!(assign_plan.operation_code, crate::SYSTEM_ASSIGN_OPERATION); + } + + #[test] + fn seeded_system_operations_match_official_builders() { + let base = parsed(kb_program_ids::VOTE_PROGRAM_ID); + let owner = parsed(kb_program_ids::SYSTEM_PROGRAM_ID); + let destination = parsed(kb_program_ids::STAKE_PROGRAM_ID); + let seed = "khadhroony-seed"; + let derived = solana_pubkey::Pubkey::create_with_seed(&base, seed, &owner) + .unwrap_or_else(|error| panic!("seed fixture failed: {error}")); + let base_text = base.to_string(); + let owner_text = owner.to_string(); + let derived_text = derived.to_string(); + let destination_text = destination.to_string(); + + let create_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("create-seed-1"), + fee_payer: pubkey(base_text.as_str()), + policy: policy(&[base_text.as_str()]), + operation: crate::SolanaCoreOperation::SystemCreateAccountWithSeed { + from: pubkey(base_text.as_str()), + new_account: pubkey(derived_text.as_str()), + base: pubkey(base_text.as_str()), + seed: std::string::String::from(seed), + lamports: 5_000, + space: 0, + owner: pubkey(owner_text.as_str()), + }, + }; + let create_plan = crate::builder_build_prepared_plan(&create_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let create_expected = solana_system_interface::instruction::create_account_with_seed( + &base, &derived, &base, seed, 5_000, 0, &owner, + ); + assert_matches_official(&create_plan, &create_expected); + assert_eq!(create_plan.operation_code, crate::SYSTEM_CREATE_ACCOUNT_WITH_SEED_OPERATION); + assert_eq!(create_plan.required_signers.len(), 1); + + let transfer_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("transfer-seed-1"), + fee_payer: pubkey(base_text.as_str()), + policy: policy(&[base_text.as_str()]), + operation: crate::SolanaCoreOperation::SystemTransferWithSeed { + from: pubkey(derived_text.as_str()), + from_base: pubkey(base_text.as_str()), + from_seed: std::string::String::from(seed), + from_owner: pubkey(owner_text.as_str()), + to: pubkey(destination_text.as_str()), + lamports: 500, + }, + }; + let transfer_plan = crate::builder_build_prepared_plan(&transfer_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let transfer_expected = solana_system_interface::instruction::transfer_with_seed( + &derived, + &base, + std::string::String::from(seed), + &owner, + &destination, + 500, + ); + assert_matches_official(&transfer_plan, &transfer_expected); + assert_eq!(transfer_plan.operation_code, crate::SYSTEM_TRANSFER_WITH_SEED_OPERATION); + + let allocate_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("allocate-seed-1"), + fee_payer: pubkey(base_text.as_str()), + policy: policy(&[base_text.as_str()]), + operation: crate::SolanaCoreOperation::SystemAllocateWithSeed { + account: pubkey(derived_text.as_str()), + base: pubkey(base_text.as_str()), + seed: std::string::String::from(seed), + space: 256, + owner: pubkey(owner_text.as_str()), + }, + }; + let allocate_plan = crate::builder_build_prepared_plan(&allocate_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let allocate_expected = solana_system_interface::instruction::allocate_with_seed( + &derived, &base, seed, 256, &owner, + ); + assert_matches_official(&allocate_plan, &allocate_expected); + assert_eq!(allocate_plan.operation_code, crate::SYSTEM_ALLOCATE_WITH_SEED_OPERATION); + + let assign_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("assign-seed-1"), + fee_payer: pubkey(base_text.as_str()), + policy: policy(&[base_text.as_str()]), + operation: crate::SolanaCoreOperation::SystemAssignWithSeed { + account: pubkey(derived_text.as_str()), + base: pubkey(base_text.as_str()), + seed: std::string::String::from(seed), + owner: pubkey(owner_text.as_str()), + }, + }; + let assign_plan = crate::builder_build_prepared_plan(&assign_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let assign_expected = + solana_system_interface::instruction::assign_with_seed(&derived, &base, seed, &owner); + assert_matches_official(&assign_plan, &assign_expected); + assert_eq!(assign_plan.operation_code, crate::SYSTEM_ASSIGN_WITH_SEED_OPERATION); + } + + #[test] + fn seeded_create_with_distinct_funding_and_base_declares_both_signers() { + let funding = parsed(kb_program_ids::VOTE_PROGRAM_ID); + let base = parsed(kb_program_ids::CONFIG_PROGRAM_ID); + let owner = parsed(kb_program_ids::SYSTEM_PROGRAM_ID); + let seed = "khadhroony-distinct-base"; + let derived = solana_pubkey::Pubkey::create_with_seed(&base, seed, &owner) + .unwrap_or_else(|error| panic!("seed fixture failed: {error}")); + let funding_text = funding.to_string(); + let base_text = base.to_string(); + let owner_text = owner.to_string(); + let derived_text = derived.to_string(); + let intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("create-seed-distinct-base"), + fee_payer: pubkey(funding_text.as_str()), + policy: policy(&[funding_text.as_str(), base_text.as_str()]), + operation: crate::SolanaCoreOperation::SystemCreateAccountWithSeed { + from: pubkey(funding_text.as_str()), + new_account: pubkey(derived_text.as_str()), + base: pubkey(base_text.as_str()), + seed: std::string::String::from(seed), + lamports: 5_000, + space: 0, + owner: pubkey(owner_text.as_str()), + }, + }; + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let expected = solana_system_interface::instruction::create_account_with_seed( + &funding, &derived, &base, seed, 5_000, 0, &owner, + ); + assert_matches_official(&plan, &expected); + assert_eq!(plan.required_signers.len(), 2); + assert!( + plan.required_signers + .iter() + .any(|signer| return signer.pubkey.0 == funding_text) + ); + assert!(plan.required_signers.iter().any(|signer| return signer.pubkey.0 == base_text)); + } + + #[test] + fn create_account_allow_prefund_matches_official_builder() { + let payer = kb_program_ids::VOTE_PROGRAM_ID; + let new_account = kb_program_ids::STAKE_PROGRAM_ID; + let owner = kb_program_ids::SYSTEM_PROGRAM_ID; + let funded_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("prefund-funded"), + fee_payer: pubkey(payer), + policy: policy(&[payer, new_account]), + operation: crate::SolanaCoreOperation::SystemCreateAccountAllowPrefund { + new_account: pubkey(new_account), + payer: std::option::Option::Some(pubkey(payer)), + lamports: 5_000, + space: 0, + owner: pubkey(owner), + }, + }; + let funded_plan = crate::builder_build_prepared_plan(&funded_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let payer_address = parsed(payer); + let new_account_address = parsed(new_account); + let owner_address = parsed(owner); + let funded_expected = solana_system_interface::instruction::create_account_allow_prefund( + &new_account_address, + std::option::Option::Some((&payer_address, 5_000)), + 0, + &owner_address, + ); + assert_matches_official(&funded_plan, &funded_expected); + assert_eq!( + funded_plan.operation_code, + crate::SYSTEM_CREATE_ACCOUNT_ALLOW_PREFUND_OPERATION + ); + assert_eq!(funded_plan.requested_spend_lamports, 5_000); + assert_eq!(funded_plan.required_signers.len(), 2); + + let prefunded_account = kb_program_ids::CONFIG_PROGRAM_ID; + let prefunded_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("prefund-existing"), + fee_payer: pubkey(payer), + policy: policy(&[payer, prefunded_account]), + operation: crate::SolanaCoreOperation::SystemCreateAccountAllowPrefund { + new_account: pubkey(prefunded_account), + payer: std::option::Option::None, + lamports: 0, + space: 0, + owner: pubkey(owner), + }, + }; + let prefunded_plan = crate::builder_build_prepared_plan(&prefunded_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let prefunded_address = parsed(prefunded_account); + let prefunded_expected = solana_system_interface::instruction::create_account_allow_prefund( + &prefunded_address, + std::option::Option::None, + 0, + &owner_address, + ); + assert_matches_official(&prefunded_plan, &prefunded_expected); + assert_eq!(prefunded_plan.requested_spend_lamports, 0); + } + + #[test] + fn transfer_many_matches_official_builder_and_sums_spend() { + let from = kb_program_ids::VOTE_PROGRAM_ID; + let first = kb_program_ids::STAKE_PROGRAM_ID; + let second = kb_program_ids::CONFIG_PROGRAM_ID; + let intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("transfer-many-1"), + fee_payer: pubkey(from), + policy: policy(&[from]), + operation: crate::SolanaCoreOperation::SystemTransferMany { + from: pubkey(from), + recipients: vec![ + crate::SystemTransferRecipient { to: pubkey(first), lamports: 400 }, + crate::SystemTransferRecipient { to: pubkey(second), lamports: 600 }, + ], + }, + }; + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let expected = solana_system_interface::instruction::transfer_many( + &parsed(from), + &[(parsed(first), 400), (parsed(second), 600)], + ); + assert_many_match_official(&plan, expected.as_slice()); + assert_eq!(plan.operation_code, crate::SYSTEM_TRANSFER_MANY_OPERATION); + assert_eq!(plan.requested_spend_lamports, 1_000); + assert_eq!(plan.required_signers.len(), 1); + } + + #[test] + fn nonce_creation_helpers_match_official_builders() { + let from = kb_program_ids::VOTE_PROGRAM_ID; + let nonce = kb_program_ids::STAKE_PROGRAM_ID; + let authority = kb_program_ids::CONFIG_PROGRAM_ID; + let create_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("nonce-create-1"), + fee_payer: pubkey(from), + policy: policy(&[from, nonce]), + operation: crate::SolanaCoreOperation::SystemCreateNonceAccount { + from: pubkey(from), + nonce_account: pubkey(nonce), + authority: pubkey(authority), + lamports: 5_000, + }, + }; + let create_plan = crate::builder_build_prepared_plan(&create_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let create_expected = solana_system_interface::instruction::create_nonce_account( + &parsed(from), + &parsed(nonce), + &parsed(authority), + 5_000, + ); + assert_many_match_official(&create_plan, create_expected.as_slice()); + assert_eq!(create_plan.required_signers.len(), 2); + assert_eq!(create_plan.requested_spend_lamports, 5_000); + + let base = parsed(kb_program_ids::STAKE_PROGRAM_ID); + let system = parsed(kb_program_ids::SYSTEM_PROGRAM_ID); + let seed = "nonce-seed"; + let seeded_nonce = solana_pubkey::Pubkey::create_with_seed(&base, seed, &system) + .unwrap_or_else(|error| panic!("seed fixture failed: {error}")); + let seeded_nonce_text = seeded_nonce.to_string(); + let seeded_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("nonce-create-seed-1"), + fee_payer: pubkey(from), + policy: policy(&[from, kb_program_ids::STAKE_PROGRAM_ID]), + operation: crate::SolanaCoreOperation::SystemCreateNonceAccountWithSeed { + from: pubkey(from), + nonce_account: pubkey(seeded_nonce_text.as_str()), + base: pubkey(kb_program_ids::STAKE_PROGRAM_ID), + seed: std::string::String::from(seed), + authority: pubkey(authority), + lamports: 5_000, + }, + }; + let seeded_plan = crate::builder_build_prepared_plan(&seeded_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let seeded_expected = solana_system_interface::instruction::create_nonce_account_with_seed( + &parsed(from), + &seeded_nonce, + &base, + seed, + &parsed(authority), + 5_000, + ); + assert_many_match_official(&seeded_plan, seeded_expected.as_slice()); + assert_eq!(seeded_plan.required_signers.len(), 2); + } + + #[test] + fn nonce_lifecycle_operations_match_official_builders() { + let payer = kb_program_ids::VOTE_PROGRAM_ID; + let nonce = kb_program_ids::STAKE_PROGRAM_ID; + let authority = kb_program_ids::CONFIG_PROGRAM_ID; + let new_authority = kb_program_ids::FEATURE_PROGRAM_ID; + let recipient = kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID; + let base_policy = policy(&[payer, authority]); + + let initialize_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("nonce-initialize-1"), + fee_payer: pubkey(payer), + policy: base_policy.clone(), + operation: crate::SolanaCoreOperation::SystemInitializeNonceAccount { + nonce_account: pubkey(nonce), + authority: pubkey(authority), + }, + }; + let initialize_plan = crate::builder_build_prepared_plan(&initialize_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let initialize_expected = solana_system_interface::instruction::create_nonce_account( + &parsed(payer), + &parsed(nonce), + &parsed(authority), + 1, + ); + assert_matches_official(&initialize_plan, &initialize_expected[1]); + assert_eq!(initialize_plan.required_signers.len(), 1); + + let advance_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("nonce-advance-1"), + fee_payer: pubkey(payer), + policy: base_policy.clone(), + operation: crate::SolanaCoreOperation::SystemAdvanceNonceAccount { + nonce_account: pubkey(nonce), + authority: pubkey(authority), + }, + }; + let advance_plan = crate::builder_build_prepared_plan(&advance_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let advance_expected = solana_system_interface::instruction::advance_nonce_account( + &parsed(nonce), + &parsed(authority), + ); + assert_matches_official(&advance_plan, &advance_expected); + + let authorize_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("nonce-authorize-1"), + fee_payer: pubkey(payer), + policy: base_policy.clone(), + operation: crate::SolanaCoreOperation::SystemAuthorizeNonceAccount { + nonce_account: pubkey(nonce), + authority: pubkey(authority), + new_authority: pubkey(new_authority), + }, + }; + let authorize_plan = crate::builder_build_prepared_plan(&authorize_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let authorize_expected = solana_system_interface::instruction::authorize_nonce_account( + &parsed(nonce), + &parsed(authority), + &parsed(new_authority), + ); + assert_matches_official(&authorize_plan, &authorize_expected); + + let withdraw_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("nonce-withdraw-1"), + fee_payer: pubkey(payer), + policy: base_policy.clone(), + operation: crate::SolanaCoreOperation::SystemWithdrawNonceAccount { + nonce_account: pubkey(nonce), + authority: pubkey(authority), + to: pubkey(recipient), + lamports: 1_000, + }, + }; + let withdraw_plan = crate::builder_build_prepared_plan(&withdraw_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let withdraw_expected = solana_system_interface::instruction::withdraw_nonce_account( + &parsed(nonce), + &parsed(authority), + &parsed(recipient), + 1_000, + ); + assert_matches_official(&withdraw_plan, &withdraw_expected); + assert_eq!(withdraw_plan.requested_spend_lamports, 1_000); + + let upgrade_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("nonce-upgrade-1"), + fee_payer: pubkey(payer), + policy: base_policy, + operation: crate::SolanaCoreOperation::SystemUpgradeNonceAccount { + nonce_account: pubkey(nonce), + }, + }; + let upgrade_plan = crate::builder_build_prepared_plan(&upgrade_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let upgrade_expected = + solana_system_interface::instruction::upgrade_nonce_account(parsed(nonce)); + assert_matches_official(&upgrade_plan, &upgrade_expected); + assert_eq!(upgrade_plan.required_signers.len(), 1); + } + + #[test] + fn transfer_many_and_nonce_policy_reject_invalid_contracts() { + let payer = kb_program_ids::VOTE_PROGRAM_ID; + let empty_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("transfer-many-empty"), + fee_payer: pubkey(payer), + policy: policy(&[payer]), + operation: crate::SolanaCoreOperation::SystemTransferMany { + from: pubkey(payer), + recipients: std::vec::Vec::new(), + }, + }; + let empty_error = crate::builder_build_prepared_plan(&empty_intent) + .expect_err("empty transfer-many must fail"); + assert_eq!(empty_error.code(), "execution_transfer_many_empty"); + + let mut nonce_policy = policy(&[payer]); + nonce_policy.blockhash = kb_execution_api::ExecutionBlockhashPolicy { + kind: kb_execution_api::ExecutionBlockhashKind::DurableNonce, + max_age_slots: std::option::Option::None, + nonce_account: std::option::Option::Some(pubkey(kb_program_ids::STAKE_PROGRAM_ID)), + nonce_authority: std::option::Option::Some(pubkey(payer)), + }; + let nonce_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("nonce-management-durable-policy"), + fee_payer: pubkey(payer), + policy: nonce_policy, + operation: crate::SolanaCoreOperation::SystemAdvanceNonceAccount { + nonce_account: pubkey(kb_program_ids::STAKE_PROGRAM_ID), + authority: pubkey(payer), + }, + }; + let nonce_error = crate::builder_build_prepared_plan(&nonce_intent) + .expect_err("nonce lifecycle builder must not impersonate durable assembly"); + assert_eq!(nonce_error.code(), "execution_nonce_management_requires_latest_blockhash"); + } + + #[test] + fn durable_nonce_policy_declares_nonce_authority_signer() { + let payer = kb_program_ids::VOTE_PROGRAM_ID; + let authority = kb_program_ids::STAKE_PROGRAM_ID; + let nonce_account = kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID; + let mut policy = policy(&[payer, authority]); + policy.blockhash = kb_execution_api::ExecutionBlockhashPolicy { + kind: kb_execution_api::ExecutionBlockhashKind::DurableNonce, + max_age_slots: std::option::Option::None, + nonce_account: std::option::Option::Some(pubkey(nonce_account)), + nonce_authority: std::option::Option::Some(pubkey(authority)), + }; + let intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("durable-transfer-1"), + fee_payer: pubkey(payer), + policy, + operation: crate::SolanaCoreOperation::SystemTransfer { + from: pubkey(payer), + to: pubkey(kb_program_ids::SYSTEM_PROGRAM_ID), + lamports: 500, + }, + }; + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(plan.required_signers.len(), 2); + assert!(plan.required_signers.iter().any(|required| { + return required.pubkey.0 == authority && required.role == "durable_nonce_authority"; + })); + } + + #[test] + fn compute_budget_operations_match_official_builders() { + let payer = kb_program_ids::VOTE_PROGRAM_ID; + let heap_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("heap-1"), + fee_payer: pubkey(payer), + policy: policy(&[payer]), + operation: crate::SolanaCoreOperation::ComputeBudgetRequestHeapFrame { + bytes: 64 * 1_024, + }, + }; + let heap_plan = crate::builder_build_prepared_plan(&heap_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let heap_expected = + solana_compute_budget_interface::ComputeBudgetInstruction::request_heap_frame( + 64 * 1_024, + ); + assert_matches_official(&heap_plan, &heap_expected); + assert_eq!(heap_plan.operation_code, crate::COMPUTE_BUDGET_REQUEST_HEAP_FRAME_OPERATION); + let limit_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("limit-1"), + fee_payer: pubkey(payer), + policy: policy(&[payer]), + operation: crate::SolanaCoreOperation::ComputeBudgetSetUnitLimit { units: 500_000 }, + }; + let limit_plan = crate::builder_build_prepared_plan(&limit_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let limit_expected = + solana_compute_budget_interface::ComputeBudgetInstruction::set_compute_unit_limit( + 500_000, + ); + assert_matches_official(&limit_plan, &limit_expected); + assert_eq!(limit_plan.operation_code, crate::COMPUTE_BUDGET_SET_UNIT_LIMIT_OPERATION); + let price_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("price-1"), + fee_payer: pubkey(payer), + policy: policy(&[payer]), + operation: crate::SolanaCoreOperation::ComputeBudgetSetUnitPrice { micro_lamports: 7 }, + }; + let price_plan = crate::builder_build_prepared_plan(&price_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let price_expected = + solana_compute_budget_interface::ComputeBudgetInstruction::set_compute_unit_price(7); + assert_matches_official(&price_plan, &price_expected); + assert_eq!(price_plan.operation_code, crate::COMPUTE_BUDGET_SET_UNIT_PRICE_OPERATION); + assert_eq!( + price_plan.requested_compute_unit_price_micro_lamports, + std::option::Option::Some(7) + ); + let loaded_data_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("loaded-data-1"), + fee_payer: pubkey(payer), + policy: policy(&[payer]), + operation: crate::SolanaCoreOperation::ComputeBudgetSetLoadedAccountsDataSizeLimit { + bytes: 8 * 1_024 * 1_024, + }, + }; + let loaded_data_plan = crate::builder_build_prepared_plan(&loaded_data_intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let loaded_data_expected = solana_compute_budget_interface::ComputeBudgetInstruction::set_loaded_accounts_data_size_limit( + 8 * 1_024 * 1_024, + ); + assert_matches_official(&loaded_data_plan, &loaded_data_expected); + assert_eq!( + loaded_data_plan.operation_code, + crate::COMPUTE_BUDGET_SET_LOADED_ACCOUNTS_DATA_SIZE_LIMIT_OPERATION + ); + } + + #[test] + fn compute_budget_resource_limits_reject_invalid_values() { + let payer = kb_program_ids::VOTE_PROGRAM_ID; + let invalid_heap_values = [ + super::MIN_HEAP_FRAME_BYTES - 1, + super::MIN_HEAP_FRAME_BYTES + 1, + super::MAX_HEAP_FRAME_BYTES + super::HEAP_FRAME_BYTES_GRANULARITY, + ]; + for bytes in invalid_heap_values { + let intent = crate::SolanaCoreExecutionIntent { + intent_id: format!("invalid-heap-{bytes}"), + fee_payer: pubkey(payer), + policy: policy(&[payer]), + operation: crate::SolanaCoreOperation::ComputeBudgetRequestHeapFrame { bytes }, + }; + let error = crate::builder_build_prepared_plan(&intent) + .expect_err("invalid heap frame must fail"); + assert_eq!(error.code(), "execution_compute_budget_heap_frame_invalid"); + } + let invalid_loaded_data_values = [0, super::MAX_LOADED_ACCOUNTS_DATA_SIZE_BYTES + 1]; + for bytes in invalid_loaded_data_values { + let intent = crate::SolanaCoreExecutionIntent { + intent_id: format!("invalid-loaded-data-{bytes}"), + fee_payer: pubkey(payer), + policy: policy(&[payer]), + operation: + crate::SolanaCoreOperation::ComputeBudgetSetLoadedAccountsDataSizeLimit { + bytes, + }, + }; + let error = crate::builder_build_prepared_plan(&intent) + .expect_err("invalid loaded-account data-size limit must fail"); + assert_eq!( + error.code(), + "execution_compute_budget_loaded_accounts_data_size_limit_invalid" + ); + } + } + + #[test] + fn malformed_seed_and_prefund_contracts_are_rejected() { + let payer = kb_program_ids::VOTE_PROGRAM_ID; + let owner = kb_program_ids::SYSTEM_PROGRAM_ID; + let mismatch_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("seed-mismatch"), + fee_payer: pubkey(payer), + policy: policy(&[payer]), + operation: crate::SolanaCoreOperation::SystemAllocateWithSeed { + account: pubkey(kb_program_ids::STAKE_PROGRAM_ID), + base: pubkey(payer), + seed: std::string::String::from("seed"), + space: 1, + owner: pubkey(owner), + }, + }; + let mismatch_error = crate::builder_build_prepared_plan(&mismatch_intent) + .expect_err("mismatched seeded address must fail"); + assert_eq!(mismatch_error.code(), "execution_seeded_address_mismatch"); + + let long_seed = "x".repeat(33); + let invalid_seed_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("seed-too-long"), + fee_payer: pubkey(payer), + policy: policy(&[payer]), + operation: crate::SolanaCoreOperation::SystemAllocateWithSeed { + account: pubkey(kb_program_ids::STAKE_PROGRAM_ID), + base: pubkey(payer), + seed: long_seed, + space: 1, + owner: pubkey(owner), + }, + }; + let seed_error = crate::builder_build_prepared_plan(&invalid_seed_intent) + .expect_err("oversized seed must fail"); + assert_eq!(seed_error.code(), "execution_seed_invalid"); + + let missing_payer_intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("prefund-missing-payer"), + fee_payer: pubkey(payer), + policy: policy(&[payer, kb_program_ids::STAKE_PROGRAM_ID]), + operation: crate::SolanaCoreOperation::SystemCreateAccountAllowPrefund { + new_account: pubkey(kb_program_ids::STAKE_PROGRAM_ID), + payer: std::option::Option::None, + lamports: 1, + space: 0, + owner: pubkey(owner), + }, + }; + let payer_error = crate::builder_build_prepared_plan(&missing_payer_intent) + .expect_err("additional lamports without payer must fail"); + assert_eq!(payer_error.code(), "execution_prefund_payer_missing"); + } + + #[test] + fn malformed_and_dangerous_values_are_rejected() { + let payer = kb_program_ids::VOTE_PROGRAM_ID; + let invalid_pubkey = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("invalid-key"), + fee_payer: pubkey("invalid"), + policy: policy(&[payer]), + operation: crate::SolanaCoreOperation::ComputeBudgetSetUnitLimit { units: 1 }, + }; + let invalid_key_error = crate::builder_build_prepared_plan(&invalid_pubkey) + .expect_err("invalid public key must fail"); + assert_eq!(invalid_key_error.code(), "execution_pubkey_invalid"); + let zero_transfer = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("zero-transfer"), + fee_payer: pubkey(payer), + policy: policy(&[payer]), + operation: crate::SolanaCoreOperation::SystemTransfer { + from: pubkey(payer), + to: pubkey(kb_program_ids::STAKE_PROGRAM_ID), + lamports: 0, + }, + }; + let zero_error = crate::builder_build_prepared_plan(&zero_transfer) + .expect_err("zero transfer must fail"); + assert_eq!(zero_error.code(), "execution_transfer_lamports_zero"); + let zero_allocate = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("zero-allocate"), + fee_payer: pubkey(payer), + policy: policy(&[payer]), + operation: crate::SolanaCoreOperation::SystemAllocate { + account: pubkey(payer), + space: 0, + }, + }; + let allocate_error = crate::builder_build_prepared_plan(&zero_allocate) + .expect_err("zero allocation must fail"); + assert_eq!(allocate_error.code(), "execution_system_space_out_of_range"); + let excessive_compute = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("excessive-compute"), + fee_payer: pubkey(payer), + policy: policy(&[payer]), + operation: crate::SolanaCoreOperation::ComputeBudgetSetUnitLimit { units: 1_400_001 }, + }; + let compute_error = crate::builder_build_prepared_plan(&excessive_compute) + .expect_err("excessive compute limit must fail"); + assert_eq!(compute_error.code(), "execution_compute_unit_limit_out_of_range"); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/constants.rs new file mode 100644 index 0000000..abbd1c7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_executor_solana_core/src/constants.rs +// version: 3 + +//! Local constants for the `kb_executor_solana_core` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_solana_core"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/executor.rs new file mode 100644 index 0000000..2727b15 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/executor.rs @@ -0,0 +1,651 @@ +// file: kb_executor_solana_core/src/executor.rs +// version: 16 + +//! Exact capability dispatch and typed plan construction for Solana core. + +/// Solana core executor implementation. +#[derive(Clone, Debug, Default)] +pub struct SolanaCoreExecutor; + +impl crate::SolanaCoreExecutor { + fn exact_capability( + &self, + program_id: &kb_model::ProgramId, + operation_code: &str, + ) -> kb_execution_api::ExecutionCapability { + if program_id.0.as_str() == kb_program_ids::SYSTEM_PROGRAM_ID { + return match operation_code { + crate::SYSTEM_TRANSFER_OPERATION + | crate::SYSTEM_TRANSFER_MANY_OPERATION + | crate::SYSTEM_TRANSFER_WITH_SEED_OPERATION + | crate::SYSTEM_CREATE_ACCOUNT_OPERATION + | crate::SYSTEM_CREATE_ACCOUNT_WITH_SEED_OPERATION + | crate::SYSTEM_CREATE_ACCOUNT_ALLOW_PREFUND_OPERATION + | crate::SYSTEM_ALLOCATE_OPERATION + | crate::SYSTEM_ALLOCATE_WITH_SEED_OPERATION + | crate::SYSTEM_ASSIGN_OPERATION + | crate::SYSTEM_ASSIGN_WITH_SEED_OPERATION + | crate::SYSTEM_NONCE_CREATE_OPERATION + | crate::SYSTEM_NONCE_CREATE_WITH_SEED_OPERATION + | crate::SYSTEM_NONCE_INITIALIZE_OPERATION + | crate::SYSTEM_NONCE_ADVANCE_OPERATION + | crate::SYSTEM_NONCE_AUTHORIZE_OPERATION + | crate::SYSTEM_NONCE_WITHDRAW_OPERATION + | crate::SYSTEM_NONCE_UPGRADE_OPERATION => { + kb_execution_api::ExecutionCapability::supported(operation_code) + }, + _ => kb_execution_api::ExecutionCapability::unsupported( + "execution_system_operation_unsupported", + format!("System operation {operation_code} is not implemented"), + ), + }; + } + if program_id.0.as_str() == kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID { + return match operation_code { + crate::COMPUTE_BUDGET_REQUEST_HEAP_FRAME_OPERATION + | crate::COMPUTE_BUDGET_SET_UNIT_LIMIT_OPERATION + | crate::COMPUTE_BUDGET_SET_UNIT_PRICE_OPERATION + | crate::COMPUTE_BUDGET_SET_LOADED_ACCOUNTS_DATA_SIZE_LIMIT_OPERATION => { + kb_execution_api::ExecutionCapability::supported(operation_code) + }, + _ => kb_execution_api::ExecutionCapability::unsupported( + "execution_compute_budget_operation_unsupported", + format!("Compute Budget operation {operation_code} is not implemented"), + ), + }; + } + if program_id.0.as_str() == kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID { + return match operation_code { + crate::ADDRESS_LOOKUP_TABLE_CREATE_OPERATION + | crate::ADDRESS_LOOKUP_TABLE_EXTEND_OPERATION + | crate::ADDRESS_LOOKUP_TABLE_FREEZE_OPERATION + | crate::ADDRESS_LOOKUP_TABLE_DEACTIVATE_OPERATION + | crate::ADDRESS_LOOKUP_TABLE_CLOSE_OPERATION => { + kb_execution_api::ExecutionCapability::supported(operation_code) + }, + _ => kb_execution_api::ExecutionCapability::unsupported( + "execution_address_lookup_table_operation_unsupported", + format!("Address Lookup Table operation {operation_code} is not implemented"), + ), + }; + } + if program_id.0.as_str() == kb_program_ids::ED25519_PROGRAM_ID { + return match operation_code { + crate::ED25519_VERIFY_INLINE_OPERATION + | crate::ED25519_VERIFY_OFFSETS_OPERATION => { + kb_execution_api::ExecutionCapability::supported(operation_code) + }, + _ => kb_execution_api::ExecutionCapability::unsupported( + "execution_ed25519_operation_unsupported", + format!("Ed25519 operation {operation_code} is not implemented"), + ), + }; + } + if program_id.0.as_str() == kb_program_ids::SECP256K1_PROGRAM_ID { + return match operation_code { + crate::SECP256K1_VERIFY_INLINE_OPERATION + | crate::SECP256K1_VERIFY_OFFSETS_OPERATION => { + kb_execution_api::ExecutionCapability::supported(operation_code) + }, + _ => kb_execution_api::ExecutionCapability::unsupported( + "execution_secp256k1_operation_unsupported", + format!("secp256k1 operation {operation_code} is not implemented"), + ), + }; + } + if program_id.0.as_str() == kb_program_ids::SECP256R1_PROGRAM_ID { + return match operation_code { + crate::SECP256R1_VERIFY_INLINE_OPERATION + | crate::SECP256R1_VERIFY_OFFSETS_OPERATION => { + kb_execution_api::ExecutionCapability::supported(operation_code) + }, + _ => kb_execution_api::ExecutionCapability::unsupported( + "execution_secp256r1_operation_unsupported", + format!("secp256r1 operation {operation_code} is not implemented"), + ), + }; + } + if program_id.0.as_str() == kb_program_ids::STAKE_PROGRAM_ID { + return match operation_code { + crate::STAKE_INITIALIZE_OPERATION + | crate::STAKE_INITIALIZE_CHECKED_OPERATION + | crate::STAKE_CREATE_ACCOUNT_OPERATION + | crate::STAKE_CREATE_ACCOUNT_WITH_SEED_OPERATION + | crate::STAKE_CREATE_ACCOUNT_CHECKED_OPERATION + | crate::STAKE_CREATE_ACCOUNT_WITH_SEED_CHECKED_OPERATION + | crate::STAKE_SPLIT_OPERATION + | crate::STAKE_SPLIT_WITH_SEED_OPERATION + | crate::STAKE_MERGE_OPERATION + | crate::STAKE_CREATE_ACCOUNT_AND_DELEGATE_OPERATION + | crate::STAKE_CREATE_ACCOUNT_WITH_SEED_AND_DELEGATE_OPERATION + | crate::STAKE_AUTHORIZE_OPERATION + | crate::STAKE_AUTHORIZE_CHECKED_OPERATION + | crate::STAKE_AUTHORIZE_WITH_SEED_OPERATION + | crate::STAKE_AUTHORIZE_CHECKED_WITH_SEED_OPERATION + | crate::STAKE_DELEGATE_OPERATION + | crate::STAKE_WITHDRAW_OPERATION + | crate::STAKE_DEACTIVATE_OPERATION + | crate::STAKE_SET_LOCKUP_OPERATION + | crate::STAKE_SET_LOCKUP_CHECKED_OPERATION + | crate::STAKE_GET_MINIMUM_DELEGATION_OPERATION + | crate::STAKE_DEACTIVATE_DELINQUENT_OPERATION + | crate::STAKE_MOVE_STAKE_OPERATION + | crate::STAKE_MOVE_LAMPORTS_OPERATION => { + kb_execution_api::ExecutionCapability::supported(operation_code) + }, + _ => kb_execution_api::ExecutionCapability::unsupported( + "execution_stake_operation_unsupported", + format!("Stake operation {operation_code} is not implemented"), + ), + }; + } + if program_id.0.as_str() == kb_program_ids::VOTE_PROGRAM_ID { + return match operation_code { + crate::VOTE_INITIALIZE_ACCOUNT_OPERATION + | crate::VOTE_INITIALIZE_ACCOUNT_V2_OPERATION + | crate::VOTE_CREATE_ACCOUNT_OPERATION + | crate::VOTE_CREATE_ACCOUNT_WITH_SEED_OPERATION + | crate::VOTE_CREATE_ACCOUNT_V2_OPERATION + | crate::VOTE_CREATE_ACCOUNT_WITH_SEED_V2_OPERATION + | crate::VOTE_AUTHORIZE_OPERATION + | crate::VOTE_AUTHORIZE_CHECKED_OPERATION + | crate::VOTE_AUTHORIZE_WITH_SEED_OPERATION + | crate::VOTE_AUTHORIZE_CHECKED_WITH_SEED_OPERATION + | crate::VOTE_SUBMIT_OPERATION + | crate::VOTE_SWITCH_OPERATION + | crate::VOTE_WITHDRAW_OPERATION + | crate::VOTE_UPDATE_VALIDATOR_IDENTITY_OPERATION + | crate::VOTE_UPDATE_COMMISSION_OPERATION + | crate::VOTE_UPDATE_VOTE_STATE_OPERATION + | crate::VOTE_UPDATE_VOTE_STATE_SWITCH_OPERATION + | crate::VOTE_COMPACT_UPDATE_VOTE_STATE_OPERATION + | crate::VOTE_COMPACT_UPDATE_VOTE_STATE_SWITCH_OPERATION + | crate::VOTE_TOWER_SYNC_OPERATION + | crate::VOTE_TOWER_SYNC_SWITCH_OPERATION + | crate::VOTE_UPDATE_COMMISSION_COLLECTOR_OPERATION + | crate::VOTE_UPDATE_COMMISSION_BPS_OPERATION + | crate::VOTE_DEPOSIT_DELEGATOR_REWARDS_OPERATION => { + kb_execution_api::ExecutionCapability::supported(operation_code) + }, + _ => kb_execution_api::ExecutionCapability::unsupported( + "execution_vote_operation_unsupported", + format!("Vote operation {operation_code} is not implemented"), + ), + }; + } + if program_id.0.as_str() == kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID { + return match operation_code { + crate::LOADER_V3_CREATE_BUFFER_OPERATION + | crate::LOADER_V3_WRITE_OPERATION + | crate::LOADER_V3_DEPLOY_WITH_MAX_PROGRAM_LEN_OPERATION + | crate::LOADER_V3_UPGRADE_OPERATION + | crate::LOADER_V3_SET_BUFFER_AUTHORITY_OPERATION + | crate::LOADER_V3_SET_BUFFER_AUTHORITY_CHECKED_OPERATION + | crate::LOADER_V3_SET_UPGRADE_AUTHORITY_OPERATION + | crate::LOADER_V3_SET_UPGRADE_AUTHORITY_CHECKED_OPERATION + | crate::LOADER_V3_CLOSE_OPERATION + | crate::LOADER_V3_CLOSE_ANY_OPERATION + | crate::LOADER_V3_EXTEND_PROGRAM_OPERATION => { + kb_execution_api::ExecutionCapability::supported(operation_code) + }, + _ => kb_execution_api::ExecutionCapability::unsupported( + "execution_loader_v3_operation_unsupported", + format!("Loader v3 operation {operation_code} is not implemented"), + ), + }; + } + if program_id.0.as_str() == kb_program_ids::BPF_LOADER_DEPRECATED_PROGRAM_ID + || program_id.0.as_str() == kb_program_ids::BPF_LOADER_PROGRAM_ID + { + return kb_execution_api::ExecutionCapability::unsupported( + "execution_legacy_bpf_loader_management_disabled", + "BPF Loader v1/v2 management is historical and disabled on current clusters; the surfaces remain decode-only", + ); + } + if program_id.0.as_str() == kb_program_ids::LOADER_V4_PROGRAM_ID { + return match operation_code { + crate::LOADER_V4_CREATE_BUFFER_OPERATION + | crate::LOADER_V4_SET_PROGRAM_LENGTH_OPERATION + | crate::LOADER_V4_WRITE_OPERATION + | crate::LOADER_V4_COPY_OPERATION + | crate::LOADER_V4_DEPLOY_OPERATION + | crate::LOADER_V4_DEPLOY_FROM_SOURCE_OPERATION + | crate::LOADER_V4_RETRACT_OPERATION + | crate::LOADER_V4_TRANSFER_AUTHORITY_OPERATION + | crate::LOADER_V4_FINALIZE_OPERATION => { + kb_execution_api::ExecutionCapability::supported(operation_code) + }, + _ => kb_execution_api::ExecutionCapability::unsupported( + "execution_loader_v4_operation_unsupported", + format!("Loader v4 operation {operation_code} is not implemented"), + ), + }; + } + if program_id.0.as_str() == kb_program_ids::NATIVE_LOADER_PROGRAM_ID { + return kb_execution_api::ExecutionCapability::unsupported( + "execution_native_loader_validator_only", + "Native Loader deployment is a validator software upgrade path and has no client instruction builder", + ); + } + if program_id.0.as_str() == kb_program_ids::CONFIG_PROGRAM_ID { + return match operation_code { + crate::CONFIG_CREATE_ACCOUNT_OPERATION | crate::CONFIG_STORE_OPERATION => { + kb_execution_api::ExecutionCapability::supported(operation_code) + }, + _ => kb_execution_api::ExecutionCapability::unsupported( + "execution_config_operation_unsupported", + format!("Config operation {operation_code} is not implemented"), + ), + }; + } + if program_id.0.as_str() == kb_program_ids::FEATURE_PROGRAM_ID { + return match operation_code { + crate::FEATURE_ACTIVATE_OPERATION + | crate::FEATURE_REVOKE_PENDING_ACTIVATION_OPERATION => { + kb_execution_api::ExecutionCapability::supported(operation_code) + }, + _ => kb_execution_api::ExecutionCapability::unsupported( + "execution_feature_operation_unsupported", + format!("Feature operation {operation_code} is not implemented"), + ), + }; + } + if program_id.0.as_str() == kb_program_ids::SLASHING_PROGRAM_ID { + return match operation_code { + crate::SLASHING_CLOSE_VIOLATION_REPORT_OPERATION + | crate::SLASHING_SUBMIT_DUPLICATE_BLOCK_PROOF_OPERATION => { + kb_execution_api::ExecutionCapability::supported(operation_code) + }, + _ => kb_execution_api::ExecutionCapability::unsupported( + "execution_slashing_operation_unsupported", + format!("Slashing operation {operation_code} is not implemented"), + ), + }; + } + if program_id.0.as_str() == kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID { + return match operation_code { + crate::ZK_ELGAMAL_VERIFY_INLINE_OPERATION + | crate::ZK_ELGAMAL_VERIFY_FROM_ACCOUNT_OPERATION + | crate::ZK_ELGAMAL_CLOSE_CONTEXT_STATE_OPERATION => { + kb_execution_api::ExecutionCapability::supported(operation_code) + }, + _ => kb_execution_api::ExecutionCapability::unsupported( + "execution_zk_elgamal_operation_unsupported", + format!("ZK ElGamal operation {operation_code} is not implemented"), + ), + }; + } + if program_id.0.as_str() == kb_program_ids::ZK_TOKEN_PROOF_PROGRAM_ID { + return kb_execution_api::ExecutionCapability::unsupported( + "execution_historical_zk_token_proof_decode_only", + "the historical ZK Token Proof program is retained for decoding only and has no current callable runtime surface", + ); + } + if kb_execution_api::InstructionExecutor::handles_program_id(self, program_id) { + return kb_execution_api::ExecutionCapability::unsupported( + "execution_native_program_deferred", + format!( + "program {} is classified but intentionally has no executable operation in 0.4.2", + program_id.0 + ), + ); + } + return kb_execution_api::ExecutionCapability::unsupported( + "execution_program_not_owned", + format!("program {} is not owned by kb_executor_solana_core", program_id.0), + ); + } +} + +impl kb_execution_api::TypedInstructionExecutor for crate::SolanaCoreExecutor { + type Intent = crate::SolanaCoreExecutionIntent; + + fn capability( + &self, + program_id: &kb_model::ProgramId, + operation_code: &str, + ) -> kb_execution_api::ExecutionCapability { + return self.exact_capability(program_id, operation_code); + } + + fn build_prepared_plan( + &self, + intent: &Self::Intent, + ) -> kb_core::Result { + let program_id = + kb_model::ProgramId(std::string::String::from(intent.operation.program_id())); + return match self.exact_capability(&program_id, intent.operation.operation_code()) { + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => { + crate::builder_build_prepared_plan(intent) + }, + kb_execution_api::ExecutionCapability::Unsupported { reason_code, reason } => { + std::result::Result::Err(kb_core::Error::new(reason_code, reason)) + }, + }; + } +} + +impl kb_execution_api::InstructionExecutor for crate::SolanaCoreExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_solana_core"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[ + kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID, + kb_program_ids::BPF_LOADER_DEPRECATED_PROGRAM_ID, + kb_program_ids::BPF_LOADER_PROGRAM_ID, + kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID, + kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID, + kb_program_ids::CONFIG_PROGRAM_ID, + kb_program_ids::ED25519_PROGRAM_ID, + kb_program_ids::FEATURE_PROGRAM_ID, + kb_program_ids::LOADER_V4_PROGRAM_ID, + kb_program_ids::NATIVE_LOADER_PROGRAM_ID, + kb_program_ids::SLASHING_PROGRAM_ID, + kb_program_ids::SECP256K1_PROGRAM_ID, + kb_program_ids::SECP256R1_PROGRAM_ID, + kb_program_ids::STAKE_PROGRAM_ID, + kb_program_ids::SYSTEM_PROGRAM_ID, + kb_program_ids::VOTE_PROGRAM_ID, + kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID, + kb_program_ids::ZK_TOKEN_PROOF_PROGRAM_ID, + ]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + return match self.exact_capability(&request.program_id, &request.operation_code) { + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => { + kb_execution_api::ExecutionSupport::Yes + }, + kb_execution_api::ExecutionCapability::Unsupported { reason_code: _, reason: _ } => { + kb_execution_api::ExecutionSupport::No + }, + }; + } + + fn build_plan( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + match self.exact_capability(&request.program_id, &request.operation_code) { + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => {}, + kb_execution_api::ExecutionCapability::Unsupported { reason_code, reason } => { + return std::result::Result::Err(kb_core::Error::new(reason_code, reason)); + }, + } + let intent = + match serde_json::from_str::(&request.payload_json) { + std::result::Result::Ok(intent) => intent, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_solana_core_intent_deserialize_failed", + error.to_string(), + )); + }, + }; + if intent.operation.operation_code() != request.operation_code.as_str() { + return std::result::Result::Err(kb_core::Error::new( + "execution_operation_code_mismatch", + format!( + "request operation {} does not match typed intent operation {}", + request.operation_code, + intent.operation.operation_code() + ), + )); + } + if intent.operation.program_id() != request.program_id.0.as_str() { + return std::result::Result::Err(kb_core::Error::new( + "execution_program_id_mismatch", + format!( + "request program {} does not match typed intent program {}", + request.program_id.0, + intent.operation.program_id() + ), + )); + } + let prepared = + match kb_execution_api::TypedInstructionExecutor::build_prepared_plan(self, &intent) { + std::result::Result::Ok(plan) => plan, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let payload_json = match serde_json::to_value(&prepared) { + std::result::Result::Ok(value) => { + match kb_execution_api::serialize_payload_json(&value) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + }, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_solana_core_plan_serialize_failed", + error.to_string(), + )); + }, + }; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_solana_core"), + instruction_count: prepared.instructions.len(), + payload_json, + }); + } +} + +#[cfg(test)] +mod tests { + #[test] + fn executor_programs_match_native_registry_without_spl_memo() { + let executor = crate::SolanaCoreExecutor; + let executor_ids = kb_execution_api::InstructionExecutor::program_ids(&executor); + let native_ids = kb_program_ids::native_program_ids() + .iter() + .map(|entry| return entry.program_id()) + .collect::>(); + let actual_ids = executor_ids.iter().copied().collect::>(); + assert_eq!(actual_ids, native_ids); + assert!(!actual_ids.contains(kb_program_ids::SPL_MEMO_V1_PROGRAM_ID)); + assert!(!actual_ids.contains(kb_program_ids::SPL_MEMO_V3_PROGRAM_ID)); + assert!(!actual_ids.contains(kb_program_ids::SPL_MEMO_V4_PROGRAM_ID)); + assert!(!actual_ids.contains(kb_program_ids::STAKE_CONFIG_ACCOUNT_ID)); + } + + #[test] + fn capabilities_are_exact_and_never_maybe() { + let executor = crate::SolanaCoreExecutor; + let transfer = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from(kb_program_ids::SYSTEM_PROGRAM_ID)), + crate::SYSTEM_TRANSFER_OPERATION, + ); + assert!(transfer.is_supported()); + let seeded_transfer = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from(kb_program_ids::SYSTEM_PROGRAM_ID)), + crate::SYSTEM_TRANSFER_WITH_SEED_OPERATION, + ); + assert!(seeded_transfer.is_supported()); + let prefund = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from(kb_program_ids::SYSTEM_PROGRAM_ID)), + crate::SYSTEM_CREATE_ACCOUNT_ALLOW_PREFUND_OPERATION, + ); + assert!(prefund.is_supported()); + let nonce = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from(kb_program_ids::SYSTEM_PROGRAM_ID)), + crate::SYSTEM_NONCE_ADVANCE_OPERATION, + ); + assert!(nonce.is_supported()); + let transfer_many = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from(kb_program_ids::SYSTEM_PROGRAM_ID)), + crate::SYSTEM_TRANSFER_MANY_OPERATION, + ); + assert!(transfer_many.is_supported()); + let heap_frame = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from( + kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID, + )), + crate::COMPUTE_BUDGET_REQUEST_HEAP_FRAME_OPERATION, + ); + assert!(heap_frame.is_supported()); + let loaded_accounts_data_size = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from( + kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID, + )), + crate::COMPUTE_BUDGET_SET_LOADED_ACCOUNTS_DATA_SIZE_LIMIT_OPERATION, + ); + assert!(loaded_accounts_data_size.is_supported()); + let lookup_table_create = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from( + kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID, + )), + crate::ADDRESS_LOOKUP_TABLE_CREATE_OPERATION, + ); + assert!(lookup_table_create.is_supported()); + let ed25519 = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from(kb_program_ids::ED25519_PROGRAM_ID)), + crate::ED25519_VERIFY_INLINE_OPERATION, + ); + assert!(ed25519.is_supported()); + let secp256k1 = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from(kb_program_ids::SECP256K1_PROGRAM_ID)), + crate::SECP256K1_VERIFY_OFFSETS_OPERATION, + ); + assert!(secp256k1.is_supported()); + let secp256r1 = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from(kb_program_ids::SECP256R1_PROGRAM_ID)), + crate::SECP256R1_VERIFY_INLINE_OPERATION, + ); + assert!(secp256r1.is_supported()); + let config = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from(kb_program_ids::CONFIG_PROGRAM_ID)), + crate::CONFIG_STORE_OPERATION, + ); + assert!(config.is_supported()); + let feature = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from(kb_program_ids::FEATURE_PROGRAM_ID)), + crate::FEATURE_ACTIVATE_OPERATION, + ); + assert!(feature.is_supported()); + let slashing = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from(kb_program_ids::SLASHING_PROGRAM_ID)), + crate::SLASHING_SUBMIT_DUPLICATE_BLOCK_PROOF_OPERATION, + ); + assert!(slashing.is_supported()); + let zk_elgamal = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from( + kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID, + )), + crate::ZK_ELGAMAL_VERIFY_INLINE_OPERATION, + ); + assert!(zk_elgamal.is_supported()); + let historical_zk = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from( + kb_program_ids::ZK_TOKEN_PROOF_PROGRAM_ID, + )), + "solana_core.zk_token.verify_zero_balance", + ); + assert!(matches!( + historical_zk, + kb_execution_api::ExecutionCapability::Unsupported { reason_code, reason: _ } + if reason_code == "execution_historical_zk_token_proof_decode_only" + )); + let stake = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from(kb_program_ids::STAKE_PROGRAM_ID)), + crate::STAKE_DELEGATE_OPERATION, + ); + assert!(stake.is_supported()); + let vote = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from(kb_program_ids::VOTE_PROGRAM_ID)), + crate::VOTE_TOWER_SYNC_SWITCH_OPERATION, + ); + assert!(vote.is_supported()); + let legacy_supported = kb_execution_api::InstructionExecutor::supports_request( + &executor, + &kb_execution_api::ExecutionRequest { + program_id: kb_model::ProgramId(std::string::String::from( + kb_program_ids::SYSTEM_PROGRAM_ID, + )), + operation_code: std::string::String::from(crate::SYSTEM_TRANSFER_OPERATION), + payload_json: std::string::String::new(), + }, + ); + assert_eq!(legacy_supported, kb_execution_api::ExecutionSupport::Yes); + let legacy_nonce = kb_execution_api::InstructionExecutor::supports_request( + &executor, + &kb_execution_api::ExecutionRequest { + program_id: kb_model::ProgramId(std::string::String::from( + kb_program_ids::SYSTEM_PROGRAM_ID, + )), + operation_code: std::string::String::from(crate::SYSTEM_NONCE_ADVANCE_OPERATION), + payload_json: std::string::String::new(), + }, + ); + assert_eq!(legacy_nonce, kb_execution_api::ExecutionSupport::Yes); + } + + #[test] + fn legacy_request_builds_the_same_typed_plan_payload() { + let payer = kb_model::Pubkey(std::string::String::from(kb_program_ids::VOTE_PROGRAM_ID)); + let intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("legacy-transfer"), + fee_payer: payer.clone(), + policy: kb_execution_api::ExecutionPolicy { + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(10), + max_fee_lamports: std::option::Option::Some(10_000), + max_compute_unit_price_micro_lamports: std::option::Option::Some(1), + }, + authorized_signers: vec![payer.clone()], + ..kb_execution_api::ExecutionPolicy::default() + }, + operation: crate::SolanaCoreOperation::SystemTransfer { + from: payer, + to: kb_model::Pubkey(std::string::String::from(kb_program_ids::STAKE_PROGRAM_ID)), + lamports: 10, + }, + }; + let payload_json = serde_json::to_string(&intent) + .unwrap_or_else(|error| panic!("unexpected serialization error: {error}")); + let plan = kb_execution_api::InstructionExecutor::build_plan( + &crate::SolanaCoreExecutor, + &kb_execution_api::ExecutionRequest { + program_id: kb_model::ProgramId(std::string::String::from( + kb_program_ids::SYSTEM_PROGRAM_ID, + )), + operation_code: std::string::String::from(crate::SYSTEM_TRANSFER_OPERATION), + payload_json, + }, + ) + .unwrap_or_else(|error| panic!("unexpected build error: {error}")); + assert_eq!(plan.instruction_count, 1); + let typed: kb_execution_api::PreparedExecutionPlan = + serde_json::from_str(&plan.payload_json) + .unwrap_or_else(|error| panic!("unexpected plan payload: {error}")); + assert_eq!(typed.intent_id, "legacy-transfer"); + assert_eq!(typed.instructions.len(), 1); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/intent.rs b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/intent.rs new file mode 100644 index 0000000..f4a9a43 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/intent.rs @@ -0,0 +1,2472 @@ +// file: kb_executor_solana_core/src/intent.rs +// version: 13 + +//! Typed Solana core execution intents. + +use ts_rs::TS; // rust-rules: derive-import + +/// Stable operation code for a System Program lamport transfer. +pub const SYSTEM_TRANSFER_OPERATION: &str = "solana_core.system.transfer"; +/// Stable operation code for a bounded batch of System Program transfers. +pub const SYSTEM_TRANSFER_MANY_OPERATION: &str = "solana_core.system.transfer_many"; +/// Stable operation code for controlled System Program account creation. +pub const SYSTEM_CREATE_ACCOUNT_OPERATION: &str = "solana_core.system.create_account"; +/// Stable operation code for System Program account creation with a seed. +pub const SYSTEM_CREATE_ACCOUNT_WITH_SEED_OPERATION: &str = + "solana_core.system.create_account_with_seed"; +/// Stable operation code for System Program account allocation. +pub const SYSTEM_ALLOCATE_OPERATION: &str = "solana_core.system.allocate"; +/// Stable operation code for System Program seeded account allocation. +pub const SYSTEM_ALLOCATE_WITH_SEED_OPERATION: &str = "solana_core.system.allocate_with_seed"; +/// Stable operation code for System Program account owner assignment. +pub const SYSTEM_ASSIGN_OPERATION: &str = "solana_core.system.assign"; +/// Stable operation code for System Program seeded account owner assignment. +pub const SYSTEM_ASSIGN_WITH_SEED_OPERATION: &str = "solana_core.system.assign_with_seed"; +/// Stable operation code for a System Program transfer from a seeded account. +pub const SYSTEM_TRANSFER_WITH_SEED_OPERATION: &str = "solana_core.system.transfer_with_seed"; +/// Stable operation code for System Program account creation that accepts prefunding. +pub const SYSTEM_CREATE_ACCOUNT_ALLOW_PREFUND_OPERATION: &str = + "solana_core.system.create_account_allow_prefund"; +/// Stable operation code for durable nonce account creation. +pub const SYSTEM_NONCE_CREATE_OPERATION: &str = "solana_core.system.nonce.create"; +/// Stable operation code for seeded durable nonce account creation. +pub const SYSTEM_NONCE_CREATE_WITH_SEED_OPERATION: &str = + "solana_core.system.nonce.create_with_seed"; +/// Stable operation code for durable nonce initialization. +pub const SYSTEM_NONCE_INITIALIZE_OPERATION: &str = "solana_core.system.nonce.initialize"; +/// Stable operation code for durable nonce advancement. +pub const SYSTEM_NONCE_ADVANCE_OPERATION: &str = "solana_core.system.nonce.advance"; +/// Stable operation code for durable nonce authority changes. +pub const SYSTEM_NONCE_AUTHORIZE_OPERATION: &str = "solana_core.system.nonce.authorize"; +/// Stable operation code for durable nonce withdrawal. +pub const SYSTEM_NONCE_WITHDRAW_OPERATION: &str = "solana_core.system.nonce.withdraw"; +/// Stable operation code for durable nonce state upgrade. +pub const SYSTEM_NONCE_UPGRADE_OPERATION: &str = "solana_core.system.nonce.upgrade"; +/// Stable operation code for a Compute Budget heap-frame request. +pub const COMPUTE_BUDGET_REQUEST_HEAP_FRAME_OPERATION: &str = + "solana_core.compute_budget.request_heap_frame"; +/// Stable operation code for a Compute Budget unit limit. +pub const COMPUTE_BUDGET_SET_UNIT_LIMIT_OPERATION: &str = + "solana_core.compute_budget.set_compute_unit_limit"; +/// Stable operation code for a Compute Budget unit price. +pub const COMPUTE_BUDGET_SET_UNIT_PRICE_OPERATION: &str = + "solana_core.compute_budget.set_compute_unit_price"; +/// Stable operation code for a Compute Budget loaded-account data-size limit. +pub const COMPUTE_BUDGET_SET_LOADED_ACCOUNTS_DATA_SIZE_LIMIT_OPERATION: &str = + "solana_core.compute_budget.set_loaded_accounts_data_size_limit"; +/// Stable operation code for Address Lookup Table creation. +pub const ADDRESS_LOOKUP_TABLE_CREATE_OPERATION: &str = "solana_core.address_lookup_table.create"; +/// Stable operation code for Address Lookup Table extension. +pub const ADDRESS_LOOKUP_TABLE_EXTEND_OPERATION: &str = "solana_core.address_lookup_table.extend"; +/// Stable operation code for permanently freezing an Address Lookup Table. +pub const ADDRESS_LOOKUP_TABLE_FREEZE_OPERATION: &str = "solana_core.address_lookup_table.freeze"; +/// Stable operation code for Address Lookup Table deactivation. +pub const ADDRESS_LOOKUP_TABLE_DEACTIVATE_OPERATION: &str = + "solana_core.address_lookup_table.deactivate"; +/// Stable operation code for closing an Address Lookup Table. +pub const ADDRESS_LOOKUP_TABLE_CLOSE_OPERATION: &str = "solana_core.address_lookup_table.close"; +/// Stable operation code for one inline Ed25519 verification. +pub const ED25519_VERIFY_INLINE_OPERATION: &str = "solana_core.ed25519.verify_inline"; +/// Stable operation code for an Ed25519 verification table with explicit offsets. +pub const ED25519_VERIFY_OFFSETS_OPERATION: &str = "solana_core.ed25519.verify_offsets"; +/// Stable operation code for one inline secp256k1 verification. +pub const SECP256K1_VERIFY_INLINE_OPERATION: &str = "solana_core.secp256k1.verify_inline"; +/// Stable operation code for a secp256k1 verification table with explicit offsets. +pub const SECP256K1_VERIFY_OFFSETS_OPERATION: &str = "solana_core.secp256k1.verify_offsets"; +/// Stable operation code for one inline secp256r1 verification. +pub const SECP256R1_VERIFY_INLINE_OPERATION: &str = "solana_core.secp256r1.verify_inline"; +/// Stable operation code for a secp256r1 verification table with explicit offsets. +pub const SECP256R1_VERIFY_OFFSETS_OPERATION: &str = "solana_core.secp256r1.verify_offsets"; +/// Stable operation code for initializing a Stake account. +pub const STAKE_INITIALIZE_OPERATION: &str = "solana_core.stake.initialize"; +/// Stable operation code for checked Stake account initialization. +pub const STAKE_INITIALIZE_CHECKED_OPERATION: &str = "solana_core.stake.initialize_checked"; +/// Stable operation code for creating and initializing a Stake account. +pub const STAKE_CREATE_ACCOUNT_OPERATION: &str = "solana_core.stake.create_account"; +/// Stable operation code for seeded Stake account creation. +pub const STAKE_CREATE_ACCOUNT_WITH_SEED_OPERATION: &str = + "solana_core.stake.create_account_with_seed"; +/// Stable operation code for checked Stake account creation. +pub const STAKE_CREATE_ACCOUNT_CHECKED_OPERATION: &str = "solana_core.stake.create_account_checked"; +/// Stable operation code for checked seeded Stake account creation. +pub const STAKE_CREATE_ACCOUNT_WITH_SEED_CHECKED_OPERATION: &str = + "solana_core.stake.create_account_with_seed_checked"; +/// Stable operation code for splitting a Stake account. +pub const STAKE_SPLIT_OPERATION: &str = "solana_core.stake.split"; +/// Stable operation code for splitting into a seeded Stake account. +pub const STAKE_SPLIT_WITH_SEED_OPERATION: &str = "solana_core.stake.split_with_seed"; +/// Stable operation code for merging Stake accounts. +pub const STAKE_MERGE_OPERATION: &str = "solana_core.stake.merge"; +/// Stable operation code for creating and delegating a Stake account. +pub const STAKE_CREATE_ACCOUNT_AND_DELEGATE_OPERATION: &str = + "solana_core.stake.create_account_and_delegate"; +/// Stable operation code for seeded Stake account creation and delegation. +pub const STAKE_CREATE_ACCOUNT_WITH_SEED_AND_DELEGATE_OPERATION: &str = + "solana_core.stake.create_account_with_seed_and_delegate"; +/// Stable operation code for changing a Stake authority. +pub const STAKE_AUTHORIZE_OPERATION: &str = "solana_core.stake.authorize"; +/// Stable operation code for a checked Stake authority change. +pub const STAKE_AUTHORIZE_CHECKED_OPERATION: &str = "solana_core.stake.authorize_checked"; +/// Stable operation code for changing a seeded Stake authority. +pub const STAKE_AUTHORIZE_WITH_SEED_OPERATION: &str = "solana_core.stake.authorize_with_seed"; +/// Stable operation code for a checked seeded Stake authority change. +pub const STAKE_AUTHORIZE_CHECKED_WITH_SEED_OPERATION: &str = + "solana_core.stake.authorize_checked_with_seed"; +/// Stable operation code for delegating Stake. +pub const STAKE_DELEGATE_OPERATION: &str = "solana_core.stake.delegate"; +/// Stable operation code for withdrawing lamports from a Stake account. +pub const STAKE_WITHDRAW_OPERATION: &str = "solana_core.stake.withdraw"; +/// Stable operation code for deactivating Stake. +pub const STAKE_DEACTIVATE_OPERATION: &str = "solana_core.stake.deactivate"; +/// Stable operation code for changing a Stake lockup. +pub const STAKE_SET_LOCKUP_OPERATION: &str = "solana_core.stake.set_lockup"; +/// Stable operation code for a checked Stake lockup change. +pub const STAKE_SET_LOCKUP_CHECKED_OPERATION: &str = "solana_core.stake.set_lockup_checked"; +/// Stable operation code for querying the minimum delegation. +pub const STAKE_GET_MINIMUM_DELEGATION_OPERATION: &str = "solana_core.stake.get_minimum_delegation"; +/// Stable operation code for deactivating delinquent Stake. +pub const STAKE_DEACTIVATE_DELINQUENT_OPERATION: &str = "solana_core.stake.deactivate_delinquent"; +/// Stable operation code for moving delegated Stake. +pub const STAKE_MOVE_STAKE_OPERATION: &str = "solana_core.stake.move_stake"; +/// Stable operation code for moving lamports between Stake accounts. +pub const STAKE_MOVE_LAMPORTS_OPERATION: &str = "solana_core.stake.move_lamports"; +/// Stable operation code for initializing a legacy Vote account. +pub const VOTE_INITIALIZE_ACCOUNT_OPERATION: &str = "solana_core.vote.initialize_account"; +/// Stable operation code for initializing a Vote v2 account. +pub const VOTE_INITIALIZE_ACCOUNT_V2_OPERATION: &str = "solana_core.vote.initialize_account_v2"; +/// Stable operation code for creating and initializing a legacy Vote account. +pub const VOTE_CREATE_ACCOUNT_OPERATION: &str = "solana_core.vote.create_account"; +/// Stable operation code for creating a seeded legacy Vote account. +pub const VOTE_CREATE_ACCOUNT_WITH_SEED_OPERATION: &str = + "solana_core.vote.create_account_with_seed"; +/// Stable operation code for creating and initializing a Vote v2 account. +pub const VOTE_CREATE_ACCOUNT_V2_OPERATION: &str = "solana_core.vote.create_account_v2"; +/// Stable operation code for creating a seeded Vote v2 account. +pub const VOTE_CREATE_ACCOUNT_WITH_SEED_V2_OPERATION: &str = + "solana_core.vote.create_account_with_seed_v2"; +/// Stable operation code for changing a Vote authority. +pub const VOTE_AUTHORIZE_OPERATION: &str = "solana_core.vote.authorize"; +/// Stable operation code for a checked Vote authority change. +pub const VOTE_AUTHORIZE_CHECKED_OPERATION: &str = "solana_core.vote.authorize_checked"; +/// Stable operation code for changing a seed-derived Vote authority. +pub const VOTE_AUTHORIZE_WITH_SEED_OPERATION: &str = "solana_core.vote.authorize_with_seed"; +/// Stable operation code for a checked seed-derived Vote authority change. +pub const VOTE_AUTHORIZE_CHECKED_WITH_SEED_OPERATION: &str = + "solana_core.vote.authorize_checked_with_seed"; +/// Stable operation code for submitting a Vote instruction. +pub const VOTE_SUBMIT_OPERATION: &str = "solana_core.vote.vote"; +/// Stable operation code for submitting a switch-proof Vote instruction. +pub const VOTE_SWITCH_OPERATION: &str = "solana_core.vote.vote_switch"; +/// Stable operation code for withdrawing lamports from a Vote account. +pub const VOTE_WITHDRAW_OPERATION: &str = "solana_core.vote.withdraw"; +/// Stable operation code for changing a validator identity. +pub const VOTE_UPDATE_VALIDATOR_IDENTITY_OPERATION: &str = + "solana_core.vote.update_validator_identity"; +/// Stable operation code for changing a legacy Vote commission percentage. +pub const VOTE_UPDATE_COMMISSION_OPERATION: &str = "solana_core.vote.update_commission"; +/// Stable operation code for replacing Vote state. +pub const VOTE_UPDATE_VOTE_STATE_OPERATION: &str = "solana_core.vote.update_vote_state"; +/// Stable operation code for replacing Vote state with a switch proof. +pub const VOTE_UPDATE_VOTE_STATE_SWITCH_OPERATION: &str = + "solana_core.vote.update_vote_state_switch"; +/// Stable operation code for compact Vote-state replacement. +pub const VOTE_COMPACT_UPDATE_VOTE_STATE_OPERATION: &str = + "solana_core.vote.compact_update_vote_state"; +/// Stable operation code for compact Vote-state replacement with a switch proof. +pub const VOTE_COMPACT_UPDATE_VOTE_STATE_SWITCH_OPERATION: &str = + "solana_core.vote.compact_update_vote_state_switch"; +/// Stable operation code for synchronizing a Vote tower. +pub const VOTE_TOWER_SYNC_OPERATION: &str = "solana_core.vote.tower_sync"; +/// Stable operation code for synchronizing a Vote tower with a switch proof. +pub const VOTE_TOWER_SYNC_SWITCH_OPERATION: &str = "solana_core.vote.tower_sync_switch"; +/// Stable operation code for changing a Vote v2 commission collector. +pub const VOTE_UPDATE_COMMISSION_COLLECTOR_OPERATION: &str = + "solana_core.vote.update_commission_collector"; +/// Stable operation code for changing a Vote v2 commission in basis points. +pub const VOTE_UPDATE_COMMISSION_BPS_OPERATION: &str = "solana_core.vote.update_commission_bps"; +/// Stable operation code for depositing delegator rewards into a Vote account. +pub const VOTE_DEPOSIT_DELEGATOR_REWARDS_OPERATION: &str = + "solana_core.vote.deposit_delegator_rewards"; +/// Stable operation code for atomically creating and initializing a Loader v3 buffer. +pub const LOADER_V3_CREATE_BUFFER_OPERATION: &str = "solana_core.loader_v3.create_buffer"; +/// Stable operation code for writing one bounded chunk into a Loader v3 buffer. +pub const LOADER_V3_WRITE_OPERATION: &str = "solana_core.loader_v3.write"; +/// Stable operation code for atomically creating and deploying a Loader v3 program. +pub const LOADER_V3_DEPLOY_WITH_MAX_PROGRAM_LEN_OPERATION: &str = + "solana_core.loader_v3.deploy_with_max_program_len"; +/// Stable operation code for upgrading a Loader v3 program from a buffer. +pub const LOADER_V3_UPGRADE_OPERATION: &str = "solana_core.loader_v3.upgrade"; +/// Stable operation code for changing a Loader v3 buffer authority. +pub const LOADER_V3_SET_BUFFER_AUTHORITY_OPERATION: &str = + "solana_core.loader_v3.set_buffer_authority"; +/// Stable operation code for a checked Loader v3 buffer authority change. +pub const LOADER_V3_SET_BUFFER_AUTHORITY_CHECKED_OPERATION: &str = + "solana_core.loader_v3.set_buffer_authority_checked"; +/// Stable operation code for changing or revoking a Loader v3 program authority. +pub const LOADER_V3_SET_UPGRADE_AUTHORITY_OPERATION: &str = + "solana_core.loader_v3.set_upgrade_authority"; +/// Stable operation code for a checked Loader v3 program authority change. +pub const LOADER_V3_SET_UPGRADE_AUTHORITY_CHECKED_OPERATION: &str = + "solana_core.loader_v3.set_upgrade_authority_checked"; +/// Stable operation code for closing an initialized Loader v3 buffer. +pub const LOADER_V3_CLOSE_OPERATION: &str = "solana_core.loader_v3.close"; +/// Stable operation code for closing any supported Loader v3 account shape. +pub const LOADER_V3_CLOSE_ANY_OPERATION: &str = "solana_core.loader_v3.close_any"; +/// Stable operation code for extending a Loader v3 ProgramData allocation. +pub const LOADER_V3_EXTEND_PROGRAM_OPERATION: &str = "solana_core.loader_v3.extend_program"; +/// Stable operation code for atomically creating and initializing a Loader v4 buffer. +pub const LOADER_V4_CREATE_BUFFER_OPERATION: &str = "solana_core.loader_v4.create_buffer"; +/// Stable operation code for changing a Loader v4 program allocation. +pub const LOADER_V4_SET_PROGRAM_LENGTH_OPERATION: &str = "solana_core.loader_v4.set_program_length"; +/// Stable operation code for writing one bounded chunk into a Loader v4 program account. +pub const LOADER_V4_WRITE_OPERATION: &str = "solana_core.loader_v4.write"; +/// Stable operation code for copying bytes between Loader v4 program accounts. +pub const LOADER_V4_COPY_OPERATION: &str = "solana_core.loader_v4.copy"; +/// Stable operation code for deploying a Loader v4 program in place. +pub const LOADER_V4_DEPLOY_OPERATION: &str = "solana_core.loader_v4.deploy"; +/// Stable operation code for deploying a Loader v4 program from a source buffer. +pub const LOADER_V4_DEPLOY_FROM_SOURCE_OPERATION: &str = "solana_core.loader_v4.deploy_from_source"; +/// Stable operation code for retracting a Loader v4 program into maintenance. +pub const LOADER_V4_RETRACT_OPERATION: &str = "solana_core.loader_v4.retract"; +/// Stable operation code for transferring Loader v4 program authority. +pub const LOADER_V4_TRANSFER_AUTHORITY_OPERATION: &str = "solana_core.loader_v4.transfer_authority"; +/// Stable operation code for finalizing a Loader v4 program. +pub const LOADER_V4_FINALIZE_OPERATION: &str = "solana_core.loader_v4.finalize"; +/// Stable operation code for creating and initializing a Config Program account. +pub const CONFIG_CREATE_ACCOUNT_OPERATION: &str = "solana_core.config.create_account"; +/// Stable operation code for storing opaque serialized data in a Config Program account. +pub const CONFIG_STORE_OPERATION: &str = "solana_core.config.store"; +/// Stable operation code for activating a native feature account. +pub const FEATURE_ACTIVATE_OPERATION: &str = "solana_core.feature.activate"; +/// Stable operation code for revoking a pending native feature activation. +pub const FEATURE_REVOKE_PENDING_ACTIVATION_OPERATION: &str = + "solana_core.feature.revoke_pending_activation"; +/// Stable operation code for closing a retained Slashing violation report. +pub const SLASHING_CLOSE_VIOLATION_REPORT_OPERATION: &str = + "solana_core.slashing.close_violation_report"; +/// Stable operation code for the atomic duplicate-block proof submission plan. +pub const SLASHING_SUBMIT_DUPLICATE_BLOCK_PROOF_OPERATION: &str = + "solana_core.slashing.submit_duplicate_block_proof"; +/// Stable operation code for an inline ZK ElGamal proof verification. +pub const ZK_ELGAMAL_VERIFY_INLINE_OPERATION: &str = "solana_core.zk_elgamal.verify_inline"; +/// Stable operation code for a ZK ElGamal proof read from an account. +pub const ZK_ELGAMAL_VERIFY_FROM_ACCOUNT_OPERATION: &str = + "solana_core.zk_elgamal.verify_from_account"; +/// Stable operation code for closing a ZK ElGamal context-state account. +pub const ZK_ELGAMAL_CLOSE_CONTEXT_STATE_OPERATION: &str = + "solana_core.zk_elgamal.close_context_state"; + +/// Every stable callable operation code exposed by the Solana core executor. +pub const SOLANA_CORE_OPERATION_CODES: [&str; 109] = [ + crate::SYSTEM_TRANSFER_OPERATION, + crate::SYSTEM_TRANSFER_MANY_OPERATION, + crate::SYSTEM_CREATE_ACCOUNT_OPERATION, + crate::SYSTEM_CREATE_ACCOUNT_WITH_SEED_OPERATION, + crate::SYSTEM_ALLOCATE_OPERATION, + crate::SYSTEM_ALLOCATE_WITH_SEED_OPERATION, + crate::SYSTEM_ASSIGN_OPERATION, + crate::SYSTEM_ASSIGN_WITH_SEED_OPERATION, + crate::SYSTEM_TRANSFER_WITH_SEED_OPERATION, + crate::SYSTEM_CREATE_ACCOUNT_ALLOW_PREFUND_OPERATION, + crate::SYSTEM_NONCE_CREATE_OPERATION, + crate::SYSTEM_NONCE_CREATE_WITH_SEED_OPERATION, + crate::SYSTEM_NONCE_INITIALIZE_OPERATION, + crate::SYSTEM_NONCE_ADVANCE_OPERATION, + crate::SYSTEM_NONCE_AUTHORIZE_OPERATION, + crate::SYSTEM_NONCE_WITHDRAW_OPERATION, + crate::SYSTEM_NONCE_UPGRADE_OPERATION, + crate::COMPUTE_BUDGET_REQUEST_HEAP_FRAME_OPERATION, + crate::COMPUTE_BUDGET_SET_UNIT_LIMIT_OPERATION, + crate::COMPUTE_BUDGET_SET_UNIT_PRICE_OPERATION, + crate::COMPUTE_BUDGET_SET_LOADED_ACCOUNTS_DATA_SIZE_LIMIT_OPERATION, + crate::ADDRESS_LOOKUP_TABLE_CREATE_OPERATION, + crate::ADDRESS_LOOKUP_TABLE_EXTEND_OPERATION, + crate::ADDRESS_LOOKUP_TABLE_FREEZE_OPERATION, + crate::ADDRESS_LOOKUP_TABLE_DEACTIVATE_OPERATION, + crate::ADDRESS_LOOKUP_TABLE_CLOSE_OPERATION, + crate::ED25519_VERIFY_INLINE_OPERATION, + crate::ED25519_VERIFY_OFFSETS_OPERATION, + crate::SECP256K1_VERIFY_INLINE_OPERATION, + crate::SECP256K1_VERIFY_OFFSETS_OPERATION, + crate::SECP256R1_VERIFY_INLINE_OPERATION, + crate::SECP256R1_VERIFY_OFFSETS_OPERATION, + crate::STAKE_INITIALIZE_OPERATION, + crate::STAKE_INITIALIZE_CHECKED_OPERATION, + crate::STAKE_CREATE_ACCOUNT_OPERATION, + crate::STAKE_CREATE_ACCOUNT_WITH_SEED_OPERATION, + crate::STAKE_CREATE_ACCOUNT_CHECKED_OPERATION, + crate::STAKE_CREATE_ACCOUNT_WITH_SEED_CHECKED_OPERATION, + crate::STAKE_SPLIT_OPERATION, + crate::STAKE_SPLIT_WITH_SEED_OPERATION, + crate::STAKE_MERGE_OPERATION, + crate::STAKE_CREATE_ACCOUNT_AND_DELEGATE_OPERATION, + crate::STAKE_CREATE_ACCOUNT_WITH_SEED_AND_DELEGATE_OPERATION, + crate::STAKE_AUTHORIZE_OPERATION, + crate::STAKE_AUTHORIZE_CHECKED_OPERATION, + crate::STAKE_AUTHORIZE_WITH_SEED_OPERATION, + crate::STAKE_AUTHORIZE_CHECKED_WITH_SEED_OPERATION, + crate::STAKE_DELEGATE_OPERATION, + crate::STAKE_WITHDRAW_OPERATION, + crate::STAKE_DEACTIVATE_OPERATION, + crate::STAKE_SET_LOCKUP_OPERATION, + crate::STAKE_SET_LOCKUP_CHECKED_OPERATION, + crate::STAKE_GET_MINIMUM_DELEGATION_OPERATION, + crate::STAKE_DEACTIVATE_DELINQUENT_OPERATION, + crate::STAKE_MOVE_STAKE_OPERATION, + crate::STAKE_MOVE_LAMPORTS_OPERATION, + crate::VOTE_INITIALIZE_ACCOUNT_OPERATION, + crate::VOTE_INITIALIZE_ACCOUNT_V2_OPERATION, + crate::VOTE_CREATE_ACCOUNT_OPERATION, + crate::VOTE_CREATE_ACCOUNT_WITH_SEED_OPERATION, + crate::VOTE_CREATE_ACCOUNT_V2_OPERATION, + crate::VOTE_CREATE_ACCOUNT_WITH_SEED_V2_OPERATION, + crate::VOTE_AUTHORIZE_OPERATION, + crate::VOTE_AUTHORIZE_CHECKED_OPERATION, + crate::VOTE_AUTHORIZE_WITH_SEED_OPERATION, + crate::VOTE_AUTHORIZE_CHECKED_WITH_SEED_OPERATION, + crate::VOTE_SUBMIT_OPERATION, + crate::VOTE_SWITCH_OPERATION, + crate::VOTE_WITHDRAW_OPERATION, + crate::VOTE_UPDATE_VALIDATOR_IDENTITY_OPERATION, + crate::VOTE_UPDATE_COMMISSION_OPERATION, + crate::VOTE_UPDATE_VOTE_STATE_OPERATION, + crate::VOTE_UPDATE_VOTE_STATE_SWITCH_OPERATION, + crate::VOTE_COMPACT_UPDATE_VOTE_STATE_OPERATION, + crate::VOTE_COMPACT_UPDATE_VOTE_STATE_SWITCH_OPERATION, + crate::VOTE_TOWER_SYNC_OPERATION, + crate::VOTE_TOWER_SYNC_SWITCH_OPERATION, + crate::VOTE_UPDATE_COMMISSION_COLLECTOR_OPERATION, + crate::VOTE_UPDATE_COMMISSION_BPS_OPERATION, + crate::VOTE_DEPOSIT_DELEGATOR_REWARDS_OPERATION, + crate::LOADER_V3_CREATE_BUFFER_OPERATION, + crate::LOADER_V3_WRITE_OPERATION, + crate::LOADER_V3_DEPLOY_WITH_MAX_PROGRAM_LEN_OPERATION, + crate::LOADER_V3_UPGRADE_OPERATION, + crate::LOADER_V3_SET_BUFFER_AUTHORITY_OPERATION, + crate::LOADER_V3_SET_BUFFER_AUTHORITY_CHECKED_OPERATION, + crate::LOADER_V3_SET_UPGRADE_AUTHORITY_OPERATION, + crate::LOADER_V3_SET_UPGRADE_AUTHORITY_CHECKED_OPERATION, + crate::LOADER_V3_CLOSE_OPERATION, + crate::LOADER_V3_CLOSE_ANY_OPERATION, + crate::LOADER_V3_EXTEND_PROGRAM_OPERATION, + crate::LOADER_V4_CREATE_BUFFER_OPERATION, + crate::LOADER_V4_SET_PROGRAM_LENGTH_OPERATION, + crate::LOADER_V4_WRITE_OPERATION, + crate::LOADER_V4_COPY_OPERATION, + crate::LOADER_V4_DEPLOY_OPERATION, + crate::LOADER_V4_DEPLOY_FROM_SOURCE_OPERATION, + crate::LOADER_V4_RETRACT_OPERATION, + crate::LOADER_V4_TRANSFER_AUTHORITY_OPERATION, + crate::LOADER_V4_FINALIZE_OPERATION, + crate::CONFIG_CREATE_ACCOUNT_OPERATION, + crate::CONFIG_STORE_OPERATION, + crate::FEATURE_ACTIVATE_OPERATION, + crate::FEATURE_REVOKE_PENDING_ACTIVATION_OPERATION, + crate::SLASHING_CLOSE_VIOLATION_REPORT_OPERATION, + crate::SLASHING_SUBMIT_DUPLICATE_BLOCK_PROOF_OPERATION, + crate::ZK_ELGAMAL_VERIFY_INLINE_OPERATION, + crate::ZK_ELGAMAL_VERIFY_FROM_ACCOUNT_OPERATION, + crate::ZK_ELGAMAL_CLOSE_CONTEXT_STATE_OPERATION, +]; + +/// One destination in a System Program multi-transfer. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_solana_core/intent/SystemTransferRecipient.ts" +)] +pub struct SystemTransferRecipient { + /// Recipient account. + pub to: kb_model::Pubkey, + /// Lamports transferred to the recipient. + pub lamports: u64, +} + +/// One Ed25519 signature verification offset record. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_solana_core/intent/Ed25519VerificationOffsets.ts" +)] +pub struct Ed25519VerificationOffsets { + /// Offset to the 64-byte signature. + pub signature_offset: u16, + /// Instruction index containing the signature; `u16::MAX` means this instruction. + pub signature_instruction_index: u16, + /// Offset to the 32-byte public key. + pub public_key_offset: u16, + /// Instruction index containing the public key; `u16::MAX` means this instruction. + pub public_key_instruction_index: u16, + /// Offset to the message bytes. + pub message_data_offset: u16, + /// Message size in bytes. + pub message_data_size: u16, + /// Instruction index containing the message; `u16::MAX` means this instruction. + pub message_instruction_index: u16, +} + +/// One secp256k1 signature verification offset record. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_solana_core/intent/Secp256k1VerificationOffsets.ts" +)] +pub struct Secp256k1VerificationOffsets { + /// Offset to the 64-byte signature followed by its one-byte recovery id. + pub signature_offset: u16, + /// Transaction instruction index containing the signature. + pub signature_instruction_index: u8, + /// Offset to the 20-byte Ethereum address. + pub ethereum_address_offset: u16, + /// Transaction instruction index containing the Ethereum address. + pub ethereum_address_instruction_index: u8, + /// Offset to the message bytes. + pub message_data_offset: u16, + /// Message size in bytes. + pub message_data_size: u16, + /// Transaction instruction index containing the message. + pub message_instruction_index: u8, +} + +/// One secp256r1 signature verification offset record. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_solana_core/intent/Secp256r1VerificationOffsets.ts" +)] +pub struct Secp256r1VerificationOffsets { + /// Offset to the compact 64-byte signature. + pub signature_offset: u16, + /// Instruction index containing the signature; `u16::MAX` means this instruction. + pub signature_instruction_index: u16, + /// Offset to the 33-byte compressed public key. + pub public_key_offset: u16, + /// Instruction index containing the public key; `u16::MAX` means this instruction. + pub public_key_instruction_index: u16, + /// Offset to the message bytes. + pub message_data_offset: u16, + /// Message size in bytes. + pub message_data_size: u16, + /// Instruction index containing the message; `u16::MAX` means this instruction. + pub message_instruction_index: u16, +} + +/// Stake authority role changed by an authorize instruction. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_solana_core/intent/StakeAuthorizationKind.ts" +)] +pub enum StakeAuthorizationKind { + /// Staker authority controlling delegation and stake movement. + Staker, + /// Withdrawer authority controlling withdrawals and authority changes. + Withdrawer, +} + +impl crate::StakeAuthorizationKind { + pub(crate) fn as_official(self) -> solana_stake_interface::state::StakeAuthorize { + return match self { + Self::Staker => solana_stake_interface::state::StakeAuthorize::Staker, + Self::Withdrawer => solana_stake_interface::state::StakeAuthorize::Withdrawer, + }; + } +} + +/// Vote authority role changed by an authorize instruction. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(tag = "kind", rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_solana_core/intent/VoteAuthorizationKind.ts" +)] +pub enum VoteAuthorizationKind { + /// Vote authority using an Ed25519 public key. + Voter, + /// Authority permitted to withdraw and administer the Vote account. + Withdrawer, + /// Vote authority using a BLS public key and proof of possession. + VoterWithBls { + /// Compressed BLS public key; exactly 48 bytes. + bls_pubkey: std::vec::Vec, + /// Compressed BLS proof of possession; exactly 96 bytes. + bls_proof_of_possession: std::vec::Vec, + }, +} + +/// Vote v2 commission category. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_solana_core/intent/VoteCommissionKind.ts" +)] +pub enum VoteCommissionKind { + /// Inflation-reward commission. + InflationRewards, + /// Block-revenue commission. + BlockRevenue, +} + +impl crate::VoteCommissionKind { + pub(crate) fn as_official(self) -> solana_vote_interface::instruction::CommissionKind { + return match self { + Self::InflationRewards => { + solana_vote_interface::instruction::CommissionKind::InflationRewards + }, + Self::BlockRevenue => solana_vote_interface::instruction::CommissionKind::BlockRevenue, + }; + } +} + +/// One lockout entry used by Vote-state and tower synchronization instructions. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_solana_core/intent/VoteLockout.ts" +)] +pub struct VoteLockout { + /// Voted slot. + pub slot: u64, + /// Lockout confirmation count, bounded by the official history size. + pub confirmation_count: u32, +} + +/// One key entry serialized in the Config Program `ConfigKeys` short vector. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_solana_core/intent/ConfigKey.ts" +)] +pub struct ConfigKey { + /// Public key stored in the Config Program key list. + pub pubkey: kb_model::Pubkey, + /// Whether the corresponding account must sign a store instruction. + pub is_signer: bool, +} + +/// Optional context-state accounts attached to a ZK ElGamal proof verification. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_solana_core/intent/ZkElGamalContextState.ts" +)] +pub struct ZkElGamalContextState { + /// Preallocated writable context-state account. + pub account: kb_model::Pubkey, + /// Read-only authority recorded as the context-state owner. + pub authority: kb_model::Pubkey, +} + +/// Official proof kinds accepted by the native ZK ElGamal Proof program. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_solana_core/intent/ZkElGamalProofType.ts" +)] +pub enum ZkElGamalProofType { + /// Zero-ciphertext proof. + ZeroCiphertext, + /// Ciphertext-ciphertext equality proof. + CiphertextCiphertextEquality, + /// Ciphertext-commitment equality proof. + CiphertextCommitmentEquality, + /// ElGamal public-key validity proof. + PubkeyValidity, + /// Percentage-with-cap proof. + PercentageWithCap, + /// Batched 64-bit range proof. + BatchedRangeProofU64, + /// Batched 128-bit range proof. + BatchedRangeProofU128, + /// Batched 256-bit range proof. + BatchedRangeProofU256, + /// Grouped ciphertext with two handles validity proof. + GroupedCiphertext2HandlesValidity, + /// Batched grouped ciphertext with two handles validity proof. + BatchedGroupedCiphertext2HandlesValidity, + /// Grouped ciphertext with three handles validity proof. + GroupedCiphertext3HandlesValidity, + /// Batched grouped ciphertext with three handles validity proof. + BatchedGroupedCiphertext3HandlesValidity, +} + +impl crate::ZkElGamalProofType { + /// Returns the official one-byte instruction discriminator. + pub fn discriminator(self) -> u8 { + return match self { + Self::ZeroCiphertext => 1, + Self::CiphertextCiphertextEquality => 2, + Self::CiphertextCommitmentEquality => 3, + Self::PubkeyValidity => 4, + Self::PercentageWithCap => 5, + Self::BatchedRangeProofU64 => 6, + Self::BatchedRangeProofU128 => 7, + Self::BatchedRangeProofU256 => 8, + Self::GroupedCiphertext2HandlesValidity => 9, + Self::BatchedGroupedCiphertext2HandlesValidity => 10, + Self::GroupedCiphertext3HandlesValidity => 11, + Self::BatchedGroupedCiphertext3HandlesValidity => 12, + }; + } + + /// Returns the exact official POD proof-data size for this proof kind. + pub fn proof_data_size(self) -> usize { + return match self { + Self::ZeroCiphertext => std::mem::size_of::(), + Self::CiphertextCiphertextEquality => std::mem::size_of::(), + Self::CiphertextCommitmentEquality => std::mem::size_of::(), + Self::PubkeyValidity => std::mem::size_of::(), + Self::PercentageWithCap => std::mem::size_of::(), + Self::BatchedRangeProofU64 => std::mem::size_of::(), + Self::BatchedRangeProofU128 => std::mem::size_of::(), + Self::BatchedRangeProofU256 => std::mem::size_of::(), + Self::GroupedCiphertext2HandlesValidity => std::mem::size_of::(), + Self::BatchedGroupedCiphertext2HandlesValidity => std::mem::size_of::(), + Self::GroupedCiphertext3HandlesValidity => std::mem::size_of::(), + Self::BatchedGroupedCiphertext3HandlesValidity => std::mem::size_of::(), + }; + } + + /// Returns the exact official context-state account size for this proof kind. + pub fn context_state_size(self) -> usize { + return match self { + Self::ZeroCiphertext => std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::ZeroCiphertextProofContext, + >, + >(), + Self::CiphertextCiphertextEquality => std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::CiphertextCiphertextEqualityProofContext, + >, + >(), + Self::CiphertextCommitmentEquality => std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::CiphertextCommitmentEqualityProofContext, + >, + >(), + Self::PubkeyValidity => std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::PubkeyValidityProofContext, + >, + >(), + Self::PercentageWithCap => std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::PercentageWithCapProofContext, + >, + >(), + Self::BatchedRangeProofU64 + | Self::BatchedRangeProofU128 + | Self::BatchedRangeProofU256 => std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::BatchedRangeProofContext, + >, + >(), + Self::GroupedCiphertext2HandlesValidity => std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::GroupedCiphertext2HandlesValidityProofContext, + >, + >(), + Self::BatchedGroupedCiphertext2HandlesValidity => std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::BatchedGroupedCiphertext2HandlesValidityProofContext, + >, + >(), + Self::GroupedCiphertext3HandlesValidity => std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::GroupedCiphertext3HandlesValidityProofContext, + >, + >(), + Self::BatchedGroupedCiphertext3HandlesValidity => std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::BatchedGroupedCiphertext3HandlesValidityProofContext, + >, + >(), + }; + } +} + +/// Typed Solana core operation arguments. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(tag = "operation", rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_solana_core/intent/SolanaCoreOperation.ts" +)] +pub enum SolanaCoreOperation { + /// Transfer lamports between System Program accounts. + SystemTransfer { + /// Funding account and required signer. + from: kb_model::Pubkey, + /// Recipient account. + to: kb_model::Pubkey, + /// Lamports to transfer. + lamports: u64, + }, + /// Transfer lamports from one System Program account to several recipients. + SystemTransferMany { + /// Funding account and required signer. + from: kb_model::Pubkey, + /// Ordered recipients; each entry becomes one official transfer instruction. + recipients: std::vec::Vec, + }, + /// Transfer lamports from a System Program account derived with a seed. + SystemTransferWithSeed { + /// Seed-derived source account. + from: kb_model::Pubkey, + /// Base account and required signer. + from_base: kb_model::Pubkey, + /// Seed used to derive the source account. + from_seed: std::string::String, + /// Owner used to derive the source account. + from_owner: kb_model::Pubkey, + /// Recipient account. + to: kb_model::Pubkey, + /// Lamports to transfer. + lamports: u64, + }, + /// Create, fund, allocate and assign a new account. + SystemCreateAccount { + /// Funding account and required signer. + from: kb_model::Pubkey, + /// New account and required signer. + new_account: kb_model::Pubkey, + /// Lamports transferred into the new account. + lamports: u64, + /// Data space allocated to the new account. + space: u64, + /// Program that will own the new account. + owner: kb_model::Pubkey, + }, + /// Create, fund, allocate and assign an account derived with a seed. + SystemCreateAccountWithSeed { + /// Funding account and required signer. + from: kb_model::Pubkey, + /// Seed-derived account address. + new_account: kb_model::Pubkey, + /// Base account and required signer when distinct from `from`. + base: kb_model::Pubkey, + /// Seed used to derive the account. + seed: std::string::String, + /// Lamports transferred into the new account. + lamports: u64, + /// Data space allocated to the new account. + space: u64, + /// Program that will own the new account. + owner: kb_model::Pubkey, + }, + /// Create an account while allowing an already prefunded destination. + SystemCreateAccountAllowPrefund { + /// New account and required signer. + new_account: kb_model::Pubkey, + /// Optional payer and signer for additional lamports. + payer: std::option::Option, + /// Additional lamports transferred by `payer`; must be zero without a payer. + lamports: u64, + /// Data space allocated to the account. + space: u64, + /// Program that will own the account. + owner: kb_model::Pubkey, + }, + /// Allocate data space for a System Program owned account. + SystemAllocate { + /// Account to allocate and required signer. + account: kb_model::Pubkey, + /// Data space to allocate. + space: u64, + }, + /// Allocate data space for a System Program account derived with a seed. + SystemAllocateWithSeed { + /// Seed-derived account address. + account: kb_model::Pubkey, + /// Base account and required signer. + base: kb_model::Pubkey, + /// Seed used to derive the account. + seed: std::string::String, + /// Data space to allocate. + space: u64, + /// Owner used to derive the account. + owner: kb_model::Pubkey, + }, + /// Assign a System Program owned account to another program. + SystemAssign { + /// Account to assign and required signer. + account: kb_model::Pubkey, + /// New owner program. + owner: kb_model::Pubkey, + }, + /// Assign a System Program account derived with a seed. + SystemAssignWithSeed { + /// Seed-derived account address. + account: kb_model::Pubkey, + /// Base account and required signer. + base: kb_model::Pubkey, + /// Seed used to derive the account. + seed: std::string::String, + /// New owner program, also used in the seeded derivation contract. + owner: kb_model::Pubkey, + }, + /// Create and initialize a durable nonce account with a new keypair address. + SystemCreateNonceAccount { + /// Funding account and required signer. + from: kb_model::Pubkey, + /// New nonce account and required signer. + nonce_account: kb_model::Pubkey, + /// Authority stored in the initialized nonce state. + authority: kb_model::Pubkey, + /// Rent-exempt lamports transferred into the nonce account. + lamports: u64, + }, + /// Create and initialize a durable nonce account at a seeded address. + SystemCreateNonceAccountWithSeed { + /// Funding account and required signer. + from: kb_model::Pubkey, + /// Seed-derived nonce account address. + nonce_account: kb_model::Pubkey, + /// Base account and required signer when distinct from `from`. + base: kb_model::Pubkey, + /// Seed used to derive the nonce account under the System Program. + seed: std::string::String, + /// Authority stored in the initialized nonce state. + authority: kb_model::Pubkey, + /// Rent-exempt lamports transferred into the nonce account. + lamports: u64, + }, + /// Initialize an existing uninitialized, rent-exempt System nonce account. + SystemInitializeNonceAccount { + /// Writable nonce account. + nonce_account: kb_model::Pubkey, + /// Authority stored in the initialized nonce state. + authority: kb_model::Pubkey, + }, + /// Advance a durable nonce value using its current authority. + SystemAdvanceNonceAccount { + /// Writable nonce account. + nonce_account: kb_model::Pubkey, + /// Current nonce authority and required signer. + authority: kb_model::Pubkey, + }, + /// Change the authority of a durable nonce account. + SystemAuthorizeNonceAccount { + /// Writable nonce account. + nonce_account: kb_model::Pubkey, + /// Current nonce authority and required signer. + authority: kb_model::Pubkey, + /// New nonce authority. + new_authority: kb_model::Pubkey, + }, + /// Withdraw lamports from a durable nonce account. + SystemWithdrawNonceAccount { + /// Writable nonce account. + nonce_account: kb_model::Pubkey, + /// Current nonce authority and required signer. + authority: kb_model::Pubkey, + /// Writable recipient account. + to: kb_model::Pubkey, + /// Lamports to withdraw. + lamports: u64, + }, + /// Upgrade a legacy durable nonce state once and idempotently. + SystemUpgradeNonceAccount { + /// Writable nonce account. + nonce_account: kb_model::Pubkey, + }, + /// Request a transaction-wide heap frame for every invoked program. + ComputeBudgetRequestHeapFrame { + /// Requested heap-frame size in bytes. + bytes: u32, + }, + /// Set the transaction compute-unit limit. + ComputeBudgetSetUnitLimit { + /// Requested compute-unit limit. + units: u32, + }, + /// Set the transaction compute-unit price. + ComputeBudgetSetUnitPrice { + /// Requested price in micro-lamports per compute unit. + micro_lamports: u64, + }, + /// Set the transaction-wide loaded-account data-size limit. + ComputeBudgetSetLoadedAccountsDataSizeLimit { + /// Requested loaded-account data-size limit in bytes. + bytes: u32, + }, + /// Create an Address Lookup Table at the PDA derived from an authority and recent slot. + AddressLookupTableCreate { + /// Authority recorded in the new lookup table metadata. + authority: kb_model::Pubkey, + /// Writable payer and required signer for runtime-computed rent funding. + payer: kb_model::Pubkey, + /// Recent slot used by the official PDA derivation. + recent_slot: u64, + }, + /// Extend an Address Lookup Table with an ordered list of addresses. + AddressLookupTableExtend { + /// Writable lookup table account. + lookup_table: kb_model::Pubkey, + /// Current lookup table authority and required signer. + authority: kb_model::Pubkey, + /// Optional writable payer and signer for a rent top-up. + payer: std::option::Option, + /// Ordered addresses appended to the table. + new_addresses: std::vec::Vec, + }, + /// Permanently freeze an Address Lookup Table. + AddressLookupTableFreeze { + /// Writable lookup table account. + lookup_table: kb_model::Pubkey, + /// Current lookup table authority and required signer. + authority: kb_model::Pubkey, + }, + /// Deactivate an Address Lookup Table before its cooldown and closure. + AddressLookupTableDeactivate { + /// Writable lookup table account. + lookup_table: kb_model::Pubkey, + /// Current lookup table authority and required signer. + authority: kb_model::Pubkey, + }, + /// Close a fully deactivated Address Lookup Table and drain its lamports. + AddressLookupTableClose { + /// Writable lookup table account. + lookup_table: kb_model::Pubkey, + /// Current lookup table authority and required signer. + authority: kb_model::Pubkey, + /// Writable recipient of the closed account lamports. + recipient: kb_model::Pubkey, + }, + /// Verify one Ed25519 signature with all bytes embedded in the precompile instruction. + Ed25519VerifyInline { + /// Original message bytes. + message: std::vec::Vec, + /// Raw 64-byte Ed25519 signature. + signature: std::vec::Vec, + /// Raw 32-byte Ed25519 public key. + public_key: std::vec::Vec, + }, + /// Verify one or more Ed25519 signatures through explicit instruction offsets. + Ed25519VerifyOffsets { + /// Ordered offset records. + offsets: std::vec::Vec, + /// Optional bytes appended after the offset table. + data: std::vec::Vec, + }, + /// Verify one secp256k1 signature with all bytes embedded in instruction zero. + Secp256k1VerifyInline { + /// Original message bytes; the runtime hashes them with Keccak-256. + message: std::vec::Vec, + /// Raw compact 64-byte secp256k1 signature. + signature: std::vec::Vec, + /// Recovery id accepted by the runtime. + recovery_id: u8, + /// Twenty-byte Ethereum address expected from the recovered key. + ethereum_address: std::vec::Vec, + }, + /// Verify one or more secp256k1 signatures through explicit transaction offsets. + Secp256k1VerifyOffsets { + /// Ordered offset records; index zero refers to this standalone precompile plan. + offsets: std::vec::Vec, + /// Optional bytes appended after the offset table. + data: std::vec::Vec, + }, + /// Verify one secp256r1 signature with all bytes embedded in the precompile instruction. + Secp256r1VerifyInline { + /// Original message bytes; the runtime hashes them with SHA-256. + message: std::vec::Vec, + /// Raw compact 64-byte low-S secp256r1 signature. + signature: std::vec::Vec, + /// Compressed 33-byte secp256r1 public key. + public_key: std::vec::Vec, + }, + /// Verify one or more secp256r1 signatures through explicit instruction offsets. + Secp256r1VerifyOffsets { + /// Ordered offset records. + offsets: std::vec::Vec, + /// Optional bytes appended after the offset table. + data: std::vec::Vec, + }, + /// Initialize an existing rent-exempt Stake account. + StakeInitialize { + /// Writable uninitialized Stake account. + stake_account: kb_model::Pubkey, + /// Staker authority recorded in the account. + staker: kb_model::Pubkey, + /// Withdraw authority recorded in the account. + withdrawer: kb_model::Pubkey, + /// Initial lockup Unix timestamp. + lockup_unix_timestamp: i64, + /// Initial lockup epoch. + lockup_epoch: u64, + /// Initial lockup custodian. + lockup_custodian: kb_model::Pubkey, + }, + /// Initialize a Stake account while requiring the withdraw authority signature. + StakeInitializeChecked { + /// Writable uninitialized Stake account. + stake_account: kb_model::Pubkey, + /// Staker authority recorded in the account. + staker: kb_model::Pubkey, + /// Withdraw authority and required signer. + withdrawer: kb_model::Pubkey, + }, + /// Create and initialize a new Stake account. + StakeCreateAccount { + /// Funding account and required signer. + from: kb_model::Pubkey, + /// New Stake account and required signer. + stake_account: kb_model::Pubkey, + /// Staker authority recorded in the account. + staker: kb_model::Pubkey, + /// Withdraw authority recorded in the account. + withdrawer: kb_model::Pubkey, + /// Initial lockup Unix timestamp. + lockup_unix_timestamp: i64, + /// Initial lockup epoch. + lockup_epoch: u64, + /// Initial lockup custodian. + lockup_custodian: kb_model::Pubkey, + /// Exact measured rent-exempt funding amount. + lamports: u64, + }, + /// Create and initialize a Stake account at a seeded address. + StakeCreateAccountWithSeed { + /// Funding account and required signer. + from: kb_model::Pubkey, + /// Seed-derived Stake account. + stake_account: kb_model::Pubkey, + /// Seed base and required signer. + base: kb_model::Pubkey, + /// Seed used for derivation. + seed: std::string::String, + /// Staker authority recorded in the account. + staker: kb_model::Pubkey, + /// Withdraw authority recorded in the account. + withdrawer: kb_model::Pubkey, + /// Initial lockup Unix timestamp. + lockup_unix_timestamp: i64, + /// Initial lockup epoch. + lockup_epoch: u64, + /// Initial lockup custodian. + lockup_custodian: kb_model::Pubkey, + /// Exact measured rent-exempt funding amount. + lamports: u64, + }, + /// Create and checked-initialize a new Stake account. + StakeCreateAccountChecked { + /// Funding account and required signer. + from: kb_model::Pubkey, + /// New Stake account and required signer. + stake_account: kb_model::Pubkey, + /// Staker authority recorded in the account. + staker: kb_model::Pubkey, + /// Withdraw authority and required signer. + withdrawer: kb_model::Pubkey, + /// Exact measured rent-exempt funding amount. + lamports: u64, + }, + /// Create and checked-initialize a seeded Stake account. + StakeCreateAccountWithSeedChecked { + /// Funding account and required signer. + from: kb_model::Pubkey, + /// Seed-derived Stake account. + stake_account: kb_model::Pubkey, + /// Seed base and required signer. + base: kb_model::Pubkey, + /// Seed used for derivation. + seed: std::string::String, + /// Staker authority recorded in the account. + staker: kb_model::Pubkey, + /// Withdraw authority and required signer. + withdrawer: kb_model::Pubkey, + /// Exact measured rent-exempt funding amount. + lamports: u64, + }, + /// Allocate and split lamports into a new Stake account. + StakeSplit { + /// Writable source Stake account. + stake_account: kb_model::Pubkey, + /// Current staker authority and required signer. + stake_authority: kb_model::Pubkey, + /// Lamports assigned to the split account. + lamports: u64, + /// New split Stake account and required signer. + split_stake_account: kb_model::Pubkey, + }, + /// Allocate and split into a seeded Stake account. + StakeSplitWithSeed { + /// Writable source Stake account. + stake_account: kb_model::Pubkey, + /// Current staker authority and required signer. + stake_authority: kb_model::Pubkey, + /// Lamports assigned to the split account. + lamports: u64, + /// Seed-derived split Stake account. + split_stake_account: kb_model::Pubkey, + /// Seed base and required signer. + base: kb_model::Pubkey, + /// Seed used for derivation. + seed: std::string::String, + }, + /// Merge one compatible Stake account into another. + StakeMerge { + /// Writable destination Stake account. + destination_stake_account: kb_model::Pubkey, + /// Writable source Stake account consumed by the merge. + source_stake_account: kb_model::Pubkey, + /// Current staker authority and required signer. + stake_authority: kb_model::Pubkey, + }, + /// Create, initialize and delegate a new Stake account. + StakeCreateAccountAndDelegate { + /// Funding account and required signer. + from: kb_model::Pubkey, + /// New Stake account and required signer. + stake_account: kb_model::Pubkey, + /// Vote account receiving the delegation. + vote_account: kb_model::Pubkey, + /// Staker authority and required signer for delegation. + staker: kb_model::Pubkey, + /// Withdraw authority recorded in the account. + withdrawer: kb_model::Pubkey, + /// Initial lockup Unix timestamp. + lockup_unix_timestamp: i64, + /// Initial lockup epoch. + lockup_epoch: u64, + /// Initial lockup custodian. + lockup_custodian: kb_model::Pubkey, + /// Exact measured funding amount. + lamports: u64, + }, + /// Create, initialize and delegate a seeded Stake account. + StakeCreateAccountWithSeedAndDelegate { + /// Funding account and required signer. + from: kb_model::Pubkey, + /// Seed-derived Stake account. + stake_account: kb_model::Pubkey, + /// Seed base and required signer. + base: kb_model::Pubkey, + /// Seed used for derivation. + seed: std::string::String, + /// Vote account receiving the delegation. + vote_account: kb_model::Pubkey, + /// Staker authority and required signer for delegation. + staker: kb_model::Pubkey, + /// Withdraw authority recorded in the account. + withdrawer: kb_model::Pubkey, + /// Initial lockup Unix timestamp. + lockup_unix_timestamp: i64, + /// Initial lockup epoch. + lockup_epoch: u64, + /// Initial lockup custodian. + lockup_custodian: kb_model::Pubkey, + /// Exact measured funding amount. + lamports: u64, + }, + /// Change a staker or withdraw authority. + StakeAuthorize { + /// Writable Stake account. + stake_account: kb_model::Pubkey, + /// Current authority and required signer. + authority: kb_model::Pubkey, + /// New authority. + new_authority: kb_model::Pubkey, + /// Authority role being changed. + authorization: crate::StakeAuthorizationKind, + /// Optional current lockup custodian and required signer. + lockup_authority: std::option::Option, + }, + /// Change a Stake authority while requiring the new authority signature. + StakeAuthorizeChecked { + /// Writable Stake account. + stake_account: kb_model::Pubkey, + /// Current authority and required signer. + authority: kb_model::Pubkey, + /// New authority and required signer. + new_authority: kb_model::Pubkey, + /// Authority role being changed. + authorization: crate::StakeAuthorizationKind, + /// Optional current lockup custodian and required signer. + lockup_authority: std::option::Option, + }, + /// Change a Stake authority derived from a base, seed and owner. + StakeAuthorizeWithSeed { + /// Writable Stake account. + stake_account: kb_model::Pubkey, + /// Authority base and required signer. + authority_base: kb_model::Pubkey, + /// Authority derivation seed. + authority_seed: std::string::String, + /// Authority derivation owner. + authority_owner: kb_model::Pubkey, + /// New authority. + new_authority: kb_model::Pubkey, + /// Authority role being changed. + authorization: crate::StakeAuthorizationKind, + /// Optional current lockup custodian and required signer. + lockup_authority: std::option::Option, + }, + /// Change a seeded Stake authority while requiring the new authority signature. + StakeAuthorizeCheckedWithSeed { + /// Writable Stake account. + stake_account: kb_model::Pubkey, + /// Authority base and required signer. + authority_base: kb_model::Pubkey, + /// Authority derivation seed. + authority_seed: std::string::String, + /// Authority derivation owner. + authority_owner: kb_model::Pubkey, + /// New authority and required signer. + new_authority: kb_model::Pubkey, + /// Authority role being changed. + authorization: crate::StakeAuthorizationKind, + /// Optional current lockup custodian and required signer. + lockup_authority: std::option::Option, + }, + /// Delegate a Stake account to a Vote account. + StakeDelegate { + /// Writable Stake account. + stake_account: kb_model::Pubkey, + /// Current staker authority and required signer. + stake_authority: kb_model::Pubkey, + /// Vote account receiving the delegation. + vote_account: kb_model::Pubkey, + }, + /// Withdraw lamports from a Stake account. + StakeWithdraw { + /// Writable Stake account. + stake_account: kb_model::Pubkey, + /// Current withdraw authority and required signer. + withdraw_authority: kb_model::Pubkey, + /// Writable recipient. + to: kb_model::Pubkey, + /// Lamports to withdraw. + lamports: u64, + /// Optional lockup custodian and required signer. + lockup_authority: std::option::Option, + }, + /// Deactivate a delegated Stake account. + StakeDeactivate { + /// Writable Stake account. + stake_account: kb_model::Pubkey, + /// Current staker authority and required signer. + stake_authority: kb_model::Pubkey, + }, + /// Change lockup fields using the current custodian. + StakeSetLockup { + /// Writable Stake account. + stake_account: kb_model::Pubkey, + /// Current custodian and required signer. + authority: kb_model::Pubkey, + /// Optional replacement Unix timestamp. + unix_timestamp: std::option::Option, + /// Optional replacement epoch. + epoch: std::option::Option, + /// Optional replacement custodian encoded in the payload. + custodian: std::option::Option, + }, + /// Change lockup fields and require a new custodian signature when supplied. + StakeSetLockupChecked { + /// Writable Stake account. + stake_account: kb_model::Pubkey, + /// Current custodian and required signer. + authority: kb_model::Pubkey, + /// Optional replacement Unix timestamp. + unix_timestamp: std::option::Option, + /// Optional replacement epoch. + epoch: std::option::Option, + /// Optional new custodian and required signer. + new_custodian: std::option::Option, + }, + /// Request the current minimum Stake delegation from return data. + StakeGetMinimumDelegation, + /// Deactivate Stake delegated to a delinquent Vote account. + StakeDeactivateDelinquent { + /// Writable delegated Stake account. + stake_account: kb_model::Pubkey, + /// Delinquent Vote account. + delinquent_vote_account: kb_model::Pubkey, + /// Reference Vote account satisfying the runtime activity rule. + reference_vote_account: kb_model::Pubkey, + }, + /// Move delegated Stake between compatible Stake accounts. + StakeMoveStake { + /// Writable source Stake account. + source_stake_account: kb_model::Pubkey, + /// Writable destination Stake account. + destination_stake_account: kb_model::Pubkey, + /// Current staker authority and required signer. + stake_authority: kb_model::Pubkey, + /// Stake lamports to move. + lamports: u64, + }, + /// Move free lamports between compatible Stake accounts. + StakeMoveLamports { + /// Writable source Stake account. + source_stake_account: kb_model::Pubkey, + /// Writable destination Stake account. + destination_stake_account: kb_model::Pubkey, + /// Current staker authority and required signer. + stake_authority: kb_model::Pubkey, + /// Lamports to move. + lamports: u64, + }, + /// Initialize an already allocated legacy Vote account. + VoteInitializeAccount { + /// Writable uninitialized Vote account. + vote_account: kb_model::Pubkey, + /// Validator identity and required signer. + node_pubkey: kb_model::Pubkey, + /// Initial vote authority. + authorized_voter: kb_model::Pubkey, + /// Initial withdraw authority. + authorized_withdrawer: kb_model::Pubkey, + /// Legacy commission percentage in the inclusive range 0..=100. + commission_percent: u8, + }, + /// Initialize an already allocated Vote v2 account. + VoteInitializeAccountV2 { + /// Writable uninitialized Vote account. + vote_account: kb_model::Pubkey, + /// Validator identity and required signer. + node_pubkey: kb_model::Pubkey, + /// Initial vote authority. + authorized_voter: kb_model::Pubkey, + /// Compressed BLS vote-authority public key. + authorized_voter_bls_pubkey: std::vec::Vec, + /// Compressed BLS proof of possession. + authorized_voter_bls_proof_of_possession: std::vec::Vec, + /// Initial withdraw authority. + authorized_withdrawer: kb_model::Pubkey, + /// Inflation-reward commission in basis points. + inflation_rewards_commission_bps: u16, + /// Block-revenue commission in basis points. + block_revenue_commission_bps: u16, + /// Writable inflation-reward collector. + inflation_rewards_collector: kb_model::Pubkey, + /// Writable block-revenue collector. + block_revenue_collector: kb_model::Pubkey, + }, + /// Create and initialize a legacy Vote account. + VoteCreateAccount { + /// Funding account and required signer. + from: kb_model::Pubkey, + /// New Vote account and required signer. + vote_account: kb_model::Pubkey, + /// Validator identity and required signer. + node_pubkey: kb_model::Pubkey, + /// Initial vote authority. + authorized_voter: kb_model::Pubkey, + /// Initial withdraw authority. + authorized_withdrawer: kb_model::Pubkey, + /// Legacy commission percentage. + commission_percent: u8, + /// Exact measured rent-exempt funding amount. + lamports: u64, + }, + /// Create and initialize a seeded legacy Vote account. + VoteCreateAccountWithSeed { + /// Funding account and required signer. + from: kb_model::Pubkey, + /// Seed-derived Vote account. + vote_account: kb_model::Pubkey, + /// Seed base and required signer when distinct from `from`. + base: kb_model::Pubkey, + /// Vote-account derivation seed. + seed: std::string::String, + /// Validator identity and required signer. + node_pubkey: kb_model::Pubkey, + /// Initial vote authority. + authorized_voter: kb_model::Pubkey, + /// Initial withdraw authority. + authorized_withdrawer: kb_model::Pubkey, + /// Legacy commission percentage. + commission_percent: u8, + /// Exact measured rent-exempt funding amount. + lamports: u64, + }, + /// Create and initialize a Vote v2 account. + VoteCreateAccountV2 { + /// Funding account and required signer. + from: kb_model::Pubkey, + /// New Vote account and required signer. + vote_account: kb_model::Pubkey, + /// Validator identity and required signer. + node_pubkey: kb_model::Pubkey, + /// Initial vote authority. + authorized_voter: kb_model::Pubkey, + /// Compressed BLS vote-authority public key. + authorized_voter_bls_pubkey: std::vec::Vec, + /// Compressed BLS proof of possession. + authorized_voter_bls_proof_of_possession: std::vec::Vec, + /// Initial withdraw authority. + authorized_withdrawer: kb_model::Pubkey, + /// Inflation-reward commission in basis points. + inflation_rewards_commission_bps: u16, + /// Block-revenue commission in basis points. + block_revenue_commission_bps: u16, + /// Writable inflation-reward collector. + inflation_rewards_collector: kb_model::Pubkey, + /// Writable block-revenue collector. + block_revenue_collector: kb_model::Pubkey, + /// Exact measured rent-exempt funding amount. + lamports: u64, + }, + /// Create and initialize a seeded Vote v2 account. + VoteCreateAccountWithSeedV2 { + /// Funding account and required signer. + from: kb_model::Pubkey, + /// Seed-derived Vote account. + vote_account: kb_model::Pubkey, + /// Seed base and required signer when distinct from `from`. + base: kb_model::Pubkey, + /// Vote-account derivation seed. + seed: std::string::String, + /// Validator identity and required signer. + node_pubkey: kb_model::Pubkey, + /// Initial vote authority. + authorized_voter: kb_model::Pubkey, + /// Compressed BLS vote-authority public key. + authorized_voter_bls_pubkey: std::vec::Vec, + /// Compressed BLS proof of possession. + authorized_voter_bls_proof_of_possession: std::vec::Vec, + /// Initial withdraw authority. + authorized_withdrawer: kb_model::Pubkey, + /// Inflation-reward commission in basis points. + inflation_rewards_commission_bps: u16, + /// Block-revenue commission in basis points. + block_revenue_commission_bps: u16, + /// Writable inflation-reward collector. + inflation_rewards_collector: kb_model::Pubkey, + /// Writable block-revenue collector. + block_revenue_collector: kb_model::Pubkey, + /// Exact measured rent-exempt funding amount. + lamports: u64, + }, + /// Change a Vote authority. + VoteAuthorize { + /// Writable Vote account. + vote_account: kb_model::Pubkey, + /// Current authority and required signer. + current_authority: kb_model::Pubkey, + /// New authority recorded in the payload. + new_authority: kb_model::Pubkey, + /// Authority role and optional BLS material. + authorization: crate::VoteAuthorizationKind, + }, + /// Change a Vote authority while requiring the new authority signature. + VoteAuthorizeChecked { + /// Writable Vote account. + vote_account: kb_model::Pubkey, + /// Current authority and required signer. + current_authority: kb_model::Pubkey, + /// New authority and required signer. + new_authority: kb_model::Pubkey, + /// Authority role and optional BLS material. + authorization: crate::VoteAuthorizationKind, + }, + /// Change a Vote authority derived from a base, seed and owner. + VoteAuthorizeWithSeed { + /// Writable Vote account. + vote_account: kb_model::Pubkey, + /// Current authority base and required signer. + current_authority_base: kb_model::Pubkey, + /// Owner used to derive the current authority. + current_authority_derived_key_owner: kb_model::Pubkey, + /// Seed used to derive the current authority. + current_authority_derived_key_seed: std::string::String, + /// New authority recorded in the payload. + new_authority: kb_model::Pubkey, + /// Authority role and optional BLS material. + authorization: crate::VoteAuthorizationKind, + }, + /// Change a seeded Vote authority while requiring the new authority signature. + VoteAuthorizeCheckedWithSeed { + /// Writable Vote account. + vote_account: kb_model::Pubkey, + /// Current authority base and required signer. + current_authority_base: kb_model::Pubkey, + /// Owner used to derive the current authority. + current_authority_derived_key_owner: kb_model::Pubkey, + /// Seed used to derive the current authority. + current_authority_derived_key_seed: std::string::String, + /// New authority and required signer. + new_authority: kb_model::Pubkey, + /// Authority role and optional BLS material. + authorization: crate::VoteAuthorizationKind, + }, + /// Submit recent voted slots. + VoteSubmit { + /// Writable Vote account. + vote_account: kb_model::Pubkey, + /// Vote authority and required signer. + authorized_voter: kb_model::Pubkey, + /// Strictly increasing voted slots. + slots: std::vec::Vec, + /// Bank hash for the last voted slot. + bank_hash: std::string::String, + /// Optional vote timestamp. + timestamp: std::option::Option, + }, + /// Submit recent voted slots with a fork-switch proof. + VoteSwitch { + /// Writable Vote account. + vote_account: kb_model::Pubkey, + /// Vote authority and required signer. + authorized_voter: kb_model::Pubkey, + /// Strictly increasing voted slots. + slots: std::vec::Vec, + /// Bank hash for the last voted slot. + bank_hash: std::string::String, + /// Optional vote timestamp. + timestamp: std::option::Option, + /// Fork-switch proof hash. + switch_proof_hash: std::string::String, + }, + /// Withdraw lamports from a Vote account. + VoteWithdraw { + /// Writable Vote account. + vote_account: kb_model::Pubkey, + /// Withdraw authority and required signer. + withdraw_authority: kb_model::Pubkey, + /// Writable recipient account. + to: kb_model::Pubkey, + /// Lamports to withdraw. + lamports: u64, + }, + /// Change the validator identity recorded by a Vote account. + VoteUpdateValidatorIdentity { + /// Writable Vote account. + vote_account: kb_model::Pubkey, + /// Withdraw authority and required signer. + withdraw_authority: kb_model::Pubkey, + /// New validator identity and required signer. + new_validator_identity: kb_model::Pubkey, + }, + /// Change the legacy commission percentage. + VoteUpdateCommission { + /// Writable Vote account. + vote_account: kb_model::Pubkey, + /// Withdraw authority and required signer. + withdraw_authority: kb_model::Pubkey, + /// Commission percentage in the inclusive range 0..=100. + commission_percent: u8, + }, + /// Replace the proposed Vote state. + VoteUpdateVoteState { + /// Writable Vote account. + vote_account: kb_model::Pubkey, + /// Vote authority and required signer. + authorized_voter: kb_model::Pubkey, + /// Strictly increasing lockout tower. + lockouts: std::vec::Vec, + /// Optional root at or below the first lockout slot. + root: std::option::Option, + /// Bank hash for the last voted slot. + bank_hash: std::string::String, + /// Optional vote timestamp. + timestamp: std::option::Option, + }, + /// Replace the proposed Vote state with a fork-switch proof. + VoteUpdateVoteStateSwitch { + /// Writable Vote account. + vote_account: kb_model::Pubkey, + /// Vote authority and required signer. + authorized_voter: kb_model::Pubkey, + /// Strictly increasing lockout tower. + lockouts: std::vec::Vec, + /// Optional root at or below the first lockout slot. + root: std::option::Option, + /// Bank hash for the last voted slot. + bank_hash: std::string::String, + /// Optional vote timestamp. + timestamp: std::option::Option, + /// Fork-switch proof hash. + switch_proof_hash: std::string::String, + }, + /// Replace Vote state through the compact wire representation. + VoteCompactUpdateVoteState { + /// Writable Vote account. + vote_account: kb_model::Pubkey, + /// Vote authority and required signer. + authorized_voter: kb_model::Pubkey, + /// Strictly increasing lockout tower. + lockouts: std::vec::Vec, + /// Optional root at or below the first lockout slot. + root: std::option::Option, + /// Bank hash for the last voted slot. + bank_hash: std::string::String, + /// Optional vote timestamp. + timestamp: std::option::Option, + }, + /// Replace compact Vote state with a fork-switch proof. + VoteCompactUpdateVoteStateSwitch { + /// Writable Vote account. + vote_account: kb_model::Pubkey, + /// Vote authority and required signer. + authorized_voter: kb_model::Pubkey, + /// Strictly increasing lockout tower. + lockouts: std::vec::Vec, + /// Optional root at or below the first lockout slot. + root: std::option::Option, + /// Bank hash for the last voted slot. + bank_hash: std::string::String, + /// Optional vote timestamp. + timestamp: std::option::Option, + /// Fork-switch proof hash. + switch_proof_hash: std::string::String, + }, + /// Synchronize the complete Vote tower. + VoteTowerSync { + /// Writable Vote account. + vote_account: kb_model::Pubkey, + /// Vote authority and required signer. + authorized_voter: kb_model::Pubkey, + /// Strictly increasing lockout tower. + lockouts: std::vec::Vec, + /// Optional root at or below the first lockout slot. + root: std::option::Option, + /// Bank hash for the last voted slot. + bank_hash: std::string::String, + /// Optional vote timestamp. + timestamp: std::option::Option, + /// Chain block identifier. + block_id: std::string::String, + }, + /// Synchronize the complete Vote tower with a fork-switch proof. + VoteTowerSyncSwitch { + /// Writable Vote account. + vote_account: kb_model::Pubkey, + /// Vote authority and required signer. + authorized_voter: kb_model::Pubkey, + /// Strictly increasing lockout tower. + lockouts: std::vec::Vec, + /// Optional root at or below the first lockout slot. + root: std::option::Option, + /// Bank hash for the last voted slot. + bank_hash: std::string::String, + /// Optional vote timestamp. + timestamp: std::option::Option, + /// Chain block identifier. + block_id: std::string::String, + /// Fork-switch proof hash. + switch_proof_hash: std::string::String, + }, + /// Change one Vote v2 commission collector. + VoteUpdateCommissionCollector { + /// Writable Vote account. + vote_account: kb_model::Pubkey, + /// Withdraw authority and required signer. + withdraw_authority: kb_model::Pubkey, + /// Writable replacement collector. + new_collector: kb_model::Pubkey, + /// Commission category whose collector is changed. + commission_kind: crate::VoteCommissionKind, + }, + /// Change one Vote v2 commission in basis points. + VoteUpdateCommissionBps { + /// Writable Vote account. + vote_account: kb_model::Pubkey, + /// Withdraw authority and required signer. + withdraw_authority: kb_model::Pubkey, + /// Commission category being changed. + commission_kind: crate::VoteCommissionKind, + /// Commission in the inclusive range 0..=10_000 basis points. + commission_bps: u16, + }, + /// Deposit delegator rewards into a Vote account. + VoteDepositDelegatorRewards { + /// Writable Vote account. + vote_account: kb_model::Pubkey, + /// Writable source and required signer. + source: kb_model::Pubkey, + /// Lamports deposited from the source account. + deposit_lamports: u64, + }, + /// Atomically create and initialize a mutable Loader v3 buffer. + LoaderV3CreateBuffer { + /// Funding account and required signer. + payer: kb_model::Pubkey, + /// New buffer account and required signer. + buffer: kb_model::Pubkey, + /// Initial buffer authority. + authority: kb_model::Pubkey, + /// Rent-exempt lamports supplied by the caller. + lamports: u64, + /// Maximum program bytes stored after the Loader v3 buffer metadata. + program_len: u64, + }, + /// Write one bounded chunk into a Loader v3 buffer. + LoaderV3Write { + /// Writable buffer account. + buffer: kb_model::Pubkey, + /// Buffer authority and required signer. + authority: kb_model::Pubkey, + /// Byte offset inside the buffer program-data region. + offset: u32, + /// Program bytes to write. + bytes: std::vec::Vec, + }, + /// Atomically create a Program account and deploy from a Loader v3 buffer. + LoaderV3DeployWithMaxProgramLen { + /// Funding account and required signer. + payer: kb_model::Pubkey, + /// New Program account and required signer. + program: kb_model::Pubkey, + /// Populated writable buffer account. + buffer: kb_model::Pubkey, + /// Upgrade authority and required signer. + upgrade_authority: kb_model::Pubkey, + /// Rent-exempt lamports for the Program account. + program_lamports: u64, + /// Maximum future ProgramData bytecode length. + max_data_len: u64, + /// Whether the runtime should close the buffer after deployment. + close_buffer: bool, + }, + /// Upgrade one Loader v3 program from a populated buffer. + LoaderV3Upgrade { + /// Writable executable Program account. + program: kb_model::Pubkey, + /// Writable source buffer. + buffer: kb_model::Pubkey, + /// Upgrade authority and required signer. + authority: kb_model::Pubkey, + /// Writable account receiving excess buffer lamports. + spill: kb_model::Pubkey, + /// Whether the runtime should close the buffer after upgrade. + close_buffer: bool, + }, + /// Change a Loader v3 buffer authority without requiring the new authority to sign. + LoaderV3SetBufferAuthority { + /// Writable buffer account. + buffer: kb_model::Pubkey, + /// Current authority and required signer. + current_authority: kb_model::Pubkey, + /// Replacement buffer authority. + new_authority: kb_model::Pubkey, + }, + /// Change a Loader v3 buffer authority while requiring the new authority to sign. + LoaderV3SetBufferAuthorityChecked { + /// Writable buffer account. + buffer: kb_model::Pubkey, + /// Current authority and required signer. + current_authority: kb_model::Pubkey, + /// Replacement authority and required signer. + new_authority: kb_model::Pubkey, + }, + /// Change or permanently revoke a Loader v3 program upgrade authority. + LoaderV3SetUpgradeAuthority { + /// Executable Program account whose ProgramData authority is changed. + program: kb_model::Pubkey, + /// Current authority and required signer. + current_authority: kb_model::Pubkey, + /// Replacement authority, or `None` to make the program immutable. + new_authority: std::option::Option, + }, + /// Change a Loader v3 program authority while requiring the new authority to sign. + LoaderV3SetUpgradeAuthorityChecked { + /// Executable Program account whose ProgramData authority is changed. + program: kb_model::Pubkey, + /// Current authority and required signer. + current_authority: kb_model::Pubkey, + /// Replacement authority and required signer. + new_authority: kb_model::Pubkey, + }, + /// Close an initialized Loader v3 buffer. + LoaderV3Close { + /// Writable Loader v3 account to close. + close_account: kb_model::Pubkey, + /// Writable lamport recipient. + recipient: kb_model::Pubkey, + /// Account authority and required signer. + authority: kb_model::Pubkey, + /// Whether to request the current tombstone wire flag. + tombstone: bool, + }, + /// Close a Loader v3 ProgramData, buffer or uninitialized account. + LoaderV3CloseAny { + /// Writable Loader v3 account to close. + close_account: kb_model::Pubkey, + /// Writable lamport recipient. + recipient: kb_model::Pubkey, + /// Optional authority signer required for initialized accounts. + authority: std::option::Option, + /// Optional writable associated Program account for ProgramData closure. + program: std::option::Option, + /// Whether to tombstone the associated program cache entry. + tombstone: bool, + }, + /// Extend one Loader v3 ProgramData allocation. + LoaderV3ExtendProgram { + /// Writable executable Program account. + program: kb_model::Pubkey, + /// Optional writable payer signer when a rent top-up is required. + payer: std::option::Option, + /// Positive number of bytes added to ProgramData. + additional_bytes: u32, + }, + /// Atomically create and initialize one Loader v4 buffer account. + LoaderV4CreateBuffer { + /// Funding account and required signer. + payer: kb_model::Pubkey, + /// New Loader v4 buffer account and required signer. + buffer: kb_model::Pubkey, + /// Program authority and required signer for initial sizing. + authority: kb_model::Pubkey, + /// Writable account receiving superfluous lamports or closure proceeds. + recipient: kb_model::Pubkey, + /// Lamports transferred by the System Program account creation. + lamports: u64, + /// Positive initial byte length assigned by Loader v4. + new_size: u32, + }, + /// Change the allocation of an undeployed Loader v4 program account. + LoaderV4SetProgramLength { + /// Writable Loader v4 program account. + program: kb_model::Pubkey, + /// Program authority and required signer. + authority: kb_model::Pubkey, + /// Writable account receiving superfluous lamports or closure proceeds. + recipient: kb_model::Pubkey, + /// New program length; zero closes and resets the account. + new_size: u32, + }, + /// Write one bounded chunk into an undeployed Loader v4 program account. + LoaderV4Write { + /// Writable Loader v4 program account. + program: kb_model::Pubkey, + /// Program authority and required signer. + authority: kb_model::Pubkey, + /// Byte offset inside the program account. + offset: u32, + /// Program bytes to write. + bytes: std::vec::Vec, + }, + /// Copy one bounded range between Loader v4 program accounts. + LoaderV4Copy { + /// Writable destination Loader v4 program account. + program: kb_model::Pubkey, + /// Destination authority and required signer. + authority: kb_model::Pubkey, + /// Read-only source Loader v4 program account. + source_program: kb_model::Pubkey, + /// Destination byte offset. + destination_offset: u32, + /// Source byte offset. + source_offset: u32, + /// Positive number of bytes copied. + length: u32, + }, + /// Deploy an undeployed Loader v4 program account in place. + LoaderV4Deploy { + /// Writable Loader v4 program account. + program: kb_model::Pubkey, + /// Program authority and required signer. + authority: kb_model::Pubkey, + }, + /// Deploy a Loader v4 program from another undeployed source account. + LoaderV4DeployFromSource { + /// Writable Loader v4 destination program account. + program: kb_model::Pubkey, + /// Destination authority and required signer. + authority: kb_model::Pubkey, + /// Writable source account consumed by deployment. + source_program: kb_model::Pubkey, + }, + /// Retract a deployed Loader v4 program into maintenance. + LoaderV4Retract { + /// Writable Loader v4 program account. + program: kb_model::Pubkey, + /// Program authority and required signer. + authority: kb_model::Pubkey, + }, + /// Transfer Loader v4 program authority. + LoaderV4TransferAuthority { + /// Writable Loader v4 program account. + program: kb_model::Pubkey, + /// Current authority and required signer. + current_authority: kb_model::Pubkey, + /// Replacement authority and required signer. + new_authority: kb_model::Pubkey, + }, + /// Permanently finalize a Loader v4 program account. + LoaderV4Finalize { + /// Writable Loader v4 program account. + program: kb_model::Pubkey, + /// Current authority and required signer. + authority: kb_model::Pubkey, + /// Read-only next-version program; the same program is accepted. + next_version_program: kb_model::Pubkey, + }, + /// Create and initialize a Config Program account with opaque serialized default data. + ConfigCreateAccount { + /// Funding account and required signer. + from: kb_model::Pubkey, + /// New Config Program account and required signer. + config_account: kb_model::Pubkey, + /// Rent-exempt lamports transferred to the new account. + lamports: u64, + /// Maximum serialized application-data bytes reserved after `ConfigKeys`. + max_config_data_space: u64, + /// Maximum key list used only to size the account allocation. + max_keys: std::vec::Vec, + /// Interface-compatible serialized `T::default()` bytes written by initialization. + initial_data: std::vec::Vec, + }, + /// Store opaque serialized application data and a Config Program signer-key list. + ConfigStore { + /// Writable Config Program account. + config_account: kb_model::Pubkey, + /// Whether the Config Program account itself must sign. + config_account_is_signer: bool, + /// Ordered key list serialized with Solana compact-u16 length encoding. + keys: std::vec::Vec, + /// Already serialized application data appended after `ConfigKeys`. + data: std::vec::Vec, + }, + /// Activate a feature account through transfer, allocate and assign instructions. + FeatureActivate { + /// New feature account and required signer. + feature_account: kb_model::Pubkey, + /// Funding account and required signer. + funding_account: kb_model::Pubkey, + /// Rent-exempt lamports measured by the caller. + lamports: u64, + }, + /// Revoke a feature account whose activation is still pending. + FeatureRevokePendingActivation { + /// Writable feature account and required signer. + feature_account: kb_model::Pubkey, + }, + /// Close a Slashing violation report after its runtime retention period. + SlashingCloseViolationReport { + /// Writable violation-report PDA. + report_account: kb_model::Pubkey, + /// Writable destination encoded in the retained report state. + destination: kb_model::Pubkey, + }, + /// Submit an atomic prefunded duplicate-block proof with Ed25519 verification. + SlashingSubmitDuplicateBlockProof { + /// Read-only account containing the duplicate-block proof bytes. + proof_account: kb_model::Pubkey, + /// Expected report PDA derived from node, slot and proof type. + report_account: kb_model::Pubkey, + /// Optional transfer signer; the reporter funds the PDA when absent. + funder: std::option::Option, + /// Exact measured rent-exempt lamports transferred to the report PDA. + report_lamports: u64, + /// Byte offset into the proof account. + proof_offset: u64, + /// Slot containing the duplicate block. + slot: u64, + /// Validator identity whose Ed25519 key signed both roots. + node_pubkey: kb_model::Pubkey, + /// Reporter credited by the runtime. + reporter: kb_model::Pubkey, + /// Destination credited when the report is later closed. + destination: kb_model::Pubkey, + /// First 32-byte shred Merkle root. + shred_1_merkle_root: std::vec::Vec, + /// First 64-byte Ed25519 signature. + shred_1_signature: std::vec::Vec, + /// Second 32-byte shred Merkle root. + shred_2_merkle_root: std::vec::Vec, + /// Second 64-byte Ed25519 signature. + shred_2_signature: std::vec::Vec, + }, + /// Verify an inline ZK ElGamal proof with exact official POD size. + ZkElGamalVerifyInline { + /// Proof kind and discriminator. + proof_type: crate::ZkElGamalProofType, + /// Exact official POD proof bytes. + proof_data: std::vec::Vec, + /// Optional preallocated context-state target and owner. + context_state: std::option::Option, + }, + /// Verify ZK ElGamal proof bytes stored in a read-only account. + ZkElGamalVerifyFromAccount { + /// Proof kind and discriminator. + proof_type: crate::ZkElGamalProofType, + /// Read-only account containing the proof POD bytes. + proof_account: kb_model::Pubkey, + /// Byte offset to the proof data inside the account. + proof_offset: u32, + /// Optional preallocated context-state target and owner. + context_state: std::option::Option, + }, + /// Close a ZK ElGamal context-state account and reclaim its lamports. + ZkElGamalCloseContextState { + /// Writable context-state account. + context_state: kb_model::Pubkey, + /// Writable lamport destination. + destination: kb_model::Pubkey, + /// Context-state authority and required signer. + authority: kb_model::Pubkey, + }, +} + +impl crate::SolanaCoreOperation { + /// Returns the stable operation code. + pub fn operation_code(&self) -> &'static str { + return match self { + Self::SystemTransfer { .. } => crate::SYSTEM_TRANSFER_OPERATION, + Self::SystemTransferMany { .. } => crate::SYSTEM_TRANSFER_MANY_OPERATION, + Self::SystemTransferWithSeed { .. } => crate::SYSTEM_TRANSFER_WITH_SEED_OPERATION, + Self::SystemCreateAccount { .. } => crate::SYSTEM_CREATE_ACCOUNT_OPERATION, + Self::SystemCreateAccountWithSeed { .. } => { + crate::SYSTEM_CREATE_ACCOUNT_WITH_SEED_OPERATION + }, + Self::SystemCreateAccountAllowPrefund { .. } => { + crate::SYSTEM_CREATE_ACCOUNT_ALLOW_PREFUND_OPERATION + }, + Self::SystemAllocate { .. } => crate::SYSTEM_ALLOCATE_OPERATION, + Self::SystemAllocateWithSeed { .. } => crate::SYSTEM_ALLOCATE_WITH_SEED_OPERATION, + Self::SystemAssign { .. } => crate::SYSTEM_ASSIGN_OPERATION, + Self::SystemAssignWithSeed { .. } => crate::SYSTEM_ASSIGN_WITH_SEED_OPERATION, + Self::SystemCreateNonceAccount { .. } => crate::SYSTEM_NONCE_CREATE_OPERATION, + Self::SystemCreateNonceAccountWithSeed { .. } => { + crate::SYSTEM_NONCE_CREATE_WITH_SEED_OPERATION + }, + Self::SystemInitializeNonceAccount { .. } => crate::SYSTEM_NONCE_INITIALIZE_OPERATION, + Self::SystemAdvanceNonceAccount { .. } => crate::SYSTEM_NONCE_ADVANCE_OPERATION, + Self::SystemAuthorizeNonceAccount { .. } => crate::SYSTEM_NONCE_AUTHORIZE_OPERATION, + Self::SystemWithdrawNonceAccount { .. } => crate::SYSTEM_NONCE_WITHDRAW_OPERATION, + Self::SystemUpgradeNonceAccount { .. } => crate::SYSTEM_NONCE_UPGRADE_OPERATION, + Self::ComputeBudgetRequestHeapFrame { .. } => { + crate::COMPUTE_BUDGET_REQUEST_HEAP_FRAME_OPERATION + }, + Self::ComputeBudgetSetUnitLimit { .. } => { + crate::COMPUTE_BUDGET_SET_UNIT_LIMIT_OPERATION + }, + Self::ComputeBudgetSetUnitPrice { .. } => { + crate::COMPUTE_BUDGET_SET_UNIT_PRICE_OPERATION + }, + Self::ComputeBudgetSetLoadedAccountsDataSizeLimit { .. } => { + crate::COMPUTE_BUDGET_SET_LOADED_ACCOUNTS_DATA_SIZE_LIMIT_OPERATION + }, + Self::AddressLookupTableCreate { .. } => crate::ADDRESS_LOOKUP_TABLE_CREATE_OPERATION, + Self::AddressLookupTableExtend { .. } => crate::ADDRESS_LOOKUP_TABLE_EXTEND_OPERATION, + Self::AddressLookupTableFreeze { .. } => crate::ADDRESS_LOOKUP_TABLE_FREEZE_OPERATION, + Self::AddressLookupTableDeactivate { .. } => { + crate::ADDRESS_LOOKUP_TABLE_DEACTIVATE_OPERATION + }, + Self::AddressLookupTableClose { .. } => crate::ADDRESS_LOOKUP_TABLE_CLOSE_OPERATION, + Self::Ed25519VerifyInline { .. } => crate::ED25519_VERIFY_INLINE_OPERATION, + Self::Ed25519VerifyOffsets { .. } => crate::ED25519_VERIFY_OFFSETS_OPERATION, + Self::Secp256k1VerifyInline { .. } => crate::SECP256K1_VERIFY_INLINE_OPERATION, + Self::Secp256k1VerifyOffsets { .. } => crate::SECP256K1_VERIFY_OFFSETS_OPERATION, + Self::Secp256r1VerifyInline { .. } => crate::SECP256R1_VERIFY_INLINE_OPERATION, + Self::Secp256r1VerifyOffsets { .. } => crate::SECP256R1_VERIFY_OFFSETS_OPERATION, + Self::StakeInitialize { .. } => crate::STAKE_INITIALIZE_OPERATION, + Self::StakeInitializeChecked { .. } => crate::STAKE_INITIALIZE_CHECKED_OPERATION, + Self::StakeCreateAccount { .. } => crate::STAKE_CREATE_ACCOUNT_OPERATION, + Self::StakeCreateAccountWithSeed { .. } => { + crate::STAKE_CREATE_ACCOUNT_WITH_SEED_OPERATION + }, + Self::StakeCreateAccountChecked { .. } => crate::STAKE_CREATE_ACCOUNT_CHECKED_OPERATION, + Self::StakeCreateAccountWithSeedChecked { .. } => { + crate::STAKE_CREATE_ACCOUNT_WITH_SEED_CHECKED_OPERATION + }, + Self::StakeSplit { .. } => crate::STAKE_SPLIT_OPERATION, + Self::StakeSplitWithSeed { .. } => crate::STAKE_SPLIT_WITH_SEED_OPERATION, + Self::StakeMerge { .. } => crate::STAKE_MERGE_OPERATION, + Self::StakeCreateAccountAndDelegate { .. } => { + crate::STAKE_CREATE_ACCOUNT_AND_DELEGATE_OPERATION + }, + Self::StakeCreateAccountWithSeedAndDelegate { .. } => { + crate::STAKE_CREATE_ACCOUNT_WITH_SEED_AND_DELEGATE_OPERATION + }, + Self::StakeAuthorize { .. } => crate::STAKE_AUTHORIZE_OPERATION, + Self::StakeAuthorizeChecked { .. } => crate::STAKE_AUTHORIZE_CHECKED_OPERATION, + Self::StakeAuthorizeWithSeed { .. } => crate::STAKE_AUTHORIZE_WITH_SEED_OPERATION, + Self::StakeAuthorizeCheckedWithSeed { .. } => { + crate::STAKE_AUTHORIZE_CHECKED_WITH_SEED_OPERATION + }, + Self::StakeDelegate { .. } => crate::STAKE_DELEGATE_OPERATION, + Self::StakeWithdraw { .. } => crate::STAKE_WITHDRAW_OPERATION, + Self::StakeDeactivate { .. } => crate::STAKE_DEACTIVATE_OPERATION, + Self::StakeSetLockup { .. } => crate::STAKE_SET_LOCKUP_OPERATION, + Self::StakeSetLockupChecked { .. } => crate::STAKE_SET_LOCKUP_CHECKED_OPERATION, + Self::StakeGetMinimumDelegation => crate::STAKE_GET_MINIMUM_DELEGATION_OPERATION, + Self::StakeDeactivateDelinquent { .. } => crate::STAKE_DEACTIVATE_DELINQUENT_OPERATION, + Self::StakeMoveStake { .. } => crate::STAKE_MOVE_STAKE_OPERATION, + Self::StakeMoveLamports { .. } => crate::STAKE_MOVE_LAMPORTS_OPERATION, + Self::VoteInitializeAccount { .. } => crate::VOTE_INITIALIZE_ACCOUNT_OPERATION, + Self::VoteInitializeAccountV2 { .. } => crate::VOTE_INITIALIZE_ACCOUNT_V2_OPERATION, + Self::VoteCreateAccount { .. } => crate::VOTE_CREATE_ACCOUNT_OPERATION, + Self::VoteCreateAccountWithSeed { .. } => { + crate::VOTE_CREATE_ACCOUNT_WITH_SEED_OPERATION + }, + Self::VoteCreateAccountV2 { .. } => crate::VOTE_CREATE_ACCOUNT_V2_OPERATION, + Self::VoteCreateAccountWithSeedV2 { .. } => { + crate::VOTE_CREATE_ACCOUNT_WITH_SEED_V2_OPERATION + }, + Self::VoteAuthorize { .. } => crate::VOTE_AUTHORIZE_OPERATION, + Self::VoteAuthorizeChecked { .. } => crate::VOTE_AUTHORIZE_CHECKED_OPERATION, + Self::VoteAuthorizeWithSeed { .. } => crate::VOTE_AUTHORIZE_WITH_SEED_OPERATION, + Self::VoteAuthorizeCheckedWithSeed { .. } => { + crate::VOTE_AUTHORIZE_CHECKED_WITH_SEED_OPERATION + }, + Self::VoteSubmit { .. } => crate::VOTE_SUBMIT_OPERATION, + Self::VoteSwitch { .. } => crate::VOTE_SWITCH_OPERATION, + Self::VoteWithdraw { .. } => crate::VOTE_WITHDRAW_OPERATION, + Self::VoteUpdateValidatorIdentity { .. } => { + crate::VOTE_UPDATE_VALIDATOR_IDENTITY_OPERATION + }, + Self::VoteUpdateCommission { .. } => crate::VOTE_UPDATE_COMMISSION_OPERATION, + Self::VoteUpdateVoteState { .. } => crate::VOTE_UPDATE_VOTE_STATE_OPERATION, + Self::VoteUpdateVoteStateSwitch { .. } => { + crate::VOTE_UPDATE_VOTE_STATE_SWITCH_OPERATION + }, + Self::VoteCompactUpdateVoteState { .. } => { + crate::VOTE_COMPACT_UPDATE_VOTE_STATE_OPERATION + }, + Self::VoteCompactUpdateVoteStateSwitch { .. } => { + crate::VOTE_COMPACT_UPDATE_VOTE_STATE_SWITCH_OPERATION + }, + Self::VoteTowerSync { .. } => crate::VOTE_TOWER_SYNC_OPERATION, + Self::VoteTowerSyncSwitch { .. } => crate::VOTE_TOWER_SYNC_SWITCH_OPERATION, + Self::VoteUpdateCommissionCollector { .. } => { + crate::VOTE_UPDATE_COMMISSION_COLLECTOR_OPERATION + }, + Self::VoteUpdateCommissionBps { .. } => crate::VOTE_UPDATE_COMMISSION_BPS_OPERATION, + Self::VoteDepositDelegatorRewards { .. } => { + crate::VOTE_DEPOSIT_DELEGATOR_REWARDS_OPERATION + }, + Self::LoaderV3CreateBuffer { .. } => crate::LOADER_V3_CREATE_BUFFER_OPERATION, + Self::LoaderV3Write { .. } => crate::LOADER_V3_WRITE_OPERATION, + Self::LoaderV3DeployWithMaxProgramLen { .. } => { + crate::LOADER_V3_DEPLOY_WITH_MAX_PROGRAM_LEN_OPERATION + }, + Self::LoaderV3Upgrade { .. } => crate::LOADER_V3_UPGRADE_OPERATION, + Self::LoaderV3SetBufferAuthority { .. } => { + crate::LOADER_V3_SET_BUFFER_AUTHORITY_OPERATION + }, + Self::LoaderV3SetBufferAuthorityChecked { .. } => { + crate::LOADER_V3_SET_BUFFER_AUTHORITY_CHECKED_OPERATION + }, + Self::LoaderV3SetUpgradeAuthority { .. } => { + crate::LOADER_V3_SET_UPGRADE_AUTHORITY_OPERATION + }, + Self::LoaderV3SetUpgradeAuthorityChecked { .. } => { + crate::LOADER_V3_SET_UPGRADE_AUTHORITY_CHECKED_OPERATION + }, + Self::LoaderV3Close { .. } => crate::LOADER_V3_CLOSE_OPERATION, + Self::LoaderV3CloseAny { .. } => crate::LOADER_V3_CLOSE_ANY_OPERATION, + Self::LoaderV3ExtendProgram { .. } => crate::LOADER_V3_EXTEND_PROGRAM_OPERATION, + Self::LoaderV4CreateBuffer { .. } => crate::LOADER_V4_CREATE_BUFFER_OPERATION, + Self::LoaderV4SetProgramLength { .. } => crate::LOADER_V4_SET_PROGRAM_LENGTH_OPERATION, + Self::LoaderV4Write { .. } => crate::LOADER_V4_WRITE_OPERATION, + Self::LoaderV4Copy { .. } => crate::LOADER_V4_COPY_OPERATION, + Self::LoaderV4Deploy { .. } => crate::LOADER_V4_DEPLOY_OPERATION, + Self::LoaderV4DeployFromSource { .. } => crate::LOADER_V4_DEPLOY_FROM_SOURCE_OPERATION, + Self::LoaderV4Retract { .. } => crate::LOADER_V4_RETRACT_OPERATION, + Self::LoaderV4TransferAuthority { .. } => crate::LOADER_V4_TRANSFER_AUTHORITY_OPERATION, + Self::LoaderV4Finalize { .. } => crate::LOADER_V4_FINALIZE_OPERATION, + Self::ConfigCreateAccount { .. } => crate::CONFIG_CREATE_ACCOUNT_OPERATION, + Self::ConfigStore { .. } => crate::CONFIG_STORE_OPERATION, + Self::FeatureActivate { .. } => crate::FEATURE_ACTIVATE_OPERATION, + Self::FeatureRevokePendingActivation { .. } => { + crate::FEATURE_REVOKE_PENDING_ACTIVATION_OPERATION + }, + Self::SlashingCloseViolationReport { .. } => { + crate::SLASHING_CLOSE_VIOLATION_REPORT_OPERATION + }, + Self::SlashingSubmitDuplicateBlockProof { .. } => { + crate::SLASHING_SUBMIT_DUPLICATE_BLOCK_PROOF_OPERATION + }, + Self::ZkElGamalVerifyInline { .. } => crate::ZK_ELGAMAL_VERIFY_INLINE_OPERATION, + Self::ZkElGamalVerifyFromAccount { .. } => { + crate::ZK_ELGAMAL_VERIFY_FROM_ACCOUNT_OPERATION + }, + Self::ZkElGamalCloseContextState { .. } => { + crate::ZK_ELGAMAL_CLOSE_CONTEXT_STATE_OPERATION + }, + }; + } + + /// Returns the program id expected for this operation. + pub fn program_id(&self) -> &'static str { + return match self { + Self::SystemTransfer { .. } + | Self::SystemTransferMany { .. } + | Self::SystemTransferWithSeed { .. } + | Self::SystemCreateAccount { .. } + | Self::SystemCreateAccountWithSeed { .. } + | Self::SystemCreateAccountAllowPrefund { .. } + | Self::SystemAllocate { .. } + | Self::SystemAllocateWithSeed { .. } + | Self::SystemAssign { .. } + | Self::SystemAssignWithSeed { .. } + | Self::SystemCreateNonceAccount { .. } + | Self::SystemCreateNonceAccountWithSeed { .. } + | Self::SystemInitializeNonceAccount { .. } + | Self::SystemAdvanceNonceAccount { .. } + | Self::SystemAuthorizeNonceAccount { .. } + | Self::SystemWithdrawNonceAccount { .. } + | Self::SystemUpgradeNonceAccount { .. } => kb_program_ids::SYSTEM_PROGRAM_ID, + Self::ComputeBudgetRequestHeapFrame { .. } + | Self::ComputeBudgetSetUnitLimit { .. } + | Self::ComputeBudgetSetUnitPrice { .. } + | Self::ComputeBudgetSetLoadedAccountsDataSizeLimit { .. } => { + kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID + }, + Self::AddressLookupTableCreate { .. } + | Self::AddressLookupTableExtend { .. } + | Self::AddressLookupTableFreeze { .. } + | Self::AddressLookupTableDeactivate { .. } + | Self::AddressLookupTableClose { .. } => { + kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID + }, + Self::Ed25519VerifyInline { .. } | Self::Ed25519VerifyOffsets { .. } => { + kb_program_ids::ED25519_PROGRAM_ID + }, + Self::Secp256k1VerifyInline { .. } | Self::Secp256k1VerifyOffsets { .. } => { + kb_program_ids::SECP256K1_PROGRAM_ID + }, + Self::Secp256r1VerifyInline { .. } | Self::Secp256r1VerifyOffsets { .. } => { + kb_program_ids::SECP256R1_PROGRAM_ID + }, + Self::StakeInitialize { .. } + | Self::StakeInitializeChecked { .. } + | Self::StakeCreateAccount { .. } + | Self::StakeCreateAccountWithSeed { .. } + | Self::StakeCreateAccountChecked { .. } + | Self::StakeCreateAccountWithSeedChecked { .. } + | Self::StakeSplit { .. } + | Self::StakeSplitWithSeed { .. } + | Self::StakeMerge { .. } + | Self::StakeCreateAccountAndDelegate { .. } + | Self::StakeCreateAccountWithSeedAndDelegate { .. } + | Self::StakeAuthorize { .. } + | Self::StakeAuthorizeChecked { .. } + | Self::StakeAuthorizeWithSeed { .. } + | Self::StakeAuthorizeCheckedWithSeed { .. } + | Self::StakeDelegate { .. } + | Self::StakeWithdraw { .. } + | Self::StakeDeactivate { .. } + | Self::StakeSetLockup { .. } + | Self::StakeSetLockupChecked { .. } + | Self::StakeGetMinimumDelegation + | Self::StakeDeactivateDelinquent { .. } + | Self::StakeMoveStake { .. } + | Self::StakeMoveLamports { .. } => kb_program_ids::STAKE_PROGRAM_ID, + Self::VoteInitializeAccount { .. } + | Self::VoteInitializeAccountV2 { .. } + | Self::VoteCreateAccount { .. } + | Self::VoteCreateAccountWithSeed { .. } + | Self::VoteCreateAccountV2 { .. } + | Self::VoteCreateAccountWithSeedV2 { .. } + | Self::VoteAuthorize { .. } + | Self::VoteAuthorizeChecked { .. } + | Self::VoteAuthorizeWithSeed { .. } + | Self::VoteAuthorizeCheckedWithSeed { .. } + | Self::VoteSubmit { .. } + | Self::VoteSwitch { .. } + | Self::VoteWithdraw { .. } + | Self::VoteUpdateValidatorIdentity { .. } + | Self::VoteUpdateCommission { .. } + | Self::VoteUpdateVoteState { .. } + | Self::VoteUpdateVoteStateSwitch { .. } + | Self::VoteCompactUpdateVoteState { .. } + | Self::VoteCompactUpdateVoteStateSwitch { .. } + | Self::VoteTowerSync { .. } + | Self::VoteTowerSyncSwitch { .. } + | Self::VoteUpdateCommissionCollector { .. } + | Self::VoteUpdateCommissionBps { .. } + | Self::VoteDepositDelegatorRewards { .. } => kb_program_ids::VOTE_PROGRAM_ID, + Self::LoaderV3CreateBuffer { .. } + | Self::LoaderV3Write { .. } + | Self::LoaderV3DeployWithMaxProgramLen { .. } + | Self::LoaderV3Upgrade { .. } + | Self::LoaderV3SetBufferAuthority { .. } + | Self::LoaderV3SetBufferAuthorityChecked { .. } + | Self::LoaderV3SetUpgradeAuthority { .. } + | Self::LoaderV3SetUpgradeAuthorityChecked { .. } + | Self::LoaderV3Close { .. } + | Self::LoaderV3CloseAny { .. } + | Self::LoaderV3ExtendProgram { .. } => { + kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID + }, + Self::LoaderV4CreateBuffer { .. } + | Self::LoaderV4SetProgramLength { .. } + | Self::LoaderV4Write { .. } + | Self::LoaderV4Copy { .. } + | Self::LoaderV4Deploy { .. } + | Self::LoaderV4DeployFromSource { .. } + | Self::LoaderV4Retract { .. } + | Self::LoaderV4TransferAuthority { .. } + | Self::LoaderV4Finalize { .. } => kb_program_ids::LOADER_V4_PROGRAM_ID, + Self::ConfigCreateAccount { .. } | Self::ConfigStore { .. } => { + kb_program_ids::CONFIG_PROGRAM_ID + }, + Self::FeatureActivate { .. } | Self::FeatureRevokePendingActivation { .. } => { + kb_program_ids::FEATURE_PROGRAM_ID + }, + Self::SlashingCloseViolationReport { .. } + | Self::SlashingSubmitDuplicateBlockProof { .. } => kb_program_ids::SLASHING_PROGRAM_ID, + Self::ZkElGamalVerifyInline { .. } + | Self::ZkElGamalVerifyFromAccount { .. } + | Self::ZkElGamalCloseContextState { .. } => { + kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID + }, + }; + } +} + +/// Complete typed intent accepted by the Solana core executor. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_solana_core/intent/SolanaCoreExecutionIntent.ts" +)] +pub struct SolanaCoreExecutionIntent { + /// Stable caller-provided identifier used for logs and replay correlation. + pub intent_id: std::string::String, + /// Transaction fee payer. + pub fee_payer: kb_model::Pubkey, + /// Conservative execution policy. + pub policy: kb_execution_api::ExecutionPolicy, + /// Typed operation and arguments. + pub operation: crate::SolanaCoreOperation, +} + +#[cfg(test)] +mod matrix_tests { + fn required_text<'a>(value: &'a serde_json::Value, field: &str) -> &'a str { + return match value.get(field).and_then(serde_json::Value::as_str) { + std::option::Option::Some(text) if !text.trim().is_empty() => text, + _ => panic!("native execution matrix requires non-empty {field}"), + }; + } + + fn expected_preflight(surface: &str) -> &'static str { + return match surface { + "address_lookup_table" | "config" | "feature" | "slashing" | "zk_elgamal" => "required", + "stake" | "vote" | "loader_v3" | "loader_v4" => "future", + _ => "none", + }; + } + + #[test] + fn native_execution_matrix_matches_compiled_operation_registry() { + let raw = include_str!("../../docs/NATIVE_SOLANA_EXECUTION_MATRIX.json"); + let parsed = match serde_json::from_str::(raw) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("native execution matrix parsing failed: {error}") + }, + }; + assert_eq!( + parsed.get("surface_count").and_then(serde_json::Value::as_u64), + std::option::Option::Some(18) + ); + assert_eq!( + parsed.get("callable_operation_count").and_then(serde_json::Value::as_u64), + std::option::Option::Some(109) + ); + let surfaces = match parsed.get("surfaces").and_then(serde_json::Value::as_array) { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("native execution matrix surfaces missing"), + }; + assert_eq!(surfaces.len(), 18); + + let registered = kb_program_ids::registered_program_ids() + .iter() + .map(kb_program_ids::ProgramIdEntry::program_id) + .collect::>(); + let mut surface_codes = std::collections::BTreeSet::new(); + let mut matrix_program_ids = std::collections::BTreeSet::new(); + let mut operation_codes = std::collections::BTreeSet::new(); + let mut preflight_counts = std::collections::BTreeMap::from([ + ("none", 0_usize), + ("required", 0_usize), + ("future", 0_usize), + ]); + let mut callable_surface_count = 0_usize; + let mut non_invocable_surface_count = 0_usize; + + for surface in surfaces { + let surface_code = required_text(surface, "surface"); + assert!(surface_codes.insert(surface_code)); + let program_ids = match surface.get("program_ids").and_then(serde_json::Value::as_array) + { + std::option::Option::Some(value) if !value.is_empty() => value, + _ => panic!("native execution matrix program_ids missing"), + }; + for program_id in program_ids { + let value = match program_id.as_str() { + std::option::Option::Some(value) if !value.is_empty() => value, + _ => panic!("native execution matrix program id is not text"), + }; + assert!(registered.contains(value)); + assert!(matrix_program_ids.insert(value)); + } + let operations = match surface.get("operations").and_then(serde_json::Value::as_array) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + panic!("native execution matrix operations missing") + }, + }; + let execution_status = required_text(surface, "execution_status"); + if execution_status == "non_invocable" { + non_invocable_surface_count = non_invocable_surface_count.saturating_add(1); + assert!(operations.is_empty()); + required_text(surface, "official_reason"); + continue; + } + assert_eq!(execution_status, "callable"); + callable_surface_count = callable_surface_count.saturating_add(1); + assert!(!operations.is_empty()); + + for operation in operations { + let operation_code = required_text(operation, "operation_code"); + assert!(operation_codes.insert(operation_code)); + let construction_kind = required_text(operation, "construction_kind"); + assert!(matches!( + construction_kind, + "official_builder" + | "official_builder_or_wire" + | "official_layout" + | "official_wire" + )); + let ui_exposure = required_text(operation, "ui_exposure"); + assert!(matches!(ui_exposure, "hidden" | "safe_subset")); + let preflight = required_text(operation, "stateful_preflight"); + assert_eq!(preflight, expected_preflight(surface_code)); + let count = match preflight_counts.get_mut(preflight) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + panic!("native execution matrix preflight class unsupported") + }, + }; + *count = count.saturating_add(1); + for field in + ["construction_source", "policy", "unit_test_contract", "cluster_validation"] + { + required_text(operation, field); + } + } + } + + assert_eq!(callable_surface_count, 14); + assert_eq!(non_invocable_surface_count, 4); + assert_eq!( + preflight_counts, + std::collections::BTreeMap::from([ + ("none", 27_usize), + ("required", 14_usize), + ("future", 68_usize), + ]) + ); + let compiled = crate::SOLANA_CORE_OPERATION_CODES + .iter() + .copied() + .collect::>(); + assert_eq!(compiled.len(), 109); + assert_eq!(operation_codes, compiled); + assert_eq!(matrix_program_ids.len(), 18); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/lib.rs new file mode 100644 index 0000000..61bf758 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/lib.rs @@ -0,0 +1,310 @@ +// file: kb_executor_solana_core/src/lib.rs +// version: 17 + +//! Safe typed executor for supported Solana core operations. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod address_lookup_table; +mod builder; +mod constants; +mod executor; +mod intent; +mod loader_v3; +mod loader_v4; +mod native_admin; +mod precompiles; +mod slashing; +mod stake; +mod validation; +mod vote; +mod zk_elgamal; + +/// Crate-root access to `build_prepared_plan` from `address_lookup_table`. +pub(crate) use crate::address_lookup_table::build_prepared_plan as address_lookup_table_build_prepared_plan; +/// Crate-root access to `build_prepared_plan` from `builder`. +pub(crate) use crate::builder::build_prepared_plan as builder_build_prepared_plan; +/// Crate-root access to `finalize_prepared_plan` from `builder`. +pub(crate) use crate::builder::finalize_prepared_plan; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; +/// Crate-root access to `build_prepared_plan` from `loader_v3`. +pub(crate) use crate::loader_v3::build_prepared_plan as loader_v3_build_prepared_plan; +/// Crate-root access to `build_prepared_plan` from `loader_v4`. +pub(crate) use crate::loader_v4::build_prepared_plan as loader_v4_build_prepared_plan; +/// Crate-root access to `build_prepared_plan` from `native_admin`. +pub(crate) use crate::native_admin::build_prepared_plan as native_admin_build_prepared_plan; +/// Crate-root access to `build_prepared_plan` from `precompiles`. +pub(crate) use crate::precompiles::build_prepared_plan as precompiles_build_prepared_plan; +/// Crate-root access to `build_prepared_plan` from `slashing`. +pub(crate) use crate::slashing::build_prepared_plan as slashing_build_prepared_plan; +/// Crate-root access to `build_prepared_plan` from `stake`. +pub(crate) use crate::stake::build_prepared_plan as stake_build_prepared_plan; +/// Builds one typed validation error without duplicating domain-local wrappers. +pub(crate) use crate::validation::invalid; +/// Parses one canonical native program identifier. +pub(crate) use crate::validation::parse_native_program_id; +/// Parses one model public key with the canonical execution error contract. +pub(crate) use crate::validation::parse_pubkey; +/// Validates that two model public keys identify distinct accounts. +pub(crate) use crate::validation::validate_distinct_pubkeys; +/// Validates an exact byte-slice length while preserving the caller error code. +pub(crate) use crate::validation::validate_exact_length; +/// Validates that an unsigned execution amount is strictly positive. +pub(crate) use crate::validation::validate_positive_u64; +/// Validates one `Pubkey::create_with_seed` derivation with caller-owned error codes. +pub(crate) use crate::validation::validate_seeded_address; +/// Crate-root access to `build_prepared_plan` from `vote`. +pub(crate) use crate::vote::build_prepared_plan as vote_build_prepared_plan; +/// Crate-root access to `build_prepared_plan` from `zk_elgamal`. +pub(crate) use crate::zk_elgamal::build_prepared_plan as zk_elgamal_build_prepared_plan; + +/// Exposes the Solana core executor. +pub use crate::executor::SolanaCoreExecutor; +/// Exposes the Address Lookup Table close operation code. +pub use crate::intent::ADDRESS_LOOKUP_TABLE_CLOSE_OPERATION; +/// Exposes the Address Lookup Table create operation code. +pub use crate::intent::ADDRESS_LOOKUP_TABLE_CREATE_OPERATION; +/// Exposes the Address Lookup Table deactivate operation code. +pub use crate::intent::ADDRESS_LOOKUP_TABLE_DEACTIVATE_OPERATION; +/// Exposes the Address Lookup Table extend operation code. +pub use crate::intent::ADDRESS_LOOKUP_TABLE_EXTEND_OPERATION; +/// Exposes the Address Lookup Table freeze operation code. +pub use crate::intent::ADDRESS_LOOKUP_TABLE_FREEZE_OPERATION; +/// Exposes the Compute Budget heap-frame operation code. +pub use crate::intent::COMPUTE_BUDGET_REQUEST_HEAP_FRAME_OPERATION; +/// Exposes the Compute Budget loaded-account data-size operation code. +pub use crate::intent::COMPUTE_BUDGET_SET_LOADED_ACCOUNTS_DATA_SIZE_LIMIT_OPERATION; +/// Exposes the Compute Budget unit-limit operation code. +pub use crate::intent::COMPUTE_BUDGET_SET_UNIT_LIMIT_OPERATION; +/// Exposes the Compute Budget unit-price operation code. +pub use crate::intent::COMPUTE_BUDGET_SET_UNIT_PRICE_OPERATION; +/// Exposes the Config account creation operation code. +pub use crate::intent::CONFIG_CREATE_ACCOUNT_OPERATION; +/// Exposes the Config store operation code. +pub use crate::intent::CONFIG_STORE_OPERATION; +/// Exposes one Config Program key entry. +pub use crate::intent::ConfigKey; +/// Exposes the Ed25519 inline verification operation code. +pub use crate::intent::ED25519_VERIFY_INLINE_OPERATION; +/// Exposes the Ed25519 offset-table verification operation code. +pub use crate::intent::ED25519_VERIFY_OFFSETS_OPERATION; +/// Exposes one Ed25519 verification offset record. +pub use crate::intent::Ed25519VerificationOffsets; +/// Exposes the feature activation operation code. +pub use crate::intent::FEATURE_ACTIVATE_OPERATION; +/// Exposes the feature pending-activation revoke operation code. +pub use crate::intent::FEATURE_REVOKE_PENDING_ACTIVATION_OPERATION; +/// Exposes Loader v3 operation codes. +pub use crate::intent::LOADER_V3_CLOSE_ANY_OPERATION; +/// Exposes Loader v3 operation codes. +pub use crate::intent::LOADER_V3_CLOSE_OPERATION; +/// Exposes Loader v3 operation codes. +pub use crate::intent::LOADER_V3_CREATE_BUFFER_OPERATION; +/// Exposes Loader v3 operation codes. +pub use crate::intent::LOADER_V3_DEPLOY_WITH_MAX_PROGRAM_LEN_OPERATION; +/// Exposes Loader v3 operation codes. +pub use crate::intent::LOADER_V3_EXTEND_PROGRAM_OPERATION; +/// Exposes Loader v3 operation codes. +pub use crate::intent::LOADER_V3_SET_BUFFER_AUTHORITY_CHECKED_OPERATION; +/// Exposes Loader v3 operation codes. +pub use crate::intent::LOADER_V3_SET_BUFFER_AUTHORITY_OPERATION; +/// Exposes Loader v3 operation codes. +pub use crate::intent::LOADER_V3_SET_UPGRADE_AUTHORITY_CHECKED_OPERATION; +/// Exposes Loader v3 operation codes. +pub use crate::intent::LOADER_V3_SET_UPGRADE_AUTHORITY_OPERATION; +/// Exposes Loader v3 operation codes. +pub use crate::intent::LOADER_V3_UPGRADE_OPERATION; +/// Exposes Loader v3 operation codes. +pub use crate::intent::LOADER_V3_WRITE_OPERATION; +/// Exposes Loader v4 operation codes. +pub use crate::intent::LOADER_V4_COPY_OPERATION; +/// Exposes Loader v4 operation codes. +pub use crate::intent::LOADER_V4_CREATE_BUFFER_OPERATION; +/// Exposes Loader v4 operation codes. +pub use crate::intent::LOADER_V4_DEPLOY_FROM_SOURCE_OPERATION; +/// Exposes Loader v4 operation codes. +pub use crate::intent::LOADER_V4_DEPLOY_OPERATION; +/// Exposes Loader v4 operation codes. +pub use crate::intent::LOADER_V4_FINALIZE_OPERATION; +/// Exposes Loader v4 operation codes. +pub use crate::intent::LOADER_V4_RETRACT_OPERATION; +/// Exposes Loader v4 operation codes. +pub use crate::intent::LOADER_V4_SET_PROGRAM_LENGTH_OPERATION; +/// Exposes Loader v4 operation codes. +pub use crate::intent::LOADER_V4_TRANSFER_AUTHORITY_OPERATION; +/// Exposes Loader v4 operation codes. +pub use crate::intent::LOADER_V4_WRITE_OPERATION; +/// Exposes the secp256k1 inline verification operation code. +pub use crate::intent::SECP256K1_VERIFY_INLINE_OPERATION; +/// Exposes the secp256k1 offset-table verification operation code. +pub use crate::intent::SECP256K1_VERIFY_OFFSETS_OPERATION; +/// Exposes the secp256r1 inline verification operation code. +pub use crate::intent::SECP256R1_VERIFY_INLINE_OPERATION; +/// Exposes the secp256r1 offset-table verification operation code. +pub use crate::intent::SECP256R1_VERIFY_OFFSETS_OPERATION; +/// Exposes the Slashing violation-report close operation code. +pub use crate::intent::SLASHING_CLOSE_VIOLATION_REPORT_OPERATION; +/// Exposes the Slashing duplicate-block proof submission operation code. +pub use crate::intent::SLASHING_SUBMIT_DUPLICATE_BLOCK_PROOF_OPERATION; +/// Exposes every stable callable Solana core operation code. +pub use crate::intent::SOLANA_CORE_OPERATION_CODES; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_AUTHORIZE_CHECKED_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_AUTHORIZE_CHECKED_WITH_SEED_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_AUTHORIZE_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_AUTHORIZE_WITH_SEED_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_CREATE_ACCOUNT_AND_DELEGATE_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_CREATE_ACCOUNT_CHECKED_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_CREATE_ACCOUNT_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_CREATE_ACCOUNT_WITH_SEED_AND_DELEGATE_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_CREATE_ACCOUNT_WITH_SEED_CHECKED_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_CREATE_ACCOUNT_WITH_SEED_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_DEACTIVATE_DELINQUENT_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_DEACTIVATE_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_DELEGATE_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_GET_MINIMUM_DELEGATION_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_INITIALIZE_CHECKED_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_INITIALIZE_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_MERGE_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_MOVE_LAMPORTS_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_MOVE_STAKE_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_SET_LOCKUP_CHECKED_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_SET_LOCKUP_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_SPLIT_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_SPLIT_WITH_SEED_OPERATION; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::STAKE_WITHDRAW_OPERATION; +/// Exposes the System allocate operation code. +pub use crate::intent::SYSTEM_ALLOCATE_OPERATION; +/// Exposes the System seeded allocate operation code. +pub use crate::intent::SYSTEM_ALLOCATE_WITH_SEED_OPERATION; +/// Exposes the System assign operation code. +pub use crate::intent::SYSTEM_ASSIGN_OPERATION; +/// Exposes the System seeded assign operation code. +pub use crate::intent::SYSTEM_ASSIGN_WITH_SEED_OPERATION; +/// Exposes the System prefunded create-account operation code. +pub use crate::intent::SYSTEM_CREATE_ACCOUNT_ALLOW_PREFUND_OPERATION; +/// Exposes the System create-account operation code. +pub use crate::intent::SYSTEM_CREATE_ACCOUNT_OPERATION; +/// Exposes the System seeded create-account operation code. +pub use crate::intent::SYSTEM_CREATE_ACCOUNT_WITH_SEED_OPERATION; +/// Exposes the durable nonce advance operation code. +pub use crate::intent::SYSTEM_NONCE_ADVANCE_OPERATION; +/// Exposes the durable nonce authorize operation code. +pub use crate::intent::SYSTEM_NONCE_AUTHORIZE_OPERATION; +/// Exposes the durable nonce account creation operation code. +pub use crate::intent::SYSTEM_NONCE_CREATE_OPERATION; +/// Exposes the seeded durable nonce account creation operation code. +pub use crate::intent::SYSTEM_NONCE_CREATE_WITH_SEED_OPERATION; +/// Exposes the durable nonce initialize operation code. +pub use crate::intent::SYSTEM_NONCE_INITIALIZE_OPERATION; +/// Exposes the durable nonce upgrade operation code. +pub use crate::intent::SYSTEM_NONCE_UPGRADE_OPERATION; +/// Exposes the durable nonce withdraw operation code. +pub use crate::intent::SYSTEM_NONCE_WITHDRAW_OPERATION; +/// Exposes the System multi-transfer operation code. +pub use crate::intent::SYSTEM_TRANSFER_MANY_OPERATION; +/// Exposes the System transfer operation code. +pub use crate::intent::SYSTEM_TRANSFER_OPERATION; +/// Exposes the System seeded transfer operation code. +pub use crate::intent::SYSTEM_TRANSFER_WITH_SEED_OPERATION; +/// Exposes one secp256k1 verification offset record. +pub use crate::intent::Secp256k1VerificationOffsets; +/// Exposes one secp256r1 verification offset record. +pub use crate::intent::Secp256r1VerificationOffsets; +/// Exposes the complete typed Solana core intent. +pub use crate::intent::SolanaCoreExecutionIntent; +/// Exposes typed Solana core operation arguments. +pub use crate::intent::SolanaCoreOperation; +/// Exposes Stake Program operation codes and the authority-role selector. +pub use crate::intent::StakeAuthorizationKind; +/// Exposes one recipient in a System multi-transfer. +pub use crate::intent::SystemTransferRecipient; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_AUTHORIZE_CHECKED_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_AUTHORIZE_CHECKED_WITH_SEED_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_AUTHORIZE_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_AUTHORIZE_WITH_SEED_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_COMPACT_UPDATE_VOTE_STATE_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_COMPACT_UPDATE_VOTE_STATE_SWITCH_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_CREATE_ACCOUNT_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_CREATE_ACCOUNT_V2_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_CREATE_ACCOUNT_WITH_SEED_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_CREATE_ACCOUNT_WITH_SEED_V2_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_DEPOSIT_DELEGATOR_REWARDS_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_INITIALIZE_ACCOUNT_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_INITIALIZE_ACCOUNT_V2_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_SUBMIT_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_SWITCH_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_TOWER_SYNC_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_TOWER_SYNC_SWITCH_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_UPDATE_COMMISSION_BPS_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_UPDATE_COMMISSION_COLLECTOR_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_UPDATE_COMMISSION_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_UPDATE_VALIDATOR_IDENTITY_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_UPDATE_VOTE_STATE_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_UPDATE_VOTE_STATE_SWITCH_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VOTE_WITHDRAW_OPERATION; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VoteAuthorizationKind; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VoteCommissionKind; +/// Exposes Vote Program operation codes and typed Vote selectors. +pub use crate::intent::VoteLockout; +/// Exposes the ZK ElGamal context-state close operation code. +pub use crate::intent::ZK_ELGAMAL_CLOSE_CONTEXT_STATE_OPERATION; +/// Exposes the ZK ElGamal proof-account verification operation code. +pub use crate::intent::ZK_ELGAMAL_VERIFY_FROM_ACCOUNT_OPERATION; +/// Exposes the ZK ElGamal inline proof verification operation code. +pub use crate::intent::ZK_ELGAMAL_VERIFY_INLINE_OPERATION; +/// Exposes optional ZK ElGamal context-state accounts. +pub use crate::intent::ZkElGamalContextState; +/// Exposes the official ZK ElGamal proof kinds. +pub use crate::intent::ZkElGamalProofType; diff --git a/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/loader_v3.rs b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/loader_v3.rs new file mode 100644 index 0000000..a7bb34d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/loader_v3.rs @@ -0,0 +1,1121 @@ +// file: kb_executor_solana_core/src/loader_v3.rs +// version: 4 + +//! Exact client-constructible Loader v3 plans backed by `solana-loader-v3-interface`. + +const SOURCE: &str = "solana-loader-v3-interface@8.0.1"; +const MAX_WRITE_BYTES: usize = 1_024; + +pub(crate) fn build_prepared_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, +) -> kb_core::Result { + return match &intent.operation { + crate::SolanaCoreOperation::LoaderV3CreateBuffer { + payer, + buffer, + authority, + lamports, + program_len, + } => build_create_buffer_plan( + intent, + fee_payer, + payer, + buffer, + authority, + *lamports, + *program_len, + ), + crate::SolanaCoreOperation::LoaderV3Write { buffer, authority, offset, bytes } => { + build_write_plan(intent, fee_payer, buffer, authority, *offset, bytes.as_slice()) + }, + crate::SolanaCoreOperation::LoaderV3DeployWithMaxProgramLen { + payer, + program, + buffer, + upgrade_authority, + program_lamports, + max_data_len, + close_buffer, + } => build_deploy_plan( + intent, + fee_payer, + payer, + program, + buffer, + upgrade_authority, + *program_lamports, + *max_data_len, + *close_buffer, + ), + crate::SolanaCoreOperation::LoaderV3Upgrade { + program, + buffer, + authority, + spill, + close_buffer, + } => { + build_upgrade_plan(intent, fee_payer, program, buffer, authority, spill, *close_buffer) + }, + crate::SolanaCoreOperation::LoaderV3SetBufferAuthority { + buffer, + current_authority, + new_authority, + } => build_set_buffer_authority_plan( + intent, + fee_payer, + buffer, + current_authority, + new_authority, + false, + ), + crate::SolanaCoreOperation::LoaderV3SetBufferAuthorityChecked { + buffer, + current_authority, + new_authority, + } => build_set_buffer_authority_plan( + intent, + fee_payer, + buffer, + current_authority, + new_authority, + true, + ), + crate::SolanaCoreOperation::LoaderV3SetUpgradeAuthority { + program, + current_authority, + new_authority, + } => build_set_upgrade_authority_plan( + intent, + fee_payer, + program, + current_authority, + new_authority.as_ref(), + false, + ), + crate::SolanaCoreOperation::LoaderV3SetUpgradeAuthorityChecked { + program, + current_authority, + new_authority, + } => build_set_upgrade_authority_plan( + intent, + fee_payer, + program, + current_authority, + std::option::Option::Some(new_authority), + true, + ), + crate::SolanaCoreOperation::LoaderV3Close { + close_account, + recipient, + authority, + tombstone, + } => build_close_plan(intent, fee_payer, close_account, recipient, authority, *tombstone), + crate::SolanaCoreOperation::LoaderV3CloseAny { + close_account, + recipient, + authority, + program, + tombstone, + } => build_close_any_plan( + intent, + fee_payer, + close_account, + recipient, + authority.as_ref(), + program.as_ref(), + *tombstone, + ), + crate::SolanaCoreOperation::LoaderV3ExtendProgram { program, payer, additional_bytes } => { + build_extend_program_plan(intent, fee_payer, program, payer.as_ref(), *additional_bytes) + }, + _ => std::result::Result::Err(kb_core::Error::new( + "execution_loader_v3_operation_dispatch_invalid", + format!( + "operation {} was dispatched to the Loader v3 builder", + intent.operation.operation_code() + ), + )), + }; +} + +fn build_create_buffer_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + payer: &kb_model::Pubkey, + buffer: &kb_model::Pubkey, + authority: &kb_model::Pubkey, + lamports: u64, + program_len: u64, +) -> kb_core::Result { + if lamports == 0 { + return crate::invalid( + "execution_loader_v3_buffer_lamports_zero", + "Loader v3 buffer creation requires positive rent-exempt lamports", + ); + } + let program_len = match bounded_data_length(program_len, "program_len") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let total_len = + solana_loader_v3_interface::state::UpgradeableLoaderState::size_of_buffer(program_len); + if total_len > solana_system_interface::MAX_PERMITTED_DATA_LENGTH as usize { + return crate::invalid( + "execution_loader_v3_buffer_space_out_of_range", + format!( + "Loader v3 buffer allocation {total_len} exceeds System Program maximum {}", + solana_system_interface::MAX_PERMITTED_DATA_LENGTH + ), + ); + } + match distinct(payer, buffer, "payer", "buffer") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let payer_address = match crate::parse_pubkey(payer, "payer") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let buffer_address = match crate::parse_pubkey(buffer, "buffer") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instructions = match solana_loader_v3_interface::instruction::create_buffer( + &payer_address, + &buffer_address, + &authority_address, + lamports, + program_len, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return official_error("create_buffer", error); + }, + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::LOADER_V3_CREATE_BUFFER_OPERATION, + instructions, + lamports, + std::option::Option::None, + vec![ + (payer.clone(), std::string::String::from("buffer_payer")), + (buffer.clone(), std::string::String::from("new_buffer_account")), + ], + ); +} + +fn build_write_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + buffer: &kb_model::Pubkey, + authority: &kb_model::Pubkey, + offset: u32, + bytes: &[u8], +) -> kb_core::Result { + if bytes.is_empty() || bytes.len() > MAX_WRITE_BYTES { + return crate::invalid( + "execution_loader_v3_write_size_invalid", + format!("Loader v3 write payload must contain between 1 and {} bytes", MAX_WRITE_BYTES), + ); + } + let byte_len = match u32::try_from(bytes.len()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return crate::invalid( + "execution_loader_v3_write_size_overflow", + "Loader v3 write payload length does not fit u32", + ); + }, + }; + if offset.checked_add(byte_len).is_none() { + return crate::invalid( + "execution_loader_v3_write_range_overflow", + "Loader v3 write offset plus payload length exceeds u32", + ); + } + let buffer_address = match crate::parse_pubkey(buffer, "buffer") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = solana_loader_v3_interface::instruction::write( + &buffer_address, + &authority_address, + offset, + bytes.to_vec(), + ); + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::LOADER_V3_WRITE_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + vec![(authority.clone(), std::string::String::from("buffer_authority"))], + ); +} + +#[allow(clippy::too_many_arguments)] +fn build_deploy_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + payer: &kb_model::Pubkey, + program: &kb_model::Pubkey, + buffer: &kb_model::Pubkey, + upgrade_authority: &kb_model::Pubkey, + program_lamports: u64, + max_data_len: u64, + close_buffer: bool, +) -> kb_core::Result { + if program_lamports == 0 { + return crate::invalid( + "execution_loader_v3_program_lamports_zero", + "Loader v3 deployment requires positive rent-exempt program lamports", + ); + } + let max_data_len = match bounded_data_length(max_data_len, "max_data_len") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let programdata_len = + solana_loader_v3_interface::state::UpgradeableLoaderState::size_of_programdata( + max_data_len, + ); + if programdata_len > solana_system_interface::MAX_PERMITTED_DATA_LENGTH as usize { + return crate::invalid( + "execution_loader_v3_programdata_space_out_of_range", + format!( + "Loader v3 ProgramData allocation {programdata_len} exceeds System Program maximum {}", + solana_system_interface::MAX_PERMITTED_DATA_LENGTH + ), + ); + } + for (first, second, first_name, second_name) in + [(payer, program, "payer", "program"), (program, buffer, "program", "buffer")] + { + match distinct(first, second, first_name, second_name) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + let payer_address = match crate::parse_pubkey(payer, "payer") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let program_address = match crate::parse_pubkey(program, "program") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let buffer_address = match crate::parse_pubkey(buffer, "buffer") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(upgrade_authority, "upgrade_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instructions = match solana_loader_v3_interface::instruction::deploy_with_max_program_len( + &payer_address, + &program_address, + &buffer_address, + &authority_address, + program_lamports, + max_data_len, + close_buffer, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return official_error("deploy_with_max_program_len", error); + }, + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::LOADER_V3_DEPLOY_WITH_MAX_PROGRAM_LEN_OPERATION, + instructions, + program_lamports, + std::option::Option::None, + vec![ + (payer.clone(), std::string::String::from("programdata_payer")), + (program.clone(), std::string::String::from("new_program_account")), + (upgrade_authority.clone(), std::string::String::from("upgrade_authority")), + ], + ); +} + +fn build_upgrade_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + program: &kb_model::Pubkey, + buffer: &kb_model::Pubkey, + authority: &kb_model::Pubkey, + spill: &kb_model::Pubkey, + close_buffer: bool, +) -> kb_core::Result { + for (first, second, first_name, second_name) in [ + (program, buffer, "program", "buffer"), + (program, spill, "program", "spill"), + (buffer, spill, "buffer", "spill"), + ] { + match distinct(first, second, first_name, second_name) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + let program_address = match crate::parse_pubkey(program, "program") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let buffer_address = match crate::parse_pubkey(buffer, "buffer") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let spill_address = match crate::parse_pubkey(spill, "spill") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = solana_loader_v3_interface::instruction::upgrade( + &program_address, + &buffer_address, + &authority_address, + &spill_address, + close_buffer, + ); + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::LOADER_V3_UPGRADE_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + vec![(authority.clone(), std::string::String::from("upgrade_authority"))], + ); +} + +fn build_set_buffer_authority_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + buffer: &kb_model::Pubkey, + current_authority: &kb_model::Pubkey, + new_authority: &kb_model::Pubkey, + checked: bool, +) -> kb_core::Result { + match distinct(current_authority, new_authority, "current_authority", "new_authority") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let buffer_address = match crate::parse_pubkey(buffer, "buffer") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let current_address = match crate::parse_pubkey(current_authority, "current_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let new_address = match crate::parse_pubkey(new_authority, "new_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let (operation_code, instruction, signer_roles) = if checked { + ( + crate::LOADER_V3_SET_BUFFER_AUTHORITY_CHECKED_OPERATION, + solana_loader_v3_interface::instruction::set_buffer_authority_checked( + &buffer_address, + ¤t_address, + &new_address, + ), + vec![ + (current_authority.clone(), std::string::String::from("current_buffer_authority")), + (new_authority.clone(), std::string::String::from("new_buffer_authority")), + ], + ) + } else { + ( + crate::LOADER_V3_SET_BUFFER_AUTHORITY_OPERATION, + solana_loader_v3_interface::instruction::set_buffer_authority( + &buffer_address, + ¤t_address, + &new_address, + ), + vec![( + current_authority.clone(), + std::string::String::from("current_buffer_authority"), + )], + ) + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + operation_code, + vec![instruction], + 0, + std::option::Option::None, + signer_roles, + ); +} + +fn build_set_upgrade_authority_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + program: &kb_model::Pubkey, + current_authority: &kb_model::Pubkey, + new_authority: std::option::Option<&kb_model::Pubkey>, + checked: bool, +) -> kb_core::Result { + if checked && new_authority.is_none() { + return crate::invalid( + "execution_loader_v3_checked_authority_missing", + "checked Loader v3 authority changes require a new authority", + ); + } + if let std::option::Option::Some(new_authority) = new_authority { + match distinct(current_authority, new_authority, "current_authority", "new_authority") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + let program_address = match crate::parse_pubkey(program, "program") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let current_address = match crate::parse_pubkey(current_authority, "current_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let new_address = match new_authority { + std::option::Option::Some(value) => match crate::parse_pubkey(value, "new_authority") { + std::result::Result::Ok(parsed) => std::option::Option::Some(parsed), + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + std::option::Option::None => std::option::Option::None, + }; + let (operation_code, instruction, signer_roles) = if checked { + let new_address = match new_address.as_ref() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return crate::invalid( + "execution_loader_v3_checked_authority_missing", + "checked Loader v3 authority changes require a new authority", + ); + }, + }; + let new_authority = match new_authority { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return crate::invalid( + "execution_loader_v3_checked_authority_missing", + "checked Loader v3 authority changes require a new authority", + ); + }, + }; + ( + crate::LOADER_V3_SET_UPGRADE_AUTHORITY_CHECKED_OPERATION, + solana_loader_v3_interface::instruction::set_upgrade_authority_checked( + &program_address, + ¤t_address, + new_address, + ), + vec![ + ( + current_authority.clone(), + std::string::String::from("current_upgrade_authority"), + ), + (new_authority.clone(), std::string::String::from("new_upgrade_authority")), + ], + ) + } else { + ( + crate::LOADER_V3_SET_UPGRADE_AUTHORITY_OPERATION, + solana_loader_v3_interface::instruction::set_upgrade_authority( + &program_address, + ¤t_address, + new_address.as_ref(), + ), + vec![( + current_authority.clone(), + std::string::String::from("current_upgrade_authority"), + )], + ) + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + operation_code, + vec![instruction], + 0, + std::option::Option::None, + signer_roles, + ); +} + +fn build_close_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + close_account: &kb_model::Pubkey, + recipient: &kb_model::Pubkey, + authority: &kb_model::Pubkey, + tombstone: bool, +) -> kb_core::Result { + match distinct(close_account, recipient, "close_account", "recipient") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let close_address = match crate::parse_pubkey(close_account, "close_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let recipient_address = match crate::parse_pubkey(recipient, "recipient") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = solana_loader_v3_interface::instruction::close( + &close_address, + &recipient_address, + &authority_address, + tombstone, + ); + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::LOADER_V3_CLOSE_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + vec![(authority.clone(), std::string::String::from("close_authority"))], + ); +} + +fn build_close_any_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + close_account: &kb_model::Pubkey, + recipient: &kb_model::Pubkey, + authority: std::option::Option<&kb_model::Pubkey>, + program: std::option::Option<&kb_model::Pubkey>, + tombstone: bool, +) -> kb_core::Result { + match distinct(close_account, recipient, "close_account", "recipient") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if let std::option::Option::Some(program) = program { + match distinct(close_account, program, "close_account", "program") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + let close_address = match crate::parse_pubkey(close_account, "close_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let recipient_address = match crate::parse_pubkey(recipient, "recipient") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match authority { + std::option::Option::Some(value) => match crate::parse_pubkey(value, "authority") { + std::result::Result::Ok(parsed) => std::option::Option::Some(parsed), + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + std::option::Option::None => std::option::Option::None, + }; + let program_address = match program { + std::option::Option::Some(value) => match crate::parse_pubkey(value, "program") { + std::result::Result::Ok(parsed) => std::option::Option::Some(parsed), + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + std::option::Option::None => std::option::Option::None, + }; + let instruction = solana_loader_v3_interface::instruction::close_any( + &close_address, + &recipient_address, + authority_address.as_ref(), + program_address.as_ref(), + tombstone, + ); + let signer_roles = match authority { + std::option::Option::Some(value) => { + vec![(value.clone(), std::string::String::from("close_authority"))] + }, + std::option::Option::None => std::vec::Vec::new(), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::LOADER_V3_CLOSE_ANY_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + signer_roles, + ); +} + +fn build_extend_program_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + program: &kb_model::Pubkey, + payer: std::option::Option<&kb_model::Pubkey>, + additional_bytes: u32, +) -> kb_core::Result { + if additional_bytes < solana_loader_v3_interface::instruction::MINIMUM_EXTEND_PROGRAM_BYTES { + return crate::invalid( + "execution_loader_v3_extend_below_current_minimum", + format!( + "Loader v3 program extension requires at least {} bytes without stateful proof of the near-maximum exception", + solana_loader_v3_interface::instruction::MINIMUM_EXTEND_PROGRAM_BYTES + ), + ); + } + if let std::option::Option::Some(payer) = payer { + match distinct(program, payer, "program", "payer") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + let program_address = match crate::parse_pubkey(program, "program") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let payer_address = match payer { + std::option::Option::Some(value) => match crate::parse_pubkey(value, "payer") { + std::result::Result::Ok(parsed) => std::option::Option::Some(parsed), + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + std::option::Option::None => std::option::Option::None, + }; + let instruction = solana_loader_v3_interface::instruction::extend_program( + &program_address, + payer_address.as_ref(), + additional_bytes, + ); + let signer_roles = match payer { + std::option::Option::Some(value) => { + vec![(value.clone(), std::string::String::from("programdata_rent_payer"))] + }, + std::option::Option::None => std::vec::Vec::new(), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::LOADER_V3_EXTEND_PROGRAM_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + signer_roles, + ); +} + +fn bounded_data_length(value: u64, field_name: &str) -> kb_core::Result { + if value == 0 || value > solana_system_interface::MAX_PERMITTED_DATA_LENGTH { + return crate::invalid( + "execution_loader_v3_data_length_out_of_range", + format!( + "Loader v3 {field_name} must be between 1 and {} bytes", + solana_system_interface::MAX_PERMITTED_DATA_LENGTH + ), + ); + } + return match usize::try_from(value) { + std::result::Result::Ok(parsed) => std::result::Result::Ok(parsed), + std::result::Result::Err(_error) => crate::invalid( + "execution_loader_v3_data_length_overflow", + format!("Loader v3 {field_name} does not fit usize"), + ), + }; +} + +fn distinct( + first: &kb_model::Pubkey, + second: &kb_model::Pubkey, + first_name: &str, + second_name: &str, +) -> kb_core::Result<()> { + return crate::validate_distinct_pubkeys( + first, + second, + "execution_loader_v3_accounts_not_distinct", + format!("Loader v3 {first_name} and {second_name} accounts must differ"), + ); +} + +fn official_error( + operation: &str, + error: solana_instruction::error::InstructionError, +) -> kb_core::Result { + return crate::invalid( + "execution_loader_v3_official_builder_failed", + format!("official {operation} builder from {} failed: {error:?}", SOURCE), + ); +} + +#[cfg(test)] +mod tests { + fn model(value: solana_pubkey::Pubkey) -> kb_model::Pubkey { + return kb_model::Pubkey(value.to_string()); + } + + fn policy(signers: &[solana_pubkey::Pubkey]) -> kb_execution_api::ExecutionPolicy { + return kb_execution_api::ExecutionPolicy { + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(20_000_000), + max_fee_lamports: std::option::Option::Some(1_000_000), + max_compute_unit_price_micro_lamports: std::option::Option::Some(100), + }, + authorized_signers: signers.iter().map(|value| return model(*value)).collect(), + ..kb_execution_api::ExecutionPolicy::default() + }; + } + + fn intent( + operation: crate::SolanaCoreOperation, + fee_payer: solana_pubkey::Pubkey, + signers: &[solana_pubkey::Pubkey], + ) -> crate::SolanaCoreExecutionIntent { + return crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("loader-v3-test"), + fee_payer: model(fee_payer), + policy: policy(signers), + operation, + }; + } + + fn build( + intent: &crate::SolanaCoreExecutionIntent, + ) -> kb_core::Result { + let executor = crate::SolanaCoreExecutor; + return kb_execution_api::TypedInstructionExecutor::build_prepared_plan(&executor, intent); + } + + fn assert_plan_matches( + plan: &kb_execution_api::PreparedExecutionPlan, + expected: &[solana_instruction::Instruction], + ) { + assert_eq!(plan.instructions.len(), expected.len()); + for (actual, expected) in plan.instructions.iter().zip(expected.iter()) { + assert_eq!(actual.program_id.0, expected.program_id.to_string()); + assert_eq!(actual.data, expected.data); + assert_eq!(actual.accounts.len(), expected.accounts.len()); + for (actual_account, expected_account) in + actual.accounts.iter().zip(expected.accounts.iter()) + { + assert_eq!(actual_account.pubkey.0, expected_account.pubkey.to_string()); + assert_eq!(actual_account.is_signer, expected_account.is_signer); + assert_eq!(actual_account.is_writable, expected_account.is_writable); + } + } + } + + fn build_or_panic( + intent: &crate::SolanaCoreExecutionIntent, + ) -> kb_execution_api::PreparedExecutionPlan { + return match build(intent) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Loader v3 test plan failed: {error}"), + }; + } + + fn official_or_panic( + result: std::result::Result< + std::vec::Vec, + solana_instruction::error::InstructionError, + >, + ) -> std::vec::Vec { + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("official Loader v3 builder failed: {error:?}") + }, + }; + } + + #[test] + fn create_buffer_matches_official_compound_plan_and_spend() { + let payer = solana_pubkey::Pubkey::new_unique(); + let buffer = solana_pubkey::Pubkey::new_unique(); + let authority = solana_pubkey::Pubkey::new_unique(); + let intent = intent( + crate::SolanaCoreOperation::LoaderV3CreateBuffer { + payer: model(payer), + buffer: model(buffer), + authority: model(authority), + lamports: 1_000_000, + program_len: 4096, + }, + payer, + &[payer, buffer], + ); + let plan = build_or_panic(&intent); + let expected = official_or_panic(solana_loader_v3_interface::instruction::create_buffer( + &payer, &buffer, &authority, 1_000_000, 4096, + )); + assert_plan_matches(&plan, expected.as_slice()); + assert_eq!(plan.requested_spend_lamports, 1_000_000); + assert_eq!(plan.required_signers.len(), 2); + } + + #[test] + fn write_matches_official_builder_and_signer() { + let fee_payer = solana_pubkey::Pubkey::new_unique(); + let buffer = solana_pubkey::Pubkey::new_unique(); + let authority = solana_pubkey::Pubkey::new_unique(); + let bytes = vec![1, 2, 3, 4]; + let intent = intent( + crate::SolanaCoreOperation::LoaderV3Write { + buffer: model(buffer), + authority: model(authority), + offset: 32, + bytes: bytes.clone(), + }, + fee_payer, + &[fee_payer, authority], + ); + let plan = build_or_panic(&intent); + let expected = + solana_loader_v3_interface::instruction::write(&buffer, &authority, 32, bytes); + assert_plan_matches(&plan, std::slice::from_ref(&expected)); + assert_eq!(plan.required_signers.len(), 2); + } + + #[test] + fn deploy_and_upgrade_match_official_builders() { + let payer = solana_pubkey::Pubkey::new_unique(); + let program = solana_pubkey::Pubkey::new_unique(); + let buffer = solana_pubkey::Pubkey::new_unique(); + let authority = solana_pubkey::Pubkey::new_unique(); + let spill = solana_pubkey::Pubkey::new_unique(); + let deploy_intent = intent( + crate::SolanaCoreOperation::LoaderV3DeployWithMaxProgramLen { + payer: model(payer), + program: model(program), + buffer: model(buffer), + upgrade_authority: model(authority), + program_lamports: 2_000_000, + max_data_len: 65_536, + close_buffer: true, + }, + payer, + &[payer, program, authority], + ); + let deploy_plan = build_or_panic(&deploy_intent); + let expected_deploy = official_or_panic( + solana_loader_v3_interface::instruction::deploy_with_max_program_len( + &payer, &program, &buffer, &authority, 2_000_000, 65_536, true, + ), + ); + assert_plan_matches(&deploy_plan, expected_deploy.as_slice()); + assert_eq!(deploy_plan.requested_spend_lamports, 2_000_000); + + let upgrade_intent = intent( + crate::SolanaCoreOperation::LoaderV3Upgrade { + program: model(program), + buffer: model(buffer), + authority: model(authority), + spill: model(spill), + close_buffer: false, + }, + payer, + &[payer, authority], + ); + let upgrade_plan = build_or_panic(&upgrade_intent); + let expected_upgrade = solana_loader_v3_interface::instruction::upgrade( + &program, &buffer, &authority, &spill, false, + ); + assert_plan_matches(&upgrade_plan, std::slice::from_ref(&expected_upgrade)); + } + + #[test] + fn authority_helpers_match_official_accounts_and_signers() { + let fee_payer = solana_pubkey::Pubkey::new_unique(); + let target = solana_pubkey::Pubkey::new_unique(); + let current = solana_pubkey::Pubkey::new_unique(); + let new = solana_pubkey::Pubkey::new_unique(); + let fixtures = [ + ( + crate::SolanaCoreOperation::LoaderV3SetBufferAuthority { + buffer: model(target), + current_authority: model(current), + new_authority: model(new), + }, + solana_loader_v3_interface::instruction::set_buffer_authority( + &target, ¤t, &new, + ), + vec![fee_payer, current], + ), + ( + crate::SolanaCoreOperation::LoaderV3SetBufferAuthorityChecked { + buffer: model(target), + current_authority: model(current), + new_authority: model(new), + }, + solana_loader_v3_interface::instruction::set_buffer_authority_checked( + &target, ¤t, &new, + ), + vec![fee_payer, current, new], + ), + ( + crate::SolanaCoreOperation::LoaderV3SetUpgradeAuthority { + program: model(target), + current_authority: model(current), + new_authority: std::option::Option::Some(model(new)), + }, + solana_loader_v3_interface::instruction::set_upgrade_authority( + &target, + ¤t, + std::option::Option::Some(&new), + ), + vec![fee_payer, current], + ), + ( + crate::SolanaCoreOperation::LoaderV3SetUpgradeAuthorityChecked { + program: model(target), + current_authority: model(current), + new_authority: model(new), + }, + solana_loader_v3_interface::instruction::set_upgrade_authority_checked( + &target, ¤t, &new, + ), + vec![fee_payer, current, new], + ), + ]; + for (operation, expected, signers) in fixtures { + let intent = intent(operation, fee_payer, signers.as_slice()); + let plan = build_or_panic(&intent); + assert_plan_matches(&plan, std::slice::from_ref(&expected)); + } + } + + #[test] + fn close_and_extend_match_official_builders() { + let fee_payer = solana_pubkey::Pubkey::new_unique(); + let close_account = solana_pubkey::Pubkey::new_unique(); + let recipient = solana_pubkey::Pubkey::new_unique(); + let authority = solana_pubkey::Pubkey::new_unique(); + let program = solana_pubkey::Pubkey::new_unique(); + let close_intent = intent( + crate::SolanaCoreOperation::LoaderV3Close { + close_account: model(close_account), + recipient: model(recipient), + authority: model(authority), + tombstone: false, + }, + fee_payer, + &[fee_payer, authority], + ); + let close_plan = build_or_panic(&close_intent); + let expected_close = solana_loader_v3_interface::instruction::close( + &close_account, + &recipient, + &authority, + false, + ); + assert_plan_matches(&close_plan, std::slice::from_ref(&expected_close)); + + let close_any_intent = intent( + crate::SolanaCoreOperation::LoaderV3CloseAny { + close_account: model(close_account), + recipient: model(recipient), + authority: std::option::Option::Some(model(authority)), + program: std::option::Option::Some(model(program)), + tombstone: true, + }, + fee_payer, + &[fee_payer, authority], + ); + let close_any_plan = build_or_panic(&close_any_intent); + let expected_close_any = solana_loader_v3_interface::instruction::close_any( + &close_account, + &recipient, + std::option::Option::Some(&authority), + std::option::Option::Some(&program), + true, + ); + assert_plan_matches(&close_any_plan, std::slice::from_ref(&expected_close_any)); + + let extend_intent = intent( + crate::SolanaCoreOperation::LoaderV3ExtendProgram { + program: model(program), + payer: std::option::Option::Some(model(fee_payer)), + additional_bytes: + solana_loader_v3_interface::instruction::MINIMUM_EXTEND_PROGRAM_BYTES, + }, + fee_payer, + &[fee_payer], + ); + let extend_plan = build_or_panic(&extend_intent); + let expected_extend = solana_loader_v3_interface::instruction::extend_program( + &program, + std::option::Option::Some(&fee_payer), + solana_loader_v3_interface::instruction::MINIMUM_EXTEND_PROGRAM_BYTES, + ); + assert_plan_matches(&extend_plan, std::slice::from_ref(&expected_extend)); + } + + #[test] + fn malformed_loader_contracts_are_rejected() { + let payer = solana_pubkey::Pubkey::new_unique(); + let other = solana_pubkey::Pubkey::new_unique(); + let empty_write = intent( + crate::SolanaCoreOperation::LoaderV3Write { + buffer: model(other), + authority: model(payer), + offset: 0, + bytes: std::vec::Vec::new(), + }, + payer, + &[payer], + ); + assert!(build(&empty_write).is_err()); + let same_accounts = intent( + crate::SolanaCoreOperation::LoaderV3CreateBuffer { + payer: model(payer), + buffer: model(payer), + authority: model(other), + lamports: 1, + program_len: 1, + }, + payer, + &[payer], + ); + assert!(build(&same_accounts).is_err()); + let same_close_accounts = intent( + crate::SolanaCoreOperation::LoaderV3CloseAny { + close_account: model(other), + recipient: model(other), + authority: std::option::Option::None, + program: std::option::Option::None, + tombstone: true, + }, + payer, + &[payer], + ); + assert!(build(&same_close_accounts).is_err()); + let zero_extend = intent( + crate::SolanaCoreOperation::LoaderV3ExtendProgram { + program: model(other), + payer: std::option::Option::None, + additional_bytes: 0, + }, + payer, + &[payer], + ); + assert!(build(&zero_extend).is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/loader_v4.rs b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/loader_v4.rs new file mode 100644 index 0000000..b6d8e82 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/loader_v4.rs @@ -0,0 +1,884 @@ +// file: kb_executor_solana_core/src/loader_v4.rs +// version: 4 + +//! Exact Loader v4 client plans without activating the interface's `bincode` helpers. + +#[cfg(test)] +const SOURCE: &str = "solana-loader-v4-interface@3.1.0"; +const MAX_WRITE_BYTES: usize = 1_024; +const WRITE_TAG: u32 = 0; +const COPY_TAG: u32 = 1; +const SET_PROGRAM_LENGTH_TAG: u32 = 2; +const DEPLOY_TAG: u32 = 3; +const RETRACT_TAG: u32 = 4; +const TRANSFER_AUTHORITY_TAG: u32 = 5; +const FINALIZE_TAG: u32 = 6; + +pub(crate) fn build_prepared_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, +) -> kb_core::Result { + return match &intent.operation { + crate::SolanaCoreOperation::LoaderV4CreateBuffer { + payer, + buffer, + authority, + recipient, + lamports, + new_size, + } => build_create_buffer_plan( + intent, fee_payer, payer, buffer, authority, recipient, *lamports, *new_size, + ), + crate::SolanaCoreOperation::LoaderV4SetProgramLength { + program, + authority, + recipient, + new_size, + } => build_set_program_length_plan( + intent, fee_payer, program, authority, recipient, *new_size, + ), + crate::SolanaCoreOperation::LoaderV4Write { program, authority, offset, bytes } => { + build_write_plan(intent, fee_payer, program, authority, *offset, bytes.as_slice()) + }, + crate::SolanaCoreOperation::LoaderV4Copy { + program, + authority, + source_program, + destination_offset, + source_offset, + length, + } => build_copy_plan( + intent, + fee_payer, + program, + authority, + source_program, + *destination_offset, + *source_offset, + *length, + ), + crate::SolanaCoreOperation::LoaderV4Deploy { program, authority } => { + build_authority_unit_plan( + intent, + fee_payer, + crate::LOADER_V4_DEPLOY_OPERATION, + program, + authority, + DEPLOY_TAG, + ) + }, + crate::SolanaCoreOperation::LoaderV4DeployFromSource { + program, + authority, + source_program, + } => build_deploy_from_source_plan(intent, fee_payer, program, authority, source_program), + crate::SolanaCoreOperation::LoaderV4Retract { program, authority } => { + build_authority_unit_plan( + intent, + fee_payer, + crate::LOADER_V4_RETRACT_OPERATION, + program, + authority, + RETRACT_TAG, + ) + }, + crate::SolanaCoreOperation::LoaderV4TransferAuthority { + program, + current_authority, + new_authority, + } => build_transfer_authority_plan( + intent, + fee_payer, + program, + current_authority, + new_authority, + ), + crate::SolanaCoreOperation::LoaderV4Finalize { + program, + authority, + next_version_program, + } => build_finalize_plan(intent, fee_payer, program, authority, next_version_program), + _ => std::result::Result::Err(kb_core::Error::new( + "execution_loader_v4_operation_dispatch_invalid", + format!( + "operation {} was dispatched to the Loader v4 builder", + intent.operation.operation_code() + ), + )), + }; +} + +fn build_create_buffer_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + payer: &kb_model::Pubkey, + buffer: &kb_model::Pubkey, + authority: &kb_model::Pubkey, + recipient: &kb_model::Pubkey, + lamports: u64, + new_size: u32, +) -> kb_core::Result { + if lamports == 0 { + return crate::invalid( + "execution_loader_v4_buffer_lamports_zero", + "Loader v4 buffer creation requires positive lamports", + ); + } + if new_size == 0 { + return crate::invalid( + "execution_loader_v4_buffer_size_zero", + "Loader v4 buffer creation requires a positive initial size", + ); + } + match validate_size(new_size) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match distinct(payer, buffer, "payer", "buffer") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match distinct(buffer, recipient, "buffer", "recipient") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let payer_address = match crate::parse_pubkey(payer, "payer") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let buffer_address = match crate::parse_pubkey(buffer, "buffer") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let recipient_address = match crate::parse_pubkey(recipient, "recipient") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instructions = vec![ + solana_system_interface::instruction::create_account( + &payer_address, + &buffer_address, + lamports, + 0, + &solana_sdk_ids::loader_v4::ID, + ), + set_program_length_instruction( + &buffer_address, + &authority_address, + &recipient_address, + new_size, + ), + ]; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::LOADER_V4_CREATE_BUFFER_OPERATION, + instructions, + lamports, + std::option::Option::None, + vec![ + (payer.clone(), std::string::String::from("buffer_payer")), + (buffer.clone(), std::string::String::from("new_buffer_account")), + (authority.clone(), std::string::String::from("program_authority")), + ], + ); +} + +fn build_set_program_length_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + program: &kb_model::Pubkey, + authority: &kb_model::Pubkey, + recipient: &kb_model::Pubkey, + new_size: u32, +) -> kb_core::Result { + match validate_size(new_size) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match distinct(program, recipient, "program", "recipient") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let program_address = match crate::parse_pubkey(program, "program") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let recipient_address = match crate::parse_pubkey(recipient, "recipient") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = set_program_length_instruction( + &program_address, + &authority_address, + &recipient_address, + new_size, + ); + return finalize_single( + intent, + fee_payer, + crate::LOADER_V4_SET_PROGRAM_LENGTH_OPERATION, + instruction, + vec![(authority.clone(), std::string::String::from("program_authority"))], + ); +} + +fn build_write_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + program: &kb_model::Pubkey, + authority: &kb_model::Pubkey, + offset: u32, + bytes: &[u8], +) -> kb_core::Result { + if bytes.is_empty() || bytes.len() > MAX_WRITE_BYTES { + return crate::invalid( + "execution_loader_v4_write_size_invalid", + format!("Loader v4 write payload must contain between 1 and {} bytes", MAX_WRITE_BYTES), + ); + } + let byte_length = match u32::try_from(bytes.len()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return crate::invalid( + "execution_loader_v4_write_size_overflow", + "Loader v4 write payload length does not fit u32", + ); + }, + }; + if offset.checked_add(byte_length).is_none() { + return crate::invalid( + "execution_loader_v4_write_range_overflow", + "Loader v4 write offset plus payload length exceeds u32", + ); + } + let program_address = match crate::parse_pubkey(program, "program") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = solana_instruction::Instruction { + program_id: solana_sdk_ids::loader_v4::ID, + accounts: vec![ + solana_instruction::AccountMeta::new(program_address, false), + solana_instruction::AccountMeta::new_readonly(authority_address, true), + ], + data: write_data(offset, bytes), + }; + return finalize_single( + intent, + fee_payer, + crate::LOADER_V4_WRITE_OPERATION, + instruction, + vec![(authority.clone(), std::string::String::from("program_authority"))], + ); +} + +fn build_copy_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + program: &kb_model::Pubkey, + authority: &kb_model::Pubkey, + source_program: &kb_model::Pubkey, + destination_offset: u32, + source_offset: u32, + length: u32, +) -> kb_core::Result { + if length == 0 { + return crate::invalid( + "execution_loader_v4_copy_length_zero", + "Loader v4 copy requires a positive byte length", + ); + } + if destination_offset.checked_add(length).is_none() + || source_offset.checked_add(length).is_none() + { + return crate::invalid( + "execution_loader_v4_copy_range_overflow", + "Loader v4 copy offset plus length exceeds u32", + ); + } + match distinct(program, source_program, "program", "source_program") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let program_address = match crate::parse_pubkey(program, "program") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let source_address = match crate::parse_pubkey(source_program, "source_program") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = solana_instruction::Instruction { + program_id: solana_sdk_ids::loader_v4::ID, + accounts: vec![ + solana_instruction::AccountMeta::new(program_address, false), + solana_instruction::AccountMeta::new_readonly(authority_address, true), + solana_instruction::AccountMeta::new_readonly(source_address, false), + ], + data: copy_data(destination_offset, source_offset, length), + }; + return finalize_single( + intent, + fee_payer, + crate::LOADER_V4_COPY_OPERATION, + instruction, + vec![(authority.clone(), std::string::String::from("program_authority"))], + ); +} + +fn build_authority_unit_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + operation_code: &str, + program: &kb_model::Pubkey, + authority: &kb_model::Pubkey, + tag: u32, +) -> kb_core::Result { + let program_address = match crate::parse_pubkey(program, "program") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = solana_instruction::Instruction { + program_id: solana_sdk_ids::loader_v4::ID, + accounts: vec![ + solana_instruction::AccountMeta::new(program_address, false), + solana_instruction::AccountMeta::new_readonly(authority_address, true), + ], + data: tag.to_le_bytes().to_vec(), + }; + return finalize_single( + intent, + fee_payer, + operation_code, + instruction, + vec![(authority.clone(), std::string::String::from("program_authority"))], + ); +} + +fn build_deploy_from_source_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + program: &kb_model::Pubkey, + authority: &kb_model::Pubkey, + source_program: &kb_model::Pubkey, +) -> kb_core::Result { + match distinct(program, source_program, "program", "source_program") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let program_address = match crate::parse_pubkey(program, "program") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let source_address = match crate::parse_pubkey(source_program, "source_program") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = solana_instruction::Instruction { + program_id: solana_sdk_ids::loader_v4::ID, + accounts: vec![ + solana_instruction::AccountMeta::new(program_address, false), + solana_instruction::AccountMeta::new_readonly(authority_address, true), + solana_instruction::AccountMeta::new(source_address, false), + ], + data: DEPLOY_TAG.to_le_bytes().to_vec(), + }; + return finalize_single( + intent, + fee_payer, + crate::LOADER_V4_DEPLOY_FROM_SOURCE_OPERATION, + instruction, + vec![(authority.clone(), std::string::String::from("program_authority"))], + ); +} + +fn build_transfer_authority_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + program: &kb_model::Pubkey, + current_authority: &kb_model::Pubkey, + new_authority: &kb_model::Pubkey, +) -> kb_core::Result { + match distinct(current_authority, new_authority, "current_authority", "new_authority") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let program_address = match crate::parse_pubkey(program, "program") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let current_address = match crate::parse_pubkey(current_authority, "current_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let new_address = match crate::parse_pubkey(new_authority, "new_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = solana_instruction::Instruction { + program_id: solana_sdk_ids::loader_v4::ID, + accounts: vec![ + solana_instruction::AccountMeta::new(program_address, false), + solana_instruction::AccountMeta::new_readonly(current_address, true), + solana_instruction::AccountMeta::new_readonly(new_address, true), + ], + data: TRANSFER_AUTHORITY_TAG.to_le_bytes().to_vec(), + }; + return finalize_single( + intent, + fee_payer, + crate::LOADER_V4_TRANSFER_AUTHORITY_OPERATION, + instruction, + vec![ + ( + current_authority.clone(), + std::string::String::from("current_program_authority"), + ), + (new_authority.clone(), std::string::String::from("new_program_authority")), + ], + ); +} + +fn build_finalize_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + program: &kb_model::Pubkey, + authority: &kb_model::Pubkey, + next_version_program: &kb_model::Pubkey, +) -> kb_core::Result { + let program_address = match crate::parse_pubkey(program, "program") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let next_version_address = + match crate::parse_pubkey(next_version_program, "next_version_program") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = solana_instruction::Instruction { + program_id: solana_sdk_ids::loader_v4::ID, + accounts: vec![ + solana_instruction::AccountMeta::new(program_address, false), + solana_instruction::AccountMeta::new_readonly(authority_address, true), + solana_instruction::AccountMeta::new_readonly(next_version_address, false), + ], + data: FINALIZE_TAG.to_le_bytes().to_vec(), + }; + return finalize_single( + intent, + fee_payer, + crate::LOADER_V4_FINALIZE_OPERATION, + instruction, + vec![(authority.clone(), std::string::String::from("program_authority"))], + ); +} + +fn set_program_length_instruction( + program: &solana_pubkey::Pubkey, + authority: &solana_pubkey::Pubkey, + recipient: &solana_pubkey::Pubkey, + new_size: u32, +) -> solana_instruction::Instruction { + let mut data = SET_PROGRAM_LENGTH_TAG.to_le_bytes().to_vec(); + data.extend_from_slice(&new_size.to_le_bytes()); + return solana_instruction::Instruction { + program_id: solana_sdk_ids::loader_v4::ID, + accounts: vec![ + solana_instruction::AccountMeta::new(*program, false), + solana_instruction::AccountMeta::new_readonly(*authority, true), + solana_instruction::AccountMeta::new(*recipient, false), + ], + data, + }; +} + +fn write_data(offset: u32, bytes: &[u8]) -> std::vec::Vec { + let mut data = WRITE_TAG.to_le_bytes().to_vec(); + data.extend_from_slice(&offset.to_le_bytes()); + data.extend_from_slice(&(bytes.len() as u64).to_le_bytes()); + data.extend_from_slice(bytes); + return data; +} + +fn copy_data(destination_offset: u32, source_offset: u32, length: u32) -> std::vec::Vec { + let mut data = COPY_TAG.to_le_bytes().to_vec(); + data.extend_from_slice(&destination_offset.to_le_bytes()); + data.extend_from_slice(&source_offset.to_le_bytes()); + data.extend_from_slice(&length.to_le_bytes()); + return data; +} + +fn finalize_single( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + operation_code: &str, + instruction: solana_instruction::Instruction, + signer_roles: std::vec::Vec<(kb_model::Pubkey, std::string::String)>, +) -> kb_core::Result { + return crate::finalize_prepared_plan( + intent, + fee_payer, + operation_code, + vec![instruction], + 0, + std::option::Option::None, + signer_roles, + ); +} + +fn validate_size(new_size: u32) -> kb_core::Result<()> { + if u64::from(new_size) > solana_system_interface::MAX_PERMITTED_DATA_LENGTH { + return crate::invalid( + "execution_loader_v4_program_size_out_of_range", + format!( + "Loader v4 program size must not exceed {} bytes", + solana_system_interface::MAX_PERMITTED_DATA_LENGTH + ), + ); + } + return std::result::Result::Ok(()); +} + +fn distinct( + first: &kb_model::Pubkey, + second: &kb_model::Pubkey, + first_name: &str, + second_name: &str, +) -> kb_core::Result<()> { + return crate::validate_distinct_pubkeys( + first, + second, + "execution_loader_v4_accounts_not_distinct", + format!("Loader v4 {first_name} and {second_name} accounts must be distinct"), + ); +} + +#[cfg(test)] +mod tests { + fn model(value: solana_pubkey::Pubkey) -> kb_model::Pubkey { + return kb_model::Pubkey(value.to_string()); + } + + fn policy(signers: &[solana_pubkey::Pubkey]) -> kb_execution_api::ExecutionPolicy { + return kb_execution_api::ExecutionPolicy { + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(20_000_000), + max_fee_lamports: std::option::Option::Some(1_000_000), + max_compute_unit_price_micro_lamports: std::option::Option::Some(100), + }, + authorized_signers: signers.iter().map(|value| return model(*value)).collect(), + ..kb_execution_api::ExecutionPolicy::default() + }; + } + + fn intent( + operation: crate::SolanaCoreOperation, + fee_payer: solana_pubkey::Pubkey, + signers: &[solana_pubkey::Pubkey], + ) -> crate::SolanaCoreExecutionIntent { + return crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("loader-v4-test"), + fee_payer: model(fee_payer), + policy: policy(signers), + operation, + }; + } + + fn build( + intent: &crate::SolanaCoreExecutionIntent, + ) -> kb_core::Result { + let executor = crate::SolanaCoreExecutor; + return kb_execution_api::TypedInstructionExecutor::build_prepared_plan(&executor, intent); + } + + fn build_or_panic( + intent: &crate::SolanaCoreExecutionIntent, + ) -> kb_execution_api::PreparedExecutionPlan { + return match build(intent) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Loader v4 test plan failed: {error}"), + }; + } + + fn assert_account( + account: &kb_execution_api::PlannedAccount, + expected: solana_pubkey::Pubkey, + signer: bool, + writable: bool, + ) { + assert_eq!(account.pubkey.0, expected.to_string()); + assert_eq!(account.is_signer, signer); + assert_eq!(account.is_writable, writable); + } + + #[test] + fn create_buffer_preserves_official_compound_order_wire_and_spend() { + let payer = solana_pubkey::Pubkey::new_unique(); + let buffer = solana_pubkey::Pubkey::new_unique(); + let authority = solana_pubkey::Pubkey::new_unique(); + let recipient = solana_pubkey::Pubkey::new_unique(); + let intent = intent( + crate::SolanaCoreOperation::LoaderV4CreateBuffer { + payer: model(payer), + buffer: model(buffer), + authority: model(authority), + recipient: model(recipient), + lamports: 1_000_000, + new_size: 4096, + }, + payer, + &[payer, buffer, authority], + ); + let plan = build_or_panic(&intent); + assert_eq!(plan.instructions.len(), 2); + assert_eq!( + plan.instructions[0].program_id.0, + solana_sdk_ids::system_program::ID.to_string() + ); + assert_eq!(plan.instructions[1].program_id.0, solana_sdk_ids::loader_v4::ID.to_string()); + assert_eq!( + plan.instructions[1].data, + [ + super::SET_PROGRAM_LENGTH_TAG.to_le_bytes().as_slice(), + 4096_u32.to_le_bytes().as_slice(), + ] + .concat() + ); + assert_account(&plan.instructions[1].accounts[0], buffer, false, true); + assert_account(&plan.instructions[1].accounts[1], authority, true, false); + assert_account(&plan.instructions[1].accounts[2], recipient, false, true); + assert_eq!(plan.requested_spend_lamports, 1_000_000); + assert_eq!(plan.required_signers.len(), 3); + } + + #[test] + fn write_copy_and_set_length_preserve_exact_bincode_wire_contract() { + let fee_payer = solana_pubkey::Pubkey::new_unique(); + let program = solana_pubkey::Pubkey::new_unique(); + let authority = solana_pubkey::Pubkey::new_unique(); + let source = solana_pubkey::Pubkey::new_unique(); + let recipient = solana_pubkey::Pubkey::new_unique(); + let write_intent = intent( + crate::SolanaCoreOperation::LoaderV4Write { + program: model(program), + authority: model(authority), + offset: 7, + bytes: vec![1, 2, 3], + }, + fee_payer, + &[fee_payer, authority], + ); + let write_plan = build_or_panic(&write_intent); + assert_eq!( + write_plan.instructions[0].data, + [ + super::WRITE_TAG.to_le_bytes().as_slice(), + 7_u32.to_le_bytes().as_slice(), + 3_u64.to_le_bytes().as_slice(), + &[1, 2, 3], + ] + .concat() + ); + let copy_intent = intent( + crate::SolanaCoreOperation::LoaderV4Copy { + program: model(program), + authority: model(authority), + source_program: model(source), + destination_offset: 11, + source_offset: 13, + length: 17, + }, + fee_payer, + &[fee_payer, authority], + ); + let copy_plan = build_or_panic(©_intent); + assert_eq!( + copy_plan.instructions[0].data, + [ + super::COPY_TAG.to_le_bytes().as_slice(), + 11_u32.to_le_bytes().as_slice(), + 13_u32.to_le_bytes().as_slice(), + 17_u32.to_le_bytes().as_slice(), + ] + .concat() + ); + assert_account(©_plan.instructions[0].accounts[2], source, false, false); + let length_intent = intent( + crate::SolanaCoreOperation::LoaderV4SetProgramLength { + program: model(program), + authority: model(authority), + recipient: model(recipient), + new_size: 0, + }, + fee_payer, + &[fee_payer, authority], + ); + let length_plan = build_or_panic(&length_intent); + assert_eq!( + length_plan.instructions[0].data, + [ + super::SET_PROGRAM_LENGTH_TAG.to_le_bytes().as_slice(), + 0_u32.to_le_bytes().as_slice(), + ] + .concat() + ); + assert_eq!(length_plan.requested_spend_lamports, 0); + } + + #[test] + fn deploy_retract_transfer_and_finalize_preserve_official_accounts_and_tags() { + let fee_payer = solana_pubkey::Pubkey::new_unique(); + let program = solana_pubkey::Pubkey::new_unique(); + let authority = solana_pubkey::Pubkey::new_unique(); + let source = solana_pubkey::Pubkey::new_unique(); + let new_authority = solana_pubkey::Pubkey::new_unique(); + let next_version = solana_pubkey::Pubkey::new_unique(); + let fixtures = [ + ( + crate::SolanaCoreOperation::LoaderV4Deploy { + program: model(program), + authority: model(authority), + }, + super::DEPLOY_TAG, + vec![fee_payer, authority], + 2, + ), + ( + crate::SolanaCoreOperation::LoaderV4DeployFromSource { + program: model(program), + authority: model(authority), + source_program: model(source), + }, + super::DEPLOY_TAG, + vec![fee_payer, authority], + 3, + ), + ( + crate::SolanaCoreOperation::LoaderV4Retract { + program: model(program), + authority: model(authority), + }, + super::RETRACT_TAG, + vec![fee_payer, authority], + 2, + ), + ( + crate::SolanaCoreOperation::LoaderV4TransferAuthority { + program: model(program), + current_authority: model(authority), + new_authority: model(new_authority), + }, + super::TRANSFER_AUTHORITY_TAG, + vec![fee_payer, authority, new_authority], + 3, + ), + ( + crate::SolanaCoreOperation::LoaderV4Finalize { + program: model(program), + authority: model(authority), + next_version_program: model(next_version), + }, + super::FINALIZE_TAG, + vec![fee_payer, authority], + 3, + ), + ]; + for (operation, tag, signers, account_count) in fixtures { + let intent = intent(operation, fee_payer, signers.as_slice()); + let plan = build_or_panic(&intent); + assert_eq!(plan.instructions[0].data, tag.to_le_bytes().to_vec()); + assert_eq!(plan.instructions[0].accounts.len(), account_count); + } + } + + #[test] + fn malformed_sizes_ranges_and_authorities_are_rejected() { + let fee_payer = solana_pubkey::Pubkey::new_unique(); + let program = solana_pubkey::Pubkey::new_unique(); + let authority = solana_pubkey::Pubkey::new_unique(); + let recipient = solana_pubkey::Pubkey::new_unique(); + let empty_write = intent( + crate::SolanaCoreOperation::LoaderV4Write { + program: model(program), + authority: model(authority), + offset: 0, + bytes: std::vec::Vec::new(), + }, + fee_payer, + &[fee_payer, authority], + ); + assert!(build(&empty_write).is_err()); + let zero_copy = intent( + crate::SolanaCoreOperation::LoaderV4Copy { + program: model(program), + authority: model(authority), + source_program: model(recipient), + destination_offset: 0, + source_offset: 0, + length: 0, + }, + fee_payer, + &[fee_payer, authority], + ); + assert!(build(&zero_copy).is_err()); + let same_authority = intent( + crate::SolanaCoreOperation::LoaderV4TransferAuthority { + program: model(program), + current_authority: model(authority), + new_authority: model(authority), + }, + fee_payer, + &[fee_payer, authority], + ); + assert!(build(&same_authority).is_err()); + let zero_buffer = intent( + crate::SolanaCoreOperation::LoaderV4CreateBuffer { + payer: model(fee_payer), + buffer: model(program), + authority: model(authority), + recipient: model(recipient), + lamports: 1, + new_size: 0, + }, + fee_payer, + &[fee_payer, program, authority], + ); + assert!(build(&zero_buffer).is_err()); + } + + #[test] + fn loader_v4_wire_source_is_explicit_and_stable() { + assert_eq!(super::SOURCE, "solana-loader-v4-interface@3.1.0"); + assert_eq!(super::WRITE_TAG, 0); + assert_eq!(super::FINALIZE_TAG, 6); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/native_admin.rs b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/native_admin.rs new file mode 100644 index 0000000..494aa1c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/native_admin.rs @@ -0,0 +1,530 @@ +// file: kb_executor_solana_core/src/native_admin.rs +// version: 4 + +//! Config Program and Feature Program instruction builders. + +const MAX_CONFIG_KEYS: usize = 128; +const MAX_CONFIG_INSTRUCTION_DATA_BYTES: usize = u16::MAX as usize; + +pub(crate) fn build_prepared_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, +) -> kb_core::Result { + return match &intent.operation { + crate::SolanaCoreOperation::ConfigCreateAccount { + from, + config_account, + lamports, + max_config_data_space, + max_keys, + initial_data, + } => build_config_create_plan( + intent, + fee_payer, + from, + config_account, + *lamports, + *max_config_data_space, + max_keys.as_slice(), + initial_data.as_slice(), + ), + crate::SolanaCoreOperation::ConfigStore { + config_account, + config_account_is_signer, + keys, + data, + } => build_config_store_plan( + intent, + fee_payer, + config_account, + *config_account_is_signer, + keys.as_slice(), + data.as_slice(), + ), + crate::SolanaCoreOperation::FeatureActivate { + feature_account, + funding_account, + lamports, + } => build_feature_activate_plan( + intent, + fee_payer, + feature_account, + funding_account, + *lamports, + ), + crate::SolanaCoreOperation::FeatureRevokePendingActivation { feature_account } => { + build_feature_revoke_plan(intent, fee_payer, feature_account) + }, + _ => std::result::Result::Err(kb_core::Error::new( + "execution_native_admin_operation_mismatch", + "Config/Feature builder received an unrelated Solana core operation", + )), + }; +} + +fn build_config_create_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + from: &kb_model::Pubkey, + config_account: &kb_model::Pubkey, + lamports: u64, + max_config_data_space: u64, + max_keys: &[crate::ConfigKey], + initial_data: &[u8], +) -> kb_core::Result { + if lamports == 0 { + return std::result::Result::Err(kb_core::Error::new( + "execution_config_create_lamports_zero", + "Config account creation requires a positive rent-exempt lamport amount", + )); + } + if from == config_account { + return std::result::Result::Err(kb_core::Error::new( + "execution_config_create_same_account", + "Config funding and new account addresses must differ", + )); + } + let from_address = match crate::parse_pubkey(from, "from") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let config_address = match crate::parse_pubkey(config_account, "config_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let encoded_max_keys = match encode_config_keys(max_keys) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let initial_data_len = initial_data.len() as u64; + if initial_data_len > max_config_data_space { + return std::result::Result::Err(kb_core::Error::new( + "execution_config_initial_data_exceeds_max_space", + "serialized initial Config data exceeds max_config_data_space", + )); + } + let space = match max_config_data_space.checked_add(encoded_max_keys.len() as u64) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_config_space_overflow", + "Config account space exceeds u64", + )); + }, + }; + if space > solana_system_interface::MAX_PERMITTED_DATA_LENGTH { + return std::result::Result::Err(kb_core::Error::new( + "execution_config_space_out_of_range", + format!( + "Config account space must not exceed {} bytes", + solana_system_interface::MAX_PERMITTED_DATA_LENGTH + ), + )); + } + let config_program = match crate::parse_native_program_id(kb_program_ids::CONFIG_PROGRAM_ID) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let create = solana_system_interface::instruction::create_account( + &from_address, + &config_address, + lamports, + space, + &config_program, + ); + let mut initialize_data = vec![0_u8]; + initialize_data.extend_from_slice(initial_data); + let initialize = solana_instruction::Instruction { + program_id: config_program, + accounts: vec![solana_instruction::AccountMeta::new(config_address, true)], + data: initialize_data, + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::CONFIG_CREATE_ACCOUNT_OPERATION, + vec![create, initialize], + lamports, + std::option::Option::None, + vec![ + (from.clone(), std::string::String::from("config_funding_account")), + (config_account.clone(), std::string::String::from("new_config_account")), + ], + ); +} + +fn build_config_store_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + config_account: &kb_model::Pubkey, + config_account_is_signer: bool, + keys: &[crate::ConfigKey], + data: &[u8], +) -> kb_core::Result { + let config_address = match crate::parse_pubkey(config_account, "config_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut instruction_data = match encode_config_keys(keys) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let total_length = match instruction_data.len().checked_add(data.len()) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_config_store_data_overflow", + "Config store instruction data length overflows usize", + )); + }, + }; + if total_length > MAX_CONFIG_INSTRUCTION_DATA_BYTES { + return std::result::Result::Err(kb_core::Error::new( + "execution_config_store_data_too_large", + format!( + "Config store instruction data must not exceed {MAX_CONFIG_INSTRUCTION_DATA_BYTES} bytes" + ), + )); + } + instruction_data.extend_from_slice(data); + let mut accounts = + vec![solana_instruction::AccountMeta::new(config_address, config_account_is_signer)]; + let mut signer_roles = std::vec::Vec::new(); + if config_account_is_signer { + signer_roles + .push((config_account.clone(), std::string::String::from("config_account_authority"))); + } + for key in keys { + if !key.is_signer || key.pubkey == *config_account { + continue; + } + let key_address = match crate::parse_pubkey(&key.pubkey, "config_key") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + accounts.push(solana_instruction::AccountMeta::new(key_address, true)); + signer_roles.push((key.pubkey.clone(), std::string::String::from("config_key_authority"))); + } + if signer_roles.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_config_store_signer_missing", + "Config store requires the config account or at least one declared key signer", + )); + } + let program_id = match crate::parse_native_program_id(kb_program_ids::CONFIG_PROGRAM_ID) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = solana_instruction::Instruction { + program_id, + accounts, + data: instruction_data, + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::CONFIG_STORE_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + signer_roles, + ); +} + +fn build_feature_activate_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + feature_account: &kb_model::Pubkey, + funding_account: &kb_model::Pubkey, + lamports: u64, +) -> kb_core::Result { + if lamports == 0 { + return std::result::Result::Err(kb_core::Error::new( + "execution_feature_activation_lamports_zero", + "feature activation requires a positive rent-exempt lamport amount", + )); + } + if feature_account == funding_account { + return std::result::Result::Err(kb_core::Error::new( + "execution_feature_activation_same_account", + "feature and funding accounts must differ", + )); + } + let feature_address = match crate::parse_pubkey(feature_account, "feature_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let funding_address = match crate::parse_pubkey(funding_account, "funding_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let feature_program = match crate::parse_native_program_id(kb_program_ids::FEATURE_PROGRAM_ID) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instructions = vec![ + solana_system_interface::instruction::transfer( + &funding_address, + &feature_address, + lamports, + ), + solana_system_interface::instruction::allocate( + &feature_address, + solana_feature_gate_interface::state::Feature::size_of() as u64, + ), + solana_system_interface::instruction::assign(&feature_address, &feature_program), + ]; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::FEATURE_ACTIVATE_OPERATION, + instructions, + lamports, + std::option::Option::None, + vec![ + (funding_account.clone(), std::string::String::from("feature_funding_account")), + (feature_account.clone(), std::string::String::from("new_feature_account")), + ], + ); +} + +fn build_feature_revoke_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + feature_account: &kb_model::Pubkey, +) -> kb_core::Result { + let feature_address = match crate::parse_pubkey(feature_account, "feature_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let feature_program = match crate::parse_native_program_id(kb_program_ids::FEATURE_PROGRAM_ID) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let incinerator = match crate::parse_native_program_id(kb_program_ids::INCINERATOR_PROGRAM_ID) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = solana_instruction::Instruction { + program_id: feature_program, + accounts: vec![ + solana_instruction::AccountMeta::new(feature_address, true), + solana_instruction::AccountMeta::new(incinerator, false), + solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::system_program::id(), + false, + ), + ], + data: vec![0], + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::FEATURE_REVOKE_PENDING_ACTIVATION_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + vec![(feature_account.clone(), std::string::String::from("pending_feature_account"))], + ); +} + +fn encode_config_keys(keys: &[crate::ConfigKey]) -> kb_core::Result> { + if keys.len() > MAX_CONFIG_KEYS { + return std::result::Result::Err(kb_core::Error::new( + "execution_config_keys_too_many", + format!("Config key list must not exceed {MAX_CONFIG_KEYS} entries"), + )); + } + let mut seen = std::collections::BTreeSet::new(); + let mut encoded = encode_compact_u16(keys.len() as u16); + for key in keys { + if !seen.insert(key.pubkey.0.clone()) { + return std::result::Result::Err(kb_core::Error::new( + "execution_config_key_duplicate", + format!("Config key {} is declared more than once", key.pubkey.0), + )); + } + let address = match crate::parse_pubkey(&key.pubkey, "config_key") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + encoded.extend_from_slice(address.as_ref()); + encoded.push(u8::from(key.is_signer)); + } + return std::result::Result::Ok(encoded); +} + +fn encode_compact_u16(value: u16) -> std::vec::Vec { + if value < 128 { + return vec![value as u8]; + } + if value < 16_384 { + return vec![((value & 0x7f) as u8) | 0x80, (value >> 7) as u8]; + } + return vec![ + ((value & 0x7f) as u8) | 0x80, + (((value >> 7) & 0x7f) as u8) | 0x80, + (value >> 14) as u8, + ]; +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; // rust-rules: trait-import + + fn pubkey(value: solana_pubkey::Pubkey) -> kb_model::Pubkey { + return kb_model::Pubkey(value.to_string()); + } + + fn policy(signers: &[kb_model::Pubkey], max_spend: u64) -> kb_execution_api::ExecutionPolicy { + return kb_execution_api::ExecutionPolicy { + authorized_signers: signers.to_vec(), + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(max_spend), + max_fee_lamports: std::option::Option::Some(100_000), + max_compute_unit_price_micro_lamports: std::option::Option::Some(10), + }, + ..kb_execution_api::ExecutionPolicy::default() + }; + } + + #[test] + fn config_store_matches_the_official_wire_contract() { + let config = solana_pubkey::Pubkey::new_unique(); + let authority = solana_pubkey::Pubkey::new_unique(); + let payer = pubkey(solana_pubkey::Pubkey::new_unique()); + let intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("config-store"), + fee_payer: payer.clone(), + policy: policy(&[payer, pubkey(authority)], 0), + operation: crate::SolanaCoreOperation::ConfigStore { + config_account: pubkey(config), + config_account_is_signer: false, + keys: vec![crate::ConfigKey { + pubkey: pubkey(authority), + is_signer: true, + }], + data: vec![9, 8, 7], + }, + }; + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let instruction = &plan.instructions[0]; + let mut expected = vec![1]; + expected.extend_from_slice(authority.as_ref()); + expected.push(1); + expected.extend_from_slice(&[9, 8, 7]); + assert_eq!(instruction.data, expected); + assert_eq!(instruction.accounts.len(), 2); + assert!(instruction.accounts[1].is_signer); + assert!(instruction.accounts[1].is_writable); + assert_eq!(plan.operation_code, crate::CONFIG_STORE_OPERATION); + } + + #[test] + fn config_create_uses_official_space_and_initialization_order() { + let funding = solana_pubkey::Pubkey::new_unique(); + let config = solana_pubkey::Pubkey::new_unique(); + let max_key = solana_pubkey::Pubkey::new_unique(); + let payer = pubkey(solana_pubkey::Pubkey::new_unique()); + let intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("config-create"), + fee_payer: payer.clone(), + policy: policy(&[payer, pubkey(funding), pubkey(config)], 500), + operation: crate::SolanaCoreOperation::ConfigCreateAccount { + from: pubkey(funding), + config_account: pubkey(config), + lamports: 500, + max_config_data_space: 64, + max_keys: vec![crate::ConfigKey { pubkey: pubkey(max_key), is_signer: true }], + initial_data: vec![0, 0, 0, 0], + }, + }; + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(plan.instructions.len(), 2); + assert_eq!(plan.instructions[1].data, vec![0, 0, 0, 0, 0]); + assert_eq!(plan.requested_spend_lamports, 500); + assert_eq!(plan.required_signers.len(), 3); + } + + #[test] + fn feature_operations_match_the_official_instruction_contracts() { + let feature = solana_pubkey::Pubkey::new_unique(); + let funding = solana_pubkey::Pubkey::new_unique(); + let payer = pubkey(solana_pubkey::Pubkey::new_unique()); + let activate = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("feature-activate"), + fee_payer: payer.clone(), + policy: policy(&[payer.clone(), pubkey(feature), pubkey(funding)], 600), + operation: crate::SolanaCoreOperation::FeatureActivate { + feature_account: pubkey(feature), + funding_account: pubkey(funding), + lamports: 600, + }, + }; + let activate_plan = crate::builder_build_prepared_plan(&activate) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(activate_plan.instructions.len(), 3); + assert_eq!(activate_plan.instructions[0].program_id.0, kb_program_ids::SYSTEM_PROGRAM_ID); + assert_eq!(activate_plan.requested_spend_lamports, 600); + + let revoke = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("feature-revoke"), + fee_payer: payer.clone(), + policy: policy(&[payer, pubkey(feature)], 0), + operation: crate::SolanaCoreOperation::FeatureRevokePendingActivation { + feature_account: pubkey(feature), + }, + }; + let revoke_plan = crate::builder_build_prepared_plan(&revoke) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(revoke_plan.instructions[0].data, vec![0]); + assert_eq!(revoke_plan.instructions[0].accounts.len(), 3); + assert_eq!( + revoke_plan.instructions[0].accounts[1].pubkey.0, + kb_program_ids::INCINERATOR_PROGRAM_ID + ); + } + + #[test] + fn malformed_config_and_feature_contracts_are_rejected() { + let same = pubkey(solana_pubkey::Pubkey::new_unique()); + let intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("feature-invalid"), + fee_payer: same.clone(), + policy: policy(std::slice::from_ref(&same), 0), + operation: crate::SolanaCoreOperation::FeatureActivate { + feature_account: same.clone(), + funding_account: same, + lamports: 0, + }, + }; + assert!(crate::builder_build_prepared_plan(&intent).is_err()); + + let config = solana_pubkey::Pubkey::new_unique(); + let payer = pubkey(solana_pubkey::Pubkey::new_unique()); + let no_signer = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("config-no-signer"), + fee_payer: payer.clone(), + policy: policy(&[payer], 0), + operation: crate::SolanaCoreOperation::ConfigStore { + config_account: pubkey(config), + config_account_is_signer: false, + keys: std::vec::Vec::new(), + data: vec![1], + }, + }; + assert!(crate::builder_build_prepared_plan(&no_signer).is_err()); + } + + #[test] + fn official_program_ids_parse() { + assert_eq!( + solana_pubkey::Pubkey::from_str(kb_program_ids::CONFIG_PROGRAM_ID) + .unwrap_or_else(|error| panic!("invalid id: {error}")) + .to_string(), + kb_program_ids::CONFIG_PROGRAM_ID + ); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/precompiles.rs b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/precompiles.rs new file mode 100644 index 0000000..3029f65 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/precompiles.rs @@ -0,0 +1,1247 @@ +// file: kb_executor_solana_core/src/precompiles.rs +// version: 5 + +//! Deterministic native signature-precompile instruction builders. + +use std::str::FromStr; // rust-rules: trait-import + +const MAX_SIGNATURES_PER_INSTRUCTION: usize = 255; +const MAX_OFFSET_ADDRESSABLE_DATA_LENGTH: usize = u16::MAX as usize; + +const ED25519_OFFSETS_START: usize = 2; +const ED25519_OFFSETS_SIZE: usize = 14; +const ED25519_PUBLIC_KEY_SIZE: usize = 32; +const ED25519_SIGNATURE_SIZE: usize = 64; + +const SECP256K1_OFFSETS_START: usize = 1; +const SECP256K1_OFFSETS_SIZE: usize = 11; +const SECP256K1_ETHEREUM_ADDRESS_SIZE: usize = 20; +const SECP256K1_SIGNATURE_SIZE: usize = 64; +const SECP256K1_SIGNATURE_WITH_RECOVERY_SIZE: usize = 65; + +const SECP256R1_OFFSETS_START: usize = 2; +const SECP256R1_OFFSETS_SIZE: usize = 14; +const SECP256R1_PUBLIC_KEY_SIZE: usize = 33; +const SECP256R1_SIGNATURE_SIZE: usize = 64; +const SECP256R1_HALF_ORDER: [u8; 32] = [ + 0x7f, 0xff, 0xff, 0xff, 0x80, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xde, 0x73, 0x7d, 0x56, 0xd3, 0x8b, 0xcf, 0x42, 0x79, 0xdc, 0xe5, 0x61, 0x7e, 0x31, 0x92, 0xa8, +]; + +pub(crate) fn build_prepared_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, +) -> kb_core::Result { + return match &intent.operation { + crate::SolanaCoreOperation::Ed25519VerifyInline { message, signature, public_key } => { + build_ed25519_inline_plan( + intent, + fee_payer, + message.as_slice(), + signature.as_slice(), + public_key.as_slice(), + ) + }, + crate::SolanaCoreOperation::Ed25519VerifyOffsets { offsets, data } => { + build_ed25519_offsets_plan(intent, fee_payer, offsets.as_slice(), data.as_slice()) + }, + crate::SolanaCoreOperation::Secp256k1VerifyInline { + message, + signature, + recovery_id, + ethereum_address, + } => build_secp256k1_inline_plan( + intent, + fee_payer, + message.as_slice(), + signature.as_slice(), + *recovery_id, + ethereum_address.as_slice(), + ), + crate::SolanaCoreOperation::Secp256k1VerifyOffsets { offsets, data } => { + build_secp256k1_offsets_plan(intent, fee_payer, offsets.as_slice(), data.as_slice()) + }, + crate::SolanaCoreOperation::Secp256r1VerifyInline { message, signature, public_key } => { + build_secp256r1_inline_plan( + intent, + fee_payer, + message.as_slice(), + signature.as_slice(), + public_key.as_slice(), + ) + }, + crate::SolanaCoreOperation::Secp256r1VerifyOffsets { offsets, data } => { + build_secp256r1_offsets_plan(intent, fee_payer, offsets.as_slice(), data.as_slice()) + }, + _ => std::result::Result::Err(kb_core::Error::new( + "execution_precompile_operation_mismatch", + "precompile builder received an unrelated Solana core operation", + )), + }; +} + +fn build_ed25519_inline_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + message: &[u8], + signature: &[u8], + public_key: &[u8], +) -> kb_core::Result { + match crate::validate_exact_length( + signature, + ED25519_SIGNATURE_SIZE, + "execution_ed25519_signature_length_invalid", + "Ed25519 signature", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_exact_length( + public_key, + ED25519_PUBLIC_KEY_SIZE, + "execution_ed25519_public_key_length_invalid", + "Ed25519 public key", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let data_start = ED25519_OFFSETS_START + ED25519_OFFSETS_SIZE; + let public_key_offset = data_start; + let signature_offset = public_key_offset + ED25519_PUBLIC_KEY_SIZE; + let message_data_offset = signature_offset + ED25519_SIGNATURE_SIZE; + match validate_inline_message_length(message, message_data_offset) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let offsets = crate::Ed25519VerificationOffsets { + signature_offset: signature_offset as u16, + signature_instruction_index: u16::MAX, + public_key_offset: public_key_offset as u16, + public_key_instruction_index: u16::MAX, + message_data_offset: message_data_offset as u16, + message_data_size: message.len() as u16, + message_instruction_index: u16::MAX, + }; + let mut data = std::vec::Vec::with_capacity(message_data_offset + message.len()); + data.push(1); + data.push(0); + append_ed25519_offsets(&mut data, &offsets); + data.extend_from_slice(public_key); + data.extend_from_slice(signature); + data.extend_from_slice(message); + let instruction = match instruction(kb_program_ids::ED25519_PROGRAM_ID, data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::ED25519_VERIFY_INLINE_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + std::vec::Vec::new(), + ); +} + +fn build_ed25519_offsets_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + offsets: &[crate::Ed25519VerificationOffsets], + appended_data: &[u8], +) -> kb_core::Result { + let total_length = match validate_table_shape( + offsets.len(), + ED25519_OFFSETS_START, + ED25519_OFFSETS_SIZE, + appended_data.len(), + "execution_ed25519_offsets_empty", + "execution_ed25519_offsets_too_many", + "execution_ed25519_instruction_too_large", + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + for offset in offsets { + match validate_ed25519_local_ranges(offset, total_length) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + let mut data = std::vec::Vec::with_capacity(total_length); + data.push(offsets.len() as u8); + data.push(0); + for offset in offsets { + append_ed25519_offsets(&mut data, offset); + } + data.extend_from_slice(appended_data); + let instruction = match instruction(kb_program_ids::ED25519_PROGRAM_ID, data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::ED25519_VERIFY_OFFSETS_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + std::vec::Vec::new(), + ); +} + +fn build_secp256k1_inline_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + message: &[u8], + signature: &[u8], + recovery_id: u8, + ethereum_address: &[u8], +) -> kb_core::Result { + match crate::validate_exact_length( + signature, + SECP256K1_SIGNATURE_SIZE, + "execution_secp256k1_signature_length_invalid", + "secp256k1 signature", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_exact_length( + ethereum_address, + SECP256K1_ETHEREUM_ADDRESS_SIZE, + "execution_secp256k1_ethereum_address_length_invalid", + "secp256k1 Ethereum address", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if recovery_id > 3 { + return std::result::Result::Err(kb_core::Error::new( + "execution_secp256k1_recovery_id_invalid", + "secp256k1 recovery id must be between 0 and 3", + )); + } + let data_start = SECP256K1_OFFSETS_START + SECP256K1_OFFSETS_SIZE; + let ethereum_address_offset = data_start; + let signature_offset = ethereum_address_offset + SECP256K1_ETHEREUM_ADDRESS_SIZE; + let message_data_offset = signature_offset + SECP256K1_SIGNATURE_WITH_RECOVERY_SIZE; + match validate_inline_message_length(message, message_data_offset) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let offsets = crate::Secp256k1VerificationOffsets { + signature_offset: signature_offset as u16, + signature_instruction_index: 0, + ethereum_address_offset: ethereum_address_offset as u16, + ethereum_address_instruction_index: 0, + message_data_offset: message_data_offset as u16, + message_data_size: message.len() as u16, + message_instruction_index: 0, + }; + let mut data = std::vec::Vec::with_capacity(message_data_offset + message.len()); + data.push(1); + append_secp256k1_offsets(&mut data, &offsets); + data.extend_from_slice(ethereum_address); + data.extend_from_slice(signature); + data.push(recovery_id); + data.extend_from_slice(message); + let instruction = match instruction(kb_program_ids::SECP256K1_PROGRAM_ID, data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::SECP256K1_VERIFY_INLINE_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + std::vec::Vec::new(), + ); +} + +fn build_secp256k1_offsets_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + offsets: &[crate::Secp256k1VerificationOffsets], + appended_data: &[u8], +) -> kb_core::Result { + let total_length = match validate_table_shape( + offsets.len(), + SECP256K1_OFFSETS_START, + SECP256K1_OFFSETS_SIZE, + appended_data.len(), + "execution_secp256k1_offsets_empty", + "execution_secp256k1_offsets_too_many", + "execution_secp256k1_instruction_too_large", + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + for offset in offsets { + match validate_secp256k1_local_ranges(offset, total_length) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + let mut data = std::vec::Vec::with_capacity(total_length); + data.push(offsets.len() as u8); + for offset in offsets { + append_secp256k1_offsets(&mut data, offset); + } + data.extend_from_slice(appended_data); + for offset in offsets { + match validate_secp256k1_local_payload(offset, data.as_slice()) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + let instruction = match instruction(kb_program_ids::SECP256K1_PROGRAM_ID, data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::SECP256K1_VERIFY_OFFSETS_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + std::vec::Vec::new(), + ); +} + +fn build_secp256r1_inline_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + message: &[u8], + signature: &[u8], + public_key: &[u8], +) -> kb_core::Result { + match crate::validate_exact_length( + signature, + SECP256R1_SIGNATURE_SIZE, + "execution_secp256r1_signature_length_invalid", + "secp256r1 signature", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match validate_secp256r1_signature(signature) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_exact_length( + public_key, + SECP256R1_PUBLIC_KEY_SIZE, + "execution_secp256r1_public_key_length_invalid", + "secp256r1 compressed public key", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match validate_secp256r1_public_key(public_key) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let data_start = SECP256R1_OFFSETS_START + SECP256R1_OFFSETS_SIZE; + let public_key_offset = data_start; + let signature_offset = public_key_offset + SECP256R1_PUBLIC_KEY_SIZE; + let message_data_offset = signature_offset + SECP256R1_SIGNATURE_SIZE; + match validate_inline_message_length(message, message_data_offset) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let offsets = crate::Secp256r1VerificationOffsets { + signature_offset: signature_offset as u16, + signature_instruction_index: u16::MAX, + public_key_offset: public_key_offset as u16, + public_key_instruction_index: u16::MAX, + message_data_offset: message_data_offset as u16, + message_data_size: message.len() as u16, + message_instruction_index: u16::MAX, + }; + let mut data = std::vec::Vec::with_capacity(message_data_offset + message.len()); + data.push(1); + data.push(0); + append_secp256r1_offsets(&mut data, &offsets); + data.extend_from_slice(public_key); + data.extend_from_slice(signature); + data.extend_from_slice(message); + let instruction = match instruction(kb_program_ids::SECP256R1_PROGRAM_ID, data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::SECP256R1_VERIFY_INLINE_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + std::vec::Vec::new(), + ); +} + +fn build_secp256r1_offsets_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + offsets: &[crate::Secp256r1VerificationOffsets], + appended_data: &[u8], +) -> kb_core::Result { + let total_length = match validate_table_shape( + offsets.len(), + SECP256R1_OFFSETS_START, + SECP256R1_OFFSETS_SIZE, + appended_data.len(), + "execution_secp256r1_offsets_empty", + "execution_secp256r1_offsets_too_many", + "execution_secp256r1_instruction_too_large", + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + for offset in offsets { + match validate_secp256r1_local_ranges(offset, total_length) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + let mut data = std::vec::Vec::with_capacity(total_length); + data.push(offsets.len() as u8); + data.push(0); + for offset in offsets { + append_secp256r1_offsets(&mut data, offset); + } + data.extend_from_slice(appended_data); + for offset in offsets { + match validate_secp256r1_local_payload(offset, data.as_slice()) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + let instruction = match instruction(kb_program_ids::SECP256R1_PROGRAM_ID, data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::SECP256R1_VERIFY_OFFSETS_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + std::vec::Vec::new(), + ); +} + +fn instruction( + program_id: &str, + data: std::vec::Vec, +) -> kb_core::Result { + let parsed_program_id = match solana_pubkey::Pubkey::from_str(program_id) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_precompile_program_id_invalid", + format!("invalid native precompile program id {program_id}: {error}"), + )); + }, + }; + return std::result::Result::Ok(solana_instruction::Instruction { + program_id: parsed_program_id, + accounts: std::vec::Vec::new(), + data, + }); +} + +fn validate_inline_message_length(message: &[u8], prefix_length: usize) -> kb_core::Result<()> { + let total_length = match prefix_length.checked_add(message.len()) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_precompile_message_length_overflow", + "precompile message length overflows the addressable instruction size", + )); + }, + }; + if message.len() > u16::MAX as usize || total_length > MAX_OFFSET_ADDRESSABLE_DATA_LENGTH { + return std::result::Result::Err(kb_core::Error::new( + "execution_precompile_message_too_large", + format!( + "precompile message and inline payload must fit within {MAX_OFFSET_ADDRESSABLE_DATA_LENGTH} bytes" + ), + )); + } + return std::result::Result::Ok(()); +} + +fn validate_table_shape( + count: usize, + offsets_start: usize, + offsets_size: usize, + appended_data_length: usize, + empty_code: &str, + count_code: &str, + size_code: &str, +) -> kb_core::Result { + if count == 0 { + return std::result::Result::Err(kb_core::Error::new( + empty_code, + "precompile offset table requires at least one signature record", + )); + } + if count > MAX_SIGNATURES_PER_INSTRUCTION { + return std::result::Result::Err(kb_core::Error::new( + count_code, + format!( + "precompile offset table cannot contain more than {MAX_SIGNATURES_PER_INSTRUCTION} records" + ), + )); + } + let offsets_length = match offsets_size.checked_mul(count) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + size_code, + "precompile offset table length overflow", + )); + }, + }; + let header_length = match offsets_start.checked_add(offsets_length) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + size_code, + "precompile offset header length overflow", + )); + }, + }; + let total_length = match header_length.checked_add(appended_data_length) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + size_code, + "precompile instruction length overflow", + )); + }, + }; + if total_length > MAX_OFFSET_ADDRESSABLE_DATA_LENGTH { + return std::result::Result::Err(kb_core::Error::new( + size_code, + format!( + "precompile instruction data must not exceed {MAX_OFFSET_ADDRESSABLE_DATA_LENGTH} bytes" + ), + )); + } + return std::result::Result::Ok(total_length); +} + +fn validate_ed25519_local_ranges( + offsets: &crate::Ed25519VerificationOffsets, + total_length: usize, +) -> kb_core::Result<()> { + if offsets.signature_instruction_index == u16::MAX { + match validate_local_range( + offsets.signature_offset, + ED25519_SIGNATURE_SIZE, + total_length, + "execution_ed25519_signature_offset_out_of_bounds", + "Ed25519 signature", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + if offsets.public_key_instruction_index == u16::MAX { + match validate_local_range( + offsets.public_key_offset, + ED25519_PUBLIC_KEY_SIZE, + total_length, + "execution_ed25519_public_key_offset_out_of_bounds", + "Ed25519 public key", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + if offsets.message_instruction_index == u16::MAX { + match validate_local_range( + offsets.message_data_offset, + offsets.message_data_size as usize, + total_length, + "execution_ed25519_message_offset_out_of_bounds", + "Ed25519 message", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + return std::result::Result::Ok(()); +} + +fn validate_secp256k1_local_ranges( + offsets: &crate::Secp256k1VerificationOffsets, + total_length: usize, +) -> kb_core::Result<()> { + if offsets.signature_instruction_index == 0 { + match validate_local_range( + offsets.signature_offset, + SECP256K1_SIGNATURE_WITH_RECOVERY_SIZE, + total_length, + "execution_secp256k1_signature_offset_out_of_bounds", + "secp256k1 signature and recovery id", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + if offsets.ethereum_address_instruction_index == 0 { + match validate_local_range( + offsets.ethereum_address_offset, + SECP256K1_ETHEREUM_ADDRESS_SIZE, + total_length, + "execution_secp256k1_ethereum_address_offset_out_of_bounds", + "secp256k1 Ethereum address", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + if offsets.message_instruction_index == 0 { + match validate_local_range( + offsets.message_data_offset, + offsets.message_data_size as usize, + total_length, + "execution_secp256k1_message_offset_out_of_bounds", + "secp256k1 message", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + return std::result::Result::Ok(()); +} + +fn validate_secp256r1_local_ranges( + offsets: &crate::Secp256r1VerificationOffsets, + total_length: usize, +) -> kb_core::Result<()> { + if offsets.signature_instruction_index == u16::MAX { + match validate_local_range( + offsets.signature_offset, + SECP256R1_SIGNATURE_SIZE, + total_length, + "execution_secp256r1_signature_offset_out_of_bounds", + "secp256r1 signature", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + if offsets.public_key_instruction_index == u16::MAX { + match validate_local_range( + offsets.public_key_offset, + SECP256R1_PUBLIC_KEY_SIZE, + total_length, + "execution_secp256r1_public_key_offset_out_of_bounds", + "secp256r1 public key", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + if offsets.message_instruction_index == u16::MAX { + match validate_local_range( + offsets.message_data_offset, + offsets.message_data_size as usize, + total_length, + "execution_secp256r1_message_offset_out_of_bounds", + "secp256r1 message", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + return std::result::Result::Ok(()); +} + +fn validate_local_range( + offset: u16, + length: usize, + total_length: usize, + code: &str, + label: &str, +) -> kb_core::Result<()> { + let start = offset as usize; + let end = match start.checked_add(length) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + code, + format!("{label} range overflows the instruction data"), + )); + }, + }; + if end > total_length { + return std::result::Result::Err(kb_core::Error::new( + code, + format!("{label} range exceeds the generated instruction data"), + )); + } + return std::result::Result::Ok(()); +} + +fn validate_secp256k1_local_payload( + offsets: &crate::Secp256k1VerificationOffsets, + data: &[u8], +) -> kb_core::Result<()> { + if offsets.signature_instruction_index != 0 { + return std::result::Result::Ok(()); + } + let recovery_offset = + match (offsets.signature_offset as usize).checked_add(SECP256K1_SIGNATURE_SIZE) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_secp256k1_signature_offset_out_of_bounds", + "secp256k1 recovery id offset overflows the instruction data", + )); + }, + }; + let recovery_id = match data.get(recovery_offset) { + std::option::Option::Some(value) => *value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_secp256k1_signature_offset_out_of_bounds", + "secp256k1 recovery id exceeds the generated instruction data", + )); + }, + }; + if recovery_id > 3 { + return std::result::Result::Err(kb_core::Error::new( + "execution_secp256k1_recovery_id_invalid", + "secp256k1 recovery id must be between 0 and 3", + )); + } + return std::result::Result::Ok(()); +} + +fn validate_secp256r1_local_payload( + offsets: &crate::Secp256r1VerificationOffsets, + data: &[u8], +) -> kb_core::Result<()> { + if offsets.signature_instruction_index == u16::MAX { + let start = offsets.signature_offset as usize; + let end = match start.checked_add(SECP256R1_SIGNATURE_SIZE) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_secp256r1_signature_offset_out_of_bounds", + "secp256r1 signature range overflows the instruction data", + )); + }, + }; + let signature = match data.get(start..end) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_secp256r1_signature_offset_out_of_bounds", + "secp256r1 signature exceeds the generated instruction data", + )); + }, + }; + match validate_secp256r1_signature(signature) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + if offsets.public_key_instruction_index == u16::MAX { + let start = offsets.public_key_offset as usize; + let end = match start.checked_add(SECP256R1_PUBLIC_KEY_SIZE) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_secp256r1_public_key_offset_out_of_bounds", + "secp256r1 public key range overflows the instruction data", + )); + }, + }; + let public_key = match data.get(start..end) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_secp256r1_public_key_offset_out_of_bounds", + "secp256r1 public key exceeds the generated instruction data", + )); + }, + }; + match validate_secp256r1_public_key(public_key) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + return std::result::Result::Ok(()); +} + +fn validate_secp256r1_public_key(public_key: &[u8]) -> kb_core::Result<()> { + let prefix = match public_key.first() { + std::option::Option::Some(value) => *value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_secp256r1_public_key_length_invalid", + "secp256r1 compressed public key must contain 33 bytes", + )); + }, + }; + if prefix != 2 && prefix != 3 { + return std::result::Result::Err(kb_core::Error::new( + "execution_secp256r1_public_key_prefix_invalid", + "secp256r1 compressed public key must start with 0x02 or 0x03", + )); + } + return std::result::Result::Ok(()); +} + +fn validate_secp256r1_signature(signature: &[u8]) -> kb_core::Result<()> { + let r = &signature[..32]; + let s = &signature[32..]; + if r.iter().all(|value| return *value == 0) || s.iter().all(|value| return *value == 0) { + return std::result::Result::Err(kb_core::Error::new( + "execution_secp256r1_signature_zero_component", + "secp256r1 signature components r and s must both be non-zero", + )); + } + if s > SECP256R1_HALF_ORDER.as_slice() { + return std::result::Result::Err(kb_core::Error::new( + "execution_secp256r1_signature_not_low_s", + "secp256r1 signature must use the runtime-required low-S canonical form", + )); + } + return std::result::Result::Ok(()); +} + +fn append_ed25519_offsets( + data: &mut std::vec::Vec, + offsets: &crate::Ed25519VerificationOffsets, +) { + append_u16_signature_offsets( + data, + [ + offsets.signature_offset, + offsets.signature_instruction_index, + offsets.public_key_offset, + offsets.public_key_instruction_index, + offsets.message_data_offset, + offsets.message_data_size, + offsets.message_instruction_index, + ], + ); + return; +} + +fn append_secp256r1_offsets( + data: &mut std::vec::Vec, + offsets: &crate::Secp256r1VerificationOffsets, +) { + append_u16_signature_offsets( + data, + [ + offsets.signature_offset, + offsets.signature_instruction_index, + offsets.public_key_offset, + offsets.public_key_instruction_index, + offsets.message_data_offset, + offsets.message_data_size, + offsets.message_instruction_index, + ], + ); + return; +} + +fn append_u16_signature_offsets(data: &mut std::vec::Vec, offsets: [u16; 7]) { + for value in offsets { + append_u16(data, value); + } + return; +} + +fn append_secp256k1_offsets( + data: &mut std::vec::Vec, + offsets: &crate::Secp256k1VerificationOffsets, +) { + append_u16(data, offsets.signature_offset); + data.push(offsets.signature_instruction_index); + append_u16(data, offsets.ethereum_address_offset); + data.push(offsets.ethereum_address_instruction_index); + append_u16(data, offsets.message_data_offset); + append_u16(data, offsets.message_data_size); + data.push(offsets.message_instruction_index); + return; +} + +fn append_u16(data: &mut std::vec::Vec, value: u16) { + data.extend_from_slice(&value.to_le_bytes()); + return; +} + +#[cfg(test)] +mod tests { + fn pubkey(value: &str) -> kb_model::Pubkey { + return kb_model::Pubkey(std::string::String::from(value)); + } + + fn policy() -> kb_execution_api::ExecutionPolicy { + let fee_payer = pubkey(kb_program_ids::VOTE_PROGRAM_ID); + return kb_execution_api::ExecutionPolicy { + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(0), + max_fee_lamports: std::option::Option::Some(100_000), + max_compute_unit_price_micro_lamports: std::option::Option::Some(100), + }, + authorized_signers: vec![fee_payer], + ..kb_execution_api::ExecutionPolicy::default() + }; + } + + fn intent(operation: crate::SolanaCoreOperation) -> crate::SolanaCoreExecutionIntent { + return crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("precompile-test"), + fee_payer: pubkey(kb_program_ids::VOTE_PROGRAM_ID), + policy: policy(), + operation, + }; + } + + fn assert_matches_instruction( + plan: &kb_execution_api::PreparedExecutionPlan, + expected: &solana_instruction::Instruction, + ) { + assert_eq!(plan.instructions.len(), 1); + assert_eq!(plan.instructions[0].program_id.0, expected.program_id.to_string()); + assert_eq!(plan.instructions[0].data, expected.data); + assert!(plan.instructions[0].accounts.is_empty()); + assert_eq!(plan.required_signers.len(), 1); + assert_eq!(plan.required_signers[0].pubkey.0, kb_program_ids::VOTE_PROGRAM_ID); + assert_eq!(plan.requested_spend_lamports, 0); + return; + } + + #[test] + fn ed25519_and_secp256r1_offset_adapters_share_wire_without_sharing_types() { + let ed25519_offsets = crate::Ed25519VerificationOffsets { + signature_offset: 1, + signature_instruction_index: 2, + public_key_offset: 3, + public_key_instruction_index: 4, + message_data_offset: 5, + message_data_size: 6, + message_instruction_index: 7, + }; + let secp256r1_offsets = crate::Secp256r1VerificationOffsets { + signature_offset: 1, + signature_instruction_index: 2, + public_key_offset: 3, + public_key_instruction_index: 4, + message_data_offset: 5, + message_data_size: 6, + message_instruction_index: 7, + }; + let mut ed25519_data = std::vec::Vec::new(); + let mut secp256r1_data = std::vec::Vec::new(); + super::append_ed25519_offsets(&mut ed25519_data, &ed25519_offsets); + super::append_secp256r1_offsets(&mut secp256r1_data, &secp256r1_offsets); + assert_eq!(ed25519_data, secp256r1_data); + assert_eq!(ed25519_data, vec![1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0]); + } + + #[test] + fn inline_precompile_operations_match_official_builders_and_layout() { + let message = b"khadhroony-precompile".to_vec(); + let ed_signature = [7u8; 64]; + let ed_public_key = [9u8; 32]; + let ed_plan = crate::builder_build_prepared_plan(&intent( + crate::SolanaCoreOperation::Ed25519VerifyInline { + message: message.clone(), + signature: ed_signature.to_vec(), + public_key: ed_public_key.to_vec(), + }, + )) + .unwrap_or_else(|error| panic!("unexpected Ed25519 error: {error}")); + let ed_expected = solana_ed25519_program::new_ed25519_instruction_with_signature( + message.as_slice(), + &ed_signature, + &ed_public_key, + ); + assert_matches_instruction(&ed_plan, &ed_expected); + assert_eq!(ed_plan.operation_code, crate::ED25519_VERIFY_INLINE_OPERATION); + + let mut r1_signature = [1u8; 64]; + r1_signature[32] = 0x01; + let mut r1_public_key = [3u8; 33]; + r1_public_key[0] = 2; + let r1_plan = crate::builder_build_prepared_plan(&intent( + crate::SolanaCoreOperation::Secp256r1VerifyInline { + message: message.clone(), + signature: r1_signature.to_vec(), + public_key: r1_public_key.to_vec(), + }, + )) + .unwrap_or_else(|error| panic!("unexpected secp256r1 error: {error}")); + let r1_expected = solana_secp256r1_program::new_secp256r1_instruction_with_signature( + message.as_slice(), + &r1_signature, + &r1_public_key, + ); + assert_matches_instruction(&r1_plan, &r1_expected); + assert_eq!(r1_plan.operation_code, crate::SECP256R1_VERIFY_INLINE_OPERATION); + + let k1_signature = [5u8; 64]; + let k1_address = [11u8; 20]; + let k1_plan = crate::builder_build_prepared_plan(&intent( + crate::SolanaCoreOperation::Secp256k1VerifyInline { + message: message.clone(), + signature: k1_signature.to_vec(), + recovery_id: 1, + ethereum_address: k1_address.to_vec(), + }, + )) + .unwrap_or_else(|error| panic!("unexpected secp256k1 error: {error}")); + assert_eq!(k1_plan.instructions[0].program_id.0, kb_program_ids::SECP256K1_PROGRAM_ID); + assert_eq!(k1_plan.instructions[0].data[0], 1); + assert_eq!( + &k1_plan.instructions[0].data[1..12], + &[32, 0, 0, 12, 0, 0, 97, 0, message.len() as u8, 0, 0] + ); + assert_eq!(&k1_plan.instructions[0].data[12..32], &k1_address); + assert_eq!(&k1_plan.instructions[0].data[32..96], &k1_signature); + assert_eq!(k1_plan.instructions[0].data[96], 1); + assert_eq!(&k1_plan.instructions[0].data[97..], message.as_slice()); + assert_eq!( + solana_secp256k1_program::SIGNATURE_OFFSETS_SERIALIZED_SIZE, + super::SECP256K1_OFFSETS_SIZE + ); + assert_eq!( + solana_secp256k1_program::HASHED_PUBKEY_SERIALIZED_SIZE, + super::SECP256K1_ETHEREUM_ADDRESS_SIZE + ); + } + + #[test] + fn offset_precompile_operations_preserve_official_tables_and_external_references() { + let ed_offsets = crate::Ed25519VerificationOffsets { + signature_offset: 0, + signature_instruction_index: 2, + public_key_offset: 64, + public_key_instruction_index: 2, + message_data_offset: 96, + message_data_size: 5, + message_instruction_index: 2, + }; + let ed_plan = crate::builder_build_prepared_plan(&intent( + crate::SolanaCoreOperation::Ed25519VerifyOffsets { + offsets: vec![ed_offsets], + data: vec![1, 2, 3], + }, + )) + .unwrap_or_else(|error| panic!("unexpected Ed25519 offsets error: {error}")); + let official_ed_offsets = solana_ed25519_program::Ed25519SignatureOffsets { + signature_offset: 0, + signature_instruction_index: 2, + public_key_offset: 64, + public_key_instruction_index: 2, + message_data_offset: 96, + message_data_size: 5, + message_instruction_index: 2, + }; + let mut ed_expected = + solana_ed25519_program::offsets_to_ed25519_instruction(&[official_ed_offsets]); + ed_expected.data.extend_from_slice(&[1, 2, 3]); + assert_matches_instruction(&ed_plan, &ed_expected); + + let k1_offsets = crate::Secp256k1VerificationOffsets { + signature_offset: 9, + signature_instruction_index: 3, + ethereum_address_offset: 74, + ethereum_address_instruction_index: 3, + message_data_offset: 94, + message_data_size: 12, + message_instruction_index: 4, + }; + let k1_plan = crate::builder_build_prepared_plan(&intent( + crate::SolanaCoreOperation::Secp256k1VerifyOffsets { + offsets: vec![k1_offsets], + data: vec![8, 9], + }, + )) + .unwrap_or_else(|error| panic!("unexpected secp256k1 offsets error: {error}")); + assert_eq!(k1_plan.instructions[0].data, vec![1, 9, 0, 3, 74, 0, 3, 94, 0, 12, 0, 4, 8, 9]); + let official_k1_offsets = solana_secp256k1_program::SecpSignatureOffsets { + signature_offset: 9, + signature_instruction_index: 3, + eth_address_offset: 74, + eth_address_instruction_index: 3, + message_data_offset: 94, + message_data_size: 12, + message_instruction_index: 4, + }; + assert_eq!(official_k1_offsets.signature_offset, k1_offsets.signature_offset); + assert_eq!( + official_k1_offsets.eth_address_instruction_index, + k1_offsets.ethereum_address_instruction_index + ); + + let r1_offsets = crate::Secp256r1VerificationOffsets { + signature_offset: 0, + signature_instruction_index: 5, + public_key_offset: 64, + public_key_instruction_index: 5, + message_data_offset: 97, + message_data_size: 7, + message_instruction_index: 6, + }; + let r1_plan = crate::builder_build_prepared_plan(&intent( + crate::SolanaCoreOperation::Secp256r1VerifyOffsets { + offsets: vec![r1_offsets], + data: vec![4, 5], + }, + )) + .unwrap_or_else(|error| panic!("unexpected secp256r1 offsets error: {error}")); + assert_eq!( + r1_plan.instructions[0].data, + vec![1, 0, 0, 0, 5, 0, 64, 0, 5, 0, 97, 0, 7, 0, 6, 0, 4, 5] + ); + let official_r1_offsets = solana_secp256r1_program::Secp256r1SignatureOffsets { + signature_offset: 0, + signature_instruction_index: 5, + public_key_offset: 64, + public_key_instruction_index: 5, + message_data_offset: 97, + message_data_size: 7, + message_instruction_index: 6, + }; + assert_eq!(official_r1_offsets.message_data_size, r1_offsets.message_data_size); + } + + #[test] + fn malformed_precompile_contracts_are_rejected_before_simulation() { + let short_signature = crate::builder_build_prepared_plan(&intent( + crate::SolanaCoreOperation::Ed25519VerifyInline { + message: std::vec::Vec::new(), + signature: vec![0; 63], + public_key: vec![0; 32], + }, + )) + .expect_err("short Ed25519 signature must fail"); + assert_eq!(short_signature.code(), "execution_ed25519_signature_length_invalid"); + + let bad_recovery = crate::builder_build_prepared_plan(&intent( + crate::SolanaCoreOperation::Secp256k1VerifyInline { + message: std::vec::Vec::new(), + signature: vec![1; 64], + recovery_id: 4, + ethereum_address: vec![2; 20], + }, + )) + .expect_err("invalid recovery id must fail"); + assert_eq!(bad_recovery.code(), "execution_secp256k1_recovery_id_invalid"); + + let high_s = crate::builder_build_prepared_plan(&intent( + crate::SolanaCoreOperation::Secp256r1VerifyInline { + message: std::vec::Vec::new(), + signature: vec![0xff; 64], + public_key: { + let mut value = vec![1; 33]; + value[0] = 2; + value + }, + }, + )) + .expect_err("high-S secp256r1 signature must fail"); + assert_eq!(high_s.code(), "execution_secp256r1_signature_not_low_s"); + + let empty_offsets = crate::builder_build_prepared_plan(&intent( + crate::SolanaCoreOperation::Ed25519VerifyOffsets { + offsets: std::vec::Vec::new(), + data: std::vec::Vec::new(), + }, + )) + .expect_err("empty offset table must fail"); + assert_eq!(empty_offsets.code(), "execution_ed25519_offsets_empty"); + + let template = crate::Ed25519VerificationOffsets { + signature_offset: 0, + signature_instruction_index: 1, + public_key_offset: 0, + public_key_instruction_index: 1, + message_data_offset: 0, + message_data_size: 0, + message_instruction_index: 1, + }; + let too_many = crate::builder_build_prepared_plan(&intent( + crate::SolanaCoreOperation::Ed25519VerifyOffsets { + offsets: vec![template; 256], + data: std::vec::Vec::new(), + }, + )) + .expect_err("more than 255 signatures must fail"); + assert_eq!(too_many.code(), "execution_ed25519_offsets_too_many"); + + let local_out_of_bounds = crate::builder_build_prepared_plan(&intent( + crate::SolanaCoreOperation::Secp256k1VerifyOffsets { + offsets: vec![crate::Secp256k1VerificationOffsets { + signature_offset: 500, + signature_instruction_index: 0, + ethereum_address_offset: 0, + ethereum_address_instruction_index: 2, + message_data_offset: 0, + message_data_size: 0, + message_instruction_index: 2, + }], + data: std::vec::Vec::new(), + }, + )) + .expect_err("self-referenced out-of-bounds signature must fail"); + assert_eq!( + local_out_of_bounds.code(), + "execution_secp256k1_signature_offset_out_of_bounds" + ); + + let mut invalid_local_k1 = vec![0u8; 65]; + invalid_local_k1[64] = 4; + let invalid_local_k1 = crate::builder_build_prepared_plan(&intent( + crate::SolanaCoreOperation::Secp256k1VerifyOffsets { + offsets: vec![crate::Secp256k1VerificationOffsets { + signature_offset: 12, + signature_instruction_index: 0, + ethereum_address_offset: 0, + ethereum_address_instruction_index: 2, + message_data_offset: 0, + message_data_size: 0, + message_instruction_index: 2, + }], + data: invalid_local_k1, + }, + )) + .expect_err("invalid local secp256k1 recovery id must fail"); + assert_eq!(invalid_local_k1.code(), "execution_secp256k1_recovery_id_invalid"); + + let invalid_local_r1 = crate::builder_build_prepared_plan(&intent( + crate::SolanaCoreOperation::Secp256r1VerifyOffsets { + offsets: vec![crate::Secp256r1VerificationOffsets { + signature_offset: 16, + signature_instruction_index: u16::MAX, + public_key_offset: 0, + public_key_instruction_index: 2, + message_data_offset: 0, + message_data_size: 0, + message_instruction_index: 2, + }], + data: vec![0xff; 64], + }, + )) + .expect_err("high-S local secp256r1 signature must fail"); + assert_eq!(invalid_local_r1.code(), "execution_secp256r1_signature_not_low_s"); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/slashing.rs b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/slashing.rs new file mode 100644 index 0000000..a072f4e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/slashing.rs @@ -0,0 +1,489 @@ +// file: kb_executor_solana_core/src/slashing.rs +// version: 4 + +//! Stateless Slashing Program builders and the atomic duplicate-block proof plan. + +const HASH_BYTES: usize = 32; +const SIGNATURE_BYTES: usize = 64; +const DUPLICATE_BLOCK_PROOF_DATA_BYTES: usize = 305; +const DUPLICATE_BLOCK_PROOF_TYPE: u8 = 1; + +pub(crate) fn build_prepared_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, +) -> kb_core::Result { + return match &intent.operation { + crate::SolanaCoreOperation::SlashingCloseViolationReport { + report_account, + destination, + } => build_close_plan(intent, fee_payer, report_account, destination), + crate::SolanaCoreOperation::SlashingSubmitDuplicateBlockProof { + proof_account, + report_account, + funder, + report_lamports, + proof_offset, + slot, + node_pubkey, + reporter, + destination, + shred_1_merkle_root, + shred_1_signature, + shred_2_merkle_root, + shred_2_signature, + } => build_duplicate_block_plan( + intent, + fee_payer, + proof_account, + report_account, + funder.as_ref(), + *report_lamports, + *proof_offset, + *slot, + node_pubkey, + reporter, + destination, + shred_1_merkle_root.as_slice(), + shred_1_signature.as_slice(), + shred_2_merkle_root.as_slice(), + shred_2_signature.as_slice(), + ), + _ => std::result::Result::Err(kb_core::Error::new( + "execution_slashing_operation_mismatch", + "Slashing builder received an unrelated Solana core operation", + )), + }; +} + +fn build_close_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + report_account: &kb_model::Pubkey, + destination: &kb_model::Pubkey, +) -> kb_core::Result { + if report_account == destination { + return std::result::Result::Err(kb_core::Error::new( + "execution_slashing_close_same_account", + "Slashing report and destination accounts must differ", + )); + } + let report = match crate::parse_pubkey(report_account, "report_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let destination_address = match crate::parse_pubkey(destination, "destination") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let program_id = match crate::parse_native_program_id(kb_program_ids::SLASHING_PROGRAM_ID) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = solana_instruction::Instruction { + program_id, + accounts: vec![ + solana_instruction::AccountMeta::new(report, false), + solana_instruction::AccountMeta::new(destination_address, false), + ], + data: vec![0], + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::SLASHING_CLOSE_VIOLATION_REPORT_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + std::vec::Vec::new(), + ); +} + +#[allow(clippy::too_many_arguments)] +fn build_duplicate_block_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + proof_account: &kb_model::Pubkey, + report_account: &kb_model::Pubkey, + funder: std::option::Option<&kb_model::Pubkey>, + report_lamports: u64, + proof_offset: u64, + slot: u64, + node_pubkey: &kb_model::Pubkey, + reporter: &kb_model::Pubkey, + destination: &kb_model::Pubkey, + shred_1_merkle_root: &[u8], + shred_1_signature: &[u8], + shred_2_merkle_root: &[u8], + shred_2_signature: &[u8], +) -> kb_core::Result { + if intent.policy.blockhash.kind != kb_execution_api::ExecutionBlockhashKind::Latest { + return std::result::Result::Err(kb_core::Error::new( + "execution_slashing_duplicate_block_requires_latest_blockhash", + "duplicate-block proof submission must remain the first three transaction instructions and cannot use durable-nonce prepending", + )); + } + if report_lamports == 0 { + return std::result::Result::Err(kb_core::Error::new( + "execution_slashing_report_lamports_zero", + "duplicate-block proof submission requires a positive measured report-account rent amount", + )); + } + if report_account == destination { + return std::result::Result::Err(kb_core::Error::new( + "execution_slashing_destination_is_report", + "Slashing report PDA and close destination must differ", + )); + } + match crate::validate_exact_length( + shred_1_merkle_root, + HASH_BYTES, + "execution_slashing_first_root_length_invalid", + "first shred Merkle root", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_exact_length( + shred_2_merkle_root, + HASH_BYTES, + "execution_slashing_second_root_length_invalid", + "second shred Merkle root", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_exact_length( + shred_1_signature, + SIGNATURE_BYTES, + "execution_slashing_first_signature_length_invalid", + "first shred signature", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_exact_length( + shred_2_signature, + SIGNATURE_BYTES, + "execution_slashing_second_signature_length_invalid", + "second shred signature", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let proof = match crate::parse_pubkey(proof_account, "proof_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let report = match crate::parse_pubkey(report_account, "report_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let node = match crate::parse_pubkey(node_pubkey, "node_pubkey") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let reporter_address = match crate::parse_pubkey(reporter, "reporter") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let destination_address = match crate::parse_pubkey(destination, "destination") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let funding_key = match funder { + std::option::Option::Some(value) => value, + std::option::Option::None => reporter, + }; + let funding_address = match crate::parse_pubkey(funding_key, "funder") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let slashing_program = match crate::parse_native_program_id(kb_program_ids::SLASHING_PROGRAM_ID) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let slot_bytes = slot.to_le_bytes(); + let proof_type = [DUPLICATE_BLOCK_PROOF_TYPE]; + let (derived_report, _bump) = solana_pubkey::Pubkey::find_program_address( + &[node.as_ref(), slot_bytes.as_slice(), proof_type.as_slice()], + &slashing_program, + ); + if derived_report != report { + return std::result::Result::Err(kb_core::Error::new( + "execution_slashing_report_pda_mismatch", + format!("supplied report account {report} does not match derived PDA {derived_report}"), + )); + } + if funding_address == report { + return std::result::Result::Err(kb_core::Error::new( + "execution_slashing_funder_is_report", + "Slashing report PDA cannot fund itself", + )); + } + let slashing_data = duplicate_block_instruction_data( + proof_offset, + slot, + &node, + &reporter_address, + &destination_address, + shred_1_merkle_root, + shred_1_signature, + shred_2_merkle_root, + shred_2_signature, + ); + if slashing_data.len() != DUPLICATE_BLOCK_PROOF_DATA_BYTES { + return std::result::Result::Err(kb_core::Error::new( + "execution_slashing_duplicate_block_internal_size_invalid", + "constructed duplicate-block instruction data has an invalid size", + )); + } + let transfer = + solana_system_interface::instruction::transfer(&funding_address, &report, report_lamports); + let sigverify = match duplicate_block_sigverify_instruction() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let slashing = solana_instruction::Instruction { + program_id: slashing_program, + accounts: vec![ + solana_instruction::AccountMeta::new_readonly(proof, false), + solana_instruction::AccountMeta::new(report, false), + solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::sysvar::instructions::id(), + false, + ), + solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::system_program::id(), + false, + ), + ], + data: slashing_data, + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::SLASHING_SUBMIT_DUPLICATE_BLOCK_PROOF_OPERATION, + vec![transfer, sigverify, slashing], + report_lamports, + std::option::Option::None, + vec![(funding_key.clone(), std::string::String::from("slashing_report_funder"))], + ); +} + +#[allow(clippy::too_many_arguments)] +fn duplicate_block_instruction_data( + proof_offset: u64, + slot: u64, + node: &solana_pubkey::Pubkey, + reporter: &solana_pubkey::Pubkey, + destination: &solana_pubkey::Pubkey, + shred_1_merkle_root: &[u8], + shred_1_signature: &[u8], + shred_2_merkle_root: &[u8], + shred_2_signature: &[u8], +) -> std::vec::Vec { + let mut data = std::vec::Vec::with_capacity(DUPLICATE_BLOCK_PROOF_DATA_BYTES); + data.push(1); + data.extend_from_slice(&proof_offset.to_le_bytes()); + data.extend_from_slice(&slot.to_le_bytes()); + data.extend_from_slice(node.as_ref()); + data.extend_from_slice(reporter.as_ref()); + data.extend_from_slice(destination.as_ref()); + data.extend_from_slice(shred_1_merkle_root); + data.extend_from_slice(shred_1_signature); + data.extend_from_slice(shred_2_merkle_root); + data.extend_from_slice(shred_2_signature); + return data; +} + +fn duplicate_block_sigverify_instruction() -> kb_core::Result { + let records = [ + crate::Ed25519VerificationOffsets { + signature_offset: 145, + signature_instruction_index: 2, + public_key_offset: 17, + public_key_instruction_index: 2, + message_data_offset: 113, + message_data_size: HASH_BYTES as u16, + message_instruction_index: 2, + }, + crate::Ed25519VerificationOffsets { + signature_offset: 241, + signature_instruction_index: 2, + public_key_offset: 17, + public_key_instruction_index: 2, + message_data_offset: 209, + message_data_size: HASH_BYTES as u16, + message_instruction_index: 2, + }, + ]; + let mut data = vec![2, 0]; + for record in records { + data.extend_from_slice(&record.signature_offset.to_le_bytes()); + data.extend_from_slice(&record.signature_instruction_index.to_le_bytes()); + data.extend_from_slice(&record.public_key_offset.to_le_bytes()); + data.extend_from_slice(&record.public_key_instruction_index.to_le_bytes()); + data.extend_from_slice(&record.message_data_offset.to_le_bytes()); + data.extend_from_slice(&record.message_data_size.to_le_bytes()); + data.extend_from_slice(&record.message_instruction_index.to_le_bytes()); + } + let program_id = match crate::parse_native_program_id(kb_program_ids::ED25519_PROGRAM_ID) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(solana_instruction::Instruction { + program_id, + accounts: std::vec::Vec::new(), + data, + }); +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; // rust-rules: trait-import + + fn model(value: solana_pubkey::Pubkey) -> kb_model::Pubkey { + return kb_model::Pubkey(value.to_string()); + } + + fn policy(signers: &[kb_model::Pubkey], spend: u64) -> kb_execution_api::ExecutionPolicy { + return kb_execution_api::ExecutionPolicy { + authorized_signers: signers.to_vec(), + blockhash: kb_execution_api::ExecutionBlockhashPolicy { + kind: kb_execution_api::ExecutionBlockhashKind::Latest, + max_age_slots: std::option::Option::Some(100), + nonce_account: std::option::Option::None, + nonce_authority: std::option::Option::None, + }, + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(spend), + max_fee_lamports: std::option::Option::Some(100_000), + max_compute_unit_price_micro_lamports: std::option::Option::Some(10), + }, + ..kb_execution_api::ExecutionPolicy::default() + }; + } + + #[test] + fn duplicate_block_plan_matches_official_order_offsets_and_wire() { + let proof = solana_pubkey::Pubkey::new_unique(); + let node = solana_pubkey::Pubkey::new_unique(); + let reporter = solana_pubkey::Pubkey::new_unique(); + let destination = solana_pubkey::Pubkey::new_unique(); + let slot = 42_u64; + let program = solana_pubkey::Pubkey::from_str(kb_program_ids::SLASHING_PROGRAM_ID) + .unwrap_or_else(|error| panic!("invalid id: {error}")); + let slot_bytes = slot.to_le_bytes(); + let (report, _bump) = solana_pubkey::Pubkey::find_program_address( + &[node.as_ref(), slot_bytes.as_slice(), &[1]], + &program, + ); + let payer = model(solana_pubkey::Pubkey::new_unique()); + let intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("slashing-duplicate"), + fee_payer: payer.clone(), + policy: policy(&[payer, model(reporter)], 1_000), + operation: crate::SolanaCoreOperation::SlashingSubmitDuplicateBlockProof { + proof_account: model(proof), + report_account: model(report), + funder: std::option::Option::None, + report_lamports: 1_000, + proof_offset: 34, + slot, + node_pubkey: model(node), + reporter: model(reporter), + destination: model(destination), + shred_1_merkle_root: vec![1; 32], + shred_1_signature: vec![2; 64], + shred_2_merkle_root: vec![3; 32], + shred_2_signature: vec![4; 64], + }, + }; + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(plan.instructions.len(), 3); + assert_eq!(plan.instructions[0].program_id.0, kb_program_ids::SYSTEM_PROGRAM_ID); + assert_eq!(plan.instructions[1].program_id.0, kb_program_ids::ED25519_PROGRAM_ID); + assert_eq!(plan.instructions[2].program_id.0, kb_program_ids::SLASHING_PROGRAM_ID); + assert_eq!(plan.instructions[2].data.len(), 305); + assert_eq!(&plan.instructions[2].data[1..9], &34_u64.to_le_bytes()); + assert_eq!(&plan.instructions[2].data[9..17], &slot.to_le_bytes()); + assert_eq!(&plan.instructions[1].data[2..4], &145_u16.to_le_bytes()); + assert_eq!(&plan.instructions[1].data[4..6], &2_u16.to_le_bytes()); + assert_eq!(plan.requested_spend_lamports, 1_000); + } + + #[test] + fn slashing_close_has_no_instruction_signer() { + let report = solana_pubkey::Pubkey::new_unique(); + let destination = solana_pubkey::Pubkey::new_unique(); + let payer = model(solana_pubkey::Pubkey::new_unique()); + let intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("slashing-close"), + fee_payer: payer.clone(), + policy: policy(&[payer], 0), + operation: crate::SolanaCoreOperation::SlashingCloseViolationReport { + report_account: model(report), + destination: model(destination), + }, + }; + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(plan.instructions[0].data, vec![0]); + assert!(plan.instructions[0].accounts.iter().all(|account| return !account.is_signer)); + assert_eq!(plan.required_signers.len(), 1); + } + + #[test] + fn duplicate_block_rejects_wrong_pda_sizes_and_durable_nonce() { + let key = model(solana_pubkey::Pubkey::new_unique()); + let mut tpolicy = policy(std::slice::from_ref(&key), 1); + tpolicy.blockhash.kind = kb_execution_api::ExecutionBlockhashKind::DurableNonce; + tpolicy.blockhash.max_age_slots = std::option::Option::None; + tpolicy.blockhash.nonce_account = std::option::Option::Some(key.clone()); + tpolicy.blockhash.nonce_authority = std::option::Option::Some(key.clone()); + let intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("slashing-invalid"), + fee_payer: key.clone(), + policy: tpolicy, + operation: crate::SolanaCoreOperation::SlashingSubmitDuplicateBlockProof { + proof_account: key.clone(), + report_account: key.clone(), + funder: std::option::Option::None, + report_lamports: 1, + proof_offset: 0, + slot: 0, + node_pubkey: key.clone(), + reporter: key.clone(), + destination: key.clone(), + shred_1_merkle_root: vec![0; 31], + shred_1_signature: vec![0; 64], + shred_2_merkle_root: vec![0; 32], + shred_2_signature: vec![0; 64], + }, + }; + assert!(crate::builder_build_prepared_plan(&intent).is_err()); + let destination_is_report = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("slashing-same-destination"), + fee_payer: key.clone(), + policy: policy(std::slice::from_ref(&key), 1), + operation: crate::SolanaCoreOperation::SlashingSubmitDuplicateBlockProof { + proof_account: key.clone(), + report_account: key.clone(), + funder: std::option::Option::None, + report_lamports: 1, + proof_offset: 0, + slot: 0, + node_pubkey: key.clone(), + reporter: key.clone(), + destination: key, + shred_1_merkle_root: vec![0; 32], + shred_1_signature: vec![0; 64], + shred_2_merkle_root: vec![0; 32], + shred_2_signature: vec![0; 64], + }, + }; + assert!(crate::builder_build_prepared_plan(&destination_is_report).is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/stake.rs b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/stake.rs new file mode 100644 index 0000000..aa225d2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/stake.rs @@ -0,0 +1,1979 @@ +// file: kb_executor_solana_core/src/stake.rs +// version: 4 + +//! Stake Program instruction builders using the exact wincode-compatible wire contract. + +const SOURCE: &str = "solana-stake-interface@4.3.1"; + +#[allow(dead_code)] +#[derive(Clone, Debug, Eq, PartialEq, wincode::SchemaWrite)] +enum StakeInstructionWire { + Initialize(solana_stake_interface::state::Authorized, solana_stake_interface::state::Lockup), + Authorize([u8; 32], solana_stake_interface::state::StakeAuthorize), + DelegateStake, + Split(u64), + Withdraw(u64), + Deactivate, + SetLockup(StakeLockupArgsWire), + Merge, + AuthorizeWithSeed(StakeAuthorizeWithSeedArgsWire), + InitializeChecked, + AuthorizeChecked(solana_stake_interface::state::StakeAuthorize), + AuthorizeCheckedWithSeed(StakeAuthorizeCheckedWithSeedArgsWire), + SetLockupChecked(StakeLockupCheckedArgsWire), + GetMinimumDelegation, + DeactivateDelinquent, + Redelegate, + MoveStake(u64), + MoveLamports(u64), +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, wincode::SchemaWrite)] +struct StakeLockupArgsWire { + unix_timestamp: std::option::Option, + epoch: std::option::Option, + custodian: std::option::Option<[u8; 32]>, +} + +#[derive(Clone, Debug, Eq, PartialEq, wincode::SchemaWrite)] +struct StakeAuthorizeWithSeedArgsWire { + new_authorized_pubkey: [u8; 32], + stake_authorize: solana_stake_interface::state::StakeAuthorize, + authority_seed: std::string::String, + authority_owner: [u8; 32], +} + +#[derive(Clone, Debug, Eq, PartialEq, wincode::SchemaWrite)] +struct StakeAuthorizeCheckedWithSeedArgsWire { + stake_authorize: solana_stake_interface::state::StakeAuthorize, + authority_seed: std::string::String, + authority_owner: [u8; 32], +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, wincode::SchemaWrite)] +struct StakeLockupCheckedArgsWire { + unix_timestamp: std::option::Option, + epoch: std::option::Option, +} + +pub(crate) fn build_prepared_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, +) -> kb_core::Result { + return match &intent.operation { + crate::SolanaCoreOperation::StakeInitialize { + stake_account, + staker, + withdrawer, + lockup_unix_timestamp, + lockup_epoch, + lockup_custodian, + } => build_initialize_plan( + intent, + fee_payer, + stake_account, + staker, + withdrawer, + *lockup_unix_timestamp, + *lockup_epoch, + lockup_custodian, + ), + crate::SolanaCoreOperation::StakeInitializeChecked { + stake_account, + staker, + withdrawer, + } => build_initialize_checked_plan(intent, fee_payer, stake_account, staker, withdrawer), + crate::SolanaCoreOperation::StakeCreateAccount { + from, + stake_account, + staker, + withdrawer, + lockup_unix_timestamp, + lockup_epoch, + lockup_custodian, + lamports, + } => build_create_account_plan( + intent, + fee_payer, + from, + stake_account, + std::option::Option::None, + staker, + withdrawer, + *lockup_unix_timestamp, + *lockup_epoch, + std::option::Option::Some(lockup_custodian), + *lamports, + false, + std::option::Option::None, + ), + crate::SolanaCoreOperation::StakeCreateAccountWithSeed { + from, + stake_account, + base, + seed, + staker, + withdrawer, + lockup_unix_timestamp, + lockup_epoch, + lockup_custodian, + lamports, + } => build_create_account_plan( + intent, + fee_payer, + from, + stake_account, + std::option::Option::Some((base, seed.as_str())), + staker, + withdrawer, + *lockup_unix_timestamp, + *lockup_epoch, + std::option::Option::Some(lockup_custodian), + *lamports, + false, + std::option::Option::None, + ), + crate::SolanaCoreOperation::StakeCreateAccountChecked { + from, + stake_account, + staker, + withdrawer, + lamports, + } => build_create_account_plan( + intent, + fee_payer, + from, + stake_account, + std::option::Option::None, + staker, + withdrawer, + 0, + 0, + std::option::Option::None, + *lamports, + true, + std::option::Option::None, + ), + crate::SolanaCoreOperation::StakeCreateAccountWithSeedChecked { + from, + stake_account, + base, + seed, + staker, + withdrawer, + lamports, + } => build_create_account_plan( + intent, + fee_payer, + from, + stake_account, + std::option::Option::Some((base, seed.as_str())), + staker, + withdrawer, + 0, + 0, + std::option::Option::None, + *lamports, + true, + std::option::Option::None, + ), + crate::SolanaCoreOperation::StakeSplit { + stake_account, + stake_authority, + lamports, + split_stake_account, + } => build_split_plan( + intent, + fee_payer, + stake_account, + stake_authority, + *lamports, + split_stake_account, + std::option::Option::None, + ), + crate::SolanaCoreOperation::StakeSplitWithSeed { + stake_account, + stake_authority, + lamports, + split_stake_account, + base, + seed, + } => build_split_plan( + intent, + fee_payer, + stake_account, + stake_authority, + *lamports, + split_stake_account, + std::option::Option::Some((base, seed.as_str())), + ), + crate::SolanaCoreOperation::StakeMerge { + destination_stake_account, + source_stake_account, + stake_authority, + } => build_merge_plan( + intent, + fee_payer, + destination_stake_account, + source_stake_account, + stake_authority, + ), + crate::SolanaCoreOperation::StakeCreateAccountAndDelegate { + from, + stake_account, + vote_account, + staker, + withdrawer, + lockup_unix_timestamp, + lockup_epoch, + lockup_custodian, + lamports, + } => build_create_account_plan( + intent, + fee_payer, + from, + stake_account, + std::option::Option::None, + staker, + withdrawer, + *lockup_unix_timestamp, + *lockup_epoch, + std::option::Option::Some(lockup_custodian), + *lamports, + false, + std::option::Option::Some(vote_account), + ), + crate::SolanaCoreOperation::StakeCreateAccountWithSeedAndDelegate { + from, + stake_account, + base, + seed, + vote_account, + staker, + withdrawer, + lockup_unix_timestamp, + lockup_epoch, + lockup_custodian, + lamports, + } => build_create_account_plan( + intent, + fee_payer, + from, + stake_account, + std::option::Option::Some((base, seed.as_str())), + staker, + withdrawer, + *lockup_unix_timestamp, + *lockup_epoch, + std::option::Option::Some(lockup_custodian), + *lamports, + false, + std::option::Option::Some(vote_account), + ), + crate::SolanaCoreOperation::StakeAuthorize { + stake_account, + authority, + new_authority, + authorization, + lockup_authority, + } => build_authorize_plan( + intent, + fee_payer, + stake_account, + authority, + std::option::Option::None, + new_authority, + *authorization, + lockup_authority.as_ref(), + false, + ), + crate::SolanaCoreOperation::StakeAuthorizeChecked { + stake_account, + authority, + new_authority, + authorization, + lockup_authority, + } => build_authorize_plan( + intent, + fee_payer, + stake_account, + authority, + std::option::Option::None, + new_authority, + *authorization, + lockup_authority.as_ref(), + true, + ), + crate::SolanaCoreOperation::StakeAuthorizeWithSeed { + stake_account, + authority_base, + authority_seed, + authority_owner, + new_authority, + authorization, + lockup_authority, + } => build_authorize_plan( + intent, + fee_payer, + stake_account, + authority_base, + std::option::Option::Some((authority_seed.as_str(), authority_owner)), + new_authority, + *authorization, + lockup_authority.as_ref(), + false, + ), + crate::SolanaCoreOperation::StakeAuthorizeCheckedWithSeed { + stake_account, + authority_base, + authority_seed, + authority_owner, + new_authority, + authorization, + lockup_authority, + } => build_authorize_plan( + intent, + fee_payer, + stake_account, + authority_base, + std::option::Option::Some((authority_seed.as_str(), authority_owner)), + new_authority, + *authorization, + lockup_authority.as_ref(), + true, + ), + crate::SolanaCoreOperation::StakeDelegate { + stake_account, + stake_authority, + vote_account, + } => build_delegate_plan(intent, fee_payer, stake_account, stake_authority, vote_account), + crate::SolanaCoreOperation::StakeWithdraw { + stake_account, + withdraw_authority, + to, + lamports, + lockup_authority, + } => build_withdraw_plan( + intent, + fee_payer, + stake_account, + withdraw_authority, + to, + *lamports, + lockup_authority.as_ref(), + ), + crate::SolanaCoreOperation::StakeDeactivate { stake_account, stake_authority } => { + build_deactivate_plan(intent, fee_payer, stake_account, stake_authority) + }, + crate::SolanaCoreOperation::StakeSetLockup { + stake_account, + authority, + unix_timestamp, + epoch, + custodian, + } => build_set_lockup_plan( + intent, + fee_payer, + stake_account, + authority, + *unix_timestamp, + *epoch, + custodian.as_ref(), + false, + ), + crate::SolanaCoreOperation::StakeSetLockupChecked { + stake_account, + authority, + unix_timestamp, + epoch, + new_custodian, + } => build_set_lockup_plan( + intent, + fee_payer, + stake_account, + authority, + *unix_timestamp, + *epoch, + new_custodian.as_ref(), + true, + ), + crate::SolanaCoreOperation::StakeGetMinimumDelegation => { + build_get_minimum_delegation_plan(intent, fee_payer) + }, + crate::SolanaCoreOperation::StakeDeactivateDelinquent { + stake_account, + delinquent_vote_account, + reference_vote_account, + } => build_deactivate_delinquent_plan( + intent, + fee_payer, + stake_account, + delinquent_vote_account, + reference_vote_account, + ), + crate::SolanaCoreOperation::StakeMoveStake { + source_stake_account, + destination_stake_account, + stake_authority, + lamports, + } => build_move_plan( + intent, + fee_payer, + source_stake_account, + destination_stake_account, + stake_authority, + *lamports, + true, + ), + crate::SolanaCoreOperation::StakeMoveLamports { + source_stake_account, + destination_stake_account, + stake_authority, + lamports, + } => build_move_plan( + intent, + fee_payer, + source_stake_account, + destination_stake_account, + stake_authority, + *lamports, + false, + ), + _ => std::result::Result::Err(kb_core::Error::new( + "execution_stake_operation_mismatch", + "Stake builder received an unrelated Solana core operation", + )), + }; +} + +#[allow(clippy::too_many_arguments)] +fn build_initialize_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + stake_account: &kb_model::Pubkey, + staker: &kb_model::Pubkey, + withdrawer: &kb_model::Pubkey, + lockup_unix_timestamp: i64, + lockup_epoch: u64, + lockup_custodian: &kb_model::Pubkey, +) -> kb_core::Result { + let stake = match crate::parse_pubkey(stake_account, "stake_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authorized = match authorized(staker, withdrawer) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let custodian = match crate::parse_pubkey(lockup_custodian, "lockup_custodian") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = match stake_instruction( + StakeInstructionWire::Initialize( + authorized, + solana_stake_interface::state::Lockup { + unix_timestamp: lockup_unix_timestamp, + epoch: lockup_epoch, + custodian, + }, + ), + vec![ + solana_instruction::AccountMeta::new(stake, false), + solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::sysvar::rent::id(), + false, + ), + ], + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::STAKE_INITIALIZE_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + std::vec::Vec::new(), + ); +} + +fn build_initialize_checked_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + stake_account: &kb_model::Pubkey, + staker: &kb_model::Pubkey, + withdrawer: &kb_model::Pubkey, +) -> kb_core::Result { + let stake = match crate::parse_pubkey(stake_account, "stake_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let staker_address = match crate::parse_pubkey(staker, "staker") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let withdrawer_address = match crate::parse_pubkey(withdrawer, "withdrawer") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = match stake_instruction( + StakeInstructionWire::InitializeChecked, + vec![ + solana_instruction::AccountMeta::new(stake, false), + solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::sysvar::rent::id(), + false, + ), + solana_instruction::AccountMeta::new_readonly(staker_address, false), + solana_instruction::AccountMeta::new_readonly(withdrawer_address, true), + ], + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::STAKE_INITIALIZE_CHECKED_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + vec![(withdrawer.clone(), std::string::String::from("stake_withdraw_authority"))], + ); +} + +#[allow(clippy::too_many_arguments)] +fn build_create_account_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + from: &kb_model::Pubkey, + stake_account: &kb_model::Pubkey, + seed_contract: std::option::Option<(&kb_model::Pubkey, &str)>, + staker: &kb_model::Pubkey, + withdrawer: &kb_model::Pubkey, + lockup_unix_timestamp: i64, + lockup_epoch: u64, + lockup_custodian: std::option::Option<&kb_model::Pubkey>, + lamports: u64, + checked: bool, + vote_account: std::option::Option<&kb_model::Pubkey>, +) -> kb_core::Result { + match crate::validate_positive_u64( + lamports, + "execution_stake_create_lamports_zero", + "Stake account creation requires a positive measured rent-exempt lamport amount", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + if from == stake_account { + return std::result::Result::Err(kb_core::Error::new( + "execution_stake_create_same_account", + "Stake funding and new stake accounts must differ", + )); + } + let from_address = match crate::parse_pubkey(from, "from") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let stake_address = match crate::parse_pubkey(stake_account, "stake_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let stake_program = match stake_program_id() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut signer_roles = vec![(from.clone(), std::string::String::from("stake_funding_account"))]; + let create = match seed_contract { + std::option::Option::Some((base, seed)) => { + let base_address = match crate::parse_pubkey(base, "base") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + match crate::validate_seeded_address( + &stake_address, + &base_address, + seed, + &stake_program, + "stake_account", + "execution_stake_seed_invalid", + "execution_stake_seeded_address_mismatch", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + signer_roles.push((base.clone(), std::string::String::from("stake_seed_base"))); + solana_system_interface::instruction::create_account_with_seed( + &from_address, + &stake_address, + &base_address, + seed, + lamports, + solana_stake_interface::state::StakeStateV2::size_of() as u64, + &stake_program, + ) + }, + std::option::Option::None => { + signer_roles + .push((stake_account.clone(), std::string::String::from("new_stake_account"))); + solana_system_interface::instruction::create_account( + &from_address, + &stake_address, + lamports, + solana_stake_interface::state::StakeStateV2::size_of() as u64, + &stake_program, + ) + }, + }; + let mut instructions = vec![create]; + if checked { + let staker_address = match crate::parse_pubkey(staker, "staker") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let withdrawer_address = match crate::parse_pubkey(withdrawer, "withdrawer") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let initialize = match stake_instruction( + StakeInstructionWire::InitializeChecked, + vec![ + solana_instruction::AccountMeta::new(stake_address, false), + solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::sysvar::rent::id(), + false, + ), + solana_instruction::AccountMeta::new_readonly(staker_address, false), + solana_instruction::AccountMeta::new_readonly(withdrawer_address, true), + ], + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + instructions.push(initialize); + signer_roles + .push((withdrawer.clone(), std::string::String::from("stake_withdraw_authority"))); + } else { + let authorized = match authorized(staker, withdrawer) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let lockup_custodian = match lockup_custodian { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_stake_lockup_custodian_missing", + "unchecked Stake account creation requires a lockup custodian", + )); + }, + }; + let custodian = match crate::parse_pubkey(lockup_custodian, "lockup_custodian") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let initialize = match stake_instruction( + StakeInstructionWire::Initialize( + authorized, + solana_stake_interface::state::Lockup { + unix_timestamp: lockup_unix_timestamp, + epoch: lockup_epoch, + custodian, + }, + ), + vec![ + solana_instruction::AccountMeta::new(stake_address, false), + solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::sysvar::rent::id(), + false, + ), + ], + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + instructions.push(initialize); + } + let operation_code = match (seed_contract.is_some(), checked, vote_account.is_some()) { + (false, false, false) => crate::STAKE_CREATE_ACCOUNT_OPERATION, + (true, false, false) => crate::STAKE_CREATE_ACCOUNT_WITH_SEED_OPERATION, + (false, true, false) => crate::STAKE_CREATE_ACCOUNT_CHECKED_OPERATION, + (true, true, false) => crate::STAKE_CREATE_ACCOUNT_WITH_SEED_CHECKED_OPERATION, + (false, false, true) => crate::STAKE_CREATE_ACCOUNT_AND_DELEGATE_OPERATION, + (true, false, true) => crate::STAKE_CREATE_ACCOUNT_WITH_SEED_AND_DELEGATE_OPERATION, + (_, true, true) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_stake_checked_create_delegate_unavailable", + "the official Stake interface does not expose a checked create-and-delegate helper", + )); + }, + }; + if let std::option::Option::Some(vote) = vote_account { + let delegate = match delegate_instruction(stake_account, staker, vote) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + instructions.push(delegate); + signer_roles.push((staker.clone(), std::string::String::from("stake_authority"))); + } + return crate::finalize_prepared_plan( + intent, + fee_payer, + operation_code, + instructions, + lamports, + std::option::Option::None, + signer_roles, + ); +} + +fn build_split_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + stake_account: &kb_model::Pubkey, + stake_authority: &kb_model::Pubkey, + lamports: u64, + split_stake_account: &kb_model::Pubkey, + seed_contract: std::option::Option<(&kb_model::Pubkey, &str)>, +) -> kb_core::Result { + match crate::validate_positive_u64( + lamports, + "execution_stake_split_lamports_zero", + "Stake split requires a positive lamport amount", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_distinct_pubkeys( + stake_account, + split_stake_account, + "execution_stake_split_same_account", + "Stake split source and destination accounts must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let source = match crate::parse_pubkey(stake_account, "stake_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let destination = match crate::parse_pubkey(split_stake_account, "split_stake_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority = match crate::parse_pubkey(stake_authority, "stake_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let stake_program = match stake_program_id() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut signer_roles = + vec![(stake_authority.clone(), std::string::String::from("stake_authority"))]; + let mut instructions = match seed_contract { + std::option::Option::Some((base, seed)) => { + let base_address = match crate::parse_pubkey(base, "base") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + match crate::validate_seeded_address( + &destination, + &base_address, + seed, + &stake_program, + "split_stake_account", + "execution_stake_seed_invalid", + "execution_stake_seeded_address_mismatch", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + signer_roles.push((base.clone(), std::string::String::from("stake_split_seed_base"))); + vec![solana_system_interface::instruction::allocate_with_seed( + &destination, + &base_address, + seed, + solana_stake_interface::state::StakeStateV2::size_of() as u64, + &stake_program, + )] + }, + std::option::Option::None => { + signer_roles.push(( + split_stake_account.clone(), + std::string::String::from("new_split_stake_account"), + )); + vec![ + solana_system_interface::instruction::allocate( + &destination, + solana_stake_interface::state::StakeStateV2::size_of() as u64, + ), + solana_system_interface::instruction::assign(&destination, &stake_program), + ] + }, + }; + let split = match stake_instruction( + StakeInstructionWire::Split(lamports), + vec![ + solana_instruction::AccountMeta::new(source, false), + solana_instruction::AccountMeta::new(destination, false), + solana_instruction::AccountMeta::new_readonly(authority, true), + ], + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + instructions.push(split); + return crate::finalize_prepared_plan( + intent, + fee_payer, + if seed_contract.is_some() { + crate::STAKE_SPLIT_WITH_SEED_OPERATION + } else { + crate::STAKE_SPLIT_OPERATION + }, + instructions, + lamports, + std::option::Option::None, + signer_roles, + ); +} + +fn build_merge_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + destination_stake_account: &kb_model::Pubkey, + source_stake_account: &kb_model::Pubkey, + stake_authority: &kb_model::Pubkey, +) -> kb_core::Result { + match crate::validate_distinct_pubkeys( + destination_stake_account, + source_stake_account, + "execution_stake_merge_same_account", + "Stake merge source and destination accounts must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let destination = + match crate::parse_pubkey(destination_stake_account, "destination_stake_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let source = match crate::parse_pubkey(source_stake_account, "source_stake_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority = match crate::parse_pubkey(stake_authority, "stake_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = match stake_instruction( + StakeInstructionWire::Merge, + vec![ + solana_instruction::AccountMeta::new(destination, false), + solana_instruction::AccountMeta::new(source, false), + solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::sysvar::clock::id(), + false, + ), + solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::sysvar::stake_history::id(), + false, + ), + solana_instruction::AccountMeta::new_readonly(authority, true), + ], + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::STAKE_MERGE_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + vec![(stake_authority.clone(), std::string::String::from("stake_authority"))], + ); +} + +#[allow(clippy::too_many_arguments)] +fn build_authorize_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + stake_account: &kb_model::Pubkey, + authority_or_base: &kb_model::Pubkey, + seed_contract: std::option::Option<(&str, &kb_model::Pubkey)>, + new_authority: &kb_model::Pubkey, + authorization: crate::StakeAuthorizationKind, + lockup_authority: std::option::Option<&kb_model::Pubkey>, + checked: bool, +) -> kb_core::Result { + let stake = match crate::parse_pubkey(stake_account, "stake_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority = match crate::parse_pubkey(authority_or_base, "authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let new_authority_address = match crate::parse_pubkey(new_authority, "new_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut accounts = vec![solana_instruction::AccountMeta::new(stake, false)]; + let wire = match seed_contract { + std::option::Option::Some((seed, owner)) => { + let owner_address = match crate::parse_pubkey(owner, "authority_owner") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + accounts.push(solana_instruction::AccountMeta::new_readonly(authority, true)); + accounts.push(solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::sysvar::clock::id(), + false, + )); + if checked { + accounts.push(solana_instruction::AccountMeta::new_readonly( + new_authority_address, + true, + )); + StakeInstructionWire::AuthorizeCheckedWithSeed( + StakeAuthorizeCheckedWithSeedArgsWire { + stake_authorize: authorization.as_official(), + authority_seed: std::string::String::from(seed), + authority_owner: owner_address.to_bytes(), + }, + ) + } else { + StakeInstructionWire::AuthorizeWithSeed(StakeAuthorizeWithSeedArgsWire { + new_authorized_pubkey: new_authority_address.to_bytes(), + stake_authorize: authorization.as_official(), + authority_seed: std::string::String::from(seed), + authority_owner: owner_address.to_bytes(), + }) + } + }, + std::option::Option::None => { + accounts.push(solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::sysvar::clock::id(), + false, + )); + accounts.push(solana_instruction::AccountMeta::new_readonly(authority, true)); + if checked { + accounts.push(solana_instruction::AccountMeta::new_readonly( + new_authority_address, + true, + )); + StakeInstructionWire::AuthorizeChecked(authorization.as_official()) + } else { + StakeInstructionWire::Authorize( + new_authority_address.to_bytes(), + authorization.as_official(), + ) + } + }, + }; + let mut signer_roles = + vec![(authority_or_base.clone(), std::string::String::from("stake_current_authority"))]; + if checked { + signer_roles + .push((new_authority.clone(), std::string::String::from("stake_new_authority"))); + } + if let std::option::Option::Some(custodian) = lockup_authority { + let custodian_address = match crate::parse_pubkey(custodian, "lockup_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + accounts.push(solana_instruction::AccountMeta::new_readonly(custodian_address, true)); + signer_roles.push((custodian.clone(), std::string::String::from("stake_lockup_authority"))); + } + let instruction = match stake_instruction(wire, accounts) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let operation_code = match (seed_contract.is_some(), checked) { + (false, false) => crate::STAKE_AUTHORIZE_OPERATION, + (false, true) => crate::STAKE_AUTHORIZE_CHECKED_OPERATION, + (true, false) => crate::STAKE_AUTHORIZE_WITH_SEED_OPERATION, + (true, true) => crate::STAKE_AUTHORIZE_CHECKED_WITH_SEED_OPERATION, + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + operation_code, + vec![instruction], + 0, + std::option::Option::None, + signer_roles, + ); +} + +fn delegate_instruction( + stake_account: &kb_model::Pubkey, + stake_authority: &kb_model::Pubkey, + vote_account: &kb_model::Pubkey, +) -> kb_core::Result { + let stake = match crate::parse_pubkey(stake_account, "stake_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority = match crate::parse_pubkey(stake_authority, "stake_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let vote = match crate::parse_pubkey(vote_account, "vote_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let stake_config = match crate::parse_native_program_id(kb_program_ids::STAKE_CONFIG_ACCOUNT_ID) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return stake_instruction( + StakeInstructionWire::DelegateStake, + vec![ + solana_instruction::AccountMeta::new(stake, false), + solana_instruction::AccountMeta::new_readonly(vote, false), + solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::sysvar::clock::id(), + false, + ), + solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::sysvar::stake_history::id(), + false, + ), + solana_instruction::AccountMeta::new_readonly(stake_config, false), + solana_instruction::AccountMeta::new_readonly(authority, true), + ], + ); +} + +fn build_delegate_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + stake_account: &kb_model::Pubkey, + stake_authority: &kb_model::Pubkey, + vote_account: &kb_model::Pubkey, +) -> kb_core::Result { + let instruction = match delegate_instruction(stake_account, stake_authority, vote_account) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::STAKE_DELEGATE_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + vec![(stake_authority.clone(), std::string::String::from("stake_authority"))], + ); +} + +fn build_withdraw_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + stake_account: &kb_model::Pubkey, + withdraw_authority: &kb_model::Pubkey, + to: &kb_model::Pubkey, + lamports: u64, + lockup_authority: std::option::Option<&kb_model::Pubkey>, +) -> kb_core::Result { + match crate::validate_positive_u64( + lamports, + "execution_stake_withdraw_lamports_zero", + "Stake withdrawal requires a positive lamport amount", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_distinct_pubkeys( + stake_account, + to, + "execution_stake_withdraw_same_account", + "Stake withdrawal source and destination accounts must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let stake = match crate::parse_pubkey(stake_account, "stake_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let destination = match crate::parse_pubkey(to, "to") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let withdrawer = match crate::parse_pubkey(withdraw_authority, "withdraw_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut accounts = vec![ + solana_instruction::AccountMeta::new(stake, false), + solana_instruction::AccountMeta::new(destination, false), + solana_instruction::AccountMeta::new_readonly(solana_sdk_ids::sysvar::clock::id(), false), + solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::sysvar::stake_history::id(), + false, + ), + solana_instruction::AccountMeta::new_readonly(withdrawer, true), + ]; + let mut signer_roles = vec![( + withdraw_authority.clone(), + std::string::String::from("stake_withdraw_authority"), + )]; + if let std::option::Option::Some(custodian) = lockup_authority { + let custodian_address = match crate::parse_pubkey(custodian, "lockup_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + accounts.push(solana_instruction::AccountMeta::new_readonly(custodian_address, true)); + signer_roles.push((custodian.clone(), std::string::String::from("stake_lockup_authority"))); + } + let instruction = match stake_instruction(StakeInstructionWire::Withdraw(lamports), accounts) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::STAKE_WITHDRAW_OPERATION, + vec![instruction], + lamports, + std::option::Option::None, + signer_roles, + ); +} + +fn build_deactivate_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + stake_account: &kb_model::Pubkey, + stake_authority: &kb_model::Pubkey, +) -> kb_core::Result { + let stake = match crate::parse_pubkey(stake_account, "stake_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority = match crate::parse_pubkey(stake_authority, "stake_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = match stake_instruction( + StakeInstructionWire::Deactivate, + vec![ + solana_instruction::AccountMeta::new(stake, false), + solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::sysvar::clock::id(), + false, + ), + solana_instruction::AccountMeta::new_readonly(authority, true), + ], + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::STAKE_DEACTIVATE_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + vec![(stake_authority.clone(), std::string::String::from("stake_authority"))], + ); +} + +#[allow(clippy::too_many_arguments)] +fn build_set_lockup_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + stake_account: &kb_model::Pubkey, + authority: &kb_model::Pubkey, + unix_timestamp: std::option::Option, + epoch: std::option::Option, + custodian: std::option::Option<&kb_model::Pubkey>, + checked: bool, +) -> kb_core::Result { + if unix_timestamp.is_none() && epoch.is_none() && custodian.is_none() { + return std::result::Result::Err(kb_core::Error::new( + "execution_stake_lockup_empty", + "Stake lockup update must change at least one field", + )); + } + let stake = match crate::parse_pubkey(stake_account, "stake_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut accounts = vec![ + solana_instruction::AccountMeta::new(stake, false), + solana_instruction::AccountMeta::new_readonly(authority_address, true), + ]; + let mut signer_roles = vec![( + authority.clone(), + std::string::String::from("stake_lockup_or_withdraw_authority"), + )]; + let wire = if checked { + if let std::option::Option::Some(new_custodian) = custodian { + let new_custodian_address = match crate::parse_pubkey(new_custodian, "new_custodian") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + accounts + .push(solana_instruction::AccountMeta::new_readonly(new_custodian_address, true)); + signer_roles.push(( + new_custodian.clone(), + std::string::String::from("stake_new_lockup_authority"), + )); + } + StakeInstructionWire::SetLockupChecked(StakeLockupCheckedArgsWire { unix_timestamp, epoch }) + } else { + let custodian_bytes = match custodian { + std::option::Option::Some(value) => { + let parsed = match crate::parse_pubkey(value, "custodian") { + std::result::Result::Ok(address) => address, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + std::option::Option::Some(parsed.to_bytes()) + }, + std::option::Option::None => std::option::Option::None, + }; + StakeInstructionWire::SetLockup(StakeLockupArgsWire { + unix_timestamp, + epoch, + custodian: custodian_bytes, + }) + }; + let instruction = match stake_instruction(wire, accounts) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + if checked { + crate::STAKE_SET_LOCKUP_CHECKED_OPERATION + } else { + crate::STAKE_SET_LOCKUP_OPERATION + }, + vec![instruction], + 0, + std::option::Option::None, + signer_roles, + ); +} + +fn build_get_minimum_delegation_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, +) -> kb_core::Result { + let instruction = + match stake_instruction(StakeInstructionWire::GetMinimumDelegation, std::vec::Vec::new()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::STAKE_GET_MINIMUM_DELEGATION_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + std::vec::Vec::new(), + ); +} + +fn build_deactivate_delinquent_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + stake_account: &kb_model::Pubkey, + delinquent_vote_account: &kb_model::Pubkey, + reference_vote_account: &kb_model::Pubkey, +) -> kb_core::Result { + match crate::validate_distinct_pubkeys( + delinquent_vote_account, + reference_vote_account, + "execution_stake_delinquent_reference_same_account", + "delinquent and reference vote accounts must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let stake = match crate::parse_pubkey(stake_account, "stake_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let delinquent = match crate::parse_pubkey(delinquent_vote_account, "delinquent_vote_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let reference = match crate::parse_pubkey(reference_vote_account, "reference_vote_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = match stake_instruction( + StakeInstructionWire::DeactivateDelinquent, + vec![ + solana_instruction::AccountMeta::new(stake, false), + solana_instruction::AccountMeta::new_readonly(delinquent, false), + solana_instruction::AccountMeta::new_readonly(reference, false), + ], + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::STAKE_DEACTIVATE_DELINQUENT_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + std::vec::Vec::new(), + ); +} + +fn build_move_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + source_stake_account: &kb_model::Pubkey, + destination_stake_account: &kb_model::Pubkey, + stake_authority: &kb_model::Pubkey, + lamports: u64, + move_stake: bool, +) -> kb_core::Result { + match crate::validate_positive_u64( + lamports, + "execution_stake_move_lamports_zero", + "Stake move requires a positive lamport amount", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_distinct_pubkeys( + source_stake_account, + destination_stake_account, + "execution_stake_move_same_account", + "Stake move source and destination accounts must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let source = match crate::parse_pubkey(source_stake_account, "source_stake_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let destination = + match crate::parse_pubkey(destination_stake_account, "destination_stake_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority = match crate::parse_pubkey(stake_authority, "stake_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let wire = if move_stake { + StakeInstructionWire::MoveStake(lamports) + } else { + StakeInstructionWire::MoveLamports(lamports) + }; + let instruction = match stake_instruction( + wire, + vec![ + solana_instruction::AccountMeta::new(source, false), + solana_instruction::AccountMeta::new(destination, false), + solana_instruction::AccountMeta::new_readonly(authority, true), + ], + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + if move_stake { + crate::STAKE_MOVE_STAKE_OPERATION + } else { + crate::STAKE_MOVE_LAMPORTS_OPERATION + }, + vec![instruction], + lamports, + std::option::Option::None, + vec![(stake_authority.clone(), std::string::String::from("stake_authority"))], + ); +} + +fn authorized( + staker: &kb_model::Pubkey, + withdrawer: &kb_model::Pubkey, +) -> kb_core::Result { + let staker_address = match crate::parse_pubkey(staker, "staker") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let withdrawer_address = match crate::parse_pubkey(withdrawer, "withdrawer") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(solana_stake_interface::state::Authorized { + staker: staker_address, + withdrawer: withdrawer_address, + }); +} + +fn stake_instruction( + wire: StakeInstructionWire, + accounts: std::vec::Vec, +) -> kb_core::Result { + let data = match wincode::serialize(&wire) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_stake_wincode_failed", + format!("cannot serialize {SOURCE} instruction: {error}"), + )); + }, + }; + let program_id = match stake_program_id() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(solana_instruction::Instruction { program_id, accounts, data }); +} + +fn stake_program_id() -> kb_core::Result { + return crate::parse_native_program_id(kb_program_ids::STAKE_PROGRAM_ID); +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; // rust-rules: trait-import + + fn model(value: solana_pubkey::Pubkey) -> kb_model::Pubkey { + return kb_model::Pubkey(value.to_string()); + } + + fn policy(signers: &[kb_model::Pubkey], spend: u64) -> kb_execution_api::ExecutionPolicy { + return kb_execution_api::ExecutionPolicy { + authorized_signers: signers.to_vec(), + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(spend), + max_fee_lamports: std::option::Option::Some(100_000), + max_compute_unit_price_micro_lamports: std::option::Option::Some(10), + }, + ..kb_execution_api::ExecutionPolicy::default() + }; + } + + fn base_intent( + operation: crate::SolanaCoreOperation, + signers: &[kb_model::Pubkey], + spend: u64, + ) -> crate::SolanaCoreExecutionIntent { + let payer = model(solana_pubkey::Pubkey::new_unique()); + let mut authorized = vec![payer.clone()]; + authorized.extend_from_slice(signers); + return crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("stake-test"), + fee_payer: payer, + policy: policy(authorized.as_slice(), spend), + operation, + }; + } + + #[test] + fn initialize_variants_match_official_wire_and_accounts() { + let stake = model(solana_pubkey::Pubkey::new_unique()); + let staker = model(solana_pubkey::Pubkey::new_unique()); + let withdrawer = model(solana_pubkey::Pubkey::new_unique()); + let custodian = model(solana_pubkey::Pubkey::new_unique()); + let unchecked = base_intent( + crate::SolanaCoreOperation::StakeInitialize { + stake_account: stake.clone(), + staker: staker.clone(), + withdrawer: withdrawer.clone(), + lockup_unix_timestamp: -1, + lockup_epoch: 9, + lockup_custodian: custodian, + }, + &[], + 0, + ); + let unchecked_plan = crate::builder_build_prepared_plan(&unchecked) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(&unchecked_plan.instructions[0].data[..4], &0_u32.to_le_bytes()); + assert_eq!(unchecked_plan.instructions[0].accounts.len(), 2); + assert_eq!(unchecked_plan.required_signers.len(), 1); + let checked = base_intent( + crate::SolanaCoreOperation::StakeInitializeChecked { + stake_account: stake, + staker, + withdrawer: withdrawer.clone(), + }, + std::slice::from_ref(&withdrawer), + 0, + ); + let checked_plan = crate::builder_build_prepared_plan(&checked) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(&checked_plan.instructions[0].data[..4], &9_u32.to_le_bytes()); + assert_eq!(checked_plan.instructions[0].accounts.len(), 4); + assert!(checked_plan.instructions[0].accounts[3].is_signer); + } + + #[test] + fn create_account_variants_preserve_official_instruction_order() { + let from = model(solana_pubkey::Pubkey::new_unique()); + let stake = model(solana_pubkey::Pubkey::new_unique()); + let staker = model(solana_pubkey::Pubkey::new_unique()); + let withdrawer = model(solana_pubkey::Pubkey::new_unique()); + let custodian = model(solana_pubkey::Pubkey::default()); + let intent = base_intent( + crate::SolanaCoreOperation::StakeCreateAccount { + from: from.clone(), + stake_account: stake.clone(), + staker, + withdrawer, + lockup_unix_timestamp: 0, + lockup_epoch: 0, + lockup_custodian: custodian, + lamports: 500, + }, + &[from.clone(), stake.clone()], + 500, + ); + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(plan.instructions.len(), 2); + assert_eq!(plan.instructions[0].program_id.0, kb_program_ids::SYSTEM_PROGRAM_ID); + assert_eq!(plan.instructions[1].program_id.0, kb_program_ids::STAKE_PROGRAM_ID); + assert_eq!(plan.requested_spend_lamports, 500); + let checked_stake = model(solana_pubkey::Pubkey::new_unique()); + let checked_withdrawer = model(solana_pubkey::Pubkey::new_unique()); + let checked = base_intent( + crate::SolanaCoreOperation::StakeCreateAccountChecked { + from: from.clone(), + stake_account: checked_stake.clone(), + staker: model(solana_pubkey::Pubkey::new_unique()), + withdrawer: checked_withdrawer.clone(), + lamports: 501, + }, + &[from, checked_stake, checked_withdrawer], + 501, + ); + let checked_plan = crate::builder_build_prepared_plan(&checked) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(checked_plan.instructions.len(), 2); + assert_eq!(&checked_plan.instructions[1].data[..4], &9_u32.to_le_bytes()); + assert!(checked_plan.instructions[1].accounts[3].is_signer); + } + + #[test] + fn seeded_create_and_split_validate_derivations_and_signers() { + let from = solana_pubkey::Pubkey::new_unique(); + let base = solana_pubkey::Pubkey::new_unique(); + let stake_program = solana_pubkey::Pubkey::from_str(kb_program_ids::STAKE_PROGRAM_ID) + .unwrap_or_else(|error| panic!("invalid Stake id: {error}")); + let seed = "stake-seed"; + let stake = solana_pubkey::Pubkey::create_with_seed(&base, seed, &stake_program) + .unwrap_or_else(|error| panic!("seed failure: {error}")); + let staker = model(solana_pubkey::Pubkey::new_unique()); + let withdrawer = model(solana_pubkey::Pubkey::new_unique()); + let checked = base_intent( + crate::SolanaCoreOperation::StakeCreateAccountWithSeedChecked { + from: model(from), + stake_account: model(stake), + base: model(base), + seed: std::string::String::from(seed), + staker, + withdrawer: withdrawer.clone(), + lamports: 700, + }, + &[model(from), model(base), withdrawer], + 700, + ); + let plan = crate::builder_build_prepared_plan(&checked) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(plan.instructions.len(), 2); + assert_eq!(&plan.instructions[1].data[..4], &9_u32.to_le_bytes()); + let split_destination = + solana_pubkey::Pubkey::create_with_seed(&base, "split-seed", &stake_program) + .unwrap_or_else(|error| panic!("seed failure: {error}")); + let authority = model(solana_pubkey::Pubkey::new_unique()); + let split = base_intent( + crate::SolanaCoreOperation::StakeSplitWithSeed { + stake_account: model(stake), + stake_authority: authority.clone(), + lamports: 10, + split_stake_account: model(split_destination), + base: model(base), + seed: std::string::String::from("split-seed"), + }, + &[model(base), authority.clone()], + 10, + ); + let split_plan = crate::builder_build_prepared_plan(&split) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(split_plan.instructions.len(), 2); + assert_eq!(&split_plan.instructions[1].data[..4], &3_u32.to_le_bytes()); + let unchecked_seed = "stake-unchecked"; + let unchecked_stake = + solana_pubkey::Pubkey::create_with_seed(&base, unchecked_seed, &stake_program) + .unwrap_or_else(|error| panic!("seed failure: {error}")); + let unchecked = base_intent( + crate::SolanaCoreOperation::StakeCreateAccountWithSeed { + from: model(from), + stake_account: model(unchecked_stake), + base: model(base), + seed: std::string::String::from(unchecked_seed), + staker: model(solana_pubkey::Pubkey::new_unique()), + withdrawer: model(solana_pubkey::Pubkey::new_unique()), + lockup_unix_timestamp: 0, + lockup_epoch: 0, + lockup_custodian: model(solana_pubkey::Pubkey::default()), + lamports: 800, + }, + &[model(from), model(base)], + 800, + ); + let unchecked_plan = crate::builder_build_prepared_plan(&unchecked) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(unchecked_plan.instructions.len(), 2); + assert_eq!(&unchecked_plan.instructions[1].data[..4], &0_u32.to_le_bytes()); + let split_key = model(solana_pubkey::Pubkey::new_unique()); + let unseeded_split = base_intent( + crate::SolanaCoreOperation::StakeSplit { + stake_account: model(stake), + stake_authority: authority.clone(), + lamports: 11, + split_stake_account: split_key.clone(), + }, + &[authority, split_key], + 11, + ); + let unseeded_split_plan = crate::builder_build_prepared_plan(&unseeded_split) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(unseeded_split_plan.instructions.len(), 3); + assert_eq!(&unseeded_split_plan.instructions[2].data[..4], &3_u32.to_le_bytes()); + } + + #[test] + fn create_and_delegate_compound_plans_append_delegate_last() { + let from = model(solana_pubkey::Pubkey::new_unique()); + let stake = model(solana_pubkey::Pubkey::new_unique()); + let staker = model(solana_pubkey::Pubkey::new_unique()); + let withdrawer = model(solana_pubkey::Pubkey::new_unique()); + let vote = model(solana_pubkey::Pubkey::new_unique()); + let intent = base_intent( + crate::SolanaCoreOperation::StakeCreateAccountAndDelegate { + from: from.clone(), + stake_account: stake.clone(), + vote_account: vote, + staker: staker.clone(), + withdrawer, + lockup_unix_timestamp: 0, + lockup_epoch: 0, + lockup_custodian: model(solana_pubkey::Pubkey::default()), + lamports: 1_000, + }, + &[from.clone(), stake.clone(), staker.clone()], + 1_000, + ); + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(plan.instructions.len(), 3); + assert_eq!(&plan.instructions[2].data[..4], &2_u32.to_le_bytes()); + assert_eq!(plan.instructions[2].accounts.len(), 6); + let base = solana_pubkey::Pubkey::new_unique(); + let stake_program = solana_pubkey::Pubkey::from_str(kb_program_ids::STAKE_PROGRAM_ID) + .unwrap_or_else(|error| panic!("invalid Stake id: {error}")); + let seed = "delegated-seed"; + let seeded_stake = solana_pubkey::Pubkey::create_with_seed(&base, seed, &stake_program) + .unwrap_or_else(|error| panic!("seed failure: {error}")); + let seeded = base_intent( + crate::SolanaCoreOperation::StakeCreateAccountWithSeedAndDelegate { + from: from.clone(), + stake_account: model(seeded_stake), + base: model(base), + seed: std::string::String::from(seed), + vote_account: model(solana_pubkey::Pubkey::new_unique()), + staker: staker.clone(), + withdrawer: model(solana_pubkey::Pubkey::new_unique()), + lockup_unix_timestamp: 0, + lockup_epoch: 0, + lockup_custodian: model(solana_pubkey::Pubkey::default()), + lamports: 1_001, + }, + &[from, model(base), staker], + 1_001, + ); + let seeded_plan = crate::builder_build_prepared_plan(&seeded) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(seeded_plan.instructions.len(), 3); + assert_eq!(&seeded_plan.instructions[2].data[..4], &2_u32.to_le_bytes()); + } + + #[test] + fn authorize_variants_match_official_accounts_and_wire_tags() { + let stake = model(solana_pubkey::Pubkey::new_unique()); + let current = model(solana_pubkey::Pubkey::new_unique()); + let new_authority = model(solana_pubkey::Pubkey::new_unique()); + let owner = model(solana_pubkey::Pubkey::new_unique()); + let cases = [ + ( + crate::SolanaCoreOperation::StakeAuthorize { + stake_account: stake.clone(), + authority: current.clone(), + new_authority: new_authority.clone(), + authorization: crate::StakeAuthorizationKind::Staker, + lockup_authority: std::option::Option::None, + }, + 1_u32, + 3_usize, + false, + ), + ( + crate::SolanaCoreOperation::StakeAuthorizeChecked { + stake_account: stake.clone(), + authority: current.clone(), + new_authority: new_authority.clone(), + authorization: crate::StakeAuthorizationKind::Withdrawer, + lockup_authority: std::option::Option::None, + }, + 10_u32, + 4_usize, + true, + ), + ( + crate::SolanaCoreOperation::StakeAuthorizeWithSeed { + stake_account: stake.clone(), + authority_base: current.clone(), + authority_seed: std::string::String::from("authority-seed"), + authority_owner: owner.clone(), + new_authority: new_authority.clone(), + authorization: crate::StakeAuthorizationKind::Staker, + lockup_authority: std::option::Option::None, + }, + 8_u32, + 3_usize, + false, + ), + ( + crate::SolanaCoreOperation::StakeAuthorizeCheckedWithSeed { + stake_account: stake, + authority_base: current.clone(), + authority_seed: std::string::String::from("authority-seed"), + authority_owner: owner, + new_authority: new_authority.clone(), + authorization: crate::StakeAuthorizationKind::Withdrawer, + lockup_authority: std::option::Option::None, + }, + 11_u32, + 4_usize, + true, + ), + ]; + for (operation, tag, account_count, new_authority_signs) in cases { + let signers = if new_authority_signs { + vec![current.clone(), new_authority.clone()] + } else { + vec![current.clone()] + }; + let intent = base_intent(operation, signers.as_slice(), 0); + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(&plan.instructions[0].data[..4], &tag.to_le_bytes()); + assert_eq!(plan.instructions[0].accounts.len(), account_count); + assert_eq!( + plan.instructions[0] + .accounts + .iter() + .filter(|account| return account.is_signer) + .count(), + if new_authority_signs { 2 } else { 1 } + ); + } + } + + #[test] + fn delegate_merge_withdraw_and_deactivate_match_official_contracts() { + let stake = model(solana_pubkey::Pubkey::new_unique()); + let other = model(solana_pubkey::Pubkey::new_unique()); + let authority = model(solana_pubkey::Pubkey::new_unique()); + let vote = model(solana_pubkey::Pubkey::new_unique()); + let delegate = base_intent( + crate::SolanaCoreOperation::StakeDelegate { + stake_account: stake.clone(), + stake_authority: authority.clone(), + vote_account: vote, + }, + std::slice::from_ref(&authority), + 0, + ); + let delegate_plan = crate::builder_build_prepared_plan(&delegate) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(delegate_plan.instructions[0].accounts.len(), 6); + assert_eq!(&delegate_plan.instructions[0].data[..4], &2_u32.to_le_bytes()); + let merge = base_intent( + crate::SolanaCoreOperation::StakeMerge { + destination_stake_account: stake.clone(), + source_stake_account: other.clone(), + stake_authority: authority.clone(), + }, + std::slice::from_ref(&authority), + 0, + ); + let merge_plan = crate::builder_build_prepared_plan(&merge) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(&merge_plan.instructions[0].data[..4], &7_u32.to_le_bytes()); + let withdraw = base_intent( + crate::SolanaCoreOperation::StakeWithdraw { + stake_account: stake.clone(), + withdraw_authority: authority.clone(), + to: other, + lamports: 5, + lockup_authority: std::option::Option::None, + }, + std::slice::from_ref(&authority), + 5, + ); + let withdraw_plan = crate::builder_build_prepared_plan(&withdraw) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(&withdraw_plan.instructions[0].data[..4], &4_u32.to_le_bytes()); + assert_eq!(withdraw_plan.requested_spend_lamports, 5); + + let deactivate = base_intent( + crate::SolanaCoreOperation::StakeDeactivate { + stake_account: stake, + stake_authority: authority.clone(), + }, + std::slice::from_ref(&authority), + 0, + ); + let deactivate_plan = crate::builder_build_prepared_plan(&deactivate) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(&deactivate_plan.instructions[0].data[..4], &5_u32.to_le_bytes()); + } + + #[test] + fn lockup_variants_preserve_optional_fields_and_checked_signer() { + let stake = model(solana_pubkey::Pubkey::new_unique()); + let authority = model(solana_pubkey::Pubkey::new_unique()); + let new_custodian = model(solana_pubkey::Pubkey::new_unique()); + let checked = base_intent( + crate::SolanaCoreOperation::StakeSetLockupChecked { + stake_account: stake, + authority: authority.clone(), + unix_timestamp: std::option::Option::Some(10), + epoch: std::option::Option::Some(20), + new_custodian: std::option::Option::Some(new_custodian.clone()), + }, + &[authority, new_custodian], + 0, + ); + let plan = crate::builder_build_prepared_plan(&checked) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(&plan.instructions[0].data[..4], &12_u32.to_le_bytes()); + assert_eq!(plan.instructions[0].accounts.len(), 3); + assert!(plan.instructions[0].accounts[2].is_signer); + } + + #[test] + fn minimum_delegation_and_delinquent_deactivation_require_no_instruction_signer() { + let minimum = base_intent(crate::SolanaCoreOperation::StakeGetMinimumDelegation, &[], 0); + let minimum_plan = crate::builder_build_prepared_plan(&minimum) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(&minimum_plan.instructions[0].data[..4], &13_u32.to_le_bytes()); + assert!(minimum_plan.instructions[0].accounts.is_empty()); + let delinquent = base_intent( + crate::SolanaCoreOperation::StakeDeactivateDelinquent { + stake_account: model(solana_pubkey::Pubkey::new_unique()), + delinquent_vote_account: model(solana_pubkey::Pubkey::new_unique()), + reference_vote_account: model(solana_pubkey::Pubkey::new_unique()), + }, + &[], + 0, + ); + let delinquent_plan = crate::builder_build_prepared_plan(&delinquent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(&delinquent_plan.instructions[0].data[..4], &14_u32.to_le_bytes()); + assert!( + delinquent_plan.instructions[0] + .accounts + .iter() + .all(|account| return !account.is_signer) + ); + } + + #[test] + fn move_operations_encode_exact_amounts_and_charge_spend_policy() { + let source = model(solana_pubkey::Pubkey::new_unique()); + let destination = model(solana_pubkey::Pubkey::new_unique()); + let authority = model(solana_pubkey::Pubkey::new_unique()); + for (operation, tag) in [ + ( + crate::SolanaCoreOperation::StakeMoveStake { + source_stake_account: source.clone(), + destination_stake_account: destination.clone(), + stake_authority: authority.clone(), + lamports: 44, + }, + 16_u32, + ), + ( + crate::SolanaCoreOperation::StakeMoveLamports { + source_stake_account: source.clone(), + destination_stake_account: destination.clone(), + stake_authority: authority.clone(), + lamports: 44, + }, + 17_u32, + ), + ] { + let intent = base_intent(operation, std::slice::from_ref(&authority), 44); + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(&plan.instructions[0].data[..4], &tag.to_le_bytes()); + assert_eq!(&plan.instructions[0].data[4..12], &44_u64.to_le_bytes()); + assert_eq!(plan.requested_spend_lamports, 44); + } + } + + #[test] + fn malformed_amounts_accounts_seeds_and_empty_lockup_are_rejected() { + let same = model(solana_pubkey::Pubkey::new_unique()); + let authority = model(solana_pubkey::Pubkey::new_unique()); + let move_zero = base_intent( + crate::SolanaCoreOperation::StakeMoveLamports { + source_stake_account: same.clone(), + destination_stake_account: same.clone(), + stake_authority: authority.clone(), + lamports: 0, + }, + std::slice::from_ref(&authority), + 0, + ); + assert!(crate::builder_build_prepared_plan(&move_zero).is_err()); + let empty_lockup = base_intent( + crate::SolanaCoreOperation::StakeSetLockup { + stake_account: same, + authority: authority.clone(), + unix_timestamp: std::option::Option::None, + epoch: std::option::Option::None, + custodian: std::option::Option::None, + }, + std::slice::from_ref(&authority), + 0, + ); + assert!(crate::builder_build_prepared_plan(&empty_lockup).is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/validation.rs b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/validation.rs new file mode 100644 index 0000000..af31977 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/validation.rs @@ -0,0 +1,184 @@ +// file: kb_executor_solana_core/src/validation.rs +// version: 2 + +//! Shared parsing and validation helpers for native execution plans. + +use std::str::FromStr; // rust-rules: trait-import + +/// Parses one model public key with the canonical execution error contract. +pub(crate) fn parse_pubkey( + pubkey: &kb_model::Pubkey, + field_name: &str, +) -> kb_core::Result { + return match solana_pubkey::Pubkey::from_str(pubkey.0.as_str()) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "execution_pubkey_invalid", + format!("invalid {field_name} public key {}: {error}", pubkey.0), + )), + }; +} + +/// Parses one canonical native program identifier. +pub(crate) fn parse_native_program_id(value: &str) -> kb_core::Result { + return match solana_pubkey::Pubkey::from_str(value) { + std::result::Result::Ok(program_id) => std::result::Result::Ok(program_id), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "execution_native_program_id_invalid", + format!("invalid native program id {value}: {error}"), + )), + }; +} + +/// Validates that two model public keys identify distinct accounts. +pub(crate) fn validate_distinct_pubkeys( + first: &kb_model::Pubkey, + second: &kb_model::Pubkey, + code: &str, + message: impl std::convert::Into, +) -> kb_core::Result<()> { + if first == second { + return std::result::Result::Err(kb_core::Error::new(code, message)); + } + return std::result::Result::Ok(()); +} + +/// Builds one typed validation error without duplicating domain-local wrappers. +pub(crate) fn invalid( + code: &str, + message: impl std::convert::Into, +) -> kb_core::Result { + return std::result::Result::Err(kb_core::Error::new(code, message)); +} + +/// Validates that an unsigned execution amount is strictly positive. +pub(crate) fn validate_positive_u64(value: u64, code: &str, message: &str) -> kb_core::Result<()> { + if value == 0 { + return std::result::Result::Err(kb_core::Error::new(code, message)); + } + return std::result::Result::Ok(()); +} + +/// Validates one `Pubkey::create_with_seed` derivation with caller-owned error codes. +pub(crate) fn validate_seeded_address( + address: &solana_pubkey::Pubkey, + base: &solana_pubkey::Pubkey, + seed: &str, + owner: &solana_pubkey::Pubkey, + field_name: &str, + invalid_seed_code: &str, + mismatch_code: &str, +) -> kb_core::Result<()> { + let derived = match solana_pubkey::Pubkey::create_with_seed(base, seed, owner) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + invalid_seed_code, + format!("cannot derive {field_name} from the supplied seed: {error}"), + )); + }, + }; + if derived != *address { + return std::result::Result::Err(kb_core::Error::new( + mismatch_code, + format!("{field_name} {address} does not match derived address {derived}"), + )); + } + return std::result::Result::Ok(()); +} + +/// Validates an exact byte-slice length while preserving the caller error code. +pub(crate) fn validate_exact_length( + value: &[u8], + expected: usize, + code: &str, + label: &str, +) -> kb_core::Result<()> { + if value.len() != expected { + return std::result::Result::Err(kb_core::Error::new( + code, + format!("{label} must contain exactly {expected} bytes"), + )); + } + return std::result::Result::Ok(()); +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; // rust-rules: trait-import + + fn model(value: solana_pubkey::Pubkey) -> kb_model::Pubkey { + return kb_model::Pubkey(value.to_string()); + } + + #[test] + fn pubkey_parser_preserves_the_canonical_error_contract() { + let address = solana_pubkey::Pubkey::new_unique(); + let parsed = crate::parse_pubkey(&model(address), "account"); + match parsed { + std::result::Result::Ok(value) => assert_eq!(value, address), + std::result::Result::Err(error) => panic!("valid public key rejected: {error}"), + } + let invalid = crate::parse_pubkey( + &kb_model::Pubkey(std::string::String::from("not-base58")), + "account", + ); + assert!(invalid.is_err()); + } + + #[test] + fn native_program_id_parser_accepts_official_ids_and_rejects_invalid_text() { + let parsed = crate::parse_native_program_id(kb_program_ids::SYSTEM_PROGRAM_ID); + let expected = solana_pubkey::Pubkey::from_str(kb_program_ids::SYSTEM_PROGRAM_ID) + .unwrap_or_else(|error| panic!("invalid fixture: {error}")); + match parsed { + std::result::Result::Ok(value) => assert_eq!(value, expected), + std::result::Result::Err(error) => panic!("official program id rejected: {error}"), + } + assert!(crate::parse_native_program_id("invalid").is_err()); + } + + #[test] + fn shared_scalar_and_account_validations_preserve_caller_codes() { + let first = model(solana_pubkey::Pubkey::new_unique()); + let second = model(solana_pubkey::Pubkey::new_unique()); + assert!( + crate::validate_distinct_pubkeys(&first, &second, "distinct", "must differ",).is_ok() + ); + assert!( + crate::validate_distinct_pubkeys(&first, &first, "distinct", "must differ",).is_err() + ); + assert!(crate::validate_positive_u64(1, "positive", "must be positive").is_ok()); + assert!(crate::validate_positive_u64(0, "positive", "must be positive").is_err()); + let invalid: kb_core::Result<()> = crate::invalid("invalid", "shared validation error"); + assert!(invalid.is_err()); + let base = solana_pubkey::Pubkey::new_unique(); + let owner = solana_pubkey::Pubkey::new_unique(); + let derived = solana_pubkey::Pubkey::create_with_seed(&base, "seed", &owner) + .unwrap_or_else(|error| panic!("invalid fixture: {error}")); + assert!( + crate::validate_seeded_address( + &derived, &base, "seed", &owner, "derived", "seed", "mismatch", + ) + .is_ok() + ); + assert!( + crate::validate_seeded_address( + &solana_pubkey::Pubkey::new_unique(), + &base, + "seed", + &owner, + "derived", + "seed", + "mismatch", + ) + .is_err() + ); + } + + #[test] + fn exact_length_validation_accepts_only_the_requested_shape() { + assert!(crate::validate_exact_length(&[1_u8, 2_u8], 2, "length", "value").is_ok()); + assert!(crate::validate_exact_length(&[1_u8], 2, "length", "value").is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/vote.rs b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/vote.rs new file mode 100644 index 0000000..0571c65 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/vote.rs @@ -0,0 +1,2316 @@ +// file: kb_executor_solana_core/src/vote.rs +// version: 3 + +//! Exact Vote Program instruction builders backed by `solana-vote-interface`. + +use std::str::FromStr; // rust-rules: trait-import + +const SOURCE: &str = "solana-vote-interface@6.0.3"; +const MAX_COMMISSION_BPS: u16 = 10_000; +const MAX_SEED_LENGTH: usize = 32; + +#[derive(Clone, Copy)] +enum VoteStateWireKind { + Update, + Compact, +} + +pub(crate) fn build_prepared_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, +) -> kb_core::Result { + return match &intent.operation { + crate::SolanaCoreOperation::VoteInitializeAccount { + vote_account, + node_pubkey, + authorized_voter, + authorized_withdrawer, + commission_percent, + } => build_initialize_plan( + intent, + fee_payer, + vote_account, + node_pubkey, + authorized_voter, + authorized_withdrawer, + *commission_percent, + ), + crate::SolanaCoreOperation::VoteInitializeAccountV2 { + vote_account, + node_pubkey, + authorized_voter, + authorized_voter_bls_pubkey, + authorized_voter_bls_proof_of_possession, + authorized_withdrawer, + inflation_rewards_commission_bps, + block_revenue_commission_bps, + inflation_rewards_collector, + block_revenue_collector, + } => build_initialize_v2_plan( + intent, + fee_payer, + vote_account, + node_pubkey, + authorized_voter, + authorized_voter_bls_pubkey.as_slice(), + authorized_voter_bls_proof_of_possession.as_slice(), + authorized_withdrawer, + *inflation_rewards_commission_bps, + *block_revenue_commission_bps, + inflation_rewards_collector, + block_revenue_collector, + ), + crate::SolanaCoreOperation::VoteCreateAccount { + from, + vote_account, + node_pubkey, + authorized_voter, + authorized_withdrawer, + commission_percent, + lamports, + } => build_create_account_plan( + intent, + fee_payer, + from, + vote_account, + std::option::Option::None, + node_pubkey, + authorized_voter, + authorized_withdrawer, + *commission_percent, + *lamports, + ), + crate::SolanaCoreOperation::VoteCreateAccountWithSeed { + from, + vote_account, + base, + seed, + node_pubkey, + authorized_voter, + authorized_withdrawer, + commission_percent, + lamports, + } => build_create_account_plan( + intent, + fee_payer, + from, + vote_account, + std::option::Option::Some((base, seed.as_str())), + node_pubkey, + authorized_voter, + authorized_withdrawer, + *commission_percent, + *lamports, + ), + crate::SolanaCoreOperation::VoteCreateAccountV2 { + from, + vote_account, + node_pubkey, + authorized_voter, + authorized_voter_bls_pubkey, + authorized_voter_bls_proof_of_possession, + authorized_withdrawer, + inflation_rewards_commission_bps, + block_revenue_commission_bps, + inflation_rewards_collector, + block_revenue_collector, + lamports, + } => build_create_account_v2_plan( + intent, + fee_payer, + from, + vote_account, + std::option::Option::None, + node_pubkey, + authorized_voter, + authorized_voter_bls_pubkey.as_slice(), + authorized_voter_bls_proof_of_possession.as_slice(), + authorized_withdrawer, + *inflation_rewards_commission_bps, + *block_revenue_commission_bps, + inflation_rewards_collector, + block_revenue_collector, + *lamports, + ), + crate::SolanaCoreOperation::VoteCreateAccountWithSeedV2 { + from, + vote_account, + base, + seed, + node_pubkey, + authorized_voter, + authorized_voter_bls_pubkey, + authorized_voter_bls_proof_of_possession, + authorized_withdrawer, + inflation_rewards_commission_bps, + block_revenue_commission_bps, + inflation_rewards_collector, + block_revenue_collector, + lamports, + } => build_create_account_v2_plan( + intent, + fee_payer, + from, + vote_account, + std::option::Option::Some((base, seed.as_str())), + node_pubkey, + authorized_voter, + authorized_voter_bls_pubkey.as_slice(), + authorized_voter_bls_proof_of_possession.as_slice(), + authorized_withdrawer, + *inflation_rewards_commission_bps, + *block_revenue_commission_bps, + inflation_rewards_collector, + block_revenue_collector, + *lamports, + ), + crate::SolanaCoreOperation::VoteAuthorize { + vote_account, + current_authority, + new_authority, + authorization, + } => build_authorize_plan( + intent, + fee_payer, + vote_account, + current_authority, + std::option::Option::None, + new_authority, + authorization, + false, + ), + crate::SolanaCoreOperation::VoteAuthorizeChecked { + vote_account, + current_authority, + new_authority, + authorization, + } => build_authorize_plan( + intent, + fee_payer, + vote_account, + current_authority, + std::option::Option::None, + new_authority, + authorization, + true, + ), + crate::SolanaCoreOperation::VoteAuthorizeWithSeed { + vote_account, + current_authority_base, + current_authority_derived_key_owner, + current_authority_derived_key_seed, + new_authority, + authorization, + } => build_authorize_plan( + intent, + fee_payer, + vote_account, + current_authority_base, + std::option::Option::Some(( + current_authority_derived_key_owner, + current_authority_derived_key_seed.as_str(), + )), + new_authority, + authorization, + false, + ), + crate::SolanaCoreOperation::VoteAuthorizeCheckedWithSeed { + vote_account, + current_authority_base, + current_authority_derived_key_owner, + current_authority_derived_key_seed, + new_authority, + authorization, + } => build_authorize_plan( + intent, + fee_payer, + vote_account, + current_authority_base, + std::option::Option::Some(( + current_authority_derived_key_owner, + current_authority_derived_key_seed.as_str(), + )), + new_authority, + authorization, + true, + ), + crate::SolanaCoreOperation::VoteSubmit { + vote_account, + authorized_voter, + slots, + bank_hash, + timestamp, + } => build_vote_plan( + intent, + fee_payer, + vote_account, + authorized_voter, + slots.as_slice(), + bank_hash, + *timestamp, + std::option::Option::None, + ), + crate::SolanaCoreOperation::VoteSwitch { + vote_account, + authorized_voter, + slots, + bank_hash, + timestamp, + switch_proof_hash, + } => build_vote_plan( + intent, + fee_payer, + vote_account, + authorized_voter, + slots.as_slice(), + bank_hash, + *timestamp, + std::option::Option::Some(switch_proof_hash.as_str()), + ), + crate::SolanaCoreOperation::VoteWithdraw { + vote_account, + withdraw_authority, + to, + lamports, + } => { + build_withdraw_plan(intent, fee_payer, vote_account, withdraw_authority, to, *lamports) + }, + crate::SolanaCoreOperation::VoteUpdateValidatorIdentity { + vote_account, + withdraw_authority, + new_validator_identity, + } => build_update_validator_identity_plan( + intent, + fee_payer, + vote_account, + withdraw_authority, + new_validator_identity, + ), + crate::SolanaCoreOperation::VoteUpdateCommission { + vote_account, + withdraw_authority, + commission_percent, + } => build_update_commission_plan( + intent, + fee_payer, + vote_account, + withdraw_authority, + *commission_percent, + ), + crate::SolanaCoreOperation::VoteUpdateVoteState { + vote_account, + authorized_voter, + lockouts, + root, + bank_hash, + timestamp, + } => build_vote_state_plan( + intent, + fee_payer, + vote_account, + authorized_voter, + lockouts.as_slice(), + *root, + bank_hash, + *timestamp, + VoteStateWireKind::Update, + std::option::Option::None, + ), + crate::SolanaCoreOperation::VoteUpdateVoteStateSwitch { + vote_account, + authorized_voter, + lockouts, + root, + bank_hash, + timestamp, + switch_proof_hash, + } => build_vote_state_plan( + intent, + fee_payer, + vote_account, + authorized_voter, + lockouts.as_slice(), + *root, + bank_hash, + *timestamp, + VoteStateWireKind::Update, + std::option::Option::Some(switch_proof_hash.as_str()), + ), + crate::SolanaCoreOperation::VoteCompactUpdateVoteState { + vote_account, + authorized_voter, + lockouts, + root, + bank_hash, + timestamp, + } => build_vote_state_plan( + intent, + fee_payer, + vote_account, + authorized_voter, + lockouts.as_slice(), + *root, + bank_hash, + *timestamp, + VoteStateWireKind::Compact, + std::option::Option::None, + ), + crate::SolanaCoreOperation::VoteCompactUpdateVoteStateSwitch { + vote_account, + authorized_voter, + lockouts, + root, + bank_hash, + timestamp, + switch_proof_hash, + } => build_vote_state_plan( + intent, + fee_payer, + vote_account, + authorized_voter, + lockouts.as_slice(), + *root, + bank_hash, + *timestamp, + VoteStateWireKind::Compact, + std::option::Option::Some(switch_proof_hash.as_str()), + ), + crate::SolanaCoreOperation::VoteTowerSync { + vote_account, + authorized_voter, + lockouts, + root, + bank_hash, + timestamp, + block_id, + } => build_tower_sync_plan( + intent, + fee_payer, + vote_account, + authorized_voter, + lockouts.as_slice(), + *root, + bank_hash, + *timestamp, + block_id, + std::option::Option::None, + ), + crate::SolanaCoreOperation::VoteTowerSyncSwitch { + vote_account, + authorized_voter, + lockouts, + root, + bank_hash, + timestamp, + block_id, + switch_proof_hash, + } => build_tower_sync_plan( + intent, + fee_payer, + vote_account, + authorized_voter, + lockouts.as_slice(), + *root, + bank_hash, + *timestamp, + block_id, + std::option::Option::Some(switch_proof_hash.as_str()), + ), + crate::SolanaCoreOperation::VoteUpdateCommissionCollector { + vote_account, + withdraw_authority, + new_collector, + commission_kind, + } => build_update_commission_collector_plan( + intent, + fee_payer, + vote_account, + withdraw_authority, + new_collector, + *commission_kind, + ), + crate::SolanaCoreOperation::VoteUpdateCommissionBps { + vote_account, + withdraw_authority, + commission_kind, + commission_bps, + } => build_update_commission_bps_plan( + intent, + fee_payer, + vote_account, + withdraw_authority, + *commission_kind, + *commission_bps, + ), + crate::SolanaCoreOperation::VoteDepositDelegatorRewards { + vote_account, + source, + deposit_lamports, + } => build_deposit_delegator_rewards_plan( + intent, + fee_payer, + vote_account, + source, + *deposit_lamports, + ), + _ => std::result::Result::Err(kb_core::Error::new( + "execution_vote_operation_mismatch", + "Vote builder received an unrelated Solana core operation", + )), + }; +} + +#[allow(clippy::too_many_arguments)] +fn build_initialize_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + vote_account: &kb_model::Pubkey, + node_pubkey: &kb_model::Pubkey, + authorized_voter: &kb_model::Pubkey, + authorized_withdrawer: &kb_model::Pubkey, + commission_percent: u8, +) -> kb_core::Result { + let instruction = match initialize_instruction( + vote_account, + node_pubkey, + authorized_voter, + authorized_withdrawer, + commission_percent, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::VOTE_INITIALIZE_ACCOUNT_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + vec![(node_pubkey.clone(), std::string::String::from("vote_node_identity"))], + ); +} + +#[allow(clippy::too_many_arguments)] +fn build_initialize_v2_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + vote_account: &kb_model::Pubkey, + node_pubkey: &kb_model::Pubkey, + authorized_voter: &kb_model::Pubkey, + authorized_voter_bls_pubkey: &[u8], + authorized_voter_bls_proof_of_possession: &[u8], + authorized_withdrawer: &kb_model::Pubkey, + inflation_rewards_commission_bps: u16, + block_revenue_commission_bps: u16, + inflation_rewards_collector: &kb_model::Pubkey, + block_revenue_collector: &kb_model::Pubkey, +) -> kb_core::Result { + let instruction = match initialize_v2_instruction( + vote_account, + node_pubkey, + authorized_voter, + authorized_voter_bls_pubkey, + authorized_voter_bls_proof_of_possession, + authorized_withdrawer, + inflation_rewards_commission_bps, + block_revenue_commission_bps, + inflation_rewards_collector, + block_revenue_collector, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::VOTE_INITIALIZE_ACCOUNT_V2_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + vec![(node_pubkey.clone(), std::string::String::from("vote_node_identity"))], + ); +} + +#[allow(clippy::too_many_arguments)] +fn build_create_account_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + from: &kb_model::Pubkey, + vote_account: &kb_model::Pubkey, + seed_contract: std::option::Option<(&kb_model::Pubkey, &str)>, + node_pubkey: &kb_model::Pubkey, + authorized_voter: &kb_model::Pubkey, + authorized_withdrawer: &kb_model::Pubkey, + commission_percent: u8, + lamports: u64, +) -> kb_core::Result { + match crate::validate_positive_u64( + lamports, + "execution_vote_create_lamports_zero", + "Vote account creation requires a positive measured rent-exempt lamport amount", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_distinct_pubkeys( + from, + vote_account, + "execution_vote_create_same_account", + "Vote funding and new Vote accounts must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let from_address = match crate::parse_pubkey(from, "from") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let vote_address = match crate::parse_pubkey(vote_account, "vote_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let vote_program = match vote_program_id() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut signer_roles = vec![(from.clone(), std::string::String::from("vote_funding_account"))]; + let create = match seed_contract { + std::option::Option::Some((base, seed)) => { + let base_address = match crate::parse_pubkey(base, "base") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + match validate_seeded_address( + &vote_address, + &base_address, + seed, + &vote_program, + "vote_account", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + signer_roles.push((base.clone(), std::string::String::from("vote_seed_base"))); + solana_system_interface::instruction::create_account_with_seed( + &from_address, + &vote_address, + &base_address, + seed, + lamports, + solana_vote_interface::state::VoteStateV4::size_of() as u64, + &vote_program, + ) + }, + std::option::Option::None => { + signer_roles + .push((vote_account.clone(), std::string::String::from("new_vote_account"))); + solana_system_interface::instruction::create_account( + &from_address, + &vote_address, + lamports, + solana_vote_interface::state::VoteStateV4::size_of() as u64, + &vote_program, + ) + }, + }; + let initialize = match initialize_instruction( + vote_account, + node_pubkey, + authorized_voter, + authorized_withdrawer, + commission_percent, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + signer_roles.push((node_pubkey.clone(), std::string::String::from("vote_node_identity"))); + let operation_code = if seed_contract.is_some() { + crate::VOTE_CREATE_ACCOUNT_WITH_SEED_OPERATION + } else { + crate::VOTE_CREATE_ACCOUNT_OPERATION + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + operation_code, + vec![create, initialize], + lamports, + std::option::Option::None, + signer_roles, + ); +} + +#[allow(clippy::too_many_arguments)] +fn build_create_account_v2_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + from: &kb_model::Pubkey, + vote_account: &kb_model::Pubkey, + seed_contract: std::option::Option<(&kb_model::Pubkey, &str)>, + node_pubkey: &kb_model::Pubkey, + authorized_voter: &kb_model::Pubkey, + authorized_voter_bls_pubkey: &[u8], + authorized_voter_bls_proof_of_possession: &[u8], + authorized_withdrawer: &kb_model::Pubkey, + inflation_rewards_commission_bps: u16, + block_revenue_commission_bps: u16, + inflation_rewards_collector: &kb_model::Pubkey, + block_revenue_collector: &kb_model::Pubkey, + lamports: u64, +) -> kb_core::Result { + match crate::validate_positive_u64( + lamports, + "execution_vote_create_lamports_zero", + "Vote account creation requires a positive measured rent-exempt lamport amount", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_distinct_pubkeys( + from, + vote_account, + "execution_vote_create_same_account", + "Vote funding and new Vote accounts must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let from_address = match crate::parse_pubkey(from, "from") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let vote_address = match crate::parse_pubkey(vote_account, "vote_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let vote_program = match vote_program_id() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut signer_roles = vec![(from.clone(), std::string::String::from("vote_funding_account"))]; + let create = match seed_contract { + std::option::Option::Some((base, seed)) => { + let base_address = match crate::parse_pubkey(base, "base") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + match validate_seeded_address( + &vote_address, + &base_address, + seed, + &vote_program, + "vote_account", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + signer_roles.push((base.clone(), std::string::String::from("vote_seed_base"))); + solana_system_interface::instruction::create_account_with_seed( + &from_address, + &vote_address, + &base_address, + seed, + lamports, + solana_vote_interface::state::VoteStateV4::size_of() as u64, + &vote_program, + ) + }, + std::option::Option::None => { + signer_roles + .push((vote_account.clone(), std::string::String::from("new_vote_account"))); + solana_system_interface::instruction::create_account( + &from_address, + &vote_address, + lamports, + solana_vote_interface::state::VoteStateV4::size_of() as u64, + &vote_program, + ) + }, + }; + let initialize = match initialize_v2_instruction( + vote_account, + node_pubkey, + authorized_voter, + authorized_voter_bls_pubkey, + authorized_voter_bls_proof_of_possession, + authorized_withdrawer, + inflation_rewards_commission_bps, + block_revenue_commission_bps, + inflation_rewards_collector, + block_revenue_collector, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + signer_roles.push((node_pubkey.clone(), std::string::String::from("vote_node_identity"))); + let operation_code = if seed_contract.is_some() { + crate::VOTE_CREATE_ACCOUNT_WITH_SEED_V2_OPERATION + } else { + crate::VOTE_CREATE_ACCOUNT_V2_OPERATION + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + operation_code, + vec![create, initialize], + lamports, + std::option::Option::None, + signer_roles, + ); +} + +#[allow(clippy::too_many_arguments)] +fn build_authorize_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + vote_account: &kb_model::Pubkey, + current_authority_or_base: &kb_model::Pubkey, + seed_contract: std::option::Option<(&kb_model::Pubkey, &str)>, + new_authority: &kb_model::Pubkey, + authorization: &crate::VoteAuthorizationKind, + checked: bool, +) -> kb_core::Result { + let vote_address = match crate::parse_pubkey(vote_account, "vote_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let current_authority = + match crate::parse_pubkey(current_authority_or_base, "current_authority_or_base") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let new_authority_address = match crate::parse_pubkey(new_authority, "new_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authorization_type = match vote_authorization(authorization) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let (wire, operation_code) = match (seed_contract, checked) { + (std::option::Option::None, false) => ( + solana_vote_interface::instruction::VoteInstruction::Authorize( + new_authority_address, + authorization_type, + ), + crate::VOTE_AUTHORIZE_OPERATION, + ), + (std::option::Option::None, true) => ( + solana_vote_interface::instruction::VoteInstruction::AuthorizeChecked( + authorization_type, + ), + crate::VOTE_AUTHORIZE_CHECKED_OPERATION, + ), + (std::option::Option::Some((owner, seed)), false) => { + let owner_address = + match crate::parse_pubkey(owner, "current_authority_derived_key_owner") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + match validate_seed(seed, "current_authority_derived_key_seed") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + ( + solana_vote_interface::instruction::VoteInstruction::AuthorizeWithSeed( + solana_vote_interface::state::VoteAuthorizeWithSeedArgs { + authorization_type, + current_authority_derived_key_owner: owner_address, + current_authority_derived_key_seed: std::string::String::from(seed), + new_authority: new_authority_address, + }, + ), + crate::VOTE_AUTHORIZE_WITH_SEED_OPERATION, + ) + }, + (std::option::Option::Some((owner, seed)), true) => { + let owner_address = + match crate::parse_pubkey(owner, "current_authority_derived_key_owner") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + match validate_seed(seed, "current_authority_derived_key_seed") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + ( + solana_vote_interface::instruction::VoteInstruction::AuthorizeCheckedWithSeed( + solana_vote_interface::state::VoteAuthorizeCheckedWithSeedArgs { + authorization_type, + current_authority_derived_key_owner: owner_address, + current_authority_derived_key_seed: std::string::String::from(seed), + }, + ), + crate::VOTE_AUTHORIZE_CHECKED_WITH_SEED_OPERATION, + ) + }, + }; + let mut accounts = vec![ + solana_instruction::AccountMeta::new(vote_address, false), + solana_instruction::AccountMeta::new_readonly(solana_sdk_ids::sysvar::clock::id(), false), + solana_instruction::AccountMeta::new_readonly(current_authority, true), + ]; + let mut signer_roles = vec![( + current_authority_or_base.clone(), + std::string::String::from("vote_current_authority"), + )]; + if checked { + accounts.push(solana_instruction::AccountMeta::new_readonly(new_authority_address, true)); + signer_roles.push((new_authority.clone(), std::string::String::from("vote_new_authority"))); + } + let instruction = match vote_instruction(wire, accounts) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + operation_code, + vec![instruction], + 0, + std::option::Option::None, + signer_roles, + ); +} + +#[allow(clippy::too_many_arguments)] +fn build_vote_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + vote_account: &kb_model::Pubkey, + authorized_voter: &kb_model::Pubkey, + slots: &[u64], + bank_hash: &str, + timestamp: std::option::Option, + switch_proof_hash: std::option::Option<&str>, +) -> kb_core::Result { + let vote = match vote_value(slots, bank_hash, timestamp) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let (wire, operation_code) = match switch_proof_hash { + std::option::Option::Some(value) => { + let proof = match parse_hash(value, "switch_proof_hash") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + ( + solana_vote_interface::instruction::VoteInstruction::VoteSwitch(vote, proof), + crate::VOTE_SWITCH_OPERATION, + ) + }, + std::option::Option::None => ( + solana_vote_interface::instruction::VoteInstruction::Vote(vote), + crate::VOTE_SUBMIT_OPERATION, + ), + }; + let vote_address = match crate::parse_pubkey(vote_account, "vote_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let voter_address = match crate::parse_pubkey(authorized_voter, "authorized_voter") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = match vote_instruction( + wire, + vec![ + solana_instruction::AccountMeta::new(vote_address, false), + solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::sysvar::slot_hashes::id(), + false, + ), + solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::sysvar::clock::id(), + false, + ), + solana_instruction::AccountMeta::new_readonly(voter_address, true), + ], + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + operation_code, + vec![instruction], + 0, + std::option::Option::None, + vec![(authorized_voter.clone(), std::string::String::from("vote_authority"))], + ); +} + +fn build_withdraw_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + vote_account: &kb_model::Pubkey, + withdraw_authority: &kb_model::Pubkey, + to: &kb_model::Pubkey, + lamports: u64, +) -> kb_core::Result { + match crate::validate_positive_u64( + lamports, + "execution_vote_withdraw_lamports_zero", + "Vote withdrawal requires a positive lamport amount", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_distinct_pubkeys( + vote_account, + to, + "execution_vote_withdraw_same_account", + "Vote account and withdrawal recipient must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let vote_address = match crate::parse_pubkey(vote_account, "vote_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let to_address = match crate::parse_pubkey(to, "to") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(withdraw_authority, "withdraw_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = match vote_instruction( + solana_vote_interface::instruction::VoteInstruction::Withdraw(lamports), + vec![ + solana_instruction::AccountMeta::new(vote_address, false), + solana_instruction::AccountMeta::new(to_address, false), + solana_instruction::AccountMeta::new_readonly(authority_address, true), + ], + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::VOTE_WITHDRAW_OPERATION, + vec![instruction], + lamports, + std::option::Option::None, + vec![(withdraw_authority.clone(), std::string::String::from("vote_withdraw_authority"))], + ); +} + +fn build_update_validator_identity_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + vote_account: &kb_model::Pubkey, + withdraw_authority: &kb_model::Pubkey, + new_validator_identity: &kb_model::Pubkey, +) -> kb_core::Result { + let vote_address = match crate::parse_pubkey(vote_account, "vote_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let withdraw_address = match crate::parse_pubkey(withdraw_authority, "withdraw_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let identity_address = + match crate::parse_pubkey(new_validator_identity, "new_validator_identity") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = match vote_instruction( + solana_vote_interface::instruction::VoteInstruction::UpdateValidatorIdentity, + vec![ + solana_instruction::AccountMeta::new(vote_address, false), + solana_instruction::AccountMeta::new_readonly(identity_address, true), + solana_instruction::AccountMeta::new_readonly(withdraw_address, true), + ], + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::VOTE_UPDATE_VALIDATOR_IDENTITY_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + vec![ + ( + new_validator_identity.clone(), + std::string::String::from("vote_new_node_identity"), + ), + (withdraw_authority.clone(), std::string::String::from("vote_withdraw_authority")), + ], + ); +} + +fn build_update_commission_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + vote_account: &kb_model::Pubkey, + withdraw_authority: &kb_model::Pubkey, + commission_percent: u8, +) -> kb_core::Result { + match validate_commission_percent(commission_percent) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let instruction = match admin_instruction( + solana_vote_interface::instruction::VoteInstruction::UpdateCommission(commission_percent), + vote_account, + withdraw_authority, + std::option::Option::None, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::VOTE_UPDATE_COMMISSION_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + vec![(withdraw_authority.clone(), std::string::String::from("vote_withdraw_authority"))], + ); +} + +#[allow(clippy::too_many_arguments)] +fn build_vote_state_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + vote_account: &kb_model::Pubkey, + authorized_voter: &kb_model::Pubkey, + lockouts: &[crate::VoteLockout], + root: std::option::Option, + bank_hash: &str, + timestamp: std::option::Option, + wire_kind: VoteStateWireKind, + switch_proof_hash: std::option::Option<&str>, +) -> kb_core::Result { + let update = match vote_state_update(lockouts, root, bank_hash, timestamp) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let (wire, operation_code) = match (wire_kind, switch_proof_hash) { + (VoteStateWireKind::Update, std::option::Option::None) => ( + solana_vote_interface::instruction::VoteInstruction::UpdateVoteState(update), + crate::VOTE_UPDATE_VOTE_STATE_OPERATION, + ), + (VoteStateWireKind::Update, std::option::Option::Some(value)) => { + let proof = match parse_hash(value, "switch_proof_hash") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + ( + solana_vote_interface::instruction::VoteInstruction::UpdateVoteStateSwitch( + update, proof, + ), + crate::VOTE_UPDATE_VOTE_STATE_SWITCH_OPERATION, + ) + }, + (VoteStateWireKind::Compact, std::option::Option::None) => ( + solana_vote_interface::instruction::VoteInstruction::CompactUpdateVoteState(update), + crate::VOTE_COMPACT_UPDATE_VOTE_STATE_OPERATION, + ), + (VoteStateWireKind::Compact, std::option::Option::Some(value)) => { + let proof = match parse_hash(value, "switch_proof_hash") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + ( + solana_vote_interface::instruction::VoteInstruction::CompactUpdateVoteStateSwitch( + update, proof, + ), + crate::VOTE_COMPACT_UPDATE_VOTE_STATE_SWITCH_OPERATION, + ) + }, + }; + return finalize_vote_authority_instruction( + intent, + fee_payer, + operation_code, + vote_account, + authorized_voter, + wire, + ); +} + +#[allow(clippy::too_many_arguments)] +fn build_tower_sync_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + vote_account: &kb_model::Pubkey, + authorized_voter: &kb_model::Pubkey, + lockouts: &[crate::VoteLockout], + root: std::option::Option, + bank_hash: &str, + timestamp: std::option::Option, + block_id: &str, + switch_proof_hash: std::option::Option<&str>, +) -> kb_core::Result { + let tower = match tower_sync(lockouts, root, bank_hash, timestamp, block_id) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let (wire, operation_code) = match switch_proof_hash { + std::option::Option::Some(value) => { + let proof = match parse_hash(value, "switch_proof_hash") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + ( + solana_vote_interface::instruction::VoteInstruction::TowerSyncSwitch(tower, proof), + crate::VOTE_TOWER_SYNC_SWITCH_OPERATION, + ) + }, + std::option::Option::None => ( + solana_vote_interface::instruction::VoteInstruction::TowerSync(tower), + crate::VOTE_TOWER_SYNC_OPERATION, + ), + }; + return finalize_vote_authority_instruction( + intent, + fee_payer, + operation_code, + vote_account, + authorized_voter, + wire, + ); +} + +fn build_update_commission_collector_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + vote_account: &kb_model::Pubkey, + withdraw_authority: &kb_model::Pubkey, + new_collector: &kb_model::Pubkey, + commission_kind: crate::VoteCommissionKind, +) -> kb_core::Result { + let instruction = match admin_instruction( + solana_vote_interface::instruction::VoteInstruction::UpdateCommissionCollector( + commission_kind.as_official(), + ), + vote_account, + withdraw_authority, + std::option::Option::Some(new_collector), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::VOTE_UPDATE_COMMISSION_COLLECTOR_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + vec![(withdraw_authority.clone(), std::string::String::from("vote_withdraw_authority"))], + ); +} + +fn build_update_commission_bps_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + vote_account: &kb_model::Pubkey, + withdraw_authority: &kb_model::Pubkey, + commission_kind: crate::VoteCommissionKind, + commission_bps: u16, +) -> kb_core::Result { + match validate_commission_bps(commission_bps, "commission_bps") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let instruction = match admin_instruction( + solana_vote_interface::instruction::VoteInstruction::UpdateCommissionBps { + commission_bps, + kind: commission_kind.as_official(), + }, + vote_account, + withdraw_authority, + std::option::Option::None, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::VOTE_UPDATE_COMMISSION_BPS_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + vec![(withdraw_authority.clone(), std::string::String::from("vote_withdraw_authority"))], + ); +} + +fn build_deposit_delegator_rewards_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + vote_account: &kb_model::Pubkey, + source: &kb_model::Pubkey, + deposit_lamports: u64, +) -> kb_core::Result { + match crate::validate_positive_u64( + deposit_lamports, + "execution_vote_deposit_lamports_zero", + "Vote delegator-reward deposit requires a positive lamport amount", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::validate_distinct_pubkeys( + vote_account, + source, + "execution_vote_deposit_same_account", + "Vote account and delegator-reward source must differ", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let vote_address = match crate::parse_pubkey(vote_account, "vote_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let source_address = match crate::parse_pubkey(source, "source") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = match vote_instruction( + solana_vote_interface::instruction::VoteInstruction::DepositDelegatorRewards { + deposit: deposit_lamports, + }, + vec![ + solana_instruction::AccountMeta::new(vote_address, false), + solana_instruction::AccountMeta::new(source_address, true), + ], + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::VOTE_DEPOSIT_DELEGATOR_REWARDS_OPERATION, + vec![instruction], + deposit_lamports, + std::option::Option::None, + vec![(source.clone(), std::string::String::from("vote_reward_source"))], + ); +} + +#[allow(clippy::too_many_arguments)] +fn initialize_instruction( + vote_account: &kb_model::Pubkey, + node_pubkey: &kb_model::Pubkey, + authorized_voter: &kb_model::Pubkey, + authorized_withdrawer: &kb_model::Pubkey, + commission_percent: u8, +) -> kb_core::Result { + match validate_commission_percent(commission_percent) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let vote_address = match crate::parse_pubkey(vote_account, "vote_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let node_address = match crate::parse_pubkey(node_pubkey, "node_pubkey") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let voter_address = match crate::parse_pubkey(authorized_voter, "authorized_voter") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let withdrawer_address = + match crate::parse_pubkey(authorized_withdrawer, "authorized_withdrawer") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return vote_instruction( + solana_vote_interface::instruction::VoteInstruction::InitializeAccount( + solana_vote_interface::state::VoteInit { + node_pubkey: node_address, + authorized_voter: voter_address, + authorized_withdrawer: withdrawer_address, + commission: commission_percent, + }, + ), + vec![ + solana_instruction::AccountMeta::new(vote_address, false), + solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::sysvar::rent::id(), + false, + ), + solana_instruction::AccountMeta::new_readonly( + solana_sdk_ids::sysvar::clock::id(), + false, + ), + solana_instruction::AccountMeta::new_readonly(node_address, true), + ], + ); +} + +#[allow(clippy::too_many_arguments)] +fn initialize_v2_instruction( + vote_account: &kb_model::Pubkey, + node_pubkey: &kb_model::Pubkey, + authorized_voter: &kb_model::Pubkey, + authorized_voter_bls_pubkey: &[u8], + authorized_voter_bls_proof_of_possession: &[u8], + authorized_withdrawer: &kb_model::Pubkey, + inflation_rewards_commission_bps: u16, + block_revenue_commission_bps: u16, + inflation_rewards_collector: &kb_model::Pubkey, + block_revenue_collector: &kb_model::Pubkey, +) -> kb_core::Result { + match validate_commission_bps( + inflation_rewards_commission_bps, + "inflation_rewards_commission_bps", + ) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match validate_commission_bps(block_revenue_commission_bps, "block_revenue_commission_bps") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let bls_pubkey = match fixed_bls_pubkey(authorized_voter_bls_pubkey) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let bls_proof = match fixed_bls_proof(authorized_voter_bls_proof_of_possession) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let vote_address = match crate::parse_pubkey(vote_account, "vote_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let node_address = match crate::parse_pubkey(node_pubkey, "node_pubkey") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let voter_address = match crate::parse_pubkey(authorized_voter, "authorized_voter") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let withdrawer_address = + match crate::parse_pubkey(authorized_withdrawer, "authorized_withdrawer") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let inflation_collector = + match crate::parse_pubkey(inflation_rewards_collector, "inflation_rewards_collector") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let block_collector = + match crate::parse_pubkey(block_revenue_collector, "block_revenue_collector") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return vote_instruction( + solana_vote_interface::instruction::VoteInstruction::InitializeAccountV2( + solana_vote_interface::state::VoteInitV2 { + node_pubkey: node_address, + authorized_voter: voter_address, + authorized_voter_bls_pubkey: bls_pubkey, + authorized_voter_bls_proof_of_possession: bls_proof, + authorized_withdrawer: withdrawer_address, + inflation_rewards_commission_bps, + block_revenue_commission_bps, + }, + ), + vec![ + solana_instruction::AccountMeta::new(vote_address, false), + solana_instruction::AccountMeta::new_readonly(node_address, true), + solana_instruction::AccountMeta::new(inflation_collector, false), + solana_instruction::AccountMeta::new(block_collector, false), + ], + ); +} + +fn vote_authorization( + value: &crate::VoteAuthorizationKind, +) -> kb_core::Result { + return match value { + crate::VoteAuthorizationKind::Voter => { + std::result::Result::Ok(solana_vote_interface::state::VoteAuthorize::Voter) + }, + crate::VoteAuthorizationKind::Withdrawer => { + std::result::Result::Ok(solana_vote_interface::state::VoteAuthorize::Withdrawer) + }, + crate::VoteAuthorizationKind::VoterWithBls { bls_pubkey, bls_proof_of_possession } => { + let pubkey = match fixed_bls_pubkey(bls_pubkey.as_slice()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let proof = match fixed_bls_proof(bls_proof_of_possession.as_slice()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + std::result::Result::Ok(solana_vote_interface::state::VoteAuthorize::VoterWithBLS( + solana_vote_interface::state::VoterWithBLSArgs { + bls_pubkey: pubkey, + bls_proof_of_possession: proof, + }, + )) + }, + }; +} + +fn vote_value( + slots: &[u64], + bank_hash: &str, + timestamp: std::option::Option, +) -> kb_core::Result { + match validate_slots(slots) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let hash = match parse_hash(bank_hash, "bank_hash") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(solana_vote_interface::state::Vote { + slots: slots.to_vec(), + hash, + timestamp, + }); +} + +fn vote_state_update( + lockouts: &[crate::VoteLockout], + root: std::option::Option, + bank_hash: &str, + timestamp: std::option::Option, +) -> kb_core::Result { + let wire_lockouts = match lockout_deque(lockouts, root) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let hash = match parse_hash(bank_hash, "bank_hash") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(solana_vote_interface::state::VoteStateUpdate { + lockouts: wire_lockouts, + root, + hash, + timestamp, + }); +} + +fn tower_sync( + lockouts: &[crate::VoteLockout], + root: std::option::Option, + bank_hash: &str, + timestamp: std::option::Option, + block_id: &str, +) -> kb_core::Result { + let wire_lockouts = match lockout_deque(lockouts, root) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let hash = match parse_hash(bank_hash, "bank_hash") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let block_hash = match parse_hash(block_id, "block_id") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(solana_vote_interface::state::TowerSync { + lockouts: wire_lockouts, + root, + hash, + timestamp, + block_id: block_hash, + }); +} + +fn lockout_deque( + lockouts: &[crate::VoteLockout], + root: std::option::Option, +) -> kb_core::Result> { + if lockouts.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_vote_lockouts_empty", + "Vote state and tower updates require at least one lockout", + )); + } + if lockouts.len() > solana_vote_interface::state::MAX_LOCKOUT_HISTORY { + return std::result::Result::Err(kb_core::Error::new( + "execution_vote_lockouts_too_many", + format!( + "Vote lockout count {} exceeds official maximum {}", + lockouts.len(), + solana_vote_interface::state::MAX_LOCKOUT_HISTORY + ), + )); + } + let mut previous = std::option::Option::None; + let mut output = std::collections::VecDeque::with_capacity(lockouts.len()); + for lockout in lockouts { + if lockout.confirmation_count > solana_vote_interface::state::MAX_LOCKOUT_HISTORY as u32 { + return std::result::Result::Err(kb_core::Error::new( + "execution_vote_confirmation_count_invalid", + format!( + "Vote confirmation count {} exceeds official maximum {}", + lockout.confirmation_count, + solana_vote_interface::state::MAX_LOCKOUT_HISTORY + ), + )); + } + if let std::option::Option::Some(previous_slot) = previous { + if lockout.slot <= previous_slot { + return std::result::Result::Err(kb_core::Error::new( + "execution_vote_lockouts_not_strictly_increasing", + "Vote lockout slots must be strictly increasing", + )); + } + } + previous = std::option::Option::Some(lockout.slot); + output.push_back(solana_vote_interface::state::Lockout::new_with_confirmation_count( + lockout.slot, + lockout.confirmation_count, + )); + } + if let std::option::Option::Some(root_slot) = root { + if let std::option::Option::Some(first) = lockouts.first() { + if root_slot > first.slot { + return std::result::Result::Err(kb_core::Error::new( + "execution_vote_root_above_first_lockout", + "Vote root must be at or below the first lockout slot", + )); + } + } + } + return std::result::Result::Ok(output); +} + +fn finalize_vote_authority_instruction( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + operation_code: &str, + vote_account: &kb_model::Pubkey, + authorized_voter: &kb_model::Pubkey, + wire: solana_vote_interface::instruction::VoteInstruction, +) -> kb_core::Result { + let vote_address = match crate::parse_pubkey(vote_account, "vote_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let voter_address = match crate::parse_pubkey(authorized_voter, "authorized_voter") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = match vote_instruction( + wire, + vec![ + solana_instruction::AccountMeta::new(vote_address, false), + solana_instruction::AccountMeta::new_readonly(voter_address, true), + ], + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + operation_code, + vec![instruction], + 0, + std::option::Option::None, + vec![(authorized_voter.clone(), std::string::String::from("vote_authority"))], + ); +} + +fn admin_instruction( + wire: solana_vote_interface::instruction::VoteInstruction, + vote_account: &kb_model::Pubkey, + withdraw_authority: &kb_model::Pubkey, + writable_middle_account: std::option::Option<&kb_model::Pubkey>, +) -> kb_core::Result { + let vote_address = match crate::parse_pubkey(vote_account, "vote_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(withdraw_authority, "withdraw_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut accounts = vec![solana_instruction::AccountMeta::new(vote_address, false)]; + if let std::option::Option::Some(account) = writable_middle_account { + let account_address = match crate::parse_pubkey(account, "writable_middle_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + accounts.push(solana_instruction::AccountMeta::new(account_address, false)); + } + accounts.push(solana_instruction::AccountMeta::new_readonly(authority_address, true)); + return vote_instruction(wire, accounts); +} + +fn vote_instruction( + wire: solana_vote_interface::instruction::VoteInstruction, + accounts: std::vec::Vec, +) -> kb_core::Result { + let data = match wincode::serialize(&wire) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_vote_wincode_failed", + format!("cannot serialize {SOURCE} instruction: {error}"), + )); + }, + }; + let program_id = match vote_program_id() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(solana_instruction::Instruction { program_id, accounts, data }); +} + +fn vote_program_id() -> kb_core::Result { + return crate::parse_native_program_id(kb_program_ids::VOTE_PROGRAM_ID); +} + +fn validate_commission_percent(value: u8) -> kb_core::Result<()> { + if value > 100 { + return std::result::Result::Err(kb_core::Error::new( + "execution_vote_commission_percent_invalid", + format!("Vote legacy commission percentage {value} exceeds 100"), + )); + } + return std::result::Result::Ok(()); +} + +fn validate_commission_bps(value: u16, field_name: &str) -> kb_core::Result<()> { + if value > MAX_COMMISSION_BPS { + return std::result::Result::Err(kb_core::Error::new( + "execution_vote_commission_bps_invalid", + format!("Vote {field_name} value {value} exceeds {MAX_COMMISSION_BPS}"), + )); + } + return std::result::Result::Ok(()); +} + +fn fixed_bls_pubkey( + value: &[u8], +) -> kb_core::Result<[u8; solana_vote_interface::state::BLS_PUBLIC_KEY_COMPRESSED_SIZE]> { + if value.len() != solana_vote_interface::state::BLS_PUBLIC_KEY_COMPRESSED_SIZE { + return std::result::Result::Err(kb_core::Error::new( + "execution_vote_bls_pubkey_length_invalid", + format!( + "Vote BLS public key length {} must equal {}", + value.len(), + solana_vote_interface::state::BLS_PUBLIC_KEY_COMPRESSED_SIZE + ), + )); + } + let mut output = [0_u8; solana_vote_interface::state::BLS_PUBLIC_KEY_COMPRESSED_SIZE]; + output.copy_from_slice(value); + return std::result::Result::Ok(output); +} + +fn fixed_bls_proof( + value: &[u8], +) -> kb_core::Result<[u8; solana_vote_interface::state::BLS_PROOF_OF_POSSESSION_COMPRESSED_SIZE]> { + if value.len() != solana_vote_interface::state::BLS_PROOF_OF_POSSESSION_COMPRESSED_SIZE { + return std::result::Result::Err(kb_core::Error::new( + "execution_vote_bls_proof_length_invalid", + format!( + "Vote BLS proof length {} must equal {}", + value.len(), + solana_vote_interface::state::BLS_PROOF_OF_POSSESSION_COMPRESSED_SIZE + ), + )); + } + let mut output = [0_u8; solana_vote_interface::state::BLS_PROOF_OF_POSSESSION_COMPRESSED_SIZE]; + output.copy_from_slice(value); + return std::result::Result::Ok(output); +} + +fn validate_slots(slots: &[u64]) -> kb_core::Result<()> { + if slots.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_vote_slots_empty", + "Vote submission requires at least one slot", + )); + } + if slots.len() > solana_vote_interface::state::MAX_LOCKOUT_HISTORY { + return std::result::Result::Err(kb_core::Error::new( + "execution_vote_slots_too_many", + format!( + "Vote slot count {} exceeds official maximum {}", + slots.len(), + solana_vote_interface::state::MAX_LOCKOUT_HISTORY + ), + )); + } + for pair in slots.windows(2) { + if pair[1] <= pair[0] { + return std::result::Result::Err(kb_core::Error::new( + "execution_vote_slots_not_strictly_increasing", + "Vote slots must be strictly increasing", + )); + } + } + return std::result::Result::Ok(()); +} + +fn validate_seed(seed: &str, field_name: &str) -> kb_core::Result<()> { + if seed.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_vote_seed_empty", + format!("Vote {field_name} must not be empty"), + )); + } + if seed.len() > MAX_SEED_LENGTH { + return std::result::Result::Err(kb_core::Error::new( + "execution_vote_seed_too_long", + format!("Vote {field_name} length {} exceeds {}", seed.len(), MAX_SEED_LENGTH), + )); + } + return std::result::Result::Ok(()); +} + +fn validate_seeded_address( + address: &solana_pubkey::Pubkey, + base: &solana_pubkey::Pubkey, + seed: &str, + owner: &solana_pubkey::Pubkey, + field_name: &str, +) -> kb_core::Result<()> { + match validate_seed(seed, field_name) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + return crate::validate_seeded_address( + address, + base, + seed, + owner, + field_name, + "execution_vote_seed_invalid", + "execution_vote_seeded_address_mismatch", + ); +} + +fn parse_hash(value: &str, field_name: &str) -> kb_core::Result { + return match solana_hash::Hash::from_str(value) { + std::result::Result::Ok(hash) => std::result::Result::Ok(hash), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "execution_vote_hash_invalid", + format!("invalid Vote {field_name} hash {value}: {error}"), + )), + }; +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; // rust-rules: trait-import + + fn model(value: solana_pubkey::Pubkey) -> kb_model::Pubkey { + return kb_model::Pubkey(value.to_string()); + } + + fn base_intent( + operation: crate::SolanaCoreOperation, + signers: &[kb_model::Pubkey], + spend: u64, + ) -> crate::SolanaCoreExecutionIntent { + let payer = model(solana_pubkey::Pubkey::new_unique()); + let mut authorized = vec![payer.clone()]; + authorized.extend_from_slice(signers); + return crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("vote-test"), + fee_payer: payer, + policy: kb_execution_api::ExecutionPolicy { + authorized_signers: authorized, + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(spend), + max_fee_lamports: std::option::Option::Some(100_000), + max_compute_unit_price_micro_lamports: std::option::Option::Some(10), + }, + ..kb_execution_api::ExecutionPolicy::default() + }, + operation, + }; + } + + fn serialize(value: &solana_vote_interface::instruction::VoteInstruction) -> std::vec::Vec { + return wincode::serialize(value) + .unwrap_or_else(|error| panic!("unexpected Vote serialization error: {error}")); + } + + fn assert_discriminator(data: &[u8], expected: u32) { + let expected_bytes = expected.to_le_bytes(); + assert_eq!(data.get(..4), std::option::Option::Some(expected_bytes.as_slice())); + } + + #[test] + fn initialize_variants_match_official_wire_accounts_and_signers() { + let vote = model(solana_pubkey::Pubkey::new_unique()); + let node = model(solana_pubkey::Pubkey::new_unique()); + let voter = model(solana_pubkey::Pubkey::new_unique()); + let withdrawer = model(solana_pubkey::Pubkey::new_unique()); + let legacy = super::initialize_instruction(&vote, &node, &voter, &withdrawer, 7) + .unwrap_or_else(|error| panic!("unexpected Vote initialization error: {error}")); + let expected_legacy = + solana_vote_interface::instruction::VoteInstruction::InitializeAccount( + solana_vote_interface::state::VoteInit { + node_pubkey: solana_pubkey::Pubkey::from_str(node.0.as_str()) + .unwrap_or_else(|error| panic!("invalid test node: {error}")), + authorized_voter: solana_pubkey::Pubkey::from_str(voter.0.as_str()) + .unwrap_or_else(|error| panic!("invalid test voter: {error}")), + authorized_withdrawer: solana_pubkey::Pubkey::from_str(withdrawer.0.as_str()) + .unwrap_or_else(|error| panic!("invalid test withdrawer: {error}")), + commission: 7, + }, + ); + assert_eq!(legacy.data, serialize(&expected_legacy)); + assert_eq!(legacy.accounts.len(), 4); + assert_eq!(legacy.accounts[1].pubkey, solana_sdk_ids::sysvar::rent::id()); + assert_eq!(legacy.accounts[2].pubkey, solana_sdk_ids::sysvar::clock::id()); + assert!(legacy.accounts[3].is_signer); + + let inflation_collector = model(solana_pubkey::Pubkey::new_unique()); + let block_collector = model(solana_pubkey::Pubkey::new_unique()); + let v2 = super::initialize_v2_instruction( + &vote, + &node, + &voter, + &[1_u8; solana_vote_interface::state::BLS_PUBLIC_KEY_COMPRESSED_SIZE], + &[2_u8; solana_vote_interface::state::BLS_PROOF_OF_POSSESSION_COMPRESSED_SIZE], + &withdrawer, + 125, + 250, + &inflation_collector, + &block_collector, + ) + .unwrap_or_else(|error| panic!("unexpected Vote v2 initialization error: {error}")); + assert_eq!(v2.accounts.len(), 4); + assert!(v2.accounts[1].is_signer); + assert!(v2.accounts[2].is_writable); + assert!(v2.accounts[3].is_writable); + let expected_v2 = solana_vote_interface::instruction::VoteInstruction::InitializeAccountV2( + solana_vote_interface::state::VoteInitV2 { + node_pubkey: solana_pubkey::Pubkey::from_str(node.0.as_str()) + .unwrap_or_else(|error| panic!("invalid test node: {error}")), + authorized_voter: solana_pubkey::Pubkey::from_str(voter.0.as_str()) + .unwrap_or_else(|error| panic!("invalid test voter: {error}")), + authorized_voter_bls_pubkey: [1_u8; + solana_vote_interface::state::BLS_PUBLIC_KEY_COMPRESSED_SIZE], + authorized_voter_bls_proof_of_possession: [2_u8; + solana_vote_interface::state::BLS_PROOF_OF_POSSESSION_COMPRESSED_SIZE], + authorized_withdrawer: solana_pubkey::Pubkey::from_str(withdrawer.0.as_str()) + .unwrap_or_else(|error| panic!("invalid test withdrawer: {error}")), + inflation_rewards_commission_bps: 125, + block_revenue_commission_bps: 250, + }, + ); + assert_eq!(v2.data, serialize(&expected_v2)); + } + + #[test] + fn create_account_variants_preserve_official_order_space_and_spend() { + let from = model(solana_pubkey::Pubkey::new_unique()); + let vote = model(solana_pubkey::Pubkey::new_unique()); + let node = model(solana_pubkey::Pubkey::new_unique()); + let voter = model(solana_pubkey::Pubkey::new_unique()); + let withdrawer = model(solana_pubkey::Pubkey::new_unique()); + let intent = base_intent( + crate::SolanaCoreOperation::VoteCreateAccount { + from: from.clone(), + vote_account: vote.clone(), + node_pubkey: node.clone(), + authorized_voter: voter, + authorized_withdrawer: withdrawer, + commission_percent: 4, + lamports: 500, + }, + &[from, vote, node], + 500, + ); + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected Vote creation error: {error}")); + assert_eq!(plan.instructions.len(), 2); + assert_eq!(plan.instructions[0].program_id.0, kb_program_ids::SYSTEM_PROGRAM_ID); + assert_eq!(plan.instructions[1].program_id.0, kb_program_ids::VOTE_PROGRAM_ID); + assert_eq!(plan.requested_spend_lamports, 500); + assert_eq!(plan.instructions[1].data.first().copied(), std::option::Option::Some(0)); + } + + #[test] + fn seeded_creation_validates_derivation_and_avoids_new_account_signature() { + let from = solana_pubkey::Pubkey::new_unique(); + let base = solana_pubkey::Pubkey::new_unique(); + let vote_program = solana_pubkey::Pubkey::from_str(kb_program_ids::VOTE_PROGRAM_ID) + .unwrap_or_else(|error| panic!("invalid Vote program id: {error}")); + let seed = "vote-seed"; + let vote = solana_pubkey::Pubkey::create_with_seed(&base, seed, &vote_program) + .unwrap_or_else(|error| panic!("unexpected Vote seed error: {error}")); + let node = model(solana_pubkey::Pubkey::new_unique()); + let from_model = model(from); + let base_model = model(base); + let vote_model = model(vote); + let intent = base_intent( + crate::SolanaCoreOperation::VoteCreateAccountWithSeed { + from: from_model.clone(), + vote_account: vote_model.clone(), + base: base_model.clone(), + seed: std::string::String::from(seed), + node_pubkey: node.clone(), + authorized_voter: model(solana_pubkey::Pubkey::new_unique()), + authorized_withdrawer: model(solana_pubkey::Pubkey::new_unique()), + commission_percent: 5, + lamports: 501, + }, + &[from_model, base_model, node], + 501, + ); + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected seeded Vote creation error: {error}")); + assert_eq!(plan.instructions.len(), 2); + assert!(!plan.required_signers.iter().any(|entry| return entry.pubkey == vote_model)); + let mismatch = super::validate_seeded_address( + &solana_pubkey::Pubkey::new_unique(), + &base, + seed, + &vote_program, + "vote_account", + ); + assert!(mismatch.is_err()); + } + + #[test] + fn authority_variants_preserve_official_accounts_and_bls_contract() { + let vote = model(solana_pubkey::Pubkey::new_unique()); + let current = model(solana_pubkey::Pubkey::new_unique()); + let new_authority = model(solana_pubkey::Pubkey::new_unique()); + let intent = base_intent( + crate::SolanaCoreOperation::VoteAuthorizeChecked { + vote_account: vote, + current_authority: current.clone(), + new_authority: new_authority.clone(), + authorization: crate::VoteAuthorizationKind::VoterWithBls { + bls_pubkey: vec![3_u8; 48], + bls_proof_of_possession: vec![4_u8; 96], + }, + }, + &[current, new_authority], + 0, + ); + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected Vote authorize error: {error}")); + assert_eq!(plan.instructions[0].accounts.len(), 4); + assert_eq!( + plan.instructions[0].accounts[1].pubkey.0, + solana_sdk_ids::sysvar::clock::id().to_string() + ); + assert!(plan.instructions[0].accounts[2].is_signer); + assert!(plan.instructions[0].accounts[3].is_signer); + + let malformed = super::vote_authorization(&crate::VoteAuthorizationKind::VoterWithBls { + bls_pubkey: vec![0_u8; 47], + bls_proof_of_possession: vec![0_u8; 96], + }); + assert!(malformed.is_err()); + } + + #[test] + fn vote_and_switch_match_official_sysvar_contracts() { + let vote = model(solana_pubkey::Pubkey::new_unique()); + let voter = model(solana_pubkey::Pubkey::new_unique()); + let bank_hash = solana_hash::Hash::new_unique().to_string(); + let switch_hash = solana_hash::Hash::new_unique().to_string(); + let intent = base_intent( + crate::SolanaCoreOperation::VoteSwitch { + vote_account: vote, + authorized_voter: voter.clone(), + slots: vec![10, 20, 30], + bank_hash, + timestamp: std::option::Option::Some(123), + switch_proof_hash: switch_hash, + }, + &[voter], + 0, + ); + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected Vote switch error: {error}")); + assert_eq!(plan.instructions[0].accounts.len(), 4); + assert_eq!( + plan.instructions[0].accounts[1].pubkey.0, + solana_sdk_ids::sysvar::slot_hashes::id().to_string() + ); + assert_eq!( + plan.instructions[0].accounts[2].pubkey.0, + solana_sdk_ids::sysvar::clock::id().to_string() + ); + assert!(plan.instructions[0].accounts[3].is_signer); + } + + #[test] + fn vote_state_and_tower_sync_validate_compact_contracts() { + let lockouts = vec![ + crate::VoteLockout { slot: 100, confirmation_count: 2 }, + crate::VoteLockout { slot: 120, confirmation_count: 1 }, + ]; + let bank_hash = solana_hash::Hash::new_unique().to_string(); + let state = super::vote_state_update( + lockouts.as_slice(), + std::option::Option::Some(90), + bank_hash.as_str(), + std::option::Option::Some(456), + ) + .unwrap_or_else(|error| panic!("unexpected Vote-state error: {error}")); + let block_id = solana_hash::Hash::new_unique().to_string(); + let tower = super::tower_sync( + lockouts.as_slice(), + std::option::Option::Some(90), + bank_hash.as_str(), + std::option::Option::Some(456), + block_id.as_str(), + ) + .unwrap_or_else(|error| panic!("unexpected tower-sync error: {error}")); + assert_eq!(state.lockouts.len(), 2); + assert_eq!(tower.lockouts.len(), 2); + + let duplicate = [ + crate::VoteLockout { slot: 100, confirmation_count: 1 }, + crate::VoteLockout { slot: 100, confirmation_count: 1 }, + ]; + assert!(super::lockout_deque(duplicate.as_slice(), std::option::Option::Some(90)).is_err()); + assert!(super::lockout_deque(lockouts.as_slice(), std::option::Option::Some(101)).is_err()); + } + + #[test] + fn every_vote_state_wire_variant_preserves_discriminator_accounts_and_signer() { + let vote = model(solana_pubkey::Pubkey::new_unique()); + let voter = model(solana_pubkey::Pubkey::new_unique()); + let bank_hash = solana_hash::Hash::new_unique().to_string(); + let switch_hash = solana_hash::Hash::new_unique().to_string(); + let block_id = solana_hash::Hash::new_unique().to_string(); + let lockouts = vec![ + crate::VoteLockout { slot: 200, confirmation_count: 2 }, + crate::VoteLockout { slot: 220, confirmation_count: 1 }, + ]; + let operations = vec![ + ( + crate::SolanaCoreOperation::VoteUpdateVoteState { + vote_account: vote.clone(), + authorized_voter: voter.clone(), + lockouts: lockouts.clone(), + root: std::option::Option::Some(190), + bank_hash: bank_hash.clone(), + timestamp: std::option::Option::Some(1), + }, + crate::VOTE_UPDATE_VOTE_STATE_OPERATION, + 8_u32, + ), + ( + crate::SolanaCoreOperation::VoteUpdateVoteStateSwitch { + vote_account: vote.clone(), + authorized_voter: voter.clone(), + lockouts: lockouts.clone(), + root: std::option::Option::Some(190), + bank_hash: bank_hash.clone(), + timestamp: std::option::Option::Some(2), + switch_proof_hash: switch_hash.clone(), + }, + crate::VOTE_UPDATE_VOTE_STATE_SWITCH_OPERATION, + 9_u32, + ), + ( + crate::SolanaCoreOperation::VoteCompactUpdateVoteState { + vote_account: vote.clone(), + authorized_voter: voter.clone(), + lockouts: lockouts.clone(), + root: std::option::Option::Some(190), + bank_hash: bank_hash.clone(), + timestamp: std::option::Option::Some(3), + }, + crate::VOTE_COMPACT_UPDATE_VOTE_STATE_OPERATION, + 12_u32, + ), + ( + crate::SolanaCoreOperation::VoteCompactUpdateVoteStateSwitch { + vote_account: vote.clone(), + authorized_voter: voter.clone(), + lockouts: lockouts.clone(), + root: std::option::Option::Some(190), + bank_hash: bank_hash.clone(), + timestamp: std::option::Option::Some(4), + switch_proof_hash: switch_hash.clone(), + }, + crate::VOTE_COMPACT_UPDATE_VOTE_STATE_SWITCH_OPERATION, + 13_u32, + ), + ( + crate::SolanaCoreOperation::VoteTowerSync { + vote_account: vote.clone(), + authorized_voter: voter.clone(), + lockouts: lockouts.clone(), + root: std::option::Option::Some(190), + bank_hash: bank_hash.clone(), + timestamp: std::option::Option::Some(5), + block_id: block_id.clone(), + }, + crate::VOTE_TOWER_SYNC_OPERATION, + 14_u32, + ), + ( + crate::SolanaCoreOperation::VoteTowerSyncSwitch { + vote_account: vote.clone(), + authorized_voter: voter.clone(), + lockouts, + root: std::option::Option::Some(190), + bank_hash, + timestamp: std::option::Option::Some(6), + block_id, + switch_proof_hash: switch_hash, + }, + crate::VOTE_TOWER_SYNC_SWITCH_OPERATION, + 15_u32, + ), + ]; + for (operation, operation_code, discriminator) in operations { + let intent = base_intent(operation, std::slice::from_ref(&voter), 0); + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected Vote-state plan error: {error}")); + assert_eq!(plan.operation_code, operation_code); + assert_eq!(plan.instructions.len(), 1); + assert_eq!(plan.instructions[0].accounts.len(), 2); + assert!(plan.instructions[0].accounts[1].is_signer); + assert_discriminator(plan.instructions[0].data.as_slice(), discriminator); + } + } + + #[test] + fn validator_identity_and_commission_variants_preserve_official_contracts() { + let vote = model(solana_pubkey::Pubkey::new_unique()); + let withdrawer = model(solana_pubkey::Pubkey::new_unique()); + let identity = model(solana_pubkey::Pubkey::new_unique()); + let identity_intent = base_intent( + crate::SolanaCoreOperation::VoteUpdateValidatorIdentity { + vote_account: vote.clone(), + withdraw_authority: withdrawer.clone(), + new_validator_identity: identity.clone(), + }, + &[withdrawer.clone(), identity], + 0, + ); + let identity_plan = crate::builder_build_prepared_plan(&identity_intent) + .unwrap_or_else(|error| panic!("unexpected Vote identity error: {error}")); + assert_eq!(identity_plan.instructions[0].accounts.len(), 3); + assert!(identity_plan.instructions[0].accounts[1].is_signer); + assert!(identity_plan.instructions[0].accounts[2].is_signer); + assert_discriminator(identity_plan.instructions[0].data.as_slice(), 4); + + let legacy_intent = base_intent( + crate::SolanaCoreOperation::VoteUpdateCommission { + vote_account: vote.clone(), + withdraw_authority: withdrawer.clone(), + commission_percent: 25, + }, + std::slice::from_ref(&withdrawer), + 0, + ); + let legacy_plan = crate::builder_build_prepared_plan(&legacy_intent) + .unwrap_or_else(|error| panic!("unexpected Vote commission error: {error}")); + assert_eq!(legacy_plan.instructions[0].accounts.len(), 2); + assert_discriminator(legacy_plan.instructions[0].data.as_slice(), 5); + + let bps_intent = base_intent( + crate::SolanaCoreOperation::VoteUpdateCommissionBps { + vote_account: vote, + withdraw_authority: withdrawer.clone(), + commission_kind: crate::VoteCommissionKind::InflationRewards, + commission_bps: 1_250, + }, + std::slice::from_ref(&withdrawer), + 0, + ); + let bps_plan = crate::builder_build_prepared_plan(&bps_intent) + .unwrap_or_else(|error| panic!("unexpected Vote BPS commission error: {error}")); + assert_eq!(bps_plan.instructions[0].accounts.len(), 2); + assert_discriminator(bps_plan.instructions[0].data.as_slice(), 18); + } + + #[test] + fn commission_collector_withdraw_and_deposit_preserve_spend_semantics() { + let vote = model(solana_pubkey::Pubkey::new_unique()); + let authority = model(solana_pubkey::Pubkey::new_unique()); + let collector = model(solana_pubkey::Pubkey::new_unique()); + let collector_intent = base_intent( + crate::SolanaCoreOperation::VoteUpdateCommissionCollector { + vote_account: vote.clone(), + withdraw_authority: authority.clone(), + new_collector: collector, + commission_kind: crate::VoteCommissionKind::BlockRevenue, + }, + std::slice::from_ref(&authority), + 0, + ); + let collector_plan = crate::builder_build_prepared_plan(&collector_intent) + .unwrap_or_else(|error| panic!("unexpected collector update error: {error}")); + assert_eq!(collector_plan.instructions[0].accounts.len(), 3); + assert_eq!(collector_plan.requested_spend_lamports, 0); + + let recipient = model(solana_pubkey::Pubkey::new_unique()); + let withdraw = base_intent( + crate::SolanaCoreOperation::VoteWithdraw { + vote_account: vote.clone(), + withdraw_authority: authority.clone(), + to: recipient, + lamports: 50, + }, + std::slice::from_ref(&authority), + 50, + ); + let withdraw_plan = crate::builder_build_prepared_plan(&withdraw) + .unwrap_or_else(|error| panic!("unexpected Vote withdrawal error: {error}")); + assert_eq!(withdraw_plan.requested_spend_lamports, 50); + + let source = model(solana_pubkey::Pubkey::new_unique()); + let deposit = base_intent( + crate::SolanaCoreOperation::VoteDepositDelegatorRewards { + vote_account: vote, + source: source.clone(), + deposit_lamports: 51, + }, + std::slice::from_ref(&source), + 51, + ); + let deposit_plan = crate::builder_build_prepared_plan(&deposit) + .unwrap_or_else(|error| panic!("unexpected Vote deposit error: {error}")); + assert_eq!(deposit_plan.requested_spend_lamports, 51); + assert!(deposit_plan.instructions[0].accounts[1].is_signer); + } + + #[test] + fn malformed_amounts_commissions_hashes_slots_lockouts_and_seeds_are_rejected() { + assert!(super::validate_commission_percent(101).is_err()); + assert!(super::validate_commission_bps(10_001, "commission_bps").is_err()); + assert!(super::validate_slots(&[]).is_err()); + assert!(super::validate_slots(&[2, 1]).is_err()); + assert!(super::parse_hash("not-a-hash", "bank_hash").is_err()); + assert!(super::validate_seed("", "seed").is_err()); + assert!(super::validate_seed(&"x".repeat(33), "seed").is_err()); + assert!(crate::validate_positive_u64(0, "zero", "zero").is_err()); + let oversized = vec![ + crate::VoteLockout { slot: 1, confirmation_count: 1 }; + solana_vote_interface::state::MAX_LOCKOUT_HISTORY + 1 + ]; + assert!(super::lockout_deque(oversized.as_slice(), std::option::Option::None).is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/zk_elgamal.rs b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/zk_elgamal.rs new file mode 100644 index 0000000..95c50c8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_solana_core/src/zk_elgamal.rs @@ -0,0 +1,441 @@ +// file: kb_executor_solana_core/src/zk_elgamal.rs +// version: 5 + +//! Native ZK ElGamal Proof instruction builders. + +pub(crate) fn build_prepared_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, +) -> kb_core::Result { + return match &intent.operation { + crate::SolanaCoreOperation::ZkElGamalVerifyInline { + proof_type, + proof_data, + context_state, + } => build_inline_plan( + intent, + fee_payer, + *proof_type, + proof_data.as_slice(), + context_state.as_ref(), + ), + crate::SolanaCoreOperation::ZkElGamalVerifyFromAccount { + proof_type, + proof_account, + proof_offset, + context_state, + } => build_account_plan( + intent, + fee_payer, + *proof_type, + proof_account, + *proof_offset, + context_state.as_ref(), + ), + crate::SolanaCoreOperation::ZkElGamalCloseContextState { + context_state, + destination, + authority, + } => build_close_plan(intent, fee_payer, context_state, destination, authority), + _ => std::result::Result::Err(kb_core::Error::new( + "execution_zk_elgamal_operation_mismatch", + "ZK ElGamal builder received an unrelated Solana core operation", + )), + }; +} + +fn build_inline_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + proof_type: crate::ZkElGamalProofType, + proof_data: &[u8], + context_state: std::option::Option<&crate::ZkElGamalContextState>, +) -> kb_core::Result { + let expected = proof_type.proof_data_size(); + if proof_data.len() != expected { + return std::result::Result::Err(kb_core::Error::new( + "execution_zk_elgamal_proof_size_invalid", + format!( + "inline ZK ElGamal proof requires exactly {expected} bytes but received {}", + proof_data.len() + ), + )); + } + let accounts = match context_accounts(context_state) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut data = std::vec::Vec::with_capacity(1 + proof_data.len()); + data.push(proof_type.discriminator()); + data.extend_from_slice(proof_data); + let instruction = match instruction(accounts, data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::ZK_ELGAMAL_VERIFY_INLINE_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + std::vec::Vec::new(), + ); +} + +fn build_account_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + proof_type: crate::ZkElGamalProofType, + proof_account: &kb_model::Pubkey, + proof_offset: u32, + context_state: std::option::Option<&crate::ZkElGamalContextState>, +) -> kb_core::Result { + let proof_address = match crate::parse_pubkey(proof_account, "proof_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut accounts = vec![solana_instruction::AccountMeta::new_readonly(proof_address, false)]; + let context_accounts = match context_accounts(context_state) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if let std::option::Option::Some(context) = context_state { + if context.account == *proof_account { + return std::result::Result::Err(kb_core::Error::new( + "execution_zk_elgamal_proof_is_context", + "proof and context-state accounts must differ", + )); + } + } + accounts.extend(context_accounts); + let mut data = vec![proof_type.discriminator()]; + data.extend_from_slice(&proof_offset.to_le_bytes()); + let instruction = match instruction(accounts, data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::ZK_ELGAMAL_VERIFY_FROM_ACCOUNT_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + std::vec::Vec::new(), + ); +} + +fn build_close_plan( + intent: &crate::SolanaCoreExecutionIntent, + fee_payer: &solana_pubkey::Pubkey, + context_state: &kb_model::Pubkey, + destination: &kb_model::Pubkey, + authority: &kb_model::Pubkey, +) -> kb_core::Result { + if context_state == destination { + return std::result::Result::Err(kb_core::Error::new( + "execution_zk_elgamal_close_same_account", + "ZK ElGamal context-state and destination accounts must differ", + )); + } + let context_address = match crate::parse_pubkey(context_state, "context_state") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let destination_address = match crate::parse_pubkey(destination, "destination") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority_address = match crate::parse_pubkey(authority, "authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction = match instruction( + vec![ + solana_instruction::AccountMeta::new(context_address, false), + solana_instruction::AccountMeta::new(destination_address, false), + solana_instruction::AccountMeta::new_readonly(authority_address, true), + ], + vec![0], + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::finalize_prepared_plan( + intent, + fee_payer, + crate::ZK_ELGAMAL_CLOSE_CONTEXT_STATE_OPERATION, + vec![instruction], + 0, + std::option::Option::None, + vec![(authority.clone(), std::string::String::from("zk_elgamal_context_authority"))], + ); +} + +fn context_accounts( + context_state: std::option::Option<&crate::ZkElGamalContextState>, +) -> kb_core::Result> { + return match context_state { + std::option::Option::Some(context) => { + if context.account == context.authority { + return std::result::Result::Err(kb_core::Error::new( + "execution_zk_elgamal_context_authority_is_account", + "ZK ElGamal context-state account and authority must differ", + )); + } + let account = match crate::parse_pubkey(&context.account, "context_state") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority = match crate::parse_pubkey(&context.authority, "context_authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + std::result::Result::Ok(vec![ + solana_instruction::AccountMeta::new(account, false), + solana_instruction::AccountMeta::new_readonly(authority, false), + ]) + }, + std::option::Option::None => std::result::Result::Ok(std::vec::Vec::new()), + }; +} + +fn instruction( + accounts: std::vec::Vec, + data: std::vec::Vec, +) -> kb_core::Result { + let program_id = + match crate::parse_native_program_id(kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(solana_instruction::Instruction { program_id, accounts, data }); +} + +#[cfg(test)] +mod tests { + fn model(value: solana_pubkey::Pubkey) -> kb_model::Pubkey { + return kb_model::Pubkey(value.to_string()); + } + + fn policy(signers: &[kb_model::Pubkey]) -> kb_execution_api::ExecutionPolicy { + return kb_execution_api::ExecutionPolicy { + authorized_signers: signers.to_vec(), + ..kb_execution_api::ExecutionPolicy::default() + }; + } + + #[test] + fn inline_and_account_modes_match_official_layouts() { + let payer = model(solana_pubkey::Pubkey::new_unique()); + let proof_type = crate::ZkElGamalProofType::PubkeyValidity; + let inline = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("zk-inline"), + fee_payer: payer.clone(), + policy: policy(std::slice::from_ref(&payer)), + operation: crate::SolanaCoreOperation::ZkElGamalVerifyInline { + proof_type, + proof_data: vec![0; proof_type.proof_data_size()], + context_state: std::option::Option::None, + }, + }; + let inline_plan = crate::builder_build_prepared_plan(&inline) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(inline_plan.instructions[0].data[0], 4); + assert_eq!(inline_plan.instructions[0].data.len(), 1 + proof_type.proof_data_size()); + assert!(inline_plan.instructions[0].accounts.is_empty()); + + let proof_account = solana_pubkey::Pubkey::new_unique(); + let context = solana_pubkey::Pubkey::new_unique(); + let authority = solana_pubkey::Pubkey::new_unique(); + let account = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("zk-account"), + fee_payer: payer.clone(), + policy: policy(&[payer]), + operation: crate::SolanaCoreOperation::ZkElGamalVerifyFromAccount { + proof_type: crate::ZkElGamalProofType::ZeroCiphertext, + proof_account: model(proof_account), + proof_offset: 44, + context_state: std::option::Option::Some(crate::ZkElGamalContextState { + account: model(context), + authority: model(authority), + }), + }, + }; + let account_plan = crate::builder_build_prepared_plan(&account) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(account_plan.instructions[0].data, vec![1, 44, 0, 0, 0]); + assert_eq!(account_plan.instructions[0].accounts.len(), 3); + assert!(!account_plan.instructions[0].accounts[0].is_writable); + assert!(account_plan.instructions[0].accounts[1].is_writable); + } + + #[test] + fn every_proof_type_has_the_official_discriminator_and_nonzero_size() { + let variants = [ + crate::ZkElGamalProofType::ZeroCiphertext, + crate::ZkElGamalProofType::CiphertextCiphertextEquality, + crate::ZkElGamalProofType::CiphertextCommitmentEquality, + crate::ZkElGamalProofType::PubkeyValidity, + crate::ZkElGamalProofType::PercentageWithCap, + crate::ZkElGamalProofType::BatchedRangeProofU64, + crate::ZkElGamalProofType::BatchedRangeProofU128, + crate::ZkElGamalProofType::BatchedRangeProofU256, + crate::ZkElGamalProofType::GroupedCiphertext2HandlesValidity, + crate::ZkElGamalProofType::BatchedGroupedCiphertext2HandlesValidity, + crate::ZkElGamalProofType::GroupedCiphertext3HandlesValidity, + crate::ZkElGamalProofType::BatchedGroupedCiphertext3HandlesValidity, + ]; + for (index, proof_type) in variants.iter().enumerate() { + assert_eq!(proof_type.discriminator(), (index + 1) as u8); + assert!(proof_type.proof_data_size() > 0); + } + } + + #[test] + fn every_context_state_size_matches_the_official_generic_state() { + let variants = [ + ( + crate::ZkElGamalProofType::ZeroCiphertext, + std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::ZeroCiphertextProofContext, + >, + >(), + ), + ( + crate::ZkElGamalProofType::CiphertextCiphertextEquality, + std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::CiphertextCiphertextEqualityProofContext, + >, + >(), + ), + ( + crate::ZkElGamalProofType::CiphertextCommitmentEquality, + std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::CiphertextCommitmentEqualityProofContext, + >, + >(), + ), + ( + crate::ZkElGamalProofType::PubkeyValidity, + std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::PubkeyValidityProofContext, + >, + >(), + ), + ( + crate::ZkElGamalProofType::PercentageWithCap, + std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::PercentageWithCapProofContext, + >, + >(), + ), + ( + crate::ZkElGamalProofType::BatchedRangeProofU64, + std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::BatchedRangeProofContext, + >, + >(), + ), + ( + crate::ZkElGamalProofType::BatchedRangeProofU128, + std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::BatchedRangeProofContext, + >, + >(), + ), + ( + crate::ZkElGamalProofType::BatchedRangeProofU256, + std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::BatchedRangeProofContext, + >, + >(), + ), + ( + crate::ZkElGamalProofType::GroupedCiphertext2HandlesValidity, + std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::GroupedCiphertext2HandlesValidityProofContext, + >, + >(), + ), + ( + crate::ZkElGamalProofType::BatchedGroupedCiphertext2HandlesValidity, + std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::BatchedGroupedCiphertext2HandlesValidityProofContext, + >, + >(), + ), + ( + crate::ZkElGamalProofType::GroupedCiphertext3HandlesValidity, + std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::GroupedCiphertext3HandlesValidityProofContext, + >, + >(), + ), + ( + crate::ZkElGamalProofType::BatchedGroupedCiphertext3HandlesValidity, + std::mem::size_of::< + solana_zk_elgamal_proof_interface::state::ProofContextState< + solana_zk_elgamal_proof_interface::proof_data::BatchedGroupedCiphertext3HandlesValidityProofContext, + >, + >(), + ), + ]; + for (proof_type, expected) in variants { + assert_eq!(proof_type.context_state_size(), expected); + assert!(expected > 33); + } + } + + #[test] + fn close_context_state_matches_official_accounts_and_signer() { + let context = solana_pubkey::Pubkey::new_unique(); + let destination = solana_pubkey::Pubkey::new_unique(); + let authority = solana_pubkey::Pubkey::new_unique(); + let payer = model(solana_pubkey::Pubkey::new_unique()); + let intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("zk-close"), + fee_payer: payer.clone(), + policy: policy(&[payer, model(authority)]), + operation: crate::SolanaCoreOperation::ZkElGamalCloseContextState { + context_state: model(context), + destination: model(destination), + authority: model(authority), + }, + }; + let plan = crate::builder_build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(plan.instructions[0].data, vec![0]); + assert_eq!(plan.instructions[0].accounts.len(), 3); + assert!(plan.instructions[0].accounts[2].is_signer); + assert_eq!(plan.required_signers.len(), 2); + } + + #[test] + fn malformed_proof_shapes_are_rejected() { + let payer = model(solana_pubkey::Pubkey::new_unique()); + let intent = crate::SolanaCoreExecutionIntent { + intent_id: std::string::String::from("zk-invalid"), + fee_payer: payer.clone(), + policy: policy(&[payer]), + operation: crate::SolanaCoreOperation::ZkElGamalVerifyInline { + proof_type: crate::ZkElGamalProofType::ZeroCiphertext, + proof_data: vec![0], + context_state: std::option::Option::None, + }, + }; + assert!(crate::builder_build_prepared_plan(&intent).is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/Cargo.toml new file mode 100644 index 0000000..d81f575 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/Cargo.toml @@ -0,0 +1,20 @@ +# file: kb_executor_spl_account_compression/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_spl_account_compression" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/README.md b/migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/README.md new file mode 100644 index 0000000..d5219be --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/README.md @@ -0,0 +1,14 @@ + + + +# kb_executor_spl_account_compression + +Ce crate réserve l’exécuteur pour le programme SPL Account Compression. + +## Rôle + +Il construira des instructions ou plans d’exécution indépendamment du décodeur correspondant. Le scaffold actuel ne signe, ne simule et n’envoie aucune transaction. + +## Statut + +L’exécuteur reconnaît uniquement ses program IDs vérifiés et retourne un plan réservé contenant zéro instruction. Les builders, contrôles de comptes, simulations et garde-fous seront ajoutés dans la version SPL dédiée du `ROADMAP.md`. diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/src/constants.rs new file mode 100644 index 0000000..a489ab9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_executor_spl_account_compression/src/constants.rs +// version: 2 + +//! Local constants for the `kb_executor_spl_account_compression` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_spl_account_compression"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/src/executor.rs new file mode 100644 index 0000000..db1bf95 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/src/executor.rs @@ -0,0 +1,92 @@ +// file: kb_executor_spl_account_compression/src/executor.rs +// version: 2 + +//! Reserved executor for the `spl_account_compression` surface. + +/// Reserved executor implementation for the verified program surface. +#[derive(Clone, Debug, Default)] +pub struct SplAccountCompressionExecutor; + +impl kb_execution_api::InstructionExecutor for crate::SplAccountCompressionExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_spl_account_compression"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::SPL_ACCOUNT_COMPRESSION_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + tracing::debug!( + target: crate::TRACING_TARGET, + action = "build_reserved_execution_plan", + executor_name = "kb_executor_spl_account_compression", + program_id = %request.program_id.0.as_str(), + operation_code = %request.operation_code, + instruction_count = 0_usize, + "build reserved SPL execution plan" + ); + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_spl_account_compression"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor","surface":"spl_account_compression"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} + +#[cfg(test)] +mod tests { + fn request(program_id: &str) -> kb_execution_api::ExecutionRequest { + return kb_execution_api::ExecutionRequest { + program_id: kb_model::ProgramId(program_id.to_string()), + operation_code: "reserved".to_string(), + payload_json: "{}".to_string(), + }; + } + + #[test] + fn exposes_only_registered_program_ids() { + let executor = crate::SplAccountCompressionExecutor; + let program_ids = kb_execution_api::InstructionExecutor::program_ids(&executor); + assert_eq!(program_ids.len(), 1); + for program_id in program_ids { + assert!(kb_program_ids::find_registered_program_id(program_id).is_some()); + } + } + + #[test] + fn reserved_plan_never_contains_instructions() { + let executor = crate::SplAccountCompressionExecutor; + let program_id = kb_execution_api::InstructionExecutor::program_ids(&executor)[0]; + let result = + kb_execution_api::InstructionExecutor::build_plan(&executor, &request(program_id)); + let plan = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("reserved plan failed: {error}"), + }; + assert_eq!(plan.instruction_count, 0); + assert!(plan.payload_json.contains("reserved_executor")); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/src/lib.rs new file mode 100644 index 0000000..ced5372 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_account_compression/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_executor_spl_account_compression/src/lib.rs +// version: 3 + +//! Executor crate for `spl_account_compression`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the reserved executor type implemented by this crate. +pub use crate::executor::SplAccountCompressionExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/Cargo.toml new file mode 100644 index 0000000..2bcf3be --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/Cargo.toml @@ -0,0 +1,25 @@ +# file: kb_executor_spl_associated_token_account/Cargo.toml +# version: 4 + +[package] +name = "kb_executor_spl_associated_token_account" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true +serde.workspace = true +solana-instruction.workspace = true +solana-pubkey.workspace = true +spl-associated-token-account-interface.workspace = true +tracing.workspace = true +ts-rs.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/README.md b/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/README.md new file mode 100644 index 0000000..b8d2f63 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/README.md @@ -0,0 +1,35 @@ + + + +# kb_executor_spl_associated_token_account + +Ce crate construit les plans typés de la surface SPL Associated Token Account courante. + +## Surface + +- `Create` ; +- `CreateIdempotent` ; +- `RecoverNested` ; +- Token Program classique ou Token-2022 comme cible explicite de la dérivation. + +Chaque plan conserve l’ordre et les flags des metas produits par le builder officiel de +`spl-associated-token-account-interface 2.0.0`. Les signataires requis sont dédupliqués séparément, +sans réordonner les comptes de l’instruction. + +## Garde-fous + +La simulation est obligatoire, le mode par défaut reste `dry_run`, les plafonds de rent et de frais +sont explicites et le replay post-exécution complet est requis. Le préflight stateful Localnet/Devnet +est orchestré par `kb_pipeline` : il contrôle cluster, mints, PDA, comptes Token existants, +`RecoverNested`, signataires et solde couvrant le plafond rent-plus-frais. + +Pour Token-2022, le préflight valide uniquement le préfixe commun Mint/Token Account et accepte des +octets d’extension suffixés. Il ne décode ni instruction ni extension Token-2022 ; la simulation +officielle reste l’autorité pour la taille de compte et le rent exacts liés aux extensions. + +## Preuves Devnet + +Les parcours contrôlés du 16 juillet 2026 ont validé `CreateIdempotent` pour un mint classique et +un mint Token-2022, puis la réutilisation des deux ATA avec second replay sans doublon. Un scénario +`RecoverNested` classique a transféré 1 000 000 000 unités brutes vers l'ATA wallet, fermé le nested +ATA et confirmé les postconditions ainsi que deux matérialisations distinctes lifecycle/risk. diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/builder.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/builder.rs new file mode 100644 index 0000000..a3affc2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/builder.rs @@ -0,0 +1,490 @@ +// file: kb_executor_spl_associated_token_account/src/builder.rs +// version: 4 + +//! Official ATA builders and conservative simulation-first plan validation. + +macro_rules! parse_key { + ($value:expr, $field:expr) => { + match $value.0.parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_ata_pubkey_invalid", + format!("invalid ATA {} public key: {error}", $field), + )); + }, + } + }; +} + +pub(crate) fn build_prepared_plan( + intent: &crate::SplAssociatedTokenAccountExecutionIntent, +) -> kb_core::Result { + if intent.intent_id.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_ata_intent_id_empty", + "ATA execution intent id must not be empty", + )); + } + match crate::builder::validate_policy(intent) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let fee_payer = parse_key!(&intent.fee_payer, "fee_payer"); + let token_program_text = intent.operation.token_program().program_id(); + let instruction = match &intent.operation { + crate::SplAssociatedTokenAccountOperation::Create { + wallet_owner, + mint, + token_program: _, + } => { + let wallet_owner = parse_key!(wallet_owner, "wallet_owner"); + let mint = parse_key!(mint, "mint"); + let token_program: solana_pubkey::Pubkey = match token_program_text.parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_ata_token_program_invalid", + error.to_string(), + )); + }, + }; + spl_associated_token_account_interface::instruction::create_associated_token_account( + &fee_payer, + &wallet_owner, + &mint, + &token_program, + ) + }, + crate::SplAssociatedTokenAccountOperation::CreateIdempotent { + wallet_owner, + mint, + token_program: _, + } => { + let wallet_owner = parse_key!(wallet_owner, "wallet_owner"); + let mint = parse_key!(mint, "mint"); + let token_program: solana_pubkey::Pubkey = match token_program_text.parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_ata_token_program_invalid", + error.to_string(), + )); + }, + }; + spl_associated_token_account_interface::instruction::create_associated_token_account_idempotent( + &fee_payer, + &wallet_owner, + &mint, + &token_program, + ) + }, + crate::SplAssociatedTokenAccountOperation::RecoverNested { + wallet_owner, + owner_mint, + nested_mint, + token_program: _, + } => { + let wallet_owner = parse_key!(wallet_owner, "wallet_owner"); + let owner_mint = parse_key!(owner_mint, "owner_mint"); + let nested_mint = parse_key!(nested_mint, "nested_mint"); + let token_program: solana_pubkey::Pubkey = match token_program_text.parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_ata_token_program_invalid", + error.to_string(), + )); + }, + }; + spl_associated_token_account_interface::instruction::recover_nested( + &wallet_owner, + &owner_mint, + &nested_mint, + &token_program, + ) + }, + }; + let planned_instruction = + crate::builder::planned_instruction(intent.operation.operation_code(), &instruction); + let required_signers = + crate::builder::required_signers(&intent.fee_payer, &planned_instruction.accounts); + tracing::debug!( + target: crate::TRACING_TARGET, + action = "build_prepared_plan", + intent_id = %intent.intent_id, + operation_code = intent.operation.operation_code(), + token_program_id = token_program_text, + account_occurrence_count = planned_instruction.accounts.len(), + required_signer_count = required_signers.len(), + max_rent_lamports = intent.max_rent_lamports, + dry_run = intent.policy.dry_run, + "built SPL Associated Token Account execution plan" + ); + return std::result::Result::Ok(kb_execution_api::PreparedExecutionPlan { + executor_name: "kb_executor_spl_associated_token_account".to_string(), + executor_version: env!("CARGO_PKG_VERSION").to_string(), + intent_id: intent.intent_id.clone(), + operation_code: intent.operation.operation_code().to_string(), + fee_payer: kb_model::Pubkey(fee_payer.to_string()), + instructions: std::vec![planned_instruction], + required_signers, + policy: intent.policy.clone(), + requested_spend_lamports: intent.max_rent_lamports, + requested_compute_unit_price_micro_lamports: std::option::Option::None, + }); +} + +fn validate_policy( + intent: &crate::SplAssociatedTokenAccountExecutionIntent, +) -> kb_core::Result<()> { + if intent.policy.simulation != kb_execution_api::ExecutionSimulationPolicy::Required { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_ata_simulation_required", + "ATA execution requires simulation before signing or sending", + )); + } + if intent.operation.is_creation() && intent.max_rent_lamports == 0 { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_ata_rent_limit_missing", + "ATA creation requires a positive rent spending ceiling", + )); + } + if !intent.operation.is_creation() && intent.max_rent_lamports != 0 { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_ata_recovery_rent_must_be_zero", + "RecoverNested must declare zero rent spending", + )); + } + if intent.policy.cost_limit.max_spend_lamports + != std::option::Option::Some(intent.max_rent_lamports) + { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_ata_spend_limit_mismatch", + "ATA policy max spend must equal the declared maximum rent lamports", + )); + } + match intent.policy.cost_limit.max_fee_lamports { + std::option::Option::Some(value) if value > 0 => {}, + std::option::Option::Some(_) | std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_ata_fee_limit_missing", + "ATA execution requires a positive transaction fee ceiling", + )); + }, + } + let validation = &intent.policy.post_execution_validation; + if !validation.canonical_insert_required + || !validation.core_extraction_required + || !validation.decode_replay_required + || !validation.materialization_required + { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_ata_post_validation_required", + "ATA execution requires canonical insertion, core extraction, decode replay and materialization validation", + )); + } + return std::result::Result::Ok(()); +} + +fn planned_instruction( + operation_code: &str, + instruction: &solana_instruction::Instruction, +) -> kb_execution_api::PlannedInstruction { + return kb_execution_api::PlannedInstruction { + program_id: kb_model::ProgramId(instruction.program_id.to_string()), + operation_code: operation_code.to_string(), + accounts: instruction + .accounts + .iter() + .map(|account| { + return kb_execution_api::PlannedAccount { + pubkey: kb_model::Pubkey(account.pubkey.to_string()), + is_signer: account.is_signer, + is_writable: account.is_writable, + }; + }) + .collect(), + data: instruction.data.clone(), + }; +} + +fn required_signers( + fee_payer: &kb_model::Pubkey, + accounts: &[kb_execution_api::PlannedAccount], +) -> std::vec::Vec { + let mut required = std::vec![kb_execution_api::RequiredSigner { + pubkey: fee_payer.clone(), + role: "fee_payer".to_string(), + }]; + for account in accounts.iter().filter(|account| return account.is_signer) { + if required.iter().any(|candidate| return candidate.pubkey == account.pubkey) { + continue; + } + required.push(kb_execution_api::RequiredSigner { + pubkey: account.pubkey.clone(), + role: "wallet_owner".to_string(), + }); + } + return required; +} + +#[cfg(test)] +pub(crate) mod tests { + fn pubkey(value: &str) -> kb_model::Pubkey { + return kb_model::Pubkey(value.to_string()); + } + + fn intent( + operation: crate::SplAssociatedTokenAccountOperation, + max_rent_lamports: u64, + ) -> crate::SplAssociatedTokenAccountExecutionIntent { + let fee_payer = pubkey(kb_program_ids::SYSTEM_PROGRAM_ID); + let wallet_owner = match &operation { + crate::SplAssociatedTokenAccountOperation::Create { wallet_owner, .. } + | crate::SplAssociatedTokenAccountOperation::CreateIdempotent { + wallet_owner, .. + } + | crate::SplAssociatedTokenAccountOperation::RecoverNested { wallet_owner, .. } => { + wallet_owner.clone() + }, + }; + let mut authorized_signers = std::vec![fee_payer.clone()]; + if !authorized_signers.contains(&wallet_owner) { + authorized_signers.push(wallet_owner); + } + return crate::SplAssociatedTokenAccountExecutionIntent { + intent_id: "ata-intent-1".to_string(), + fee_payer, + max_rent_lamports, + policy: kb_execution_api::ExecutionPolicy { + simulation: kb_execution_api::ExecutionSimulationPolicy::Required, + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(max_rent_lamports), + max_fee_lamports: std::option::Option::Some(10_000), + max_compute_unit_price_micro_lamports: std::option::Option::None, + }, + authorized_signers, + dry_run: true, + post_execution_validation: kb_execution_api::PostExecutionValidationPolicy { + canonical_insert_required: true, + core_extraction_required: true, + decode_replay_required: true, + materialization_required: true, + }, + ..kb_execution_api::ExecutionPolicy::default() + }, + operation, + }; + } + + fn official_instruction( + intent: &crate::SplAssociatedTokenAccountExecutionIntent, + ) -> solana_instruction::Instruction { + let fee_payer = intent + .fee_payer + .0 + .parse() + .unwrap_or_else(|error| panic!("invalid fee payer fixture: {error}")); + let token_program = intent + .operation + .token_program() + .program_id() + .parse() + .unwrap_or_else(|error| panic!("invalid Token Program fixture: {error}")); + return match &intent.operation { + crate::SplAssociatedTokenAccountOperation::Create { + wallet_owner, + mint, + token_program: _, + } => { + let wallet_owner = wallet_owner + .0 + .parse() + .unwrap_or_else(|error| panic!("invalid wallet fixture: {error}")); + let mint = + mint.0.parse().unwrap_or_else(|error| panic!("invalid mint fixture: {error}")); + spl_associated_token_account_interface::instruction::create_associated_token_account( + &fee_payer, + &wallet_owner, + &mint, + &token_program, + ) + }, + crate::SplAssociatedTokenAccountOperation::CreateIdempotent { + wallet_owner, + mint, + token_program: _, + } => { + let wallet_owner = wallet_owner + .0 + .parse() + .unwrap_or_else(|error| panic!("invalid wallet fixture: {error}")); + let mint = + mint.0.parse().unwrap_or_else(|error| panic!("invalid mint fixture: {error}")); + spl_associated_token_account_interface::instruction::create_associated_token_account_idempotent( + &fee_payer, + &wallet_owner, + &mint, + &token_program, + ) + }, + crate::SplAssociatedTokenAccountOperation::RecoverNested { + wallet_owner, + owner_mint, + nested_mint, + token_program: _, + } => { + let wallet_owner = wallet_owner + .0 + .parse() + .unwrap_or_else(|error| panic!("invalid wallet fixture: {error}")); + let owner_mint = owner_mint + .0 + .parse() + .unwrap_or_else(|error| panic!("invalid owner mint fixture: {error}")); + let nested_mint = nested_mint + .0 + .parse() + .unwrap_or_else(|error| panic!("invalid nested mint fixture: {error}")); + spl_associated_token_account_interface::instruction::recover_nested( + &wallet_owner, + &owner_mint, + &nested_mint, + &token_program, + ) + }, + }; + } + + fn assert_matches_official(intent: &crate::SplAssociatedTokenAccountExecutionIntent) { + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplAssociatedTokenAccountExecutor, + intent, + ) + .unwrap_or_else(|error| panic!("ATA plan failed: {error}")); + let official = official_instruction(intent); + assert_eq!(plan.instructions.len(), 1); + let actual = &plan.instructions[0]; + assert_eq!(actual.program_id.0, official.program_id.to_string()); + assert_eq!(actual.data, official.data); + assert_eq!(actual.accounts.len(), official.accounts.len()); + for (actual_account, official_account) in + actual.accounts.iter().zip(official.accounts.iter()) + { + assert_eq!(actual_account.pubkey.0, official_account.pubkey.to_string()); + assert_eq!(actual_account.is_signer, official_account.is_signer); + assert_eq!(actual_account.is_writable, official_account.is_writable); + } + assert_eq!(plan.requested_spend_lamports, intent.max_rent_lamports); + assert_eq!(plan.policy.simulation, kb_execution_api::ExecutionSimulationPolicy::Required); + assert!(plan.policy.dry_run); + } + + #[test] + fn every_variant_and_token_program_matches_the_official_builder() { + for token_program in [ + crate::SplAssociatedTokenProgram::Classic, + crate::SplAssociatedTokenProgram::Token2022, + ] { + for operation in [ + crate::SplAssociatedTokenAccountOperation::Create { + wallet_owner: pubkey(kb_program_ids::STAKE_PROGRAM_ID), + mint: pubkey(kb_program_ids::VOTE_PROGRAM_ID), + token_program, + }, + crate::SplAssociatedTokenAccountOperation::CreateIdempotent { + wallet_owner: pubkey(kb_program_ids::STAKE_PROGRAM_ID), + mint: pubkey(kb_program_ids::VOTE_PROGRAM_ID), + token_program, + }, + ] { + let intent = intent(operation, 2_100_000); + assert_matches_official(&intent); + } + let recovery = intent( + crate::SplAssociatedTokenAccountOperation::RecoverNested { + wallet_owner: pubkey(kb_program_ids::STAKE_PROGRAM_ID), + owner_mint: pubkey(kb_program_ids::VOTE_PROGRAM_ID), + nested_mint: pubkey(kb_program_ids::CONFIG_PROGRAM_ID), + token_program, + }, + 0, + ); + assert_matches_official(&recovery); + } + } + + #[test] + fn signer_set_is_unique_without_reordering_instruction_metas() { + let intent = intent( + crate::SplAssociatedTokenAccountOperation::RecoverNested { + wallet_owner: pubkey(kb_program_ids::STAKE_PROGRAM_ID), + owner_mint: pubkey(kb_program_ids::VOTE_PROGRAM_ID), + nested_mint: pubkey(kb_program_ids::CONFIG_PROGRAM_ID), + token_program: crate::SplAssociatedTokenProgram::Classic, + }, + 0, + ); + let plan = crate::build_prepared_plan(&intent) + .unwrap_or_else(|error| panic!("ATA recovery plan failed: {error}")); + assert_eq!(plan.required_signers.len(), 2); + assert_eq!(plan.required_signers[0].role, "fee_payer"); + assert_eq!(plan.required_signers[1].role, "wallet_owner"); + assert_eq!(plan.instructions[0].accounts[5].pubkey, plan.required_signers[1].pubkey); + assert!(plan.instructions[0].accounts[5].is_signer); + } + + #[test] + fn conservative_policy_rejects_unsafe_cost_and_replay_shapes() { + let operation = crate::SplAssociatedTokenAccountOperation::Create { + wallet_owner: pubkey(kb_program_ids::STAKE_PROGRAM_ID), + mint: pubkey(kb_program_ids::VOTE_PROGRAM_ID), + token_program: crate::SplAssociatedTokenProgram::Classic, + }; + let mut intent = intent(operation, 2_100_000); + intent.policy.simulation = kb_execution_api::ExecutionSimulationPolicy::Optional; + let error = crate::build_prepared_plan(&intent).expect_err("optional simulation must fail"); + assert_eq!(error.code(), "execution_spl_ata_simulation_required"); + + intent.policy.simulation = kb_execution_api::ExecutionSimulationPolicy::Required; + intent.policy.cost_limit.max_spend_lamports = std::option::Option::Some(1); + let error = + crate::build_prepared_plan(&intent).expect_err("mismatched spend ceiling must fail"); + assert_eq!(error.code(), "execution_spl_ata_spend_limit_mismatch"); + + intent.policy.cost_limit.max_spend_lamports = std::option::Option::Some(2_100_000); + intent.policy.post_execution_validation.materialization_required = false; + let error = crate::build_prepared_plan(&intent) + .expect_err("incomplete replay validation must fail"); + assert_eq!(error.code(), "execution_spl_ata_post_validation_required"); + } + + #[test] + fn recovery_requires_zero_rent_and_creation_requires_a_positive_ceiling() { + let create = intent( + crate::SplAssociatedTokenAccountOperation::Create { + wallet_owner: pubkey(kb_program_ids::STAKE_PROGRAM_ID), + mint: pubkey(kb_program_ids::VOTE_PROGRAM_ID), + token_program: crate::SplAssociatedTokenProgram::Classic, + }, + 0, + ); + let error = + crate::build_prepared_plan(&create).expect_err("zero creation ceiling must fail"); + assert_eq!(error.code(), "execution_spl_ata_rent_limit_missing"); + + let recovery = intent( + crate::SplAssociatedTokenAccountOperation::RecoverNested { + wallet_owner: pubkey(kb_program_ids::STAKE_PROGRAM_ID), + owner_mint: pubkey(kb_program_ids::VOTE_PROGRAM_ID), + nested_mint: pubkey(kb_program_ids::CONFIG_PROGRAM_ID), + token_program: crate::SplAssociatedTokenProgram::Token2022, + }, + 1, + ); + let error = + crate::build_prepared_plan(&recovery).expect_err("recovery rent spending must fail"); + assert_eq!(error.code(), "execution_spl_ata_recovery_rent_must_be_zero"); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/constants.rs new file mode 100644 index 0000000..6c1394b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_executor_spl_associated_token_account/src/constants.rs +// version: 3 + +//! Local constants for the `kb_executor_spl_associated_token_account` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_spl_associated_token_account"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/executor.rs new file mode 100644 index 0000000..78c519a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/executor.rs @@ -0,0 +1,284 @@ +// file: kb_executor_spl_associated_token_account/src/executor.rs +// version: 4 + +//! Exact ATA capability dispatch and typed plan construction. + +/// SPL Associated Token Account executor implementation. +#[derive(Clone, Debug, Default)] +pub struct SplAssociatedTokenAccountExecutor; + +impl crate::SplAssociatedTokenAccountExecutor { + fn exact_capability( + &self, + program_id: &kb_model::ProgramId, + operation_code: &str, + ) -> kb_execution_api::ExecutionCapability { + if program_id.0 != kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID { + return kb_execution_api::ExecutionCapability::unsupported( + "execution_spl_ata_program_not_owned", + format!( + "program {} is not owned by kb_executor_spl_associated_token_account", + program_id.0 + ), + ); + } + if crate::SUPPORTED_OPERATION_CODES.contains(&operation_code) { + return kb_execution_api::ExecutionCapability::supported(operation_code); + } + return kb_execution_api::ExecutionCapability::unsupported( + "execution_spl_ata_operation_unsupported", + format!("SPL Associated Token Account operation {operation_code} is not implemented"), + ); + } +} + +impl kb_execution_api::TypedInstructionExecutor for crate::SplAssociatedTokenAccountExecutor { + type Intent = crate::SplAssociatedTokenAccountExecutionIntent; + + fn capability( + &self, + program_id: &kb_model::ProgramId, + operation_code: &str, + ) -> kb_execution_api::ExecutionCapability { + return self.exact_capability(program_id, operation_code); + } + + fn build_prepared_plan( + &self, + intent: &Self::Intent, + ) -> kb_core::Result { + let program_id = kb_model::ProgramId(std::string::String::from( + kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID, + )); + return match self.exact_capability(&program_id, intent.operation.operation_code()) { + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => { + crate::build_prepared_plan(intent) + }, + kb_execution_api::ExecutionCapability::Unsupported { reason_code, reason } => { + std::result::Result::Err(kb_core::Error::new(reason_code, reason)) + }, + }; + } +} + +impl kb_execution_api::InstructionExecutor for crate::SplAssociatedTokenAccountExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_spl_associated_token_account"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + return match self.exact_capability(&request.program_id, &request.operation_code) { + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => { + kb_execution_api::ExecutionSupport::Yes + }, + kb_execution_api::ExecutionCapability::Unsupported { reason_code: _, reason: _ } => { + kb_execution_api::ExecutionSupport::No + }, + }; + } + + fn build_plan( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + match self.exact_capability(&request.program_id, &request.operation_code) { + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => {}, + kb_execution_api::ExecutionCapability::Unsupported { reason_code, reason } => { + return std::result::Result::Err(kb_core::Error::new(reason_code, reason)); + }, + } + let intent = match serde_json::from_str::( + &request.payload_json, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_ata_intent_deserialize_failed", + error.to_string(), + )); + }, + }; + if intent.operation.operation_code() != request.operation_code.as_str() { + return std::result::Result::Err(kb_core::Error::new( + "execution_operation_code_mismatch", + format!( + "request operation {} does not match typed intent operation {}", + request.operation_code, + intent.operation.operation_code() + ), + )); + } + let prepared = + match kb_execution_api::TypedInstructionExecutor::build_prepared_plan(self, &intent) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let payload_value = match serde_json::to_value(&prepared) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_ata_plan_serialize_failed", + error.to_string(), + )); + }, + }; + let payload_json = match kb_execution_api::serialize_payload_json(&payload_value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_spl_associated_token_account"), + instruction_count: prepared.instructions.len(), + payload_json, + }); + } +} + +#[cfg(test)] +mod tests { + fn pubkey(value: &str) -> kb_model::Pubkey { + return kb_model::Pubkey(value.to_string()); + } + + fn intent( + operation: crate::SplAssociatedTokenAccountOperation, + max_rent_lamports: u64, + ) -> crate::SplAssociatedTokenAccountExecutionIntent { + let fee_payer = pubkey(kb_program_ids::SYSTEM_PROGRAM_ID); + let wallet_owner = match &operation { + crate::SplAssociatedTokenAccountOperation::Create { wallet_owner, .. } + | crate::SplAssociatedTokenAccountOperation::CreateIdempotent { + wallet_owner, .. + } + | crate::SplAssociatedTokenAccountOperation::RecoverNested { wallet_owner, .. } => { + wallet_owner.clone() + }, + }; + let mut authorized_signers = std::vec![fee_payer.clone()]; + if !authorized_signers.contains(&wallet_owner) { + authorized_signers.push(wallet_owner); + } + return crate::SplAssociatedTokenAccountExecutionIntent { + intent_id: "ata-intent-1".to_string(), + fee_payer, + max_rent_lamports, + policy: kb_execution_api::ExecutionPolicy { + simulation: kb_execution_api::ExecutionSimulationPolicy::Required, + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(max_rent_lamports), + max_fee_lamports: std::option::Option::Some(10_000), + max_compute_unit_price_micro_lamports: std::option::Option::None, + }, + authorized_signers, + dry_run: true, + post_execution_validation: kb_execution_api::PostExecutionValidationPolicy { + canonical_insert_required: true, + core_extraction_required: true, + decode_replay_required: true, + materialization_required: true, + }, + ..kb_execution_api::ExecutionPolicy::default() + }, + operation, + }; + } + + fn request(program_id: &str, operation_code: &str) -> kb_execution_api::ExecutionRequest { + return kb_execution_api::ExecutionRequest { + program_id: kb_model::ProgramId(program_id.to_string()), + operation_code: operation_code.to_string(), + payload_json: std::string::String::from("{}"), + }; + } + + #[test] + fn exact_capabilities_cover_every_published_current_variant() { + let executor = crate::SplAssociatedTokenAccountExecutor; + for operation_code in crate::SUPPORTED_OPERATION_CODES { + let request = request(kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID, operation_code); + assert_eq!( + kb_execution_api::InstructionExecutor::supports_request(&executor, &request), + kb_execution_api::ExecutionSupport::Yes + ); + } + for (program_id, operation_code) in [ + (kb_program_ids::SYSTEM_PROGRAM_ID, crate::CREATE_OPERATION), + ( + kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID, + "spl_associated_token_account.close", + ), + ] { + let request = request(program_id, operation_code); + assert_eq!( + kb_execution_api::InstructionExecutor::supports_request(&executor, &request), + kb_execution_api::ExecutionSupport::No + ); + } + } + + #[test] + fn generic_request_roundtrips_one_typed_instruction() { + let pintent = intent( + crate::SplAssociatedTokenAccountOperation::CreateIdempotent { + wallet_owner: pubkey(kb_program_ids::STAKE_PROGRAM_ID), + mint: pubkey(kb_program_ids::VOTE_PROGRAM_ID), + token_program: crate::SplAssociatedTokenProgram::Token2022, + }, + 2_100_000, + ); + let payload_json = serde_json::to_string(&pintent) + .unwrap_or_else(|error| panic!("ATA intent serialization failed: {error}")); + let request = kb_execution_api::ExecutionRequest { + program_id: kb_model::ProgramId( + kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID.to_string(), + ), + operation_code: crate::CREATE_IDEMPOTENT_OPERATION.to_string(), + payload_json, + }; + let plan = kb_execution_api::InstructionExecutor::build_plan( + &crate::SplAssociatedTokenAccountExecutor, + &request, + ) + .unwrap_or_else(|error| panic!("generic ATA plan failed: {error}")); + assert_eq!(plan.instruction_count, 1); + let prepared = + serde_json::from_str::(&plan.payload_json) + .unwrap_or_else(|error| panic!("prepared ATA plan parsing failed: {error}")); + assert_eq!(prepared.operation_code, crate::CREATE_IDEMPOTENT_OPERATION); + assert_eq!(prepared.instructions[0].data, std::vec![1]); + } + + #[test] + fn machine_readable_matrix_matches_compiled_executor_capabilities() { + let matrix: serde_json::Value = serde_json::from_str(include_str!( + "../../docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json" + )) + .unwrap_or_else(|error| panic!("ATA matrix parsing failed: {error}")); + let instructions = matrix["instructions"] + .as_array() + .unwrap_or_else(|| panic!("ATA matrix instructions must be an array")); + let matrix_operations = instructions + .iter() + .map(|instruction| { + assert_eq!(instruction["executorSupport"]["status"], "supported"); + return instruction["executorSupport"]["operationCode"] + .as_str() + .unwrap_or_else(|| panic!("ATA operation code must be a string")); + }) + .collect::>(); + assert_eq!(matrix_operations, crate::SUPPORTED_OPERATION_CODES); + assert_eq!(matrix["surfaceEquality"]["matrixVariantCount"], 3); + assert_eq!(matrix["surfaceEquality"]["officialInterfaceVariantCount"], 3); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/intent.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/intent.rs new file mode 100644 index 0000000..d49a54c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/intent.rs @@ -0,0 +1,122 @@ +// file: kb_executor_spl_associated_token_account/src/intent.rs +// version: 2 + +//! Typed SPL Associated Token Account execution intents. + +use ts_rs::TS; // rust-rules: derive-import + +/// Stable operation code for strict ATA creation. +pub const CREATE_OPERATION: &str = "spl_associated_token_account.create"; +/// Stable operation code for idempotent ATA creation or reuse. +pub const CREATE_IDEMPOTENT_OPERATION: &str = "spl_associated_token_account.create_idempotent"; +/// Stable operation code for nested ATA recovery. +pub const RECOVER_NESTED_OPERATION: &str = "spl_associated_token_account.recover_nested"; +/// Every current officially constructible ATA operation. +pub const SUPPORTED_OPERATION_CODES: &[&str] = + &[CREATE_OPERATION, CREATE_IDEMPOTENT_OPERATION, RECOVER_NESTED_OPERATION]; + +/// Exact Token Program targeted by ATA derivation and runtime CPIs. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_associated_token_account/intent/SplAssociatedTokenProgram.ts" +)] +pub enum SplAssociatedTokenProgram { + /// Classic SPL Token program. + Classic, + /// Token-2022 program without extension-specific executor semantics. + Token2022, +} + +impl crate::SplAssociatedTokenProgram { + /// Returns the exact target Token Program ID. + pub fn program_id(&self) -> &'static str { + return match self { + Self::Classic => kb_program_ids::SPL_TOKEN_PROGRAM_ID, + Self::Token2022 => kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + }; + } +} + +/// One officially constructible ATA operation. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(tag = "operation", rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_associated_token_account/intent/SplAssociatedTokenAccountOperation.ts" +)] +pub enum SplAssociatedTokenAccountOperation { + /// Strictly create an absent canonical ATA. + Create { + /// Wallet owner used as the first PDA seed. + wallet_owner: kb_model::Pubkey, + /// Mint used as the third PDA seed. + mint: kb_model::Pubkey, + /// Token Program used as the second PDA seed and runtime CPI target. + token_program: crate::SplAssociatedTokenProgram, + }, + /// Create an absent ATA or accept an existing compatible ATA. + CreateIdempotent { + /// Wallet owner used as the first PDA seed. + wallet_owner: kb_model::Pubkey, + /// Mint used as the third PDA seed. + mint: kb_model::Pubkey, + /// Token Program used as the second PDA seed and runtime CPI target. + token_program: crate::SplAssociatedTokenProgram, + }, + /// Recover tokens and lamports from one canonical nested ATA. + RecoverNested { + /// Wallet owner that must sign the recovery. + wallet_owner: kb_model::Pubkey, + /// Mint whose ATA currently owns the nested ATA. + owner_mint: kb_model::Pubkey, + /// Mint held by the nested ATA and the wallet destination ATA. + nested_mint: kb_model::Pubkey, + /// Token Program shared by all three derived ATA addresses. + token_program: crate::SplAssociatedTokenProgram, + }, +} + +impl crate::SplAssociatedTokenAccountOperation { + /// Returns the stable generic dispatch operation code. + pub fn operation_code(&self) -> &'static str { + return match self { + Self::Create { .. } => crate::CREATE_OPERATION, + Self::CreateIdempotent { .. } => crate::CREATE_IDEMPOTENT_OPERATION, + Self::RecoverNested { .. } => crate::RECOVER_NESTED_OPERATION, + }; + } + + /// Returns the exact target Token Program. + pub fn token_program(&self) -> crate::SplAssociatedTokenProgram { + return match self { + Self::Create { token_program, .. } + | Self::CreateIdempotent { token_program, .. } + | Self::RecoverNested { token_program, .. } => *token_program, + }; + } + + pub(crate) fn is_creation(&self) -> bool { + return matches!(self, Self::Create { .. } | Self::CreateIdempotent { .. }); + } +} + +/// Complete typed intent accepted by the ATA executor. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_associated_token_account/intent/SplAssociatedTokenAccountExecutionIntent.ts" +)] +pub struct SplAssociatedTokenAccountExecutionIntent { + /// Stable caller-provided identifier used for logs and replay correlation. + pub intent_id: std::string::String, + /// Transaction fee payer and Create/CreateIdempotent funding account. + pub fee_payer: kb_model::Pubkey, + /// Maximum rent lamports the creation operation may spend; zero for RecoverNested. + pub max_rent_lamports: u64, + /// Conservative common execution policy. + pub policy: kb_execution_api::ExecutionPolicy, + /// Typed ATA operation and exact address inputs. + pub operation: crate::SplAssociatedTokenAccountOperation, +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/lib.rs new file mode 100644 index 0000000..4fb12f0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_associated_token_account/src/lib.rs @@ -0,0 +1,34 @@ +// file: kb_executor_spl_associated_token_account/src/lib.rs +// version: 5 + +//! Executor crate for `spl_associated_token_account`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod builder; +mod constants; +mod executor; +mod intent; + +/// Crate-root access to `build_prepared_plan` from `builder`. +pub(crate) use crate::builder::build_prepared_plan; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the exact ATA executor. +pub use crate::executor::SplAssociatedTokenAccountExecutor; +/// Stable CreateIdempotent operation code. +pub use crate::intent::CREATE_IDEMPOTENT_OPERATION; +/// Stable Create operation code. +pub use crate::intent::CREATE_OPERATION; +/// Stable RecoverNested operation code. +pub use crate::intent::RECOVER_NESTED_OPERATION; +/// All officially constructible ATA operation codes. +pub use crate::intent::SUPPORTED_OPERATION_CODES; +/// Exposes the typed ATA execution intent. +pub use crate::intent::SplAssociatedTokenAccountExecutionIntent; +/// Exposes the typed ATA operation. +pub use crate::intent::SplAssociatedTokenAccountOperation; +/// Exposes the exact target Token Program selection. +pub use crate::intent::SplAssociatedTokenProgram; diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/Cargo.toml new file mode 100644 index 0000000..3083608 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/Cargo.toml @@ -0,0 +1,29 @@ +# file: kb_executor_spl_elgamal_registry/Cargo.toml +# version: 3 + +[package] +name = "kb_executor_spl_elgamal_registry" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +base64.workspace = true +bytemuck.workspace = true +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde.workspace = true +serde_json.workspace = true +solana-instruction.workspace = true +solana-pubkey.workspace = true +solana-zk-elgamal-proof-interface.workspace = true +spl-elgamal-registry-interface.workspace = true +spl-token-confidential-transfer-proof-extraction.workspace = true +tracing.workspace = true +ts-rs.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/README.md b/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/README.md new file mode 100644 index 0000000..be3dcde --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/README.md @@ -0,0 +1,6 @@ + + + +# kb_executor_spl_elgamal_registry + +Exécuteur typé du registre ElGamal SPL. Il construit `create_registry` et `update_registry` au moyen de l'interface officielle, avec preuve `PubkeyValidity` inline ou compte de contexte prévalidé. Toute exécution exige une simulation, une limite de frais positive et la validation canonique après envoi. diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/builder.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/builder.rs new file mode 100644 index 0000000..d157068 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/builder.rs @@ -0,0 +1,279 @@ +// file: kb_executor_spl_elgamal_registry/src/builder.rs +// version: 4 + +//! Official registry builders and conservative plan validation. + +use base64::Engine; // rust-rules: trait-import +use std::str::FromStr; // rust-rules: trait-import + +pub(crate) fn build_prepared_plan( + intent: &crate::SplElgamalRegistryExecutionIntent, +) -> kb_core::Result { + if intent.intent_id.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_elgamal_registry_intent_id_empty", + "ElGamal registry execution intent id must not be empty", + )); + } + if intent.policy.simulation != kb_execution_api::ExecutionSimulationPolicy::Required { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_elgamal_registry_simulation_required", + "ElGamal registry execution requires simulation", + )); + } + match intent.policy.cost_limit.max_fee_lamports { + std::option::Option::Some(value) if value > 0 => {}, + std::option::Option::Some(_) | std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_elgamal_registry_fee_limit_missing", + "ElGamal registry execution requires a positive fee ceiling", + )); + }, + } + let validation = &intent.policy.post_execution_validation; + if !validation.canonical_insert_required + || !validation.core_extraction_required + || !validation.decode_replay_required + || !validation.materialization_required + { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_elgamal_registry_post_validation_required", + "ElGamal registry execution requires canonical insert, core extraction, decode replay and materialization", + )); + } + let fee_payer = match parse_address(&intent.fee_payer, "fee_payer") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let (owner_model, proof, operation_code, create) = match &intent.operation { + crate::SplElgamalRegistryOperation::CreateRegistry { owner, proof } => { + (owner, proof, crate::CREATE_REGISTRY_OPERATION, true) + }, + crate::SplElgamalRegistryOperation::UpdateRegistry { owner, proof } => { + (owner, proof, crate::UPDATE_REGISTRY_OPERATION, false) + }, + }; + let owner = match parse_address(owner_model, "owner") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instructions = match proof { + crate::SplElgamalRegistryProofLocation::ContextStateAccount { account } => { + let context = match parse_address(account, "context_state_account") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let location = spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&context); + build_official(create, &owner, location) + }, + crate::SplElgamalRegistryProofLocation::Inline { proof_data_base64 } => { + let bytes = match base64::engine::general_purpose::STANDARD + .decode(proof_data_base64.as_bytes()) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_elgamal_registry_proof_base64_invalid", + error.to_string(), + )); + }, + }; + let expected = std::mem::size_of::< + solana_zk_elgamal_proof_interface::proof_data::PubkeyValidityProofData, + >(); + if bytes.len() != expected { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_elgamal_registry_proof_size_invalid", + format!( + "PubkeyValidityProofData requires {expected} bytes, received {}", + bytes.len() + ), + )); + } + let proof_data = bytemuck::pod_read_unaligned::< + solana_zk_elgamal_proof_interface::proof_data::PubkeyValidityProofData, + >(bytes.as_slice()); + let offset = match std::num::NonZeroI8::new(1) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_elgamal_registry_internal_offset_invalid", + "inline proof offset must be one", + )); + }, + }; + let location = spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &proof_data); + build_official(create, &owner, location) + }, + }; + let instructions: Vec = match instructions { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let planned: Vec = instructions + .iter() + .map(|instruction| return planned_instruction(operation_code, instruction)) + .collect(); + let mut required_signers = vec![kb_execution_api::RequiredSigner { + pubkey: intent.fee_payer.clone(), + role: std::string::String::from("fee_payer"), + }]; + if intent.fee_payer != *owner_model { + required_signers.push(kb_execution_api::RequiredSigner { + pubkey: owner_model.clone(), + role: std::string::String::from("registry_owner"), + }); + } + tracing::debug!( + target: crate::TRACING_TARGET, + action = "build_prepared_plan", + intent_id = %intent.intent_id, + operation_code, + owner = %owner_model.0, + instruction_count = planned.len(), + "built SPL ElGamal registry execution plan" + ); + return std::result::Result::Ok(kb_execution_api::PreparedExecutionPlan { + executor_name: std::string::String::from("kb_executor_spl_elgamal_registry"), + executor_version: std::string::String::from(env!("CARGO_PKG_VERSION")), + intent_id: intent.intent_id.clone(), + operation_code: std::string::String::from(operation_code), + fee_payer: kb_model::Pubkey(fee_payer.to_string()), + instructions: planned, + required_signers, + policy: intent.policy.clone(), + requested_spend_lamports: 0, + requested_compute_unit_price_micro_lamports: std::option::Option::None, + }); +} + +fn build_official( + create: bool, + owner: &solana_pubkey::Pubkey, + location: spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation< + solana_zk_elgamal_proof_interface::proof_data::PubkeyValidityProofData, + >, +) -> kb_core::Result> { + let result = if create { + spl_elgamal_registry_interface::instruction::create_registry(owner, location) + } else { + spl_elgamal_registry_interface::instruction::update_registry(owner, location) + }; + return match result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "execution_spl_elgamal_registry_builder_failed", + error.to_string(), + )), + }; +} + +fn parse_address(value: &kb_model::Pubkey, field: &str) -> kb_core::Result { + return match solana_pubkey::Pubkey::from_str(value.0.as_str()) { + std::result::Result::Ok(address) => std::result::Result::Ok(address), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "execution_spl_elgamal_registry_pubkey_invalid", + format!("{field}: {error}"), + )), + }; +} + +fn planned_instruction( + operation_code: &str, + instruction: &solana_instruction::Instruction, +) -> kb_execution_api::PlannedInstruction { + return kb_execution_api::PlannedInstruction { + program_id: kb_model::ProgramId(instruction.program_id.to_string()), + operation_code: std::string::String::from(operation_code), + accounts: instruction + .accounts + .iter() + .map(|account| { + return kb_execution_api::PlannedAccount { + pubkey: kb_model::Pubkey(account.pubkey.to_string()), + is_signer: account.is_signer, + is_writable: account.is_writable, + }; + }) + .collect(), + data: instruction.data.clone(), + }; +} + +#[cfg(test)] +mod tests { + fn pubkey(value: &str) -> kb_model::Pubkey { + return kb_model::Pubkey(std::string::String::from(value)); + } + + fn intent(create: bool) -> crate::SplElgamalRegistryExecutionIntent { + let owner = pubkey(kb_program_ids::SYSTEM_PROGRAM_ID); + return crate::SplElgamalRegistryExecutionIntent { + intent_id: std::string::String::from("registry-intent"), + fee_payer: owner.clone(), + policy: kb_execution_api::ExecutionPolicy { + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(0), + max_fee_lamports: std::option::Option::Some(20_000), + max_compute_unit_price_micro_lamports: std::option::Option::None, + }, + authorized_signers: vec![owner.clone()], + dry_run: true, + post_execution_validation: kb_execution_api::PostExecutionValidationPolicy { + canonical_insert_required: true, + core_extraction_required: true, + decode_replay_required: true, + materialization_required: true, + }, + ..kb_execution_api::ExecutionPolicy::default() + }, + operation: if create { + crate::SplElgamalRegistryOperation::CreateRegistry { + owner, + proof: crate::SplElgamalRegistryProofLocation::ContextStateAccount { + account: pubkey(kb_program_ids::VOTE_PROGRAM_ID), + }, + } + } else { + crate::SplElgamalRegistryOperation::UpdateRegistry { + owner, + proof: crate::SplElgamalRegistryProofLocation::ContextStateAccount { + account: pubkey(kb_program_ids::VOTE_PROGRAM_ID), + }, + } + }, + }; + } + + #[test] + fn create_and_update_use_official_registry_wire_and_accounts() { + for create in [true, false] { + let plan = crate::build_prepared_plan(&intent(create)) + .unwrap_or_else(|error| panic!("registry plan failed: {error}")); + assert_eq!(plan.instructions.len(), 1); + assert_eq!( + plan.instructions[0].program_id.0, + kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID + ); + assert_eq!(plan.instructions[0].data, if create { vec![0, 0] } else { vec![1, 0] }); + assert_eq!(plan.required_signers.len(), 1); + } + } + + #[test] + fn optional_simulation_and_invalid_inline_proof_fail_closed() { + let mut value = intent(true); + value.policy.simulation = kb_execution_api::ExecutionSimulationPolicy::Optional; + let error = crate::build_prepared_plan(&value).expect_err("optional simulation must fail"); + assert_eq!(error.code(), "execution_spl_elgamal_registry_simulation_required"); + value = intent(true); + value.operation = crate::SplElgamalRegistryOperation::CreateRegistry { + owner: pubkey(kb_program_ids::SYSTEM_PROGRAM_ID), + proof: crate::SplElgamalRegistryProofLocation::Inline { + proof_data_base64: std::string::String::from("AA=="), + }, + }; + let error = crate::build_prepared_plan(&value).expect_err("invalid proof size must fail"); + assert_eq!(error.code(), "execution_spl_elgamal_registry_proof_size_invalid"); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/constants.rs new file mode 100644 index 0000000..0904933 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_executor_spl_elgamal_registry/src/constants.rs +// version: 2 + +//! Executor-local constants. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_spl_elgamal_registry"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/executor.rs new file mode 100644 index 0000000..561a817 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/executor.rs @@ -0,0 +1,163 @@ +// file: kb_executor_spl_elgamal_registry/src/executor.rs +// version: 2 + +//! Exact capability dispatch for the SPL ElGamal registry. + +/// Typed SPL ElGamal registry executor. +#[derive(Clone, Debug, Default)] +pub struct SplElgamalRegistryExecutor; + +impl crate::SplElgamalRegistryExecutor { + fn exact_capability( + &self, + program_id: &kb_model::ProgramId, + operation_code: &str, + ) -> kb_execution_api::ExecutionCapability { + if program_id.0 != kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID { + return kb_execution_api::ExecutionCapability::unsupported( + "execution_spl_elgamal_registry_program_not_owned", + format!("program {} is not the SPL ElGamal registry", program_id.0), + ); + } + return match operation_code { + crate::CREATE_REGISTRY_OPERATION | crate::UPDATE_REGISTRY_OPERATION => { + kb_execution_api::ExecutionCapability::supported(operation_code) + }, + _ => kb_execution_api::ExecutionCapability::unsupported( + "execution_spl_elgamal_registry_operation_unsupported", + format!("ElGamal registry operation {operation_code} is not implemented"), + ), + }; + } +} + +impl kb_execution_api::TypedInstructionExecutor for crate::SplElgamalRegistryExecutor { + type Intent = crate::SplElgamalRegistryExecutionIntent; + + fn capability( + &self, + program_id: &kb_model::ProgramId, + operation_code: &str, + ) -> kb_execution_api::ExecutionCapability { + return self.exact_capability(program_id, operation_code); + } + + fn build_prepared_plan( + &self, + intent: &Self::Intent, + ) -> kb_core::Result { + let program_id = kb_model::ProgramId(std::string::String::from( + kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID, + )); + return match self.exact_capability(&program_id, intent.operation.operation_code()) { + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => { + crate::build_prepared_plan(intent) + }, + kb_execution_api::ExecutionCapability::Unsupported { reason_code, reason } => { + std::result::Result::Err(kb_core::Error::new(reason_code, reason)) + }, + }; + } +} + +impl kb_execution_api::InstructionExecutor for crate::SplElgamalRegistryExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_spl_elgamal_registry"; + } + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID]; + } + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + return if self + .exact_capability(&request.program_id, &request.operation_code) + .is_supported() + { + kb_execution_api::ExecutionSupport::Yes + } else { + kb_execution_api::ExecutionSupport::No + }; + } + fn build_plan( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + match self.exact_capability(&request.program_id, &request.operation_code) { + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => {}, + kb_execution_api::ExecutionCapability::Unsupported { reason_code, reason } => { + return std::result::Result::Err(kb_core::Error::new(reason_code, reason)); + }, + } + let intent = match serde_json::from_str::( + &request.payload_json, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_elgamal_registry_intent_deserialize_failed", + error.to_string(), + )); + }, + }; + if intent.operation.operation_code() != request.operation_code { + return std::result::Result::Err(kb_core::Error::new( + "execution_operation_code_mismatch", + "request and typed intent operations differ", + )); + } + let prepared = + match kb_execution_api::TypedInstructionExecutor::build_prepared_plan(self, &intent) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let payload = match serde_json::to_value(&prepared) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_elgamal_registry_plan_serialize_failed", + error.to_string(), + )); + }, + }; + let payload_json = match kb_execution_api::serialize_payload_json(&payload) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_spl_elgamal_registry"), + instruction_count: prepared.instructions.len(), + payload_json, + }); + } +} + +#[cfg(test)] +mod tests { + #[test] + fn program_and_operation_dispatch_are_exact() { + let executor = crate::SplElgamalRegistryExecutor; + for operation in [crate::CREATE_REGISTRY_OPERATION, crate::UPDATE_REGISTRY_OPERATION] { + let capability = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from( + kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID, + )), + operation, + ); + assert!(capability.is_supported()); + } + let foreign = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(std::string::String::from( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + )), + crate::CREATE_REGISTRY_OPERATION, + ); + assert!(!foreign.is_supported()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/intent.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/intent.rs new file mode 100644 index 0000000..09fa39e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/intent.rs @@ -0,0 +1,82 @@ +// file: kb_executor_spl_elgamal_registry/src/intent.rs +// version: 2 + +//! Typed intents for the SPL ElGamal public-key registry. + +use ts_rs::TS; // rust-rules: derive-import + +/// Stable operation code for registry creation. +pub const CREATE_REGISTRY_OPERATION: &str = "spl_elgamal_registry.create_registry"; +/// Stable operation code for registry update. +pub const UPDATE_REGISTRY_OPERATION: &str = "spl_elgamal_registry.update_registry"; + +/// Exact location of the public-key validity proof. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(tag = "location", rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_elgamal_registry/intent/SplElgamalRegistryProofLocation.ts" +)] +pub enum SplElgamalRegistryProofLocation { + /// A pre-verified proof context-state account. + ContextStateAccount { + /// Context-state account owned by the native ZK ElGamal proof program. + account: kb_model::Pubkey, + }, + /// Inline `PubkeyValidity` proof placed immediately after the registry instruction. + Inline { + /// Canonical raw `PubkeyValidityProofData` bytes encoded as base64. + proof_data_base64: std::string::String, + }, +} + +/// Typed registry operation. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(tag = "operation", rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_elgamal_registry/intent/SplElgamalRegistryOperation.ts" +)] +pub enum SplElgamalRegistryOperation { + /// Create the deterministic registry PDA for one owner. + CreateRegistry { + /// Wallet that owns and signs for the registry. + owner: kb_model::Pubkey, + /// Proof location. + proof: crate::SplElgamalRegistryProofLocation, + }, + /// Update the registry with a newly proven ElGamal public key. + UpdateRegistry { + /// Wallet that owns and signs for the registry. + owner: kb_model::Pubkey, + /// Proof location. + proof: crate::SplElgamalRegistryProofLocation, + }, +} + +impl crate::SplElgamalRegistryOperation { + /// Returns the stable operation code. + pub fn operation_code(&self) -> &'static str { + return match self { + Self::CreateRegistry { owner: _, proof: _ } => crate::CREATE_REGISTRY_OPERATION, + Self::UpdateRegistry { owner: _, proof: _ } => crate::UPDATE_REGISTRY_OPERATION, + }; + } +} + +/// Complete typed execution intent. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_elgamal_registry/intent/SplElgamalRegistryExecutionIntent.ts" +)] +pub struct SplElgamalRegistryExecutionIntent { + /// Caller-provided correlation identifier. + pub intent_id: std::string::String, + /// Transaction fee payer. + pub fee_payer: kb_model::Pubkey, + /// Conservative common execution policy. + pub policy: kb_execution_api::ExecutionPolicy, + /// Registry operation. + pub operation: crate::SplElgamalRegistryOperation, +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/lib.rs new file mode 100644 index 0000000..7d534df --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_elgamal_registry/src/lib.rs @@ -0,0 +1,30 @@ +// file: kb_executor_spl_elgamal_registry/src/lib.rs +// version: 5 + +//! Typed executor for the SPL ElGamal public-key registry. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod builder; +mod constants; +mod executor; +mod intent; + +/// Crate-root access to `build_prepared_plan` from `builder`. +pub(crate) use crate::builder::build_prepared_plan; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the typed ElGamal registry executor. +pub use crate::executor::SplElgamalRegistryExecutor; +/// Stable operation code for registry creation. +pub use crate::intent::CREATE_REGISTRY_OPERATION; +/// Exposes the complete typed execution intent. +pub use crate::intent::SplElgamalRegistryExecutionIntent; +/// Exposes one exact registry operation. +pub use crate::intent::SplElgamalRegistryOperation; +/// Exposes the proof-location contract. +pub use crate::intent::SplElgamalRegistryProofLocation; +/// Stable operation code for registry update. +pub use crate::intent::UPDATE_REGISTRY_OPERATION; diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_memo/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_spl_memo/Cargo.toml new file mode 100644 index 0000000..78319b0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_memo/Cargo.toml @@ -0,0 +1,28 @@ +# file: kb_executor_spl_memo/Cargo.toml +# version: 3 + +[package] +name = "kb_executor_spl_memo" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde.workspace = true +serde_json.workspace = true +solana-instruction.workspace = true +solana-pubkey.workspace = true +spl-memo-interface.workspace = true +tracing.workspace = true +ts-rs.workspace = true + +[dev-dependencies] +kb_execution_safety = { path = "../kb_execution_safety" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_memo/README.md b/migration/khadhroony-bot2-reference/kb_executor_spl_memo/README.md new file mode 100644 index 0000000..bf06ea5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_memo/README.md @@ -0,0 +1,50 @@ + + + +# kb_executor_spl_memo + +Ce crate reconnaît les Program IDs exacts SPL Memo v1, v3 et v4, mais construit des plans d’exécution uniquement pour la génération courante v4. Il ne dépend ni du wallet, ni du RPC, ni du décodeur et n’effectue directement aucune signature ni aucun envoi. + +## Contrat typé + +L’unique opération est `spl_memo.add_memo`, portée par : + +- `SplMemoGeneration::V1`, `V3` ou `V4`, afin de produire une capacité exacte ; +- `SplMemoOperation::AddMemo` ; +- `SplMemoExecutionIntent` avec intent ID, fee payer et politique commune ; +- une liste ordonnée de `SplMemoSigner` dont les doublons sont conservés dans l’instruction. + +Le message est un `String` Rust : sa validité UTF‑8 est donc acquise avant la construction. Les octets UTF‑8 exacts deviennent l’intégralité de `Instruction::data`, sans préfixe, discriminator, Borsh ou `bincode`. + +## Builder et comptes + +Pour v4, le crate appelle directement `spl_memo_interface::instruction::build_memo` de la version 2.1.0 avec le Program ID explicite. Les tests comparent le plan produit au builder officiel. Les générations v1 et v3 sont historiques `decode-only` et n'atteignent jamais ce builder par l'API de l'exécuteur. + +Chaque signer fourni devient un compte `readonly`, `is_signer = true`, dans l’ordre exact. Aucun compte writable n’est inventé. Les occurrences dupliquées restent dans les metas ; la liste transactionnelle `required_signers` est dédupliquée et commence toujours par le fee payer. + +Le payload est borné à 566 octets et la liste à 32 occurrences de signataires. Ces limites sont des garde-fous conservateurs de l’exécuteur, pas de nouveaux rejets attribués au runtime. L’assembleur commun vérifie ensuite la taille wire exacte de 1 232 octets, qui dépend notamment du nombre de signatures. + +## Capacités et politiques + +La construction de plan est `Supported` pour `AddMemo` uniquement sur l'ID v4 exact. V1 et v3 retournent `Unsupported(reason)` avec le code stable `execution_spl_memo_historical_generation_decode_only`. Toute autre opération ou tout autre Program ID est également `Unsupported(reason)` avec son propre motif. + +- simulation obligatoire ; +- dry-run par défaut via `ExecutionPolicy::default()` ; +- plafond de frais strictement positif obligatoire ; +- dépense d’instruction égale à zéro lamport, hors frais réseau ; +- construction v4 disponible sur Localnet, Devnet, Testnet et Mainnet ; +- aucune génération artificiellement limitée au dry-run par la bibliothèque ; +- Mainnet gouverné par la politique commune `allow_mainnet` et sa confirmation explicite ; +- insertion canonique, extraction core, decode replay Memo et matérialisation exigés après exécution. + +La disponibilité effective de v4 sur un cluster n’est pas supposée par le builder : elle est établie par la simulation RPC obligatoire. Une future génération expérimentale officiellement encodée pourra être ajoutée sans la limiter artificiellement à un cluster. Les générations historiques restent décodables dans `kb_decoder_spl_memo`, mais ne sont pas exécutables. La démo `0.4.3` reste limitée à Devnet. + +Les signataires restent visibles dans `PreparedExecutionPlan` avant toute signature. Les clés privées ne font partie ni de l’intent ni du plan. + +## Validation Devnet et démos + +`kb_pipeline::execute_devnet_memo` réutilise l’infrastructure de `0.4.2` pour construire un Memo v4, simuler, demander une autorisation explicite avant l’envoi, confirmer, hydrater, extraire, rejouer le décodeur, vérifier l’annotation puis prouver l’idempotence par un second replay non forcé. + +La fenêtre Solana Devnet existante expose ce parcours sans nouvelle WebView. Elle affiche payload, fee payer, signataire wallet readonly, plan, simulation et validation post-exécution. Le journal générique `demo_decode_replay` reste la surface de consultation des annotations matérialisées. Les DTO Memo sérialisent soldes et frais en chaînes décimales et n’exportent aucune clé privée ni `bigint` actif. + +La validation réelle du 15 juillet 2026 comprend une simulation seule et trois transactions v4 Devnet confirmées. Chacune a produit une insertion canonique, une extraction core, un decode, une annotation et un second replay `skipped` sans nouvelle matérialisation. Les signatures exactes sont enregistrées dans `docs/SPL_MEMO_MATRIX.json`. diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/builder.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/builder.rs new file mode 100644 index 0000000..cb5865f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/builder.rs @@ -0,0 +1,501 @@ +// file: kb_executor_spl_memo/src/builder.rs +// version: 6 + +//! Official SPL Memo instruction builder and conservative plan validation. + +use std::str::FromStr; // rust-rules: trait-import + +pub(crate) fn build_prepared_plan( + intent: &crate::SplMemoExecutionIntent, +) -> kb_core::Result { + if intent.intent_id.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_memo_intent_id_empty", + "SPL Memo execution intent id must not be empty", + )); + } + let fee_payer = match parse_pubkey(&intent.fee_payer, "fee_payer") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + match validate_policy(intent) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let (generation, message, signers) = match &intent.operation { + crate::SplMemoOperation::AddMemo { generation, message, signers } => { + (*generation, message, signers) + }, + }; + if message.len() > crate::MAX_MEMO_MESSAGE_BYTES { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_memo_payload_too_large", + format!( + "SPL Memo payload length {} exceeds the executor limit {}", + message.len(), + crate::MAX_MEMO_MESSAGE_BYTES + ), + )); + } + if signers.len() > crate::MAX_MEMO_SIGNERS { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_memo_signer_limit_exceeded", + format!( + "SPL Memo signer occurrence count {} exceeds the executor limit {}", + signers.len(), + crate::MAX_MEMO_SIGNERS + ), + )); + } + let program_id = match solana_pubkey::Pubkey::from_str(generation.program_id()) { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_memo_program_id_invalid", + error.to_string(), + )); + }, + }; + let mut signer_pubkeys = std::vec::Vec::with_capacity(signers.len()); + for signer in signers { + let pubkey = match parse_pubkey(&signer.pubkey, "memo_signer") { + std::result::Result::Ok(pubkey) => pubkey, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + signer_pubkeys.push(pubkey); + } + let signer_refs = signer_pubkeys.iter().collect::>(); + let instruction = spl_memo_interface::instruction::build_memo( + &program_id, + message.as_bytes(), + signer_refs.as_slice(), + ); + let planned_instruction = planned_instruction(crate::SPL_MEMO_ADD_MEMO_OPERATION, &instruction); + let required_signers = required_signers(&intent.fee_payer, signers.as_slice()); + tracing::debug!( + target: crate::TRACING_TARGET, + action = "build_prepared_plan", + intent_id = %intent.intent_id, + operation_code = crate::SPL_MEMO_ADD_MEMO_OPERATION, + program_id = generation.program_id(), + payload_length = message.len(), + signer_occurrence_count = signers.len(), + required_signer_count = required_signers.len(), + dry_run = intent.policy.dry_run, + "built SPL Memo execution plan" + ); + return std::result::Result::Ok(kb_execution_api::PreparedExecutionPlan { + executor_name: std::string::String::from("kb_executor_spl_memo"), + executor_version: std::string::String::from(env!("CARGO_PKG_VERSION")), + intent_id: intent.intent_id.clone(), + operation_code: std::string::String::from(crate::SPL_MEMO_ADD_MEMO_OPERATION), + fee_payer: kb_model::Pubkey(fee_payer.to_string()), + instructions: vec![planned_instruction], + required_signers, + policy: intent.policy.clone(), + requested_spend_lamports: 0, + requested_compute_unit_price_micro_lamports: std::option::Option::None, + }); +} + +fn validate_policy(intent: &crate::SplMemoExecutionIntent) -> kb_core::Result<()> { + if intent.policy.simulation != kb_execution_api::ExecutionSimulationPolicy::Required { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_memo_simulation_required", + "SPL Memo execution requires simulation before signing or sending", + )); + } + match intent.policy.cost_limit.max_fee_lamports { + std::option::Option::Some(limit) if limit > 0 => {}, + std::option::Option::Some(_) | std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_memo_fee_limit_missing", + "SPL Memo execution requires a positive transaction fee ceiling", + )); + }, + } + let validation = &intent.policy.post_execution_validation; + if !validation.canonical_insert_required + || !validation.core_extraction_required + || !validation.decode_replay_required + || !validation.materialization_required + { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_memo_post_validation_required", + "SPL Memo execution requires canonical insertion, core extraction, decode replay and materialization validation", + )); + } + return std::result::Result::Ok(()); +} + +fn parse_pubkey(pubkey: &kb_model::Pubkey, field: &str) -> kb_core::Result { + if pubkey.0.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_memo_pubkey_empty", + format!("SPL Memo {field} public key must not be empty"), + )); + } + return match solana_pubkey::Pubkey::from_str(pubkey.0.as_str()) { + std::result::Result::Ok(parsed) => std::result::Result::Ok(parsed), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "execution_spl_memo_pubkey_invalid", + format!("invalid SPL Memo {field} public key: {error}"), + )), + }; +} + +fn planned_instruction( + operation_code: &str, + instruction: &solana_instruction::Instruction, +) -> kb_execution_api::PlannedInstruction { + return kb_execution_api::PlannedInstruction { + program_id: kb_model::ProgramId(instruction.program_id.to_string()), + operation_code: std::string::String::from(operation_code), + accounts: instruction + .accounts + .iter() + .map(|account| { + return kb_execution_api::PlannedAccount { + pubkey: kb_model::Pubkey(account.pubkey.to_string()), + is_signer: account.is_signer, + is_writable: account.is_writable, + }; + }) + .collect(), + data: instruction.data.clone(), + }; +} + +fn required_signers( + fee_payer: &kb_model::Pubkey, + signers: &[crate::SplMemoSigner], +) -> std::vec::Vec { + let mut required = vec![kb_execution_api::RequiredSigner { + pubkey: fee_payer.clone(), + role: std::string::String::from("fee_payer"), + }]; + for signer in signers { + if required.iter().any(|candidate| return candidate.pubkey == signer.pubkey) { + continue; + } + required.push(kb_execution_api::RequiredSigner { + pubkey: signer.pubkey.clone(), + role: std::string::String::from("memo_signer"), + }); + } + return required; +} + +#[cfg(test)] +pub(crate) mod tests { + use std::str::FromStr; // rust-rules: trait-import + + fn pubkey(value: &str) -> kb_model::Pubkey { + return kb_model::Pubkey(std::string::String::from(value)); + } + + fn intent( + generation: crate::SplMemoGeneration, + message: &str, + signers: &[&str], + dry_run: bool, + ) -> crate::SplMemoExecutionIntent { + let fee_payer = kb_program_ids::SYSTEM_PROGRAM_ID; + let mut authorized_signers = vec![pubkey(fee_payer)]; + for signer in signers { + let candidate = pubkey(signer); + if !authorized_signers.contains(&candidate) { + authorized_signers.push(candidate); + } + } + return crate::SplMemoExecutionIntent { + intent_id: std::string::String::from("memo-intent-1"), + fee_payer: pubkey(fee_payer), + policy: kb_execution_api::ExecutionPolicy { + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(0), + max_fee_lamports: std::option::Option::Some(10_000), + max_compute_unit_price_micro_lamports: std::option::Option::None, + }, + authorized_signers, + dry_run, + post_execution_validation: kb_execution_api::PostExecutionValidationPolicy { + canonical_insert_required: true, + core_extraction_required: true, + decode_replay_required: true, + materialization_required: true, + }, + ..kb_execution_api::ExecutionPolicy::default() + }, + operation: crate::SplMemoOperation::AddMemo { + generation, + message: std::string::String::from(message), + signers: signers + .iter() + .map(|value| { + return crate::SplMemoSigner { pubkey: pubkey(value) }; + }) + .collect(), + }, + }; + } + + fn assert_matches_official( + plan: &kb_execution_api::PreparedExecutionPlan, + generation: crate::SplMemoGeneration, + message: &str, + signers: &[&str], + ) { + let program_id = solana_pubkey::Pubkey::from_str(generation.program_id()) + .unwrap_or_else(|error| panic!("invalid program fixture: {error}")); + let signer_pubkeys = signers + .iter() + .map(|value| { + return solana_pubkey::Pubkey::from_str(value) + .unwrap_or_else(|error| panic!("invalid signer fixture: {error}")); + }) + .collect::>(); + let signer_refs = signer_pubkeys.iter().collect::>(); + let official = spl_memo_interface::instruction::build_memo( + &program_id, + message.as_bytes(), + signer_refs.as_slice(), + ); + assert_eq!(plan.instructions.len(), 1); + let actual = &plan.instructions[0]; + assert_eq!(actual.program_id.0, official.program_id.to_string()); + assert_eq!(actual.data, official.data); + assert_eq!(actual.accounts.len(), official.accounts.len()); + for (actual_account, official_account) in + actual.accounts.iter().zip(official.accounts.iter()) + { + assert_eq!(actual_account.pubkey.0, official_account.pubkey.to_string()); + assert_eq!(actual_account.is_signer, official_account.is_signer); + assert_eq!(actual_account.is_writable, official_account.is_writable); + } + } + + #[test] + fn current_generation_matches_the_official_explicit_program_builder() { + let generation = crate::SplMemoGeneration::V4; + let intent = intent(generation, "memo 🐆", &[kb_program_ids::VOTE_PROGRAM_ID], true); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplMemoExecutor, + &intent, + ) + .unwrap_or_else(|error| panic!("Memo plan failed: {error}")); + assert_matches_official(&plan, generation, "memo 🐆", &[kb_program_ids::VOTE_PROGRAM_ID]); + } + + #[test] + fn empty_payload_and_zero_signers_are_constructible() { + let intent = intent(crate::SplMemoGeneration::V4, "", &[], false); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplMemoExecutor, + &intent, + ) + .unwrap_or_else(|error| panic!("empty Memo plan failed: {error}")); + assert!(plan.instructions[0].data.is_empty()); + assert!(plan.instructions[0].accounts.is_empty()); + assert_eq!(plan.required_signers.len(), 1); + assert_eq!(plan.requested_spend_lamports, 0); + } + + #[test] + fn duplicate_signers_remain_ordered_readonly_accounts_but_required_signers_are_unique() { + let first = kb_program_ids::VOTE_PROGRAM_ID; + let second = kb_program_ids::STAKE_PROGRAM_ID; + let intent = + intent(crate::SplMemoGeneration::V4, "ordered", &[first, second, first], false); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplMemoExecutor, + &intent, + ) + .unwrap_or_else(|error| panic!("ordered Memo plan failed: {error}")); + let accounts = &plan.instructions[0].accounts; + assert_eq!(accounts.len(), 3); + assert_eq!(accounts[0].pubkey.0, first); + assert_eq!(accounts[1].pubkey.0, second); + assert_eq!(accounts[2].pubkey.0, first); + assert!(accounts.iter().all(|account| return account.is_signer)); + assert!(accounts.iter().all(|account| return !account.is_writable)); + assert_eq!(plan.required_signers.len(), 3); + } + + #[test] + fn prepared_plan_passes_common_pre_simulation_safety() { + let intent = intent( + crate::SplMemoGeneration::V4, + "safe plan", + &[kb_program_ids::VOTE_PROGRAM_ID], + true, + ); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplMemoExecutor, + &intent, + ) + .unwrap_or_else(|error| panic!("safe Memo plan failed: {error}")); + let evaluation = kb_execution_safety::ExecutionSafetyChecker + .evaluate_prepared_plan(&plan) + .unwrap_or_else(|error| panic!("safety evaluation failed: {error}")); + assert_eq!(evaluation.decision, kb_execution_safety::ExecutionSafetyDecision::Allow); + assert!(evaluation.violations.is_empty()); + } + + #[test] + fn current_generation_accepts_non_dry_run_plans() { + let generation = crate::SplMemoGeneration::V4; + let intent = intent(generation, "dry", &[], false); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplMemoExecutor, + &intent, + ) + .unwrap_or_else(|error| panic!("non-dry-run Memo plan failed: {error}")); + assert!(!plan.policy.dry_run); + assert_eq!(plan.instructions[0].program_id.0, generation.program_id()); + } + + #[test] + fn rejects_oversize_payload_and_optional_simulation() { + let mut oversize = intent( + crate::SplMemoGeneration::V4, + std::string::String::from_utf8(vec![b'a'; crate::MAX_MEMO_MESSAGE_BYTES + 1]) + .unwrap_or_else(|error| panic!("fixture creation failed: {error}")) + .as_str(), + &[], + true, + ); + let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplMemoExecutor, + &oversize, + ) + .expect_err("oversize payload must fail"); + assert_eq!(error.code(), "execution_spl_memo_payload_too_large"); + + oversize.operation = crate::SplMemoOperation::AddMemo { + generation: crate::SplMemoGeneration::V4, + message: std::string::String::from("valid"), + signers: std::vec::Vec::new(), + }; + oversize.policy.simulation = kb_execution_api::ExecutionSimulationPolicy::Optional; + let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplMemoExecutor, + &oversize, + ) + .expect_err("optional simulation must fail"); + assert_eq!(error.code(), "execution_spl_memo_simulation_required"); + } + + #[test] + fn every_cluster_is_constructible_and_mainnet_remains_governed_by_common_safety() { + for cluster in [ + kb_execution_api::ExecutionCluster::Localnet, + kb_execution_api::ExecutionCluster::Devnet, + kb_execution_api::ExecutionCluster::Testnet, + kb_execution_api::ExecutionCluster::Mainnet, + ] { + let mut intent = intent(crate::SplMemoGeneration::V4, "universal", &[], false); + intent.policy.cluster.expected_cluster = cluster; + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplMemoExecutor, + &intent, + ) + .unwrap_or_else(|error| panic!("cluster plan failed: {error}")); + assert_eq!(plan.policy.cluster.expected_cluster, cluster); + if cluster == kb_execution_api::ExecutionCluster::Mainnet { + let evaluation = kb_execution_safety::ExecutionSafetyChecker + .evaluate_prepared_plan(&plan) + .unwrap_or_else(|error| panic!("Mainnet safety evaluation failed: {error}")); + assert_eq!(evaluation.decision, kb_execution_safety::ExecutionSafetyDecision::Deny); + assert!(evaluation.violations.iter().any(|violation| { + return violation.code == "execution_mainnet_disabled"; + })); + let mut explicitly_enabled = plan.clone(); + explicitly_enabled.policy.cluster.allow_mainnet = true; + explicitly_enabled.policy.cluster.mainnet_confirmation = true; + let enabled_evaluation = kb_execution_safety::ExecutionSafetyChecker + .evaluate_prepared_plan(&explicitly_enabled) + .unwrap_or_else(|error| { + panic!("enabled Mainnet safety evaluation failed: {error}") + }); + assert_eq!( + enabled_evaluation.decision, + kb_execution_safety::ExecutionSafetyDecision::Allow + ); + } + } + } + + #[test] + fn rejects_missing_fee_cap_incomplete_post_validation_and_signer_overflow() { + let mut intent = intent(crate::SplMemoGeneration::V4, "policy", &[], true); + intent.policy.cost_limit.max_fee_lamports = std::option::Option::None; + let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplMemoExecutor, + &intent, + ) + .expect_err("missing fee cap must fail"); + assert_eq!(error.code(), "execution_spl_memo_fee_limit_missing"); + + intent.policy.cost_limit.max_fee_lamports = std::option::Option::Some(10_000); + intent.policy.post_execution_validation.materialization_required = false; + let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplMemoExecutor, + &intent, + ) + .expect_err("incomplete post validation must fail"); + assert_eq!(error.code(), "execution_spl_memo_post_validation_required"); + + intent.policy.post_execution_validation.materialization_required = true; + intent.operation = crate::SplMemoOperation::AddMemo { + generation: crate::SplMemoGeneration::V4, + message: std::string::String::from("bounded"), + signers: (0..=crate::MAX_MEMO_SIGNERS) + .map(|_| { + return crate::SplMemoSigner { + pubkey: pubkey(kb_program_ids::VOTE_PROGRAM_ID), + }; + }) + .collect(), + }; + let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplMemoExecutor, + &intent, + ) + .expect_err("signer overflow must fail"); + assert_eq!(error.code(), "execution_spl_memo_signer_limit_exceeded"); + } + + #[test] + fn machine_readable_matrix_matches_executor_policy() { + let parsed = serde_json::from_str::(include_str!( + "../../docs/SPL_MEMO_MATRIX.json" + )) + .unwrap_or_else(|error| panic!("Memo matrix parsing failed: {error}")); + let contract = parsed + .get("executorContract") + .unwrap_or_else(|| panic!("Memo executor contract is missing")); + assert_eq!( + contract.get("operationCode").and_then(serde_json::Value::as_str), + std::option::Option::Some(crate::SPL_MEMO_ADD_MEMO_OPERATION) + ); + assert_eq!( + contract.get("maximumMessageBytes").and_then(serde_json::Value::as_u64), + std::option::Option::Some(crate::MAX_MEMO_MESSAGE_BYTES as u64) + ); + assert_eq!( + contract.get("maximumSignerOccurrences").and_then(serde_json::Value::as_u64), + std::option::Option::Some(crate::MAX_MEMO_SIGNERS as u64) + ); + assert_eq!( + contract.get("allClustersConstructible").and_then(serde_json::Value::as_bool), + std::option::Option::Some(true) + ); + assert_eq!( + contract + .get("historicalGenerationSendEnabled") + .and_then(serde_json::Value::as_bool), + std::option::Option::Some(false) + ); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/constants.rs new file mode 100644 index 0000000..b039fa9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_executor_spl_memo/src/constants.rs +// version: 2 + +//! Local constants for the `kb_executor_spl_memo` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_spl_memo"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/executor.rs new file mode 100644 index 0000000..7f34f9f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/executor.rs @@ -0,0 +1,318 @@ +// file: kb_executor_spl_memo/src/executor.rs +// version: 4 + +//! Exact capability dispatch and typed plan construction for SPL Memo. + +/// SPL Memo executor implementation. +#[derive(Clone, Debug, Default)] +pub struct SplMemoExecutor; + +impl crate::SplMemoExecutor { + fn exact_capability( + &self, + program_id: &kb_model::ProgramId, + operation_code: &str, + ) -> kb_execution_api::ExecutionCapability { + let generation = match crate::SplMemoGeneration::from_program_id(program_id.0.as_str()) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return kb_execution_api::ExecutionCapability::unsupported( + "execution_spl_memo_program_not_owned", + format!("program {} is not owned by kb_executor_spl_memo", program_id.0), + ); + }, + }; + if operation_code != crate::SPL_MEMO_ADD_MEMO_OPERATION { + return kb_execution_api::ExecutionCapability::unsupported( + "execution_spl_memo_operation_unsupported", + format!("SPL Memo operation {operation_code} is not implemented"), + ); + } + if generation != crate::SplMemoGeneration::V4 { + return kb_execution_api::ExecutionCapability::unsupported( + "execution_spl_memo_historical_generation_decode_only", + format!( + "SPL Memo generation {generation:?} is historical decode-only; only current or experimental generations are executable" + ), + ); + } + return kb_execution_api::ExecutionCapability::supported(operation_code); + } +} + +impl kb_execution_api::TypedInstructionExecutor for crate::SplMemoExecutor { + type Intent = crate::SplMemoExecutionIntent; + + fn capability( + &self, + program_id: &kb_model::ProgramId, + operation_code: &str, + ) -> kb_execution_api::ExecutionCapability { + return self.exact_capability(program_id, operation_code); + } + + fn build_prepared_plan( + &self, + intent: &Self::Intent, + ) -> kb_core::Result { + let program_id = kb_model::ProgramId(std::string::String::from( + intent.operation.generation().program_id(), + )); + return match self.exact_capability(&program_id, intent.operation.operation_code()) { + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => { + crate::build_prepared_plan(intent) + }, + kb_execution_api::ExecutionCapability::Unsupported { reason_code, reason } => { + std::result::Result::Err(kb_core::Error::new(reason_code, reason)) + }, + }; + } +} + +impl kb_execution_api::InstructionExecutor for crate::SplMemoExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_spl_memo"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[ + kb_program_ids::SPL_MEMO_V1_PROGRAM_ID, + kb_program_ids::SPL_MEMO_V3_PROGRAM_ID, + kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, + ]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + return match self.exact_capability(&request.program_id, &request.operation_code) { + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => { + kb_execution_api::ExecutionSupport::Yes + }, + kb_execution_api::ExecutionCapability::Unsupported { reason_code: _, reason: _ } => { + kb_execution_api::ExecutionSupport::No + }, + }; + } + + fn build_plan( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + match self.exact_capability(&request.program_id, &request.operation_code) { + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => {}, + kb_execution_api::ExecutionCapability::Unsupported { reason_code, reason } => { + return std::result::Result::Err(kb_core::Error::new(reason_code, reason)); + }, + } + let intent = + match serde_json::from_str::(&request.payload_json) { + std::result::Result::Ok(intent) => intent, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_memo_intent_deserialize_failed", + error.to_string(), + )); + }, + }; + if intent.operation.operation_code() != request.operation_code.as_str() { + return std::result::Result::Err(kb_core::Error::new( + "execution_operation_code_mismatch", + format!( + "request operation {} does not match typed intent operation {}", + request.operation_code, + intent.operation.operation_code() + ), + )); + } + if intent.operation.generation().program_id() != request.program_id.0.as_str() { + return std::result::Result::Err(kb_core::Error::new( + "execution_program_id_mismatch", + format!( + "request program {} does not match typed intent program {}", + request.program_id.0, + intent.operation.generation().program_id() + ), + )); + } + let prepared = + match kb_execution_api::TypedInstructionExecutor::build_prepared_plan(self, &intent) { + std::result::Result::Ok(plan) => plan, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let payload_value = match serde_json::to_value(&prepared) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_memo_plan_serialize_failed", + error.to_string(), + )); + }, + }; + let payload_json = match kb_execution_api::serialize_payload_json(&payload_value) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_spl_memo"), + instruction_count: prepared.instructions.len(), + payload_json, + }); + } +} + +#[cfg(test)] +mod tests { + fn request( + program_id: &str, + operation_code: &str, + payload_json: std::string::String, + ) -> kb_execution_api::ExecutionRequest { + return kb_execution_api::ExecutionRequest { + program_id: kb_model::ProgramId(program_id.to_string()), + operation_code: operation_code.to_string(), + payload_json, + }; + } + + fn pubkey(value: &str) -> kb_model::Pubkey { + return kb_model::Pubkey(std::string::String::from(value)); + } + + fn intent( + generation: crate::SplMemoGeneration, + message: &str, + signers: &[&str], + dry_run: bool, + ) -> crate::SplMemoExecutionIntent { + let fee_payer = kb_program_ids::SYSTEM_PROGRAM_ID; + let mut authorized_signers = vec![pubkey(fee_payer)]; + for signer in signers { + let candidate = pubkey(signer); + if !authorized_signers.contains(&candidate) { + authorized_signers.push(candidate); + } + } + return crate::SplMemoExecutionIntent { + intent_id: std::string::String::from("memo-intent-1"), + fee_payer: pubkey(fee_payer), + policy: kb_execution_api::ExecutionPolicy { + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(0), + max_fee_lamports: std::option::Option::Some(10_000), + max_compute_unit_price_micro_lamports: std::option::Option::None, + }, + authorized_signers, + dry_run, + post_execution_validation: kb_execution_api::PostExecutionValidationPolicy { + canonical_insert_required: true, + core_extraction_required: true, + decode_replay_required: true, + materialization_required: true, + }, + ..kb_execution_api::ExecutionPolicy::default() + }, + operation: crate::SplMemoOperation::AddMemo { + generation, + message: std::string::String::from(message), + signers: signers + .iter() + .map(|value| { + return crate::SplMemoSigner { pubkey: pubkey(value) }; + }) + .collect(), + }, + }; + } + + #[test] + fn exact_capabilities_support_only_current_v4_add_memo() { + let executor = crate::SplMemoExecutor; + for program_id in + [kb_program_ids::SPL_MEMO_V1_PROGRAM_ID, kb_program_ids::SPL_MEMO_V3_PROGRAM_ID] + { + let request = request( + program_id, + crate::SPL_MEMO_ADD_MEMO_OPERATION, + std::string::String::from("{}"), + ); + assert_eq!( + kb_execution_api::InstructionExecutor::supports_request(&executor, &request), + kb_execution_api::ExecutionSupport::No + ); + } + let current = request( + kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, + crate::SPL_MEMO_ADD_MEMO_OPERATION, + std::string::String::from("{}"), + ); + assert_eq!( + kb_execution_api::InstructionExecutor::supports_request(&executor, ¤t), + kb_execution_api::ExecutionSupport::Yes + ); + let unsupported_operation = request( + kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, + "spl_memo.remove_memo", + std::string::String::from("{}"), + ); + assert_eq!( + kb_execution_api::InstructionExecutor::supports_request( + &executor, + &unsupported_operation, + ), + kb_execution_api::ExecutionSupport::No + ); + let foreign = request( + kb_program_ids::SYSTEM_PROGRAM_ID, + crate::SPL_MEMO_ADD_MEMO_OPERATION, + std::string::String::from("{}"), + ); + assert_eq!( + kb_execution_api::InstructionExecutor::supports_request(&executor, &foreign), + kb_execution_api::ExecutionSupport::No + ); + } + + #[test] + fn generic_request_roundtrips_the_typed_plan() { + let intent = intent( + crate::SplMemoGeneration::V4, + "generic", + &[kb_program_ids::VOTE_PROGRAM_ID], + false, + ); + let payload_json = serde_json::to_string(&intent) + .unwrap_or_else(|error| panic!("intent serialization failed: {error}")); + let request = request( + kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, + crate::SPL_MEMO_ADD_MEMO_OPERATION, + payload_json, + ); + let plan = + kb_execution_api::InstructionExecutor::build_plan(&crate::SplMemoExecutor, &request) + .unwrap_or_else(|error| panic!("generic plan failed: {error}")); + assert_eq!(plan.instruction_count, 1); + let prepared = + serde_json::from_str::(&plan.payload_json) + .unwrap_or_else(|error| panic!("prepared plan parsing failed: {error}")); + assert_eq!(prepared.instructions[0].data, b"generic"); + } + + #[test] + fn historical_generations_are_decode_only_with_stable_reason() { + for generation in [crate::SplMemoGeneration::V1, crate::SplMemoGeneration::V3] { + let intent = intent(generation, "historical", &[], true); + let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplMemoExecutor, + &intent, + ) + .expect_err("historical Memo generation must not build a plan"); + assert_eq!(error.code(), "execution_spl_memo_historical_generation_decode_only"); + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/intent.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/intent.rs new file mode 100644 index 0000000..684e65b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/intent.rs @@ -0,0 +1,112 @@ +// file: kb_executor_spl_memo/src/intent.rs +// version: 3 + +//! Typed SPL Memo execution intents. + +use ts_rs::TS; // rust-rules: derive-import + +/// Stable operation code for adding one SPL Memo annotation. +pub const SPL_MEMO_ADD_MEMO_OPERATION: &str = "spl_memo.add_memo"; +/// Conservative UTF-8 payload bound used before transaction assembly. +pub const MAX_MEMO_MESSAGE_BYTES: usize = 566; +/// Conservative bound on ordered signer occurrences accepted by one intent. +pub const MAX_MEMO_SIGNERS: usize = 32; + +/// Exact SPL Memo program generation. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_memo/intent/SplMemoGeneration.ts" +)] +pub enum SplMemoGeneration { + /// Historical v1 program, whose runtime ignores supplied accounts. + V1, + /// Historical v3 program, whose runtime requires every supplied account to sign. + V3, + /// Current v4 program, whose runtime requires every supplied account to sign. + V4, +} + +impl crate::SplMemoGeneration { + /// Returns the exact Program ID for this generation. + pub fn program_id(&self) -> &'static str { + return match self { + Self::V1 => kb_program_ids::SPL_MEMO_V1_PROGRAM_ID, + Self::V3 => kb_program_ids::SPL_MEMO_V3_PROGRAM_ID, + Self::V4 => kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, + }; + } + + pub(crate) fn from_program_id(program_id: &str) -> std::option::Option { + return match program_id { + kb_program_ids::SPL_MEMO_V1_PROGRAM_ID => std::option::Option::Some(Self::V1), + kb_program_ids::SPL_MEMO_V3_PROGRAM_ID => std::option::Option::Some(Self::V3), + kb_program_ids::SPL_MEMO_V4_PROGRAM_ID => std::option::Option::Some(Self::V4), + _ => std::option::Option::None, + }; + } +} + +/// One ordered signer account supplied to the Memo instruction. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_memo/intent/SplMemoSigner.ts" +)] +pub struct SplMemoSigner { + /// Signer public key; duplicates are preserved in instruction order. + pub pubkey: kb_model::Pubkey, +} + +/// Typed SPL Memo operation arguments. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(tag = "operation", rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_memo/intent/SplMemoOperation.ts" +)] +pub enum SplMemoOperation { + /// Add one exact UTF-8 Memo payload with ordered readonly signer accounts. + AddMemo { + /// Exact Memo program generation. + generation: crate::SplMemoGeneration, + /// UTF-8 text whose bytes become the complete instruction payload. + message: std::string::String, + /// Ordered signer accounts. Duplicates are preserved. + signers: std::vec::Vec, + }, +} + +impl crate::SplMemoOperation { + /// Returns the stable operation code. + pub fn operation_code(&self) -> &'static str { + return match self { + Self::AddMemo { .. } => crate::SPL_MEMO_ADD_MEMO_OPERATION, + }; + } + + /// Returns the exact requested program generation. + pub fn generation(&self) -> crate::SplMemoGeneration { + return match self { + Self::AddMemo { generation, .. } => *generation, + }; + } +} + +/// Complete typed intent accepted by the SPL Memo executor. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_memo/intent/SplMemoExecutionIntent.ts" +)] +pub struct SplMemoExecutionIntent { + /// Stable caller-provided identifier used for logs and replay correlation. + pub intent_id: std::string::String, + /// Transaction fee payer. + pub fee_payer: kb_model::Pubkey, + /// Conservative execution policy. + pub policy: kb_execution_api::ExecutionPolicy, + /// Typed Memo operation and arguments. + pub operation: crate::SplMemoOperation, +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/lib.rs new file mode 100644 index 0000000..81b5ed4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_memo/src/lib.rs @@ -0,0 +1,34 @@ +// file: kb_executor_spl_memo/src/lib.rs +// version: 6 + +//! Executor crate for `spl_memo`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod builder; +mod constants; +mod executor; +mod intent; + +/// Crate-root access to `build_prepared_plan` from `builder`. +pub(crate) use crate::builder::build_prepared_plan; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the SPL Memo executor type implemented by this crate. +pub use crate::executor::SplMemoExecutor; +/// Maximum UTF-8 memo payload accepted by this executor. +pub use crate::intent::MAX_MEMO_MESSAGE_BYTES; +/// Maximum ordered signer occurrences accepted by this executor. +pub use crate::intent::MAX_MEMO_SIGNERS; +/// Stable operation code for adding one SPL Memo annotation. +pub use crate::intent::SPL_MEMO_ADD_MEMO_OPERATION; +/// Exposes the typed SPL Memo execution intent. +pub use crate::intent::SplMemoExecutionIntent; +/// Exposes the exact SPL Memo program generation. +pub use crate::intent::SplMemoGeneration; +/// Exposes the typed SPL Memo operation. +pub use crate::intent::SplMemoOperation; +/// Exposes one ordered SPL Memo signer declaration. +pub use crate::intent::SplMemoSigner; diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_noop/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_spl_noop/Cargo.toml new file mode 100644 index 0000000..64ef5fb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_noop/Cargo.toml @@ -0,0 +1,20 @@ +# file: kb_executor_spl_noop/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_spl_noop" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_noop/README.md b/migration/khadhroony-bot2-reference/kb_executor_spl_noop/README.md new file mode 100644 index 0000000..2d6f91a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_noop/README.md @@ -0,0 +1,14 @@ + + + +# kb_executor_spl_noop + +Ce crate réserve l’exécuteur pour le programme SPL Noop. + +## Rôle + +Il construira des instructions ou plans d’exécution indépendamment du décodeur correspondant. Le scaffold actuel ne signe, ne simule et n’envoie aucune transaction. + +## Statut + +L’exécuteur reconnaît uniquement ses program IDs vérifiés et retourne un plan réservé contenant zéro instruction. Les builders, contrôles de comptes, simulations et garde-fous seront ajoutés dans la version SPL dédiée du `ROADMAP.md`. diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_noop/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_noop/src/constants.rs new file mode 100644 index 0000000..847acf4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_noop/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_executor_spl_noop/src/constants.rs +// version: 2 + +//! Local constants for the `kb_executor_spl_noop` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_spl_noop"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_noop/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_noop/src/executor.rs new file mode 100644 index 0000000..a9ef77c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_noop/src/executor.rs @@ -0,0 +1,92 @@ +// file: kb_executor_spl_noop/src/executor.rs +// version: 5 + +//! Reserved executor for the `spl_noop` surface. + +/// Reserved executor implementation for the verified program surface. +#[derive(Clone, Debug, Default)] +pub struct SplNoopExecutor; + +impl kb_execution_api::InstructionExecutor for crate::SplNoopExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_spl_noop"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::SPL_NOOP_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + tracing::debug!( + target: crate::TRACING_TARGET, + action = "build_reserved_execution_plan", + executor_name = "kb_executor_spl_noop", + program_id = %request.program_id.0.as_str(), + operation_code = %request.operation_code, + instruction_count = 0_usize, + "build reserved SPL execution plan" + ); + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_spl_noop"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor","surface":"spl_noop"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} + +#[cfg(test)] +mod tests { + fn request(program_id: &str) -> kb_execution_api::ExecutionRequest { + return kb_execution_api::ExecutionRequest { + program_id: kb_model::ProgramId(program_id.to_string()), + operation_code: "reserved".to_string(), + payload_json: "{}".to_string(), + }; + } + + #[test] + fn exposes_only_registered_program_ids() { + let executor = crate::SplNoopExecutor; + let program_ids = kb_execution_api::InstructionExecutor::program_ids(&executor); + assert_eq!(program_ids.len(), 1); + for program_id in program_ids { + assert!(kb_program_ids::find_registered_program_id(program_id).is_some()); + } + } + + #[test] + fn reserved_plan_never_contains_instructions() { + let executor = crate::SplNoopExecutor; + let program_id = kb_execution_api::InstructionExecutor::program_ids(&executor)[0]; + let result = + kb_execution_api::InstructionExecutor::build_plan(&executor, &request(program_id)); + let plan = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("reserved plan failed: {error}"), + }; + assert_eq!(plan.instruction_count, 0); + assert!(plan.payload_json.contains("reserved_executor")); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_noop/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_noop/src/lib.rs new file mode 100644 index 0000000..4cbd69c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_noop/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_executor_spl_noop/src/lib.rs +// version: 3 + +//! Executor crate for `spl_noop`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the reserved executor type implemented by this crate. +pub use crate::executor::SplNoopExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/Cargo.toml new file mode 100644 index 0000000..496d3c5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/Cargo.toml @@ -0,0 +1,20 @@ +# file: kb_executor_spl_single_pool/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_spl_single_pool" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/README.md b/migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/README.md new file mode 100644 index 0000000..bd22fb1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/README.md @@ -0,0 +1,14 @@ + + + +# kb_executor_spl_single_pool + +Ce crate réserve l’exécuteur pour le programme SPL Single Pool. + +## Rôle + +Il construira des instructions ou plans d’exécution indépendamment du décodeur correspondant. Le scaffold actuel ne signe, ne simule et n’envoie aucune transaction. + +## Statut + +L’exécuteur reconnaît uniquement ses program IDs vérifiés et retourne un plan réservé contenant zéro instruction. Les builders, contrôles de comptes, simulations et garde-fous seront ajoutés dans la version SPL dédiée du `ROADMAP.md`. diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/src/constants.rs new file mode 100644 index 0000000..44e3731 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_executor_spl_single_pool/src/constants.rs +// version: 2 + +//! Local constants for the `kb_executor_spl_single_pool` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_spl_single_pool"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/src/executor.rs new file mode 100644 index 0000000..4dd1556 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/src/executor.rs @@ -0,0 +1,92 @@ +// file: kb_executor_spl_single_pool/src/executor.rs +// version: 5 + +//! Reserved executor for the `spl_single_pool` surface. + +/// Reserved executor implementation for the verified program surface. +#[derive(Clone, Debug, Default)] +pub struct SplSinglePoolExecutor; + +impl kb_execution_api::InstructionExecutor for crate::SplSinglePoolExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_spl_single_pool"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::SPL_SINGLE_POOL_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + tracing::debug!( + target: crate::TRACING_TARGET, + action = "build_reserved_execution_plan", + executor_name = "kb_executor_spl_single_pool", + program_id = %request.program_id.0.as_str(), + operation_code = %request.operation_code, + instruction_count = 0_usize, + "build reserved SPL execution plan" + ); + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_spl_single_pool"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor","surface":"spl_single_pool"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} + +#[cfg(test)] +mod tests { + fn request(program_id: &str) -> kb_execution_api::ExecutionRequest { + return kb_execution_api::ExecutionRequest { + program_id: kb_model::ProgramId(program_id.to_string()), + operation_code: "reserved".to_string(), + payload_json: "{}".to_string(), + }; + } + + #[test] + fn exposes_only_registered_program_ids() { + let executor = crate::SplSinglePoolExecutor; + let program_ids = kb_execution_api::InstructionExecutor::program_ids(&executor); + assert_eq!(program_ids.len(), 1); + for program_id in program_ids { + assert!(kb_program_ids::find_registered_program_id(program_id).is_some()); + } + } + + #[test] + fn reserved_plan_never_contains_instructions() { + let executor = crate::SplSinglePoolExecutor; + let program_id = kb_execution_api::InstructionExecutor::program_ids(&executor)[0]; + let result = + kb_execution_api::InstructionExecutor::build_plan(&executor, &request(program_id)); + let plan = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("reserved plan failed: {error}"), + }; + assert_eq!(plan.instruction_count, 0); + assert!(plan.payload_json.contains("reserved_executor")); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/src/lib.rs new file mode 100644 index 0000000..2a79c98 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_single_pool/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_executor_spl_single_pool/src/lib.rs +// version: 3 + +//! Executor crate for `spl_single_pool`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the reserved executor type implemented by this crate. +pub use crate::executor::SplSinglePoolExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/Cargo.toml new file mode 100644 index 0000000..2612768 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_spl_stake_pool/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_spl_stake_pool" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/README.md b/migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/README.md new file mode 100644 index 0000000..b66d33b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_spl_stake_pool + +Ce crate réserve l'exécuteur de la surface `spl_stake_pool`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/src/constants.rs new file mode 100644 index 0000000..8211cdd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_executor_spl_stake_pool/src/constants.rs +// version: 2 + +//! Local constants for the `kb_executor_spl_stake_pool` crate. Program identifiers live in `kb_program_ids`. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_spl_stake_pool"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/src/executor.rs new file mode 100644 index 0000000..861b110 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_spl_stake_pool/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_spl_stake_pool` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct SplStakePoolExecutor; + +impl kb_execution_api::InstructionExecutor for crate::SplStakePoolExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_spl_stake_pool"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::SPL_STAKE_POOL_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_spl_stake_pool"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/src/lib.rs new file mode 100644 index 0000000..8944b40 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_stake_pool/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_executor_spl_stake_pool/src/lib.rs +// version: 2 + +//! Executor crate for `spl_stake_pool`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::SplStakePoolExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_token/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_spl_token/Cargo.toml new file mode 100644 index 0000000..2f38311 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_token/Cargo.toml @@ -0,0 +1,28 @@ +# file: kb_executor_spl_token/Cargo.toml +# version: 3 + +[package] +name = "kb_executor_spl_token" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde.workspace = true +serde_json.workspace = true +solana-instruction.workspace = true +solana-pubkey.workspace = true +spl-token-interface.workspace = true +tracing.workspace = true +ts-rs.workspace = true + +[dev-dependencies] +kb_execution_safety = { path = "../kb_execution_safety" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_token/README.md b/migration/khadhroony-bot2-reference/kb_executor_spl_token/README.md new file mode 100644 index 0000000..2585d05 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_token/README.md @@ -0,0 +1,98 @@ + + + +# kb_executor_spl_token + +Ce crate construit des plans typés pour le programme SPL Token classique exact : + +```text +TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA +``` + +Il ne dépend ni du RPC, ni du wallet, ni de Tauri, ni du décodeur. La lecture d’état, la simulation, +la signature, l’envoi et la validation post-exécution appartiennent à l’orchestration commune. + +## Politique de surface + +Le décodeur conserve les 28 tags publiés, y compris les formes historiques. L’exécuteur expose 24 +opérations encore courantes ou récentes et refuse quatre initialisations obsolètes dépendantes de +Rent : `InitializeMint`, `InitializeAccount`, `InitializeMultisig` et `InitializeAccount2`. Leurs +équivalents actuels utilisent respectivement `InitializeMint2`, `InitializeAccount3` et +`InitializeMultisig2`. + +Les opérations non checked `Transfer`, `Approve`, `MintTo` et `Burn` restent constructibles : leur +wire est ancien, mais les builders officiels 3.0.0 les publient toujours et elles ne sont pas +obsolètes. Les variantes checked doivent être préférées lorsqu’un mint et ses decimals sont connus. + +## Contrat typé + +`SplTokenExecutionIntent` contient : + +- un identifiant d’intent et un fee payer ; +- une `ExecutionPolicy` commune, simulation obligatoire et dry-run par défaut ; +- une instruction typée ou un `Batch` de sous-instructions non batch ; +- les comptes métier explicites ; +- une autorité simple ou un multisig avec signataires ordonnés ; +- les montants bruts sous forme de chaînes décimales JSON, converties en `u64` seulement dans le + builder ; +- les decimals séparés lorsqu’ils appartiennent réellement au wire. + +Les doublons des metas multisig sont conservés dans l’ordre officiel. La liste des signataires +transactionnels est dédupliquée séparément avec le fee payer. + +## Builders + +Chaque opération appelle directement le builder `spl-token-interface 3.0.0`. Cela couvre les +initialisations actuelles, transferts, délégations, changements d’autorité, mint/burn, close, +freeze/thaw, variantes checked, `SyncNative`, les trois instructions de return data, +`InitializeImmutableOwner`, `WithdrawExcessLamports`, `UnwrapLamports` et `Batch`. + +`InitializeImmutableOwner` est explicitement un no-op de compatibilité sur le programme classique ; +il n’est jamais présenté comme une extension Token-2022. + +`Batch` est borné à 64 enfants, 512 occurrences de comptes et 255 octets de data par enfant. Son +type enfant exclut structurellement un autre `Batch`. Les slices de comptes et l’ordre produits par +le builder officiel sont conservés. + +## Constructibilité et clusters + +La constructibilité de bibliothèque ne dépend pas du cluster. `UnwrapLamports` et `Batch` sont +publiés avec un builder exact dans l’interface 3.0.0 et sont donc constructibles. Deux simulations +réussies le 16 juillet 2026 prouvent leur présence sur le programme classique Devnet. Cette preuve +n'est pas extrapolée à Localnet, Testnet ou Mainnet. Mainnet demeure gouverné par +`kb_execution_safety` et la confirmation opérateur, pas par une interdiction interne à ce crate. + +Les opérations d’initialisation supposent que les comptes ont déjà été créés avec la bonne taille, +le bon owner et le bon financement. La création System et le rent ne sont pas masqués dans une +instruction Token ; le préflight stateful de la tranche suivante les vérifiera sans utiliser ATA. + +## Coûts et validation + +Les montants token ne sont pas des frais réseau et ne sont pas reportés comme lamports dépensés. +Un montant explicite `UnwrapLamports` est déclaré dans `requested_spend_lamports`; un unwrap total +et `WithdrawExcessLamports` restent simulation-only tant qu’une lecture d’état n’a pas borné les +lamports déplaçables. Le plafond de frais reste séparé dans `ExecutionCostLimit`. + +Toute construction impose une simulation, un plafond de frais positif, une insertion canonique, +l’extraction core et le replay décodé. Les mutations exigent aussi la validation de matérialisation ; +les conversions, requêtes de return data et le no-op de compatibilité n’inventent aucune projection. + +## Limites actuelles + +- aucun préflight Mint/Account/Multisig dans ce crate : ces lectures sont implémentées dans + `kb_pipeline` afin de conserver l'exécuteur pur ; +- aucun envoi ni simulation RPC dans ce crate ; l'orchestrateur commun a validé ces étapes sur + Devnet pour `TransferChecked` et le lifecycle contrôlé ; +- aucun compte ATA créé implicitement ; +- aucun support Token-2022 ; +- aucun déploiement de `UnwrapLamports` ou `Batch` revendiqué hors Devnet sans simulation cluster + dédiée. + +Le parcours Devnet réel a prouvé simulation, soumission, confirmation, hydratation canonique, +extraction, replay, matérialisation et idempotence pour les opérations du lifecycle documentées dans +`docs/SPL_TOKEN_MATRIX.json`. La démo Tauri refuse volontairement un envoi si le wallet du profil ne +résout pas aussi l'autorité Token requise ; elle ne prend jamais en charge une clé externe implicite. + +Le probe Devnet simulation-only du 16 juillet 2026 a validé le wire déployé de `Batch` avec un +enfant `TransferChecked` de montant nul en 270 compute units, puis `UnwrapLamports` d'un lamport en +140 compute units. Aucune transaction n'a été signée ou envoyée par ce probe. diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_token/src/builder.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_token/src/builder.rs new file mode 100644 index 0000000..ffac3bb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_token/src/builder.rs @@ -0,0 +1,1179 @@ +// file: kb_executor_spl_token/src/builder.rs +// version: 5 + +//! Official classic SPL Token builders and conservative plan validation. + +use std::str::FromStr; // rust-rules: trait-import + +struct ParsedAuthority { + authority: solana_pubkey::Pubkey, + signers: std::vec::Vec, +} + +pub(crate) fn build_prepared_plan( + intent: &crate::SplTokenExecutionIntent, +) -> kb_core::Result { + if intent.intent_id.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_intent_id_empty", + "SPL Token execution intent id must not be empty", + )); + } + let fee_payer = match parse_pubkey(&intent.fee_payer, "fee_payer") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + match validate_policy(intent) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let program_id = match solana_pubkey::Pubkey::from_str(kb_program_ids::SPL_TOKEN_PROGRAM_ID) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_program_id_invalid", + error.to_string(), + )); + }, + }; + let instruction = match &intent.operation { + crate::SplTokenOperation::Instruction { value } => match build_single(&program_id, value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + crate::SplTokenOperation::Batch { instructions } => { + match build_batch(&program_id, instructions.as_slice()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + }, + }; + let requested_spend_lamports = match declared_spend_lamports(&intent.operation) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let planned_instruction = planned_instruction(intent.operation.operation_code(), &instruction); + let required_signers = required_signers(&intent.fee_payer, &instruction.accounts); + tracing::debug!( + target: crate::TRACING_TARGET, + action = "build_prepared_plan", + intent_id = %intent.intent_id, + operation_code = intent.operation.operation_code(), + program_id = kb_program_ids::SPL_TOKEN_PROGRAM_ID, + account_occurrence_count = instruction.accounts.len(), + required_signer_count = required_signers.len(), + requested_spend_lamports, + dry_run = intent.policy.dry_run, + "built classic SPL Token execution plan" + ); + return std::result::Result::Ok(kb_execution_api::PreparedExecutionPlan { + executor_name: std::string::String::from("kb_executor_spl_token"), + executor_version: std::string::String::from(env!("CARGO_PKG_VERSION")), + intent_id: intent.intent_id.clone(), + operation_code: std::string::String::from(intent.operation.operation_code()), + fee_payer: kb_model::Pubkey(fee_payer.to_string()), + instructions: vec![planned_instruction], + required_signers, + policy: intent.policy.clone(), + requested_spend_lamports, + requested_compute_unit_price_micro_lamports: std::option::Option::None, + }); +} + +fn validate_policy(intent: &crate::SplTokenExecutionIntent) -> kb_core::Result<()> { + if intent.policy.simulation != kb_execution_api::ExecutionSimulationPolicy::Required { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_simulation_required", + "SPL Token execution requires simulation before signing or sending", + )); + } + match intent.policy.cost_limit.max_fee_lamports { + std::option::Option::Some(limit) if limit > 0 => {}, + std::option::Option::Some(_) | std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_fee_limit_missing", + "SPL Token execution requires a positive transaction fee ceiling", + )); + }, + } + let validation = &intent.policy.post_execution_validation; + if !validation.canonical_insert_required + || !validation.core_extraction_required + || !validation.decode_replay_required + { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_post_validation_required", + "SPL Token execution requires canonical insertion, core extraction and decode replay validation", + )); + } + if intent.operation.requires_materialization() && !validation.materialization_required { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_materialization_validation_required", + "Mutating SPL Token execution requires materialization validation", + )); + } + if has_unbounded_lamport_movement(&intent.operation) && !intent.policy.dry_run { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_stateful_lamport_preflight_required", + "WithdrawExcessLamports and full-balance UnwrapLamports remain simulation-only until a stateful preflight bounds the movable lamports", + )); + } + return std::result::Result::Ok(()); +} + +macro_rules! parse_key { + ($value:expr, $field:expr) => { + match parse_pubkey($value, $field) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + }; +} + +macro_rules! parse_raw_amount { + ($value:expr, $field:expr) => { + match parse_amount($value, $field) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + }; +} + +macro_rules! parse_token_authority { + ($value:expr) => { + match parse_authority($value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + }; +} + +fn build_single( + program_id: &solana_pubkey::Pubkey, + operation: &crate::SplTokenSingleOperation, +) -> kb_core::Result { + return match operation { + crate::SplTokenSingleOperation::InitializeMint { + mint, + mint_authority, + freeze_authority, + decimals, + } => { + let mint = parse_key!(mint, "mint"); + let mint_authority = parse_key!(mint_authority, "mint_authority"); + let freeze_authority = match freeze_authority { + std::option::Option::Some(value) => { + std::option::Option::Some(parse_key!(value, "freeze_authority")) + }, + std::option::Option::None => std::option::Option::None, + }; + official_instruction(spl_token_interface::instruction::initialize_mint2( + program_id, + &mint, + &mint_authority, + freeze_authority.as_ref(), + *decimals, + )) + }, + crate::SplTokenSingleOperation::InitializeAccount { account, mint, owner } => { + let account = parse_key!(account, "account"); + let mint = parse_key!(mint, "mint"); + let owner = parse_key!(owner, "owner"); + official_instruction(spl_token_interface::instruction::initialize_account3( + program_id, &account, &mint, &owner, + )) + }, + crate::SplTokenSingleOperation::InitializeMultisig { multisig, members, threshold } => { + if members.is_empty() + || members.len() > crate::MAX_MULTISIG_SIGNERS + || *threshold == 0 + || usize::from(*threshold) > members.len() + { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_multisig_threshold_invalid", + format!( + "SPL Token multisig requires 1..={} members and threshold 1..=N; received M={} N={}", + crate::MAX_MULTISIG_SIGNERS, + threshold, + members.len() + ), + )); + } + let multisig = parse_key!(multisig, "multisig"); + let mut parsed_members = std::vec::Vec::with_capacity(members.len()); + for member in members { + parsed_members.push(parse_key!(member, "multisig_member")); + } + let member_refs = parsed_members.iter().collect::>(); + official_instruction(spl_token_interface::instruction::initialize_multisig2( + program_id, + &multisig, + member_refs.as_slice(), + *threshold, + )) + }, + crate::SplTokenSingleOperation::Transfer { source, destination, authority, amount } => { + let source = parse_key!(source, "source"); + let destination = parse_key!(destination, "destination"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction(spl_token_interface::instruction::transfer( + program_id, + &source, + &destination, + &authority.authority, + signer_refs.as_slice(), + amount, + )) + }, + crate::SplTokenSingleOperation::Approve { source, delegate, authority, amount } => { + let source = parse_key!(source, "source"); + let delegate = parse_key!(delegate, "delegate"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction(spl_token_interface::instruction::approve( + program_id, + &source, + &delegate, + &authority.authority, + signer_refs.as_slice(), + amount, + )) + }, + crate::SplTokenSingleOperation::Revoke { source, authority } => { + let source = parse_key!(source, "source"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction(spl_token_interface::instruction::revoke( + program_id, + &source, + &authority.authority, + signer_refs.as_slice(), + )) + }, + crate::SplTokenSingleOperation::SetAuthority { + owned, + authority_type, + new_authority, + current_authority, + } => { + let owned = parse_key!(owned, "owned"); + let new_authority = match new_authority { + std::option::Option::Some(value) => { + std::option::Option::Some(parse_key!(value, "new_authority")) + }, + std::option::Option::None => std::option::Option::None, + }; + let current_authority = parse_token_authority!(current_authority); + let signer_refs = current_authority.signers.iter().collect::>(); + let authority_type = match authority_type { + crate::SplTokenAuthorityType::MintTokens => { + spl_token_interface::instruction::AuthorityType::MintTokens + }, + crate::SplTokenAuthorityType::FreezeAccount => { + spl_token_interface::instruction::AuthorityType::FreezeAccount + }, + crate::SplTokenAuthorityType::AccountOwner => { + spl_token_interface::instruction::AuthorityType::AccountOwner + }, + crate::SplTokenAuthorityType::CloseAccount => { + spl_token_interface::instruction::AuthorityType::CloseAccount + }, + }; + official_instruction(spl_token_interface::instruction::set_authority( + program_id, + &owned, + new_authority.as_ref(), + authority_type, + ¤t_authority.authority, + signer_refs.as_slice(), + )) + }, + crate::SplTokenSingleOperation::MintTo { mint, destination, authority, amount } => { + let mint = parse_key!(mint, "mint"); + let destination = parse_key!(destination, "destination"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction(spl_token_interface::instruction::mint_to( + program_id, + &mint, + &destination, + &authority.authority, + signer_refs.as_slice(), + amount, + )) + }, + crate::SplTokenSingleOperation::Burn { source, mint, authority, amount } => { + let source = parse_key!(source, "source"); + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction(spl_token_interface::instruction::burn( + program_id, + &source, + &mint, + &authority.authority, + signer_refs.as_slice(), + amount, + )) + }, + crate::SplTokenSingleOperation::CloseAccount { account, destination, authority } => { + let account = parse_key!(account, "account"); + let destination = parse_key!(destination, "destination"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction(spl_token_interface::instruction::close_account( + program_id, + &account, + &destination, + &authority.authority, + signer_refs.as_slice(), + )) + }, + crate::SplTokenSingleOperation::FreezeAccount { account, mint, authority } => { + let account = parse_key!(account, "account"); + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction(spl_token_interface::instruction::freeze_account( + program_id, + &account, + &mint, + &authority.authority, + signer_refs.as_slice(), + )) + }, + crate::SplTokenSingleOperation::ThawAccount { account, mint, authority } => { + let account = parse_key!(account, "account"); + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction(spl_token_interface::instruction::thaw_account( + program_id, + &account, + &mint, + &authority.authority, + signer_refs.as_slice(), + )) + }, + crate::SplTokenSingleOperation::TransferChecked { + source, + mint, + destination, + authority, + amount, + decimals, + } => { + let source = parse_key!(source, "source"); + let mint = parse_key!(mint, "mint"); + let destination = parse_key!(destination, "destination"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction(spl_token_interface::instruction::transfer_checked( + program_id, + &source, + &mint, + &destination, + &authority.authority, + signer_refs.as_slice(), + amount, + *decimals, + )) + }, + crate::SplTokenSingleOperation::ApproveChecked { + source, + mint, + delegate, + authority, + amount, + decimals, + } => { + let source = parse_key!(source, "source"); + let mint = parse_key!(mint, "mint"); + let delegate = parse_key!(delegate, "delegate"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction(spl_token_interface::instruction::approve_checked( + program_id, + &source, + &mint, + &delegate, + &authority.authority, + signer_refs.as_slice(), + amount, + *decimals, + )) + }, + crate::SplTokenSingleOperation::MintToChecked { + mint, + destination, + authority, + amount, + decimals, + } => { + let mint = parse_key!(mint, "mint"); + let destination = parse_key!(destination, "destination"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction(spl_token_interface::instruction::mint_to_checked( + program_id, + &mint, + &destination, + &authority.authority, + signer_refs.as_slice(), + amount, + *decimals, + )) + }, + crate::SplTokenSingleOperation::BurnChecked { + source, + mint, + authority, + amount, + decimals, + } => { + let source = parse_key!(source, "source"); + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction(spl_token_interface::instruction::burn_checked( + program_id, + &source, + &mint, + &authority.authority, + signer_refs.as_slice(), + amount, + *decimals, + )) + }, + crate::SplTokenSingleOperation::SyncNative { account, include_rent_sysvar } => { + let account = parse_key!(account, "account"); + if *include_rent_sysvar { + official_instruction( + spl_token_interface::instruction::sync_native_with_rent_sysvar( + program_id, &account, + ), + ) + } else { + official_instruction(spl_token_interface::instruction::sync_native( + program_id, &account, + )) + } + }, + crate::SplTokenSingleOperation::GetAccountDataSize { mint } => { + let mint = parse_key!(mint, "mint"); + official_instruction(spl_token_interface::instruction::get_account_data_size( + program_id, &mint, + )) + }, + crate::SplTokenSingleOperation::InitializeImmutableOwner { account } => { + let account = parse_key!(account, "account"); + official_instruction(spl_token_interface::instruction::initialize_immutable_owner( + program_id, &account, + )) + }, + crate::SplTokenSingleOperation::AmountToUiAmount { mint, amount } => { + let mint = parse_key!(mint, "mint"); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction(spl_token_interface::instruction::amount_to_ui_amount( + program_id, &mint, amount, + )) + }, + crate::SplTokenSingleOperation::UiAmountToAmount { mint, ui_amount } => { + if ui_amount.len() > crate::MAX_UI_AMOUNT_BYTES { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_ui_amount_too_large", + format!( + "SPL Token UI amount length {} exceeds {} bytes", + ui_amount.len(), + crate::MAX_UI_AMOUNT_BYTES + ), + )); + } + let mint = parse_key!(mint, "mint"); + official_instruction(spl_token_interface::instruction::ui_amount_to_amount( + program_id, + &mint, + ui_amount.as_str(), + )) + }, + crate::SplTokenSingleOperation::WithdrawExcessLamports { + account, + destination, + authority, + } => { + let account = parse_key!(account, "account"); + let destination = parse_key!(destination, "destination"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction(spl_token_interface::instruction::withdraw_excess_lamports( + program_id, + &account, + &destination, + &authority.authority, + signer_refs.as_slice(), + )) + }, + crate::SplTokenSingleOperation::UnwrapLamports { + account, + destination, + authority, + amount_lamports, + } => { + let account = parse_key!(account, "account"); + let destination = parse_key!(destination, "destination"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount_lamports = match amount_lamports { + std::option::Option::Some(value) => { + std::option::Option::Some(parse_raw_amount!(value, "amount_lamports")) + }, + std::option::Option::None => std::option::Option::None, + }; + official_instruction(spl_token_interface::instruction::unwrap_lamports( + program_id, + &account, + &destination, + &authority.authority, + signer_refs.as_slice(), + amount_lamports, + )) + }, + }; +} + +fn build_batch( + program_id: &solana_pubkey::Pubkey, + operations: &[crate::SplTokenSingleOperation], +) -> kb_core::Result { + if operations.len() > crate::MAX_BATCH_INSTRUCTIONS { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_batch_instruction_limit_exceeded", + format!( + "SPL Token Batch child count {} exceeds {}", + operations.len(), + crate::MAX_BATCH_INSTRUCTIONS + ), + )); + } + let mut instructions = std::vec::Vec::with_capacity(operations.len()); + let mut account_count = 0_usize; + for operation in operations { + let instruction = match build_single(program_id, operation) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if instruction.data.len() > usize::from(u8::MAX) { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_batch_child_data_too_large", + format!( + "SPL Token Batch child {} has {} data bytes; maximum is {}", + instructions.len(), + instruction.data.len(), + u8::MAX + ), + )); + } + account_count = match account_count.checked_add(instruction.accounts.len()) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_batch_account_count_overflow", + "SPL Token Batch aggregate account count overflowed usize", + )); + }, + }; + if account_count > crate::MAX_BATCH_ACCOUNTS { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_batch_account_limit_exceeded", + format!( + "SPL Token Batch account occurrence count {account_count} exceeds {}", + crate::MAX_BATCH_ACCOUNTS + ), + )); + } + instructions.push(instruction); + } + return official_instruction(spl_token_interface::instruction::batch( + program_id, + instructions.as_slice(), + )); +} + +fn official_instruction( + result: std::result::Result, +) -> kb_core::Result +where + BuilderError: std::fmt::Display, +{ + return match result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_official_builder_failed", + error.to_string(), + )), + }; +} + +fn parse_pubkey(pubkey: &kb_model::Pubkey, field: &str) -> kb_core::Result { + if pubkey.0.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_pubkey_empty", + format!("SPL Token {field} public key must not be empty"), + )); + } + return match solana_pubkey::Pubkey::from_str(pubkey.0.as_str()) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_pubkey_invalid", + format!("invalid SPL Token {field} public key: {error}"), + )), + }; +} + +fn parse_amount(amount: &crate::SplTokenAmount, field: &str) -> kb_core::Result { + if amount.0.is_empty() || !amount.0.bytes().all(|value| return value.is_ascii_digit()) { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_amount_invalid", + format!("SPL Token {field} must be an unsigned decimal u64 string"), + )); + } + return match amount.0.parse::() { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_amount_invalid", + format!("invalid SPL Token {field}: {error}"), + )), + }; +} + +fn parse_authority(authority: &crate::SplTokenAuthority) -> kb_core::Result { + if authority.multisig_signers.len() > crate::MAX_MULTISIG_SIGNERS { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_multisig_signer_limit_exceeded", + format!( + "SPL Token multisig signer occurrence count {} exceeds {}", + authority.multisig_signers.len(), + crate::MAX_MULTISIG_SIGNERS + ), + )); + } + let parsed_authority = match parse_pubkey(&authority.authority, "authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut signers = std::vec::Vec::with_capacity(authority.multisig_signers.len()); + for signer in &authority.multisig_signers { + let signer = match parse_pubkey(signer, "multisig_signer") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + signers.push(signer); + } + return std::result::Result::Ok(ParsedAuthority { authority: parsed_authority, signers }); +} + +fn declared_spend_lamports(operation: &crate::SplTokenOperation) -> kb_core::Result { + return match operation { + crate::SplTokenOperation::Instruction { value } => single_spend_lamports(value), + crate::SplTokenOperation::Batch { instructions } => { + let mut total = 0_u64; + for instruction in instructions { + let amount = match single_spend_lamports(instruction) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + total = match total.checked_add(amount) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lamport_spend_overflow", + "SPL Token Batch declared lamport spend exceeds u64", + )); + }, + }; + } + std::result::Result::Ok(total) + }, + }; +} + +fn single_spend_lamports(operation: &crate::SplTokenSingleOperation) -> kb_core::Result { + return match operation { + crate::SplTokenSingleOperation::UnwrapLamports { + amount_lamports: std::option::Option::Some(value), + .. + } => parse_amount(value, "amount_lamports"), + _ => std::result::Result::Ok(0), + }; +} + +fn has_unbounded_lamport_movement(operation: &crate::SplTokenOperation) -> bool { + return match operation { + crate::SplTokenOperation::Instruction { value } => { + single_has_unbounded_lamport_movement(value) + }, + crate::SplTokenOperation::Batch { instructions } => { + instructions.iter().any(single_has_unbounded_lamport_movement) + }, + }; +} + +fn single_has_unbounded_lamport_movement(operation: &crate::SplTokenSingleOperation) -> bool { + return matches!( + operation, + crate::SplTokenSingleOperation::WithdrawExcessLamports { .. } + | crate::SplTokenSingleOperation::UnwrapLamports { + amount_lamports: std::option::Option::None, + .. + } + ); +} + +fn planned_instruction( + operation_code: &str, + instruction: &solana_instruction::Instruction, +) -> kb_execution_api::PlannedInstruction { + return kb_execution_api::PlannedInstruction { + program_id: kb_model::ProgramId(instruction.program_id.to_string()), + operation_code: std::string::String::from(operation_code), + accounts: instruction + .accounts + .iter() + .map(|account| { + return kb_execution_api::PlannedAccount { + pubkey: kb_model::Pubkey(account.pubkey.to_string()), + is_signer: account.is_signer, + is_writable: account.is_writable, + }; + }) + .collect(), + data: instruction.data.clone(), + }; +} + +fn required_signers( + fee_payer: &kb_model::Pubkey, + accounts: &[solana_instruction::AccountMeta], +) -> std::vec::Vec { + let mut required = vec![kb_execution_api::RequiredSigner { + pubkey: fee_payer.clone(), + role: std::string::String::from("fee_payer"), + }]; + for account in accounts { + if !account.is_signer { + continue; + } + let pubkey = kb_model::Pubkey(account.pubkey.to_string()); + if required.iter().any(|value| return value.pubkey == pubkey) { + continue; + } + required.push(kb_execution_api::RequiredSigner { + pubkey, + role: std::string::String::from("token_authority_or_multisig_signer"), + }); + } + return required; +} + +#[cfg(test)] +pub(crate) mod tests { + fn pubkey(value: &str) -> kb_model::Pubkey { + return kb_model::Pubkey(std::string::String::from(value)); + } + + fn policy( + signers: &[&str], + materialization_required: bool, + ) -> kb_execution_api::ExecutionPolicy { + return kb_execution_api::ExecutionPolicy { + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(u64::MAX), + max_fee_lamports: std::option::Option::Some(10_000), + max_compute_unit_price_micro_lamports: std::option::Option::None, + }, + authorized_signers: signers.iter().map(|value| return pubkey(value)).collect(), + post_execution_validation: kb_execution_api::PostExecutionValidationPolicy { + canonical_insert_required: true, + core_extraction_required: true, + decode_replay_required: true, + materialization_required, + }, + ..kb_execution_api::ExecutionPolicy::default() + }; + } + + pub(crate) fn intent(operation: crate::SplTokenOperation) -> crate::SplTokenExecutionIntent { + let fee_payer = kb_program_ids::SYSTEM_PROGRAM_ID; + return crate::SplTokenExecutionIntent { + intent_id: std::string::String::from("token-intent-1"), + fee_payer: pubkey(fee_payer), + policy: policy( + &[fee_payer, kb_program_ids::VOTE_PROGRAM_ID, kb_program_ids::STAKE_PROGRAM_ID], + operation.requires_materialization(), + ), + operation, + }; + } + + fn authority(signers: &[&str]) -> crate::SplTokenAuthority { + return crate::SplTokenAuthority { + authority: pubkey(kb_program_ids::CONFIG_PROGRAM_ID), + multisig_signers: signers.iter().map(|value| return pubkey(value)).collect(), + }; + } + + fn transfer_checked() -> crate::SplTokenSingleOperation { + return crate::SplTokenSingleOperation::TransferChecked { + source: pubkey(kb_program_ids::SYSTEM_PROGRAM_ID), + mint: pubkey(kb_program_ids::CONFIG_PROGRAM_ID), + destination: pubkey(kb_program_ids::STAKE_PROGRAM_ID), + authority: authority(&[kb_program_ids::VOTE_PROGRAM_ID]), + amount: crate::SplTokenAmount(std::string::String::from("18446744073709551615")), + decimals: 9, + }; + } + + fn all_single_operations() -> std::vec::Vec<(u8, crate::SplTokenSingleOperation)> { + let first = pubkey(kb_program_ids::SYSTEM_PROGRAM_ID); + let second = pubkey(kb_program_ids::CONFIG_PROGRAM_ID); + let third = pubkey(kb_program_ids::STAKE_PROGRAM_ID); + let authority = authority(&[]); + let amount = crate::SplTokenAmount(std::string::String::from("42")); + return vec![ + ( + 20, + crate::SplTokenSingleOperation::InitializeMint { + mint: first.clone(), + mint_authority: second.clone(), + freeze_authority: std::option::Option::Some(third.clone()), + decimals: 9, + }, + ), + ( + 18, + crate::SplTokenSingleOperation::InitializeAccount { + account: first.clone(), + mint: second.clone(), + owner: third.clone(), + }, + ), + ( + 19, + crate::SplTokenSingleOperation::InitializeMultisig { + multisig: first.clone(), + members: vec![second.clone(), third.clone()], + threshold: 1, + }, + ), + ( + 3, + crate::SplTokenSingleOperation::Transfer { + source: first.clone(), + destination: second.clone(), + authority: authority.clone(), + amount: amount.clone(), + }, + ), + ( + 4, + crate::SplTokenSingleOperation::Approve { + source: first.clone(), + delegate: second.clone(), + authority: authority.clone(), + amount: amount.clone(), + }, + ), + ( + 5, + crate::SplTokenSingleOperation::Revoke { + source: first.clone(), + authority: authority.clone(), + }, + ), + ( + 6, + crate::SplTokenSingleOperation::SetAuthority { + owned: first.clone(), + authority_type: crate::SplTokenAuthorityType::CloseAccount, + new_authority: std::option::Option::Some(second.clone()), + current_authority: authority.clone(), + }, + ), + ( + 7, + crate::SplTokenSingleOperation::MintTo { + mint: first.clone(), + destination: second.clone(), + authority: authority.clone(), + amount: amount.clone(), + }, + ), + ( + 8, + crate::SplTokenSingleOperation::Burn { + source: first.clone(), + mint: second.clone(), + authority: authority.clone(), + amount: amount.clone(), + }, + ), + ( + 9, + crate::SplTokenSingleOperation::CloseAccount { + account: first.clone(), + destination: second.clone(), + authority: authority.clone(), + }, + ), + ( + 10, + crate::SplTokenSingleOperation::FreezeAccount { + account: first.clone(), + mint: second.clone(), + authority: authority.clone(), + }, + ), + ( + 11, + crate::SplTokenSingleOperation::ThawAccount { + account: first.clone(), + mint: second.clone(), + authority: authority.clone(), + }, + ), + (12, transfer_checked()), + ( + 13, + crate::SplTokenSingleOperation::ApproveChecked { + source: first.clone(), + mint: second.clone(), + delegate: third.clone(), + authority: authority.clone(), + amount: amount.clone(), + decimals: 9, + }, + ), + ( + 14, + crate::SplTokenSingleOperation::MintToChecked { + mint: first.clone(), + destination: second.clone(), + authority: authority.clone(), + amount: amount.clone(), + decimals: 9, + }, + ), + ( + 15, + crate::SplTokenSingleOperation::BurnChecked { + source: first.clone(), + mint: second.clone(), + authority: authority.clone(), + amount: amount.clone(), + decimals: 9, + }, + ), + ( + 17, + crate::SplTokenSingleOperation::SyncNative { + account: first.clone(), + include_rent_sysvar: true, + }, + ), + (21, crate::SplTokenSingleOperation::GetAccountDataSize { mint: first.clone() }), + ( + 22, + crate::SplTokenSingleOperation::InitializeImmutableOwner { account: first.clone() }, + ), + ( + 23, + crate::SplTokenSingleOperation::AmountToUiAmount { + mint: first.clone(), + amount: amount.clone(), + }, + ), + ( + 24, + crate::SplTokenSingleOperation::UiAmountToAmount { + mint: first.clone(), + ui_amount: std::string::String::from("0.000000042"), + }, + ), + ( + 38, + crate::SplTokenSingleOperation::WithdrawExcessLamports { + account: first.clone(), + destination: second.clone(), + authority: authority.clone(), + }, + ), + ( + 45, + crate::SplTokenSingleOperation::UnwrapLamports { + account: first, + destination: second, + authority, + amount_lamports: std::option::Option::Some(amount), + }, + ), + ]; + } + + #[test] + fn every_current_or_recent_single_operation_uses_its_published_tag() { + let operations = all_single_operations(); + assert_eq!(operations.len() + 1, crate::SUPPORTED_OPERATION_CODES.len()); + for (tag, operation) in operations { + let intent = intent(crate::SplTokenOperation::Instruction { value: operation }); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplTokenExecutor, + &intent, + ) + .unwrap_or_else(|error| panic!("tag {tag} plan failed: {error}")); + assert_eq!(plan.instructions[0].data[0], tag); + } + } + + #[test] + fn checked_transfer_matches_official_builder_and_preserves_u64_max() { + let operation = transfer_checked(); + let intent = intent(crate::SplTokenOperation::Instruction { value: operation }); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplTokenExecutor, + &intent, + ) + .unwrap_or_else(|error| panic!("checked transfer plan failed: {error}")); + assert_eq!(plan.instructions.len(), 1); + assert_eq!(plan.instructions[0].data[0], 12); + assert_eq!(&plan.instructions[0].data[1..9], &u64::MAX.to_le_bytes()); + assert_eq!(plan.instructions[0].data[9], 9); + assert_eq!(plan.required_signers.len(), 2); + } + + #[test] + fn multisig_meta_order_is_preserved_while_transaction_signers_are_unique() { + let first = kb_program_ids::VOTE_PROGRAM_ID; + let second = kb_program_ids::STAKE_PROGRAM_ID; + let operation = crate::SplTokenSingleOperation::Transfer { + source: pubkey(kb_program_ids::SYSTEM_PROGRAM_ID), + destination: pubkey(kb_program_ids::CONFIG_PROGRAM_ID), + authority: authority(&[first, second, first]), + amount: crate::SplTokenAmount(std::string::String::from("1")), + }; + let intent = intent(crate::SplTokenOperation::Instruction { value: operation }); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplTokenExecutor, + &intent, + ) + .unwrap_or_else(|error| panic!("multisig transfer plan failed: {error}")); + let accounts = &plan.instructions[0].accounts; + assert_eq!(accounts[3].pubkey.0, first); + assert_eq!(accounts[4].pubkey.0, second); + assert_eq!(accounts[5].pubkey.0, first); + assert_eq!(plan.required_signers.len(), 3); + } + + #[test] + fn recent_unwrap_and_batch_match_published_wire() { + let unwrap = crate::SplTokenSingleOperation::UnwrapLamports { + account: pubkey(kb_program_ids::SYSTEM_PROGRAM_ID), + destination: pubkey(kb_program_ids::STAKE_PROGRAM_ID), + authority: authority(&[]), + amount_lamports: std::option::Option::Some(crate::SplTokenAmount( + std::string::String::from("42"), + )), + }; + let intent = intent(crate::SplTokenOperation::Batch { + instructions: vec![unwrap, transfer_checked()], + }); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplTokenExecutor, + &intent, + ) + .unwrap_or_else(|error| panic!("recent Batch plan failed: {error}")); + assert_eq!(plan.instructions[0].data[0], 255); + assert_eq!(plan.instructions[0].data[2], 10); + assert_eq!(plan.instructions[0].data[3], 45); + assert_eq!(plan.requested_spend_lamports, 42); + } + + #[test] + fn invalid_amount_multisig_and_optional_simulation_fail_closed() { + let mut operation = transfer_checked(); + if let crate::SplTokenSingleOperation::TransferChecked { amount, .. } = &mut operation { + amount.0 = std::string::String::from("-1"); + } + let pintent = intent(crate::SplTokenOperation::Instruction { value: operation }); + let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplTokenExecutor, + &pintent, + ) + .expect_err("negative amount must fail"); + assert_eq!(error.code(), "execution_spl_token_amount_invalid"); + + let mut intent = + intent(crate::SplTokenOperation::Instruction { value: transfer_checked() }); + intent.policy.simulation = kb_execution_api::ExecutionSimulationPolicy::Optional; + let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplTokenExecutor, + &intent, + ) + .expect_err("optional simulation must fail"); + assert_eq!(error.code(), "execution_spl_token_simulation_required"); + } + + #[test] + fn every_cluster_is_constructible_and_mainnet_remains_governed_by_common_safety() { + for cluster in [ + kb_execution_api::ExecutionCluster::Localnet, + kb_execution_api::ExecutionCluster::Devnet, + kb_execution_api::ExecutionCluster::Testnet, + kb_execution_api::ExecutionCluster::Mainnet, + ] { + let mut intent = + intent(crate::SplTokenOperation::Instruction { value: transfer_checked() }); + intent.policy.cluster.expected_cluster = cluster; + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplTokenExecutor, + &intent, + ) + .unwrap_or_else(|error| panic!("cluster plan failed: {error}")); + assert_eq!(plan.policy.cluster.expected_cluster, cluster); + let evaluation = kb_execution_safety::ExecutionSafetyChecker + .evaluate_prepared_plan(&plan) + .unwrap_or_else(|error| panic!("safety evaluation failed: {error}")); + if cluster == kb_execution_api::ExecutionCluster::Mainnet { + assert_eq!(evaluation.decision, kb_execution_safety::ExecutionSafetyDecision::Deny); + assert!(evaluation.violations.iter().any(|violation| { + return violation.code == "execution_mainnet_disabled"; + })); + let mut explicitly_enabled = plan.clone(); + explicitly_enabled.policy.cluster.allow_mainnet = true; + explicitly_enabled.policy.cluster.mainnet_confirmation = true; + let enabled_evaluation = kb_execution_safety::ExecutionSafetyChecker + .evaluate_prepared_plan(&explicitly_enabled) + .unwrap_or_else(|error| { + panic!("enabled Mainnet safety evaluation failed: {error}") + }); + assert_eq!( + enabled_evaluation.decision, + kb_execution_safety::ExecutionSafetyDecision::Allow + ); + } else { + assert_eq!( + evaluation.decision, + kb_execution_safety::ExecutionSafetyDecision::Allow + ); + } + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_token/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_token/src/constants.rs new file mode 100644 index 0000000..65cf350 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_token/src/constants.rs @@ -0,0 +1,15 @@ +// file: kb_executor_spl_token/src/constants.rs +// version: 3 + +//! Local constants for the `kb_executor_spl_token` crate. Program identifiers live in `kb_program_ids`. + +/// Maximum number of children accepted by one Batch plan. +pub(crate) const MAX_BATCH_INSTRUCTIONS: usize = 64; +/// Maximum aggregate account-meta occurrences accepted by one Batch plan. +pub(crate) const MAX_BATCH_ACCOUNTS: usize = 512; +/// Maximum number of ordered multisig signer occurrences. +pub(crate) const MAX_MULTISIG_SIGNERS: usize = 11; +/// Maximum UI amount string length accepted before transaction assembly. +pub(crate) const MAX_UI_AMOUNT_BYTES: usize = 255; +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_spl_token"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_token/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_token/src/executor.rs new file mode 100644 index 0000000..b6f706b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_token/src/executor.rs @@ -0,0 +1,241 @@ +// file: kb_executor_spl_token/src/executor.rs +// version: 4 + +//! Exact classic SPL Token capability dispatch and typed plan construction. + +/// Classic SPL Token executor implementation. +#[derive(Clone, Debug, Default)] +pub struct SplTokenExecutor; + +impl crate::SplTokenExecutor { + fn exact_capability( + &self, + program_id: &kb_model::ProgramId, + operation_code: &str, + ) -> kb_execution_api::ExecutionCapability { + if program_id.0 != kb_program_ids::SPL_TOKEN_PROGRAM_ID { + return kb_execution_api::ExecutionCapability::unsupported( + "execution_spl_token_program_not_owned", + format!("program {} is not owned by kb_executor_spl_token", program_id.0), + ); + } + if crate::SUPPORTED_OPERATION_CODES.contains(&operation_code) { + return kb_execution_api::ExecutionCapability::supported(operation_code); + } + if matches!( + operation_code, + "spl_token.initialize_mint_with_rent" + | "spl_token.initialize_account_with_rent" + | "spl_token.initialize_multisig_with_rent" + | "spl_token.initialize_account2" + ) { + return kb_execution_api::ExecutionCapability::unsupported( + "execution_spl_token_historical_variant_decode_only", + format!( + "SPL Token operation {operation_code} is an obsolete historical initialization variant; use the current no-Rent operation" + ), + ); + } + return kb_execution_api::ExecutionCapability::unsupported( + "execution_spl_token_operation_unsupported", + format!("SPL Token operation {operation_code} is not implemented"), + ); + } +} + +impl kb_execution_api::TypedInstructionExecutor for crate::SplTokenExecutor { + type Intent = crate::SplTokenExecutionIntent; + + fn capability( + &self, + program_id: &kb_model::ProgramId, + operation_code: &str, + ) -> kb_execution_api::ExecutionCapability { + return self.exact_capability(program_id, operation_code); + } + + fn build_prepared_plan( + &self, + intent: &Self::Intent, + ) -> kb_core::Result { + let program_id = + kb_model::ProgramId(std::string::String::from(kb_program_ids::SPL_TOKEN_PROGRAM_ID)); + return match self.exact_capability(&program_id, intent.operation.operation_code()) { + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => { + crate::build_prepared_plan(intent) + }, + kb_execution_api::ExecutionCapability::Unsupported { reason_code, reason } => { + std::result::Result::Err(kb_core::Error::new(reason_code, reason)) + }, + }; + } +} + +impl kb_execution_api::InstructionExecutor for crate::SplTokenExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_spl_token"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::SPL_TOKEN_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + return match self.exact_capability(&request.program_id, &request.operation_code) { + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => { + kb_execution_api::ExecutionSupport::Yes + }, + kb_execution_api::ExecutionCapability::Unsupported { reason_code: _, reason: _ } => { + kb_execution_api::ExecutionSupport::No + }, + }; + } + + fn build_plan( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + match self.exact_capability(&request.program_id, &request.operation_code) { + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => {}, + kb_execution_api::ExecutionCapability::Unsupported { reason_code, reason } => { + return std::result::Result::Err(kb_core::Error::new(reason_code, reason)); + }, + } + let intent = + match serde_json::from_str::(&request.payload_json) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_intent_deserialize_failed", + error.to_string(), + )); + }, + }; + if intent.operation.operation_code() != request.operation_code.as_str() { + return std::result::Result::Err(kb_core::Error::new( + "execution_operation_code_mismatch", + format!( + "request operation {} does not match typed intent operation {}", + request.operation_code, + intent.operation.operation_code() + ), + )); + } + let prepared = + match kb_execution_api::TypedInstructionExecutor::build_prepared_plan(self, &intent) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let payload_value = match serde_json::to_value(&prepared) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_plan_serialize_failed", + error.to_string(), + )); + }, + }; + let payload_json = match kb_execution_api::serialize_payload_json(&payload_value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_spl_token"), + instruction_count: prepared.instructions.len(), + payload_json, + }); + } +} + +#[cfg(test)] +mod tests { + fn request(program_id: &str, operation_code: &str) -> kb_execution_api::ExecutionRequest { + return kb_execution_api::ExecutionRequest { + program_id: kb_model::ProgramId(program_id.to_string()), + operation_code: operation_code.to_string(), + payload_json: std::string::String::from("{}"), + }; + } + + #[test] + fn exact_capabilities_cover_current_and_recent_operations_only() { + let executor = crate::SplTokenExecutor; + for operation_code in crate::SUPPORTED_OPERATION_CODES { + let request = request(kb_program_ids::SPL_TOKEN_PROGRAM_ID, operation_code); + assert_eq!( + kb_execution_api::InstructionExecutor::supports_request(&executor, &request), + kb_execution_api::ExecutionSupport::Yes + ); + } + for operation_code in [ + "spl_token.initialize_mint_with_rent", + "spl_token.initialize_account_with_rent", + "spl_token.initialize_multisig_with_rent", + "spl_token.initialize_account2", + ] { + let capability = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(kb_program_ids::SPL_TOKEN_PROGRAM_ID.to_string()), + operation_code, + ); + match capability { + kb_execution_api::ExecutionCapability::Unsupported { reason_code, reason: _ } => { + assert_eq!(reason_code, "execution_spl_token_historical_variant_decode_only"); + }, + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => { + panic!("historical variant must be decode-only"); + }, + } + } + } + + #[test] + fn program_dispatch_is_exact() { + let executor = crate::SplTokenExecutor; + let foreign = + request(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, crate::TRANSFER_CHECKED_OPERATION); + assert_eq!( + kb_execution_api::InstructionExecutor::supports_request(&executor, &foreign), + kb_execution_api::ExecutionSupport::No + ); + } + + #[test] + fn machine_readable_matrix_matches_executor_policy() { + let matrix = serde_json::from_str::(include_str!( + "../../docs/SPL_TOKEN_MATRIX.json" + )) + .unwrap_or_else(|error| panic!("matrix parsing failed: {error}")); + let instructions = matrix["instructions"] + .as_array() + .unwrap_or_else(|| panic!("matrix instructions must be an array")); + let mut supported = 0_usize; + let mut operation_codes = std::vec::Vec::new(); + for instruction in instructions { + let status = instruction["executorSupport"]["status"] + .as_str() + .unwrap_or_else(|| panic!("executorSupport.status missing")); + if status == "supported" { + supported += 1; + operation_codes.push( + instruction["executorSupport"]["operationCode"] + .as_str() + .unwrap_or_else(|| panic!("supported operationCode missing")), + ); + } + } + assert_eq!(supported, crate::SUPPORTED_OPERATION_CODES.len()); + assert_eq!(instructions.len() - supported, 4); + operation_codes.sort_unstable(); + let mut compiled = crate::SUPPORTED_OPERATION_CODES.to_vec(); + compiled.sort_unstable(); + assert_eq!(operation_codes, compiled); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_token/src/intent.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_token/src/intent.rs new file mode 100644 index 0000000..eec4de1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_token/src/intent.rs @@ -0,0 +1,457 @@ +// file: kb_executor_spl_token/src/intent.rs +// version: 3 + +//! Typed classic SPL Token execution intents. + +use ts_rs::TS; // rust-rules: derive-import + +/// Stable operation code for mint initialization through `InitializeMint2`. +pub const INITIALIZE_MINT_OPERATION: &str = "spl_token.initialize_mint"; +/// Stable operation code for token-account initialization through `InitializeAccount3`. +pub const INITIALIZE_ACCOUNT_OPERATION: &str = "spl_token.initialize_account"; +/// Stable operation code for multisig initialization through `InitializeMultisig2`. +pub const INITIALIZE_MULTISIG_OPERATION: &str = "spl_token.initialize_multisig"; +/// Stable operation code for an unchecked token transfer. +pub const TRANSFER_OPERATION: &str = "spl_token.transfer"; +/// Stable operation code for an approval. +pub const APPROVE_OPERATION: &str = "spl_token.approve"; +/// Stable operation code for a delegate revocation. +pub const REVOKE_OPERATION: &str = "spl_token.revoke"; +/// Stable operation code for an authority change. +pub const SET_AUTHORITY_OPERATION: &str = "spl_token.set_authority"; +/// Stable operation code for minting tokens. +pub const MINT_TO_OPERATION: &str = "spl_token.mint_to"; +/// Stable operation code for burning tokens. +pub const BURN_OPERATION: &str = "spl_token.burn"; +/// Stable operation code for closing a token account. +pub const CLOSE_ACCOUNT_OPERATION: &str = "spl_token.close_account"; +/// Stable operation code for freezing a token account. +pub const FREEZE_ACCOUNT_OPERATION: &str = "spl_token.freeze_account"; +/// Stable operation code for thawing a token account. +pub const THAW_ACCOUNT_OPERATION: &str = "spl_token.thaw_account"; +/// Stable operation code for a checked token transfer. +pub const TRANSFER_CHECKED_OPERATION: &str = "spl_token.transfer_checked"; +/// Stable operation code for a checked approval. +pub const APPROVE_CHECKED_OPERATION: &str = "spl_token.approve_checked"; +/// Stable operation code for checked minting. +pub const MINT_TO_CHECKED_OPERATION: &str = "spl_token.mint_to_checked"; +/// Stable operation code for checked burning. +pub const BURN_CHECKED_OPERATION: &str = "spl_token.burn_checked"; +/// Stable operation code for synchronizing wrapped SOL. +pub const SYNC_NATIVE_OPERATION: &str = "spl_token.sync_native"; +/// Stable operation code for querying account data size. +pub const GET_ACCOUNT_DATA_SIZE_OPERATION: &str = "spl_token.get_account_data_size"; +/// Stable operation code for the classic compatibility no-op. +pub const INITIALIZE_IMMUTABLE_OWNER_OPERATION: &str = "spl_token.initialize_immutable_owner"; +/// Stable operation code for raw-to-UI amount conversion. +pub const AMOUNT_TO_UI_AMOUNT_OPERATION: &str = "spl_token.amount_to_ui_amount"; +/// Stable operation code for UI-to-raw amount conversion. +pub const UI_AMOUNT_TO_AMOUNT_OPERATION: &str = "spl_token.ui_amount_to_amount"; +/// Stable operation code for rescuing excess lamports. +pub const WITHDRAW_EXCESS_LAMPORTS_OPERATION: &str = "spl_token.withdraw_excess_lamports"; +/// Stable operation code for the recent wrapped-SOL partial unwrap. +pub const UNWRAP_LAMPORTS_OPERATION: &str = "spl_token.unwrap_lamports"; +/// Stable operation code for a recent bounded batch. +pub const BATCH_OPERATION: &str = "spl_token.batch"; + +/// Operation codes constructible by this executor. +pub const SUPPORTED_OPERATION_CODES: &[&str] = &[ + INITIALIZE_MINT_OPERATION, + INITIALIZE_ACCOUNT_OPERATION, + INITIALIZE_MULTISIG_OPERATION, + TRANSFER_OPERATION, + APPROVE_OPERATION, + REVOKE_OPERATION, + SET_AUTHORITY_OPERATION, + MINT_TO_OPERATION, + BURN_OPERATION, + CLOSE_ACCOUNT_OPERATION, + FREEZE_ACCOUNT_OPERATION, + THAW_ACCOUNT_OPERATION, + TRANSFER_CHECKED_OPERATION, + APPROVE_CHECKED_OPERATION, + MINT_TO_CHECKED_OPERATION, + BURN_CHECKED_OPERATION, + SYNC_NATIVE_OPERATION, + GET_ACCOUNT_DATA_SIZE_OPERATION, + INITIALIZE_IMMUTABLE_OWNER_OPERATION, + AMOUNT_TO_UI_AMOUNT_OPERATION, + UI_AMOUNT_TO_AMOUNT_OPERATION, + WITHDRAW_EXCESS_LAMPORTS_OPERATION, + UNWRAP_LAMPORTS_OPERATION, + BATCH_OPERATION, +]; + +/// Exact unsigned on-chain amount represented as a decimal JSON string. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token/intent/SplTokenAmount.ts" +)] +pub struct SplTokenAmount( + /// Canonical unsigned decimal representation. + pub std::string::String, +); + +/// Ordered simple or multisig authority supplied to an instruction builder. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token/intent/SplTokenAuthority.ts" +)] +pub struct SplTokenAuthority { + /// Authority account. It signs only when `multisig_signers` is empty. + pub authority: kb_model::Pubkey, + /// Ordered multisig signer occurrences. Duplicate metas remain ordered. + pub multisig_signers: std::vec::Vec, +} + +/// Authority domain used by `SetAuthority`. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token/intent/SplTokenAuthorityType.ts" +)] +pub enum SplTokenAuthorityType { + /// Mint authority. + MintTokens, + /// Freeze authority. + FreezeAccount, + /// Token-account owner. + AccountOwner, + /// Token-account close authority. + CloseAccount, +} + +/// One officially constructible non-batch instruction. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(tag = "instruction", rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token/intent/SplTokenSingleOperation.ts" +)] +pub enum SplTokenSingleOperation { + /// Initialize a mint with the current no-Rent builder. + InitializeMint { + /// Mint account already created for the classic Token program. + mint: kb_model::Pubkey, + /// Mint authority. + mint_authority: kb_model::Pubkey, + /// Optional freeze authority. + freeze_authority: std::option::Option, + /// Mint decimals. + decimals: u8, + }, + /// Initialize a token account with the current no-Rent builder. + InitializeAccount { + /// Token account already created for the classic Token program. + account: kb_model::Pubkey, + /// Mint associated with the account. + mint: kb_model::Pubkey, + /// Token-account owner. + owner: kb_model::Pubkey, + }, + /// Initialize a multisig with the current no-Rent builder. + InitializeMultisig { + /// Multisig account already created for the classic Token program. + multisig: kb_model::Pubkey, + /// Ordered member accounts. + members: std::vec::Vec, + /// Required member threshold. + threshold: u8, + }, + /// Transfer a raw token amount without carrying the mint or decimals in the wire. + Transfer { + /// Source token account. + source: kb_model::Pubkey, + /// Destination token account. + destination: kb_model::Pubkey, + /// Owner, delegate or multisig authority. + authority: crate::SplTokenAuthority, + /// Exact raw token amount. + amount: crate::SplTokenAmount, + }, + /// Approve a delegate for a raw token allowance. + Approve { + /// Source token account. + source: kb_model::Pubkey, + /// Delegate account. + delegate: kb_model::Pubkey, + /// Owner or multisig authority. + authority: crate::SplTokenAuthority, + /// Exact raw allowance. + amount: crate::SplTokenAmount, + }, + /// Revoke the current delegate. + Revoke { + /// Source token account. + source: kb_model::Pubkey, + /// Owner or multisig authority. + authority: crate::SplTokenAuthority, + }, + /// Change or revoke an authority. + SetAuthority { + /// Mint or token account whose authority changes. + owned: kb_model::Pubkey, + /// Authority domain. + authority_type: crate::SplTokenAuthorityType, + /// New authority, or `None` to revoke it. + new_authority: std::option::Option, + /// Current simple or multisig authority. + current_authority: crate::SplTokenAuthority, + }, + /// Mint a raw token amount. + MintTo { + /// Mint account. + mint: kb_model::Pubkey, + /// Destination token account. + destination: kb_model::Pubkey, + /// Mint authority. + authority: crate::SplTokenAuthority, + /// Exact raw amount. + amount: crate::SplTokenAmount, + }, + /// Burn a raw token amount. + Burn { + /// Source token account. + source: kb_model::Pubkey, + /// Mint account. + mint: kb_model::Pubkey, + /// Owner, delegate or multisig authority. + authority: crate::SplTokenAuthority, + /// Exact raw amount. + amount: crate::SplTokenAmount, + }, + /// Close a token account. + CloseAccount { + /// Token account to close. + account: kb_model::Pubkey, + /// Lamport destination. + destination: kb_model::Pubkey, + /// Owner or close authority. + authority: crate::SplTokenAuthority, + }, + /// Freeze a token account. + FreezeAccount { + /// Token account to freeze. + account: kb_model::Pubkey, + /// Mint account. + mint: kb_model::Pubkey, + /// Freeze authority. + authority: crate::SplTokenAuthority, + }, + /// Thaw a token account. + ThawAccount { + /// Token account to thaw. + account: kb_model::Pubkey, + /// Mint account. + mint: kb_model::Pubkey, + /// Freeze authority. + authority: crate::SplTokenAuthority, + }, + /// Transfer while checking an explicit mint and decimals value. + TransferChecked { + /// Source token account. + source: kb_model::Pubkey, + /// Mint account. + mint: kb_model::Pubkey, + /// Destination token account. + destination: kb_model::Pubkey, + /// Owner, delegate or multisig authority. + authority: crate::SplTokenAuthority, + /// Exact raw amount. + amount: crate::SplTokenAmount, + /// Expected mint decimals. + decimals: u8, + }, + /// Approve a delegate while checking an explicit mint and decimals value. + ApproveChecked { + /// Source token account. + source: kb_model::Pubkey, + /// Mint account. + mint: kb_model::Pubkey, + /// Delegate account. + delegate: kb_model::Pubkey, + /// Owner or multisig authority. + authority: crate::SplTokenAuthority, + /// Exact raw allowance. + amount: crate::SplTokenAmount, + /// Expected mint decimals. + decimals: u8, + }, + /// Mint while checking explicit decimals. + MintToChecked { + /// Mint account. + mint: kb_model::Pubkey, + /// Destination token account. + destination: kb_model::Pubkey, + /// Mint authority. + authority: crate::SplTokenAuthority, + /// Exact raw amount. + amount: crate::SplTokenAmount, + /// Expected mint decimals. + decimals: u8, + }, + /// Burn while checking explicit decimals. + BurnChecked { + /// Source token account. + source: kb_model::Pubkey, + /// Mint account. + mint: kb_model::Pubkey, + /// Owner, delegate or multisig authority. + authority: crate::SplTokenAuthority, + /// Exact raw amount. + amount: crate::SplTokenAmount, + /// Expected mint decimals. + decimals: u8, + }, + /// Synchronize a wrapped-SOL account. + SyncNative { + /// Wrapped-SOL token account. + account: kb_model::Pubkey, + /// Include the optional Rent sysvar account published by interface 3.0.0. + include_rent_sysvar: bool, + }, + /// Query the token-account size for a mint. + GetAccountDataSize { + /// Mint account. + mint: kb_model::Pubkey, + }, + /// Build the classic-program immutable-owner compatibility no-op. + InitializeImmutableOwner { + /// Token account being prepared. + account: kb_model::Pubkey, + }, + /// Convert a raw amount using mint decimals through return data. + AmountToUiAmount { + /// Mint account. + mint: kb_model::Pubkey, + /// Exact raw amount. + amount: crate::SplTokenAmount, + }, + /// Convert a UI amount using mint decimals through return data. + UiAmountToAmount { + /// Mint account. + mint: kb_model::Pubkey, + /// Exact UTF-8 UI amount. + ui_amount: std::string::String, + }, + /// Withdraw lamports above rent exemption. + WithdrawExcessLamports { + /// Program-owned source account. + account: kb_model::Pubkey, + /// Lamport destination. + destination: kb_model::Pubkey, + /// Owner or multisig authority. + authority: crate::SplTokenAuthority, + }, + /// Unwrap some or all lamports from a wrapped-SOL account. + UnwrapLamports { + /// Wrapped-SOL token account. + account: kb_model::Pubkey, + /// Lamport destination. + destination: kb_model::Pubkey, + /// Owner or multisig authority. + authority: crate::SplTokenAuthority, + /// Exact lamports to unwrap, or `None` for the complete balance. + amount_lamports: std::option::Option, + }, +} + +impl crate::SplTokenSingleOperation { + /// Returns the stable operation code. + pub fn operation_code(&self) -> &'static str { + return match self { + Self::InitializeMint { .. } => crate::INITIALIZE_MINT_OPERATION, + Self::InitializeAccount { .. } => crate::INITIALIZE_ACCOUNT_OPERATION, + Self::InitializeMultisig { .. } => crate::INITIALIZE_MULTISIG_OPERATION, + Self::Transfer { .. } => crate::TRANSFER_OPERATION, + Self::Approve { .. } => crate::APPROVE_OPERATION, + Self::Revoke { .. } => crate::REVOKE_OPERATION, + Self::SetAuthority { .. } => crate::SET_AUTHORITY_OPERATION, + Self::MintTo { .. } => crate::MINT_TO_OPERATION, + Self::Burn { .. } => crate::BURN_OPERATION, + Self::CloseAccount { .. } => crate::CLOSE_ACCOUNT_OPERATION, + Self::FreezeAccount { .. } => crate::FREEZE_ACCOUNT_OPERATION, + Self::ThawAccount { .. } => crate::THAW_ACCOUNT_OPERATION, + Self::TransferChecked { .. } => crate::TRANSFER_CHECKED_OPERATION, + Self::ApproveChecked { .. } => crate::APPROVE_CHECKED_OPERATION, + Self::MintToChecked { .. } => crate::MINT_TO_CHECKED_OPERATION, + Self::BurnChecked { .. } => crate::BURN_CHECKED_OPERATION, + Self::SyncNative { .. } => crate::SYNC_NATIVE_OPERATION, + Self::GetAccountDataSize { .. } => crate::GET_ACCOUNT_DATA_SIZE_OPERATION, + Self::InitializeImmutableOwner { .. } => crate::INITIALIZE_IMMUTABLE_OWNER_OPERATION, + Self::AmountToUiAmount { .. } => crate::AMOUNT_TO_UI_AMOUNT_OPERATION, + Self::UiAmountToAmount { .. } => crate::UI_AMOUNT_TO_AMOUNT_OPERATION, + Self::WithdrawExcessLamports { .. } => crate::WITHDRAW_EXCESS_LAMPORTS_OPERATION, + Self::UnwrapLamports { .. } => crate::UNWRAP_LAMPORTS_OPERATION, + }; + } + + pub(crate) fn requires_materialization(&self) -> bool { + return !matches!( + self, + Self::GetAccountDataSize { .. } + | Self::InitializeImmutableOwner { .. } + | Self::AmountToUiAmount { .. } + | Self::UiAmountToAmount { .. } + ); + } +} + +/// Top-level SPL Token operation, including the recent bounded Batch builder. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(tag = "operation", rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token/intent/SplTokenOperation.ts" +)] +pub enum SplTokenOperation { + /// Build one non-batch instruction. + Instruction { + /// Exact instruction arguments. + value: crate::SplTokenSingleOperation, + }, + /// Build one Batch from ordered, non-batch child operations. + Batch { + /// Ordered child operations. Nested Batch is impossible in this contract. + instructions: std::vec::Vec, + }, +} + +impl crate::SplTokenOperation { + /// Returns the stable top-level operation code. + pub fn operation_code(&self) -> &'static str { + return match self { + Self::Instruction { value } => value.operation_code(), + Self::Batch { .. } => crate::BATCH_OPERATION, + }; + } + + pub(crate) fn requires_materialization(&self) -> bool { + return match self { + Self::Instruction { value } => value.requires_materialization(), + Self::Batch { instructions } => { + instructions.iter().any(|value| return value.requires_materialization()) + }, + }; + } +} + +/// Complete typed intent accepted by the classic SPL Token executor. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token/intent/SplTokenExecutionIntent.ts" +)] +pub struct SplTokenExecutionIntent { + /// Stable caller-provided identifier used for logs and replay correlation. + pub intent_id: std::string::String, + /// Transaction fee payer. + pub fee_payer: kb_model::Pubkey, + /// Conservative execution policy. Its default is simulation-only. + pub policy: kb_execution_api::ExecutionPolicy, + /// Typed classic Token operation. + pub operation: crate::SplTokenOperation, +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_token/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_token/src/lib.rs new file mode 100644 index 0000000..df1e641 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_token/src/lib.rs @@ -0,0 +1,90 @@ +// file: kb_executor_spl_token/src/lib.rs +// version: 8 + +//! Executor crate for `spl_token`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod builder; +mod constants; +mod executor; +mod intent; + +/// Crate-root access to `build_prepared_plan` from `builder`. +pub(crate) use crate::builder::build_prepared_plan; +/// Maximum aggregate account-meta occurrences accepted by one Batch plan. +pub(crate) use crate::constants::MAX_BATCH_ACCOUNTS; +/// Maximum number of children accepted by one Batch plan. +pub(crate) use crate::constants::MAX_BATCH_INSTRUCTIONS; +/// Maximum number of ordered multisig signer occurrences. +pub(crate) use crate::constants::MAX_MULTISIG_SIGNERS; +/// Maximum UI amount string length accepted before transaction assembly. +pub(crate) use crate::constants::MAX_UI_AMOUNT_BYTES; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the typed SPL Token executor. +pub use crate::executor::SplTokenExecutor; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::AMOUNT_TO_UI_AMOUNT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::APPROVE_CHECKED_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::APPROVE_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::BATCH_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::BURN_CHECKED_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::BURN_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::CLOSE_ACCOUNT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::FREEZE_ACCOUNT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::GET_ACCOUNT_DATA_SIZE_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_ACCOUNT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_IMMUTABLE_OWNER_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_MINT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_MULTISIG_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::MINT_TO_CHECKED_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::MINT_TO_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::REVOKE_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::SET_AUTHORITY_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::SUPPORTED_OPERATION_CODES; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::SYNC_NATIVE_OPERATION; +/// Exposes the exact raw amount string contract. +pub use crate::intent::SplTokenAmount; +/// Exposes the typed authority contract. +pub use crate::intent::SplTokenAuthority; +/// Exposes the typed authority-kind contract. +pub use crate::intent::SplTokenAuthorityType; +/// Exposes the complete typed execution intent. +pub use crate::intent::SplTokenExecutionIntent; +/// Exposes the top-level operation contract. +pub use crate::intent::SplTokenOperation; +/// Exposes the non-batch operation contract. +pub use crate::intent::SplTokenSingleOperation; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::THAW_ACCOUNT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::TRANSFER_CHECKED_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::TRANSFER_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::UI_AMOUNT_TO_AMOUNT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::UNWRAP_LAMPORTS_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::WITHDRAW_EXCESS_LAMPORTS_OPERATION; diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/Cargo.toml new file mode 100644 index 0000000..349bb56 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/Cargo.toml @@ -0,0 +1,32 @@ +# file: kb_executor_spl_token_2022/Cargo.toml +# version: 5 + +[package] +name = "kb_executor_spl_token_2022" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +bytemuck.workspace = true +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_execution_safety = { path = "../kb_execution_safety" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde.workspace = true +serde_json.workspace = true +solana-instruction.workspace = true +solana-pubkey.workspace = true +solana-zk-sdk-pod.workspace = true +solana-zk-elgamal-proof-interface.workspace = true +spl-token-confidential-transfer-proof-extraction.workspace = true +spl-token-2022-interface.workspace = true +spl-token-metadata-interface.workspace = true +spl-token-group-interface.workspace = true +tracing.workspace = true +ts-rs.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/README.md b/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/README.md new file mode 100644 index 0000000..dd1b190 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/README.md @@ -0,0 +1,139 @@ + + + +# kb_executor_spl_token_2022 + +Ce crate construit des plans d’exécution typés pour le programme SPL Token-2022. Il ne diffuse aucune transaction directement : l’orchestration impose simulation, plafonds de coûts, signataires autorisés et validation post-exécution. + +## Frontière + +- Program ID exact : `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb`. +- Builders officiels de `spl-token-2022-interface`. +- Clés publiques exposées par `solana_pubkey::Pubkey` via les contrats `kb_model`. +- `Batch` reste decode-only tant qu’aucun builder officiel n’est publié. +- Les opérations confidentielles restent séparées jusqu’à validation complète des preuves et comptes de contexte. + +## Transfer Fee + +La tranche `0.4.6-pre.043` ajoute les six builders publics officiels : + +- `initialize_transfer_fee_config` ; +- `set_transfer_fee` ; +- `transfer_checked_with_fee` ; +- `withdraw_withheld_tokens_from_mint` ; +- `withdraw_withheld_tokens_from_accounts` ; +- `harvest_withheld_tokens_to_mint`. + +Les montants restent des chaînes décimales `u64`. Les basis points sont limités à `10_000`. Les listes de comptes sources sont non vides, uniques, ordonnées et limitées à 255 occurrences. Les autorités simples et multisig conservent exactement les metas publiées par l’interface officielle. + +## Default Account State, Memo Transfer et CPI Guard + +La tranche `0.4.6-pre.044` ajoute les six builders publics officiels : + +- `initialize_default_account_state` ; +- `update_default_account_state` ; +- `enable_required_transfer_memos` ; +- `disable_required_transfer_memos` ; +- `enable_cpi_guard` ; +- `disable_cpi_guard`. + +`InitializeDefaultAccountState` doit précéder l'initialisation du mint. La mise à jour utilise la freeze authority, simple ou multisig. Memo Transfer et CPI Guard modifient un Token Account et utilisent son owner, simple ou multisig. Les états par défaut sont typés `initialized` ou `frozen`; aucune valeur numérique arbitraire n'est acceptée. + +## Transfer Hook et pointers + +La tranche `0.4.6-pre.045` ajoute les huit builders publics officiels : + +- `initialize_transfer_hook` et `update_transfer_hook` ; +- `initialize_metadata_pointer` et `update_metadata_pointer` ; +- `initialize_group_pointer` et `update_group_pointer` ; +- `initialize_group_member_pointer` et `update_group_member_pointer`. + +Les initialisations doivent précéder l'initialisation du mint et acceptent une autorité optionnelle ainsi qu'une cible optionnelle. Les mises à jour utilisent une autorité simple ou multisig et acceptent `None` pour supprimer explicitement la cible. Les metas et l'ordre des signataires proviennent exclusivement des builders officiels de `spl-token-2022-interface`. + +## Sécurité + +- simulation obligatoire ; +- dry-run par défaut ; +- plafond de frais positif obligatoire ; +- matérialisation post-exécution requise pour les mutations ; +- aucun calcul de fee implicite : `transfer_checked_with_fee` exige le montant attendu exact ; +- aucun retrait ou harvest n’invente l’état final, qui doit être relu par le pipeline stateful. +## Token Metadata et Token Group + +La tranche `0.4.6-pre.046` ajoute les builders officiels des interfaces exécutées sous le Program ID Token-2022 : + +- initialisation, mise à jour de champ et suppression de clé Token Metadata ; +- initialisation de groupe, mise à jour de `max_size` et initialisation de membre ; +- chaînes metadata bornées à 1 024 octets par champ dans le contrat d’exécution ; +- comptes et signataires conservés dans l’ordre publié par les interfaces ; +- aucune confusion avec Metaplex Token Metadata ni avec une implémentation tierce des interfaces. + +Les instructions utilisent les discriminateurs SPL des interfaces et non un tag `TokenInstruction` sur un octet. Le dispatcher reste strictement attaché au Program ID Token-2022 pour cette crate. +## Extensions publiques récentes + +L’exécuteur construit désormais les opérations officielles `ScaledUiAmount`, `Pausable` et `PermissionedBurn`. Les multiplicateurs Scaled UI sont fournis sous forme de chaîne décimale, puis refusés s’ils ne sont pas strictement positifs, finis et normaux. `PermissionedBurn` conserve séparément l’autorité dédiée obligatoire et l’owner/delegate simple ou multisig du compte source. Les opérations confidentielles de burn restent hors de cette tranche. + + +## Audit Confidential Transfer + +La tranche `0.4.6-pre.048` publie uniquement le contrat audité des opérations confidentielles : inventaire des opérations, ordre exact des preuves, modes `instruction_offset` et `context_state_account`, et classification entre builders ne nécessitant que des données publiques et opérations exigeant des entrées cryptographiques générées par l’appelant. + +Un offset inline nul est refusé : la valeur wire `0` est réservée au mode compte de contexte. Les instructions Token-2022, les instructions du programme ZK ElGamal Proof et les instructions du registre ElGamal restent trois surfaces techniques distinctes. Aucun chiffrement, déchiffrement, secret ou preuve n’est généré par ce crate. + +## Confidential Transfer public-data builders + +The executor constructs the six Confidential Transfer operations whose complete wire payload is public and requires no caller-generated proof or ciphertext: account approval, public deposit, and the four credit toggles. Initialize/update mint and apply-pending-balance remain deferred because their official data contracts include ElGamal or decryptable-balance material that must be supplied as bounded opaque caller input. + +## Confidential Transfer — entrées cryptographiques opaques + +L’exécuteur accepte désormais les clés publiques ElGamal de 32 octets et les balances déchiffrables de 36 octets sous forme hexadécimale stricte. Il construit `InitializeMint`, `UpdateMint` et `ApplyPendingBalance` avec les builders officiels, sans générer ni dériver de secret. + +## Configuration confidentielle des comptes + +`ConfigureConfidentialTransferAccount` accepte une balance zéro déchiffrable opaque, un compteur maximal de crédits en attente, une autorité simple ou multisig et une référence `PubkeyValidity` explicite. Le mode `InstructionOffset` référence une instruction ZK déjà orchestrée dans la transaction ; le mode `ContextStateAccount` référence un contexte pré-vérifié. Le crate ne fabrique aucune preuve. + +`ConfigureConfidentialTransferAccountWithRegistry` utilise un compte ElGamal Registry explicite. Le payer de réallocation est optionnel ; lorsqu’il est présent, le builder officiel ajoute le System Program. La cohérence owner/PDA du registre reste un préflight obligatoire avant diffusion. + +## Confidential Transfer — vidage du compte + +`EmptyConfidentialTransferAccount` construit l’instruction Token-2022 `EmptyAccount` avec une référence `ZeroCiphertext` explicite. Le mode `InstructionOffset` ajoute uniquement le compte Instructions Sysvar et encode l’offset signé non nul ; le mode `ContextStateAccount` ajoute le compte de contexte readonly et encode l’offset `0`. L’autorité owner/delegate simple ou multisig reste ordonnée exactement. Le crate ne génère ni preuve ni donnée secrète. + +`WithdrawConfidentialTokens` construit l’instruction `Withdraw` avec montant brut, decimals, nouvelle balance déchiffrable opaque et deux preuves ordonnées : `CiphertextCommitmentEquality`, puis `BatchedRangeProofU64`. Chaque preuve peut être référencée par offset inline non nul ou par compte context-state ; le compte Instructions Sysvar n’est ajouté qu’une seule fois lorsqu’au moins une preuve est inline. + + +## Transfer confidentiel + +`TransferConfidentialTokens` construit l'instruction Token-2022 `ConfidentialTransferExtension::Transfer` avec une balance source déchiffrable de 36 octets, deux ciphertexts auditeur ElGamal de 64 octets et trois preuves référencées dans l'ordre officiel : Equality, Grouped Ciphertext Validity à trois handles, puis Range U128. Les preuves peuvent utiliser indépendamment un offset inline non nul ou un compte context-state. Le crate ne génère aucun ciphertext, secret ou preuve. + + +## Audit Transfer With Fee (`pre.055`) + +Le contrat audité fixe le wire `27/13`, 169 octets de payload et 171 octets de données d’instruction. Le payload Token-2022 réutilise uniquement `SplTokenDecryptableBalance` et deux `SplTokenElGamalCiphertext`. Le ciphertext de frais et les paramètres de frais restent portés par les contextes de preuve, pas par un nouveau champ opaque de l’intent Token-2022. + +### Transfer confidentiel avec frais + +`TransferConfidentialTokensWithFee` construit l’instruction Token-2022 `ConfidentialTransferExtension::TransferWithFee` avec cinq références de preuve strictement ordonnées : equality, validité trois handles, percentage with fee, validité deux handles et range U256. Chaque preuve peut être référencée par offset non nul ou par compte context-state. Le crate valide uniquement les entrées opaques fournies ; il ne produit aucune preuve ni donnée secrète. + +## Confidential Transfer Fee public operations (`pre.057`) + +The executor constructs the four proof-free public operations published by +`spl-token-2022-interface 3.1.1`: + +- initialize confidential transfer fee configuration before mint initialization; +- enable harvest to mint; +- disable harvest to mint; +- permissionless harvest of ordered, distinct source accounts to the mint. + +The initialization keeps the optional configuration authority and the exact +caller-provided 32-byte ElGamal public key. Harvest source lists use the same +non-empty, unique and `u8`-bounded contract as public transfer-fee harvests. +No ciphertext is decrypted or synthesized. + +- `withdraw_confidential_withheld_tokens_from_mint` : retrait depuis le Mint avec preuve `CiphertextCiphertextEquality` inline ou context-state. + + +## Confidential Transfer Fee — retrait multi-comptes (`pre.059`) + +`WithdrawConfidentialWithheldTokensFromAccounts` construit le retrait direct des frais confidentiels depuis une liste ordonnée de Token Accounts vers un compte destination Confidential Transfer. Le Mint reste readonly, la destination est writable, puis viennent la référence de preuve, l’autorité withdraw-withheld, les signataires multisig et enfin les sources writable. + +La liste de sources doit être non vide, unique et limitée à 255 comptes. Le nombre exact est encodé sur `u8` dans le payload. La preuve exigée est `CiphertextCiphertextEquality`, référencée par offset inline non nul ou par compte context-state. Cette opération est sensible au front-running : tout changement d’un withheld ciphertext source entre la génération de la preuve et l’exécution invalide la preuve. Le parcours recommandé pour réduire ce risque reste harvest vers Mint puis retrait depuis le Mint. diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/builder.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/builder.rs new file mode 100644 index 0000000..e3a9964 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/builder.rs @@ -0,0 +1,4937 @@ +// file: kb_executor_spl_token_2022/src/builder.rs +// version: 26 + +//! Official Token-2022 builders and conservative plan validation. + +use std::str::FromStr; // rust-rules: trait-import + +struct ParsedAuthority { + authority: solana_pubkey::Pubkey, + signers: std::vec::Vec, +} + +pub(crate) fn build_prepared_plan( + intent: &crate::SplToken2022ExecutionIntent, +) -> kb_core::Result { + if intent.intent_id.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_intent_id_empty", + "SPL Token execution intent id must not be empty", + )); + } + let fee_payer = match parse_pubkey(&intent.fee_payer, "fee_payer") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + match validate_policy(intent) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let program_id = + match solana_pubkey::Pubkey::from_str(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_program_id_invalid", + error.to_string(), + )); + }, + }; + let instruction = match &intent.operation { + crate::SplToken2022Operation::Instruction { value } => { + match build_single(&program_id, value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + }, + crate::SplToken2022Operation::Batch { instructions } => { + match build_batch(&program_id, instructions.as_slice()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + }, + }; + let requested_spend_lamports = match declared_spend_lamports(&intent.operation) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let planned_instruction = planned_instruction(intent.operation.operation_code(), &instruction); + let required_signers = required_signers(&intent.fee_payer, &instruction.accounts); + tracing::debug!( + target: crate::TRACING_TARGET, + action = "build_prepared_plan", + intent_id = %intent.intent_id, + operation_code = intent.operation.operation_code(), + program_id = kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + account_occurrence_count = instruction.accounts.len(), + required_signer_count = required_signers.len(), + requested_spend_lamports, + dry_run = intent.policy.dry_run, + "built Token-2022 execution plan" + ); + return std::result::Result::Ok(kb_execution_api::PreparedExecutionPlan { + executor_name: std::string::String::from("kb_executor_spl_token_2022"), + executor_version: std::string::String::from(env!("CARGO_PKG_VERSION")), + intent_id: intent.intent_id.clone(), + operation_code: std::string::String::from(intent.operation.operation_code()), + fee_payer: kb_model::Pubkey(fee_payer.to_string()), + instructions: vec![planned_instruction], + required_signers, + policy: intent.policy.clone(), + requested_spend_lamports, + requested_compute_unit_price_micro_lamports: std::option::Option::None, + }); +} + +fn validate_policy(intent: &crate::SplToken2022ExecutionIntent) -> kb_core::Result<()> { + if intent.policy.simulation != kb_execution_api::ExecutionSimulationPolicy::Required { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_simulation_required", + "SPL Token execution requires simulation before signing or sending", + )); + } + match intent.policy.cost_limit.max_fee_lamports { + std::option::Option::Some(limit) if limit > 0 => {}, + std::option::Option::Some(_) | std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_fee_limit_missing", + "SPL Token execution requires a positive transaction fee ceiling", + )); + }, + } + let validation = &intent.policy.post_execution_validation; + if !validation.canonical_insert_required + || !validation.core_extraction_required + || !validation.decode_replay_required + { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_post_validation_required", + "SPL Token execution requires canonical insertion, core extraction and decode replay validation", + )); + } + if intent.operation.requires_materialization() && !validation.materialization_required { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_materialization_validation_required", + "Mutating SPL Token execution requires materialization validation", + )); + } + if has_unbounded_lamport_movement(&intent.operation) && !intent.policy.dry_run { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_stateful_lamport_preflight_required", + "WithdrawExcessLamports and full-balance UnwrapLamports remain simulation-only until a stateful preflight bounds the movable lamports", + )); + } + return std::result::Result::Ok(()); +} + +macro_rules! parse_key { + ($value:expr, $field:expr) => { + match parse_pubkey($value, $field) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + }; +} + +macro_rules! parse_raw_amount { + ($value:expr, $field:expr) => { + match parse_amount($value, $field) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + }; +} + +macro_rules! parse_token_authority { + ($value:expr) => { + match parse_authority($value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + }; +} + +macro_rules! parse_optional_key { + ($value:expr, $field:expr) => { + match $value { + std::option::Option::Some(value) => { + std::option::Option::Some(parse_key!(value, $field)) + }, + std::option::Option::None => std::option::Option::None, + } + }; +} + +fn validate_metadata_string(value: &str, field: &str) -> kb_core::Result<()> { + if value.len() > 1_024 { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_metadata_string_too_long", + format!("Token metadata {field} exceeds the 1024-byte executor bound"), + )); + } + return std::result::Result::Ok(()); +} + +fn default_account_state( + state: crate::SplTokenDefaultAccountState, +) -> spl_token_2022_interface::state::AccountState { + return match state { + crate::SplTokenDefaultAccountState::Initialized => { + spl_token_2022_interface::state::AccountState::Initialized + }, + crate::SplTokenDefaultAccountState::Frozen => { + spl_token_2022_interface::state::AccountState::Frozen + }, + }; +} + +#[allow(deprecated)] +fn parse_multiplier(value: &crate::SplTokenMultiplier, field: &str) -> kb_core::Result { + let parsed = match value.0.parse::() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_multiplier_invalid", + format!("{field} must be a decimal f64: {error}"), + )); + }, + }; + if !parsed.is_finite() || !parsed.is_normal() || parsed <= 0.0 { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_multiplier_invalid", + format!("{field} must be positive, finite and normal"), + )); + } + return std::result::Result::Ok(parsed); +} + +#[allow(deprecated)] +fn build_single( + program_id: &solana_pubkey::Pubkey, + operation: &crate::SplTokenSingleOperation, +) -> kb_core::Result { + return match operation { + crate::SplTokenSingleOperation::InitializeMint { + mint, + mint_authority, + freeze_authority, + decimals, + } => { + let mint = parse_key!(mint, "mint"); + let mint_authority = parse_key!(mint_authority, "mint_authority"); + let freeze_authority = match freeze_authority { + std::option::Option::Some(value) => { + std::option::Option::Some(parse_key!(value, "freeze_authority")) + }, + std::option::Option::None => std::option::Option::None, + }; + official_instruction(spl_token_2022_interface::instruction::initialize_mint2( + program_id, + &mint, + &mint_authority, + freeze_authority.as_ref(), + *decimals, + )) + }, + crate::SplTokenSingleOperation::InitializeAccount { account, mint, owner } => { + let account = parse_key!(account, "account"); + let mint = parse_key!(mint, "mint"); + let owner = parse_key!(owner, "owner"); + official_instruction(spl_token_2022_interface::instruction::initialize_account3( + program_id, &account, &mint, &owner, + )) + }, + crate::SplTokenSingleOperation::InitializeMultisig { multisig, members, threshold } => { + if members.is_empty() + || members.len() > crate::MAX_MULTISIG_SIGNERS + || *threshold == 0 + || usize::from(*threshold) > members.len() + { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_multisig_threshold_invalid", + format!( + "SPL Token multisig requires 1..={} members and threshold 1..=N; received M={} N={}", + crate::MAX_MULTISIG_SIGNERS, + threshold, + members.len() + ), + )); + } + let multisig = parse_key!(multisig, "multisig"); + let mut parsed_members = std::vec::Vec::with_capacity(members.len()); + for member in members { + parsed_members.push(parse_key!(member, "multisig_member")); + } + let member_refs = parsed_members.iter().collect::>(); + official_instruction(spl_token_2022_interface::instruction::initialize_multisig2( + program_id, + &multisig, + member_refs.as_slice(), + *threshold, + )) + }, + crate::SplTokenSingleOperation::Transfer { source, destination, authority, amount } => { + let source = parse_key!(source, "source"); + let destination = parse_key!(destination, "destination"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction(spl_token_2022_interface::instruction::transfer( + program_id, + &source, + &destination, + &authority.authority, + signer_refs.as_slice(), + amount, + )) + }, + crate::SplTokenSingleOperation::Approve { source, delegate, authority, amount } => { + let source = parse_key!(source, "source"); + let delegate = parse_key!(delegate, "delegate"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction(spl_token_2022_interface::instruction::approve( + program_id, + &source, + &delegate, + &authority.authority, + signer_refs.as_slice(), + amount, + )) + }, + crate::SplTokenSingleOperation::Revoke { source, authority } => { + let source = parse_key!(source, "source"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction(spl_token_2022_interface::instruction::revoke( + program_id, + &source, + &authority.authority, + signer_refs.as_slice(), + )) + }, + crate::SplTokenSingleOperation::SetAuthority { + owned, + authority_type, + new_authority, + current_authority, + } => { + let owned = parse_key!(owned, "owned"); + let new_authority = match new_authority { + std::option::Option::Some(value) => { + std::option::Option::Some(parse_key!(value, "new_authority")) + }, + std::option::Option::None => std::option::Option::None, + }; + let current_authority = parse_token_authority!(current_authority); + let signer_refs = current_authority.signers.iter().collect::>(); + let authority_type = match authority_type { + crate::SplTokenAuthorityType::MintTokens => { + spl_token_2022_interface::instruction::AuthorityType::MintTokens + }, + crate::SplTokenAuthorityType::FreezeAccount => { + spl_token_2022_interface::instruction::AuthorityType::FreezeAccount + }, + crate::SplTokenAuthorityType::AccountOwner => { + spl_token_2022_interface::instruction::AuthorityType::AccountOwner + }, + crate::SplTokenAuthorityType::CloseAccount => { + spl_token_2022_interface::instruction::AuthorityType::CloseAccount + }, + }; + official_instruction(spl_token_2022_interface::instruction::set_authority( + program_id, + &owned, + new_authority.as_ref(), + authority_type, + ¤t_authority.authority, + signer_refs.as_slice(), + )) + }, + crate::SplTokenSingleOperation::MintTo { mint, destination, authority, amount } => { + let mint = parse_key!(mint, "mint"); + let destination = parse_key!(destination, "destination"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction(spl_token_2022_interface::instruction::mint_to( + program_id, + &mint, + &destination, + &authority.authority, + signer_refs.as_slice(), + amount, + )) + }, + crate::SplTokenSingleOperation::Burn { source, mint, authority, amount } => { + let source = parse_key!(source, "source"); + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction(spl_token_2022_interface::instruction::burn( + program_id, + &source, + &mint, + &authority.authority, + signer_refs.as_slice(), + amount, + )) + }, + crate::SplTokenSingleOperation::CloseAccount { account, destination, authority } => { + let account = parse_key!(account, "account"); + let destination = parse_key!(destination, "destination"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction(spl_token_2022_interface::instruction::close_account( + program_id, + &account, + &destination, + &authority.authority, + signer_refs.as_slice(), + )) + }, + crate::SplTokenSingleOperation::FreezeAccount { account, mint, authority } => { + let account = parse_key!(account, "account"); + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction(spl_token_2022_interface::instruction::freeze_account( + program_id, + &account, + &mint, + &authority.authority, + signer_refs.as_slice(), + )) + }, + crate::SplTokenSingleOperation::ThawAccount { account, mint, authority } => { + let account = parse_key!(account, "account"); + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction(spl_token_2022_interface::instruction::thaw_account( + program_id, + &account, + &mint, + &authority.authority, + signer_refs.as_slice(), + )) + }, + crate::SplTokenSingleOperation::TransferChecked { + source, + mint, + destination, + authority, + amount, + decimals, + } => { + let source = parse_key!(source, "source"); + let mint = parse_key!(mint, "mint"); + let destination = parse_key!(destination, "destination"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction(spl_token_2022_interface::instruction::transfer_checked( + program_id, + &source, + &mint, + &destination, + &authority.authority, + signer_refs.as_slice(), + amount, + *decimals, + )) + }, + crate::SplTokenSingleOperation::ApproveChecked { + source, + mint, + delegate, + authority, + amount, + decimals, + } => { + let source = parse_key!(source, "source"); + let mint = parse_key!(mint, "mint"); + let delegate = parse_key!(delegate, "delegate"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction(spl_token_2022_interface::instruction::approve_checked( + program_id, + &source, + &mint, + &delegate, + &authority.authority, + signer_refs.as_slice(), + amount, + *decimals, + )) + }, + crate::SplTokenSingleOperation::MintToChecked { + mint, + destination, + authority, + amount, + decimals, + } => { + let mint = parse_key!(mint, "mint"); + let destination = parse_key!(destination, "destination"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction(spl_token_2022_interface::instruction::mint_to_checked( + program_id, + &mint, + &destination, + &authority.authority, + signer_refs.as_slice(), + amount, + *decimals, + )) + }, + crate::SplTokenSingleOperation::BurnChecked { + source, + mint, + authority, + amount, + decimals, + } => { + let source = parse_key!(source, "source"); + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction(spl_token_2022_interface::instruction::burn_checked( + program_id, + &source, + &mint, + &authority.authority, + signer_refs.as_slice(), + amount, + *decimals, + )) + }, + crate::SplTokenSingleOperation::SyncNative { account, include_rent_sysvar } => { + let account = parse_key!(account, "account"); + if *include_rent_sysvar { + official_instruction( + spl_token_2022_interface::instruction::sync_native_with_rent_sysvar( + program_id, &account, + ), + ) + } else { + official_instruction(spl_token_2022_interface::instruction::sync_native( + program_id, &account, + )) + } + }, + crate::SplTokenSingleOperation::GetAccountDataSize { mint } => { + let mint = parse_key!(mint, "mint"); + official_instruction(spl_token_2022_interface::instruction::get_account_data_size( + program_id, + &mint, + &[], + )) + }, + crate::SplTokenSingleOperation::InitializeImmutableOwner { account } => { + let account = parse_key!(account, "account"); + official_instruction(spl_token_2022_interface::instruction::initialize_immutable_owner( + program_id, &account, + )) + }, + crate::SplTokenSingleOperation::AmountToUiAmount { mint, amount } => { + let mint = parse_key!(mint, "mint"); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction(spl_token_2022_interface::instruction::amount_to_ui_amount( + program_id, &mint, amount, + )) + }, + crate::SplTokenSingleOperation::UiAmountToAmount { mint, ui_amount } => { + if ui_amount.len() > crate::MAX_UI_AMOUNT_BYTES { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_ui_amount_too_large", + format!( + "SPL Token UI amount length {} exceeds {} bytes", + ui_amount.len(), + crate::MAX_UI_AMOUNT_BYTES + ), + )); + } + let mint = parse_key!(mint, "mint"); + official_instruction(spl_token_2022_interface::instruction::ui_amount_to_amount( + program_id, + &mint, + ui_amount.as_str(), + )) + }, + crate::SplTokenSingleOperation::WithdrawExcessLamports { + account, + destination, + authority, + } => { + let account = parse_key!(account, "account"); + let destination = parse_key!(destination, "destination"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction(spl_token_2022_interface::instruction::withdraw_excess_lamports( + program_id, + &account, + &destination, + &authority.authority, + signer_refs.as_slice(), + )) + }, + crate::SplTokenSingleOperation::Reallocate { + account, + payer, + authority, + extension_types, + } => { + let account = parse_key!(account, "account"); + let payer = parse_key!(payer, "payer"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let mut parsed_extension_types = std::vec::Vec::with_capacity(extension_types.len()); + for extension_type in extension_types { + let parsed = match spl_token_2022_interface::extension::ExtensionType::try_from( + *extension_type, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_extension_type_unknown", + format!("unknown Token-2022 extension type {extension_type}"), + )); + }, + }; + parsed_extension_types.push(parsed); + } + official_instruction(spl_token_2022_interface::instruction::reallocate( + program_id, + &account, + &payer, + &authority.authority, + signer_refs.as_slice(), + parsed_extension_types.as_slice(), + )) + }, + crate::SplTokenSingleOperation::InitializeNonTransferableMint { mint } => { + let mint = parse_key!(mint, "mint"); + official_instruction( + spl_token_2022_interface::instruction::initialize_non_transferable_mint( + program_id, &mint, + ), + ) + }, + crate::SplTokenSingleOperation::InitializePermanentDelegate { mint, delegate } => { + let mint = parse_key!(mint, "mint"); + let delegate = parse_key!(delegate, "delegate"); + official_instruction( + spl_token_2022_interface::instruction::initialize_permanent_delegate( + program_id, &mint, &delegate, + ), + ) + }, + crate::SplTokenSingleOperation::InitializeInterestBearingMint { + mint, + rate_authority, + rate_basis_points, + } => { + let mint = parse_key!(mint, "mint"); + let rate_authority = match rate_authority { + std::option::Option::Some(value) => { + std::option::Option::Some(parse_key!(value, "rate_authority")) + }, + std::option::Option::None => std::option::Option::None, + }; + official_instruction( + spl_token_2022_interface::extension::interest_bearing_mint::instruction::initialize( + program_id, + &mint, + rate_authority, + *rate_basis_points, + ), + ) + }, + crate::SplTokenSingleOperation::UpdateInterestBearingRate { + mint, + authority, + rate_basis_points, + } => { + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction( + spl_token_2022_interface::extension::interest_bearing_mint::instruction::update_rate( + program_id, + &mint, + &authority.authority, + signer_refs.as_slice(), + *rate_basis_points, + ), + ) + }, + crate::SplTokenSingleOperation::InitializeTransferFeeConfig { + mint, + transfer_fee_config_authority, + withdraw_withheld_authority, + transfer_fee_basis_points, + maximum_fee, + } => { + if *transfer_fee_basis_points + > spl_token_2022_interface::extension::transfer_fee::MAX_FEE_BASIS_POINTS + { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_transfer_fee_basis_points_invalid", + format!( + "transfer fee basis points {} exceed {}", + transfer_fee_basis_points, + spl_token_2022_interface::extension::transfer_fee::MAX_FEE_BASIS_POINTS + ), + )); + } + let mint = parse_key!(mint, "mint"); + let config_authority = match transfer_fee_config_authority { + std::option::Option::Some(value) => { + std::option::Option::Some(parse_key!(value, "transfer_fee_config_authority")) + }, + std::option::Option::None => std::option::Option::None, + }; + let withdraw_authority = match withdraw_withheld_authority { + std::option::Option::Some(value) => { + std::option::Option::Some(parse_key!(value, "withdraw_withheld_authority")) + }, + std::option::Option::None => std::option::Option::None, + }; + let maximum_fee = parse_raw_amount!(maximum_fee, "maximum_fee"); + official_instruction( + spl_token_2022_interface::extension::transfer_fee::instruction::initialize_transfer_fee_config( + program_id, + &mint, + config_authority.as_ref(), + withdraw_authority.as_ref(), + *transfer_fee_basis_points, + maximum_fee, + ), + ) + }, + crate::SplTokenSingleOperation::SetTransferFee { + mint, + authority, + transfer_fee_basis_points, + maximum_fee, + } => { + if *transfer_fee_basis_points + > spl_token_2022_interface::extension::transfer_fee::MAX_FEE_BASIS_POINTS + { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_transfer_fee_basis_points_invalid", + "transfer fee basis points exceed the official maximum", + )); + } + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let maximum_fee = parse_raw_amount!(maximum_fee, "maximum_fee"); + official_instruction( + spl_token_2022_interface::extension::transfer_fee::instruction::set_transfer_fee( + program_id, + &mint, + &authority.authority, + signer_refs.as_slice(), + *transfer_fee_basis_points, + maximum_fee, + ), + ) + }, + crate::SplTokenSingleOperation::TransferCheckedWithFee { + source, + mint, + destination, + authority, + amount, + decimals, + fee, + } => { + let source = parse_key!(source, "source"); + let mint = parse_key!(mint, "mint"); + let destination = parse_key!(destination, "destination"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + let fee = parse_raw_amount!(fee, "fee"); + official_instruction( + spl_token_2022_interface::extension::transfer_fee::instruction::transfer_checked_with_fee( + program_id, + &source, + &mint, + &destination, + &authority.authority, + signer_refs.as_slice(), + amount, + *decimals, + fee, + ), + ) + }, + crate::SplTokenSingleOperation::WithdrawWithheldTokensFromMint { + mint, + destination, + authority, + } => { + let mint = parse_key!(mint, "mint"); + let destination = parse_key!(destination, "destination"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction( + spl_token_2022_interface::extension::transfer_fee::instruction::withdraw_withheld_tokens_from_mint( + program_id, + &mint, + &destination, + &authority.authority, + signer_refs.as_slice(), + ), + ) + }, + crate::SplTokenSingleOperation::WithdrawWithheldTokensFromAccounts { + mint, + destination, + authority, + sources, + } => { + let sources = match parse_transfer_fee_sources(sources) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let source_refs = sources.iter().collect::>(); + let mint = parse_key!(mint, "mint"); + let destination = parse_key!(destination, "destination"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction( + spl_token_2022_interface::extension::transfer_fee::instruction::withdraw_withheld_tokens_from_accounts( + program_id, + &mint, + &destination, + &authority.authority, + signer_refs.as_slice(), + source_refs.as_slice(), + ), + ) + }, + crate::SplTokenSingleOperation::HarvestWithheldTokensToMint { mint, sources } => { + let sources = match parse_transfer_fee_sources(sources) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let source_refs = sources.iter().collect::>(); + let mint = parse_key!(mint, "mint"); + official_instruction( + spl_token_2022_interface::extension::transfer_fee::instruction::harvest_withheld_tokens_to_mint( + program_id, + &mint, + source_refs.as_slice(), + ), + ) + }, + crate::SplTokenSingleOperation::InitializeDefaultAccountState { mint, state } => { + let mint = parse_key!(mint, "mint"); + let state = default_account_state(*state); + official_instruction( + spl_token_2022_interface::extension::default_account_state::instruction::initialize_default_account_state( + program_id, + &mint, + &state, + ), + ) + }, + crate::SplTokenSingleOperation::UpdateDefaultAccountState { mint, authority, state } => { + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let state = default_account_state(*state); + official_instruction( + spl_token_2022_interface::extension::default_account_state::instruction::update_default_account_state( + program_id, + &mint, + &authority.authority, + signer_refs.as_slice(), + &state, + ), + ) + }, + crate::SplTokenSingleOperation::EnableRequiredTransferMemos { account, authority } => { + let account = parse_key!(account, "account"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction( + spl_token_2022_interface::extension::memo_transfer::instruction::enable_required_transfer_memos( + program_id, + &account, + &authority.authority, + signer_refs.as_slice(), + ), + ) + }, + crate::SplTokenSingleOperation::DisableRequiredTransferMemos { account, authority } => { + let account = parse_key!(account, "account"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction( + spl_token_2022_interface::extension::memo_transfer::instruction::disable_required_transfer_memos( + program_id, + &account, + &authority.authority, + signer_refs.as_slice(), + ), + ) + }, + crate::SplTokenSingleOperation::EnableCpiGuard { account, authority } => { + let account = parse_key!(account, "account"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction( + spl_token_2022_interface::extension::cpi_guard::instruction::enable_cpi_guard( + program_id, + &account, + &authority.authority, + signer_refs.as_slice(), + ), + ) + }, + crate::SplTokenSingleOperation::DisableCpiGuard { account, authority } => { + let account = parse_key!(account, "account"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction( + spl_token_2022_interface::extension::cpi_guard::instruction::disable_cpi_guard( + program_id, + &account, + &authority.authority, + signer_refs.as_slice(), + ), + ) + }, + crate::SplTokenSingleOperation::InitializeTransferHook { + mint, + authority, + program_id: hook_program_id, + } => { + let mint = parse_key!(mint, "mint"); + let authority = match authority { + std::option::Option::Some(value) => { + std::option::Option::Some(parse_key!(value, "transfer_hook_authority")) + }, + std::option::Option::None => std::option::Option::None, + }; + let hook_program_id = match hook_program_id { + std::option::Option::Some(value) => { + std::option::Option::Some(parse_key!(value, "transfer_hook_program_id")) + }, + std::option::Option::None => std::option::Option::None, + }; + official_instruction( + spl_token_2022_interface::extension::transfer_hook::instruction::initialize( + program_id, + &mint, + authority, + hook_program_id, + ), + ) + }, + crate::SplTokenSingleOperation::UpdateTransferHook { + mint, + authority, + program_id: hook_program_id, + } => { + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let hook_program_id = match hook_program_id { + std::option::Option::Some(value) => { + std::option::Option::Some(parse_key!(value, "transfer_hook_program_id")) + }, + std::option::Option::None => std::option::Option::None, + }; + official_instruction( + spl_token_2022_interface::extension::transfer_hook::instruction::update( + program_id, + &mint, + &authority.authority, + signer_refs.as_slice(), + hook_program_id, + ), + ) + }, + crate::SplTokenSingleOperation::InitializeMetadataPointer { + mint, + authority, + metadata_address, + } => { + let mint = parse_key!(mint, "mint"); + let authority = parse_optional_key!(authority, "metadata_pointer_authority"); + let metadata_address = parse_optional_key!(metadata_address, "metadata_address"); + official_instruction( + spl_token_2022_interface::extension::metadata_pointer::instruction::initialize( + program_id, + &mint, + authority, + metadata_address, + ), + ) + }, + crate::SplTokenSingleOperation::UpdateMetadataPointer { + mint, + authority, + metadata_address, + } => { + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let metadata_address = parse_optional_key!(metadata_address, "metadata_address"); + official_instruction( + spl_token_2022_interface::extension::metadata_pointer::instruction::update( + program_id, + &mint, + &authority.authority, + signer_refs.as_slice(), + metadata_address, + ), + ) + }, + crate::SplTokenSingleOperation::InitializeGroupPointer { + mint, + authority, + group_address, + } => { + let mint = parse_key!(mint, "mint"); + let authority = parse_optional_key!(authority, "group_pointer_authority"); + let group_address = parse_optional_key!(group_address, "group_address"); + official_instruction( + spl_token_2022_interface::extension::group_pointer::instruction::initialize( + program_id, + &mint, + authority, + group_address, + ), + ) + }, + crate::SplTokenSingleOperation::UpdateGroupPointer { mint, authority, group_address } => { + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let group_address = parse_optional_key!(group_address, "group_address"); + official_instruction( + spl_token_2022_interface::extension::group_pointer::instruction::update( + program_id, + &mint, + &authority.authority, + signer_refs.as_slice(), + group_address, + ), + ) + }, + crate::SplTokenSingleOperation::InitializeGroupMemberPointer { + mint, + authority, + member_address, + } => { + let mint = parse_key!(mint, "mint"); + let authority = parse_optional_key!(authority, "group_member_pointer_authority"); + let member_address = parse_optional_key!(member_address, "group_member_address"); + official_instruction( + spl_token_2022_interface::extension::group_member_pointer::instruction::initialize( + program_id, + &mint, + authority, + member_address, + ), + ) + }, + crate::SplTokenSingleOperation::UpdateGroupMemberPointer { + mint, + authority, + member_address, + } => { + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let member_address = parse_optional_key!(member_address, "group_member_address"); + official_instruction( + spl_token_2022_interface::extension::group_member_pointer::instruction::update( + program_id, + &mint, + &authority.authority, + signer_refs.as_slice(), + member_address, + ), + ) + }, + crate::SplTokenSingleOperation::InitializeTokenMetadata { + metadata, + update_authority, + mint, + mint_authority, + name, + symbol, + uri, + } => { + match validate_metadata_string(name, "name") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match validate_metadata_string(symbol, "symbol") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match validate_metadata_string(uri, "uri") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let metadata = parse_key!(metadata, "metadata"); + let update_authority = parse_key!(update_authority, "update_authority"); + let mint = parse_key!(mint, "mint"); + let mint_authority = parse_key!(mint_authority, "mint_authority"); + std::result::Result::Ok(spl_token_metadata_interface::instruction::initialize( + program_id, + &metadata, + &update_authority, + &mint, + &mint_authority, + name.clone(), + symbol.clone(), + uri.clone(), + )) + }, + crate::SplTokenSingleOperation::UpdateTokenMetadataField { + metadata, + update_authority, + field, + value, + } => { + match validate_metadata_string(value, "value") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let metadata = parse_key!(metadata, "metadata"); + let update_authority = parse_key!(update_authority, "update_authority"); + let field = match field { + crate::SplTokenMetadataField::Name => { + spl_token_metadata_interface::state::Field::Name + }, + crate::SplTokenMetadataField::Symbol => { + spl_token_metadata_interface::state::Field::Symbol + }, + crate::SplTokenMetadataField::Uri => { + spl_token_metadata_interface::state::Field::Uri + }, + crate::SplTokenMetadataField::Key(key) => { + match validate_metadata_string(key, "key") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + spl_token_metadata_interface::state::Field::Key(key.clone()) + }, + }; + std::result::Result::Ok(spl_token_metadata_interface::instruction::update_field( + program_id, + &metadata, + &update_authority, + field, + value.clone(), + )) + }, + crate::SplTokenSingleOperation::RemoveTokenMetadataKey { + metadata, + update_authority, + key, + idempotent, + } => { + match validate_metadata_string(key, "key") { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let metadata = parse_key!(metadata, "metadata"); + let update_authority = parse_key!(update_authority, "update_authority"); + std::result::Result::Ok(spl_token_metadata_interface::instruction::remove_key( + program_id, + &metadata, + &update_authority, + key.clone(), + *idempotent, + )) + }, + crate::SplTokenSingleOperation::InitializeTokenGroup { + group, + mint, + mint_authority, + update_authority, + max_size, + } => { + let group = parse_key!(group, "group"); + let mint = parse_key!(mint, "mint"); + let mint_authority = parse_key!(mint_authority, "mint_authority"); + let update_authority = parse_optional_key!(update_authority, "group_update_authority"); + std::result::Result::Ok(spl_token_group_interface::instruction::initialize_group( + program_id, + &group, + &mint, + &mint_authority, + update_authority, + *max_size, + )) + }, + crate::SplTokenSingleOperation::UpdateTokenGroupMaxSize { + group, + update_authority, + max_size, + } => { + let group = parse_key!(group, "group"); + let update_authority = parse_key!(update_authority, "group_update_authority"); + std::result::Result::Ok(spl_token_group_interface::instruction::update_group_max_size( + program_id, + &group, + &update_authority, + *max_size, + )) + }, + crate::SplTokenSingleOperation::InitializeTokenGroupMember { + member, + member_mint, + member_mint_authority, + group, + group_update_authority, + } => { + let member = parse_key!(member, "member"); + let member_mint = parse_key!(member_mint, "member_mint"); + let member_mint_authority = parse_key!(member_mint_authority, "member_mint_authority"); + let group = parse_key!(group, "group"); + let group_update_authority = + parse_key!(group_update_authority, "group_update_authority"); + std::result::Result::Ok(spl_token_group_interface::instruction::initialize_member( + program_id, + &member, + &member_mint, + &member_mint_authority, + &group, + &group_update_authority, + )) + }, + crate::SplTokenSingleOperation::InitializeScaledUiAmount { + mint, + authority, + multiplier, + } => { + let mint = parse_key!(mint, "mint"); + let authority = match authority { + std::option::Option::Some(value) => { + std::option::Option::Some(parse_key!(value, "authority")) + }, + std::option::Option::None => std::option::Option::None, + }; + let multiplier = match parse_multiplier(multiplier, "multiplier") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + official_instruction( + spl_token_2022_interface::extension::scaled_ui_amount::instruction::initialize( + program_id, &mint, authority, multiplier, + ), + ) + }, + crate::SplTokenSingleOperation::UpdateScaledUiAmountMultiplier { + mint, + authority, + multiplier, + effective_timestamp, + } => { + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let multiplier = match parse_multiplier(multiplier, "multiplier") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + official_instruction( + spl_token_2022_interface::extension::scaled_ui_amount::instruction::update_multiplier( + program_id, &mint, &authority.authority, signer_refs.as_slice(), multiplier, *effective_timestamp, + ), + ) + }, + crate::SplTokenSingleOperation::InitializePausableConfig { mint, authority } => { + let mint = parse_key!(mint, "mint"); + let authority = parse_key!(authority, "authority"); + official_instruction( + spl_token_2022_interface::extension::pausable::instruction::initialize( + program_id, &mint, &authority, + ), + ) + }, + crate::SplTokenSingleOperation::Pause { mint, authority } => { + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction(spl_token_2022_interface::extension::pausable::instruction::pause( + program_id, + &mint, + &authority.authority, + signer_refs.as_slice(), + )) + }, + crate::SplTokenSingleOperation::Resume { mint, authority } => { + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction( + spl_token_2022_interface::extension::pausable::instruction::resume( + program_id, + &mint, + &authority.authority, + signer_refs.as_slice(), + ), + ) + }, + crate::SplTokenSingleOperation::InitializePermissionedBurn { mint, authority } => { + let mint = parse_key!(mint, "mint"); + let authority = parse_key!(authority, "authority"); + official_instruction( + spl_token_2022_interface::extension::permissioned_burn::instruction::initialize( + program_id, &mint, &authority, + ), + ) + }, + crate::SplTokenSingleOperation::PermissionedBurn { + source, + mint, + permissioned_burn_authority, + authority, + amount, + } => { + let source = parse_key!(source, "source"); + let mint = parse_key!(mint, "mint"); + let permissioned_burn_authority = + parse_key!(permissioned_burn_authority, "permissioned_burn_authority"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction( + spl_token_2022_interface::extension::permissioned_burn::instruction::burn( + program_id, + &source, + &mint, + &permissioned_burn_authority, + &authority.authority, + signer_refs.as_slice(), + amount, + ), + ) + }, + crate::SplTokenSingleOperation::PermissionedBurnChecked { + source, + mint, + permissioned_burn_authority, + authority, + amount, + decimals, + } => { + let source = parse_key!(source, "source"); + let mint = parse_key!(mint, "mint"); + let permissioned_burn_authority = + parse_key!(permissioned_burn_authority, "permissioned_burn_authority"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction( + spl_token_2022_interface::extension::permissioned_burn::instruction::burn_checked( + program_id, + &source, + &mint, + &permissioned_burn_authority, + &authority.authority, + signer_refs.as_slice(), + amount, + *decimals, + ), + ) + }, + crate::SplTokenSingleOperation::InitializeConfidentialTransferMint { + mint, + authority, + auto_approve_new_accounts, + auditor_elgamal_pubkey, + } => { + let mint = parse_key!(mint, "mint"); + let authority = match authority { + Some(value) => Some(parse_key!(value, "authority")), + None => None, + }; + let auditor_elgamal_pubkey = match auditor_elgamal_pubkey { + Some(value) => match value.parse() { + Ok(parsed) => Some(parsed), + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_invalid_elgamal_pubkey", + error, + )); + }, + }, + None => None, + }; + official_instruction( + spl_token_2022_interface::extension::confidential_transfer::instruction::initialize_mint( + program_id, &mint, authority, *auto_approve_new_accounts, auditor_elgamal_pubkey, + ), + ) + }, + crate::SplTokenSingleOperation::UpdateConfidentialTransferMint { + mint, + authority, + auto_approve_new_accounts, + auditor_elgamal_pubkey, + } => { + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let auditor_elgamal_pubkey = match auditor_elgamal_pubkey { + Some(value) => match value.parse() { + Ok(parsed) => Some(parsed), + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_invalid_elgamal_pubkey", + error, + )); + }, + }, + None => None, + }; + official_instruction( + spl_token_2022_interface::extension::confidential_transfer::instruction::update_mint( + program_id, &mint, &authority.authority, signer_refs.as_slice(), + *auto_approve_new_accounts, auditor_elgamal_pubkey, + ), + ) + }, + crate::SplTokenSingleOperation::ApplyPendingConfidentialBalance { + account, + expected_pending_balance_credit_counter, + new_decryptable_available_balance, + authority, + } => { + let account = parse_key!(account, "account"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let balance = match new_decryptable_available_balance.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_invalid_decryptable_balance", + error, + )); + }, + }; + official_instruction( + spl_token_2022_interface::extension::confidential_transfer::instruction::apply_pending_balance( + program_id, &account, *expected_pending_balance_credit_counter, &balance, + &authority.authority, signer_refs.as_slice(), + ), + ) + }, + crate::SplTokenSingleOperation::ApproveConfidentialTransferAccount { + account, + mint, + authority, + } => { + let account = parse_key!(account, "account"); + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction( + spl_token_2022_interface::extension::confidential_transfer::instruction::approve_account( + program_id, &account, &mint, &authority.authority, signer_refs.as_slice(), + ), + ) + }, + crate::SplTokenSingleOperation::DepositConfidentialTokens { + account, + mint, + authority, + amount, + decimals, + } => { + let account = parse_key!(account, "account"); + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount = parse_raw_amount!(amount, "amount"); + official_instruction( + spl_token_2022_interface::extension::confidential_transfer::instruction::deposit( + program_id, + &account, + &mint, + amount, + *decimals, + &authority.authority, + signer_refs.as_slice(), + ), + ) + }, + crate::SplTokenSingleOperation::EnableConfidentialCredits { account, authority } => { + let account = parse_key!(account, "account"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction( + spl_token_2022_interface::extension::confidential_transfer::instruction::enable_confidential_credits( + program_id, &account, &authority.authority, signer_refs.as_slice(), + ), + ) + }, + crate::SplTokenSingleOperation::DisableConfidentialCredits { account, authority } => { + let account = parse_key!(account, "account"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction( + spl_token_2022_interface::extension::confidential_transfer::instruction::disable_confidential_credits( + program_id, &account, &authority.authority, signer_refs.as_slice(), + ), + ) + }, + crate::SplTokenSingleOperation::EnableNonConfidentialCredits { account, authority } => { + let account = parse_key!(account, "account"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction( + spl_token_2022_interface::extension::confidential_transfer::instruction::enable_non_confidential_credits( + program_id, &account, &authority.authority, signer_refs.as_slice(), + ), + ) + }, + crate::SplTokenSingleOperation::DisableNonConfidentialCredits { account, authority } => { + let account = parse_key!(account, "account"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction( + spl_token_2022_interface::extension::confidential_transfer::instruction::disable_non_confidential_credits( + program_id, &account, &authority.authority, signer_refs.as_slice(), + ), + ) + }, + crate::SplTokenSingleOperation::ConfigureConfidentialTransferAccount { + account, + mint, + decryptable_zero_balance, + maximum_pending_balance_credit_counter, + authority, + proof, + } => { + if proof.kind != crate::SplTokenConfidentialProofKind::PubkeyValidity { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_kind_invalid", + "ConfigureAccount requires exactly one PubkeyValidity proof reference", + )); + } + match proof.location.validate() { + Ok(()) => {}, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_location_invalid", + error, + )); + }, + } + let account = parse_key!(account, "account"); + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let decryptable_zero_balance = match decryptable_zero_balance.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_invalid_decryptable_balance", + error, + )); + }, + }; + let proof_data = ::zeroed(); + let context_account; + let proof_location = match &proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_offset_invalid", + "ConfigureAccount inline proof offset must be non-zero", + )); + }, + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &proof_data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + context_account = parse_key!(account, "proof_context_state_account"); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&context_account) + }, + }; + official_instruction( + spl_token_2022_interface::extension::confidential_transfer::instruction::inner_configure_account( + program_id, + &account, + &mint, + &decryptable_zero_balance, + *maximum_pending_balance_credit_counter, + &authority.authority, + signer_refs.as_slice(), + proof_location, + ), + ) + }, + crate::SplTokenSingleOperation::ConfigureConfidentialTransferAccountWithRegistry { + account, + mint, + registry_account, + payer, + } => { + let account = parse_key!(account, "account"); + let mint = parse_key!(mint, "mint"); + let registry_account = parse_key!(registry_account, "registry_account"); + let payer = match payer { + Some(value) => Some(parse_key!(value, "payer")), + None => None, + }; + official_instruction( + spl_token_2022_interface::extension::confidential_transfer::instruction::configure_account_with_registry( + program_id, + &account, + &mint, + ®istry_account, + payer.as_ref(), + ), + ) + }, + crate::SplTokenSingleOperation::EmptyConfidentialTransferAccount { + account, + authority, + proof, + } => { + if proof.kind != crate::SplTokenConfidentialProofKind::ZeroCiphertext { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_kind_invalid", + "EmptyAccount requires exactly one ZeroCiphertext proof reference", + )); + } + match proof.location.validate() { + Ok(()) => {}, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_location_invalid", + error, + )); + }, + } + let account = parse_key!(account, "account"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let proof_data = ::zeroed(); + let context_account; + let proof_location = match &proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_offset_invalid", + "EmptyAccount inline proof offset must be non-zero", + )); + }, + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &proof_data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + context_account = parse_key!(account, "proof_context_state_account"); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&context_account) + }, + }; + official_instruction( + spl_token_2022_interface::extension::confidential_transfer::instruction::inner_empty_account( + program_id, + &account, + &authority.authority, + signer_refs.as_slice(), + proof_location, + ), + ) + }, + crate::SplTokenSingleOperation::WithdrawConfidentialTokens { + account, + mint, + amount, + decimals, + new_decryptable_available_balance, + authority, + equality_proof, + range_proof, + } => { + if equality_proof.kind + != crate::SplTokenConfidentialProofKind::CiphertextCommitmentEquality + || range_proof.kind != crate::SplTokenConfidentialProofKind::BatchedRangeProofU64 + { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_kind_invalid", + "Withdraw requires CiphertextCommitmentEquality then BatchedRangeProofU64 proof references", + )); + } + for proof in [equality_proof, range_proof] { + match proof.location.validate() { + Ok(()) => {}, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_location_invalid", + error, + )); + }, + } + } + let account = parse_key!(account, "account"); + let mint = parse_key!(mint, "mint"); + let amount = parse_raw_amount!(amount, "amount"); + let new_balance = match new_decryptable_available_balance.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_decryptable_balance_invalid", + error, + )); + }, + }; + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let equality_data = ::zeroed(); + let range_data = ::zeroed(); + let equality_context; + let range_context; + let equality_location = match &equality_proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_offset_invalid", + "Withdraw equality proof offset must be non-zero", + )); + }, + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &equality_data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + equality_context = parse_key!(account, "equality_proof_context_state_account"); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&equality_context) + }, + }; + let range_location = match &range_proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_offset_invalid", + "Withdraw range proof offset must be non-zero", + )); + }, + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &range_data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + range_context = parse_key!(account, "range_proof_context_state_account"); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&range_context) + }, + }; + official_instruction( + spl_token_2022_interface::extension::confidential_transfer::instruction::inner_withdraw( + program_id, + &account, + &mint, + amount, + *decimals, + &new_balance, + &authority.authority, + signer_refs.as_slice(), + equality_location, + range_location, + ), + ) + }, + crate::SplTokenSingleOperation::TransferConfidentialTokens { + source, + mint, + destination, + new_source_decryptable_available_balance, + transfer_amount_auditor_ciphertext_lo, + transfer_amount_auditor_ciphertext_hi, + authority, + equality_proof, + ciphertext_validity_proof, + range_proof, + } => { + if equality_proof.kind != crate::SplTokenConfidentialProofKind::CiphertextCommitmentEquality + || ciphertext_validity_proof.kind != crate::SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity + || range_proof.kind != crate::SplTokenConfidentialProofKind::BatchedRangeProofU128 + { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_kind_invalid", + "Transfer requires CiphertextCommitmentEquality, BatchedGroupedCiphertext3HandlesValidity, then BatchedRangeProofU128 proof references", + )); + } + for proof in [equality_proof, ciphertext_validity_proof, range_proof] { + match proof.location.validate() { + Ok(()) => {}, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_location_invalid", + error, + )); + }, + } + } + let source = parse_key!(source, "source"); + let mint = parse_key!(mint, "mint"); + let destination = parse_key!(destination, "destination"); + let new_balance = match new_source_decryptable_available_balance.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_decryptable_balance_invalid", + error, + )); + }, + }; + let auditor_ciphertext_lo = match transfer_amount_auditor_ciphertext_lo.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_ciphertext_invalid", + error, + )); + }, + }; + let auditor_ciphertext_hi = match transfer_amount_auditor_ciphertext_hi.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_ciphertext_invalid", + error, + )); + }, + }; + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let equality_data = ::zeroed(); + let validity_data = ::zeroed(); + let range_data = ::zeroed(); + let equality_context; + let validity_context; + let range_context; + let equality_location = match &equality_proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_offset_invalid", + "Transfer equality proof offset must be non-zero", + )); + }, + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &equality_data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + equality_context = parse_key!(account, "equality_proof_context_state_account"); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&equality_context) + }, + }; + let validity_location = match &ciphertext_validity_proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_offset_invalid", + "Transfer ciphertext-validity proof offset must be non-zero", + )); + }, + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &validity_data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + validity_context = + parse_key!(account, "ciphertext_validity_proof_context_state_account"); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&validity_context) + }, + }; + let range_location = match &range_proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_offset_invalid", + "Transfer range proof offset must be non-zero", + )); + }, + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &range_data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + range_context = parse_key!(account, "range_proof_context_state_account"); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&range_context) + }, + }; + official_instruction( + spl_token_2022_interface::extension::confidential_transfer::instruction::inner_transfer( + program_id, + &source, + &mint, + &destination, + &new_balance, + &auditor_ciphertext_lo, + &auditor_ciphertext_hi, + &authority.authority, + signer_refs.as_slice(), + equality_location, + validity_location, + range_location, + ), + ) + }, + crate::SplTokenSingleOperation::TransferConfidentialTokensWithFee { + source, + mint, + destination, + new_source_decryptable_available_balance, + transfer_amount_auditor_ciphertext_lo, + transfer_amount_auditor_ciphertext_hi, + authority, + equality_proof, + transfer_amount_ciphertext_validity_proof, + fee_sigma_proof, + fee_ciphertext_validity_proof, + range_proof, + } => { + let expected = [ + crate::SplTokenConfidentialProofKind::CiphertextCommitmentEquality, + crate::SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity, + crate::SplTokenConfidentialProofKind::PercentageWithFee, + crate::SplTokenConfidentialProofKind::BatchedGroupedCiphertext2HandlesValidity, + crate::SplTokenConfidentialProofKind::BatchedRangeProofU256, + ]; + let proofs = [ + equality_proof, + transfer_amount_ciphertext_validity_proof, + fee_sigma_proof, + fee_ciphertext_validity_proof, + range_proof, + ]; + for (proof, kind) in proofs.iter().zip(expected.iter()) { + if proof.kind != *kind { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_kind_invalid", + "TransferWithFee requires Equality, 3-handles validity, PercentageWithFee, 2-handles validity, then U256 range proof references", + )); + } + match proof.location.validate() { + Ok(()) => {}, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_location_invalid", + error, + )); + }, + } + } + let source = parse_key!(source, "source"); + let mint = parse_key!(mint, "mint"); + let destination = parse_key!(destination, "destination"); + let new_balance = match new_source_decryptable_available_balance.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_decryptable_balance_invalid", + error, + )); + }, + }; + let auditor_ciphertext_lo = match transfer_amount_auditor_ciphertext_lo.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_ciphertext_invalid", + error, + )); + }, + }; + let auditor_ciphertext_hi = match transfer_amount_auditor_ciphertext_hi.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_ciphertext_invalid", + error, + )); + }, + }; + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let equality_data = ::zeroed(); + let transfer_validity_data = ::zeroed(); + let fee_sigma_data = ::zeroed(); + let fee_validity_data = ::zeroed(); + let range_data = ::zeroed(); + let equality_context; + let transfer_validity_context; + let fee_sigma_context; + let fee_validity_context; + let range_context; + macro_rules! proof_location { + ($proof:expr, $data:expr, $context:ident, $field:literal) => {{ + match &$proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => return Err(kb_core::Error::new("execution_spl_token_2022_confidential_proof_offset_invalid", concat!($field, " offset must be non-zero"))), + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, $data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + $context = parse_key!(account, concat!($field, "_context_state_account")); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&$context) + }, + } + }}; + } + let equality_location = + proof_location!(equality_proof, &equality_data, equality_context, "equality_proof"); + let transfer_validity_location = proof_location!( + transfer_amount_ciphertext_validity_proof, + &transfer_validity_data, + transfer_validity_context, + "transfer_validity_proof" + ); + let fee_sigma_location = proof_location!( + fee_sigma_proof, + &fee_sigma_data, + fee_sigma_context, + "fee_sigma_proof" + ); + let fee_validity_location = proof_location!( + fee_ciphertext_validity_proof, + &fee_validity_data, + fee_validity_context, + "fee_validity_proof" + ); + let range_location = + proof_location!(range_proof, &range_data, range_context, "range_proof"); + official_instruction( + spl_token_2022_interface::extension::confidential_transfer::instruction::inner_transfer_with_fee( + program_id, &source, &mint, &destination, &new_balance, + &auditor_ciphertext_lo, &auditor_ciphertext_hi, &authority.authority, + signer_refs.as_slice(), equality_location, transfer_validity_location, + fee_sigma_location, fee_validity_location, range_location, + ), + ) + }, + crate::SplTokenSingleOperation::InitializeConfidentialTransferFeeConfig { + mint, + authority, + withdraw_withheld_authority_elgamal_pubkey, + } => { + let mint = parse_key!(mint, "mint"); + let authority = match authority { + Some(value) => Some(parse_key!(value, "authority")), + None => None, + }; + let withdraw_key = match withdraw_withheld_authority_elgamal_pubkey.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_invalid_elgamal_pubkey", + error, + )); + }, + }; + official_instruction( + spl_token_2022_interface::extension::confidential_transfer_fee::instruction::initialize_confidential_transfer_fee_config( + program_id, + &mint, + authority, + &withdraw_key, + ), + ) + }, + crate::SplTokenSingleOperation::EnableConfidentialTransferFeeHarvest { + mint, + authority, + } => { + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction( + spl_token_2022_interface::extension::confidential_transfer_fee::instruction::enable_harvest_to_mint( + program_id, + &mint, + &authority.authority, + signer_refs.as_slice(), + ), + ) + }, + crate::SplTokenSingleOperation::DisableConfidentialTransferFeeHarvest { + mint, + authority, + } => { + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction( + spl_token_2022_interface::extension::confidential_transfer_fee::instruction::disable_harvest_to_mint( + program_id, + &mint, + &authority.authority, + signer_refs.as_slice(), + ), + ) + }, + crate::SplTokenSingleOperation::HarvestConfidentialWithheldTokensToMint { + mint, + sources, + } => { + let sources = match parse_transfer_fee_sources(sources) { + Ok(value) => value, + Err(error) => return Err(error), + }; + let source_refs = sources.iter().collect::>(); + let mint = parse_key!(mint, "mint"); + official_instruction( + spl_token_2022_interface::extension::confidential_transfer_fee::instruction::harvest_withheld_tokens_to_mint( + program_id, + &mint, + source_refs.as_slice(), + ), + ) + }, + crate::SplTokenSingleOperation::WithdrawConfidentialWithheldTokensFromMint { + mint, + destination, + new_decryptable_available_balance, + authority, + proof, + } => { + if proof.kind != crate::SplTokenConfidentialProofKind::CiphertextCiphertextEquality { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_kind_invalid", + "confidential fee withdraw from mint requires exactly one CiphertextCiphertextEquality proof reference", + )); + } + match proof.location.validate() { + Ok(()) => {}, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_location_invalid", + error, + )); + }, + } + let mint = parse_key!(mint, "mint"); + let destination = parse_key!(destination, "destination"); + let new_balance = match new_decryptable_available_balance.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_invalid_decryptable_balance", + error, + )); + }, + }; + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let proof_data = ::zeroed(); + let context_account; + let proof_location = match &proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_offset_invalid", + "confidential fee withdraw from mint inline proof offset must be non-zero", + )); + }, + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &proof_data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + context_account = parse_key!(account, "proof_context_state_account"); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&context_account) + }, + }; + official_instruction( + spl_token_2022_interface::extension::confidential_transfer_fee::instruction::inner_withdraw_withheld_tokens_from_mint( + program_id, + &mint, + &destination, + &new_balance, + &authority.authority, + signer_refs.as_slice(), + proof_location, + ), + ) + }, + crate::SplTokenSingleOperation::WithdrawConfidentialWithheldTokensFromAccounts { + mint, + destination, + new_decryptable_available_balance, + authority, + sources, + proof, + } => { + if proof.kind != crate::SplTokenConfidentialProofKind::CiphertextCiphertextEquality { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_kind_invalid", + "confidential fee withdraw from accounts requires exactly one CiphertextCiphertextEquality proof reference", + )); + } + match proof.location.validate() { + Ok(()) => {}, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_location_invalid", + error, + )); + }, + } + let sources = match parse_transfer_fee_sources(sources) { + Ok(value) => value, + Err(error) => return Err(error), + }; + let source_refs = sources.iter().collect::>(); + let mint = parse_key!(mint, "mint"); + let destination = parse_key!(destination, "destination"); + let new_balance = match new_decryptable_available_balance.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_invalid_decryptable_balance", + error, + )); + }, + }; + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let proof_data = ::zeroed(); + let context_account; + let proof_location = match &proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_offset_invalid", + "confidential fee withdraw from accounts inline proof offset must be non-zero", + )); + }, + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &proof_data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + context_account = parse_key!(account, "proof_context_state_account"); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&context_account) + }, + }; + official_instruction( + spl_token_2022_interface::extension::confidential_transfer_fee::instruction::inner_withdraw_withheld_tokens_from_accounts( + program_id, + &mint, + &destination, + &new_balance, + &authority.authority, + signer_refs.as_slice(), + source_refs.as_slice(), + proof_location, + ), + ) + }, + crate::SplTokenSingleOperation::InitializeConfidentialMintBurnMint { + mint, + supply_elgamal_pubkey, + decryptable_supply, + } => { + let mint = parse_key!(mint, "mint"); + let supply_key = match supply_elgamal_pubkey.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_invalid_elgamal_pubkey", + error, + )); + }, + }; + let decryptable_supply = match decryptable_supply.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_invalid_decryptable_balance", + error, + )); + }, + }; + official_instruction( + spl_token_2022_interface::extension::confidential_mint_burn::instruction::initialize_mint( + program_id, + &mint, + &supply_key, + &decryptable_supply, + ), + ) + }, + crate::SplTokenSingleOperation::RotateConfidentialSupplyElGamalPubkey { + mint, + new_supply_elgamal_pubkey, + authority, + proof, + } => { + if proof.kind != crate::SplTokenConfidentialProofKind::CiphertextCiphertextEquality { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_kind_invalid", + "confidential supply-key rotation requires exactly one CiphertextCiphertextEquality proof reference", + )); + } + match proof.location.validate() { + Ok(()) => {}, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_location_invalid", + error, + )); + }, + } + let mint = parse_key!(mint, "mint"); + let new_supply_key = match new_supply_elgamal_pubkey.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_invalid_elgamal_pubkey", + error, + )); + }, + }; + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let proof_data = ::zeroed(); + let context_account; + let proof_location = match &proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_offset_invalid", + "confidential supply-key rotation inline proof offset must be non-zero", + )); + }, + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &proof_data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + context_account = parse_key!(account, "proof_context_state_account"); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&context_account) + }, + }; + let instructions = match spl_token_2022_interface::extension::confidential_mint_burn::instruction::rotate_supply_elgamal_pubkey( + program_id, + &mint, + &authority.authority, + signer_refs.as_slice(), + &new_supply_key, + proof_location, + ) { + Ok(value) => value, + Err(error) => return Err(kb_core::Error::new( + "execution_spl_token_2022_official_builder_failed", + error.to_string(), + )), + }; + match instructions.into_iter().next() { + Some(value) => Ok(value), + None => Err(kb_core::Error::new( + "execution_spl_token_2022_official_builder_empty", + "official confidential supply-key rotation builder returned no instruction", + )), + } + }, + crate::SplTokenSingleOperation::UpdateConfidentialDecryptableSupply { + mint, + new_decryptable_supply, + authority, + } => { + let mint = parse_key!(mint, "mint"); + let new_supply = match new_decryptable_supply.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_invalid_decryptable_balance", + error, + )); + }, + }; + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction( + spl_token_2022_interface::extension::confidential_mint_burn::instruction::update_decryptable_supply( + program_id, + &mint, + &authority.authority, + signer_refs.as_slice(), + &new_supply, + ), + ) + }, + crate::SplTokenSingleOperation::ApplyPendingConfidentialBurn { mint, authority } => { + let mint = parse_key!(mint, "mint"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + official_instruction( + spl_token_2022_interface::extension::confidential_mint_burn::instruction::apply_pending_burn( + program_id, + &mint, + &authority.authority, + signer_refs.as_slice(), + ), + ) + }, + crate::SplTokenSingleOperation::ConfidentialMint { + destination, + mint, + new_decryptable_supply, + mint_amount_auditor_ciphertext_lo, + mint_amount_auditor_ciphertext_hi, + authority, + equality_proof, + ciphertext_validity_proof, + range_proof, + } => { + if equality_proof.kind != crate::SplTokenConfidentialProofKind::CiphertextCommitmentEquality + || ciphertext_validity_proof.kind != crate::SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity + || range_proof.kind != crate::SplTokenConfidentialProofKind::BatchedRangeProofU128 + { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_kind_invalid", + "Confidential Mint requires CiphertextCommitmentEquality, BatchedGroupedCiphertext3HandlesValidity, then BatchedRangeProofU128 proof references", + )); + } + for proof in [equality_proof, ciphertext_validity_proof, range_proof] { + match proof.location.validate() { + Ok(()) => {}, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_location_invalid", + error, + )); + }, + } + } + let destination = parse_key!(destination, "destination"); + let mint = parse_key!(mint, "mint"); + let new_supply = match new_decryptable_supply.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_invalid_decryptable_balance", + error, + )); + }, + }; + let auditor_ciphertext_lo = match mint_amount_auditor_ciphertext_lo.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_ciphertext_invalid", + error, + )); + }, + }; + let auditor_ciphertext_hi = match mint_amount_auditor_ciphertext_hi.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_ciphertext_invalid", + error, + )); + }, + }; + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let equality_data = ::zeroed(); + let validity_data = ::zeroed(); + let range_data = ::zeroed(); + let equality_context; + let validity_context; + let range_context; + let equality_location = match &equality_proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_offset_invalid", + "Confidential Mint equality proof offset must be non-zero", + )); + }, + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &equality_data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + equality_context = parse_key!(account, "equality_proof_context_state_account"); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&equality_context) + }, + }; + let validity_location = match &ciphertext_validity_proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_offset_invalid", + "Confidential Mint ciphertext-validity proof offset must be non-zero", + )); + }, + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &validity_data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + validity_context = + parse_key!(account, "ciphertext_validity_proof_context_state_account"); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&validity_context) + }, + }; + let range_location = match &range_proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_offset_invalid", + "Confidential Mint range proof offset must be non-zero", + )); + }, + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &range_data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + range_context = parse_key!(account, "range_proof_context_state_account"); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&range_context) + }, + }; + official_instruction( + spl_token_2022_interface::extension::confidential_mint_burn::instruction::inner_confidential_mint( + program_id, + &destination, + &mint, + &auditor_ciphertext_lo, + &auditor_ciphertext_hi, + &authority.authority, + signer_refs.as_slice(), + equality_location, + validity_location, + range_location, + &new_supply, + ), + ) + }, + crate::SplTokenSingleOperation::ConfidentialBurn { + source, + mint, + new_decryptable_available_balance, + burn_amount_auditor_ciphertext_lo, + burn_amount_auditor_ciphertext_hi, + authority, + equality_proof, + ciphertext_validity_proof, + range_proof, + } => { + if equality_proof.kind != crate::SplTokenConfidentialProofKind::CiphertextCommitmentEquality + || ciphertext_validity_proof.kind != crate::SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity + || range_proof.kind != crate::SplTokenConfidentialProofKind::BatchedRangeProofU128 + { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_kind_invalid", + "Confidential Burn requires CiphertextCommitmentEquality, BatchedGroupedCiphertext3HandlesValidity, then BatchedRangeProofU128 proof references", + )); + } + for proof in [equality_proof, ciphertext_validity_proof, range_proof] { + match proof.location.validate() { + Ok(()) => {}, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_location_invalid", + error, + )); + }, + } + } + let source = parse_key!(source, "source"); + let mint = parse_key!(mint, "mint"); + let new_balance = match new_decryptable_available_balance.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_invalid_decryptable_balance", + error, + )); + }, + }; + let auditor_ciphertext_lo = match burn_amount_auditor_ciphertext_lo.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_ciphertext_invalid", + error, + )); + }, + }; + let auditor_ciphertext_hi = match burn_amount_auditor_ciphertext_hi.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_ciphertext_invalid", + error, + )); + }, + }; + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let equality_data = ::zeroed(); + let validity_data = ::zeroed(); + let range_data = ::zeroed(); + let equality_context; + let validity_context; + let range_context; + let equality_location = match &equality_proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_offset_invalid", + "Confidential Burn equality proof offset must be non-zero", + )); + }, + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &equality_data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + equality_context = parse_key!(account, "equality_proof_context_state_account"); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&equality_context) + }, + }; + let validity_location = match &ciphertext_validity_proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_offset_invalid", + "Confidential Burn ciphertext-validity proof offset must be non-zero", + )); + }, + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &validity_data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + validity_context = + parse_key!(account, "ciphertext_validity_proof_context_state_account"); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&validity_context) + }, + }; + let range_location = match &range_proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_offset_invalid", + "Confidential Burn range proof offset must be non-zero", + )); + }, + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &range_data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + range_context = parse_key!(account, "range_proof_context_state_account"); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&range_context) + }, + }; + official_instruction( + spl_token_2022_interface::extension::confidential_mint_burn::instruction::inner_confidential_burn( + program_id, + &source, + &mint, + &new_balance, + &auditor_ciphertext_lo, + &auditor_ciphertext_hi, + &authority.authority, + signer_refs.as_slice(), + equality_location, + validity_location, + range_location, + ), + ) + }, + crate::SplTokenSingleOperation::PermissionedConfidentialBurn { + source, + mint, + permissioned_burn_authority, + new_decryptable_available_balance, + burn_amount_auditor_ciphertext_lo, + burn_amount_auditor_ciphertext_hi, + authority, + equality_proof, + ciphertext_validity_proof, + range_proof, + } => { + if equality_proof.kind != crate::SplTokenConfidentialProofKind::CiphertextCommitmentEquality + || ciphertext_validity_proof.kind != crate::SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity + || range_proof.kind != crate::SplTokenConfidentialProofKind::BatchedRangeProofU128 + { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_kind_invalid", + "Permissioned Confidential Burn requires CiphertextCommitmentEquality, BatchedGroupedCiphertext3HandlesValidity, then BatchedRangeProofU128 proof references", + )); + } + for proof in [equality_proof, ciphertext_validity_proof, range_proof] { + match proof.location.validate() { + Ok(()) => {}, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_location_invalid", + error, + )); + }, + } + } + let source = parse_key!(source, "source"); + let mint = parse_key!(mint, "mint"); + let permissioned_burn_authority = + parse_key!(permissioned_burn_authority, "permissioned_burn_authority"); + let new_balance = match new_decryptable_available_balance.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_invalid_decryptable_balance", + error, + )); + }, + }; + let auditor_ciphertext_lo = match burn_amount_auditor_ciphertext_lo.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_ciphertext_invalid", + error, + )); + }, + }; + let auditor_ciphertext_hi = match burn_amount_auditor_ciphertext_hi.parse() { + Ok(value) => value, + Err(error) => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_ciphertext_invalid", + error, + )); + }, + }; + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let equality_data = ::zeroed(); + let validity_data = ::zeroed(); + let range_data = ::zeroed(); + let equality_context; + let validity_context; + let range_context; + let equality_location = match &equality_proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_offset_invalid", + "Permissioned Confidential Burn equality proof offset must be non-zero", + )); + }, + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &equality_data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + equality_context = parse_key!(account, "equality_proof_context_state_account"); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&equality_context) + }, + }; + let validity_location = match &ciphertext_validity_proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_offset_invalid", + "Permissioned Confidential Burn validity proof offset must be non-zero", + )); + }, + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &validity_data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + validity_context = + parse_key!(account, "ciphertext_validity_proof_context_state_account"); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&validity_context) + }, + }; + let range_location = match &range_proof.location { + crate::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + let offset = match std::num::NonZeroI8::new(*offset) { + Some(value) => value, + None => { + return Err(kb_core::Error::new( + "execution_spl_token_2022_confidential_proof_offset_invalid", + "Permissioned Confidential Burn range proof offset must be non-zero", + )); + }, + }; + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::InstructionOffset(offset, &range_data) + }, + crate::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + range_context = parse_key!(account, "range_proof_context_state_account"); + spl_token_confidential_transfer_proof_extraction::instruction::ProofLocation::ContextStateAccount(&range_context) + }, + }; + let instructions = match spl_token_2022_interface::extension::permissioned_burn::instruction::confidential_burn_with_split_proofs( + program_id, + &source, + &mint, + &permissioned_burn_authority, + &new_balance, + &auditor_ciphertext_lo, + &auditor_ciphertext_hi, + &authority.authority, + signer_refs.as_slice(), + equality_location, + validity_location, + range_location, + ) { + Ok(value) => value, + Err(error) => return Err(kb_core::Error::new( + "execution_spl_token_2022_official_builder_failed", + error.to_string(), + )), + }; + match instructions.into_iter().next() { + Some(value) => Ok(value), + None => Err(kb_core::Error::new( + "execution_spl_token_2022_official_builder_empty", + "official permissioned confidential burn builder returned no instruction", + )), + } + }, + crate::SplTokenSingleOperation::UnwrapLamports { + account, + destination, + authority, + amount_lamports, + } => { + let account = parse_key!(account, "account"); + let destination = parse_key!(destination, "destination"); + let authority = parse_token_authority!(authority); + let signer_refs = authority.signers.iter().collect::>(); + let amount_lamports = match amount_lamports { + std::option::Option::Some(value) => { + std::option::Option::Some(parse_raw_amount!(value, "amount_lamports")) + }, + std::option::Option::None => std::option::Option::None, + }; + official_instruction(spl_token_2022_interface::instruction::unwrap_lamports( + program_id, + &account, + &destination, + &authority.authority, + signer_refs.as_slice(), + amount_lamports, + )) + }, + }; +} + +fn build_batch( + _program_id: &solana_pubkey::Pubkey, + _instructions: &[crate::SplTokenSingleOperation], +) -> kb_core::Result { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_batch_builder_unavailable", + "Token-2022 Batch is decode-only because spl-token-2022-interface 3.1.1 publishes no official Batch builder", + )); +} + +fn official_instruction( + result: std::result::Result, +) -> kb_core::Result +where + BuilderError: std::fmt::Display, +{ + return match result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_official_builder_failed", + error.to_string(), + )), + }; +} + +fn parse_pubkey(pubkey: &kb_model::Pubkey, field: &str) -> kb_core::Result { + if pubkey.0.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_pubkey_empty", + format!("SPL Token {field} public key must not be empty"), + )); + } + return match solana_pubkey::Pubkey::from_str(pubkey.0.as_str()) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_pubkey_invalid", + format!("invalid SPL Token {field} public key: {error}"), + )), + }; +} + +fn parse_amount(amount: &crate::SplTokenAmount, field: &str) -> kb_core::Result { + if amount.0.is_empty() || !amount.0.bytes().all(|value| return value.is_ascii_digit()) { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_amount_invalid", + format!("SPL Token {field} must be an unsigned decimal u64 string"), + )); + } + return match amount.0.parse::() { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_amount_invalid", + format!("invalid SPL Token {field}: {error}"), + )), + }; +} + +fn parse_authority(authority: &crate::SplTokenAuthority) -> kb_core::Result { + if authority.multisig_signers.len() > crate::MAX_MULTISIG_SIGNERS { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_multisig_signer_limit_exceeded", + format!( + "SPL Token multisig signer occurrence count {} exceeds {}", + authority.multisig_signers.len(), + crate::MAX_MULTISIG_SIGNERS + ), + )); + } + let parsed_authority = match parse_pubkey(&authority.authority, "authority") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut signers = std::vec::Vec::with_capacity(authority.multisig_signers.len()); + for signer in &authority.multisig_signers { + let signer = match parse_pubkey(signer, "multisig_signer") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + signers.push(signer); + } + return std::result::Result::Ok(ParsedAuthority { authority: parsed_authority, signers }); +} + +fn parse_transfer_fee_sources( + sources: &[kb_model::Pubkey], +) -> kb_core::Result> { + if sources.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_transfer_fee_sources_empty", + "transfer-fee source account list must not be empty", + )); + } + if sources.len() > crate::MAX_TRANSFER_FEE_SOURCE_ACCOUNTS { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_transfer_fee_source_limit_exceeded", + format!( + "transfer-fee source account count {} exceeds {}", + sources.len(), + crate::MAX_TRANSFER_FEE_SOURCE_ACCOUNTS + ), + )); + } + let mut parsed = std::vec::Vec::with_capacity(sources.len()); + let mut unique = std::collections::BTreeSet::new(); + for source in sources { + let source = match parse_pubkey(source, "transfer_fee_source") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if !unique.insert(source) { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_transfer_fee_source_duplicate", + "transfer-fee source accounts must be unique", + )); + } + parsed.push(source); + } + return std::result::Result::Ok(parsed); +} + +fn declared_spend_lamports(operation: &crate::SplToken2022Operation) -> kb_core::Result { + return match operation { + crate::SplToken2022Operation::Instruction { value } => single_spend_lamports(value), + crate::SplToken2022Operation::Batch { instructions } => { + let mut total = 0_u64; + for instruction in instructions { + let amount = match single_spend_lamports(instruction) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + total = match total.checked_add(amount) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_lamport_spend_overflow", + "SPL Token Batch declared lamport spend exceeds u64", + )); + }, + }; + } + std::result::Result::Ok(total) + }, + }; +} + +fn single_spend_lamports(operation: &crate::SplTokenSingleOperation) -> kb_core::Result { + return match operation { + crate::SplTokenSingleOperation::UnwrapLamports { + amount_lamports: std::option::Option::Some(value), + .. + } => parse_amount(value, "amount_lamports"), + _ => std::result::Result::Ok(0), + }; +} + +fn has_unbounded_lamport_movement(operation: &crate::SplToken2022Operation) -> bool { + return match operation { + crate::SplToken2022Operation::Instruction { value } => { + single_has_unbounded_lamport_movement(value) + }, + crate::SplToken2022Operation::Batch { instructions } => { + instructions.iter().any(single_has_unbounded_lamport_movement) + }, + }; +} + +fn single_has_unbounded_lamport_movement(operation: &crate::SplTokenSingleOperation) -> bool { + return matches!( + operation, + crate::SplTokenSingleOperation::WithdrawExcessLamports { .. } + | crate::SplTokenSingleOperation::UnwrapLamports { + amount_lamports: std::option::Option::None, + .. + } + ); +} + +fn planned_instruction( + operation_code: &str, + instruction: &solana_instruction::Instruction, +) -> kb_execution_api::PlannedInstruction { + return kb_execution_api::PlannedInstruction { + program_id: kb_model::ProgramId(instruction.program_id.to_string()), + operation_code: std::string::String::from(operation_code), + accounts: instruction + .accounts + .iter() + .map(|account| { + return kb_execution_api::PlannedAccount { + pubkey: kb_model::Pubkey(account.pubkey.to_string()), + is_signer: account.is_signer, + is_writable: account.is_writable, + }; + }) + .collect(), + data: instruction.data.clone(), + }; +} + +fn required_signers( + fee_payer: &kb_model::Pubkey, + accounts: &[solana_instruction::AccountMeta], +) -> std::vec::Vec { + let mut required = vec![kb_execution_api::RequiredSigner { + pubkey: fee_payer.clone(), + role: std::string::String::from("fee_payer"), + }]; + for account in accounts { + if !account.is_signer { + continue; + } + let pubkey = kb_model::Pubkey(account.pubkey.to_string()); + if required.iter().any(|value| return value.pubkey == pubkey) { + continue; + } + required.push(kb_execution_api::RequiredSigner { + pubkey, + role: std::string::String::from("token_authority_or_multisig_signer"), + }); + } + return required; +} + +#[cfg(test)] +pub(crate) mod tests { + fn pubkey(value: &str) -> kb_model::Pubkey { + return kb_model::Pubkey(std::string::String::from(value)); + } + + fn policy( + signers: &[&str], + materialization_required: bool, + ) -> kb_execution_api::ExecutionPolicy { + return kb_execution_api::ExecutionPolicy { + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(u64::MAX), + max_fee_lamports: std::option::Option::Some(10_000), + max_compute_unit_price_micro_lamports: std::option::Option::None, + }, + authorized_signers: signers.iter().map(|value| return pubkey(value)).collect(), + post_execution_validation: kb_execution_api::PostExecutionValidationPolicy { + canonical_insert_required: true, + core_extraction_required: true, + decode_replay_required: true, + materialization_required, + }, + ..kb_execution_api::ExecutionPolicy::default() + }; + } + + pub(crate) fn intent( + operation: crate::SplToken2022Operation, + ) -> crate::SplToken2022ExecutionIntent { + let fee_payer = kb_program_ids::SYSTEM_PROGRAM_ID; + return crate::SplToken2022ExecutionIntent { + intent_id: std::string::String::from("token-intent-1"), + fee_payer: pubkey(fee_payer), + policy: policy( + &[fee_payer, kb_program_ids::VOTE_PROGRAM_ID, kb_program_ids::STAKE_PROGRAM_ID], + operation.requires_materialization(), + ), + operation, + }; + } + + fn authority(signers: &[&str]) -> crate::SplTokenAuthority { + return crate::SplTokenAuthority { + authority: pubkey(kb_program_ids::CONFIG_PROGRAM_ID), + multisig_signers: signers.iter().map(|value| return pubkey(value)).collect(), + }; + } + + fn planned_single_instruction( + operation: crate::SplTokenSingleOperation, + ) -> kb_execution_api::PlannedInstruction { + let intent = intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(operation), + }); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &intent, + ) + .unwrap_or_else(|error| panic!("single instruction plan failed: {error}")); + return plan + .instructions + .into_iter() + .next() + .unwrap_or_else(|| panic!("single instruction plan must contain one instruction")); + } + + fn transfer_checked() -> crate::SplTokenSingleOperation { + return crate::SplTokenSingleOperation::TransferChecked { + source: pubkey(kb_program_ids::SYSTEM_PROGRAM_ID), + mint: pubkey(kb_program_ids::CONFIG_PROGRAM_ID), + destination: pubkey(kb_program_ids::STAKE_PROGRAM_ID), + authority: authority(&[kb_program_ids::VOTE_PROGRAM_ID]), + amount: crate::SplTokenAmount(std::string::String::from("18446744073709551615")), + decimals: 9, + }; + } + + fn all_single_operations() -> std::vec::Vec<(u8, crate::SplTokenSingleOperation)> { + let first = pubkey(kb_program_ids::SYSTEM_PROGRAM_ID); + let second = pubkey(kb_program_ids::CONFIG_PROGRAM_ID); + let third = pubkey(kb_program_ids::STAKE_PROGRAM_ID); + let sauthority = authority(&[]); + let amount = crate::SplTokenAmount(std::string::String::from("42")); + return vec![ + ( + 20, + crate::SplTokenSingleOperation::InitializeMint { + mint: first.clone(), + mint_authority: second.clone(), + freeze_authority: std::option::Option::Some(third.clone()), + decimals: 9, + }, + ), + ( + 18, + crate::SplTokenSingleOperation::InitializeAccount { + account: first.clone(), + mint: second.clone(), + owner: third.clone(), + }, + ), + ( + 19, + crate::SplTokenSingleOperation::InitializeMultisig { + multisig: first.clone(), + members: vec![second.clone(), third.clone()], + threshold: 1, + }, + ), + ( + 3, + crate::SplTokenSingleOperation::Transfer { + source: first.clone(), + destination: second.clone(), + authority: sauthority.clone(), + amount: amount.clone(), + }, + ), + ( + 4, + crate::SplTokenSingleOperation::Approve { + source: first.clone(), + delegate: second.clone(), + authority: sauthority.clone(), + amount: amount.clone(), + }, + ), + ( + 5, + crate::SplTokenSingleOperation::Revoke { + source: first.clone(), + authority: sauthority.clone(), + }, + ), + ( + 6, + crate::SplTokenSingleOperation::SetAuthority { + owned: first.clone(), + authority_type: crate::SplTokenAuthorityType::CloseAccount, + new_authority: std::option::Option::Some(second.clone()), + current_authority: sauthority.clone(), + }, + ), + ( + 7, + crate::SplTokenSingleOperation::MintTo { + mint: first.clone(), + destination: second.clone(), + authority: sauthority.clone(), + amount: amount.clone(), + }, + ), + ( + 8, + crate::SplTokenSingleOperation::Burn { + source: first.clone(), + mint: second.clone(), + authority: sauthority.clone(), + amount: amount.clone(), + }, + ), + ( + 9, + crate::SplTokenSingleOperation::CloseAccount { + account: first.clone(), + destination: second.clone(), + authority: sauthority.clone(), + }, + ), + ( + 10, + crate::SplTokenSingleOperation::FreezeAccount { + account: first.clone(), + mint: second.clone(), + authority: sauthority.clone(), + }, + ), + ( + 11, + crate::SplTokenSingleOperation::ThawAccount { + account: first.clone(), + mint: second.clone(), + authority: sauthority.clone(), + }, + ), + (12, transfer_checked()), + ( + 13, + crate::SplTokenSingleOperation::ApproveChecked { + source: first.clone(), + mint: second.clone(), + delegate: third.clone(), + authority: sauthority.clone(), + amount: amount.clone(), + decimals: 9, + }, + ), + ( + 14, + crate::SplTokenSingleOperation::MintToChecked { + mint: first.clone(), + destination: second.clone(), + authority: sauthority.clone(), + amount: amount.clone(), + decimals: 9, + }, + ), + ( + 15, + crate::SplTokenSingleOperation::BurnChecked { + source: first.clone(), + mint: second.clone(), + authority: sauthority.clone(), + amount: amount.clone(), + decimals: 9, + }, + ), + ( + 17, + crate::SplTokenSingleOperation::SyncNative { + account: first.clone(), + include_rent_sysvar: true, + }, + ), + (21, crate::SplTokenSingleOperation::GetAccountDataSize { mint: first.clone() }), + ( + 22, + crate::SplTokenSingleOperation::InitializeImmutableOwner { account: first.clone() }, + ), + ( + 23, + crate::SplTokenSingleOperation::AmountToUiAmount { + mint: first.clone(), + amount: amount.clone(), + }, + ), + ( + 24, + crate::SplTokenSingleOperation::UiAmountToAmount { + mint: first.clone(), + ui_amount: std::string::String::from("0.000000042"), + }, + ), + ( + 29, + crate::SplTokenSingleOperation::Reallocate { + account: first.clone(), + payer: second.clone(), + authority: sauthority.clone(), + extension_types: vec![9, 10, 12], + }, + ), + ( + 32, + crate::SplTokenSingleOperation::InitializeNonTransferableMint { + mint: first.clone(), + }, + ), + ( + 35, + crate::SplTokenSingleOperation::InitializePermanentDelegate { + mint: first.clone(), + delegate: second.clone(), + }, + ), + ( + 33, + crate::SplTokenSingleOperation::InitializeInterestBearingMint { + mint: first.clone(), + rate_authority: std::option::Option::Some(second.clone()), + rate_basis_points: -125, + }, + ), + ( + 33, + crate::SplTokenSingleOperation::UpdateInterestBearingRate { + mint: first.clone(), + authority: sauthority.clone(), + rate_basis_points: 250, + }, + ), + ( + 38, + crate::SplTokenSingleOperation::WithdrawExcessLamports { + account: first.clone(), + destination: second.clone(), + authority: sauthority.clone(), + }, + ), + ( + 26, + crate::SplTokenSingleOperation::InitializeTransferFeeConfig { + mint: first.clone(), + transfer_fee_config_authority: std::option::Option::Some(second.clone()), + withdraw_withheld_authority: std::option::Option::Some(third.clone()), + transfer_fee_basis_points: 250, + maximum_fee: amount.clone(), + }, + ), + ( + 26, + crate::SplTokenSingleOperation::SetTransferFee { + mint: first.clone(), + authority: sauthority.clone(), + transfer_fee_basis_points: 500, + maximum_fee: amount.clone(), + }, + ), + ( + 26, + crate::SplTokenSingleOperation::TransferCheckedWithFee { + source: first.clone(), + mint: second.clone(), + destination: third.clone(), + authority: sauthority.clone(), + amount: amount.clone(), + decimals: 9, + fee: crate::SplTokenAmount(std::string::String::from("1")), + }, + ), + ( + 26, + crate::SplTokenSingleOperation::WithdrawWithheldTokensFromMint { + mint: first.clone(), + destination: second.clone(), + authority: sauthority.clone(), + }, + ), + ( + 26, + crate::SplTokenSingleOperation::WithdrawWithheldTokensFromAccounts { + mint: first.clone(), + destination: second.clone(), + authority: sauthority.clone(), + sources: vec![third.clone()], + }, + ), + ( + 26, + crate::SplTokenSingleOperation::HarvestWithheldTokensToMint { + mint: first.clone(), + sources: vec![third.clone()], + }, + ), + ( + 28, + crate::SplTokenSingleOperation::InitializeDefaultAccountState { + mint: first.clone(), + state: crate::SplTokenDefaultAccountState::Frozen, + }, + ), + ( + 28, + crate::SplTokenSingleOperation::UpdateDefaultAccountState { + mint: first.clone(), + authority: sauthority.clone(), + state: crate::SplTokenDefaultAccountState::Initialized, + }, + ), + ( + 30, + crate::SplTokenSingleOperation::EnableRequiredTransferMemos { + account: first.clone(), + authority: sauthority.clone(), + }, + ), + ( + 30, + crate::SplTokenSingleOperation::DisableRequiredTransferMemos { + account: first.clone(), + authority: sauthority.clone(), + }, + ), + ( + 34, + crate::SplTokenSingleOperation::EnableCpiGuard { + account: first.clone(), + authority: sauthority.clone(), + }, + ), + ( + 34, + crate::SplTokenSingleOperation::DisableCpiGuard { + account: first.clone(), + authority: sauthority.clone(), + }, + ), + ( + 36, + crate::SplTokenSingleOperation::InitializeTransferHook { + mint: first.clone(), + authority: std::option::Option::Some(second.clone()), + program_id: std::option::Option::Some(third.clone()), + }, + ), + ( + 36, + crate::SplTokenSingleOperation::UpdateTransferHook { + mint: first.clone(), + authority: sauthority.clone(), + program_id: std::option::Option::None, + }, + ), + ( + 39, + crate::SplTokenSingleOperation::InitializeMetadataPointer { + mint: first.clone(), + authority: std::option::Option::Some(second.clone()), + metadata_address: std::option::Option::Some(third.clone()), + }, + ), + ( + 39, + crate::SplTokenSingleOperation::UpdateMetadataPointer { + mint: first.clone(), + authority: sauthority.clone(), + metadata_address: std::option::Option::None, + }, + ), + ( + 40, + crate::SplTokenSingleOperation::InitializeGroupPointer { + mint: first.clone(), + authority: std::option::Option::Some(second.clone()), + group_address: std::option::Option::Some(third.clone()), + }, + ), + ( + 40, + crate::SplTokenSingleOperation::UpdateGroupPointer { + mint: first.clone(), + authority: sauthority.clone(), + group_address: std::option::Option::None, + }, + ), + ( + 41, + crate::SplTokenSingleOperation::InitializeGroupMemberPointer { + mint: first.clone(), + authority: std::option::Option::Some(second.clone()), + member_address: std::option::Option::Some(third.clone()), + }, + ), + ( + 41, + crate::SplTokenSingleOperation::UpdateGroupMemberPointer { + mint: first.clone(), + authority: sauthority.clone(), + member_address: std::option::Option::None, + }, + ), + ( + 255, + crate::SplTokenSingleOperation::InitializeTokenMetadata { + metadata: first.clone(), + update_authority: second.clone(), + mint: first.clone(), + mint_authority: third.clone(), + name: std::string::String::from("Token"), + symbol: std::string::String::from("TKN"), + uri: std::string::String::from("https://example.invalid/token.json"), + }, + ), + ( + 255, + crate::SplTokenSingleOperation::UpdateTokenMetadataField { + metadata: first.clone(), + update_authority: second.clone(), + field: crate::SplTokenMetadataField::Uri, + value: std::string::String::from("https://example.invalid/updated.json"), + }, + ), + ( + 255, + crate::SplTokenSingleOperation::RemoveTokenMetadataKey { + metadata: first.clone(), + update_authority: second.clone(), + key: std::string::String::from("website"), + idempotent: true, + }, + ), + ( + 255, + crate::SplTokenSingleOperation::InitializeTokenGroup { + group: first.clone(), + mint: first.clone(), + mint_authority: second.clone(), + update_authority: std::option::Option::Some(third.clone()), + max_size: 42, + }, + ), + ( + 255, + crate::SplTokenSingleOperation::UpdateTokenGroupMaxSize { + group: first.clone(), + update_authority: second.clone(), + max_size: 84, + }, + ), + ( + 255, + crate::SplTokenSingleOperation::InitializeTokenGroupMember { + member: first.clone(), + member_mint: first.clone(), + member_mint_authority: second.clone(), + group: third.clone(), + group_update_authority: second.clone(), + }, + ), + ( + 43, + crate::SplTokenSingleOperation::InitializeScaledUiAmount { + mint: first.clone(), + authority: std::option::Option::Some(second.clone()), + multiplier: crate::SplTokenMultiplier(std::string::String::from("1.5")), + }, + ), + ( + 43, + crate::SplTokenSingleOperation::UpdateScaledUiAmountMultiplier { + mint: first.clone(), + authority: sauthority.clone(), + multiplier: crate::SplTokenMultiplier(std::string::String::from("2.0")), + effective_timestamp: 1_900_000_000, + }, + ), + ( + 44, + crate::SplTokenSingleOperation::InitializePausableConfig { + mint: first.clone(), + authority: second.clone(), + }, + ), + ( + 44, + crate::SplTokenSingleOperation::Pause { + mint: first.clone(), + authority: sauthority.clone(), + }, + ), + ( + 44, + crate::SplTokenSingleOperation::Resume { + mint: first.clone(), + authority: sauthority.clone(), + }, + ), + ( + 46, + crate::SplTokenSingleOperation::InitializePermissionedBurn { + mint: first.clone(), + authority: second.clone(), + }, + ), + ( + 46, + crate::SplTokenSingleOperation::PermissionedBurn { + source: first.clone(), + mint: second.clone(), + permissioned_burn_authority: third.clone(), + authority: sauthority.clone(), + amount: amount.clone(), + }, + ), + ( + 46, + crate::SplTokenSingleOperation::PermissionedBurnChecked { + source: first.clone(), + mint: second.clone(), + permissioned_burn_authority: third.clone(), + authority: sauthority.clone(), + amount: amount.clone(), + decimals: 9, + }, + ), + ( + 27, + crate::SplTokenSingleOperation::InitializeConfidentialTransferMint { + mint: first.clone(), + authority: Some(second.clone()), + auto_approve_new_accounts: true, + auditor_elgamal_pubkey: Some(crate::SplTokenElGamalPubkey("11".repeat(32))), + }, + ), + ( + 27, + crate::SplTokenSingleOperation::UpdateConfidentialTransferMint { + mint: first.clone(), + authority: sauthority.clone(), + auto_approve_new_accounts: false, + auditor_elgamal_pubkey: None, + }, + ), + ( + 27, + crate::SplTokenSingleOperation::ApplyPendingConfidentialBalance { + account: first.clone(), + expected_pending_balance_credit_counter: u64::MAX, + new_decryptable_available_balance: crate::SplTokenDecryptableBalance("22".repeat(36)), + authority: sauthority.clone(), + }, + ), + ( + 27, + crate::SplTokenSingleOperation::ApproveConfidentialTransferAccount { + account: first.clone(), + mint: second.clone(), + authority: sauthority.clone(), + }, + ), + ( + 27, + crate::SplTokenSingleOperation::DepositConfidentialTokens { + account: first.clone(), + mint: second.clone(), + authority: sauthority.clone(), + amount: amount.clone(), + decimals: 9, + }, + ), + ( + 27, + crate::SplTokenSingleOperation::EnableConfidentialCredits { + account: first.clone(), + authority: sauthority.clone(), + }, + ), + ( + 27, + crate::SplTokenSingleOperation::DisableConfidentialCredits { + account: first.clone(), + authority: sauthority.clone(), + }, + ), + ( + 27, + crate::SplTokenSingleOperation::EnableNonConfidentialCredits { + account: first.clone(), + authority: sauthority.clone(), + }, + ), + ( + 27, + crate::SplTokenSingleOperation::DisableNonConfidentialCredits { + account: first.clone(), + authority: sauthority.clone(), + }, + ), + ( + 27, + crate::SplTokenSingleOperation::ConfigureConfidentialTransferAccount { + account: first.clone(), + mint: second.clone(), + decryptable_zero_balance: crate::SplTokenDecryptableBalance("33".repeat(36)), + maximum_pending_balance_credit_counter: u64::MAX, + authority: sauthority.clone(), + proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::PubkeyValidity, + location: crate::SplTokenConfidentialProofLocation::ContextStateAccount { + account: third.clone(), + }, + }, + }, + ), + ( + 27, + crate::SplTokenSingleOperation::ConfigureConfidentialTransferAccountWithRegistry { + account: first.clone(), + mint: second.clone(), + registry_account: third.clone(), + payer: None, + }, + ), + ( + 27, + crate::SplTokenSingleOperation::EmptyConfidentialTransferAccount { + account: first.clone(), + authority: sauthority.clone(), + proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::ZeroCiphertext, + location: crate::SplTokenConfidentialProofLocation::ContextStateAccount { + account: third.clone(), + }, + }, + }, + ), + ( + 27, + crate::SplTokenSingleOperation::WithdrawConfidentialTokens { + account: first.clone(), + mint: second.clone(), + amount: amount.clone(), + decimals: 9, + new_decryptable_available_balance: crate::SplTokenDecryptableBalance("44".repeat(36)), + authority: sauthority.clone(), + equality_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::CiphertextCommitmentEquality, + location: crate::SplTokenConfidentialProofLocation::ContextStateAccount { account: third.clone() }, + }, + range_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::BatchedRangeProofU64, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { offset: 1 }, + }, + }, + ), + ( + 27, + crate::SplTokenSingleOperation::TransferConfidentialTokens { + source: first.clone(), + mint: second.clone(), + destination: third.clone(), + new_source_decryptable_available_balance: crate::SplTokenDecryptableBalance("55".repeat(36)), + transfer_amount_auditor_ciphertext_lo: crate::SplTokenElGamalCiphertext("66".repeat(64)), + transfer_amount_auditor_ciphertext_hi: crate::SplTokenElGamalCiphertext("77".repeat(64)), + authority: sauthority.clone(), + equality_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::CiphertextCommitmentEquality, + location: crate::SplTokenConfidentialProofLocation::ContextStateAccount { account: third.clone() }, + }, + ciphertext_validity_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { offset: 1 }, + }, + range_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::BatchedRangeProofU128, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { offset: 2 }, + }, + }, + ), + ( + 27, + crate::SplTokenSingleOperation::TransferConfidentialTokensWithFee { + source: first.clone(), + mint: second.clone(), + destination: third.clone(), + new_source_decryptable_available_balance: crate::SplTokenDecryptableBalance("00".repeat(36)), + transfer_amount_auditor_ciphertext_lo: crate::SplTokenElGamalCiphertext("00".repeat(64)), + transfer_amount_auditor_ciphertext_hi: crate::SplTokenElGamalCiphertext("00".repeat(64)), + authority: sauthority.clone(), + equality_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::CiphertextCommitmentEquality, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { offset: -1 }, + }, + transfer_amount_ciphertext_validity_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { offset: -2 }, + }, + fee_sigma_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::PercentageWithFee, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { offset: -3 }, + }, + fee_ciphertext_validity_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::BatchedGroupedCiphertext2HandlesValidity, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { offset: -4 }, + }, + range_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::BatchedRangeProofU256, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { offset: -5 }, + }, + }, + ), + ( + 37, + crate::SplTokenSingleOperation::InitializeConfidentialTransferFeeConfig { + mint: pubkey(kb_program_ids::SYSTEM_PROGRAM_ID), + authority: Some(pubkey(kb_program_ids::CONFIG_PROGRAM_ID)), + withdraw_withheld_authority_elgamal_pubkey: crate::SplTokenElGamalPubkey("11".repeat(32)), + }, + ), + ( + 37, + crate::SplTokenSingleOperation::EnableConfidentialTransferFeeHarvest { + mint: pubkey(kb_program_ids::SYSTEM_PROGRAM_ID), + authority: authority(&[]), + }, + ), + ( + 37, + crate::SplTokenSingleOperation::DisableConfidentialTransferFeeHarvest { + mint: pubkey(kb_program_ids::SYSTEM_PROGRAM_ID), + authority: authority(&[]), + }, + ), + ( + 37, + crate::SplTokenSingleOperation::HarvestConfidentialWithheldTokensToMint { + mint: pubkey(kb_program_ids::SYSTEM_PROGRAM_ID), + sources: vec![pubkey(kb_program_ids::CONFIG_PROGRAM_ID)], + }, + ), + ( + 37, + crate::SplTokenSingleOperation::WithdrawConfidentialWithheldTokensFromMint { + mint: pubkey(kb_program_ids::SYSTEM_PROGRAM_ID), + destination: pubkey(kb_program_ids::CONFIG_PROGRAM_ID), + new_decryptable_available_balance: crate::SplTokenDecryptableBalance("22".repeat(36)), + authority: authority(&[]), + proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::CiphertextCiphertextEquality, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { offset: -1 }, + }, + }, + ), + ( + 37, + crate::SplTokenSingleOperation::WithdrawConfidentialWithheldTokensFromAccounts { + mint: pubkey(kb_program_ids::SYSTEM_PROGRAM_ID), + destination: pubkey(kb_program_ids::CONFIG_PROGRAM_ID), + new_decryptable_available_balance: crate::SplTokenDecryptableBalance("33".repeat(36)), + authority: authority(&[]), + sources: vec![pubkey(kb_program_ids::STAKE_PROGRAM_ID)], + proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::CiphertextCiphertextEquality, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { offset: -1 }, + }, + }, + ), + ( + 42, + crate::SplTokenSingleOperation::InitializeConfidentialMintBurnMint { + mint: first.clone(), + supply_elgamal_pubkey: crate::SplTokenElGamalPubkey("11".repeat(32)), + decryptable_supply: crate::SplTokenDecryptableBalance("22".repeat(36)), + }, + ), + ( + 42, + crate::SplTokenSingleOperation::RotateConfidentialSupplyElGamalPubkey { + mint: first.clone(), + new_supply_elgamal_pubkey: crate::SplTokenElGamalPubkey("33".repeat(32)), + authority: sauthority.clone(), + proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::CiphertextCiphertextEquality, + location: crate::SplTokenConfidentialProofLocation::ContextStateAccount { + account: third.clone(), + }, + }, + }, + ), + ( + 42, + crate::SplTokenSingleOperation::UpdateConfidentialDecryptableSupply { + mint: first.clone(), + new_decryptable_supply: crate::SplTokenDecryptableBalance("44".repeat(36)), + authority: sauthority.clone(), + }, + ), + ( + 42, + crate::SplTokenSingleOperation::ApplyPendingConfidentialBurn { + mint: first.clone(), + authority: sauthority.clone(), + }, + ), + ( + 42, + crate::SplTokenSingleOperation::ConfidentialMint { + destination: first.clone(), + mint: second.clone(), + new_decryptable_supply: crate::SplTokenDecryptableBalance("55".repeat(36)), + mint_amount_auditor_ciphertext_lo: crate::SplTokenElGamalCiphertext("66".repeat(64)), + mint_amount_auditor_ciphertext_hi: crate::SplTokenElGamalCiphertext("77".repeat(64)), + authority: sauthority.clone(), + equality_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::CiphertextCommitmentEquality, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { offset: -1 }, + }, + ciphertext_validity_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity, + location: crate::SplTokenConfidentialProofLocation::ContextStateAccount { account: third.clone() }, + }, + range_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::BatchedRangeProofU128, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { offset: -2 }, + }, + }, + ), + ( + 42, + crate::SplTokenSingleOperation::ConfidentialBurn { + source: first.clone(), + mint: second.clone(), + new_decryptable_available_balance: crate::SplTokenDecryptableBalance("88".repeat(36)), + burn_amount_auditor_ciphertext_lo: crate::SplTokenElGamalCiphertext("99".repeat(64)), + burn_amount_auditor_ciphertext_hi: crate::SplTokenElGamalCiphertext("aa".repeat(64)), + authority: sauthority.clone(), + equality_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::CiphertextCommitmentEquality, + location: crate::SplTokenConfidentialProofLocation::ContextStateAccount { account: third.clone() }, + }, + ciphertext_validity_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { offset: -1 }, + }, + range_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::BatchedRangeProofU128, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { offset: -2 }, + }, + }, + ), + ( + 46, + crate::SplTokenSingleOperation::PermissionedConfidentialBurn { + source: first.clone(), + mint: second.clone(), + permissioned_burn_authority: third.clone(), + new_decryptable_available_balance: crate::SplTokenDecryptableBalance("bb".repeat(36)), + burn_amount_auditor_ciphertext_lo: crate::SplTokenElGamalCiphertext("cc".repeat(64)), + burn_amount_auditor_ciphertext_hi: crate::SplTokenElGamalCiphertext("dd".repeat(64)), + authority: sauthority.clone(), + equality_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::CiphertextCommitmentEquality, + location: crate::SplTokenConfidentialProofLocation::ContextStateAccount { account: third.clone() }, + }, + ciphertext_validity_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity, + location: crate::SplTokenConfidentialProofLocation::ContextStateAccount { account: third.clone() }, + }, + range_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::BatchedRangeProofU128, + location: crate::SplTokenConfidentialProofLocation::ContextStateAccount { account: third.clone() }, + }, + }, + ), + ( + 45, + crate::SplTokenSingleOperation::UnwrapLamports { + account: first, + destination: second, + authority: sauthority, + amount_lamports: std::option::Option::Some(amount), + }, + ), + ]; + } + + #[test] + fn empty_confidential_account_preserves_zero_ciphertext_proof_modes_and_authority_metas() { + let account = pubkey(kb_program_ids::SYSTEM_PROGRAM_ID); + let authority_key = pubkey(kb_program_ids::CONFIG_PROGRAM_ID); + let signer = pubkey(kb_program_ids::STAKE_PROGRAM_ID); + let context = pubkey(kb_program_ids::VOTE_PROGRAM_ID); + for proof in [ + crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::ZeroCiphertext, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { + offset: -1, + }, + }, + crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::ZeroCiphertext, + location: crate::SplTokenConfidentialProofLocation::ContextStateAccount { + account: context.clone(), + }, + }, + ] { + let operation = crate::SplTokenSingleOperation::EmptyConfidentialTransferAccount { + account: account.clone(), + authority: crate::SplTokenAuthority { + authority: authority_key.clone(), + multisig_signers: vec![signer.clone()], + }, + proof, + }; + let intent = intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(operation), + }); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &intent, + ) + .unwrap_or_else(|error| panic!("empty account plan failed: {error}")); + assert_eq!(plan.instructions.len(), 1); + let instruction = &plan.instructions[0]; + assert_eq!(instruction.data[0], 27); + assert_eq!(instruction.data[1], 4); + assert_eq!(instruction.accounts[0].pubkey, account); + assert!(instruction.accounts[0].is_writable); + assert_eq!(instruction.accounts[2].pubkey, authority_key); + assert!(!instruction.accounts[2].is_signer); + assert_eq!(instruction.accounts[3].pubkey, signer); + assert!(instruction.accounts[3].is_signer); + } + let invalid = crate::SplTokenSingleOperation::EmptyConfidentialTransferAccount { + account, + authority: crate::SplTokenAuthority { + authority: authority_key, + multisig_signers: vec![], + }, + proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::PubkeyValidity, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { offset: 1 }, + }, + }; + let result = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(invalid), + }), + ); + assert!(result.is_err()); + } + + #[test] + fn withdraw_confidential_tokens_preserves_two_proof_modes_wire_and_authority_metas() { + let account = pubkey(kb_program_ids::SYSTEM_PROGRAM_ID); + let mint = pubkey(kb_program_ids::CONFIG_PROGRAM_ID); + let sauthority = pubkey(kb_program_ids::STAKE_PROGRAM_ID); + let signer = pubkey(kb_program_ids::VOTE_PROGRAM_ID); + let context = pubkey(kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID); + let operation = crate::SplTokenSingleOperation::WithdrawConfidentialTokens { + account: account.clone(), + mint: mint.clone(), + amount: crate::SplTokenAmount("18446744073709551615".to_owned()), + decimals: 9, + new_decryptable_available_balance: crate::SplTokenDecryptableBalance("55".repeat(36)), + authority: crate::SplTokenAuthority { + authority: sauthority.clone(), + multisig_signers: vec![signer.clone()], + }, + equality_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::CiphertextCommitmentEquality, + location: crate::SplTokenConfidentialProofLocation::ContextStateAccount { + account: context.clone(), + }, + }, + range_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::BatchedRangeProofU64, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { + offset: -1, + }, + }, + }; + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(operation), + }), + ) + .unwrap_or_else(|error| panic!("withdraw plan failed: {error}")); + let instruction = &plan.instructions[0]; + assert_eq!(instruction.data[0], 27); + assert_eq!(instruction.data[1], 6); + assert_eq!(instruction.accounts[0].pubkey, account); + assert!(instruction.accounts[0].is_writable); + assert_eq!(instruction.accounts[1].pubkey, mint); + assert!(!instruction.accounts[1].is_writable); + assert_eq!(instruction.accounts[3].pubkey, context); + assert_eq!(instruction.accounts[4].pubkey, sauthority); + assert!(!instruction.accounts[4].is_signer); + assert_eq!(instruction.accounts[5].pubkey, signer); + assert!(instruction.accounts[5].is_signer); + } + + #[test] + fn transfer_confidential_tokens_preserves_three_proof_modes_ciphertexts_and_authority_metas() { + let source = pubkey(kb_program_ids::SYSTEM_PROGRAM_ID); + let mint = pubkey(kb_program_ids::CONFIG_PROGRAM_ID); + let destination = pubkey(kb_program_ids::STAKE_PROGRAM_ID); + let sauthority = pubkey(kb_program_ids::VOTE_PROGRAM_ID); + let signer = pubkey(kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID); + let equality_context = pubkey(kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID); + let range_context = pubkey(kb_program_ids::BPF_LOADER_UPGRADEABLE_PROGRAM_ID); + let operation = crate::SplTokenSingleOperation::TransferConfidentialTokens { + source: source.clone(), + mint: mint.clone(), + destination: destination.clone(), + new_source_decryptable_available_balance: crate::SplTokenDecryptableBalance( + "88".repeat(36), + ), + transfer_amount_auditor_ciphertext_lo: crate::SplTokenElGamalCiphertext( + "99".repeat(64), + ), + transfer_amount_auditor_ciphertext_hi: crate::SplTokenElGamalCiphertext( + "aa".repeat(64), + ), + authority: crate::SplTokenAuthority { + authority: sauthority.clone(), + multisig_signers: vec![signer.clone()], + }, + equality_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::CiphertextCommitmentEquality, + location: crate::SplTokenConfidentialProofLocation::ContextStateAccount { + account: equality_context.clone(), + }, + }, + ciphertext_validity_proof: crate::SplTokenConfidentialProofReference { + kind: + crate::SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { + offset: -1, + }, + }, + range_proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::BatchedRangeProofU128, + location: crate::SplTokenConfidentialProofLocation::ContextStateAccount { + account: range_context.clone(), + }, + }, + }; + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(operation), + }), + ) + .unwrap_or_else(|error| panic!("confidential transfer plan failed: {error}")); + let instruction = &plan.instructions[0]; + assert_eq!(instruction.data[0], 27); + assert_eq!(instruction.data[1], 7); + assert_eq!(instruction.accounts[0].pubkey, source); + assert!(instruction.accounts[0].is_writable); + assert_eq!(instruction.accounts[1].pubkey, mint); + assert!(!instruction.accounts[1].is_writable); + assert_eq!(instruction.accounts[2].pubkey, destination); + assert!(instruction.accounts[2].is_writable); + assert_eq!(instruction.accounts[4].pubkey, equality_context); + assert_eq!(instruction.accounts[5].pubkey, range_context); + assert_eq!(instruction.accounts[6].pubkey, sauthority); + assert!(!instruction.accounts[6].is_signer); + assert_eq!(instruction.accounts[7].pubkey, signer); + assert!(instruction.accounts[7].is_signer); + } + + #[test] + fn scaled_ui_pausable_and_permissioned_burn_preserve_exact_public_wire_and_metas() { + let operations = all_single_operations(); + for expected_tag in [43u8, 44u8, 46u8] { + let matching = operations + .iter() + .filter(|(tag, _)| return *tag == expected_tag) + .collect::>(); + assert!(!matching.is_empty()); + for (_, operation) in matching { + let intent = intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(operation.clone()), + }); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &intent, + ) + .unwrap_or_else(|error| panic!("extension tag {expected_tag} failed: {error}")); + assert_eq!(plan.instructions[0].data[0], expected_tag); + } + } + let invalid = crate::SplTokenSingleOperation::InitializeScaledUiAmount { + mint: kb_model::Pubkey(std::string::String::from("11111111111111111111111111111111")), + authority: std::option::Option::None, + multiplier: crate::SplTokenMultiplier(std::string::String::from("0")), + }; + let intent = intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(invalid), + }); + let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &intent, + ) + .expect_err("zero multiplier must fail closed"); + assert_eq!(error.code(), "execution_spl_token_2022_multiplier_invalid"); + } + + #[test] + fn confidential_transfer_fee_public_builders_preserve_wire_accounts_and_authorities() { + let mint = pubkey(kb_program_ids::SYSTEM_PROGRAM_ID); + let authority_key = pubkey(kb_program_ids::CONFIG_PROGRAM_ID); + let signer = pubkey(kb_program_ids::STAKE_PROGRAM_ID); + let source = pubkey(kb_program_ids::VOTE_PROGRAM_ID); + let initialize = planned_single_instruction( + crate::SplTokenSingleOperation::InitializeConfidentialTransferFeeConfig { + mint: mint.clone(), + authority: Some(authority_key.clone()), + withdraw_withheld_authority_elgamal_pubkey: crate::SplTokenElGamalPubkey( + "11".repeat(32), + ), + }, + ); + assert_eq!(initialize.data[0], 37); + assert_eq!(initialize.data[1], 0); + assert_eq!(initialize.accounts.len(), 1); + assert!(initialize.accounts[0].is_writable); + let multisig = crate::SplTokenAuthority { + authority: authority_key.clone(), + multisig_signers: vec![signer.clone()], + }; + let enable = planned_single_instruction( + crate::SplTokenSingleOperation::EnableConfidentialTransferFeeHarvest { + mint: mint.clone(), + authority: multisig.clone(), + }, + ); + assert_eq!(&enable.data[..2], &[37, 4]); + assert_eq!(enable.accounts.len(), 3); + assert!(!enable.accounts[1].is_signer); + assert!(enable.accounts[2].is_signer); + let disable = planned_single_instruction( + crate::SplTokenSingleOperation::DisableConfidentialTransferFeeHarvest { + mint: mint.clone(), + authority: multisig, + }, + ); + assert_eq!(&disable.data[..2], &[37, 5]); + let harvest = planned_single_instruction( + crate::SplTokenSingleOperation::HarvestConfidentialWithheldTokensToMint { + mint, + sources: vec![source], + }, + ); + assert_eq!(&harvest.data[..2], &[37, 3]); + assert_eq!(harvest.accounts.len(), 2); + assert!(harvest.accounts.iter().all(|meta| return meta.is_writable)); + } + + #[test] + fn confidential_fee_withdraw_from_mint_preserves_equality_proof_modes_and_authority_metas() { + let mint = pubkey(kb_program_ids::SYSTEM_PROGRAM_ID); + let destination = pubkey(kb_program_ids::CONFIG_PROGRAM_ID); + let sauthority = pubkey(kb_program_ids::STAKE_PROGRAM_ID); + let signer = pubkey(kb_program_ids::VOTE_PROGRAM_ID); + let context = pubkey(kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID); + for proof in [ + crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::CiphertextCiphertextEquality, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { + offset: -1, + }, + }, + crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::CiphertextCiphertextEquality, + location: crate::SplTokenConfidentialProofLocation::ContextStateAccount { + account: context.clone(), + }, + }, + ] { + let instruction = planned_single_instruction( + crate::SplTokenSingleOperation::WithdrawConfidentialWithheldTokensFromMint { + mint: mint.clone(), + destination: destination.clone(), + new_decryptable_available_balance: crate::SplTokenDecryptableBalance( + "33".repeat(36), + ), + authority: crate::SplTokenAuthority { + authority: sauthority.clone(), + multisig_signers: vec![signer.clone()], + }, + proof, + }, + ); + assert_eq!(&instruction.data[..2], &[37, 1]); + assert_eq!(instruction.accounts[0].pubkey, mint); + assert!(instruction.accounts[0].is_writable); + assert_eq!(instruction.accounts[1].pubkey, destination); + assert!(instruction.accounts[1].is_writable); + assert_eq!(instruction.accounts[3].pubkey, sauthority); + assert!(!instruction.accounts[3].is_signer); + assert_eq!(instruction.accounts[4].pubkey, signer); + assert!(instruction.accounts[4].is_signer); + } + let invalid = crate::SplTokenSingleOperation::WithdrawConfidentialWithheldTokensFromMint { + mint, + destination, + new_decryptable_available_balance: crate::SplTokenDecryptableBalance("44".repeat(36)), + authority: crate::SplTokenAuthority { + authority: sauthority, + multisig_signers: vec![signer], + }, + proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::CiphertextCommitmentEquality, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { + offset: -1, + }, + }, + }; + let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(invalid), + }), + ) + .expect_err("wrong confidential fee proof kind must fail closed"); + assert_eq!(error.code(), "execution_spl_token_2022_confidential_proof_kind_invalid"); + } + + #[test] + fn confidential_fee_withdraw_from_accounts_preserves_sources_proof_and_multisig_order() { + let mint = pubkey(kb_program_ids::SYSTEM_PROGRAM_ID); + let destination = pubkey(kb_program_ids::CONFIG_PROGRAM_ID); + let sauthority = pubkey(kb_program_ids::STAKE_PROGRAM_ID); + let signer = pubkey(kb_program_ids::VOTE_PROGRAM_ID); + let first_source = pubkey(kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID); + let second_source = pubkey(kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID); + let instruction = planned_single_instruction( + crate::SplTokenSingleOperation::WithdrawConfidentialWithheldTokensFromAccounts { + mint: mint.clone(), + destination: destination.clone(), + new_decryptable_available_balance: crate::SplTokenDecryptableBalance( + "55".repeat(36), + ), + authority: crate::SplTokenAuthority { + authority: sauthority.clone(), + multisig_signers: vec![signer.clone()], + }, + sources: vec![first_source.clone(), second_source.clone()], + proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::CiphertextCiphertextEquality, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { + offset: -1, + }, + }, + }, + ); + assert_eq!(&instruction.data[..2], &[37, 2]); + assert_eq!(instruction.data[2], 2); + assert_eq!(instruction.data[3] as i8, -1); + assert_eq!(instruction.accounts[0].pubkey, mint); + assert!(instruction.accounts[0].is_writable); + assert_eq!(instruction.accounts[1].pubkey, destination); + assert!(instruction.accounts[1].is_writable); + assert_eq!(instruction.accounts[3].pubkey, sauthority); + assert!(!instruction.accounts[3].is_signer); + assert_eq!(instruction.accounts[4].pubkey, signer); + assert!(instruction.accounts[4].is_signer); + assert_eq!(instruction.accounts[5].pubkey, first_source); + assert!(instruction.accounts[5].is_writable); + assert_eq!(instruction.accounts[6].pubkey, second_source); + assert!(instruction.accounts[6].is_writable); + let duplicate_error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new( + crate::SplTokenSingleOperation::WithdrawConfidentialWithheldTokensFromAccounts { + mint, + destination, + new_decryptable_available_balance: crate::SplTokenDecryptableBalance("66".repeat(36)), + authority: crate::SplTokenAuthority { + authority: sauthority, + multisig_signers: vec![signer], + }, + sources: vec![first_source.clone(), first_source], + proof: crate::SplTokenConfidentialProofReference { + kind: crate::SplTokenConfidentialProofKind::CiphertextCiphertextEquality, + location: crate::SplTokenConfidentialProofLocation::InstructionOffset { offset: -1 }, + }, + }, + ), + }), + ) + .expect_err("duplicate confidential fee source must fail closed"); + assert_eq!( + duplicate_error.code(), + "execution_spl_token_2022_transfer_fee_source_duplicate" + ); + } + + #[test] + fn every_current_or_recent_single_operation_uses_its_published_tag() { + let operations = all_single_operations(); + assert_eq!(operations.len(), crate::SUPPORTED_OPERATION_CODES.len()); + for (tag, operation) in operations { + let intent = intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(operation), + }); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &intent, + ) + .unwrap_or_else(|error| panic!("tag {tag} plan failed: {error}")); + if tag == 255 { + assert!(plan.instructions[0].data.len() >= 8); + } else { + assert_eq!(plan.instructions[0].data[0], tag); + } + } + } + + #[test] + fn checked_transfer_matches_official_builder_and_preserves_u64_max() { + let operation = transfer_checked(); + let intent = intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(operation), + }); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &intent, + ) + .unwrap_or_else(|error| panic!("checked transfer plan failed: {error}")); + assert_eq!(plan.instructions.len(), 1); + assert_eq!(plan.instructions[0].data[0], 12); + assert_eq!(&plan.instructions[0].data[1..9], &u64::MAX.to_le_bytes()); + assert_eq!(plan.instructions[0].data[9], 9); + assert_eq!(plan.required_signers.len(), 2); + } + + #[test] + fn multisig_meta_order_is_preserved_while_transaction_signers_are_unique() { + let first = kb_program_ids::VOTE_PROGRAM_ID; + let second = kb_program_ids::STAKE_PROGRAM_ID; + let operation = crate::SplTokenSingleOperation::Transfer { + source: pubkey(kb_program_ids::SYSTEM_PROGRAM_ID), + destination: pubkey(kb_program_ids::CONFIG_PROGRAM_ID), + authority: authority(&[first, second, first]), + amount: crate::SplTokenAmount(std::string::String::from("1")), + }; + let intent = intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(operation), + }); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &intent, + ) + .unwrap_or_else(|error| panic!("multisig transfer plan failed: {error}")); + let accounts = &plan.instructions[0].accounts; + assert_eq!(accounts[3].pubkey.0, first); + assert_eq!(accounts[4].pubkey.0, second); + assert_eq!(accounts[5].pubkey.0, first); + assert_eq!(plan.required_signers.len(), 3); + } + + #[test] + fn default_state_memo_and_cpi_guard_builders_preserve_exact_wire_and_authority_metas() { + let first = pubkey(kb_program_ids::SYSTEM_PROGRAM_ID); + let sauthority = + authority(&[kb_program_ids::VOTE_PROGRAM_ID, kb_program_ids::STAKE_PROGRAM_ID]); + let cases = [ + ( + crate::SplTokenSingleOperation::InitializeDefaultAccountState { + mint: first.clone(), + state: crate::SplTokenDefaultAccountState::Frozen, + }, + 28_u8, + 0_u8, + 1_usize, + ), + ( + crate::SplTokenSingleOperation::UpdateDefaultAccountState { + mint: first.clone(), + authority: sauthority.clone(), + state: crate::SplTokenDefaultAccountState::Initialized, + }, + 28_u8, + 1_u8, + 4_usize, + ), + ( + crate::SplTokenSingleOperation::EnableRequiredTransferMemos { + account: first.clone(), + authority: sauthority.clone(), + }, + 30_u8, + 0_u8, + 4_usize, + ), + ( + crate::SplTokenSingleOperation::DisableRequiredTransferMemos { + account: first.clone(), + authority: sauthority.clone(), + }, + 30_u8, + 1_u8, + 4_usize, + ), + ( + crate::SplTokenSingleOperation::EnableCpiGuard { + account: first.clone(), + authority: sauthority.clone(), + }, + 34_u8, + 0_u8, + 4_usize, + ), + ( + crate::SplTokenSingleOperation::DisableCpiGuard { + account: first, + authority: sauthority, + }, + 34_u8, + 1_u8, + 4_usize, + ), + ]; + for (operation, outer_tag, sub_tag, account_count) in cases { + let intent = intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(operation), + }); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &intent, + ) + .unwrap_or_else(|error| panic!("extension plan failed: {error}")); + assert_eq!(plan.instructions[0].data[0], outer_tag); + assert_eq!(plan.instructions[0].data[1], sub_tag); + assert_eq!(plan.instructions[0].accounts.len(), account_count); + assert!(plan.instructions[0].accounts[0].is_writable); + } + } + + #[test] + fn hook_and_pointer_builders_preserve_exact_wire_optional_values_and_authority_metas() { + let mint = pubkey(kb_program_ids::SYSTEM_PROGRAM_ID); + let target = pubkey(kb_program_ids::CONFIG_PROGRAM_ID); + let sauthority = + authority(&[kb_program_ids::VOTE_PROGRAM_ID, kb_program_ids::STAKE_PROGRAM_ID]); + let cases = [ + ( + crate::SplTokenSingleOperation::InitializeTransferHook { + mint: mint.clone(), + authority: std::option::Option::Some(target.clone()), + program_id: std::option::Option::Some(target.clone()), + }, + 36_u8, + 0_u8, + 1_usize, + ), + ( + crate::SplTokenSingleOperation::UpdateTransferHook { + mint: mint.clone(), + authority: sauthority.clone(), + program_id: std::option::Option::None, + }, + 36_u8, + 1_u8, + 4_usize, + ), + ( + crate::SplTokenSingleOperation::InitializeMetadataPointer { + mint: mint.clone(), + authority: std::option::Option::Some(target.clone()), + metadata_address: std::option::Option::Some(target.clone()), + }, + 39_u8, + 0_u8, + 1_usize, + ), + ( + crate::SplTokenSingleOperation::UpdateMetadataPointer { + mint: mint.clone(), + authority: sauthority.clone(), + metadata_address: std::option::Option::None, + }, + 39_u8, + 1_u8, + 4_usize, + ), + ( + crate::SplTokenSingleOperation::InitializeGroupPointer { + mint: mint.clone(), + authority: std::option::Option::Some(target.clone()), + group_address: std::option::Option::Some(target.clone()), + }, + 40_u8, + 0_u8, + 1_usize, + ), + ( + crate::SplTokenSingleOperation::UpdateGroupPointer { + mint: mint.clone(), + authority: sauthority.clone(), + group_address: std::option::Option::None, + }, + 40_u8, + 1_u8, + 4_usize, + ), + ( + crate::SplTokenSingleOperation::InitializeGroupMemberPointer { + mint: mint.clone(), + authority: std::option::Option::Some(target.clone()), + member_address: std::option::Option::Some(target), + }, + 41_u8, + 0_u8, + 1_usize, + ), + ( + crate::SplTokenSingleOperation::UpdateGroupMemberPointer { + mint, + authority: sauthority, + member_address: std::option::Option::None, + }, + 41_u8, + 1_u8, + 4_usize, + ), + ]; + for (operation, outer_tag, sub_tag, account_count) in cases { + let intent = intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(operation), + }); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &intent, + ) + .unwrap_or_else(|error| panic!("hook or pointer plan failed: {error}")); + assert_eq!(plan.instructions[0].data[0], outer_tag); + assert_eq!(plan.instructions[0].data[1], sub_tag); + assert_eq!(plan.instructions[0].accounts.len(), account_count); + assert!(plan.instructions[0].accounts[0].is_writable); + } + } + + #[test] + fn metadata_and_group_interface_builders_preserve_discriminated_wire_and_accounts() { + let first = pubkey(kb_program_ids::SYSTEM_PROGRAM_ID); + let second = pubkey(kb_program_ids::CONFIG_PROGRAM_ID); + let third = pubkey(kb_program_ids::STAKE_PROGRAM_ID); + let cases = [ + ( + crate::SplTokenSingleOperation::InitializeTokenMetadata { + metadata: first.clone(), + update_authority: second.clone(), + mint: first.clone(), + mint_authority: third.clone(), + name: std::string::String::from("Token"), + symbol: std::string::String::from("TKN"), + uri: std::string::String::from("https://example.invalid/token.json"), + }, + 4_usize, + ), + ( + crate::SplTokenSingleOperation::UpdateTokenMetadataField { + metadata: first.clone(), + update_authority: second.clone(), + field: crate::SplTokenMetadataField::Key(std::string::String::from("website")), + value: std::string::String::from("https://example.invalid"), + }, + 2_usize, + ), + ( + crate::SplTokenSingleOperation::RemoveTokenMetadataKey { + metadata: first.clone(), + update_authority: second.clone(), + key: std::string::String::from("website"), + idempotent: true, + }, + 2_usize, + ), + ( + crate::SplTokenSingleOperation::InitializeTokenGroup { + group: first.clone(), + mint: first.clone(), + mint_authority: second.clone(), + update_authority: std::option::Option::Some(third.clone()), + max_size: 42, + }, + 3_usize, + ), + ( + crate::SplTokenSingleOperation::UpdateTokenGroupMaxSize { + group: first.clone(), + update_authority: second.clone(), + max_size: 84, + }, + 2_usize, + ), + ( + crate::SplTokenSingleOperation::InitializeTokenGroupMember { + member: first.clone(), + member_mint: first.clone(), + member_mint_authority: second.clone(), + group: third.clone(), + group_update_authority: second, + }, + 5_usize, + ), + ]; + for (operation, account_count) in cases { + let intent = intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(operation), + }); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &intent, + ) + .unwrap_or_else(|error| panic!("metadata or group plan failed: {error}")); + assert!(plan.instructions[0].data.len() >= 8); + assert_eq!(plan.instructions[0].accounts.len(), account_count); + assert!(plan.instructions[0].accounts[0].is_writable); + } + } + + #[test] + fn recent_unwrap_is_constructible_and_batch_is_explicitly_decode_only() { + let unwrap = crate::SplTokenSingleOperation::UnwrapLamports { + account: pubkey(kb_program_ids::SYSTEM_PROGRAM_ID), + destination: pubkey(kb_program_ids::STAKE_PROGRAM_ID), + authority: authority(&[]), + amount_lamports: std::option::Option::Some(crate::SplTokenAmount( + std::string::String::from("42"), + )), + }; + let unwrap_intent = intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(unwrap.clone()), + }); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &unwrap_intent, + ) + .unwrap_or_else(|error| panic!("recent unwrap plan failed: {error}")); + assert_eq!(plan.instructions[0].data[0], 45); + assert_eq!(plan.requested_spend_lamports, 42); + + let batch_intent = intent(crate::SplToken2022Operation::Batch { + instructions: vec![unwrap, transfer_checked()], + }); + let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &batch_intent, + ) + .expect_err("Batch must remain decode-only without an official interface builder"); + assert_eq!(error.code(), "execution_spl_token_2022_batch_builder_unavailable"); + } + + #[test] + fn invalid_amount_multisig_and_optional_simulation_fail_closed() { + let mut operation = transfer_checked(); + if let crate::SplTokenSingleOperation::TransferChecked { amount, .. } = &mut operation { + amount.0 = std::string::String::from("-1"); + } + let pintent = intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(operation), + }); + let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &pintent, + ) + .expect_err("negative amount must fail"); + assert_eq!(error.code(), "execution_spl_token_2022_amount_invalid"); + + let mut intent = intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(transfer_checked()), + }); + intent.policy.simulation = kb_execution_api::ExecutionSimulationPolicy::Optional; + let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &intent, + ) + .expect_err("optional simulation must fail"); + assert_eq!(error.code(), "execution_spl_token_2022_simulation_required"); + } + + #[test] + fn every_cluster_is_constructible_and_mainnet_remains_governed_by_common_safety() { + for cluster in [ + kb_execution_api::ExecutionCluster::Localnet, + kb_execution_api::ExecutionCluster::Devnet, + kb_execution_api::ExecutionCluster::Testnet, + kb_execution_api::ExecutionCluster::Mainnet, + ] { + let mut intent = intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(transfer_checked()), + }); + intent.policy.cluster.expected_cluster = cluster; + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &intent, + ) + .unwrap_or_else(|error| panic!("cluster plan failed: {error}")); + assert_eq!(plan.policy.cluster.expected_cluster, cluster); + let evaluation = kb_execution_safety::ExecutionSafetyChecker + .evaluate_prepared_plan(&plan) + .unwrap_or_else(|error| panic!("safety evaluation failed: {error}")); + if cluster == kb_execution_api::ExecutionCluster::Mainnet { + assert_eq!(evaluation.decision, kb_execution_safety::ExecutionSafetyDecision::Deny); + assert!(evaluation.violations.iter().any(|violation| { + return violation.code == "execution_mainnet_disabled"; + })); + let mut explicitly_enabled = plan.clone(); + explicitly_enabled.policy.cluster.allow_mainnet = true; + explicitly_enabled.policy.cluster.mainnet_confirmation = true; + let enabled_evaluation = kb_execution_safety::ExecutionSafetyChecker + .evaluate_prepared_plan(&explicitly_enabled) + .unwrap_or_else(|error| { + panic!("enabled Mainnet safety evaluation failed: {error}") + }); + assert_eq!( + enabled_evaluation.decision, + kb_execution_safety::ExecutionSafetyDecision::Allow + ); + } else { + assert_eq!( + evaluation.decision, + kb_execution_safety::ExecutionSafetyDecision::Allow + ); + } + } + } + + #[test] + fn transfer_fee_builders_preserve_exact_wire_accounts_and_bounds() { + let source = pubkey(kb_program_ids::SYSTEM_PROGRAM_ID); + let mint = pubkey(kb_program_ids::CONFIG_PROGRAM_ID); + let destination = pubkey(kb_program_ids::STAKE_PROGRAM_ID); + let signer = pubkey(kb_program_ids::VOTE_PROGRAM_ID); + let operation = crate::SplTokenSingleOperation::TransferCheckedWithFee { + source: source.clone(), + mint: mint.clone(), + destination: destination.clone(), + authority: crate::SplTokenAuthority { + authority: pubkey(kb_program_ids::FEATURE_PROGRAM_ID), + multisig_signers: vec![signer.clone()], + }, + amount: crate::SplTokenAmount(std::string::String::from("42")), + decimals: 9, + fee: crate::SplTokenAmount(std::string::String::from("2")), + }; + let pintent = intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(operation), + }); + let plan = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &pintent, + ) + .unwrap_or_else(|error| panic!("transfer fee plan failed: {error}")); + let instruction = &plan.instructions[0]; + assert_eq!(instruction.data[0], 26); + assert_eq!(instruction.data[1], 1); + assert_eq!(&instruction.data[2..10], &42_u64.to_le_bytes()); + assert_eq!(instruction.data[10], 9); + assert_eq!(&instruction.data[11..19], &2_u64.to_le_bytes()); + assert_eq!(instruction.accounts.len(), 5); + assert_eq!(instruction.accounts[0].pubkey, source); + assert!(instruction.accounts[0].is_writable); + assert_eq!(instruction.accounts[1].pubkey, mint); + assert!(!instruction.accounts[1].is_writable); + assert_eq!(instruction.accounts[2].pubkey, destination); + assert!(instruction.accounts[2].is_writable); + assert!(!instruction.accounts[3].is_signer); + assert_eq!(instruction.accounts[4].pubkey, signer); + assert!(instruction.accounts[4].is_signer); + + let invalid = crate::SplTokenSingleOperation::InitializeTransferFeeConfig { + mint: pubkey(kb_program_ids::SYSTEM_PROGRAM_ID), + transfer_fee_config_authority: std::option::Option::None, + withdraw_withheld_authority: std::option::Option::None, + transfer_fee_basis_points: 10_001, + maximum_fee: crate::SplTokenAmount(std::string::String::from("0")), + }; + let invalid_intent = intent(crate::SplToken2022Operation::Instruction { + value: std::boxed::Box::new(invalid), + }); + let error = kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &crate::SplToken2022Executor, + &invalid_intent, + ) + .expect_err("basis points above 100% must fail"); + assert_eq!(error.code(), "execution_spl_token_2022_transfer_fee_basis_points_invalid"); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/confidential.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/confidential.rs new file mode 100644 index 0000000..7839ac8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/confidential.rs @@ -0,0 +1,576 @@ +// file: kb_executor_spl_token_2022/src/confidential.rs +// version: 8 + +//! Audited Confidential Transfer execution contracts. + +use ts_rs::TS; // rust-rules: derive-import + +/// Maximum number of proof references accepted by one confidential operation. +pub const MAX_CONFIDENTIAL_PROOF_REFERENCES: usize = 5; +/// Exact byte length of an ElGamal public key accepted from callers. +pub const ELGAMAL_PUBKEY_BYTES: usize = 32; +/// Exact byte length of one decryptable authenticated balance ciphertext. +pub const DECRYPTABLE_BALANCE_BYTES: usize = 36; +/// Exact byte length of one ElGamal ciphertext accepted from callers. +pub const ELGAMAL_CIPHERTEXT_BYTES: usize = 64; +/// Token-2022 outer tag shared by all Confidential Transfer instructions. +pub const CONFIDENTIAL_TRANSFER_EXTERNAL_TAG: u8 = 27; +/// Confidential Transfer subtag published for `TransferWithFee`. +pub const CONFIDENTIAL_TRANSFER_WITH_FEE_SUBTAG: u8 = 13; +/// Exact payload bytes after the two instruction discriminants for `TransferWithFee`. +pub const CONFIDENTIAL_TRANSFER_WITH_FEE_PAYLOAD_BYTES: usize = + DECRYPTABLE_BALANCE_BYTES + (ELGAMAL_CIPHERTEXT_BYTES * 2) + MAX_CONFIDENTIAL_PROOF_REFERENCES; +/// Exact total Token-2022 instruction-data bytes for `TransferWithFee`. +pub const CONFIDENTIAL_TRANSFER_WITH_FEE_INSTRUCTION_DATA_BYTES: usize = + 2 + CONFIDENTIAL_TRANSFER_WITH_FEE_PAYLOAD_BYTES; + +/// Caller-generated ElGamal public key encoded as lowercase or uppercase hexadecimal. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token_2022/confidential/SplTokenElGamalPubkey.ts" +)] +pub struct SplTokenElGamalPubkey(pub std::string::String); + +impl SplTokenElGamalPubkey { + pub(crate) fn parse( + &self, + ) -> Result { + let bytes = match decode_exact_hex(&self.0, ELGAMAL_PUBKEY_BYTES, "ElGamal public key") { + Ok(value) => value, + Err(error) => return Err(error), + }; + let array = match <[u8; ELGAMAL_PUBKEY_BYTES]>::try_from(bytes.as_slice()) { + Ok(value) => value, + Err(_) => return Err("ElGamal public key has an invalid length".to_owned()), + }; + return Ok(bytemuck::pod_read_unaligned(array.as_slice())); + } +} + +/// Caller-generated decryptable balance encoded as hexadecimal bytes. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token_2022/confidential/SplTokenDecryptableBalance.ts" +)] +pub struct SplTokenDecryptableBalance(pub std::string::String); + +impl SplTokenDecryptableBalance { + pub(crate) fn parse( + &self, + ) -> Result + { + let bytes = + match decode_exact_hex(&self.0, DECRYPTABLE_BALANCE_BYTES, "decryptable balance") { + Ok(value) => value, + Err(error) => return Err(error), + }; + let array = match <[u8; DECRYPTABLE_BALANCE_BYTES]>::try_from(bytes.as_slice()) { + Ok(value) => value, + Err(_) => return Err("decryptable balance has an invalid length".to_owned()), + }; + return Ok(bytemuck::pod_read_unaligned(array.as_slice())); + } +} + +/// Caller-generated ElGamal ciphertext encoded as hexadecimal bytes. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token_2022/confidential/SplTokenElGamalCiphertext.ts" +)] +pub struct SplTokenElGamalCiphertext(pub std::string::String); + +impl SplTokenElGamalCiphertext { + pub(crate) fn parse( + &self, + ) -> Result + { + let bytes = match decode_exact_hex(&self.0, ELGAMAL_CIPHERTEXT_BYTES, "ElGamal ciphertext") + { + Ok(value) => value, + Err(error) => return Err(error), + }; + let array = match <[u8; ELGAMAL_CIPHERTEXT_BYTES]>::try_from(bytes.as_slice()) { + Ok(value) => value, + Err(_) => return Err("ElGamal ciphertext has an invalid length".to_owned()), + }; + return Ok(bytemuck::pod_read_unaligned(array.as_slice())); + } +} + +fn decode_exact_hex( + value: &str, + expected_bytes: usize, + label: &str, +) -> Result, std::string::String> { + if value.len() != expected_bytes.saturating_mul(2) { + return Err(format!( + "{label} must contain exactly {expected_bytes} bytes encoded as hexadecimal" + )); + } + let input = value.as_bytes(); + let mut output = std::vec::Vec::with_capacity(expected_bytes); + let mut index = 0usize; + while index < input.len() { + let high = match decode_hex_nibble(input[index]) { + Some(v) => v, + None => return Err(format!("{label} contains a non-hexadecimal character")), + }; + let low = match decode_hex_nibble(input[index + 1]) { + Some(v) => v, + None => return Err(format!("{label} contains a non-hexadecimal character")), + }; + output.push((high << 4) | low); + index += 2; + } + return Ok(output); +} + +fn decode_hex_nibble(value: u8) -> Option { + return match value { + b'0'..=b'9' => Some(value - b'0'), + b'a'..=b'f' => Some(value - b'a' + 10), + b'A'..=b'F' => Some(value - b'A' + 10), + _ => None, + }; +} + +/// Proof statement required by a Confidential Transfer operation. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token_2022/confidential/SplTokenConfidentialProofKind.ts" +)] +pub enum SplTokenConfidentialProofKind { + /// ElGamal public-key validity. + PubkeyValidity, + /// Zero-ciphertext proof used before account closure. + ZeroCiphertext, + /// Ciphertext-commitment equality. + CiphertextCommitmentEquality, + /// Ciphertext-ciphertext equality used by confidential fee withdrawals. + CiphertextCiphertextEquality, + /// Batched grouped-ciphertext validity with three decryption handles. + BatchedGroupedCiphertext3HandlesValidity, + /// Batched grouped-ciphertext validity with two decryption handles. + BatchedGroupedCiphertext2HandlesValidity, + /// Percentage-with-fee sigma proof. + PercentageWithFee, + /// Batched 64-bit range proof. + BatchedRangeProofU64, + /// Batched 128-bit range proof. + BatchedRangeProofU128, + /// Batched 256-bit range proof. + BatchedRangeProofU256, +} + +/// Explicit source for one already-generated zero-knowledge proof statement. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case", tag = "mode")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token_2022/confidential/SplTokenConfidentialProofLocation.ts" +)] +pub enum SplTokenConfidentialProofLocation { + /// Relative transaction offset of a separate ZK proof-program instruction. + InstructionOffset { + /// Non-zero signed relative instruction offset. + offset: i8, + }, + /// Account containing a previously verified proof context state. + ContextStateAccount { + /// Proof context-state account. + account: kb_model::Pubkey, + }, +} + +impl SplTokenConfidentialProofLocation { + /// Validates the mode-specific proof-location contract. + pub fn validate(&self) -> Result<(), std::string::String> { + return match self { + Self::InstructionOffset { offset } if *offset == 0 => Err( + "an inline proof instruction offset must be non-zero; zero is reserved for a context-state account" + .to_owned(), + ), + Self::InstructionOffset { .. } | Self::ContextStateAccount { .. } => Ok(()), + }; + } +} + +/// One proof requirement paired with its explicit location. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "camelCase")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token_2022/confidential/SplTokenConfidentialProofReference.ts" +)] +pub struct SplTokenConfidentialProofReference { + /// Exact proof statement required by the Token-2022 builder. + pub kind: SplTokenConfidentialProofKind, + /// Inline-instruction or context-state source. + pub location: SplTokenConfidentialProofLocation, +} + +/// Audited Confidential Transfer operation surface. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token_2022/confidential/SplTokenConfidentialOperation.ts" +)] +pub enum SplTokenConfidentialOperation { + /// Initialize confidential-transfer mint configuration. + InitializeMint, + /// Update confidential-transfer mint configuration. + UpdateMint, + /// Configure a token account with an ElGamal key. + ConfigureAccount, + /// Approve a configured account. + ApproveAccount, + /// Empty the available ciphertext before closing an account. + EmptyAccount, + /// Deposit public tokens into pending confidential balance. + Deposit, + /// Withdraw confidential balance into public balance. + Withdraw, + /// Transfer confidentially without a transfer fee. + Transfer, + /// Apply pending balance credits. + ApplyPendingBalance, + /// Enable confidential credits. + EnableConfidentialCredits, + /// Disable confidential credits. + DisableConfidentialCredits, + /// Enable non-confidential credits. + EnableNonConfidentialCredits, + /// Disable non-confidential credits. + DisableNonConfidentialCredits, + /// Transfer confidentially with fee proofs. + TransferWithFee, + /// Configure an account through an ElGamal Registry account. + ConfigureAccountWithRegistry, +} + +impl SplTokenConfidentialOperation { + /// Returns the ordered proof statements required by the official builder. + pub fn required_proofs(&self) -> &'static [SplTokenConfidentialProofKind] { + return match self { + Self::ConfigureAccount | Self::ConfigureAccountWithRegistry => { + &[SplTokenConfidentialProofKind::PubkeyValidity] + }, + Self::EmptyAccount => &[SplTokenConfidentialProofKind::ZeroCiphertext], + Self::Withdraw => &[ + SplTokenConfidentialProofKind::CiphertextCommitmentEquality, + SplTokenConfidentialProofKind::BatchedRangeProofU64, + ], + Self::Transfer => &[ + SplTokenConfidentialProofKind::CiphertextCommitmentEquality, + SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity, + SplTokenConfidentialProofKind::BatchedRangeProofU128, + ], + Self::TransferWithFee => &[ + SplTokenConfidentialProofKind::CiphertextCommitmentEquality, + SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity, + SplTokenConfidentialProofKind::PercentageWithFee, + SplTokenConfidentialProofKind::BatchedGroupedCiphertext2HandlesValidity, + SplTokenConfidentialProofKind::BatchedRangeProofU256, + ], + Self::InitializeMint + | Self::UpdateMint + | Self::ApproveAccount + | Self::Deposit + | Self::ApplyPendingBalance + | Self::EnableConfidentialCredits + | Self::DisableConfidentialCredits + | Self::EnableNonConfidentialCredits + | Self::DisableNonConfidentialCredits => &[], + }; + } + + /// Indicates whether the Token-2022 instruction can be constructed without secret material. + pub fn executor_support(&self) -> SplTokenConfidentialExecutorSupport { + return match self { + Self::InitializeMint + | Self::UpdateMint + | Self::ApproveAccount + | Self::Deposit + | Self::ApplyPendingBalance + | Self::EnableConfidentialCredits + | Self::DisableConfidentialCredits + | Self::EnableNonConfidentialCredits + | Self::DisableNonConfidentialCredits => { + SplTokenConfidentialExecutorSupport::PublicBuilderReady + }, + Self::ConfigureAccount + | Self::EmptyAccount + | Self::Withdraw + | Self::Transfer + | Self::TransferWithFee + | Self::ConfigureAccountWithRegistry => { + SplTokenConfidentialExecutorSupport::RequiresCallerGeneratedCryptographicInputs + }, + }; + } +} + +/// Audited Confidential Mint/Burn operation surface. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token_2022/confidential/SplTokenConfidentialMintBurnOperation.ts" +)] +pub enum SplTokenConfidentialMintBurnOperation { + /// Initialize the mint extension with supply encryption material. + InitializeMint, + /// Rotate the supply ElGamal public key. + RotateSupplyElGamalPubkey, + /// Replace the caller-maintained decryptable supply. + UpdateDecryptableSupply, + /// Mint tokens directly into a confidential token account. + Mint, + /// Burn tokens from a confidential token account. + Burn, + /// Aggregate the pending burn ciphertext into confidential supply. + ApplyPendingBurn, +} + +impl SplTokenConfidentialMintBurnOperation { + /// Returns the published subdiscriminant. + pub fn subtag(&self) -> u8 { + return match self { + Self::InitializeMint => 0, + Self::RotateSupplyElGamalPubkey => 1, + Self::UpdateDecryptableSupply => 2, + Self::Mint => 3, + Self::Burn => 4, + Self::ApplyPendingBurn => 5, + }; + } + + /// Returns the ordered proof statements required by the official builder. + pub fn required_proofs(&self) -> &'static [SplTokenConfidentialProofKind] { + return match self { + Self::RotateSupplyElGamalPubkey => { + &[SplTokenConfidentialProofKind::CiphertextCiphertextEquality] + }, + Self::Mint | Self::Burn => &[ + SplTokenConfidentialProofKind::CiphertextCommitmentEquality, + SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity, + SplTokenConfidentialProofKind::BatchedRangeProofU128, + ], + Self::InitializeMint | Self::UpdateDecryptableSupply | Self::ApplyPendingBurn => &[], + }; + } + + /// Classifies whether the caller must supply cryptographic material. + pub fn executor_support(&self) -> SplTokenConfidentialExecutorSupport { + return match self { + Self::UpdateDecryptableSupply | Self::ApplyPendingBurn => { + SplTokenConfidentialExecutorSupport::PublicBuilderReady + }, + Self::InitializeMint | Self::RotateSupplyElGamalPubkey | Self::Mint | Self::Burn => { + SplTokenConfidentialExecutorSupport::RequiresCallerGeneratedCryptographicInputs + }, + }; + } +} + +/// Executor-readiness classification established by the Confidential Transfer audit. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token_2022/confidential/SplTokenConfidentialExecutorSupport.ts" +)] +pub enum SplTokenConfidentialExecutorSupport { + /// The builder needs only public fields and ordinary authorities. + PublicBuilderReady, + /// The caller must provide ciphertexts, decryptable balances and/or proof data generated outside this executor. + RequiresCallerGeneratedCryptographicInputs, +} + +/// Validates an ordered proof-reference list against the audited operation contract. +pub fn validate_confidential_proof_references( + operation: SplTokenConfidentialOperation, + references: &[SplTokenConfidentialProofReference], +) -> Result<(), std::string::String> { + let required = operation.required_proofs(); + if references.len() > MAX_CONFIDENTIAL_PROOF_REFERENCES { + return Err("too many confidential proof references".to_owned()); + } + if references.len() != required.len() { + return Err( + "confidential proof reference count does not match the operation contract".to_owned() + ); + } + for (index, reference) in references.iter().enumerate() { + if let Err(error) = reference.location.validate() { + return Err(error); + } + if reference.kind != required[index] { + return Err( + "confidential proof references are not in the official builder order".to_owned() + ); + } + } + return Ok(()); +} + +#[cfg(test)] +mod tests { + + #[test] + fn audit_partitions_public_builders_from_caller_generated_cryptographic_inputs() { + assert_eq!( + super::SplTokenConfidentialOperation::Deposit.executor_support(), + super::SplTokenConfidentialExecutorSupport::PublicBuilderReady + ); + assert_eq!( + super::SplTokenConfidentialOperation::Transfer.executor_support(), + super::SplTokenConfidentialExecutorSupport::RequiresCallerGeneratedCryptographicInputs + ); + assert_eq!( + super::SplTokenConfidentialOperation::ConfigureAccountWithRegistry.executor_support(), + super::SplTokenConfidentialExecutorSupport::RequiresCallerGeneratedCryptographicInputs + ); + } + + #[test] + fn opaque_public_inputs_require_exact_hex_lengths() { + let pubkey = super::SplTokenElGamalPubkey("11".repeat(super::ELGAMAL_PUBKEY_BYTES)); + assert!(pubkey.parse().is_ok()); + let balance = + super::SplTokenDecryptableBalance("22".repeat(super::DECRYPTABLE_BALANCE_BYTES)); + assert!(balance.parse().is_ok()); + assert!(super::SplTokenElGamalPubkey("00".repeat(31)).parse().is_err()); + assert!(super::SplTokenDecryptableBalance("zz".repeat(36)).parse().is_err()); + let ciphertext = + super::SplTokenElGamalCiphertext("33".repeat(super::ELGAMAL_CIPHERTEXT_BYTES)); + assert!(ciphertext.parse().is_ok()); + assert!(super::SplTokenElGamalCiphertext("33".repeat(63)).parse().is_err()); + } + + #[test] + fn proof_inventory_preserves_official_transfer_and_fee_order() { + assert_eq!( + super::SplTokenConfidentialOperation::Transfer.required_proofs(), + &[ + super::SplTokenConfidentialProofKind::CiphertextCommitmentEquality, + super::SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity, + super::SplTokenConfidentialProofKind::BatchedRangeProofU128, + ] + ); + assert_eq!( + super::SplTokenConfidentialOperation::TransferWithFee.required_proofs(), + &[ + super::SplTokenConfidentialProofKind::CiphertextCommitmentEquality, + super::SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity, + super::SplTokenConfidentialProofKind::PercentageWithFee, + super::SplTokenConfidentialProofKind::BatchedGroupedCiphertext2HandlesValidity, + super::SplTokenConfidentialProofKind::BatchedRangeProofU256, + ] + ); + } + + #[test] + fn transfer_with_fee_audit_matches_official_wire_and_requires_no_extra_token_payload_ciphertext() + { + assert_eq!(super::CONFIDENTIAL_TRANSFER_EXTERNAL_TAG, 27); + assert_eq!(super::CONFIDENTIAL_TRANSFER_WITH_FEE_SUBTAG, 13); + assert_eq!(super::CONFIDENTIAL_TRANSFER_WITH_FEE_PAYLOAD_BYTES, 169); + assert_eq!(super::CONFIDENTIAL_TRANSFER_WITH_FEE_INSTRUCTION_DATA_BYTES, 171); + assert_eq!( + core::mem::size_of::< + spl_token_2022_interface::extension::confidential_transfer::instruction::TransferWithFeeInstructionData, + >(), + super::CONFIDENTIAL_TRANSFER_WITH_FEE_PAYLOAD_BYTES + ); + assert_eq!( + super::SplTokenConfidentialOperation::TransferWithFee.required_proofs().len(), + super::MAX_CONFIDENTIAL_PROOF_REFERENCES + ); + let decryptable_balance = + super::SplTokenDecryptableBalance("44".repeat(super::DECRYPTABLE_BALANCE_BYTES)); + let auditor_ciphertext_lo = + super::SplTokenElGamalCiphertext("55".repeat(super::ELGAMAL_CIPHERTEXT_BYTES)); + let auditor_ciphertext_hi = + super::SplTokenElGamalCiphertext("66".repeat(super::ELGAMAL_CIPHERTEXT_BYTES)); + assert!(decryptable_balance.parse().is_ok()); + assert!(auditor_ciphertext_lo.parse().is_ok()); + assert!(auditor_ciphertext_hi.parse().is_ok()); + } + + #[test] + fn confidential_mint_burn_audit_matches_published_subtags_proofs_and_support() { + assert_eq!(super::SplTokenConfidentialMintBurnOperation::InitializeMint.subtag(), 0); + assert_eq!( + super::SplTokenConfidentialMintBurnOperation::RotateSupplyElGamalPubkey.subtag(), + 1 + ); + assert_eq!( + super::SplTokenConfidentialMintBurnOperation::UpdateDecryptableSupply.subtag(), + 2 + ); + assert_eq!(super::SplTokenConfidentialMintBurnOperation::Mint.subtag(), 3); + assert_eq!(super::SplTokenConfidentialMintBurnOperation::Burn.subtag(), 4); + assert_eq!(super::SplTokenConfidentialMintBurnOperation::ApplyPendingBurn.subtag(), 5); + assert_eq!( + super::SplTokenConfidentialMintBurnOperation::RotateSupplyElGamalPubkey + .required_proofs(), + &[super::SplTokenConfidentialProofKind::CiphertextCiphertextEquality] + ); + assert_eq!( + super::SplTokenConfidentialMintBurnOperation::Mint.required_proofs(), + &[ + super::SplTokenConfidentialProofKind::CiphertextCommitmentEquality, + super::SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity, + super::SplTokenConfidentialProofKind::BatchedRangeProofU128, + ] + ); + assert_eq!( + super::SplTokenConfidentialMintBurnOperation::Burn.required_proofs(), + super::SplTokenConfidentialMintBurnOperation::Mint.required_proofs() + ); + assert_eq!( + super::SplTokenConfidentialMintBurnOperation::ApplyPendingBurn.executor_support(), + super::SplTokenConfidentialExecutorSupport::PublicBuilderReady + ); + assert_eq!( + super::SplTokenConfidentialMintBurnOperation::Mint.executor_support(), + super::SplTokenConfidentialExecutorSupport::RequiresCallerGeneratedCryptographicInputs + ); + } + + #[test] + fn proof_locations_keep_inline_and_context_state_modes_unambiguous() { + let context_account = kb_model::Pubkey("11111111111111111111111111111111".to_owned()); + let references = vec![super::SplTokenConfidentialProofReference { + kind: super::SplTokenConfidentialProofKind::ZeroCiphertext, + location: super::SplTokenConfidentialProofLocation::ContextStateAccount { + account: context_account, + }, + }]; + assert!( + super::validate_confidential_proof_references( + super::SplTokenConfidentialOperation::EmptyAccount, + &references + ) + .is_ok() + ); + let invalid = vec![super::SplTokenConfidentialProofReference { + kind: super::SplTokenConfidentialProofKind::ZeroCiphertext, + location: super::SplTokenConfidentialProofLocation::InstructionOffset { offset: 0 }, + }]; + assert!( + super::validate_confidential_proof_references( + super::SplTokenConfidentialOperation::EmptyAccount, + &invalid + ) + .is_err() + ); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/constants.rs new file mode 100644 index 0000000..956de3c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/constants.rs @@ -0,0 +1,13 @@ +// file: kb_executor_spl_token_2022/src/constants.rs +// version: 5 + +//! Local constants for the `kb_executor_spl_token_2022` crate. Program identifiers live in `kb_program_ids`. + +/// Maximum number of ordered multisig signer occurrences. +pub(crate) const MAX_MULTISIG_SIGNERS: usize = 11; +/// Maximum UI amount string length accepted before transaction assembly. +pub(crate) const MAX_UI_AMOUNT_BYTES: usize = 255; +/// Maximum ordered token accounts accepted by transfer-fee harvest or withdraw builders. +pub(crate) const MAX_TRANSFER_FEE_SOURCE_ACCOUNTS: usize = 255; +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_spl_token_2022"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/executor.rs new file mode 100644 index 0000000..4435a53 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/executor.rs @@ -0,0 +1,332 @@ +// file: kb_executor_spl_token_2022/src/executor.rs +// version: 19 + +//! Exact Token-2022 capability dispatch and typed plan construction. + +/// Token-2022 executor implementation. +#[derive(Clone, Debug, Default)] +pub struct SplToken2022Executor; + +impl crate::SplToken2022Executor { + fn exact_capability( + &self, + program_id: &kb_model::ProgramId, + operation_code: &str, + ) -> kb_execution_api::ExecutionCapability { + if program_id.0 != kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID { + return kb_execution_api::ExecutionCapability::unsupported( + "execution_spl_token_2022_program_not_owned", + format!("program {} is not owned by kb_executor_spl_token_2022", program_id.0), + ); + } + if crate::SUPPORTED_OPERATION_CODES.contains(&operation_code) { + return kb_execution_api::ExecutionCapability::supported(operation_code); + } + if operation_code == crate::BATCH_OPERATION { + return kb_execution_api::ExecutionCapability::unsupported( + "execution_spl_token_2022_batch_builder_unavailable", + "Token-2022 Batch is decode-only because spl-token-2022-interface 3.1.1 publishes no official Batch builder", + ); + } + if matches!( + operation_code, + "spl_token_2022.initialize_mint_with_rent" + | "spl_token_2022.initialize_account_with_rent" + | "spl_token_2022.initialize_multisig_with_rent" + | "spl_token_2022.initialize_account2" + ) { + return kb_execution_api::ExecutionCapability::unsupported( + "execution_spl_token_2022_historical_variant_decode_only", + format!( + "SPL Token operation {operation_code} is an obsolete historical initialization variant; use the current no-Rent operation" + ), + ); + } + return kb_execution_api::ExecutionCapability::unsupported( + "execution_spl_token_2022_operation_unsupported", + format!("SPL Token operation {operation_code} is not implemented"), + ); + } +} + +impl kb_execution_api::TypedInstructionExecutor for crate::SplToken2022Executor { + type Intent = crate::SplToken2022ExecutionIntent; + + fn capability( + &self, + program_id: &kb_model::ProgramId, + operation_code: &str, + ) -> kb_execution_api::ExecutionCapability { + return self.exact_capability(program_id, operation_code); + } + + fn build_prepared_plan( + &self, + intent: &Self::Intent, + ) -> kb_core::Result { + let program_id = kb_model::ProgramId(std::string::String::from( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + )); + return match self.exact_capability(&program_id, intent.operation.operation_code()) { + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => { + crate::build_prepared_plan(intent) + }, + kb_execution_api::ExecutionCapability::Unsupported { reason_code, reason } => { + std::result::Result::Err(kb_core::Error::new(reason_code, reason)) + }, + }; + } +} + +impl kb_execution_api::InstructionExecutor for crate::SplToken2022Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_spl_token_2022"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + return match self.exact_capability(&request.program_id, &request.operation_code) { + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => { + kb_execution_api::ExecutionSupport::Yes + }, + kb_execution_api::ExecutionCapability::Unsupported { reason_code: _, reason: _ } => { + kb_execution_api::ExecutionSupport::No + }, + }; + } + + fn build_plan( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + match self.exact_capability(&request.program_id, &request.operation_code) { + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => {}, + kb_execution_api::ExecutionCapability::Unsupported { reason_code, reason } => { + return std::result::Result::Err(kb_core::Error::new(reason_code, reason)); + }, + } + let intent = + match serde_json::from_str::(&request.payload_json) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_intent_deserialize_failed", + error.to_string(), + )); + }, + }; + if intent.operation.operation_code() != request.operation_code.as_str() { + return std::result::Result::Err(kb_core::Error::new( + "execution_operation_code_mismatch", + format!( + "request operation {} does not match typed intent operation {}", + request.operation_code, + intent.operation.operation_code() + ), + )); + } + let prepared = + match kb_execution_api::TypedInstructionExecutor::build_prepared_plan(self, &intent) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let payload_value = match serde_json::to_value(&prepared) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_plan_serialize_failed", + error.to_string(), + )); + }, + }; + let payload_json = match kb_execution_api::serialize_payload_json(&payload_value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_spl_token_2022"), + instruction_count: prepared.instructions.len(), + payload_json, + }); + } +} + +#[cfg(test)] +mod tests { + fn request(program_id: &str, operation_code: &str) -> kb_execution_api::ExecutionRequest { + return kb_execution_api::ExecutionRequest { + program_id: kb_model::ProgramId(program_id.to_string()), + operation_code: operation_code.to_string(), + payload_json: std::string::String::from("{}"), + }; + } + + #[test] + fn exact_capabilities_cover_current_and_recent_operations_only() { + let executor = crate::SplToken2022Executor; + for operation_code in crate::SUPPORTED_OPERATION_CODES { + let request = request(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, operation_code); + assert_eq!( + kb_execution_api::InstructionExecutor::supports_request(&executor, &request), + kb_execution_api::ExecutionSupport::Yes + ); + } + for operation_code in [ + "spl_token_2022.initialize_mint_with_rent", + "spl_token_2022.initialize_account_with_rent", + "spl_token_2022.initialize_multisig_with_rent", + "spl_token_2022.initialize_account2", + ] { + let capability = kb_execution_api::TypedInstructionExecutor::capability( + &executor, + &kb_model::ProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string()), + operation_code, + ); + match capability { + kb_execution_api::ExecutionCapability::Unsupported { reason_code, reason: _ } => { + assert_eq!( + reason_code, + "execution_spl_token_2022_historical_variant_decode_only" + ); + }, + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => { + panic!("historical variant must be decode-only"); + }, + } + } + } + + #[test] + fn program_dispatch_is_exact() { + let executor = crate::SplToken2022Executor; + let foreign = + request(kb_program_ids::SPL_TOKEN_PROGRAM_ID, crate::TRANSFER_CHECKED_OPERATION); + assert_eq!( + kb_execution_api::InstructionExecutor::supports_request(&executor, &foreign), + kb_execution_api::ExecutionSupport::No + ); + } + + #[test] + fn machine_readable_matrix_matches_executor_policy() { + let matrix = serde_json::from_str::(include_str!( + "../../docs/SPL_TOKEN_2022_MATRIX.json" + )) + .unwrap_or_else(|error| panic!("matrix parsing failed: {error}")); + let instructions = matrix["instructions"] + .as_array() + .unwrap_or_else(|| panic!("matrix instructions must be an array")); + let extension_types = matrix["extensionTypes"] + .as_array() + .unwrap_or_else(|| panic!("matrix extensionTypes must be an array")); + let matrix_names = instructions + .iter() + .chain(extension_types.iter()) + .filter_map(|instruction| return instruction["name"].as_str()) + .collect::>(); + assert_eq!(instructions.len(), 48); + for operation_code in crate::SUPPORTED_OPERATION_CODES { + let name = operation_code + .strip_prefix("spl_token_2022.") + .unwrap_or_else(|| panic!("invalid operation code {operation_code}")); + let matrix_name = match name { + "initialize_interest_bearing_mint" | "update_interest_bearing_rate" => { + "interest_bearing_mint_extension" + }, + "initialize_transfer_fee_config" + | "set_transfer_fee" + | "transfer_checked_with_fee" + | "withdraw_withheld_tokens_from_mint" + | "withdraw_withheld_tokens_from_accounts" + | "harvest_withheld_tokens_to_mint" => "transfer_fee_extension", + "initialize_default_account_state" | "update_default_account_state" => { + "default_account_state_extension" + }, + "enable_required_transfer_memos" | "disable_required_transfer_memos" => { + "memo_transfer_extension" + }, + "enable_cpi_guard" | "disable_cpi_guard" => "cpi_guard_extension", + "initialize_transfer_hook" | "update_transfer_hook" => "transfer_hook_extension", + "initialize_metadata_pointer" | "update_metadata_pointer" => { + "metadata_pointer_extension" + }, + "initialize_group_pointer" | "update_group_pointer" => "group_pointer_extension", + "initialize_group_member_pointer" | "update_group_member_pointer" => { + "group_member_pointer_extension" + }, + "initialize_token_metadata" + | "update_token_metadata_field" + | "remove_token_metadata_key" => "token_metadata", + "initialize_token_group" | "update_token_group_max_size" => "token_group", + "initialize_token_group_member" => "token_group_member", + "initialize_scaled_ui_amount" | "update_scaled_ui_amount_multiplier" => { + "scaled_ui_amount_extension" + }, + "initialize_pausable_config" | "pause" | "resume" => "pausable_extension", + "initialize_permissioned_burn" + | "permissioned_burn" + | "permissioned_burn_checked" + | "permissioned_confidential_burn" => "permissioned_burn_extension", + "initialize_confidential_transfer_mint" + | "update_confidential_transfer_mint" + | "apply_pending_confidential_balance" + | "approve_confidential_transfer_account" + | "deposit_confidential_tokens" + | "enable_confidential_credits" + | "disable_confidential_credits" + | "enable_non_confidential_credits" + | "disable_non_confidential_credits" + | "configure_confidential_transfer_account" + | "configure_confidential_transfer_account_with_registry" + | "empty_confidential_transfer_account" + | "withdraw_confidential_tokens" + | "transfer_confidential_tokens" + | "transfer_confidential_tokens_with_fee" => "confidential_transfer_extension", + "initialize_confidential_transfer_fee_config" + | "enable_confidential_transfer_fee_harvest" + | "disable_confidential_transfer_fee_harvest" + | "harvest_confidential_withheld_tokens_to_mint" + | "withdraw_confidential_withheld_tokens_from_mint" + | "withdraw_confidential_withheld_tokens_from_accounts" => { + "confidential_transfer_fee_extension" + }, + "initialize_confidential_mint_burn_mint" + | "rotate_confidential_supply_elgamal_pubkey" + | "update_confidential_decryptable_supply" + | "apply_pending_confidential_burn" + | "confidential_mint" + | "confidential_burn" => "confidential_mint_burn_extension", + _ => name, + }; + assert!( + matrix_names.contains(matrix_name), + "matrix is missing supported operation {name}" + ); + } + assert!(matrix_names.contains("batch")); + let batch = kb_execution_api::TypedInstructionExecutor::capability( + &crate::SplToken2022Executor, + &kb_model::ProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string()), + crate::BATCH_OPERATION, + ); + match batch { + kb_execution_api::ExecutionCapability::Unsupported { reason_code, reason: _ } => { + assert_eq!(reason_code, "execution_spl_token_2022_batch_builder_unavailable"); + }, + kb_execution_api::ExecutionCapability::Supported { operation_code: _ } => { + panic!("Batch must remain decode-only"); + }, + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/intent.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/intent.rs new file mode 100644 index 0000000..145bcab --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/intent.rs @@ -0,0 +1,1612 @@ +// file: kb_executor_spl_token_2022/src/intent.rs +// version: 23 + +//! Typed Token-2022 execution intents. + +use ts_rs::TS; // rust-rules: derive-import + +/// Stable operation code for mint initialization through `InitializeMint2`. +pub const INITIALIZE_MINT_OPERATION: &str = "spl_token_2022.initialize_mint"; +/// Stable operation code for token-account initialization through `InitializeAccount3`. +pub const INITIALIZE_ACCOUNT_OPERATION: &str = "spl_token_2022.initialize_account"; +/// Stable operation code for multisig initialization through `InitializeMultisig2`. +pub const INITIALIZE_MULTISIG_OPERATION: &str = "spl_token_2022.initialize_multisig"; +/// Stable operation code for an unchecked token transfer. +pub const TRANSFER_OPERATION: &str = "spl_token_2022.transfer"; +/// Stable operation code for an approval. +pub const APPROVE_OPERATION: &str = "spl_token_2022.approve"; +/// Stable operation code for a delegate revocation. +pub const REVOKE_OPERATION: &str = "spl_token_2022.revoke"; +/// Stable operation code for an authority change. +pub const SET_AUTHORITY_OPERATION: &str = "spl_token_2022.set_authority"; +/// Stable operation code for minting tokens. +pub const MINT_TO_OPERATION: &str = "spl_token_2022.mint_to"; +/// Stable operation code for burning tokens. +pub const BURN_OPERATION: &str = "spl_token_2022.burn"; +/// Stable operation code for closing a token account. +pub const CLOSE_ACCOUNT_OPERATION: &str = "spl_token_2022.close_account"; +/// Stable operation code for freezing a token account. +pub const FREEZE_ACCOUNT_OPERATION: &str = "spl_token_2022.freeze_account"; +/// Stable operation code for thawing a token account. +pub const THAW_ACCOUNT_OPERATION: &str = "spl_token_2022.thaw_account"; +/// Stable operation code for a checked token transfer. +pub const TRANSFER_CHECKED_OPERATION: &str = "spl_token_2022.transfer_checked"; +/// Stable operation code for a checked approval. +pub const APPROVE_CHECKED_OPERATION: &str = "spl_token_2022.approve_checked"; +/// Stable operation code for checked minting. +pub const MINT_TO_CHECKED_OPERATION: &str = "spl_token_2022.mint_to_checked"; +/// Stable operation code for checked burning. +pub const BURN_CHECKED_OPERATION: &str = "spl_token_2022.burn_checked"; +/// Stable operation code for synchronizing wrapped SOL. +pub const SYNC_NATIVE_OPERATION: &str = "spl_token_2022.sync_native"; +/// Stable operation code for querying account data size. +pub const GET_ACCOUNT_DATA_SIZE_OPERATION: &str = "spl_token_2022.get_account_data_size"; +/// Stable operation code for the classic compatibility no-op. +pub const INITIALIZE_IMMUTABLE_OWNER_OPERATION: &str = "spl_token_2022.initialize_immutable_owner"; +/// Stable operation code for raw-to-UI amount conversion. +pub const AMOUNT_TO_UI_AMOUNT_OPERATION: &str = "spl_token_2022.amount_to_ui_amount"; +/// Stable operation code for UI-to-raw amount conversion. +pub const UI_AMOUNT_TO_AMOUNT_OPERATION: &str = "spl_token_2022.ui_amount_to_amount"; +/// Stable operation code for rescuing excess lamports. +pub const WITHDRAW_EXCESS_LAMPORTS_OPERATION: &str = "spl_token_2022.withdraw_excess_lamports"; +/// Stable operation code for the recent wrapped-SOL partial unwrap. +pub const UNWRAP_LAMPORTS_OPERATION: &str = "spl_token_2022.unwrap_lamports"; +/// Stable operation code for Token-2022 account reallocation. +pub const REALLOCATE_OPERATION: &str = "spl_token_2022.reallocate"; +/// Stable operation code for initializing a non-transferable mint. +pub const INITIALIZE_NON_TRANSFERABLE_MINT_OPERATION: &str = + "spl_token_2022.initialize_non_transferable_mint"; +/// Stable operation code for initializing a permanent delegate. +pub const INITIALIZE_PERMANENT_DELEGATE_OPERATION: &str = + "spl_token_2022.initialize_permanent_delegate"; +/// Stable operation code for initializing an interest-bearing mint. +pub const INITIALIZE_INTEREST_BEARING_MINT_OPERATION: &str = + "spl_token_2022.initialize_interest_bearing_mint"; +/// Stable operation code for updating an interest-bearing mint rate. +pub const UPDATE_INTEREST_BEARING_RATE_OPERATION: &str = + "spl_token_2022.update_interest_bearing_rate"; +/// Stable operation code for transfer-fee configuration initialization. +pub const INITIALIZE_TRANSFER_FEE_CONFIG_OPERATION: &str = + "spl_token_2022.initialize_transfer_fee_config"; +/// Stable operation code for updating transfer-fee parameters. +pub const SET_TRANSFER_FEE_OPERATION: &str = "spl_token_2022.set_transfer_fee"; +/// Stable operation code for a checked transfer with an expected fee. +pub const TRANSFER_CHECKED_WITH_FEE_OPERATION: &str = "spl_token_2022.transfer_checked_with_fee"; +/// Stable operation code for withdrawing withheld fees from a mint. +pub const WITHDRAW_WITHHELD_TOKENS_FROM_MINT_OPERATION: &str = + "spl_token_2022.withdraw_withheld_tokens_from_mint"; +/// Stable operation code for withdrawing withheld fees from token accounts. +pub const WITHDRAW_WITHHELD_TOKENS_FROM_ACCOUNTS_OPERATION: &str = + "spl_token_2022.withdraw_withheld_tokens_from_accounts"; +/// Stable operation code for harvesting withheld fees into a mint. +pub const HARVEST_WITHHELD_TOKENS_TO_MINT_OPERATION: &str = + "spl_token_2022.harvest_withheld_tokens_to_mint"; +/// Stable operation code for initializing a default account state. +pub const INITIALIZE_DEFAULT_ACCOUNT_STATE_OPERATION: &str = + "spl_token_2022.initialize_default_account_state"; +/// Stable operation code for updating a default account state. +pub const UPDATE_DEFAULT_ACCOUNT_STATE_OPERATION: &str = + "spl_token_2022.update_default_account_state"; +/// Stable operation code for requiring memos on incoming transfers. +pub const ENABLE_REQUIRED_TRANSFER_MEMOS_OPERATION: &str = + "spl_token_2022.enable_required_transfer_memos"; +/// Stable operation code for disabling required incoming-transfer memos. +pub const DISABLE_REQUIRED_TRANSFER_MEMOS_OPERATION: &str = + "spl_token_2022.disable_required_transfer_memos"; +/// Stable operation code for enabling CPI Guard on a token account. +pub const ENABLE_CPI_GUARD_OPERATION: &str = "spl_token_2022.enable_cpi_guard"; +/// Stable operation code for disabling CPI Guard on a token account. +pub const DISABLE_CPI_GUARD_OPERATION: &str = "spl_token_2022.disable_cpi_guard"; +/// Stable operation code for initializing a transfer-hook pointer. +pub const INITIALIZE_TRANSFER_HOOK_OPERATION: &str = "spl_token_2022.initialize_transfer_hook"; +/// Stable operation code for updating or clearing a transfer-hook program id. +pub const UPDATE_TRANSFER_HOOK_OPERATION: &str = "spl_token_2022.update_transfer_hook"; +/// Stable operation code for initializing a metadata pointer. +pub const INITIALIZE_METADATA_POINTER_OPERATION: &str = + "spl_token_2022.initialize_metadata_pointer"; +/// Stable operation code for updating or clearing a metadata pointer. +pub const UPDATE_METADATA_POINTER_OPERATION: &str = "spl_token_2022.update_metadata_pointer"; +/// Stable operation code for initializing a group pointer. +pub const INITIALIZE_GROUP_POINTER_OPERATION: &str = "spl_token_2022.initialize_group_pointer"; +/// Stable operation code for updating or clearing a group pointer. +pub const UPDATE_GROUP_POINTER_OPERATION: &str = "spl_token_2022.update_group_pointer"; +/// Stable operation code for initializing a group-member pointer. +pub const INITIALIZE_GROUP_MEMBER_POINTER_OPERATION: &str = + "spl_token_2022.initialize_group_member_pointer"; +/// Stable operation code for updating or clearing a group-member pointer. +pub const UPDATE_GROUP_MEMBER_POINTER_OPERATION: &str = + "spl_token_2022.update_group_member_pointer"; +/// Stable operation code for initializing Token-2022 embedded metadata. +pub const INITIALIZE_TOKEN_METADATA_OPERATION: &str = "spl_token_2022.initialize_token_metadata"; +/// Stable operation code for updating an embedded metadata field. +pub const UPDATE_TOKEN_METADATA_FIELD_OPERATION: &str = + "spl_token_2022.update_token_metadata_field"; +/// Stable operation code for removing an embedded metadata key. +pub const REMOVE_TOKEN_METADATA_KEY_OPERATION: &str = "spl_token_2022.remove_token_metadata_key"; +/// Stable operation code for initializing an embedded token group. +pub const INITIALIZE_TOKEN_GROUP_OPERATION: &str = "spl_token_2022.initialize_token_group"; +/// Stable operation code for updating an embedded token-group maximum size. +pub const UPDATE_TOKEN_GROUP_MAX_SIZE_OPERATION: &str = + "spl_token_2022.update_token_group_max_size"; +/// Stable operation code for initializing an embedded token-group member. +pub const INITIALIZE_TOKEN_GROUP_MEMBER_OPERATION: &str = + "spl_token_2022.initialize_token_group_member"; +/// Stable operation code for initializing scaled UI amounts. +pub const INITIALIZE_SCALED_UI_AMOUNT_OPERATION: &str = + "spl_token_2022.initialize_scaled_ui_amount"; +/// Stable operation code for scheduling a scaled UI multiplier update. +pub const UPDATE_SCALED_UI_AMOUNT_MULTIPLIER_OPERATION: &str = + "spl_token_2022.update_scaled_ui_amount_multiplier"; +/// Stable operation code for initializing pausable mint state. +pub const INITIALIZE_PAUSABLE_CONFIG_OPERATION: &str = "spl_token_2022.initialize_pausable_config"; +/// Stable operation code for pausing mint activity. +pub const PAUSE_OPERATION: &str = "spl_token_2022.pause"; +/// Stable operation code for resuming mint activity. +pub const RESUME_OPERATION: &str = "spl_token_2022.resume"; +/// Stable operation code for initializing permissioned burn. +pub const INITIALIZE_PERMISSIONED_BURN_OPERATION: &str = + "spl_token_2022.initialize_permissioned_burn"; +/// Stable operation code for a permissioned burn. +pub const PERMISSIONED_BURN_OPERATION: &str = "spl_token_2022.permissioned_burn"; +/// Stable operation code for a checked permissioned burn. +pub const PERMISSIONED_BURN_CHECKED_OPERATION: &str = "spl_token_2022.permissioned_burn_checked"; +/// Stable operation code for initializing Confidential Transfer mint configuration. +pub const INITIALIZE_CONFIDENTIAL_TRANSFER_MINT_OPERATION: &str = + "spl_token_2022.initialize_confidential_transfer_mint"; +/// Stable operation code for updating Confidential Transfer mint configuration. +pub const UPDATE_CONFIDENTIAL_TRANSFER_MINT_OPERATION: &str = + "spl_token_2022.update_confidential_transfer_mint"; +/// Stable operation code for applying pending confidential balance credits. +pub const APPLY_PENDING_CONFIDENTIAL_BALANCE_OPERATION: &str = + "spl_token_2022.apply_pending_confidential_balance"; +/// Stable operation code for approving a configured confidential account. +pub const APPROVE_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION: &str = + "spl_token_2022.approve_confidential_transfer_account"; +/// Stable operation code for depositing public tokens into a confidential pending balance. +pub const DEPOSIT_CONFIDENTIAL_TOKENS_OPERATION: &str = + "spl_token_2022.deposit_confidential_tokens"; +/// Stable operation code for enabling incoming confidential credits. +pub const ENABLE_CONFIDENTIAL_CREDITS_OPERATION: &str = + "spl_token_2022.enable_confidential_credits"; +/// Stable operation code for disabling incoming confidential credits. +pub const DISABLE_CONFIDENTIAL_CREDITS_OPERATION: &str = + "spl_token_2022.disable_confidential_credits"; +/// Stable operation code for enabling incoming non-confidential credits. +pub const ENABLE_NON_CONFIDENTIAL_CREDITS_OPERATION: &str = + "spl_token_2022.enable_non_confidential_credits"; +/// Stable operation code for disabling incoming non-confidential credits. +pub const DISABLE_NON_CONFIDENTIAL_CREDITS_OPERATION: &str = + "spl_token_2022.disable_non_confidential_credits"; +/// Stable operation code for configuring one confidential token account. +pub const CONFIGURE_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION: &str = + "spl_token_2022.configure_confidential_transfer_account"; +/// Stable operation code for configuring one confidential token account through the registry. +pub const CONFIGURE_CONFIDENTIAL_TRANSFER_ACCOUNT_WITH_REGISTRY_OPERATION: &str = + "spl_token_2022.configure_confidential_transfer_account_with_registry"; +/// Stable operation code for emptying a confidential available balance before close. +pub const EMPTY_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION: &str = + "spl_token_2022.empty_confidential_transfer_account"; +/// Stable operation code for withdrawing confidential balance into public balance. +pub const WITHDRAW_CONFIDENTIAL_TOKENS_OPERATION: &str = + "spl_token_2022.withdraw_confidential_tokens"; +/// Stable operation code for transferring confidential tokens. +pub const TRANSFER_CONFIDENTIAL_TOKENS_OPERATION: &str = + "spl_token_2022.transfer_confidential_tokens"; +/// Stable operation code for confidential transfer with fee. +pub const TRANSFER_CONFIDENTIAL_TOKENS_WITH_FEE_OPERATION: &str = + "spl_token_2022.transfer_confidential_tokens_with_fee"; +/// Stable operation code for initializing confidential transfer fee configuration. +pub const INITIALIZE_CONFIDENTIAL_TRANSFER_FEE_CONFIG_OPERATION: &str = + "spl_token_2022.initialize_confidential_transfer_fee_config"; +/// Stable operation code for enabling confidential fee harvest to the mint. +pub const ENABLE_CONFIDENTIAL_TRANSFER_FEE_HARVEST_OPERATION: &str = + "spl_token_2022.enable_confidential_transfer_fee_harvest"; +/// Stable operation code for disabling confidential fee harvest to the mint. +pub const DISABLE_CONFIDENTIAL_TRANSFER_FEE_HARVEST_OPERATION: &str = + "spl_token_2022.disable_confidential_transfer_fee_harvest"; +/// Stable operation code for harvesting confidential withheld fees to the mint. +pub const HARVEST_CONFIDENTIAL_WITHHELD_TOKENS_TO_MINT_OPERATION: &str = + "spl_token_2022.harvest_confidential_withheld_tokens_to_mint"; +/// Stable operation code for withdrawing confidential withheld fees from the mint. +pub const WITHDRAW_CONFIDENTIAL_WITHHELD_TOKENS_FROM_MINT_OPERATION: &str = + "spl_token_2022.withdraw_confidential_withheld_tokens_from_mint"; +/// Stable operation code for withdrawing confidential withheld fees from token accounts. +pub const WITHDRAW_CONFIDENTIAL_WITHHELD_TOKENS_FROM_ACCOUNTS_OPERATION: &str = + "spl_token_2022.withdraw_confidential_withheld_tokens_from_accounts"; +/// Stable operation code for initializing confidential mint and burn. +pub const INITIALIZE_CONFIDENTIAL_MINT_BURN_MINT_OPERATION: &str = + "spl_token_2022.initialize_confidential_mint_burn_mint"; +/// Stable operation code for rotating the confidential supply ElGamal public key. +pub const ROTATE_CONFIDENTIAL_SUPPLY_ELGAMAL_PUBKEY_OPERATION: &str = + "spl_token_2022.rotate_confidential_supply_elgamal_pubkey"; +/// Stable operation code for updating the decryptable confidential supply. +pub const UPDATE_CONFIDENTIAL_DECRYPTABLE_SUPPLY_OPERATION: &str = + "spl_token_2022.update_confidential_decryptable_supply"; +/// Stable operation code for applying pending confidential burns. +pub const APPLY_PENDING_CONFIDENTIAL_BURN_OPERATION: &str = + "spl_token_2022.apply_pending_confidential_burn"; +/// Stable operation code for confidential minting. +pub const CONFIDENTIAL_MINT_OPERATION: &str = "spl_token_2022.confidential_mint"; +/// Stable operation code for confidential burning. +pub const CONFIDENTIAL_BURN_OPERATION: &str = "spl_token_2022.confidential_burn"; +/// Stable operation code for permissioned confidential burning. +pub const PERMISSIONED_CONFIDENTIAL_BURN_OPERATION: &str = + "spl_token_2022.permissioned_confidential_burn"; + +/// Stable operation code for a recent bounded batch. +pub const BATCH_OPERATION: &str = "spl_token_2022.batch"; + +/// Operation codes constructible by this executor. +pub const SUPPORTED_OPERATION_CODES: &[&str] = &[ + INITIALIZE_MINT_OPERATION, + INITIALIZE_ACCOUNT_OPERATION, + INITIALIZE_MULTISIG_OPERATION, + TRANSFER_OPERATION, + APPROVE_OPERATION, + REVOKE_OPERATION, + SET_AUTHORITY_OPERATION, + MINT_TO_OPERATION, + BURN_OPERATION, + CLOSE_ACCOUNT_OPERATION, + FREEZE_ACCOUNT_OPERATION, + THAW_ACCOUNT_OPERATION, + TRANSFER_CHECKED_OPERATION, + APPROVE_CHECKED_OPERATION, + MINT_TO_CHECKED_OPERATION, + BURN_CHECKED_OPERATION, + SYNC_NATIVE_OPERATION, + GET_ACCOUNT_DATA_SIZE_OPERATION, + INITIALIZE_IMMUTABLE_OWNER_OPERATION, + AMOUNT_TO_UI_AMOUNT_OPERATION, + UI_AMOUNT_TO_AMOUNT_OPERATION, + WITHDRAW_EXCESS_LAMPORTS_OPERATION, + UNWRAP_LAMPORTS_OPERATION, + REALLOCATE_OPERATION, + INITIALIZE_NON_TRANSFERABLE_MINT_OPERATION, + INITIALIZE_PERMANENT_DELEGATE_OPERATION, + INITIALIZE_INTEREST_BEARING_MINT_OPERATION, + UPDATE_INTEREST_BEARING_RATE_OPERATION, + INITIALIZE_TRANSFER_FEE_CONFIG_OPERATION, + SET_TRANSFER_FEE_OPERATION, + TRANSFER_CHECKED_WITH_FEE_OPERATION, + WITHDRAW_WITHHELD_TOKENS_FROM_MINT_OPERATION, + WITHDRAW_WITHHELD_TOKENS_FROM_ACCOUNTS_OPERATION, + HARVEST_WITHHELD_TOKENS_TO_MINT_OPERATION, + INITIALIZE_DEFAULT_ACCOUNT_STATE_OPERATION, + UPDATE_DEFAULT_ACCOUNT_STATE_OPERATION, + ENABLE_REQUIRED_TRANSFER_MEMOS_OPERATION, + DISABLE_REQUIRED_TRANSFER_MEMOS_OPERATION, + ENABLE_CPI_GUARD_OPERATION, + DISABLE_CPI_GUARD_OPERATION, + INITIALIZE_TRANSFER_HOOK_OPERATION, + UPDATE_TRANSFER_HOOK_OPERATION, + INITIALIZE_METADATA_POINTER_OPERATION, + UPDATE_METADATA_POINTER_OPERATION, + INITIALIZE_GROUP_POINTER_OPERATION, + UPDATE_GROUP_POINTER_OPERATION, + INITIALIZE_GROUP_MEMBER_POINTER_OPERATION, + UPDATE_GROUP_MEMBER_POINTER_OPERATION, + INITIALIZE_TOKEN_METADATA_OPERATION, + UPDATE_TOKEN_METADATA_FIELD_OPERATION, + REMOVE_TOKEN_METADATA_KEY_OPERATION, + INITIALIZE_TOKEN_GROUP_OPERATION, + UPDATE_TOKEN_GROUP_MAX_SIZE_OPERATION, + INITIALIZE_TOKEN_GROUP_MEMBER_OPERATION, + INITIALIZE_SCALED_UI_AMOUNT_OPERATION, + UPDATE_SCALED_UI_AMOUNT_MULTIPLIER_OPERATION, + INITIALIZE_PAUSABLE_CONFIG_OPERATION, + PAUSE_OPERATION, + RESUME_OPERATION, + INITIALIZE_PERMISSIONED_BURN_OPERATION, + PERMISSIONED_BURN_OPERATION, + PERMISSIONED_BURN_CHECKED_OPERATION, + INITIALIZE_CONFIDENTIAL_TRANSFER_MINT_OPERATION, + UPDATE_CONFIDENTIAL_TRANSFER_MINT_OPERATION, + APPLY_PENDING_CONFIDENTIAL_BALANCE_OPERATION, + APPROVE_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION, + DEPOSIT_CONFIDENTIAL_TOKENS_OPERATION, + ENABLE_CONFIDENTIAL_CREDITS_OPERATION, + DISABLE_CONFIDENTIAL_CREDITS_OPERATION, + ENABLE_NON_CONFIDENTIAL_CREDITS_OPERATION, + DISABLE_NON_CONFIDENTIAL_CREDITS_OPERATION, + CONFIGURE_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION, + CONFIGURE_CONFIDENTIAL_TRANSFER_ACCOUNT_WITH_REGISTRY_OPERATION, + EMPTY_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION, + WITHDRAW_CONFIDENTIAL_TOKENS_OPERATION, + TRANSFER_CONFIDENTIAL_TOKENS_OPERATION, + TRANSFER_CONFIDENTIAL_TOKENS_WITH_FEE_OPERATION, + INITIALIZE_CONFIDENTIAL_TRANSFER_FEE_CONFIG_OPERATION, + ENABLE_CONFIDENTIAL_TRANSFER_FEE_HARVEST_OPERATION, + DISABLE_CONFIDENTIAL_TRANSFER_FEE_HARVEST_OPERATION, + HARVEST_CONFIDENTIAL_WITHHELD_TOKENS_TO_MINT_OPERATION, + WITHDRAW_CONFIDENTIAL_WITHHELD_TOKENS_FROM_MINT_OPERATION, + WITHDRAW_CONFIDENTIAL_WITHHELD_TOKENS_FROM_ACCOUNTS_OPERATION, + INITIALIZE_CONFIDENTIAL_MINT_BURN_MINT_OPERATION, + ROTATE_CONFIDENTIAL_SUPPLY_ELGAMAL_PUBKEY_OPERATION, + UPDATE_CONFIDENTIAL_DECRYPTABLE_SUPPLY_OPERATION, + APPLY_PENDING_CONFIDENTIAL_BURN_OPERATION, + CONFIDENTIAL_MINT_OPERATION, + CONFIDENTIAL_BURN_OPERATION, + PERMISSIONED_CONFIDENTIAL_BURN_OPERATION, +]; + +/// Exact unsigned on-chain amount represented as a decimal JSON string. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token_2022/intent/SplTokenAmount.ts" +)] +pub struct SplTokenAmount( + /// Canonical unsigned decimal representation. + pub std::string::String, +); + +/// Positive normal finite scaled-UI multiplier represented as a decimal JSON string. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token_2022/intent/SplTokenMultiplier.ts" +)] +pub struct SplTokenMultiplier( + /// Decimal representation parsed to the official `f64` wire contract. + pub std::string::String, +); + +/// Ordered simple or multisig authority supplied to an instruction builder. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token_2022/intent/SplTokenAuthority.ts" +)] +pub struct SplTokenAuthority { + /// Authority account. It signs only when `multisig_signers` is empty. + pub authority: kb_model::Pubkey, + /// Ordered multisig signer occurrences. Duplicate metas remain ordered. + pub multisig_signers: std::vec::Vec, +} + +/// Authority domain used by `SetAuthority`. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token_2022/intent/SplTokenAuthorityType.ts" +)] +pub enum SplTokenAuthorityType { + /// Mint authority. + MintTokens, + /// Freeze authority. + FreezeAccount, + /// Token-account owner. + AccountOwner, + /// Token-account close authority. + CloseAccount, +} + +/// Default state assigned to newly initialized Token-2022 accounts. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token_2022/intent/SplTokenDefaultAccountState.ts" +)] +pub enum SplTokenDefaultAccountState { + /// Newly initialized accounts remain usable. + Initialized, + /// Newly initialized accounts start frozen. + Frozen, +} + +/// Token-metadata field selected for an update. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(rename_all = "snake_case", tag = "kind", content = "key")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token_2022/intent/SplTokenMetadataField.ts" +)] +pub enum SplTokenMetadataField { + /// Update the required name field. + Name, + /// Update the required symbol field. + Symbol, + /// Update the required URI field. + Uri, + /// Update or create an additional key. + Key(std::string::String), +} + +/// One officially constructible non-batch instruction. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(tag = "instruction", rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token_2022/intent/SplTokenSingleOperation.ts" +)] +pub enum SplTokenSingleOperation { + /// Initialize a mint with the current no-Rent builder. + InitializeMint { + /// Mint account already created for the classic Token program. + mint: kb_model::Pubkey, + /// Mint authority. + mint_authority: kb_model::Pubkey, + /// Optional freeze authority. + freeze_authority: std::option::Option, + /// Mint decimals. + decimals: u8, + }, + /// Initialize a token account with the current no-Rent builder. + InitializeAccount { + /// Token account already created for the classic Token program. + account: kb_model::Pubkey, + /// Mint associated with the account. + mint: kb_model::Pubkey, + /// Token-account owner. + owner: kb_model::Pubkey, + }, + /// Initialize a multisig with the current no-Rent builder. + InitializeMultisig { + /// Multisig account already created for the classic Token program. + multisig: kb_model::Pubkey, + /// Ordered member accounts. + members: std::vec::Vec, + /// Required member threshold. + threshold: u8, + }, + /// Transfer a raw token amount without carrying the mint or decimals in the wire. + Transfer { + /// Source token account. + source: kb_model::Pubkey, + /// Destination token account. + destination: kb_model::Pubkey, + /// Owner, delegate or multisig authority. + authority: crate::SplTokenAuthority, + /// Exact raw token amount. + amount: crate::SplTokenAmount, + }, + /// Approve a delegate for a raw token allowance. + Approve { + /// Source token account. + source: kb_model::Pubkey, + /// Delegate account. + delegate: kb_model::Pubkey, + /// Owner or multisig authority. + authority: crate::SplTokenAuthority, + /// Exact raw allowance. + amount: crate::SplTokenAmount, + }, + /// Revoke the current delegate. + Revoke { + /// Source token account. + source: kb_model::Pubkey, + /// Owner or multisig authority. + authority: crate::SplTokenAuthority, + }, + /// Change or revoke an authority. + SetAuthority { + /// Mint or token account whose authority changes. + owned: kb_model::Pubkey, + /// Authority domain. + authority_type: crate::SplTokenAuthorityType, + /// New authority, or `None` to revoke it. + new_authority: std::option::Option, + /// Current simple or multisig authority. + current_authority: crate::SplTokenAuthority, + }, + /// Mint a raw token amount. + MintTo { + /// Mint account. + mint: kb_model::Pubkey, + /// Destination token account. + destination: kb_model::Pubkey, + /// Mint authority. + authority: crate::SplTokenAuthority, + /// Exact raw amount. + amount: crate::SplTokenAmount, + }, + /// Burn a raw token amount. + Burn { + /// Source token account. + source: kb_model::Pubkey, + /// Mint account. + mint: kb_model::Pubkey, + /// Owner, delegate or multisig authority. + authority: crate::SplTokenAuthority, + /// Exact raw amount. + amount: crate::SplTokenAmount, + }, + /// Close a token account. + CloseAccount { + /// Token account to close. + account: kb_model::Pubkey, + /// Lamport destination. + destination: kb_model::Pubkey, + /// Owner or close authority. + authority: crate::SplTokenAuthority, + }, + /// Freeze a token account. + FreezeAccount { + /// Token account to freeze. + account: kb_model::Pubkey, + /// Mint account. + mint: kb_model::Pubkey, + /// Freeze authority. + authority: crate::SplTokenAuthority, + }, + /// Thaw a token account. + ThawAccount { + /// Token account to thaw. + account: kb_model::Pubkey, + /// Mint account. + mint: kb_model::Pubkey, + /// Freeze authority. + authority: crate::SplTokenAuthority, + }, + /// Transfer while checking an explicit mint and decimals value. + TransferChecked { + /// Source token account. + source: kb_model::Pubkey, + /// Mint account. + mint: kb_model::Pubkey, + /// Destination token account. + destination: kb_model::Pubkey, + /// Owner, delegate or multisig authority. + authority: crate::SplTokenAuthority, + /// Exact raw amount. + amount: crate::SplTokenAmount, + /// Expected mint decimals. + decimals: u8, + }, + /// Approve a delegate while checking an explicit mint and decimals value. + ApproveChecked { + /// Source token account. + source: kb_model::Pubkey, + /// Mint account. + mint: kb_model::Pubkey, + /// Delegate account. + delegate: kb_model::Pubkey, + /// Owner or multisig authority. + authority: crate::SplTokenAuthority, + /// Exact raw allowance. + amount: crate::SplTokenAmount, + /// Expected mint decimals. + decimals: u8, + }, + /// Mint while checking explicit decimals. + MintToChecked { + /// Mint account. + mint: kb_model::Pubkey, + /// Destination token account. + destination: kb_model::Pubkey, + /// Mint authority. + authority: crate::SplTokenAuthority, + /// Exact raw amount. + amount: crate::SplTokenAmount, + /// Expected mint decimals. + decimals: u8, + }, + /// Burn while checking explicit decimals. + BurnChecked { + /// Source token account. + source: kb_model::Pubkey, + /// Mint account. + mint: kb_model::Pubkey, + /// Owner, delegate or multisig authority. + authority: crate::SplTokenAuthority, + /// Exact raw amount. + amount: crate::SplTokenAmount, + /// Expected mint decimals. + decimals: u8, + }, + /// Synchronize a wrapped-SOL account. + SyncNative { + /// Wrapped-SOL token account. + account: kb_model::Pubkey, + /// Include the optional Rent sysvar account published by interface 3.0.0. + include_rent_sysvar: bool, + }, + /// Query the token-account size for a mint. + GetAccountDataSize { + /// Mint account. + mint: kb_model::Pubkey, + }, + /// Build the classic-program immutable-owner compatibility no-op. + InitializeImmutableOwner { + /// Token account being prepared. + account: kb_model::Pubkey, + }, + /// Convert a raw amount using mint decimals through return data. + AmountToUiAmount { + /// Mint account. + mint: kb_model::Pubkey, + /// Exact raw amount. + amount: crate::SplTokenAmount, + }, + /// Convert a UI amount using mint decimals through return data. + UiAmountToAmount { + /// Mint account. + mint: kb_model::Pubkey, + /// Exact UTF-8 UI amount. + ui_amount: std::string::String, + }, + /// Withdraw lamports above rent exemption. + WithdrawExcessLamports { + /// Program-owned source account. + account: kb_model::Pubkey, + /// Lamport destination. + destination: kb_model::Pubkey, + /// Owner or multisig authority. + authority: crate::SplTokenAuthority, + }, + /// Reallocate a token account to add extension storage. + Reallocate { + /// Token account to grow. + account: kb_model::Pubkey, + /// Payer funding rent growth. + payer: kb_model::Pubkey, + /// Owner or multisig authority. + authority: crate::SplTokenAuthority, + /// Ordered published extension type codes. + extension_types: std::vec::Vec, + }, + /// Initialize the non-transferable mint extension before mint initialization. + InitializeNonTransferableMint { + /// Mint receiving the non-transferable extension. + mint: kb_model::Pubkey, + }, + /// Initialize a permanent delegate before mint initialization. + InitializePermanentDelegate { + /// Mint receiving the permanent delegate extension. + mint: kb_model::Pubkey, + /// Permanent delegate public key. + delegate: kb_model::Pubkey, + }, + /// Initialize interest-bearing configuration before mint initialization. + InitializeInterestBearingMint { + /// Mint receiving the interest-bearing extension. + mint: kb_model::Pubkey, + /// Optional authority allowed to update the rate. + rate_authority: std::option::Option, + /// Initial signed interest rate in basis points. + rate_basis_points: i16, + }, + /// Update the interest-bearing rate through a simple or multisig authority. + UpdateInterestBearingRate { + /// Interest-bearing mint to update. + mint: kb_model::Pubkey, + /// Current rate authority or multisig authority. + authority: crate::SplTokenAuthority, + /// New signed interest rate in basis points. + rate_basis_points: i16, + }, + /// Initialize transfer-fee configuration before mint initialization. + InitializeTransferFeeConfig { + /// Mint receiving the extension. + mint: kb_model::Pubkey, + /// Optional authority allowed to update fee parameters. + transfer_fee_config_authority: std::option::Option, + /// Optional authority allowed to withdraw withheld fees. + withdraw_withheld_authority: std::option::Option, + /// Fee in basis points, bounded by the official interface. + transfer_fee_basis_points: u16, + /// Maximum raw fee amount. + maximum_fee: crate::SplTokenAmount, + }, + /// Update transfer-fee parameters. + SetTransferFee { + /// Mint containing `TransferFeeConfig`. + mint: kb_model::Pubkey, + /// Current transfer-fee configuration authority. + authority: crate::SplTokenAuthority, + /// New fee in basis points. + transfer_fee_basis_points: u16, + /// New maximum raw fee amount. + maximum_fee: crate::SplTokenAmount, + }, + /// Transfer a checked amount while asserting the exact expected fee. + TransferCheckedWithFee { + /// Source token account. + source: kb_model::Pubkey, + /// Mint account. + mint: kb_model::Pubkey, + /// Destination token account. + destination: kb_model::Pubkey, + /// Owner, delegate or multisig authority. + authority: crate::SplTokenAuthority, + /// Exact raw amount. + amount: crate::SplTokenAmount, + /// Mint decimals asserted by the wire. + decimals: u8, + /// Exact expected raw fee. + fee: crate::SplTokenAmount, + }, + /// Withdraw all fees withheld by a mint. + WithdrawWithheldTokensFromMint { + /// Mint containing `TransferFeeConfig`. + mint: kb_model::Pubkey, + /// Destination token account. + destination: kb_model::Pubkey, + /// Withdraw-withheld authority. + authority: crate::SplTokenAuthority, + }, + /// Withdraw all fees withheld by an ordered set of token accounts. + WithdrawWithheldTokensFromAccounts { + /// Mint shared by all source accounts. + mint: kb_model::Pubkey, + /// Destination token account. + destination: kb_model::Pubkey, + /// Withdraw-withheld authority. + authority: crate::SplTokenAuthority, + /// Ordered source token accounts. + sources: std::vec::Vec, + }, + /// Permissionlessly harvest withheld fees from token accounts into a mint. + HarvestWithheldTokensToMint { + /// Destination mint. + mint: kb_model::Pubkey, + /// Ordered source token accounts. + sources: std::vec::Vec, + }, + /// Initialize the default state for accounts created from a mint. + InitializeDefaultAccountState { + /// Mint receiving the extension before mint initialization. + mint: kb_model::Pubkey, + /// Default state for subsequently initialized token accounts. + state: crate::SplTokenDefaultAccountState, + }, + /// Update the default state through the mint freeze authority. + UpdateDefaultAccountState { + /// Mint containing the extension. + mint: kb_model::Pubkey, + /// Current freeze authority or multisig authority. + authority: crate::SplTokenAuthority, + /// New default account state. + state: crate::SplTokenDefaultAccountState, + }, + /// Require incoming transfers to be preceded by a memo. + EnableRequiredTransferMemos { + /// Token account to update. + account: kb_model::Pubkey, + /// Token-account owner or multisig authority. + authority: crate::SplTokenAuthority, + }, + /// Stop requiring incoming-transfer memos. + DisableRequiredTransferMemos { + /// Token account to update. + account: kb_model::Pubkey, + /// Token-account owner or multisig authority. + authority: crate::SplTokenAuthority, + }, + /// Enable CPI Guard for a token account. + EnableCpiGuard { + /// Token account to update. + account: kb_model::Pubkey, + /// Token-account owner or multisig authority. + authority: crate::SplTokenAuthority, + }, + /// Disable CPI Guard for a token account. + DisableCpiGuard { + /// Token account to update. + account: kb_model::Pubkey, + /// Token-account owner or multisig authority. + authority: crate::SplTokenAuthority, + }, + /// Initialize a transfer-hook extension before mint initialization. + InitializeTransferHook { + /// Mint receiving the extension. + mint: kb_model::Pubkey, + /// Optional authority allowed to update the hook program id. + authority: std::option::Option, + /// Optional program invoked during transfers. + program_id: std::option::Option, + }, + /// Update or clear the transfer-hook program id. + UpdateTransferHook { + /// Mint containing the extension. + mint: kb_model::Pubkey, + /// Current simple or multisig authority. + authority: crate::SplTokenAuthority, + /// New hook program id, or `None` to clear it. + program_id: std::option::Option, + }, + /// Initialize a metadata pointer before mint initialization. + InitializeMetadataPointer { + /// Mint receiving the extension. + mint: kb_model::Pubkey, + /// Optional authority allowed to update the pointer. + authority: std::option::Option, + /// Optional account holding metadata. + metadata_address: std::option::Option, + }, + /// Update or clear a metadata pointer. + UpdateMetadataPointer { + /// Mint containing the extension. + mint: kb_model::Pubkey, + /// Current simple or multisig authority. + authority: crate::SplTokenAuthority, + /// New metadata account, or `None` to clear it. + metadata_address: std::option::Option, + }, + /// Initialize a group pointer before mint initialization. + InitializeGroupPointer { + /// Mint receiving the extension. + mint: kb_model::Pubkey, + /// Optional authority allowed to update the pointer. + authority: std::option::Option, + /// Optional account holding the group. + group_address: std::option::Option, + }, + /// Update or clear a group pointer. + UpdateGroupPointer { + /// Mint containing the extension. + mint: kb_model::Pubkey, + /// Current simple or multisig authority. + authority: crate::SplTokenAuthority, + /// New group account, or `None` to clear it. + group_address: std::option::Option, + }, + /// Initialize a group-member pointer before mint initialization. + InitializeGroupMemberPointer { + /// Mint receiving the extension. + mint: kb_model::Pubkey, + /// Optional authority allowed to update the pointer. + authority: std::option::Option, + /// Optional account holding the group member. + member_address: std::option::Option, + }, + /// Update or clear a group-member pointer. + UpdateGroupMemberPointer { + /// Mint containing the extension. + mint: kb_model::Pubkey, + /// Current simple or multisig authority. + authority: crate::SplTokenAuthority, + /// New group-member account, or `None` to clear it. + member_address: std::option::Option, + }, + /// Initialize Token-2022 metadata in the mint account. + InitializeTokenMetadata { + /// Metadata account, normally the mint itself. + metadata: kb_model::Pubkey, + /// Update authority stored in metadata. + update_authority: kb_model::Pubkey, + /// Associated mint. + mint: kb_model::Pubkey, + /// Current mint authority signer. + mint_authority: kb_model::Pubkey, + /// Bounded token name. + name: std::string::String, + /// Bounded token symbol. + symbol: std::string::String, + /// Bounded metadata URI. + uri: std::string::String, + }, + /// Update a required or additional metadata field. + UpdateTokenMetadataField { + /// Metadata account. + metadata: kb_model::Pubkey, + /// Current update authority signer. + update_authority: kb_model::Pubkey, + /// Field to update. + field: crate::SplTokenMetadataField, + /// New bounded value. + value: std::string::String, + }, + /// Remove an additional metadata key. + RemoveTokenMetadataKey { + /// Metadata account. + metadata: kb_model::Pubkey, + /// Current update authority signer. + update_authority: kb_model::Pubkey, + /// Additional key to remove. + key: std::string::String, + /// Whether a missing key is accepted. + idempotent: bool, + }, + /// Initialize a Token Group entry. + InitializeTokenGroup { + /// Group state account, normally the group mint. + group: kb_model::Pubkey, + /// Group mint. + mint: kb_model::Pubkey, + /// Current mint authority signer. + mint_authority: kb_model::Pubkey, + /// Optional future group update authority. + update_authority: std::option::Option, + /// Maximum number of members. + max_size: u64, + }, + /// Update a Token Group maximum size. + UpdateTokenGroupMaxSize { + /// Group state account. + group: kb_model::Pubkey, + /// Current group update authority signer. + update_authority: kb_model::Pubkey, + /// New maximum number of members. + max_size: u64, + }, + /// Initialize a Token Group Member entry. + InitializeTokenGroupMember { + /// Member state account, normally the member mint. + member: kb_model::Pubkey, + /// Member mint. + member_mint: kb_model::Pubkey, + /// Member mint authority signer. + member_mint_authority: kb_model::Pubkey, + /// Group state account. + group: kb_model::Pubkey, + /// Group update authority signer. + group_update_authority: kb_model::Pubkey, + }, + /// Initialize scaled UI amount configuration before mint initialization. + InitializeScaledUiAmount { + /// Mint receiving the extension. + mint: kb_model::Pubkey, + /// Optional future multiplier authority. + authority: std::option::Option, + /// Positive normal finite initial multiplier. + multiplier: crate::SplTokenMultiplier, + }, + /// Schedule or immediately apply a scaled UI multiplier update. + UpdateScaledUiAmountMultiplier { + /// Mint containing the extension. + mint: kb_model::Pubkey, + /// Current simple or multisig multiplier authority. + authority: crate::SplTokenAuthority, + /// Positive normal finite new multiplier. + multiplier: crate::SplTokenMultiplier, + /// UNIX timestamp at which the multiplier becomes effective. + effective_timestamp: i64, + }, + /// Initialize pausable configuration before mint initialization. + InitializePausableConfig { + /// Mint receiving the extension. + mint: kb_model::Pubkey, + /// Authority permitted to pause and resume the mint. + authority: kb_model::Pubkey, + }, + /// Pause minting, burning and transferring. + Pause { + /// Mint containing pausable configuration. + mint: kb_model::Pubkey, + /// Current simple or multisig pause authority. + authority: crate::SplTokenAuthority, + }, + /// Resume minting, burning and transferring. + Resume { + /// Mint containing pausable configuration. + mint: kb_model::Pubkey, + /// Current simple or multisig pause authority. + authority: crate::SplTokenAuthority, + }, + /// Initialize permissioned burn before mint initialization. + InitializePermissionedBurn { + /// Mint receiving the extension. + mint: kb_model::Pubkey, + /// Mandatory permissioned burn authority. + authority: kb_model::Pubkey, + }, + /// Burn using both the permissioned authority and source owner/delegate. + PermissionedBurn { + /// Source token account. + source: kb_model::Pubkey, + /// Token mint. + mint: kb_model::Pubkey, + /// Dedicated permissioned burn authority signer. + permissioned_burn_authority: kb_model::Pubkey, + /// Source owner/delegate, simple or multisig. + authority: crate::SplTokenAuthority, + /// Raw amount to burn. + amount: crate::SplTokenAmount, + }, + /// Checked permissioned burn with exact mint decimals. + PermissionedBurnChecked { + /// Source token account. + source: kb_model::Pubkey, + /// Token mint. + mint: kb_model::Pubkey, + /// Dedicated permissioned burn authority signer. + permissioned_burn_authority: kb_model::Pubkey, + /// Source owner/delegate, simple or multisig. + authority: crate::SplTokenAuthority, + /// Raw amount to burn. + amount: crate::SplTokenAmount, + /// Expected mint decimals. + decimals: u8, + }, + /// Initialize Confidential Transfer configuration before mint initialization. + InitializeConfidentialTransferMint { + /// Mint receiving the extension. + mint: kb_model::Pubkey, + /// Optional authority for configuration updates and account approval. + authority: std::option::Option, + /// Whether newly configured accounts are approved automatically. + auto_approve_new_accounts: bool, + /// Optional caller-generated auditor ElGamal public key. + auditor_elgamal_pubkey: std::option::Option, + }, + /// Update Confidential Transfer mint configuration. + UpdateConfidentialTransferMint { + /// Mint containing the extension. + mint: kb_model::Pubkey, + /// Current configuration authority, simple or multisig. + authority: crate::SplTokenAuthority, + /// Whether newly configured accounts are approved automatically. + auto_approve_new_accounts: bool, + /// Optional caller-generated auditor ElGamal public key. + auditor_elgamal_pubkey: std::option::Option, + }, + /// Apply pending confidential credits to the available balance. + ApplyPendingConfidentialBalance { + /// Token account whose pending credits are applied. + account: kb_model::Pubkey, + /// Expected pending-balance credit counter. + expected_pending_balance_credit_counter: u64, + /// Caller-generated new decryptable available balance. + new_decryptable_available_balance: crate::SplTokenDecryptableBalance, + /// Owner or delegate, simple or multisig. + authority: crate::SplTokenAuthority, + }, + /// Approve a configured account for confidential transfers. + ApproveConfidentialTransferAccount { + /// Token account to approve. + account: kb_model::Pubkey, + /// Mint containing the confidential-transfer configuration. + mint: kb_model::Pubkey, + /// Confidential-transfer mint authority, simple or multisig. + authority: crate::SplTokenAuthority, + }, + /// Deposit a public token amount into the account confidential pending balance. + DepositConfidentialTokens { + /// Token account receiving the confidential pending credit. + account: kb_model::Pubkey, + /// Token mint. + mint: kb_model::Pubkey, + /// Owner or delegate, simple or multisig. + authority: crate::SplTokenAuthority, + /// Exact public amount deposited. + amount: crate::SplTokenAmount, + /// Expected mint decimals. + decimals: u8, + }, + /// Enable incoming confidential credits on a configured token account. + EnableConfidentialCredits { + /// Token account to update. + account: kb_model::Pubkey, + /// Owner or delegate, simple or multisig. + authority: crate::SplTokenAuthority, + }, + /// Disable incoming confidential credits on a configured token account. + DisableConfidentialCredits { + /// Token account to update. + account: kb_model::Pubkey, + /// Owner or delegate, simple or multisig. + authority: crate::SplTokenAuthority, + }, + /// Enable incoming non-confidential credits on a configured token account. + EnableNonConfidentialCredits { + /// Token account to update. + account: kb_model::Pubkey, + /// Owner or delegate, simple or multisig. + authority: crate::SplTokenAuthority, + }, + /// Disable incoming non-confidential credits on a configured token account. + DisableNonConfidentialCredits { + /// Token account to update. + account: kb_model::Pubkey, + /// Owner or delegate, simple or multisig. + authority: crate::SplTokenAuthority, + }, + /// Configure a confidential token account using a public-key validity proof reference. + ConfigureConfidentialTransferAccount { + /// Token account receiving the confidential extension. + account: kb_model::Pubkey, + /// Corresponding Token-2022 mint. + mint: kb_model::Pubkey, + /// Caller-generated decryptable zero balance. + decryptable_zero_balance: crate::SplTokenDecryptableBalance, + /// Maximum accepted pending-balance credit counter. + maximum_pending_balance_credit_counter: u64, + /// Owner or delegate, simple or multisig. + authority: crate::SplTokenAuthority, + /// Exact PubkeyValidity proof reference. + proof: crate::SplTokenConfidentialProofReference, + }, + /// Configure a confidential token account through an ElGamal Registry account. + ConfigureConfidentialTransferAccountWithRegistry { + /// Token account receiving the confidential extension. + account: kb_model::Pubkey, + /// Corresponding Token-2022 mint. + mint: kb_model::Pubkey, + /// Valid ElGamal Registry PDA for the token-account owner. + registry_account: kb_model::Pubkey, + /// Optional payer used only when the account must be reallocated. + payer: std::option::Option, + }, + /// Empty the confidential available-balance ciphertext before account close. + EmptyConfidentialTransferAccount { + /// Token account whose available confidential balance must already be zero. + account: kb_model::Pubkey, + /// Owner or delegate, simple or multisig. + authority: crate::SplTokenAuthority, + /// Exact ZeroCiphertext proof reference. + proof: crate::SplTokenConfidentialProofReference, + }, + /// Withdraw confidential available balance into the public token balance. + WithdrawConfidentialTokens { + /// Token account whose confidential available balance is debited. + account: kb_model::Pubkey, + /// Token mint. + mint: kb_model::Pubkey, + /// Exact raw amount withdrawn. + amount: crate::SplTokenAmount, + /// Expected mint decimals. + decimals: u8, + /// Caller-generated new decryptable available balance. + new_decryptable_available_balance: crate::SplTokenDecryptableBalance, + /// Owner or delegate, simple or multisig. + authority: crate::SplTokenAuthority, + /// Ciphertext-commitment equality proof reference. + equality_proof: crate::SplTokenConfidentialProofReference, + /// Batched U64 range proof reference. + range_proof: crate::SplTokenConfidentialProofReference, + }, + /// Transfer confidential tokens between two Token-2022 accounts. + TransferConfidentialTokens { + /// Source token account whose confidential available balance is debited. + source: kb_model::Pubkey, + /// Token mint. + mint: kb_model::Pubkey, + /// Destination token account whose pending confidential balance is credited. + destination: kb_model::Pubkey, + /// Caller-generated new source decryptable available balance. + new_source_decryptable_available_balance: crate::SplTokenDecryptableBalance, + /// Caller-generated lower auditor ciphertext component. + transfer_amount_auditor_ciphertext_lo: crate::SplTokenElGamalCiphertext, + /// Caller-generated upper auditor ciphertext component. + transfer_amount_auditor_ciphertext_hi: crate::SplTokenElGamalCiphertext, + /// Source owner or delegate, simple or multisig. + authority: crate::SplTokenAuthority, + /// Ciphertext-commitment equality proof reference. + equality_proof: crate::SplTokenConfidentialProofReference, + /// Batched grouped-ciphertext validity proof reference. + ciphertext_validity_proof: crate::SplTokenConfidentialProofReference, + /// Batched U128 range proof reference. + range_proof: crate::SplTokenConfidentialProofReference, + }, + /// Transfer confidential tokens while applying the mint confidential transfer fee. + TransferConfidentialTokensWithFee { + /// Source token account whose confidential available balance is debited. + source: kb_model::Pubkey, + /// Token mint. + mint: kb_model::Pubkey, + /// Destination token account whose pending confidential balance is credited. + destination: kb_model::Pubkey, + /// Caller-generated new source decryptable available balance. + new_source_decryptable_available_balance: crate::SplTokenDecryptableBalance, + /// Caller-generated lower auditor ciphertext component. + transfer_amount_auditor_ciphertext_lo: crate::SplTokenElGamalCiphertext, + /// Caller-generated upper auditor ciphertext component. + transfer_amount_auditor_ciphertext_hi: crate::SplTokenElGamalCiphertext, + /// Source owner or delegate, simple or multisig. + authority: crate::SplTokenAuthority, + /// Ciphertext-commitment equality proof reference. + equality_proof: crate::SplTokenConfidentialProofReference, + /// Transfer-amount grouped-ciphertext validity proof reference. + transfer_amount_ciphertext_validity_proof: crate::SplTokenConfidentialProofReference, + /// Percentage-with-fee proof reference. + fee_sigma_proof: crate::SplTokenConfidentialProofReference, + /// Fee-ciphertext grouped-validity proof reference. + fee_ciphertext_validity_proof: crate::SplTokenConfidentialProofReference, + /// Batched U256 range proof reference. + range_proof: crate::SplTokenConfidentialProofReference, + }, + /// Initialize confidential transfer fee configuration before mint initialization. + InitializeConfidentialTransferFeeConfig { + /// Mint receiving the extension. + mint: kb_model::Pubkey, + /// Optional authority permitted to enable or disable harvest to mint. + authority: std::option::Option, + /// Caller-provided ElGamal public key used to encrypt withheld fees. + withdraw_withheld_authority_elgamal_pubkey: crate::SplTokenElGamalPubkey, + }, + /// Enable permissionless harvest of confidential withheld fees to the mint. + EnableConfidentialTransferFeeHarvest { + /// Mint containing confidential transfer fee configuration. + mint: kb_model::Pubkey, + /// Current confidential transfer fee authority, simple or multisig. + authority: crate::SplTokenAuthority, + }, + /// Disable permissionless harvest of confidential withheld fees to the mint. + DisableConfidentialTransferFeeHarvest { + /// Mint containing confidential transfer fee configuration. + mint: kb_model::Pubkey, + /// Current confidential transfer fee authority, simple or multisig. + authority: crate::SplTokenAuthority, + }, + /// Harvest confidential withheld fees from ordered token accounts to the mint. + HarvestConfidentialWithheldTokensToMint { + /// Mint receiving aggregated confidential withheld fees. + mint: kb_model::Pubkey, + /// Ordered distinct source token accounts. + sources: std::vec::Vec, + }, + /// Withdraw all confidential withheld fees aggregated on the mint. + WithdrawConfidentialWithheldTokensFromMint { + /// Mint containing the aggregated confidential withheld ciphertext. + mint: kb_model::Pubkey, + /// Confidential-transfer account receiving the withdrawn fees. + destination: kb_model::Pubkey, + /// Caller-generated new decryptable available balance for the destination. + new_decryptable_available_balance: crate::SplTokenDecryptableBalance, + /// Mint withdraw-withheld authority, simple or multisig. + authority: crate::SplTokenAuthority, + /// Ciphertext-ciphertext equality proof reference. + proof: crate::SplTokenConfidentialProofReference, + }, + /// Withdraw confidential withheld fees directly from ordered token accounts. + WithdrawConfidentialWithheldTokensFromAccounts { + /// Mint governing the confidential transfer fee configuration. + mint: kb_model::Pubkey, + /// Confidential-transfer account receiving the withdrawn fees. + destination: kb_model::Pubkey, + /// Caller-generated new decryptable available balance for the destination. + new_decryptable_available_balance: crate::SplTokenDecryptableBalance, + /// Mint withdraw-withheld authority, simple or multisig. + authority: crate::SplTokenAuthority, + /// Ordered distinct source token accounts. + sources: std::vec::Vec, + /// Ciphertext-ciphertext equality proof reference. + proof: crate::SplTokenConfidentialProofReference, + }, + /// Initialize confidential mint and burn configuration before mint initialization. + InitializeConfidentialMintBurnMint { + /// Mint receiving the confidential mint-burn extension. + mint: kb_model::Pubkey, + /// Caller-provided ElGamal public key used for confidential supply encryption. + supply_elgamal_pubkey: crate::SplTokenElGamalPubkey, + /// Caller-provided initial decryptable zero supply. + decryptable_supply: crate::SplTokenDecryptableBalance, + }, + /// Rotate the ElGamal public key used for confidential supply encryption. + RotateConfidentialSupplyElGamalPubkey { + /// Mint containing the confidential mint-burn extension. + mint: kb_model::Pubkey, + /// New caller-provided supply ElGamal public key. + new_supply_elgamal_pubkey: crate::SplTokenElGamalPubkey, + /// Confidential mint authority, simple or multisig. + authority: crate::SplTokenAuthority, + /// Ciphertext-ciphertext equality proof reference. + proof: crate::SplTokenConfidentialProofReference, + }, + /// Update the caller-decryptable confidential supply ciphertext. + UpdateConfidentialDecryptableSupply { + /// Mint containing the confidential mint-burn extension. + mint: kb_model::Pubkey, + /// New caller-provided decryptable supply. + new_decryptable_supply: crate::SplTokenDecryptableBalance, + /// Confidential mint authority, simple or multisig. + authority: crate::SplTokenAuthority, + }, + /// Apply the pending confidential burn ciphertext to the supply. + ApplyPendingConfidentialBurn { + /// Mint containing the confidential mint-burn extension. + mint: kb_model::Pubkey, + /// Confidential mint authority, simple or multisig. + authority: crate::SplTokenAuthority, + }, + /// Mint tokens directly into one confidential token account. + ConfidentialMint { + /// Confidential token account receiving the minted amount. + destination: kb_model::Pubkey, + /// Mint containing the confidential mint-burn extension. + mint: kb_model::Pubkey, + /// Caller-generated new decryptable confidential supply. + new_decryptable_supply: crate::SplTokenDecryptableBalance, + /// Mint amount ciphertext under the auditor key, low limb. + mint_amount_auditor_ciphertext_lo: crate::SplTokenElGamalCiphertext, + /// Mint amount ciphertext under the auditor key, high limb. + mint_amount_auditor_ciphertext_hi: crate::SplTokenElGamalCiphertext, + /// Confidential mint authority, simple or multisig. + authority: crate::SplTokenAuthority, + /// Ciphertext-commitment equality proof reference. + equality_proof: crate::SplTokenConfidentialProofReference, + /// Three-handle grouped-ciphertext validity proof reference. + ciphertext_validity_proof: crate::SplTokenConfidentialProofReference, + /// Batched U128 range proof reference. + range_proof: crate::SplTokenConfidentialProofReference, + }, + /// Burn tokens directly from one confidential token account. + ConfidentialBurn { + /// Confidential token account supplying the burned amount. + source: kb_model::Pubkey, + /// Mint containing the confidential mint-burn extension. + mint: kb_model::Pubkey, + /// Caller-generated new decryptable available balance for the source. + new_decryptable_available_balance: crate::SplTokenDecryptableBalance, + /// Burn amount ciphertext under the auditor key, low limb. + burn_amount_auditor_ciphertext_lo: crate::SplTokenElGamalCiphertext, + /// Burn amount ciphertext under the auditor key, high limb. + burn_amount_auditor_ciphertext_hi: crate::SplTokenElGamalCiphertext, + /// Confidential account owner/delegate, simple or multisig. + authority: crate::SplTokenAuthority, + /// Ciphertext-commitment equality proof reference. + equality_proof: crate::SplTokenConfidentialProofReference, + /// Three-handle grouped-ciphertext validity proof reference. + ciphertext_validity_proof: crate::SplTokenConfidentialProofReference, + /// Batched U128 range proof reference. + range_proof: crate::SplTokenConfidentialProofReference, + }, + /// Burn tokens from one confidential account with the mint permissioned-burn authority. + PermissionedConfidentialBurn { + /// Confidential token account supplying the burned amount. + source: kb_model::Pubkey, + /// Mint containing Permissioned Burn and Confidential Mint/Burn extensions. + mint: kb_model::Pubkey, + /// Permissioned-burn authority configured on the mint; always a direct signer. + permissioned_burn_authority: kb_model::Pubkey, + /// Caller-generated new decryptable available balance for the source. + new_decryptable_available_balance: crate::SplTokenDecryptableBalance, + /// Burn amount ciphertext under the auditor key, low limb. + burn_amount_auditor_ciphertext_lo: crate::SplTokenElGamalCiphertext, + /// Burn amount ciphertext under the auditor key, high limb. + burn_amount_auditor_ciphertext_hi: crate::SplTokenElGamalCiphertext, + /// Confidential account owner/delegate, simple or multisig. + authority: crate::SplTokenAuthority, + /// Ciphertext-commitment equality proof reference. + equality_proof: crate::SplTokenConfidentialProofReference, + /// Three-handle grouped-ciphertext validity proof reference. + ciphertext_validity_proof: crate::SplTokenConfidentialProofReference, + /// Batched U128 range proof reference. + range_proof: crate::SplTokenConfidentialProofReference, + }, + /// Unwrap some or all lamports from a wrapped-SOL account. + UnwrapLamports { + /// Wrapped-SOL token account. + account: kb_model::Pubkey, + /// Lamport destination. + destination: kb_model::Pubkey, + /// Owner or multisig authority. + authority: crate::SplTokenAuthority, + /// Exact lamports to unwrap, or `None` for the complete balance. + amount_lamports: std::option::Option, + }, +} + +impl crate::SplTokenSingleOperation { + /// Returns the stable operation code. + pub fn operation_code(&self) -> &'static str { + return match self { + Self::InitializeMint { .. } => crate::INITIALIZE_MINT_OPERATION, + Self::InitializeAccount { .. } => crate::INITIALIZE_ACCOUNT_OPERATION, + Self::InitializeMultisig { .. } => crate::INITIALIZE_MULTISIG_OPERATION, + Self::Transfer { .. } => crate::TRANSFER_OPERATION, + Self::Approve { .. } => crate::APPROVE_OPERATION, + Self::Revoke { .. } => crate::REVOKE_OPERATION, + Self::SetAuthority { .. } => crate::SET_AUTHORITY_OPERATION, + Self::MintTo { .. } => crate::MINT_TO_OPERATION, + Self::Burn { .. } => crate::BURN_OPERATION, + Self::CloseAccount { .. } => crate::CLOSE_ACCOUNT_OPERATION, + Self::FreezeAccount { .. } => crate::FREEZE_ACCOUNT_OPERATION, + Self::ThawAccount { .. } => crate::THAW_ACCOUNT_OPERATION, + Self::TransferChecked { .. } => crate::TRANSFER_CHECKED_OPERATION, + Self::ApproveChecked { .. } => crate::APPROVE_CHECKED_OPERATION, + Self::MintToChecked { .. } => crate::MINT_TO_CHECKED_OPERATION, + Self::BurnChecked { .. } => crate::BURN_CHECKED_OPERATION, + Self::SyncNative { .. } => crate::SYNC_NATIVE_OPERATION, + Self::GetAccountDataSize { .. } => crate::GET_ACCOUNT_DATA_SIZE_OPERATION, + Self::InitializeImmutableOwner { .. } => crate::INITIALIZE_IMMUTABLE_OWNER_OPERATION, + Self::AmountToUiAmount { .. } => crate::AMOUNT_TO_UI_AMOUNT_OPERATION, + Self::UiAmountToAmount { .. } => crate::UI_AMOUNT_TO_AMOUNT_OPERATION, + Self::WithdrawExcessLamports { .. } => crate::WITHDRAW_EXCESS_LAMPORTS_OPERATION, + Self::InitializeTransferFeeConfig { .. } => { + crate::INITIALIZE_TRANSFER_FEE_CONFIG_OPERATION + }, + Self::SetTransferFee { .. } => crate::SET_TRANSFER_FEE_OPERATION, + Self::TransferCheckedWithFee { .. } => crate::TRANSFER_CHECKED_WITH_FEE_OPERATION, + Self::WithdrawWithheldTokensFromMint { .. } => { + crate::WITHDRAW_WITHHELD_TOKENS_FROM_MINT_OPERATION + }, + Self::WithdrawWithheldTokensFromAccounts { .. } => { + crate::WITHDRAW_WITHHELD_TOKENS_FROM_ACCOUNTS_OPERATION + }, + Self::HarvestWithheldTokensToMint { .. } => { + crate::HARVEST_WITHHELD_TOKENS_TO_MINT_OPERATION + }, + Self::InitializeDefaultAccountState { .. } => { + crate::INITIALIZE_DEFAULT_ACCOUNT_STATE_OPERATION + }, + Self::UpdateDefaultAccountState { .. } => crate::UPDATE_DEFAULT_ACCOUNT_STATE_OPERATION, + Self::EnableRequiredTransferMemos { .. } => { + crate::ENABLE_REQUIRED_TRANSFER_MEMOS_OPERATION + }, + Self::DisableRequiredTransferMemos { .. } => { + crate::DISABLE_REQUIRED_TRANSFER_MEMOS_OPERATION + }, + Self::EnableCpiGuard { .. } => crate::ENABLE_CPI_GUARD_OPERATION, + Self::DisableCpiGuard { .. } => crate::DISABLE_CPI_GUARD_OPERATION, + Self::InitializeTransferHook { .. } => crate::INITIALIZE_TRANSFER_HOOK_OPERATION, + Self::UpdateTransferHook { .. } => crate::UPDATE_TRANSFER_HOOK_OPERATION, + Self::InitializeMetadataPointer { .. } => crate::INITIALIZE_METADATA_POINTER_OPERATION, + Self::UpdateMetadataPointer { .. } => crate::UPDATE_METADATA_POINTER_OPERATION, + Self::InitializeGroupPointer { .. } => crate::INITIALIZE_GROUP_POINTER_OPERATION, + Self::UpdateGroupPointer { .. } => crate::UPDATE_GROUP_POINTER_OPERATION, + Self::InitializeGroupMemberPointer { .. } => { + crate::INITIALIZE_GROUP_MEMBER_POINTER_OPERATION + }, + Self::UpdateGroupMemberPointer { .. } => crate::UPDATE_GROUP_MEMBER_POINTER_OPERATION, + Self::InitializeTokenMetadata { .. } => crate::INITIALIZE_TOKEN_METADATA_OPERATION, + Self::UpdateTokenMetadataField { .. } => crate::UPDATE_TOKEN_METADATA_FIELD_OPERATION, + Self::RemoveTokenMetadataKey { .. } => crate::REMOVE_TOKEN_METADATA_KEY_OPERATION, + Self::InitializeTokenGroup { .. } => crate::INITIALIZE_TOKEN_GROUP_OPERATION, + Self::UpdateTokenGroupMaxSize { .. } => crate::UPDATE_TOKEN_GROUP_MAX_SIZE_OPERATION, + Self::InitializeTokenGroupMember { .. } => { + crate::INITIALIZE_TOKEN_GROUP_MEMBER_OPERATION + }, + Self::InitializeScaledUiAmount { .. } => crate::INITIALIZE_SCALED_UI_AMOUNT_OPERATION, + Self::UpdateScaledUiAmountMultiplier { .. } => { + crate::UPDATE_SCALED_UI_AMOUNT_MULTIPLIER_OPERATION + }, + Self::InitializePausableConfig { .. } => crate::INITIALIZE_PAUSABLE_CONFIG_OPERATION, + Self::Pause { .. } => crate::PAUSE_OPERATION, + Self::Resume { .. } => crate::RESUME_OPERATION, + Self::InitializePermissionedBurn { .. } => { + crate::INITIALIZE_PERMISSIONED_BURN_OPERATION + }, + Self::PermissionedBurn { .. } => crate::PERMISSIONED_BURN_OPERATION, + Self::PermissionedBurnChecked { .. } => crate::PERMISSIONED_BURN_CHECKED_OPERATION, + Self::InitializeConfidentialTransferMint { .. } => { + crate::INITIALIZE_CONFIDENTIAL_TRANSFER_MINT_OPERATION + }, + Self::UpdateConfidentialTransferMint { .. } => { + crate::UPDATE_CONFIDENTIAL_TRANSFER_MINT_OPERATION + }, + Self::ApplyPendingConfidentialBalance { .. } => { + crate::APPLY_PENDING_CONFIDENTIAL_BALANCE_OPERATION + }, + Self::ApproveConfidentialTransferAccount { .. } => { + crate::APPROVE_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION + }, + Self::DepositConfidentialTokens { .. } => crate::DEPOSIT_CONFIDENTIAL_TOKENS_OPERATION, + Self::EnableConfidentialCredits { .. } => crate::ENABLE_CONFIDENTIAL_CREDITS_OPERATION, + Self::DisableConfidentialCredits { .. } => { + crate::DISABLE_CONFIDENTIAL_CREDITS_OPERATION + }, + Self::EnableNonConfidentialCredits { .. } => { + crate::ENABLE_NON_CONFIDENTIAL_CREDITS_OPERATION + }, + Self::DisableNonConfidentialCredits { .. } => { + crate::DISABLE_NON_CONFIDENTIAL_CREDITS_OPERATION + }, + Self::ConfigureConfidentialTransferAccount { .. } => { + crate::CONFIGURE_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION + }, + Self::ConfigureConfidentialTransferAccountWithRegistry { .. } => { + crate::CONFIGURE_CONFIDENTIAL_TRANSFER_ACCOUNT_WITH_REGISTRY_OPERATION + }, + Self::EmptyConfidentialTransferAccount { .. } => { + crate::EMPTY_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION + }, + Self::WithdrawConfidentialTokens { .. } => { + crate::WITHDRAW_CONFIDENTIAL_TOKENS_OPERATION + }, + Self::TransferConfidentialTokens { .. } => { + crate::TRANSFER_CONFIDENTIAL_TOKENS_OPERATION + }, + Self::TransferConfidentialTokensWithFee { .. } => { + crate::TRANSFER_CONFIDENTIAL_TOKENS_WITH_FEE_OPERATION + }, + Self::InitializeConfidentialTransferFeeConfig { .. } => { + crate::INITIALIZE_CONFIDENTIAL_TRANSFER_FEE_CONFIG_OPERATION + }, + Self::EnableConfidentialTransferFeeHarvest { .. } => { + crate::ENABLE_CONFIDENTIAL_TRANSFER_FEE_HARVEST_OPERATION + }, + Self::DisableConfidentialTransferFeeHarvest { .. } => { + crate::DISABLE_CONFIDENTIAL_TRANSFER_FEE_HARVEST_OPERATION + }, + Self::HarvestConfidentialWithheldTokensToMint { .. } => { + crate::HARVEST_CONFIDENTIAL_WITHHELD_TOKENS_TO_MINT_OPERATION + }, + Self::WithdrawConfidentialWithheldTokensFromMint { .. } => { + crate::WITHDRAW_CONFIDENTIAL_WITHHELD_TOKENS_FROM_MINT_OPERATION + }, + Self::WithdrawConfidentialWithheldTokensFromAccounts { .. } => { + crate::WITHDRAW_CONFIDENTIAL_WITHHELD_TOKENS_FROM_ACCOUNTS_OPERATION + }, + Self::InitializeConfidentialMintBurnMint { .. } => { + crate::INITIALIZE_CONFIDENTIAL_MINT_BURN_MINT_OPERATION + }, + Self::RotateConfidentialSupplyElGamalPubkey { .. } => { + crate::ROTATE_CONFIDENTIAL_SUPPLY_ELGAMAL_PUBKEY_OPERATION + }, + Self::UpdateConfidentialDecryptableSupply { .. } => { + crate::UPDATE_CONFIDENTIAL_DECRYPTABLE_SUPPLY_OPERATION + }, + Self::ApplyPendingConfidentialBurn { .. } => { + crate::APPLY_PENDING_CONFIDENTIAL_BURN_OPERATION + }, + Self::ConfidentialMint { .. } => crate::CONFIDENTIAL_MINT_OPERATION, + Self::ConfidentialBurn { .. } => crate::CONFIDENTIAL_BURN_OPERATION, + Self::PermissionedConfidentialBurn { .. } => { + crate::PERMISSIONED_CONFIDENTIAL_BURN_OPERATION + }, + Self::UnwrapLamports { .. } => crate::UNWRAP_LAMPORTS_OPERATION, + Self::Reallocate { .. } => crate::REALLOCATE_OPERATION, + Self::InitializeNonTransferableMint { .. } => { + crate::INITIALIZE_NON_TRANSFERABLE_MINT_OPERATION + }, + Self::InitializePermanentDelegate { .. } => { + crate::INITIALIZE_PERMANENT_DELEGATE_OPERATION + }, + Self::InitializeInterestBearingMint { .. } => { + crate::INITIALIZE_INTEREST_BEARING_MINT_OPERATION + }, + Self::UpdateInterestBearingRate { .. } => crate::UPDATE_INTEREST_BEARING_RATE_OPERATION, + }; + } + + pub(crate) fn requires_materialization(&self) -> bool { + return !matches!( + self, + Self::GetAccountDataSize { .. } + | Self::InitializeImmutableOwner { .. } + | Self::AmountToUiAmount { .. } + | Self::UiAmountToAmount { .. } + ); + } +} + +/// Top-level SPL Token operation, including the recent bounded Batch builder. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[serde(tag = "operation", rename_all = "snake_case")] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token_2022/intent/SplToken2022Operation.ts" +)] +pub enum SplToken2022Operation { + /// Build one non-batch instruction. + Instruction { + /// Exact instruction arguments. + value: std::boxed::Box, + }, + /// Build one Batch from ordered, non-batch child operations. + Batch { + /// Ordered child operations. Nested Batch is impossible in this contract. + instructions: std::vec::Vec, + }, +} + +impl crate::SplToken2022Operation { + /// Returns the stable top-level operation code. + pub fn operation_code(&self) -> &'static str { + return match self { + Self::Instruction { value } => value.operation_code(), + Self::Batch { .. } => crate::BATCH_OPERATION, + }; + } + + pub(crate) fn requires_materialization(&self) -> bool { + return match self { + Self::Instruction { value } => value.requires_materialization(), + Self::Batch { instructions } => { + instructions.iter().any(|value| return value.requires_materialization()) + }, + }; + } +} + +/// Complete typed intent accepted by the Token-2022 executor. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_executor_spl_token_2022/intent/SplToken2022ExecutionIntent.ts" +)] +pub struct SplToken2022ExecutionIntent { + /// Stable caller-provided identifier used for logs and replay correlation. + pub intent_id: std::string::String, + /// Transaction fee payer. + pub fee_payer: kb_model::Pubkey, + /// Conservative execution policy. Its default is simulation-only. + pub policy: kb_execution_api::ExecutionPolicy, + /// Typed classic Token operation. + pub operation: crate::SplToken2022Operation, +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/lib.rs new file mode 100644 index 0000000..cccdbe9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_spl_token_2022/src/lib.rs @@ -0,0 +1,265 @@ +// file: kb_executor_spl_token_2022/src/lib.rs +// version: 27 + +//! Executor crate for `spl_token_2022`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod builder; +mod confidential; +mod constants; +mod executor; +mod intent; + +/// Crate-root access to `build_prepared_plan` from `builder`. +pub(crate) use crate::builder::build_prepared_plan; +/// Maximum number of ordered multisig signer occurrences. +pub(crate) use crate::constants::MAX_MULTISIG_SIGNERS; +/// Maximum ordered token accounts accepted by transfer-fee harvest or withdraw builders. +pub(crate) use crate::constants::MAX_TRANSFER_FEE_SOURCE_ACCOUNTS; +/// Maximum UI amount string length accepted before transaction assembly. +pub(crate) use crate::constants::MAX_UI_AMOUNT_BYTES; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the audited Confidential Transfer operation inventory. +pub use crate::confidential::CONFIDENTIAL_TRANSFER_EXTERNAL_TAG; +/// Exposes the audited Confidential Transfer operation inventory. +pub use crate::confidential::CONFIDENTIAL_TRANSFER_WITH_FEE_INSTRUCTION_DATA_BYTES; +/// Exposes the audited Confidential Transfer operation inventory. +pub use crate::confidential::CONFIDENTIAL_TRANSFER_WITH_FEE_PAYLOAD_BYTES; +/// Exposes the audited Confidential Transfer operation inventory. +pub use crate::confidential::CONFIDENTIAL_TRANSFER_WITH_FEE_SUBTAG; +/// Exposes the audited Confidential Transfer operation inventory. +pub use crate::confidential::DECRYPTABLE_BALANCE_BYTES; +/// Exposes the audited Confidential Transfer operation inventory. +pub use crate::confidential::ELGAMAL_CIPHERTEXT_BYTES; +/// Exposes the audited Confidential Transfer operation inventory. +pub use crate::confidential::ELGAMAL_PUBKEY_BYTES; +/// Exposes the audited Confidential Transfer operation inventory. +pub use crate::confidential::MAX_CONFIDENTIAL_PROOF_REFERENCES; +/// Exposes the audited Confidential Transfer operation inventory. +pub use crate::confidential::SplTokenConfidentialExecutorSupport; +/// Exposes the audited Confidential Transfer operation inventory. +pub use crate::confidential::SplTokenConfidentialMintBurnOperation; +/// Exposes the audited Confidential Transfer operation inventory. +pub use crate::confidential::SplTokenConfidentialOperation; +/// Exposes the audited Confidential Transfer operation inventory. +pub use crate::confidential::SplTokenConfidentialProofKind; +/// Exposes the audited Confidential Transfer operation inventory. +pub use crate::confidential::SplTokenConfidentialProofLocation; +/// Exposes the audited Confidential Transfer operation inventory. +pub use crate::confidential::SplTokenConfidentialProofReference; +/// Exposes the audited Confidential Transfer operation inventory. +pub use crate::confidential::SplTokenDecryptableBalance; +/// Exposes the audited Confidential Transfer operation inventory. +pub use crate::confidential::SplTokenElGamalCiphertext; +/// Exposes the audited Confidential Transfer operation inventory. +pub use crate::confidential::SplTokenElGamalPubkey; +/// Exposes the audited Confidential Transfer operation inventory. +pub use crate::confidential::validate_confidential_proof_references; +/// Exposes the typed SPL Token executor. +pub use crate::executor::SplToken2022Executor; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::AMOUNT_TO_UI_AMOUNT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::APPLY_PENDING_CONFIDENTIAL_BALANCE_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::APPLY_PENDING_CONFIDENTIAL_BURN_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::APPROVE_CHECKED_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::APPROVE_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::APPROVE_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::BATCH_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::BURN_CHECKED_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::BURN_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::CLOSE_ACCOUNT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::CONFIDENTIAL_BURN_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::CONFIDENTIAL_MINT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::CONFIGURE_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::CONFIGURE_CONFIDENTIAL_TRANSFER_ACCOUNT_WITH_REGISTRY_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::DEPOSIT_CONFIDENTIAL_TOKENS_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::DISABLE_CONFIDENTIAL_CREDITS_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::DISABLE_CONFIDENTIAL_TRANSFER_FEE_HARVEST_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::DISABLE_CPI_GUARD_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::DISABLE_NON_CONFIDENTIAL_CREDITS_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::DISABLE_REQUIRED_TRANSFER_MEMOS_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::EMPTY_CONFIDENTIAL_TRANSFER_ACCOUNT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::ENABLE_CONFIDENTIAL_CREDITS_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::ENABLE_CONFIDENTIAL_TRANSFER_FEE_HARVEST_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::ENABLE_CPI_GUARD_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::ENABLE_NON_CONFIDENTIAL_CREDITS_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::ENABLE_REQUIRED_TRANSFER_MEMOS_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::FREEZE_ACCOUNT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::GET_ACCOUNT_DATA_SIZE_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::HARVEST_CONFIDENTIAL_WITHHELD_TOKENS_TO_MINT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::HARVEST_WITHHELD_TOKENS_TO_MINT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_ACCOUNT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_CONFIDENTIAL_MINT_BURN_MINT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_CONFIDENTIAL_TRANSFER_FEE_CONFIG_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_CONFIDENTIAL_TRANSFER_MINT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_DEFAULT_ACCOUNT_STATE_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_GROUP_MEMBER_POINTER_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_GROUP_POINTER_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_IMMUTABLE_OWNER_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_INTEREST_BEARING_MINT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_METADATA_POINTER_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_MINT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_MULTISIG_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_NON_TRANSFERABLE_MINT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_PAUSABLE_CONFIG_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_PERMANENT_DELEGATE_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_PERMISSIONED_BURN_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_SCALED_UI_AMOUNT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_TOKEN_GROUP_MEMBER_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_TOKEN_GROUP_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_TOKEN_METADATA_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_TRANSFER_FEE_CONFIG_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::INITIALIZE_TRANSFER_HOOK_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::MINT_TO_CHECKED_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::MINT_TO_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::PAUSE_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::PERMISSIONED_BURN_CHECKED_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::PERMISSIONED_BURN_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::PERMISSIONED_CONFIDENTIAL_BURN_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::REALLOCATE_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::REMOVE_TOKEN_METADATA_KEY_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::RESUME_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::REVOKE_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::ROTATE_CONFIDENTIAL_SUPPLY_ELGAMAL_PUBKEY_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::SET_AUTHORITY_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::SET_TRANSFER_FEE_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::SUPPORTED_OPERATION_CODES; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::SYNC_NATIVE_OPERATION; +/// Exposes the complete typed execution intent. +pub use crate::intent::SplToken2022ExecutionIntent; +/// Exposes the top-level operation contract. +pub use crate::intent::SplToken2022Operation; +/// Exposes the exact raw amount string contract. +pub use crate::intent::SplTokenAmount; +/// Exposes the typed authority contract. +pub use crate::intent::SplTokenAuthority; +/// Exposes the typed authority-kind contract. +pub use crate::intent::SplTokenAuthorityType; +/// Exposes the typed default-account-state contract. +pub use crate::intent::SplTokenDefaultAccountState; +/// Exposes the typed token-metadata field contract. +pub use crate::intent::SplTokenMetadataField; +/// Exposes the bounded scaled-UI multiplier contract. +pub use crate::intent::SplTokenMultiplier; +/// Exposes the non-batch operation contract. +pub use crate::intent::SplTokenSingleOperation; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::THAW_ACCOUNT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::TRANSFER_CHECKED_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::TRANSFER_CHECKED_WITH_FEE_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::TRANSFER_CONFIDENTIAL_TOKENS_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::TRANSFER_CONFIDENTIAL_TOKENS_WITH_FEE_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::TRANSFER_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::UI_AMOUNT_TO_AMOUNT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::UNWRAP_LAMPORTS_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::UPDATE_CONFIDENTIAL_DECRYPTABLE_SUPPLY_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::UPDATE_CONFIDENTIAL_TRANSFER_MINT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::UPDATE_DEFAULT_ACCOUNT_STATE_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::UPDATE_GROUP_MEMBER_POINTER_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::UPDATE_GROUP_POINTER_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::UPDATE_INTEREST_BEARING_RATE_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::UPDATE_METADATA_POINTER_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::UPDATE_SCALED_UI_AMOUNT_MULTIPLIER_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::UPDATE_TOKEN_GROUP_MAX_SIZE_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::UPDATE_TOKEN_METADATA_FIELD_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::UPDATE_TRANSFER_HOOK_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::WITHDRAW_CONFIDENTIAL_TOKENS_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::WITHDRAW_CONFIDENTIAL_WITHHELD_TOKENS_FROM_ACCOUNTS_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::WITHDRAW_CONFIDENTIAL_WITHHELD_TOKENS_FROM_MINT_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::WITHDRAW_EXCESS_LAMPORTS_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::WITHDRAW_WITHHELD_TOKENS_FROM_ACCOUNTS_OPERATION; +/// Exposes stable operation codes for generic executor dispatch. +pub use crate::intent::WITHDRAW_WITHHELD_TOKENS_FROM_MINT_OPERATION; diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/Cargo.toml new file mode 100644 index 0000000..ca483db --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_stable_swap_hylo_exchange/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_stable_swap_hylo_exchange" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/README.md b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/README.md new file mode 100644 index 0000000..2ff5196 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_stable_swap_hylo_exchange + +Ce crate réserve l'exécuteur de la surface `stable_swap_hylo_exchange`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/src/constants.rs new file mode 100644 index 0000000..089b2ae --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_stable_swap_hylo_exchange/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_stable_swap_hylo_exchange"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/src/executor.rs new file mode 100644 index 0000000..8fe3460 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_stable_swap_hylo_exchange/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_stable_swap_hylo_exchange` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct StableSwapHyloExchangeExecutor; + +impl kb_execution_api::InstructionExecutor for crate::StableSwapHyloExchangeExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_stable_swap_hylo_exchange"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::STABLE_SWAP_HYLO_EXCHANGE_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_stable_swap_hylo_exchange"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/src/lib.rs new file mode 100644 index 0000000..d13a044 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_hylo_exchange/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_stable_swap_hylo_exchange/src/lib.rs +// version: 2 + +//! Executor crate for `stable_swap_hylo_exchange`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::StableSwapHyloExchangeExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/Cargo.toml new file mode 100644 index 0000000..c93d14d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_stable_swap_jupiter_stable/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_stable_swap_jupiter_stable" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/README.md b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/README.md new file mode 100644 index 0000000..dde7dfe --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_stable_swap_jupiter_stable + +Ce crate réserve l'exécuteur de la surface `stable_swap_jupiter_stable`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/src/constants.rs new file mode 100644 index 0000000..68112c6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_stable_swap_jupiter_stable/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_stable_swap_jupiter_stable"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/src/executor.rs new file mode 100644 index 0000000..d96fe12 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_stable_swap_jupiter_stable/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_stable_swap_jupiter_stable` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct StableSwapJupiterStableExecutor; + +impl kb_execution_api::InstructionExecutor for crate::StableSwapJupiterStableExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_stable_swap_jupiter_stable"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::STABLE_SWAP_JUPITER_STABLE_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_stable_swap_jupiter_stable"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/src/lib.rs new file mode 100644 index 0000000..c1c6508 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_jupiter_stable/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_stable_swap_jupiter_stable/src/lib.rs +// version: 2 + +//! Executor crate for `stable_swap_jupiter_stable`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::StableSwapJupiterStableExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/Cargo.toml new file mode 100644 index 0000000..59dc90c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_stable_swap_numeraire/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_stable_swap_numeraire" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/README.md b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/README.md new file mode 100644 index 0000000..d1e9e79 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_stable_swap_numeraire + +Ce crate réserve l'exécuteur de la surface `stable_swap_numeraire`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/src/constants.rs new file mode 100644 index 0000000..fe6cd19 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_stable_swap_numeraire/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_stable_swap_numeraire"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/src/executor.rs new file mode 100644 index 0000000..5d148d9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_stable_swap_numeraire/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_stable_swap_numeraire` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct StableSwapNumeraireExecutor; + +impl kb_execution_api::InstructionExecutor for crate::StableSwapNumeraireExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_stable_swap_numeraire"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::STABLE_SWAP_NUMERAIRE_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_stable_swap_numeraire"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/src/lib.rs new file mode 100644 index 0000000..29402b1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_numeraire/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_stable_swap_numeraire/src/lib.rs +// version: 2 + +//! Executor crate for `stable_swap_numeraire`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::StableSwapNumeraireExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/Cargo.toml new file mode 100644 index 0000000..868d5b5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_stable_swap_stabble/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_stable_swap_stabble" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/README.md b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/README.md new file mode 100644 index 0000000..cb0210f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_stable_swap_stabble + +Ce crate réserve l'exécuteur de la surface `stable_swap_stabble`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/src/constants.rs new file mode 100644 index 0000000..34c31bf --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_stable_swap_stabble/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_stable_swap_stabble"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/src/executor.rs new file mode 100644 index 0000000..2ab38fc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_stable_swap_stabble/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_stable_swap_stabble` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct StableSwapStabbleExecutor; + +impl kb_execution_api::InstructionExecutor for crate::StableSwapStabbleExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_stable_swap_stabble"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::STABLE_SWAP_STABBLE_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_stable_swap_stabble"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/src/lib.rs new file mode 100644 index 0000000..325f47f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_stable_swap_stabble/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_stable_swap_stabble/src/lib.rs +// version: 2 + +//! Executor crate for `stable_swap_stabble`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::StableSwapStabbleExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/Cargo.toml new file mode 100644 index 0000000..be42eb0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_staking_kamino_farm/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_staking_kamino_farm" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/README.md b/migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/README.md new file mode 100644 index 0000000..02362e5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_staking_kamino_farm + +Ce crate réserve l'exécuteur de la surface `staking_kamino_farm`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/src/constants.rs new file mode 100644 index 0000000..3b26d9e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_staking_kamino_farm/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_staking_kamino_farm"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/src/executor.rs new file mode 100644 index 0000000..fabb3db --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_staking_kamino_farm/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_staking_kamino_farm` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct StakingKaminoFarmExecutor; + +impl kb_execution_api::InstructionExecutor for crate::StakingKaminoFarmExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_staking_kamino_farm"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::STAKING_KAMINO_FARM_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_staking_kamino_farm"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/src/lib.rs new file mode 100644 index 0000000..666d3f6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_staking_kamino_farm/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_staking_kamino_farm/src/lib.rs +// version: 2 + +//! Executor crate for `staking_kamino_farm`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::StakingKaminoFarmExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/Cargo.toml new file mode 100644 index 0000000..241d5aa --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_staking_marinade_finance/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_staking_marinade_finance" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/README.md b/migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/README.md new file mode 100644 index 0000000..dc7f36e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_staking_marinade_finance + +Ce crate réserve l'exécuteur de la surface `staking_marinade_finance`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/src/constants.rs new file mode 100644 index 0000000..47799f5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_staking_marinade_finance/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_staking_marinade_finance"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/src/executor.rs new file mode 100644 index 0000000..54847d2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_staking_marinade_finance/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_staking_marinade_finance` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct StakingMarinadeFinanceExecutor; + +impl kb_execution_api::InstructionExecutor for crate::StakingMarinadeFinanceExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_staking_marinade_finance"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::STAKING_MARINADE_FINANCE_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_staking_marinade_finance"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/src/lib.rs new file mode 100644 index 0000000..349329b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_staking_marinade_finance/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_staking_marinade_finance/src/lib.rs +// version: 2 + +//! Executor crate for `staking_marinade_finance`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::StakingMarinadeFinanceExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_staking_solayer/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_staking_solayer/Cargo.toml new file mode 100644 index 0000000..36e753d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_staking_solayer/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_staking_solayer/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_staking_solayer" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_staking_solayer/README.md b/migration/khadhroony-bot2-reference/kb_executor_staking_solayer/README.md new file mode 100644 index 0000000..f634da3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_staking_solayer/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_staking_solayer + +Ce crate réserve l'exécuteur de la surface `staking_solayer`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_staking_solayer/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_staking_solayer/src/constants.rs new file mode 100644 index 0000000..c1386c6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_staking_solayer/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_staking_solayer/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_staking_solayer"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_staking_solayer/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_staking_solayer/src/executor.rs new file mode 100644 index 0000000..0cd022f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_staking_solayer/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_staking_solayer/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_staking_solayer` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct StakingSolayerExecutor; + +impl kb_execution_api::InstructionExecutor for crate::StakingSolayerExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_staking_solayer"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::STAKING_SOLAYER_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_staking_solayer"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_staking_solayer/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_staking_solayer/src/lib.rs new file mode 100644 index 0000000..db132a1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_staking_solayer/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_staking_solayer/src/lib.rs +// version: 2 + +//! Executor crate for `staking_solayer`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::StakingSolayerExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/Cargo.toml new file mode 100644 index 0000000..e334994 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_treasury_helium_treasury_management/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_treasury_helium_treasury_management" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/README.md b/migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/README.md new file mode 100644 index 0000000..d922e66 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_treasury_helium_treasury_management + +Ce crate réserve l'exécuteur de la surface `treasury_helium_treasury_management`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/src/constants.rs new file mode 100644 index 0000000..67bcb35 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_treasury_helium_treasury_management/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_treasury_helium_treasury_management"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/src/executor.rs new file mode 100644 index 0000000..dd75465 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/src/executor.rs @@ -0,0 +1,51 @@ +// file: kb_executor_treasury_helium_treasury_management/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_treasury_helium_treasury_management` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct TreasuryHeliumTreasuryManagementExecutor; + +impl kb_execution_api::InstructionExecutor for crate::TreasuryHeliumTreasuryManagementExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_treasury_helium_treasury_management"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::TREASURY_HELIUM_TREASURY_MANAGEMENT_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from( + "kb_executor_treasury_helium_treasury_management", + ), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/src/lib.rs new file mode 100644 index 0000000..50f1cd8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_treasury_helium_treasury_management/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_treasury_helium_treasury_management/src/lib.rs +// version: 2 + +//! Executor crate for `treasury_helium_treasury_management`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::TreasuryHeliumTreasuryManagementExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/Cargo.toml new file mode 100644 index 0000000..75a6240 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_vault_carrot_defi/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_vault_carrot_defi" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/README.md b/migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/README.md new file mode 100644 index 0000000..8b11e6b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_vault_carrot_defi + +Ce crate réserve l'exécuteur de la surface `vault_carrot_defi`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/src/constants.rs new file mode 100644 index 0000000..dbd9be8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_vault_carrot_defi/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_vault_carrot_defi"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/src/executor.rs new file mode 100644 index 0000000..1a79900 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_vault_carrot_defi/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_vault_carrot_defi` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct VaultCarrotDefiExecutor; + +impl kb_execution_api::InstructionExecutor for crate::VaultCarrotDefiExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_vault_carrot_defi"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::VAULT_CARROT_DEFI_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_vault_carrot_defi"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/src/lib.rs new file mode 100644 index 0000000..8d5dbd4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_carrot_defi/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_vault_carrot_defi/src/lib.rs +// version: 2 + +//! Executor crate for `vault_carrot_defi`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::VaultCarrotDefiExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/Cargo.toml new file mode 100644 index 0000000..b0bce09 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_vault_hylo_stability_pool/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_vault_hylo_stability_pool" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/README.md b/migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/README.md new file mode 100644 index 0000000..ce12e5e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_vault_hylo_stability_pool + +Ce crate réserve l'exécuteur de la surface `vault_hylo_stability_pool`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/src/constants.rs new file mode 100644 index 0000000..4cca54d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_vault_hylo_stability_pool/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_vault_hylo_stability_pool"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/src/executor.rs new file mode 100644 index 0000000..49d70f8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_vault_hylo_stability_pool/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_vault_hylo_stability_pool` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct VaultHyloStabilityPoolExecutor; + +impl kb_execution_api::InstructionExecutor for crate::VaultHyloStabilityPoolExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_vault_hylo_stability_pool"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::VAULT_HYLO_STABILITY_POOL_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_vault_hylo_stability_pool"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/src/lib.rs new file mode 100644 index 0000000..58ba31e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_hylo_stability_pool/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_vault_hylo_stability_pool/src/lib.rs +// version: 2 + +//! Executor crate for `vault_hylo_stability_pool`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::VaultHyloStabilityPoolExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_kamino/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino/Cargo.toml new file mode 100644 index 0000000..92dc4a9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_vault_kamino/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_vault_kamino" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_kamino/README.md b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino/README.md new file mode 100644 index 0000000..b1739b9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_vault_kamino + +Ce crate réserve l'exécuteur de la surface `vault_kamino`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_kamino/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino/src/constants.rs new file mode 100644 index 0000000..30b43db --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_vault_kamino/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_vault_kamino"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_kamino/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino/src/executor.rs new file mode 100644 index 0000000..844ea15 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_vault_kamino/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_vault_kamino` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct VaultKaminoExecutor; + +impl kb_execution_api::InstructionExecutor for crate::VaultKaminoExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_vault_kamino"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::VAULT_KAMINO_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_vault_kamino"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_kamino/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino/src/lib.rs new file mode 100644 index 0000000..b8996a6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_vault_kamino/src/lib.rs +// version: 2 + +//! Executor crate for `vault_kamino`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::VaultKaminoExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/Cargo.toml new file mode 100644 index 0000000..8a1f4bb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_vault_kamino_v2/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_vault_kamino_v2" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/README.md b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/README.md new file mode 100644 index 0000000..bef1f0e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_vault_kamino_v2 + +Ce crate réserve l'exécuteur de la surface `vault_kamino_v2`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/src/constants.rs new file mode 100644 index 0000000..87f0519 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_vault_kamino_v2/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_vault_kamino_v2"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/src/executor.rs new file mode 100644 index 0000000..03d67aa --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_vault_kamino_v2/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_vault_kamino_v2` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct VaultKaminoV2Executor; + +impl kb_execution_api::InstructionExecutor for crate::VaultKaminoV2Executor { + fn executor_name(&self) -> &'static str { + return "kb_executor_vault_kamino_v2"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::VAULT_KAMINO_V2_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_vault_kamino_v2"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/src/lib.rs new file mode 100644 index 0000000..d202b6c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_v2/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_vault_kamino_v2/src/lib.rs +// version: 2 + +//! Executor crate for `vault_kamino_v2`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::VaultKaminoV2Executor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/Cargo.toml new file mode 100644 index 0000000..3704434 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_vault_kamino_yvaults/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_vault_kamino_yvaults" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/README.md b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/README.md new file mode 100644 index 0000000..9b7bdd6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_vault_kamino_yvaults + +Ce crate réserve l'exécuteur de la surface `vault_kamino_yvaults`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/src/constants.rs new file mode 100644 index 0000000..2bbca50 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_vault_kamino_yvaults/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_vault_kamino_yvaults"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/src/executor.rs new file mode 100644 index 0000000..eeb70f5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_vault_kamino_yvaults/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_vault_kamino_yvaults` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct VaultKaminoYvaultsExecutor; + +impl kb_execution_api::InstructionExecutor for crate::VaultKaminoYvaultsExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_vault_kamino_yvaults"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::VAULT_KAMINO_YVAULTS_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_vault_kamino_yvaults"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/src/lib.rs new file mode 100644 index 0000000..dd3d351 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_kamino_yvaults/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_vault_kamino_yvaults/src/lib.rs +// version: 2 + +//! Executor crate for `vault_kamino_yvaults`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::VaultKaminoYvaultsExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_meteora/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_vault_meteora/Cargo.toml new file mode 100644 index 0000000..9e0e1f4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_meteora/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_vault_meteora/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_vault_meteora" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_meteora/README.md b/migration/khadhroony-bot2-reference/kb_executor_vault_meteora/README.md new file mode 100644 index 0000000..0ba203e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_meteora/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_vault_meteora + +Ce crate réserve l'exécuteur de la surface `vault_meteora`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_meteora/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_vault_meteora/src/constants.rs new file mode 100644 index 0000000..daba547 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_meteora/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_vault_meteora/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_vault_meteora"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_meteora/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_vault_meteora/src/executor.rs new file mode 100644 index 0000000..a712067 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_meteora/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_vault_meteora/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_vault_meteora` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct VaultMeteoraExecutor; + +impl kb_execution_api::InstructionExecutor for crate::VaultMeteoraExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_vault_meteora"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::VAULT_METEORA_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_vault_meteora"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_vault_meteora/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_vault_meteora/src/lib.rs new file mode 100644 index 0000000..02fdd0e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vault_meteora/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_vault_meteora/src/lib.rs +// version: 2 + +//! Executor crate for `vault_meteora`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::VaultMeteoraExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/Cargo.toml new file mode 100644 index 0000000..9369251 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_vesting_streamflow/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_vesting_streamflow" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/README.md b/migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/README.md new file mode 100644 index 0000000..0dc40fc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_vesting_streamflow + +Ce crate réserve l'exécuteur de la surface `vesting_streamflow`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/src/constants.rs new file mode 100644 index 0000000..cfbac0b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_vesting_streamflow/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_vesting_streamflow"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/src/executor.rs new file mode 100644 index 0000000..ee0bd65 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_vesting_streamflow/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_vesting_streamflow` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct VestingStreamflowExecutor; + +impl kb_execution_api::InstructionExecutor for crate::VestingStreamflowExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_vesting_streamflow"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::VESTING_STREAMFLOW_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_vesting_streamflow"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/src/lib.rs new file mode 100644 index 0000000..c0e17a8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_vesting_streamflow/src/lib.rs @@ -0,0 +1,17 @@ +// file: kb_executor_vesting_streamflow/src/lib.rs +// version: 2 + +//! Executor crate for `vesting_streamflow`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; + +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::VestingStreamflowExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/Cargo.toml new file mode 100644 index 0000000..c964b07 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_wallet_jupiter_apepro_smart_wallet/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_wallet_jupiter_apepro_smart_wallet" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/README.md b/migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/README.md new file mode 100644 index 0000000..e38bbc3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_wallet_jupiter_apepro_smart_wallet + +Ce crate réserve l'exécuteur de la surface `wallet_jupiter_apepro_smart_wallet`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/src/constants.rs new file mode 100644 index 0000000..7f90327 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_wallet_jupiter_apepro_smart_wallet/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_wallet_jupiter_apepro_smart_wallet"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/src/executor.rs new file mode 100644 index 0000000..d36017a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/src/executor.rs @@ -0,0 +1,51 @@ +// file: kb_executor_wallet_jupiter_apepro_smart_wallet/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_wallet_jupiter_apepro_smart_wallet` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct WalletJupiterApeproSmartWalletExecutor; + +impl kb_execution_api::InstructionExecutor for crate::WalletJupiterApeproSmartWalletExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_wallet_jupiter_apepro_smart_wallet"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::WALLET_JUPITER_APEPRO_SMART_WALLET_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from( + "kb_executor_wallet_jupiter_apepro_smart_wallet", + ), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/src/lib.rs new file mode 100644 index 0000000..812ab15 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_wallet_jupiter_apepro_smart_wallet/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_executor_wallet_jupiter_apepro_smart_wallet/src/lib.rs +// version: 2 + +//! Executor crate for `wallet_jupiter_apepro_smart_wallet`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::WalletJupiterApeproSmartWalletExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/Cargo.toml b/migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/Cargo.toml new file mode 100644 index 0000000..5293f0f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_executor_weighted_swap_stabble/Cargo.toml +# version: 2 + +[package] +name = "kb_executor_weighted_swap_stabble" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/README.md b/migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/README.md new file mode 100644 index 0000000..0639942 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/README.md @@ -0,0 +1,15 @@ + + + +# kb_executor_weighted_swap_stabble + +Ce crate réserve l'exécuteur de la surface `weighted_swap_stabble`. + +## Rôle dans l'écosystème + +Il construira les instructions ou plans d'exécution nécessaires pour appeler directement cette surface, sans dépendre du décodeur correspondant. + +## Statut + +Le crate est un squelette. Il ne doit pas envoyer de transaction tant que les builders, les simulations, les garde-fous et les tests dédiés ne sont pas validés. + diff --git a/migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/src/constants.rs b/migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/src/constants.rs new file mode 100644 index 0000000..57ca3db --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/src/constants.rs @@ -0,0 +1,5 @@ +// file: kb_executor_weighted_swap_stabble/src/constants.rs +// version: 1 + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_executor_weighted_swap_stabble"; diff --git a/migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/src/executor.rs b/migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/src/executor.rs new file mode 100644 index 0000000..5603b38 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/src/executor.rs @@ -0,0 +1,49 @@ +// file: kb_executor_weighted_swap_stabble/src/executor.rs +// version: 5 + +//! Executor scaffold for the `kb_executor_weighted_swap_stabble` crate. + +/// Initial executor implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct WeightedSwapStabbleExecutor; + +impl kb_execution_api::InstructionExecutor for crate::WeightedSwapStabbleExecutor { + fn executor_name(&self) -> &'static str { + return "kb_executor_weighted_swap_stabble"; + } + + fn executor_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn program_ids(&self) -> &'static [&'static str] { + return &[kb_program_ids::WEIGHTED_SWAP_STABBLE_PROGRAM_ID]; + } + + fn supports_request( + &self, + request: &kb_execution_api::ExecutionRequest, + ) -> kb_execution_api::ExecutionSupport { + if kb_execution_api::InstructionExecutor::handles_program_id(self, &request.program_id) { + return kb_execution_api::ExecutionSupport::Maybe; + } + return kb_execution_api::ExecutionSupport::No; + } + + fn build_plan( + &self, + _request: &kb_execution_api::ExecutionRequest, + ) -> kb_core::Result { + let _target = crate::TRACING_TARGET; + return std::result::Result::Ok(kb_execution_api::ExecutionPlan { + executor_name: std::string::String::from("kb_executor_weighted_swap_stabble"), + instruction_count: 0, + payload_json: match kb_execution_api::serialize_payload_json( + &serde_json::json!({"status":"reserved_executor"}), + ) { + std::result::Result::Ok(serialized) => serialized, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + }); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/src/lib.rs b/migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/src/lib.rs new file mode 100644 index 0000000..dab3c6b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_executor_weighted_swap_stabble/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_executor_weighted_swap_stabble/src/lib.rs +// version: 2 + +//! Executor crate for `weighted_swap_stabble`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod executor; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the executor type implemented by this crate. +pub use crate::executor::WeightedSwapStabbleExecutor; diff --git a/migration/khadhroony-bot2-reference/kb_logging/Cargo.toml b/migration/khadhroony-bot2-reference/kb_logging/Cargo.toml new file mode 100644 index 0000000..1c6d4ed --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_logging/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_logging/Cargo.toml +# version: 2 + +[package] +name = "kb_logging" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +tracing.workspace = true +tracing-appender.workspace = true +tracing-subscriber.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_logging/README.md b/migration/khadhroony-bot2-reference/kb_logging/README.md new file mode 100644 index 0000000..a079a90 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_logging/README.md @@ -0,0 +1,38 @@ + + + +# kb_logging + +`kb_logging` initialise le routage `tracing` commun du workspace. + +## Responsabilité + +Le crate transforme la configuration du profil actif en layers `tracing_subscriber` et route les événements vers la console, les fichiers humains et les fichiers JSONL. Le `LoggingGuard` retourné par `init_logging` doit rester vivant pendant toute la durée du processus afin de conserver les writers non bloquants. + +Les décisions de niveau et de target sont appliquées par chaque writer de route, avec un seul filtre d’admission agrégé en amont. Elles ne créent pas un `Filtered` layer par sortie : `tracing-subscriber` réserve seulement 64 identifiants de filtres par subscriber, tandis que la matrice complète comporte désormais plus de 64 routes activables. Le filtrage writer conserve les niveaux, targets exacts, préfixes et overrides configurés sans imposer cette limite au nombre de sorties ; le filtre agrégé évite de formater un événement qu’aucune route n’accepte. + +Les chemins relatifs sont résolus depuis la racine du workspace. L’ANSI est désactivé dans les formatters fichier et retiré une seconde fois par `StripAnsiMakeWriter` afin de nettoyer les messages provenant d’une WebView ou d’une dépendance externe. + +## Contrat de targets + +`kb_logging` utilise le target canonique `kb_logging`, défini dans `src/constants.rs`. Chaque crate qui dépend de `tracing` doit suivre le même contrat avec son propre nom Cargo. + +Le test `every_tracing_crate_has_one_canonical_target_constant` inspecte les manifests du workspace et vérifie qu’une crate déclarant `tracing.workspace = true` possède exactement une constante conforme. + +## Matrice de fichiers + +Chaque profil actif configure : + +```text +logs//debug.log +logs//info.log +logs//error.jsonl +logs//app.log +logs///debug.log +logs///info.log +logs///error.jsonl +``` + +Les fichiers sont en rotation quotidienne. Les routes `debug` et `info` sont cumulatives ; les routes `error.jsonl` sont forcées au niveau `error` et n’ajoutent pas les overrides verbeux des dépendances. + +Le contrat détaillé se trouve dans `docs/LOGGING.md` et `docs/TRACING_CONTRACT.md`. diff --git a/migration/khadhroony-bot2-reference/kb_logging/src/config.rs b/migration/khadhroony-bot2-reference/kb_logging/src/config.rs new file mode 100644 index 0000000..b52aaeb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_logging/src/config.rs @@ -0,0 +1,58 @@ +// file: kb_logging/src/config.rs +// version: 3 + +//! Logging configuration data structures consumed by the tracing runtime. + +/// Legacy file route shape kept for early callers that only need human file logs. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct LogFileRoute { + /// Output file path. + pub file: std::string::String, + /// Logging level filter. + pub level: std::string::String, + /// Target filters handled by this route. + pub targets: std::vec::Vec, +} + +/// One target-level filter shared by routes that include the wildcard target. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct LogTargetFilterConfig { + /// Tracing target or crate prefix. + pub target: std::string::String, + /// Minimum level assigned to this target. + pub level: std::string::String, +} + +/// One logging output route. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct LogTargetConfig { + /// Route name used for diagnostics. + pub name: std::string::String, + /// Enables this route. + pub enabled: bool, + /// Sink kind: `console` or `file`. + pub sink: std::string::String, + /// Minimum level for this route. + pub level: std::string::String, + /// File path for file sinks, empty for console sinks. + pub path: std::string::String, + /// Rotation mode: `none`, `never`, `daily`, or `hourly`. + pub rotation: std::string::String, + /// Format kind: `human`, `compact`, `pretty`, or `json`. + pub format: std::string::String, + /// Enables ANSI formatting for this route. + pub ansi: bool, + /// Included tracing targets or wildcard target globs. + pub targets: std::vec::Vec, +} + +/// Logging configuration consumed by `kb_logging` before profile binding exists. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct LoggingConfig { + /// Default log level used when a route does not provide target directives. + pub default_level: std::string::String, + /// Output routes. + pub targets: std::vec::Vec, + /// Target-specific overrides appended to wildcard routes. + pub target_filters: std::vec::Vec, +} diff --git a/migration/khadhroony-bot2-reference/kb_logging/src/constants.rs b/migration/khadhroony-bot2-reference/kb_logging/src/constants.rs new file mode 100644 index 0000000..71e15b3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_logging/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_logging/src/constants.rs +// version: 1 + +//! Local constants for the `kb_logging` crate. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_logging"; diff --git a/migration/khadhroony-bot2-reference/kb_logging/src/lib.rs b/migration/khadhroony-bot2-reference/kb_logging/src/lib.rs new file mode 100644 index 0000000..87f0e99 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_logging/src/lib.rs @@ -0,0 +1,27 @@ +// file: kb_logging/src/lib.rs +// version: 5 + +//! Logging and tracing initialization for applications and workers. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod config; +mod constants; +mod tracing_runtime; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the legacy file route shape kept for early file-only callers. +pub use crate::config::LogFileRoute; +/// Exposes one configured logging output target. +pub use crate::config::LogTargetConfig; +/// Exposes one configured tracing target filter. +pub use crate::config::LogTargetFilterConfig; +/// Exposes the logging configuration consumed by this crate. +pub use crate::config::LoggingConfig; +/// Exposes the guard that keeps non-blocking logging workers alive. +pub use crate::tracing_runtime::LoggingGuard; +/// Exposes initialization from a raw logging configuration section. +pub use crate::tracing_runtime::init_logging; diff --git a/migration/khadhroony-bot2-reference/kb_logging/src/tracing_runtime.rs b/migration/khadhroony-bot2-reference/kb_logging/src/tracing_runtime.rs new file mode 100644 index 0000000..29ee1d1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_logging/src/tracing_runtime.rs @@ -0,0 +1,1110 @@ +// file: kb_logging/src/tracing_runtime.rs +// version: 12 + +//! Runtime initialization helpers for `tracing_subscriber`. + +use tracing_subscriber::Layer; // rust-rules: trait-import +use tracing_subscriber::layer::SubscriberExt; // rust-rules: trait-import +use tracing_subscriber::util::SubscriberInitExt; // rust-rules: trait-import + +/// Guard keeping non-blocking logging workers alive. +#[derive(Debug)] +pub struct LoggingGuard { + guards: std::vec::Vec, + route_names: std::vec::Vec, +} + +impl crate::LoggingGuard { + /// Returns the number of active non-blocking writer guards. + pub fn guard_count(&self) -> usize { + return self.guards.len(); + } + + /// Returns the number of enabled logging routes installed at startup. + pub fn route_count(&self) -> usize { + return self.route_names.len(); + } + + /// Returns the configured names of the enabled routes installed at startup. + pub fn route_names(&self) -> &[std::string::String] { + return &self.route_names; + } +} + +type BoxedLayer = std::boxed::Box< + dyn tracing_subscriber::Layer + + std::marker::Send + + std::marker::Sync, +>; + +/// Initializes global tracing from a logging configuration. +pub fn init_logging(config: &crate::LoggingConfig) -> kb_core::Result { + let mut guards = std::vec::Vec::::new(); + let mut route_names = std::vec::Vec::::new(); + let layers_result = build_enabled_layers(config, &mut guards, &mut route_names); + let layers = match layers_result { + std::result::Result::Ok(layers) => layers, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if layers.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "logging_no_enabled_routes", + "at least one logging target must be enabled", + )); + } + let admission_filters_result = build_enabled_route_writer_filters(config); + let admission_filters = match admission_filters_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let admission_filter = tracing_subscriber::filter::filter_fn(move |metadata| { + return admission_filters.iter().any(|filter| return filter.allows(metadata)); + }); + let init_result = tracing_subscriber::registry() + .with(layers.with_filter(admission_filter)) + .try_init(); + return match init_result { + std::result::Result::Ok(()) => { + tracing::debug!( + target: crate::TRACING_TARGET, + action = "initialize", + route_count = route_names.len(), + route_names = ?route_names, + "tracing subscriber initialized" + ); + std::result::Result::Ok(crate::LoggingGuard { guards, route_names }) + }, + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "logging_subscriber_init_failed", + format!("cannot initialize tracing subscriber: {error}"), + )), + }; +} + +fn build_enabled_layers( + config: &crate::LoggingConfig, + guards: &mut std::vec::Vec, + route_names: &mut std::vec::Vec, +) -> kb_core::Result> { + let mut layers = std::vec::Vec::::new(); + let enabled_routes = enabled_routes(config); + for route in enabled_routes { + let filter = match build_route_writer_filter(route, config) { + std::result::Result::Ok(filter) => filter, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if route.sink == "console" { + let (writer, guard) = tracing_appender::non_blocking(std::io::stdout()); + let layer_result = build_console_layer(route, filter, writer); + let layer = match layer_result { + std::result::Result::Ok(layer) => layer, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + guards.push(guard); + route_names.push(route.name.clone()); + layers.push(layer); + continue; + } + if route.sink == "file" { + let writer_result = build_file_writer(route); + let (writer, guard) = match writer_result { + std::result::Result::Ok(parts) => parts, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let layer_result = build_file_layer(route, filter, writer); + let layer = match layer_result { + std::result::Result::Ok(layer) => layer, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + guards.push(guard); + route_names.push(route.name.clone()); + layers.push(layer); + continue; + } + return std::result::Result::Err(kb_core::Error::new( + "logging_unknown_sink", + format!("unknown logging sink '{}' for route '{}'", route.sink, route.name), + )); + } + return std::result::Result::Ok(layers); +} + +fn enabled_routes(config: &crate::LoggingConfig) -> std::vec::Vec<&crate::LogTargetConfig> { + let mut routes = std::vec::Vec::<&crate::LogTargetConfig>::new(); + for route in &config.targets { + if route.enabled { + routes.push(route); + } + } + return routes; +} + +fn build_enabled_route_writer_filters( + config: &crate::LoggingConfig, +) -> kb_core::Result> { + let mut filters = std::vec::Vec::new(); + for route in enabled_routes(config) { + let result = build_route_writer_filter(route, config); + match result { + std::result::Result::Ok(value) => filters.push(value), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + return std::result::Result::Ok(filters); +} + +fn build_console_layer( + route: &crate::LogTargetConfig, + filter: RouteWriterFilter, + writer: tracing_appender::non_blocking::NonBlocking, +) -> kb_core::Result { + let route_writer = RouteMakeWriter::new(writer, filter); + return match route.format.as_str() { + "compact" => std::result::Result::Ok( + tracing_subscriber::fmt::layer() + .compact() + .with_timer(timer()) + .with_ansi(route.ansi) + .with_target(true) + .with_thread_ids(false) + .with_thread_names(false) + .with_writer(route_writer) + .boxed(), + ), + "pretty" => std::result::Result::Ok( + tracing_subscriber::fmt::layer() + .pretty() + .with_timer(timer()) + .with_ansi(route.ansi) + .with_target(true) + .with_thread_ids(false) + .with_thread_names(false) + .with_writer(route_writer) + .boxed(), + ), + "json" => std::result::Result::Ok( + tracing_subscriber::fmt::layer() + .json() + .with_timer(timer()) + .with_ansi(false) + .with_target(true) + .with_thread_ids(false) + .with_thread_names(false) + .with_writer(route_writer) + .boxed(), + ), + "human" => std::result::Result::Ok( + tracing_subscriber::fmt::layer() + .with_timer(timer()) + .with_ansi(route.ansi) + .with_target(true) + .with_thread_ids(false) + .with_thread_names(false) + .with_writer(route_writer) + .boxed(), + ), + _ => std::result::Result::Err(kb_core::Error::new( + "logging_unknown_format", + format!("unknown logging format '{}' for route '{}'", route.format, route.name), + )), + }; +} + +fn build_file_layer( + route: &crate::LogTargetConfig, + filter: RouteWriterFilter, + writer: StripAnsiMakeWriter, +) -> kb_core::Result { + let route_writer = RouteMakeWriter::new(writer, filter); + return match route.format.as_str() { + "compact" => std::result::Result::Ok( + tracing_subscriber::fmt::layer() + .compact() + .with_timer(timer()) + .with_ansi(false) + .with_target(true) + .with_thread_ids(false) + .with_thread_names(false) + .with_writer(route_writer) + .boxed(), + ), + "pretty" => std::result::Result::Ok( + tracing_subscriber::fmt::layer() + .pretty() + .with_timer(timer()) + .with_ansi(false) + .with_target(true) + .with_thread_ids(false) + .with_thread_names(false) + .with_writer(route_writer) + .boxed(), + ), + "json" => std::result::Result::Ok( + tracing_subscriber::fmt::layer() + .json() + .with_timer(timer()) + .with_ansi(false) + .with_target(true) + .with_thread_ids(false) + .with_thread_names(false) + .with_writer(route_writer) + .boxed(), + ), + "human" => std::result::Result::Ok( + tracing_subscriber::fmt::layer() + .with_timer(timer()) + .with_ansi(false) + .with_target(true) + .with_thread_ids(false) + .with_thread_names(false) + .with_writer(route_writer) + .boxed(), + ), + _ => std::result::Result::Err(kb_core::Error::new( + "logging_unknown_format", + format!("unknown logging format '{}' for route '{}'", route.format, route.name), + )), + }; +} + +fn timer() -> tracing_subscriber::fmt::time::ChronoLocal { + return tracing_subscriber::fmt::time::ChronoLocal::new("%Y-%m-%dT%H:%M:%S%.3f%:z".to_string()); +} + +fn is_error_route(route: &crate::LogTargetConfig) -> bool { + let name = route.name.to_ascii_lowercase(); + return route.level == "error" || name.contains("error"); +} + +#[derive(Clone, Debug)] +struct RouteWriterFilter { + default_level: std::option::Option, + target_levels: std::vec::Vec<(std::string::String, u8)>, +} + +impl RouteWriterFilter { + fn allows(&self, metadata: &tracing::Metadata<'_>) -> bool { + return self.allows_target_level(metadata.target(), metadata.level()); + } + + fn allows_target_level(&self, target: &str, level: &tracing::Level) -> bool { + let mut selected_level = self.default_level; + let mut selected_target_length = 0_usize; + for (configured_target, configured_level) in &self.target_levels { + if target_matches(configured_target, target) + && configured_target.len() >= selected_target_length + { + selected_level = std::option::Option::Some(*configured_level); + selected_target_length = configured_target.len(); + } + } + return match selected_level { + std::option::Option::Some(maximum) => tracing_level_rank(level) <= maximum, + std::option::Option::None => false, + }; + } +} + +fn build_route_writer_filter( + route: &crate::LogTargetConfig, + config: &crate::LoggingConfig, +) -> kb_core::Result { + let route_level_text = if is_error_route(route) { "error" } else { route.level.as_str() }; + let route_level_result = parse_level_rank(route_level_text); + let route_level = match route_level_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut default_level = std::option::Option::None; + let mut target_levels = std::vec::Vec::new(); + for target in &route.targets { + match normalize_target_filter(target) { + std::option::Option::Some(normalized) => { + target_levels.push((normalized, route_level)); + }, + std::option::Option::None => default_level = std::option::Option::Some(route_level), + } + } + if route.targets.is_empty() { + default_level = std::option::Option::Some(route_level); + } + if default_level.is_some() && !is_error_route(route) { + for filter in &config.target_filters { + let normalized = match normalize_target_filter(&filter.target) { + std::option::Option::Some(value) => value, + std::option::Option::None => continue, + }; + let level_result = parse_level_rank(&filter.level); + let level = match level_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + target_levels.push((normalized, level)); + } + } + return std::result::Result::Ok(RouteWriterFilter { default_level, target_levels }); +} + +fn parse_level_rank(level: &str) -> kb_core::Result { + return match level.trim().to_ascii_lowercase().as_str() { + "error" => std::result::Result::Ok(1), + "warn" => std::result::Result::Ok(2), + "info" => std::result::Result::Ok(3), + "debug" => std::result::Result::Ok(4), + "trace" => std::result::Result::Ok(5), + _ => std::result::Result::Err(kb_core::Error::new( + "logging_filter_build_failed", + format!("unsupported tracing level '{level}'"), + )), + }; +} + +fn tracing_level_rank(level: &tracing::Level) -> u8 { + if *level == tracing::Level::ERROR { + return 1; + } + if *level == tracing::Level::WARN { + return 2; + } + if *level == tracing::Level::INFO { + return 3; + } + if *level == tracing::Level::DEBUG { + return 4; + } + return 5; +} + +fn target_matches(configured: &str, observed: &str) -> bool { + return observed == configured + || observed + .strip_prefix(configured) + .is_some_and(|suffix| return suffix.starts_with("::")); +} + +#[cfg(test)] +fn build_route_filter_expression( + route: &crate::LogTargetConfig, + config: &crate::LoggingConfig, +) -> std::string::String { + let mut directives = std::vec::Vec::::new(); + let mut has_wildcard = false; + let route_level = if is_error_route(route) { "error" } else { route.level.as_str() }; + for target in &route.targets { + let normalized_option = normalize_target_filter(target); + match normalized_option { + std::option::Option::Some(normalized) => { + directives.push(format!("{}={}", normalized, route_level)) + }, + std::option::Option::None => has_wildcard = true, + } + } + if route.targets.is_empty() || has_wildcard { + directives.push(route_level.to_string()); + } + if (route.targets.is_empty() || has_wildcard) && !is_error_route(route) { + for filter in &config.target_filters { + let normalized_option = normalize_target_filter(&filter.target); + if let std::option::Option::Some(normalized) = normalized_option { + directives.push(format!("{}={}", normalized, filter.level)); + } + } + } + if directives.is_empty() { + directives.push(config.default_level.clone()); + } + return directives.join(","); +} + +fn normalize_target_filter(target: &str) -> std::option::Option { + let trimmed = target.trim(); + if trimmed.is_empty() || trimmed == "*" { + return std::option::Option::None; + } + if trimmed.ends_with(".*") { + let shortened = trimmed.trim_end_matches(".*"); + if shortened.is_empty() { + return std::option::Option::None; + } + return std::option::Option::Some(shortened.to_string()); + } + if trimmed.ends_with("::*") { + let shortened = trimmed.trim_end_matches("::*"); + if shortened.is_empty() { + return std::option::Option::None; + } + return std::option::Option::Some(shortened.to_string()); + } + return std::option::Option::Some(trimmed.to_string()); +} + +fn build_file_writer( + route: &crate::LogTargetConfig, +) -> kb_core::Result<( + StripAnsiMakeWriter, + tracing_appender::non_blocking::WorkerGuard, +)> { + let path = resolve_workspace_relative_path(&route.path); + let parent = match path.parent() { + std::option::Option::Some(parent) => parent.to_path_buf(), + std::option::Option::None => workspace_root_dir(), + }; + let create_result = std::fs::create_dir_all(&parent); + if let std::result::Result::Err(error) = create_result { + return std::result::Result::Err(kb_core::Error::new( + "logging_directory_create_failed", + format!("cannot create logging directory '{}': {error}", parent.display()), + )); + } + let rotation = if route.rotation == "hourly" { + tracing_appender::rolling::Rotation::HOURLY + } else if route.rotation == "none" || route.rotation == "never" { + tracing_appender::rolling::Rotation::NEVER + } else { + tracing_appender::rolling::Rotation::DAILY + }; + let file_name = match path.file_name() { + std::option::Option::Some(value) => match value.to_str() { + std::option::Option::Some(value) => value.to_string(), + std::option::Option::None => route.name.clone(), + }, + std::option::Option::None => route.name.clone(), + }; + let (prefix, suffix) = split_file_name(&file_name); + let appender_result = tracing_appender::rolling::Builder::default() + .rotation(rotation) + .filename_prefix(prefix) + .filename_suffix(suffix) + .build(&parent); + let appender = match appender_result { + std::result::Result::Ok(appender) => appender, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "logging_appender_build_failed", + format!("cannot build rolling logging appender in '{}': {error}", parent.display()), + )); + }, + }; + let (writer, guard) = tracing_appender::non_blocking(appender); + return std::result::Result::Ok((StripAnsiMakeWriter::new(writer), guard)); +} + +fn split_file_name(file_name: &str) -> (std::string::String, std::string::String) { + let path = std::path::Path::new(file_name); + let stem = match path.file_stem() { + std::option::Option::Some(value) => match value.to_str() { + std::option::Option::Some(value) => value.to_string(), + std::option::Option::None => file_name.to_string(), + }, + std::option::Option::None => file_name.to_string(), + }; + let suffix = match path.extension() { + std::option::Option::Some(value) => match value.to_str() { + std::option::Option::Some(value) => value.to_string(), + std::option::Option::None => "log".to_string(), + }, + std::option::Option::None => "log".to_string(), + }; + return (stem, suffix); +} + +fn workspace_root_dir() -> std::path::PathBuf { + let manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + return match manifest_dir.parent() { + std::option::Option::Some(parent) => parent.to_path_buf(), + std::option::Option::None => manifest_dir, + }; +} + +fn resolve_workspace_relative_path(path: &str) -> std::path::PathBuf { + let input_path = std::path::PathBuf::from(path); + if input_path.is_absolute() { + return input_path; + } + return workspace_root_dir().join(input_path); +} + +struct RouteMakeWriter { + inner: W, + filter: RouteWriterFilter, +} + +impl RouteMakeWriter { + fn new(inner: W, filter: RouteWriterFilter) -> Self { + return Self { inner, filter }; + } +} + +enum RouteWriter { + Enabled(W), + Disabled(std::io::Sink), +} + +impl<'a, W> tracing_subscriber::fmt::MakeWriter<'a> for RouteMakeWriter +where + W: tracing_subscriber::fmt::MakeWriter<'a>, +{ + type Writer = RouteWriter; + + fn make_writer(&'a self) -> Self::Writer { + return RouteWriter::Disabled(std::io::sink()); + } + + fn make_writer_for(&'a self, metadata: &tracing::Metadata<'_>) -> Self::Writer { + if self.filter.allows(metadata) { + return RouteWriter::Enabled(self.inner.make_writer_for(metadata)); + } + return RouteWriter::Disabled(std::io::sink()); + } +} + +impl std::io::Write for RouteWriter +where + W: std::io::Write, +{ + fn write(&mut self, buf: &[u8]) -> std::io::Result { + return match self { + Self::Enabled(writer) => writer.write(buf), + Self::Disabled(writer) => writer.write(buf), + }; + } + + fn flush(&mut self) -> std::io::Result<()> { + return match self { + Self::Enabled(writer) => writer.flush(), + Self::Disabled(writer) => writer.flush(), + }; + } +} + +struct StripAnsiMakeWriter { + inner: W, +} + +impl StripAnsiMakeWriter { + fn new(inner: W) -> Self { + return Self { inner }; + } +} + +struct StripAnsiWriter { + inner: W, +} + +impl<'a, W> tracing_subscriber::fmt::MakeWriter<'a> for StripAnsiMakeWriter +where + W: tracing_subscriber::fmt::MakeWriter<'a>, +{ + type Writer = StripAnsiWriter; + + fn make_writer(&'a self) -> Self::Writer { + return StripAnsiWriter { inner: self.inner.make_writer() }; + } +} + +impl std::io::Write for StripAnsiWriter +where + W: std::io::Write, +{ + fn write(&mut self, buf: &[u8]) -> std::io::Result { + let stripped = strip_ansi_bytes(buf); + let write_result = self.inner.write_all(&stripped); + return match write_result { + std::result::Result::Ok(()) => std::result::Result::Ok(buf.len()), + std::result::Result::Err(error) => std::result::Result::Err(error), + }; + } + + fn flush(&mut self) -> std::io::Result<()> { + return self.inner.flush(); + } +} + +fn strip_ansi_bytes(input: &[u8]) -> std::vec::Vec { + let mut output = std::vec::Vec::with_capacity(input.len()); + let mut index = 0usize; + while index < input.len() { + let byte = input[index]; + if byte == 0x1B && index + 1 < input.len() && input[index + 1] == b'[' { + index += 2; + while index < input.len() { + let current = input[index]; + if (0x40..=0x7E).contains(¤t) { + index += 1; + break; + } + index += 1; + } + continue; + } + output.push(byte); + index += 1; + } + return output; +} + +#[cfg(test)] +mod tests { + use tracing_subscriber::Layer; // rust-rules: trait-import + use tracing_subscriber::layer::SubscriberExt; // rust-rules: trait-import + + #[derive(Clone, Debug)] + struct TestMakeWriter { + output: std::sync::Arc>>, + } + + #[derive(Debug)] + struct TestWriter { + output: std::sync::Arc>>, + } + + impl<'a> tracing_subscriber::fmt::MakeWriter<'a> for TestMakeWriter { + type Writer = TestWriter; + + fn make_writer(&'a self) -> Self::Writer { + return TestWriter { output: self.output.clone() }; + } + } + + impl std::io::Write for TestWriter { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + let lock_result = self.output.lock(); + let mut output = match lock_result { + std::result::Result::Ok(output) => output, + std::result::Result::Err(_) => { + return std::result::Result::Err(std::io::Error::other("poisoned test writer")); + }, + }; + output.extend_from_slice(buf); + return std::result::Result::Ok(buf.len()); + } + + fn flush(&mut self) -> std::io::Result<()> { + return std::result::Result::Ok(()); + } + } + + fn test_route( + name: &str, + sink: &str, + level: &str, + format: &str, + enabled: bool, + targets: std::vec::Vec, + ) -> crate::LogTargetConfig { + return crate::LogTargetConfig { + name: name.to_string(), + enabled, + sink: sink.to_string(), + level: level.to_string(), + path: format!("logs/{name}.log"), + rotation: "daily".to_string(), + format: format.to_string(), + ansi: false, + targets, + }; + } + + fn sample_logging_config() -> crate::LoggingConfig { + return crate::LoggingConfig { + default_level: "info".to_string(), + targets: std::vec![test_route( + "console_main", + "console", + "debug", + "human", + true, + std::vec!["kb_rpc.*".to_string()] + )], + target_filters: std::vec![crate::LogTargetFilterConfig { + target: "hyper".to_string(), + level: "warn".to_string() + }], + }; + } + + fn package_name(cargo_toml: &str) -> std::option::Option { + let mut in_package = false; + for line in cargo_toml.lines() { + let trimmed = line.trim(); + if trimmed == "[package]" { + in_package = true; + continue; + } + if in_package && trimmed.starts_with('[') { + return std::option::Option::None; + } + if in_package && trimmed.starts_with("name = ") { + let value = trimmed.trim_start_matches("name = ").trim_matches('"'); + return std::option::Option::Some(value.to_string()); + } + } + return std::option::Option::None; + } + + fn count_tracing_target_constants(path: &std::path::Path) -> usize { + let read_result = std::fs::read_dir(path); + let entries = match read_result { + std::result::Result::Ok(entries) => entries, + std::result::Result::Err(_) => return 0, + }; + let mut count = 0_usize; + for entry_result in entries { + let entry = match entry_result { + std::result::Result::Ok(entry) => entry, + std::result::Result::Err(_) => continue, + }; + let entry_path = entry.path(); + if entry_path.is_dir() { + count += count_tracing_target_constants(&entry_path); + continue; + } + if entry_path.extension().and_then(std::ffi::OsStr::to_str) + != std::option::Option::Some("rs") + { + continue; + } + let content_result = std::fs::read_to_string(&entry_path); + let content = match content_result { + std::result::Result::Ok(content) => content, + std::result::Result::Err(_) => continue, + }; + count += content + .lines() + .filter(|line| { + return line.trim().starts_with("pub(crate) const TRACING_TARGET:"); + }) + .count(); + } + return count; + } + + #[test] + fn every_tracing_crate_has_one_canonical_target_constant() { + let manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let workspace_root = match manifest_dir.parent() { + std::option::Option::Some(path) => path, + std::option::Option::None => panic!("workspace root must exist"), + }; + let read_result = std::fs::read_dir(workspace_root); + let entries = match read_result { + std::result::Result::Ok(entries) => entries, + std::result::Result::Err(error) => panic!("workspace must be readable: {error}"), + }; + let mut tracing_crate_count = 0_usize; + for entry_result in entries { + let entry = match entry_result { + std::result::Result::Ok(entry) => entry, + std::result::Result::Err(error) => { + panic!("workspace entry must be readable: {error}") + }, + }; + let crate_path = entry.path(); + if !crate_path.is_dir() { + continue; + } + let cargo_path = crate_path.join("Cargo.toml"); + if !cargo_path.is_file() { + continue; + } + let cargo_result = std::fs::read_to_string(&cargo_path); + let cargo_toml = match cargo_result { + std::result::Result::Ok(content) => content, + std::result::Result::Err(error) => panic!("Cargo.toml must be readable: {error}"), + }; + if !cargo_toml.lines().any(|line| return line.trim() == "tracing.workspace = true") { + continue; + } + tracing_crate_count += 1; + let package_name = match package_name(&cargo_toml) { + std::option::Option::Some(name) => name, + std::option::Option::None => panic!("tracing crate must declare a package name"), + }; + let constants_path = crate_path.join("src/constants.rs"); + let constants_result = std::fs::read_to_string(&constants_path); + let constants = match constants_result { + std::result::Result::Ok(content) => content, + std::result::Result::Err(error) => { + panic!("tracing crate {package_name} must expose src/constants.rs: {error}") + }, + }; + let expected = format!("pub(crate) const TRACING_TARGET: &str = \"{package_name}\";"); + assert!(constants.contains(&expected), "invalid target for {package_name}"); + assert_eq!( + count_tracing_target_constants(&crate_path.join("src")), + 1, + "tracing crate {package_name} must declare exactly one target constant" + ); + } + assert!(tracing_crate_count > 0); + } + + #[test] + fn route_filter_converts_wildcard_prefix() { + let config = sample_logging_config(); + let expression = super::build_route_filter_expression(&config.targets[0], &config); + assert_eq!(expression, "kb_rpc=debug"); + } + + #[test] + fn route_filter_converts_rust_wildcard_prefix() { + let mut config = sample_logging_config(); + config.targets[0].targets = std::vec!["kb_rpc::*".to_string()]; + let expression = super::build_route_filter_expression(&config.targets[0], &config); + assert_eq!(expression, "kb_rpc=debug"); + } + + #[test] + fn route_filter_keeps_exact_target() { + let mut config = sample_logging_config(); + config.targets[0].targets = std::vec!["kb_rpc::client".to_string()]; + let expression = super::build_route_filter_expression(&config.targets[0], &config); + assert_eq!(expression, "kb_rpc::client=debug"); + } + + #[test] + fn route_filter_adds_target_filters_for_wildcard_routes() { + let mut config = sample_logging_config(); + config.targets[0].targets = std::vec!["*".to_string()]; + let expression = super::build_route_filter_expression(&config.targets[0], &config); + assert_eq!(expression, "debug,hyper=warn"); + } + + #[test] + fn route_filter_uses_route_level_for_empty_targets() { + let mut config = sample_logging_config(); + config.targets[0].targets = std::vec![]; + let expression = super::build_route_filter_expression(&config.targets[0], &config); + assert_eq!(expression, "debug,hyper=warn"); + } + + #[test] + fn target_filters_accept_rust_wildcard_prefix() { + let mut config = sample_logging_config(); + config.targets[0].targets = std::vec!["*".to_string()]; + config.target_filters = std::vec![crate::LogTargetFilterConfig { + target: "kb_store_pg::*".to_string(), + level: "trace".to_string() + }]; + let expression = super::build_route_filter_expression(&config.targets[0], &config); + assert_eq!(expression, "debug,kb_store_pg=trace"); + } + + #[test] + fn route_writer_filter_preserves_target_and_level_semantics() { + let mut config = sample_logging_config(); + config.targets[0].targets = std::vec!["*".to_string()]; + let result = super::build_route_writer_filter(&config.targets[0], &config); + let filter = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("route writer filter failed: {error}"), + }; + assert!(filter.allows_target_level("kb_rpc", &tracing::Level::DEBUG)); + assert!(!filter.allows_target_level("kb_rpc", &tracing::Level::TRACE)); + assert!(filter.allows_target_level("hyper", &tracing::Level::WARN)); + assert!(!filter.allows_target_level("hyper", &tracing::Level::INFO)); + } + + #[test] + fn more_than_64_routes_compose_without_filtered_layer_ids() { + let mut routes = std::vec::Vec::new(); + for index in 0..65_u32 { + let route_name = format!("console_{index}"); + routes.push(test_route( + route_name.as_str(), + "console", + "info", + "human", + true, + std::vec![format!("target_{index}")], + )); + } + let config = crate::LoggingConfig { + default_level: "info".to_string(), + targets: routes, + target_filters: std::vec::Vec::new(), + }; + let mut guards = std::vec::Vec::new(); + let mut route_names = std::vec::Vec::new(); + let result = super::build_enabled_layers(&config, &mut guards, &mut route_names); + let layers = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("layer construction failed: {error}"), + }; + assert_eq!(layers.len(), 65); + assert_eq!(route_names.len(), 65); + let filter_result = super::build_enabled_route_writer_filters(&config); + let filters = match filter_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("admission filters failed: {error}"), + }; + let admission_filter = tracing_subscriber::filter::filter_fn(move |metadata| { + return filters.iter().any(|filter| return filter.allows(metadata)); + }); + let _subscriber = tracing_subscriber::registry().with(layers.with_filter(admission_filter)); + } + + #[test] + fn error_route_forces_error_level() { + let config = crate::LoggingConfig { + default_level: "info".to_string(), + targets: std::vec![test_route( + "file_errors", + "file", + "debug", + "human", + true, + std::vec!["*".to_string()] + )], + target_filters: std::vec![], + }; + let expression = super::build_route_filter_expression(&config.targets[0], &config); + assert_eq!(expression, "error"); + } + + #[test] + fn error_route_does_not_append_target_filters() { + let config = crate::LoggingConfig { + default_level: "info".to_string(), + targets: std::vec![test_route( + "file_errors", + "file", + "debug", + "human", + true, + std::vec!["*".to_string()] + )], + target_filters: std::vec![crate::LogTargetFilterConfig { + target: "hyper".to_string(), + level: "warn".to_string() + }], + }; + let expression = super::build_route_filter_expression(&config.targets[0], &config); + assert_eq!(expression, "error"); + } + + #[test] + fn disabled_routes_are_not_selected() { + let config = crate::LoggingConfig { + default_level: "info".to_string(), + targets: std::vec![ + test_route( + "console_main", + "console", + "info", + "human", + false, + std::vec!["*".to_string()] + ), + test_route( + "file_human", + "file", + "info", + "human", + false, + std::vec!["*".to_string()] + ) + ], + target_filters: std::vec![], + }; + let routes = super::enabled_routes(&config); + assert!(routes.is_empty()); + } + + #[test] + fn enabled_routes_keep_all_configured_outputs() { + let config = crate::LoggingConfig { + default_level: "info".to_string(), + targets: std::vec![ + test_route( + "console_main", + "console", + "debug", + "compact", + true, + std::vec!["*".to_string()] + ), + test_route( + "file_kb_core", + "file", + "debug", + "human", + true, + std::vec!["kb_core::*".to_string()] + ), + test_route( + "file_kb_config", + "file", + "debug", + "human", + true, + std::vec!["kb_config::*".to_string()] + ), + test_route( + "file_errors", + "file", + "debug", + "json", + true, + std::vec!["*".to_string()] + ) + ], + target_filters: std::vec![], + }; + let routes = super::enabled_routes(&config); + let route_names = routes + .iter() + .map(|route| return route.name.as_str()) + .collect::>(); + assert_eq!( + route_names, + std::vec!["console_main", "file_kb_core", "file_kb_config", "file_errors"] + ); + } + + #[test] + fn init_logging_rejects_configuration_without_enabled_routes() { + let config = crate::LoggingConfig { + default_level: "info".to_string(), + targets: std::vec![test_route( + "console_main", + "console", + "info", + "human", + false, + std::vec!["*".to_string()] + )], + target_filters: std::vec![], + }; + let result = super::init_logging(&config); + assert!(result.is_err()); + } + + #[test] + fn split_file_name_keeps_stem_and_suffix() { + let parts = super::split_file_name("khadhroony.bot2.log"); + assert_eq!(parts, ("khadhroony.bot2".to_string(), "log".to_string())); + } + + #[test] + fn ansi_stripper_removes_escape_sequences() { + let stripped = super::strip_ansi_bytes(b"a\x1b[31mb"); + assert_eq!(stripped, b"ab"); + } + + #[test] + fn ansi_stripping_writer_removes_escape_sequences_before_inner_writer() { + let output = std::sync::Arc::new(std::sync::Mutex::new(std::vec::Vec::::new())); + let make_writer = + super::StripAnsiMakeWriter::new(TestMakeWriter { output: output.clone() }); + let mut writer = tracing_subscriber::fmt::MakeWriter::make_writer(&make_writer); + let write_result = std::io::Write::write_all(&mut writer, b"a\x1b[32mb"); + assert!(write_result.is_ok()); + drop(writer); + let actual = match output.lock() { + std::result::Result::Ok(output) => output.clone(), + std::result::Result::Err(_) => std::vec::Vec::::new(), + }; + assert_eq!(actual, b"ab"); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_admin/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_admin/Cargo.toml new file mode 100644 index 0000000..26e3922 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_admin/Cargo.toml @@ -0,0 +1,23 @@ +# file: kb_materializer_admin/Cargo.toml +# version: 5 + +[package] +name = "kb_materializer_admin" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_decoder_spl_elgamal_registry = { path = "../kb_decoder_spl_elgamal_registry" } +kb_decoder_spl_token_2022 = { path = "../kb_decoder_spl_token_2022" } +kb_model = { path = "../kb_model" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_admin/README.md b/migration/khadhroony-bot2-reference/kb_materializer_admin/README.md new file mode 100644 index 0000000..520e47f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_admin/README.md @@ -0,0 +1,25 @@ + + + +# kb_materializer_admin + +Ce crate matérialise les mutations administratives exactes et commitées qui possèdent une identité stable sans nécessiter un snapshot complet du compte. + +## Projections natives actives + +- `system_account_ownership::0` pour `assign` et `assign_with_seed` ; +- `config_account_update:store:0` pour les écritures Config génériques, avec clés, signataires, longueur, SHA-256 et préfixe borné du payload opaque ; +- `program_loader_authority:::0` pour `set_authority`, `set_authority_checked` et `transfer_authority`. +- `spl_token_authority:set_authority:0` pour les changements ou révocations d'autorité Token ; +- `spl_token_multisig::0` pour les configurations multisig classiques, avec seuil et membres ordonnés. + +Les sorties utilisent la famille matérialisée `Admin` et la politique `SuccessfulCommittedOnly`. Une transaction échouée ou une observation non commitée ne produit aucune mutation administrative. + +## Limites + +Le crate conserve l'intention commitée et les paramètres observables, mais ne prétend pas reconstruire l'état final du compte. Les données Config complètes et le bytecode Loader ne sont jamais recopiés dans les sorties matérialisées. +Les instructions ATA ne modifient aucune autorité de mint, freeze, close ou multisig et restent donc explicitement sans projection administrative. + +## État ElGamal Registry + +`materialize_elgamal_registry_state_snapshot` projette l’état exact lu du compte registre (`owner`, clé ElGamal publique et wire). Cette projection ne prétend pas revalider la preuve cryptographique depuis les seuls octets du compte et ne déchiffre aucune valeur. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_admin/src/constants.rs b/migration/khadhroony-bot2-reference/kb_materializer_admin/src/constants.rs new file mode 100644 index 0000000..ad4215c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_admin/src/constants.rs @@ -0,0 +1,10 @@ +// file: kb_materializer_admin/src/constants.rs +// version: 2 + +//! Local constants for the `kb_materializer_admin` crate. + +/// Canonical processor name for this crate. +pub(crate) const PROCESSOR_NAME: &str = "solana_native_admin"; + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_materializer_admin"; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_admin/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_admin/src/lib.rs new file mode 100644 index 0000000..9eef974 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_admin/src/lib.rs @@ -0,0 +1,23 @@ +// file: kb_materializer_admin/src/lib.rs +// version: 8 + +//! Materializer crate for stable administrative projections. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod materializer; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Stable administrative materializer for supported native mutations. +pub use crate::materializer::AdminMaterializer; +/// Materialize one authoritative SPL ElGamal registry account snapshot. +pub use crate::materializer::materialize_elgamal_registry_state_snapshot; +/// Materialize authoritative Token-2022 administrative extension snapshots. +pub use crate::materializer::materialize_token_2022_admin_state_snapshots; + +/// Canonical processor name for this crate. +pub(crate) use crate::constants::PROCESSOR_NAME; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_admin/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_admin/src/materializer.rs new file mode 100644 index 0000000..57fccb1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_admin/src/materializer.rs @@ -0,0 +1,1151 @@ +// file: kb_materializer_admin/src/materializer.rs +// version: 15 + +//! Stable administrative projections derived from exact committed observations. + +const ACCEPTED_FAMILIES: &[kb_model::EventFamily] = + &[kb_model::EventFamily::Admin, kb_model::EventFamily::TokenAccount]; +const SYSTEM_SURFACE: &str = "solana_native_system"; +const SYSTEM_ENTRIES: &[&str] = &["assign", "assign_with_seed"]; +const CONFIG_SURFACE: &str = "solana_native_config"; +const CONFIG_ENTRIES: &[&str] = &["store"]; +const BPF_LOADER_UPGRADEABLE_SURFACE: &str = "solana_native_bpf_loader_upgradeable"; +const BPF_LOADER_UPGRADEABLE_ENTRIES: &[&str] = &["set_authority", "set_authority_checked"]; +const LOADER_V4_SURFACE: &str = "solana_native_loader_v4"; +const LOADER_V4_ENTRIES: &[&str] = &["transfer_authority"]; +const SPL_TOKEN_SURFACE: &str = "spl_token"; +const SPL_TOKEN_2022_SURFACE: &str = "spl_token_2022"; +const SPL_ELGAMAL_REGISTRY_SURFACE: &str = "spl_elgamal_registry"; +const SPL_ELGAMAL_REGISTRY_ENTRIES: &[&str] = &["create_registry", "update_registry"]; +const SPL_TOKEN_AUTHORITY_ENTRIES: &[&str] = &[ + "set_authority", + "initialize_permanent_delegate", + "initialize_interest_bearing_mint", + "update_interest_bearing_rate", +]; +const SPL_TOKEN_MULTISIG_ENTRIES: &[&str] = &["initialize_multisig", "initialize_multisig2"]; +const SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES: &[&str] = &[ + "initialize_transfer_hook", + "update_transfer_hook_program_id", + "initialize_metadata_pointer", + "update_metadata_pointer", + "initialize_group_pointer", + "update_group_pointer", + "initialize_group_member_pointer", + "update_group_member_pointer", + "initialize_scaled_ui_amount", + "update_scaled_ui_amount_multiplier", +]; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum ProjectionKind { + SystemAccountOwnership, + ConfigAccountUpdate, + ProgramLoaderAuthority(&'static str), + SplTokenAuthority, + SplTokenMultisig, + SplElgamalRegistry, + SplToken2022ExtensionAdmin, +} + +impl ProjectionKind { + fn domain(self) -> &'static str { + return match self { + Self::SystemAccountOwnership => "system_account_ownership", + Self::ConfigAccountUpdate => "config_account_update", + Self::ProgramLoaderAuthority(_) => "program_loader_authority", + Self::SplTokenAuthority => "spl_token_authority", + Self::SplTokenMultisig => "spl_token_multisig", + Self::SplElgamalRegistry => "spl_elgamal_registry", + Self::SplToken2022ExtensionAdmin => "spl_token_2022_extension_admin", + }; + } + + fn loader_surface(self) -> std::option::Option<&'static str> { + return match self { + Self::ProgramLoaderAuthority(surface) => std::option::Option::Some(surface), + _ => std::option::Option::None, + }; + } + + fn output_key(self, surface_code: &str, operation: &str) -> std::string::String { + return match self { + Self::SystemAccountOwnership => format!("system_account_ownership:{operation}:0"), + Self::ConfigAccountUpdate => format!("config_account_update:{operation}:0"), + Self::ProgramLoaderAuthority(_) => { + format!("program_loader_authority:{surface_code}:{operation}:0") + }, + Self::SplTokenAuthority => format!("spl_token_authority:{operation}:0"), + Self::SplTokenMultisig => format!("spl_token_multisig:{operation}:0"), + Self::SplElgamalRegistry => format!("spl_elgamal_registry:{operation}:0"), + Self::SplToken2022ExtensionAdmin => { + format!("spl_token_2022_extension_admin:{operation}:0") + }, + }; + } +} + +/// Stable administrative materializer for supported native mutations. +#[derive(Clone, Debug, Default)] +pub struct AdminMaterializer; + +impl kb_materializer_api::Materializer for crate::AdminMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_admin"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, event: &kb_model::DecodedProtocolEvent) -> bool { + return ACCEPTED_FAMILIES.contains(&event.event_family) + && program_matches_surface(event) + && projection(event.surface_code.0.as_str(), event.event_name.0.as_str()).is_some(); + } + + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + if !kb_materializer_api::Materializer::accepts_event(self, event) { + return std::result::Result::Ok(std::vec::Vec::new()); + } + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +impl kb_materializer_api::EventMaterializer for crate::AdminMaterializer { + fn identity(&self) -> kb_materializer_api::MaterializerIdentity { + return kb_materializer_api::MaterializerIdentity { + name: "solana_native_admin".to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + }; + } + + fn accepted_families(&self) -> &'static [kb_model::EventFamily] { + return ACCEPTED_FAMILIES; + } + + fn accepts_observation(&self, observation: &kb_decoder_api::DecodedObservation) -> bool { + return ACCEPTED_FAMILIES.contains(&observation.event.event_family) + && program_matches_surface(&observation.event) + && projection( + observation.event.surface_code.0.as_str(), + observation.event.event_name.0.as_str(), + ) + .is_some(); + } + + fn transaction_policy( + &self, + _family: kb_model::EventFamily, + ) -> kb_materializer_api::MaterializationTransactionPolicy { + return kb_materializer_api::MaterializationTransactionPolicy::SuccessfulCommittedOnly; + } + + fn materialize( + &self, + observation: &kb_decoder_api::DecodedObservation, + ) -> kb_materializer_api::MaterializerExecutionResult { + let projection = match projection( + observation.event.surface_code.0.as_str(), + observation.event.event_name.0.as_str(), + ) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + tracing::debug!( + target: crate::TRACING_TARGET, + action = "materialize_admin", + surface_code = %observation.event.surface_code.0.as_str(), + entry_code = %observation.event.event_name.0.as_str(), + accepted = false, + "ignore observation outside supported native administrative projections" + ); + return kb_materializer_api::MaterializerExecutionResult::ignored(); + }, + }; + if !ACCEPTED_FAMILIES.contains(&observation.event.event_family) { + return kb_materializer_api::MaterializerExecutionResult::ignored(); + } + if !program_matches_surface(&observation.event) { + return kb_materializer_api::MaterializerExecutionResult::ignored(); + } + if observation.transaction_failed || !observation.observation_committed { + tracing::debug!( + target: crate::TRACING_TARGET, + action = "materialize_admin", + surface_code = %observation.event.surface_code.0.as_str(), + entry_code = %observation.event.event_name.0.as_str(), + committed = observation.observation_committed, + transaction_failed = observation.transaction_failed, + "refuse uncommitted native administrative observation" + ); + return kb_materializer_api::MaterializerExecutionResult::refused( + "failed_transaction_admin_refused", + "failed or uncommitted native observations cannot create administrative mutation outputs", + ); + } + let accounts = observation + .payload_json + .get("accounts") + .cloned() + .unwrap_or(serde_json::Value::Null); + let parameters = observation + .payload_json + .get("parameters") + .cloned() + .unwrap_or_else(|| return observation.payload_json.clone()); + let operation = observation.event.event_name.0.clone(); + let output = kb_materializer_api::MaterializedOutput { + output_key: projection + .output_key(observation.event.surface_code.0.as_str(), operation.as_str()), + family: kb_model::MaterializedEventFamily::Admin, + payload_json: serde_json::json!({ + "projectionVersion": 1, + "projectionSemantics": "committed_instruction_admin_event", + "domain": projection.domain(), + "loaderSurface": projection.loader_surface(), + "operation": operation, + "programId": observation.event.program_id.0.clone(), + "signature": observation.event.signature.0.clone(), + "slot": observation.event.slot.0, + "instructionPath": observation.event.instruction_path.0.clone(), + "transactionSucceeded": true, + "accounts": accounts, + "parameters": parameters, + "projection": projection_details( + projection, + &accounts, + ¶meters, + observation.event.event_name.0.as_str(), + ), + }), + }; + tracing::debug!( + target: crate::TRACING_TARGET, + action = "materialize_admin", + surface_code = %observation.event.surface_code.0.as_str(), + entry_code = %observation.event.event_name.0.as_str(), + projection_domain = projection.domain(), + output_count = 1_usize, + "materialize committed native administrative observation" + ); + return kb_materializer_api::MaterializerExecutionResult { + status: kb_materializer_api::MaterializerOutcomeStatus::Inserted, + outputs: std::vec![output], + diagnostics: std::vec::Vec::new(), + }; + } +} + +fn projection(surface_code: &str, entry_code: &str) -> std::option::Option { + if surface_code == SYSTEM_SURFACE && contains(SYSTEM_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::SystemAccountOwnership); + } + if surface_code == CONFIG_SURFACE && contains(CONFIG_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::ConfigAccountUpdate); + } + if surface_code == BPF_LOADER_UPGRADEABLE_SURFACE + && contains(BPF_LOADER_UPGRADEABLE_ENTRIES, entry_code) + { + return std::option::Option::Some(ProjectionKind::ProgramLoaderAuthority( + BPF_LOADER_UPGRADEABLE_SURFACE, + )); + } + if surface_code == LOADER_V4_SURFACE && contains(LOADER_V4_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::ProgramLoaderAuthority( + LOADER_V4_SURFACE, + )); + } + if matches!(surface_code, SPL_TOKEN_SURFACE | SPL_TOKEN_2022_SURFACE) + && contains(SPL_TOKEN_AUTHORITY_ENTRIES, entry_code) + { + return std::option::Option::Some(ProjectionKind::SplTokenAuthority); + } + if matches!(surface_code, SPL_TOKEN_SURFACE | SPL_TOKEN_2022_SURFACE) + && contains(SPL_TOKEN_MULTISIG_ENTRIES, entry_code) + { + return std::option::Option::Some(ProjectionKind::SplTokenMultisig); + } + if surface_code == SPL_TOKEN_2022_SURFACE + && contains(SPL_TOKEN_2022_EXTENSION_ADMIN_ENTRIES, entry_code) + { + return std::option::Option::Some(ProjectionKind::SplToken2022ExtensionAdmin); + } + if surface_code == SPL_ELGAMAL_REGISTRY_SURFACE + && contains(SPL_ELGAMAL_REGISTRY_ENTRIES, entry_code) + { + return std::option::Option::Some(ProjectionKind::SplElgamalRegistry); + } + return std::option::Option::None; +} + +fn program_matches_surface(event: &kb_model::DecodedProtocolEvent) -> bool { + if event.surface_code.0 == SPL_TOKEN_SURFACE { + return event.program_id.0 == kb_program_ids::SPL_TOKEN_PROGRAM_ID; + } + if event.surface_code.0 == SPL_TOKEN_2022_SURFACE { + return event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID; + } + if event.surface_code.0 == SPL_ELGAMAL_REGISTRY_SURFACE { + return event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID; + } + return true; +} + +fn projection_details( + projection: ProjectionKind, + accounts: &serde_json::Value, + parameters: &serde_json::Value, + operation: &str, +) -> serde_json::Value { + return match projection { + ProjectionKind::SystemAccountOwnership => serde_json::json!({ + "targetAccount": first_account_key_for_roles( + accounts, + &["assigned_account", "derived_account"], + ), + "baseAccount": account_key_for_role(accounts, "base_account"), + "newOwner": parameters.get("owner").cloned().unwrap_or(serde_json::Value::Null), + "seed": parameters.get("seed").cloned().unwrap_or(serde_json::Value::Null), + "stateTransition": if operation == "assign_with_seed" { + "derived_account_owner_assigned" + } else { + "account_owner_assigned" + }, + "transactionFinalAccountStateCaptured": false, + }), + ProjectionKind::ConfigAccountUpdate => serde_json::json!({ + "targetAccount": account_key_for_role(accounts, "config_account"), + "configuredKeys": parameters.get("configuredKeys").cloned().unwrap_or(serde_json::Value::Null), + "configuredKeyCount": parameters.get("configuredKeyCount").cloned().unwrap_or(serde_json::Value::Null), + "configuredSignerCount": parameters.get("configuredSignerCount").cloned().unwrap_or(serde_json::Value::Null), + "configDataByteLength": parameters.get("configDataByteLength").cloned().unwrap_or(serde_json::Value::Null), + "configDataSha256": parameters.get("configDataSha256").cloned().unwrap_or(serde_json::Value::Null), + "configDataPrefixHex": parameters.get("configDataPrefixHex").cloned().unwrap_or(serde_json::Value::Null), + "configDataSemantics": "opaque_program_specific", + "stateTransition": "config_account_data_replaced", + "payloadMaterialized": false, + "transactionFinalAccountStateCaptured": false, + }), + ProjectionKind::ProgramLoaderAuthority(_) => serde_json::json!({ + "targetAccount": first_account_key_for_roles( + accounts, + &["authority_target", "program_account"], + ), + "currentAuthority": first_account_key_for_roles( + accounts, + &["current_authority", "authority"], + ), + "newAuthority": account_key_for_role(accounts, "new_authority"), + "stateTransition": match operation { + "set_authority" => "authority_changed_or_revoked", + "set_authority_checked" => "authority_changed_with_new_authority_signature", + "transfer_authority" => "authority_transferred_with_new_authority_signature", + _ => "authority_changed", + }, + "newAuthorityOptional": operation == "set_authority", + "transactionFinalAccountStateCaptured": false, + }), + ProjectionKind::SplTokenAuthority => serde_json::json!({ + "targetAccount": account_key_for_role(accounts, "authority_target"), + "currentAuthority": account_key_for_role(accounts, "authority"), + "authorityType": parameters.get("authorityType").cloned().unwrap_or(serde_json::Value::Null), + "newAuthority": parameters.get("newAuthority").cloned().unwrap_or(serde_json::Value::Null), + "stateTransition": if parameters.get("newAuthority").is_some_and(serde_json::Value::is_null) { + "token_authority_revoked" + } else { + "token_authority_changed" + }, + "transactionFinalAccountStateCaptured": false, + }), + ProjectionKind::SplTokenMultisig => serde_json::json!({ + "multisigAccount": account_key_for_role(accounts, "multisig_account"), + "thresholdM": parameters.get("m").cloned().unwrap_or(serde_json::Value::Null), + "members": account_keys_for_role(accounts, "multisig_member"), + "stateTransition": "token_multisig_initialized", + "transactionFinalAccountStateCaptured": false, + }), + ProjectionKind::SplElgamalRegistry => serde_json::json!({ + "registryAccount": account_key_at_position(accounts, 0), + "ownerAccount": account_key_at_position( + accounts, + if operation == "create_registry" { 1 } else { 2 }, + ), + "proofAccountOrInstructionsSysvar": account_key_at_position( + accounts, + if operation == "create_registry" { 3 } else { 1 }, + ), + "proofInstructionOffset": parameters + .get("proofInstructionOffset") + .cloned() + .unwrap_or(serde_json::Value::Null), + "proofLocationKind": parameters + .get("proofLocationKind") + .cloned() + .unwrap_or(serde_json::Value::Null), + "stateTransition": if operation == "create_registry" { + "elgamal_registry_created" + } else { + "elgamal_registry_key_updated" + }, + "registeredElgamalPubkeyCaptured": false, + "proofValidatedByRegistryProgram": true, + "transactionFinalAccountStateCaptured": false, + }), + ProjectionKind::SplToken2022ExtensionAdmin => serde_json::json!({ + "targetAccount": account_key_at_position(accounts, 0), + "authority": parameters.get("authority").cloned().unwrap_or(serde_json::Value::Null), + "programId": parameters.get("programId").cloned().unwrap_or(serde_json::Value::Null), + "metadataAddress": parameters.get("metadataAddress").cloned().unwrap_or(serde_json::Value::Null), + "groupAddress": parameters.get("groupAddress").cloned().unwrap_or(serde_json::Value::Null), + "memberAddress": parameters.get("memberAddress").cloned().unwrap_or(serde_json::Value::Null), + "multiplier": parameters.get("multiplier").cloned().unwrap_or(serde_json::Value::Null), + "effectiveTimestamp": parameters.get("effectiveTimestamp").cloned().unwrap_or(serde_json::Value::Null), + "stateTransition": operation, + "stateReadRequired": true, + "transactionFinalAccountStateCaptured": false, + }), + }; +} + +fn account_keys_for_role(accounts: &serde_json::Value, role: &str) -> serde_json::Value { + let values = accounts + .as_array() + .map(|rows| { + return rows + .iter() + .filter_map(|row| { + if row.get("role").and_then(serde_json::Value::as_str) + != std::option::Option::Some(role) + { + return std::option::Option::None; + } + return row + .get("accountKey") + .and_then(serde_json::Value::as_str) + .map(|value| return serde_json::Value::String(value.to_string())); + }) + .collect::>(); + }) + .unwrap_or_default(); + return serde_json::Value::Array(values); +} + +fn first_account_key_for_roles(accounts: &serde_json::Value, roles: &[&str]) -> serde_json::Value { + for role in roles { + let value = account_key_for_role(accounts, role); + if !value.is_null() { + return value; + } + } + return serde_json::Value::Null; +} + +fn account_key_for_role(accounts: &serde_json::Value, role: &str) -> serde_json::Value { + let account_key = accounts.as_array().and_then(|values| { + return values.iter().find_map(|value| { + if value.get("role").and_then(serde_json::Value::as_str) + != std::option::Option::Some(role) + { + return std::option::Option::None; + } + return value + .get("accountKey") + .and_then(serde_json::Value::as_str) + .map(|account_key| return account_key.to_string()); + }); + }); + return match account_key { + std::option::Option::Some(value) => serde_json::Value::String(value), + std::option::Option::None => serde_json::Value::Null, + }; +} + +fn account_key_at_position(accounts: &serde_json::Value, position: usize) -> serde_json::Value { + let account_key = accounts.as_array().and_then(|values| { + return values.iter().find_map(|value| { + let row_position = value + .get("position") + .and_then(serde_json::Value::as_u64) + .map(|value| return value as usize); + if row_position != std::option::Option::Some(position) { + return std::option::Option::None; + } + return value + .get("accountKey") + .and_then(serde_json::Value::as_str) + .map(|account_key| return account_key.to_string()); + }); + }); + return match account_key { + std::option::Option::Some(value) => serde_json::Value::String(value), + std::option::Option::None => serde_json::Value::Null, + }; +} + +fn contains(entries: &[&str], entry_code: &str) -> bool { + return entries.iter().any(|entry| return *entry == entry_code); +} + +#[cfg(test)] +mod tests { + fn observation( + surface_code: &str, + entry_code: &str, + transaction_failed: bool, + accounts: serde_json::Value, + parameters: serde_json::Value, + ) -> kb_decoder_api::DecodedObservation { + return kb_decoder_api::DecodedObservation { + event_key: format!("{entry_code}:0"), + event: kb_model::DecodedProtocolEvent { + signature: kb_model::Signature("signature".to_string()), + slot: kb_model::Slot(42), + instruction_path: kb_model::InstructionPath("0".to_string()), + program_id: kb_model::ProgramId("program-id".to_string()), + protocol_code: kb_model::ProtocolCode("solana_native".to_string()), + surface_code: kb_model::SurfaceCode(surface_code.to_string()), + event_code: kb_model::EventCode(format!("{surface_code}.{entry_code}")), + event_name: kb_model::EventName(entry_code.to_string()), + event_family: kb_model::EventFamily::Admin, + source_kind: kb_model::EventSourceKind::Instruction, + confidence: kb_model::DecoderConfidence::ManualExact, + }, + payload_json: serde_json::json!({"accounts": accounts, "parameters": parameters}), + transaction_failed, + transaction_error: if transaction_failed { + std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]})) + } else { + std::option::Option::None + }, + observation_committed: !transaction_failed, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::Manual, + confidence: kb_model::DecoderConfidence::ManualExact, + evidence: std::vec!["fixture".to_string()], + }, + }; + } + + fn account(role: &str, account_key: &str) -> serde_json::Value { + return serde_json::json!({"role": role, "accountKey": account_key}); + } + + #[test] + fn system_assign_creates_owner_projection() { + let materializer = crate::AdminMaterializer; + let observation = observation( + super::SYSTEM_SURFACE, + "assign", + false, + serde_json::json!([account("assigned_account", "account111",)]), + serde_json::json!({"owner": "owner111"}), + ); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs[0].family, kb_model::MaterializedEventFamily::Admin); + assert_eq!(result.outputs[0].output_key, "system_account_ownership:assign:0"); + assert_eq!(result.outputs[0].payload_json["projection"]["newOwner"], "owner111"); + } + + #[test] + fn seeded_assign_preserves_base_seed_and_owner() { + let materializer = crate::AdminMaterializer; + let observation = observation( + super::SYSTEM_SURFACE, + "assign_with_seed", + false, + serde_json::json!([ + account("derived_account", "derived111"), + account("base_account", "base111"), + ]), + serde_json::json!({"owner": "owner111", "seed": "seed"}), + ); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.outputs[0].payload_json["projection"]["targetAccount"], "derived111"); + assert_eq!(result.outputs[0].payload_json["projection"]["baseAccount"], "base111"); + assert_eq!(result.outputs[0].payload_json["projection"]["seed"], "seed"); + } + + #[test] + fn config_store_preserves_bounded_opaque_audit_fields() { + let materializer = crate::AdminMaterializer; + let observation = observation( + super::CONFIG_SURFACE, + "store", + false, + serde_json::json!([ + account("config_account", "config111"), + account("configured_signer", "signer111"), + ]), + serde_json::json!({ + "configuredKeys": [{"pubkey": "signer111", "signer": true}], + "configuredKeyCount": 1, + "configuredSignerCount": 1, + "configDataByteLength": 64, + "configDataSha256": "abc", + "configDataPrefixHex": "0102", + }), + ); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.outputs[0].output_key, "config_account_update:store:0"); + assert_eq!(result.outputs[0].payload_json["projection"]["targetAccount"], "config111"); + assert_eq!(result.outputs[0].payload_json["projection"]["configDataSha256"], "abc"); + assert_eq!(result.outputs[0].payload_json["projection"]["payloadMaterialized"], false); + } + + #[test] + fn loader_authority_variants_create_exact_admin_outputs() { + let materializer = crate::AdminMaterializer; + for (surface, entry, target_role) in [ + (super::BPF_LOADER_UPGRADEABLE_SURFACE, "set_authority", "authority_target"), + ( + super::BPF_LOADER_UPGRADEABLE_SURFACE, + "set_authority_checked", + "authority_target", + ), + (super::LOADER_V4_SURFACE, "transfer_authority", "program_account"), + ] { + let observation = observation( + surface, + entry, + false, + serde_json::json!([ + account(target_role, "target111"), + account("current_authority", "current111"), + account("new_authority", "new111"), + ]), + serde_json::json!({}), + ); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs[0].payload_json["projection"]["targetAccount"], "target111"); + assert_eq!(result.outputs[0].payload_json["projection"]["newAuthority"], "new111"); + } + } + + #[test] + fn classic_token_authority_and_multisig_create_distinct_admin_outputs() { + let mut authority = observation( + super::SPL_TOKEN_SURFACE, + "set_authority", + false, + serde_json::json!([ + account("authority_target", "mint111"), + account("authority", "current111"), + ]), + serde_json::json!({"authorityType":"mint_tokens","newAuthority":null}), + ); + authority.event.program_id = + kb_model::ProgramId(kb_program_ids::SPL_TOKEN_PROGRAM_ID.to_string()); + let authority_result = kb_materializer_api::EventMaterializer::materialize( + &crate::AdminMaterializer, + &authority, + ); + assert_eq!(authority_result.outputs[0].output_key, "spl_token_authority:set_authority:0"); + assert_eq!( + authority_result.outputs[0].payload_json["projection"]["stateTransition"], + "token_authority_revoked" + ); + + let mut multisig = observation( + super::SPL_TOKEN_SURFACE, + "initialize_multisig2", + false, + serde_json::json!([ + account("multisig_account", "multisig111"), + account("multisig_member", "member1"), + account("multisig_member", "member2"), + ]), + serde_json::json!({"m":2}), + ); + multisig.event.program_id = + kb_model::ProgramId(kb_program_ids::SPL_TOKEN_PROGRAM_ID.to_string()); + let multisig_result = kb_materializer_api::EventMaterializer::materialize( + &crate::AdminMaterializer, + &multisig, + ); + assert_eq!( + multisig_result.outputs[0].output_key, + "spl_token_multisig:initialize_multisig2:0" + ); + assert_eq!( + multisig_result.outputs[0].payload_json["projection"]["members"] + .as_array() + .map(std::vec::Vec::len), + std::option::Option::Some(2) + ); + } + + #[test] + fn token_2022_authority_change_uses_the_admin_owner_once() { + let mut observation = observation( + super::SPL_TOKEN_2022_SURFACE, + "set_authority", + false, + serde_json::json!([ + account("owned_account", "mint111"), + account("current_authority", "authority111"), + ]), + serde_json::json!({"authorityType":"mint_tokens","newAuthority":null}), + ); + observation.event.program_id = + kb_model::ProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string()); + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::AdminMaterializer, + &observation, + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs.len(), 1); + assert_eq!( + result.outputs[0].payload_json["programId"], + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID + ); + } + + #[test] + fn failed_admin_mutation_is_refused() { + let materializer = crate::AdminMaterializer; + let observation = observation( + super::SYSTEM_SURFACE, + "assign", + true, + serde_json::json!([account("assigned_account", "account111",)]), + serde_json::json!({"owner": "owner111"}), + ); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Refused); + assert!(result.outputs.is_empty()); + } + + #[test] + fn ata_instructions_do_not_create_admin_outputs() { + let mut observation = observation( + "spl_associated_token_account", + "create", + false, + serde_json::json!([]), + serde_json::json!({}), + ); + observation.event.program_id = + kb_model::ProgramId(kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID.to_string()); + observation.event.event_family = kb_model::EventFamily::Lifecycle; + assert!(!kb_materializer_api::EventMaterializer::accepts_observation( + &crate::AdminMaterializer, + &observation, + )); + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::AdminMaterializer, + &observation, + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Ignored); + assert!(result.outputs.is_empty()); + } + + #[test] + fn unrelated_admin_observation_is_ignored() { + let materializer = crate::AdminMaterializer; + let observation = observation( + "other_surface", + "set_authority", + false, + serde_json::json!([]), + serde_json::json!({}), + ); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Ignored); + } + + #[test] + fn output_serialization_is_deterministic() { + let materializer = crate::AdminMaterializer; + let observation = observation( + super::CONFIG_SURFACE, + "store", + false, + serde_json::json!([account("config_account", "config111",)]), + serde_json::json!({ + "configuredKeys": [], + "configuredKeyCount": 0, + "configuredSignerCount": 0, + "configDataByteLength": 1, + "configDataSha256": "abc", + "configDataPrefixHex": "01", + }), + ); + let first = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + let second = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + let first_json = match serde_json::to_string(&first) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("first admin serialization failed: {error}"), + }; + let second_json = match serde_json::to_string(&second) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("second admin serialization failed: {error}"), + }; + assert_eq!(first_json, second_json); + } + + #[test] + fn elgamal_registry_create_and_update_have_one_admin_owner() { + let materializer = crate::AdminMaterializer; + for (operation, offset, owner_position, transition) in [ + ("create_registry", 1_i64, 1_u64, "elgamal_registry_created"), + ("update_registry", 0_i64, 2_u64, "elgamal_registry_key_updated"), + ] { + let mut observation = observation( + super::SPL_ELGAMAL_REGISTRY_SURFACE, + operation, + false, + serde_json::json!([ + {"position": 0, "accountKey": "registry111"}, + {"position": 1, "accountKey": "proof111"}, + {"position": 2, "accountKey": "owner111"}, + {"position": 3, "accountKey": "proofCreate111"} + ]), + serde_json::json!({}), + ); + observation.event.program_id = kb_model::ProgramId( + kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID.to_string(), + ); + observation.payload_json = serde_json::json!({ + "accounts": observation.payload_json["accounts"].clone(), + "proofInstructionOffset": offset, + "proofLocationKind": if offset == 0 { + "context_state_account" + } else { + "relative_instruction_offset" + }, + }); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs.len(), 1); + assert_eq!( + result.outputs[0].payload_json["projection"]["registryAccount"], + "registry111" + ); + assert_eq!( + result.outputs[0].payload_json["projection"]["ownerAccount"], + if owner_position == 1 { "proof111" } else { "owner111" } + ); + assert_eq!(result.outputs[0].payload_json["projection"]["stateTransition"], transition); + } + } + + #[test] + fn elgamal_registry_foreign_program_and_failed_transaction_are_refused() { + let materializer = crate::AdminMaterializer; + let mut foreign = observation( + super::SPL_ELGAMAL_REGISTRY_SURFACE, + "create_registry", + false, + serde_json::json!([]), + serde_json::json!({}), + ); + foreign.event.program_id = + kb_model::ProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string()); + assert!(!kb_materializer_api::EventMaterializer::accepts_observation( + &materializer, + &foreign, + )); + let mut failed = foreign.clone(); + failed.event.program_id = kb_model::ProgramId( + kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID.to_string(), + ); + failed.transaction_failed = true; + failed.observation_committed = false; + let result = kb_materializer_api::EventMaterializer::materialize(&materializer, &failed); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Refused); + } + + #[test] + fn token_2022_pointer_and_hook_admin_events_have_one_owner() { + for (operation, field, value) in [ + ("initialize_transfer_hook", "programId", "hook111"), + ("update_metadata_pointer", "metadataAddress", "metadata111"), + ("update_group_pointer", "groupAddress", "group111"), + ("update_group_member_pointer", "memberAddress", "member111"), + ] { + let mut observation = observation( + super::SPL_TOKEN_2022_SURFACE, + operation, + false, + serde_json::json!([{"position":0,"accountKey":"mint111"}]), + serde_json::json!({(field):value,"authority":"authority111"}), + ); + observation.event.event_family = kb_model::EventFamily::TokenAccount; + observation.event.program_id = + kb_model::ProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string()); + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::AdminMaterializer, + &observation, + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs.len(), 1); + assert_eq!(result.outputs[0].payload_json["projection"][field], value); + assert_eq!(result.outputs[0].payload_json["projection"]["stateReadRequired"], true); + } + } + + #[test] + fn token_2022_extension_admin_rejects_foreign_program_and_failed_transaction() { + let mut observation = observation( + super::SPL_TOKEN_2022_SURFACE, + "initialize_metadata_pointer", + false, + serde_json::json!([]), + serde_json::json!({"metadataAddress":"metadata111"}), + ); + observation.event.event_family = kb_model::EventFamily::TokenAccount; + observation.event.program_id = + kb_model::ProgramId(kb_program_ids::SPL_TOKEN_PROGRAM_ID.to_string()); + assert!(!kb_materializer_api::EventMaterializer::accepts_observation( + &crate::AdminMaterializer, + &observation, + )); + observation.event.program_id = + kb_model::ProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string()); + observation.transaction_failed = true; + observation.observation_committed = false; + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::AdminMaterializer, + &observation, + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Refused); + } +} + +/// Materialize authoritative Token-2022 administrative extension snapshots. +pub fn materialize_token_2022_admin_state_snapshots( + account_key: &str, + slot: u64, + state: &kb_decoder_spl_token_2022::state::Token2022State, +) -> std::result::Result, String> { + if account_key.is_empty() { + return std::result::Result::Err( + "Token-2022 admin state snapshot requires a non-empty account key".to_string(), + ); + } + if state.kind != kb_decoder_spl_token_2022::state::Token2022StateKind::Mint { + for entry in &state.extensions { + if is_token_2022_admin_mint_extension(entry.extension_name) { + return std::result::Result::Err(format!( + "Token-2022 {} extension requires mint state", + entry.extension_name + )); + } + } + return std::result::Result::Ok(std::vec::Vec::new()); + } + let mut outputs = std::vec::Vec::new(); + for entry in &state.extensions { + if !is_token_2022_admin_mint_extension(entry.extension_name) { + continue; + } + let output_key = + format!("token_2022_admin_state:{}:{account_key}:{slot}", entry.extension_name); + outputs.push(kb_materializer_api::MaterializedOutput { + output_key: output_key.clone(), + family: kb_model::MaterializedEventFamily::Admin, + payload_json: serde_json::json!({ + "projectionVersion": 1, + "projectionSemantics": "authoritative_bounded_admin_state_snapshot", + "domain": "token_2022_extension_admin_state", + "idempotenceKey": output_key, + "accountKey": account_key, + "slot": slot, + "programId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + "stateKind": "mint", + "extensionType": entry.extension_type, + "extensionName": entry.extension_name, + "valueHex": entry.value_hex, + "valueFields": entry.value_fields, + "finalAccountStateCaptured": true, + "provenance": { + "processorName": crate::PROCESSOR_NAME, + "processorVersion": env!("CARGO_PKG_VERSION"), + "source": "exact_token_2022_tlv_state_parser" + } + }), + }); + } + return std::result::Result::Ok(outputs); +} + +fn is_token_2022_admin_mint_extension(extension_name: &str) -> bool { + return matches!( + extension_name, + "mint_close_authority" + | "default_account_state" + | "transfer_hook" + | "permanent_delegate" + | "metadata_pointer" + | "group_pointer" + | "group_member_pointer" + | "interest_bearing_config" + | "scaled_ui_amount" + | "pausable" + | "permissioned_burn" + ); +} + +#[cfg(test)] +mod token_2022_admin_state_tests { + fn mint_state() -> kb_decoder_spl_token_2022::state::Token2022State { + return kb_decoder_spl_token_2022::state::Token2022State { + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Mint, + base_fields: serde_json::json!({}), + base_hex: String::new(), + account_type: std::option::Option::Some(1), + extensions: vec![ + kb_decoder_spl_token_2022::state::Token2022TlvEntry { + extension_type: 6, + extension_name: "default_account_state", + value_hex: "02".to_string(), + value_fields: serde_json::json!({"state": 2}), + }, + kb_decoder_spl_token_2022::state::Token2022TlvEntry { + extension_type: 26, + extension_name: "pausable", + value_hex: "00".repeat(33), + value_fields: serde_json::json!({"paused": false}), + }, + kb_decoder_spl_token_2022::state::Token2022TlvEntry { + extension_type: 1, + extension_name: "transfer_fee_config", + value_hex: "00".to_string(), + value_fields: serde_json::json!({}), + }, + ], + }; + } + + #[test] + fn admin_snapshots_are_owned_once_filtered_and_idempotent() { + let state = mint_state(); + let first = crate::materialize_token_2022_admin_state_snapshots("mint111", 44, &state); + let second = crate::materialize_token_2022_admin_state_snapshots("mint111", 44, &state); + assert_eq!(first, second); + assert_eq!(first.as_ref().map(std::vec::Vec::len), std::result::Result::Ok(2)); + assert_eq!( + first.as_ref().map(|outputs| { + return outputs + .iter() + .map(|output| return output.payload_json["extensionName"].clone()) + .collect::>(); + }), + std::result::Result::Ok(vec![ + serde_json::json!("default_account_state"), + serde_json::json!("pausable"), + ]) + ); + } + + #[test] + fn admin_snapshots_reject_empty_identity_and_wrong_state_kind() { + let state = mint_state(); + assert!(crate::materialize_token_2022_admin_state_snapshots("", 44, &state).is_err()); + let account_state = kb_decoder_spl_token_2022::state::Token2022State { + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Account, + base_fields: serde_json::json!({}), + base_hex: String::new(), + account_type: std::option::Option::Some(2), + extensions: state.extensions[..1].to_vec(), + }; + assert!( + crate::materialize_token_2022_admin_state_snapshots("account111", 44, &account_state,) + .is_err() + ); + } +} + +/// Materialize one authoritative SPL ElGamal registry account snapshot. +pub fn materialize_elgamal_registry_state_snapshot( + registry_account_key: &str, + slot: u64, + state: &kb_decoder_spl_elgamal_registry::state::ElGamalRegistryState, +) -> std::result::Result { + if registry_account_key.is_empty() { + return std::result::Result::Err( + "SPL ElGamal registry snapshot requires a non-empty account key".to_string(), + ); + } + return std::result::Result::Ok(kb_materializer_api::MaterializedOutput { + output_key: format!("spl_elgamal_registry_state:{registry_account_key}:{slot}"), + family: kb_model::MaterializedEventFamily::Admin, + payload_json: serde_json::json!({ + "projectionVersion": 1, + "domain": "spl_elgamal_registry_account_state", + "projectionSemantics": "authoritative_exact_account_snapshot", + "idempotenceKey": format!("spl_elgamal_registry:{registry_account_key}:{slot}"), + "registryAccountKey": registry_account_key, + "slot": slot, + "programId": kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID, + "owner": state.owner, + "elgamalPubkeyBase64": state.elgamal_pubkey_base64, + "wireHex": state.wire_hex, + "elgamalKeyValidityProvenByStateParser": false, + "confidentialValuesDecrypted": false, + "finalAccountStateCaptured": true, + "provenance": { + "processorName": crate::PROCESSOR_NAME, + "processorVersion": env!("CARGO_PKG_VERSION"), + "source": "exact_spl_elgamal_registry_state_parser" + } + }), + }); +} + +#[cfg(test)] +mod elgamal_registry_state_tests { + #[test] + fn exact_registry_snapshot_preserves_public_state_without_claiming_proof_validation() { + let state = kb_decoder_spl_elgamal_registry::state::ElGamalRegistryState { + owner: "owner111".to_string(), + elgamal_pubkey_base64: "AQID".to_string(), + wire_hex: "00".repeat(64), + }; + let output = crate::materialize_elgamal_registry_state_snapshot("registry111", 11, &state); + assert_eq!( + output.as_ref().map(|value| return value.payload_json["owner"].clone()), + std::result::Result::Ok(serde_json::json!("owner111")) + ); + assert_eq!( + output + .as_ref() + .map(|value| return value.payload_json["elgamalKeyValidityProvenByStateParser"] + .clone()), + std::result::Result::Ok(serde_json::json!(false)) + ); + assert_eq!( + output + .as_ref() + .map(|value| return value.payload_json["confidentialValuesDecrypted"].clone()), + std::result::Result::Ok(serde_json::json!(false)) + ); + } + + #[test] + fn registry_snapshot_is_idempotent_and_requires_account_identity() { + let state = kb_decoder_spl_elgamal_registry::state::ElGamalRegistryState { + owner: "owner111".to_string(), + elgamal_pubkey_base64: "AQID".to_string(), + wire_hex: "00".repeat(64), + }; + let first = crate::materialize_elgamal_registry_state_snapshot("registry111", 11, &state); + let second = crate::materialize_elgamal_registry_state_snapshot("registry111", 11, &state); + assert_eq!(first, second); + assert!(crate::materialize_elgamal_registry_state_snapshot("", 11, &state).is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_api/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_api/Cargo.toml new file mode 100644 index 0000000..2b57032 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_api/Cargo.toml @@ -0,0 +1,19 @@ +# file: kb_materializer_api/Cargo.toml +# version: 2 + +[package] +name = "kb_materializer_api" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_model = { path = "../kb_model" } +serde.workspace = true +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_api/README.md b/migration/khadhroony-bot2-reference/kb_materializer_api/README.md new file mode 100644 index 0000000..5ef5776 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_api/README.md @@ -0,0 +1,25 @@ + + + +# kb_materializer_api + +Ce crate déclare l'interface commune des matérialisateurs métier. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. + +## Contrats `0.4.0` + +Le trait `EventMaterializer` consomme des observations décodées stables et déclare les familles acceptées, l’applicabilité exacte par observation ainsi que la politique applicable aux transactions réussies ou échouées. Les sorties possèdent des clés idempotentes et un statut explicite `inserted`, `replaced`, `ignored`, `refused` ou `failed`. + +La garde commune refuse toute matérialisation de trade, liquidité ou lifecycle supposée réussie depuis une observation échouée ou non commitée. + +À partir de `0.4.1-pre.005`, le pipeline filtre une observation par surface et entrée avant le ledger et la politique. Deux événements de même famille ne sont donc plus automatiquement proposés au même matérialiseur. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_api/src/contracts.rs b/migration/khadhroony-bot2-reference/kb_materializer_api/src/contracts.rs new file mode 100644 index 0000000..352e3de --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_api/src/contracts.rs @@ -0,0 +1,416 @@ +// file: kb_materializer_api/src/contracts.rs +// version: 7 + +//! Backend-neutral decoded observation materialization contracts. + +/// Stable identity of one decoded event materializer. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct MaterializerIdentity { + /// Stable lower snake case processor name. + pub name: std::string::String, + /// Semantic or deterministic implementation version. + pub version: std::string::String, +} + +impl MaterializerIdentity { + /// Builds a validated materializer identity. + pub fn new( + name: impl std::convert::Into, + version: impl std::convert::Into, + ) -> kb_core::Result { + let value = Self { + name: name.into(), + version: version.into(), + }; + if value.name.trim().is_empty() || value.version.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "materializer identity fields must not be empty", + )); + } + return std::result::Result::Ok(value); + } +} + +/// Explicit policy applied to successful and failed source transactions. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum MaterializationTransactionPolicy { + /// Accept committed observations from successful transactions only. + SuccessfulCommittedOnly, + /// Accept successful observations and failed audit-only observations. + SuccessfulOrFailedAudit, + /// Accept failed observations for a specifically declared non-mutating family. + ExplicitFailedObservation, +} + +/// Terminal materializer result status. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum MaterializerOutcomeStatus { + /// One or more new stable outputs were inserted. + Inserted, + /// One or more processor-owned outputs were deterministically replaced. + Replaced, + /// The decoded observation was intentionally ignored. + Ignored, + /// The decoded observation was rejected by the declared policy. + Refused, + /// Materialization failed and diagnostics were produced. + Failed, +} + +/// Structured materializer diagnostic without generic error wrappers. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct MaterializerDiagnostic { + /// Stable machine-readable diagnostic code. + pub code: std::string::String, + /// Human-readable diagnostic message. + pub message: std::string::String, + /// Whether retrying the same version and input may succeed. + pub retriable: bool, +} + +impl MaterializerDiagnostic { + /// Builds a validated materializer diagnostic. + pub fn new( + code: impl std::convert::Into, + message: impl std::convert::Into, + retriable: bool, + ) -> kb_core::Result { + let value = Self { + code: code.into(), + message: message.into(), + retriable, + }; + if value.code.trim().is_empty() || value.message.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "materializer diagnostic code and message must not be empty", + )); + } + return std::result::Result::Ok(value); + } +} + +/// Stable processor-owned business output derived from one decoded observation. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct MaterializedOutput { + /// Stable processor-owned output key within one decoded observation. + pub output_key: std::string::String, + /// Business output family. + pub family: kb_model::MaterializedEventFamily, + /// Typed business payload serialized as deterministic JSON. + pub payload_json: serde_json::Value, +} + +impl MaterializedOutput { + /// Validates stable output identity. + pub fn validate(&self) -> kb_core::Result<()> { + if self.output_key.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "materialized output key must not be empty", + )); + } + return std::result::Result::Ok(()); + } +} + +/// Complete explicit result produced by one materializer invocation. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct MaterializerExecutionResult { + /// Terminal materializer status. + pub status: MaterializerOutcomeStatus, + /// Stable business outputs. + pub outputs: std::vec::Vec, + /// Structured diagnostics. + pub diagnostics: std::vec::Vec, +} + +impl MaterializerExecutionResult { + /// Builds a policy refusal result. + pub fn refused(code: &str, message: &str) -> Self { + return Self { + status: MaterializerOutcomeStatus::Refused, + outputs: std::vec::Vec::new(), + diagnostics: std::vec![MaterializerDiagnostic { + code: code.to_string(), + message: message.to_string(), + retriable: false, + }], + }; + } + + /// Builds an ignored result. + pub fn ignored() -> Self { + return Self { + status: MaterializerOutcomeStatus::Ignored, + outputs: std::vec::Vec::new(), + diagnostics: std::vec::Vec::new(), + }; + } + + /// Validates status and output consistency. + pub fn validate(&self) -> kb_core::Result<()> { + let output_status = self.status == MaterializerOutcomeStatus::Inserted + || self.status == MaterializerOutcomeStatus::Replaced; + if output_status && self.outputs.is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "inserted or replaced materializer status requires outputs", + )); + } + if !output_status && !self.outputs.is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "ignored, refused or failed materializer status must not contain outputs", + )); + } + if self.status == MaterializerOutcomeStatus::Failed && self.diagnostics.is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "failed materializer status requires at least one diagnostic", + )); + } + for output in &self.outputs { + let validation_result = output.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + } + return std::result::Result::Ok(()); + } +} + +/// Stable decoded observation materializer contract used by the common pipeline. +pub trait EventMaterializer: std::marker::Send + std::marker::Sync { + /// Returns the stable materializer identity. + fn identity(&self) -> MaterializerIdentity; + /// Returns every decoded event family explicitly accepted by this materializer. + fn accepted_families(&self) -> &'static [kb_model::EventFamily]; + /// Returns whether this materializer accepts one exact decoded observation. + fn accepts_observation(&self, observation: &kb_decoder_api::DecodedObservation) -> bool { + let family = observation.event.event_family; + return self.accepted_families().iter().any(|accepted| return *accepted == family); + } + /// Returns the failed/successful transaction policy for one accepted family. + fn transaction_policy(&self, family: kb_model::EventFamily) + -> MaterializationTransactionPolicy; + /// Materializes one validated decoded observation. + fn materialize( + &self, + observation: &kb_decoder_api::DecodedObservation, + ) -> MaterializerExecutionResult; +} + +/// Returns true when a materializer explicitly accepts one decoded family. +pub fn materializer_accepts_family( + materializer: &dyn EventMaterializer, + family: kb_model::EventFamily, +) -> bool { + return materializer + .accepted_families() + .iter() + .any(|accepted| return *accepted == family); +} + +/// Returns true when a materializer accepts one exact decoded observation. +pub fn materializer_accepts_observation( + materializer: &dyn EventMaterializer, + observation: &kb_decoder_api::DecodedObservation, +) -> bool { + return materializer.accepts_observation(observation); +} + +/// Applies the mandatory source transaction policy before materialization. +pub fn validate_materialization_policy( + materializer: &dyn EventMaterializer, + observation: &kb_decoder_api::DecodedObservation, +) -> std::result::Result<(), MaterializerExecutionResult> { + let family = observation.event.event_family; + if !crate::materializer_accepts_observation(materializer, observation) { + return std::result::Result::Err(MaterializerExecutionResult::refused( + "unsupported_decoded_observation", + "materializer does not accept the decoded observation surface and entry", + )); + } + let policy = materializer.transaction_policy(family); + if observation.transaction_failed || !observation.observation_committed { + if family == kb_model::EventFamily::Trade + || family == kb_model::EventFamily::Liquidity + || family == kb_model::EventFamily::Lifecycle + { + return std::result::Result::Err(MaterializerExecutionResult::refused( + "failed_transaction_mutation_refused", + "failed or uncommitted observations cannot create successful mutable business outputs", + )); + } + if policy == MaterializationTransactionPolicy::SuccessfulCommittedOnly { + return std::result::Result::Err(MaterializerExecutionResult::refused( + "failed_transaction_policy_refused", + "materializer accepts committed observations from successful transactions only", + )); + } + if policy == MaterializationTransactionPolicy::SuccessfulOrFailedAudit + && family != kb_model::EventFamily::Audit + && family != kb_model::EventFamily::ComplianceAudit + && family != kb_model::EventFamily::Risk + { + return std::result::Result::Err(MaterializerExecutionResult::refused( + "failed_transaction_non_audit_refused", + "failed transaction materialization is limited to declared audit or risk families", + )); + } + } + return std::result::Result::Ok(()); +} + +/// Rejects successful mutable business outputs derived from failed or uncommitted observations. +pub fn validate_materialized_output_policy( + observation: &kb_decoder_api::DecodedObservation, + result: &MaterializerExecutionResult, +) -> std::result::Result<(), MaterializerExecutionResult> { + if !observation.transaction_failed && observation.observation_committed { + return std::result::Result::Ok(()); + } + let forbidden_output = result.outputs.iter().any(|output| { + return output.family != kb_model::MaterializedEventFamily::ComplianceAudit + && output.family != kb_model::MaterializedEventFamily::TokenMetadataRisk + && output.family != kb_model::MaterializedEventFamily::Risk; + }); + if forbidden_output { + return std::result::Result::Err(MaterializerExecutionResult::refused( + "failed_transaction_output_refused", + "failed or uncommitted observations may only produce audit or risk outputs", + )); + } + return std::result::Result::Ok(()); +} + +#[cfg(test)] +mod tests { + struct TradeMaterializer; + + impl crate::EventMaterializer for TradeMaterializer { + fn identity(&self) -> crate::MaterializerIdentity { + return crate::MaterializerIdentity { + name: "trade_materializer".to_string(), + version: "1".to_string(), + }; + } + + fn accepted_families(&self) -> &'static [kb_model::EventFamily] { + return &[kb_model::EventFamily::Trade]; + } + + fn transaction_policy( + &self, + _family: kb_model::EventFamily, + ) -> crate::MaterializationTransactionPolicy { + return crate::MaterializationTransactionPolicy::SuccessfulCommittedOnly; + } + + fn materialize( + &self, + _observation: &kb_decoder_api::DecodedObservation, + ) -> crate::MaterializerExecutionResult { + return crate::MaterializerExecutionResult::ignored(); + } + } + + fn failed_trade_observation() -> kb_decoder_api::DecodedObservation { + return kb_decoder_api::DecodedObservation { + event_key: "trade".to_string(), + event: kb_model::DecodedProtocolEvent { + signature: kb_model::Signature("signature".to_string()), + slot: kb_model::Slot(42), + instruction_path: kb_model::InstructionPath("0".to_string()), + program_id: kb_model::ProgramId("program".to_string()), + protocol_code: kb_model::ProtocolCode("protocol".to_string()), + surface_code: kb_model::SurfaceCode("surface".to_string()), + event_code: kb_model::EventCode("trade".to_string()), + event_name: kb_model::EventName("trade".to_string()), + event_family: kb_model::EventFamily::Trade, + source_kind: kb_model::EventSourceKind::Instruction, + confidence: kb_model::DecoderConfidence::Exact, + }, + payload_json: serde_json::json!({}), + transaction_failed: true, + transaction_error: std::option::Option::Some( + serde_json::json!({"InstructionError": [0, "Custom"]}), + ), + observation_committed: false, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::ExactLayout, + confidence: kb_model::DecoderConfidence::Exact, + evidence: std::vec!["layout".to_string()], + }, + }; + } + + #[test] + fn failed_trade_is_refused_before_materialization() { + let result = + crate::validate_materialization_policy(&TradeMaterializer, &failed_trade_observation()); + assert!(result.is_err()); + let refusal = match result { + std::result::Result::Ok(()) => panic!("failed trade unexpectedly accepted"), + std::result::Result::Err(value) => value, + }; + assert_eq!(refusal.status, crate::MaterializerOutcomeStatus::Refused); + assert_eq!(refusal.diagnostics[0].code, "failed_transaction_mutation_refused"); + } + + struct FailedAuditToTradeMaterializer; + + impl crate::EventMaterializer for FailedAuditToTradeMaterializer { + fn identity(&self) -> crate::MaterializerIdentity { + return crate::MaterializerIdentity { + name: "failed_audit_to_trade".to_string(), + version: "1".to_string(), + }; + } + + fn accepted_families(&self) -> &'static [kb_model::EventFamily] { + return &[kb_model::EventFamily::Audit]; + } + + fn transaction_policy( + &self, + _family: kb_model::EventFamily, + ) -> crate::MaterializationTransactionPolicy { + return crate::MaterializationTransactionPolicy::SuccessfulOrFailedAudit; + } + + fn materialize( + &self, + _observation: &kb_decoder_api::DecodedObservation, + ) -> crate::MaterializerExecutionResult { + return crate::MaterializerExecutionResult { + status: crate::MaterializerOutcomeStatus::Inserted, + outputs: std::vec![crate::MaterializedOutput { + output_key: "trade".to_string(), + family: kb_model::MaterializedEventFamily::Trade, + payload_json: serde_json::json!({}), + }], + diagnostics: std::vec::Vec::new(), + }; + } + } + + fn failed_audit_observation() -> kb_decoder_api::DecodedObservation { + let mut observation = failed_trade_observation(); + observation.event.event_family = kb_model::EventFamily::Audit; + return observation; + } + + #[test] + fn failed_audit_cannot_emit_successful_trade_output() { + let materializer = FailedAuditToTradeMaterializer; + let observation = failed_audit_observation(); + let policy_result = crate::validate_materialization_policy(&materializer, &observation); + assert!(policy_result.is_ok()); + let materialized = crate::EventMaterializer::materialize(&materializer, &observation); + let output_result = crate::validate_materialized_output_policy(&observation, &materialized); + assert!(output_result.is_err()); + let refusal = match output_result { + std::result::Result::Ok(()) => panic!("failed audit unexpectedly emitted a trade"), + std::result::Result::Err(value) => value, + }; + assert_eq!(refusal.status, crate::MaterializerOutcomeStatus::Refused); + assert_eq!(refusal.diagnostics[0].code, "failed_transaction_output_refused"); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_api/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_api/src/lib.rs new file mode 100644 index 0000000..31a3bba --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_api/src/lib.rs @@ -0,0 +1,35 @@ +// file: kb_materializer_api/src/lib.rs +// version: 4 + +//! Materializer API shared by business materializer crates. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod contracts; +mod materializer; + +/// Stable decoded observation materializer contract used by the common pipeline. +pub use crate::contracts::EventMaterializer; +/// Explicit policy applied to successful and failed source transactions. +pub use crate::contracts::MaterializationTransactionPolicy; +/// Stable processor-owned business output derived from one decoded observation. +pub use crate::contracts::MaterializedOutput; +/// Structured materializer diagnostic without generic error wrappers. +pub use crate::contracts::MaterializerDiagnostic; +/// Complete explicit result produced by one materializer invocation. +pub use crate::contracts::MaterializerExecutionResult; +/// Stable identity of one decoded event materializer. +pub use crate::contracts::MaterializerIdentity; +/// Terminal materializer result status. +pub use crate::contracts::MaterializerOutcomeStatus; +/// Returns true when a materializer explicitly accepts one decoded family. +pub use crate::contracts::materializer_accepts_family; +/// Returns true when a materializer accepts one exact decoded observation. +pub use crate::contracts::materializer_accepts_observation; +/// Applies the mandatory source transaction policy before materialization. +pub use crate::contracts::validate_materialization_policy; +/// Rejects mutable business outputs derived from failed or uncommitted observations. +pub use crate::contracts::validate_materialized_output_policy; +/// Exposes the legacy common business materializer trait. +pub use crate::materializer::Materializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_api/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_api/src/materializer.rs new file mode 100644 index 0000000..ae50ef2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_api/src/materializer.rs @@ -0,0 +1,22 @@ +// file: kb_materializer_api/src/materializer.rs +// version: 2 + +//! Materializer API contract used by business materializer crates. + +/// Common contract implemented by all business materializer crates. +pub trait Materializer { + /// Returns the stable materializer name. + fn materializer_name(&self) -> &'static str; + + /// Returns the materializer version. + fn materializer_version(&self) -> &'static str; + + /// Tests whether this materializer accepts a decoded event. + fn accepts_event(&self, event: &kb_model::DecodedProtocolEvent) -> bool; + + /// Materializes a decoded event into zero or more business events. + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result>; +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_bridge/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_bridge/Cargo.toml new file mode 100644 index 0000000..9362652 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_bridge/Cargo.toml @@ -0,0 +1,17 @@ +# file: kb_materializer_bridge/Cargo.toml +# version: 2 + +[package] +name = "kb_materializer_bridge" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_bridge/README.md b/migration/khadhroony-bot2-reference/kb_materializer_bridge/README.md new file mode 100644 index 0000000..e29f17c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_bridge/README.md @@ -0,0 +1,14 @@ + + + +# kb_materializer_bridge + +Ce crate matérialise les événements décodés de la famille `bridge` dans les tables métier correspondantes. + +## Rôle dans l'écosystème + +Ce module travaille sur les événements décodés et produit des projections métier. Il ne doit pas accéder directement au RPC, au wallet, à Tauri ou aux décodeurs spécifiques. + +## État initial + +Le crate est créé comme point d'ancrage. Les règles de promotion métier, les tables PostgreSQL et les contrôles SQL seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_bridge/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_bridge/src/lib.rs new file mode 100644 index 0000000..c9176e1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_bridge/src/lib.rs @@ -0,0 +1,12 @@ +// file: kb_materializer_bridge/src/lib.rs +// version: 1 + +//! Materializer crate for `bridge`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod materializer; + +/// Exposes the materializer type implemented by this crate. +pub use crate::materializer::BridgeMaterializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_bridge/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_bridge/src/materializer.rs new file mode 100644 index 0000000..a3258ca --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_bridge/src/materializer.rs @@ -0,0 +1,35 @@ +// file: kb_materializer_bridge/src/materializer.rs +// version: 5 + +//! Materializer scaffold for the `kb_materializer_bridge` crate. + +/// Initial materializer implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct BridgeMaterializer; + +impl kb_materializer_api::Materializer for crate::BridgeMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_bridge"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, _event: &kb_model::DecodedProtocolEvent) -> bool { + return false; + } + + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + let materialized = kb_model::MaterializedEvent { + signature: event.signature.clone(), + slot: event.slot, + decoded_family: event.event_family, + materialized_family: kb_model::MaterializedEventFamily::Bridge, + }; + return std::result::Result::Ok(std::vec![materialized]); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/Cargo.toml new file mode 100644 index 0000000..4424d96 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/Cargo.toml @@ -0,0 +1,20 @@ +# file: kb_materializer_compliance_audit/Cargo.toml +# version: 3 + +[package] +name = "kb_materializer_compliance_audit" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } +serde_json.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/README.md b/migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/README.md new file mode 100644 index 0000000..9a256e9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/README.md @@ -0,0 +1,19 @@ + + + +# kb_materializer_compliance_audit + +Ce crate matérialise les mutations techniques commitées qui doivent rester auditables sans recopier leurs payloads complets. + +## Projections natives actives + +`program_loader_code:::0` couvre : + +- `write` pour BPF Loader deprecated, BPF Loader, BPF Loader upgradeable et Loader v4 ; +- `copy` pour Loader v4. + +Les sorties conservent les comptes cibles, autorités, offsets, longueurs, SHA-256 et préfixes hexadécimaux bornés disponibles. Le bytecode complet n'est jamais recopié dans `kb_sol_mat_events`. + +## Politique + +La famille matérialisée est `ComplianceAudit`. Seules les observations réussies et commitées sont projetées, car la sortie décrit une mutation de code effectivement appliquée. Les tentatives échouées restent disponibles dans les observations décodées, sans être présentées comme des écritures commitées. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/src/constants.rs b/migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/src/constants.rs new file mode 100644 index 0000000..4c61acd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_materializer_compliance_audit/src/constants.rs +// version: 1 + +//! Local constants for the `kb_materializer_compliance_audit` crate. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_materializer_compliance_audit"; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/src/lib.rs new file mode 100644 index 0000000..83ca09b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_materializer_compliance_audit/src/lib.rs +// version: 4 + +//! Materializer crate for stable compliance-audit projections. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod materializer; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Stable compliance-audit materializer for committed native bytecode mutations. +pub use crate::materializer::ComplianceAuditMaterializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/src/materializer.rs new file mode 100644 index 0000000..c119169 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_compliance_audit/src/materializer.rs @@ -0,0 +1,588 @@ +// file: kb_materializer_compliance_audit/src/materializer.rs +// version: 7 + +//! Stable compliance-audit projections for committed native bytecode mutations and execution profiles. + +const ACCEPTED_FAMILIES: &[kb_model::EventFamily] = &[kb_model::EventFamily::Audit]; +const COMPUTE_BUDGET_SURFACE: &str = "solana_native_compute_budget"; +const COMPUTE_BUDGET_ENTRIES: &[&str] = &[ + "request_units_deprecated", + "request_heap_frame", + "set_compute_unit_limit", + "set_compute_unit_price", + "set_loaded_accounts_data_size_limit", +]; +const BPF_LOADER_DEPRECATED_SURFACE: &str = "solana_native_bpf_loader_deprecated"; +const BPF_LOADER_SURFACE: &str = "solana_native_bpf_loader"; +const BPF_LOADER_UPGRADEABLE_SURFACE: &str = "solana_native_bpf_loader_upgradeable"; +const LOADER_V4_SURFACE: &str = "solana_native_loader_v4"; +const WRITE_ENTRIES: &[&str] = &["write"]; +const LOADER_V4_COPY_ENTRIES: &[&str] = &["copy"]; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum ProjectionKind { + ProgramByteWrite(&'static str), + ProgramByteCopy, + ComputeBudgetProfile, +} + +impl ProjectionKind { + fn output_key(self, surface_code: &str, operation: &str) -> std::string::String { + return match self { + Self::ProgramByteWrite(_) | Self::ProgramByteCopy => { + format!("program_loader_code:{surface_code}:{operation}:0") + }, + Self::ComputeBudgetProfile => "transaction_compute_budget_profile:0".to_string(), + }; + } + + fn loader_surface(self) -> &'static str { + return match self { + Self::ProgramByteWrite(surface) => surface, + Self::ProgramByteCopy => LOADER_V4_SURFACE, + Self::ComputeBudgetProfile => COMPUTE_BUDGET_SURFACE, + }; + } +} + +/// Stable compliance-audit materializer for native bytecode mutations and execution profiles. +#[derive(Clone, Debug, Default)] +pub struct ComplianceAuditMaterializer; + +impl kb_materializer_api::Materializer for crate::ComplianceAuditMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_compliance_audit"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, event: &kb_model::DecodedProtocolEvent) -> bool { + return event.event_family == kb_model::EventFamily::Audit + && projection(event.surface_code.0.as_str(), event.event_name.0.as_str()).is_some(); + } + + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + if !kb_materializer_api::Materializer::accepts_event(self, event) { + return std::result::Result::Ok(std::vec::Vec::new()); + } + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +impl kb_materializer_api::EventMaterializer for crate::ComplianceAuditMaterializer { + fn identity(&self) -> kb_materializer_api::MaterializerIdentity { + return kb_materializer_api::MaterializerIdentity { + name: "solana_native_compliance_audit".to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + }; + } + + fn accepted_families(&self) -> &'static [kb_model::EventFamily] { + return ACCEPTED_FAMILIES; + } + + fn accepts_observation(&self, observation: &kb_decoder_api::DecodedObservation) -> bool { + return observation.event.event_family == kb_model::EventFamily::Audit + && projection( + observation.event.surface_code.0.as_str(), + observation.event.event_name.0.as_str(), + ) + .is_some(); + } + + fn transaction_policy( + &self, + _family: kb_model::EventFamily, + ) -> kb_materializer_api::MaterializationTransactionPolicy { + return kb_materializer_api::MaterializationTransactionPolicy::SuccessfulOrFailedAudit; + } + + fn materialize( + &self, + observation: &kb_decoder_api::DecodedObservation, + ) -> kb_materializer_api::MaterializerExecutionResult { + let projection = match projection( + observation.event.surface_code.0.as_str(), + observation.event.event_name.0.as_str(), + ) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + tracing::debug!( + target: crate::TRACING_TARGET, + action = "materialize_compliance_audit", + surface_code = %observation.event.surface_code.0.as_str(), + entry_code = %observation.event.event_name.0.as_str(), + accepted = false, + "ignore observation outside supported native compliance-audit projections" + ); + return kb_materializer_api::MaterializerExecutionResult::ignored(); + }, + }; + if observation.event.event_family != kb_model::EventFamily::Audit { + return kb_materializer_api::MaterializerExecutionResult::ignored(); + } + if projection != ProjectionKind::ComputeBudgetProfile + && (observation.transaction_failed || !observation.observation_committed) + { + tracing::debug!( + target: crate::TRACING_TARGET, + action = "materialize_compliance_audit", + surface_code = %observation.event.surface_code.0.as_str(), + entry_code = %observation.event.event_name.0.as_str(), + committed = observation.observation_committed, + transaction_failed = observation.transaction_failed, + "refuse uncommitted native bytecode mutation observation" + ); + return kb_materializer_api::MaterializerExecutionResult::refused( + "failed_transaction_compliance_audit_refused", + "failed or uncommitted native observations cannot create committed bytecode mutation outputs", + ); + } + let accounts = observation + .payload_json + .get("accounts") + .cloned() + .unwrap_or(serde_json::Value::Null); + let parameters = observation + .payload_json + .get("parameters") + .cloned() + .unwrap_or(serde_json::Value::Null); + let operation = observation.event.event_name.0.clone(); + if projection == ProjectionKind::ComputeBudgetProfile { + return materialize_compute_budget_profile(observation, parameters, operation); + } + let output = kb_materializer_api::MaterializedOutput { + output_key: projection + .output_key(observation.event.surface_code.0.as_str(), operation.as_str()), + family: kb_model::MaterializedEventFamily::ComplianceAudit, + payload_json: serde_json::json!({ + "projectionVersion": 1, + "projectionSemantics": "committed_instruction_compliance_audit_event", + "domain": "program_loader_code_mutation", + "loaderSurface": projection.loader_surface(), + "operation": operation, + "programId": observation.event.program_id.0.clone(), + "signature": observation.event.signature.0.clone(), + "slot": observation.event.slot.0, + "instructionPath": observation.event.instruction_path.0.clone(), + "transactionSucceeded": true, + "accounts": accounts, + "parameters": parameters, + "projection": projection_details(projection, &accounts, ¶meters), + }), + }; + tracing::debug!( + target: crate::TRACING_TARGET, + action = "materialize_compliance_audit", + surface_code = %observation.event.surface_code.0.as_str(), + entry_code = %observation.event.event_name.0.as_str(), + loader_surface = projection.loader_surface(), + output_count = 1_usize, + "materialize committed native bytecode mutation observation" + ); + return kb_materializer_api::MaterializerExecutionResult { + status: kb_materializer_api::MaterializerOutcomeStatus::Inserted, + outputs: std::vec![output], + diagnostics: std::vec::Vec::new(), + }; + } +} + +fn materialize_compute_budget_profile( + observation: &kb_decoder_api::DecodedObservation, + parameters: serde_json::Value, + operation: std::string::String, +) -> kb_materializer_api::MaterializerExecutionResult { + let profile = parameters.get("transactionProfile").cloned().unwrap_or(serde_json::Value::Null); + if !compute_budget_profile_emits(&profile) { + return kb_materializer_api::MaterializerExecutionResult::ignored(); + } + let output = kb_materializer_api::MaterializedOutput { + output_key: "transaction_compute_budget_profile:0".to_string(), + family: kb_model::MaterializedEventFamily::ComplianceAudit, + payload_json: serde_json::json!({ + "projectionVersion": 1, + "projectionSemantics": "transaction_compute_budget_profile", + "domain": "transaction_compute_budget_profile", + "operation": operation, + "programId": observation.event.program_id.0.clone(), + "signature": observation.event.signature.0.clone(), + "slot": observation.event.slot.0, + "instructionPath": observation.event.instruction_path.0.clone(), + "transactionSucceeded": !observation.transaction_failed, + "profile": profile, + "profileSource": { + "surfaceCode": observation.event.surface_code.0.clone(), + "entryCode": observation.event.event_name.0.clone(), + "eventKey": observation.event_key.clone() + }, + }), + }; + tracing::debug!( + target: crate::TRACING_TARGET, + action = "materialize_compute_budget_profile", + surface_code = %observation.event.surface_code.0.as_str(), + entry_code = %observation.event.event_name.0.as_str(), + output_count = 1_usize, + transaction_failed = observation.transaction_failed, + "materialize native Compute Budget transaction profile" + ); + return kb_materializer_api::MaterializerExecutionResult { + status: kb_materializer_api::MaterializerOutcomeStatus::Inserted, + outputs: std::vec![output], + diagnostics: std::vec::Vec::new(), + }; +} + +fn compute_budget_profile_emits(profile: &serde_json::Value) -> bool { + return profile + .get("currentInstructionIsProfileEmitter") + .and_then(serde_json::Value::as_bool) + == std::option::Option::Some(true); +} + +fn projection(surface_code: &str, entry_code: &str) -> std::option::Option { + if surface_code == COMPUTE_BUDGET_SURFACE && contains(COMPUTE_BUDGET_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::ComputeBudgetProfile); + } + if contains(WRITE_ENTRIES, entry_code) { + for supported in [ + BPF_LOADER_DEPRECATED_SURFACE, + BPF_LOADER_SURFACE, + BPF_LOADER_UPGRADEABLE_SURFACE, + LOADER_V4_SURFACE, + ] { + if surface_code == supported { + return std::option::Option::Some(ProjectionKind::ProgramByteWrite(supported)); + } + } + } + if surface_code == LOADER_V4_SURFACE && contains(LOADER_V4_COPY_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::ProgramByteCopy); + } + return std::option::Option::None; +} + +fn projection_details( + projection: ProjectionKind, + accounts: &serde_json::Value, + parameters: &serde_json::Value, +) -> serde_json::Value { + return match projection { + ProjectionKind::ProgramByteWrite(_) => serde_json::json!({ + "targetAccount": first_account_key_for_roles( + accounts, + &["program_account", "buffer_account"], + ), + "authorityAccount": first_account_key_for_roles( + accounts, + &["authority", "buffer_authority"], + ), + "offset": parameters.get("offset").cloned().unwrap_or(serde_json::Value::Null), + "byteLength": parameters.get("byteLength").cloned().unwrap_or(serde_json::Value::Null), + "bytesSha256": parameters.get("bytesSha256").cloned().unwrap_or(serde_json::Value::Null), + "bytesPrefixHex": parameters.get("bytesPrefixHex").cloned().unwrap_or(serde_json::Value::Null), + "stateTransition": "program_bytes_written", + "codeBytesMaterialized": false, + "transactionFinalAccountStateCaptured": false, + }), + ProjectionKind::ProgramByteCopy => serde_json::json!({ + "targetAccount": account_key_for_role(accounts, "program_account"), + "authorityAccount": account_key_for_role(accounts, "authority"), + "sourceProgramAccount": account_key_for_role(accounts, "source_program_account"), + "destinationOffset": parameters.get("destinationOffset").cloned().unwrap_or(serde_json::Value::Null), + "sourceOffset": parameters.get("sourceOffset").cloned().unwrap_or(serde_json::Value::Null), + "byteLength": parameters.get("length").cloned().unwrap_or(serde_json::Value::Null), + "stateTransition": "program_bytes_copied", + "codeBytesMaterialized": false, + "transactionFinalAccountStateCaptured": false, + }), + ProjectionKind::ComputeBudgetProfile => serde_json::Value::Null, + }; +} + +fn first_account_key_for_roles(accounts: &serde_json::Value, roles: &[&str]) -> serde_json::Value { + for role in roles { + let value = account_key_for_role(accounts, role); + if !value.is_null() { + return value; + } + } + return serde_json::Value::Null; +} + +fn account_key_for_role(accounts: &serde_json::Value, role: &str) -> serde_json::Value { + let account_key = accounts.as_array().and_then(|values| { + return values.iter().find_map(|value| { + if value.get("role").and_then(serde_json::Value::as_str) + != std::option::Option::Some(role) + { + return std::option::Option::None; + } + return value + .get("accountKey") + .and_then(serde_json::Value::as_str) + .map(|account_key| return account_key.to_string()); + }); + }); + return match account_key { + std::option::Option::Some(value) => serde_json::Value::String(value), + std::option::Option::None => serde_json::Value::Null, + }; +} + +fn contains(entries: &[&str], entry_code: &str) -> bool { + return entries.iter().any(|entry| return *entry == entry_code); +} + +#[cfg(test)] +mod tests { + fn observation( + surface_code: &str, + entry_code: &str, + transaction_failed: bool, + accounts: serde_json::Value, + parameters: serde_json::Value, + ) -> kb_decoder_api::DecodedObservation { + return kb_decoder_api::DecodedObservation { + event_key: format!("{entry_code}:0"), + event: kb_model::DecodedProtocolEvent { + signature: kb_model::Signature("signature".to_string()), + slot: kb_model::Slot(42), + instruction_path: kb_model::InstructionPath("0".to_string()), + program_id: kb_model::ProgramId("program-id".to_string()), + protocol_code: kb_model::ProtocolCode("solana_native".to_string()), + surface_code: kb_model::SurfaceCode(surface_code.to_string()), + event_code: kb_model::EventCode(format!("{surface_code}.{entry_code}")), + event_name: kb_model::EventName(entry_code.to_string()), + event_family: kb_model::EventFamily::Audit, + source_kind: kb_model::EventSourceKind::Instruction, + confidence: kb_model::DecoderConfidence::ManualExact, + }, + payload_json: serde_json::json!({"accounts": accounts, "parameters": parameters}), + transaction_failed, + transaction_error: if transaction_failed { + std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]})) + } else { + std::option::Option::None + }, + observation_committed: !transaction_failed, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::Manual, + confidence: kb_model::DecoderConfidence::ManualExact, + evidence: std::vec!["fixture".to_string()], + }, + }; + } + + fn account(role: &str, account_key: &str) -> serde_json::Value { + return serde_json::json!({"role": role, "accountKey": account_key}); + } + + #[test] + fn loader_write_variants_create_bounded_code_audits() { + let materializer = crate::ComplianceAuditMaterializer; + for (surface, target_role, authority_role) in [ + (super::BPF_LOADER_DEPRECATED_SURFACE, "program_account", "missing"), + (super::BPF_LOADER_SURFACE, "program_account", "missing"), + (super::BPF_LOADER_UPGRADEABLE_SURFACE, "buffer_account", "buffer_authority"), + (super::LOADER_V4_SURFACE, "program_account", "authority"), + ] { + let observation = observation( + surface, + "write", + false, + serde_json::json!([ + account(target_role, "target111"), + account(authority_role, "authority111"), + ]), + serde_json::json!({ + "offset": 7, + "byteLength": 32, + "bytesSha256": "abc", + "bytesPrefixHex": "0102", + }), + ); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!( + result.outputs[0].family, + kb_model::MaterializedEventFamily::ComplianceAudit + ); + assert_eq!(result.outputs[0].payload_json["projection"]["targetAccount"], "target111"); + assert_eq!(result.outputs[0].payload_json["projection"]["bytesSha256"], "abc"); + assert_eq!( + result.outputs[0].payload_json["projection"]["codeBytesMaterialized"], + false + ); + } + } + + #[test] + fn loader_v4_copy_preserves_source_and_bounded_ranges() { + let materializer = crate::ComplianceAuditMaterializer; + let observation = observation( + super::LOADER_V4_SURFACE, + "copy", + false, + serde_json::json!([ + account("program_account", "target111"), + account("authority", "authority111"), + account("source_program_account", "source111"), + ]), + serde_json::json!({"destinationOffset": 8, "sourceOffset": 4, "length": 16}), + ); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!( + result.outputs[0].output_key, + "program_loader_code:solana_native_loader_v4:copy:0" + ); + assert_eq!( + result.outputs[0].payload_json["projection"]["sourceProgramAccount"], + "source111" + ); + assert_eq!(result.outputs[0].payload_json["projection"]["byteLength"], 16); + } + + #[test] + fn compute_budget_profile_is_emitted_only_by_profile_emitter() { + let materializer = crate::ComplianceAuditMaterializer; + let emitter = observation( + super::COMPUTE_BUDGET_SURFACE, + "set_compute_unit_limit", + false, + serde_json::json!([]), + serde_json::json!({ + "transactionProfile": { + "currentInstructionIsProfileEmitter": true, + "profileEmitterInstructionPath": "0", + "computeBudgetInstructionCount": 2, + "effectiveValues": { + "set_compute_unit_limit": { + "value": 200000, + "sourceInstructionPath": "0" + } + } + } + }), + ); + let emitted = kb_materializer_api::EventMaterializer::materialize(&materializer, &emitter); + assert_eq!(emitted.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(emitted.outputs[0].output_key, "transaction_compute_budget_profile:0"); + assert_eq!(emitted.outputs[0].family, kb_model::MaterializedEventFamily::ComplianceAudit); + assert_eq!(emitted.outputs[0].payload_json["profile"]["computeBudgetInstructionCount"], 2); + let non_emitter = observation( + super::COMPUTE_BUDGET_SURFACE, + "set_compute_unit_price", + false, + serde_json::json!([]), + serde_json::json!({ + "transactionProfile": { + "currentInstructionIsProfileEmitter": false, + "profileEmitterInstructionPath": "0" + } + }), + ); + let ignored = + kb_materializer_api::EventMaterializer::materialize(&materializer, &non_emitter); + assert_eq!(ignored.status, kb_materializer_api::MaterializerOutcomeStatus::Ignored); + } + + #[test] + fn failed_compute_budget_profile_remains_audit_materializable() { + let materializer = crate::ComplianceAuditMaterializer; + let observation = observation( + super::COMPUTE_BUDGET_SURFACE, + "set_compute_unit_price", + true, + serde_json::json!([]), + serde_json::json!({ + "transactionProfile": { + "currentInstructionIsProfileEmitter": true, + "profileEmitterInstructionPath": "0", + "computeBudgetInstructionCount": 1 + } + }), + ); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs[0].payload_json["transactionSucceeded"], false); + } + + #[test] + fn failed_code_mutation_is_refused() { + let materializer = crate::ComplianceAuditMaterializer; + let observation = observation( + super::LOADER_V4_SURFACE, + "write", + true, + serde_json::json!([account("program_account", "target111")]), + serde_json::json!({"offset": 0, "byteLength": 1, "bytesSha256": "abc"}), + ); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Refused); + assert!(result.outputs.is_empty()); + } + + #[test] + fn unrelated_audit_observation_is_ignored() { + let materializer = crate::ComplianceAuditMaterializer; + let observation = observation( + "other_surface", + "write", + false, + serde_json::json!([]), + serde_json::json!({}), + ); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Ignored); + } + + #[test] + fn output_serialization_is_deterministic() { + let materializer = crate::ComplianceAuditMaterializer; + let observation = observation( + super::BPF_LOADER_UPGRADEABLE_SURFACE, + "write", + false, + serde_json::json!([ + account("buffer_account", "buffer111"), + account("buffer_authority", "authority111"), + ]), + serde_json::json!({ + "offset": 0, + "byteLength": 2, + "bytesSha256": "abc", + "bytesPrefixHex": "0102", + }), + ); + let first = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + let second = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + let first_json = match serde_json::to_string(&first) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("first compliance serialization failed: {error}") + }, + }; + let second_json = match serde_json::to_string(&second) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("second compliance serialization failed: {error}") + }, + }; + assert_eq!(first_json, second_json); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_fees/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_fees/Cargo.toml new file mode 100644 index 0000000..3360052 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_fees/Cargo.toml @@ -0,0 +1,21 @@ +# file: kb_materializer_fees/Cargo.toml +# version: 5 + +[package] +name = "kb_materializer_fees" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_decoder_spl_token_2022 = { path = "../kb_decoder_spl_token_2022" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_fees/README.md b/migration/khadhroony-bot2-reference/kb_materializer_fees/README.md new file mode 100644 index 0000000..1fb486b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_fees/README.md @@ -0,0 +1,16 @@ + + + +# kb_materializer_fees + +Ce crate conserve le domaine commun des frais, mais n'active aucune projection pour le programme SPL Token classique. + +## Décision SPL Token classique + +SPL Token classique ne possède pas les transfer fees de Token-2022. Un transfert Token ne permet donc pas d'inventer un frais, et les frais réseau appartiennent déjà au core. Le contrat `no projection` est couvert par tests. + +Le matérialiseur reste disponible pour de futures surfaces qui produisent de véritables faits `Fee`, sans anticiper leur schéma. + +## Décision ATA + +Le wire ATA ne transporte aucun montant exact de rent ni de frais réseau. Ces coûts doivent provenir du contexte core ou de la simulation d'exécution ; aucune projection `Fee` ATA n'est donc produite. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_fees/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_fees/src/lib.rs new file mode 100644 index 0000000..a05d68b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_fees/src/lib.rs @@ -0,0 +1,14 @@ +// file: kb_materializer_fees/src/lib.rs +// version: 5 + +//! Materializer crate for `fees`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod materializer; + +/// Fee materializer for committed Token-2022 fee instructions; classic SPL Token remains unsupported. +pub use crate::materializer::FeesMaterializer; +/// Materialize the authoritative public and confidential Token-2022 fee extensions from one parsed account snapshot. +pub use crate::materializer::materialize_token_2022_fee_state_snapshots; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_fees/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_fees/src/materializer.rs new file mode 100644 index 0000000..b17a6cf --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_fees/src/materializer.rs @@ -0,0 +1,441 @@ +// file: kb_materializer_fees/src/materializer.rs +// version: 9 + +//! Stable fee projections derived from exact committed Token-2022 observations. + +const ACCEPTED_FAMILIES: &[kb_model::EventFamily] = &[kb_model::EventFamily::Fee]; +const SPL_TOKEN_2022_SURFACE: &str = "spl_token_2022"; +const TOKEN_2022_FEE_ENTRIES: &[&str] = &[ + "initialize_transfer_fee_config", + "set_transfer_fee", + "withdraw_withheld_tokens_from_mint", + "withdraw_withheld_tokens_from_accounts", + "harvest_withheld_tokens_to_mint", + "transfer_checked_with_fee", + "initialize_confidential_transfer_fee_config", + "withdraw_confidential_withheld_tokens_from_mint", + "withdraw_confidential_withheld_tokens_from_accounts", + "harvest_confidential_withheld_tokens_to_mint", + "enable_confidential_harvest_to_mint", + "disable_confidential_harvest_to_mint", + "transfer_confidential_tokens_with_fee", +]; + +/// Fee materializer for committed Token-2022 fee instructions. +#[derive(Clone, Debug, Default)] +pub struct FeesMaterializer; + +impl kb_materializer_api::Materializer for crate::FeesMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_fees"; + } + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + fn accepts_event(&self, event: &kb_model::DecodedProtocolEvent) -> bool { + return accepts_event(event); + } + fn materialize_event( + &self, + _event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +impl kb_materializer_api::EventMaterializer for crate::FeesMaterializer { + fn identity(&self) -> kb_materializer_api::MaterializerIdentity { + return kb_materializer_api::MaterializerIdentity { + name: "fees".to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + }; + } + fn accepted_families(&self) -> &'static [kb_model::EventFamily] { + return ACCEPTED_FAMILIES; + } + fn accepts_observation(&self, observation: &kb_decoder_api::DecodedObservation) -> bool { + return accepts_event(&observation.event); + } + fn transaction_policy( + &self, + _family: kb_model::EventFamily, + ) -> kb_materializer_api::MaterializationTransactionPolicy { + return kb_materializer_api::MaterializationTransactionPolicy::SuccessfulCommittedOnly; + } + fn materialize( + &self, + observation: &kb_decoder_api::DecodedObservation, + ) -> kb_materializer_api::MaterializerExecutionResult { + if !accepts_event(&observation.event) { + return kb_materializer_api::MaterializerExecutionResult::ignored(); + } + if observation.transaction_failed || !observation.observation_committed { + return kb_materializer_api::MaterializerExecutionResult::refused( + "failed_transaction_fee_refused", + "failed or uncommitted Token-2022 observations cannot create fee outputs", + ); + } + let operation = observation.event.event_name.0.clone(); + let accounts = observation + .payload_json + .get("accounts") + .cloned() + .unwrap_or(serde_json::Value::Null); + let parameters = observation + .payload_json + .get("parameters") + .cloned() + .unwrap_or_else(|| return observation.payload_json.clone()); + let output = kb_materializer_api::MaterializedOutput { + output_key: format!("token_2022_fee:{operation}:0"), + family: kb_model::MaterializedEventFamily::Fee, + payload_json: serde_json::json!({ + "projectionVersion": 1, + "projectionSemantics": "committed_instruction_fee_event", + "domain": fee_domain(observation.event.event_name.0.as_str()), + "operation": operation, + "programId": observation.event.program_id.0.clone(), + "signature": observation.event.signature.0.clone(), + "slot": observation.event.slot.0, + "instructionPath": observation.event.instruction_path.0.clone(), + "transactionSucceeded": true, + "accounts": accounts, + "parameters": parameters, + "stateReadRequired": state_read_required( + observation.event.event_name.0.as_str(), + ), + "confidentialValuesDecrypted": false, + }), + }; + return kb_materializer_api::MaterializerExecutionResult { + status: kb_materializer_api::MaterializerOutcomeStatus::Inserted, + outputs: std::vec![output], + diagnostics: std::vec::Vec::new(), + }; + } +} + +fn accepts_event(event: &kb_model::DecodedProtocolEvent) -> bool { + return event.event_family == kb_model::EventFamily::Fee + && event.surface_code.0 == SPL_TOKEN_2022_SURFACE + && event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID + && contains(TOKEN_2022_FEE_ENTRIES, event.event_name.0.as_str()); +} + +fn fee_domain(operation: &str) -> &'static str { + if operation.contains("confidential") { + return "token_2022_confidential_transfer_fee"; + } + return "token_2022_transfer_fee"; +} + +fn state_read_required(operation: &str) -> bool { + return matches!( + operation, + "initialize_transfer_fee_config" + | "set_transfer_fee" + | "withdraw_withheld_tokens_from_mint" + | "withdraw_withheld_tokens_from_accounts" + | "harvest_withheld_tokens_to_mint" + | "initialize_confidential_transfer_fee_config" + | "withdraw_confidential_withheld_tokens_from_mint" + | "withdraw_confidential_withheld_tokens_from_accounts" + | "harvest_confidential_withheld_tokens_to_mint" + | "enable_confidential_harvest_to_mint" + | "disable_confidential_harvest_to_mint" + ); +} + +fn contains(entries: &[&str], entry_code: &str) -> bool { + return entries.iter().any(|entry| return *entry == entry_code); +} + +/// Materialize authoritative Token-2022 fee extension snapshots without decrypting confidential values. +pub fn materialize_token_2022_fee_state_snapshots( + account_key: &str, + slot: u64, + state: &kb_decoder_spl_token_2022::state::Token2022State, +) -> std::result::Result, String> { + if account_key.is_empty() { + return std::result::Result::Err( + "Token-2022 fee state snapshot requires a non-empty account key".to_string(), + ); + } + let state_kind = match state.kind { + kb_decoder_spl_token_2022::state::Token2022StateKind::Mint => "mint", + kb_decoder_spl_token_2022::state::Token2022StateKind::Account => "account", + kb_decoder_spl_token_2022::state::Token2022StateKind::Multisig => { + return std::result::Result::Err( + "Token-2022 multisig state cannot contain fee extensions".to_string(), + ); + }, + }; + let mut outputs = std::vec::Vec::new(); + for entry in &state.extensions { + let confidential = match entry.extension_name { + "transfer_fee_config" | "transfer_fee_amount" => false, + "confidential_transfer_fee_config" | "confidential_transfer_fee_amount" => true, + _ => continue, + }; + let expected_kind = match entry.extension_name { + "transfer_fee_config" | "confidential_transfer_fee_config" => "mint", + "transfer_fee_amount" | "confidential_transfer_fee_amount" => "account", + _ => continue, + }; + if state_kind != expected_kind { + return std::result::Result::Err(format!( + "Token-2022 {} extension requires {expected_kind} state, got {state_kind}", + entry.extension_name + )); + } + outputs.push(kb_materializer_api::MaterializedOutput { + output_key: format!( + "token_2022_fee_state:{}:{account_key}:{slot}", + entry.extension_name + ), + family: kb_model::MaterializedEventFamily::Fee, + payload_json: serde_json::json!({ + "projectionVersion": 1, + "projectionSemantics": "authoritative_bounded_fee_state_snapshot", + "domain": if confidential { + "token_2022_confidential_transfer_fee_state" + } else { + "token_2022_transfer_fee_state" + }, + "idempotenceKey": format!( + "token_2022_fee_state:{}:{account_key}:{slot}", + entry.extension_name + ), + "accountKey": account_key, + "slot": slot, + "programId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + "stateKind": state_kind, + "extensionType": entry.extension_type, + "extensionName": entry.extension_name, + "valueHex": entry.value_hex, + "valueFields": entry.value_fields, + "containsConfidentialState": confidential, + "confidentialValuesDecrypted": false, + "finalAccountStateCaptured": true, + "provenance": { + "processorName": "fees", + "processorVersion": env!("CARGO_PKG_VERSION"), + "source": "bounded_token_2022_state_parser" + } + }), + }); + } + return std::result::Result::Ok(outputs); +} + +#[cfg(test)] +mod tests { + fn observation( + surface_code: &str, + program_id: &str, + entry_code: &str, + transaction_failed: bool, + ) -> kb_decoder_api::DecodedObservation { + return kb_decoder_api::DecodedObservation { + event_key: format!("{entry_code}:0"), + event: kb_model::DecodedProtocolEvent { + signature: kb_model::Signature("signature".to_string()), + slot: kb_model::Slot(42), + instruction_path: kb_model::InstructionPath("0".to_string()), + program_id: kb_model::ProgramId(program_id.to_string()), + protocol_code: kb_model::ProtocolCode("spl_token_2022".to_string()), + surface_code: kb_model::SurfaceCode(surface_code.to_string()), + event_code: kb_model::EventCode(format!("{surface_code}.{entry_code}")), + event_name: kb_model::EventName(entry_code.to_string()), + event_family: kb_model::EventFamily::Fee, + source_kind: kb_model::EventSourceKind::Instruction, + confidence: kb_model::DecoderConfidence::ManualExact, + }, + payload_json: serde_json::json!({ + "accounts": [{"position": 0, "role": "mint", "accountKey": "mint111"}], + "parameters": {"extension": "transfer_fee", "basisPoints": 25}, + }), + transaction_failed, + transaction_error: if transaction_failed { + std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]})) + } else { + std::option::Option::None + }, + observation_committed: !transaction_failed, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::Manual, + confidence: kb_model::DecoderConfidence::ManualExact, + evidence: std::vec!["fixture".to_string()], + }, + }; + } + + #[test] + fn committed_token_2022_fee_instruction_creates_one_stable_output() { + let observation = observation( + super::SPL_TOKEN_2022_SURFACE, + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + "set_transfer_fee", + false, + ); + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::FeesMaterializer, + &observation, + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs.len(), 1); + assert_eq!(result.outputs[0].family, kb_model::MaterializedEventFamily::Fee); + assert_eq!(result.outputs[0].output_key, "token_2022_fee:set_transfer_fee:0"); + assert_eq!(result.outputs[0].payload_json["stateReadRequired"], true); + } + + #[test] + fn failed_token_2022_fee_instruction_is_refused() { + let observation = observation( + super::SPL_TOKEN_2022_SURFACE, + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + "harvest_withheld_tokens_to_mint", + true, + ); + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::FeesMaterializer, + &observation, + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Refused); + assert!(result.outputs.is_empty()); + } + + #[test] + fn classic_token_and_program_surface_mismatches_are_ignored() { + let classic = observation( + "spl_token", + kb_program_ids::SPL_TOKEN_PROGRAM_ID, + "set_transfer_fee", + false, + ); + let wrong_program = observation( + super::SPL_TOKEN_2022_SURFACE, + kb_program_ids::SPL_TOKEN_PROGRAM_ID, + "set_transfer_fee", + false, + ); + assert!(!kb_materializer_api::EventMaterializer::accepts_observation( + &crate::FeesMaterializer, + &classic, + )); + assert!(!kb_materializer_api::EventMaterializer::accepts_observation( + &crate::FeesMaterializer, + &wrong_program, + )); + } + + #[test] + fn transfer_checked_with_fee_is_self_contained_but_state_mutations_are_not() { + assert!(!super::state_read_required("transfer_checked_with_fee")); + assert!(super::state_read_required("withdraw_withheld_tokens_from_accounts")); + } + + #[test] + fn committed_confidential_fee_instruction_is_projected_without_decryption() { + let observation = observation( + super::SPL_TOKEN_2022_SURFACE, + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + "initialize_confidential_transfer_fee_config", + false, + ); + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::FeesMaterializer, + &observation, + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs.len(), 1); + assert_eq!( + result.outputs[0].payload_json["domain"], + "token_2022_confidential_transfer_fee" + ); + assert_eq!(result.outputs[0].payload_json["stateReadRequired"], true); + assert_eq!(result.outputs[0].payload_json["confidentialValuesDecrypted"], false); + } + + #[test] + fn confidential_transfer_with_fee_is_instructionally_self_contained() { + assert!(!super::state_read_required("transfer_confidential_tokens_with_fee",)); + assert_eq!( + super::fee_domain("transfer_confidential_tokens_with_fee"), + "token_2022_confidential_transfer_fee" + ); + } + + #[test] + fn public_fee_state_snapshots_are_owned_once_and_idempotent() { + let mint = kb_decoder_spl_token_2022::state::Token2022State { + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Mint, + base_fields: serde_json::json!({}), + base_hex: "00".to_string(), + account_type: std::option::Option::Some(1), + extensions: std::vec![kb_decoder_spl_token_2022::state::Token2022TlvEntry { + extension_type: 1, + extension_name: "transfer_fee_config", + value_hex: "01".to_string(), + value_fields: serde_json::json!({"withheldAmount":"7"}), + }], + }; + let first = crate::materialize_token_2022_fee_state_snapshots("mint111", 42, &mint); + let second = crate::materialize_token_2022_fee_state_snapshots("mint111", 42, &mint); + assert!(first.is_ok()); + assert_eq!(first, second); + let outputs = match first { + std::result::Result::Ok(outputs) => outputs, + std::result::Result::Err(error) => panic!("unexpected error: {error}"), + }; + assert_eq!(outputs.len(), 1); + assert_eq!(outputs[0].payload_json["stateKind"], "mint"); + assert_eq!(outputs[0].payload_json["valueFields"]["withheldAmount"], "7"); + assert_eq!(outputs[0].payload_json["confidentialValuesDecrypted"], false); + } + + #[test] + fn confidential_fee_amount_preserves_ciphertext_without_decryption_claim() { + let account = kb_decoder_spl_token_2022::state::Token2022State { + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Account, + base_fields: serde_json::json!({}), + base_hex: "00".to_string(), + account_type: std::option::Option::Some(2), + extensions: std::vec![kb_decoder_spl_token_2022::state::Token2022TlvEntry { + extension_type: 17, + extension_name: "confidential_transfer_fee_amount", + value_hex: "02".to_string(), + value_fields: serde_json::json!({"withheldAmount":"ciphertext"}), + }], + }; + let result = crate::materialize_token_2022_fee_state_snapshots("account111", 43, &account); + assert!(result.is_ok()); + let outputs = match result { + std::result::Result::Ok(outputs) => outputs, + std::result::Result::Err(error) => panic!("unexpected error: {error}"), + }; + assert_eq!(outputs[0].payload_json["containsConfidentialState"], true); + assert_eq!(outputs[0].payload_json["confidentialValuesDecrypted"], false); + assert_eq!(outputs[0].payload_json["valueFields"]["withheldAmount"], "ciphertext"); + } + + #[test] + fn fee_state_snapshot_rejects_wrong_state_kind_and_empty_identity() { + let wrong = kb_decoder_spl_token_2022::state::Token2022State { + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Account, + base_fields: serde_json::json!({}), + base_hex: "00".to_string(), + account_type: std::option::Option::Some(2), + extensions: std::vec![kb_decoder_spl_token_2022::state::Token2022TlvEntry { + extension_type: 1, + extension_name: "transfer_fee_config", + value_hex: "01".to_string(), + value_fields: serde_json::json!({}), + }], + }; + assert!( + crate::materialize_token_2022_fee_state_snapshots("account111", 44, &wrong,).is_err() + ); + assert!(crate::materialize_token_2022_fee_state_snapshots("", 44, &wrong,).is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_governance/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_governance/Cargo.toml new file mode 100644 index 0000000..35ebba6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_governance/Cargo.toml @@ -0,0 +1,17 @@ +# file: kb_materializer_governance/Cargo.toml +# version: 1 + +[package] +name = "kb_materializer_governance" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_governance/README.md b/migration/khadhroony-bot2-reference/kb_materializer_governance/README.md new file mode 100644 index 0000000..3ff3ae4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_governance/README.md @@ -0,0 +1,14 @@ + + + +# kb_materializer_governance + +Ce crate matérialise les événements décodés de la famille `governance` dans les tables métier correspondantes. + +## Rôle dans l'écosystème + +Ce module travaille sur les événements décodés et produit des projections métier. Il ne doit pas accéder directement au RPC, au wallet, à Tauri ou aux décodeurs spécifiques. + +## État initial + +Le crate est créé comme point d'ancrage. Les règles de promotion métier, les tables PostgreSQL et les contrôles SQL seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_governance/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_governance/src/lib.rs new file mode 100644 index 0000000..7f5d6fb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_governance/src/lib.rs @@ -0,0 +1,12 @@ +// file: kb_materializer_governance/src/lib.rs +// version: 1 + +//! Materializer crate for `governance`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod materializer; + +/// Exposes the materializer type implemented by this crate. +pub use crate::materializer::GovernanceMaterializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_governance/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_governance/src/materializer.rs new file mode 100644 index 0000000..34b69ef --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_governance/src/materializer.rs @@ -0,0 +1,35 @@ +// file: kb_materializer_governance/src/materializer.rs +// version: 5 + +//! Materializer scaffold for the `kb_materializer_governance` crate. + +/// Initial materializer implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct GovernanceMaterializer; + +impl kb_materializer_api::Materializer for crate::GovernanceMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_governance"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, _event: &kb_model::DecodedProtocolEvent) -> bool { + return false; + } + + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + let materialized = kb_model::MaterializedEvent { + signature: event.signature.clone(), + slot: event.slot, + decoded_family: event.event_family, + materialized_family: kb_model::MaterializedEventFamily::Governance, + }; + return std::result::Result::Ok(std::vec![materialized]); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_lending/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_lending/Cargo.toml new file mode 100644 index 0000000..d69242f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_lending/Cargo.toml @@ -0,0 +1,17 @@ +# file: kb_materializer_lending/Cargo.toml +# version: 2 + +[package] +name = "kb_materializer_lending" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_lending/README.md b/migration/khadhroony-bot2-reference/kb_materializer_lending/README.md new file mode 100644 index 0000000..7b8dc33 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_lending/README.md @@ -0,0 +1,14 @@ + + + +# kb_materializer_lending + +Ce crate matérialise les événements décodés de la famille `lending` dans les tables métier correspondantes. + +## Rôle dans l'écosystème + +Ce module travaille sur les événements décodés et produit des projections métier. Il ne doit pas accéder directement au RPC, au wallet, à Tauri ou aux décodeurs spécifiques. + +## État initial + +Le crate est créé comme point d'ancrage. Les règles de promotion métier, les tables PostgreSQL et les contrôles SQL seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_lending/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_lending/src/lib.rs new file mode 100644 index 0000000..2ad937f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_lending/src/lib.rs @@ -0,0 +1,12 @@ +// file: kb_materializer_lending/src/lib.rs +// version: 1 + +//! Materializer crate for `lending`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod materializer; + +/// Exposes the materializer type implemented by this crate. +pub use crate::materializer::LendingMaterializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_lending/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_lending/src/materializer.rs new file mode 100644 index 0000000..85773e3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_lending/src/materializer.rs @@ -0,0 +1,35 @@ +// file: kb_materializer_lending/src/materializer.rs +// version: 5 + +//! Materializer scaffold for the `kb_materializer_lending` crate. + +/// Initial materializer implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LendingMaterializer; + +impl kb_materializer_api::Materializer for crate::LendingMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_lending"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, _event: &kb_model::DecodedProtocolEvent) -> bool { + return false; + } + + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + let materialized = kb_model::MaterializedEvent { + signature: event.signature.clone(), + slot: event.slot, + decoded_family: event.event_family, + materialized_family: kb_model::MaterializedEventFamily::Lending, + }; + return std::result::Result::Ok(std::vec![materialized]); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_lifecycle/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_lifecycle/Cargo.toml new file mode 100644 index 0000000..8404812 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_lifecycle/Cargo.toml @@ -0,0 +1,21 @@ +# file: kb_materializer_lifecycle/Cargo.toml +# version: 5 + +[package] +name = "kb_materializer_lifecycle" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_lifecycle/README.md b/migration/khadhroony-bot2-reference/kb_materializer_lifecycle/README.md new file mode 100644 index 0000000..26d925b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_lifecycle/README.md @@ -0,0 +1,36 @@ + + + +# kb_materializer_lifecycle + +Ce crate matérialise des mutations lifecycle natives stables dans des projections métier génériques et versionnées. Il ne se limite pas à la famille décodée `Lifecycle` : certaines mutations exactes sont classées `Admin` ou `Audit` par le décodeur, puis promues en sortie `Lifecycle` uniquement lorsque leur effet durable est explicite et que la transaction est réussie et commitée. + +## Projections actives + +- comptes System génériques : `system_account::0` pour create/allocate ; +- Address Lookup Table : `address_lookup_table::0` ; +- loaders natifs : `program_loader:::0` ; +- Feature Gate : `feature_gate:revoke_pending_activation:0` ; +- durable nonce System : `durable_nonce_account::0` pour initialize, advance, authorize, upgrade et withdraw ; +- contexte ZK ElGamal : `zk_proof_context::0` pour l’initialisation demandée par une vérification réussie et pour la fermeture ; +- rapport Slashing : `slashing_violation_report::0` pour l’initialisation après preuve acceptée et la fermeture après rétention minimale. + +Le retrait d’un nonce account est projeté comme une opération de retrait avec fermeture conditionnelle. Le runtime détruit le compte seulement lorsque la totalité du solde est retirée ; le matérialiseur ne prétend pas reconstruire l’état final du compte à partir de l’instruction seule et ne duplique pas les deltas SOL déjà présents dans core. + +Une vérification ZK ElGamal sans compte de contexte reste en decode/audit. Lorsqu’un contexte est demandé, la projection conserve le compte de contexte, son autorité et le type de preuve, mais ne matérialise jamais la preuve cryptographique. Le rapport Slashing est un enregistrement de violation et non une pénalité appliquée par le programme ; la projection conserve explicitement `penaltyAppliedByProgram = false`. L’ancien ZK Token Proof Program reste exclu : le runtime Agave actuel est un stub sans effet et les layouts historiques ne disposent pas d’un corpus mainnet fiable. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Toute projection mutable applique `SuccessfulCommittedOnly`. +- L’applicabilité est filtrée au niveau exact de la surface, de l’entrée et, pour ZK ElGamal, de `contextStateRequested`. +- Le lifecycle ATA appartient exclusivement à `kb_materializer_token_accounts` et reste explicitement ignoré ici afin d'éviter toute duplication. +- Les écritures/copies de bytecode et changements d’autorité Loader sont pris en charge par les matérialiseurs compliance audit et admin ; Compute Budget, les précompiles de signature et les preuves ZK sans contexte restent dans decode. + +Depuis `0.4.1-pre.005`, l’applicabilité est déclarée au niveau exact de l’observation. `pre.006` ajoute les mutations loader stables, `pre.008` la révocation Feature Gate et `pre.017` les durable nonce accounts System ainsi que les comptes de contexte ZK ElGamal, puis `pre.018` les rapports Slashing. Toute transaction échouée ou observation non commitée reste refusée. + +## Comptes System génériques + +`pre.020` ajoute les sorties `system_account::0` pour `create_account`, `create_account_with_seed`, `create_account_allow_prefund`, `allocate` et `allocate_with_seed`. La projection conserve la cible, l'owner, l'espace, la seed et le montant demandé, mais ne recopie pas les deltas SOL déjà présents dans le graphe core. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_lifecycle/src/constants.rs b/migration/khadhroony-bot2-reference/kb_materializer_lifecycle/src/constants.rs new file mode 100644 index 0000000..7e5b4de --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_lifecycle/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_materializer_lifecycle/src/constants.rs +// version: 1 + +//! Local constants for the `kb_materializer_lifecycle` crate. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_materializer_lifecycle"; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_lifecycle/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_lifecycle/src/lib.rs new file mode 100644 index 0000000..ea358e2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_lifecycle/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_materializer_lifecycle/src/lib.rs +// version: 5 + +//! Materializer crate for `lifecycle`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod materializer; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Stable lifecycle materializer for supported native lifecycle projections. +pub use crate::materializer::LifecycleMaterializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_lifecycle/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_lifecycle/src/materializer.rs new file mode 100644 index 0000000..073ea88 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_lifecycle/src/materializer.rs @@ -0,0 +1,993 @@ +// file: kb_materializer_lifecycle/src/materializer.rs +// version: 13 + +//! Stable lifecycle projections derived from exact decoded observations. + +const ACCEPTED_FAMILIES: &[kb_model::EventFamily] = &[ + kb_model::EventFamily::Lifecycle, + kb_model::EventFamily::Admin, + kb_model::EventFamily::Audit, +]; +const ADDRESS_LOOKUP_TABLE_SURFACE: &str = "solana_native_address_lookup_table"; +const ADDRESS_LOOKUP_TABLE_ENTRIES: &[&str] = &[ + "create_lookup_table", + "freeze_lookup_table", + "extend_lookup_table", + "deactivate_lookup_table", + "close_lookup_table", +]; +const BPF_LOADER_DEPRECATED_SURFACE: &str = "solana_native_bpf_loader_deprecated"; +const BPF_LOADER_SURFACE: &str = "solana_native_bpf_loader"; +const BPF_LOADER_UPGRADEABLE_SURFACE: &str = "solana_native_bpf_loader_upgradeable"; +const LOADER_V4_SURFACE: &str = "solana_native_loader_v4"; +const FEATURE_SURFACE: &str = "solana_native_feature"; +const FEATURE_ENTRIES: &[&str] = &["revoke_pending_activation"]; +const IMMUTABLE_LOADER_ENTRIES: &[&str] = &["finalize"]; +const UPGRADEABLE_LOADER_ENTRIES: &[&str] = &[ + "initialize_buffer", + "deploy_with_max_data_len", + "upgrade", + "close", + "extend_program", +]; +const LOADER_V4_ENTRIES: &[&str] = &["set_program_length", "deploy", "retract", "finalize"]; +const SLASHING_SURFACE: &str = "solana_native_slashing"; +const SLASHING_ENTRIES: &[&str] = &["close_violation_report", "duplicate_block_proof"]; +const SYSTEM_SURFACE: &str = "solana_native_system"; +const SYSTEM_ACCOUNT_ENTRIES: &[&str] = &[ + "create_account", + "create_account_with_seed", + "allocate", + "allocate_with_seed", + "create_account_allow_prefund", +]; +const SYSTEM_NONCE_ENTRIES: &[&str] = &[ + "advance_nonce_account", + "withdraw_nonce_account", + "initialize_nonce_account", + "authorize_nonce_account", + "upgrade_nonce_account", +]; +const ZK_ELGAMAL_PROOF_SURFACE: &str = "solana_native_zk_elgamal_proof"; +const ZK_ELGAMAL_CLOSE_ENTRIES: &[&str] = &["close_context_state"]; +const ZK_ELGAMAL_VERIFY_ENTRIES: &[&str] = &[ + "verify_zero_ciphertext", + "verify_ciphertext_ciphertext_equality", + "verify_ciphertext_commitment_equality", + "verify_pubkey_validity", + "verify_percentage_with_cap", + "verify_batched_range_proof_u64", + "verify_batched_range_proof_u128", + "verify_batched_range_proof_u256", + "verify_grouped_ciphertext_2_handles_validity", + "verify_batched_grouped_ciphertext_2_handles_validity", + "verify_grouped_ciphertext_3_handles_validity", + "verify_batched_grouped_ciphertext_3_handles_validity", +]; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum ProjectionKind { + AddressLookupTable, + FeatureGate, + ProgramLoader(&'static str), + SlashingViolationReportInitialization, + SlashingViolationReportClosure, + SystemAccountLifecycle, + DurableNonceAccount, + ZkProofContextInitialization, + ZkProofContextClosure, +} + +impl ProjectionKind { + fn domain(self) -> &'static str { + return match self { + Self::AddressLookupTable => "address_lookup_table", + Self::FeatureGate => "feature_gate", + Self::ProgramLoader(_) => "program_loader", + Self::SlashingViolationReportInitialization | Self::SlashingViolationReportClosure => { + "slashing_violation_report" + }, + Self::SystemAccountLifecycle => "system_account", + Self::DurableNonceAccount => "durable_nonce_account", + Self::ZkProofContextInitialization | Self::ZkProofContextClosure => "zk_proof_context", + }; + } + + fn loader_surface(self) -> std::option::Option<&'static str> { + return match self { + Self::ProgramLoader(surface) => std::option::Option::Some(surface), + _ => std::option::Option::None, + }; + } + + fn output_key(self, surface_code: &str, operation: &str) -> std::string::String { + return match self { + Self::AddressLookupTable => format!("address_lookup_table:{operation}:0"), + Self::FeatureGate => format!("feature_gate:{operation}:0"), + Self::ProgramLoader(_) => { + format!("program_loader:{surface_code}:{operation}:0") + }, + Self::SlashingViolationReportInitialization | Self::SlashingViolationReportClosure => { + format!("slashing_violation_report:{operation}:0") + }, + Self::SystemAccountLifecycle => format!("system_account:{operation}:0"), + Self::DurableNonceAccount => format!("durable_nonce_account:{operation}:0"), + Self::ZkProofContextInitialization | Self::ZkProofContextClosure => { + format!("zk_proof_context:{operation}:0") + }, + }; + } +} + +/// Stable lifecycle materializer for supported native lifecycle projections. +#[derive(Clone, Debug, Default)] +pub struct LifecycleMaterializer; + +impl kb_materializer_api::Materializer for crate::LifecycleMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_lifecycle"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, event: &kb_model::DecodedProtocolEvent) -> bool { + return family_is_accepted(event.event_family) + && static_projection(event.surface_code.0.as_str(), event.event_name.0.as_str()) + .is_some(); + } + + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + if !kb_materializer_api::Materializer::accepts_event(self, event) { + return std::result::Result::Ok(std::vec::Vec::new()); + } + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +impl kb_materializer_api::EventMaterializer for crate::LifecycleMaterializer { + fn identity(&self) -> kb_materializer_api::MaterializerIdentity { + return kb_materializer_api::MaterializerIdentity { + name: "solana_native_lifecycle".to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + }; + } + + fn accepted_families(&self) -> &'static [kb_model::EventFamily] { + return ACCEPTED_FAMILIES; + } + + fn accepts_observation(&self, observation: &kb_decoder_api::DecodedObservation) -> bool { + return family_is_accepted(observation.event.event_family) + && observation_projection(observation).is_some(); + } + + fn transaction_policy( + &self, + _family: kb_model::EventFamily, + ) -> kb_materializer_api::MaterializationTransactionPolicy { + return kb_materializer_api::MaterializationTransactionPolicy::SuccessfulCommittedOnly; + } + + fn materialize( + &self, + observation: &kb_decoder_api::DecodedObservation, + ) -> kb_materializer_api::MaterializerExecutionResult { + let projection = match observation_projection(observation) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + tracing::debug!( + target: crate::TRACING_TARGET, + action = "materialize_lifecycle", + surface_code = %observation.event.surface_code.0.as_str(), + entry_code = %observation.event.event_name.0.as_str(), + accepted = false, + "ignore observation outside supported native lifecycle projections" + ); + return kb_materializer_api::MaterializerExecutionResult::ignored(); + }, + }; + if observation.transaction_failed || !observation.observation_committed { + tracing::debug!( + target: crate::TRACING_TARGET, + action = "materialize_lifecycle", + surface_code = %observation.event.surface_code.0.as_str(), + entry_code = %observation.event.event_name.0.as_str(), + accepted = true, + committed = observation.observation_committed, + transaction_failed = observation.transaction_failed, + "refuse uncommitted native lifecycle observation" + ); + return kb_materializer_api::MaterializerExecutionResult::refused( + "failed_transaction_lifecycle_refused", + "failed or uncommitted native lifecycle observations cannot create lifecycle outputs", + ); + } + let accounts = observation + .payload_json + .get("accounts") + .cloned() + .unwrap_or(serde_json::Value::Null); + let parameters = observation + .payload_json + .get("parameters") + .cloned() + .unwrap_or(serde_json::Value::Null); + let operation = observation.event.event_name.0.clone(); + let output_key = + projection.output_key(observation.event.surface_code.0.as_str(), operation.as_str()); + let domain = projection.domain(); + let loader_surface = projection.loader_surface(); + let projection_details = + projection_details(projection, &accounts, ¶meters, operation.as_str()); + tracing::debug!( + target: crate::TRACING_TARGET, + action = "materialize_lifecycle", + surface_code = %observation.event.surface_code.0.as_str(), + entry_code = %observation.event.event_name.0.as_str(), + projection_domain = domain, + accepted = true, + committed = true, + output_count = 1_usize, + "materialize committed native lifecycle observation" + ); + let output = kb_materializer_api::MaterializedOutput { + output_key, + family: kb_model::MaterializedEventFamily::Lifecycle, + payload_json: serde_json::json!({ + "projectionVersion": 1, + "projectionSemantics": "committed_instruction_lifecycle_event", + "domain": domain, + "loaderSurface": loader_surface, + "operation": operation, + "programId": observation.event.program_id.0.clone(), + "signature": observation.event.signature.0.clone(), + "slot": observation.event.slot.0, + "instructionPath": observation.event.instruction_path.0.clone(), + "transactionSucceeded": true, + "accounts": accounts, + "parameters": parameters, + "projection": projection_details, + }), + }; + return kb_materializer_api::MaterializerExecutionResult { + status: kb_materializer_api::MaterializerOutcomeStatus::Inserted, + outputs: std::vec![output], + diagnostics: std::vec::Vec::new(), + }; + } +} + +fn family_is_accepted(family: kb_model::EventFamily) -> bool { + return ACCEPTED_FAMILIES.iter().any(|accepted| return *accepted == family); +} + +fn observation_projection( + observation: &kb_decoder_api::DecodedObservation, +) -> std::option::Option { + let surface_code = observation.event.surface_code.0.as_str(); + let entry_code = observation.event.event_name.0.as_str(); + let static_projection = static_projection(surface_code, entry_code); + if static_projection.is_some() { + return static_projection; + } + if surface_code == ZK_ELGAMAL_PROOF_SURFACE + && contains(ZK_ELGAMAL_VERIFY_ENTRIES, entry_code) + && observation + .payload_json + .get("parameters") + .and_then(|parameters| return parameters.get("contextStateRequested")) + .and_then(serde_json::Value::as_bool) + == std::option::Option::Some(true) + { + return std::option::Option::Some(ProjectionKind::ZkProofContextInitialization); + } + return std::option::Option::None; +} + +fn static_projection(surface_code: &str, entry_code: &str) -> std::option::Option { + if surface_code == FEATURE_SURFACE && contains(FEATURE_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::FeatureGate); + } + if surface_code == ADDRESS_LOOKUP_TABLE_SURFACE + && contains(ADDRESS_LOOKUP_TABLE_ENTRIES, entry_code) + { + return std::option::Option::Some(ProjectionKind::AddressLookupTable); + } + if (surface_code == BPF_LOADER_DEPRECATED_SURFACE || surface_code == BPF_LOADER_SURFACE) + && contains(IMMUTABLE_LOADER_ENTRIES, entry_code) + { + let loader_surface = if surface_code == BPF_LOADER_DEPRECATED_SURFACE { + BPF_LOADER_DEPRECATED_SURFACE + } else { + BPF_LOADER_SURFACE + }; + return std::option::Option::Some(ProjectionKind::ProgramLoader(loader_surface)); + } + if surface_code == BPF_LOADER_UPGRADEABLE_SURFACE + && contains(UPGRADEABLE_LOADER_ENTRIES, entry_code) + { + return std::option::Option::Some(ProjectionKind::ProgramLoader( + BPF_LOADER_UPGRADEABLE_SURFACE, + )); + } + if surface_code == LOADER_V4_SURFACE && contains(LOADER_V4_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::ProgramLoader(LOADER_V4_SURFACE)); + } + if surface_code == SLASHING_SURFACE && contains(SLASHING_ENTRIES, entry_code) { + return if entry_code == "duplicate_block_proof" { + std::option::Option::Some(ProjectionKind::SlashingViolationReportInitialization) + } else { + std::option::Option::Some(ProjectionKind::SlashingViolationReportClosure) + }; + } + if surface_code == SYSTEM_SURFACE && contains(SYSTEM_ACCOUNT_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::SystemAccountLifecycle); + } + if surface_code == SYSTEM_SURFACE && contains(SYSTEM_NONCE_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::DurableNonceAccount); + } + if surface_code == ZK_ELGAMAL_PROOF_SURFACE && contains(ZK_ELGAMAL_CLOSE_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::ZkProofContextClosure); + } + return std::option::Option::None; +} + +fn projection_details( + projection: ProjectionKind, + accounts: &serde_json::Value, + parameters: &serde_json::Value, + operation: &str, +) -> serde_json::Value { + return match projection { + ProjectionKind::SlashingViolationReportInitialization => { + serde_json::json!({ + "targetAccount": account_key_for_role(accounts, "violation_report"), + "proofAccount": account_key_for_role(accounts, "proof_account"), + "violationSlot": parameters.get("violationSlot").cloned().unwrap_or(serde_json::Value::Null), + "nodePubkey": parameters.get("nodePubkey").cloned().unwrap_or(serde_json::Value::Null), + "reporter": parameters.get("reporter").cloned().unwrap_or(serde_json::Value::Null), + "lamportDestination": parameters.get("destination").cloned().unwrap_or(serde_json::Value::Null), + "stateTransition": "pda_assigned_allocated_and_data_stored_after_successful_duplicate_block_proof_acceptance", + "proofMaterialized": false, + "penaltyAppliedByProgram": false, + "enforcementSemantics": "violation_report_only_external_consensus_enforcement", + }) + }, + ProjectionKind::SlashingViolationReportClosure => serde_json::json!({ + "targetAccount": account_key_for_role(accounts, "violation_report"), + "lamportDestination": account_key_for_role(accounts, "destination"), + "stateTransition": "closed_after_minimum_retention_lamports_reclaimed_owner_reset_to_system_program", + "minimumRetentionEpochs": 3, + "transactionFinalAccountStateCaptured": false, + }), + ProjectionKind::SystemAccountLifecycle => serde_json::json!({ + "targetAccount": first_account_key_for_roles( + accounts, + &["new_account", "allocated_account", "derived_account"], + ), + "fundingAccount": account_key_for_role(accounts, "funding_account"), + "baseAccount": account_key_for_role(accounts, "base_account"), + "requestedLamports": parameters.get("lamports").cloned().unwrap_or(serde_json::Value::Null), + "requestedSpace": parameters.get("space").cloned().unwrap_or(serde_json::Value::Null), + "requestedOwner": parameters.get("owner").cloned().unwrap_or(serde_json::Value::Null), + "seed": parameters.get("seed").cloned().unwrap_or(serde_json::Value::Null), + "stateTransition": system_account_transition(operation), + "lamportTransferRepresentedByCoreBalanceChanges": operation == "create_account" + || operation == "create_account_with_seed" + || operation == "create_account_allow_prefund", + "transactionFinalAccountStateCaptured": false, + }), + ProjectionKind::DurableNonceAccount => serde_json::json!({ + "targetAccount": account_key_for_role(accounts, "nonce_account"), + "authorityAccount": account_key_for_role(accounts, "nonce_authority"), + "lamportDestination": account_key_for_role(accounts, "recipient_account"), + "stateTransition": nonce_transition(operation), + "newAuthority": parameters.get("authority").cloned().unwrap_or(serde_json::Value::Null), + "withdrawLamports": parameters.get("lamports").cloned().unwrap_or(serde_json::Value::Null), + "closureSemantics": if operation == "withdraw_nonce_account" { + serde_json::Value::String("closes_when_entire_balance_is_withdrawn".to_string()) + } else { + serde_json::Value::Null + }, + "transactionFinalAccountStateCaptured": false, + }), + ProjectionKind::ZkProofContextInitialization => serde_json::json!({ + "targetAccount": account_key_for_role(accounts, "proof_context_state"), + "contextStateAuthority": account_key_for_role(accounts, "context_state_authority"), + "proofType": parameters.get("proofType").cloned().unwrap_or(serde_json::Value::Null), + "stateTransition": "initialized_after_successful_runtime_verification", + "proofMaterialized": false, + }), + ProjectionKind::ZkProofContextClosure => serde_json::json!({ + "targetAccount": account_key_for_role(accounts, "proof_context_state"), + "lamportDestination": account_key_for_role(accounts, "lamport_destination"), + "contextStateAuthority": account_key_for_role(accounts, "context_state_authority"), + "stateTransition": "closed_lamports_reclaimed_owner_reset_to_system_program", + "proofMaterialized": false, + }), + ProjectionKind::AddressLookupTable + | ProjectionKind::FeatureGate + | ProjectionKind::ProgramLoader(_) => serde_json::json!({ + "stateTransition": operation, + }), + }; +} + +fn system_account_transition(operation: &str) -> &'static str { + return match operation { + "create_account" => "created_allocated_funded_and_assigned", + "create_account_with_seed" => "derived_account_created_allocated_funded_and_assigned", + "create_account_allow_prefund" => "prefunded_account_initialized_allocated_and_assigned", + "allocate" => "account_data_space_allocated", + "allocate_with_seed" => "derived_account_data_space_allocated", + _ => "unknown", + }; +} + +fn nonce_transition(operation: &str) -> &'static str { + return match operation { + "initialize_nonce_account" => "initialized", + "advance_nonce_account" => "nonce_advanced", + "authorize_nonce_account" => "authority_changed", + "upgrade_nonce_account" => "legacy_version_upgraded", + "withdraw_nonce_account" => "lamports_withdrawn_possible_closure", + _ => "unknown", + }; +} + +fn first_account_key_for_roles(accounts: &serde_json::Value, roles: &[&str]) -> serde_json::Value { + for role in roles { + let value = account_key_for_role(accounts, role); + if !value.is_null() { + return value; + } + } + return serde_json::Value::Null; +} + +fn account_key_for_role(accounts: &serde_json::Value, role: &str) -> serde_json::Value { + let account_key = accounts.as_array().and_then(|values| { + return values.iter().find_map(|value| { + if value.get("role").and_then(serde_json::Value::as_str) + != std::option::Option::Some(role) + { + return std::option::Option::None; + } + return value + .get("accountKey") + .and_then(serde_json::Value::as_str) + .map(|account_key| return account_key.to_string()); + }); + }); + return match account_key { + std::option::Option::Some(value) => serde_json::Value::String(value), + std::option::Option::None => serde_json::Value::Null, + }; +} + +fn contains(entries: &[&str], entry_code: &str) -> bool { + return entries.iter().any(|entry| return *entry == entry_code); +} + +#[cfg(test)] +mod tests { + fn observation_with_payload( + surface_code: &str, + entry_code: &str, + event_family: kb_model::EventFamily, + transaction_failed: bool, + accounts: serde_json::Value, + parameters: serde_json::Value, + ) -> kb_decoder_api::DecodedObservation { + return kb_decoder_api::DecodedObservation { + event_key: format!("{entry_code}:0"), + event: kb_model::DecodedProtocolEvent { + signature: kb_model::Signature("signature".to_string()), + slot: kb_model::Slot(42), + instruction_path: kb_model::InstructionPath("0".to_string()), + program_id: kb_model::ProgramId("program-id".to_string()), + protocol_code: kb_model::ProtocolCode("solana_native".to_string()), + surface_code: kb_model::SurfaceCode(surface_code.to_string()), + event_code: kb_model::EventCode(format!("{surface_code}.{entry_code}")), + event_name: kb_model::EventName(entry_code.to_string()), + event_family, + source_kind: kb_model::EventSourceKind::Instruction, + confidence: kb_model::DecoderConfidence::ManualExact, + }, + payload_json: serde_json::json!({ + "accounts": accounts, + "parameters": parameters, + }), + transaction_failed, + transaction_error: if transaction_failed { + std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]})) + } else { + std::option::Option::None + }, + observation_committed: !transaction_failed, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::Manual, + confidence: kb_model::DecoderConfidence::ManualExact, + evidence: std::vec!["fixture".to_string()], + }, + }; + } + + fn observation( + surface_code: &str, + entry_code: &str, + transaction_failed: bool, + ) -> kb_decoder_api::DecodedObservation { + return observation_with_payload( + surface_code, + entry_code, + kb_model::EventFamily::Lifecycle, + transaction_failed, + serde_json::json!([{"role": "target", "accountKey": "account"}]), + serde_json::json!({"value": 41}), + ); + } + + fn nonce_observation( + entry_code: &str, + family: kb_model::EventFamily, + transaction_failed: bool, + ) -> kb_decoder_api::DecodedObservation { + return observation_with_payload( + super::SYSTEM_SURFACE, + entry_code, + family, + transaction_failed, + serde_json::json!([ + {"role": "nonce_account", "accountKey": "nonce111"}, + {"role": "recipient_account", "accountKey": "recipient111"}, + {"role": "nonce_authority", "accountKey": "authority111"} + ]), + serde_json::json!({ + "authority": "new-authority111", + "lamports": 55, + }), + ); + } + + fn slashing_observation( + entry_code: &str, + transaction_failed: bool, + ) -> kb_decoder_api::DecodedObservation { + let (family, accounts) = if entry_code == "duplicate_block_proof" { + ( + kb_model::EventFamily::Audit, + serde_json::json!([ + {"role": "proof_account", "accountKey": "proof111"}, + {"role": "violation_report", "accountKey": "report111"}, + {"role": "instructions_sysvar", "accountKey": kb_program_ids::SYSVAR_INSTRUCTIONS_PROGRAM_ID}, + {"role": "system_program", "accountKey": kb_program_ids::SYSTEM_PROGRAM_ID} + ]), + ) + } else { + ( + kb_model::EventFamily::Lifecycle, + serde_json::json!([ + {"role": "violation_report", "accountKey": "report111"}, + {"role": "destination", "accountKey": "destination111"} + ]), + ) + }; + return observation_with_payload( + super::SLASHING_SURFACE, + entry_code, + family, + transaction_failed, + accounts, + serde_json::json!({ + "proofType": "duplicate_block", + "violationSlot": 42, + "nodePubkey": "node111", + "reporter": "reporter111", + "destination": "destination111", + "penaltyAppliedByProgram": false, + }), + ); + } + + fn zk_observation( + entry_code: &str, + context_state_requested: bool, + transaction_failed: bool, + ) -> kb_decoder_api::DecodedObservation { + let accounts = if entry_code == "close_context_state" { + serde_json::json!([ + {"role": "proof_context_state", "accountKey": "context111"}, + {"role": "lamport_destination", "accountKey": "destination111"}, + {"role": "context_state_authority", "accountKey": "authority111"} + ]) + } else if context_state_requested { + serde_json::json!([ + {"role": "proof_context_state", "accountKey": "context111"}, + {"role": "context_state_authority", "accountKey": "authority111"} + ]) + } else { + serde_json::json!([]) + }; + let family = if entry_code == "close_context_state" { + kb_model::EventFamily::Lifecycle + } else { + kb_model::EventFamily::Audit + }; + return observation_with_payload( + super::ZK_ELGAMAL_PROOF_SURFACE, + entry_code, + family, + transaction_failed, + accounts, + serde_json::json!({ + "proofType": entry_code, + "contextStateRequested": context_state_requested, + }), + ); + } + + #[test] + fn successful_alt_lifecycle_creates_one_stable_output() { + let materializer = crate::LifecycleMaterializer; + let result = kb_materializer_api::EventMaterializer::materialize( + &materializer, + &observation(super::ADDRESS_LOOKUP_TABLE_SURFACE, "create_lookup_table", false), + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs.len(), 1); + assert_eq!(result.outputs[0].output_key, "address_lookup_table:create_lookup_table:0"); + assert_eq!(result.outputs[0].payload_json["domain"], "address_lookup_table"); + assert_eq!(result.outputs[0].payload_json["loaderSurface"], serde_json::Value::Null); + } + + #[test] + fn successful_loader_lifecycle_creates_program_projection() { + let materializer = crate::LifecycleMaterializer; + let result = kb_materializer_api::EventMaterializer::materialize( + &materializer, + &observation(super::BPF_LOADER_UPGRADEABLE_SURFACE, "deploy_with_max_data_len", false), + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!( + result.outputs[0].output_key, + "program_loader:solana_native_bpf_loader_upgradeable:deploy_with_max_data_len:0" + ); + assert_eq!(result.outputs[0].payload_json["domain"], "program_loader"); + assert_eq!( + result.outputs[0].payload_json["loaderSurface"], + super::BPF_LOADER_UPGRADEABLE_SURFACE + ); + } + + #[test] + fn successful_feature_revoke_creates_feature_gate_projection() { + let materializer = crate::LifecycleMaterializer; + let result = kb_materializer_api::EventMaterializer::materialize( + &materializer, + &observation(super::FEATURE_SURFACE, "revoke_pending_activation", false), + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs[0].output_key, "feature_gate:revoke_pending_activation:0"); + assert_eq!(result.outputs[0].payload_json["domain"], "feature_gate"); + } + + #[test] + fn every_committed_system_account_operation_creates_a_lifecycle_projection() { + let materializer = crate::LifecycleMaterializer; + for (operation, role, transition) in [ + ("create_account", "new_account", "created_allocated_funded_and_assigned"), + ( + "create_account_with_seed", + "new_account", + "derived_account_created_allocated_funded_and_assigned", + ), + ("allocate", "allocated_account", "account_data_space_allocated"), + ("allocate_with_seed", "derived_account", "derived_account_data_space_allocated"), + ( + "create_account_allow_prefund", + "new_account", + "prefunded_account_initialized_allocated_and_assigned", + ), + ] { + let observation = observation_with_payload( + super::SYSTEM_SURFACE, + operation, + kb_model::EventFamily::Lifecycle, + false, + serde_json::json!([ + {"role": role, "accountKey": "target111"}, + {"role": "funding_account", "accountKey": "funding111"}, + {"role": "base_account", "accountKey": "base111"}, + ]), + serde_json::json!({ + "lamports": 55, + "space": 64, + "owner": "owner111", + "seed": "seed", + }), + ); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs[0].output_key, format!("system_account:{operation}:0")); + assert_eq!(result.outputs[0].payload_json["projection"]["targetAccount"], "target111"); + assert_eq!(result.outputs[0].payload_json["projection"]["stateTransition"], transition); + } + } + + #[test] + fn system_account_creation_does_not_duplicate_balance_changes() { + let materializer = crate::LifecycleMaterializer; + let observation = observation_with_payload( + super::SYSTEM_SURFACE, + "create_account", + kb_model::EventFamily::Lifecycle, + false, + serde_json::json!([ + {"role": "funding_account", "accountKey": "funding111"}, + {"role": "new_account", "accountKey": "target111"}, + ]), + serde_json::json!({"lamports": 55, "space": 64, "owner": "owner111"}), + ); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.outputs[0].payload_json["projection"]["requestedLamports"], 55); + assert_eq!( + result.outputs[0].payload_json["projection"]["lamportTransferRepresentedByCoreBalanceChanges"], + true + ); + assert!(result.outputs[0].payload_json["projection"].get("balanceChanges").is_none()); + } + + #[test] + fn every_committed_system_nonce_operation_creates_a_lifecycle_projection() { + let materializer = crate::LifecycleMaterializer; + for (entry_code, family, transition) in [ + ("initialize_nonce_account", kb_model::EventFamily::Lifecycle, "initialized"), + ("advance_nonce_account", kb_model::EventFamily::Lifecycle, "nonce_advanced"), + ("authorize_nonce_account", kb_model::EventFamily::Admin, "authority_changed"), + ( + "upgrade_nonce_account", + kb_model::EventFamily::Lifecycle, + "legacy_version_upgraded", + ), + ( + "withdraw_nonce_account", + kb_model::EventFamily::Lifecycle, + "lamports_withdrawn_possible_closure", + ), + ] { + let observation = nonce_observation(entry_code, family, false); + assert!(kb_materializer_api::EventMaterializer::accepts_observation( + &materializer, + &observation, + )); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs[0].payload_json["domain"], "durable_nonce_account"); + assert_eq!(result.outputs[0].payload_json["projection"]["targetAccount"], "nonce111"); + assert_eq!( + result.outputs[0].payload_json["projection"]["authorityAccount"], + "authority111" + ); + assert_eq!(result.outputs[0].payload_json["projection"]["stateTransition"], transition); + } + } + + #[test] + fn nonce_withdrawal_preserves_conditional_closure_semantics() { + let materializer = crate::LifecycleMaterializer; + let observation = + nonce_observation("withdraw_nonce_account", kb_model::EventFamily::Lifecycle, false); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.outputs[0].payload_json["projection"]["withdrawLamports"], 55); + assert_eq!( + result.outputs[0].payload_json["projection"]["lamportDestination"], + "recipient111" + ); + assert_eq!( + result.outputs[0].payload_json["projection"]["closureSemantics"], + "closes_when_entire_balance_is_withdrawn" + ); + assert_eq!( + result.outputs[0].payload_json["projection"]["transactionFinalAccountStateCaptured"] + .as_bool(), + std::option::Option::Some(false) + ); + } + + #[test] + fn zk_context_is_materialized_only_when_requested() { + let materializer = crate::LifecycleMaterializer; + let without_context = zk_observation("verify_zero_ciphertext", false, false); + assert!(!kb_materializer_api::EventMaterializer::accepts_observation( + &materializer, + &without_context, + )); + let with_context = zk_observation("verify_zero_ciphertext", true, false); + assert!(kb_materializer_api::EventMaterializer::accepts_observation( + &materializer, + &with_context, + )); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &with_context); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs[0].payload_json["domain"], "zk_proof_context"); + assert_eq!(result.outputs[0].payload_json["projection"]["targetAccount"], "context111"); + assert_eq!( + result.outputs[0].payload_json["projection"]["stateTransition"], + "initialized_after_successful_runtime_verification" + ); + assert_eq!( + result.outputs[0].payload_json["projection"]["proofMaterialized"].as_bool(), + std::option::Option::Some(false) + ); + } + + #[test] + fn zk_context_close_creates_a_stable_lifecycle_projection() { + let materializer = crate::LifecycleMaterializer; + let observation = zk_observation("close_context_state", false, false); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs[0].output_key, "zk_proof_context:close_context_state:0"); + assert_eq!( + result.outputs[0].payload_json["projection"]["lamportDestination"], + "destination111" + ); + assert_eq!( + result.outputs[0].payload_json["projection"]["stateTransition"], + "closed_lamports_reclaimed_owner_reset_to_system_program" + ); + } + + #[test] + fn successful_slashing_proof_initializes_violation_report_projection() { + let materializer = crate::LifecycleMaterializer; + let observation = slashing_observation("duplicate_block_proof", false); + assert!(kb_materializer_api::EventMaterializer::accepts_observation( + &materializer, + &observation, + )); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!( + result.outputs[0].output_key, + "slashing_violation_report:duplicate_block_proof:0" + ); + assert_eq!(result.outputs[0].payload_json["domain"], "slashing_violation_report"); + assert_eq!(result.outputs[0].payload_json["projection"]["targetAccount"], "report111"); + assert_eq!(result.outputs[0].payload_json["projection"]["proofMaterialized"], false); + assert_eq!(result.outputs[0].payload_json["projection"]["penaltyAppliedByProgram"], false); + } + + #[test] + fn successful_slashing_close_creates_stable_closure_projection() { + let materializer = crate::LifecycleMaterializer; + let observation = slashing_observation("close_violation_report", false); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!( + result.outputs[0].payload_json["projection"]["stateTransition"], + "closed_after_minimum_retention_lamports_reclaimed_owner_reset_to_system_program" + ); + assert_eq!(result.outputs[0].payload_json["projection"]["minimumRetentionEpochs"], 3); + assert_eq!( + result.outputs[0].payload_json["projection"]["lamportDestination"], + "destination111" + ); + } + + #[test] + fn historical_zk_token_proof_is_not_materialized() { + let materializer = crate::LifecycleMaterializer; + let observation = observation_with_payload( + "solana_native_zk_token_proof", + "close_context_state", + kb_model::EventFamily::Audit, + false, + serde_json::json!([]), + serde_json::json!({}), + ); + assert!(!kb_materializer_api::EventMaterializer::accepts_observation( + &materializer, + &observation, + )); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Ignored); + } + + #[test] + fn loader_write_and_admin_events_are_not_lifecycle_projections() { + let materializer = crate::LifecycleMaterializer; + for entry_code in ["write", "set_authority", "transfer_authority"] { + let observation = observation(super::BPF_LOADER_UPGRADEABLE_SURFACE, entry_code, false); + assert!(!kb_materializer_api::EventMaterializer::accepts_observation( + &materializer, + &observation, + )); + } + } + + #[test] + fn unrelated_lifecycle_is_ignored() { + let materializer = crate::LifecycleMaterializer; + let result = kb_materializer_api::EventMaterializer::materialize( + &materializer, + &observation(super::SYSTEM_SURFACE, "transfer", false), + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Ignored); + } + + #[test] + fn ata_lifecycle_is_owned_by_token_accounts_materializer_only() { + let mut observation = observation("spl_associated_token_account", "create", false); + observation.event.program_id = + kb_model::ProgramId(kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID.to_string()); + assert!(!kb_materializer_api::EventMaterializer::accepts_observation( + &crate::LifecycleMaterializer, + &observation, + )); + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::LifecycleMaterializer, + &observation, + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Ignored); + assert!(result.outputs.is_empty()); + } + + #[test] + fn failed_native_lifecycle_is_refused() { + let materializer = crate::LifecycleMaterializer; + let observations = [ + observation(super::ADDRESS_LOOKUP_TABLE_SURFACE, "close_lookup_table", true), + observation(super::LOADER_V4_SURFACE, "deploy", true), + nonce_observation("authorize_nonce_account", kb_model::EventFamily::Admin, true), + zk_observation("verify_zero_ciphertext", true, true), + slashing_observation("duplicate_block_proof", true), + ]; + for observation in observations { + let policy = + kb_materializer_api::validate_materialization_policy(&materializer, &observation); + assert!(policy.is_err()); + let direct = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(direct.status, kb_materializer_api::MaterializerOutcomeStatus::Refused); + } + } + + #[test] + fn output_serialization_is_deterministic() { + let materializer = crate::LifecycleMaterializer; + let observation = zk_observation("verify_batched_range_proof_u64", true, false); + let first = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + let second = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + let first_json = match serde_json::to_string(&first) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("first lifecycle serialization failed: {error}") + }, + }; + let second_json = match serde_json::to_string(&second) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("second lifecycle serialization failed: {error}") + }, + }; + assert_eq!(first_json, second_json); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_liquidity/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_liquidity/Cargo.toml new file mode 100644 index 0000000..abe0def --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_liquidity/Cargo.toml @@ -0,0 +1,17 @@ +# file: kb_materializer_liquidity/Cargo.toml +# version: 2 + +[package] +name = "kb_materializer_liquidity" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_liquidity/README.md b/migration/khadhroony-bot2-reference/kb_materializer_liquidity/README.md new file mode 100644 index 0000000..fc68554 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_liquidity/README.md @@ -0,0 +1,17 @@ + + + +# kb_materializer_liquidity + +Ce crate matérialise les événements décodés de la famille `liquidity` dans les tables métier correspondantes. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_liquidity/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_liquidity/src/lib.rs new file mode 100644 index 0000000..6e57d7d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_liquidity/src/lib.rs @@ -0,0 +1,12 @@ +// file: kb_materializer_liquidity/src/lib.rs +// version: 1 + +//! Materializer crate for `liquidity`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod materializer; + +/// Initial materializer implementation placeholder. +pub use crate::materializer::LiquidityMaterializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_liquidity/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_liquidity/src/materializer.rs new file mode 100644 index 0000000..4f1d048 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_liquidity/src/materializer.rs @@ -0,0 +1,35 @@ +// file: kb_materializer_liquidity/src/materializer.rs +// version: 5 + +//! Materializer scaffold for the `kb_materializer_liquidity` crate. + +/// Initial materializer implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct LiquidityMaterializer; + +impl kb_materializer_api::Materializer for crate::LiquidityMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_liquidity"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, _event: &kb_model::DecodedProtocolEvent) -> bool { + return false; + } + + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + let materialized = kb_model::MaterializedEvent { + signature: event.signature.clone(), + slot: event.slot, + decoded_family: event.event_family, + materialized_family: kb_model::MaterializedEventFamily::Liquidity, + }; + return std::result::Result::Ok(std::vec![materialized]); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_metadata/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_metadata/Cargo.toml new file mode 100644 index 0000000..216c461 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_metadata/Cargo.toml @@ -0,0 +1,21 @@ +# file: kb_materializer_metadata/Cargo.toml +# version: 5 + +[package] +name = "kb_materializer_metadata" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_spl_token_2022 = { path = "../kb_decoder_spl_token_2022" } +kb_decoder_metadata_metaplex_token_metadata = { path = "../kb_decoder_metadata_metaplex_token_metadata" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_metadata/README.md b/migration/khadhroony-bot2-reference/kb_materializer_metadata/README.md new file mode 100644 index 0000000..74be6e5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_metadata/README.md @@ -0,0 +1,45 @@ + + + +# kb_materializer_metadata + +Ce crate matérialise les états et observations de metadata dans une projection métier canonique. + +## Frontière + +Le matérialisateur ne lit ni RPC, ni wallet, ni Tauri. Il reçoit des snapshots déjà décodés, bornés et accompagnés de leur provenance. + +Une même réalité métier possède une seule projection canonique. Lorsqu’un snapshot de compte owned est disponible, il constitue la source autoritative de l’état final. Les instructions restent des observations de mutation et ne produisent pas une seconde copie concurrente du même état. + +## Metaplex Token Metadata + +`materialize_metaplex_metadata_snapshot` matérialise un `CanonicalMetadataAccountSnapshot` de type `MetadataV1` lorsque sa provenance est commitée. + +La projection conserve notamment : + +- l’identité stable programme/compte ; +- mint et update authority ; +- name, symbol et URI ; +- royalties en basis points ; +- primary sale et mutabilité ; +- creators, collection, uses, token standard et programmable config depuis le payload borné ; +- layout, lifecycle et version canonique ; +- source, slot, write version, signature et indices d’instruction lorsqu’ils sont disponibles. + +La fonction ne télécharge jamais l’URI externe. Sa clé de sortie stable est basée sur l’adresse du compte Metadata, afin qu’un replay ou backfill mette à jour la même identité logique au lieu de créer un doublon par instruction. + +Une provenance non commitée ou un autre layout est refusé. + +## Token-2022 incorporé + +`materialize_token_2022_metadata_snapshot` projette uniquement les extensions `TokenMetadata`, `TokenGroup` et `TokenGroupMember` d’un état Mint déjà parsé et borné. La provenance reste `token_2022_embedded_tlv`; aucun URI externe n’est téléchargé et aucune donnée Metaplex n’est fusionnée implicitement. + +Les conflits éventuels entre Metaplex, Token-2022 embedded metadata et futures sources externes doivent être résolus par une politique de provenance explicite, jamais par écrasement silencieux. + +## Metaplex Token Metadata — `0.4.7-pre.035` + +Les comptes Edition, Master Edition, Edition Marker, Token Record, Delegate Record, Authority Record, Token Owned Escrow et Reservation List sont matérialisés depuis leurs snapshots owned commités. + +Chaque compte conserve une clé stable, son lifecycle actif/expérimental/historique, sa provenance et un propriétaire unique du fait. Les comptes historiques restent matérialisables pendant les backfills et replays, mais leur politique d’exécution demeure `never_historical`. + +Les instructions décodées décrivent les mutations ; elles ne produisent pas une seconde copie concurrente de l’état final du compte. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_metadata/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_metadata/src/lib.rs new file mode 100644 index 0000000..a8e10be --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_metadata/src/lib.rs @@ -0,0 +1,18 @@ +// file: kb_materializer_metadata/src/lib.rs +// version: 5 + +//! Materializer crate for `metadata`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod materializer; + +/// Exposes the materializer type implemented by this crate. +pub use crate::materializer::MetadataMaterializer; +/// Materialize one canonical non-Metadata Metaplex account snapshot. +pub use crate::materializer::materialize_metaplex_account_snapshot; +/// Materialize one canonical Metaplex Metadata account snapshot without instruction-state duplication. +pub use crate::materializer::materialize_metaplex_metadata_snapshot; +/// Materialize Token-2022 embedded metadata and group state from one parsed mint snapshot. +pub use crate::materializer::materialize_token_2022_metadata_snapshot; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_metadata/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_metadata/src/materializer.rs new file mode 100644 index 0000000..02c502e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_metadata/src/materializer.rs @@ -0,0 +1,585 @@ +// file: kb_materializer_metadata/src/materializer.rs +// version: 7 + +//! Materializer scaffold for the `kb_materializer_metadata` crate. + +/// Initial materializer implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct MetadataMaterializer; + +impl kb_materializer_api::Materializer for crate::MetadataMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_metadata"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, _event: &kb_model::DecodedProtocolEvent) -> bool { + return false; + } + + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + let materialized = kb_model::MaterializedEvent { + signature: event.signature.clone(), + slot: event.slot, + decoded_family: event.event_family, + materialized_family: kb_model::MaterializedEventFamily::Metadata, + }; + return std::result::Result::Ok(std::vec![materialized]); + } +} + +/// Materialize embedded Token-2022 metadata, group, and group-member extensions. +pub fn materialize_token_2022_metadata_snapshot( + account_key: &str, + slot: u64, + state: &kb_decoder_spl_token_2022::state::Token2022State, +) -> std::result::Result, String> { + if account_key.is_empty() { + return std::result::Result::Err( + "Token-2022 metadata snapshot requires a non-empty account key".to_string(), + ); + } + if state.kind != kb_decoder_spl_token_2022::state::Token2022StateKind::Mint { + return std::result::Result::Err( + "Token-2022 metadata snapshot requires a parsed mint state".to_string(), + ); + } + let mut outputs = std::vec::Vec::new(); + for entry in &state.extensions { + let projection_kind = match entry.extension_name { + "token_metadata" => std::option::Option::Some("token_metadata"), + "token_group" => std::option::Option::Some("token_group"), + "token_group_member" => std::option::Option::Some("token_group_member"), + _ => std::option::Option::None, + }; + let kind = match projection_kind { + std::option::Option::Some(value) => value, + std::option::Option::None => continue, + }; + outputs.push(kb_materializer_api::MaterializedOutput { + output_key: format!("token_2022_metadata_snapshot:{account_key}:{slot}:{kind}"), + family: kb_model::MaterializedEventFamily::Metadata, + payload_json: serde_json::json!({ + "projectionVersion": 1, + "domain": "spl_token_2022_embedded_metadata", + "projectionSemantics": "authoritative_bounded_mint_extension_snapshot", + "idempotenceKey": format!("token_2022_metadata:{account_key}:{slot}:{kind}"), + "accountKey": account_key, + "slot": slot, + "programId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + "metadataSource": "token_2022_embedded_tlv", + "projectionKind": kind, + "extensionType": entry.extension_type, + "extensionName": entry.extension_name, + "valueHex": entry.value_hex, + "valueFields": entry.value_fields, + "externalUriFetched": false, + "identityAuthority": "mint_address", + "finalAccountStateCaptured": true, + "provenance": { + "processorName": "kb_materializer_metadata", + "processorVersion": env!("CARGO_PKG_VERSION"), + "source": "bounded_token_2022_state_parser" + } + }), + }); + } + return std::result::Result::Ok(outputs); +} + +/// Materialize one canonical Metaplex `MetadataV1` account snapshot. +/// +/// Account snapshots are authoritative for current state. Instruction observations may describe +/// mutations, but must not emit a second competing metadata-state projection. +pub fn materialize_metaplex_metadata_snapshot( + snapshot: &kb_decoder_metadata_metaplex_token_metadata::CanonicalMetadataAccountSnapshot, +) -> std::result::Result { + if !snapshot.provenance.committed { + return std::result::Result::Err( + "Metaplex metadata state materialization requires committed provenance".to_string(), + ); + } + if snapshot.identity.materialization + != kb_decoder_metadata_metaplex_token_metadata::CanonicalMaterializationPolicy::ActiveState + { + return std::result::Result::Err( + "Metaplex MetadataV1 materialization requires active-state policy".to_string(), + ); + } + let metadata = match &snapshot.state { + kb_decoder_metadata_metaplex_token_metadata::CanonicalMetadataAccountState::Metadata( + value, + ) => value, + _ => { + return std::result::Result::Err( + "Metaplex metadata materializer received another account layout".to_string(), + ); + }, + }; + let slot = snapshot.provenance.slot.map(|value| return value.to_string()); + let write_version = snapshot.provenance.write_version.map(|value| return value.to_string()); + let payload = serde_json::json!({ + "projectionVersion": 1, + "domain": "metaplex_token_metadata", + "projectionSemantics": "authoritative_committed_account_snapshot", + "stableId": snapshot.identity.stable_id, + "account": metadata.account, + "mint": metadata.mint, + "updateAuthority": metadata.update_authority, + "name": metadata.name, + "symbol": metadata.symbol, + "uri": metadata.uri, + "sellerFeeBasisPoints": metadata.seller_fee_basis_points, + "primarySaleHappened": metadata.primary_sale_happened, + "isMutable": metadata.is_mutable, + "layout": snapshot.identity.layout, + "layoutDiscriminant": snapshot.identity.discriminant, + "canonicalVersion": snapshot.identity.canonical_version, + "lifecycle": "active", + "materializationPolicy": "active_state", + "metadataSource": "metaplex_owned_account", + "accountState": metadata.payload_json, + "externalUriFetched": false, + "stateAuthority": "owned_account_snapshot", + "instructionProjectionRole": "mutation_observation_only_no_duplicate_state", + "provenance": { + "source": format!("{:?}", snapshot.provenance.source), + "slot": slot, + "writeVersion": write_version, + "signature": snapshot.provenance.signature, + "instructionIndex": snapshot.provenance.instruction_index, + "innerInstructionIndex": snapshot.provenance.inner_instruction_index, + "committed": snapshot.provenance.committed, + "processorName": "kb_materializer_metadata", + "processorVersion": env!("CARGO_PKG_VERSION") + } + }); + return std::result::Result::Ok(kb_materializer_api::MaterializedOutput { + output_key: format!("metaplex_metadata_state:{}", metadata.account), + family: kb_model::MaterializedEventFamily::Metadata, + payload_json: payload, + }); +} + +#[cfg(test)] +mod tests_metaplex_metadata_state { + pub(super) fn snapshot( + committed: bool, + ) -> kb_decoder_metadata_metaplex_token_metadata::CanonicalMetadataAccountSnapshot { + let metadata = kb_decoder_metadata_metaplex_token_metadata::MetadataAccountSnapshot { + account: "metadata111".to_string(), + bump: 1, + mint: "mint111".to_string(), + update_authority: "authority111".to_string(), + name: "Asset".to_string(), + symbol: "AST".to_string(), + uri: "https://example.invalid/asset.json".to_string(), + seller_fee_basis_points: 500, + primary_sale_happened: true, + is_mutable: false, + payload_json: serde_json::json!({ + "creators": [{"address":"creator111","verified":true,"share":100}], + "collection": {"verified":true,"key":"collection111"}, + "uses": {"useMethod":"Single","remaining":"0","total":"1"}, + "tokenStandard": "ProgrammableNonFungible", + "programmableConfig": {"ruleSet":"rules111"} + }), + }; + return kb_decoder_metadata_metaplex_token_metadata::CanonicalMetadataAccountSnapshot::from_metadata( + metadata, + kb_decoder_metadata_metaplex_token_metadata::CanonicalAccountProvenance { + source: kb_decoder_metadata_metaplex_token_metadata::CanonicalObservationSource::Backfill, + slot: std::option::Option::Some(42), + write_version: std::option::Option::Some(7), + signature: std::option::Option::Some("signature111".to_string()), + instruction_index: std::option::Option::Some(3), + inner_instruction_index: std::option::Option::None, + committed, + }, + ); + } + + #[test] + fn account_snapshot_is_one_stable_authoritative_metadata_projection() { + let first = crate::materialize_metaplex_metadata_snapshot(&snapshot(true)); + let second = crate::materialize_metaplex_metadata_snapshot(&snapshot(true)); + assert_eq!(first, second); + assert_eq!( + first.as_ref().map(|value| return value.output_key.as_str()), + std::result::Result::Ok("metaplex_metadata_state:metadata111") + ); + assert_eq!( + first.as_ref().map(|value| return value.payload_json["metadataSource"].clone()), + std::result::Result::Ok(serde_json::json!("metaplex_owned_account")) + ); + assert_eq!( + first + .as_ref() + .map(|value| return value.payload_json["instructionProjectionRole"].clone()), + std::result::Result::Ok(serde_json::json!( + "mutation_observation_only_no_duplicate_state" + )) + ); + assert_eq!( + first + .as_ref() + .map(|value| return value.payload_json["accountState"]["creators"][0]["share"] + .clone()), + std::result::Result::Ok(serde_json::json!(100)) + ); + } + + #[test] + fn uncommitted_or_non_metadata_state_is_rejected() { + assert!(crate::materialize_metaplex_metadata_snapshot(&snapshot(false)).is_err()); + let mut wrong = snapshot(true); + wrong.state = kb_decoder_metadata_metaplex_token_metadata::CanonicalMetadataAccountState::EditionMarker( + kb_decoder_metadata_metaplex_token_metadata::EditionMarkerAccountSnapshot { + account: "marker111".to_string(), + bump: 1, + mint: "mint111".to_string(), + kind: kb_decoder_metadata_metaplex_token_metadata::EditionMarkerAccountKind::EditionMarkerV1, + marker_group: std::option::Option::Some(0), + edition: 1, + byte_index: 0, + bit_mask: 64, + edition_taken: true, + ledger: std::vec![64], + payload_json: serde_json::json!({}), + }, + ); + assert!(crate::materialize_metaplex_metadata_snapshot(&wrong).is_err()); + } +} + +/// Materialize any canonical Metaplex account state other than `MetadataV1`. +/// +/// Every account address maps to one stable state key. Historical layouts remain materializable +/// with an explicit historical lifecycle, while execution policy remains independently disabled. +pub fn materialize_metaplex_account_snapshot( + snapshot: &kb_decoder_metadata_metaplex_token_metadata::CanonicalMetadataAccountSnapshot, +) -> std::result::Result { + if !snapshot.provenance.committed { + return std::result::Result::Err( + "Metaplex account-state materialization requires committed provenance".to_string(), + ); + } + let (projection_kind, fact_owner, state_json) = match &snapshot.state { + kb_decoder_metadata_metaplex_token_metadata::CanonicalMetadataAccountState::Metadata(_) => { + return std::result::Result::Err( + "MetadataV1 must use the dedicated metadata-state materializer".to_string(), + ); + }, + kb_decoder_metadata_metaplex_token_metadata::CanonicalMetadataAccountState::Edition(value) => ( + "edition_state", + "kb_materializer_metadata", + value.payload_json.clone(), + ), + kb_decoder_metadata_metaplex_token_metadata::CanonicalMetadataAccountState::EditionMarker(value) => ( + "edition_marker_state", + "kb_materializer_metadata", + value.payload_json.clone(), + ), + kb_decoder_metadata_metaplex_token_metadata::CanonicalMetadataAccountState::TokenRecord(value) => ( + "programmable_token_state", + "kb_materializer_lifecycle", + value.payload_json.clone(), + ), + kb_decoder_metadata_metaplex_token_metadata::CanonicalMetadataAccountState::DelegateRecord(value) => ( + "delegate_admin_state", + "kb_materializer_admin", + value.payload_json.clone(), + ), + kb_decoder_metadata_metaplex_token_metadata::CanonicalMetadataAccountState::AuthorityRecord(value) => ( + "authority_admin_state", + "kb_materializer_admin", + value.payload_json.clone(), + ), + kb_decoder_metadata_metaplex_token_metadata::CanonicalMetadataAccountState::TokenOwnedEscrow(value) => ( + "token_owned_escrow_state", + "kb_materializer_lifecycle", + value.payload_json.clone(), + ), + kb_decoder_metadata_metaplex_token_metadata::CanonicalMetadataAccountState::ReservationList(value) => ( + "historical_reservation_state", + "kb_materializer_metadata", + value.payload_json.clone(), + ), + }; + let lifecycle = match snapshot.identity.lifecycle { + kb_decoder_metadata_metaplex_token_metadata::CanonicalAccountLifecycle::Active => "active", + kb_decoder_metadata_metaplex_token_metadata::CanonicalAccountLifecycle::ActiveExperimental => { + "active_experimental" + }, + kb_decoder_metadata_metaplex_token_metadata::CanonicalAccountLifecycle::Historical => { + "historical" + }, + }; + let materialization_policy = match snapshot.identity.materialization { + kb_decoder_metadata_metaplex_token_metadata::CanonicalMaterializationPolicy::ActiveState => { + "active_state" + }, + kb_decoder_metadata_metaplex_token_metadata::CanonicalMaterializationPolicy::HistoricalState => { + "historical_state" + }, + }; + let execution_policy = match snapshot.identity.execution { + kb_decoder_metadata_metaplex_token_metadata::CanonicalExecutionPolicy::CurrentCandidate => { + "current_candidate" + }, + kb_decoder_metadata_metaplex_token_metadata::CanonicalExecutionPolicy::ExperimentalCandidate => { + "experimental_candidate" + }, + kb_decoder_metadata_metaplex_token_metadata::CanonicalExecutionPolicy::NeverHistorical => { + "never_historical" + }, + }; + return std::result::Result::Ok(kb_materializer_api::MaterializedOutput { + output_key: format!("metaplex_account_state:{}", snapshot.identity.account), + family: kb_model::MaterializedEventFamily::Metadata, + payload_json: serde_json::json!({ + "projectionVersion": 1, + "domain": "metaplex_token_metadata", + "projectionKind": projection_kind, + "projectionSemantics": "authoritative_committed_owned_account_snapshot", + "stableId": snapshot.identity.stable_id, + "account": snapshot.identity.account, + "programId": snapshot.identity.program_id, + "layout": snapshot.identity.layout, + "layoutDiscriminant": snapshot.identity.discriminant, + "canonicalVersion": snapshot.identity.canonical_version, + "lifecycle": lifecycle, + "materializationPolicy": materialization_policy, + "executionPolicy": execution_policy, + "factOwner": fact_owner, + "metadataSource": "metaplex_owned_account", + "accountState": state_json, + "instructionProjectionRole": "mutation_observation_only_no_duplicate_state", + "provenance": { + "source": format!("{:?}", snapshot.provenance.source), + "slot": snapshot.provenance.slot.map(|value: u64| return value.to_string()), + "writeVersion": snapshot.provenance.write_version.map(|value: u64| return value.to_string()), + "signature": snapshot.provenance.signature, + "instructionIndex": snapshot.provenance.instruction_index, + "innerInstructionIndex": snapshot.provenance.inner_instruction_index, + "committed": snapshot.provenance.committed, + "processorName": "kb_materializer_metadata", + "processorVersion": env!("CARGO_PKG_VERSION") + } + }), + }); +} + +#[cfg(test)] +mod tests_metaplex_account_state { + fn provenance() -> kb_decoder_metadata_metaplex_token_metadata::CanonicalAccountProvenance { + return kb_decoder_metadata_metaplex_token_metadata::CanonicalAccountProvenance { + source: + kb_decoder_metadata_metaplex_token_metadata::CanonicalObservationSource::Backfill, + slot: std::option::Option::Some(90), + write_version: std::option::Option::Some(2), + signature: std::option::Option::None, + instruction_index: std::option::Option::None, + inner_instruction_index: std::option::Option::None, + committed: true, + }; + } + + #[test] + fn historical_edition_and_reservation_are_stable_materializable_state() { + let edition = kb_decoder_metadata_metaplex_token_metadata::CanonicalMetadataAccountSnapshot::from_edition( + kb_decoder_metadata_metaplex_token_metadata::EditionAccountSnapshot { + account: "master-v1".to_string(), + bump: 1, + mint: "mint-v1".to_string(), + kind: kb_decoder_metadata_metaplex_token_metadata::EditionAccountKind::MasterEditionV1, + supply: std::option::Option::Some(3), + max_supply: std::option::Option::Some(10), + parent: std::option::Option::None, + edition: std::option::Option::None, + printing_mint: std::option::Option::Some("printing".to_string()), + one_time_printing_authorization_mint: std::option::Option::Some("authorization".to_string()), + payload_json: serde_json::json!({"supply":"3","maxSupply":"10"}), + }, + provenance(), + ); + let reservation = kb_decoder_metadata_metaplex_token_metadata::CanonicalMetadataAccountSnapshot::from_reservation_list( + kb_decoder_metadata_metaplex_token_metadata::ReservationListAccountSnapshot { + account: "reservation-v2".to_string(), + bump: 2, + kind: kb_decoder_metadata_metaplex_token_metadata::ReservationListAccountKind::V2, + master_edition: "master-v1".to_string(), + resource: "resource".to_string(), + supply_snapshot: std::option::Option::Some(3), + reservations: std::vec::Vec::new(), + total_reservation_spots: 0, + current_reservation_spots: 0, + payload_json: serde_json::json!({"reservations":[]}), + }, + provenance(), + ); + let first = crate::materialize_metaplex_account_snapshot(&edition); + let second = crate::materialize_metaplex_account_snapshot(&reservation); + assert_eq!( + first.as_ref().map(|value| return value.payload_json["lifecycle"].clone()), + std::result::Result::Ok(serde_json::json!("historical")) + ); + assert_eq!( + first.as_ref().map(|value| return value.payload_json["executionPolicy"].clone()), + std::result::Result::Ok(serde_json::json!("never_historical")) + ); + assert_eq!( + second.as_ref().map(|value| return value.output_key.as_str()), + std::result::Result::Ok("metaplex_account_state:reservation-v2") + ); + } + + #[test] + fn programmable_delegate_authority_and_escrow_keep_unique_fact_owners() { + let token = kb_decoder_metadata_metaplex_token_metadata::CanonicalMetadataAccountSnapshot::from_token_record( + kb_decoder_metadata_metaplex_token_metadata::TokenRecordAccountSnapshot { + account: "token-record".to_string(), + bump: 1, + mint: "mint".to_string(), + token: "token".to_string(), + stored_bump: 1, + state: "Locked".to_string(), + rule_set_revision: std::option::Option::Some(4), + delegate: std::option::Option::Some("delegate".to_string()), + delegate_role: std::option::Option::Some("Transfer".to_string()), + locked_transfer: std::option::Option::Some("destination".to_string()), + locked: true, + payload_json: serde_json::json!({"state":"Locked"}), + }, + provenance(), + ); + let output = crate::materialize_metaplex_account_snapshot(&token); + assert_eq!( + output.as_ref().map(|value| return value.payload_json["factOwner"].clone()), + std::result::Result::Ok(serde_json::json!("kb_materializer_lifecycle")) + ); + assert_eq!( + output.as_ref().map(|value| return value.payload_json["projectionKind"].clone()), + std::result::Result::Ok(serde_json::json!("programmable_token_state")) + ); + } + + #[test] + fn uncommitted_and_metadata_snapshots_use_explicit_boundaries() { + let mut marker = kb_decoder_metadata_metaplex_token_metadata::CanonicalMetadataAccountSnapshot::from_edition_marker( + kb_decoder_metadata_metaplex_token_metadata::EditionMarkerAccountSnapshot { + account: "marker".to_string(), + bump: 1, + mint: "mint".to_string(), + kind: kb_decoder_metadata_metaplex_token_metadata::EditionMarkerAccountKind::EditionMarkerV2, + marker_group: std::option::Option::None, + edition: 8, + byte_index: 1, + bit_mask: 128, + edition_taken: true, + ledger: std::vec![0, 128], + payload_json: serde_json::json!({"editionTaken":true}), + }, + provenance(), + ); + marker.provenance.committed = false; + assert!(crate::materialize_metaplex_account_snapshot(&marker).is_err()); + assert!( + crate::materialize_metaplex_account_snapshot( + &super::tests_metaplex_metadata_state::snapshot(true) + ) + .is_err() + ); + } +} + +#[cfg(test)] +mod tests_token_2022_state { + #[test] + fn embedded_metadata_group_and_member_have_distinct_stable_outputs() { + let state = kb_decoder_spl_token_2022::state::Token2022State { + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Mint, + base_fields: serde_json::json!({"supply":"1","decimals":0}), + base_hex: "00".repeat(82), + account_type: std::option::Option::Some(1), + extensions: std::vec![ + kb_decoder_spl_token_2022::state::Token2022TlvEntry { + extension_type: 19, + extension_name: "token_metadata", + value_hex: "01".to_string(), + value_fields: serde_json::json!({"name":"Token","symbol":"TOK","uri":"https://example.invalid/token.json"}), + }, + kb_decoder_spl_token_2022::state::Token2022TlvEntry { + extension_type: 20, + extension_name: "token_group", + value_hex: "02".to_string(), + value_fields: serde_json::json!({"mint":"mint111","size":"1","maxSize":"10"}), + }, + kb_decoder_spl_token_2022::state::Token2022TlvEntry { + extension_type: 21, + extension_name: "token_group_member", + value_hex: "03".to_string(), + value_fields: serde_json::json!({"mint":"mint111","group":"group111","memberNumber":"1"}), + }, + ], + }; + let outputs = crate::materialize_token_2022_metadata_snapshot("mint111", 9, &state); + assert_eq!(outputs.as_ref().map(|values| return values.len()), std::result::Result::Ok(3)); + assert_eq!( + outputs + .as_ref() + .map(|values| return values[0].payload_json["metadataSource"].clone()), + std::result::Result::Ok(serde_json::json!("token_2022_embedded_tlv")) + ); + assert_eq!( + outputs + .as_ref() + .map(|values| return values[0].payload_json["externalUriFetched"].clone()), + std::result::Result::Ok(serde_json::json!(false)) + ); + assert_eq!( + outputs + .as_ref() + .map(|values| return values[1].payload_json["projectionKind"].clone()), + std::result::Result::Ok(serde_json::json!("token_group")) + ); + } + + #[test] + fn snapshot_is_idempotent_filters_unrelated_extensions_and_requires_mint_identity() { + let state = kb_decoder_spl_token_2022::state::Token2022State { + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Mint, + base_fields: serde_json::json!({}), + base_hex: "00".repeat(82), + account_type: std::option::Option::Some(1), + extensions: std::vec![kb_decoder_spl_token_2022::state::Token2022TlvEntry { + extension_type: 1, + extension_name: "transfer_fee_config", + value_hex: "00".to_string(), + value_fields: serde_json::json!({}), + }], + }; + let first = crate::materialize_token_2022_metadata_snapshot("mint111", 9, &state); + let second = crate::materialize_token_2022_metadata_snapshot("mint111", 9, &state); + assert_eq!(first, second); + assert_eq!(first.as_ref().map(|values| return values.len()), std::result::Result::Ok(0)); + assert!(crate::materialize_token_2022_metadata_snapshot("", 9, &state).is_err()); + let account_state = kb_decoder_spl_token_2022::state::Token2022State { + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Account, + base_fields: serde_json::json!({}), + base_hex: "00".repeat(165), + account_type: std::option::Option::None, + extensions: std::vec::Vec::new(), + }; + assert!( + crate::materialize_token_2022_metadata_snapshot("account111", 9, &account_state) + .is_err() + ); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_nft/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_nft/Cargo.toml new file mode 100644 index 0000000..bd36e61 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_nft/Cargo.toml @@ -0,0 +1,17 @@ +# file: kb_materializer_nft/Cargo.toml +# version: 2 + +[package] +name = "kb_materializer_nft" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_nft/README.md b/migration/khadhroony-bot2-reference/kb_materializer_nft/README.md new file mode 100644 index 0000000..0e2a09d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_nft/README.md @@ -0,0 +1,14 @@ + + + +# kb_materializer_nft + +Ce crate matérialise les événements décodés de la famille `nft` dans les tables métier correspondantes. + +## Rôle dans l'écosystème + +Ce module travaille sur les événements décodés et produit des projections métier. Il ne doit pas accéder directement au RPC, au wallet, à Tauri ou aux décodeurs spécifiques. + +## État initial + +Le crate est créé comme point d'ancrage. Les règles de promotion métier, les tables PostgreSQL et les contrôles SQL seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_nft/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_nft/src/lib.rs new file mode 100644 index 0000000..c7d9f30 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_nft/src/lib.rs @@ -0,0 +1,12 @@ +// file: kb_materializer_nft/src/lib.rs +// version: 1 + +//! Materializer crate for `nft`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod materializer; + +/// Exposes the materializer type implemented by this crate. +pub use crate::materializer::NftMaterializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_nft/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_nft/src/materializer.rs new file mode 100644 index 0000000..4e4f111 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_nft/src/materializer.rs @@ -0,0 +1,35 @@ +// file: kb_materializer_nft/src/materializer.rs +// version: 5 + +//! Materializer scaffold for the `kb_materializer_nft` crate. + +/// Initial materializer implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct NftMaterializer; + +impl kb_materializer_api::Materializer for crate::NftMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_nft"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, _event: &kb_model::DecodedProtocolEvent) -> bool { + return false; + } + + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + let materialized = kb_model::MaterializedEvent { + signature: event.signature.clone(), + slot: event.slot, + decoded_family: event.event_family, + materialized_family: kb_model::MaterializedEventFamily::Nft, + }; + return std::result::Result::Ok(std::vec![materialized]); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_oracle/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_oracle/Cargo.toml new file mode 100644 index 0000000..27d4a37 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_oracle/Cargo.toml @@ -0,0 +1,17 @@ +# file: kb_materializer_oracle/Cargo.toml +# version: 2 + +[package] +name = "kb_materializer_oracle" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_oracle/README.md b/migration/khadhroony-bot2-reference/kb_materializer_oracle/README.md new file mode 100644 index 0000000..fb8d557 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_oracle/README.md @@ -0,0 +1,14 @@ + + + +# kb_materializer_oracle + +Ce crate matérialise les événements décodés de la famille `oracle` dans les tables métier correspondantes. + +## Rôle dans l'écosystème + +Ce module travaille sur les événements décodés et produit des projections métier. Il ne doit pas accéder directement au RPC, au wallet, à Tauri ou aux décodeurs spécifiques. + +## État initial + +Le crate est créé comme point d'ancrage. Les règles de promotion métier, les tables PostgreSQL et les contrôles SQL seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_oracle/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_oracle/src/lib.rs new file mode 100644 index 0000000..b548146 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_oracle/src/lib.rs @@ -0,0 +1,12 @@ +// file: kb_materializer_oracle/src/lib.rs +// version: 1 + +//! Materializer crate for `oracle`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod materializer; + +/// Exposes the materializer type implemented by this crate. +pub use crate::materializer::OracleMaterializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_oracle/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_oracle/src/materializer.rs new file mode 100644 index 0000000..f35f7f9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_oracle/src/materializer.rs @@ -0,0 +1,35 @@ +// file: kb_materializer_oracle/src/materializer.rs +// version: 5 + +//! Materializer scaffold for the `kb_materializer_oracle` crate. + +/// Initial materializer implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct OracleMaterializer; + +impl kb_materializer_api::Materializer for crate::OracleMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_oracle"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, _event: &kb_model::DecodedProtocolEvent) -> bool { + return false; + } + + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + let materialized = kb_model::MaterializedEvent { + signature: event.signature.clone(), + slot: event.slot, + decoded_family: event.event_family, + materialized_family: kb_model::MaterializedEventFamily::Oracle, + }; + return std::result::Result::Ok(std::vec![materialized]); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_orderbook/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_orderbook/Cargo.toml new file mode 100644 index 0000000..cd4ea0d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_orderbook/Cargo.toml @@ -0,0 +1,17 @@ +# file: kb_materializer_orderbook/Cargo.toml +# version: 2 + +[package] +name = "kb_materializer_orderbook" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_orderbook/README.md b/migration/khadhroony-bot2-reference/kb_materializer_orderbook/README.md new file mode 100644 index 0000000..e4e98dd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_orderbook/README.md @@ -0,0 +1,17 @@ + + + +# kb_materializer_orderbook + +Ce crate matérialise les événements décodés de la famille `orderbook` dans les tables métier correspondantes. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_orderbook/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_orderbook/src/lib.rs new file mode 100644 index 0000000..28f5c69 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_orderbook/src/lib.rs @@ -0,0 +1,12 @@ +// file: kb_materializer_orderbook/src/lib.rs +// version: 1 + +//! Materializer crate for `orderbook`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod materializer; + +/// Initial materializer implementation placeholder. +pub use crate::materializer::OrderbookMaterializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_orderbook/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_orderbook/src/materializer.rs new file mode 100644 index 0000000..c165fda --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_orderbook/src/materializer.rs @@ -0,0 +1,35 @@ +// file: kb_materializer_orderbook/src/materializer.rs +// version: 5 + +//! Materializer scaffold for the `kb_materializer_orderbook` crate. + +/// Initial materializer implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct OrderbookMaterializer; + +impl kb_materializer_api::Materializer for crate::OrderbookMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_orderbook"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, _event: &kb_model::DecodedProtocolEvent) -> bool { + return false; + } + + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + let materialized = kb_model::MaterializedEvent { + signature: event.signature.clone(), + slot: event.slot, + decoded_family: event.event_family, + materialized_family: kb_model::MaterializedEventFamily::Orderbook, + }; + return std::result::Result::Ok(std::vec![materialized]); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_perpetuals/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_perpetuals/Cargo.toml new file mode 100644 index 0000000..bb61c8c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_perpetuals/Cargo.toml @@ -0,0 +1,17 @@ +# file: kb_materializer_perpetuals/Cargo.toml +# version: 2 + +[package] +name = "kb_materializer_perpetuals" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_perpetuals/README.md b/migration/khadhroony-bot2-reference/kb_materializer_perpetuals/README.md new file mode 100644 index 0000000..38824ca --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_perpetuals/README.md @@ -0,0 +1,14 @@ + + + +# kb_materializer_perpetuals + +Ce crate matérialise les événements décodés de la famille `perpetuals` dans les tables métier correspondantes. + +## Rôle dans l'écosystème + +Ce module travaille sur les événements décodés et produit des projections métier. Il ne doit pas accéder directement au RPC, au wallet, à Tauri ou aux décodeurs spécifiques. + +## État initial + +Le crate est créé comme point d'ancrage. Les règles de promotion métier, les tables PostgreSQL et les contrôles SQL seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_perpetuals/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_perpetuals/src/lib.rs new file mode 100644 index 0000000..6e5a027 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_perpetuals/src/lib.rs @@ -0,0 +1,12 @@ +// file: kb_materializer_perpetuals/src/lib.rs +// version: 1 + +//! Materializer crate for `perpetuals`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod materializer; + +/// Exposes the materializer type implemented by this crate. +pub use crate::materializer::PerpetualsMaterializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_perpetuals/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_perpetuals/src/materializer.rs new file mode 100644 index 0000000..61fc9e6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_perpetuals/src/materializer.rs @@ -0,0 +1,35 @@ +// file: kb_materializer_perpetuals/src/materializer.rs +// version: 5 + +//! Materializer scaffold for the `kb_materializer_perpetuals` crate. + +/// Initial materializer implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct PerpetualsMaterializer; + +impl kb_materializer_api::Materializer for crate::PerpetualsMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_perpetuals"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, _event: &kb_model::DecodedProtocolEvent) -> bool { + return false; + } + + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + let materialized = kb_model::MaterializedEvent { + signature: event.signature.clone(), + slot: event.slot, + decoded_family: event.event_family, + materialized_family: kb_model::MaterializedEventFamily::Perpetuals, + }; + return std::result::Result::Ok(std::vec![materialized]); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_pool_state/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_pool_state/Cargo.toml new file mode 100644 index 0000000..21e92a7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_pool_state/Cargo.toml @@ -0,0 +1,17 @@ +# file: kb_materializer_pool_state/Cargo.toml +# version: 2 + +[package] +name = "kb_materializer_pool_state" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_pool_state/README.md b/migration/khadhroony-bot2-reference/kb_materializer_pool_state/README.md new file mode 100644 index 0000000..0d6b379 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_pool_state/README.md @@ -0,0 +1,17 @@ + + + +# kb_materializer_pool_state + +Ce crate matérialise les événements décodés de la famille `pool_state` dans les tables métier correspondantes. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_pool_state/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_pool_state/src/lib.rs new file mode 100644 index 0000000..310f2e5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_pool_state/src/lib.rs @@ -0,0 +1,12 @@ +// file: kb_materializer_pool_state/src/lib.rs +// version: 1 + +//! Materializer crate for `pool_state`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod materializer; + +/// Initial materializer implementation placeholder. +pub use crate::materializer::PoolStateMaterializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_pool_state/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_pool_state/src/materializer.rs new file mode 100644 index 0000000..67dc454 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_pool_state/src/materializer.rs @@ -0,0 +1,35 @@ +// file: kb_materializer_pool_state/src/materializer.rs +// version: 5 + +//! Materializer scaffold for the `kb_materializer_pool_state` crate. + +/// Initial materializer implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct PoolStateMaterializer; + +impl kb_materializer_api::Materializer for crate::PoolStateMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_pool_state"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, _event: &kb_model::DecodedProtocolEvent) -> bool { + return false; + } + + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + let materialized = kb_model::MaterializedEvent { + signature: event.signature.clone(), + slot: event.slot, + decoded_family: event.event_family, + materialized_family: kb_model::MaterializedEventFamily::PoolState, + }; + return std::result::Result::Ok(std::vec![materialized]); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_rewards/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_rewards/Cargo.toml new file mode 100644 index 0000000..99f1010 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_rewards/Cargo.toml @@ -0,0 +1,17 @@ +# file: kb_materializer_rewards/Cargo.toml +# version: 2 + +[package] +name = "kb_materializer_rewards" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_rewards/README.md b/migration/khadhroony-bot2-reference/kb_materializer_rewards/README.md new file mode 100644 index 0000000..aa6af7e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_rewards/README.md @@ -0,0 +1,17 @@ + + + +# kb_materializer_rewards + +Ce crate matérialise les événements décodés de la famille `rewards` dans les tables métier correspondantes. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_rewards/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_rewards/src/lib.rs new file mode 100644 index 0000000..d7d8b62 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_rewards/src/lib.rs @@ -0,0 +1,12 @@ +// file: kb_materializer_rewards/src/lib.rs +// version: 1 + +//! Materializer crate for `rewards`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod materializer; + +/// Initial materializer implementation placeholder. +pub use crate::materializer::RewardsMaterializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_rewards/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_rewards/src/materializer.rs new file mode 100644 index 0000000..3ad75d6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_rewards/src/materializer.rs @@ -0,0 +1,35 @@ +// file: kb_materializer_rewards/src/materializer.rs +// version: 5 + +//! Materializer scaffold for the `kb_materializer_rewards` crate. + +/// Initial materializer implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct RewardsMaterializer; + +impl kb_materializer_api::Materializer for crate::RewardsMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_rewards"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, _event: &kb_model::DecodedProtocolEvent) -> bool { + return false; + } + + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + let materialized = kb_model::MaterializedEvent { + signature: event.signature.clone(), + slot: event.slot, + decoded_family: event.event_family, + materialized_family: kb_model::MaterializedEventFamily::Reward, + }; + return std::result::Result::Ok(std::vec![materialized]); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_risk/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_risk/Cargo.toml new file mode 100644 index 0000000..bb1ac56 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_risk/Cargo.toml @@ -0,0 +1,21 @@ +# file: kb_materializer_risk/Cargo.toml +# version: 3 + +[package] +name = "kb_materializer_risk" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_risk/README.md b/migration/khadhroony-bot2-reference/kb_materializer_risk/README.md new file mode 100644 index 0000000..e40bf56 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_risk/README.md @@ -0,0 +1,18 @@ + + + +# kb_materializer_risk + +Ce crate matérialise des constats de risque factuels issus d'instructions SPL Token classiques ou ATA commitées. + +## Constats actifs + +- délégation/allowance accordée ou révoquée ; +- compte gelé ou dégelé ; +- autorité changée ou révoquée ; +- multisig structurellement faible lorsque `M = 1` et `N > 1`. +- anti-pattern ATA nested effectivement récupéré par un `RecoverNested` réussi dont les trois dérivations sont valides. + +## Limites + +Les sorties ne contiennent ni score, ni sévérité arbitraire, ni recommandation de trading. Elles séparent le constat on-chain de toute interprétation future et refusent les transactions non commitées. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_risk/src/constants.rs b/migration/khadhroony-bot2-reference/kb_materializer_risk/src/constants.rs new file mode 100644 index 0000000..c772bd7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_risk/src/constants.rs @@ -0,0 +1,11 @@ +// file: kb_materializer_risk/src/constants.rs +// version: 2 + +//! Local constants for fact-only risk projections. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_materializer_risk"; +/// Stable processor name persisted by the replay ledger. +pub(crate) const PROCESSOR_NAME: &str = "spl_token_risk"; +/// Stable projection payload contract version. +pub(crate) const PROJECTION_VERSION: u32 = 2; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_risk/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_risk/src/lib.rs new file mode 100644 index 0000000..95edd73 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_risk/src/lib.rs @@ -0,0 +1,20 @@ +// file: kb_materializer_risk/src/lib.rs +// version: 7 + +//! Materializer crate for stable SPL Token and ATA risk facts. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod materializer; + +/// Stable processor name persisted by the replay ledger. +pub(crate) use crate::constants::PROCESSOR_NAME; +/// Stable projection payload contract version. +pub(crate) use crate::constants::PROJECTION_VERSION; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Stable fact-only SPL Token and ATA risk materializer. +pub use crate::materializer::RiskMaterializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_risk/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_risk/src/materializer.rs new file mode 100644 index 0000000..6cca047 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_risk/src/materializer.rs @@ -0,0 +1,646 @@ +// file: kb_materializer_risk/src/materializer.rs +// version: 10 + +//! Stable fact-only risk projections for committed SPL Token and ATA observations. + +const ACCEPTED_FAMILIES: &[kb_model::EventFamily] = &[ + kb_model::EventFamily::TokenAccount, + kb_model::EventFamily::TokenMint, + kb_model::EventFamily::Admin, + kb_model::EventFamily::Lifecycle, + kb_model::EventFamily::Audit, +]; +const RISK_ENTRIES: &[&str] = &[ + "approve", + "approve_checked", + "revoke", + "freeze_account", + "thaw_account", + "set_authority", + "initialize_multisig", + "initialize_multisig2", + "initialize_default_account_state", + "update_default_account_state", + "enable_required_transfer_memos", + "disable_required_transfer_memos", + "enable_cpi_guard", + "disable_cpi_guard", + "initialize_non_transferable_mint", + "initialize_pausable_config", + "pause", + "resume", + "initialize_permissioned_burn", + "enable_confidential_credits", + "disable_confidential_credits", + "enable_non_confidential_credits", + "disable_non_confidential_credits", +]; + +/// Stable fact-only classic SPL Token risk materializer. +#[derive(Clone, Debug, Default)] +pub struct RiskMaterializer; + +impl kb_materializer_api::Materializer for crate::RiskMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_risk"; + } + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + fn accepts_event(&self, event: &kb_model::DecodedProtocolEvent) -> bool { + return accepts_event(event); + } + fn materialize_event( + &self, + _event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +impl kb_materializer_api::EventMaterializer for crate::RiskMaterializer { + fn identity(&self) -> kb_materializer_api::MaterializerIdentity { + return kb_materializer_api::MaterializerIdentity { + name: crate::PROCESSOR_NAME.to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + }; + } + fn accepted_families(&self) -> &'static [kb_model::EventFamily] { + return ACCEPTED_FAMILIES; + } + fn accepts_observation(&self, observation: &kb_decoder_api::DecodedObservation) -> bool { + return accepts_event(&observation.event); + } + fn transaction_policy( + &self, + _family: kb_model::EventFamily, + ) -> kb_materializer_api::MaterializationTransactionPolicy { + return kb_materializer_api::MaterializationTransactionPolicy::SuccessfulCommittedOnly; + } + fn materialize( + &self, + observation: &kb_decoder_api::DecodedObservation, + ) -> kb_materializer_api::MaterializerExecutionResult { + if !accepts_event(&observation.event) { + return kb_materializer_api::MaterializerExecutionResult::ignored(); + } + if observation.transaction_failed || !observation.observation_committed { + return kb_materializer_api::MaterializerExecutionResult::refused( + "uncommitted_token_risk_fact_refused", + "failed or uncommitted SPL Token or ATA observations cannot create risk facts", + ); + } + let ata = is_ata_recovery(&observation.event); + if ata && !ata_addresses_valid(&observation.payload_json) { + return kb_materializer_api::MaterializerExecutionResult::refused( + "invalid_nested_ata_risk_fact_refused", + "nested ATA risk fact requires all three canonical address validations", + ); + } + let operation = observation.event.event_name.0.as_str(); + let parameters = observation + .payload_json + .get("parameters") + .cloned() + .unwrap_or(serde_json::Value::Null); + let accounts = observation + .payload_json + .get("accounts") + .cloned() + .unwrap_or(serde_json::Value::Null); + let authority = observation + .payload_json + .get("authority") + .cloned() + .unwrap_or(serde_json::Value::Null); + let addresses = observation + .payload_json + .get("addresses") + .cloned() + .unwrap_or(serde_json::Value::Null); + let risk_kind = match risk_kind(operation, ¶meters, &accounts) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return kb_materializer_api::MaterializerExecutionResult::ignored(); + }, + }; + let output = kb_materializer_api::MaterializedOutput { + output_key: if ata { + format!("ata_risk_fact:{risk_kind}:0") + } else { + format!("spl_token_risk_fact:{risk_kind}:0") + }, + family: kb_model::MaterializedEventFamily::Risk, + payload_json: serde_json::json!({ + "projectionVersion": crate::PROJECTION_VERSION, + "domain": if ata { "spl_associated_token_account_risk_fact" } else { "spl_token_risk_fact" }, + "riskKind": risk_kind, + "operation": operation, + "signature": observation.event.signature.0.clone(), + "slot": observation.event.slot.0, + "instructionPath": observation.event.instruction_path.0.clone(), + "programId": observation.event.program_id.0.clone(), + "parameters": parameters, + "accounts": accounts, + "authority": authority, + "addresses": addresses, + "interpretation": { + "score": serde_json::Value::Null, + "severity": serde_json::Value::Null, + "factOnly": true + }, + "committed": true, + "provenance": { + "processorName": crate::PROCESSOR_NAME, + "processorVersion": env!("CARGO_PKG_VERSION"), + "sourceEventKey": observation.event_key.clone() + } + }), + }; + tracing::debug!(target: crate::TRACING_TARGET, action = "materialize_token_risk_fact", risk_kind, operation, ata, "materialize committed SPL Token or ATA risk fact"); + return kb_materializer_api::MaterializerExecutionResult { + status: kb_materializer_api::MaterializerOutcomeStatus::Inserted, + outputs: std::vec![output], + diagnostics: std::vec::Vec::new(), + }; + } +} + +fn accepts_event(event: &kb_model::DecodedProtocolEvent) -> bool { + let token_program = (event.program_id.0 == kb_program_ids::SPL_TOKEN_PROGRAM_ID + && event.surface_code.0 == "spl_token") + || (event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID + && event.surface_code.0 == "spl_token_2022"); + let token_risk = token_program && RISK_ENTRIES.contains(&event.event_name.0.as_str()); + return ACCEPTED_FAMILIES.contains(&event.event_family) + && (token_risk || is_ata_recovery(event)); +} + +fn is_ata_recovery(event: &kb_model::DecodedProtocolEvent) -> bool { + return event.program_id.0 == kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID + && event.surface_code.0 == "spl_associated_token_account" + && event.event_family == kb_model::EventFamily::Lifecycle + && event.event_name.0 == "recover_nested"; +} + +fn ata_addresses_valid(payload: &serde_json::Value) -> bool { + let addresses = match payload.get("addresses") { + std::option::Option::Some(value) => value, + std::option::Option::None => return false, + }; + let token_supported = addresses + .get("tokenProgram") + .and_then(|value| return value.get("supported")) + .and_then(serde_json::Value::as_bool) + == std::option::Option::Some(true); + let token_program_id = addresses + .get("tokenProgram") + .and_then(|value| return value.get("programId")) + .and_then(serde_json::Value::as_str); + let token_program_exact = matches!( + token_program_id, + std::option::Option::Some(kb_program_ids::SPL_TOKEN_PROGRAM_ID) + | std::option::Option::Some(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID) + ); + return token_supported + && token_program_exact + && [ + "ownerAssociatedTokenAccount", + "nestedAssociatedTokenAccount", + "walletNestedMintAssociatedTokenAccount", + ] + .iter() + .all(|field| { + let address = match addresses.get(*field) { + std::option::Option::Some(value) => value, + std::option::Option::None => return false, + }; + return address + .get("observed") + .and_then(serde_json::Value::as_str) + .is_some_and(|value| return !value.is_empty()) + && address.get("valid").and_then(serde_json::Value::as_bool) + == std::option::Option::Some(true); + }); +} + +fn risk_kind( + operation: &str, + parameters: &serde_json::Value, + accounts: &serde_json::Value, +) -> std::option::Option<&'static str> { + return match operation { + "recover_nested" => std::option::Option::Some("nested_ata_anti_pattern_recovered"), + "approve" | "approve_checked" => std::option::Option::Some("delegation_allowance_granted"), + "revoke" => std::option::Option::Some("delegation_revoked"), + "freeze_account" => std::option::Option::Some("account_frozen"), + "thaw_account" => std::option::Option::Some("account_thawed"), + "set_authority" + if parameters.get("newAuthority").is_some_and(serde_json::Value::is_null) => + { + std::option::Option::Some("authority_revoked") + }, + "set_authority" => std::option::Option::Some("authority_changed"), + "initialize_default_account_state" | "update_default_account_state" => { + std::option::Option::Some("default_account_state_configured") + }, + "enable_required_transfer_memos" => { + std::option::Option::Some("required_transfer_memos_enabled") + }, + "disable_required_transfer_memos" => { + std::option::Option::Some("required_transfer_memos_disabled") + }, + "enable_cpi_guard" => std::option::Option::Some("cpi_guard_enabled"), + "disable_cpi_guard" => std::option::Option::Some("cpi_guard_disabled"), + "initialize_non_transferable_mint" => { + std::option::Option::Some("non_transferable_mint_configured") + }, + "initialize_pausable_config" => std::option::Option::Some("pausable_mint_configured"), + "pause" => std::option::Option::Some("mint_activity_paused"), + "resume" => std::option::Option::Some("mint_activity_resumed"), + "initialize_permissioned_burn" => std::option::Option::Some("permissioned_burn_configured"), + "enable_confidential_credits" => std::option::Option::Some("confidential_credits_enabled"), + "disable_confidential_credits" => { + std::option::Option::Some("confidential_credits_disabled") + }, + "enable_non_confidential_credits" => { + std::option::Option::Some("non_confidential_credits_enabled") + }, + "disable_non_confidential_credits" => { + std::option::Option::Some("non_confidential_credits_disabled") + }, + "initialize_multisig" | "initialize_multisig2" => { + let m = parameters.get("m").and_then(serde_json::Value::as_u64).unwrap_or(0); + let n = role_count(accounts, "multisig_member"); + if m == 1 && n > 1 { + std::option::Option::Some("weak_multisig_threshold") + } else { + std::option::Option::None + } + }, + _ => std::option::Option::None, + }; +} + +fn role_count(accounts: &serde_json::Value, role: &str) -> usize { + return accounts + .as_array() + .map(|rows| { + return rows + .iter() + .filter(|row| { + return row.get("role").and_then(serde_json::Value::as_str) + == std::option::Option::Some(role); + }) + .count(); + }) + .unwrap_or(0); +} + +#[cfg(test)] +mod tests { + fn observation( + entry: &str, + family: kb_model::EventFamily, + parameters: serde_json::Value, + failed: bool, + ) -> kb_decoder_api::DecodedObservation { + return kb_decoder_api::DecodedObservation { + event_key: format!("token:{entry}:0"), + event: kb_model::DecodedProtocolEvent { + signature: kb_model::Signature("signature".to_string()), + slot: kb_model::Slot(1), + instruction_path: kb_model::InstructionPath("0".to_string()), + program_id: kb_model::ProgramId(kb_program_ids::SPL_TOKEN_PROGRAM_ID.to_string()), + protocol_code: kb_model::ProtocolCode("spl_token".to_string()), + surface_code: kb_model::SurfaceCode("spl_token".to_string()), + event_code: kb_model::EventCode(format!("spl_token.{entry}")), + event_name: kb_model::EventName(entry.to_string()), + event_family: family, + source_kind: kb_model::EventSourceKind::Instruction, + confidence: kb_model::DecoderConfidence::ManualExact, + }, + payload_json: serde_json::json!({"parameters":parameters,"accounts":[{"role":"multisig_member"},{"role":"multisig_member"}],"authority":{}}), + transaction_failed: failed, + transaction_error: std::option::Option::None, + observation_committed: !failed, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::Manual, + confidence: kb_model::DecoderConfidence::ManualExact, + evidence: std::vec!["fixture".to_string()], + }, + }; + } + + fn token_2022_observation( + entry: &str, + family: kb_model::EventFamily, + parameters: serde_json::Value, + failed: bool, + ) -> kb_decoder_api::DecodedObservation { + let mut observation = observation(entry, family, parameters, failed); + observation.event.program_id = + kb_model::ProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string()); + observation.event.protocol_code = kb_model::ProtocolCode("spl_token_2022".to_string()); + observation.event.surface_code = kb_model::SurfaceCode("spl_token_2022".to_string()); + observation.event.event_code = kb_model::EventCode(format!("spl_token_2022.{entry}")); + observation.event_key = format!("token_2022:{entry}:0"); + return observation; + } + + fn nested_ata_observation(failed: bool, valid: bool) -> kb_decoder_api::DecodedObservation { + return kb_decoder_api::DecodedObservation { + event_key: "ata:recover_nested:0".to_string(), + event: kb_model::DecodedProtocolEvent { + signature: kb_model::Signature("signature".to_string()), + slot: kb_model::Slot(2), + instruction_path: kb_model::InstructionPath("1/0".to_string()), + program_id: kb_model::ProgramId( + kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID.to_string(), + ), + protocol_code: kb_model::ProtocolCode("spl_associated_token_account".to_string()), + surface_code: kb_model::SurfaceCode("spl_associated_token_account".to_string()), + event_code: kb_model::EventCode( + "spl_associated_token_account.recover_nested".to_string(), + ), + event_name: kb_model::EventName("recover_nested".to_string()), + event_family: kb_model::EventFamily::Lifecycle, + source_kind: kb_model::EventSourceKind::InnerInstruction, + confidence: kb_model::DecoderConfidence::ManualExact, + }, + payload_json: serde_json::json!({ + "addresses": { + "tokenProgram": { + "programId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + "kind": "token_2022", + "supported": true + }, + "ownerAssociatedTokenAccount": {"observed":"ownerAta","valid":valid}, + "nestedAssociatedTokenAccount": {"observed":"nestedAta","valid":valid}, + "walletNestedMintAssociatedTokenAccount": {"observed":"destinationAta","valid":valid} + } + }), + transaction_failed: failed, + transaction_error: std::option::Option::None, + observation_committed: !failed, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::Manual, + confidence: kb_model::DecoderConfidence::ManualExact, + evidence: std::vec!["fixture".to_string()], + }, + }; + } + + #[test] + fn approval_and_authority_revocation_are_fact_only_without_score() { + for (entry, family, parameters, expected) in [ + ( + "approve_checked", + kb_model::EventFamily::TokenAccount, + serde_json::json!({"amountRaw":"10"}), + "delegation_allowance_granted", + ), + ( + "set_authority", + kb_model::EventFamily::Admin, + serde_json::json!({"newAuthority":null}), + "authority_revoked", + ), + ] { + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::RiskMaterializer, + &observation(entry, family, parameters, false), + ); + assert_eq!(result.outputs[0].payload_json["riskKind"], expected); + assert!(result.outputs[0].payload_json["interpretation"]["score"].is_null()); + } + } + + #[test] + fn token_2022_revocation_is_owned_once_without_an_invented_score() { + let observation = token_2022_observation( + "set_authority", + kb_model::EventFamily::Admin, + serde_json::json!({"newAuthority":null}), + false, + ); + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::RiskMaterializer, + &observation, + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs.len(), 1); + assert_eq!(result.outputs[0].payload_json["riskKind"], "authority_revoked"); + assert!(result.outputs[0].payload_json["interpretation"]["score"].is_null()); + } + + #[test] + fn only_structurally_weak_multisig_creates_risk_fact() { + let weak = observation( + "initialize_multisig2", + kb_model::EventFamily::Admin, + serde_json::json!({"m":1}), + false, + ); + let strong = observation( + "initialize_multisig2", + kb_model::EventFamily::Admin, + serde_json::json!({"m":2}), + false, + ); + assert_eq!( + kb_materializer_api::EventMaterializer::materialize(&crate::RiskMaterializer, &weak) + .status, + kb_materializer_api::MaterializerOutcomeStatus::Inserted + ); + assert_eq!( + kb_materializer_api::EventMaterializer::materialize(&crate::RiskMaterializer, &strong) + .status, + kb_materializer_api::MaterializerOutcomeStatus::Ignored + ); + } + + #[test] + fn failed_risk_fact_is_refused() { + let failed = observation( + "freeze_account", + kb_model::EventFamily::TokenAccount, + serde_json::json!({}), + true, + ); + assert_eq!( + kb_materializer_api::EventMaterializer::materialize(&crate::RiskMaterializer, &failed) + .status, + kb_materializer_api::MaterializerOutcomeStatus::Refused + ); + } + + #[test] + fn committed_nested_ata_recovery_creates_one_fact_without_score() { + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::RiskMaterializer, + &nested_ata_observation(false, true), + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs.len(), 1); + assert_eq!( + result.outputs[0].output_key, + "ata_risk_fact:nested_ata_anti_pattern_recovered:0" + ); + assert_eq!(result.outputs[0].payload_json["riskKind"], "nested_ata_anti_pattern_recovered"); + assert_eq!( + result.outputs[0].payload_json["domain"], + "spl_associated_token_account_risk_fact" + ); + assert!(result.outputs[0].payload_json["interpretation"]["score"].is_null()); + assert!(result.outputs[0].payload_json["interpretation"]["severity"].is_null()); + } + + #[test] + fn failed_or_invalid_nested_ata_recovery_creates_no_risk_fact() { + for observation in + [nested_ata_observation(true, true), nested_ata_observation(false, false)] + { + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::RiskMaterializer, + &observation, + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Refused); + assert!(result.outputs.is_empty()); + } + } + + #[test] + fn ata_matrix_declares_the_single_compiled_risk_fact() { + let matrix: serde_json::Value = match serde_json::from_str(include_str!( + "../../docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json" + )) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("ATA matrix is invalid: {error}"), + }; + let instructions = match matrix["instructions"].as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("ATA matrix instructions are absent"), + }; + let recover = match instructions.iter().find(|row| { + return row.get("name").and_then(serde_json::Value::as_str) + == std::option::Option::Some("recover_nested"); + }) { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("RecoverNested matrix entry is absent"), + }; + assert!(recover["authorizedProjections"].as_array().is_some_and(|values| { + return values.iter().any(|value| { + return value == "risk:nested_ata_anti_pattern_recovered"; + }); + })); + assert_eq!( + matrix["materializationContract"]["projectionOwners"][1]["facts"], + serde_json::json!(["nested_ata_anti_pattern_recovered"]) + ); + } + + #[test] + fn token_2022_compliance_toggles_are_projected_as_fact_only_risk_rows() { + for (entry, expected) in [ + ("initialize_default_account_state", "default_account_state_configured"), + ("enable_required_transfer_memos", "required_transfer_memos_enabled"), + ("disable_required_transfer_memos", "required_transfer_memos_disabled"), + ("enable_cpi_guard", "cpi_guard_enabled"), + ("disable_cpi_guard", "cpi_guard_disabled"), + ] { + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::RiskMaterializer, + &token_2022_observation( + entry, + kb_model::EventFamily::Audit, + serde_json::json!({}), + false, + ), + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs[0].payload_json["riskKind"], expected); + assert_eq!(result.outputs[0].payload_json["interpretation"]["factOnly"], true); + assert!(result.outputs[0].payload_json["interpretation"]["score"].is_null()); + } + } + + #[test] + fn token_2022_restriction_and_pause_facts_have_unique_stable_kinds() { + for (entry, family, expected) in [ + ( + "initialize_non_transferable_mint", + kb_model::EventFamily::TokenMint, + "non_transferable_mint_configured", + ), + ( + "initialize_pausable_config", + kb_model::EventFamily::TokenAccount, + "pausable_mint_configured", + ), + ("pause", kb_model::EventFamily::TokenAccount, "mint_activity_paused"), + ("resume", kb_model::EventFamily::TokenAccount, "mint_activity_resumed"), + ( + "initialize_permissioned_burn", + kb_model::EventFamily::TokenAccount, + "permissioned_burn_configured", + ), + ] { + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::RiskMaterializer, + &token_2022_observation(entry, family, serde_json::json!({}), false), + ); + assert_eq!(result.outputs[0].payload_json["riskKind"], expected); + } + } + #[test] + fn confidential_credit_toggles_are_fact_only_and_keep_wire_parameters() { + for (entry, credit_kind, enabled, expected) in [ + ( + "enable_confidential_credits", + "confidential", + true, + "confidential_credits_enabled", + ), + ( + "disable_confidential_credits", + "confidential", + false, + "confidential_credits_disabled", + ), + ( + "enable_non_confidential_credits", + "non_confidential", + true, + "non_confidential_credits_enabled", + ), + ( + "disable_non_confidential_credits", + "non_confidential", + false, + "non_confidential_credits_disabled", + ), + ] { + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::RiskMaterializer, + &token_2022_observation( + entry, + kb_model::EventFamily::TokenAccount, + serde_json::json!({ + "extension":"confidential_transfer", + "creditKind":credit_kind, + "enabled":enabled, + }), + false, + ), + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs.len(), 1); + assert_eq!(result.outputs[0].payload_json["riskKind"], expected); + assert_eq!(result.outputs[0].payload_json["parameters"]["creditKind"], credit_kind); + assert_eq!(result.outputs[0].payload_json["parameters"]["enabled"], enabled); + assert_eq!(result.outputs[0].payload_json["interpretation"]["factOnly"], true); + assert!(result.outputs[0].payload_json["interpretation"]["score"].is_null()); + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_routing/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_routing/Cargo.toml new file mode 100644 index 0000000..ea740d2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_routing/Cargo.toml @@ -0,0 +1,17 @@ +# file: kb_materializer_routing/Cargo.toml +# version: 2 + +[package] +name = "kb_materializer_routing" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_routing/README.md b/migration/khadhroony-bot2-reference/kb_materializer_routing/README.md new file mode 100644 index 0000000..018841b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_routing/README.md @@ -0,0 +1,14 @@ + + + +# kb_materializer_routing + +Ce crate matérialise les événements décodés de la famille `routing` dans les tables métier correspondantes. + +## Rôle dans l'écosystème + +Ce module travaille sur les événements décodés et produit des projections métier. Il ne doit pas accéder directement au RPC, au wallet, à Tauri ou aux décodeurs spécifiques. + +## État initial + +Le crate est créé comme point d'ancrage. Les règles de promotion métier, les tables PostgreSQL et les contrôles SQL seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_routing/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_routing/src/lib.rs new file mode 100644 index 0000000..8010737 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_routing/src/lib.rs @@ -0,0 +1,12 @@ +// file: kb_materializer_routing/src/lib.rs +// version: 1 + +//! Materializer crate for `routing`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod materializer; + +/// Exposes the materializer type implemented by this crate. +pub use crate::materializer::RoutingMaterializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_routing/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_routing/src/materializer.rs new file mode 100644 index 0000000..e43562f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_routing/src/materializer.rs @@ -0,0 +1,35 @@ +// file: kb_materializer_routing/src/materializer.rs +// version: 5 + +//! Materializer scaffold for the `kb_materializer_routing` crate. + +/// Initial materializer implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct RoutingMaterializer; + +impl kb_materializer_api::Materializer for crate::RoutingMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_routing"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, _event: &kb_model::DecodedProtocolEvent) -> bool { + return false; + } + + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + let materialized = kb_model::MaterializedEvent { + signature: event.signature.clone(), + slot: event.slot, + decoded_family: event.event_family, + materialized_family: kb_model::MaterializedEventFamily::Routing, + }; + return std::result::Result::Ok(std::vec![materialized]); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_staking/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_staking/Cargo.toml new file mode 100644 index 0000000..66a1dee --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_staking/Cargo.toml @@ -0,0 +1,20 @@ +# file: kb_materializer_staking/Cargo.toml +# version: 3 + +[package] +name = "kb_materializer_staking" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } +serde_json.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_staking/README.md b/migration/khadhroony-bot2-reference/kb_materializer_staking/README.md new file mode 100644 index 0000000..9938408 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_staking/README.md @@ -0,0 +1,56 @@ + + + +# kb_materializer_staking + +Ce crate produit des projections instructionnelles stables pour les observations natives Stake et Vote décodées par `kb_decoder_solana_core`. + +## Statut + +`0.4.1-pre.021` active le matérialiseur sous l'identité : + +```text +solana_native_staking +``` + +Le matérialiseur est enregistré par `kb_app_demo::demo_decode_replay` avec les autres matérialiseurs natifs. Il applique `SuccessfulCommittedOnly` : une transaction échouée ou une observation non commitée reste décodable, mais ne produit aucune sortie métier mutable. + +## Surfaces couvertes + +### Stake Program + +Les projections sont instructionnelles et ne prétendent pas reconstruire l'état final du compte Stake. + +| Domaine | Entrées | +|-------------------|-----------------------------------------------------------------------------------| +| `stake_account` | initialize, initialize_checked, delegate_stake, deactivate, deactivate_delinquent | +| `stake_value` | split, withdraw, merge, move_stake, move_lamports | +| `stake_authority` | authorize, authorize_with_seed, authorize_checked, authorize_checked_with_seed | +| `stake_lockup` | set_lockup, set_lockup_checked | + +`redelegate` reste ignoré : l'instruction est conservée par le décodeur comme intention historique officiellement désactivée, mais elle ne doit pas produire une mutation staking réussie. + +### Vote Program + +| Domaine | Entrées | +|------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `vote_account` | initialize_account, initialize_account_v2 | +| `vote_authority` | authorize, authorize_checked, authorize_with_seed, authorize_checked_with_seed | +| `vote_admin` | update_validator_identity, update_commission, update_commission_collector, update_commission_bps | +| `vote_state` | vote, vote_switch, update_vote_state, update_vote_state_switch, compact_update_vote_state, compact_update_vote_state_switch, tower_sync, tower_sync_switch | +| `vote_value` | withdraw | +| `vote_reward` | deposit_delegator_rewards | + +`deposit_delegator_rewards` produit une sortie de famille `Reward`; les autres sorties produisent une famille matérialisée `Staking`. + +## Limites explicites + +Le crate ne reconstruit pas : + +- activation/désactivation effective d'un stake par epoch ; +- crédits Vote cumulés ; +- statut final exact d'un compte après withdraw ; +- état antérieur et état suivant du compte ; +- sysvars historiques non portées par le core replay. + +Ces projections doivent donc être lues comme des événements instructionnels commités. Les snapshots d'état final seront un futur contrat distinct lorsque le replay core transportera les données de comptes et sysvars nécessaires. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_staking/src/constants.rs b/migration/khadhroony-bot2-reference/kb_materializer_staking/src/constants.rs new file mode 100644 index 0000000..094235d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_staking/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_materializer_staking/src/constants.rs +// version: 1 + +//! Local constants for the `kb_materializer_staking` crate. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_materializer_staking"; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_staking/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_staking/src/lib.rs new file mode 100644 index 0000000..e34d394 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_staking/src/lib.rs @@ -0,0 +1,16 @@ +// file: kb_materializer_staking/src/lib.rs +// version: 4 + +//! Materializer crate for instruction-level native Stake and Vote projections. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod materializer; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Exposes the materializer type implemented by this crate. +pub use crate::materializer::StakingMaterializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_staking/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_staking/src/materializer.rs new file mode 100644 index 0000000..e99a752 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_staking/src/materializer.rs @@ -0,0 +1,886 @@ +// file: kb_materializer_staking/src/materializer.rs +// version: 7 + +//! Instruction-level staking projections for native Stake and Vote observations. + +const ACCEPTED_FAMILIES: &[kb_model::EventFamily] = &[ + kb_model::EventFamily::Lifecycle, + kb_model::EventFamily::Admin, + kb_model::EventFamily::Staking, + kb_model::EventFamily::Reward, +]; +const STAKE_SURFACE: &str = "solana_native_stake"; +const VOTE_SURFACE: &str = "solana_native_vote"; +const STAKE_ACCOUNT_ENTRIES: &[&str] = &[ + "initialize", + "initialize_checked", + "delegate_stake", + "deactivate", + "deactivate_delinquent", +]; +const STAKE_VALUE_ENTRIES: &[&str] = &["split", "withdraw", "merge", "move_stake", "move_lamports"]; +const STAKE_AUTHORITY_ENTRIES: &[&str] = &[ + "authorize", + "authorize_with_seed", + "authorize_checked", + "authorize_checked_with_seed", +]; +const STAKE_LOCKUP_ENTRIES: &[&str] = &["set_lockup", "set_lockup_checked"]; +const VOTE_ACCOUNT_ENTRIES: &[&str] = &["initialize_account", "initialize_account_v2"]; +const VOTE_AUTHORITY_ENTRIES: &[&str] = &[ + "authorize", + "authorize_checked", + "authorize_with_seed", + "authorize_checked_with_seed", +]; +const VOTE_ADMIN_ENTRIES: &[&str] = &[ + "update_validator_identity", + "update_commission", + "update_commission_collector", + "update_commission_bps", +]; +const VOTE_STATE_ENTRIES: &[&str] = &[ + "vote", + "vote_switch", + "update_vote_state", + "update_vote_state_switch", + "compact_update_vote_state", + "compact_update_vote_state_switch", + "tower_sync", + "tower_sync_switch", +]; +const VOTE_VALUE_ENTRIES: &[&str] = &["withdraw"]; +const VOTE_REWARD_ENTRIES: &[&str] = &["deposit_delegator_rewards"]; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum ProjectionKind { + StakeAccount, + StakeValue, + StakeAuthority, + StakeLockup, + VoteAccount, + VoteAuthority, + VoteAdmin, + VoteState, + VoteValue, + VoteReward, +} + +impl ProjectionKind { + fn domain(self) -> &'static str { + return match self { + Self::StakeAccount => "stake_account", + Self::StakeValue => "stake_value", + Self::StakeAuthority => "stake_authority", + Self::StakeLockup => "stake_lockup", + Self::VoteAccount => "vote_account", + Self::VoteAuthority => "vote_authority", + Self::VoteAdmin => "vote_admin", + Self::VoteState => "vote_state", + Self::VoteValue => "vote_value", + Self::VoteReward => "vote_reward", + }; + } + + fn family(self) -> kb_model::MaterializedEventFamily { + return match self { + Self::VoteReward => kb_model::MaterializedEventFamily::Reward, + _ => kb_model::MaterializedEventFamily::Staking, + }; + } + + fn output_key(self, operation: &str) -> std::string::String { + return format!("{}:{operation}:0", self.domain()); + } +} + +/// Stable staking materializer for committed native Stake and Vote observations. +#[derive(Clone, Debug, Default)] +pub struct StakingMaterializer; + +impl kb_materializer_api::Materializer for crate::StakingMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_staking"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, event: &kb_model::DecodedProtocolEvent) -> bool { + return family_is_accepted(event.event_family) + && projection(event.surface_code.0.as_str(), event.event_name.0.as_str()).is_some(); + } + + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + if !kb_materializer_api::Materializer::accepts_event(self, event) { + return std::result::Result::Ok(std::vec::Vec::new()); + } + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +impl kb_materializer_api::EventMaterializer for crate::StakingMaterializer { + fn identity(&self) -> kb_materializer_api::MaterializerIdentity { + return kb_materializer_api::MaterializerIdentity { + name: "solana_native_staking".to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + }; + } + + fn accepted_families(&self) -> &'static [kb_model::EventFamily] { + return ACCEPTED_FAMILIES; + } + + fn accepts_observation(&self, observation: &kb_decoder_api::DecodedObservation) -> bool { + return family_is_accepted(observation.event.event_family) + && projection( + observation.event.surface_code.0.as_str(), + observation.event.event_name.0.as_str(), + ) + .is_some(); + } + + fn transaction_policy( + &self, + _family: kb_model::EventFamily, + ) -> kb_materializer_api::MaterializationTransactionPolicy { + return kb_materializer_api::MaterializationTransactionPolicy::SuccessfulCommittedOnly; + } + + fn materialize( + &self, + observation: &kb_decoder_api::DecodedObservation, + ) -> kb_materializer_api::MaterializerExecutionResult { + let projection = match projection( + observation.event.surface_code.0.as_str(), + observation.event.event_name.0.as_str(), + ) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + tracing::debug!( + target: crate::TRACING_TARGET, + action = "materialize_staking", + surface_code = %observation.event.surface_code.0.as_str(), + entry_code = %observation.event.event_name.0.as_str(), + accepted = false, + "ignore observation outside supported native staking projections" + ); + return kb_materializer_api::MaterializerExecutionResult::ignored(); + }, + }; + if !family_is_accepted(observation.event.event_family) { + return kb_materializer_api::MaterializerExecutionResult::ignored(); + } + if observation.transaction_failed || !observation.observation_committed { + tracing::debug!( + target: crate::TRACING_TARGET, + action = "materialize_staking", + surface_code = %observation.event.surface_code.0.as_str(), + entry_code = %observation.event.event_name.0.as_str(), + committed = observation.observation_committed, + transaction_failed = observation.transaction_failed, + "refuse uncommitted native staking observation" + ); + return kb_materializer_api::MaterializerExecutionResult::refused( + "failed_transaction_staking_refused", + "failed or uncommitted native Stake/Vote observations cannot create staking outputs", + ); + } + let accounts = observation + .payload_json + .get("accounts") + .cloned() + .unwrap_or(serde_json::Value::Null); + let parameters = observation + .payload_json + .get("parameters") + .cloned() + .unwrap_or(serde_json::Value::Null); + let operation = observation.event.event_name.0.clone(); + let output = kb_materializer_api::MaterializedOutput { + output_key: projection.output_key(operation.as_str()), + family: projection.family(), + payload_json: serde_json::json!({ + "projectionVersion": 1, + "projectionSemantics": "committed_instruction_staking_event", + "domain": projection.domain(), + "operation": operation, + "programId": observation.event.program_id.0.clone(), + "signature": observation.event.signature.0.clone(), + "slot": observation.event.slot.0, + "instructionPath": observation.event.instruction_path.0.clone(), + "transactionSucceeded": true, + "accounts": accounts, + "parameters": parameters, + "projection": projection_details( + projection, + &accounts, + ¶meters, + observation.event.event_name.0.as_str(), + ), + }), + }; + tracing::debug!( + target: crate::TRACING_TARGET, + action = "materialize_staking", + surface_code = %observation.event.surface_code.0.as_str(), + entry_code = %observation.event.event_name.0.as_str(), + projection_domain = projection.domain(), + output_count = 1_usize, + "materialize committed native staking observation" + ); + return kb_materializer_api::MaterializerExecutionResult { + status: kb_materializer_api::MaterializerOutcomeStatus::Inserted, + outputs: std::vec![output], + diagnostics: std::vec::Vec::new(), + }; + } +} + +fn family_is_accepted(family: kb_model::EventFamily) -> bool { + return ACCEPTED_FAMILIES.iter().any(|accepted| return *accepted == family); +} + +fn projection(surface_code: &str, entry_code: &str) -> std::option::Option { + if surface_code == STAKE_SURFACE { + return stake_projection(entry_code); + } + if surface_code == VOTE_SURFACE { + return vote_projection(entry_code); + } + return std::option::Option::None; +} + +fn stake_projection(entry_code: &str) -> std::option::Option { + if contains(STAKE_ACCOUNT_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::StakeAccount); + } + if contains(STAKE_VALUE_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::StakeValue); + } + if contains(STAKE_AUTHORITY_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::StakeAuthority); + } + if contains(STAKE_LOCKUP_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::StakeLockup); + } + return std::option::Option::None; +} + +fn vote_projection(entry_code: &str) -> std::option::Option { + if contains(VOTE_ACCOUNT_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::VoteAccount); + } + if contains(VOTE_AUTHORITY_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::VoteAuthority); + } + if contains(VOTE_ADMIN_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::VoteAdmin); + } + if contains(VOTE_STATE_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::VoteState); + } + if contains(VOTE_VALUE_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::VoteValue); + } + if contains(VOTE_REWARD_ENTRIES, entry_code) { + return std::option::Option::Some(ProjectionKind::VoteReward); + } + return std::option::Option::None; +} + +fn projection_details( + projection: ProjectionKind, + accounts: &serde_json::Value, + parameters: &serde_json::Value, + operation: &str, +) -> serde_json::Value { + return match projection { + ProjectionKind::StakeAccount => stake_account_details(accounts, parameters, operation), + ProjectionKind::StakeValue => stake_value_details(accounts, parameters, operation), + ProjectionKind::StakeAuthority => stake_authority_details(accounts, parameters, operation), + ProjectionKind::StakeLockup => stake_lockup_details(accounts, parameters, operation), + ProjectionKind::VoteAccount => vote_account_details(accounts, parameters, operation), + ProjectionKind::VoteAuthority => vote_authority_details(accounts, parameters, operation), + ProjectionKind::VoteAdmin => vote_admin_details(accounts, parameters, operation), + ProjectionKind::VoteState => vote_state_details(accounts, parameters, operation), + ProjectionKind::VoteValue => vote_value_details(accounts, parameters, operation), + ProjectionKind::VoteReward => vote_reward_details(accounts, parameters, operation), + }; +} + +fn stake_account_details( + accounts: &serde_json::Value, + parameters: &serde_json::Value, + operation: &str, +) -> serde_json::Value { + return serde_json::json!({ + "stakeAccount": account_key_for_role(accounts, "stake_account"), + "voteAccount": account_key_for_role(accounts, "vote_account"), + "authorized": parameters.get("authorized").cloned().unwrap_or(serde_json::Value::Null), + "lockup": parameters.get("lockup").cloned().unwrap_or(serde_json::Value::Null), + "activationStateComputed": false, + "transactionFinalAccountStateCaptured": false, + "stateTransition": match operation { + "initialize" | "initialize_checked" => "stake_account_initialized", + "delegate_stake" => "stake_delegation_requested", + "deactivate" => "stake_deactivation_requested", + "deactivate_delinquent" => "stake_deactivation_requested_for_delinquent_vote_account", + _ => "stake_account_state_transition_requested", + }, + }); +} + +fn stake_value_details( + accounts: &serde_json::Value, + parameters: &serde_json::Value, + operation: &str, +) -> serde_json::Value { + return serde_json::json!({ + "stakeAccount": account_key_for_role(accounts, "stake_account"), + "splitAccount": account_key_for_role(accounts, "split_account"), + "destinationAccount": first_account_key_for_roles(accounts, &["destination", "recipient_account", "destination_stake_account"]), + "sourceStakeAccount": account_key_for_role(accounts, "source_stake_account"), + "lamports": parameters.get("lamports").cloned().unwrap_or(serde_json::Value::Null), + "lamportBalanceChangesDuplicated": false, + "activationStateComputed": false, + "transactionFinalAccountStateCaptured": false, + "stateTransition": match operation { + "split" => "stake_split_requested", + "withdraw" => "stake_lamports_withdrawn_or_account_closed_conditionally", + "merge" => "stake_accounts_merged", + "move_stake" => "active_stake_moved", + "move_lamports" => "inactive_lamports_moved", + _ => "stake_value_transition_requested", + }, + }); +} + +fn stake_authority_details( + accounts: &serde_json::Value, + parameters: &serde_json::Value, + operation: &str, +) -> serde_json::Value { + return serde_json::json!({ + "stakeAccount": account_key_for_role(accounts, "stake_account"), + "currentAuthority": first_account_key_for_roles(accounts, &["stake_or_withdraw_authority", "current_authority", "stake_authority", "withdraw_authority", "authority_base"]), + "newAuthority": first_parameter_or_account( + parameters, + &["newAuthority", "newAuthorizedPubkey"], + accounts, + "new_authority", + ), + "authorizationType": parameters.get("authorizationType").cloned().unwrap_or(serde_json::Value::Null), + "authoritySeed": parameters.get("authoritySeed").cloned().unwrap_or(serde_json::Value::Null), + "authorityOwner": parameters.get("authorityOwner").cloned().unwrap_or(serde_json::Value::Null), + "stateTransition": match operation { + "authorize_with_seed" | "authorize_checked_with_seed" => "stake_authority_changed_with_seed", + "authorize_checked" => "stake_authority_changed_with_new_authority_signature", + _ => "stake_authority_changed", + }, + "transactionFinalAccountStateCaptured": false, + }); +} + +fn stake_lockup_details( + accounts: &serde_json::Value, + parameters: &serde_json::Value, + operation: &str, +) -> serde_json::Value { + return serde_json::json!({ + "stakeAccount": account_key_for_role(accounts, "stake_account"), + "custodian": first_account_key_for_roles(accounts, &["lockup_custodian", "lockup_or_withdraw_authority", "new_lockup_authority"]), + "lockup": parameters.clone(), + "checkedCustodianFromAccount": operation == "set_lockup_checked", + "stateTransition": "stake_lockup_updated", + "transactionFinalAccountStateCaptured": false, + }); +} + +fn vote_account_details( + accounts: &serde_json::Value, + parameters: &serde_json::Value, + operation: &str, +) -> serde_json::Value { + return serde_json::json!({ + "voteAccount": account_key_for_role(accounts, "vote_account"), + "validatorIdentity": account_key_for_role(accounts, "validator_identity"), + "authorizedVoter": parameters.get("authorizedVoter").cloned().unwrap_or(serde_json::Value::Null), + "authorizedWithdrawer": parameters.get("authorizedWithdrawer").cloned().unwrap_or(serde_json::Value::Null), + "commissionPercent": parameters.get("commissionPercent").cloned().unwrap_or(serde_json::Value::Null), + "inflationRewardsCommissionBasisPoints": parameters.get("inflationRewardsCommissionBasisPoints").cloned().unwrap_or(serde_json::Value::Null), + "blockRevenueCommissionBasisPoints": parameters.get("blockRevenueCommissionBasisPoints").cloned().unwrap_or(serde_json::Value::Null), + "inflationRewardsCollector": account_key_for_role(accounts, "inflation_rewards_collector"), + "blockRevenueCollector": account_key_for_role(accounts, "block_revenue_collector"), + "stateTransition": if operation == "initialize_account_v2" { + "vote_account_initialized_v2" + } else { + "vote_account_initialized" + }, + "transactionFinalAccountStateCaptured": false, + }); +} + +fn vote_authority_details( + accounts: &serde_json::Value, + parameters: &serde_json::Value, + operation: &str, +) -> serde_json::Value { + return serde_json::json!({ + "voteAccount": account_key_for_role(accounts, "vote_account"), + "currentAuthority": first_account_key_for_roles(accounts, &["current_authority", "current_authority_base"]), + "newAuthority": first_parameter_or_account( + parameters, + &["newAuthority"], + accounts, + "new_authority", + ), + "authorizationType": parameters.get("authorizationType").cloned().unwrap_or(serde_json::Value::Null), + "currentAuthorityDerivedKeyOwner": parameters.get("currentAuthorityDerivedKeyOwner").cloned().unwrap_or(serde_json::Value::Null), + "currentAuthorityDerivedKeySeed": parameters.get("currentAuthorityDerivedKeySeed").cloned().unwrap_or(serde_json::Value::Null), + "newAuthorityFromAccount": parameters.get("newAuthorityFromAccount").cloned().unwrap_or(serde_json::Value::Bool(false)), + "stateTransition": match operation { + "authorize_with_seed" | "authorize_checked_with_seed" => "vote_authority_changed_with_seed", + "authorize_checked" => "vote_authority_changed_with_new_authority_signature", + _ => "vote_authority_changed", + }, + "transactionFinalAccountStateCaptured": false, + }); +} + +fn vote_admin_details( + accounts: &serde_json::Value, + parameters: &serde_json::Value, + operation: &str, +) -> serde_json::Value { + return serde_json::json!({ + "voteAccount": account_key_for_role(accounts, "vote_account"), + "withdrawAuthority": account_key_for_role(accounts, "withdraw_authority"), + "newValidatorIdentity": account_key_for_role(accounts, "new_validator_identity"), + "newCommissionCollector": account_key_for_role(accounts, "new_commission_collector"), + "commissionPercent": parameters.get("commissionPercent").cloned().unwrap_or(serde_json::Value::Null), + "commissionBasisPoints": parameters.get("commissionBasisPoints").cloned().unwrap_or(serde_json::Value::Null), + "commissionKind": parameters.get("commissionKind").cloned().unwrap_or(serde_json::Value::Null), + "stateTransition": match operation { + "update_validator_identity" => "vote_validator_identity_updated", + "update_commission" | "update_commission_bps" => "vote_commission_updated", + "update_commission_collector" => "vote_commission_collector_updated", + _ => "vote_admin_updated", + }, + "transactionFinalAccountStateCaptured": false, + }); +} + +fn vote_state_details( + accounts: &serde_json::Value, + parameters: &serde_json::Value, + operation: &str, +) -> serde_json::Value { + return serde_json::json!({ + "voteAccount": account_key_for_role(accounts, "vote_account"), + "voteAuthority": account_key_for_role(accounts, "vote_authority"), + "lastVotedSlot": parameters.get("lastVotedSlot").cloned().unwrap_or(serde_json::Value::Null), + "slots": parameters.get("slots").cloned().unwrap_or(serde_json::Value::Null), + "root": parameters.get("root").cloned().unwrap_or(serde_json::Value::Null), + "hash": first_parameter_value(parameters, &["hash", "bankHash"]), + "timestamp": parameters.get("timestamp").cloned().unwrap_or(serde_json::Value::Null), + "switchProofHash": parameters.get("switchProofHash").cloned().unwrap_or(serde_json::Value::Null), + "stateTransition": if operation.contains("switch") { + "vote_state_updated_with_switch_proof" + } else if operation.starts_with("tower_sync") { + "vote_tower_synchronized" + } else { + "vote_state_updated" + }, + "voteCreditsComputed": false, + "transactionFinalAccountStateCaptured": false, + }); +} + +fn vote_value_details( + accounts: &serde_json::Value, + parameters: &serde_json::Value, + _operation: &str, +) -> serde_json::Value { + return serde_json::json!({ + "voteAccount": account_key_for_role(accounts, "vote_account"), + "recipientAccount": account_key_for_role(accounts, "recipient_account"), + "withdrawAuthority": account_key_for_role(accounts, "withdraw_authority"), + "lamports": parameters.get("lamports").cloned().unwrap_or(serde_json::Value::Null), + "lamportBalanceChangesDuplicated": false, + "stateTransition": "vote_lamports_withdrawn_or_account_closed_conditionally", + "transactionFinalAccountStateCaptured": false, + }); +} + +fn vote_reward_details( + accounts: &serde_json::Value, + parameters: &serde_json::Value, + _operation: &str, +) -> serde_json::Value { + return serde_json::json!({ + "voteAccount": account_key_for_role(accounts, "vote_account"), + "depositSource": account_key_for_role(accounts, "deposit_source"), + "depositLamports": parameters.get("depositLamports").cloned().unwrap_or(serde_json::Value::Null), + "lamportBalanceChangesDuplicated": false, + "stateTransition": "delegator_rewards_deposited_into_vote_account", + "transactionFinalAccountStateCaptured": false, + }); +} + +fn first_parameter_or_account( + parameters: &serde_json::Value, + keys: &[&str], + accounts: &serde_json::Value, + fallback_role: &str, +) -> serde_json::Value { + let value = first_parameter_value(parameters, keys); + if !value.is_null() { + return value; + } + return account_key_for_role(accounts, fallback_role); +} + +fn first_parameter_value(parameters: &serde_json::Value, keys: &[&str]) -> serde_json::Value { + for key in keys { + if let std::option::Option::Some(value) = parameters.get(*key) { + return value.clone(); + } + } + return serde_json::Value::Null; +} + +fn first_account_key_for_roles(accounts: &serde_json::Value, roles: &[&str]) -> serde_json::Value { + for role in roles { + let value = account_key_for_role(accounts, role); + if !value.is_null() { + return value; + } + } + return serde_json::Value::Null; +} + +fn account_key_for_role(accounts: &serde_json::Value, role: &str) -> serde_json::Value { + let account_key = accounts.as_array().and_then(|values| { + return values.iter().find_map(|value| { + if value.get("role").and_then(serde_json::Value::as_str) + != std::option::Option::Some(role) + { + return std::option::Option::None; + } + return value + .get("accountKey") + .and_then(serde_json::Value::as_str) + .map(|account_key| return account_key.to_string()); + }); + }); + return match account_key { + std::option::Option::Some(value) => serde_json::Value::String(value), + std::option::Option::None => serde_json::Value::Null, + }; +} + +fn contains(entries: &[&str], entry_code: &str) -> bool { + return entries.iter().any(|entry| return *entry == entry_code); +} + +#[cfg(test)] +mod tests { + fn observation( + surface_code: &str, + entry_code: &str, + family: kb_model::EventFamily, + transaction_failed: bool, + accounts: serde_json::Value, + parameters: serde_json::Value, + ) -> kb_decoder_api::DecodedObservation { + return kb_decoder_api::DecodedObservation { + event_key: format!("{entry_code}:0"), + event: kb_model::DecodedProtocolEvent { + signature: kb_model::Signature("signature".to_string()), + slot: kb_model::Slot(42), + instruction_path: kb_model::InstructionPath("0".to_string()), + program_id: kb_model::ProgramId("program-id".to_string()), + protocol_code: kb_model::ProtocolCode("solana_native".to_string()), + surface_code: kb_model::SurfaceCode(surface_code.to_string()), + event_code: kb_model::EventCode(format!("{surface_code}.{entry_code}")), + event_name: kb_model::EventName(entry_code.to_string()), + event_family: family, + source_kind: kb_model::EventSourceKind::Instruction, + confidence: kb_model::DecoderConfidence::ManualExact, + }, + payload_json: serde_json::json!({"accounts": accounts, "parameters": parameters}), + transaction_failed, + transaction_error: if transaction_failed { + std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]})) + } else { + std::option::Option::None + }, + observation_committed: !transaction_failed, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::Manual, + confidence: kb_model::DecoderConfidence::ManualExact, + evidence: std::vec!["fixture".to_string()], + }, + }; + } + + fn account(role: &str, account_key: &str) -> serde_json::Value { + return serde_json::json!({"role": role, "accountKey": account_key}); + } + + #[test] + fn stake_initialize_creates_instructional_staking_projection() { + let materializer = crate::StakingMaterializer; + let observation = observation( + super::STAKE_SURFACE, + "initialize", + kb_model::EventFamily::Lifecycle, + false, + serde_json::json!([account("stake_account", "stake111")]), + serde_json::json!({"authorized": {"staker": "staker111"}}), + ); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs[0].family, kb_model::MaterializedEventFamily::Staking); + assert_eq!(result.outputs[0].output_key, "stake_account:initialize:0"); + assert_eq!(result.outputs[0].payload_json["projection"]["stakeAccount"], "stake111"); + assert_eq!( + result.outputs[0].payload_json["projection"]["transactionFinalAccountStateCaptured"], + false + ); + } + + #[test] + fn stake_authority_and_lockup_events_are_projected() { + let materializer = crate::StakingMaterializer; + let authority = observation( + super::STAKE_SURFACE, + "authorize_with_seed", + kb_model::EventFamily::Admin, + false, + serde_json::json!([account("stake_account", "stake111")]), + serde_json::json!({ + "newAuthorizedPubkey": "new111", + "authorizationType": "staker", + "authoritySeed": "seed", + "authorityOwner": "owner111", + }), + ); + let lockup = observation( + super::STAKE_SURFACE, + "set_lockup_checked", + kb_model::EventFamily::Admin, + false, + serde_json::json!([ + account("stake_account", "stake111"), + account("lockup_custodian", "custodian111"), + ]), + serde_json::json!({"epoch": 42}), + ); + let authority_result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &authority); + let lockup_result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &lockup); + assert_eq!(authority_result.outputs[0].output_key, "stake_authority:authorize_with_seed:0"); + assert_eq!( + authority_result.outputs[0].payload_json["projection"]["newAuthority"], + "new111" + ); + assert_eq!(lockup_result.outputs[0].output_key, "stake_lockup:set_lockup_checked:0"); + assert_eq!( + lockup_result.outputs[0].payload_json["projection"]["checkedCustodianFromAccount"], + true + ); + } + + #[test] + fn stake_value_projection_does_not_duplicate_lamport_changes() { + let materializer = crate::StakingMaterializer; + let observation = observation( + super::STAKE_SURFACE, + "withdraw", + kb_model::EventFamily::Staking, + false, + serde_json::json!([ + account("stake_account", "stake111"), + account("destination", "dest111"), + ]), + serde_json::json!({"lamports": 99}), + ); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.outputs[0].output_key, "stake_value:withdraw:0"); + assert_eq!(result.outputs[0].payload_json["projection"]["lamports"], 99); + assert_eq!( + result.outputs[0].payload_json["projection"]["lamportBalanceChangesDuplicated"], + false + ); + } + + #[test] + fn vote_account_and_authority_events_are_projected() { + let materializer = crate::StakingMaterializer; + let initialize = observation( + super::VOTE_SURFACE, + "initialize_account_v2", + kb_model::EventFamily::Lifecycle, + false, + serde_json::json!([ + account("vote_account", "vote111"), + account("validator_identity", "validator111"), + ]), + serde_json::json!({ + "authorizedVoter": "voter111", + "authorizedWithdrawer": "withdrawer111", + "inflationRewardsCommissionBasisPoints": 500, + }), + ); + let authorize = observation( + super::VOTE_SURFACE, + "authorize_checked", + kb_model::EventFamily::Admin, + false, + serde_json::json!([ + account("vote_account", "vote111"), + account("new_authority", "new111"), + ]), + serde_json::json!({"authorizationType": {"kind": "voter"}, "newAuthorityFromAccount": true}), + ); + let initialize_result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &initialize); + let authorize_result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &authorize); + assert_eq!(initialize_result.outputs[0].output_key, "vote_account:initialize_account_v2:0"); + assert_eq!( + initialize_result.outputs[0].payload_json["projection"]["validatorIdentity"], + "validator111" + ); + assert_eq!(authorize_result.outputs[0].output_key, "vote_authority:authorize_checked:0"); + assert_eq!( + authorize_result.outputs[0].payload_json["projection"]["newAuthority"], + "new111" + ); + } + + #[test] + fn vote_state_and_reward_outputs_are_distinguished() { + let materializer = crate::StakingMaterializer; + let vote_state = observation( + super::VOTE_SURFACE, + "tower_sync_switch", + kb_model::EventFamily::Staking, + false, + serde_json::json!([ + account("vote_account", "vote111"), + account("vote_authority", "authority111"), + ]), + serde_json::json!({"root": 10, "hash": "hash111", "switchProofHash": "switch111"}), + ); + let reward = observation( + super::VOTE_SURFACE, + "deposit_delegator_rewards", + kb_model::EventFamily::Reward, + false, + serde_json::json!([ + account("vote_account", "vote111"), + account("deposit_source", "source111"), + ]), + serde_json::json!({"depositLamports": 123}), + ); + let vote_result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &vote_state); + let reward_result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &reward); + assert_eq!(vote_result.outputs[0].output_key, "vote_state:tower_sync_switch:0"); + assert_eq!( + vote_result.outputs[0].payload_json["projection"]["switchProofHash"], + "switch111" + ); + assert_eq!(reward_result.outputs[0].family, kb_model::MaterializedEventFamily::Reward); + assert_eq!(reward_result.outputs[0].output_key, "vote_reward:deposit_delegator_rewards:0"); + } + + #[test] + fn failed_staking_mutation_is_refused() { + let materializer = crate::StakingMaterializer; + let observation = observation( + super::STAKE_SURFACE, + "delegate_stake", + kb_model::EventFamily::Staking, + true, + serde_json::json!([account("stake_account", "stake111")]), + serde_json::json!({}), + ); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Refused); + assert!(result.outputs.is_empty()); + } + + #[test] + fn unrelated_or_disabled_observations_are_ignored() { + let materializer = crate::StakingMaterializer; + let unrelated = observation( + "solana_native_system", + "create_account", + kb_model::EventFamily::Lifecycle, + false, + serde_json::json!([]), + serde_json::json!({}), + ); + let redelegate = observation( + super::STAKE_SURFACE, + "redelegate", + kb_model::EventFamily::Staking, + false, + serde_json::json!([]), + serde_json::json!({"officiallyDisabled": true}), + ); + let unrelated_result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &unrelated); + let redelegate_result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &redelegate); + assert_eq!( + unrelated_result.status, + kb_materializer_api::MaterializerOutcomeStatus::Ignored + ); + assert_eq!( + redelegate_result.status, + kb_materializer_api::MaterializerOutcomeStatus::Ignored + ); + } + + #[test] + fn output_serialization_is_deterministic() { + let materializer = crate::StakingMaterializer; + let observation = observation( + super::VOTE_SURFACE, + "update_commission_bps", + kb_model::EventFamily::Admin, + false, + serde_json::json!([account("vote_account", "vote111")]), + serde_json::json!({"commissionBasisPoints": 250, "commissionKind": "inflation_rewards"}), + ); + let first = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + let second = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + let first_json_result = serde_json::to_string(&first.outputs[0].payload_json); + let first_json = match first_json_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected serialization error: {error}"), + }; + let second_json_result = serde_json::to_string(&second.outputs[0].payload_json); + let second_json = match second_json_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected serialization error: {error}"), + }; + assert_eq!(first_json, second_json); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_token_accounts/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_token_accounts/Cargo.toml new file mode 100644 index 0000000..bb50907 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_token_accounts/Cargo.toml @@ -0,0 +1,22 @@ +# file: kb_materializer_token_accounts/Cargo.toml +# version: 3 + +[package] +name = "kb_materializer_token_accounts" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_decoder_spl_token_2022 = { path = "../kb_decoder_spl_token_2022" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_token_accounts/README.md b/migration/khadhroony-bot2-reference/kb_materializer_token_accounts/README.md new file mode 100644 index 0000000..2f4f26c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_token_accounts/README.md @@ -0,0 +1,29 @@ + + + +# kb_materializer_token_accounts + +Ce crate matérialise les mutations instructionnelles commitées du programme SPL Token classique et le lifecycle d'adresses associées ATA dans le journal générique `kb_sol_mat_events`. + +## Projections actives + +- initialisations de mint et de compte ; +- transferts, approvals/revocations, mint et burn ; +- freeze/thaw et close ; +- `SyncNative`, `InitializeImmutableOwner`, `WithdrawExcessLamports` et `UnwrapLamports`. +- `ata_created` pour `Create` ; +- `ata_created_or_reused_idempotently` pour `CreateIdempotent`, sans inventer laquelle des deux branches s'est produite ; +- `nested_ata_recovered` pour `RecoverNested`, avec wallet, mints, trois ATA et Token Program classique ou Token-2022. + +Chaque sortie conserve signature, slot, chemin, comptes, autorité, mint lorsqu'il est explicite, montant brut en chaîne et decimals présents dans le wire. Le matérialiseur utilise `SuccessfulCommittedOnly` et refuse toute transaction échouée. + +## Limites et déduplication + +La projection décrit une mutation d'instruction, jamais le snapshot final du compte. Elle ne recopie pas les changements de balances core. Le parent ATA possède uniquement le lifecycle de l'adresse associée ; les CPI SPL Token conservent la propriété exclusive des initialisations, transferts et fermetures de comptes. Le parent `Batch`, les conversions amount/UI amount, `set_authority` et la configuration multisig sont laissés respectivement aux enfants, au decode et au matérialiseur admin. + +## Corpus validé + +Le replay Mainnet du 15 juillet 2026 a produit 362 sorties `token_account` et neuf constats `risk` +depuis 370 instructions décodées. Les huit refus de matérialisation correspondent exactement à des +transactions échouées ; aucune sortie non commitée, aucun parent `Batch` dupliqué et aucun faux frais +classique n'ont été persistés. Un second replay sans force a sélectionné zéro entrée. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_token_accounts/src/constants.rs b/migration/khadhroony-bot2-reference/kb_materializer_token_accounts/src/constants.rs new file mode 100644 index 0000000..8307625 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_token_accounts/src/constants.rs @@ -0,0 +1,11 @@ +// file: kb_materializer_token_accounts/src/constants.rs +// version: 3 + +//! Local constants for classic SPL Token account projections. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_materializer_token_accounts"; +/// Stable processor name persisted by the replay ledger. +pub(crate) const PROCESSOR_NAME: &str = "spl_token_accounts"; +/// Stable projection payload contract version. +pub(crate) const PROJECTION_VERSION: u32 = 3; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_token_accounts/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_token_accounts/src/lib.rs new file mode 100644 index 0000000..f59ab1b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_token_accounts/src/lib.rs @@ -0,0 +1,22 @@ +// file: kb_materializer_token_accounts/src/lib.rs +// version: 8 + +//! Materializer crate for classic SPL Token mutations and ATA lifecycle facts. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod materializer; + +/// Stable processor name persisted by the replay ledger. +pub(crate) use crate::constants::PROCESSOR_NAME; +/// Stable projection payload contract version. +pub(crate) use crate::constants::PROJECTION_VERSION; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Stable committed classic SPL Token mutation and ATA lifecycle materializer. +pub use crate::materializer::TokenAccountsMaterializer; +/// Materialize one bounded Token-2022 Mint, Account, or Multisig state snapshot. +pub use crate::materializer::materialize_token_2022_state_snapshot; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_token_accounts/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_token_accounts/src/materializer.rs new file mode 100644 index 0000000..690f87b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_token_accounts/src/materializer.rs @@ -0,0 +1,835 @@ +// file: kb_materializer_token_accounts/src/materializer.rs +// version: 9 + +//! Stable committed SPL Token mutation and ATA lifecycle projections. + +const ACCEPTED_FAMILIES: &[kb_model::EventFamily] = &[ + kb_model::EventFamily::TokenAccount, + kb_model::EventFamily::TokenMint, + kb_model::EventFamily::TokenBurn, + kb_model::EventFamily::Lifecycle, +]; + +const MUTATION_ENTRIES: &[&str] = &[ + "initialize_mint", + "initialize_mint2", + "initialize_account", + "initialize_account2", + "initialize_account3", + "transfer", + "transfer_checked", + "approve", + "approve_checked", + "revoke", + "mint_to", + "mint_to_checked", + "burn", + "burn_checked", + "close_account", + "freeze_account", + "thaw_account", + "sync_native", + "initialize_immutable_owner", + "withdraw_excess_lamports", + "unwrap_lamports", + "initialize_non_transferable_mint", + "reallocate", +]; +const ATA_LIFECYCLE_ENTRIES: &[(&str, &str)] = &[ + ("create", "ata_created"), + ("create_idempotent", "ata_created_or_reused_idempotently"), + ("recover_nested", "nested_ata_recovered"), +]; + +/// Stable committed classic SPL Token account and mint mutation materializer. +#[derive(Clone, Debug, Default)] +pub struct TokenAccountsMaterializer; + +impl kb_materializer_api::Materializer for crate::TokenAccountsMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_token_accounts"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, event: &kb_model::DecodedProtocolEvent) -> bool { + return accepts_event(event); + } + + fn materialize_event( + &self, + _event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +impl kb_materializer_api::EventMaterializer for crate::TokenAccountsMaterializer { + fn identity(&self) -> kb_materializer_api::MaterializerIdentity { + return kb_materializer_api::MaterializerIdentity { + name: crate::PROCESSOR_NAME.to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + }; + } + + fn accepted_families(&self) -> &'static [kb_model::EventFamily] { + return ACCEPTED_FAMILIES; + } + + fn accepts_observation(&self, observation: &kb_decoder_api::DecodedObservation) -> bool { + return accepts_event(&observation.event); + } + + fn transaction_policy( + &self, + _family: kb_model::EventFamily, + ) -> kb_materializer_api::MaterializationTransactionPolicy { + return kb_materializer_api::MaterializationTransactionPolicy::SuccessfulCommittedOnly; + } + + fn materialize( + &self, + observation: &kb_decoder_api::DecodedObservation, + ) -> kb_materializer_api::MaterializerExecutionResult { + if !accepts_event(&observation.event) { + return kb_materializer_api::MaterializerExecutionResult::ignored(); + } + if observation.transaction_failed || !observation.observation_committed { + return kb_materializer_api::MaterializerExecutionResult::refused( + "uncommitted_token_account_projection_refused", + "failed or uncommitted SPL Token or ATA observations cannot create mutable outputs", + ); + } + if is_ata_event(&observation.event) { + return materialize_ata(observation); + } + let parameters = observation + .payload_json + .get("parameters") + .cloned() + .unwrap_or(serde_json::Value::Null); + let accounts = observation + .payload_json + .get("accounts") + .cloned() + .unwrap_or(serde_json::Value::Null); + let authority = observation + .payload_json + .get("authority") + .cloned() + .unwrap_or(serde_json::Value::Null); + let operation = observation.event.event_name.0.clone(); + let output = kb_materializer_api::MaterializedOutput { + output_key: format!("token_account_mutation:{operation}:0"), + family: kb_model::MaterializedEventFamily::TokenAccount, + payload_json: serde_json::json!({ + "projectionVersion": crate::PROJECTION_VERSION, + "domain": "spl_token_account_mutation", + "projectionSemantics": "committed_instruction_mutation_not_final_account_snapshot", + "idempotenceKey": format!( + "spl_token:{}:{}:{}", + observation.event.signature.0, + observation.event.instruction_path.0, + operation + ), + "operation": operation, + "signature": observation.event.signature.0.clone(), + "slot": observation.event.slot.0, + "instructionPath": observation.event.instruction_path.0.clone(), + "programId": observation.event.program_id.0.clone(), + "mint": account_key_for_role(&accounts, "mint"), + "amountRaw": parameters.get("amountRaw").cloned().unwrap_or(serde_json::Value::Null), + "decimals": parameters.get("decimals").cloned().unwrap_or(serde_json::Value::Null), + "parameters": parameters, + "accounts": accounts, + "authority": authority, + "committed": true, + "transactionFinalAccountStateCaptured": false, + "coreBalanceChangesDuplicated": false, + "provenance": { + "processorName": crate::PROCESSOR_NAME, + "processorVersion": env!("CARGO_PKG_VERSION"), + "sourceSurface": observation.event.surface_code.0.clone(), + "sourceEventCode": observation.event.event_code.0.clone(), + "sourceEventKey": observation.event_key.clone() + } + }), + }; + tracing::debug!( + target: crate::TRACING_TARGET, + action = "materialize_token_account_mutation", + signature = %observation.event.signature.0, + instruction_path = %observation.event.instruction_path.0, + operation = %observation.event.event_name.0, + "materialize committed classic SPL Token mutation" + ); + return kb_materializer_api::MaterializerExecutionResult { + status: kb_materializer_api::MaterializerOutcomeStatus::Inserted, + outputs: std::vec![output], + diagnostics: std::vec::Vec::new(), + }; + } +} + +fn accepts_event(event: &kb_model::DecodedProtocolEvent) -> bool { + let token_program = (event.program_id.0 == kb_program_ids::SPL_TOKEN_PROGRAM_ID + && event.surface_code.0 == "spl_token") + || (event.program_id.0 == kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID + && event.surface_code.0 == "spl_token_2022"); + let token_mutation = token_program && MUTATION_ENTRIES.contains(&event.event_name.0.as_str()); + let ata = is_ata_event(event); + return ACCEPTED_FAMILIES.contains(&event.event_family) && (token_mutation || ata); +} + +fn is_ata_event(event: &kb_model::DecodedProtocolEvent) -> bool { + return event.program_id.0 == kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID + && event.surface_code.0 == "spl_associated_token_account" + && event.event_family == kb_model::EventFamily::Lifecycle + && ATA_LIFECYCLE_ENTRIES + .iter() + .any(|(entry, _)| return *entry == event.event_name.0); +} + +fn materialize_ata( + observation: &kb_decoder_api::DecodedObservation, +) -> kb_materializer_api::MaterializerExecutionResult { + let operation = observation.event.event_name.0.as_str(); + let addresses = match observation.payload_json.get("addresses") { + std::option::Option::Some(value) if value.is_object() => value, + _ => { + return kb_materializer_api::MaterializerExecutionResult::refused( + "ata_addresses_missing", + "committed ATA lifecycle projection requires the decoded address contract", + ); + }, + }; + let validation = validate_ata_addresses(operation, addresses); + if let std::result::Result::Err((code, message)) = validation { + return kb_materializer_api::MaterializerExecutionResult::refused(code, message); + } + let lifecycle_kind = + match ATA_LIFECYCLE_ENTRIES.iter().find(|(entry, _)| return *entry == operation) { + std::option::Option::Some((_, value)) => *value, + std::option::Option::None => { + return kb_materializer_api::MaterializerExecutionResult::ignored(); + }, + }; + let associated_token_account = if operation == "recover_nested" { + observed_address(addresses, "nestedAssociatedTokenAccount") + } else { + observed_address(addresses, "associatedTokenAccount") + }; + let token_program = addresses + .get("tokenProgram") + .and_then(|value| return value.get("programId")) + .cloned() + .unwrap_or(serde_json::Value::Null); + let wallet = addresses.get("walletOwner").cloned().unwrap_or(serde_json::Value::Null); + let mint = if operation == "recover_nested" { + addresses.get("nestedMint").cloned().unwrap_or(serde_json::Value::Null) + } else { + addresses.get("mint").cloned().unwrap_or(serde_json::Value::Null) + }; + let output = kb_materializer_api::MaterializedOutput { + output_key: format!("ata_lifecycle:{lifecycle_kind}:0"), + family: kb_model::MaterializedEventFamily::TokenAccount, + payload_json: serde_json::json!({ + "projectionVersion": crate::PROJECTION_VERSION, + "domain": "spl_associated_token_account_lifecycle", + "projectionSemantics": "committed_associated_address_lifecycle_not_token_state_snapshot", + "idempotenceKey": format!( + "ata:{}:{}:{}:{}:{}", + observation.event.signature.0, + observation.event.instruction_path.0, + observation.event.program_id.0, + lifecycle_kind, + associated_token_account.as_str().unwrap_or("") + ), + "operation": operation, + "lifecycleKind": lifecycle_kind, + "associatedTokenAccount": associated_token_account, + "walletOwner": wallet, + "mint": mint, + "tokenProgramId": token_program, + "fundingAccount": addresses.get("fundingAccount").cloned().unwrap_or(serde_json::Value::Null), + "ownerMint": addresses.get("ownerMint").cloned().unwrap_or(serde_json::Value::Null), + "nestedMint": addresses.get("nestedMint").cloned().unwrap_or(serde_json::Value::Null), + "ownerAssociatedTokenAccount": observed_address(addresses, "ownerAssociatedTokenAccount"), + "nestedAssociatedTokenAccount": observed_address(addresses, "nestedAssociatedTokenAccount"), + "walletNestedMintAssociatedTokenAccount": observed_address(addresses, "walletNestedMintAssociatedTokenAccount"), + "idempotentOutcome": if operation == "create_idempotent" { + "created_or_reused_not_distinguishable_from_parent_instruction_alone" + } else { + "not_applicable" + }, + "signature": observation.event.signature.0.clone(), + "slot": observation.event.slot.0, + "instructionPath": observation.event.instruction_path.0.clone(), + "programId": observation.event.program_id.0.clone(), + "committed": true, + "transactionFinalAccountStateCaptured": false, + "tokenBalanceReconstructed": false, + "splTokenCpiMutationsDuplicated": false, + "ownership": { + "ataParent": "associated_address_lifecycle_only", + "splTokenCpi": "initialize_transfer_and_close_mutations" + }, + "provenance": { + "processorName": crate::PROCESSOR_NAME, + "processorVersion": env!("CARGO_PKG_VERSION"), + "sourceSurface": observation.event.surface_code.0.clone(), + "sourceEventCode": observation.event.event_code.0.clone(), + "sourceEventKey": observation.event_key.clone() + } + }), + }; + tracing::debug!( + target: crate::TRACING_TARGET, + action = "materialize_ata_lifecycle", + signature = %observation.event.signature.0, + instruction_path = %observation.event.instruction_path.0, + operation, + lifecycle_kind, + "materialize committed ATA lifecycle without duplicating SPL Token CPI mutations" + ); + return kb_materializer_api::MaterializerExecutionResult { + status: kb_materializer_api::MaterializerOutcomeStatus::Inserted, + outputs: std::vec![output], + diagnostics: std::vec::Vec::new(), + }; +} + +fn validate_ata_addresses( + operation: &str, + addresses: &serde_json::Value, +) -> std::result::Result<(), (&'static str, &'static str)> { + let token_supported = addresses + .get("tokenProgram") + .and_then(|value| return value.get("supported")) + .and_then(serde_json::Value::as_bool) + == std::option::Option::Some(true); + let token_program_id = addresses + .get("tokenProgram") + .and_then(|value| return value.get("programId")) + .and_then(serde_json::Value::as_str); + let token_program_exact = matches!( + token_program_id, + std::option::Option::Some(kb_program_ids::SPL_TOKEN_PROGRAM_ID) + | std::option::Option::Some(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID) + ); + if !token_supported || !token_program_exact { + return std::result::Result::Err(( + "ata_token_program_not_supported", + "ATA lifecycle projection requires classic SPL Token or Token-2022", + )); + } + let fields: &[&str] = if operation == "recover_nested" { + &[ + "ownerAssociatedTokenAccount", + "nestedAssociatedTokenAccount", + "walletNestedMintAssociatedTokenAccount", + ] + } else { + &["associatedTokenAccount"] + }; + for field in fields { + let valid = addresses + .get(*field) + .and_then(|value| return value.get("valid")) + .and_then(serde_json::Value::as_bool) + == std::option::Option::Some(true); + let observed = observed_address(addresses, field); + if !valid || observed.as_str().is_none_or(|value| return value.is_empty()) { + return std::result::Result::Err(( + "ata_derived_address_invalid", + "ATA lifecycle projection requires every canonical address validation to pass", + )); + } + } + let identity_fields: &[&str] = if operation == "recover_nested" { + &["walletOwner", "ownerMint", "nestedMint"] + } else { + &["walletOwner", "mint"] + }; + if identity_fields.iter().any(|field| { + return addresses + .get(*field) + .and_then(serde_json::Value::as_str) + .is_none_or(|value| return value.is_empty()); + }) { + return std::result::Result::Err(( + "ata_identity_field_missing", + "ATA lifecycle projection requires wallet and mint identities", + )); + } + return std::result::Result::Ok(()); +} + +fn observed_address(addresses: &serde_json::Value, field: &str) -> serde_json::Value { + return addresses + .get(field) + .and_then(|value| return value.get("observed")) + .cloned() + .unwrap_or(serde_json::Value::Null); +} + +fn account_key_for_role(accounts: &serde_json::Value, role: &str) -> serde_json::Value { + let value = accounts.as_array().and_then(|rows| { + return rows.iter().find_map(|row| { + if row.get("role").and_then(serde_json::Value::as_str) + != std::option::Option::Some(role) + { + return std::option::Option::None; + } + return row.get("accountKey").and_then(serde_json::Value::as_str); + }); + }); + return match value { + std::option::Option::Some(account_key) => { + serde_json::Value::String(account_key.to_string()) + }, + std::option::Option::None => serde_json::Value::Null, + }; +} + +/// Materialize one already parsed Token-2022 account snapshot without inferring hidden state. +pub fn materialize_token_2022_state_snapshot( + account_key: &str, + slot: u64, + state: &kb_decoder_spl_token_2022::state::Token2022State, +) -> std::result::Result { + if account_key.is_empty() { + return std::result::Result::Err( + "Token-2022 state snapshot requires a non-empty account key".to_string(), + ); + } + let kind = match state.kind { + kb_decoder_spl_token_2022::state::Token2022StateKind::Mint => "mint", + kb_decoder_spl_token_2022::state::Token2022StateKind::Account => "account", + kb_decoder_spl_token_2022::state::Token2022StateKind::Multisig => "multisig", + }; + let extensions = state + .extensions + .iter() + .map(|entry| { + return serde_json::json!({ + "extensionType": entry.extension_type, + "extensionName": entry.extension_name, + "valueHex": entry.value_hex, + "valueFields": entry.value_fields, + }); + }) + .collect::>(); + let extension_names = state + .extensions + .iter() + .map(|entry| return entry.extension_name) + .collect::>(); + let contains_confidential_state = state.extensions.iter().any(|entry| { + return entry.extension_name.starts_with("confidential_") + || entry.extension_name == "confidential_mint_burn"; + }); + return std::result::Result::Ok(kb_materializer_api::MaterializedOutput { + output_key: format!("token_2022_state_snapshot:{account_key}:{slot}"), + family: kb_model::MaterializedEventFamily::TokenAccount, + payload_json: serde_json::json!({ + "projectionVersion": crate::PROJECTION_VERSION, + "domain": "spl_token_2022_account_state", + "projectionSemantics": "authoritative_bounded_account_snapshot", + "idempotenceKey": format!("token_2022_state:{account_key}:{slot}"), + "accountKey": account_key, + "slot": slot, + "programId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + "stateKind": kind, + "baseFields": state.base_fields, + "baseHex": state.base_hex, + "accountType": state.account_type, + "extensionCount": state.extensions.len(), + "extensionNames": extension_names, + "extensions": extensions, + "containsConfidentialState": contains_confidential_state, + "confidentialValuesDecrypted": false, + "finalAccountStateCaptured": true, + "provenance": { + "processorName": crate::PROCESSOR_NAME, + "processorVersion": env!("CARGO_PKG_VERSION"), + "source": "bounded_token_2022_state_parser" + } + }), + }); +} + +#[cfg(test)] +mod tests { + fn observation( + entry: &str, + family: kb_model::EventFamily, + failed: bool, + ) -> kb_decoder_api::DecodedObservation { + return kb_decoder_api::DecodedObservation { + event_key: format!("token:{entry}:0"), + event: kb_model::DecodedProtocolEvent { + signature: kb_model::Signature("signature".to_string()), + slot: kb_model::Slot(42), + instruction_path: kb_model::InstructionPath("1/0".to_string()), + program_id: kb_model::ProgramId(kb_program_ids::SPL_TOKEN_PROGRAM_ID.to_string()), + protocol_code: kb_model::ProtocolCode("spl_token".to_string()), + surface_code: kb_model::SurfaceCode("spl_token".to_string()), + event_code: kb_model::EventCode(format!("spl_token.{entry}")), + event_name: kb_model::EventName(entry.to_string()), + event_family: family, + source_kind: kb_model::EventSourceKind::InnerInstruction, + confidence: kb_model::DecoderConfidence::ManualExact, + }, + payload_json: serde_json::json!({ + "parameters": {"amountRaw":"18446744073709551615","decimals":9}, + "accounts": [{"role":"mint","accountKey":"mint111"}], + "authority": {"form":"single"} + }), + transaction_failed: failed, + transaction_error: if failed { + std::option::Option::Some(serde_json::json!({"error":"failed"})) + } else { + std::option::Option::None + }, + observation_committed: !failed, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::Manual, + confidence: kb_model::DecoderConfidence::ManualExact, + evidence: std::vec!["fixture".to_string()], + }, + }; + } + + fn token_2022_observation( + entry: &str, + family: kb_model::EventFamily, + failed: bool, + ) -> kb_decoder_api::DecodedObservation { + let mut observation = observation(entry, family, failed); + observation.event.program_id = + kb_model::ProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string()); + observation.event.protocol_code = kb_model::ProtocolCode("spl_token_2022".to_string()); + observation.event.surface_code = kb_model::SurfaceCode("spl_token_2022".to_string()); + observation.event.event_code = kb_model::EventCode(format!("spl_token_2022.{entry}")); + observation.event_key = format!("token_2022:{entry}:0"); + return observation; + } + + fn ata_observation( + entry: &str, + failed: bool, + valid: bool, + ) -> kb_decoder_api::DecodedObservation { + let addresses = if entry == "recover_nested" { + serde_json::json!({ + "tokenProgram": { + "programId": kb_program_ids::SPL_TOKEN_PROGRAM_ID, + "kind": "spl_token_classic", + "supported": true + }, + "ownerAssociatedTokenAccount": {"expected":"ownerAta","observed":"ownerAta","valid":valid}, + "nestedAssociatedTokenAccount": {"expected":"nestedAta","observed":"nestedAta","valid":valid}, + "walletNestedMintAssociatedTokenAccount": {"expected":"destinationAta","observed":"destinationAta","valid":valid}, + "walletOwner": "wallet111", + "ownerMint": "ownerMint111", + "nestedMint": "nestedMint111" + }) + } else { + serde_json::json!({ + "tokenProgram": { + "programId": kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + "kind": "token_2022", + "supported": true + }, + "associatedTokenAccount": {"expected":"ata111","observed":"ata111","valid":valid}, + "fundingAccount": "payer111", + "walletOwner": "wallet111", + "mint": "mint111" + }) + }; + return kb_decoder_api::DecodedObservation { + event_key: format!("ata:{entry}:0"), + event: kb_model::DecodedProtocolEvent { + signature: kb_model::Signature("signature".to_string()), + slot: kb_model::Slot(42), + instruction_path: kb_model::InstructionPath("0".to_string()), + program_id: kb_model::ProgramId( + kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID.to_string(), + ), + protocol_code: kb_model::ProtocolCode("spl_associated_token_account".to_string()), + surface_code: kb_model::SurfaceCode("spl_associated_token_account".to_string()), + event_code: kb_model::EventCode(format!("spl_associated_token_account.{entry}")), + event_name: kb_model::EventName(entry.to_string()), + event_family: kb_model::EventFamily::Lifecycle, + source_kind: kb_model::EventSourceKind::Instruction, + confidence: kb_model::DecoderConfidence::ManualExact, + }, + payload_json: serde_json::json!({"addresses":addresses}), + transaction_failed: failed, + transaction_error: std::option::Option::None, + observation_committed: !failed, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::Manual, + confidence: kb_model::DecoderConfidence::ManualExact, + evidence: std::vec!["fixture".to_string()], + }, + }; + } + + #[test] + fn committed_checked_transfer_preserves_exact_amount_mint_and_path() { + let observation = + observation("transfer_checked", kb_model::EventFamily::TokenAccount, false); + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::TokenAccountsMaterializer, + &observation, + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs[0].payload_json["amountRaw"], "18446744073709551615"); + assert_eq!(result.outputs[0].payload_json["mint"], "mint111"); + assert_eq!(result.outputs[0].payload_json["instructionPath"], "1/0"); + assert_eq!(result.outputs[0].payload_json["transactionFinalAccountStateCaptured"], false); + } + + #[test] + fn committed_token_2022_transfer_has_the_same_single_projection_owner() { + let observation = + token_2022_observation("transfer_checked", kb_model::EventFamily::TokenAccount, false); + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::TokenAccountsMaterializer, + &observation, + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs.len(), 1); + assert_eq!(result.outputs[0].payload_json["operation"], "transfer_checked"); + assert_eq!( + result.outputs[0].payload_json["programId"], + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID + ); + } + + #[test] + fn failed_mutation_is_refused() { + let observation = observation("burn", kb_model::EventFamily::TokenBurn, true); + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::TokenAccountsMaterializer, + &observation, + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Refused); + assert!(result.outputs.is_empty()); + } + + #[test] + fn batch_parent_admin_and_conversion_events_are_not_duplicated() { + for (entry, family) in [ + ("batch", kb_model::EventFamily::Audit), + ("set_authority", kb_model::EventFamily::Admin), + ("amount_to_ui_amount", kb_model::EventFamily::Audit), + ] { + let observation = observation(entry, family, false); + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::TokenAccountsMaterializer, + &observation, + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Ignored); + } + } + + #[test] + fn every_ata_variant_has_one_exact_lifecycle_projection() { + for (entry, expected_kind, expected_ata, expected_token_program) in [ + ("create", "ata_created", "ata111", kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID), + ( + "create_idempotent", + "ata_created_or_reused_idempotently", + "ata111", + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + ), + ( + "recover_nested", + "nested_ata_recovered", + "nestedAta", + kb_program_ids::SPL_TOKEN_PROGRAM_ID, + ), + ] { + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::TokenAccountsMaterializer, + &ata_observation(entry, false, true), + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(result.outputs.len(), 1); + assert_eq!(result.outputs[0].payload_json["lifecycleKind"], expected_kind); + assert_eq!(result.outputs[0].payload_json["associatedTokenAccount"], expected_ata); + assert_eq!(result.outputs[0].payload_json["tokenProgramId"], expected_token_program); + assert_eq!(result.outputs[0].payload_json["tokenBalanceReconstructed"], false); + assert_eq!(result.outputs[0].payload_json["splTokenCpiMutationsDuplicated"], false); + } + } + + #[test] + fn idempotent_parent_does_not_invent_created_or_reused_branch() { + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::TokenAccountsMaterializer, + &ata_observation("create_idempotent", false, true), + ); + assert_eq!( + result.outputs[0].payload_json["idempotentOutcome"], + "created_or_reused_not_distinguishable_from_parent_instruction_alone" + ); + assert!(result.outputs[0].payload_json.get("reused").is_none()); + assert!(result.outputs[0].payload_json.get("created").is_none()); + } + + #[test] + fn uncommitted_or_invalid_ata_lifecycle_is_refused() { + for observation in [ + ata_observation("create", true, true), + ata_observation("recover_nested", false, false), + ] { + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::TokenAccountsMaterializer, + &observation, + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Refused); + assert!(result.outputs.is_empty()); + } + } + + #[test] + fn ata_parent_and_classic_token_cpi_children_own_distinct_facts() { + let parent = kb_materializer_api::EventMaterializer::materialize( + &crate::TokenAccountsMaterializer, + &ata_observation("recover_nested", false, true), + ); + let transfer = kb_materializer_api::EventMaterializer::materialize( + &crate::TokenAccountsMaterializer, + &observation("transfer", kb_model::EventFamily::TokenAccount, false), + ); + let close = kb_materializer_api::EventMaterializer::materialize( + &crate::TokenAccountsMaterializer, + &observation("close_account", kb_model::EventFamily::TokenAccount, false), + ); + assert_eq!(parent.outputs[0].payload_json["lifecycleKind"], "nested_ata_recovered"); + assert_eq!( + parent.outputs[0].payload_json["ownership"]["ataParent"], + "associated_address_lifecycle_only" + ); + assert!(parent.outputs[0].payload_json.get("amountRaw").is_none()); + assert_eq!(transfer.outputs[0].payload_json["operation"], "transfer"); + assert_eq!(close.outputs[0].payload_json["operation"], "close_account"); + assert_eq!(transfer.outputs[0].payload_json["domain"], "spl_token_account_mutation"); + assert_eq!(close.outputs[0].payload_json["domain"], "spl_token_account_mutation"); + } + + #[test] + fn ata_matrix_and_compiled_token_account_projection_ownership_are_equal() { + let matrix: serde_json::Value = match serde_json::from_str(include_str!( + "../../docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json" + )) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("ATA matrix is invalid: {error}"), + }; + let instructions = match matrix.get("instructions").and_then(serde_json::Value::as_array) { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("ATA matrix instructions are absent"), + }; + for (entry, fact) in super::ATA_LIFECYCLE_ENTRIES { + let row = match instructions.iter().find(|row| { + return row.get("name").and_then(serde_json::Value::as_str) + == std::option::Option::Some(*entry); + }) { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("ATA matrix entry {entry} is absent"), + }; + let expected = format!("token_accounts:{fact}"); + assert!(row["authorizedProjections"].as_array().is_some_and(|values| { + return values.iter().any(|value| return value == expected.as_str()); + })); + } + let owner = &matrix["materializationContract"]["projectionOwners"][0]; + let matrix_facts = match owner["facts"].as_array() { + std::option::Option::Some(values) => values + .iter() + .filter_map(|value| return value.as_str()) + .collect::>(), + std::option::Option::None => panic!("ATA token-account owner facts are absent"), + }; + let compiled_facts = super::ATA_LIFECYCLE_ENTRIES + .iter() + .map(|(_, fact)| return *fact) + .collect::>(); + assert_eq!(matrix_facts, compiled_facts); + } + + #[test] + fn second_ata_materialization_is_byte_for_byte_idempotent() { + let observation = ata_observation("create_idempotent", false, true); + let first = kb_materializer_api::EventMaterializer::materialize( + &crate::TokenAccountsMaterializer, + &observation, + ); + let second = kb_materializer_api::EventMaterializer::materialize( + &crate::TokenAccountsMaterializer, + &observation, + ); + assert_eq!(first, second); + assert_eq!(first.outputs[0].output_key, second.outputs[0].output_key); + assert_eq!( + first.outputs[0].payload_json["idempotenceKey"], + second.outputs[0].payload_json["idempotenceKey"] + ); + } + + #[test] + fn token_2022_state_snapshot_preserves_base_and_ordered_extensions() { + let state = kb_decoder_spl_token_2022::state::Token2022State { + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Mint, + base_fields: serde_json::json!({"supply":"42","decimals":9,"initialized":true}), + base_hex: "00".repeat(82), + account_type: std::option::Option::Some(1), + extensions: std::vec![ + kb_decoder_spl_token_2022::state::Token2022TlvEntry { + extension_type: 1, + extension_name: "transfer_fee_config", + value_hex: "11".repeat(108), + value_fields: serde_json::json!({"withheldAmount":"7"}), + }, + kb_decoder_spl_token_2022::state::Token2022TlvEntry { + extension_type: 4, + extension_name: "confidential_transfer_mint", + value_hex: "22".repeat(65), + value_fields: serde_json::json!({"autoApproveNewAccounts":true}), + }, + ], + }; + let output = match crate::materialize_token_2022_state_snapshot("mint111", 99, &state) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("snapshot failed: {error}"), + }; + assert_eq!(output.payload_json["stateKind"], "mint"); + assert_eq!(output.payload_json["extensionCount"], 2); + assert_eq!(output.payload_json["extensionNames"][0], "transfer_fee_config"); + assert_eq!(output.payload_json["extensionNames"][1], "confidential_transfer_mint"); + assert_eq!(output.payload_json["containsConfidentialState"], true); + assert_eq!(output.payload_json["confidentialValuesDecrypted"], false); + assert_eq!(output.payload_json["baseFields"]["supply"], "42"); + } + + #[test] + fn token_2022_state_snapshot_is_idempotent_and_rejects_missing_identity() { + let state = kb_decoder_spl_token_2022::state::Token2022State { + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Account, + base_fields: serde_json::json!({"mint":"mint111","owner":"wallet111","amount":"5"}), + base_hex: "00".repeat(165), + account_type: std::option::Option::None, + extensions: std::vec::Vec::new(), + }; + let first = crate::materialize_token_2022_state_snapshot("account111", 7, &state); + let second = crate::materialize_token_2022_state_snapshot("account111", 7, &state); + assert_eq!(first, second); + assert!(crate::materialize_token_2022_state_snapshot("", 7, &state).is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_token_metadata_risk/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_token_metadata_risk/Cargo.toml new file mode 100644 index 0000000..8c46d5a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_token_metadata_risk/Cargo.toml @@ -0,0 +1,17 @@ +# file: kb_materializer_token_metadata_risk/Cargo.toml +# version: 1 + +[package] +name = "kb_materializer_token_metadata_risk" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_token_metadata_risk/README.md b/migration/khadhroony-bot2-reference/kb_materializer_token_metadata_risk/README.md new file mode 100644 index 0000000..324070d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_token_metadata_risk/README.md @@ -0,0 +1,14 @@ + + + +# kb_materializer_token_metadata_risk + +Ce crate matérialise les événements décodés de la famille `token_metadata_risk` dans les tables métier correspondantes. + +## Rôle dans l'écosystème + +Ce module travaille sur les événements décodés et produit des projections métier. Il ne doit pas accéder directement au RPC, au wallet, à Tauri ou aux décodeurs spécifiques. + +## État initial + +Le crate est créé comme point d'ancrage. Les règles de promotion métier, les tables PostgreSQL et les contrôles SQL seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_token_metadata_risk/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_token_metadata_risk/src/lib.rs new file mode 100644 index 0000000..64e5d6b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_token_metadata_risk/src/lib.rs @@ -0,0 +1,12 @@ +// file: kb_materializer_token_metadata_risk/src/lib.rs +// version: 1 + +//! Materializer crate for `token_metadata_risk`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod materializer; + +/// Exposes the materializer type implemented by this crate. +pub use crate::materializer::TokenMetadataRiskMaterializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_token_metadata_risk/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_token_metadata_risk/src/materializer.rs new file mode 100644 index 0000000..fb50cdc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_token_metadata_risk/src/materializer.rs @@ -0,0 +1,35 @@ +// file: kb_materializer_token_metadata_risk/src/materializer.rs +// version: 5 + +//! Materializer scaffold for the `kb_materializer_token_metadata_risk` crate. + +/// Initial materializer implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct TokenMetadataRiskMaterializer; + +impl kb_materializer_api::Materializer for crate::TokenMetadataRiskMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_token_metadata_risk"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, _event: &kb_model::DecodedProtocolEvent) -> bool { + return false; + } + + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + let materialized = kb_model::MaterializedEvent { + signature: event.signature.clone(), + slot: event.slot, + decoded_family: event.event_family, + materialized_family: kb_model::MaterializedEventFamily::TokenMetadataRisk, + }; + return std::result::Result::Ok(std::vec![materialized]); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_trades/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_trades/Cargo.toml new file mode 100644 index 0000000..54754e0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_trades/Cargo.toml @@ -0,0 +1,17 @@ +# file: kb_materializer_trades/Cargo.toml +# version: 2 + +[package] +name = "kb_materializer_trades" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_trades/README.md b/migration/khadhroony-bot2-reference/kb_materializer_trades/README.md new file mode 100644 index 0000000..1baf7c3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_trades/README.md @@ -0,0 +1,17 @@ + + + +# kb_materializer_trades + +Ce crate matérialise les événements décodés de la famille `trades` dans les tables métier correspondantes. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_trades/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_trades/src/lib.rs new file mode 100644 index 0000000..377ebdb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_trades/src/lib.rs @@ -0,0 +1,12 @@ +// file: kb_materializer_trades/src/lib.rs +// version: 1 + +//! Materializer crate for `trades`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod materializer; + +/// Initial materializer implementation placeholder. +pub use crate::materializer::TradesMaterializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_trades/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_trades/src/materializer.rs new file mode 100644 index 0000000..e82628e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_trades/src/materializer.rs @@ -0,0 +1,35 @@ +// file: kb_materializer_trades/src/materializer.rs +// version: 5 + +//! Materializer scaffold for the `kb_materializer_trades` crate. + +/// Initial materializer implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct TradesMaterializer; + +impl kb_materializer_api::Materializer for crate::TradesMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_trades"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, _event: &kb_model::DecodedProtocolEvent) -> bool { + return false; + } + + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + let materialized = kb_model::MaterializedEvent { + signature: event.signature.clone(), + slot: event.slot, + decoded_family: event.event_family, + materialized_family: kb_model::MaterializedEventFamily::Trade, + }; + return std::result::Result::Ok(std::vec![materialized]); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/Cargo.toml new file mode 100644 index 0000000..206905d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/Cargo.toml @@ -0,0 +1,21 @@ +# file: kb_materializer_transaction_annotations/Cargo.toml +# version: 1 + +[package] +name = "kb_materializer_transaction_annotations" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +serde_json.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/README.md b/migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/README.md new file mode 100644 index 0000000..004d769 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/README.md @@ -0,0 +1,47 @@ + + + +# kb_materializer_transaction_annotations + +Ce crate fournit une projection réutilisable d’annotations de transaction. La première surface active est SPL Memo v1, v3 et v4 ; le domaine n’est volontairement pas fusionné avec metadata, admin, lifecycle ou compliance audit. + +## Projection Memo + +Le matérialiseur accepte uniquement les observations `spl_memo` dont le Program ID, la surface et l’entrée correspondent exactement. `add_memo`, `memo_intent` et `invalid_memo_attempt` sont reconnus afin que la politique commune puisse enregistrer un refus explicite, mais seule une observation `add_memo` réussie et commitée produit une sortie. + +La sortie `TransactionAnnotation` conserve : + +- signature, slot et chemin d’instruction outer/inner ; +- génération et Program ID exacts ; +- texte UTF‑8 complet dans la borne de 4 096 octets ; +- longueur et SHA-256 du payload ; +- signataires exigés, observés et réellement vérifiés par la génération ; +- état committed, version de projection et provenance du matérialiseur ; +- clé d’idempotence déterministe fondée sur signature, chemin, Program ID et hash. + +Pour v1, les comptes signers restent observables mais la liste `verified` est vide, car le runtime historique ne les vérifie pas. Pour v3/v4, une projection commitée exige l’égalité ordonnée entre signataires requis et observés, doublons compris. + +## Persistance et replay + +La sortie utilise le store commun `kb_sol_mat_events` avec la famille `transaction_annotation`. Aucune migration SQL supplémentaire n’est nécessaire et les événements decode/core ne sont pas dupliqués. Le ledger commun assure : + +- replay identique et même version : skip ; +- force replay : remplacement atomique des sorties appartenant à la version ciblée ; +- changement de version : identité de processor distincte et déterministe. + +Les transactions échouées, observations non commitées, UTF‑8 invalides et validations de signataires invalides restent uniquement dans `kb_sol_decode_events`. Les comptes complets, flags writable, préfixes diagnostics, erreurs UTF‑8 et preuves détaillées restent également dans l’événement décodé ; la projection ne conserve que les champs utiles à la consultation d’annotations. + +## Consultation + +La fenêtre `demo_decode_replay` affiche les sorties comme un journal d’annotations, et non comme une série OHLC : slot, signature, instruction path, génération, texte, longueur, signataires vérifiés, hash et provenance. La lecture passe par le contrat borné `DecodePipelineStore::list_materialized_events`, filtré exactement sur le processor `transaction_annotations` et la famille `transaction_annotation` ; aucun SQL libre ni payload JSON arbitraire ne traverse l’UI. + +La démo d’exécution Memo v4 réutilise ce même contrat de lecture pour vérifier la projection après confirmation. Trois transactions Devnet réelles ont chacune produit exactement une annotation ; le second replay a été `skipped` sans sortie supplémentaire ni refus. Cette projection reste un journal consultable et corrélable, pas une agrégation temporelle de type OHLC. + +Le parcours Mainnet du 14 juillet 2026 a acquis puis extrait 300 transactions réelles, 100 par Program ID. Le replay a décodé 102 instructions v1, 439 v3 et 100 v4 sans unmatched ni échec de décodage. La projection a produit respectivement 69, 422 et 100 annotations ; les 33 refus v1 et 17 refus v3 correspondaient aux intentions issues de transactions échouées. Un second replay v3 non forcé a sélectionné zéro entrée, validant le skip idempotent du ledger. Les campagnes forcées v3/v4 ont également reproduit les mêmes nombres de sorties. + +## Règles locales + +- `SuccessfulCommittedOnly` est appliqué avant toute sortie. +- Un payload décodé incohérent échoue fermé avec un diagnostic stable. +- Le target tracing canonique est `kb_materializer_transaction_annotations`. +- Aucun log ne contient de clé privée ni de texte Memo complet. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/src/constants.rs b/migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/src/constants.rs new file mode 100644 index 0000000..4beba2b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/src/constants.rs @@ -0,0 +1,13 @@ +// file: kb_materializer_transaction_annotations/src/constants.rs +// version: 1 + +//! Local constants for the transaction annotation materializer. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_materializer_transaction_annotations"; +/// Stable projection contract version. +pub(crate) const PROJECTION_VERSION: u32 = 1; +/// Stable materializer processor name. +pub(crate) const PROCESSOR_NAME: &str = "transaction_annotations"; +/// Maximum Memo payload accepted from the decoded contract. +pub(crate) const MAX_MEMO_PAYLOAD_BYTES: u64 = 4_096; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/src/lib.rs new file mode 100644 index 0000000..ef05ee2 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/src/lib.rs @@ -0,0 +1,22 @@ +// file: kb_materializer_transaction_annotations/src/lib.rs +// version: 4 + +//! Reusable transaction annotation materializer. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod materializer; + +/// Maximum Memo payload accepted from the decoded contract. +pub(crate) use crate::constants::MAX_MEMO_PAYLOAD_BYTES; +/// Stable materializer processor name. +pub(crate) use crate::constants::PROCESSOR_NAME; +/// Stable projection contract version. +pub(crate) use crate::constants::PROJECTION_VERSION; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Stable transaction annotation materializer for committed SPL Memo observations. +pub use crate::materializer::TransactionAnnotationMaterializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/src/materializer.rs new file mode 100644 index 0000000..10c0910 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_transaction_annotations/src/materializer.rs @@ -0,0 +1,618 @@ +// file: kb_materializer_transaction_annotations/src/materializer.rs +// version: 6 + +//! Exact committed SPL Memo transaction annotation projection. + +const ACCEPTED_FAMILIES: &[kb_model::EventFamily] = &[kb_model::EventFamily::Audit]; +const MEMO_ENTRY_NAMES: &[&str] = &["add_memo", "memo_intent", "invalid_memo_attempt"]; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum MemoGeneration { + V1, + V3, + V4, +} + +impl MemoGeneration { + fn from_event(event: &kb_model::DecodedProtocolEvent) -> std::option::Option { + if event.program_id.0 == kb_program_ids::SPL_MEMO_V1_PROGRAM_ID + && event.surface_code.0 == "spl_memo_v1" + { + return std::option::Option::Some(Self::V1); + } + if event.program_id.0 == kb_program_ids::SPL_MEMO_V3_PROGRAM_ID + && event.surface_code.0 == "spl_memo_v3" + { + return std::option::Option::Some(Self::V3); + } + if event.program_id.0 == kb_program_ids::SPL_MEMO_V4_PROGRAM_ID + && event.surface_code.0 == "spl_memo_v4" + { + return std::option::Option::Some(Self::V4); + } + return std::option::Option::None; + } + + fn code(self) -> &'static str { + return match self { + Self::V1 => "v1", + Self::V3 => "v3", + Self::V4 => "v4", + }; + } + + fn signer_status(self) -> &'static str { + return match self { + Self::V1 => "not_applicable", + Self::V3 | Self::V4 => "valid", + }; + } +} + +struct MemoProjection { + generation: MemoGeneration, + memo_text: std::string::String, + payload_length_bytes: u64, + payload_sha256: std::string::String, + required_signers: std::vec::Vec, + observed_signers: std::vec::Vec, + verified_signers: std::vec::Vec, + signer_validation_status: std::string::String, +} + +/// Stable transaction annotation materializer. +#[derive(Clone, Debug, Default)] +pub struct TransactionAnnotationMaterializer; + +impl kb_materializer_api::Materializer for crate::TransactionAnnotationMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_transaction_annotations"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, event: &kb_model::DecodedProtocolEvent) -> bool { + return accepts_event(event); + } + + fn materialize_event( + &self, + _event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +impl kb_materializer_api::EventMaterializer for crate::TransactionAnnotationMaterializer { + fn identity(&self) -> kb_materializer_api::MaterializerIdentity { + return kb_materializer_api::MaterializerIdentity { + name: crate::PROCESSOR_NAME.to_string(), + version: env!("CARGO_PKG_VERSION").to_string(), + }; + } + + fn accepted_families(&self) -> &'static [kb_model::EventFamily] { + return ACCEPTED_FAMILIES; + } + + fn accepts_observation(&self, observation: &kb_decoder_api::DecodedObservation) -> bool { + return accepts_event(&observation.event); + } + + fn transaction_policy( + &self, + _family: kb_model::EventFamily, + ) -> kb_materializer_api::MaterializationTransactionPolicy { + return kb_materializer_api::MaterializationTransactionPolicy::SuccessfulCommittedOnly; + } + + fn materialize( + &self, + observation: &kb_decoder_api::DecodedObservation, + ) -> kb_materializer_api::MaterializerExecutionResult { + if !accepts_event(&observation.event) { + return kb_materializer_api::MaterializerExecutionResult::ignored(); + } + if observation.transaction_failed || !observation.observation_committed { + return kb_materializer_api::MaterializerExecutionResult::refused( + "uncommitted_transaction_annotation_refused", + "failed or uncommitted Memo observations cannot create transaction annotations", + ); + } + if observation.event.event_name.0 != "add_memo" { + return kb_materializer_api::MaterializerExecutionResult::refused( + "invalid_transaction_annotation_source_refused", + "only a committed add_memo observation can create a transaction annotation", + ); + } + let projection_result = parse_projection(observation); + let projection = match projection_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => { + return failed("memo_annotation_payload_invalid", message); + }, + }; + let idempotence_key = format!( + "spl_memo:{}:{}:{}:{}", + observation.event.signature.0, + observation.event.instruction_path.0, + observation.event.program_id.0, + projection.payload_sha256 + ); + let output = kb_materializer_api::MaterializedOutput { + output_key: "transaction_annotation:spl_memo:0".to_string(), + family: kb_model::MaterializedEventFamily::TransactionAnnotation, + payload_json: serde_json::json!({ + "projectionVersion": crate::PROJECTION_VERSION, + "domain": "transaction_annotation", + "annotationKind": "spl_memo", + "idempotenceKey": idempotence_key, + "signature": observation.event.signature.0.clone(), + "slot": observation.event.slot.0, + "instructionPath": observation.event.instruction_path.0.clone(), + "sourceKind": match observation.event.source_kind { + kb_model::EventSourceKind::InnerInstruction => "inner_instruction", + _ => "instruction" + }, + "generation": projection.generation.code(), + "programId": observation.event.program_id.0.clone(), + "text": projection.memo_text, + "payloadLengthBytes": projection.payload_length_bytes, + "payloadSha256": projection.payload_sha256, + "signers": { + "validationStatus": projection.signer_validation_status, + "required": projection.required_signers, + "observed": projection.observed_signers, + "verified": projection.verified_signers + }, + "committed": true, + "provenance": { + "processorName": crate::PROCESSOR_NAME, + "processorVersion": env!("CARGO_PKG_VERSION"), + "sourceProtocol": observation.event.protocol_code.0.clone(), + "sourceSurface": observation.event.surface_code.0.clone(), + "sourceEventCode": observation.event.event_code.0.clone(), + "sourceEventKey": observation.event_key.clone() + } + }), + }; + tracing::debug!( + target: crate::TRACING_TARGET, + action = "materialize_transaction_annotation", + signature = %observation.event.signature.0, + instruction_path = %observation.event.instruction_path.0, + program_id = %observation.event.program_id.0, + generation = projection.generation.code(), + payload_length_bytes = projection.payload_length_bytes, + output_count = 1_usize, + "materialize committed SPL Memo transaction annotation" + ); + return kb_materializer_api::MaterializerExecutionResult { + status: kb_materializer_api::MaterializerOutcomeStatus::Inserted, + outputs: std::vec![output], + diagnostics: std::vec::Vec::new(), + }; + } +} + +fn accepts_event(event: &kb_model::DecodedProtocolEvent) -> bool { + return event.event_family == kb_model::EventFamily::Audit + && event.protocol_code.0 == "spl_memo" + && (event.source_kind == kb_model::EventSourceKind::Instruction + || event.source_kind == kb_model::EventSourceKind::InnerInstruction) + && MemoGeneration::from_event(event).is_some() + && MEMO_ENTRY_NAMES.contains(&event.event_name.0.as_str()); +} + +fn parse_projection( + observation: &kb_decoder_api::DecodedObservation, +) -> std::result::Result { + let generation = match MemoGeneration::from_event(&observation.event) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "Memo generation and Program ID do not match".to_string(), + ); + }, + }; + let payload = &observation.payload_json; + if payload.get("committed").and_then(serde_json::Value::as_bool) + != std::option::Option::Some(true) + { + return std::result::Result::Err("Memo payload is not marked committed".to_string()); + } + if payload.get("transactionSucceeded").and_then(serde_json::Value::as_bool) + != std::option::Option::Some(true) + || payload.get("payloadComplete").and_then(serde_json::Value::as_bool) + != std::option::Option::Some(true) + || string_field(payload, "runtimeValidation") + != std::option::Option::Some("proven_by_successful_transaction") + || payload + .get("utf8Validation") + .and_then(|value| return string_field(value, "status")) + != std::option::Option::Some("valid") + { + return std::result::Result::Err( + "Memo successful runtime validation proof is incomplete".to_string(), + ); + } + if string_field(payload, "generation") != std::option::Option::Some(generation.code()) { + return std::result::Result::Err( + "Memo payload generation does not match its surface".to_string(), + ); + } + if string_field(payload, "programId") + != std::option::Option::Some(observation.event.program_id.0.as_str()) + { + return std::result::Result::Err( + "Memo payload Program ID does not match its event".to_string(), + ); + } + if string_field(payload, "instructionPath") + != std::option::Option::Some(observation.event.instruction_path.0.as_str()) + { + return std::result::Result::Err( + "Memo payload instruction path does not match its event".to_string(), + ); + } + let memo_text = match string_field(payload, "memoText") { + std::option::Option::Some(value) => value.to_string(), + std::option::Option::None => { + return std::result::Result::Err("committed Memo has no UTF-8 text".to_string()); + }, + }; + let payload_length_bytes = + match payload.get("payloadLengthBytes").and_then(serde_json::Value::as_u64) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err("committed Memo has no byte length".to_string()); + }, + }; + let measured_length = match u64::try_from(memo_text.len()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "Memo text length conversion failed: {error}" + )); + }, + }; + if measured_length != payload_length_bytes { + return std::result::Result::Err( + "Memo byte length does not match its UTF-8 text".to_string(), + ); + } + if payload_length_bytes > crate::MAX_MEMO_PAYLOAD_BYTES { + return std::result::Result::Err( + "Memo byte length exceeds the projection limit".to_string(), + ); + } + let payload_sha256 = match string_field(payload, "payloadSha256") { + std::option::Option::Some(value) if is_sha256(value) => value.to_string(), + _ => return std::result::Result::Err("Memo payload SHA-256 is invalid".to_string()), + }; + let signer_validation = match payload.get("signerValidation") { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err("Memo signer validation is absent".to_string()); + }, + }; + let signer_validation_status = match string_field(signer_validation, "status") { + std::option::Option::Some(value) if value == generation.signer_status() => { + value.to_string() + }, + _ => { + return std::result::Result::Err( + "Memo signer validation status is inconsistent".to_string(), + ); + }, + }; + let missing_signers = match string_array(signer_validation, "missingRequiredSigners") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + if !missing_signers.is_empty() { + return std::result::Result::Err("committed Memo reports missing signers".to_string()); + } + let required_signers = match string_array(signer_validation, "orderedRequiredSigners") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + let observed_signers = match string_array(signer_validation, "orderedObservedSigners") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + let verified_signers = if generation == MemoGeneration::V1 { + if !required_signers.is_empty() { + return std::result::Result::Err("Memo v1 cannot require signers".to_string()); + } + std::vec::Vec::new() + } else { + if required_signers != observed_signers { + return std::result::Result::Err( + "committed Memo required and observed signers differ".to_string(), + ); + } + observed_signers.clone() + }; + return std::result::Result::Ok(MemoProjection { + generation, + memo_text, + payload_length_bytes, + payload_sha256, + required_signers, + observed_signers, + verified_signers, + signer_validation_status, + }); +} + +fn string_field<'a>(value: &'a serde_json::Value, key: &str) -> std::option::Option<&'a str> { + return value.get(key).and_then(serde_json::Value::as_str); +} + +fn string_array( + value: &serde_json::Value, + key: &str, +) -> std::result::Result, std::string::String> { + let values = match value.get(key).and_then(serde_json::Value::as_array) { + std::option::Option::Some(values) => values, + std::option::Option::None => { + return std::result::Result::Err(format!("Memo signer field {key} is not an array")); + }, + }; + let mut output = std::vec::Vec::with_capacity(values.len()); + for item in values { + let text = match item.as_str() { + std::option::Option::Some(text) if !text.trim().is_empty() => text, + _ => { + return std::result::Result::Err(format!( + "Memo signer field {key} contains an invalid pubkey" + )); + }, + }; + output.push(text.to_string()); + } + return std::result::Result::Ok(output); +} + +fn is_sha256(value: &str) -> bool { + return value.len() == 64 + && value + .as_bytes() + .iter() + .all(|byte| return byte.is_ascii_hexdigit() && !byte.is_ascii_uppercase()); +} + +fn failed( + code: &str, + message: std::string::String, +) -> kb_materializer_api::MaterializerExecutionResult { + return kb_materializer_api::MaterializerExecutionResult { + status: kb_materializer_api::MaterializerOutcomeStatus::Failed, + outputs: std::vec::Vec::new(), + diagnostics: std::vec![kb_materializer_api::MaterializerDiagnostic { + code: code.to_string(), + message, + retriable: false, + }], + }; +} + +#[cfg(test)] +mod tests { + fn observation( + program_id: &str, + surface_code: &str, + generation: &str, + event_name: &str, + failed: bool, + committed: bool, + required_signers: serde_json::Value, + observed_signers: serde_json::Value, + ) -> kb_decoder_api::DecodedObservation { + return kb_decoder_api::DecodedObservation { + event_key: "memo:0".to_string(), + event: kb_model::DecodedProtocolEvent { + signature: kb_model::Signature("signature".to_string()), + slot: kb_model::Slot(42), + instruction_path: kb_model::InstructionPath("1/0".to_string()), + program_id: kb_model::ProgramId(program_id.to_string()), + protocol_code: kb_model::ProtocolCode("spl_memo".to_string()), + surface_code: kb_model::SurfaceCode(surface_code.to_string()), + event_code: kb_model::EventCode(format!("{surface_code}.{event_name}")), + event_name: kb_model::EventName(event_name.to_string()), + event_family: kb_model::EventFamily::Audit, + source_kind: kb_model::EventSourceKind::InnerInstruction, + confidence: kb_model::DecoderConfidence::ManualExact, + }, + payload_json: serde_json::json!({ + "generation": generation, + "programId": program_id, + "instructionPath": "1/0", + "transactionSucceeded": !failed, + "committed": committed, + "memoText": "memo", + "payloadLengthBytes": 4, + "payloadSha256": "1111111111111111111111111111111111111111111111111111111111111111", + "payloadComplete": true, + "utf8Validation": {"status":"valid","invalidFromByte":null}, + "signerValidation": { + "status": if generation == "v1" { "not_applicable" } else { "valid" }, + "orderedRequiredSigners": required_signers, + "orderedObservedSigners": observed_signers, + "missingRequiredSigners": [] + }, + "runtimeValidation": if failed { + "not_proven_transaction_failed" + } else { + "proven_by_successful_transaction" + } + }), + transaction_failed: failed, + transaction_error: if failed { + std::option::Option::Some(serde_json::json!({"InstructionError":[0,"Custom"]})) + } else { + std::option::Option::None + }, + observation_committed: committed, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::Manual, + confidence: kb_model::DecoderConfidence::ManualExact, + evidence: std::vec!["memo".to_string()], + }, + }; + } + + fn v4() -> kb_decoder_api::DecodedObservation { + return observation( + kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, + "spl_memo_v4", + "v4", + "add_memo", + false, + true, + serde_json::json!(["signer", "signer"]), + serde_json::json!(["signer", "signer"]), + ); + } + + #[test] + fn committed_v4_memo_becomes_deterministic_transaction_annotation() { + let materializer = crate::TransactionAnnotationMaterializer; + let first = kb_materializer_api::EventMaterializer::materialize(&materializer, &v4()); + let second = kb_materializer_api::EventMaterializer::materialize(&materializer, &v4()); + assert_eq!(first, second); + assert_eq!(first.status, kb_materializer_api::MaterializerOutcomeStatus::Inserted); + assert_eq!(first.outputs.len(), 1); + assert_eq!( + first.outputs[0].family, + kb_model::MaterializedEventFamily::TransactionAnnotation + ); + assert_eq!( + first.outputs[0].payload_json["signers"]["verified"], + serde_json::json!(["signer", "signer"]) + ); + assert_eq!(first.outputs[0].payload_json["committed"], true); + assert!(first.validate().is_ok()); + } + + #[test] + fn exact_v1_v3_and_v4_surfaces_are_accepted() { + for (program_id, surface, generation) in [ + (kb_program_ids::SPL_MEMO_V1_PROGRAM_ID, "spl_memo_v1", "v1"), + (kb_program_ids::SPL_MEMO_V3_PROGRAM_ID, "spl_memo_v3", "v3"), + (kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, "spl_memo_v4", "v4"), + ] { + let observation = observation( + program_id, + surface, + generation, + "add_memo", + false, + true, + serde_json::json!([]), + serde_json::json!([]), + ); + assert!(kb_materializer_api::EventMaterializer::accepts_observation( + &crate::TransactionAnnotationMaterializer, + &observation, + )); + } + } + + #[test] + fn v1_observed_accounts_are_not_claimed_as_runtime_verified() { + let observation = observation( + kb_program_ids::SPL_MEMO_V1_PROGRAM_ID, + "spl_memo_v1", + "v1", + "add_memo", + false, + true, + serde_json::json!([]), + serde_json::json!(["observed_only"]), + ); + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::TransactionAnnotationMaterializer, + &observation, + ); + assert_eq!(result.outputs[0].payload_json["signers"]["verified"], serde_json::json!([])); + assert_eq!( + result.outputs[0].payload_json["signers"]["observed"], + serde_json::json!(["observed_only"]) + ); + } + + #[test] + fn failed_or_uncommitted_memo_is_refused_without_output() { + let observation = observation( + kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, + "spl_memo_v4", + "v4", + "memo_intent", + true, + false, + serde_json::json!([]), + serde_json::json!([]), + ); + let materializer = crate::TransactionAnnotationMaterializer; + let policy = + kb_materializer_api::validate_materialization_policy(&materializer, &observation); + assert!(policy.is_err()); + let result = + kb_materializer_api::EventMaterializer::materialize(&materializer, &observation); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Refused); + assert!(result.outputs.is_empty()); + } + + #[test] + fn malformed_committed_payload_fails_closed() { + let mut observation = v4(); + observation.payload_json["payloadSha256"] = serde_json::Value::String("bad".to_string()); + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::TransactionAnnotationMaterializer, + &observation, + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Failed); + assert!(result.outputs.is_empty()); + assert_eq!(result.diagnostics[0].code, "memo_annotation_payload_invalid"); + } + + #[test] + fn foreign_audit_observation_is_not_accepted() { + let mut observation = v4(); + observation.event.protocol_code = kb_model::ProtocolCode("foreign".to_string()); + assert!(!kb_materializer_api::EventMaterializer::accepts_observation( + &crate::TransactionAnnotationMaterializer, + &observation, + )); + let result = kb_materializer_api::EventMaterializer::materialize( + &crate::TransactionAnnotationMaterializer, + &observation, + ); + assert_eq!(result.status, kb_materializer_api::MaterializerOutcomeStatus::Ignored); + } + + #[test] + fn machine_readable_matrix_declares_transaction_annotation_policy() { + let parsed = serde_json::from_str::(include_str!( + "../../docs/SPL_MEMO_MATRIX.json" + )); + let matrix = match parsed { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("memo matrix is invalid JSON: {error}"), + }; + assert_eq!(matrix["matrixVersion"], 4); + assert_eq!( + matrix["materializationContract"]["crate"], + "kb_materializer_transaction_annotations" + ); + assert_eq!( + matrix["materializationContract"]["transactionPolicy"], + "SuccessfulCommittedOnly" + ); + assert_eq!(matrix["materializationContract"]["failedOrUncommittedOutput"], false); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_materializer_vault/Cargo.toml b/migration/khadhroony-bot2-reference/kb_materializer_vault/Cargo.toml new file mode 100644 index 0000000..96b5931 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_vault/Cargo.toml @@ -0,0 +1,17 @@ +# file: kb_materializer_vault/Cargo.toml +# version: 2 + +[package] +name = "kb_materializer_vault" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_model = { path = "../kb_model" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_materializer_vault/README.md b/migration/khadhroony-bot2-reference/kb_materializer_vault/README.md new file mode 100644 index 0000000..58f4ef5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_vault/README.md @@ -0,0 +1,14 @@ + + + +# kb_materializer_vault + +Ce crate matérialise les événements décodés de la famille `vault` dans les tables métier correspondantes. + +## Rôle dans l'écosystème + +Ce module travaille sur les événements décodés et produit des projections métier. Il ne doit pas accéder directement au RPC, au wallet, à Tauri ou aux décodeurs spécifiques. + +## État initial + +Le crate est créé comme point d'ancrage. Les règles de promotion métier, les tables PostgreSQL et les contrôles SQL seront ajoutés dans des deltas dédiés. diff --git a/migration/khadhroony-bot2-reference/kb_materializer_vault/src/lib.rs b/migration/khadhroony-bot2-reference/kb_materializer_vault/src/lib.rs new file mode 100644 index 0000000..79d30a3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_vault/src/lib.rs @@ -0,0 +1,12 @@ +// file: kb_materializer_vault/src/lib.rs +// version: 1 + +//! Materializer crate for `vault`. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod materializer; + +/// Exposes the materializer type implemented by this crate. +pub use crate::materializer::VaultMaterializer; diff --git a/migration/khadhroony-bot2-reference/kb_materializer_vault/src/materializer.rs b/migration/khadhroony-bot2-reference/kb_materializer_vault/src/materializer.rs new file mode 100644 index 0000000..0569d88 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_materializer_vault/src/materializer.rs @@ -0,0 +1,35 @@ +// file: kb_materializer_vault/src/materializer.rs +// version: 5 + +//! Materializer scaffold for the `kb_materializer_vault` crate. + +/// Initial materializer implementation placeholder. +#[derive(Clone, Debug, Default)] +pub struct VaultMaterializer; + +impl kb_materializer_api::Materializer for crate::VaultMaterializer { + fn materializer_name(&self) -> &'static str { + return "kb_materializer_vault"; + } + + fn materializer_version(&self) -> &'static str { + return env!("CARGO_PKG_VERSION"); + } + + fn accepts_event(&self, _event: &kb_model::DecodedProtocolEvent) -> bool { + return false; + } + + fn materialize_event( + &self, + event: &kb_model::DecodedProtocolEvent, + ) -> kb_core::Result> { + let materialized = kb_model::MaterializedEvent { + signature: event.signature.clone(), + slot: event.slot, + decoded_family: event.event_family, + materialized_family: kb_model::MaterializedEventFamily::Vault, + }; + return std::result::Result::Ok(std::vec![materialized]); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_model/Cargo.toml b/migration/khadhroony-bot2-reference/kb_model/Cargo.toml new file mode 100644 index 0000000..d3c5780 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_model/Cargo.toml @@ -0,0 +1,21 @@ +# file: kb_model/Cargo.toml +# version: 2 + +[package] +name = "kb_model" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +base64.workspace = true +bs58.workspace = true +kb_core = { path = "../kb_core" } +serde.workspace = true +serde_json.workspace = true +sha2.workspace = true +ts-rs.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_model/README.md b/migration/khadhroony-bot2-reference/kb_model/README.md new file mode 100644 index 0000000..d07762a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_model/README.md @@ -0,0 +1,32 @@ + + + +# kb_model + +Ce crate définit les modèles neutres utilisés par l'ingestion, l'extraction, le décodage et la matérialisation. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. + +## Contrat canonique `0.3.2` + +Le crate expose maintenant `CanonicalTransaction` et les structures associées pour représenter une transaction Solana indépendamment de sa source. Le contrat couvre les transactions legacy et v0, les comptes statiques et chargés par ALT, les instructions externes et internes, les logs, les balances SOL/SPL, les erreurs, rewards, return data, compute units et block time. + +La sérialisation canonique : + +- utilise des noms de champs stables en camelCase ; +- trie récursivement les clés des objets JSON ; +- conserve signatures et public keys en base58 ; +- conserve les payloads binaires en base64 ; +- conserve les valeurs entières et recalcule localement les montants SPL décimaux à partir du montant brut et des décimales ; +- exclut toute information de provider, endpoint ou transport ; +- calcule `canonical_json_hash` en SHA-256 sur le JSON compact déterministe. + diff --git a/migration/khadhroony-bot2-reference/kb_model/src/canonical_transaction.rs b/migration/khadhroony-bot2-reference/kb_model/src/canonical_transaction.rs new file mode 100644 index 0000000..9298bcc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_model/src/canonical_transaction.rs @@ -0,0 +1,819 @@ +// file: kb_model/src/canonical_transaction.rs +// version: 7 + +//! Source-independent canonical Solana transaction contract. + +use base64::Engine; // rust-rules: trait-import +use sha2::Digest; // rust-rules: trait-import + +/// Current canonical transaction document version. +pub const CANONICAL_TRANSACTION_FORMAT_VERSION: u32 = 1; + +/// Source-independent canonical Solana transaction. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalTransaction { + /// Canonical contract version included in the hashed document. + pub format_version: u32, + /// Primary transaction signature as base58 text. + pub primary_signature: std::string::String, + /// Slot in which the transaction was processed. + pub slot: u64, + /// Optional block timestamp as Unix seconds. + pub block_time: std::option::Option, + /// Transaction message version. + pub version: CanonicalTransactionVersion, + /// All transaction signatures in message order. + pub signatures: std::vec::Vec, + /// Canonical transaction message. + pub message: CanonicalTransactionMessage, + /// Optional execution metadata. + pub metadata: std::option::Option, +} + +impl CanonicalTransaction { + /// Validates the canonical transaction invariants. + pub fn validate(&self) -> kb_core::Result<()> { + if self.format_version != crate::CANONICAL_TRANSACTION_FORMAT_VERSION { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "unsupported canonical transaction format version: {}", + self.format_version + ))); + } + let primary_result = validate_signature_text( + self.primary_signature.as_str(), + "primary transaction signature", + ); + if let std::result::Result::Err(error) = primary_result { + return std::result::Result::Err(error); + } + if self.signatures.is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical transaction must contain at least one signature", + )); + } + if self.signatures[0] != self.primary_signature { + return std::result::Result::Err(kb_core::Error::invalid_state( + "primary transaction signature must equal the first signature", + )); + } + if self.signatures.len() != usize::from(self.message.header.num_required_signatures) { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical signature count must match the message header", + )); + } + for signature in &self.signatures { + let signature_result = + validate_signature_text(signature.as_str(), "transaction signature"); + if let std::result::Result::Err(error) = signature_result { + return std::result::Result::Err(error); + } + } + let message_result = self.message.validate(); + if let std::result::Result::Err(error) = message_result { + return std::result::Result::Err(error); + } + if self.version == CanonicalTransactionVersion::Legacy + && (!self.message.address_table_lookups.is_empty() + || !self.message.loaded_addresses.writable.is_empty() + || !self.message.loaded_addresses.readonly.is_empty()) + { + return std::result::Result::Err(kb_core::Error::invalid_state( + "legacy canonical transaction must not contain lookup table data", + )); + } + if let std::option::Option::Some(metadata) = &self.metadata { + let metadata_result = metadata + .validate(self.message.resolved_account_count(), self.message.instructions.len()); + if let std::result::Result::Err(error) = metadata_result { + return std::result::Result::Err(error); + } + } + return std::result::Result::Ok(()); + } + + /// Serializes the transaction into the canonical JSON value. + pub fn to_canonical_json(&self) -> kb_core::Result { + let validation_result = self.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let serialization_result = serde_json::to_value(self); + let mut value = match serialization_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot serialize canonical transaction: {error}" + ))); + }, + }; + sort_json_value(&mut value); + return std::result::Result::Ok(value); + } + + /// Serializes the transaction into deterministic compact canonical JSON bytes. + pub fn to_canonical_json_bytes(&self) -> kb_core::Result> { + let json_result = self.to_canonical_json(); + let json = match json_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let serialization_result = serde_json::to_vec(&json); + return match serialization_result { + std::result::Result::Ok(bytes) => std::result::Result::Ok(bytes), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::json( + format!("cannot serialize canonical transaction bytes: {error}"), + )), + }; + } + + /// Calculates the lowercase SHA-256 digest of the canonical JSON bytes. + pub fn canonical_json_hash(&self) -> kb_core::Result { + let bytes_result = self.to_canonical_json_bytes(); + let bytes = match bytes_result { + std::result::Result::Ok(bytes) => bytes, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let digest = sha2::Sha256::digest(bytes); + let mut text = std::string::String::with_capacity(64); + for byte in digest { + text.push_str(format!("{byte:02x}").as_str()); + } + return std::result::Result::Ok(text); + } +} + +/// Canonical Solana transaction version. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(tag = "kind", content = "number", rename_all = "snake_case")] +pub enum CanonicalTransactionVersion { + /// Legacy transaction message. + Legacy, + /// Numbered versioned transaction message. + Number(u8), +} + +/// Canonical Solana message header. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalMessageHeader { + /// Number of required transaction signatures. + pub num_required_signatures: u8, + /// Number of readonly signed accounts. + pub num_readonly_signed_accounts: u8, + /// Number of readonly unsigned accounts. + pub num_readonly_unsigned_accounts: u8, +} + +/// Canonical Solana transaction message. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalTransactionMessage { + /// Message header. + pub header: CanonicalMessageHeader, + /// Static account keys in message order. + pub static_account_keys: std::vec::Vec, + /// Recent blockhash as base58 text. + pub recent_blockhash: std::string::String, + /// Top-level compiled instructions. + pub instructions: std::vec::Vec, + /// Address lookup table references for versioned messages. + pub address_table_lookups: std::vec::Vec, + /// Loaded writable and readonly addresses resolved by the runtime. + pub loaded_addresses: CanonicalLoadedAddresses, +} + +impl CanonicalTransactionMessage { + /// Returns the resolved account count including loaded addresses. + pub fn resolved_account_count(&self) -> usize { + return self.static_account_keys.len() + + self.loaded_addresses.writable.len() + + self.loaded_addresses.readonly.len(); + } + + fn validate(&self) -> kb_core::Result<()> { + if self.static_account_keys.is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical transaction message must contain static account keys", + )); + } + for account_key in &self.static_account_keys { + let key_result = validate_pubkey_text(account_key.as_str(), "static account key"); + if let std::result::Result::Err(error) = key_result { + return std::result::Result::Err(error); + } + } + let blockhash_result = + validate_pubkey_text(self.recent_blockhash.as_str(), "recent blockhash"); + if let std::result::Result::Err(error) = blockhash_result { + return std::result::Result::Err(error); + } + let required_signature_count = usize::from(self.header.num_required_signatures); + if required_signature_count == 0 + || required_signature_count > self.static_account_keys.len() + { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical message required signature count is invalid", + )); + } + if usize::from(self.header.num_readonly_signed_accounts) > required_signature_count { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical readonly signed account count is invalid", + )); + } + let unsigned_account_count = self.static_account_keys.len() - required_signature_count; + if usize::from(self.header.num_readonly_unsigned_accounts) > unsigned_account_count { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical readonly unsigned account count is invalid", + )); + } + let resolved_account_count = self.resolved_account_count(); + for instruction in &self.instructions { + let instruction_result = instruction.validate(resolved_account_count); + if let std::result::Result::Err(error) = instruction_result { + return std::result::Result::Err(error); + } + } + for lookup in &self.address_table_lookups { + let lookup_result = + validate_pubkey_text(lookup.account_key.as_str(), "address lookup table key"); + if let std::result::Result::Err(error) = lookup_result { + return std::result::Result::Err(error); + } + } + for account_key in &self.loaded_addresses.writable { + let key_result = + validate_pubkey_text(account_key.as_str(), "loaded writable account key"); + if let std::result::Result::Err(error) = key_result { + return std::result::Result::Err(error); + } + } + for account_key in &self.loaded_addresses.readonly { + let key_result = + validate_pubkey_text(account_key.as_str(), "loaded readonly account key"); + if let std::result::Result::Err(error) = key_result { + return std::result::Result::Err(error); + } + } + return std::result::Result::Ok(()); + } +} + +/// Canonical compiled Solana instruction. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalCompiledInstruction { + /// Resolved account index of the invoked program. + pub program_id_index: u16, + /// Resolved account indexes consumed by the instruction. + pub account_indexes: std::vec::Vec, + /// Instruction data encoded as standard base64. + pub data_base64: std::string::String, + /// Optional runtime invocation stack height. + pub stack_height: std::option::Option, +} + +impl CanonicalCompiledInstruction { + fn validate(&self, resolved_account_count: usize) -> kb_core::Result<()> { + if usize::from(self.program_id_index) >= resolved_account_count { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical instruction program id index is outside resolved accounts", + )); + } + for account_index in &self.account_indexes { + if usize::from(*account_index) >= resolved_account_count { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical instruction account index is outside resolved accounts", + )); + } + } + let decode_result = + base64::engine::general_purpose::STANDARD.decode(self.data_base64.as_bytes()); + if let std::result::Result::Err(error) = decode_result { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "canonical instruction data is not valid base64: {error}" + ))); + } + return std::result::Result::Ok(()); + } +} + +/// Canonical address lookup table reference. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalAddressTableLookup { + /// Lookup table account key as base58 text. + pub account_key: std::string::String, + /// Writable lookup indexes. + pub writable_indexes: std::vec::Vec, + /// Readonly lookup indexes. + pub readonly_indexes: std::vec::Vec, +} + +/// Runtime-loaded addresses for a versioned transaction. +#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalLoadedAddresses { + /// Writable loaded addresses in runtime order. + pub writable: std::vec::Vec, + /// Readonly loaded addresses in runtime order. + pub readonly: std::vec::Vec, +} + +/// Canonical transaction execution status. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum CanonicalTransactionStatus { + /// Transaction execution succeeded. + Success, + /// Transaction execution failed. + Failed, +} + +/// Canonical transaction execution metadata. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalTransactionMetadata { + /// Canonical execution status. + pub status: CanonicalTransactionStatus, + /// Optional source-independent transaction error document. + pub error: std::option::Option, + /// Transaction fee in lamports. + pub fee: u64, + /// Account balances before execution in lamports. + pub pre_balances: std::vec::Vec, + /// Account balances after execution in lamports. + pub post_balances: std::vec::Vec, + /// Inner instruction groups indexed by parent top-level instruction. + pub inner_instructions: std::vec::Vec, + /// Runtime log messages in execution order. + pub log_messages: std::vec::Vec, + /// Token balances before execution. + pub pre_token_balances: std::vec::Vec, + /// Token balances after execution. + pub post_token_balances: std::vec::Vec, + /// Transaction rewards. + pub rewards: std::vec::Vec, + /// Optional transaction return data. + pub return_data: std::option::Option, + /// Optional consumed compute units. + pub compute_units_consumed: std::option::Option, + /// Optional runtime cost units. + pub cost_units: std::option::Option, +} + +impl CanonicalTransactionMetadata { + fn validate( + &self, + resolved_account_count: usize, + top_level_instruction_count: usize, + ) -> kb_core::Result<()> { + if self.pre_balances.len() != self.post_balances.len() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical pre and post balance arrays must have the same length", + )); + } + if self.pre_balances.len() != resolved_account_count { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical balance arrays must match the resolved account count", + )); + } + match self.status { + CanonicalTransactionStatus::Success => { + if self.error.is_some() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "successful canonical transaction must not contain an error", + )); + } + }, + CanonicalTransactionStatus::Failed => { + if self.error.is_none() { + return std::result::Result::Err(kb_core::Error::invalid_state( + "failed canonical transaction must contain an error", + )); + } + }, + } + for inner_group in &self.inner_instructions { + if usize::from(inner_group.parent_instruction_index) >= top_level_instruction_count { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical inner instruction parent index is outside top-level instructions", + )); + } + for instruction in &inner_group.instructions { + let instruction_result = instruction.validate(resolved_account_count); + if let std::result::Result::Err(error) = instruction_result { + return std::result::Result::Err(error); + } + } + } + for balance in &self.pre_token_balances { + let balance_result = balance.validate(resolved_account_count); + if let std::result::Result::Err(error) = balance_result { + return std::result::Result::Err(error); + } + } + for balance in &self.post_token_balances { + let balance_result = balance.validate(resolved_account_count); + if let std::result::Result::Err(error) = balance_result { + return std::result::Result::Err(error); + } + } + for reward in &self.rewards { + let reward_result = validate_pubkey_text(reward.pubkey.as_str(), "reward public key"); + if let std::result::Result::Err(error) = reward_result { + return std::result::Result::Err(error); + } + if let std::option::Option::Some(commission) = reward.commission { + if commission > 100 { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical reward commission must not exceed 100 percent", + )); + } + } + } + if let std::option::Option::Some(return_data) = &self.return_data { + let program_result = + validate_pubkey_text(return_data.program_id.as_str(), "return data program id"); + if let std::result::Result::Err(error) = program_result { + return std::result::Result::Err(error); + } + let decode_result = base64::engine::general_purpose::STANDARD + .decode(return_data.data_base64.as_bytes()); + if let std::result::Result::Err(error) = decode_result { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "canonical return data is not valid base64: {error}" + ))); + } + } + return std::result::Result::Ok(()); + } +} + +/// Group of inner instructions attached to one top-level instruction. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalInnerInstructionGroup { + /// Parent top-level instruction index. + pub parent_instruction_index: u16, + /// Inner instructions in runtime order. + pub instructions: std::vec::Vec, +} + +/// Exact SPL token balance representation. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalTokenBalance { + /// Resolved account index. + pub account_index: u16, + /// Token mint public key. + pub mint: std::string::String, + /// Optional token account owner public key. + pub owner: std::option::Option, + /// Optional token program public key. + pub program_id: std::option::Option, + /// Exact raw token amount as an unsigned decimal string. + pub amount: std::string::String, + /// Mint decimals. + pub decimals: u8, + /// Exact fixed-scale decimal amount derived from `amount` and `decimals`. + pub decimal_amount: std::string::String, +} + +impl CanonicalTokenBalance { + /// Creates an exact canonical token balance without provider-specific UI formatting. + pub fn new( + account_index: u16, + mint: impl std::convert::Into, + owner: std::option::Option, + program_id: std::option::Option, + amount: impl std::convert::Into, + decimals: u8, + ) -> kb_core::Result { + let amount_value = amount.into(); + let normalized_result = normalize_unsigned_decimal(amount_value.as_str()); + let normalized_amount = match normalized_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let decimal_result = exact_decimal_amount(normalized_amount.as_str(), decimals); + let decimal_amount = match decimal_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(Self { + account_index, + mint: mint.into(), + owner, + program_id, + amount: normalized_amount, + decimals, + decimal_amount, + }); + } + + fn validate(&self, resolved_account_count: usize) -> kb_core::Result<()> { + if usize::from(self.account_index) >= resolved_account_count { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical token balance account index is outside resolved accounts", + )); + } + let mint_result = validate_pubkey_text(self.mint.as_str(), "token balance mint"); + if let std::result::Result::Err(error) = mint_result { + return std::result::Result::Err(error); + } + if let std::option::Option::Some(owner) = &self.owner { + let owner_result = validate_pubkey_text(owner.as_str(), "token balance owner"); + if let std::result::Result::Err(error) = owner_result { + return std::result::Result::Err(error); + } + } + if let std::option::Option::Some(program_id) = &self.program_id { + let program_result = + validate_pubkey_text(program_id.as_str(), "token balance program id"); + if let std::result::Result::Err(error) = program_result { + return std::result::Result::Err(error); + } + } + let decimal_result = exact_decimal_amount(self.amount.as_str(), self.decimals); + let expected_decimal_amount = match decimal_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if self.decimal_amount != expected_decimal_amount { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical token balance decimal amount does not match amount and decimals", + )); + } + return std::result::Result::Ok(()); + } +} + +/// Canonical transaction reward. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalReward { + /// Reward account public key. + pub pubkey: std::string::String, + /// Signed lamport delta. + pub lamports: i64, + /// Post-reward balance in lamports. + pub post_balance: u64, + /// Optional reward type string. + pub reward_type: std::option::Option, + /// Optional validator commission percentage. + pub commission: std::option::Option, +} + +/// Canonical transaction return data. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct CanonicalReturnData { + /// Program that returned the data. + pub program_id: std::string::String, + /// Return bytes encoded as standard base64. + pub data_base64: std::string::String, +} + +fn normalize_unsigned_decimal(amount: &str) -> kb_core::Result { + if amount.is_empty() || !amount.chars().all(|character| return character.is_ascii_digit()) { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical token balance amount must be an unsigned decimal string", + )); + } + let trimmed = amount.trim_start_matches('0'); + if trimmed.is_empty() { + return std::result::Result::Ok("0".to_string()); + } + return std::result::Result::Ok(trimmed.to_string()); +} + +fn exact_decimal_amount(amount: &str, decimals: u8) -> kb_core::Result { + let normalized_result = normalize_unsigned_decimal(amount); + let normalized = match normalized_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let scale = usize::from(decimals); + if scale == 0 { + return std::result::Result::Ok(normalized.clone()); + } + if normalized.len() > scale { + let split_index = normalized.len() - scale; + return std::result::Result::Ok(format!( + "{}.{}", + &normalized[..split_index], + &normalized[split_index..] + )); + } + let zero_count = scale - normalized.len(); + return std::result::Result::Ok(format!("0.{}{}", "0".repeat(zero_count), normalized)); +} + +fn sort_json_value(value: &mut serde_json::Value) { + match value { + serde_json::Value::Object(object) => { + let mut keys = object.keys().cloned().collect::>(); + keys.sort(); + let mut sorted = serde_json::Map::new(); + for key in keys { + let removed = object.remove(key.as_str()); + if let std::option::Option::Some(mut child) = removed { + sort_json_value(&mut child); + sorted.insert(key, child); + } + } + *object = sorted; + }, + serde_json::Value::Array(items) => { + for item in items { + sort_json_value(item); + } + }, + _ => {}, + } +} + +fn validate_signature_text(value: &str, field_name: &str) -> kb_core::Result<()> { + return validate_base58_length(value, field_name, 64); +} + +fn validate_pubkey_text(value: &str, field_name: &str) -> kb_core::Result<()> { + return validate_base58_length(value, field_name, 32); +} + +fn validate_base58_length( + value: &str, + field_name: &str, + expected_length: usize, +) -> kb_core::Result<()> { + if value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "{field_name} must not be empty" + ))); + } + let decode_result = bs58::decode(value).into_vec(); + let decoded = match decode_result { + std::result::Result::Ok(bytes) => bytes, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "{field_name} is not valid base58: {error}" + ))); + }, + }; + if decoded.len() != expected_length { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "{field_name} must decode to {expected_length} bytes" + ))); + } + return std::result::Result::Ok(()); +} + +#[cfg(test)] +mod tests { + fn sample_transaction() -> crate::CanonicalTransaction { + return crate::CanonicalTransaction { + format_version: crate::CANONICAL_TRANSACTION_FORMAT_VERSION, + primary_signature: "2Ana1pUpv2ZbMVkwF5FXapYeBEjdxDatLn7nvJkhgTSXbs59SyZSx866bXirPgj8QQVB57uxHJBG1YFvkRbFj4T".to_string(), + slot: 42, + block_time: std::option::Option::Some(1_700_000_000), + version: crate::CanonicalTransactionVersion::Legacy, + signatures: std::vec![ + "2Ana1pUpv2ZbMVkwF5FXapYeBEjdxDatLn7nvJkhgTSXbs59SyZSx866bXirPgj8QQVB57uxHJBG1YFvkRbFj4T".to_string(), + ], + message: crate::CanonicalTransactionMessage { + header: crate::CanonicalMessageHeader { + num_required_signatures: 1, + num_readonly_signed_accounts: 0, + num_readonly_unsigned_accounts: 1, + }, + static_account_keys: std::vec![ + "11111111111111111111111111111111".to_string(), + "SysvarC1ock11111111111111111111111111111111".to_string(), + ], + recent_blockhash: "11111111111111111111111111111111".to_string(), + instructions: std::vec![crate::CanonicalCompiledInstruction { + program_id_index: 0, + account_indexes: std::vec![1], + data_base64: "AQ==".to_string(), + stack_height: std::option::Option::Some(1), + }], + address_table_lookups: std::vec::Vec::new(), + loaded_addresses: crate::CanonicalLoadedAddresses::default(), + }, + metadata: std::option::Option::Some(crate::CanonicalTransactionMetadata { + status: crate::CanonicalTransactionStatus::Success, + error: std::option::Option::None, + fee: 5000, + pre_balances: std::vec![10_000, 1], + post_balances: std::vec![5_000, 1], + inner_instructions: std::vec::Vec::new(), + log_messages: std::vec!["Program 11111111111111111111111111111111 success".to_string()], + pre_token_balances: std::vec::Vec::new(), + post_token_balances: std::vec::Vec::new(), + rewards: std::vec::Vec::new(), + return_data: std::option::Option::None, + compute_units_consumed: std::option::Option::Some(100), + cost_units: std::option::Option::Some(120), + }), + }; + } + + #[test] + fn canonical_hash_is_stable() { + let first = sample_transaction(); + let second = sample_transaction(); + let first_hash = match first.canonical_json_hash() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("first hash failed: {error}"), + }; + let second_hash = match second.canonical_json_hash() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("second hash failed: {error}"), + }; + assert_eq!(first_hash, second_hash); + assert_eq!(first_hash.len(), 64); + } + + #[test] + fn canonical_hash_sorts_nested_error_objects() { + let mut first = sample_transaction(); + let mut second = sample_transaction(); + if let std::option::Option::Some(metadata) = &mut first.metadata { + metadata.status = crate::CanonicalTransactionStatus::Failed; + metadata.error = std::option::Option::Some(serde_json::json!({ + "z": 3, + "nested": {"b": 2, "a": 1} + })); + } + if let std::option::Option::Some(metadata) = &mut second.metadata { + metadata.status = crate::CanonicalTransactionStatus::Failed; + let parse_result = + serde_json::from_str::(r#"{"nested":{"a":1,"b":2},"z":3}"#); + metadata.error = match parse_result { + std::result::Result::Ok(value) => std::option::Option::Some(value), + std::result::Result::Err(error) => panic!("error fixture parse failed: {error}"), + }; + } + let first_hash = match first.canonical_json_hash() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("first nested hash failed: {error}"), + }; + let second_hash = match second.canonical_json_hash() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("second nested hash failed: {error}"), + }; + assert_eq!(first_hash, second_hash); + } + + #[test] + fn validation_rejects_signature_with_public_key_length() { + let mut transaction = sample_transaction(); + transaction.primary_signature = "3Bxs4NN8M2Yn4TLb7gR6Xy7n2D1Q8VjQqWcnpX8C6pQw".to_string(); + transaction.signatures[0] = transaction.primary_signature.clone(); + let result = transaction.validate(); + assert!(result.is_err()); + } + + #[test] + fn token_decimal_amount_is_derived_without_source_ui_formatting() { + let result = crate::CanonicalTokenBalance::new( + 0, + "So11111111111111111111111111111111111111112", + std::option::Option::None, + std::option::Option::None, + "000123", + 5, + ); + let balance = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("token balance creation failed: {error}"), + }; + assert_eq!(balance.amount, "123"); + assert_eq!(balance.decimal_amount, "0.00123"); + } + + #[test] + fn canonical_json_contains_no_provider_fields() { + let transaction = sample_transaction(); + let json_result = transaction.to_canonical_json(); + let json = match json_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("canonical json failed: {error}"), + }; + let text = json.to_string(); + assert!(!text.contains("provider")); + assert!(!text.contains("endpoint")); + assert!(!text.contains("protocol")); + } + + #[test] + fn validation_rejects_inconsistent_status_and_error() { + let mut transaction = sample_transaction(); + if let std::option::Option::Some(metadata) = &mut transaction.metadata { + metadata.status = crate::CanonicalTransactionStatus::Failed; + } + let result = transaction.validate(); + assert!(result.is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_model/src/decoded.rs b/migration/khadhroony-bot2-reference/kb_model/src/decoded.rs new file mode 100644 index 0000000..569b9fb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_model/src/decoded.rs @@ -0,0 +1,87 @@ +// file: kb_model/src/decoded.rs +// version: 4 + +//! Shared decoded protocol event model. + +use ts_rs::TS; // rust-rules: derive-import + +/// Source from which a decoded event was derived. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/decoded/EventSourceKind.ts" +)] +pub enum EventSourceKind { + /// Top-level instruction. + Instruction, + /// Inner instruction. + InnerInstruction, + /// Program log line. + Log, + /// Anchor event. + AnchorEvent, + /// Anchor self-CPI event. + AnchorSelfCpiEvent, + /// Balance delta inference. + BalanceDelta, + /// Synthetic test or generated event. + Synthetic, + /// Inferred event. + Inferred, + /// Audit-only event. + Audit, +} + +/// Confidence level for a decoded event. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/decoded/DecoderConfidence.ts" +)] +pub enum DecoderConfidence { + /// Exact manual decode. + Exact, + /// Exact IDL-based decode. + IdlExact, + /// Exact manually verified decode. + ManualExact, + /// Inferred decode. + Inferred, + /// Unsafe decode. + Unsafe, + /// Audit-only decode. + AuditOnly, + /// Unknown confidence. + Unknown, +} + +/// Protocol-level decoded event produced by a decoder. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/decoded/DecodedProtocolEvent.ts" +)] +pub struct DecodedProtocolEvent { + /// Transaction signature. + pub signature: crate::Signature, + /// Transaction slot. + pub slot: crate::Slot, + /// Instruction path. + pub instruction_path: crate::InstructionPath, + /// Program id. + pub program_id: crate::ProgramId, + /// Protocol family. + pub protocol_code: crate::ProtocolCode, + /// Protocol surface. + pub surface_code: crate::SurfaceCode, + /// Canonical event code. + pub event_code: crate::EventCode, + /// Short event name. + pub event_name: crate::EventName, + /// Event family. + pub event_family: crate::EventFamily, + /// Event source kind. + pub source_kind: crate::EventSourceKind, + /// Decoder confidence. + pub confidence: crate::DecoderConfidence, +} diff --git a/migration/khadhroony-bot2-reference/kb_model/src/lib.rs b/migration/khadhroony-bot2-reference/kb_model/src/lib.rs new file mode 100644 index 0000000..4cc4b98 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_model/src/lib.rs @@ -0,0 +1,77 @@ +// file: kb_model/src/lib.rs +// version: 2 + +//! Shared data model types. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod canonical_transaction; +mod decoded; +mod materialized; +mod nomenclature; +mod observation; +mod solana; + +/// Current canonical transaction document version. +pub use crate::canonical_transaction::CANONICAL_TRANSACTION_FORMAT_VERSION; +/// Canonical address lookup table reference. +pub use crate::canonical_transaction::CanonicalAddressTableLookup; +/// Canonical compiled Solana instruction. +pub use crate::canonical_transaction::CanonicalCompiledInstruction; +/// Canonical group of inner instructions. +pub use crate::canonical_transaction::CanonicalInnerInstructionGroup; +/// Canonical runtime-loaded addresses. +pub use crate::canonical_transaction::CanonicalLoadedAddresses; +/// Canonical Solana message header. +pub use crate::canonical_transaction::CanonicalMessageHeader; +/// Canonical transaction return data. +pub use crate::canonical_transaction::CanonicalReturnData; +/// Canonical transaction reward. +pub use crate::canonical_transaction::CanonicalReward; +/// Canonical exact token balance. +pub use crate::canonical_transaction::CanonicalTokenBalance; +/// Source-independent canonical Solana transaction. +pub use crate::canonical_transaction::CanonicalTransaction; +/// Canonical Solana transaction message. +pub use crate::canonical_transaction::CanonicalTransactionMessage; +/// Canonical Solana transaction execution metadata. +pub use crate::canonical_transaction::CanonicalTransactionMetadata; +/// Canonical Solana transaction execution status. +pub use crate::canonical_transaction::CanonicalTransactionStatus; +/// Canonical Solana transaction version. +pub use crate::canonical_transaction::CanonicalTransactionVersion; +/// Protocol-level decoded event produced by a decoder. +pub use crate::decoded::DecodedProtocolEvent; +/// Confidence level for a decoded event. +pub use crate::decoded::DecoderConfidence; +/// Source from which a decoded event was derived. +pub use crate::decoded::EventSourceKind; +/// Business-level materialized event placeholder. +pub use crate::materialized::MaterializedEvent; +/// Materialized event family. +pub use crate::materialized::MaterializedEventFamily; +/// Canonical event code in `.` format. +pub use crate::nomenclature::EventCode; +/// High-level event family. +pub use crate::nomenclature::EventFamily; +/// Event name without surface prefix. +pub use crate::nomenclature::EventName; +/// Stable internal program code. +pub use crate::nomenclature::ProgramCode; +/// Protocol family code, for example `raydium` or `meteora`. +pub use crate::nomenclature::ProtocolCode; +/// Concrete protocol surface code, for example `raydium_amm_v4`. +pub use crate::nomenclature::SurfaceCode; +/// Generic program observation derived from a Solana instruction. +pub use crate::observation::ProgramObservation; +/// Stable top-level or inner instruction path. +pub use crate::solana::InstructionPath; +/// Solana program id. +pub use crate::solana::ProgramId; +/// Solana public key. +pub use crate::solana::Pubkey; +/// Solana transaction signature. +pub use crate::solana::Signature; +/// Solana slot. +pub use crate::solana::Slot; diff --git a/migration/khadhroony-bot2-reference/kb_model/src/materialized.rs b/migration/khadhroony-bot2-reference/kb_model/src/materialized.rs new file mode 100644 index 0000000..681986d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_model/src/materialized.rs @@ -0,0 +1,80 @@ +// file: kb_model/src/materialized.rs +// version: 5 + +//! Shared materialized business event model. + +use ts_rs::TS; // rust-rules: derive-import + +/// Materialized event family. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/materialized/MaterializedEventFamily.ts" +)] +pub enum MaterializedEventFamily { + /// Trade materialization. + Trade, + /// Liquidity materialization. + Liquidity, + /// Lifecycle materialization. + Lifecycle, + /// Fee materialization. + Fee, + /// Admin materialization. + Admin, + /// Token-account materialization. + TokenAccount, + /// Pool-state materialization. + PoolState, + /// Orderbook materialization. + Orderbook, + /// Reward materialization. + Reward, + /// NFT materialization. + Nft, + /// Metadata materialization. + Metadata, + /// Oracle materialization. + Oracle, + /// Lending materialization. + Lending, + /// Staking materialization. + Staking, + /// Governance materialization. + Governance, + /// Bridge materialization. + Bridge, + /// Perpetuals materialization. + Perpetuals, + /// Vault materialization. + Vault, + /// Routing materialization. + Routing, + /// Compliance audit materialization. + ComplianceAudit, + /// Token metadata risk materialization. + TokenMetadataRisk, + /// Risk materialization. + Risk, + /// Transaction annotation materialization. + TransactionAnnotation, + /// Unknown materialization family. + Unknown, +} + +/// Business-level materialized event placeholder. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/materialized/MaterializedEvent.ts" +)] +pub struct MaterializedEvent { + /// Source transaction signature. + pub signature: crate::Signature, + /// Source transaction slot. + pub slot: crate::Slot, + /// Source decoded family. + pub decoded_family: crate::EventFamily, + /// Materialized family. + pub materialized_family: crate::MaterializedEventFamily, +} diff --git a/migration/khadhroony-bot2-reference/kb_model/src/nomenclature.rs b/migration/khadhroony-bot2-reference/kb_model/src/nomenclature.rs new file mode 100644 index 0000000..f1c722f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_model/src/nomenclature.rs @@ -0,0 +1,99 @@ +// file: kb_model/src/nomenclature.rs +// version: 3 + +//! Shared protocol, surface, and event nomenclature types. + +use ts_rs::TS; // rust-rules: derive-import + +/// Stable internal program code. +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/nomenclature/ProgramCode.ts" +)] +pub struct ProgramCode(pub std::string::String); + +/// Protocol family code, for example `raydium` or `meteora`. +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/nomenclature/ProtocolCode.ts" +)] +pub struct ProtocolCode(pub std::string::String); + +/// Concrete protocol surface code, for example `raydium_amm_v4`. +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/nomenclature/SurfaceCode.ts" +)] +pub struct SurfaceCode(pub std::string::String); + +/// Event name without surface prefix. +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_model/nomenclature/EventName.ts")] +pub struct EventName(pub std::string::String); + +/// Canonical event code in `.` format. +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_model/nomenclature/EventCode.ts")] +pub struct EventCode(pub std::string::String); + +/// High-level event family. +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/nomenclature/EventFamily.ts" +)] +pub enum EventFamily { + /// Swap or buy/sell event. + Trade, + /// Liquidity deposit/withdraw or equivalent. + Liquidity, + /// Pool, market, curve, or config lifecycle event. + Lifecycle, + /// Fee claim, collection, sweep, or update. + Fee, + /// Admin or authority event. + Admin, + /// Reward event. + Reward, + /// Orderbook event. + Orderbook, + /// Token account event. + TokenAccount, + /// Token mint event. + TokenMint, + /// Token burn event. + TokenBurn, + /// NFT event. + Nft, + /// Token or NFT metadata event. + Metadata, + /// Oracle event. + Oracle, + /// Lending or borrowing event. + Lending, + /// Staking event. + Staking, + /// Governance event. + Governance, + /// Bridge event. + Bridge, + /// Perpetuals or derivatives event. + Perpetuals, + /// Vault event. + Vault, + /// Router or aggregator event. + Routing, + /// Compliance audit event. + ComplianceAudit, + /// Token metadata risk event. + TokenMetadataRisk, + /// Risk signal. + Risk, + /// Audit-only event. + Audit, + /// Unknown family. + Unknown, +} diff --git a/migration/khadhroony-bot2-reference/kb_model/src/observation.rs b/migration/khadhroony-bot2-reference/kb_model/src/observation.rs new file mode 100644 index 0000000..80f02b1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_model/src/observation.rs @@ -0,0 +1,31 @@ +// file: kb_model/src/observation.rs +// version: 4 + +//! Shared program observation model. + +use ts_rs::TS; // rust-rules: derive-import + +/// Generic program observation derived from a Solana instruction. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize, TS)] +#[ts( + export, + export_to = "../frontend/ts/bindings/kb_model/observation/ProgramObservation.ts" +)] +pub struct ProgramObservation { + /// Transaction signature. + pub signature: crate::Signature, + /// Transaction slot. + pub slot: crate::Slot, + /// Instruction path. + pub instruction_path: crate::InstructionPath, + /// Program id. + pub program_id: crate::ProgramId, + /// Optional 8-byte discriminator in hexadecimal. + pub discriminator_8: std::option::Option, + /// Raw instruction data length. + pub data_len: usize, + /// Number of instruction accounts. + pub accounts_len: usize, + /// Whether the source transaction failed. + pub failed: bool, +} diff --git a/migration/khadhroony-bot2-reference/kb_model/src/solana.rs b/migration/khadhroony-bot2-reference/kb_model/src/solana.rs new file mode 100644 index 0000000..c895080 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_model/src/solana.rs @@ -0,0 +1,43 @@ +// file: kb_model/src/solana.rs +// version: 3 + +//! Shared Solana primitive wrapper types. + +use ts_rs::TS; // rust-rules: derive-import + +/// Solana transaction signature. +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_model/solana/Signature.ts")] +pub struct Signature(pub std::string::String); + +/// Solana slot. +#[derive( + Clone, + Copy, + Debug, + Eq, + PartialEq, + Ord, + PartialOrd, + Hash, + serde::Deserialize, + serde::Serialize, + TS, +)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_model/solana/Slot.ts")] +pub struct Slot(pub u64); + +/// Solana program id. +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_model/solana/ProgramId.ts")] +pub struct ProgramId(pub std::string::String); + +/// Solana public key. +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_model/solana/Pubkey.ts")] +pub struct Pubkey(pub std::string::String); + +/// Stable top-level or inner instruction path. +#[derive(Clone, Debug, Eq, PartialEq, Hash, serde::Deserialize, serde::Serialize, TS)] +#[ts(export, export_to = "../frontend/ts/bindings/kb_model/solana/InstructionPath.ts")] +pub struct InstructionPath(pub std::string::String); diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/Cargo.toml b/migration/khadhroony-bot2-reference/kb_pipeline/Cargo.toml new file mode 100644 index 0000000..e584a70 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/Cargo.toml @@ -0,0 +1,60 @@ +# file: kb_pipeline/Cargo.toml +# version: 17 + +[package] +name = "kb_pipeline" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +bs58.workspace = true +chrono.workspace = true +futures-util.workspace = true +kb_config = { path = "../kb_config" } +kb_core = { path = "../kb_core" } +kb_decoder_api = { path = "../kb_decoder_api" } +kb_decoder_solana_core = { path = "../kb_decoder_solana_core" } +kb_decoder_spl_elgamal_registry = { path = "../kb_decoder_spl_elgamal_registry" } +kb_decoder_spl_token_2022 = { path = "../kb_decoder_spl_token_2022" } +kb_execution_api = { path = "../kb_execution_api" } +kb_execution_safety = { path = "../kb_execution_safety" } +kb_execution_solana = { path = "../kb_execution_solana" } +kb_executor_solana_core = { path = "../kb_executor_solana_core" } +kb_executor_spl_token_2022 = { path = "../kb_executor_spl_token_2022" } +kb_executor_spl_associated_token_account = { path = "../kb_executor_spl_associated_token_account" } +kb_executor_spl_memo = { path = "../kb_executor_spl_memo" } +kb_executor_spl_token = { path = "../kb_executor_spl_token" } +kb_materializer_admin = { path = "../kb_materializer_admin" } +kb_materializer_api = { path = "../kb_materializer_api" } +kb_materializer_fees = { path = "../kb_materializer_fees" } +kb_materializer_metadata = { path = "../kb_materializer_metadata" } +kb_materializer_token_accounts = { path = "../kb_materializer_token_accounts" } +kb_model = { path = "../kb_model" } +kb_program_ids = { path = "../kb_program_ids" } +kb_rpc = { path = "../kb_rpc" } +kb_store_core = { path = "../kb_store_core" } +kb_wallet = { path = "../kb_wallet" } +serde.workspace = true +serde_json.workspace = true +sha2.workspace = true +solana-address-lookup-table-interface.workspace = true +solana-pubkey.workspace = true +spl-associated-token-account-interface.workspace = true +spl-elgamal-registry-interface.workspace = true +tokio.workspace = true +tracing.workspace = true +uuid.workspace = true + +[dev-dependencies] +async-trait.workspace = true +kb_decoder_spl_associated_token_account = { path = "../kb_decoder_spl_associated_token_account" } +kb_decoder_spl_memo = { path = "../kb_decoder_spl_memo" } +kb_decoder_spl_token = { path = "../kb_decoder_spl_token" } +kb_materializer_risk = { path = "../kb_materializer_risk" } +kb_materializer_transaction_annotations = { path = "../kb_materializer_transaction_annotations" } +kb_store_pg = { path = "../kb_store_pg" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/README.md b/migration/khadhroony-bot2-reference/kb_pipeline/README.md new file mode 100644 index 0000000..5344097 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/README.md @@ -0,0 +1,214 @@ + + + +# kb_pipeline + +Ce crate orchestre les étapes d'ingestion, extraction, observation, décodage, matérialisation et validation. + + +## Lectures stateful Token-2022 et ElGamal Registry + +`read_token_2022_stateful_snapshot` effectue un `getAccountInfo` complet avec commitment `confirmed`, `minContextSlot` optionnel et limite de données explicite comprise entre 1 et 65 536 octets. La réponse est refusée avant parsing si le compte est absent, exécutable, détenu par un autre programme, plus grand que la limite ou si les données décodées ne couvrent pas exactement `space`. Le buffer validé est ensuite parsé comme Mint, Account ou Multisig et routé vers les matérialiseurs propriétaires token accounts, metadata/group, fees et admin. + +`read_elgamal_registry_stateful_snapshot` applique le même contrat avec une taille exacte de 64 octets, le Program ID propre du registre et la validation du PDA dérivé du propriétaire stocké. Ces fonctions n'effectuent ni déchiffrement ni validation rétroactive des preuves cryptographiques. + +Les variantes pures `materialize_token_2022_account_info_result` et `materialize_elgamal_registry_account_info_result` permettent de tester ou réutiliser la validation d'une réponse RPC déjà obtenue. + +## Token-2022 state snapshots + +`materialize_token_2022_stateful_snapshot` validates the Token-2022 owner boundary, parses one bounded Mint, Account, or Multisig buffer, checks embedded Token Metadata and Token Group mint identities against the carrying account, and routes outputs to their unique materializers. The function does not perform RPC reads itself; callers retain control over endpoint selection, account-data bounds, commitment, and post-execution timing. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. + +## Backfill HTTP 0.3.3 + +Le crate expose `execute_http_backfill`, `BackfillRequest`, `BackfillSource` et `BackfillObserver`. Le moteur prend en charge les signatures explicites et les historiques programme/token/pool. Une recherche `Before` sans ancre commence sur les signatures les plus récentes et utilise un `filter_code` `program_latest`, `token_latest` ou `pool_latest`; une recherche `After` conserve une ancre obligatoire. Il applique les limites des rôles `kb_rpc`, normalise avec `getTransaction`, écrit le store canonique et produit une observation légère par tentative. + +## Replay decode `0.4.0` + +`execute_decode_replay` sélectionne des instructions core contextualisées par signatures, états, slots, program IDs et instruction paths. Il applique un dispatch déterministe, une concurrence bornée, l’arrêt coopératif, le skip processor/version/hash, un force replay obligatoirement borné par signatures ou par autorisation « toutes les signatures », et la persistance atomique via `DecodePipelineStore`. +Depuis `0.4.1-pre.014`, le contrat core `2` ajoute les instructions outer ordonnées au calcul de `contextual_input_hash`. `DECODE_PIPELINE_VERSION` reste `1` : le changement de contrat est déjà versionné et force naturellement un nouveau hash. Après application du delta, un force replay est nécessaire pour remplacer les résultats calculés avec le contrat `1`. Les transactions, rollbacks et clés d’idempotence du pipeline restent inchangés. + +La matérialisation reste optionnelle, exige au moins un matérialiseur enregistré et n’est appelée qu’après la persistance réussie du décodage. Depuis `0.4.1-pre.005`, la sélection vérifie d’abord l’applicabilité exacte surface/entrée ; les observations de même famille mais hors périmètre ne créent plus de faux refus. Le résumé distingue les statuts par processor, les skips, les sorties matérialisées et les refus de politique. + +## Tracing opérationnel + +`kb_pipeline` utilise le target canonique `kb_pipeline`. Les champs `action`, `campaign_id`, `capture_session_id`, `signature`, `instruction_path`, processor et statut remplacent les anciens targets suffixés par module. + +Le backfill journalise sa validation, la sélection du client, la découverte des candidats, les erreurs terminales et le résumé. Le replay decode émet un événement `error` pour tout input sélectionné mais unmatched, tout résultat `failed`/`unsupported`, tout résultat de matérialisation `failed` et toute erreur de persistance. Une annulation coopérative reste un `warn`, et une transaction on-chain échouée mais correctement décodée n’est pas une erreur logicielle. + +## Orchestration Devnet `0.4.2-pre.009` + +`execute_devnet_system_transfer` relie les briques déjà stabilisées sans les fusionner. L’appel vérifie le genesis hash Devnet, charge ou crée le wallet temporaire persistant du profil, contrôle si le destinataire existe et impose le minimum rent-exempt retourné par le RPC pour une adresse neuve, contrôle le solde et l’airdrop éventuel, construit un plan System transfer, estime les frais, simule le message exact, puis signe et envoie uniquement lorsque `submit` et la confirmation opérateur sont explicites. + +Après confirmation, l’orchestrateur réutilise successivement : + +```text +execute_http_backfill + -> execute_core_extraction + -> execute_decode_replay +``` + +La signature exacte est l’unique sélection de chaque campagne. Le résumé conserve séparément le plan, le blockhash, les frais, la simulation, l’envoi, la confirmation, le backfill, l’extraction, le replay et le diagnostic agrégé. Le mode simulation seule retourne avant signature et ne crée pas de faux diagnostic post-exécution. Après signature, les erreurs d’envoi, de confirmation ou de replay restent attachées à la signature dans le résumé au lieu de masquer une transaction potentiellement diffusée. + +`SolanaExecutionObserver` compose les observers existants du backfill, de l’extraction et du replay avec les événements propres à l’exécution. Cette API reste utilisable par une CLI, un worker ou une application distincte de `kb_app_demo`. + +Le test réseau réel reste opt-in via `KB_DEVNET_EXECUTION_TEST=1`; il n’est jamais lancé par la suite de tests standard. Il a été validé avec un wallet préfinancé et un transfert de 1 000 000 lamports vers une adresse neuve. Une simulation refusée conserve son erreur runtime et un extrait borné des logs afin de distinguer un défaut de transaction d’un défaut de transport. + +## Orchestration Memo v4 Devnet `0.4.3-pre.004` + +`execute_devnet_memo` est le parcours opérateur Devnet de l’exécuteur SPL Memo universel. La restriction à v4 et Devnet appartient uniquement à cette démonstration : `kb_executor_spl_memo` reste capable de construire v1, v3 et v4 pour tous les clusters, sous le contrôle de la politique commune et de la simulation effective du programme demandé. + +`DevnetMemoExecutionRequest::new` produit un dry-run. Le champ `submit` n’autorise la signature et l’envoi qu’avec un profil Devnet mutable et la confirmation opérateur requise. Le wallet temporaire persistant du profil paie uniquement les frais réseau ; le plan annonce une dépense d’instruction nulle. Le wallet peut également être fourni comme compte signer readonly du Memo afin de valider le contrat runtime v4 sans exposer une seconde clé. + +Après une simulation réussie et un envoi explicitement autorisé, le parcours confirme la signature puis enchaîne l’hydratation canonique, l’extraction core, le decode replay Memo avec matérialisation, une lecture bornée de la projection `transaction_annotation` et un second replay non forcé. Le résumé conserve les lignes d’annotation exactes et le résultat du second replay ; celui-ci doit rester sans échec, sans nouvelle sortie matérialisée et sans refus pour établir l’idempotence. + +La première tranche n’ajoute aucune commande ni fenêtre Tauri. Elle stabilise le contrat backend avant l’exposition dans `kb_app_demo`. Le wallet Devnet doit déjà disposer du solde nécessaire au plafond de frais configuré ; aucun airdrop implicite n’est déclenché par le parcours Memo. + +Le test réseau est opt-in avec `KB_DEVNET_MEMO_EXECUTION_TEST=1` et exige `KB_POSTGRES_TEST_URL`. Il reste en simulation seule par défaut. L’envoi réel demande en plus `KB_DEVNET_MEMO_SUBMIT=1`, ce qui constitue l’autorisation explicite de dépenser les frais Devnet et d’exécuter toute la post-validation. `KB_DEVNET_WALLET_DIR` permet de sélectionner le wallet persistant déjà financé. + +Le parcours a été validé réellement le 15 juillet 2026 : une simulation seule, puis trois envois v4 confirmés. Les trois transactions ont été insérées, extraites, décodées et matérialisées ; leurs seconds replays ont chacun compté un skip, zéro échec, zéro refus et zéro nouvelle sortie matérialisée. + + +## Préflight stateful Solana Core + +`inspect_solana_core_stateful_readiness(pool, request)` lit de manière bornée les comptes et l’époque nécessaires avant simulation. La requête contient un rôle HTTP, le cluster attendu Localnet/Devnet et un `SolanaCoreOperation`. Le résultat contient `Ready`, `Blocked` ou `NotRequired`, un slot de contexte, des contrôles ordonnés et des faits mesurés. + +La fonction couvre ALT, Config, Feature, Slashing et les contextes ZK ElGamal. Elle vérifie le genesis hash, mais ne construit pas le plan, ne signe pas, ne simule pas et n’envoie pas. Les erreurs de transport ou de forme RPC restent des `kb_core::Error`; une précondition métier non satisfaite produit un rapport `Blocked` exploitable par les CLI, workers et interfaces. + +## Préflight stateful SPL Token classique `0.4.4-pre.005` + +`inspect_spl_token_stateful_readiness` conserve l’exécuteur déterministe et sans I/O. Le pipeline vérifie le genesis Localnet/Devnet puis lit au maximum 128 comptes complets, chacun borné à la taille officielle maximale du multisig classique. Les parseurs locaux reproduisent exactement les layouts `Mint` de 82 octets, `Account` de 165 octets et `Multisig` de 355 octets, sans activer `bincode` et sans accepter de suffixe. + +Le rapport `Ready` ou `Blocked` expose owner programme, existence, layout, initialisation, mint, decimals, état frozen, solde brut, réserve native, delegate et allowance, autorités mint/freeze/owner/close, ainsi que `M/N` et les membres effectivement fournis. Les comptes à initialiser sont également comparés au minimum rent-exempt renvoyé par le cluster. `Batch` conserve l’ordre de ses enfants et mutualise les lectures de comptes. + +Les tests standards restent entièrement offline. Le test `optional_devnet_checked_transfer_readiness_from_env` est activé uniquement par `KB_DEVNET_SPL_TOKEN_PREFLIGHT_TEST=1`; il exige les variables `KB_DEVNET_SPL_TOKEN_SOURCE`, `KB_DEVNET_SPL_TOKEN_MINT`, `KB_DEVNET_SPL_TOKEN_DESTINATION` et `KB_DEVNET_SPL_TOKEN_AUTHORITY`. `KB_DEVNET_SPL_TOKEN_DECIMALS` et `KB_DEVNET_SPL_TOKEN_AMOUNT` sont optionnels. Ce test lit et vérifie un `TransferChecked`, mais ne signe, ne simule et n’envoie rien. Le parcours complet de création contrôlée, simulation, envoi et post-validation reste une étape d’orchestration distincte. + +## Orchestration SPL Token Devnet `0.4.4-pre.005-delta-fix-001` + +`simulate_devnet_spl_token` accepte une opération typée universelle mais impose Devnet dans ce parcours opérateur. Il exécute le préflight stateful, charge le wallet persistant du profil comme fee payer, construit le plan officiel, applique la safety commune, estime les frais et simule la transaction exacte avec une preuve liée au hash du message. Cette API ne dépend d’aucun store, ne signe pas et exige `submit=false`. + +`execute_devnet_spl_token` réutilise exactement la même préparation. L’envoi demande `submit=true`, la confirmation opérateur configurée et un wallet capable de résoudre tous les signataires du message ; une autorité externe ou un multisig non disponible est refusé avant signature. Après confirmation, l’orchestrateur hydrate la transaction canonique, exécute core extraction, rejoue uniquement le programme Token classique, lit les matérialisations de la signature et lance un second replay pour vérifier l’absence de nouvelle sortie. + +Le test réseau `optional_devnet_checked_transfer_simulation_and_submission_from_env` utilise les mêmes variables que le préflight, avec `KB_DEVNET_SPL_TOKEN_EXECUTION_TEST=1`. Il reste en simulation seule par défaut. `KB_DEVNET_SPL_TOKEN_SUBMIT=1` autorise l’envoi et exige alors `KB_POSTGRES_TEST_URL`; le wallet sélectionné par `KB_DEVNET_WALLET_DIR` doit correspondre à l’autorité Token fournie. + +La simulation puis l’envoi réel ont réussi sur Devnet le 15 juillet 2026 avec deux comptes Token auxiliaires classiques. Le test soumis vérifie désormais explicitement la soumission, la confirmation, le premier replay sans échec ni refus, la présence d’une matérialisation et le second replay sans nouvelle sortie ; avec `--nocapture`, il imprime la signature, le statut et le nombre de matérialisations. + +Le probe `optional_devnet_recent_instruction_probe_from_env`, activé par +`KB_DEVNET_SPL_TOKEN_RECENT_PROBE_TEST=1`, audite séparément les deux tags récents sans envoi. Son +`Batch` contient un `TransferChecked` de montant brut nul sur les comptes classiques déjà contrôlés. +`UnwrapLamports` utilise un compte wrapped SOL auxiliaire classique fourni par +`KB_DEVNET_SPL_TOKEN_NATIVE_ACCOUNT` et simule le retrait d'un lamport vers l'autorité. Le compte +natif doit être initialisé, possédé par le programme Token classique, contenir au moins un lamport +wrapped et avoir `KB_DEVNET_SPL_TOKEN_AUTHORITY` comme owner ou close authority. + +Le test n'impose pas le succès runtime : il imprime pour chaque tag `success`, l'erreur, les compute +units et les logs afin qu'un rejet de déploiement reste une preuve d'audit exploitable. Il échoue en +revanche lorsque le préflight, la configuration ou le transport RPC ne permettent pas d'atteindre +la simulation. Il ne signe et n'envoie aucune transaction. + +Le probe exécuté le 16 juillet 2026 sur le programme classique Devnet a réussi pour les deux tags : +`Batch` a consommé 270 compute units et `UnwrapLamports` 140, sans erreur runtime. Cette preuve +simulation-only établit le déploiement Devnet observé, mais ne vaut ni soumission ni validation de +déploiement Localnet, Testnet ou Mainnet. + +## Lifecycle SPL Token Devnet `0.4.4-pre.006` + +`prepare_devnet_spl_token_lifecycle_accounts` remplace la commande CLI historique `solana create-account`, absente de Solana CLI 4. Il génère trois keypairs éphémères, mesure le rent exact, construit trois `SystemCreateAccount` avec les tailles 82/165/165 et le propriétaire Token classique, simule, signe avec le payeur et le nouveau compte, confirme puis vérifie les données entièrement nulles. Cette préparation dépense du rent et exige donc `submit=true` et confirmation opérateur explicite. + +`execute_devnet_spl_token_lifecycle` accepte ensuite le mint et les deux comptes Token frais, rent-exempt, non initialisés et possédés par le programme classique. Il refuse les alias de comptes, les montants non canoniques, une délégation vers l'autorité elle-même et toute exécution sans `submit=true` et confirmation opérateur explicite. + +Le lifecycle conserve les montants bruts en chaînes et enchaîne onze appels à l'orchestrateur unitaire : `InitializeMint2`, deux `InitializeAccount3`, `MintToChecked`, `TransferChecked`, `ApproveChecked`, `Revoke`, deux `BurnChecked` et deux `CloseAccount`. Le montant minté est exactement réparti entre les deux burns. Chaque étape doit produire une signature confirmée, une matérialisation et un second replay sans nouvelle sortie avant le début de la suivante. Une séquence partiellement réussie n'est jamais présentée comme atomique ni automatiquement compensée. + +Le test opt-in prépare et consomme automatiquement des comptes frais avec `KB_DEVNET_SPL_TOKEN_LIFECYCLE_PREPARE=1`. Les variables manuelles `MINT`, `SOURCE`, `DESTINATION`, `DELEGATE` et `AUTHORITY` restent acceptées lorsque la préparation automatique est désactivée. + +Une reprise utilise `KB_DEVNET_SPL_TOKEN_LIFECYCLE_FIRST_STEP_INDEX` et exige `KB_DEVNET_SPL_TOKEN_LIFECYCLE_RESUME_PREDECESSOR_SIGNATURE`. La signature n'est pas un simple acquittement opérateur : elle doit produire la transaction canonique réussie et exactement l'opération matérialisée attendue pour l'étape précédente. `KB_DEVNET_SPL_TOKEN_LIFECYCLE_INTER_STEP_DELAY_MS`, borné à 30 secondes, espace les étapes ; le test imprime chaque signature dès la récupération ou la validation correspondante. + +## Orchestration ATA Devnet `0.4.5-pre.006` + +Le test opt-in `optional_devnet_operation_from_env` appelle le même orchestrateur que Tauri. Il +reste en simulation seule par défaut et demande `KB_DEVNET_SPL_ATA_EXECUTION_TEST=1`. Le mode +`create_idempotent`, utilisé par défaut, reçoit `KB_DEVNET_SPL_ATA_MINT`. Le mode +`recover_nested` exige en plus : + +```text +KB_DEVNET_SPL_ATA_OPERATION=recover_nested +KB_DEVNET_SPL_ATA_OWNER_MINT= +KB_DEVNET_SPL_ATA_NESTED_MINT= +KB_DEVNET_SPL_ATA_TOKEN_PROGRAM=classic|token_2022 +``` + +La fixture doit exister avant le test : les deux mints sont initialisés sous le Token Program +sélectionné ; l'ATA owner du wallet, le nested ATA dérivé avec cet ATA comme owner et l'ATA de +destination du wallet sont initialisés et cohérents. Un solde positif du nested mint rend le transfert +observable. `KB_DEVNET_SPL_ATA_SUBMIT=1` autorise ensuite explicitement la récupération destructive. +Le wallet persistant du profil doit être le wallet owner et signer ; aucun autre signataire n'est +résolu silencieusement. + +Après confirmation, le test exige la fermeture du nested ATA, la cohérence des deux comptes +restants, la projection lifecycle `nested_ata_recovered`, le fait distinct +`nested_ata_anti_pattern_recovered` sans score et un second replay sans nouvelle sortie. Les CPI +Token classiques restent la propriété des matérialiseurs Token ; les CPI Token-2022 ne commencent +pas le décodage général réservé à `0.4.6`. + +Le scénario classique contrôlé a été validé sur Devnet le 16 juillet 2026. La signature +`37LG6GdMRp5ECG8qf1RSYxmriJk75UtCwqwzXZWpArebkKo6BQvGhYEnbDh2A6KWiUaHK28Zy1gGGZ5zihvrHskM` +a déplacé 1 000 000 000 unités brutes vers l'ATA du wallet, fermé le nested ATA et produit exactement +deux matérialisations parent ATA. L'owner ATA est resté initialisé et le second replay n'a créé +aucune nouvelle sortie. + +## Validation de clôture `0.4.5` + +La régression finale du 16 juillet 2026 confirme 90 tests pipeline, dont les préflights et +postconditions ATA offline, ainsi que le test Devnet `RecoverNested` soumis. Les régressions +associées confirment 10 tests décodeur ATA, 10 tests exécuteur ATA, 113 tests RPC, 41 tests +configuration, 111 tests application, 46 tests PostgreSQL réels et Clippy global propre. + +## Validation finale `0.4.2` + +La suite finale compte 56 tests. Le test réseau opt-in `optional_devnet_system_transfer_from_env` a réussi avec un wallet Devnet persistant préfinancé, un transfert de 1 000 000 lamports et PostgreSQL réel. Il valide le parcours commun jusqu’à l’insertion canonique, l’extraction core et le decode replay ciblé. + +Cette preuve ne contourne pas les préflights propres aux opérations administratives. Une opération ALT, Config, Feature, Slashing, ZK, Stake, Vote ou Loader doit encore produire un rapport stateful admissible et une preuve cluster dédiée avant toute exposition mutable supplémentaire. + +### Préflight stateful Token-2022 borné + +`inspect_token_2022_preflight` compose les lectures RPC validées de `pre.040` avant simulation. La requête impose un plafond de comptes distincts, un budget cumulé de données, des rôles non vides et des exigences exactes par compte (`Mint`, `Account` ou `Multisig`). Les doublons strictement identiques sont dédupliqués dans l'ordre de première apparition ; les doublons contradictoires sont refusés. + +Le préflight peut vérifier le mint et le propriétaire attendus d'un Token Account, les decimals d'un Mint, les extensions requises et, lorsque nécessaire, un registre ElGamal lu et validé par son PDA officiel. Il retourne le slot contextuel maximal observé et ne simule ni n'envoie aucune transaction. + +### Corrélation instruction/snapshot Token-2022 + +`correlate_token_2022_instruction_with_snapshot` compare un fait instructionnel matérialisé avec l’inventaire d’extensions d’un snapshot final déjà validé. Le résultat est explicitement `Confirmed`, `Contradicted` ou `NotApplicable`; il ne produit ni score, ni violation, ni persistance implicite. + +## Préflight cryptographique Token-2022 + +`inspect_token_2022_cryptographic_preflight` valide avant simulation les comptes `ProofContextState` déjà vérifiés utilisés par les builders confidentiels. La lecture est bornée à huit comptes distincts. Chaque compte doit appartenir au programme natif ZK ElGamal Proof, être non exécutable, avoir la taille exacte du type de preuve attendu et conserver le discriminant officiel correspondant. Une autorité de fermeture attendue peut être exigée. Les doublons exacts sont dédupliqués ; les exigences contradictoires échouent avant tout appel RPC. + +Cette API ne génère, ne vérifie et ne déchiffre aucune preuve. Les preuves inline restent validées par leurs offsets et par la simulation du message exact ; les comptes context-state sont vérifiés ici avant cette simulation. + +### Orchestration des preuves Token-2022 + +`orchestrate_token_2022_proofs` relie les références de preuve des intents confidentiels au rapport de préflight ZK. Le contrat conserve l'ordre des preuves, refuse les kinds, offsets ou comptes context-state dupliqués, exige l'Instructions Sysvar seulement en présence d'au moins une preuve inline, impose simulation et plafonds non nuls, et refuse toute soumission non confirmée. Il ne génère aucune preuve et ne signe aucune transaction. + +### Orchestration d’exécution Token-2022 + +`validate_token_2022_execution_readiness` relie le plan exact de l’exécuteur, le hash du message compilé, la preuve de simulation, les préflights Token-2022 et ZK, l’orchestration ordonnée des preuves et la résolution complète des signataires. Une soumission est refusée si le message simulé diffère, si un signataire manque, si le plan reste en dry-run ou si la confirmation opérateur est absente. + +Les postconditions utilisent exclusivement `confirmed`, `contradicted` et `not_applicable`. Une postcondition non implémentée ne peut jamais être transformée en succès. + +## Validation de clôture Token-2022 + +Le module `solana_token_2022_validation` impose un vocabulaire de statut explicite et un inventaire borné des preuves de validation. Un scénario end-to-end ne peut être marqué `confirmed` que si les preuves d’hydratation canonique, extraction core, replay, matérialisation et second replay sont toutes présentes. Les scénarios non exécutés ou indisponibles restent explicitement `not_run` ou `unavailable`. + +### Matrice de validation Token-2022 + +`load_token_2022_validation_matrix` charge `docs/SPL_TOKEN_2022_VALIDATION_MATRIX.json` et impose l'inventaire exact des neuf scénarios, le vocabulaire des statuts et toutes les preuves requises avant un statut `confirmed`. Une preuve observée ne peut pas être attachée à un scénario `not_run` ou `unavailable`. diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/backfill.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/backfill.rs new file mode 100644 index 0000000..42953d7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/backfill.rs @@ -0,0 +1,2017 @@ +// file: kb_pipeline/src/backfill.rs +// version: 9 + +//! Reusable HTTP transaction backfill orchestration. + +use futures_util::FutureExt; // rust-rules: trait-import +use futures_util::StreamExt; // rust-rules: trait-import +use sha2::Digest; // rust-rules: trait-import + +const AFTER_HISTORY_PAGE_SIZE: u16 = 1000; +const CANCELLATION_POLL_INTERVAL_MS: u64 = 25; + +/// Address category used to identify one targeted signature history campaign. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum BackfillAddressKind { + /// Executable Solana program address. + Program, + /// Token mint address. + Token, + /// Pool account address. + Pool, +} + +impl BackfillAddressKind { + /// Returns the stable filter code prefix. + pub fn code(&self) -> &'static str { + return match self { + crate::BackfillAddressKind::Program => "program", + crate::BackfillAddressKind::Token => "token", + crate::BackfillAddressKind::Pool => "pool", + }; + } +} + +/// Chronological direction relative to one anchor signature. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum BackfillDirection { + /// Fetch signatures older than the anchor. + Before, + /// Fetch the nearest signatures newer than the anchor. + After, +} + +impl BackfillDirection { + /// Returns the stable direction code. + pub fn code(&self) -> &'static str { + return match self { + crate::BackfillDirection::Before => "before", + crate::BackfillDirection::After => "after", + }; + } +} + +/// Candidate source used by one backfill campaign. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum BackfillSource { + /// Explicit transaction signatures supplied by the operator. + ExplicitSignatures(std::vec::Vec), + /// Signatures discovered from one address history. + AddressHistory { + /// Semantic category of the address. + kind: crate::BackfillAddressKind, + /// Program, token mint or pool address. + address: std::string::String, + /// Optional anchor signature. `Before` without an anchor starts from the latest history page. + anchor_signature: std::option::Option, + /// Chronological direction relative to the anchor. + direction: crate::BackfillDirection, + /// Maximum number of signatures to hydrate. + limit: usize, + }, +} + +/// Complete bounded backfill request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BackfillRequest { + /// Endpoint role used by the HTTP pool. + pub role: std::string::String, + /// Requested commitment for history and transaction calls. + pub commitment: std::string::String, + /// Candidate source. + pub source: crate::BackfillSource, + /// Maximum RPC page size between 1 and 1000. + pub page_size: u16, + /// Maximum number of history pages inspected. + pub max_pages: u32, + /// Operator concurrency cap before endpoint role limits are applied. + pub max_concurrent_requests: u32, + /// Number of retries after the initial HTTP attempt. + pub max_retries: u32, +} + +impl BackfillRequest { + /// Validates request bounds and source identifiers. + pub fn validate(&self) -> kb_core::Result<()> { + if self.role.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "backfill endpoint role must not be empty", + )); + } + let transaction_config_result = + kb_rpc::GetTransactionConfig::new(self.commitment.clone(), 0); + if let std::result::Result::Err(error) = transaction_config_result { + return std::result::Result::Err(error); + } + if self.page_size == 0 || self.page_size > 1000 { + return std::result::Result::Err(kb_core::Error::config( + "backfill page size must be between 1 and 1000", + )); + } + if self.max_pages == 0 { + return std::result::Result::Err(kb_core::Error::config( + "backfill max pages must be greater than zero", + )); + } + if self.max_concurrent_requests == 0 { + return std::result::Result::Err(kb_core::Error::config( + "backfill concurrency must be greater than zero", + )); + } + match &self.source { + crate::BackfillSource::ExplicitSignatures(signatures) => { + if signatures.is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "explicit signature backfill requires at least one signature", + )); + } + for signature in signatures { + let validation_result = kb_rpc::validate_transaction_signature_text( + signature.as_str(), + "explicit backfill signature", + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + } + }, + crate::BackfillSource::AddressHistory { + address, + anchor_signature, + direction, + limit, + .. + } => { + let address_result = kb_rpc::validate_solana_pubkey_text( + address.as_str(), + "backfill history address", + ); + if let std::result::Result::Err(error) = address_result { + return std::result::Result::Err(error); + } + if let std::option::Option::Some(signature) = anchor_signature { + let signature_result = kb_rpc::validate_transaction_signature_text( + signature.as_str(), + "backfill anchor signature", + ); + if let std::result::Result::Err(error) = signature_result { + return std::result::Result::Err(error); + } + } else if *direction == crate::BackfillDirection::After { + return std::result::Result::Err(kb_core::Error::config( + "newer address history backfill requires an anchor signature", + )); + } + if *limit == 0 { + return std::result::Result::Err(kb_core::Error::config( + "address history backfill limit must be greater than zero", + )); + } + }, + } + return std::result::Result::Ok(()); + } + + /// Returns a stable filter code for transaction observations. + pub fn filter_code(&self) -> std::string::String { + return match &self.source { + crate::BackfillSource::ExplicitSignatures(_) => "explicit_signatures".to_string(), + crate::BackfillSource::AddressHistory { kind, anchor_signature, direction, .. } => { + if *direction == crate::BackfillDirection::Before && anchor_signature.is_none() { + return format!("{}_latest", kind.code()); + } + format!("{}_{}", kind.code(), direction.code()) + }, + }; + } +} + +/// Backfill progress severity. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum BackfillProgressLevel { + /// Diagnostic detail. + Debug, + /// Normal campaign information. + Info, + /// Recoverable issue or retry. + Warning, + /// Non-recoverable item or campaign failure. + Error, +} + +impl BackfillProgressLevel { + /// Returns the stable lowercase level code. + pub fn code(&self) -> &'static str { + return match self { + crate::BackfillProgressLevel::Debug => "debug", + crate::BackfillProgressLevel::Info => "info", + crate::BackfillProgressLevel::Warning => "warn", + crate::BackfillProgressLevel::Error => "error", + }; + } +} + +/// One operator-visible progress event. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BackfillProgressEvent { + /// UTC timestamp rendered in RFC 3339. + pub timestamp: std::string::String, + /// Severity. + pub level: crate::BackfillProgressLevel, + /// Human-readable message. + pub message: std::string::String, + /// Number of candidates completed when known. + pub completed: std::option::Option, + /// Total candidates when known. + pub total: std::option::Option, +} + +impl BackfillProgressEvent { + fn new( + level: crate::BackfillProgressLevel, + message: impl std::convert::Into, + completed: std::option::Option, + total: std::option::Option, + ) -> Self { + return Self { + timestamp: chrono::Utc::now().to_rfc3339(), + level, + message: message.into(), + completed, + total, + }; + } +} + +/// Progress and cancellation contract implemented by applications. +pub trait BackfillObserver: Sync { + /// Receives one progress event. + fn on_progress(&self, event: &crate::BackfillProgressEvent); + /// Returns true when the operator requested cancellation. + fn is_cancelled(&self) -> bool; +} + +/// Final counters and pagination information for one campaign. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BackfillSummary { + /// Unique capture session identifier. + pub capture_session_id: std::string::String, + /// Stable filter code. + pub filter_code: std::string::String, + /// Endpoint role. + pub role: std::string::String, + /// Provider used for transaction hydration. + pub provider: std::string::String, + /// Endpoint code used for transaction hydration. + pub endpoint_code: std::string::String, + /// Number of history pages fetched. + pub pages_fetched: u64, + /// Number of unique candidates selected. + pub candidates_selected: u64, + /// Number of candidates admitted to the bounded execution queue. + pub candidates_started: u64, + /// Number of candidates that reached a terminal outcome. + pub candidates_completed: u64, + /// Number of admitted candidates interrupted before a terminal outcome. + pub candidates_cancelled: u64, + /// Number of selected candidates never admitted after cancellation. + pub candidates_not_started: u64, + /// Number of source transactions received and normalized. + pub transactions_received: u64, + /// Number of canonical transaction rows inserted. + pub canonical_inserted: u64, + /// Number of canonical inserts skipped by idempotence. + pub canonical_skipped: u64, + /// Number of signatures skipped because they already existed before hydration. + pub existing_skipped: u64, + /// Number of missing transaction results after retries. + pub missing: u64, + /// Number of failed candidates after retries or persistence errors. + pub failed: u64, + /// Number of observation rows inserted. + pub observations_inserted: u64, + /// Number of source attempts performed. + pub attempts: u64, + /// Whether the campaign stopped after a cancellation request. + pub cancelled: bool, + /// Optional cursor suitable for continuing an older-history scan. + pub resume_before_signature: std::option::Option, + /// Campaign start time. + pub started_at: std::string::String, + /// Campaign completion time. + pub finished_at: std::string::String, +} + +#[derive(Clone, Debug)] +struct BackfillCandidate { + signature: std::string::String, + slot: std::option::Option, + detected_at: chrono::DateTime, +} + +#[derive(Clone, Copy, Debug)] +struct HttpRoleLimits { + requests_per_second: u32, + max_concurrent_requests: u32, + pause_after_rate_limit_ms: u64, +} + +#[derive(Debug)] +struct RequestPacer { + interval: std::time::Duration, + next_start: tokio::sync::Mutex, +} + +impl RequestPacer { + fn new(requests_per_second: u32) -> Self { + let micros = 1_000_000_u64.div_ceil(u64::from(requests_per_second)); + return Self { + interval: std::time::Duration::from_micros(micros), + next_start: tokio::sync::Mutex::new(tokio::time::Instant::now()), + }; + } + + async fn wait(&self) { + let mut next_start = self.next_start.lock().await; + let now = tokio::time::Instant::now(); + if *next_start > now { + tokio::time::sleep(*next_start - now).await; + } + *next_start = tokio::time::Instant::now() + self.interval; + } +} + +async fn wait_until_cancelled(observer: &O) +where + O: crate::BackfillObserver + Sync, +{ + loop { + if observer.is_cancelled() { + return; + } + tokio::time::sleep(std::time::Duration::from_millis( + crate::backfill::CANCELLATION_POLL_INTERVAL_MS, + )) + .await; + } +} + +async fn await_or_cancelled(observer: &O, future: F) -> std::option::Option +where + O: crate::BackfillObserver + Sync, + F: std::future::Future, +{ + if observer.is_cancelled() { + return std::option::Option::None; + } + tokio::pin!(future); + let outcome = tokio::select! { + _ = crate::backfill::wait_until_cancelled(observer) => std::option::Option::None, + value = &mut future => std::option::Option::Some(value), + }; + return outcome; +} + +async fn wait_with_cancellation(observer: &O, duration: std::time::Duration) -> bool +where + O: crate::BackfillObserver + Sync, +{ + let outcome = crate::backfill::await_or_cancelled(observer, tokio::time::sleep(duration)).await; + return outcome.is_some(); +} + +#[derive(Clone, Debug)] +struct CandidateOutcome { + transactions_received: u64, + canonical_inserted: u64, + canonical_skipped: u64, + existing_skipped: u64, + missing: u64, + failed: u64, + observations_inserted: u64, + attempts: u64, + cancelled: bool, +} + +impl CandidateOutcome { + fn empty() -> Self { + return Self { + transactions_received: 0, + canonical_inserted: 0, + canonical_skipped: 0, + existing_skipped: 0, + missing: 0, + failed: 0, + observations_inserted: 0, + attempts: 0, + cancelled: false, + }; + } +} + +#[derive(Debug)] +struct CandidateTaskResult { + index: usize, + result: kb_core::Result, +} + +#[derive(Debug)] +struct CandidateCompletionFrontier { + completed: std::vec::Vec, + contiguous_completed: usize, +} + +impl CandidateCompletionFrontier { + fn new(candidate_count: usize) -> Self { + return Self { + completed: vec![false; candidate_count], + contiguous_completed: 0, + }; + } + + fn mark_completed(&mut self, index: usize) { + if let std::option::Option::Some(completed) = self.completed.get_mut(index) { + *completed = true; + } + while self.completed.get(self.contiguous_completed).copied().unwrap_or(false) { + self.contiguous_completed += 1; + } + } + + fn contiguous_completed(&self) -> usize { + return self.contiguous_completed; + } +} + +/// Executes one bounded HTTP backfill and persists canonical transactions plus observations. +pub async fn execute_http_backfill( + http_pool: &kb_rpc::HttpEndpointPool, + store: &S, + request: &crate::BackfillRequest, + observer: &O, +) -> kb_core::Result +where + S: kb_store_core::RawTransactionStore + Sync, + O: crate::BackfillObserver + Sync, +{ + let validation_result = request.validate(); + if let std::result::Result::Err(error) = validation_result { + tracing::error!(target: crate::TRACING_TARGET, action = "validate_http_backfill", role = %request.role, error = %error, "HTTP backfill request validation failed"); + return std::result::Result::Err(error); + } + let started_at = chrono::Utc::now(); + let capture_session_id = uuid::Uuid::new_v4().to_string(); + let filter_code = request.filter_code(); + tracing::info!(target: crate::TRACING_TARGET, action = "execute_http_backfill", capture_session_id = %capture_session_id, filter_code = %filter_code, role = %request.role, commitment = %request.commitment, page_size = request.page_size, max_pages = request.max_pages, requested_concurrency = request.max_concurrent_requests, max_retries = request.max_retries, "HTTP backfill campaign started"); + crate::backfill::emit( + observer, + crate::BackfillProgressLevel::Info, + format!("backfill session {capture_session_id} started with filter {filter_code}"), + std::option::Option::None, + std::option::Option::None, + ); + let transaction_client_result = + http_pool.select_client_for_role_and_method(request.role.as_str(), "getTransaction"); + let transaction_client = match transaction_client_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "select_backfill_transaction_client", capture_session_id = %capture_session_id, filter_code = %filter_code, role = %request.role, error = %error, "backfill transaction client selection failed"); + return std::result::Result::Err(error); + }, + }; + let transaction_limits_result = + crate::backfill::role_limits(&transaction_client, request.role.as_str(), "get_transaction"); + let transaction_limits = match transaction_limits_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "resolve_backfill_role_limits", capture_session_id = %capture_session_id, filter_code = %filter_code, role = %request.role, endpoint_name = %transaction_client.endpoint_name(), error = %error, "backfill role limit resolution failed"); + return std::result::Result::Err(error); + }, + }; + let discovery_result = crate::backfill::collect_candidates(http_pool, request, observer).await; + let discovery = match discovery_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "collect_backfill_candidates", capture_session_id = %capture_session_id, filter_code = %filter_code, role = %request.role, error = %error, "backfill candidate discovery failed"); + return std::result::Result::Err(error); + }, + }; + let total = match u64::try_from(discovery.candidates.len()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => u64::MAX, + }; + let ordered_signatures = discovery + .candidates + .iter() + .map(|candidate| return candidate.signature.clone()) + .collect::>(); + let discovery_resume_before_signature = discovery.resume_before_signature.clone(); + let mut summary = crate::BackfillSummary { + capture_session_id: capture_session_id.clone(), + filter_code: filter_code.clone(), + role: request.role.clone(), + provider: transaction_client.provider().to_string(), + endpoint_code: transaction_client.endpoint_name().to_string(), + pages_fetched: discovery.pages_fetched, + candidates_selected: total, + candidates_started: 0, + candidates_completed: 0, + candidates_cancelled: 0, + candidates_not_started: 0, + transactions_received: 0, + canonical_inserted: 0, + canonical_skipped: 0, + existing_skipped: 0, + missing: 0, + failed: 0, + observations_inserted: 0, + attempts: 0, + cancelled: false, + resume_before_signature: discovery_resume_before_signature.clone(), + started_at: started_at.to_rfc3339(), + finished_at: started_at.to_rfc3339(), + }; + if observer.is_cancelled() { + tracing::warn!(target: crate::TRACING_TARGET, action = "execute_http_backfill", capture_session_id = %capture_session_id, filter_code = %filter_code, candidates_selected = total, decision = "cancelled_before_admission", "HTTP backfill cancelled before candidate admission"); + summary.cancelled = true; + summary.candidates_not_started = total; + summary.resume_before_signature = crate::backfill::resolved_resume_before_signature( + request, + ordered_signatures.as_slice(), + 0, + discovery_resume_before_signature, + true, + ); + summary.finished_at = chrono::Utc::now().to_rfc3339(); + return std::result::Result::Ok(summary); + } + let effective_concurrency = std::cmp::max( + 1_usize, + std::cmp::min( + request.max_concurrent_requests as usize, + transaction_limits.max_concurrent_requests as usize, + ), + ); + let pacer = std::sync::Arc::new(crate::backfill::RequestPacer::new( + transaction_limits.requests_per_second, + )); + let transaction_config_result = + kb_rpc::GetTransactionConfig::new(request.commitment.clone(), 0); + let transaction_config = match transaction_config_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut pending = discovery.candidates.into_iter().enumerate(); + let mut in_flight = futures_util::stream::FuturesUnordered::new(); + let mut completion_frontier = + crate::backfill::CandidateCompletionFrontier::new(ordered_signatures.len()); + let create_task = |index: usize, candidate: BackfillCandidate| { + let pacer_value = std::sync::Arc::clone(&pacer); + let capture_session_id_value = capture_session_id.clone(); + let filter_code_value = filter_code.clone(); + let transaction_client_value = transaction_client.clone(); + let transaction_config_value = transaction_config.clone(); + return async move { + let result = crate::backfill::hydrate_candidate( + store, + observer, + &transaction_client_value, + &transaction_config_value, + request, + &capture_session_id_value, + &filter_code_value, + candidate, + pacer_value.as_ref(), + transaction_limits.pause_after_rate_limit_ms, + ) + .await; + return crate::backfill::CandidateTaskResult { index, result }; + } + .boxed(); + }; + while in_flight.len() < effective_concurrency && !observer.is_cancelled() { + let next_candidate = pending.next(); + let (index, candidate) = match next_candidate { + std::option::Option::Some(value) => value, + std::option::Option::None => break, + }; + summary.candidates_started += 1; + in_flight.push(create_task(index, candidate)); + } + while let std::option::Option::Some(task) = in_flight.next().await { + let index = task.index; + match task.result { + std::result::Result::Ok(outcome) => { + if outcome.cancelled { + summary.candidates_cancelled += 1; + summary.cancelled = true; + crate::backfill::emit( + observer, + crate::BackfillProgressLevel::Debug, + format!("candidate {} cancelled before terminal processing", index + 1), + std::option::Option::Some(summary.candidates_completed), + std::option::Option::Some(total), + ); + } else { + summary.candidates_completed += 1; + summary.transactions_received += outcome.transactions_received; + summary.canonical_inserted += outcome.canonical_inserted; + summary.canonical_skipped += outcome.canonical_skipped; + summary.existing_skipped += outcome.existing_skipped; + summary.missing += outcome.missing; + summary.failed += outcome.failed; + summary.observations_inserted += outcome.observations_inserted; + summary.attempts += outcome.attempts; + completion_frontier.mark_completed(index); + crate::backfill::emit( + observer, + crate::BackfillProgressLevel::Info, + format!("processed {}/{} candidates", summary.candidates_completed, total), + std::option::Option::Some(summary.candidates_completed), + std::option::Option::Some(total), + ); + } + }, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "hydrate_backfill_candidate", capture_session_id = %capture_session_id, filter_code = %filter_code, candidate_index = index, error = %error, "backfill candidate task failed without a normal outcome"); + summary.candidates_completed += 1; + summary.failed += 1; + completion_frontier.mark_completed(index); + crate::backfill::emit( + observer, + crate::BackfillProgressLevel::Error, + format!("candidate {} failed without a normal outcome: {error}", index + 1), + std::option::Option::Some(summary.candidates_completed), + std::option::Option::Some(total), + ); + }, + } + if observer.is_cancelled() { + summary.cancelled = true; + } else { + let next_candidate = pending.next(); + if let std::option::Option::Some((next_index, next_candidate_value)) = next_candidate { + summary.candidates_started += 1; + in_flight.push(create_task(next_index, next_candidate_value)); + } + } + } + if observer.is_cancelled() { + summary.cancelled = true; + } + summary.candidates_not_started = total.saturating_sub(summary.candidates_started); + if summary.candidates_cancelled > 0 || summary.candidates_not_started > 0 { + summary.cancelled = true; + } + summary.resume_before_signature = crate::backfill::resolved_resume_before_signature( + request, + ordered_signatures.as_slice(), + completion_frontier.contiguous_completed(), + discovery_resume_before_signature, + summary.cancelled, + ); + summary.finished_at = chrono::Utc::now().to_rfc3339(); + let completion_level = if summary.cancelled { + crate::BackfillProgressLevel::Warning + } else { + crate::BackfillProgressLevel::Info + }; + if summary.failed > 0 { + tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_backfill", capture_session_id = %summary.capture_session_id, filter_code = %summary.filter_code, endpoint_name = %summary.endpoint_code, candidates_selected = summary.candidates_selected, candidates_completed = summary.candidates_completed, failed = summary.failed, missing = summary.missing, cancelled = summary.cancelled, "HTTP backfill completed with failed candidates"); + } else if summary.cancelled { + tracing::warn!(target: crate::TRACING_TARGET, action = "execute_http_backfill", capture_session_id = %summary.capture_session_id, filter_code = %summary.filter_code, endpoint_name = %summary.endpoint_code, candidates_selected = summary.candidates_selected, candidates_completed = summary.candidates_completed, candidates_cancelled = summary.candidates_cancelled, candidates_not_started = summary.candidates_not_started, "HTTP backfill campaign cancelled"); + } else { + tracing::info!(target: crate::TRACING_TARGET, action = "execute_http_backfill", capture_session_id = %summary.capture_session_id, filter_code = %summary.filter_code, endpoint_name = %summary.endpoint_code, candidates_selected = summary.candidates_selected, candidates_completed = summary.candidates_completed, canonical_inserted = summary.canonical_inserted, canonical_skipped = summary.canonical_skipped, existing_skipped = summary.existing_skipped, missing = summary.missing, observations_inserted = summary.observations_inserted, "HTTP backfill campaign completed"); + } + crate::backfill::emit( + observer, + completion_level, + format!( + "backfill completed: completed={}, candidate_cancelled={}, not_started={}, inserted={}, skipped={}, existing={}, missing={}, failed={}, observations={}", + summary.candidates_completed, + summary.candidates_cancelled, + summary.candidates_not_started, + summary.canonical_inserted, + summary.canonical_skipped, + summary.existing_skipped, + summary.missing, + summary.failed, + summary.observations_inserted + ), + std::option::Option::Some(summary.candidates_completed), + std::option::Option::Some(total), + ); + return std::result::Result::Ok(summary); +} + +fn resolved_resume_before_signature( + request: &crate::BackfillRequest, + ordered_signatures: &[std::string::String], + contiguous_completed: usize, + discovery_resume_before_signature: std::option::Option, + cancelled: bool, +) -> std::option::Option { + if !cancelled { + return discovery_resume_before_signature; + } + return match &request.source { + crate::BackfillSource::AddressHistory { + anchor_signature, + direction: crate::BackfillDirection::Before, + .. + } => { + if contiguous_completed == 0 { + return anchor_signature.clone(); + } + if let std::option::Option::Some(signature) = + ordered_signatures.get(contiguous_completed - 1) + { + return std::option::Option::Some(signature.clone()); + } + anchor_signature.clone() + }, + crate::BackfillSource::ExplicitSignatures(_) + | crate::BackfillSource::AddressHistory { + direction: crate::BackfillDirection::After, + .. + } => std::option::Option::None, + }; +} + +struct CandidateDiscovery { + candidates: std::vec::Vec, + pages_fetched: u64, + resume_before_signature: std::option::Option, +} + +async fn collect_candidates( + http_pool: &kb_rpc::HttpEndpointPool, + request: &crate::BackfillRequest, + observer: &O, +) -> kb_core::Result +where + O: crate::BackfillObserver + Sync, +{ + return match &request.source { + crate::BackfillSource::ExplicitSignatures(signatures) => { + std::result::Result::Ok(crate::backfill::explicit_candidates(signatures)) + }, + crate::BackfillSource::AddressHistory { + address, + anchor_signature, + direction, + limit, + .. + } => { + let client_result = http_pool.select_client_for_role_and_method( + request.role.as_str(), + "getSignaturesForAddress", + ); + let client = match client_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let limits_result = crate::backfill::role_limits( + &client, + request.role.as_str(), + "get_signatures_for_address", + ); + let limits = match limits_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let pacer = crate::backfill::RequestPacer::new(limits.requests_per_second); + match direction { + crate::BackfillDirection::Before => { + crate::backfill::collect_before_candidates( + &client, + request, + observer, + address, + anchor_signature.as_deref(), + *limit, + &pacer, + limits.pause_after_rate_limit_ms, + ) + .await + }, + crate::BackfillDirection::After => { + crate::backfill::collect_after_candidates( + &client, + request, + observer, + address, + match anchor_signature.as_deref() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::config( + "newer address history backfill requires an anchor signature", + )); + }, + }, + *limit, + &pacer, + limits.pause_after_rate_limit_ms, + ) + .await + }, + } + }, + }; +} + +fn explicit_candidates(signatures: &[std::string::String]) -> CandidateDiscovery { + let mut seen = std::collections::HashSet::::new(); + let mut candidates = std::vec::Vec::::new(); + let detected_at = chrono::Utc::now(); + for signature in signatures { + let trimmed = signature.trim(); + if trimmed.is_empty() || !seen.insert(trimmed.to_string()) { + continue; + } + candidates.push(BackfillCandidate { + signature: trimmed.to_string(), + slot: std::option::Option::None, + detected_at, + }); + } + return CandidateDiscovery { + candidates, + pages_fetched: 0, + resume_before_signature: std::option::Option::None, + }; +} + +async fn collect_before_candidates( + client: &kb_rpc::HttpClient, + request: &crate::BackfillRequest, + observer: &O, + address: &str, + anchor_signature: std::option::Option<&str>, + limit: usize, + pacer: &RequestPacer, + pause_after_rate_limit_ms: u64, +) -> kb_core::Result +where + O: crate::BackfillObserver + Sync, +{ + let mut candidates = std::vec::Vec::::new(); + let mut seen = std::collections::HashSet::::new(); + let mut before = anchor_signature.map(str::to_string); + let mut pages_fetched = 0_u64; + while candidates.len() < limit && pages_fetched < u64::from(request.max_pages) { + if observer.is_cancelled() { + break; + } + let remaining = limit - candidates.len(); + let page_limit = std::cmp::min(usize::from(request.page_size), remaining); + let page_limit_u16 = match u16::try_from(page_limit) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::config(format!( + "backfill page limit conversion failed: {error}" + ))); + }, + }; + let config_result = kb_rpc::GetSignaturesForAddressConfig::new( + request.commitment.clone(), + page_limit_u16, + before.clone(), + std::option::Option::None, + std::option::Option::None, + ); + let config = match config_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let page_result = crate::backfill::fetch_signature_page( + client, + address, + &config, + request.max_retries, + pacer, + pause_after_rate_limit_ms, + observer, + ) + .await; + let page = match page_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if observer.is_cancelled() { + break; + } + pages_fetched += 1; + crate::backfill::append_signature_candidates(&mut candidates, &mut seen, &page); + before = page.last().map(|item| return item.signature.clone()); + crate::backfill::emit( + observer, + crate::BackfillProgressLevel::Debug, + format!("history page {pages_fetched} returned {} signatures", page.len()), + std::option::Option::None, + std::option::Option::None, + ); + if page.len() < page_limit || page.is_empty() { + break; + } + } + let resume_before_signature = before; + return std::result::Result::Ok(CandidateDiscovery { + candidates, + pages_fetched, + resume_before_signature, + }); +} + +async fn collect_after_candidates( + client: &kb_rpc::HttpClient, + request: &crate::BackfillRequest, + observer: &O, + address: &str, + anchor_signature: &str, + limit: usize, + pacer: &RequestPacer, + pause_after_rate_limit_ms: u64, +) -> kb_core::Result +where + O: crate::BackfillObserver + Sync, +{ + let page_size = crate::backfill::AFTER_HISTORY_PAGE_SIZE; + let mut nearest_newer = std::collections::VecDeque::::with_capacity(limit); + let mut seen = std::collections::HashSet::::new(); + let mut before = std::option::Option::None; + let until = std::option::Option::Some(anchor_signature.to_string()); + let mut pages_fetched = 0_u64; + let mut scanned = 0_u64; + let mut boundary_reached = false; + while pages_fetched < u64::from(request.max_pages) { + if observer.is_cancelled() { + break; + } + let config_result = kb_rpc::GetSignaturesForAddressConfig::new( + request.commitment.clone(), + page_size, + before.clone(), + until.clone(), + std::option::Option::None, + ); + let config = match config_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let page_result = crate::backfill::fetch_signature_page( + client, + address, + &config, + request.max_retries, + pacer, + pause_after_rate_limit_ms, + observer, + ) + .await; + let page = match page_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if observer.is_cancelled() { + break; + } + pages_fetched += 1; + let page_count = match u64::try_from(page.len()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::config(format!( + "after-history page length conversion failed: {error}" + ))); + }, + }; + scanned = scanned.saturating_add(page_count); + crate::backfill::append_nearest_newer_candidates( + &mut nearest_newer, + &mut seen, + &page, + limit, + ); + before = page.last().map(|item| return item.signature.clone()); + let oldest_scanned_slot = page.last().map(|item| return item.slot); + crate::backfill::emit( + observer, + crate::BackfillProgressLevel::Debug, + format!( + "newer-history page={pages_fetched} page_size={} scanned={scanned} oldest_scanned_slot={oldest_scanned_slot:?}", + page.len() + ), + std::option::Option::None, + std::option::Option::None, + ); + if page.len() < usize::from(page_size) { + boundary_reached = true; + break; + } + } + if !boundary_reached && !observer.is_cancelled() { + return std::result::Result::Err(kb_core::Error::config(format!( + "anchor boundary was not reached within {} pages after scanning {scanned} signatures; increase max_pages", + request.max_pages + ))); + } + return std::result::Result::Ok(CandidateDiscovery { + candidates: nearest_newer.into_iter().collect(), + pages_fetched, + resume_before_signature: std::option::Option::None, + }); +} + +fn append_nearest_newer_candidates( + candidates: &mut std::collections::VecDeque, + seen: &mut std::collections::HashSet, + page: &[kb_rpc::AddressSignatureInfo], + limit: usize, +) { + let detected_at = chrono::Utc::now(); + for item in page { + if !seen.insert(item.signature.clone()) { + continue; + } + candidates.push_back(BackfillCandidate { + signature: item.signature.clone(), + slot: std::option::Option::Some(item.slot), + detected_at, + }); + if candidates.len() > limit { + candidates.pop_front(); + } + } +} + +fn append_signature_candidates( + candidates: &mut std::vec::Vec, + seen: &mut std::collections::HashSet, + page: &[kb_rpc::AddressSignatureInfo], +) { + let detected_at = chrono::Utc::now(); + for item in page { + if !seen.insert(item.signature.clone()) { + continue; + } + candidates.push(BackfillCandidate { + signature: item.signature.clone(), + slot: std::option::Option::Some(item.slot), + detected_at, + }); + } +} + +async fn fetch_signature_page( + client: &kb_rpc::HttpClient, + address: &str, + config: &kb_rpc::GetSignaturesForAddressConfig, + max_retries: u32, + pacer: &RequestPacer, + pause_after_rate_limit_ms: u64, + observer: &O, +) -> kb_core::Result> +where + O: crate::BackfillObserver + Sync, +{ + let mut attempt = 0_u32; + loop { + if observer.is_cancelled() { + return std::result::Result::Ok(std::vec::Vec::new()); + } + let pacing_result = crate::backfill::await_or_cancelled(observer, pacer.wait()).await; + if pacing_result.is_none() { + return std::result::Result::Ok(std::vec::Vec::new()); + } + let request_result = crate::backfill::await_or_cancelled( + observer, + client.get_signatures_for_address(address, config), + ) + .await; + let result = match request_result { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::result::Result::Ok(std::vec::Vec::new()), + }; + match result { + std::result::Result::Ok(page) => return std::result::Result::Ok(page), + std::result::Result::Err(error) => { + if attempt >= max_retries { + return std::result::Result::Err(error); + } + let error_text = error.to_string(); + let delay_ms = crate::backfill::retry_delay_ms( + error_text.as_str(), + attempt, + pause_after_rate_limit_ms, + ); + crate::backfill::emit( + observer, + crate::BackfillProgressLevel::Warning, + format!( + "getSignaturesForAddress retry {} after {} ms: {}", + attempt + 1, + delay_ms, + error_text + ), + std::option::Option::None, + std::option::Option::None, + ); + let wait_completed = crate::backfill::wait_with_cancellation( + observer, + std::time::Duration::from_millis(delay_ms), + ) + .await; + if !wait_completed { + return std::result::Result::Ok(std::vec::Vec::new()); + } + attempt += 1; + }, + } + } +} + +async fn hydrate_candidate( + store: &S, + observer: &O, + client: &kb_rpc::HttpClient, + config: &kb_rpc::GetTransactionConfig, + request: &crate::BackfillRequest, + capture_session_id: &str, + filter_code: &str, + candidate: BackfillCandidate, + pacer: &RequestPacer, + pause_after_rate_limit_ms: u64, +) -> kb_core::Result +where + S: kb_store_core::RawTransactionStore + Sync, + O: crate::BackfillObserver + Sync, +{ + let mut outcome = CandidateOutcome::empty(); + if observer.is_cancelled() { + outcome.cancelled = true; + return std::result::Result::Ok(outcome); + } + let signature_model = kb_model::Signature(candidate.signature.clone()); + let exists_result = + kb_store_core::RawTransactionStore::has_raw_transaction_signature(store, &signature_model) + .await; + let exists = match exists_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if exists { + outcome.existing_skipped = 1; + return std::result::Result::Ok(outcome); + } + let mut attempt = 0_u32; + loop { + if observer.is_cancelled() { + outcome.cancelled = true; + return std::result::Result::Ok(outcome); + } + let pacing_result = crate::backfill::await_or_cancelled(observer, pacer.wait()).await; + if pacing_result.is_none() { + outcome.cancelled = true; + return std::result::Result::Ok(outcome); + } + outcome.attempts += 1; + let request_result = crate::backfill::await_or_cancelled( + observer, + client.get_transaction_acquisition(candidate.signature.as_str(), config), + ) + .await; + let acquisition_result = match request_result { + std::option::Option::Some(value) => value, + std::option::Option::None => { + outcome.cancelled = true; + return std::result::Result::Ok(outcome); + }, + }; + let received_at = chrono::Utc::now(); + match acquisition_result { + std::result::Result::Ok(acquisition) => { + let metadata_result = + crate::backfill::source_payload_metadata(&acquisition.source_json); + let (payload_size_bytes, source_payload_hash) = match metadata_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + }; + match acquisition.canonical_transaction { + std::option::Option::Some(transaction) => { + outcome.transactions_received = 1; + let normalized_at = chrono::Utc::now(); + let raw_insert_result = + kb_store_core::RawTransactionInsert::from_canonical(&transaction); + let raw_insert = match raw_insert_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + let observation_result = + crate::backfill::persist_failed_observation( + store, + client, + request, + capture_session_id, + filter_code, + &candidate, + attempt, + received_at, + std::option::Option::Some(normalized_at), + payload_size_bytes, + source_payload_hash, + "canonical_normalization_failed", + error.to_string(), + ) + .await; + if let std::result::Result::Ok(count) = observation_result { + outcome.observations_inserted += count; + } + tracing::error!(target: crate::TRACING_TARGET, action = "normalize_backfill_transaction", capture_session_id, filter_code, signature = %candidate.signature, attempt, error = %error, "canonical transaction normalization failed"); + outcome.failed = 1; + return std::result::Result::Ok(outcome); + }, + }; + let persist_result = + kb_store_core::RawTransactionStore::insert_raw_transaction( + store, + &raw_insert, + ) + .await; + let persist_outcome = match persist_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + let observation_result = + crate::backfill::persist_failed_observation( + store, + client, + request, + capture_session_id, + filter_code, + &candidate, + attempt, + received_at, + std::option::Option::Some(normalized_at), + payload_size_bytes, + source_payload_hash, + "canonical_persist_failed", + error.to_string(), + ) + .await; + if let std::result::Result::Ok(count) = observation_result { + outcome.observations_inserted += count; + } + tracing::error!(target: crate::TRACING_TARGET, action = "persist_backfill_transaction", capture_session_id, filter_code, signature = %candidate.signature, attempt, error = %error, "canonical transaction persistence failed"); + outcome.failed = 1; + return std::result::Result::Ok(outcome); + }, + }; + outcome.canonical_inserted += persist_outcome.inserted_count; + outcome.canonical_skipped += persist_outcome.skipped_count; + let observation_result = crate::backfill::persist_success_observation( + store, + client, + request, + capture_session_id, + filter_code, + &candidate, + transaction.slot, + attempt, + received_at, + normalized_at, + payload_size_bytes, + source_payload_hash, + ) + .await; + let observation_count = match observation_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "persist_backfill_observation", capture_session_id, filter_code, signature = %candidate.signature, attempt, error = %error, "successful backfill observation persistence failed"); + outcome.failed = 1; + crate::backfill::emit( + observer, + crate::BackfillProgressLevel::Error, + format!( + "observation persistence failed for {}: {}", + candidate.signature, error + ), + std::option::Option::None, + std::option::Option::None, + ); + return std::result::Result::Ok(outcome); + }, + }; + outcome.observations_inserted += observation_count; + return std::result::Result::Ok(outcome); + }, + std::option::Option::None => { + let observation_result = crate::backfill::persist_missing_observation( + store, + client, + request, + capture_session_id, + filter_code, + &candidate, + attempt, + received_at, + payload_size_bytes, + source_payload_hash, + ) + .await; + let observation_count = match observation_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + }; + outcome.observations_inserted += observation_count; + if attempt >= request.max_retries { + outcome.missing = 1; + return std::result::Result::Ok(outcome); + } + }, + } + }, + std::result::Result::Err(error) => { + let error_text = error.to_string(); + let observation_result = crate::backfill::persist_failed_observation( + store, + client, + request, + capture_session_id, + filter_code, + &candidate, + attempt, + received_at, + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + crate::backfill::error_code(error_text.as_str()), + error_text.clone(), + ) + .await; + let observation_count = match observation_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(observation_error) => { + return std::result::Result::Err(observation_error); + }, + }; + outcome.observations_inserted += observation_count; + if attempt >= request.max_retries { + tracing::error!(target: crate::TRACING_TARGET, action = "hydrate_backfill_candidate", capture_session_id, filter_code, endpoint_name = %client.endpoint_name(), provider = %client.provider(), signature = %candidate.signature, attempt, max_retries = request.max_retries, error_code = crate::backfill::error_code(error_text.as_str()), error_message = %error_text, "getTransaction failed after all retries"); + outcome.failed = 1; + return std::result::Result::Ok(outcome); + } + let delay_ms = crate::backfill::retry_delay_ms( + error_text.as_str(), + attempt, + pause_after_rate_limit_ms, + ); + crate::backfill::emit( + observer, + crate::BackfillProgressLevel::Warning, + format!( + "getTransaction retry {} for {} after {} ms: {}", + attempt + 1, + candidate.signature, + delay_ms, + error_text + ), + std::option::Option::None, + std::option::Option::None, + ); + let wait_completed = crate::backfill::wait_with_cancellation( + observer, + std::time::Duration::from_millis(delay_ms), + ) + .await; + if !wait_completed { + outcome.cancelled = true; + return std::result::Result::Ok(outcome); + } + }, + } + attempt += 1; + } +} + +async fn persist_success_observation( + store: &S, + client: &kb_rpc::HttpClient, + request: &crate::BackfillRequest, + capture_session_id: &str, + filter_code: &str, + candidate: &BackfillCandidate, + slot: u64, + attempt: u32, + received_at: chrono::DateTime, + normalized_at: chrono::DateTime, + payload_size_bytes: std::option::Option, + source_payload_hash: std::option::Option, +) -> kb_core::Result +where + S: kb_store_core::RawTransactionStore + Sync, +{ + let observation_result = crate::backfill::base_observation( + client, + request, + capture_session_id, + filter_code, + candidate, + attempt, + received_at, + ); + let observation_base = match observation_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let identity_result = observation_base + .with_transaction_identity(candidate.signature.clone(), std::option::Option::Some(slot)); + let observation_with_identity = match identity_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let observation_with_timings = observation_with_identity.with_timings( + std::option::Option::Some(candidate.detected_at), + std::option::Option::Some(normalized_at), + ); + let payload_result = + observation_with_timings.with_payload_metadata(payload_size_bytes, source_payload_hash); + let observation = match payload_result { + std::result::Result::Ok(value) => { + value.with_status(kb_store_core::TransactionObservationStatus::Persisted) + }, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::backfill::insert_observation(store, &observation).await; +} + +async fn persist_missing_observation( + store: &S, + client: &kb_rpc::HttpClient, + request: &crate::BackfillRequest, + capture_session_id: &str, + filter_code: &str, + candidate: &BackfillCandidate, + attempt: u32, + received_at: chrono::DateTime, + payload_size_bytes: std::option::Option, + source_payload_hash: std::option::Option, +) -> kb_core::Result +where + S: kb_store_core::RawTransactionStore + Sync, +{ + let observation_result = crate::backfill::base_observation( + client, + request, + capture_session_id, + filter_code, + candidate, + attempt, + received_at, + ); + let observation_base = match observation_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let identity_result = + observation_base.with_transaction_identity(candidate.signature.clone(), candidate.slot); + let observation_with_identity = match identity_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let observation_with_timings = observation_with_identity + .with_timings(std::option::Option::Some(candidate.detected_at), std::option::Option::None); + let payload_result = + observation_with_timings.with_payload_metadata(payload_size_bytes, source_payload_hash); + let observation = match payload_result { + std::result::Result::Ok(value) => { + value.with_status(kb_store_core::TransactionObservationStatus::Missing) + }, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::backfill::insert_observation(store, &observation).await; +} + +async fn persist_failed_observation( + store: &S, + client: &kb_rpc::HttpClient, + request: &crate::BackfillRequest, + capture_session_id: &str, + filter_code: &str, + candidate: &BackfillCandidate, + attempt: u32, + received_at: chrono::DateTime, + normalized_at: std::option::Option>, + payload_size_bytes: std::option::Option, + source_payload_hash: std::option::Option, + error_code: &str, + error_message: std::string::String, +) -> kb_core::Result +where + S: kb_store_core::RawTransactionStore + Sync, +{ + let observation_result = crate::backfill::base_observation( + client, + request, + capture_session_id, + filter_code, + candidate, + attempt, + received_at, + ); + let observation_base = match observation_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let identity_result = + observation_base.with_transaction_identity(candidate.signature.clone(), candidate.slot); + let observation_with_identity = match identity_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let observation_with_timings = observation_with_identity + .with_timings(std::option::Option::Some(candidate.detected_at), normalized_at); + let payload_result = + observation_with_timings.with_payload_metadata(payload_size_bytes, source_payload_hash); + let observation_with_payload = match payload_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let error_result = observation_with_payload + .with_error(error_code.to_string(), std::option::Option::Some(error_message)); + let observation = match error_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::backfill::insert_observation(store, &observation).await; +} + +fn base_observation( + client: &kb_rpc::HttpClient, + request: &crate::BackfillRequest, + capture_session_id: &str, + filter_code: &str, + candidate: &BackfillCandidate, + attempt: u32, + received_at: chrono::DateTime, +) -> kb_core::Result { + let observation_key = format!( + "backfill:{}:{}:{}:{}", + capture_session_id, + client.endpoint_name(), + candidate.signature, + attempt + ); + let observation_result = kb_store_core::TransactionObservationInsert::new( + observation_key, + client.provider().to_string(), + "solana_http_json_rpc".to_string(), + "getTransaction".to_string(), + kb_store_core::TransactionObservationOrigin::Backfill, + received_at, + ); + let observation = match observation_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let endpoint_result = observation.with_endpoint_code(client.endpoint_name().to_string()); + let endpoint_observation = match endpoint_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let commitment_result = endpoint_observation.with_commitment(request.commitment.clone()); + let commitment_observation = match commitment_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return commitment_observation.with_capture_context( + std::option::Option::Some(capture_session_id.to_string()), + std::option::Option::Some(filter_code.to_string()), + ); +} + +async fn insert_observation( + store: &S, + observation: &kb_store_core::TransactionObservationInsert, +) -> kb_core::Result +where + S: kb_store_core::RawTransactionStore + Sync, +{ + let insert_result = + kb_store_core::RawTransactionStore::insert_transaction_observation(store, observation) + .await; + let outcome = match insert_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(outcome.inserted_count); +} + +fn source_payload_metadata( + source: &serde_json::Value, +) -> kb_core::Result<(std::option::Option, std::option::Option)> { + let bytes_result = serde_json::to_vec(source); + let bytes = match bytes_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot serialize source getTransaction payload for metrics: {error}" + ))); + }, + }; + let size = match u64::try_from(bytes.len()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "source payload size conversion failed: {error}" + ))); + }, + }; + let digest = sha2::Sha256::digest(bytes.as_slice()); + return std::result::Result::Ok(( + std::option::Option::Some(size), + std::option::Option::Some(lower_hex(digest.as_ref())), + )); +} + +fn lower_hex(bytes: &[u8]) -> std::string::String { + const HEX_DIGITS: &[u8; 16] = b"0123456789abcdef"; + let mut output = std::string::String::with_capacity(bytes.len().saturating_mul(2)); + for byte in bytes { + output.push(char::from(HEX_DIGITS[usize::from(byte >> 4)])); + output.push(char::from(HEX_DIGITS[usize::from(byte & 0x0f)])); + } + return output; +} + +fn role_limits( + client: &kb_rpc::HttpClient, + required_role: &str, + request_kind: &str, +) -> kb_core::Result { + for role in &client.endpoint_config().roles { + if !role.enabled || role.role != required_role { + continue; + } + let supports = + role.request_kinds.iter().any(|kind| return kind == request_kind || kind == "*"); + if !supports { + continue; + } + return std::result::Result::Ok(HttpRoleLimits { + requests_per_second: role.requests_per_second, + max_concurrent_requests: role.max_concurrent_requests, + pause_after_rate_limit_ms: role.pause_after_rate_limit_ms, + }); + } + return std::result::Result::Err(kb_core::Error::config(format!( + "endpoint '{}' does not expose role '{}' for '{}'", + client.endpoint_name(), + required_role, + request_kind + ))); +} + +fn retry_delay_ms(error_text: &str, attempt: u32, pause_after_rate_limit_ms: u64) -> u64 { + if error_text.contains("429") || error_text.to_ascii_lowercase().contains("rate limit") { + return pause_after_rate_limit_ms; + } + let exponent = std::cmp::min(attempt, 4); + return 250_u64.saturating_mul(1_u64 << exponent); +} + +fn error_code(error_text: &str) -> &'static str { + if error_text.contains("429") || error_text.to_ascii_lowercase().contains("rate limit") { + return "http_rate_limited"; + } + if error_text.to_ascii_lowercase().contains("timeout") { + return "http_timeout"; + } + return "http_rpc_failed"; +} + +fn emit( + observer: &O, + level: crate::BackfillProgressLevel, + message: impl std::convert::Into, + completed: std::option::Option, + total: std::option::Option, +) where + O: crate::BackfillObserver + Sync, +{ + let event = crate::BackfillProgressEvent::new(level, message, completed, total); + observer.on_progress(&event); +} + +#[cfg(test)] +mod tests { + fn signature(byte: u8) -> std::string::String { + return bs58::encode([byte; 64]).into_string(); + } + + fn pubkey(byte: u8) -> std::string::String { + return bs58::encode([byte; 32]).into_string(); + } + + #[test] + fn explicit_candidates_are_deduplicated_in_input_order() { + let first = signature(1); + let second = signature(2); + let discovery = + crate::backfill::explicit_candidates(&[first.clone(), second.clone(), first.clone()]); + assert_eq!(discovery.candidates.len(), 2); + assert_eq!(discovery.candidates[0].signature, first); + assert_eq!(discovery.candidates[1].signature, second); + } + + #[test] + fn address_request_requires_non_zero_limit() { + let request = crate::BackfillRequest { + role: "history_backfill".to_string(), + commitment: "confirmed".to_string(), + source: crate::BackfillSource::AddressHistory { + kind: crate::BackfillAddressKind::Program, + address: pubkey(3), + anchor_signature: std::option::Option::Some(signature(4)), + direction: crate::BackfillDirection::Before, + limit: 0, + }, + page_size: 100, + max_pages: 10, + max_concurrent_requests: 2, + max_retries: 2, + }; + assert!(request.validate().is_err()); + } + + #[test] + fn before_address_request_accepts_missing_anchor() { + let request = crate::BackfillRequest { + role: "history_backfill".to_string(), + commitment: "confirmed".to_string(), + source: crate::BackfillSource::AddressHistory { + kind: crate::BackfillAddressKind::Program, + address: pubkey(40), + anchor_signature: std::option::Option::None, + direction: crate::BackfillDirection::Before, + limit: 10, + }, + page_size: 100, + max_pages: 10, + max_concurrent_requests: 2, + max_retries: 2, + }; + assert!(request.validate().is_ok()); + assert_eq!(request.filter_code(), "program_latest"); + } + + #[test] + fn after_address_request_rejects_missing_anchor() { + let request = crate::BackfillRequest { + role: "history_backfill".to_string(), + commitment: "confirmed".to_string(), + source: crate::BackfillSource::AddressHistory { + kind: crate::BackfillAddressKind::Program, + address: pubkey(41), + anchor_signature: std::option::Option::None, + direction: crate::BackfillDirection::After, + limit: 10, + }, + page_size: 100, + max_pages: 10, + max_concurrent_requests: 2, + max_retries: 2, + }; + assert!(request.validate().is_err()); + } + + #[test] + fn filter_code_distinguishes_target_and_direction() { + let request = crate::BackfillRequest { + role: "history_backfill".to_string(), + commitment: "confirmed".to_string(), + source: crate::BackfillSource::AddressHistory { + kind: crate::BackfillAddressKind::Pool, + address: pubkey(5), + anchor_signature: std::option::Option::Some(signature(6)), + direction: crate::BackfillDirection::After, + limit: 10, + }, + page_size: 100, + max_pages: 10, + max_concurrent_requests: 2, + max_retries: 2, + }; + assert_eq!(request.filter_code(), "pool_after"); + } + + #[test] + fn nearest_newer_candidates_keep_only_entries_closest_to_anchor() { + let page = (1_u8..=6_u8) + .map(|byte| { + return kb_rpc::AddressSignatureInfo { + signature: signature(byte), + slot: u64::from(byte), + err: std::option::Option::None, + memo: std::option::Option::None, + block_time: std::option::Option::None, + confirmation_status: std::option::Option::Some("confirmed".to_string()), + }; + }) + .collect::>(); + let mut candidates = std::collections::VecDeque::new(); + let mut seen = std::collections::HashSet::new(); + crate::backfill::append_nearest_newer_candidates(&mut candidates, &mut seen, &page, 3); + let signatures = candidates + .into_iter() + .map(|candidate| return candidate.signature) + .collect::>(); + assert_eq!(signatures, vec![signature(4), signature(5), signature(6)]); + } + + #[test] + fn nearest_newer_candidates_deduplicate_page_entries() { + let duplicate = signature(7); + let page = vec![ + kb_rpc::AddressSignatureInfo { + signature: duplicate.clone(), + slot: 7, + err: std::option::Option::None, + memo: std::option::Option::None, + block_time: std::option::Option::None, + confirmation_status: std::option::Option::Some("confirmed".to_string()), + }, + kb_rpc::AddressSignatureInfo { + signature: duplicate, + slot: 7, + err: std::option::Option::None, + memo: std::option::Option::None, + block_time: std::option::Option::None, + confirmation_status: std::option::Option::Some("confirmed".to_string()), + }, + ]; + let mut candidates = std::collections::VecDeque::new(); + let mut seen = std::collections::HashSet::new(); + crate::backfill::append_nearest_newer_candidates(&mut candidates, &mut seen, &page, 5); + assert_eq!(candidates.len(), 1); + } + + #[test] + fn lower_hex_encodes_digest_bytes_without_lower_hex_trait() { + assert_eq!(crate::backfill::lower_hex(&[0x00, 0xab, 0xcd, 0xef]), "00abcdef"); + } + + #[test] + fn completion_frontier_advances_only_across_contiguous_results() { + let mut frontier = crate::backfill::CandidateCompletionFrontier::new(4); + frontier.mark_completed(1); + assert_eq!(frontier.contiguous_completed(), 0); + frontier.mark_completed(0); + assert_eq!(frontier.contiguous_completed(), 2); + frontier.mark_completed(3); + assert_eq!(frontier.contiguous_completed(), 2); + frontier.mark_completed(2); + assert_eq!(frontier.contiguous_completed(), 4); + } + + #[test] + fn cancelled_before_resume_uses_last_contiguous_candidate() { + let anchor = signature(20); + let candidates = vec![signature(21), signature(22), signature(23)]; + let request = crate::BackfillRequest { + role: "history_backfill".to_string(), + commitment: "confirmed".to_string(), + source: crate::BackfillSource::AddressHistory { + kind: crate::BackfillAddressKind::Program, + address: pubkey(24), + anchor_signature: std::option::Option::Some(anchor), + direction: crate::BackfillDirection::Before, + limit: 3, + }, + page_size: 100, + max_pages: 10, + max_concurrent_requests: 2, + max_retries: 2, + }; + let resume = crate::backfill::resolved_resume_before_signature( + &request, + candidates.as_slice(), + 2, + std::option::Option::Some(signature(25)), + true, + ); + assert_eq!(resume, std::option::Option::Some(candidates[1].clone())); + } + + #[test] + fn cancelled_before_resume_keeps_anchor_when_nothing_completed() { + let anchor = signature(30); + let request = crate::BackfillRequest { + role: "history_backfill".to_string(), + commitment: "confirmed".to_string(), + source: crate::BackfillSource::AddressHistory { + kind: crate::BackfillAddressKind::Token, + address: pubkey(31), + anchor_signature: std::option::Option::Some(anchor.clone()), + direction: crate::BackfillDirection::Before, + limit: 2, + }, + page_size: 100, + max_pages: 10, + max_concurrent_requests: 2, + max_retries: 2, + }; + let resume = crate::backfill::resolved_resume_before_signature( + &request, + &[signature(32), signature(33)], + 0, + std::option::Option::Some(signature(34)), + true, + ); + assert_eq!(resume, std::option::Option::Some(anchor)); + } + + #[test] + fn cancelled_latest_scan_without_completed_candidate_restarts_from_latest() { + let request = crate::BackfillRequest { + role: "history_backfill".to_string(), + commitment: "confirmed".to_string(), + source: crate::BackfillSource::AddressHistory { + kind: crate::BackfillAddressKind::Program, + address: pubkey(42), + anchor_signature: std::option::Option::None, + direction: crate::BackfillDirection::Before, + limit: 2, + }, + page_size: 100, + max_pages: 10, + max_concurrent_requests: 2, + max_retries: 2, + }; + let resume = crate::backfill::resolved_resume_before_signature( + &request, + &[signature(43), signature(44)], + 0, + std::option::Option::Some(signature(45)), + true, + ); + assert_eq!(resume, std::option::Option::None); + } + + #[test] + fn retry_delay_uses_configured_pause_for_rate_limit() { + assert_eq!(crate::backfill::retry_delay_ms("HTTP 429", 2, 1500), 1500); + assert_eq!(crate::backfill::retry_delay_ms("timeout", 2, 1500), 1000); + } + + struct TestObserver { + cancelled: std::sync::atomic::AtomicBool, + } + + impl TestObserver { + fn new(cancelled: bool) -> Self { + return Self { + cancelled: std::sync::atomic::AtomicBool::new(cancelled), + }; + } + + fn cancel(&self) { + self.cancelled.store(true, std::sync::atomic::Ordering::SeqCst); + } + } + + impl crate::BackfillObserver for TestObserver { + fn on_progress(&self, _event: &crate::BackfillProgressEvent) {} + + fn is_cancelled(&self) -> bool { + return self.cancelled.load(std::sync::atomic::Ordering::SeqCst); + } + } + + #[tokio::test] + async fn cancellable_retry_wait_returns_immediately_when_already_cancelled() { + let observer = TestObserver::new(true); + let started = std::time::Instant::now(); + let completed = + crate::backfill::wait_with_cancellation(&observer, std::time::Duration::from_secs(60)) + .await; + assert!(!completed); + assert!(started.elapsed() < std::time::Duration::from_secs(1)); + } + + #[tokio::test] + async fn long_running_rpc_future_is_cancelled_cooperatively() { + let observer = TestObserver::new(false); + let operation = crate::backfill::await_or_cancelled( + &observer, + tokio::time::sleep(std::time::Duration::from_secs(60)), + ); + let cancellation = async { + tokio::time::sleep(std::time::Duration::from_millis(10)).await; + observer.cancel(); + }; + let (result, ()) = tokio::join!(operation, cancellation); + assert!(result.is_none()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/constants.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/constants.rs new file mode 100644 index 0000000..838e468 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_pipeline/src/constants.rs +// version: 1 + +//! Local constants for the `kb_pipeline` crate. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_pipeline"; diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/core_extraction.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/core_extraction.rs new file mode 100644 index 0000000..1965ca4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/core_extraction.rs @@ -0,0 +1,1844 @@ +// file: kb_pipeline/src/core_extraction.rs +// version: 9 + +//! Canonical Solana transaction to normalized core extraction pipeline. + +use futures_util::FutureExt; // rust-rules: trait-import +use futures_util::StreamExt; // rust-rules: trait-import +use sha2::Digest; // rust-rules: trait-import + +/// Stable processing stage used by the canonical to core extractor. +pub const CORE_EXTRACTION_STAGE: &str = "core_extraction"; +/// Stable processor name used by the canonical to core extractor. +pub const CORE_EXTRACTION_PROCESSOR_NAME: &str = "canonical_to_core"; +/// Current extractor implementation version. +pub const CORE_EXTRACTION_PROCESSOR_VERSION: &str = "1"; + +/// Source selection used by one core extraction campaign. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum CoreExtractionSource { + /// Exact canonical transaction signatures. + Signatures(std::vec::Vec), + /// Canonical transactions still in the received state. + Pending, + /// Canonical transactions inside an inclusive slot range. + SlotRange { + /// Inclusive minimum slot. + min_slot: u64, + /// Inclusive maximum slot. + max_slot: u64, + }, + /// Transactions already containing one resolved core instruction for a program id. + ProgramId { + /// Solana program id. + program_id: std::string::String, + }, +} + +/// Complete bounded core extraction request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct CoreExtractionRequest { + /// Source selection. + pub source: crate::CoreExtractionSource, + /// Maximum number of canonical transactions selected. + pub limit: u32, + /// Maximum concurrent extraction operations. + pub max_concurrent_extractions: u32, + /// Forces replacement even when the same processor version and hash succeeded. + pub force_replay: bool, +} + +impl CoreExtractionRequest { + /// Validates campaign bounds and source values. + pub fn validate(&self) -> kb_core::Result<()> { + if self.limit == 0 { + return std::result::Result::Err(kb_core::Error::config( + "core extraction limit must be greater than zero", + )); + } + if self.max_concurrent_extractions == 0 { + return std::result::Result::Err(kb_core::Error::config( + "core extraction concurrency must be greater than zero", + )); + } + match &self.source { + crate::CoreExtractionSource::Signatures(signatures) => { + if signatures.is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "signature extraction requires at least one signature", + )); + } + for signature in signatures { + let validation_result = kb_rpc::validate_transaction_signature_text( + signature.as_str(), + "core extraction signature", + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + } + }, + crate::CoreExtractionSource::Pending => {}, + crate::CoreExtractionSource::SlotRange { min_slot, max_slot } => { + if min_slot > max_slot { + return std::result::Result::Err(kb_core::Error::config( + "core extraction minimum slot must not exceed maximum slot", + )); + } + }, + crate::CoreExtractionSource::ProgramId { program_id } => { + let validation_result = kb_rpc::validate_solana_pubkey_text( + program_id.as_str(), + "core extraction program id", + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + }, + } + return std::result::Result::Ok(()); + } + + fn selection_filter(&self) -> kb_core::Result { + return match &self.source { + crate::CoreExtractionSource::Signatures(signatures) => { + kb_store_core::CoreExtractionSelectionFilter::new( + signatures.clone(), + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + self.limit, + ) + }, + crate::CoreExtractionSource::Pending => { + kb_store_core::CoreExtractionSelectionFilter::pending(self.limit) + }, + crate::CoreExtractionSource::SlotRange { min_slot, max_slot } => { + kb_store_core::CoreExtractionSelectionFilter::new( + std::vec::Vec::new(), + std::option::Option::Some(*min_slot), + std::option::Option::Some(*max_slot), + std::option::Option::None, + std::option::Option::None, + self.limit, + ) + }, + crate::CoreExtractionSource::ProgramId { program_id } => { + kb_store_core::CoreExtractionSelectionFilter::new( + std::vec::Vec::new(), + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + std::option::Option::Some(program_id.clone()), + self.limit, + ) + }, + }; + } +} + +/// Core extraction progress severity. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum CoreExtractionProgressLevel { + /// Diagnostic detail. + Debug, + /// Normal campaign information. + Info, + /// Recoverable issue or cancellation. + Warning, + /// Extraction failure. + Error, +} + +impl CoreExtractionProgressLevel { + /// Returns the stable lowercase level code. + pub fn code(&self) -> &'static str { + return match self { + crate::CoreExtractionProgressLevel::Debug => "debug", + crate::CoreExtractionProgressLevel::Info => "info", + crate::CoreExtractionProgressLevel::Warning => "warn", + crate::CoreExtractionProgressLevel::Error => "error", + }; + } +} + +/// One operator-visible extraction progress event. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct CoreExtractionProgressEvent { + /// UTC timestamp rendered in RFC 3339. + pub timestamp: std::string::String, + /// Severity. + pub level: crate::CoreExtractionProgressLevel, + /// Human-readable message. + pub message: std::string::String, + /// Number of terminal candidates. + pub completed: u64, + /// Total selected candidates. + pub total: u64, +} + +impl CoreExtractionProgressEvent { + fn new( + level: crate::CoreExtractionProgressLevel, + message: impl std::convert::Into, + completed: u64, + total: u64, + ) -> Self { + return Self { + timestamp: chrono::Utc::now().to_rfc3339(), + level, + message: message.into(), + completed, + total, + }; + } +} + +/// Progress and cancellation contract implemented by applications. +pub trait CoreExtractionObserver: Sync { + /// Receives one progress event. + fn on_progress(&self, event: &crate::CoreExtractionProgressEvent); + /// Returns true when cooperative cancellation was requested. + fn is_cancelled(&self) -> bool; +} + +/// Final counters for one core extraction campaign. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct CoreExtractionSummary { + /// Current extractor implementation version. + pub processor_version: std::string::String, + /// Number of selected canonical transactions. + pub selected: u64, + /// Number admitted to the bounded execution queue. + pub started: u64, + /// Number reaching a terminal result. + pub completed: u64, + /// Number skipped because the version and hash already succeeded. + pub skipped: u64, + /// Number extracted and committed successfully. + pub extracted: u64, + /// Number failing extraction or persistence. + pub failed: u64, + /// Number admitted but cancelled before a terminal result. + pub cancelled_candidates: u64, + /// Number never admitted after cancellation. + pub not_started: u64, + /// Whether the campaign was cancelled. + pub cancelled: bool, + /// Campaign start timestamp. + pub started_at: std::string::String, + /// Campaign finish timestamp. + pub finished_at: std::string::String, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum CoreExtractionItemStatus { + Skipped, + Extracted, + Failed, + Cancelled, +} + +struct CoreExtractionItemOutcome { + index: usize, + status: CoreExtractionItemStatus, + message: std::string::String, +} + +/// Executes one bounded canonical transaction to core extraction campaign. +pub async fn execute_core_extraction( + store: &S, + request: &crate::CoreExtractionRequest, + observer: &O, +) -> kb_core::Result +where + S: kb_store_core::CoreExtractionStore + Sync, + O: crate::CoreExtractionObserver, +{ + let validation_result = request.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let filter_result = request.selection_filter(); + let filter = match filter_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let rows_result = store.list_raw_transactions_for_core_extraction(&filter).await; + let rows = match rows_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let started_at = chrono::Utc::now().to_rfc3339(); + let total = match u64::try_from(rows.len()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "core extraction selected count overflow: {error}" + ))); + }, + }; + tracing::info!( + target: crate::TRACING_TARGET, + selected = total, + processor_version = crate::CORE_EXTRACTION_PROCESSOR_VERSION, + force_replay = request.force_replay, + "core extraction campaign started" + ); + observer.on_progress(&crate::CoreExtractionProgressEvent::new( + crate::CoreExtractionProgressLevel::Info, + format!( + "core extraction started with processor version {} and {total} selected transactions", + crate::CORE_EXTRACTION_PROCESSOR_VERSION + ), + 0, + total, + )); + let concurrency = match usize::try_from(request.max_concurrent_extractions) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::config(format!( + "core extraction concurrency conversion failed: {error}" + ))); + }, + }; + let mut next_index = 0_usize; + let mut in_flight = futures_util::stream::FuturesUnordered::new(); + let mut summary = crate::CoreExtractionSummary { + processor_version: crate::CORE_EXTRACTION_PROCESSOR_VERSION.to_string(), + selected: total, + started: 0, + completed: 0, + skipped: 0, + extracted: 0, + failed: 0, + cancelled_candidates: 0, + not_started: 0, + cancelled: false, + started_at, + finished_at: std::string::String::new(), + }; + loop { + while in_flight.len() < concurrency && next_index < rows.len() && !observer.is_cancelled() { + let row = rows[next_index].clone(); + let item_index = next_index; + let force_replay = request.force_replay; + in_flight.push( + async move { + return crate::core_extraction::process_one_core_extraction( + store, + row, + item_index, + force_replay, + ) + .await; + } + .boxed(), + ); + next_index += 1; + summary.started += 1; + } + if observer.is_cancelled() { + summary.cancelled = true; + } + let next_result = in_flight.next().await; + let outcome = match next_result { + std::option::Option::Some(value) => value, + std::option::Option::None => break, + }; + match outcome.status { + CoreExtractionItemStatus::Skipped => summary.skipped += 1, + CoreExtractionItemStatus::Extracted => summary.extracted += 1, + CoreExtractionItemStatus::Failed => summary.failed += 1, + CoreExtractionItemStatus::Cancelled => summary.cancelled_candidates += 1, + } + if outcome.status != CoreExtractionItemStatus::Cancelled { + summary.completed += 1; + } + let level = match outcome.status { + CoreExtractionItemStatus::Skipped => crate::CoreExtractionProgressLevel::Debug, + CoreExtractionItemStatus::Extracted => crate::CoreExtractionProgressLevel::Info, + CoreExtractionItemStatus::Failed => crate::CoreExtractionProgressLevel::Error, + CoreExtractionItemStatus::Cancelled => crate::CoreExtractionProgressLevel::Warning, + }; + observer.on_progress(&crate::CoreExtractionProgressEvent::new( + level, + format!("candidate {}: {}", outcome.index + 1, outcome.message), + summary.completed, + total, + )); + } + let started_and_completed = summary.completed + summary.cancelled_candidates; + if summary.started > started_and_completed { + summary.cancelled_candidates += summary.started - started_and_completed; + } + if summary.selected > summary.started { + summary.not_started = summary.selected - summary.started; + } + summary.finished_at = chrono::Utc::now().to_rfc3339(); + let final_level = if summary.failed > 0 || summary.cancelled { + crate::CoreExtractionProgressLevel::Warning + } else { + crate::CoreExtractionProgressLevel::Info + }; + tracing::info!( + target: crate::TRACING_TARGET, + extracted = summary.extracted, + skipped = summary.skipped, + failed = summary.failed, + cancelled = summary.cancelled_candidates, + not_started = summary.not_started, + "core extraction campaign completed" + ); + observer.on_progress(&crate::CoreExtractionProgressEvent::new( + final_level, + format!( + "core extraction completed: extracted={}, skipped={}, failed={}, cancelled={}, not_started={}", + summary.extracted, + summary.skipped, + summary.failed, + summary.cancelled_candidates, + summary.not_started + ), + summary.completed, + total, + )); + return std::result::Result::Ok(summary); +} + +async fn process_one_core_extraction( + store: &S, + row: kb_store_core::RawTransactionRow, + index: usize, + force_replay: bool, +) -> crate::core_extraction::CoreExtractionItemOutcome +where + S: kb_store_core::CoreExtractionStore + Sync, +{ + let identity_result = crate::core_extraction::ledger_identity_from_raw(&row); + let identity = match identity_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return crate::core_extraction::CoreExtractionItemOutcome { + index, + status: CoreExtractionItemStatus::Failed, + message: error.to_string(), + }; + }, + }; + if !force_replay { + let current_result = store.is_core_extraction_current(&identity).await; + match current_result { + std::result::Result::Ok(true) => { + return crate::core_extraction::CoreExtractionItemOutcome { + index, + status: CoreExtractionItemStatus::Skipped, + message: "already current for the same version and hash".to_string(), + }; + }, + std::result::Result::Ok(false) => {}, + std::result::Result::Err(error) => { + return crate::core_extraction::CoreExtractionItemOutcome { + index, + status: CoreExtractionItemStatus::Failed, + message: error.to_string(), + }; + }, + } + } + tracing::debug!( + target: crate::TRACING_TARGET, + signature = row.signature.as_str(), + index, + force_replay, + "extracting canonical transaction to core" + ); + let extraction_result = crate::extract_raw_transaction_to_core(&row); + let bundle = match extraction_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + let failure_result = kb_store_core::CoreExtractionFailure::new( + row.id, + identity, + "canonical_to_core_extraction_failed", + error.to_string(), + ); + if let std::result::Result::Ok(failure) = failure_result { + let _mark_result = store.mark_core_extraction_failed(&failure).await; + } + return crate::core_extraction::CoreExtractionItemOutcome { + index, + status: CoreExtractionItemStatus::Failed, + message: error.to_string(), + }; + }, + }; + let persist_result = store.persist_core_extraction(&bundle, force_replay).await; + return match persist_result { + std::result::Result::Ok(_) => crate::core_extraction::CoreExtractionItemOutcome { + index, + status: CoreExtractionItemStatus::Extracted, + message: "extracted and committed".to_string(), + }, + std::result::Result::Err(error) => { + let failure_result = kb_store_core::CoreExtractionFailure::new( + row.id, + bundle.ledger_identity, + "canonical_to_core_persist_failed", + error.to_string(), + ); + if let std::result::Result::Ok(failure) = failure_result { + let _mark_result = store.mark_core_extraction_failed(&failure).await; + } + crate::core_extraction::CoreExtractionItemOutcome { + index, + status: CoreExtractionItemStatus::Failed, + message: error.to_string(), + } + }, + }; +} + +/// Extracts one canonical raw transaction into a complete normalized core bundle. +pub fn extract_raw_transaction_to_core( + row: &kb_store_core::RawTransactionRow, +) -> kb_core::Result { + let canonical_json = match &row.canonical_json { + std::option::Option::Some(value) => value.clone(), + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical JSON is unavailable for core extraction", + )); + }, + }; + let input_hash = match &row.canonical_json_hash { + std::option::Option::Some(value) if !value.trim().is_empty() => value.clone(), + _ => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical JSON hash is unavailable for core extraction", + )); + }, + }; + let expected_format_version = + match i32::try_from(kb_model::CANONICAL_TRANSACTION_FORMAT_VERSION) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "canonical format version conversion failed: {error}" + ))); + }, + }; + if row.canonical_format_version != expected_format_version { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "unsupported canonical format version in raw store: {}", + row.canonical_format_version + ))); + } + let parse_result = serde_json::from_value::(canonical_json); + let transaction = match parse_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot parse canonical transaction for core extraction: {error}" + ))); + }, + }; + let validation_result = transaction.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + if transaction.primary_signature != row.signature { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical transaction signature does not match raw row signature", + )); + } + let row_slot_result = u64::try_from(row.slot); + let row_slot = match row_slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "raw transaction slot is negative: {error}" + ))); + }, + }; + if transaction.slot != row_slot { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical transaction slot does not match raw row slot", + )); + } + let computed_hash_result = transaction.canonical_json_hash(); + let computed_hash = match computed_hash_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if computed_hash != input_hash { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical transaction hash does not match the stored hash", + )); + } + let ledger_identity_result = crate::core_extraction::ledger_identity_from_raw(row); + let ledger_identity = match ledger_identity_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if let std::option::Option::Some(metadata) = &transaction.metadata { + return crate::core_extraction::build_bundle_with_metadata( + row, + &transaction, + metadata, + ledger_identity, + ); + } + let core_transaction_result = kb_store_core::CoreTransactionInsert::new( + transaction.primary_signature.clone(), + transaction.slot, + false, + std::option::Option::None, + ); + let core_transaction = match core_transaction_result { + std::result::Result::Ok(value) => value.with_raw_transaction_id(row.id), + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let account_keys_result = crate::core_extraction::extract_account_keys(&transaction); + let account_keys = match account_keys_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction_result = crate::core_extraction::extract_instructions(&transaction); + let (instructions, inner_instructions) = match instruction_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(kb_store_core::CoreExtractionBundle { + raw_transaction_id: row.id, + ledger_identity, + transaction: core_transaction, + account_keys, + instructions, + inner_instructions, + logs: std::vec::Vec::new(), + balance_changes: std::vec::Vec::new(), + }); +} + +fn build_bundle_with_metadata( + row: &kb_store_core::RawTransactionRow, + transaction: &kb_model::CanonicalTransaction, + metadata: &kb_model::CanonicalTransactionMetadata, + ledger_identity: kb_store_core::ProcessingLedgerIdentity, +) -> kb_core::Result { + let failed = metadata.status == kb_model::CanonicalTransactionStatus::Failed; + let core_transaction_result = kb_store_core::CoreTransactionInsert::new( + transaction.primary_signature.clone(), + transaction.slot, + failed, + metadata.error.clone(), + ); + let core_transaction = match core_transaction_result { + std::result::Result::Ok(value) => value.with_raw_transaction_id(row.id), + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let account_keys_result = crate::core_extraction::extract_account_keys(transaction); + let account_keys = match account_keys_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction_result = crate::core_extraction::extract_instructions(transaction); + let (instructions, inner_instructions) = match instruction_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let logs_result = crate::core_extraction::extract_logs(transaction, metadata); + let logs = match logs_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let balances_result = crate::core_extraction::extract_balance_changes(transaction, metadata); + let balance_changes = match balances_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(kb_store_core::CoreExtractionBundle { + raw_transaction_id: row.id, + ledger_identity, + transaction: core_transaction, + account_keys, + instructions, + inner_instructions, + logs, + balance_changes, + }); +} + +fn ledger_identity_from_raw( + row: &kb_store_core::RawTransactionRow, +) -> kb_core::Result { + let input_hash = match &row.canonical_json_hash { + std::option::Option::Some(value) => value.clone(), + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "canonical JSON hash is required for the processing ledger", + )); + }, + }; + return kb_store_core::ProcessingLedgerIdentity::new( + crate::CORE_EXTRACTION_STAGE, + crate::CORE_EXTRACTION_PROCESSOR_NAME, + crate::CORE_EXTRACTION_PROCESSOR_VERSION, + row.signature.clone(), + input_hash, + ); +} + +fn resolved_account_keys( + transaction: &kb_model::CanonicalTransaction, +) -> std::vec::Vec { + let mut keys = std::vec::Vec::with_capacity(transaction.message.resolved_account_count()); + keys.extend(transaction.message.static_account_keys.iter().cloned()); + keys.extend(transaction.message.loaded_addresses.writable.iter().cloned()); + keys.extend(transaction.message.loaded_addresses.readonly.iter().cloned()); + return keys; +} + +fn extract_account_keys( + transaction: &kb_model::CanonicalTransaction, +) -> kb_core::Result> { + let static_count = transaction.message.static_account_keys.len(); + let required = usize::from(transaction.message.header.num_required_signatures); + let readonly_signed = usize::from(transaction.message.header.num_readonly_signed_accounts); + let readonly_unsigned = usize::from(transaction.message.header.num_readonly_unsigned_accounts); + let writable_signed_count = required - readonly_signed; + let writable_unsigned_end = static_count - readonly_unsigned; + let mut output = std::vec::Vec::with_capacity(transaction.message.resolved_account_count()); + for (index, key) in transaction.message.static_account_keys.iter().enumerate() { + let account_index_result = u32::try_from(index); + let account_index = match account_index_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "static account index overflow: {error}" + ))); + }, + }; + let signer = index < required; + let writable = if signer { + index < writable_signed_count + } else { + index < writable_unsigned_end + }; + let insert_result = kb_store_core::CoreAccountKeyInsert::new( + transaction.primary_signature.clone(), + transaction.slot, + account_index, + key.clone(), + kb_store_core::CoreAccountKeySource::Static, + writable, + signer, + std::option::Option::None, + ); + let insert = match insert_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + output.push(insert); + } + for key in &transaction.message.loaded_addresses.writable { + let account_index_result = u32::try_from(output.len()); + let account_index = match account_index_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "loaded writable account index overflow: {error}" + ))); + }, + }; + let insert_result = kb_store_core::CoreAccountKeyInsert::new( + transaction.primary_signature.clone(), + transaction.slot, + account_index, + key.clone(), + kb_store_core::CoreAccountKeySource::LoadedWritable, + true, + false, + std::option::Option::None, + ); + let insert = match insert_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + output.push(insert); + } + for key in &transaction.message.loaded_addresses.readonly { + let account_index_result = u32::try_from(output.len()); + let account_index = match account_index_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "loaded readonly account index overflow: {error}" + ))); + }, + }; + let insert_result = kb_store_core::CoreAccountKeyInsert::new( + transaction.primary_signature.clone(), + transaction.slot, + account_index, + key.clone(), + kb_store_core::CoreAccountKeySource::LoadedReadonly, + false, + false, + std::option::Option::None, + ); + let insert = match insert_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + output.push(insert); + } + return std::result::Result::Ok(output); +} + +fn extract_instructions( + transaction: &kb_model::CanonicalTransaction, +) -> kb_core::Result<( + std::vec::Vec, + std::vec::Vec, +)> { + let keys = crate::core_extraction::resolved_account_keys(transaction); + let mut instructions = std::vec::Vec::new(); + for (index, instruction) in transaction.message.instructions.iter().enumerate() { + let path = index.to_string(); + let resolved_result = crate::core_extraction::resolve_instruction(&keys, instruction); + let (program_id, accounts_json, payload_json, payload_hash) = match resolved_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let insert_result = kb_store_core::CoreInstructionInsert::new( + transaction.primary_signature.clone(), + transaction.slot, + path, + program_id, + accounts_json, + payload_json, + ); + let insert = match insert_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let hash_result = insert.with_payload_json_hash(payload_hash); + let hashed = match hash_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + instructions.push(hashed); + } + let mut inner_instructions = std::vec::Vec::new(); + if let std::option::Option::Some(metadata) = &transaction.metadata { + for group in &metadata.inner_instructions { + let parent_path = group.parent_instruction_index.to_string(); + for (inner_index, instruction) in group.instructions.iter().enumerate() { + let path = format!("{parent_path}/{inner_index}"); + let resolved_result = + crate::core_extraction::resolve_instruction(&keys, instruction); + let (program_id, accounts_json, payload_json, payload_hash) = match resolved_result + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let insert_result = kb_store_core::CoreInnerInstructionInsert::new( + transaction.primary_signature.clone(), + transaction.slot, + parent_path.clone(), + path, + program_id, + accounts_json, + payload_json, + ); + let insert = match insert_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let hash_result = insert.with_payload_json_hash(payload_hash); + let hashed = match hash_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + inner_instructions.push(hashed); + } + } + } + return std::result::Result::Ok((instructions, inner_instructions)); +} + +fn resolve_instruction( + keys: &[std::string::String], + instruction: &kb_model::CanonicalCompiledInstruction, +) -> kb_core::Result<(std::string::String, serde_json::Value, serde_json::Value, std::string::String)> +{ + let program_id = match keys.get(usize::from(instruction.program_id_index)) { + std::option::Option::Some(value) => value.clone(), + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "instruction program id index is outside resolved accounts", + )); + }, + }; + let mut accounts = std::vec::Vec::with_capacity(instruction.account_indexes.len()); + for account_index in &instruction.account_indexes { + let account_key = match keys.get(usize::from(*account_index)) { + std::option::Option::Some(value) => value.clone(), + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "instruction account index is outside resolved accounts", + )); + }, + }; + accounts.push(serde_json::json!({ + "accountIndex": account_index, + "accountKey": account_key, + })); + } + let accounts_json = serde_json::Value::Array(accounts); + let payload_json = serde_json::json!({ + "programIdIndex": instruction.program_id_index, + "dataBase64": instruction.data_base64, + "stackHeight": instruction.stack_height, + }); + let payload_hash_result = crate::core_extraction::hash_json(&payload_json); + let payload_hash = match payload_hash_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok((program_id, accounts_json, payload_json, payload_hash)); +} + +fn extract_logs( + transaction: &kb_model::CanonicalTransaction, + metadata: &kb_model::CanonicalTransactionMetadata, +) -> kb_core::Result> { + let mut output = std::vec::Vec::with_capacity(metadata.log_messages.len()); + let links = crate::core_extraction::resolve_log_links(transaction, metadata); + for (index, text) in metadata.log_messages.iter().enumerate() { + let log_index_result = u32::try_from(index); + let log_index = match log_index_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "log index overflow: {error}" + ))); + }, + }; + let link = match links.get(index) { + std::option::Option::Some(value) => value.clone(), + std::option::Option::None => (std::option::Option::None, std::option::Option::None), + }; + let insert_result = kb_store_core::CoreLogInsert::new( + transaction.primary_signature.clone(), + transaction.slot, + log_index, + link.1, + link.0, + text.clone(), + ); + let insert = match insert_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let hash = crate::core_extraction::hash_bytes(text.as_bytes()); + let hash_result = insert.with_log_text_hash(hash); + let hashed = match hash_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + output.push(hashed); + } + return std::result::Result::Ok(output); +} + +fn resolve_log_links( + transaction: &kb_model::CanonicalTransaction, + metadata: &kb_model::CanonicalTransactionMetadata, +) -> std::vec::Vec<( + std::option::Option, + std::option::Option, +)> { + let keys = crate::core_extraction::resolved_account_keys(transaction); + let mut top_level = std::vec::Vec::<(std::string::String, std::string::String)>::new(); + for (index, instruction) in transaction.message.instructions.iter().enumerate() { + if let std::option::Option::Some(program_id) = + keys.get(usize::from(instruction.program_id_index)) + { + top_level.push((program_id.clone(), index.to_string())); + } + } + let mut inner_by_parent = std::collections::BTreeMap::< + std::string::String, + std::vec::Vec<(std::string::String, std::string::String)>, + >::new(); + for group in &metadata.inner_instructions { + let parent = group.parent_instruction_index.to_string(); + let entries = inner_by_parent.entry(parent.clone()).or_default(); + for (index, instruction) in group.instructions.iter().enumerate() { + if let std::option::Option::Some(program_id) = + keys.get(usize::from(instruction.program_id_index)) + { + entries.push((program_id.clone(), format!("{parent}/{index}"))); + } + } + } + let mut top_cursor = 0_usize; + let mut inner_cursors = std::collections::BTreeMap::::new(); + let mut stack = + std::vec::Vec::<(std::string::String, std::option::Option)>::new(); + let mut output = std::vec::Vec::with_capacity(metadata.log_messages.len()); + for text in &metadata.log_messages { + let program_line = crate::core_extraction::parse_program_log(text.as_str()); + match program_line { + std::option::Option::Some(( + program_id, + crate::core_extraction::ProgramLogKind::Invoke(depth), + )) => { + let path = if depth == 1 { + let mut resolved = std::option::Option::None; + while top_cursor < top_level.len() { + let candidate = &top_level[top_cursor]; + top_cursor += 1; + if candidate.0 == program_id { + resolved = std::option::Option::Some(candidate.1.clone()); + break; + } + } + resolved + } else { + let parent_path = stack.first().and_then(|entry| return entry.1.clone()); + match parent_path { + std::option::Option::Some(parent) => { + let cursor = inner_cursors.entry(parent.clone()).or_insert(0); + let mut resolved = std::option::Option::None; + if let std::option::Option::Some(entries) = + inner_by_parent.get(parent.as_str()) + { + while *cursor < entries.len() { + let candidate = &entries[*cursor]; + *cursor += 1; + if candidate.0 == program_id { + resolved = std::option::Option::Some(candidate.1.clone()); + break; + } + } + } + resolved + }, + std::option::Option::None => std::option::Option::None, + } + }; + stack.push((program_id.clone(), path.clone())); + output.push((std::option::Option::Some(program_id), path)); + }, + std::option::Option::Some(( + program_id, + crate::core_extraction::ProgramLogKind::Terminal, + )) => { + let path = stack.last().and_then(|entry| { + return if entry.0 == program_id { + entry.1.clone() + } else { + std::option::Option::None + }; + }); + output.push((std::option::Option::Some(program_id.clone()), path)); + if stack.last().map(|entry| return entry.0.as_str()) + == std::option::Option::Some(program_id.as_str()) + { + stack.pop(); + } + }, + std::option::Option::None => { + let current = stack.last().cloned(); + match current { + std::option::Option::Some((program_id, path)) => { + output.push((std::option::Option::Some(program_id), path)) + }, + std::option::Option::None => { + output.push((std::option::Option::None, std::option::Option::None)) + }, + } + }, + } + } + return output; +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum ProgramLogKind { + Invoke(u32), + Terminal, +} + +fn parse_program_log(text: &str) -> std::option::Option<(std::string::String, ProgramLogKind)> { + let remainder = match text.strip_prefix("Program ") { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::option::Option::None, + }; + let split = remainder.split_once(' '); + let (program_id, suffix) = match split { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::option::Option::None, + }; + if let std::option::Option::Some(depth_text) = + suffix.strip_prefix("invoke [").and_then(|value| return value.strip_suffix(']')) + { + let parse_result = depth_text.parse::(); + if let std::result::Result::Ok(depth) = parse_result { + return std::option::Option::Some(( + program_id.to_string(), + ProgramLogKind::Invoke(depth), + )); + } + } + if suffix == "success" || suffix.starts_with("failed:") { + return std::option::Option::Some((program_id.to_string(), ProgramLogKind::Terminal)); + } + return std::option::Option::None; +} + +fn extract_balance_changes( + transaction: &kb_model::CanonicalTransaction, + metadata: &kb_model::CanonicalTransactionMetadata, +) -> kb_core::Result> { + let keys = crate::core_extraction::resolved_account_keys(transaction); + let mut output = std::vec::Vec::new(); + for index in 0..metadata.pre_balances.len() { + let pre = metadata.pre_balances[index]; + let post = metadata.post_balances[index]; + let delta = i128::from(post) - i128::from(pre); + let account_index_result = u32::try_from(index); + let account_index = match account_index_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "native balance account index overflow: {error}" + ))); + }, + }; + let balance_index_result = u32::try_from(output.len()); + let balance_index = match balance_index_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "balance change index overflow: {error}" + ))); + }, + }; + let account_key = keys.get(index).cloned(); + let insert_result = kb_store_core::CoreBalanceChangeInsert::new( + transaction.primary_signature.clone(), + transaction.slot, + balance_index, + kb_store_core::CoreBalanceChangeKind::NativeLamports, + std::option::Option::Some(account_index), + account_key, + std::option::Option::None, + std::option::Option::None, + std::option::Option::Some(serde_json::Value::String(pre.to_string())), + std::option::Option::Some(serde_json::Value::String(post.to_string())), + std::option::Option::Some(serde_json::Value::String(delta.to_string())), + ); + let insert = match insert_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + output.push(insert); + } + let mut token_keys = std::collections::BTreeSet::<( + u16, + std::string::String, + std::option::Option, + )>::new(); + for balance in &metadata.pre_token_balances { + token_keys.insert(( + balance.account_index, + balance.mint.clone(), + balance.program_id.clone(), + )); + } + for balance in &metadata.post_token_balances { + token_keys.insert(( + balance.account_index, + balance.mint.clone(), + balance.program_id.clone(), + )); + } + for key in token_keys { + let pre = metadata.pre_token_balances.iter().find(|balance| { + return balance.account_index == key.0 + && balance.mint == key.1 + && balance.program_id == key.2; + }); + let post = metadata.post_token_balances.iter().find(|balance| { + return balance.account_index == key.0 + && balance.mint == key.1 + && balance.program_id == key.2; + }); + let decimals = match (pre, post) { + (std::option::Option::Some(left), std::option::Option::Some(right)) => { + if left.decimals != right.decimals { + return std::result::Result::Err(kb_core::Error::invalid_state( + "token balance decimals changed between pre and post metadata", + )); + } + left.decimals + }, + (std::option::Option::Some(value), std::option::Option::None) => value.decimals, + (std::option::Option::None, std::option::Option::Some(value)) => value.decimals, + (std::option::Option::None, std::option::Option::None) => continue, + }; + let pre_amount = match pre { + std::option::Option::Some(balance) => balance.amount.as_str(), + std::option::Option::None => "0", + }; + let post_amount = match post { + std::option::Option::Some(balance) => balance.amount.as_str(), + std::option::Option::None => "0", + }; + let delta_amount = + crate::core_extraction::subtract_unsigned_decimal(post_amount, pre_amount); + let delta_decimal = + crate::core_extraction::signed_fixed_decimal(delta_amount.as_str(), decimals); + let balance_index_result = u32::try_from(output.len()); + let balance_index = match balance_index_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "token balance change index overflow: {error}" + ))); + }, + }; + let account_key = keys.get(usize::from(key.0)).cloned(); + let owner = post + .and_then(|balance| return balance.owner.clone()) + .or_else(|| return pre.and_then(|balance| return balance.owner.clone())); + let pre_json = match pre { + std::option::Option::Some(balance) => serde_json::json!({ + "amount": balance.amount, + "decimals": balance.decimals, + "decimalAmount": balance.decimal_amount, + "programId": balance.program_id, + }), + std::option::Option::None => serde_json::json!({ + "amount": "0", + "decimals": decimals, + "decimalAmount": crate::core_extraction::signed_fixed_decimal("0", decimals), + "programId": key.2.clone(), + }), + }; + let post_json = match post { + std::option::Option::Some(balance) => serde_json::json!({ + "amount": balance.amount, + "decimals": balance.decimals, + "decimalAmount": balance.decimal_amount, + "programId": balance.program_id, + }), + std::option::Option::None => serde_json::json!({ + "amount": "0", + "decimals": decimals, + "decimalAmount": crate::core_extraction::signed_fixed_decimal("0", decimals), + "programId": key.2.clone(), + }), + }; + let delta_json = serde_json::json!({ + "amount": delta_amount, + "decimals": decimals, + "decimalAmount": delta_decimal, + "programId": key.2.clone(), + }); + let insert_result = kb_store_core::CoreBalanceChangeInsert::new( + transaction.primary_signature.clone(), + transaction.slot, + balance_index, + kb_store_core::CoreBalanceChangeKind::TokenAmount, + std::option::Option::Some(u32::from(key.0)), + account_key, + std::option::Option::Some(key.1), + owner, + std::option::Option::Some(pre_json), + std::option::Option::Some(post_json), + std::option::Option::Some(delta_json), + ); + let insert = match insert_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + output.push(insert); + } + return std::result::Result::Ok(output); +} + +fn subtract_unsigned_decimal(left: &str, right: &str) -> std::string::String { + let ordering = crate::core_extraction::compare_unsigned_decimal(left, right); + return match ordering { + std::cmp::Ordering::Equal => "0".to_string(), + std::cmp::Ordering::Greater => crate::core_extraction::subtract_magnitude(left, right), + std::cmp::Ordering::Less => { + format!("-{}", crate::core_extraction::subtract_magnitude(right, left)) + }, + }; +} + +fn compare_unsigned_decimal(left: &str, right: &str) -> std::cmp::Ordering { + let left_trimmed = left.trim_start_matches('0'); + let right_trimmed = right.trim_start_matches('0'); + let left_value = if left_trimmed.is_empty() { "0" } else { left_trimmed }; + let right_value = if right_trimmed.is_empty() { "0" } else { right_trimmed }; + return match left_value.len().cmp(&right_value.len()) { + std::cmp::Ordering::Equal => left_value.cmp(right_value), + ordering => ordering, + }; +} + +fn subtract_magnitude(larger: &str, smaller: &str) -> std::string::String { + let mut result = std::vec::Vec::::new(); + let larger_bytes = larger.as_bytes(); + let smaller_bytes = smaller.as_bytes(); + let mut borrow = 0_i16; + for offset in 0..larger_bytes.len() { + let larger_index = larger_bytes.len() - 1 - offset; + let mut digit = i16::from(larger_bytes[larger_index] - b'0') - borrow; + let smaller_digit = if offset < smaller_bytes.len() { + i16::from(smaller_bytes[smaller_bytes.len() - 1 - offset] - b'0') + } else { + 0 + }; + if digit < smaller_digit { + digit += 10; + borrow = 1; + } else { + borrow = 0; + } + let conversion_result = u8::try_from(digit - smaller_digit); + let value = match conversion_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => 0, + }; + result.push(value + b'0'); + } + while result.len() > 1 && result.last() == std::option::Option::Some(&b'0') { + result.pop(); + } + result.reverse(); + let text_result = std::string::String::from_utf8(result); + return match text_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => "0".to_string(), + }; +} + +fn signed_fixed_decimal(amount: &str, decimals: u8) -> std::string::String { + let negative = amount.starts_with('-'); + let magnitude = match amount.strip_prefix('-') { + std::option::Option::Some(value) => value, + std::option::Option::None => amount, + }; + let scale = usize::from(decimals); + let unsigned = if scale == 0 { + magnitude.to_string() + } else if magnitude.len() > scale { + let split = magnitude.len() - scale; + format!("{}.{}", &magnitude[..split], &magnitude[split..]) + } else { + format!("0.{}{}", "0".repeat(scale - magnitude.len()), magnitude) + }; + if negative && magnitude != "0" { + return format!("-{unsigned}"); + } + return unsigned; +} + +fn hash_json(value: &serde_json::Value) -> kb_core::Result { + let serialization_result = serde_json::to_vec(value); + let bytes = match serialization_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot serialize core payload for hashing: {error}" + ))); + }, + }; + return std::result::Result::Ok(crate::core_extraction::hash_bytes(bytes.as_slice())); +} + +fn hash_bytes(bytes: &[u8]) -> std::string::String { + let digest = sha2::Sha256::digest(bytes); + let mut output = std::string::String::with_capacity(64); + for byte in digest { + output.push_str(format!("{byte:02x}").as_str()); + } + return output; +} + +#[cfg(test)] +mod tests { + struct TestCoreExtractionStore { + rows: std::vec::Vec, + current: std::sync::atomic::AtomicBool, + persist_calls: std::sync::atomic::AtomicU64, + failure_calls: std::sync::atomic::AtomicU64, + } + + impl TestCoreExtractionStore { + fn new(rows: std::vec::Vec, current: bool) -> Self { + return Self { + rows, + current: std::sync::atomic::AtomicBool::new(current), + persist_calls: std::sync::atomic::AtomicU64::new(0), + failure_calls: std::sync::atomic::AtomicU64::new(0), + }; + } + } + + #[async_trait::async_trait] + impl kb_store_core::CoreExtractionStore for TestCoreExtractionStore { + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn list_raw_transactions_for_core_extraction( + &self, + _filter: &kb_store_core::CoreExtractionSelectionFilter, + ) -> kb_core::Result> { + return std::result::Result::Ok(self.rows.clone()); + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn is_core_extraction_current( + &self, + _identity: &kb_store_core::ProcessingLedgerIdentity, + ) -> kb_core::Result { + return std::result::Result::Ok( + self.current.load(std::sync::atomic::Ordering::Acquire), + ); + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn persist_core_extraction( + &self, + _bundle: &kb_store_core::CoreExtractionBundle, + _force_replay: bool, + ) -> kb_core::Result { + self.persist_calls.fetch_add(1, std::sync::atomic::Ordering::AcqRel); + return std::result::Result::Ok(kb_store_core::InsertOutcome::new(1, 0, 0)); + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn mark_core_extraction_failed( + &self, + _failure: &kb_store_core::CoreExtractionFailure, + ) -> kb_core::Result { + self.failure_calls.fetch_add(1, std::sync::atomic::Ordering::AcqRel); + return std::result::Result::Ok(kb_store_core::InsertOutcome::new(0, 1, 0)); + } + } + + struct TestCoreExtractionObserver { + cancelled: bool, + events: std::sync::Mutex>, + } + + impl TestCoreExtractionObserver { + fn new(cancelled: bool) -> Self { + return Self { + cancelled, + events: std::sync::Mutex::new(std::vec::Vec::new()), + }; + } + } + + impl crate::CoreExtractionObserver for TestCoreExtractionObserver { + fn on_progress(&self, event: &crate::CoreExtractionProgressEvent) { + let lock_result = self.events.lock(); + if let std::result::Result::Ok(mut events) = lock_result { + events.push(event.clone()); + } + } + + fn is_cancelled(&self) -> bool { + return self.cancelled; + } + } + + fn sample_transaction() -> kb_model::CanonicalTransaction { + let token_pre = match kb_model::CanonicalTokenBalance::new( + 1, + "11111111111111111111111111111111", + std::option::Option::Some("SysvarC1ock11111111111111111111111111111111".to_string()), + std::option::Option::Some("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA".to_string()), + "100", + 2, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("sample token pre balance failed: {error}"), + }; + let token_post = match kb_model::CanonicalTokenBalance::new( + 1, + "11111111111111111111111111111111", + std::option::Option::Some("SysvarC1ock11111111111111111111111111111111".to_string()), + std::option::Option::Some("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA".to_string()), + "125", + 2, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("sample token post balance failed: {error}"), + }; + return kb_model::CanonicalTransaction { + format_version: kb_model::CANONICAL_TRANSACTION_FORMAT_VERSION, + primary_signature: "2Ana1pUpv2ZbMVkwF5FXapYeBEjdxDatLn7nvJkhgTSXbs59SyZSx866bXirPgj8QQVB57uxHJBG1YFvkRbFj4T".to_string(), + slot: 42, + block_time: std::option::Option::Some(1_700_000_000), + version: kb_model::CanonicalTransactionVersion::Legacy, + signatures: std::vec!["2Ana1pUpv2ZbMVkwF5FXapYeBEjdxDatLn7nvJkhgTSXbs59SyZSx866bXirPgj8QQVB57uxHJBG1YFvkRbFj4T".to_string()], + message: kb_model::CanonicalTransactionMessage { + header: kb_model::CanonicalMessageHeader { + num_required_signatures: 1, + num_readonly_signed_accounts: 0, + num_readonly_unsigned_accounts: 1, + }, + static_account_keys: std::vec![ + "11111111111111111111111111111111".to_string(), + "SysvarC1ock11111111111111111111111111111111".to_string(), + ], + recent_blockhash: "11111111111111111111111111111111".to_string(), + instructions: std::vec![kb_model::CanonicalCompiledInstruction { + program_id_index: 0, + account_indexes: std::vec![1], + data_base64: "AQ==".to_string(), + stack_height: std::option::Option::Some(1), + }], + address_table_lookups: std::vec::Vec::new(), + loaded_addresses: kb_model::CanonicalLoadedAddresses::default(), + }, + metadata: std::option::Option::Some(kb_model::CanonicalTransactionMetadata { + status: kb_model::CanonicalTransactionStatus::Success, + error: std::option::Option::None, + fee: 5000, + pre_balances: std::vec![10_000, 1], + post_balances: std::vec![5_000, 1], + inner_instructions: std::vec![kb_model::CanonicalInnerInstructionGroup { + parent_instruction_index: 0, + instructions: std::vec![kb_model::CanonicalCompiledInstruction { + program_id_index: 0, + account_indexes: std::vec![1], + data_base64: "Ag==".to_string(), + stack_height: std::option::Option::Some(2), + }], + }], + log_messages: std::vec![ + "Program 11111111111111111111111111111111 invoke [1]".to_string(), + "Program 11111111111111111111111111111111 invoke [2]".to_string(), + "Program 11111111111111111111111111111111 success".to_string(), + "Program 11111111111111111111111111111111 success".to_string(), + ], + pre_token_balances: std::vec![token_pre], + post_token_balances: std::vec![token_post], + rewards: std::vec::Vec::new(), + return_data: std::option::Option::None, + compute_units_consumed: std::option::Option::Some(100), + cost_units: std::option::Option::Some(120), + }), + }; + } + + fn raw_row(transaction: &kb_model::CanonicalTransaction) -> kb_store_core::RawTransactionRow { + let json = match transaction.to_canonical_json() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("sample canonical JSON failed: {error}"), + }; + let hash = match transaction.canonical_json_hash() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("sample canonical hash failed: {error}"), + }; + return kb_store_core::RawTransactionRow { + id: 1, + signature: transaction.primary_signature.clone(), + slot: 42, + canonical_json: std::option::Option::Some(json), + canonical_json_hash: std::option::Option::Some(hash), + canonical_format_version: 1, + retention_state: kb_store_core::RawPayloadRetentionState::Full, + processing_state: kb_store_core::RawPayloadProcessingState::Received, + created_at: chrono::Utc::now(), + updated_at: chrono::Utc::now(), + }; + } + + #[test] + fn extraction_resolves_accounts_instructions_logs_and_balances() { + let transaction = crate::core_extraction::tests::sample_transaction(); + let row = crate::core_extraction::tests::raw_row(&transaction); + let result = crate::extract_raw_transaction_to_core(&row); + let bundle = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("core extraction failed: {error}"), + }; + assert_eq!(bundle.account_keys.len(), 2); + assert_eq!(bundle.instructions.len(), 1); + assert_eq!(bundle.inner_instructions.len(), 1); + assert_eq!(bundle.logs.len(), 4); + assert_eq!(bundle.balance_changes.len(), 3); + assert_eq!(bundle.inner_instructions[0].instruction_path, "0/0"); + assert_eq!(bundle.logs[1].instruction_path.as_deref(), std::option::Option::Some("0/0")); + } + + #[test] + fn extraction_rejects_hash_mismatch() { + let transaction = crate::core_extraction::tests::sample_transaction(); + let mut row = crate::core_extraction::tests::raw_row(&transaction); + row.canonical_json_hash = std::option::Option::Some("bad".to_string()); + let result = crate::extract_raw_transaction_to_core(&row); + assert!(result.is_err()); + } + + #[test] + fn decimal_subtraction_handles_positive_negative_and_zero() { + assert_eq!(crate::core_extraction::subtract_unsigned_decimal("125", "100"), "25"); + assert_eq!(crate::core_extraction::subtract_unsigned_decimal("100", "125"), "-25"); + assert_eq!(crate::core_extraction::subtract_unsigned_decimal("000", "0"), "0"); + assert_eq!(crate::core_extraction::signed_fixed_decimal("-25", 2), "-0.25"); + } + + #[test] + fn static_account_flags_follow_message_header() { + let transaction = crate::core_extraction::tests::sample_transaction(); + let result = crate::core_extraction::extract_account_keys(&transaction); + let keys = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("account extraction failed: {error}"), + }; + assert!(keys[0].signer); + assert!(keys[0].writable); + assert!(!keys[1].signer); + assert!(!keys[1].writable); + } + + #[test] + fn version_zero_loaded_addresses_extend_resolved_account_space() { + let mut transaction = crate::core_extraction::tests::sample_transaction(); + transaction.version = kb_model::CanonicalTransactionVersion::Number(0); + transaction.message.loaded_addresses.writable = + std::vec!["Vote111111111111111111111111111111111111111".to_string(),]; + transaction.message.loaded_addresses.readonly = + std::vec!["Stake11111111111111111111111111111111111111".to_string(),]; + transaction.message.instructions[0].program_id_index = 2; + transaction.message.instructions[0].account_indexes = std::vec![3]; + let result = crate::core_extraction::extract_account_keys(&transaction); + let keys = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("loaded account extraction failed: {error}"), + }; + assert_eq!(keys.len(), 4); + assert_eq!(keys[2].source, kb_store_core::CoreAccountKeySource::LoadedWritable); + assert!(keys[2].writable); + assert!(!keys[2].signer); + assert_eq!(keys[3].source, kb_store_core::CoreAccountKeySource::LoadedReadonly); + assert!(!keys[3].writable); + let instruction_result = crate::core_extraction::extract_instructions(&transaction); + let (instructions, _) = match instruction_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("loaded instruction extraction failed: {error}") + }, + }; + assert_eq!(instructions[0].program_id, keys[2].account_key); + } + + #[test] + fn failed_transaction_preserves_error_json() { + let mut transaction = crate::core_extraction::tests::sample_transaction(); + let metadata = match transaction.metadata.as_mut() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("sample metadata is required"), + }; + metadata.status = kb_model::CanonicalTransactionStatus::Failed; + metadata.error = + std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]})); + let expected_error = metadata.error.clone(); + let row = crate::core_extraction::tests::raw_row(&transaction); + let result = crate::extract_raw_transaction_to_core(&row); + let bundle = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("failed transaction extraction failed: {error}") + }, + }; + assert!(bundle.transaction.failed); + assert_eq!(bundle.transaction.err_json, expected_error); + } + + #[test] + fn transaction_without_metadata_keeps_structural_core_rows() { + let mut transaction = crate::core_extraction::tests::sample_transaction(); + transaction.metadata = std::option::Option::None; + let row = crate::core_extraction::tests::raw_row(&transaction); + let result = crate::extract_raw_transaction_to_core(&row); + let bundle = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("metadata-free extraction failed: {error}"), + }; + assert!(!bundle.transaction.failed); + assert_eq!(bundle.account_keys.len(), 2); + assert_eq!(bundle.instructions.len(), 1); + assert!(bundle.logs.is_empty()); + assert!(bundle.balance_changes.is_empty()); + } + + #[test] + fn invalid_instruction_indexes_are_rejected() { + let transaction = crate::core_extraction::tests::sample_transaction(); + let mut row = crate::core_extraction::tests::raw_row(&transaction); + let canonical_json = match row.canonical_json.as_mut() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("sample canonical JSON is required"), + }; + canonical_json["message"]["instructions"][0]["programIdIndex"] = serde_json::json!(99); + let result = crate::extract_raw_transaction_to_core(&row); + assert!(result.is_err()); + let mut account_row = crate::core_extraction::tests::raw_row(&transaction); + let account_json = match account_row.canonical_json.as_mut() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("sample canonical JSON is required"), + }; + account_json["message"]["instructions"][0]["accountIndexes"] = serde_json::json!([99]); + let account_result = crate::extract_raw_transaction_to_core(&account_row); + assert!(account_result.is_err()); + } + + #[test] + fn token_account_created_or_closed_produces_deterministic_changes() { + let mut created = crate::core_extraction::tests::sample_transaction(); + let created_metadata = match created.metadata.as_mut() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("sample metadata is required"), + }; + created_metadata.pre_token_balances.clear(); + let created_row = crate::core_extraction::tests::raw_row(&created); + let created_bundle = match crate::extract_raw_transaction_to_core(&created_row) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("created token extraction failed: {error}"), + }; + let created_token = match created_bundle.balance_changes.last() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("created token balance is required"), + }; + assert_eq!( + created_token.pre_balance_json, + std::option::Option::Some(serde_json::json!({ + "amount": "0", + "decimals": 2, + "decimalAmount": "0.00", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + })) + ); + let mut closed = crate::core_extraction::tests::sample_transaction(); + let closed_metadata = match closed.metadata.as_mut() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("sample metadata is required"), + }; + closed_metadata.post_token_balances.clear(); + let closed_row = crate::core_extraction::tests::raw_row(&closed); + let closed_bundle = match crate::extract_raw_transaction_to_core(&closed_row) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("closed token extraction failed: {error}"), + }; + let closed_token = match closed_bundle.balance_changes.last() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("closed token balance is required"), + }; + assert_eq!( + closed_token.post_balance_json, + std::option::Option::Some(serde_json::json!({ + "amount": "0", + "decimals": 2, + "decimalAmount": "0.00", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + })) + ); + } + + #[tokio::test] + async fn current_version_and_hash_are_skipped_without_persistence() { + let transaction = crate::core_extraction::tests::sample_transaction(); + let row = crate::core_extraction::tests::raw_row(&transaction); + let store = + crate::core_extraction::tests::TestCoreExtractionStore::new(std::vec![row], true); + let observer = crate::core_extraction::tests::TestCoreExtractionObserver::new(false); + let request = crate::CoreExtractionRequest { + source: crate::CoreExtractionSource::Pending, + limit: 1, + max_concurrent_extractions: 1, + force_replay: false, + }; + let result = crate::execute_core_extraction(&store, &request, &observer).await; + let summary = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("skip campaign failed: {error}"), + }; + assert_eq!(summary.skipped, 1); + assert_eq!(summary.extracted, 0); + assert_eq!(store.persist_calls.load(std::sync::atomic::Ordering::Acquire), 0); + } + + #[tokio::test] + async fn force_replay_persists_even_when_ledger_is_current() { + let transaction = crate::core_extraction::tests::sample_transaction(); + let row = crate::core_extraction::tests::raw_row(&transaction); + let store = + crate::core_extraction::tests::TestCoreExtractionStore::new(std::vec![row], true); + let observer = crate::core_extraction::tests::TestCoreExtractionObserver::new(false); + let request = crate::CoreExtractionRequest { + source: crate::CoreExtractionSource::Pending, + limit: 1, + max_concurrent_extractions: 1, + force_replay: true, + }; + let result = crate::execute_core_extraction(&store, &request, &observer).await; + let summary = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("force replay campaign failed: {error}"), + }; + assert_eq!(summary.extracted, 1); + assert_eq!(summary.skipped, 0); + assert_eq!(store.persist_calls.load(std::sync::atomic::Ordering::Acquire), 1); + } + + #[tokio::test] + async fn cancellation_before_admission_keeps_every_candidate_not_started() { + let transaction = crate::core_extraction::tests::sample_transaction(); + let row = crate::core_extraction::tests::raw_row(&transaction); + let store = + crate::core_extraction::tests::TestCoreExtractionStore::new(std::vec![row], false); + let observer = crate::core_extraction::tests::TestCoreExtractionObserver::new(true); + let request = crate::CoreExtractionRequest { + source: crate::CoreExtractionSource::Pending, + limit: 1, + max_concurrent_extractions: 1, + force_replay: false, + }; + let result = crate::execute_core_extraction(&store, &request, &observer).await; + let summary = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("cancelled campaign failed: {error}"), + }; + assert!(summary.cancelled); + assert_eq!(summary.started, 0); + assert_eq!(summary.completed, 0); + assert_eq!(summary.not_started, 1); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/decode_replay.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/decode_replay.rs new file mode 100644 index 0000000..9c4d511 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/decode_replay.rs @@ -0,0 +1,2293 @@ +// file: kb_pipeline/src/decode_replay.rs +// version: 25 + +//! Common contextual instruction decode and optional materialization pipeline. + +use futures_util::StreamExt; // rust-rules: trait-import +use tracing::Instrument; // rust-rules: trait-import + +static DECODE_CAMPAIGN_SEQUENCE: std::sync::atomic::AtomicU64 = + std::sync::atomic::AtomicU64::new(1); + +/// Stable processing ledger stage used by contextual instruction decoders. +pub const INSTRUCTION_DECODE_STAGE: &str = "instruction_decode"; +/// Stable processing ledger stage used by decoded event materializers. +pub const EVENT_MATERIALIZATION_STAGE: &str = "event_materialization"; +/// Current common decode pipeline orchestration version. +pub const DECODE_PIPELINE_VERSION: &str = "1"; + +/// Deterministic policy used when several decoders recognize one input. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum DecodeDispatchPolicy { + /// Run only the highest-ranked compatible decoder. + HighestPriority, + /// Run every compatible decoder in deterministic rank order. + AllCompatible, +} + +/// Complete bounded contextual decode replay request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DecodeReplayRequest { + /// Stable caller-provided campaign identifier used by structured tracing. + pub campaign_id: std::string::String, + /// Bounded backend-neutral input selection. + pub selection: kb_store_core::DecodeSelectionFilter, + /// Explicit decoder names to enable, or every supplied decoder when empty. + pub decoder_names: std::vec::Vec, + /// Deterministic multi-decoder dispatch policy. + pub dispatch_policy: crate::DecodeDispatchPolicy, + /// Maximum concurrent contextual input executions. + pub max_concurrent_inputs: u32, + /// Replaces processor-owned outputs for the selected version and input. + pub force_replay: bool, + /// Explicitly authorizes a bounded force replay without exact signatures. + pub force_replay_all_matching: bool, + /// Runs compatible materializers after decoded observations are committed. + pub materialize_after_decode: bool, +} + +impl DecodeReplayRequest { + /// Validates campaign bounds and explicit decoder names. + pub fn validate(&self) -> kb_core::Result<()> { + if self.campaign_id.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "decode replay campaign id must not be empty", + )); + } + if self.max_concurrent_inputs == 0 { + return std::result::Result::Err(kb_core::Error::config( + "decode replay concurrency must be greater than zero", + )); + } + if self.force_replay + && self.selection.signatures.is_empty() + && !self.force_replay_all_matching + { + return std::result::Result::Err(kb_core::Error::config( + "force replay requires exact signatures or explicit all-matching authorization", + )); + } + if self.force_replay_all_matching && !self.force_replay { + return std::result::Result::Err(kb_core::Error::config( + "all-matching replay authorization requires force replay", + )); + } + if self.force_replay_all_matching && !self.selection.signatures.is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "all-matching replay authorization cannot be combined with exact signatures", + )); + } + if self.decoder_names.iter().any(|name| return name.trim().is_empty()) { + return std::result::Result::Err(kb_core::Error::config( + "decode replay decoder names must not be empty", + )); + } + return std::result::Result::Ok(()); + } +} + +/// Decode replay progress severity. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum DecodeReplayProgressLevel { + /// Diagnostic detail. + Debug, + /// Normal campaign information. + Info, + /// Recoverable issue or cancellation. + Warning, + /// Decode or persistence failure. + Error, +} + +impl DecodeReplayProgressLevel { + /// Returns the stable lowercase level code. + pub fn code(&self) -> &'static str { + return match self { + crate::DecodeReplayProgressLevel::Debug => "debug", + crate::DecodeReplayProgressLevel::Info => "info", + crate::DecodeReplayProgressLevel::Warning => "warn", + crate::DecodeReplayProgressLevel::Error => "error", + }; + } +} + +/// One operator-visible decode replay progress event. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DecodeReplayProgressEvent { + /// UTC timestamp rendered in RFC 3339. + pub timestamp: std::string::String, + /// Severity. + pub level: crate::DecodeReplayProgressLevel, + /// Human-readable message. + pub message: std::string::String, + /// Number of terminal contextual inputs. + pub completed: u64, + /// Total selected contextual inputs. + pub total: u64, +} + +impl DecodeReplayProgressEvent { + fn new( + level: crate::DecodeReplayProgressLevel, + message: impl std::convert::Into, + completed: u64, + total: u64, + ) -> Self { + return Self { + timestamp: chrono::Utc::now().to_rfc3339(), + level, + message: message.into(), + completed, + total, + }; + } +} + +/// Progress and cooperative cancellation contract implemented by applications. +pub trait DecodeReplayObserver: Sync { + /// Receives one progress event. + fn on_progress(&self, event: &crate::DecodeReplayProgressEvent); + /// Returns true when cooperative cancellation was requested. + fn is_cancelled(&self) -> bool; +} + +/// Aggregated terminal counters for one decoder version. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DecodeProcessorSummary { + /// Stable decoder name. + pub processor_name: std::string::String, + /// Stable decoder version. + pub processor_version: std::string::String, + /// Number of compatible dispatches. + pub dispatched: u64, + /// Number skipped because version and contextual input hash already succeeded. + pub skipped: u64, + /// Number producing decoded observations. + pub decoded: u64, + /// Number intentionally ignored. + pub ignored: u64, + /// Number recognized but unsupported. + pub unsupported: u64, + /// Number failing decode, validation or persistence. + pub failed: u64, + /// Number of materialized outputs committed. + pub materialized_outputs: u64, + /// Number of materializer policy refusals committed to the ledger. + pub materialization_refused: u64, +} + +/// Final counters for one bounded contextual decode replay campaign. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DecodeReplaySummary { + /// Stable process-local campaign identifier used by structured tracing. + pub campaign_id: std::string::String, + /// Common orchestration implementation version. + pub pipeline_version: std::string::String, + /// Number of selected contextual inputs. + pub selected: u64, + /// Number admitted to execution. + pub started: u64, + /// Number reaching a terminal input result. + pub completed: u64, + /// Number with no compatible enabled decoder. + pub unmatched: u64, + /// Number never decoded because cancellation was already requested. + pub not_started: u64, + /// Number of input-level failures. + pub failed_inputs: u64, + /// Whether cooperative cancellation was observed. + pub cancelled: bool, + /// Per-decoder counters ordered by name and version. + pub processors: std::vec::Vec, + /// Campaign start timestamp. + pub started_at: std::string::String, + /// Campaign finish timestamp. + pub finished_at: std::string::String, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +struct DecodeItemOutcome { + signature: std::string::String, + slot: u64, + instruction_path: std::string::String, + program_id: std::string::String, + started: bool, + unmatched: bool, + failed: bool, + cancelled: bool, + processors: std::vec::Vec, +} + +struct RankedDecoder<'decoder> { + decoder: &'decoder dyn kb_decoder_api::InstructionDecoder, + identity: kb_decoder_api::DecoderIdentity, + recognition: kb_decoder_api::DecoderRecognition, +} + +/// Executes one bounded contextual instruction decode and optional materialization campaign. +pub async fn execute_decode_replay( + store: &S, + request: &crate::DecodeReplayRequest, + decoders: &[std::sync::Arc], + materializers: &[std::sync::Arc], + observer: &O, +) -> kb_core::Result +where + S: kb_store_core::DecodePipelineStore + Sync, + O: crate::DecodeReplayObserver, +{ + let campaign_id = request.campaign_id.clone(); + let campaign_span = tracing::debug_span!( + target: crate::TRACING_TARGET, + "decode_replay_campaign", + campaign_id = %campaign_id, + pipeline_version = crate::DECODE_PIPELINE_VERSION + ); + return crate::decode_replay::execute_decode_replay_campaign( + store, + request, + decoders, + materializers, + observer, + campaign_id, + ) + .instrument(campaign_span) + .await; +} + +async fn execute_decode_replay_campaign( + store: &S, + request: &crate::DecodeReplayRequest, + decoders: &[std::sync::Arc], + materializers: &[std::sync::Arc], + observer: &O, + campaign_id: std::string::String, +) -> kb_core::Result +where + S: kb_store_core::DecodePipelineStore + Sync, + O: crate::DecodeReplayObserver, +{ + let signature_sample = + crate::decode_replay::text_sample(request.selection.signatures.as_slice(), 5); + tracing::debug!( + target: crate::TRACING_TARGET, + action = "execute_campaign", + campaign_id = %campaign_id, + signature_count = request.selection.signatures.len(), + signature_sample = ?signature_sample, + processing_states = ?request.selection.processing_states, + min_slot = ?request.selection.min_slot, + max_slot = ?request.selection.max_slot, + program_ids = ?request.selection.program_ids, + instruction_paths = ?request.selection.instruction_paths, + limit = request.selection.limit, + decoder_names = ?request.decoder_names, + dispatch_policy = ?request.dispatch_policy, + max_concurrent_inputs = request.max_concurrent_inputs, + force_replay = request.force_replay, + force_replay_all_matching = request.force_replay_all_matching, + materialize_after_decode = request.materialize_after_decode, + supplied_decoder_count = decoders.len(), + supplied_materializer_count = materializers.len(), + "received contextual decode replay campaign" + ); + let validation_result = request.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let enabled = crate::decode_replay::enabled_decoders(request, decoders); + if enabled.is_empty() { + tracing::error!(target: crate::TRACING_TARGET, action = "resolve_decoders", campaign_id = %campaign_id, requested_decoder_names = ?request.decoder_names, supplied_decoder_count = decoders.len(), "no contextual decoder is enabled"); + return std::result::Result::Err(kb_core::Error::config( + "decode replay requires at least one enabled decoder", + )); + } + let enabled_decoder_identities = enabled + .iter() + .map(|decoder| { + let identity = decoder.identity(); + return format!("{}@{}", identity.name, identity.version); + }) + .collect::>(); + tracing::debug!(target: crate::TRACING_TARGET, action = "resolve_decoders", campaign_id = %campaign_id, enabled_decoders = ?enabled_decoder_identities, "resolved contextual decode replay decoders"); + let decoder_registry_result = crate::decode_replay::validate_decoder_registry(&enabled); + if let std::result::Result::Err(error) = decoder_registry_result { + return std::result::Result::Err(error); + } + let effective_selection_result = + crate::decode_replay::effective_selection(request, enabled.as_slice()); + let effective_selection = match effective_selection_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + tracing::debug!( + target: crate::TRACING_TARGET, + action = "resolve_selection", + campaign_id = %campaign_id, + requested_program_ids = ?request.selection.program_ids, + effective_program_ids = ?effective_selection.program_ids, + requested_processing_states = ?request.selection.processing_states, + effective_processing_states = ?effective_selection.processing_states, + force_replay = request.force_replay, + explicit_signature_selection = !effective_selection.signatures.is_empty(), + processing_state_filter_bypassed = request.force_replay + && (!effective_selection.signatures.is_empty() || request.force_replay_all_matching), + force_replay_all_matching = request.force_replay_all_matching, + "resolved contextual decode replay selection" + ); + let ordered_materializers_result = crate::decode_replay::ordered_materializers(materializers); + let ordered_materializers = match ordered_materializers_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let ordered_materializer_identities = ordered_materializers + .iter() + .map(|materializer| { + let identity = materializer.identity(); + return format!("{}@{}", identity.name, identity.version); + }) + .collect::>(); + if request.materialize_after_decode && ordered_materializers.is_empty() { + tracing::error!(target: crate::TRACING_TARGET, action = "resolve_materializers", campaign_id = %campaign_id, requested = true, available_count = 0_usize, "materialization requested without available materializer"); + return std::result::Result::Err(kb_core::Error::config( + "decode replay materialization requires at least one available materializer", + )); + } + tracing::debug!(target: crate::TRACING_TARGET, action = "resolve_materializers", campaign_id = %campaign_id, materializers = ?ordered_materializer_identities, "resolved contextual decode replay materializers"); + for decoder in &enabled { + let identity = decoder.identity(); + let declarations = crate::decode_replay::coverage_declaration_inserts(decoder.as_ref()); + tracing::debug!(target: crate::TRACING_TARGET, action = "persist_coverage_declarations", campaign_id = %campaign_id, processor_name = %identity.name, processor_version = %identity.version, declaration_count = declarations.len(), "persist decoder coverage declarations"); + let persist_result = store.persist_decode_coverage_declarations(&declarations).await; + match persist_result { + std::result::Result::Ok(outcome) => { + tracing::debug!(target: crate::TRACING_TARGET, action = "persist_coverage_declarations", campaign_id = %campaign_id, processor_name = %identity.name, processor_version = %identity.version, outcome = ?outcome, "decoder coverage declarations persisted"); + }, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "persist_coverage_declarations", campaign_id = %campaign_id, processor_name = %identity.name, processor_version = %identity.version, error = %error, "decoder coverage declaration persistence failed"); + return std::result::Result::Err(error); + }, + } + } + tracing::debug!(target: crate::TRACING_TARGET, action = "select_inputs", campaign_id = %campaign_id, signature_count = effective_selection.signatures.len(), processing_states = ?effective_selection.processing_states, min_slot = ?effective_selection.min_slot, max_slot = ?effective_selection.max_slot, program_ids = ?effective_selection.program_ids, instruction_paths = ?effective_selection.instruction_paths, limit = effective_selection.limit, "select contextual decode replay inputs"); + let inputs_result = store.list_decode_inputs(&effective_selection).await; + let inputs = match inputs_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "select_inputs", campaign_id = %campaign_id, error = %error, "contextual decode replay input selection failed"); + return std::result::Result::Err(error); + }, + }; + tracing::debug!(target: crate::TRACING_TARGET, action = "select_inputs", campaign_id = %campaign_id, selected_count = inputs.len(), "selected contextual decode replay inputs"); + for input in &inputs { + tracing::debug!( + target: crate::TRACING_TARGET, + action = "selected_input", + campaign_id = %campaign_id, + signature = %input.signature, + slot = input.slot, + instruction_path = %input.instruction_path, + program_id = %input.program_id, + input_key = %input.replay_input_key, + transaction_failed = input.transaction_failed, + surface_code_hint = ?input.surface_code_hint, + payload_hash = ?input.instruction_payload_hash, + "selected contextual decode replay input" + ); + } + let started_at = chrono::Utc::now().to_rfc3339(); + let selected_result = u64::try_from(inputs.len()); + let selected = match selected_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "decode selected count conversion failed: {error}" + ))); + }, + }; + observer.on_progress(&crate::DecodeReplayProgressEvent::new( + crate::DecodeReplayProgressLevel::Info, + format!( + "decode replay campaign {campaign_id} selected {selected} contextual inputs for {} decoders", + enabled.len() + ), + 0, + selected, + )); + let concurrency_result = usize::try_from(request.max_concurrent_inputs); + let concurrency = match concurrency_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::config(format!( + "decode concurrency conversion failed: {error}" + ))); + }, + }; + let enabled_ref = enabled.as_slice(); + let materializers_ref = ordered_materializers.as_slice(); + let campaign_id_ref = campaign_id.as_str(); + let stream = futures_util::stream::iter(inputs.into_iter().map(|input| { + return async move { + let input_span = tracing::debug_span!( + target: crate::TRACING_TARGET, + "decode_replay_input", + campaign_id = %campaign_id_ref, + signature = %input.signature, + slot = input.slot, + instruction_path = %input.instruction_path, + program_id = %input.program_id, + input_key = %input.replay_input_key + ); + return crate::decode_replay::execute_decode_input( + store, + request, + enabled_ref, + materializers_ref, + observer, + campaign_id_ref, + &input, + ) + .instrument(input_span) + .await; + }; + })) + .buffer_unordered(concurrency); + futures_util::pin_mut!(stream); + let mut outcomes = std::vec::Vec::new(); + let mut settled = 0_u64; + while let std::option::Option::Some(outcome) = stream.next().await { + settled += 1; + let level = if outcome.failed { + crate::DecodeReplayProgressLevel::Error + } else if outcome.cancelled { + crate::DecodeReplayProgressLevel::Warning + } else { + crate::DecodeReplayProgressLevel::Debug + }; + observer.on_progress(&crate::DecodeReplayProgressEvent::new( + level, + format!( + "decode replay input terminal signature={} slot={} instruction_path={} program_id={} unmatched={} failed={} cancelled={} processors={:?}", + outcome.signature, + outcome.slot, + outcome.instruction_path, + outcome.program_id, + outcome.unmatched, + outcome.failed, + outcome.cancelled, + outcome.processors + ), + settled, + selected, + )); + outcomes.push(outcome); + } + let mut summary = crate::DecodeReplaySummary { + campaign_id: campaign_id.clone(), + pipeline_version: crate::DECODE_PIPELINE_VERSION.to_string(), + selected, + started: 0, + completed: 0, + unmatched: 0, + not_started: 0, + failed_inputs: 0, + cancelled: observer.is_cancelled(), + processors: std::vec::Vec::new(), + started_at, + finished_at: chrono::Utc::now().to_rfc3339(), + }; + for outcome in outcomes { + if outcome.started { + summary.started += 1; + summary.completed += 1; + } + if outcome.unmatched { + summary.unmatched += 1; + } + if outcome.failed { + summary.failed_inputs += 1; + } + if outcome.cancelled && !outcome.started { + summary.not_started += 1; + } + for processor in outcome.processors { + crate::decode_replay::merge_processor_summary(&mut summary.processors, processor); + } + } + summary.processors.sort_by(|left, right| { + return left + .processor_name + .cmp(&right.processor_name) + .then(left.processor_version.cmp(&right.processor_version)); + }); + if summary.unmatched > 0 || summary.failed_inputs > 0 { + tracing::error!(target: crate::TRACING_TARGET, action = "execute_campaign", campaign_id = %summary.campaign_id, selected = summary.selected, started = summary.started, completed = summary.completed, unmatched = summary.unmatched, not_started = summary.not_started, failed_inputs = summary.failed_inputs, cancelled = summary.cancelled, processors = ?summary.processors, started_at = %summary.started_at, finished_at = %summary.finished_at, "contextual decode replay completed with failures"); + } else if summary.cancelled { + tracing::warn!(target: crate::TRACING_TARGET, action = "execute_campaign", campaign_id = %summary.campaign_id, selected = summary.selected, started = summary.started, completed = summary.completed, unmatched = summary.unmatched, not_started = summary.not_started, failed_inputs = summary.failed_inputs, cancelled = summary.cancelled, processors = ?summary.processors, started_at = %summary.started_at, finished_at = %summary.finished_at, "contextual decode replay cancelled"); + } else { + tracing::info!(target: crate::TRACING_TARGET, action = "execute_campaign", campaign_id = %summary.campaign_id, selected = summary.selected, started = summary.started, completed = summary.completed, unmatched = summary.unmatched, not_started = summary.not_started, failed_inputs = summary.failed_inputs, cancelled = summary.cancelled, processors = ?summary.processors, started_at = %summary.started_at, finished_at = %summary.finished_at, "contextual decode replay completed"); + } + return std::result::Result::Ok(summary); +} + +async fn execute_decode_input( + store: &S, + request: &crate::DecodeReplayRequest, + decoders: &[std::sync::Arc], + materializers: &[std::sync::Arc], + observer: &O, + campaign_id: &str, + input: &kb_store_core::CoreInstructionReplayInput, +) -> crate::decode_replay::DecodeItemOutcome +where + S: kb_store_core::DecodePipelineStore + Sync, + O: crate::DecodeReplayObserver, +{ + tracing::debug!(target: crate::TRACING_TARGET, action = "admit_input", campaign_id = %campaign_id, signature = %input.signature, slot = input.slot, instruction_path = %input.instruction_path, program_id = %input.program_id, input_key = %input.replay_input_key, transaction_failed = input.transaction_failed, "consider contextual decode replay input"); + if observer.is_cancelled() { + tracing::debug!(target: crate::TRACING_TARGET, action = "admit_input", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, admitted = false, reason = "cancelled_before_admission", "contextual decode replay input not admitted"); + return crate::decode_replay::DecodeItemOutcome { + signature: input.signature.clone(), + slot: input.slot, + instruction_path: input.instruction_path.clone(), + program_id: input.program_id.clone(), + started: false, + unmatched: false, + failed: false, + cancelled: true, + processors: std::vec::Vec::new(), + }; + } + let ranked = crate::decode_replay::rank_decoders(campaign_id, input, decoders); + if ranked.is_empty() { + tracing::error!(target: crate::TRACING_TARGET, action = "dispatch_input", campaign_id = %campaign_id, signature = %input.signature, slot = input.slot, instruction_path = %input.instruction_path, program_id = %input.program_id, matched_decoder_count = 0_usize, error_code = "decode_input_unmatched", "no contextual decoder matched selected input"); + return crate::decode_replay::DecodeItemOutcome { + signature: input.signature.clone(), + slot: input.slot, + instruction_path: input.instruction_path.clone(), + program_id: input.program_id.clone(), + started: true, + unmatched: true, + failed: false, + cancelled: false, + processors: std::vec::Vec::new(), + }; + } + let selected = match request.dispatch_policy { + crate::DecodeDispatchPolicy::HighestPriority => ranked.into_iter().take(1).collect(), + crate::DecodeDispatchPolicy::AllCompatible => ranked, + }; + let selected_decoder_identities = selected + .iter() + .map(|ranked_decoder: &crate::decode_replay::RankedDecoder<'_>| { + return format!("{}@{}", ranked_decoder.identity.name, ranked_decoder.identity.version); + }) + .collect::>(); + tracing::debug!(target: crate::TRACING_TARGET, action = "dispatch_input", campaign_id = %campaign_id, signature = %input.signature, slot = input.slot, instruction_path = %input.instruction_path, program_id = %input.program_id, dispatch_policy = ?request.dispatch_policy, selected_decoders = ?selected_decoder_identities, "dispatch contextual decode replay input"); + let mut processors = std::vec::Vec::new(); + let mut failed = false; + for ranked_decoder in selected { + if observer.is_cancelled() { + break; + } + tracing::debug!(target: crate::TRACING_TARGET, action = "invoke_decoder", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, program_id = %input.program_id, processor_name = %ranked_decoder.identity.name, processor_version = %ranked_decoder.identity.version, recognition = ?ranked_decoder.recognition, "invoke contextual instruction decoder"); + let processor_span = tracing::debug_span!( + target: crate::TRACING_TARGET, + "decode_replay_processor", + campaign_id = %campaign_id, + signature = %input.signature, + instruction_path = %input.instruction_path, + program_id = %input.program_id, + processor_name = %ranked_decoder.identity.name, + processor_version = %ranked_decoder.identity.version + ); + let processor_result = crate::decode_replay::execute_decoder( + store, + request, + campaign_id, + &ranked_decoder, + materializers, + input, + ) + .instrument(processor_span) + .await; + match processor_result { + std::result::Result::Ok(value) => { + tracing::debug!(target: crate::TRACING_TARGET, action = "invoke_decoder", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, program_id = %input.program_id, processor_name = %value.processor_name, processor_version = %value.processor_version, summary = ?value, "contextual instruction decoder completed"); + if value.failed > 0 { + failed = true; + } + processors.push(value); + }, + std::result::Result::Err(error) => { + failed = true; + processors.push(crate::DecodeProcessorSummary { + processor_name: ranked_decoder.identity.name.clone(), + processor_version: ranked_decoder.identity.version.clone(), + dispatched: 1, + skipped: 0, + decoded: 0, + ignored: 0, + unsupported: 0, + failed: 1, + materialized_outputs: 0, + materialization_refused: 0, + }); + tracing::error!( + target: crate::TRACING_TARGET, + action = "invoke_decoder", campaign_id = %campaign_id, + signature = %input.signature, + slot = input.slot, + instruction_path = %input.instruction_path, + program_id = %input.program_id, + processor_name = %ranked_decoder.identity.name, + processor_version = %ranked_decoder.identity.version, + input_key = %input.replay_input_key, + error = %error, + "contextual instruction decode failed" + ); + }, + } + } + let cancelled = observer.is_cancelled(); + tracing::debug!(target: crate::TRACING_TARGET, action = "complete_input", campaign_id = %campaign_id, signature = %input.signature, slot = input.slot, instruction_path = %input.instruction_path, program_id = %input.program_id, failed, cancelled, processors = ?processors, "contextual decode replay input completed"); + return crate::decode_replay::DecodeItemOutcome { + signature: input.signature.clone(), + slot: input.slot, + instruction_path: input.instruction_path.clone(), + program_id: input.program_id.clone(), + started: true, + unmatched: false, + failed, + cancelled, + processors, + }; +} + +async fn execute_decoder( + store: &S, + request: &crate::DecodeReplayRequest, + campaign_id: &str, + ranked: &crate::decode_replay::RankedDecoder<'_>, + materializers: &[std::sync::Arc], + input: &kb_store_core::CoreInstructionReplayInput, +) -> kb_core::Result +where + S: kb_store_core::DecodePipelineStore + Sync, +{ + let input_hash_result = kb_decoder_api::contextual_input_hash(input); + let input_hash = match input_hash_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let ledger_result = kb_store_core::ProcessingLedgerIdentity::new( + crate::INSTRUCTION_DECODE_STAGE, + ranked.identity.name.clone(), + ranked.identity.version.clone(), + input.replay_input_key.clone(), + input_hash.clone(), + ); + let ledger_identity = match ledger_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + tracing::debug!( + target: crate::TRACING_TARGET, + action = "execute_decoder", + campaign_id = %campaign_id, + signature = %input.signature, + slot = input.slot, + instruction_path = %input.instruction_path, + program_id = %input.program_id, + input_key = %input.replay_input_key, + input_hash = %input_hash, + processor_name = %ranked.identity.name, + processor_version = %ranked.identity.version, + recognition = ?ranked.recognition, + force_replay = request.force_replay, + materialize_after_decode = request.materialize_after_decode, + "execute contextual instruction decoder" + ); + let mut summary = crate::DecodeProcessorSummary { + processor_name: ranked.identity.name.clone(), + processor_version: ranked.identity.version.clone(), + dispatched: 1, + skipped: 0, + decoded: 0, + ignored: 0, + unsupported: 0, + failed: 0, + materialized_outputs: 0, + materialization_refused: 0, + }; + if !request.force_replay && !request.selection.incomplete_signatures { + tracing::debug!(target: crate::TRACING_TARGET, action = "check_decode_ledger", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, processor_name = %ranked.identity.name, processor_version = %ranked.identity.version, input_key = %ledger_identity.input_key, input_hash = %ledger_identity.input_hash, "check current contextual decode ledger identity"); + let current_result = store.is_decode_current(&ledger_identity).await; + match current_result { + std::result::Result::Ok(true) => { + summary.skipped = 1; + tracing::debug!(target: crate::TRACING_TARGET, action = "skip_decoder", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, program_id = %input.program_id, processor_name = %ranked.identity.name, processor_version = %ranked.identity.version, input_key = %ledger_identity.input_key, input_hash = %ledger_identity.input_hash, reason = "same_processor_version_and_input_hash", "skip current contextual instruction decode"); + return std::result::Result::Ok(summary); + }, + std::result::Result::Ok(false) => { + tracing::debug!(target: crate::TRACING_TARGET, action = "check_decode_ledger", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, processor_name = %ranked.identity.name, processor_version = %ranked.identity.version, current = false, "contextual decode ledger identity is not current"); + }, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } else { + tracing::debug!(target: crate::TRACING_TARGET, action = "check_decode_ledger", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, processor_name = %ranked.identity.name, processor_version = %ranked.identity.version, force_replay = request.force_replay, incomplete_signatures = request.selection.incomplete_signatures, ledger_check_bypassed = true, "selected replay scope bypasses contextual decode skip check"); + } + tracing::debug!(target: crate::TRACING_TARGET, action = "call_decoder", campaign_id = %campaign_id, signature = %input.signature, slot = input.slot, instruction_path = %input.instruction_path, program_id = %input.program_id, processor_name = %ranked.identity.name, processor_version = %ranked.identity.version, transaction_failed = input.transaction_failed, "call contextual instruction decoder implementation"); + let decode_result = ranked.decoder.decode(input); + tracing::debug!(target: crate::TRACING_TARGET, action = "call_decoder", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, processor_name = %ranked.identity.name, processor_version = %ranked.identity.version, status = ?decode_result.status, recognized_entry_code = ?decode_result.recognized_entry_code, observation_count = decode_result.observations.len(), diagnostics = ?decode_result.diagnostics, "contextual instruction decoder returned"); + if matches!( + decode_result.status, + kb_decoder_api::DecoderOutcomeStatus::Failed + | kb_decoder_api::DecoderOutcomeStatus::Unsupported + ) { + tracing::error!( + target: crate::TRACING_TARGET, + action = "decoder_outcome_failure", + campaign_id = %campaign_id, + signature = %input.signature, + slot = input.slot, + instruction_path = %input.instruction_path, + program_id = %input.program_id, + processor_name = %ranked.identity.name, + processor_version = %ranked.identity.version, + input_key = %input.replay_input_key, + status = ?decode_result.status, + recognized_entry_code = ?decode_result.recognized_entry_code, + diagnostics = ?decode_result.diagnostics, + "contextual instruction was not decoded successfully" + ); + } + let validation_result = decode_result.validate(); + if let std::result::Result::Err(error) = validation_result { + tracing::error!(target: crate::TRACING_TARGET, action = "validate_decoder_result", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, processor_name = %ranked.identity.name, processor_version = %ranked.identity.version, error = %error, "contextual decoder returned an invalid result"); + let failure = kb_store_core::DecodeFailure { + ledger_identity, + signature: input.signature.clone(), + instruction_path: input.instruction_path.clone(), + error_code: "invalid_decoder_result".to_string(), + error_message: error.to_string(), + }; + let persist_failure_result = store.mark_decode_failed(&failure).await; + if let std::result::Result::Err(persist_error) = persist_failure_result { + return std::result::Result::Err(persist_error); + } + return std::result::Result::Err(error); + } + let observation_inserts_result = crate::decode_replay::observation_inserts( + &ranked.identity, + input, + input_hash.as_str(), + &decode_result.observations, + ); + let observation_inserts = match observation_inserts_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let status = crate::decode_replay::decoder_status_code(decode_result.status).to_string(); + let decoded_count_result = u32::try_from(decode_result.observations.len()); + let decoded_count = match decoded_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "decoded observation count conversion failed: {error}" + ))); + }, + }; + let error_count_result = u32::try_from(decode_result.diagnostics.len()); + let error_count = match error_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "decoder diagnostic count conversion failed: {error}" + ))); + }, + }; + let coverage = kb_store_core::DecodeCoverageObservationInsert { + processor_name: ranked.identity.name.clone(), + processor_version: ranked.identity.version.clone(), + input_key: input.replay_input_key.clone(), + input_hash: input_hash.clone(), + signature: input.signature.clone(), + slot: input.slot, + instruction_path: input.instruction_path.clone(), + program_id: input.program_id.clone(), + surface_code: ranked.recognition.surface_code.clone(), + entry_code: decode_result + .recognized_entry_code + .clone() + .or_else(|| return ranked.recognition.entry_code.clone()), + discriminator_hex: ranked.recognition.discriminator_hex.clone(), + status: status.clone(), + recognized: ranked.recognition.compatible, + decoded_count, + materialized_count: 0, + error_count, + transaction_failed: input.transaction_failed, + }; + let decode_error = if decode_result.status == kb_decoder_api::DecoderOutcomeStatus::Failed { + decode_result.diagnostics.first() + } else { + std::option::Option::None + }; + let bundle = kb_store_core::DecodePersistenceBundle { + ledger_identity, + signature: input.signature.clone(), + instruction_path: input.instruction_path.clone(), + status: status.clone(), + error_code: decode_error.map(|diagnostic| return diagnostic.code.clone()), + error_message: decode_error.map(|diagnostic| return diagnostic.message.clone()), + observations: observation_inserts, + coverage, + }; + tracing::debug!(target: crate::TRACING_TARGET, action = "persist_decode_result", campaign_id = %campaign_id, signature = %input.signature, slot = input.slot, instruction_path = %input.instruction_path, program_id = %input.program_id, processor_name = %ranked.identity.name, processor_version = %ranked.identity.version, input_key = %bundle.ledger_identity.input_key, input_hash = %bundle.ledger_identity.input_hash, status = %bundle.status, observation_count = bundle.observations.len(), force_replay = request.force_replay, "persist contextual decode result"); + let persist_result = store.persist_decode_result(&bundle, request.force_replay).await; + let persist_outcome = match persist_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "persist_decode_result", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, processor_name = %ranked.identity.name, processor_version = %ranked.identity.version, error = %error, "contextual decode result persistence failed"); + return std::result::Result::Err(error); + }, + }; + tracing::debug!(target: crate::TRACING_TARGET, action = "persist_decode_result", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, processor_name = %ranked.identity.name, processor_version = %ranked.identity.version, outcome = ?persist_outcome, "contextual decode result persisted"); + match decode_result.status { + kb_decoder_api::DecoderOutcomeStatus::Decoded => summary.decoded = 1, + kb_decoder_api::DecoderOutcomeStatus::Ignored => summary.ignored = 1, + kb_decoder_api::DecoderOutcomeStatus::Unsupported => summary.unsupported = 1, + kb_decoder_api::DecoderOutcomeStatus::Failed => summary.failed = 1, + } + if request.materialize_after_decode + && decode_result.status == kb_decoder_api::DecoderOutcomeStatus::Decoded + { + tracing::debug!(target: crate::TRACING_TARGET, action = "materialize_after_decode", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, processor_name = %ranked.identity.name, processor_version = %ranked.identity.version, observation_count = decode_result.observations.len(), materializer_count = materializers.len(), "run materializers after contextual decode"); + for observation in &decode_result.observations { + let materialization_result = crate::decode_replay::execute_materializers( + store, + request.force_replay, + campaign_id, + &ranked.identity, + input, + observation, + materializers, + ) + .await; + let materialization = match materialization_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + summary.materialized_outputs += materialization.0; + summary.materialization_refused += materialization.1; + } + } else { + tracing::debug!(target: crate::TRACING_TARGET, action = "materialize_after_decode", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, processor_name = %ranked.identity.name, processor_version = %ranked.identity.version, requested = request.materialize_after_decode, decode_status = ?decode_result.status, executed = false, "materialization not executed for contextual decode result"); + } + tracing::debug!(target: crate::TRACING_TARGET, action = "execute_decoder", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, processor_name = %ranked.identity.name, processor_version = %ranked.identity.version, summary = ?summary, "contextual instruction decoder execution completed"); + return std::result::Result::Ok(summary); +} + +async fn execute_materializers( + store: &S, + force_replay: bool, + campaign_id: &str, + decoder_identity: &kb_decoder_api::DecoderIdentity, + input: &kb_store_core::CoreInstructionReplayInput, + observation: &kb_decoder_api::DecodedObservation, + materializers: &[std::sync::Arc], +) -> kb_core::Result<(u64, u64)> +where + S: kb_store_core::DecodePipelineStore + Sync, +{ + tracing::debug!(target: crate::TRACING_TARGET, action = "execute_materializers", campaign_id = %campaign_id, signature = %input.signature, slot = input.slot, instruction_path = %input.instruction_path, program_id = %input.program_id, source_decoder_name = %decoder_identity.name, source_decoder_version = %decoder_identity.version, event_key = %observation.event_key, event_family = ?observation.event.event_family, materializer_count = materializers.len(), force_replay, "evaluate materializers for decoded observation"); + let mut output_count = 0_u64; + let mut refusal_count = 0_u64; + for materializer in materializers { + let identity = materializer.identity(); + if !kb_materializer_api::materializer_accepts_observation( + materializer.as_ref(), + observation, + ) { + tracing::debug!(target: crate::TRACING_TARGET, action = "select_materializer", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, event_key = %observation.event_key, materializer_name = %identity.name, materializer_version = %identity.version, accepted = false, event_family = ?observation.event.event_family, surface_code = %observation.event.surface_code.0.as_str(), entry_code = %observation.event.event_name.0.as_str(), "materializer does not accept decoded observation"); + continue; + } + tracing::debug!(target: crate::TRACING_TARGET, action = "select_materializer", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, event_key = %observation.event_key, materializer_name = %identity.name, materializer_version = %identity.version, accepted = true, event_family = ?observation.event.event_family, surface_code = %observation.event.surface_code.0.as_str(), entry_code = %observation.event.event_name.0.as_str(), "materializer accepts decoded observation"); + let materializer_input_key = crate::decode_replay::materializer_input_key( + input, + decoder_identity, + observation.event_key.as_str(), + ); + let serialize_result = serde_json::to_value(observation); + let mut observation_json = match serialize_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot serialize decoded observation for materialization hash: {error}" + ))); + }, + }; + let hash_result = kb_decoder_api::deterministic_json_hash(&mut observation_json); + let input_hash = match hash_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let ledger_result = kb_store_core::ProcessingLedgerIdentity::new( + crate::EVENT_MATERIALIZATION_STAGE, + identity.name.clone(), + identity.version.clone(), + materializer_input_key.clone(), + input_hash.clone(), + ); + let ledger_identity = match ledger_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if !force_replay { + tracing::debug!(target: crate::TRACING_TARGET, action = "check_materialization_ledger", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, event_key = %observation.event_key, materializer_name = %identity.name, materializer_version = %identity.version, input_key = %ledger_identity.input_key, input_hash = %ledger_identity.input_hash, "check current materialization ledger identity"); + let current_result = store.is_decode_current(&ledger_identity).await; + match current_result { + std::result::Result::Ok(true) => { + tracing::debug!(target: crate::TRACING_TARGET, action = "skip_materializer", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, event_key = %observation.event_key, materializer_name = %identity.name, materializer_version = %identity.version, reason = "same_processor_version_and_input_hash", "skip current materialization"); + continue; + }, + std::result::Result::Ok(false) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } else { + tracing::debug!(target: crate::TRACING_TARGET, action = "check_materialization_ledger", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, event_key = %observation.event_key, materializer_name = %identity.name, materializer_version = %identity.version, force_replay = true, ledger_check_bypassed = true, "force replay bypasses materialization skip check"); + } + let policy_result = kb_materializer_api::validate_materialization_policy( + materializer.as_ref(), + observation, + ); + let mut execution = match policy_result { + std::result::Result::Ok(()) => materializer.materialize(observation), + std::result::Result::Err(refusal) => refusal, + }; + let output_policy_result = + kb_materializer_api::validate_materialized_output_policy(observation, &execution); + if let std::result::Result::Err(refusal) = output_policy_result { + execution = refusal; + } + if execution.status == kb_materializer_api::MaterializerOutcomeStatus::Refused { + refusal_count += 1; + } + tracing::debug!(target: crate::TRACING_TARGET, action = "call_materializer", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, event_key = %observation.event_key, materializer_name = %identity.name, materializer_version = %identity.version, status = ?execution.status, output_count = execution.outputs.len(), diagnostics = ?execution.diagnostics, "materializer returned"); + if execution.status == kb_materializer_api::MaterializerOutcomeStatus::Failed { + tracing::error!( + target: crate::TRACING_TARGET, + action = "materializer_outcome_failure", + campaign_id = %campaign_id, + signature = %input.signature, + slot = input.slot, + instruction_path = %input.instruction_path, + program_id = %input.program_id, + event_key = %observation.event_key, + materializer_name = %identity.name, + materializer_version = %identity.version, + surface_code = %observation.event.surface_code.0.as_str(), + entry_code = %observation.event.event_name.0.as_str(), + diagnostics = ?execution.diagnostics, + "decoded observation materialization failed" + ); + } + let validation_result = execution.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let mut outputs = std::vec::Vec::with_capacity(execution.outputs.len()); + for output in &execution.outputs { + outputs.push(kb_store_core::MaterializedOutputInsert { + processor_name: identity.name.clone(), + processor_version: identity.version.clone(), + input_key: materializer_input_key.clone(), + input_hash: input_hash.clone(), + output_key: output.output_key.clone(), + source_event_key: observation.event_key.clone(), + signature: input.signature.clone(), + slot: input.slot, + materialized_family: crate::decode_replay::materialized_family_code(output.family) + .to_string(), + payload_json: output.payload_json.clone(), + }); + } + let materialization_error = + if execution.status == kb_materializer_api::MaterializerOutcomeStatus::Failed { + execution.diagnostics.first() + } else { + std::option::Option::None + }; + let bundle = kb_store_core::MaterializationPersistenceBundle { + ledger_identity, + source_decoder_name: decoder_identity.name.clone(), + source_decoder_version: decoder_identity.version.clone(), + source_decode_input_key: input.replay_input_key.clone(), + signature: input.signature.clone(), + instruction_path: input.instruction_path.clone(), + status: crate::decode_replay::materializer_status_code(execution.status).to_string(), + error_code: materialization_error.map(|diagnostic| return diagnostic.code.clone()), + error_message: materialization_error + .map(|diagnostic| return diagnostic.message.clone()), + outputs, + }; + tracing::debug!(target: crate::TRACING_TARGET, action = "persist_materialization_result", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, event_key = %observation.event_key, materializer_name = %identity.name, materializer_version = %identity.version, input_key = %bundle.ledger_identity.input_key, input_hash = %bundle.ledger_identity.input_hash, status = %bundle.status, output_count = bundle.outputs.len(), force_replay, "persist materialization result"); + let persist_result = store.persist_materialization_result(&bundle, force_replay).await; + let persist_outcome = match persist_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "persist_materialization_result", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, event_key = %observation.event_key, materializer_name = %identity.name, materializer_version = %identity.version, error = %error, "materialization result persistence failed"); + return std::result::Result::Err(error); + }, + }; + tracing::debug!(target: crate::TRACING_TARGET, action = "persist_materialization_result", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, event_key = %observation.event_key, materializer_name = %identity.name, materializer_version = %identity.version, outcome = ?persist_outcome, "materialization result persisted"); + let count_result = u64::try_from(execution.outputs.len()); + let count = match count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "materialized output count conversion failed: {error}" + ))); + }, + }; + output_count += count; + } + tracing::debug!(target: crate::TRACING_TARGET, action = "execute_materializers", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, event_key = %observation.event_key, output_count, refusal_count, "materializer evaluation completed"); + return std::result::Result::Ok((output_count, refusal_count)); +} + +fn materializer_input_key( + input: &kb_store_core::CoreInstructionReplayInput, + decoder_identity: &kb_decoder_api::DecoderIdentity, + event_key: &str, +) -> std::string::String { + return format!( + "{}:{}:{}:{}", + input.replay_input_key, decoder_identity.name, decoder_identity.version, event_key + ); +} + +fn validate_decoder_registry( + decoders: &[std::sync::Arc], +) -> kb_core::Result<()> { + let mut identities = std::collections::BTreeSet::new(); + for decoder in decoders { + let identity = decoder.identity(); + if identity.name.trim().is_empty() || identity.version.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "decoder registry contains an empty processor identity", + )); + } + if !identities.insert((identity.name, identity.version)) { + return std::result::Result::Err(kb_core::Error::config( + "decoder registry contains a duplicate processor identity", + )); + } + } + return std::result::Result::Ok(()); +} + +fn ordered_materializers( + materializers: &[std::sync::Arc], +) -> kb_core::Result>> { + let mut output = materializers.to_vec(); + output.sort_by(|left, right| { + let left_identity = left.identity(); + let right_identity = right.identity(); + return left_identity + .name + .cmp(&right_identity.name) + .then(left_identity.version.cmp(&right_identity.version)); + }); + let mut previous = std::option::Option::None; + for materializer in &output { + let identity = materializer.identity(); + if identity.name.trim().is_empty() || identity.version.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "materializer registry contains an empty processor identity", + )); + } + let key = (identity.name, identity.version); + if previous.as_ref() == std::option::Option::Some(&key) { + return std::result::Result::Err(kb_core::Error::config( + "materializer registry contains a duplicate processor identity", + )); + } + previous = std::option::Option::Some(key); + } + return std::result::Result::Ok(output); +} + +/// Creates one stable process-local contextual decode campaign identifier. +pub fn new_decode_campaign_id() -> std::string::String { + let sequence = crate::decode_replay::DECODE_CAMPAIGN_SEQUENCE + .fetch_add(1, std::sync::atomic::Ordering::Relaxed); + return format!("decode-{}-{sequence}", chrono::Utc::now().timestamp_micros()); +} + +fn text_sample(values: &[std::string::String], limit: usize) -> std::vec::Vec<&str> { + return values.iter().take(limit).map(std::string::String::as_str).collect(); +} + +fn effective_selection( + request: &crate::DecodeReplayRequest, + decoders: &[std::sync::Arc], +) -> kb_core::Result { + let mut supported_program_ids = std::collections::BTreeSet::::new(); + for decoder in decoders { + for surface in decoder.surfaces() { + supported_program_ids.insert(surface.program_id.to_string()); + } + } + if supported_program_ids.is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "enabled decoders declare no exact program ids", + )); + } + let effective_program_ids = if request.selection.program_ids.is_empty() { + supported_program_ids.iter().cloned().collect::>() + } else { + let mut requested = std::collections::BTreeSet::::new(); + let mut unsupported = std::vec::Vec::::new(); + for program_id in &request.selection.program_ids { + if supported_program_ids.contains(program_id) { + requested.insert(program_id.clone()); + } else { + unsupported.push(program_id.clone()); + } + } + if !unsupported.is_empty() { + unsupported.sort(); + return std::result::Result::Err(kb_core::Error::config(format!( + "selected program ids are not supported by enabled decoders: {}", + unsupported.join(", ") + ))); + } + requested.into_iter().collect::>() + }; + let bypass_processing_states = request.force_replay + && (!request.selection.signatures.is_empty() || request.force_replay_all_matching); + let processing_states = if bypass_processing_states { + std::vec::Vec::new() + } else { + request.selection.processing_states.clone() + }; + return kb_store_core::DecodeSelectionFilter::new( + request.selection.signatures.clone(), + processing_states, + request.selection.min_slot, + request.selection.max_slot, + effective_program_ids, + request.selection.instruction_paths.clone(), + request.selection.incomplete_signatures, + request.selection.limit, + ); +} + +fn enabled_decoders( + request: &crate::DecodeReplayRequest, + decoders: &[std::sync::Arc], +) -> std::vec::Vec> { + let mut output = decoders + .iter() + .filter(|decoder| { + if request.decoder_names.is_empty() { + return true; + } + let identity = decoder.identity(); + return request.decoder_names.iter().any(|name| return *name == identity.name); + }) + .cloned() + .collect::>(); + output.sort_by(|left, right| { + let left_identity = left.identity(); + let right_identity = right.identity(); + return left_identity + .name + .cmp(&right_identity.name) + .then(left_identity.version.cmp(&right_identity.version)); + }); + return output; +} + +fn rank_decoders<'decoder>( + campaign_id: &str, + input: &kb_store_core::CoreInstructionReplayInput, + decoders: &'decoder [std::sync::Arc], +) -> std::vec::Vec> { + let mut output = std::vec::Vec::new(); + for decoder in decoders { + let identity = decoder.identity(); + if !kb_decoder_api::decoder_handles_program_id(decoder.as_ref(), input.program_id.as_str()) + { + tracing::debug!(target: crate::TRACING_TARGET, action = "recognize_input", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, program_id = %input.program_id, processor_name = %identity.name, processor_version = %identity.version, handles_program_id = false, recognize_called = false, "decoder rejected by exact program id before recognition"); + continue; + } + tracing::debug!(target: crate::TRACING_TARGET, action = "recognize_input", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, program_id = %input.program_id, processor_name = %identity.name, processor_version = %identity.version, handles_program_id = true, recognize_called = true, "call decoder recognition"); + let recognition = decoder.recognize(input); + if !recognition.compatible { + tracing::debug!(target: crate::TRACING_TARGET, action = "recognize_input", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, program_id = %input.program_id, processor_name = %identity.name, processor_version = %identity.version, recognition = ?recognition, compatible = false, "decoder recognition rejected contextual input"); + continue; + } + tracing::debug!(target: crate::TRACING_TARGET, action = "recognize_input", campaign_id = %campaign_id, signature = %input.signature, instruction_path = %input.instruction_path, program_id = %input.program_id, processor_name = %identity.name, processor_version = %identity.version, recognition = ?recognition, compatible = true, "decoder recognition accepted contextual input"); + output.push(crate::decode_replay::RankedDecoder { + decoder: decoder.as_ref(), + identity, + recognition, + }); + } + output.sort_by(|left, right| { + return right + .recognition + .exact + .cmp(&left.recognition.exact) + .then(right.recognition.priority.cmp(&left.recognition.priority)) + .then(left.identity.name.cmp(&right.identity.name)) + .then(left.identity.version.cmp(&right.identity.version)); + }); + return output; +} + +fn coverage_declaration_inserts( + decoder: &dyn kb_decoder_api::InstructionDecoder, +) -> std::vec::Vec { + let identity = decoder.identity(); + return decoder + .coverage() + .into_iter() + .map(|entry| { + return kb_store_core::DecodeCoverageDeclarationInsert { + processor_name: identity.name.clone(), + processor_version: identity.version.clone(), + program_id: entry.program_id, + surface_code: entry.surface_code, + entry_kind: crate::decode_replay::coverage_entry_kind_code(entry.entry_kind) + .to_string(), + entry_code: entry.entry_code, + discriminator_hex: entry.discriminator_hex, + historical: entry.historical, + }; + }) + .collect(); +} + +fn observation_inserts( + identity: &kb_decoder_api::DecoderIdentity, + input: &kb_store_core::CoreInstructionReplayInput, + input_hash: &str, + observations: &[kb_decoder_api::DecodedObservation], +) -> kb_core::Result> { + let mut output = std::vec::Vec::with_capacity(observations.len()); + for observation in observations { + if observation.event.signature.0 != input.signature + || observation.event.slot.0 != input.slot + || observation.event.instruction_path.0 != input.instruction_path + || observation.event.program_id.0 != input.program_id + || observation.transaction_failed != input.transaction_failed + || observation.transaction_error != input.transaction_err_json + { + return std::result::Result::Err(kb_core::Error::invalid_state( + "decoded observation context does not match contextual input", + )); + } + let proof_result = serde_json::to_value(&observation.proof); + let proof_json = match proof_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot serialize decoder proof: {error}" + ))); + }, + }; + output.push(kb_store_core::DecodeObservationInsert { + processor_name: identity.name.clone(), + processor_version: identity.version.clone(), + input_key: input.replay_input_key.clone(), + input_hash: input_hash.to_string(), + event_key: observation.event_key.clone(), + signature: observation.event.signature.0.clone(), + slot: observation.event.slot.0, + instruction_path: observation.event.instruction_path.0.clone(), + program_id: observation.event.program_id.0.clone(), + protocol_code: observation.event.protocol_code.0.clone(), + surface_code: observation.event.surface_code.0.clone(), + event_code: observation.event.event_code.0.clone(), + event_name: observation.event.event_name.0.clone(), + event_family: crate::decode_replay::event_family_code(observation.event.event_family) + .to_string(), + source_kind: crate::decode_replay::event_source_kind_code( + observation.event.source_kind, + ) + .to_string(), + confidence: crate::decode_replay::decoder_confidence_code(observation.event.confidence) + .to_string(), + proof_kind: crate::decode_replay::proof_kind_code(observation.proof.kind).to_string(), + proof_json, + payload_json: observation.payload_json.clone(), + transaction_failed: observation.transaction_failed, + transaction_error: observation.transaction_error.clone(), + observation_committed: observation.observation_committed, + }); + } + return std::result::Result::Ok(output); +} + +fn merge_processor_summary( + summaries: &mut std::vec::Vec, + value: crate::DecodeProcessorSummary, +) { + for summary in summaries.iter_mut() { + if summary.processor_name == value.processor_name + && summary.processor_version == value.processor_version + { + summary.dispatched += value.dispatched; + summary.skipped += value.skipped; + summary.decoded += value.decoded; + summary.ignored += value.ignored; + summary.unsupported += value.unsupported; + summary.failed += value.failed; + summary.materialized_outputs += value.materialized_outputs; + summary.materialization_refused += value.materialization_refused; + return; + } + } + summaries.push(value); +} + +fn decoder_status_code(status: kb_decoder_api::DecoderOutcomeStatus) -> &'static str { + return match status { + kb_decoder_api::DecoderOutcomeStatus::Decoded => "decoded", + kb_decoder_api::DecoderOutcomeStatus::Ignored => "ignored", + kb_decoder_api::DecoderOutcomeStatus::Unsupported => "unsupported", + kb_decoder_api::DecoderOutcomeStatus::Failed => "failed", + }; +} + +fn materializer_status_code( + status: kb_materializer_api::MaterializerOutcomeStatus, +) -> &'static str { + return match status { + kb_materializer_api::MaterializerOutcomeStatus::Inserted => "inserted", + kb_materializer_api::MaterializerOutcomeStatus::Replaced => "replaced", + kb_materializer_api::MaterializerOutcomeStatus::Ignored => "ignored", + kb_materializer_api::MaterializerOutcomeStatus::Refused => "refused", + kb_materializer_api::MaterializerOutcomeStatus::Failed => "failed", + }; +} + +fn coverage_entry_kind_code(kind: kb_decoder_api::DecoderCoverageEntryKind) -> &'static str { + return match kind { + kb_decoder_api::DecoderCoverageEntryKind::Instruction => "instruction", + kb_decoder_api::DecoderCoverageEntryKind::Event => "event", + kb_decoder_api::DecoderCoverageEntryKind::Discriminator => "discriminator", + }; +} + +fn proof_kind_code(kind: kb_decoder_api::DecoderProofKind) -> &'static str { + return match kind { + kb_decoder_api::DecoderProofKind::ExactDiscriminator => "exact_discriminator", + kb_decoder_api::DecoderProofKind::ExactLayout => "exact_layout", + kb_decoder_api::DecoderProofKind::Idl => "idl", + kb_decoder_api::DecoderProofKind::Manual => "manual", + kb_decoder_api::DecoderProofKind::LogCorrelation => "log_correlation", + kb_decoder_api::DecoderProofKind::BalanceDelta => "balance_delta", + kb_decoder_api::DecoderProofKind::Heuristic => "heuristic", + kb_decoder_api::DecoderProofKind::Audit => "audit", + kb_decoder_api::DecoderProofKind::Unknown => "unknown", + }; +} + +fn decoder_confidence_code(confidence: kb_model::DecoderConfidence) -> &'static str { + return match confidence { + kb_model::DecoderConfidence::Exact => "exact", + kb_model::DecoderConfidence::IdlExact => "idl_exact", + kb_model::DecoderConfidence::ManualExact => "manual_exact", + kb_model::DecoderConfidence::Inferred => "inferred", + kb_model::DecoderConfidence::Unsafe => "unsafe", + kb_model::DecoderConfidence::AuditOnly => "audit_only", + kb_model::DecoderConfidence::Unknown => "unknown", + }; +} + +fn event_source_kind_code(kind: kb_model::EventSourceKind) -> &'static str { + return match kind { + kb_model::EventSourceKind::Instruction => "instruction", + kb_model::EventSourceKind::InnerInstruction => "inner_instruction", + kb_model::EventSourceKind::Log => "log", + kb_model::EventSourceKind::AnchorEvent => "anchor_event", + kb_model::EventSourceKind::AnchorSelfCpiEvent => "anchor_self_cpi_event", + kb_model::EventSourceKind::BalanceDelta => "balance_delta", + kb_model::EventSourceKind::Synthetic => "synthetic", + kb_model::EventSourceKind::Inferred => "inferred", + kb_model::EventSourceKind::Audit => "audit", + }; +} + +fn event_family_code(family: kb_model::EventFamily) -> &'static str { + return match family { + kb_model::EventFamily::Trade => "trade", + kb_model::EventFamily::Liquidity => "liquidity", + kb_model::EventFamily::Lifecycle => "lifecycle", + kb_model::EventFamily::Fee => "fee", + kb_model::EventFamily::Admin => "admin", + kb_model::EventFamily::Reward => "reward", + kb_model::EventFamily::Orderbook => "orderbook", + kb_model::EventFamily::TokenAccount => "token_account", + kb_model::EventFamily::TokenMint => "token_mint", + kb_model::EventFamily::TokenBurn => "token_burn", + kb_model::EventFamily::Nft => "nft", + kb_model::EventFamily::Metadata => "metadata", + kb_model::EventFamily::Oracle => "oracle", + kb_model::EventFamily::Lending => "lending", + kb_model::EventFamily::Staking => "staking", + kb_model::EventFamily::Governance => "governance", + kb_model::EventFamily::Bridge => "bridge", + kb_model::EventFamily::Perpetuals => "perpetuals", + kb_model::EventFamily::Vault => "vault", + kb_model::EventFamily::Routing => "routing", + kb_model::EventFamily::ComplianceAudit => "compliance_audit", + kb_model::EventFamily::TokenMetadataRisk => "token_metadata_risk", + kb_model::EventFamily::Risk => "risk", + kb_model::EventFamily::Audit => "audit", + kb_model::EventFamily::Unknown => "unknown", + }; +} + +fn materialized_family_code(family: kb_model::MaterializedEventFamily) -> &'static str { + return match family { + kb_model::MaterializedEventFamily::Trade => "trade", + kb_model::MaterializedEventFamily::Liquidity => "liquidity", + kb_model::MaterializedEventFamily::Lifecycle => "lifecycle", + kb_model::MaterializedEventFamily::Fee => "fee", + kb_model::MaterializedEventFamily::Admin => "admin", + kb_model::MaterializedEventFamily::TokenAccount => "token_account", + kb_model::MaterializedEventFamily::PoolState => "pool_state", + kb_model::MaterializedEventFamily::Orderbook => "orderbook", + kb_model::MaterializedEventFamily::Reward => "reward", + kb_model::MaterializedEventFamily::Nft => "nft", + kb_model::MaterializedEventFamily::Metadata => "metadata", + kb_model::MaterializedEventFamily::Oracle => "oracle", + kb_model::MaterializedEventFamily::Lending => "lending", + kb_model::MaterializedEventFamily::Staking => "staking", + kb_model::MaterializedEventFamily::Governance => "governance", + kb_model::MaterializedEventFamily::Bridge => "bridge", + kb_model::MaterializedEventFamily::Perpetuals => "perpetuals", + kb_model::MaterializedEventFamily::Vault => "vault", + kb_model::MaterializedEventFamily::Routing => "routing", + kb_model::MaterializedEventFamily::ComplianceAudit => "compliance_audit", + kb_model::MaterializedEventFamily::TokenMetadataRisk => "token_metadata_risk", + kb_model::MaterializedEventFamily::Risk => "risk", + kb_model::MaterializedEventFamily::TransactionAnnotation => "transaction_annotation", + kb_model::MaterializedEventFamily::Unknown => "unknown", + }; +} + +#[cfg(test)] +mod tests { + fn lock_or_panic(mutex: &std::sync::Mutex) -> std::sync::MutexGuard<'_, T> { + return match mutex.lock() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("test mutex is poisoned: {error}"), + }; + } + + fn result_or_panic(result: std::result::Result) -> T + where + E: std::fmt::Display, + { + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("test operation failed: {error}"), + }; + } + + static SURFACES: &[kb_decoder_api::DecoderSurface] = &[kb_decoder_api::DecoderSurface { + program_id: "program_a", + surface_code: "surface_a", + priority: 10, + }]; + + struct TestDecoder { + version: &'static str, + exact: bool, + priority: u16, + } + + impl kb_decoder_api::InstructionDecoder for TestDecoder { + fn identity(&self) -> kb_decoder_api::DecoderIdentity { + return kb_decoder_api::DecoderIdentity { + name: "test_decoder".to_string(), + version: self.version.to_string(), + }; + } + + fn surfaces(&self) -> &'static [kb_decoder_api::DecoderSurface] { + return SURFACES; + } + + fn coverage(&self) -> std::vec::Vec { + return std::vec![kb_decoder_api::DecoderCoverageDeclaration { + program_id: "program_a".to_string(), + surface_code: std::option::Option::Some("surface_a".to_string()), + entry_kind: kb_decoder_api::DecoderCoverageEntryKind::Instruction, + entry_code: "attempt".to_string(), + discriminator_hex: std::option::Option::None, + historical: false, + }]; + } + + fn recognize( + &self, + input: &kb_store_core::CoreInstructionReplayInput, + ) -> kb_decoder_api::DecoderRecognition { + if input.program_id != "program_a" { + return kb_decoder_api::DecoderRecognition::incompatible(); + } + return kb_decoder_api::DecoderRecognition::compatible( + self.exact, + self.priority, + std::option::Option::Some("surface_a".to_string()), + std::option::Option::Some("attempt".to_string()), + kb_decoder_api::discriminator_8_hex(input), + ); + } + + fn decode( + &self, + input: &kb_store_core::CoreInstructionReplayInput, + ) -> kb_decoder_api::DecoderExecutionResult { + let observation = kb_decoder_api::DecodedObservation { + event_key: "attempt".to_string(), + event: kb_model::DecodedProtocolEvent { + signature: kb_model::Signature(input.signature.clone()), + slot: kb_model::Slot(input.slot), + instruction_path: kb_model::InstructionPath(input.instruction_path.clone()), + program_id: kb_model::ProgramId(input.program_id.clone()), + protocol_code: kb_model::ProtocolCode("test".to_string()), + surface_code: kb_model::SurfaceCode("surface_a".to_string()), + event_code: kb_model::EventCode("attempt".to_string()), + event_name: kb_model::EventName("attempt".to_string()), + event_family: kb_model::EventFamily::Audit, + source_kind: kb_model::EventSourceKind::Instruction, + confidence: kb_model::DecoderConfidence::Exact, + }, + payload_json: serde_json::json!({"attempted": true}), + transaction_failed: input.transaction_failed, + transaction_error: input.transaction_err_json.clone(), + observation_committed: !input.transaction_failed, + proof: kb_decoder_api::DecoderProof { + kind: kb_decoder_api::DecoderProofKind::ExactLayout, + confidence: kb_model::DecoderConfidence::Exact, + evidence: std::vec!["test".to_string()], + }, + }; + return kb_decoder_api::DecoderExecutionResult { + status: kb_decoder_api::DecoderOutcomeStatus::Decoded, + recognized_entry_code: std::option::Option::Some("attempt".to_string()), + observations: std::vec![observation], + diagnostics: std::vec::Vec::new(), + }; + } + } + + struct SelectiveAuditMaterializer; + + impl kb_materializer_api::EventMaterializer for SelectiveAuditMaterializer { + fn identity(&self) -> kb_materializer_api::MaterializerIdentity { + return kb_materializer_api::MaterializerIdentity { + name: "selective_audit_materializer".to_string(), + version: "1".to_string(), + }; + } + + fn accepted_families(&self) -> &'static [kb_model::EventFamily] { + return &[kb_model::EventFamily::Audit]; + } + + fn accepts_observation(&self, observation: &kb_decoder_api::DecodedObservation) -> bool { + return observation.event.surface_code.0.as_str() == "accepted_surface"; + } + + fn transaction_policy( + &self, + _family: kb_model::EventFamily, + ) -> kb_materializer_api::MaterializationTransactionPolicy { + return kb_materializer_api::MaterializationTransactionPolicy::SuccessfulCommittedOnly; + } + + fn materialize( + &self, + _observation: &kb_decoder_api::DecodedObservation, + ) -> kb_materializer_api::MaterializerExecutionResult { + return kb_materializer_api::MaterializerExecutionResult { + status: kb_materializer_api::MaterializerOutcomeStatus::Failed, + outputs: std::vec::Vec::new(), + diagnostics: std::vec![kb_materializer_api::MaterializerDiagnostic { + code: "unexpected_materializer_call".to_string(), + message: + "selective materializer must not be called for an unaccepted observation" + .to_string(), + retriable: false, + }], + }; + } + } + + struct TestObserver { + cancelled: std::sync::atomic::AtomicBool, + } + + impl crate::DecodeReplayObserver for TestObserver { + fn on_progress(&self, _event: &crate::DecodeReplayProgressEvent) {} + + fn is_cancelled(&self) -> bool { + return self.cancelled.load(std::sync::atomic::Ordering::SeqCst); + } + } + + #[derive(Default)] + struct TestStore { + inputs: std::sync::Mutex>, + filters: std::sync::Mutex>, + current: std::sync::Mutex>, + decode_bundles: std::sync::Mutex>, + materialization_bundles: + std::sync::Mutex>, + declarations: + std::sync::Mutex>, + } + + #[async_trait::async_trait] + impl kb_store_core::DecodePipelineStore for TestStore { + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn list_decode_inputs( + &self, + filter: &kb_store_core::DecodeSelectionFilter, + ) -> kb_core::Result> { + crate::decode_replay::tests::lock_or_panic(&self.filters).push(filter.clone()); + let limit_result = usize::try_from(filter.limit); + let limit = match limit_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => usize::MAX, + }; + let inputs = crate::decode_replay::tests::lock_or_panic(&self.inputs) + .iter() + .filter(|input| { + return (filter.signatures.is_empty() + || filter.signatures.contains(&input.signature)) + && (filter.program_ids.is_empty() + || filter.program_ids.contains(&input.program_id)) + && (filter.instruction_paths.is_empty() + || filter.instruction_paths.contains(&input.instruction_path)); + }) + .take(limit) + .cloned() + .collect::>(); + return std::result::Result::Ok(inputs); + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn is_decode_current( + &self, + identity: &kb_store_core::ProcessingLedgerIdentity, + ) -> kb_core::Result { + let key = format!( + "{}:{}:{}:{}:{}", + identity.stage, + identity.processor_name, + identity.processor_version, + identity.input_key, + identity.input_hash + ); + return std::result::Result::Ok( + crate::decode_replay::tests::lock_or_panic(&self.current).contains(&key), + ); + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn persist_decode_coverage_declarations( + &self, + declarations: &[kb_store_core::DecodeCoverageDeclarationInsert], + ) -> kb_core::Result { + crate::decode_replay::tests::lock_or_panic(&self.declarations) + .extend_from_slice(declarations); + return std::result::Result::Ok(kb_store_core::InsertOutcome::new( + declarations.len() as u64, + 0, + 0, + )); + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn persist_decode_result( + &self, + bundle: &kb_store_core::DecodePersistenceBundle, + force_replay: bool, + ) -> kb_core::Result { + let key = format!( + "{}:{}:{}:{}:{}", + bundle.ledger_identity.stage, + bundle.ledger_identity.processor_name, + bundle.ledger_identity.processor_version, + bundle.ledger_identity.input_key, + bundle.ledger_identity.input_hash + ); + crate::decode_replay::tests::lock_or_panic(&self.current).insert(key); + let mut bundles = crate::decode_replay::tests::lock_or_panic(&self.decode_bundles); + if force_replay { + bundles.retain(|existing| { + return existing.ledger_identity.processor_name + != bundle.ledger_identity.processor_name + || existing.ledger_identity.processor_version + != bundle.ledger_identity.processor_version + || existing.ledger_identity.input_key != bundle.ledger_identity.input_key; + }); + } + bundles.push(bundle.clone()); + return std::result::Result::Ok(kb_store_core::InsertOutcome::new(1, 1, 0)); + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn mark_decode_failed( + &self, + _failure: &kb_store_core::DecodeFailure, + ) -> kb_core::Result { + return std::result::Result::Ok(kb_store_core::InsertOutcome::new(0, 1, 0)); + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn persist_materialization_result( + &self, + bundle: &kb_store_core::MaterializationPersistenceBundle, + force_replay: bool, + ) -> kb_core::Result { + let key = format!( + "{}:{}:{}:{}:{}", + bundle.ledger_identity.stage, + bundle.ledger_identity.processor_name, + bundle.ledger_identity.processor_version, + bundle.ledger_identity.input_key, + bundle.ledger_identity.input_hash + ); + crate::decode_replay::tests::lock_or_panic(&self.current).insert(key); + let mut bundles = + crate::decode_replay::tests::lock_or_panic(&self.materialization_bundles); + if force_replay { + bundles.retain(|existing| { + return existing.ledger_identity.processor_name + != bundle.ledger_identity.processor_name + || existing.ledger_identity.processor_version + != bundle.ledger_identity.processor_version + || existing.ledger_identity.input_key != bundle.ledger_identity.input_key; + }); + } + bundles.push(bundle.clone()); + let count_result = u64::try_from(bundle.outputs.len()); + let count = match count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "test materialization output count conversion failed: {error}" + ))); + }, + }; + return std::result::Result::Ok(kb_store_core::InsertOutcome::new(count, 1, 0)); + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn list_decode_coverage_summary( + &self, + _processor_name: std::option::Option<&str>, + _processor_version: std::option::Option<&str>, + _limit: u32, + ) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn list_materialized_events( + &self, + _filter: &kb_store_core::MaterializedEventFilter, + ) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } + } + + fn replay_input(program_id: &str, failed: bool) -> kb_store_core::CoreInstructionReplayInput { + let result = kb_store_core::CoreInstructionReplayInput::new( + "signature:0", + "signature", + 42, + "0", + program_id, + failed, + if failed { + std::option::Option::Some(serde_json::json!({"InstructionError": [0, "Custom"]})) + } else { + std::option::Option::None + }, + serde_json::json!([]), + serde_json::json!([]), + std::option::Option::Some(serde_json::json!({"dataBase64": "AQIDBAUGBwg="})), + std::option::Option::Some("payload-hash".to_string()), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return crate::decode_replay::tests::result_or_panic(result); + } + + fn request(force_replay: bool) -> crate::DecodeReplayRequest { + return crate::DecodeReplayRequest { + campaign_id: "decode-test-campaign".to_string(), + selection: crate::decode_replay::tests::result_or_panic( + kb_store_core::DecodeSelectionFilter::actionable(10), + ), + decoder_names: std::vec::Vec::new(), + dispatch_policy: crate::DecodeDispatchPolicy::HighestPriority, + max_concurrent_inputs: 2, + force_replay, + force_replay_all_matching: force_replay, + materialize_after_decode: false, + }; + } + + fn memo_replay_input(failed: bool) -> kb_store_core::CoreInstructionReplayInput { + let result = kb_store_core::CoreInstructionReplayInput::new( + "memo-signature:0", + "memo-signature", + 84, + "0", + kb_program_ids::SPL_MEMO_V4_PROGRAM_ID, + failed, + if failed { + std::option::Option::Some(serde_json::json!({ + "InstructionError": [0, "InvalidInstructionData"] + })) + } else { + std::option::Option::None + }, + serde_json::json!([]), + serde_json::json!([]), + std::option::Option::Some(serde_json::json!({ + "dataBase64": "ZGVtbyBhbm5vdGF0aW9u" + })), + std::option::Option::Some("memo-payload-hash".to_string()), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + return crate::decode_replay::tests::result_or_panic(result); + } + + #[test] + fn dispatch_is_exact_by_program_and_rank() { + let decoders: std::vec::Vec> = std::vec![ + std::sync::Arc::new(TestDecoder { + version: "1", + exact: false, + priority: 100 + }), + std::sync::Arc::new(TestDecoder { version: "2", exact: true, priority: 1 }), + ]; + let ranked = crate::decode_replay::rank_decoders( + "test-campaign", + &replay_input("program_a", false), + &decoders, + ); + assert_eq!(ranked.len(), 2); + assert_eq!(ranked[0].identity.version, "2"); + assert!( + crate::decode_replay::rank_decoders( + "test-campaign", + &replay_input("unknown", false), + &decoders + ) + .is_empty() + ); + } + + #[test] + fn decoded_observation_context_must_match_core_input() { + let input = replay_input("program_a", true); + let decoder = TestDecoder { version: "1", exact: true, priority: 1 }; + let mut execution = kb_decoder_api::InstructionDecoder::decode(&decoder, &input); + execution.observations[0].event.slot = kb_model::Slot(input.slot + 1); + let identity = kb_decoder_api::InstructionDecoder::identity(&decoder); + let result = crate::decode_replay::observation_inserts( + &identity, + &input, + "input-hash", + &execution.observations, + ); + assert!(result.is_err()); + } + + #[test] + fn duplicate_decoder_identity_is_rejected() { + let decoders: std::vec::Vec> = std::vec![ + std::sync::Arc::new(TestDecoder { version: "1", exact: true, priority: 1 }), + std::sync::Arc::new(TestDecoder { version: "1", exact: false, priority: 2 }), + ]; + let result = crate::decode_replay::validate_decoder_registry(&decoders); + assert!(result.is_err()); + } + + #[test] + fn materializer_input_key_changes_with_decoder_version() { + let input = replay_input("program_a", false); + let version_one = kb_decoder_api::DecoderIdentity { + name: "test_decoder".to_string(), + version: "1".to_string(), + }; + let version_two = kb_decoder_api::DecoderIdentity { + name: "test_decoder".to_string(), + version: "2".to_string(), + }; + let first = crate::decode_replay::materializer_input_key(&input, &version_one, "attempt"); + let second = crate::decode_replay::materializer_input_key(&input, &version_two, "attempt"); + assert_ne!(first, second); + } + + #[test] + fn effective_selection_uses_enabled_decoder_programs() { + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(TestDecoder { version: "1", exact: true, priority: 1 })]; + let selection = crate::decode_replay::tests::result_or_panic( + crate::decode_replay::effective_selection(&request(false), decoders.as_slice()), + ); + assert_eq!(selection.program_ids, std::vec!["program_a".to_string()]); + assert_eq!(selection.processing_states.len(), 3); + } + + #[test] + fn force_replay_with_explicit_signatures_bypasses_state_filter() { + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(TestDecoder { version: "1", exact: true, priority: 1 })]; + let mut replay_request = request(true); + replay_request.force_replay_all_matching = false; + replay_request.selection.signatures = std::vec!["signature".to_string()]; + let selection = crate::decode_replay::tests::result_or_panic( + crate::decode_replay::effective_selection(&replay_request, decoders.as_slice()), + ); + assert!(selection.processing_states.is_empty()); + } + + #[test] + fn force_replay_without_explicit_scope_is_rejected() { + let mut replay_request = request(false); + replay_request.force_replay = true; + let result = replay_request.validate(); + assert!(result.is_err()); + } + + #[test] + fn all_matching_authorization_rejects_explicit_signatures() { + let mut replay_request = crate::decode_replay::tests::request(true); + replay_request.selection.signatures = std::vec!["signature".to_string()]; + let result = replay_request.validate(); + assert!(result.is_err()); + } + + #[test] + fn force_replay_all_matching_bypasses_state_filter() { + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(TestDecoder { version: "1", exact: true, priority: 1 })]; + let replay_request = request(true); + let selection = crate::decode_replay::tests::result_or_panic( + crate::decode_replay::effective_selection(&replay_request, decoders.as_slice()), + ); + assert!(selection.processing_states.is_empty()); + } + + #[tokio::test] + async fn observation_level_materializer_filter_avoids_false_refusals() { + let store = TestStore::default(); + crate::decode_replay::tests::lock_or_panic(&store.inputs) + .push(replay_input("program_a", false)); + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(TestDecoder { version: "1", exact: true, priority: 1 })]; + let materializers: std::vec::Vec< + std::sync::Arc, + > = std::vec![std::sync::Arc::new(SelectiveAuditMaterializer)]; + let observer = TestObserver { + cancelled: std::sync::atomic::AtomicBool::new(false), + }; + let mut replay_request = request(false); + replay_request.materialize_after_decode = true; + let summary = crate::decode_replay::tests::result_or_panic( + crate::execute_decode_replay( + &store, + &replay_request, + &decoders, + &materializers, + &observer, + ) + .await, + ); + assert_eq!(summary.processors[0].decoded, 1); + assert_eq!(summary.processors[0].materialized_outputs, 0); + assert_eq!(summary.processors[0].materialization_refused, 0); + } + + #[tokio::test] + async fn materialization_without_materializer_is_rejected() { + let store = TestStore::default(); + crate::decode_replay::tests::lock_or_panic(&store.inputs) + .push(replay_input("program_a", false)); + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(TestDecoder { version: "1", exact: true, priority: 1 })]; + let observer = TestObserver { + cancelled: std::sync::atomic::AtomicBool::new(false), + }; + let mut replay_request = request(false); + replay_request.materialize_after_decode = true; + let result = + crate::execute_decode_replay(&store, &replay_request, &decoders, &[], &observer).await; + assert!(result.is_err()); + } + + #[test] + fn explicit_unsupported_program_is_rejected_before_selection() { + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(TestDecoder { version: "1", exact: true, priority: 1 })]; + let mut replay_request = request(false); + replay_request.selection.program_ids = std::vec!["unrelated".to_string()]; + let result = + crate::decode_replay::effective_selection(&replay_request, decoders.as_slice()); + assert!(result.is_err()); + } + + #[tokio::test] + async fn automatic_program_scope_prevents_unrelated_unmatched_inputs() { + let store = TestStore::default(); + { + let mut inputs = crate::decode_replay::tests::lock_or_panic(&store.inputs); + inputs.push(replay_input("program_a", false)); + inputs.push(replay_input("unrelated", false)); + } + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(TestDecoder { version: "1", exact: true, priority: 1 })]; + let observer = TestObserver { + cancelled: std::sync::atomic::AtomicBool::new(false), + }; + let summary = crate::decode_replay::tests::result_or_panic( + crate::execute_decode_replay(&store, &request(false), &decoders, &[], &observer).await, + ); + assert_eq!(summary.selected, 1); + assert_eq!(summary.unmatched, 0); + assert_eq!(summary.processors[0].decoded, 1); + } + + #[tokio::test] + async fn same_version_and_hash_is_skipped() { + let store = TestStore::default(); + crate::decode_replay::tests::lock_or_panic(&store.inputs) + .push(replay_input("program_a", false)); + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(TestDecoder { version: "1", exact: true, priority: 1 })]; + let observer = TestObserver { + cancelled: std::sync::atomic::AtomicBool::new(false), + }; + let first = crate::decode_replay::tests::result_or_panic( + crate::execute_decode_replay(&store, &request(false), &decoders, &[], &observer).await, + ); + let second = crate::decode_replay::tests::result_or_panic( + crate::execute_decode_replay(&store, &request(false), &decoders, &[], &observer).await, + ); + assert_eq!(first.processors[0].decoded, 1); + assert_eq!(second.processors[0].skipped, 1); + } + + #[tokio::test] + async fn version_change_replays_and_force_replaces_target_version_only() { + let store = TestStore::default(); + crate::decode_replay::tests::lock_or_panic(&store.inputs) + .push(replay_input("program_a", false)); + let observer = TestObserver { + cancelled: std::sync::atomic::AtomicBool::new(false), + }; + let version_one: std::vec::Vec> = + std::vec![std::sync::Arc::new(TestDecoder { version: "1", exact: true, priority: 1 })]; + let version_two: std::vec::Vec> = + std::vec![std::sync::Arc::new(TestDecoder { version: "2", exact: true, priority: 1 })]; + crate::decode_replay::tests::result_or_panic( + crate::execute_decode_replay(&store, &request(false), &version_one, &[], &observer) + .await, + ); + crate::decode_replay::tests::result_or_panic( + crate::execute_decode_replay(&store, &request(false), &version_two, &[], &observer) + .await, + ); + crate::decode_replay::tests::result_or_panic( + crate::execute_decode_replay(&store, &request(true), &version_two, &[], &observer) + .await, + ); + let bundles = crate::decode_replay::tests::lock_or_panic(&store.decode_bundles); + assert_eq!(bundles.len(), 2); + assert!( + bundles + .iter() + .any(|bundle| return bundle.ledger_identity.processor_version == "1") + ); + assert!( + bundles + .iter() + .any(|bundle| return bundle.ledger_identity.processor_version == "2") + ); + } + + #[tokio::test] + async fn failed_transaction_observation_is_persisted_uncommitted() { + let store = TestStore::default(); + crate::decode_replay::tests::lock_or_panic(&store.inputs) + .push(replay_input("program_a", true)); + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(TestDecoder { version: "1", exact: true, priority: 1 })]; + let observer = TestObserver { + cancelled: std::sync::atomic::AtomicBool::new(false), + }; + crate::decode_replay::tests::result_or_panic( + crate::execute_decode_replay(&store, &request(false), &decoders, &[], &observer).await, + ); + let bundles = crate::decode_replay::tests::lock_or_panic(&store.decode_bundles); + assert!(bundles[0].observations[0].transaction_failed); + assert!(!bundles[0].observations[0].observation_committed); + } + + #[tokio::test] + async fn concrete_memo_replay_materializes_only_committed_annotations_idempotently() { + let store = TestStore::default(); + crate::decode_replay::tests::lock_or_panic(&store.inputs).push(memo_replay_input(false)); + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(kb_decoder_spl_memo::SplMemoDecoder)]; + let materializers: std::vec::Vec< + std::sync::Arc, + > = std::vec![std::sync::Arc::new( + kb_materializer_transaction_annotations::TransactionAnnotationMaterializer, + )]; + let observer = TestObserver { + cancelled: std::sync::atomic::AtomicBool::new(false), + }; + let mut replay_request = request(false); + replay_request.materialize_after_decode = true; + let first = crate::decode_replay::tests::result_or_panic( + crate::execute_decode_replay( + &store, + &replay_request, + &decoders, + &materializers, + &observer, + ) + .await, + ); + let second = crate::decode_replay::tests::result_or_panic( + crate::execute_decode_replay( + &store, + &replay_request, + &decoders, + &materializers, + &observer, + ) + .await, + ); + assert_eq!(first.processors[0].decoded, 1); + assert_eq!(first.processors[0].materialized_outputs, 1); + assert_eq!(second.processors[0].skipped, 1); + { + let bundles = + crate::decode_replay::tests::lock_or_panic(&store.materialization_bundles); + assert_eq!(bundles.len(), 1); + assert_eq!(bundles[0].outputs.len(), 1); + assert_eq!(bundles[0].outputs[0].materialized_family, "transaction_annotation"); + assert_eq!(bundles[0].outputs[0].payload_json["text"], "demo annotation"); + } + + let failed_store = TestStore::default(); + crate::decode_replay::tests::lock_or_panic(&failed_store.inputs) + .push(memo_replay_input(true)); + let failed = crate::decode_replay::tests::result_or_panic( + crate::execute_decode_replay( + &failed_store, + &replay_request, + &decoders, + &materializers, + &observer, + ) + .await, + ); + assert_eq!(failed.processors[0].decoded, 1); + assert_eq!(failed.processors[0].materialized_outputs, 0); + assert_eq!(failed.processors[0].materialization_refused, 1); + } + + #[tokio::test] + async fn declared_and_observed_coverage_are_recorded() { + let store = TestStore::default(); + crate::decode_replay::tests::lock_or_panic(&store.inputs) + .push(replay_input("program_a", false)); + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(TestDecoder { version: "1", exact: true, priority: 1 })]; + let observer = TestObserver { + cancelled: std::sync::atomic::AtomicBool::new(false), + }; + crate::decode_replay::tests::result_or_panic( + crate::execute_decode_replay(&store, &request(false), &decoders, &[], &observer).await, + ); + let declarations = crate::decode_replay::tests::lock_or_panic(&store.declarations); + assert_eq!(declarations.len(), 1); + assert_eq!(declarations[0].entry_code, "attempt"); + let bundles = crate::decode_replay::tests::lock_or_panic(&store.decode_bundles); + assert_eq!(bundles.len(), 1); + assert_eq!(bundles[0].coverage.entry_code.as_deref(), std::option::Option::Some("attempt")); + assert!(bundles[0].coverage.recognized); + assert_eq!(bundles[0].coverage.decoded_count, 1); + assert_eq!(bundles[0].coverage.status, "decoded"); + } + + #[tokio::test] + async fn cancellation_leaves_candidates_not_started() { + let store = TestStore::default(); + crate::decode_replay::tests::lock_or_panic(&store.inputs) + .push(replay_input("program_a", false)); + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(TestDecoder { version: "1", exact: true, priority: 1 })]; + let observer = TestObserver { + cancelled: std::sync::atomic::AtomicBool::new(true), + }; + let summary = crate::decode_replay::tests::result_or_panic( + crate::execute_decode_replay(&store, &request(false), &decoders, &[], &observer).await, + ); + assert_eq!(summary.selected, 1); + assert_eq!(summary.not_started, 1); + assert_eq!(summary.started, 0); + assert_eq!(summary.completed, 0); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/lib.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/lib.rs new file mode 100644 index 0000000..6b6a28a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/lib.rs @@ -0,0 +1,323 @@ +// file: kb_pipeline/src/lib.rs +// version: 29 + +//! Pipeline orchestration primitives. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod backfill; +mod constants; +mod core_extraction; +mod decode_replay; +mod plan; +mod solana_ata_execution; +mod solana_ata_stateful; +mod solana_elgamal_registry_stateful; +mod solana_execution; +mod solana_memo_execution; +mod solana_stateful; +mod solana_token_2022_correlation; +mod solana_token_2022_crypto_preflight; +mod solana_token_2022_devnet_execution; +mod solana_token_2022_devnet_scenarios; +mod solana_token_2022_execution_orchestration; +mod solana_token_2022_preflight; +mod solana_token_2022_proof_orchestration; +mod solana_token_2022_stateful; +mod solana_token_2022_validation; +mod solana_token_execution; +mod solana_token_lifecycle; +mod solana_token_stateful; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Address category used by targeted history backfills. +pub use crate::backfill::BackfillAddressKind; +/// Chronological direction relative to an anchor signature. +pub use crate::backfill::BackfillDirection; +/// Application progress and cancellation observer. +pub use crate::backfill::BackfillObserver; +/// Operator-visible backfill progress event. +pub use crate::backfill::BackfillProgressEvent; +/// Backfill progress severity. +pub use crate::backfill::BackfillProgressLevel; +/// Complete bounded backfill request. +pub use crate::backfill::BackfillRequest; +/// Candidate source used by one campaign. +pub use crate::backfill::BackfillSource; +/// Final backfill counters and pagination information. +pub use crate::backfill::BackfillSummary; +/// Executes one bounded HTTP backfill. +pub use crate::backfill::execute_http_backfill; +/// Stable canonical to core processor name. +pub use crate::core_extraction::CORE_EXTRACTION_PROCESSOR_NAME; +/// Current canonical to core extractor version. +pub use crate::core_extraction::CORE_EXTRACTION_PROCESSOR_VERSION; +/// Stable canonical to core processing stage. +pub use crate::core_extraction::CORE_EXTRACTION_STAGE; +/// Core extraction progress and cancellation observer. +pub use crate::core_extraction::CoreExtractionObserver; +/// Operator-visible core extraction progress event. +pub use crate::core_extraction::CoreExtractionProgressEvent; +/// Core extraction progress severity. +pub use crate::core_extraction::CoreExtractionProgressLevel; +/// Complete bounded core extraction request. +pub use crate::core_extraction::CoreExtractionRequest; +/// Core extraction source selection. +pub use crate::core_extraction::CoreExtractionSource; +/// Final core extraction counters. +pub use crate::core_extraction::CoreExtractionSummary; +/// Executes one bounded canonical transaction to core extraction campaign. +pub use crate::core_extraction::execute_core_extraction; +/// Extracts one canonical raw transaction into a complete core write bundle. +pub use crate::core_extraction::extract_raw_transaction_to_core; +/// Current common decode pipeline orchestration version. +pub use crate::decode_replay::DECODE_PIPELINE_VERSION; +/// Deterministic policy used when several decoders recognize one input. +pub use crate::decode_replay::DecodeDispatchPolicy; +/// Aggregated terminal counters for one decoder version. +pub use crate::decode_replay::DecodeProcessorSummary; +/// Progress and cooperative cancellation contract implemented by applications. +pub use crate::decode_replay::DecodeReplayObserver; +/// One operator-visible decode replay progress event. +pub use crate::decode_replay::DecodeReplayProgressEvent; +/// Decode replay progress severity. +pub use crate::decode_replay::DecodeReplayProgressLevel; +/// Complete bounded contextual decode replay request. +pub use crate::decode_replay::DecodeReplayRequest; +/// Final counters for one bounded contextual decode replay campaign. +pub use crate::decode_replay::DecodeReplaySummary; +/// Stable processing ledger stage used by decoded event materializers. +pub use crate::decode_replay::EVENT_MATERIALIZATION_STAGE; +/// Stable processing ledger stage used by contextual instruction decoders. +pub use crate::decode_replay::INSTRUCTION_DECODE_STAGE; +/// Executes one bounded contextual instruction decode and optional materialization campaign. +pub use crate::decode_replay::execute_decode_replay; +/// Creates one stable process-local contextual decode campaign identifier. +pub use crate::decode_replay::new_decode_campaign_id; +/// Pipeline stage identifier. +pub use crate::plan::PipelineStage; +/// Replay selection scope. +pub use crate::plan::ReplayScope; +/// Complete request for one Devnet ATA execution. +pub use crate::solana_ata_execution::DevnetSplAssociatedTokenAccountExecutionRequest; +/// Complete result of one Devnet ATA execution. +pub use crate::solana_ata_execution::DevnetSplAssociatedTokenAccountExecutionSummary; +/// Executes one Devnet ATA simulation or explicitly authorized submission. +pub use crate::solana_ata_execution::execute_devnet_spl_associated_token_account; +/// Simulates one Devnet ATA operation after stateful preflight. +pub use crate::solana_ata_execution::simulate_devnet_spl_associated_token_account; +/// Stateful ATA invariants observed after a confirmed execution. +pub use crate::solana_ata_stateful::SplAssociatedTokenAccountPostExecutionReport; +/// One ATA stateful readiness check. +pub use crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulCheck; +/// One ATA stateful readiness fact. +pub use crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulFact; +/// Complete ATA stateful readiness report. +pub use crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessReport; +/// Complete ATA stateful readiness request. +pub use crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessRequest; +/// ATA stateful readiness outcome. +pub use crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessStatus; +/// Verifies final ATA account relationships after a confirmed execution. +pub use crate::solana_ata_stateful::inspect_spl_associated_token_account_post_execution; +/// Inspects Localnet or Devnet ATA state required before simulation. +pub use crate::solana_ata_stateful::inspect_spl_associated_token_account_stateful_readiness; +/// Exact byte length of one SPL ElGamal registry account. +pub use crate::solana_elgamal_registry_stateful::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES; +/// One bounded SPL ElGamal registry RPC read request. +pub use crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadRequest; +/// One bounded registry RPC read and validated administrative projection. +pub use crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadResult; +/// One contextually validated SPL ElGamal registry state snapshot. +pub use crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulSnapshot; +/// Validates one complete RPC registry response before parsing and routing. +pub use crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result; +/// Parses, validates the official owner-derived PDA, and materializes one registry snapshot. +pub use crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot; +/// Reads, validates, and routes one exact SPL ElGamal registry account. +pub use crate::solana_elgamal_registry_stateful::read_elgamal_registry_stateful_snapshot; +/// Complete request for one bounded Devnet System transfer. +pub use crate::solana_execution::DevnetSystemTransferRequest; +/// Complete result of one Devnet System transfer and post-validation. +pub use crate::solana_execution::DevnetSystemTransferSummary; +/// No-op observer for execution orchestration. +pub use crate::solana_execution::NoopSolanaExecutionObserver; +/// Composite execution, backfill, extraction and decode observer. +pub use crate::solana_execution::SolanaExecutionObserver; +/// One operator-visible execution progress event. +pub use crate::solana_execution::SolanaExecutionProgressEvent; +/// Execution progress severity. +pub use crate::solana_execution::SolanaExecutionProgressLevel; +/// Executes one bounded Devnet System transfer and canonical replay validation. +pub use crate::solana_execution::execute_devnet_system_transfer; +/// Complete request for one SPL Memo v4 Devnet execution. +pub use crate::solana_memo_execution::DevnetMemoExecutionRequest; +/// Complete result of one SPL Memo v4 Devnet execution and post-validation. +pub use crate::solana_memo_execution::DevnetMemoExecutionSummary; +/// Executes one SPL Memo v4 Devnet simulation or explicitly authorized submission. +pub use crate::solana_memo_execution::execute_devnet_memo; +/// One machine-readable stateful readiness check. +pub use crate::solana_stateful::SolanaCoreStatefulCheck; +/// One contextual fact measured during stateful readiness inspection. +pub use crate::solana_stateful::SolanaCoreStatefulFact; +/// Complete stateful readiness report produced before simulation. +pub use crate::solana_stateful::SolanaCoreStatefulReadinessReport; +/// Complete request for one native Solana stateful readiness inspection. +pub use crate::solana_stateful::SolanaCoreStatefulReadinessRequest; +/// Stateful readiness outcome for one native Solana operation. +pub use crate::solana_stateful::SolanaCoreStatefulReadinessStatus; +/// Inspects Localnet or Devnet state required before simulation. +pub use crate::solana_stateful::inspect_solana_core_stateful_readiness; +/// Correlation outcome between one committed instruction fact and one final state snapshot. +pub use crate::solana_token_2022_correlation::Token2022CorrelationStatus; +/// Deterministic correlation report for one instruction output and one final snapshot. +pub use crate::solana_token_2022_correlation::Token2022StateCorrelationReport; +/// Correlates one materialized instruction fact with one authoritative Token-2022 snapshot. +pub use crate::solana_token_2022_correlation::correlate_token_2022_instruction_with_snapshot; +/// Maximum proof context-state accounts accepted by one Token-2022 cryptographic preflight. +pub use crate::solana_token_2022_crypto_preflight::MAX_TOKEN_2022_PROOF_CONTEXTS; +/// Complete Token-2022 cryptographic preflight report. +pub use crate::solana_token_2022_crypto_preflight::Token2022CryptographicPreflightReport; +/// One bounded Token-2022 cryptographic preflight request. +pub use crate::solana_token_2022_crypto_preflight::Token2022CryptographicPreflightRequest; +/// One validated proof context-state result. +pub use crate::solana_token_2022_crypto_preflight::Token2022ProofContextReport; +/// One exact pre-verified proof context-state requirement. +pub use crate::solana_token_2022_crypto_preflight::Token2022ProofContextRequirement; +/// Generic metadata bytes retained by every ZK proof context-state account. +pub use crate::solana_token_2022_crypto_preflight::ZK_PROOF_CONTEXT_META_BYTES; +/// Reads and validates pre-verified proof context-state accounts before simulation. +pub use crate::solana_token_2022_crypto_preflight::inspect_token_2022_cryptographic_preflight; +/// Complete request for one public Devnet Token-2022 execution. +pub use crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionRequest; +/// Complete result of one public Devnet Token-2022 execution. +pub use crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionSummary; +/// Executes one public Devnet Token-2022 simulation or authorized submission. +pub use crate::solana_token_2022_devnet_execution::execute_devnet_spl_token_2022; +/// Simulates one public Devnet Token-2022 operation. +pub use crate::solana_token_2022_devnet_execution::simulate_devnet_spl_token_2022; +/// Stable family of one Devnet SPL validation scenario. +pub use crate::solana_token_2022_devnet_scenarios::DevnetSplValidationFamily; +/// Current implementation status of one Devnet SPL validation scenario. +pub use crate::solana_token_2022_devnet_scenarios::DevnetSplValidationImplementationStatus; +/// One independent Devnet validation scenario required by milestone 0.4.6. +pub use crate::solana_token_2022_devnet_scenarios::DevnetSplValidationScenario; +/// Returns the ordered Devnet SPL scenario inventory required by milestone 0.4.6. +pub use crate::solana_token_2022_devnet_scenarios::devnet_spl_validation_scenarios; +/// Maximum distinct signers accepted by one Token-2022 execution envelope. +pub use crate::solana_token_2022_execution_orchestration::MAX_TOKEN_2022_EXECUTION_SIGNERS; +/// One explicit stateful postcondition retained after confirmation. +pub use crate::solana_token_2022_execution_orchestration::Token2022ExecutionPostcondition; +/// Explicit result of one Token-2022 stateful postcondition. +pub use crate::solana_token_2022_execution_orchestration::Token2022ExecutionPostconditionStatus; +/// Deterministic report authorizing or refusing Token-2022 signing and submission. +pub use crate::solana_token_2022_execution_orchestration::Token2022ExecutionReadinessReport; +/// Complete request checked before Token-2022 transaction signing. +pub use crate::solana_token_2022_execution_orchestration::Token2022ExecutionReadinessRequest; +/// Aggregates explicit postconditions without inventing success. +pub use crate::solana_token_2022_execution_orchestration::summarize_token_2022_postconditions; +/// Validates exact-message simulation, preflights, signers, and submission policy. +pub use crate::solana_token_2022_execution_orchestration::validate_token_2022_execution_readiness; +/// Maximum distinct Token-2022 accounts accepted by one preflight inspection. +pub use crate::solana_token_2022_preflight::MAX_TOKEN_2022_PREFLIGHT_ACCOUNTS; +/// Maximum aggregate account-data budget accepted by one preflight inspection. +pub use crate::solana_token_2022_preflight::MAX_TOKEN_2022_PREFLIGHT_TOTAL_BYTES; +/// One validated account result in a Token-2022 preflight report. +pub use crate::solana_token_2022_preflight::Token2022PreflightAccountReport; +/// Complete bounded Token-2022 stateful preflight report. +pub use crate::solana_token_2022_preflight::Token2022PreflightReport; +/// One bounded Token-2022 preflight request. +pub use crate::solana_token_2022_preflight::Token2022PreflightRequest; +/// One exact Token-2022 account requirement for a stateful preflight. +pub use crate::solana_token_2022_preflight::Token2022PreflightRequirement; +/// Inspects all bounded Token-2022 state required before simulation. +pub use crate::solana_token_2022_preflight::inspect_token_2022_preflight; +/// Maximum proof references accepted by one confidential Token-2022 operation. +pub use crate::solana_token_2022_proof_orchestration::MAX_TOKEN_2022_OPERATION_PROOFS; +/// One deterministic mixed-proof orchestration report. +pub use crate::solana_token_2022_proof_orchestration::Token2022ProofOrchestrationReport; +/// One bounded mixed-proof orchestration request. +pub use crate::solana_token_2022_proof_orchestration::Token2022ProofOrchestrationRequest; +/// Validates mixed inline/context-state proofs before transaction assembly. +pub use crate::solana_token_2022_proof_orchestration::orchestrate_token_2022_proofs; +/// Maximum complete Token-2022 account data accepted by one bounded RPC read. +pub use crate::solana_token_2022_stateful::MAX_TOKEN_2022_STATEFUL_ACCOUNT_BYTES; +/// Optional external identities required to validate cross-account Token-2022 state. +pub use crate::solana_token_2022_stateful::Token2022StatefulContext; +/// One bounded Token-2022 account read request. +pub use crate::solana_token_2022_stateful::Token2022StatefulReadRequest; +/// One bounded RPC read and its contextually validated projections. +pub use crate::solana_token_2022_stateful::Token2022StatefulReadResult; +/// One contextually validated Token-2022 state snapshot and its owned projections. +pub use crate::solana_token_2022_stateful::Token2022StatefulSnapshotBundle; +/// Contextually validates and materializes one already parsed Token-2022 account snapshot. +pub use crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot; +/// Contextually validates one parsed Token-2022 snapshot with external cross-account identities. +pub use crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot_with_context; +/// Validates one complete RPC account response before Token-2022 parsing and routing. +pub use crate::solana_token_2022_stateful::materialize_token_2022_account_info_result; +/// Parses, contextually validates, and materializes one bounded Token-2022 account snapshot. +pub use crate::solana_token_2022_stateful::materialize_token_2022_stateful_snapshot; +/// Reads, validates, parses, and routes one bounded Token-2022 account snapshot. +pub use crate::solana_token_2022_stateful::read_token_2022_stateful_snapshot; +/// Maximum evidence records retained per Token-2022 validation scenario. +pub use crate::solana_token_2022_validation::MAX_TOKEN_2022_VALIDATION_EVIDENCE; +/// Required environment for one Token-2022 validation scenario. +pub use crate::solana_token_2022_validation::Token2022ValidationEnvironment; +/// One bounded validation evidence record. +pub use crate::solana_token_2022_validation::Token2022ValidationEvidence; +/// Canonical machine-readable Token-2022 validation matrix. +pub use crate::solana_token_2022_validation::Token2022ValidationMatrix; +/// One scenario declared in the canonical Token-2022 validation matrix. +pub use crate::solana_token_2022_validation::Token2022ValidationMatrixScenario; +/// Complete Token-2022 milestone validation report. +pub use crate::solana_token_2022_validation::Token2022ValidationReport; +/// One declared Token-2022 validation scenario. +pub use crate::solana_token_2022_validation::Token2022ValidationScenario; +/// Exact observed status of one Token-2022 validation scenario. +pub use crate::solana_token_2022_validation::Token2022ValidationStatus; +/// Loads and validates the canonical Token-2022 validation matrix. +pub use crate::solana_token_2022_validation::load_token_2022_validation_matrix; +/// Validates the canonical Token-2022 validation matrix contract and evidence. +pub use crate::solana_token_2022_validation::validate_token_2022_validation_matrix; +/// Validates a bounded Token-2022 milestone evidence report. +pub use crate::solana_token_2022_validation::validate_token_2022_validation_report; +/// Complete request for one Devnet classic SPL Token execution. +pub use crate::solana_token_execution::DevnetSplTokenExecutionRequest; +/// Complete result of one Devnet classic SPL Token execution. +pub use crate::solana_token_execution::DevnetSplTokenExecutionSummary; +/// Executes one Devnet classic SPL Token simulation or authorized submission. +pub use crate::solana_token_execution::execute_devnet_spl_token; +/// Simulates one Devnet classic SPL Token operation after stateful preflight. +pub use crate::solana_token_execution::simulate_devnet_spl_token; +/// Explicit request to prepare fresh raw accounts for one SPL Token Devnet lifecycle. +pub use crate::solana_token_lifecycle::DevnetSplTokenLifecyclePreparationRequest; +/// One confirmed raw account creation retained by lifecycle preparation. +pub use crate::solana_token_lifecycle::DevnetSplTokenLifecyclePreparationStep; +/// Complete fresh-account preparation result for one SPL Token Devnet lifecycle. +pub use crate::solana_token_lifecycle::DevnetSplTokenLifecyclePreparationSummary; +/// Request for one destructive, explicitly authorized SPL Token Devnet lifecycle. +pub use crate::solana_token_lifecycle::DevnetSplTokenLifecycleRequest; +/// Result retained for one fully post-validated lifecycle transaction. +pub use crate::solana_token_lifecycle::DevnetSplTokenLifecycleStepSummary; +/// Complete result of the controlled SPL Token Devnet lifecycle. +pub use crate::solana_token_lifecycle::DevnetSplTokenLifecycleSummary; +/// Executes initialize, mint, transfer, approve/revoke, burn and close on Devnet. +pub use crate::solana_token_lifecycle::execute_devnet_spl_token_lifecycle; +/// Creates and verifies fresh raw Token-owned accounts for one Devnet lifecycle. +pub use crate::solana_token_lifecycle::prepare_devnet_spl_token_lifecycle_accounts; +/// One machine-readable classic SPL Token stateful check. +pub use crate::solana_token_stateful::SplTokenStatefulCheck; +/// One contextual fact measured during classic SPL Token state inspection. +pub use crate::solana_token_stateful::SplTokenStatefulFact; +/// Complete classic SPL Token stateful readiness report. +pub use crate::solana_token_stateful::SplTokenStatefulReadinessReport; +/// Complete request for one classic SPL Token stateful readiness inspection. +pub use crate::solana_token_stateful::SplTokenStatefulReadinessRequest; +/// Stateful readiness outcome for one classic SPL Token operation. +pub use crate::solana_token_stateful::SplTokenStatefulReadinessStatus; +/// Inspects Localnet or Devnet Token state before simulation. +pub use crate::solana_token_stateful::inspect_spl_token_stateful_readiness; diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/plan.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/plan.rs new file mode 100644 index 0000000..7fc18c0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/plan.rs @@ -0,0 +1,44 @@ +// file: kb_pipeline/src/plan.rs +// version: 1 + +//! Pipeline planning primitives. + +/// Pipeline stage identifier. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum PipelineStage { + /// Ingest raw transactions. + Ingest, + /// Extract generic Solana structures. + Extract, + /// Build program observations. + Observe, + /// Decode protocol events. + Decode, + /// Materialize business events. + Materialize, + /// Aggregate materialized events. + Aggregate, + /// Validate outputs. + Validate, +} + +/// Replay selection scope. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ReplayScope { + /// Optional module name. + pub module_name: std::option::Option, + /// Optional module version. + pub module_version: std::option::Option, + /// Optional program id. + pub program_id: std::option::Option, + /// Optional surface code. + pub surface_code: std::option::Option, + /// Optional 8-byte discriminator in hexadecimal. + pub discriminator_8: std::option::Option, + /// Optional inclusive start slot. + pub slot_start: std::option::Option, + /// Optional inclusive end slot. + pub slot_end: std::option::Option, + /// Whether to ignore existing ledger rows. + pub force: bool, +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_ata_execution.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_ata_execution.rs new file mode 100644 index 0000000..e527c6c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_ata_execution.rs @@ -0,0 +1,1092 @@ +// file: kb_pipeline/src/solana_ata_execution.rs +// version: 5 + +//! Devnet ATA execution with stateful and canonical post-validation. + +/// Complete request for one Devnet SPL Associated Token Account execution. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DevnetSplAssociatedTokenAccountExecutionRequest { + /// Stable caller-provided execution identifier. + pub intent_id: std::string::String, + /// Endpoint role used for state, balance, blockhash, fee and hydration calls. + pub query_role: std::string::String, + /// Endpoint role used for simulation, submission and confirmation polling. + pub transaction_role: std::string::String, + /// Exact typed SPL Associated Token Account operation. + pub operation: kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation, + /// Explicitly authorizes signing and submission after successful simulation. + pub submit: bool, + /// Explicit operator confirmation required by the active profile. + pub operator_confirmed: bool, + /// Number of `getTransaction` retries after the first hydration attempt. + pub post_validation_max_retries: u32, + /// Replaces existing core and decode outputs for the submitted signature. + pub force_post_validation_replay: bool, +} + +impl crate::solana_ata_execution::DevnetSplAssociatedTokenAccountExecutionRequest { + /// Creates a conservative simulation-only request. + pub fn new( + intent_id: impl std::convert::Into, + operation: kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation, + ) -> Self { + return Self { + intent_id: intent_id.into(), + query_role: "http_queries".to_string(), + transaction_role: "http_transactions".to_string(), + operation, + submit: false, + operator_confirmed: false, + post_validation_max_retries: 10, + force_post_validation_replay: false, + }; + } + + /// Validates request-local bounds independently from one profile. + pub fn validate(&self) -> kb_core::Result<()> { + if self.intent_id.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Devnet SPL Associated Token Account execution intent id must not be empty", + )); + } + if self.query_role.trim().is_empty() || self.transaction_role.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Devnet SPL Associated Token Account execution endpoint roles must not be empty", + )); + } + if self.post_validation_max_retries > 20 { + return std::result::Result::Err(kb_core::Error::config( + "post-execution getTransaction retries must not exceed 20", + )); + } + return std::result::Result::Ok(()); + } +} + +/// Complete result of one Devnet SPL Associated Token Account execution. +#[derive(Clone, Debug, PartialEq)] +pub struct DevnetSplAssociatedTokenAccountExecutionSummary { + /// Profile used by the orchestration. + pub profile_name: std::string::String, + /// Exact classified cluster. + pub cluster: kb_execution_api::ExecutionCluster, + /// Genesis hash returned by the selected endpoint. + pub genesis_hash: std::string::String, + /// Non-secret persistent wallet description. + pub wallet: kb_wallet::WalletSummary, + /// Wallet balance observed before planning. + pub balance_lamports: u64, + /// Stateful readiness report produced before plan simulation. + pub stateful_readiness: crate::SplAssociatedTokenAccountStatefulReadinessReport, + /// Exact prepared Token plan. + pub plan: kb_execution_api::PreparedExecutionPlan, + /// Recent blockhash used by the exact transaction. + pub latest_blockhash: kb_rpc::LatestBlockhashResult, + /// Fee estimate for the exact compiled message. + pub fee: kb_rpc::FeeForMessageResult, + /// Exact simulation result bound to the compiled message. + pub simulation: kb_execution_api::ExecutionSimulationResult, + /// Submission result when explicitly authorized. + pub send_result: std::option::Option, + /// Confirmation result when submitted. + pub confirmation: std::option::Option, + /// Stateful account relationships observed after confirmation. + pub post_state_validation: + std::option::Option, + /// Canonical hydration result for the exact signature. + pub backfill: std::option::Option, + /// Core extraction result for the exact signature. + pub core_extraction: std::option::Option, + /// First Token decode and materialization replay. + pub decode_replay: std::option::Option, + /// Second replay proving idempotence for the same decoder version and input. + pub idempotence_replay: std::option::Option, + /// Exact materialized rows produced for the submitted Token transaction. + pub materializations: std::vec::Vec, + /// Aggregated post-execution validation diagnostic. + pub post_execution: std::option::Option, +} + +struct PreparedAtaExecution { + wallet: kb_wallet::TemporaryWallet, + unsigned: kb_execution_solana::UnsignedSolanaTransaction, + evidence: kb_execution_solana::SolanaSimulationEvidence, + summary: crate::solana_ata_execution::DevnetSplAssociatedTokenAccountExecutionSummary, +} + +/// Simulates one Devnet SPL Associated Token Account operation after stateful preflight. +pub async fn simulate_devnet_spl_associated_token_account( + http_pool: &kb_rpc::HttpEndpointPool, + profile: &kb_config::ProfileConfig, + workspace_root: &std::path::Path, + request: &crate::solana_ata_execution::DevnetSplAssociatedTokenAccountExecutionRequest, + observer: &O, +) -> kb_core::Result +where + O: crate::SolanaExecutionObserver, +{ + if request.submit { + return std::result::Result::Err(kb_core::Error::config( + "simulate_devnet_spl_associated_token_account requires submit=false", + )); + } + let prepared = match crate::solana_ata_execution::prepare_execution( + http_pool, + profile, + workspace_root, + request, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(prepared.summary); +} + +/// Executes one Devnet SPL Associated Token Account simulation or authorized submission. +#[allow(clippy::too_many_arguments)] +pub async fn execute_devnet_spl_associated_token_account( + http_pool: &kb_rpc::HttpEndpointPool, + store: &S, + profile: &kb_config::ProfileConfig, + workspace_root: &std::path::Path, + request: &crate::solana_ata_execution::DevnetSplAssociatedTokenAccountExecutionRequest, + decoders: &[std::sync::Arc], + materializers: &[std::sync::Arc], + observer: &O, +) -> kb_core::Result +where + S: kb_store_core::RawTransactionStore + + kb_store_core::CoreExtractionStore + + kb_store_core::DecodePipelineStore + + Sync, + O: crate::SolanaExecutionObserver, +{ + let prepared = match crate::solana_ata_execution::prepare_execution( + http_pool, + profile, + workspace_root, + request, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if !request.submit { + return std::result::Result::Ok(prepared.summary); + } + if !prepared.summary.simulation.success { + return std::result::Result::Err(kb_core::Error::new( + "execution_simulation_failed", + crate::solana_execution::simulation_failure_message(&prepared.summary.simulation), + )); + } + if let std::result::Result::Err(error) = + crate::solana_ata_execution::validate_profile_wallet_signers( + prepared.unsigned.required_signer_pubkeys(), + prepared.summary.wallet.public_key.as_str(), + ) + { + return std::result::Result::Err(error); + } + let send_evaluation = match kb_execution_safety::ExecutionSafetyChecker + .evaluate_send(&prepared.summary.plan, &prepared.summary.simulation) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if send_evaluation.decision == kb_execution_safety::ExecutionSafetyDecision::Deny { + return std::result::Result::Err(kb_core::Error::new( + "execution_send_denied", + crate::solana_execution::violation_message(send_evaluation.violations.as_slice()), + )); + } + let signed = match prepared + .unsigned + .sign_after_simulation(&prepared.evidence, &[prepared.wallet.as_signer()]) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if let std::result::Result::Err(error) = signed.verify_signatures() { + return std::result::Result::Err(error); + } + let signature = signed.primary_signature().clone(); + let mut summary = prepared.summary; + let mut diagnostic = kb_execution_api::PostExecutionDiagnostic { + signature: signature.clone(), + canonical_inserted: false, + core_extracted: false, + decode_replayed: false, + materialized: false, + diagnostics: std::vec::Vec::new(), + }; + let send_config = match kb_rpc::SendTransactionConfig::from_execution_config( + &profile.execution, + std::option::Option::Some(summary.latest_blockhash.context.slot), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let sent = match http_pool + .send_transaction_for_role( + request.transaction_role.as_str(), + signed.transaction_base64().as_str(), + &signature, + &send_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic + .diagnostics + .push(format!("SPL Associated Token Account submission failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + summary.send_result = std::option::Option::Some( + sent.to_execution_result(kb_execution_api::ExecutionCluster::Devnet), + ); + let confirmation_config = match kb_rpc::ConfirmTransactionConfig::from_execution_config( + &profile.execution, + std::option::Option::Some(summary.latest_blockhash.last_valid_block_height), + std::option::Option::Some(summary.latest_blockhash.context.slot), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let confirmation = match http_pool + .confirm_transaction_for_roles( + request.transaction_role.as_str(), + request.query_role.as_str(), + kb_execution_api::ExecutionCluster::Devnet, + &signature, + &confirmation_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic + .diagnostics + .push(format!("SPL Associated Token Account confirmation failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + let confirmation_status = confirmation.status; + summary.confirmation = std::option::Option::Some(confirmation); + if !matches!( + confirmation_status, + kb_execution_api::ExecutionConfirmationStatus::Confirmed + | kb_execution_api::ExecutionConfirmationStatus::Finalized + ) { + diagnostic.diagnostics.push(format!("SPL Associated Token Account post-validation stopped at confirmation status {confirmation_status:?}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + } + let post_state = match crate::solana_ata_execution::validate_post_state_with_retries( + http_pool, + profile, + request.query_role.as_str(), + request.post_validation_max_retries, + &request.operation, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic.diagnostics.push(format!( + "SPL Associated Token Account post-state validation failed: {error}" + )); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + let post_state_ready = + post_state.status == crate::SplAssociatedTokenAccountStatefulReadinessStatus::Ready; + summary.post_state_validation = std::option::Option::Some(post_state); + if !post_state_ready { + diagnostic.diagnostics.push("confirmed SPL Associated Token Account execution did not satisfy final account relationships".to_string()); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + } + let backfill = match crate::solana_token_execution::hydrate_signature( + http_pool, + store, + profile, + request.query_role.as_str(), + request.post_validation_max_retries, + observer, + &signature, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic + .diagnostics + .push(format!("SPL Associated Token Account hydration failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + diagnostic.canonical_inserted = crate::solana_token_execution::canonical_available(&backfill); + summary.backfill = std::option::Option::Some(backfill); + if !diagnostic.canonical_inserted { + diagnostic.diagnostics.push("confirmed SPL Associated Token Account transaction was unavailable for canonical hydration".to_string()); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + } + let extraction = match crate::execute_core_extraction( + store, + &crate::CoreExtractionRequest { + source: crate::CoreExtractionSource::Signatures(std::vec![signature.0.clone()]), + limit: 1, + max_concurrent_extractions: 1, + force_replay: request.force_post_validation_replay, + }, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic + .diagnostics + .push(format!("SPL Associated Token Account core extraction failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + diagnostic.core_extracted = extraction.failed == 0 + && !extraction.cancelled + && extraction.selected == 1 + && extraction.extracted.saturating_add(extraction.skipped) >= 1; + summary.core_extraction = std::option::Option::Some(extraction); + if !diagnostic.core_extracted { + diagnostic + .diagnostics + .push("SPL Associated Token Account core extraction did not complete".to_string()); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + } + let replay_program_ids = crate::solana_ata_execution::replay_program_ids(&request.operation); + let first_replay = match crate::solana_token_execution::replay_program( + store, + &signature, + false, + request.force_post_validation_replay, + replay_program_ids.as_slice(), + decoders, + materializers, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic + .diagnostics + .push(format!("SPL Associated Token Account decode replay failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + diagnostic.decode_replayed = crate::solana_token_execution::decode_completed(&first_replay); + summary.decode_replay = std::option::Option::Some(first_replay); + let filter = match kb_store_core::MaterializedEventFilter::new( + std::option::Option::None, + std::option::Option::None, + std::option::Option::Some(signature.0.clone()), + 64, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let rows = + match kb_store_core::DecodePipelineStore::list_materialized_events(store, &filter).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic.diagnostics.push(format!( + "SPL Associated Token Account materialization query failed: {error}" + )); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + summary.materializations = rows + .into_iter() + .filter(|row| return row.source_decoder_name == "spl_associated_token_account") + .collect(); + diagnostic.materialized = + !summary.plan.policy.post_execution_validation.materialization_required + || !summary.materializations.is_empty(); + let second_replay = match crate::solana_token_execution::replay_program( + store, + &signature, + true, + request.force_post_validation_replay, + replay_program_ids.as_slice(), + decoders, + materializers, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic + .diagnostics + .push(format!("SPL Associated Token Account idempotence replay failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + let idempotent = second_replay.failed_inputs == 0 + && second_replay.processors.iter().all(|processor| { + return processor.failed == 0 + && processor.materialized_outputs == 0 + && processor.materialization_refused == 0; + }); + summary.idempotence_replay = std::option::Option::Some(second_replay); + if !idempotent { + diagnostic.diagnostics.push( + "second SPL Associated Token Account replay did not prove a clean idempotent skip" + .to_string(), + ); + } else if diagnostic.canonical_inserted + && diagnostic.core_extracted + && diagnostic.decode_replayed + && diagnostic.materialized + { + diagnostic.diagnostics.push("SPL Associated Token Account completed canonical hydration, core extraction, decode, materialization and idempotence validation".to_string()); + } + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); +} + +async fn prepare_execution( + http_pool: &kb_rpc::HttpEndpointPool, + profile: &kb_config::ProfileConfig, + workspace_root: &std::path::Path, + request: &crate::solana_ata_execution::DevnetSplAssociatedTokenAccountExecutionRequest, + observer: &O, +) -> kb_core::Result +where + O: crate::SolanaExecutionObserver, +{ + if let std::result::Result::Err(error) = request.validate() { + return std::result::Result::Err(error); + } + if let std::result::Result::Err(error) = + crate::solana_ata_execution::validate_profile(profile, request) + { + return std::result::Result::Err(error); + } + if let std::result::Result::Err(error) = + crate::solana_execution::ensure_not_cancelled(observer, "ata_validate") + { + return std::result::Result::Err(error); + } + let genesis = match http_pool.get_genesis_hash_for_role(request.query_role.as_str()).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if genesis.classified_cluster + != std::option::Option::Some(kb_execution_api::ExecutionCluster::Devnet) + { + return std::result::Result::Err(kb_core::Error::new( + "execution_cluster_mismatch", + format!( + "expected Devnet genesis hash but endpoint returned {} classified as {:?}", + genesis.genesis_hash, genesis.classified_cluster + ), + )); + } + let wallet = match crate::solana_execution::load_profile_wallet(profile, workspace_root).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let wallet_summary = wallet.summary(); + let fee_payer = kb_model::Pubkey(wallet_summary.public_key.clone()); + let balance = match http_pool + .get_balance_for_role( + request.query_role.as_str(), + &fee_payer, + &kb_rpc::GetBalanceConfig::confirmed(), + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if balance.lamports < profile.execution.max_fee_lamports { + return std::result::Result::Err(kb_core::Error::new( + "execution_balance_insufficient", + format!( + "Devnet wallet balance {} is below the configured fee ceiling {}", + balance.lamports, profile.execution.max_fee_lamports + ), + )); + } + let intent = crate::solana_ata_execution::build_intent(profile, request, fee_payer.clone()); + let intent = match intent { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let plan = match kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &kb_executor_spl_associated_token_account::SplAssociatedTokenAccountExecutor, + &intent, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let readiness = match crate::inspect_spl_associated_token_account_stateful_readiness( + http_pool, + &crate::SplAssociatedTokenAccountStatefulReadinessRequest { + query_role: request.query_role.clone(), + cluster: kb_execution_api::ExecutionCluster::Devnet, + intent, + available_signers: std::vec![fee_payer.clone()], + }, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if readiness.status != crate::SplAssociatedTokenAccountStatefulReadinessStatus::Ready { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_associated_token_account_stateful_blocked", + crate::solana_ata_execution::failed_readiness_message(&readiness), + )); + } + let plan_evaluation = + match kb_execution_safety::ExecutionSafetyChecker.evaluate_prepared_plan(&plan) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if plan_evaluation.decision == kb_execution_safety::ExecutionSafetyDecision::Deny { + return std::result::Result::Err(kb_core::Error::new( + "execution_plan_denied", + crate::solana_execution::violation_message(plan_evaluation.violations.as_slice()), + )); + } + let latest_blockhash = match http_pool + .get_latest_blockhash_for_role( + request.query_role.as_str(), + &kb_rpc::GetLatestBlockhashConfig::confirmed(), + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let unsigned = match kb_execution_solana::build_legacy_transaction( + &plan, + latest_blockhash.blockhash.as_str(), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let fee = match http_pool + .get_fee_for_message_for_role( + request.query_role.as_str(), + unsigned.message_base64().as_str(), + &kb_rpc::GetFeeForMessageConfig::new( + kb_rpc::RpcCommitmentLevel::Confirmed, + std::option::Option::Some(latest_blockhash.context.slot), + ), + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if fee.fee_lamports.is_none() { + return std::result::Result::Err(kb_core::Error::new( + "execution_fee_unavailable", + "getFeeForMessage returned null for the selected recent blockhash", + )); + } + let unsigned_base64 = match unsigned.transaction_base64() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let simulation_config = match kb_rpc::SimulateTransactionConfig::new( + kb_rpc::RpcCommitmentLevel::Confirmed, + false, + false, + std::option::Option::Some(latest_blockhash.context.slot), + true, + std::option::Option::None, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_execution::emit( + observer, + crate::SolanaExecutionProgressLevel::Info, + "spl_associated_token_account_simulation", + format!( + "simulating exact SPL Associated Token Account message {}", + unsigned.message_hash() + ), + std::option::Option::None, + ); + let simulation_rpc = match http_pool + .simulate_transaction_for_role( + request.transaction_role.as_str(), + unsigned_base64.as_str(), + &simulation_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let simulation = simulation_rpc.to_execution_result( + kb_execution_api::ExecutionCluster::Devnet, + kb_execution_api::ExecutionBlockhashKind::Latest, + std::option::Option::Some( + simulation_rpc.context.slot.saturating_sub(latest_blockhash.context.slot), + ), + std::option::Option::None, + std::option::Option::None, + std::option::Option::Some(&fee), + ); + let evidence = unsigned.bind_simulation(simulation.clone()); + let summary = crate::solana_ata_execution::DevnetSplAssociatedTokenAccountExecutionSummary { + profile_name: profile.name.clone(), + cluster: kb_execution_api::ExecutionCluster::Devnet, + genesis_hash: genesis.genesis_hash, + wallet: wallet_summary, + balance_lamports: balance.lamports, + stateful_readiness: readiness, + plan, + latest_blockhash, + fee, + simulation, + send_result: std::option::Option::None, + confirmation: std::option::Option::None, + post_state_validation: std::option::Option::None, + backfill: std::option::Option::None, + core_extraction: std::option::Option::None, + decode_replay: std::option::Option::None, + idempotence_replay: std::option::Option::None, + materializations: std::vec::Vec::new(), + post_execution: std::option::Option::None, + }; + return std::result::Result::Ok(crate::solana_ata_execution::PreparedAtaExecution { + wallet, + unsigned, + evidence, + summary, + }); +} + +fn validate_profile( + profile: &kb_config::ProfileConfig, + request: &crate::solana_ata_execution::DevnetSplAssociatedTokenAccountExecutionRequest, +) -> kb_core::Result<()> { + if profile.wallet.cluster != "devnet" { + return std::result::Result::Err(kb_core::Error::config( + "SPL Associated Token Account Devnet orchestration requires a Devnet wallet profile", + )); + } + if !profile.wallet.temporary_wallet_enabled || !profile.wallet.temporary_wallet_persist { + return std::result::Result::Err(kb_core::Error::config( + "SPL Associated Token Account Devnet orchestration requires an enabled persistent temporary wallet", + )); + } + if !profile.execution.require_simulation { + return std::result::Result::Err(kb_core::Error::config( + "SPL Associated Token Account Devnet orchestration requires simulation", + )); + } + if request.submit && !profile.wallet.devnet_send_enabled { + return std::result::Result::Err(kb_core::Error::config( + "Devnet transaction submission is disabled by the wallet profile", + )); + } + if request.submit + && profile.execution.require_operator_confirmation + && !request.operator_confirmed + { + return std::result::Result::Err(kb_core::Error::config( + "SPL Associated Token Account Devnet submission requires explicit operator confirmation", + )); + } + return std::result::Result::Ok(()); +} + +fn build_intent( + profile: &kb_config::ProfileConfig, + request: &crate::solana_ata_execution::DevnetSplAssociatedTokenAccountExecutionRequest, + fee_payer: kb_model::Pubkey, +) -> kb_core::Result< + kb_executor_spl_associated_token_account::SplAssociatedTokenAccountExecutionIntent, +> { + let max_rent_lamports = if matches!(&request.operation, kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::RecoverNested { .. }) { 0 } else { profile.execution.devnet_max_spend_lamports }; + let mut authorized_signers = std::vec![fee_payer.clone()]; + if let kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::RecoverNested { wallet_owner, .. } = &request.operation { + if !authorized_signers.contains(wallet_owner) { + authorized_signers.push(wallet_owner.clone()); + } + } + let intent = + kb_executor_spl_associated_token_account::SplAssociatedTokenAccountExecutionIntent { + intent_id: request.intent_id.clone(), + fee_payer, + max_rent_lamports, + policy: kb_execution_api::ExecutionPolicy { + cluster: kb_execution_api::ExecutionClusterPolicy { + expected_cluster: kb_execution_api::ExecutionCluster::Devnet, + allow_mainnet: false, + mainnet_confirmation: false, + }, + simulation: kb_execution_api::ExecutionSimulationPolicy::Required, + blockhash: kb_execution_api::ExecutionBlockhashPolicy { + kind: kb_execution_api::ExecutionBlockhashKind::Latest, + max_age_slots: std::option::Option::Some( + profile.execution.recent_blockhash_max_age_slots, + ), + nonce_account: std::option::Option::None, + nonce_authority: std::option::Option::None, + }, + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(max_rent_lamports), + max_fee_lamports: std::option::Option::Some(profile.execution.max_fee_lamports), + max_compute_unit_price_micro_lamports: std::option::Option::Some( + profile.execution.max_compute_unit_price_micro_lamports, + ), + }, + authorized_signers, + dry_run: !request.submit, + post_execution_validation: kb_execution_api::PostExecutionValidationPolicy { + canonical_insert_required: true, + core_extraction_required: true, + decode_replay_required: true, + materialization_required: true, + }, + }, + operation: request.operation.clone(), + }; + return std::result::Result::Ok(intent); +} + +fn failed_readiness_message( + readiness: &crate::SplAssociatedTokenAccountStatefulReadinessReport, +) -> std::string::String { + let failures = readiness + .checks + .iter() + .filter(|check| return !check.passed) + .take(8) + .map(|check| return format!("{}: {}", check.code, check.message)) + .collect::>(); + if failures.is_empty() { + return "SPL Associated Token Account stateful readiness returned Blocked without diagnostics".to_string(); + } + return failures.join("; "); +} + +fn replay_program_ids( + operation: &kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation, +) -> std::vec::Vec<&'static str> { + let mut output = std::vec![kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID]; + if operation.token_program() + == kb_executor_spl_associated_token_account::SplAssociatedTokenProgram::Classic + { + output.push(kb_program_ids::SPL_TOKEN_PROGRAM_ID); + } + return output; +} + +async fn validate_post_state_with_retries( + http_pool: &kb_rpc::HttpEndpointPool, + profile: &kb_config::ProfileConfig, + query_role: &str, + maximum_retries: u32, + operation: &kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation, +) -> kb_core::Result { + let mut retry = 0_u32; + loop { + let result = crate::inspect_spl_associated_token_account_post_execution( + http_pool, + query_role, + kb_execution_api::ExecutionCluster::Devnet, + operation, + ) + .await; + match result { + std::result::Result::Ok(report) + if report.status + == crate::SplAssociatedTokenAccountStatefulReadinessStatus::Ready + || retry >= maximum_retries => + { + return std::result::Result::Ok(report); + }, + std::result::Result::Err(error) if retry >= maximum_retries => { + return std::result::Result::Err(error); + }, + std::result::Result::Ok(_) | std::result::Result::Err(_) => {}, + } + retry = retry.saturating_add(1); + tokio::time::sleep(std::time::Duration::from_millis(std::cmp::max( + profile.execution.confirmation_poll_interval_ms, + 500, + ))) + .await; + } +} + +fn validate_profile_wallet_signers( + required_signers: &[std::string::String], + wallet_pubkey: &str, +) -> kb_core::Result<()> { + if required_signers.iter().any(|value| return value.as_str() != wallet_pubkey) { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_associated_token_account_external_signer_unavailable", + format!( + "the Devnet ATA orchestrator can sign only with profile wallet {}; required signers are {}", + wallet_pubkey, + required_signers.join(",") + ), + )); + } + return std::result::Result::Ok(()); +} + +#[cfg(test)] +mod tests { + fn required_environment_value(name: &str) -> std::string::String { + return match std::env::var(name) { + std::result::Result::Ok(value) if !value.trim().is_empty() => value, + std::result::Result::Ok(_) | std::result::Result::Err(_) => { + panic!("{name} is required") + }, + }; + } + + fn local_devnet_profile() -> kb_config::ProfileConfig { + let config = + match kb_config::parse_config_json(include_str!("../../config/example.config.json")) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("example config parse failed: {error}"), + }; + for profile in config.profiles { + if profile.name == "local_devnet" { + return profile; + } + } + panic!("local_devnet profile missing"); + } + + fn create_operation( + wallet: &str, + ) -> kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation { + return kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::CreateIdempotent { + wallet_owner: kb_model::Pubkey(wallet.to_string()), + mint: kb_model::Pubkey(kb_program_ids::VOTE_PROGRAM_ID.to_string()), + token_program: kb_executor_spl_associated_token_account::SplAssociatedTokenProgram::Classic, + }; + } + + #[test] + fn request_is_simulation_only_and_bounded_by_default() { + let request = crate::DevnetSplAssociatedTokenAccountExecutionRequest::new( + "ata-1", + crate::solana_ata_execution::tests::create_operation(kb_program_ids::SYSTEM_PROGRAM_ID), + ); + assert!(!request.submit); + assert!(request.validate().is_ok()); + let mut invalid = request; + invalid.post_validation_max_retries = 21; + assert!(invalid.validate().is_err()); + } + + #[test] + fn creation_and_recovery_intents_preserve_exact_spend_and_signers() { + let profile = crate::solana_ata_execution::tests::local_devnet_profile(); + let fee_payer = kb_model::Pubkey(kb_program_ids::SYSTEM_PROGRAM_ID.to_string()); + let create = crate::DevnetSplAssociatedTokenAccountExecutionRequest::new( + "ata-2", + crate::solana_ata_execution::tests::create_operation(fee_payer.0.as_str()), + ); + let create_intent = + crate::solana_ata_execution::build_intent(&profile, &create, fee_payer.clone()) + .unwrap_or_else(|error| panic!("create intent failed: {error}")); + assert_eq!(create_intent.max_rent_lamports, profile.execution.devnet_max_spend_lamports); + assert!(create_intent.policy.dry_run); + assert_eq!(create_intent.policy.authorized_signers, std::vec![fee_payer.clone()]); + + let recovery = crate::DevnetSplAssociatedTokenAccountExecutionRequest::new( + "ata-3", + kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::RecoverNested { + wallet_owner: kb_model::Pubkey(kb_program_ids::STAKE_PROGRAM_ID.to_string()), + owner_mint: kb_model::Pubkey(kb_program_ids::VOTE_PROGRAM_ID.to_string()), + nested_mint: kb_model::Pubkey(kb_program_ids::CONFIG_PROGRAM_ID.to_string()), + token_program: kb_executor_spl_associated_token_account::SplAssociatedTokenProgram::Token2022, + }, + ); + let recovery_intent = + crate::solana_ata_execution::build_intent(&profile, &recovery, fee_payer) + .unwrap_or_else(|error| panic!("recovery intent failed: {error}")); + assert_eq!(recovery_intent.max_rent_lamports, 0); + assert_eq!(recovery_intent.policy.authorized_signers.len(), 2); + } + + #[test] + fn submission_signers_must_resolve_to_the_profile_wallet() { + let wallet = kb_program_ids::SYSTEM_PROGRAM_ID.to_string(); + assert!( + crate::solana_ata_execution::validate_profile_wallet_signers( + std::slice::from_ref(&wallet), + wallet.as_str(), + ) + .is_ok() + ); + let error = crate::solana_ata_execution::validate_profile_wallet_signers( + &[wallet.clone(), kb_program_ids::VOTE_PROGRAM_ID.to_string()], + wallet.as_str(), + ); + assert!(error.is_err()); + } + + #[tokio::test] + async fn optional_devnet_operation_from_env() { + if std::env::var("KB_DEVNET_SPL_ATA_EXECUTION_TEST").ok().as_deref() + != std::option::Option::Some("1") + { + return; + } + let operation_code = std::env::var("KB_DEVNET_SPL_ATA_OPERATION") + .unwrap_or_else(|_| return "create_idempotent".to_string()); + let token_program = match std::env::var("KB_DEVNET_SPL_ATA_TOKEN_PROGRAM").ok().as_deref() { + std::option::Option::Some("token_2022") => { + kb_executor_spl_associated_token_account::SplAssociatedTokenProgram::Token2022 + }, + std::option::Option::Some("classic") | std::option::Option::None => { + kb_executor_spl_associated_token_account::SplAssociatedTokenProgram::Classic + }, + std::option::Option::Some(value) => panic!("unsupported Token Program {value}"), + }; + let submit = std::env::var("KB_DEVNET_SPL_ATA_SUBMIT").ok().as_deref() + == std::option::Option::Some("1"); + let profile = crate::solana_ata_execution::tests::local_devnet_profile(); + let workspace_root = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .parent() + .map(std::path::Path::to_path_buf) + .unwrap_or_else(|| panic!("workspace root missing")); + let wallet = + crate::solana_execution::load_profile_wallet(&profile, workspace_root.as_path()) + .await + .unwrap_or_else(|error| panic!("profile wallet failed: {error}")); + let wallet_pubkey = wallet.public_key(); + let operation = match operation_code.as_str() { + "create_idempotent" => kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::CreateIdempotent { + wallet_owner: kb_model::Pubkey(wallet_pubkey), + mint: kb_model::Pubkey(crate::solana_ata_execution::tests::required_environment_value("KB_DEVNET_SPL_ATA_MINT")), + token_program, + }, + "recover_nested" => kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::RecoverNested { + wallet_owner: kb_model::Pubkey(wallet_pubkey), + owner_mint: kb_model::Pubkey(crate::solana_ata_execution::tests::required_environment_value("KB_DEVNET_SPL_ATA_OWNER_MINT")), + nested_mint: kb_model::Pubkey(crate::solana_ata_execution::tests::required_environment_value("KB_DEVNET_SPL_ATA_NESTED_MINT")), + token_program, + }, + value => panic!("unsupported ATA operation {value}"), + }; + let mut request = crate::DevnetSplAssociatedTokenAccountExecutionRequest::new( + "optional-devnet-ata", + operation, + ); + request.submit = submit; + request.operator_confirmed = submit; + request.post_validation_max_retries = 20; + let pool = kb_rpc::HttpEndpointPool::from_profile(&profile) + .unwrap_or_else(|error| panic!("HTTP pool failed: {error}")); + let store = kb_store_pg::PostgresStore::connect_from_profile_config(&profile) + .await + .unwrap_or_else(|error| panic!("PostgreSQL connection failed: {error}")); + store + .initialize_store_schema() + .await + .unwrap_or_else(|error| panic!("schema initialization failed: {error}")); + let decoders: std::vec::Vec> = std::vec![ + std::sync::Arc::new( + kb_decoder_spl_associated_token_account::SplAssociatedTokenAccountDecoder + ), + std::sync::Arc::new(kb_decoder_spl_token::SplTokenDecoder), + ]; + let materializers: std::vec::Vec< + std::sync::Arc, + > = std::vec![ + std::sync::Arc::new(kb_materializer_token_accounts::TokenAccountsMaterializer), + std::sync::Arc::new(kb_materializer_risk::RiskMaterializer), + ]; + let summary = crate::execute_devnet_spl_associated_token_account( + &pool, + &store, + &profile, + workspace_root.as_path(), + &request, + decoders.as_slice(), + materializers.as_slice(), + &crate::NoopSolanaExecutionObserver, + ) + .await + .unwrap_or_else(|error| panic!("Devnet ATA execution failed: {error}")); + assert_eq!( + summary.stateful_readiness.status, + crate::SplAssociatedTokenAccountStatefulReadinessStatus::Ready + ); + assert!(summary.simulation.success, "{:?}", summary.simulation.error); + if submit { + let send_result = match summary.send_result.as_ref() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + panic!("submitted ATA execution returned no signature") + }, + }; + println!( + "Devnet ATA operation={} signature={} materializations={}", + operation_code, + send_result.signature.0, + summary.materializations.len() + ); + assert!(summary.post_execution.as_ref().is_some_and(|value| { + return value.canonical_inserted + && value.core_extracted + && value.decode_replayed + && value.materialized; + })); + assert!(summary.idempotence_replay.as_ref().is_some_and(|value| { + return value.failed_inputs == 0 + && value.processors.iter().all(|processor| { + return processor.failed == 0 + && processor.materialized_outputs == 0 + && processor.materialization_refused == 0; + }); + })); + if operation_code == "recover_nested" { + assert!(summary.materializations.iter().any(|row| { + return row.materialized_family == "token_account" + && row.payload_json["lifecycleKind"] == "nested_ata_recovered"; + })); + assert!(summary.materializations.iter().any(|row| { + return row.materialized_family == "risk" + && row.payload_json["riskKind"] == "nested_ata_anti_pattern_recovered"; + })); + } + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_ata_stateful.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_ata_stateful.rs new file mode 100644 index 0000000..91eb4d1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_ata_stateful.rs @@ -0,0 +1,1304 @@ +// file: kb_pipeline/src/solana_ata_stateful.rs +// version: 3 + +//! Stateful Localnet and Devnet readiness checks for ATA operations. + +const BASE_MINT_LEN: usize = 82; +const BASE_TOKEN_ACCOUNT_LEN: usize = 165; +const MAX_TOKEN_2022_ACCOUNT_BYTES: usize = 16_384; + +macro_rules! result_value { + ($expression:expr) => { + match $expression { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + }; +} + +/// ATA stateful readiness outcome. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum SplAssociatedTokenAccountStatefulReadinessStatus { + /// Every stateful check passed. + Ready, + /// At least one stateful check failed. + Blocked, +} + +/// One machine-readable ATA stateful check. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SplAssociatedTokenAccountStatefulCheck { + /// Stable diagnostic code. + pub code: std::string::String, + /// Whether the check passed. + pub passed: bool, + /// Operator-readable explanation. + pub message: std::string::String, +} + +/// One contextual ATA stateful fact. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SplAssociatedTokenAccountStatefulFact { + /// Stable fact key. + pub key: std::string::String, + /// Exact string representation. + pub value: std::string::String, +} + +/// Complete request for one ATA stateful readiness inspection. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SplAssociatedTokenAccountStatefulReadinessRequest { + /// HTTP endpoint role used for every state read. + pub query_role: std::string::String, + /// Expected Localnet or Devnet cluster. + pub cluster: kb_execution_api::ExecutionCluster, + /// Typed simulation-first ATA intent. + pub intent: kb_executor_spl_associated_token_account::SplAssociatedTokenAccountExecutionIntent, + /// Signer public keys currently available to the caller. + pub available_signers: std::vec::Vec, +} + +/// Complete ATA stateful readiness report. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SplAssociatedTokenAccountStatefulReadinessReport { + /// Expected cluster. + pub cluster: kb_execution_api::ExecutionCluster, + /// Stable operation code. + pub operation_code: std::string::String, + /// Aggregate readiness status. + pub status: crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessStatus, + /// Highest contextual slot observed. + pub context_slot: std::option::Option, + /// Ordered checks. + pub checks: std::vec::Vec, + /// Ordered facts. + pub facts: std::vec::Vec, +} + +/// Stateful ATA invariants observed after a confirmed execution. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SplAssociatedTokenAccountPostExecutionReport { + /// Expected Localnet or Devnet cluster. + pub cluster: kb_execution_api::ExecutionCluster, + /// Stable operation code. + pub operation_code: std::string::String, + /// Aggregate postcondition status. + pub status: crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessStatus, + /// Highest contextual slot observed. + pub context_slot: std::option::Option, + /// Ordered state checks. + pub checks: std::vec::Vec, + /// Ordered derived-address facts. + pub facts: std::vec::Vec, +} + +type AccountCache = + std::collections::BTreeMap>; + +/// Inspects Localnet or Devnet ATA state required before simulation. +pub async fn inspect_spl_associated_token_account_stateful_readiness( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessRequest, +) -> kb_core::Result { + if request.query_role.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "ATA stateful readiness query_role must not be empty", + )); + } + match crate::solana_ata_stateful::validate_cluster_kind(request.cluster) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let genesis = match pool.get_genesis_hash_for_role(request.query_role.as_str()).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + match crate::solana_ata_stateful::validate_genesis(request.cluster, &genesis) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let plan = match kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &kb_executor_spl_associated_token_account::SplAssociatedTokenAccountExecutor, + &request.intent, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let addresses = match crate::solana_ata_stateful::operation_addresses(&request.intent.operation) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let config = match kb_rpc::GetAccountInfoConfig::confirmed_with_data( + crate::solana_ata_stateful::MAX_TOKEN_2022_ACCOUNT_BYTES, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut cache = crate::solana_ata_stateful::AccountCache::new(); + let mut context_slot: std::option::Option = std::option::Option::None; + for address in &addresses { + let result = match pool + .get_account_info_for_role(request.query_role.as_str(), address, &config) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + context_slot = std::option::Option::Some(match context_slot { + std::option::Option::Some(slot) => slot.max(result.context.slot), + std::option::Option::None => result.context.slot, + }); + cache.insert(address.0.clone(), result.account); + } + let rent = match pool + .get_minimum_balance_for_rent_exemption_for_role( + request.query_role.as_str(), + crate::solana_ata_stateful::BASE_TOKEN_ACCOUNT_LEN as u64, + &kb_rpc::GetMinimumBalanceForRentExemptionConfig::confirmed(), + ) + .await + { + std::result::Result::Ok(value) => value.minimum_balance_lamports, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let balance_config = kb_rpc::GetBalanceConfig::confirmed(); + let payer_balance = match pool + .get_balance_for_role( + request.query_role.as_str(), + &request.intent.fee_payer, + &balance_config, + ) + .await + { + std::result::Result::Ok(value) => value.lamports, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::solana_ata_stateful::evaluate( + request, + &plan, + &genesis.genesis_hash, + context_slot, + &cache, + rent, + payer_balance, + ); +} + +/// Verifies final ATA account relationships after a confirmed execution. +pub async fn inspect_spl_associated_token_account_post_execution( + pool: &kb_rpc::HttpEndpointPool, + query_role: &str, + cluster: kb_execution_api::ExecutionCluster, + operation: &kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation, +) -> kb_core::Result { + if query_role.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "ATA post-execution query role must not be empty", + )); + } + if let std::result::Result::Err(error) = + crate::solana_ata_stateful::validate_cluster_kind(cluster) + { + return std::result::Result::Err(error); + } + let genesis = match pool.get_genesis_hash_for_role(query_role).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if let std::result::Result::Err(error) = + crate::solana_ata_stateful::validate_genesis(cluster, &genesis) + { + return std::result::Result::Err(error); + } + let addresses = match crate::solana_ata_stateful::operation_addresses(operation) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let config = match kb_rpc::GetAccountInfoConfig::confirmed_with_data( + crate::solana_ata_stateful::MAX_TOKEN_2022_ACCOUNT_BYTES, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut cache = crate::solana_ata_stateful::AccountCache::new(); + let mut context_slot: std::option::Option = std::option::Option::None; + for address in &addresses { + let result = match pool.get_account_info_for_role(query_role, address, &config).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + context_slot = std::option::Option::Some(match context_slot { + std::option::Option::Some(slot) => slot.max(result.context.slot), + std::option::Option::None => result.context.slot, + }); + cache.insert(address.0.clone(), result.account); + } + return crate::solana_ata_stateful::evaluate_post_execution( + cluster, + operation, + context_slot, + &cache, + ); +} + +fn validate_cluster_kind(cluster: kb_execution_api::ExecutionCluster) -> kb_core::Result<()> { + return match cluster { + kb_execution_api::ExecutionCluster::Localnet + | kb_execution_api::ExecutionCluster::Devnet => std::result::Result::Ok(()), + kb_execution_api::ExecutionCluster::Testnet + | kb_execution_api::ExecutionCluster::Mainnet => { + std::result::Result::Err(kb_core::Error::new( + "spl_ata_stateful_cluster_unsupported", + "ATA stateful readiness is restricted to Localnet and Devnet", + )) + }, + }; +} + +fn validate_genesis( + expected: kb_execution_api::ExecutionCluster, + genesis: &kb_rpc::GenesisHashResult, +) -> kb_core::Result<()> { + return match expected { + kb_execution_api::ExecutionCluster::Devnet + if genesis.classified_cluster + == std::option::Option::Some(kb_execution_api::ExecutionCluster::Devnet) => + { + std::result::Result::Ok(()) + }, + kb_execution_api::ExecutionCluster::Localnet if genesis.classified_cluster.is_none() => { + std::result::Result::Ok(()) + }, + kb_execution_api::ExecutionCluster::Devnet => { + std::result::Result::Err(kb_core::Error::new( + "spl_ata_stateful_devnet_genesis_mismatch", + "ATA Devnet readiness requires the official Devnet genesis hash", + )) + }, + kb_execution_api::ExecutionCluster::Localnet => { + std::result::Result::Err(kb_core::Error::new( + "spl_ata_stateful_localnet_public_cluster", + "ATA Localnet readiness refuses endpoints classified as a public cluster", + )) + }, + kb_execution_api::ExecutionCluster::Testnet + | kb_execution_api::ExecutionCluster::Mainnet => { + crate::solana_ata_stateful::validate_cluster_kind(expected) + }, + }; +} + +fn operation_addresses( + operation: &kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation, +) -> kb_core::Result> { + let mut values = std::vec::Vec::new(); + match operation { + kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::Create { + wallet_owner, + mint, + token_program, + } + | kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::CreateIdempotent { + wallet_owner, + mint, + token_program, + } => { + values.push(mint.clone()); + values.push(result_value!(crate::solana_ata_stateful::derive_ata( + wallet_owner, + mint, + token_program.program_id(), + ))); + }, + kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::RecoverNested { + wallet_owner, + owner_mint, + nested_mint, + token_program, + } => { + let owner_ata = result_value!(crate::solana_ata_stateful::derive_ata( + wallet_owner, + owner_mint, + token_program.program_id(), + )); + let nested_ata = result_value!(crate::solana_ata_stateful::derive_ata( + &owner_ata, + nested_mint, + token_program.program_id(), + )); + let destination_ata = result_value!(crate::solana_ata_stateful::derive_ata( + wallet_owner, + nested_mint, + token_program.program_id(), + )); + values.extend([ + owner_mint.clone(), + nested_mint.clone(), + owner_ata, + nested_ata, + destination_ata, + ]); + }, + } + values.sort_by(|left, right| return left.0.cmp(&right.0)); + values.dedup(); + return std::result::Result::Ok(values); +} + +fn derive_ata( + wallet: &kb_model::Pubkey, + mint: &kb_model::Pubkey, + token_program: &str, +) -> kb_core::Result { + let wallet: solana_pubkey::Pubkey = match wallet.0.parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "spl_ata_stateful_wallet_invalid", + error.to_string(), + )); + }, + }; + let mint: solana_pubkey::Pubkey = match mint.0.parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "spl_ata_stateful_mint_invalid", + error.to_string(), + )); + }, + }; + let token_program: solana_pubkey::Pubkey = match token_program.parse() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "spl_ata_stateful_token_program_invalid", + error.to_string(), + )); + }, + }; + let derived = spl_associated_token_account_interface::address::get_associated_token_address_with_program_id( + &wallet, + &mint, + &token_program, + ); + return std::result::Result::Ok(kb_model::Pubkey(derived.to_string())); +} + +fn evaluate( + request: &crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessRequest, + plan: &kb_execution_api::PreparedExecutionPlan, + genesis_hash: &str, + context_slot: std::option::Option, + cache: &crate::solana_ata_stateful::AccountCache, + base_rent_lamports: u64, + payer_balance_lamports: u64, +) -> kb_core::Result { + let mut report = crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessReport { + cluster: request.cluster, + operation_code: request.intent.operation.operation_code().to_string(), + status: crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessStatus::Ready, + context_slot, + checks: std::vec::Vec::new(), + facts: std::vec![ + crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulFact { + key: "genesis_hash".to_string(), + value: genesis_hash.to_string(), + }, + crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulFact { + key: "base_token_account_rent_lamports".to_string(), + value: base_rent_lamports.to_string(), + }, + ], + }; + crate::solana_ata_stateful::push_check( + &mut report, + "ata_program_id", + plan.instructions.len() == 1 + && plan.instructions[0].program_id.0 == kb_program_ids::ASSOCIATED_TOKEN_PROGRAM_ID, + "plan must contain exactly one instruction for the canonical ATA Program", + ); + crate::solana_ata_stateful::push_check( + &mut report, + "simulation_required", + plan.policy.simulation == kb_execution_api::ExecutionSimulationPolicy::Required, + "ATA plan must require simulation", + ); + for signer in &plan.required_signers { + crate::solana_ata_stateful::push_check( + &mut report, + "required_signer_available", + request.available_signers.contains(&signer.pubkey), + format!("required {} signer {} must be available", signer.role, signer.pubkey.0), + ); + } + let max_fee = match plan.policy.cost_limit.max_fee_lamports { + std::option::Option::Some(value) => value, + std::option::Option::None => 0, + }; + let total_ceiling = match plan.requested_spend_lamports.checked_add(max_fee) { + std::option::Option::Some(value) => value, + std::option::Option::None => u64::MAX, + }; + crate::solana_ata_stateful::push_check( + &mut report, + "payer_balance_covers_ceiling", + payer_balance_lamports >= total_ceiling, + format!("payer balance must cover rent-plus-fee ceiling {total_ceiling}"), + ); + let operation = &request.intent.operation; + match operation { + kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::Create { + wallet_owner, + mint, + token_program, + } => { + crate::solana_ata_stateful::check_mint(&mut report, cache, mint, token_program.program_id()); + let ata = result_value!(crate::solana_ata_stateful::derive_ata( + wallet_owner, + mint, + token_program.program_id(), + )); + crate::solana_ata_stateful::push_fact(&mut report, "derived_ata", ata.0.clone()); + crate::solana_ata_stateful::check_creation_plan_accounts( + &mut report, + plan, + &ata, + token_program.program_id(), + ); + crate::solana_ata_stateful::push_check( + &mut report, + "create_ata_absent", + cache.get(ata.0.as_str()).is_some_and(|value| return value.is_none()), + "strict Create requires the canonical ATA to be absent", + ); + crate::solana_ata_stateful::push_check( + &mut report, + "rent_ceiling_covers_base_account", + plan.requested_spend_lamports >= base_rent_lamports, + "creation rent ceiling must cover at least the base Token account rent", + ); + }, + kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::CreateIdempotent { + wallet_owner, + mint, + token_program, + } => { + crate::solana_ata_stateful::check_mint(&mut report, cache, mint, token_program.program_id()); + let ata = result_value!(crate::solana_ata_stateful::derive_ata( + wallet_owner, + mint, + token_program.program_id(), + )); + crate::solana_ata_stateful::push_fact(&mut report, "derived_ata", ata.0.clone()); + crate::solana_ata_stateful::check_creation_plan_accounts( + &mut report, + plan, + &ata, + token_program.program_id(), + ); + if let std::option::Option::Some(std::option::Option::Some(account)) = cache.get(ata.0.as_str()) { + crate::solana_ata_stateful::check_token_account( + &mut report, + account, + token_program.program_id(), + mint, + wallet_owner, + "idempotent_existing_ata", + ); + } else { + crate::solana_ata_stateful::push_check( + &mut report, + "idempotent_absent_or_compatible", + cache.contains_key(ata.0.as_str()), + "idempotent ATA must be confirmed absent or validated as compatible", + ); + crate::solana_ata_stateful::push_check( + &mut report, + "rent_ceiling_covers_base_account", + plan.requested_spend_lamports >= base_rent_lamports, + "absent idempotent ATA requires a ceiling covering base account rent", + ); + } + }, + kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::RecoverNested { + wallet_owner, + owner_mint, + nested_mint, + token_program, + } => { + crate::solana_ata_stateful::check_mint(&mut report, cache, owner_mint, token_program.program_id()); + crate::solana_ata_stateful::check_mint(&mut report, cache, nested_mint, token_program.program_id()); + let owner_ata = result_value!(crate::solana_ata_stateful::derive_ata( + wallet_owner, + owner_mint, + token_program.program_id(), + )); + let nested_ata = result_value!(crate::solana_ata_stateful::derive_ata( + &owner_ata, + nested_mint, + token_program.program_id(), + )); + let destination_ata = result_value!(crate::solana_ata_stateful::derive_ata( + wallet_owner, + nested_mint, + token_program.program_id(), + )); + for (key, value) in [ + ("derived_owner_ata", &owner_ata), + ("derived_nested_ata", &nested_ata), + ("derived_destination_ata", &destination_ata), + ] { + crate::solana_ata_stateful::push_fact(&mut report, key, value.0.clone()); + } + let accounts_exact = match plan.instructions.first() { + std::option::Option::Some(instruction) => { + instruction.accounts.len() == 7 + && instruction.accounts[0].pubkey == nested_ata + && instruction.accounts[2].pubkey == destination_ata + && instruction.accounts[3].pubkey == owner_ata + && instruction.accounts[6].pubkey.0 == token_program.program_id() + }, + std::option::Option::None => false, + }; + crate::solana_ata_stateful::push_check( + &mut report, + "recover_plan_accounts_exact", + accounts_exact, + "RecoverNested plan must retain all three derived ATA addresses and selected Token Program in official positions", + ); + crate::solana_ata_stateful::check_cached_token_account(&mut report, cache, &owner_ata, token_program.program_id(), owner_mint, wallet_owner, "owner_ata"); + crate::solana_ata_stateful::check_cached_token_account(&mut report, cache, &nested_ata, token_program.program_id(), nested_mint, &owner_ata, "nested_ata"); + crate::solana_ata_stateful::check_cached_token_account(&mut report, cache, &destination_ata, token_program.program_id(), nested_mint, wallet_owner, "destination_ata"); + crate::solana_ata_stateful::push_check( + &mut report, + "recovery_zero_rent_spend", + plan.requested_spend_lamports == 0, + "RecoverNested must not reserve rent spending", + ); + }, + } + report.status = if report.checks.iter().all(|check| return check.passed) { + crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessStatus::Ready + } else { + crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessStatus::Blocked + }; + return std::result::Result::Ok(report); +} + +fn evaluate_post_execution( + cluster: kb_execution_api::ExecutionCluster, + operation: &kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation, + context_slot: std::option::Option, + cache: &crate::solana_ata_stateful::AccountCache, +) -> kb_core::Result { + let mut report = crate::solana_ata_stateful::SplAssociatedTokenAccountPostExecutionReport { + cluster, + operation_code: operation.operation_code().to_string(), + status: crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessStatus::Ready, + context_slot, + checks: std::vec::Vec::new(), + facts: std::vec::Vec::new(), + }; + match operation { + kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::Create { + wallet_owner, + mint, + token_program, + } + | kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::CreateIdempotent { + wallet_owner, + mint, + token_program, + } => { + let ata = match crate::solana_ata_stateful::derive_ata( + wallet_owner, + mint, + token_program.program_id(), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_ata_stateful::push_post_fact(&mut report, "derived_ata", ata.0.clone()); + crate::solana_ata_stateful::check_post_cached_token_account( + &mut report, + cache, + &ata, + token_program.program_id(), + mint, + wallet_owner, + "post_execution_ata", + ); + }, + kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::RecoverNested { + wallet_owner, + owner_mint, + nested_mint, + token_program, + } => { + let owner_ata = result_value!(crate::solana_ata_stateful::derive_ata( + wallet_owner, + owner_mint, + token_program.program_id(), + )); + let nested_ata = result_value!(crate::solana_ata_stateful::derive_ata( + &owner_ata, + nested_mint, + token_program.program_id(), + )); + let destination_ata = result_value!(crate::solana_ata_stateful::derive_ata( + wallet_owner, + nested_mint, + token_program.program_id(), + )); + for (key, value) in [ + ("derived_owner_ata", &owner_ata), + ("derived_nested_ata", &nested_ata), + ("derived_destination_ata", &destination_ata), + ] { + crate::solana_ata_stateful::push_post_fact(&mut report, key, value.0.clone()); + } + crate::solana_ata_stateful::check_post_cached_token_account( + &mut report, + cache, + &owner_ata, + token_program.program_id(), + owner_mint, + wallet_owner, + "post_execution_owner_ata", + ); + crate::solana_ata_stateful::push_post_check( + &mut report, + "post_execution_nested_ata_closed", + cache + .get(nested_ata.0.as_str()) + .is_some_and(|account| return account.is_none()), + "confirmed RecoverNested must leave the nested ATA closed", + ); + crate::solana_ata_stateful::check_post_cached_token_account( + &mut report, + cache, + &destination_ata, + token_program.program_id(), + nested_mint, + wallet_owner, + "post_execution_destination_ata", + ); + }, + } + report.status = if report.checks.iter().all(|check| return check.passed) { + crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessStatus::Ready + } else { + crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessStatus::Blocked + }; + return std::result::Result::Ok(report); +} + +fn check_post_cached_token_account( + report: &mut crate::solana_ata_stateful::SplAssociatedTokenAccountPostExecutionReport, + cache: &crate::solana_ata_stateful::AccountCache, + address: &kb_model::Pubkey, + token_program: &str, + expected_mint: &kb_model::Pubkey, + expected_owner: &kb_model::Pubkey, + role: &str, +) { + let account = cache.get(address.0.as_str()).and_then(|value| return value.as_ref()); + crate::solana_ata_stateful::push_post_check( + report, + format!("{role}_exists"), + account.is_some(), + format!("{role} {} must exist after confirmed execution", address.0), + ); + if let std::option::Option::Some(account) = account { + crate::solana_ata_stateful::push_post_check( + report, + format!("{role}_token_program_owner"), + account.owner.0 == token_program, + format!("{role} must be owned by the selected Token Program"), + ); + let layout_valid = account.data.len() >= crate::solana_ata_stateful::BASE_TOKEN_ACCOUNT_LEN; + crate::solana_ata_stateful::push_post_check( + report, + format!("{role}_base_layout"), + layout_valid, + format!("{role} must expose the complete base Token Account layout"), + ); + if layout_valid { + let mint = bs58::encode(&account.data[0..32]).into_string(); + let owner = bs58::encode(&account.data[32..64]).into_string(); + crate::solana_ata_stateful::push_post_check( + report, + format!("{role}_mint"), + mint == expected_mint.0, + format!("{role} mint must match {}", expected_mint.0), + ); + crate::solana_ata_stateful::push_post_check( + report, + format!("{role}_wallet_owner"), + owner == expected_owner.0, + format!("{role} owner must match {}", expected_owner.0), + ); + crate::solana_ata_stateful::push_post_check( + report, + format!("{role}_initialized"), + matches!(account.data[108], 1 | 2), + format!("{role} must be initialized or frozen"), + ); + } + } +} + +fn push_post_check( + report: &mut crate::solana_ata_stateful::SplAssociatedTokenAccountPostExecutionReport, + code: impl std::convert::Into, + passed: bool, + message: impl std::convert::Into, +) { + report + .checks + .push(crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulCheck { + code: code.into(), + passed, + message: message.into(), + }); +} + +fn push_post_fact( + report: &mut crate::solana_ata_stateful::SplAssociatedTokenAccountPostExecutionReport, + key: impl std::convert::Into, + value: impl std::convert::Into, +) { + report + .facts + .push(crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulFact { + key: key.into(), + value: value.into(), + }); +} + +fn check_creation_plan_accounts( + report: &mut crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessReport, + plan: &kb_execution_api::PreparedExecutionPlan, + derived_ata: &kb_model::Pubkey, + token_program: &str, +) { + let accounts_exact = match plan.instructions.first() { + std::option::Option::Some(instruction) => { + instruction.accounts.len() == 6 + && instruction.accounts[1].pubkey == derived_ata.clone() + && instruction.accounts[4].pubkey.0 == kb_program_ids::SYSTEM_PROGRAM_ID + && instruction.accounts[5].pubkey.0 == token_program + }, + std::option::Option::None => false, + }; + crate::solana_ata_stateful::push_check( + report, + "creation_plan_accounts_exact", + accounts_exact, + "creation plan must retain derived ATA, System Program and selected Token Program in official positions", + ); +} + +fn check_mint( + report: &mut crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessReport, + cache: &crate::solana_ata_stateful::AccountCache, + mint: &kb_model::Pubkey, + token_program: &str, +) { + let account = cache.get(mint.0.as_str()).and_then(|value| return value.as_ref()); + crate::solana_ata_stateful::push_check( + report, + "mint_exists", + account.is_some(), + format!("mint {} must exist", mint.0), + ); + if let std::option::Option::Some(account) = account { + crate::solana_ata_stateful::push_check( + report, + "mint_token_program_owner", + account.owner.0 == token_program, + format!("mint {} must be owned by selected Token Program", mint.0), + ); + crate::solana_ata_stateful::push_check( + report, + "mint_base_layout", + account.data.len() >= crate::solana_ata_stateful::BASE_MINT_LEN, + format!("mint {} must expose the complete base Mint layout", mint.0), + ); + crate::solana_ata_stateful::push_check( + report, + "mint_initialized", + account.data.get(45) == std::option::Option::Some(&1), + format!("mint {} must be initialized", mint.0), + ); + } +} + +fn check_cached_token_account( + report: &mut crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessReport, + cache: &crate::solana_ata_stateful::AccountCache, + address: &kb_model::Pubkey, + token_program: &str, + expected_mint: &kb_model::Pubkey, + expected_owner: &kb_model::Pubkey, + role: &str, +) { + let account = cache.get(address.0.as_str()).and_then(|value| return value.as_ref()); + crate::solana_ata_stateful::push_check( + report, + format!("{role}_exists"), + account.is_some(), + format!("{role} {} must exist", address.0), + ); + if let std::option::Option::Some(account) = account { + crate::solana_ata_stateful::check_token_account( + report, + account, + token_program, + expected_mint, + expected_owner, + role, + ); + } +} + +fn check_token_account( + report: &mut crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessReport, + account: &kb_rpc::AccountInfoValue, + token_program: &str, + expected_mint: &kb_model::Pubkey, + expected_owner: &kb_model::Pubkey, + role: &str, +) { + crate::solana_ata_stateful::push_check( + report, + format!("{role}_token_program_owner"), + account.owner.0 == token_program, + format!("{role} must be owned by selected Token Program"), + ); + let layout_valid = account.data.len() >= crate::solana_ata_stateful::BASE_TOKEN_ACCOUNT_LEN; + crate::solana_ata_stateful::push_check( + report, + format!("{role}_base_layout"), + layout_valid, + format!("{role} must expose the complete base Token Account layout"), + ); + if layout_valid { + let mint = bs58::encode(&account.data[0..32]).into_string(); + let owner = bs58::encode(&account.data[32..64]).into_string(); + crate::solana_ata_stateful::push_check( + report, + format!("{role}_mint"), + mint == expected_mint.0, + format!("{role} mint must match {}", expected_mint.0), + ); + crate::solana_ata_stateful::push_check( + report, + format!("{role}_wallet_owner"), + owner == expected_owner.0, + format!("{role} owner must match {}", expected_owner.0), + ); + crate::solana_ata_stateful::push_check( + report, + format!("{role}_initialized"), + matches!(account.data[108], 1 | 2), + format!("{role} must be initialized or frozen"), + ); + } +} + +fn push_check( + report: &mut crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessReport, + code: impl std::convert::Into, + passed: bool, + message: impl std::convert::Into, +) { + report + .checks + .push(crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulCheck { + code: code.into(), + passed, + message: message.into(), + }); +} + +fn push_fact( + report: &mut crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessReport, + key: impl std::convert::Into, + value: impl std::convert::Into, +) { + report + .facts + .push(crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulFact { + key: key.into(), + value: value.into(), + }); +} + +#[cfg(test)] +mod tests { + fn pubkey(value: &str) -> kb_model::Pubkey { + return kb_model::Pubkey(value.to_string()); + } + + fn policy( + rent: u64, + signers: std::vec::Vec, + ) -> kb_execution_api::ExecutionPolicy { + return kb_execution_api::ExecutionPolicy { + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(rent), + max_fee_lamports: std::option::Option::Some(10_000), + max_compute_unit_price_micro_lamports: std::option::Option::None, + }, + authorized_signers: signers, + post_execution_validation: kb_execution_api::PostExecutionValidationPolicy { + canonical_insert_required: true, + core_extraction_required: true, + decode_replay_required: true, + materialization_required: true, + }, + ..kb_execution_api::ExecutionPolicy::default() + }; + } + + fn request( + operation: kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation, + rent: u64, + ) -> crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessRequest { + let fee_payer = + crate::solana_ata_stateful::tests::pubkey(kb_program_ids::SYSTEM_PROGRAM_ID); + let wallet = match &operation { + kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::Create { + wallet_owner, + .. + } + | kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::CreateIdempotent { + wallet_owner, + .. + } + | kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::RecoverNested { + wallet_owner, + .. + } => wallet_owner.clone(), + }; + let mut signers = std::vec![fee_payer.clone()]; + if !signers.contains(&wallet) { + signers.push(wallet); + } + return crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessRequest { + query_role: "query".to_string(), + cluster: kb_execution_api::ExecutionCluster::Devnet, + intent: + kb_executor_spl_associated_token_account::SplAssociatedTokenAccountExecutionIntent { + intent_id: "ata-stateful-1".to_string(), + fee_payer, + max_rent_lamports: rent, + policy: crate::solana_ata_stateful::tests::policy(rent, signers.clone()), + operation, + }, + available_signers: signers, + }; + } + + fn mint_account(token_program: &str) -> kb_rpc::AccountInfoValue { + let mut data = std::vec![0; crate::solana_ata_stateful::BASE_MINT_LEN]; + data[45] = 1; + return kb_rpc::AccountInfoValue { + lamports: 1, + owner: kb_model::ProgramId(token_program.to_string()), + executable: false, + rent_epoch: 0, + space: data.len() as u64, + data, + }; + } + + fn token_account( + token_program: &str, + mint: &kb_model::Pubkey, + owner: &kb_model::Pubkey, + ) -> kb_rpc::AccountInfoValue { + let mut data = std::vec![0; crate::solana_ata_stateful::BASE_TOKEN_ACCOUNT_LEN]; + let mint_bytes = bs58::decode(&mint.0) + .into_vec() + .unwrap_or_else(|error| panic!("mint fixture decode failed: {error}")); + let owner_bytes = bs58::decode(&owner.0) + .into_vec() + .unwrap_or_else(|error| panic!("owner fixture decode failed: {error}")); + data[0..32].copy_from_slice(&mint_bytes); + data[32..64].copy_from_slice(&owner_bytes); + data[108] = 1; + return kb_rpc::AccountInfoValue { + lamports: 1, + owner: kb_model::ProgramId(token_program.to_string()), + executable: false, + rent_epoch: 0, + space: data.len() as u64, + data, + }; + } + + fn plan( + request: &crate::solana_ata_stateful::SplAssociatedTokenAccountStatefulReadinessRequest, + ) -> kb_execution_api::PreparedExecutionPlan { + return kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &kb_executor_spl_associated_token_account::SplAssociatedTokenAccountExecutor, + &request.intent, + ) + .unwrap_or_else(|error| panic!("ATA test plan failed: {error}")); + } + + #[test] + fn absent_classic_create_is_ready_with_exact_derived_address_and_rent_ceiling() { + let wallet = crate::solana_ata_stateful::tests::pubkey(kb_program_ids::STAKE_PROGRAM_ID); + let mint = crate::solana_ata_stateful::tests::pubkey(kb_program_ids::VOTE_PROGRAM_ID); + let request = crate::solana_ata_stateful::tests::request( + kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::Create { + wallet_owner: wallet.clone(), + mint: mint.clone(), + token_program: + kb_executor_spl_associated_token_account::SplAssociatedTokenProgram::Classic, + }, + 2_100_000, + ); + let ata = crate::solana_ata_stateful::derive_ata( + &wallet, + &mint, + kb_program_ids::SPL_TOKEN_PROGRAM_ID, + ) + .unwrap_or_else(|error| panic!("ATA derivation failed: {error}")); + let mut cache = crate::solana_ata_stateful::AccountCache::new(); + cache.insert( + mint.0.clone(), + std::option::Option::Some(crate::solana_ata_stateful::tests::mint_account( + kb_program_ids::SPL_TOKEN_PROGRAM_ID, + )), + ); + cache.insert(ata.0, std::option::Option::None); + let report = crate::solana_ata_stateful::evaluate( + &request, + &crate::solana_ata_stateful::tests::plan(&request), + "devnet", + std::option::Option::Some(1), + &cache, + 2_000_000, + 3_000_000, + ) + .unwrap_or_else(|error| panic!("ATA readiness failed: {error}")); + assert_eq!(report.status, crate::SplAssociatedTokenAccountStatefulReadinessStatus::Ready); + } + + #[test] + fn idempotent_existing_account_conflict_is_blocked() { + let wallet = crate::solana_ata_stateful::tests::pubkey(kb_program_ids::STAKE_PROGRAM_ID); + let mint = crate::solana_ata_stateful::tests::pubkey(kb_program_ids::VOTE_PROGRAM_ID); + let request = crate::solana_ata_stateful::tests::request( + kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::CreateIdempotent { + wallet_owner: wallet.clone(), + mint: mint.clone(), + token_program: kb_executor_spl_associated_token_account::SplAssociatedTokenProgram::Token2022, + }, + 2_100_000, + ); + let ata = crate::solana_ata_stateful::derive_ata( + &wallet, + &mint, + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + ) + .unwrap_or_else(|error| panic!("ATA derivation failed: {error}")); + let mut cache = crate::solana_ata_stateful::AccountCache::new(); + cache.insert( + mint.0.clone(), + std::option::Option::Some(crate::solana_ata_stateful::tests::mint_account( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + )), + ); + cache.insert( + ata.0, + std::option::Option::Some(crate::solana_ata_stateful::tests::token_account( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + &mint, + &crate::solana_ata_stateful::tests::pubkey(kb_program_ids::CONFIG_PROGRAM_ID), + )), + ); + let report = crate::solana_ata_stateful::evaluate( + &request, + &crate::solana_ata_stateful::tests::plan(&request), + "devnet", + std::option::Option::Some(1), + &cache, + 2_000_000, + 3_000_000, + ) + .unwrap_or_else(|error| panic!("ATA readiness failed: {error}")); + assert_eq!(report.status, crate::SplAssociatedTokenAccountStatefulReadinessStatus::Blocked); + assert!( + report + .checks + .iter() + .any(|check| return check.code == "idempotent_existing_ata_wallet_owner" + && !check.passed) + ); + } + + #[test] + fn recover_nested_validates_all_three_canonical_accounts_and_signers() { + let wallet = crate::solana_ata_stateful::tests::pubkey(kb_program_ids::STAKE_PROGRAM_ID); + let owner_mint = crate::solana_ata_stateful::tests::pubkey(kb_program_ids::VOTE_PROGRAM_ID); + let nested_mint = + crate::solana_ata_stateful::tests::pubkey(kb_program_ids::CONFIG_PROGRAM_ID); + let token_program = kb_program_ids::SPL_TOKEN_PROGRAM_ID; + let request = crate::solana_ata_stateful::tests::request( + kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::RecoverNested { + wallet_owner: wallet.clone(), + owner_mint: owner_mint.clone(), + nested_mint: nested_mint.clone(), + token_program: kb_executor_spl_associated_token_account::SplAssociatedTokenProgram::Classic, + }, + 0, + ); + let owner_ata = crate::solana_ata_stateful::derive_ata(&wallet, &owner_mint, token_program) + .unwrap_or_else(|error| panic!("owner ATA failed: {error}")); + let nested_ata = + crate::solana_ata_stateful::derive_ata(&owner_ata, &nested_mint, token_program) + .unwrap_or_else(|error| panic!("nested ATA failed: {error}")); + let destination = + crate::solana_ata_stateful::derive_ata(&wallet, &nested_mint, token_program) + .unwrap_or_else(|error| panic!("destination ATA failed: {error}")); + let mut cache = crate::solana_ata_stateful::AccountCache::new(); + cache.insert( + owner_mint.0.clone(), + std::option::Option::Some(crate::solana_ata_stateful::tests::mint_account( + token_program, + )), + ); + cache.insert( + nested_mint.0.clone(), + std::option::Option::Some(crate::solana_ata_stateful::tests::mint_account( + token_program, + )), + ); + cache.insert( + owner_ata.0.clone(), + std::option::Option::Some(crate::solana_ata_stateful::tests::token_account( + token_program, + &owner_mint, + &wallet, + )), + ); + cache.insert( + nested_ata.0, + std::option::Option::Some(crate::solana_ata_stateful::tests::token_account( + token_program, + &nested_mint, + &owner_ata, + )), + ); + cache.insert( + destination.0, + std::option::Option::Some(crate::solana_ata_stateful::tests::token_account( + token_program, + &nested_mint, + &wallet, + )), + ); + let report = crate::solana_ata_stateful::evaluate( + &request, + &crate::solana_ata_stateful::tests::plan(&request), + "devnet", + std::option::Option::Some(1), + &cache, + 2_000_000, + 20_000, + ) + .unwrap_or_else(|error| panic!("ATA readiness failed: {error}")); + assert_eq!(report.status, crate::SplAssociatedTokenAccountStatefulReadinessStatus::Ready); + } + + #[test] + fn creation_postcondition_requires_one_compatible_existing_ata() { + let wallet = crate::solana_ata_stateful::tests::pubkey(kb_program_ids::STAKE_PROGRAM_ID); + let mint = crate::solana_ata_stateful::tests::pubkey(kb_program_ids::VOTE_PROGRAM_ID); + let operation = kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::CreateIdempotent { + wallet_owner: wallet.clone(), + mint: mint.clone(), + token_program: kb_executor_spl_associated_token_account::SplAssociatedTokenProgram::Token2022, + }; + let ata = crate::solana_ata_stateful::derive_ata( + &wallet, + &mint, + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + ) + .unwrap_or_else(|error| panic!("ATA derivation failed: {error}")); + let mut cache = crate::solana_ata_stateful::AccountCache::new(); + cache.insert( + ata.0, + std::option::Option::Some(crate::solana_ata_stateful::tests::token_account( + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + &mint, + &wallet, + )), + ); + let report = crate::solana_ata_stateful::evaluate_post_execution( + kb_execution_api::ExecutionCluster::Devnet, + &operation, + std::option::Option::Some(12), + &cache, + ) + .unwrap_or_else(|error| panic!("ATA postcondition failed: {error}")); + assert_eq!(report.status, crate::SplAssociatedTokenAccountStatefulReadinessStatus::Ready); + } + + #[test] + fn recover_postcondition_requires_nested_closed_and_destination_compatible() { + let wallet = crate::solana_ata_stateful::tests::pubkey(kb_program_ids::STAKE_PROGRAM_ID); + let owner_mint = crate::solana_ata_stateful::tests::pubkey(kb_program_ids::VOTE_PROGRAM_ID); + let nested_mint = + crate::solana_ata_stateful::tests::pubkey(kb_program_ids::CONFIG_PROGRAM_ID); + let token_program = kb_program_ids::SPL_TOKEN_PROGRAM_ID; + let operation = kb_executor_spl_associated_token_account::SplAssociatedTokenAccountOperation::RecoverNested { + wallet_owner: wallet.clone(), + owner_mint: owner_mint.clone(), + nested_mint: nested_mint.clone(), + token_program: kb_executor_spl_associated_token_account::SplAssociatedTokenProgram::Classic, + }; + let owner_ata = crate::solana_ata_stateful::derive_ata(&wallet, &owner_mint, token_program) + .unwrap_or_else(|error| panic!("owner ATA failed: {error}")); + let nested_ata = + crate::solana_ata_stateful::derive_ata(&owner_ata, &nested_mint, token_program) + .unwrap_or_else(|error| panic!("nested ATA failed: {error}")); + let destination = + crate::solana_ata_stateful::derive_ata(&wallet, &nested_mint, token_program) + .unwrap_or_else(|error| panic!("destination ATA failed: {error}")); + let mut cache = crate::solana_ata_stateful::AccountCache::new(); + cache.insert( + owner_ata.0, + std::option::Option::Some(crate::solana_ata_stateful::tests::token_account( + token_program, + &owner_mint, + &wallet, + )), + ); + cache.insert(nested_ata.0, std::option::Option::None); + cache.insert( + destination.0, + std::option::Option::Some(crate::solana_ata_stateful::tests::token_account( + token_program, + &nested_mint, + &wallet, + )), + ); + let report = crate::solana_ata_stateful::evaluate_post_execution( + kb_execution_api::ExecutionCluster::Devnet, + &operation, + std::option::Option::Some(13), + &cache, + ) + .unwrap_or_else(|error| panic!("RecoverNested postcondition failed: {error}")); + assert_eq!(report.status, crate::SplAssociatedTokenAccountStatefulReadinessStatus::Ready); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_elgamal_registry_stateful.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_elgamal_registry_stateful.rs new file mode 100644 index 0000000..2b7f33d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_elgamal_registry_stateful.rs @@ -0,0 +1,406 @@ +// file: kb_pipeline/src/solana_elgamal_registry_stateful.rs +// version: 3 + +//! Contextual SPL ElGamal registry account-state validation and materialization routing. + +use std::str::FromStr; // rust-rules: trait-import + +/// Exact byte length of one SPL ElGamal registry account. +pub const ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES: usize = 64; + +/// One bounded SPL ElGamal registry RPC read request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ElGamalRegistryStatefulReadRequest { + /// Endpoint role used for the HTTP RPC request. + pub query_role: std::string::String, + /// Canonical registry PDA. + pub registry_account: kb_model::Pubkey, + /// Optional minimum RPC context slot. + pub min_context_slot: std::option::Option, +} + +/// One bounded registry RPC read and validated administrative projection. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct ElGamalRegistryStatefulReadResult { + /// Commitment used for the RPC read. + pub commitment: std::string::String, + /// Context slot returned by the endpoint. + pub context_slot: u64, + /// Complete validated registry snapshot. + pub snapshot: crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulSnapshot, +} + +/// Reads, validates, and routes one exact SPL ElGamal registry account. +pub async fn read_elgamal_registry_stateful_snapshot( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadRequest, +) -> kb_core::Result { + if request.query_role.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "SPL ElGamal registry stateful read query_role must not be empty", + )); + } + let config = match kb_rpc::GetAccountInfoConfig::new_with_data( + kb_rpc::RpcCommitmentLevel::Confirmed, + request.min_context_slot, + crate::solana_elgamal_registry_stateful::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let result = match pool + .get_account_info_for_role(request.query_role.as_str(), &request.registry_account, &config) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result( + request, + &result, + ); +} + +/// Validates one complete RPC registry response before parsing and routing. +pub fn materialize_elgamal_registry_account_info_result( + request: &crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadRequest, + result: &kb_rpc::AccountInfoResult, +) -> kb_core::Result { + if let std::option::Option::Some(min_context_slot) = request.min_context_slot { + if result.context.slot < min_context_slot { + return std::result::Result::Err(kb_core::Error::new( + "elgamal_registry_stateful_context_slot_too_old", + format!( + "SPL ElGamal registry context slot {} is below requested minimum {min_context_slot}", + result.context.slot + ), + )); + } + } + let account = match result.account.as_ref() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "elgamal_registry_stateful_account_missing", + format!( + "SPL ElGamal registry account {} does not exist", + request.registry_account.0 + ), + )); + }, + }; + if account.executable { + return std::result::Result::Err(kb_core::Error::new( + "elgamal_registry_stateful_account_executable", + format!( + "SPL ElGamal registry state account {} must not be executable", + request.registry_account.0 + ), + )); + } + if account.owner.0 != kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID { + return std::result::Result::Err(kb_core::Error::new( + "elgamal_registry_stateful_owner_mismatch", + format!( + "SPL ElGamal registry owner must be {}, got {}", + kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID, + account.owner.0 + ), + )); + } + if account.space + != crate::solana_elgamal_registry_stateful::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES as u64 + || account.data.len() + != crate::solana_elgamal_registry_stateful::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES + { + return std::result::Result::Err(kb_core::Error::new( + "elgamal_registry_stateful_account_length_invalid", + format!( + "SPL ElGamal registry account must report and return exactly {} bytes, got space {} and data {}", + crate::solana_elgamal_registry_stateful::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES, + account.space, + account.data.len() + ), + )); + } + let snapshot = match crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot( + request.registry_account.0.as_str(), + account.owner.0.as_str(), + result.context.slot, + account.data.as_slice(), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "elgamal_registry_stateful_projection_failed", + error, + )); + }, + }; + return std::result::Result::Ok( + crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadResult { + commitment: "confirmed".to_string(), + context_slot: result.context.slot, + snapshot, + }, + ); +} + +/// One contextually validated SPL ElGamal registry state snapshot. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct ElGamalRegistryStatefulSnapshot { + /// Canonical registry PDA identity. + pub registry_account_key: std::string::String, + /// Wallet address stored as the registry owner. + pub owner: std::string::String, + /// Context slot associated with the account read. + pub slot: u64, + /// Processor-owned administrative projection. + pub output: kb_materializer_api::MaterializedOutput, +} + +/// Parse, validate the official owner-derived PDA, and materialize one registry snapshot. +pub fn materialize_elgamal_registry_stateful_snapshot( + registry_account_key: &str, + owner_program_id: &str, + slot: u64, + data: &[u8], +) -> std::result::Result< + crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulSnapshot, + String, +> { + if owner_program_id != kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID { + return std::result::Result::Err(format!( + "SPL ElGamal registry snapshot owner must be {}, got {owner_program_id}", + kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID + )); + } + let state = match kb_decoder_spl_elgamal_registry::state::parse_elgamal_registry_state(data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let owner = match solana_pubkey::Pubkey::from_str(state.owner.as_str()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "SPL ElGamal registry owner is not a valid address: {error}" + )); + }, + }; + let program_id = match solana_pubkey::Pubkey::from_str( + kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(format!( + "Configured SPL ElGamal registry program ID is invalid: {error}" + )); + }, + }; + let expected = + spl_elgamal_registry_interface::get_elgamal_registry_address(&owner, &program_id); + if expected.to_string() != registry_account_key { + return std::result::Result::Err(format!( + "SPL ElGamal registry account {registry_account_key} does not match owner-derived PDA {expected}" + )); + } + let output = match kb_materializer_admin::materialize_elgamal_registry_state_snapshot( + registry_account_key, + slot, + &state, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok( + crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulSnapshot { + registry_account_key: registry_account_key.to_string(), + owner: state.owner, + slot, + output, + }, + ); +} + +#[cfg(test)] +mod tests { + use std::str::FromStr; // rust-rules: trait-import + + fn registry_fixture(owner: &solana_pubkey::Pubkey) -> (std::string::String, [u8; 64]) { + let program_id = match solana_pubkey::Pubkey::from_str( + kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => solana_pubkey::Pubkey::default(), + }; + let registry = + spl_elgamal_registry_interface::get_elgamal_registry_address(owner, &program_id); + let mut data = [0u8; 64]; + data[..32].copy_from_slice(owner.as_ref()); + data[32..].copy_from_slice(&[9u8; 32]); + return (registry.to_string(), data); + } + + #[test] + fn exact_owner_derived_registry_pda_routes_one_admin_snapshot() { + let owner = solana_pubkey::Pubkey::new_from_array([7u8; 32]); + let (registry, data) = + crate::solana_elgamal_registry_stateful::tests::registry_fixture(&owner); + let snapshot = + crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot( + registry.as_str(), + kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID, + 55, + &data, + ); + assert_eq!( + snapshot.as_ref().map(|value| return value.owner.clone()), + std::result::Result::Ok(owner.to_string()) + ); + assert_eq!( + snapshot + .as_ref() + .map(|value| return value.output.payload_json["finalAccountStateCaptured"].clone()), + std::result::Result::Ok(serde_json::json!(true)) + ); + } + + #[test] + fn foreign_owner_program_wrong_pda_and_invalid_length_fail_closed() { + let owner = solana_pubkey::Pubkey::new_from_array([8u8; 32]); + let (registry, data) = + crate::solana_elgamal_registry_stateful::tests::registry_fixture(&owner); + let foreign = + crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot( + registry.as_str(), + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + 55, + &data, + ); + assert!(foreign.is_err()); + let wrong = + crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot( + solana_pubkey::Pubkey::new_from_array([10u8; 32]).to_string().as_str(), + kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID, + 55, + &data, + ); + assert!(wrong.is_err()); + let invalid = + crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_stateful_snapshot( + registry.as_str(), + kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID, + 55, + &data[..63], + ); + assert!(invalid.is_err()); + } + + #[test] + fn exact_rpc_registry_routes_after_context_owner_and_length_validation() { + let owner = solana_pubkey::Pubkey::new_from_array([13u8; 32]); + let (registry, data) = + crate::solana_elgamal_registry_stateful::tests::registry_fixture(&owner); + let request = crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadRequest { + query_role: "execution".to_string(), + registry_account: kb_model::Pubkey(registry), + min_context_slot: std::option::Option::Some(70), + }; + let result = kb_rpc::AccountInfoResult { + context: kb_rpc::RpcResponseContext { + slot: 71, + api_version: std::option::Option::None, + }, + account: std::option::Option::Some(kb_rpc::AccountInfoValue { + lamports: 1, + owner: kb_model::ProgramId( + kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID.to_string(), + ), + executable: false, + rent_epoch: 0, + space: 64, + data: data.to_vec(), + }), + }; + let materialized = crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result( + &request, + &result, + ); + assert_eq!( + materialized.as_ref().map(|value| return value.context_slot), + std::result::Result::Ok(71) + ); + assert_eq!( + materialized.as_ref().map(|value| return value.snapshot.owner.clone()), + std::result::Result::Ok(owner.to_string()) + ); + } + + #[test] + fn stale_missing_foreign_executable_and_wrong_length_registry_reads_fail_closed() { + let owner = solana_pubkey::Pubkey::new_from_array([14u8; 32]); + let (registry, data) = + crate::solana_elgamal_registry_stateful::tests::registry_fixture(&owner); + let request = crate::solana_elgamal_registry_stateful::ElGamalRegistryStatefulReadRequest { + query_role: "execution".to_string(), + registry_account: kb_model::Pubkey(registry), + min_context_slot: std::option::Option::Some(80), + }; + let base = kb_rpc::AccountInfoValue { + lamports: 1, + owner: kb_model::ProgramId( + kb_program_ids::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID.to_string(), + ), + executable: false, + rent_epoch: 0, + space: 64, + data: data.to_vec(), + }; + let stale = kb_rpc::AccountInfoResult { + context: kb_rpc::RpcResponseContext { + slot: 79, + api_version: std::option::Option::None, + }, + account: std::option::Option::Some(base.clone()), + }; + assert!(crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result(&request, &stale).is_err()); + let missing = kb_rpc::AccountInfoResult { + context: kb_rpc::RpcResponseContext { + slot: 80, + api_version: std::option::Option::None, + }, + account: std::option::Option::None, + }; + assert!(crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result(&request, &missing).is_err()); + let mut foreign = base.clone(); + foreign.owner = kb_model::ProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string()); + let foreign = kb_rpc::AccountInfoResult { + context: kb_rpc::RpcResponseContext { + slot: 80, + api_version: std::option::Option::None, + }, + account: std::option::Option::Some(foreign), + }; + assert!(crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result(&request, &foreign).is_err()); + let mut executable = base.clone(); + executable.executable = true; + let executable = kb_rpc::AccountInfoResult { + context: kb_rpc::RpcResponseContext { + slot: 80, + api_version: std::option::Option::None, + }, + account: std::option::Option::Some(executable), + }; + assert!(crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result(&request, &executable).is_err()); + let mut wrong_length = base; + wrong_length.data.pop(); + let wrong_length = kb_rpc::AccountInfoResult { + context: kb_rpc::RpcResponseContext { + slot: 80, + api_version: std::option::Option::None, + }, + account: std::option::Option::Some(wrong_length), + }; + assert!(crate::solana_elgamal_registry_stateful::materialize_elgamal_registry_account_info_result(&request, &wrong_length).is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_execution.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_execution.rs new file mode 100644 index 0000000..ccaa28e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_execution.rs @@ -0,0 +1,1448 @@ +// file: kb_pipeline/src/solana_execution.rs +// version: 4 + +//! Devnet Solana execution orchestration with canonical post-validation. + +/// Progress severity for one Solana execution orchestration. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum SolanaExecutionProgressLevel { + /// Diagnostic detail. + Debug, + /// Normal execution information. + Info, + /// Recoverable issue or non-terminal limitation. + Warning, + /// Terminal execution or validation failure. + Error, +} + +impl crate::solana_execution::SolanaExecutionProgressLevel { + /// Returns the stable lowercase level code. + pub fn code(&self) -> &'static str { + return match self { + Self::Debug => "debug", + Self::Info => "info", + Self::Warning => "warn", + Self::Error => "error", + }; + } +} + +/// One operator-visible execution progress event. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct SolanaExecutionProgressEvent { + /// UTC timestamp rendered in RFC 3339. + pub timestamp: std::string::String, + /// Severity. + pub level: crate::solana_execution::SolanaExecutionProgressLevel, + /// Stable execution stage code. + pub stage: std::string::String, + /// Human-readable message. + pub message: std::string::String, + /// Transaction signature when available. + pub signature: std::option::Option, +} + +impl crate::solana_execution::SolanaExecutionProgressEvent { + fn new( + level: crate::solana_execution::SolanaExecutionProgressLevel, + stage: impl std::convert::Into, + message: impl std::convert::Into, + signature: std::option::Option, + ) -> Self { + return Self { + timestamp: chrono::Utc::now().to_rfc3339(), + level, + stage: stage.into(), + message: message.into(), + signature, + }; + } +} + +/// Composite observer used by execution, backfill, extraction and decode replay. +pub trait SolanaExecutionObserver: + crate::BackfillObserver + crate::CoreExtractionObserver + crate::DecodeReplayObserver + Sync +{ + /// Receives one execution-specific progress event. + fn on_execution_progress(&self, event: &crate::SolanaExecutionProgressEvent); + /// Returns true when execution orchestration should stop before the next stage. + fn is_execution_cancelled(&self) -> bool; +} + +/// No-op observer suitable for CLI tools and opt-in integration tests. +#[derive(Clone, Copy, Debug, Default)] +pub struct NoopSolanaExecutionObserver; + +impl crate::BackfillObserver for crate::solana_execution::NoopSolanaExecutionObserver { + fn on_progress(&self, _event: &crate::BackfillProgressEvent) { + return; + } + + fn is_cancelled(&self) -> bool { + return false; + } +} + +impl crate::CoreExtractionObserver for crate::solana_execution::NoopSolanaExecutionObserver { + fn on_progress(&self, _event: &crate::CoreExtractionProgressEvent) { + return; + } + + fn is_cancelled(&self) -> bool { + return false; + } +} + +impl crate::DecodeReplayObserver for crate::solana_execution::NoopSolanaExecutionObserver { + fn on_progress(&self, _event: &crate::DecodeReplayProgressEvent) { + return; + } + + fn is_cancelled(&self) -> bool { + return false; + } +} + +impl crate::solana_execution::SolanaExecutionObserver + for crate::solana_execution::NoopSolanaExecutionObserver +{ + fn on_execution_progress(&self, _event: &crate::SolanaExecutionProgressEvent) { + return; + } + + fn is_execution_cancelled(&self) -> bool { + return false; + } +} + +/// Complete request for one bounded System Program transfer on Devnet. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DevnetSystemTransferRequest { + /// Stable caller-provided identifier used in tracing and plan correlation. + pub intent_id: std::string::String, + /// Endpoint role used for cluster, blockhash, fee, balance, airdrop and hydration calls. + pub query_role: std::string::String, + /// Endpoint role used for simulation, submission and signature status polling. + pub transaction_role: std::string::String, + /// Recipient public key. + pub recipient: kb_model::Pubkey, + /// Lamports transferred by the System instruction. + pub lamports: u64, + /// Maximum faucet airdrop requested only when the source balance is insufficient. + pub airdrop_lamports: u64, + /// Explicitly authorizes signing and submission instead of returning after simulation. + pub submit: bool, + /// Explicit operator confirmation required by the active profile when configured. + pub operator_confirmed: bool, + /// Number of `getTransaction` retries after the first post-confirmation attempt. + pub post_validation_max_retries: u32, + /// Replaces existing core/decode outputs for the exact submitted signature. + pub force_post_validation_replay: bool, + /// Runs compatible materializers after decode replay. + pub materialize_after_decode: bool, +} + +impl crate::solana_execution::DevnetSystemTransferRequest { + /// Creates a conservative simulation-only request. + pub fn new( + intent_id: impl std::convert::Into, + recipient: kb_model::Pubkey, + lamports: u64, + ) -> Self { + return Self { + intent_id: intent_id.into(), + query_role: "http_queries".to_string(), + transaction_role: "http_transactions".to_string(), + recipient, + lamports, + airdrop_lamports: 0, + submit: false, + operator_confirmed: false, + post_validation_max_retries: 10, + force_post_validation_replay: false, + materialize_after_decode: false, + }; + } + + /// Validates request-local values independently from one profile. + pub fn validate(&self) -> kb_core::Result<()> { + if self.intent_id.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "devnet execution intent id must not be empty", + )); + } + if self.query_role.trim().is_empty() || self.transaction_role.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "devnet execution endpoint roles must not be empty", + )); + } + let recipient_result = kb_rpc::validate_solana_pubkey_text( + self.recipient.0.as_str(), + "devnet System transfer recipient", + ); + if let std::result::Result::Err(error) = recipient_result { + return std::result::Result::Err(error); + } + if self.lamports == 0 { + return std::result::Result::Err(kb_core::Error::config( + "devnet System transfer lamports must be greater than zero", + )); + } + if self.post_validation_max_retries > 20 { + return std::result::Result::Err(kb_core::Error::config( + "post-execution getTransaction retries must not exceed 20", + )); + } + return std::result::Result::Ok(()); + } +} + +/// Complete result of one Devnet transfer orchestration. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DevnetSystemTransferSummary { + /// Profile used by the orchestration. + pub profile_name: std::string::String, + /// Classified cluster. + pub cluster: kb_execution_api::ExecutionCluster, + /// Genesis hash returned by the selected endpoint. + pub genesis_hash: std::string::String, + /// Non-secret persistent wallet description. + pub wallet: kb_wallet::WalletSummary, + /// Transfer recipient. + pub recipient: kb_model::Pubkey, + /// Whether the recipient account existed before execution. + pub recipient_existed_before: bool, + /// Recipient balance before execution when the account existed. + pub recipient_balance_before_lamports: std::option::Option, + /// Minimum lamports required when creating a zero-data recipient account. + pub recipient_minimum_balance_lamports: u64, + /// Source wallet balance before an optional airdrop. + pub balance_before_lamports: u64, + /// Source wallet balance after optional funding. + pub balance_after_funding_lamports: u64, + /// Faucet signature when an airdrop was required. + pub airdrop_signature: std::option::Option, + /// Airdrop confirmation when an airdrop was required. + pub airdrop_confirmation: std::option::Option, + /// Exact prepared plan. + pub plan: kb_execution_api::PreparedExecutionPlan, + /// Recent blockhash and expiration height used by the transaction. + pub latest_blockhash: kb_rpc::LatestBlockhashResult, + /// Fee estimate for the exact compiled message. + pub fee: kb_rpc::FeeForMessageResult, + /// Exact simulation result bound to the compiled message. + pub simulation: kb_execution_api::ExecutionSimulationResult, + /// Submission result when `submit` was enabled. + pub send_result: std::option::Option, + /// Confirmation result when the transaction was submitted. + pub confirmation: std::option::Option, + /// Canonical hydration result after an observed transaction reached a terminal status. + pub backfill: std::option::Option, + /// Core extraction result for the exact signature. + pub core_extraction: std::option::Option, + /// Contextual decode result for the exact signature. + pub decode_replay: std::option::Option, + /// Aggregated post-execution validation diagnostic. + pub post_execution: std::option::Option, +} + +/// Executes a bounded Devnet System transfer and its canonical replay validation. +#[allow(clippy::too_many_arguments)] +pub async fn execute_devnet_system_transfer( + http_pool: &kb_rpc::HttpEndpointPool, + store: &S, + profile: &kb_config::ProfileConfig, + workspace_root: &std::path::Path, + request: &crate::DevnetSystemTransferRequest, + decoders: &[std::sync::Arc], + materializers: &[std::sync::Arc], + observer: &O, +) -> kb_core::Result +where + S: kb_store_core::RawTransactionStore + + kb_store_core::CoreExtractionStore + + kb_store_core::DecodePipelineStore + + Sync, + O: crate::SolanaExecutionObserver, +{ + let request_result = request.validate(); + if let std::result::Result::Err(error) = request_result { + return std::result::Result::Err(error); + } + let profile_result = crate::solana_execution::validate_devnet_profile(profile, request); + if let std::result::Result::Err(error) = profile_result { + return std::result::Result::Err(error); + } + let cancellation_result = crate::solana_execution::ensure_not_cancelled(observer, "validate"); + if let std::result::Result::Err(error) = cancellation_result { + return std::result::Result::Err(error); + } + crate::solana_execution::emit( + observer, + crate::SolanaExecutionProgressLevel::Info, + "cluster_check", + format!("checking Devnet cluster for profile {}", profile.name), + std::option::Option::None, + ); + let genesis = match http_pool.get_genesis_hash_for_role(request.query_role.as_str()).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if genesis.classified_cluster + != std::option::Option::Some(kb_execution_api::ExecutionCluster::Devnet) + { + return std::result::Result::Err(kb_core::Error::new( + "execution_cluster_mismatch", + format!( + "expected Devnet genesis hash but endpoint returned {} classified as {:?}", + genesis.genesis_hash, genesis.classified_cluster + ), + )); + } + let cancellation_result = crate::solana_execution::ensure_not_cancelled(observer, "wallet"); + if let std::result::Result::Err(error) = cancellation_result { + return std::result::Result::Err(error); + } + let wallet = match crate::solana_execution::load_profile_wallet(profile, workspace_root).await { + std::result::Result::Ok(wallet) => wallet, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let wallet_summary = wallet.summary(); + let source_pubkey = kb_model::Pubkey(wallet_summary.public_key.clone()); + if source_pubkey == request.recipient { + return std::result::Result::Err(kb_core::Error::config( + "devnet System transfer recipient must differ from the source wallet", + )); + } + let account_info_config = kb_rpc::GetAccountInfoConfig::confirmed(); + let recipient_account = match http_pool + .get_account_info_for_role( + request.query_role.as_str(), + &request.recipient, + &account_info_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let recipient_existed_before = recipient_account.account.is_some(); + let recipient_balance_before_lamports = + recipient_account.account.as_ref().map(|account| return account.lamports); + let recipient_minimum_balance_lamports = if recipient_existed_before { + 0 + } else { + let rent_config = kb_rpc::GetMinimumBalanceForRentExemptionConfig::confirmed(); + let rent = match http_pool + .get_minimum_balance_for_rent_exemption_for_role( + request.query_role.as_str(), + 0, + &rent_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + rent.minimum_balance_lamports + }; + let recipient_validation = crate::solana_execution::validate_recipient_transfer_amount( + recipient_existed_before, + request.lamports, + recipient_minimum_balance_lamports, + ); + if let std::result::Result::Err(error) = recipient_validation { + return std::result::Result::Err(error); + } + let balance_config = kb_rpc::GetBalanceConfig::confirmed(); + let balance_before = match http_pool + .get_balance_for_role(request.query_role.as_str(), &source_pubkey, &balance_config) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let required_balance = match request.lamports.checked_add(profile.execution.max_fee_lamports) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::config( + "devnet transfer amount plus fee ceiling overflows u64", + )); + }, + }; + let funding = match crate::solana_execution::ensure_devnet_funding( + http_pool, + profile, + request, + observer, + &source_pubkey, + balance_before.lamports, + required_balance, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let cancellation_result = crate::solana_execution::ensure_not_cancelled(observer, "plan"); + if let std::result::Result::Err(error) = cancellation_result { + return std::result::Result::Err(error); + } + let plan = + match crate::solana_execution::build_transfer_plan(profile, request, source_pubkey.clone()) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let plan_evaluation = + match kb_execution_safety::ExecutionSafetyChecker.evaluate_prepared_plan(&plan) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if plan_evaluation.decision == kb_execution_safety::ExecutionSafetyDecision::Deny { + return std::result::Result::Err(kb_core::Error::new( + "execution_plan_denied", + crate::solana_execution::violation_message(plan_evaluation.violations.as_slice()), + )); + } + let latest_config = kb_rpc::GetLatestBlockhashConfig::confirmed(); + let latest_blockhash = match http_pool + .get_latest_blockhash_for_role(request.query_role.as_str(), &latest_config) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let unsigned = match kb_execution_solana::build_legacy_transaction( + &plan, + latest_blockhash.blockhash.as_str(), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let fee_config = kb_rpc::GetFeeForMessageConfig::new( + kb_rpc::RpcCommitmentLevel::Confirmed, + std::option::Option::Some(latest_blockhash.context.slot), + ); + let message_base64 = unsigned.message_base64(); + let fee = match http_pool + .get_fee_for_message_for_role( + request.query_role.as_str(), + message_base64.as_str(), + &fee_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if fee.fee_lamports.is_none() { + return std::result::Result::Err(kb_core::Error::new( + "execution_fee_unavailable", + "getFeeForMessage returned null for the selected recent blockhash", + )); + } + let unsigned_base64 = match unsigned.transaction_base64() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let simulation_config = match kb_rpc::SimulateTransactionConfig::new( + kb_rpc::RpcCommitmentLevel::Confirmed, + false, + false, + std::option::Option::Some(latest_blockhash.context.slot), + true, + std::option::Option::None, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_execution::emit( + observer, + crate::SolanaExecutionProgressLevel::Info, + "simulation", + format!("simulating exact message {}", unsigned.message_hash()), + std::option::Option::None, + ); + let simulation_rpc = match http_pool + .simulate_transaction_for_role( + request.transaction_role.as_str(), + unsigned_base64.as_str(), + &simulation_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let blockhash_age_slots = + simulation_rpc.context.slot.saturating_sub(latest_blockhash.context.slot); + let simulation = simulation_rpc.to_execution_result( + kb_execution_api::ExecutionCluster::Devnet, + kb_execution_api::ExecutionBlockhashKind::Latest, + std::option::Option::Some(blockhash_age_slots), + std::option::Option::None, + std::option::Option::None, + std::option::Option::Some(&fee), + ); + let evidence = unsigned.bind_simulation(simulation.clone()); + let mut summary = crate::DevnetSystemTransferSummary { + profile_name: profile.name.clone(), + cluster: kb_execution_api::ExecutionCluster::Devnet, + genesis_hash: genesis.genesis_hash, + wallet: wallet_summary, + recipient: request.recipient.clone(), + recipient_existed_before, + recipient_balance_before_lamports, + recipient_minimum_balance_lamports, + balance_before_lamports: balance_before.lamports, + balance_after_funding_lamports: funding.balance_after_lamports, + airdrop_signature: funding.airdrop_signature, + airdrop_confirmation: funding.airdrop_confirmation, + plan, + latest_blockhash, + fee, + simulation, + send_result: std::option::Option::None, + confirmation: std::option::Option::None, + backfill: std::option::Option::None, + core_extraction: std::option::Option::None, + decode_replay: std::option::Option::None, + post_execution: std::option::Option::None, + }; + if !request.submit { + return std::result::Result::Ok(summary); + } + if !summary.simulation.success { + return std::result::Result::Err(kb_core::Error::new( + "execution_simulation_failed", + crate::solana_execution::simulation_failure_message(&summary.simulation), + )); + } + let cancellation_result = crate::solana_execution::ensure_not_cancelled(observer, "sign"); + if let std::result::Result::Err(error) = cancellation_result { + return std::result::Result::Err(error); + } + let signed = match evidence.message_hash() == unsigned.message_hash() { + true => match unsigned.sign_after_simulation(&evidence, &[wallet.as_signer()]) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }, + false => { + return std::result::Result::Err(kb_core::Error::new( + "execution_simulation_message_mismatch", + "simulation evidence does not match the unsigned transaction", + )); + }, + }; + let verify_result = signed.verify_signatures(); + if let std::result::Result::Err(error) = verify_result { + return std::result::Result::Err(error); + } + let signature = signed.primary_signature().clone(); + let mut post_execution = kb_execution_api::PostExecutionDiagnostic { + signature: signature.clone(), + canonical_inserted: false, + core_extracted: false, + decode_replayed: false, + materialized: false, + diagnostics: std::vec::Vec::new(), + }; + let send_config = match kb_rpc::SendTransactionConfig::from_execution_config( + &profile.execution, + std::option::Option::Some(summary.latest_blockhash.context.slot), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_execution::emit( + observer, + crate::SolanaExecutionProgressLevel::Info, + "send", + "submitting signed Devnet transaction", + std::option::Option::Some(signature.0.clone()), + ); + let signed_transaction_base64 = signed.transaction_base64(); + let sent = match http_pool + .send_transaction_for_role( + request.transaction_role.as_str(), + signed_transaction_base64.as_str(), + &signature, + &send_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + post_execution + .diagnostics + .push(format!("sendTransaction failed for locally signed transaction: {error}")); + summary.post_execution = std::option::Option::Some(post_execution); + tracing::error!( + target: crate::TRACING_TARGET, + action = "execute_devnet_system_transfer", + stage = "send", + intent_id = %request.intent_id, + signature = %signature.0, + error_message = %error, + "Devnet System transfer submission failed" + ); + return std::result::Result::Ok(summary); + }, + }; + summary.send_result = std::option::Option::Some( + sent.to_execution_result(kb_execution_api::ExecutionCluster::Devnet), + ); + let confirmation_config = match kb_rpc::ConfirmTransactionConfig::from_execution_config( + &profile.execution, + std::option::Option::Some(summary.latest_blockhash.last_valid_block_height), + std::option::Option::Some(summary.latest_blockhash.context.slot), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + post_execution.diagnostics.push(format!( + "confirmation policy could not be created after submission: {error}" + )); + summary.post_execution = std::option::Option::Some(post_execution); + return std::result::Result::Ok(summary); + }, + }; + let confirmation = match http_pool + .confirm_transaction_for_roles( + request.transaction_role.as_str(), + request.query_role.as_str(), + kb_execution_api::ExecutionCluster::Devnet, + &signature, + &confirmation_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + post_execution + .diagnostics + .push(format!("confirmation polling failed after transaction submission: {error}")); + summary.post_execution = std::option::Option::Some(post_execution); + tracing::error!( + target: crate::TRACING_TARGET, + action = "execute_devnet_system_transfer", + stage = "confirmation", + intent_id = %request.intent_id, + signature = %signature.0, + error_message = %error, + "Devnet System transfer confirmation failed" + ); + return std::result::Result::Ok(summary); + }, + }; + let confirmation_status = confirmation.status; + summary.confirmation = std::option::Option::Some(confirmation); + if confirmation_status == kb_execution_api::ExecutionConfirmationStatus::Expired + || confirmation_status == kb_execution_api::ExecutionConfirmationStatus::TimedOut + { + post_execution.diagnostics.push(format!( + "transaction was submitted but post-validation stopped at confirmation status {:?}", + confirmation_status + )); + summary.post_execution = std::option::Option::Some(post_execution); + return std::result::Result::Ok(summary); + } + let cancellation_result = + crate::solana_execution::ensure_not_cancelled(observer, "canonical_insert"); + if let std::result::Result::Err(error) = cancellation_result { + post_execution + .diagnostics + .push(format!("post-execution validation stopped after submission: {error}")); + summary.post_execution = std::option::Option::Some(post_execution); + return std::result::Result::Ok(summary); + } + let backfill = match crate::solana_execution::execute_post_validation_backfill( + http_pool, store, profile, request, observer, &signature, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + post_execution + .diagnostics + .push(format!("canonical hydration failed after submission: {error}")); + summary.post_execution = std::option::Option::Some(post_execution); + return std::result::Result::Ok(summary); + }, + }; + let canonical_available = crate::solana_execution::canonical_backfill_available(&backfill); + post_execution.canonical_inserted = canonical_available; + summary.backfill = std::option::Option::Some(backfill); + if !canonical_available { + post_execution.diagnostics.push( + "confirmed transaction was not available for canonical post-validation".to_string(), + ); + summary.post_execution = std::option::Option::Some(post_execution); + return std::result::Result::Ok(summary); + } + let extraction_request = crate::CoreExtractionRequest { + source: crate::CoreExtractionSource::Signatures(std::vec![signature.0.clone()]), + limit: 1, + max_concurrent_extractions: 1, + force_replay: request.force_post_validation_replay, + }; + let extraction = + match crate::execute_core_extraction(store, &extraction_request, observer).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + post_execution + .diagnostics + .push(format!("core extraction failed after submission: {error}")); + summary.post_execution = std::option::Option::Some(post_execution); + return std::result::Result::Ok(summary); + }, + }; + let core_extracted = extraction.failed == 0 + && !extraction.cancelled + && extraction.selected == 1 + && extraction.extracted.saturating_add(extraction.skipped) >= 1; + post_execution.core_extracted = core_extracted; + summary.core_extraction = std::option::Option::Some(extraction); + if !core_extracted { + post_execution + .diagnostics + .push("canonical transaction did not complete core extraction".to_string()); + summary.post_execution = std::option::Option::Some(post_execution); + return std::result::Result::Ok(summary); + } + let program_ids = summary + .plan + .instructions + .iter() + .map(|instruction| return instruction.program_id.0.clone()) + .collect::>(); + let selection = match kb_store_core::DecodeSelectionFilter::new( + std::vec![signature.0.clone()], + std::vec![ + kb_store_core::CoreInstructionProcessingState::Pending, + kb_store_core::CoreInstructionProcessingState::Failed, + kb_store_core::CoreInstructionProcessingState::ReplayRequested, + ], + std::option::Option::None, + std::option::Option::None, + program_ids, + std::vec::Vec::new(), + false, + 32, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + post_execution.diagnostics.push(format!( + "decode selection could not be constructed after submission: {error}" + )); + summary.post_execution = std::option::Option::Some(post_execution); + return std::result::Result::Ok(summary); + }, + }; + let decode_request = crate::DecodeReplayRequest { + campaign_id: crate::new_decode_campaign_id(), + selection, + decoder_names: std::vec::Vec::new(), + dispatch_policy: crate::DecodeDispatchPolicy::HighestPriority, + max_concurrent_inputs: 1, + force_replay: request.force_post_validation_replay, + force_replay_all_matching: false, + materialize_after_decode: request.materialize_after_decode, + }; + let decode = match crate::execute_decode_replay( + store, + &decode_request, + decoders, + materializers, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + post_execution + .diagnostics + .push(format!("decode replay failed after submission: {error}")); + summary.post_execution = std::option::Option::Some(post_execution); + return std::result::Result::Ok(summary); + }, + }; + let processors_clean = decode.processors.iter().all(|processor| { + return processor.failed == 0 && processor.unsupported == 0; + }); + let decoded_observations = + decode.processors.iter().map(|processor| return processor.decoded).sum::(); + let decode_replayed = decode.failed_inputs == 0 + && decode.unmatched == 0 + && !decode.cancelled + && decode.completed >= 1 + && processors_clean + && decoded_observations >= 1; + let materialized = request.materialize_after_decode + && decode_replayed + && decode + .processors + .iter() + .all(|processor| return processor.materialization_refused == 0); + post_execution.decode_replayed = decode_replayed; + post_execution.materialized = materialized; + summary.decode_replay = std::option::Option::Some(decode); + if confirmation_status == kb_execution_api::ExecutionConfirmationStatus::Failed { + post_execution.diagnostics.push( + "transaction was committed with an on-chain error and replayed as a failed intent" + .to_string(), + ); + } else if !decode_replayed { + post_execution.diagnostics.push( + "transaction reached core storage but decode replay did not complete cleanly" + .to_string(), + ); + } else if request.materialize_after_decode && !materialized { + post_execution.diagnostics.push( + "decode replay completed but requested materialization was refused or failed" + .to_string(), + ); + } else { + post_execution.diagnostics.push( + "transaction completed canonical insertion, core extraction and decode replay" + .to_string(), + ); + } + let canonical_inserted = post_execution.canonical_inserted; + let core_extracted = post_execution.core_extracted; + let decode_replayed = post_execution.decode_replayed; + let materialized = post_execution.materialized; + summary.post_execution = std::option::Option::Some(post_execution); + tracing::info!( + target: crate::TRACING_TARGET, + action = "execute_devnet_system_transfer", + intent_id = %request.intent_id, + signature = %signature.0, + confirmation_status = ?confirmation_status, + canonical_inserted, + core_extracted, + decode_replayed, + materialized, + "Devnet System transfer orchestration completed" + ); + return std::result::Result::Ok(summary); +} + +async fn execute_post_validation_backfill( + http_pool: &kb_rpc::HttpEndpointPool, + store: &S, + profile: &kb_config::ProfileConfig, + request: &crate::DevnetSystemTransferRequest, + observer: &O, + signature: &kb_model::Signature, +) -> kb_core::Result +where + S: kb_store_core::RawTransactionStore + Sync, + O: crate::SolanaExecutionObserver, +{ + let mut retry_index = 0_u32; + loop { + let backfill_request = crate::BackfillRequest { + role: request.query_role.clone(), + commitment: "confirmed".to_string(), + source: crate::BackfillSource::ExplicitSignatures(std::vec![signature.0.clone()]), + page_size: 1, + max_pages: 1, + max_concurrent_requests: 1, + max_retries: 0, + }; + let backfill = + match crate::execute_http_backfill(http_pool, store, &backfill_request, observer).await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if crate::solana_execution::canonical_backfill_available(&backfill) + || retry_index >= request.post_validation_max_retries + || observer.is_execution_cancelled() + { + return std::result::Result::Ok(backfill); + } + retry_index = retry_index.saturating_add(1); + let delay_ms = std::cmp::max(profile.execution.confirmation_poll_interval_ms, 500); + crate::solana_execution::emit( + observer, + crate::SolanaExecutionProgressLevel::Warning, + "canonical_insert", + format!( + "getTransaction is not available yet for {}; retry {}/{} after {} ms", + signature.0, retry_index, request.post_validation_max_retries, delay_ms + ), + std::option::Option::Some(signature.0.clone()), + ); + tokio::time::sleep(std::time::Duration::from_millis(delay_ms)).await; + } +} + +fn canonical_backfill_available(summary: &crate::BackfillSummary) -> bool { + return summary.failed == 0 + && summary.missing == 0 + && summary.candidates_completed == 1 + && summary.candidates_cancelled == 0 + && summary.candidates_not_started == 0 + && summary + .canonical_inserted + .saturating_add(summary.canonical_skipped) + .saturating_add(summary.existing_skipped) + >= 1; +} + +fn validate_recipient_transfer_amount( + recipient_exists: bool, + lamports: u64, + minimum_balance_lamports: u64, +) -> kb_core::Result<()> { + if !recipient_exists && lamports < minimum_balance_lamports { + return std::result::Result::Err(kb_core::Error::new( + "execution_recipient_rent_exemption_required", + format!( + "recipient account does not exist: transfer requires at least {minimum_balance_lamports} lamports for a zero-data rent-exempt account, requested {lamports}" + ), + )); + } + return std::result::Result::Ok(()); +} + +pub(crate) fn simulation_failure_message( + simulation: &kb_execution_api::ExecutionSimulationResult, +) -> std::string::String { + let error = match simulation.error.as_deref() { + std::option::Option::Some(value) => value, + std::option::Option::None => "runtime returned no structured error", + }; + let retained_logs = simulation.logs.iter().take(20).cloned().collect::>(); + if retained_logs.is_empty() { + return format!("exact transaction simulation failed: error={error}; logs=[]"); + } + return format!( + "exact transaction simulation failed: error={error}; logs=[{}]", + retained_logs.join(" | ") + ); +} + +#[derive(Clone, Debug, Eq, PartialEq)] +struct FundingResult { + balance_after_lamports: u64, + airdrop_signature: std::option::Option, + airdrop_confirmation: std::option::Option, +} + +fn validate_devnet_profile( + profile: &kb_config::ProfileConfig, + request: &crate::DevnetSystemTransferRequest, +) -> kb_core::Result<()> { + if profile.wallet.cluster != "devnet" { + return std::result::Result::Err(kb_core::Error::config(format!( + "profile {} wallet cluster must be devnet for this orchestration", + profile.name + ))); + } + if !profile.wallet.temporary_wallet_enabled || !profile.wallet.temporary_wallet_persist { + return std::result::Result::Err(kb_core::Error::config( + "Devnet execution requires an enabled persistent temporary wallet", + )); + } + if !profile.execution.require_simulation { + return std::result::Result::Err(kb_core::Error::config( + "Devnet execution requires simulation in the active profile", + )); + } + if request.lamports > profile.execution.devnet_max_spend_lamports { + return std::result::Result::Err(kb_core::Error::config(format!( + "requested transfer {} exceeds Devnet spend ceiling {}", + request.lamports, profile.execution.devnet_max_spend_lamports + ))); + } + if request.airdrop_lamports > profile.execution.devnet_airdrop_max_lamports { + return std::result::Result::Err(kb_core::Error::config(format!( + "requested airdrop {} exceeds Devnet airdrop ceiling {}", + request.airdrop_lamports, profile.execution.devnet_airdrop_max_lamports + ))); + } + if request.submit && !profile.wallet.devnet_send_enabled { + return std::result::Result::Err(kb_core::Error::config( + "Devnet transaction submission is disabled by the wallet profile", + )); + } + if request.submit + && profile.execution.require_operator_confirmation + && !request.operator_confirmed + { + return std::result::Result::Err(kb_core::Error::config( + "Devnet submission requires explicit operator confirmation", + )); + } + return std::result::Result::Ok(()); +} + +pub(crate) async fn load_profile_wallet( + profile: &kb_config::ProfileConfig, + workspace_root: &std::path::Path, +) -> kb_core::Result { + let configured = std::path::PathBuf::from(profile.wallet.wallet_dir.as_str()); + let directory = if configured.is_absolute() { + configured + } else { + workspace_root.join(configured) + }; + let store = match kb_wallet::TemporaryWalletStore::new(directory) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let alias = match kb_wallet::WalletAlias::parse(profile.wallet.temporary_wallet_alias.clone()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return store.load_or_create(alias).await; +} + +async fn ensure_devnet_funding( + http_pool: &kb_rpc::HttpEndpointPool, + profile: &kb_config::ProfileConfig, + request: &crate::DevnetSystemTransferRequest, + observer: &O, + source_pubkey: &kb_model::Pubkey, + balance_before_lamports: u64, + required_balance_lamports: u64, +) -> kb_core::Result +where + O: crate::SolanaExecutionObserver, +{ + if balance_before_lamports >= required_balance_lamports { + return std::result::Result::Ok(FundingResult { + balance_after_lamports: balance_before_lamports, + airdrop_signature: std::option::Option::None, + airdrop_confirmation: std::option::Option::None, + }); + } + if !request.submit { + return std::result::Result::Err(kb_core::Error::new( + "execution_balance_insufficient", + format!( + "wallet balance {} is below required {} and simulation-only mode cannot request a funding transaction", + balance_before_lamports, required_balance_lamports + ), + )); + } + if request.airdrop_lamports == 0 { + return std::result::Result::Err(kb_core::Error::new( + "execution_airdrop_required", + format!( + "wallet balance {} is below required {}; configure a bounded Devnet airdrop", + balance_before_lamports, required_balance_lamports + ), + )); + } + let projected_balance = match balance_before_lamports.checked_add(request.airdrop_lamports) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::config( + "Devnet balance plus airdrop overflows u64", + )); + }, + }; + if projected_balance < required_balance_lamports { + return std::result::Result::Err(kb_core::Error::config(format!( + "configured airdrop would raise balance only to {}, below required {}", + projected_balance, required_balance_lamports + ))); + } + crate::solana_execution::emit( + observer, + crate::SolanaExecutionProgressLevel::Info, + "airdrop", + format!("requesting {} Devnet lamports for temporary wallet", request.airdrop_lamports), + std::option::Option::None, + ); + let airdrop = match http_pool + .request_airdrop_for_role( + request.query_role.as_str(), + source_pubkey, + request.airdrop_lamports, + &kb_rpc::RequestAirdropConfig::confirmed(), + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let confirmation_config = match kb_rpc::ConfirmTransactionConfig::from_execution_config( + &profile.execution, + std::option::Option::None, + std::option::Option::None, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let confirmation = match http_pool + .confirm_transaction_for_roles( + request.transaction_role.as_str(), + request.query_role.as_str(), + kb_execution_api::ExecutionCluster::Devnet, + &airdrop.signature, + &confirmation_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if confirmation.status != kb_execution_api::ExecutionConfirmationStatus::Confirmed + && confirmation.status != kb_execution_api::ExecutionConfirmationStatus::Finalized + { + return std::result::Result::Err(kb_core::Error::new( + "execution_airdrop_not_confirmed", + format!("Devnet airdrop ended with confirmation status {:?}", confirmation.status), + )); + } + let balance = match http_pool + .get_balance_for_role( + request.query_role.as_str(), + source_pubkey, + &kb_rpc::GetBalanceConfig::confirmed(), + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if balance.lamports < required_balance_lamports { + return std::result::Result::Err(kb_core::Error::new( + "execution_balance_still_insufficient", + format!( + "confirmed airdrop left balance {} below required {}", + balance.lamports, required_balance_lamports + ), + )); + } + return std::result::Result::Ok(FundingResult { + balance_after_lamports: balance.lamports, + airdrop_signature: std::option::Option::Some(airdrop.signature), + airdrop_confirmation: std::option::Option::Some(confirmation), + }); +} + +fn build_transfer_plan( + profile: &kb_config::ProfileConfig, + request: &crate::DevnetSystemTransferRequest, + source_pubkey: kb_model::Pubkey, +) -> kb_core::Result { + let policy = kb_execution_api::ExecutionPolicy { + cluster: kb_execution_api::ExecutionClusterPolicy { + expected_cluster: kb_execution_api::ExecutionCluster::Devnet, + allow_mainnet: false, + mainnet_confirmation: false, + }, + simulation: kb_execution_api::ExecutionSimulationPolicy::Required, + blockhash: kb_execution_api::ExecutionBlockhashPolicy { + kind: kb_execution_api::ExecutionBlockhashKind::Latest, + max_age_slots: std::option::Option::Some( + profile.execution.recent_blockhash_max_age_slots, + ), + nonce_account: std::option::Option::None, + nonce_authority: std::option::Option::None, + }, + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some( + profile.execution.devnet_max_spend_lamports, + ), + max_fee_lamports: std::option::Option::Some(profile.execution.max_fee_lamports), + max_compute_unit_price_micro_lamports: std::option::Option::Some( + profile.execution.max_compute_unit_price_micro_lamports, + ), + }, + authorized_signers: std::vec![source_pubkey.clone()], + dry_run: !request.submit, + post_execution_validation: kb_execution_api::PostExecutionValidationPolicy { + canonical_insert_required: request.submit, + core_extraction_required: request.submit, + decode_replay_required: request.submit, + materialization_required: request.submit && request.materialize_after_decode, + }, + }; + let intent = kb_executor_solana_core::SolanaCoreExecutionIntent { + intent_id: request.intent_id.clone(), + fee_payer: source_pubkey.clone(), + policy, + operation: kb_executor_solana_core::SolanaCoreOperation::SystemTransfer { + from: source_pubkey, + to: request.recipient.clone(), + lamports: request.lamports, + }, + }; + let executor = kb_executor_solana_core::SolanaCoreExecutor; + return kb_execution_api::TypedInstructionExecutor::build_prepared_plan(&executor, &intent); +} + +pub(crate) fn violation_message( + violations: &[kb_execution_safety::ExecutionSafetyViolation], +) -> std::string::String { + if violations.is_empty() { + return "execution policy denied the plan without a diagnostic".to_string(); + } + return violations + .iter() + .map(|violation| return format!("{}: {}", violation.code, violation.message)) + .collect::>() + .join("; "); +} + +pub(crate) fn ensure_not_cancelled(observer: &O, stage: &str) -> kb_core::Result<()> +where + O: crate::SolanaExecutionObserver, +{ + if observer.is_execution_cancelled() { + return std::result::Result::Err(kb_core::Error::new( + "execution_cancelled", + format!("execution was cancelled before stage {stage}"), + )); + } + return std::result::Result::Ok(()); +} + +pub(crate) fn emit( + observer: &O, + level: crate::SolanaExecutionProgressLevel, + stage: impl std::convert::Into, + message: impl std::convert::Into, + signature: std::option::Option, +) where + O: crate::SolanaExecutionObserver, +{ + observer.on_execution_progress(&crate::SolanaExecutionProgressEvent::new( + level, stage, message, signature, + )); +} + +#[cfg(test)] +mod tests { + fn local_devnet_profile() -> kb_config::ProfileConfig { + let config = + match kb_config::parse_config_json(include_str!("../../config/example.config.json")) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("example config parse failed: {error}"), + }; + for profile in config.profiles { + if profile.name == "local_devnet" { + return profile; + } + } + panic!("local_devnet profile missing from example config"); + } + + fn recipient() -> kb_model::Pubkey { + return kb_model::Pubkey("Vote111111111111111111111111111111111111111".to_string()); + } + + #[test] + fn request_and_profile_validation_are_conservative() { + let profile = crate::solana_execution::tests::local_devnet_profile(); + let mut request = crate::DevnetSystemTransferRequest::new( + "intent-1", + crate::solana_execution::tests::recipient(), + 1_000, + ); + assert!(request.validate().is_ok()); + assert!(crate::solana_execution::validate_devnet_profile(&profile, &request).is_ok()); + request.submit = true; + assert!(crate::solana_execution::validate_devnet_profile(&profile, &request).is_err()); + request.operator_confirmed = true; + assert!(crate::solana_execution::validate_devnet_profile(&profile, &request).is_ok()); + request.airdrop_lamports = profile.execution.devnet_airdrop_max_lamports.saturating_add(1); + assert!(crate::solana_execution::validate_devnet_profile(&profile, &request).is_err()); + } + + #[test] + fn local_devnet_profile_routes_complete_execution_flow() { + let profile = crate::solana_execution::tests::local_devnet_profile(); + let pool = match kb_rpc::HttpEndpointPool::from_profile(&profile) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("HTTP pool creation failed: {error}"), + }; + for method in [ + "getGenesisHash", + "getAccountInfo", + "getMinimumBalanceForRentExemption", + "getBalance", + "requestAirdrop", + "getLatestBlockhash", + "getFeeForMessage", + "getBlockHeight", + "getTransaction", + ] { + let selected = pool.select_client_for_role_and_method("http_queries", method); + assert!(selected.is_ok(), "http_queries must route {method}"); + } + for method in ["simulateTransaction", "sendTransaction", "getSignatureStatuses"] { + let selected = pool.select_client_for_role_and_method("http_transactions", method); + assert!(selected.is_ok(), "http_transactions must route {method}"); + } + } + + #[test] + fn transfer_plan_uses_exact_devnet_policy() { + let profile = crate::solana_execution::tests::local_devnet_profile(); + let mut request = crate::DevnetSystemTransferRequest::new( + "intent-2", + crate::solana_execution::tests::recipient(), + 5_000, + ); + request.submit = true; + request.operator_confirmed = true; + let source = kb_model::Pubkey("11111111111111111111111111111111".to_string()); + let plan = match crate::solana_execution::build_transfer_plan( + &profile, + &request, + source.clone(), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("plan build failed: {error}"), + }; + assert_eq!(plan.fee_payer, source); + assert_eq!(plan.requested_spend_lamports, 5_000); + assert_eq!( + plan.policy.cluster.expected_cluster, + kb_execution_api::ExecutionCluster::Devnet + ); + assert!(!plan.policy.dry_run); + assert!(plan.policy.post_execution_validation.canonical_insert_required); + assert_eq!(plan.instructions.len(), 1); + assert_eq!(plan.operation_code, kb_executor_solana_core::SYSTEM_TRANSFER_OPERATION); + } + + #[test] + fn new_recipient_requires_zero_data_rent_exemption() { + assert!( + crate::solana_execution::validate_recipient_transfer_amount(false, 1_000, 890_880,) + .is_err() + ); + assert!( + crate::solana_execution::validate_recipient_transfer_amount(false, 890_880, 890_880,) + .is_ok() + ); + assert!( + crate::solana_execution::validate_recipient_transfer_amount(true, 1, 890_880,).is_ok() + ); + } + + #[test] + fn simulation_failure_message_preserves_runtime_error_and_logs() { + let simulation = kb_execution_api::ExecutionSimulationResult { + simulated: true, + success: false, + cluster: kb_execution_api::ExecutionCluster::Devnet, + blockhash_kind: kb_execution_api::ExecutionBlockhashKind::Latest, + blockhash_age_slots: std::option::Option::Some(1), + replacement_blockhash: std::option::Option::None, + replacement_last_valid_block_height: std::option::Option::None, + nonce_account: std::option::Option::None, + nonce_authority: std::option::Option::None, + units_consumed: std::option::Option::Some(150), + estimated_fee_lamports: std::option::Option::Some(5_000), + logs: std::vec!["Program log: insufficient rent".to_string()], + error: std::option::Option::Some("InsufficientFundsForRent".to_string()), + }; + let message = crate::solana_execution::simulation_failure_message(&simulation); + assert!(message.contains("InsufficientFundsForRent")); + assert!(message.contains("insufficient rent")); + } + + #[tokio::test] + async fn optional_devnet_system_transfer_from_env() { + if std::env::var("KB_DEVNET_EXECUTION_TEST").ok().as_deref() + != std::option::Option::Some("1") + { + return; + } + let database_url = match std::env::var("KB_POSTGRES_TEST_URL") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("KB_POSTGRES_TEST_URL is required: {error}"); + }, + }; + let mut profile = crate::solana_execution::tests::local_devnet_profile(); + profile.database.backend = "postgres".to_string(); + profile.database.postgres.url = database_url; + if let std::result::Result::Ok(directory) = std::env::var("KB_DEVNET_WALLET_DIR") { + profile.wallet.wallet_dir = directory; + } + let pool = match kb_rpc::HttpEndpointPool::from_profile(&profile) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("HTTP pool creation failed: {error}"), + }; + let store = match kb_store_pg::PostgresStore::connect_from_profile_config(&profile).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("PostgreSQL connection failed: {error}"), + }; + if let std::result::Result::Err(error) = store.initialize_store_schema().await { + panic!("PostgreSQL schema initialization failed: {error}"); + } + let recipient_alias = match kb_wallet::WalletAlias::parse("devnet-recipient") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("recipient alias failed: {error}"), + }; + let recipient_wallet = kb_wallet::TemporaryWallet::generate(recipient_alias); + let transfer_lamports = std::env::var("KB_DEVNET_TRANSFER_LAMPORTS") + .ok() + .and_then(|value| return value.parse::().ok()) + .unwrap_or(1_000_000); + let airdrop_lamports = std::env::var("KB_DEVNET_AIRDROP_LAMPORTS") + .ok() + .and_then(|value| return value.parse::().ok()) + .unwrap_or(100_000_000); + let mut request = crate::DevnetSystemTransferRequest::new( + format!("devnet-test-{}", uuid::Uuid::new_v4()), + kb_model::Pubkey(recipient_wallet.public_key()), + transfer_lamports, + ); + request.airdrop_lamports = airdrop_lamports; + request.post_validation_max_retries = 20; + request.submit = true; + request.operator_confirmed = true; + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(kb_decoder_solana_core::SolanaCoreDecoder,)]; + let materializers: std::vec::Vec< + std::sync::Arc, + > = std::vec::Vec::new(); + let workspace_root = match std::path::Path::new(env!("CARGO_MANIFEST_DIR")).parent() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("workspace root cannot be resolved"), + }; + let summary = match crate::execute_devnet_system_transfer( + &pool, + &store, + &profile, + workspace_root, + &request, + decoders.as_slice(), + materializers.as_slice(), + &crate::NoopSolanaExecutionObserver, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Devnet execution failed: {error}"), + }; + let confirmation = match summary.confirmation { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("transaction confirmation missing"), + }; + assert!( + confirmation.status == kb_execution_api::ExecutionConfirmationStatus::Confirmed + || confirmation.status == kb_execution_api::ExecutionConfirmationStatus::Finalized + ); + let post_execution = match summary.post_execution { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("post-execution diagnostic missing"), + }; + assert!(post_execution.canonical_inserted); + assert!(post_execution.core_extracted); + assert!(post_execution.decode_replayed); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_memo_execution.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_memo_execution.rs new file mode 100644 index 0000000..886c7e7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_memo_execution.rs @@ -0,0 +1,918 @@ +// file: kb_pipeline/src/solana_memo_execution.rs +// version: 2 + +//! Devnet SPL Memo v4 execution with canonical post-validation. + +/// Complete request for one SPL Memo v4 Devnet execution. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DevnetMemoExecutionRequest { + /// Stable caller-provided execution identifier. + pub intent_id: std::string::String, + /// Endpoint role used for cluster, balance, blockhash, fee and hydration calls. + pub query_role: std::string::String, + /// Endpoint role used for simulation, submission and confirmation polling. + pub transaction_role: std::string::String, + /// Exact UTF-8 Memo payload. + pub message: std::string::String, + /// Supplies the persistent Devnet fee payer as a Memo signer account. + pub include_wallet_as_memo_signer: bool, + /// Explicitly authorizes signing and submission after successful simulation. + pub submit: bool, + /// Explicit operator confirmation required by the active profile. + pub operator_confirmed: bool, + /// Number of `getTransaction` retries after the first hydration attempt. + pub post_validation_max_retries: u32, + /// Replaces existing core and decode outputs for the submitted signature. + pub force_post_validation_replay: bool, +} + +impl DevnetMemoExecutionRequest { + /// Creates a conservative simulation-only Memo v4 request. + pub fn new( + intent_id: impl std::convert::Into, + message: impl std::convert::Into, + ) -> Self { + return Self { + intent_id: intent_id.into(), + query_role: "http_queries".to_string(), + transaction_role: "http_transactions".to_string(), + message: message.into(), + include_wallet_as_memo_signer: true, + submit: false, + operator_confirmed: false, + post_validation_max_retries: 10, + force_post_validation_replay: false, + }; + } + + /// Validates request-local bounds independently from one profile. + pub fn validate(&self) -> kb_core::Result<()> { + if self.intent_id.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Devnet Memo execution intent id must not be empty", + )); + } + if self.query_role.trim().is_empty() || self.transaction_role.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Devnet Memo execution endpoint roles must not be empty", + )); + } + if self.message.len() > kb_executor_spl_memo::MAX_MEMO_MESSAGE_BYTES { + return std::result::Result::Err(kb_core::Error::config(format!( + "Devnet Memo payload length {} exceeds the executor limit {}", + self.message.len(), + kb_executor_spl_memo::MAX_MEMO_MESSAGE_BYTES + ))); + } + if self.post_validation_max_retries > 20 { + return std::result::Result::Err(kb_core::Error::config( + "post-execution getTransaction retries must not exceed 20", + )); + } + return std::result::Result::Ok(()); + } +} + +/// Complete result of one SPL Memo v4 Devnet execution. +#[derive(Clone, Debug, PartialEq)] +pub struct DevnetMemoExecutionSummary { + /// Profile used by the orchestration. + pub profile_name: std::string::String, + /// Exact classified cluster. + pub cluster: kb_execution_api::ExecutionCluster, + /// Genesis hash returned by the selected endpoint. + pub genesis_hash: std::string::String, + /// Non-secret persistent wallet description. + pub wallet: kb_wallet::WalletSummary, + /// Wallet balance observed before planning. + pub balance_lamports: u64, + /// Exact prepared Memo plan. + pub plan: kb_execution_api::PreparedExecutionPlan, + /// Recent blockhash used by the exact transaction. + pub latest_blockhash: kb_rpc::LatestBlockhashResult, + /// Fee estimate for the exact compiled message. + pub fee: kb_rpc::FeeForMessageResult, + /// Exact simulation result bound to the compiled message. + pub simulation: kb_execution_api::ExecutionSimulationResult, + /// Submission result when explicitly authorized. + pub send_result: std::option::Option, + /// Confirmation result when submitted. + pub confirmation: std::option::Option, + /// Canonical hydration result for the exact signature. + pub backfill: std::option::Option, + /// Core extraction result for the exact signature. + pub core_extraction: std::option::Option, + /// First Memo decode and materialization replay. + pub decode_replay: std::option::Option, + /// Second replay proving that the same decoder version and input are idempotent. + pub idempotence_replay: std::option::Option, + /// Exact persisted transaction annotation rows for the submitted signature. + pub annotations: std::vec::Vec, + /// Aggregated post-execution validation diagnostic. + pub post_execution: std::option::Option, +} + +/// Executes one Memo v4 Devnet simulation or explicitly authorized submission. +#[allow(clippy::too_many_arguments)] +pub async fn execute_devnet_memo( + http_pool: &kb_rpc::HttpEndpointPool, + store: &S, + profile: &kb_config::ProfileConfig, + workspace_root: &std::path::Path, + request: &crate::DevnetMemoExecutionRequest, + decoders: &[std::sync::Arc], + materializers: &[std::sync::Arc], + observer: &O, +) -> kb_core::Result +where + S: kb_store_core::RawTransactionStore + + kb_store_core::CoreExtractionStore + + kb_store_core::DecodePipelineStore + + Sync, + O: crate::SolanaExecutionObserver, +{ + if let std::result::Result::Err(error) = request.validate() { + return std::result::Result::Err(error); + } + if let std::result::Result::Err(error) = + crate::solana_memo_execution::validate_profile(profile, request) + { + return std::result::Result::Err(error); + } + if let std::result::Result::Err(error) = + crate::solana_execution::ensure_not_cancelled(observer, "validate") + { + return std::result::Result::Err(error); + } + let genesis = match http_pool.get_genesis_hash_for_role(request.query_role.as_str()).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if genesis.classified_cluster + != std::option::Option::Some(kb_execution_api::ExecutionCluster::Devnet) + { + return std::result::Result::Err(kb_core::Error::new( + "execution_cluster_mismatch", + format!( + "expected Devnet genesis hash but endpoint returned {} classified as {:?}", + genesis.genesis_hash, genesis.classified_cluster + ), + )); + } + let wallet = match crate::solana_execution::load_profile_wallet(profile, workspace_root).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let wallet_summary = wallet.summary(); + let fee_payer = kb_model::Pubkey(wallet_summary.public_key.clone()); + let balance = match http_pool + .get_balance_for_role( + request.query_role.as_str(), + &fee_payer, + &kb_rpc::GetBalanceConfig::confirmed(), + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if balance.lamports < profile.execution.max_fee_lamports { + return std::result::Result::Err(kb_core::Error::new( + "execution_balance_insufficient", + format!( + "Devnet wallet balance {} is below the configured fee ceiling {}", + balance.lamports, profile.execution.max_fee_lamports + ), + )); + } + let plan = match crate::solana_memo_execution::build_plan(profile, request, fee_payer.clone()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let plan_evaluation = + match kb_execution_safety::ExecutionSafetyChecker.evaluate_prepared_plan(&plan) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if plan_evaluation.decision == kb_execution_safety::ExecutionSafetyDecision::Deny { + return std::result::Result::Err(kb_core::Error::new( + "execution_plan_denied", + crate::solana_execution::violation_message(plan_evaluation.violations.as_slice()), + )); + } + let latest_blockhash = match http_pool + .get_latest_blockhash_for_role( + request.query_role.as_str(), + &kb_rpc::GetLatestBlockhashConfig::confirmed(), + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let unsigned = match kb_execution_solana::build_legacy_transaction( + &plan, + latest_blockhash.blockhash.as_str(), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let message_base64 = unsigned.message_base64(); + let fee = match http_pool + .get_fee_for_message_for_role( + request.query_role.as_str(), + message_base64.as_str(), + &kb_rpc::GetFeeForMessageConfig::new( + kb_rpc::RpcCommitmentLevel::Confirmed, + std::option::Option::Some(latest_blockhash.context.slot), + ), + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if fee.fee_lamports.is_none() { + return std::result::Result::Err(kb_core::Error::new( + "execution_fee_unavailable", + "getFeeForMessage returned null for the selected recent blockhash", + )); + } + let unsigned_base64 = match unsigned.transaction_base64() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let simulation_config = match kb_rpc::SimulateTransactionConfig::new( + kb_rpc::RpcCommitmentLevel::Confirmed, + false, + false, + std::option::Option::Some(latest_blockhash.context.slot), + true, + std::option::Option::None, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_execution::emit( + observer, + crate::SolanaExecutionProgressLevel::Info, + "memo_simulation", + format!("simulating exact Memo message {}", unsigned.message_hash()), + std::option::Option::None, + ); + let simulation_rpc = match http_pool + .simulate_transaction_for_role( + request.transaction_role.as_str(), + unsigned_base64.as_str(), + &simulation_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let simulation = simulation_rpc.to_execution_result( + kb_execution_api::ExecutionCluster::Devnet, + kb_execution_api::ExecutionBlockhashKind::Latest, + std::option::Option::Some( + simulation_rpc.context.slot.saturating_sub(latest_blockhash.context.slot), + ), + std::option::Option::None, + std::option::Option::None, + std::option::Option::Some(&fee), + ); + let evidence = unsigned.bind_simulation(simulation.clone()); + let mut summary = crate::DevnetMemoExecutionSummary { + profile_name: profile.name.clone(), + cluster: kb_execution_api::ExecutionCluster::Devnet, + genesis_hash: genesis.genesis_hash, + wallet: wallet_summary, + balance_lamports: balance.lamports, + plan, + latest_blockhash, + fee, + simulation, + send_result: std::option::Option::None, + confirmation: std::option::Option::None, + backfill: std::option::Option::None, + core_extraction: std::option::Option::None, + decode_replay: std::option::Option::None, + idempotence_replay: std::option::Option::None, + annotations: std::vec::Vec::new(), + post_execution: std::option::Option::None, + }; + if !request.submit { + return std::result::Result::Ok(summary); + } + if !summary.simulation.success { + return std::result::Result::Err(kb_core::Error::new( + "execution_simulation_failed", + crate::solana_execution::simulation_failure_message(&summary.simulation), + )); + } + let send_evaluation = match kb_execution_safety::ExecutionSafetyChecker + .evaluate_send(&summary.plan, &summary.simulation) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if send_evaluation.decision == kb_execution_safety::ExecutionSafetyDecision::Deny { + return std::result::Result::Err(kb_core::Error::new( + "execution_send_denied", + crate::solana_execution::violation_message(send_evaluation.violations.as_slice()), + )); + } + let signed = match unsigned.sign_after_simulation(&evidence, &[wallet.as_signer()]) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if let std::result::Result::Err(error) = signed.verify_signatures() { + return std::result::Result::Err(error); + } + let signature = signed.primary_signature().clone(); + let mut diagnostic = kb_execution_api::PostExecutionDiagnostic { + signature: signature.clone(), + canonical_inserted: false, + core_extracted: false, + decode_replayed: false, + materialized: false, + diagnostics: std::vec::Vec::new(), + }; + let send_config = match kb_rpc::SendTransactionConfig::from_execution_config( + &profile.execution, + std::option::Option::Some(summary.latest_blockhash.context.slot), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let signed_base64 = signed.transaction_base64(); + let sent = match http_pool + .send_transaction_for_role( + request.transaction_role.as_str(), + signed_base64.as_str(), + &signature, + &send_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic.diagnostics.push(format!("Memo submission failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + summary.send_result = std::option::Option::Some( + sent.to_execution_result(kb_execution_api::ExecutionCluster::Devnet), + ); + let confirmation_config = match kb_rpc::ConfirmTransactionConfig::from_execution_config( + &profile.execution, + std::option::Option::Some(summary.latest_blockhash.last_valid_block_height), + std::option::Option::Some(summary.latest_blockhash.context.slot), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let confirmation = match http_pool + .confirm_transaction_for_roles( + request.transaction_role.as_str(), + request.query_role.as_str(), + kb_execution_api::ExecutionCluster::Devnet, + &signature, + &confirmation_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic.diagnostics.push(format!("Memo confirmation failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + let confirmation_status = confirmation.status; + summary.confirmation = std::option::Option::Some(confirmation); + if !matches!( + confirmation_status, + kb_execution_api::ExecutionConfirmationStatus::Confirmed + | kb_execution_api::ExecutionConfirmationStatus::Finalized + ) { + diagnostic.diagnostics.push(format!( + "Memo post-validation stopped at confirmation status {confirmation_status:?}" + )); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + } + let backfill = match crate::solana_memo_execution::hydrate_signature( + http_pool, store, profile, request, observer, &signature, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic.diagnostics.push(format!("Memo hydration failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + diagnostic.canonical_inserted = crate::solana_memo_execution::canonical_available(&backfill); + summary.backfill = std::option::Option::Some(backfill); + if !diagnostic.canonical_inserted { + diagnostic + .diagnostics + .push("confirmed Memo transaction was unavailable for canonical hydration".to_string()); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + } + let extraction = match crate::execute_core_extraction( + store, + &crate::CoreExtractionRequest { + source: crate::CoreExtractionSource::Signatures(std::vec![signature.0.clone()]), + limit: 1, + max_concurrent_extractions: 1, + force_replay: request.force_post_validation_replay, + }, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic.diagnostics.push(format!("Memo core extraction failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + diagnostic.core_extracted = extraction.failed == 0 + && !extraction.cancelled + && extraction.selected == 1 + && extraction.extracted.saturating_add(extraction.skipped) >= 1; + summary.core_extraction = std::option::Option::Some(extraction); + if !diagnostic.core_extracted { + diagnostic.diagnostics.push("Memo core extraction did not complete".to_string()); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + } + let first_replay = match crate::solana_memo_execution::replay_memo( + store, + request, + &signature, + false, + decoders, + materializers, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic.diagnostics.push(format!("Memo decode replay failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + diagnostic.decode_replayed = crate::solana_memo_execution::decode_completed(&first_replay); + summary.decode_replay = std::option::Option::Some(first_replay); + let filter = match kb_store_core::MaterializedEventFilter::new( + std::option::Option::Some("transaction_annotations".to_string()), + std::option::Option::Some("transaction_annotation".to_string()), + std::option::Option::Some(signature.0.clone()), + 8, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + summary.annotations = + match kb_store_core::DecodePipelineStore::list_materialized_events(store, &filter).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic.diagnostics.push(format!("Memo annotation query failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + diagnostic.materialized = diagnostic.decode_replayed + && summary + .annotations + .iter() + .any(|row| return row.signature.as_str() == signature.0.as_str()); + let second_replay = match crate::solana_memo_execution::replay_memo( + store, + request, + &signature, + true, + decoders, + materializers, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic.diagnostics.push(format!("Memo idempotence replay failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + let idempotent = second_replay.failed_inputs == 0 + && second_replay.processors.iter().all(|processor| { + return processor.failed == 0 + && processor.materialized_outputs == 0 + && processor.materialization_refused == 0; + }); + summary.idempotence_replay = std::option::Option::Some(second_replay); + if !idempotent { + diagnostic + .diagnostics + .push("second Memo replay did not prove a clean idempotent skip".to_string()); + } else if diagnostic.canonical_inserted + && diagnostic.core_extracted + && diagnostic.decode_replayed + && diagnostic.materialized + { + diagnostic.diagnostics.push( + "Memo completed canonical hydration, core extraction, decode, annotation projection and idempotence validation" + .to_string(), + ); + } + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); +} + +fn validate_profile( + profile: &kb_config::ProfileConfig, + request: &crate::DevnetMemoExecutionRequest, +) -> kb_core::Result<()> { + if profile.wallet.cluster != "devnet" { + return std::result::Result::Err(kb_core::Error::config( + "Memo Devnet orchestration requires a Devnet wallet profile", + )); + } + if !profile.wallet.temporary_wallet_enabled || !profile.wallet.temporary_wallet_persist { + return std::result::Result::Err(kb_core::Error::config( + "Memo Devnet orchestration requires an enabled persistent temporary wallet", + )); + } + if !profile.execution.require_simulation { + return std::result::Result::Err(kb_core::Error::config( + "Memo Devnet orchestration requires simulation", + )); + } + if request.submit && !profile.wallet.devnet_send_enabled { + return std::result::Result::Err(kb_core::Error::config( + "Devnet transaction submission is disabled by the wallet profile", + )); + } + if request.submit + && profile.execution.require_operator_confirmation + && !request.operator_confirmed + { + return std::result::Result::Err(kb_core::Error::config( + "Memo Devnet submission requires explicit operator confirmation", + )); + } + return std::result::Result::Ok(()); +} + +fn build_plan( + profile: &kb_config::ProfileConfig, + request: &crate::DevnetMemoExecutionRequest, + fee_payer: kb_model::Pubkey, +) -> kb_core::Result { + let signers = if request.include_wallet_as_memo_signer { + std::vec![kb_executor_spl_memo::SplMemoSigner { pubkey: fee_payer.clone() }] + } else { + std::vec::Vec::new() + }; + let intent = kb_executor_spl_memo::SplMemoExecutionIntent { + intent_id: request.intent_id.clone(), + fee_payer: fee_payer.clone(), + policy: kb_execution_api::ExecutionPolicy { + cluster: kb_execution_api::ExecutionClusterPolicy { + expected_cluster: kb_execution_api::ExecutionCluster::Devnet, + allow_mainnet: false, + mainnet_confirmation: false, + }, + simulation: kb_execution_api::ExecutionSimulationPolicy::Required, + blockhash: kb_execution_api::ExecutionBlockhashPolicy { + kind: kb_execution_api::ExecutionBlockhashKind::Latest, + max_age_slots: std::option::Option::Some( + profile.execution.recent_blockhash_max_age_slots, + ), + nonce_account: std::option::Option::None, + nonce_authority: std::option::Option::None, + }, + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some(0), + max_fee_lamports: std::option::Option::Some(profile.execution.max_fee_lamports), + max_compute_unit_price_micro_lamports: std::option::Option::Some( + profile.execution.max_compute_unit_price_micro_lamports, + ), + }, + authorized_signers: std::vec![fee_payer.clone()], + dry_run: !request.submit, + post_execution_validation: kb_execution_api::PostExecutionValidationPolicy { + canonical_insert_required: true, + core_extraction_required: true, + decode_replay_required: true, + materialization_required: true, + }, + }, + operation: kb_executor_spl_memo::SplMemoOperation::AddMemo { + generation: kb_executor_spl_memo::SplMemoGeneration::V4, + message: request.message.clone(), + signers, + }, + }; + return kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &kb_executor_spl_memo::SplMemoExecutor, + &intent, + ); +} + +async fn hydrate_signature( + http_pool: &kb_rpc::HttpEndpointPool, + store: &S, + profile: &kb_config::ProfileConfig, + request: &crate::DevnetMemoExecutionRequest, + observer: &O, + signature: &kb_model::Signature, +) -> kb_core::Result +where + S: kb_store_core::RawTransactionStore + Sync, + O: crate::SolanaExecutionObserver, +{ + let mut retry = 0_u32; + loop { + let result = match crate::execute_http_backfill( + http_pool, + store, + &crate::BackfillRequest { + role: request.query_role.clone(), + commitment: "confirmed".to_string(), + source: crate::BackfillSource::ExplicitSignatures(std::vec![signature.0.clone()]), + page_size: 1, + max_pages: 1, + max_concurrent_requests: 1, + max_retries: 0, + }, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if crate::solana_memo_execution::canonical_available(&result) + || retry >= request.post_validation_max_retries + || observer.is_execution_cancelled() + { + return std::result::Result::Ok(result); + } + retry = retry.saturating_add(1); + tokio::time::sleep(std::time::Duration::from_millis(std::cmp::max( + profile.execution.confirmation_poll_interval_ms, + 500, + ))) + .await; + } +} + +fn canonical_available(summary: &crate::BackfillSummary) -> bool { + return summary.failed == 0 + && summary.missing == 0 + && summary.candidates_completed == 1 + && summary.candidates_cancelled == 0 + && summary.candidates_not_started == 0 + && summary + .canonical_inserted + .saturating_add(summary.canonical_skipped) + .saturating_add(summary.existing_skipped) + >= 1; +} + +async fn replay_memo( + store: &S, + request: &crate::DevnetMemoExecutionRequest, + signature: &kb_model::Signature, + include_materialized_state: bool, + decoders: &[std::sync::Arc], + materializers: &[std::sync::Arc], + observer: &O, +) -> kb_core::Result +where + S: kb_store_core::DecodePipelineStore + Sync, + O: crate::SolanaExecutionObserver, +{ + let mut states = std::vec![ + kb_store_core::CoreInstructionProcessingState::Pending, + kb_store_core::CoreInstructionProcessingState::Failed, + kb_store_core::CoreInstructionProcessingState::ReplayRequested, + ]; + if include_materialized_state { + states.push(kb_store_core::CoreInstructionProcessingState::Materialized); + } + let selection = match kb_store_core::DecodeSelectionFilter::new( + std::vec![signature.0.clone()], + states, + std::option::Option::None, + std::option::Option::None, + std::vec![kb_program_ids::SPL_MEMO_V4_PROGRAM_ID.to_string()], + std::vec::Vec::new(), + false, + 8, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::execute_decode_replay( + store, + &crate::DecodeReplayRequest { + campaign_id: crate::new_decode_campaign_id(), + selection, + decoder_names: std::vec::Vec::new(), + dispatch_policy: crate::DecodeDispatchPolicy::HighestPriority, + max_concurrent_inputs: 1, + force_replay: if include_materialized_state { + false + } else { + request.force_post_validation_replay + }, + force_replay_all_matching: false, + materialize_after_decode: true, + }, + decoders, + materializers, + observer, + ) + .await; +} + +fn decode_completed(summary: &crate::DecodeReplaySummary) -> bool { + return summary.failed_inputs == 0 + && summary.unmatched == 0 + && !summary.cancelled + && summary.completed >= 1 + && summary.processors.iter().all(|processor| { + return processor.failed == 0 + && processor.unsupported == 0 + && processor.materialization_refused == 0; + }) + && summary.processors.iter().map(|processor| return processor.decoded).sum::() >= 1; +} + +#[cfg(test)] +mod tests { + fn local_devnet_profile() -> kb_config::ProfileConfig { + let config = + match kb_config::parse_config_json(include_str!("../../config/example.config.json")) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("example config parse failed: {error}"), + }; + for profile in config.profiles { + if profile.name == "local_devnet" { + return profile; + } + } + panic!("local_devnet profile missing"); + } + + #[test] + fn request_is_simulation_only_and_bounded_by_default() { + let request = crate::DevnetMemoExecutionRequest::new("memo-1", "audit annotation"); + assert!(!request.submit); + assert!(request.include_wallet_as_memo_signer); + assert!(request.validate().is_ok()); + let oversized = "x".repeat(kb_executor_spl_memo::MAX_MEMO_MESSAGE_BYTES + 1); + assert!(crate::DevnetMemoExecutionRequest::new("memo-2", oversized).validate().is_err()); + } + + #[test] + fn exact_v4_plan_has_zero_spend_and_wallet_signer() { + let profile = crate::solana_memo_execution::tests::local_devnet_profile(); + let fee_payer = kb_model::Pubkey(kb_program_ids::SYSTEM_PROGRAM_ID.to_string()); + let mut request = crate::DevnetMemoExecutionRequest::new("memo-3", "hello"); + request.submit = true; + request.operator_confirmed = true; + let plan = + match crate::solana_memo_execution::build_plan(&profile, &request, fee_payer.clone()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Memo plan failed: {error}"), + }; + assert_eq!(plan.requested_spend_lamports, 0); + assert_eq!(plan.fee_payer, fee_payer); + assert_eq!(plan.instructions.len(), 1); + assert_eq!(plan.instructions[0].program_id.0, kb_program_ids::SPL_MEMO_V4_PROGRAM_ID); + assert_eq!(plan.instructions[0].accounts.len(), 1); + assert!(plan.instructions[0].accounts[0].is_signer); + assert!(!plan.instructions[0].accounts[0].is_writable); + assert!( + kb_execution_safety::ExecutionSafetyChecker + .evaluate_prepared_plan(&plan) + .is_ok() + ); + } + + #[test] + fn submission_requires_profile_enablement_and_confirmation() { + let profile = crate::solana_memo_execution::tests::local_devnet_profile(); + let mut request = crate::DevnetMemoExecutionRequest::new("memo-4", "hello"); + assert!(crate::solana_memo_execution::validate_profile(&profile, &request).is_ok()); + request.submit = true; + assert!(crate::solana_memo_execution::validate_profile(&profile, &request).is_err()); + request.operator_confirmed = true; + assert!(crate::solana_memo_execution::validate_profile(&profile, &request).is_ok()); + } + + #[tokio::test] + async fn optional_devnet_memo_execution_from_env() { + if std::env::var("KB_DEVNET_MEMO_EXECUTION_TEST").ok().as_deref() + != std::option::Option::Some("1") + { + return; + } + let database_url = match std::env::var("KB_POSTGRES_TEST_URL") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("KB_POSTGRES_TEST_URL is required: {error}"); + }, + }; + let mut profile = crate::solana_memo_execution::tests::local_devnet_profile(); + profile.database.backend = "postgres".to_string(); + profile.database.postgres.url = database_url; + if let std::result::Result::Ok(directory) = std::env::var("KB_DEVNET_WALLET_DIR") { + profile.wallet.wallet_dir = directory; + } + let pool = match kb_rpc::HttpEndpointPool::from_profile(&profile) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("HTTP pool creation failed: {error}"), + }; + let store = match kb_store_pg::PostgresStore::connect_from_profile_config(&profile).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("PostgreSQL connection failed: {error}"), + }; + if let std::result::Result::Err(error) = store.initialize_store_schema().await { + panic!("PostgreSQL schema initialization failed: {error}"); + } + let mut request = crate::DevnetMemoExecutionRequest::new( + format!("devnet-memo-test-{}", uuid::Uuid::new_v4()), + format!("khadhroony-bot2 memo validation {}", uuid::Uuid::new_v4()), + ); + request.post_validation_max_retries = 20; + if std::env::var("KB_DEVNET_MEMO_SUBMIT").ok().as_deref() == std::option::Option::Some("1") + { + request.submit = true; + request.operator_confirmed = true; + } + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(kb_decoder_spl_memo::SplMemoDecoder)]; + let materializers: std::vec::Vec< + std::sync::Arc, + > = std::vec![std::sync::Arc::new( + kb_materializer_transaction_annotations::TransactionAnnotationMaterializer, + )]; + let workspace_root = match std::path::Path::new(env!("CARGO_MANIFEST_DIR")).parent() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("workspace root cannot be resolved"), + }; + let summary = match crate::execute_devnet_memo( + &pool, + &store, + &profile, + workspace_root, + &request, + decoders.as_slice(), + materializers.as_slice(), + &crate::NoopSolanaExecutionObserver, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Devnet Memo execution failed: {error}"), + }; + assert!(summary.simulation.success); + if request.submit { + let post_execution = match summary.post_execution { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("Memo post-execution diagnostic missing"), + }; + assert!(post_execution.canonical_inserted); + assert!(post_execution.core_extracted); + assert!(post_execution.decode_replayed); + assert!(post_execution.materialized); + assert!(!summary.annotations.is_empty()); + let idempotence = match summary.idempotence_replay { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("Memo idempotence replay missing"), + }; + assert_eq!( + idempotence + .processors + .iter() + .map(|processor| return processor.materialized_outputs) + .sum::(), + 0 + ); + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_stateful.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_stateful.rs new file mode 100644 index 0000000..554c013 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_stateful.rs @@ -0,0 +1,1785 @@ +// file: kb_pipeline/src/solana_stateful.rs +// version: 1 + +//! Stateful Localnet and Devnet readiness checks for native Solana execution plans. + +use std::str::FromStr; // rust-rules: trait-import + +const COMPLETE_ACCOUNT_DATA_LIMIT: usize = 65_536; +const ALT_META_SIZE: usize = 56; +const ALT_MAX_ADDRESSES: usize = 256; +const FEATURE_ACCOUNT_SIZE: u64 = 9; +const SLASHING_REPORT_HEADER_SIZE: usize = 114; +const MAX_SHRED_SIZE: usize = 1_232; +const ZK_CONTEXT_META_SIZE: usize = 33; + +/// Stateful readiness outcome for one native Solana operation. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum SolanaCoreStatefulReadinessStatus { + /// The operation has no stateful preflight contract in this layer. + NotRequired, + /// Every stateful preflight check passed. + Ready, + /// At least one stateful preflight check failed. + Blocked, +} + +/// One machine-readable stateful readiness check. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SolanaCoreStatefulCheck { + /// Stable diagnostic code. + pub code: std::string::String, + /// Whether the check passed. + pub passed: bool, + /// Operator-readable explanation. + pub message: std::string::String, +} + +/// One contextual fact measured during stateful readiness inspection. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SolanaCoreStatefulFact { + /// Stable fact key. + pub key: std::string::String, + /// String representation of the measured value. + pub value: std::string::String, +} + +/// Complete request for one native Solana stateful readiness inspection. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SolanaCoreStatefulReadinessRequest { + /// HTTP endpoint role used for all state reads. + pub query_role: std::string::String, + /// Expected Localnet or Devnet cluster. + pub cluster: kb_execution_api::ExecutionCluster, + /// Native operation whose external state must be inspected. + pub operation: kb_executor_solana_core::SolanaCoreOperation, +} + +/// Complete stateful readiness report produced before simulation. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SolanaCoreStatefulReadinessReport { + /// Expected cluster. + pub cluster: kb_execution_api::ExecutionCluster, + /// Stable native operation code. + pub operation_code: std::string::String, + /// Aggregate stateful readiness status. + pub status: crate::solana_stateful::SolanaCoreStatefulReadinessStatus, + /// Highest contextual slot observed while reading accounts. + pub context_slot: std::option::Option, + /// Ordered stateful checks. + pub checks: std::vec::Vec, + /// Ordered measured facts. + pub facts: std::vec::Vec, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +struct AddressLookupTableState { + deactivation_slot: u64, + authority: std::option::Option, + address_count: usize, +} + +/// Inspects Localnet or Devnet state required before simulating one native operation. +pub async fn inspect_solana_core_stateful_readiness( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::solana_stateful::SolanaCoreStatefulReadinessRequest, +) -> kb_core::Result { + if request.query_role.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "stateful execution readiness query_role must not be empty", + )); + } + match request.cluster { + kb_execution_api::ExecutionCluster::Localnet + | kb_execution_api::ExecutionCluster::Devnet => {}, + kb_execution_api::ExecutionCluster::Testnet + | kb_execution_api::ExecutionCluster::Mainnet => { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_cluster_unsupported", + "stateful native execution readiness is restricted to Localnet and Devnet", + )); + }, + } + let genesis = match pool.get_genesis_hash_for_role(request.query_role.as_str()).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + match crate::solana_stateful::validate_cluster(request.cluster, &genesis) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let mut report = crate::solana_stateful::new_report(request); + crate::solana_stateful::push_fact(&mut report, "genesis_hash", genesis.genesis_hash.as_str()); + match &request.operation { + kb_executor_solana_core::SolanaCoreOperation::AddressLookupTableCreate { + authority, + payer, + recent_slot, + } => { + match crate::solana_stateful::inspect_alt_create( + pool, + request, + &mut report, + authority, + payer, + *recent_slot, + ) + .await + { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + }, + kb_executor_solana_core::SolanaCoreOperation::AddressLookupTableExtend { + lookup_table, + authority, + payer, + new_addresses, + } => { + match crate::solana_stateful::inspect_alt_existing( + pool, + request, + &mut report, + lookup_table, + authority, + crate::solana_stateful::AltAction::Extend { + payer_present: payer.is_some(), + added_addresses: new_addresses.len(), + }, + ) + .await + { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + }, + kb_executor_solana_core::SolanaCoreOperation::AddressLookupTableFreeze { + lookup_table, + authority, + } => { + match crate::solana_stateful::inspect_alt_existing( + pool, + request, + &mut report, + lookup_table, + authority, + crate::solana_stateful::AltAction::Freeze, + ) + .await + { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + }, + kb_executor_solana_core::SolanaCoreOperation::AddressLookupTableDeactivate { + lookup_table, + authority, + } => { + match crate::solana_stateful::inspect_alt_existing( + pool, + request, + &mut report, + lookup_table, + authority, + crate::solana_stateful::AltAction::Deactivate, + ) + .await + { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + }, + kb_executor_solana_core::SolanaCoreOperation::AddressLookupTableClose { + lookup_table, + authority, + .. + } => { + match crate::solana_stateful::inspect_alt_existing( + pool, + request, + &mut report, + lookup_table, + authority, + crate::solana_stateful::AltAction::Close, + ) + .await + { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + }, + kb_executor_solana_core::SolanaCoreOperation::ConfigCreateAccount { + config_account, + lamports, + max_config_data_space, + max_keys, + .. + } => { + match crate::solana_stateful::inspect_config_create( + pool, + request, + &mut report, + config_account, + *lamports, + *max_config_data_space, + max_keys.len(), + ) + .await + { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + }, + kb_executor_solana_core::SolanaCoreOperation::ConfigStore { + config_account, + keys, + data, + .. + } => { + match crate::solana_stateful::inspect_config_store( + pool, + request, + &mut report, + config_account, + keys.len(), + data.len(), + ) + .await + { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + }, + kb_executor_solana_core::SolanaCoreOperation::FeatureActivate { + feature_account, + lamports, + .. + } => { + match crate::solana_stateful::inspect_feature_activate( + pool, + request, + &mut report, + feature_account, + *lamports, + ) + .await + { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + }, + kb_executor_solana_core::SolanaCoreOperation::FeatureRevokePendingActivation { + feature_account, + } => { + match crate::solana_stateful::inspect_feature_revoke( + pool, + request, + &mut report, + feature_account, + ) + .await + { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + }, + kb_executor_solana_core::SolanaCoreOperation::SlashingSubmitDuplicateBlockProof { + proof_account, + report_account, + report_lamports, + proof_offset, + slot, + .. + } => { + match crate::solana_stateful::inspect_slashing_submit( + pool, + request, + &mut report, + proof_account, + report_account, + *report_lamports, + *proof_offset, + *slot, + ) + .await + { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + }, + kb_executor_solana_core::SolanaCoreOperation::SlashingCloseViolationReport { + report_account, + destination, + } => { + match crate::solana_stateful::inspect_slashing_close( + pool, + request, + &mut report, + report_account, + destination, + ) + .await + { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + }, + kb_executor_solana_core::SolanaCoreOperation::ZkElGamalVerifyInline { + proof_type, + context_state, + .. + } => { + match crate::solana_stateful::inspect_zk_verify( + pool, + request, + &mut report, + *proof_type, + std::option::Option::None, + context_state.as_ref(), + ) + .await + { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + }, + kb_executor_solana_core::SolanaCoreOperation::ZkElGamalVerifyFromAccount { + proof_type, + proof_account, + proof_offset, + context_state, + } => { + match crate::solana_stateful::inspect_zk_verify( + pool, + request, + &mut report, + *proof_type, + std::option::Option::Some((proof_account, *proof_offset)), + context_state.as_ref(), + ) + .await + { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + }, + kb_executor_solana_core::SolanaCoreOperation::ZkElGamalCloseContextState { + context_state, + authority, + .. + } => { + match crate::solana_stateful::inspect_zk_close( + pool, + request, + &mut report, + context_state, + authority, + ) + .await + { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(error); + }, + } + }, + _ => { + report.status = crate::solana_stateful::SolanaCoreStatefulReadinessStatus::NotRequired; + crate::solana_stateful::push_fact( + &mut report, + "stateful_policy", + "not_required_for_operation", + ); + return std::result::Result::Ok(report); + }, + } + crate::solana_stateful::finalize_report(&mut report); + return std::result::Result::Ok(report); +} + +fn new_report( + request: &crate::solana_stateful::SolanaCoreStatefulReadinessRequest, +) -> crate::solana_stateful::SolanaCoreStatefulReadinessReport { + return crate::solana_stateful::SolanaCoreStatefulReadinessReport { + cluster: request.cluster, + operation_code: request.operation.operation_code().to_string(), + status: crate::solana_stateful::SolanaCoreStatefulReadinessStatus::Ready, + context_slot: std::option::Option::None, + checks: std::vec::Vec::new(), + facts: std::vec::Vec::new(), + }; +} + +fn validate_cluster( + expected: kb_execution_api::ExecutionCluster, + genesis: &kb_rpc::GenesisHashResult, +) -> kb_core::Result<()> { + return match expected { + kb_execution_api::ExecutionCluster::Devnet => { + if genesis.classified_cluster + == std::option::Option::Some(kb_execution_api::ExecutionCluster::Devnet) + { + std::result::Result::Ok(()) + } else { + std::result::Result::Err(kb_core::Error::new( + "execution_stateful_devnet_genesis_mismatch", + "stateful Devnet readiness requires the official Devnet genesis hash", + )) + } + }, + kb_execution_api::ExecutionCluster::Localnet => { + if genesis.classified_cluster.is_none() { + std::result::Result::Ok(()) + } else { + std::result::Result::Err(kb_core::Error::new( + "execution_stateful_localnet_public_cluster", + "Localnet readiness refuses endpoints classified as an official public cluster", + )) + } + }, + kb_execution_api::ExecutionCluster::Testnet + | kb_execution_api::ExecutionCluster::Mainnet => { + std::result::Result::Err(kb_core::Error::new( + "execution_stateful_cluster_unsupported", + "stateful native execution readiness is restricted to Localnet and Devnet", + )) + }, + }; +} + +fn push_check( + report: &mut crate::solana_stateful::SolanaCoreStatefulReadinessReport, + code: &str, + passed: bool, + message: impl std::convert::Into, +) { + report.checks.push(crate::solana_stateful::SolanaCoreStatefulCheck { + code: code.to_string(), + passed, + message: message.into(), + }); +} + +fn push_fact( + report: &mut crate::solana_stateful::SolanaCoreStatefulReadinessReport, + key: &str, + value: impl std::fmt::Display, +) { + report.facts.push(crate::solana_stateful::SolanaCoreStatefulFact { + key: key.to_string(), + value: value.to_string(), + }); +} + +fn update_context_slot( + report: &mut crate::solana_stateful::SolanaCoreStatefulReadinessReport, + slot: u64, +) { + let current = match report.context_slot { + std::option::Option::Some(value) => value.max(slot), + std::option::Option::None => slot, + }; + report.context_slot = std::option::Option::Some(current); +} + +fn finalize_report(report: &mut crate::solana_stateful::SolanaCoreStatefulReadinessReport) { + report.status = if report.checks.iter().all(|check| return check.passed) { + crate::solana_stateful::SolanaCoreStatefulReadinessStatus::Ready + } else { + crate::solana_stateful::SolanaCoreStatefulReadinessStatus::Blocked + }; +} + +async fn account_with_data( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::solana_stateful::SolanaCoreStatefulReadinessRequest, + pubkey: &kb_model::Pubkey, +) -> kb_core::Result { + let config = match kb_rpc::GetAccountInfoConfig::confirmed_with_data( + crate::solana_stateful::COMPLETE_ACCOUNT_DATA_LIMIT, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return pool + .get_account_info_for_role(request.query_role.as_str(), pubkey, &config) + .await; +} + +async fn rent_minimum( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::solana_stateful::SolanaCoreStatefulReadinessRequest, + data_length: u64, +) -> kb_core::Result { + let result = pool + .get_minimum_balance_for_rent_exemption_for_role( + request.query_role.as_str(), + data_length, + &kb_rpc::GetMinimumBalanceForRentExemptionConfig::confirmed(), + ) + .await; + return match result { + std::result::Result::Ok(value) => std::result::Result::Ok(value.minimum_balance_lamports), + std::result::Result::Err(error) => std::result::Result::Err(error), + }; +} + +async fn epoch_info( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::solana_stateful::SolanaCoreStatefulReadinessRequest, +) -> kb_core::Result { + return pool + .get_epoch_info_for_role( + request.query_role.as_str(), + &kb_rpc::GetEpochInfoConfig::confirmed(), + ) + .await; +} + +async fn inspect_alt_create( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::solana_stateful::SolanaCoreStatefulReadinessRequest, + report: &mut crate::solana_stateful::SolanaCoreStatefulReadinessReport, + authority: &kb_model::Pubkey, + payer: &kb_model::Pubkey, + recent_slot: u64, +) -> kb_core::Result<()> { + let authority_address = match solana_pubkey::Pubkey::from_str(authority.0.as_str()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::config(format!( + "invalid ALT authority: {error}" + ))); + }, + }; + let payer_address = match solana_pubkey::Pubkey::from_str(payer.0.as_str()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::config(format!( + "invalid ALT payer: {error}" + ))); + }, + }; + let (_, table_address) = + solana_address_lookup_table_interface::instruction::create_lookup_table( + authority_address, + payer_address, + recent_slot, + ); + let table = kb_model::Pubkey(table_address.to_string()); + let account_result = + match crate::solana_stateful::account_with_data(pool, request, &table).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::update_context_slot(report, account_result.context.slot); + crate::solana_stateful::push_fact(report, "lookup_table", table.0.as_str()); + crate::solana_stateful::push_check( + report, + "alt_create_target_absent", + account_result.account.is_none(), + "derived lookup table account must not already exist", + ); + let epoch = match crate::solana_stateful::epoch_info(pool, request).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::push_fact(report, "absolute_slot", epoch.absolute_slot); + let last_valid = + solana_address_lookup_table_interface::state::estimate_last_valid_slot(recent_slot); + crate::solana_stateful::push_fact(report, "recent_slot_last_valid", last_valid); + crate::solana_stateful::push_check( + report, + "alt_create_recent_slot_not_future", + recent_slot <= epoch.absolute_slot, + "ALT recent slot must not be ahead of the current confirmed slot", + ); + crate::solana_stateful::push_check( + report, + "alt_create_recent_slot_still_recent", + epoch.absolute_slot <= last_valid, + "ALT recent slot must remain in the runtime slot-hashes window", + ); + let rent = match crate::solana_stateful::rent_minimum( + pool, + request, + crate::solana_stateful::ALT_META_SIZE as u64, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::push_fact(report, "minimum_rent_lamports", rent); + return std::result::Result::Ok(()); +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum AltAction { + Extend { + payer_present: bool, + added_addresses: usize, + }, + Freeze, + Deactivate, + Close, +} + +async fn inspect_alt_existing( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::solana_stateful::SolanaCoreStatefulReadinessRequest, + report: &mut crate::solana_stateful::SolanaCoreStatefulReadinessReport, + lookup_table: &kb_model::Pubkey, + authority: &kb_model::Pubkey, + action: crate::solana_stateful::AltAction, +) -> kb_core::Result<()> { + let account_result = + match crate::solana_stateful::account_with_data(pool, request, lookup_table).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::update_context_slot(report, account_result.context.slot); + let account = match account_result.account { + std::option::Option::Some(value) => value, + std::option::Option::None => { + crate::solana_stateful::push_check( + report, + "alt_account_exists", + false, + "lookup table account does not exist", + ); + return std::result::Result::Ok(()); + }, + }; + crate::solana_stateful::push_check( + report, + "alt_owner_matches", + account.owner.0 == kb_program_ids::ADDRESS_LOOKUP_TABLE_PROGRAM_ID, + "lookup table account must be owned by the Address Lookup Table program", + ); + crate::solana_stateful::push_check( + report, + "alt_not_executable", + !account.executable, + "lookup table state account must not be executable", + ); + let state = match crate::solana_stateful::parse_alt_state(account.data.as_slice()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + crate::solana_stateful::push_check( + report, + "alt_state_decodes", + false, + error.to_string(), + ); + return std::result::Result::Ok(()); + }, + }; + crate::solana_stateful::push_fact(report, "alt_address_count", state.address_count); + crate::solana_stateful::push_fact(report, "alt_deactivation_slot", state.deactivation_slot); + crate::solana_stateful::push_check( + report, + "alt_authority_matches", + state.authority.as_deref() == std::option::Option::Some(authority.0.as_str()), + "lookup table authority must match the requested authority", + ); + let active = state.deactivation_slot == u64::MAX; + match action { + crate::solana_stateful::AltAction::Extend { payer_present, added_addresses } => { + crate::solana_stateful::push_check( + report, + "alt_extend_active", + active, + "lookup table must be active before extension", + ); + let target_count = state.address_count.saturating_add(added_addresses); + crate::solana_stateful::push_fact(report, "alt_target_address_count", target_count); + crate::solana_stateful::push_check( + report, + "alt_extend_capacity", + target_count <= crate::solana_stateful::ALT_MAX_ADDRESSES, + "lookup table cannot exceed 256 addresses", + ); + let target_space = crate::solana_stateful::ALT_META_SIZE + .saturating_add(target_count.saturating_mul(32)); + let rent = match crate::solana_stateful::rent_minimum( + pool, + request, + target_space as u64, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let top_up = rent.saturating_sub(account.lamports); + crate::solana_stateful::push_fact(report, "alt_target_space", target_space); + crate::solana_stateful::push_fact(report, "alt_rent_top_up_lamports", top_up); + crate::solana_stateful::push_check( + report, + "alt_extend_payer_available_when_needed", + top_up == 0 || payer_present, + "ALT extension requires the optional payer/system pair when rent top-up is needed", + ); + }, + crate::solana_stateful::AltAction::Freeze => { + crate::solana_stateful::push_check( + report, + "alt_freeze_active", + active, + "lookup table must be active before freezing", + ); + crate::solana_stateful::push_check( + report, + "alt_freeze_non_empty", + state.address_count > 0, + "empty lookup tables cannot be frozen", + ); + }, + crate::solana_stateful::AltAction::Deactivate => { + crate::solana_stateful::push_check( + report, + "alt_deactivate_active", + active, + "lookup table must still be active before deactivation", + ); + }, + crate::solana_stateful::AltAction::Close => { + crate::solana_stateful::push_check( + report, + "alt_close_deactivated", + !active, + "lookup table must be deactivated before closing", + ); + if !active { + let epoch = match crate::solana_stateful::epoch_info(pool, request).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let last_valid = + solana_address_lookup_table_interface::state::estimate_last_valid_slot( + state.deactivation_slot, + ); + crate::solana_stateful::push_fact(report, "alt_close_last_valid_slot", last_valid); + crate::solana_stateful::push_check( + report, + "alt_close_cooldown_elapsed", + epoch.absolute_slot > last_valid, + "lookup table cooldown must be conservatively elapsed before close", + ); + } + }, + } + return std::result::Result::Ok(()); +} + +fn parse_alt_state( + data: &[u8], +) -> kb_core::Result { + if data.len() < crate::solana_stateful::ALT_META_SIZE { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_alt_data_short", + "lookup table account data is shorter than the 56-byte metadata region", + )); + } + let tag = match crate::solana_stateful::read_u32(data, 0, "ALT program state tag") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if tag != 1 { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_alt_uninitialized", + "lookup table account is not in the initialized LookupTable program state", + )); + } + let deactivation_slot = match crate::solana_stateful::read_u64(data, 4, "ALT deactivation slot") + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority = match data[21] { + 0 => std::option::Option::None, + 1 => { + let bytes = match data.get(22..54) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_alt_authority_short", + "lookup table authority bytes are truncated", + )); + }, + }; + let array = match <[u8; 32]>::try_from(bytes) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_alt_authority_short", + "lookup table authority bytes are not exactly 32 bytes", + )); + }, + }; + std::option::Option::Some(solana_pubkey::Pubkey::new_from_array(array).to_string()) + }, + value => { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_alt_authority_option_invalid", + format!("lookup table authority option tag must be 0 or 1 but received {value}"), + )); + }, + }; + let address_bytes = data.len() - crate::solana_stateful::ALT_META_SIZE; + if address_bytes % 32 != 0 { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_alt_addresses_misaligned", + "lookup table address bytes must be aligned to 32-byte public keys", + )); + } + let address_count = address_bytes / 32; + if address_count > crate::solana_stateful::ALT_MAX_ADDRESSES { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_alt_capacity_invalid", + "lookup table account contains more than 256 addresses", + )); + } + return std::result::Result::Ok(crate::solana_stateful::AddressLookupTableState { + deactivation_slot, + authority, + address_count, + }); +} + +async fn inspect_config_create( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::solana_stateful::SolanaCoreStatefulReadinessRequest, + report: &mut crate::solana_stateful::SolanaCoreStatefulReadinessReport, + config_account: &kb_model::Pubkey, + lamports: u64, + max_data_space: u64, + max_key_count: usize, +) -> kb_core::Result<()> { + let account_result = + match crate::solana_stateful::account_with_data(pool, request, config_account).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::update_context_slot(report, account_result.context.slot); + crate::solana_stateful::push_check( + report, + "config_create_target_absent", + account_result.account.is_none(), + "new Config account must not already exist", + ); + let key_bytes = crate::solana_stateful::compact_u16_length(max_key_count) + .saturating_add(max_key_count.saturating_mul(33)); + let space = match max_data_space.checked_add(key_bytes as u64) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_config_space_overflow", + "Config account allocation length overflow", + )); + }, + }; + let rent = match crate::solana_stateful::rent_minimum(pool, request, space).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::push_fact(report, "config_account_space", space); + crate::solana_stateful::push_fact(report, "minimum_rent_lamports", rent); + crate::solana_stateful::push_check( + report, + "config_create_rent_sufficient", + lamports >= rent, + "Config account lamports must meet the current rent-exempt minimum", + ); + return std::result::Result::Ok(()); +} + +async fn inspect_config_store( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::solana_stateful::SolanaCoreStatefulReadinessRequest, + report: &mut crate::solana_stateful::SolanaCoreStatefulReadinessReport, + config_account: &kb_model::Pubkey, + key_count: usize, + data_length: usize, +) -> kb_core::Result<()> { + let account_result = + match crate::solana_stateful::account_with_data(pool, request, config_account).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::update_context_slot(report, account_result.context.slot); + let account = match account_result.account { + std::option::Option::Some(value) => value, + std::option::Option::None => { + crate::solana_stateful::push_check( + report, + "config_store_account_exists", + false, + "Config account does not exist", + ); + return std::result::Result::Ok(()); + }, + }; + crate::solana_stateful::push_check( + report, + "config_store_owner_matches", + account.owner.0 == kb_program_ids::CONFIG_PROGRAM_ID, + "Config account must be owned by the Config program", + ); + crate::solana_stateful::push_check( + report, + "config_store_not_executable", + !account.executable, + "Config state account must not be executable", + ); + let required = crate::solana_stateful::compact_u16_length(key_count) + .saturating_add(key_count.saturating_mul(33)) + .saturating_add(data_length); + crate::solana_stateful::push_fact(report, "config_required_space", required); + crate::solana_stateful::push_fact(report, "config_existing_space", account.space); + crate::solana_stateful::push_check( + report, + "config_store_space_sufficient", + account.space >= required as u64, + "Config account allocation must fit the complete encoded key list and opaque data", + ); + return std::result::Result::Ok(()); +} + +async fn inspect_feature_activate( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::solana_stateful::SolanaCoreStatefulReadinessRequest, + report: &mut crate::solana_stateful::SolanaCoreStatefulReadinessReport, + feature_account: &kb_model::Pubkey, + lamports: u64, +) -> kb_core::Result<()> { + let account_result = + match crate::solana_stateful::account_with_data(pool, request, feature_account).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::update_context_slot(report, account_result.context.slot); + let existing_lamports = match account_result.account.as_ref() { + std::option::Option::Some(account) => account.lamports, + std::option::Option::None => 0, + }; + let reusable = account_result.account.as_ref().map(|account| { + return account.owner.0 == kb_program_ids::SYSTEM_PROGRAM_ID + && !account.executable + && account.space == 0 + && account.data.is_empty(); + }); + crate::solana_stateful::push_check( + report, + "feature_activate_target_available", + match reusable { + std::option::Option::Some(value) => value, + std::option::Option::None => true, + }, + "feature target must be absent or a prefunded zero-space System account", + ); + let rent = match crate::solana_stateful::rent_minimum( + pool, + request, + crate::solana_stateful::FEATURE_ACCOUNT_SIZE, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let required_top_up = rent.saturating_sub(existing_lamports); + crate::solana_stateful::push_fact(report, "feature_existing_lamports", existing_lamports); + crate::solana_stateful::push_fact(report, "feature_required_top_up_lamports", required_top_up); + crate::solana_stateful::push_check( + report, + "feature_activate_rent_sufficient", + lamports >= required_top_up, + "feature activation transfer must cover the measured rent shortfall", + ); + return std::result::Result::Ok(()); +} + +async fn inspect_feature_revoke( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::solana_stateful::SolanaCoreStatefulReadinessRequest, + report: &mut crate::solana_stateful::SolanaCoreStatefulReadinessReport, + feature_account: &kb_model::Pubkey, +) -> kb_core::Result<()> { + let account_result = + match crate::solana_stateful::account_with_data(pool, request, feature_account).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::update_context_slot(report, account_result.context.slot); + let account = match account_result.account { + std::option::Option::Some(value) => value, + std::option::Option::None => { + crate::solana_stateful::push_check( + report, + "feature_revoke_account_exists", + false, + "feature account does not exist", + ); + return std::result::Result::Ok(()); + }, + }; + crate::solana_stateful::push_check( + report, + "feature_revoke_owner_matches", + account.owner.0 == kb_program_ids::FEATURE_PROGRAM_ID, + "feature account must be owned by the Feature program", + ); + crate::solana_stateful::push_check( + report, + "feature_revoke_not_executable", + !account.executable, + "feature state account must not be executable", + ); + let pending = account.data.len() == crate::solana_stateful::FEATURE_ACCOUNT_SIZE as usize + && account.data.first() == std::option::Option::Some(&0); + crate::solana_stateful::push_check( + report, + "feature_revoke_pending", + pending, + "only a pending feature state encoded as Feature { activated_at: None } may be revoked", + ); + return std::result::Result::Ok(()); +} + +async fn inspect_slashing_submit( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::solana_stateful::SolanaCoreStatefulReadinessRequest, + report: &mut crate::solana_stateful::SolanaCoreStatefulReadinessReport, + proof_account: &kb_model::Pubkey, + report_account: &kb_model::Pubkey, + report_lamports: u64, + proof_offset: u64, + slot: u64, +) -> kb_core::Result<()> { + let proof_result = + match crate::solana_stateful::account_with_data(pool, request, proof_account).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::update_context_slot(report, proof_result.context.slot); + let proof = match proof_result.account { + std::option::Option::Some(value) => value, + std::option::Option::None => { + crate::solana_stateful::push_check( + report, + "slashing_proof_account_exists", + false, + "duplicate-block proof account does not exist", + ); + return std::result::Result::Ok(()); + }, + }; + crate::solana_stateful::push_check( + report, + "slashing_proof_not_executable", + !proof.executable, + "duplicate-block proof account must not be executable", + ); + let packed_length = match crate::solana_stateful::duplicate_block_proof_length( + proof.data.as_slice(), + proof_offset, + ) { + std::result::Result::Ok(value) => { + crate::solana_stateful::push_check( + report, + "slashing_proof_shape_valid", + true, + "duplicate-block proof contains two bounded length-prefixed shreds", + ); + value + }, + std::result::Result::Err(error) => { + crate::solana_stateful::push_check( + report, + "slashing_proof_shape_valid", + false, + error.to_string(), + ); + 0 + }, + }; + crate::solana_stateful::push_fact(report, "slashing_packed_proof_bytes", packed_length); + let report_result = + match crate::solana_stateful::account_with_data(pool, request, report_account).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::update_context_slot(report, report_result.context.slot); + let report_reusable = report_result.account.as_ref().map(|account| { + return account.owner.0 == kb_program_ids::SYSTEM_PROGRAM_ID + && !account.executable + && account.space == 0 + && account.data.is_empty(); + }); + crate::solana_stateful::push_check( + report, + "slashing_report_target_available", + match report_reusable { + std::option::Option::Some(value) => value, + std::option::Option::None => true, + }, + "violation report PDA must be absent or an empty prefunded System account", + ); + let epoch = match crate::solana_stateful::epoch_info(pool, request).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::push_fact(report, "absolute_slot", epoch.absolute_slot); + crate::solana_stateful::push_fact(report, "slots_in_epoch", epoch.slots_in_epoch); + let age = epoch.absolute_slot.saturating_sub(slot); + crate::solana_stateful::push_check( + report, + "slashing_slot_not_future", + slot <= epoch.absolute_slot, + "reported duplicate-block slot must not be in the future", + ); + crate::solana_stateful::push_check( + report, + "slashing_slot_within_one_epoch", + slot <= epoch.absolute_slot && age <= epoch.slots_in_epoch, + "duplicate-block proof must be submitted within one epoch worth of slots", + ); + if packed_length > 0 { + let report_size = + crate::solana_stateful::SLASHING_REPORT_HEADER_SIZE.saturating_add(packed_length); + let rent = + match crate::solana_stateful::rent_minimum(pool, request, report_size as u64).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::push_fact(report, "slashing_report_space", report_size); + crate::solana_stateful::push_fact(report, "minimum_rent_lamports", rent); + crate::solana_stateful::push_check( + report, + "slashing_report_rent_sufficient", + report_lamports >= rent, + "report_lamports must cover the exact report header and retained proof bytes", + ); + } + return std::result::Result::Ok(()); +} + +async fn inspect_slashing_close( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::solana_stateful::SolanaCoreStatefulReadinessRequest, + report: &mut crate::solana_stateful::SolanaCoreStatefulReadinessReport, + report_account: &kb_model::Pubkey, + destination: &kb_model::Pubkey, +) -> kb_core::Result<()> { + let account_result = + match crate::solana_stateful::account_with_data(pool, request, report_account).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::update_context_slot(report, account_result.context.slot); + let account = match account_result.account { + std::option::Option::Some(value) => value, + std::option::Option::None => { + crate::solana_stateful::push_check( + report, + "slashing_report_exists", + false, + "violation report account does not exist", + ); + return std::result::Result::Ok(()); + }, + }; + crate::solana_stateful::push_check( + report, + "slashing_report_owner_matches", + account.owner.0 == kb_program_ids::SLASHING_PROGRAM_ID, + "violation report must be owned by the Slashing program", + ); + crate::solana_stateful::push_check( + report, + "slashing_report_not_executable", + !account.executable, + "violation report state account must not be executable", + ); + if account.data.len() < crate::solana_stateful::SLASHING_REPORT_HEADER_SIZE { + crate::solana_stateful::push_check( + report, + "slashing_report_decodes", + false, + "violation report is shorter than its 114-byte fixed header", + ); + return std::result::Result::Ok(()); + } + let version = account.data[0]; + let retained_destination = match crate::solana_stateful::pubkey_text(&account.data[33..65]) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + crate::solana_stateful::push_check( + report, + "slashing_report_decodes", + false, + error.to_string(), + ); + return std::result::Result::Ok(()); + }, + }; + let report_epoch = match crate::solana_stateful::read_u64( + account.data.as_slice(), + 65, + "Slashing report epoch", + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::push_fact(report, "slashing_report_version", version); + crate::solana_stateful::push_fact(report, "slashing_report_epoch", report_epoch); + crate::solana_stateful::push_check( + report, + "slashing_report_version_supported", + version == 1, + "violation report version must be 1", + ); + crate::solana_stateful::push_check( + report, + "slashing_report_destination_matches", + retained_destination == destination.0, + "close destination must match the destination retained in the violation report", + ); + let epoch = match crate::solana_stateful::epoch_info(pool, request).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::push_fact(report, "current_epoch", epoch.epoch); + crate::solana_stateful::push_check( + report, + "slashing_report_retention_elapsed", + epoch.epoch >= report_epoch.saturating_add(3), + "violation report must remain on-chain for at least three epochs", + ); + return std::result::Result::Ok(()); +} + +async fn inspect_zk_verify( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::solana_stateful::SolanaCoreStatefulReadinessRequest, + report: &mut crate::solana_stateful::SolanaCoreStatefulReadinessReport, + proof_type: kb_executor_solana_core::ZkElGamalProofType, + proof_account: std::option::Option<(&kb_model::Pubkey, u32)>, + context_state: std::option::Option<&kb_executor_solana_core::ZkElGamalContextState>, +) -> kb_core::Result<()> { + if let std::option::Option::Some((account_pubkey, offset)) = proof_account { + let proof_result = + match crate::solana_stateful::account_with_data(pool, request, account_pubkey).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::update_context_slot(report, proof_result.context.slot); + let proof = match proof_result.account { + std::option::Option::Some(value) => value, + std::option::Option::None => { + crate::solana_stateful::push_check( + report, + "zk_proof_account_exists", + false, + "ZK proof account does not exist", + ); + return std::result::Result::Ok(()); + }, + }; + crate::solana_stateful::push_check( + report, + "zk_proof_account_not_executable", + !proof.executable, + "ZK proof account must not be executable", + ); + let end = (offset as usize).checked_add(proof_type.proof_data_size()); + crate::solana_stateful::push_check( + report, + "zk_proof_range_available", + match end { + std::option::Option::Some(end) => end <= proof.data.len(), + std::option::Option::None => false, + }, + "ZK proof account must contain the complete proof POD at the requested offset", + ); + } + let context = match context_state { + std::option::Option::Some(value) => value, + std::option::Option::None => { + crate::solana_stateful::push_fact(report, "zk_context_state", "not_requested"); + return std::result::Result::Ok(()); + }, + }; + let context_result = + match crate::solana_stateful::account_with_data(pool, request, &context.account).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::update_context_slot(report, context_result.context.slot); + let account = match context_result.account { + std::option::Option::Some(value) => value, + std::option::Option::None => { + crate::solana_stateful::push_check( + report, + "zk_context_account_exists", + false, + "preallocated ZK context-state account does not exist", + ); + return std::result::Result::Ok(()); + }, + }; + let expected_size = proof_type.context_state_size(); + crate::solana_stateful::push_fact(report, "zk_context_expected_space", expected_size); + crate::solana_stateful::push_fact(report, "zk_context_existing_space", account.space); + crate::solana_stateful::push_check( + report, + "zk_context_owner_matches", + account.owner.0 == kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID, + "ZK context-state account must be owned by the ZK ElGamal Proof program", + ); + crate::solana_stateful::push_check( + report, + "zk_context_not_executable", + !account.executable, + "ZK context-state account must not be executable", + ); + crate::solana_stateful::push_check( + report, + "zk_context_space_exact", + account.space == expected_size as u64 && account.data.len() == expected_size, + "ZK context-state allocation must exactly match the official ProofContextState size", + ); + crate::solana_stateful::push_check( + report, + "zk_context_uninitialized", + account.data.iter().all(|byte| return *byte == 0), + "preallocated ZK context-state account must still be zero-initialized", + ); + let rent = match crate::solana_stateful::rent_minimum(pool, request, expected_size as u64).await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::push_fact(report, "minimum_rent_lamports", rent); + crate::solana_stateful::push_check( + report, + "zk_context_rent_exempt", + account.lamports >= rent, + "preallocated ZK context-state account must be rent exempt", + ); + return std::result::Result::Ok(()); +} + +async fn inspect_zk_close( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::solana_stateful::SolanaCoreStatefulReadinessRequest, + report: &mut crate::solana_stateful::SolanaCoreStatefulReadinessReport, + context_state: &kb_model::Pubkey, + authority: &kb_model::Pubkey, +) -> kb_core::Result<()> { + let account_result = + match crate::solana_stateful::account_with_data(pool, request, context_state).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_stateful::update_context_slot(report, account_result.context.slot); + let account = match account_result.account { + std::option::Option::Some(value) => value, + std::option::Option::None => { + crate::solana_stateful::push_check( + report, + "zk_context_account_exists", + false, + "ZK context-state account does not exist", + ); + return std::result::Result::Ok(()); + }, + }; + crate::solana_stateful::push_check( + report, + "zk_context_owner_matches", + account.owner.0 == kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID, + "ZK context-state account must be owned by the ZK ElGamal Proof program", + ); + crate::solana_stateful::push_check( + report, + "zk_context_not_executable", + !account.executable, + "ZK context-state account must not be executable", + ); + if account.data.len() < crate::solana_stateful::ZK_CONTEXT_META_SIZE { + crate::solana_stateful::push_check( + report, + "zk_context_meta_decodes", + false, + "ZK context-state account is shorter than its 33-byte generic metadata", + ); + return std::result::Result::Ok(()); + } + let retained_authority = match crate::solana_stateful::pubkey_text(&account.data[..32]) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + crate::solana_stateful::push_check( + report, + "zk_context_meta_decodes", + false, + error.to_string(), + ); + return std::result::Result::Ok(()); + }, + }; + let proof_type = crate::solana_stateful::proof_type_from_discriminator(account.data[32]); + crate::solana_stateful::push_check( + report, + "zk_context_authority_matches", + retained_authority == authority.0, + "close authority must match the authority retained in ProofContextState", + ); + crate::solana_stateful::push_check( + report, + "zk_context_proof_type_supported", + proof_type.is_some(), + "ProofContextState must contain a supported proof type discriminator", + ); + if let std::option::Option::Some(value) = proof_type { + let expected_size = value.context_state_size(); + crate::solana_stateful::push_fact(report, "zk_context_proof_type", value.discriminator()); + crate::solana_stateful::push_fact(report, "zk_context_expected_space", expected_size); + crate::solana_stateful::push_check( + report, + "zk_context_space_exact", + account.space == expected_size as u64 && account.data.len() == expected_size, + "stored ZK context-state allocation must match its proof type", + ); + } + return std::result::Result::Ok(()); +} + +fn proof_type_from_discriminator( + discriminator: u8, +) -> std::option::Option { + return match discriminator { + 1 => std::option::Option::Some(kb_executor_solana_core::ZkElGamalProofType::ZeroCiphertext), + 2 => std::option::Option::Some( + kb_executor_solana_core::ZkElGamalProofType::CiphertextCiphertextEquality, + ), + 3 => std::option::Option::Some( + kb_executor_solana_core::ZkElGamalProofType::CiphertextCommitmentEquality, + ), + 4 => std::option::Option::Some(kb_executor_solana_core::ZkElGamalProofType::PubkeyValidity), + 5 => std::option::Option::Some( + kb_executor_solana_core::ZkElGamalProofType::PercentageWithCap, + ), + 6 => std::option::Option::Some( + kb_executor_solana_core::ZkElGamalProofType::BatchedRangeProofU64, + ), + 7 => std::option::Option::Some( + kb_executor_solana_core::ZkElGamalProofType::BatchedRangeProofU128, + ), + 8 => std::option::Option::Some( + kb_executor_solana_core::ZkElGamalProofType::BatchedRangeProofU256, + ), + 9 => std::option::Option::Some( + kb_executor_solana_core::ZkElGamalProofType::GroupedCiphertext2HandlesValidity, + ), + 10 => std::option::Option::Some( + kb_executor_solana_core::ZkElGamalProofType::BatchedGroupedCiphertext2HandlesValidity, + ), + 11 => std::option::Option::Some( + kb_executor_solana_core::ZkElGamalProofType::GroupedCiphertext3HandlesValidity, + ), + 12 => std::option::Option::Some( + kb_executor_solana_core::ZkElGamalProofType::BatchedGroupedCiphertext3HandlesValidity, + ), + _ => std::option::Option::None, + }; +} + +fn duplicate_block_proof_length(data: &[u8], offset: u64) -> kb_core::Result { + let start = match usize::try_from(offset) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_slashing_offset_invalid", + "duplicate-block proof offset does not fit the local address space", + )); + }, + }; + let first_length = + match crate::solana_stateful::read_u32(data, start, "first duplicate-block shred length") { + std::result::Result::Ok(value) => value as usize, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if first_length == 0 || first_length > crate::solana_stateful::MAX_SHRED_SIZE { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_slashing_first_shred_length_invalid", + "first duplicate-block shred length must be between 1 and 1232 bytes", + )); + } + let second_length_offset = + match start.checked_add(4).and_then(|value| return value.checked_add(first_length)) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_slashing_proof_overflow", + "duplicate-block proof length arithmetic overflow", + )); + }, + }; + let second_length = match crate::solana_stateful::read_u32( + data, + second_length_offset, + "second duplicate-block shred length", + ) { + std::result::Result::Ok(value) => value as usize, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if second_length == 0 || second_length > crate::solana_stateful::MAX_SHRED_SIZE { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_slashing_second_shred_length_invalid", + "second duplicate-block shred length must be between 1 and 1232 bytes", + )); + } + let total = 4_usize + .checked_add(first_length) + .and_then(|value| return value.checked_add(4)) + .and_then(|value| return value.checked_add(second_length)); + let total = match total { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_slashing_proof_overflow", + "duplicate-block proof length arithmetic overflow", + )); + }, + }; + let end = match start.checked_add(total) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_slashing_proof_overflow", + "duplicate-block proof end offset overflow", + )); + }, + }; + if end > data.len() { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_slashing_proof_truncated", + "duplicate-block proof account does not contain both declared shreds", + )); + } + return std::result::Result::Ok(total); +} + +fn compact_u16_length(value: usize) -> usize { + if value < 0x80 { + return 1; + } + if value < 0x4000 { + return 2; + } + return 3; +} + +fn read_u32(data: &[u8], offset: usize, label: &str) -> kb_core::Result { + let end = match offset.checked_add(4) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_integer_overflow", + format!("{label} offset overflow"), + )); + }, + }; + let bytes = match data.get(offset..end) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_integer_truncated", + format!("{label} is truncated"), + )); + }, + }; + let array = match <[u8; 4]>::try_from(bytes) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_integer_truncated", + format!("{label} is not exactly four bytes"), + )); + }, + }; + return std::result::Result::Ok(u32::from_le_bytes(array)); +} + +fn read_u64(data: &[u8], offset: usize, label: &str) -> kb_core::Result { + let end = match offset.checked_add(8) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_integer_overflow", + format!("{label} offset overflow"), + )); + }, + }; + let bytes = match data.get(offset..end) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_integer_truncated", + format!("{label} is truncated"), + )); + }, + }; + let array = match <[u8; 8]>::try_from(bytes) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_integer_truncated", + format!("{label} is not exactly eight bytes"), + )); + }, + }; + return std::result::Result::Ok(u64::from_le_bytes(array)); +} + +fn pubkey_text(bytes: &[u8]) -> kb_core::Result { + let array = match <[u8; 32]>::try_from(bytes) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_stateful_pubkey_length_invalid", + "stateful account field is not exactly 32 bytes", + )); + }, + }; + return std::result::Result::Ok(solana_pubkey::Pubkey::new_from_array(array).to_string()); +} + +#[cfg(test)] +mod tests { + #[test] + fn cluster_policy_restricts_stateful_reads_to_localnet_and_devnet() { + let devnet = kb_rpc::GenesisHashResult { + genesis_hash: std::string::String::from("devnet"), + classified_cluster: std::option::Option::Some( + kb_execution_api::ExecutionCluster::Devnet, + ), + }; + assert!( + crate::solana_stateful::validate_cluster( + kb_execution_api::ExecutionCluster::Devnet, + &devnet, + ) + .is_ok() + ); + assert!( + crate::solana_stateful::validate_cluster( + kb_execution_api::ExecutionCluster::Localnet, + &devnet, + ) + .is_err() + ); + let local = kb_rpc::GenesisHashResult { + genesis_hash: std::string::String::from("local"), + classified_cluster: std::option::Option::None, + }; + assert!( + crate::solana_stateful::validate_cluster( + kb_execution_api::ExecutionCluster::Localnet, + &local, + ) + .is_ok() + ); + } + + #[test] + fn alt_state_parser_preserves_authority_lifecycle_and_capacity() { + let authority = solana_pubkey::Pubkey::new_unique(); + let mut data = vec![0_u8; 56 + 64]; + data[..4].copy_from_slice(&1_u32.to_le_bytes()); + data[4..12].copy_from_slice(&u64::MAX.to_le_bytes()); + data[21] = 1; + let authority_bytes = authority.to_bytes(); + data[22..54].copy_from_slice(&authority_bytes); + let state = crate::solana_stateful::parse_alt_state(data.as_slice()) + .unwrap_or_else(|error| panic!("unexpected ALT state error: {error}")); + assert_eq!(state.deactivation_slot, u64::MAX); + assert_eq!(state.authority, std::option::Option::Some(authority.to_string())); + assert_eq!(state.address_count, 2); + data.push(0); + assert!(crate::solana_stateful::parse_alt_state(data.as_slice()).is_err()); + } + + #[test] + fn slashing_proof_shape_is_bounded_and_exact() { + let mut data = std::vec::Vec::new(); + data.extend_from_slice(&3_u32.to_le_bytes()); + data.extend_from_slice(&[1, 2, 3]); + data.extend_from_slice(&2_u32.to_le_bytes()); + data.extend_from_slice(&[4, 5]); + assert_eq!( + crate::solana_stateful::duplicate_block_proof_length(data.as_slice(), 0) + .unwrap_or_else(|error| panic!("unexpected proof shape error: {error}")), + 13, + ); + assert!(crate::solana_stateful::duplicate_block_proof_length(&data[..12], 0).is_err()); + let mut too_large = std::vec::Vec::new(); + too_large.extend_from_slice(&1_233_u32.to_le_bytes()); + assert!( + crate::solana_stateful::duplicate_block_proof_length(too_large.as_slice(), 0,).is_err() + ); + } + + #[test] + fn proof_discriminators_map_to_exact_context_sizes() { + for discriminator in 1_u8..=12 { + let proof_type = crate::solana_stateful::proof_type_from_discriminator(discriminator) + .unwrap_or_else(|| panic!("missing proof type {discriminator}")); + assert_eq!(proof_type.discriminator(), discriminator); + assert!(proof_type.context_state_size() > crate::solana_stateful::ZK_CONTEXT_META_SIZE); + } + assert_eq!( + kb_executor_solana_core::ZkElGamalProofType::BatchedRangeProofU64.context_state_size(), + kb_executor_solana_core::ZkElGamalProofType::BatchedRangeProofU256.context_state_size(), + ); + assert!(crate::solana_stateful::proof_type_from_discriminator(0).is_none()); + } + + #[test] + fn compact_u16_length_matches_config_key_boundaries() { + assert_eq!(crate::solana_stateful::compact_u16_length(0), 1); + assert_eq!(crate::solana_stateful::compact_u16_length(127), 1); + assert_eq!(crate::solana_stateful::compact_u16_length(128), 2); + assert_eq!(crate::solana_stateful::compact_u16_length(16_384), 3); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_correlation.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_correlation.rs new file mode 100644 index 0000000..87461c0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_correlation.rs @@ -0,0 +1,242 @@ +// file: kb_pipeline/src/solana_token_2022_correlation.rs +// version: 1 + +//! Deterministic Token-2022 instruction-to-state correlation. + +/// Correlation outcome between one committed instruction fact and one final state snapshot. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum Token2022CorrelationStatus { + /// The final snapshot contains the extension expected by the instruction fact. + Confirmed, + /// The final snapshot does not contain the extension expected by the instruction fact. + Contradicted, + /// The instruction output is outside the supported correlation inventory. + NotApplicable, +} + +/// Deterministic correlation report for one instruction output and one final snapshot. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct Token2022StateCorrelationReport { + /// Stable caller-provided correlation identity. + pub correlation_key: std::string::String, + /// Canonical account identity checked against the snapshot. + pub account: kb_model::Pubkey, + /// Instruction operation or risk fact used for correlation. + pub fact_code: std::string::String, + /// Extension expected from the fact when the fact is supported. + pub expected_extension: std::option::Option, + /// Final correlation outcome. + pub status: Token2022CorrelationStatus, + /// Snapshot context slot. + pub snapshot_slot: u64, + /// Whether the snapshot contains the expected extension. + pub extension_present: std::option::Option, + /// Explicit semantic limitation of this correlation. + pub fact_only: bool, +} + +/// Correlates one materialized instruction fact with one authoritative Token-2022 snapshot. +pub fn correlate_token_2022_instruction_with_snapshot( + correlation_key: &str, + account: &kb_model::Pubkey, + instruction_output: &kb_materializer_api::MaterializedOutput, + snapshot: &crate::Token2022StatefulSnapshotBundle, +) -> kb_core::Result { + if correlation_key.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Token-2022 correlation key must not be empty", + )); + } + if snapshot.account_key != account.0 { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_correlation_account_mismatch", + format!( + "Token-2022 correlation expected account {}, got snapshot {}", + account.0, snapshot.account_key + ), + )); + } + let fact_code = match instruction_output.payload_json.get("riskKind") { + std::option::Option::Some(value) => value.as_str().unwrap_or("").to_string(), + std::option::Option::None => instruction_output + .payload_json + .get("operation") + .and_then(serde_json::Value::as_str) + .unwrap_or("") + .to_string(), + }; + if fact_code.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_correlation_fact_missing", + "Token-2022 correlation requires riskKind or operation in the instruction output", + )); + } + let expected_extension = expected_extension(fact_code.as_str()); + let extension_present = expected_extension.map(|extension| { + return snapshot.extension_names.iter().any(|candidate| return candidate == extension); + }); + let status = match extension_present { + std::option::Option::Some(true) => Token2022CorrelationStatus::Confirmed, + std::option::Option::Some(false) => Token2022CorrelationStatus::Contradicted, + std::option::Option::None => Token2022CorrelationStatus::NotApplicable, + }; + return std::result::Result::Ok(Token2022StateCorrelationReport { + correlation_key: correlation_key.to_string(), + account: account.clone(), + fact_code, + expected_extension: expected_extension.map(str::to_string), + status, + snapshot_slot: snapshot.slot, + extension_present, + fact_only: true, + }); +} + +fn expected_extension(fact_code: &str) -> std::option::Option<&'static str> { + return match fact_code { + "default_account_state_configured" + | "initialize_default_account_state" + | "update_default_account_state" => std::option::Option::Some("default_account_state"), + "required_transfer_memos_enabled" + | "required_transfer_memos_disabled" + | "enable_required_transfer_memos" + | "disable_required_transfer_memos" => std::option::Option::Some("memo_transfer"), + "cpi_guard_enabled" | "cpi_guard_disabled" | "enable_cpi_guard" | "disable_cpi_guard" => { + std::option::Option::Some("cpi_guard") + }, + "non_transferable_mint_configured" | "initialize_non_transferable_mint" => { + std::option::Option::Some("non_transferable") + }, + "pausable_mint_configured" + | "mint_activity_paused" + | "mint_activity_resumed" + | "initialize_pausable_config" + | "pause" + | "resume" => std::option::Option::Some("pausable"), + "permissioned_burn_configured" | "initialize_permissioned_burn" => { + std::option::Option::Some("permissioned_burn") + }, + "confidential_credits_enabled" + | "confidential_credits_disabled" + | "non_confidential_credits_enabled" + | "non_confidential_credits_disabled" + | "enable_confidential_credits" + | "disable_confidential_credits" + | "enable_non_confidential_credits" + | "disable_non_confidential_credits" => { + std::option::Option::Some("confidential_transfer_account") + }, + "initialize_transfer_fee_config" + | "set_transfer_fee" + | "withdraw_withheld_tokens_from_mint" + | "harvest_withheld_tokens_to_mint" => std::option::Option::Some("transfer_fee_config"), + "withdraw_withheld_tokens_from_accounts" | "transfer_checked_with_fee" => { + std::option::Option::Some("transfer_fee_amount") + }, + "initialize_confidential_transfer_fee_config" + | "withdraw_confidential_withheld_tokens_from_mint" + | "harvest_confidential_withheld_tokens_to_mint" + | "enable_confidential_harvest_to_mint" + | "disable_confidential_harvest_to_mint" => { + std::option::Option::Some("confidential_transfer_fee_config") + }, + "withdraw_confidential_withheld_tokens_from_accounts" + | "transfer_confidential_tokens_with_fee" => { + std::option::Option::Some("confidential_transfer_fee_amount") + }, + _ => std::option::Option::None, + }; +} + +#[cfg(test)] +mod tests { + fn snapshot(extension_names: &[&str]) -> crate::Token2022StatefulSnapshotBundle { + return crate::Token2022StatefulSnapshotBundle { + account_key: solana_pubkey::Pubkey::new_from_array([7u8; 32]).to_string(), + slot: 42, + state_kind: "mint".to_string(), + extension_names: extension_names + .iter() + .map(|value| return (*value).to_string()) + .collect(), + outputs: std::vec::Vec::new(), + }; + } + + fn output(field: &str, value: &str) -> kb_materializer_api::MaterializedOutput { + let mut payload = serde_json::Map::new(); + payload.insert(field.to_string(), serde_json::Value::String(value.to_string())); + return kb_materializer_api::MaterializedOutput { + output_key: "fact:0".to_string(), + family: kb_model::MaterializedEventFamily::Risk, + payload_json: serde_json::Value::Object(payload), + }; + } + + #[test] + fn known_fact_is_confirmed_or_contradicted_by_the_final_extension_inventory() { + let snapshot = snapshot(&["pausable"]); + let account = kb_model::Pubkey(snapshot.account_key.clone()); + let confirmed = super::correlate_token_2022_instruction_with_snapshot( + "corr:pause", + &account, + &output("riskKind", "mint_activity_paused"), + &snapshot, + ); + assert!(confirmed.is_ok()); + if let std::result::Result::Ok(confirmed) = confirmed { + assert_eq!(confirmed.status, super::Token2022CorrelationStatus::Confirmed); + assert_eq!(confirmed.extension_present, std::option::Option::Some(true)); + } + let contradicted = super::correlate_token_2022_instruction_with_snapshot( + "corr:fee", + &account, + &output("operation", "initialize_transfer_fee_config"), + &snapshot, + ); + assert!(contradicted.is_ok()); + if let std::result::Result::Ok(contradicted) = contradicted { + assert_eq!(contradicted.status, super::Token2022CorrelationStatus::Contradicted); + assert_eq!(contradicted.extension_present, std::option::Option::Some(false)); + } + } + + #[test] + fn unsupported_fact_is_explicitly_not_applicable_without_inventing_a_conclusion() { + let snapshot = snapshot(&[]); + let account = kb_model::Pubkey(snapshot.account_key.clone()); + let report = super::correlate_token_2022_instruction_with_snapshot( + "corr:unknown", + &account, + &output("operation", "unknown_future_operation"), + &snapshot, + ); + assert!(report.is_ok()); + if let std::result::Result::Ok(report) = report { + assert_eq!(report.status, super::Token2022CorrelationStatus::NotApplicable); + assert_eq!(report.extension_present, std::option::Option::None); + assert!(report.fact_only); + } + } + + #[test] + fn correlation_identity_and_account_mismatch_fail_closed() { + let snapshot = snapshot(&["memo_transfer"]); + let account = kb_model::Pubkey(snapshot.account_key.clone()); + let empty = super::correlate_token_2022_instruction_with_snapshot( + "", + &account, + &output("riskKind", "required_transfer_memos_enabled"), + &snapshot, + ); + assert!(empty.is_err()); + let wrong = kb_model::Pubkey(solana_pubkey::Pubkey::new_from_array([8u8; 32]).to_string()); + let mismatch = super::correlate_token_2022_instruction_with_snapshot( + "corr:mismatch", + &wrong, + &output("riskKind", "required_transfer_memos_enabled"), + &snapshot, + ); + assert!(mismatch.is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_crypto_preflight.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_crypto_preflight.rs new file mode 100644 index 0000000..0bb014b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_crypto_preflight.rs @@ -0,0 +1,337 @@ +// file: kb_pipeline/src/solana_token_2022_crypto_preflight.rs +// version: 2 + +//! Bounded cryptographic preflight for Token-2022 proof context-state accounts. + +/// Maximum number of distinct proof context-state accounts accepted by one request. +pub const MAX_TOKEN_2022_PROOF_CONTEXTS: usize = 8; +/// Generic metadata bytes retained by every ZK ElGamal proof context-state account. +pub const ZK_PROOF_CONTEXT_META_BYTES: usize = 33; + +/// One exact pre-verified proof context-state requirement. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Token2022ProofContextRequirement { + /// Stable semantic role used in diagnostics. + pub role: std::string::String, + /// Canonical proof context-state account. + pub account: kb_model::Pubkey, + /// Exact proof kind expected by the Token-2022 builder. + pub proof_type: kb_executor_solana_core::ZkElGamalProofType, + /// Optional authority retained by the context-state account. + pub expected_authority: std::option::Option, +} + +/// One bounded cryptographic preflight request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Token2022CryptographicPreflightRequest { + /// Endpoint role used by every account read. + pub query_role: std::string::String, + /// Optional minimum RPC context slot. + pub min_context_slot: std::option::Option, + /// Ordered proof context-state requirements. + pub proof_contexts: std::vec::Vec, +} + +/// One validated proof context-state result. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct Token2022ProofContextReport { + /// Stable semantic role. + pub role: std::string::String, + /// Canonical proof context-state account. + pub account: kb_model::Pubkey, + /// Official one-byte proof discriminator. + pub proof_discriminator: u8, + /// Exact context-state size for the proof type. + pub context_state_bytes: usize, + /// Context slot returned by the endpoint. + pub context_slot: u64, + /// Ordered successful checks. + pub checks: std::vec::Vec, +} + +/// Complete cryptographic preflight report. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct Token2022CryptographicPreflightReport { + /// Commitment used for all reads. + pub commitment: std::string::String, + /// Highest context slot observed across all reads. + pub context_slot: u64, + /// Ordered validated proof contexts. + pub proof_contexts: std::vec::Vec, +} + +/// Reads and validates all pre-verified proof context-state accounts required by one operation. +pub async fn inspect_token_2022_cryptographic_preflight( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::Token2022CryptographicPreflightRequest, +) -> kb_core::Result { + let requirements = match validate_proof_context_requirements(request) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut context_slot = request.min_context_slot.unwrap_or(0); + let mut reports = std::vec::Vec::with_capacity(requirements.len()); + for requirement in requirements { + let expected_size = requirement.proof_type.context_state_size(); + let config = match kb_rpc::GetAccountInfoConfig::new_with_data( + kb_rpc::RpcCommitmentLevel::Confirmed, + request.min_context_slot, + expected_size, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let result = match pool + .get_account_info_for_role(request.query_role.as_str(), &requirement.account, &config) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let report = match validate_proof_context_account(&requirement, &result) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + context_slot = context_slot.max(report.context_slot); + reports.push(report); + } + return std::result::Result::Ok(crate::Token2022CryptographicPreflightReport { + commitment: "confirmed".to_string(), + context_slot, + proof_contexts: reports, + }); +} + +fn validate_proof_context_requirements( + request: &crate::Token2022CryptographicPreflightRequest, +) -> kb_core::Result> { + if request.query_role.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Token-2022 cryptographic preflight query_role must not be empty", + )); + } + if request.proof_contexts.len() > crate::MAX_TOKEN_2022_PROOF_CONTEXTS { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_proof_context_limit_exceeded", + format!( + "Token-2022 cryptographic preflight accepts at most {} proof contexts", + crate::MAX_TOKEN_2022_PROOF_CONTEXTS + ), + )); + } + let mut indexes = std::collections::BTreeMap::::new(); + let mut unique = std::vec::Vec::::new(); + for requirement in &request.proof_contexts { + if requirement.role.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Token-2022 proof context role must not be empty", + )); + } + if let std::option::Option::Some(index) = indexes.get(requirement.account.0.as_str()) { + if &unique[*index] != requirement { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_proof_context_conflicting_duplicate", + format!( + "Token-2022 proof context {} has conflicting requirements", + requirement.account.0 + ), + )); + } + continue; + } + indexes.insert(requirement.account.0.clone(), unique.len()); + unique.push(requirement.clone()); + } + return std::result::Result::Ok(unique); +} + +fn validate_proof_context_account( + requirement: &crate::Token2022ProofContextRequirement, + result: &kb_rpc::AccountInfoResult, +) -> kb_core::Result { + let account = match result.account.as_ref() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_proof_context_missing", + format!( + "Token-2022 proof context account {} does not exist", + requirement.account.0 + ), + )); + }, + }; + if account.owner.0 != kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_proof_context_owner_mismatch", + format!( + "Token-2022 proof context owner must be {}, got {}", + kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID, + account.owner.0 + ), + )); + } + if account.executable { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_proof_context_executable", + "Token-2022 proof context account must not be executable", + )); + } + let expected_size = requirement.proof_type.context_state_size(); + if account.space != expected_size as u64 || account.data.len() != expected_size { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_proof_context_size_mismatch", + format!( + "Token-2022 proof context {} must contain exactly {expected_size} bytes, got space {} and data {}", + requirement.account.0, + account.space, + account.data.len() + ), + )); + } + if account.data.len() < crate::ZK_PROOF_CONTEXT_META_BYTES { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Token-2022 proof context is shorter than its generic metadata", + )); + } + let discriminator = account.data[32]; + if discriminator != requirement.proof_type.discriminator() { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_proof_context_type_mismatch", + format!( + "Token-2022 proof context {} discriminator {} does not match expected {}", + requirement.account.0, + discriminator, + requirement.proof_type.discriminator() + ), + )); + } + let mut checks = std::vec![ + "zk_program_owner".to_string(), + "not_executable".to_string(), + "exact_context_state_size".to_string(), + "proof_type_discriminator".to_string(), + ]; + if let std::option::Option::Some(expected_authority) = requirement.expected_authority.as_ref() { + let retained_authority = bs58::encode(&account.data[..32]).into_string(); + if retained_authority != expected_authority.0 { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_proof_context_authority_mismatch", + format!( + "Token-2022 proof context {} retains authority {}, expected {}", + requirement.account.0, retained_authority, expected_authority.0 + ), + )); + } + checks.push("retained_authority".to_string()); + } + return std::result::Result::Ok(crate::Token2022ProofContextReport { + role: requirement.role.clone(), + account: requirement.account.clone(), + proof_discriminator: discriminator, + context_state_bytes: expected_size, + context_slot: result.context.slot, + checks, + }); +} + +#[cfg(test)] +mod tests { + fn pubkey(byte: u8) -> kb_model::Pubkey { + return kb_model::Pubkey(bs58::encode([byte; 32]).into_string()); + } + + fn requirement( + account: kb_model::Pubkey, + proof_type: kb_executor_solana_core::ZkElGamalProofType, + ) -> crate::Token2022ProofContextRequirement { + return crate::Token2022ProofContextRequirement { + role: "equality_proof".to_string(), + account, + proof_type, + expected_authority: std::option::Option::Some(pubkey(9)), + }; + } + + fn account_result( + proof_type: kb_executor_solana_core::ZkElGamalProofType, + ) -> kb_rpc::AccountInfoResult { + let mut data = std::vec![0u8; proof_type.context_state_size()]; + data[..32].copy_from_slice(&[9u8; 32]); + data[32] = proof_type.discriminator(); + return kb_rpc::AccountInfoResult { + context: kb_rpc::RpcResponseContext { + slot: 77, + api_version: std::option::Option::None, + }, + account: std::option::Option::Some(kb_rpc::AccountInfoValue { + lamports: 1, + owner: kb_model::ProgramId(kb_program_ids::ZK_ELGAMAL_PROOF_PROGRAM_ID.to_string()), + executable: false, + rent_epoch: 0, + space: data.len() as u64, + data, + }), + }; + } + + #[test] + fn exact_duplicates_are_deduplicated_and_conflicts_fail_closed() { + let item = requirement( + pubkey(1), + kb_executor_solana_core::ZkElGamalProofType::CiphertextCiphertextEquality, + ); + let request = crate::Token2022CryptographicPreflightRequest { + query_role: "query".to_string(), + min_context_slot: std::option::Option::Some(7), + proof_contexts: std::vec![item.clone(), item.clone()], + }; + let unique = crate::solana_token_2022_crypto_preflight::validate_proof_context_requirements( + &request, + ); + assert_eq!(unique.as_ref().map(std::vec::Vec::len), std::result::Result::Ok(1)); + let conflict = crate::Token2022CryptographicPreflightRequest { + query_role: "query".to_string(), + min_context_slot: std::option::Option::None, + proof_contexts: std::vec![ + item, + requirement( + pubkey(1), + kb_executor_solana_core::ZkElGamalProofType::BatchedRangeProofU128, + ), + ], + }; + assert!( + crate::solana_token_2022_crypto_preflight::validate_proof_context_requirements( + &conflict + ) + .is_err() + ); + } + + #[test] + fn proof_context_validation_checks_owner_size_type_and_authority() { + let proof_type = kb_executor_solana_core::ZkElGamalProofType::CiphertextCiphertextEquality; + let context_requirement = requirement(pubkey(1), proof_type); + let result = account_result(proof_type); + let report = crate::solana_token_2022_crypto_preflight::validate_proof_context_account( + &context_requirement, + &result, + ); + assert_eq!( + report.as_ref().map(|value| return value.checks.len()), + std::result::Result::Ok(5) + ); + let wrong_type = requirement( + pubkey(1), + kb_executor_solana_core::ZkElGamalProofType::BatchedRangeProofU128, + ); + assert!( + crate::solana_token_2022_crypto_preflight::validate_proof_context_account( + &wrong_type, + &result, + ) + .is_err() + ); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_devnet_execution.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_devnet_execution.rs new file mode 100644 index 0000000..f96643e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_devnet_execution.rs @@ -0,0 +1,968 @@ +// file: kb_pipeline/src/solana_token_2022_devnet_execution.rs +// version: 2 + +//! Devnet Token-2022 execution with stateful and canonical post-validation. + +/// Complete request for one Devnet Token-2022 execution. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DevnetSplToken2022ExecutionRequest { + /// Stable caller-provided execution identifier. + pub intent_id: std::string::String, + /// Endpoint role used for state, balance, blockhash, fee and hydration calls. + pub query_role: std::string::String, + /// Endpoint role used for simulation, submission and confirmation polling. + pub transaction_role: std::string::String, + /// Exact typed Token-2022 operation. + pub operation: kb_executor_spl_token_2022::SplToken2022Operation, + /// Explicitly authorizes signing and submission after successful simulation. + pub submit: bool, + /// Explicit operator confirmation required by the active profile. + pub operator_confirmed: bool, + /// Number of `getTransaction` retries after the first hydration attempt. + pub post_validation_max_retries: u32, + /// Replaces existing core and decode outputs for the submitted signature. + pub force_post_validation_replay: bool, +} + +impl crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionRequest { + /// Creates a conservative simulation-only request. + pub fn new( + intent_id: impl std::convert::Into, + operation: kb_executor_spl_token_2022::SplToken2022Operation, + ) -> Self { + return Self { + intent_id: intent_id.into(), + query_role: "http_queries".to_string(), + transaction_role: "http_transactions".to_string(), + operation, + submit: false, + operator_confirmed: false, + post_validation_max_retries: 10, + force_post_validation_replay: false, + }; + } + + /// Validates request-local bounds independently from one profile. + pub fn validate(&self) -> kb_core::Result<()> { + if self.intent_id.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Devnet Token-2022 execution intent id must not be empty", + )); + } + if self.query_role.trim().is_empty() || self.transaction_role.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Devnet Token-2022 execution endpoint roles must not be empty", + )); + } + if self.post_validation_max_retries > 20 { + return std::result::Result::Err(kb_core::Error::config( + "post-execution getTransaction retries must not exceed 20", + )); + } + return std::result::Result::Ok(()); + } +} + +/// Complete result of one Devnet Token-2022 execution. +#[derive(Clone, Debug, PartialEq)] +pub struct DevnetSplToken2022ExecutionSummary { + /// Profile used by the orchestration. + pub profile_name: std::string::String, + /// Exact classified cluster. + pub cluster: kb_execution_api::ExecutionCluster, + /// Genesis hash returned by the selected endpoint. + pub genesis_hash: std::string::String, + /// Non-secret persistent wallet description. + pub wallet: kb_wallet::WalletSummary, + /// Wallet balance observed before planning. + pub balance_lamports: u64, + /// Stateful readiness report produced before plan simulation. + pub stateful_preflight: crate::Token2022PreflightReport, + /// Exact prepared Token plan. + pub plan: kb_execution_api::PreparedExecutionPlan, + /// Recent blockhash used by the exact transaction. + pub latest_blockhash: kb_rpc::LatestBlockhashResult, + /// Fee estimate for the exact compiled message. + pub fee: kb_rpc::FeeForMessageResult, + /// Exact simulation result bound to the compiled message. + pub simulation: kb_execution_api::ExecutionSimulationResult, + /// Submission result when explicitly authorized. + pub send_result: std::option::Option, + /// Confirmation result when submitted. + pub confirmation: std::option::Option, + /// Canonical hydration result for the exact signature. + pub backfill: std::option::Option, + /// Core extraction result for the exact signature. + pub core_extraction: std::option::Option, + /// First Token decode and materialization replay. + pub decode_replay: std::option::Option, + /// Second replay proving idempotence for the same decoder version and input. + pub idempotence_replay: std::option::Option, + /// Exact materialized rows produced for the submitted Token transaction. + pub materializations: std::vec::Vec, + /// Aggregated post-execution validation diagnostic. + pub post_execution: std::option::Option, +} + +struct PreparedToken2022Execution { + wallet: kb_wallet::TemporaryWallet, + unsigned: kb_execution_solana::UnsignedSolanaTransaction, + evidence: kb_execution_solana::SolanaSimulationEvidence, + summary: crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionSummary, +} + +/// Simulates one Devnet Token-2022 operation after stateful preflight. +pub async fn simulate_devnet_spl_token_2022( + http_pool: &kb_rpc::HttpEndpointPool, + profile: &kb_config::ProfileConfig, + workspace_root: &std::path::Path, + request: &crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionRequest, + observer: &O, +) -> kb_core::Result +where + O: crate::SolanaExecutionObserver, +{ + if request.submit { + return std::result::Result::Err(kb_core::Error::config( + "simulate_devnet_spl_token_2022 requires submit=false", + )); + } + let prepared = match crate::solana_token_2022_devnet_execution::prepare_execution( + http_pool, + profile, + workspace_root, + request, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(prepared.summary); +} + +/// Executes one Devnet Token-2022 simulation or authorized submission. +#[allow(clippy::too_many_arguments)] +pub async fn execute_devnet_spl_token_2022( + http_pool: &kb_rpc::HttpEndpointPool, + store: &S, + profile: &kb_config::ProfileConfig, + workspace_root: &std::path::Path, + request: &crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionRequest, + decoders: &[std::sync::Arc], + materializers: &[std::sync::Arc], + observer: &O, +) -> kb_core::Result +where + S: kb_store_core::RawTransactionStore + + kb_store_core::CoreExtractionStore + + kb_store_core::DecodePipelineStore + + Sync, + O: crate::SolanaExecutionObserver, +{ + let prepared = match crate::solana_token_2022_devnet_execution::prepare_execution( + http_pool, + profile, + workspace_root, + request, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if !request.submit { + return std::result::Result::Ok(prepared.summary); + } + if !prepared.summary.simulation.success { + return std::result::Result::Err(kb_core::Error::new( + "execution_simulation_failed", + crate::solana_execution::simulation_failure_message(&prepared.summary.simulation), + )); + } + if let std::result::Result::Err(error) = + crate::solana_token_2022_devnet_execution::validate_profile_wallet_signers( + prepared.unsigned.required_signer_pubkeys(), + prepared.summary.wallet.public_key.as_str(), + ) + { + return std::result::Result::Err(error); + } + let send_evaluation = match kb_execution_safety::ExecutionSafetyChecker + .evaluate_send(&prepared.summary.plan, &prepared.summary.simulation) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if send_evaluation.decision == kb_execution_safety::ExecutionSafetyDecision::Deny { + return std::result::Result::Err(kb_core::Error::new( + "execution_send_denied", + crate::solana_execution::violation_message(send_evaluation.violations.as_slice()), + )); + } + let signed = match prepared + .unsigned + .sign_after_simulation(&prepared.evidence, &[prepared.wallet.as_signer()]) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if let std::result::Result::Err(error) = signed.verify_signatures() { + return std::result::Result::Err(error); + } + let signature = signed.primary_signature().clone(); + let mut summary = prepared.summary; + let mut diagnostic = kb_execution_api::PostExecutionDiagnostic { + signature: signature.clone(), + canonical_inserted: false, + core_extracted: false, + decode_replayed: false, + materialized: false, + diagnostics: std::vec::Vec::new(), + }; + let send_config = match kb_rpc::SendTransactionConfig::from_execution_config( + &profile.execution, + std::option::Option::Some(summary.latest_blockhash.context.slot), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let sent = match http_pool + .send_transaction_for_role( + request.transaction_role.as_str(), + signed.transaction_base64().as_str(), + &signature, + &send_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic.diagnostics.push(format!("Token-2022 submission failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + summary.send_result = std::option::Option::Some( + sent.to_execution_result(kb_execution_api::ExecutionCluster::Devnet), + ); + let confirmation_config = match kb_rpc::ConfirmTransactionConfig::from_execution_config( + &profile.execution, + std::option::Option::Some(summary.latest_blockhash.last_valid_block_height), + std::option::Option::Some(summary.latest_blockhash.context.slot), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let confirmation = match http_pool + .confirm_transaction_for_roles( + request.transaction_role.as_str(), + request.query_role.as_str(), + kb_execution_api::ExecutionCluster::Devnet, + &signature, + &confirmation_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic.diagnostics.push(format!("Token-2022 confirmation failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + let confirmation_status = confirmation.status; + summary.confirmation = std::option::Option::Some(confirmation); + if !matches!( + confirmation_status, + kb_execution_api::ExecutionConfirmationStatus::Confirmed + | kb_execution_api::ExecutionConfirmationStatus::Finalized + ) { + diagnostic.diagnostics.push(format!( + "Token-2022 post-validation stopped at confirmation status {confirmation_status:?}" + )); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + } + let backfill = match crate::solana_token_execution::hydrate_signature( + http_pool, + store, + profile, + request.query_role.as_str(), + request.post_validation_max_retries, + observer, + &signature, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic.diagnostics.push(format!("Token-2022 hydration failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + diagnostic.canonical_inserted = crate::solana_token_execution::canonical_available(&backfill); + summary.backfill = std::option::Option::Some(backfill); + if !diagnostic.canonical_inserted { + diagnostic.diagnostics.push( + "confirmed Token-2022 transaction was unavailable for canonical hydration".to_string(), + ); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + } + let extraction = match crate::execute_core_extraction( + store, + &crate::CoreExtractionRequest { + source: crate::CoreExtractionSource::Signatures(std::vec![signature.0.clone()]), + limit: 1, + max_concurrent_extractions: 1, + force_replay: request.force_post_validation_replay, + }, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic + .diagnostics + .push(format!("Token-2022 core extraction failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + diagnostic.core_extracted = extraction.failed == 0 + && !extraction.cancelled + && extraction.selected == 1 + && extraction.extracted.saturating_add(extraction.skipped) >= 1; + summary.core_extraction = std::option::Option::Some(extraction); + if !diagnostic.core_extracted { + diagnostic + .diagnostics + .push("Token-2022 core extraction did not complete".to_string()); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + } + let first_replay = match crate::solana_token_execution::replay_program( + store, + &signature, + false, + request.force_post_validation_replay, + &[kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID], + decoders, + materializers, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic.diagnostics.push(format!("Token-2022 decode replay failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + diagnostic.decode_replayed = crate::solana_token_execution::decode_completed(&first_replay); + summary.decode_replay = std::option::Option::Some(first_replay); + let filter = match kb_store_core::MaterializedEventFilter::new( + std::option::Option::None, + std::option::Option::None, + std::option::Option::Some(signature.0.clone()), + 64, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let rows = + match kb_store_core::DecodePipelineStore::list_materialized_events(store, &filter).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic + .diagnostics + .push(format!("Token-2022 materialization query failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + summary.materializations = rows + .into_iter() + .filter(|row| return row.source_decoder_name == "spl_token_2022") + .collect(); + diagnostic.materialized = + !summary.plan.policy.post_execution_validation.materialization_required + || !summary.materializations.is_empty(); + let second_replay = match crate::solana_token_execution::replay_program( + store, + &signature, + true, + request.force_post_validation_replay, + &[kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID], + decoders, + materializers, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic + .diagnostics + .push(format!("Token-2022 idempotence replay failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + let idempotent = second_replay.failed_inputs == 0 + && second_replay.processors.iter().all(|processor| { + return processor.failed == 0 + && processor.materialized_outputs == 0 + && processor.materialization_refused == 0; + }); + summary.idempotence_replay = std::option::Option::Some(second_replay); + if !idempotent { + diagnostic + .diagnostics + .push("second Token-2022 replay did not prove a clean idempotent skip".to_string()); + } else if diagnostic.canonical_inserted + && diagnostic.core_extracted + && diagnostic.decode_replayed + && diagnostic.materialized + { + diagnostic.diagnostics.push( + "Token-2022 completed canonical hydration, core extraction, decode, materialization and idempotence validation" + .to_string(), + ); + } + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); +} + +async fn prepare_execution( + http_pool: &kb_rpc::HttpEndpointPool, + profile: &kb_config::ProfileConfig, + workspace_root: &std::path::Path, + request: &crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionRequest, + observer: &O, +) -> kb_core::Result +where + O: crate::SolanaExecutionObserver, +{ + if let std::result::Result::Err(error) = request.validate() { + return std::result::Result::Err(error); + } + if let std::result::Result::Err(error) = + crate::solana_token_2022_devnet_execution::validate_profile(profile, request) + { + return std::result::Result::Err(error); + } + if let std::result::Result::Err(error) = + crate::solana_execution::ensure_not_cancelled(observer, "token_validate") + { + return std::result::Result::Err(error); + } + let genesis = match http_pool.get_genesis_hash_for_role(request.query_role.as_str()).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if genesis.classified_cluster + != std::option::Option::Some(kb_execution_api::ExecutionCluster::Devnet) + { + return std::result::Result::Err(kb_core::Error::new( + "execution_cluster_mismatch", + format!( + "expected Devnet genesis hash but endpoint returned {} classified as {:?}", + genesis.genesis_hash, genesis.classified_cluster + ), + )); + } + let preflight_request = + match crate::solana_token_2022_devnet_execution::preflight_request(request) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let readiness = match crate::inspect_token_2022_preflight(http_pool, &preflight_request).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let wallet = match crate::solana_execution::load_profile_wallet(profile, workspace_root).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let wallet_summary = wallet.summary(); + let fee_payer = kb_model::Pubkey(wallet_summary.public_key.clone()); + let balance = match http_pool + .get_balance_for_role( + request.query_role.as_str(), + &fee_payer, + &kb_rpc::GetBalanceConfig::confirmed(), + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if balance.lamports < profile.execution.max_fee_lamports { + return std::result::Result::Err(kb_core::Error::new( + "execution_balance_insufficient", + format!( + "Devnet wallet balance {} is below the configured fee ceiling {}", + balance.lamports, profile.execution.max_fee_lamports + ), + )); + } + let plan = match crate::solana_token_2022_devnet_execution::build_plan( + profile, + request, + fee_payer.clone(), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let plan_evaluation = + match kb_execution_safety::ExecutionSafetyChecker.evaluate_prepared_plan(&plan) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if plan_evaluation.decision == kb_execution_safety::ExecutionSafetyDecision::Deny { + return std::result::Result::Err(kb_core::Error::new( + "execution_plan_denied", + crate::solana_execution::violation_message(plan_evaluation.violations.as_slice()), + )); + } + let latest_blockhash = match http_pool + .get_latest_blockhash_for_role( + request.query_role.as_str(), + &kb_rpc::GetLatestBlockhashConfig::confirmed(), + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let unsigned = match kb_execution_solana::build_legacy_transaction( + &plan, + latest_blockhash.blockhash.as_str(), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let fee = match http_pool + .get_fee_for_message_for_role( + request.query_role.as_str(), + unsigned.message_base64().as_str(), + &kb_rpc::GetFeeForMessageConfig::new( + kb_rpc::RpcCommitmentLevel::Confirmed, + std::option::Option::Some(latest_blockhash.context.slot), + ), + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if fee.fee_lamports.is_none() { + return std::result::Result::Err(kb_core::Error::new( + "execution_fee_unavailable", + "getFeeForMessage returned null for the selected recent blockhash", + )); + } + let unsigned_base64 = match unsigned.transaction_base64() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let simulation_config = match kb_rpc::SimulateTransactionConfig::new( + kb_rpc::RpcCommitmentLevel::Confirmed, + false, + false, + std::option::Option::Some(latest_blockhash.context.slot), + true, + std::option::Option::None, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_execution::emit( + observer, + crate::SolanaExecutionProgressLevel::Info, + "spl_token_2022_simulation", + format!("simulating exact Token-2022 message {}", unsigned.message_hash()), + std::option::Option::None, + ); + let simulation_rpc = match http_pool + .simulate_transaction_for_role( + request.transaction_role.as_str(), + unsigned_base64.as_str(), + &simulation_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let simulation = simulation_rpc.to_execution_result( + kb_execution_api::ExecutionCluster::Devnet, + kb_execution_api::ExecutionBlockhashKind::Latest, + std::option::Option::Some( + simulation_rpc.context.slot.saturating_sub(latest_blockhash.context.slot), + ), + std::option::Option::None, + std::option::Option::None, + std::option::Option::Some(&fee), + ); + let evidence = unsigned.bind_simulation(simulation.clone()); + let summary = crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionSummary { + profile_name: profile.name.clone(), + cluster: kb_execution_api::ExecutionCluster::Devnet, + genesis_hash: genesis.genesis_hash, + wallet: wallet_summary, + balance_lamports: balance.lamports, + stateful_preflight: readiness, + plan, + latest_blockhash, + fee, + simulation, + send_result: std::option::Option::None, + confirmation: std::option::Option::None, + backfill: std::option::Option::None, + core_extraction: std::option::Option::None, + decode_replay: std::option::Option::None, + idempotence_replay: std::option::Option::None, + materializations: std::vec::Vec::new(), + post_execution: std::option::Option::None, + }; + return std::result::Result::Ok( + crate::solana_token_2022_devnet_execution::PreparedToken2022Execution { + wallet, + unsigned, + evidence, + summary, + }, + ); +} + +fn preflight_request( + request: &crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionRequest, +) -> kb_core::Result { + let operation = match &request.operation { + kb_executor_spl_token_2022::SplToken2022Operation::Instruction { value } => value.as_ref(), + kb_executor_spl_token_2022::SplToken2022Operation::Batch { instructions: _ } => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_batch_not_supported", + "Devnet Token-2022 validation scenarios require one non-batch operation", + )); + }, + }; + let mut requirements = std::vec::Vec::new(); + match operation { + kb_executor_spl_token_2022::SplTokenSingleOperation::MintToChecked { + mint, + destination, + authority, + amount: _, + decimals, + } => { + requirements.push(mint_requirement("mint", mint, std::option::Option::Some(*decimals))); + requirements.push(account_requirement( + "destination", + destination, + mint, + std::option::Option::None, + )); + if let std::result::Result::Err(error) = validate_single_authority(authority) { + return std::result::Result::Err(error); + } + }, + kb_executor_spl_token_2022::SplTokenSingleOperation::TransferChecked { + source, + mint, + destination, + authority, + amount: _, + decimals, + } => { + requirements.push(account_requirement( + "source", + source, + mint, + std::option::Option::Some(authority.authority.clone()), + )); + requirements.push(mint_requirement("mint", mint, std::option::Option::Some(*decimals))); + requirements.push(account_requirement( + "destination", + destination, + mint, + std::option::Option::None, + )); + if let std::result::Result::Err(error) = validate_single_authority(authority) { + return std::result::Result::Err(error); + } + }, + kb_executor_spl_token_2022::SplTokenSingleOperation::ApproveChecked { + source, + mint, + delegate: _, + authority, + amount: _, + decimals, + } => { + requirements.push(account_requirement( + "source", + source, + mint, + std::option::Option::Some(authority.authority.clone()), + )); + requirements.push(mint_requirement("mint", mint, std::option::Option::Some(*decimals))); + if let std::result::Result::Err(error) = validate_single_authority(authority) { + return std::result::Result::Err(error); + } + }, + kb_executor_spl_token_2022::SplTokenSingleOperation::Revoke { source, authority } => { + requirements.push(account_requirement( + "source", + source, + &kb_model::Pubkey(std::string::String::new()), + std::option::Option::Some(authority.authority.clone()), + )); + requirements[0].expected_mint = std::option::Option::None; + if let std::result::Result::Err(error) = validate_single_authority(authority) { + return std::result::Result::Err(error); + } + }, + kb_executor_spl_token_2022::SplTokenSingleOperation::BurnChecked { + source, + mint, + authority, + amount: _, + decimals, + } => { + requirements.push(account_requirement( + "source", + source, + mint, + std::option::Option::Some(authority.authority.clone()), + )); + requirements.push(mint_requirement("mint", mint, std::option::Option::Some(*decimals))); + if let std::result::Result::Err(error) = validate_single_authority(authority) { + return std::result::Result::Err(error); + } + }, + kb_executor_spl_token_2022::SplTokenSingleOperation::FreezeAccount { + account, + mint, + authority, + } + | kb_executor_spl_token_2022::SplTokenSingleOperation::ThawAccount { + account, + mint, + authority, + } => { + requirements.push(account_requirement( + "account", + account, + mint, + std::option::Option::None, + )); + requirements.push(mint_requirement("mint", mint, std::option::Option::None)); + if let std::result::Result::Err(error) = validate_single_authority(authority) { + return std::result::Result::Err(error); + } + }, + kb_executor_spl_token_2022::SplTokenSingleOperation::CloseAccount { + account, + destination: _, + authority, + } => { + requirements.push(account_requirement( + "account", + account, + &kb_model::Pubkey(std::string::String::new()), + std::option::Option::Some(authority.authority.clone()), + )); + requirements[0].expected_mint = std::option::Option::None; + if let std::result::Result::Err(error) = validate_single_authority(authority) { + return std::result::Result::Err(error); + } + }, + _ => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_devnet_operation_unsupported", + format!( + "Token-2022 Devnet validation does not expose {}", + operation.operation_code() + ), + )); + }, + } + return std::result::Result::Ok(crate::Token2022PreflightRequest { + query_role: request.query_role.clone(), + min_context_slot: std::option::Option::None, + max_accounts: crate::MAX_TOKEN_2022_PREFLIGHT_ACCOUNTS, + max_total_data_bytes: crate::MAX_TOKEN_2022_PREFLIGHT_TOTAL_BYTES, + requirements, + elgamal_registry: std::option::Option::None, + }); +} + +fn validate_single_authority( + authority: &kb_executor_spl_token_2022::SplTokenAuthority, +) -> kb_core::Result<()> { + if !authority.multisig_signers.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_devnet_multisig_not_supported", + "the Devnet validation UI currently supports one profile-wallet authority", + )); + } + return std::result::Result::Ok(()); +} + +fn mint_requirement( + role: &str, + mint: &kb_model::Pubkey, + decimals: std::option::Option, +) -> crate::Token2022PreflightRequirement { + return crate::Token2022PreflightRequirement { + role: role.to_string(), + account: mint.clone(), + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Mint, + max_data_bytes: 65_536, + expected_mint: std::option::Option::None, + expected_owner: std::option::Option::None, + expected_decimals: decimals, + required_extensions: std::vec::Vec::new(), + context: crate::Token2022StatefulContext::default(), + }; +} + +fn account_requirement( + role: &str, + account: &kb_model::Pubkey, + mint: &kb_model::Pubkey, + owner: std::option::Option, +) -> crate::Token2022PreflightRequirement { + return crate::Token2022PreflightRequirement { + role: role.to_string(), + account: account.clone(), + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Account, + max_data_bytes: 65_536, + expected_mint: std::option::Option::Some(mint.clone()), + expected_owner: owner, + expected_decimals: std::option::Option::None, + required_extensions: std::vec::Vec::new(), + context: crate::Token2022StatefulContext::default(), + }; +} + +fn validate_profile( + profile: &kb_config::ProfileConfig, + request: &crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionRequest, +) -> kb_core::Result<()> { + if profile.wallet.cluster != "devnet" { + return std::result::Result::Err(kb_core::Error::config( + "Token-2022 Devnet orchestration requires a Devnet wallet profile", + )); + } + if !profile.wallet.temporary_wallet_enabled || !profile.wallet.temporary_wallet_persist { + return std::result::Result::Err(kb_core::Error::config( + "Token-2022 Devnet orchestration requires an enabled persistent temporary wallet", + )); + } + if !profile.execution.require_simulation { + return std::result::Result::Err(kb_core::Error::config( + "Token-2022 Devnet orchestration requires simulation", + )); + } + if request.submit && !profile.wallet.devnet_send_enabled { + return std::result::Result::Err(kb_core::Error::config( + "Devnet transaction submission is disabled by the wallet profile", + )); + } + if request.submit + && profile.execution.require_operator_confirmation + && !request.operator_confirmed + { + return std::result::Result::Err(kb_core::Error::config( + "Token-2022 Devnet submission requires explicit operator confirmation", + )); + } + return std::result::Result::Ok(()); +} + +fn build_plan( + profile: &kb_config::ProfileConfig, + request: &crate::solana_token_2022_devnet_execution::DevnetSplToken2022ExecutionRequest, + fee_payer: kb_model::Pubkey, +) -> kb_core::Result { + let materialization_required = + crate::solana_token_2022_devnet_execution::operation_requires_materialization( + &request.operation, + ); + let authorized_signers = std::vec![fee_payer.clone()]; + let intent = kb_executor_spl_token_2022::SplToken2022ExecutionIntent { + intent_id: request.intent_id.clone(), + fee_payer, + policy: kb_execution_api::ExecutionPolicy { + cluster: kb_execution_api::ExecutionClusterPolicy { + expected_cluster: kb_execution_api::ExecutionCluster::Devnet, + allow_mainnet: false, + mainnet_confirmation: false, + }, + simulation: kb_execution_api::ExecutionSimulationPolicy::Required, + blockhash: kb_execution_api::ExecutionBlockhashPolicy { + kind: kb_execution_api::ExecutionBlockhashKind::Latest, + max_age_slots: std::option::Option::Some( + profile.execution.recent_blockhash_max_age_slots, + ), + nonce_account: std::option::Option::None, + nonce_authority: std::option::Option::None, + }, + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some( + profile.execution.devnet_max_spend_lamports, + ), + max_fee_lamports: std::option::Option::Some(profile.execution.max_fee_lamports), + max_compute_unit_price_micro_lamports: std::option::Option::Some( + profile.execution.max_compute_unit_price_micro_lamports, + ), + }, + authorized_signers, + dry_run: !request.submit, + post_execution_validation: kb_execution_api::PostExecutionValidationPolicy { + canonical_insert_required: true, + core_extraction_required: true, + decode_replay_required: true, + materialization_required, + }, + }, + operation: request.operation.clone(), + }; + return kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &kb_executor_spl_token_2022::SplToken2022Executor, + &intent, + ); +} + +fn operation_requires_materialization( + _operation: &kb_executor_spl_token_2022::SplToken2022Operation, +) -> bool { + return true; +} + +fn validate_profile_wallet_signers( + required_signers: &[std::string::String], + wallet_pubkey: &str, +) -> kb_core::Result<()> { + if required_signers.iter().any(|value| return value.as_str() != wallet_pubkey) { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_2022_external_signer_unavailable", + format!( + "the Devnet Token orchestrator can sign only with profile wallet {}; required signers are {}", + wallet_pubkey, + required_signers.join(",") + ), + )); + } + return std::result::Result::Ok(()); +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_devnet_scenarios.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_devnet_scenarios.rs new file mode 100644 index 0000000..66d4e86 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_devnet_scenarios.rs @@ -0,0 +1,180 @@ +// file: kb_pipeline/src/solana_token_2022_devnet_scenarios.rs +// version: 2 + +//! Stable Devnet validation scenarios required to close milestone 0.4.6. + +/// Stable category of one independent Devnet validation scenario. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum DevnetSplValidationFamily { + /// Public Token-2022 state mutations without cryptographic proofs. + Token2022Public, + /// ElGamal Registry lifecycle kept technically separate from Token-2022. + ElGamalRegistry, + /// Token-2022 confidential operations requiring proof material. + Token2022Confidential, +} + +/// Stable status of one scenario in the application validation workflow. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum DevnetSplValidationImplementationStatus { + /// The scenario can be simulated and submitted through the application. + Executable, + /// The typed backend exists but the application execution path remains to be connected. + BackendReady, + /// The scenario requires externally prepared proof material before execution. + ProofFixtureRequired, +} + +/// One independent Devnet validation scenario exposed to applications. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DevnetSplValidationScenario { + /// Stable identifier used by scripts, tests and the frontend. + pub id: std::string::String, + /// Operator-visible label. + pub label: std::string::String, + /// Scenario family. + pub family: crate::DevnetSplValidationFamily, + /// Stable executor operation code. + pub operation_code: std::string::String, + /// Current implementation status. + pub implementation_status: crate::DevnetSplValidationImplementationStatus, + /// Whether this scenario mutates on-chain state. + pub destructive: bool, + /// Whether explicit operator confirmation is mandatory before submission. + pub operator_confirmation_required: bool, + /// Whether cryptographic proof material is required. + pub proof_required: bool, + /// Ordered fixture variables required by the scenario. + pub required_fixture_variables: std::vec::Vec, +} + +/// Returns the complete ordered Devnet scenario inventory for milestone 0.4.6. +pub fn devnet_spl_validation_scenarios() -> std::vec::Vec { + return vec![ + public_scenario("token_2022_mint_to_checked", "Token-2022 MintToChecked", kb_executor_spl_token_2022::MINT_TO_CHECKED_OPERATION, &["TOKEN_2022_MINT", "TOKEN_2022_SOURCE", "TOKEN_2022_AUTHORITY", "TOKEN_2022_DECIMALS", "TOKEN_2022_MINT_AMOUNT_RAW"]), + public_scenario("token_2022_transfer_checked", "Token-2022 TransferChecked", kb_executor_spl_token_2022::TRANSFER_CHECKED_OPERATION, &["TOKEN_2022_SOURCE", "TOKEN_2022_MINT", "TOKEN_2022_DESTINATION", "TOKEN_2022_AUTHORITY", "TOKEN_2022_DECIMALS", "TOKEN_2022_TRANSFER_AMOUNT_RAW"]), + public_scenario("token_2022_approve_checked", "Token-2022 ApproveChecked", kb_executor_spl_token_2022::APPROVE_CHECKED_OPERATION, &["TOKEN_2022_SOURCE", "TOKEN_2022_MINT", "TOKEN_2022_DELEGATE", "TOKEN_2022_AUTHORITY", "TOKEN_2022_DECIMALS", "TOKEN_2022_APPROVE_AMOUNT_RAW"]), + public_scenario("token_2022_revoke", "Token-2022 Revoke", kb_executor_spl_token_2022::REVOKE_OPERATION, &["TOKEN_2022_SOURCE", "TOKEN_2022_AUTHORITY"]), + public_scenario("token_2022_burn_checked", "Token-2022 BurnChecked", kb_executor_spl_token_2022::BURN_CHECKED_OPERATION, &["TOKEN_2022_SOURCE", "TOKEN_2022_MINT", "TOKEN_2022_AUTHORITY", "TOKEN_2022_DECIMALS", "TOKEN_2022_BURN_AMOUNT_RAW"]), + public_scenario("token_2022_freeze_account", "Token-2022 FreezeAccount", kb_executor_spl_token_2022::FREEZE_ACCOUNT_OPERATION, &["TOKEN_2022_SOURCE", "TOKEN_2022_MINT", "TOKEN_2022_FREEZE_AUTHORITY"]), + public_scenario("token_2022_thaw_account", "Token-2022 ThawAccount", kb_executor_spl_token_2022::THAW_ACCOUNT_OPERATION, &["TOKEN_2022_SOURCE", "TOKEN_2022_MINT", "TOKEN_2022_FREEZE_AUTHORITY"]), + public_scenario("token_2022_close_destination", "Token-2022 CloseAccount destination", kb_executor_spl_token_2022::CLOSE_ACCOUNT_OPERATION, &["TOKEN_2022_DESTINATION", "KB_DEVNET_WALLET_ADDRESS", "TOKEN_2022_AUTHORITY"]), + registry_scenario("elgamal_registry_create", "ElGamal Registry CreateRegistry", "spl_elgamal_registry.create_registry", &["ELGAMAL_REGISTRY_ADDRESS", "ELGAMAL_PUBKEY_BASE64", "PUBKEY_VALIDITY_PROOF_CONTEXT"]), + registry_scenario("elgamal_registry_update", "ElGamal Registry UpdateRegistry", "spl_elgamal_registry.update_registry", &["ELGAMAL_REGISTRY_ADDRESS", "ELGAMAL_PUBKEY_BASE64", "PUBKEY_VALIDITY_PROOF_CONTEXT"]), + confidential_scenario("token_2022_configure_confidential_account", "Token-2022 ConfigureAccountWithRegistry", kb_executor_spl_token_2022::CONFIGURE_CONFIDENTIAL_TRANSFER_ACCOUNT_WITH_REGISTRY_OPERATION, &["TOKEN_2022_SOURCE", "TOKEN_2022_MINT", "ELGAMAL_REGISTRY_ADDRESS", "PUBKEY_VALIDITY_PROOF_CONTEXT"]), + ]; +} + +fn public_scenario( + id: &str, + label: &str, + operation_code: &str, + variables: &[&str], +) -> crate::DevnetSplValidationScenario { + return scenario( + id, + label, + crate::DevnetSplValidationFamily::Token2022Public, + operation_code, + crate::DevnetSplValidationImplementationStatus::Executable, + true, + false, + variables, + ); +} + +fn registry_scenario( + id: &str, + label: &str, + operation_code: &str, + variables: &[&str], +) -> crate::DevnetSplValidationScenario { + return scenario( + id, + label, + crate::DevnetSplValidationFamily::ElGamalRegistry, + operation_code, + crate::DevnetSplValidationImplementationStatus::ProofFixtureRequired, + true, + true, + variables, + ); +} + +fn confidential_scenario( + id: &str, + label: &str, + operation_code: &str, + variables: &[&str], +) -> crate::DevnetSplValidationScenario { + return scenario( + id, + label, + crate::DevnetSplValidationFamily::Token2022Confidential, + operation_code, + crate::DevnetSplValidationImplementationStatus::ProofFixtureRequired, + true, + true, + variables, + ); +} + +fn scenario( + id: &str, + label: &str, + family: crate::DevnetSplValidationFamily, + operation_code: &str, + implementation_status: crate::DevnetSplValidationImplementationStatus, + destructive: bool, + proof_required: bool, + variables: &[&str], +) -> crate::DevnetSplValidationScenario { + return crate::DevnetSplValidationScenario { + id: id.to_string(), + label: label.to_string(), + family, + operation_code: operation_code.to_string(), + implementation_status, + destructive, + operator_confirmation_required: destructive, + proof_required, + required_fixture_variables: variables + .iter() + .map(|value| return (*value).to_string()) + .collect(), + }; +} + +#[cfg(test)] +mod tests { + #[test] + fn milestone_inventory_is_stable_unique_and_keeps_registry_separate() { + let scenarios = crate::devnet_spl_validation_scenarios(); + assert_eq!(scenarios.len(), 11); + let ids = scenarios + .iter() + .map(|scenario| return scenario.id.as_str()) + .collect::>(); + assert_eq!(ids.len(), scenarios.len()); + assert!( + scenarios.iter().any(|scenario| return scenario.family + == crate::DevnetSplValidationFamily::ElGamalRegistry) + ); + assert!( + scenarios + .iter() + .filter(|scenario| return scenario.family + == crate::DevnetSplValidationFamily::Token2022Public) + .all(|scenario| return !scenario.proof_required) + ); + } + + #[test] + fn every_mutating_scenario_requires_operator_confirmation() { + assert!(crate::devnet_spl_validation_scenarios().iter().all(|scenario| { + return !scenario.destructive || scenario.operator_confirmation_required; + })); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_execution_orchestration.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_execution_orchestration.rs new file mode 100644 index 0000000..8516f20 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_execution_orchestration.rs @@ -0,0 +1,331 @@ +// file: kb_pipeline/src/solana_token_2022_execution_orchestration.rs +// version: 3 + +//! Final Token-2022 execution-readiness and stateful postcondition contracts. + +/// Maximum number of distinct transaction signers accepted by one Token-2022 execution envelope. +pub const MAX_TOKEN_2022_EXECUTION_SIGNERS: usize = 16; + +/// Result of one stateful postcondition checked after a confirmed Token-2022 transaction. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum Token2022ExecutionPostconditionStatus { + /// The observed final state confirms the expected operation effect. + Confirmed, + /// The observed final state contradicts the expected operation effect. + Contradicted, + /// The operation has no supported stateful postcondition in the current contract. + NotApplicable, +} + +/// One explicit stateful postcondition retained by the execution orchestrator. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct Token2022ExecutionPostcondition { + /// Stable semantic role such as `mint`, `source`, `destination`, or `registry`. + pub role: std::string::String, + /// Canonical account whose final state was inspected. + pub account: kb_model::Pubkey, + /// Explicit result of the postcondition. + pub status: crate::Token2022ExecutionPostconditionStatus, + /// Bounded diagnostic explaining the result without retaining complete account data. + pub diagnostic: std::string::String, +} + +/// Complete deterministic request checked before Token-2022 transaction signing. +#[derive(Clone, Debug, PartialEq)] +pub struct Token2022ExecutionReadinessRequest { + /// Exact prepared executor plan. + pub plan: kb_execution_api::PreparedExecutionPlan, + /// Exact hash of the compiled Solana message. + pub message_hash: std::string::String, + /// Hash retained by the simulation evidence. + pub simulated_message_hash: std::string::String, + /// Whether the exact compiled message was simulated. + pub simulated: bool, + /// Whether the exact simulation succeeded. + pub simulation_succeeded: bool, + /// Stateful Token-2022 account preflight report. + pub stateful_preflight: crate::Token2022PreflightReport, + /// Cryptographic proof context-state preflight report. + pub cryptographic_preflight: crate::Token2022CryptographicPreflightReport, + /// Ordered proof orchestration report. + pub proof_orchestration: crate::Token2022ProofOrchestrationReport, + /// Public keys actually available to sign the transaction. + pub resolved_signers: std::vec::Vec, + /// Whether submission was explicitly requested. + pub submit: bool, + /// Whether submission received explicit operator confirmation. + pub operator_confirmed: bool, +} + +/// Deterministic execution-readiness report produced before signing. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct Token2022ExecutionReadinessReport { + /// Stable operation code. + pub operation_code: std::string::String, + /// Exact compiled message hash bound to simulation. + pub message_hash: std::string::String, + /// Highest RPC context slot across stateful and cryptographic preflights. + pub context_slot: u64, + /// Ordered signer public keys required by the plan. + pub required_signers: std::vec::Vec, + /// Whether transaction signing and submission are authorized. + pub send_authorized: bool, + /// Ordered successful checks. + pub checks: std::vec::Vec, +} + +/// Validates the complete Token-2022 execution envelope before transaction signing. +pub fn validate_token_2022_execution_readiness( + request: &crate::Token2022ExecutionReadinessRequest, +) -> kb_core::Result { + if request.plan.operation_code.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Token-2022 execution plan operation_code must not be empty", + )); + } + if request.plan.operation_code != request.proof_orchestration.operation_code { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_execution_operation_mismatch", + "Token-2022 plan and proof orchestration operation codes must match", + )); + } + if request.message_hash.trim().is_empty() + || request.simulated_message_hash.trim().is_empty() + || request.message_hash != request.simulated_message_hash + { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_execution_simulation_message_mismatch", + "Token-2022 simulation must be bound to the exact compiled message hash", + )); + } + if !request.simulated || !request.simulation_succeeded { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_execution_simulation_required", + "Token-2022 execution requires one successful exact-message simulation", + )); + } + if !request.proof_orchestration.simulation_required { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_execution_proof_policy_mismatch", + "Token-2022 proof orchestration must require simulation", + )); + } + if request.stateful_preflight.commitment != "confirmed" + || request.cryptographic_preflight.commitment != "confirmed" + { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_execution_preflight_commitment_mismatch", + "Token-2022 stateful and cryptographic preflights must use confirmed commitment", + )); + } + let required_signers = match validate_signers( + request.plan.required_signers.as_slice(), + request.resolved_signers.as_slice(), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if request.submit && !request.operator_confirmed { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_execution_confirmation_required", + "Token-2022 submission requires explicit operator confirmation", + )); + } + if request.submit && !request.proof_orchestration.send_authorized { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_execution_proof_send_not_authorized", + "Token-2022 proof orchestration did not authorize submission", + )); + } + if request.submit && request.plan.policy.dry_run { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_execution_dry_run_blocks_submission", + "Token-2022 plan remains dry-run and cannot be submitted", + )); + } + let context_slot = request + .stateful_preflight + .context_slot + .max(request.cryptographic_preflight.context_slot); + return std::result::Result::Ok(crate::Token2022ExecutionReadinessReport { + operation_code: request.plan.operation_code.clone(), + message_hash: request.message_hash.clone(), + context_slot, + required_signers, + send_authorized: request.submit + && request.operator_confirmed + && request.proof_orchestration.send_authorized + && !request.plan.policy.dry_run, + checks: vec![ + "operation_matches_proof_orchestration".to_string(), + "simulation_bound_to_exact_message".to_string(), + "confirmed_stateful_preflight".to_string(), + "confirmed_cryptographic_preflight".to_string(), + "all_required_signers_resolved".to_string(), + "submission_policy_consistent".to_string(), + ], + }); +} + +/// Aggregates explicit postconditions without converting unsupported checks into success. +pub fn summarize_token_2022_postconditions( + postconditions: &[crate::Token2022ExecutionPostcondition], +) -> crate::Token2022ExecutionPostconditionStatus { + if postconditions.iter().any(|item| { + return item.status == crate::Token2022ExecutionPostconditionStatus::Contradicted; + }) { + return crate::Token2022ExecutionPostconditionStatus::Contradicted; + } + if postconditions.iter().any(|item| { + return item.status == crate::Token2022ExecutionPostconditionStatus::Confirmed; + }) { + return crate::Token2022ExecutionPostconditionStatus::Confirmed; + } + return crate::Token2022ExecutionPostconditionStatus::NotApplicable; +} + +fn validate_signers( + required: &[kb_execution_api::RequiredSigner], + resolved: &[kb_model::Pubkey], +) -> kb_core::Result> { + if required.len() > crate::MAX_TOKEN_2022_EXECUTION_SIGNERS + || resolved.len() > crate::MAX_TOKEN_2022_EXECUTION_SIGNERS + { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_execution_signer_limit_exceeded", + format!( + "Token-2022 execution accepts at most {} signers", + crate::MAX_TOKEN_2022_EXECUTION_SIGNERS + ), + )); + } + let mut required_unique = + std::collections::BTreeMap::::new(); + for signer in required { + required_unique + .entry(signer.pubkey.0.clone()) + .or_insert_with(|| return signer.pubkey.clone()); + } + let resolved_set = resolved + .iter() + .map(|signer| return signer.0.clone()) + .collect::>(); + for signer in required_unique.values() { + if !resolved_set.contains(signer.0.as_str()) { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_execution_signer_unresolved", + format!("Token-2022 required signer {} is unresolved", signer.0), + )); + } + } + return std::result::Result::Ok(required_unique.into_values().collect()); +} + +#[cfg(test)] +mod tests { + fn pubkey(byte: u8) -> kb_model::Pubkey { + return kb_model::Pubkey(bs58::encode([byte; 32]).into_string()); + } + + fn request() -> crate::Token2022ExecutionReadinessRequest { + let signer = pubkey(1); + let policy = kb_execution_api::ExecutionPolicy { + dry_run: false, + ..std::default::Default::default() + }; + let plan = kb_execution_api::PreparedExecutionPlan { + executor_name: "kb_executor_spl_token_2022".to_string(), + executor_version: "0.4.6".to_string(), + intent_id: "intent".to_string(), + operation_code: "spl_token_2022.confidential_transfer".to_string(), + fee_payer: signer.clone(), + instructions: vec![], + required_signers: vec![kb_execution_api::RequiredSigner { + pubkey: signer.clone(), + role: "authority".to_string(), + }], + policy, + requested_spend_lamports: 0, + requested_compute_unit_price_micro_lamports: std::option::Option::None, + }; + return crate::Token2022ExecutionReadinessRequest { + plan, + message_hash: "message-hash".to_string(), + simulated_message_hash: "message-hash".to_string(), + simulated: true, + simulation_succeeded: true, + stateful_preflight: crate::Token2022PreflightReport { + commitment: "confirmed".to_string(), + context_slot: 100, + requested_data_bytes: 0, + accounts: vec![], + elgamal_registry_validated: false, + }, + cryptographic_preflight: crate::Token2022CryptographicPreflightReport { + commitment: "confirmed".to_string(), + context_slot: 101, + proof_contexts: vec![], + }, + proof_orchestration: crate::Token2022ProofOrchestrationReport { + operation_code: "spl_token_2022.confidential_transfer".to_string(), + instructions_sysvar_required: true, + inline_offsets: vec![1], + context_requirements: vec![], + simulation_required: true, + send_authorized: true, + checks: vec![], + }, + resolved_signers: vec![signer], + submit: true, + operator_confirmed: true, + }; + } + + #[test] + fn exact_message_preflights_and_signers_authorize_submission() { + let report = crate::validate_token_2022_execution_readiness(&request()) + .expect("complete Token-2022 readiness must succeed"); + assert!(report.send_authorized); + assert_eq!(report.context_slot, 101); + assert_eq!(report.required_signers.len(), 1); + } + + #[test] + fn message_mismatch_missing_signer_and_dry_run_fail_closed() { + let mut mismatched = request(); + mismatched.simulated_message_hash = "other".to_string(); + assert!(crate::validate_token_2022_execution_readiness(&mismatched).is_err()); + let mut missing = request(); + missing.resolved_signers.clear(); + assert!(crate::validate_token_2022_execution_readiness(&missing).is_err()); + let mut dry_run = request(); + dry_run.plan.policy.dry_run = true; + assert!(crate::validate_token_2022_execution_readiness(&dry_run).is_err()); + } + + #[test] + fn postcondition_summary_preserves_contradicted_and_not_applicable() { + let account = pubkey(2); + let not_applicable = crate::Token2022ExecutionPostcondition { + role: "mint".to_string(), + account: account.clone(), + status: crate::Token2022ExecutionPostconditionStatus::NotApplicable, + diagnostic: "no supported final-state assertion".to_string(), + }; + assert_eq!( + crate::summarize_token_2022_postconditions(&[not_applicable]), + crate::Token2022ExecutionPostconditionStatus::NotApplicable + ); + let contradicted = crate::Token2022ExecutionPostcondition { + role: "source".to_string(), + account, + status: crate::Token2022ExecutionPostconditionStatus::Contradicted, + diagnostic: "final extension inventory contradicts the expected effect".to_string(), + }; + assert_eq!( + crate::summarize_token_2022_postconditions(&[contradicted]), + crate::Token2022ExecutionPostconditionStatus::Contradicted + ); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_preflight.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_preflight.rs new file mode 100644 index 0000000..da8abd5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_preflight.rs @@ -0,0 +1,445 @@ +// file: kb_pipeline/src/solana_token_2022_preflight.rs +// version: 1 + +//! Bounded Token-2022 stateful preflight orchestration. + +/// Maximum distinct Token-2022 accounts accepted by one preflight inspection. +pub const MAX_TOKEN_2022_PREFLIGHT_ACCOUNTS: usize = 16; +/// Maximum aggregate account-data budget accepted by one preflight inspection. +pub const MAX_TOKEN_2022_PREFLIGHT_TOTAL_BYTES: usize = 262_144; + +/// One exact Token-2022 account requirement for a stateful preflight. +#[derive(Clone, Debug, PartialEq)] +pub struct Token2022PreflightRequirement { + /// Stable semantic role used in diagnostics. + pub role: std::string::String, + /// Canonical account address. + pub account: kb_model::Pubkey, + /// Expected Token-2022 state category. + pub kind: kb_decoder_spl_token_2022::state::Token2022StateKind, + /// Maximum complete account bytes accepted for this requirement. + pub max_data_bytes: usize, + /// Optional expected mint for a Token Account. + pub expected_mint: std::option::Option, + /// Optional expected owner for a Token Account. + pub expected_owner: std::option::Option, + /// Optional exact decimals expected for a Mint. + pub expected_decimals: std::option::Option, + /// Published extension names required on the account. + pub required_extensions: std::vec::Vec, + /// Optional external identities needed by cross-account extensions. + pub context: crate::Token2022StatefulContext, +} + +/// One bounded Token-2022 preflight request. +#[derive(Clone, Debug, PartialEq)] +pub struct Token2022PreflightRequest { + /// Endpoint role used by all RPC reads. + pub query_role: std::string::String, + /// Optional minimum context slot shared by all reads. + pub min_context_slot: std::option::Option, + /// Maximum distinct accounts accepted after deduplication. + pub max_accounts: usize, + /// Maximum aggregate requested account bytes. + pub max_total_data_bytes: usize, + /// Ordered account requirements. + pub requirements: std::vec::Vec, + /// Optional ElGamal registry read required by the operation. + pub elgamal_registry: std::option::Option, +} + +/// One validated account result in a Token-2022 preflight report. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct Token2022PreflightAccountReport { + /// Stable semantic role. + pub role: std::string::String, + /// Canonical account address. + pub account: kb_model::Pubkey, + /// State category observed after parsing. + pub state_kind: std::string::String, + /// Context slot returned by the endpoint. + pub context_slot: u64, + /// Ordered extension names observed on the account. + pub extension_names: std::vec::Vec, + /// Ordered successful semantic checks. + pub checks: std::vec::Vec, +} + +/// Complete bounded Token-2022 stateful preflight report. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct Token2022PreflightReport { + /// Commitment used for all account reads. + pub commitment: std::string::String, + /// Highest context slot observed across all reads. + pub context_slot: u64, + /// Aggregate requested account-data budget. + pub requested_data_bytes: usize, + /// Ordered distinct Token-2022 account reports. + pub accounts: std::vec::Vec, + /// Whether an ElGamal registry was required and validated. + pub elgamal_registry_validated: bool, +} + +/// Inspects all bounded Token-2022 state required before simulation. +pub async fn inspect_token_2022_preflight( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::Token2022PreflightRequest, +) -> kb_core::Result { + let requirements = match validate_and_deduplicate_requirements(request) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let requested_data_bytes = requirements + .iter() + .fold(0usize, |total, requirement| { + return total.saturating_add(requirement.max_data_bytes); + }) + .saturating_add(if request.elgamal_registry.is_some() { + crate::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES + } else { + 0 + }); + let mut context_slot = request.min_context_slot.unwrap_or(0); + let mut accounts = std::vec::Vec::with_capacity(requirements.len()); + for requirement in requirements { + let read_request = crate::Token2022StatefulReadRequest { + query_role: request.query_role.clone(), + account: requirement.account.clone(), + kind: requirement.kind, + min_context_slot: request.min_context_slot, + max_data_bytes: requirement.max_data_bytes, + context: requirement.context.clone(), + }; + let read_result = match crate::read_token_2022_stateful_snapshot(pool, &read_request).await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let account_report = match validate_snapshot_requirement(&requirement, &read_result) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + context_slot = context_slot.max(account_report.context_slot); + accounts.push(account_report); + } + let mut elgamal_registry_validated = false; + if let std::option::Option::Some(registry_request) = request.elgamal_registry.as_ref() { + if registry_request.query_role != request.query_role { + return std::result::Result::Err(kb_core::Error::config( + "Token-2022 preflight ElGamal registry query role must match the shared query role", + )); + } + let registry_result = + match crate::read_elgamal_registry_stateful_snapshot(pool, registry_request).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + context_slot = context_slot.max(registry_result.context_slot); + elgamal_registry_validated = true; + } + return std::result::Result::Ok(crate::Token2022PreflightReport { + commitment: "confirmed".to_string(), + context_slot, + requested_data_bytes, + accounts, + elgamal_registry_validated, + }); +} + +fn validate_and_deduplicate_requirements( + request: &crate::Token2022PreflightRequest, +) -> kb_core::Result> { + if request.query_role.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Token-2022 preflight query_role must not be empty", + )); + } + if request.max_accounts == 0 || request.max_accounts > crate::MAX_TOKEN_2022_PREFLIGHT_ACCOUNTS + { + return std::result::Result::Err(kb_core::Error::config(format!( + "Token-2022 preflight max_accounts must be between 1 and {}", + crate::MAX_TOKEN_2022_PREFLIGHT_ACCOUNTS + ))); + } + if request.max_total_data_bytes == 0 + || request.max_total_data_bytes > crate::MAX_TOKEN_2022_PREFLIGHT_TOTAL_BYTES + { + return std::result::Result::Err(kb_core::Error::config(format!( + "Token-2022 preflight max_total_data_bytes must be between 1 and {}", + crate::MAX_TOKEN_2022_PREFLIGHT_TOTAL_BYTES + ))); + } + if request.requirements.is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Token-2022 preflight requires at least one account", + )); + } + let mut indexes = std::collections::BTreeMap::::new(); + let mut unique = std::vec::Vec::::new(); + for requirement in &request.requirements { + if requirement.role.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Token-2022 preflight requirement role must not be empty", + )); + } + if requirement.max_data_bytes == 0 + || requirement.max_data_bytes > crate::MAX_TOKEN_2022_STATEFUL_ACCOUNT_BYTES + { + return std::result::Result::Err(kb_core::Error::config(format!( + "Token-2022 preflight account {} has invalid max_data_bytes {}", + requirement.account.0, requirement.max_data_bytes + ))); + } + if let std::option::Option::Some(index) = indexes.get(requirement.account.0.as_str()) { + if &unique[*index] != requirement { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_preflight_conflicting_duplicate", + format!( + "Token-2022 preflight account {} has conflicting requirements", + requirement.account.0 + ), + )); + } + continue; + } + indexes.insert(requirement.account.0.clone(), unique.len()); + unique.push(requirement.clone()); + } + let total_accounts = + unique + .len() + .saturating_add(if request.elgamal_registry.is_some() { 1 } else { 0 }); + if total_accounts > request.max_accounts { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_preflight_account_limit_exceeded", + format!( + "Token-2022 preflight requires {total_accounts} distinct accounts above limit {}", + request.max_accounts + ), + )); + } + let requested_data_bytes = unique + .iter() + .fold(0usize, |total, requirement| { + return total.saturating_add(requirement.max_data_bytes); + }) + .saturating_add(if request.elgamal_registry.is_some() { + crate::ELGAMAL_REGISTRY_STATEFUL_ACCOUNT_BYTES + } else { + 0 + }); + if requested_data_bytes > request.max_total_data_bytes { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_preflight_data_budget_exceeded", + format!( + "Token-2022 preflight requests {requested_data_bytes} bytes above aggregate limit {}", + request.max_total_data_bytes + ), + )); + } + return std::result::Result::Ok(unique); +} + +fn validate_snapshot_requirement( + requirement: &crate::Token2022PreflightRequirement, + result: &crate::Token2022StatefulReadResult, +) -> kb_core::Result { + let token_output = result + .snapshot + .outputs + .iter() + .find(|output| return output.payload_json["domain"] == "spl_token_2022_account_state"); + let token_output = match token_output { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "Token-2022 preflight snapshot is missing its token account owner projection", + )); + }, + }; + let base_fields = &token_output.payload_json["baseFields"]; + let mut checks = std::vec![ + "owner_program_id".to_string(), + "complete_account_data".to_string(), + "state_kind".to_string() + ]; + if let std::option::Option::Some(expected_mint) = requirement.expected_mint.as_ref() { + if base_fields["mint"].as_str() != std::option::Option::Some(expected_mint.0.as_str()) { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_preflight_mint_mismatch", + format!( + "Token-2022 account {} mint does not match expected {}", + requirement.account.0, expected_mint.0 + ), + )); + } + checks.push("mint_identity".to_string()); + } + if let std::option::Option::Some(expected_owner) = requirement.expected_owner.as_ref() { + if base_fields["owner"].as_str() != std::option::Option::Some(expected_owner.0.as_str()) { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_preflight_owner_mismatch", + format!( + "Token-2022 account {} owner does not match expected {}", + requirement.account.0, expected_owner.0 + ), + )); + } + checks.push("token_account_owner".to_string()); + } + if let std::option::Option::Some(expected_decimals) = requirement.expected_decimals { + if base_fields["decimals"].as_u64() != std::option::Option::Some(expected_decimals as u64) { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_preflight_decimals_mismatch", + format!( + "Token-2022 mint {} decimals do not match expected {expected_decimals}", + requirement.account.0 + ), + )); + } + checks.push("mint_decimals".to_string()); + } + for required_extension in &requirement.required_extensions { + if !result + .snapshot + .extension_names + .iter() + .any(|value| return value == required_extension) + { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_preflight_extension_missing", + format!( + "Token-2022 account {} is missing required extension {required_extension}", + requirement.account.0 + ), + )); + } + } + if !requirement.required_extensions.is_empty() { + checks.push("required_extensions".to_string()); + } + return std::result::Result::Ok(crate::Token2022PreflightAccountReport { + role: requirement.role.clone(), + account: requirement.account.clone(), + state_kind: result.snapshot.state_kind.clone(), + context_slot: result.context_slot, + extension_names: result.snapshot.extension_names.clone(), + checks, + }); +} + +#[cfg(test)] +mod tests { + fn pubkey(byte: u8) -> kb_model::Pubkey { + return kb_model::Pubkey(bs58::encode([byte; 32]).into_string()); + } + + fn requirement(account: kb_model::Pubkey) -> crate::Token2022PreflightRequirement { + return crate::Token2022PreflightRequirement { + role: "source".to_string(), + account, + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Account, + max_data_bytes: 512, + expected_mint: std::option::Option::None, + expected_owner: std::option::Option::None, + expected_decimals: std::option::Option::None, + required_extensions: std::vec::Vec::new(), + context: crate::Token2022StatefulContext::default(), + }; + } + + #[test] + fn exact_duplicate_requirements_are_deduplicated_and_bounded() { + let item = requirement(pubkey(1)); + let request = crate::Token2022PreflightRequest { + query_role: "query".to_string(), + min_context_slot: std::option::Option::Some(7), + max_accounts: 1, + max_total_data_bytes: 512, + requirements: std::vec![item.clone(), item], + elgamal_registry: std::option::Option::None, + }; + let result = + crate::solana_token_2022_preflight::validate_and_deduplicate_requirements(&request); + assert_eq!(result.as_ref().map(std::vec::Vec::len), std::result::Result::Ok(1)); + } + + #[test] + fn conflicting_duplicates_and_aggregate_budget_fail_closed() { + let first = requirement(pubkey(2)); + let mut conflicting = first.clone(); + conflicting.role = "destination".to_string(); + let conflict_request = crate::Token2022PreflightRequest { + query_role: "query".to_string(), + min_context_slot: std::option::Option::None, + max_accounts: 2, + max_total_data_bytes: 1024, + requirements: std::vec![first, conflicting], + elgamal_registry: std::option::Option::None, + }; + assert!( + crate::solana_token_2022_preflight::validate_and_deduplicate_requirements( + &conflict_request + ) + .is_err() + ); + let budget_request = crate::Token2022PreflightRequest { + query_role: "query".to_string(), + min_context_slot: std::option::Option::None, + max_accounts: 2, + max_total_data_bytes: 700, + requirements: std::vec![requirement(pubkey(3)), requirement(pubkey(4))], + elgamal_registry: std::option::Option::None, + }; + assert!( + crate::solana_token_2022_preflight::validate_and_deduplicate_requirements( + &budget_request + ) + .is_err() + ); + } + + #[test] + fn requirement_validation_checks_mint_owner_decimals_and_extensions() { + let account = pubkey(5); + let mint = pubkey(6); + let owner = pubkey(7); + let requirement = crate::Token2022PreflightRequirement { + role: "source".to_string(), + account: account.clone(), + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Account, + max_data_bytes: 512, + expected_mint: std::option::Option::Some(mint.clone()), + expected_owner: std::option::Option::Some(owner.clone()), + expected_decimals: std::option::Option::None, + required_extensions: std::vec!["memo_transfer".to_string()], + context: crate::Token2022StatefulContext::default(), + }; + let result = crate::Token2022StatefulReadResult { + commitment: "confirmed".to_string(), + context_slot: 42, + snapshot: crate::Token2022StatefulSnapshotBundle { + account_key: account.0.clone(), + slot: 42, + state_kind: "account".to_string(), + extension_names: std::vec!["memo_transfer".to_string()], + outputs: std::vec![kb_materializer_api::MaterializedOutput { + output_key: "state".to_string(), + family: kb_model::MaterializedEventFamily::TokenAccount, + payload_json: serde_json::json!({ + "domain":"spl_token_2022_account_state", + "baseFields":{"mint":mint.0,"owner":owner.0} + }), + }], + }, + }; + let report = crate::solana_token_2022_preflight::validate_snapshot_requirement( + &requirement, + &result, + ); + assert_eq!( + report.as_ref().map(|value| return value.checks.len()), + std::result::Result::Ok(6) + ); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_proof_orchestration.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_proof_orchestration.rs new file mode 100644 index 0000000..76c82ab --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_proof_orchestration.rs @@ -0,0 +1,271 @@ +// file: kb_pipeline/src/solana_token_2022_proof_orchestration.rs +// version: 2 + +//! Deterministic orchestration contract for mixed Token-2022 proof locations. + +/// Maximum proof references accepted by one confidential Token-2022 operation. +pub const MAX_TOKEN_2022_OPERATION_PROOFS: usize = 5; + +/// One bounded proof-orchestration request prepared before transaction assembly. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Token2022ProofOrchestrationRequest { + /// Stable executor operation code. + pub operation_code: std::string::String, + /// Exact ordered proof references required by the operation. + pub proofs: std::vec::Vec, + /// Optional authority expected on every context-state account. + pub expected_context_authority: std::option::Option, + /// Maximum compute-unit limit accepted for the future transaction. + pub compute_unit_limit: u32, + /// Maximum total fee accepted for the future transaction. + pub max_fee_lamports: u64, + /// Whether a future submission was explicitly requested. + pub submit: bool, + /// Whether the operator explicitly confirmed the future submission. + pub operator_confirmed: bool, +} + +/// One deterministic mixed-proof orchestration result. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Token2022ProofOrchestrationReport { + /// Stable executor operation code. + pub operation_code: std::string::String, + /// Whether the instructions sysvar must be present. + pub instructions_sysvar_required: bool, + /// Ordered non-zero inline offsets. + pub inline_offsets: std::vec::Vec, + /// Ordered validated context-state requirements. + pub context_requirements: std::vec::Vec, + /// Simulation is always mandatory. + pub simulation_required: bool, + /// Whether the future send path is authorized by request-local policy. + pub send_authorized: bool, + /// Ordered successful checks. + pub checks: std::vec::Vec, +} + +/// Validates mixed inline/context-state proof orchestration before transaction assembly. +pub fn orchestrate_token_2022_proofs( + request: &crate::Token2022ProofOrchestrationRequest, + cryptographic_preflight: &crate::Token2022CryptographicPreflightReport, +) -> kb_core::Result { + if request.operation_code.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Token-2022 proof orchestration operation_code must not be empty", + )); + } + if request.proofs.len() > crate::MAX_TOKEN_2022_OPERATION_PROOFS { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_operation_proof_limit_exceeded", + format!( + "Token-2022 proof orchestration accepts at most {} proofs", + crate::MAX_TOKEN_2022_OPERATION_PROOFS + ), + )); + } + if request.compute_unit_limit == 0 || request.max_fee_lamports == 0 { + return std::result::Result::Err(kb_core::Error::config( + "Token-2022 proof orchestration requires non-zero compute and fee ceilings", + )); + } + if request.submit && !request.operator_confirmed { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_submission_confirmation_required", + "Token-2022 submission requires explicit operator confirmation", + )); + } + let mut kinds = std::collections::BTreeSet::::new(); + let mut offsets = std::collections::BTreeSet::::new(); + let mut context_accounts = std::collections::BTreeSet::::new(); + let mut inline_offsets = std::vec::Vec::::new(); + let mut context_requirements = std::vec::Vec::::new(); + for proof in &request.proofs { + let kind_code = proof_kind_code(proof.kind).to_string(); + if !kinds.insert(kind_code.clone()) { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_duplicate_proof_kind", + format!( + "Token-2022 operation {} contains duplicate proof kind {kind_code}", + request.operation_code + ), + )); + } + if let std::result::Result::Err(error) = proof.location.validate() { + return std::result::Result::Err(kb_core::Error::invalid_state(error)); + } + match &proof.location { + kb_executor_spl_token_2022::SplTokenConfidentialProofLocation::InstructionOffset { offset } => { + if !offsets.insert(*offset) { + return std::result::Result::Err(kb_core::Error::new("token_2022_duplicate_inline_proof_offset", format!("Token-2022 operation {} reuses inline proof offset {offset}", request.operation_code))); + } + inline_offsets.push(*offset); + }, + kb_executor_spl_token_2022::SplTokenConfidentialProofLocation::ContextStateAccount { account } => { + if !context_accounts.insert(account.0.clone()) { + return std::result::Result::Err(kb_core::Error::new("token_2022_duplicate_context_state_account", format!("Token-2022 operation {} reuses context-state account {}", request.operation_code, account.0))); + } + context_requirements.push(crate::Token2022ProofContextRequirement { role: kind_code, account: account.clone(), proof_type: proof_kind_to_zk_type(proof.kind), expected_authority: request.expected_context_authority.clone() }); + }, + } + } + if let std::result::Result::Err(error) = + validate_context_reports(context_requirements.as_slice(), cryptographic_preflight) + { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(crate::Token2022ProofOrchestrationReport { + operation_code: request.operation_code.clone(), + instructions_sysvar_required: !inline_offsets.is_empty(), + inline_offsets, + context_requirements, + simulation_required: true, + send_authorized: request.submit && request.operator_confirmed, + checks: vec![ + "ordered_unique_proof_kinds".to_string(), + "non_zero_unique_inline_offsets".to_string(), + "unique_context_state_accounts".to_string(), + "context_preflight_matches_operation".to_string(), + "simulation_required".to_string(), + "bounded_compute_and_fee".to_string(), + ], + }); +} + +fn validate_context_reports( + requirements: &[crate::Token2022ProofContextRequirement], + report: &crate::Token2022CryptographicPreflightReport, +) -> kb_core::Result<()> { + if requirements.len() != report.proof_contexts.len() { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_context_preflight_count_mismatch", + format!( + "Token-2022 operation requires {} context states, preflight contains {}", + requirements.len(), + report.proof_contexts.len() + ), + )); + } + for (requirement, observed) in requirements.iter().zip(report.proof_contexts.iter()) { + if requirement.account != observed.account + || requirement.proof_type.discriminator() != observed.proof_discriminator + { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_context_preflight_mismatch", + format!( + "Token-2022 proof context {} does not match the ordered operation requirement", + observed.account.0 + ), + )); + } + } + return std::result::Result::Ok(()); +} + +fn proof_kind_code( + kind: kb_executor_spl_token_2022::SplTokenConfidentialProofKind, +) -> &'static str { + return match kind { + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::PubkeyValidity => "pubkey_validity", + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::ZeroCiphertext => "zero_ciphertext", + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::CiphertextCommitmentEquality => "ciphertext_commitment_equality", + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::CiphertextCiphertextEquality => "ciphertext_ciphertext_equality", + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity => "batched_grouped_ciphertext_3_handles_validity", + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedGroupedCiphertext2HandlesValidity => "batched_grouped_ciphertext_2_handles_validity", + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::PercentageWithFee => "percentage_with_fee", + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedRangeProofU64 => "batched_range_proof_u64", + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedRangeProofU128 => "batched_range_proof_u128", + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedRangeProofU256 => "batched_range_proof_u256", + }; +} + +fn proof_kind_to_zk_type( + kind: kb_executor_spl_token_2022::SplTokenConfidentialProofKind, +) -> kb_executor_solana_core::ZkElGamalProofType { + return match kind { + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::PubkeyValidity => kb_executor_solana_core::ZkElGamalProofType::PubkeyValidity, + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::ZeroCiphertext => kb_executor_solana_core::ZkElGamalProofType::ZeroCiphertext, + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::CiphertextCommitmentEquality => kb_executor_solana_core::ZkElGamalProofType::CiphertextCommitmentEquality, + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::CiphertextCiphertextEquality => kb_executor_solana_core::ZkElGamalProofType::CiphertextCiphertextEquality, + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedGroupedCiphertext3HandlesValidity => kb_executor_solana_core::ZkElGamalProofType::BatchedGroupedCiphertext3HandlesValidity, + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedGroupedCiphertext2HandlesValidity => kb_executor_solana_core::ZkElGamalProofType::BatchedGroupedCiphertext2HandlesValidity, + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::PercentageWithFee => kb_executor_solana_core::ZkElGamalProofType::PercentageWithCap, + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedRangeProofU64 => kb_executor_solana_core::ZkElGamalProofType::BatchedRangeProofU64, + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedRangeProofU128 => kb_executor_solana_core::ZkElGamalProofType::BatchedRangeProofU128, + kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedRangeProofU256 => kb_executor_solana_core::ZkElGamalProofType::BatchedRangeProofU256, + }; +} + +#[cfg(test)] +mod tests { + fn pubkey(byte: u8) -> kb_model::Pubkey { + return kb_model::Pubkey(bs58::encode([byte; 32]).into_string()); + } + + fn context_report( + account: kb_model::Pubkey, + proof_type: kb_executor_solana_core::ZkElGamalProofType, + ) -> crate::Token2022ProofContextReport { + return crate::Token2022ProofContextReport { + role: "proof".to_string(), + account, + proof_discriminator: proof_type.discriminator(), + context_state_bytes: proof_type.context_state_size(), + context_slot: 100, + checks: vec!["validated".to_string()], + }; + } + + #[test] + fn mixed_proofs_require_sysvar_and_preserve_ordered_contexts() { + let context = pubkey(1); + let request = crate::Token2022ProofOrchestrationRequest { operation_code: "spl_token_2022.confidential_transfer".to_string(), proofs: vec![kb_executor_spl_token_2022::SplTokenConfidentialProofReference { kind: kb_executor_spl_token_2022::SplTokenConfidentialProofKind::CiphertextCommitmentEquality, location: kb_executor_spl_token_2022::SplTokenConfidentialProofLocation::InstructionOffset { offset: 1 } }, kb_executor_spl_token_2022::SplTokenConfidentialProofReference { kind: kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedRangeProofU128, location: kb_executor_spl_token_2022::SplTokenConfidentialProofLocation::ContextStateAccount { account: context.clone() } }], expected_context_authority: std::option::Option::None, compute_unit_limit: 400_000, max_fee_lamports: 50_000, submit: false, operator_confirmed: false }; + let preflight = crate::Token2022CryptographicPreflightReport { + commitment: "confirmed".to_string(), + context_slot: 100, + proof_contexts: vec![context_report( + context, + kb_executor_solana_core::ZkElGamalProofType::BatchedRangeProofU128, + )], + }; + let report = crate::orchestrate_token_2022_proofs(&request, &preflight) + .expect("mixed proof orchestration must succeed"); + assert!(report.instructions_sysvar_required); + assert_eq!(report.inline_offsets, vec![1]); + assert_eq!(report.context_requirements.len(), 1); + assert!(report.simulation_required); + assert!(!report.send_authorized); + } + + #[test] + fn context_only_proofs_do_not_require_instructions_sysvar() { + let context = pubkey(2); + let request = crate::Token2022ProofOrchestrationRequest { operation_code: "spl_token_2022.empty_confidential_account".to_string(), proofs: vec![kb_executor_spl_token_2022::SplTokenConfidentialProofReference { kind: kb_executor_spl_token_2022::SplTokenConfidentialProofKind::ZeroCiphertext, location: kb_executor_spl_token_2022::SplTokenConfidentialProofLocation::ContextStateAccount { account: context.clone() } }], expected_context_authority: std::option::Option::None, compute_unit_limit: 200_000, max_fee_lamports: 20_000, submit: true, operator_confirmed: true }; + let preflight = crate::Token2022CryptographicPreflightReport { + commitment: "confirmed".to_string(), + context_slot: 100, + proof_contexts: vec![context_report( + context, + kb_executor_solana_core::ZkElGamalProofType::ZeroCiphertext, + )], + }; + let report = crate::orchestrate_token_2022_proofs(&request, &preflight) + .expect("context-only orchestration must succeed"); + assert!(!report.instructions_sysvar_required); + assert!(report.send_authorized); + } + + #[test] + fn duplicate_offsets_contexts_and_unconfirmed_submission_fail_closed() { + let duplicate_offset = crate::Token2022ProofOrchestrationRequest { operation_code: "operation".to_string(), proofs: vec![kb_executor_spl_token_2022::SplTokenConfidentialProofReference { kind: kb_executor_spl_token_2022::SplTokenConfidentialProofKind::ZeroCiphertext, location: kb_executor_spl_token_2022::SplTokenConfidentialProofLocation::InstructionOffset { offset: 1 } }, kb_executor_spl_token_2022::SplTokenConfidentialProofReference { kind: kb_executor_spl_token_2022::SplTokenConfidentialProofKind::BatchedRangeProofU64, location: kb_executor_spl_token_2022::SplTokenConfidentialProofLocation::InstructionOffset { offset: 1 } }], expected_context_authority: std::option::Option::None, compute_unit_limit: 1, max_fee_lamports: 1, submit: false, operator_confirmed: false }; + let empty = crate::Token2022CryptographicPreflightReport { + commitment: "confirmed".to_string(), + context_slot: 0, + proof_contexts: vec![], + }; + assert!(crate::orchestrate_token_2022_proofs(&duplicate_offset, &empty).is_err()); + let mut unconfirmed = duplicate_offset; + unconfirmed.proofs.clear(); + unconfirmed.submit = true; + assert!(crate::orchestrate_token_2022_proofs(&unconfirmed, &empty).is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_stateful.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_stateful.rs new file mode 100644 index 0000000..5d756ad --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_stateful.rs @@ -0,0 +1,803 @@ +// file: kb_pipeline/src/solana_token_2022_stateful.rs +// version: 5 + +//! Contextual Token-2022 account-state validation and materialization routing. + +/// Optional external identities required to validate cross-account Token-2022 state. +#[derive(Clone, Debug, Default, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct Token2022StatefulContext { + /// Expected group account for one TokenGroupMember extension. + pub expected_group_address: std::option::Option, +} + +/// Maximum complete Token-2022 account data accepted by one bounded RPC read. +pub const MAX_TOKEN_2022_STATEFUL_ACCOUNT_BYTES: usize = 65_536; + +/// One bounded Token-2022 account read request. +#[derive(Clone, Debug, PartialEq)] +pub struct Token2022StatefulReadRequest { + /// Endpoint role used for the HTTP RPC request. + pub query_role: std::string::String, + /// Canonical account address. + pub account: kb_model::Pubkey, + /// Expected Token-2022 base-state category. + pub kind: kb_decoder_spl_token_2022::state::Token2022StateKind, + /// Optional minimum RPC context slot. + pub min_context_slot: std::option::Option, + /// Maximum decoded account bytes accepted from the endpoint. + pub max_data_bytes: usize, + /// Optional external identities needed by cross-account extensions. + pub context: crate::solana_token_2022_stateful::Token2022StatefulContext, +} + +/// One bounded RPC read and its contextually validated projections. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct Token2022StatefulReadResult { + /// Commitment used for the RPC read. + pub commitment: std::string::String, + /// Context slot returned by the endpoint. + pub context_slot: u64, + /// Complete validated snapshot bundle. + pub snapshot: crate::solana_token_2022_stateful::Token2022StatefulSnapshotBundle, +} + +/// Reads, validates, parses, and routes one bounded Token-2022 account snapshot. +pub async fn read_token_2022_stateful_snapshot( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::solana_token_2022_stateful::Token2022StatefulReadRequest, +) -> kb_core::Result { + if request.query_role.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Token-2022 stateful read query_role must not be empty", + )); + } + if request.max_data_bytes == 0 + || request.max_data_bytes + > crate::solana_token_2022_stateful::MAX_TOKEN_2022_STATEFUL_ACCOUNT_BYTES + { + return std::result::Result::Err(kb_core::Error::config(format!( + "Token-2022 stateful read max_data_bytes must be between 1 and {}", + crate::solana_token_2022_stateful::MAX_TOKEN_2022_STATEFUL_ACCOUNT_BYTES + ))); + } + let config = match kb_rpc::GetAccountInfoConfig::new_with_data( + kb_rpc::RpcCommitmentLevel::Confirmed, + request.min_context_slot, + request.max_data_bytes, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let result = match pool + .get_account_info_for_role(request.query_role.as_str(), &request.account, &config) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::solana_token_2022_stateful::materialize_token_2022_account_info_result( + request, &result, + ); +} + +/// Validates one complete RPC account response before Token-2022 parsing and routing. +pub fn materialize_token_2022_account_info_result( + request: &crate::solana_token_2022_stateful::Token2022StatefulReadRequest, + result: &kb_rpc::AccountInfoResult, +) -> kb_core::Result { + if request.max_data_bytes == 0 + || request.max_data_bytes + > crate::solana_token_2022_stateful::MAX_TOKEN_2022_STATEFUL_ACCOUNT_BYTES + { + return std::result::Result::Err(kb_core::Error::config(format!( + "Token-2022 stateful read max_data_bytes must be between 1 and {}", + crate::solana_token_2022_stateful::MAX_TOKEN_2022_STATEFUL_ACCOUNT_BYTES + ))); + } + if let std::option::Option::Some(min_context_slot) = request.min_context_slot { + if result.context.slot < min_context_slot { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_stateful_context_slot_too_old", + format!( + "Token-2022 account context slot {} is below requested minimum {min_context_slot}", + result.context.slot + ), + )); + } + } + let account = match result.account.as_ref() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_stateful_account_missing", + format!("Token-2022 account {} does not exist", request.account.0), + )); + }, + }; + if account.executable { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_stateful_account_executable", + format!("Token-2022 state account {} must not be executable", request.account.0), + )); + } + if account.owner.0 != kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_stateful_owner_mismatch", + format!( + "Token-2022 state account {} owner must be {}, got {}", + request.account.0, + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID, + account.owner.0 + ), + )); + } + if account.space > request.max_data_bytes as u64 || account.data.len() > request.max_data_bytes + { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_stateful_account_too_large", + format!( + "Token-2022 account {} reports {} bytes and returned {} bytes above limit {}", + request.account.0, + account.space, + account.data.len(), + request.max_data_bytes + ), + )); + } + if account.space != account.data.len() as u64 { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_stateful_account_data_incomplete", + format!( + "Token-2022 account {} reports {} bytes but returned {} decoded bytes", + request.account.0, + account.space, + account.data.len() + ), + )); + } + let state = match kb_decoder_spl_token_2022::state::parse_token_2022_state( + request.kind, + account.data.as_slice(), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_stateful_parse_failed", + error, + )); + }, + }; + let snapshot = match crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot_with_context( + request.account.0.as_str(), + result.context.slot, + &state, + &request.context, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_stateful_projection_failed", + error, + )); + }, + }; + return std::result::Result::Ok( + crate::solana_token_2022_stateful::Token2022StatefulReadResult { + commitment: "confirmed".to_string(), + context_slot: result.context.slot, + snapshot, + }, + ); +} + +/// One contextually validated Token-2022 state snapshot and its owned projections. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct Token2022StatefulSnapshotBundle { + /// Canonical account identity used for contextual checks and stable output keys. + pub account_key: std::string::String, + /// Context slot associated with the account read. + pub slot: u64, + /// Parsed base-state category. + pub state_kind: std::string::String, + /// Ordered published or future extension names retained by the parser. + pub extension_names: std::vec::Vec, + /// Processor-owned projections routed without duplicate ownership. + pub outputs: std::vec::Vec, +} + +/// Parse, contextually validate, and materialize one bounded Token-2022 account snapshot. +pub fn materialize_token_2022_stateful_snapshot( + account_key: &str, + owner_program_id: &str, + slot: u64, + kind: kb_decoder_spl_token_2022::state::Token2022StateKind, + data: &[u8], +) -> std::result::Result +{ + if owner_program_id != kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID { + return std::result::Result::Err(format!( + "Token-2022 state snapshot owner must be {}, got {owner_program_id}", + kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID + )); + } + let state = match kb_decoder_spl_token_2022::state::parse_token_2022_state(kind, data) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot_with_context( + account_key, + slot, + &state, + &crate::solana_token_2022_stateful::Token2022StatefulContext::default(), + ); +} + +/// Contextually validate and materialize one already parsed Token-2022 account snapshot. +pub fn materialize_parsed_token_2022_stateful_snapshot( + account_key: &str, + slot: u64, + state: &kb_decoder_spl_token_2022::state::Token2022State, +) -> std::result::Result +{ + return crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot_with_context( + account_key, + slot, + state, + &crate::solana_token_2022_stateful::Token2022StatefulContext::default(), + ); +} + +/// Contextually validate one parsed Token-2022 snapshot with external cross-account identities. +pub fn materialize_parsed_token_2022_stateful_snapshot_with_context( + account_key: &str, + slot: u64, + state: &kb_decoder_spl_token_2022::state::Token2022State, + context: &crate::solana_token_2022_stateful::Token2022StatefulContext, +) -> std::result::Result +{ + if account_key.trim().is_empty() { + return std::result::Result::Err( + "Token-2022 stateful snapshot requires a non-empty account key".to_string(), + ); + } + let decoded = match bs58::decode(account_key).into_vec() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err( + "Token-2022 stateful snapshot account key must be valid base58".to_string(), + ); + }, + }; + if decoded.len() != 32 { + return std::result::Result::Err(format!( + "Token-2022 stateful snapshot account key must decode to 32 bytes, got {}", + decoded.len() + )); + } + match crate::solana_token_2022_stateful::validate_embedded_mint_identity(account_key, state) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + match crate::solana_token_2022_stateful::validate_group_member_identity(state, context) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let account_output = match kb_materializer_token_accounts::materialize_token_2022_state_snapshot( + account_key, + slot, + state, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut outputs = std::vec![account_output]; + if state.kind == kb_decoder_spl_token_2022::state::Token2022StateKind::Mint { + let metadata_outputs = + match kb_materializer_metadata::materialize_token_2022_metadata_snapshot( + account_key, + slot, + state, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + outputs.extend(metadata_outputs); + } + let fee_outputs = match kb_materializer_fees::materialize_token_2022_fee_state_snapshots( + account_key, + slot, + state, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + outputs.extend(fee_outputs); + let admin_outputs = match kb_materializer_admin::materialize_token_2022_admin_state_snapshots( + account_key, + slot, + state, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + outputs.extend(admin_outputs); + let state_kind = match state.kind { + kb_decoder_spl_token_2022::state::Token2022StateKind::Mint => "mint", + kb_decoder_spl_token_2022::state::Token2022StateKind::Account => "account", + kb_decoder_spl_token_2022::state::Token2022StateKind::Multisig => "multisig", + }; + return std::result::Result::Ok( + crate::solana_token_2022_stateful::Token2022StatefulSnapshotBundle { + account_key: account_key.to_string(), + slot, + state_kind: state_kind.to_string(), + extension_names: state + .extensions + .iter() + .map(|entry| return entry.extension_name.to_string()) + .collect(), + outputs, + }, + ); +} + +fn validate_embedded_mint_identity( + account_key: &str, + state: &kb_decoder_spl_token_2022::state::Token2022State, +) -> std::result::Result<(), String> { + for entry in &state.extensions { + if entry.extension_name != "token_metadata" + && entry.extension_name != "token_group" + && entry.extension_name != "token_group_member" + { + continue; + } + let embedded_mint = entry.value_fields.get("mint").and_then(serde_json::Value::as_str); + let embedded_mint = match embedded_mint { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(format!( + "Token-2022 {} extension requires a structured mint field", + entry.extension_name + )); + }, + }; + if embedded_mint != account_key { + return std::result::Result::Err(format!( + "Token-2022 {} mint {} does not match account {account_key}", + entry.extension_name, embedded_mint + )); + } + } + return std::result::Result::Ok(()); +} + +fn validate_group_member_identity( + state: &kb_decoder_spl_token_2022::state::Token2022State, + context: &crate::solana_token_2022_stateful::Token2022StatefulContext, +) -> std::result::Result<(), String> { + for entry in &state.extensions { + if entry.extension_name != "token_group_member" { + continue; + } + let group = entry.value_fields.get("group").and_then(serde_json::Value::as_str); + let group = match group { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "Token-2022 token_group_member extension requires a structured group field" + .to_string(), + ); + }, + }; + let expected = match context.expected_group_address.as_deref() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err( + "Token-2022 token_group_member validation requires an expected group address" + .to_string(), + ); + }, + }; + if group != expected { + return std::result::Result::Err(format!( + "Token-2022 token_group_member group {group} does not match expected group {expected}" + )); + } + } + return std::result::Result::Ok(()); +} + +#[cfg(test)] +mod tests { + fn account_key(byte: u8) -> String { + return bs58::encode([byte; 32]).into_string(); + } + + fn mint_state(account_key: &str) -> kb_decoder_spl_token_2022::state::Token2022State { + return kb_decoder_spl_token_2022::state::Token2022State { + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Mint, + base_fields: serde_json::json!({"supply":"1","decimals":0,"initialized":true}), + base_hex: "00".repeat(82), + account_type: std::option::Option::Some(1), + extensions: std::vec![ + kb_decoder_spl_token_2022::state::Token2022TlvEntry { + extension_type: 19, + extension_name: "token_metadata", + value_hex: "01".to_string(), + value_fields: serde_json::json!({ + "mint": account_key, + "name": "Token", + "symbol": "TOK", + "uri": "https://example.invalid/token.json" + }), + }, + kb_decoder_spl_token_2022::state::Token2022TlvEntry { + extension_type: 20, + extension_name: "token_group", + value_hex: "02".to_string(), + value_fields: serde_json::json!({ + "mint": account_key, + "size": "1", + "maxSize": "10" + }), + }, + ], + }; + } + + #[test] + fn mint_snapshot_routes_account_and_metadata_outputs_once() { + let account_key = account_key(7); + let state = mint_state(account_key.as_str()); + let bundle = + crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot( + account_key.as_str(), + 42, + &state, + ); + assert_eq!( + bundle.as_ref().map(|value| return value.outputs.len()), + std::result::Result::Ok(3) + ); + assert_eq!( + bundle.as_ref().map(|value| return value.state_kind.clone()), + std::result::Result::Ok("mint".to_string()) + ); + assert_eq!( + bundle.as_ref().map(|value| return value.extension_names.clone()), + std::result::Result::Ok(std::vec![ + "token_metadata".to_string(), + "token_group".to_string() + ]) + ); + } + + #[test] + fn embedded_metadata_mint_must_match_the_account_identity() { + let account_address = account_key(8); + let mut state = mint_state(account_address.as_str()); + state.extensions[0].value_fields["mint"] = serde_json::json!(account_key(9)); + let result = + crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot( + account_address.as_str(), + 42, + &state, + ); + assert!(result.is_err()); + } + + #[test] + fn owner_and_account_identity_fail_closed_before_projection() { + let account_key = account_key(10); + let data = [0u8; 82]; + let wrong_owner = + crate::solana_token_2022_stateful::materialize_token_2022_stateful_snapshot( + account_key.as_str(), + kb_program_ids::SPL_TOKEN_PROGRAM_ID, + 1, + kb_decoder_spl_token_2022::state::Token2022StateKind::Mint, + &data, + ); + assert!(wrong_owner.is_err()); + let state = mint_state(account_key.as_str()); + let malformed_key = + crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot( + "not-base58-0", + 1, + &state, + ); + assert!(malformed_key.is_err()); + } + + #[test] + fn account_snapshot_has_no_metadata_projection() { + let account_key = account_key(11); + let state = kb_decoder_spl_token_2022::state::Token2022State { + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Account, + base_fields: serde_json::json!({"amount":"0","state":"initialized"}), + base_hex: "00".repeat(165), + account_type: std::option::Option::None, + extensions: std::vec::Vec::new(), + }; + let bundle = + crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot( + account_key.as_str(), + 9, + &state, + ); + assert_eq!( + bundle.as_ref().map(|value| return value.outputs.len()), + std::result::Result::Ok(1) + ); + } + #[test] + fn group_member_requires_and_matches_external_group_identity() { + let account_address = account_key(12); + let group_address = account_key(13); + let state = kb_decoder_spl_token_2022::state::Token2022State { + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Mint, + base_fields: serde_json::json!({"supply":"1","decimals":0,"initialized":true}), + base_hex: "00".repeat(82), + account_type: std::option::Option::Some(1), + extensions: std::vec![kb_decoder_spl_token_2022::state::Token2022TlvEntry { + extension_type: 23, + extension_name: "token_group_member", + value_hex: "03".to_string(), + value_fields: serde_json::json!({ + "mint": account_address, + "group": group_address, + "memberNumber": "1" + }), + }], + }; + let missing = + crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot( + account_address.as_str(), + 44, + &state, + ); + assert!(missing.is_err()); + let wrong_context = crate::solana_token_2022_stateful::Token2022StatefulContext { + expected_group_address: std::option::Option::Some(account_key(14)), + }; + let wrong = crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot_with_context( + account_address.as_str(), + 44, + &state, + &wrong_context, + ); + assert!(wrong.is_err()); + let context = crate::solana_token_2022_stateful::Token2022StatefulContext { + expected_group_address: std::option::Option::Some(group_address), + }; + let valid = crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot_with_context( + account_address.as_str(), + 44, + &state, + &context, + ); + assert_eq!( + valid.as_ref().map(|bundle| return bundle.outputs.len()), + std::result::Result::Ok(2) + ); + } + #[test] + fn mint_and_account_fee_extensions_route_to_the_fee_owner_once() { + let mint_address = account_key(15); + let mint = kb_decoder_spl_token_2022::state::Token2022State { + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Mint, + base_fields: serde_json::json!({"supply":"1","decimals":0,"initialized":true}), + base_hex: "00".repeat(82), + account_type: std::option::Option::Some(1), + extensions: std::vec![kb_decoder_spl_token_2022::state::Token2022TlvEntry { + extension_type: 1, + extension_name: "transfer_fee_config", + value_hex: "01".to_string(), + value_fields: serde_json::json!({"withheldAmount":"7"}), + }], + }; + let mint_bundle = + crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot( + mint_address.as_str(), + 50, + &mint, + ); + assert_eq!( + mint_bundle.as_ref().map(|bundle| return bundle.outputs.len()), + std::result::Result::Ok(2) + ); + assert_eq!( + mint_bundle + .as_ref() + .map(|bundle| return bundle.outputs[1].payload_json["provenance"]["processorName"] + .clone()), + std::result::Result::Ok(serde_json::json!("fees")) + ); + let account_address = account_key(16); + let account = kb_decoder_spl_token_2022::state::Token2022State { + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Account, + base_fields: serde_json::json!({"amount":"0","state":"initialized"}), + base_hex: "00".repeat(165), + account_type: std::option::Option::Some(2), + extensions: std::vec![kb_decoder_spl_token_2022::state::Token2022TlvEntry { + extension_type: 17, + extension_name: "confidential_transfer_fee_amount", + value_hex: "02".to_string(), + value_fields: serde_json::json!({"withheldAmount":"ciphertext"}), + }], + }; + let account_bundle = + crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot( + account_address.as_str(), + 51, + &account, + ); + assert_eq!( + account_bundle.as_ref().map(|bundle| return bundle.outputs.len()), + std::result::Result::Ok(2) + ); + assert_eq!( + account_bundle + .as_ref() + .map(|bundle| return bundle.outputs[1].payload_json["confidentialValuesDecrypted"] + .clone()), + std::result::Result::Ok(serde_json::json!(false)) + ); + } + + #[test] + fn mint_admin_extensions_route_to_the_admin_owner_once() { + let account_key = account_key(12); + let mut state = mint_state(account_key.as_str()); + state.extensions.push(kb_decoder_spl_token_2022::state::Token2022TlvEntry { + extension_type: 6, + extension_name: "default_account_state", + value_hex: "02".to_string(), + value_fields: serde_json::json!({"state": 2}), + }); + let bundle = + crate::solana_token_2022_stateful::materialize_parsed_token_2022_stateful_snapshot( + account_key.as_str(), + 17, + &state, + ); + assert_eq!( + bundle.as_ref().map(|value| return value.outputs.len()), + std::result::Result::Ok(4) + ); + assert_eq!( + bundle.as_ref().map(|value| { + return value + .outputs + .iter() + .filter(|output| { + return output.payload_json["domain"] + == serde_json::json!("token_2022_extension_admin_state"); + }) + .count(); + }), + std::result::Result::Ok(1) + ); + } + + #[test] + fn complete_rpc_account_routes_only_after_owner_size_and_context_validation() { + let account = crate::solana_token_2022_stateful::tests::account_key(21); + let request = crate::solana_token_2022_stateful::Token2022StatefulReadRequest { + query_role: "execution".to_string(), + account: kb_model::Pubkey(account.clone()), + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Mint, + min_context_slot: std::option::Option::Some(40), + max_data_bytes: 82, + context: crate::solana_token_2022_stateful::Token2022StatefulContext::default(), + }; + let result = kb_rpc::AccountInfoResult { + context: kb_rpc::RpcResponseContext { + slot: 41, + api_version: std::option::Option::None, + }, + account: std::option::Option::Some(kb_rpc::AccountInfoValue { + lamports: 1, + owner: kb_model::ProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string()), + executable: false, + rent_epoch: 0, + space: 82, + data: std::vec![0; 82], + }), + }; + let materialized = + crate::solana_token_2022_stateful::materialize_token_2022_account_info_result( + &request, &result, + ); + assert_eq!( + materialized.as_ref().map(|value| return value.context_slot), + std::result::Result::Ok(41) + ); + assert_eq!( + materialized.as_ref().map(|value| return value.snapshot.outputs.len()), + std::result::Result::Ok(1) + ); + } + + #[test] + fn incomplete_foreign_executable_and_stale_rpc_accounts_fail_closed() { + let account = crate::solana_token_2022_stateful::tests::account_key(22); + let request = crate::solana_token_2022_stateful::Token2022StatefulReadRequest { + query_role: "execution".to_string(), + account: kb_model::Pubkey(account), + kind: kb_decoder_spl_token_2022::state::Token2022StateKind::Mint, + min_context_slot: std::option::Option::Some(50), + max_data_bytes: 82, + context: crate::solana_token_2022_stateful::Token2022StatefulContext::default(), + }; + let base = kb_rpc::AccountInfoValue { + lamports: 1, + owner: kb_model::ProgramId(kb_program_ids::SPL_TOKEN_2022_PROGRAM_ID.to_string()), + executable: false, + rent_epoch: 0, + space: 82, + data: std::vec![0; 82], + }; + let stale = kb_rpc::AccountInfoResult { + context: kb_rpc::RpcResponseContext { + slot: 49, + api_version: std::option::Option::None, + }, + account: std::option::Option::Some(base.clone()), + }; + assert!( + crate::solana_token_2022_stateful::materialize_token_2022_account_info_result( + &request, &stale + ) + .is_err() + ); + let mut foreign = base.clone(); + foreign.owner = kb_model::ProgramId(kb_program_ids::SPL_TOKEN_PROGRAM_ID.to_string()); + let foreign = kb_rpc::AccountInfoResult { + context: kb_rpc::RpcResponseContext { + slot: 50, + api_version: std::option::Option::None, + }, + account: std::option::Option::Some(foreign), + }; + assert!( + crate::solana_token_2022_stateful::materialize_token_2022_account_info_result( + &request, &foreign + ) + .is_err() + ); + let mut executable = base.clone(); + executable.executable = true; + let executable = kb_rpc::AccountInfoResult { + context: kb_rpc::RpcResponseContext { + slot: 50, + api_version: std::option::Option::None, + }, + account: std::option::Option::Some(executable), + }; + assert!( + crate::solana_token_2022_stateful::materialize_token_2022_account_info_result( + &request, + &executable + ) + .is_err() + ); + let mut incomplete = base; + incomplete.data.pop(); + let incomplete = kb_rpc::AccountInfoResult { + context: kb_rpc::RpcResponseContext { + slot: 50, + api_version: std::option::Option::None, + }, + account: std::option::Option::Some(incomplete), + }; + assert!( + crate::solana_token_2022_stateful::materialize_token_2022_account_info_result( + &request, + &incomplete + ) + .is_err() + ); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_validation.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_validation.rs new file mode 100644 index 0000000..75e3630 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_2022_validation.rs @@ -0,0 +1,440 @@ +// file: kb_pipeline/src/solana_token_2022_validation.rs +// version: 2 + +//! Machine-readable validation evidence contract for the Token-2022 milestone. + +/// Maximum number of evidence records accepted in one validation report. +pub const MAX_TOKEN_2022_VALIDATION_EVIDENCE: usize = 64; + +/// Required validation environment for one scenario. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum Token2022ValidationEnvironment { + /// Deterministic offline fixtures and builder comparisons. + Offline, + /// A local validator under operator control. + Localnet, + /// Solana Devnet. + Devnet, + /// Scenario may run on Localnet or Devnet according to deployment availability. + LocalnetOrDevnet, + /// Mainnet observations without mutable execution. + MainnetObservation, + /// PostgreSQL persistence and replay validation. + Postgres, + /// Tauri application smoke validation. + Tauri, +} + +/// Exact status of one validation scenario. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum Token2022ValidationStatus { + /// Scenario is declared but has not been run. + NotRun, + /// Scenario was simulated without submission. + Simulated, + /// Scenario was submitted but confirmation evidence is incomplete. + Submitted, + /// Scenario was confirmed and its required postconditions passed. + Confirmed, + /// Scenario is not available in the selected environment. + Unavailable, + /// Scenario ran and failed. + Failed, +} + +/// One bounded proof attached to a validation scenario. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct Token2022ValidationEvidence { + /// Stable evidence kind such as `signature`, `test_suite`, or `replay`. + pub kind: std::string::String, + /// Bounded evidence value. + pub value: std::string::String, +} + +/// One declared Token-2022 validation scenario and its observed evidence. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct Token2022ValidationScenario { + /// Stable scenario identifier. + pub id: std::string::String, + /// Validation environment. + pub environment: crate::Token2022ValidationEnvironment, + /// Exact observed status. + pub status: crate::Token2022ValidationStatus, + /// Whether canonical hydration is required. + pub requires_canonical_hydration: bool, + /// Whether core extraction is required. + pub requires_core_extraction: bool, + /// Whether decode replay is required. + pub requires_decode_replay: bool, + /// Whether materialization is required. + pub requires_materialization: bool, + /// Whether a second idempotent replay is required. + pub requires_second_replay: bool, + /// Ordered evidence records. + pub evidence: std::vec::Vec, +} + +/// Machine-readable validation matrix loaded from the canonical JSON document. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Token2022ValidationMatrix { + /// Matrix schema version. + pub matrix_version: u32, + /// Milestone owning this matrix. + pub milestone: std::string::String, + /// Aggregate matrix status. + pub status: std::string::String, + /// Exact accepted status vocabulary. + pub status_vocabulary: std::vec::Vec, + /// Scenarios in stable roadmap order. + pub scenarios: std::vec::Vec, +} + +/// One scenario declared by the canonical validation matrix. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Token2022ValidationMatrixScenario { + /// Stable scenario identifier. + pub id: std::string::String, + /// Required execution environment. + pub environment: crate::Token2022ValidationEnvironment, + /// Exact observed status. + pub status: crate::Token2022ValidationStatus, + /// Evidence kinds required before this scenario may be confirmed. + pub required_evidence: std::vec::Vec, + /// Observed bounded evidence. + #[serde(default)] + pub evidence: std::vec::Vec, +} + +/// Loads and validates the canonical Token-2022 validation matrix. +pub fn load_token_2022_validation_matrix() -> kb_core::Result { + let parsed = match serde_json::from_str::(include_str!( + "../../docs/SPL_TOKEN_2022_VALIDATION_MATRIX.json" + )) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_validation_matrix_invalid_json", + format!("Token-2022 validation matrix JSON is invalid: {error}"), + )); + }, + }; + if let std::result::Result::Err(error) = validate_token_2022_validation_matrix(&parsed) { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(parsed); +} + +/// Validates schema, scenario inventory, statuses, and observed evidence. +pub fn validate_token_2022_validation_matrix( + matrix: &crate::Token2022ValidationMatrix, +) -> kb_core::Result<()> { + if matrix.matrix_version != 2 || matrix.milestone != "0.4.6" { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_validation_matrix_contract_mismatch", + "Token-2022 validation matrix must use version 2 for milestone 0.4.6", + )); + } + let expected_statuses = + ["not_run", "simulated", "submitted", "confirmed", "unavailable", "failed"]; + let actual_statuses = matrix + .status_vocabulary + .iter() + .map(|status| return status.as_str()) + .collect::>(); + if actual_statuses.as_slice() != expected_statuses { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_validation_matrix_status_vocabulary_mismatch", + "Token-2022 validation status vocabulary differs from the compiled contract", + )); + } + let expected_ids = [ + "offline_full_regression", + "token_2022_public_devnet", + "elgamal_registry_devnet", + "confidential_transfer_localnet_or_devnet", + "confidential_mint_burn_localnet_or_devnet", + "permissioned_confidential_burn_localnet_or_devnet", + "mainnet_observation_corpus", + "postgres_double_replay", + "tauri_smoke", + ]; + let actual_ids = matrix + .scenarios + .iter() + .map(|scenario| return scenario.id.as_str()) + .collect::>(); + if actual_ids.as_slice() != expected_ids { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_validation_matrix_scenario_inventory_mismatch", + "Token-2022 validation scenario inventory or order differs from the compiled contract", + )); + } + for scenario in &matrix.scenarios { + if scenario.required_evidence.is_empty() + || scenario.required_evidence.len() > crate::MAX_TOKEN_2022_VALIDATION_EVIDENCE + { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_validation_matrix_required_evidence_invalid", + "Every Token-2022 validation scenario must declare bounded required evidence", + )); + } + let mut required = std::collections::BTreeSet::<&str>::new(); + for evidence_kind in &scenario.required_evidence { + if evidence_kind.trim().is_empty() || !required.insert(evidence_kind.as_str()) { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_validation_matrix_required_evidence_invalid", + "Required Token-2022 validation evidence must be non-empty and unique", + )); + } + } + if scenario.status == crate::Token2022ValidationStatus::Confirmed { + let observed = scenario + .evidence + .iter() + .map(|evidence| return evidence.kind.as_str()) + .collect::>(); + if !required.iter().all(|kind| return observed.contains(kind)) { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_validation_matrix_confirmed_without_required_evidence", + format!( + "Confirmed Token-2022 validation scenario {} lacks required evidence", + scenario.id + ), + )); + } + } else if matches!( + scenario.status, + crate::Token2022ValidationStatus::NotRun + | crate::Token2022ValidationStatus::Unavailable + ) && !scenario.evidence.is_empty() + { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_validation_matrix_unobserved_with_evidence", + "Not-run or unavailable Token-2022 scenarios must not retain observed evidence", + )); + } + } + return std::result::Result::Ok(()); +} + +/// Complete validation report checked before milestone closure. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct Token2022ValidationReport { + /// Scenarios in stable roadmap order. + pub scenarios: std::vec::Vec, +} + +/// Validates that a milestone report is bounded, unique, and does not overclaim evidence. +pub fn validate_token_2022_validation_report( + report: &crate::Token2022ValidationReport, +) -> kb_core::Result<()> { + if report.scenarios.is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Token-2022 validation report must contain at least one scenario", + )); + } + let mut ids = std::collections::BTreeSet::::new(); + for scenario in &report.scenarios { + if scenario.id.trim().is_empty() || !ids.insert(scenario.id.clone()) { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_validation_scenario_identity_invalid", + "Token-2022 validation scenario ids must be non-empty and unique", + )); + } + if scenario.evidence.len() > crate::MAX_TOKEN_2022_VALIDATION_EVIDENCE { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_validation_evidence_limit_exceeded", + format!( + "Token-2022 validation accepts at most {} evidence records per scenario", + crate::MAX_TOKEN_2022_VALIDATION_EVIDENCE + ), + )); + } + for evidence in &scenario.evidence { + if evidence.kind.trim().is_empty() || evidence.value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_validation_evidence_invalid", + "Token-2022 validation evidence kind and value must be non-empty", + )); + } + } + let completed = matches!( + scenario.status, + crate::Token2022ValidationStatus::Simulated + | crate::Token2022ValidationStatus::Submitted + | crate::Token2022ValidationStatus::Confirmed + | crate::Token2022ValidationStatus::Failed + ); + if completed && scenario.evidence.is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_validation_completed_without_evidence", + "A completed Token-2022 validation scenario must retain evidence", + )); + } + if scenario.status == crate::Token2022ValidationStatus::Confirmed + && (scenario.requires_canonical_hydration + || scenario.requires_core_extraction + || scenario.requires_decode_replay + || scenario.requires_materialization + || scenario.requires_second_replay) + && !has_pipeline_evidence(scenario) + { + return std::result::Result::Err(kb_core::Error::new( + "token_2022_validation_confirmed_without_pipeline_evidence", + "A confirmed end-to-end Token-2022 scenario must retain hydration, extraction, replay, materialization, and idempotence evidence", + )); + } + } + return std::result::Result::Ok(()); +} + +fn has_pipeline_evidence(scenario: &crate::Token2022ValidationScenario) -> bool { + let kinds = scenario + .evidence + .iter() + .map(|evidence| return evidence.kind.as_str()) + .collect::>(); + if scenario.requires_canonical_hydration && !kinds.contains("canonical_hydration") { + return false; + } + if scenario.requires_core_extraction && !kinds.contains("core_extraction") { + return false; + } + if scenario.requires_decode_replay && !kinds.contains("decode_replay") { + return false; + } + if scenario.requires_materialization && !kinds.contains("materialization") { + return false; + } + if scenario.requires_second_replay && !kinds.contains("second_replay") { + return false; + } + return true; +} + +#[cfg(test)] +mod tests { + fn scenario(status: crate::Token2022ValidationStatus) -> crate::Token2022ValidationScenario { + return crate::Token2022ValidationScenario { + id: "confidential_transfer_devnet".to_string(), + environment: crate::Token2022ValidationEnvironment::Devnet, + status, + requires_canonical_hydration: true, + requires_core_extraction: true, + requires_decode_replay: true, + requires_materialization: true, + requires_second_replay: true, + evidence: vec![ + crate::Token2022ValidationEvidence { + kind: "signature".to_string(), + value: "signature".to_string(), + }, + crate::Token2022ValidationEvidence { + kind: "canonical_hydration".to_string(), + value: "1".to_string(), + }, + crate::Token2022ValidationEvidence { + kind: "core_extraction".to_string(), + value: "1".to_string(), + }, + crate::Token2022ValidationEvidence { + kind: "decode_replay".to_string(), + value: "1".to_string(), + }, + crate::Token2022ValidationEvidence { + kind: "materialization".to_string(), + value: "1".to_string(), + }, + crate::Token2022ValidationEvidence { + kind: "second_replay".to_string(), + value: "0_new_outputs".to_string(), + }, + ], + }; + } + + #[test] + fn confirmed_end_to_end_scenario_requires_complete_pipeline_evidence() { + let report = crate::Token2022ValidationReport { + scenarios: vec![scenario(crate::Token2022ValidationStatus::Confirmed)], + }; + assert!(crate::validate_token_2022_validation_report(&report).is_ok()); + let mut incomplete = scenario(crate::Token2022ValidationStatus::Confirmed); + incomplete.evidence.retain(|evidence| return evidence.kind != "second_replay"); + assert!( + crate::validate_token_2022_validation_report(&crate::Token2022ValidationReport { + scenarios: vec![incomplete] + }) + .is_err() + ); + } + + #[test] + fn not_run_and_unavailable_scenarios_do_not_invent_evidence() { + let mut not_run = scenario(crate::Token2022ValidationStatus::NotRun); + not_run.evidence.clear(); + let mut unavailable = scenario(crate::Token2022ValidationStatus::Unavailable); + unavailable.id = "permissioned_burn_devnet".to_string(); + unavailable.evidence.clear(); + assert!( + crate::validate_token_2022_validation_report(&crate::Token2022ValidationReport { + scenarios: vec![not_run, unavailable] + }) + .is_ok() + ); + } + + #[test] + fn duplicate_ids_empty_evidence_and_completed_without_evidence_fail_closed() { + let first = scenario(crate::Token2022ValidationStatus::Confirmed); + let duplicate = first.clone(); + assert!( + crate::validate_token_2022_validation_report(&crate::Token2022ValidationReport { + scenarios: vec![first, duplicate] + }) + .is_err() + ); + let mut failed = scenario(crate::Token2022ValidationStatus::Failed); + failed.evidence.clear(); + assert!( + crate::validate_token_2022_validation_report(&crate::Token2022ValidationReport { + scenarios: vec![failed] + }) + .is_err() + ); + } + + #[test] + fn canonical_matrix_matches_compiled_inventory_and_observed_offline_evidence() { + let matrix = crate::load_token_2022_validation_matrix(); + assert!(matrix.is_ok()); + let matrix = match matrix { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected matrix error: {error}"), + }; + assert_eq!(matrix.scenarios.len(), 9); + assert_eq!(matrix.scenarios[0].status, crate::Token2022ValidationStatus::Confirmed); + assert_eq!(matrix.scenarios[0].evidence.len(), 4); + assert!( + matrix.scenarios[1..] + .iter() + .all(|scenario| return scenario.status == crate::Token2022ValidationStatus::NotRun) + ); + } + + #[test] + fn confirmed_matrix_scenario_requires_every_declared_evidence_kind() { + let matrix = crate::load_token_2022_validation_matrix(); + assert!(matrix.is_ok()); + let mut matrix = match matrix { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected matrix error: {error}"), + }; + matrix.scenarios[0].evidence.retain(|evidence| return evidence.kind != "clippy"); + assert!(crate::validate_token_2022_validation_matrix(&matrix).is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_execution.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_execution.rs new file mode 100644 index 0000000..e36e393 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_execution.rs @@ -0,0 +1,1406 @@ +// file: kb_pipeline/src/solana_token_execution.rs +// version: 5 + +//! Devnet classic SPL Token execution with stateful and canonical post-validation. + +/// Complete request for one Devnet classic SPL Token execution. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DevnetSplTokenExecutionRequest { + /// Stable caller-provided execution identifier. + pub intent_id: std::string::String, + /// Endpoint role used for state, balance, blockhash, fee and hydration calls. + pub query_role: std::string::String, + /// Endpoint role used for simulation, submission and confirmation polling. + pub transaction_role: std::string::String, + /// Exact typed classic SPL Token operation. + pub operation: kb_executor_spl_token::SplTokenOperation, + /// Explicitly authorizes signing and submission after successful simulation. + pub submit: bool, + /// Explicit operator confirmation required by the active profile. + pub operator_confirmed: bool, + /// Number of `getTransaction` retries after the first hydration attempt. + pub post_validation_max_retries: u32, + /// Replaces existing core and decode outputs for the submitted signature. + pub force_post_validation_replay: bool, +} + +impl crate::solana_token_execution::DevnetSplTokenExecutionRequest { + /// Creates a conservative simulation-only request. + pub fn new( + intent_id: impl std::convert::Into, + operation: kb_executor_spl_token::SplTokenOperation, + ) -> Self { + return Self { + intent_id: intent_id.into(), + query_role: "http_queries".to_string(), + transaction_role: "http_transactions".to_string(), + operation, + submit: false, + operator_confirmed: false, + post_validation_max_retries: 10, + force_post_validation_replay: false, + }; + } + + /// Validates request-local bounds independently from one profile. + pub fn validate(&self) -> kb_core::Result<()> { + if self.intent_id.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Devnet SPL Token execution intent id must not be empty", + )); + } + if self.query_role.trim().is_empty() || self.transaction_role.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Devnet SPL Token execution endpoint roles must not be empty", + )); + } + if self.post_validation_max_retries > 20 { + return std::result::Result::Err(kb_core::Error::config( + "post-execution getTransaction retries must not exceed 20", + )); + } + return std::result::Result::Ok(()); + } +} + +/// Complete result of one Devnet classic SPL Token execution. +#[derive(Clone, Debug, PartialEq)] +pub struct DevnetSplTokenExecutionSummary { + /// Profile used by the orchestration. + pub profile_name: std::string::String, + /// Exact classified cluster. + pub cluster: kb_execution_api::ExecutionCluster, + /// Genesis hash returned by the selected endpoint. + pub genesis_hash: std::string::String, + /// Non-secret persistent wallet description. + pub wallet: kb_wallet::WalletSummary, + /// Wallet balance observed before planning. + pub balance_lamports: u64, + /// Stateful readiness report produced before plan simulation. + pub stateful_readiness: crate::SplTokenStatefulReadinessReport, + /// Exact prepared Token plan. + pub plan: kb_execution_api::PreparedExecutionPlan, + /// Recent blockhash used by the exact transaction. + pub latest_blockhash: kb_rpc::LatestBlockhashResult, + /// Fee estimate for the exact compiled message. + pub fee: kb_rpc::FeeForMessageResult, + /// Exact simulation result bound to the compiled message. + pub simulation: kb_execution_api::ExecutionSimulationResult, + /// Submission result when explicitly authorized. + pub send_result: std::option::Option, + /// Confirmation result when submitted. + pub confirmation: std::option::Option, + /// Canonical hydration result for the exact signature. + pub backfill: std::option::Option, + /// Core extraction result for the exact signature. + pub core_extraction: std::option::Option, + /// First Token decode and materialization replay. + pub decode_replay: std::option::Option, + /// Second replay proving idempotence for the same decoder version and input. + pub idempotence_replay: std::option::Option, + /// Exact materialized rows produced for the submitted Token transaction. + pub materializations: std::vec::Vec, + /// Aggregated post-execution validation diagnostic. + pub post_execution: std::option::Option, +} + +struct PreparedTokenExecution { + wallet: kb_wallet::TemporaryWallet, + unsigned: kb_execution_solana::UnsignedSolanaTransaction, + evidence: kb_execution_solana::SolanaSimulationEvidence, + summary: crate::solana_token_execution::DevnetSplTokenExecutionSummary, +} + +/// Simulates one Devnet classic SPL Token operation after stateful preflight. +pub async fn simulate_devnet_spl_token( + http_pool: &kb_rpc::HttpEndpointPool, + profile: &kb_config::ProfileConfig, + workspace_root: &std::path::Path, + request: &crate::solana_token_execution::DevnetSplTokenExecutionRequest, + observer: &O, +) -> kb_core::Result +where + O: crate::SolanaExecutionObserver, +{ + if request.submit { + return std::result::Result::Err(kb_core::Error::config( + "simulate_devnet_spl_token requires submit=false", + )); + } + let prepared = match crate::solana_token_execution::prepare_execution( + http_pool, + profile, + workspace_root, + request, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(prepared.summary); +} + +/// Executes one Devnet classic SPL Token simulation or authorized submission. +#[allow(clippy::too_many_arguments)] +pub async fn execute_devnet_spl_token( + http_pool: &kb_rpc::HttpEndpointPool, + store: &S, + profile: &kb_config::ProfileConfig, + workspace_root: &std::path::Path, + request: &crate::solana_token_execution::DevnetSplTokenExecutionRequest, + decoders: &[std::sync::Arc], + materializers: &[std::sync::Arc], + observer: &O, +) -> kb_core::Result +where + S: kb_store_core::RawTransactionStore + + kb_store_core::CoreExtractionStore + + kb_store_core::DecodePipelineStore + + Sync, + O: crate::SolanaExecutionObserver, +{ + let prepared = match crate::solana_token_execution::prepare_execution( + http_pool, + profile, + workspace_root, + request, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if !request.submit { + return std::result::Result::Ok(prepared.summary); + } + if !prepared.summary.simulation.success { + return std::result::Result::Err(kb_core::Error::new( + "execution_simulation_failed", + crate::solana_execution::simulation_failure_message(&prepared.summary.simulation), + )); + } + if let std::result::Result::Err(error) = + crate::solana_token_execution::validate_profile_wallet_signers( + prepared.unsigned.required_signer_pubkeys(), + prepared.summary.wallet.public_key.as_str(), + ) + { + return std::result::Result::Err(error); + } + let send_evaluation = match kb_execution_safety::ExecutionSafetyChecker + .evaluate_send(&prepared.summary.plan, &prepared.summary.simulation) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if send_evaluation.decision == kb_execution_safety::ExecutionSafetyDecision::Deny { + return std::result::Result::Err(kb_core::Error::new( + "execution_send_denied", + crate::solana_execution::violation_message(send_evaluation.violations.as_slice()), + )); + } + let signed = match prepared + .unsigned + .sign_after_simulation(&prepared.evidence, &[prepared.wallet.as_signer()]) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if let std::result::Result::Err(error) = signed.verify_signatures() { + return std::result::Result::Err(error); + } + let signature = signed.primary_signature().clone(); + let mut summary = prepared.summary; + let mut diagnostic = kb_execution_api::PostExecutionDiagnostic { + signature: signature.clone(), + canonical_inserted: false, + core_extracted: false, + decode_replayed: false, + materialized: false, + diagnostics: std::vec::Vec::new(), + }; + let send_config = match kb_rpc::SendTransactionConfig::from_execution_config( + &profile.execution, + std::option::Option::Some(summary.latest_blockhash.context.slot), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let sent = match http_pool + .send_transaction_for_role( + request.transaction_role.as_str(), + signed.transaction_base64().as_str(), + &signature, + &send_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic.diagnostics.push(format!("SPL Token submission failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + summary.send_result = std::option::Option::Some( + sent.to_execution_result(kb_execution_api::ExecutionCluster::Devnet), + ); + let confirmation_config = match kb_rpc::ConfirmTransactionConfig::from_execution_config( + &profile.execution, + std::option::Option::Some(summary.latest_blockhash.last_valid_block_height), + std::option::Option::Some(summary.latest_blockhash.context.slot), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let confirmation = match http_pool + .confirm_transaction_for_roles( + request.transaction_role.as_str(), + request.query_role.as_str(), + kb_execution_api::ExecutionCluster::Devnet, + &signature, + &confirmation_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic.diagnostics.push(format!("SPL Token confirmation failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + let confirmation_status = confirmation.status; + summary.confirmation = std::option::Option::Some(confirmation); + if !matches!( + confirmation_status, + kb_execution_api::ExecutionConfirmationStatus::Confirmed + | kb_execution_api::ExecutionConfirmationStatus::Finalized + ) { + diagnostic.diagnostics.push(format!( + "SPL Token post-validation stopped at confirmation status {confirmation_status:?}" + )); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + } + let backfill = match crate::solana_token_execution::hydrate_signature( + http_pool, + store, + profile, + request.query_role.as_str(), + request.post_validation_max_retries, + observer, + &signature, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic.diagnostics.push(format!("SPL Token hydration failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + diagnostic.canonical_inserted = crate::solana_token_execution::canonical_available(&backfill); + summary.backfill = std::option::Option::Some(backfill); + if !diagnostic.canonical_inserted { + diagnostic.diagnostics.push( + "confirmed SPL Token transaction was unavailable for canonical hydration".to_string(), + ); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + } + let extraction = match crate::execute_core_extraction( + store, + &crate::CoreExtractionRequest { + source: crate::CoreExtractionSource::Signatures(std::vec![signature.0.clone()]), + limit: 1, + max_concurrent_extractions: 1, + force_replay: request.force_post_validation_replay, + }, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic + .diagnostics + .push(format!("SPL Token core extraction failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + diagnostic.core_extracted = extraction.failed == 0 + && !extraction.cancelled + && extraction.selected == 1 + && extraction.extracted.saturating_add(extraction.skipped) >= 1; + summary.core_extraction = std::option::Option::Some(extraction); + if !diagnostic.core_extracted { + diagnostic + .diagnostics + .push("SPL Token core extraction did not complete".to_string()); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + } + let first_replay = match crate::solana_token_execution::replay_program( + store, + &signature, + false, + request.force_post_validation_replay, + &[kb_program_ids::SPL_TOKEN_PROGRAM_ID], + decoders, + materializers, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic.diagnostics.push(format!("SPL Token decode replay failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + diagnostic.decode_replayed = crate::solana_token_execution::decode_completed(&first_replay); + summary.decode_replay = std::option::Option::Some(first_replay); + let filter = match kb_store_core::MaterializedEventFilter::new( + std::option::Option::None, + std::option::Option::None, + std::option::Option::Some(signature.0.clone()), + 64, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let rows = + match kb_store_core::DecodePipelineStore::list_materialized_events(store, &filter).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic + .diagnostics + .push(format!("SPL Token materialization query failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + summary.materializations = rows + .into_iter() + .filter(|row| return row.source_decoder_name == "spl_token") + .collect(); + diagnostic.materialized = + !summary.plan.policy.post_execution_validation.materialization_required + || !summary.materializations.is_empty(); + let second_replay = match crate::solana_token_execution::replay_program( + store, + &signature, + true, + request.force_post_validation_replay, + &[kb_program_ids::SPL_TOKEN_PROGRAM_ID], + decoders, + materializers, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + diagnostic + .diagnostics + .push(format!("SPL Token idempotence replay failed: {error}")); + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); + }, + }; + let idempotent = second_replay.failed_inputs == 0 + && second_replay.processors.iter().all(|processor| { + return processor.failed == 0 + && processor.materialized_outputs == 0 + && processor.materialization_refused == 0; + }); + summary.idempotence_replay = std::option::Option::Some(second_replay); + if !idempotent { + diagnostic + .diagnostics + .push("second SPL Token replay did not prove a clean idempotent skip".to_string()); + } else if diagnostic.canonical_inserted + && diagnostic.core_extracted + && diagnostic.decode_replayed + && diagnostic.materialized + { + diagnostic.diagnostics.push( + "SPL Token completed canonical hydration, core extraction, decode, materialization and idempotence validation" + .to_string(), + ); + } + summary.post_execution = std::option::Option::Some(diagnostic); + return std::result::Result::Ok(summary); +} + +async fn prepare_execution( + http_pool: &kb_rpc::HttpEndpointPool, + profile: &kb_config::ProfileConfig, + workspace_root: &std::path::Path, + request: &crate::solana_token_execution::DevnetSplTokenExecutionRequest, + observer: &O, +) -> kb_core::Result +where + O: crate::SolanaExecutionObserver, +{ + if let std::result::Result::Err(error) = request.validate() { + return std::result::Result::Err(error); + } + if let std::result::Result::Err(error) = + crate::solana_token_execution::validate_profile(profile, request) + { + return std::result::Result::Err(error); + } + if let std::result::Result::Err(error) = + crate::solana_execution::ensure_not_cancelled(observer, "token_validate") + { + return std::result::Result::Err(error); + } + let genesis = match http_pool.get_genesis_hash_for_role(request.query_role.as_str()).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if genesis.classified_cluster + != std::option::Option::Some(kb_execution_api::ExecutionCluster::Devnet) + { + return std::result::Result::Err(kb_core::Error::new( + "execution_cluster_mismatch", + format!( + "expected Devnet genesis hash but endpoint returned {} classified as {:?}", + genesis.genesis_hash, genesis.classified_cluster + ), + )); + } + let readiness = match crate::inspect_spl_token_stateful_readiness( + http_pool, + &crate::SplTokenStatefulReadinessRequest { + query_role: request.query_role.clone(), + cluster: kb_execution_api::ExecutionCluster::Devnet, + operation: request.operation.clone(), + }, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if readiness.status != crate::SplTokenStatefulReadinessStatus::Ready { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_stateful_blocked", + crate::solana_token_execution::failed_readiness_message(&readiness), + )); + } + let wallet = match crate::solana_execution::load_profile_wallet(profile, workspace_root).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let wallet_summary = wallet.summary(); + let fee_payer = kb_model::Pubkey(wallet_summary.public_key.clone()); + let balance = match http_pool + .get_balance_for_role( + request.query_role.as_str(), + &fee_payer, + &kb_rpc::GetBalanceConfig::confirmed(), + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if balance.lamports < profile.execution.max_fee_lamports { + return std::result::Result::Err(kb_core::Error::new( + "execution_balance_insufficient", + format!( + "Devnet wallet balance {} is below the configured fee ceiling {}", + balance.lamports, profile.execution.max_fee_lamports + ), + )); + } + let plan = match crate::solana_token_execution::build_plan(profile, request, fee_payer.clone()) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let plan_evaluation = + match kb_execution_safety::ExecutionSafetyChecker.evaluate_prepared_plan(&plan) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if plan_evaluation.decision == kb_execution_safety::ExecutionSafetyDecision::Deny { + return std::result::Result::Err(kb_core::Error::new( + "execution_plan_denied", + crate::solana_execution::violation_message(plan_evaluation.violations.as_slice()), + )); + } + let latest_blockhash = match http_pool + .get_latest_blockhash_for_role( + request.query_role.as_str(), + &kb_rpc::GetLatestBlockhashConfig::confirmed(), + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let unsigned = match kb_execution_solana::build_legacy_transaction( + &plan, + latest_blockhash.blockhash.as_str(), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let fee = match http_pool + .get_fee_for_message_for_role( + request.query_role.as_str(), + unsigned.message_base64().as_str(), + &kb_rpc::GetFeeForMessageConfig::new( + kb_rpc::RpcCommitmentLevel::Confirmed, + std::option::Option::Some(latest_blockhash.context.slot), + ), + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if fee.fee_lamports.is_none() { + return std::result::Result::Err(kb_core::Error::new( + "execution_fee_unavailable", + "getFeeForMessage returned null for the selected recent blockhash", + )); + } + let unsigned_base64 = match unsigned.transaction_base64() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let simulation_config = match kb_rpc::SimulateTransactionConfig::new( + kb_rpc::RpcCommitmentLevel::Confirmed, + false, + false, + std::option::Option::Some(latest_blockhash.context.slot), + true, + std::option::Option::None, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_execution::emit( + observer, + crate::SolanaExecutionProgressLevel::Info, + "spl_token_simulation", + format!("simulating exact SPL Token message {}", unsigned.message_hash()), + std::option::Option::None, + ); + let simulation_rpc = match http_pool + .simulate_transaction_for_role( + request.transaction_role.as_str(), + unsigned_base64.as_str(), + &simulation_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let simulation = simulation_rpc.to_execution_result( + kb_execution_api::ExecutionCluster::Devnet, + kb_execution_api::ExecutionBlockhashKind::Latest, + std::option::Option::Some( + simulation_rpc.context.slot.saturating_sub(latest_blockhash.context.slot), + ), + std::option::Option::None, + std::option::Option::None, + std::option::Option::Some(&fee), + ); + let evidence = unsigned.bind_simulation(simulation.clone()); + let summary = crate::solana_token_execution::DevnetSplTokenExecutionSummary { + profile_name: profile.name.clone(), + cluster: kb_execution_api::ExecutionCluster::Devnet, + genesis_hash: genesis.genesis_hash, + wallet: wallet_summary, + balance_lamports: balance.lamports, + stateful_readiness: readiness, + plan, + latest_blockhash, + fee, + simulation, + send_result: std::option::Option::None, + confirmation: std::option::Option::None, + backfill: std::option::Option::None, + core_extraction: std::option::Option::None, + decode_replay: std::option::Option::None, + idempotence_replay: std::option::Option::None, + materializations: std::vec::Vec::new(), + post_execution: std::option::Option::None, + }; + return std::result::Result::Ok(crate::solana_token_execution::PreparedTokenExecution { + wallet, + unsigned, + evidence, + summary, + }); +} + +fn validate_profile( + profile: &kb_config::ProfileConfig, + request: &crate::solana_token_execution::DevnetSplTokenExecutionRequest, +) -> kb_core::Result<()> { + if profile.wallet.cluster != "devnet" { + return std::result::Result::Err(kb_core::Error::config( + "SPL Token Devnet orchestration requires a Devnet wallet profile", + )); + } + if !profile.wallet.temporary_wallet_enabled || !profile.wallet.temporary_wallet_persist { + return std::result::Result::Err(kb_core::Error::config( + "SPL Token Devnet orchestration requires an enabled persistent temporary wallet", + )); + } + if !profile.execution.require_simulation { + return std::result::Result::Err(kb_core::Error::config( + "SPL Token Devnet orchestration requires simulation", + )); + } + if request.submit && !profile.wallet.devnet_send_enabled { + return std::result::Result::Err(kb_core::Error::config( + "Devnet transaction submission is disabled by the wallet profile", + )); + } + if request.submit + && profile.execution.require_operator_confirmation + && !request.operator_confirmed + { + return std::result::Result::Err(kb_core::Error::config( + "SPL Token Devnet submission requires explicit operator confirmation", + )); + } + return std::result::Result::Ok(()); +} + +fn build_plan( + profile: &kb_config::ProfileConfig, + request: &crate::solana_token_execution::DevnetSplTokenExecutionRequest, + fee_payer: kb_model::Pubkey, +) -> kb_core::Result { + let materialization_required = + crate::solana_token_execution::operation_requires_materialization(&request.operation); + let mut authorized_signers = std::vec![fee_payer.clone()]; + crate::solana_token_execution::collect_operation_signers( + &request.operation, + &mut authorized_signers, + ); + let intent = kb_executor_spl_token::SplTokenExecutionIntent { + intent_id: request.intent_id.clone(), + fee_payer, + policy: kb_execution_api::ExecutionPolicy { + cluster: kb_execution_api::ExecutionClusterPolicy { + expected_cluster: kb_execution_api::ExecutionCluster::Devnet, + allow_mainnet: false, + mainnet_confirmation: false, + }, + simulation: kb_execution_api::ExecutionSimulationPolicy::Required, + blockhash: kb_execution_api::ExecutionBlockhashPolicy { + kind: kb_execution_api::ExecutionBlockhashKind::Latest, + max_age_slots: std::option::Option::Some( + profile.execution.recent_blockhash_max_age_slots, + ), + nonce_account: std::option::Option::None, + nonce_authority: std::option::Option::None, + }, + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some( + profile.execution.devnet_max_spend_lamports, + ), + max_fee_lamports: std::option::Option::Some(profile.execution.max_fee_lamports), + max_compute_unit_price_micro_lamports: std::option::Option::Some( + profile.execution.max_compute_unit_price_micro_lamports, + ), + }, + authorized_signers, + dry_run: !request.submit, + post_execution_validation: kb_execution_api::PostExecutionValidationPolicy { + canonical_insert_required: true, + core_extraction_required: true, + decode_replay_required: true, + materialization_required, + }, + }, + operation: request.operation.clone(), + }; + return kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &kb_executor_spl_token::SplTokenExecutor, + &intent, + ); +} + +fn collect_operation_signers( + operation: &kb_executor_spl_token::SplTokenOperation, + signers: &mut std::vec::Vec, +) { + match operation { + kb_executor_spl_token::SplTokenOperation::Instruction { value } => { + crate::solana_token_execution::collect_single_signers(value, signers); + }, + kb_executor_spl_token::SplTokenOperation::Batch { instructions } => { + for value in instructions { + crate::solana_token_execution::collect_single_signers(value, signers); + } + }, + } +} + +fn collect_single_signers( + operation: &kb_executor_spl_token::SplTokenSingleOperation, + signers: &mut std::vec::Vec, +) { + let authority = match operation { + kb_executor_spl_token::SplTokenSingleOperation::Transfer { authority, .. } + | kb_executor_spl_token::SplTokenSingleOperation::Approve { authority, .. } + | kb_executor_spl_token::SplTokenSingleOperation::Revoke { authority, .. } + | kb_executor_spl_token::SplTokenSingleOperation::MintTo { authority, .. } + | kb_executor_spl_token::SplTokenSingleOperation::Burn { authority, .. } + | kb_executor_spl_token::SplTokenSingleOperation::CloseAccount { authority, .. } + | kb_executor_spl_token::SplTokenSingleOperation::FreezeAccount { authority, .. } + | kb_executor_spl_token::SplTokenSingleOperation::ThawAccount { authority, .. } + | kb_executor_spl_token::SplTokenSingleOperation::TransferChecked { authority, .. } + | kb_executor_spl_token::SplTokenSingleOperation::ApproveChecked { authority, .. } + | kb_executor_spl_token::SplTokenSingleOperation::MintToChecked { authority, .. } + | kb_executor_spl_token::SplTokenSingleOperation::BurnChecked { authority, .. } + | kb_executor_spl_token::SplTokenSingleOperation::WithdrawExcessLamports { + authority, + .. + } + | kb_executor_spl_token::SplTokenSingleOperation::UnwrapLamports { authority, .. } => { + std::option::Option::Some(authority) + }, + kb_executor_spl_token::SplTokenSingleOperation::SetAuthority { + current_authority, .. + } => std::option::Option::Some(current_authority), + kb_executor_spl_token::SplTokenSingleOperation::InitializeMint { .. } + | kb_executor_spl_token::SplTokenSingleOperation::InitializeAccount { .. } + | kb_executor_spl_token::SplTokenSingleOperation::InitializeMultisig { .. } + | kb_executor_spl_token::SplTokenSingleOperation::SyncNative { .. } + | kb_executor_spl_token::SplTokenSingleOperation::GetAccountDataSize { .. } + | kb_executor_spl_token::SplTokenSingleOperation::InitializeImmutableOwner { .. } + | kb_executor_spl_token::SplTokenSingleOperation::AmountToUiAmount { .. } + | kb_executor_spl_token::SplTokenSingleOperation::UiAmountToAmount { .. } => { + std::option::Option::None + }, + }; + if let std::option::Option::Some(authority) = authority { + if authority.multisig_signers.is_empty() { + crate::solana_token_execution::add_signer(signers, &authority.authority); + } else { + for signer in &authority.multisig_signers { + crate::solana_token_execution::add_signer(signers, signer); + } + } + } +} + +fn add_signer(signers: &mut std::vec::Vec, signer: &kb_model::Pubkey) { + if !signers.iter().any(|value| return value == signer) { + signers.push(signer.clone()); + } +} + +fn operation_requires_materialization( + operation: &kb_executor_spl_token::SplTokenOperation, +) -> bool { + return match operation { + kb_executor_spl_token::SplTokenOperation::Instruction { value } => { + crate::solana_token_execution::single_requires_materialization(value) + }, + kb_executor_spl_token::SplTokenOperation::Batch { instructions } => { + instructions.iter().any(|value| { + return crate::solana_token_execution::single_requires_materialization(value); + }) + }, + }; +} + +fn single_requires_materialization( + operation: &kb_executor_spl_token::SplTokenSingleOperation, +) -> bool { + return !matches!( + operation, + kb_executor_spl_token::SplTokenSingleOperation::GetAccountDataSize { .. } + | kb_executor_spl_token::SplTokenSingleOperation::InitializeImmutableOwner { .. } + | kb_executor_spl_token::SplTokenSingleOperation::AmountToUiAmount { .. } + | kb_executor_spl_token::SplTokenSingleOperation::UiAmountToAmount { .. } + ); +} + +fn failed_readiness_message( + readiness: &crate::SplTokenStatefulReadinessReport, +) -> std::string::String { + let failures = readiness + .checks + .iter() + .filter(|check| return !check.passed) + .take(8) + .map(|check| return format!("{}: {}", check.code, check.message)) + .collect::>(); + if failures.is_empty() { + return "SPL Token stateful readiness returned Blocked without diagnostics".to_string(); + } + return failures.join("; "); +} + +fn validate_profile_wallet_signers( + required_signers: &[std::string::String], + wallet_pubkey: &str, +) -> kb_core::Result<()> { + if required_signers.iter().any(|value| return value.as_str() != wallet_pubkey) { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_external_signer_unavailable", + format!( + "the Devnet Token orchestrator can sign only with profile wallet {}; required signers are {}", + wallet_pubkey, + required_signers.join(",") + ), + )); + } + return std::result::Result::Ok(()); +} + +pub(crate) async fn hydrate_signature( + http_pool: &kb_rpc::HttpEndpointPool, + store: &S, + profile: &kb_config::ProfileConfig, + query_role: &str, + post_validation_max_retries: u32, + observer: &O, + signature: &kb_model::Signature, +) -> kb_core::Result +where + S: kb_store_core::RawTransactionStore + Sync, + O: crate::SolanaExecutionObserver, +{ + let mut retry = 0_u32; + loop { + let result = match crate::execute_http_backfill( + http_pool, + store, + &crate::BackfillRequest { + role: query_role.to_string(), + commitment: "confirmed".to_string(), + source: crate::BackfillSource::ExplicitSignatures(std::vec![signature.0.clone()]), + page_size: 1, + max_pages: 1, + max_concurrent_requests: 1, + max_retries: 2, + }, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if crate::solana_token_execution::canonical_available(&result) + || retry >= post_validation_max_retries + || observer.is_execution_cancelled() + { + return std::result::Result::Ok(result); + } + retry = retry.saturating_add(1); + tokio::time::sleep(std::time::Duration::from_millis(std::cmp::max( + profile.execution.confirmation_poll_interval_ms, + 500, + ))) + .await; + } +} + +pub(crate) fn canonical_available(summary: &crate::BackfillSummary) -> bool { + return summary.failed == 0 + && summary.missing == 0 + && summary.candidates_completed == 1 + && summary.candidates_cancelled == 0 + && summary.candidates_not_started == 0 + && summary + .canonical_inserted + .saturating_add(summary.canonical_skipped) + .saturating_add(summary.existing_skipped) + >= 1; +} + +pub(crate) async fn replay_program( + store: &S, + signature: &kb_model::Signature, + include_materialized_state: bool, + force_post_validation_replay: bool, + program_ids: &[&str], + decoders: &[std::sync::Arc], + materializers: &[std::sync::Arc], + observer: &O, +) -> kb_core::Result +where + S: kb_store_core::DecodePipelineStore + Sync, + O: crate::SolanaExecutionObserver, +{ + let mut states = std::vec![ + kb_store_core::CoreInstructionProcessingState::Pending, + kb_store_core::CoreInstructionProcessingState::Failed, + kb_store_core::CoreInstructionProcessingState::ReplayRequested, + ]; + if include_materialized_state { + states.push(kb_store_core::CoreInstructionProcessingState::Materialized); + } + let selection = match kb_store_core::DecodeSelectionFilter::new( + std::vec![signature.0.clone()], + states, + std::option::Option::None, + std::option::Option::None, + program_ids.iter().map(|value| return (*value).to_string()).collect(), + std::vec::Vec::new(), + false, + 64, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::execute_decode_replay( + store, + &crate::DecodeReplayRequest { + campaign_id: crate::new_decode_campaign_id(), + selection, + decoder_names: std::vec::Vec::new(), + dispatch_policy: crate::DecodeDispatchPolicy::HighestPriority, + max_concurrent_inputs: 1, + force_replay: if include_materialized_state { + false + } else { + force_post_validation_replay + }, + force_replay_all_matching: false, + materialize_after_decode: true, + }, + decoders, + materializers, + observer, + ) + .await; +} + +pub(crate) fn decode_completed(summary: &crate::DecodeReplaySummary) -> bool { + return summary.failed_inputs == 0 + && summary.unmatched == 0 + && !summary.cancelled + && summary.completed >= 1 + && summary.processors.iter().all(|processor| { + return processor.failed == 0 + && processor.unsupported == 0 + && processor.materialization_refused == 0; + }) + && summary.processors.iter().map(|processor| return processor.decoded).sum::() >= 1; +} + +#[cfg(test)] +mod tests { + fn local_devnet_profile() -> kb_config::ProfileConfig { + let config = + match kb_config::parse_config_json(include_str!("../../config/example.config.json")) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("example config parse failed: {error}"), + }; + for profile in config.profiles { + if profile.name == "local_devnet" { + return profile; + } + } + panic!("local_devnet profile missing"); + } + + fn checked_transfer(fee_authority: &str) -> kb_executor_spl_token::SplTokenOperation { + return kb_executor_spl_token::SplTokenOperation::Instruction { + value: kb_executor_spl_token::SplTokenSingleOperation::TransferChecked { + source: kb_model::Pubkey(kb_program_ids::VOTE_PROGRAM_ID.to_string()), + mint: kb_model::Pubkey(kb_program_ids::STAKE_PROGRAM_ID.to_string()), + destination: kb_model::Pubkey(kb_program_ids::CONFIG_PROGRAM_ID.to_string()), + authority: kb_executor_spl_token::SplTokenAuthority { + authority: kb_model::Pubkey(fee_authority.to_string()), + multisig_signers: std::vec::Vec::new(), + }, + amount: kb_executor_spl_token::SplTokenAmount("1".to_string()), + decimals: 9, + }, + }; + } + + #[test] + fn request_is_simulation_only_and_bounded_by_default() { + let request = crate::DevnetSplTokenExecutionRequest::new( + "token-1", + crate::solana_token_execution::tests::checked_transfer( + kb_program_ids::SYSTEM_PROGRAM_ID, + ), + ); + assert!(!request.submit); + assert!(request.validate().is_ok()); + let mut invalid = request; + invalid.post_validation_max_retries = 21; + assert!(invalid.validate().is_err()); + } + + #[test] + fn checked_transfer_plan_is_simulation_first_and_authorizes_exact_signers() { + let profile = crate::solana_token_execution::tests::local_devnet_profile(); + let fee_payer = kb_model::Pubkey(kb_program_ids::SYSTEM_PROGRAM_ID.to_string()); + let authority = kb_program_ids::VOTE_PROGRAM_ID; + let request = crate::DevnetSplTokenExecutionRequest::new( + "token-2", + crate::solana_token_execution::tests::checked_transfer(authority), + ); + let plan = match crate::solana_token_execution::build_plan( + &profile, + &request, + fee_payer.clone(), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Token plan failed: {error}"), + }; + assert!(plan.policy.dry_run); + assert_eq!(plan.fee_payer, fee_payer); + assert!(plan.policy.authorized_signers.iter().any(|value| return value.0 == authority)); + assert!(plan.policy.post_execution_validation.materialization_required); + assert_eq!(plan.instructions.len(), 1); + } + + #[test] + fn conversion_plan_does_not_invent_materialization_requirement() { + let profile = crate::solana_token_execution::tests::local_devnet_profile(); + let request = crate::DevnetSplTokenExecutionRequest::new( + "token-3", + kb_executor_spl_token::SplTokenOperation::Instruction { + value: kb_executor_spl_token::SplTokenSingleOperation::AmountToUiAmount { + mint: kb_model::Pubkey(kb_program_ids::STAKE_PROGRAM_ID.to_string()), + amount: kb_executor_spl_token::SplTokenAmount("1".to_string()), + }, + }, + ); + let plan = match crate::solana_token_execution::build_plan( + &profile, + &request, + kb_model::Pubkey(kb_program_ids::SYSTEM_PROGRAM_ID.to_string()), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("conversion plan failed: {error}"), + }; + assert!(!plan.policy.post_execution_validation.materialization_required); + } + + #[test] + fn submission_signers_must_all_resolve_to_the_profile_wallet() { + let wallet = kb_program_ids::SYSTEM_PROGRAM_ID.to_string(); + assert!( + super::validate_profile_wallet_signers(std::slice::from_ref(&wallet), wallet.as_str(),) + .is_ok() + ); + let external = kb_program_ids::VOTE_PROGRAM_ID.to_string(); + let error = + super::validate_profile_wallet_signers(&[wallet.clone(), external], wallet.as_str()); + assert!(error.is_err()); + } + + #[tokio::test] + async fn optional_devnet_checked_transfer_simulation_and_submission_from_env() { + if std::env::var("KB_DEVNET_SPL_TOKEN_EXECUTION_TEST").ok().as_deref() + != std::option::Option::Some("1") + { + return; + } + let source = + crate::solana_token_execution::tests::required_pubkey_env("KB_DEVNET_SPL_TOKEN_SOURCE"); + let mint = + crate::solana_token_execution::tests::required_pubkey_env("KB_DEVNET_SPL_TOKEN_MINT"); + let destination = crate::solana_token_execution::tests::required_pubkey_env( + "KB_DEVNET_SPL_TOKEN_DESTINATION", + ); + let authority = crate::solana_token_execution::tests::required_pubkey_env( + "KB_DEVNET_SPL_TOKEN_AUTHORITY", + ); + let decimals = match std::env::var("KB_DEVNET_SPL_TOKEN_DECIMALS") { + std::result::Result::Ok(value) => match value.parse::() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("invalid Token decimals: {error}"), + }, + std::result::Result::Err(_) => 9, + }; + let amount = match std::env::var("KB_DEVNET_SPL_TOKEN_AMOUNT") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => "1".to_string(), + }; + let mut profile = crate::solana_token_execution::tests::local_devnet_profile(); + if let std::result::Result::Ok(directory) = std::env::var("KB_DEVNET_WALLET_DIR") { + profile.wallet.wallet_dir = directory; + } + let pool = match kb_rpc::HttpEndpointPool::from_profile(&profile) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("HTTP pool creation failed: {error}"), + }; + let workspace_root = match std::path::Path::new(env!("CARGO_MANIFEST_DIR")).parent() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("workspace root cannot be resolved"), + }; + let mut request = crate::DevnetSplTokenExecutionRequest::new( + format!("devnet-token-test-{}", uuid::Uuid::new_v4()), + kb_executor_spl_token::SplTokenOperation::Instruction { + value: kb_executor_spl_token::SplTokenSingleOperation::TransferChecked { + source, + mint, + destination, + authority: kb_executor_spl_token::SplTokenAuthority { + authority, + multisig_signers: std::vec::Vec::new(), + }, + amount: kb_executor_spl_token::SplTokenAmount(amount), + decimals, + }, + }, + ); + if std::env::var("KB_DEVNET_SPL_TOKEN_SUBMIT").ok().as_deref() + != std::option::Option::Some("1") + { + let summary = match crate::simulate_devnet_spl_token( + &pool, + &profile, + workspace_root, + &request, + &crate::NoopSolanaExecutionObserver, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("Devnet Token simulation failed: {error}"); + }, + }; + assert_eq!( + summary.stateful_readiness.status, + crate::SplTokenStatefulReadinessStatus::Ready + ); + assert!(summary.simulation.success, "{:#?}", summary.simulation); + return; + } + request.submit = true; + request.operator_confirmed = true; + request.post_validation_max_retries = 20; + let database_url = match std::env::var("KB_POSTGRES_TEST_URL") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("KB_POSTGRES_TEST_URL is required for submission: {error}"); + }, + }; + profile.database.backend = "postgres".to_string(); + profile.database.postgres.url = database_url; + let store = match kb_store_pg::PostgresStore::connect_from_profile_config(&profile).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("PostgreSQL connection failed: {error}"), + }; + if let std::result::Result::Err(error) = store.initialize_store_schema().await { + panic!("PostgreSQL schema initialization failed: {error}"); + } + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(kb_decoder_spl_token::SplTokenDecoder)]; + let materializers: std::vec::Vec< + std::sync::Arc, + > = std::vec![std::sync::Arc::new( + kb_materializer_token_accounts::TokenAccountsMaterializer, + )]; + let summary = match crate::execute_devnet_spl_token( + &pool, + &store, + &profile, + workspace_root, + &request, + decoders.as_slice(), + materializers.as_slice(), + &crate::NoopSolanaExecutionObserver, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Devnet Token execution failed: {error}"), + }; + assert!(summary.simulation.success, "{:#?}", summary.simulation); + let send_result = match summary.send_result.as_ref() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("Token submission result missing"), + }; + assert!(send_result.submitted); + let confirmation = match summary.confirmation.as_ref() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("Token confirmation result missing"), + }; + assert!(matches!( + confirmation.status, + kb_execution_api::ExecutionConfirmationStatus::Confirmed + | kb_execution_api::ExecutionConfirmationStatus::Finalized + )); + let first_replay = match summary.decode_replay.as_ref() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("Token first decode replay missing"), + }; + assert_eq!(first_replay.failed_inputs, 0); + assert!(first_replay.processors.iter().all(|processor| { + return processor.failed == 0 && processor.materialization_refused == 0; + })); + let idempotence_replay = match summary.idempotence_replay.as_ref() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("Token idempotence replay missing"), + }; + assert_eq!(idempotence_replay.failed_inputs, 0); + assert!(idempotence_replay.processors.iter().all(|processor| { + return processor.failed == 0 + && processor.materialized_outputs == 0 + && processor.materialization_refused == 0; + })); + assert!(!summary.materializations.is_empty()); + let diagnostic = match summary.post_execution.as_ref() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("Token post-execution diagnostic missing"), + }; + assert!(diagnostic.canonical_inserted); + assert!(diagnostic.core_extracted); + assert!(diagnostic.decode_replayed); + assert!(diagnostic.materialized); + println!( + "SPL Token Devnet signature={} status={:?} materializations={} idempotent=true", + send_result.signature.0, + confirmation.status, + summary.materializations.len(), + ); + } + + #[tokio::test] + async fn optional_devnet_recent_instruction_probe_from_env() { + if std::env::var("KB_DEVNET_SPL_TOKEN_RECENT_PROBE_TEST").ok().as_deref() + != std::option::Option::Some("1") + { + return; + } + let source = + crate::solana_token_execution::tests::required_pubkey_env("KB_DEVNET_SPL_TOKEN_SOURCE"); + let mint = + crate::solana_token_execution::tests::required_pubkey_env("KB_DEVNET_SPL_TOKEN_MINT"); + let destination = crate::solana_token_execution::tests::required_pubkey_env( + "KB_DEVNET_SPL_TOKEN_DESTINATION", + ); + let authority = crate::solana_token_execution::tests::required_pubkey_env( + "KB_DEVNET_SPL_TOKEN_AUTHORITY", + ); + let native_account = crate::solana_token_execution::tests::required_pubkey_env( + "KB_DEVNET_SPL_TOKEN_NATIVE_ACCOUNT", + ); + let decimals = match std::env::var("KB_DEVNET_SPL_TOKEN_DECIMALS") { + std::result::Result::Ok(value) => match value.parse::() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("invalid Token decimals: {error}"), + }, + std::result::Result::Err(_) => 9, + }; + let mut profile = crate::solana_token_execution::tests::local_devnet_profile(); + if let std::result::Result::Ok(directory) = std::env::var("KB_DEVNET_WALLET_DIR") { + profile.wallet.wallet_dir = directory; + } + let pool = match kb_rpc::HttpEndpointPool::from_profile(&profile) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("HTTP pool creation failed: {error}"), + }; + let workspace_root = match std::path::Path::new(env!("CARGO_MANIFEST_DIR")).parent() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("workspace root cannot be resolved"), + }; + let token_authority = kb_executor_spl_token::SplTokenAuthority { + authority: authority.clone(), + multisig_signers: std::vec::Vec::new(), + }; + let batch_request = crate::DevnetSplTokenExecutionRequest::new( + format!("devnet-token-batch-probe-{}", uuid::Uuid::new_v4()), + kb_executor_spl_token::SplTokenOperation::Batch { + instructions: std::vec![ + kb_executor_spl_token::SplTokenSingleOperation::TransferChecked { + source, + mint, + destination, + authority: token_authority.clone(), + amount: kb_executor_spl_token::SplTokenAmount("0".to_string()), + decimals, + }, + ], + }, + ); + let batch = match crate::simulate_devnet_spl_token( + &pool, + &profile, + workspace_root, + &batch_request, + &crate::NoopSolanaExecutionObserver, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Devnet Batch probe failed: {error}"), + }; + assert!(batch.simulation.simulated); + crate::solana_token_execution::tests::print_recent_probe("batch", &batch.simulation); + + let unwrap_request = crate::DevnetSplTokenExecutionRequest::new( + format!("devnet-token-unwrap-probe-{}", uuid::Uuid::new_v4()), + kb_executor_spl_token::SplTokenOperation::Instruction { + value: kb_executor_spl_token::SplTokenSingleOperation::UnwrapLamports { + account: native_account, + destination: authority, + authority: token_authority, + amount_lamports: std::option::Option::Some( + kb_executor_spl_token::SplTokenAmount("1".to_string()), + ), + }, + }, + ); + let unwrap = match crate::simulate_devnet_spl_token( + &pool, + &profile, + workspace_root, + &unwrap_request, + &crate::NoopSolanaExecutionObserver, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("Devnet UnwrapLamports probe failed: {error}") + }, + }; + assert!(unwrap.simulation.simulated); + crate::solana_token_execution::tests::print_recent_probe( + "unwrap_lamports", + &unwrap.simulation, + ); + } + + fn print_recent_probe( + operation: &str, + simulation: &kb_execution_api::ExecutionSimulationResult, + ) { + println!( + "SPL Token recent Devnet probe operation={operation} success={} error={:?} units={:?} logs={:?}", + simulation.success, simulation.error, simulation.units_consumed, simulation.logs, + ); + } + + fn required_pubkey_env(name: &str) -> kb_model::Pubkey { + return match std::env::var(name) { + std::result::Result::Ok(value) => kb_model::Pubkey(value), + std::result::Result::Err(error) => panic!("{name} is required: {error}"), + }; + } +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_lifecycle.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_lifecycle.rs new file mode 100644 index 0000000..2b02aef --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_lifecycle.rs @@ -0,0 +1,1801 @@ +// file: kb_pipeline/src/solana_token_lifecycle.rs +// version: 4 + +//! Controlled Devnet lifecycle for freshly prepared classic SPL Token accounts. + +const MINT_ACCOUNT_SPACE: u64 = 82; +const TOKEN_ACCOUNT_SPACE: u64 = 165; + +/// Explicit request to prepare the three raw accounts used by one Devnet lifecycle. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DevnetSplTokenLifecyclePreparationRequest { + /// Stable caller-provided preparation identifier. + pub intent_id: std::string::String, + /// Endpoint role used for cluster, rent, blockhash and account reads. + pub query_role: std::string::String, + /// Endpoint role used for simulation, submission and confirmation. + pub transaction_role: std::string::String, + /// Explicitly authorizes three rent-bearing System account creations. + pub submit: bool, + /// Explicit operator confirmation for the preparation spend. + pub operator_confirmed: bool, +} + +impl crate::solana_token_lifecycle::DevnetSplTokenLifecyclePreparationRequest { + /// Creates a conservative preparation request that cannot submit by default. + pub fn new(intent_id: impl std::convert::Into) -> Self { + return Self { + intent_id: intent_id.into(), + query_role: "http_queries".to_string(), + transaction_role: "http_transactions".to_string(), + submit: false, + operator_confirmed: false, + }; + } + + fn validate(&self) -> kb_core::Result<()> { + if self.intent_id.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Devnet SPL Token lifecycle preparation intent id must not be empty", + )); + } + if self.query_role.trim().is_empty() || self.transaction_role.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Devnet SPL Token lifecycle preparation endpoint roles must not be empty", + )); + } + if !self.submit || !self.operator_confirmed { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_preparation_authorization_required", + "raw account preparation requires submit=true and explicit operator confirmation", + )); + } + return std::result::Result::Ok(()); + } +} + +/// Result retained for one confirmed raw System account creation. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DevnetSplTokenLifecyclePreparationStep { + /// Stable account role: `mint`, `source` or `destination`. + pub account_role: std::string::String, + /// Newly generated account public key. + pub account: kb_model::Pubkey, + /// Exact allocated data length. + pub space: u64, + /// Exact rent-exempt lamports funded into the account. + pub rent_lamports: u64, + /// Confirmed System account-creation transaction signature. + pub signature: kb_model::Signature, + /// Terminal confirmation status. + pub confirmation_status: kb_execution_api::ExecutionConfirmationStatus, +} + +/// Fresh raw accounts and identities ready for the controlled Token lifecycle. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DevnetSplTokenLifecyclePreparationSummary { + /// Profile wallet that will own and administer the lifecycle accounts. + pub authority: kb_model::Pubkey, + /// Fresh uninitialized 82-byte Token-owned mint account. + pub mint: kb_model::Pubkey, + /// Fresh uninitialized 165-byte Token-owned source account. + pub source: kb_model::Pubkey, + /// Fresh uninitialized 165-byte Token-owned destination account. + pub destination: kb_model::Pubkey, + /// Fresh off-chain delegate identity used only by approve/revoke. + pub delegate: kb_model::Pubkey, + /// Ordered confirmed account-creation results. + pub steps: std::vec::Vec, +} + +/// Request for one destructive, explicitly authorized SPL Token Devnet lifecycle. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DevnetSplTokenLifecycleRequest { + /// Stable caller-provided lifecycle identifier. + pub intent_id: std::string::String, + /// Endpoint role used for state reads and canonical hydration. + pub query_role: std::string::String, + /// Endpoint role used for simulation, submission and confirmation. + pub transaction_role: std::string::String, + /// Pre-created, rent-exempt, uninitialized 82-byte mint account. + pub mint: kb_model::Pubkey, + /// Pre-created, rent-exempt, uninitialized 165-byte source token account. + pub source: kb_model::Pubkey, + /// Pre-created, rent-exempt, uninitialized 165-byte destination token account. + pub destination: kb_model::Pubkey, + /// Delegate recorded by the approval and removed by the following revoke. + pub delegate: kb_model::Pubkey, + /// Mint authority and owner of both token accounts. + pub authority: kb_model::Pubkey, + /// Exact decimals used by all checked instructions. + pub decimals: u8, + /// Exact raw amount minted into the source account. + pub mint_amount: kb_executor_spl_token::SplTokenAmount, + /// Exact raw amount transferred from source to destination. + pub transfer_amount: kb_executor_spl_token::SplTokenAmount, + /// Exact raw allowance approved on the destination account. + pub approve_amount: kb_executor_spl_token::SplTokenAmount, + /// Explicitly authorizes every submitted lifecycle transaction. + pub submit: bool, + /// Explicit operator confirmation applied to every lifecycle transaction. + pub operator_confirmed: bool, + /// Number of canonical hydration retries after each confirmation. + pub post_validation_max_retries: u32, + /// First zero-based lifecycle step to execute. + pub first_step_index: u8, + /// Confirmed signature of the immediately preceding step when resuming. + pub resume_predecessor_signature: std::option::Option, + /// Bounded pause inserted between recovered or newly executed steps. + pub inter_step_delay_ms: u64, +} + +impl crate::solana_token_lifecycle::DevnetSplTokenLifecycleRequest { + /// Creates a lifecycle request that remains non-submittable until explicitly authorized. + pub fn new( + intent_id: impl std::convert::Into, + mint: kb_model::Pubkey, + source: kb_model::Pubkey, + destination: kb_model::Pubkey, + delegate: kb_model::Pubkey, + authority: kb_model::Pubkey, + ) -> Self { + return Self { + intent_id: intent_id.into(), + query_role: "http_queries".to_string(), + transaction_role: "http_transactions".to_string(), + mint, + source, + destination, + delegate, + authority, + decimals: 9, + mint_amount: kb_executor_spl_token::SplTokenAmount("10".to_string()), + transfer_amount: kb_executor_spl_token::SplTokenAmount("4".to_string()), + approve_amount: kb_executor_spl_token::SplTokenAmount("2".to_string()), + submit: false, + operator_confirmed: false, + post_validation_max_retries: 10, + first_step_index: 0, + resume_predecessor_signature: std::option::Option::None, + inter_step_delay_ms: 2_000, + }; + } + + /// Validates lifecycle-local bounds and amount conservation. + pub fn validate(&self) -> kb_core::Result<()> { + if self.intent_id.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Devnet SPL Token lifecycle intent id must not be empty", + )); + } + if self.query_role.trim().is_empty() || self.transaction_role.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "Devnet SPL Token lifecycle endpoint roles must not be empty", + )); + } + if self.post_validation_max_retries > 20 { + return std::result::Result::Err(kb_core::Error::config( + "lifecycle post-execution retries must not exceed 20", + )); + } + if self.first_step_index > 10 { + return std::result::Result::Err(kb_core::Error::config( + "lifecycle first step index must be between 0 and 10", + )); + } + if self.inter_step_delay_ms > 30_000 { + return std::result::Result::Err(kb_core::Error::config( + "lifecycle inter-step delay must not exceed 30000 milliseconds", + )); + } + if (self.first_step_index == 0 && self.resume_predecessor_signature.is_some()) + || (self.first_step_index > 0 && self.resume_predecessor_signature.is_none()) + { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_resume_contract_invalid", + "a resumed lifecycle requires exactly the confirmed predecessor signature", + )); + } + if let std::option::Option::Some(signature) = &self.resume_predecessor_signature { + if let std::result::Result::Err(error) = kb_rpc::validate_transaction_signature_text( + signature.0.as_str(), + "lifecycle predecessor signature", + ) { + return std::result::Result::Err(error); + } + } + if self.mint == self.source + || self.mint == self.destination + || self.source == self.destination + { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_accounts_not_distinct", + "lifecycle mint, source and destination accounts must be distinct", + )); + } + if self.delegate == self.authority { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_delegate_is_authority", + "lifecycle delegate must differ from the token-account authority", + )); + } + let mint_amount = + match crate::solana_token_lifecycle::canonical_amount(&self.mint_amount, "mint_amount") + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let transfer_amount = match crate::solana_token_lifecycle::canonical_amount( + &self.transfer_amount, + "transfer_amount", + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let approve_amount = match crate::solana_token_lifecycle::canonical_amount( + &self.approve_amount, + "approve_amount", + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if mint_amount == 0 + || transfer_amount == 0 + || transfer_amount >= mint_amount + || approve_amount == 0 + || approve_amount > transfer_amount + { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_amounts_invalid", + "lifecycle requires mint > transfer > 0 and 0 < approval <= transfer", + )); + } + return std::result::Result::Ok(()); + } +} + +/// Result retained for one fully post-validated lifecycle transaction. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DevnetSplTokenLifecycleStepSummary { + /// Zero-based position in the controlled lifecycle. + pub index: u8, + /// Stable SPL Token operation code. + pub operation_code: std::string::String, + /// Confirmed transaction signature. + pub signature: kb_model::Signature, + /// Terminal confirmation status. + pub confirmation_status: kb_execution_api::ExecutionConfirmationStatus, + /// Number of exact SPL Token materializations returned for the signature. + pub materialization_count: u32, + /// Whether the second replay produced no failure, refusal or new output. + pub idempotence_validated: bool, +} + +/// Complete result of the eleven-step controlled lifecycle. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct DevnetSplTokenLifecycleSummary { + /// Stable lifecycle identifier. + pub intent_id: std::string::String, + /// Mint initialized and exercised by the lifecycle. + pub mint: kb_model::Pubkey, + /// Source token account closed by the lifecycle. + pub source: kb_model::Pubkey, + /// Destination token account closed by the lifecycle. + pub destination: kb_model::Pubkey, + /// Ordered, fully validated lifecycle transactions. + pub steps: std::vec::Vec, +} + +/// Creates and verifies the three raw Token-owned accounts required by one lifecycle. +pub async fn prepare_devnet_spl_token_lifecycle_accounts( + http_pool: &kb_rpc::HttpEndpointPool, + profile: &kb_config::ProfileConfig, + workspace_root: &std::path::Path, + request: &crate::solana_token_lifecycle::DevnetSplTokenLifecyclePreparationRequest, + observer: &O, +) -> kb_core::Result +where + O: crate::SolanaExecutionObserver, +{ + if let std::result::Result::Err(error) = request.validate() { + return std::result::Result::Err(error); + } + if profile.wallet.cluster != "devnet" + || !profile.wallet.devnet_send_enabled + || !profile.execution.require_simulation + { + return std::result::Result::Err(kb_core::Error::config( + "lifecycle account preparation requires a simulation-enabled Devnet send profile", + )); + } + if profile.execution.require_operator_confirmation && !request.operator_confirmed { + return std::result::Result::Err(kb_core::Error::config( + "lifecycle account preparation requires explicit operator confirmation", + )); + } + let genesis = match http_pool.get_genesis_hash_for_role(request.query_role.as_str()).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if genesis.classified_cluster + != std::option::Option::Some(kb_execution_api::ExecutionCluster::Devnet) + { + return std::result::Result::Err(kb_core::Error::new( + "execution_cluster_mismatch", + "lifecycle account preparation endpoint is not Devnet", + )); + } + let payer = match crate::solana_execution::load_profile_wallet(profile, workspace_root).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority = kb_model::Pubkey(payer.public_key()); + let mint_wallet = match crate::solana_token_lifecycle::generated_wallet("token-lifecycle-mint") + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let source_wallet = + match crate::solana_token_lifecycle::generated_wallet("token-lifecycle-source") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let destination_wallet = + match crate::solana_token_lifecycle::generated_wallet("token-lifecycle-destination") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let delegate_wallet = + match crate::solana_token_lifecycle::generated_wallet("token-lifecycle-delegate") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let rent_config = kb_rpc::GetMinimumBalanceForRentExemptionConfig::confirmed(); + let mint_rent = match http_pool + .get_minimum_balance_for_rent_exemption_for_role( + request.query_role.as_str(), + MINT_ACCOUNT_SPACE, + &rent_config, + ) + .await + { + std::result::Result::Ok(value) => value.minimum_balance_lamports, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let token_rent = match http_pool + .get_minimum_balance_for_rent_exemption_for_role( + request.query_role.as_str(), + TOKEN_ACCOUNT_SPACE, + &rent_config, + ) + .await + { + std::result::Result::Ok(value) => value.minimum_balance_lamports, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let doubled_token_rent = token_rent.checked_mul(2); + let tripled_fee_ceiling = profile.execution.max_fee_lamports.checked_mul(3); + let required_balance = match doubled_token_rent + .and_then(|value| return mint_rent.checked_add(value)) + .and_then(|value| { + return tripled_fee_ceiling.and_then(|fees| return value.checked_add(fees)); + }) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_preparation_cost_overflow", + "lifecycle preparation rent and fee ceiling overflow u64", + )); + }, + }; + let balance = match http_pool + .get_balance_for_role( + request.query_role.as_str(), + &authority, + &kb_rpc::GetBalanceConfig::confirmed(), + ) + .await + { + std::result::Result::Ok(value) => value.lamports, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if balance < required_balance { + return std::result::Result::Err(kb_core::Error::new( + "execution_balance_insufficient", + format!( + "lifecycle preparation requires at most {required_balance} lamports but wallet has {balance}" + ), + )); + } + let specifications = [ + ("mint", &mint_wallet, MINT_ACCOUNT_SPACE, mint_rent), + ("source", &source_wallet, TOKEN_ACCOUNT_SPACE, token_rent), + ("destination", &destination_wallet, TOKEN_ACCOUNT_SPACE, token_rent), + ]; + let mut steps = std::vec::Vec::with_capacity(specifications.len()); + for (account_role, wallet, space, rent_lamports) in specifications { + let step = match crate::solana_token_lifecycle::create_raw_token_account( + http_pool, + profile, + request, + observer, + &payer, + wallet, + account_role, + space, + rent_lamports, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + steps.push(step); + } + return std::result::Result::Ok( + crate::solana_token_lifecycle::DevnetSplTokenLifecyclePreparationSummary { + authority, + mint: kb_model::Pubkey(mint_wallet.public_key()), + source: kb_model::Pubkey(source_wallet.public_key()), + destination: kb_model::Pubkey(destination_wallet.public_key()), + delegate: kb_model::Pubkey(delegate_wallet.public_key()), + steps, + }, + ); +} + +/// Executes initialize, mint, transfer, approve/revoke, burn and close on Devnet. +#[allow(clippy::too_many_arguments)] +pub async fn execute_devnet_spl_token_lifecycle( + http_pool: &kb_rpc::HttpEndpointPool, + store: &S, + profile: &kb_config::ProfileConfig, + workspace_root: &std::path::Path, + request: &crate::solana_token_lifecycle::DevnetSplTokenLifecycleRequest, + decoders: &[std::sync::Arc], + materializers: &[std::sync::Arc], + observer: &O, +) -> kb_core::Result +where + S: kb_store_core::RawTransactionStore + + kb_store_core::CoreExtractionStore + + kb_store_core::DecodePipelineStore + + Sync, + O: crate::SolanaExecutionObserver, +{ + if let std::result::Result::Err(error) = request.validate() { + return std::result::Result::Err(error); + } + if !request.submit || !request.operator_confirmed { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_authorization_required", + "the destructive lifecycle requires submit=true and explicit operator confirmation", + )); + } + let profile_wallet = + match crate::solana_execution::load_profile_wallet(profile, workspace_root).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if profile_wallet.public_key() != request.authority.0 { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_authority_wallet_mismatch", + "lifecycle authority must equal the selected profile wallet before the first mutation", + )); + } + let operations = match crate::solana_token_lifecycle::lifecycle_operations(request) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut steps = std::vec::Vec::with_capacity(operations.len()); + if request.first_step_index > 0 { + let predecessor_index = usize::from(request.first_step_index.saturating_sub(1)); + let predecessor_operation = match operations.get(predecessor_index) { + std::option::Option::Some(value) => value.clone(), + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_resume_index_missing", + "lifecycle predecessor operation is unavailable", + )); + }, + }; + let predecessor_signature = match request.resume_predecessor_signature.as_ref() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_resume_signature_missing", + "lifecycle predecessor signature is required for recovery", + )); + }, + }; + let recovered = match crate::solana_token_lifecycle::recover_predecessor_step( + http_pool, + store, + profile, + request, + predecessor_index, + predecessor_operation, + predecessor_signature, + decoders, + materializers, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_token_lifecycle::emit_completed_step(observer, &recovered, true); + steps.push(recovered); + } + for (index, operation) in + operations.into_iter().enumerate().skip(usize::from(request.first_step_index)) + { + if !steps.is_empty() && request.inter_step_delay_ms > 0 { + tokio::time::sleep(std::time::Duration::from_millis(request.inter_step_delay_ms)).await; + } + let operation_code = operation.operation_code().to_string(); + let mut step_request = crate::DevnetSplTokenExecutionRequest::new( + format!("{}-{index:02}-{}", request.intent_id, operation_code), + operation, + ); + step_request.query_role = request.query_role.clone(); + step_request.transaction_role = request.transaction_role.clone(); + step_request.submit = true; + step_request.operator_confirmed = true; + step_request.post_validation_max_retries = request.post_validation_max_retries; + let summary = match crate::execute_devnet_spl_token( + http_pool, + store, + profile, + workspace_root, + &step_request, + decoders, + materializers, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_step_failed", + format!("lifecycle step {index} {operation_code} failed: {error}"), + )); + }, + }; + let step = match crate::solana_token_lifecycle::validated_step( + index, + operation_code.as_str(), + &summary, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_token_lifecycle::emit_completed_step(observer, &step, false); + steps.push(step); + } + return std::result::Result::Ok( + crate::solana_token_lifecycle::DevnetSplTokenLifecycleSummary { + intent_id: request.intent_id.clone(), + mint: request.mint.clone(), + source: request.source.clone(), + destination: request.destination.clone(), + steps, + }, + ); +} + +#[allow(clippy::too_many_arguments)] +async fn recover_predecessor_step( + http_pool: &kb_rpc::HttpEndpointPool, + store: &S, + profile: &kb_config::ProfileConfig, + lifecycle_request: &crate::solana_token_lifecycle::DevnetSplTokenLifecycleRequest, + index: usize, + operation: kb_executor_spl_token::SplTokenOperation, + signature: &kb_model::Signature, + decoders: &[std::sync::Arc], + materializers: &[std::sync::Arc], + observer: &O, +) -> kb_core::Result +where + S: kb_store_core::RawTransactionStore + + kb_store_core::CoreExtractionStore + + kb_store_core::DecodePipelineStore + + Sync, + O: crate::SolanaExecutionObserver, +{ + let operation_code = operation.operation_code().to_string(); + let expected_operation = + crate::solana_token_lifecycle::materialized_operation_code(&operation).to_string(); + let mut request = crate::DevnetSplTokenExecutionRequest::new( + format!("{}-recover-{index:02}", lifecycle_request.intent_id), + operation, + ); + request.query_role = lifecycle_request.query_role.clone(); + request.transaction_role = lifecycle_request.transaction_role.clone(); + request.post_validation_max_retries = lifecycle_request.post_validation_max_retries; + let backfill = match crate::solana_token_execution::hydrate_signature( + http_pool, + store, + profile, + request.query_role.as_str(), + request.post_validation_max_retries, + observer, + signature, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if !crate::solana_token_execution::canonical_available(&backfill) { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_resume_canonical_missing", + "confirmed lifecycle predecessor is unavailable for canonical recovery", + )); + } + let extraction = match crate::execute_core_extraction( + store, + &crate::CoreExtractionRequest { + source: crate::CoreExtractionSource::Signatures(std::vec![signature.0.clone()]), + limit: 1, + max_concurrent_extractions: 1, + force_replay: false, + }, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if extraction.failed != 0 + || extraction.cancelled + || extraction.selected != 1 + || extraction.extracted.saturating_add(extraction.skipped) < 1 + { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_resume_extraction_incomplete", + "lifecycle predecessor core extraction did not complete", + )); + } + let first_replay = match crate::solana_token_execution::replay_program( + store, + signature, + false, + request.force_post_validation_replay, + &[kb_program_ids::SPL_TOKEN_PROGRAM_ID], + decoders, + materializers, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if !crate::solana_token_execution::decode_completed(&first_replay) { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_resume_decode_incomplete", + "lifecycle predecessor decode and materialization did not complete", + )); + } + let filter = match kb_store_core::MaterializedEventFilter::new( + std::option::Option::None, + std::option::Option::None, + std::option::Option::Some(signature.0.clone()), + 64, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let rows = + match kb_store_core::DecodePipelineStore::list_materialized_events(store, &filter).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let recovered_count = rows + .iter() + .filter(|row| { + return row.source_decoder_name == "spl_token" + && row.payload_json.get("operation").and_then(serde_json::Value::as_str) + == std::option::Option::Some(expected_operation.as_str()); + }) + .count(); + if recovered_count == 0 { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_resume_materialization_missing", + format!( + "lifecycle predecessor signature did not materialize expected operation {expected_operation}" + ), + )); + } + let second_replay = match crate::solana_token_execution::replay_program( + store, + signature, + true, + request.force_post_validation_replay, + &[kb_program_ids::SPL_TOKEN_PROGRAM_ID], + decoders, + materializers, + observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let idempotent = second_replay.failed_inputs == 0 + && second_replay.processors.iter().all(|processor| { + return processor.failed == 0 + && processor.materialized_outputs == 0 + && processor.materialization_refused == 0; + }); + if !idempotent { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_resume_not_idempotent", + "lifecycle predecessor second replay was not idempotent", + )); + } + let materialization_count = match u32::try_from(recovered_count) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_materializations_overflow", + "recovered lifecycle materialization count exceeds u32", + )); + }, + }; + let step_index = match u8::try_from(index) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_step_index_overflow", + "recovered lifecycle step index exceeds u8", + )); + }, + }; + return std::result::Result::Ok( + crate::solana_token_lifecycle::DevnetSplTokenLifecycleStepSummary { + index: step_index, + operation_code, + signature: signature.clone(), + confirmation_status: kb_execution_api::ExecutionConfirmationStatus::Confirmed, + materialization_count, + idempotence_validated: true, + }, + ); +} + +fn materialized_operation_code(operation: &kb_executor_spl_token::SplTokenOperation) -> &str { + return match operation { + kb_executor_spl_token::SplTokenOperation::Instruction { + value: kb_executor_spl_token::SplTokenSingleOperation::InitializeMint { .. }, + } => "initialize_mint2", + kb_executor_spl_token::SplTokenOperation::Instruction { + value: kb_executor_spl_token::SplTokenSingleOperation::InitializeAccount { .. }, + } => "initialize_account3", + _ => match operation.operation_code().strip_prefix("spl_token.") { + std::option::Option::Some(value) => value, + std::option::Option::None => operation.operation_code(), + }, + }; +} + +fn emit_completed_step( + observer: &O, + step: &crate::solana_token_lifecycle::DevnetSplTokenLifecycleStepSummary, + recovered: bool, +) where + O: crate::SolanaExecutionObserver, +{ + crate::solana_execution::emit( + observer, + crate::SolanaExecutionProgressLevel::Info, + "spl_token_lifecycle_step", + format!( + "lifecycle step {} {} {}", + step.index, + step.operation_code, + if recovered { "recovered" } else { "completed" } + ), + std::option::Option::Some(step.signature.0.clone()), + ); +} + +fn generated_wallet(alias: &str) -> kb_core::Result { + let alias = match kb_wallet::WalletAlias::parse(alias) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(kb_wallet::TemporaryWallet::generate(alias)); +} + +#[allow(clippy::too_many_arguments)] +async fn create_raw_token_account( + http_pool: &kb_rpc::HttpEndpointPool, + profile: &kb_config::ProfileConfig, + request: &crate::solana_token_lifecycle::DevnetSplTokenLifecyclePreparationRequest, + observer: &O, + payer: &kb_wallet::TemporaryWallet, + account_wallet: &kb_wallet::TemporaryWallet, + account_role: &str, + space: u64, + rent_lamports: u64, +) -> kb_core::Result +where + O: crate::SolanaExecutionObserver, +{ + if let std::result::Result::Err(error) = + crate::solana_execution::ensure_not_cancelled(observer, "token_lifecycle_prepare") + { + return std::result::Result::Err(error); + } + let account = kb_model::Pubkey(account_wallet.public_key()); + let preflight = match http_pool + .get_account_info_for_role( + request.query_role.as_str(), + &account, + &kb_rpc::GetAccountInfoConfig::confirmed(), + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if preflight.account.is_some() { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_preparation_account_exists", + format!("generated lifecycle {account_role} account already exists"), + )); + } + let payer_pubkey = kb_model::Pubkey(payer.public_key()); + let plan = match crate::solana_token_lifecycle::build_account_creation_plan( + profile, + format!("{}-{account_role}", request.intent_id), + payer_pubkey, + account.clone(), + space, + rent_lamports, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let plan_evaluation = + match kb_execution_safety::ExecutionSafetyChecker.evaluate_prepared_plan(&plan) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if plan_evaluation.decision == kb_execution_safety::ExecutionSafetyDecision::Deny { + return std::result::Result::Err(kb_core::Error::new( + "execution_plan_denied", + crate::solana_execution::violation_message(plan_evaluation.violations.as_slice()), + )); + } + let latest_blockhash = match http_pool + .get_latest_blockhash_for_role( + request.query_role.as_str(), + &kb_rpc::GetLatestBlockhashConfig::confirmed(), + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let unsigned = match kb_execution_solana::build_legacy_transaction( + &plan, + latest_blockhash.blockhash.as_str(), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let fee = match http_pool + .get_fee_for_message_for_role( + request.query_role.as_str(), + unsigned.message_base64().as_str(), + &kb_rpc::GetFeeForMessageConfig::new( + kb_rpc::RpcCommitmentLevel::Confirmed, + std::option::Option::Some(latest_blockhash.context.slot), + ), + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if fee.fee_lamports.is_none() { + return std::result::Result::Err(kb_core::Error::new( + "execution_fee_unavailable", + "lifecycle account-creation fee is unavailable", + )); + } + let unsigned_base64 = match unsigned.transaction_base64() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let simulation_config = match kb_rpc::SimulateTransactionConfig::new( + kb_rpc::RpcCommitmentLevel::Confirmed, + false, + false, + std::option::Option::Some(latest_blockhash.context.slot), + true, + std::option::Option::None, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let simulation_rpc = match http_pool + .simulate_transaction_for_role( + request.transaction_role.as_str(), + unsigned_base64.as_str(), + &simulation_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let simulation = simulation_rpc.to_execution_result( + kb_execution_api::ExecutionCluster::Devnet, + kb_execution_api::ExecutionBlockhashKind::Latest, + std::option::Option::Some( + simulation_rpc.context.slot.saturating_sub(latest_blockhash.context.slot), + ), + std::option::Option::None, + std::option::Option::None, + std::option::Option::Some(&fee), + ); + let send_evaluation = + match kb_execution_safety::ExecutionSafetyChecker.evaluate_send(&plan, &simulation) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if send_evaluation.decision == kb_execution_safety::ExecutionSafetyDecision::Deny { + return std::result::Result::Err(kb_core::Error::new( + "execution_send_denied", + crate::solana_execution::violation_message(send_evaluation.violations.as_slice()), + )); + } + let evidence = unsigned.bind_simulation(simulation); + let signed = match unsigned + .sign_after_simulation(&evidence, &[payer.as_signer(), account_wallet.as_signer()]) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if let std::result::Result::Err(error) = signed.verify_signatures() { + return std::result::Result::Err(error); + } + let signature = signed.primary_signature().clone(); + let send_config = match kb_rpc::SendTransactionConfig::from_execution_config( + &profile.execution, + std::option::Option::Some(latest_blockhash.context.slot), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if let std::result::Result::Err(error) = http_pool + .send_transaction_for_role( + request.transaction_role.as_str(), + signed.transaction_base64().as_str(), + &signature, + &send_config, + ) + .await + { + return std::result::Result::Err(error); + } + let confirmation_config = match kb_rpc::ConfirmTransactionConfig::from_execution_config( + &profile.execution, + std::option::Option::Some(latest_blockhash.last_valid_block_height), + std::option::Option::Some(latest_blockhash.context.slot), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let confirmation = match http_pool + .confirm_transaction_for_roles( + request.transaction_role.as_str(), + request.query_role.as_str(), + kb_execution_api::ExecutionCluster::Devnet, + &signature, + &confirmation_config, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if !matches!( + confirmation.status, + kb_execution_api::ExecutionConfirmationStatus::Confirmed + | kb_execution_api::ExecutionConfirmationStatus::Finalized + ) { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_preparation_unconfirmed", + format!("lifecycle {account_role} account creation was not confirmed"), + )); + } + let data_limit = match usize::try_from(space) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_preparation_space_overflow", + "lifecycle account space exceeds usize", + )); + }, + }; + let account_config = match kb_rpc::GetAccountInfoConfig::confirmed_with_data(data_limit) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let account_info = match http_pool + .get_account_info_for_role(request.query_role.as_str(), &account, &account_config) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let value = match account_info.account { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_preparation_account_missing", + format!("confirmed lifecycle {account_role} account is unavailable"), + )); + }, + }; + if value.owner.0 != kb_program_ids::SPL_TOKEN_PROGRAM_ID + || value.executable + || value.space != space + || value.data.len() != data_limit + || value.data.iter().any(|byte| return *byte != 0) + || value.lamports < rent_lamports + { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_preparation_account_invalid", + format!("prepared lifecycle {account_role} account failed exact state validation"), + )); + } + return std::result::Result::Ok( + crate::solana_token_lifecycle::DevnetSplTokenLifecyclePreparationStep { + account_role: account_role.to_string(), + account, + space, + rent_lamports, + signature, + confirmation_status: confirmation.status, + }, + ); +} + +fn build_account_creation_plan( + profile: &kb_config::ProfileConfig, + intent_id: std::string::String, + payer: kb_model::Pubkey, + new_account: kb_model::Pubkey, + space: u64, + rent_lamports: u64, +) -> kb_core::Result { + let intent = kb_executor_solana_core::SolanaCoreExecutionIntent { + intent_id, + fee_payer: payer.clone(), + policy: kb_execution_api::ExecutionPolicy { + cluster: kb_execution_api::ExecutionClusterPolicy { + expected_cluster: kb_execution_api::ExecutionCluster::Devnet, + allow_mainnet: false, + mainnet_confirmation: false, + }, + simulation: kb_execution_api::ExecutionSimulationPolicy::Required, + blockhash: kb_execution_api::ExecutionBlockhashPolicy { + kind: kb_execution_api::ExecutionBlockhashKind::Latest, + max_age_slots: std::option::Option::Some( + profile.execution.recent_blockhash_max_age_slots, + ), + nonce_account: std::option::Option::None, + nonce_authority: std::option::Option::None, + }, + cost_limit: kb_execution_api::ExecutionCostLimit { + max_spend_lamports: std::option::Option::Some( + profile.execution.devnet_max_spend_lamports, + ), + max_fee_lamports: std::option::Option::Some(profile.execution.max_fee_lamports), + max_compute_unit_price_micro_lamports: std::option::Option::Some( + profile.execution.max_compute_unit_price_micro_lamports, + ), + }, + authorized_signers: std::vec![payer.clone(), new_account.clone()], + dry_run: false, + post_execution_validation: kb_execution_api::PostExecutionValidationPolicy { + canonical_insert_required: false, + core_extraction_required: false, + decode_replay_required: false, + materialization_required: false, + }, + }, + operation: kb_executor_solana_core::SolanaCoreOperation::SystemCreateAccount { + from: payer, + new_account, + lamports: rent_lamports, + space, + owner: kb_model::Pubkey(kb_program_ids::SPL_TOKEN_PROGRAM_ID.to_string()), + }, + }; + return kb_execution_api::TypedInstructionExecutor::build_prepared_plan( + &kb_executor_solana_core::SolanaCoreExecutor, + &intent, + ); +} + +fn canonical_amount( + amount: &kb_executor_spl_token::SplTokenAmount, + field: &str, +) -> kb_core::Result { + return match amount.0.parse::() { + std::result::Result::Ok(value) if value.to_string() == amount.0 => { + std::result::Result::Ok(value) + }, + std::result::Result::Ok(_) | std::result::Result::Err(_) => { + std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_amount_invalid", + format!("{field} must be a canonical u64 decimal string"), + )) + }, + }; +} + +fn lifecycle_operations( + request: &crate::solana_token_lifecycle::DevnetSplTokenLifecycleRequest, +) -> kb_core::Result> { + let mint_amount = match crate::solana_token_lifecycle::canonical_amount( + &request.mint_amount, + "mint_amount", + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let transfer_amount = match crate::solana_token_lifecycle::canonical_amount( + &request.transfer_amount, + "transfer_amount", + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let authority = kb_executor_spl_token::SplTokenAuthority { + authority: request.authority.clone(), + multisig_signers: std::vec::Vec::new(), + }; + let source_burn = mint_amount.saturating_sub(transfer_amount).to_string(); + let operations = std::vec![ + kb_executor_spl_token::SplTokenSingleOperation::InitializeMint { + mint: request.mint.clone(), + mint_authority: request.authority.clone(), + freeze_authority: std::option::Option::Some(request.authority.clone()), + decimals: request.decimals, + }, + kb_executor_spl_token::SplTokenSingleOperation::InitializeAccount { + account: request.source.clone(), + mint: request.mint.clone(), + owner: request.authority.clone(), + }, + kb_executor_spl_token::SplTokenSingleOperation::InitializeAccount { + account: request.destination.clone(), + mint: request.mint.clone(), + owner: request.authority.clone(), + }, + kb_executor_spl_token::SplTokenSingleOperation::MintToChecked { + mint: request.mint.clone(), + destination: request.source.clone(), + authority: authority.clone(), + amount: request.mint_amount.clone(), + decimals: request.decimals, + }, + kb_executor_spl_token::SplTokenSingleOperation::TransferChecked { + source: request.source.clone(), + mint: request.mint.clone(), + destination: request.destination.clone(), + authority: authority.clone(), + amount: request.transfer_amount.clone(), + decimals: request.decimals, + }, + kb_executor_spl_token::SplTokenSingleOperation::ApproveChecked { + source: request.destination.clone(), + mint: request.mint.clone(), + delegate: request.delegate.clone(), + authority: authority.clone(), + amount: request.approve_amount.clone(), + decimals: request.decimals, + }, + kb_executor_spl_token::SplTokenSingleOperation::Revoke { + source: request.destination.clone(), + authority: authority.clone(), + }, + kb_executor_spl_token::SplTokenSingleOperation::BurnChecked { + source: request.source.clone(), + mint: request.mint.clone(), + authority: authority.clone(), + amount: kb_executor_spl_token::SplTokenAmount(source_burn), + decimals: request.decimals, + }, + kb_executor_spl_token::SplTokenSingleOperation::BurnChecked { + source: request.destination.clone(), + mint: request.mint.clone(), + authority: authority.clone(), + amount: request.transfer_amount.clone(), + decimals: request.decimals, + }, + kb_executor_spl_token::SplTokenSingleOperation::CloseAccount { + account: request.source.clone(), + destination: request.authority.clone(), + authority: authority.clone(), + }, + kb_executor_spl_token::SplTokenSingleOperation::CloseAccount { + account: request.destination.clone(), + destination: request.authority.clone(), + authority, + }, + ]; + return std::result::Result::Ok( + operations + .into_iter() + .map(|value| return kb_executor_spl_token::SplTokenOperation::Instruction { value }) + .collect(), + ); +} + +fn validated_step( + index: usize, + operation_code: &str, + summary: &crate::DevnetSplTokenExecutionSummary, +) -> kb_core::Result { + let send = match summary.send_result.as_ref() { + std::option::Option::Some(value) if value.submitted => value, + std::option::Option::Some(_) | std::option::Option::None => { + return std::result::Result::Err(crate::solana_token_lifecycle::incomplete_step( + index, + operation_code, + "submission", + )); + }, + }; + let confirmation = match summary.confirmation.as_ref() { + std::option::Option::Some(value) + if matches!( + value.status, + kb_execution_api::ExecutionConfirmationStatus::Confirmed + | kb_execution_api::ExecutionConfirmationStatus::Finalized + ) => + { + value + }, + std::option::Option::Some(_) | std::option::Option::None => { + return std::result::Result::Err(crate::solana_token_lifecycle::incomplete_step( + index, + operation_code, + "confirmation", + )); + }, + }; + match summary.post_execution.as_ref() { + std::option::Option::Some(value) + if value.canonical_inserted + && value.core_extracted + && value.decode_replayed + && value.materialized => {}, + std::option::Option::Some(_) | std::option::Option::None => { + return std::result::Result::Err(crate::solana_token_lifecycle::incomplete_step( + index, + operation_code, + "post_validation", + )); + }, + }; + let idempotence = summary.idempotence_replay.as_ref().is_some_and(|value| { + return value.failed_inputs == 0 + && value.processors.iter().all(|processor| { + return processor.failed == 0 + && processor.materialized_outputs == 0 + && processor.materialization_refused == 0; + }); + }); + if !idempotence || summary.materializations.is_empty() { + return std::result::Result::Err(crate::solana_token_lifecycle::incomplete_step( + index, + operation_code, + "materialization_idempotence", + )); + } + let materialization_count = match u32::try_from(summary.materializations.len()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_materializations_overflow", + "lifecycle materialization count exceeds u32", + )); + }, + }; + let step_index = match u8::try_from(index) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(kb_core::Error::new( + "execution_spl_token_lifecycle_step_index_overflow", + "lifecycle step index exceeds u8", + )); + }, + }; + return std::result::Result::Ok( + crate::solana_token_lifecycle::DevnetSplTokenLifecycleStepSummary { + index: step_index, + operation_code: operation_code.to_string(), + signature: send.signature.clone(), + confirmation_status: confirmation.status, + materialization_count, + idempotence_validated: true, + }, + ); +} + +fn incomplete_step(index: usize, operation_code: &str, stage: &str) -> kb_core::Error { + return kb_core::Error::new( + "execution_spl_token_lifecycle_post_validation_incomplete", + format!("lifecycle step {index} {operation_code} did not validate {stage}"), + ); +} + +#[cfg(test)] +mod tests { + struct LifecycleTestObserver; + + impl crate::BackfillObserver for LifecycleTestObserver { + fn on_progress(&self, _event: &crate::BackfillProgressEvent) { + return; + } + + fn is_cancelled(&self) -> bool { + return false; + } + } + + impl crate::CoreExtractionObserver for LifecycleTestObserver { + fn on_progress(&self, _event: &crate::CoreExtractionProgressEvent) { + return; + } + + fn is_cancelled(&self) -> bool { + return false; + } + } + + impl crate::DecodeReplayObserver for LifecycleTestObserver { + fn on_progress(&self, _event: &crate::DecodeReplayProgressEvent) { + return; + } + + fn is_cancelled(&self) -> bool { + return false; + } + } + + impl crate::SolanaExecutionObserver for LifecycleTestObserver { + fn on_execution_progress(&self, event: &crate::SolanaExecutionProgressEvent) { + if event.stage == "spl_token_lifecycle_step" { + let signature = match event.signature.as_deref() { + std::option::Option::Some(value) => value, + std::option::Option::None => "missing", + }; + println!("SPL Token lifecycle progress={} signature={}", event.message, signature,); + } + return; + } + + fn is_execution_cancelled(&self) -> bool { + return false; + } + } + + fn key(value: &str) -> kb_model::Pubkey { + return kb_model::Pubkey(value.to_string()); + } + + fn request() -> crate::DevnetSplTokenLifecycleRequest { + return crate::DevnetSplTokenLifecycleRequest::new( + "lifecycle-1", + crate::solana_token_lifecycle::tests::key(kb_program_ids::STAKE_PROGRAM_ID), + crate::solana_token_lifecycle::tests::key(kb_program_ids::VOTE_PROGRAM_ID), + crate::solana_token_lifecycle::tests::key(kb_program_ids::CONFIG_PROGRAM_ID), + crate::solana_token_lifecycle::tests::key(kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID), + crate::solana_token_lifecycle::tests::key(kb_program_ids::SYSTEM_PROGRAM_ID), + ); + } + + #[test] + fn conservative_request_requires_explicit_destructive_authorization() { + let request = crate::solana_token_lifecycle::tests::request(); + assert!(!request.submit); + assert!(!request.operator_confirmed); + assert!(request.validate().is_ok()); + } + + #[test] + fn lifecycle_operations_preserve_order_and_conserve_raw_amounts() { + let request = crate::solana_token_lifecycle::tests::request(); + let operations = match super::lifecycle_operations(&request) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("lifecycle construction failed: {error}"), + }; + let codes = operations + .iter() + .map(|value| return value.operation_code()) + .collect::>(); + assert_eq!( + codes, + std::vec![ + "spl_token.initialize_mint", + "spl_token.initialize_account", + "spl_token.initialize_account", + "spl_token.mint_to_checked", + "spl_token.transfer_checked", + "spl_token.approve_checked", + "spl_token.revoke", + "spl_token.burn_checked", + "spl_token.burn_checked", + "spl_token.close_account", + "spl_token.close_account", + ], + ); + let source_burn = match &operations[7] { + kb_executor_spl_token::SplTokenOperation::Instruction { + value: kb_executor_spl_token::SplTokenSingleOperation::BurnChecked { amount, .. }, + } => amount.0.as_str(), + _ => panic!("source burn step missing"), + }; + assert_eq!(source_burn, "6"); + } + + #[test] + fn invalid_amounts_aliases_and_delegate_fail_closed() { + let mut request = crate::solana_token_lifecycle::tests::request(); + request.transfer_amount = kb_executor_spl_token::SplTokenAmount("10".to_string()); + assert!(request.validate().is_err()); + request.transfer_amount = kb_executor_spl_token::SplTokenAmount("04".to_string()); + assert!(request.validate().is_err()); + request.transfer_amount = kb_executor_spl_token::SplTokenAmount("4".to_string()); + request.delegate = request.authority.clone(); + assert!(request.validate().is_err()); + request.delegate = + crate::solana_token_lifecycle::tests::key(kb_program_ids::COMPUTE_BUDGET_PROGRAM_ID); + request.destination = request.source.clone(); + assert!(request.validate().is_err()); + } + + #[test] + fn account_preparation_requires_explicit_spend_authorization() { + let mut request = crate::DevnetSplTokenLifecyclePreparationRequest::new("prepare-1"); + assert!(request.validate().is_err()); + request.submit = true; + assert!(request.validate().is_err()); + request.operator_confirmed = true; + assert!(request.validate().is_ok()); + } + + #[test] + fn account_creation_plan_uses_exact_token_owner_space_spend_and_signers() { + let profile = crate::solana_token_lifecycle::tests::local_devnet_profile(); + let payer = crate::solana_token_lifecycle::tests::key(kb_program_ids::SYSTEM_PROGRAM_ID); + let account = crate::solana_token_lifecycle::tests::key(kb_program_ids::STAKE_PROGRAM_ID); + let plan = match super::build_account_creation_plan( + &profile, + "prepare-mint".to_string(), + payer.clone(), + account.clone(), + super::MINT_ACCOUNT_SPACE, + 1_461_600, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("account creation plan failed: {error}"), + }; + assert_eq!(plan.operation_code, "solana_core.system.create_account"); + assert_eq!(plan.requested_spend_lamports, 1_461_600); + assert_eq!(plan.instructions.len(), 1); + assert_eq!(plan.instructions[0].accounts.len(), 2); + assert_eq!(plan.instructions[0].accounts[0].pubkey, payer); + assert_eq!(plan.instructions[0].accounts[1].pubkey, account); + assert_eq!(plan.required_signers.len(), 2); + assert_eq!(plan.policy.authorized_signers.len(), 2); + assert!(!plan.policy.dry_run); + } + + #[test] + fn resume_requires_exact_predecessor_signature_and_bounded_delay() { + let mut request = crate::solana_token_lifecycle::tests::request(); + request.first_step_index = 3; + assert!(request.validate().is_err()); + request.resume_predecessor_signature = std::option::Option::Some(kb_model::Signature( + "3yxsCf8tc1H2P9E3m6Kgu7J3XVeBw7wePuRTHvCaidQ74bsa2TP5xg4hpXAw1jkRSB3LrkLrXgx5eqvCQqRsXcfq".to_string(), + )); + assert!(request.validate().is_ok()); + request.inter_step_delay_ms = 30_001; + assert!(request.validate().is_err()); + request.inter_step_delay_ms = 2_000; + request.first_step_index = 0; + assert!(request.validate().is_err()); + } + + #[test] + fn recovery_uses_current_initialization_wire_names() { + let request = crate::solana_token_lifecycle::tests::request(); + let operations = match super::lifecycle_operations(&request) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("lifecycle construction failed: {error}"), + }; + assert_eq!(super::materialized_operation_code(&operations[0]), "initialize_mint2"); + assert_eq!(super::materialized_operation_code(&operations[2]), "initialize_account3"); + assert_eq!(super::materialized_operation_code(&operations[3]), "mint_to_checked"); + } + + #[tokio::test] + async fn optional_devnet_controlled_lifecycle_from_env() { + if std::env::var("KB_DEVNET_SPL_TOKEN_LIFECYCLE_TEST").ok().as_deref() + != std::option::Option::Some("1") + { + return; + } + if std::env::var("KB_DEVNET_SPL_TOKEN_LIFECYCLE_SUBMIT").ok().as_deref() + != std::option::Option::Some("1") + { + panic!("controlled lifecycle requires KB_DEVNET_SPL_TOKEN_LIFECYCLE_SUBMIT=1"); + } + let mut profile = crate::solana_token_lifecycle::tests::local_devnet_profile(); + if let std::result::Result::Ok(directory) = std::env::var("KB_DEVNET_WALLET_DIR") { + profile.wallet.wallet_dir = directory; + } + let database_url = match std::env::var("KB_POSTGRES_TEST_URL") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("KB_POSTGRES_TEST_URL is required for lifecycle submission: {error}"); + }, + }; + profile.database.backend = "postgres".to_string(); + profile.database.postgres.url = database_url; + let pool = match kb_rpc::HttpEndpointPool::from_profile(&profile) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("HTTP pool creation failed: {error}"), + }; + let store = match kb_store_pg::PostgresStore::connect_from_profile_config(&profile).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("PostgreSQL connection failed: {error}"), + }; + if let std::result::Result::Err(error) = store.initialize_store_schema().await { + panic!("PostgreSQL schema initialization failed: {error}"); + } + let workspace_root = match std::path::Path::new(env!("CARGO_MANIFEST_DIR")).parent() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("workspace root cannot be resolved"), + }; + let observer = crate::solana_token_lifecycle::tests::LifecycleTestObserver; + let prepared_accounts = if std::env::var("KB_DEVNET_SPL_TOKEN_LIFECYCLE_PREPARE") + .ok() + .as_deref() + == std::option::Option::Some("1") + { + let mut preparation = crate::DevnetSplTokenLifecyclePreparationRequest::new(format!( + "devnet-token-lifecycle-prepare-{}", + uuid::Uuid::new_v4() + )); + preparation.submit = true; + preparation.operator_confirmed = true; + let summary = match crate::prepare_devnet_spl_token_lifecycle_accounts( + &pool, + &profile, + workspace_root, + &preparation, + &observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("Devnet Token lifecycle account preparation failed: {error}"); + }, + }; + for step in &summary.steps { + println!( + "SPL Token lifecycle preparation role={} account={} space={} rent={} signature={} status={:?}", + step.account_role, + step.account.0, + step.space, + step.rent_lamports, + step.signature.0, + step.confirmation_status, + ); + } + std::option::Option::Some(summary) + } else { + std::option::Option::None + }; + if prepared_accounts.is_some() { + tokio::time::sleep(std::time::Duration::from_millis( + crate::solana_token_lifecycle::tests::optional_u64_env( + "KB_DEVNET_SPL_TOKEN_LIFECYCLE_PREPARATION_SETTLE_DELAY_MS", + 10_000, + ), + )) + .await; + } + let (mint, source, destination, delegate, authority) = match prepared_accounts.as_ref() { + std::option::Option::Some(value) => ( + value.mint.clone(), + value.source.clone(), + value.destination.clone(), + value.delegate.clone(), + value.authority.clone(), + ), + std::option::Option::None => ( + crate::solana_token_lifecycle::tests::required_pubkey_env( + "KB_DEVNET_SPL_TOKEN_LIFECYCLE_MINT", + ), + crate::solana_token_lifecycle::tests::required_pubkey_env( + "KB_DEVNET_SPL_TOKEN_LIFECYCLE_SOURCE", + ), + crate::solana_token_lifecycle::tests::required_pubkey_env( + "KB_DEVNET_SPL_TOKEN_LIFECYCLE_DESTINATION", + ), + crate::solana_token_lifecycle::tests::required_pubkey_env( + "KB_DEVNET_SPL_TOKEN_LIFECYCLE_DELEGATE", + ), + crate::solana_token_lifecycle::tests::required_pubkey_env( + "KB_DEVNET_SPL_TOKEN_LIFECYCLE_AUTHORITY", + ), + ), + }; + let mut request = crate::DevnetSplTokenLifecycleRequest::new( + format!("devnet-token-lifecycle-{}", uuid::Uuid::new_v4()), + mint, + source, + destination, + delegate, + authority, + ); + request.decimals = crate::solana_token_lifecycle::tests::optional_u8_env( + "KB_DEVNET_SPL_TOKEN_LIFECYCLE_DECIMALS", + 9, + ); + request.mint_amount = kb_executor_spl_token::SplTokenAmount( + crate::solana_token_lifecycle::tests::optional_string_env( + "KB_DEVNET_SPL_TOKEN_LIFECYCLE_MINT_AMOUNT", + "10", + ), + ); + request.transfer_amount = kb_executor_spl_token::SplTokenAmount( + crate::solana_token_lifecycle::tests::optional_string_env( + "KB_DEVNET_SPL_TOKEN_LIFECYCLE_TRANSFER_AMOUNT", + "4", + ), + ); + request.approve_amount = kb_executor_spl_token::SplTokenAmount( + crate::solana_token_lifecycle::tests::optional_string_env( + "KB_DEVNET_SPL_TOKEN_LIFECYCLE_APPROVE_AMOUNT", + "2", + ), + ); + request.submit = true; + request.operator_confirmed = true; + request.post_validation_max_retries = 20; + request.first_step_index = crate::solana_token_lifecycle::tests::optional_u8_env( + "KB_DEVNET_SPL_TOKEN_LIFECYCLE_FIRST_STEP_INDEX", + 0, + ); + request.resume_predecessor_signature = if request.first_step_index > 0 { + std::option::Option::Some(kb_model::Signature( + crate::solana_token_lifecycle::tests::required_string_env( + "KB_DEVNET_SPL_TOKEN_LIFECYCLE_RESUME_PREDECESSOR_SIGNATURE", + ), + )) + } else { + std::option::Option::None + }; + request.inter_step_delay_ms = crate::solana_token_lifecycle::tests::optional_u64_env( + "KB_DEVNET_SPL_TOKEN_LIFECYCLE_INTER_STEP_DELAY_MS", + 2_000, + ); + let decoders: std::vec::Vec> = + std::vec![std::sync::Arc::new(kb_decoder_spl_token::SplTokenDecoder)]; + let materializers: std::vec::Vec< + std::sync::Arc, + > = std::vec![std::sync::Arc::new( + kb_materializer_token_accounts::TokenAccountsMaterializer, + )]; + let summary = match crate::execute_devnet_spl_token_lifecycle( + &pool, + &store, + &profile, + workspace_root, + &request, + decoders.as_slice(), + materializers.as_slice(), + &observer, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Devnet Token lifecycle failed: {error}"), + }; + let expected_step_count = if request.first_step_index == 0 { + 11 + } else { + 12_usize.saturating_sub(usize::from(request.first_step_index)) + }; + assert_eq!(summary.steps.len(), expected_step_count); + assert!(summary.steps.iter().all(|step| { + return step.idempotence_validated && step.materialization_count > 0; + })); + for step in summary.steps { + println!( + "SPL Token lifecycle step={} operation={} signature={} status={:?} materializations={} idempotent={}", + step.index, + step.operation_code, + step.signature.0, + step.confirmation_status, + step.materialization_count, + step.idempotence_validated, + ); + } + } + + fn local_devnet_profile() -> kb_config::ProfileConfig { + let config = + match kb_config::parse_config_json(include_str!("../../config/example.config.json")) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("example config parse failed: {error}"), + }; + for profile in config.profiles { + if profile.name == "local_devnet" { + return profile; + } + } + panic!("local_devnet profile missing"); + } + + fn required_pubkey_env(name: &str) -> kb_model::Pubkey { + return match std::env::var(name) { + std::result::Result::Ok(value) => kb_model::Pubkey(value), + std::result::Result::Err(error) => panic!("{name} is required: {error}"), + }; + } + + fn optional_string_env(name: &str, default: &str) -> std::string::String { + return match std::env::var(name) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => default.to_string(), + }; + } + + fn required_string_env(name: &str) -> std::string::String { + return match std::env::var(name) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("{name} is required: {error}"), + }; + } + + fn optional_u64_env(name: &str, default: u64) -> u64 { + return match std::env::var(name) { + std::result::Result::Ok(value) => match value.parse::() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("{name} is invalid: {error}"), + }, + std::result::Result::Err(_) => default, + }; + } + + fn optional_u8_env(name: &str, default: u8) -> u8 { + return match std::env::var(name) { + std::result::Result::Ok(value) => match value.parse::() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("{name} is invalid: {error}"), + }, + std::result::Result::Err(_) => default, + }; + } +} diff --git a/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_stateful.rs b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_stateful.rs new file mode 100644 index 0000000..9f9e454 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_pipeline/src/solana_token_stateful.rs @@ -0,0 +1,1910 @@ +// file: kb_pipeline/src/solana_token_stateful.rs +// version: 1 + +//! Stateful Localnet and Devnet readiness checks for classic SPL Token operations. + +const MINT_LEN: usize = 82; +const TOKEN_ACCOUNT_LEN: usize = 165; +const MULTISIG_LEN: usize = 355; +const MAX_STATE_ACCOUNTS: usize = 128; + +/// Stateful readiness outcome for one classic SPL Token operation. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum SplTokenStatefulReadinessStatus { + /// Every stateful preflight check passed. + Ready, + /// At least one stateful preflight check failed. + Blocked, +} + +/// One machine-readable classic SPL Token stateful check. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SplTokenStatefulCheck { + /// Stable diagnostic code. + pub code: std::string::String, + /// Whether the check passed. + pub passed: bool, + /// Operator-readable explanation. + pub message: std::string::String, +} + +/// One contextual fact measured during classic SPL Token state inspection. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SplTokenStatefulFact { + /// Stable fact key. + pub key: std::string::String, + /// Exact string representation of the measured value. + pub value: std::string::String, +} + +/// Complete request for one classic SPL Token stateful readiness inspection. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SplTokenStatefulReadinessRequest { + /// HTTP endpoint role used for all state reads. + pub query_role: std::string::String, + /// Expected Localnet or Devnet cluster. + pub cluster: kb_execution_api::ExecutionCluster, + /// Typed operation whose external state must be inspected. + pub operation: kb_executor_spl_token::SplTokenOperation, +} + +/// Complete classic SPL Token stateful readiness report. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SplTokenStatefulReadinessReport { + /// Expected cluster. + pub cluster: kb_execution_api::ExecutionCluster, + /// Stable top-level operation code. + pub operation_code: std::string::String, + /// Aggregate readiness status. + pub status: crate::solana_token_stateful::SplTokenStatefulReadinessStatus, + /// Highest contextual slot observed while reading accounts. + pub context_slot: std::option::Option, + /// Ordered stateful checks. + pub checks: std::vec::Vec, + /// Ordered measured facts. + pub facts: std::vec::Vec, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +struct MintState { + mint_authority: std::option::Option, + supply: u64, + decimals: u8, + initialized: bool, + freeze_authority: std::option::Option, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +struct TokenAccountState { + mint: std::string::String, + owner: std::string::String, + amount: u64, + delegate: std::option::Option, + state: u8, + native_reserve: std::option::Option, + delegated_amount: u64, + close_authority: std::option::Option, +} + +#[derive(Clone, Debug, Eq, PartialEq)] +struct MultisigState { + threshold: u8, + member_count: u8, + initialized: bool, + members: std::vec::Vec, +} + +type AccountCache = + std::collections::BTreeMap>; + +/// Inspects Localnet or Devnet Token state required before simulation. +pub async fn inspect_spl_token_stateful_readiness( + pool: &kb_rpc::HttpEndpointPool, + request: &crate::solana_token_stateful::SplTokenStatefulReadinessRequest, +) -> kb_core::Result { + if request.query_role.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "SPL Token stateful readiness query_role must not be empty", + )); + } + match request.cluster { + kb_execution_api::ExecutionCluster::Localnet + | kb_execution_api::ExecutionCluster::Devnet => {}, + kb_execution_api::ExecutionCluster::Testnet + | kb_execution_api::ExecutionCluster::Mainnet => { + return std::result::Result::Err(kb_core::Error::new( + "spl_token_stateful_cluster_unsupported", + "SPL Token stateful readiness is restricted to Localnet and Devnet", + )); + }, + } + let genesis = match pool.get_genesis_hash_for_role(request.query_role.as_str()).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + match crate::solana_token_stateful::validate_cluster(request.cluster, &genesis) { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let addresses = match crate::solana_token_stateful::operation_addresses(&request.operation) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let config = match kb_rpc::GetAccountInfoConfig::confirmed_with_data(MULTISIG_LEN) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut cache = crate::solana_token_stateful::AccountCache::new(); + let mut highest_slot: std::option::Option = std::option::Option::None; + for address in addresses { + let result = pool + .get_account_info_for_role(request.query_role.as_str(), &address, &config) + .await; + let result = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + highest_slot = std::option::Option::Some(match highest_slot { + std::option::Option::Some(slot) => slot.max(result.context.slot), + std::option::Option::None => result.context.slot, + }); + cache.insert(address.0, result.account); + } + let mut report = crate::solana_token_stateful::SplTokenStatefulReadinessReport { + cluster: request.cluster, + operation_code: request.operation.operation_code().to_string(), + status: crate::solana_token_stateful::SplTokenStatefulReadinessStatus::Ready, + context_slot: highest_slot, + checks: std::vec::Vec::new(), + facts: std::vec![crate::solana_token_stateful::SplTokenStatefulFact { + key: "genesis_hash".to_string(), + value: genesis.genesis_hash, + }], + }; + let initialization_accounts = + crate::solana_token_stateful::initialization_accounts(&request.operation); + for (address, data_length) in initialization_accounts { + let rent = pool + .get_minimum_balance_for_rent_exemption_for_role( + request.query_role.as_str(), + data_length as u64, + &kb_rpc::GetMinimumBalanceForRentExemptionConfig::confirmed(), + ) + .await; + let rent = match rent { + std::result::Result::Ok(value) => value.minimum_balance_lamports, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_token_stateful::push_fact( + &mut report, + "rent_exempt_minimum_lamports", + format!("{}:{rent}", address.0), + ); + let lamports = cache + .get(address.0.as_str()) + .and_then(|value| return value.as_ref()) + .map(|value| return value.lamports); + crate::solana_token_stateful::push_check( + &mut report, + "rent_exempt", + lamports.is_some_and(|value| return value >= rent), + format!("initialization account {} must be rent exempt", address.0), + ); + } + let withdraw_accounts = + crate::solana_token_stateful::withdraw_excess_accounts(&request.operation); + for address in withdraw_accounts { + let account = + cache.get(address.0.as_str()).and_then(|value| return value.as_ref()).cloned(); + if let std::option::Option::Some(account) = account { + let rent = pool + .get_minimum_balance_for_rent_exemption_for_role( + request.query_role.as_str(), + account.space, + &kb_rpc::GetMinimumBalanceForRentExemptionConfig::confirmed(), + ) + .await; + let rent = match rent { + std::result::Result::Ok(value) => value.minimum_balance_lamports, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + crate::solana_token_stateful::push_fact( + &mut report, + "withdraw_rent_exempt_minimum_lamports", + format!("{}:{rent}", address.0), + ); + crate::solana_token_stateful::push_check( + &mut report, + "excess_lamports_present", + account.lamports > rent, + format!("source account {} must contain lamports above rent exemption", address.0), + ); + } + } + match &request.operation { + kb_executor_spl_token::SplTokenOperation::Instruction { value } => { + crate::solana_token_stateful::inspect_single(&mut report, &cache, value); + }, + kb_executor_spl_token::SplTokenOperation::Batch { instructions } => { + for (index, value) in instructions.iter().enumerate() { + crate::solana_token_stateful::push_fact( + &mut report, + "batch_child", + format!("{index}:{}", value.operation_code()), + ); + crate::solana_token_stateful::inspect_single(&mut report, &cache, value); + } + }, + } + report.status = if report.checks.iter().all(|check| return check.passed) { + crate::solana_token_stateful::SplTokenStatefulReadinessStatus::Ready + } else { + crate::solana_token_stateful::SplTokenStatefulReadinessStatus::Blocked + }; + return std::result::Result::Ok(report); +} + +fn validate_cluster( + expected: kb_execution_api::ExecutionCluster, + genesis: &kb_rpc::GenesisHashResult, +) -> kb_core::Result<()> { + return match expected { + kb_execution_api::ExecutionCluster::Devnet => { + if genesis.classified_cluster + == std::option::Option::Some(kb_execution_api::ExecutionCluster::Devnet) + { + std::result::Result::Ok(()) + } else { + std::result::Result::Err(kb_core::Error::new( + "spl_token_stateful_devnet_genesis_mismatch", + "SPL Token Devnet readiness requires the official Devnet genesis hash", + )) + } + }, + kb_execution_api::ExecutionCluster::Localnet => { + if genesis.classified_cluster.is_none() { + std::result::Result::Ok(()) + } else { + std::result::Result::Err(kb_core::Error::new( + "spl_token_stateful_localnet_public_cluster", + "Localnet readiness refuses endpoints classified as a public cluster", + )) + } + }, + kb_execution_api::ExecutionCluster::Testnet + | kb_execution_api::ExecutionCluster::Mainnet => { + std::result::Result::Err(kb_core::Error::new( + "spl_token_stateful_cluster_unsupported", + "SPL Token stateful readiness is restricted to Localnet and Devnet", + )) + }, + }; +} + +fn operation_addresses( + operation: &kb_executor_spl_token::SplTokenOperation, +) -> kb_core::Result> { + let mut addresses = std::vec::Vec::new(); + match operation { + kb_executor_spl_token::SplTokenOperation::Instruction { value } => { + crate::solana_token_stateful::collect_single_addresses(&mut addresses, value); + }, + kb_executor_spl_token::SplTokenOperation::Batch { instructions } => { + for value in instructions { + crate::solana_token_stateful::collect_single_addresses(&mut addresses, value); + } + }, + } + if addresses.len() > MAX_STATE_ACCOUNTS { + return std::result::Result::Err(kb_core::Error::new( + "spl_token_stateful_account_limit", + format!("SPL Token stateful readiness exceeds {MAX_STATE_ACCOUNTS} unique accounts"), + )); + } + return std::result::Result::Ok(addresses); +} + +fn initialization_accounts( + operation: &kb_executor_spl_token::SplTokenOperation, +) -> std::vec::Vec<(kb_model::Pubkey, usize)> { + let mut values = std::vec::Vec::new(); + let instructions: std::vec::Vec<&kb_executor_spl_token::SplTokenSingleOperation> = + match operation { + kb_executor_spl_token::SplTokenOperation::Instruction { value } => { + std::vec![value] + }, + kb_executor_spl_token::SplTokenOperation::Batch { instructions } => { + instructions.iter().collect() + }, + }; + for operation in instructions { + let candidate = match operation { + kb_executor_spl_token::SplTokenSingleOperation::InitializeMint { mint, .. } => { + std::option::Option::Some((mint.clone(), MINT_LEN)) + }, + kb_executor_spl_token::SplTokenSingleOperation::InitializeAccount { + account, .. + } => std::option::Option::Some((account.clone(), TOKEN_ACCOUNT_LEN)), + kb_executor_spl_token::SplTokenSingleOperation::InitializeMultisig { + multisig, .. + } => std::option::Option::Some((multisig.clone(), MULTISIG_LEN)), + _ => std::option::Option::None, + }; + if let std::option::Option::Some(candidate) = candidate { + if !values + .iter() + .any(|(address, length)| return address == &candidate.0 && *length == candidate.1) + { + values.push(candidate); + } + } + } + return values; +} + +fn withdraw_excess_accounts( + operation: &kb_executor_spl_token::SplTokenOperation, +) -> std::vec::Vec { + let instructions: std::vec::Vec<&kb_executor_spl_token::SplTokenSingleOperation> = + match operation { + kb_executor_spl_token::SplTokenOperation::Instruction { value } => { + std::vec![value] + }, + kb_executor_spl_token::SplTokenOperation::Batch { instructions } => { + instructions.iter().collect() + }, + }; + let mut values = std::vec::Vec::new(); + for operation in instructions { + if let kb_executor_spl_token::SplTokenSingleOperation::WithdrawExcessLamports { + account, + .. + } = operation + { + if !values.iter().any(|value| return value == account) { + values.push(account.clone()); + } + } + } + return values; +} + +fn add_address(addresses: &mut std::vec::Vec, value: &kb_model::Pubkey) { + if !addresses.iter().any(|existing| return existing == value) { + addresses.push(value.clone()); + } +} + +fn add_authority( + addresses: &mut std::vec::Vec, + authority: &kb_executor_spl_token::SplTokenAuthority, +) { + if !authority.multisig_signers.is_empty() { + crate::solana_token_stateful::add_address(addresses, &authority.authority); + } +} + +fn collect_single_addresses( + addresses: &mut std::vec::Vec, + operation: &kb_executor_spl_token::SplTokenSingleOperation, +) { + match operation { + kb_executor_spl_token::SplTokenSingleOperation::InitializeMint { mint, .. } + | kb_executor_spl_token::SplTokenSingleOperation::GetAccountDataSize { mint } + | kb_executor_spl_token::SplTokenSingleOperation::AmountToUiAmount { mint, .. } + | kb_executor_spl_token::SplTokenSingleOperation::UiAmountToAmount { mint, .. } => { + crate::solana_token_stateful::add_address(addresses, mint); + }, + kb_executor_spl_token::SplTokenSingleOperation::InitializeAccount { + account, mint, .. + } => { + crate::solana_token_stateful::add_address(addresses, account); + crate::solana_token_stateful::add_address(addresses, mint); + }, + kb_executor_spl_token::SplTokenSingleOperation::InitializeMultisig { multisig, .. } => { + crate::solana_token_stateful::add_address(addresses, multisig); + }, + kb_executor_spl_token::SplTokenSingleOperation::Transfer { + source, + destination, + authority, + .. + } => { + crate::solana_token_stateful::add_address(addresses, source); + crate::solana_token_stateful::add_address(addresses, destination); + crate::solana_token_stateful::add_authority(addresses, authority); + }, + kb_executor_spl_token::SplTokenSingleOperation::Approve { source, authority, .. } + | kb_executor_spl_token::SplTokenSingleOperation::Revoke { source, authority } => { + crate::solana_token_stateful::add_address(addresses, source); + crate::solana_token_stateful::add_authority(addresses, authority); + }, + kb_executor_spl_token::SplTokenSingleOperation::SetAuthority { + owned, + current_authority, + .. + } => { + crate::solana_token_stateful::add_address(addresses, owned); + crate::solana_token_stateful::add_authority(addresses, current_authority); + }, + kb_executor_spl_token::SplTokenSingleOperation::MintTo { + mint, + destination, + authority, + .. + } + | kb_executor_spl_token::SplTokenSingleOperation::MintToChecked { + mint, + destination, + authority, + .. + } => { + crate::solana_token_stateful::add_address(addresses, mint); + crate::solana_token_stateful::add_address(addresses, destination); + crate::solana_token_stateful::add_authority(addresses, authority); + }, + kb_executor_spl_token::SplTokenSingleOperation::Burn { + source, mint, authority, .. + } + | kb_executor_spl_token::SplTokenSingleOperation::BurnChecked { + source, + mint, + authority, + .. + } => { + crate::solana_token_stateful::add_address(addresses, source); + crate::solana_token_stateful::add_address(addresses, mint); + crate::solana_token_stateful::add_authority(addresses, authority); + }, + kb_executor_spl_token::SplTokenSingleOperation::CloseAccount { + account, authority, .. + } + | kb_executor_spl_token::SplTokenSingleOperation::UnwrapLamports { + account, + authority, + .. + } + | kb_executor_spl_token::SplTokenSingleOperation::WithdrawExcessLamports { + account, + authority, + .. + } => { + crate::solana_token_stateful::add_address(addresses, account); + crate::solana_token_stateful::add_authority(addresses, authority); + }, + kb_executor_spl_token::SplTokenSingleOperation::FreezeAccount { + account, + mint, + authority, + } + | kb_executor_spl_token::SplTokenSingleOperation::ThawAccount { + account, + mint, + authority, + } => { + crate::solana_token_stateful::add_address(addresses, account); + crate::solana_token_stateful::add_address(addresses, mint); + crate::solana_token_stateful::add_authority(addresses, authority); + }, + kb_executor_spl_token::SplTokenSingleOperation::TransferChecked { + source, + mint, + destination, + authority, + .. + } + | kb_executor_spl_token::SplTokenSingleOperation::ApproveChecked { + source, + mint, + delegate: destination, + authority, + .. + } => { + crate::solana_token_stateful::add_address(addresses, source); + crate::solana_token_stateful::add_address(addresses, mint); + if matches!( + operation, + kb_executor_spl_token::SplTokenSingleOperation::TransferChecked { .. } + ) { + crate::solana_token_stateful::add_address(addresses, destination); + } + crate::solana_token_stateful::add_authority(addresses, authority); + }, + kb_executor_spl_token::SplTokenSingleOperation::SyncNative { account, .. } + | kb_executor_spl_token::SplTokenSingleOperation::InitializeImmutableOwner { account } => { + crate::solana_token_stateful::add_address(addresses, account); + }, + } +} + +fn inspect_single( + report: &mut crate::solana_token_stateful::SplTokenStatefulReadinessReport, + cache: &crate::solana_token_stateful::AccountCache, + operation: &kb_executor_spl_token::SplTokenSingleOperation, +) { + match operation { + kb_executor_spl_token::SplTokenSingleOperation::InitializeMint { mint, .. } => { + let state = crate::solana_token_stateful::mint_state(report, cache, mint); + if let std::option::Option::Some(state) = state { + crate::solana_token_stateful::push_check( + report, + "mint_uninitialized", + !state.initialized, + "mint must be uninitialized before InitializeMint2", + ); + } + }, + kb_executor_spl_token::SplTokenSingleOperation::InitializeAccount { + account, mint, .. + } => { + let state = crate::solana_token_stateful::token_state(report, cache, account); + if let std::option::Option::Some(state) = state { + crate::solana_token_stateful::push_check( + report, + "account_uninitialized", + state.state == 0, + "token account must be uninitialized before InitializeAccount3", + ); + } + let mint_state = crate::solana_token_stateful::mint_state(report, cache, mint); + crate::solana_token_stateful::check_mint_initialized(report, mint_state.as_ref()); + }, + kb_executor_spl_token::SplTokenSingleOperation::InitializeMultisig { multisig, .. } => { + let state = crate::solana_token_stateful::multisig_state(report, cache, multisig); + if let std::option::Option::Some(state) = state { + crate::solana_token_stateful::push_check( + report, + "multisig_uninitialized", + !state.initialized, + "multisig must be uninitialized before InitializeMultisig2", + ); + } + }, + kb_executor_spl_token::SplTokenSingleOperation::Transfer { + source, + destination, + authority, + amount, + } => { + crate::solana_token_stateful::inspect_transfer( + report, + cache, + source, + destination, + std::option::Option::None, + authority, + amount, + std::option::Option::None, + ); + }, + kb_executor_spl_token::SplTokenSingleOperation::TransferChecked { + source, + mint, + destination, + authority, + amount, + decimals, + } => { + crate::solana_token_stateful::inspect_transfer( + report, + cache, + source, + destination, + std::option::Option::Some(mint), + authority, + amount, + std::option::Option::Some(*decimals), + ); + }, + kb_executor_spl_token::SplTokenSingleOperation::Approve { + source, + authority, + amount, + .. + } => { + crate::solana_token_stateful::inspect_owner_amount( + report, cache, source, authority, amount, false, + ); + }, + kb_executor_spl_token::SplTokenSingleOperation::ApproveChecked { + source, + mint, + authority, + amount, + decimals, + .. + } => { + crate::solana_token_stateful::inspect_owner_amount( + report, cache, source, authority, amount, false, + ); + crate::solana_token_stateful::inspect_checked_mint( + report, cache, source, mint, *decimals, + ); + }, + kb_executor_spl_token::SplTokenSingleOperation::Revoke { source, authority } => { + let state = crate::solana_token_stateful::token_state(report, cache, source); + if let std::option::Option::Some(state) = state { + crate::solana_token_stateful::check_authority( + report, + cache, + authority, + std::option::Option::Some(state.owner.as_str()), + ); + crate::solana_token_stateful::push_check( + report, + "delegate_present", + state.delegate.is_some(), + "revoke requires an existing delegate", + ); + } + }, + kb_executor_spl_token::SplTokenSingleOperation::MintTo { + mint, + destination, + authority, + amount, + } + | kb_executor_spl_token::SplTokenSingleOperation::MintToChecked { + mint, + destination, + authority, + amount, + .. + } => { + let mint_value = crate::solana_token_stateful::mint_state(report, cache, mint); + let destination_value = + crate::solana_token_stateful::token_state(report, cache, destination); + crate::solana_token_stateful::check_mint_initialized(report, mint_value.as_ref()); + if let ( + std::option::Option::Some(mint_value), + std::option::Option::Some(destination_value), + ) = (mint_value, destination_value) + { + crate::solana_token_stateful::push_check( + report, + "destination_mint_matches", + destination_value.mint == mint.0, + "destination token account must use the mint", + ); + crate::solana_token_stateful::check_authority( + report, + cache, + authority, + mint_value.mint_authority.as_deref(), + ); + let parsed = crate::solana_token_stateful::amount_value(report, amount); + if let std::option::Option::Some(parsed) = parsed { + crate::solana_token_stateful::push_check( + report, + "mint_supply_no_overflow", + mint_value.supply.checked_add(parsed).is_some(), + "mint supply must not overflow u64", + ); + } + } + if let kb_executor_spl_token::SplTokenSingleOperation::MintToChecked { + decimals, .. + } = operation + { + if let std::option::Option::Some(mint_value) = + crate::solana_token_stateful::mint_state_quiet(cache, mint) + { + crate::solana_token_stateful::push_check( + report, + "mint_decimals_match", + mint_value.decimals == *decimals, + "checked decimals must match mint state", + ); + } + } + }, + kb_executor_spl_token::SplTokenSingleOperation::Burn { + source, + mint, + authority, + amount, + } + | kb_executor_spl_token::SplTokenSingleOperation::BurnChecked { + source, + mint, + authority, + amount, + .. + } => { + crate::solana_token_stateful::inspect_owner_amount( + report, cache, source, authority, amount, true, + ); + crate::solana_token_stateful::inspect_checked_relation(report, cache, source, mint); + if let (std::option::Option::Some(mint_state), std::option::Option::Some(parsed)) = ( + crate::solana_token_stateful::mint_state_quiet(cache, mint), + amount.0.parse::().ok(), + ) { + crate::solana_token_stateful::push_check( + report, + "mint_supply_sufficient", + mint_state.supply >= parsed, + "mint supply must cover burned amount", + ); + } + if let kb_executor_spl_token::SplTokenSingleOperation::BurnChecked { + decimals, .. + } = operation + { + crate::solana_token_stateful::inspect_checked_mint( + report, cache, source, mint, *decimals, + ); + } + }, + kb_executor_spl_token::SplTokenSingleOperation::CloseAccount { + account, authority, .. + } => { + let state = crate::solana_token_stateful::token_state(report, cache, account); + if let std::option::Option::Some(state) = state { + let expected = match state.close_authority.as_deref() { + std::option::Option::Some(value) => value, + std::option::Option::None => state.owner.as_str(), + }; + crate::solana_token_stateful::check_authority( + report, + cache, + authority, + std::option::Option::Some(expected), + ); + crate::solana_token_stateful::push_check( + report, + "close_balance_allowed", + state.native_reserve.is_some() || state.amount == 0, + "non-native token account must have zero token balance before close", + ); + } + }, + kb_executor_spl_token::SplTokenSingleOperation::FreezeAccount { + account, + mint, + authority, + } => { + crate::solana_token_stateful::inspect_freeze_thaw( + report, cache, account, mint, authority, 1, + ); + }, + kb_executor_spl_token::SplTokenSingleOperation::ThawAccount { + account, + mint, + authority, + } => { + crate::solana_token_stateful::inspect_freeze_thaw( + report, cache, account, mint, authority, 2, + ); + }, + kb_executor_spl_token::SplTokenSingleOperation::SyncNative { account, .. } => { + let state = crate::solana_token_stateful::token_state(report, cache, account); + if let std::option::Option::Some(state) = state { + crate::solana_token_stateful::push_check( + report, + "native_account", + state.native_reserve.is_some(), + "SyncNative requires a wrapped-SOL token account", + ); + } + }, + kb_executor_spl_token::SplTokenSingleOperation::GetAccountDataSize { mint } + | kb_executor_spl_token::SplTokenSingleOperation::AmountToUiAmount { mint, .. } + | kb_executor_spl_token::SplTokenSingleOperation::UiAmountToAmount { mint, .. } => { + let state = crate::solana_token_stateful::mint_state(report, cache, mint); + crate::solana_token_stateful::check_mint_initialized(report, state.as_ref()); + }, + kb_executor_spl_token::SplTokenSingleOperation::InitializeImmutableOwner { account } => { + let state = crate::solana_token_stateful::token_state(report, cache, account); + if let std::option::Option::Some(state) = state { + crate::solana_token_stateful::push_check( + report, + "account_uninitialized", + state.state == 0, + "classic immutable-owner compatibility no-op precedes account initialization", + ); + } + }, + kb_executor_spl_token::SplTokenSingleOperation::UnwrapLamports { + account, + authority, + amount_lamports, + .. + } => { + let state = crate::solana_token_stateful::token_state(report, cache, account); + if let std::option::Option::Some(state) = state { + crate::solana_token_stateful::push_check( + report, + "native_account", + state.native_reserve.is_some(), + "UnwrapLamports requires a wrapped-SOL token account", + ); + let expected = match state.close_authority.as_deref() { + std::option::Option::Some(value) => value, + std::option::Option::None => state.owner.as_str(), + }; + crate::solana_token_stateful::check_authority( + report, + cache, + authority, + std::option::Option::Some(expected), + ); + if let std::option::Option::Some(amount) = amount_lamports { + if let std::option::Option::Some(parsed) = + crate::solana_token_stateful::amount_value(report, amount) + { + crate::solana_token_stateful::push_check( + report, + "unwrap_balance_sufficient", + state.amount >= parsed, + "wrapped-SOL balance must cover requested unwrap amount", + ); + } + } + } + }, + kb_executor_spl_token::SplTokenSingleOperation::WithdrawExcessLamports { + account, + authority, + .. + } => { + let value = crate::solana_token_stateful::account_value( + report, + cache, + account, + std::option::Option::None, + ); + if let std::option::Option::Some(value) = value { + match value.data.len() { + MINT_LEN => { + if let std::result::Result::Ok(state) = + crate::solana_token_stateful::parse_mint(value.data.as_slice()) + { + crate::solana_token_stateful::check_authority( + report, + cache, + authority, + state.mint_authority.as_deref(), + ); + } + }, + TOKEN_ACCOUNT_LEN => { + if let std::result::Result::Ok(state) = + crate::solana_token_stateful::parse_token_account(value.data.as_slice()) + { + crate::solana_token_stateful::check_authority( + report, + cache, + authority, + std::option::Option::Some(state.owner.as_str()), + ); + } + }, + MULTISIG_LEN => { + crate::solana_token_stateful::check_authority( + report, + cache, + authority, + std::option::Option::Some(account.0.as_str()), + ); + }, + _ => {}, + } + } + }, + kb_executor_spl_token::SplTokenSingleOperation::SetAuthority { + owned, + authority_type, + new_authority, + current_authority, + } => { + crate::solana_token_stateful::inspect_set_authority( + report, + cache, + owned, + *authority_type, + new_authority, + current_authority, + ); + }, + } +} + +fn inspect_transfer( + report: &mut crate::solana_token_stateful::SplTokenStatefulReadinessReport, + cache: &crate::solana_token_stateful::AccountCache, + source: &kb_model::Pubkey, + destination: &kb_model::Pubkey, + explicit_mint: std::option::Option<&kb_model::Pubkey>, + authority: &kb_executor_spl_token::SplTokenAuthority, + amount: &kb_executor_spl_token::SplTokenAmount, + decimals: std::option::Option, +) { + let source_state = crate::solana_token_stateful::token_state(report, cache, source); + let destination_state = crate::solana_token_stateful::token_state(report, cache, destination); + if let (std::option::Option::Some(source_state), std::option::Option::Some(destination_state)) = + (source_state, destination_state) + { + crate::solana_token_stateful::push_check( + report, + "transfer_mint_matches", + source_state.mint == destination_state.mint, + "source and destination token accounts must share a mint", + ); + crate::solana_token_stateful::push_check( + report, + "source_not_frozen", + source_state.state == 1, + "source token account must be initialized and unfrozen", + ); + crate::solana_token_stateful::push_check( + report, + "destination_not_frozen", + destination_state.state == 1, + "destination token account must be initialized and unfrozen", + ); + if let std::option::Option::Some(parsed) = + crate::solana_token_stateful::amount_value(report, amount) + { + crate::solana_token_stateful::push_check( + report, + "source_balance_sufficient", + source_state.amount >= parsed, + "source token balance must cover transfer amount", + ); + if source_state.delegate.as_deref() + == std::option::Option::Some(authority.authority.0.as_str()) + { + crate::solana_token_stateful::push_check( + report, + "delegate_allowance_sufficient", + source_state.delegated_amount >= parsed, + "delegate allowance must cover transfer amount", + ); + } + } + crate::solana_token_stateful::check_token_authority( + report, + cache, + authority, + &source_state, + ); + if let std::option::Option::Some(mint) = explicit_mint { + crate::solana_token_stateful::push_check( + report, + "explicit_mint_matches", + source_state.mint == mint.0, + "checked mint must match source account state", + ); + if let std::option::Option::Some(decimals) = decimals { + if let std::option::Option::Some(mint_state) = + crate::solana_token_stateful::mint_state(report, cache, mint) + { + crate::solana_token_stateful::push_check( + report, + "mint_decimals_match", + mint_state.decimals == decimals, + "checked decimals must match mint state", + ); + } + } + } + } +} + +fn inspect_owner_amount( + report: &mut crate::solana_token_stateful::SplTokenStatefulReadinessReport, + cache: &crate::solana_token_stateful::AccountCache, + source: &kb_model::Pubkey, + authority: &kb_executor_spl_token::SplTokenAuthority, + amount: &kb_executor_spl_token::SplTokenAmount, + require_balance: bool, +) { + let state = crate::solana_token_stateful::token_state(report, cache, source); + if let std::option::Option::Some(state) = state { + crate::solana_token_stateful::push_check( + report, + "source_not_frozen", + state.state == 1, + "source token account must be initialized and unfrozen", + ); + crate::solana_token_stateful::check_token_authority(report, cache, authority, &state); + if let std::option::Option::Some(parsed) = + crate::solana_token_stateful::amount_value(report, amount) + { + if require_balance { + crate::solana_token_stateful::push_check( + report, + "source_balance_sufficient", + state.amount >= parsed, + "source token balance must cover requested amount", + ); + } + if state.delegate.as_deref() + == std::option::Option::Some(authority.authority.0.as_str()) + { + crate::solana_token_stateful::push_check( + report, + "delegate_allowance_sufficient", + state.delegated_amount >= parsed, + "delegate allowance must cover requested amount", + ); + } + } + } +} + +fn inspect_checked_relation( + report: &mut crate::solana_token_stateful::SplTokenStatefulReadinessReport, + cache: &crate::solana_token_stateful::AccountCache, + source: &kb_model::Pubkey, + mint: &kb_model::Pubkey, +) { + if let std::option::Option::Some(state) = + crate::solana_token_stateful::token_state_quiet(cache, source) + { + crate::solana_token_stateful::push_check( + report, + "source_mint_matches", + state.mint == mint.0, + "source token account must use the explicit mint", + ); + } + let mint_state = crate::solana_token_stateful::mint_state(report, cache, mint); + crate::solana_token_stateful::check_mint_initialized(report, mint_state.as_ref()); +} + +fn inspect_checked_mint( + report: &mut crate::solana_token_stateful::SplTokenStatefulReadinessReport, + cache: &crate::solana_token_stateful::AccountCache, + source: &kb_model::Pubkey, + mint: &kb_model::Pubkey, + decimals: u8, +) { + crate::solana_token_stateful::inspect_checked_relation(report, cache, source, mint); + if let std::option::Option::Some(state) = + crate::solana_token_stateful::mint_state_quiet(cache, mint) + { + crate::solana_token_stateful::push_check( + report, + "mint_decimals_match", + state.decimals == decimals, + "checked decimals must match mint state", + ); + } +} + +fn inspect_freeze_thaw( + report: &mut crate::solana_token_stateful::SplTokenStatefulReadinessReport, + cache: &crate::solana_token_stateful::AccountCache, + account: &kb_model::Pubkey, + mint: &kb_model::Pubkey, + authority: &kb_executor_spl_token::SplTokenAuthority, + required_state: u8, +) { + let account_state = crate::solana_token_stateful::token_state(report, cache, account); + let mint_state = crate::solana_token_stateful::mint_state(report, cache, mint); + if let std::option::Option::Some(account_state) = account_state { + crate::solana_token_stateful::push_check( + report, + "account_mint_matches", + account_state.mint == mint.0, + "token account must use the supplied mint", + ); + crate::solana_token_stateful::push_check( + report, + "account_state_matches", + account_state.state == required_state, + "token account state must match freeze or thaw precondition", + ); + } + if let std::option::Option::Some(mint_state) = mint_state { + crate::solana_token_stateful::check_authority( + report, + cache, + authority, + mint_state.freeze_authority.as_deref(), + ); + } +} + +fn inspect_set_authority( + report: &mut crate::solana_token_stateful::SplTokenStatefulReadinessReport, + cache: &crate::solana_token_stateful::AccountCache, + owned: &kb_model::Pubkey, + authority_type: kb_executor_spl_token::SplTokenAuthorityType, + new_authority: &std::option::Option, + current: &kb_executor_spl_token::SplTokenAuthority, +) { + let value = crate::solana_token_stateful::account_value( + report, + cache, + owned, + std::option::Option::None, + ); + if let std::option::Option::Some(value) = value { + match authority_type { + kb_executor_spl_token::SplTokenAuthorityType::MintTokens + | kb_executor_spl_token::SplTokenAuthorityType::FreezeAccount => { + if let std::result::Result::Ok(state) = + crate::solana_token_stateful::parse_mint(value.data.as_slice()) + { + let expected = if authority_type + == kb_executor_spl_token::SplTokenAuthorityType::MintTokens + { + state.mint_authority.as_deref() + } else { + state.freeze_authority.as_deref() + }; + crate::solana_token_stateful::check_authority(report, cache, current, expected); + } else { + crate::solana_token_stateful::push_check( + report, + "authority_domain_layout", + false, + "mint authority domains require a Mint account", + ); + } + }, + kb_executor_spl_token::SplTokenAuthorityType::AccountOwner + | kb_executor_spl_token::SplTokenAuthorityType::CloseAccount => { + if let std::result::Result::Ok(state) = + crate::solana_token_stateful::parse_token_account(value.data.as_slice()) + { + let close_authority = match state.close_authority.as_deref() { + std::option::Option::Some(value) => value, + std::option::Option::None => state.owner.as_str(), + }; + let expected = if authority_type + == kb_executor_spl_token::SplTokenAuthorityType::AccountOwner + { + std::option::Option::Some(state.owner.as_str()) + } else { + std::option::Option::Some(close_authority) + }; + crate::solana_token_stateful::check_authority(report, cache, current, expected); + if authority_type == kb_executor_spl_token::SplTokenAuthorityType::AccountOwner + { + crate::solana_token_stateful::push_check( + report, + "account_owner_not_revoked", + new_authority.is_some(), + "token-account owner cannot be revoked", + ); + } + } else { + crate::solana_token_stateful::push_check( + report, + "authority_domain_layout", + false, + "account authority domains require a token Account", + ); + } + }, + } + } +} + +fn check_token_authority( + report: &mut crate::solana_token_stateful::SplTokenStatefulReadinessReport, + cache: &crate::solana_token_stateful::AccountCache, + authority: &kb_executor_spl_token::SplTokenAuthority, + account: &crate::solana_token_stateful::TokenAccountState, +) { + let supplied = authority.authority.0.as_str(); + let expected = if account.delegate.as_deref() == std::option::Option::Some(supplied) { + account.delegate.as_deref() + } else { + std::option::Option::Some(account.owner.as_str()) + }; + crate::solana_token_stateful::check_authority(report, cache, authority, expected); +} + +fn check_authority( + report: &mut crate::solana_token_stateful::SplTokenStatefulReadinessReport, + cache: &crate::solana_token_stateful::AccountCache, + authority: &kb_executor_spl_token::SplTokenAuthority, + expected: std::option::Option<&str>, +) { + crate::solana_token_stateful::push_check( + report, + "authority_present", + expected.is_some(), + "required authority must not be revoked", + ); + if let std::option::Option::Some(expected) = expected { + crate::solana_token_stateful::push_check( + report, + "authority_matches", + authority.authority.0 == expected, + "declared authority must match on-chain state", + ); + } + if authority.multisig_signers.is_empty() { + return; + } + let state = crate::solana_token_stateful::multisig_state(report, cache, &authority.authority); + if let std::option::Option::Some(state) = state { + crate::solana_token_stateful::push_check( + report, + "multisig_initialized", + state.initialized, + "multisig authority must be initialized", + ); + crate::solana_token_stateful::push_check( + report, + "multisig_shape", + state.threshold >= 1 + && state.threshold <= state.member_count + && state.member_count <= 11, + "multisig M/N must satisfy 1 <= M <= N <= 11", + ); + let mut unique = std::collections::BTreeSet::new(); + for signer in &authority.multisig_signers { + if state.members.iter().any(|member| return member == &signer.0) { + unique.insert(signer.0.clone()); + } + } + crate::solana_token_stateful::push_check( + report, + "multisig_threshold", + unique.len() >= usize::from(state.threshold), + "distinct supplied member signers must meet the multisig threshold", + ); + } +} + +fn check_mint_initialized( + report: &mut crate::solana_token_stateful::SplTokenStatefulReadinessReport, + state: std::option::Option<&crate::solana_token_stateful::MintState>, +) { + if let std::option::Option::Some(state) = state { + crate::solana_token_stateful::push_check( + report, + "mint_initialized", + state.initialized, + "mint must be initialized", + ); + } +} + +fn account_value<'a>( + report: &mut crate::solana_token_stateful::SplTokenStatefulReadinessReport, + cache: &'a crate::solana_token_stateful::AccountCache, + address: &kb_model::Pubkey, + expected_len: std::option::Option, +) -> std::option::Option<&'a kb_rpc::AccountInfoValue> { + let value = cache.get(address.0.as_str()).and_then(|value| return value.as_ref()); + crate::solana_token_stateful::push_check( + report, + "account_exists", + value.is_some(), + format!("state account {} must exist", address.0), + ); + if let std::option::Option::Some(value) = value { + crate::solana_token_stateful::push_check( + report, + "classic_token_owner", + value.owner.0 == kb_program_ids::SPL_TOKEN_PROGRAM_ID, + format!("state account {} must be owned by classic SPL Token", address.0), + ); + if let std::option::Option::Some(expected_len) = expected_len { + crate::solana_token_stateful::push_check( + report, + "account_layout", + value.data.len() == expected_len && value.space == expected_len as u64, + format!( + "state account {} must use the exact {expected_len}-byte layout", + address.0 + ), + ); + } + } + return value; +} + +fn mint_state( + report: &mut crate::solana_token_stateful::SplTokenStatefulReadinessReport, + cache: &crate::solana_token_stateful::AccountCache, + address: &kb_model::Pubkey, +) -> std::option::Option { + let value = crate::solana_token_stateful::account_value( + report, + cache, + address, + std::option::Option::Some(MINT_LEN), + ); + return match value { + std::option::Option::Some(value) => { + match crate::solana_token_stateful::parse_mint(value.data.as_slice()) { + std::result::Result::Ok(state) => { + crate::solana_token_stateful::push_fact( + report, + "mint_decimals", + state.decimals, + ); + crate::solana_token_stateful::push_fact(report, "mint_supply", state.supply); + std::option::Option::Some(state) + }, + std::result::Result::Err(message) => { + crate::solana_token_stateful::push_check( + report, + "mint_layout_valid", + false, + message, + ); + std::option::Option::None + }, + } + }, + std::option::Option::None => std::option::Option::None, + }; +} + +fn mint_state_quiet( + cache: &crate::solana_token_stateful::AccountCache, + address: &kb_model::Pubkey, +) -> std::option::Option { + return cache.get(address.0.as_str()).and_then(|value| return value.as_ref()).and_then( + |value| return crate::solana_token_stateful::parse_mint(value.data.as_slice()).ok(), + ); +} + +fn token_state( + report: &mut crate::solana_token_stateful::SplTokenStatefulReadinessReport, + cache: &crate::solana_token_stateful::AccountCache, + address: &kb_model::Pubkey, +) -> std::option::Option { + let value = crate::solana_token_stateful::account_value( + report, + cache, + address, + std::option::Option::Some(TOKEN_ACCOUNT_LEN), + ); + return match value { + std::option::Option::Some(value) => { + match crate::solana_token_stateful::parse_token_account(value.data.as_slice()) { + std::result::Result::Ok(state) => { + crate::solana_token_stateful::push_fact(report, "token_amount", state.amount); + crate::solana_token_stateful::push_fact( + report, + "token_mint", + state.mint.as_str(), + ); + std::option::Option::Some(state) + }, + std::result::Result::Err(message) => { + crate::solana_token_stateful::push_check( + report, + "token_account_layout_valid", + false, + message, + ); + std::option::Option::None + }, + } + }, + std::option::Option::None => std::option::Option::None, + }; +} + +fn token_state_quiet( + cache: &crate::solana_token_stateful::AccountCache, + address: &kb_model::Pubkey, +) -> std::option::Option { + return cache.get(address.0.as_str()).and_then(|value| return value.as_ref()).and_then( + |value| { + return crate::solana_token_stateful::parse_token_account(value.data.as_slice()).ok(); + }, + ); +} + +fn multisig_state( + report: &mut crate::solana_token_stateful::SplTokenStatefulReadinessReport, + cache: &crate::solana_token_stateful::AccountCache, + address: &kb_model::Pubkey, +) -> std::option::Option { + let value = crate::solana_token_stateful::account_value( + report, + cache, + address, + std::option::Option::Some(MULTISIG_LEN), + ); + return match value { + std::option::Option::Some(value) => { + match crate::solana_token_stateful::parse_multisig(value.data.as_slice()) { + std::result::Result::Ok(state) => std::option::Option::Some(state), + std::result::Result::Err(message) => { + crate::solana_token_stateful::push_check( + report, + "multisig_layout_valid", + false, + message, + ); + std::option::Option::None + }, + } + }, + std::option::Option::None => std::option::Option::None, + }; +} + +fn amount_value( + report: &mut crate::solana_token_stateful::SplTokenStatefulReadinessReport, + amount: &kb_executor_spl_token::SplTokenAmount, +) -> std::option::Option { + return match amount.0.parse::() { + std::result::Result::Ok(value) => std::option::Option::Some(value), + std::result::Result::Err(_) => { + crate::solana_token_stateful::push_check( + report, + "amount_valid", + false, + "raw token amount must be a canonical u64", + ); + std::option::Option::None + }, + }; +} + +fn push_check( + report: &mut crate::solana_token_stateful::SplTokenStatefulReadinessReport, + code: &str, + passed: bool, + message: impl std::convert::Into, +) { + report.checks.push(crate::solana_token_stateful::SplTokenStatefulCheck { + code: code.to_string(), + passed, + message: message.into(), + }); +} + +fn push_fact( + report: &mut crate::solana_token_stateful::SplTokenStatefulReadinessReport, + key: &str, + value: impl std::fmt::Display, +) { + report.facts.push(crate::solana_token_stateful::SplTokenStatefulFact { + key: key.to_string(), + value: value.to_string(), + }); +} + +fn parse_mint( + bytes: &[u8], +) -> std::result::Result { + if bytes.len() != MINT_LEN { + return std::result::Result::Err("Mint data must contain exactly 82 bytes"); + } + let mint_authority = match crate::solana_token_stateful::parse_coption_pubkey(&bytes[0..36]) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + let supply = match crate::solana_token_stateful::read_u64(&bytes[36..44]) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + let initialized = match bytes[45] { + 0 => false, + 1 => true, + _ => return std::result::Result::Err("Mint initialized flag must be 0 or 1"), + }; + let freeze_authority = match crate::solana_token_stateful::parse_coption_pubkey(&bytes[46..82]) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + return std::result::Result::Ok(crate::solana_token_stateful::MintState { + mint_authority, + supply, + decimals: bytes[44], + initialized, + freeze_authority, + }); +} + +fn parse_token_account( + bytes: &[u8], +) -> std::result::Result { + if bytes.len() != TOKEN_ACCOUNT_LEN { + return std::result::Result::Err("Token Account data must contain exactly 165 bytes"); + } + if bytes[108] > 2 { + return std::result::Result::Err( + "Token Account state must be Uninitialized, Initialized or Frozen", + ); + } + let amount = match crate::solana_token_stateful::read_u64(&bytes[64..72]) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + let delegate = match crate::solana_token_stateful::parse_coption_pubkey(&bytes[72..108]) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + let native_reserve = match crate::solana_token_stateful::parse_coption_u64(&bytes[109..121]) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + let delegated_amount = match crate::solana_token_stateful::read_u64(&bytes[121..129]) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + let close_authority = match crate::solana_token_stateful::parse_coption_pubkey(&bytes[129..165]) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(message) => return std::result::Result::Err(message), + }; + return std::result::Result::Ok(crate::solana_token_stateful::TokenAccountState { + mint: bs58::encode(&bytes[0..32]).into_string(), + owner: bs58::encode(&bytes[32..64]).into_string(), + amount, + delegate, + state: bytes[108], + native_reserve, + delegated_amount, + close_authority, + }); +} + +fn parse_multisig( + bytes: &[u8], +) -> std::result::Result { + if bytes.len() != MULTISIG_LEN { + return std::result::Result::Err("Multisig data must contain exactly 355 bytes"); + } + let initialized = match bytes[2] { + 0 => false, + 1 => true, + _ => return std::result::Result::Err("Multisig initialized flag must be 0 or 1"), + }; + let count = usize::from(bytes[1].min(11)); + let mut members = std::vec::Vec::new(); + for index in 0..count { + let start = 3 + index * 32; + members.push(bs58::encode(&bytes[start..start + 32]).into_string()); + } + return std::result::Result::Ok(crate::solana_token_stateful::MultisigState { + threshold: bytes[0], + member_count: bytes[1], + initialized, + members, + }); +} + +fn parse_coption_pubkey( + bytes: &[u8], +) -> std::result::Result, &'static str> { + if bytes.len() != 36 { + return std::result::Result::Err("COption must contain exactly 36 bytes"); + } + return match &bytes[0..4] { + [0, 0, 0, 0] => std::result::Result::Ok(std::option::Option::None), + [1, 0, 0, 0] => std::result::Result::Ok(std::option::Option::Some( + bs58::encode(&bytes[4..36]).into_string(), + )), + _ => std::result::Result::Err("COption tag must be 0 or 1"), + }; +} + +fn parse_coption_u64(bytes: &[u8]) -> std::result::Result, &'static str> { + if bytes.len() != 12 { + return std::result::Result::Err("COption must contain exactly 12 bytes"); + } + return match &bytes[0..4] { + [0, 0, 0, 0] => std::result::Result::Ok(std::option::Option::None), + [1, 0, 0, 0] => match crate::solana_token_stateful::read_u64(&bytes[4..12]) { + std::result::Result::Ok(value) => { + std::result::Result::Ok(std::option::Option::Some(value)) + }, + std::result::Result::Err(message) => std::result::Result::Err(message), + }, + _ => std::result::Result::Err("COption tag must be 0 or 1"), + }; +} + +fn read_u64(bytes: &[u8]) -> std::result::Result { + let array = match <[u8; 8]>::try_from(bytes) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err("u64 field must contain exactly 8 bytes"); + }, + }; + return std::result::Result::Ok(u64::from_le_bytes(array)); +} + +#[cfg(test)] +mod tests { + fn empty_report() -> crate::SplTokenStatefulReadinessReport { + return crate::SplTokenStatefulReadinessReport { + cluster: kb_execution_api::ExecutionCluster::Devnet, + operation_code: kb_executor_spl_token::TRANSFER_CHECKED_OPERATION.to_string(), + status: crate::SplTokenStatefulReadinessStatus::Ready, + context_slot: std::option::Option::Some(1), + checks: std::vec::Vec::new(), + facts: std::vec::Vec::new(), + }; + } + + fn account_info(data: std::vec::Vec) -> kb_rpc::AccountInfoValue { + return kb_rpc::AccountInfoValue { + lamports: 10_000_000, + owner: kb_model::ProgramId(kb_program_ids::SPL_TOKEN_PROGRAM_ID.to_string()), + executable: false, + rent_epoch: 0, + space: data.len() as u64, + data, + }; + } + + fn local_devnet_profile() -> kb_config::ProfileConfig { + let config = + match kb_config::parse_config_json(include_str!("../../config/example.config.json")) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("example config parse failed: {error}"), + }; + for profile in config.profiles { + if profile.name == "local_devnet" { + return profile; + } + } + panic!("local_devnet profile missing"); + } + + #[test] + fn exact_mint_account_and_multisig_layouts_are_parsed() { + let mut mint = [0_u8; super::MINT_LEN]; + mint[0..4].copy_from_slice(&1_u32.to_le_bytes()); + mint[4..36].copy_from_slice(&[1_u8; 32]); + mint[36..44].copy_from_slice(&u64::MAX.to_le_bytes()); + mint[44] = 9; + mint[45] = 1; + let parsed_mint = match super::parse_mint(&mint) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Mint parse failed: {error}"), + }; + assert_eq!(parsed_mint.supply, u64::MAX); + assert_eq!(parsed_mint.decimals, 9); + assert!(parsed_mint.initialized); + + let mut account = [0_u8; super::TOKEN_ACCOUNT_LEN]; + account[0..32].copy_from_slice(&[2_u8; 32]); + account[32..64].copy_from_slice(&[3_u8; 32]); + account[64..72].copy_from_slice(&42_u64.to_le_bytes()); + account[108] = 1; + account[109..113].copy_from_slice(&1_u32.to_le_bytes()); + account[113..121].copy_from_slice(&2_039_280_u64.to_le_bytes()); + let parsed_account = match super::parse_token_account(&account) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Account parse failed: {error}"), + }; + assert_eq!(parsed_account.amount, 42); + assert_eq!(parsed_account.native_reserve, std::option::Option::Some(2_039_280)); + + let mut multisig = [0_u8; super::MULTISIG_LEN]; + multisig[0] = 2; + multisig[1] = 3; + multisig[2] = 1; + multisig[3..35].copy_from_slice(&[4_u8; 32]); + multisig[35..67].copy_from_slice(&[5_u8; 32]); + multisig[67..99].copy_from_slice(&[6_u8; 32]); + let parsed_multisig = match super::parse_multisig(&multisig) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Multisig parse failed: {error}"), + }; + assert_eq!(parsed_multisig.threshold, 2); + assert_eq!(parsed_multisig.members.len(), 3); + } + + #[test] + fn malformed_layouts_and_option_tags_fail_closed() { + assert!(super::parse_mint(&[0_u8; 81]).is_err()); + let mut account = [0_u8; super::TOKEN_ACCOUNT_LEN]; + account[108] = 3; + assert!(super::parse_token_account(&account).is_err()); + assert!(super::parse_coption_pubkey(&[2_u8; 36]).is_err()); + assert!(super::parse_coption_u64(&[2_u8; 12]).is_err()); + } + + #[test] + fn checked_transfer_state_checks_exact_mint_decimals_balance_and_owner() { + let mint_key = kb_model::Pubkey(bs58::encode([2_u8; 32]).into_string()); + let owner_key = kb_model::Pubkey(bs58::encode([3_u8; 32]).into_string()); + let source_key = kb_model::Pubkey(bs58::encode([7_u8; 32]).into_string()); + let destination_key = kb_model::Pubkey(bs58::encode([8_u8; 32]).into_string()); + let mut mint = [0_u8; super::MINT_LEN]; + mint[44] = 9; + mint[45] = 1; + let mut source = [0_u8; super::TOKEN_ACCOUNT_LEN]; + source[0..32].copy_from_slice(&[2_u8; 32]); + source[32..64].copy_from_slice(&[3_u8; 32]); + source[64..72].copy_from_slice(&42_u64.to_le_bytes()); + source[108] = 1; + let mut destination = [0_u8; super::TOKEN_ACCOUNT_LEN]; + destination[0..32].copy_from_slice(&[2_u8; 32]); + destination[32..64].copy_from_slice(&[9_u8; 32]); + destination[108] = 1; + let mut cache = super::AccountCache::new(); + cache.insert( + mint_key.0.clone(), + std::option::Option::Some(crate::solana_token_stateful::tests::account_info( + mint.to_vec(), + )), + ); + cache.insert( + source_key.0.clone(), + std::option::Option::Some(crate::solana_token_stateful::tests::account_info( + source.to_vec(), + )), + ); + cache.insert( + destination_key.0.clone(), + std::option::Option::Some(crate::solana_token_stateful::tests::account_info( + destination.to_vec(), + )), + ); + let mut report = crate::solana_token_stateful::tests::empty_report(); + super::inspect_single( + &mut report, + &cache, + &kb_executor_spl_token::SplTokenSingleOperation::TransferChecked { + source: source_key, + mint: mint_key, + destination: destination_key, + authority: kb_executor_spl_token::SplTokenAuthority { + authority: owner_key, + multisig_signers: std::vec::Vec::new(), + }, + amount: kb_executor_spl_token::SplTokenAmount("42".to_string()), + decimals: 9, + }, + ); + assert!(report.checks.iter().all(|check| return check.passed)); + assert!(report.checks.iter().any(|check| return check.code == "mint_decimals_match")); + assert!( + report + .checks + .iter() + .any(|check| return check.code == "source_balance_sufficient") + ); + } + + #[test] + fn multisig_members_are_deduplicated_before_threshold_validation() { + let multisig_key = kb_model::Pubkey(bs58::encode([10_u8; 32]).into_string()); + let member_one = kb_model::Pubkey(bs58::encode([11_u8; 32]).into_string()); + let member_two = kb_model::Pubkey(bs58::encode([12_u8; 32]).into_string()); + let mut multisig = [0_u8; super::MULTISIG_LEN]; + multisig[0] = 2; + multisig[1] = 2; + multisig[2] = 1; + multisig[3..35].copy_from_slice(&[11_u8; 32]); + multisig[35..67].copy_from_slice(&[12_u8; 32]); + let mut cache = super::AccountCache::new(); + cache.insert( + multisig_key.0.clone(), + std::option::Option::Some(crate::solana_token_stateful::tests::account_info( + multisig.to_vec(), + )), + ); + let mut report = crate::solana_token_stateful::tests::empty_report(); + super::check_authority( + &mut report, + &cache, + &kb_executor_spl_token::SplTokenAuthority { + authority: multisig_key.clone(), + multisig_signers: std::vec![member_one.clone(), member_one, member_two,], + }, + std::option::Option::Some(multisig_key.0.as_str()), + ); + assert!(report.checks.iter().all(|check| return check.passed)); + assert!(report.checks.iter().any(|check| return check.code == "multisig_threshold")); + } + + #[test] + fn operation_account_collection_is_bounded_and_deduplicated() { + let key = kb_model::Pubkey(kb_program_ids::SYSTEM_PROGRAM_ID.to_string()); + let operation = kb_executor_spl_token::SplTokenOperation::Instruction { + value: kb_executor_spl_token::SplTokenSingleOperation::TransferChecked { + source: key.clone(), + mint: key.clone(), + destination: key.clone(), + authority: kb_executor_spl_token::SplTokenAuthority { + authority: key.clone(), + multisig_signers: std::vec::Vec::new(), + }, + amount: kb_executor_spl_token::SplTokenAmount("1".to_string()), + decimals: 9, + }, + }; + let addresses = match super::operation_addresses(&operation) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("address collection failed: {error}"), + }; + assert_eq!(addresses, std::vec![key]); + } + + #[tokio::test] + async fn optional_devnet_checked_transfer_readiness_from_env() { + if std::env::var("KB_DEVNET_SPL_TOKEN_PREFLIGHT_TEST").ok().as_deref() + != std::option::Option::Some("1") + { + return; + } + let source = match std::env::var("KB_DEVNET_SPL_TOKEN_SOURCE") { + std::result::Result::Ok(value) => kb_model::Pubkey(value), + std::result::Result::Err(error) => { + panic!("KB_DEVNET_SPL_TOKEN_SOURCE is required: {error}"); + }, + }; + let mint = match std::env::var("KB_DEVNET_SPL_TOKEN_MINT") { + std::result::Result::Ok(value) => kb_model::Pubkey(value), + std::result::Result::Err(error) => { + panic!("KB_DEVNET_SPL_TOKEN_MINT is required: {error}"); + }, + }; + let destination = match std::env::var("KB_DEVNET_SPL_TOKEN_DESTINATION") { + std::result::Result::Ok(value) => kb_model::Pubkey(value), + std::result::Result::Err(error) => { + panic!("KB_DEVNET_SPL_TOKEN_DESTINATION is required: {error}"); + }, + }; + let authority = match std::env::var("KB_DEVNET_SPL_TOKEN_AUTHORITY") { + std::result::Result::Ok(value) => kb_model::Pubkey(value), + std::result::Result::Err(error) => { + panic!("KB_DEVNET_SPL_TOKEN_AUTHORITY is required: {error}"); + }, + }; + let decimals = match std::env::var("KB_DEVNET_SPL_TOKEN_DECIMALS") { + std::result::Result::Ok(value) => match value.parse::() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("KB_DEVNET_SPL_TOKEN_DECIMALS must be a u8: {error}"); + }, + }, + std::result::Result::Err(_) => 9, + }; + let amount = match std::env::var("KB_DEVNET_SPL_TOKEN_AMOUNT") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => "1".to_string(), + }; + let pool = match kb_rpc::HttpEndpointPool::from_profile( + &crate::solana_token_stateful::tests::local_devnet_profile(), + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("HTTP pool creation failed: {error}"), + }; + let report = match crate::inspect_spl_token_stateful_readiness( + &pool, + &crate::SplTokenStatefulReadinessRequest { + query_role: "http_queries".to_string(), + cluster: kb_execution_api::ExecutionCluster::Devnet, + operation: kb_executor_spl_token::SplTokenOperation::Instruction { + value: kb_executor_spl_token::SplTokenSingleOperation::TransferChecked { + source, + mint, + destination, + authority: kb_executor_spl_token::SplTokenAuthority { + authority, + multisig_signers: std::vec::Vec::new(), + }, + amount: kb_executor_spl_token::SplTokenAmount(amount), + decimals, + }, + }, + }, + ) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("Devnet Token preflight failed: {error}"), + }; + assert_eq!( + report.status, + crate::SplTokenStatefulReadinessStatus::Ready, + "{:#?}", + report.checks + ); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_program_ids/Cargo.toml b/migration/khadhroony-bot2-reference/kb_program_ids/Cargo.toml new file mode 100644 index 0000000..3ec0427 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_program_ids/Cargo.toml @@ -0,0 +1,14 @@ +# file: kb_program_ids/Cargo.toml +# version: 1 + +[package] +name = "kb_program_ids" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_program_ids/README.md b/migration/khadhroony-bot2-reference/kb_program_ids/README.md new file mode 100644 index 0000000..18fcee7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_program_ids/README.md @@ -0,0 +1,30 @@ + + + +# kb_program_ids + +Ce crate centralise les identifiants de programmes Solana utilisés par les décodeurs, exécuteurs, stores, applications et outils du workspace. + +## Rôle + +Il fournit une source unique pour les `program_id` connus afin d’éviter les divergences entre les crates de lecture et les crates d’exécution. + +## Règle + +Les crates spécialisés peuvent conserver un fichier `constants.rs` pour leurs constantes internes, mais les identifiants de programmes doivent être référencés via `kb_program_ids::...`. + +## Registre énumérable + +`registered_program_ids()` expose les identifiants connus avec un code stable en lower snake case. `find_registered_program_id()` permet la résolution inverse exacte d’un program ID vers ce code. Ce registre sert notamment aux sélecteurs de l’application de démonstration sans dupliquer la liste des constantes. + +## Registre natif + +`native_program_ids()` expose séparément les dix-huit programmes exécutables runtime natifs, loaders, précompiles et surfaces historiques suivis par `0.4.x`. `native_well_known_account_ids()` expose les comptes natifs non exécutables nécessaires au décodage et à l’audit. Cette liste inclut notamment `vote`, les loaders BPF v1/v2/upgradeable, loader-v4 et les précompiles Ed25519, Secp256k1 et Secp256r1, ainsi que le stateless Slashing Program `S1ashing...`. + +## Registre SPL vérifié + +Le registre distingue les trois générations Memo (`v1`, `v3`, `v4`) et conserve `MEMO_PROGRAM_ID` comme alias de compatibilité vers Memo v3. Il expose également Single Pool, Account Compression, Noop, le registre ElGamal de Token-2022 et un alias explicite pour SPL Name Service. + +## Stake Config + +`StakeConfig11111111111111111111111111111111` est exposé par `STAKE_CONFIG_ACCOUNT_ID`. L’ancien nom `STAKE_CONFIG_PROGRAM_ID` reste un alias déprécié de compatibilité, mais l’adresse ne figure plus dans `native_program_ids()`. diff --git a/migration/khadhroony-bot2-reference/kb_program_ids/src/lib.rs b/migration/khadhroony-bot2-reference/kb_program_ids/src/lib.rs new file mode 100644 index 0000000..1456926 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_program_ids/src/lib.rs @@ -0,0 +1,1584 @@ +// file: kb_program_ids/src/lib.rs +// version: 7 + +//! Shared Solana program identifiers used by the workspace. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +/// One enumerable program identifier entry. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct ProgramIdEntry { + code: &'static str, + program_id: &'static str, +} + +impl ProgramIdEntry { + /// Returns the stable lower snake case registry code. + pub const fn code(&self) -> &'static str { + return self.code; + } + + /// Returns the base58 Solana program identifier. + pub const fn program_id(&self) -> &'static str { + return self.program_id; + } +} + +/// Returns every program identifier registered by this crate. +pub fn registered_program_ids() -> &'static [crate::ProgramIdEntry] { + return crate::PROGRAM_ID_ENTRIES; +} + +/// Returns every executable runtime-native, loader, precompile and historical native program identifier. +pub fn native_program_ids() -> &'static [crate::ProgramIdEntry] { + return crate::NATIVE_PROGRAM_ID_ENTRIES; +} + +/// Returns native well-known accounts that are not executable programs. +pub fn native_well_known_account_ids() -> &'static [crate::ProgramIdEntry] { + return crate::NATIVE_WELL_KNOWN_ACCOUNT_ID_ENTRIES; +} + +/// Finds one registered program by exact base58 identifier. +pub fn find_registered_program_id( + program_id: &str, +) -> std::option::Option<&'static crate::ProgramIdEntry> { + return crate::PROGRAM_ID_ENTRIES + .iter() + .find(|entry| return entry.program_id == program_id); +} + +/// Adapter Saber Decimal Wrapper program identifier. +pub const ADAPTER_SABER_DECIMAL_WRAPPER_PROGRAM_ID: &str = + "DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB"; + +/// Address Lookup Table program identifier. +pub const ADDRESS_LOOKUP_TABLE_PROGRAM_ID: &str = "AddressLookupTab1e1111111111111111111111111"; + +/// Admin Jupiter Lock program identifier. +pub const ADMIN_JUPITER_LOCK_PROGRAM_ID: &str = "LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn"; + +/// Admin Pump Fees program identifier. +pub const ADMIN_PUMP_FEES_PROGRAM_ID: &str = "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ"; + +/// Amm Aldrin V1 program identifier. +pub const AMM_ALDRIN_V1_PROGRAM_ID: &str = "AMM55ShdkoGRB5jVYPjWziwk8m5MpwyDgsMWHaMSQWH6"; + +/// Amm Aldrin V2 program identifier. +pub const AMM_ALDRIN_V2_PROGRAM_ID: &str = "CURVGoZn8zycx6FXwwevgBTB2gVvdbGTEpvMJDbgs2t4"; + +/// Amm Alphaq program identifier. +pub const AMM_ALPHAQ_PROGRAM_ID: &str = "ALPHAQmeA7bjrVuccPsYPiCvsi428SNwte66Srvs4pHA"; + +/// Amm Believe program identifier. +pub const AMM_BELIEVE_PROGRAM_ID: &str = "5qWya6UjwWnGVhdSBL3hyZ7B45jbk6Byt1hwd7ohEGXE"; + +/// Amm Bonk Swap program identifier. +pub const AMM_BONK_SWAP_PROGRAM_ID: &str = "BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p"; + +/// Amm Cropper Finance program identifier. +pub const AMM_CROPPER_FINANCE_PROGRAM_ID: &str = "CTMAxxk34HjKWxQ3QLZK1HpaLXmBveao3ESePXbiyfzh"; + +/// Amm Dexlab Swap program identifier. +pub const AMM_DEXLAB_SWAP_PROGRAM_ID: &str = "DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6N"; + +/// Amm Fluxbeam program identifier. +pub const AMM_FLUXBEAM_PROGRAM_ID: &str = "FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X"; + +/// Amm Goon Fi program identifier. +pub const AMM_GOON_FI_PROGRAM_ID: &str = "goonERTdGsjnkZqWuVjs73BZ3Pb9qoCUdBUL17BnS5j"; + +/// Amm Goon Fi V2 program identifier. +pub const AMM_GOON_FI_V2_PROGRAM_ID: &str = "goonuddtQRrWqqn5nFyczVKaie28f3kDkHWkHtURSLE"; + +/// Amm Goosefx Gamma program identifier. +pub const AMM_GOOSEFX_GAMMA_PROGRAM_ID: &str = "GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT"; + +/// Amm Goosefx V2 program identifier. +pub const AMM_GOOSEFX_V2_PROGRAM_ID: &str = "GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn"; + +/// Amm Guac Swap program identifier. +pub const AMM_GUAC_SWAP_PROGRAM_ID: &str = "Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1"; + +/// Amm Heaven Dex program identifier. +pub const AMM_HEAVEN_DEX_PROGRAM_ID: &str = "HEAVENoP2qxoeuF8Dj2oT1GHEnu49U5mJYkdeC8BAX2o"; + +/// Amm Invariant Swap program identifier. +pub const AMM_INVARIANT_SWAP_PROGRAM_ID: &str = "HyaB3W9q6XdA5xwpU4XnSZV94htfmbmqJXZcEbRaJutt"; + +/// Amm Lifinity Swap program identifier. +pub const AMM_LIFINITY_SWAP_PROGRAM_ID: &str = "EewxydAPCCVuNEyrVN68PuSYdQ7wKn27V9Gjeoi8dy3S"; + +/// Amm Lifinity Swap V2 program identifier. +pub const AMM_LIFINITY_SWAP_V2_PROGRAM_ID: &str = "2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c"; + +/// Amm Marcopolo Swap program identifier. +pub const AMM_MARCOPOLO_SWAP_PROGRAM_ID: &str = "9tKE7Mbmj4mxDjWatikzGAtkoWosiiZX9y6J4Hfm2R8H"; + +/// Amm Metadao Futarchy Amm program identifier. +pub const AMM_METADAO_FUTARCHY_AMM_PROGRAM_ID: &str = + "FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq"; + +/// Amm Metadao V0 5 program identifier. +pub const AMM_METADAO_V0_5_PROGRAM_ID: &str = "AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ"; + +/// Amm Meteora Damm V1 program identifier. +pub const AMM_METEORA_DAMM_V1_PROGRAM_ID: &str = "Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB"; + +/// Amm Meteora Damm V2 program identifier. +pub const AMM_METEORA_DAMM_V2_PROGRAM_ID: &str = "cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG"; + +/// Amm Obric V2 program identifier. +pub const AMM_OBRIC_V2_PROGRAM_ID: &str = "obriQD1zbpyLz95G5n7nJe6a4DPjpFwa5XYPoNm113y"; + +/// Amm One Dex program identifier. +pub const AMM_ONE_DEX_PROGRAM_ID: &str = "DEXYosS6oEGvk8uCDayvwEZz4qEyDJRf9nFgYCaqPMTm"; + +/// Amm Orca Token Swap program identifier. +pub const AMM_ORCA_TOKEN_SWAP_PROGRAM_ID: &str = "DjVE6JNiYqPL2QXyCUUh8rNjHrbz9hXHNYt99MQ59qw1"; + +/// Amm Orca Token Swap V2 program identifier. +pub const AMM_ORCA_TOKEN_SWAP_V2_PROGRAM_ID: &str = "9W959DqEETiGZocYWCQPaJ6sBmUzgfxXfqGeTEdp3aQP"; + +/// Amm Pump Swap program identifier. +pub const AMM_PUMP_SWAP_PROGRAM_ID: &str = "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA"; + +/// Amm Raydium Lp Amm program identifier. +pub const AMM_RAYDIUM_LP_AMM_PROGRAM_ID: &str = "5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h"; + +/// Amm Raydium Lp V2 program identifier. +pub const AMM_RAYDIUM_LP_V2_PROGRAM_ID: &str = "RVKd61ztZW9GUwhRbbLoYVRE5Xf1B2tVscKqwZqXgEr"; + +/// Amm Raydium Lp V3 program identifier. +pub const AMM_RAYDIUM_LP_V3_PROGRAM_ID: &str = "27haf8L6oxUeXrHrgEgsexjSY5hbVUWEmvv9Nyxg8vQv"; + +/// Amm Raydium Lp V4 program identifier. +pub const AMM_RAYDIUM_LP_V4_PROGRAM_ID: &str = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8"; + +/// Amm Saros program identifier. +pub const AMM_SAROS_PROGRAM_ID: &str = "SSwapUtytfBdBn1b9NUGG6foMVPtcWgpRU32HToDUZr"; + +/// Amm Solfi program identifier. +pub const AMM_SOLFI_PROGRAM_ID: &str = "SoLFiHG9TfgtdUXUjWAxi3LtvYuFyDLVhBWxdMZxyCe"; + +/// Amm Solfi V2 program identifier. +pub const AMM_SOLFI_V2_PROGRAM_ID: &str = "SV2EYYJyRz2YhfXwXnhNAevDEui5Q6yrfyo13WtupPF"; + +/// Amm Stepn Dooar Swap program identifier. +pub const AMM_STEPN_DOOAR_SWAP_PROGRAM_ID: &str = "Dooar9JkhdZ7J3LHN3A7YCuoGRUggXhQaG4kijfLGU2j"; + +/// Amm Step Finance Swap program identifier. +pub const AMM_STEP_FINANCE_SWAP_PROGRAM_ID: &str = "SSwpMgqNDsyV7mAgN9ady4bDVu5ySjmmXejXvy2vLt1"; + +/// Amm Swap V1 program identifier. +pub const AMM_SWAP_V1_PROGRAM_ID: &str = "SwaPpA9LAaLfeLi3a68M4DjnLqgtticKg6CnyNwgAC8"; + +/// Amm Vertigo program identifier. +pub const AMM_VERTIGO_PROGRAM_ID: &str = "vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ"; + +/// Amm Virtuals program identifier. +pub const AMM_VIRTUALS_PROGRAM_ID: &str = "5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki"; + +/// Amm Woofi program identifier. +pub const AMM_WOOFI_PROGRAM_ID: &str = "WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb"; + +/// Amm Zero Fi program identifier. +pub const AMM_ZERO_FI_PROGRAM_ID: &str = "ZERor4xhbUycZ6gb9ntrhqscUcZmAbQDjEAtCf4hbZY"; + +/// Amm Zora program identifier. +pub const AMM_ZORA_PROGRAM_ID: &str = "zoRabwLGd5zXaV7Gxacppw8tcceXEiTrSKyNLSaSTUc"; + +/// Associated Token program identifier. +pub const ASSOCIATED_TOKEN_PROGRAM_ID: &str = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL"; + +/// Bpf Loader Deprecated program identifier. +pub const BPF_LOADER_DEPRECATED_PROGRAM_ID: &str = "BPFLoader1111111111111111111111111111111111"; + +/// Bpf Loader version 2 program identifier. +pub const BPF_LOADER_PROGRAM_ID: &str = "BPFLoader2111111111111111111111111111111111"; + +/// Bpf Loader Upgradeable program identifier. +pub const BPF_LOADER_UPGRADEABLE_PROGRAM_ID: &str = "BPFLoaderUpgradeab1e11111111111111111111111"; + +/// Bridge Circle Cctp Cctp Token Messenger Minter program identifier. +pub const BRIDGE_CIRCLE_CCTP_CCTP_TOKEN_MESSENGER_MINTER_PROGRAM_ID: &str = + "CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3"; + +/// Bridge Circle Cctp Cctp Token Messenger Minter V2 program identifier. +pub const BRIDGE_CIRCLE_CCTP_CCTP_TOKEN_MESSENGER_MINTER_V2_PROGRAM_ID: &str = + "CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe"; + +/// Bridge De Bridge Destination program identifier. +pub const BRIDGE_DE_BRIDGE_DESTINATION_PROGRAM_ID: &str = + "dst5MGcFPoBeREFAA5E3tU5ij8m5uVYwkzkSAbsLbNo"; + +/// Bridge De Bridge Source program identifier. +pub const BRIDGE_DE_BRIDGE_SOURCE_PROGRAM_ID: &str = "src5qyZHqTqecJV4aY6Cb6zDZLMDzrDKKezs22MPHr4"; + +/// Bridge Layer Zero Endpoint program identifier. +pub const BRIDGE_LAYER_ZERO_ENDPOINT_PROGRAM_ID: &str = + "76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6"; + +/// Bridge Layer Zero Executor program identifier. +pub const BRIDGE_LAYER_ZERO_EXECUTOR_PROGRAM_ID: &str = + "6doghB248px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn"; + +/// Bridge Wormhole program identifier. +pub const BRIDGE_WORMHOLE_PROGRAM_ID: &str = "wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb"; + +/// Clmm Byreal program identifier. +pub const CLMM_BYREAL_PROGRAM_ID: &str = "REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2"; + +/// Clmm Crema Finance program identifier. +pub const CLMM_CREMA_FINANCE_PROGRAM_ID: &str = "CLMM9tUoggJu2wagPkkqs9eFG4BWhVBZWkP1qv3Sp7tR"; + +/// Clmm Cropper Whirlpool program identifier. +pub const CLMM_CROPPER_WHIRLPOOL_PROGRAM_ID: &str = "H8W3ctz92svYg6mkn1UtGfu2aQr2fnUFHM1RhScEtQDt"; + +/// Clmm Fusion program identifier. +pub const CLMM_FUSION_PROGRAM_ID: &str = "fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9"; + +/// Clmm Orca Whirlpool program identifier. +pub const CLMM_ORCA_WHIRLPOOL_PROGRAM_ID: &str = "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc"; + +/// Clmm Pancake Swap program identifier. +pub const CLMM_PANCAKE_SWAP_PROGRAM_ID: &str = "HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq"; + +/// Clmm Raydium program identifier. +pub const CLMM_RAYDIUM_PROGRAM_ID: &str = "CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK"; + +/// Clmm Stabble program identifier. +pub const CLMM_STABBLE_PROGRAM_ID: &str = "6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6"; + +/// Compute Budget program identifier. +pub const COMPUTE_BUDGET_PROGRAM_ID: &str = "ComputeBudget111111111111111111111111111111"; + +/// Config program identifier. +pub const CONFIG_PROGRAM_ID: &str = "Config1111111111111111111111111111111111111"; + +/// Cpmm Raydium program identifier. +pub const CPMM_RAYDIUM_PROGRAM_ID: &str = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C"; + +/// Dlmm Meteora program identifier. +pub const DLMM_METEORA_PROGRAM_ID: &str = "LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo"; + +/// Ed25519 program identifier. +pub const ED25519_PROGRAM_ID: &str = "Ed25519SigVerify111111111111111111111111111"; + +/// Feature program identifier. +pub const FEATURE_PROGRAM_ID: &str = "Feature111111111111111111111111111111111111"; + +/// Fees Bags Fee Share V1 program identifier. +pub const FEES_BAGS_FEE_SHARE_V1_PROGRAM_ID: &str = "FEEhPbKVKnco9EXnaY3i4R5rQVUx91wgVfu8qokixywi"; + +/// Fees Bags Fee Share V2 program identifier. +pub const FEES_BAGS_FEE_SHARE_V2_PROGRAM_ID: &str = "FEE2tBhCKAt7shrod19QttSVREUYPiyMzoku1mL1gqVK"; + +/// Governance Metadao Bid Wall program identifier. +pub const GOVERNANCE_METADAO_BID_WALL_PROGRAM_ID: &str = + "WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx"; + +/// Incinerator program identifier. +pub const INCINERATOR_PROGRAM_ID: &str = "1nc1nerator11111111111111111111111111111111"; + +/// Launchpad Boop Fun program identifier. +pub const LAUNCHPAD_BOOP_FUN_PROGRAM_ID: &str = "boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4"; + +/// Launchpad Letsbonk Fun program identifier. +pub const LAUNCHPAD_LETSBONK_FUN_PROGRAM_ID: &str = "FfYek5vEz23cMkWsdJwG2oa6EphsvXSHrGpdALN4g6W1"; + +/// Launchpad Metadao Ico program identifier. +pub const LAUNCHPAD_METADAO_ICO_PROGRAM_ID: &str = "moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM"; + +/// Launchpad Meteora Dbc program identifier. +pub const LAUNCHPAD_METEORA_DBC_PROGRAM_ID: &str = "dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN"; + +/// Launchpad Moonit program identifier. +pub const LAUNCHPAD_MOONIT_PROGRAM_ID: &str = "MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG"; + +/// Launchpad Moonshot Token Authority program identifier. +pub const LAUNCHPAD_MOONSHOT_TOKEN_AUTHORITY_PROGRAM_ID: &str = + "7rtiKSUDLBm59b1SBmD9oajcP8xE64vAGSMbAN5CXy1q"; + +/// Launchpad Orca Wavebreak program identifier. +pub const LAUNCHPAD_ORCA_WAVEBREAK_PROGRAM_ID: &str = "waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF"; + +/// Launchpad Printr program identifier. +pub const LAUNCHPAD_PRINTR_PROGRAM_ID: &str = "T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint"; + +/// Launchpad Pump Fun program identifier. +pub const LAUNCHPAD_PUMP_FUN_PROGRAM_ID: &str = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P"; + +/// Launchpad Pump Pumpup Ai program identifier. +pub const LAUNCHPAD_PUMP_PUMPUP_AI_PROGRAM_ID: &str = "PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd"; + +/// Launchpad Raydium Launchlab program identifier. +pub const LAUNCHPAD_RAYDIUM_LAUNCHLAB_PROGRAM_ID: &str = + "LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj"; + +/// Lending Clone program identifier. +pub const LENDING_CLONE_PROGRAM_ID: &str = "C1onEW2kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr"; + +/// Lending Jupiter Lend Borrow program identifier. +pub const LENDING_JUPITER_LEND_BORROW_PROGRAM_ID: &str = + "jupr81YtYssSyPt8jbnGuiWon5f6x9TcDEFxYe3Bdzi"; + +/// Lending Jupiter Lend Earn program identifier. +pub const LENDING_JUPITER_LEND_EARN_PROGRAM_ID: &str = + "jup3YeL8QhtSx1e253b2FDvsMNC87fDrgQZivbrndc9"; + +/// Lending Jupiter Lend Flash Loan program identifier. +pub const LENDING_JUPITER_LEND_FLASH_LOAN_PROGRAM_ID: &str = + "jupgfSgfuAXv4B6R2Uxu85Z1qdzgju79s6MfZekN6XS"; + +/// Lending Jupiter Lend Liquidity program identifier. +pub const LENDING_JUPITER_LEND_LIQUIDITY_PROGRAM_ID: &str = + "jupeiUmn818Jg1ekPURTpr4mFo29p46vygyykFJ3wZC"; + +/// Lending Kamino program identifier. +pub const LENDING_KAMINO_PROGRAM_ID: &str = "KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD"; + +/// Lending Marginfi program identifier. +pub const LENDING_MARGINFI_PROGRAM_ID: &str = "MFLQPPPPjNinkdKoy2odNFBhvpY43XtCDZjBwG2fwn5"; + +/// Lending Marginfi V2 program identifier. +pub const LENDING_MARGINFI_V2_PROGRAM_ID: &str = "MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA"; + +/// Lending Sharky Fi program identifier. +pub const LENDING_SHARKY_FI_PROGRAM_ID: &str = "SHARKobtfF1bHhxD2eqftjHBdVSCbKo9JtgK71FhELP"; + +/// Lending Solend Protocol program identifier. +pub const LENDING_SOLEND_PROTOCOL_PROGRAM_ID: &str = "So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo"; + +/// Loader V4 program identifier. +pub const LOADER_V4_PROGRAM_ID: &str = "LoaderV411111111111111111111111111111111111"; + +/// Lock Raydium Lp program identifier. +pub const LOCK_RAYDIUM_LP_PROGRAM_ID: &str = "LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE"; + +/// Legacy SPL Memo v1 program identifier. +pub const SPL_MEMO_V1_PROGRAM_ID: &str = "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo"; + +/// SPL Memo v3 program identifier. +pub const SPL_MEMO_V3_PROGRAM_ID: &str = "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr"; + +/// SPL Memo v4 program identifier. +pub const SPL_MEMO_V4_PROGRAM_ID: &str = "Memo4c2pN8afCj432Lb7RMVKi9PbQnnW7ewFFaV3oAH"; + +/// Compatibility alias for the previously registered SPL Memo v3 identifier. +pub const MEMO_PROGRAM_ID: &str = crate::SPL_MEMO_V3_PROGRAM_ID; + +/// Metadata Metaplex Token Metadata program identifier. +pub const METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID: &str = + "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"; + +/// Metadata Name Service program identifier. +pub const METADATA_NAME_SERVICE_PROGRAM_ID: &str = "namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX"; + +/// SPL Name Service program identifier compatibility alias. +pub const SPL_NAME_SERVICE_PROGRAM_ID: &str = crate::METADATA_NAME_SERVICE_PROGRAM_ID; + +/// Native Loader program identifier. +pub const NATIVE_LOADER_PROGRAM_ID: &str = "NativeLoader1111111111111111111111111111111"; + +/// Nft Metaplex Bubblegum program identifier. +pub const NFT_METAPLEX_BUBBLEGUM_PROGRAM_ID: &str = "BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY"; + +/// Nft Metaplex Mpl Core program identifier. +pub const NFT_METAPLEX_MPL_CORE_PROGRAM_ID: &str = "CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d"; + +/// Oracle Jupiter Prediction Market program identifier. +pub const ORACLE_JUPITER_PREDICTION_MARKET_PROGRAM_ID: &str = + "3ZZuTbwC6aJbvteyVxXUS7gtFYdf7AuXeitx6VyvjvUp"; + +/// Orderbook Jupiter Limit Order program identifier. +pub const ORDERBOOK_JUPITER_LIMIT_ORDER_PROGRAM_ID: &str = + "jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu"; + +/// Orderbook Jupiter Limit Order V2 program identifier. +pub const ORDERBOOK_JUPITER_LIMIT_ORDER_V2_PROGRAM_ID: &str = + "j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X"; + +/// Orderbook Manifest V1 program identifier. +pub const ORDERBOOK_MANIFEST_V1_PROGRAM_ID: &str = "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms"; + +/// Orderbook Openbook V2 program identifier. +pub const ORDERBOOK_OPENBOOK_V2_PROGRAM_ID: &str = "opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb"; + +/// Orderbook Phoenix V1 program identifier. +pub const ORDERBOOK_PHOENIX_V1_PROGRAM_ID: &str = "PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY"; + +/// Perpetuals Drift V2 program identifier. +pub const PERPETUALS_DRIFT_V2_PROGRAM_ID: &str = "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH"; + +/// Perpetuals Jupiter program identifier. +pub const PERPETUALS_JUPITER_PROGRAM_ID: &str = "PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu"; + +/// Perpetuals Zeta Matching Engine program identifier. +pub const PERPETUALS_ZETA_MATCHING_ENGINE_PROGRAM_ID: &str = + "zDEXqXEG7gAyxb1Kg9mK5fPnUdENCGKzWrM21RMdWRq"; + +/// Perpetuals Zeta program identifier. +pub const PERPETUALS_ZETA_PROGRAM_ID: &str = "ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD"; + +/// Router Dflow Aggregator V4 program identifier. +pub const ROUTER_DFLOW_AGGREGATOR_V4_PROGRAM_ID: &str = + "DF1ow4tspfHX9WJsAb9epbkA8hmpSEAtxXy1V27QBH"; + +/// Router Jupiter Aggregator V4 program identifier. +pub const ROUTER_JUPITER_AGGREGATOR_V4_PROGRAM_ID: &str = + "JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB"; + +/// Router Jupiter Aggregator V6 program identifier. +pub const ROUTER_JUPITER_AGGREGATOR_V6_PROGRAM_ID: &str = + "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4"; + +/// Router Jupiter Dca program identifier. +pub const ROUTER_JUPITER_DCA_PROGRAM_ID: &str = "DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M"; + +/// Router Okx Labs V1 program identifier. +pub const ROUTER_OKX_LABS_V1_PROGRAM_ID: &str = "6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma"; + +/// Router Okx Labs V2 program identifier. +pub const ROUTER_OKX_LABS_V2_PROGRAM_ID: &str = "proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u"; + +/// Router Raydium Amm Routing program identifier. +pub const ROUTER_RAYDIUM_AMM_ROUTING_PROGRAM_ID: &str = + "routeUGWgWzqBWFcrCfv8tritsqukccJPu3q5GPP3xS"; + +/// Router Sanctum program identifier. +pub const ROUTER_SANCTUM_PROGRAM_ID: &str = "stkitrT1Uoy18Dk1fTrgPw8W6MVzoCfYoAFT4MLsmhq"; + +/// Router Tessera V program identifier. +pub const ROUTER_TESSERA_V_PROGRAM_ID: &str = "TessVdML9pBGgG9yGks7o4HewRaXVAMuoVj4x83GLQH"; + +/// Router Titan Exchange Router program identifier. +pub const ROUTER_TITAN_EXCHANGE_ROUTER_PROGRAM_ID: &str = + "T1TANpTeScyeqVzzgNViGDNrkQ6qHz9KrSBS4aNXvGT"; + +/// Rwa Ondo Global Markets program identifier. +pub const RWA_ONDO_GLOBAL_MARKETS_PROGRAM_ID: &str = "XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm"; + +/// Secp256K1 program identifier. +pub const SECP256K1_PROGRAM_ID: &str = "KeccakSecp256k11111111111111111111111111111"; + +/// Secp256R1 program identifier. +pub const SECP256R1_PROGRAM_ID: &str = "Secp256r1SigVerify1111111111111111111111111"; + +/// SPL Account Compression program identifier. +pub const SPL_ACCOUNT_COMPRESSION_PROGRAM_ID: &str = "cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK"; + +/// SPL Noop program identifier. +pub const SPL_NOOP_PROGRAM_ID: &str = "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV"; + +/// SPL Single Pool program identifier. +pub const SPL_SINGLE_POOL_PROGRAM_ID: &str = "SVSPxpvHdN29nkVg9rPapPNDddN5DipNLRUFhyjFThE"; + +/// SPL Stake Pool program identifier. +pub const SPL_STAKE_POOL_PROGRAM_ID: &str = "SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy"; + +/// SPL Token-2022 ElGamal public-key registry program identifier. +pub const SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID: &str = + "regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg"; + +/// Spl Token 2022 program identifier. +pub const SPL_TOKEN_2022_PROGRAM_ID: &str = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb"; + +/// Spl Token program identifier. +pub const SPL_TOKEN_PROGRAM_ID: &str = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"; + +/// Stable Swap Hylo Exchange program identifier. +pub const STABLE_SWAP_HYLO_EXCHANGE_PROGRAM_ID: &str = + "HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn"; + +/// Stable Swap Jupiter Stable program identifier. +pub const STABLE_SWAP_JUPITER_STABLE_PROGRAM_ID: &str = + "JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr"; + +/// Stable Swap Mercurial program identifier. +pub const STABLE_SWAP_MERCURIAL_PROGRAM_ID: &str = "MERLuDFBMmsHnsBPZw2sDQZHvXFMwp8EdjudcU2HKky"; + +/// Stable Swap Numeraire program identifier. +pub const STABLE_SWAP_NUMERAIRE_PROGRAM_ID: &str = "NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P"; + +/// Stable Swap Saber program identifier. +pub const STABLE_SWAP_SABER_PROGRAM_ID: &str = "SSwpkEEcbUqx4vtoEByFjSkhKdCT862DNVb52nZg1UZ"; + +/// Stable Swap Stabble program identifier. +pub const STABLE_SWAP_STABBLE_PROGRAM_ID: &str = "swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ"; + +/// Deprecated Stake Program configuration account identifier. +pub const STAKE_CONFIG_ACCOUNT_ID: &str = "StakeConfig11111111111111111111111111111111"; + +/// Compatibility alias for the deprecated Stake Program configuration account identifier. +#[deprecated(note = "use STAKE_CONFIG_ACCOUNT_ID; this address is not an executable program")] +pub const STAKE_CONFIG_PROGRAM_ID: &str = crate::STAKE_CONFIG_ACCOUNT_ID; + +/// Enshrined stateless Slashing Program identifier. +pub const SLASHING_PROGRAM_ID: &str = "S1ashing11111111111111111111111111111111111"; + +/// Stake program identifier. +pub const STAKE_PROGRAM_ID: &str = "Stake11111111111111111111111111111111111111"; + +/// Staking Jito Tip Distribution program identifier. +pub const STAKING_JITO_TIP_DISTRIBUTION_PROGRAM_ID: &str = + "4R3gSG8BpU4t19KYj8CfnbtRpnT8gtk4dvTHxVRwc2r7"; + +/// Staking Kamino Farm program identifier. +pub const STAKING_KAMINO_FARM_PROGRAM_ID: &str = "FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr"; + +/// Staking Marinade Finance program identifier. +pub const STAKING_MARINADE_FINANCE_PROGRAM_ID: &str = "MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD"; + +/// Staking Sanctum Multi Validator Spl Stake Pool program identifier. +pub const STAKING_SANCTUM_MULTI_VALIDATOR_SPL_STAKE_POOL_PROGRAM_ID: &str = + "SPMBzsVUuoHA4Jm6KunbsotaahvVikZs1JyTW6iJvbn"; + +/// Staking Sanctum Single Validator Spl Stake Pool program identifier. +pub const STAKING_SANCTUM_SINGLE_VALIDATOR_SPL_STAKE_POOL_PROGRAM_ID: &str = + "SP12tWFxD9oJsVWNavTTBZvMbA6gkAmxtVgxdqvyvhY"; + +/// Staking Solayer program identifier. +pub const STAKING_SOLAYER_PROGRAM_ID: &str = "sSo1iU21jBrU9VaJ8PJib1MtorefUV4fzC9GURa2KNn"; + +/// System program identifier. +pub const SYSTEM_PROGRAM_ID: &str = "11111111111111111111111111111111"; + +/// Sysvar Clock program identifier. +pub const SYSVAR_CLOCK_PROGRAM_ID: &str = "SysvarC1ock11111111111111111111111111111111"; + +/// Sysvar Epoch Rewards program identifier. +pub const SYSVAR_EPOCH_REWARDS_PROGRAM_ID: &str = "SysvarEpochRewards1111111111111111111111111"; + +/// Sysvar Epoch Schedule program identifier. +pub const SYSVAR_EPOCH_SCHEDULE_PROGRAM_ID: &str = "SysvarEpochSchedu1e111111111111111111111111"; + +/// Sysvar Fees program identifier. +pub const SYSVAR_FEES_PROGRAM_ID: &str = "SysvarFees111111111111111111111111111111111"; + +/// Sysvar Instructions program identifier. +pub const SYSVAR_INSTRUCTIONS_PROGRAM_ID: &str = "Sysvar1nstructions1111111111111111111111111"; + +/// Sysvar Last Restart Slot program identifier. +pub const SYSVAR_LAST_RESTART_SLOT_PROGRAM_ID: &str = "SysvarLastRestartS1ot1111111111111111111111"; + +/// Sysvar program identifier. +pub const SYSVAR_PROGRAM_ID: &str = "Sysvar1111111111111111111111111111111111111"; + +/// Sysvar Recent Blockhashes program identifier. +pub const SYSVAR_RECENT_BLOCKHASHES_PROGRAM_ID: &str = + "SysvarRecentB1ockHashes11111111111111111111"; + +/// Sysvar Rent program identifier. +pub const SYSVAR_RENT_PROGRAM_ID: &str = "SysvarRent111111111111111111111111111111111"; + +/// Sysvar Rewards program identifier. +pub const SYSVAR_REWARDS_PROGRAM_ID: &str = "SysvarRewards111111111111111111111111111111"; + +/// Sysvar Slot Hashes program identifier. +pub const SYSVAR_SLOT_HASHES_PROGRAM_ID: &str = "SysvarS1otHashes111111111111111111111111111"; + +/// Sysvar Slot History program identifier. +pub const SYSVAR_SLOT_HISTORY_PROGRAM_ID: &str = "SysvarS1otHistory11111111111111111111111111"; + +/// Sysvar Stake History program identifier. +pub const SYSVAR_STAKE_HISTORY_PROGRAM_ID: &str = "SysvarStakeHistory1111111111111111111111111"; + +/// Treasury Helium Treasury Management program identifier. +pub const TREASURY_HELIUM_TREASURY_MANAGEMENT_PROGRAM_ID: &str = + "treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5"; + +/// Unknown Aquifer program identifier. +pub const UNKNOWN_AQUIFER_PROGRAM_ID: &str = "AQU1FRd7papthgdrwPTTq5JacJh8YtwEXaBfKU3bTz45"; + +/// Unknown Axiom Trade program identifier. +pub const UNKNOWN_AXIOM_TRADE_PROGRAM_ID: &str = "FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9"; + +/// Unknown Bison Fi program identifier. +pub const UNKNOWN_BISON_FI_PROGRAM_ID: &str = "BiSoNHVpsVZW2F7rx2eQ59yQwKxzU5NvBcmKshCSUypi"; + +/// Unknown Gavel program identifier. +pub const UNKNOWN_GAVEL_PROGRAM_ID: &str = "srAMMzfVHVAtgSJc8iH6CfKzuWuUTzLHVCE81QU1rgi"; + +/// Unknown Hawk Fi program identifier. +pub const UNKNOWN_HAWK_FI_PROGRAM_ID: &str = "FqGg2Y1FNxMiGd51Q6UETixQWkF5fB92MysbYogRJb3P"; + +/// Unknown Humidi Fi program identifier. +pub const UNKNOWN_HUMIDI_FI_PROGRAM_ID: &str = "9H6tua7jkLhdm3w8BvgpTn5LZNU7g4ZynDmCiNN3q6Rp"; + +/// Unknown Jup Studio Authority program identifier. +pub const UNKNOWN_JUP_STUDIO_AUTHORITY_PROGRAM_ID: &str = + "8rE9CtCjwhSmbwL5fbJBtRFsS3ohfMcDFeTTC7t4ciUA"; + +/// Unknown Ore V3 program identifier. +pub const UNKNOWN_ORE_V3_PROGRAM_ID: &str = "oreV3EG1i9BEgiAJ8b177Z2S2rMarzak4NMv1kULvWv"; + +/// Unknown Penguin Finance program identifier. +pub const UNKNOWN_PENGUIN_FINANCE_PROGRAM_ID: &str = "PSwapMdSai8tjrEXcxFeQth87xC4rRsa4VA5mhGhXkP"; + +/// Unknown Sanctum S Controller program identifier. +pub const UNKNOWN_SANCTUM_S_CONTROLLER_PROGRAM_ID: &str = + "5ocnV1qiCgaQR8Jb8xWnVbApfaygJ8tNoZfgPwsgx9kx"; + +/// Unknown Scorch program identifier. +pub const UNKNOWN_SCORCH_PROGRAM_ID: &str = "SCoRcH8c2dpjvcJD6FiPbCSQyQgu3PcUAWj2Xxx3mqn"; + +/// Unknown Swig program identifier. +pub const UNKNOWN_SWIG_PROGRAM_ID: &str = "swigypWHEksbC64pWKwah1WTeh9JXwx8H1rJHLdbQMB"; + +/// Vault Carrot Defi program identifier. +pub const VAULT_CARROT_DEFI_PROGRAM_ID: &str = "CarrotwivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s"; + +/// Vault Hylo Stability Pool program identifier. +pub const VAULT_HYLO_STABILITY_POOL_PROGRAM_ID: &str = + "HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ"; + +/// Vault Kamino program identifier. +pub const VAULT_KAMINO_PROGRAM_ID: &str = "kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr"; + +/// Vault Kamino V2 program identifier. +pub const VAULT_KAMINO_V2_PROGRAM_ID: &str = "KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd"; + +/// Vault Kamino Yvaults program identifier. +pub const VAULT_KAMINO_YVAULTS_PROGRAM_ID: &str = "6LtLpnUFNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc"; + +/// Vault Meteora program identifier. +pub const VAULT_METEORA_PROGRAM_ID: &str = "24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi"; + +/// Vesting Streamflow program identifier. +pub const VESTING_STREAMFLOW_PROGRAM_ID: &str = "strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m"; + +/// Vote program identifier. +pub const VOTE_PROGRAM_ID: &str = "Vote111111111111111111111111111111111111111"; + +/// Wallet Jupiter Apepro Smart Wallet program identifier. +pub const WALLET_JUPITER_APEPRO_SMART_WALLET_PROGRAM_ID: &str = + "JSW99DKmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw"; + +/// Weighted Swap Stabble program identifier. +pub const WEIGHTED_SWAP_STABBLE_PROGRAM_ID: &str = "swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW"; + +/// Zk Elgamal Proof program identifier. +pub const ZK_ELGAMAL_PROOF_PROGRAM_ID: &str = "ZkE1Gama1Proof11111111111111111111111111111"; + +/// Zk Token Proof program identifier. +pub const ZK_TOKEN_PROOF_PROGRAM_ID: &str = "ZkTokenProof1111111111111111111111111111111"; + +const NATIVE_PROGRAM_ID_ENTRIES: &[crate::ProgramIdEntry] = &[ + crate::ProgramIdEntry { + code: "address_lookup_table", + program_id: crate::ADDRESS_LOOKUP_TABLE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "bpf_loader_deprecated", + program_id: crate::BPF_LOADER_DEPRECATED_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "bpf_loader", + program_id: crate::BPF_LOADER_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "bpf_loader_upgradeable", + program_id: crate::BPF_LOADER_UPGRADEABLE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "compute_budget", + program_id: crate::COMPUTE_BUDGET_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "config", + program_id: crate::CONFIG_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "ed25519", + program_id: crate::ED25519_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "feature", + program_id: crate::FEATURE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "loader_v4", + program_id: crate::LOADER_V4_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "native_loader", + program_id: crate::NATIVE_LOADER_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "secp256k1", + program_id: crate::SECP256K1_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "secp256r1", + program_id: crate::SECP256R1_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "slashing", + program_id: crate::SLASHING_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "stake", + program_id: crate::STAKE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "system", + program_id: crate::SYSTEM_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "vote", + program_id: crate::VOTE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "zk_elgamal_proof", + program_id: crate::ZK_ELGAMAL_PROOF_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "zk_token_proof", + program_id: crate::ZK_TOKEN_PROOF_PROGRAM_ID, + }, +]; + +const NATIVE_WELL_KNOWN_ACCOUNT_ID_ENTRIES: &[crate::ProgramIdEntry] = &[ + crate::ProgramIdEntry { + code: "incinerator", + program_id: crate::INCINERATOR_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "stake_config", + program_id: crate::STAKE_CONFIG_ACCOUNT_ID, + }, +]; + +const PROGRAM_ID_ENTRIES: &[crate::ProgramIdEntry] = &[ + crate::ProgramIdEntry { + code: "adapter_saber_decimal_wrapper", + program_id: crate::ADAPTER_SABER_DECIMAL_WRAPPER_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "address_lookup_table", + program_id: crate::ADDRESS_LOOKUP_TABLE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "admin_jupiter_lock", + program_id: crate::ADMIN_JUPITER_LOCK_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "admin_pump_fees", + program_id: crate::ADMIN_PUMP_FEES_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_aldrin_v1", + program_id: crate::AMM_ALDRIN_V1_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_aldrin_v2", + program_id: crate::AMM_ALDRIN_V2_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_alphaq", + program_id: crate::AMM_ALPHAQ_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_believe", + program_id: crate::AMM_BELIEVE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_bonk_swap", + program_id: crate::AMM_BONK_SWAP_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_cropper_finance", + program_id: crate::AMM_CROPPER_FINANCE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_dexlab_swap", + program_id: crate::AMM_DEXLAB_SWAP_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_fluxbeam", + program_id: crate::AMM_FLUXBEAM_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_goon_fi", + program_id: crate::AMM_GOON_FI_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_goon_fi_v2", + program_id: crate::AMM_GOON_FI_V2_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_goosefx_gamma", + program_id: crate::AMM_GOOSEFX_GAMMA_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_goosefx_v2", + program_id: crate::AMM_GOOSEFX_V2_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_guac_swap", + program_id: crate::AMM_GUAC_SWAP_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_heaven_dex", + program_id: crate::AMM_HEAVEN_DEX_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_invariant_swap", + program_id: crate::AMM_INVARIANT_SWAP_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_lifinity_swap", + program_id: crate::AMM_LIFINITY_SWAP_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_lifinity_swap_v2", + program_id: crate::AMM_LIFINITY_SWAP_V2_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_marcopolo_swap", + program_id: crate::AMM_MARCOPOLO_SWAP_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_metadao_futarchy_amm", + program_id: crate::AMM_METADAO_FUTARCHY_AMM_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_metadao_v0_5", + program_id: crate::AMM_METADAO_V0_5_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_meteora_damm_v1", + program_id: crate::AMM_METEORA_DAMM_V1_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_meteora_damm_v2", + program_id: crate::AMM_METEORA_DAMM_V2_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_obric_v2", + program_id: crate::AMM_OBRIC_V2_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_one_dex", + program_id: crate::AMM_ONE_DEX_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_orca_token_swap", + program_id: crate::AMM_ORCA_TOKEN_SWAP_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_orca_token_swap_v2", + program_id: crate::AMM_ORCA_TOKEN_SWAP_V2_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_pump_swap", + program_id: crate::AMM_PUMP_SWAP_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_raydium_lp_amm", + program_id: crate::AMM_RAYDIUM_LP_AMM_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_raydium_lp_v2", + program_id: crate::AMM_RAYDIUM_LP_V2_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_raydium_lp_v3", + program_id: crate::AMM_RAYDIUM_LP_V3_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_raydium_lp_v4", + program_id: crate::AMM_RAYDIUM_LP_V4_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_saros", + program_id: crate::AMM_SAROS_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_solfi", + program_id: crate::AMM_SOLFI_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_solfi_v2", + program_id: crate::AMM_SOLFI_V2_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_stepn_dooar_swap", + program_id: crate::AMM_STEPN_DOOAR_SWAP_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_step_finance_swap", + program_id: crate::AMM_STEP_FINANCE_SWAP_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_swap_v1", + program_id: crate::AMM_SWAP_V1_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_vertigo", + program_id: crate::AMM_VERTIGO_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_virtuals", + program_id: crate::AMM_VIRTUALS_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_woofi", + program_id: crate::AMM_WOOFI_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_zero_fi", + program_id: crate::AMM_ZERO_FI_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "amm_zora", + program_id: crate::AMM_ZORA_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "associated_token", + program_id: crate::ASSOCIATED_TOKEN_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "bpf_loader_deprecated", + program_id: crate::BPF_LOADER_DEPRECATED_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "bpf_loader", + program_id: crate::BPF_LOADER_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "bpf_loader_upgradeable", + program_id: crate::BPF_LOADER_UPGRADEABLE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "bridge_circle_cctp_token_messenger_minter", + program_id: crate::BRIDGE_CIRCLE_CCTP_CCTP_TOKEN_MESSENGER_MINTER_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "bridge_circle_cctp_token_messenger_minter_v2", + program_id: crate::BRIDGE_CIRCLE_CCTP_CCTP_TOKEN_MESSENGER_MINTER_V2_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "bridge_de_bridge_destination", + program_id: crate::BRIDGE_DE_BRIDGE_DESTINATION_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "bridge_de_bridge_source", + program_id: crate::BRIDGE_DE_BRIDGE_SOURCE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "bridge_layer_zero_endpoint", + program_id: crate::BRIDGE_LAYER_ZERO_ENDPOINT_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "bridge_layer_zero_executor", + program_id: crate::BRIDGE_LAYER_ZERO_EXECUTOR_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "bridge_wormhole", + program_id: crate::BRIDGE_WORMHOLE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "clmm_byreal", + program_id: crate::CLMM_BYREAL_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "clmm_crema_finance", + program_id: crate::CLMM_CREMA_FINANCE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "clmm_cropper_whirlpool", + program_id: crate::CLMM_CROPPER_WHIRLPOOL_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "clmm_fusion", + program_id: crate::CLMM_FUSION_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "clmm_orca_whirlpool", + program_id: crate::CLMM_ORCA_WHIRLPOOL_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "clmm_pancake_swap", + program_id: crate::CLMM_PANCAKE_SWAP_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "clmm_raydium", + program_id: crate::CLMM_RAYDIUM_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "clmm_stabble", + program_id: crate::CLMM_STABBLE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "compute_budget", + program_id: crate::COMPUTE_BUDGET_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "config", + program_id: crate::CONFIG_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "cpmm_raydium", + program_id: crate::CPMM_RAYDIUM_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "dlmm_meteora", + program_id: crate::DLMM_METEORA_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "ed25519", + program_id: crate::ED25519_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "feature", + program_id: crate::FEATURE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "fees_bags_fee_share_v1", + program_id: crate::FEES_BAGS_FEE_SHARE_V1_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "fees_bags_fee_share_v2", + program_id: crate::FEES_BAGS_FEE_SHARE_V2_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "governance_metadao_bid_wall", + program_id: crate::GOVERNANCE_METADAO_BID_WALL_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "incinerator", + program_id: crate::INCINERATOR_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "launchpad_boop_fun", + program_id: crate::LAUNCHPAD_BOOP_FUN_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "launchpad_letsbonk_fun", + program_id: crate::LAUNCHPAD_LETSBONK_FUN_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "launchpad_metadao_ico", + program_id: crate::LAUNCHPAD_METADAO_ICO_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "launchpad_meteora_dbc", + program_id: crate::LAUNCHPAD_METEORA_DBC_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "launchpad_moonit", + program_id: crate::LAUNCHPAD_MOONIT_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "launchpad_moonshot_token_authority", + program_id: crate::LAUNCHPAD_MOONSHOT_TOKEN_AUTHORITY_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "launchpad_orca_wavebreak", + program_id: crate::LAUNCHPAD_ORCA_WAVEBREAK_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "launchpad_printr", + program_id: crate::LAUNCHPAD_PRINTR_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "launchpad_pump_fun", + program_id: crate::LAUNCHPAD_PUMP_FUN_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "launchpad_pump_pumpup_ai", + program_id: crate::LAUNCHPAD_PUMP_PUMPUP_AI_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "launchpad_raydium_launchlab", + program_id: crate::LAUNCHPAD_RAYDIUM_LAUNCHLAB_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "lending_clone", + program_id: crate::LENDING_CLONE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "lending_jupiter_lend_borrow", + program_id: crate::LENDING_JUPITER_LEND_BORROW_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "lending_jupiter_lend_earn", + program_id: crate::LENDING_JUPITER_LEND_EARN_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "lending_jupiter_lend_flash_loan", + program_id: crate::LENDING_JUPITER_LEND_FLASH_LOAN_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "lending_jupiter_lend_liquidity", + program_id: crate::LENDING_JUPITER_LEND_LIQUIDITY_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "lending_kamino", + program_id: crate::LENDING_KAMINO_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "lending_marginfi", + program_id: crate::LENDING_MARGINFI_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "lending_marginfi_v2", + program_id: crate::LENDING_MARGINFI_V2_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "lending_sharky_fi", + program_id: crate::LENDING_SHARKY_FI_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "lending_solend_protocol", + program_id: crate::LENDING_SOLEND_PROTOCOL_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "loader_v4", + program_id: crate::LOADER_V4_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "lock_raydium_lp", + program_id: crate::LOCK_RAYDIUM_LP_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "spl_memo_v1", + program_id: crate::SPL_MEMO_V1_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "spl_memo_v3", + program_id: crate::SPL_MEMO_V3_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "spl_memo_v4", + program_id: crate::SPL_MEMO_V4_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "metadata_metaplex_token_metadata", + program_id: crate::METADATA_METAPLEX_TOKEN_METADATA_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "metadata_name_service", + program_id: crate::METADATA_NAME_SERVICE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "native_loader", + program_id: crate::NATIVE_LOADER_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "nft_metaplex_bubblegum", + program_id: crate::NFT_METAPLEX_BUBBLEGUM_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "nft_metaplex_mpl_core", + program_id: crate::NFT_METAPLEX_MPL_CORE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "oracle_jupiter_prediction_market", + program_id: crate::ORACLE_JUPITER_PREDICTION_MARKET_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "orderbook_jupiter_limit_order", + program_id: crate::ORDERBOOK_JUPITER_LIMIT_ORDER_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "orderbook_jupiter_limit_order_v2", + program_id: crate::ORDERBOOK_JUPITER_LIMIT_ORDER_V2_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "orderbook_manifest_v1", + program_id: crate::ORDERBOOK_MANIFEST_V1_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "orderbook_openbook_v2", + program_id: crate::ORDERBOOK_OPENBOOK_V2_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "orderbook_phoenix_v1", + program_id: crate::ORDERBOOK_PHOENIX_V1_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "perpetuals_drift_v2", + program_id: crate::PERPETUALS_DRIFT_V2_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "perpetuals_jupiter", + program_id: crate::PERPETUALS_JUPITER_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "perpetuals_zeta_matching_engine", + program_id: crate::PERPETUALS_ZETA_MATCHING_ENGINE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "perpetuals_zeta", + program_id: crate::PERPETUALS_ZETA_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "router_dflow_aggregator_v4", + program_id: crate::ROUTER_DFLOW_AGGREGATOR_V4_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "router_jupiter_aggregator_v4", + program_id: crate::ROUTER_JUPITER_AGGREGATOR_V4_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "router_jupiter_aggregator_v6", + program_id: crate::ROUTER_JUPITER_AGGREGATOR_V6_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "router_jupiter_dca", + program_id: crate::ROUTER_JUPITER_DCA_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "router_okx_labs_v1", + program_id: crate::ROUTER_OKX_LABS_V1_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "router_okx_labs_v2", + program_id: crate::ROUTER_OKX_LABS_V2_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "router_raydium_amm_routing", + program_id: crate::ROUTER_RAYDIUM_AMM_ROUTING_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "router_sanctum", + program_id: crate::ROUTER_SANCTUM_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "router_tessera_v", + program_id: crate::ROUTER_TESSERA_V_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "router_titan_exchange_router", + program_id: crate::ROUTER_TITAN_EXCHANGE_ROUTER_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "rwa_ondo_global_markets", + program_id: crate::RWA_ONDO_GLOBAL_MARKETS_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "secp256k1", + program_id: crate::SECP256K1_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "secp256r1", + program_id: crate::SECP256R1_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "slashing", + program_id: crate::SLASHING_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "spl_account_compression", + program_id: crate::SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "spl_noop", + program_id: crate::SPL_NOOP_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "spl_single_pool", + program_id: crate::SPL_SINGLE_POOL_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "spl_token_2022_elgamal_registry", + program_id: crate::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "spl_stake_pool", + program_id: crate::SPL_STAKE_POOL_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "spl_token_2022", + program_id: crate::SPL_TOKEN_2022_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "spl_token", + program_id: crate::SPL_TOKEN_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "stable_swap_hylo_exchange", + program_id: crate::STABLE_SWAP_HYLO_EXCHANGE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "stable_swap_jupiter_stable", + program_id: crate::STABLE_SWAP_JUPITER_STABLE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "stable_swap_mercurial", + program_id: crate::STABLE_SWAP_MERCURIAL_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "stable_swap_numeraire", + program_id: crate::STABLE_SWAP_NUMERAIRE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "stable_swap_saber", + program_id: crate::STABLE_SWAP_SABER_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "stable_swap_stabble", + program_id: crate::STABLE_SWAP_STABBLE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "stake_config", + program_id: crate::STAKE_CONFIG_ACCOUNT_ID, + }, + crate::ProgramIdEntry { + code: "stake", + program_id: crate::STAKE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "staking_jito_tip_distribution", + program_id: crate::STAKING_JITO_TIP_DISTRIBUTION_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "staking_kamino_farm", + program_id: crate::STAKING_KAMINO_FARM_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "staking_marinade_finance", + program_id: crate::STAKING_MARINADE_FINANCE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "staking_sanctum_multi_validator_spl_stake_pool", + program_id: crate::STAKING_SANCTUM_MULTI_VALIDATOR_SPL_STAKE_POOL_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "staking_sanctum_single_validator_spl_stake_pool", + program_id: crate::STAKING_SANCTUM_SINGLE_VALIDATOR_SPL_STAKE_POOL_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "staking_solayer", + program_id: crate::STAKING_SOLAYER_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "system", + program_id: crate::SYSTEM_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "sysvar_clock", + program_id: crate::SYSVAR_CLOCK_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "sysvar_epoch_rewards", + program_id: crate::SYSVAR_EPOCH_REWARDS_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "sysvar_epoch_schedule", + program_id: crate::SYSVAR_EPOCH_SCHEDULE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "sysvar_fees", + program_id: crate::SYSVAR_FEES_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "sysvar_instructions", + program_id: crate::SYSVAR_INSTRUCTIONS_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "sysvar_last_restart_slot", + program_id: crate::SYSVAR_LAST_RESTART_SLOT_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "sysvar", + program_id: crate::SYSVAR_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "sysvar_recent_blockhashes", + program_id: crate::SYSVAR_RECENT_BLOCKHASHES_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "sysvar_rent", + program_id: crate::SYSVAR_RENT_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "sysvar_rewards", + program_id: crate::SYSVAR_REWARDS_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "sysvar_slot_hashes", + program_id: crate::SYSVAR_SLOT_HASHES_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "sysvar_slot_history", + program_id: crate::SYSVAR_SLOT_HISTORY_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "sysvar_stake_history", + program_id: crate::SYSVAR_STAKE_HISTORY_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "treasury_helium_treasury_management", + program_id: crate::TREASURY_HELIUM_TREASURY_MANAGEMENT_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "unknown_aquifer", + program_id: crate::UNKNOWN_AQUIFER_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "unknown_axiom_trade", + program_id: crate::UNKNOWN_AXIOM_TRADE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "unknown_bison_fi", + program_id: crate::UNKNOWN_BISON_FI_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "unknown_gavel", + program_id: crate::UNKNOWN_GAVEL_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "unknown_hawk_fi", + program_id: crate::UNKNOWN_HAWK_FI_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "unknown_humidi_fi", + program_id: crate::UNKNOWN_HUMIDI_FI_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "unknown_jup_studio_authority", + program_id: crate::UNKNOWN_JUP_STUDIO_AUTHORITY_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "unknown_ore_v3", + program_id: crate::UNKNOWN_ORE_V3_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "unknown_penguin_finance", + program_id: crate::UNKNOWN_PENGUIN_FINANCE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "unknown_sanctum_s_controller", + program_id: crate::UNKNOWN_SANCTUM_S_CONTROLLER_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "unknown_scorch", + program_id: crate::UNKNOWN_SCORCH_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "unknown_swig", + program_id: crate::UNKNOWN_SWIG_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "vault_carrot_defi", + program_id: crate::VAULT_CARROT_DEFI_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "vault_hylo_stability_pool", + program_id: crate::VAULT_HYLO_STABILITY_POOL_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "vault_kamino", + program_id: crate::VAULT_KAMINO_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "vault_kamino_v2", + program_id: crate::VAULT_KAMINO_V2_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "vault_kamino_yvaults", + program_id: crate::VAULT_KAMINO_YVAULTS_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "vault_meteora", + program_id: crate::VAULT_METEORA_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "vesting_streamflow", + program_id: crate::VESTING_STREAMFLOW_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "vote", + program_id: crate::VOTE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "wallet_jupiter_apepro_smart_wallet", + program_id: crate::WALLET_JUPITER_APEPRO_SMART_WALLET_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "weighted_swap_stabble", + program_id: crate::WEIGHTED_SWAP_STABBLE_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "zk_elgamal_proof", + program_id: crate::ZK_ELGAMAL_PROOF_PROGRAM_ID, + }, + crate::ProgramIdEntry { + code: "zk_token_proof", + program_id: crate::ZK_TOKEN_PROOF_PROGRAM_ID, + }, +]; + +#[cfg(test)] +mod tests { + #[test] + fn registry_entries_are_unique_and_non_empty() { + let mut codes = std::collections::BTreeSet::new(); + let mut program_ids = std::collections::BTreeSet::new(); + for entry in crate::registered_program_ids() { + assert!(!entry.code().is_empty()); + assert!(!entry.program_id().is_empty()); + assert!(codes.insert(entry.code())); + assert!(program_ids.insert(entry.program_id())); + } + assert_eq!(crate::registered_program_ids().len(), 190); + } + + #[test] + fn native_registry_contains_vote_slashing_and_every_loader_generation() { + let codes = crate::native_program_ids() + .iter() + .map(crate::ProgramIdEntry::code) + .collect::>(); + assert!(codes.contains("vote")); + assert!(codes.contains("slashing")); + assert!(codes.contains("bpf_loader_deprecated")); + assert!(codes.contains("bpf_loader")); + assert!(codes.contains("bpf_loader_upgradeable")); + assert!(codes.contains("loader_v4")); + assert_eq!(codes.len(), crate::native_program_ids().len()); + } + + #[test] + fn stake_config_is_a_well_known_account_not_an_executable_program() { + assert!(crate::native_well_known_account_ids().iter().any(|entry| { + return entry.program_id() == crate::STAKE_CONFIG_ACCOUNT_ID; + })); + assert!(!crate::native_program_ids().iter().any(|entry| { + return entry.program_id() == crate::STAKE_CONFIG_ACCOUNT_ID; + })); + } + + #[test] + fn registry_lookup_finds_system_program() { + let entry = crate::find_registered_program_id(crate::SYSTEM_PROGRAM_ID); + assert_eq!(entry.map(crate::ProgramIdEntry::code), std::option::Option::Some("system")); + } + + #[test] + fn registry_contains_verified_spl_generations_and_primitives() { + let expected = [ + (crate::SPL_MEMO_V1_PROGRAM_ID, "spl_memo_v1"), + (crate::SPL_MEMO_V3_PROGRAM_ID, "spl_memo_v3"), + (crate::SPL_MEMO_V4_PROGRAM_ID, "spl_memo_v4"), + (crate::SPL_SINGLE_POOL_PROGRAM_ID, "spl_single_pool"), + (crate::SPL_ACCOUNT_COMPRESSION_PROGRAM_ID, "spl_account_compression"), + (crate::SPL_NOOP_PROGRAM_ID, "spl_noop"), + ( + crate::SPL_TOKEN_2022_ELGAMAL_REGISTRY_PROGRAM_ID, + "spl_token_2022_elgamal_registry", + ), + ]; + for (program_id, code) in expected { + let entry = crate::find_registered_program_id(program_id); + assert_eq!(entry.map(crate::ProgramIdEntry::code), std::option::Option::Some(code)); + } + assert_eq!(crate::MEMO_PROGRAM_ID, crate::SPL_MEMO_V3_PROGRAM_ID); + assert_eq!(crate::SPL_NAME_SERVICE_PROGRAM_ID, crate::METADATA_NAME_SERVICE_PROGRAM_ID); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/Cargo.toml b/migration/khadhroony-bot2-reference/kb_rpc/Cargo.toml new file mode 100644 index 0000000..8424e7f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/Cargo.toml @@ -0,0 +1,27 @@ +# file: kb_rpc/Cargo.toml +# version: 5 + +[package] +name = "kb_rpc" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +base64.workspace = true +bs58.workspace = true +kb_config = { path = "../kb_config" } +kb_core = { path = "../kb_core" } +kb_execution_api = { path = "../kb_execution_api" } +kb_model = { path = "../kb_model" } +futures-util.workspace = true +reqwest.workspace = true +serde.workspace = true +serde_json.workspace = true +tokio.workspace = true +tokio-tungstenite.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_rpc/README.md b/migration/khadhroony-bot2-reference/kb_rpc/README.md new file mode 100644 index 0000000..d247677 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/README.md @@ -0,0 +1,209 @@ + + + +# kb_rpc + +Ce crate contient les clients et adaptateurs Solana JSON-RPC HTTP/WebSocket du workspace. Il reste indépendant des stores concrets, des décodeurs, des matérialiseurs, du wallet et de Tauri. + + +## API publique principale + +| Famille d’exports | Usage | +|---------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------| +| `RpcEndpoint`, `SolanaRpcClient` | Configuration d’un endpoint et abstraction minimale d’un client RPC. | +| `HttpClient`, `HttpEndpointPool` | Appels JSON-RPC HTTP et routage déterministe par rôle/request kind. | +| `WsClient`, `WsEndpointPool` | Configuration WebSocket et sélection d’un endpoint compatible par rôle. | +| `StandardWsRequest`, `StandardWsNotification`, `WsSession` | Neuf contrats PubSub typés et runtime persistant multiplexé avec unsubscribe, reconnexion et réabonnement bornés. | +| `JsonRpcRequest`, `JsonRpcResponse`, `parse_json_rpc_text(...)` | Contrats et parsing JSON-RPC 2.0 communs aux transports. | +| `GetTransactionConfig`, `GetTransactionAdapter`, `CanonicalTransactionAdapter` | Acquisition et adaptation d’une transaction vers le modèle canonique. | +| `GetSignaturesForAddressConfig`, `AddressSignatureInfo` | Pagination bornée des signatures pour les backfills. | +| configurations/résultats `execution_rpc` | Paramètres et réponses typés pour cluster, blockhash, frais, simulation, comptes, airdrop, envoi et confirmation. | +| fonctions `adapt_*` | Conversion explicite d’une valeur JSON-RPC en contrat typé validé. | +| `validate_solana_hash_text`, `validate_solana_pubkey_text`, `validate_transaction_signature_text` | Validation base58 et longueur des identifiants Solana. | +| `request_kind_from_method(...)` | Normalisation stable du nom d’une méthode pour le routage d’endpoint. | +| `StandardHttpRequest`, requêtes `Get*Request`, configurations `Rpc*Config` | Contrats configurables propres à `kb_rpc` pour toutes les méthodes HTTP standard. | + +Les méthodes réseau retournent `kb_core::Result`. Les fonctions `adapt_*` sont utiles aux tests, aux transports alternatifs et à l’analyse de réponses déjà acquises ; elles n’effectuent aucun I/O. + +### Exemple minimal + +```rust +let config = kb_rpc::GetLatestBlockhashConfig::confirmed(); +let result = client.get_latest_blockhash(&config).await?; +let blockhash = result.blockhash; +``` + +L’application choisit un `HttpClient` direct ou un `HttpEndpointPool`. Le pool vérifie les rôles et request kinds avant d’appeler le client ; il ne modifie pas la sémantique du résultat. + +## Transports et routage + +- client HTTP JSON-RPC standard basé sur `reqwest` ; +- pool HTTP sélectionné par `role` et `request_kind` ; +- client WebSocket standard basé sur `tokio-tungstenite` ; +- pool WebSocket par rôle ; +- contrats JSON-RPC 2.0 communs ; +- tracing canonique `kb_rpc` sans journalisation des secrets intégrés aux URL. + +## Acquisition canonique + +### `getTransaction` + +`GetTransactionConfig`, `GetTransactionAdapter` et `CanonicalTransactionAdapter` transforment une réponse JSON-RPC standard en `kb_model::CanonicalTransaction` indépendante du fournisseur. + +### `getSignaturesForAddress` + +`GetSignaturesForAddressConfig` construit les pages bornées `before`/`until` et `AddressSignatureInfo` conserve les métadonnées nécessaires au backfill. + +## RPC d’exécution — `0.4.2-pre.004` à `pre.013` + +Les méthodes suivantes possèdent maintenant des configurations, résultats et adaptateurs typés : + +| Méthode | Entrée principale | Résultat | +|------------------------------------------------------|---------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------| +| `HttpClient::get_genesis_hash` | aucune | `GenesisHashResult` avec hash et classification Devnet/Testnet/Mainnet éventuelle. | +| `HttpClient::get_latest_blockhash` | `GetLatestBlockhashConfig` | `LatestBlockhashResult` avec contexte, blockhash et dernière block height valide. | +| `HttpClient::get_fee_for_message` | message legacy/v0 sérialisé en base64, `GetFeeForMessageConfig` | `FeeForMessageResult`, dont la valeur peut être absente si le blockhash n’est plus valide. | +| `HttpClient::simulate_transaction` | transaction sérialisée en base64, `SimulateTransactionConfig` | `SimulateTransactionResult` avec erreur runtime, logs, unités, frais, blockhash de remplacement et diagnostics optionnels. | +| `HttpClient::get_account_info` | public key, `GetAccountInfoConfig` | `AccountInfoResult` avec contexte, métadonnées, `space` et données complètes optionnelles bornées. | +| `HttpClient::get_minimum_balance_for_rent_exemption` | longueur de données, `GetMinimumBalanceForRentExemptionConfig` | minimum rent-exempt en lamports pour cette taille. | +| `HttpClient::get_balance` | public key, `GetBalanceConfig` | `BalanceResult` avec contexte et lamports. | +| `HttpClient::request_airdrop` | public key, lamports, `RequestAirdropConfig` | `AirdropResult` avec signature du faucet. | +| `HttpClient::send_transaction` | transaction signée base64, signature primaire attendue, `SendTransactionConfig` | `SendTransactionResult` uniquement si la signature RPC correspond à la transaction locale. | +| `HttpClient::get_signature_statuses` | une à 256 signatures, `GetSignatureStatusesConfig` | `SignatureStatusesResult` positionnel. | +| `HttpClient::get_block_height` | `GetBlockHeightConfig` | `BlockHeightResult`. | + +Chaque méthode existe également sur `HttpEndpointPool` avec le suffixe `_for_role` afin de conserver le routage configuré. + +`SimulateTransactionConfig::unsigned_with_replacement()` prépare une simulation sans vérification de signature avec remplacement du recent blockhash. Elle permet de simuler avant la phase de signature, conformément au pipeline d’exécution du projet. + +`SimulateTransactionResult::to_execution_result(...)` convertit le résultat RPC vers `kb_execution_api::ExecutionSimulationResult`. Le contexte de cluster, de blockhash récent ou de nonce durable reste fourni explicitement par l’orchestrateur ; `kb_rpc` ne l’invente pas. + +Les adaptateurs appliquent : + +- validation base58 des genesis hashes et blockhashes ; +- validation base64 non vide et bornée des messages/transactions ; +- limite locale du nombre de snapshots de comptes demandés ; +- conservation provider-neutral des erreurs, return data, inner instructions et comptes simulés ; +- lecture metadata-only de l’existence, du solde, de l’owner, de `space` et du statut executable avec `dataSlice.length = 0` ; +- lecture optionnelle des données complètes en base64 avec limite locale, contrôle de l’encodage et égalité stricte entre `space` et longueur décodée ; +- conservation explicite du minimum rent-exempt et de la longueur de données demandée ; +- distinction entre erreur de transport/JSON-RPC et erreur runtime de la transaction simulée. + +### Modes `getAccountInfo` + +`GetAccountInfoConfig::confirmed()` conserve le mode léger historique. La requête utilise `dataSlice = { offset: 0, length: 0 }` et l’adaptateur exige un payload décodé vide tout en conservant le champ `space` complet. + +`GetAccountInfoConfig::confirmed_with_data(max_data_bytes)` demande les données complètes en omettant `dataSlice`. La limite doit être comprise entre 1 et 65 536 octets. L’adaptateur refuse avant utilisation : + +- un `space` supérieur à la limite demandée ; +- un tuple `data` mal formé ; +- un encodage différent de `base64` ; +- un payload encodé ou décodé trop grand ; +- une longueur décodée différente de `space`. + +Ce mode est utilisé pour charger exactement les 80 octets d’un compte durable nonce avant validation dans `kb_execution_solana`. Le RPC ne décode pas lui-même la sémantique du compte. + +## Classification du cluster + +Les genesis hashes officiels Devnet, Testnet et Mainnet sont reconnus. Le hash Mainnet n’a pas changé ; `mainnet-beta` reste un alias historique encore utilisé par les endpoints RPC et la CLI. Le contrat public utilise `ExecutionCluster::Mainnet`, sérialisé `mainnet`, tout en acceptant `mainnet_beta` à la désérialisation pour compatibilité. Un hash différent reste non classifié : il peut correspondre à un validateur local ou à un cluster privé et doit être rapproché du contexte attendu par l’orchestrateur. + +Pour le chemin de signature, `SimulateTransactionConfig::unsigned_exact()` conserve le blockhash du message. `unsigned_with_replacement()` reste disponible pour le diagnostic, mais son résultat est marqué par le contrat commun et ne peut pas autoriser la signature ou l’envoi du message original. + +## Envoi et confirmation bornée + +`SendTransactionConfig` interdit `skipPreflight = true` dans le chemin d’exécution. Il peut être construit depuis `ExecutionConfig`, transmet `maxRetries` et `minContextSlot`, puis vérifie que la signature retournée par le nœud est exactement la signature primaire calculée localement. + +`ConfirmTransactionConfig` borne l’intervalle et le nombre de polls. `HttpEndpointPool::confirm_transaction_for_roles` interroge `getSignatureStatuses`, contrôle l’erreur runtime, compare le commitment demandé et utilise `getBlockHeight` lorsqu’une dernière block height valide est connue. Le résultat commun contient le statut, le slot éventuel, le nombre de tentatives, la dernière block height observée et le diagnostic éventuel. + +## Frontières + +`kb_rpc` ne construit pas le message, ne résout aucun signer et ne signe rien. Il accepte uniquement un payload déjà signé et conserve l’envoi séparé de la confirmation : + +```text +PreparedExecutionPlan + -> kb_execution_solana build/simulation/signature + -> kb_rpc sendTransaction + -> kb_rpc confirmation bornée + -> orchestration post-exécution +``` + +## Validation + +Les tests offline vérifient les paramètres JSON-RPC, les réponses valides/nulles, les erreurs runtime, les hashes invalides, les payloads base64 invalides, les limites des données de compte, les divergences `space`/longueur, la classification de cluster et la conversion vers le contrat d’exécution commun. + + +## Informations d’époque pour l’exécution stateful + +`GetEpochInfoConfig`, `EpochInfoResult`, `HttpClient::get_epoch_info` et `HttpEndpointPool::get_epoch_info_for_role` encapsulent `getEpochInfo` avec commitment et `minContextSlot` optionnel. Cette primitive est utilisée pour les fenêtres de slot ALT/Slashing et les délais de fermeture. Elle reste indépendante de toute logique métier d’exécution. +## Inventaire HTTP standard configurable — `0.4.2-pre.024` + +`STANDARD_HTTP_METHODS` enregistre exactement les 52 méthodes HTTP de la référence Solana. Elles possèdent maintenant toutes un contrat typé : les 14 adaptateurs spécialisés existants restent inchangés et les 38 méthodes auparavant limitées au JSON brut utilisent des requêtes `Get*Request` propres à `kb_rpc`. + +La couche configurable ne réexporte aucun DTO d’Agave. Les options sont des champs `Option` indépendants : une valeur absente n’est pas sérialisée, tandis que `Some(false)`, `Some(0)` ou `Some([])` reste distinguable lorsqu’il s’agit d’une valeur valide du contrat RPC. Les types officiels peuvent servir d’oracle dans les tests, mais ne constituent pas l’API publique du crate. + +```rust +let request = kb_rpc::GetProgramAccountsRequest { + program_id, + config: Some(kb_rpc::RpcProgramAccountsConfig { + filters: Some(vec![kb_rpc::RpcProgramAccountFilter::DataSize(165)]), + encoding: Some(kb_rpc::RpcAccountEncoding::Base64Zstd), + data_slice: None, + commitment: Some(kb_rpc::RpcCommitmentLevel::Confirmed), + min_context_slot: Some(anchor_slot), + with_context: Some(true), + sort_results: Some(true), + }), +}; +let response = client.execute_standard_request(&request).await?; +``` + +Le même contrat est disponible via `HttpEndpointPool::execute_standard_request_for_role`. La requête fournit son nom de méthode, construit le tableau positionnel exact et définit son type de réponse associé. Le transport refuse un type absent du registre ou déclaré avec un contrat autre que `typed_adapter`. + +Les validations locales couvrent notamment : + +- 100 comptes maximum pour `getMultipleAccounts` ; +- 128 comptes verrouillés maximum pour `getRecentPrioritizationFees` ; +- 500 000 slots maximum pour les plages et limites de blocs ; +- 720 entrées maximum pour `getRecentPerformanceSamples` ; +- 128 octets décodés maximum pour un filtre memcmp ; +- refus de `processed` pour les méthodes historiques qui ne l’acceptent pas ; +- incompatibilité `jsonParsed` / `dataSlice` ; +- validation base58 de toutes les clés et blockhashes structurants. + +| Surface | Inventaire | Contrat actuel | +|--------------------|-----------------------:|-----------------------------------------------------------------------------------| +| HTTP | 52 méthodes | 52 adaptateurs typés, 0 contrat standard limité au JSON brut | +| WebSocket | 9 paires / 18 méthodes | 9 requêtes typées, 9 notifications typées et runtime persistant réutilisable | +| WebSocket instable | 3 paires | `block`, `slotsUpdates` et `vote`, refusées sans capacité explicite de l’endpoint | + +`execute_standard_method_raw` reste disponible pour les outils bas niveau qui manipulent explicitement un `StandardHttpMethodSpec`, mais n’est plus nécessaire pour couvrir une méthode HTTP standard. + +## PubSub WebSocket standard — `0.4.2-pre.025` + +`StandardWsRequest` représente exactement les neuf familles officielles. Chaque variante construit son tableau de paramètres sans valeur implicite imposée par la bibliothèque : encodage, `dataSlice`, filtres, commitment, détails de bloc, rewards, version maximale et notification anticipée de signature restent sélectionnables indépendamment. + +`adapt_standard_ws_notification` transforme les neuf méthodes de notification en `StandardWsNotification`. Les réponses contextuelles, les comptes directs ou contextualisés de `programNotification`, le marqueur `receivedSignature`, les mises à jour de slots taguées et les votes gossip sont conservés dans des types propres à `kb_rpc`. + +```rust +let client = pool.select_client_for_role_and_method("slot_notifications", "slotSubscribe")?; +let session = kb_rpc::WsSession::connect( + client, + kb_rpc::StandardWsCapabilities::default(), + kb_rpc::WsReconnectPolicy::bounded(3, 250, 2_000)?, +) +.await?; +let mut events = session.subscribe_events(); +let acknowledgement = session + .subscribe(kb_rpc::StandardWsRequest::Slot(kb_rpc::SlotSubscribeRequest)) + .await?; +let remote_id = acknowledgement.subscription.remote_subscription_id; +``` + +La session possède un identifiant local stable par souscription et suit séparément l’identifiant distant attribué par le nœud. Après reconnexion, elle réémet les requêtes actives, remappe les IDs distants et publie `SubscriptionRemapped`. L’unsubscribe explicite attend une réponse booléenne vraie ; les requêtes et désabonnements ont leurs timeouts propres. Une notification terminale `signatureNotification` retire automatiquement la souscription one-shot, tandis que `receivedSignature` la conserve jusqu’au statut terminal. La fermeture envoie des désabonnements best-effort avant la frame Close. + +Les méthodes instables `blockSubscribe`, `slotsUpdatesSubscribe` et `voteSubscribe` ne sont jamais déduites d’un simple nom de méthode demandé : `StandardWsCapabilities::from_endpoint` exige qu’un rôle activé de l’endpoint annonce explicitement le request kind correspondant, ou un wildcard volontaire. + +La première souscription réelle sert de probe du nœud. Lorsqu’une réponse JSON-RPC indique que la méthode est absente, non activée, indisponible ou non supportée, `WsSession` désactive uniquement la capacité concernée, met à jour son snapshot et refuse localement les appels suivants. Une erreur de paramètres, d’authentification ou de rate limit ne désactive pas la capacité. + +La validation finale `0.4.2` compte 113 tests `kb_rpc`. Les logs Tauri réels confirment des souscriptions et désabonnements `slot`, `root` et `program`, ainsi que le garde-fou interdisant de changer d’endpoint sans fermer la session active. L’état détaillé est documenté dans `docs/SOLANA_NATIVE_RPC_CLOSURE_AUDIT.md`. + diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/client.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/client.rs new file mode 100644 index 0000000..f8de5fe --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/client.rs @@ -0,0 +1,22 @@ +// file: kb_rpc/src/client.rs +// version: 2 + +//! RPC client scaffold for Solana ingestion. + +/// RPC endpoint configuration. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct RpcEndpoint { + /// HTTP RPC URL. + pub http_url: std::string::String, + /// Optional WebSocket RPC URL. + pub ws_url: std::option::Option, +} + +/// Minimal Solana RPC client abstraction. +pub trait SolanaRpcClient { + /// Fetches a raw transaction payload by signature. + fn get_transaction_raw_json( + &self, + signature: &kb_model::Signature, + ) -> kb_core::Result>; +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/constants.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/constants.rs new file mode 100644 index 0000000..3410e02 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/constants.rs @@ -0,0 +1,41 @@ +// file: kb_rpc/src/constants.rs +// version: 6 + +//! Local constants for the `kb_rpc` crate. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_rpc"; +/// Official Devnet genesis hash. +pub(crate) const DEVNET_GENESIS_HASH: &str = "EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG"; +/// Official Testnet genesis hash. +pub(crate) const TESTNET_GENESIS_HASH: &str = "4uhcVJyU9pJkvQyS88uRDiswHXSCkY3zQawwpjk2NsNY"; +/// Official Mainnet genesis hash; RPC and CLI endpoints retain the legacy `mainnet-beta` alias. +pub(crate) const MAINNET_GENESIS_HASH: &str = "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d"; +/// Local defensive maximum for one base64 message or transaction request value. +pub(crate) const MAX_EXECUTION_RPC_BASE64_LENGTH: usize = 65_536; +/// Local defensive maximum for decoded account data returned by one execution RPC request. +pub(crate) const MAX_EXECUTION_ACCOUNT_DATA_BYTES: usize = 65_536; +/// Local defensive maximum for account snapshots requested from one simulation. +pub(crate) const MAX_SIMULATION_ACCOUNT_COUNT: usize = 128; +/// Maximum signatures accepted by one `getSignatureStatuses` request. +pub(crate) const MAX_SIGNATURE_STATUS_COUNT: usize = 256; +/// Maximum confirmation polling attempts accepted by the bounded helper. +pub(crate) const MAX_CONFIRMATION_ATTEMPTS: u32 = 10_000; +/// Maximum delay between confirmation polls. +pub(crate) const MAX_CONFIRMATION_POLL_INTERVAL_MS: u64 = 60_000; +/// Maximum number of public keys accepted by `getMultipleAccounts`. +pub(crate) const MAX_MULTIPLE_ACCOUNT_COUNT: usize = 100; +/// Maximum writable-account set accepted by `getRecentPrioritizationFees`. +pub(crate) const MAX_PRIORITIZATION_FEE_ACCOUNT_COUNT: usize = 128; +/// Maximum sample count accepted by `getRecentPerformanceSamples`. +pub(crate) const MAX_PERFORMANCE_SAMPLE_COUNT: usize = 720; +/// Maximum consecutive slot leaders accepted by `getSlotLeaders`. +pub(crate) const MAX_SLOT_LEADER_COUNT: u64 = 5_000; +/// Maximum slot span or result limit accepted by block-range methods. +pub(crate) const MAX_BLOCK_RANGE: u64 = 500_000; +/// Maximum decoded memcmp payload accepted by the standard RPC contract. +pub(crate) const MAX_MEMCMP_DECODED_BYTES: usize = 128; +/// Maximum base58 text length for a 128-byte memcmp payload. +pub(crate) const MAX_MEMCMP_BASE58_LENGTH: usize = 175; +/// Maximum base64 text length for a 128-byte memcmp payload. +pub(crate) const MAX_MEMCMP_BASE64_LENGTH: usize = 172; diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/endpoint_role.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/endpoint_role.rs new file mode 100644 index 0000000..ed42065 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/endpoint_role.rs @@ -0,0 +1,190 @@ +// file: kb_rpc/src/endpoint_role.rs +// version: 2 + +//! Endpoint role helpers shared by HTTP and WebSocket pools. + +/// Snapshot of one endpoint role and its local limits. +#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct EndpointRoleSnapshot { + /// Role code used by endpoint pools. + pub role: std::string::String, + /// Enables this role on the endpoint. + pub enabled: bool, + /// Request or subscription kinds handled by this role. + pub request_kinds: std::vec::Vec, + /// Role priority where lower values are preferred. + pub priority: u32, + /// Requests per second allowed for this role on this URL. + pub requests_per_second: u32, + /// Burst capacity allowed for this role on this URL. + pub burst_capacity: u32, + /// Maximum concurrent requests allowed for this role on this URL. + pub max_concurrent_requests: u32, + /// Maximum subscriptions allowed for this role on this URL. + pub max_subscriptions: u32, + /// Pause after a rate limit response in milliseconds. + pub pause_after_rate_limit_ms: u64, +} + +impl crate::EndpointRoleSnapshot { + /// Builds a serializable snapshot from configuration. + pub fn from_config(config: &kb_config::EndpointRoleConfig) -> Self { + return Self { + role: config.role.clone(), + enabled: config.enabled, + request_kinds: config.request_kinds.clone(), + priority: config.priority, + requests_per_second: config.requests_per_second, + burst_capacity: config.burst_capacity, + max_concurrent_requests: config.max_concurrent_requests, + max_subscriptions: config.max_subscriptions, + pause_after_rate_limit_ms: config.pause_after_rate_limit_ms, + }; + } +} + +/// Converts a JSON-RPC method name into a stable snake_case request kind. +pub fn request_kind_from_method(method: &str) -> std::string::String { + let trimmed = method.trim(); + if trimmed == "logsSubscribe" { + return "logs_subscribe_mentions".to_string(); + } + return crate::endpoint_role::camel_or_pascal_to_snake(trimmed); +} + +/// Returns true when one endpoint role can handle the requested role and kind. +pub(crate) fn role_matches( + role_config: &kb_config::EndpointRoleConfig, + required_role: &str, + request_kind: &str, +) -> bool { + if !role_config.enabled { + return false; + } + if role_config.role != required_role { + return false; + } + for configured_kind in &role_config.request_kinds { + if configured_kind == request_kind { + return true; + } + if configured_kind == "*" { + return true; + } + } + return false; +} + +fn camel_or_pascal_to_snake(value: &str) -> std::string::String { + let mut output = std::string::String::new(); + let mut previous_was_lower_or_digit = false; + for character in value.chars() { + if character == '-' || character == ' ' || character == '.' { + if !output.ends_with('_') && !output.is_empty() { + output.push('_'); + } + previous_was_lower_or_digit = false; + continue; + } + if character.is_ascii_uppercase() { + if previous_was_lower_or_digit && !output.ends_with('_') && !output.is_empty() { + output.push('_'); + } + output.push(character.to_ascii_lowercase()); + previous_was_lower_or_digit = false; + continue; + } + if character == '_' { + if !output.ends_with('_') && !output.is_empty() { + output.push('_'); + } + previous_was_lower_or_digit = false; + continue; + } + output.push(character); + previous_was_lower_or_digit = character.is_ascii_lowercase() || character.is_ascii_digit(); + } + return output.trim_matches('_').to_string(); +} + +#[cfg(test)] +mod tests { + fn role_config( + role: &str, + enabled: bool, + request_kinds: std::vec::Vec, + ) -> kb_config::EndpointRoleConfig { + return kb_config::EndpointRoleConfig { + role: role.to_string(), + enabled, + request_kinds, + priority: 1, + requests_per_second: 10, + burst_capacity: 10, + max_concurrent_requests: 4, + max_subscriptions: 16, + pause_after_rate_limit_ms: 1500, + }; + } + + #[test] + fn request_kind_converts_camel_case_methods() { + assert_eq!(crate::request_kind_from_method("getLatestBlockhash"), "get_latest_blockhash"); + assert_eq!(crate::request_kind_from_method("sendTransaction"), "send_transaction"); + } + + #[test] + fn request_kind_keeps_existing_snake_case_methods() { + assert_eq!(crate::request_kind_from_method("get_latest_blockhash"), "get_latest_blockhash"); + assert_eq!( + crate::request_kind_from_method("logs_subscribe_mentions"), + "logs_subscribe_mentions" + ); + } + + #[test] + fn request_kind_normalizes_separators() { + assert_eq!(crate::request_kind_from_method("get-Block"), "get_block"); + assert_eq!(crate::request_kind_from_method("program.Subscribe"), "program_subscribe"); + } + + #[test] + fn request_kind_maps_logs_subscribe_to_mentions_role_kind() { + assert_eq!(crate::request_kind_from_method("logsSubscribe"), "logs_subscribe_mentions"); + } + + #[test] + fn role_matches_exact_request_kind() { + let role = role_config("http_queries", true, std::vec!["get_version".to_string()]); + assert!(crate::endpoint_role::role_matches(&role, "http_queries", "get_version")); + } + + #[test] + fn role_matches_wildcard_request_kind() { + let role = role_config("http_queries", true, std::vec!["*".to_string()]); + assert!(crate::endpoint_role::role_matches(&role, "http_queries", "get_block")); + } + + #[test] + fn role_does_not_match_when_disabled() { + let role = role_config("http_queries", false, std::vec!["*".to_string()]); + assert!(!crate::endpoint_role::role_matches(&role, "http_queries", "get_version")); + } + + #[test] + fn role_does_not_match_different_role() { + let role = role_config("http_queries", true, std::vec!["*".to_string()]); + assert!(!crate::endpoint_role::role_matches(&role, "http_heavy", "get_block")); + } + + #[test] + fn endpoint_role_snapshot_preserves_limits() { + let role = role_config("http_queries", true, std::vec!["get_version".to_string()]); + let snapshot = crate::EndpointRoleSnapshot::from_config(&role); + assert_eq!(snapshot.role, "http_queries"); + assert_eq!(snapshot.request_kinds, std::vec!["get_version".to_string()]); + assert_eq!(snapshot.requests_per_second, 10); + assert_eq!(snapshot.max_subscriptions, 16); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/execution_rpc.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/execution_rpc.rs new file mode 100644 index 0000000..e37c140 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/execution_rpc.rs @@ -0,0 +1,2753 @@ +// file: kb_rpc/src/execution_rpc.rs +// version: 8 + +//! Typed Solana JSON-RPC adapters used by execution orchestration. + +use base64::Engine; // rust-rules: trait-import + +/// Commitment level accepted by execution-oriented RPC methods. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum RpcCommitmentLevel { + /// The node's most recent processed state. + Processed, + /// A supermajority-confirmed state. + Confirmed, + /// The strongest finalized state. + Finalized, +} + +impl crate::execution_rpc::RpcCommitmentLevel { + /// Returns the wire value expected by Solana JSON-RPC. + pub fn as_str(&self) -> &'static str { + return match self { + Self::Processed => "processed", + Self::Confirmed => "confirmed", + Self::Finalized => "finalized", + }; + } +} + +/// Standard context attached to Solana RPC responses. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcResponseContext { + /// Slot used by the RPC node to answer the request. + pub slot: u64, + /// Optional node API version. + #[serde(default)] + pub api_version: std::option::Option, +} + +/// Genesis hash and optional classification of a public Solana cluster. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct GenesisHashResult { + /// Base58 genesis hash returned by the endpoint. + pub genesis_hash: std::string::String, + /// Known public cluster when the hash matches an official public cluster. + pub classified_cluster: std::option::Option, +} + +/// Configuration for `getEpochInfo` used by stateful execution readiness checks. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetEpochInfoConfig { + /// Requested commitment. + pub commitment: crate::execution_rpc::RpcCommitmentLevel, + /// Optional minimum context slot. + pub min_context_slot: std::option::Option, +} + +impl crate::execution_rpc::GetEpochInfoConfig { + /// Creates an epoch information request configuration. + pub fn new( + commitment: crate::execution_rpc::RpcCommitmentLevel, + min_context_slot: std::option::Option, + ) -> Self { + return Self { commitment, min_context_slot }; + } + + /// Creates the default confirmed epoch information request. + pub fn confirmed() -> Self { + return Self::new( + crate::execution_rpc::RpcCommitmentLevel::Confirmed, + std::option::Option::None, + ); + } + + fn request_params(&self) -> std::vec::Vec { + let mut config = serde_json::Map::::new(); + config.insert( + "commitment".to_string(), + serde_json::Value::String(self.commitment.as_str().to_string()), + ); + if let std::option::Option::Some(min_context_slot) = self.min_context_slot { + config.insert( + "minContextSlot".to_string(), + serde_json::Value::Number(serde_json::Number::from(min_context_slot)), + ); + } + return vec![serde_json::Value::Object(config)]; + } +} + +/// Current epoch and slot progression returned by `getEpochInfo`. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct EpochInfoResult { + /// Absolute cluster slot. + pub absolute_slot: u64, + /// Current block height. + pub block_height: u64, + /// Current epoch number. + pub epoch: u64, + /// Slot index within the current epoch. + pub slot_index: u64, + /// Number of slots in the current epoch. + pub slots_in_epoch: u64, + /// Optional transaction count when the node exposes it. + pub transaction_count: std::option::Option, +} + +/// Configuration for `getLatestBlockhash`. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetLatestBlockhashConfig { + /// Requested commitment. + pub commitment: crate::execution_rpc::RpcCommitmentLevel, + /// Optional minimum context slot. + pub min_context_slot: std::option::Option, +} + +impl crate::execution_rpc::GetLatestBlockhashConfig { + /// Creates a blockhash request configuration. + pub fn new( + commitment: crate::execution_rpc::RpcCommitmentLevel, + min_context_slot: std::option::Option, + ) -> Self { + return Self { commitment, min_context_slot }; + } + + /// Creates the default confirmed blockhash request. + pub fn confirmed() -> Self { + return Self::new( + crate::execution_rpc::RpcCommitmentLevel::Confirmed, + std::option::Option::None, + ); + } + + fn request_params(&self) -> std::vec::Vec { + let mut config = serde_json::Map::::new(); + config.insert( + "commitment".to_string(), + serde_json::Value::String(self.commitment.as_str().to_string()), + ); + if let std::option::Option::Some(min_context_slot) = self.min_context_slot { + config.insert( + "minContextSlot".to_string(), + serde_json::Value::Number(serde_json::Number::from(min_context_slot)), + ); + } + return std::vec![serde_json::Value::Object(config)]; + } +} + +/// Latest recent blockhash returned by the cluster. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct LatestBlockhashResult { + /// RPC response context. + pub context: crate::execution_rpc::RpcResponseContext, + /// Base58 recent blockhash. + pub blockhash: std::string::String, + /// Last block height at which the blockhash remains valid. + pub last_valid_block_height: u64, +} + +/// Configuration for `getFeeForMessage`. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetFeeForMessageConfig { + /// Requested commitment. + pub commitment: crate::execution_rpc::RpcCommitmentLevel, + /// Optional minimum context slot. + pub min_context_slot: std::option::Option, +} + +impl crate::execution_rpc::GetFeeForMessageConfig { + /// Creates a fee request configuration. + pub fn new( + commitment: crate::execution_rpc::RpcCommitmentLevel, + min_context_slot: std::option::Option, + ) -> Self { + return Self { commitment, min_context_slot }; + } + + /// Creates the default confirmed fee request. + pub fn confirmed() -> Self { + return Self::new( + crate::execution_rpc::RpcCommitmentLevel::Confirmed, + std::option::Option::None, + ); + } + + fn request_params( + &self, + encoded_message: &str, + ) -> kb_core::Result> { + let validation_result = crate::execution_rpc::validate_base64_payload( + encoded_message, + "getFeeForMessage message", + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let mut config = serde_json::Map::::new(); + config.insert( + "commitment".to_string(), + serde_json::Value::String(self.commitment.as_str().to_string()), + ); + if let std::option::Option::Some(min_context_slot) = self.min_context_slot { + config.insert( + "minContextSlot".to_string(), + serde_json::Value::Number(serde_json::Number::from(min_context_slot)), + ); + } + return std::result::Result::Ok(std::vec![ + serde_json::Value::String(encoded_message.to_string()), + serde_json::Value::Object(config), + ]); + } +} + +/// Fee estimate returned for one serialized transaction message. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct FeeForMessageResult { + /// RPC response context. + pub context: crate::execution_rpc::RpcResponseContext, + /// Fee in lamports, or `None` when the referenced blockhash is no longer valid. + pub fee_lamports: std::option::Option, +} + +/// Optional accounts requested from `simulateTransaction`. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct SimulationAccountsConfig { + /// Account addresses returned after simulation. + pub addresses: std::vec::Vec, +} + +impl crate::execution_rpc::SimulationAccountsConfig { + /// Creates a bounded account-return request using base64 account data. + pub fn new(addresses: std::vec::Vec) -> kb_core::Result { + if addresses.is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "simulateTransaction accounts must not be empty", + )); + } + if addresses.len() > crate::constants::MAX_SIMULATION_ACCOUNT_COUNT { + return std::result::Result::Err(kb_core::Error::config(format!( + "simulateTransaction accounts must not exceed {} entries", + crate::constants::MAX_SIMULATION_ACCOUNT_COUNT + ))); + } + for address in &addresses { + let validation_result = crate::validate_solana_pubkey_text( + address.0.as_str(), + "simulateTransaction account address", + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + } + return std::result::Result::Ok(Self { addresses }); + } + + fn request_value(&self) -> serde_json::Value { + let addresses = self + .addresses + .iter() + .map(|address| return serde_json::Value::String(address.0.clone())) + .collect::>(); + return serde_json::json!({ + "encoding": "base64", + "addresses": addresses + }); + } +} + +/// Configuration for `simulateTransaction`. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct SimulateTransactionConfig { + /// Requested commitment. + pub commitment: crate::execution_rpc::RpcCommitmentLevel, + /// Whether transaction signatures must be verified by the node. + pub signature_verification: bool, + /// Whether the node should replace the recent blockhash before simulation. + pub replace_recent_blockhash: bool, + /// Optional minimum context slot. + pub min_context_slot: std::option::Option, + /// Whether inner instructions should be returned. + pub inner_instructions: bool, + /// Optional post-simulation account values to return. + pub accounts: std::option::Option, +} + +impl crate::execution_rpc::SimulateTransactionConfig { + /// Creates and validates a simulation request configuration. + pub fn new( + commitment: crate::execution_rpc::RpcCommitmentLevel, + signature_verification: bool, + replace_recent_blockhash: bool, + min_context_slot: std::option::Option, + inner_instructions: bool, + accounts: std::option::Option, + ) -> kb_core::Result { + if signature_verification && replace_recent_blockhash { + return std::result::Result::Err(kb_core::Error::config( + "simulateTransaction cannot verify signatures while replacing the recent blockhash", + )); + } + return std::result::Result::Ok(Self { + commitment, + signature_verification, + replace_recent_blockhash, + min_context_slot, + inner_instructions, + accounts, + }); + } + + /// Creates an unsigned simulation configuration for the exact supplied blockhash. + pub fn unsigned_exact() -> Self { + return Self { + commitment: crate::execution_rpc::RpcCommitmentLevel::Confirmed, + signature_verification: false, + replace_recent_blockhash: false, + min_context_slot: std::option::Option::None, + inner_instructions: true, + accounts: std::option::Option::None, + }; + } + + /// Creates an unsigned diagnostic simulation that replaces the recent blockhash. + /// + /// Its result cannot authorize signing of the original message. + pub fn unsigned_with_replacement() -> Self { + return Self { + commitment: crate::execution_rpc::RpcCommitmentLevel::Confirmed, + signature_verification: false, + replace_recent_blockhash: true, + min_context_slot: std::option::Option::None, + inner_instructions: true, + accounts: std::option::Option::None, + }; + } + + fn request_params( + &self, + encoded_transaction: &str, + ) -> kb_core::Result> { + let validation_result = crate::execution_rpc::validate_base64_payload( + encoded_transaction, + "simulateTransaction transaction", + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let mut config = serde_json::Map::::new(); + config.insert( + "commitment".to_string(), + serde_json::Value::String(self.commitment.as_str().to_string()), + ); + config.insert("encoding".to_string(), serde_json::Value::String("base64".to_string())); + config + .insert("sigVerify".to_string(), serde_json::Value::Bool(self.signature_verification)); + config.insert( + "replaceRecentBlockhash".to_string(), + serde_json::Value::Bool(self.replace_recent_blockhash), + ); + config.insert( + "innerInstructions".to_string(), + serde_json::Value::Bool(self.inner_instructions), + ); + if let std::option::Option::Some(min_context_slot) = self.min_context_slot { + config.insert( + "minContextSlot".to_string(), + serde_json::Value::Number(serde_json::Number::from(min_context_slot)), + ); + } + if let std::option::Option::Some(accounts) = &self.accounts { + config.insert("accounts".to_string(), accounts.request_value()); + } + return std::result::Result::Ok(std::vec![ + serde_json::Value::String(encoded_transaction.to_string()), + serde_json::Value::Object(config), + ]); + } +} + +/// Replacement blockhash returned by an unsigned simulation. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct SimulationReplacementBlockhash { + /// Base58 replacement blockhash. + pub blockhash: std::string::String, + /// Last valid block height for the replacement blockhash. + pub last_valid_block_height: u64, +} + +/// Typed result returned by `simulateTransaction`. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SimulateTransactionResult { + /// RPC response context. + pub context: crate::execution_rpc::RpcResponseContext, + /// Whether the transaction completed without a runtime error. + pub success: bool, + /// Runtime error value returned by the node. + pub error: std::option::Option, + /// Runtime logs in execution order. + pub logs: std::vec::Vec, + /// Compute units consumed when reported. + pub units_consumed: std::option::Option, + /// Fee reported by simulation when supported by the node. + pub fee_lamports: std::option::Option, + /// Total loaded account data size when reported. + pub loaded_accounts_data_size: std::option::Option, + /// Replacement recent blockhash when requested. + pub replacement_blockhash: + std::option::Option, + /// Program return data retained as provider-neutral JSON. + pub return_data: std::option::Option, + /// Inner instructions retained as provider-neutral JSON. + pub inner_instructions: std::option::Option, + /// Requested account snapshots retained as provider-neutral JSON. + pub accounts: std::option::Option>>, +} + +impl crate::execution_rpc::SimulateTransactionResult { + /// Converts this RPC result into the common execution simulation contract. + pub fn to_execution_result( + &self, + cluster: kb_execution_api::ExecutionCluster, + blockhash_kind: kb_execution_api::ExecutionBlockhashKind, + blockhash_age_slots: std::option::Option, + nonce_account: std::option::Option, + nonce_authority: std::option::Option, + fee_fallback: std::option::Option<&crate::execution_rpc::FeeForMessageResult>, + ) -> kb_execution_api::ExecutionSimulationResult { + let estimated_fee_lamports = match self.fee_lamports { + std::option::Option::Some(fee) => std::option::Option::Some(fee), + std::option::Option::None => fee_fallback.and_then(|result| return result.fee_lamports), + }; + let error = self.error.as_ref().map(|value| return value.to_string()); + let replacement_blockhash = self + .replacement_blockhash + .as_ref() + .map(|replacement| return replacement.blockhash.clone()); + let replacement_last_valid_block_height = self + .replacement_blockhash + .as_ref() + .map(|replacement| return replacement.last_valid_block_height); + return kb_execution_api::ExecutionSimulationResult { + simulated: true, + success: self.success, + cluster, + blockhash_kind, + blockhash_age_slots, + replacement_blockhash, + replacement_last_valid_block_height, + nonce_account, + nonce_authority, + units_consumed: self.units_consumed, + estimated_fee_lamports, + logs: self.logs.clone(), + error, + }; + } +} + +/// Configuration for `getBalance`. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetBalanceConfig { + /// Requested commitment. + pub commitment: crate::execution_rpc::RpcCommitmentLevel, + /// Optional minimum context slot. + pub min_context_slot: std::option::Option, +} + +impl crate::execution_rpc::GetBalanceConfig { + /// Creates a balance request configuration. + pub fn new( + commitment: crate::execution_rpc::RpcCommitmentLevel, + min_context_slot: std::option::Option, + ) -> Self { + return Self { commitment, min_context_slot }; + } + + /// Creates the default confirmed balance request. + pub fn confirmed() -> Self { + return Self::new( + crate::execution_rpc::RpcCommitmentLevel::Confirmed, + std::option::Option::None, + ); + } + + fn request_params( + &self, + pubkey: &kb_model::Pubkey, + ) -> kb_core::Result> { + let validation_result = + crate::validate_solana_pubkey_text(pubkey.0.as_str(), "getBalance account"); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let mut config = serde_json::Map::::new(); + config.insert( + "commitment".to_string(), + serde_json::Value::String(self.commitment.as_str().to_string()), + ); + if let std::option::Option::Some(min_context_slot) = self.min_context_slot { + config.insert( + "minContextSlot".to_string(), + serde_json::Value::Number(serde_json::Number::from(min_context_slot)), + ); + } + return std::result::Result::Ok(std::vec![ + serde_json::Value::String(pubkey.0.clone()), + serde_json::Value::Object(config), + ]); + } +} + +/// Lamport balance returned for one account. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct BalanceResult { + /// RPC response context. + pub context: crate::execution_rpc::RpcResponseContext, + /// Account balance in lamports. + pub lamports: u64, +} + +/// Configuration for `getAccountInfo` used by execution readiness checks. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetAccountInfoConfig { + /// Requested commitment. + pub commitment: crate::execution_rpc::RpcCommitmentLevel, + /// Optional minimum context slot. + pub min_context_slot: std::option::Option, + /// Maximum complete decoded account-data length, or `None` for metadata-only requests. + pub max_data_bytes: std::option::Option, +} + +impl crate::execution_rpc::GetAccountInfoConfig { + /// Creates an account information request configuration. + pub fn new( + commitment: crate::execution_rpc::RpcCommitmentLevel, + min_context_slot: std::option::Option, + ) -> Self { + return Self { + commitment, + min_context_slot, + max_data_bytes: std::option::Option::None, + }; + } + + /// Creates a complete account-data request bounded by the decoded byte limit. + pub fn new_with_data( + commitment: crate::execution_rpc::RpcCommitmentLevel, + min_context_slot: std::option::Option, + max_data_bytes: usize, + ) -> kb_core::Result { + if max_data_bytes == 0 + || max_data_bytes > crate::constants::MAX_EXECUTION_ACCOUNT_DATA_BYTES + { + return std::result::Result::Err(kb_core::Error::config(format!( + "getAccountInfo complete data limit must be between 1 and {} bytes", + crate::constants::MAX_EXECUTION_ACCOUNT_DATA_BYTES + ))); + } + return std::result::Result::Ok(Self { + commitment, + min_context_slot, + max_data_bytes: std::option::Option::Some(max_data_bytes), + }); + } + + /// Creates the default confirmed metadata-only account information request. + pub fn confirmed() -> Self { + return Self::new( + crate::execution_rpc::RpcCommitmentLevel::Confirmed, + std::option::Option::None, + ); + } + + /// Creates a confirmed complete account-data request bounded by the decoded byte limit. + pub fn confirmed_with_data(max_data_bytes: usize) -> kb_core::Result { + return Self::new_with_data( + crate::execution_rpc::RpcCommitmentLevel::Confirmed, + std::option::Option::None, + max_data_bytes, + ); + } + + fn request_params( + &self, + pubkey: &kb_model::Pubkey, + ) -> kb_core::Result> { + if let std::option::Option::Some(max_data_bytes) = self.max_data_bytes { + if max_data_bytes == 0 + || max_data_bytes > crate::constants::MAX_EXECUTION_ACCOUNT_DATA_BYTES + { + return std::result::Result::Err(kb_core::Error::config(format!( + "getAccountInfo complete data limit must be between 1 and {} bytes", + crate::constants::MAX_EXECUTION_ACCOUNT_DATA_BYTES + ))); + } + } + let validation_result = + crate::validate_solana_pubkey_text(pubkey.0.as_str(), "getAccountInfo account"); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let mut config = serde_json::Map::::new(); + config.insert( + "commitment".to_string(), + serde_json::Value::String(self.commitment.as_str().to_string()), + ); + config.insert("encoding".to_string(), serde_json::Value::String("base64".to_string())); + if self.max_data_bytes.is_none() { + config.insert("dataSlice".to_string(), serde_json::json!({"offset": 0, "length": 0})); + } + if let std::option::Option::Some(min_context_slot) = self.min_context_slot { + config.insert( + "minContextSlot".to_string(), + serde_json::Value::Number(serde_json::Number::from(min_context_slot)), + ); + } + return std::result::Result::Ok(std::vec![ + serde_json::Value::String(pubkey.0.clone()), + serde_json::Value::Object(config), + ]); + } +} + +/// Bounded account metadata and optional complete decoded data returned by `getAccountInfo`. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct AccountInfoValue { + /// Account balance in lamports. + pub lamports: u64, + /// Program that owns the account. + pub owner: kb_model::ProgramId, + /// Whether the account contains executable program code. + pub executable: bool, + /// Rent epoch reported by the node. + pub rent_epoch: u64, + /// Complete on-chain account-data length reported independently by the node. + pub space: u64, + /// Decoded account bytes, empty for metadata-only requests. + pub data: std::vec::Vec, +} + +/// Contextual result returned by `getAccountInfo`. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct AccountInfoResult { + /// RPC response context. + pub context: crate::execution_rpc::RpcResponseContext, + /// Account value, or `None` when the address does not exist. + pub account: std::option::Option, +} + +/// Configuration for `getMinimumBalanceForRentExemption`. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetMinimumBalanceForRentExemptionConfig { + /// Requested commitment. + pub commitment: crate::execution_rpc::RpcCommitmentLevel, +} + +impl crate::execution_rpc::GetMinimumBalanceForRentExemptionConfig { + /// Creates a rent-exemption request configuration. + pub fn new(commitment: crate::execution_rpc::RpcCommitmentLevel) -> Self { + return Self { commitment }; + } + + /// Creates the default confirmed rent-exemption request. + pub fn confirmed() -> Self { + return Self::new(crate::execution_rpc::RpcCommitmentLevel::Confirmed); + } + + fn request_params(&self, data_length: u64) -> std::vec::Vec { + return std::vec![ + serde_json::Value::Number(serde_json::Number::from(data_length)), + serde_json::json!({"commitment": self.commitment.as_str()}), + ]; + } +} + +/// Rent-exempt minimum returned for one account data length. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct MinimumBalanceForRentExemptionResult { + /// Account data length used by the request. + pub data_length: u64, + /// Minimum balance in lamports. + pub minimum_balance_lamports: u64, +} + +/// Configuration for `requestAirdrop`. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct RequestAirdropConfig { + /// Requested commitment. + pub commitment: crate::execution_rpc::RpcCommitmentLevel, + /// Optional recent blockhash supplied to the faucet. + pub recent_blockhash: std::option::Option, +} + +impl crate::execution_rpc::RequestAirdropConfig { + /// Creates an airdrop request configuration. + pub fn new( + commitment: crate::execution_rpc::RpcCommitmentLevel, + recent_blockhash: std::option::Option, + ) -> kb_core::Result { + if let std::option::Option::Some(blockhash) = &recent_blockhash { + let validation_result = crate::validate_solana_hash_text( + blockhash.as_str(), + "requestAirdrop recent blockhash", + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + } + return std::result::Result::Ok(Self { commitment, recent_blockhash }); + } + + /// Creates the default confirmed airdrop request. + pub fn confirmed() -> Self { + return Self { + commitment: crate::execution_rpc::RpcCommitmentLevel::Confirmed, + recent_blockhash: std::option::Option::None, + }; + } + + fn request_params( + &self, + pubkey: &kb_model::Pubkey, + lamports: u64, + ) -> kb_core::Result> { + let validation_result = + crate::validate_solana_pubkey_text(pubkey.0.as_str(), "requestAirdrop recipient"); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + if lamports == 0 { + return std::result::Result::Err(kb_core::Error::config( + "requestAirdrop lamports must be greater than zero", + )); + } + let mut config = serde_json::Map::::new(); + config.insert( + "commitment".to_string(), + serde_json::Value::String(self.commitment.as_str().to_string()), + ); + if let std::option::Option::Some(blockhash) = &self.recent_blockhash { + config.insert( + "recentBlockhash".to_string(), + serde_json::Value::String(blockhash.clone()), + ); + } + return std::result::Result::Ok(std::vec![ + serde_json::Value::String(pubkey.0.clone()), + serde_json::Value::Number(serde_json::Number::from(lamports)), + serde_json::Value::Object(config), + ]); + } +} + +/// Signature returned by a faucet airdrop request. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct AirdropResult { + /// Faucet transaction signature. + pub signature: kb_model::Signature, +} + +/// Configuration for `sendTransaction`. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct SendTransactionConfig { + /// Whether the node should skip signature verification and preflight simulation. + pub skip_preflight: bool, + /// Commitment used by the node's preflight simulation. + pub preflight_commitment: crate::execution_rpc::RpcCommitmentLevel, + /// Optional maximum number of node retransmission retries. + pub max_retries: std::option::Option, + /// Optional minimum context slot. + pub min_context_slot: std::option::Option, +} + +impl crate::execution_rpc::SendTransactionConfig { + /// Creates a validated transaction submission configuration. + pub fn new( + skip_preflight: bool, + preflight_commitment: crate::execution_rpc::RpcCommitmentLevel, + max_retries: std::option::Option, + min_context_slot: std::option::Option, + ) -> kb_core::Result { + if skip_preflight { + return std::result::Result::Err(kb_core::Error::config( + "sendTransaction preflight cannot be skipped by execution orchestration", + )); + } + return std::result::Result::Ok(Self { + skip_preflight, + preflight_commitment, + max_retries, + min_context_slot, + }); + } + + /// Creates the default confirmed submission configuration with three retries. + pub fn confirmed() -> Self { + return Self { + skip_preflight: false, + preflight_commitment: crate::execution_rpc::RpcCommitmentLevel::Confirmed, + max_retries: std::option::Option::Some(3), + min_context_slot: std::option::Option::None, + }; + } + + /// Creates a confirmed submission configuration from the active execution settings. + pub fn from_execution_config( + config: &kb_config::ExecutionConfig, + min_context_slot: std::option::Option, + ) -> kb_core::Result { + return Self::new( + false, + crate::execution_rpc::RpcCommitmentLevel::Confirmed, + std::option::Option::Some(config.send_max_retries), + min_context_slot, + ); + } + + fn request_params( + &self, + encoded_transaction: &str, + ) -> kb_core::Result> { + let validation_result = crate::execution_rpc::validate_base64_payload( + encoded_transaction, + "sendTransaction signed transaction", + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let mut config = serde_json::Map::::new(); + config.insert("encoding".to_string(), serde_json::Value::String("base64".to_string())); + config.insert("skipPreflight".to_string(), serde_json::Value::Bool(self.skip_preflight)); + config.insert( + "preflightCommitment".to_string(), + serde_json::Value::String(self.preflight_commitment.as_str().to_string()), + ); + if let std::option::Option::Some(max_retries) = self.max_retries { + config.insert( + "maxRetries".to_string(), + serde_json::Value::Number(serde_json::Number::from(max_retries)), + ); + } + if let std::option::Option::Some(min_context_slot) = self.min_context_slot { + config.insert( + "minContextSlot".to_string(), + serde_json::Value::Number(serde_json::Number::from(min_context_slot)), + ); + } + return std::result::Result::Ok(std::vec![ + serde_json::Value::String(encoded_transaction.to_string()), + serde_json::Value::Object(config), + ]); + } +} + +/// Result returned after a node accepts a signed transaction for relay. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SendTransactionResult { + /// First signature embedded in the submitted transaction. + pub signature: kb_model::Signature, +} + +impl crate::execution_rpc::SendTransactionResult { + /// Converts the RPC result into the common execution send contract. + pub fn to_execution_result( + &self, + cluster: kb_execution_api::ExecutionCluster, + ) -> kb_execution_api::ExecutionSendResult { + return kb_execution_api::ExecutionSendResult { + cluster, + signature: self.signature.clone(), + submitted: true, + }; + } +} + +/// Configuration for `getSignatureStatuses`. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetSignatureStatusesConfig { + /// Whether the node may search its full transaction history. + pub search_transaction_history: bool, +} + +impl crate::execution_rpc::GetSignatureStatusesConfig { + /// Creates a status request configuration. + pub fn new(search_transaction_history: bool) -> Self { + return Self { search_transaction_history }; + } + + fn request_params( + &self, + signatures: &[kb_model::Signature], + ) -> kb_core::Result> { + if signatures.is_empty() { + return std::result::Result::Err(kb_core::Error::config( + "getSignatureStatuses requires at least one signature", + )); + } + if signatures.len() > crate::constants::MAX_SIGNATURE_STATUS_COUNT { + return std::result::Result::Err(kb_core::Error::config(format!( + "getSignatureStatuses supports at most {} signatures", + crate::constants::MAX_SIGNATURE_STATUS_COUNT + ))); + } + let mut values = std::vec::Vec::with_capacity(signatures.len()); + for signature in signatures { + let validation_result = crate::validate_transaction_signature_text( + signature.0.as_str(), + "getSignatureStatuses signature", + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + values.push(serde_json::Value::String(signature.0.clone())); + } + return std::result::Result::Ok(std::vec![ + serde_json::Value::Array(values), + serde_json::json!({ + "searchTransactionHistory": self.search_transaction_history + }), + ]); + } +} + +/// Current status for one submitted transaction signature. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SignatureStatus { + /// Slot that processed the transaction. + pub slot: u64, + /// Number of blocks since processing, or `None` once rooted/finalized. + pub confirmations: std::option::Option, + /// Runtime transaction error when execution failed. + pub error: std::option::Option, + /// Highest commitment reported by the node. + pub confirmation_status: std::option::Option, +} + +/// Status response preserving positional correspondence with requested signatures. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct SignatureStatusesResult { + /// RPC response context. + pub context: crate::execution_rpc::RpcResponseContext, + /// Status values in request order; absent entries have not been observed. + pub statuses: std::vec::Vec>, +} + +/// Configuration for `getBlockHeight`. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetBlockHeightConfig { + /// Requested commitment. + pub commitment: crate::execution_rpc::RpcCommitmentLevel, + /// Optional minimum context slot. + pub min_context_slot: std::option::Option, +} + +impl crate::execution_rpc::GetBlockHeightConfig { + /// Creates a block-height request configuration. + pub fn new( + commitment: crate::execution_rpc::RpcCommitmentLevel, + min_context_slot: std::option::Option, + ) -> Self { + return Self { commitment, min_context_slot }; + } + + fn request_params(&self) -> std::vec::Vec { + let mut config = serde_json::Map::::new(); + config.insert( + "commitment".to_string(), + serde_json::Value::String(self.commitment.as_str().to_string()), + ); + if let std::option::Option::Some(min_context_slot) = self.min_context_slot { + config.insert( + "minContextSlot".to_string(), + serde_json::Value::Number(serde_json::Number::from(min_context_slot)), + ); + } + return std::vec![serde_json::Value::Object(config)]; + } +} + +/// Current block height returned by the node. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct BlockHeightResult { + /// Block height observed at the requested commitment. + pub block_height: u64, +} + +/// Bounded polling policy used to confirm one submitted transaction. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ConfirmTransactionConfig { + /// Commitment required before confirmation succeeds. + pub commitment: crate::execution_rpc::RpcCommitmentLevel, + /// Whether status polling may search transaction history. + pub search_transaction_history: bool, + /// Delay between status polls. + pub poll_interval_ms: u64, + /// Maximum number of status polls. + pub max_attempts: u32, + /// Last valid block height of the recent blockhash, when applicable. + pub last_valid_block_height: std::option::Option, + /// Optional minimum context slot for block-height checks. + pub min_context_slot: std::option::Option, +} + +impl crate::execution_rpc::ConfirmTransactionConfig { + /// Creates a bounded confirmation policy. + pub fn new( + commitment: crate::execution_rpc::RpcCommitmentLevel, + search_transaction_history: bool, + poll_interval_ms: u64, + max_attempts: u32, + last_valid_block_height: std::option::Option, + min_context_slot: std::option::Option, + ) -> kb_core::Result { + if poll_interval_ms == 0 + || poll_interval_ms > crate::constants::MAX_CONFIRMATION_POLL_INTERVAL_MS + { + return std::result::Result::Err(kb_core::Error::config(format!( + "confirmation poll interval must be between 1 and {} milliseconds", + crate::constants::MAX_CONFIRMATION_POLL_INTERVAL_MS + ))); + } + if max_attempts == 0 || max_attempts > crate::constants::MAX_CONFIRMATION_ATTEMPTS { + return std::result::Result::Err(kb_core::Error::config(format!( + "confirmation attempts must be between 1 and {}", + crate::constants::MAX_CONFIRMATION_ATTEMPTS + ))); + } + return std::result::Result::Ok(Self { + commitment, + search_transaction_history, + poll_interval_ms, + max_attempts, + last_valid_block_height, + min_context_slot, + }); + } + + /// Creates a confirmed recent-blockhash confirmation policy. + pub fn confirmed( + last_valid_block_height: u64, + poll_interval_ms: u64, + max_attempts: u32, + ) -> kb_core::Result { + return Self::new( + crate::execution_rpc::RpcCommitmentLevel::Confirmed, + false, + poll_interval_ms, + max_attempts, + std::option::Option::Some(last_valid_block_height), + std::option::Option::None, + ); + } + + /// Creates a confirmed bounded policy from the active execution settings. + pub fn from_execution_config( + config: &kb_config::ExecutionConfig, + last_valid_block_height: std::option::Option, + min_context_slot: std::option::Option, + ) -> kb_core::Result { + return Self::new( + crate::execution_rpc::RpcCommitmentLevel::Confirmed, + false, + config.confirmation_poll_interval_ms, + config.confirmation_max_attempts, + last_valid_block_height, + min_context_slot, + ); + } +} + +/// Classifies a genesis hash as one of the three official public clusters. +pub fn classify_genesis_hash( + genesis_hash: &str, +) -> std::option::Option { + return match genesis_hash { + crate::constants::DEVNET_GENESIS_HASH => { + std::option::Option::Some(kb_execution_api::ExecutionCluster::Devnet) + }, + crate::constants::TESTNET_GENESIS_HASH => { + std::option::Option::Some(kb_execution_api::ExecutionCluster::Testnet) + }, + crate::constants::MAINNET_GENESIS_HASH => { + std::option::Option::Some(kb_execution_api::ExecutionCluster::Mainnet) + }, + _ => std::option::Option::None, + }; +} + +/// Adapts a raw `getGenesisHash` result. +pub fn adapt_get_genesis_hash_result( + source: &serde_json::Value, +) -> kb_core::Result { + let genesis_hash = match source.as_str() { + std::option::Option::Some(value) => value.to_string(), + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::json( + "getGenesisHash result must be a string", + )); + }, + }; + let validation_result = + crate::validate_solana_hash_text(genesis_hash.as_str(), "getGenesisHash result"); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(crate::execution_rpc::GenesisHashResult { + classified_cluster: crate::execution_rpc::classify_genesis_hash(genesis_hash.as_str()), + genesis_hash, + }); +} + +/// Adapts a raw `getLatestBlockhash` result. +pub fn adapt_get_latest_blockhash_result( + source: &serde_json::Value, +) -> kb_core::Result { + let parse_result = + serde_json::from_value::>(source.clone()); + let parsed = match parse_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot parse getLatestBlockhash result: {error}" + ))); + }, + }; + let validation_result = crate::validate_solana_hash_text( + parsed.value.blockhash.as_str(), + "getLatestBlockhash blockhash", + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(crate::execution_rpc::LatestBlockhashResult { + context: parsed.context, + blockhash: parsed.value.blockhash, + last_valid_block_height: parsed.value.last_valid_block_height, + }); +} + +/// Adapts a raw `getFeeForMessage` result. +pub fn adapt_get_fee_for_message_result( + source: &serde_json::Value, +) -> kb_core::Result { + let parse_result = + serde_json::from_value::>>(source.clone()); + let parsed = match parse_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot parse getFeeForMessage result: {error}" + ))); + }, + }; + return std::result::Result::Ok(crate::execution_rpc::FeeForMessageResult { + context: parsed.context, + fee_lamports: parsed.value, + }); +} + +/// Adapts a raw `simulateTransaction` result. +pub fn adapt_simulate_transaction_result( + source: &serde_json::Value, +) -> kb_core::Result { + let parse_result = + serde_json::from_value::>(source.clone()); + let parsed = match parse_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot parse simulateTransaction result: {error}" + ))); + }, + }; + if let std::option::Option::Some(replacement) = &parsed.value.replacement_blockhash { + let validation_result = crate::validate_solana_hash_text( + replacement.blockhash.as_str(), + "simulateTransaction replacement blockhash", + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + } + let logs = match parsed.value.logs { + std::option::Option::Some(logs) => logs, + std::option::Option::None => std::vec::Vec::new(), + }; + return std::result::Result::Ok(crate::execution_rpc::SimulateTransactionResult { + context: parsed.context, + success: parsed.value.error.is_none(), + error: parsed.value.error, + logs, + units_consumed: parsed.value.units_consumed, + fee_lamports: parsed.value.fee, + loaded_accounts_data_size: parsed.value.loaded_accounts_data_size, + replacement_blockhash: parsed.value.replacement_blockhash, + return_data: parsed.value.return_data, + inner_instructions: parsed.value.inner_instructions, + accounts: parsed.value.accounts, + }); +} + +/// Adapts a raw `getBalance` result. +pub fn adapt_get_balance_result( + source: &serde_json::Value, +) -> kb_core::Result { + let parse_result = serde_json::from_value::>(source.clone()); + let parsed = match parse_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot parse getBalance result: {error}" + ))); + }, + }; + return std::result::Result::Ok(crate::execution_rpc::BalanceResult { + context: parsed.context, + lamports: parsed.value, + }); +} + +/// Adapts a raw metadata-only `getAccountInfo` result. +pub fn adapt_get_account_info_result( + source: &serde_json::Value, +) -> kb_core::Result { + return crate::execution_rpc::adapt_get_account_info_result_with_data_limit( + source, + std::option::Option::None, + ); +} + +fn adapt_get_account_info_result_with_data_limit( + source: &serde_json::Value, + max_data_bytes: std::option::Option, +) -> kb_core::Result { + let context = match source.get("context") { + std::option::Option::Some(value) => { + let parse_result = + serde_json::from_value::(value.clone()); + match parse_result { + std::result::Result::Ok(context) => context, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot parse getAccountInfo context: {error}" + ))); + }, + } + }, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::json( + "getAccountInfo result is missing context", + )); + }, + }; + let value = match source.get("value") { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::json( + "getAccountInfo result is missing value", + )); + }, + }; + if value.is_null() { + return std::result::Result::Ok(crate::execution_rpc::AccountInfoResult { + context, + account: std::option::Option::None, + }); + } + let lamports = match value.get("lamports").and_then(serde_json::Value::as_u64) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::json( + "getAccountInfo account lamports must be an unsigned integer", + )); + }, + }; + let owner_text = match value.get("owner").and_then(serde_json::Value::as_str) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::json( + "getAccountInfo account owner must be a string", + )); + }, + }; + let owner_validation = + crate::validate_solana_pubkey_text(owner_text, "getAccountInfo account owner"); + if let std::result::Result::Err(error) = owner_validation { + return std::result::Result::Err(error); + } + let executable = match value.get("executable").and_then(serde_json::Value::as_bool) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::json( + "getAccountInfo account executable must be a boolean", + )); + }, + }; + let rent_epoch = match value.get("rentEpoch").and_then(serde_json::Value::as_u64) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::json( + "getAccountInfo account rentEpoch must be an unsigned integer", + )); + }, + }; + let space = match value.get("space").and_then(serde_json::Value::as_u64) { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::json( + "getAccountInfo account space must be an unsigned integer", + )); + }, + }; + let data = match crate::execution_rpc::decode_account_data(value, space, max_data_bytes) { + std::result::Result::Ok(data) => data, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(crate::execution_rpc::AccountInfoResult { + context, + account: std::option::Option::Some(crate::execution_rpc::AccountInfoValue { + lamports, + owner: kb_model::ProgramId(owner_text.to_string()), + executable, + rent_epoch, + space, + data, + }), + }); +} + +fn decode_account_data( + value: &serde_json::Value, + space: u64, + max_data_bytes: std::option::Option, +) -> kb_core::Result> { + let tuple = match value.get("data").and_then(serde_json::Value::as_array) { + std::option::Option::Some(tuple) if tuple.len() == 2 => tuple, + _ => { + return std::result::Result::Err(kb_core::Error::json( + "getAccountInfo account data must be a two-element encoded tuple", + )); + }, + }; + let encoded = match tuple.first().and_then(serde_json::Value::as_str) { + std::option::Option::Some(encoded) => encoded, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::json( + "getAccountInfo account data payload must be a string", + )); + }, + }; + let encoding = match tuple.get(1).and_then(serde_json::Value::as_str) { + std::option::Option::Some(encoding) => encoding, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::json( + "getAccountInfo account data encoding must be a string", + )); + }, + }; + if encoding != "base64" { + return std::result::Result::Err(kb_core::Error::json(format!( + "getAccountInfo account data encoding {encoding} is unsupported" + ))); + } + match max_data_bytes { + std::option::Option::None => { + if !encoded.is_empty() { + return std::result::Result::Err(kb_core::Error::json( + "metadata-only getAccountInfo returned non-empty account data", + )); + } + }, + std::option::Option::Some(max_data_bytes) => { + if max_data_bytes == 0 + || max_data_bytes > crate::constants::MAX_EXECUTION_ACCOUNT_DATA_BYTES + { + return std::result::Result::Err(kb_core::Error::json(format!( + "getAccountInfo complete data limit must be between 1 and {} bytes", + crate::constants::MAX_EXECUTION_ACCOUNT_DATA_BYTES + ))); + } + if space > max_data_bytes as u64 { + return std::result::Result::Err(kb_core::Error::json(format!( + "getAccountInfo account space {space} exceeds the requested complete-data limit {max_data_bytes}" + ))); + } + let maximum_encoded_length = max_data_bytes.div_ceil(3) * 4; + if encoded.len() > maximum_encoded_length { + return std::result::Result::Err(kb_core::Error::json(format!( + "getAccountInfo encoded account data length {} exceeds the limit {maximum_encoded_length}", + encoded.len() + ))); + } + }, + } + let decoded = match base64::engine::general_purpose::STANDARD.decode(encoded) { + std::result::Result::Ok(decoded) => decoded, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot decode getAccountInfo base64 account data: {error}" + ))); + }, + }; + if let std::option::Option::Some(max_data_bytes) = max_data_bytes { + if decoded.len() > max_data_bytes { + return std::result::Result::Err(kb_core::Error::json(format!( + "getAccountInfo decoded account data length {} exceeds the requested limit {max_data_bytes}", + decoded.len() + ))); + } + if decoded.len() as u64 != space { + return std::result::Result::Err(kb_core::Error::json(format!( + "getAccountInfo returned {} decoded bytes for an account reporting {space} bytes", + decoded.len() + ))); + } + } + return std::result::Result::Ok(decoded); +} + +/// Adapts a raw `getMinimumBalanceForRentExemption` result. +pub fn adapt_get_minimum_balance_for_rent_exemption_result( + source: &serde_json::Value, + data_length: u64, +) -> kb_core::Result { + let minimum_balance_lamports = match source.as_u64() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::json( + "getMinimumBalanceForRentExemption result must be an unsigned integer", + )); + }, + }; + return std::result::Result::Ok(crate::execution_rpc::MinimumBalanceForRentExemptionResult { + data_length, + minimum_balance_lamports, + }); +} + +/// Adapts a raw `requestAirdrop` result. +pub fn adapt_request_airdrop_result( + source: &serde_json::Value, +) -> kb_core::Result { + let signature = + match crate::execution_rpc::adapt_signature_result(source, "requestAirdrop result") { + std::result::Result::Ok(signature) => signature, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(crate::execution_rpc::AirdropResult { signature }); +} + +/// Adapts a raw `sendTransaction` result and verifies the returned primary signature. +pub fn adapt_send_transaction_result( + source: &serde_json::Value, + expected_signature: &kb_model::Signature, +) -> kb_core::Result { + let expected_validation = crate::validate_transaction_signature_text( + expected_signature.0.as_str(), + "sendTransaction expected signature", + ); + if let std::result::Result::Err(error) = expected_validation { + return std::result::Result::Err(error); + } + let signature = + match crate::execution_rpc::adapt_signature_result(source, "sendTransaction result") { + std::result::Result::Ok(signature) => signature, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if signature != *expected_signature { + return std::result::Result::Err(kb_core::Error::new( + "execution_send_signature_mismatch", + format!( + "sendTransaction returned signature '{}' but the signed transaction primary signature is '{}'", + signature.0, expected_signature.0 + ), + )); + } + return std::result::Result::Ok(crate::execution_rpc::SendTransactionResult { signature }); +} + +/// Adapts a raw `getSignatureStatuses` result. +pub fn adapt_get_signature_statuses_result( + source: &serde_json::Value, +) -> kb_core::Result { + let parse_result = serde_json::from_value::< + RpcContextValue>>, + >(source.clone()); + let parsed = match parse_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot parse getSignatureStatuses result: {error}" + ))); + }, + }; + let mut statuses = std::vec::Vec::with_capacity(parsed.value.len()); + for value in parsed.value { + let status = value.map(|value| { + return crate::execution_rpc::SignatureStatus { + slot: value.slot, + confirmations: value.confirmations, + error: value.error, + confirmation_status: value.confirmation_status, + }; + }); + statuses.push(status); + } + return std::result::Result::Ok(crate::execution_rpc::SignatureStatusesResult { + context: parsed.context, + statuses, + }); +} + +/// Adapts a raw `getEpochInfo` result. +pub fn adapt_get_epoch_info_result( + source: &serde_json::Value, +) -> kb_core::Result { + let parse_result = + serde_json::from_value::(source.clone()); + return match parse_result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::json(format!( + "cannot parse getEpochInfo result: {error}" + ))), + }; +} + +/// Adapts a raw `getBlockHeight` result. +pub fn adapt_get_block_height_result( + source: &serde_json::Value, +) -> kb_core::Result { + let block_height = match source.as_u64() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::json( + "getBlockHeight result must be an unsigned integer", + )); + }, + }; + return std::result::Result::Ok(crate::execution_rpc::BlockHeightResult { block_height }); +} + +impl crate::HttpClient { + /// Returns the connected cluster genesis hash and known public-cluster classification. + pub async fn get_genesis_hash( + &self, + ) -> kb_core::Result { + let result = self + .execute_json_rpc_result_raw("getGenesisHash".to_string(), std::vec::Vec::new()) + .await; + let value = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::execution_rpc::adapt_get_genesis_hash_result(&value); + } + + /// Returns current epoch and slot progression information. + pub async fn get_epoch_info( + &self, + config: &crate::execution_rpc::GetEpochInfoConfig, + ) -> kb_core::Result { + let result = self + .execute_json_rpc_result_raw("getEpochInfo".to_string(), config.request_params()) + .await; + let value = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::execution_rpc::adapt_get_epoch_info_result(&value); + } + + /// Returns the latest blockhash and its last valid block height. + pub async fn get_latest_blockhash( + &self, + config: &crate::execution_rpc::GetLatestBlockhashConfig, + ) -> kb_core::Result { + let result = self + .execute_json_rpc_result_raw("getLatestBlockhash".to_string(), config.request_params()) + .await; + let value = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::execution_rpc::adapt_get_latest_blockhash_result(&value); + } + + /// Returns the fee the cluster would charge for a serialized message. + pub async fn get_fee_for_message( + &self, + encoded_message: &str, + config: &crate::execution_rpc::GetFeeForMessageConfig, + ) -> kb_core::Result { + let params_result = config.request_params(encoded_message); + let params = match params_result { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let result = self.execute_json_rpc_result_raw("getFeeForMessage".to_string(), params).await; + let value = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::execution_rpc::adapt_get_fee_for_message_result(&value); + } + + /// Simulates one base64-encoded transaction without broadcasting it. + pub async fn simulate_transaction( + &self, + encoded_transaction: &str, + config: &crate::execution_rpc::SimulateTransactionConfig, + ) -> kb_core::Result { + let params_result = config.request_params(encoded_transaction); + let params = match params_result { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let result = self + .execute_json_rpc_result_raw("simulateTransaction".to_string(), params) + .await; + let value = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::execution_rpc::adapt_simulate_transaction_result(&value); + } + + /// Returns the lamport balance for one account. + pub async fn get_balance( + &self, + pubkey: &kb_model::Pubkey, + config: &crate::execution_rpc::GetBalanceConfig, + ) -> kb_core::Result { + let params = match config.request_params(pubkey) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let result = self.execute_json_rpc_result_raw("getBalance".to_string(), params).await; + let value = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::execution_rpc::adapt_get_balance_result(&value); + } + + /// Returns minimal account metadata or `None` when the address does not exist. + pub async fn get_account_info( + &self, + pubkey: &kb_model::Pubkey, + config: &crate::execution_rpc::GetAccountInfoConfig, + ) -> kb_core::Result { + let params = match config.request_params(pubkey) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let result = self.execute_json_rpc_result_raw("getAccountInfo".to_string(), params).await; + let value = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::execution_rpc::adapt_get_account_info_result_with_data_limit( + &value, + config.max_data_bytes, + ); + } + + /// Returns the rent-exempt minimum for one account data length. + pub async fn get_minimum_balance_for_rent_exemption( + &self, + data_length: u64, + config: &crate::execution_rpc::GetMinimumBalanceForRentExemptionConfig, + ) -> kb_core::Result { + let result = self + .execute_json_rpc_result_raw( + "getMinimumBalanceForRentExemption".to_string(), + config.request_params(data_length), + ) + .await; + let value = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::execution_rpc::adapt_get_minimum_balance_for_rent_exemption_result( + &value, + data_length, + ); + } + + /// Requests a faucet airdrop to one account. + pub async fn request_airdrop( + &self, + pubkey: &kb_model::Pubkey, + lamports: u64, + config: &crate::execution_rpc::RequestAirdropConfig, + ) -> kb_core::Result { + let params = match config.request_params(pubkey, lamports) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let result = self.execute_json_rpc_result_raw("requestAirdrop".to_string(), params).await; + let value = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::execution_rpc::adapt_request_airdrop_result(&value); + } + + /// Submits one fully signed base64 transaction and verifies the returned signature. + pub async fn send_transaction( + &self, + encoded_transaction: &str, + expected_signature: &kb_model::Signature, + config: &crate::execution_rpc::SendTransactionConfig, + ) -> kb_core::Result { + let params = match config.request_params(encoded_transaction) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let result = self.execute_json_rpc_result_raw("sendTransaction".to_string(), params).await; + let value = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let adapted = + match crate::execution_rpc::adapt_send_transaction_result(&value, expected_signature) { + std::result::Result::Ok(adapted) => adapted, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + tracing::info!( + target: crate::TRACING_TARGET, + action = "send_transaction", + endpoint_name = %self.endpoint_name(), + provider = %self.provider(), + signature = %adapted.signature.0, + status = "submitted", + "signed transaction accepted for relay" + ); + return std::result::Result::Ok(adapted); + } + + /// Returns current statuses for a bounded signature list. + pub async fn get_signature_statuses( + &self, + signatures: &[kb_model::Signature], + config: &crate::execution_rpc::GetSignatureStatusesConfig, + ) -> kb_core::Result { + let params = match config.request_params(signatures) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let result = self + .execute_json_rpc_result_raw("getSignatureStatuses".to_string(), params) + .await; + let value = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::execution_rpc::adapt_get_signature_statuses_result(&value); + } + + /// Returns the block height observed by the node. + pub async fn get_block_height( + &self, + config: &crate::execution_rpc::GetBlockHeightConfig, + ) -> kb_core::Result { + let result = self + .execute_json_rpc_result_raw("getBlockHeight".to_string(), config.request_params()) + .await; + let value = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::execution_rpc::adapt_get_block_height_result(&value); + } +} + +impl crate::HttpEndpointPool { + /// Executes `getGenesisHash` through one endpoint selected for the requested role. + pub async fn get_genesis_hash_for_role( + &self, + required_role: &str, + ) -> kb_core::Result { + let client_result = self.select_client_for_role_and_method(required_role, "getGenesisHash"); + let client = match client_result { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return client.get_genesis_hash().await; + } + + /// Executes `getEpochInfo` through one endpoint selected for the requested role. + pub async fn get_epoch_info_for_role( + &self, + required_role: &str, + config: &crate::execution_rpc::GetEpochInfoConfig, + ) -> kb_core::Result { + let client_result = self.select_client_for_role_and_method(required_role, "getEpochInfo"); + let client = match client_result { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return client.get_epoch_info(config).await; + } + + /// Executes `getLatestBlockhash` through one endpoint selected for the requested role. + pub async fn get_latest_blockhash_for_role( + &self, + required_role: &str, + config: &crate::execution_rpc::GetLatestBlockhashConfig, + ) -> kb_core::Result { + let client_result = + self.select_client_for_role_and_method(required_role, "getLatestBlockhash"); + let client = match client_result { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return client.get_latest_blockhash(config).await; + } + + /// Executes `getFeeForMessage` through one endpoint selected for the requested role. + pub async fn get_fee_for_message_for_role( + &self, + required_role: &str, + encoded_message: &str, + config: &crate::execution_rpc::GetFeeForMessageConfig, + ) -> kb_core::Result { + let client_result = + self.select_client_for_role_and_method(required_role, "getFeeForMessage"); + let client = match client_result { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return client.get_fee_for_message(encoded_message, config).await; + } + + /// Executes `simulateTransaction` through one endpoint selected for the requested role. + pub async fn simulate_transaction_for_role( + &self, + required_role: &str, + encoded_transaction: &str, + config: &crate::execution_rpc::SimulateTransactionConfig, + ) -> kb_core::Result { + let client_result = + self.select_client_for_role_and_method(required_role, "simulateTransaction"); + let client = match client_result { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return client.simulate_transaction(encoded_transaction, config).await; + } + + /// Executes `getBalance` through one endpoint selected for the requested role. + pub async fn get_balance_for_role( + &self, + required_role: &str, + pubkey: &kb_model::Pubkey, + config: &crate::execution_rpc::GetBalanceConfig, + ) -> kb_core::Result { + let client_result = self.select_client_for_role_and_method(required_role, "getBalance"); + let client = match client_result { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return client.get_balance(pubkey, config).await; + } + + /// Executes `getAccountInfo` through one endpoint selected for the requested role. + pub async fn get_account_info_for_role( + &self, + required_role: &str, + pubkey: &kb_model::Pubkey, + config: &crate::execution_rpc::GetAccountInfoConfig, + ) -> kb_core::Result { + let client_result = self.select_client_for_role_and_method(required_role, "getAccountInfo"); + let client = match client_result { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return client.get_account_info(pubkey, config).await; + } + + /// Executes `getMinimumBalanceForRentExemption` through one selected endpoint. + pub async fn get_minimum_balance_for_rent_exemption_for_role( + &self, + required_role: &str, + data_length: u64, + config: &crate::execution_rpc::GetMinimumBalanceForRentExemptionConfig, + ) -> kb_core::Result { + let client_result = self + .select_client_for_role_and_method(required_role, "getMinimumBalanceForRentExemption"); + let client = match client_result { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return client.get_minimum_balance_for_rent_exemption(data_length, config).await; + } + + /// Executes `requestAirdrop` through one endpoint selected for the requested role. + pub async fn request_airdrop_for_role( + &self, + required_role: &str, + pubkey: &kb_model::Pubkey, + lamports: u64, + config: &crate::execution_rpc::RequestAirdropConfig, + ) -> kb_core::Result { + let client_result = self.select_client_for_role_and_method(required_role, "requestAirdrop"); + let client = match client_result { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return client.request_airdrop(pubkey, lamports, config).await; + } + + /// Executes `sendTransaction` through one endpoint selected for the requested role. + pub async fn send_transaction_for_role( + &self, + required_role: &str, + encoded_transaction: &str, + expected_signature: &kb_model::Signature, + config: &crate::execution_rpc::SendTransactionConfig, + ) -> kb_core::Result { + let client_result = + self.select_client_for_role_and_method(required_role, "sendTransaction"); + let client = match client_result { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return client.send_transaction(encoded_transaction, expected_signature, config).await; + } + + /// Executes `getSignatureStatuses` through one endpoint selected for the requested role. + pub async fn get_signature_statuses_for_role( + &self, + required_role: &str, + signatures: &[kb_model::Signature], + config: &crate::execution_rpc::GetSignatureStatusesConfig, + ) -> kb_core::Result { + let client_result = + self.select_client_for_role_and_method(required_role, "getSignatureStatuses"); + let client = match client_result { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return client.get_signature_statuses(signatures, config).await; + } + + /// Executes `getBlockHeight` through one endpoint selected for the requested role. + pub async fn get_block_height_for_role( + &self, + required_role: &str, + config: &crate::execution_rpc::GetBlockHeightConfig, + ) -> kb_core::Result { + let client_result = self.select_client_for_role_and_method(required_role, "getBlockHeight"); + let client = match client_result { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return client.get_block_height(config).await; + } + + /// Polls one signature until the requested commitment, failure, expiration or timeout. + pub async fn confirm_transaction_for_roles( + &self, + status_role: &str, + block_height_role: &str, + cluster: kb_execution_api::ExecutionCluster, + signature: &kb_model::Signature, + config: &crate::execution_rpc::ConfirmTransactionConfig, + ) -> kb_core::Result { + let status_config = crate::execution_rpc::GetSignatureStatusesConfig::new( + config.search_transaction_history, + ); + let block_height_config = crate::execution_rpc::GetBlockHeightConfig::new( + config.commitment, + config.min_context_slot, + ); + let mut attempts = 0_u32; + let mut last_slot = std::option::Option::None; + let mut last_observed_block_height = std::option::Option::None; + while attempts < config.max_attempts { + attempts = attempts.saturating_add(1); + let statuses_result = self + .get_signature_statuses_for_role( + status_role, + std::slice::from_ref(signature), + &status_config, + ) + .await; + let statuses = match statuses_result { + std::result::Result::Ok(statuses) => statuses, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if statuses.statuses.len() != 1 { + return std::result::Result::Err(kb_core::Error::new( + "execution_confirmation_status_count_mismatch", + format!( + "getSignatureStatuses returned {} entries for one requested signature", + statuses.statuses.len() + ), + )); + } + let status = match statuses.statuses.first() { + std::option::Option::Some(status) => status.as_ref(), + std::option::Option::None => std::option::Option::None, + }; + if let std::option::Option::Some(status) = status { + last_slot = std::option::Option::Some(status.slot); + if let std::option::Option::Some(error) = &status.error { + return std::result::Result::Ok(crate::execution_rpc::confirmation_result( + cluster, + signature, + kb_execution_api::ExecutionConfirmationStatus::Failed, + last_slot, + attempts, + last_observed_block_height, + std::option::Option::Some(error.to_string()), + )); + } + let observed_commitment = match status.confirmation_status { + std::option::Option::Some(commitment) => commitment, + std::option::Option::None => { + crate::execution_rpc::RpcCommitmentLevel::Processed + }, + }; + if crate::execution_rpc::commitment_reached(observed_commitment, config.commitment) + { + let execution_status = + crate::execution_rpc::execution_confirmation_status(observed_commitment); + tracing::info!( + target: crate::TRACING_TARGET, + action = "confirm_transaction", + signature = %signature.0, + attempts, + slot = status.slot, + confirmation_status = observed_commitment.as_str(), + "transaction reached requested commitment" + ); + return std::result::Result::Ok(crate::execution_rpc::confirmation_result( + cluster, + signature, + execution_status, + last_slot, + attempts, + last_observed_block_height, + std::option::Option::None, + )); + } + } + if let std::option::Option::Some(last_valid_block_height) = + config.last_valid_block_height + { + let height_result = + self.get_block_height_for_role(block_height_role, &block_height_config).await; + let height = match height_result { + std::result::Result::Ok(height) => height, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + last_observed_block_height = std::option::Option::Some(height.block_height); + if height.block_height > last_valid_block_height { + return std::result::Result::Ok(crate::execution_rpc::confirmation_result( + cluster, + signature, + kb_execution_api::ExecutionConfirmationStatus::Expired, + last_slot, + attempts, + last_observed_block_height, + std::option::Option::Some(format!( + "recent blockhash expired at block height {last_valid_block_height}; observed {observed}", + observed = height.block_height + )), + )); + } + } + if attempts < config.max_attempts { + tokio::time::sleep(std::time::Duration::from_millis(config.poll_interval_ms)).await; + } + } + return std::result::Result::Ok(crate::execution_rpc::confirmation_result( + cluster, + signature, + kb_execution_api::ExecutionConfirmationStatus::TimedOut, + last_slot, + attempts, + last_observed_block_height, + std::option::Option::Some(format!( + "transaction did not reach '{}' commitment after {} attempts", + config.commitment.as_str(), + attempts + )), + )); + } +} + +fn validate_base64_payload(value: &str, field_name: &str) -> kb_core::Result<()> { + if value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config(format!( + "{field_name} must not be empty" + ))); + } + if value.len() > crate::constants::MAX_EXECUTION_RPC_BASE64_LENGTH { + return std::result::Result::Err(kb_core::Error::config(format!( + "{field_name} exceeds the local encoded length limit of {} bytes", + crate::constants::MAX_EXECUTION_RPC_BASE64_LENGTH + ))); + } + let decode_result = base64::engine::general_purpose::STANDARD.decode(value); + let decoded = match decode_result { + std::result::Result::Ok(decoded) => decoded, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::config(format!( + "{field_name} is not valid base64: {error}" + ))); + }, + }; + if decoded.is_empty() { + return std::result::Result::Err(kb_core::Error::config(format!( + "{field_name} must decode to at least one byte" + ))); + } + return std::result::Result::Ok(()); +} + +#[derive(Clone, Debug, serde::Deserialize)] +struct RpcContextValue { + context: crate::execution_rpc::RpcResponseContext, + value: T, +} + +#[derive(Clone, Debug, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +struct RpcLatestBlockhashValue { + blockhash: std::string::String, + last_valid_block_height: u64, +} + +#[derive(Clone, Debug, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +struct RpcSimulationValue { + #[serde(default, rename = "err")] + error: std::option::Option, + #[serde(default)] + logs: std::option::Option>, + #[serde(default)] + units_consumed: std::option::Option, + #[serde(default)] + fee: std::option::Option, + #[serde(default)] + loaded_accounts_data_size: std::option::Option, + #[serde(default)] + replacement_blockhash: + std::option::Option, + #[serde(default)] + return_data: std::option::Option, + #[serde(default)] + inner_instructions: std::option::Option, + #[serde(default)] + accounts: std::option::Option>>, +} + +fn adapt_signature_result( + source: &serde_json::Value, + field_name: &str, +) -> kb_core::Result { + let signature = match source.as_str() { + std::option::Option::Some(signature) => signature.to_string(), + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::json(format!( + "{field_name} must be a base58 signature string" + ))); + }, + }; + let validation_result = + crate::validate_transaction_signature_text(signature.as_str(), field_name); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(kb_model::Signature(signature)); +} + +fn commitment_rank(commitment: crate::execution_rpc::RpcCommitmentLevel) -> u8 { + return match commitment { + crate::execution_rpc::RpcCommitmentLevel::Processed => 0, + crate::execution_rpc::RpcCommitmentLevel::Confirmed => 1, + crate::execution_rpc::RpcCommitmentLevel::Finalized => 2, + }; +} + +fn commitment_reached( + observed: crate::execution_rpc::RpcCommitmentLevel, + required: crate::execution_rpc::RpcCommitmentLevel, +) -> bool { + return crate::execution_rpc::commitment_rank(observed) + >= crate::execution_rpc::commitment_rank(required); +} + +fn execution_confirmation_status( + commitment: crate::execution_rpc::RpcCommitmentLevel, +) -> kb_execution_api::ExecutionConfirmationStatus { + return match commitment { + crate::execution_rpc::RpcCommitmentLevel::Processed => { + kb_execution_api::ExecutionConfirmationStatus::Processed + }, + crate::execution_rpc::RpcCommitmentLevel::Confirmed => { + kb_execution_api::ExecutionConfirmationStatus::Confirmed + }, + crate::execution_rpc::RpcCommitmentLevel::Finalized => { + kb_execution_api::ExecutionConfirmationStatus::Finalized + }, + }; +} + +fn confirmation_result( + cluster: kb_execution_api::ExecutionCluster, + signature: &kb_model::Signature, + status: kb_execution_api::ExecutionConfirmationStatus, + slot: std::option::Option, + attempts: u32, + last_observed_block_height: std::option::Option, + error: std::option::Option, +) -> kb_execution_api::ExecutionConfirmationResult { + return kb_execution_api::ExecutionConfirmationResult { + cluster, + signature: signature.clone(), + status, + slot, + attempts, + last_observed_block_height, + error, + }; +} + +#[derive(Clone, Debug, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +struct RpcSignatureStatusValue { + slot: u64, + #[serde(default)] + confirmations: std::option::Option, + #[serde(default, rename = "err")] + error: std::option::Option, + #[serde(default)] + confirmation_status: std::option::Option, +} + +#[cfg(test)] +mod tests { + use base64::Engine; // rust-rules: trait-import + + fn valid_hash(byte: u8) -> std::string::String { + return bs58::encode([byte; 32]).into_string(); + } + + fn encoded_bytes(bytes: &[u8]) -> std::string::String { + return base64::engine::general_purpose::STANDARD.encode(bytes); + } + + #[test] + fn genesis_hash_classifies_official_public_clusters() { + let devnet = crate::adapt_get_genesis_hash_result(&serde_json::Value::String( + crate::constants::DEVNET_GENESIS_HASH.to_string(), + )) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!( + devnet.classified_cluster, + std::option::Option::Some(kb_execution_api::ExecutionCluster::Devnet) + ); + let mainnet = crate::adapt_get_genesis_hash_result(&serde_json::Value::String( + crate::constants::MAINNET_GENESIS_HASH.to_string(), + )) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!( + mainnet.classified_cluster, + std::option::Option::Some(kb_execution_api::ExecutionCluster::Mainnet) + ); + let local = crate::adapt_get_genesis_hash_result(&serde_json::Value::String(valid_hash(7))) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(local.classified_cluster, std::option::Option::None); + } + + #[test] + fn latest_blockhash_request_and_result_are_typed() { + let config = crate::GetLatestBlockhashConfig::new( + crate::RpcCommitmentLevel::Processed, + std::option::Option::Some(41), + ); + assert_eq!( + config.request_params(), + std::vec![serde_json::json!({ + "commitment": "processed", + "minContextSlot": 41 + })] + ); + let blockhash = valid_hash(9); + let result = crate::adapt_get_latest_blockhash_result(&serde_json::json!({ + "context": {"slot": 42, "apiVersion": "4.1.1"}, + "value": {"blockhash": blockhash, "lastValidBlockHeight": 99} + })) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(result.context.slot, 42); + assert_eq!(result.last_valid_block_height, 99); + } + + #[test] + fn fee_request_rejects_invalid_base64_and_accepts_null_fee() { + let config = crate::GetFeeForMessageConfig::confirmed(); + assert!(config.request_params("not base64").is_err()); + let result = crate::adapt_get_fee_for_message_result(&serde_json::json!({ + "context": {"slot": 51}, + "value": null + })) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(result.fee_lamports, std::option::Option::None); + } + + #[test] + fn simulation_configuration_builds_unsigned_request() { + let exact = crate::SimulateTransactionConfig::unsigned_exact(); + assert!(!exact.signature_verification); + assert!(!exact.replace_recent_blockhash); + let account = kb_model::Pubkey(valid_hash(11)); + let accounts = crate::SimulationAccountsConfig::new(std::vec![account]) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let config = crate::SimulateTransactionConfig::new( + crate::RpcCommitmentLevel::Confirmed, + false, + true, + std::option::Option::Some(60), + true, + std::option::Option::Some(accounts), + ) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + let encoded = encoded_bytes(&[1, 2, 3]); + let params = config + .request_params(encoded.as_str()) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(params[0], serde_json::Value::String(encoded)); + assert_eq!(params[1]["encoding"], "base64"); + assert_eq!(params[1]["sigVerify"], false); + assert_eq!(params[1]["replaceRecentBlockhash"], true); + assert_eq!(params[1]["innerInstructions"], true); + assert_eq!(params[1]["minContextSlot"], 60); + assert_eq!(params[1]["accounts"]["encoding"], "base64"); + } + + #[test] + fn simulation_configuration_rejects_signature_verification_with_replacement() { + let result = crate::SimulateTransactionConfig::new( + crate::RpcCommitmentLevel::Confirmed, + true, + true, + std::option::Option::None, + false, + std::option::Option::None, + ); + assert!(result.is_err()); + } + + #[test] + fn simulation_result_preserves_runtime_diagnostics() { + let replacement = valid_hash(13); + let result = crate::adapt_simulate_transaction_result(&serde_json::json!({ + "context": {"slot": 70, "apiVersion": "4.1.1"}, + "value": { + "err": null, + "logs": ["Program 111 invoke [1]", "Program 111 success"], + "unitsConsumed": 1714, + "fee": 5000, + "loadedAccountsDataSize": 413, + "replacementBlockhash": { + "blockhash": replacement, + "lastValidBlockHeight": 100 + }, + "returnData": null, + "innerInstructions": null, + "accounts": null + } + })) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert!(result.success); + assert_eq!(result.units_consumed, std::option::Option::Some(1714)); + assert_eq!(result.fee_lamports, std::option::Option::Some(5000)); + assert_eq!(result.logs.len(), 2); + } + + #[test] + fn simulation_result_converts_to_common_execution_contract() { + let rpc = crate::SimulateTransactionResult { + context: crate::RpcResponseContext { + slot: 90, + api_version: std::option::Option::None, + }, + success: false, + error: std::option::Option::Some( + serde_json::json!({"InstructionError": [0, "Custom"]}), + ), + logs: std::vec!["failed".to_string()], + units_consumed: std::option::Option::Some(12), + fee_lamports: std::option::Option::None, + loaded_accounts_data_size: std::option::Option::None, + replacement_blockhash: std::option::Option::Some( + crate::SimulationReplacementBlockhash { + blockhash: valid_hash(14), + last_valid_block_height: 101, + }, + ), + return_data: std::option::Option::None, + inner_instructions: std::option::Option::None, + accounts: std::option::Option::None, + }; + let fee = crate::FeeForMessageResult { + context: crate::RpcResponseContext { + slot: 90, + api_version: std::option::Option::None, + }, + fee_lamports: std::option::Option::Some(5000), + }; + let execution = rpc.to_execution_result( + kb_execution_api::ExecutionCluster::Devnet, + kb_execution_api::ExecutionBlockhashKind::Latest, + std::option::Option::Some(3), + std::option::Option::None, + std::option::Option::None, + std::option::Option::Some(&fee), + ); + assert!(execution.simulated); + assert!(!execution.success); + assert_eq!(execution.estimated_fee_lamports, std::option::Option::Some(5000)); + assert_eq!(execution.replacement_last_valid_block_height, std::option::Option::Some(101)); + assert!(execution.replacement_blockhash.is_some()); + assert!(execution.error.is_some()); + } + + #[test] + fn adapters_reject_invalid_hash_lengths() { + let invalid = bs58::encode([1_u8; 31]).into_string(); + let result = crate::adapt_get_latest_blockhash_result(&serde_json::json!({ + "context": {"slot": 1}, + "value": {"blockhash": invalid, "lastValidBlockHeight": 2} + })); + assert!(result.is_err()); + } + fn valid_signature(byte: u8) -> std::string::String { + return bs58::encode([byte; 64]).into_string(); + } + + fn execution_config() -> kb_config::ExecutionConfig { + return kb_config::ExecutionConfig { + dry_run_default: true, + require_simulation: true, + require_operator_confirmation: true, + localnet_max_spend_lamports: 1_000_000, + devnet_max_spend_lamports: 1_000_000, + testnet_max_spend_lamports: 0, + mainnet_max_spend_lamports: 0, + max_fee_lamports: 10_000, + max_compute_unit_price_micro_lamports: 5, + recent_blockhash_max_age_slots: 150, + send_max_retries: 7, + confirmation_poll_interval_ms: 250, + confirmation_max_attempts: 80, + devnet_airdrop_max_lamports: 2_000_000_000, + }; + } + + #[test] + fn epoch_info_request_and_result_are_typed() { + let config = crate::GetEpochInfoConfig::new( + crate::RpcCommitmentLevel::Confirmed, + std::option::Option::Some(44), + ); + let params = config.request_params(); + assert_eq!(params[0]["commitment"], "confirmed"); + assert_eq!(params[0]["minContextSlot"], 44); + let epoch = crate::adapt_get_epoch_info_result(&serde_json::json!({ + "absoluteSlot": 500, + "blockHeight": 480, + "epoch": 9, + "slotIndex": 20, + "slotsInEpoch": 100, + "transactionCount": null + })) + .unwrap_or_else(|error| panic!("unexpected epoch error: {error}")); + assert_eq!(epoch.absolute_slot, 500); + assert_eq!(epoch.epoch, 9); + assert_eq!(epoch.transaction_count, std::option::Option::None); + assert!( + crate::adapt_get_epoch_info_result(&serde_json::json!({ + "absoluteSlot": "bad" + })) + .is_err() + ); + } + + #[test] + fn balance_and_airdrop_requests_are_typed() { + let pubkey = kb_model::Pubkey(valid_hash(20)); + let balance_config = crate::GetBalanceConfig::confirmed(); + let balance_params = balance_config + .request_params(&pubkey) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(balance_params[0], pubkey.0); + assert_eq!(balance_params[1]["commitment"], "confirmed"); + let balance = crate::adapt_get_balance_result(&serde_json::json!({ + "context": {"slot": 101}, + "value": 9000 + })) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(balance.lamports, 9000); + let airdrop_config = crate::RequestAirdropConfig::confirmed(); + let airdrop_params = airdrop_config + .request_params(&pubkey, 1_000_000) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(airdrop_params[1], 1_000_000); + let signature = valid_signature(21); + let airdrop = crate::adapt_request_airdrop_result(&serde_json::json!(signature)) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(airdrop.signature.0, signature); + assert!(airdrop_config.request_params(&pubkey, 0).is_err()); + } + + #[test] + fn account_existence_and_rent_requests_are_typed() { + let pubkey = kb_model::Pubkey(valid_hash(22)); + let account_config = crate::GetAccountInfoConfig::confirmed(); + let account_params = account_config + .request_params(&pubkey) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(account_params[0], pubkey.0); + assert_eq!(account_params[1]["encoding"], "base64"); + assert_eq!(account_params[1]["dataSlice"]["length"], 0); + let missing = crate::adapt_get_account_info_result(&serde_json::json!({ + "context": {"slot": 102}, + "value": null + })) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert!(missing.account.is_none()); + let owner = valid_hash(23); + let existing = crate::adapt_get_account_info_result(&serde_json::json!({ + "context": {"slot": 103}, + "value": { + "lamports": 890880, + "owner": owner, + "executable": false, + "rentEpoch": 0, + "space": 0, + "data": ["", "base64"] + } + })) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!( + existing.account.as_ref().map(|account| return account.lamports), + std::option::Option::Some(890880) + ); + assert_eq!( + existing.account.as_ref().map(|account| return account.space), + std::option::Option::Some(0) + ); + assert!( + existing + .account + .as_ref() + .map(|account| return account.data.is_empty()) + .unwrap_or(false) + ); + let complete_config = crate::GetAccountInfoConfig::confirmed_with_data(80) + .unwrap_or_else(|error| panic!("unexpected complete-data config error: {error}")); + let complete_params = complete_config + .request_params(&pubkey) + .unwrap_or_else(|error| panic!("unexpected complete-data request error: {error}")); + assert!(complete_params[1].get("dataSlice").is_none()); + let complete_bytes = vec![7_u8; 80]; + let complete = crate::execution_rpc::adapt_get_account_info_result_with_data_limit( + &serde_json::json!({ + "context": {"slot": 104}, + "value": { + "lamports": 1_500_000, + "owner": owner, + "executable": false, + "rentEpoch": 0, + "space": 80, + "data": [encoded_bytes(complete_bytes.as_slice()), "base64"] + } + }), + std::option::Option::Some(80), + ) + .unwrap_or_else(|error| panic!("unexpected complete-data adapter error: {error}")); + assert_eq!( + complete.account.as_ref().map(|account| return account.data.clone()), + std::option::Option::Some(complete_bytes) + ); + assert!(crate::GetAccountInfoConfig::confirmed_with_data(0).is_err()); + assert!( + crate::GetAccountInfoConfig::confirmed_with_data( + crate::constants::MAX_EXECUTION_ACCOUNT_DATA_BYTES + 1, + ) + .is_err() + ); + assert!( + crate::execution_rpc::adapt_get_account_info_result_with_data_limit( + &serde_json::json!({ + "context": {"slot": 105}, + "value": { + "lamports": 1, + "owner": valid_hash(24), + "executable": false, + "rentEpoch": 0, + "space": 81, + "data": [encoded_bytes(&[0_u8; 81]), "base64"] + } + }), + std::option::Option::Some(80), + ) + .is_err() + ); + let rent_config = crate::GetMinimumBalanceForRentExemptionConfig::confirmed(); + assert_eq!(rent_config.request_params(0)[0], 0); + let rent = crate::adapt_get_minimum_balance_for_rent_exemption_result( + &serde_json::json!(890880), + 0, + ) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(rent.minimum_balance_lamports, 890880); + } + + #[test] + fn execution_settings_build_submission_and_confirmation_policies() { + let execution = execution_config(); + let send = crate::SendTransactionConfig::from_execution_config( + &execution, + std::option::Option::Some(44), + ) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert!(!send.skip_preflight); + assert_eq!(send.max_retries, std::option::Option::Some(7)); + assert_eq!(send.min_context_slot, std::option::Option::Some(44)); + let confirmation = crate::ConfirmTransactionConfig::from_execution_config( + &execution, + std::option::Option::Some(900), + std::option::Option::Some(44), + ) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(confirmation.poll_interval_ms, 250); + assert_eq!(confirmation.max_attempts, 80); + assert_eq!(confirmation.last_valid_block_height, std::option::Option::Some(900)); + assert_eq!(confirmation.min_context_slot, std::option::Option::Some(44)); + } + + #[test] + fn send_transaction_requires_preflight_and_matching_signature() { + let invalid = crate::SendTransactionConfig::new( + true, + crate::RpcCommitmentLevel::Confirmed, + std::option::Option::Some(3), + std::option::Option::None, + ); + assert!(invalid.is_err()); + let config = crate::SendTransactionConfig::confirmed(); + let encoded = encoded_bytes(&[4, 5, 6]); + let params = config + .request_params(encoded.as_str()) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(params[1]["encoding"], "base64"); + assert_eq!(params[1]["skipPreflight"], false); + assert_eq!(params[1]["preflightCommitment"], "confirmed"); + assert_eq!(params[1]["maxRetries"], 3); + let expected = kb_model::Signature(valid_signature(22)); + let accepted = + crate::adapt_send_transaction_result(&serde_json::json!(expected.0.clone()), &expected) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(accepted.signature, expected); + let different = serde_json::json!(valid_signature(23)); + assert!(crate::adapt_send_transaction_result(&different, &expected).is_err()); + } + + #[test] + fn signature_statuses_preserve_order_and_runtime_errors() { + let signature = kb_model::Signature(valid_signature(24)); + let config = crate::GetSignatureStatusesConfig::new(true); + let params = config + .request_params(std::slice::from_ref(&signature)) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(params[0][0], signature.0); + assert_eq!(params[1]["searchTransactionHistory"], true); + let statuses = crate::adapt_get_signature_statuses_result(&serde_json::json!({ + "context": {"slot": 120}, + "value": [ + { + "slot": 118, + "confirmations": 2, + "err": null, + "status": {"Ok": null}, + "confirmationStatus": "confirmed" + }, + null, + { + "slot": 119, + "confirmations": null, + "err": {"InstructionError": [0, "Custom"]}, + "confirmationStatus": "finalized" + } + ] + })) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(statuses.statuses.len(), 3); + let confirmed = statuses.statuses[0] + .as_ref() + .unwrap_or_else(|| panic!("missing confirmed status")); + assert_eq!( + confirmed.confirmation_status, + std::option::Option::Some(crate::RpcCommitmentLevel::Confirmed) + ); + assert!(statuses.statuses[1].is_none()); + let failed = + statuses.statuses[2].as_ref().unwrap_or_else(|| panic!("missing failed status")); + assert!(failed.error.is_some()); + } + + #[test] + fn signature_status_request_is_bounded_to_official_limit() { + let config = crate::GetSignatureStatusesConfig::new(false); + assert!(config.request_params(&[]).is_err()); + let signatures = (0..=crate::constants::MAX_SIGNATURE_STATUS_COUNT) + .map(|index| { + return kb_model::Signature(valid_signature((index % 251) as u8)); + }) + .collect::>(); + assert!(config.request_params(signatures.as_slice()).is_err()); + } + + #[test] + fn confirmation_policy_and_commitment_order_are_bounded() { + assert!(crate::ConfirmTransactionConfig::confirmed(100, 0, 10).is_err()); + assert!(crate::ConfirmTransactionConfig::confirmed(100, 500, 0).is_err()); + let config = crate::ConfirmTransactionConfig::confirmed(100, 500, 20) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(config.last_valid_block_height, std::option::Option::Some(100)); + assert!(crate::execution_rpc::commitment_reached( + crate::RpcCommitmentLevel::Finalized, + crate::RpcCommitmentLevel::Confirmed, + )); + assert!(!crate::execution_rpc::commitment_reached( + crate::RpcCommitmentLevel::Processed, + crate::RpcCommitmentLevel::Confirmed, + )); + let height = crate::adapt_get_block_height_result(&serde_json::json!(121)) + .unwrap_or_else(|error| panic!("unexpected error: {error}")); + assert_eq!(height.block_height, 121); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/get_signatures_for_address.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/get_signatures_for_address.rs new file mode 100644 index 0000000..0060d22 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/get_signatures_for_address.rs @@ -0,0 +1,236 @@ +// file: kb_rpc/src/get_signatures_for_address.rs +// version: 1 + +//! Standard Solana `getSignaturesForAddress` request and response contracts. + +/// One transaction signature summary returned by `getSignaturesForAddress`. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AddressSignatureInfo { + /// Transaction signature. + pub signature: std::string::String, + /// Slot containing the transaction. + pub slot: u64, + /// Optional transaction error returned by the RPC node. + pub err: std::option::Option, + /// Optional memo associated with the transaction. + pub memo: std::option::Option, + /// Optional block time as a Unix timestamp. + pub block_time: std::option::Option, + /// Optional confirmation status reported by the RPC node. + pub confirmation_status: std::option::Option, +} + +/// Configuration for one standard Solana `getSignaturesForAddress` request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetSignaturesForAddressConfig { + /// Requested commitment, restricted to `confirmed` or `finalized`. + pub commitment: std::string::String, + /// Maximum number of signatures requested from the node. + pub limit: u16, + /// Optional exclusive cursor toward older signatures. + pub before: std::option::Option, + /// Optional exclusive lower boundary toward newer signatures. + pub until: std::option::Option, + /// Optional minimum context slot. + pub min_context_slot: std::option::Option, +} + +impl GetSignaturesForAddressConfig { + /// Creates and validates one signature history request configuration. + pub fn new( + commitment: impl std::convert::Into, + limit: u16, + before: std::option::Option, + until: std::option::Option, + min_context_slot: std::option::Option, + ) -> kb_core::Result { + let commitment_value = commitment.into(); + if commitment_value != "confirmed" && commitment_value != "finalized" { + return std::result::Result::Err(kb_core::Error::config( + "getSignaturesForAddress commitment must be 'confirmed' or 'finalized'", + )); + } + if limit == 0 || limit > 1000 { + return std::result::Result::Err(kb_core::Error::config( + "getSignaturesForAddress limit must be between 1 and 1000", + )); + } + let before_result = crate::get_signatures_for_address::validate_optional_signature( + before.as_ref(), + "getSignaturesForAddress before signature", + ); + if let std::result::Result::Err(error) = before_result { + return std::result::Result::Err(error); + } + let until_result = crate::get_signatures_for_address::validate_optional_signature( + until.as_ref(), + "getSignaturesForAddress until signature", + ); + if let std::result::Result::Err(error) = until_result { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(Self { + commitment: commitment_value, + limit, + before, + until, + min_context_slot, + }); + } + + fn request_params(&self, address: &str) -> kb_core::Result> { + let address_result = + crate::validate_solana_pubkey_text(address, "getSignaturesForAddress address"); + if let std::result::Result::Err(error) = address_result { + return std::result::Result::Err(error); + } + let mut config = serde_json::Map::::new(); + config.insert("commitment".to_string(), serde_json::Value::String(self.commitment.clone())); + config.insert( + "limit".to_string(), + serde_json::Value::Number(serde_json::Number::from(self.limit)), + ); + if let std::option::Option::Some(before) = &self.before { + config.insert("before".to_string(), serde_json::Value::String(before.clone())); + } + if let std::option::Option::Some(until) = &self.until { + config.insert("until".to_string(), serde_json::Value::String(until.clone())); + } + if let std::option::Option::Some(min_context_slot) = self.min_context_slot { + config.insert( + "minContextSlot".to_string(), + serde_json::Value::Number(serde_json::Number::from(min_context_slot)), + ); + } + return std::result::Result::Ok(std::vec![ + serde_json::Value::String(address.to_string()), + serde_json::Value::Object(config), + ]); + } +} + +impl crate::HttpClient { + /// Fetches one page of signatures associated with an account or program address. + pub async fn get_signatures_for_address( + &self, + address: &str, + config: &crate::GetSignaturesForAddressConfig, + ) -> kb_core::Result> { + let params_result = config.request_params(address); + let params = match params_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let result = self + .execute_json_rpc_result_raw("getSignaturesForAddress".to_string(), params) + .await; + let value = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return crate::adapt_get_signatures_for_address_result(&value); + } +} + +/// Adapts a raw standard Solana `getSignaturesForAddress` result value. +pub fn adapt_get_signatures_for_address_result( + source: &serde_json::Value, +) -> kb_core::Result> { + let parse_result = + serde_json::from_value::>(source.clone()); + let parsed = match parse_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot parse getSignaturesForAddress result: {error}" + ))); + }, + }; + for item in &parsed { + let validation_result = crate::validate_transaction_signature_text( + item.signature.as_str(), + "getSignaturesForAddress result signature", + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + } + return std::result::Result::Ok(parsed); +} + +fn validate_optional_signature( + value: std::option::Option<&std::string::String>, + field_name: &str, +) -> kb_core::Result<()> { + if let std::option::Option::Some(signature) = value { + return crate::validate_transaction_signature_text(signature.as_str(), field_name); + } + return std::result::Result::Ok(()); +} + +#[cfg(test)] +mod tests { + fn signature(byte: u8) -> std::string::String { + return bs58::encode([byte; 64]).into_string(); + } + + fn pubkey(byte: u8) -> std::string::String { + return bs58::encode([byte; 32]).into_string(); + } + + #[test] + fn config_supports_before_and_until_for_bounded_pagination() { + let config_result = crate::GetSignaturesForAddressConfig::new( + "confirmed", + 1000, + std::option::Option::Some(signature(1)), + std::option::Option::Some(signature(2)), + std::option::Option::Some(44), + ); + let config = match config_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected config error: {error}"), + }; + let params_result = config.request_params(pubkey(3).as_str()); + let params = match params_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected params error: {error}"), + }; + assert_eq!(params.len(), 2); + assert_eq!(params[1]["limit"], serde_json::json!(1000)); + assert_eq!(params[1]["minContextSlot"], serde_json::json!(44)); + } + + #[test] + fn config_rejects_limit_above_rpc_maximum() { + let result = crate::GetSignaturesForAddressConfig::new( + "confirmed", + 1001, + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + ); + assert!(result.is_err()); + } + + #[test] + fn result_adapter_preserves_signature_metadata() { + let source = serde_json::json!([{ + "signature": signature(4), + "slot": 123, + "err": null, + "memo": "hello", + "blockTime": 456, + "confirmationStatus": "finalized" + }]); + let result = crate::adapt_get_signatures_for_address_result(&source); + let values = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected adapter error: {error}"), + }; + assert_eq!(values.len(), 1); + assert_eq!(values[0].slot, 123); + assert_eq!(values[0].memo.as_deref(), std::option::Option::Some("hello")); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/get_transaction.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/get_transaction.rs new file mode 100644 index 0000000..482ce30 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/get_transaction.rs @@ -0,0 +1,736 @@ +// file: kb_rpc/src/get_transaction.rs +// version: 7 + +//! Standard Solana `getTransaction` adapter for the canonical transaction contract. + +use base64::Engine; // rust-rules: trait-import + +/// Common adapter contract for transport-specific transaction payloads. +pub trait CanonicalTransactionAdapter { + /// Source payload type accepted by the adapter. + type Source; + + /// Adapts one source payload into an optional canonical transaction. + fn adapt( + &self, + source: &Self::Source, + ) -> kb_core::Result>; +} + +/// Configuration for a standard Solana `getTransaction` request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetTransactionConfig { + /// Requested commitment, restricted to `confirmed` or `finalized`. + pub commitment: std::string::String, + /// Maximum supported numbered transaction version. + pub max_supported_transaction_version: u8, +} + +impl GetTransactionConfig { + /// Creates and validates a `getTransaction` request configuration. + pub fn new( + commitment: impl std::convert::Into, + max_supported_transaction_version: u8, + ) -> kb_core::Result { + let commitment_value = commitment.into(); + if commitment_value != "confirmed" && commitment_value != "finalized" { + return std::result::Result::Err(kb_core::Error::config( + "getTransaction commitment must be 'confirmed' or 'finalized'", + )); + } + return std::result::Result::Ok(Self { + commitment: commitment_value, + max_supported_transaction_version, + }); + } + + /// Creates the default confirmed configuration supporting transaction version zero. + pub fn confirmed_v0() -> Self { + return Self { + commitment: "confirmed".to_string(), + max_supported_transaction_version: 0, + }; + } + + fn request_params(&self, signature: &str) -> kb_core::Result> { + let signature_result = + crate::validate_transaction_signature_text(signature, "getTransaction signature"); + if let std::result::Result::Err(error) = signature_result { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(std::vec![ + serde_json::Value::String(signature.to_string()), + serde_json::json!({ + "commitment": self.commitment.clone(), + "encoding": "json", + "maxSupportedTransactionVersion": self.max_supported_transaction_version + }), + ]); + } +} + +/// Source payload and canonical result returned by one `getTransaction` request. +#[derive(Clone, Debug, PartialEq)] +pub struct GetTransactionAcquisition { + /// Source-specific JSON result retained only for immediate metrics and hashing. + pub source_json: serde_json::Value, + /// Canonical source-independent transaction when the RPC result is not null. + pub canonical_transaction: std::option::Option, +} + +/// Adapter from the standard Solana `getTransaction` JSON result to the canonical model. +#[derive(Clone, Copy, Debug, Default)] +pub struct GetTransactionAdapter; + +impl crate::CanonicalTransactionAdapter for crate::GetTransactionAdapter { + type Source = serde_json::Value; + + fn adapt( + &self, + source: &Self::Source, + ) -> kb_core::Result> { + return crate::get_transaction::adapt_get_transaction_result(source); + } +} + +impl crate::HttpClient { + /// Fetches one source payload and its canonical transaction adaptation. + pub async fn get_transaction_acquisition( + &self, + signature: &str, + config: &crate::GetTransactionConfig, + ) -> kb_core::Result { + let params_result = config.request_params(signature); + let params = match params_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let result = self.execute_json_rpc_result_raw("getTransaction".to_string(), params).await; + let value = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let adapter = crate::GetTransactionAdapter; + let canonical_result = crate::CanonicalTransactionAdapter::adapt(&adapter, &value); + let canonical_transaction = match canonical_result { + std::result::Result::Ok(canonical) => canonical, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(crate::GetTransactionAcquisition { + source_json: value, + canonical_transaction, + }); + } + + /// Fetches and adapts one standard Solana transaction into the canonical model. + pub async fn get_transaction_canonical( + &self, + signature: &str, + config: &crate::GetTransactionConfig, + ) -> kb_core::Result> { + let acquisition_result = self.get_transaction_acquisition(signature, config).await; + let acquisition = match acquisition_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(acquisition.canonical_transaction); + } +} + +/// Adapts a raw standard Solana `getTransaction` result value. +pub fn adapt_get_transaction_result( + source: &serde_json::Value, +) -> kb_core::Result> { + if source.is_null() { + return std::result::Result::Ok(std::option::Option::None); + } + let parse_result = serde_json::from_value::(source.clone()); + let parsed = match parse_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot parse getTransaction result: {error}" + ))); + }, + }; + let canonical_result = crate::get_transaction::adapt_parsed_transaction(parsed); + return match canonical_result { + std::result::Result::Ok(transaction) => { + std::result::Result::Ok(std::option::Option::Some(transaction)) + }, + std::result::Result::Err(error) => std::result::Result::Err(error), + }; +} + +#[derive(Clone, Debug, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +struct RpcGetTransactionResult { + slot: u64, + #[serde(default)] + block_time: std::option::Option, + #[serde(default)] + meta: std::option::Option, + transaction: RpcJsonTransaction, + #[serde(default)] + version: std::option::Option, +} + +#[derive(Clone, Debug, serde::Deserialize)] +#[serde(untagged)] +enum RpcTransactionVersion { + Legacy(std::string::String), + Number(u8), +} + +#[derive(Clone, Debug, serde::Deserialize)] +struct RpcJsonTransaction { + signatures: std::vec::Vec, + message: RpcRawMessage, +} + +#[derive(Clone, Debug, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +struct RpcRawMessage { + header: RpcMessageHeader, + account_keys: std::vec::Vec, + recent_blockhash: std::string::String, + instructions: std::vec::Vec, + #[serde(default)] + address_table_lookups: std::vec::Vec, +} + +#[derive(Clone, Copy, Debug, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +struct RpcMessageHeader { + num_required_signatures: u8, + num_readonly_signed_accounts: u8, + num_readonly_unsigned_accounts: u8, +} + +#[derive(Clone, Debug, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +struct RpcCompiledInstruction { + program_id_index: u16, + accounts: std::vec::Vec, + data: std::string::String, + #[serde(default)] + stack_height: std::option::Option, +} + +#[derive(Clone, Debug, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +struct RpcAddressTableLookup { + account_key: std::string::String, + writable_indexes: std::vec::Vec, + readonly_indexes: std::vec::Vec, +} + +#[derive(Clone, Debug, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +struct RpcTransactionMeta { + #[serde(default)] + err: std::option::Option, + fee: u64, + pre_balances: std::vec::Vec, + post_balances: std::vec::Vec, + #[serde(default)] + inner_instructions: std::option::Option>, + #[serde(default)] + log_messages: std::option::Option>, + #[serde(default)] + pre_token_balances: std::option::Option>, + #[serde(default)] + post_token_balances: std::option::Option>, + #[serde(default)] + rewards: std::option::Option>, + #[serde(default)] + loaded_addresses: std::option::Option, + #[serde(default)] + return_data: std::option::Option, + #[serde(default)] + compute_units_consumed: std::option::Option, + #[serde(default)] + cost_units: std::option::Option, +} + +#[derive(Clone, Debug, serde::Deserialize)] +struct RpcInnerInstructionGroup { + index: u16, + instructions: std::vec::Vec, +} + +#[derive(Clone, Debug, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +struct RpcTokenBalance { + account_index: u16, + mint: std::string::String, + #[serde(default)] + owner: std::option::Option, + #[serde(default)] + program_id: std::option::Option, + ui_token_amount: RpcUiTokenAmount, +} + +#[derive(Clone, Debug, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +struct RpcUiTokenAmount { + amount: std::string::String, + decimals: u8, +} + +#[derive(Clone, Debug, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +struct RpcReward { + pubkey: std::string::String, + lamports: i64, + post_balance: u64, + #[serde(default)] + reward_type: std::option::Option, + #[serde(default)] + commission: std::option::Option, +} + +#[derive(Clone, Debug, Default, serde::Deserialize)] +struct RpcLoadedAddresses { + #[serde(default)] + writable: std::vec::Vec, + #[serde(default)] + readonly: std::vec::Vec, +} + +#[derive(Clone, Debug, serde::Deserialize)] +#[serde(rename_all = "camelCase")] +struct RpcReturnData { + program_id: std::string::String, + data: std::vec::Vec, +} + +fn adapt_parsed_transaction( + parsed: RpcGetTransactionResult, +) -> kb_core::Result { + let primary_signature = match parsed.transaction.signatures.first() { + std::option::Option::Some(value) => value.clone(), + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::json( + "getTransaction result contains no signatures", + )); + }, + }; + let version_result = crate::get_transaction::adapt_version(parsed.version); + let version = match version_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let loaded_addresses = match &parsed.meta { + std::option::Option::Some(metadata) => { + crate::get_transaction::adapt_loaded_addresses(metadata.loaded_addresses.clone()) + }, + std::option::Option::None => kb_model::CanonicalLoadedAddresses::default(), + }; + let instructions_result = + crate::get_transaction::adapt_instructions(parsed.transaction.message.instructions); + let instructions = match instructions_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let metadata_result = crate::get_transaction::adapt_metadata(parsed.meta); + let metadata = match metadata_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let transaction = kb_model::CanonicalTransaction { + format_version: kb_model::CANONICAL_TRANSACTION_FORMAT_VERSION, + primary_signature, + slot: parsed.slot, + block_time: parsed.block_time, + version, + signatures: parsed.transaction.signatures, + message: kb_model::CanonicalTransactionMessage { + header: kb_model::CanonicalMessageHeader { + num_required_signatures: parsed.transaction.message.header.num_required_signatures, + num_readonly_signed_accounts: parsed + .transaction + .message + .header + .num_readonly_signed_accounts, + num_readonly_unsigned_accounts: parsed + .transaction + .message + .header + .num_readonly_unsigned_accounts, + }, + static_account_keys: parsed.transaction.message.account_keys, + recent_blockhash: parsed.transaction.message.recent_blockhash, + instructions, + address_table_lookups: parsed + .transaction + .message + .address_table_lookups + .into_iter() + .map(|lookup| { + return kb_model::CanonicalAddressTableLookup { + account_key: lookup.account_key, + writable_indexes: lookup.writable_indexes, + readonly_indexes: lookup.readonly_indexes, + }; + }) + .collect(), + loaded_addresses, + }, + metadata, + }; + let validation_result = transaction.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(transaction); +} + +fn adapt_version( + version: std::option::Option, +) -> kb_core::Result { + return match version { + std::option::Option::Some(RpcTransactionVersion::Legacy(value)) => { + if value != "legacy" { + return std::result::Result::Err(kb_core::Error::json(format!( + "unsupported textual transaction version: {value}" + ))); + } + std::result::Result::Ok(kb_model::CanonicalTransactionVersion::Legacy) + }, + std::option::Option::Some(RpcTransactionVersion::Number(value)) => { + std::result::Result::Ok(kb_model::CanonicalTransactionVersion::Number(value)) + }, + std::option::Option::None => { + std::result::Result::Ok(kb_model::CanonicalTransactionVersion::Legacy) + }, + }; +} + +fn adapt_instructions( + instructions: std::vec::Vec, +) -> kb_core::Result> { + let mut adapted = std::vec::Vec::with_capacity(instructions.len()); + for instruction in instructions { + let instruction_result = crate::get_transaction::adapt_instruction(instruction); + let canonical = match instruction_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + adapted.push(canonical); + } + return std::result::Result::Ok(adapted); +} + +fn adapt_instruction( + instruction: RpcCompiledInstruction, +) -> kb_core::Result { + let decode_result = bs58::decode(instruction.data.as_str()).into_vec(); + let bytes = match decode_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "getTransaction instruction data is not valid base58: {error}" + ))); + }, + }; + let data_base64 = base64::engine::general_purpose::STANDARD.encode(bytes); + return std::result::Result::Ok(kb_model::CanonicalCompiledInstruction { + program_id_index: instruction.program_id_index, + account_indexes: instruction.accounts, + data_base64, + stack_height: instruction.stack_height, + }); +} + +fn adapt_loaded_addresses( + loaded_addresses: std::option::Option, +) -> kb_model::CanonicalLoadedAddresses { + return match loaded_addresses { + std::option::Option::Some(value) => kb_model::CanonicalLoadedAddresses { + writable: value.writable, + readonly: value.readonly, + }, + std::option::Option::None => kb_model::CanonicalLoadedAddresses::default(), + }; +} + +fn adapt_metadata( + metadata: std::option::Option, +) -> kb_core::Result> { + let source = match metadata { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::result::Result::Ok(std::option::Option::None), + }; + let inner_result = crate::get_transaction::adapt_inner_instructions( + source.inner_instructions.unwrap_or_default(), + ); + let inner_instructions = match inner_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let return_result = crate::get_transaction::adapt_return_data(source.return_data); + let return_data = match return_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let pre_token_result = + crate::get_transaction::adapt_token_balances(source.pre_token_balances.unwrap_or_default()); + let pre_token_balances = match pre_token_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let post_token_result = crate::get_transaction::adapt_token_balances( + source.post_token_balances.unwrap_or_default(), + ); + let post_token_balances = match post_token_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let status = if source.err.is_some() { + kb_model::CanonicalTransactionStatus::Failed + } else { + kb_model::CanonicalTransactionStatus::Success + }; + return std::result::Result::Ok(std::option::Option::Some( + kb_model::CanonicalTransactionMetadata { + status, + error: source.err, + fee: source.fee, + pre_balances: source.pre_balances, + post_balances: source.post_balances, + inner_instructions, + log_messages: source.log_messages.unwrap_or_default(), + pre_token_balances, + post_token_balances, + rewards: source + .rewards + .unwrap_or_default() + .into_iter() + .map(|reward| { + return kb_model::CanonicalReward { + pubkey: reward.pubkey, + lamports: reward.lamports, + post_balance: reward.post_balance, + reward_type: reward.reward_type, + commission: reward.commission, + }; + }) + .collect(), + return_data, + compute_units_consumed: source.compute_units_consumed, + cost_units: source.cost_units, + }, + )); +} + +fn adapt_inner_instructions( + groups: std::vec::Vec, +) -> kb_core::Result> { + let mut adapted = std::vec::Vec::with_capacity(groups.len()); + for group in groups { + let instructions_result = crate::get_transaction::adapt_instructions(group.instructions); + let instructions = match instructions_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + adapted.push(kb_model::CanonicalInnerInstructionGroup { + parent_instruction_index: group.index, + instructions, + }); + } + return std::result::Result::Ok(adapted); +} + +fn adapt_token_balances( + balances: std::vec::Vec, +) -> kb_core::Result> { + let mut adapted = std::vec::Vec::with_capacity(balances.len()); + for balance in balances { + let balance_result = kb_model::CanonicalTokenBalance::new( + balance.account_index, + balance.mint, + balance.owner, + balance.program_id, + balance.ui_token_amount.amount, + balance.ui_token_amount.decimals, + ); + let canonical = match balance_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + adapted.push(canonical); + } + return std::result::Result::Ok(adapted); +} + +fn adapt_return_data( + return_data: std::option::Option, +) -> kb_core::Result> { + let source = match return_data { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::result::Result::Ok(std::option::Option::None), + }; + if source.data.len() != 2 { + return std::result::Result::Err(kb_core::Error::json( + "getTransaction return data must contain payload and encoding", + )); + } + if source.data[1] != "base64" { + return std::result::Result::Err(kb_core::Error::json(format!( + "unsupported getTransaction return data encoding: {}", + source.data[1] + ))); + } + return std::result::Result::Ok(std::option::Option::Some(kb_model::CanonicalReturnData { + program_id: source.program_id, + data_base64: source.data[0].clone(), + })); +} + +#[cfg(test)] +mod tests { + fn fixture(name: &str) -> serde_json::Value { + let text = match name { + "legacy_success" => { + include_str!("../tests/fixtures/get_transaction_legacy_success.json") + }, + "legacy_failure" => { + include_str!("../tests/fixtures/get_transaction_legacy_failure.json") + }, + "v0_alt" => { + include_str!("../tests/fixtures/get_transaction_v0_alt_cpi_token_2022.json") + }, + "optional_absent" => { + include_str!("../tests/fixtures/get_transaction_optional_absent.json") + }, + "equivalent_a" => include_str!("../tests/fixtures/get_transaction_equivalent_a.json"), + "equivalent_b" => include_str!("../tests/fixtures/get_transaction_equivalent_b.json"), + _ => panic!("unknown fixture: {name}"), + }; + let parse_result = serde_json::from_str(text); + match parse_result { + std::result::Result::Ok(value) => return value, + std::result::Result::Err(error) => panic!("fixture parse failed: {error}"), + } + } + + fn adapt_fixture(name: &str) -> kb_model::CanonicalTransaction { + let value = fixture(name); + let result = crate::adapt_get_transaction_result(&value); + match result { + std::result::Result::Ok(std::option::Option::Some(transaction)) => return transaction, + std::result::Result::Ok(std::option::Option::None) => { + panic!("fixture unexpectedly adapted to none"); + }, + std::result::Result::Err(error) => panic!("fixture adaptation failed: {error}"), + } + } + + #[test] + fn null_result_adapts_to_none() { + let result = crate::adapt_get_transaction_result(&serde_json::Value::Null); + assert_eq!(result, std::result::Result::Ok(std::option::Option::None)); + } + + #[test] + fn legacy_success_preserves_token_balances_and_return_data() { + let transaction = adapt_fixture("legacy_success"); + assert_eq!(transaction.version, kb_model::CanonicalTransactionVersion::Legacy); + assert_eq!(transaction.block_time, std::option::Option::Some(1_700_000_001)); + let metadata = match transaction.metadata { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("metadata missing"), + }; + assert_eq!(metadata.status, kb_model::CanonicalTransactionStatus::Success); + assert_eq!(transaction.message.instructions[0].data_base64, "AQ=="); + assert_eq!(metadata.post_token_balances.len(), 1); + assert_eq!(metadata.post_token_balances[0].amount, "12345678901234567890"); + assert_eq!(metadata.post_token_balances[0].decimal_amount, "12345678901.234567890"); + assert!(metadata.return_data.is_some()); + assert_eq!(metadata.inner_instructions.len(), 1); + } + + #[test] + fn legacy_failure_preserves_error() { + let transaction = adapt_fixture("legacy_failure"); + let metadata = match transaction.metadata { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("metadata missing"), + }; + assert_eq!(metadata.status, kb_model::CanonicalTransactionStatus::Failed); + assert!(metadata.error.is_some()); + } + + #[test] + fn version_zero_preserves_alt_cpi_and_token_2022() { + let transaction = adapt_fixture("v0_alt"); + assert_eq!(transaction.version, kb_model::CanonicalTransactionVersion::Number(0)); + assert_eq!(transaction.message.address_table_lookups.len(), 1); + assert_eq!(transaction.message.loaded_addresses.writable.len(), 1); + let metadata = match transaction.metadata { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("metadata missing"), + }; + assert_eq!(metadata.inner_instructions.len(), 1); + assert_eq!( + metadata.post_token_balances[0].program_id.as_deref(), + std::option::Option::Some("TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb") + ); + } + + #[test] + fn optional_fields_and_block_time_can_be_absent() { + let transaction = adapt_fixture("optional_absent"); + assert_eq!(transaction.block_time, std::option::Option::None); + let metadata = match transaction.metadata { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("metadata missing"), + }; + assert!(metadata.log_messages.is_empty()); + assert!(metadata.rewards.is_empty()); + assert!(metadata.return_data.is_none()); + } + + #[test] + fn equivalent_provider_shapes_produce_identical_hashes() { + let first = adapt_fixture("equivalent_a"); + let second = adapt_fixture("equivalent_b"); + let first_hash = match first.canonical_json_hash() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("first hash failed: {error}"), + }; + let second_hash = match second.canonical_json_hash() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("second hash failed: {error}"), + }; + assert_eq!(first_hash, second_hash); + } + + #[test] + fn request_config_rejects_public_key_length_signature() { + let config = crate::GetTransactionConfig::confirmed_v0(); + let result = config.request_params("3Bxs4NN8M2Yn4TLb7gR6Xy7n2D1Q8VjQqWcnpX8C6pQw"); + assert!(result.is_err()); + } + + #[test] + fn request_config_rejects_processed_commitment() { + let result = crate::GetTransactionConfig::new("processed", 0); + assert!(result.is_err()); + } + + #[test] + fn request_config_uses_json_encoding_and_version_zero() { + let config = crate::GetTransactionConfig::confirmed_v0(); + let params_result = config.request_params( + "2Ana1pUpv2ZbMVkwF5FXapYeBEjdxDatLn7nvJkhgTSXbs59SyZSx866bXirPgj8QQVB57uxHJBG1YFvkRbFj4T", + ); + let params = match params_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("request params failed: {error}"), + }; + assert_eq!(params[1]["encoding"], serde_json::Value::String("json".to_string())); + assert_eq!(params[1]["maxSupportedTransactionVersion"], serde_json::Value::from(0)); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/http_client.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/http_client.rs new file mode 100644 index 0000000..6e83776 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/http_client.rs @@ -0,0 +1,383 @@ +// file: kb_rpc/src/http_client.rs +// version: 10 + +//! HTTP JSON-RPC client for standard Solana RPC endpoints. + +/// Local HTTP method class used for routing diagnostics. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Serialize)] +pub enum HttpMethodClass { + /// Standard RPC reads and generic methods. + GeneralRpc, + /// Transaction submission methods. + SendTransaction, + /// Resource-intensive read methods. + HeavyRead, +} + +/// Snapshot of one pooled HTTP endpoint. +#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct HttpPoolClientSnapshot { + /// Logical endpoint name. + pub endpoint_name: std::string::String, + /// Provider name. + pub provider: std::string::String, + /// Endpoint URL. + pub endpoint_url: std::string::String, + /// Supported roles. + pub roles: std::vec::Vec, + /// Status string. + pub status: std::string::String, +} + +/// HTTP JSON-RPC client bound to one configured endpoint. +#[derive(Clone, Debug)] +pub struct HttpClient { + endpoint: kb_config::HttpEndpointConfig, + client: reqwest::Client, + next_request_id: std::sync::Arc, +} + +impl crate::HttpClient { + /// Creates a new HTTP client bound to one endpoint. + pub fn new(endpoint: kb_config::HttpEndpointConfig) -> kb_core::Result { + if !endpoint.enabled { + tracing::error!(target: crate::TRACING_TARGET, action = "create_http_client", endpoint_name = %endpoint.name, provider = %endpoint.provider, error_code = "http_endpoint_disabled", "cannot create HTTP client for disabled endpoint"); + return std::result::Result::Err(kb_core::Error::config(format!( + "http endpoint '{}' is disabled", + endpoint.name + ))); + } + let timeout = std::time::Duration::from_millis(endpoint.request_timeout_ms); + let connect_timeout = std::time::Duration::from_millis(endpoint.connect_timeout_ms); + let client_result = reqwest::Client::builder() + .timeout(timeout) + .connect_timeout(connect_timeout) + .pool_max_idle_per_host(endpoint.max_idle_connections_per_host as usize) + .build(); + let client = match client_result { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "create_http_client", endpoint_name = %endpoint.name, provider = %endpoint.provider, error = %error, "HTTP client construction failed"); + return std::result::Result::Err(kb_core::Error::http(format!( + "cannot build http client for endpoint '{}': {error}", + endpoint.name + ))); + }, + }; + tracing::debug!(target: crate::TRACING_TARGET, action = "create_http_client", endpoint_name = %endpoint.name, provider = %endpoint.provider, role_count = endpoint.roles.len(), "HTTP client created"); + return std::result::Result::Ok(Self { + endpoint, + client, + next_request_id: std::sync::Arc::new(std::sync::atomic::AtomicU64::new(1)), + }); + } + + /// Returns the endpoint name. + pub fn endpoint_name(&self) -> &str { + return self.endpoint.name.as_str(); + } + + /// Returns the provider name. + pub fn provider(&self) -> &str { + return self.endpoint.provider.as_str(); + } + + /// Returns the endpoint URL. + pub fn endpoint_url(&self) -> &str { + return self.endpoint.url.as_str(); + } + + /// Returns the endpoint configuration. + pub fn endpoint_config(&self) -> &kb_config::HttpEndpointConfig { + return &self.endpoint; + } + + /// Returns true when this endpoint supports the required role and request kind. + pub fn can_handle(&self, required_role: &str, request_kind: &str) -> bool { + if !self.endpoint.enabled { + return false; + } + for role in &self.endpoint.roles { + if crate::endpoint_role::role_matches(role, required_role, request_kind) { + return true; + } + } + return false; + } + + /// Returns a serializable endpoint snapshot. + pub fn snapshot(&self) -> crate::HttpPoolClientSnapshot { + let mut roles = std::vec::Vec::new(); + for role in &self.endpoint.roles { + roles.push(crate::EndpointRoleSnapshot::from_config(role)); + } + return crate::HttpPoolClientSnapshot { + endpoint_name: self.endpoint.name.clone(), + provider: self.endpoint.provider.clone(), + endpoint_url: self.endpoint.url.clone(), + roles, + status: "active".to_string(), + }; + } + + /// Classifies a Solana HTTP method into a broad local class. + pub fn classify_method(method: &str) -> crate::HttpMethodClass { + let standard = crate::standard_http_method(method); + return match standard { + std::option::Option::Some(specification) => specification.method_class(), + std::option::Option::None => crate::HttpMethodClass::GeneralRpc, + }; + } + + /// Executes one typed standard HTTP request and decodes its method-specific result. + pub async fn execute_standard_request( + &self, + request: &Request, + ) -> kb_core::Result<::Response> + where + Request: crate::StandardHttpRequest, + { + let specification = + match crate::standard_http_method(::METHOD) { + std::option::Option::Some(specification) => specification, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "typed standard request '{}' is absent from the canonical registry", + ::METHOD + ))); + }, + }; + if specification.contract != crate::StandardRpcContract::TypedAdapter { + return std::result::Result::Err(kb_core::Error::invalid_state(format!( + "standard request '{}' is not declared as a typed adapter", + ::METHOD + ))); + } + let params = match request.params() { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let raw = match self + .execute_json_rpc_result_raw( + ::METHOD.to_string(), + params, + ) + .await + { + std::result::Result::Ok(raw) => raw, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return match serde_json::from_value::<::Response>( + raw, + ) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => { + std::result::Result::Err(kb_core::Error::json(format!( + "cannot decode standard JSON-RPC result for '{}': {error}", + ::METHOD + ))) + }, + }; + } + + /// Executes one explicitly registered standard HTTP method and returns its raw result. + pub async fn execute_standard_method_raw( + &self, + method: &crate::StandardHttpMethodSpec, + params: std::vec::Vec, + ) -> kb_core::Result { + return self.execute_json_rpc_result_raw(method.method.to_string(), params).await; + } + + /// Executes one JSON-RPC request and returns the raw result value. + pub async fn execute_json_rpc_result_raw( + &self, + method: std::string::String, + params: std::vec::Vec, + ) -> kb_core::Result { + let request_id = self.next_request_id.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let parameter_count = params.len(); + let method_class = crate::HttpClient::classify_method(method.as_str()); + tracing::debug!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, method_class = ?method_class, parameter_count, "send HTTP JSON-RPC request"); + let request = crate::JsonRpcRequest::new_with_u64_id(request_id, method.clone(), params); + let response_result = + self.client.post(self.endpoint.url.as_str()).json(&request).send().await; + let response = match response_result { + std::result::Result::Ok(response) => response, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, error = %error, "HTTP JSON-RPC transport failed"); + return std::result::Result::Err(kb_core::Error::http(format!( + "http json-rpc request '{}' failed on endpoint '{}': {error}", + method, self.endpoint.name + ))); + }, + }; + let status = response.status(); + let text_result = response.text().await; + let text = match text_result { + std::result::Result::Ok(text) => text, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, http_status = %status, error = %error, "HTTP JSON-RPC response body read failed"); + return std::result::Result::Err(kb_core::Error::http(format!( + "cannot read http json-rpc response '{}' from endpoint '{}': {error}", + method, self.endpoint.name + ))); + }, + }; + if !status.is_success() { + tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, http_status = %status, response_byte_length = text.len(), "HTTP JSON-RPC endpoint returned non-success status"); + return std::result::Result::Err(kb_core::Error::http(format!( + "http json-rpc endpoint '{}' returned status {}: {}", + self.endpoint.name, status, text + ))); + } + let parsed = match crate::parse_json_rpc_text(&text) { + std::result::Result::Ok(parsed) => parsed, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, http_status = %status, response_byte_length = text.len(), error = %error, "HTTP JSON-RPC response parsing failed"); + return std::result::Result::Err(error); + }, + }; + return match parsed { + crate::JsonRpcResponse::Success(success) => { + tracing::debug!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, http_status = %status, response_byte_length = text.len(), outcome = "success", "HTTP JSON-RPC request completed"); + std::result::Result::Ok(success.result) + }, + crate::JsonRpcResponse::Error(error_response) => { + tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, rpc_error_code = error_response.error.code, rpc_error_message = %error_response.error.message, "HTTP JSON-RPC endpoint returned an RPC error"); + std::result::Result::Err(kb_core::Error::http(format!( + "json-rpc error {} from '{}': {}", + error_response.error.code, self.endpoint.name, error_response.error.message + ))) + }, + crate::JsonRpcResponse::Notification(_) => { + tracing::error!(target: crate::TRACING_TARGET, action = "execute_http_json_rpc", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id, method = %method, "HTTP JSON-RPC response was an unexpected notification"); + std::result::Result::Err(kb_core::Error::http( + "http json-rpc response cannot be a notification".to_string(), + )) + }, + }; + } +} + +#[cfg(test)] +mod tests { + fn role_config( + role: &str, + request_kinds: std::vec::Vec, + ) -> kb_config::EndpointRoleConfig { + return kb_config::EndpointRoleConfig { + role: role.to_string(), + enabled: true, + request_kinds, + priority: 1, + requests_per_second: 10, + burst_capacity: 10, + max_concurrent_requests: 4, + max_subscriptions: 16, + pause_after_rate_limit_ms: 1500, + }; + } + + fn endpoint(enabled: bool) -> kb_config::HttpEndpointConfig { + return kb_config::HttpEndpointConfig { + name: "http_a".to_string(), + enabled, + provider: "test".to_string(), + cluster: "devnet".to_string(), + url: "https://example.invalid".to_string(), + connect_timeout_ms: 100, + request_timeout_ms: 100, + max_idle_connections_per_host: 2, + roles: std::vec![ + role_config("http_queries", std::vec!["get_version".to_string()]), + role_config("http_heavy", std::vec!["get_block".to_string()]), + role_config("http_any", std::vec!["*".to_string()]), + ], + }; + } + + #[test] + fn new_rejects_disabled_endpoint() { + let result = crate::HttpClient::new(endpoint(false)); + assert!(result.is_err()); + } + + #[test] + fn can_handle_matches_exact_role_and_kind() { + let client = match crate::HttpClient::new(endpoint(true)) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("client creation failed: {error}"), + }; + assert!(client.can_handle("http_queries", "get_version")); + assert!(!client.can_handle("http_queries", "get_block")); + } + + #[test] + fn can_handle_matches_wildcard_kind() { + let client = match crate::HttpClient::new(endpoint(true)) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("client creation failed: {error}"), + }; + assert!(client.can_handle("http_any", "send_transaction")); + } + + #[test] + fn snapshot_preserves_endpoint_metadata() { + let client = match crate::HttpClient::new(endpoint(true)) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("client creation failed: {error}"), + }; + let snapshot = client.snapshot(); + assert_eq!(snapshot.endpoint_name, "http_a"); + assert_eq!(snapshot.provider, "test"); + assert_eq!(snapshot.endpoint_url, "https://example.invalid"); + assert_eq!(snapshot.roles.len(), 3); + } + + #[test] + fn classify_method_detects_transaction_submission() { + for method in ["requestAirdrop", "sendTransaction"] { + assert_eq!( + crate::HttpClient::classify_method(method), + crate::HttpMethodClass::SendTransaction + ); + } + } + + #[test] + fn classify_method_detects_heavy_reads() { + assert_eq!( + crate::HttpClient::classify_method("getBlock"), + crate::HttpMethodClass::HeavyRead + ); + assert_eq!( + crate::HttpClient::classify_method("getProgramAccounts"), + crate::HttpMethodClass::HeavyRead + ); + assert_eq!( + crate::HttpClient::classify_method("getSignaturesForAddress"), + crate::HttpMethodClass::HeavyRead + ); + assert_eq!( + crate::HttpClient::classify_method("simulateTransaction"), + crate::HttpMethodClass::HeavyRead + ); + } + + #[test] + fn every_standard_method_uses_its_declared_routing_class() { + for method in &crate::STANDARD_HTTP_METHODS { + assert_eq!(crate::HttpClient::classify_method(method.method), method.method_class()); + } + } + + #[test] + fn classify_method_defaults_to_general_rpc() { + assert_eq!( + crate::HttpClient::classify_method("getVersion"), + crate::HttpMethodClass::GeneralRpc + ); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/http_pool.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/http_pool.rs new file mode 100644 index 0000000..3e19bb1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/http_pool.rs @@ -0,0 +1,279 @@ +// file: kb_rpc/src/http_pool.rs +// version: 6 + +//! HTTP endpoint pool and role-based routing. + +/// Pool of HTTP JSON-RPC endpoints. +#[derive(Clone, Debug)] +pub struct HttpEndpointPool { + clients: std::vec::Vec, + next_index: std::sync::Arc, +} + +impl crate::HttpEndpointPool { + /// Builds a pool from the active profile HTTP endpoint list. + pub fn from_profile(profile: &kb_config::ProfileConfig) -> kb_core::Result { + let mut clients = std::vec::Vec::new(); + for endpoint in &profile.solana.http_endpoints { + if !endpoint.enabled { + continue; + } + let client = match crate::HttpClient::new(endpoint.clone()) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + clients.push(client); + } + return crate::HttpEndpointPool::new(clients); + } + + /// Creates a pool from already constructed clients. + pub fn new(clients: std::vec::Vec) -> kb_core::Result { + if clients.is_empty() { + tracing::error!(target: crate::TRACING_TARGET, action = "create_http_pool", error_code = "http_pool_empty", "HTTP endpoint pool has no enabled endpoint"); + return std::result::Result::Err(kb_core::Error::config( + "http endpoint pool requires at least one enabled endpoint".to_string(), + )); + } + tracing::debug!(target: crate::TRACING_TARGET, action = "create_http_pool", endpoint_count = clients.len(), "HTTP endpoint pool created"); + return std::result::Result::Ok(Self { + clients, + next_index: std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0)), + }); + } + + /// Returns a serializable snapshot of every endpoint in the pool. + pub fn snapshot(&self) -> std::vec::Vec { + let mut snapshots = std::vec::Vec::new(); + for client in &self.clients { + snapshots.push(client.snapshot()); + } + return snapshots; + } + + /// Selects one endpoint for the requested role and method. + pub fn select_client_for_role_and_method( + &self, + required_role: &str, + method: &str, + ) -> kb_core::Result { + let request_kind = crate::request_kind_from_method(method); + return self.select_client_for_role_and_kind(required_role, &request_kind); + } + + /// Selects one endpoint for the requested role and request kind. + pub fn select_client_for_role_and_kind( + &self, + required_role: &str, + request_kind: &str, + ) -> kb_core::Result { + if self.clients.is_empty() { + return std::result::Result::Err(kb_core::Error::not_connected( + "http endpoint pool has no clients".to_string(), + )); + } + let start_index = self.next_index.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let client_count = self.clients.len(); + let mut offset = 0_usize; + while offset < client_count { + let index = (start_index + offset) % client_count; + let client = self.clients[index].clone(); + if client.can_handle(required_role, request_kind) { + tracing::debug!(target: crate::TRACING_TARGET, action = "select_http_endpoint", required_role, request_kind, endpoint_name = %client.endpoint_name(), provider = %client.provider(), "selected HTTP endpoint"); + return std::result::Result::Ok(client); + } + offset += 1; + } + tracing::error!(target: crate::TRACING_TARGET, action = "select_http_endpoint", required_role, request_kind, endpoint_count = self.clients.len(), error_code = "http_endpoint_not_found", "no HTTP endpoint supports requested role and kind"); + return std::result::Result::Err(kb_core::Error::config(format!( + "no http endpoint supports role '{}' and request kind '{}'", + required_role, request_kind + ))); + } + + /// Executes one typed standard HTTP request through an endpoint selected by role. + pub async fn execute_standard_request_for_role( + &self, + required_role: &str, + request: &Request, + ) -> kb_core::Result<::Response> + where + Request: crate::StandardHttpRequest, + { + let client = match self.select_client_for_role_and_method( + required_role, + ::METHOD, + ) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return client.execute_standard_request(request).await; + } + + /// Executes one explicitly registered standard HTTP method through the selected endpoint. + pub async fn execute_standard_method_raw_for_role( + &self, + required_role: &str, + method: &crate::StandardHttpMethodSpec, + params: std::vec::Vec, + ) -> kb_core::Result { + let client = match self.select_client_for_role_and_method(required_role, method.method) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return client.execute_standard_method_raw(method, params).await; + } + + /// Executes one JSON-RPC request through the selected endpoint. + pub async fn execute_json_rpc_result_raw_for_role( + &self, + required_role: &str, + method: std::string::String, + params: std::vec::Vec, + ) -> kb_core::Result { + let client = match self.select_client_for_role_and_method(required_role, &method) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return client.execute_json_rpc_result_raw(method, params).await; + } +} + +#[cfg(test)] +mod tests { + fn role_config( + role: &str, + request_kinds: std::vec::Vec, + ) -> kb_config::EndpointRoleConfig { + return kb_config::EndpointRoleConfig { + role: role.to_string(), + enabled: true, + request_kinds, + priority: 1, + requests_per_second: 10, + burst_capacity: 10, + max_concurrent_requests: 4, + max_subscriptions: 16, + pause_after_rate_limit_ms: 1500, + }; + } + + fn endpoint( + name: &str, + role: &str, + request_kinds: std::vec::Vec, + ) -> kb_config::HttpEndpointConfig { + return kb_config::HttpEndpointConfig { + name: name.to_string(), + enabled: true, + provider: "test".to_string(), + cluster: "devnet".to_string(), + url: format!("https://{name}.invalid"), + connect_timeout_ms: 100, + request_timeout_ms: 100, + max_idle_connections_per_host: 2, + roles: std::vec![role_config(role, request_kinds)], + }; + } + + fn client(endpoint: kb_config::HttpEndpointConfig) -> crate::HttpClient { + match crate::HttpClient::new(endpoint) { + std::result::Result::Ok(client) => return client, + std::result::Result::Err(error) => panic!("client creation failed: {error}"), + } + } + + #[test] + fn new_rejects_empty_pool() { + let result = crate::HttpEndpointPool::new(std::vec::Vec::new()); + assert!(result.is_err()); + } + + #[test] + fn snapshot_lists_every_client() { + let pool = match crate::HttpEndpointPool::new(std::vec![ + client(endpoint("a", "http_queries", std::vec!["get_version".to_string()])), + client(endpoint("b", "http_queries", std::vec!["get_version".to_string()])), + ]) { + std::result::Result::Ok(pool) => pool, + std::result::Result::Err(error) => panic!("pool creation failed: {error}"), + }; + let snapshot = pool.snapshot(); + assert_eq!(snapshot.len(), 2); + assert_eq!(snapshot[0].endpoint_name, "a"); + assert_eq!(snapshot[1].endpoint_name, "b"); + } + + #[test] + fn select_client_round_robins_matching_clients() { + let pool = match crate::HttpEndpointPool::new(std::vec![ + client(endpoint("a", "http_queries", std::vec!["get_version".to_string()])), + client(endpoint("b", "http_queries", std::vec!["get_version".to_string()])), + ]) { + std::result::Result::Ok(pool) => pool, + std::result::Result::Err(error) => panic!("pool creation failed: {error}"), + }; + let first = match pool.select_client_for_role_and_method("http_queries", "getVersion") { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("selection failed: {error}"), + }; + let second = match pool.select_client_for_role_and_method("http_queries", "getVersion") { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("selection failed: {error}"), + }; + assert_eq!(first.endpoint_name(), "a"); + assert_eq!(second.endpoint_name(), "b"); + } + + #[test] + fn select_client_skips_unsupported_clients() { + let pool = match crate::HttpEndpointPool::new(std::vec![ + client(endpoint("a", "http_heavy", std::vec!["get_block".to_string()])), + client(endpoint("b", "http_queries", std::vec!["get_version".to_string()])), + ]) { + std::result::Result::Ok(pool) => pool, + std::result::Result::Err(error) => panic!("pool creation failed: {error}"), + }; + let selected = match pool.select_client_for_role_and_method("http_queries", "getVersion") { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("selection failed: {error}"), + }; + assert_eq!(selected.endpoint_name(), "b"); + } + + #[test] + fn standard_method_selection_uses_the_canonical_request_kind() { + let pool = match crate::HttpEndpointPool::new(std::vec![client(endpoint( + "a", + "http_queries", + std::vec!["get_version".to_string()], + ))]) { + std::result::Result::Ok(pool) => pool, + std::result::Result::Err(error) => panic!("pool creation failed: {error}"), + }; + let method = match crate::standard_http_method("getVersion") { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("getVersion specification missing"), + }; + let selected = match pool.select_client_for_role_and_method("http_queries", method.method) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("selection failed: {error}"), + }; + assert_eq!(selected.endpoint_name(), "a"); + } + + #[test] + fn select_client_returns_error_for_missing_role() { + let pool = match crate::HttpEndpointPool::new(std::vec![client(endpoint( + "a", + "http_queries", + std::vec!["get_version".to_string()] + )),]) + { + std::result::Result::Ok(pool) => pool, + std::result::Result::Err(error) => panic!("pool creation failed: {error}"), + }; + let selected = pool.select_client_for_role_and_kind("http_heavy", "get_block"); + assert!(selected.is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/json_rpc.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/json_rpc.rs new file mode 100644 index 0000000..40e0899 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/json_rpc.rs @@ -0,0 +1,315 @@ +// file: kb_rpc/src/json_rpc.rs +// version: 3 + +//! JSON-RPC 2.0 envelopes used by Solana HTTP and WebSocket transports. + +/// Generic JSON-RPC 2.0 request. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct JsonRpcRequest { + /// JSON-RPC version, expected to be `"2.0"`. + pub jsonrpc: std::string::String, + /// Client request identifier. + pub id: serde_json::Value, + /// RPC method name. + pub method: std::string::String, + /// Ordered method parameters. + pub params: std::vec::Vec, +} + +impl crate::JsonRpcRequest { + /// Creates a request with a numeric identifier. + pub fn new_with_u64_id( + id: u64, + method: std::string::String, + params: std::vec::Vec, + ) -> Self { + return Self { + jsonrpc: "2.0".to_string(), + id: serde_json::Value::from(id), + method, + params, + }; + } + + /// Serializes the request into a compact JSON string. + pub fn to_json_string(&self) -> kb_core::Result { + let serialization_result = serde_json::to_string(self); + return match serialization_result { + std::result::Result::Ok(text) => std::result::Result::Ok(text), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::json( + format!("cannot serialize json-rpc request '{}': {error}", self.method), + )), + }; + } +} + +/// JSON-RPC 2.0 success response. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct JsonRpcSuccessResponse { + /// JSON-RPC version, expected to be `"2.0"`. + pub jsonrpc: std::string::String, + /// Result payload. + pub result: serde_json::Value, + /// Request identifier echoed by the server. + pub id: serde_json::Value, +} + +/// JSON-RPC 2.0 error object. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct JsonRpcErrorObject { + /// Numeric JSON-RPC error code. + pub code: i64, + /// Human-readable error message. + pub message: std::string::String, + /// Optional server-provided payload. + pub data: std::option::Option, +} + +/// JSON-RPC 2.0 error response. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct JsonRpcErrorResponse { + /// JSON-RPC version, expected to be `"2.0"`. + pub jsonrpc: std::string::String, + /// Error payload. + pub error: crate::JsonRpcErrorObject, + /// Request identifier echoed by the server. + pub id: serde_json::Value, +} + +/// JSON-RPC 2.0 notification parameters. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct JsonRpcNotificationParams { + /// Method-specific result payload. + pub result: serde_json::Value, + /// Remote subscription identifier. + pub subscription: u64, +} + +/// JSON-RPC 2.0 notification message. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct JsonRpcNotification { + /// JSON-RPC version, expected to be `"2.0"`. + pub jsonrpc: std::string::String, + /// Notification method name. + pub method: std::string::String, + /// Notification payload. + pub params: crate::JsonRpcNotificationParams, +} + +/// Parsed JSON-RPC response or notification. +#[derive(Clone, Debug, PartialEq)] +pub enum JsonRpcResponse { + /// Success response. + Success(crate::JsonRpcSuccessResponse), + /// Error response. + Error(crate::JsonRpcErrorResponse), + /// Notification message. + Notification(crate::JsonRpcNotification), +} + +impl crate::JsonRpcResponse { + /// Returns a stable diagnostic kind name. + pub fn kind_name(&self) -> &'static str { + return match self { + Self::Success(_) => "success", + Self::Error(_) => "error", + Self::Notification(_) => "notification", + }; + } + + /// Converts the parsed response into a JSON value for UI display. + pub fn to_value(&self) -> kb_core::Result { + return match self { + Self::Success(response) => { + let value_result = serde_json::to_value(response); + match value_result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => { + std::result::Result::Err(kb_core::Error::json(error.to_string())) + }, + } + }, + Self::Error(response) => { + let value_result = serde_json::to_value(response); + match value_result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => { + std::result::Result::Err(kb_core::Error::json(error.to_string())) + }, + } + }, + Self::Notification(notification) => { + let value_result = serde_json::to_value(notification); + match value_result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => { + std::result::Result::Err(kb_core::Error::json(error.to_string())) + }, + } + }, + }; + } +} + +/// Parses an incoming JSON-RPC text payload. +pub fn parse_json_rpc_text(text: &str) -> kb_core::Result { + let value = match serde_json::from_str::(text) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot parse json-rpc text: {error}" + ))); + }, + }; + let object = match value.as_object() { + std::option::Option::Some(object) => object, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::json( + "json-rpc payload must be an object".to_string(), + )); + }, + }; + let has_method = object.contains_key("method"); + let has_params = object.contains_key("params"); + let has_result = object.contains_key("result"); + let has_error = object.contains_key("error"); + let has_id = object.contains_key("id"); + if has_method && has_params && !has_id { + let parse_result = serde_json::from_value::(value); + return match parse_result { + std::result::Result::Ok(notification) => { + std::result::Result::Ok(crate::JsonRpcResponse::Notification(notification)) + }, + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::json( + format!("cannot parse json-rpc notification: {error}"), + )), + }; + } + if has_id && has_result && !has_error { + let parse_result = serde_json::from_value::(value); + return match parse_result { + std::result::Result::Ok(response) => { + std::result::Result::Ok(crate::JsonRpcResponse::Success(response)) + }, + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::json( + format!("cannot parse json-rpc success response: {error}"), + )), + }; + } + if has_id && has_error && !has_result { + let parse_result = serde_json::from_value::(value); + return match parse_result { + std::result::Result::Ok(response) => { + std::result::Result::Ok(crate::JsonRpcResponse::Error(response)) + }, + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::json( + format!("cannot parse json-rpc error response: {error}"), + )), + }; + } + return std::result::Result::Err(kb_core::Error::json( + "unsupported json-rpc response shape".to_string(), + )); +} + +#[cfg(test)] +mod tests { + #[test] + fn request_serialization_contains_jsonrpc_version() { + let request = crate::JsonRpcRequest::new_with_u64_id( + 1, + "getVersion".to_string(), + std::vec::Vec::new(), + ); + let text = match request.to_json_string() { + std::result::Result::Ok(text) => text, + std::result::Result::Err(error) => panic!("request serialization failed: {error}"), + }; + assert!(text.contains("\"jsonrpc\":\"2.0\"")); + assert!(text.contains("\"method\":\"getVersion\"")); + } + + #[test] + fn request_serialization_preserves_params() { + let request = crate::JsonRpcRequest::new_with_u64_id( + 9, + "getBalance".to_string(), + std::vec![serde_json::Value::String("account".to_string())], + ); + let text = match request.to_json_string() { + std::result::Result::Ok(text) => text, + std::result::Result::Err(error) => panic!("request serialization failed: {error}"), + }; + assert!(text.contains("\"params\":[\"account\"]")); + } + + #[test] + fn parses_success_response() { + let parsed = match crate::parse_json_rpc_text("{\"jsonrpc\":\"2.0\",\"result\":7,\"id\":1}") + { + std::result::Result::Ok(parsed) => parsed, + std::result::Result::Err(error) => panic!("response parsing failed: {error}"), + }; + assert_eq!(parsed.kind_name(), "success"); + } + + #[test] + fn parses_error_response() { + let parsed = match crate::parse_json_rpc_text( + "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"invalid params\"},\"id\":2}", + ) { + std::result::Result::Ok(parsed) => parsed, + std::result::Result::Err(error) => panic!("error response parsing failed: {error}"), + }; + match parsed { + crate::JsonRpcResponse::Error(response) => { + assert_eq!(response.error.code, -32602); + assert_eq!(response.error.message, "invalid params"); + }, + _ => panic!("expected error response"), + } + } + + #[test] + fn parses_notification_response() { + let text = "{\"jsonrpc\":\"2.0\",\"method\":\"slotNotification\",\"params\":{\"result\":123,\"subscription\":77}}"; + let parsed = match crate::parse_json_rpc_text(text) { + std::result::Result::Ok(parsed) => parsed, + std::result::Result::Err(error) => panic!("notification parsing failed: {error}"), + }; + match parsed { + crate::JsonRpcResponse::Notification(notification) => { + assert_eq!(notification.method, "slotNotification"); + assert_eq!(notification.params.subscription, 77); + }, + _ => panic!("expected notification"), + } + } + + #[test] + fn parses_rejects_non_object_payload() { + let parsed = crate::parse_json_rpc_text("[1,2,3]"); + assert!(parsed.is_err()); + } + + #[test] + fn parses_rejects_unsupported_shape() { + let parsed = crate::parse_json_rpc_text("{\"jsonrpc\":\"2.0\",\"method\":\"x\",\"id\":1}"); + assert!(parsed.is_err()); + } + + #[test] + fn response_to_value_preserves_success_result() { + let parsed = match crate::parse_json_rpc_text( + "{\"jsonrpc\":\"2.0\",\"result\":{\"solana-core\":\"x\"},\"id\":1}", + ) { + std::result::Result::Ok(parsed) => parsed, + std::result::Result::Err(error) => panic!("response parsing failed: {error}"), + }; + let value = match parsed.to_value() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("response value conversion failed: {error}"), + }; + assert_eq!(value["result"]["solana-core"].as_str(), std::option::Option::Some("x")); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/lib.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/lib.rs new file mode 100644 index 0000000..080c2b7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/lib.rs @@ -0,0 +1,462 @@ +// file: kb_rpc/src/lib.rs +// version: 16 + +//! Solana HTTP JSON-RPC and standard WebSocket client layer. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod client; +mod constants; +mod endpoint_role; +mod execution_rpc; +mod get_signatures_for_address; +mod get_transaction; +mod http_client; +mod http_pool; +mod json_rpc; +mod standard_http; +mod standard_http_accounts; +mod standard_http_blocks; +mod standard_http_cluster; +mod standard_http_economics; +mod standard_http_tokens; +mod standard_http_transactions; +mod standard_methods; +mod standard_ws; +mod validation; +mod ws_client; +mod ws_pool; +mod ws_session; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// RPC endpoint configuration. +pub use crate::client::RpcEndpoint; +/// Minimal Solana RPC client abstraction. +pub use crate::client::SolanaRpcClient; +/// Endpoint role snapshot shared by HTTP and WebSocket pools. +pub use crate::endpoint_role::EndpointRoleSnapshot; +/// Converts a JSON-RPC method name into a stable request kind. +pub use crate::endpoint_role::request_kind_from_method; +/// Contextual account information result. +pub use crate::execution_rpc::AccountInfoResult; +/// Bounded account metadata and optional complete decoded data returned by `getAccountInfo`. +pub use crate::execution_rpc::AccountInfoValue; +/// Signature returned by a faucet airdrop request. +pub use crate::execution_rpc::AirdropResult; +/// Lamport balance returned for one account. +pub use crate::execution_rpc::BalanceResult; +/// Current block height returned by the node. +pub use crate::execution_rpc::BlockHeightResult; +/// Bounded transaction confirmation policy. +pub use crate::execution_rpc::ConfirmTransactionConfig; +/// Current epoch and slot progression returned by `getEpochInfo`. +pub use crate::execution_rpc::EpochInfoResult; +/// Fee estimate returned for one serialized message. +pub use crate::execution_rpc::FeeForMessageResult; +/// Genesis hash and known public-cluster classification. +pub use crate::execution_rpc::GenesisHashResult; +/// Configuration for `getAccountInfo`. +pub use crate::execution_rpc::GetAccountInfoConfig; +/// Configuration for `getBalance`. +pub use crate::execution_rpc::GetBalanceConfig; +/// Configuration for `getBlockHeight`. +pub use crate::execution_rpc::GetBlockHeightConfig; +/// Configuration for `getEpochInfo`. +pub use crate::execution_rpc::GetEpochInfoConfig; +/// Configuration for `getFeeForMessage`. +pub use crate::execution_rpc::GetFeeForMessageConfig; +/// Configuration for `getLatestBlockhash`. +pub use crate::execution_rpc::GetLatestBlockhashConfig; +/// Configuration for `getMinimumBalanceForRentExemption`. +pub use crate::execution_rpc::GetMinimumBalanceForRentExemptionConfig; +/// Configuration for `getSignatureStatuses`. +pub use crate::execution_rpc::GetSignatureStatusesConfig; +/// Latest recent blockhash returned by the cluster. +pub use crate::execution_rpc::LatestBlockhashResult; +/// Rent-exempt minimum for one account data length. +pub use crate::execution_rpc::MinimumBalanceForRentExemptionResult; +/// Configuration for `requestAirdrop`. +pub use crate::execution_rpc::RequestAirdropConfig; +/// Commitment level accepted by execution-oriented RPC methods. +pub use crate::execution_rpc::RpcCommitmentLevel; +/// Standard context attached to Solana RPC responses. +pub use crate::execution_rpc::RpcResponseContext; +/// Configuration for `sendTransaction`. +pub use crate::execution_rpc::SendTransactionConfig; +/// Result returned after a node accepts a signed transaction. +pub use crate::execution_rpc::SendTransactionResult; +/// Current status for one submitted signature. +pub use crate::execution_rpc::SignatureStatus; +/// Positional status response for submitted signatures. +pub use crate::execution_rpc::SignatureStatusesResult; +/// Configuration for `simulateTransaction`. +pub use crate::execution_rpc::SimulateTransactionConfig; +/// Typed result returned by `simulateTransaction`. +pub use crate::execution_rpc::SimulateTransactionResult; +/// Optional accounts requested from `simulateTransaction`. +pub use crate::execution_rpc::SimulationAccountsConfig; +/// Replacement blockhash returned by simulation. +pub use crate::execution_rpc::SimulationReplacementBlockhash; +/// Adapts a raw `getAccountInfo` result. +pub use crate::execution_rpc::adapt_get_account_info_result; +/// Adapts a raw `getBalance` result. +pub use crate::execution_rpc::adapt_get_balance_result; +/// Adapts a raw `getBlockHeight` result. +pub use crate::execution_rpc::adapt_get_block_height_result; +/// Adapts a raw `getEpochInfo` result. +pub use crate::execution_rpc::adapt_get_epoch_info_result; +/// Adapts a raw `getFeeForMessage` result. +pub use crate::execution_rpc::adapt_get_fee_for_message_result; +/// Adapts a raw `getGenesisHash` result. +pub use crate::execution_rpc::adapt_get_genesis_hash_result; +/// Adapts a raw `getLatestBlockhash` result. +pub use crate::execution_rpc::adapt_get_latest_blockhash_result; +/// Adapts a raw `getMinimumBalanceForRentExemption` result. +pub use crate::execution_rpc::adapt_get_minimum_balance_for_rent_exemption_result; +/// Adapts a raw `getSignatureStatuses` result. +pub use crate::execution_rpc::adapt_get_signature_statuses_result; +/// Adapts a raw `requestAirdrop` result. +pub use crate::execution_rpc::adapt_request_airdrop_result; +/// Adapts a raw `sendTransaction` result. +pub use crate::execution_rpc::adapt_send_transaction_result; +/// Adapts a raw `simulateTransaction` result. +pub use crate::execution_rpc::adapt_simulate_transaction_result; +/// Classifies a genesis hash as an official public cluster. +pub use crate::execution_rpc::classify_genesis_hash; +/// One signature summary returned by `getSignaturesForAddress`. +pub use crate::get_signatures_for_address::AddressSignatureInfo; +/// Standard Solana `getSignaturesForAddress` request configuration. +pub use crate::get_signatures_for_address::GetSignaturesForAddressConfig; +/// Adapts a standard Solana `getSignaturesForAddress` result value. +pub use crate::get_signatures_for_address::adapt_get_signatures_for_address_result; +/// Common adapter contract for transport-specific transaction payloads. +pub use crate::get_transaction::CanonicalTransactionAdapter; +/// Source payload and canonical result from one `getTransaction` acquisition. +pub use crate::get_transaction::GetTransactionAcquisition; +/// Standard Solana `getTransaction` JSON adapter. +pub use crate::get_transaction::GetTransactionAdapter; +/// Standard Solana `getTransaction` request configuration. +pub use crate::get_transaction::GetTransactionConfig; +/// Adapts a standard Solana `getTransaction` result value. +pub use crate::get_transaction::adapt_get_transaction_result; +/// HTTP JSON-RPC client bound to one endpoint. +pub use crate::http_client::HttpClient; +/// HTTP method class used for routing diagnostics. +pub use crate::http_client::HttpMethodClass; +/// Snapshot of one HTTP pool endpoint. +pub use crate::http_client::HttpPoolClientSnapshot; +/// HTTP endpoint pool with role-based routing. +pub use crate::http_pool::HttpEndpointPool; +/// JSON-RPC 2.0 error object. +pub use crate::json_rpc::JsonRpcErrorObject; +/// JSON-RPC 2.0 error response. +pub use crate::json_rpc::JsonRpcErrorResponse; +/// JSON-RPC 2.0 notification. +pub use crate::json_rpc::JsonRpcNotification; +/// JSON-RPC 2.0 notification parameters. +pub use crate::json_rpc::JsonRpcNotificationParams; +/// JSON-RPC 2.0 request. +pub use crate::json_rpc::JsonRpcRequest; +/// JSON-RPC 2.0 response parsed from HTTP or WebSocket text. +pub use crate::json_rpc::JsonRpcResponse; +/// JSON-RPC 2.0 success response. +pub use crate::json_rpc::JsonRpcSuccessResponse; +/// Parses an incoming JSON-RPC text payload. +pub use crate::json_rpc::parse_json_rpc_text; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::RpcAccountBalance; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::RpcAccountEncoding; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::RpcAccountInfoConfig; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::RpcCommitmentConfig; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::RpcContextConfig; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::RpcDataSlice; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::RpcKeyedAccount; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::RpcMemcmp; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::RpcMemcmpEncodedBytes; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::RpcOptionalContext; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::RpcProgramAccountFilter; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::RpcProgramAccountsConfig; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::RpcResponse; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::RpcTokenAccountBalance; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::RpcTokenAccountsFilter; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::RpcTokenAmount; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::RpcTransactionDetails; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::RpcTransactionEncoding; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::RpcUiAccount; +/// Configurable standard HTTP request trait and shared wire contracts. +pub use crate::standard_http::StandardHttpRequest; +/// Configurable account-oriented standard HTTP requests. +pub use crate::standard_http_accounts::GetLargestAccountsRequest; +/// Configurable account-oriented standard HTTP requests. +pub use crate::standard_http_accounts::GetMultipleAccountsRequest; +/// Configurable account-oriented standard HTTP requests. +pub use crate::standard_http_accounts::GetProgramAccountsRequest; +/// Configurable account-oriented standard HTTP requests. +pub use crate::standard_http_accounts::RpcLargestAccountsConfig; +/// Configurable account-oriented standard HTTP requests. +pub use crate::standard_http_accounts::RpcLargestAccountsFilter; +/// Configurable block and ledger standard HTTP requests. +pub use crate::standard_http_blocks::GetBlockCommitmentRequest; +/// Configurable block and ledger standard HTTP requests. +pub use crate::standard_http_blocks::GetBlockProductionRequest; +/// Configurable block and ledger standard HTTP requests. +pub use crate::standard_http_blocks::GetBlockRequest; +/// Configurable block and ledger standard HTTP requests. +pub use crate::standard_http_blocks::GetBlockTimeRequest; +/// Configurable block and ledger standard HTTP requests. +pub use crate::standard_http_blocks::GetBlocksRequest; +/// Configurable block and ledger standard HTTP requests. +pub use crate::standard_http_blocks::GetBlocksWithLimitRequest; +/// Configurable block and ledger standard HTTP requests. +pub use crate::standard_http_blocks::GetFirstAvailableBlockRequest; +/// Configurable block and ledger standard HTTP requests. +pub use crate::standard_http_blocks::GetRecentPerformanceSamplesRequest; +/// Configurable block and ledger standard HTTP requests. +pub use crate::standard_http_blocks::MinimumLedgerSlotRequest; +/// Configurable block and ledger standard HTTP requests. +pub use crate::standard_http_blocks::RpcBlockCommitment; +/// Configurable block and ledger standard HTTP requests. +pub use crate::standard_http_blocks::RpcBlockConfig; +/// Configurable block and ledger standard HTTP requests. +pub use crate::standard_http_blocks::RpcBlockProduction; +/// Configurable block and ledger standard HTTP requests. +pub use crate::standard_http_blocks::RpcBlockProductionConfig; +/// Configurable block and ledger standard HTTP requests. +pub use crate::standard_http_blocks::RpcBlockProductionConfigRange; +/// Configurable block and ledger standard HTTP requests. +pub use crate::standard_http_blocks::RpcBlockProductionRange; +/// Configurable block and ledger standard HTTP requests. +pub use crate::standard_http_blocks::RpcConfirmedBlock; +/// Configurable block and ledger standard HTTP requests. +pub use crate::standard_http_blocks::RpcPerformanceSample; +/// Configurable block and ledger standard HTTP requests. +pub use crate::standard_http_blocks::RpcReward; +/// Configurable block and ledger standard HTTP requests. +pub use crate::standard_http_blocks::RpcRewardType; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::GetClusterNodesRequest; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::GetEpochScheduleRequest; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::GetHealthRequest; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::GetHighestSnapshotSlotRequest; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::GetIdentityRequest; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::GetLeaderScheduleRequest; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::GetMaxRetransmitSlotRequest; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::GetMaxShredInsertSlotRequest; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::GetSlotLeaderRequest; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::GetSlotLeadersRequest; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::GetSlotRequest; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::GetVersionRequest; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::GetVoteAccountsRequest; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::RpcContactInfo; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::RpcEpochSchedule; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::RpcGetVoteAccountsConfig; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::RpcIdentity; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::RpcLeaderSchedule; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::RpcLeaderScheduleConfig; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::RpcSnapshotSlotInfo; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::RpcVersionInfo; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::RpcVoteAccountInfo; +/// Configurable cluster-oriented standard HTTP requests. +pub use crate::standard_http_cluster::RpcVoteAccountStatus; +/// Configurable economics-oriented standard HTTP requests. +pub use crate::standard_http_economics::GetInflationGovernorRequest; +/// Configurable economics-oriented standard HTTP requests. +pub use crate::standard_http_economics::GetInflationRateRequest; +/// Configurable economics-oriented standard HTTP requests. +pub use crate::standard_http_economics::GetInflationRewardRequest; +/// Configurable economics-oriented standard HTTP requests. +pub use crate::standard_http_economics::GetStakeMinimumDelegationRequest; +/// Configurable economics-oriented standard HTTP requests. +pub use crate::standard_http_economics::GetSupplyRequest; +/// Configurable economics-oriented standard HTTP requests. +pub use crate::standard_http_economics::RpcEpochConfig; +/// Configurable economics-oriented standard HTTP requests. +pub use crate::standard_http_economics::RpcInflationGovernor; +/// Configurable economics-oriented standard HTTP requests. +pub use crate::standard_http_economics::RpcInflationGovernorConfig; +/// Configurable economics-oriented standard HTTP requests. +pub use crate::standard_http_economics::RpcInflationRate; +/// Configurable economics-oriented standard HTTP requests. +pub use crate::standard_http_economics::RpcInflationReward; +/// Configurable economics-oriented standard HTTP requests. +pub use crate::standard_http_economics::RpcSupply; +/// Configurable economics-oriented standard HTTP requests. +pub use crate::standard_http_economics::RpcSupplyConfig; +/// Configurable token-oriented standard HTTP requests. +pub use crate::standard_http_tokens::GetTokenAccountBalanceRequest; +/// Configurable token-oriented standard HTTP requests. +pub use crate::standard_http_tokens::GetTokenAccountsByDelegateRequest; +/// Configurable token-oriented standard HTTP requests. +pub use crate::standard_http_tokens::GetTokenAccountsByOwnerRequest; +/// Configurable token-oriented standard HTTP requests. +pub use crate::standard_http_tokens::GetTokenLargestAccountsRequest; +/// Configurable token-oriented standard HTTP requests. +pub use crate::standard_http_tokens::GetTokenSupplyRequest; +/// Configurable transaction-oriented standard HTTP requests. +pub use crate::standard_http_transactions::GetRecentPrioritizationFeesRequest; +/// Configurable transaction-oriented standard HTTP requests. +pub use crate::standard_http_transactions::GetTransactionCountRequest; +/// Configurable transaction-oriented standard HTTP requests. +pub use crate::standard_http_transactions::IsBlockhashValidRequest; +/// Configurable transaction-oriented standard HTTP requests. +pub use crate::standard_http_transactions::RpcPrioritizationFee; +/// Every standard Solana HTTP JSON-RPC method. +pub use crate::standard_methods::STANDARD_HTTP_METHODS; +/// Every standard Solana WebSocket subscription pair. +pub use crate::standard_methods::STANDARD_WS_SUBSCRIPTIONS; +/// Official category used to group standard Solana HTTP methods. +pub use crate::standard_methods::StandardHttpCategory; +/// One canonical standard Solana HTTP method specification. +pub use crate::standard_methods::StandardHttpMethodSpec; +/// Documentation-level contract exposed for a standard RPC method. +pub use crate::standard_methods::StandardRpcContract; +/// Stability of one standard Solana WebSocket subscription surface. +pub use crate::standard_methods::StandardWsStability; +/// One canonical standard Solana WebSocket subscribe/unsubscribe pair. +pub use crate::standard_methods::StandardWsSubscriptionSpec; +/// Finds one exact standard HTTP method specification. +pub use crate::standard_methods::standard_http_method; +/// Finds one standard WebSocket subscription from its subscribe or unsubscribe method. +pub use crate::standard_methods::standard_ws_subscription; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::AccountSubscribeRequest; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::BlockSubscribeRequest; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::LogsSubscribeRequest; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::ProgramSubscribeRequest; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::RootSubscribeRequest; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::SignatureSubscribeRequest; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::SlotSubscribeRequest; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::SlotsUpdatesSubscribeRequest; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::StandardWsCapabilities; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::StandardWsNotification; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::StandardWsRequest; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::VoteSubscribeRequest; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsAccountNotification; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsAccountSubscribeConfig; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsBlockFilter; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsBlockNotification; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsBlockSubscribeConfig; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsBlockUpdate; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsLogsFilter; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsLogsNotification; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsLogsSubscribeConfig; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsLogsValue; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsProgramNotification; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsProgramSubscribeConfig; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsRootNotification; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsSignatureNotification; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsSignatureSubscribeConfig; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsSignatureValue; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsSlotInfo; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsSlotNotification; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsSlotTransactionStats; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsSlotUpdate; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsSlotsUpdatesNotification; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsVoteNotification; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::WsVoteValue; +/// Typed contracts for all standard Solana WebSocket subscriptions. +pub use crate::standard_ws::adapt_standard_ws_notification; +/// Validates one base58 Solana blockhash or genesis hash. +pub use crate::validation::validate_solana_hash_text; +/// Validates one base58 Solana public key. +pub use crate::validation::validate_solana_pubkey_text; +/// Validates one base58 Solana transaction signature. +pub use crate::validation::validate_transaction_signature_text; +/// Standard Solana WebSocket client bound to one endpoint. +pub use crate::ws_client::WsClient; +/// Snapshot of one WebSocket pool endpoint. +pub use crate::ws_client::WsPoolClientSnapshot; +/// WebSocket endpoint pool with role-based routing. +pub use crate::ws_pool::WsEndpointPool; +/// Persistent WebSocket session contracts. +pub use crate::ws_session::WsReconnectPolicy; +/// Persistent WebSocket session contracts. +pub use crate::ws_session::WsSession; +/// Persistent WebSocket session contracts. +pub use crate::ws_session::WsSessionEvent; +/// Persistent WebSocket session contracts. +pub use crate::ws_session::WsSessionSnapshot; +/// Persistent WebSocket session contracts. +pub use crate::ws_session::WsSessionState; +/// Persistent WebSocket session contracts. +pub use crate::ws_session::WsSubscriptionAck; +/// Persistent WebSocket session contracts. +pub use crate::ws_session::WsSubscriptionSnapshot; +/// Persistent WebSocket session contracts. +pub use crate::ws_session::WsUnsubscribeAck; diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http.rs new file mode 100644 index 0000000..8d6b39a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http.rs @@ -0,0 +1,499 @@ +// file: kb_rpc/src/standard_http.rs +// version: 2 + +//! Shared contracts for configurable standard Solana HTTP JSON-RPC requests. + +use base64::Engine; // rust-rules: trait-import + +/// Typed request contract for one standard Solana HTTP JSON-RPC method. +pub trait StandardHttpRequest { + /// Method-specific response decoded from the JSON-RPC `result` value. + type Response: serde::de::DeserializeOwned; + + /// Exact standard Solana JSON-RPC method name. + const METHOD: &'static str; + + /// Builds the exact positional JSON-RPC parameter array. + fn params(&self) -> kb_core::Result>; +} + +/// Standard contextual Solana RPC response. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct RpcResponse { + /// Slot and optional API version used by the node. + pub context: crate::RpcResponseContext, + /// Method-specific response value. + pub value: T, +} + +/// Response that may be returned directly or wrapped with an RPC context. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(untagged)] +pub enum RpcOptionalContext { + /// Contextual response form. + Context(crate::RpcResponse), + /// Backward-compatible response form without context. + Value(T), +} + +/// Commitment and minimum-context options shared by standard read methods. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcContextConfig { + /// Optional commitment level. Absence delegates the default to the endpoint. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub commitment: std::option::Option, + /// Optional minimum slot at which the request may be evaluated. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub min_context_slot: std::option::Option, +} + +/// Commitment-only options used by methods that do not accept `minContextSlot`. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcCommitmentConfig { + /// Optional commitment level. Absence delegates the default to the endpoint. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub commitment: std::option::Option, +} + +/// Account-data encoding accepted by standard account RPC methods. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum RpcAccountEncoding { + /// Legacy binary account data encoding alias. + #[serde(rename = "binary")] + Binary, + /// Legacy base58 account data encoding. + #[serde(rename = "base58")] + Base58, + /// Base64 account data encoding. + #[serde(rename = "base64")] + Base64, + /// Zstandard-compressed base64 account data encoding. + #[serde(rename = "base64+zstd")] + Base64Zstd, + /// Program-aware parsed JSON account data. + #[serde(rename = "jsonParsed")] + JsonParsed, +} + +/// Transaction encoding accepted by block and transaction RPC methods. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum RpcTransactionEncoding { + /// Legacy binary transaction encoding. + #[serde(rename = "binary")] + Binary, + /// Base58 transaction encoding. + #[serde(rename = "base58")] + Base58, + /// Base64 transaction encoding. + #[serde(rename = "base64")] + Base64, + /// Structured JSON transaction encoding. + #[serde(rename = "json")] + Json, + /// Program-aware parsed JSON transaction encoding. + #[serde(rename = "jsonParsed")] + JsonParsed, +} + +/// Transaction detail level accepted by block-oriented RPC methods. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub enum RpcTransactionDetails { + /// Full transactions and metadata. + Full, + /// Signatures only. + Signatures, + /// No transaction entries. + None, + /// Account lists without full transaction data. + Accounts, +} + +/// Optional byte range requested from account data. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcDataSlice { + /// Byte offset from the start of account data. + pub offset: usize, + /// Number of bytes requested. + pub length: usize, +} + +impl crate::RpcDataSlice { + fn validate(&self) -> kb_core::Result<()> { + if self.offset.checked_add(self.length).is_none() { + return std::result::Result::Err(kb_core::Error::config( + "account data slice offset and length overflow usize", + )); + } + return std::result::Result::Ok(()); + } +} + +/// Configurable account representation shared by account and token methods. +#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcAccountInfoConfig { + /// Optional account-data encoding. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub encoding: std::option::Option, + /// Optional account-data byte slice. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub data_slice: std::option::Option, + /// Optional commitment level. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub commitment: std::option::Option, + /// Optional minimum context slot. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub min_context_slot: std::option::Option, +} + +impl crate::RpcAccountInfoConfig { + /// Validates combinations that the standard account RPC contract cannot represent. + pub fn validate(&self) -> kb_core::Result<()> { + if let std::option::Option::Some(data_slice) = self.data_slice { + let slice_result = data_slice.validate(); + if let std::result::Result::Err(error) = slice_result { + return std::result::Result::Err(error); + } + if self.encoding == std::option::Option::Some(crate::RpcAccountEncoding::JsonParsed) { + return std::result::Result::Err(kb_core::Error::config( + "jsonParsed account encoding cannot be combined with dataSlice", + )); + } + } + return std::result::Result::Ok(()); + } +} + +/// Encoded bytes used by a program-account memcmp filter. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase", tag = "encoding", content = "bytes")] +pub enum RpcMemcmpEncodedBytes { + /// Base58-encoded bytes. + Base58(std::string::String), + /// Base64-encoded bytes. + Base64(std::string::String), + /// Explicit raw byte array. + Bytes(std::vec::Vec), +} + +impl crate::RpcMemcmpEncodedBytes { + fn validate(&self) -> kb_core::Result<()> { + let decoded_length = match self { + Self::Base58(value) => { + if value.len() > crate::constants::MAX_MEMCMP_BASE58_LENGTH { + return std::result::Result::Err(kb_core::Error::config(format!( + "memcmp base58 value must not exceed {} characters", + crate::constants::MAX_MEMCMP_BASE58_LENGTH + ))); + } + let decoded = match bs58::decode(value).into_vec() { + std::result::Result::Ok(decoded) => decoded, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::config(format!( + "memcmp base58 value is invalid: {error}" + ))); + }, + }; + decoded.len() + }, + Self::Base64(value) => { + if value.len() > crate::constants::MAX_MEMCMP_BASE64_LENGTH { + return std::result::Result::Err(kb_core::Error::config(format!( + "memcmp base64 value must not exceed {} characters", + crate::constants::MAX_MEMCMP_BASE64_LENGTH + ))); + } + let decoded = match base64::prelude::BASE64_STANDARD.decode(value) { + std::result::Result::Ok(decoded) => decoded, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::config(format!( + "memcmp base64 value is invalid: {error}" + ))); + }, + }; + decoded.len() + }, + Self::Bytes(value) => value.len(), + }; + if decoded_length > crate::constants::MAX_MEMCMP_DECODED_BYTES { + return std::result::Result::Err(kb_core::Error::config(format!( + "memcmp value must not exceed {} decoded bytes", + crate::constants::MAX_MEMCMP_DECODED_BYTES + ))); + } + return std::result::Result::Ok(()); + } +} + +/// Offset and encoded bytes used by a memcmp account filter. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct RpcMemcmp { + /// Data offset at which the comparison starts. + pub offset: usize, + /// Bytes compared at the requested offset. + #[serde(flatten)] + pub bytes: crate::RpcMemcmpEncodedBytes, +} + +/// One filter accepted by `getProgramAccounts` and `programSubscribe`. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub enum RpcProgramAccountFilter { + /// Exact account data size. + DataSize(u64), + /// Byte comparison at an account-data offset. + Memcmp(crate::RpcMemcmp), + /// Standard SPL Token account state filter. + TokenAccountState, +} + +impl crate::RpcProgramAccountFilter { + fn validate(&self) -> kb_core::Result<()> { + if let Self::Memcmp(memcmp) = self { + return memcmp.bytes.validate(); + } + return std::result::Result::Ok(()); + } +} + +/// Configurable `getProgramAccounts` request options. +#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcProgramAccountsConfig { + /// Optional account filters, evaluated by the node in the provided order. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub filters: std::option::Option>, + /// Optional account-data encoding. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub encoding: std::option::Option, + /// Optional account-data byte slice. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub data_slice: std::option::Option, + /// Optional commitment level. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub commitment: std::option::Option, + /// Optional minimum context slot. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub min_context_slot: std::option::Option, + /// Whether the response must include a context wrapper. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub with_context: std::option::Option, + /// Optional validator-side deterministic result sorting. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub sort_results: std::option::Option, +} + +impl crate::RpcProgramAccountsConfig { + /// Validates account encoding, data slicing and every filter. + pub fn validate(&self) -> kb_core::Result<()> { + let account_config = crate::RpcAccountInfoConfig { + encoding: self.encoding, + data_slice: self.data_slice, + commitment: self.commitment, + min_context_slot: self.min_context_slot, + }; + let account_result = account_config.validate(); + if let std::result::Result::Err(error) = account_result { + return std::result::Result::Err(error); + } + if let std::option::Option::Some(filters) = &self.filters { + for filter in filters { + let filter_result = filter.validate(); + if let std::result::Result::Err(error) = filter_result { + return std::result::Result::Err(error); + } + } + } + return std::result::Result::Ok(()); + } +} + +/// Mint or Token Program selector used by token-account queries. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub enum RpcTokenAccountsFilter { + /// Select token accounts for one mint. + Mint(std::string::String), + /// Select accounts owned by one Token Program generation. + ProgramId(std::string::String), +} + +impl crate::RpcTokenAccountsFilter { + /// Validates the public key embedded in the selected filter. + pub fn validate(&self) -> kb_core::Result<()> { + let value = match self { + Self::Mint(value) | Self::ProgramId(value) => value, + }; + return crate::validate_solana_pubkey_text(value, "token account filter public key"); + } +} + +/// Account representation returned by configurable standard account methods. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcUiAccount { + /// Account lamports. + pub lamports: u64, + /// Owner Program ID. + pub owner: std::string::String, + /// Whether the account is executable. + pub executable: bool, + /// Rent epoch reported by the node. + pub rent_epoch: u64, + /// Account data length when exposed by the node. + #[serde(default)] + pub space: std::option::Option, + /// Encoding-dependent account data payload. + pub data: serde_json::Value, +} + +/// Public key and account pair returned by program and token-account scans. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcKeyedAccount { + /// Account public key. + pub pubkey: std::string::String, + /// Account state and data. + pub account: crate::RpcUiAccount, +} + +/// Token amount with exact integer and decimal string representations. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcTokenAmount { + /// Raw token amount as an unsigned decimal string. + pub amount: std::string::String, + /// Mint decimal precision. + pub decimals: u8, + /// Optional floating representation retained for wire compatibility. + pub ui_amount: std::option::Option, + /// Exact decimal display string. + pub ui_amount_string: std::string::String, +} + +/// Token account address and balance returned by `getTokenLargestAccounts`. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcTokenAccountBalance { + /// Token account public key. + pub address: std::string::String, + /// Raw token amount as an unsigned decimal string. + pub amount: std::string::String, + /// Mint decimal precision. + pub decimals: u8, + /// Optional floating representation retained for wire compatibility. + pub ui_amount: std::option::Option, + /// Exact decimal display string. + pub ui_amount_string: std::string::String, +} + +/// One lamport-ranked account returned by `getLargestAccounts`. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcAccountBalance { + /// Account public key. + pub address: std::string::String, + /// Lamport balance. + pub lamports: u64, +} + +pub(crate) fn serialize_parameter( + method: &str, + value: &T, +) -> kb_core::Result { + return match serde_json::to_value(value) { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::json(format!( + "cannot serialize {method} parameter: {error}" + ))), + }; +} + +pub(crate) fn validate_pubkey_list( + values: &[std::string::String], + field: &str, + maximum: usize, +) -> kb_core::Result<()> { + if values.len() > maximum { + return std::result::Result::Err(kb_core::Error::config(format!( + "{field} must not exceed {maximum} entries" + ))); + } + for value in values { + let validation_result = crate::validate_solana_pubkey_text(value, field); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + } + return std::result::Result::Ok(()); +} + +#[cfg(test)] +mod tests { + #[test] + fn optional_account_options_serialize_only_selected_fields() { + let config = crate::RpcAccountInfoConfig { + encoding: std::option::Option::Some(crate::RpcAccountEncoding::Base64Zstd), + data_slice: std::option::Option::Some(crate::RpcDataSlice { offset: 32, length: 64 }), + commitment: std::option::Option::None, + min_context_slot: std::option::Option::Some(91), + }; + let value = match serde_json::to_value(config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("config serialization failed: {error}"), + }; + assert_eq!( + value, + serde_json::json!({ + "encoding": "base64+zstd", + "dataSlice": { "offset": 32, "length": 64 }, + "minContextSlot": 91 + }) + ); + let binary = match serde_json::to_value(crate::RpcAccountEncoding::Binary) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("binary encoding serialization failed: {error}") + }, + }; + assert_eq!(binary, serde_json::Value::String("binary".to_string())); + } + + #[test] + fn memcmp_filter_preserves_encoding_and_enforces_decoded_bound() { + let filter = crate::RpcProgramAccountFilter::Memcmp(crate::RpcMemcmp { + offset: 8, + bytes: crate::RpcMemcmpEncodedBytes::Bytes(std::vec![1_u8, 2_u8, 3_u8]), + }); + let value = match serde_json::to_value(&filter) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("filter serialization failed: {error}"), + }; + assert_eq!( + value, + serde_json::json!({ + "memcmp": { "offset": 8, "encoding": "bytes", "bytes": [1, 2, 3] } + }) + ); + let oversized = crate::RpcProgramAccountFilter::Memcmp(crate::RpcMemcmp { + offset: 0, + bytes: crate::RpcMemcmpEncodedBytes::Bytes(std::vec![0_u8; 129]), + }); + assert!(oversized.validate().is_err()); + } + + #[test] + fn json_parsed_account_data_rejects_data_slice() { + let config = crate::RpcAccountInfoConfig { + encoding: std::option::Option::Some(crate::RpcAccountEncoding::JsonParsed), + data_slice: std::option::Option::Some(crate::RpcDataSlice { offset: 0, length: 1 }), + commitment: std::option::Option::None, + min_context_slot: std::option::Option::None, + }; + assert!(config.validate().is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_accounts.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_accounts.rs new file mode 100644 index 0000000..556979e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_accounts.rs @@ -0,0 +1,215 @@ +// file: kb_rpc/src/standard_http_accounts.rs +// version: 1 + +//! Configurable standard account-oriented Solana HTTP JSON-RPC requests. + +/// Optional circulating-supply filter for `getLargestAccounts`. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub enum RpcLargestAccountsFilter { + /// Accounts included in circulating supply. + Circulating, + /// Accounts excluded from circulating supply. + NonCirculating, +} + +/// Options accepted by `getLargestAccounts`. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcLargestAccountsConfig { + /// Optional commitment level. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub commitment: std::option::Option, + /// Optional circulating-supply filter. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub filter: std::option::Option, + /// Optional validator-side deterministic sorting flag. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub sort_results: std::option::Option, +} + +/// Typed `getLargestAccounts` request. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct GetLargestAccountsRequest { + /// Optional request configuration. `None` emits no parameter. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetLargestAccountsRequest { + type Response = crate::RpcResponse>; + + const METHOD: &'static str = "getLargestAccounts"; + + fn params(&self) -> kb_core::Result> { + if let std::option::Option::Some(config) = &self.config { + let value = match crate::standard_http::serialize_parameter(Self::METHOD, config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(std::vec![value]); + } + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +/// Typed `getMultipleAccounts` request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetMultipleAccountsRequest { + /// Account public keys in positional response order. + pub addresses: std::vec::Vec, + /// Optional account encoding, slicing and context options. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetMultipleAccountsRequest { + type Response = crate::RpcResponse>>; + + const METHOD: &'static str = "getMultipleAccounts"; + + fn params(&self) -> kb_core::Result> { + let validation_result = crate::standard_http::validate_pubkey_list( + &self.addresses, + "getMultipleAccounts address", + crate::constants::MAX_MULTIPLE_ACCOUNT_COUNT, + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + if let std::option::Option::Some(config) = &self.config { + let config_result = config.validate(); + if let std::result::Result::Err(error) = config_result { + return std::result::Result::Err(error); + } + } + let addresses = + match crate::standard_http::serialize_parameter(Self::METHOD, &self.addresses) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut params = std::vec![addresses]; + if let std::option::Option::Some(config) = &self.config { + let config_value = match crate::standard_http::serialize_parameter(Self::METHOD, config) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + params.push(config_value); + } + return std::result::Result::Ok(params); + } +} + +/// Typed `getProgramAccounts` request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetProgramAccountsRequest { + /// Program ID whose owned accounts are requested. + pub program_id: std::string::String, + /// Optional filters, account representation and contextual response options. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetProgramAccountsRequest { + type Response = crate::RpcOptionalContext>; + + const METHOD: &'static str = "getProgramAccounts"; + + fn params(&self) -> kb_core::Result> { + let validation_result = + crate::validate_solana_pubkey_text(&self.program_id, "getProgramAccounts program id"); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + if let std::option::Option::Some(config) = &self.config { + let config_result = config.validate(); + if let std::result::Result::Err(error) = config_result { + return std::result::Result::Err(error); + } + } + let mut params = std::vec![serde_json::Value::String(self.program_id.clone())]; + if let std::option::Option::Some(config) = &self.config { + let config_value = match crate::standard_http::serialize_parameter(Self::METHOD, config) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + params.push(config_value); + } + return std::result::Result::Ok(params); + } +} + +#[cfg(test)] +mod tests { + fn pubkey(seed: u8) -> std::string::String { + return bs58::encode([seed; 32]).into_string(); + } + + #[test] + fn multiple_accounts_preserves_optional_encoding_slice_and_context() { + let request = crate::GetMultipleAccountsRequest { + addresses: std::vec![pubkey(1), pubkey(2)], + config: std::option::Option::Some(crate::RpcAccountInfoConfig { + encoding: std::option::Option::Some(crate::RpcAccountEncoding::Base64), + data_slice: std::option::Option::Some(crate::RpcDataSlice { offset: 4, length: 8 }), + commitment: std::option::Option::Some(crate::RpcCommitmentLevel::Confirmed), + min_context_slot: std::option::Option::Some(99), + }), + }; + let params = match crate::StandardHttpRequest::params(&request) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("params failed: {error}"), + }; + assert_eq!(params.len(), 2); + assert_eq!(params[0].as_array().map(std::vec::Vec::len), std::option::Option::Some(2)); + assert_eq!(params[1]["encoding"], serde_json::Value::String("base64".to_string())); + assert_eq!(params[1]["minContextSlot"], serde_json::Value::from(99_u64)); + } + + #[test] + fn multiple_accounts_enforces_official_request_bound() { + let request = crate::GetMultipleAccountsRequest { + addresses: (0_u8..101_u8).map(pubkey).collect(), + config: std::option::Option::None, + }; + assert!(crate::StandardHttpRequest::params(&request).is_err()); + + let empty = crate::GetMultipleAccountsRequest { + addresses: std::vec::Vec::new(), + config: std::option::Option::None, + }; + let empty_params = match crate::StandardHttpRequest::params(&empty) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("empty params failed: {error}"), + }; + assert_eq!(empty_params, std::vec![serde_json::json!([])]); + } + + #[test] + fn program_accounts_preserves_filters_and_context_switch() { + let request = crate::GetProgramAccountsRequest { + program_id: pubkey(7), + config: std::option::Option::Some(crate::RpcProgramAccountsConfig { + filters: std::option::Option::Some(std::vec![ + crate::RpcProgramAccountFilter::DataSize(165), + crate::RpcProgramAccountFilter::TokenAccountState, + ]), + encoding: std::option::Option::Some(crate::RpcAccountEncoding::JsonParsed), + data_slice: std::option::Option::None, + commitment: std::option::Option::Some(crate::RpcCommitmentLevel::Finalized), + min_context_slot: std::option::Option::None, + with_context: std::option::Option::Some(true), + sort_results: std::option::Option::Some(true), + }), + }; + let params = match crate::StandardHttpRequest::params(&request) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("params failed: {error}"), + }; + assert_eq!(params[1]["withContext"], serde_json::Value::Bool(true)); + assert_eq!(params[1]["sortResults"], serde_json::Value::Bool(true)); + assert_eq!( + params[1]["filters"][1], + serde_json::Value::String("tokenAccountState".to_string()) + ); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_blocks.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_blocks.rs new file mode 100644 index 0000000..332fdfb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_blocks.rs @@ -0,0 +1,548 @@ +// file: kb_rpc/src/standard_http_blocks.rs +// version: 1 + +//! Configurable standard block and ledger Solana HTTP JSON-RPC requests. + +/// Options accepted by `getBlock`. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcBlockConfig { + /// Optional transaction encoding. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub encoding: std::option::Option, + /// Optional transaction detail level. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub transaction_details: std::option::Option, + /// Whether rewards must be included. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub rewards: std::option::Option, + /// Optional confirmed or finalized commitment. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub commitment: std::option::Option, + /// Highest transaction version the caller can decode. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub max_supported_transaction_version: std::option::Option, +} + +impl crate::RpcBlockConfig { + /// Rejects the processed commitment unsupported by block-history methods. + pub fn validate(&self) -> kb_core::Result<()> { + if self.commitment == std::option::Option::Some(crate::RpcCommitmentLevel::Processed) { + return std::result::Result::Err(kb_core::Error::config( + "getBlock does not support processed commitment", + )); + } + return std::result::Result::Ok(()); + } +} + +/// Inclusive slot range accepted by `getBlockProduction`. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcBlockProductionConfigRange { + /// First slot included in the range. + pub first_slot: u64, + /// Optional final slot included in the range. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub last_slot: std::option::Option, +} + +/// Options accepted by `getBlockProduction`. +#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcBlockProductionConfig { + /// Optional validator identity filter. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub identity: std::option::Option, + /// Optional inclusive slot range. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub range: std::option::Option, + /// Optional commitment level. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub commitment: std::option::Option, +} + +impl crate::RpcBlockProductionConfig { + /// Validates identity and range ordering. + pub fn validate(&self) -> kb_core::Result<()> { + if let std::option::Option::Some(identity) = &self.identity { + let identity_result = + crate::validate_solana_pubkey_text(identity, "getBlockProduction identity"); + if let std::result::Result::Err(error) = identity_result { + return std::result::Result::Err(error); + } + } + if let std::option::Option::Some(range) = self.range { + if let std::option::Option::Some(last_slot) = range.last_slot { + if last_slot < range.first_slot { + return std::result::Result::Err(kb_core::Error::config( + "getBlockProduction last slot must not precede first slot", + )); + } + } + } + return std::result::Result::Ok(()); + } +} + +/// Reward category attached to a block reward entry. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub enum RpcRewardType { + /// Transaction fee reward. + Fee, + /// Rent reward. + Rent, + /// Staking reward. + Staking, + /// Vote reward. + Voting, +} + +/// One reward entry returned with a block. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcReward { + /// Recipient public key. + pub pubkey: std::string::String, + /// Signed lamport balance change. + pub lamports: i64, + /// Recipient balance after the reward. + pub post_balance: u64, + /// Optional reward category. + pub reward_type: std::option::Option, + /// Optional validator commission percentage. + pub commission: std::option::Option, + /// Optional validator commission in basis points. + #[serde(default, skip_serializing_if = "std::option::Option::is_none")] + pub commission_bps: std::option::Option, +} + +/// Encoding-dependent confirmed block returned by `getBlock`. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcConfirmedBlock { + /// Block hash. + pub blockhash: std::string::String, + /// Previous block hash. + pub previous_blockhash: std::string::String, + /// Parent slot. + pub parent_slot: u64, + /// Encoding-dependent transaction entries when requested. + #[serde(default)] + pub transactions: std::option::Option>, + /// Signature list when signature-only details are requested. + #[serde(default)] + pub signatures: std::option::Option>, + /// Rewards when requested. + #[serde(default)] + pub rewards: std::option::Option>, + /// Unix block time when available. + #[serde(default)] + pub block_time: std::option::Option, + /// Block height when available. + #[serde(default)] + pub block_height: std::option::Option, + /// Number of reward partitions when available. + #[serde(default)] + pub num_reward_partitions: std::option::Option, +} + +/// Stake commitment information returned for one block. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcBlockCommitment { + /// Commitment stake by lockout depth, or `None` when unavailable. + pub commitment: std::option::Option>, + /// Total active stake used for the commitment calculation. + pub total_stake: u64, +} + +/// Actual slot range represented by a block-production response. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcBlockProductionRange { + /// First represented slot. + pub first_slot: u64, + /// Last represented slot. + pub last_slot: u64, +} + +/// Block-production counts grouped by validator identity. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcBlockProduction { + /// Validator identity to `(leader slots, blocks produced)` map. + pub by_identity: std::collections::BTreeMap, + /// Actual represented slot range. + pub range: crate::RpcBlockProductionRange, +} + +/// Recent cluster performance sample. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcPerformanceSample { + /// Slot at the end of the sample window. + pub slot: u64, + /// Total transactions processed during the sample. + pub num_transactions: u64, + /// Optional count excluding vote transactions. + #[serde(default)] + pub num_non_vote_transactions: std::option::Option, + /// Slots processed during the sample. + pub num_slots: u64, + /// Sample period in seconds. + pub sample_period_secs: u16, +} + +/// Typed `getBlock` request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetBlockRequest { + /// Block slot. + pub slot: u64, + /// Optional encoding, details, rewards, commitment and version options. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetBlockRequest { + type Response = std::option::Option; + + const METHOD: &'static str = "getBlock"; + + fn params(&self) -> kb_core::Result> { + let mut params = std::vec![serde_json::Value::from(self.slot)]; + if let std::option::Option::Some(config) = &self.config { + let config_result = config.validate(); + if let std::result::Result::Err(error) = config_result { + return std::result::Result::Err(error); + } + let value = match crate::standard_http::serialize_parameter(Self::METHOD, config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + params.push(value); + } + return std::result::Result::Ok(params); + } +} + +/// Typed `getBlockCommitment` request. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct GetBlockCommitmentRequest { + /// Block slot. + pub slot: u64, +} + +impl crate::StandardHttpRequest for crate::GetBlockCommitmentRequest { + type Response = crate::RpcBlockCommitment; + + const METHOD: &'static str = "getBlockCommitment"; + + fn params(&self) -> kb_core::Result> { + return std::result::Result::Ok(std::vec![serde_json::Value::from(self.slot)]); + } +} + +/// Typed `getBlockProduction` request. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct GetBlockProductionRequest { + /// Optional identity, range and commitment options. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetBlockProductionRequest { + type Response = crate::RpcResponse; + + const METHOD: &'static str = "getBlockProduction"; + + fn params(&self) -> kb_core::Result> { + if let std::option::Option::Some(config) = &self.config { + let validation_result = config.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let value = match crate::standard_http::serialize_parameter(Self::METHOD, config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(std::vec![value]); + } + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +/// Typed `getBlocks` request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetBlocksRequest { + /// First slot included in the scan. + pub start_slot: u64, + /// Optional final slot included in the scan. + pub end_slot: std::option::Option, + /// Optional confirmed or finalized context options. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetBlocksRequest { + type Response = std::vec::Vec; + + const METHOD: &'static str = "getBlocks"; + + fn params(&self) -> kb_core::Result> { + if let std::option::Option::Some(end_slot) = self.end_slot { + if end_slot >= self.start_slot + && end_slot.saturating_sub(self.start_slot) > crate::constants::MAX_BLOCK_RANGE + { + return std::result::Result::Err(kb_core::Error::config(format!( + "getBlocks range must not exceed {} slots", + crate::constants::MAX_BLOCK_RANGE + ))); + } + } + if let std::option::Option::Some(config) = &self.config { + if config.commitment == std::option::Option::Some(crate::RpcCommitmentLevel::Processed) + { + return std::result::Result::Err(kb_core::Error::config( + "getBlocks does not support processed commitment", + )); + } + } + let mut params = std::vec![serde_json::Value::from(self.start_slot)]; + if let std::option::Option::Some(end_slot) = self.end_slot { + params.push(serde_json::Value::from(end_slot)); + } + if let std::option::Option::Some(config) = &self.config { + let value = match crate::standard_http::serialize_parameter(Self::METHOD, config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + params.push(value); + } + return std::result::Result::Ok(params); + } +} + +/// Typed `getBlocksWithLimit` request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetBlocksWithLimitRequest { + /// First slot considered by the scan. + pub start_slot: u64, + /// Maximum number of block slots returned. + pub limit: u64, + /// Optional confirmed or finalized context options. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetBlocksWithLimitRequest { + type Response = std::vec::Vec; + + const METHOD: &'static str = "getBlocksWithLimit"; + + fn params(&self) -> kb_core::Result> { + if self.limit > crate::constants::MAX_BLOCK_RANGE { + return std::result::Result::Err(kb_core::Error::config(format!( + "getBlocksWithLimit limit must not exceed {}", + crate::constants::MAX_BLOCK_RANGE + ))); + } + if let std::option::Option::Some(config) = &self.config { + if config.commitment == std::option::Option::Some(crate::RpcCommitmentLevel::Processed) + { + return std::result::Result::Err(kb_core::Error::config( + "getBlocksWithLimit does not support processed commitment", + )); + } + } + let mut params = std::vec![ + serde_json::Value::from(self.start_slot), + serde_json::Value::from(self.limit), + ]; + if let std::option::Option::Some(config) = &self.config { + let value = match crate::standard_http::serialize_parameter(Self::METHOD, config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + params.push(value); + } + return std::result::Result::Ok(params); + } +} + +/// Typed `getBlockTime` request. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct GetBlockTimeRequest { + /// Block slot. + pub slot: u64, +} + +impl crate::StandardHttpRequest for crate::GetBlockTimeRequest { + type Response = std::option::Option; + + const METHOD: &'static str = "getBlockTime"; + + fn params(&self) -> kb_core::Result> { + return std::result::Result::Ok(std::vec![serde_json::Value::from(self.slot)]); + } +} + +/// Typed `getFirstAvailableBlock` request. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct GetFirstAvailableBlockRequest; + +impl crate::StandardHttpRequest for crate::GetFirstAvailableBlockRequest { + type Response = u64; + + const METHOD: &'static str = "getFirstAvailableBlock"; + + fn params(&self) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +/// Typed `getRecentPerformanceSamples` request. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct GetRecentPerformanceSamplesRequest { + /// Optional sample count. Absence delegates the default to the endpoint. + pub limit: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetRecentPerformanceSamplesRequest { + type Response = std::vec::Vec; + + const METHOD: &'static str = "getRecentPerformanceSamples"; + + fn params(&self) -> kb_core::Result> { + if let std::option::Option::Some(limit) = self.limit { + if limit > crate::constants::MAX_PERFORMANCE_SAMPLE_COUNT { + return std::result::Result::Err(kb_core::Error::config(format!( + "getRecentPerformanceSamples limit must not exceed {}", + crate::constants::MAX_PERFORMANCE_SAMPLE_COUNT + ))); + } + return std::result::Result::Ok(std::vec![serde_json::Value::from(limit)]); + } + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +/// Typed `minimumLedgerSlot` request. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct MinimumLedgerSlotRequest; + +impl crate::StandardHttpRequest for crate::MinimumLedgerSlotRequest { + type Response = u64; + + const METHOD: &'static str = "minimumLedgerSlot"; + + fn params(&self) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +#[cfg(test)] +mod tests { + #[test] + fn block_options_remain_independently_selectable() { + let request = crate::GetBlockRequest { + slot: 55, + config: std::option::Option::Some(crate::RpcBlockConfig { + encoding: std::option::Option::Some(crate::RpcTransactionEncoding::JsonParsed), + transaction_details: std::option::Option::Some( + crate::RpcTransactionDetails::Accounts, + ), + rewards: std::option::Option::Some(false), + commitment: std::option::Option::Some(crate::RpcCommitmentLevel::Confirmed), + max_supported_transaction_version: std::option::Option::Some(0), + }), + }; + let params = match crate::StandardHttpRequest::params(&request) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("params failed: {error}"), + }; + assert_eq!(params[1]["encoding"], serde_json::Value::String("jsonParsed".to_string())); + assert_eq!( + params[1]["transactionDetails"], + serde_json::Value::String("accounts".to_string()) + ); + assert_eq!(params[1]["rewards"], serde_json::Value::Bool(false)); + assert_eq!(params[1]["maxSupportedTransactionVersion"], serde_json::Value::from(0_u64)); + + let reward = match serde_json::from_value::(serde_json::json!({ + "pubkey": "validator", + "lamports": 42, + "postBalance": 84, + "rewardType": "voting", + "commission": 5, + "commissionBps": 550 + })) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("reward parsing failed: {error}"), + }; + assert_eq!(reward.commission, std::option::Option::Some(5)); + assert_eq!(reward.commission_bps, std::option::Option::Some(550)); + } + + #[test] + fn blocks_without_end_slot_places_config_in_second_position() { + let request = crate::GetBlocksRequest { + start_slot: 10, + end_slot: std::option::Option::None, + config: std::option::Option::Some(crate::RpcContextConfig { + commitment: std::option::Option::Some(crate::RpcCommitmentLevel::Finalized), + min_context_slot: std::option::Option::Some(9), + }), + }; + let params = match crate::StandardHttpRequest::params(&request) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("params failed: {error}"), + }; + assert_eq!(params.len(), 2); + assert_eq!(params[1]["commitment"], serde_json::Value::String("finalized".to_string())); + } + + #[test] + fn block_ranges_and_performance_samples_are_bounded() { + let range = crate::GetBlocksRequest { + start_slot: 0, + end_slot: std::option::Option::Some(500_001), + config: std::option::Option::None, + }; + let samples = + crate::GetRecentPerformanceSamplesRequest { limit: std::option::Option::Some(721) }; + assert!(crate::StandardHttpRequest::params(&range).is_err()); + assert!(crate::StandardHttpRequest::params(&samples).is_err()); + + let reversed_range = crate::GetBlocksRequest { + start_slot: 10, + end_slot: std::option::Option::Some(9), + config: std::option::Option::None, + }; + let zero_blocks = crate::GetBlocksWithLimitRequest { + start_slot: 10, + limit: 0, + config: std::option::Option::None, + }; + let zero_samples = + crate::GetRecentPerformanceSamplesRequest { limit: std::option::Option::Some(0) }; + let reversed_params = match crate::StandardHttpRequest::params(&reversed_range) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("reversed range failed: {error}"), + }; + let zero_block_params = match crate::StandardHttpRequest::params(&zero_blocks) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("zero block limit failed: {error}"), + }; + let zero_sample_params = match crate::StandardHttpRequest::params(&zero_samples) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("zero sample limit failed: {error}"), + }; + assert_eq!( + reversed_params, + std::vec![serde_json::Value::from(10_u64), serde_json::Value::from(9_u64),] + ); + assert_eq!( + zero_block_params, + std::vec![serde_json::Value::from(10_u64), serde_json::Value::from(0_u64),] + ); + assert_eq!(zero_sample_params, std::vec![serde_json::Value::from(0_u64)]); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_cluster.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_cluster.rs new file mode 100644 index 0000000..7000215 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_cluster.rs @@ -0,0 +1,505 @@ +// file: kb_rpc/src/standard_http_cluster.rs +// version: 1 + +//! Configurable standard cluster-oriented Solana HTTP JSON-RPC requests. + +/// Contact information returned for one cluster node. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcContactInfo { + /// Node identity public key. + pub pubkey: std::string::String, + /// Gossip socket address. + pub gossip: std::option::Option, + /// TVU UDP socket address. + pub tvu: std::option::Option, + /// TPU UDP socket address. + pub tpu: std::option::Option, + /// TPU QUIC socket address. + pub tpu_quic: std::option::Option, + /// TPU forwarding UDP socket address. + pub tpu_forwards: std::option::Option, + /// TPU forwarding QUIC socket address. + pub tpu_forwards_quic: std::option::Option, + /// TPU vote socket address. + pub tpu_vote: std::option::Option, + /// Repair service socket address. + pub serve_repair: std::option::Option, + /// JSON-RPC socket address. + pub rpc: std::option::Option, + /// PubSub socket address. + pub pubsub: std::option::Option, + /// Validator software version. + pub version: std::option::Option, + /// Validator client identifier. + pub client_id: std::option::Option, + /// Feature-set identifier prefix. + pub feature_set: std::option::Option, + /// Shred version. + pub shred_version: std::option::Option, +} + +/// Epoch schedule derived from the cluster genesis configuration. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcEpochSchedule { + /// Slots in a normal epoch. + pub slots_per_epoch: u64, + /// Leader-schedule offset in slots. + pub leader_schedule_slot_offset: u64, + /// Whether warmup epochs are enabled. + pub warmup: bool, + /// First epoch using the normal slot count. + pub first_normal_epoch: u64, + /// First slot of the first normal epoch. + pub first_normal_slot: u64, +} + +/// Highest complete and incremental snapshot slots available from a node. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct RpcSnapshotSlotInfo { + /// Highest full snapshot slot. + pub full: u64, + /// Highest incremental snapshot slot when available. + pub incremental: std::option::Option, +} + +/// Node identity response. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct RpcIdentity { + /// Node identity public key. + pub identity: std::string::String, +} + +/// Options accepted by `getLeaderSchedule`. +#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcLeaderScheduleConfig { + /// Optional validator identity whose schedule is requested. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub identity: std::option::Option, + /// Optional commitment level. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub commitment: std::option::Option, +} + +impl crate::RpcLeaderScheduleConfig { + /// Validates the optional identity public key. + pub fn validate(&self) -> kb_core::Result<()> { + if let std::option::Option::Some(identity) = &self.identity { + return crate::validate_solana_pubkey_text(identity, "getLeaderSchedule identity"); + } + return std::result::Result::Ok(()); + } +} + +/// Leader schedule keyed by validator identity. +pub type RpcLeaderSchedule = std::collections::BTreeMap>; + +/// Validator software version information. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "kebab-case")] +pub struct RpcVersionInfo { + /// Validator software version. + pub solana_core: std::string::String, + /// Feature-set identifier prefix. + pub feature_set: std::option::Option, +} + +/// Options accepted by `getVoteAccounts`. +#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcGetVoteAccountsConfig { + /// Optional vote account public key filter. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub vote_pubkey: std::option::Option, + /// Optional commitment level. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub commitment: std::option::Option, + /// Whether unstaked delinquent validators must be retained. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub keep_unstaked_delinquents: std::option::Option, + /// Optional delinquency threshold in slots. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub delinquent_slot_distance: std::option::Option, +} + +impl crate::RpcGetVoteAccountsConfig { + /// Validates the optional vote account public key. + pub fn validate(&self) -> kb_core::Result<()> { + if let std::option::Option::Some(vote_pubkey) = &self.vote_pubkey { + return crate::validate_solana_pubkey_text(vote_pubkey, "getVoteAccounts vote account"); + } + return std::result::Result::Ok(()); + } +} + +/// Vote account information returned by `getVoteAccounts`. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcVoteAccountInfo { + /// Vote account public key. + pub vote_pubkey: std::string::String, + /// Validator identity public key. + pub node_pubkey: std::string::String, + /// Activated stake in lamports. + pub activated_stake: u64, + /// Vote commission percentage. + pub commission: u8, + /// Vote inflation-reward commission in basis points when exposed by the node. + #[serde(default, skip_serializing_if = "std::option::Option::is_none")] + pub inflation_rewards_commission_bps: std::option::Option, + /// Whether the vote account is staked in the current epoch. + pub epoch_vote_account: bool, + /// `(epoch, credits, previous credits)` history. + pub epoch_credits: std::vec::Vec<(u64, u64, u64)>, + /// Most recent voted slot. + pub last_vote: u64, + /// Current root slot. + pub root_slot: u64, +} + +/// Current and delinquent validator vote accounts. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct RpcVoteAccountStatus { + /// Current validator vote accounts. + pub current: std::vec::Vec, + /// Delinquent validator vote accounts. + pub delinquent: std::vec::Vec, +} + +/// Typed `getClusterNodes` request. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct GetClusterNodesRequest; + +impl crate::StandardHttpRequest for crate::GetClusterNodesRequest { + type Response = std::vec::Vec; + + const METHOD: &'static str = "getClusterNodes"; + + fn params(&self) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +/// Typed `getEpochSchedule` request. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct GetEpochScheduleRequest; + +impl crate::StandardHttpRequest for crate::GetEpochScheduleRequest { + type Response = crate::RpcEpochSchedule; + + const METHOD: &'static str = "getEpochSchedule"; + + fn params(&self) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +/// Typed `getHealth` request. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct GetHealthRequest; + +impl crate::StandardHttpRequest for crate::GetHealthRequest { + type Response = std::string::String; + + const METHOD: &'static str = "getHealth"; + + fn params(&self) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +/// Typed `getHighestSnapshotSlot` request. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct GetHighestSnapshotSlotRequest; + +impl crate::StandardHttpRequest for crate::GetHighestSnapshotSlotRequest { + type Response = crate::RpcSnapshotSlotInfo; + + const METHOD: &'static str = "getHighestSnapshotSlot"; + + fn params(&self) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +/// Typed `getIdentity` request. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct GetIdentityRequest; + +impl crate::StandardHttpRequest for crate::GetIdentityRequest { + type Response = crate::RpcIdentity; + + const METHOD: &'static str = "getIdentity"; + + fn params(&self) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +/// Typed `getLeaderSchedule` request. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct GetLeaderScheduleRequest { + /// Optional slot selecting the epoch whose schedule is requested. + pub slot: std::option::Option, + /// Optional identity and commitment options. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetLeaderScheduleRequest { + type Response = std::option::Option; + + const METHOD: &'static str = "getLeaderSchedule"; + + fn params(&self) -> kb_core::Result> { + if let std::option::Option::Some(config) = &self.config { + let validation_result = config.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + } + let mut params = std::vec::Vec::new(); + if let std::option::Option::Some(slot) = self.slot { + params.push(serde_json::Value::from(slot)); + } else if self.config.is_some() { + params.push(serde_json::Value::Null); + } + if let std::option::Option::Some(config) = &self.config { + let value = match crate::standard_http::serialize_parameter(Self::METHOD, config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + params.push(value); + } + return std::result::Result::Ok(params); + } +} + +/// Typed `getMaxRetransmitSlot` request. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct GetMaxRetransmitSlotRequest; + +impl crate::StandardHttpRequest for crate::GetMaxRetransmitSlotRequest { + type Response = u64; + + const METHOD: &'static str = "getMaxRetransmitSlot"; + + fn params(&self) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +/// Typed `getMaxShredInsertSlot` request. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct GetMaxShredInsertSlotRequest; + +impl crate::StandardHttpRequest for crate::GetMaxShredInsertSlotRequest { + type Response = u64; + + const METHOD: &'static str = "getMaxShredInsertSlot"; + + fn params(&self) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +/// Typed `getSlot` request. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct GetSlotRequest { + /// Optional commitment and minimum-context options. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetSlotRequest { + type Response = u64; + + const METHOD: &'static str = "getSlot"; + + fn params(&self) -> kb_core::Result> { + if let std::option::Option::Some(config) = &self.config { + let value = match crate::standard_http::serialize_parameter(Self::METHOD, config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(std::vec![value]); + } + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +/// Typed `getSlotLeader` request. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct GetSlotLeaderRequest { + /// Optional commitment and minimum-context options. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetSlotLeaderRequest { + type Response = std::string::String; + + const METHOD: &'static str = "getSlotLeader"; + + fn params(&self) -> kb_core::Result> { + if let std::option::Option::Some(config) = &self.config { + let value = match crate::standard_http::serialize_parameter(Self::METHOD, config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(std::vec![value]); + } + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +/// Typed `getSlotLeaders` request. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct GetSlotLeadersRequest { + /// First slot whose leader is requested. + pub start_slot: u64, + /// Number of consecutive leaders requested. + pub limit: u64, +} + +impl crate::StandardHttpRequest for crate::GetSlotLeadersRequest { + type Response = std::vec::Vec; + + const METHOD: &'static str = "getSlotLeaders"; + + fn params(&self) -> kb_core::Result> { + if self.limit == 0 || self.limit > crate::constants::MAX_SLOT_LEADER_COUNT { + return std::result::Result::Err(kb_core::Error::config(format!( + "getSlotLeaders limit must be between 1 and {}", + crate::constants::MAX_SLOT_LEADER_COUNT + ))); + } + return std::result::Result::Ok(std::vec![ + serde_json::Value::from(self.start_slot), + serde_json::Value::from(self.limit), + ]); + } +} + +/// Typed `getVersion` request. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct GetVersionRequest; + +impl crate::StandardHttpRequest for crate::GetVersionRequest { + type Response = crate::RpcVersionInfo; + + const METHOD: &'static str = "getVersion"; + + fn params(&self) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +/// Typed `getVoteAccounts` request. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct GetVoteAccountsRequest { + /// Optional vote-account, commitment and delinquency options. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetVoteAccountsRequest { + type Response = crate::RpcVoteAccountStatus; + + const METHOD: &'static str = "getVoteAccounts"; + + fn params(&self) -> kb_core::Result> { + if let std::option::Option::Some(config) = &self.config { + let validation_result = config.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let value = match crate::standard_http::serialize_parameter(Self::METHOD, config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(std::vec![value]); + } + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +#[cfg(test)] +mod tests { + fn pubkey(seed: u8) -> std::string::String { + return bs58::encode([seed; 32]).into_string(); + } + + #[test] + fn leader_schedule_uses_null_slot_placeholder_when_only_config_is_selected() { + let request = crate::GetLeaderScheduleRequest { + slot: std::option::Option::None, + config: std::option::Option::Some(crate::RpcLeaderScheduleConfig { + identity: std::option::Option::Some(pubkey(1)), + commitment: std::option::Option::Some(crate::RpcCommitmentLevel::Processed), + }), + }; + let params = match crate::StandardHttpRequest::params(&request) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("params failed: {error}"), + }; + assert_eq!(params[0], serde_json::Value::Null); + assert_eq!(params[1]["identity"], serde_json::Value::String(pubkey(1))); + + let maximum = crate::GetSlotLeadersRequest { + start_slot: 1, + limit: crate::constants::MAX_SLOT_LEADER_COUNT, + }; + let too_many = crate::GetSlotLeadersRequest { + start_slot: 1, + limit: crate::constants::MAX_SLOT_LEADER_COUNT + 1, + }; + assert!(crate::StandardHttpRequest::params(&maximum).is_ok()); + assert!(crate::StandardHttpRequest::params(&too_many).is_err()); + } + + #[test] + fn vote_account_options_are_independently_selectable() { + let request = crate::GetVoteAccountsRequest { + config: std::option::Option::Some(crate::RpcGetVoteAccountsConfig { + vote_pubkey: std::option::Option::Some(pubkey(2)), + commitment: std::option::Option::None, + keep_unstaked_delinquents: std::option::Option::Some(true), + delinquent_slot_distance: std::option::Option::Some(512), + }), + }; + let params = match crate::StandardHttpRequest::params(&request) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("params failed: {error}"), + }; + assert!(params[0].get("commitment").is_none()); + assert_eq!(params[0]["keepUnstakedDelinquents"], serde_json::Value::Bool(true)); + assert_eq!(params[0]["delinquentSlotDistance"], serde_json::Value::from(512_u64)); + + let vote_account = + match serde_json::from_value::(serde_json::json!({ + "votePubkey": pubkey(3), + "nodePubkey": pubkey(4), + "activatedStake": 1, + "commission": 5, + "inflationRewardsCommissionBps": 525, + "epochVoteAccount": true, + "epochCredits": [[1, 2, 1]], + "lastVote": 8, + "rootSlot": 7 + })) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("vote account parsing failed: {error}"), + }; + assert_eq!(vote_account.inflation_rewards_commission_bps, std::option::Option::Some(525)); + } + + #[test] + fn version_response_accepts_kebab_case_wire_fields() { + let value = serde_json::json!({ "solana-core": "4.0.0", "feature-set": 123 }); + let parsed = match serde_json::from_value::(value) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("version parsing failed: {error}"), + }; + assert_eq!(parsed.solana_core, "4.0.0"); + assert_eq!(parsed.feature_set, std::option::Option::Some(123)); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_economics.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_economics.rs new file mode 100644 index 0000000..0ab7cc9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_economics.rs @@ -0,0 +1,309 @@ +// file: kb_rpc/src/standard_http_economics.rs +// version: 1 + +//! Configurable standard inflation, supply and stake-economics HTTP requests. + +/// Inflation governor parameters. +#[derive(Clone, Copy, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcInflationGovernor { + /// Initial inflation rate. + pub initial: f64, + /// Terminal inflation rate. + pub terminal: f64, + /// Annual taper rate. + pub taper: f64, + /// Foundation allocation rate. + pub foundation: f64, + /// Foundation allocation term in years. + pub foundation_term: f64, +} + +/// Inflation rates for the current epoch. +#[derive(Clone, Copy, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcInflationRate { + /// Total inflation rate. + pub total: f64, + /// Validator inflation rate. + pub validator: f64, + /// Foundation inflation rate. + pub foundation: f64, + /// Epoch represented by the rates. + pub epoch: u64, +} + +/// Inflation reward credited to one requested address. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcInflationReward { + /// Reward epoch. + pub epoch: u64, + /// First effective slot of the rewarded epoch. + pub effective_slot: u64, + /// Reward amount in lamports. + pub amount: u64, + /// Account balance after the reward. + pub post_balance: u64, + /// Legacy vote commission percentage when applicable. + pub commission: std::option::Option, + /// Vote commission in basis points when exposed by the node. + #[serde(default, skip_serializing_if = "std::option::Option::is_none")] + pub commission_bps: std::option::Option, +} + +/// Current lamport supply breakdown. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcSupply { + /// Total lamport supply. + pub total: u64, + /// Circulating lamport supply. + pub circulating: u64, + /// Non-circulating lamport supply. + pub non_circulating: u64, + /// Non-circulating account list when requested. + #[serde(default)] + pub non_circulating_accounts: std::vec::Vec, +} + +/// Optional commitment accepted by `getInflationGovernor`. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcInflationGovernorConfig { + /// Optional commitment level. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub commitment: std::option::Option, +} + +/// Epoch and contextual options accepted by `getInflationReward`. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcEpochConfig { + /// Optional reward epoch. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub epoch: std::option::Option, + /// Optional commitment level. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub commitment: std::option::Option, + /// Optional minimum context slot. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub min_context_slot: std::option::Option, +} + +/// Options accepted by `getSupply`. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcSupplyConfig { + /// Optional commitment level. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub commitment: std::option::Option, + /// Optional omission of the potentially large non-circulating account list. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub exclude_non_circulating_accounts_list: std::option::Option, +} + +/// Typed `getInflationGovernor` request. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct GetInflationGovernorRequest { + /// Optional commitment configuration. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetInflationGovernorRequest { + type Response = crate::RpcInflationGovernor; + + const METHOD: &'static str = "getInflationGovernor"; + + fn params(&self) -> kb_core::Result> { + if let std::option::Option::Some(config) = &self.config { + let value = match crate::standard_http::serialize_parameter(Self::METHOD, config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(std::vec![value]); + } + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +/// Typed `getInflationRate` request. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct GetInflationRateRequest; + +impl crate::StandardHttpRequest for crate::GetInflationRateRequest { + type Response = crate::RpcInflationRate; + + const METHOD: &'static str = "getInflationRate"; + + fn params(&self) -> kb_core::Result> { + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +/// Typed `getInflationReward` request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetInflationRewardRequest { + /// Account public keys whose rewards are requested, in response order. + pub addresses: std::vec::Vec, + /// Optional epoch, commitment and minimum-context options. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetInflationRewardRequest { + type Response = std::vec::Vec>; + + const METHOD: &'static str = "getInflationReward"; + + fn params(&self) -> kb_core::Result> { + let validation_result = crate::standard_http::validate_pubkey_list( + &self.addresses, + "getInflationReward address", + usize::MAX, + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let addresses = + match crate::standard_http::serialize_parameter(Self::METHOD, &self.addresses) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut params = std::vec![addresses]; + if let std::option::Option::Some(config) = &self.config { + let value = match crate::standard_http::serialize_parameter(Self::METHOD, config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + params.push(value); + } + return std::result::Result::Ok(params); + } +} + +/// Typed `getStakeMinimumDelegation` request. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct GetStakeMinimumDelegationRequest { + /// Optional commitment and minimum-context options. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetStakeMinimumDelegationRequest { + type Response = crate::RpcResponse; + + const METHOD: &'static str = "getStakeMinimumDelegation"; + + fn params(&self) -> kb_core::Result> { + if let std::option::Option::Some(config) = &self.config { + let value = match crate::standard_http::serialize_parameter(Self::METHOD, config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(std::vec![value]); + } + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +/// Typed `getSupply` request. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct GetSupplyRequest { + /// Optional commitment and non-circulating-list options. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetSupplyRequest { + type Response = crate::RpcResponse; + + const METHOD: &'static str = "getSupply"; + + fn params(&self) -> kb_core::Result> { + if let std::option::Option::Some(config) = &self.config { + let value = match crate::standard_http::serialize_parameter(Self::METHOD, config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(std::vec![value]); + } + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +#[cfg(test)] +mod tests { + fn pubkey(seed: u8) -> std::string::String { + return bs58::encode([seed; 32]).into_string(); + } + + #[test] + fn supply_distinguishes_omitted_and_explicit_false_option() { + let omitted = crate::GetSupplyRequest { + config: std::option::Option::Some(crate::RpcSupplyConfig { + commitment: std::option::Option::Some(crate::RpcCommitmentLevel::Finalized), + exclude_non_circulating_accounts_list: std::option::Option::None, + }), + }; + let explicit = crate::GetSupplyRequest { + config: std::option::Option::Some(crate::RpcSupplyConfig { + commitment: std::option::Option::Some(crate::RpcCommitmentLevel::Finalized), + exclude_non_circulating_accounts_list: std::option::Option::Some(false), + }), + }; + let omitted_params = match crate::StandardHttpRequest::params(&omitted) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("params failed: {error}"), + }; + let explicit_params = match crate::StandardHttpRequest::params(&explicit) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("params failed: {error}"), + }; + assert!(omitted_params[0].get("excludeNonCirculatingAccountsList").is_none()); + assert_eq!( + explicit_params[0]["excludeNonCirculatingAccountsList"], + serde_json::Value::Bool(false) + ); + } + + #[test] + fn inflation_reward_preserves_epoch_commitment_and_minimum_context() { + let request = crate::GetInflationRewardRequest { + addresses: std::vec![pubkey(1), pubkey(2)], + config: std::option::Option::Some(crate::RpcEpochConfig { + epoch: std::option::Option::Some(44), + commitment: std::option::Option::Some(crate::RpcCommitmentLevel::Confirmed), + min_context_slot: std::option::Option::Some(99), + }), + }; + let params = match crate::StandardHttpRequest::params(&request) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("params failed: {error}"), + }; + assert_eq!(params[1]["epoch"], serde_json::Value::from(44_u64)); + assert_eq!(params[1]["commitment"], serde_json::Value::String("confirmed".to_string())); + assert_eq!(params[1]["minContextSlot"], serde_json::Value::from(99_u64)); + + let reward = match serde_json::from_value::(serde_json::json!({ + "epoch": 44, + "effectiveSlot": 100, + "amount": 200, + "postBalance": 300, + "commission": 5, + "commissionBps": 575 + })) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("inflation reward parsing failed: {error}"), + }; + assert_eq!(reward.commission, std::option::Option::Some(5)); + assert_eq!(reward.commission_bps, std::option::Option::Some(575)); + + let empty = crate::GetInflationRewardRequest { + addresses: std::vec::Vec::new(), + config: std::option::Option::None, + }; + let empty_params = match crate::StandardHttpRequest::params(&empty) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("empty params failed: {error}"), + }; + assert_eq!(empty_params, std::vec![serde_json::json!([])]); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_tokens.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_tokens.rs new file mode 100644 index 0000000..22489cd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_tokens.rs @@ -0,0 +1,241 @@ +// file: kb_rpc/src/standard_http_tokens.rs +// version: 1 + +//! Configurable standard SPL Token-oriented Solana HTTP JSON-RPC requests. + +fn pubkey_with_optional_commitment_params( + method: &str, + address: &str, + field: &str, + config: &std::option::Option, +) -> kb_core::Result> { + let validation_result = crate::validate_solana_pubkey_text(address, field); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let mut params = std::vec![serde_json::Value::String(address.to_string())]; + if let std::option::Option::Some(config) = config { + let config_value = match crate::standard_http::serialize_parameter(method, config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + params.push(config_value); + } + return std::result::Result::Ok(params); +} + +fn token_accounts_query_params( + method: &str, + authority: &str, + field: &str, + filter: &crate::RpcTokenAccountsFilter, + config: &std::option::Option, +) -> kb_core::Result> { + let authority_result = crate::validate_solana_pubkey_text(authority, field); + if let std::result::Result::Err(error) = authority_result { + return std::result::Result::Err(error); + } + let filter_result = filter.validate(); + if let std::result::Result::Err(error) = filter_result { + return std::result::Result::Err(error); + } + if let std::option::Option::Some(config) = config { + let config_result = config.validate(); + if let std::result::Result::Err(error) = config_result { + return std::result::Result::Err(error); + } + } + let filter_value = match crate::standard_http::serialize_parameter(method, filter) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut params = std::vec![serde_json::Value::String(authority.to_string()), filter_value,]; + if let std::option::Option::Some(config) = config { + let config_value = match crate::standard_http::serialize_parameter(method, config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + params.push(config_value); + } + return std::result::Result::Ok(params); +} + +/// Typed `getTokenAccountBalance` request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetTokenAccountBalanceRequest { + /// Token account public key. + pub address: std::string::String, + /// Optional commitment option. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetTokenAccountBalanceRequest { + type Response = crate::RpcResponse; + + const METHOD: &'static str = "getTokenAccountBalance"; + + fn params(&self) -> kb_core::Result> { + return crate::standard_http_tokens::pubkey_with_optional_commitment_params( + Self::METHOD, + &self.address, + "getTokenAccountBalance address", + &self.config, + ); + } +} + +/// Typed `getTokenAccountsByDelegate` request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetTokenAccountsByDelegateRequest { + /// Delegate public key. + pub delegate: std::string::String, + /// Mint or Token Program selector. + pub filter: crate::RpcTokenAccountsFilter, + /// Optional account representation and context options. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetTokenAccountsByDelegateRequest { + type Response = crate::RpcResponse>; + + const METHOD: &'static str = "getTokenAccountsByDelegate"; + + fn params(&self) -> kb_core::Result> { + return crate::standard_http_tokens::token_accounts_query_params( + Self::METHOD, + &self.delegate, + "getTokenAccountsByDelegate delegate", + &self.filter, + &self.config, + ); + } +} + +/// Typed `getTokenAccountsByOwner` request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetTokenAccountsByOwnerRequest { + /// Owner public key. + pub owner: std::string::String, + /// Mint or Token Program selector. + pub filter: crate::RpcTokenAccountsFilter, + /// Optional account representation and context options. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetTokenAccountsByOwnerRequest { + type Response = crate::RpcResponse>; + + const METHOD: &'static str = "getTokenAccountsByOwner"; + + fn params(&self) -> kb_core::Result> { + return crate::standard_http_tokens::token_accounts_query_params( + Self::METHOD, + &self.owner, + "getTokenAccountsByOwner owner", + &self.filter, + &self.config, + ); + } +} + +/// Typed `getTokenLargestAccounts` request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetTokenLargestAccountsRequest { + /// Mint public key. + pub mint: std::string::String, + /// Optional commitment option. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetTokenLargestAccountsRequest { + type Response = crate::RpcResponse>; + + const METHOD: &'static str = "getTokenLargestAccounts"; + + fn params(&self) -> kb_core::Result> { + return crate::standard_http_tokens::pubkey_with_optional_commitment_params( + Self::METHOD, + &self.mint, + "getTokenLargestAccounts mint", + &self.config, + ); + } +} + +/// Typed `getTokenSupply` request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct GetTokenSupplyRequest { + /// Mint public key. + pub mint: std::string::String, + /// Optional commitment option. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetTokenSupplyRequest { + type Response = crate::RpcResponse; + + const METHOD: &'static str = "getTokenSupply"; + + fn params(&self) -> kb_core::Result> { + return crate::standard_http_tokens::pubkey_with_optional_commitment_params( + Self::METHOD, + &self.mint, + "getTokenSupply mint", + &self.config, + ); + } +} + +#[cfg(test)] +mod tests { + fn pubkey(seed: u8) -> std::string::String { + return bs58::encode([seed; 32]).into_string(); + } + + #[test] + fn token_owner_query_preserves_filter_and_independent_account_options() { + let request = crate::GetTokenAccountsByOwnerRequest { + owner: pubkey(1), + filter: crate::RpcTokenAccountsFilter::ProgramId(pubkey(2)), + config: std::option::Option::Some(crate::RpcAccountInfoConfig { + encoding: std::option::Option::Some(crate::RpcAccountEncoding::Base64Zstd), + data_slice: std::option::Option::None, + commitment: std::option::Option::Some(crate::RpcCommitmentLevel::Processed), + min_context_slot: std::option::Option::Some(42), + }), + }; + let params = match crate::StandardHttpRequest::params(&request) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("params failed: {error}"), + }; + assert_eq!(params[1], serde_json::json!({ "programId": pubkey(2) })); + assert_eq!(params[2]["encoding"], serde_json::Value::String("base64+zstd".to_string())); + assert_eq!(params[2]["commitment"], serde_json::Value::String("processed".to_string())); + } + + #[test] + fn token_balance_omits_unselected_options() { + let request = crate::GetTokenAccountBalanceRequest { + address: pubkey(3), + config: std::option::Option::None, + }; + let params = match crate::StandardHttpRequest::params(&request) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("params failed: {error}"), + }; + assert_eq!(params, std::vec![serde_json::Value::String(pubkey(3))]); + + let configured = crate::GetTokenSupplyRequest { + mint: pubkey(4), + config: std::option::Option::Some(crate::RpcCommitmentConfig { + commitment: std::option::Option::Some(crate::RpcCommitmentLevel::Finalized), + }), + }; + let configured_params = match crate::StandardHttpRequest::params(&configured) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("params failed: {error}"), + }; + assert_eq!(configured_params[1], serde_json::json!({ "commitment": "finalized" })); + assert!(configured_params[1].get("minContextSlot").is_none()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_transactions.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_transactions.rs new file mode 100644 index 0000000..829cb5a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/standard_http_transactions.rs @@ -0,0 +1,155 @@ +// file: kb_rpc/src/standard_http_transactions.rs +// version: 2 + +//! Configurable standard transaction-oriented Solana HTTP JSON-RPC requests. + +/// Prioritization fee observed for one recent slot. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct RpcPrioritizationFee { + /// Slot from which the fee sample was retained. + pub slot: u64, + /// Minimum compute-unit price in micro-lamports for the requested writable set. + pub prioritization_fee: u64, +} + +/// Typed `getRecentPrioritizationFees` request. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct GetRecentPrioritizationFeesRequest { + /// Optional writable account set. `None` omits the parameter; `Some([])` sends an explicit empty set. + pub locked_writable_accounts: std::option::Option>, +} + +impl crate::StandardHttpRequest for crate::GetRecentPrioritizationFeesRequest { + type Response = std::vec::Vec; + + const METHOD: &'static str = "getRecentPrioritizationFees"; + + fn params(&self) -> kb_core::Result> { + if let std::option::Option::Some(accounts) = &self.locked_writable_accounts { + let validation_result = crate::standard_http::validate_pubkey_list( + accounts, + "getRecentPrioritizationFees writable account", + crate::constants::MAX_PRIORITIZATION_FEE_ACCOUNT_COUNT, + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let value = match crate::standard_http::serialize_parameter(Self::METHOD, accounts) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(std::vec![value]); + } + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +/// Typed `getTransactionCount` request. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct GetTransactionCountRequest { + /// Optional commitment and minimum-context options. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::GetTransactionCountRequest { + type Response = u64; + + const METHOD: &'static str = "getTransactionCount"; + + fn params(&self) -> kb_core::Result> { + if let std::option::Option::Some(config) = &self.config { + let value = match crate::standard_http::serialize_parameter(Self::METHOD, config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(std::vec![value]); + } + return std::result::Result::Ok(std::vec::Vec::new()); + } +} + +/// Typed `isBlockhashValid` request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct IsBlockhashValidRequest { + /// Base58 blockhash being checked. + pub blockhash: std::string::String, + /// Optional commitment and minimum-context options. + pub config: std::option::Option, +} + +impl crate::StandardHttpRequest for crate::IsBlockhashValidRequest { + type Response = crate::RpcResponse; + + const METHOD: &'static str = "isBlockhashValid"; + + fn params(&self) -> kb_core::Result> { + let validation_result = + crate::validate_solana_hash_text(&self.blockhash, "isBlockhashValid blockhash"); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let mut params = std::vec![serde_json::Value::String(self.blockhash.clone())]; + if let std::option::Option::Some(config) = &self.config { + let value = match crate::standard_http::serialize_parameter(Self::METHOD, config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + params.push(value); + } + return std::result::Result::Ok(params); + } +} + +#[cfg(test)] +mod tests { + fn pubkey(seed: u8) -> std::string::String { + return bs58::encode([seed; 32]).into_string(); + } + + #[test] + fn prioritization_fee_request_distinguishes_omitted_and_explicit_empty_sets() { + let omitted = crate::GetRecentPrioritizationFeesRequest { + locked_writable_accounts: std::option::Option::None, + }; + let explicit = crate::GetRecentPrioritizationFeesRequest { + locked_writable_accounts: std::option::Option::Some(std::vec::Vec::new()), + }; + let omitted_params = match crate::StandardHttpRequest::params(&omitted) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("unexpected omitted params error: {error}"), + }; + let explicit_params = match crate::StandardHttpRequest::params(&explicit) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("unexpected explicit params error: {error}"), + }; + assert_eq!(omitted_params, std::vec::Vec::::new()); + assert_eq!(explicit_params, std::vec![serde_json::json!([])]); + } + + #[test] + fn prioritization_fee_request_enforces_official_account_bound() { + let request = crate::GetRecentPrioritizationFeesRequest { + locked_writable_accounts: std::option::Option::Some( + (0_u16..129_u16).map(|value| return pubkey(value as u8)).collect(), + ), + }; + assert!(crate::StandardHttpRequest::params(&request).is_err()); + } + + #[test] + fn blockhash_validity_preserves_minimum_context_slot() { + let request = crate::IsBlockhashValidRequest { + blockhash: pubkey(9), + config: std::option::Option::Some(crate::RpcContextConfig { + commitment: std::option::Option::Some(crate::RpcCommitmentLevel::Finalized), + min_context_slot: std::option::Option::Some(123), + }), + }; + let params = match crate::StandardHttpRequest::params(&request) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("params failed: {error}"), + }; + assert_eq!(params[1]["minContextSlot"], serde_json::Value::from(123_u64)); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/standard_methods.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/standard_methods.rs new file mode 100644 index 0000000..58cb979 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/standard_methods.rs @@ -0,0 +1,670 @@ +// file: kb_rpc/src/standard_methods.rs +// version: 3 + +//! Canonical inventory of standard Solana HTTP and WebSocket JSON-RPC methods. + +/// Strongest implementation contract exposed for one standard RPC method. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum StandardRpcContract { + /// The crate exposes a dedicated request/result adapter in addition to raw JSON transport. + TypedAdapter, + /// The method is explicitly registered and callable through validated raw JSON transport. + RawJson, +} + +impl crate::StandardRpcContract { + /// Returns the stable matrix code for this contract. + pub const fn code(self) -> &'static str { + return match self { + Self::TypedAdapter => "typed_adapter", + Self::RawJson => "raw_json", + }; + } +} + +/// Local category used to group standard Solana HTTP methods. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum StandardHttpCategory { + /// Account state and account ownership reads. + Accounts, + /// SPL Token account and mint reads. + Tokens, + /// Transaction, signature, fee, simulation and submission methods. + Transactions, + /// Block, ledger and performance methods. + Blocks, + /// Cluster, node, epoch, slot and validator methods. + Cluster, + /// Inflation, supply and stake-economics methods. + Economics, +} + +impl crate::StandardHttpCategory { + /// Returns the stable matrix code for this category. + pub const fn code(self) -> &'static str { + return match self { + Self::Accounts => "accounts", + Self::Tokens => "tokens", + Self::Transactions => "transactions", + Self::Blocks => "blocks", + Self::Cluster => "cluster", + Self::Economics => "economics", + }; + } +} + +/// One standard Solana HTTP JSON-RPC method specification. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct StandardHttpMethodSpec { + /// Exact JSON-RPC method name. + pub method: &'static str, + /// Local inventory category. + pub category: crate::StandardHttpCategory, + /// Strongest contract currently exposed by `kb_rpc`. + pub contract: crate::StandardRpcContract, +} + +impl crate::StandardHttpMethodSpec { + /// Returns the local HTTP routing class for this method. + pub fn method_class(&self) -> crate::HttpMethodClass { + return match self.method { + "requestAirdrop" | "sendTransaction" => crate::HttpMethodClass::SendTransaction, + "getBlock" + | "getBlocks" + | "getBlocksWithLimit" + | "getProgramAccounts" + | "getSignaturesForAddress" + | "getTransaction" + | "simulateTransaction" => crate::HttpMethodClass::HeavyRead, + _ => crate::HttpMethodClass::GeneralRpc, + }; + } +} + +/// Every standard Solana HTTP JSON-RPC method documented by the canonical RPC reference. +pub const STANDARD_HTTP_METHODS: [crate::StandardHttpMethodSpec; 52] = [ + crate::StandardHttpMethodSpec { + method: "getAccountInfo", + category: crate::StandardHttpCategory::Accounts, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getBalance", + category: crate::StandardHttpCategory::Accounts, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getLargestAccounts", + category: crate::StandardHttpCategory::Accounts, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getMinimumBalanceForRentExemption", + category: crate::StandardHttpCategory::Accounts, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getMultipleAccounts", + category: crate::StandardHttpCategory::Accounts, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getProgramAccounts", + category: crate::StandardHttpCategory::Accounts, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getTokenAccountBalance", + category: crate::StandardHttpCategory::Tokens, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getTokenAccountsByDelegate", + category: crate::StandardHttpCategory::Tokens, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getTokenAccountsByOwner", + category: crate::StandardHttpCategory::Tokens, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getTokenLargestAccounts", + category: crate::StandardHttpCategory::Tokens, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getTokenSupply", + category: crate::StandardHttpCategory::Tokens, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getFeeForMessage", + category: crate::StandardHttpCategory::Transactions, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getLatestBlockhash", + category: crate::StandardHttpCategory::Transactions, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getRecentPrioritizationFees", + category: crate::StandardHttpCategory::Transactions, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getSignaturesForAddress", + category: crate::StandardHttpCategory::Transactions, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getSignatureStatuses", + category: crate::StandardHttpCategory::Transactions, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getTransaction", + category: crate::StandardHttpCategory::Transactions, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getTransactionCount", + category: crate::StandardHttpCategory::Transactions, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "isBlockhashValid", + category: crate::StandardHttpCategory::Transactions, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "requestAirdrop", + category: crate::StandardHttpCategory::Transactions, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "sendTransaction", + category: crate::StandardHttpCategory::Transactions, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "simulateTransaction", + category: crate::StandardHttpCategory::Transactions, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getBlock", + category: crate::StandardHttpCategory::Blocks, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getBlockCommitment", + category: crate::StandardHttpCategory::Blocks, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getBlockHeight", + category: crate::StandardHttpCategory::Blocks, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getBlockProduction", + category: crate::StandardHttpCategory::Blocks, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getBlocks", + category: crate::StandardHttpCategory::Blocks, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getBlocksWithLimit", + category: crate::StandardHttpCategory::Blocks, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getBlockTime", + category: crate::StandardHttpCategory::Blocks, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getFirstAvailableBlock", + category: crate::StandardHttpCategory::Blocks, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getRecentPerformanceSamples", + category: crate::StandardHttpCategory::Blocks, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "minimumLedgerSlot", + category: crate::StandardHttpCategory::Blocks, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getClusterNodes", + category: crate::StandardHttpCategory::Cluster, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getEpochInfo", + category: crate::StandardHttpCategory::Cluster, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getEpochSchedule", + category: crate::StandardHttpCategory::Cluster, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getGenesisHash", + category: crate::StandardHttpCategory::Cluster, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getHealth", + category: crate::StandardHttpCategory::Cluster, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getHighestSnapshotSlot", + category: crate::StandardHttpCategory::Cluster, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getIdentity", + category: crate::StandardHttpCategory::Cluster, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getLeaderSchedule", + category: crate::StandardHttpCategory::Cluster, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getMaxRetransmitSlot", + category: crate::StandardHttpCategory::Cluster, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getMaxShredInsertSlot", + category: crate::StandardHttpCategory::Cluster, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getSlot", + category: crate::StandardHttpCategory::Cluster, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getSlotLeader", + category: crate::StandardHttpCategory::Cluster, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getSlotLeaders", + category: crate::StandardHttpCategory::Cluster, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getVersion", + category: crate::StandardHttpCategory::Cluster, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getVoteAccounts", + category: crate::StandardHttpCategory::Cluster, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getInflationGovernor", + category: crate::StandardHttpCategory::Economics, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getInflationRate", + category: crate::StandardHttpCategory::Economics, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getInflationReward", + category: crate::StandardHttpCategory::Economics, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getStakeMinimumDelegation", + category: crate::StandardHttpCategory::Economics, + contract: crate::StandardRpcContract::TypedAdapter, + }, + crate::StandardHttpMethodSpec { + method: "getSupply", + category: crate::StandardHttpCategory::Economics, + contract: crate::StandardRpcContract::TypedAdapter, + }, +]; + +/// Returns the canonical specification for one exact standard HTTP method name. +pub fn standard_http_method( + method: &str, +) -> std::option::Option<&'static crate::StandardHttpMethodSpec> { + return crate::STANDARD_HTTP_METHODS.iter().find(|entry| return entry.method == method); +} + +/// Stability of one standard Solana WebSocket subscription surface. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum StandardWsStability { + /// Stable standard PubSub method. + Stable, + /// Method documented as unstable and potentially gated by validator flags. + Unstable, +} + +impl crate::StandardWsStability { + /// Returns the stable matrix code for this stability class. + pub const fn code(self) -> &'static str { + return match self { + Self::Stable => "stable", + Self::Unstable => "unstable", + }; + } +} + +/// One standard Solana WebSocket subscribe/unsubscribe pair. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct StandardWsSubscriptionSpec { + /// Exact subscribe method name. + pub subscribe_method: &'static str, + /// Exact unsubscribe method name. + pub unsubscribe_method: &'static str, + /// Exact notification method name emitted by the server. + pub notification_method: &'static str, + /// Stability declared by the canonical Solana RPC documentation. + pub stability: crate::StandardWsStability, + /// Strongest typed request contract exposed by `kb_rpc`. + pub request_contract: crate::StandardRpcContract, + /// Strongest typed notification contract exposed by `kb_rpc`. + pub notification_contract: crate::StandardRpcContract, + /// Whether the pair is supported by the reusable persistent session runtime. + pub persistent_runtime: bool, +} + +/// Every standard Solana WebSocket subscription pair documented by the canonical RPC reference. +pub const STANDARD_WS_SUBSCRIPTIONS: [crate::StandardWsSubscriptionSpec; 9] = [ + crate::StandardWsSubscriptionSpec { + subscribe_method: "accountSubscribe", + unsubscribe_method: "accountUnsubscribe", + notification_method: "accountNotification", + stability: crate::StandardWsStability::Stable, + request_contract: crate::StandardRpcContract::TypedAdapter, + notification_contract: crate::StandardRpcContract::TypedAdapter, + persistent_runtime: true, + }, + crate::StandardWsSubscriptionSpec { + subscribe_method: "blockSubscribe", + unsubscribe_method: "blockUnsubscribe", + notification_method: "blockNotification", + stability: crate::StandardWsStability::Unstable, + request_contract: crate::StandardRpcContract::TypedAdapter, + notification_contract: crate::StandardRpcContract::TypedAdapter, + persistent_runtime: true, + }, + crate::StandardWsSubscriptionSpec { + subscribe_method: "logsSubscribe", + unsubscribe_method: "logsUnsubscribe", + notification_method: "logsNotification", + stability: crate::StandardWsStability::Stable, + request_contract: crate::StandardRpcContract::TypedAdapter, + notification_contract: crate::StandardRpcContract::TypedAdapter, + persistent_runtime: true, + }, + crate::StandardWsSubscriptionSpec { + subscribe_method: "programSubscribe", + unsubscribe_method: "programUnsubscribe", + notification_method: "programNotification", + stability: crate::StandardWsStability::Stable, + request_contract: crate::StandardRpcContract::TypedAdapter, + notification_contract: crate::StandardRpcContract::TypedAdapter, + persistent_runtime: true, + }, + crate::StandardWsSubscriptionSpec { + subscribe_method: "rootSubscribe", + unsubscribe_method: "rootUnsubscribe", + notification_method: "rootNotification", + stability: crate::StandardWsStability::Stable, + request_contract: crate::StandardRpcContract::TypedAdapter, + notification_contract: crate::StandardRpcContract::TypedAdapter, + persistent_runtime: true, + }, + crate::StandardWsSubscriptionSpec { + subscribe_method: "signatureSubscribe", + unsubscribe_method: "signatureUnsubscribe", + notification_method: "signatureNotification", + stability: crate::StandardWsStability::Stable, + request_contract: crate::StandardRpcContract::TypedAdapter, + notification_contract: crate::StandardRpcContract::TypedAdapter, + persistent_runtime: true, + }, + crate::StandardWsSubscriptionSpec { + subscribe_method: "slotSubscribe", + unsubscribe_method: "slotUnsubscribe", + notification_method: "slotNotification", + stability: crate::StandardWsStability::Stable, + request_contract: crate::StandardRpcContract::TypedAdapter, + notification_contract: crate::StandardRpcContract::TypedAdapter, + persistent_runtime: true, + }, + crate::StandardWsSubscriptionSpec { + subscribe_method: "slotsUpdatesSubscribe", + unsubscribe_method: "slotsUpdatesUnsubscribe", + notification_method: "slotsUpdatesNotification", + stability: crate::StandardWsStability::Unstable, + request_contract: crate::StandardRpcContract::TypedAdapter, + notification_contract: crate::StandardRpcContract::TypedAdapter, + persistent_runtime: true, + }, + crate::StandardWsSubscriptionSpec { + subscribe_method: "voteSubscribe", + unsubscribe_method: "voteUnsubscribe", + notification_method: "voteNotification", + stability: crate::StandardWsStability::Unstable, + request_contract: crate::StandardRpcContract::TypedAdapter, + notification_contract: crate::StandardRpcContract::TypedAdapter, + persistent_runtime: true, + }, +]; + +/// Returns the canonical subscription specification matching a subscribe or unsubscribe method. +pub fn standard_ws_subscription( + method: &str, +) -> std::option::Option<&'static crate::StandardWsSubscriptionSpec> { + return crate::STANDARD_WS_SUBSCRIPTIONS.iter().find(|entry| { + return entry.subscribe_method == method || entry.unsubscribe_method == method; + }); +} + +#[cfg(test)] +mod tests { + #[test] + fn standard_http_inventory_is_exact_unique_and_classified() { + assert_eq!(crate::STANDARD_HTTP_METHODS.len(), 52); + let mut names = std::collections::BTreeSet::new(); + let mut typed_count = 0_usize; + for method in &crate::STANDARD_HTTP_METHODS { + assert!(names.insert(method.method)); + if method.contract == crate::StandardRpcContract::TypedAdapter { + typed_count = typed_count.saturating_add(1); + } + assert_eq!( + crate::standard_http_method(method.method), + std::option::Option::Some(method) + ); + } + assert_eq!(typed_count, 52); + } + + #[test] + fn configurable_request_registry_covers_the_former_thirty_eight_raw_methods() { + fn method() -> &'static str { + return ::METHOD; + } + let methods = [ + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + method::(), + ]; + assert_eq!(methods.len(), 38); + let mut unique = std::collections::BTreeSet::new(); + for method_name in methods { + assert!(unique.insert(method_name)); + let specification = match crate::standard_http_method(method_name) { + std::option::Option::Some(specification) => specification, + std::option::Option::None => panic!("typed method absent from registry"), + }; + assert_eq!(specification.contract, crate::StandardRpcContract::TypedAdapter); + } + } + + #[test] + fn standard_ws_inventory_contains_nine_pairs_and_eighteen_unique_methods() { + assert_eq!(crate::STANDARD_WS_SUBSCRIPTIONS.len(), 9); + let mut names = std::collections::BTreeSet::new(); + let mut unstable_count = 0_usize; + for subscription in &crate::STANDARD_WS_SUBSCRIPTIONS { + assert!(names.insert(subscription.subscribe_method)); + assert!(names.insert(subscription.unsubscribe_method)); + if subscription.stability == crate::StandardWsStability::Unstable { + unstable_count = unstable_count.saturating_add(1); + } + assert_eq!(subscription.request_contract, crate::StandardRpcContract::TypedAdapter); + assert_eq!( + subscription.notification_contract, + crate::StandardRpcContract::TypedAdapter + ); + assert!(subscription.persistent_runtime); + assert_eq!( + crate::standard_ws_subscription(subscription.subscribe_method), + std::option::Option::Some(subscription) + ); + assert_eq!( + crate::standard_ws_subscription(subscription.unsubscribe_method), + std::option::Option::Some(subscription) + ); + } + assert_eq!(names.len(), 18); + assert_eq!(unstable_count, 3); + } + + #[test] + fn standard_rpc_matrix_matches_compiled_inventory() { + let raw = include_str!("../../docs/SOLANA_STANDARD_RPC_MATRIX.json"); + let parsed = match serde_json::from_str::(raw) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("rpc matrix parsing failed: {error}"), + }; + let http_methods = match parsed.get("http_methods").and_then(serde_json::Value::as_array) { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("rpc matrix http_methods missing"), + }; + let ws_subscriptions = + match parsed.get("ws_subscriptions").and_then(serde_json::Value::as_array) { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("rpc matrix ws_subscriptions missing"), + }; + assert_eq!(http_methods.len(), crate::STANDARD_HTTP_METHODS.len()); + assert_eq!(ws_subscriptions.len(), crate::STANDARD_WS_SUBSCRIPTIONS.len()); + for method in &crate::STANDARD_HTTP_METHODS { + let matrix_entry = http_methods.iter().find(|entry| { + return entry.get("method").and_then(serde_json::Value::as_str) + == std::option::Option::Some(method.method); + }); + let matrix_entry = match matrix_entry { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("rpc matrix HTTP method missing"), + }; + assert_eq!( + matrix_entry.get("category").and_then(serde_json::Value::as_str), + std::option::Option::Some(method.category.code()) + ); + assert_eq!( + matrix_entry.get("contract").and_then(serde_json::Value::as_str), + std::option::Option::Some(method.contract.code()) + ); + } + for subscription in &crate::STANDARD_WS_SUBSCRIPTIONS { + let matrix_entry = ws_subscriptions.iter().find(|entry| { + return entry.get("subscribe_method").and_then(serde_json::Value::as_str) + == std::option::Option::Some(subscription.subscribe_method); + }); + let matrix_entry = match matrix_entry { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("rpc matrix WS subscription missing"), + }; + assert_eq!( + matrix_entry.get("unsubscribe_method").and_then(serde_json::Value::as_str), + std::option::Option::Some(subscription.unsubscribe_method) + ); + assert_eq!( + matrix_entry.get("notification_method").and_then(serde_json::Value::as_str), + std::option::Option::Some(subscription.notification_method) + ); + assert_eq!( + matrix_entry.get("stability").and_then(serde_json::Value::as_str), + std::option::Option::Some(subscription.stability.code()) + ); + assert_eq!( + matrix_entry.get("request_contract").and_then(serde_json::Value::as_str), + std::option::Option::Some(subscription.request_contract.code()) + ); + assert_eq!( + matrix_entry.get("notification_contract").and_then(serde_json::Value::as_str), + std::option::Option::Some(subscription.notification_contract.code()) + ); + assert_eq!( + matrix_entry.get("persistent_runtime").and_then(serde_json::Value::as_bool), + std::option::Option::Some(subscription.persistent_runtime) + ); + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/standard_ws.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/standard_ws.rs new file mode 100644 index 0000000..1278c6e --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/standard_ws.rs @@ -0,0 +1,1274 @@ +// file: kb_rpc/src/standard_ws.rs +// version: 2 + +//! Typed contracts for every standard Solana WebSocket subscription. + +/// Effective endpoint capabilities for unstable standard subscriptions. +/// +/// Configuration enables an optimistic first attempt. A persistent session may disable one +/// capability after the node explicitly reports that the method is absent or not enabled. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct StandardWsCapabilities { + /// Whether `blockSubscribe` may be attempted on this endpoint. + pub block_subscribe: bool, + /// Whether `slotsUpdatesSubscribe` may be attempted on this endpoint. + pub slots_updates_subscribe: bool, + /// Whether `voteSubscribe` may be attempted on this endpoint. + pub vote_subscribe: bool, +} + +impl crate::StandardWsCapabilities { + /// Builds initial capabilities from request kinds explicitly advertised by an endpoint. + pub fn from_endpoint(endpoint: &kb_config::WsEndpointConfig) -> Self { + return Self { + block_subscribe: crate::standard_ws::endpoint_declares_method( + endpoint, + "blockSubscribe", + ), + slots_updates_subscribe: crate::standard_ws::endpoint_declares_method( + endpoint, + "slotsUpdatesSubscribe", + ), + vote_subscribe: crate::standard_ws::endpoint_declares_method(endpoint, "voteSubscribe"), + }; + } + + fn allows(&self, method: &str) -> bool { + return match method { + "blockSubscribe" => self.block_subscribe, + "slotsUpdatesSubscribe" => self.slots_updates_subscribe, + "voteSubscribe" => self.vote_subscribe, + _ => true, + }; + } + + pub(crate) fn disable_method(&mut self, method: &str) -> bool { + let capability = match method { + "blockSubscribe" => &mut self.block_subscribe, + "slotsUpdatesSubscribe" => &mut self.slots_updates_subscribe, + "voteSubscribe" => &mut self.vote_subscribe, + _ => return false, + }; + if !*capability { + return false; + } + *capability = false; + return true; + } + + pub(crate) fn is_unstable_method(method: &str) -> bool { + return matches!(method, "blockSubscribe" | "slotsUpdatesSubscribe" | "voteSubscribe"); + } +} + +/// Account options accepted by `accountSubscribe`. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct WsAccountSubscribeConfig { + /// Optional commitment level. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub commitment: std::option::Option, + /// Optional account-data encoding. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub encoding: std::option::Option, + /// Optional account-data byte slice. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub data_slice: std::option::Option, +} + +impl crate::WsAccountSubscribeConfig { + fn validate(&self) -> kb_core::Result<()> { + return crate::RpcAccountInfoConfig { + encoding: self.encoding, + data_slice: self.data_slice, + commitment: self.commitment, + min_context_slot: std::option::Option::None, + } + .validate(); + } +} + +/// Filter accepted by `blockSubscribe`. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum WsBlockFilter { + /// Subscribe to every block. + All, + /// Subscribe to blocks mentioning one account or Program ID. + MentionsAccountOrProgram(std::string::String), +} + +impl serde::Serialize for crate::WsBlockFilter { + fn serialize( + &self, + serializer: Serializer, + ) -> std::result::Result + where + Serializer: serde::Serializer, + { + return match self { + Self::All => serializer.serialize_str("all"), + Self::MentionsAccountOrProgram(pubkey) => { + let value = serde_json::json!({ "mentionsAccountOrProgram": pubkey }); + serde::Serialize::serialize(&value, serializer) + }, + }; + } +} + +impl<'de> serde::Deserialize<'de> for crate::WsBlockFilter { + fn deserialize( + deserializer: Deserializer, + ) -> std::result::Result + where + Deserializer: serde::Deserializer<'de>, + { + let value = match ::deserialize(deserializer) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if value == serde_json::Value::String("all".to_string()) { + return std::result::Result::Ok(Self::All); + } + let pubkey = value + .as_object() + .filter(|object| return object.len() == 1) + .and_then(|object| return object.get("mentionsAccountOrProgram")) + .and_then(serde_json::Value::as_str); + return match pubkey { + std::option::Option::Some(pubkey) => { + std::result::Result::Ok(Self::MentionsAccountOrProgram(pubkey.to_string())) + }, + std::option::Option::None => { + std::result::Result::Err(::custom( + "block filter must be 'all' or mentionsAccountOrProgram", + )) + }, + }; + } +} + +/// Options accepted by `blockSubscribe`. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct WsBlockSubscribeConfig { + /// Optional confirmed or finalized commitment. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub commitment: std::option::Option, + /// Optional transaction encoding. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub encoding: std::option::Option, + /// Optional transaction detail level. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub transaction_details: std::option::Option, + /// Whether rewards are included. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub show_rewards: std::option::Option, + /// Highest transaction version the caller can decode. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub max_supported_transaction_version: std::option::Option, +} + +impl crate::WsBlockSubscribeConfig { + fn validate(&self) -> kb_core::Result<()> { + if self.commitment == std::option::Option::Some(crate::RpcCommitmentLevel::Processed) { + return std::result::Result::Err(kb_core::Error::config( + "blockSubscribe does not accept processed commitment", + )); + } + return std::result::Result::Ok(()); + } +} + +/// Filter accepted by `logsSubscribe`. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum WsLogsFilter { + /// Subscribe to non-simple-vote transaction logs. + All, + /// Subscribe to every transaction log, including simple votes. + AllWithVotes, + /// Subscribe to transactions mentioning one account or Program ID. + Mentions(std::string::String), +} + +impl serde::Serialize for crate::WsLogsFilter { + fn serialize( + &self, + serializer: Serializer, + ) -> std::result::Result + where + Serializer: serde::Serializer, + { + return match self { + Self::All => serializer.serialize_str("all"), + Self::AllWithVotes => serializer.serialize_str("allWithVotes"), + Self::Mentions(pubkey) => { + let value = serde_json::json!({ "mentions": [pubkey] }); + serde::Serialize::serialize(&value, serializer) + }, + }; + } +} + +impl<'de> serde::Deserialize<'de> for crate::WsLogsFilter { + fn deserialize( + deserializer: Deserializer, + ) -> std::result::Result + where + Deserializer: serde::Deserializer<'de>, + { + let value = match ::deserialize(deserializer) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if value == serde_json::Value::String("all".to_string()) { + return std::result::Result::Ok(Self::All); + } + if value == serde_json::Value::String("allWithVotes".to_string()) { + return std::result::Result::Ok(Self::AllWithVotes); + } + let mentions = value + .as_object() + .filter(|object| return object.len() == 1) + .and_then(|object| return object.get("mentions")) + .and_then(serde_json::Value::as_array); + let pubkey = mentions + .and_then(|values| { + if values.len() == 1 { + return values.first(); + } + return std::option::Option::None; + }) + .and_then(serde_json::Value::as_str); + return match pubkey { + std::option::Option::Some(pubkey) => { + std::result::Result::Ok(Self::Mentions(pubkey.to_string())) + }, + std::option::Option::None => { + std::result::Result::Err(::custom( + "logs mentions filter must contain exactly one public key", + )) + }, + }; + } +} + +/// Options accepted by `logsSubscribe`. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct WsLogsSubscribeConfig { + /// Optional commitment level. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub commitment: std::option::Option, +} + +/// Options accepted by `programSubscribe`. +#[derive(Clone, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct WsProgramSubscribeConfig { + /// Optional account filters, evaluated in order. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub filters: std::option::Option>, + /// Optional account-data encoding. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub encoding: std::option::Option, + /// Optional account-data byte slice. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub data_slice: std::option::Option, + /// Optional commitment level. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub commitment: std::option::Option, + /// Whether the result includes the standard context wrapper. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub with_context: std::option::Option, +} + +impl crate::WsProgramSubscribeConfig { + fn validate(&self) -> kb_core::Result<()> { + return crate::RpcProgramAccountsConfig { + filters: self.filters.clone(), + encoding: self.encoding, + data_slice: self.data_slice, + commitment: self.commitment, + min_context_slot: std::option::Option::None, + with_context: self.with_context, + sort_results: std::option::Option::None, + } + .validate(); + } +} + +/// Options accepted by `signatureSubscribe`. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase", deny_unknown_fields)] +pub struct WsSignatureSubscribeConfig { + /// Optional commitment level. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub commitment: std::option::Option, + /// Whether an early `receivedSignature` notification is requested. + #[serde(skip_serializing_if = "std::option::Option::is_none")] + pub enable_received_notification: std::option::Option, +} + +/// Typed account subscription request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct AccountSubscribeRequest { + /// Account public key. + pub pubkey: std::string::String, + /// Optional commitment, encoding and slice options. + pub config: std::option::Option, +} + +/// Typed block subscription request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct BlockSubscribeRequest { + /// Block selection filter. + pub filter: crate::WsBlockFilter, + /// Optional commitment, encoding, details, rewards and version options. + pub config: std::option::Option, +} + +/// Typed logs subscription request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct LogsSubscribeRequest { + /// Log selection filter. + pub filter: crate::WsLogsFilter, + /// Optional commitment. + pub config: std::option::Option, +} + +/// Typed program-account subscription request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct ProgramSubscribeRequest { + /// Program public key. + pub program_id: std::string::String, + /// Optional filters, commitment, encoding, slice and context switch. + pub config: std::option::Option, +} + +/// Typed root subscription request. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct RootSubscribeRequest; + +/// Typed signature subscription request. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct SignatureSubscribeRequest { + /// Transaction signature. + pub signature: std::string::String, + /// Optional commitment and received-notification switch. + pub config: std::option::Option, +} + +/// Typed slot subscription request. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct SlotSubscribeRequest; + +/// Typed unstable slot-updates subscription request. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct SlotsUpdatesSubscribeRequest; + +/// Typed unstable vote subscription request. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct VoteSubscribeRequest; + +/// One of the nine standard Solana WebSocket subscription requests. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum StandardWsRequest { + /// `accountSubscribe` request. + Account(crate::AccountSubscribeRequest), + /// `blockSubscribe` request. + Block(crate::BlockSubscribeRequest), + /// `logsSubscribe` request. + Logs(crate::LogsSubscribeRequest), + /// `programSubscribe` request. + Program(crate::ProgramSubscribeRequest), + /// `rootSubscribe` request. + Root(crate::RootSubscribeRequest), + /// `signatureSubscribe` request. + Signature(crate::SignatureSubscribeRequest), + /// `slotSubscribe` request. + Slot(crate::SlotSubscribeRequest), + /// `slotsUpdatesSubscribe` request. + SlotsUpdates(crate::SlotsUpdatesSubscribeRequest), + /// `voteSubscribe` request. + Vote(crate::VoteSubscribeRequest), +} + +impl crate::StandardWsRequest { + /// Returns the canonical subscription specification. + pub fn specification(&self) -> &'static crate::StandardWsSubscriptionSpec { + return match self { + Self::Account(_) => &crate::STANDARD_WS_SUBSCRIPTIONS[0], + Self::Block(_) => &crate::STANDARD_WS_SUBSCRIPTIONS[1], + Self::Logs(_) => &crate::STANDARD_WS_SUBSCRIPTIONS[2], + Self::Program(_) => &crate::STANDARD_WS_SUBSCRIPTIONS[3], + Self::Root(_) => &crate::STANDARD_WS_SUBSCRIPTIONS[4], + Self::Signature(_) => &crate::STANDARD_WS_SUBSCRIPTIONS[5], + Self::Slot(_) => &crate::STANDARD_WS_SUBSCRIPTIONS[6], + Self::SlotsUpdates(_) => &crate::STANDARD_WS_SUBSCRIPTIONS[7], + Self::Vote(_) => &crate::STANDARD_WS_SUBSCRIPTIONS[8], + }; + } + + /// Returns the exact subscribe method. + pub const fn subscribe_method(&self) -> &'static str { + return match self { + Self::Account(_) => "accountSubscribe", + Self::Block(_) => "blockSubscribe", + Self::Logs(_) => "logsSubscribe", + Self::Program(_) => "programSubscribe", + Self::Root(_) => "rootSubscribe", + Self::Signature(_) => "signatureSubscribe", + Self::Slot(_) => "slotSubscribe", + Self::SlotsUpdates(_) => "slotsUpdatesSubscribe", + Self::Vote(_) => "voteSubscribe", + }; + } + + /// Validates capability gates and builds exact positional JSON-RPC parameters. + pub fn params( + &self, + capabilities: crate::StandardWsCapabilities, + ) -> kb_core::Result> { + if !capabilities.allows(self.subscribe_method()) { + return std::result::Result::Err(kb_core::Error::config(format!( + "unstable WebSocket method '{}' is not enabled by the endpoint capability set", + self.subscribe_method() + ))); + } + return match self { + Self::Account(request) => crate::standard_ws::account_params(request), + Self::Block(request) => crate::standard_ws::block_params(request), + Self::Logs(request) => crate::standard_ws::logs_params(request), + Self::Program(request) => crate::standard_ws::program_params(request), + Self::Root(_) | Self::Slot(_) | Self::SlotsUpdates(_) | Self::Vote(_) => { + std::result::Result::Ok(std::vec::Vec::new()) + }, + Self::Signature(request) => crate::standard_ws::signature_params(request), + }; + } +} + +/// Typed account notification. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct WsAccountNotification { + /// Remote subscription identifier. + pub subscription: u64, + /// Contextual account value. + pub result: crate::RpcResponse, +} + +/// One block update emitted by `blockSubscribe`. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct WsBlockUpdate { + /// Block slot. + pub slot: u64, + /// Block when the node produced one for the slot. + #[serde(default)] + pub block: std::option::Option, + /// Node-provided block error when present. + #[serde(default)] + pub err: std::option::Option, +} + +/// Typed block notification. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct WsBlockNotification { + /// Remote subscription identifier. + pub subscription: u64, + /// Contextual block update. + pub result: crate::RpcResponse, +} + +/// One log notification value. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct WsLogsValue { + /// Transaction signature. + pub signature: std::string::String, + /// Runtime transaction error, or `None` on success. + pub err: std::option::Option, + /// Runtime log messages. + pub logs: std::vec::Vec, +} + +/// Typed logs notification. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct WsLogsNotification { + /// Remote subscription identifier. + pub subscription: u64, + /// Contextual log result. + pub result: crate::RpcResponse, +} + +/// Typed program-account notification. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct WsProgramNotification { + /// Remote subscription identifier. + pub subscription: u64, + /// Contextual keyed account result. + pub result: crate::RpcOptionalContext, +} + +/// Typed root notification. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct WsRootNotification { + /// Remote subscription identifier. + pub subscription: u64, + /// New root slot. + pub result: u64, +} + +/// Signature notification result. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(untagged)] +pub enum WsSignatureValue { + /// Final signature status. + Status { + /// Runtime transaction error, or `None` on success. + err: std::option::Option, + }, + /// Early node-receipt marker. + Received(std::string::String), +} + +/// Typed signature notification. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct WsSignatureNotification { + /// Remote subscription identifier. + pub subscription: u64, + /// Contextual receipt or final status. + pub result: crate::RpcResponse, +} + +/// One slot progression notification. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct WsSlotInfo { + /// Current slot. + pub slot: u64, + /// Parent slot. + pub parent: u64, + /// Current root slot. + pub root: u64, +} + +/// Typed slot notification. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct WsSlotNotification { + /// Remote subscription identifier. + pub subscription: u64, + /// Slot progression value. + pub result: crate::WsSlotInfo, +} + +/// Transaction counters included by a frozen-slot update. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct WsSlotTransactionStats { + /// Total transaction entries processed by the bank. + pub num_transaction_entries: u64, + /// Successful transactions. + pub num_successful_transactions: u64, + /// Failed transactions. + pub num_failed_transactions: u64, + /// Maximum transactions per entry. + pub max_transactions_per_entry: u64, +} + +/// One unstable slot lifecycle update. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(tag = "type", rename_all = "camelCase")] +pub enum WsSlotUpdate { + /// First shred for a slot was received. + FirstShredReceived { + /// Slot. + slot: u64, + /// Unix timestamp in milliseconds. + timestamp: i64, + }, + /// Every shred for a slot was received. + Completed { + /// Slot. + slot: u64, + /// Unix timestamp in milliseconds. + timestamp: i64, + }, + /// A bank was created for a slot. + CreatedBank { + /// Slot. + slot: u64, + /// Parent slot. + parent: u64, + /// Unix timestamp in milliseconds. + timestamp: i64, + }, + /// A bank was frozen. + Frozen { + /// Slot. + slot: u64, + /// Unix timestamp in milliseconds. + timestamp: i64, + /// Bank transaction counters. + stats: crate::WsSlotTransactionStats, + }, + /// A slot was marked dead. + Dead { + /// Slot. + slot: u64, + /// Unix timestamp in milliseconds. + timestamp: i64, + /// Node-provided failure description. + err: std::string::String, + }, + /// A slot reached optimistic confirmation. + OptimisticConfirmation { + /// Slot. + slot: u64, + /// Unix timestamp in milliseconds. + timestamp: i64, + }, + /// A slot became root. + Root { + /// Slot. + slot: u64, + /// Unix timestamp in milliseconds. + timestamp: i64, + }, +} + +/// Typed unstable slot-updates notification. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct WsSlotsUpdatesNotification { + /// Remote subscription identifier. + pub subscription: u64, + /// Slot lifecycle update. + pub result: crate::WsSlotUpdate, +} + +/// One vote notification value. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct WsVoteValue { + /// Vote account public key. + pub vote_pubkey: std::string::String, + /// Voted slots. + pub slots: std::vec::Vec, + /// Vote hash. + pub hash: std::string::String, + /// Optional vote timestamp. + #[serde(default)] + pub timestamp: std::option::Option, + /// Vote transaction signature. + pub signature: std::string::String, +} + +/// Typed unstable vote notification. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct WsVoteNotification { + /// Remote subscription identifier. + pub subscription: u64, + /// Vote value. + pub result: crate::WsVoteValue, +} + +/// Typed notification emitted by one of the nine standard subscriptions. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(tag = "kind", content = "notification", rename_all = "camelCase")] +pub enum StandardWsNotification { + /// `accountNotification`. + Account(crate::WsAccountNotification), + /// `blockNotification`. + Block(crate::WsBlockNotification), + /// `logsNotification`. + Logs(crate::WsLogsNotification), + /// `programNotification`. + Program(crate::WsProgramNotification), + /// `rootNotification`. + Root(crate::WsRootNotification), + /// `signatureNotification`. + Signature(crate::WsSignatureNotification), + /// `slotNotification`. + Slot(crate::WsSlotNotification), + /// `slotsUpdatesNotification`. + SlotsUpdates(crate::WsSlotsUpdatesNotification), + /// `voteNotification`. + Vote(crate::WsVoteNotification), +} + +impl crate::StandardWsNotification { + /// Returns the remote subscription identifier. + pub const fn subscription_id(&self) -> u64 { + return match self { + Self::Account(value) => value.subscription, + Self::Block(value) => value.subscription, + Self::Logs(value) => value.subscription, + Self::Program(value) => value.subscription, + Self::Root(value) => value.subscription, + Self::Signature(value) => value.subscription, + Self::Slot(value) => value.subscription, + Self::SlotsUpdates(value) => value.subscription, + Self::Vote(value) => value.subscription, + }; + } +} + +/// Parses and validates one standard WebSocket notification. +pub fn adapt_standard_ws_notification( + notification: &crate::JsonRpcNotification, +) -> kb_core::Result { + let subscription = notification.params.subscription; + let result = ¬ification.params.result; + return match notification.method.as_str() { + "accountNotification" => crate::standard_ws::adapt_notification_result( + subscription, + result, + crate::WsAccountNotification::new, + ) + .map(crate::StandardWsNotification::Account), + "blockNotification" => crate::standard_ws::adapt_notification_result( + subscription, + result, + crate::WsBlockNotification::new, + ) + .map(crate::StandardWsNotification::Block), + "logsNotification" => crate::standard_ws::adapt_notification_result( + subscription, + result, + crate::WsLogsNotification::new, + ) + .map(crate::StandardWsNotification::Logs), + "programNotification" => crate::standard_ws::adapt_notification_result( + subscription, + result, + crate::WsProgramNotification::new, + ) + .map(crate::StandardWsNotification::Program), + "rootNotification" => crate::standard_ws::adapt_notification_result( + subscription, + result, + crate::WsRootNotification::new, + ) + .map(crate::StandardWsNotification::Root), + "signatureNotification" => { + let adapted = crate::standard_ws::adapt_notification_result( + subscription, + result, + crate::WsSignatureNotification::new, + ); + return match adapted { + std::result::Result::Ok(notification) => { + if let crate::WsSignatureValue::Received(marker) = ¬ification.result.value { + if marker != "receivedSignature" { + return std::result::Result::Err(kb_core::Error::json(format!( + "unknown signature notification marker '{marker}'" + ))); + } + } + std::result::Result::Ok(crate::StandardWsNotification::Signature(notification)) + }, + std::result::Result::Err(error) => std::result::Result::Err(error), + }; + }, + "slotNotification" => crate::standard_ws::adapt_notification_result( + subscription, + result, + crate::WsSlotNotification::new, + ) + .map(crate::StandardWsNotification::Slot), + "slotsUpdatesNotification" => crate::standard_ws::adapt_notification_result( + subscription, + result, + crate::WsSlotsUpdatesNotification::new, + ) + .map(crate::StandardWsNotification::SlotsUpdates), + "voteNotification" => crate::standard_ws::adapt_notification_result( + subscription, + result, + crate::WsVoteNotification::new, + ) + .map(crate::StandardWsNotification::Vote), + method => std::result::Result::Err(kb_core::Error::json(format!( + "unsupported standard WebSocket notification method '{method}'" + ))), + }; +} + +impl crate::WsAccountNotification { + fn new(subscription: u64, result: crate::RpcResponse) -> Self { + return Self { subscription, result }; + } +} + +impl crate::WsBlockNotification { + fn new(subscription: u64, result: crate::RpcResponse) -> Self { + return Self { subscription, result }; + } +} + +impl crate::WsLogsNotification { + fn new(subscription: u64, result: crate::RpcResponse) -> Self { + return Self { subscription, result }; + } +} + +impl crate::WsProgramNotification { + fn new(subscription: u64, result: crate::RpcOptionalContext) -> Self { + return Self { subscription, result }; + } +} + +impl crate::WsRootNotification { + fn new(subscription: u64, result: u64) -> Self { + return Self { subscription, result }; + } +} + +impl crate::WsSignatureNotification { + fn new(subscription: u64, result: crate::RpcResponse) -> Self { + return Self { subscription, result }; + } +} + +impl crate::WsSlotNotification { + fn new(subscription: u64, result: crate::WsSlotInfo) -> Self { + return Self { subscription, result }; + } +} + +impl crate::WsSlotsUpdatesNotification { + fn new(subscription: u64, result: crate::WsSlotUpdate) -> Self { + return Self { subscription, result }; + } +} + +impl crate::WsVoteNotification { + fn new(subscription: u64, result: crate::WsVoteValue) -> Self { + return Self { subscription, result }; + } +} + +fn endpoint_declares_method(endpoint: &kb_config::WsEndpointConfig, method: &str) -> bool { + let request_kind = crate::request_kind_from_method(method); + for role in &endpoint.roles { + if !role.enabled { + continue; + } + for configured_kind in &role.request_kinds { + if configured_kind == "*" || configured_kind == &request_kind { + return true; + } + } + } + return false; +} + +fn account_params( + request: &crate::AccountSubscribeRequest, +) -> kb_core::Result> { + let validation_result = crate::validate_solana_pubkey_text(&request.pubkey, "account pubkey"); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let mut params = std::vec![serde_json::Value::String(request.pubkey.clone())]; + if let std::option::Option::Some(config) = request.config { + let config_result = config.validate(); + if let std::result::Result::Err(error) = config_result { + return std::result::Result::Err(error); + } + let value = match crate::standard_http::serialize_parameter("accountSubscribe", &config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + params.push(value); + } + return std::result::Result::Ok(params); +} + +fn block_params( + request: &crate::BlockSubscribeRequest, +) -> kb_core::Result> { + if let crate::WsBlockFilter::MentionsAccountOrProgram(pubkey) = &request.filter { + let validation_result = + crate::validate_solana_pubkey_text(pubkey, "block mentions account or program"); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + } + let filter = match crate::standard_http::serialize_parameter("blockSubscribe", &request.filter) + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut params = std::vec![filter]; + if let std::option::Option::Some(config) = request.config { + let config_result = config.validate(); + if let std::result::Result::Err(error) = config_result { + return std::result::Result::Err(error); + } + let value = match crate::standard_http::serialize_parameter("blockSubscribe", &config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + params.push(value); + } + return std::result::Result::Ok(params); +} + +fn logs_params( + request: &crate::LogsSubscribeRequest, +) -> kb_core::Result> { + if let crate::WsLogsFilter::Mentions(pubkey) = &request.filter { + let validation_result = crate::validate_solana_pubkey_text(pubkey, "logs mention"); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + } + let filter = match crate::standard_http::serialize_parameter("logsSubscribe", &request.filter) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut params = std::vec![filter]; + if let std::option::Option::Some(config) = request.config { + let value = match crate::standard_http::serialize_parameter("logsSubscribe", &config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + params.push(value); + } + return std::result::Result::Ok(params); +} + +fn program_params( + request: &crate::ProgramSubscribeRequest, +) -> kb_core::Result> { + let validation_result = crate::validate_solana_pubkey_text(&request.program_id, "program id"); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let mut params = std::vec![serde_json::Value::String(request.program_id.clone())]; + if let std::option::Option::Some(config) = &request.config { + let config_result = config.validate(); + if let std::result::Result::Err(error) = config_result { + return std::result::Result::Err(error); + } + let value = match crate::standard_http::serialize_parameter("programSubscribe", config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + params.push(value); + } + return std::result::Result::Ok(params); +} + +fn signature_params( + request: &crate::SignatureSubscribeRequest, +) -> kb_core::Result> { + let validation_result = + crate::validate_transaction_signature_text(&request.signature, "transaction signature"); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let mut params = std::vec![serde_json::Value::String(request.signature.clone())]; + if let std::option::Option::Some(config) = request.config { + let value = match crate::standard_http::serialize_parameter("signatureSubscribe", &config) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + params.push(value); + } + return std::result::Result::Ok(params); +} + +fn adapt_notification_result( + subscription: u64, + value: &serde_json::Value, + constructor: Constructor, +) -> kb_core::Result +where + ResultType: serde::de::DeserializeOwned, + Constructor: FnOnce(u64, ResultType) -> NotificationType, +{ + let result = match serde_json::from_value::(value.clone()) { + std::result::Result::Ok(result) => result, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::json(format!( + "cannot decode standard WebSocket notification result: {error}" + ))); + }, + }; + return std::result::Result::Ok(constructor(subscription, result)); +} + +#[cfg(test)] +mod tests { + const PUBKEY: &str = "11111111111111111111111111111111"; + const SIGNATURE: &str = "1111111111111111111111111111111111111111111111111111111111111111"; + + #[test] + fn every_standard_request_maps_to_the_compiled_registry() { + let requests = [ + crate::StandardWsRequest::Account(crate::AccountSubscribeRequest { + pubkey: PUBKEY.to_string(), + config: std::option::Option::None, + }), + crate::StandardWsRequest::Block(crate::BlockSubscribeRequest { + filter: crate::WsBlockFilter::All, + config: std::option::Option::None, + }), + crate::StandardWsRequest::Logs(crate::LogsSubscribeRequest { + filter: crate::WsLogsFilter::All, + config: std::option::Option::None, + }), + crate::StandardWsRequest::Program(crate::ProgramSubscribeRequest { + program_id: PUBKEY.to_string(), + config: std::option::Option::None, + }), + crate::StandardWsRequest::Root(crate::RootSubscribeRequest), + crate::StandardWsRequest::Signature(crate::SignatureSubscribeRequest { + signature: SIGNATURE.to_string(), + config: std::option::Option::None, + }), + crate::StandardWsRequest::Slot(crate::SlotSubscribeRequest), + crate::StandardWsRequest::SlotsUpdates(crate::SlotsUpdatesSubscribeRequest), + crate::StandardWsRequest::Vote(crate::VoteSubscribeRequest), + ]; + let mut methods = std::collections::BTreeSet::new(); + for request in requests { + assert!(methods.insert(request.subscribe_method())); + assert_eq!(request.specification().subscribe_method, request.subscribe_method()); + } + assert_eq!(methods.len(), crate::STANDARD_WS_SUBSCRIPTIONS.len()); + } + + #[test] + fn stable_request_options_are_independently_serialized() { + let request = crate::StandardWsRequest::Account(crate::AccountSubscribeRequest { + pubkey: PUBKEY.to_string(), + config: std::option::Option::Some(crate::WsAccountSubscribeConfig { + commitment: std::option::Option::Some(crate::RpcCommitmentLevel::Confirmed), + encoding: std::option::Option::Some(crate::RpcAccountEncoding::Base64Zstd), + data_slice: std::option::Option::Some(crate::RpcDataSlice { + offset: 8, + length: 16, + }), + }), + }); + let params = match request.params(crate::StandardWsCapabilities::default()) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("params failed: {error}"), + }; + assert_eq!(params[0], serde_json::Value::String(PUBKEY.to_string())); + assert_eq!(params[1]["commitment"], "confirmed"); + assert_eq!(params[1]["encoding"], "base64+zstd"); + assert_eq!(params[1]["dataSlice"]["offset"], 8); + } + + #[test] + fn custom_filter_objects_reject_unknown_fields() { + let block = serde_json::json!({ + "mentionsAccountOrProgram": PUBKEY, + "unexpected": true + }); + assert!(serde_json::from_value::(block).is_err()); + let logs = serde_json::json!({ + "mentions": [PUBKEY], + "unexpected": true + }); + assert!(serde_json::from_value::(logs).is_err()); + } + + #[test] + fn logs_mentions_keeps_exact_single_key_shape() { + let request = crate::StandardWsRequest::Logs(crate::LogsSubscribeRequest { + filter: crate::WsLogsFilter::Mentions(PUBKEY.to_string()), + config: std::option::Option::Some(crate::WsLogsSubscribeConfig { + commitment: std::option::Option::Some(crate::RpcCommitmentLevel::Finalized), + }), + }); + let params = match request.params(crate::StandardWsCapabilities::default()) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => panic!("params failed: {error}"), + }; + assert_eq!(params[0]["mentions"][0], PUBKEY); + assert_eq!(params[1]["commitment"], "finalized"); + } + + #[test] + fn unstable_requests_require_explicit_capabilities() { + let requests = [ + crate::StandardWsRequest::Block(crate::BlockSubscribeRequest { + filter: crate::WsBlockFilter::All, + config: std::option::Option::None, + }), + crate::StandardWsRequest::SlotsUpdates(crate::SlotsUpdatesSubscribeRequest), + crate::StandardWsRequest::Vote(crate::VoteSubscribeRequest), + ]; + for request in &requests { + assert!(request.params(crate::StandardWsCapabilities::default()).is_err()); + } + let capabilities = crate::StandardWsCapabilities { + block_subscribe: true, + slots_updates_subscribe: true, + vote_subscribe: true, + }; + for request in &requests { + assert!(request.params(capabilities).is_ok()); + } + } + + #[test] + fn unstable_capabilities_can_be_disabled_after_runtime_rejection() { + let mut capabilities = crate::StandardWsCapabilities { + block_subscribe: true, + slots_updates_subscribe: true, + vote_subscribe: true, + }; + assert!(capabilities.disable_method("blockSubscribe")); + assert!(!capabilities.block_subscribe); + assert!(!capabilities.disable_method("blockSubscribe")); + assert!(capabilities.disable_method("slotsUpdatesSubscribe")); + assert!(capabilities.disable_method("voteSubscribe")); + assert!(!capabilities.disable_method("slotSubscribe")); + assert!(crate::StandardWsCapabilities::is_unstable_method("blockSubscribe")); + assert!(!crate::StandardWsCapabilities::is_unstable_method("slotSubscribe")); + } + + #[test] + fn block_processed_commitment_is_rejected() { + let request = crate::StandardWsRequest::Block(crate::BlockSubscribeRequest { + filter: crate::WsBlockFilter::All, + config: std::option::Option::Some(crate::WsBlockSubscribeConfig { + commitment: std::option::Option::Some(crate::RpcCommitmentLevel::Processed), + ..crate::WsBlockSubscribeConfig::default() + }), + }); + let capabilities = crate::StandardWsCapabilities { + block_subscribe: true, + slots_updates_subscribe: false, + vote_subscribe: false, + }; + assert!(request.params(capabilities).is_err()); + } + + #[test] + fn notification_adapter_decodes_each_standard_shape() { + let account = serde_json::json!({ + "context": { "slot": 1 }, + "value": { + "lamports": 1, + "owner": PUBKEY, + "executable": false, + "rentEpoch": 2, + "space": 0, + "data": ["", "base64"] + } + }); + let keyed_account = serde_json::json!({ + "context": { "slot": 1 }, + "value": { + "pubkey": PUBKEY, + "account": { + "lamports": 1, + "owner": PUBKEY, + "executable": false, + "rentEpoch": 2, + "space": 0, + "data": ["", "base64"] + } + } + }); + let cases = [ + ("accountNotification", account, "account"), + ( + "blockNotification", + serde_json::json!({ + "context": { "slot": 4 }, + "value": { + "slot": 4, + "block": { + "blockhash": PUBKEY, + "previousBlockhash": PUBKEY, + "parentSlot": 3 + }, + "err": null + } + }), + "block", + ), + ( + "logsNotification", + serde_json::json!({ + "context": { "slot": 5 }, + "value": { "signature": SIGNATURE, "err": null, "logs": ["Program log"] } + }), + "logs", + ), + ("programNotification", keyed_account, "program"), + ("rootNotification", serde_json::Value::from(42_u64), "root"), + ( + "signatureNotification", + serde_json::json!({ + "context": { "slot": 6 }, + "value": { "err": null } + }), + "signature", + ), + ( + "slotNotification", + serde_json::json!({ "slot": 3, "parent": 2, "root": 1 }), + "slot", + ), + ( + "slotsUpdatesNotification", + serde_json::json!({ "type": "completed", "slot": 3, "timestamp": 4 }), + "slotsUpdates", + ), + ( + "voteNotification", + serde_json::json!({ + "votePubkey": PUBKEY, + "slots": [1, 2], + "hash": PUBKEY, + "timestamp": null, + "signature": SIGNATURE + }), + "vote", + ), + ]; + for (method, result, kind) in cases { + let notification = crate::JsonRpcNotification { + jsonrpc: "2.0".to_string(), + method: method.to_string(), + params: crate::JsonRpcNotificationParams { result, subscription: 7 }, + }; + let adapted = match crate::adapt_standard_ws_notification(¬ification) { + std::result::Result::Ok(adapted) => adapted, + std::result::Result::Err(error) => panic!("notification failed: {error}"), + }; + let serialized = match serde_json::to_value(adapted) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("serialization failed: {error}"), + }; + assert_eq!(serialized["kind"], kind); + } + } + + #[test] + fn signature_received_marker_is_exact() { + let valid = crate::JsonRpcNotification { + jsonrpc: "2.0".to_string(), + method: "signatureNotification".to_string(), + params: crate::JsonRpcNotificationParams { + result: serde_json::json!({ + "context": { "slot": 1 }, + "value": "receivedSignature" + }), + subscription: 9, + }, + }; + assert!(crate::adapt_standard_ws_notification(&valid).is_ok()); + let mut invalid = valid; + invalid.params.result["value"] = serde_json::Value::String("received".to_string()); + assert!(crate::adapt_standard_ws_notification(&invalid).is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/validation.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/validation.rs new file mode 100644 index 0000000..8a37a70 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/validation.rs @@ -0,0 +1,77 @@ +// file: kb_rpc/src/validation.rs +// version: 2 + +//! Shared validation helpers for Solana RPC addresses, hashes and signatures. + +/// Validates one base58 Solana transaction signature. +pub fn validate_transaction_signature_text(value: &str, field_name: &str) -> kb_core::Result<()> { + return crate::validation::validate_base58_length(value, field_name, 64); +} + +/// Validates one base58 Solana public key or account address. +pub fn validate_solana_pubkey_text(value: &str, field_name: &str) -> kb_core::Result<()> { + return crate::validation::validate_base58_length(value, field_name, 32); +} + +/// Validates one base58 Solana blockhash or genesis hash. +pub fn validate_solana_hash_text(value: &str, field_name: &str) -> kb_core::Result<()> { + return crate::validation::validate_base58_length(value, field_name, 32); +} + +fn validate_base58_length( + value: &str, + field_name: &str, + expected_length: usize, +) -> kb_core::Result<()> { + if value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::config(format!( + "{field_name} must not be empty" + ))); + } + let decode_result = bs58::decode(value).into_vec(); + let decoded = match decode_result { + std::result::Result::Ok(bytes) => bytes, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::config(format!( + "{field_name} is not valid base58: {error}" + ))); + }, + }; + if decoded.len() != expected_length { + return std::result::Result::Err(kb_core::Error::config(format!( + "{field_name} must decode to {expected_length} bytes" + ))); + } + return std::result::Result::Ok(()); +} + +#[cfg(test)] +mod tests { + #[test] + fn signature_validation_accepts_sixty_four_bytes() { + let signature = bs58::encode([7_u8; 64]).into_string(); + let result = crate::validate_transaction_signature_text(&signature, "signature"); + assert!(result.is_ok()); + } + + #[test] + fn pubkey_validation_accepts_thirty_two_bytes() { + let pubkey = bs58::encode([9_u8; 32]).into_string(); + let result = crate::validate_solana_pubkey_text(&pubkey, "pubkey"); + assert!(result.is_ok()); + } + + #[test] + fn hash_validation_accepts_thirty_two_bytes() { + let hash = bs58::encode([10_u8; 32]).into_string(); + let result = crate::validate_solana_hash_text(&hash, "hash"); + assert!(result.is_ok()); + } + + #[test] + fn signature_validation_rejects_public_key_length() { + let pubkey = bs58::encode([11_u8; 32]).into_string(); + let result = crate::validate_transaction_signature_text(&pubkey, "signature"); + assert!(result.is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/ws_client.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/ws_client.rs new file mode 100644 index 0000000..5725fe5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/ws_client.rs @@ -0,0 +1,386 @@ +// file: kb_rpc/src/ws_client.rs +// version: 6 + +//! Standard Solana WebSocket client helpers. + +use futures_util::SinkExt; // rust-rules: trait-import +use futures_util::StreamExt; // rust-rules: trait-import + +/// Snapshot of one pooled WebSocket endpoint. +#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct WsPoolClientSnapshot { + /// Logical endpoint name. + pub endpoint_name: std::string::String, + /// Provider name. + pub provider: std::string::String, + /// Endpoint URL. + pub endpoint_url: std::string::String, + /// Supported roles. + pub roles: std::vec::Vec, + /// Status string. + pub status: std::string::String, +} + +/// Standard Solana WebSocket client bound to one configured endpoint. +#[derive(Clone, Debug)] +pub struct WsClient { + endpoint: kb_config::WsEndpointConfig, + next_request_id: std::sync::Arc, +} + +impl crate::WsClient { + /// Creates a new WebSocket client bound to one endpoint. + pub fn new(endpoint: kb_config::WsEndpointConfig) -> kb_core::Result { + if !endpoint.enabled { + tracing::error!(target: crate::TRACING_TARGET, action = "create_ws_client", endpoint_name = %endpoint.name, provider = %endpoint.provider, error_code = "ws_endpoint_disabled", "cannot create WebSocket client for disabled endpoint"); + return std::result::Result::Err(kb_core::Error::config(format!( + "ws endpoint '{}' is disabled", + endpoint.name + ))); + } + tracing::debug!(target: crate::TRACING_TARGET, action = "create_ws_client", endpoint_name = %endpoint.name, provider = %endpoint.provider, role_count = endpoint.roles.len(), "WebSocket client created"); + return std::result::Result::Ok(Self { + endpoint, + next_request_id: std::sync::Arc::new(std::sync::atomic::AtomicU64::new(1)), + }); + } + + /// Returns the endpoint name. + pub fn endpoint_name(&self) -> &str { + return self.endpoint.name.as_str(); + } + + /// Returns the provider name. + pub fn provider(&self) -> &str { + return self.endpoint.provider.as_str(); + } + + /// Returns the endpoint URL. + pub fn endpoint_url(&self) -> &str { + return self.endpoint.url.as_str(); + } + + /// Returns the endpoint configuration. + pub fn endpoint_config(&self) -> &kb_config::WsEndpointConfig { + return &self.endpoint; + } + + /// Returns true when this endpoint supports the required role and request kind. + pub fn can_handle(&self, required_role: &str, request_kind: &str) -> bool { + if !self.endpoint.enabled { + return false; + } + for role in &self.endpoint.roles { + if crate::endpoint_role::role_matches(role, required_role, request_kind) { + return true; + } + } + return false; + } + + /// Returns a serializable endpoint snapshot. + pub fn snapshot(&self) -> crate::WsPoolClientSnapshot { + let mut roles = std::vec::Vec::new(); + for role in &self.endpoint.roles { + roles.push(crate::EndpointRoleSnapshot::from_config(role)); + } + return crate::WsPoolClientSnapshot { + endpoint_name: self.endpoint.name.clone(), + provider: self.endpoint.provider.clone(), + endpoint_url: self.endpoint.url.clone(), + roles, + status: "idle".to_string(), + }; + } + + /// Builds a JSON-RPC request with a generated id. + pub fn build_json_rpc_request( + &self, + method: std::string::String, + params: std::vec::Vec, + ) -> crate::JsonRpcRequest { + let request_id = self.next_request_id.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + return crate::JsonRpcRequest::new_with_u64_id(request_id, method, params); + } + + /// Builds a subscribe request for one explicitly registered standard subscription. + pub fn build_standard_subscribe_request( + &self, + subscription: &crate::StandardWsSubscriptionSpec, + params: std::vec::Vec, + ) -> crate::JsonRpcRequest { + return self.build_json_rpc_request(subscription.subscribe_method.to_string(), params); + } + + /// Builds an unsubscribe request for one explicitly registered standard subscription. + pub fn build_standard_unsubscribe_request( + &self, + subscription: &crate::StandardWsSubscriptionSpec, + subscription_id: u64, + ) -> crate::JsonRpcRequest { + return self.build_json_rpc_request( + subscription.unsubscribe_method.to_string(), + std::vec![serde_json::Value::from(subscription_id)], + ); + } + + /// Connects, sends one JSON-RPC request, waits for one response and closes. + pub async fn execute_json_rpc_once( + &self, + method: std::string::String, + params: std::vec::Vec, + ) -> kb_core::Result { + let parameter_count = params.len(); + let request = self.build_json_rpc_request(method.clone(), params); + tracing::debug!(target: crate::TRACING_TARGET, action = "execute_ws_json_rpc_once", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, parameter_count, "start one-shot WebSocket JSON-RPC request"); + let request_text = match request.to_json_string() { + std::result::Result::Ok(text) => text, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "serialize_ws_json_rpc_request", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, error = %error, "WebSocket JSON-RPC request serialization failed"); + return std::result::Result::Err(error); + }, + }; + let connect_timeout = std::time::Duration::from_millis(self.endpoint.connect_timeout_ms); + let connect_future = tokio_tungstenite::connect_async(self.endpoint.url.as_str()); + let connect_timeout_result = tokio::time::timeout(connect_timeout, connect_future).await; + let connect_result = match connect_timeout_result { + std::result::Result::Ok(result) => result, + std::result::Result::Err(_) => { + tracing::error!(target: crate::TRACING_TARGET, action = "connect_ws_endpoint", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, timeout_ms = self.endpoint.connect_timeout_ms, error_code = "ws_connect_timeout", "WebSocket endpoint connection timed out"); + return std::result::Result::Err(kb_core::Error::ws(format!( + "websocket connect timed out for endpoint '{}'", + self.endpoint.name + ))); + }, + }; + let (mut stream, _response) = match connect_result { + std::result::Result::Ok(pair) => pair, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "connect_ws_endpoint", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, error = %error, "WebSocket endpoint connection failed"); + return std::result::Result::Err(kb_core::Error::ws(format!( + "cannot connect websocket endpoint '{}': {error}", + self.endpoint.name + ))); + }, + }; + let send_result = stream + .send(tokio_tungstenite::tungstenite::Message::Text(request_text.into())) + .await; + if let std::result::Result::Err(error) = send_result { + tracing::error!(target: crate::TRACING_TARGET, action = "send_ws_json_rpc_request", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, error = %error, "WebSocket JSON-RPC request send failed"); + return std::result::Result::Err(kb_core::Error::ws(format!( + "cannot send websocket request '{}' to endpoint '{}': {error}", + method, self.endpoint.name + ))); + } + let response_timeout = std::time::Duration::from_millis(self.endpoint.request_timeout_ms); + let next_timeout_result = tokio::time::timeout(response_timeout, stream.next()).await; + let next_result = match next_timeout_result { + std::result::Result::Ok(result) => result, + std::result::Result::Err(_) => { + tracing::error!(target: crate::TRACING_TARGET, action = "read_ws_json_rpc_response", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, timeout_ms = self.endpoint.request_timeout_ms, error_code = "ws_response_timeout", "WebSocket JSON-RPC response timed out"); + return std::result::Result::Err(kb_core::Error::ws(format!( + "websocket response timed out for endpoint '{}'", + self.endpoint.name + ))); + }, + }; + let message = match next_result { + std::option::Option::Some(result) => match result { + std::result::Result::Ok(message) => message, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "read_ws_json_rpc_response", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, error = %error, "WebSocket JSON-RPC response read failed"); + return std::result::Result::Err(kb_core::Error::ws(format!( + "websocket read failed for endpoint '{}': {error}", + self.endpoint.name + ))); + }, + }, + std::option::Option::None => { + tracing::error!(target: crate::TRACING_TARGET, action = "read_ws_json_rpc_response", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, error_code = "ws_closed_before_response", "WebSocket endpoint closed before response"); + return std::result::Result::Err(kb_core::Error::ws(format!( + "websocket endpoint '{}' closed before response", + self.endpoint.name + ))); + }, + }; + let close_result = stream + .send(tokio_tungstenite::tungstenite::Message::Close(std::option::Option::None)) + .await; + if let std::result::Result::Err(error) = close_result { + tracing::debug!(target: crate::TRACING_TARGET, action = "close_ws_endpoint", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, error = %error, "WebSocket close send failed"); + } + return match message { + tokio_tungstenite::tungstenite::Message::Text(text) => { + let parse_result = crate::parse_json_rpc_text(text.as_str()); + match parse_result { + std::result::Result::Ok(response) => { + if let crate::JsonRpcResponse::Error(error_response) = &response { + tracing::error!(target: crate::TRACING_TARGET, action = "execute_ws_json_rpc_once", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, response_kind = response.kind_name(), rpc_error_code = error_response.error.code, rpc_error_message = %error_response.error.message, "WebSocket JSON-RPC endpoint returned an RPC error"); + } else { + tracing::debug!(target: crate::TRACING_TARGET, action = "execute_ws_json_rpc_once", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, response_kind = response.kind_name(), "one-shot WebSocket JSON-RPC request completed"); + } + std::result::Result::Ok(response) + }, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "parse_ws_json_rpc_response", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, response_byte_length = text.len(), error = %error, "WebSocket JSON-RPC response parsing failed"); + std::result::Result::Err(error) + }, + } + }, + tokio_tungstenite::tungstenite::Message::Binary(binary) => { + tracing::error!(target: crate::TRACING_TARGET, action = "read_ws_json_rpc_response", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, message_kind = "binary", response_byte_length = binary.len(), "WebSocket endpoint returned binary data before JSON response"); + std::result::Result::Err(kb_core::Error::ws(format!( + "websocket endpoint '{}' returned binary message with {} bytes", + self.endpoint.name, + binary.len() + ))) + }, + tokio_tungstenite::tungstenite::Message::Ping(_) => { + tracing::error!(target: crate::TRACING_TARGET, action = "read_ws_json_rpc_response", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, message_kind = "ping", "WebSocket endpoint returned ping before JSON response"); + std::result::Result::Err(kb_core::Error::ws(format!( + "websocket endpoint '{}' returned ping before json response", + self.endpoint.name + ))) + }, + tokio_tungstenite::tungstenite::Message::Pong(_) => { + tracing::error!(target: crate::TRACING_TARGET, action = "read_ws_json_rpc_response", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, message_kind = "pong", "WebSocket endpoint returned pong before JSON response"); + std::result::Result::Err(kb_core::Error::ws(format!( + "websocket endpoint '{}' returned pong before json response", + self.endpoint.name + ))) + }, + tokio_tungstenite::tungstenite::Message::Close(_) => { + tracing::error!(target: crate::TRACING_TARGET, action = "read_ws_json_rpc_response", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, message_kind = "close", "WebSocket endpoint closed before JSON response"); + std::result::Result::Err(kb_core::Error::ws(format!( + "websocket endpoint '{}' closed before json response", + self.endpoint.name + ))) + }, + tokio_tungstenite::tungstenite::Message::Frame(_) => { + tracing::error!(target: crate::TRACING_TARGET, action = "read_ws_json_rpc_response", endpoint_name = %self.endpoint.name, provider = %self.endpoint.provider, request_id = ?request.id, method = %method, message_kind = "frame", "WebSocket endpoint returned raw frame before JSON response"); + std::result::Result::Err(kb_core::Error::ws(format!( + "websocket endpoint '{}' returned raw frame before json response", + self.endpoint.name + ))) + }, + }; + } +} + +#[cfg(test)] +mod tests { + fn role_config( + role: &str, + request_kinds: std::vec::Vec, + ) -> kb_config::EndpointRoleConfig { + return kb_config::EndpointRoleConfig { + role: role.to_string(), + enabled: true, + request_kinds, + priority: 1, + requests_per_second: 10, + burst_capacity: 10, + max_concurrent_requests: 4, + max_subscriptions: 16, + pause_after_rate_limit_ms: 1500, + }; + } + + fn endpoint(enabled: bool) -> kb_config::WsEndpointConfig { + return kb_config::WsEndpointConfig { + name: "ws_a".to_string(), + enabled, + provider: "test".to_string(), + cluster: "devnet".to_string(), + url: "wss://example.invalid".to_string(), + connect_timeout_ms: 100, + request_timeout_ms: 100, + unsubscribe_timeout_ms: 100, + write_channel_capacity: 8, + event_channel_capacity: 16, + auto_reconnect: false, + roles: std::vec![ + role_config("slot_notifications", std::vec!["slot_subscribe".to_string()]), + role_config("program_subscribe", std::vec!["program_subscribe".to_string()]), + role_config("ws_any", std::vec!["*".to_string()]), + ], + }; + } + + #[test] + fn new_rejects_disabled_endpoint() { + let result = crate::WsClient::new(endpoint(false)); + assert!(result.is_err()); + } + + #[test] + fn can_handle_matches_exact_role_and_kind() { + let client = match crate::WsClient::new(endpoint(true)) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("client creation failed: {error}"), + }; + assert!(client.can_handle("slot_notifications", "slot_subscribe")); + assert!(!client.can_handle("slot_notifications", "root_subscribe")); + } + + #[test] + fn can_handle_matches_wildcard_kind() { + let client = match crate::WsClient::new(endpoint(true)) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("client creation failed: {error}"), + }; + assert!(client.can_handle("ws_any", "logs_subscribe_mentions")); + } + + #[test] + fn snapshot_preserves_endpoint_metadata() { + let client = match crate::WsClient::new(endpoint(true)) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("client creation failed: {error}"), + }; + let snapshot = client.snapshot(); + assert_eq!(snapshot.endpoint_name, "ws_a"); + assert_eq!(snapshot.provider, "test"); + assert_eq!(snapshot.endpoint_url, "wss://example.invalid"); + assert_eq!(snapshot.roles.len(), 3); + } + + #[test] + fn standard_subscription_builders_preserve_exact_methods_and_subscription_id() { + let client = match crate::WsClient::new(endpoint(true)) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("client creation failed: {error}"), + }; + let subscription = match crate::standard_ws_subscription("slotSubscribe") { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("slot subscription missing"), + }; + let subscribe = client.build_standard_subscribe_request( + subscription, + std::vec![serde_json::json!({"commitment": "confirmed"})], + ); + let unsubscribe = client.build_standard_unsubscribe_request(subscription, 42); + assert_eq!(subscribe.method, "slotSubscribe"); + assert_eq!(subscribe.params.len(), 1); + assert_eq!(unsubscribe.method, "slotUnsubscribe"); + assert_eq!(unsubscribe.params, std::vec![serde_json::Value::from(42)]); + assert_eq!(subscribe.id, serde_json::Value::from(1)); + assert_eq!(unsubscribe.id, serde_json::Value::from(2)); + } + + #[test] + fn build_json_rpc_request_increments_ids() { + let client = match crate::WsClient::new(endpoint(true)) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("client creation failed: {error}"), + }; + let first = + client.build_json_rpc_request("slotSubscribe".to_string(), std::vec::Vec::new()); + let second = + client.build_json_rpc_request("rootSubscribe".to_string(), std::vec::Vec::new()); + assert_eq!(first.id, serde_json::Value::from(1)); + assert_eq!(second.id, serde_json::Value::from(2)); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/ws_pool.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/ws_pool.rs new file mode 100644 index 0000000..3e83662 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/ws_pool.rs @@ -0,0 +1,266 @@ +// file: kb_rpc/src/ws_pool.rs +// version: 5 + +//! WebSocket endpoint pool and role-based routing. + +/// Pool of standard Solana WebSocket endpoints. +#[derive(Clone, Debug)] +pub struct WsEndpointPool { + clients: std::vec::Vec, + next_index: std::sync::Arc, +} + +impl crate::WsEndpointPool { + /// Builds a pool from the active profile WebSocket endpoint list. + pub fn from_profile(profile: &kb_config::ProfileConfig) -> kb_core::Result { + let mut clients = std::vec::Vec::new(); + for endpoint in &profile.solana.ws_endpoints { + if !endpoint.enabled { + continue; + } + let client = match crate::WsClient::new(endpoint.clone()) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + clients.push(client); + } + return crate::WsEndpointPool::new(clients); + } + + /// Creates a pool from already constructed clients. + pub fn new(clients: std::vec::Vec) -> kb_core::Result { + if clients.is_empty() { + tracing::error!(target: crate::TRACING_TARGET, action = "create_ws_pool", error_code = "ws_pool_empty", "WebSocket endpoint pool has no enabled endpoint"); + return std::result::Result::Err(kb_core::Error::config( + "ws endpoint pool requires at least one enabled endpoint".to_string(), + )); + } + tracing::debug!(target: crate::TRACING_TARGET, action = "create_ws_pool", endpoint_count = clients.len(), "WebSocket endpoint pool created"); + return std::result::Result::Ok(Self { + clients, + next_index: std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0)), + }); + } + + /// Returns a serializable snapshot of every endpoint in the pool. + pub fn snapshot(&self) -> std::vec::Vec { + let mut snapshots = std::vec::Vec::new(); + for client in &self.clients { + snapshots.push(client.snapshot()); + } + return snapshots; + } + + /// Selects one endpoint for the requested role and method. + pub fn select_client_for_role_and_method( + &self, + required_role: &str, + method: &str, + ) -> kb_core::Result { + let request_kind = crate::request_kind_from_method(method); + return self.select_client_for_role_and_kind(required_role, &request_kind); + } + + /// Selects one endpoint for the requested role and request kind. + pub fn select_client_for_role_and_kind( + &self, + required_role: &str, + request_kind: &str, + ) -> kb_core::Result { + if self.clients.is_empty() { + tracing::error!(target: crate::TRACING_TARGET, action = "select_ws_endpoint", required_role, request_kind, error_code = "ws_pool_empty", "WebSocket endpoint pool has no clients"); + return std::result::Result::Err(kb_core::Error::not_connected( + "ws endpoint pool has no clients".to_string(), + )); + } + let start_index = self.next_index.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + let client_count = self.clients.len(); + let mut offset = 0_usize; + while offset < client_count { + let index = (start_index + offset) % client_count; + let client = self.clients[index].clone(); + if client.can_handle(required_role, request_kind) { + tracing::debug!(target: crate::TRACING_TARGET, action = "select_ws_endpoint", required_role, request_kind, endpoint_name = %client.endpoint_name(), provider = %client.provider(), "selected WebSocket endpoint"); + return std::result::Result::Ok(client); + } + offset += 1; + } + tracing::error!(target: crate::TRACING_TARGET, action = "select_ws_endpoint", required_role, request_kind, endpoint_count = self.clients.len(), error_code = "ws_endpoint_not_found", "no WebSocket endpoint supports requested role and kind"); + return std::result::Result::Err(kb_core::Error::config(format!( + "no ws endpoint supports role '{}' and request kind '{}'", + required_role, request_kind + ))); + } + + /// Selects one endpoint for an explicitly registered standard WebSocket subscription. + pub fn select_client_for_standard_subscription( + &self, + required_role: &str, + subscription: &crate::StandardWsSubscriptionSpec, + ) -> kb_core::Result { + return self + .select_client_for_role_and_method(required_role, subscription.subscribe_method); + } + + /// Executes one short WebSocket JSON-RPC request through the selected endpoint. + pub async fn execute_json_rpc_once_for_role( + &self, + required_role: &str, + method: std::string::String, + params: std::vec::Vec, + ) -> kb_core::Result { + let client = match self.select_client_for_role_and_method(required_role, &method) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return client.execute_json_rpc_once(method, params).await; + } +} + +#[cfg(test)] +mod tests { + fn role_config( + role: &str, + request_kinds: std::vec::Vec, + ) -> kb_config::EndpointRoleConfig { + return kb_config::EndpointRoleConfig { + role: role.to_string(), + enabled: true, + request_kinds, + priority: 1, + requests_per_second: 10, + burst_capacity: 10, + max_concurrent_requests: 4, + max_subscriptions: 16, + pause_after_rate_limit_ms: 1500, + }; + } + + fn endpoint( + name: &str, + role: &str, + request_kinds: std::vec::Vec, + ) -> kb_config::WsEndpointConfig { + return kb_config::WsEndpointConfig { + name: name.to_string(), + enabled: true, + provider: "test".to_string(), + cluster: "devnet".to_string(), + url: format!("wss://{name}.invalid"), + connect_timeout_ms: 100, + request_timeout_ms: 100, + unsubscribe_timeout_ms: 100, + write_channel_capacity: 8, + event_channel_capacity: 16, + auto_reconnect: false, + roles: std::vec![role_config(role, request_kinds)], + }; + } + + fn client(endpoint: kb_config::WsEndpointConfig) -> crate::WsClient { + match crate::WsClient::new(endpoint) { + std::result::Result::Ok(client) => return client, + std::result::Result::Err(error) => panic!("client creation failed: {error}"), + } + } + + #[test] + fn new_rejects_empty_pool() { + let result = crate::WsEndpointPool::new(std::vec::Vec::new()); + assert!(result.is_err()); + } + + #[test] + fn snapshot_lists_every_client() { + let pool = match crate::WsEndpointPool::new(std::vec![ + client(endpoint("a", "slot_notifications", std::vec!["slot_subscribe".to_string()])), + client(endpoint("b", "slot_notifications", std::vec!["slot_subscribe".to_string()])), + ]) { + std::result::Result::Ok(pool) => pool, + std::result::Result::Err(error) => panic!("pool creation failed: {error}"), + }; + let snapshot = pool.snapshot(); + assert_eq!(snapshot.len(), 2); + assert_eq!(snapshot[0].endpoint_name, "a"); + assert_eq!(snapshot[1].endpoint_name, "b"); + } + + #[test] + fn select_client_round_robins_matching_clients() { + let pool = match crate::WsEndpointPool::new(std::vec![ + client(endpoint("a", "slot_notifications", std::vec!["slot_subscribe".to_string()])), + client(endpoint("b", "slot_notifications", std::vec!["slot_subscribe".to_string()])), + ]) { + std::result::Result::Ok(pool) => pool, + std::result::Result::Err(error) => panic!("pool creation failed: {error}"), + }; + let first = + match pool.select_client_for_role_and_method("slot_notifications", "slotSubscribe") { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("selection failed: {error}"), + }; + let second = + match pool.select_client_for_role_and_method("slot_notifications", "slotSubscribe") { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("selection failed: {error}"), + }; + assert_eq!(first.endpoint_name(), "a"); + assert_eq!(second.endpoint_name(), "b"); + } + + #[test] + fn select_client_skips_unsupported_clients() { + let pool = match crate::WsEndpointPool::new(std::vec![ + client(endpoint("a", "program_subscribe", std::vec!["program_subscribe".to_string()])), + client(endpoint("b", "slot_notifications", std::vec!["slot_subscribe".to_string()])), + ]) { + std::result::Result::Ok(pool) => pool, + std::result::Result::Err(error) => panic!("pool creation failed: {error}"), + }; + let selected = + match pool.select_client_for_role_and_method("slot_notifications", "slotSubscribe") { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("selection failed: {error}"), + }; + assert_eq!(selected.endpoint_name(), "b"); + } + + #[test] + fn standard_subscription_selection_uses_the_subscribe_method() { + let pool = match crate::WsEndpointPool::new(std::vec![client(endpoint( + "a", + "slot_notifications", + std::vec!["slot_subscribe".to_string()], + ))]) { + std::result::Result::Ok(pool) => pool, + std::result::Result::Err(error) => panic!("pool creation failed: {error}"), + }; + let subscription = match crate::standard_ws_subscription("slotSubscribe") { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("slot subscription missing"), + }; + let selected = match pool + .select_client_for_standard_subscription("slot_notifications", subscription) + { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("selection failed: {error}"), + }; + assert_eq!(selected.endpoint_name(), "a"); + } + + #[test] + fn select_client_returns_error_for_missing_role() { + let pool = match crate::WsEndpointPool::new(std::vec![client(endpoint( + "a", + "slot_notifications", + std::vec!["slot_subscribe".to_string()] + )),]) + { + std::result::Result::Ok(pool) => pool, + std::result::Result::Err(error) => panic!("pool creation failed: {error}"), + }; + let selected = + pool.select_client_for_role_and_kind("program_subscribe", "program_subscribe"); + assert!(selected.is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/src/ws_session.rs b/migration/khadhroony-bot2-reference/kb_rpc/src/ws_session.rs new file mode 100644 index 0000000..3737ffc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/src/ws_session.rs @@ -0,0 +1,1699 @@ +// file: kb_rpc/src/ws_session.rs +// version: 3 + +//! Persistent multiplexed WebSocket session with bounded reconnect and resubscription. + +use futures_util::SinkExt; // rust-rules: trait-import +use futures_util::StreamExt; // rust-rules: trait-import + +/// Bounded reconnect policy for a persistent WebSocket session. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct WsReconnectPolicy { + /// Whether reconnect and resubscription are enabled. + pub enabled: bool, + /// Maximum connection attempts after one transport failure. + pub max_attempts: u32, + /// Delay before the first reconnect attempt. + pub initial_delay_ms: u64, + /// Maximum exponential-backoff delay. + pub max_delay_ms: u64, +} + +impl crate::WsReconnectPolicy { + /// Returns a policy that never reconnects. + pub const fn disabled() -> Self { + return Self { + enabled: false, + max_attempts: 0, + initial_delay_ms: 0, + max_delay_ms: 0, + }; + } + + /// Creates a bounded reconnect policy. + pub fn bounded( + max_attempts: u32, + initial_delay_ms: u64, + max_delay_ms: u64, + ) -> kb_core::Result { + if max_attempts == 0 { + return std::result::Result::Err(kb_core::Error::config( + "WebSocket reconnect max attempts must be greater than zero", + )); + } + if initial_delay_ms == 0 { + return std::result::Result::Err(kb_core::Error::config( + "WebSocket reconnect initial delay must be greater than zero", + )); + } + if max_delay_ms < initial_delay_ms { + return std::result::Result::Err(kb_core::Error::config( + "WebSocket reconnect maximum delay must not be smaller than the initial delay", + )); + } + return std::result::Result::Ok(Self { + enabled: true, + max_attempts, + initial_delay_ms, + max_delay_ms, + }); + } + + fn delay_for_attempt(&self, attempt: u32) -> std::time::Duration { + let exponent = attempt.saturating_sub(1).min(31); + let multiplier = match 1_u64.checked_shl(exponent) { + std::option::Option::Some(multiplier) => multiplier, + std::option::Option::None => u64::MAX, + }; + let delay = self.initial_delay_ms.saturating_mul(multiplier).min(self.max_delay_ms); + return std::time::Duration::from_millis(delay); + } +} + +/// Observable lifecycle state of a persistent WebSocket session. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "snake_case")] +pub enum WsSessionState { + /// Socket is connected and accepts commands. + Connected, + /// Socket failed and a bounded reconnect is running. + Reconnecting, + /// Session is permanently disconnected. + Disconnected, +} + +/// One active standard subscription in a persistent session. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct WsSubscriptionSnapshot { + /// Stable local identifier retained across reconnects. + pub local_subscription_id: u64, + /// Current server identifier, absent while resubscription is pending. + pub remote_subscription_id: std::option::Option, + /// Exact subscribe method. + pub subscribe_method: std::string::String, + /// Exact unsubscribe method. + pub unsubscribe_method: std::string::String, + /// Exact notification method. + pub notification_method: std::string::String, +} + +/// Current snapshot of one persistent WebSocket session. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct WsSessionSnapshot { + /// Endpoint name. + pub endpoint_name: std::string::String, + /// Provider name. + pub provider: std::string::String, + /// Endpoint URL. + pub endpoint_url: std::string::String, + /// Current lifecycle state. + pub state: crate::WsSessionState, + /// Number of successful reconnects since session creation. + pub reconnect_count: u32, + /// Effective standard WebSocket capabilities after runtime probing. + pub capabilities: crate::StandardWsCapabilities, + /// Active subscriptions ordered by stable local identifier. + pub subscriptions: std::vec::Vec, +} + +/// Successful subscription acknowledgement. +#[derive(Clone, Debug, PartialEq)] +pub struct WsSubscriptionAck { + /// JSON-RPC response received from the node. + pub response: crate::JsonRpcResponse, + /// Registered subscription snapshot. + pub subscription: crate::WsSubscriptionSnapshot, +} + +/// Successful explicit unsubscribe acknowledgement. +#[derive(Clone, Debug, PartialEq)] +pub struct WsUnsubscribeAck { + /// JSON-RPC response received from the node. + pub response: crate::JsonRpcResponse, + /// Removed subscription snapshot. + pub subscription: crate::WsSubscriptionSnapshot, +} + +/// Event emitted by a persistent WebSocket session. +#[derive(Clone, Debug, PartialEq)] +pub enum WsSessionEvent { + /// Initial socket connection is ready. + Connected, + /// Reconnect attempt started. + Reconnecting { + /// One-based attempt number. + attempt: u32, + /// Configured attempt limit. + maximum_attempts: u32, + }, + /// Socket reconnected and active subscriptions are being restored. + Reconnected { + /// Successful reconnect count. + reconnect_count: u32, + }, + /// New subscription was acknowledged. + SubscriptionAdded(crate::WsSubscriptionSnapshot), + /// Existing subscription received a new remote identifier after reconnect. + SubscriptionRemapped { + /// Stable local identifier. + local_subscription_id: u64, + /// Previous remote identifier. + previous_remote_subscription_id: std::option::Option, + /// New remote identifier. + remote_subscription_id: u64, + }, + /// Subscription was removed explicitly or completed by a one-shot server contract. + SubscriptionRemoved(crate::WsSubscriptionSnapshot), + /// Typed standard notification. + Notification { + /// Subscription that received the notification. + subscription: crate::WsSubscriptionSnapshot, + /// Typed notification payload. + notification: std::boxed::Box, + /// Original JSON-RPC notification envelope. + raw: crate::JsonRpcNotification, + }, + /// Protocol or transport diagnostic. + Diagnostic { + /// Stable error family or diagnostic code. + code: std::string::String, + /// Human-readable diagnostic. + message: std::string::String, + }, + /// Session ended and will not reconnect. + Disconnected, +} + +/// Persistent multiplexed standard Solana WebSocket session. +pub struct WsSession { + endpoint: kb_config::WsEndpointConfig, + capabilities: std::sync::Arc>, + control_sender: tokio::sync::mpsc::Sender, + event_sender: tokio::sync::broadcast::Sender, + snapshot: std::sync::Arc>, +} + +impl std::fmt::Debug for crate::WsSession { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + return formatter + .debug_struct("WsSession") + .field("endpoint_name", &self.endpoint.name) + .field("provider", &self.endpoint.provider) + .field("endpoint_url", &self.endpoint.url) + .finish_non_exhaustive(); + } +} + +impl crate::WsSession { + /// Connects one persistent session and starts its multiplexing task. + /// + /// Enabled unstable methods are probed by their first real subscription attempt. When the + /// node explicitly rejects one as absent or disabled, that capability is disabled for the + /// remaining lifetime of this session. + pub async fn connect( + client: crate::WsClient, + capabilities: crate::StandardWsCapabilities, + reconnect_policy: crate::WsReconnectPolicy, + ) -> kb_core::Result { + let stream = match crate::ws_session::connect_stream(&client).await { + std::result::Result::Ok(stream) => stream, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let endpoint = client.endpoint_config().clone(); + let channel_capacity = match usize::try_from(endpoint.write_channel_capacity) { + std::result::Result::Ok(capacity) if capacity > 0 => capacity, + _ => 1, + }; + let event_capacity = match usize::try_from(endpoint.event_channel_capacity) { + std::result::Result::Ok(capacity) if capacity > 0 => capacity, + _ => 1, + }; + let (control_sender, control_receiver) = tokio::sync::mpsc::channel(channel_capacity); + let (event_sender, _event_receiver) = tokio::sync::broadcast::channel(event_capacity); + let initial_capabilities = capabilities; + let capabilities = std::sync::Arc::new(tokio::sync::RwLock::new(initial_capabilities)); + let snapshot = std::sync::Arc::new(tokio::sync::RwLock::new(crate::WsSessionSnapshot { + endpoint_name: endpoint.name.clone(), + provider: endpoint.provider.clone(), + endpoint_url: endpoint.url.clone(), + state: crate::WsSessionState::Connected, + reconnect_count: 0, + capabilities: initial_capabilities, + subscriptions: std::vec::Vec::new(), + })); + let runtime = crate::ws_session::WsSessionRuntime { + client, + reconnect_policy, + capabilities: capabilities.clone(), + control_receiver, + event_sender: event_sender.clone(), + snapshot: snapshot.clone(), + active: std::collections::BTreeMap::new(), + remote_to_local: std::collections::BTreeMap::new(), + pending: std::collections::BTreeMap::new(), + next_local_subscription_id: 1, + reconnect_count: 0, + }; + tokio::spawn(async move { + runtime.run(stream).await; + }); + let _send_result = event_sender.send(crate::WsSessionEvent::Connected); + tracing::info!(target: crate::TRACING_TARGET, action = "connect_ws_session", endpoint_name = %endpoint.name, provider = %endpoint.provider, "persistent WebSocket session connected"); + return std::result::Result::Ok(Self { + endpoint, + capabilities, + control_sender, + event_sender, + snapshot, + }); + } + + /// Returns a receiver for session events. + pub fn subscribe_events(&self) -> tokio::sync::broadcast::Receiver { + return self.event_sender.subscribe(); + } + + /// Returns the current session snapshot. + pub async fn snapshot(&self) -> crate::WsSessionSnapshot { + return self.snapshot.read().await.clone(); + } + + /// Subscribes one typed standard request on the existing socket. + /// + /// For an enabled unstable method, the first request also verifies actual node support. + pub async fn subscribe( + &self, + request: crate::StandardWsRequest, + ) -> kb_core::Result { + let capabilities = *self.capabilities.read().await; + let params = match request.params(capabilities) { + std::result::Result::Ok(params) => params, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let (response_sender, response_receiver) = tokio::sync::oneshot::channel(); + let command = + crate::ws_session::WsSessionCommand::Subscribe { request, params, response_sender }; + let send_result = self.control_sender.send(command).await; + if send_result.is_err() { + return std::result::Result::Err(kb_core::Error::not_connected(format!( + "WebSocket session '{}' is not accepting subscriptions", + self.endpoint.name + ))); + } + return match response_receiver.await { + std::result::Result::Ok(result) => result, + std::result::Result::Err(error) => { + std::result::Result::Err(kb_core::Error::not_connected(format!( + "WebSocket subscription response channel closed for endpoint '{}': {error}", + self.endpoint.name + ))) + }, + }; + } + + /// Explicitly unsubscribes one current remote subscription identifier. + pub async fn unsubscribe( + &self, + remote_subscription_id: u64, + ) -> kb_core::Result { + let (response_sender, response_receiver) = tokio::sync::oneshot::channel(); + let command = crate::ws_session::WsSessionCommand::Unsubscribe { + remote_subscription_id, + response_sender, + }; + let send_result = self.control_sender.send(command).await; + if send_result.is_err() { + return std::result::Result::Err(kb_core::Error::not_connected(format!( + "WebSocket session '{}' is not accepting unsubscribe commands", + self.endpoint.name + ))); + } + return match response_receiver.await { + std::result::Result::Ok(result) => result, + std::result::Result::Err(error) => { + std::result::Result::Err(kb_core::Error::not_connected(format!( + "WebSocket unsubscribe response channel closed for endpoint '{}': {error}", + self.endpoint.name + ))) + }, + }; + } + + /// Unsubscribes active subscriptions, closes the socket and stops the session task. + pub async fn disconnect(&self) -> kb_core::Result<()> { + let (response_sender, response_receiver) = tokio::sync::oneshot::channel(); + let send_result = self + .control_sender + .send(crate::ws_session::WsSessionCommand::Disconnect { response_sender }) + .await; + if send_result.is_err() { + return std::result::Result::Ok(()); + } + return match response_receiver.await { + std::result::Result::Ok(result) => result, + std::result::Result::Err(_) => std::result::Result::Ok(()), + }; + } +} + +type WsStream = + tokio_tungstenite::WebSocketStream>; + +enum WsSessionCommand { + Subscribe { + request: crate::StandardWsRequest, + params: std::vec::Vec, + response_sender: tokio::sync::oneshot::Sender>, + }, + Unsubscribe { + remote_subscription_id: u64, + response_sender: tokio::sync::oneshot::Sender>, + }, + Disconnect { + response_sender: tokio::sync::oneshot::Sender>, + }, +} + +struct ActiveWsSubscription { + request: crate::StandardWsRequest, + params: std::vec::Vec, + snapshot: crate::WsSubscriptionSnapshot, +} + +enum PendingWsRequest { + Subscribe { + local_subscription_id: u64, + request: crate::StandardWsRequest, + params: std::vec::Vec, + deadline: tokio::time::Instant, + response_sender: tokio::sync::oneshot::Sender>, + }, + Unsubscribe { + local_subscription_id: u64, + remote_subscription_id: u64, + deadline: tokio::time::Instant, + response_sender: tokio::sync::oneshot::Sender>, + }, + Resubscribe { + local_subscription_id: u64, + previous_remote_subscription_id: std::option::Option, + deadline: tokio::time::Instant, + }, +} + +struct WsSessionRuntime { + client: crate::WsClient, + reconnect_policy: crate::WsReconnectPolicy, + capabilities: std::sync::Arc>, + control_receiver: tokio::sync::mpsc::Receiver, + event_sender: tokio::sync::broadcast::Sender, + snapshot: std::sync::Arc>, + active: std::collections::BTreeMap, + remote_to_local: std::collections::BTreeMap, + pending: std::collections::BTreeMap, + next_local_subscription_id: u64, + reconnect_count: u32, +} + +impl crate::ws_session::WsSessionRuntime { + async fn run(mut self, mut stream: crate::ws_session::WsStream) { + let mut timeout_tick = tokio::time::interval(std::time::Duration::from_millis(50)); + timeout_tick.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Skip); + loop { + tokio::select! { + command = self.control_receiver.recv() => { + let should_continue = match command { + std::option::Option::Some(command) => self.handle_command(&mut stream, command).await, + std::option::Option::None => false, + }; + if !should_continue { + break; + } + }, + message = stream.next() => { + let transport_ok = self.handle_stream_message(&mut stream, message).await; + if !transport_ok { + let reconnect = self.reconnect().await; + match reconnect { + std::option::Option::Some(reconnected_stream) => { + stream = reconnected_stream; + }, + std::option::Option::None => break, + } + } + }, + _ = timeout_tick.tick() => { + self.expire_pending().await; + }, + } + } + self.finish_disconnected().await; + } + + async fn handle_command( + &mut self, + stream: &mut crate::ws_session::WsStream, + command: crate::ws_session::WsSessionCommand, + ) -> bool { + return match command { + crate::ws_session::WsSessionCommand::Subscribe { request, params, response_sender } => { + self.send_subscribe(stream, request, params, response_sender).await; + true + }, + crate::ws_session::WsSessionCommand::Unsubscribe { + remote_subscription_id, + response_sender, + } => { + self.send_unsubscribe(stream, remote_subscription_id, response_sender).await; + true + }, + crate::ws_session::WsSessionCommand::Disconnect { response_sender } => { + let result = self.close_stream(stream).await; + let _send_result = response_sender.send(result); + false + }, + }; + } + + async fn send_subscribe( + &mut self, + stream: &mut crate::ws_session::WsStream, + request: crate::StandardWsRequest, + params: std::vec::Vec, + response_sender: tokio::sync::oneshot::Sender>, + ) { + let local_subscription_id = self.next_local_subscription_id; + self.next_local_subscription_id = self.next_local_subscription_id.saturating_add(1); + let json_request = self + .client + .build_json_rpc_request(request.subscribe_method().to_string(), params.clone()); + let request_id = match json_request.id.as_u64() { + std::option::Option::Some(request_id) => request_id, + std::option::Option::None => { + let _send_result = response_sender.send(std::result::Result::Err( + kb_core::Error::invalid_state("numeric WebSocket request id is required"), + )); + return; + }, + }; + let text = match json_request.to_json_string() { + std::result::Result::Ok(text) => text, + std::result::Result::Err(error) => { + let _send_result = response_sender.send(std::result::Result::Err(error)); + return; + }, + }; + let send_result = + stream.send(tokio_tungstenite::tungstenite::Message::Text(text.into())).await; + if let std::result::Result::Err(error) = send_result { + let _send_result = response_sender.send(std::result::Result::Err(kb_core::Error::ws( + format!("cannot send WebSocket subscribe request: {error}"), + ))); + return; + } + self.pending.insert( + request_id, + crate::ws_session::PendingWsRequest::Subscribe { + local_subscription_id, + request, + params, + deadline: tokio::time::Instant::now() + + std::time::Duration::from_millis( + self.client.endpoint_config().request_timeout_ms, + ), + response_sender, + }, + ); + } + + async fn send_unsubscribe( + &mut self, + stream: &mut crate::ws_session::WsStream, + remote_subscription_id: u64, + response_sender: tokio::sync::oneshot::Sender>, + ) { + let local_subscription_id = match self.remote_to_local.get(&remote_subscription_id) { + std::option::Option::Some(local_subscription_id) => *local_subscription_id, + std::option::Option::None => { + let _send_result = response_sender.send(std::result::Result::Err( + kb_core::Error::invalid_state(format!( + "unknown remote WebSocket subscription id {remote_subscription_id}" + )), + )); + return; + }, + }; + let subscription = match self.active.get(&local_subscription_id) { + std::option::Option::Some(subscription) => subscription, + std::option::Option::None => { + let _send_result = + response_sender.send(std::result::Result::Err(kb_core::Error::invalid_state( + format!("missing local WebSocket subscription id {local_subscription_id}"), + ))); + return; + }, + }; + let json_request = self.client.build_json_rpc_request( + subscription.snapshot.unsubscribe_method.clone(), + std::vec![serde_json::Value::from(remote_subscription_id)], + ); + let request_id = match json_request.id.as_u64() { + std::option::Option::Some(request_id) => request_id, + std::option::Option::None => { + let _send_result = response_sender.send(std::result::Result::Err( + kb_core::Error::invalid_state("numeric WebSocket request id is required"), + )); + return; + }, + }; + let text = match json_request.to_json_string() { + std::result::Result::Ok(text) => text, + std::result::Result::Err(error) => { + let _send_result = response_sender.send(std::result::Result::Err(error)); + return; + }, + }; + let send_result = + stream.send(tokio_tungstenite::tungstenite::Message::Text(text.into())).await; + if let std::result::Result::Err(error) = send_result { + let _send_result = response_sender.send(std::result::Result::Err(kb_core::Error::ws( + format!("cannot send WebSocket unsubscribe request: {error}"), + ))); + return; + } + self.pending.insert( + request_id, + crate::ws_session::PendingWsRequest::Unsubscribe { + local_subscription_id, + remote_subscription_id, + deadline: tokio::time::Instant::now() + + std::time::Duration::from_millis( + self.client.endpoint_config().unsubscribe_timeout_ms, + ), + response_sender, + }, + ); + } + + async fn handle_stream_message( + &mut self, + stream: &mut crate::ws_session::WsStream, + message: std::option::Option< + std::result::Result< + tokio_tungstenite::tungstenite::Message, + tokio_tungstenite::tungstenite::Error, + >, + >, + ) -> bool { + let message = match message { + std::option::Option::Some(std::result::Result::Ok(message)) => message, + std::option::Option::Some(std::result::Result::Err(error)) => { + self.emit_diagnostic("ws_read", format!("WebSocket read failed: {error}")); + return false; + }, + std::option::Option::None => { + self.emit_diagnostic("ws_closed", "WebSocket stream ended".to_string()); + return false; + }, + }; + return match message { + tokio_tungstenite::tungstenite::Message::Text(text) => { + self.handle_text(text.as_str()).await; + true + }, + tokio_tungstenite::tungstenite::Message::Ping(payload) => { + let pong_result = + stream.send(tokio_tungstenite::tungstenite::Message::Pong(payload)).await; + if let std::result::Result::Err(error) = pong_result { + self.emit_diagnostic("ws_pong", format!("WebSocket pong failed: {error}")); + return false; + } + true + }, + tokio_tungstenite::tungstenite::Message::Close(_) => false, + tokio_tungstenite::tungstenite::Message::Binary(_) + | tokio_tungstenite::tungstenite::Message::Pong(_) + | tokio_tungstenite::tungstenite::Message::Frame(_) => true, + }; + } + + async fn handle_text(&mut self, text: &str) { + let response = match crate::parse_json_rpc_text(text) { + std::result::Result::Ok(response) => response, + std::result::Result::Err(error) => { + self.emit_error(error); + return; + }, + }; + match response { + crate::JsonRpcResponse::Success(success) => { + self.handle_success(success).await; + }, + crate::JsonRpcResponse::Error(error_response) => { + self.handle_error_response(error_response).await; + }, + crate::JsonRpcResponse::Notification(notification) => { + self.handle_notification(notification).await; + }, + } + } + + async fn handle_success(&mut self, success: crate::JsonRpcSuccessResponse) { + let request_id = match success.id.as_u64() { + std::option::Option::Some(request_id) => request_id, + std::option::Option::None => { + self.emit_diagnostic( + "ws_response_id", + "WebSocket success response has no numeric request id".to_string(), + ); + return; + }, + }; + let pending = match self.pending.remove(&request_id) { + std::option::Option::Some(pending) => pending, + std::option::Option::None => { + self.emit_diagnostic( + "ws_unmatched_response", + format!("unmatched WebSocket response id {request_id}"), + ); + return; + }, + }; + let response = crate::JsonRpcResponse::Success(success.clone()); + match pending { + crate::ws_session::PendingWsRequest::Subscribe { + local_subscription_id, + request, + params, + deadline: _, + response_sender, + } => { + let remote_subscription_id = match success.result.as_u64() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + let _send_result = response_sender.send(std::result::Result::Err( + kb_core::Error::json("subscribe result must be a numeric id"), + )); + return; + }, + }; + let specification = request.specification(); + let snapshot = crate::WsSubscriptionSnapshot { + local_subscription_id, + remote_subscription_id: std::option::Option::Some(remote_subscription_id), + subscribe_method: specification.subscribe_method.to_string(), + unsubscribe_method: specification.unsubscribe_method.to_string(), + notification_method: specification.notification_method.to_string(), + }; + self.remote_to_local.insert(remote_subscription_id, local_subscription_id); + self.active.insert( + local_subscription_id, + crate::ws_session::ActiveWsSubscription { + request, + params, + snapshot: snapshot.clone(), + }, + ); + self.refresh_snapshot().await; + let _event_result = self + .event_sender + .send(crate::WsSessionEvent::SubscriptionAdded(snapshot.clone())); + let _send_result = + response_sender.send(std::result::Result::Ok(crate::WsSubscriptionAck { + response, + subscription: snapshot, + })); + }, + crate::ws_session::PendingWsRequest::Unsubscribe { + local_subscription_id, + remote_subscription_id, + deadline: _, + response_sender, + } => { + if success.result.as_bool() != std::option::Option::Some(true) { + let _send_result = response_sender.send(std::result::Result::Err( + kb_core::Error::json("unsubscribe result must be true"), + )); + return; + } + self.remote_to_local.remove(&remote_subscription_id); + let removed = self.active.remove(&local_subscription_id); + let snapshot = match removed { + std::option::Option::Some(subscription) => subscription.snapshot, + std::option::Option::None => { + let _send_result = response_sender.send(std::result::Result::Err( + kb_core::Error::invalid_state( + "acknowledged subscription disappeared before removal", + ), + )); + return; + }, + }; + self.refresh_snapshot().await; + let _event_result = self + .event_sender + .send(crate::WsSessionEvent::SubscriptionRemoved(snapshot.clone())); + let _send_result = + response_sender.send(std::result::Result::Ok(crate::WsUnsubscribeAck { + response, + subscription: snapshot, + })); + }, + crate::ws_session::PendingWsRequest::Resubscribe { + local_subscription_id, + previous_remote_subscription_id, + deadline: _, + } => { + let remote_subscription_id = match success.result.as_u64() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + self.emit_diagnostic( + "ws_resubscribe_result", + "resubscribe result must be a numeric id".to_string(), + ); + return; + }, + }; + let remapped = match self.active.get_mut(&local_subscription_id) { + std::option::Option::Some(active) => { + active.snapshot.remote_subscription_id = + std::option::Option::Some(remote_subscription_id); + true + }, + std::option::Option::None => false, + }; + if remapped { + self.remote_to_local.insert(remote_subscription_id, local_subscription_id); + let _event_result = + self.event_sender.send(crate::WsSessionEvent::SubscriptionRemapped { + local_subscription_id, + previous_remote_subscription_id, + remote_subscription_id, + }); + self.refresh_snapshot().await; + } + }, + } + } + + async fn handle_error_response(&mut self, error_response: crate::JsonRpcErrorResponse) { + let request_id = error_response.id.as_u64(); + let error = kb_core::Error::ws(format!( + "WebSocket JSON-RPC error {}: {}", + error_response.error.code, error_response.error.message + )); + if let std::option::Option::Some(request_id) = request_id { + if let std::option::Option::Some(pending) = self.pending.remove(&request_id) { + match pending { + crate::ws_session::PendingWsRequest::Subscribe { + request, + response_sender, + .. + } => { + self.disable_unsupported_capability(&request, &error_response).await; + let _send_result = response_sender.send(std::result::Result::Err(error)); + }, + crate::ws_session::PendingWsRequest::Unsubscribe { + response_sender, .. + } => { + let _send_result = response_sender.send(std::result::Result::Err(error)); + }, + crate::ws_session::PendingWsRequest::Resubscribe { + local_subscription_id, + .. + } => { + let request = self + .active + .get(&local_subscription_id) + .map(|active| return active.request.clone()); + let disabled = match request { + std::option::Option::Some(request) => { + self.disable_unsupported_capability(&request, &error_response).await + }, + std::option::Option::None => false, + }; + if disabled { + let removed = self.active.remove(&local_subscription_id); + if let std::option::Option::Some(removed) = removed { + self.refresh_snapshot().await; + let _event_result = self.event_sender.send( + crate::WsSessionEvent::SubscriptionRemoved(removed.snapshot), + ); + } + } else { + self.emit_error(error); + } + }, + } + return; + } + } + self.emit_error(error); + } + + async fn disable_unsupported_capability( + &mut self, + request: &crate::StandardWsRequest, + error_response: &crate::JsonRpcErrorResponse, + ) -> bool { + let method = request.subscribe_method(); + if !crate::StandardWsCapabilities::is_unstable_method(method) + || !crate::ws_session::is_method_unavailable_error(error_response) + { + return false; + } + let disabled = { + let mut capabilities = self.capabilities.write().await; + capabilities.disable_method(method) + }; + if disabled { + self.refresh_snapshot().await; + self.emit_diagnostic( + "ws_capability_disabled", + format!( + "endpoint '{}' rejected unstable method '{method}'; capability disabled for this session: JSON-RPC {} {}", + self.client.endpoint_name(), + error_response.error.code, + error_response.error.message + ), + ); + } + return true; + } + + async fn handle_notification(&mut self, notification: crate::JsonRpcNotification) { + let remote_subscription_id = notification.params.subscription; + let local_subscription_id = match self.remote_to_local.get(&remote_subscription_id) { + std::option::Option::Some(value) => *value, + std::option::Option::None => { + self.emit_diagnostic( + "ws_unknown_subscription", + format!( + "notification references unknown remote subscription id {remote_subscription_id}" + ), + ); + return; + }, + }; + let subscription = match self.active.get(&local_subscription_id) { + std::option::Option::Some(active) => active.snapshot.clone(), + std::option::Option::None => return, + }; + if notification.method != subscription.notification_method { + self.emit_diagnostic( + "ws_notification_method", + format!( + "notification method '{}' does not match expected '{}'", + notification.method, subscription.notification_method + ), + ); + return; + } + let typed = match crate::adapt_standard_ws_notification(¬ification) { + std::result::Result::Ok(typed) => typed, + std::result::Result::Err(error) => { + self.emit_error(error); + return; + }, + }; + let terminal_signature = matches!( + &typed, + crate::StandardWsNotification::Signature(crate::WsSignatureNotification { + result: crate::RpcResponse { + value: crate::WsSignatureValue::Status { .. }, + .. + }, + .. + }) + ); + let _event_result = self.event_sender.send(crate::WsSessionEvent::Notification { + subscription: subscription.clone(), + notification: std::boxed::Box::new(typed), + raw: notification, + }); + if terminal_signature { + self.remote_to_local.remove(&remote_subscription_id); + self.active.remove(&local_subscription_id); + self.refresh_snapshot().await; + let _event_result = + self.event_sender.send(crate::WsSessionEvent::SubscriptionRemoved(subscription)); + } + } + + async fn expire_pending(&mut self) { + let now = tokio::time::Instant::now(); + let mut expired = std::vec::Vec::new(); + for (request_id, pending) in &self.pending { + let deadline = match pending { + crate::ws_session::PendingWsRequest::Subscribe { deadline, .. } + | crate::ws_session::PendingWsRequest::Unsubscribe { deadline, .. } + | crate::ws_session::PendingWsRequest::Resubscribe { deadline, .. } => *deadline, + }; + if deadline <= now { + expired.push(*request_id); + } + } + return for request_id in expired { + let pending = self.pending.remove(&request_id); + if let std::option::Option::Some(pending) = pending { + let error = + kb_core::Error::ws(format!("WebSocket request id {request_id} timed out")); + match pending { + crate::ws_session::PendingWsRequest::Subscribe { response_sender, .. } => { + let _send_result = response_sender.send(std::result::Result::Err(error)); + }, + crate::ws_session::PendingWsRequest::Unsubscribe { + response_sender, .. + } => { + let _send_result = response_sender.send(std::result::Result::Err(error)); + }, + crate::ws_session::PendingWsRequest::Resubscribe { .. } => { + self.emit_error(error); + }, + } + } + }; + } + + async fn reconnect(&mut self) -> std::option::Option { + self.fail_pending(kb_core::Error::ws( + "WebSocket transport failed before request completion", + )); + if !self.reconnect_policy.enabled { + return std::option::Option::None; + } + let mut previous_remote_ids = std::collections::BTreeMap::new(); + self.remote_to_local.clear(); + for (local_subscription_id, active) in &mut self.active { + previous_remote_ids + .insert(*local_subscription_id, active.snapshot.remote_subscription_id); + active.snapshot.remote_subscription_id = std::option::Option::None; + } + self.set_state(crate::WsSessionState::Reconnecting).await; + let mut attempt = 1_u32; + while attempt <= self.reconnect_policy.max_attempts { + let _event_result = self.event_sender.send(crate::WsSessionEvent::Reconnecting { + attempt, + maximum_attempts: self.reconnect_policy.max_attempts, + }); + tokio::time::sleep(self.reconnect_policy.delay_for_attempt(attempt)).await; + let stream_result = crate::ws_session::connect_stream(&self.client).await; + match stream_result { + std::result::Result::Ok(mut stream) => { + self.reconnect_count = self.reconnect_count.saturating_add(1); + self.set_state(crate::WsSessionState::Connected).await; + let _event_result = + self.event_sender.send(crate::WsSessionEvent::Reconnected { + reconnect_count: self.reconnect_count, + }); + let restore_result = + self.restore_subscriptions(&mut stream, &previous_remote_ids).await; + if let std::result::Result::Err(error) = restore_result { + self.emit_error(error); + attempt = attempt.saturating_add(1); + continue; + } + return std::option::Option::Some(stream); + }, + std::result::Result::Err(error) => self.emit_error(error), + } + attempt = attempt.saturating_add(1); + } + return std::option::Option::None; + } + + async fn restore_subscriptions( + &mut self, + stream: &mut crate::ws_session::WsStream, + previous_remote_ids: &std::collections::BTreeMap>, + ) -> kb_core::Result<()> { + let local_ids = self.active.keys().copied().collect::>(); + for local_subscription_id in local_ids { + let (subscribe_method, params, previous_remote_subscription_id) = + match self.active.get(&local_subscription_id) { + std::option::Option::Some(active) => ( + active.request.subscribe_method().to_string(), + active.params.clone(), + previous_remote_ids.get(&local_subscription_id).copied().flatten(), + ), + std::option::Option::None => continue, + }; + let json_request = self.client.build_json_rpc_request(subscribe_method, params); + let request_id = match json_request.id.as_u64() { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::invalid_state( + "numeric WebSocket request id is required", + )); + }, + }; + let text = match json_request.to_json_string() { + std::result::Result::Ok(text) => text, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let send_result = + stream.send(tokio_tungstenite::tungstenite::Message::Text(text.into())).await; + if let std::result::Result::Err(error) = send_result { + return std::result::Result::Err(kb_core::Error::ws(format!( + "cannot restore WebSocket subscription: {error}" + ))); + } + self.pending.insert( + request_id, + crate::ws_session::PendingWsRequest::Resubscribe { + local_subscription_id, + previous_remote_subscription_id, + deadline: tokio::time::Instant::now() + + std::time::Duration::from_millis( + self.client.endpoint_config().request_timeout_ms, + ), + }, + ); + } + return std::result::Result::Ok(()); + } + + fn fail_pending(&mut self, error: kb_core::Error) { + let pending = std::mem::take(&mut self.pending); + for (_request_id, request) in pending { + match request { + crate::ws_session::PendingWsRequest::Subscribe { response_sender, .. } => { + let _send_result = + response_sender.send(std::result::Result::Err(error.clone())); + }, + crate::ws_session::PendingWsRequest::Unsubscribe { response_sender, .. } => { + let _send_result = + response_sender.send(std::result::Result::Err(error.clone())); + }, + crate::ws_session::PendingWsRequest::Resubscribe { .. } => {}, + } + } + } + + async fn close_stream( + &mut self, + stream: &mut crate::ws_session::WsStream, + ) -> kb_core::Result<()> { + let subscriptions = self + .active + .values() + .filter_map(|active| { + return active.snapshot.remote_subscription_id.map(|remote_id| { + return (active.snapshot.unsubscribe_method.clone(), remote_id); + }); + }) + .collect::>(); + for (method, remote_id) in subscriptions { + let request = self + .client + .build_json_rpc_request(method, std::vec![serde_json::Value::from(remote_id)]); + let text = match request.to_json_string() { + std::result::Result::Ok(text) => text, + std::result::Result::Err(error) => { + self.emit_error(error); + continue; + }, + }; + let send_result = + stream.send(tokio_tungstenite::tungstenite::Message::Text(text.into())).await; + if let std::result::Result::Err(error) = send_result { + self.emit_diagnostic( + "ws_disconnect_unsubscribe", + format!("cannot send disconnect unsubscribe: {error}"), + ); + break; + } + } + let close_result = stream + .send(tokio_tungstenite::tungstenite::Message::Close(std::option::Option::None)) + .await; + return match close_result { + std::result::Result::Ok(()) => std::result::Result::Ok(()), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::ws( + format!("cannot close WebSocket session: {error}"), + )), + }; + } + + async fn refresh_snapshot(&self) { + let capabilities = *self.capabilities.read().await; + let mut snapshot = self.snapshot.write().await; + snapshot.reconnect_count = self.reconnect_count; + snapshot.capabilities = capabilities; + snapshot.subscriptions = + self.active.values().map(|active| return active.snapshot.clone()).collect(); + } + + async fn set_state(&self, state: crate::WsSessionState) { + let capabilities = *self.capabilities.read().await; + let mut snapshot = self.snapshot.write().await; + snapshot.state = state; + snapshot.reconnect_count = self.reconnect_count; + snapshot.capabilities = capabilities; + snapshot.subscriptions = + self.active.values().map(|active| return active.snapshot.clone()).collect(); + } + + async fn finish_disconnected(&mut self) { + self.fail_pending(kb_core::Error::not_connected( + "persistent WebSocket session disconnected", + )); + self.set_state(crate::WsSessionState::Disconnected).await; + let _event_result = self.event_sender.send(crate::WsSessionEvent::Disconnected); + tracing::info!(target: crate::TRACING_TARGET, action = "disconnect_ws_session", endpoint_name = %self.client.endpoint_name(), provider = %self.client.provider(), "persistent WebSocket session disconnected"); + } + + fn emit_error(&self, error: kb_core::Error) { + self.emit_diagnostic(error.code(), error.message().to_string()); + } + + fn emit_diagnostic(&self, code: &str, message: std::string::String) { + tracing::warn!(target: crate::TRACING_TARGET, action = "ws_session_diagnostic", endpoint_name = %self.client.endpoint_name(), provider = %self.client.provider(), diagnostic_code = code, diagnostic = %message, "persistent WebSocket session diagnostic"); + let _event_result = self + .event_sender + .send(crate::WsSessionEvent::Diagnostic { code: code.to_string(), message }); + } +} + +fn is_method_unavailable_error(error_response: &crate::JsonRpcErrorResponse) -> bool { + if error_response.error.code == -32601 { + return true; + } + let message = error_response.error.message.to_ascii_lowercase(); + return message.contains("method not found") + || message.contains("method is not available") + || message.contains("method not available") + || message.contains("method is not enabled") + || message.contains("method not enabled") + || message.contains("unsupported method") + || message.contains("unstable method"); +} + +async fn connect_stream(client: &crate::WsClient) -> kb_core::Result { + let connect_future = tokio_tungstenite::connect_async(client.endpoint_url()); + let timeout = std::time::Duration::from_millis(client.endpoint_config().connect_timeout_ms); + let timeout_result = tokio::time::timeout(timeout, connect_future).await; + let connect_result = match timeout_result { + std::result::Result::Ok(connect_result) => connect_result, + std::result::Result::Err(_) => { + return std::result::Result::Err(kb_core::Error::ws(format!( + "WebSocket connect timed out for endpoint '{}'", + client.endpoint_name() + ))); + }, + }; + return match connect_result { + std::result::Result::Ok((stream, _response)) => std::result::Result::Ok(stream), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::ws(format!( + "cannot connect WebSocket endpoint '{}': {error}", + client.endpoint_name() + ))), + }; +} + +#[cfg(test)] +mod tests { + use futures_util::SinkExt; // rust-rules: trait-import + use futures_util::StreamExt; // rust-rules: trait-import + + fn endpoint(url: std::string::String) -> kb_config::WsEndpointConfig { + return kb_config::WsEndpointConfig { + name: "local-test".to_string(), + enabled: true, + provider: "offline".to_string(), + cluster: "localnet".to_string(), + url, + connect_timeout_ms: 1_000, + request_timeout_ms: 1_000, + unsubscribe_timeout_ms: 1_000, + write_channel_capacity: 16, + event_channel_capacity: 32, + auto_reconnect: true, + roles: std::vec![kb_config::EndpointRoleConfig { + role: "slot_notifications".to_string(), + enabled: true, + request_kinds: std::vec!["slot_subscribe".to_string()], + priority: 1, + requests_per_second: 100, + burst_capacity: 100, + max_concurrent_requests: 8, + max_subscriptions: 32, + pause_after_rate_limit_ms: 10, + }], + }; + } + + async fn next_text( + stream: &mut tokio_tungstenite::WebSocketStream, + ) -> serde_json::Value { + let message = match stream.next().await { + std::option::Option::Some(std::result::Result::Ok(message)) => message, + std::option::Option::Some(std::result::Result::Err(error)) => { + panic!("server read failed: {error}") + }, + std::option::Option::None => panic!("server stream ended"), + }; + let text = match message { + tokio_tungstenite::tungstenite::Message::Text(text) => text, + other => panic!("expected text, got {other:?}"), + }; + return match serde_json::from_str(text.as_str()) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("server json failed: {error}"), + }; + } + + async fn send_json( + stream: &mut tokio_tungstenite::WebSocketStream, + value: serde_json::Value, + ) { + let text = match serde_json::to_string(&value) { + std::result::Result::Ok(text) => text, + std::result::Result::Err(error) => panic!("server serialization failed: {error}"), + }; + if let std::result::Result::Err(error) = + stream.send(tokio_tungstenite::tungstenite::Message::Text(text.into())).await + { + panic!("server send failed: {error}"); + } + } + + #[test] + fn reconnect_policy_is_bounded_and_validated() { + assert!(crate::WsReconnectPolicy::bounded(0, 100, 1000).is_err()); + assert!(crate::WsReconnectPolicy::bounded(3, 0, 1000).is_err()); + assert!(crate::WsReconnectPolicy::bounded(3, 1000, 100).is_err()); + let policy = match crate::WsReconnectPolicy::bounded(3, 100, 250) { + std::result::Result::Ok(policy) => policy, + std::result::Result::Err(error) => panic!("policy failed: {error}"), + }; + assert_eq!(policy.delay_for_attempt(1), std::time::Duration::from_millis(100)); + assert_eq!(policy.delay_for_attempt(2), std::time::Duration::from_millis(200)); + assert_eq!(policy.delay_for_attempt(3), std::time::Duration::from_millis(250)); + } + + #[test] + fn disabled_policy_never_reconnects() { + assert_eq!( + crate::WsReconnectPolicy::disabled(), + crate::WsReconnectPolicy { + enabled: false, + max_attempts: 0, + initial_delay_ms: 0, + max_delay_ms: 0, + } + ); + } + + #[tokio::test] + async fn persistent_session_multiplexes_notification_and_explicit_unsubscribe() { + let listener = match tokio::net::TcpListener::bind("127.0.0.1:0").await { + std::result::Result::Ok(listener) => listener, + std::result::Result::Err(error) => panic!("listener failed: {error}"), + }; + let address = match listener.local_addr() { + std::result::Result::Ok(address) => address, + std::result::Result::Err(error) => panic!("local address failed: {error}"), + }; + let server = tokio::spawn(async move { + let (socket, _peer) = match listener.accept().await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("accept failed: {error}"), + }; + let mut stream = match tokio_tungstenite::accept_async(socket).await { + std::result::Result::Ok(stream) => stream, + std::result::Result::Err(error) => panic!("handshake failed: {error}"), + }; + let subscribe = next_text(&mut stream).await; + assert_eq!(subscribe["method"], "slotSubscribe"); + send_json( + &mut stream, + serde_json::json!({ "jsonrpc": "2.0", "result": 42, "id": subscribe["id"].clone() }), + ) + .await; + send_json( + &mut stream, + serde_json::json!({ + "jsonrpc": "2.0", + "method": "slotNotification", + "params": { + "result": { "slot": 8, "parent": 7, "root": 6 }, + "subscription": 42 + } + }), + ) + .await; + let unsubscribe = next_text(&mut stream).await; + assert_eq!(unsubscribe["method"], "slotUnsubscribe"); + assert_eq!(unsubscribe["params"][0], 42); + send_json( + &mut stream, + serde_json::json!({ "jsonrpc": "2.0", "result": true, "id": unsubscribe["id"].clone() }), + ) + .await; + let close = match stream.next().await { + std::option::Option::Some(std::result::Result::Ok(message)) => message, + other => panic!("close read failed: {other:?}"), + }; + assert!(matches!(close, tokio_tungstenite::tungstenite::Message::Close(_))); + }); + let client = match crate::WsClient::new(endpoint(format!("ws://{address}"))) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("client failed: {error}"), + }; + let session = match crate::WsSession::connect( + client, + crate::StandardWsCapabilities::default(), + crate::WsReconnectPolicy::disabled(), + ) + .await + { + std::result::Result::Ok(session) => session, + std::result::Result::Err(error) => panic!("session failed: {error}"), + }; + let mut events = session.subscribe_events(); + let acknowledgement = match session + .subscribe(crate::StandardWsRequest::Slot(crate::SlotSubscribeRequest)) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("subscribe failed: {error}"), + }; + assert_eq!(acknowledgement.subscription.remote_subscription_id, Some(42)); + let notification = tokio::time::timeout(std::time::Duration::from_secs(2), async { + loop { + let event = match events.recv().await { + std::result::Result::Ok(event) => event, + std::result::Result::Err(error) => panic!("event failed: {error}"), + }; + if let crate::WsSessionEvent::Notification { notification, .. } = event { + return notification; + } + } + }) + .await; + let notification = match notification { + std::result::Result::Ok(notification) => notification, + std::result::Result::Err(_) => panic!("notification timed out"), + }; + assert!(matches!(notification.as_ref(), crate::StandardWsNotification::Slot(_))); + let unsubscribe = match session.unsubscribe(42).await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unsubscribe failed: {error}"), + }; + assert_eq!(unsubscribe.subscription.remote_subscription_id, Some(42)); + assert!(session.disconnect().await.is_ok()); + if let std::result::Result::Err(error) = server.await { + panic!("server task failed: {error}"); + } + } + + #[tokio::test] + async fn unsupported_unstable_method_is_disabled_after_first_server_rejection() { + let listener = match tokio::net::TcpListener::bind("127.0.0.1:0").await { + std::result::Result::Ok(listener) => listener, + std::result::Result::Err(error) => panic!("listener failed: {error}"), + }; + let address = match listener.local_addr() { + std::result::Result::Ok(address) => address, + std::result::Result::Err(error) => panic!("local address failed: {error}"), + }; + let server = tokio::spawn(async move { + let (socket, _peer) = match listener.accept().await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("accept failed: {error}"), + }; + let mut stream = match tokio_tungstenite::accept_async(socket).await { + std::result::Result::Ok(stream) => stream, + std::result::Result::Err(error) => panic!("handshake failed: {error}"), + }; + let subscribe = next_text(&mut stream).await; + assert_eq!(subscribe["method"], "blockSubscribe"); + send_json( + &mut stream, + serde_json::json!({ + "jsonrpc": "2.0", + "error": { + "code": -32601, + "message": "Method not found" + }, + "id": subscribe["id"].clone() + }), + ) + .await; + let close = match stream.next().await { + std::option::Option::Some(std::result::Result::Ok(message)) => message, + other => panic!("close read failed: {other:?}"), + }; + assert!(matches!(close, tokio_tungstenite::tungstenite::Message::Close(_))); + }); + let client = match crate::WsClient::new(endpoint(format!("ws://{address}"))) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("client failed: {error}"), + }; + let session = match crate::WsSession::connect( + client, + crate::StandardWsCapabilities { + block_subscribe: true, + slots_updates_subscribe: false, + vote_subscribe: false, + }, + crate::WsReconnectPolicy::disabled(), + ) + .await + { + std::result::Result::Ok(session) => session, + std::result::Result::Err(error) => panic!("session failed: {error}"), + }; + let request = crate::StandardWsRequest::Block(crate::BlockSubscribeRequest { + filter: crate::WsBlockFilter::All, + config: std::option::Option::None, + }); + assert!(session.subscribe(request.clone()).await.is_err()); + let snapshot = session.snapshot().await; + assert!(!snapshot.capabilities.block_subscribe); + assert!(session.subscribe(request).await.is_err()); + assert!(session.disconnect().await.is_ok()); + if let std::result::Result::Err(error) = server.await { + panic!("server task failed: {error}"); + } + } + + #[tokio::test] + async fn terminal_signature_notification_removes_one_shot_subscription() { + let listener = match tokio::net::TcpListener::bind("127.0.0.1:0").await { + std::result::Result::Ok(listener) => listener, + std::result::Result::Err(error) => panic!("listener failed: {error}"), + }; + let address = match listener.local_addr() { + std::result::Result::Ok(address) => address, + std::result::Result::Err(error) => panic!("local address failed: {error}"), + }; + let server = tokio::spawn(async move { + let (socket, _peer) = match listener.accept().await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("accept failed: {error}"), + }; + let mut stream = match tokio_tungstenite::accept_async(socket).await { + std::result::Result::Ok(stream) => stream, + std::result::Result::Err(error) => panic!("handshake failed: {error}"), + }; + let subscribe = next_text(&mut stream).await; + assert_eq!(subscribe["method"], "signatureSubscribe"); + send_json( + &mut stream, + serde_json::json!({ + "jsonrpc": "2.0", + "result": 77, + "id": subscribe["id"].clone() + }), + ) + .await; + send_json( + &mut stream, + serde_json::json!({ + "jsonrpc": "2.0", + "method": "signatureNotification", + "params": { + "result": { + "context": { "slot": 9 }, + "value": { "err": null } + }, + "subscription": 77 + } + }), + ) + .await; + let close = match stream.next().await { + std::option::Option::Some(std::result::Result::Ok(message)) => message, + other => panic!("close read failed: {other:?}"), + }; + assert!(matches!(close, tokio_tungstenite::tungstenite::Message::Close(_))); + }); + let client = match crate::WsClient::new(endpoint(format!("ws://{address}"))) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("client failed: {error}"), + }; + let session = match crate::WsSession::connect( + client, + crate::StandardWsCapabilities::default(), + crate::WsReconnectPolicy::disabled(), + ) + .await + { + std::result::Result::Ok(session) => session, + std::result::Result::Err(error) => panic!("session failed: {error}"), + }; + let mut events = session.subscribe_events(); + let acknowledgement = match session + .subscribe(crate::StandardWsRequest::Signature(crate::SignatureSubscribeRequest { + signature: "1111111111111111111111111111111111111111111111111111111111111111" + .to_string(), + config: std::option::Option::None, + })) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("subscribe failed: {error}"), + }; + assert_eq!(acknowledgement.subscription.remote_subscription_id, Some(77)); + let removed = tokio::time::timeout(std::time::Duration::from_secs(2), async { + loop { + let event = match events.recv().await { + std::result::Result::Ok(event) => event, + std::result::Result::Err(error) => panic!("event failed: {error}"), + }; + if let crate::WsSessionEvent::SubscriptionRemoved(subscription) = event { + return subscription; + } + } + }) + .await; + let removed = match removed { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => panic!("terminal removal timed out"), + }; + assert_eq!(removed.remote_subscription_id, Some(77)); + assert!(session.snapshot().await.subscriptions.is_empty()); + assert!(session.disconnect().await.is_ok()); + if let std::result::Result::Err(error) = server.await { + panic!("server task failed: {error}"); + } + } + + #[tokio::test] + async fn reconnect_restores_subscription_with_new_remote_id() { + let listener = match tokio::net::TcpListener::bind("127.0.0.1:0").await { + std::result::Result::Ok(listener) => listener, + std::result::Result::Err(error) => panic!("listener failed: {error}"), + }; + let address = match listener.local_addr() { + std::result::Result::Ok(address) => address, + std::result::Result::Err(error) => panic!("local address failed: {error}"), + }; + let server = tokio::spawn(async move { + let (first_socket, _peer) = match listener.accept().await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("first accept failed: {error}"), + }; + let mut first = match tokio_tungstenite::accept_async(first_socket).await { + std::result::Result::Ok(stream) => stream, + std::result::Result::Err(error) => panic!("first handshake failed: {error}"), + }; + let subscribe = next_text(&mut first).await; + send_json( + &mut first, + serde_json::json!({ "jsonrpc": "2.0", "result": 10, "id": subscribe["id"].clone() }), + ) + .await; + tokio::time::sleep(std::time::Duration::from_millis(20)).await; + if let std::result::Result::Err(error) = + first.send(tokio_tungstenite::tungstenite::Message::Close(None)).await + { + panic!("first close failed: {error}"); + } + let (second_socket, _peer) = match listener.accept().await { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("second accept failed: {error}"), + }; + let mut second = match tokio_tungstenite::accept_async(second_socket).await { + std::result::Result::Ok(stream) => stream, + std::result::Result::Err(error) => panic!("second handshake failed: {error}"), + }; + let resubscribe = next_text(&mut second).await; + assert_eq!(resubscribe["method"], "slotSubscribe"); + send_json( + &mut second, + serde_json::json!({ "jsonrpc": "2.0", "result": 20, "id": resubscribe["id"].clone() }), + ) + .await; + let disconnect_unsubscribe = next_text(&mut second).await; + assert_eq!(disconnect_unsubscribe["method"], "slotUnsubscribe"); + assert_eq!(disconnect_unsubscribe["params"][0], 20); + let close = match second.next().await { + std::option::Option::Some(std::result::Result::Ok(message)) => message, + other => panic!("second close read failed: {other:?}"), + }; + assert!(matches!(close, tokio_tungstenite::tungstenite::Message::Close(_))); + }); + let client = match crate::WsClient::new(endpoint(format!("ws://{address}"))) { + std::result::Result::Ok(client) => client, + std::result::Result::Err(error) => panic!("client failed: {error}"), + }; + let policy = match crate::WsReconnectPolicy::bounded(2, 10, 20) { + std::result::Result::Ok(policy) => policy, + std::result::Result::Err(error) => panic!("policy failed: {error}"), + }; + let session = match crate::WsSession::connect( + client, + crate::StandardWsCapabilities::default(), + policy, + ) + .await + { + std::result::Result::Ok(session) => session, + std::result::Result::Err(error) => panic!("session failed: {error}"), + }; + let mut events = session.subscribe_events(); + let acknowledgement = match session + .subscribe(crate::StandardWsRequest::Slot(crate::SlotSubscribeRequest)) + .await + { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("subscribe failed: {error}"), + }; + assert_eq!(acknowledgement.subscription.remote_subscription_id, Some(10)); + let remapped = tokio::time::timeout(std::time::Duration::from_secs(3), async { + loop { + let event = match events.recv().await { + std::result::Result::Ok(event) => event, + std::result::Result::Err(error) => panic!("event failed: {error}"), + }; + if let crate::WsSessionEvent::SubscriptionRemapped { + previous_remote_subscription_id, + remote_subscription_id, + .. + } = event + { + return (previous_remote_subscription_id, remote_subscription_id); + } + } + }) + .await; + let remapped = match remapped { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => panic!("remap timed out"), + }; + assert_eq!(remapped, (Some(10), 20)); + let snapshot = session.snapshot().await; + assert_eq!(snapshot.reconnect_count, 1); + assert_eq!(snapshot.subscriptions[0].remote_subscription_id, Some(20)); + assert!(session.disconnect().await.is_ok()); + if let std::result::Result::Err(error) = server.await { + panic!("server task failed: {error}"); + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_equivalent_a.json b/migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_equivalent_a.json new file mode 100644 index 0000000..f9d1be8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_equivalent_a.json @@ -0,0 +1,39 @@ +{ + "slot": 104, + "blockTime": null, + "version": "legacy", + "transaction": { + "signatures": ["6psR5zDjAeh7HyLceXFQkWTJeRxjor8asYrG1AeUrrKVNXGE7DkvDqvVo98R9rPPQKHGm6TW3PBMSLrw2fHfxaX"], + "message": { + "header": { + "numRequiredSignatures": 1, + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 1 + }, + "accountKeys": [ + "11111111111111111111111111111111", + "ComputeBudget111111111111111111111111111111" + ], + "recentBlockhash": "11111111111111111111111111111111", + "instructions": [ + {"programIdIndex": 1, "accounts": [0], "data": "8", "stackHeight": null} + ], + "addressTableLookups": [] + } + }, + "meta": { + "err": null, + "fee": 5000, + "preBalances": [10000, 1], + "postBalances": [5000, 1], + "innerInstructions": null, + "logMessages": null, + "preTokenBalances": null, + "postTokenBalances": null, + "rewards": null, + "loadedAddresses": null, + "returnData": null, + "computeUnitsConsumed": null, + "costUnits": null + } +} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_equivalent_b.json b/migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_equivalent_b.json new file mode 100644 index 0000000..00728a1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_equivalent_b.json @@ -0,0 +1,30 @@ +{ + "meta": { + "postBalances": [5000, 1], + "fee": 5000, + "status": {"Ok": null}, + "err": null, + "preBalances": [10000, 1] + }, + "transaction": { + "message": { + "instructions": [ + {"data": "8", "accounts": [0], "programIdIndex": 1} + ], + "recentBlockhash": "11111111111111111111111111111111", + "accountKeys": [ + "11111111111111111111111111111111", + "ComputeBudget111111111111111111111111111111" + ], + "header": { + "numReadonlyUnsignedAccounts": 1, + "numReadonlySignedAccounts": 0, + "numRequiredSignatures": 1 + } + }, + "signatures": ["6psR5zDjAeh7HyLceXFQkWTJeRxjor8asYrG1AeUrrKVNXGE7DkvDqvVo98R9rPPQKHGm6TW3PBMSLrw2fHfxaX"] + }, + "blockTime": null, + "slot": 104, + "providerSpecificField": "ignored" +} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_legacy_failure.json b/migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_legacy_failure.json new file mode 100644 index 0000000..7caeb1c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_legacy_failure.json @@ -0,0 +1,37 @@ +{ + "blockTime": 1700000002, + "meta": { + "err": {"InstructionError": [0, {"Custom": 6001}]}, + "fee": 5000, + "innerInstructions": null, + "loadedAddresses": {"readonly": [], "writable": []}, + "logMessages": ["Program 11111111111111111111111111111111 failed: custom program error: 0x1771"], + "postBalances": [5000, 1], + "postTokenBalances": [], + "preBalances": [10000, 1], + "preTokenBalances": [], + "rewards": null, + "status": {"Err": {"InstructionError": [0, {"Custom": 6001}]}} + }, + "slot": 101, + "transaction": { + "message": { + "accountKeys": [ + "11111111111111111111111111111111", + "ComputeBudget111111111111111111111111111111" + ], + "addressTableLookups": [], + "header": { + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 1, + "numRequiredSignatures": 1 + }, + "instructions": [ + {"accounts": [0], "data": "4", "programIdIndex": 1} + ], + "recentBlockhash": "11111111111111111111111111111111" + }, + "signatures": ["3LJnHMv3ygbULcerbSFVsk2Jo33Qv8DoyUJQh2E9UZAGYXcv7Y7KGZ3hPw5F5j9C9tBhVcYqipRnNEugKV1rnSU"] + }, + "version": "legacy" +} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_legacy_success.json b/migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_legacy_success.json new file mode 100644 index 0000000..5c2821d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_legacy_success.json @@ -0,0 +1,80 @@ +{ + "slot": 100, + "blockTime": 1700000001, + "version": "legacy", + "transaction": { + "signatures": ["2Ana1pUpv2ZbMVkwF5FXapYeBEjdxDatLn7nvJkhgTSXbs59SyZSx866bXirPgj8QQVB57uxHJBG1YFvkRbFj4T"], + "message": { + "header": { + "numRequiredSignatures": 1, + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 2 + }, + "accountKeys": [ + "11111111111111111111111111111111", + "ComputeBudget111111111111111111111111111111", + "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" + ], + "recentBlockhash": "11111111111111111111111111111111", + "instructions": [ + { + "programIdIndex": 1, + "accounts": [0], + "data": "2", + "stackHeight": 1 + } + ], + "addressTableLookups": [] + } + }, + "meta": { + "err": null, + "status": {"Ok": null}, + "fee": 5000, + "preBalances": [10000, 1, 1], + "postBalances": [5000, 1, 1], + "innerInstructions": [ + { + "index": 0, + "instructions": [ + { + "programIdIndex": 2, + "accounts": [0], + "data": "3", + "stackHeight": 2 + } + ] + } + ], + "logMessages": [ + "Program ComputeBudget111111111111111111111111111111 invoke [1]", + "Program ComputeBudget111111111111111111111111111111 success" + ], + "preTokenBalances": [], + "postTokenBalances": [ + { + "accountIndex": 0, + "mint": "So11111111111111111111111111111111111111112", + "owner": "11111111111111111111111111111111", + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "uiTokenAmount": { + "amount": "12345678901234567890", + "decimals": 9, + "uiAmount": null, + "uiAmountString": "12345678901.23456789" + } + } + ], + "rewards": [], + "loadedAddresses": { + "writable": [], + "readonly": [] + }, + "returnData": { + "programId": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA", + "data": ["AQI=", "base64"] + }, + "computeUnitsConsumed": 1200, + "costUnits": 1300 + } +} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_optional_absent.json b/migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_optional_absent.json new file mode 100644 index 0000000..5470640 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_optional_absent.json @@ -0,0 +1,28 @@ +{ + "slot": 103, + "blockTime": null, + "transaction": { + "signatures": ["5fMCpSnW6zfEJrShJAFSTaye2dexqwVfErfeETB34kbkRriTSfD3uQxtzjn2ToyKeqakLbpcbrvq5eDBTbs4uCW"], + "message": { + "header": { + "numRequiredSignatures": 1, + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 1 + }, + "accountKeys": [ + "11111111111111111111111111111111", + "ComputeBudget111111111111111111111111111111" + ], + "recentBlockhash": "11111111111111111111111111111111", + "instructions": [ + {"programIdIndex": 1, "accounts": [0], "data": "7"} + ] + } + }, + "meta": { + "err": null, + "fee": 5000, + "preBalances": [10000, 1], + "postBalances": [5000, 1] + } +} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_v0_alt_cpi_token_2022.json b/migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_v0_alt_cpi_token_2022.json new file mode 100644 index 0000000..10b0aff --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_rpc/tests/fixtures/get_transaction_v0_alt_cpi_token_2022.json @@ -0,0 +1,88 @@ +{ + "slot": 102, + "blockTime": 1700000003, + "version": 0, + "transaction": { + "signatures": ["4VpzYuMH3LdMKjYmwoFUAfVyQqMBt2rjcAV2TjhbGet1VCAgn6fBaz1JCLRdmmZFuMtDv7BjALgJiwZRtYSTqpV"], + "message": { + "header": { + "numRequiredSignatures": 1, + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 2 + }, + "accountKeys": [ + "11111111111111111111111111111111", + "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "ComputeBudget111111111111111111111111111111" + ], + "recentBlockhash": "11111111111111111111111111111111", + "instructions": [ + { + "programIdIndex": 3, + "accounts": [0, 4], + "data": "5", + "stackHeight": 1 + } + ], + "addressTableLookups": [ + { + "accountKey": "AddressLookupTab1e1111111111111111111111111", + "writableIndexes": [0], + "readonlyIndexes": [1] + } + ] + } + }, + "meta": { + "err": null, + "fee": 7000, + "preBalances": [100000, 1, 1, 1, 1], + "postBalances": [93000, 1, 1, 1, 1], + "innerInstructions": [ + { + "index": 0, + "instructions": [ + { + "programIdIndex": 4, + "accounts": [0, 1], + "data": "6", + "stackHeight": 2 + } + ] + } + ], + "logMessages": ["Program log: Token-2022 CPI"], + "preTokenBalances": [ + { + "accountIndex": 1, + "mint": "So11111111111111111111111111111111111111112", + "owner": "11111111111111111111111111111111", + "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "uiTokenAmount": { + "amount": "10", + "decimals": 0, + "uiAmountString": "10" + } + } + ], + "postTokenBalances": [ + { + "accountIndex": 1, + "mint": "So11111111111111111111111111111111111111112", + "owner": "11111111111111111111111111111111", + "programId": "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", + "uiTokenAmount": { + "amount": "9", + "decimals": 0, + "uiAmountString": "9" + } + } + ], + "rewards": [], + "loadedAddresses": { + "writable": ["SysvarRent111111111111111111111111111111111"], + "readonly": ["SysvarC1ock11111111111111111111111111111111"] + }, + "computeUnitsConsumed": 9999 + } +} \ No newline at end of file diff --git a/migration/khadhroony-bot2-reference/kb_store_core/Cargo.toml b/migration/khadhroony-bot2-reference/kb_store_core/Cargo.toml new file mode 100644 index 0000000..12e306c --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/Cargo.toml @@ -0,0 +1,21 @@ +# file: kb_store_core/Cargo.toml +# version: 2 + +[package] +name = "kb_store_core" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +async-trait.workspace = true +chrono.workspace = true +kb_core = { path = "../kb_core" } +kb_model = { path = "../kb_model" } +serde.workspace = true +serde_json.workspace = true + +[lints] +workspace = true + diff --git a/migration/khadhroony-bot2-reference/kb_store_core/README.md b/migration/khadhroony-bot2-reference/kb_store_core/README.md new file mode 100644 index 0000000..9164430 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/README.md @@ -0,0 +1,222 @@ + + + +# kb_store_core + +`kb_store_core` déclare les contrats de stockage indépendants du backend concret. + +Ce crate ne dépend pas de PostgreSQL, SQLite, Tauri ou du RPC. Il sert de frontière commune entre le pipeline, les applications, les workers et les implémentations de stockage. + +## Rôle exact + +`kb_store_core` contient : + +- les traits repository backend-agnostiques ; +- les DTOs applicatifs ou repository ; +- les entities proches des lignes SQL, sans dépendance vers PostgreSQL ; +- les types communs de pagination, tri et limites ; +- les contrats de healthcheck et de statut migrations ; +- les helpers d'erreur storage basés sur `kb_core::Error` et `kb_core::Result`. + +`kb_store_core` ne contient pas : + +- de pool PostgreSQL ; +- de SQL ; +- de migrations ; +- de logique Tauri ; +- de client RPC ; +- de dépendance vers `kb_store_pg`. + +## Layout obligatoire + +```text +src/ + lib.rs + pagination.rs + health.rs + db_error.rs + dtos.rs + entities.rs + repositories.rs + dtos/ + entities/ + repositories/ +``` + +Les fichiers `dtos.rs`, `entities.rs` et `repositories.rs` servent uniquement de façade de modules. Ils évitent `mod.rs` et conservent les sous-dossiers spécialisés. + +## Conventions de dossiers + +| Dossier | Rôle | +|-----------------|--------------------------------------------| +| `entities/` | Représentations proches des lignes SQL. | +| `dtos/` | Contrats applicatifs, Tauri ou repository. | +| `repositories/` | Traits repository backend-agnostiques. | + +Les structures Rust ne doivent pas être placées dans des modules de requêtes SQL. + +## Contrats actifs `0.3.4` + +Les premiers contrats stabilisés sont volontairement minimaux : + +| Type | Rôle | +|----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------| +| `RawTransactionInsert` | Entrée d’écriture pour une transaction canonique dans `kb_sol_raw_transactions`; `from_canonical` calcule le JSON et le hash déterministes. | +| `TransactionObservationInsert` | Entrée d’écriture légère pour `kb_sol_obs_transaction_observations`, sans payload source complet. | +| `TransactionObservationOrigin` | Origine `Live`, `Backfill`, `Replay`, `Repair` ou `Migration`. | +| `TransactionObservationStatus` | État technique de détection, réception, normalisation, persistance, échec ou absence temporaire. | +| `CoreTransactionInsert` | Transaction normalisée liée à la ligne canonical raw. | +| `CoreAccountKeyInsert` | Compte résolu statique ou ALT avec flags signer/writable. | +| `CoreInstructionInsert` | Instruction top-level résolue avec chemin et hash de payload. | +| `CoreInnerInstructionInsert` | Instruction CPI résolue avec parent, chemin et hash de payload. | +| `CoreLogInsert` | Log ordonné avec rattachement prudent et hash de texte. | +| `CoreBalanceChangeInsert` | Delta SOL ou token exact, déterministe et lié au compte résolu. | +| `CoreInstructionReplayInput` | Instruction avec contexte extrait, dont les instructions outer ordonnées depuis le contrat `2`, pour les décodeurs. | +| `CoreInstructionReplayFilter` | Filtre de sélection des instructions à traiter ou rejouer. | +| `CoreInstructionLifecycleMark` | Marquage d'état pour une instruction normalisée. | +| `CoreInstructionProcessingState` | État opérationnel d'une instruction pour replay partiel. | +| `DecodedEventInsert` | Entrée d'écriture future pour `kb_sol_decode_decoded_events`. | +| `MaterializedEventInsert` | Entrée d'écriture future pour `kb_sol_mat_*`. | +| `ProcessingLedgerMark` | Contrat de marquage pour `kb_sol_ops_processing_ledger`. | +| `CoreExtractionSelectionFilter` | Sélection bornée par signatures, slots, état raw ou programme déjà indexé. | +| `ProcessingLedgerIdentity` | Identité stable stage/processor/version/input/hash. | +| `CoreExtractionBundle` | Graphe complet à persister atomiquement pour une signature. | +| `CoreExtractionFailure` | Échec rejouable avec code et message explicites. | +| `RawPayloadLifecycleMark` | Marquage d'état de rétention et de traitement raw. | +| `InsertOutcome` | Résultat commun d'insert, upsert ou skip. | +| `PageRequest` | Contrat de pagination borné. | +| `SortDirection` | Contrat de tri générique. | +| `StoreBackendDescriptor` | Diagnostic backend sans exposer le DSN complet. | +| `StoreMigrationSnapshot` | Diagnostic de version migrations sans imposer une stratégie SQL. | + +## Traits repository + +Les traits publics de `0.2.1` sont async et sans implémentation SQL : + +```text +StoreHealthStore +RawTransactionStore +CoreTransactionStore +CoreExtractionStore +ProgramObservationStore +DecodedEventStore +MaterializedEventStore +ProcessingLedgerStore +``` + +Ils définissent les frontières utilisées par `kb_store_pg`, `kb_store_sqlite`, le pipeline et les futures fenêtres de diagnostic. + +## Convention `Entity` + +Une `Entity` représente une ligne logique stockée. Elle reste proche de la DB, mais ne doit pas imposer PostgreSQL dans `kb_store_core`. + +Exemples actuels : + +```text +RawTransactionRow +TransactionObservationRow +CoreTransactionRow +CoreAccountKeyRow +CoreInstructionRow +CoreInnerInstructionRow +CoreLogRow +CoreBalanceChangeRow +DecodedEventRow +MaterializedEventRow +ProcessingLedgerRow +``` + +## Convention `Dto` + +Un `Dto` représente un contrat d'entrée, de sortie ou de diagnostic. + +Exemples actuels : + +```text +RawTransactionInsert +TransactionObservationInsert +CoreTransactionInsert +CoreAccountKeyInsert +CoreInstructionInsert +CoreInnerInstructionInsert +CoreLogInsert +CoreBalanceChangeInsert +CoreInstructionReplayInput +StoreBackendDescriptor +StoreMigrationSnapshot +``` + +## Tables Solana référencées + +Les traits et DTOs doivent référencer les tables Solana par convention logique, mais sans SQL concret. Le nom physique PostgreSQL suit : + +```text +kb_sol__ +``` + +Exemples : + +```text +kb_sol_raw_transactions +kb_sol_obs_transaction_observations +kb_sol_core_transactions +kb_sol_core_account_keys +kb_sol_core_instructions +kb_sol_core_inner_instructions +kb_sol_core_logs +kb_sol_core_balance_changes +kb_sol_obs_program_observations +kb_sol_ops_processing_ledger +``` + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs`. +- Les erreurs passent par `kb_core::Error` et `kb_core::Result`. +- Le crate doit rester testable offline. + +## Replay instruction-level + +Le replay opérationnel doit pouvoir cibler `CoreInstructionRow`, pas seulement `CoreTransactionRow`. Cela permet à un décodeur de demander uniquement les instructions `Pending`, `Failed` ou `ReplayRequested`, filtrées par `program_id` et plage de slots. + +Le décodage réel doit ensuite recevoir `CoreInstructionReplayInput`, c'est-à-dire une instruction avec son contexte extrait : comptes résolus, inner instructions, logs, balances et erreur de transaction. Cette décision prépare les futures tables `kb_sol_core_instructions`, `kb_sol_core_logs`, `kb_sol_core_balance_changes` et `kb_sol_ops_processing_ledger` sans figer encore leur SQL exact. + + +## Extension `0.2.4` + +`0.2.4` rend les contrats core effectivement utilisés par `kb_store_pg`. Le replay reste planifié depuis `CoreInstructionRow`, mais les décodeurs reçoivent `CoreInstructionReplayInput` avec account keys, inner instructions, logs et balance changes. + +Le champ `balance_change_index` est ajouté au contrat `CoreBalanceChangeInsert` afin de dédupliquer les balance changes par ordre d'extraction dans une transaction. + +## Migration corrective `0.3.1` + +Les noms historiques `RawRpcTransaction*`, `RawWsNotification*`, `kb_sol_raw_rpc_transactions` et `kb_sol_raw_ws_notifications` restent uniquement dans les migrations et la documentation historique `0.2.x`. + +Le contrat actif utilise : + +```text +RawTransaction* +TransactionObservation* +kb_sol_raw_transactions +kb_sol_obs_transaction_observations +``` + +La transaction canonique contient le document rejouable source-indépendant et sa version. L’observation conserve uniquement la provenance, la méthode, les timestamps, les tailles, les hashes et les statuts techniques. + +## Contrat atomique `0.3.4` + +`CoreExtractionStore` sépare la logique de transformation de l’implémentation SQL. Le pipeline peut sélectionner les lignes raw, vérifier le ledger, persister un `CoreExtractionBundle` ou enregistrer un `CoreExtractionFailure` sans dépendre de PostgreSQL. + +## Contrats decode et matérialisation `0.4.0` + +`DecodePipelineStore` regroupe les opérations backend-agnostiques de sélection, skip, couverture, persistance atomique decode, échec et matérialisation. Les DTOs `DecodePersistenceBundle` et `MaterializationPersistenceBundle` imposent la cohérence entre processor, version, input key/hash, signature, instruction path, sorties et ledger. + +La couverture machine-readable est portée par `DecodeCoverageDeclarationInsert`, `DecodeCoverageObservationInsert` et `DecodeCoverageSummaryRow`. + +## Contrat contextualisé `2` + +Depuis `0.4.1-pre.014`, `CoreInstructionReplayInput` transporte `outer_instructions_json`, obligatoirement un tableau JSON. Chaque entrée représente une instruction outer avec `instructionIndex`, `instructionPath`, `programId`, `payloadJson` et `payloadHash`. La liste inclut l’instruction cible et conserve l’ordre numérique du message. Ce champ participe à la sérialisation déterministe et donc au hash de replay. + +Ce changement est purement contractuel : il ne requiert aucune migration SQL et ne modifie pas les garanties d’idempotence ou de rollback des stores. diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/db_error.rs b/migration/khadhroony-bot2-reference/kb_store_core/src/db_error.rs new file mode 100644 index 0000000..cbfff2b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/db_error.rs @@ -0,0 +1,27 @@ +// file: kb_store_core/src/db_error.rs +// version: 2 + +//! Backend-neutral storage error helpers. + +/// Creates a storage contract error with a stable code. +pub fn storage_contract_error(code: &str, message: &str) -> kb_core::Error { + if code.trim().is_empty() { + return kb_core::Error::db(message); + } + return kb_core::Error::new(code, message); +} + +#[cfg(test)] +mod tests { + #[test] + fn storage_error_preserves_non_empty_code() { + let error = crate::storage_contract_error("store_contract", "invalid value"); + assert_eq!(error.code(), "store_contract"); + } + + #[test] + fn storage_error_falls_back_to_db_for_empty_code() { + let error = crate::storage_contract_error(" ", "invalid value"); + assert_eq!(error.code(), "db"); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/dtos.rs b/migration/khadhroony-bot2-reference/kb_store_core/src/dtos.rs new file mode 100644 index 0000000..d5650a6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/dtos.rs @@ -0,0 +1,103 @@ +// file: kb_store_core/src/dtos.rs +// version: 11 + +//! Backend-neutral DTO exports for storage repository contracts. + +mod core_dtos; +mod core_extraction_dtos; +mod decode_dtos; +mod event_dtos; +mod ledger_dtos; +mod raw_dtos; +mod store_dtos; + +/// Current normalized core replay input contract version. +pub use crate::dtos::core_dtos::CORE_REPLAY_INPUT_CONTRACT_VERSION; +/// Core account key insert contract. +pub use crate::dtos::core_dtos::CoreAccountKeyInsert; +/// Core account key source category. +pub use crate::dtos::core_dtos::CoreAccountKeySource; +/// Core balance change insert contract. +pub use crate::dtos::core_dtos::CoreBalanceChangeInsert; +/// Core balance change kind. +pub use crate::dtos::core_dtos::CoreBalanceChangeKind; +/// Core inner instruction insert contract. +pub use crate::dtos::core_dtos::CoreInnerInstructionInsert; +/// Core instruction insert contract. +pub use crate::dtos::core_dtos::CoreInstructionInsert; +/// Core instruction lifecycle mark request. +pub use crate::dtos::core_dtos::CoreInstructionLifecycleMark; +/// Core instruction processing state. +pub use crate::dtos::core_dtos::CoreInstructionProcessingState; +/// Core instruction replay filter contract. +pub use crate::dtos::core_dtos::CoreInstructionReplayFilter; +/// Decoder replay input containing one instruction plus extracted transaction context. +pub use crate::dtos::core_dtos::CoreInstructionReplayInput; +/// Core log insert contract. +pub use crate::dtos::core_dtos::CoreLogInsert; +/// Core transaction insert contract. +pub use crate::dtos::core_dtos::CoreTransactionInsert; +/// Complete normalized core extraction write bundle. +pub use crate::dtos::core_extraction_dtos::CoreExtractionBundle; +/// Failure details persisted for one canonical to core extraction attempt. +pub use crate::dtos::core_extraction_dtos::CoreExtractionFailure; +/// Bounded canonical transaction selection filter for core extraction. +pub use crate::dtos::core_extraction_dtos::CoreExtractionSelectionFilter; +/// Stable processing ledger identity. +pub use crate::dtos::core_extraction_dtos::ProcessingLedgerIdentity; +/// Stable processing ledger status. +pub use crate::dtos::core_extraction_dtos::ProcessingLedgerStatus; +/// One machine-readable decoder coverage declaration row. +pub use crate::dtos::decode_dtos::DecodeCoverageDeclarationInsert; +/// One observed coverage classification row owned by one decode attempt. +pub use crate::dtos::decode_dtos::DecodeCoverageObservationInsert; +/// One row of aggregated decoder coverage diagnostics. +pub use crate::dtos::decode_dtos::DecodeCoverageSummaryRow; +/// Failed decode attempt persisted in the common ledger. +pub use crate::dtos::decode_dtos::DecodeFailure; +/// One processor-owned decoded observation row. +pub use crate::dtos::decode_dtos::DecodeObservationInsert; +/// Atomic persistence bundle for one decoder and one contextual input. +pub use crate::dtos::decode_dtos::DecodePersistenceBundle; +/// Bounded contextual instruction selection filter for decode campaigns. +pub use crate::dtos::decode_dtos::DecodeSelectionFilter; +/// Maximum number of materialized rows returned by one bounded query. +pub use crate::dtos::decode_dtos::MAX_MATERIALIZED_EVENT_QUERY_ROWS; +/// Atomic persistence bundle for one materializer and one decoded observation. +pub use crate::dtos::decode_dtos::MaterializationPersistenceBundle; +/// Bounded read-only materialized event selection. +pub use crate::dtos::decode_dtos::MaterializedEventFilter; +/// One materialized output returned by a bounded query. +pub use crate::dtos::decode_dtos::MaterializedEventQueryRow; +/// One processor-owned materialized output row. +pub use crate::dtos::decode_dtos::MaterializedOutputInsert; +/// Decoded event insert contract. +pub use crate::dtos::event_dtos::DecodedEventInsert; +/// Insert or upsert result contract returned by repositories. +pub use crate::dtos::event_dtos::InsertOutcome; +/// Materialized event insert contract. +pub use crate::dtos::event_dtos::MaterializedEventInsert; +/// Processing ledger mark request contract. +pub use crate::dtos::ledger_dtos::ProcessingLedgerMark; +/// Raw payload lifecycle mark request. +pub use crate::dtos::raw_dtos::RawPayloadLifecycleMark; +/// Raw payload processing state. +pub use crate::dtos::raw_dtos::RawPayloadProcessingState; +/// Raw payload retention state. +pub use crate::dtos::raw_dtos::RawPayloadRetentionState; +/// Canonical raw Solana transaction insert contract. +pub use crate::dtos::raw_dtos::RawTransactionInsert; +/// Transaction acquisition observation insert contract. +pub use crate::dtos::raw_dtos::TransactionObservationInsert; +/// Transaction acquisition observation origin. +pub use crate::dtos::raw_dtos::TransactionObservationOrigin; +/// Transaction acquisition observation status. +pub use crate::dtos::raw_dtos::TransactionObservationStatus; +/// Store backend diagnostic contract. +pub use crate::dtos::store_dtos::StoreBackendDescriptor; +/// Store backend kind contract. +pub use crate::dtos::store_dtos::StoreBackendKind; +/// Store migration diagnostic snapshot contract. +pub use crate::dtos::store_dtos::StoreMigrationSnapshot; +/// Store migration status contract. +pub use crate::dtos::store_dtos::StoreMigrationStatus; diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/README.md b/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/README.md new file mode 100644 index 0000000..5c4efab --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/README.md @@ -0,0 +1,25 @@ + + + +# DTOs + +Ce dossier contient les contrats applicatifs et repository indépendants du backend. + +## Règles + +- Un DTO représente une entrée, une sortie ou un diagnostic. +- Un DTO ne représente pas directement une ligne SQL complète. +- Un DTO peut valider les contraintes minimales communes : signature non vide, program id non vide, limite de pagination, identifiant de module non vide. +- Un DTO ne doit pas dépendre de `sqlx`, PostgreSQL, SQLite ou Tauri. +- Le core store sépare explicitement l'unité de replay (`CoreInstructionInsert`) du contexte d'arbre (`CoreInnerInstructionInsert`, logs, balances et account keys). + +## Fichiers actuels + +| Fichier | Rôle | +|------------------|---------------------------------------------------------------------------------------------------------------------------| +| `raw_dtos.rs` | Contrats raw RPC, raw WebSocket, déduplication et cycle de vie raw. | +| `core_dtos.rs` | Contrats core transaction, account keys, instructions, inner instructions, logs, balance changes et replay contextualisé. | +| `event_dtos.rs` | Contrats decode/materialization et résultat d'écriture. | +| `ledger_dtos.rs` | Contrat du ledger de traitement. | +| `store_dtos.rs` | Diagnostics backend et migrations. | + diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/core_dtos.rs b/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/core_dtos.rs new file mode 100644 index 0000000..f1934d9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/core_dtos.rs @@ -0,0 +1,1023 @@ +// file: kb_store_core/src/dtos/core_dtos.rs +// version: 9 + +//! Core Solana storage DTOs. + +/// Current normalized core replay input contract version. +pub const CORE_REPLAY_INPUT_CONTRACT_VERSION: u32 = 2; + +/// Processing state for one normalized core instruction. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum CoreInstructionProcessingState { + /// Instruction is available for replay or first processing. + Pending, + /// Instruction was decoded by at least one decoder version. + Decoded, + /// Instruction produced materialized outputs. + Materialized, + /// Instruction is intentionally skipped for the current pipeline policy. + Ignored, + /// Instruction processing failed and requires diagnostics. + Failed, + /// Instruction must be replayed even if a previous processor marked it. + ReplayRequested, +} + +/// Source category for one normalized Solana account key. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum CoreAccountKeySource { + /// Account key came from the static transaction message account keys. + Static, + /// Account key came from loaded writable address table entries. + LoadedWritable, + /// Account key came from loaded readonly address table entries. + LoadedReadonly, +} + +/// Balance change family extracted from Solana transaction metadata. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum CoreBalanceChangeKind { + /// Native lamports balance change. + NativeLamports, + /// SPL token balance change. + TokenAmount, +} + +/// Core transaction insert contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoreTransactionInsert { + /// Transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Transaction slot in Solana unsigned representation. + pub slot: u64, + /// Whether the transaction failed on-chain. + pub failed: bool, + /// Optional raw error JSON extracted from transaction metadata. + pub err_json: std::option::Option, + /// Optional canonical raw transaction row id used for lineage when available. + pub raw_transaction_id: std::option::Option, +} + +impl CoreTransactionInsert { + /// Builds a core transaction insert contract after minimal validation. + pub fn new( + signature: impl std::convert::Into, + slot: u64, + failed: bool, + err_json: std::option::Option, + ) -> kb_core::Result { + let signature_value = signature.into(); + let signature_result = validate_required_text( + &signature_value, + "core transaction signature must not be empty", + ); + if let std::result::Result::Err(error) = signature_result { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(Self { + signature: signature_value, + slot, + failed, + err_json, + raw_transaction_id: std::option::Option::None, + }); + } + + /// Adds canonical raw transaction lineage to an already validated core transaction insert. + pub fn with_raw_transaction_id(mut self, raw_transaction_id: i64) -> Self { + self.raw_transaction_id = std::option::Option::Some(raw_transaction_id); + return self; + } +} + +/// Core account key insert contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoreAccountKeyInsert { + /// Transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Transaction slot in Solana unsigned representation. + pub slot: u64, + /// Stable account index after static and loaded keys are resolved. + pub account_index: u32, + /// Account public key as non-empty base58 text. + pub account_key: std::string::String, + /// Source category for this account key. + pub source: CoreAccountKeySource, + /// Whether the resolved account is writable for the transaction. + pub writable: bool, + /// Whether the resolved account signed the transaction. + pub signer: bool, + /// Whether the resolved account is executable when known. + pub executable: std::option::Option, +} + +impl CoreAccountKeyInsert { + /// Builds a core account key insert contract after minimal validation. + #[allow(clippy::too_many_arguments)] + pub fn new( + signature: impl std::convert::Into, + slot: u64, + account_index: u32, + account_key: impl std::convert::Into, + source: CoreAccountKeySource, + writable: bool, + signer: bool, + executable: std::option::Option, + ) -> kb_core::Result { + let signature_value = signature.into(); + let account_key_value = account_key.into(); + let signature_result = validate_required_text( + &signature_value, + "core account key signature must not be empty", + ); + if let std::result::Result::Err(error) = signature_result { + return std::result::Result::Err(error); + } + let account_key_result = + validate_required_text(&account_key_value, "core account key must not be empty"); + if let std::result::Result::Err(error) = account_key_result { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(Self { + signature: signature_value, + slot, + account_index, + account_key: account_key_value, + source, + writable, + signer, + executable, + }); + } +} + +/// Core instruction insert contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoreInstructionInsert { + /// Transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Transaction slot in Solana unsigned representation. + pub slot: u64, + /// Stable instruction path, for example `0` or `2/1`. + pub instruction_path: std::string::String, + /// Program id as non-empty base58 text. + pub program_id: std::string::String, + /// Instruction accounts as JSON, preserving unresolved forms when needed. + pub accounts_json: serde_json::Value, + /// Instruction payload JSON, preserving raw and partially decoded forms when needed. + pub payload_json: serde_json::Value, + /// Optional deterministic payload JSON hash. + pub payload_json_hash: std::option::Option, + /// Initial processing state used by replay schedulers. + pub processing_state: CoreInstructionProcessingState, +} + +impl CoreInstructionInsert { + /// Builds a core instruction insert contract after minimal validation. + pub fn new( + signature: impl std::convert::Into, + slot: u64, + instruction_path: impl std::convert::Into, + program_id: impl std::convert::Into, + accounts_json: serde_json::Value, + payload_json: serde_json::Value, + ) -> kb_core::Result { + let signature_value = signature.into(); + let instruction_path_value = instruction_path.into(); + let program_id_value = program_id.into(); + let validation_result = validate_instruction_identity( + signature_value.as_str(), + instruction_path_value.as_str(), + program_id_value.as_str(), + "core instruction", + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(Self { + signature: signature_value, + slot, + instruction_path: instruction_path_value, + program_id: program_id_value, + accounts_json, + payload_json, + payload_json_hash: std::option::Option::None, + processing_state: CoreInstructionProcessingState::Pending, + }); + } + + /// Adds a deterministic payload JSON hash. + pub fn with_payload_json_hash( + mut self, + payload_json_hash: impl std::convert::Into, + ) -> kb_core::Result { + let value = payload_json_hash.into(); + if value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "core instruction payload hash must not be empty", + )); + } + self.payload_json_hash = std::option::Option::Some(value); + return std::result::Result::Ok(self); + } +} + +/// Core inner instruction insert contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoreInnerInstructionInsert { + /// Transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Transaction slot in Solana unsigned representation. + pub slot: u64, + /// Parent top-level or inner instruction path. + pub parent_instruction_path: std::string::String, + /// Stable inner instruction path, for example `2/1`. + pub instruction_path: std::string::String, + /// Program id as non-empty base58 text. + pub program_id: std::string::String, + /// Inner instruction accounts as JSON. + pub accounts_json: serde_json::Value, + /// Inner instruction payload JSON. + pub payload_json: serde_json::Value, + /// Optional deterministic payload JSON hash. + pub payload_json_hash: std::option::Option, +} + +impl CoreInnerInstructionInsert { + /// Builds a core inner instruction insert contract after minimal validation. + pub fn new( + signature: impl std::convert::Into, + slot: u64, + parent_instruction_path: impl std::convert::Into, + instruction_path: impl std::convert::Into, + program_id: impl std::convert::Into, + accounts_json: serde_json::Value, + payload_json: serde_json::Value, + ) -> kb_core::Result { + let signature_value = signature.into(); + let parent_instruction_path_value = parent_instruction_path.into(); + let instruction_path_value = instruction_path.into(); + let program_id_value = program_id.into(); + let parent_result = validate_required_text( + &parent_instruction_path_value, + "core inner instruction parent path must not be empty", + ); + if let std::result::Result::Err(error) = parent_result { + return std::result::Result::Err(error); + } + let validation_result = validate_instruction_identity( + signature_value.as_str(), + instruction_path_value.as_str(), + program_id_value.as_str(), + "core inner instruction", + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(Self { + signature: signature_value, + slot, + parent_instruction_path: parent_instruction_path_value, + instruction_path: instruction_path_value, + program_id: program_id_value, + accounts_json, + payload_json, + payload_json_hash: std::option::Option::None, + }); + } + + /// Adds a deterministic payload JSON hash. + pub fn with_payload_json_hash( + mut self, + payload_json_hash: impl std::convert::Into, + ) -> kb_core::Result { + let value = payload_json_hash.into(); + if value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "core inner instruction payload hash must not be empty", + )); + } + self.payload_json_hash = std::option::Option::Some(value); + return std::result::Result::Ok(self); + } +} + +/// Core log insert contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoreLogInsert { + /// Transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Transaction slot in Solana unsigned representation. + pub slot: u64, + /// Log index preserving transaction log order. + pub log_index: u32, + /// Optional instruction path resolved from invocation depth when known. + pub instruction_path: std::option::Option, + /// Optional program id resolved from the log line or invocation context. + pub program_id: std::option::Option, + /// Original log text. + pub log_text: std::string::String, + /// Optional deterministic log text hash. + pub log_text_hash: std::option::Option, +} + +impl CoreLogInsert { + /// Builds a core log insert contract after minimal validation. + pub fn new( + signature: impl std::convert::Into, + slot: u64, + log_index: u32, + instruction_path: std::option::Option, + program_id: std::option::Option, + log_text: impl std::convert::Into, + ) -> kb_core::Result { + let signature_value = signature.into(); + let log_text_value = log_text.into(); + let signature_result = + validate_required_text(&signature_value, "core log signature must not be empty"); + if let std::result::Result::Err(error) = signature_result { + return std::result::Result::Err(error); + } + let log_text_result = + validate_required_text(&log_text_value, "core log text must not be empty"); + if let std::result::Result::Err(error) = log_text_result { + return std::result::Result::Err(error); + } + let instruction_path_result = validate_optional_text( + instruction_path.as_deref(), + "core log instruction path must not be empty when present", + ); + if let std::result::Result::Err(error) = instruction_path_result { + return std::result::Result::Err(error); + } + let program_id_result = validate_optional_text( + program_id.as_deref(), + "core log program id must not be empty when present", + ); + if let std::result::Result::Err(error) = program_id_result { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(Self { + signature: signature_value, + slot, + log_index, + instruction_path, + program_id, + log_text: log_text_value, + log_text_hash: std::option::Option::None, + }); + } + + /// Adds a deterministic log text hash. + pub fn with_log_text_hash( + mut self, + log_text_hash: impl std::convert::Into, + ) -> kb_core::Result { + let value = log_text_hash.into(); + if value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "core log text hash must not be empty", + )); + } + self.log_text_hash = std::option::Option::Some(value); + return std::result::Result::Ok(self); + } +} + +/// Core balance change insert contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoreBalanceChangeInsert { + /// Transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Transaction slot in Solana unsigned representation. + pub slot: u64, + /// Stable balance change index preserving extraction order. + pub balance_change_index: u32, + /// Balance change family. + pub balance_kind: CoreBalanceChangeKind, + /// Optional account index when available. + pub account_index: std::option::Option, + /// Optional account public key when available. + pub account_key: std::option::Option, + /// Optional SPL token mint for token balances. + pub mint: std::option::Option, + /// Optional owner public key for token balances. + pub owner: std::option::Option, + /// Pre-balance JSON value preserving RPC representation. + pub pre_balance_json: std::option::Option, + /// Post-balance JSON value preserving RPC representation. + pub post_balance_json: std::option::Option, + /// Delta JSON value preserving integer or decimal-safe representation. + pub delta_json: std::option::Option, +} + +impl CoreBalanceChangeInsert { + /// Builds a core balance change insert contract after minimal validation. + #[allow(clippy::too_many_arguments)] + pub fn new( + signature: impl std::convert::Into, + slot: u64, + balance_change_index: u32, + balance_kind: CoreBalanceChangeKind, + account_index: std::option::Option, + account_key: std::option::Option, + mint: std::option::Option, + owner: std::option::Option, + pre_balance_json: std::option::Option, + post_balance_json: std::option::Option, + delta_json: std::option::Option, + ) -> kb_core::Result { + let signature_value = signature.into(); + let signature_result = validate_required_text( + &signature_value, + "core balance change signature must not be empty", + ); + if let std::result::Result::Err(error) = signature_result { + return std::result::Result::Err(error); + } + let account_key_result = validate_optional_text( + account_key.as_deref(), + "core balance change account key must not be empty when present", + ); + if let std::result::Result::Err(error) = account_key_result { + return std::result::Result::Err(error); + } + let mint_result = validate_optional_text( + mint.as_deref(), + "core balance change mint must not be empty when present", + ); + if let std::result::Result::Err(error) = mint_result { + return std::result::Result::Err(error); + } + let owner_result = validate_optional_text( + owner.as_deref(), + "core balance change owner must not be empty when present", + ); + if let std::result::Result::Err(error) = owner_result { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(Self { + signature: signature_value, + slot, + balance_change_index, + balance_kind, + account_index, + account_key, + mint, + owner, + pre_balance_json, + post_balance_json, + delta_json, + }); + } +} + +/// Decoder replay input containing one instruction plus extracted transaction context. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoreInstructionReplayInput { + /// Version of the normalized core replay contract. + pub core_contract_version: u32, + /// Stable deduplication key for this replay input. + pub replay_input_key: std::string::String, + /// Transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Transaction slot in Solana unsigned representation. + pub slot: u64, + /// Stable instruction path, for example `0` or `2/1`. + pub instruction_path: std::string::String, + /// Program id as non-empty base58 text. + pub program_id: std::string::String, + /// Optional surface hint supplied by an operator or upstream classifier. + pub surface_code_hint: std::option::Option, + /// Whether the parent transaction failed on-chain. + pub transaction_failed: bool, + /// Optional transaction error JSON. + pub transaction_err_json: std::option::Option, + /// Resolved account keys for the whole transaction. + pub account_keys_json: serde_json::Value, + /// Instruction accounts for the target instruction. + pub instruction_accounts_json: serde_json::Value, + /// Optional instruction payload when it is retained in the hot store. + pub instruction_payload_json: std::option::Option, + /// Optional deterministic hash of the retained instruction payload. + pub instruction_payload_hash: std::option::Option, + /// Ordered outer instructions for the transaction. Each array entry carries the numeric outer + /// instruction index, stable instruction path, program id, retained payload JSON and payload hash. + pub outer_instructions_json: serde_json::Value, + /// Inner instruction tree or subtree relevant to the target instruction. + pub inner_instructions_json: serde_json::Value, + /// Ordered logs relevant to the transaction or target instruction. + pub logs_json: serde_json::Value, + /// Balance changes relevant to the transaction or target instruction. + pub balance_changes_json: serde_json::Value, +} + +impl CoreInstructionReplayInput { + /// Builds a decoder replay input after minimal validation. + #[allow(clippy::too_many_arguments)] + pub fn new( + replay_input_key: impl std::convert::Into, + signature: impl std::convert::Into, + slot: u64, + instruction_path: impl std::convert::Into, + program_id: impl std::convert::Into, + transaction_failed: bool, + transaction_err_json: std::option::Option, + account_keys_json: serde_json::Value, + instruction_accounts_json: serde_json::Value, + instruction_payload_json: std::option::Option, + instruction_payload_hash: std::option::Option, + outer_instructions_json: serde_json::Value, + inner_instructions_json: serde_json::Value, + logs_json: serde_json::Value, + balance_changes_json: serde_json::Value, + ) -> kb_core::Result { + let replay_input_key_value = replay_input_key.into(); + let signature_value = signature.into(); + let instruction_path_value = instruction_path.into(); + let program_id_value = program_id.into(); + let replay_input_key_result = validate_required_text( + &replay_input_key_value, + "core replay input key must not be empty", + ); + if let std::result::Result::Err(error) = replay_input_key_result { + return std::result::Result::Err(error); + } + let validation_result = validate_instruction_identity( + signature_value.as_str(), + instruction_path_value.as_str(), + program_id_value.as_str(), + "core replay input", + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + if instruction_payload_hash + .as_deref() + .is_some_and(|hash| return hash.trim().is_empty()) + { + return std::result::Result::Err(kb_core::Error::db( + "core replay input payload hash must not be empty when present", + )); + } + if !outer_instructions_json.is_array() { + return std::result::Result::Err(kb_core::Error::db( + "core replay input outer instructions must be a JSON array", + )); + } + return std::result::Result::Ok(Self { + core_contract_version: crate::dtos::core_dtos::CORE_REPLAY_INPUT_CONTRACT_VERSION, + replay_input_key: replay_input_key_value, + signature: signature_value, + slot, + instruction_path: instruction_path_value, + program_id: program_id_value, + surface_code_hint: std::option::Option::None, + transaction_failed, + transaction_err_json, + account_keys_json, + instruction_accounts_json, + instruction_payload_json, + instruction_payload_hash, + outer_instructions_json, + inner_instructions_json, + logs_json, + balance_changes_json, + }); + } + + /// Adds an optional stable surface hint after validation. + pub fn with_surface_code_hint( + mut self, + surface_code_hint: std::option::Option, + ) -> kb_core::Result { + if surface_code_hint + .as_deref() + .is_some_and(|surface| return surface.trim().is_empty()) + { + return std::result::Result::Err(kb_core::Error::db( + "core replay input surface hint must not be empty when present", + )); + } + self.surface_code_hint = surface_code_hint; + return std::result::Result::Ok(self); + } +} + +/// Core instruction replay filter contract. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoreInstructionReplayFilter { + /// Optional processing state to select, usually `Pending`, `Failed` or `ReplayRequested`. + pub processing_state: std::option::Option, + /// Optional program id filter. + pub program_id: std::option::Option, + /// Optional inclusive minimum slot. + pub min_slot: std::option::Option, + /// Optional inclusive maximum slot. + pub max_slot: std::option::Option, +} + +impl CoreInstructionReplayFilter { + /// Builds a replay filter after minimal validation. + pub fn new( + processing_state: std::option::Option, + program_id: std::option::Option, + min_slot: std::option::Option, + max_slot: std::option::Option, + ) -> kb_core::Result { + if let std::option::Option::Some(program_id_value) = program_id.as_ref() { + if program_id_value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "core instruction replay program id must not be empty when present", + )); + } + } + if let ( + std::option::Option::Some(min_slot_value), + std::option::Option::Some(max_slot_value), + ) = (min_slot, max_slot) + { + if min_slot_value > max_slot_value { + return std::result::Result::Err(kb_core::Error::db( + "core instruction replay min slot must be lower than or equal to max slot", + )); + } + } + return std::result::Result::Ok(Self { + processing_state, + program_id, + min_slot, + max_slot, + }); + } + + /// Builds the default pending instruction replay filter. + pub fn pending() -> Self { + return Self { + processing_state: std::option::Option::Some(CoreInstructionProcessingState::Pending), + program_id: std::option::Option::None, + min_slot: std::option::Option::None, + max_slot: std::option::Option::None, + }; + } +} + +/// Core instruction lifecycle mark request. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoreInstructionLifecycleMark { + /// Transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Stable instruction path, for example `0` or `2/1`. + pub instruction_path: std::string::String, + /// New processing state. + pub processing_state: CoreInstructionProcessingState, + /// Optional processor name that produced the lifecycle transition. + pub processor_name: std::option::Option, + /// Optional processor version that produced the lifecycle transition. + pub processor_version: std::option::Option, + /// Optional reason visible in diagnostics. + pub reason: std::option::Option, +} + +impl CoreInstructionLifecycleMark { + /// Builds a core instruction lifecycle mark after minimal validation. + pub fn new( + signature: impl std::convert::Into, + instruction_path: impl std::convert::Into, + processing_state: CoreInstructionProcessingState, + processor_name: std::option::Option, + processor_version: std::option::Option, + reason: std::option::Option, + ) -> kb_core::Result { + let signature_value = signature.into(); + let instruction_path_value = instruction_path.into(); + let signature_result = validate_required_text( + &signature_value, + "core instruction lifecycle signature must not be empty", + ); + if let std::result::Result::Err(error) = signature_result { + return std::result::Result::Err(error); + } + let instruction_path_result = validate_required_text( + &instruction_path_value, + "core instruction lifecycle path must not be empty", + ); + if let std::result::Result::Err(error) = instruction_path_result { + return std::result::Result::Err(error); + } + let processor_name_result = validate_optional_text( + processor_name.as_deref(), + "core instruction lifecycle processor name must not be empty when present", + ); + if let std::result::Result::Err(error) = processor_name_result { + return std::result::Result::Err(error); + } + let processor_version_result = validate_optional_text( + processor_version.as_deref(), + "core instruction lifecycle processor version must not be empty when present", + ); + if let std::result::Result::Err(error) = processor_version_result { + return std::result::Result::Err(error); + } + let reason_result = validate_optional_text( + reason.as_deref(), + "core instruction lifecycle reason must not be empty when present", + ); + if let std::result::Result::Err(error) = reason_result { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(Self { + signature: signature_value, + instruction_path: instruction_path_value, + processing_state, + processor_name, + processor_version, + reason, + }); + } +} + +fn validate_instruction_identity( + signature: &str, + instruction_path: &str, + program_id: &str, + label: &str, +) -> kb_core::Result<()> { + let signature_result = + validate_required_text(signature, "instruction signature must not be empty"); + if let std::result::Result::Err(_error) = signature_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "{label} signature must not be empty" + ))); + } + let instruction_path_result = + validate_required_text(instruction_path, "instruction path must not be empty"); + if let std::result::Result::Err(_error) = instruction_path_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "{label} path must not be empty" + ))); + } + let program_id_result = + validate_required_text(program_id, "instruction program id must not be empty"); + if let std::result::Result::Err(_error) = program_id_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "{label} program id must not be empty" + ))); + } + return std::result::Result::Ok(()); +} + +fn validate_required_text( + value: &str, + message: impl std::convert::Into, +) -> kb_core::Result<()> { + if value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db(message.into())); + } + return std::result::Result::Ok(()); +} + +fn validate_optional_text( + value: std::option::Option<&str>, + message: &'static str, +) -> kb_core::Result<()> { + if let std::option::Option::Some(text_value) = value { + if text_value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db(message)); + } + } + return std::result::Result::Ok(()); +} + +#[cfg(test)] +mod tests { + #[test] + fn core_transaction_rejects_empty_signature() { + let result = crate::CoreTransactionInsert::new(" ", 1, false, std::option::Option::None); + assert!(result.is_err()); + } + + #[test] + fn core_transaction_accepts_optional_raw_lineage() { + let result = crate::CoreTransactionInsert::new("abc", 1, false, std::option::Option::None); + let input = match result { + std::result::Result::Ok(value) => value.with_raw_transaction_id(7), + std::result::Result::Err(error) => panic!("unexpected transaction error: {error}"), + }; + assert_eq!(input.raw_transaction_id, std::option::Option::Some(7)); + } + + #[test] + fn core_account_key_rejects_empty_key() { + let result = crate::CoreAccountKeyInsert::new( + "abc", + 1, + 0, + " ", + crate::CoreAccountKeySource::Static, + false, + false, + std::option::Option::None, + ); + assert!(result.is_err()); + } + + #[test] + fn core_instruction_rejects_empty_program_id() { + let result = crate::CoreInstructionInsert::new( + "abc", + 1, + "0", + " ", + serde_json::json!([]), + serde_json::json!({}), + ); + assert!(result.is_err()); + } + + #[test] + fn core_instruction_defaults_to_pending() { + let result = crate::CoreInstructionInsert::new( + "abc", + 1, + "0", + "program", + serde_json::json!([]), + serde_json::json!({}), + ); + if let std::result::Result::Ok(input) = result { + assert_eq!(crate::CoreInstructionProcessingState::Pending, input.processing_state); + } else { + panic!("core instruction insert should be valid"); + } + } + + #[test] + fn core_inner_instruction_rejects_empty_parent_path() { + let result = crate::CoreInnerInstructionInsert::new( + "abc", + 1, + " ", + "0/0", + "program", + serde_json::json!([]), + serde_json::json!({}), + ); + assert!(result.is_err()); + } + + #[test] + fn core_log_rejects_empty_text() { + let result = crate::CoreLogInsert::new( + "abc", + 1, + 0, + std::option::Option::None, + std::option::Option::None, + " ", + ); + assert!(result.is_err()); + } + + #[test] + fn core_balance_change_rejects_empty_optional_mint() { + let result = crate::CoreBalanceChangeInsert::new( + "abc", + 1, + 0, + crate::CoreBalanceChangeKind::TokenAmount, + std::option::Option::Some(0), + std::option::Option::Some("account".to_string()), + std::option::Option::Some(" ".to_string()), + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + ); + assert!(result.is_err()); + } + + #[test] + fn replay_input_rejects_empty_key() { + let result = crate::CoreInstructionReplayInput::new( + " ", + "abc", + 1, + "0", + "program", + false, + std::option::Option::None, + serde_json::json!([]), + serde_json::json!([]), + std::option::Option::Some(serde_json::json!({})), + std::option::Option::None, + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + assert!(result.is_err()); + } + + #[test] + fn replay_input_accepts_ordered_outer_instruction_array() { + let result = crate::CoreInstructionReplayInput::new( + "abc:2", + "abc", + 1, + "2", + "program", + false, + std::option::Option::None, + serde_json::json!([]), + serde_json::json!([]), + std::option::Option::Some(serde_json::json!({"dataBase64": "AQ=="})), + std::option::Option::Some("payload-hash".to_string()), + serde_json::json!([ + { + "instructionIndex": 0, + "instructionPath": "0", + "programId": "other", + "payloadJson": {"dataBase64": "Ag=="}, + "payloadHash": "other-hash" + }, + { + "instructionIndex": 2, + "instructionPath": "2", + "programId": "program", + "payloadJson": {"dataBase64": "AQ=="}, + "payloadHash": "payload-hash" + } + ]), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + let input = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected replay input error: {error}"), + }; + assert_eq!(input.core_contract_version, 2); + assert_eq!( + input.outer_instructions_json.as_array().map(std::vec::Vec::len), + std::option::Option::Some(2) + ); + } + + #[test] + fn replay_input_rejects_non_array_outer_instruction_context() { + let result = crate::CoreInstructionReplayInput::new( + "abc:0", + "abc", + 1, + "0", + "program", + false, + std::option::Option::None, + serde_json::json!([]), + serde_json::json!([]), + std::option::Option::Some(serde_json::json!({"dataBase64": "AQ=="})), + std::option::Option::None, + serde_json::json!({"instructionIndex": 0}), + serde_json::json!([]), + serde_json::json!([]), + serde_json::json!([]), + ); + assert!(result.is_err()); + } + + #[test] + fn replay_filter_rejects_empty_program_id() { + let result = crate::CoreInstructionReplayFilter::new( + std::option::Option::Some(crate::CoreInstructionProcessingState::Pending), + std::option::Option::Some(" ".to_string()), + std::option::Option::None, + std::option::Option::None, + ); + assert!(result.is_err()); + } + + #[test] + fn replay_filter_rejects_inverted_slots() { + let result = crate::CoreInstructionReplayFilter::new( + std::option::Option::None, + std::option::Option::None, + std::option::Option::Some(10), + std::option::Option::Some(1), + ); + assert!(result.is_err()); + } + + #[test] + fn lifecycle_mark_rejects_empty_path() { + let result = crate::CoreInstructionLifecycleMark::new( + "signature", + " ", + crate::CoreInstructionProcessingState::Decoded, + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + ); + assert!(result.is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/core_extraction_dtos.rs b/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/core_extraction_dtos.rs new file mode 100644 index 0000000..d36eaac --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/core_extraction_dtos.rs @@ -0,0 +1,289 @@ +// file: kb_store_core/src/dtos/core_extraction_dtos.rs +// version: 2 + +//! Canonical transaction to core extraction storage contracts. + +/// Stable processing status for one extraction ledger entry. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum ProcessingLedgerStatus { + /// Processing is currently running. + Running, + /// Processing completed successfully. + Succeeded, + /// Processing failed and may be retried. + Failed, +} + +/// Bounded selection filter for canonical transactions awaiting core extraction. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoreExtractionSelectionFilter { + /// Optional exact signatures selected by the operator. + pub signatures: std::vec::Vec, + /// Optional inclusive minimum slot. + pub min_slot: std::option::Option, + /// Optional inclusive maximum slot. + pub max_slot: std::option::Option, + /// Optional raw processing state restriction. + pub processing_state: std::option::Option, + /// Optional program id previously resolved in core instructions. + pub program_id: std::option::Option, + /// Maximum number of canonical transactions returned. + pub limit: u32, +} + +impl CoreExtractionSelectionFilter { + /// Builds a validated extraction selection filter. + pub fn new( + signatures: std::vec::Vec, + min_slot: std::option::Option, + max_slot: std::option::Option, + processing_state: std::option::Option, + program_id: std::option::Option, + limit: u32, + ) -> kb_core::Result { + if limit == 0 { + return std::result::Result::Err(kb_core::Error::db( + "core extraction selection limit must be greater than zero", + )); + } + if let (std::option::Option::Some(minimum), std::option::Option::Some(maximum)) = + (min_slot, max_slot) + { + if minimum > maximum { + return std::result::Result::Err(kb_core::Error::db( + "core extraction minimum slot must not exceed maximum slot", + )); + } + } + for signature in &signatures { + if signature.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "core extraction signature filter must not contain empty values", + )); + } + } + if program_id.as_deref().is_some_and(|value| return value.trim().is_empty()) { + return std::result::Result::Err(kb_core::Error::db( + "core extraction program id filter must not be empty", + )); + } + return std::result::Result::Ok(Self { + signatures, + min_slot, + max_slot, + processing_state, + program_id, + limit, + }); + } + + /// Builds a pending raw transaction selection. + pub fn pending(limit: u32) -> kb_core::Result { + return Self::new( + std::vec::Vec::new(), + std::option::Option::None, + std::option::Option::None, + std::option::Option::Some(crate::RawPayloadProcessingState::Received), + std::option::Option::None, + limit, + ); + } +} + +/// Stable identity of one processor input in the processing ledger. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct ProcessingLedgerIdentity { + /// Processing stage code. + pub stage: std::string::String, + /// Processor implementation name. + pub processor_name: std::string::String, + /// Processor semantic version. + pub processor_version: std::string::String, + /// Stable input key. + pub input_key: std::string::String, + /// Deterministic input hash. + pub input_hash: std::string::String, +} + +impl ProcessingLedgerIdentity { + /// Builds a validated processing ledger identity. + pub fn new( + stage: impl std::convert::Into, + processor_name: impl std::convert::Into, + processor_version: impl std::convert::Into, + input_key: impl std::convert::Into, + input_hash: impl std::convert::Into, + ) -> kb_core::Result { + let value = Self { + stage: stage.into(), + processor_name: processor_name.into(), + processor_version: processor_version.into(), + input_key: input_key.into(), + input_hash: input_hash.into(), + }; + if value.stage.trim().is_empty() + || value.processor_name.trim().is_empty() + || value.processor_version.trim().is_empty() + || value.input_key.trim().is_empty() + || value.input_hash.trim().is_empty() + { + return std::result::Result::Err(kb_core::Error::db( + "processing ledger identity fields must not be empty", + )); + } + return std::result::Result::Ok(value); + } +} + +/// Complete set of normalized rows produced from one canonical transaction. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoreExtractionBundle { + /// Source canonical raw transaction technical id. + pub raw_transaction_id: i64, + /// Processing ledger identity. + pub ledger_identity: crate::ProcessingLedgerIdentity, + /// Core transaction row. + pub transaction: crate::CoreTransactionInsert, + /// Resolved account keys. + pub account_keys: std::vec::Vec, + /// Top-level instructions. + pub instructions: std::vec::Vec, + /// Inner instructions. + pub inner_instructions: std::vec::Vec, + /// Ordered transaction logs. + pub logs: std::vec::Vec, + /// Native and token balance changes. + pub balance_changes: std::vec::Vec, +} + +impl CoreExtractionBundle { + /// Validates lineage and stable signature consistency across the bundle. + pub fn validate(&self) -> kb_core::Result<()> { + if self.raw_transaction_id <= 0 { + return std::result::Result::Err(kb_core::Error::db( + "core extraction raw transaction id must be positive", + )); + } + if self.transaction.raw_transaction_id != std::option::Option::Some(self.raw_transaction_id) + { + return std::result::Result::Err(kb_core::Error::db( + "core extraction transaction lineage does not match the raw transaction id", + )); + } + if self.transaction.signature != self.ledger_identity.input_key { + return std::result::Result::Err(kb_core::Error::db( + "core extraction ledger input key must equal the transaction signature", + )); + } + let signature = self.transaction.signature.as_str(); + for input in &self.account_keys { + if input.signature != signature { + return std::result::Result::Err(kb_core::Error::db( + "core extraction account key signature mismatch", + )); + } + } + for input in &self.instructions { + if input.signature != signature { + return std::result::Result::Err(kb_core::Error::db( + "core extraction instruction signature mismatch", + )); + } + } + for input in &self.inner_instructions { + if input.signature != signature { + return std::result::Result::Err(kb_core::Error::db( + "core extraction inner instruction signature mismatch", + )); + } + } + for input in &self.logs { + if input.signature != signature { + return std::result::Result::Err(kb_core::Error::db( + "core extraction log signature mismatch", + )); + } + } + for input in &self.balance_changes { + if input.signature != signature { + return std::result::Result::Err(kb_core::Error::db( + "core extraction balance signature mismatch", + )); + } + } + return std::result::Result::Ok(()); + } +} + +/// Failure details persisted when canonical to core extraction cannot complete. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoreExtractionFailure { + /// Source canonical raw transaction technical id. + pub raw_transaction_id: i64, + /// Processing ledger identity. + pub ledger_identity: crate::ProcessingLedgerIdentity, + /// Stable machine-readable error code. + pub error_code: std::string::String, + /// Human-readable diagnostic message. + pub error_message: std::string::String, +} + +impl CoreExtractionFailure { + /// Builds a validated failure record. + pub fn new( + raw_transaction_id: i64, + ledger_identity: crate::ProcessingLedgerIdentity, + error_code: impl std::convert::Into, + error_message: impl std::convert::Into, + ) -> kb_core::Result { + let value = Self { + raw_transaction_id, + ledger_identity, + error_code: error_code.into(), + error_message: error_message.into(), + }; + if value.raw_transaction_id <= 0 + || value.error_code.trim().is_empty() + || value.error_message.trim().is_empty() + { + return std::result::Result::Err(kb_core::Error::db( + "core extraction failure fields are invalid", + )); + } + return std::result::Result::Ok(value); + } +} + +#[cfg(test)] +mod tests { + #[test] + fn pending_filter_requires_positive_limit() { + let result = crate::CoreExtractionSelectionFilter::pending(0); + assert!(result.is_err()); + } + + #[test] + fn selection_filter_rejects_inverted_slots() { + let result = crate::CoreExtractionSelectionFilter::new( + std::vec::Vec::new(), + std::option::Option::Some(20), + std::option::Option::Some(10), + std::option::Option::None, + std::option::Option::None, + 10, + ); + assert!(result.is_err()); + } + + #[test] + fn ledger_identity_requires_input_hash() { + let result = crate::ProcessingLedgerIdentity::new( + "core_extraction", + "canonical_to_core", + "1", + "signature", + " ", + ); + assert!(result.is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/decode_dtos.rs b/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/decode_dtos.rs new file mode 100644 index 0000000..43aaec7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/decode_dtos.rs @@ -0,0 +1,684 @@ +// file: kb_store_core/src/dtos/decode_dtos.rs +// version: 8 + +//! Backend-neutral decode, coverage and materialization persistence DTOs. + +/// Maximum number of materialized rows returned by one bounded query. +pub const MAX_MATERIALIZED_EVENT_QUERY_ROWS: u32 = 500; + +/// Bounded read-only materialized event selection. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct MaterializedEventFilter { + /// Optional exact materializer processor name. + pub processor_name: std::option::Option, + /// Optional exact materialized family code. + pub materialized_family: std::option::Option, + /// Optional partial transaction signature. + pub signature_contains: std::option::Option, + /// Maximum returned rows. + pub limit: u32, +} + +impl MaterializedEventFilter { + /// Builds and validates a bounded materialized event filter. + pub fn new( + processor_name: std::option::Option, + materialized_family: std::option::Option, + signature_contains: std::option::Option, + limit: u32, + ) -> kb_core::Result { + if limit == 0 || limit > crate::MAX_MATERIALIZED_EVENT_QUERY_ROWS { + return std::result::Result::Err(kb_core::Error::db(format!( + "materialized event query limit must be between 1 and {}", + crate::MAX_MATERIALIZED_EVENT_QUERY_ROWS + ))); + } + return std::result::Result::Ok(Self { + processor_name: crate::dtos::decode_dtos::trim_optional_text(processor_name), + materialized_family: crate::dtos::decode_dtos::trim_optional_text(materialized_family), + signature_contains: crate::dtos::decode_dtos::trim_optional_text(signature_contains), + limit, + }); + } +} + +/// One materialized output returned by the common bounded query contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct MaterializedEventQueryRow { + /// Materializer processor name. + pub processor_name: std::string::String, + /// Materializer processor version. + pub processor_version: std::string::String, + /// Stable materializer input key. + pub input_key: std::string::String, + /// Stable processor-owned output key. + pub output_key: std::string::String, + /// Source decoded event key. + pub source_event_key: std::string::String, + /// Source decoder name. + pub source_decoder_name: std::string::String, + /// Source decoder version. + pub source_decoder_version: std::string::String, + /// Source transaction signature. + pub signature: std::string::String, + /// Source transaction slot. + pub slot: u64, + /// Materialized family code. + pub materialized_family: std::string::String, + /// Typed materialized payload. + pub payload_json: serde_json::Value, + /// Creation timestamp rendered by the backend. + pub created_at: std::string::String, + /// Last replacement timestamp rendered by the backend. + pub updated_at: std::string::String, +} + +/// Bounded contextual instruction selection filter for decode campaigns. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecodeSelectionFilter { + /// Explicit transaction signatures to select. + pub signatures: std::vec::Vec, + /// Explicit instruction processing states to select. + pub processing_states: std::vec::Vec, + /// Optional inclusive minimum slot. + pub min_slot: std::option::Option, + /// Optional inclusive maximum slot. + pub max_slot: std::option::Option, + /// Explicit program identifiers to select. + pub program_ids: std::vec::Vec, + /// Explicit stable instruction paths to select. + pub instruction_paths: std::vec::Vec, + /// Expands any incomplete instruction match to every instruction in the same signature. + pub incomplete_signatures: bool, + /// Maximum number of contextual inputs, or signatures when expansion is enabled. + pub limit: u32, +} + +impl DecodeSelectionFilter { + /// Builds a validated bounded decode selection filter. + #[allow(clippy::too_many_arguments)] + pub fn new( + signatures: std::vec::Vec, + processing_states: std::vec::Vec, + min_slot: std::option::Option, + max_slot: std::option::Option, + program_ids: std::vec::Vec, + instruction_paths: std::vec::Vec, + incomplete_signatures: bool, + limit: u32, + ) -> kb_core::Result { + if limit == 0 { + return std::result::Result::Err(kb_core::Error::db( + "decode selection limit must be greater than zero", + )); + } + if min_slot.is_some() && max_slot.is_some() && min_slot > max_slot { + return std::result::Result::Err(kb_core::Error::db( + "decode selection minimum slot must not exceed maximum slot", + )); + } + let signatures_result = validate_text_list(&signatures, "decode selection signature"); + if let std::result::Result::Err(error) = signatures_result { + return std::result::Result::Err(error); + } + let program_ids_result = validate_text_list(&program_ids, "decode selection program id"); + if let std::result::Result::Err(error) = program_ids_result { + return std::result::Result::Err(error); + } + let paths_result = + validate_text_list(&instruction_paths, "decode selection instruction path"); + if let std::result::Result::Err(error) = paths_result { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(Self { + signatures, + processing_states, + min_slot, + max_slot, + program_ids, + instruction_paths, + incomplete_signatures, + limit, + }); + } + + /// Builds the default pending, failed and replay-requested selection. + pub fn actionable(limit: u32) -> kb_core::Result { + return crate::DecodeSelectionFilter::new( + std::vec::Vec::new(), + std::vec![ + crate::CoreInstructionProcessingState::Pending, + crate::CoreInstructionProcessingState::Failed, + crate::CoreInstructionProcessingState::ReplayRequested, + ], + std::option::Option::None, + std::option::Option::None, + std::vec::Vec::new(), + std::vec::Vec::new(), + false, + limit, + ); + } +} + +/// One processor-owned decoded observation row. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecodeObservationInsert { + /// Stable decode processor name. + pub processor_name: std::string::String, + /// Stable decode processor version. + pub processor_version: std::string::String, + /// Stable contextual input key. + pub input_key: std::string::String, + /// Deterministic contextual input hash. + pub input_hash: std::string::String, + /// Stable event key within the processor and input. + pub event_key: std::string::String, + /// Source transaction signature. + pub signature: std::string::String, + /// Source transaction slot. + pub slot: u64, + /// Source instruction path. + pub instruction_path: std::string::String, + /// Source program identifier. + pub program_id: std::string::String, + /// Stable protocol code. + pub protocol_code: std::string::String, + /// Stable surface code. + pub surface_code: std::string::String, + /// Stable event code. + pub event_code: std::string::String, + /// Stable event name. + pub event_name: std::string::String, + /// Stable event family code. + pub event_family: std::string::String, + /// Stable event source code. + pub source_kind: std::string::String, + /// Stable decoder confidence code. + pub confidence: std::string::String, + /// Stable proof kind code. + pub proof_kind: std::string::String, + /// Proof evidence JSON. + pub proof_json: serde_json::Value, + /// Typed decoded payload JSON. + pub payload_json: serde_json::Value, + /// Whether the source transaction failed on-chain. + pub transaction_failed: bool, + /// Optional source transaction error JSON. + pub transaction_error: std::option::Option, + /// Whether the observed state mutation was committed on-chain. + pub observation_committed: bool, +} + +impl DecodeObservationInsert { + /// Validates stable identities and failed transaction commit semantics. + pub fn validate(&self) -> kb_core::Result<()> { + let fields = [ + self.processor_name.as_str(), + self.processor_version.as_str(), + self.input_key.as_str(), + self.input_hash.as_str(), + self.event_key.as_str(), + self.signature.as_str(), + self.instruction_path.as_str(), + self.program_id.as_str(), + self.protocol_code.as_str(), + self.surface_code.as_str(), + self.event_code.as_str(), + self.event_name.as_str(), + self.event_family.as_str(), + self.source_kind.as_str(), + self.confidence.as_str(), + self.proof_kind.as_str(), + ]; + if fields.iter().any(|value| return value.trim().is_empty()) { + return std::result::Result::Err(kb_core::Error::db( + "decoded observation identity fields must not be empty", + )); + } + if self.transaction_failed && self.observation_committed { + return std::result::Result::Err(kb_core::Error::db( + "failed transaction decoded observations must not be committed", + )); + } + return std::result::Result::Ok(()); + } +} + +/// One machine-readable decoder coverage declaration row. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecodeCoverageDeclarationInsert { + /// Stable decoder name. + pub processor_name: std::string::String, + /// Stable decoder version. + pub processor_version: std::string::String, + /// Exact Solana program identifier. + pub program_id: std::string::String, + /// Optional stable surface code. + pub surface_code: std::option::Option, + /// Stable entry kind code. + pub entry_kind: std::string::String, + /// Stable instruction, event or discriminator code. + pub entry_code: std::string::String, + /// Optional normalized hexadecimal discriminator. + pub discriminator_hex: std::option::Option, + /// Whether the entry is historical or deprecated. + pub historical: bool, +} + +/// One observed coverage classification row owned by one decode attempt. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecodeCoverageObservationInsert { + /// Stable decoder name. + pub processor_name: std::string::String, + /// Stable decoder version. + pub processor_version: std::string::String, + /// Stable contextual input key. + pub input_key: std::string::String, + /// Deterministic contextual input hash. + pub input_hash: std::string::String, + /// Source transaction signature. + pub signature: std::string::String, + /// Source transaction slot. + pub slot: u64, + /// Source instruction path. + pub instruction_path: std::string::String, + /// Source program identifier. + pub program_id: std::string::String, + /// Optional stable surface code. + pub surface_code: std::option::Option, + /// Optional recognized entry code. + pub entry_code: std::option::Option, + /// Optional discriminator. + pub discriminator_hex: std::option::Option, + /// Stable decode terminal status code. + pub status: std::string::String, + /// Whether the processor recognized the input as compatible. + pub recognized: bool, + /// Number of decoded observations produced. + pub decoded_count: u32, + /// Number of materialized outputs produced immediately after decoding. + pub materialized_count: u32, + /// Number of decoder diagnostics classified as errors. + pub error_count: u32, + /// Whether the source transaction failed on-chain. + pub transaction_failed: bool, +} + +/// Atomic persistence bundle for one decoder and one contextual input. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecodePersistenceBundle { + /// Processing ledger identity for the decode attempt. + pub ledger_identity: crate::ProcessingLedgerIdentity, + /// Source transaction signature. + pub signature: std::string::String, + /// Source instruction path. + pub instruction_path: std::string::String, + /// Stable terminal status code. + pub status: std::string::String, + /// Optional stable machine-readable terminal error code. + pub error_code: std::option::Option, + /// Optional human-readable terminal error message. + pub error_message: std::option::Option, + /// Processor-owned decoded observations. + pub observations: std::vec::Vec, + /// Coverage observation for this attempt. + pub coverage: DecodeCoverageObservationInsert, +} + +impl DecodePersistenceBundle { + /// Validates identities shared by every atomic decode output. + pub fn validate(&self) -> kb_core::Result<()> { + if self.ledger_identity.stage != "instruction_decode" + || self.signature.trim().is_empty() + || self.instruction_path.trim().is_empty() + || self.status.trim().is_empty() + { + return std::result::Result::Err(kb_core::Error::db( + "decode persistence bundle identity is invalid", + )); + } + if !matches!(self.status.as_str(), "decoded" | "ignored" | "unsupported" | "failed") { + return std::result::Result::Err(kb_core::Error::db( + "decode persistence bundle status is unsupported", + )); + } + if self.coverage.processor_name != self.ledger_identity.processor_name + || self.coverage.processor_version != self.ledger_identity.processor_version + || self.coverage.input_key != self.ledger_identity.input_key + || self.coverage.input_hash != self.ledger_identity.input_hash + || self.coverage.signature != self.signature + || self.coverage.instruction_path != self.instruction_path + || self.coverage.status != self.status + { + return std::result::Result::Err(kb_core::Error::db( + "decode coverage observation does not match bundle identity", + )); + } + let observation_count_result = u32::try_from(self.observations.len()); + let observation_count = match observation_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => { + return std::result::Result::Err(kb_core::Error::db( + "decode observation count exceeds the supported range", + )); + }, + }; + if self.coverage.decoded_count != observation_count + || (self.status == "decoded" && self.observations.is_empty()) + || (self.status != "decoded" && !self.observations.is_empty()) + { + return std::result::Result::Err(kb_core::Error::db( + "decode status, coverage count and observations are inconsistent", + )); + } + let missing_decode_error = match (&self.error_code, &self.error_message) { + (std::option::Option::Some(code), std::option::Option::Some(message)) => { + code.trim().is_empty() || message.trim().is_empty() + }, + _ => true, + }; + if self.status == "failed" && missing_decode_error { + return std::result::Result::Err(kb_core::Error::db( + "failed decode bundle requires error code and message", + )); + } + for observation in &self.observations { + let validation_result = observation.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + if observation.processor_name != self.ledger_identity.processor_name + || observation.processor_version != self.ledger_identity.processor_version + || observation.input_key != self.ledger_identity.input_key + || observation.input_hash != self.ledger_identity.input_hash + || observation.signature != self.signature + || observation.instruction_path != self.instruction_path + || observation.transaction_failed != self.coverage.transaction_failed + { + return std::result::Result::Err(kb_core::Error::db( + "decoded observation does not match bundle identity", + )); + } + } + return std::result::Result::Ok(()); + } +} + +/// Failed decode attempt persisted in the common ledger. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecodeFailure { + /// Processing ledger identity. + pub ledger_identity: crate::ProcessingLedgerIdentity, + /// Source transaction signature. + pub signature: std::string::String, + /// Source instruction path. + pub instruction_path: std::string::String, + /// Stable machine-readable error code. + pub error_code: std::string::String, + /// Human-readable diagnostic message. + pub error_message: std::string::String, +} + +/// One processor-owned materialized output row. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct MaterializedOutputInsert { + /// Stable materializer name. + pub processor_name: std::string::String, + /// Stable materializer version. + pub processor_version: std::string::String, + /// Stable decoded observation source key. + pub input_key: std::string::String, + /// Deterministic decoded observation input hash. + pub input_hash: std::string::String, + /// Stable output key within the materializer and input. + pub output_key: std::string::String, + /// Source decoded event key. + pub source_event_key: std::string::String, + /// Source transaction signature. + pub signature: std::string::String, + /// Source transaction slot. + pub slot: u64, + /// Stable materialized family code. + pub materialized_family: std::string::String, + /// Typed business payload JSON. + pub payload_json: serde_json::Value, +} + +/// Atomic persistence bundle for one materializer and one decoded observation. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct MaterializationPersistenceBundle { + /// Processing ledger identity for the materialization attempt. + pub ledger_identity: crate::ProcessingLedgerIdentity, + /// Source decoder name owning the decoded observation. + pub source_decoder_name: std::string::String, + /// Source decoder version owning the decoded observation. + pub source_decoder_version: std::string::String, + /// Source contextual decode input key. + pub source_decode_input_key: std::string::String, + /// Source transaction signature. + pub signature: std::string::String, + /// Source stable instruction path. + pub instruction_path: std::string::String, + /// Stable terminal materializer status code. + pub status: std::string::String, + /// Optional stable machine-readable terminal error code. + pub error_code: std::option::Option, + /// Optional human-readable terminal error message. + pub error_message: std::option::Option, + /// Processor-owned materialized outputs. + pub outputs: std::vec::Vec, +} + +impl MaterializationPersistenceBundle { + /// Validates the materializer, source decoder and output identities. + pub fn validate(&self) -> kb_core::Result<()> { + if self.ledger_identity.stage != "event_materialization" + || self.source_decoder_name.trim().is_empty() + || self.source_decoder_version.trim().is_empty() + || self.source_decode_input_key.trim().is_empty() + || self.signature.trim().is_empty() + || self.instruction_path.trim().is_empty() + || self.status.trim().is_empty() + { + return std::result::Result::Err(kb_core::Error::db( + "materialization persistence bundle identity is invalid", + )); + } + if !matches!( + self.status.as_str(), + "inserted" | "replaced" | "ignored" | "refused" | "failed" + ) { + return std::result::Result::Err(kb_core::Error::db( + "materialization persistence bundle status is unsupported", + )); + } + if (matches!(self.status.as_str(), "inserted" | "replaced") && self.outputs.is_empty()) + || (matches!(self.status.as_str(), "ignored" | "refused" | "failed") + && !self.outputs.is_empty()) + { + return std::result::Result::Err(kb_core::Error::db( + "materialization status and outputs are inconsistent", + )); + } + let missing_materialization_error = match (&self.error_code, &self.error_message) { + (std::option::Option::Some(code), std::option::Option::Some(message)) => { + code.trim().is_empty() || message.trim().is_empty() + }, + _ => true, + }; + if self.status == "failed" && missing_materialization_error { + return std::result::Result::Err(kb_core::Error::db( + "failed materialization bundle requires error code and message", + )); + } + for output in &self.outputs { + if output.output_key.trim().is_empty() + || output.source_event_key.trim().is_empty() + || output.materialized_family.trim().is_empty() + || output.processor_name != self.ledger_identity.processor_name + || output.processor_version != self.ledger_identity.processor_version + || output.input_key != self.ledger_identity.input_key + || output.input_hash != self.ledger_identity.input_hash + || output.signature != self.signature + { + return std::result::Result::Err(kb_core::Error::db( + "materialized output does not match bundle identity", + )); + } + } + return std::result::Result::Ok(()); + } +} + +/// One row of aggregated decoder coverage diagnostics. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecodeCoverageSummaryRow { + /// Stable decoder name. + pub processor_name: std::string::String, + /// Stable decoder version. + pub processor_version: std::string::String, + /// Exact Solana program identifier. + pub program_id: std::string::String, + /// Optional stable surface code. + pub surface_code: std::option::Option, + /// Stable entry code or unknown classifier. + pub entry_code: std::string::String, + /// Number of declared matching entries. + pub declared_count: i64, + /// Number of observed inputs. + pub observed_count: i64, + /// Number of recognized inputs. + pub recognized_count: i64, + /// Number of decoded observations. + pub decoded_count: i64, + /// Number of materialized outputs. + pub materialized_count: i64, + /// Number of errors. + pub error_count: i64, + /// Number of observations classified as unknown or unsupported. + pub unknown_count: i64, + /// Number of successful source transactions. + pub successful_transaction_count: i64, + /// Number of failed source transactions. + pub failed_transaction_count: i64, +} + +fn validate_text_list(values: &[std::string::String], label: &str) -> kb_core::Result<()> { + if values.iter().any(|value| return value.trim().is_empty()) { + return std::result::Result::Err(kb_core::Error::db(format!("{label} must not be empty"))); + } + return std::result::Result::Ok(()); +} + +fn trim_optional_text( + value: std::option::Option, +) -> std::option::Option { + return value.and_then(|text| { + let trimmed = text.trim(); + if trimmed.is_empty() { + return std::option::Option::None; + } + return std::option::Option::Some(trimmed.to_string()); + }); +} + +#[cfg(test)] +mod tests { + #[test] + fn actionable_filter_requires_positive_limit() { + assert!(crate::DecodeSelectionFilter::actionable(0).is_err()); + } + + #[test] + fn incomplete_signature_filter_preserves_signature_limit_semantics() { + let result = crate::DecodeSelectionFilter::new( + std::vec::Vec::new(), + std::vec![crate::CoreInstructionProcessingState::Failed], + std::option::Option::None, + std::option::Option::None, + std::vec::Vec::new(), + std::vec::Vec::new(), + true, + 25, + ); + let filter = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected filter error: {error}"), + }; + assert!(filter.incomplete_signatures); + assert_eq!(filter.limit, 25); + } + + #[test] + fn selection_filter_rejects_inverted_slots() { + let result = crate::DecodeSelectionFilter::new( + std::vec::Vec::new(), + std::vec::Vec::new(), + std::option::Option::Some(20), + std::option::Option::Some(10), + std::vec::Vec::new(), + std::vec::Vec::new(), + false, + 10, + ); + assert!(result.is_err()); + } + + #[test] + fn materialized_event_filter_is_bounded_and_trims_optional_text() { + let result = crate::MaterializedEventFilter::new( + std::option::Option::Some(" transaction_annotations ".to_string()), + std::option::Option::Some(" transaction_annotation ".to_string()), + std::option::Option::Some(" signature ".to_string()), + crate::MAX_MATERIALIZED_EVENT_QUERY_ROWS, + ); + let filter = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected filter error: {error}"), + }; + assert_eq!( + filter.processor_name.as_deref(), + std::option::Option::Some("transaction_annotations") + ); + assert_eq!(filter.signature_contains.as_deref(), std::option::Option::Some("signature")); + assert!( + crate::MaterializedEventFilter::new( + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + 0, + ) + .is_err() + ); + } + + #[test] + fn failed_decoded_observation_cannot_be_committed() { + let input = crate::DecodeObservationInsert { + processor_name: "decoder".to_string(), + processor_version: "1".to_string(), + input_key: "signature:0".to_string(), + input_hash: "hash".to_string(), + event_key: "event".to_string(), + signature: "signature".to_string(), + slot: 1, + instruction_path: "0".to_string(), + program_id: "program".to_string(), + protocol_code: "protocol".to_string(), + surface_code: "surface".to_string(), + event_code: "event".to_string(), + event_name: "event".to_string(), + event_family: "audit".to_string(), + source_kind: "instruction".to_string(), + confidence: "exact".to_string(), + proof_kind: "exact_layout".to_string(), + proof_json: serde_json::json!({}), + payload_json: serde_json::json!({}), + transaction_failed: true, + transaction_error: std::option::Option::Some(serde_json::json!({})), + observation_committed: true, + }; + assert!(input.validate().is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/event_dtos.rs b/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/event_dtos.rs new file mode 100644 index 0000000..01f905b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/event_dtos.rs @@ -0,0 +1,143 @@ +// file: kb_store_core/src/dtos/event_dtos.rs +// version: 1 + +//! Decoded and materialized event storage DTOs. + +/// Insert or upsert result contract returned by repositories. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct InsertOutcome { + /// Number of rows inserted by the repository call. + pub inserted_count: u64, + /// Number of rows updated by the repository call. + pub updated_count: u64, + /// Number of rows skipped by the repository call. + pub skipped_count: u64, +} + +impl InsertOutcome { + /// Builds an insert outcome from explicit counters. + pub fn new(inserted_count: u64, updated_count: u64, skipped_count: u64) -> Self { + return Self { + inserted_count, + updated_count, + skipped_count, + }; + } + + /// Returns the sum of inserted, updated and skipped rows. + pub fn total_count(&self) -> u64 { + return self.inserted_count + self.updated_count + self.skipped_count; + } +} + +/// Decoded event insert contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecodedEventInsert { + /// Transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Transaction slot in Solana unsigned representation. + pub slot: u64, + /// Stable instruction path, for example `0` or `2/1`. + pub instruction_path: std::string::String, + /// Program id as non-empty base58 text. + pub program_id: std::string::String, + /// Protocol family code. + pub protocol_code: std::string::String, + /// Protocol surface code. + pub surface_code: std::string::String, + /// Canonical event code. + pub event_code: std::string::String, + /// Decoded payload JSON. + pub payload_json: serde_json::Value, +} + +impl DecodedEventInsert { + /// Builds a decoded event insert contract from the shared decoded model. + pub fn from_model( + event: &kb_model::DecodedProtocolEvent, + payload_json: serde_json::Value, + ) -> kb_core::Result { + if event.signature.0.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "decoded event signature must not be empty", + )); + } + if event.program_id.0.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "decoded event program id must not be empty", + )); + } + return std::result::Result::Ok(Self { + signature: event.signature.0.clone(), + slot: event.slot.0, + instruction_path: event.instruction_path.0.clone(), + program_id: event.program_id.0.clone(), + protocol_code: event.protocol_code.0.clone(), + surface_code: event.surface_code.0.clone(), + event_code: event.event_code.0.clone(), + payload_json, + }); + } +} + +/// Materialized event insert contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct MaterializedEventInsert { + /// Source transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Source transaction slot in Solana unsigned representation. + pub slot: u64, + /// Materialized event family code. + pub materialized_family: std::string::String, + /// Materialized payload JSON. + pub payload_json: serde_json::Value, +} + +impl MaterializedEventInsert { + /// Builds a materialized event insert contract after minimal validation. + pub fn new( + signature: impl std::convert::Into, + slot: u64, + materialized_family: impl std::convert::Into, + payload_json: serde_json::Value, + ) -> kb_core::Result { + let signature_value = signature.into(); + let materialized_family_value = materialized_family.into(); + if signature_value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "materialized event signature must not be empty", + )); + } + if materialized_family_value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "materialized event family must not be empty", + )); + } + return std::result::Result::Ok(Self { + signature: signature_value, + slot, + materialized_family: materialized_family_value, + payload_json, + }); + } +} + +#[cfg(test)] +mod tests { + #[test] + fn insert_outcome_total_counts_all_buckets() { + let outcome = crate::InsertOutcome::new(1, 2, 3); + assert_eq!(outcome.total_count(), 6); + } + + #[test] + fn materialized_event_rejects_empty_family() { + let result = crate::MaterializedEventInsert::new( + "abc", + 1, + " ", + serde_json::json!({"kind": "trade"}), + ); + assert!(result.is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/ledger_dtos.rs b/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/ledger_dtos.rs new file mode 100644 index 0000000..b18053b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/ledger_dtos.rs @@ -0,0 +1,73 @@ +// file: kb_store_core/src/dtos/ledger_dtos.rs +// version: 1 + +//! Processing ledger storage DTOs. + +/// Processing ledger mark request contract. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct ProcessingLedgerMark { + /// Processing stage name, for example `raw_ingest`, `core_extract` or `decode`. + pub stage: std::string::String, + /// Processing module name. + pub module_name: std::string::String, + /// Processing module version. + pub module_version: std::string::String, + /// Stable input key, usually a signature or notification id. + pub input_key: std::string::String, +} + +impl ProcessingLedgerMark { + /// Builds a processing ledger mark request after minimal validation. + pub fn new( + stage: impl std::convert::Into, + module_name: impl std::convert::Into, + module_version: impl std::convert::Into, + input_key: impl std::convert::Into, + ) -> kb_core::Result { + let stage_value = stage.into(); + let module_name_value = module_name.into(); + let module_version_value = module_version.into(); + let input_key_value = input_key.into(); + if stage_value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "processing ledger stage must not be empty", + )); + } + if module_name_value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "processing ledger module name must not be empty", + )); + } + if module_version_value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "processing ledger module version must not be empty", + )); + } + if input_key_value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "processing ledger input key must not be empty", + )); + } + return std::result::Result::Ok(Self { + stage: stage_value, + module_name: module_name_value, + module_version: module_version_value, + input_key: input_key_value, + }); + } +} + +#[cfg(test)] +mod tests { + #[test] + fn ledger_mark_rejects_empty_input_key() { + let result = crate::ProcessingLedgerMark::new("decode", "module", "1", " "); + assert!(result.is_err()); + } + + #[test] + fn ledger_mark_accepts_minimal_values() { + let result = crate::ProcessingLedgerMark::new("decode", "module", "1", "signature"); + assert!(result.is_ok()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/raw_dtos.rs b/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/raw_dtos.rs new file mode 100644 index 0000000..29613cf --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/raw_dtos.rs @@ -0,0 +1,606 @@ +// file: kb_store_core/src/dtos/raw_dtos.rs +// version: 5 + +//! Canonical Solana transaction and acquisition observation storage DTOs. + +/// Retention state for a canonical raw transaction payload. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum RawPayloadRetentionState { + /// Full canonical payload is still present in the primary store. + Full, + /// Canonical payload was reduced to a compact audit representation. + Compacted, + /// Canonical payload was moved to an archive tier outside the primary hot store. + Archived, + /// Canonical payload was purged after derived data became authoritative enough. + Purged, +} + +/// Processing state for a canonical raw transaction payload. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum RawPayloadProcessingState { + /// Transaction was received but not extracted yet. + Received, + /// Generic Solana core data was extracted from the canonical transaction. + CoreExtracted, + /// Decoder outputs were produced from the canonical transaction or its core extraction. + Decoded, + /// Business projections were materialized from decoded or core data. + Materialized, + /// Processing failed and requires diagnostics or replay. + Failed, +} + +/// Origin of one transaction acquisition observation. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum TransactionObservationOrigin { + /// Transaction was observed from a current live stream. + Live, + /// Transaction was acquired by an explicit historical backfill. + Backfill, + /// Transaction was replayed from an already captured source. + Replay, + /// Transaction was fetched to repair an acquisition gap. + Repair, + /// Observation was converted from a historical storage table. + Migration, +} + +/// Technical status of one transaction acquisition observation. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum TransactionObservationStatus { + /// A transaction candidate was detected before a complete payload was received. + Detected, + /// A source payload was received. + Received, + /// A source payload was normalized into the canonical transaction contract. + Normalized, + /// The observation and any linked canonical transaction were persisted. + Persisted, + /// Acquisition or normalization failed. + Failed, + /// The source reported or implied a transaction that was temporarily unavailable. + Missing, +} + +/// Canonical raw Solana transaction insert contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct RawTransactionInsert { + /// Transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Transaction slot in Solana unsigned representation. + pub slot: u64, + /// Canonical source-independent transaction document. + pub canonical_json: serde_json::Value, + /// Optional deterministic digest of the canonical document. + pub canonical_json_hash: std::option::Option, + /// Positive version of the canonical transaction contract. + pub canonical_format_version: u32, +} + +impl RawTransactionInsert { + /// Builds a canonical raw transaction insert contract after minimal validation. + pub fn new( + signature: impl std::convert::Into, + slot: u64, + canonical_json: serde_json::Value, + canonical_format_version: u32, + ) -> kb_core::Result { + let signature_value = signature.into(); + if signature_value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "canonical transaction signature must not be empty", + )); + } + if canonical_format_version == 0 { + return std::result::Result::Err(kb_core::Error::db( + "canonical transaction format version must be greater than zero", + )); + } + return std::result::Result::Ok(Self { + signature: signature_value, + slot, + canonical_json, + canonical_json_hash: std::option::Option::None, + canonical_format_version, + }); + } + + /// Builds a storage insert from the source-independent canonical transaction model. + pub fn from_canonical(transaction: &kb_model::CanonicalTransaction) -> kb_core::Result { + let canonical_json_result = transaction.to_canonical_json(); + let canonical_json = match canonical_json_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let hash_result = transaction.canonical_json_hash(); + let hash = match hash_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let insert_result = Self::new( + transaction.primary_signature.clone(), + transaction.slot, + canonical_json, + transaction.format_version, + ); + let insert = match insert_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return insert.with_canonical_json_hash(hash); + } + + /// Adds a precomputed deterministic canonical document hash. + pub fn with_canonical_json_hash( + mut self, + canonical_json_hash: impl std::convert::Into, + ) -> kb_core::Result { + let hash_value = canonical_json_hash.into(); + if hash_value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "canonical transaction hash must not be empty", + )); + } + self.canonical_json_hash = std::option::Option::Some(hash_value); + return std::result::Result::Ok(self); + } +} + +/// Lightweight transaction acquisition observation insert contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct TransactionObservationInsert { + /// Stable observation key used for deduplication. + pub observation_key: std::string::String, + /// Optional known canonical transaction row id. + pub raw_transaction_id: std::option::Option, + /// Optional transaction signature when known. + pub signature: std::option::Option, + /// Optional transaction slot when known. + pub slot: std::option::Option, + /// Provider code. + pub provider: std::string::String, + /// Optional endpoint code from the active configuration. + pub endpoint_code: std::option::Option, + /// Source protocol. + pub protocol: std::string::String, + /// Source acquisition method. + pub acquisition_method: std::string::String, + /// Observation origin. + pub origin: TransactionObservationOrigin, + /// Optional Solana commitment. + pub commitment: std::option::Option, + /// Optional capture session identifier. + pub capture_session_id: std::option::Option, + /// Optional configured filter code. + pub filter_code: std::option::Option, + /// Optional first detection timestamp. + pub detected_at: std::option::Option>, + /// Timestamp at which the source payload was received locally. + pub received_at: chrono::DateTime, + /// Optional timestamp at which canonical normalization completed. + pub normalized_at: std::option::Option>, + /// Optional uncompressed source payload size in bytes. + pub payload_size_bytes: std::option::Option, + /// Optional digest of the source-specific payload without retaining that payload. + pub source_payload_hash: std::option::Option, + /// Observation status. + pub status: TransactionObservationStatus, + /// Optional machine-readable error code. + pub error_code: std::option::Option, + /// Optional diagnostic error message. + pub error_message: std::option::Option, +} + +impl TransactionObservationInsert { + /// Builds a lightweight transaction observation after validating required source metadata. + pub fn new( + observation_key: impl std::convert::Into, + provider: impl std::convert::Into, + protocol: impl std::convert::Into, + acquisition_method: impl std::convert::Into, + origin: TransactionObservationOrigin, + received_at: chrono::DateTime, + ) -> kb_core::Result { + let observation_key_value = observation_key.into(); + let provider_value = provider.into(); + let protocol_value = protocol.into(); + let acquisition_method_value = acquisition_method.into(); + let validation_result = validate_required_observation_texts( + observation_key_value.as_str(), + provider_value.as_str(), + protocol_value.as_str(), + acquisition_method_value.as_str(), + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(Self { + observation_key: observation_key_value, + raw_transaction_id: std::option::Option::None, + signature: std::option::Option::None, + slot: std::option::Option::None, + provider: provider_value, + endpoint_code: std::option::Option::None, + protocol: protocol_value, + acquisition_method: acquisition_method_value, + origin, + commitment: std::option::Option::None, + capture_session_id: std::option::Option::None, + filter_code: std::option::Option::None, + detected_at: std::option::Option::None, + received_at, + normalized_at: std::option::Option::None, + payload_size_bytes: std::option::Option::None, + source_payload_hash: std::option::Option::None, + status: TransactionObservationStatus::Received, + error_code: std::option::Option::None, + error_message: std::option::Option::None, + }); + } + + /// Links the observation to a known canonical transaction row id. + pub fn with_raw_transaction_id(mut self, raw_transaction_id: i64) -> kb_core::Result { + if raw_transaction_id <= 0 { + return std::result::Result::Err(kb_core::Error::db( + "transaction observation raw transaction id must be greater than zero", + )); + } + self.raw_transaction_id = std::option::Option::Some(raw_transaction_id); + return std::result::Result::Ok(self); + } + + /// Adds the transaction signature and optional slot carried by the source. + pub fn with_transaction_identity( + mut self, + signature: impl std::convert::Into, + slot: std::option::Option, + ) -> kb_core::Result { + let signature_value = signature.into(); + if signature_value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "transaction observation signature must not be empty", + )); + } + self.signature = std::option::Option::Some(signature_value); + self.slot = slot; + return std::result::Result::Ok(self); + } + + /// Adds an endpoint code after validating non-empty optional text. + pub fn with_endpoint_code( + mut self, + endpoint_code: impl std::convert::Into, + ) -> kb_core::Result { + let endpoint_code_value = endpoint_code.into(); + let validation_result = validate_optional_text( + endpoint_code_value.as_str(), + "transaction observation endpoint code must not be empty", + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + self.endpoint_code = std::option::Option::Some(endpoint_code_value); + return std::result::Result::Ok(self); + } + + /// Adds an optional commitment value after validation. + pub fn with_commitment( + mut self, + commitment: impl std::convert::Into, + ) -> kb_core::Result { + let commitment_value = commitment.into(); + let validation_result = validate_optional_text( + commitment_value.as_str(), + "transaction observation commitment must not be empty", + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + self.commitment = std::option::Option::Some(commitment_value); + return std::result::Result::Ok(self); + } + + /// Adds optional capture session and filter codes after validation. + pub fn with_capture_context( + mut self, + capture_session_id: std::option::Option, + filter_code: std::option::Option, + ) -> kb_core::Result { + let session_result = validate_optional_owned_text( + capture_session_id.as_ref(), + "transaction observation capture session id must not be empty when present", + ); + if let std::result::Result::Err(error) = session_result { + return std::result::Result::Err(error); + } + let filter_result = validate_optional_owned_text( + filter_code.as_ref(), + "transaction observation filter code must not be empty when present", + ); + if let std::result::Result::Err(error) = filter_result { + return std::result::Result::Err(error); + } + self.capture_session_id = capture_session_id; + self.filter_code = filter_code; + return std::result::Result::Ok(self); + } + + /// Adds detection and normalization timestamps. + pub fn with_timings( + mut self, + detected_at: std::option::Option>, + normalized_at: std::option::Option>, + ) -> Self { + self.detected_at = detected_at; + self.normalized_at = normalized_at; + return self; + } + + /// Adds source payload size and hash metadata without retaining the source payload. + pub fn with_payload_metadata( + mut self, + payload_size_bytes: std::option::Option, + source_payload_hash: std::option::Option, + ) -> kb_core::Result { + let hash_result = validate_optional_owned_text( + source_payload_hash.as_ref(), + "transaction observation source payload hash must not be empty when present", + ); + if let std::result::Result::Err(error) = hash_result { + return std::result::Result::Err(error); + } + self.payload_size_bytes = payload_size_bytes; + self.source_payload_hash = source_payload_hash; + return std::result::Result::Ok(self); + } + + /// Replaces the current observation status. + pub fn with_status(mut self, status: TransactionObservationStatus) -> Self { + self.status = status; + return self; + } + + /// Adds an acquisition error and marks the observation as failed. + pub fn with_error( + mut self, + error_code: impl std::convert::Into, + error_message: std::option::Option, + ) -> kb_core::Result { + let error_code_value = error_code.into(); + let code_result = validate_optional_text( + error_code_value.as_str(), + "transaction observation error code must not be empty", + ); + if let std::result::Result::Err(error) = code_result { + return std::result::Result::Err(error); + } + let message_result = validate_optional_owned_text( + error_message.as_ref(), + "transaction observation error message must not be empty when present", + ); + if let std::result::Result::Err(error) = message_result { + return std::result::Result::Err(error); + } + self.error_code = std::option::Option::Some(error_code_value); + self.error_message = error_message; + self.status = TransactionObservationStatus::Failed; + return std::result::Result::Ok(self); + } +} + +/// Canonical raw transaction lifecycle mark request. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct RawPayloadLifecycleMark { + /// Physical canonical raw table name using the `kb_sol__` convention. + pub raw_table_name: std::string::String, + /// Stable canonical raw row key, currently the transaction signature. + pub raw_row_key: std::string::String, + /// Retention state to record for the canonical payload. + pub retention_state: RawPayloadRetentionState, + /// Processing state to record for the canonical payload. + pub processing_state: RawPayloadProcessingState, + /// Optional reason visible in diagnostics. + pub reason: std::option::Option, +} + +impl RawPayloadLifecycleMark { + /// Builds a canonical raw payload lifecycle mark after minimal validation. + pub fn new( + raw_table_name: impl std::convert::Into, + raw_row_key: impl std::convert::Into, + retention_state: RawPayloadRetentionState, + processing_state: RawPayloadProcessingState, + reason: std::option::Option, + ) -> kb_core::Result { + let raw_table_name_value = raw_table_name.into(); + let raw_row_key_value = raw_row_key.into(); + if raw_table_name_value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "raw lifecycle table name must not be empty", + )); + } + if raw_row_key_value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "raw lifecycle row key must not be empty", + )); + } + let reason_result = validate_optional_owned_text( + reason.as_ref(), + "raw lifecycle reason must not be empty when present", + ); + if let std::result::Result::Err(error) = reason_result { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(Self { + raw_table_name: raw_table_name_value, + raw_row_key: raw_row_key_value, + retention_state, + processing_state, + reason, + }); + } +} + +fn validate_required_observation_texts( + observation_key: &str, + provider: &str, + protocol: &str, + acquisition_method: &str, +) -> kb_core::Result<()> { + let values = [ + (observation_key, "transaction observation key must not be empty"), + (provider, "transaction observation provider must not be empty"), + (protocol, "transaction observation protocol must not be empty"), + ( + acquisition_method, + "transaction observation acquisition method must not be empty", + ), + ]; + for (value, message) in values { + if value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db(message)); + } + } + return std::result::Result::Ok(()); +} + +fn validate_optional_text(value: &str, message: &str) -> kb_core::Result<()> { + if value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db(message)); + } + return std::result::Result::Ok(()); +} + +fn validate_optional_owned_text( + value: std::option::Option<&std::string::String>, + message: &str, +) -> kb_core::Result<()> { + if let std::option::Option::Some(text) = value { + if text.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db(message)); + } + } + return std::result::Result::Ok(()); +} + +#[cfg(test)] +mod tests { + #[test] + fn raw_transaction_rejects_empty_signature() { + let result = crate::RawTransactionInsert::new(" ", 1, serde_json::json!({"ok": true}), 1); + assert!(result.is_err()); + } + + #[test] + fn raw_transaction_rejects_zero_format_version() { + let result = crate::RawTransactionInsert::new("abc", 1, serde_json::json!({"ok": true}), 0); + assert!(result.is_err()); + } + + fn canonical_transaction_fixture() -> kb_model::CanonicalTransaction { + return kb_model::CanonicalTransaction { + format_version: kb_model::CANONICAL_TRANSACTION_FORMAT_VERSION, + primary_signature: "2Ana1pUpv2ZbMVkwF5FXapYeBEjdxDatLn7nvJkhgTSXbs59SyZSx866bXirPgj8QQVB57uxHJBG1YFvkRbFj4T".to_string(), + slot: 9, + block_time: std::option::Option::None, + version: kb_model::CanonicalTransactionVersion::Legacy, + signatures: std::vec![ + "2Ana1pUpv2ZbMVkwF5FXapYeBEjdxDatLn7nvJkhgTSXbs59SyZSx866bXirPgj8QQVB57uxHJBG1YFvkRbFj4T".to_string(), + ], + message: kb_model::CanonicalTransactionMessage { + header: kb_model::CanonicalMessageHeader { + num_required_signatures: 1, + num_readonly_signed_accounts: 0, + num_readonly_unsigned_accounts: 1, + }, + static_account_keys: std::vec![ + "11111111111111111111111111111111".to_string(), + "ComputeBudget111111111111111111111111111111".to_string(), + ], + recent_blockhash: "11111111111111111111111111111111".to_string(), + instructions: std::vec![kb_model::CanonicalCompiledInstruction { + program_id_index: 1, + account_indexes: std::vec![0], + data_base64: "AQ==".to_string(), + stack_height: std::option::Option::Some(1), + }], + address_table_lookups: std::vec::Vec::new(), + loaded_addresses: kb_model::CanonicalLoadedAddresses::default(), + }, + metadata: std::option::Option::Some(kb_model::CanonicalTransactionMetadata { + status: kb_model::CanonicalTransactionStatus::Success, + error: std::option::Option::None, + fee: 5000, + pre_balances: std::vec![10000, 1], + post_balances: std::vec![5000, 1], + inner_instructions: std::vec::Vec::new(), + log_messages: std::vec::Vec::new(), + pre_token_balances: std::vec::Vec::new(), + post_token_balances: std::vec::Vec::new(), + rewards: std::vec::Vec::new(), + return_data: std::option::Option::None, + compute_units_consumed: std::option::Option::Some(100), + cost_units: std::option::Option::None, + }), + }; + } + + #[test] + fn raw_transaction_builds_from_canonical_model() { + let transaction = canonical_transaction_fixture(); + let result = crate::RawTransactionInsert::from_canonical(&transaction); + let insert = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("canonical insert failed: {error}"), + }; + assert_eq!(insert.signature, transaction.primary_signature); + assert_eq!(insert.slot, transaction.slot); + assert_eq!(insert.canonical_format_version, kb_model::CANONICAL_TRANSACTION_FORMAT_VERSION); + assert_eq!(insert.canonical_json_hash.as_deref().map(|value| return value.len()), Some(64)); + } + + #[test] + fn raw_transaction_accepts_canonical_payload() { + let result = crate::RawTransactionInsert::new("abc", 1, serde_json::json!({"ok": true}), 1); + assert!(result.is_ok()); + } + + #[test] + fn transaction_observation_rejects_empty_provider() { + let result = crate::TransactionObservationInsert::new( + "obs:1", + " ", + "solana_http", + "getTransaction", + crate::TransactionObservationOrigin::Backfill, + chrono::Utc::now(), + ); + assert!(result.is_err()); + } + + #[test] + fn transaction_observation_accepts_signatureless_failure_candidate() { + let result = crate::TransactionObservationInsert::new( + "obs:1", + "helius", + "solana_http", + "getTransaction", + crate::TransactionObservationOrigin::Repair, + chrono::Utc::now(), + ); + assert!(result.is_ok()); + } + + #[test] + fn raw_lifecycle_rejects_empty_reason_when_present() { + let result = crate::RawPayloadLifecycleMark::new( + "kb_sol_raw_transactions", + "signature", + crate::RawPayloadRetentionState::Full, + crate::RawPayloadProcessingState::Received, + std::option::Option::Some(" ".to_string()), + ); + assert!(result.is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/store_dtos.rs b/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/store_dtos.rs new file mode 100644 index 0000000..a365797 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/dtos/store_dtos.rs @@ -0,0 +1,125 @@ +// file: kb_store_core/src/dtos/store_dtos.rs +// version: 2 + +//! Store backend diagnostic DTOs. + +/// Store backend kind contract. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum StoreBackendKind { + /// PostgreSQL backend. + Postgres, + /// SQLite backend retained for tests and legacy imports. + Sqlite, + /// In-memory backend used by offline tests. + Memory, + /// Backend is not known. + Unknown, +} + +/// Store migration status contract. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum StoreMigrationStatus { + /// Migration status is not known yet. + Unknown, + /// Store has no migration table or migration history yet. + NotInitialized, + /// Store migrations are current. + Current, + /// Store has pending migrations. + Pending, + /// Store migration history is inconsistent. + Drift, + /// Store migration check failed. + Failed, +} + +/// Store backend diagnostic contract. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct StoreBackendDescriptor { + /// Store backend kind. + pub backend_kind: StoreBackendKind, + /// Human-readable backend label. + pub backend_label: std::string::String, + /// Masked DSN or connection descriptor safe for diagnostics. + pub masked_dsn: std::option::Option, + /// Current PostgreSQL schema or equivalent namespace when known. + pub current_schema: std::option::Option, +} + +impl StoreBackendDescriptor { + /// Builds a store backend descriptor after minimal validation. + pub fn new( + backend_kind: StoreBackendKind, + backend_label: impl std::convert::Into, + masked_dsn: std::option::Option, + current_schema: std::option::Option, + ) -> kb_core::Result { + let backend_label_value = backend_label.into(); + if backend_label_value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "store backend label must not be empty", + )); + } + return std::result::Result::Ok(Self { + backend_kind, + backend_label: backend_label_value, + masked_dsn, + current_schema, + }); + } +} + +/// Store migration diagnostic snapshot contract. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct StoreMigrationSnapshot { + /// Current migration status. + pub status: StoreMigrationStatus, + /// Last applied migration identifier when known. + pub current_version: std::option::Option, + /// Pending migration identifiers when known. + pub pending_versions: std::vec::Vec, + /// Optional human-readable diagnostic message. + pub message: std::option::Option, +} + +impl StoreMigrationSnapshot { + /// Builds a migration snapshot from explicit values. + pub fn new( + status: StoreMigrationStatus, + current_version: std::option::Option, + pending_versions: std::vec::Vec, + message: std::option::Option, + ) -> Self { + return Self { + status, + current_version, + pending_versions, + message, + }; + } +} + +#[cfg(test)] +mod tests { + #[test] + fn store_descriptor_rejects_empty_label() { + let result = crate::StoreBackendDescriptor::new( + crate::StoreBackendKind::Postgres, + " ", + std::option::Option::None, + std::option::Option::None, + ); + assert!(result.is_err()); + } + + #[test] + fn migration_snapshot_keeps_pending_versions() { + let snapshot = crate::StoreMigrationSnapshot::new( + crate::StoreMigrationStatus::Pending, + std::option::Option::None, + std::vec![std::string::String::from("0001")], + std::option::Option::None, + ); + assert_eq!(snapshot.pending_versions.len(), 1); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/entities.rs b/migration/khadhroony-bot2-reference/kb_store_core/src/entities.rs new file mode 100644 index 0000000..2129f32 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/entities.rs @@ -0,0 +1,32 @@ +// file: kb_store_core/src/entities.rs +// version: 5 + +//! Backend-neutral SQL-like entity exports for storage adapters. + +mod core_entities; +mod event_entities; +mod ledger_entities; +mod raw_entities; + +/// Core account key SQL-like row contract. +pub use crate::entities::core_entities::CoreAccountKeyRow; +/// Core balance change SQL-like row contract. +pub use crate::entities::core_entities::CoreBalanceChangeRow; +/// Core inner instruction SQL-like row contract. +pub use crate::entities::core_entities::CoreInnerInstructionRow; +/// Core instruction SQL-like row contract. +pub use crate::entities::core_entities::CoreInstructionRow; +/// Core log SQL-like row contract. +pub use crate::entities::core_entities::CoreLogRow; +/// Core transaction SQL-like row contract. +pub use crate::entities::core_entities::CoreTransactionRow; +/// Decoded event SQL-like row contract. +pub use crate::entities::event_entities::DecodedEventRow; +/// Materialized event SQL-like row contract. +pub use crate::entities::event_entities::MaterializedEventRow; +/// Processing ledger SQL-like row contract. +pub use crate::entities::ledger_entities::ProcessingLedgerRow; +/// Canonical raw Solana transaction SQL-like row contract. +pub use crate::entities::raw_entities::RawTransactionRow; +/// Transaction acquisition observation SQL-like row contract. +pub use crate::entities::raw_entities::TransactionObservationRow; diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/entities/README.md b/migration/khadhroony-bot2-reference/kb_store_core/src/entities/README.md new file mode 100644 index 0000000..11923d8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/entities/README.md @@ -0,0 +1,26 @@ + + + +# Entities + +Ce dossier contient les représentations proches des lignes SQL. + +## Règles + +- Une entity reste proche d'une table ou d'une ligne logique stockée. +- Les timestamps utilisent `chrono::DateTime` côté Rust. +- Les slots proches SQL utilisent `i64`, car PostgreSQL stockera `slot` en `BIGINT`. +- Les payloads bruts utilisent `raw_json`. +- Les payloads décodés ou matérialisés utilisent `payload_json`. +- Les payloads purgables peuvent devenir optionnels si un hash stable est conservé. +- Les entities ne contiennent pas de SQL, de bind ou d'exécution. + +## Fichiers actuels + +| Fichier | Rôle | +|----------------------|-------------------------------------------------------------------------------| +| `raw_entities.rs` | Lignes raw RPC et raw WebSocket. | +| `core_entities.rs` | Lignes core transaction, account keys, instructions, logs et balance changes. | +| `event_entities.rs` | Lignes decode et materialization. | +| `ledger_entities.rs` | Lignes du ledger de traitement. | + diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/entities/core_entities.rs b/migration/khadhroony-bot2-reference/kb_store_core/src/entities/core_entities.rs new file mode 100644 index 0000000..2c12a5b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/entities/core_entities.rs @@ -0,0 +1,166 @@ +// file: kb_store_core/src/entities/core_entities.rs +// version: 5 + +//! Core Solana SQL-like entities. + +/// Core transaction SQL-like row contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoreTransactionRow { + /// Technical primary key. + pub id: i64, + /// Transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Transaction slot stored as SQL `BIGINT`. + pub slot: i64, + /// Whether the transaction failed on-chain. + pub failed: bool, + /// Optional canonical raw transaction row id used for lineage when available. + pub raw_transaction_id: std::option::Option, + /// Optional raw error JSON extracted from transaction metadata. + pub err_json: std::option::Option, + /// Insert timestamp. + pub created_at: chrono::DateTime, + /// Last update timestamp. + pub updated_at: chrono::DateTime, +} + +/// Core account key SQL-like row contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoreAccountKeyRow { + /// Technical primary key. + pub id: i64, + /// Parent core transaction technical key. + pub transaction_id: i64, + /// Transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Transaction slot stored as SQL `BIGINT`. + pub slot: i64, + /// Stable account index after static and loaded keys are resolved. + pub account_index: i32, + /// Account public key as non-empty base58 text. + pub account_key: std::string::String, + /// Source category for this account key. + pub source: crate::CoreAccountKeySource, + /// Whether the resolved account is writable for the transaction. + pub writable: bool, + /// Whether the resolved account signed the transaction. + pub signer: bool, + /// Whether the resolved account is executable when known. + pub executable: std::option::Option, + /// Insert timestamp. + pub created_at: chrono::DateTime, +} + +/// Core instruction SQL-like row contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoreInstructionRow { + /// Technical primary key. + pub id: i64, + /// Parent core transaction technical key. + pub transaction_id: i64, + /// Transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Transaction slot stored as SQL `BIGINT`. + pub slot: i64, + /// Stable instruction path, for example `0` or `2/1`. + pub instruction_path: std::string::String, + /// Program id as non-empty base58 text. + pub program_id: std::string::String, + /// Instruction accounts as JSON, preserving unresolved forms when needed. + pub accounts_json: serde_json::Value, + /// Instruction payload JSON while it is still retained in the hot store. + pub payload_json: std::option::Option, + /// Optional digest of the instruction payload after compaction or purge. + pub payload_json_hash: std::option::Option, + /// Current processing state used by instruction-level replay. + pub processing_state: crate::CoreInstructionProcessingState, + /// Insert timestamp. + pub created_at: chrono::DateTime, + /// Last lifecycle update timestamp. + pub updated_at: chrono::DateTime, +} + +/// Core inner instruction SQL-like row contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoreInnerInstructionRow { + /// Technical primary key. + pub id: i64, + /// Parent core transaction technical key. + pub transaction_id: i64, + /// Transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Transaction slot stored as SQL `BIGINT`. + pub slot: i64, + /// Parent top-level or inner instruction path. + pub parent_instruction_path: std::string::String, + /// Stable inner instruction path, for example `2/1`. + pub instruction_path: std::string::String, + /// Program id as non-empty base58 text. + pub program_id: std::string::String, + /// Inner instruction accounts as JSON. + pub accounts_json: serde_json::Value, + /// Inner instruction payload JSON while it is retained in the hot store. + pub payload_json: std::option::Option, + /// Optional digest of the inner instruction payload after compaction or purge. + pub payload_json_hash: std::option::Option, + /// Insert timestamp. + pub created_at: chrono::DateTime, +} + +/// Core log SQL-like row contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoreLogRow { + /// Technical primary key. + pub id: i64, + /// Parent core transaction technical key. + pub transaction_id: i64, + /// Transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Transaction slot stored as SQL `BIGINT`. + pub slot: i64, + /// Log index preserving transaction log order. + pub log_index: i32, + /// Optional instruction path resolved from invocation depth when known. + pub instruction_path: std::option::Option, + /// Optional program id resolved from the log line or invocation context. + pub program_id: std::option::Option, + /// Original log text while it is retained in the hot store. + pub log_text: std::option::Option, + /// Optional digest of the log text after compaction or purge. + pub log_text_hash: std::option::Option, + /// Insert timestamp. + pub created_at: chrono::DateTime, +} + +/// Core balance change SQL-like row contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct CoreBalanceChangeRow { + /// Technical primary key. + pub id: i64, + /// Parent core transaction technical key. + pub transaction_id: i64, + /// Transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Transaction slot stored as SQL `BIGINT`. + pub slot: i64, + /// Stable balance change index preserving extraction order. + pub balance_change_index: i32, + /// Balance change family. + pub balance_kind: crate::CoreBalanceChangeKind, + /// Optional account index when available. + pub account_index: std::option::Option, + /// Optional account public key when available. + pub account_key: std::option::Option, + /// Optional SPL token mint for token balances. + pub mint: std::option::Option, + /// Optional owner public key for token balances. + pub owner: std::option::Option, + /// Pre-balance JSON value preserving RPC representation. + pub pre_balance_json: std::option::Option, + /// Post-balance JSON value preserving RPC representation. + pub post_balance_json: std::option::Option, + /// Delta JSON value preserving integer or decimal-safe representation. + pub delta_json: std::option::Option, + /// Insert timestamp. + pub created_at: chrono::DateTime, +} diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/entities/event_entities.rs b/migration/khadhroony-bot2-reference/kb_store_core/src/entities/event_entities.rs new file mode 100644 index 0000000..2e6f068 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/entities/event_entities.rs @@ -0,0 +1,46 @@ +// file: kb_store_core/src/entities/event_entities.rs +// version: 1 + +//! Decoded and materialized event SQL-like entities. + +/// Decoded event SQL-like row contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct DecodedEventRow { + /// Technical primary key. + pub id: i64, + /// Transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Transaction slot stored as SQL `BIGINT`. + pub slot: i64, + /// Stable instruction path, for example `0` or `2/1`. + pub instruction_path: std::string::String, + /// Program id as non-empty base58 text. + pub program_id: std::string::String, + /// Protocol family code. + pub protocol_code: std::string::String, + /// Protocol surface code. + pub surface_code: std::string::String, + /// Canonical event code. + pub event_code: std::string::String, + /// Decoded payload JSON. + pub payload_json: serde_json::Value, + /// Insert timestamp. + pub created_at: chrono::DateTime, +} + +/// Materialized event SQL-like row contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct MaterializedEventRow { + /// Technical primary key. + pub id: i64, + /// Source transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Source transaction slot stored as SQL `BIGINT`. + pub slot: i64, + /// Materialized event family code. + pub materialized_family: std::string::String, + /// Materialized payload JSON. + pub payload_json: serde_json::Value, + /// Insert timestamp. + pub created_at: chrono::DateTime, +} diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/entities/ledger_entities.rs b/migration/khadhroony-bot2-reference/kb_store_core/src/entities/ledger_entities.rs new file mode 100644 index 0000000..f1e3a02 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/entities/ledger_entities.rs @@ -0,0 +1,37 @@ +// file: kb_store_core/src/entities/ledger_entities.rs +// version: 2 + +//! Processing ledger SQL-like entities. + +/// Processing ledger SQL-like row contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct ProcessingLedgerRow { + /// Technical primary key. + pub id: i64, + /// Processing stage name. + pub stage: std::string::String, + /// Processor implementation name. + pub processor_name: std::string::String, + /// Processor semantic version. + pub processor_version: std::string::String, + /// Stable input key, usually a transaction signature. + pub input_key: std::string::String, + /// Deterministic input hash. + pub input_hash: std::string::String, + /// Current processing status. + pub status: crate::ProcessingLedgerStatus, + /// Number of started attempts for this processor identity. + pub attempt_count: i32, + /// Optional start timestamp for the latest attempt. + pub started_at: std::option::Option>, + /// Optional finish timestamp for the latest terminal attempt. + pub finished_at: std::option::Option>, + /// Optional machine-readable error code. + pub error_code: std::option::Option, + /// Optional diagnostic error message. + pub error_message: std::option::Option, + /// Insert timestamp. + pub created_at: chrono::DateTime, + /// Last update timestamp. + pub updated_at: chrono::DateTime, +} diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/entities/raw_entities.rs b/migration/khadhroony-bot2-reference/kb_store_core/src/entities/raw_entities.rs new file mode 100644 index 0000000..5d1f34b --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/entities/raw_entities.rs @@ -0,0 +1,78 @@ +// file: kb_store_core/src/entities/raw_entities.rs +// version: 3 + +//! Canonical Solana transaction and acquisition observation SQL-like entities. + +/// Canonical raw Solana transaction SQL-like row contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct RawTransactionRow { + /// Technical primary key. + pub id: i64, + /// Transaction signature as non-empty base58 text. + pub signature: std::string::String, + /// Transaction slot stored as SQL `BIGINT`. + pub slot: i64, + /// Canonical source-independent transaction document while retained in the hot store. + pub canonical_json: std::option::Option, + /// Optional deterministic digest of the canonical document. + pub canonical_json_hash: std::option::Option, + /// Version of the canonical transaction document contract. + pub canonical_format_version: i32, + /// Current raw payload retention state. + pub retention_state: crate::RawPayloadRetentionState, + /// Current processing state derived from this canonical transaction. + pub processing_state: crate::RawPayloadProcessingState, + /// Insert timestamp. + pub created_at: chrono::DateTime, + /// Last lifecycle update timestamp. + pub updated_at: chrono::DateTime, +} + +/// Transaction acquisition observation SQL-like row contract. +#[derive(Clone, Debug, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct TransactionObservationRow { + /// Technical primary key. + pub id: i64, + /// Optional linked canonical transaction row id. + pub raw_transaction_id: std::option::Option, + /// Stable observation key used for deduplication. + pub observation_key: std::string::String, + /// Optional transaction signature when known. + pub signature: std::option::Option, + /// Optional transaction slot stored as SQL `BIGINT`. + pub slot: std::option::Option, + /// Provider code, for example `helius`, `triton` or `legacy_unknown`. + pub provider: std::string::String, + /// Optional endpoint code from the active configuration. + pub endpoint_code: std::option::Option, + /// Acquisition protocol, for example `solana_http`, `solana_websocket` or `yellowstone_grpc`. + pub protocol: std::string::String, + /// Acquisition method, for example `getTransaction`, `transactionSubscribe` or `transactions`. + pub acquisition_method: std::string::String, + /// Acquisition origin category. + pub origin: crate::TransactionObservationOrigin, + /// Optional Solana commitment. + pub commitment: std::option::Option, + /// Optional capture session identifier. + pub capture_session_id: std::option::Option, + /// Optional configured filter code. + pub filter_code: std::option::Option, + /// Optional first detection timestamp. + pub detected_at: std::option::Option>, + /// Timestamp at which the source payload was received locally. + pub received_at: chrono::DateTime, + /// Optional timestamp at which canonical normalization completed. + pub normalized_at: std::option::Option>, + /// Timestamp at which the observation was persisted. + pub persisted_at: chrono::DateTime, + /// Optional uncompressed source payload size in bytes. + pub payload_size_bytes: std::option::Option, + /// Optional digest of the source-specific payload without retaining that payload. + pub source_payload_hash: std::option::Option, + /// Current observation status. + pub status: crate::TransactionObservationStatus, + /// Optional machine-readable error code. + pub error_code: std::option::Option, + /// Optional diagnostic error message. + pub error_message: std::option::Option, +} diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/health.rs b/migration/khadhroony-bot2-reference/kb_store_core/src/health.rs new file mode 100644 index 0000000..5fb06d7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/health.rs @@ -0,0 +1,58 @@ +// file: kb_store_core/src/health.rs +// version: 2 + +//! Backend-neutral health contracts for storage implementations. + +/// Store backend health status. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum StoreHealthStatus { + /// Health is not known yet. + Unknown, + /// Backend is reachable and usable. + Healthy, + /// Backend is reachable but not fully usable. + Degraded, + /// Backend is not usable. + Unhealthy, +} + +/// Store backend health snapshot. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct StoreHealthSnapshot { + /// Stable backend code such as `postgres` or `sqlite`. + pub backend: std::string::String, + /// Current health status. + pub status: StoreHealthStatus, + /// Optional human-readable diagnostic message. + pub message: std::option::Option, +} + +impl StoreHealthSnapshot { + /// Builds a store health snapshot after minimal validation. + pub fn new( + backend: impl std::convert::Into, + status: StoreHealthStatus, + message: std::option::Option, + ) -> kb_core::Result { + let backend_value = backend.into(); + if backend_value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "store health backend must not be empty", + )); + } + return std::result::Result::Ok(Self { backend: backend_value, status, message }); + } +} + +#[cfg(test)] +mod tests { + #[test] + fn health_snapshot_rejects_empty_backend() { + let result = crate::StoreHealthSnapshot::new( + " ", + crate::StoreHealthStatus::Unknown, + std::option::Option::None, + ); + assert!(result.is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/lib.rs b/migration/khadhroony-bot2-reference/kb_store_core/src/lib.rs new file mode 100644 index 0000000..6f63f31 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/lib.rs @@ -0,0 +1,159 @@ +// file: kb_store_core/src/lib.rs +// version: 14 + +//! Backend-neutral storage contracts used by pipeline crates. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod db_error; +mod dtos; +mod entities; +mod health; +mod pagination; +mod repositories; + +/// Storage error helper functions. +pub use crate::db_error::storage_contract_error; +/// Current normalized core replay input contract version. +pub use crate::dtos::CORE_REPLAY_INPUT_CONTRACT_VERSION; +/// Core account key insert contract. +pub use crate::dtos::CoreAccountKeyInsert; +/// Core account key source category. +pub use crate::dtos::CoreAccountKeySource; +/// Core balance change insert contract. +pub use crate::dtos::CoreBalanceChangeInsert; +/// Core balance change kind. +pub use crate::dtos::CoreBalanceChangeKind; +/// Complete normalized core extraction write bundle. +pub use crate::dtos::CoreExtractionBundle; +/// Failure details persisted for one canonical to core extraction attempt. +pub use crate::dtos::CoreExtractionFailure; +/// Bounded canonical transaction selection filter for core extraction. +pub use crate::dtos::CoreExtractionSelectionFilter; +/// Core inner instruction insert contract. +pub use crate::dtos::CoreInnerInstructionInsert; +/// Core instruction insert contract. +pub use crate::dtos::CoreInstructionInsert; +/// Core instruction lifecycle mark request. +pub use crate::dtos::CoreInstructionLifecycleMark; +/// Core instruction processing state. +pub use crate::dtos::CoreInstructionProcessingState; +/// Core instruction replay filter contract. +pub use crate::dtos::CoreInstructionReplayFilter; +/// Decoder replay input containing one instruction plus extracted transaction context. +pub use crate::dtos::CoreInstructionReplayInput; +/// Core log insert contract. +pub use crate::dtos::CoreLogInsert; +/// Core transaction insert contract. +pub use crate::dtos::CoreTransactionInsert; +/// One machine-readable decoder coverage declaration row. +pub use crate::dtos::DecodeCoverageDeclarationInsert; +/// One observed coverage classification row owned by one decode attempt. +pub use crate::dtos::DecodeCoverageObservationInsert; +/// One row of aggregated decoder coverage diagnostics. +pub use crate::dtos::DecodeCoverageSummaryRow; +/// Failed decode attempt persisted in the common ledger. +pub use crate::dtos::DecodeFailure; +/// One processor-owned decoded observation row. +pub use crate::dtos::DecodeObservationInsert; +/// Atomic persistence bundle for one decoder and one contextual input. +pub use crate::dtos::DecodePersistenceBundle; +/// Bounded contextual instruction selection filter for decode campaigns. +pub use crate::dtos::DecodeSelectionFilter; +/// Decoded event insert contract. +pub use crate::dtos::DecodedEventInsert; +/// Insert or upsert result contract returned by repositories. +pub use crate::dtos::InsertOutcome; +/// Maximum number of materialized rows returned by one bounded query. +pub use crate::dtos::MAX_MATERIALIZED_EVENT_QUERY_ROWS; +/// Atomic persistence bundle for one materializer and one decoded observation. +pub use crate::dtos::MaterializationPersistenceBundle; +/// Bounded read-only materialized event selection. +pub use crate::dtos::MaterializedEventFilter; +/// Materialized event insert contract. +pub use crate::dtos::MaterializedEventInsert; +/// One materialized output returned by a bounded query. +pub use crate::dtos::MaterializedEventQueryRow; +/// One processor-owned materialized output row. +pub use crate::dtos::MaterializedOutputInsert; +/// Stable processing ledger identity. +pub use crate::dtos::ProcessingLedgerIdentity; +/// Processing ledger mark request contract. +pub use crate::dtos::ProcessingLedgerMark; +/// Stable processing ledger status. +pub use crate::dtos::ProcessingLedgerStatus; +/// Raw payload lifecycle mark request. +pub use crate::dtos::RawPayloadLifecycleMark; +/// Raw payload processing state. +pub use crate::dtos::RawPayloadProcessingState; +/// Raw payload retention state. +pub use crate::dtos::RawPayloadRetentionState; +/// Canonical raw Solana transaction insert contract. +pub use crate::dtos::RawTransactionInsert; +/// Store backend diagnostic contract. +pub use crate::dtos::StoreBackendDescriptor; +/// Store backend kind contract. +pub use crate::dtos::StoreBackendKind; +/// Store migration diagnostic snapshot contract. +pub use crate::dtos::StoreMigrationSnapshot; +/// Store migration status contract. +pub use crate::dtos::StoreMigrationStatus; +/// Transaction acquisition observation insert contract. +pub use crate::dtos::TransactionObservationInsert; +/// Transaction acquisition observation origin. +pub use crate::dtos::TransactionObservationOrigin; +/// Transaction acquisition observation status. +pub use crate::dtos::TransactionObservationStatus; +/// Core account key SQL-like row contract. +pub use crate::entities::CoreAccountKeyRow; +/// Core balance change SQL-like row contract. +pub use crate::entities::CoreBalanceChangeRow; +/// Core inner instruction SQL-like row contract. +pub use crate::entities::CoreInnerInstructionRow; +/// Core instruction SQL-like row contract. +pub use crate::entities::CoreInstructionRow; +/// Core log SQL-like row contract. +pub use crate::entities::CoreLogRow; +/// Core transaction SQL-like row contract. +pub use crate::entities::CoreTransactionRow; +/// Decoded event SQL-like row contract. +pub use crate::entities::DecodedEventRow; +/// Materialized event SQL-like row contract. +pub use crate::entities::MaterializedEventRow; +/// Processing ledger SQL-like row contract. +pub use crate::entities::ProcessingLedgerRow; +/// Canonical raw Solana transaction SQL-like row contract. +pub use crate::entities::RawTransactionRow; +/// Transaction acquisition observation SQL-like row contract. +pub use crate::entities::TransactionObservationRow; +/// Store backend health snapshot. +pub use crate::health::StoreHealthSnapshot; +/// Store backend health status. +pub use crate::health::StoreHealthStatus; +/// Default repository page size. +pub use crate::pagination::DEFAULT_PAGE_SIZE; +/// Maximum repository page size. +pub use crate::pagination::MAX_PAGE_SIZE; +/// Page request contract for repository list operations. +pub use crate::pagination::PageRequest; +/// Sort direction for repository list operations. +pub use crate::pagination::SortDirection; +/// Canonical transaction to core extraction storage behavior. +pub use crate::repositories::CoreExtractionStore; +/// Core Solana storage behavior. +pub use crate::repositories::CoreTransactionStore; +/// Contextual instruction decode and materialization storage behavior. +pub use crate::repositories::DecodePipelineStore; +/// Decoded event storage behavior. +pub use crate::repositories::DecodedEventStore; +/// Materialized event storage behavior. +pub use crate::repositories::MaterializedEventStore; +/// Processing ledger storage behavior. +pub use crate::repositories::ProcessingLedgerStore; +/// Program observation storage behavior. +pub use crate::repositories::ProgramObservationStore; +/// Raw transaction storage behavior. +pub use crate::repositories::RawTransactionStore; +/// Store health storage behavior. +pub use crate::repositories::StoreHealthStore; diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/pagination.rs b/migration/khadhroony-bot2-reference/kb_store_core/src/pagination.rs new file mode 100644 index 0000000..0bcc8c3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/pagination.rs @@ -0,0 +1,75 @@ +// file: kb_store_core/src/pagination.rs +// version: 2 + +//! Backend-neutral pagination and sorting contracts for repository operations. + +/// Default page size for repository list operations. +pub const DEFAULT_PAGE_SIZE: u16 = 100; + +/// Maximum page size for repository list operations. +pub const MAX_PAGE_SIZE: u16 = 1000; + +/// Sort direction for repository list operations. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub enum SortDirection { + /// Sort values in ascending order. + Asc, + /// Sort values in descending order. + Desc, +} + +/// Page request contract for repository list operations. +#[derive(Clone, Copy, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct PageRequest { + /// Maximum number of rows to return. + pub limit: u16, + /// Zero-based row offset. + pub offset: u64, +} + +impl PageRequest { + /// Builds a page request after minimal bounds validation. + pub fn new(limit: u16, offset: u64) -> kb_core::Result { + if limit == 0 { + return std::result::Result::Err(kb_core::Error::db( + "page limit must be greater than zero", + )); + } + if limit > crate::MAX_PAGE_SIZE { + return std::result::Result::Err(kb_core::Error::db( + "page limit exceeds maximum page size", + )); + } + return std::result::Result::Ok(Self { limit, offset }); + } + + /// Builds the default first page request. + pub fn first_page() -> Self { + return Self { + limit: crate::DEFAULT_PAGE_SIZE, + offset: 0, + }; + } +} + +#[cfg(test)] +mod tests { + #[test] + fn page_request_rejects_zero_limit() { + let result = crate::PageRequest::new(0, 0); + assert!(result.is_err()); + } + + #[test] + fn page_request_rejects_limit_above_maximum() { + let result = crate::PageRequest::new(crate::MAX_PAGE_SIZE + 1, 0); + assert!(result.is_err()); + } + + #[test] + fn first_page_uses_default_limit() { + let request = crate::PageRequest::first_page(); + assert_eq!(request.limit, crate::DEFAULT_PAGE_SIZE); + assert_eq!(request.offset, 0); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/repositories.rs b/migration/khadhroony-bot2-reference/kb_store_core/src/repositories.rs new file mode 100644 index 0000000..a45a3ce --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/repositories.rs @@ -0,0 +1,25 @@ +// file: kb_store_core/src/repositories.rs +// version: 4 + +//! Backend-neutral repository trait exports. + +mod storage_repositories; + +/// Canonical transaction to core extraction storage behavior. +pub use crate::repositories::storage_repositories::CoreExtractionStore; +/// Core Solana storage behavior. +pub use crate::repositories::storage_repositories::CoreTransactionStore; +/// Contextual instruction decode and materialization storage behavior. +pub use crate::repositories::storage_repositories::DecodePipelineStore; +/// Decoded event storage behavior. +pub use crate::repositories::storage_repositories::DecodedEventStore; +/// Materialized event storage behavior. +pub use crate::repositories::storage_repositories::MaterializedEventStore; +/// Processing ledger storage behavior. +pub use crate::repositories::storage_repositories::ProcessingLedgerStore; +/// Program observation storage behavior. +pub use crate::repositories::storage_repositories::ProgramObservationStore; +/// Raw transaction storage behavior. +pub use crate::repositories::storage_repositories::RawTransactionStore; +/// Store health storage behavior. +pub use crate::repositories::storage_repositories::StoreHealthStore; diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/repositories/README.md b/migration/khadhroony-bot2-reference/kb_store_core/src/repositories/README.md new file mode 100644 index 0000000..cc33c15 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/repositories/README.md @@ -0,0 +1,29 @@ + + + +# Repositories + +Ce dossier contient les APIs de stockage backend-agnostiques. + +## Règles + +- Les traits sont async pour rester compatibles avec `sqlx` et les stores distants. +- Les traits ne contiennent pas de SQL. +- Les traits manipulent des DTOs ou des modèles partagés. +- Les implémentations concrètes appartiennent à `kb_store_pg`, `kb_store_sqlite` ou à des stores de tests. +- Le replay doit pouvoir sélectionner des instructions normalisées sans rejouer toute une transaction. +- Les décodeurs doivent pouvoir lire une instruction avec contexte extrait : account keys, inner instructions, logs et balance changes. +- `CoreTransactionStore` doit permettre une insertion idempotente séparée des transactions, account keys, instructions, inner instructions, logs et balance changes. + +## Traits actuels + +```text +StoreHealthStore +RawTransactionStore +CoreTransactionStore +ProgramObservationStore +DecodedEventStore +MaterializedEventStore +ProcessingLedgerStore +``` + diff --git a/migration/khadhroony-bot2-reference/kb_store_core/src/repositories/storage_repositories.rs b/migration/khadhroony-bot2-reference/kb_store_core/src/repositories/storage_repositories.rs new file mode 100644 index 0000000..1aee81d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_core/src/repositories/storage_repositories.rs @@ -0,0 +1,237 @@ +// file: kb_store_core/src/repositories/storage_repositories.rs +// version: 11 + +//! Storage trait definitions shared by concrete stores. + +/// Store health storage behavior. +#[async_trait::async_trait] +pub trait StoreHealthStore { + /// Reads the backend descriptor visible to diagnostics. + async fn backend_descriptor(&self) -> kb_core::Result; + + /// Reads the current health snapshot. + async fn health_snapshot(&self) -> kb_core::Result; + + /// Reads the current migration snapshot when the backend supports migrations. + async fn migration_snapshot(&self) -> kb_core::Result; +} + +/// Canonical raw transaction and acquisition observation storage behavior. +#[async_trait::async_trait] +pub trait RawTransactionStore { + /// Returns true when a canonical transaction signature is already stored. + async fn has_raw_transaction_signature( + &self, + signature: &kb_model::Signature, + ) -> kb_core::Result; + + /// Returns true when a transaction observation key is already stored. + async fn has_transaction_observation_key(&self, observation_key: &str) + -> kb_core::Result; + + /// Stores one canonical source-independent transaction payload. + async fn insert_raw_transaction( + &self, + input: &crate::RawTransactionInsert, + ) -> kb_core::Result; + + /// Stores one lightweight transaction acquisition observation. + async fn insert_transaction_observation( + &self, + input: &crate::TransactionObservationInsert, + ) -> kb_core::Result; + + /// Updates canonical raw transaction retention and processing metadata. + async fn mark_raw_payload_lifecycle( + &self, + mark: &crate::RawPayloadLifecycleMark, + ) -> kb_core::Result; +} + +/// Core Solana storage behavior. +#[async_trait::async_trait] +pub trait CoreTransactionStore { + /// Stores one normalized core transaction. + async fn insert_core_transaction( + &self, + input: &crate::CoreTransactionInsert, + ) -> kb_core::Result; + + /// Stores normalized core account keys. + async fn insert_core_account_keys( + &self, + inputs: &[crate::CoreAccountKeyInsert], + ) -> kb_core::Result; + + /// Stores normalized core instructions. + async fn insert_core_instructions( + &self, + inputs: &[crate::CoreInstructionInsert], + ) -> kb_core::Result; + + /// Stores normalized core inner instructions. + async fn insert_core_inner_instructions( + &self, + inputs: &[crate::CoreInnerInstructionInsert], + ) -> kb_core::Result; + + /// Stores normalized core logs. + async fn insert_core_logs( + &self, + inputs: &[crate::CoreLogInsert], + ) -> kb_core::Result; + + /// Stores normalized core balance changes. + async fn insert_core_balance_changes( + &self, + inputs: &[crate::CoreBalanceChangeInsert], + ) -> kb_core::Result; + + /// Lists normalized core instructions selected for replay or first processing. + async fn list_core_instructions_for_replay( + &self, + filter: &crate::CoreInstructionReplayFilter, + page_request: &crate::PageRequest, + ) -> kb_core::Result>; + + /// Lists replay inputs with instruction context, logs, balances and account keys. + async fn list_core_instruction_replay_inputs( + &self, + filter: &crate::CoreInstructionReplayFilter, + page_request: &crate::PageRequest, + ) -> kb_core::Result>; + + /// Updates one normalized core instruction lifecycle state. + async fn mark_core_instruction_lifecycle( + &self, + mark: &crate::CoreInstructionLifecycleMark, + ) -> kb_core::Result; +} + +/// Canonical transaction to core extraction storage behavior. +#[async_trait::async_trait] +pub trait CoreExtractionStore { + /// Lists canonical raw transactions selected for core extraction. + async fn list_raw_transactions_for_core_extraction( + &self, + filter: &crate::CoreExtractionSelectionFilter, + ) -> kb_core::Result>; + + /// Returns true when the same processor version already succeeded for the same input hash. + async fn is_core_extraction_current( + &self, + identity: &crate::ProcessingLedgerIdentity, + ) -> kb_core::Result; + + /// Atomically replaces one signature core graph and marks the processing ledger as succeeded. + async fn persist_core_extraction( + &self, + bundle: &crate::CoreExtractionBundle, + force_replay: bool, + ) -> kb_core::Result; + + /// Persists one failed extraction attempt and marks the canonical raw transaction as failed. + async fn mark_core_extraction_failed( + &self, + failure: &crate::CoreExtractionFailure, + ) -> kb_core::Result; +} + +/// Contextual instruction decode and materialization storage behavior. +#[async_trait::async_trait] +pub trait DecodePipelineStore { + /// Lists contextual core instructions selected for a bounded decode campaign. + async fn list_decode_inputs( + &self, + filter: &crate::DecodeSelectionFilter, + ) -> kb_core::Result>; + + /// Returns true when the same processor version already succeeded for the same input hash. + async fn is_decode_current( + &self, + identity: &crate::ProcessingLedgerIdentity, + ) -> kb_core::Result; + + /// Replaces declarations owned by one decoder version. + async fn persist_decode_coverage_declarations( + &self, + declarations: &[crate::DecodeCoverageDeclarationInsert], + ) -> kb_core::Result; + + /// Atomically persists decoded observations, coverage and the common ledger. + async fn persist_decode_result( + &self, + bundle: &crate::DecodePersistenceBundle, + force_replay: bool, + ) -> kb_core::Result; + + /// Persists a failed decode attempt and marks the source instruction as failed. + async fn mark_decode_failed( + &self, + failure: &crate::DecodeFailure, + ) -> kb_core::Result; + + /// Atomically persists materialized outputs and the common ledger. + async fn persist_materialization_result( + &self, + bundle: &crate::MaterializationPersistenceBundle, + force_replay: bool, + ) -> kb_core::Result; + + /// Reads aggregated machine-readable coverage diagnostics. + async fn list_decode_coverage_summary( + &self, + processor_name: std::option::Option<&str>, + processor_version: std::option::Option<&str>, + limit: u32, + ) -> kb_core::Result>; + + /// Lists bounded materialized outputs for read-only application views. + async fn list_materialized_events( + &self, + filter: &crate::MaterializedEventFilter, + ) -> kb_core::Result>; +} + +/// Program observation storage behavior. +#[async_trait::async_trait] +pub trait ProgramObservationStore { + /// Stores program observations. + async fn store_observations( + &self, + observations: &[kb_model::ProgramObservation], + ) -> kb_core::Result; +} + +/// Decoded event storage behavior. +#[async_trait::async_trait] +pub trait DecodedEventStore { + /// Stores decoded protocol events. + async fn store_decoded_events( + &self, + events: &[crate::DecodedEventInsert], + ) -> kb_core::Result; +} + +/// Materialized event storage behavior. +#[async_trait::async_trait] +pub trait MaterializedEventStore { + /// Stores materialized business events. + async fn store_materialized_events( + &self, + events: &[crate::MaterializedEventInsert], + ) -> kb_core::Result; +} + +/// Processing ledger storage behavior. +#[async_trait::async_trait] +pub trait ProcessingLedgerStore { + /// Marks an input as processed for a module version. + async fn mark_processed( + &self, + mark: &crate::ProcessingLedgerMark, + ) -> kb_core::Result; + + /// Returns true when an input was already processed for a module version. + async fn is_processed(&self, mark: &crate::ProcessingLedgerMark) -> kb_core::Result; +} diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/Cargo.toml b/migration/khadhroony-bot2-reference/kb_store_pg/Cargo.toml new file mode 100644 index 0000000..d7f4386 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/Cargo.toml @@ -0,0 +1,25 @@ +# file: kb_store_pg/Cargo.toml +# version: 5 + +[package] +name = "kb_store_pg" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +async-trait.workspace = true +chrono.workspace = true +kb_config = { path = "../kb_config" } +kb_core = { path = "../kb_core" } +kb_model = { path = "../kb_model" } +kb_store_core = { path = "../kb_store_core" } +serde.workspace = true +serde_json.workspace = true +sqlx.workspace = true +tokio.workspace = true +tracing.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/README.md b/migration/khadhroony-bot2-reference/kb_store_pg/README.md new file mode 100644 index 0000000..b57f152 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/README.md @@ -0,0 +1,246 @@ + + + +# kb_store_pg + +`kb_store_pg` implémente le backend PostgreSQL destiné à la production. + +Ce crate dépend de `kb_store_core` pour les contrats et fournit les implémentations concrètes PostgreSQL : pool, healthcheck, migrations, requêtes SQL et repositories. + +## Rôle exact + +`kb_store_pg` contient : + +- le handle de store PostgreSQL ; +- la création effective du pool PostgreSQL ; +- le healthcheck PostgreSQL minimal ; +- les diagnostics backend : DSN masqué, schéma courant et version serveur ; +- la stratégie de migrations PostgreSQL ; +- les requêtes SQL dans `queries/` ; +- les implémentations repository dans `repositories/`. + +`kb_store_pg` ne contient pas : + +- de contrat applicatif générique qui devrait vivre dans `kb_store_core` ; +- de logique Tauri ; +- de logique RPC ; +- de décodeur ; +- de matérialisateur métier ; +- de création de schémas PostgreSQL applicatifs explicites ; +- de création des tables `decode`, `mat`, `catalog`, `agg` ou `wallet` avant les jalons dédiés ; la seule table `ops` active en `0.3.4` est le ledger générique de traitement. + +## Convention PostgreSQL + +`kb_store_pg` utilise le schéma courant/default du profil PostgreSQL, généralement `public`. + +Il est interdit de créer ou d'utiliser des schémas applicatifs explicites comme : + +```text +raw +core +obs +decode +mat +catalog +agg +ops +wallet +``` + +La séparation logique se fait dans le nom de table : + +```text +kb_sol__ +``` + +Exemples valides : + +```text +kb_sol_raw_transactions +kb_sol_obs_transaction_observations +kb_sol_core_transactions +kb_sol_core_account_keys +kb_sol_core_instructions +kb_sol_core_inner_instructions +kb_sol_core_logs +kb_sol_core_balance_changes +kb_sol_obs_program_observations +kb_sol_obs_instruction_observations +kb_sol_decode_decoded_events +kb_sol_mat_trade_events +kb_sol_catalog_tokens +kb_sol_catalog_pools +kb_sol_catalog_pairs +kb_sol_ops_processing_ledger +``` + +Exemples interdits, écrits avec `DOT` pour que les audits textuels simples ne confondent pas documentation et usage réel : + +```text +raw DOT kb_sol_rpc_transactions +raw DOT sol_transactions +core DOT sol_instructions +obs DOT program_observations +decode DOT protocol_events +ops DOT processing_ledger +``` + +## Layout obligatoire + +```text +src/ + lib.rs + pg_store.rs + migrations.rs + queries/ + repositories/ +``` + +## Conventions de dossiers + +| Dossier | Rôle | +|-----------------|--------------------------------------------------------| +| `queries/` | SQL, bind et exécution SQL uniquement. | +| `repositories/` | Implémentations PostgreSQL des traits `kb_store_core`. | + +Aucune structure métier, DTO ou entity ne doit être créée dans `queries/`. + +## Infrastructure `0.2.2` + +Le jalon `0.2.2` introduit `PostgresStoreOptions` et `PostgresStore`. + +Le store sait : + +- construire ses options depuis `kb_config::PostgresConfig` ; +- valider `url`, `max_connections` et `connect_timeout_ms` ; +- créer un pool `sqlx::PgPool` ; +- masquer le DSN pour les logs et l'interface ; +- lire `current_schema()` ; +- lire `version()` ; +- exécuter un healthcheck `SELECT 1` ; +- lire un snapshot de migrations non destructif. + +Le store ne crée aucune table Solana lourde en `0.2.2`; raw est ajouté en `0.2.3` et core en `0.2.4`. + +## Migrations + +Le store applique un DDL idempotent contrôlé par le crate, dans le schéma courant du profil. Après validation réelle de la transition historique `0.2.x -> 0.3.1`, la baseline a été consolidée puis étendue par une migration additive dédiée au ledger : + +```text +0001_canonical_transaction_store.sql +0002_core_store.sql +0003_processing_ledger.sql +``` + +Ces fichiers ne créent que les tables canoniques actuelles. Les anciens noms RPC/WS et la logique de conversion historique restent documentés dans `CHANGELOG.md`, mais ne font plus partie du DDL runtime. + +Les futures migrations doivent : + +- utiliser uniquement le schéma courant du profil ; +- créer des tables non qualifiées comme `kb_sol_raw_transactions` ; +- ajouter des contraintes strictes mais réversibles ; +- privilégier des index minimaux par `signature`, `slot`, `program_id` et `created_at` selon table ; +- éviter les contraintes métier prématurées. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs`. +- Les erreurs passent par `kb_core::Error` et `kb_core::Result`. +- Les tests PostgreSQL réels sont reportables si aucun serveur local n'est garanti. + +## Raw store `0.2.3` + +Le jalon `0.2.3` ajoute les premières tables Solana réelles : + +```text +kb_sol_raw_rpc_transactions +kb_sol_raw_ws_notifications +``` + +Ces tables restent dans le schéma PostgreSQL courant du profil actif. Elles sont créées par une initialisation idempotente du store PostgreSQL et non par des schémas applicatifs explicites. + +La table RPC est dédupliquée par `signature`. La table WebSocket est dédupliquée par `notification_key`, avec signature optionnelle et lien optionnel vers une transaction RPC canonique déjà stockée. + +La méthode `PostgresStore::initialize_raw_store_schema()` applique uniquement le schéma raw minimal. Si `database.postgres.auto_initialize_schema` vaut `true`, cette initialisation est appelée après la connexion. + +`0.2.3` ne supprime pas encore physiquement les payloads raw. Les colonnes `retention_state`, `processing_state`, `raw_json_hash` et `lifecycle_reason` préparent la compaction, l'archivage et la purge future. + +## Core store `0.2.4` + +Le jalon `0.2.4` ajoute les tables core minimales : + +```text +kb_sol_core_transactions +kb_sol_core_account_keys +kb_sol_core_instructions +kb_sol_core_inner_instructions +kb_sol_core_logs +kb_sol_core_balance_changes +``` + +`PostgresStore::initialize_core_store_schema()` applique le schéma raw puis le schéma core, car les transactions core peuvent référencer les transactions raw. + +`PostgresStore::initialize_store_schema()` applique maintenant raw puis core. Si `database.postgres.auto_initialize_schema` vaut `true`, la connexion applique donc les deux familles de tables. + +Le repository `CoreTransactionStore` est implémenté pour : + +- insérer la transaction core canonique ; +- insérer les account keys résolus ; +- insérer les instructions replayables ; +- insérer les inner instructions comme contexte d'arbre ; +- insérer les logs ordonnés ; +- insérer les balance changes ; +- lister les instructions à rejouer ; +- construire `CoreInstructionReplayInput` avec contexte, dont toutes les instructions outer de la signature ordonnées par index numérique ; +- marquer le lifecycle d'une instruction. + +## Baseline canonique `0.3.1` + +`0.3.1` a validé sur PostgreSQL réel la transition des anciennes tables vers : + +```text +kb_sol_raw_transactions +kb_sol_obs_transaction_observations +kb_sol_core_transactions.raw_transaction_id +``` + +La transaction raw est unique par signature et porte le document canonique versionné. Les observations sont légères et ne contiennent aucun payload transactionnel complet. Les repositories actifs exposent `RawTransactionInsert` et `TransactionObservationInsert`. + +Après validation, les scripts SQL historiques ont été remplacés par la baseline courante `0001`/`0002`. L’initialiseur crate-managed conserve temporairement un chemin de compatibilité idempotent pour les workspaces encore issus de `pre.001`; il ne recrée pas les anciennes tables. + +## Extraction core `0.3.4` + +`kb_store_pg` implémente `CoreExtractionStore` avec une transaction PostgreSQL unique par signature. Une extraction réussie : + +- remplace le graphe core de la signature par cascade ; +- insère transaction, account keys, instructions, inner instructions, logs et balance changes ; +- marque la ligne raw `core_extracted` ; +- upsert le ledger `kb_sol_ops_processing_ledger` avec la version et le hash canonique ; +- commit l’ensemble ou annule toutes les écritures. + +Les échecs d’extraction sont enregistrés séparément dans le ledger et dans l’état raw afin de rester rejouables. + +## Store decode et matérialisation `0.4.0` + +La migration additive `0004_decode_materialization_store.sql` crée dans le schéma courant : + +```text +kb_sol_decode_events +kb_sol_decode_coverage_declarations +kb_sol_decode_coverage_observations +kb_sol_mat_events +``` + +Ces tables réutilisent `kb_sol_ops_processing_ledger`. Les observations decode, leur couverture, l’état lifecycle et le ledger sont écrits dans une même transaction PostgreSQL. Les sorties materialized et leur ledger suivent la même règle. Les colonnes JSONB portent le suffixe `_jsonb`. + +## Tracing opérationnel + +`kb_store_pg` utilise le target canonique `kb_store_pg`. Les décisions SQL de decode/mat/ledger et leurs rollbacks sont émises par le store lui-même. Un `mark_decode_failed`, une persistance de bundle matérialisation en statut `failed` ou une erreur transactionnelle sont écrits au niveau `error` avec les identifiants de corrélation disponibles. + +Le store ne délègue pas ces détails à `kb_app_demo` et ne journalise jamais un DSN non masqué. + +## Contexte outer du replay `0.4.1-pre.014` + +La lecture contextualisée agrège directement `kb_sol_core_instructions` pour la signature ciblée. Elle expose les chemins outer purement numériques sous la forme stable `instructionIndex`, `instructionPath`, `programId`, `payloadJson`, `payloadHash`, avec un `ORDER BY instruction_path::BIGINT`. L’instruction cible reste présente dans le tableau. La requête est strictement en lecture et n’ajoute aucune migration. diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/maintenance/drop_raw_core_store.sql b/migration/khadhroony-bot2-reference/kb_store_pg/maintenance/drop_raw_core_store.sql new file mode 100644 index 0000000..a03726f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/maintenance/drop_raw_core_store.sql @@ -0,0 +1,21 @@ +-- file: kb_store_pg/maintenance/drop_raw_core_store.sql +-- version: 4 + +-- Drop raw and core Solana storage tables in reverse dependency order. +-- This script is intended for local development and test databases only. +-- It does not drop unrelated schemas, migrations, roles or configuration state. +-- It intentionally avoids CASCADE so unexpected dependencies fail loudly. +-- No custom PostgreSQL enum/domain types are owned by these 0.2.3/0.2.4 tables. + +BEGIN; + +DROP TABLE IF EXISTS kb_sol_core_balance_changes; +DROP TABLE IF EXISTS kb_sol_core_logs; +DROP TABLE IF EXISTS kb_sol_core_inner_instructions; +DROP TABLE IF EXISTS kb_sol_core_instructions; +DROP TABLE IF EXISTS kb_sol_core_account_keys; +DROP TABLE IF EXISTS kb_sol_core_transactions; +DROP TABLE IF EXISTS kb_sol_obs_transaction_observations; +DROP TABLE IF EXISTS kb_sol_raw_transactions; + +COMMIT; diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/migrations/0001_canonical_transaction_store.sql b/migration/khadhroony-bot2-reference/kb_store_pg/migrations/0001_canonical_transaction_store.sql new file mode 100644 index 0000000..2538bb0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/migrations/0001_canonical_transaction_store.sql @@ -0,0 +1,94 @@ +-- file: kb_store_pg/migrations/0001_canonical_transaction_store.sql +-- version: 1 + +-- Current canonical transaction and acquisition observation store. +-- This baseline contains only active tables and uses the current PostgreSQL schema. +-- Historical 0.2.x upgrade logic was validated before this baseline was consolidated. + +CREATE TABLE IF NOT EXISTS kb_sol_raw_transactions ( + id BIGSERIAL, + signature TEXT NOT NULL, + slot BIGINT NOT NULL, + canonical_json JSONB, + canonical_json_hash TEXT, + canonical_format_version INTEGER NOT NULL DEFAULT 1, + retention_state TEXT NOT NULL DEFAULT 'full', + processing_state TEXT NOT NULL DEFAULT 'received', + lifecycle_reason TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_raw_transactions PRIMARY KEY (id), + CONSTRAINT ck_kb_sol_raw_transactions_signature_not_empty CHECK (length(btrim(signature)) > 0), + CONSTRAINT ck_kb_sol_raw_transactions_slot_non_negative CHECK (slot >= 0), + CONSTRAINT ck_kb_sol_raw_transactions_format_version_positive CHECK (canonical_format_version > 0), + CONSTRAINT ck_kb_sol_raw_transactions_retention_state CHECK (retention_state IN ('full', 'compacted', 'archived', 'purged')), + CONSTRAINT ck_kb_sol_raw_transactions_processing_state CHECK (processing_state IN ('received', 'core_extracted', 'decoded', 'materialized', 'failed')), + CONSTRAINT ck_kb_sol_raw_transactions_full_has_json CHECK (retention_state <> 'full' OR canonical_json IS NOT NULL) + ); + +CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_raw_transactions_signature ON kb_sol_raw_transactions (signature); + +CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_slot ON kb_sol_raw_transactions (slot); + +CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_created_at ON kb_sol_raw_transactions (created_at); + +CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_processing ON kb_sol_raw_transactions (processing_state); + +CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_canonical_hash ON kb_sol_raw_transactions (canonical_json_hash) WHERE canonical_json_hash IS NOT NULL; + +CREATE TABLE IF NOT EXISTS kb_sol_obs_transaction_observations ( + id BIGSERIAL, + raw_transaction_id BIGINT, + observation_key TEXT NOT NULL, + signature TEXT, + slot BIGINT, + provider TEXT NOT NULL, + endpoint_code TEXT, + protocol TEXT NOT NULL, + acquisition_method TEXT NOT NULL, + origin TEXT NOT NULL, + commitment TEXT, + capture_session_id TEXT, + filter_code TEXT, + detected_at TIMESTAMPTZ, + received_at TIMESTAMPTZ NOT NULL, + normalized_at TIMESTAMPTZ, + persisted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + payload_size_bytes BIGINT, + source_payload_hash TEXT, + status TEXT NOT NULL DEFAULT 'received', + error_code TEXT, + error_message TEXT, + CONSTRAINT pk_kb_sol_obs_transaction_observations PRIMARY KEY (id), + CONSTRAINT fk_kb_sol_obs_transaction_observations_raw_transaction FOREIGN KEY (raw_transaction_id) REFERENCES kb_sol_raw_transactions(id) ON DELETE SET NULL, + CONSTRAINT ck_kb_sol_obs_transaction_observations_key_not_empty CHECK (length(btrim(observation_key)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_signature_not_empty CHECK (signature IS NULL OR length(btrim(signature)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_slot_non_negative CHECK (slot IS NULL OR slot >= 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_provider_not_empty CHECK (length(btrim(provider)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_endpoint_not_empty CHECK (endpoint_code IS NULL OR length(btrim(endpoint_code)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_protocol_not_empty CHECK (length(btrim(protocol)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_method_not_empty CHECK (length(btrim(acquisition_method)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_origin CHECK (origin IN ('live', 'backfill', 'replay', 'repair', 'migration')), + CONSTRAINT ck_kb_sol_obs_transaction_observations_commitment_not_empty CHECK (commitment IS NULL OR length(btrim(commitment)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_session_not_empty CHECK (capture_session_id IS NULL OR length(btrim(capture_session_id)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_filter_not_empty CHECK (filter_code IS NULL OR length(btrim(filter_code)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_payload_size_non_negative CHECK (payload_size_bytes IS NULL OR payload_size_bytes >= 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_hash_not_empty CHECK (source_payload_hash IS NULL OR length(btrim(source_payload_hash)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_status CHECK (status IN ('detected', 'received', 'normalized', 'persisted', 'failed', 'missing')), + CONSTRAINT ck_kb_sol_obs_transaction_observations_error_code_not_empty CHECK (error_code IS NULL OR length(btrim(error_code)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_error_message_not_empty CHECK (error_message IS NULL OR length(btrim(error_message)) > 0) + ); + +CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_obs_transaction_observations_key ON kb_sol_obs_transaction_observations (observation_key); + +CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_signature ON kb_sol_obs_transaction_observations (signature) WHERE signature IS NOT NULL; + +CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_slot ON kb_sol_obs_transaction_observations (slot) WHERE slot IS NOT NULL; + +CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_provider_method ON kb_sol_obs_transaction_observations (provider, acquisition_method); + +CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_received_at ON kb_sol_obs_transaction_observations (received_at); + +CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_raw_transaction ON kb_sol_obs_transaction_observations (raw_transaction_id) WHERE raw_transaction_id IS NOT NULL; + +CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_status ON kb_sol_obs_transaction_observations (status); diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/migrations/0002_core_store.sql b/migration/khadhroony-bot2-reference/kb_store_pg/migrations/0002_core_store.sql new file mode 100644 index 0000000..8939f4f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/migrations/0002_core_store.sql @@ -0,0 +1,196 @@ +-- file: kb_store_pg/migrations/0002_core_store.sql +-- version: 1 + +-- Current normalized Solana core store. +-- This migration uses the current PostgreSQL schema from the active profile. +-- It must not create application schemas such as raw, core, obs, decode, mat or catalog. +-- Object naming convention: pk_ for primary keys, fk_ for foreign keys, ux_ for unique indexes and ix_ for non-unique indexes. + +CREATE TABLE IF NOT EXISTS kb_sol_core_transactions ( + id BIGSERIAL, + raw_transaction_id BIGINT, + signature TEXT NOT NULL, + slot BIGINT NOT NULL, + failed BOOLEAN NOT NULL DEFAULT FALSE, + err_json JSONB, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_core_transactions PRIMARY KEY (id), + CONSTRAINT fk_kb_sol_core_transactions_raw_transaction FOREIGN KEY (raw_transaction_id) REFERENCES kb_sol_raw_transactions(id) ON DELETE SET NULL, + CONSTRAINT ck_kb_sol_core_transactions_signature_not_empty CHECK (length(btrim(signature)) > 0), + CONSTRAINT ck_kb_sol_core_transactions_slot_non_negative CHECK (slot >= 0) +); + +CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_transactions_signature + ON kb_sol_core_transactions (signature); + +CREATE INDEX IF NOT EXISTS ix_kb_sol_core_transactions_slot + ON kb_sol_core_transactions (slot); + +CREATE INDEX IF NOT EXISTS ix_kb_sol_core_transactions_created_at + ON kb_sol_core_transactions (created_at); + +CREATE TABLE IF NOT EXISTS kb_sol_core_account_keys ( + id BIGSERIAL, + transaction_id BIGINT NOT NULL, + signature TEXT NOT NULL, + slot BIGINT NOT NULL, + account_index INTEGER NOT NULL, + account_key TEXT NOT NULL, + source TEXT NOT NULL, + writable BOOLEAN NOT NULL, + signer BOOLEAN NOT NULL, + executable BOOLEAN, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_core_account_keys PRIMARY KEY (id), + CONSTRAINT fk_kb_sol_core_account_keys_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE, + CONSTRAINT ck_kb_sol_core_account_keys_signature_not_empty CHECK (length(btrim(signature)) > 0), + CONSTRAINT ck_kb_sol_core_account_keys_slot_non_negative CHECK (slot >= 0), + CONSTRAINT ck_kb_sol_core_account_keys_index_non_negative CHECK (account_index >= 0), + CONSTRAINT ck_kb_sol_core_account_keys_key_not_empty CHECK (length(btrim(account_key)) > 0), + CONSTRAINT ck_kb_sol_core_account_keys_source CHECK (source IN ('static', 'loaded_writable', 'loaded_readonly')) +); + +CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_account_keys_sig_index + ON kb_sol_core_account_keys (signature, account_index); + +CREATE INDEX IF NOT EXISTS ix_kb_sol_core_account_keys_key + ON kb_sol_core_account_keys (account_key); + +CREATE TABLE IF NOT EXISTS kb_sol_core_instructions ( + id BIGSERIAL, + transaction_id BIGINT NOT NULL, + signature TEXT NOT NULL, + slot BIGINT NOT NULL, + instruction_path TEXT NOT NULL, + program_id TEXT NOT NULL, + accounts_json JSONB NOT NULL, + payload_json JSONB, + payload_json_hash TEXT, + processing_state TEXT NOT NULL DEFAULT 'pending', + processor_name TEXT, + processor_version TEXT, + lifecycle_reason TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_core_instructions PRIMARY KEY (id), + CONSTRAINT fk_kb_sol_core_instructions_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE, + CONSTRAINT ck_kb_sol_core_instructions_signature_not_empty CHECK (length(btrim(signature)) > 0), + CONSTRAINT ck_kb_sol_core_instructions_slot_non_negative CHECK (slot >= 0), + CONSTRAINT ck_kb_sol_core_instructions_path_not_empty CHECK (length(btrim(instruction_path)) > 0), + CONSTRAINT ck_kb_sol_core_instructions_program_not_empty CHECK (length(btrim(program_id)) > 0), + CONSTRAINT ck_kb_sol_core_instructions_processing_state CHECK (processing_state IN ('pending', 'decoded', 'materialized', 'ignored', 'failed', 'replay_requested')) +); + +CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_instructions_sig_path + ON kb_sol_core_instructions (signature, instruction_path); + +CREATE INDEX IF NOT EXISTS ix_kb_sol_core_instructions_program + ON kb_sol_core_instructions (program_id); + +CREATE INDEX IF NOT EXISTS ix_kb_sol_core_instructions_slot + ON kb_sol_core_instructions (slot); + +CREATE INDEX IF NOT EXISTS ix_kb_sol_core_instructions_processing + ON kb_sol_core_instructions (processing_state); + +CREATE TABLE IF NOT EXISTS kb_sol_core_inner_instructions ( + id BIGSERIAL, + transaction_id BIGINT NOT NULL, + signature TEXT NOT NULL, + slot BIGINT NOT NULL, + parent_instruction_path TEXT NOT NULL, + instruction_path TEXT NOT NULL, + program_id TEXT NOT NULL, + accounts_json JSONB NOT NULL, + payload_json JSONB, + payload_json_hash TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_core_inner_instructions PRIMARY KEY (id), + CONSTRAINT fk_kb_sol_core_inner_instructions_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE, + CONSTRAINT ck_kb_sol_core_inner_instructions_signature_not_empty CHECK (length(btrim(signature)) > 0), + CONSTRAINT ck_kb_sol_core_inner_instructions_slot_non_negative CHECK (slot >= 0), + CONSTRAINT ck_kb_sol_core_inner_instructions_parent_not_empty CHECK (length(btrim(parent_instruction_path)) > 0), + CONSTRAINT ck_kb_sol_core_inner_instructions_path_not_empty CHECK (length(btrim(instruction_path)) > 0), + CONSTRAINT ck_kb_sol_core_inner_instructions_program_not_empty CHECK (length(btrim(program_id)) > 0) +); + +CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_inner_instructions_sig_path + ON kb_sol_core_inner_instructions (signature, instruction_path); + +CREATE INDEX IF NOT EXISTS ix_kb_sol_core_inner_instructions_parent + ON kb_sol_core_inner_instructions (signature, parent_instruction_path); + +CREATE INDEX IF NOT EXISTS ix_kb_sol_core_inner_instructions_program + ON kb_sol_core_inner_instructions (program_id); + +CREATE TABLE IF NOT EXISTS kb_sol_core_logs ( + id BIGSERIAL, + transaction_id BIGINT NOT NULL, + signature TEXT NOT NULL, + slot BIGINT NOT NULL, + log_index INTEGER NOT NULL, + instruction_path TEXT, + program_id TEXT, + log_text TEXT, + log_text_hash TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_core_logs PRIMARY KEY (id), + CONSTRAINT fk_kb_sol_core_logs_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE, + CONSTRAINT ck_kb_sol_core_logs_signature_not_empty CHECK (length(btrim(signature)) > 0), + CONSTRAINT ck_kb_sol_core_logs_slot_non_negative CHECK (slot >= 0), + CONSTRAINT ck_kb_sol_core_logs_index_non_negative CHECK (log_index >= 0), + CONSTRAINT ck_kb_sol_core_logs_text_not_empty CHECK (log_text IS NULL OR length(btrim(log_text)) > 0), + CONSTRAINT ck_kb_sol_core_logs_path_not_empty CHECK (instruction_path IS NULL OR length(btrim(instruction_path)) > 0), + CONSTRAINT ck_kb_sol_core_logs_program_not_empty CHECK (program_id IS NULL OR length(btrim(program_id)) > 0) +); + +CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_logs_sig_index + ON kb_sol_core_logs (signature, log_index); + +CREATE INDEX IF NOT EXISTS ix_kb_sol_core_logs_program + ON kb_sol_core_logs (program_id) + WHERE program_id IS NOT NULL; + +CREATE INDEX IF NOT EXISTS ix_kb_sol_core_logs_path + ON kb_sol_core_logs (signature, instruction_path) + WHERE instruction_path IS NOT NULL; + +CREATE TABLE IF NOT EXISTS kb_sol_core_balance_changes ( + id BIGSERIAL, + transaction_id BIGINT NOT NULL, + signature TEXT NOT NULL, + slot BIGINT NOT NULL, + balance_change_index INTEGER NOT NULL, + balance_kind TEXT NOT NULL, + account_index INTEGER, + account_key TEXT, + mint TEXT, + owner TEXT, + pre_balance_json JSONB, + post_balance_json JSONB, + delta_json JSONB, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_core_balance_changes PRIMARY KEY (id), + CONSTRAINT fk_kb_sol_core_balance_changes_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE, + CONSTRAINT ck_kb_sol_core_balance_changes_signature_not_empty CHECK (length(btrim(signature)) > 0), + CONSTRAINT ck_kb_sol_core_balance_changes_slot_non_negative CHECK (slot >= 0), + CONSTRAINT ck_kb_sol_core_balance_changes_index_non_negative CHECK (balance_change_index >= 0), + CONSTRAINT ck_kb_sol_core_balance_changes_account_index_non_negative CHECK (account_index IS NULL OR account_index >= 0), + CONSTRAINT ck_kb_sol_core_balance_changes_kind CHECK (balance_kind IN ('native_lamports', 'token_amount')), + CONSTRAINT ck_kb_sol_core_balance_changes_account_not_empty CHECK (account_key IS NULL OR length(btrim(account_key)) > 0), + CONSTRAINT ck_kb_sol_core_balance_changes_mint_not_empty CHECK (mint IS NULL OR length(btrim(mint)) > 0), + CONSTRAINT ck_kb_sol_core_balance_changes_owner_not_empty CHECK (owner IS NULL OR length(btrim(owner)) > 0) +); + +CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_balance_changes_sig_index + ON kb_sol_core_balance_changes (signature, balance_change_index); + +CREATE INDEX IF NOT EXISTS ix_kb_sol_core_balance_changes_account + ON kb_sol_core_balance_changes (account_key) + WHERE account_key IS NOT NULL; + +CREATE INDEX IF NOT EXISTS ix_kb_sol_core_balance_changes_mint + ON kb_sol_core_balance_changes (mint) + WHERE mint IS NOT NULL; + diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/migrations/0003_processing_ledger.sql b/migration/khadhroony-bot2-reference/kb_store_pg/migrations/0003_processing_ledger.sql new file mode 100644 index 0000000..7faddc1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/migrations/0003_processing_ledger.sql @@ -0,0 +1,41 @@ +-- file: kb_store_pg/migrations/0003_processing_ledger.sql +-- version: 1 + +-- Current processing ledger for canonical to core extraction and later pipeline stages. +-- This migration uses the current PostgreSQL schema from the active profile. + +CREATE TABLE IF NOT EXISTS kb_sol_ops_processing_ledger ( + id BIGSERIAL, + stage TEXT NOT NULL, + processor_name TEXT NOT NULL, + processor_version TEXT NOT NULL, + input_key TEXT NOT NULL, + input_hash TEXT NOT NULL, + status TEXT NOT NULL, + attempt_count INTEGER NOT NULL DEFAULT 0, + started_at TIMESTAMPTZ, + finished_at TIMESTAMPTZ, + error_code TEXT, + error_message TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_ops_processing_ledger PRIMARY KEY (id), + CONSTRAINT ck_kb_sol_ops_processing_ledger_stage_not_empty CHECK (length(btrim(stage)) > 0), + CONSTRAINT ck_kb_sol_ops_processing_ledger_processor_name_not_empty CHECK (length(btrim(processor_name)) > 0), + CONSTRAINT ck_kb_sol_ops_processing_ledger_processor_version_not_empty CHECK (length(btrim(processor_version)) > 0), + CONSTRAINT ck_kb_sol_ops_processing_ledger_input_key_not_empty CHECK (length(btrim(input_key)) > 0), + CONSTRAINT ck_kb_sol_ops_processing_ledger_input_hash_not_empty CHECK (length(btrim(input_hash)) > 0), + CONSTRAINT ck_kb_sol_ops_processing_ledger_status CHECK (status IN ('running', 'succeeded', 'failed')), + CONSTRAINT ck_kb_sol_ops_processing_ledger_attempt_count_non_negative CHECK (attempt_count >= 0), + CONSTRAINT ck_kb_sol_ops_processing_ledger_error_code_not_empty CHECK (error_code IS NULL OR length(btrim(error_code)) > 0), + CONSTRAINT ck_kb_sol_ops_processing_ledger_error_message_not_empty CHECK (error_message IS NULL OR length(btrim(error_message)) > 0) +); + +CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_ops_processing_ledger_identity + ON kb_sol_ops_processing_ledger (stage, processor_name, processor_version, input_key); + +CREATE INDEX IF NOT EXISTS ix_kb_sol_ops_processing_ledger_status + ON kb_sol_ops_processing_ledger (stage, processor_name, status, updated_at); + +CREATE INDEX IF NOT EXISTS ix_kb_sol_ops_processing_ledger_input_hash + ON kb_sol_ops_processing_ledger (input_hash); diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/migrations/0004_decode_materialization_store.sql b/migration/khadhroony-bot2-reference/kb_store_pg/migrations/0004_decode_materialization_store.sql new file mode 100644 index 0000000..fa31480 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/migrations/0004_decode_materialization_store.sql @@ -0,0 +1,132 @@ +-- file: kb_store_pg/migrations/0004_decode_materialization_store.sql +-- version: 4 + +-- Common versioned decode, coverage and materialization store. +-- This migration uses the current PostgreSQL schema from the active profile. + +CREATE TABLE IF NOT EXISTS kb_sol_decode_events ( + id BIGSERIAL, + processor_name TEXT NOT NULL, + processor_version TEXT NOT NULL, + input_key TEXT NOT NULL, + input_hash TEXT NOT NULL, + event_key TEXT NOT NULL, + signature TEXT NOT NULL, + slot BIGINT NOT NULL, + instruction_path TEXT NOT NULL, + program_id TEXT NOT NULL, + protocol_code TEXT NOT NULL, + surface_code TEXT NOT NULL, + event_code TEXT NOT NULL, + event_name TEXT NOT NULL, + event_family TEXT NOT NULL, + source_kind TEXT NOT NULL, + confidence TEXT NOT NULL, + proof_kind TEXT NOT NULL, + proof_jsonb JSONB NOT NULL, + payload_jsonb JSONB NOT NULL, + transaction_failed BOOLEAN NOT NULL, + transaction_error_jsonb JSONB, + observation_committed BOOLEAN NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_decode_events PRIMARY KEY (id), + CONSTRAINT ck_kb_sol_decode_events_slot_non_negative CHECK (slot >= 0), + CONSTRAINT ck_kb_sol_decode_events_failed_not_committed CHECK (NOT transaction_failed OR NOT observation_committed) +); + +CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_decode_events_processor_input_event + ON kb_sol_decode_events (processor_name, processor_version, input_key, event_key); +CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_events_signature_path + ON kb_sol_decode_events (signature, instruction_path); +CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_events_program_surface + ON kb_sol_decode_events (program_id, surface_code, event_code); +CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_events_family_commit + ON kb_sol_decode_events (event_family, transaction_failed, observation_committed); + +CREATE TABLE IF NOT EXISTS kb_sol_decode_coverage_declarations ( + id BIGSERIAL, + processor_name TEXT NOT NULL, + processor_version TEXT NOT NULL, + program_id TEXT NOT NULL, + surface_code TEXT, + entry_kind TEXT NOT NULL, + entry_code TEXT NOT NULL, + discriminator_hex TEXT, + historical BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_decode_coverage_declarations PRIMARY KEY (id) +); + +CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_decode_coverage_declarations_identity + ON kb_sol_decode_coverage_declarations ( + processor_name, + processor_version, + program_id, + COALESCE(surface_code, ''), + entry_kind, + entry_code, + COALESCE(discriminator_hex, '') + ); +CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_coverage_declarations_program + ON kb_sol_decode_coverage_declarations (program_id, processor_name, processor_version); + +CREATE TABLE IF NOT EXISTS kb_sol_decode_coverage_observations ( + id BIGSERIAL, + processor_name TEXT NOT NULL, + processor_version TEXT NOT NULL, + input_key TEXT NOT NULL, + input_hash TEXT NOT NULL, + signature TEXT NOT NULL, + slot BIGINT NOT NULL, + instruction_path TEXT NOT NULL, + program_id TEXT NOT NULL, + surface_code TEXT, + entry_code TEXT, + discriminator_hex TEXT, + status TEXT NOT NULL, + recognized BOOLEAN NOT NULL, + decoded_count INTEGER NOT NULL DEFAULT 0, + materialized_count INTEGER NOT NULL DEFAULT 0, + error_count INTEGER NOT NULL DEFAULT 0, + transaction_failed BOOLEAN NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_decode_coverage_observations PRIMARY KEY (id), + CONSTRAINT ck_kb_sol_decode_coverage_observations_slot_non_negative CHECK (slot >= 0), + CONSTRAINT ck_kb_sol_decode_coverage_observations_counts_non_negative CHECK (decoded_count >= 0 AND materialized_count >= 0 AND error_count >= 0) +); + +CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_decode_coverage_observations_identity + ON kb_sol_decode_coverage_observations (processor_name, processor_version, input_key); +CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_coverage_observations_program_status + ON kb_sol_decode_coverage_observations (program_id, status, transaction_failed); +CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_coverage_observations_entry + ON kb_sol_decode_coverage_observations (processor_name, processor_version, surface_code, entry_code); + +CREATE TABLE IF NOT EXISTS kb_sol_mat_events ( + id BIGSERIAL, + processor_name TEXT NOT NULL, + processor_version TEXT NOT NULL, + input_key TEXT NOT NULL, + input_hash TEXT NOT NULL, + output_key TEXT NOT NULL, + source_event_key TEXT NOT NULL, + source_decoder_name TEXT NOT NULL, + source_decoder_version TEXT NOT NULL, + source_decode_input_key TEXT NOT NULL, + signature TEXT NOT NULL, + slot BIGINT NOT NULL, + materialized_family TEXT NOT NULL, + payload_jsonb JSONB NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_mat_events PRIMARY KEY (id), + CONSTRAINT ck_kb_sol_mat_events_slot_non_negative CHECK (slot >= 0) +); + +CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_mat_events_processor_input_output + ON kb_sol_mat_events (processor_name, processor_version, input_key, output_key); +CREATE INDEX IF NOT EXISTS ix_kb_sol_mat_events_signature_family + ON kb_sol_mat_events (source_decoder_name, source_decoder_version, source_decode_input_key, signature, materialized_family); diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/constants.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/constants.rs new file mode 100644 index 0000000..2178312 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/constants.rs @@ -0,0 +1,7 @@ +// file: kb_store_pg/src/constants.rs +// version: 1 + +//! Local constants for the `kb_store_pg` crate. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_store_pg"; diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/lib.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/lib.rs new file mode 100644 index 0000000..88bd547 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/lib.rs @@ -0,0 +1,118 @@ +// file: kb_store_pg/src/lib.rs +// version: 15 + +//! PostgreSQL storage implementation boundary. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod migrations; +mod pg_store; +mod queries; +mod replay_candidates; +mod repositories; +#[cfg(test)] +mod test_serial; + +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Allowed Solana table domains encoded in table names. +pub use crate::migrations::ALLOWED_SOLANA_TABLE_DOMAINS; +/// Core account key table name. +pub use crate::migrations::CORE_ACCOUNT_KEYS_TABLE_NAME; +/// Core balance change table name. +pub use crate::migrations::CORE_BALANCE_CHANGES_TABLE_NAME; +/// Core inner instruction table name. +pub use crate::migrations::CORE_INNER_INSTRUCTIONS_TABLE_NAME; +/// Core instruction table name. +pub use crate::migrations::CORE_INSTRUCTIONS_TABLE_NAME; +/// Core log table name. +pub use crate::migrations::CORE_LOGS_TABLE_NAME; +/// Core Solana table names introduced by `0.2.4`. +pub use crate::migrations::CORE_STORE_TABLE_NAMES; +/// Core transaction table name. +pub use crate::migrations::CORE_TRANSACTIONS_TABLE_NAME; +/// Machine-readable decoder coverage declaration table name. +pub use crate::migrations::DECODE_COVERAGE_DECLARATIONS_TABLE_NAME; +/// Observed decoder coverage classification table name. +pub use crate::migrations::DECODE_COVERAGE_OBSERVATIONS_TABLE_NAME; +/// Versioned decoded event table name. +pub use crate::migrations::DECODE_EVENTS_TABLE_NAME; +/// Decode and materialization table names introduced by `0.4.0`. +pub use crate::migrations::DECODE_STORE_TABLE_NAMES; +/// Default PostgreSQL schema policy. +pub use crate::migrations::DEFAULT_SCHEMA_POLICY; +/// Versioned materialized output table name. +pub use crate::migrations::MATERIALIZED_EVENTS_TABLE_NAME; +/// PostgreSQL migration strategy used by this crate. +pub use crate::migrations::MIGRATION_STRATEGY; +/// Migration table name used by sqlx when migrations are enabled later. +pub use crate::migrations::MIGRATION_TABLE_NAME; +/// Processing ledger table name. +pub use crate::migrations::PROCESSING_LEDGER_TABLE_NAME; +/// Table diagnostic metadata. +pub use crate::migrations::PostgresTableDiagnosticSpec; +/// Canonical transaction acquisition table names active since `0.3.1`. +pub use crate::migrations::RAW_STORE_TABLE_NAMES; +/// Canonical raw transaction table name. +pub use crate::migrations::RAW_TRANSACTIONS_TABLE_NAME; +/// Solana application table prefix. +pub use crate::migrations::SOLANA_TABLE_PREFIX; +/// Advisory lock id used while initializing store schemas. +pub use crate::migrations::STORE_SCHEMA_ADVISORY_LOCK_ID; +/// Transaction acquisition observation table name. +pub use crate::migrations::TRANSACTION_OBSERVATIONS_TABLE_NAME; +/// Core store SQL statements applied by the idempotent initializer. +pub use crate::migrations::core_store_schema_statements; +/// Core table diagnostic specs. +pub use crate::migrations::core_store_table_diagnostic_specs; +/// Decode and materialization SQL statements applied by the idempotent initializer. +pub use crate::migrations::decode_store_schema_statements; +/// Decode and materialization table diagnostic specs. +pub use crate::migrations::decode_store_table_diagnostic_specs; +/// Returns true when a Solana table name follows the canonical prefix and domain rules. +pub use crate::migrations::is_valid_solana_table_name; +/// Canonical transaction acquisition SQL statements applied by the initializer. +pub use crate::migrations::raw_store_schema_statements; +/// Canonical transaction acquisition diagnostic specs. +pub use crate::migrations::raw_store_table_diagnostic_specs; +/// Validates core store table names. +pub use crate::migrations::validate_core_store_table_names; +/// Validates decode and materialization store table names. +pub use crate::migrations::validate_decode_store_table_names; +/// Validates canonical transaction acquisition table names. +pub use crate::migrations::validate_raw_store_table_names; +/// Validates a canonical Solana table name. +pub use crate::migrations::validate_solana_table_name; +/// PostgreSQL diagnostic snapshot. +pub use crate::pg_store::PostgresBackendDiagnostics; +/// Minimal PostgreSQL store handle. +pub use crate::pg_store::PostgresStore; +/// PostgreSQL store connection options. +pub use crate::pg_store::PostgresStoreOptions; +/// PostgreSQL table diagnostic snapshot. +pub use crate::pg_store::PostgresTableDiagnostics; +/// PostgreSQL table statistics snapshot. +pub use crate::pg_store::PostgresTableStatistics; +/// Returns a DSN masked for logs and UI diagnostics. +pub use crate::pg_store::mask_postgres_dsn; +/// Maximum number of rows returned by one replay candidate query. +pub use crate::replay_candidates::MAX_REPLAY_CANDIDATE_ROWS; +/// Bounded read-only filter for core entity summaries. +pub use crate::replay_candidates::PostgresReplayEntityFilter; +/// Core entity kind used while filtering replay candidates. +pub use crate::replay_candidates::PostgresReplayEntityKind; +/// Aggregated mint, owner or account-key occurrences from core tables. +pub use crate::replay_candidates::PostgresReplayEntitySummary; +/// Bounded read-only filter for program summaries. +pub use crate::replay_candidates::PostgresReplayProgramFilter; +/// Program occurrence scope used while filtering replay candidates. +pub use crate::replay_candidates::PostgresReplayProgramScope; +/// Aggregated program occurrences across outer, inner and linked logs. +pub use crate::replay_candidates::PostgresReplayProgramSummary; +/// One raw transaction candidate enriched with core and ledger diagnostics. +pub use crate::replay_candidates::PostgresReplayTransactionCandidate; +/// Bounded read-only filter for transaction replay candidates. +pub use crate::replay_candidates::PostgresReplayTransactionFilter; diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/migrations.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/migrations.rs new file mode 100644 index 0000000..29a81c9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/migrations.rs @@ -0,0 +1,1158 @@ +// file: kb_store_pg/src/migrations.rs +// version: 15 + +//! PostgreSQL migration conventions for the storage backend. + +/// PostgreSQL schema policy used by application migrations. +pub const DEFAULT_SCHEMA_POLICY: &str = "current_profile_schema"; + +/// Canonical Solana table prefix. +pub const SOLANA_TABLE_PREFIX: &str = "kb_sol_"; + +/// Migration table name used by sqlx when migrations are enabled later. +pub const MIGRATION_TABLE_NAME: &str = "_sqlx_migrations"; + +/// Migration strategy used by the canonical transaction and core stores. +pub const MIGRATION_STRATEGY: &str = + "idempotent_canonical_transaction_observation_and_core_tables_without_application_schemas"; + +/// Advisory lock id used while applying idempotent store schema statements. +pub const STORE_SCHEMA_ADVISORY_LOCK_ID: i64 = 2_024_000_204; + +/// Canonical raw transaction table name. +pub const RAW_TRANSACTIONS_TABLE_NAME: &str = "kb_sol_raw_transactions"; + +/// Lightweight transaction acquisition observation table name. +pub const TRANSACTION_OBSERVATIONS_TABLE_NAME: &str = "kb_sol_obs_transaction_observations"; + +/// Core transaction table name. +pub const CORE_TRANSACTIONS_TABLE_NAME: &str = "kb_sol_core_transactions"; + +/// Core account key table name. +pub const CORE_ACCOUNT_KEYS_TABLE_NAME: &str = "kb_sol_core_account_keys"; + +/// Core instruction table name. +pub const CORE_INSTRUCTIONS_TABLE_NAME: &str = "kb_sol_core_instructions"; + +/// Core inner instruction table name. +pub const CORE_INNER_INSTRUCTIONS_TABLE_NAME: &str = "kb_sol_core_inner_instructions"; + +/// Core log table name. +pub const CORE_LOGS_TABLE_NAME: &str = "kb_sol_core_logs"; + +/// Core balance change table name. +pub const CORE_BALANCE_CHANGES_TABLE_NAME: &str = "kb_sol_core_balance_changes"; + +/// Processing ledger table name. +pub const PROCESSING_LEDGER_TABLE_NAME: &str = "kb_sol_ops_processing_ledger"; + +/// Versioned decoded event table name. +pub const DECODE_EVENTS_TABLE_NAME: &str = "kb_sol_decode_events"; + +/// Machine-readable decoder coverage declaration table name. +pub const DECODE_COVERAGE_DECLARATIONS_TABLE_NAME: &str = "kb_sol_decode_coverage_declarations"; + +/// Observed decoder coverage classification table name. +pub const DECODE_COVERAGE_OBSERVATIONS_TABLE_NAME: &str = "kb_sol_decode_coverage_observations"; + +/// Versioned materialized output table name. +pub const MATERIALIZED_EVENTS_TABLE_NAME: &str = "kb_sol_mat_events"; + +/// Canonical transaction acquisition table names active since `0.3.1`. +pub const RAW_STORE_TABLE_NAMES: &[&str] = &[ + crate::migrations::RAW_TRANSACTIONS_TABLE_NAME, + crate::migrations::TRANSACTION_OBSERVATIONS_TABLE_NAME, +]; + +/// Core Solana table names introduced by `0.2.4`. +pub const CORE_STORE_TABLE_NAMES: &[&str] = &[ + crate::migrations::CORE_TRANSACTIONS_TABLE_NAME, + crate::migrations::CORE_ACCOUNT_KEYS_TABLE_NAME, + crate::migrations::CORE_INSTRUCTIONS_TABLE_NAME, + crate::migrations::CORE_INNER_INSTRUCTIONS_TABLE_NAME, + crate::migrations::CORE_LOGS_TABLE_NAME, + crate::migrations::CORE_BALANCE_CHANGES_TABLE_NAME, + crate::migrations::PROCESSING_LEDGER_TABLE_NAME, +]; + +/// Decode and materialization table names introduced by `0.4.0`. +pub const DECODE_STORE_TABLE_NAMES: &[&str] = &[ + crate::migrations::DECODE_EVENTS_TABLE_NAME, + crate::migrations::DECODE_COVERAGE_DECLARATIONS_TABLE_NAME, + crate::migrations::DECODE_COVERAGE_OBSERVATIONS_TABLE_NAME, + crate::migrations::MATERIALIZED_EVENTS_TABLE_NAME, +]; + +/// Allowed Solana table domains encoded after the `kb_sol_` prefix. +pub const ALLOWED_SOLANA_TABLE_DOMAINS: &[&str] = + &["raw", "core", "obs", "decode", "mat", "catalog", "agg", "ops", "wallet"]; + +/// Diagnostic metadata for one expected PostgreSQL table. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct PostgresTableDiagnosticSpec { + /// Expected table name. + pub table_name: &'static str, + /// Logical Solana domain encoded in the table name. + pub domain: &'static str, + /// Human-readable table role. + pub role: &'static str, +} + +/// Diagnostic metadata for canonical transaction acquisition tables. +pub fn raw_store_table_diagnostic_specs() -> [crate::migrations::PostgresTableDiagnosticSpec; 2] { + return [ + crate::migrations::PostgresTableDiagnosticSpec { + table_name: crate::RAW_TRANSACTIONS_TABLE_NAME, + domain: "raw", + role: "Canonical transactions", + }, + crate::migrations::PostgresTableDiagnosticSpec { + table_name: crate::TRANSACTION_OBSERVATIONS_TABLE_NAME, + domain: "obs", + role: "Transaction acquisition observations", + }, + ]; +} + +/// Diagnostic metadata for core Solana store tables. +pub fn core_store_table_diagnostic_specs() -> [crate::migrations::PostgresTableDiagnosticSpec; 7] { + return [ + crate::migrations::PostgresTableDiagnosticSpec { + table_name: crate::CORE_TRANSACTIONS_TABLE_NAME, + domain: "core", + role: "Transactions", + }, + crate::migrations::PostgresTableDiagnosticSpec { + table_name: crate::CORE_ACCOUNT_KEYS_TABLE_NAME, + domain: "core", + role: "Resolved account keys", + }, + crate::migrations::PostgresTableDiagnosticSpec { + table_name: crate::CORE_INSTRUCTIONS_TABLE_NAME, + domain: "core", + role: "Top-level instructions", + }, + crate::migrations::PostgresTableDiagnosticSpec { + table_name: crate::CORE_INNER_INSTRUCTIONS_TABLE_NAME, + domain: "core", + role: "Inner instruction tree", + }, + crate::migrations::PostgresTableDiagnosticSpec { + table_name: crate::CORE_LOGS_TABLE_NAME, + domain: "core", + role: "Transaction logs", + }, + crate::migrations::PostgresTableDiagnosticSpec { + table_name: crate::CORE_BALANCE_CHANGES_TABLE_NAME, + domain: "core", + role: "Balance changes", + }, + crate::migrations::PostgresTableDiagnosticSpec { + table_name: crate::PROCESSING_LEDGER_TABLE_NAME, + domain: "ops", + role: "Processing ledger", + }, + ]; +} + +/// Diagnostic metadata for decode and materialization store tables. +pub fn decode_store_table_diagnostic_specs() -> [crate::migrations::PostgresTableDiagnosticSpec; 4] +{ + return [ + crate::migrations::PostgresTableDiagnosticSpec { + table_name: crate::DECODE_EVENTS_TABLE_NAME, + domain: "decode", + role: "Versioned decoded observations", + }, + crate::migrations::PostgresTableDiagnosticSpec { + table_name: crate::DECODE_COVERAGE_DECLARATIONS_TABLE_NAME, + domain: "decode", + role: "Declared decoder coverage", + }, + crate::migrations::PostgresTableDiagnosticSpec { + table_name: crate::DECODE_COVERAGE_OBSERVATIONS_TABLE_NAME, + domain: "decode", + role: "Observed decoder coverage", + }, + crate::migrations::PostgresTableDiagnosticSpec { + table_name: crate::MATERIALIZED_EVENTS_TABLE_NAME, + domain: "mat", + role: "Versioned materialized outputs", + }, + ]; +} + +/// Idempotent SQL statements creating or upgrading the canonical transaction acquisition store. +pub fn raw_store_schema_statements() -> std::vec::Vec<&'static str> { + return vec![ + crate::migrations::migrate_legacy_raw_table_name_sql(), + crate::migrations::migrate_legacy_raw_columns_sql(), + crate::migrations::migrate_legacy_raw_constraints_sql(), + crate::migrations::migrate_legacy_raw_indexes_sql(), + crate::migrations::migrate_legacy_core_lineage_sql(), + crate::migrations::create_table_kb_sol_raw_transactions_sql(), + crate::migrations::create_ux_kb_sol_raw_transactions_signature_sql(), + crate::migrations::create_ix_kb_sol_raw_transactions_slot_sql(), + crate::migrations::create_ix_kb_sol_raw_transactions_created_at_sql(), + crate::migrations::create_ix_kb_sol_raw_transactions_processing_sql(), + crate::migrations::create_ix_kb_sol_raw_transactions_canonical_hash_sql(), + crate::migrations::create_table_kb_sol_obs_transaction_observations_sql(), + crate::migrations::create_ux_kb_sol_obs_transaction_observations_key_sql(), + crate::migrations::create_ix_kb_sol_obs_transaction_observations_signature_sql(), + crate::migrations::create_ix_kb_sol_obs_transaction_observations_slot_sql(), + crate::migrations::create_ix_kb_sol_obs_transaction_observations_provider_method_sql(), + crate::migrations::create_ix_kb_sol_obs_transaction_observations_received_at_sql(), + crate::migrations::create_ix_kb_sol_obs_transaction_observations_raw_transaction_sql(), + crate::migrations::create_ix_kb_sol_obs_transaction_observations_status_sql(), + crate::migrations::migrate_and_drop_legacy_ws_notifications_sql(), + ]; +} + +/// Idempotent SQL statements creating the minimal core Solana store. +pub fn core_store_schema_statements() -> [&'static str; 28] { + return [ + crate::migrations::create_table_kb_sol_core_transactions_sql(), + crate::migrations::create_ux_kb_sol_core_transactions_signature_sql(), + crate::migrations::create_ix_kb_sol_core_transactions_slot_sql(), + crate::migrations::create_ix_kb_sol_core_transactions_created_at_sql(), + crate::migrations::create_table_kb_sol_core_account_keys_sql(), + crate::migrations::create_ux_kb_sol_core_account_keys_sig_index_sql(), + crate::migrations::create_ix_kb_sol_core_account_keys_key_sql(), + crate::migrations::create_table_kb_sol_core_instructions_sql(), + crate::migrations::create_ux_kb_sol_core_instructions_sig_path_sql(), + crate::migrations::create_ix_kb_sol_core_instructions_program_sql(), + crate::migrations::create_ix_kb_sol_core_instructions_slot_sql(), + crate::migrations::create_ix_kb_sol_core_instructions_processing_sql(), + crate::migrations::create_table_kb_sol_core_inner_instructions_sql(), + crate::migrations::create_ux_kb_sol_core_inner_instructions_sig_path_sql(), + crate::migrations::create_ix_kb_sol_core_inner_instructions_parent_sql(), + crate::migrations::create_ix_kb_sol_core_inner_instructions_program_sql(), + crate::migrations::create_table_kb_sol_core_logs_sql(), + crate::migrations::create_ux_kb_sol_core_logs_sig_index_sql(), + crate::migrations::create_ix_kb_sol_core_logs_program_sql(), + crate::migrations::create_ix_kb_sol_core_logs_path_sql(), + crate::migrations::create_table_kb_sol_core_balance_changes_sql(), + crate::migrations::create_ux_kb_sol_core_balance_changes_sig_index_sql(), + crate::migrations::create_ix_kb_sol_core_balance_changes_account_sql(), + crate::migrations::create_ix_kb_sol_core_balance_changes_mint_sql(), + crate::migrations::create_table_kb_sol_ops_processing_ledger_sql(), + crate::migrations::create_ux_kb_sol_ops_processing_ledger_identity_sql(), + crate::migrations::create_ix_kb_sol_ops_processing_ledger_status_sql(), + crate::migrations::create_ix_kb_sol_ops_processing_ledger_input_hash_sql(), + ]; +} + +/// Idempotent SQL statements creating the common decode and materialization store. +pub fn decode_store_schema_statements() -> [&'static str; 15] { + return [ + crate::migrations::create_table_kb_sol_decode_events_sql(), + crate::migrations::create_ux_kb_sol_decode_events_identity_sql(), + crate::migrations::create_ix_kb_sol_decode_events_signature_path_sql(), + crate::migrations::create_ix_kb_sol_decode_events_program_surface_sql(), + crate::migrations::create_ix_kb_sol_decode_events_family_commit_sql(), + crate::migrations::create_table_kb_sol_decode_coverage_declarations_sql(), + crate::migrations::create_ux_kb_sol_decode_coverage_declarations_identity_sql(), + crate::migrations::create_ix_kb_sol_decode_coverage_declarations_program_sql(), + crate::migrations::create_table_kb_sol_decode_coverage_observations_sql(), + crate::migrations::create_ux_kb_sol_decode_coverage_observations_identity_sql(), + crate::migrations::create_ix_kb_sol_decode_coverage_observations_program_status_sql(), + crate::migrations::create_ix_kb_sol_decode_coverage_observations_entry_sql(), + crate::migrations::create_table_kb_sol_mat_events_sql(), + crate::migrations::create_ux_kb_sol_mat_events_identity_sql(), + crate::migrations::create_ix_kb_sol_mat_events_signature_family_sql(), + ]; +} + +/// SQL conditionally renaming the historical raw RPC transaction table. +pub(crate) fn migrate_legacy_raw_table_name_sql() -> &'static str { + return r#"DO $$ +BEGIN + IF to_regclass('kb_sol_raw_rpc_transactions') IS NOT NULL + AND to_regclass('kb_sol_raw_transactions') IS NULL THEN + ALTER TABLE kb_sol_raw_rpc_transactions RENAME TO kb_sol_raw_transactions; + ELSIF to_regclass('kb_sol_raw_rpc_transactions') IS NOT NULL + AND to_regclass('kb_sol_raw_transactions') IS NOT NULL THEN + RAISE EXCEPTION 'both legacy and canonical raw transaction tables exist'; + END IF; +END +$$"#; +} + +/// SQL conditionally renaming historical raw payload columns and adding the format version. +pub(crate) fn migrate_legacy_raw_columns_sql() -> &'static str { + return r#"DO $$ +BEGIN + IF to_regclass('kb_sol_raw_transactions') IS NOT NULL THEN + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'kb_sol_raw_transactions' AND column_name = 'raw_json') + AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'kb_sol_raw_transactions' AND column_name = 'canonical_json') THEN + ALTER TABLE kb_sol_raw_transactions RENAME COLUMN raw_json TO canonical_json; + END IF; + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'kb_sol_raw_transactions' AND column_name = 'raw_json_hash') + AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'kb_sol_raw_transactions' AND column_name = 'canonical_json_hash') THEN + ALTER TABLE kb_sol_raw_transactions RENAME COLUMN raw_json_hash TO canonical_json_hash; + END IF; + ALTER TABLE kb_sol_raw_transactions ADD COLUMN IF NOT EXISTS canonical_format_version INTEGER NOT NULL DEFAULT 1; + END IF; +END +$$"#; +} + +/// SQL conditionally renaming historical canonical transaction constraints. +pub(crate) fn migrate_legacy_raw_constraints_sql() -> &'static str { + return r#"DO $$ +BEGIN + IF to_regclass('kb_sol_raw_transactions') IS NOT NULL THEN + IF EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'kb_sol_raw_transactions'::regclass AND conname = 'pk_kb_sol_raw_rpc_transactions') THEN ALTER TABLE kb_sol_raw_transactions RENAME CONSTRAINT pk_kb_sol_raw_rpc_transactions TO pk_kb_sol_raw_transactions; END IF; + IF EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'kb_sol_raw_transactions'::regclass AND conname = 'ck_kb_sol_raw_rpc_transactions_signature_not_empty') THEN ALTER TABLE kb_sol_raw_transactions RENAME CONSTRAINT ck_kb_sol_raw_rpc_transactions_signature_not_empty TO ck_kb_sol_raw_transactions_signature_not_empty; END IF; + IF EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'kb_sol_raw_transactions'::regclass AND conname = 'ck_kb_sol_raw_rpc_transactions_slot_non_negative') THEN ALTER TABLE kb_sol_raw_transactions RENAME CONSTRAINT ck_kb_sol_raw_rpc_transactions_slot_non_negative TO ck_kb_sol_raw_transactions_slot_non_negative; END IF; + IF EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'kb_sol_raw_transactions'::regclass AND conname = 'ck_kb_sol_raw_rpc_transactions_retention_state') THEN ALTER TABLE kb_sol_raw_transactions RENAME CONSTRAINT ck_kb_sol_raw_rpc_transactions_retention_state TO ck_kb_sol_raw_transactions_retention_state; END IF; + IF EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'kb_sol_raw_transactions'::regclass AND conname = 'ck_kb_sol_raw_rpc_transactions_processing_state') THEN ALTER TABLE kb_sol_raw_transactions RENAME CONSTRAINT ck_kb_sol_raw_rpc_transactions_processing_state TO ck_kb_sol_raw_transactions_processing_state; END IF; + IF EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'kb_sol_raw_transactions'::regclass AND conname = 'ck_kb_sol_raw_rpc_transactions_full_has_json') THEN ALTER TABLE kb_sol_raw_transactions RENAME CONSTRAINT ck_kb_sol_raw_rpc_transactions_full_has_json TO ck_kb_sol_raw_transactions_full_has_json; END IF; + IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'kb_sol_raw_transactions'::regclass AND conname = 'ck_kb_sol_raw_transactions_format_version_positive') THEN ALTER TABLE kb_sol_raw_transactions ADD CONSTRAINT ck_kb_sol_raw_transactions_format_version_positive CHECK (canonical_format_version > 0); END IF; + END IF; +END +$$"#; +} + +/// SQL conditionally renaming historical canonical transaction indexes. +pub(crate) fn migrate_legacy_raw_indexes_sql() -> &'static str { + return r#"DO $$ +BEGIN + IF to_regclass('ux_kb_sol_raw_rpc_transactions_signature') IS NOT NULL AND to_regclass('ux_kb_sol_raw_transactions_signature') IS NULL THEN ALTER INDEX ux_kb_sol_raw_rpc_transactions_signature RENAME TO ux_kb_sol_raw_transactions_signature; END IF; + IF to_regclass('ix_kb_sol_raw_rpc_transactions_slot') IS NOT NULL AND to_regclass('ix_kb_sol_raw_transactions_slot') IS NULL THEN ALTER INDEX ix_kb_sol_raw_rpc_transactions_slot RENAME TO ix_kb_sol_raw_transactions_slot; END IF; + IF to_regclass('ix_kb_sol_raw_rpc_transactions_created_at') IS NOT NULL AND to_regclass('ix_kb_sol_raw_transactions_created_at') IS NULL THEN ALTER INDEX ix_kb_sol_raw_rpc_transactions_created_at RENAME TO ix_kb_sol_raw_transactions_created_at; END IF; + IF to_regclass('ix_kb_sol_raw_rpc_transactions_processing') IS NOT NULL AND to_regclass('ix_kb_sol_raw_transactions_processing') IS NULL THEN ALTER INDEX ix_kb_sol_raw_rpc_transactions_processing RENAME TO ix_kb_sol_raw_transactions_processing; END IF; +END +$$"#; +} + +/// SQL conditionally renaming historical core-to-raw lineage. +pub(crate) fn migrate_legacy_core_lineage_sql() -> &'static str { + return r#"DO $$ +BEGIN + IF to_regclass('kb_sol_core_transactions') IS NOT NULL THEN + IF EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'kb_sol_core_transactions' AND column_name = 'raw_rpc_transaction_id') + AND NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_schema = current_schema() AND table_name = 'kb_sol_core_transactions' AND column_name = 'raw_transaction_id') THEN + ALTER TABLE kb_sol_core_transactions RENAME COLUMN raw_rpc_transaction_id TO raw_transaction_id; + END IF; + IF EXISTS (SELECT 1 FROM pg_constraint WHERE conrelid = 'kb_sol_core_transactions'::regclass AND conname = 'fk_kb_sol_core_transactions_raw_rpc_transaction') THEN + ALTER TABLE kb_sol_core_transactions RENAME CONSTRAINT fk_kb_sol_core_transactions_raw_rpc_transaction TO fk_kb_sol_core_transactions_raw_transaction; + END IF; + END IF; +END +$$"#; +} + +/// SQL creating `kb_sol_raw_transactions`. +pub(crate) fn create_table_kb_sol_raw_transactions_sql() -> &'static str { + return r#"CREATE TABLE IF NOT EXISTS kb_sol_raw_transactions ( + id BIGSERIAL, + signature TEXT NOT NULL, + slot BIGINT NOT NULL, + canonical_json JSONB, + canonical_json_hash TEXT, + canonical_format_version INTEGER NOT NULL DEFAULT 1, + retention_state TEXT NOT NULL DEFAULT 'full', + processing_state TEXT NOT NULL DEFAULT 'received', + lifecycle_reason TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_raw_transactions PRIMARY KEY (id), + CONSTRAINT ck_kb_sol_raw_transactions_signature_not_empty CHECK (length(btrim(signature)) > 0), + CONSTRAINT ck_kb_sol_raw_transactions_slot_non_negative CHECK (slot >= 0), + CONSTRAINT ck_kb_sol_raw_transactions_format_version_positive CHECK (canonical_format_version > 0), + CONSTRAINT ck_kb_sol_raw_transactions_retention_state CHECK (retention_state IN ('full', 'compacted', 'archived', 'purged')), + CONSTRAINT ck_kb_sol_raw_transactions_processing_state CHECK (processing_state IN ('received', 'core_extracted', 'decoded', 'materialized', 'failed')), + CONSTRAINT ck_kb_sol_raw_transactions_full_has_json CHECK (retention_state <> 'full' OR canonical_json IS NOT NULL) + )"#; +} + +/// SQL creating the unique signature index for canonical raw transactions. +pub(crate) fn create_ux_kb_sol_raw_transactions_signature_sql() -> &'static str { + return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_raw_transactions_signature ON kb_sol_raw_transactions (signature)"; +} + +/// SQL creating the slot index for canonical raw transactions. +pub(crate) fn create_ix_kb_sol_raw_transactions_slot_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_slot ON kb_sol_raw_transactions (slot)"; +} + +/// SQL creating the created timestamp index for canonical raw transactions. +pub(crate) fn create_ix_kb_sol_raw_transactions_created_at_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_created_at ON kb_sol_raw_transactions (created_at)"; +} + +/// SQL creating the processing state index for canonical raw transactions. +pub(crate) fn create_ix_kb_sol_raw_transactions_processing_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_processing ON kb_sol_raw_transactions (processing_state)"; +} + +/// SQL creating the optional canonical document hash index. +pub(crate) fn create_ix_kb_sol_raw_transactions_canonical_hash_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_raw_transactions_canonical_hash ON kb_sol_raw_transactions (canonical_json_hash) WHERE canonical_json_hash IS NOT NULL"; +} + +/// SQL creating `kb_sol_obs_transaction_observations`. +pub(crate) fn create_table_kb_sol_obs_transaction_observations_sql() -> &'static str { + return r#"CREATE TABLE IF NOT EXISTS kb_sol_obs_transaction_observations ( + id BIGSERIAL, + raw_transaction_id BIGINT, + observation_key TEXT NOT NULL, + signature TEXT, + slot BIGINT, + provider TEXT NOT NULL, + endpoint_code TEXT, + protocol TEXT NOT NULL, + acquisition_method TEXT NOT NULL, + origin TEXT NOT NULL, + commitment TEXT, + capture_session_id TEXT, + filter_code TEXT, + detected_at TIMESTAMPTZ, + received_at TIMESTAMPTZ NOT NULL, + normalized_at TIMESTAMPTZ, + persisted_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + payload_size_bytes BIGINT, + source_payload_hash TEXT, + status TEXT NOT NULL DEFAULT 'received', + error_code TEXT, + error_message TEXT, + CONSTRAINT pk_kb_sol_obs_transaction_observations PRIMARY KEY (id), + CONSTRAINT fk_kb_sol_obs_transaction_observations_raw_transaction FOREIGN KEY (raw_transaction_id) REFERENCES kb_sol_raw_transactions(id) ON DELETE SET NULL, + CONSTRAINT ck_kb_sol_obs_transaction_observations_key_not_empty CHECK (length(btrim(observation_key)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_signature_not_empty CHECK (signature IS NULL OR length(btrim(signature)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_slot_non_negative CHECK (slot IS NULL OR slot >= 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_provider_not_empty CHECK (length(btrim(provider)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_endpoint_not_empty CHECK (endpoint_code IS NULL OR length(btrim(endpoint_code)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_protocol_not_empty CHECK (length(btrim(protocol)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_method_not_empty CHECK (length(btrim(acquisition_method)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_origin CHECK (origin IN ('live', 'backfill', 'replay', 'repair', 'migration')), + CONSTRAINT ck_kb_sol_obs_transaction_observations_commitment_not_empty CHECK (commitment IS NULL OR length(btrim(commitment)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_session_not_empty CHECK (capture_session_id IS NULL OR length(btrim(capture_session_id)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_filter_not_empty CHECK (filter_code IS NULL OR length(btrim(filter_code)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_payload_size_non_negative CHECK (payload_size_bytes IS NULL OR payload_size_bytes >= 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_hash_not_empty CHECK (source_payload_hash IS NULL OR length(btrim(source_payload_hash)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_status CHECK (status IN ('detected', 'received', 'normalized', 'persisted', 'failed', 'missing')), + CONSTRAINT ck_kb_sol_obs_transaction_observations_error_code_not_empty CHECK (error_code IS NULL OR length(btrim(error_code)) > 0), + CONSTRAINT ck_kb_sol_obs_transaction_observations_error_message_not_empty CHECK (error_message IS NULL OR length(btrim(error_message)) > 0) + )"#; +} + +/// SQL creating the unique observation key index. +pub(crate) fn create_ux_kb_sol_obs_transaction_observations_key_sql() -> &'static str { + return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_obs_transaction_observations_key ON kb_sol_obs_transaction_observations (observation_key)"; +} + +/// SQL creating the optional signature index for transaction observations. +pub(crate) fn create_ix_kb_sol_obs_transaction_observations_signature_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_signature ON kb_sol_obs_transaction_observations (signature) WHERE signature IS NOT NULL"; +} + +/// SQL creating the optional slot index for transaction observations. +pub(crate) fn create_ix_kb_sol_obs_transaction_observations_slot_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_slot ON kb_sol_obs_transaction_observations (slot) WHERE slot IS NOT NULL"; +} + +/// SQL creating the provider and acquisition method index for transaction observations. +pub(crate) fn create_ix_kb_sol_obs_transaction_observations_provider_method_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_provider_method ON kb_sol_obs_transaction_observations (provider, acquisition_method)"; +} + +/// SQL creating the received timestamp index for transaction observations. +pub(crate) fn create_ix_kb_sol_obs_transaction_observations_received_at_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_received_at ON kb_sol_obs_transaction_observations (received_at)"; +} + +/// SQL creating the optional canonical transaction lineage index for observations. +pub(crate) fn create_ix_kb_sol_obs_transaction_observations_raw_transaction_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_raw_transaction ON kb_sol_obs_transaction_observations (raw_transaction_id) WHERE raw_transaction_id IS NOT NULL"; +} + +/// SQL creating the status index for transaction observations. +pub(crate) fn create_ix_kb_sol_obs_transaction_observations_status_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_obs_transaction_observations_status ON kb_sol_obs_transaction_observations (status)"; +} + +/// SQL migrating lightweight metadata and dropping the historical WebSocket payload table. +pub(crate) fn migrate_and_drop_legacy_ws_notifications_sql() -> &'static str { + return r#"DO $$ +BEGIN + IF to_regclass('kb_sol_raw_ws_notifications') IS NOT NULL THEN + INSERT INTO kb_sol_obs_transaction_observations (raw_transaction_id, observation_key, signature, slot, provider, protocol, acquisition_method, origin, received_at, persisted_at, payload_size_bytes, source_payload_hash, status, error_message) + SELECT linked_raw_rpc_transaction_id, notification_key, signature, slot, 'legacy_unknown', 'solana_websocket', subscription_kind, 'migration', created_at, created_at, CASE WHEN raw_json IS NULL THEN NULL ELSE octet_length(convert_to(raw_json::text, 'UTF8'))::BIGINT END, NULLIF(btrim(raw_json_hash), ''), CASE WHEN processing_state = 'failed' THEN 'failed' ELSE 'received' END, NULLIF(btrim(lifecycle_reason), '') + FROM kb_sol_raw_ws_notifications + ON CONFLICT (observation_key) DO NOTHING; + DROP TABLE kb_sol_raw_ws_notifications; + END IF; +END +$$"#; +} + +/// SQL creating `kb_sol_core_transactions`. +pub(crate) fn create_table_kb_sol_core_transactions_sql() -> &'static str { + return r#"CREATE TABLE IF NOT EXISTS kb_sol_core_transactions ( + id BIGSERIAL, + raw_transaction_id BIGINT, + signature TEXT NOT NULL, + slot BIGINT NOT NULL, + failed BOOLEAN NOT NULL DEFAULT FALSE, + err_json JSONB, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_core_transactions PRIMARY KEY (id), + CONSTRAINT fk_kb_sol_core_transactions_raw_transaction FOREIGN KEY (raw_transaction_id) REFERENCES kb_sol_raw_transactions(id) ON DELETE SET NULL, + CONSTRAINT ck_kb_sol_core_transactions_signature_not_empty CHECK (length(btrim(signature)) > 0), + CONSTRAINT ck_kb_sol_core_transactions_slot_non_negative CHECK (slot >= 0) + )"#; +} + +/// SQL creating the unique signature index for core transactions. +pub(crate) fn create_ux_kb_sol_core_transactions_signature_sql() -> &'static str { + return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_transactions_signature ON kb_sol_core_transactions (signature)"; +} + +/// SQL creating the slot index for core transactions. +pub(crate) fn create_ix_kb_sol_core_transactions_slot_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_transactions_slot ON kb_sol_core_transactions (slot)"; +} + +/// SQL creating the created_at index for core transactions. +pub(crate) fn create_ix_kb_sol_core_transactions_created_at_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_transactions_created_at ON kb_sol_core_transactions (created_at)"; +} + +/// SQL creating `kb_sol_core_account_keys`. +pub(crate) fn create_table_kb_sol_core_account_keys_sql() -> &'static str { + return r#"CREATE TABLE IF NOT EXISTS kb_sol_core_account_keys ( + id BIGSERIAL, + transaction_id BIGINT NOT NULL, + signature TEXT NOT NULL, + slot BIGINT NOT NULL, + account_index INTEGER NOT NULL, + account_key TEXT NOT NULL, + source TEXT NOT NULL, + writable BOOLEAN NOT NULL, + signer BOOLEAN NOT NULL, + executable BOOLEAN, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_core_account_keys PRIMARY KEY (id), + CONSTRAINT fk_kb_sol_core_account_keys_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE, + CONSTRAINT ck_kb_sol_core_account_keys_signature_not_empty CHECK (length(btrim(signature)) > 0), + CONSTRAINT ck_kb_sol_core_account_keys_slot_non_negative CHECK (slot >= 0), + CONSTRAINT ck_kb_sol_core_account_keys_index_non_negative CHECK (account_index >= 0), + CONSTRAINT ck_kb_sol_core_account_keys_key_not_empty CHECK (length(btrim(account_key)) > 0), + CONSTRAINT ck_kb_sol_core_account_keys_source CHECK (source IN ('static', 'loaded_writable', 'loaded_readonly')) + )"#; +} + +/// SQL creating the unique signature/account index for core account keys. +pub(crate) fn create_ux_kb_sol_core_account_keys_sig_index_sql() -> &'static str { + return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_account_keys_sig_index ON kb_sol_core_account_keys (signature, account_index)"; +} + +/// SQL creating the account key index for core account keys. +pub(crate) fn create_ix_kb_sol_core_account_keys_key_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_account_keys_key ON kb_sol_core_account_keys (account_key)"; +} + +/// SQL creating `kb_sol_core_instructions`. +pub(crate) fn create_table_kb_sol_core_instructions_sql() -> &'static str { + return r#"CREATE TABLE IF NOT EXISTS kb_sol_core_instructions ( + id BIGSERIAL, + transaction_id BIGINT NOT NULL, + signature TEXT NOT NULL, + slot BIGINT NOT NULL, + instruction_path TEXT NOT NULL, + program_id TEXT NOT NULL, + accounts_json JSONB NOT NULL, + payload_json JSONB, + payload_json_hash TEXT, + processing_state TEXT NOT NULL DEFAULT 'pending', + processor_name TEXT, + processor_version TEXT, + lifecycle_reason TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_core_instructions PRIMARY KEY (id), + CONSTRAINT fk_kb_sol_core_instructions_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE, + CONSTRAINT ck_kb_sol_core_instructions_signature_not_empty CHECK (length(btrim(signature)) > 0), + CONSTRAINT ck_kb_sol_core_instructions_slot_non_negative CHECK (slot >= 0), + CONSTRAINT ck_kb_sol_core_instructions_path_not_empty CHECK (length(btrim(instruction_path)) > 0), + CONSTRAINT ck_kb_sol_core_instructions_program_not_empty CHECK (length(btrim(program_id)) > 0), + CONSTRAINT ck_kb_sol_core_instructions_processing_state CHECK (processing_state IN ('pending', 'decoded', 'materialized', 'ignored', 'failed', 'replay_requested')) + )"#; +} + +/// SQL creating the unique signature/path index for core instructions. +pub(crate) fn create_ux_kb_sol_core_instructions_sig_path_sql() -> &'static str { + return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_instructions_sig_path ON kb_sol_core_instructions (signature, instruction_path)"; +} + +/// SQL creating the program index for core instructions. +pub(crate) fn create_ix_kb_sol_core_instructions_program_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_instructions_program ON kb_sol_core_instructions (program_id)"; +} + +/// SQL creating the slot index for core instructions. +pub(crate) fn create_ix_kb_sol_core_instructions_slot_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_instructions_slot ON kb_sol_core_instructions (slot)"; +} + +/// SQL creating the processing state index for core instructions. +pub(crate) fn create_ix_kb_sol_core_instructions_processing_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_instructions_processing ON kb_sol_core_instructions (processing_state)"; +} + +/// SQL creating `kb_sol_core_inner_instructions`. +pub(crate) fn create_table_kb_sol_core_inner_instructions_sql() -> &'static str { + return r#"CREATE TABLE IF NOT EXISTS kb_sol_core_inner_instructions ( + id BIGSERIAL, + transaction_id BIGINT NOT NULL, + signature TEXT NOT NULL, + slot BIGINT NOT NULL, + parent_instruction_path TEXT NOT NULL, + instruction_path TEXT NOT NULL, + program_id TEXT NOT NULL, + accounts_json JSONB NOT NULL, + payload_json JSONB, + payload_json_hash TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_core_inner_instructions PRIMARY KEY (id), + CONSTRAINT fk_kb_sol_core_inner_instructions_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE, + CONSTRAINT ck_kb_sol_core_inner_instructions_signature_not_empty CHECK (length(btrim(signature)) > 0), + CONSTRAINT ck_kb_sol_core_inner_instructions_slot_non_negative CHECK (slot >= 0), + CONSTRAINT ck_kb_sol_core_inner_instructions_parent_not_empty CHECK (length(btrim(parent_instruction_path)) > 0), + CONSTRAINT ck_kb_sol_core_inner_instructions_path_not_empty CHECK (length(btrim(instruction_path)) > 0), + CONSTRAINT ck_kb_sol_core_inner_instructions_program_not_empty CHECK (length(btrim(program_id)) > 0) + )"#; +} + +/// SQL creating the unique signature/path index for core inner instructions. +pub(crate) fn create_ux_kb_sol_core_inner_instructions_sig_path_sql() -> &'static str { + return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_inner_instructions_sig_path ON kb_sol_core_inner_instructions (signature, instruction_path)"; +} + +/// SQL creating the parent instruction index for core inner instructions. +pub(crate) fn create_ix_kb_sol_core_inner_instructions_parent_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_inner_instructions_parent ON kb_sol_core_inner_instructions (signature, parent_instruction_path)"; +} + +/// SQL creating the program index for core inner instructions. +pub(crate) fn create_ix_kb_sol_core_inner_instructions_program_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_inner_instructions_program ON kb_sol_core_inner_instructions (program_id)"; +} + +/// SQL creating `kb_sol_core_logs`. +pub(crate) fn create_table_kb_sol_core_logs_sql() -> &'static str { + return r#"CREATE TABLE IF NOT EXISTS kb_sol_core_logs ( + id BIGSERIAL, + transaction_id BIGINT NOT NULL, + signature TEXT NOT NULL, + slot BIGINT NOT NULL, + log_index INTEGER NOT NULL, + instruction_path TEXT, + program_id TEXT, + log_text TEXT, + log_text_hash TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_core_logs PRIMARY KEY (id), + CONSTRAINT fk_kb_sol_core_logs_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE, + CONSTRAINT ck_kb_sol_core_logs_signature_not_empty CHECK (length(btrim(signature)) > 0), + CONSTRAINT ck_kb_sol_core_logs_slot_non_negative CHECK (slot >= 0), + CONSTRAINT ck_kb_sol_core_logs_index_non_negative CHECK (log_index >= 0), + CONSTRAINT ck_kb_sol_core_logs_text_not_empty CHECK (log_text IS NULL OR length(btrim(log_text)) > 0), + CONSTRAINT ck_kb_sol_core_logs_path_not_empty CHECK (instruction_path IS NULL OR length(btrim(instruction_path)) > 0), + CONSTRAINT ck_kb_sol_core_logs_program_not_empty CHECK (program_id IS NULL OR length(btrim(program_id)) > 0) + )"#; +} + +/// SQL creating the unique signature/log index for core logs. +pub(crate) fn create_ux_kb_sol_core_logs_sig_index_sql() -> &'static str { + return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_logs_sig_index ON kb_sol_core_logs (signature, log_index)"; +} + +/// SQL creating the program index for core logs. +pub(crate) fn create_ix_kb_sol_core_logs_program_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_logs_program ON kb_sol_core_logs (program_id) WHERE program_id IS NOT NULL"; +} + +/// SQL creating the instruction path index for core logs. +pub(crate) fn create_ix_kb_sol_core_logs_path_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_logs_path ON kb_sol_core_logs (signature, instruction_path) WHERE instruction_path IS NOT NULL"; +} + +/// SQL creating `kb_sol_core_balance_changes`. +pub(crate) fn create_table_kb_sol_core_balance_changes_sql() -> &'static str { + return r#"CREATE TABLE IF NOT EXISTS kb_sol_core_balance_changes ( + id BIGSERIAL, + transaction_id BIGINT NOT NULL, + signature TEXT NOT NULL, + slot BIGINT NOT NULL, + balance_change_index INTEGER NOT NULL, + balance_kind TEXT NOT NULL, + account_index INTEGER, + account_key TEXT, + mint TEXT, + owner TEXT, + pre_balance_json JSONB, + post_balance_json JSONB, + delta_json JSONB, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_core_balance_changes PRIMARY KEY (id), + CONSTRAINT fk_kb_sol_core_balance_changes_transaction FOREIGN KEY (transaction_id) REFERENCES kb_sol_core_transactions(id) ON DELETE CASCADE, + CONSTRAINT ck_kb_sol_core_balance_changes_signature_not_empty CHECK (length(btrim(signature)) > 0), + CONSTRAINT ck_kb_sol_core_balance_changes_slot_non_negative CHECK (slot >= 0), + CONSTRAINT ck_kb_sol_core_balance_changes_index_non_negative CHECK (balance_change_index >= 0), + CONSTRAINT ck_kb_sol_core_balance_changes_account_index_non_negative CHECK (account_index IS NULL OR account_index >= 0), + CONSTRAINT ck_kb_sol_core_balance_changes_kind CHECK (balance_kind IN ('native_lamports', 'token_amount')), + CONSTRAINT ck_kb_sol_core_balance_changes_account_not_empty CHECK (account_key IS NULL OR length(btrim(account_key)) > 0), + CONSTRAINT ck_kb_sol_core_balance_changes_mint_not_empty CHECK (mint IS NULL OR length(btrim(mint)) > 0), + CONSTRAINT ck_kb_sol_core_balance_changes_owner_not_empty CHECK (owner IS NULL OR length(btrim(owner)) > 0) + )"#; +} + +/// SQL creating the unique signature/balance index for core balance changes. +pub(crate) fn create_ux_kb_sol_core_balance_changes_sig_index_sql() -> &'static str { + return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_core_balance_changes_sig_index ON kb_sol_core_balance_changes (signature, balance_change_index)"; +} + +/// SQL creating the account key index for core balance changes. +pub(crate) fn create_ix_kb_sol_core_balance_changes_account_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_balance_changes_account ON kb_sol_core_balance_changes (account_key) WHERE account_key IS NOT NULL"; +} + +/// SQL creating the mint index for core balance changes. +pub(crate) fn create_ix_kb_sol_core_balance_changes_mint_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_core_balance_changes_mint ON kb_sol_core_balance_changes (mint) WHERE mint IS NOT NULL"; +} + +/// Returns true when a Solana table name follows the canonical prefix and domain rules. +pub fn is_valid_solana_table_name(table_name: &str) -> bool { + return crate::migrations::validate_solana_table_name(table_name).is_ok(); +} + +/// Validates a canonical Solana table name. +pub fn validate_solana_table_name(table_name: &str) -> kb_core::Result<()> { + let trimmed_table_name = table_name.trim(); + if trimmed_table_name.is_empty() { + return std::result::Result::Err(kb_core::Error::db("table name must not be empty")); + } + if trimmed_table_name.contains('.') { + return std::result::Result::Err(kb_core::Error::db( + "solana table names must not contain an explicit PostgreSQL schema", + )); + } + if !trimmed_table_name.starts_with(crate::migrations::SOLANA_TABLE_PREFIX) { + return std::result::Result::Err(kb_core::Error::db( + "solana table name must start with kb_sol_", + )); + } + let suffix = &trimmed_table_name[crate::migrations::SOLANA_TABLE_PREFIX.len()..]; + let domain = crate::migrations::first_segment(suffix); + if domain.is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "solana table name must include a domain after kb_sol_", + )); + } + if !crate::migrations::is_allowed_domain(domain) { + return std::result::Result::Err(kb_core::Error::db( + "solana table name domain is not allowed", + )); + } + if suffix.len() <= domain.len() + 1 { + return std::result::Result::Err(kb_core::Error::db( + "solana table name must include a name after the domain", + )); + } + if !crate::migrations::contains_only_table_name_chars(trimmed_table_name) { + return std::result::Result::Err(kb_core::Error::db( + "solana table name must use lowercase ascii, digits and underscores only", + )); + } + return std::result::Result::Ok(()); +} + +/// Validates the raw store table names introduced by `0.2.3`. +pub fn validate_raw_store_table_names() -> kb_core::Result<()> { + return crate::migrations::validate_table_names(crate::migrations::RAW_STORE_TABLE_NAMES); +} + +/// Validates the core store table names introduced by `0.2.4`. +pub fn validate_core_store_table_names() -> kb_core::Result<()> { + return crate::migrations::validate_table_names(crate::migrations::CORE_STORE_TABLE_NAMES); +} + +/// Validates the decode and materialization table names introduced by `0.4.0`. +pub fn validate_decode_store_table_names() -> kb_core::Result<()> { + return crate::migrations::validate_table_names(crate::migrations::DECODE_STORE_TABLE_NAMES); +} + +fn validate_table_names(table_names: &[&str]) -> kb_core::Result<()> { + for table_name in table_names { + let validation_result = crate::migrations::validate_solana_table_name(table_name); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + } + return std::result::Result::Ok(()); +} + +fn first_segment(value: &str) -> &str { + return match value.split_once('_') { + std::option::Option::Some((first, _rest)) => first, + std::option::Option::None => value, + }; +} + +fn is_allowed_domain(domain: &str) -> bool { + for allowed_domain in crate::migrations::ALLOWED_SOLANA_TABLE_DOMAINS { + if domain == *allowed_domain { + return true; + } + } + return false; +} + +fn contains_only_table_name_chars(table_name: &str) -> bool { + for character in table_name.chars() { + if character == '_' { + continue; + } + if character.is_ascii_lowercase() { + continue; + } + if character.is_ascii_digit() { + continue; + } + return false; + } + return true; +} + +/// SQL creating `kb_sol_ops_processing_ledger`. +pub(crate) fn create_table_kb_sol_ops_processing_ledger_sql() -> &'static str { + return r#"CREATE TABLE IF NOT EXISTS kb_sol_ops_processing_ledger ( + id BIGSERIAL, + stage TEXT NOT NULL, + processor_name TEXT NOT NULL, + processor_version TEXT NOT NULL, + input_key TEXT NOT NULL, + input_hash TEXT NOT NULL, + status TEXT NOT NULL, + attempt_count INTEGER NOT NULL DEFAULT 0, + started_at TIMESTAMPTZ, + finished_at TIMESTAMPTZ, + error_code TEXT, + error_message TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_ops_processing_ledger PRIMARY KEY (id), + CONSTRAINT ck_kb_sol_ops_processing_ledger_stage_not_empty CHECK (length(btrim(stage)) > 0), + CONSTRAINT ck_kb_sol_ops_processing_ledger_processor_name_not_empty CHECK (length(btrim(processor_name)) > 0), + CONSTRAINT ck_kb_sol_ops_processing_ledger_processor_version_not_empty CHECK (length(btrim(processor_version)) > 0), + CONSTRAINT ck_kb_sol_ops_processing_ledger_input_key_not_empty CHECK (length(btrim(input_key)) > 0), + CONSTRAINT ck_kb_sol_ops_processing_ledger_input_hash_not_empty CHECK (length(btrim(input_hash)) > 0), + CONSTRAINT ck_kb_sol_ops_processing_ledger_status CHECK (status IN ('running', 'succeeded', 'failed')), + CONSTRAINT ck_kb_sol_ops_processing_ledger_attempt_count_non_negative CHECK (attempt_count >= 0), + CONSTRAINT ck_kb_sol_ops_processing_ledger_error_code_not_empty CHECK (error_code IS NULL OR length(btrim(error_code)) > 0), + CONSTRAINT ck_kb_sol_ops_processing_ledger_error_message_not_empty CHECK (error_message IS NULL OR length(btrim(error_message)) > 0) + )"#; +} + +/// SQL creating the unique processing ledger identity index. +pub(crate) fn create_ux_kb_sol_ops_processing_ledger_identity_sql() -> &'static str { + return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_ops_processing_ledger_identity ON kb_sol_ops_processing_ledger (stage, processor_name, processor_version, input_key)"; +} + +/// SQL creating the processing ledger status index. +pub(crate) fn create_ix_kb_sol_ops_processing_ledger_status_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_ops_processing_ledger_status ON kb_sol_ops_processing_ledger (stage, processor_name, status, updated_at)"; +} + +/// SQL creating the processing ledger input hash index. +pub(crate) fn create_ix_kb_sol_ops_processing_ledger_input_hash_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_ops_processing_ledger_input_hash ON kb_sol_ops_processing_ledger (input_hash)"; +} + +/// SQL statistics query for `kb_sol_raw_transactions`. +pub(crate) fn table_stats_kb_sol_raw_transactions_sql() -> &'static str { + return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_raw_transactions"; +} + +/// SQL statistics query for `kb_sol_obs_transaction_observations`. +pub(crate) fn table_stats_kb_sol_obs_transaction_observations_sql() -> &'static str { + return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(persisted_at)::text AS latest_created_at FROM kb_sol_obs_transaction_observations"; +} + +/// SQL statistics query for `kb_sol_core_transactions`. +pub(crate) fn table_stats_kb_sol_core_transactions_sql() -> &'static str { + return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_core_transactions"; +} + +/// SQL statistics query for `kb_sol_core_account_keys`. +pub(crate) fn table_stats_kb_sol_core_account_keys_sql() -> &'static str { + return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_core_account_keys"; +} + +/// SQL statistics query for `kb_sol_core_instructions`. +pub(crate) fn table_stats_kb_sol_core_instructions_sql() -> &'static str { + return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_core_instructions"; +} + +/// SQL statistics query for `kb_sol_core_inner_instructions`. +pub(crate) fn table_stats_kb_sol_core_inner_instructions_sql() -> &'static str { + return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_core_inner_instructions"; +} + +/// SQL statistics query for `kb_sol_core_logs`. +pub(crate) fn table_stats_kb_sol_core_logs_sql() -> &'static str { + return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_core_logs"; +} + +/// SQL statistics query for `kb_sol_core_balance_changes`. +pub(crate) fn table_stats_kb_sol_core_balance_changes_sql() -> &'static str { + return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_core_balance_changes"; +} + +/// SQL statistics query for `kb_sol_ops_processing_ledger`. +pub(crate) fn table_stats_kb_sol_ops_processing_ledger_sql() -> &'static str { + return "SELECT COUNT(*)::BIGINT AS row_count, NULL::BIGINT AS min_slot, NULL::BIGINT AS max_slot, MAX(updated_at)::text AS latest_created_at FROM kb_sol_ops_processing_ledger"; +} + +/// SQL statistics query for `kb_sol_decode_events`. +pub(crate) fn table_stats_kb_sol_decode_events_sql() -> &'static str { + return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_decode_events"; +} + +/// SQL statistics query for `kb_sol_decode_coverage_declarations`. +pub(crate) fn table_stats_kb_sol_decode_coverage_declarations_sql() -> &'static str { + return "SELECT COUNT(*)::BIGINT AS row_count, NULL::BIGINT AS min_slot, NULL::BIGINT AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_decode_coverage_declarations"; +} + +/// SQL statistics query for `kb_sol_decode_coverage_observations`. +pub(crate) fn table_stats_kb_sol_decode_coverage_observations_sql() -> &'static str { + return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_decode_coverage_observations"; +} + +/// SQL statistics query for `kb_sol_mat_events`. +pub(crate) fn table_stats_kb_sol_mat_events_sql() -> &'static str { + return "SELECT COUNT(*)::BIGINT AS row_count, MIN(slot) AS min_slot, MAX(slot) AS max_slot, MAX(created_at)::text AS latest_created_at FROM kb_sol_mat_events"; +} + +/// SQL creating `kb_sol_decode_events`. +pub(crate) fn create_table_kb_sol_decode_events_sql() -> &'static str { + return r#"CREATE TABLE IF NOT EXISTS kb_sol_decode_events ( + id BIGSERIAL, processor_name TEXT NOT NULL, processor_version TEXT NOT NULL, + input_key TEXT NOT NULL, input_hash TEXT NOT NULL, event_key TEXT NOT NULL, + signature TEXT NOT NULL, slot BIGINT NOT NULL, instruction_path TEXT NOT NULL, + program_id TEXT NOT NULL, protocol_code TEXT NOT NULL, surface_code TEXT NOT NULL, + event_code TEXT NOT NULL, event_name TEXT NOT NULL, event_family TEXT NOT NULL, + source_kind TEXT NOT NULL, confidence TEXT NOT NULL, proof_kind TEXT NOT NULL, + proof_jsonb JSONB NOT NULL, payload_jsonb JSONB NOT NULL, transaction_failed BOOLEAN NOT NULL, + transaction_error_jsonb JSONB, observation_committed BOOLEAN NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_decode_events PRIMARY KEY (id), + CONSTRAINT ck_kb_sol_decode_events_slot_non_negative CHECK (slot >= 0), + CONSTRAINT ck_kb_sol_decode_events_failed_not_committed CHECK (NOT transaction_failed OR NOT observation_committed) + )"#; +} + +/// SQL creating the decoded observation stable identity index. +pub(crate) fn create_ux_kb_sol_decode_events_identity_sql() -> &'static str { + return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_decode_events_processor_input_event ON kb_sol_decode_events (processor_name, processor_version, input_key, event_key)"; +} + +/// SQL creating the decoded observation signature and path index. +pub(crate) fn create_ix_kb_sol_decode_events_signature_path_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_events_signature_path ON kb_sol_decode_events (signature, instruction_path)"; +} + +/// SQL creating the decoded observation program and surface index. +pub(crate) fn create_ix_kb_sol_decode_events_program_surface_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_events_program_surface ON kb_sol_decode_events (program_id, surface_code, event_code)"; +} + +/// SQL creating the decoded observation family and commit index. +pub(crate) fn create_ix_kb_sol_decode_events_family_commit_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_events_family_commit ON kb_sol_decode_events (event_family, transaction_failed, observation_committed)"; +} + +/// SQL creating `kb_sol_decode_coverage_declarations`. +pub(crate) fn create_table_kb_sol_decode_coverage_declarations_sql() -> &'static str { + return r#"CREATE TABLE IF NOT EXISTS kb_sol_decode_coverage_declarations ( + id BIGSERIAL, processor_name TEXT NOT NULL, processor_version TEXT NOT NULL, + program_id TEXT NOT NULL, surface_code TEXT, entry_kind TEXT NOT NULL, + entry_code TEXT NOT NULL, discriminator_hex TEXT, historical BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_decode_coverage_declarations PRIMARY KEY (id) + )"#; +} + +/// SQL creating the declared coverage stable identity index. +pub(crate) fn create_ux_kb_sol_decode_coverage_declarations_identity_sql() -> &'static str { + return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_decode_coverage_declarations_identity ON kb_sol_decode_coverage_declarations (processor_name, processor_version, program_id, COALESCE(surface_code, ''), entry_kind, entry_code, COALESCE(discriminator_hex, ''))"; +} + +/// SQL creating the declared coverage program index. +pub(crate) fn create_ix_kb_sol_decode_coverage_declarations_program_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_coverage_declarations_program ON kb_sol_decode_coverage_declarations (program_id, processor_name, processor_version)"; +} + +/// SQL creating `kb_sol_decode_coverage_observations`. +pub(crate) fn create_table_kb_sol_decode_coverage_observations_sql() -> &'static str { + return r#"CREATE TABLE IF NOT EXISTS kb_sol_decode_coverage_observations ( + id BIGSERIAL, processor_name TEXT NOT NULL, processor_version TEXT NOT NULL, + input_key TEXT NOT NULL, input_hash TEXT NOT NULL, signature TEXT NOT NULL, + slot BIGINT NOT NULL, instruction_path TEXT NOT NULL, program_id TEXT NOT NULL, + surface_code TEXT, entry_code TEXT, discriminator_hex TEXT, status TEXT NOT NULL, + recognized BOOLEAN NOT NULL, decoded_count INTEGER NOT NULL DEFAULT 0, + materialized_count INTEGER NOT NULL DEFAULT 0, error_count INTEGER NOT NULL DEFAULT 0, + transaction_failed BOOLEAN NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_decode_coverage_observations PRIMARY KEY (id), + CONSTRAINT ck_kb_sol_decode_coverage_observations_slot_non_negative CHECK (slot >= 0), + CONSTRAINT ck_kb_sol_decode_coverage_observations_counts_non_negative CHECK (decoded_count >= 0 AND materialized_count >= 0 AND error_count >= 0) + )"#; +} + +/// SQL creating the observed coverage stable identity index. +pub(crate) fn create_ux_kb_sol_decode_coverage_observations_identity_sql() -> &'static str { + return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_decode_coverage_observations_identity ON kb_sol_decode_coverage_observations (processor_name, processor_version, input_key)"; +} + +/// SQL creating the observed coverage program and status index. +pub(crate) fn create_ix_kb_sol_decode_coverage_observations_program_status_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_coverage_observations_program_status ON kb_sol_decode_coverage_observations (program_id, status, transaction_failed)"; +} + +/// SQL creating the observed coverage entry index. +pub(crate) fn create_ix_kb_sol_decode_coverage_observations_entry_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_decode_coverage_observations_entry ON kb_sol_decode_coverage_observations (processor_name, processor_version, surface_code, entry_code)"; +} + +/// SQL creating `kb_sol_mat_events`. +pub(crate) fn create_table_kb_sol_mat_events_sql() -> &'static str { + return r#"CREATE TABLE IF NOT EXISTS kb_sol_mat_events ( + id BIGSERIAL, processor_name TEXT NOT NULL, processor_version TEXT NOT NULL, + input_key TEXT NOT NULL, input_hash TEXT NOT NULL, output_key TEXT NOT NULL, + source_event_key TEXT NOT NULL, source_decoder_name TEXT NOT NULL, + source_decoder_version TEXT NOT NULL, source_decode_input_key TEXT NOT NULL, + signature TEXT NOT NULL, slot BIGINT NOT NULL, materialized_family TEXT NOT NULL, + payload_jsonb JSONB NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + CONSTRAINT pk_kb_sol_mat_events PRIMARY KEY (id), + CONSTRAINT ck_kb_sol_mat_events_slot_non_negative CHECK (slot >= 0) + )"#; +} + +/// SQL creating the materialized output stable identity index. +pub(crate) fn create_ux_kb_sol_mat_events_identity_sql() -> &'static str { + return "CREATE UNIQUE INDEX IF NOT EXISTS ux_kb_sol_mat_events_processor_input_output ON kb_sol_mat_events (processor_name, processor_version, input_key, output_key)"; +} + +/// SQL creating materialized output signature and family indexes. +pub(crate) fn create_ix_kb_sol_mat_events_signature_family_sql() -> &'static str { + return "CREATE INDEX IF NOT EXISTS ix_kb_sol_mat_events_signature_family ON kb_sol_mat_events (source_decoder_name, source_decoder_version, source_decode_input_key, signature, materialized_family)"; +} + +#[cfg(test)] +mod tests { + #[test] + fn valid_table_name_accepts_domain_prefix() { + assert!(crate::is_valid_solana_table_name("kb_sol_raw_transactions")); + } + + #[test] + fn table_name_rejects_explicit_schema() { + let invalid_name = std::string::String::from("raw") + "." + "kb_sol_rpc_transactions"; + assert!(!crate::is_valid_solana_table_name(invalid_name.as_str())); + } + + #[test] + fn table_name_rejects_unknown_domain() { + assert!(!crate::is_valid_solana_table_name("kb_sol_unknown_rows")); + } + + #[test] + fn table_name_rejects_missing_table_suffix() { + assert!(!crate::is_valid_solana_table_name("kb_sol_raw")); + } + + #[test] + fn table_name_rejects_uppercase() { + assert!(!crate::is_valid_solana_table_name("kb_sol_raw_RPC_transactions")); + } + + #[test] + fn raw_store_table_names_are_valid() { + let result = crate::validate_raw_store_table_names(); + assert!(result.is_ok()); + } + + #[test] + fn core_store_table_names_are_valid() { + let result = crate::validate_core_store_table_names(); + assert!(result.is_ok()); + } + + #[test] + fn decode_store_table_names_are_valid() { + let result = crate::validate_decode_store_table_names(); + assert!(result.is_ok()); + } + + #[test] + fn decode_store_schema_contains_no_create_schema_statement() { + let forbidden = std::string::String::from("CREATE") + " " + "SCHEMA"; + for statement in crate::decode_store_schema_statements() { + assert!(!statement.contains(forbidden.as_str())); + } + } + + #[test] + fn raw_store_schema_contains_no_create_schema_statement() { + let forbidden = std::string::String::from("CREATE") + " " + "SCHEMA"; + for statement in crate::raw_store_schema_statements() { + assert!(!statement.contains(forbidden.as_str())); + } + } + + #[test] + fn core_store_schema_contains_no_create_schema_statement() { + let forbidden = std::string::String::from("CREATE") + " " + "SCHEMA"; + for statement in crate::core_store_schema_statements() { + assert!(!statement.contains(forbidden.as_str())); + } + } + + #[test] + fn raw_store_schema_uses_prefixed_index_names() { + let statements = crate::raw_store_schema_statements(); + assert!(statements.iter().any(|statement| -> bool { + return statement.contains("ux_kb_sol_raw_transactions_signature"); + })); + assert!(statements.iter().any(|statement| -> bool { + return statement.contains("ix_kb_sol_obs_transaction_observations_signature"); + })); + } + + #[test] + fn core_store_schema_uses_prefixed_constraint_and_index_names() { + let statements = crate::core_store_schema_statements(); + assert!(statements.iter().any(|statement| -> bool { + return statement.contains("pk_kb_sol_core_transactions"); + })); + assert!(statements.iter().any(|statement| -> bool { + return statement.contains("fk_kb_sol_core_instructions_transaction"); + })); + assert!(statements.iter().any(|statement| -> bool { + return statement.contains("ux_kb_sol_core_instructions_sig_path"); + })); + assert!(statements.iter().any(|statement| -> bool { + return statement.contains("ix_kb_sol_core_instructions_program"); + })); + assert!(statements.iter().any(|statement| -> bool { + return statement.contains("ux_kb_sol_ops_processing_ledger_identity"); + })); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/pg_store.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/pg_store.rs new file mode 100644 index 0000000..7a19984 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/pg_store.rs @@ -0,0 +1,704 @@ +// file: kb_store_pg/src/pg_store.rs +// version: 15 + +//! Store implementation scaffold for the `kb_store_pg` crate. + +/// PostgreSQL store connection options. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct PostgresStoreOptions { + /// Database URL or DSN. + pub database_url: std::string::String, + /// Maximum connection count. + pub max_connections: u32, + /// Connection timeout in milliseconds. + pub connect_timeout_ms: u64, + /// Enables idempotent raw schema initialization at startup. + pub auto_initialize_schema: bool, +} + +impl PostgresStoreOptions { + /// Builds options from typed PostgreSQL configuration. + pub fn from_postgres_config(config: &kb_config::PostgresConfig) -> kb_core::Result { + return crate::pg_store::PostgresStoreOptions::new( + config.url.clone(), + config.max_connections, + config.connect_timeout_ms, + config.auto_initialize_schema, + ); + } + + /// Builds options from the selected database backend configuration. + pub fn from_database_config(config: &kb_config::DatabaseConfig) -> kb_core::Result { + if config.backend != "postgres" { + return std::result::Result::Err(kb_core::Error::db( + "postgres store requires database.backend to be postgres", + )); + } + return crate::pg_store::PostgresStoreOptions::from_postgres_config(&config.postgres); + } + + /// Builds options from one resolved profile. + pub fn from_profile_config(config: &kb_config::ProfileConfig) -> kb_core::Result { + return crate::pg_store::PostgresStoreOptions::from_database_config(&config.database); + } + + /// Builds options from the active profile of an application configuration. + pub fn from_active_app_config(config: &kb_config::AppConfig) -> kb_core::Result { + let profile_result = kb_config::active_profile(config); + return match profile_result { + std::result::Result::Ok(profile) => { + crate::pg_store::PostgresStoreOptions::from_profile_config(profile) + }, + std::result::Result::Err(error) => std::result::Result::Err(error), + }; + } + + /// Creates validated PostgreSQL store options. + pub fn new( + database_url: impl std::convert::Into, + max_connections: u32, + connect_timeout_ms: u64, + auto_initialize_schema: bool, + ) -> kb_core::Result { + let database_url_value = database_url.into(); + if database_url_value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db( + "postgres database url must not be empty", + )); + } + if max_connections == 0 { + return std::result::Result::Err(kb_core::Error::db( + "postgres max_connections must be greater than zero", + )); + } + if connect_timeout_ms == 0 { + return std::result::Result::Err(kb_core::Error::db( + "postgres connect_timeout_ms must be greater than zero", + )); + } + return std::result::Result::Ok(Self { + database_url: database_url_value, + max_connections, + connect_timeout_ms, + auto_initialize_schema, + }); + } + + /// Returns a DSN masked for diagnostics. + pub fn masked_dsn(&self) -> std::string::String { + return crate::mask_postgres_dsn(self.database_url.as_str()); + } +} + +/// PostgreSQL diagnostic snapshot. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct PostgresBackendDiagnostics { + /// Backend descriptor safe for UI display. + pub descriptor: kb_store_core::StoreBackendDescriptor, + /// Backend health snapshot. + pub health: kb_store_core::StoreHealthSnapshot, + /// Migration status snapshot. + pub migrations: kb_store_core::StoreMigrationSnapshot, + /// Full PostgreSQL server version string when available. + pub server_version: std::option::Option, +} + +/// Read-only statistics for one PostgreSQL table. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct PostgresTableStatistics { + /// Number of rows currently stored in the table. + pub row_count: i64, + /// Lowest observed Solana slot when the table contains a slot column and rows. + pub min_slot: std::option::Option, + /// Highest observed Solana slot when the table contains a slot column and rows. + pub max_slot: std::option::Option, + /// Latest insertion timestamp rendered by PostgreSQL for UI diagnostics. + pub latest_created_at: std::option::Option, +} + +/// Read-only diagnostics for one expected PostgreSQL table. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct PostgresTableDiagnostics { + /// Expected table name. + pub table_name: std::string::String, + /// Logical Solana domain encoded in the table name. + pub domain: std::string::String, + /// Human-readable role of the table. + pub role: std::string::String, + /// Whether the table exists in the current PostgreSQL search path. + pub exists: bool, + /// Table statistics when the table exists. + pub statistics: std::option::Option, +} + +/// PostgreSQL store handle. +#[derive(Clone, Debug)] +pub struct PostgresStore { + options: crate::PostgresStoreOptions, + pool: sqlx::PgPool, +} + +impl PostgresStore { + /// Connects to PostgreSQL from typed store options. + pub async fn connect(options: crate::PostgresStoreOptions) -> kb_core::Result { + let pool_options = sqlx::postgres::PgPoolOptions::new() + .max_connections(options.max_connections) + .acquire_timeout(std::time::Duration::from_millis(options.connect_timeout_ms)); + let connect_result = pool_options.connect(options.database_url.as_str()).await; + return match connect_result { + std::result::Result::Ok(pool) => { + let store = Self { options, pool }; + if store.options.auto_initialize_schema { + let schema_result = store.initialize_store_schema().await; + if let std::result::Result::Err(error) = schema_result { + return std::result::Result::Err(error); + } + } + std::result::Result::Ok(store) + }, + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::db( + format!("postgres connection failed: {error}"), + )), + }; + } + + /// Connects to PostgreSQL from typed configuration. + pub async fn connect_from_config(config: &kb_config::PostgresConfig) -> kb_core::Result { + let options_result = crate::PostgresStoreOptions::from_postgres_config(config); + return match options_result { + std::result::Result::Ok(options) => crate::PostgresStore::connect(options).await, + std::result::Result::Err(error) => std::result::Result::Err(error), + }; + } + + /// Connects to PostgreSQL from the selected database backend configuration. + pub async fn connect_from_database_config( + config: &kb_config::DatabaseConfig, + ) -> kb_core::Result { + let options_result = crate::PostgresStoreOptions::from_database_config(config); + return match options_result { + std::result::Result::Ok(options) => crate::PostgresStore::connect(options).await, + std::result::Result::Err(error) => std::result::Result::Err(error), + }; + } + + /// Connects to PostgreSQL from one resolved profile. + pub async fn connect_from_profile_config( + config: &kb_config::ProfileConfig, + ) -> kb_core::Result { + let options_result = crate::PostgresStoreOptions::from_profile_config(config); + return match options_result { + std::result::Result::Ok(options) => crate::PostgresStore::connect(options).await, + std::result::Result::Err(error) => std::result::Result::Err(error), + }; + } + + /// Connects to PostgreSQL from the active profile of an application configuration. + pub async fn connect_from_active_app_config( + config: &kb_config::AppConfig, + ) -> kb_core::Result { + let options_result = crate::PostgresStoreOptions::from_active_app_config(config); + return match options_result { + std::result::Result::Ok(options) => crate::PostgresStore::connect(options).await, + std::result::Result::Err(error) => std::result::Result::Err(error), + }; + } + + /// Creates a store handle from an existing PostgreSQL pool. + pub fn from_pool(options: crate::PostgresStoreOptions, pool: sqlx::PgPool) -> Self { + return Self { options, pool }; + } + + /// Returns the underlying PostgreSQL pool. + pub fn pool(&self) -> &sqlx::PgPool { + return &self.pool; + } + + /// Returns the connection options used to create this store. + pub fn options(&self) -> &crate::PostgresStoreOptions { + return &self.options; + } + + /// Applies each idempotent store schema once per invocation in dependency order. + pub async fn initialize_store_schema(&self) -> kb_core::Result<()> { + let raw_result = crate::queries::apply_raw_store_schema(&self.pool).await; + if let std::result::Result::Err(error) = raw_result { + return std::result::Result::Err(error); + } + let core_result = crate::queries::apply_core_store_schema(&self.pool).await; + if let std::result::Result::Err(error) = core_result { + return std::result::Result::Err(error); + } + return crate::queries::apply_decode_store_schema(&self.pool).await; + } + + /// Applies the idempotent minimal raw Solana store schema. + pub async fn initialize_raw_store_schema(&self) -> kb_core::Result<()> { + return crate::queries::apply_raw_store_schema(&self.pool).await; + } + + /// Applies the idempotent minimal core Solana store schema. + pub async fn initialize_core_store_schema(&self) -> kb_core::Result<()> { + let raw_result = self.initialize_raw_store_schema().await; + if let std::result::Result::Err(error) = raw_result { + return std::result::Result::Err(error); + } + return crate::queries::apply_core_store_schema(&self.pool).await; + } + + /// Applies the idempotent common decode and materialization store schema. + pub async fn initialize_decode_store_schema(&self) -> kb_core::Result<()> { + let core_result = self.initialize_core_store_schema().await; + if let std::result::Result::Err(error) = core_result { + return std::result::Result::Err(error); + } + return crate::queries::apply_decode_store_schema(&self.pool).await; + } + + /// Reads a UI-safe backend descriptor. + pub async fn backend_descriptor( + &self, + ) -> kb_core::Result { + let schema_result = crate::queries::load_current_schema(&self.pool).await; + return match schema_result { + std::result::Result::Ok(schema) => kb_store_core::StoreBackendDescriptor::new( + kb_store_core::StoreBackendKind::Postgres, + "postgres", + std::option::Option::Some(self.options.masked_dsn()), + std::option::Option::Some(schema), + ), + std::result::Result::Err(error) => std::result::Result::Err(error), + }; + } + + /// Reads a PostgreSQL health snapshot. + pub async fn health_snapshot(&self) -> kb_core::Result { + let health_result = crate::queries::run_health_check(&self.pool).await; + return match health_result { + std::result::Result::Ok(()) => kb_store_core::StoreHealthSnapshot::new( + "postgres", + kb_store_core::StoreHealthStatus::Healthy, + std::option::Option::Some(std::string::String::from("SELECT 1 succeeded")), + ), + std::result::Result::Err(error) => kb_store_core::StoreHealthSnapshot::new( + "postgres", + kb_store_core::StoreHealthStatus::Unhealthy, + std::option::Option::Some(error.to_string()), + ), + }; + } + + /// Reads a non-destructive migration snapshot. + pub async fn migration_snapshot( + &self, + ) -> kb_core::Result { + let migration_table_result = crate::queries::load_migration_table_name(&self.pool).await; + return match migration_table_result { + std::result::Result::Ok(std::option::Option::None) => { + std::result::Result::Ok(kb_store_core::StoreMigrationSnapshot::new( + kb_store_core::StoreMigrationStatus::NotInitialized, + std::option::Option::None, + std::vec::Vec::new(), + std::option::Option::Some(std::string::String::from( + "no sqlx migration table detected; 0.3.1 canonical acquisition/core schemas use idempotent crate-managed DDL", + )), + )) + }, + std::result::Result::Ok(std::option::Option::Some(_table_name)) => { + self.migration_snapshot_from_existing_table().await + }, + std::result::Result::Err(error) => std::result::Result::Err(error), + }; + } + + /// Reads a complete PostgreSQL diagnostic snapshot. + pub async fn backend_diagnostics(&self) -> kb_core::Result { + let descriptor_result = self.backend_descriptor().await; + let descriptor = match descriptor_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let health_result = self.health_snapshot().await; + let health = match health_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let migrations_result = self.migration_snapshot().await; + let migrations = match migrations_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let server_version = match crate::queries::load_server_version(&self.pool).await { + std::result::Result::Ok(value) => std::option::Option::Some(value), + std::result::Result::Err(_error) => std::option::Option::None, + }; + return std::result::Result::Ok(crate::PostgresBackendDiagnostics { + descriptor, + health, + migrations, + server_version, + }); + } + + /// Lists bounded raw transaction candidates enriched with core and ledger diagnostics. + pub async fn replay_transaction_candidates( + &self, + filter: &crate::PostgresReplayTransactionFilter, + ) -> kb_core::Result> { + return crate::queries::list_replay_transaction_candidates(&self.pool, filter).await; + } + + /// Lists bounded program summaries across outer, inner and reliably linked logs. + pub async fn replay_program_summaries( + &self, + filter: &crate::PostgresReplayProgramFilter, + ) -> kb_core::Result> { + return crate::queries::list_replay_program_summaries(&self.pool, filter).await; + } + + /// Lists bounded mint, owner or account-key summaries from core tables. + pub async fn replay_entity_summaries( + &self, + filter: &crate::PostgresReplayEntityFilter, + ) -> kb_core::Result> { + return crate::queries::list_replay_entity_summaries(&self.pool, filter).await; + } + + /// Reads diagnostics for raw Solana store tables without changing the schema. + pub async fn raw_table_diagnostics( + &self, + ) -> kb_core::Result> { + let specs = crate::migrations::raw_store_table_diagnostic_specs(); + return self.table_diagnostics(&specs).await; + } + + /// Reads diagnostics for core Solana store tables without changing the schema. + pub async fn core_table_diagnostics( + &self, + ) -> kb_core::Result> { + let specs = crate::migrations::core_store_table_diagnostic_specs(); + return self.table_diagnostics(&specs).await; + } + + /// Reads diagnostics for decode and materialization store tables without changing the schema. + pub async fn decode_table_diagnostics( + &self, + ) -> kb_core::Result> { + let specs = crate::migrations::decode_store_table_diagnostic_specs(); + return self.table_diagnostics(&specs).await; + } + + /// Reads diagnostics for every known raw/core/decode Solana store table. + pub async fn known_table_diagnostics( + &self, + ) -> kb_core::Result> { + let mut diagnostics = std::vec::Vec::new(); + let raw_result = self.raw_table_diagnostics().await; + let raw_tables = match raw_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + for table in raw_tables { + diagnostics.push(table); + } + let core_result = self.core_table_diagnostics().await; + let core_tables = match core_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + for table in core_tables { + diagnostics.push(table); + } + let decode_result = self.decode_table_diagnostics().await; + let decode_tables = match decode_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + for table in decode_tables { + diagnostics.push(table); + } + return std::result::Result::Ok(diagnostics); + } + + async fn table_diagnostics( + &self, + specs: &[crate::migrations::PostgresTableDiagnosticSpec], + ) -> kb_core::Result> { + let mut diagnostics = std::vec::Vec::new(); + for spec in specs { + let exists_result = crate::queries::table_exists(&self.pool, spec.table_name).await; + let exists = match exists_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let statistics = if exists { + let statistics_result = + crate::queries::load_table_statistics(&self.pool, spec.table_name).await; + match statistics_result { + std::result::Result::Ok(value) => std::option::Option::Some(value), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } else { + std::option::Option::None + }; + diagnostics.push(crate::PostgresTableDiagnostics { + table_name: spec.table_name.to_string(), + domain: spec.domain.to_string(), + role: spec.role.to_string(), + exists, + statistics, + }); + } + return std::result::Result::Ok(diagnostics); + } + + async fn migration_snapshot_from_existing_table( + &self, + ) -> kb_core::Result { + let version_result = crate::queries::load_latest_migration_version(&self.pool).await; + return match version_result { + std::result::Result::Ok(current_version) => { + std::result::Result::Ok(kb_store_core::StoreMigrationSnapshot::new( + kb_store_core::StoreMigrationStatus::Current, + current_version, + std::vec::Vec::new(), + std::option::Option::Some(std::string::String::from( + "sqlx migration table detected; canonical acquisition/core schema remains idempotent and crate-managed in 0.3.1", + )), + )) + }, + std::result::Result::Err(error) => std::result::Result::Err(error), + }; + } +} + +/// Returns a DSN masked for logs and UI diagnostics. +pub fn mask_postgres_dsn(dsn: &str) -> std::string::String { + let trimmed_dsn = dsn.trim(); + if trimmed_dsn.is_empty() { + return std::string::String::from(""); + } + let queryless = crate::pg_store::strip_query(trimmed_dsn); + return match queryless.split_once("://") { + std::option::Option::Some((scheme, remainder)) => { + crate::pg_store::mask_scheme_remainder(scheme, remainder, trimmed_dsn.contains('?')) + }, + std::option::Option::None => crate::pg_store::mask_plain_dsn(queryless.as_str()), + }; +} + +fn strip_query(dsn: &str) -> std::string::String { + return match dsn.split_once('?') { + std::option::Option::Some((prefix, _query)) => std::string::String::from(prefix), + std::option::Option::None => std::string::String::from(dsn), + }; +} + +fn mask_scheme_remainder(scheme: &str, remainder: &str, had_query: bool) -> std::string::String { + let suffix = crate::pg_store::query_suffix(had_query); + return match remainder.rsplit_once('@') { + std::option::Option::Some((_userinfo, host_path)) => { + format!("{scheme}://***:***@{host_path}{suffix}") + }, + std::option::Option::None => format!("{scheme}://{remainder}{suffix}"), + }; +} + +fn mask_plain_dsn(dsn: &str) -> std::string::String { + if dsn.contains("password=") { + return std::string::String::from(""); + } + return std::string::String::from(dsn); +} + +fn query_suffix(had_query: bool) -> std::string::String { + if had_query { + return std::string::String::from("?"); + } + return std::string::String::from(""); +} + +#[cfg(test)] +mod tests { + #[test] + fn options_reject_empty_database_url() { + let result = crate::PostgresStoreOptions::new(" ", 1, 1000, false); + assert!(result.is_err()); + } + + #[test] + fn options_reject_zero_max_connections() { + let result = crate::PostgresStoreOptions::new("postgres://localhost/db", 0, 1000, false); + assert!(result.is_err()); + } + + #[test] + fn options_reject_zero_connect_timeout() { + let result = crate::PostgresStoreOptions::new("postgres://localhost/db", 1, 0, false); + assert!(result.is_err()); + } + + #[test] + fn options_from_database_config_rejects_sqlite_backend() { + let config = sample_database_config("sqlite"); + let result = crate::PostgresStoreOptions::from_database_config(&config); + assert!(result.is_err()); + } + + #[test] + fn options_from_database_config_accepts_postgres_backend() { + let config = sample_database_config("postgres"); + let result = crate::PostgresStoreOptions::from_database_config(&config); + let options = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected error: {error}"), + }; + assert_eq!(options.database_url, "postgres://localhost/solana"); + } + + #[test] + fn options_from_active_app_config_uses_active_profile() { + let app_config = kb_config::AppConfig { + active_profile: std::string::String::from("selected"), + profiles: std::vec![ + sample_profile_config("ignored", "sqlite"), + sample_profile_config("selected", "postgres"), + ], + }; + let result = crate::PostgresStoreOptions::from_active_app_config(&app_config); + let options = match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected error: {error}"), + }; + assert_eq!(options.database_url, "postgres://localhost/solana"); + } + + #[tokio::test] + async fn optional_postgres_healthcheck_from_env() { + let database_url = match std::env::var("KB_POSTGRES_TEST_URL") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => return, + }; + let _postgres_guard = crate::test_serial::postgres_test_guard().await; + let options_result = crate::PostgresStoreOptions::new(database_url, 1, 5000, false); + let options = match options_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected options error: {error}"), + }; + let store_result = crate::PostgresStore::connect(options).await; + let store = match store_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected connect error: {error}"), + }; + let health_result = store.health_snapshot().await; + let health = match health_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected health error: {error}"), + }; + assert_eq!(health.status, kb_store_core::StoreHealthStatus::Healthy); + return; + } + + #[test] + fn mask_postgres_dsn_masks_userinfo() { + let masked = crate::mask_postgres_dsn("postgres://user:secret@localhost:5432/db"); + assert_eq!(masked, "postgres://***:***@localhost:5432/db"); + } + + #[test] + fn mask_postgres_dsn_masks_query_string() { + let masked = + crate::mask_postgres_dsn("postgres://localhost/db?sslmode=require&password=secret"); + assert_eq!(masked, "postgres://localhost/db?"); + } + + #[test] + fn mask_postgres_dsn_masks_plain_password_dsn() { + let masked = crate::mask_postgres_dsn("host=localhost password=secret dbname=khadhroony"); + assert_eq!(masked, ""); + } + + fn sample_database_config(backend: &str) -> kb_config::DatabaseConfig { + return kb_config::DatabaseConfig { + enabled: true, + backend: std::string::String::from(backend), + postgres: kb_config::PostgresConfig { + url: std::string::String::from("postgres://localhost/solana"), + max_connections: 2, + connect_timeout_ms: 1000, + auto_initialize_schema: false, + }, + sqlite: kb_config::SqliteConfig { + path: std::string::String::from("data/test.db"), + create_if_missing: true, + busy_timeout_ms: 1000, + max_connections: 1, + auto_initialize_schema: false, + use_wal: true, + }, + }; + } + + fn sample_profile_config(name: &str, backend: &str) -> kb_config::ProfileConfig { + return kb_config::ProfileConfig { + name: std::string::String::from(name), + app: kb_config::AppSectionConfig { + name: std::string::String::from("test"), + environment: std::string::String::from("test"), + auto_reconnect_default: false, + }, + logging: kb_config::LoggingConfig { + default_level: std::string::String::from("info"), + targets: std::vec::Vec::new(), + target_filters: std::vec::Vec::new(), + }, + database: sample_database_config(backend), + data: kb_config::DataConfig { + wallets_directory: std::string::String::from("wallets"), + logs_directory: std::string::String::from("logs"), + }, + solana: kb_config::SolanaConfig { + http_endpoints: std::vec::Vec::new(), + ws_endpoints: std::vec::Vec::new(), + listeners: kb_config::ListenerConfig { + enabled: false, + default_commitment: std::string::String::from("confirmed"), + log_listeners: std::vec::Vec::new(), + program_listeners: std::vec::Vec::new(), + account_listeners: std::vec::Vec::new(), + }, + }, + demo: kb_config::DemoConfig { + live_demo_enabled: false, + trading_demo_enabled: false, + }, + wallet: kb_config::WalletConfig { + wallet_dir: std::string::String::from("wallets"), + cluster: std::string::String::from("mainnet-beta"), + temporary_wallet_enabled: false, + temporary_wallet_alias: std::string::String::from("test-disabled"), + temporary_wallet_persist: false, + localnet_send_enabled: false, + devnet_send_enabled: false, + testnet_send_enabled: false, + mainnet_send_enabled: false, + }, + execution: kb_config::ExecutionConfig { + dry_run_default: true, + require_simulation: true, + require_operator_confirmation: true, + localnet_max_spend_lamports: 0, + devnet_max_spend_lamports: 0, + testnet_max_spend_lamports: 0, + mainnet_max_spend_lamports: 0, + max_fee_lamports: 10_000, + max_compute_unit_price_micro_lamports: 0, + recent_blockhash_max_age_slots: 150, + send_max_retries: 3, + confirmation_poll_interval_ms: 500, + confirmation_max_attempts: 120, + devnet_airdrop_max_lamports: 0, + }, + }; + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/queries.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/queries.rs new file mode 100644 index 0000000..48b244f --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/queries.rs @@ -0,0 +1,52 @@ +// file: kb_store_pg/src/queries.rs +// version: 9 + +//! PostgreSQL query modules. + +mod core_extraction_queries; +mod core_queries; +mod decode_pipeline_queries; +mod health_queries; +mod raw_queries; +mod replay_candidate_queries; +mod table_diagnostics_queries; + +pub(crate) use crate::queries::core_extraction_queries::is_core_extraction_current; +pub(crate) use crate::queries::core_extraction_queries::list_raw_transactions_for_core_extraction; +pub(crate) use crate::queries::core_extraction_queries::mark_core_extraction_failed; +pub(crate) use crate::queries::core_extraction_queries::persist_core_extraction; +pub(crate) use crate::queries::core_queries::apply_core_store_schema; +pub(crate) use crate::queries::core_queries::insert_core_account_keys; +pub(crate) use crate::queries::core_queries::insert_core_balance_changes; +pub(crate) use crate::queries::core_queries::insert_core_inner_instructions; +pub(crate) use crate::queries::core_queries::insert_core_instructions; +pub(crate) use crate::queries::core_queries::insert_core_logs; +pub(crate) use crate::queries::core_queries::insert_core_transaction; +pub(crate) use crate::queries::core_queries::list_core_instruction_replay_inputs; +pub(crate) use crate::queries::core_queries::list_core_instructions_for_replay; +pub(crate) use crate::queries::core_queries::update_core_instruction_lifecycle; +pub(crate) use crate::queries::decode_pipeline_queries::apply_decode_store_schema; +pub(crate) use crate::queries::decode_pipeline_queries::is_decode_current; +pub(crate) use crate::queries::decode_pipeline_queries::list_decode_coverage_summary; +pub(crate) use crate::queries::decode_pipeline_queries::list_decode_inputs; +pub(crate) use crate::queries::decode_pipeline_queries::list_materialized_events; +pub(crate) use crate::queries::decode_pipeline_queries::mark_decode_failed; +pub(crate) use crate::queries::decode_pipeline_queries::persist_decode_coverage_declarations; +pub(crate) use crate::queries::decode_pipeline_queries::persist_decode_result; +pub(crate) use crate::queries::decode_pipeline_queries::persist_materialization_result; +pub(crate) use crate::queries::health_queries::load_current_schema; +pub(crate) use crate::queries::health_queries::load_latest_migration_version; +pub(crate) use crate::queries::health_queries::load_migration_table_name; +pub(crate) use crate::queries::health_queries::load_server_version; +pub(crate) use crate::queries::health_queries::run_health_check; +pub(crate) use crate::queries::raw_queries::apply_raw_store_schema; +pub(crate) use crate::queries::raw_queries::has_raw_transaction_signature; +pub(crate) use crate::queries::raw_queries::has_transaction_observation_key; +pub(crate) use crate::queries::raw_queries::insert_raw_transaction; +pub(crate) use crate::queries::raw_queries::insert_transaction_observation; +pub(crate) use crate::queries::raw_queries::update_raw_payload_lifecycle; +pub(crate) use crate::queries::replay_candidate_queries::list_replay_entity_summaries; +pub(crate) use crate::queries::replay_candidate_queries::list_replay_program_summaries; +pub(crate) use crate::queries::replay_candidate_queries::list_replay_transaction_candidates; +pub(crate) use crate::queries::table_diagnostics_queries::load_table_statistics; +pub(crate) use crate::queries::table_diagnostics_queries::table_exists; diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/README.md b/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/README.md new file mode 100644 index 0000000..ad7df96 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/README.md @@ -0,0 +1,18 @@ + + + +# Queries PostgreSQL + +Ce dossier contient uniquement le SQL, les binds et l'exécution SQL PostgreSQL. + +Interdiction : ne pas placer de struct métier, entity ou DTO dans ce dossier. + +## Modules actuels + +| Module | Rôle | +|--------------------------------|---------------------------------------------------------------------------------------| +| `health_queries.rs` | Healthcheck, schéma courant, version serveur et diagnostic migrations. | +| `raw_queries.rs` | Schéma raw minimal, insertions raw RPC/WS, déduplication et lifecycle raw. | +| `core_queries.rs` | Schéma core minimal, insertions core, lifecycle instruction et reconstruction replay. | +| `table_diagnostics_queries.rs` | Diagnostics read-only des tables raw/core connues. | + diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/core_extraction_queries.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/core_extraction_queries.rs new file mode 100644 index 0000000..fe6a7cc --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/core_extraction_queries.rs @@ -0,0 +1,1017 @@ +// file: kb_store_pg/src/queries/core_extraction_queries.rs +// version: 5 + +//! PostgreSQL queries for atomic canonical transaction to core extraction. + +use sqlx::Row; // rust-rules: trait-import + +pub(crate) async fn list_raw_transactions_for_core_extraction( + pool: &sqlx::PgPool, + filter: &kb_store_core::CoreExtractionSelectionFilter, +) -> kb_core::Result> { + let min_slot_result = + crate::queries::core_extraction_queries::optional_sql_slot(filter.min_slot); + let min_slot = match min_slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let max_slot_result = + crate::queries::core_extraction_queries::optional_sql_slot(filter.max_slot); + let max_slot = match max_slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let processing_state = filter + .processing_state + .map(crate::queries::core_extraction_queries::raw_processing_state_to_sql); + let limit = i64::from(filter.limit); + let signatures_are_empty = filter.signatures.is_empty(); + let query_result = sqlx::query( + "SELECT raw.id, raw.signature, raw.slot, raw.canonical_json, raw.canonical_json_hash, raw.canonical_format_version, raw.retention_state, raw.processing_state, raw.created_at, raw.updated_at FROM kb_sol_raw_transactions raw WHERE ($1 OR raw.signature = ANY($2)) AND ($3::BIGINT IS NULL OR raw.slot >= $3) AND ($4::BIGINT IS NULL OR raw.slot <= $4) AND ($5::TEXT IS NULL OR raw.processing_state = $5) AND ($6::TEXT IS NULL OR EXISTS (SELECT 1 FROM kb_sol_core_instructions instruction WHERE instruction.signature = raw.signature AND instruction.program_id = $6)) ORDER BY raw.slot ASC, raw.signature ASC LIMIT $7", + ) + .bind(signatures_are_empty) + .bind(&filter.signatures) + .bind(min_slot) + .bind(max_slot) + .bind(processing_state) + .bind(filter.program_id.as_deref()) + .bind(limit) + .fetch_all(pool) + .await; + let rows = match query_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core extraction raw selection failed: {error}" + ))); + }, + }; + let mut output = std::vec::Vec::with_capacity(rows.len()); + for row in rows { + let mapped_result = crate::queries::core_extraction_queries::map_raw_transaction_row(&row); + let mapped = match mapped_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + output.push(mapped); + } + return std::result::Result::Ok(output); +} + +pub(crate) async fn is_core_extraction_current( + pool: &sqlx::PgPool, + identity: &kb_store_core::ProcessingLedgerIdentity, +) -> kb_core::Result { + let query_result = sqlx::query_scalar::( + "SELECT EXISTS(SELECT 1 FROM kb_sol_ops_processing_ledger WHERE stage = $1 AND processor_name = $2 AND processor_version = $3 AND input_key = $4 AND input_hash = $5 AND status = 'succeeded')", + ) + .bind(identity.stage.as_str()) + .bind(identity.processor_name.as_str()) + .bind(identity.processor_version.as_str()) + .bind(identity.input_key.as_str()) + .bind(identity.input_hash.as_str()) + .fetch_one(pool) + .await; + return match query_result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::db(format!( + "postgres core extraction ledger lookup failed: {error}" + ))), + }; +} + +pub(crate) async fn persist_core_extraction( + pool: &sqlx::PgPool, + bundle: &kb_store_core::CoreExtractionBundle, + _force_replay: bool, +) -> kb_core::Result { + let validation_result = bundle.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let transaction_result = pool.begin().await; + let mut transaction = match transaction_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core extraction transaction start failed: {error}" + ))); + }, + }; + let existing_result = sqlx::query_scalar::( + "SELECT id FROM kb_sol_core_transactions WHERE signature = $1 LIMIT 1", + ) + .bind(bundle.transaction.signature.as_str()) + .fetch_optional(&mut *transaction) + .await; + let existing = match existing_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core extraction existing transaction lookup failed: {error}" + ))); + }, + }; + if existing.is_some() { + let delete_result = + sqlx::query("DELETE FROM kb_sol_core_transactions WHERE signature = $1") + .bind(bundle.transaction.signature.as_str()) + .execute(&mut *transaction) + .await; + if let std::result::Result::Err(error) = delete_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core extraction replacement delete failed: {error}" + ))); + } + } + let transaction_id_result = + crate::queries::core_extraction_queries::insert_core_transaction_in_transaction( + &mut transaction, + &bundle.transaction, + ) + .await; + let transaction_id = match transaction_id_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let account_result = + crate::queries::core_extraction_queries::insert_account_keys_in_transaction( + &mut transaction, + transaction_id, + &bundle.account_keys, + ) + .await; + if let std::result::Result::Err(error) = account_result { + return std::result::Result::Err(error); + } + let instruction_result = + crate::queries::core_extraction_queries::insert_instructions_in_transaction( + &mut transaction, + transaction_id, + &bundle.instructions, + ) + .await; + if let std::result::Result::Err(error) = instruction_result { + return std::result::Result::Err(error); + } + let inner_result = + crate::queries::core_extraction_queries::insert_inner_instructions_in_transaction( + &mut transaction, + transaction_id, + &bundle.inner_instructions, + ) + .await; + if let std::result::Result::Err(error) = inner_result { + return std::result::Result::Err(error); + } + let log_result = crate::queries::core_extraction_queries::insert_logs_in_transaction( + &mut transaction, + transaction_id, + &bundle.logs, + ) + .await; + if let std::result::Result::Err(error) = log_result { + return std::result::Result::Err(error); + } + let balance_result = + crate::queries::core_extraction_queries::insert_balance_changes_in_transaction( + &mut transaction, + transaction_id, + &bundle.balance_changes, + ) + .await; + if let std::result::Result::Err(error) = balance_result { + return std::result::Result::Err(error); + } + let raw_update_result = sqlx::query( + "UPDATE kb_sol_raw_transactions SET processing_state = 'core_extracted', lifecycle_reason = NULL, updated_at = NOW() WHERE id = $1", + ) + .bind(bundle.raw_transaction_id) + .execute(&mut *transaction) + .await; + if let std::result::Result::Err(error) = raw_update_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core extraction raw lifecycle update failed: {error}" + ))); + } + let ledger_result = + crate::queries::core_extraction_queries::upsert_ledger_terminal_in_transaction( + &mut transaction, + &bundle.ledger_identity, + "succeeded", + std::option::Option::None, + std::option::Option::None, + ) + .await; + if let std::result::Result::Err(error) = ledger_result { + return std::result::Result::Err(error); + } + let commit_result = transaction.commit().await; + if let std::result::Result::Err(error) = commit_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core extraction commit failed: {error}" + ))); + } + let inserted_count_result = + crate::queries::core_extraction_queries::bundle_inserted_count(bundle); + let inserted_count = match inserted_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(kb_store_core::InsertOutcome::new( + inserted_count, + if existing.is_some() { 1 } else { 0 }, + 0, + )); +} + +pub(crate) async fn mark_core_extraction_failed( + pool: &sqlx::PgPool, + failure: &kb_store_core::CoreExtractionFailure, +) -> kb_core::Result { + let transaction_result = pool.begin().await; + let mut transaction = match transaction_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core extraction failure transaction start failed: {error}" + ))); + }, + }; + let raw_update_result = sqlx::query( + "UPDATE kb_sol_raw_transactions SET processing_state = 'failed', lifecycle_reason = $2, updated_at = NOW() WHERE id = $1", + ) + .bind(failure.raw_transaction_id) + .bind(failure.error_message.as_str()) + .execute(&mut *transaction) + .await; + if let std::result::Result::Err(error) = raw_update_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core extraction failed lifecycle update failed: {error}" + ))); + } + let ledger_result = + crate::queries::core_extraction_queries::upsert_ledger_terminal_in_transaction( + &mut transaction, + &failure.ledger_identity, + "failed", + std::option::Option::Some(failure.error_code.as_str()), + std::option::Option::Some(failure.error_message.as_str()), + ) + .await; + if let std::result::Result::Err(error) = ledger_result { + return std::result::Result::Err(error); + } + let commit_result = transaction.commit().await; + if let std::result::Result::Err(error) = commit_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core extraction failure commit failed: {error}" + ))); + } + return std::result::Result::Ok(kb_store_core::InsertOutcome::new(0, 1, 0)); +} + +async fn insert_core_transaction_in_transaction( + transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>, + input: &kb_store_core::CoreTransactionInsert, +) -> kb_core::Result { + let slot_result = crate::queries::core_extraction_queries::sql_slot(input.slot); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let query_result = sqlx::query_scalar::( + "INSERT INTO kb_sol_core_transactions (raw_transaction_id, signature, slot, failed, err_json) VALUES ($1, $2, $3, $4, $5) RETURNING id", + ) + .bind(input.raw_transaction_id) + .bind(input.signature.as_str()) + .bind(slot) + .bind(input.failed) + .bind(input.err_json.clone()) + .fetch_one(&mut **transaction) + .await; + return match query_result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::db(format!( + "postgres core extraction transaction insert failed: {error}" + ))), + }; +} + +async fn insert_account_keys_in_transaction( + transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>, + transaction_id: i64, + inputs: &[kb_store_core::CoreAccountKeyInsert], +) -> kb_core::Result<()> { + for input in inputs { + let slot_result = crate::queries::core_extraction_queries::sql_slot(input.slot); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let account_index_result = i32::try_from(input.account_index); + let account_index = match account_index_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "core account index does not fit into SQL INTEGER: {error}" + ))); + }, + }; + let query_result = sqlx::query( + "INSERT INTO kb_sol_core_account_keys (transaction_id, signature, slot, account_index, account_key, source, writable, signer, executable) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + ) + .bind(transaction_id) + .bind(input.signature.as_str()) + .bind(slot) + .bind(account_index) + .bind(input.account_key.as_str()) + .bind(crate::queries::core_extraction_queries::account_key_source_to_sql(input.source)) + .bind(input.writable) + .bind(input.signer) + .bind(input.executable) + .execute(&mut **transaction) + .await; + if let std::result::Result::Err(error) = query_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core extraction account key insert failed: {error}" + ))); + } + } + return std::result::Result::Ok(()); +} + +async fn insert_instructions_in_transaction( + transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>, + transaction_id: i64, + inputs: &[kb_store_core::CoreInstructionInsert], +) -> kb_core::Result<()> { + for input in inputs { + let slot_result = crate::queries::core_extraction_queries::sql_slot(input.slot); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let query_result = sqlx::query( + "INSERT INTO kb_sol_core_instructions (transaction_id, signature, slot, instruction_path, program_id, accounts_json, payload_json, payload_json_hash, processing_state) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, 'pending')", + ) + .bind(transaction_id) + .bind(input.signature.as_str()) + .bind(slot) + .bind(input.instruction_path.as_str()) + .bind(input.program_id.as_str()) + .bind(&input.accounts_json) + .bind(&input.payload_json) + .bind(input.payload_json_hash.as_deref()) + .execute(&mut **transaction) + .await; + if let std::result::Result::Err(error) = query_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core extraction instruction insert failed: {error}" + ))); + } + } + return std::result::Result::Ok(()); +} + +async fn insert_inner_instructions_in_transaction( + transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>, + transaction_id: i64, + inputs: &[kb_store_core::CoreInnerInstructionInsert], +) -> kb_core::Result<()> { + for input in inputs { + let slot_result = crate::queries::core_extraction_queries::sql_slot(input.slot); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let query_result = sqlx::query( + "INSERT INTO kb_sol_core_inner_instructions (transaction_id, signature, slot, parent_instruction_path, instruction_path, program_id, accounts_json, payload_json, payload_json_hash) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + ) + .bind(transaction_id) + .bind(input.signature.as_str()) + .bind(slot) + .bind(input.parent_instruction_path.as_str()) + .bind(input.instruction_path.as_str()) + .bind(input.program_id.as_str()) + .bind(&input.accounts_json) + .bind(&input.payload_json) + .bind(input.payload_json_hash.as_deref()) + .execute(&mut **transaction) + .await; + if let std::result::Result::Err(error) = query_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core extraction inner instruction insert failed: {error}" + ))); + } + } + return std::result::Result::Ok(()); +} + +async fn insert_logs_in_transaction( + transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>, + transaction_id: i64, + inputs: &[kb_store_core::CoreLogInsert], +) -> kb_core::Result<()> { + for input in inputs { + let slot_result = crate::queries::core_extraction_queries::sql_slot(input.slot); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let log_index_result = i32::try_from(input.log_index); + let log_index = match log_index_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "core log index does not fit into SQL INTEGER: {error}" + ))); + }, + }; + let query_result = sqlx::query( + "INSERT INTO kb_sol_core_logs (transaction_id, signature, slot, log_index, instruction_path, program_id, log_text, log_text_hash) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", + ) + .bind(transaction_id) + .bind(input.signature.as_str()) + .bind(slot) + .bind(log_index) + .bind(input.instruction_path.as_deref()) + .bind(input.program_id.as_deref()) + .bind(input.log_text.as_str()) + .bind(input.log_text_hash.as_deref()) + .execute(&mut **transaction) + .await; + if let std::result::Result::Err(error) = query_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core extraction log insert failed: {error}" + ))); + } + } + return std::result::Result::Ok(()); +} + +async fn insert_balance_changes_in_transaction( + transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>, + transaction_id: i64, + inputs: &[kb_store_core::CoreBalanceChangeInsert], +) -> kb_core::Result<()> { + for input in inputs { + let slot_result = crate::queries::core_extraction_queries::sql_slot(input.slot); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let balance_index_result = i32::try_from(input.balance_change_index); + let balance_index = match balance_index_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "core balance change index does not fit into SQL INTEGER: {error}" + ))); + }, + }; + let account_index_result = input.account_index.map(i32::try_from).transpose(); + let account_index = match account_index_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "core balance account index does not fit into SQL INTEGER: {error}" + ))); + }, + }; + let query_result = sqlx::query( + "INSERT INTO kb_sol_core_balance_changes (transaction_id, signature, slot, balance_change_index, balance_kind, account_index, account_key, mint, owner, pre_balance_json, post_balance_json, delta_json) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", + ) + .bind(transaction_id) + .bind(input.signature.as_str()) + .bind(slot) + .bind(balance_index) + .bind(crate::queries::core_extraction_queries::balance_change_kind_to_sql(input.balance_kind)) + .bind(account_index) + .bind(input.account_key.as_deref()) + .bind(input.mint.as_deref()) + .bind(input.owner.as_deref()) + .bind(input.pre_balance_json.clone()) + .bind(input.post_balance_json.clone()) + .bind(input.delta_json.clone()) + .execute(&mut **transaction) + .await; + if let std::result::Result::Err(error) = query_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core extraction balance insert failed: {error}" + ))); + } + } + return std::result::Result::Ok(()); +} + +async fn upsert_ledger_terminal_in_transaction( + transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>, + identity: &kb_store_core::ProcessingLedgerIdentity, + status: &str, + error_code: std::option::Option<&str>, + error_message: std::option::Option<&str>, +) -> kb_core::Result<()> { + let query_result = sqlx::query( + "INSERT INTO kb_sol_ops_processing_ledger (stage, processor_name, processor_version, input_key, input_hash, status, attempt_count, started_at, finished_at, error_code, error_message) VALUES ($1, $2, $3, $4, $5, $6, 1, NOW(), NOW(), $7, $8) ON CONFLICT (stage, processor_name, processor_version, input_key) DO UPDATE SET input_hash = EXCLUDED.input_hash, status = EXCLUDED.status, attempt_count = kb_sol_ops_processing_ledger.attempt_count + 1, started_at = NOW(), finished_at = NOW(), error_code = EXCLUDED.error_code, error_message = EXCLUDED.error_message, updated_at = NOW()", + ) + .bind(identity.stage.as_str()) + .bind(identity.processor_name.as_str()) + .bind(identity.processor_version.as_str()) + .bind(identity.input_key.as_str()) + .bind(identity.input_hash.as_str()) + .bind(status) + .bind(error_code) + .bind(error_message) + .execute(&mut **transaction) + .await; + if let std::result::Result::Err(error) = query_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core extraction ledger upsert failed: {error}" + ))); + } + return std::result::Result::Ok(()); +} + +fn map_raw_transaction_row( + row: &sqlx::postgres::PgRow, +) -> kb_core::Result { + let retention_text_result = row.try_get::("retention_state"); + let retention_text = match retention_text_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres raw retention state mapping failed: {error}" + ))); + }, + }; + let processing_text_result = row.try_get::("processing_state"); + let processing_text = match processing_text_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres raw processing state mapping failed: {error}" + ))); + }, + }; + let retention_result = crate::queries::core_extraction_queries::raw_retention_state_from_sql( + retention_text.as_str(), + ); + let retention_state = match retention_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let processing_result = crate::queries::core_extraction_queries::raw_processing_state_from_sql( + processing_text.as_str(), + ); + let processing_state = match processing_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let id = match row.try_get::("id") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres raw id mapping failed: {error}" + ))); + }, + }; + let signature = match row.try_get::("signature") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres raw signature mapping failed: {error}" + ))); + }, + }; + let slot = match row.try_get::("slot") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres raw slot mapping failed: {error}" + ))); + }, + }; + let canonical_json = + match row.try_get::, _>("canonical_json") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres canonical JSON mapping failed: {error}" + ))); + }, + }; + let canonical_json_hash = + match row.try_get::, _>("canonical_json_hash") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres canonical hash mapping failed: {error}" + ))); + }, + }; + let canonical_format_version = match row.try_get::("canonical_format_version") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres canonical format version mapping failed: {error}" + ))); + }, + }; + let created_at = match row.try_get::, _>("created_at") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres raw created_at mapping failed: {error}" + ))); + }, + }; + let updated_at = match row.try_get::, _>("updated_at") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres raw updated_at mapping failed: {error}" + ))); + }, + }; + return std::result::Result::Ok(kb_store_core::RawTransactionRow { + id, + signature, + slot, + canonical_json, + canonical_json_hash, + canonical_format_version, + retention_state, + processing_state, + created_at, + updated_at, + }); +} + +fn bundle_inserted_count(bundle: &kb_store_core::CoreExtractionBundle) -> kb_core::Result { + let mut total = 1_u64; + for length in [ + bundle.account_keys.len(), + bundle.instructions.len(), + bundle.inner_instructions.len(), + bundle.logs.len(), + bundle.balance_changes.len(), + ] { + let value_result = u64::try_from(length); + let value = match value_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "core extraction inserted row count overflow: {error}" + ))); + }, + }; + let sum_result = total.checked_add(value); + total = match sum_result { + std::option::Option::Some(value) => value, + std::option::Option::None => { + return std::result::Result::Err(kb_core::Error::db( + "core extraction inserted row count overflow", + )); + }, + }; + } + return std::result::Result::Ok(total); +} + +fn optional_sql_slot(value: std::option::Option) -> kb_core::Result> { + return match value { + std::option::Option::Some(slot) => { + let converted_result = crate::queries::core_extraction_queries::sql_slot(slot); + match converted_result { + std::result::Result::Ok(converted) => { + std::result::Result::Ok(std::option::Option::Some(converted)) + }, + std::result::Result::Err(error) => std::result::Result::Err(error), + } + }, + std::option::Option::None => std::result::Result::Ok(std::option::Option::None), + }; +} + +fn sql_slot(value: u64) -> kb_core::Result { + let conversion_result = i64::try_from(value); + return match conversion_result { + std::result::Result::Ok(converted) => std::result::Result::Ok(converted), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::db(format!( + "Solana slot does not fit into SQL BIGINT: {error}" + ))), + }; +} + +fn raw_processing_state_to_sql(value: kb_store_core::RawPayloadProcessingState) -> &'static str { + return match value { + kb_store_core::RawPayloadProcessingState::Received => "received", + kb_store_core::RawPayloadProcessingState::CoreExtracted => "core_extracted", + kb_store_core::RawPayloadProcessingState::Decoded => "decoded", + kb_store_core::RawPayloadProcessingState::Materialized => "materialized", + kb_store_core::RawPayloadProcessingState::Failed => "failed", + }; +} + +fn raw_processing_state_from_sql( + value: &str, +) -> kb_core::Result { + return match value { + "received" => std::result::Result::Ok(kb_store_core::RawPayloadProcessingState::Received), + "core_extracted" => { + std::result::Result::Ok(kb_store_core::RawPayloadProcessingState::CoreExtracted) + }, + "decoded" => std::result::Result::Ok(kb_store_core::RawPayloadProcessingState::Decoded), + "materialized" => { + std::result::Result::Ok(kb_store_core::RawPayloadProcessingState::Materialized) + }, + "failed" => std::result::Result::Ok(kb_store_core::RawPayloadProcessingState::Failed), + _ => std::result::Result::Err(kb_core::Error::db(format!( + "unknown raw processing state: {value}" + ))), + }; +} + +fn raw_retention_state_from_sql( + value: &str, +) -> kb_core::Result { + return match value { + "full" => std::result::Result::Ok(kb_store_core::RawPayloadRetentionState::Full), + "compacted" => std::result::Result::Ok(kb_store_core::RawPayloadRetentionState::Compacted), + "archived" => std::result::Result::Ok(kb_store_core::RawPayloadRetentionState::Archived), + "purged" => std::result::Result::Ok(kb_store_core::RawPayloadRetentionState::Purged), + _ => std::result::Result::Err(kb_core::Error::db(format!( + "unknown raw retention state: {value}" + ))), + }; +} + +fn account_key_source_to_sql(value: kb_store_core::CoreAccountKeySource) -> &'static str { + return match value { + kb_store_core::CoreAccountKeySource::Static => "static", + kb_store_core::CoreAccountKeySource::LoadedWritable => "loaded_writable", + kb_store_core::CoreAccountKeySource::LoadedReadonly => "loaded_readonly", + }; +} + +fn balance_change_kind_to_sql(value: kb_store_core::CoreBalanceChangeKind) -> &'static str { + return match value { + kb_store_core::CoreBalanceChangeKind::NativeLamports => "native_lamports", + kb_store_core::CoreBalanceChangeKind::TokenAmount => "token_amount", + }; +} + +#[cfg(test)] +mod tests { + #[test] + fn processing_state_serializes_to_expected_sql() { + assert_eq!( + crate::queries::core_extraction_queries::raw_processing_state_to_sql( + kb_store_core::RawPayloadProcessingState::CoreExtracted, + ), + "core_extracted" + ); + } + + #[test] + fn slot_rejects_values_above_bigint() { + let result = crate::queries::core_extraction_queries::sql_slot(u64::MAX); + assert!(result.is_err()); + } + + #[tokio::test] + async fn optional_postgres_core_extraction_rolls_back_partial_graph_from_env() { + let database_url = match std::env::var("KB_POSTGRES_TEST_URL") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => return, + }; + let _postgres_guard = crate::test_serial::postgres_test_guard().await; + let options_result = crate::PostgresStoreOptions::new(database_url, 1, 5000, false); + let options = match options_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected options error: {error}"), + }; + let store_result = crate::PostgresStore::connect(options).await; + let store = match store_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected connect error: {error}"), + }; + let schema_result = store.initialize_core_store_schema().await; + if let std::result::Result::Err(error) = schema_result { + panic!("unexpected schema error: {error}"); + } + let signature = rollback_test_signature(); + let raw_input_result = kb_store_core::RawTransactionInsert::new( + signature.clone(), + 1, + serde_json::json!({"test": "core_extraction_rollback"}), + 1, + ); + let raw_input = match raw_input_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected raw input error: {error}"), + }; + let hashed_input_result = raw_input.with_canonical_json_hash("rollback-input-hash"); + let hashed_input = match hashed_input_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected raw hash error: {error}"), + }; + let raw_insert_result = + kb_store_core::RawTransactionStore::insert_raw_transaction(&store, &hashed_input).await; + if let std::result::Result::Err(error) = raw_insert_result { + panic!("unexpected raw insert error: {error}"); + } + let raw_id_result = sqlx::query_scalar::( + "SELECT id FROM kb_sol_raw_transactions WHERE signature = $1", + ) + .bind(signature.as_str()) + .fetch_one(store.pool()) + .await; + let raw_id = match raw_id_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected raw id query error: {error}"), + }; + let invalid_bundle = rollback_test_bundle(raw_id, signature.as_str(), true); + let failed_result = kb_store_core::CoreExtractionStore::persist_core_extraction( + &store, + &invalid_bundle, + true, + ) + .await; + assert!(failed_result.is_err()); + assert_rollback_state(&store, signature.as_str(), "received", 0, 0).await; + let valid_bundle = rollback_test_bundle(raw_id, signature.as_str(), false); + let success_result = kb_store_core::CoreExtractionStore::persist_core_extraction( + &store, + &valid_bundle, + true, + ) + .await; + if let std::result::Result::Err(error) = success_result { + panic!("unexpected successful replay error: {error}"); + } + assert_rollback_state(&store, signature.as_str(), "core_extracted", 1, 1).await; + cleanup_rollback_test(&store, signature.as_str()).await; + } + + fn rollback_test_signature() -> std::string::String { + let duration_result = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH); + let nanos = match duration_result { + std::result::Result::Ok(value) => value.as_nanos(), + std::result::Result::Err(_error) => 0, + }; + return format!("kb-core-rollback-{}-{nanos}", std::process::id()); + } + + fn rollback_test_bundle( + raw_transaction_id: i64, + signature: &str, + duplicate_account_index: bool, + ) -> kb_store_core::CoreExtractionBundle { + let transaction_result = kb_store_core::CoreTransactionInsert::new( + signature, + 1, + false, + std::option::Option::None, + ); + let transaction = match transaction_result { + std::result::Result::Ok(value) => value.with_raw_transaction_id(raw_transaction_id), + std::result::Result::Err(error) => panic!("unexpected transaction error: {error}"), + }; + let identity_result = kb_store_core::ProcessingLedgerIdentity::new( + "core_extraction", + "canonical_to_core", + "rollback-test", + signature, + "rollback-input-hash", + ); + let ledger_identity = match identity_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected ledger identity error: {error}"), + }; + let first_account_result = kb_store_core::CoreAccountKeyInsert::new( + signature, + 1, + 0, + "rollback-account-a", + kb_store_core::CoreAccountKeySource::Static, + true, + true, + std::option::Option::None, + ); + let first_account = match first_account_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected first account error: {error}"), + }; + let mut account_keys = vec![first_account]; + if duplicate_account_index { + let duplicate_result = kb_store_core::CoreAccountKeyInsert::new( + signature, + 1, + 0, + "rollback-account-b", + kb_store_core::CoreAccountKeySource::Static, + false, + false, + std::option::Option::None, + ); + let duplicate = match duplicate_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("unexpected duplicate account error: {error}") + }, + }; + account_keys.push(duplicate); + } + return kb_store_core::CoreExtractionBundle { + raw_transaction_id, + ledger_identity, + transaction, + account_keys, + instructions: std::vec::Vec::new(), + inner_instructions: std::vec::Vec::new(), + logs: std::vec::Vec::new(), + balance_changes: std::vec::Vec::new(), + }; + } + + async fn assert_rollback_state( + store: &crate::PostgresStore, + signature: &str, + expected_processing_state: &str, + expected_core_count: i64, + expected_ledger_count: i64, + ) { + let core_count_result = sqlx::query_scalar::( + "SELECT COUNT(*)::BIGINT FROM kb_sol_core_transactions WHERE signature = $1", + ) + .bind(signature) + .fetch_one(store.pool()) + .await; + let core_count = match core_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected core count error: {error}"), + }; + let account_count_result = sqlx::query_scalar::( + "SELECT COUNT(*)::BIGINT FROM kb_sol_core_account_keys WHERE signature = $1", + ) + .bind(signature) + .fetch_one(store.pool()) + .await; + let account_count = match account_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected account count error: {error}"), + }; + let processing_state_result = sqlx::query_scalar::( + "SELECT processing_state FROM kb_sol_raw_transactions WHERE signature = $1", + ) + .bind(signature) + .fetch_one(store.pool()) + .await; + let processing_state = match processing_state_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected processing state error: {error}"), + }; + let ledger_count_result = sqlx::query_scalar::( + "SELECT COUNT(*)::BIGINT FROM kb_sol_ops_processing_ledger WHERE stage = 'core_extraction' AND processor_name = 'canonical_to_core' AND processor_version = 'rollback-test' AND input_key = $1", + ) + .bind(signature) + .fetch_one(store.pool()) + .await; + let ledger_count = match ledger_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected ledger count error: {error}"), + }; + assert_eq!(core_count, expected_core_count); + assert_eq!(account_count, expected_core_count); + assert_eq!(processing_state, expected_processing_state); + assert_eq!(ledger_count, expected_ledger_count); + } + + async fn cleanup_rollback_test(store: &crate::PostgresStore, signature: &str) { + let ledger_result = sqlx::query( + "DELETE FROM kb_sol_ops_processing_ledger WHERE processor_version = 'rollback-test' AND input_key = $1", + ) + .bind(signature) + .execute(store.pool()) + .await; + if let std::result::Result::Err(error) = ledger_result { + panic!("unexpected ledger cleanup error: {error}"); + } + let core_result = sqlx::query("DELETE FROM kb_sol_core_transactions WHERE signature = $1") + .bind(signature) + .execute(store.pool()) + .await; + if let std::result::Result::Err(error) = core_result { + panic!("unexpected core cleanup error: {error}"); + } + let raw_result = sqlx::query("DELETE FROM kb_sol_raw_transactions WHERE signature = $1") + .bind(signature) + .execute(store.pool()) + .await; + if let std::result::Result::Err(error) = raw_result { + panic!("unexpected raw cleanup error: {error}"); + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/core_queries.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/core_queries.rs new file mode 100644 index 0000000..e1f6a91 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/core_queries.rs @@ -0,0 +1,1234 @@ +// file: kb_store_pg/src/queries/core_queries.rs +// version: 13 + +//! PostgreSQL queries for normalized Solana core storage. + +use sqlx::Row; // rust-rules: trait-import + +const OUTER_INSTRUCTIONS_CONTEXT_SQL: &str = "SELECT COALESCE(jsonb_agg(jsonb_build_object('instructionIndex', instruction_path::BIGINT, 'instructionPath', instruction_path, 'programId', program_id, 'payloadJson', payload_json, 'payloadHash', payload_json_hash) ORDER BY instruction_path::BIGINT, instruction_path ASC), '[]'::jsonb) FROM kb_sol_core_instructions WHERE signature = $1 AND instruction_path ~ '^[0-9]+$'"; + +pub(crate) async fn apply_core_store_schema(pool: &sqlx::PgPool) -> kb_core::Result<()> { + let validation_result = crate::validate_core_store_table_names(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let transaction_result = pool.begin().await; + let mut transaction = match transaction_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core store schema transaction failed: {error}" + ))); + }, + }; + let lock_result = sqlx::query("SELECT pg_advisory_xact_lock($1)") + .bind(crate::migrations::STORE_SCHEMA_ADVISORY_LOCK_ID) + .execute(&mut *transaction) + .await; + if let std::result::Result::Err(error) = lock_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core store schema advisory lock failed: {error}" + ))); + } + for statement in crate::migrations::core_store_schema_statements() { + let execution_result = sqlx::query(statement).execute(&mut *transaction).await; + if let std::result::Result::Err(error) = execution_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core store schema initialization failed: {error}" + ))); + } + } + let commit_result = transaction.commit().await; + if let std::result::Result::Err(error) = commit_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core store schema commit failed: {error}" + ))); + } + return std::result::Result::Ok(()); +} + +pub(crate) async fn insert_core_transaction( + pool: &sqlx::PgPool, + input: &kb_store_core::CoreTransactionInsert, +) -> kb_core::Result { + let slot_result = crate::queries::core_queries::sql_slot_from_u64(input.slot); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let query_result = sqlx::query_scalar::( + "INSERT INTO kb_sol_core_transactions (raw_transaction_id, signature, slot, failed, err_json) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (signature) DO NOTHING RETURNING id", + ) + .bind(input.raw_transaction_id) + .bind(input.signature.as_str()) + .bind(slot) + .bind(input.failed) + .bind(input.err_json.clone()) + .fetch_optional(pool) + .await; + return crate::queries::core_queries::outcome_from_insert_optional( + query_result, + "postgres core transaction insert failed", + ); +} + +pub(crate) async fn insert_core_account_keys( + pool: &sqlx::PgPool, + inputs: &[kb_store_core::CoreAccountKeyInsert], +) -> kb_core::Result { + let mut inserted_count = 0_u64; + let mut skipped_count = 0_u64; + for input in inputs { + let slot_result = crate::queries::core_queries::sql_slot_from_u64(input.slot); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let account_index_result = crate::queries::core_queries::sql_i32_from_u32( + input.account_index, + "core account index does not fit into SQL INTEGER", + ); + let account_index = match account_index_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let source = crate::queries::core_queries::account_key_source_to_sql(input.source); + let query_result = sqlx::query_scalar::( + "INSERT INTO kb_sol_core_account_keys (transaction_id, signature, slot, account_index, account_key, source, writable, signer, executable) VALUES ((SELECT id FROM kb_sol_core_transactions WHERE signature = $1 LIMIT 1), $1, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT (signature, account_index) DO NOTHING RETURNING id", + ) + .bind(input.signature.as_str()) + .bind(slot) + .bind(account_index) + .bind(input.account_key.as_str()) + .bind(source) + .bind(input.writable) + .bind(input.signer) + .bind(input.executable) + .fetch_optional(pool) + .await; + let outcome_result = crate::queries::core_queries::outcome_from_insert_optional( + query_result, + "postgres core account key insert failed", + ); + let outcome = match outcome_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + inserted_count += outcome.inserted_count; + skipped_count += outcome.skipped_count; + } + return std::result::Result::Ok(kb_store_core::InsertOutcome::new( + inserted_count, + 0, + skipped_count, + )); +} + +pub(crate) async fn insert_core_instructions( + pool: &sqlx::PgPool, + inputs: &[kb_store_core::CoreInstructionInsert], +) -> kb_core::Result { + let mut inserted_count = 0_u64; + let mut skipped_count = 0_u64; + for input in inputs { + let slot_result = crate::queries::core_queries::sql_slot_from_u64(input.slot); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let processing_state = crate::queries::core_queries::instruction_processing_state_to_sql( + input.processing_state, + ); + let query_result = sqlx::query_scalar::( + "INSERT INTO kb_sol_core_instructions (transaction_id, signature, slot, instruction_path, program_id, accounts_json, payload_json, payload_json_hash, processing_state) VALUES ((SELECT id FROM kb_sol_core_transactions WHERE signature = $1 LIMIT 1), $1, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT (signature, instruction_path) DO NOTHING RETURNING id", + ) + .bind(input.signature.as_str()) + .bind(slot) + .bind(input.instruction_path.as_str()) + .bind(input.program_id.as_str()) + .bind(&input.accounts_json) + .bind(&input.payload_json) + .bind(input.payload_json_hash.as_deref()) + .bind(processing_state) + .fetch_optional(pool) + .await; + let outcome_result = crate::queries::core_queries::outcome_from_insert_optional( + query_result, + "postgres core instruction insert failed", + ); + let outcome = match outcome_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + inserted_count += outcome.inserted_count; + skipped_count += outcome.skipped_count; + } + return std::result::Result::Ok(kb_store_core::InsertOutcome::new( + inserted_count, + 0, + skipped_count, + )); +} + +pub(crate) async fn insert_core_inner_instructions( + pool: &sqlx::PgPool, + inputs: &[kb_store_core::CoreInnerInstructionInsert], +) -> kb_core::Result { + let mut inserted_count = 0_u64; + let mut skipped_count = 0_u64; + for input in inputs { + let slot_result = crate::queries::core_queries::sql_slot_from_u64(input.slot); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let query_result = sqlx::query_scalar::( + "INSERT INTO kb_sol_core_inner_instructions (transaction_id, signature, slot, parent_instruction_path, instruction_path, program_id, accounts_json, payload_json, payload_json_hash) VALUES ((SELECT id FROM kb_sol_core_transactions WHERE signature = $1 LIMIT 1), $1, $2, $3, $4, $5, $6, $7, $8) ON CONFLICT (signature, instruction_path) DO NOTHING RETURNING id", + ) + .bind(input.signature.as_str()) + .bind(slot) + .bind(input.parent_instruction_path.as_str()) + .bind(input.instruction_path.as_str()) + .bind(input.program_id.as_str()) + .bind(&input.accounts_json) + .bind(&input.payload_json) + .bind(input.payload_json_hash.as_deref()) + .fetch_optional(pool) + .await; + let outcome_result = crate::queries::core_queries::outcome_from_insert_optional( + query_result, + "postgres core inner instruction insert failed", + ); + let outcome = match outcome_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + inserted_count += outcome.inserted_count; + skipped_count += outcome.skipped_count; + } + return std::result::Result::Ok(kb_store_core::InsertOutcome::new( + inserted_count, + 0, + skipped_count, + )); +} + +pub(crate) async fn insert_core_logs( + pool: &sqlx::PgPool, + inputs: &[kb_store_core::CoreLogInsert], +) -> kb_core::Result { + let mut inserted_count = 0_u64; + let mut skipped_count = 0_u64; + for input in inputs { + let slot_result = crate::queries::core_queries::sql_slot_from_u64(input.slot); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let log_index_result = crate::queries::core_queries::sql_i32_from_u32( + input.log_index, + "core log index does not fit into SQL INTEGER", + ); + let log_index = match log_index_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let query_result = sqlx::query_scalar::( + "INSERT INTO kb_sol_core_logs (transaction_id, signature, slot, log_index, instruction_path, program_id, log_text, log_text_hash) VALUES ((SELECT id FROM kb_sol_core_transactions WHERE signature = $1 LIMIT 1), $1, $2, $3, $4, $5, $6, $7) ON CONFLICT (signature, log_index) DO NOTHING RETURNING id", + ) + .bind(input.signature.as_str()) + .bind(slot) + .bind(log_index) + .bind(input.instruction_path.as_deref()) + .bind(input.program_id.as_deref()) + .bind(input.log_text.as_str()) + .bind(input.log_text_hash.as_deref()) + .fetch_optional(pool) + .await; + let outcome_result = crate::queries::core_queries::outcome_from_insert_optional( + query_result, + "postgres core log insert failed", + ); + let outcome = match outcome_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + inserted_count += outcome.inserted_count; + skipped_count += outcome.skipped_count; + } + return std::result::Result::Ok(kb_store_core::InsertOutcome::new( + inserted_count, + 0, + skipped_count, + )); +} + +pub(crate) async fn insert_core_balance_changes( + pool: &sqlx::PgPool, + inputs: &[kb_store_core::CoreBalanceChangeInsert], +) -> kb_core::Result { + let mut inserted_count = 0_u64; + let mut skipped_count = 0_u64; + for input in inputs { + let slot_result = crate::queries::core_queries::sql_slot_from_u64(input.slot); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let balance_change_index_result = crate::queries::core_queries::sql_i32_from_u32( + input.balance_change_index, + "core balance change index does not fit into SQL INTEGER", + ); + let balance_change_index = match balance_change_index_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let account_index_result = crate::queries::core_queries::optional_sql_i32_from_u32( + input.account_index, + "core balance account index does not fit into SQL INTEGER", + ); + let account_index = match account_index_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let balance_kind = + crate::queries::core_queries::balance_change_kind_to_sql(input.balance_kind); + let query_result = sqlx::query_scalar::( + "INSERT INTO kb_sol_core_balance_changes (transaction_id, signature, slot, balance_change_index, balance_kind, account_index, account_key, mint, owner, pre_balance_json, post_balance_json, delta_json) VALUES ((SELECT id FROM kb_sol_core_transactions WHERE signature = $1 LIMIT 1), $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) ON CONFLICT (signature, balance_change_index) DO NOTHING RETURNING id", + ) + .bind(input.signature.as_str()) + .bind(slot) + .bind(balance_change_index) + .bind(balance_kind) + .bind(account_index) + .bind(input.account_key.as_deref()) + .bind(input.mint.as_deref()) + .bind(input.owner.as_deref()) + .bind(input.pre_balance_json.clone()) + .bind(input.post_balance_json.clone()) + .bind(input.delta_json.clone()) + .fetch_optional(pool) + .await; + let outcome_result = crate::queries::core_queries::outcome_from_insert_optional( + query_result, + "postgres core balance change insert failed", + ); + let outcome = match outcome_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + inserted_count += outcome.inserted_count; + skipped_count += outcome.skipped_count; + } + return std::result::Result::Ok(kb_store_core::InsertOutcome::new( + inserted_count, + 0, + skipped_count, + )); +} + +pub(crate) async fn list_core_instructions_for_replay( + pool: &sqlx::PgPool, + filter: &kb_store_core::CoreInstructionReplayFilter, + page_request: &kb_store_core::PageRequest, +) -> kb_core::Result> { + let min_slot_result = crate::queries::core_queries::optional_sql_slot_from_u64(filter.min_slot); + let min_slot = match min_slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let max_slot_result = crate::queries::core_queries::optional_sql_slot_from_u64(filter.max_slot); + let max_slot = match max_slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let offset_result = crate::queries::core_queries::sql_i64_from_u64( + page_request.offset, + "page offset does not fit into SQL BIGINT", + ); + let offset = match offset_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let processing_state = filter + .processing_state + .map(crate::queries::core_queries::instruction_processing_state_to_sql); + let query_result = sqlx::query( + "SELECT id, transaction_id, signature, slot, instruction_path, program_id, accounts_json, payload_json, payload_json_hash, processing_state, created_at, updated_at FROM kb_sol_core_instructions WHERE ($1::TEXT IS NULL OR processing_state = $1) AND ($2::TEXT IS NULL OR program_id = $2) AND ($3::BIGINT IS NULL OR slot >= $3) AND ($4::BIGINT IS NULL OR slot <= $4) ORDER BY slot ASC, id ASC LIMIT $5 OFFSET $6", + ) + .bind(processing_state) + .bind(filter.program_id.as_deref()) + .bind(min_slot) + .bind(max_slot) + .bind(i64::from(page_request.limit)) + .bind(offset) + .fetch_all(pool) + .await; + let rows = match query_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core instruction replay query failed: {error}" + ))); + }, + }; + let mut output = std::vec::Vec::with_capacity(rows.len()); + for row in rows { + let mapped_result = crate::queries::core_queries::row_to_core_instruction_row(&row); + match mapped_result { + std::result::Result::Ok(value) => output.push(value), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + return std::result::Result::Ok(output); +} + +pub(crate) async fn list_core_instruction_replay_inputs( + pool: &sqlx::PgPool, + filter: &kb_store_core::CoreInstructionReplayFilter, + page_request: &kb_store_core::PageRequest, +) -> kb_core::Result> { + let instructions_result = + crate::queries::core_queries::list_core_instructions_for_replay(pool, filter, page_request) + .await; + let instructions = match instructions_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let mut output = std::vec::Vec::with_capacity(instructions.len()); + for instruction in instructions { + let input_result = + crate::queries::core_queries::load_replay_input_for_instruction(pool, &instruction) + .await; + match input_result { + std::result::Result::Ok(value) => output.push(value), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + return std::result::Result::Ok(output); +} + +pub(crate) async fn list_decode_replay_inputs( + pool: &sqlx::PgPool, + filter: &kb_store_core::DecodeSelectionFilter, +) -> kb_core::Result> { + let min_slot_result = crate::queries::core_queries::optional_sql_slot_from_u64(filter.min_slot); + let min_slot = match min_slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let max_slot_result = crate::queries::core_queries::optional_sql_slot_from_u64(filter.max_slot); + let max_slot = match max_slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let limit = i64::from(filter.limit); + let processing_states = filter + .processing_states + .iter() + .map(|state| { + return crate::queries::core_queries::instruction_processing_state_to_sql(*state) + .to_string(); + }) + .collect::>(); + let rows_result = if filter.incomplete_signatures { + sqlx::query( + "WITH incomplete AS (SELECT signature, MIN(slot) AS first_slot FROM kb_sol_core_instructions WHERE (cardinality($1::text[]) = 0 OR signature = ANY($1)) AND ($3::bigint IS NULL OR slot >= $3) AND ($4::bigint IS NULL OR slot <= $4) AND (cardinality($5::text[]) = 0 OR program_id = ANY($5)) AND (processing_state = ANY($2) OR (processing_state = 'ignored' AND lifecycle_reason = 'unsupported')) GROUP BY signature ORDER BY MIN(slot), signature LIMIT $7) SELECT i.id, i.transaction_id, i.signature, i.slot, i.instruction_path, i.program_id, i.accounts_json, i.payload_json, i.payload_json_hash, i.processing_state, i.created_at, i.updated_at FROM kb_sol_core_instructions i JOIN incomplete s ON s.signature = i.signature WHERE (cardinality($5::text[]) = 0 OR i.program_id = ANY($5)) AND (cardinality($6::text[]) = 0 OR i.instruction_path = ANY($6)) ORDER BY i.slot ASC, i.signature ASC, i.instruction_path ASC", + ) + .bind(&filter.signatures) + .bind(&processing_states) + .bind(min_slot) + .bind(max_slot) + .bind(&filter.program_ids) + .bind(&filter.instruction_paths) + .bind(limit) + .fetch_all(pool) + .await + } else { + sqlx::query( + "SELECT id, transaction_id, signature, slot, instruction_path, program_id, accounts_json, payload_json, payload_json_hash, processing_state, created_at, updated_at FROM kb_sol_core_instructions WHERE (cardinality($1::text[]) = 0 OR signature = ANY($1)) AND (cardinality($2::text[]) = 0 OR processing_state = ANY($2)) AND ($3::bigint IS NULL OR slot >= $3) AND ($4::bigint IS NULL OR slot <= $4) AND (cardinality($5::text[]) = 0 OR program_id = ANY($5)) AND (cardinality($6::text[]) = 0 OR instruction_path = ANY($6)) ORDER BY slot ASC, signature ASC, instruction_path ASC LIMIT $7", + ) + .bind(&filter.signatures) + .bind(&processing_states) + .bind(min_slot) + .bind(max_slot) + .bind(&filter.program_ids) + .bind(&filter.instruction_paths) + .bind(limit) + .fetch_all(pool) + .await + }; + let rows = match rows_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode replay input selection failed: {error}" + ))); + }, + }; + let mut output = std::vec::Vec::with_capacity(rows.len()); + for row in rows { + let instruction_result = crate::queries::core_queries::row_to_core_instruction_row(&row); + let instruction = match instruction_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let input_result = + crate::queries::core_queries::load_replay_input_for_instruction(pool, &instruction) + .await; + match input_result { + std::result::Result::Ok(value) => output.push(value), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + return std::result::Result::Ok(output); +} + +pub(crate) async fn update_core_instruction_lifecycle( + pool: &sqlx::PgPool, + mark: &kb_store_core::CoreInstructionLifecycleMark, +) -> kb_core::Result { + let processing_state = + crate::queries::core_queries::instruction_processing_state_to_sql(mark.processing_state); + let query_result = sqlx::query( + "UPDATE kb_sol_core_instructions SET processing_state = $1, processor_name = $2, processor_version = $3, lifecycle_reason = $4, updated_at = NOW() WHERE signature = $5 AND instruction_path = $6", + ) + .bind(processing_state) + .bind(mark.processor_name.as_deref()) + .bind(mark.processor_version.as_deref()) + .bind(mark.reason.as_deref()) + .bind(mark.signature.as_str()) + .bind(mark.instruction_path.as_str()) + .execute(pool) + .await; + return crate::queries::core_queries::outcome_from_update_result( + query_result, + "postgres core instruction lifecycle update failed", + ); +} + +pub(crate) fn instruction_processing_state_to_sql( + state: kb_store_core::CoreInstructionProcessingState, +) -> &'static str { + return match state { + kb_store_core::CoreInstructionProcessingState::Pending => "pending", + kb_store_core::CoreInstructionProcessingState::Decoded => "decoded", + kb_store_core::CoreInstructionProcessingState::Materialized => "materialized", + kb_store_core::CoreInstructionProcessingState::Ignored => "ignored", + kb_store_core::CoreInstructionProcessingState::Failed => "failed", + kb_store_core::CoreInstructionProcessingState::ReplayRequested => "replay_requested", + }; +} + +fn instruction_processing_state_from_sql( + state: &str, +) -> kb_core::Result { + return match state { + "pending" => { + std::result::Result::Ok(kb_store_core::CoreInstructionProcessingState::Pending) + }, + "decoded" => { + std::result::Result::Ok(kb_store_core::CoreInstructionProcessingState::Decoded) + }, + "materialized" => { + std::result::Result::Ok(kb_store_core::CoreInstructionProcessingState::Materialized) + }, + "ignored" => { + std::result::Result::Ok(kb_store_core::CoreInstructionProcessingState::Ignored) + }, + "failed" => std::result::Result::Ok(kb_store_core::CoreInstructionProcessingState::Failed), + "replay_requested" => { + std::result::Result::Ok(kb_store_core::CoreInstructionProcessingState::ReplayRequested) + }, + _unknown => std::result::Result::Err(kb_core::Error::db( + "unknown core instruction processing state", + )), + }; +} + +fn account_key_source_to_sql(source: kb_store_core::CoreAccountKeySource) -> &'static str { + return match source { + kb_store_core::CoreAccountKeySource::Static => "static", + kb_store_core::CoreAccountKeySource::LoadedWritable => "loaded_writable", + kb_store_core::CoreAccountKeySource::LoadedReadonly => "loaded_readonly", + }; +} + +fn balance_change_kind_to_sql(kind: kb_store_core::CoreBalanceChangeKind) -> &'static str { + return match kind { + kb_store_core::CoreBalanceChangeKind::NativeLamports => "native_lamports", + kb_store_core::CoreBalanceChangeKind::TokenAmount => "token_amount", + }; +} + +fn sql_slot_from_u64(slot: u64) -> kb_core::Result { + return crate::queries::core_queries::sql_i64_from_u64( + slot, + "solana slot does not fit into SQL BIGINT", + ); +} + +fn optional_sql_slot_from_u64( + slot: std::option::Option, +) -> kb_core::Result> { + return match slot { + std::option::Option::Some(value) => { + let conversion_result = crate::queries::core_queries::sql_slot_from_u64(value); + match conversion_result { + std::result::Result::Ok(converted) => { + std::result::Result::Ok(std::option::Option::Some(converted)) + }, + std::result::Result::Err(error) => std::result::Result::Err(error), + } + }, + std::option::Option::None => std::result::Result::Ok(std::option::Option::None), + }; +} + +fn sql_i32_from_u32(value: u32, message: &str) -> kb_core::Result { + let conversion_result = i32::try_from(value); + return match conversion_result { + std::result::Result::Ok(converted) => std::result::Result::Ok(converted), + std::result::Result::Err(_error) => std::result::Result::Err(kb_core::Error::db(message)), + }; +} + +fn optional_sql_i32_from_u32( + value: std::option::Option, + message: &str, +) -> kb_core::Result> { + return match value { + std::option::Option::Some(inner) => { + let conversion_result = crate::queries::core_queries::sql_i32_from_u32(inner, message); + match conversion_result { + std::result::Result::Ok(converted) => { + std::result::Result::Ok(std::option::Option::Some(converted)) + }, + std::result::Result::Err(error) => std::result::Result::Err(error), + } + }, + std::option::Option::None => std::result::Result::Ok(std::option::Option::None), + }; +} + +fn sql_i64_from_u64(value: u64, message: &str) -> kb_core::Result { + let conversion_result = i64::try_from(value); + return match conversion_result { + std::result::Result::Ok(converted) => std::result::Result::Ok(converted), + std::result::Result::Err(_error) => std::result::Result::Err(kb_core::Error::db(message)), + }; +} + +fn u64_from_sql_i64(value: i64, message: &str) -> kb_core::Result { + let conversion_result = u64::try_from(value); + return match conversion_result { + std::result::Result::Ok(converted) => std::result::Result::Ok(converted), + std::result::Result::Err(_error) => std::result::Result::Err(kb_core::Error::db(message)), + }; +} + +fn outcome_from_insert_optional( + query_result: std::result::Result, sqlx::Error>, + error_prefix: &str, +) -> kb_core::Result { + return match query_result { + std::result::Result::Ok(std::option::Option::Some(_id)) => { + std::result::Result::Ok(kb_store_core::InsertOutcome::new(1, 0, 0)) + }, + std::result::Result::Ok(std::option::Option::None) => { + std::result::Result::Ok(kb_store_core::InsertOutcome::new(0, 0, 1)) + }, + std::result::Result::Err(error) => { + std::result::Result::Err(kb_core::Error::db(format!("{error_prefix}: {error}"))) + }, + }; +} + +fn outcome_from_update_result( + query_result: std::result::Result, + error_prefix: &str, +) -> kb_core::Result { + return match query_result { + std::result::Result::Ok(result) => { + let rows_affected = result.rows_affected(); + if rows_affected == 0 { + return std::result::Result::Ok(kb_store_core::InsertOutcome::new(0, 0, 1)); + } + std::result::Result::Ok(kb_store_core::InsertOutcome::new(0, rows_affected, 0)) + }, + std::result::Result::Err(error) => { + std::result::Result::Err(kb_core::Error::db(format!("{error_prefix}: {error}"))) + }, + }; +} + +fn row_to_core_instruction_row( + row: &sqlx::postgres::PgRow, +) -> kb_core::Result { + let state_text_result: std::result::Result = + row.try_get("processing_state"); + let state_text = match state_text_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core instruction processing state read failed: {error}" + ))); + }, + }; + let state_result = + crate::queries::core_queries::instruction_processing_state_from_sql(state_text.as_str()); + let processing_state = match state_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let id = match crate::queries::core_queries::read_row_value(row, "id") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let transaction_id = match crate::queries::core_queries::read_row_value(row, "transaction_id") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let signature = match crate::queries::core_queries::read_row_value(row, "signature") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let slot = match crate::queries::core_queries::read_row_value(row, "slot") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let instruction_path = + match crate::queries::core_queries::read_row_value(row, "instruction_path") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let program_id = match crate::queries::core_queries::read_row_value(row, "program_id") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let accounts_json = match crate::queries::core_queries::read_row_value(row, "accounts_json") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let payload_json = match crate::queries::core_queries::read_row_value(row, "payload_json") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let payload_json_hash = + match crate::queries::core_queries::read_row_value(row, "payload_json_hash") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let created_at = match crate::queries::core_queries::read_row_value(row, "created_at") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let updated_at = match crate::queries::core_queries::read_row_value(row, "updated_at") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(kb_store_core::CoreInstructionRow { + id, + transaction_id, + signature, + slot, + instruction_path, + program_id, + accounts_json, + payload_json, + payload_json_hash, + processing_state, + created_at, + updated_at, + }); +} + +fn read_row_value<'row, T>(row: &'row sqlx::postgres::PgRow, column: &str) -> kb_core::Result +where + T: sqlx::Decode<'row, sqlx::Postgres> + sqlx::Type, +{ + let value_result = row.try_get::(column); + return match value_result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::db(format!( + "postgres row read failed for {column}: {error}" + ))), + }; +} + +pub(crate) async fn load_replay_input_for_instruction( + pool: &sqlx::PgPool, + instruction: &kb_store_core::CoreInstructionRow, +) -> kb_core::Result { + let transaction_context_result = + sqlx::query_as::)>( + "SELECT failed, err_json FROM kb_sol_core_transactions WHERE signature = $1 LIMIT 1", + ) + .bind(instruction.signature.as_str()) + .fetch_one(pool) + .await; + let transaction_context = match transaction_context_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres core transaction context query failed: {error}" + ))); + }, + }; + let account_keys_json_result = + crate::queries::core_queries::load_account_keys_json(pool, instruction.signature.as_str()) + .await; + let account_keys_json = match account_keys_json_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let outer_instructions_json_result = + crate::queries::core_queries::load_outer_instructions_json( + pool, + instruction.signature.as_str(), + ) + .await; + let outer_instructions_json = match outer_instructions_json_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let inner_instructions_json_result = + crate::queries::core_queries::load_inner_instructions_json( + pool, + instruction.signature.as_str(), + instruction.instruction_path.as_str(), + ) + .await; + let inner_instructions_json = match inner_instructions_json_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let logs_json_result = + crate::queries::core_queries::load_logs_json(pool, instruction.signature.as_str()).await; + let logs_json = match logs_json_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let balance_changes_json_result = crate::queries::core_queries::load_balance_changes_json( + pool, + instruction.signature.as_str(), + ) + .await; + let balance_changes_json = match balance_changes_json_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let replay_input_key = format!("{}:{}", instruction.signature, instruction.instruction_path); + let slot_result = crate::queries::core_queries::u64_from_sql_i64( + instruction.slot, + "core instruction slot is negative", + ); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return kb_store_core::CoreInstructionReplayInput::new( + replay_input_key, + instruction.signature.clone(), + slot, + instruction.instruction_path.clone(), + instruction.program_id.clone(), + transaction_context.0, + transaction_context.1, + account_keys_json, + instruction.accounts_json.clone(), + instruction.payload_json.clone(), + instruction.payload_json_hash.clone(), + outer_instructions_json, + inner_instructions_json, + logs_json, + balance_changes_json, + ); +} + +async fn load_account_keys_json( + pool: &sqlx::PgPool, + signature: &str, +) -> kb_core::Result { + return crate::queries::core_queries::load_json_aggregate( + pool, + "SELECT COALESCE(jsonb_agg(jsonb_build_object('accountIndex', account_index, 'accountKey', account_key, 'source', source, 'writable', writable, 'signer', signer, 'executable', executable) ORDER BY account_index), '[]'::jsonb) FROM kb_sol_core_account_keys WHERE signature = $1", + signature, + "postgres core account keys context query failed", + ) + .await; +} + +async fn load_outer_instructions_json( + pool: &sqlx::PgPool, + signature: &str, +) -> kb_core::Result { + return crate::queries::core_queries::load_json_aggregate( + pool, + crate::queries::core_queries::OUTER_INSTRUCTIONS_CONTEXT_SQL, + signature, + "postgres core outer instructions context query failed", + ) + .await; +} + +async fn load_inner_instructions_json( + pool: &sqlx::PgPool, + signature: &str, + instruction_path: &str, +) -> kb_core::Result { + let like_pattern = format!("{instruction_path}/%"); + let query_result = sqlx::query_scalar::( + "SELECT COALESCE(jsonb_agg(jsonb_build_object('parentInstructionPath', parent_instruction_path, 'instructionPath', instruction_path, 'programId', program_id, 'accounts', accounts_json, 'payload', payload_json) ORDER BY instruction_path), '[]'::jsonb) FROM kb_sol_core_inner_instructions WHERE signature = $1 AND (parent_instruction_path = $2 OR instruction_path LIKE $3)", + ) + .bind(signature) + .bind(instruction_path) + .bind(like_pattern.as_str()) + .fetch_one(pool) + .await; + return match query_result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::db(format!( + "postgres core inner instructions context query failed: {error}" + ))), + }; +} + +async fn load_logs_json( + pool: &sqlx::PgPool, + signature: &str, +) -> kb_core::Result { + return crate::queries::core_queries::load_json_aggregate( + pool, + "SELECT COALESCE(jsonb_agg(jsonb_build_object('logIndex', log_index, 'instructionPath', instruction_path, 'programId', program_id, 'text', log_text) ORDER BY log_index), '[]'::jsonb) FROM kb_sol_core_logs WHERE signature = $1", + signature, + "postgres core logs context query failed", + ) + .await; +} + +async fn load_balance_changes_json( + pool: &sqlx::PgPool, + signature: &str, +) -> kb_core::Result { + return crate::queries::core_queries::load_json_aggregate( + pool, + "SELECT COALESCE(jsonb_agg(jsonb_build_object('balanceChangeIndex', balance_change_index, 'balanceKind', balance_kind, 'accountIndex', account_index, 'accountKey', account_key, 'mint', mint, 'owner', owner, 'pre', pre_balance_json, 'post', post_balance_json, 'delta', delta_json) ORDER BY balance_change_index), '[]'::jsonb) FROM kb_sol_core_balance_changes WHERE signature = $1", + signature, + "postgres core balance changes context query failed", + ) + .await; +} + +async fn load_json_aggregate( + pool: &sqlx::PgPool, + sql: &'static str, + signature: &str, + error_prefix: &str, +) -> kb_core::Result { + let query_result = sqlx::query_scalar::(sql) + .bind(signature) + .fetch_one(pool) + .await; + return match query_result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => { + std::result::Result::Err(kb_core::Error::db(format!("{error_prefix}: {error}"))) + }, + }; +} + +#[cfg(test)] +mod tests { + #[test] + fn outer_instruction_context_query_is_read_only_and_numeric() { + let normalized = + crate::queries::core_queries::OUTER_INSTRUCTIONS_CONTEXT_SQL.to_ascii_lowercase(); + assert!(normalized.starts_with("select ")); + assert!(normalized.contains("order by instruction_path::bigint, instruction_path asc")); + assert!(!normalized.contains("create ")); + assert!(!normalized.contains("alter ")); + assert!(!normalized.contains("drop ")); + } + + #[test] + fn instruction_processing_state_serializes_to_lower_snake_case() { + let value = crate::queries::core_queries::instruction_processing_state_to_sql( + kb_store_core::CoreInstructionProcessingState::ReplayRequested, + ); + assert_eq!(value, "replay_requested"); + } + + #[test] + fn account_key_source_serializes_to_lower_snake_case() { + let value = crate::queries::core_queries::account_key_source_to_sql( + kb_store_core::CoreAccountKeySource::LoadedWritable, + ); + assert_eq!(value, "loaded_writable"); + } + + #[test] + fn balance_change_kind_serializes_to_lower_snake_case() { + let value = crate::queries::core_queries::balance_change_kind_to_sql( + kb_store_core::CoreBalanceChangeKind::NativeLamports, + ); + assert_eq!(value, "native_lamports"); + } + + #[test] + fn sql_slot_rejects_values_above_bigint() { + let result = crate::queries::core_queries::sql_slot_from_u64(u64::MAX); + assert!(result.is_err()); + } + + #[tokio::test] + async fn optional_postgres_core_store_roundtrip_from_env() { + let database_url = match std::env::var("KB_POSTGRES_TEST_URL") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => return, + }; + let _postgres_guard = crate::test_serial::postgres_test_guard().await; + let options_result = crate::PostgresStoreOptions::new(database_url, 1, 5000, false); + let options = match options_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected options error: {error}"), + }; + let store_result = crate::PostgresStore::connect(options).await; + let store = match store_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected connect error: {error}"), + }; + let schema_result = store.initialize_core_store_schema().await; + if let std::result::Result::Err(error) = schema_result { + panic!("unexpected schema error: {error}"); + } + let signature = test_signature(); + let tx_input = test_core_transaction(signature.as_str()); + let tx_result = + kb_store_core::CoreTransactionStore::insert_core_transaction(&store, &tx_input).await; + let tx_outcome = match tx_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected core tx insert error: {error}"), + }; + assert_eq!(tx_outcome.inserted_count, 1); + let account_inputs = std::vec![test_account_key(signature.as_str())]; + let account_result = + kb_store_core::CoreTransactionStore::insert_core_account_keys(&store, &account_inputs) + .await; + if let std::result::Result::Err(error) = account_result { + panic!("unexpected account insert error: {error}"); + } + let instruction_inputs = std::vec![ + test_instruction(signature.as_str(), "10"), + test_instruction(signature.as_str(), "0"), + test_instruction(signature.as_str(), "2"), + ]; + let instruction_result = kb_store_core::CoreTransactionStore::insert_core_instructions( + &store, + &instruction_inputs, + ) + .await; + if let std::result::Result::Err(error) = instruction_result { + panic!("unexpected instruction insert error: {error}"); + } + let inner_inputs = std::vec![test_inner_instruction(signature.as_str())]; + let inner_result = kb_store_core::CoreTransactionStore::insert_core_inner_instructions( + &store, + &inner_inputs, + ) + .await; + if let std::result::Result::Err(error) = inner_result { + panic!("unexpected inner instruction insert error: {error}"); + } + let log_inputs = std::vec![test_log(signature.as_str())]; + let log_result = + kb_store_core::CoreTransactionStore::insert_core_logs(&store, &log_inputs).await; + if let std::result::Result::Err(error) = log_result { + panic!("unexpected log insert error: {error}"); + } + let balance_inputs = std::vec![test_balance_change(signature.as_str())]; + let balance_result = kb_store_core::CoreTransactionStore::insert_core_balance_changes( + &store, + &balance_inputs, + ) + .await; + if let std::result::Result::Err(error) = balance_result { + panic!("unexpected balance change insert error: {error}"); + } + let page = kb_store_core::PageRequest::first_page(); + let filter = kb_store_core::CoreInstructionReplayFilter::pending(); + let replay_result = + kb_store_core::CoreTransactionStore::list_core_instruction_replay_inputs( + &store, &filter, &page, + ) + .await; + let replay_inputs = match replay_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("unexpected replay input query error: {error}") + }, + }; + let target = replay_inputs.iter().find(|input| { + return input.signature == signature && input.instruction_path == "2"; + }); + let target = match target { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("expected replay input for outer instruction 2"), + }; + let outer = match target.outer_instructions_json.as_array() { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("outer instruction context must be an array"), + }; + let ordered_indexes = outer + .iter() + .filter_map(|value| return value.get("instructionIndex")) + .filter_map(serde_json::Value::as_i64) + .collect::>(); + assert_eq!(ordered_indexes, vec![0, 2, 10]); + assert!(outer.iter().any(|value| { + return value.get("instructionPath").and_then(serde_json::Value::as_str) + == std::option::Option::Some("2"); + })); + let mark_result = kb_store_core::CoreInstructionLifecycleMark::new( + signature, + "0", + kb_store_core::CoreInstructionProcessingState::Decoded, + std::option::Option::Some(std::string::String::from("test_decoder")), + std::option::Option::Some(std::string::String::from("0.0.0")), + std::option::Option::Some(std::string::String::from("test decoded")), + ); + let mark = match mark_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected mark error: {error}"), + }; + let mark_outcome_result = + kb_store_core::CoreTransactionStore::mark_core_instruction_lifecycle(&store, &mark) + .await; + let mark_outcome = match mark_outcome_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected lifecycle mark error: {error}"), + }; + assert_eq!(mark_outcome.updated_count, 1); + return; + } + + fn test_signature() -> std::string::String { + let now_result = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH); + let duration = match now_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return std::string::String::from("test_core_signature_fallback"); + }, + }; + return format!("test_core_signature_{}", duration.as_nanos()); + } + + fn test_core_transaction(signature: &str) -> kb_store_core::CoreTransactionInsert { + let result = kb_store_core::CoreTransactionInsert::new( + signature, + 1, + false, + std::option::Option::None, + ); + match result { + std::result::Result::Ok(value) => return value, + std::result::Result::Err(error) => { + panic!("unexpected core transaction build error: {error}") + }, + } + } + + fn test_account_key(signature: &str) -> kb_store_core::CoreAccountKeyInsert { + let result = kb_store_core::CoreAccountKeyInsert::new( + signature, + 1, + 0, + "account", + kb_store_core::CoreAccountKeySource::Static, + true, + true, + std::option::Option::None, + ); + match result { + std::result::Result::Ok(value) => return value, + std::result::Result::Err(error) => { + panic!("unexpected account key build error: {error}") + }, + } + } + + fn test_instruction( + signature: &str, + instruction_path: &str, + ) -> kb_store_core::CoreInstructionInsert { + let result = kb_store_core::CoreInstructionInsert::new( + signature, + 1, + instruction_path, + "program", + serde_json::json!([0]), + serde_json::json!({"dataBase64": "YWJj"}), + ); + match result { + std::result::Result::Ok(value) => return value, + std::result::Result::Err(error) => { + panic!("unexpected instruction build error: {error}") + }, + } + } + + fn test_inner_instruction(signature: &str) -> kb_store_core::CoreInnerInstructionInsert { + let result = kb_store_core::CoreInnerInstructionInsert::new( + signature, + 1, + "0", + "0/0", + "inner_program", + serde_json::json!([0]), + serde_json::json!({"data": "inner"}), + ); + match result { + std::result::Result::Ok(value) => return value, + std::result::Result::Err(error) => { + panic!("unexpected inner instruction build error: {error}") + }, + } + } + + fn test_log(signature: &str) -> kb_store_core::CoreLogInsert { + let result = kb_store_core::CoreLogInsert::new( + signature, + 1, + 0, + std::option::Option::Some(std::string::String::from("0")), + std::option::Option::Some(std::string::String::from("program")), + "Program log: test", + ); + match result { + std::result::Result::Ok(value) => return value, + std::result::Result::Err(error) => panic!("unexpected log build error: {error}"), + } + } + + fn test_balance_change(signature: &str) -> kb_store_core::CoreBalanceChangeInsert { + let result = kb_store_core::CoreBalanceChangeInsert::new( + signature, + 1, + 0, + kb_store_core::CoreBalanceChangeKind::NativeLamports, + std::option::Option::Some(0), + std::option::Option::Some(std::string::String::from("account")), + std::option::Option::None, + std::option::Option::None, + std::option::Option::Some(serde_json::json!(1)), + std::option::Option::Some(serde_json::json!(2)), + std::option::Option::Some(serde_json::json!(1)), + ); + match result { + std::result::Result::Ok(value) => return value, + std::result::Result::Err(error) => { + panic!("unexpected balance change build error: {error}") + }, + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/decode_pipeline_queries.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/decode_pipeline_queries.rs new file mode 100644 index 0000000..32a8eaa --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/decode_pipeline_queries.rs @@ -0,0 +1,1323 @@ +// file: kb_store_pg/src/queries/decode_pipeline_queries.rs +// version: 15 + +//! PostgreSQL queries for contextual decode, coverage and materialization persistence. + +use sqlx::Row; // rust-rules: trait-import + +#[derive(sqlx::FromRow)] +struct MaterializedEventDatabaseRow { + processor_name: std::string::String, + processor_version: std::string::String, + input_key: std::string::String, + output_key: std::string::String, + source_event_key: std::string::String, + source_decoder_name: std::string::String, + source_decoder_version: std::string::String, + signature: std::string::String, + slot: i64, + materialized_family: std::string::String, + payload_json: serde_json::Value, + created_at: std::string::String, + updated_at: std::string::String, +} + +pub(crate) async fn apply_decode_store_schema(pool: &sqlx::PgPool) -> kb_core::Result<()> { + tracing::debug!(target: crate::TRACING_TARGET, action = "apply_decode_store_schema", statement_count = crate::migrations::decode_store_schema_statements().len(), "apply PostgreSQL decode store schema"); + let validation_result = crate::validate_decode_store_table_names(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let transaction_result = pool.begin().await; + let mut transaction = match transaction_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode store schema transaction failed: {error}" + ))); + }, + }; + let lock_result = sqlx::query("SELECT pg_advisory_xact_lock($1)") + .bind(crate::migrations::STORE_SCHEMA_ADVISORY_LOCK_ID) + .execute(&mut *transaction) + .await; + if let std::result::Result::Err(error) = lock_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode store schema advisory lock failed: {error}" + ))); + } + for statement in crate::migrations::decode_store_schema_statements() { + let execution_result = sqlx::query(statement).execute(&mut *transaction).await; + if let std::result::Result::Err(error) = execution_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode store schema initialization failed: {error}" + ))); + } + } + let commit_result = transaction.commit().await; + if let std::result::Result::Err(error) = commit_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode store schema commit failed: {error}" + ))); + } + tracing::debug!(target: crate::TRACING_TARGET, action = "apply_decode_store_schema", committed = true, "PostgreSQL decode store schema applied"); + return std::result::Result::Ok(()); +} + +pub(crate) async fn list_decode_inputs( + pool: &sqlx::PgPool, + filter: &kb_store_core::DecodeSelectionFilter, +) -> kb_core::Result> { + tracing::debug!(target: crate::TRACING_TARGET, action = "list_decode_inputs", signature_count = filter.signatures.len(), signature_sample = ?filter.signatures.iter().take(5).map(std::string::String::as_str).collect::>(), processing_states = ?filter.processing_states, min_slot = ?filter.min_slot, max_slot = ?filter.max_slot, program_ids = ?filter.program_ids, instruction_paths = ?filter.instruction_paths, limit = filter.limit, "query PostgreSQL contextual decode inputs"); + let result = crate::queries::core_queries::list_decode_replay_inputs(pool, filter).await; + return match result { + std::result::Result::Ok(inputs) => { + let selected_input_keys = inputs + .iter() + .take(10) + .map(|input| return input.replay_input_key.as_str()) + .collect::>(); + tracing::debug!(target: crate::TRACING_TARGET, action = "list_decode_inputs", selected_count = inputs.len(), input_key_sample = ?selected_input_keys, "PostgreSQL contextual decode inputs selected"); + std::result::Result::Ok(inputs) + }, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "list_decode_inputs", error = %error, "PostgreSQL contextual decode input query failed"); + std::result::Result::Err(error) + }, + }; +} + +pub(crate) async fn list_materialized_events( + pool: &sqlx::PgPool, + filter: &kb_store_core::MaterializedEventFilter, +) -> kb_core::Result> { + tracing::debug!(target: crate::TRACING_TARGET, action = "list_materialized_events", processor_name = ?filter.processor_name, materialized_family = ?filter.materialized_family, signature_contains = ?filter.signature_contains, limit = filter.limit, "query bounded PostgreSQL materialized events"); + if filter.limit == 0 || filter.limit > kb_store_core::MAX_MATERIALIZED_EVENT_QUERY_ROWS { + return std::result::Result::Err(kb_core::Error::db(format!( + "materialized event query limit must be between 1 and {}", + kb_store_core::MAX_MATERIALIZED_EVENT_QUERY_ROWS + ))); + } + let query_result = sqlx::query_as::( + "SELECT processor_name, processor_version, input_key, output_key, source_event_key, source_decoder_name, source_decoder_version, signature, slot, materialized_family, payload_jsonb AS payload_json, created_at::text AS created_at, updated_at::text AS updated_at FROM kb_sol_mat_events WHERE ($1::text IS NULL OR processor_name = $1) AND ($2::text IS NULL OR materialized_family = $2) AND ($3::text IS NULL OR POSITION(LOWER($3) IN LOWER(signature)) > 0) ORDER BY slot DESC, id DESC LIMIT $4", + ) + .bind(filter.processor_name.as_deref()) + .bind(filter.materialized_family.as_deref()) + .bind(filter.signature_contains.as_deref()) + .bind(i64::from(filter.limit)) + .fetch_all(pool) + .await; + let rows = match query_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres materialized event query failed: {error}" + ))); + }, + }; + let mut output = std::vec::Vec::with_capacity(rows.len()); + for row in rows { + let slot_result = u64::try_from(row.slot); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres materialized event slot conversion failed: {error}" + ))); + }, + }; + output.push(kb_store_core::MaterializedEventQueryRow { + processor_name: row.processor_name, + processor_version: row.processor_version, + input_key: row.input_key, + output_key: row.output_key, + source_event_key: row.source_event_key, + source_decoder_name: row.source_decoder_name, + source_decoder_version: row.source_decoder_version, + signature: row.signature, + slot, + materialized_family: row.materialized_family, + payload_json: row.payload_json, + created_at: row.created_at, + updated_at: row.updated_at, + }); + } + tracing::debug!(target: crate::TRACING_TARGET, action = "list_materialized_events", row_count = output.len(), "bounded PostgreSQL materialized events loaded"); + return std::result::Result::Ok(output); +} + +pub(crate) async fn is_decode_current( + pool: &sqlx::PgPool, + identity: &kb_store_core::ProcessingLedgerIdentity, +) -> kb_core::Result { + tracing::debug!(target: crate::TRACING_TARGET, action = "is_decode_current", stage = %identity.stage, processor_name = %identity.processor_name, processor_version = %identity.processor_version, input_key = %identity.input_key, input_hash = %identity.input_hash, "query PostgreSQL processing ledger current state"); + let query_result = sqlx::query_scalar::( + "SELECT EXISTS(SELECT 1 FROM kb_sol_ops_processing_ledger WHERE stage = $1 AND processor_name = $2 AND processor_version = $3 AND input_key = $4 AND input_hash = $5 AND status = 'succeeded')", + ) + .bind(identity.stage.as_str()) + .bind(identity.processor_name.as_str()) + .bind(identity.processor_version.as_str()) + .bind(identity.input_key.as_str()) + .bind(identity.input_hash.as_str()) + .fetch_one(pool) + .await; + return match query_result { + std::result::Result::Ok(value) => { + tracing::debug!(target: crate::TRACING_TARGET, action = "is_decode_current", stage = %identity.stage, processor_name = %identity.processor_name, processor_version = %identity.processor_version, input_key = %identity.input_key, input_hash = %identity.input_hash, current = value, "PostgreSQL processing ledger current state loaded"); + std::result::Result::Ok(value) + }, + std::result::Result::Err(error) => { + tracing::error!(target: crate::TRACING_TARGET, action = "is_decode_current", stage = %identity.stage, processor_name = %identity.processor_name, processor_version = %identity.processor_version, input_key = %identity.input_key, error = %error, "PostgreSQL processing ledger current check failed"); + std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode ledger current check failed: {error}" + ))) + }, + }; +} + +pub(crate) async fn persist_decode_coverage_declarations( + pool: &sqlx::PgPool, + declarations: &[kb_store_core::DecodeCoverageDeclarationInsert], +) -> kb_core::Result { + if declarations.is_empty() { + tracing::debug!(target: crate::TRACING_TARGET, action = "persist_coverage_declarations", declaration_count = 0_usize, "skip empty PostgreSQL decode coverage declarations"); + return std::result::Result::Ok(kb_store_core::InsertOutcome::new(0, 0, 0)); + } + let processor_name = declarations[0].processor_name.as_str(); + let processor_version = declarations[0].processor_version.as_str(); + let program_ids = declarations + .iter() + .map(|entry| return entry.program_id.as_str()) + .collect::>(); + tracing::debug!(target: crate::TRACING_TARGET, action = "persist_coverage_declarations", processor_name = %processor_name, processor_version = %processor_version, declaration_count = declarations.len(), program_ids = ?program_ids, "persist PostgreSQL decode coverage declarations"); + if declarations.iter().any(|entry| { + return entry.processor_name != processor_name + || entry.processor_version != processor_version + || entry.program_id.trim().is_empty() + || entry.entry_kind.trim().is_empty() + || entry.entry_code.trim().is_empty(); + }) { + return std::result::Result::Err(kb_core::Error::db( + "decode coverage declarations must share one valid processor identity", + )); + } + let mut desired_keys = std::collections::BTreeSet::new(); + for declaration in declarations { + let key = ( + declaration.program_id.clone(), + declaration.surface_code.clone(), + declaration.entry_kind.clone(), + declaration.entry_code.clone(), + declaration.discriminator_hex.clone(), + ); + if !desired_keys.insert(key) { + return std::result::Result::Err(kb_core::Error::db( + "decode coverage declarations contain a duplicate identity", + )); + } + } + let transaction_result = pool.begin().await; + let mut transaction = match transaction_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode coverage transaction failed: {error}" + ))); + }, + }; + let advisory_key = format!("decode_coverage:{processor_name}:{processor_version}"); + let lock_result = sqlx::query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))") + .bind(advisory_key.as_str()) + .execute(&mut *transaction) + .await; + if let std::result::Result::Err(error) = lock_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode coverage declaration lock failed: {error}" + ))); + } + let existing_result = sqlx::query( + "SELECT program_id, surface_code, entry_kind, entry_code, discriminator_hex, historical FROM kb_sol_decode_coverage_declarations WHERE processor_name = $1 AND processor_version = $2 FOR UPDATE", + ) + .bind(processor_name) + .bind(processor_version) + .fetch_all(&mut *transaction) + .await; + let existing_rows = match existing_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode coverage declaration lookup failed: {error}" + ))); + }, + }; + let mut existing = std::collections::BTreeMap::new(); + for row in existing_rows { + let key = ( + row.get::("program_id"), + row.get::, _>("surface_code"), + row.get::("entry_kind"), + row.get::("entry_code"), + row.get::, _>("discriminator_hex"), + ); + existing.insert(key, row.get::("historical")); + } + let mut inserted_count = 0_u64; + let mut updated_count = 0_u64; + let mut skipped_count = 0_u64; + for declaration in declarations { + let key = ( + declaration.program_id.clone(), + declaration.surface_code.clone(), + declaration.entry_kind.clone(), + declaration.entry_code.clone(), + declaration.discriminator_hex.clone(), + ); + let existing_historical = existing.remove(&key); + match existing_historical { + std::option::Option::None => { + let insert_result = sqlx::query( + "INSERT INTO kb_sol_decode_coverage_declarations (processor_name, processor_version, program_id, surface_code, entry_kind, entry_code, discriminator_hex, historical) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", + ) + .bind(declaration.processor_name.as_str()) + .bind(declaration.processor_version.as_str()) + .bind(declaration.program_id.as_str()) + .bind(declaration.surface_code.as_deref()) + .bind(declaration.entry_kind.as_str()) + .bind(declaration.entry_code.as_str()) + .bind(declaration.discriminator_hex.as_deref()) + .bind(declaration.historical) + .execute(&mut *transaction) + .await; + if let std::result::Result::Err(error) = insert_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode coverage declaration insert failed: {error}" + ))); + } + inserted_count += 1; + }, + std::option::Option::Some(historical) if historical == declaration.historical => { + skipped_count += 1; + }, + std::option::Option::Some(_historical) => { + let update_result = sqlx::query( + "UPDATE kb_sol_decode_coverage_declarations SET historical = $1, updated_at = NOW() WHERE processor_name = $2 AND processor_version = $3 AND program_id = $4 AND COALESCE(surface_code, '') = COALESCE($5::text, '') AND entry_kind = $6 AND entry_code = $7 AND COALESCE(discriminator_hex, '') = COALESCE($8::text, '')", + ) + .bind(declaration.historical) + .bind(declaration.processor_name.as_str()) + .bind(declaration.processor_version.as_str()) + .bind(declaration.program_id.as_str()) + .bind(declaration.surface_code.as_deref()) + .bind(declaration.entry_kind.as_str()) + .bind(declaration.entry_code.as_str()) + .bind(declaration.discriminator_hex.as_deref()) + .execute(&mut *transaction) + .await; + if let std::result::Result::Err(error) = update_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode coverage declaration update failed: {error}" + ))); + } + updated_count += 1; + }, + } + } + for (key, _historical) in existing { + let delete_result = sqlx::query( + "DELETE FROM kb_sol_decode_coverage_declarations WHERE processor_name = $1 AND processor_version = $2 AND program_id = $3 AND COALESCE(surface_code, '') = COALESCE($4::text, '') AND entry_kind = $5 AND entry_code = $6 AND COALESCE(discriminator_hex, '') = COALESCE($7::text, '')", + ) + .bind(processor_name) + .bind(processor_version) + .bind(key.0.as_str()) + .bind(key.1.as_deref()) + .bind(key.2.as_str()) + .bind(key.3.as_str()) + .bind(key.4.as_deref()) + .execute(&mut *transaction) + .await; + if let std::result::Result::Err(error) = delete_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres stale decode coverage declaration delete failed: {error}" + ))); + } + updated_count += 1; + } + let commit_result = transaction.commit().await; + if let std::result::Result::Err(error) = commit_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode coverage commit failed: {error}" + ))); + } + let outcome = kb_store_core::InsertOutcome::new(inserted_count, updated_count, skipped_count); + tracing::debug!(target: crate::TRACING_TARGET, action = "persist_coverage_declarations", processor_name = %processor_name, processor_version = %processor_version, outcome = ?outcome, "PostgreSQL decode coverage declarations persisted"); + return std::result::Result::Ok(outcome); +} + +pub(crate) async fn persist_decode_result( + pool: &sqlx::PgPool, + bundle: &kb_store_core::DecodePersistenceBundle, + force_replay: bool, +) -> kb_core::Result { + tracing::debug!(target: crate::TRACING_TARGET, action = "persist_decode_result", signature = %bundle.signature, instruction_path = %bundle.instruction_path, stage = %bundle.ledger_identity.stage, processor_name = %bundle.ledger_identity.processor_name, processor_version = %bundle.ledger_identity.processor_version, input_key = %bundle.ledger_identity.input_key, input_hash = %bundle.ledger_identity.input_hash, status = %bundle.status, observation_count = bundle.observations.len(), coverage_program_id = %bundle.coverage.program_id, force_replay, "persist PostgreSQL contextual decode result"); + let validation_result = bundle.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let transaction_result = pool.begin().await; + let mut transaction = match transaction_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode persistence transaction failed: {error}" + ))); + }, + }; + let delete_events_result = sqlx::query( + "DELETE FROM kb_sol_decode_events WHERE processor_name = $1 AND processor_version = $2 AND input_key = $3", + ) + .bind(bundle.ledger_identity.processor_name.as_str()) + .bind(bundle.ledger_identity.processor_version.as_str()) + .bind(bundle.ledger_identity.input_key.as_str()) + .execute(&mut *transaction) + .await; + if let std::result::Result::Err(error) = delete_events_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode output replacement failed: {error}" + ))); + } + let delete_coverage_result = sqlx::query( + "DELETE FROM kb_sol_decode_coverage_observations WHERE processor_name = $1 AND processor_version = $2 AND input_key = $3", + ) + .bind(bundle.ledger_identity.processor_name.as_str()) + .bind(bundle.ledger_identity.processor_version.as_str()) + .bind(bundle.ledger_identity.input_key.as_str()) + .execute(&mut *transaction) + .await; + if let std::result::Result::Err(error) = delete_coverage_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode coverage replacement failed: {error}" + ))); + } + for observation in &bundle.observations { + let slot_result = i64::try_from(observation.slot); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "decoded observation slot conversion failed: {error}" + ))); + }, + }; + let insert_result = sqlx::query( + "INSERT INTO kb_sol_decode_events (processor_name, processor_version, input_key, input_hash, event_key, signature, slot, instruction_path, program_id, protocol_code, surface_code, event_code, event_name, event_family, source_kind, confidence, proof_kind, proof_jsonb, payload_jsonb, transaction_failed, transaction_error_jsonb, observation_committed) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22) ON CONFLICT (processor_name, processor_version, input_key, event_key) DO UPDATE SET input_hash = EXCLUDED.input_hash, payload_jsonb = EXCLUDED.payload_jsonb, proof_jsonb = EXCLUDED.proof_jsonb, transaction_failed = EXCLUDED.transaction_failed, transaction_error_jsonb = EXCLUDED.transaction_error_jsonb, observation_committed = EXCLUDED.observation_committed, updated_at = NOW()", + ) + .bind(observation.processor_name.as_str()) + .bind(observation.processor_version.as_str()) + .bind(observation.input_key.as_str()) + .bind(observation.input_hash.as_str()) + .bind(observation.event_key.as_str()) + .bind(observation.signature.as_str()) + .bind(slot) + .bind(observation.instruction_path.as_str()) + .bind(observation.program_id.as_str()) + .bind(observation.protocol_code.as_str()) + .bind(observation.surface_code.as_str()) + .bind(observation.event_code.as_str()) + .bind(observation.event_name.as_str()) + .bind(observation.event_family.as_str()) + .bind(observation.source_kind.as_str()) + .bind(observation.confidence.as_str()) + .bind(observation.proof_kind.as_str()) + .bind(&observation.proof_json) + .bind(&observation.payload_json) + .bind(observation.transaction_failed) + .bind(observation.transaction_error.clone()) + .bind(observation.observation_committed) + .execute(&mut *transaction) + .await; + if let std::result::Result::Err(error) = insert_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decoded observation insert failed: {error}" + ))); + } + } + let coverage_result = crate::queries::decode_pipeline_queries::upsert_coverage_observation( + &mut transaction, + &bundle.coverage, + ) + .await; + if let std::result::Result::Err(error) = coverage_result { + return std::result::Result::Err(error); + } + let materialized_count_result = + crate::queries::decode_pipeline_queries::refresh_materialized_coverage_count( + &mut transaction, + bundle.ledger_identity.processor_name.as_str(), + bundle.ledger_identity.processor_version.as_str(), + bundle.ledger_identity.input_key.as_str(), + ) + .await; + if let std::result::Result::Err(error) = materialized_count_result { + return std::result::Result::Err(error); + } + let lifecycle_state = match bundle.status.as_str() { + "decoded" => "decoded", + "ignored" | "unsupported" => "ignored", + "failed" => "failed", + _unknown => "decoded", + }; + let lifecycle_result = sqlx::query( + "UPDATE kb_sol_core_instructions SET processing_state = CASE WHEN $1 = 'decoded' AND processing_state <> 'materialized' THEN 'decoded' WHEN $1 IN ('ignored', 'failed') AND processing_state IN ('pending', 'failed', 'replay_requested', 'ignored') THEN $1 ELSE processing_state END, processor_name = CASE WHEN $1 = 'decoded' AND processing_state <> 'materialized' THEN $2 WHEN $1 IN ('ignored', 'failed') AND processing_state IN ('pending', 'failed', 'replay_requested', 'ignored') THEN $2 ELSE processor_name END, processor_version = CASE WHEN $1 = 'decoded' AND processing_state <> 'materialized' THEN $3 WHEN $1 IN ('ignored', 'failed') AND processing_state IN ('pending', 'failed', 'replay_requested', 'ignored') THEN $3 ELSE processor_version END, lifecycle_reason = CASE WHEN $1 = 'decoded' AND processing_state <> 'materialized' THEN $4 WHEN $1 IN ('ignored', 'failed') AND processing_state IN ('pending', 'failed', 'replay_requested', 'ignored') THEN $4 ELSE lifecycle_reason END, updated_at = NOW() WHERE signature = $5 AND instruction_path = $6", + ) + .bind(lifecycle_state) + .bind(bundle.ledger_identity.processor_name.as_str()) + .bind(bundle.ledger_identity.processor_version.as_str()) + .bind(bundle.status.as_str()) + .bind(bundle.signature.as_str()) + .bind(bundle.instruction_path.as_str()) + .execute(&mut *transaction) + .await; + if let std::result::Result::Err(error) = lifecycle_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode instruction lifecycle update failed: {error}" + ))); + } + let ledger_status = if bundle.status == "failed" { "failed" } else { "succeeded" }; + let ledger_result = crate::queries::decode_pipeline_queries::upsert_ledger_terminal( + &mut transaction, + &bundle.ledger_identity, + ledger_status, + bundle.error_code.as_deref(), + bundle.error_message.as_deref(), + ) + .await; + if let std::result::Result::Err(error) = ledger_result { + return std::result::Result::Err(error); + } + let commit_result = transaction.commit().await; + if let std::result::Result::Err(error) = commit_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode persistence commit failed: {error}" + ))); + } + let count_result = u64::try_from(bundle.observations.len()); + let count = match count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "decoded observation count conversion failed: {error}" + ))); + }, + }; + let outcome = kb_store_core::InsertOutcome::new(count, 1, 0); + tracing::debug!(target: crate::TRACING_TARGET, action = "persist_decode_result", signature = %bundle.signature, instruction_path = %bundle.instruction_path, processor_name = %bundle.ledger_identity.processor_name, processor_version = %bundle.ledger_identity.processor_version, status = %bundle.status, outcome = ?outcome, committed = true, "PostgreSQL contextual decode result persisted"); + return std::result::Result::Ok(outcome); +} + +pub(crate) async fn mark_decode_failed( + pool: &sqlx::PgPool, + failure: &kb_store_core::DecodeFailure, +) -> kb_core::Result { + tracing::error!(target: crate::TRACING_TARGET, action = "mark_decode_failed", signature = %failure.signature, instruction_path = %failure.instruction_path, processor_name = %failure.ledger_identity.processor_name, processor_version = %failure.ledger_identity.processor_version, input_key = %failure.ledger_identity.input_key, input_hash = %failure.ledger_identity.input_hash, error_code = %failure.error_code, error_message = %failure.error_message, "persist PostgreSQL contextual decode failure"); + if failure.ledger_identity.stage != "instruction_decode" + || failure.signature.trim().is_empty() + || failure.instruction_path.trim().is_empty() + || failure.error_code.trim().is_empty() + || failure.error_message.trim().is_empty() + { + return std::result::Result::Err(kb_core::Error::db("decode failure identity is invalid")); + } + let transaction_result = pool.begin().await; + let mut transaction = match transaction_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode failure transaction failed: {error}" + ))); + }, + }; + let lifecycle_result = sqlx::query( + "UPDATE kb_sol_core_instructions SET processing_state = CASE WHEN processing_state IN ('pending', 'failed', 'replay_requested', 'ignored') THEN 'failed' ELSE processing_state END, processor_name = CASE WHEN processing_state IN ('pending', 'failed', 'replay_requested', 'ignored') THEN $1 ELSE processor_name END, processor_version = CASE WHEN processing_state IN ('pending', 'failed', 'replay_requested', 'ignored') THEN $2 ELSE processor_version END, lifecycle_reason = CASE WHEN processing_state IN ('pending', 'failed', 'replay_requested', 'ignored') THEN $3 ELSE lifecycle_reason END, updated_at = NOW() WHERE signature = $4 AND instruction_path = $5", + ) + .bind(failure.ledger_identity.processor_name.as_str()) + .bind(failure.ledger_identity.processor_version.as_str()) + .bind(failure.error_code.as_str()) + .bind(failure.signature.as_str()) + .bind(failure.instruction_path.as_str()) + .execute(&mut *transaction) + .await; + if let std::result::Result::Err(error) = lifecycle_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode failure lifecycle update failed: {error}" + ))); + } + let ledger_result = crate::queries::decode_pipeline_queries::upsert_ledger_terminal( + &mut transaction, + &failure.ledger_identity, + "failed", + std::option::Option::Some(failure.error_code.as_str()), + std::option::Option::Some(failure.error_message.as_str()), + ) + .await; + if let std::result::Result::Err(error) = ledger_result { + return std::result::Result::Err(error); + } + let commit_result = transaction.commit().await; + if let std::result::Result::Err(error) = commit_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode failure commit failed: {error}" + ))); + } + let outcome = kb_store_core::InsertOutcome::new(0, 1, 0); + tracing::debug!(target: crate::TRACING_TARGET, action = "mark_decode_failed", signature = %failure.signature, instruction_path = %failure.instruction_path, processor_name = %failure.ledger_identity.processor_name, processor_version = %failure.ledger_identity.processor_version, outcome = ?outcome, committed = true, "PostgreSQL contextual decode failure persisted"); + return std::result::Result::Ok(outcome); +} + +pub(crate) async fn persist_materialization_result( + pool: &sqlx::PgPool, + bundle: &kb_store_core::MaterializationPersistenceBundle, + force_replay: bool, +) -> kb_core::Result { + if bundle.status == "failed" { + tracing::error!( + target: crate::TRACING_TARGET, + action = "persist_materialization_failure", + signature = %bundle.signature, + instruction_path = %bundle.instruction_path, + processor_name = %bundle.ledger_identity.processor_name, + processor_version = %bundle.ledger_identity.processor_version, + input_key = %bundle.ledger_identity.input_key, + input_hash = %bundle.ledger_identity.input_hash, + source_decoder_name = %bundle.source_decoder_name, + source_decoder_version = %bundle.source_decoder_version, + error_code = ?bundle.error_code, + error_message = ?bundle.error_message, + "persist PostgreSQL materialization failure" + ); + } + tracing::debug!(target: crate::TRACING_TARGET, action = "persist_materialization_result", signature = %bundle.signature, instruction_path = %bundle.instruction_path, stage = %bundle.ledger_identity.stage, processor_name = %bundle.ledger_identity.processor_name, processor_version = %bundle.ledger_identity.processor_version, input_key = %bundle.ledger_identity.input_key, input_hash = %bundle.ledger_identity.input_hash, source_decoder_name = %bundle.source_decoder_name, source_decoder_version = %bundle.source_decoder_version, source_decode_input_key = %bundle.source_decode_input_key, status = %bundle.status, output_count = bundle.outputs.len(), force_replay, "persist PostgreSQL materialization result"); + let validation_result = bundle.validate(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let transaction_result = pool.begin().await; + let mut transaction = match transaction_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres materialization transaction failed: {error}" + ))); + }, + }; + let delete_result = sqlx::query( + "DELETE FROM kb_sol_mat_events WHERE processor_name = $1 AND processor_version = $2 AND input_key = $3", + ) + .bind(bundle.ledger_identity.processor_name.as_str()) + .bind(bundle.ledger_identity.processor_version.as_str()) + .bind(bundle.ledger_identity.input_key.as_str()) + .execute(&mut *transaction) + .await; + if let std::result::Result::Err(error) = delete_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres materialization output replacement failed: {error}" + ))); + } + for output in &bundle.outputs { + let slot_result = i64::try_from(output.slot); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "materialized output slot conversion failed: {error}" + ))); + }, + }; + let insert_result = sqlx::query( + "INSERT INTO kb_sol_mat_events (processor_name, processor_version, input_key, input_hash, output_key, source_event_key, source_decoder_name, source_decoder_version, source_decode_input_key, signature, slot, materialized_family, payload_jsonb) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13) ON CONFLICT (processor_name, processor_version, input_key, output_key) DO UPDATE SET input_hash = EXCLUDED.input_hash, source_decoder_name = EXCLUDED.source_decoder_name, source_decoder_version = EXCLUDED.source_decoder_version, source_decode_input_key = EXCLUDED.source_decode_input_key, payload_jsonb = EXCLUDED.payload_jsonb, updated_at = NOW()", + ) + .bind(output.processor_name.as_str()) + .bind(output.processor_version.as_str()) + .bind(output.input_key.as_str()) + .bind(output.input_hash.as_str()) + .bind(output.output_key.as_str()) + .bind(output.source_event_key.as_str()) + .bind(bundle.source_decoder_name.as_str()) + .bind(bundle.source_decoder_version.as_str()) + .bind(bundle.source_decode_input_key.as_str()) + .bind(output.signature.as_str()) + .bind(slot) + .bind(output.materialized_family.as_str()) + .bind(&output.payload_json) + .execute(&mut *transaction) + .await; + if let std::result::Result::Err(error) = insert_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres materialized output insert failed: {error}" + ))); + } + } + let ledger_status = if bundle.status == "failed" { "failed" } else { "succeeded" }; + let ledger_result = crate::queries::decode_pipeline_queries::upsert_ledger_terminal( + &mut transaction, + &bundle.ledger_identity, + ledger_status, + bundle.error_code.as_deref(), + bundle.error_message.as_deref(), + ) + .await; + if let std::result::Result::Err(error) = ledger_result { + return std::result::Result::Err(error); + } + if !bundle.outputs.is_empty() { + let lifecycle_result = sqlx::query( + "UPDATE kb_sol_core_instructions SET processing_state = 'materialized', processor_name = $1, processor_version = $2, lifecycle_reason = $3, updated_at = NOW() WHERE signature = $4 AND instruction_path = $5", + ) + .bind(bundle.ledger_identity.processor_name.as_str()) + .bind(bundle.ledger_identity.processor_version.as_str()) + .bind(bundle.status.as_str()) + .bind(bundle.signature.as_str()) + .bind(bundle.instruction_path.as_str()) + .execute(&mut *transaction) + .await; + if let std::result::Result::Err(error) = lifecycle_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres materialization lifecycle update failed: {error}" + ))); + } + } + let coverage_result = + crate::queries::decode_pipeline_queries::refresh_materialized_coverage_count( + &mut transaction, + bundle.source_decoder_name.as_str(), + bundle.source_decoder_version.as_str(), + bundle.source_decode_input_key.as_str(), + ) + .await; + if let std::result::Result::Err(error) = coverage_result { + return std::result::Result::Err(error); + } + let commit_result = transaction.commit().await; + if let std::result::Result::Err(error) = commit_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres materialization commit failed: {error}" + ))); + } + let count_result = u64::try_from(bundle.outputs.len()); + let count = match count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "materialized output count conversion failed: {error}" + ))); + }, + }; + let outcome = kb_store_core::InsertOutcome::new(count, 1, 0); + tracing::debug!(target: crate::TRACING_TARGET, action = "persist_materialization_result", signature = %bundle.signature, instruction_path = %bundle.instruction_path, processor_name = %bundle.ledger_identity.processor_name, processor_version = %bundle.ledger_identity.processor_version, status = %bundle.status, outcome = ?outcome, committed = true, "PostgreSQL materialization result persisted"); + return std::result::Result::Ok(outcome); +} + +pub(crate) async fn list_decode_coverage_summary( + pool: &sqlx::PgPool, + processor_name: std::option::Option<&str>, + processor_version: std::option::Option<&str>, + limit: u32, +) -> kb_core::Result> { + tracing::debug!(target: crate::TRACING_TARGET, action = "list_decode_coverage_summary", processor_name = ?processor_name, processor_version = ?processor_version, limit, "query PostgreSQL decode coverage summary"); + if limit == 0 { + return std::result::Result::Err(kb_core::Error::db( + "decode coverage summary limit must be greater than zero", + )); + } + let query_result = sqlx::query( + "WITH declared AS (SELECT processor_name, processor_version, program_id, surface_code, entry_code, COUNT(*)::BIGINT AS declared_count FROM kb_sol_decode_coverage_declarations WHERE ($1::text IS NULL OR processor_name = $1) AND ($2::text IS NULL OR processor_version = $2) GROUP BY processor_name, processor_version, program_id, surface_code, entry_code), observed AS (SELECT processor_name, processor_version, program_id, surface_code, COALESCE(entry_code, 'unknown') AS entry_code, COUNT(*)::BIGINT AS observed_count, COUNT(*) FILTER (WHERE recognized)::BIGINT AS recognized_count, COALESCE(SUM(decoded_count), 0)::BIGINT AS decoded_count, COALESCE(SUM(materialized_count), 0)::BIGINT AS materialized_count, COALESCE(SUM(error_count), 0)::BIGINT AS error_count, COUNT(*) FILTER (WHERE status = 'unsupported' OR entry_code IS NULL)::BIGINT AS unknown_count, COUNT(*) FILTER (WHERE NOT transaction_failed)::BIGINT AS successful_transaction_count, COUNT(*) FILTER (WHERE transaction_failed)::BIGINT AS failed_transaction_count FROM kb_sol_decode_coverage_observations WHERE ($1::text IS NULL OR processor_name = $1) AND ($2::text IS NULL OR processor_version = $2) GROUP BY processor_name, processor_version, program_id, surface_code, COALESCE(entry_code, 'unknown')) SELECT COALESCE(d.processor_name, o.processor_name) AS processor_name, COALESCE(d.processor_version, o.processor_version) AS processor_version, COALESCE(d.program_id, o.program_id) AS program_id, COALESCE(d.surface_code, o.surface_code) AS surface_code, COALESCE(d.entry_code, o.entry_code) AS entry_code, COALESCE(d.declared_count, 0)::BIGINT AS declared_count, COALESCE(o.observed_count, 0)::BIGINT AS observed_count, COALESCE(o.recognized_count, 0)::BIGINT AS recognized_count, COALESCE(o.decoded_count, 0)::BIGINT AS decoded_count, COALESCE(o.materialized_count, 0)::BIGINT AS materialized_count, COALESCE(o.error_count, 0)::BIGINT AS error_count, COALESCE(o.unknown_count, 0)::BIGINT AS unknown_count, COALESCE(o.successful_transaction_count, 0)::BIGINT AS successful_transaction_count, COALESCE(o.failed_transaction_count, 0)::BIGINT AS failed_transaction_count FROM declared d FULL OUTER JOIN observed o ON d.processor_name = o.processor_name AND d.processor_version = o.processor_version AND d.program_id = o.program_id AND COALESCE(d.surface_code, '') = COALESCE(o.surface_code, '') AND d.entry_code = o.entry_code ORDER BY observed_count DESC, processor_name, program_id, entry_code LIMIT $3", + ) + .bind(processor_name) + .bind(processor_version) + .bind(i64::from(limit)) + .fetch_all(pool) + .await; + let rows = match query_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode coverage summary query failed: {error}" + ))); + }, + }; + let mut output = std::vec::Vec::with_capacity(rows.len()); + for row in rows { + let mapped_result = crate::queries::decode_pipeline_queries::map_coverage_summary_row(&row); + match mapped_result { + std::result::Result::Ok(value) => output.push(value), + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + } + tracing::debug!(target: crate::TRACING_TARGET, action = "list_decode_coverage_summary", processor_name = ?processor_name, processor_version = ?processor_version, row_count = output.len(), "PostgreSQL decode coverage summary loaded"); + return std::result::Result::Ok(output); +} + +async fn upsert_coverage_observation( + transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>, + coverage: &kb_store_core::DecodeCoverageObservationInsert, +) -> kb_core::Result<()> { + let slot_result = i64::try_from(coverage.slot); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "decode coverage slot conversion failed: {error}" + ))); + }, + }; + let decoded_count_result = i32::try_from(coverage.decoded_count); + let decoded_count = match decoded_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "decode coverage decoded count conversion failed: {error}" + ))); + }, + }; + let materialized_count_result = i32::try_from(coverage.materialized_count); + let materialized_count = match materialized_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "decode coverage materialized count conversion failed: {error}" + ))); + }, + }; + let error_count_result = i32::try_from(coverage.error_count); + let error_count = match error_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "decode coverage error count conversion failed: {error}" + ))); + }, + }; + let query_result = sqlx::query( + "INSERT INTO kb_sol_decode_coverage_observations (processor_name, processor_version, input_key, input_hash, signature, slot, instruction_path, program_id, surface_code, entry_code, discriminator_hex, status, recognized, decoded_count, materialized_count, error_count, transaction_failed) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17) ON CONFLICT (processor_name, processor_version, input_key) DO UPDATE SET input_hash = EXCLUDED.input_hash, surface_code = EXCLUDED.surface_code, entry_code = EXCLUDED.entry_code, discriminator_hex = EXCLUDED.discriminator_hex, status = EXCLUDED.status, recognized = EXCLUDED.recognized, decoded_count = EXCLUDED.decoded_count, materialized_count = EXCLUDED.materialized_count, error_count = EXCLUDED.error_count, transaction_failed = EXCLUDED.transaction_failed, updated_at = NOW()", + ) + .bind(coverage.processor_name.as_str()) + .bind(coverage.processor_version.as_str()) + .bind(coverage.input_key.as_str()) + .bind(coverage.input_hash.as_str()) + .bind(coverage.signature.as_str()) + .bind(slot) + .bind(coverage.instruction_path.as_str()) + .bind(coverage.program_id.as_str()) + .bind(coverage.surface_code.as_deref()) + .bind(coverage.entry_code.as_deref()) + .bind(coverage.discriminator_hex.as_deref()) + .bind(coverage.status.as_str()) + .bind(coverage.recognized) + .bind(decoded_count) + .bind(materialized_count) + .bind(error_count) + .bind(coverage.transaction_failed) + .execute(&mut **transaction) + .await; + if let std::result::Result::Err(error) = query_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode coverage observation upsert failed: {error}" + ))); + } + return std::result::Result::Ok(()); +} + +async fn refresh_materialized_coverage_count( + transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>, + decoder_name: &str, + decoder_version: &str, + decode_input_key: &str, +) -> kb_core::Result<()> { + let query_result = sqlx::query( + "UPDATE kb_sol_decode_coverage_observations SET materialized_count = (SELECT COUNT(*)::INTEGER FROM kb_sol_mat_events WHERE source_decoder_name = $1 AND source_decoder_version = $2 AND source_decode_input_key = $3), updated_at = NOW() WHERE processor_name = $1 AND processor_version = $2 AND input_key = $3", + ) + .bind(decoder_name) + .bind(decoder_version) + .bind(decode_input_key) + .execute(&mut **transaction) + .await; + if let std::result::Result::Err(error) = query_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres materialization coverage refresh failed: {error}" + ))); + } + return std::result::Result::Ok(()); +} + +async fn upsert_ledger_terminal( + transaction: &mut sqlx::Transaction<'_, sqlx::Postgres>, + identity: &kb_store_core::ProcessingLedgerIdentity, + status: &str, + error_code: std::option::Option<&str>, + error_message: std::option::Option<&str>, +) -> kb_core::Result<()> { + let query_result = sqlx::query( + "INSERT INTO kb_sol_ops_processing_ledger (stage, processor_name, processor_version, input_key, input_hash, status, attempt_count, started_at, finished_at, error_code, error_message) VALUES ($1, $2, $3, $4, $5, $6, 1, NOW(), NOW(), $7, $8) ON CONFLICT (stage, processor_name, processor_version, input_key) DO UPDATE SET input_hash = EXCLUDED.input_hash, status = EXCLUDED.status, attempt_count = kb_sol_ops_processing_ledger.attempt_count + 1, started_at = NOW(), finished_at = NOW(), error_code = EXCLUDED.error_code, error_message = EXCLUDED.error_message, updated_at = NOW()", + ) + .bind(identity.stage.as_str()) + .bind(identity.processor_name.as_str()) + .bind(identity.processor_version.as_str()) + .bind(identity.input_key.as_str()) + .bind(identity.input_hash.as_str()) + .bind(status) + .bind(error_code) + .bind(error_message) + .execute(&mut **transaction) + .await; + if let std::result::Result::Err(error) = query_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode processing ledger upsert failed: {error}" + ))); + } + return std::result::Result::Ok(()); +} + +fn map_coverage_summary_row( + row: &sqlx::postgres::PgRow, +) -> kb_core::Result { + let processor_name_result = row.try_get::("processor_name"); + let processor_name = match processor_name_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return map_error("processor_name", error), + }; + let processor_version_result = row.try_get::("processor_version"); + let processor_version = match processor_version_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return map_error("processor_version", error), + }; + let program_id_result = row.try_get::("program_id"); + let program_id = match program_id_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return map_error("program_id", error), + }; + let surface_code_result = + row.try_get::, _>("surface_code"); + let surface_code = match surface_code_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return map_error("surface_code", error), + }; + let entry_code_result = row.try_get::("entry_code"); + let entry_code = match entry_code_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return map_error("entry_code", error), + }; + let declared_count_result = + crate::queries::decode_pipeline_queries::read_i64(row, "declared_count"); + let declared_count = match declared_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let observed_count_result = + crate::queries::decode_pipeline_queries::read_i64(row, "observed_count"); + let observed_count = match observed_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let recognized_count_result = + crate::queries::decode_pipeline_queries::read_i64(row, "recognized_count"); + let recognized_count = match recognized_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let decoded_count_result = + crate::queries::decode_pipeline_queries::read_i64(row, "decoded_count"); + let decoded_count = match decoded_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let materialized_count_result = + crate::queries::decode_pipeline_queries::read_i64(row, "materialized_count"); + let materialized_count = match materialized_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let error_count_result = crate::queries::decode_pipeline_queries::read_i64(row, "error_count"); + let error_count = match error_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let unknown_count_result = + crate::queries::decode_pipeline_queries::read_i64(row, "unknown_count"); + let unknown_count = match unknown_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let successful_transaction_count_result = + crate::queries::decode_pipeline_queries::read_i64(row, "successful_transaction_count"); + let successful_transaction_count = match successful_transaction_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let failed_transaction_count_result = + crate::queries::decode_pipeline_queries::read_i64(row, "failed_transaction_count"); + let failed_transaction_count = match failed_transaction_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + return std::result::Result::Ok(kb_store_core::DecodeCoverageSummaryRow { + processor_name, + processor_version, + program_id, + surface_code, + entry_code, + declared_count, + observed_count, + recognized_count, + decoded_count, + materialized_count, + error_count, + unknown_count, + successful_transaction_count, + failed_transaction_count, + }); +} + +fn read_i64(row: &sqlx::postgres::PgRow, column: &str) -> kb_core::Result { + let result = row.try_get::(column); + return match result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => { + crate::queries::decode_pipeline_queries::map_error(column, error) + }, + }; +} + +fn map_error(column: &str, error: sqlx::Error) -> kb_core::Result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres decode coverage column {column} mapping failed: {error}" + ))); +} + +#[cfg(test)] +mod tests { + fn result_or_panic(result: std::result::Result) -> T + where + E: std::fmt::Display, + { + return match result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("postgres decode test operation failed: {error}") + }, + }; + } + + async fn execute_sql(pool: &sqlx::PgPool, statement: &'static str) { + let result = sqlx::query(statement).execute(pool).await; + result_or_panic(result); + } + + fn unique_processor_name(prefix: &str) -> std::string::String { + return format!( + "{}_{}_{}", + prefix, + std::process::id(), + chrono::Utc::now().timestamp_micros() + ); + } + + async fn test_pool_from_env() -> std::option::Option { + let url = match std::env::var("KB_POSTGRES_TEST_URL") { + std::result::Result::Ok(value) if !value.trim().is_empty() => value, + _ => return std::option::Option::None, + }; + let pool_result = sqlx::PgPool::connect(url.as_str()).await; + let pool = result_or_panic(pool_result); + result_or_panic(crate::queries::raw_queries::apply_raw_store_schema(&pool).await); + result_or_panic(crate::queries::core_queries::apply_core_store_schema(&pool).await); + result_or_panic( + crate::queries::decode_pipeline_queries::apply_decode_store_schema(&pool).await, + ); + return std::option::Option::Some(pool); + } + + #[tokio::test] + async fn optional_postgres_coverage_declarations_report_insert_skip_and_update_from_env() { + let _postgres_guard = crate::test_serial::postgres_test_guard().await; + let pool = match test_pool_from_env().await { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let processor_name = unique_processor_name("decode_coverage_outcome_test"); + let mut declaration = kb_store_core::DecodeCoverageDeclarationInsert { + processor_name: processor_name.clone(), + processor_version: "1".to_string(), + program_id: "11111111111111111111111111111111".to_string(), + surface_code: std::option::Option::Some("solana_native_system".to_string()), + entry_kind: "instruction".to_string(), + entry_code: "test".to_string(), + discriminator_hex: std::option::Option::None, + historical: false, + }; + let first = result_or_panic( + crate::queries::decode_pipeline_queries::persist_decode_coverage_declarations( + &pool, + &[declaration.clone()], + ) + .await, + ); + assert_eq!(first, kb_store_core::InsertOutcome::new(1, 0, 0)); + let second = result_or_panic( + crate::queries::decode_pipeline_queries::persist_decode_coverage_declarations( + &pool, + &[declaration.clone()], + ) + .await, + ); + assert_eq!(second, kb_store_core::InsertOutcome::new(0, 0, 1)); + declaration.historical = true; + let third = result_or_panic( + crate::queries::decode_pipeline_queries::persist_decode_coverage_declarations( + &pool, + &[declaration], + ) + .await, + ); + assert_eq!(third, kb_store_core::InsertOutcome::new(0, 1, 0)); + let cleanup_result = sqlx::query( + "DELETE FROM kb_sol_decode_coverage_declarations WHERE processor_name = $1", + ) + .bind(processor_name.as_str()) + .execute(&pool) + .await; + result_or_panic(cleanup_result); + } + + #[tokio::test] + async fn optional_postgres_same_version_and_hash_is_current_from_env() { + let _postgres_guard = crate::test_serial::postgres_test_guard().await; + let pool = match test_pool_from_env().await { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let processor_name = unique_processor_name("decode_current_test"); + let input_key = format!("{}:0", processor_name); + let identity = result_or_panic(kb_store_core::ProcessingLedgerIdentity::new( + "instruction_decode", + processor_name.clone(), + "1", + input_key.clone(), + "stable-input-hash", + )); + let coverage = kb_store_core::DecodeCoverageObservationInsert { + processor_name: processor_name.clone(), + processor_version: "1".to_string(), + input_key: input_key.clone(), + input_hash: "stable-input-hash".to_string(), + signature: processor_name.clone(), + slot: 1, + instruction_path: "0".to_string(), + program_id: "11111111111111111111111111111111".to_string(), + surface_code: std::option::Option::Some("solana_native_system".to_string()), + entry_code: std::option::Option::Some("unknown".to_string()), + discriminator_hex: std::option::Option::None, + status: "unsupported".to_string(), + recognized: true, + decoded_count: 0, + materialized_count: 0, + error_count: 0, + transaction_failed: false, + }; + let bundle = kb_store_core::DecodePersistenceBundle { + ledger_identity: identity.clone(), + signature: processor_name.clone(), + instruction_path: "0".to_string(), + status: "unsupported".to_string(), + error_code: std::option::Option::None, + error_message: std::option::Option::None, + observations: std::vec::Vec::new(), + coverage, + }; + result_or_panic( + crate::queries::decode_pipeline_queries::persist_decode_result(&pool, &bundle, true) + .await, + ); + let current = result_or_panic( + crate::queries::decode_pipeline_queries::is_decode_current(&pool, &identity).await, + ); + assert!(current); + let cleanup_coverage = sqlx::query( + "DELETE FROM kb_sol_decode_coverage_observations WHERE processor_name = $1", + ) + .bind(processor_name.as_str()) + .execute(&pool) + .await; + result_or_panic(cleanup_coverage); + let cleanup_ledger = + sqlx::query("DELETE FROM kb_sol_ops_processing_ledger WHERE processor_name = $1") + .bind(processor_name.as_str()) + .execute(&pool) + .await; + result_or_panic(cleanup_ledger); + } + + #[tokio::test] + async fn optional_postgres_materialized_event_query_is_bounded_and_typed_from_env() { + let _postgres_guard = crate::test_serial::postgres_test_guard().await; + let pool = match test_pool_from_env().await { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let input_key = unique_processor_name("annotation_query_test"); + let insert_result = sqlx::query( + "INSERT INTO kb_sol_mat_events (processor_name, processor_version, input_key, input_hash, output_key, source_event_key, source_decoder_name, source_decoder_version, source_decode_input_key, signature, slot, materialized_family, payload_jsonb) VALUES ('transaction_annotations', '0.4.3', $1, 'hash', 'annotation', 'memo:0', 'spl_memo', '0.4.3', 'decode-input', $2, 42, 'transaction_annotation', $3)", + ) + .bind(input_key.as_str()) + .bind(input_key.as_str()) + .bind(serde_json::json!({"text":"postgres annotation"})) + .execute(&pool) + .await; + result_or_panic(insert_result); + let filter = result_or_panic(kb_store_core::MaterializedEventFilter::new( + std::option::Option::Some("transaction_annotations".to_string()), + std::option::Option::Some("transaction_annotation".to_string()), + std::option::Option::Some(input_key.clone()), + 1, + )); + let rows = result_or_panic( + crate::queries::decode_pipeline_queries::list_materialized_events(&pool, &filter).await, + ); + assert_eq!(rows.len(), 1); + assert_eq!(rows[0].slot, 42); + assert_eq!(rows[0].payload_json["text"], "postgres annotation"); + let cleanup_result = sqlx::query("DELETE FROM kb_sol_mat_events WHERE input_key = $1") + .bind(input_key.as_str()) + .execute(&pool) + .await; + result_or_panic(cleanup_result); + } + + #[tokio::test] + async fn decoded_events_and_ledger_roll_back_together() { + let url = match std::env::var("KB_POSTGRES_TEST_URL") { + std::result::Result::Ok(value) if !value.trim().is_empty() => value, + _ => return, + }; + let _postgres_guard = crate::test_serial::postgres_test_guard().await; + let pool_result = sqlx::PgPool::connect(url.as_str()).await; + let pool = result_or_panic(pool_result); + result_or_panic(crate::queries::raw_queries::apply_raw_store_schema(&pool).await); + result_or_panic(crate::queries::core_queries::apply_core_store_schema(&pool).await); + result_or_panic( + crate::queries::decode_pipeline_queries::apply_decode_store_schema(&pool).await, + ); + execute_sql( + &pool, + "DELETE FROM kb_sol_decode_events WHERE processor_name = 'decode_atomic_rollback_test'", + ) + .await; + execute_sql( + &pool, + "DELETE FROM kb_sol_decode_coverage_observations WHERE processor_name = 'decode_atomic_rollback_test'", + ) + .await; + execute_sql( + &pool, + "DELETE FROM kb_sol_ops_processing_ledger WHERE processor_name = 'decode_atomic_rollback_test'", + ) + .await; + execute_sql( + &pool, + "CREATE OR REPLACE FUNCTION kb_test_reject_decode_ledger() RETURNS trigger LANGUAGE plpgsql AS $$ BEGIN IF NEW.processor_name = 'decode_atomic_rollback_test' THEN RAISE EXCEPTION 'forced decode ledger failure'; END IF; RETURN NEW; END; $$", + ) + .await; + execute_sql( + &pool, + "DROP TRIGGER IF EXISTS kb_test_reject_decode_ledger_trigger ON kb_sol_ops_processing_ledger", + ) + .await; + execute_sql( + &pool, + "CREATE TRIGGER kb_test_reject_decode_ledger_trigger BEFORE INSERT OR UPDATE ON kb_sol_ops_processing_ledger FOR EACH ROW EXECUTE FUNCTION kb_test_reject_decode_ledger()", + ) + .await; + let identity_result = kb_store_core::ProcessingLedgerIdentity::new( + "instruction_decode", + "decode_atomic_rollback_test", + "1", + "rollback-signature:0", + "rollback-input-hash", + ); + let identity = result_or_panic(identity_result); + let coverage = kb_store_core::DecodeCoverageObservationInsert { + processor_name: identity.processor_name.clone(), + processor_version: identity.processor_version.clone(), + input_key: identity.input_key.clone(), + input_hash: identity.input_hash.clone(), + signature: "rollback-signature".to_string(), + slot: 1, + instruction_path: "0".to_string(), + program_id: "11111111111111111111111111111111".to_string(), + surface_code: std::option::Option::Some("system_program".to_string()), + entry_code: std::option::Option::Some("test".to_string()), + discriminator_hex: std::option::Option::None, + status: "decoded".to_string(), + recognized: true, + decoded_count: 1, + materialized_count: 0, + error_count: 0, + transaction_failed: false, + }; + let observation = kb_store_core::DecodeObservationInsert { + processor_name: identity.processor_name.clone(), + processor_version: identity.processor_version.clone(), + input_key: identity.input_key.clone(), + input_hash: identity.input_hash.clone(), + event_key: "test".to_string(), + signature: "rollback-signature".to_string(), + slot: 1, + instruction_path: "0".to_string(), + program_id: "11111111111111111111111111111111".to_string(), + protocol_code: "solana_core".to_string(), + surface_code: "system_program".to_string(), + event_code: "test".to_string(), + event_name: "test".to_string(), + event_family: "audit".to_string(), + source_kind: "instruction".to_string(), + confidence: "exact".to_string(), + proof_kind: "exact_layout".to_string(), + proof_json: serde_json::json!({"source": "test"}), + payload_json: serde_json::json!({"value": 1}), + transaction_failed: false, + transaction_error: std::option::Option::None, + observation_committed: true, + }; + let bundle = kb_store_core::DecodePersistenceBundle { + ledger_identity: identity, + signature: "rollback-signature".to_string(), + instruction_path: "0".to_string(), + status: "decoded".to_string(), + error_code: std::option::Option::None, + error_message: std::option::Option::None, + observations: std::vec![observation], + coverage, + }; + let persistence_result = + crate::queries::decode_pipeline_queries::persist_decode_result(&pool, &bundle, true) + .await; + assert!(persistence_result.is_err()); + let event_count_result = sqlx::query_scalar::( + "SELECT COUNT(*) FROM kb_sol_decode_events WHERE processor_name = 'decode_atomic_rollback_test'", + ) + .fetch_one(&pool) + .await; + let event_count = result_or_panic(event_count_result); + let ledger_count_result = sqlx::query_scalar::( + "SELECT COUNT(*) FROM kb_sol_ops_processing_ledger WHERE processor_name = 'decode_atomic_rollback_test'", + ) + .fetch_one(&pool) + .await; + let ledger_count = result_or_panic(ledger_count_result); + assert_eq!(event_count, 0); + assert_eq!(ledger_count, 0); + execute_sql( + &pool, + "DROP TRIGGER IF EXISTS kb_test_reject_decode_ledger_trigger ON kb_sol_ops_processing_ledger", + ) + .await; + execute_sql(&pool, "DROP FUNCTION IF EXISTS kb_test_reject_decode_ledger()").await; + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/health_queries.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/health_queries.rs new file mode 100644 index 0000000..674f2f1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/health_queries.rs @@ -0,0 +1,77 @@ +// file: kb_store_pg/src/queries/health_queries.rs +// version: 2 + +//! PostgreSQL health and diagnostic SQL queries. + +pub(crate) async fn run_health_check(pool: &sqlx::PgPool) -> kb_core::Result<()> { + let query_result = sqlx::query_scalar::("SELECT 1").fetch_one(pool).await; + return match query_result { + std::result::Result::Ok(_value) => std::result::Result::Ok(()), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::db(format!( + "postgres healthcheck failed: {error}" + ))), + }; +} + +pub(crate) async fn load_current_schema( + pool: &sqlx::PgPool, +) -> kb_core::Result { + let query_result = + sqlx::query_scalar::("SELECT current_schema()") + .fetch_one(pool) + .await; + return match query_result { + std::result::Result::Ok(schema) => std::result::Result::Ok(schema), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::db(format!( + "postgres current schema query failed: {error}" + ))), + }; +} + +pub(crate) async fn load_server_version( + pool: &sqlx::PgPool, +) -> kb_core::Result { + let query_result = + sqlx::query_scalar::("SELECT version()") + .fetch_one(pool) + .await; + return match query_result { + std::result::Result::Ok(version) => std::result::Result::Ok(version), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::db(format!( + "postgres version query failed: {error}" + ))), + }; +} + +pub(crate) async fn load_migration_table_name( + pool: &sqlx::PgPool, +) -> kb_core::Result> { + let query_result = + sqlx::query_scalar::>( + "SELECT to_regclass('_sqlx_migrations')::text", + ) + .fetch_one(pool) + .await; + return match query_result { + std::result::Result::Ok(table_name) => std::result::Result::Ok(table_name), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::db(format!( + "postgres migration table query failed: {error}" + ))), + }; +} + +pub(crate) async fn load_latest_migration_version( + pool: &sqlx::PgPool, +) -> kb_core::Result> { + let query_result = sqlx::query_scalar::>( + "SELECT version::text FROM _sqlx_migrations WHERE success = true ORDER BY version DESC LIMIT 1", + ) + .fetch_optional(pool) + .await; + return match query_result { + std::result::Result::Ok(version) => std::result::Result::Ok(version.flatten()), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::db(format!( + "postgres latest migration query failed: {error}" + ))), + }; +} diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/raw_queries.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/raw_queries.rs new file mode 100644 index 0000000..2812a41 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/raw_queries.rs @@ -0,0 +1,503 @@ +// file: kb_store_pg/src/queries/raw_queries.rs +// version: 7 + +//! PostgreSQL canonical transaction and acquisition observation SQL queries. + +pub(crate) async fn apply_raw_store_schema(pool: &sqlx::PgPool) -> kb_core::Result<()> { + let validation_result = crate::validate_raw_store_table_names(); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let transaction_result = pool.begin().await; + let mut transaction = match transaction_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres canonical raw store schema transaction failed: {error}" + ))); + }, + }; + let lock_result = sqlx::query("SELECT pg_advisory_xact_lock($1)") + .bind(crate::migrations::STORE_SCHEMA_ADVISORY_LOCK_ID) + .execute(&mut *transaction) + .await; + if let std::result::Result::Err(error) = lock_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres canonical raw store schema advisory lock failed: {error}" + ))); + } + for statement in crate::migrations::raw_store_schema_statements() { + let execution_result = sqlx::query(statement).execute(&mut *transaction).await; + if let std::result::Result::Err(error) = execution_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres canonical raw store schema statement failed: {error}" + ))); + } + } + let commit_result = transaction.commit().await; + if let std::result::Result::Err(error) = commit_result { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres canonical raw store schema commit failed: {error}" + ))); + } + return std::result::Result::Ok(()); +} + +pub(crate) async fn has_raw_transaction_signature( + pool: &sqlx::PgPool, + signature: &str, +) -> kb_core::Result { + let validation_result = crate::queries::raw_queries::validate_required_text( + signature, + "canonical raw transaction signature must not be empty", + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let query_result = sqlx::query_scalar::( + "SELECT EXISTS(SELECT 1 FROM kb_sol_raw_transactions WHERE signature = $1)", + ) + .bind(signature) + .fetch_one(pool) + .await; + return match query_result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::db(format!( + "postgres canonical transaction signature lookup failed: {error}" + ))), + }; +} + +pub(crate) async fn has_transaction_observation_key( + pool: &sqlx::PgPool, + observation_key: &str, +) -> kb_core::Result { + let validation_result = crate::queries::raw_queries::validate_required_text( + observation_key, + "transaction observation key must not be empty", + ); + if let std::result::Result::Err(error) = validation_result { + return std::result::Result::Err(error); + } + let query_result = sqlx::query_scalar::( + "SELECT EXISTS(SELECT 1 FROM kb_sol_obs_transaction_observations WHERE observation_key = $1)", + ) + .bind(observation_key) + .fetch_one(pool) + .await; + return match query_result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::db(format!( + "postgres transaction observation lookup failed: {error}" + ))), + }; +} + +pub(crate) async fn insert_raw_transaction( + pool: &sqlx::PgPool, + input: &kb_store_core::RawTransactionInsert, +) -> kb_core::Result { + let slot_result = crate::queries::raw_queries::sql_slot_from_u64(input.slot); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let version_result = i32::try_from(input.canonical_format_version); + let canonical_format_version = match version_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "canonical transaction format version does not fit into SQL INTEGER: {error}" + ))); + }, + }; + let query_result = sqlx::query_scalar::( + "INSERT INTO kb_sol_raw_transactions (signature, slot, canonical_json, canonical_json_hash, canonical_format_version, retention_state, processing_state) VALUES ($1, $2, $3, $4, $5, 'full', 'received') ON CONFLICT (signature) DO NOTHING RETURNING id", + ) + .bind(input.signature.as_str()) + .bind(slot) + .bind(&input.canonical_json) + .bind(input.canonical_json_hash.as_deref()) + .bind(canonical_format_version) + .fetch_optional(pool) + .await; + return match query_result { + std::result::Result::Ok(std::option::Option::Some(_id)) => { + std::result::Result::Ok(kb_store_core::InsertOutcome::new(1, 0, 0)) + }, + std::result::Result::Ok(std::option::Option::None) => { + std::result::Result::Ok(kb_store_core::InsertOutcome::new(0, 0, 1)) + }, + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::db(format!( + "postgres canonical transaction insert failed: {error}" + ))), + }; +} + +pub(crate) async fn insert_transaction_observation( + pool: &sqlx::PgPool, + input: &kb_store_core::TransactionObservationInsert, +) -> kb_core::Result { + let slot_result = crate::queries::raw_queries::optional_sql_slot_from_u64(input.slot); + let slot = match slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let payload_size_result = + crate::queries::raw_queries::optional_sql_bigint_from_u64(input.payload_size_bytes); + let payload_size_bytes = match payload_size_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let origin = crate::queries::raw_queries::transaction_observation_origin_to_sql(input.origin); + let status = crate::queries::raw_queries::transaction_observation_status_to_sql(input.status); + let query_result = sqlx::query_scalar::( + "INSERT INTO kb_sol_obs_transaction_observations (raw_transaction_id, observation_key, signature, slot, provider, endpoint_code, protocol, acquisition_method, origin, commitment, capture_session_id, filter_code, detected_at, received_at, normalized_at, payload_size_bytes, source_payload_hash, status, error_code, error_message) VALUES (COALESCE($1, (SELECT id FROM kb_sol_raw_transactions WHERE signature = $3 LIMIT 1)), $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20) ON CONFLICT (observation_key) DO NOTHING RETURNING id", + ) + .bind(input.raw_transaction_id) + .bind(input.observation_key.as_str()) + .bind(input.signature.as_deref()) + .bind(slot) + .bind(input.provider.as_str()) + .bind(input.endpoint_code.as_deref()) + .bind(input.protocol.as_str()) + .bind(input.acquisition_method.as_str()) + .bind(origin) + .bind(input.commitment.as_deref()) + .bind(input.capture_session_id.as_deref()) + .bind(input.filter_code.as_deref()) + .bind(input.detected_at.as_ref()) + .bind(input.received_at) + .bind(input.normalized_at.as_ref()) + .bind(payload_size_bytes) + .bind(input.source_payload_hash.as_deref()) + .bind(status) + .bind(input.error_code.as_deref()) + .bind(input.error_message.as_deref()) + .fetch_optional(pool) + .await; + return match query_result { + std::result::Result::Ok(std::option::Option::Some(_id)) => { + std::result::Result::Ok(kb_store_core::InsertOutcome::new(1, 0, 0)) + }, + std::result::Result::Ok(std::option::Option::None) => { + std::result::Result::Ok(kb_store_core::InsertOutcome::new(0, 0, 1)) + }, + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::db(format!( + "postgres transaction observation insert failed: {error}" + ))), + }; +} + +pub(crate) async fn update_raw_payload_lifecycle( + pool: &sqlx::PgPool, + mark: &kb_store_core::RawPayloadLifecycleMark, +) -> kb_core::Result { + if mark.raw_table_name != crate::RAW_TRANSACTIONS_TABLE_NAME { + return std::result::Result::Err(kb_core::Error::db( + "raw lifecycle table name is not supported by the PostgreSQL canonical raw store", + )); + } + return crate::queries::raw_queries::update_raw_transaction_lifecycle(pool, mark).await; +} + +fn raw_retention_state_to_sql(state: kb_store_core::RawPayloadRetentionState) -> &'static str { + return match state { + kb_store_core::RawPayloadRetentionState::Full => "full", + kb_store_core::RawPayloadRetentionState::Compacted => "compacted", + kb_store_core::RawPayloadRetentionState::Archived => "archived", + kb_store_core::RawPayloadRetentionState::Purged => "purged", + }; +} + +fn raw_processing_state_to_sql(state: kb_store_core::RawPayloadProcessingState) -> &'static str { + return match state { + kb_store_core::RawPayloadProcessingState::Received => "received", + kb_store_core::RawPayloadProcessingState::CoreExtracted => "core_extracted", + kb_store_core::RawPayloadProcessingState::Decoded => "decoded", + kb_store_core::RawPayloadProcessingState::Materialized => "materialized", + kb_store_core::RawPayloadProcessingState::Failed => "failed", + }; +} + +fn transaction_observation_origin_to_sql( + origin: kb_store_core::TransactionObservationOrigin, +) -> &'static str { + return match origin { + kb_store_core::TransactionObservationOrigin::Live => "live", + kb_store_core::TransactionObservationOrigin::Backfill => "backfill", + kb_store_core::TransactionObservationOrigin::Replay => "replay", + kb_store_core::TransactionObservationOrigin::Repair => "repair", + kb_store_core::TransactionObservationOrigin::Migration => "migration", + }; +} + +fn transaction_observation_status_to_sql( + status: kb_store_core::TransactionObservationStatus, +) -> &'static str { + return match status { + kb_store_core::TransactionObservationStatus::Detected => "detected", + kb_store_core::TransactionObservationStatus::Received => "received", + kb_store_core::TransactionObservationStatus::Normalized => "normalized", + kb_store_core::TransactionObservationStatus::Persisted => "persisted", + kb_store_core::TransactionObservationStatus::Failed => "failed", + kb_store_core::TransactionObservationStatus::Missing => "missing", + }; +} + +fn sql_slot_from_u64(slot: u64) -> kb_core::Result { + let conversion_result = i64::try_from(slot); + return match conversion_result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::db(format!( + "Solana slot does not fit into PostgreSQL BIGINT: {error}" + ))), + }; +} + +fn optional_sql_slot_from_u64( + slot: std::option::Option, +) -> kb_core::Result> { + return crate::queries::raw_queries::optional_sql_bigint_from_u64(slot); +} + +fn optional_sql_bigint_from_u64( + value: std::option::Option, +) -> kb_core::Result> { + return match value { + std::option::Option::Some(raw_value) => { + let conversion_result = i64::try_from(raw_value); + match conversion_result { + std::result::Result::Ok(converted) => { + std::result::Result::Ok(std::option::Option::Some(converted)) + }, + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::db( + format!("unsigned value does not fit into PostgreSQL BIGINT: {error}"), + )), + } + }, + std::option::Option::None => std::result::Result::Ok(std::option::Option::None), + }; +} + +fn validate_required_text(value: &str, message: &str) -> kb_core::Result<()> { + if value.trim().is_empty() { + return std::result::Result::Err(kb_core::Error::db(message)); + } + return std::result::Result::Ok(()); +} + +async fn update_raw_transaction_lifecycle( + pool: &sqlx::PgPool, + mark: &kb_store_core::RawPayloadLifecycleMark, +) -> kb_core::Result { + let retention_state = + crate::queries::raw_queries::raw_retention_state_to_sql(mark.retention_state); + let processing_state = + crate::queries::raw_queries::raw_processing_state_to_sql(mark.processing_state); + let query_result = sqlx::query( + "UPDATE kb_sol_raw_transactions SET retention_state = $1, processing_state = $2, lifecycle_reason = $3, updated_at = NOW() WHERE signature = $4", + ) + .bind(retention_state) + .bind(processing_state) + .bind(mark.reason.as_deref()) + .bind(mark.raw_row_key.as_str()) + .execute(pool) + .await; + return crate::queries::raw_queries::outcome_from_update_result( + query_result, + "postgres canonical raw transaction lifecycle update failed", + ); +} + +fn outcome_from_update_result( + query_result: std::result::Result, + error_prefix: &str, +) -> kb_core::Result { + return match query_result { + std::result::Result::Ok(result) => { + let rows_affected = result.rows_affected(); + if rows_affected == 0 { + return std::result::Result::Ok(kb_store_core::InsertOutcome::new(0, 0, 1)); + } + std::result::Result::Ok(kb_store_core::InsertOutcome::new(0, rows_affected, 0)) + }, + std::result::Result::Err(error) => { + std::result::Result::Err(kb_core::Error::db(format!("{error_prefix}: {error}"))) + }, + }; +} + +#[cfg(test)] +mod tests { + #[test] + fn retention_state_serializes_to_lower_snake_case() { + let value = crate::queries::raw_queries::raw_retention_state_to_sql( + kb_store_core::RawPayloadRetentionState::Compacted, + ); + assert_eq!(value, "compacted"); + } + + #[test] + fn processing_state_serializes_to_lower_snake_case() { + let value = crate::queries::raw_queries::raw_processing_state_to_sql( + kb_store_core::RawPayloadProcessingState::CoreExtracted, + ); + assert_eq!(value, "core_extracted"); + } + + #[test] + fn observation_origin_serializes_to_lower_snake_case() { + let value = crate::queries::raw_queries::transaction_observation_origin_to_sql( + kb_store_core::TransactionObservationOrigin::Backfill, + ); + assert_eq!(value, "backfill"); + } + + #[test] + fn observation_status_serializes_to_lower_snake_case() { + let value = crate::queries::raw_queries::transaction_observation_status_to_sql( + kb_store_core::TransactionObservationStatus::Normalized, + ); + assert_eq!(value, "normalized"); + } + + #[test] + fn sql_slot_rejects_values_above_bigint() { + let result = crate::queries::raw_queries::sql_slot_from_u64(u64::MAX); + assert!(result.is_err()); + } + + #[tokio::test] + async fn optional_postgres_canonical_store_roundtrip_from_env() { + let database_url = match std::env::var("KB_POSTGRES_TEST_URL") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => return, + }; + let _postgres_guard = crate::test_serial::postgres_test_guard().await; + let options_result = crate::PostgresStoreOptions::new(database_url, 1, 5000, false); + let options = match options_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected options error: {error}"), + }; + let store_result = crate::PostgresStore::connect(options).await; + let store = match store_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected connect error: {error}"), + }; + let schema_result = store.initialize_raw_store_schema().await; + if let std::result::Result::Err(error) = schema_result { + panic!("unexpected schema error: {error}"); + } + let signature = test_signature(); + let raw_input_result = kb_store_core::RawTransactionInsert::new( + signature.clone(), + 1, + serde_json::json!({"source": "test"}), + 1, + ); + let raw_input = match raw_input_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected raw input error: {error}"), + }; + let first_result = + kb_store_core::RawTransactionStore::insert_raw_transaction(&store, &raw_input).await; + let first_outcome = match first_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected first insert error: {error}"), + }; + assert_eq!(first_outcome.inserted_count, 1); + let duplicate_result = + kb_store_core::RawTransactionStore::insert_raw_transaction(&store, &raw_input).await; + let duplicate_outcome = match duplicate_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected duplicate insert error: {error}"), + }; + assert_eq!(duplicate_outcome.skipped_count, 1); + let has_result = kb_store_core::RawTransactionStore::has_raw_transaction_signature( + &store, + &kb_model::Signature(signature.clone()), + ) + .await; + let has_signature = match has_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected signature lookup error: {error}"), + }; + assert!(has_signature); + let observation_key = std::string::String::from("test:http:") + signature.as_str(); + let observation_result = kb_store_core::TransactionObservationInsert::new( + observation_key.clone(), + "test_provider", + "solana_http", + "getTransaction", + kb_store_core::TransactionObservationOrigin::Backfill, + chrono::Utc::now(), + ); + let observation = match observation_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected observation error: {error}"), + }; + let identity_result = + observation.with_transaction_identity(signature.clone(), std::option::Option::Some(1)); + let observation_with_identity = match identity_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected identity error: {error}"), + }; + let observation_insert_result = + kb_store_core::RawTransactionStore::insert_transaction_observation( + &store, + &observation_with_identity, + ) + .await; + let observation_outcome = match observation_insert_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("unexpected observation insert error: {error}") + }, + }; + assert_eq!(observation_outcome.inserted_count, 1); + let observation_lookup_result = + kb_store_core::RawTransactionStore::has_transaction_observation_key( + &store, + observation_key.as_str(), + ) + .await; + let has_observation = match observation_lookup_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("unexpected observation lookup error: {error}") + }, + }; + assert!(has_observation); + let mark_result = kb_store_core::RawPayloadLifecycleMark::new( + crate::RAW_TRANSACTIONS_TABLE_NAME, + signature, + kb_store_core::RawPayloadRetentionState::Full, + kb_store_core::RawPayloadProcessingState::CoreExtracted, + std::option::Option::Some(std::string::String::from("test extraction")), + ); + let mark = match mark_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected mark error: {error}"), + }; + let mark_outcome_result = + kb_store_core::RawTransactionStore::mark_raw_payload_lifecycle(&store, &mark).await; + let mark_outcome = match mark_outcome_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected lifecycle update error: {error}"), + }; + assert_eq!(mark_outcome.updated_count, 1); + return; + } + + fn test_signature() -> std::string::String { + let now_result = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH); + let duration = match now_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => { + return std::string::String::from("test_signature_fallback"); + }, + }; + return format!("test_signature_{}", duration.as_nanos()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/replay_candidate_queries.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/replay_candidate_queries.rs new file mode 100644 index 0000000..7cea818 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/replay_candidate_queries.rs @@ -0,0 +1,474 @@ +// file: kb_store_pg/src/queries/replay_candidate_queries.rs +// version: 2 + +//! Read-only PostgreSQL queries for replay candidate discovery. + +#[derive(sqlx::FromRow)] +struct ReplayTransactionCandidateRow { + signature: std::string::String, + slot: i64, + raw_processing_state: std::string::String, + retention_state: std::string::String, + has_core_transaction: bool, + transaction_failed: std::option::Option, + ledger_status: std::string::String, + processor_version: std::option::Option, + attempt_count: i32, + outer_instruction_count: i64, + inner_instruction_count: i64, + outer_program_count: i64, + inner_program_count: i64, + updated_at: std::string::String, +} + +#[derive(sqlx::FromRow)] +struct ReplayProgramSummaryRow { + program_id: std::string::String, + transaction_count: i64, + outer_instruction_count: i64, + inner_instruction_count: i64, + log_count: i64, + min_slot: i64, + max_slot: i64, +} + +#[derive(sqlx::FromRow)] +struct ReplayEntitySummaryRow { + entity_kind: std::string::String, + entity_value: std::string::String, + transaction_count: i64, + occurrence_count: i64, + min_slot: i64, + max_slot: i64, +} + +pub(crate) async fn list_replay_transaction_candidates( + pool: &sqlx::PgPool, + filter: &crate::PostgresReplayTransactionFilter, +) -> kb_core::Result> { + let min_slot_result = + crate::queries::replay_candidate_queries::optional_sql_bigint(filter.min_slot); + let min_slot = match min_slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let max_slot_result = + crate::queries::replay_candidate_queries::optional_sql_bigint(filter.max_slot); + let max_slot = match max_slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let entity_kind = match filter.entity_kind { + std::option::Option::Some(value) => std::option::Option::Some(value.as_sql()), + std::option::Option::None => std::option::Option::None, + }; + let sql = if filter.newest_first { + crate::queries::replay_candidate_queries::transaction_candidate_sql_desc() + } else { + crate::queries::replay_candidate_queries::transaction_candidate_sql_asc() + }; + let query_result = sqlx::query_as::< + sqlx::Postgres, + crate::queries::replay_candidate_queries::ReplayTransactionCandidateRow, + >(sql) + .bind(filter.signature_contains.as_deref()) + .bind(min_slot) + .bind(max_slot) + .bind(filter.raw_processing_state.as_deref()) + .bind(filter.ledger_status.as_deref()) + .bind(filter.program_id.as_deref()) + .bind(filter.program_scope.as_sql()) + .bind(entity_kind) + .bind(filter.entity_value.as_deref()) + .bind(i64::from(filter.limit)) + .fetch_all(pool) + .await; + let rows = match query_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres replay transaction candidate query failed: {error}" + ))); + }, + }; + let mut output = std::vec::Vec::with_capacity(rows.len()); + for row in rows { + output.push(crate::PostgresReplayTransactionCandidate { + signature: row.signature, + slot: row.slot, + raw_processing_state: row.raw_processing_state, + retention_state: row.retention_state, + has_core_transaction: row.has_core_transaction, + transaction_failed: row.transaction_failed, + ledger_status: row.ledger_status, + processor_version: row.processor_version, + attempt_count: row.attempt_count, + outer_instruction_count: row.outer_instruction_count, + inner_instruction_count: row.inner_instruction_count, + outer_program_count: row.outer_program_count, + inner_program_count: row.inner_program_count, + updated_at: row.updated_at, + }); + } + return std::result::Result::Ok(output); +} + +pub(crate) async fn list_replay_program_summaries( + pool: &sqlx::PgPool, + filter: &crate::PostgresReplayProgramFilter, +) -> kb_core::Result> { + let query_result = sqlx::query_as::( + r#"WITH occurrences AS ( + SELECT program_id, signature, slot, 'outer'::TEXT AS scope FROM kb_sol_core_instructions + UNION ALL + SELECT program_id, signature, slot, 'inner'::TEXT AS scope FROM kb_sol_core_inner_instructions + UNION ALL + SELECT program_id, signature, slot, 'logs'::TEXT AS scope FROM kb_sol_core_logs WHERE program_id IS NOT NULL + ) + SELECT program_id, + COUNT(DISTINCT signature)::BIGINT AS transaction_count, + COUNT(*) FILTER (WHERE scope = 'outer')::BIGINT AS outer_instruction_count, + COUNT(*) FILTER (WHERE scope = 'inner')::BIGINT AS inner_instruction_count, + COUNT(*) FILTER (WHERE scope = 'logs')::BIGINT AS log_count, + MIN(slot)::BIGINT AS min_slot, + MAX(slot)::BIGINT AS max_slot + FROM occurrences + WHERE ($1::TEXT IS NULL OR program_id ILIKE '%' || $1 || '%') + GROUP BY program_id + ORDER BY transaction_count DESC, program_id ASC + LIMIT $2"#, + ) + .bind(filter.program_id_contains.as_deref()) + .bind(i64::from(filter.limit)) + .fetch_all(pool) + .await; + let rows = match query_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres replay program summary query failed: {error}" + ))); + }, + }; + let mut output = std::vec::Vec::with_capacity(rows.len()); + for row in rows { + output.push(crate::PostgresReplayProgramSummary { + program_id: row.program_id, + transaction_count: row.transaction_count, + outer_instruction_count: row.outer_instruction_count, + inner_instruction_count: row.inner_instruction_count, + log_count: row.log_count, + min_slot: row.min_slot, + max_slot: row.max_slot, + }); + } + return std::result::Result::Ok(output); +} + +pub(crate) async fn list_replay_entity_summaries( + pool: &sqlx::PgPool, + filter: &crate::PostgresReplayEntityFilter, +) -> kb_core::Result> { + let entity_kind = filter.entity_kind.as_sql(); + let query_result = sqlx::query_as::< + sqlx::Postgres, + crate::queries::replay_candidate_queries::ReplayEntitySummaryRow, + >( + r#"WITH entities AS ( + SELECT 'mint'::TEXT AS entity_kind, mint AS entity_value, signature, slot + FROM kb_sol_core_balance_changes + WHERE mint IS NOT NULL + UNION ALL + SELECT 'owner'::TEXT AS entity_kind, owner AS entity_value, signature, slot + FROM kb_sol_core_balance_changes + WHERE owner IS NOT NULL + UNION ALL + SELECT 'account_key'::TEXT AS entity_kind, account_key AS entity_value, signature, slot + FROM kb_sol_core_account_keys + ) + SELECT entity_kind, + entity_value, + COUNT(DISTINCT signature)::BIGINT AS transaction_count, + COUNT(*)::BIGINT AS occurrence_count, + MIN(slot)::BIGINT AS min_slot, + MAX(slot)::BIGINT AS max_slot + FROM entities + WHERE entity_kind = $1 + AND ($2::TEXT IS NULL OR entity_value ILIKE '%' || $2 || '%') + GROUP BY entity_kind, entity_value + ORDER BY transaction_count DESC, entity_value ASC + LIMIT $3"#, + ) + .bind(entity_kind) + .bind(filter.entity_value_contains.as_deref()) + .bind(i64::from(filter.limit)) + .fetch_all(pool) + .await; + let rows = match query_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres replay entity summary query failed: {error}" + ))); + }, + }; + let mut output = std::vec::Vec::with_capacity(rows.len()); + for row in rows { + output.push(crate::PostgresReplayEntitySummary { + entity_kind: row.entity_kind, + entity_value: row.entity_value, + transaction_count: row.transaction_count, + occurrence_count: row.occurrence_count, + min_slot: row.min_slot, + max_slot: row.max_slot, + }); + } + return std::result::Result::Ok(output); +} + +fn transaction_candidate_sql_desc() -> &'static str { + return crate::queries::replay_candidate_queries::transaction_candidate_sql("DESC"); +} + +fn transaction_candidate_sql_asc() -> &'static str { + return crate::queries::replay_candidate_queries::transaction_candidate_sql("ASC"); +} + +fn transaction_candidate_sql(order: &str) -> &'static str { + if order == "DESC" { + return r#"SELECT raw.signature, + raw.slot, + raw.processing_state AS raw_processing_state, + raw.retention_state, + (core.id IS NOT NULL) AS has_core_transaction, + core.failed AS transaction_failed, + COALESCE(ledger.status, 'not_started') AS ledger_status, + ledger.processor_version, + COALESCE(ledger.attempt_count, 0)::INTEGER AS attempt_count, + COALESCE(outer_stats.instruction_count, 0)::BIGINT AS outer_instruction_count, + COALESCE(inner_stats.instruction_count, 0)::BIGINT AS inner_instruction_count, + COALESCE(outer_stats.program_count, 0)::BIGINT AS outer_program_count, + COALESCE(inner_stats.program_count, 0)::BIGINT AS inner_program_count, + raw.updated_at::TEXT AS updated_at + FROM kb_sol_raw_transactions raw + LEFT JOIN kb_sol_core_transactions core ON core.signature = raw.signature + LEFT JOIN LATERAL ( + SELECT status, processor_version, attempt_count + FROM kb_sol_ops_processing_ledger + WHERE stage = 'core_extraction' + AND processor_name = 'canonical_to_core' + AND input_key = raw.signature + ORDER BY updated_at DESC, id DESC + LIMIT 1 + ) ledger ON TRUE + LEFT JOIN LATERAL ( + SELECT COUNT(*)::BIGINT AS instruction_count, COUNT(DISTINCT program_id)::BIGINT AS program_count + FROM kb_sol_core_instructions + WHERE signature = raw.signature + ) outer_stats ON TRUE + LEFT JOIN LATERAL ( + SELECT COUNT(*)::BIGINT AS instruction_count, COUNT(DISTINCT program_id)::BIGINT AS program_count + FROM kb_sol_core_inner_instructions + WHERE signature = raw.signature + ) inner_stats ON TRUE + WHERE ($1::TEXT IS NULL OR raw.signature ILIKE '%' || $1 || '%') + AND ($2::BIGINT IS NULL OR raw.slot >= $2) + AND ($3::BIGINT IS NULL OR raw.slot <= $3) + AND ($4::TEXT IS NULL OR raw.processing_state = $4) + AND ($5::TEXT IS NULL OR ($5 = 'not_started' AND ledger.status IS NULL) OR ledger.status = $5) + AND ($6::TEXT IS NULL OR + ($7 = 'any' AND ( + EXISTS (SELECT 1 FROM kb_sol_core_instructions candidate_outer WHERE candidate_outer.signature = raw.signature AND candidate_outer.program_id = $6) OR + EXISTS (SELECT 1 FROM kb_sol_core_inner_instructions candidate_inner WHERE candidate_inner.signature = raw.signature AND candidate_inner.program_id = $6) OR + EXISTS (SELECT 1 FROM kb_sol_core_logs candidate_log WHERE candidate_log.signature = raw.signature AND candidate_log.program_id = $6) + )) OR + ($7 = 'outer' AND EXISTS (SELECT 1 FROM kb_sol_core_instructions candidate_outer WHERE candidate_outer.signature = raw.signature AND candidate_outer.program_id = $6)) OR + ($7 = 'inner' AND EXISTS (SELECT 1 FROM kb_sol_core_inner_instructions candidate_inner WHERE candidate_inner.signature = raw.signature AND candidate_inner.program_id = $6)) OR + ($7 = 'logs' AND EXISTS (SELECT 1 FROM kb_sol_core_logs candidate_log WHERE candidate_log.signature = raw.signature AND candidate_log.program_id = $6))) + AND ($8::TEXT IS NULL OR + ($8 = 'mint' AND EXISTS ( + SELECT 1 FROM kb_sol_core_balance_changes candidate_balance + WHERE candidate_balance.signature = raw.signature + AND candidate_balance.mint = $9 + )) OR + ($8 = 'owner' AND EXISTS ( + SELECT 1 FROM kb_sol_core_balance_changes candidate_balance + WHERE candidate_balance.signature = raw.signature + AND candidate_balance.owner = $9 + )) OR + ($8 = 'account_key' AND EXISTS ( + SELECT 1 FROM kb_sol_core_account_keys candidate_account + WHERE candidate_account.signature = raw.signature + AND candidate_account.account_key = $9 + ))) + ORDER BY raw.slot DESC, raw.signature DESC + LIMIT $10"#; + } + return r#"SELECT raw.signature, + raw.slot, + raw.processing_state AS raw_processing_state, + raw.retention_state, + (core.id IS NOT NULL) AS has_core_transaction, + core.failed AS transaction_failed, + COALESCE(ledger.status, 'not_started') AS ledger_status, + ledger.processor_version, + COALESCE(ledger.attempt_count, 0)::INTEGER AS attempt_count, + COALESCE(outer_stats.instruction_count, 0)::BIGINT AS outer_instruction_count, + COALESCE(inner_stats.instruction_count, 0)::BIGINT AS inner_instruction_count, + COALESCE(outer_stats.program_count, 0)::BIGINT AS outer_program_count, + COALESCE(inner_stats.program_count, 0)::BIGINT AS inner_program_count, + raw.updated_at::TEXT AS updated_at + FROM kb_sol_raw_transactions raw + LEFT JOIN kb_sol_core_transactions core ON core.signature = raw.signature + LEFT JOIN LATERAL ( + SELECT status, processor_version, attempt_count + FROM kb_sol_ops_processing_ledger + WHERE stage = 'core_extraction' + AND processor_name = 'canonical_to_core' + AND input_key = raw.signature + ORDER BY updated_at DESC, id DESC + LIMIT 1 + ) ledger ON TRUE + LEFT JOIN LATERAL ( + SELECT COUNT(*)::BIGINT AS instruction_count, COUNT(DISTINCT program_id)::BIGINT AS program_count + FROM kb_sol_core_instructions + WHERE signature = raw.signature + ) outer_stats ON TRUE + LEFT JOIN LATERAL ( + SELECT COUNT(*)::BIGINT AS instruction_count, COUNT(DISTINCT program_id)::BIGINT AS program_count + FROM kb_sol_core_inner_instructions + WHERE signature = raw.signature + ) inner_stats ON TRUE + WHERE ($1::TEXT IS NULL OR raw.signature ILIKE '%' || $1 || '%') + AND ($2::BIGINT IS NULL OR raw.slot >= $2) + AND ($3::BIGINT IS NULL OR raw.slot <= $3) + AND ($4::TEXT IS NULL OR raw.processing_state = $4) + AND ($5::TEXT IS NULL OR ($5 = 'not_started' AND ledger.status IS NULL) OR ledger.status = $5) + AND ($6::TEXT IS NULL OR + ($7 = 'any' AND ( + EXISTS (SELECT 1 FROM kb_sol_core_instructions candidate_outer WHERE candidate_outer.signature = raw.signature AND candidate_outer.program_id = $6) OR + EXISTS (SELECT 1 FROM kb_sol_core_inner_instructions candidate_inner WHERE candidate_inner.signature = raw.signature AND candidate_inner.program_id = $6) OR + EXISTS (SELECT 1 FROM kb_sol_core_logs candidate_log WHERE candidate_log.signature = raw.signature AND candidate_log.program_id = $6) + )) OR + ($7 = 'outer' AND EXISTS (SELECT 1 FROM kb_sol_core_instructions candidate_outer WHERE candidate_outer.signature = raw.signature AND candidate_outer.program_id = $6)) OR + ($7 = 'inner' AND EXISTS (SELECT 1 FROM kb_sol_core_inner_instructions candidate_inner WHERE candidate_inner.signature = raw.signature AND candidate_inner.program_id = $6)) OR + ($7 = 'logs' AND EXISTS (SELECT 1 FROM kb_sol_core_logs candidate_log WHERE candidate_log.signature = raw.signature AND candidate_log.program_id = $6))) + AND ($8::TEXT IS NULL OR + ($8 = 'mint' AND EXISTS ( + SELECT 1 FROM kb_sol_core_balance_changes candidate_balance + WHERE candidate_balance.signature = raw.signature + AND candidate_balance.mint = $9 + )) OR + ($8 = 'owner' AND EXISTS ( + SELECT 1 FROM kb_sol_core_balance_changes candidate_balance + WHERE candidate_balance.signature = raw.signature + AND candidate_balance.owner = $9 + )) OR + ($8 = 'account_key' AND EXISTS ( + SELECT 1 FROM kb_sol_core_account_keys candidate_account + WHERE candidate_account.signature = raw.signature + AND candidate_account.account_key = $9 + ))) + ORDER BY raw.slot ASC, raw.signature ASC + LIMIT $10"#; +} + +fn optional_sql_bigint( + value: std::option::Option, +) -> kb_core::Result> { + return match value { + std::option::Option::Some(raw_value) => { + let conversion_result = i64::try_from(raw_value); + match conversion_result { + std::result::Result::Ok(converted) => { + std::result::Result::Ok(std::option::Option::Some(converted)) + }, + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::db( + format!("replay candidate slot does not fit into PostgreSQL BIGINT: {error}"), + )), + } + }, + std::option::Option::None => std::result::Result::Ok(std::option::Option::None), + }; +} + +#[cfg(test)] +mod tests { + #[tokio::test] + async fn optional_postgres_replay_candidate_queries_from_env() { + let database_url = match std::env::var("KB_POSTGRES_TEST_URL") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_error) => return, + }; + let _postgres_guard = crate::test_serial::postgres_test_guard().await; + let options_result = crate::PostgresStoreOptions::new(database_url, 1, 5000, false); + let options = match options_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected options error: {error}"), + }; + let store_result = crate::PostgresStore::connect(options).await; + let store = match store_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected connect error: {error}"), + }; + let raw_schema_result = store.initialize_raw_store_schema().await; + if let std::result::Result::Err(error) = raw_schema_result { + panic!("unexpected raw schema error: {error}"); + } + let core_schema_result = store.initialize_core_store_schema().await; + if let std::result::Result::Err(error) = core_schema_result { + panic!("unexpected core schema error: {error}"); + } + let transaction_filter_result = crate::PostgresReplayTransactionFilter::new( + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + crate::PostgresReplayProgramScope::Any, + std::option::Option::None, + std::option::Option::None, + 10, + true, + ); + let transaction_filter = match transaction_filter_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("unexpected transaction filter error: {error}") + }, + }; + let transaction_result = store.replay_transaction_candidates(&transaction_filter).await; + if let std::result::Result::Err(error) = transaction_result { + panic!("unexpected transaction candidate query error: {error}"); + } + let program_filter_result = + crate::PostgresReplayProgramFilter::new(std::option::Option::None, 10); + let program_filter = match program_filter_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected program filter error: {error}"), + }; + let program_result = store.replay_program_summaries(&program_filter).await; + if let std::result::Result::Err(error) = program_result { + panic!("unexpected program summary query error: {error}"); + } + for entity_kind in [ + crate::PostgresReplayEntityKind::Mint, + crate::PostgresReplayEntityKind::Owner, + crate::PostgresReplayEntityKind::AccountKey, + ] { + let entity_filter_result = + crate::PostgresReplayEntityFilter::new(entity_kind, std::option::Option::None, 10); + let entity_filter = match entity_filter_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + panic!("unexpected entity filter error: {error}") + }, + }; + let entity_result = store.replay_entity_summaries(&entity_filter).await; + if let std::result::Result::Err(error) = entity_result { + panic!("unexpected entity summary query error: {error}"); + } + } + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/table_diagnostics_queries.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/table_diagnostics_queries.rs new file mode 100644 index 0000000..78160e3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/queries/table_diagnostics_queries.rs @@ -0,0 +1,194 @@ +// file: kb_store_pg/src/queries/table_diagnostics_queries.rs +// version: 5 + +//! Read-only PostgreSQL diagnostics for known Solana store tables. + +use sqlx::Row; // rust-rules: trait-import + +pub(crate) async fn table_exists(pool: &sqlx::PgPool, table_name: &str) -> kb_core::Result { + let query_result = + sqlx::query_scalar::("SELECT to_regclass($1)::text IS NOT NULL") + .bind(table_name) + .fetch_one(pool) + .await; + return match query_result { + std::result::Result::Ok(value) => std::result::Result::Ok(value), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::db(format!( + "postgres table existence diagnostic failed for '{table_name}': {error}" + ))), + }; +} + +pub(crate) async fn load_table_statistics( + pool: &sqlx::PgPool, + table_name: &str, +) -> kb_core::Result { + return match table_name { + crate::RAW_TRANSACTIONS_TABLE_NAME => { + crate::queries::table_diagnostics_queries::load_table_statistics_from_sql( + pool, + crate::migrations::table_stats_kb_sol_raw_transactions_sql(), + table_name, + ) + .await + }, + crate::TRANSACTION_OBSERVATIONS_TABLE_NAME => { + crate::queries::table_diagnostics_queries::load_table_statistics_from_sql( + pool, + crate::migrations::table_stats_kb_sol_obs_transaction_observations_sql(), + table_name, + ) + .await + }, + crate::CORE_TRANSACTIONS_TABLE_NAME => { + crate::queries::table_diagnostics_queries::load_table_statistics_from_sql( + pool, + crate::migrations::table_stats_kb_sol_core_transactions_sql(), + table_name, + ) + .await + }, + crate::CORE_ACCOUNT_KEYS_TABLE_NAME => { + crate::queries::table_diagnostics_queries::load_table_statistics_from_sql( + pool, + crate::migrations::table_stats_kb_sol_core_account_keys_sql(), + table_name, + ) + .await + }, + crate::CORE_INSTRUCTIONS_TABLE_NAME => { + crate::queries::table_diagnostics_queries::load_table_statistics_from_sql( + pool, + crate::migrations::table_stats_kb_sol_core_instructions_sql(), + table_name, + ) + .await + }, + crate::CORE_INNER_INSTRUCTIONS_TABLE_NAME => { + crate::queries::table_diagnostics_queries::load_table_statistics_from_sql( + pool, + crate::migrations::table_stats_kb_sol_core_inner_instructions_sql(), + table_name, + ) + .await + }, + crate::CORE_LOGS_TABLE_NAME => { + crate::queries::table_diagnostics_queries::load_table_statistics_from_sql( + pool, + crate::migrations::table_stats_kb_sol_core_logs_sql(), + table_name, + ) + .await + }, + crate::CORE_BALANCE_CHANGES_TABLE_NAME => { + crate::queries::table_diagnostics_queries::load_table_statistics_from_sql( + pool, + crate::migrations::table_stats_kb_sol_core_balance_changes_sql(), + table_name, + ) + .await + }, + crate::PROCESSING_LEDGER_TABLE_NAME => { + crate::queries::table_diagnostics_queries::load_table_statistics_from_sql( + pool, + crate::migrations::table_stats_kb_sol_ops_processing_ledger_sql(), + table_name, + ) + .await + }, + crate::DECODE_EVENTS_TABLE_NAME => { + crate::queries::table_diagnostics_queries::load_table_statistics_from_sql( + pool, + crate::migrations::table_stats_kb_sol_decode_events_sql(), + table_name, + ) + .await + }, + crate::DECODE_COVERAGE_DECLARATIONS_TABLE_NAME => { + crate::queries::table_diagnostics_queries::load_table_statistics_from_sql( + pool, + crate::migrations::table_stats_kb_sol_decode_coverage_declarations_sql(), + table_name, + ) + .await + }, + crate::DECODE_COVERAGE_OBSERVATIONS_TABLE_NAME => { + crate::queries::table_diagnostics_queries::load_table_statistics_from_sql( + pool, + crate::migrations::table_stats_kb_sol_decode_coverage_observations_sql(), + table_name, + ) + .await + }, + crate::MATERIALIZED_EVENTS_TABLE_NAME => { + crate::queries::table_diagnostics_queries::load_table_statistics_from_sql( + pool, + crate::migrations::table_stats_kb_sol_mat_events_sql(), + table_name, + ) + .await + }, + _ => std::result::Result::Err(kb_core::Error::db(format!( + "postgres table statistics are not supported for '{table_name}'" + ))), + }; +} + +async fn load_table_statistics_from_sql( + pool: &sqlx::PgPool, + sql: &'static str, + table_name: &str, +) -> kb_core::Result { + let query_result = sqlx::query(sql).fetch_one(pool).await; + let row = match query_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres table statistics failed for '{table_name}': {error}" + ))); + }, + }; + let row_count_result = row.try_get::("row_count"); + let row_count = match row_count_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres table row_count mapping failed for '{table_name}': {error}" + ))); + }, + }; + let min_slot_result = row.try_get::, _>("min_slot"); + let min_slot = match min_slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres table min_slot mapping failed for '{table_name}': {error}" + ))); + }, + }; + let max_slot_result = row.try_get::, _>("max_slot"); + let max_slot = match max_slot_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres table max_slot mapping failed for '{table_name}': {error}" + ))); + }, + }; + let latest_created_at_result = + row.try_get::, _>("latest_created_at"); + let latest_created_at = match latest_created_at_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::db(format!( + "postgres table latest_created_at mapping failed for '{table_name}': {error}" + ))); + }, + }; + return std::result::Result::Ok(crate::PostgresTableStatistics { + row_count, + min_slot, + max_slot, + latest_created_at, + }); +} diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/replay_candidates.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/replay_candidates.rs new file mode 100644 index 0000000..c0920b6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/replay_candidates.rs @@ -0,0 +1,380 @@ +// file: kb_store_pg/src/replay_candidates.rs +// version: 2 + +//! Read-only replay candidate filters and PostgreSQL result rows. + +/// Maximum number of rows returned by one replay candidate query. +pub const MAX_REPLAY_CANDIDATE_ROWS: u32 = 5_000; + +/// Program occurrence scope used while filtering replay candidates. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum PostgresReplayProgramScope { + /// Match outer instructions, inner instructions or reliably linked logs. + Any, + /// Match only top-level instructions. + Outer, + /// Match only inner instructions. + Inner, + /// Match only logs with a reliably linked program id. + Logs, +} + +impl PostgresReplayProgramScope { + /// Returns the stable SQL code for this scope. + pub fn as_sql(self) -> &'static str { + return match self { + Self::Any => "any", + Self::Outer => "outer", + Self::Inner => "inner", + Self::Logs => "logs", + }; + } +} + +/// Core entity kind used while filtering replay candidates. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum PostgresReplayEntityKind { + /// SPL or Token-2022 mint address. + Mint, + /// Token account owner address. + Owner, + /// Native or token account address. + AccountKey, +} + +impl PostgresReplayEntityKind { + /// Returns the stable SQL code for this entity kind. + pub fn as_sql(self) -> &'static str { + return match self { + Self::Mint => "mint", + Self::Owner => "owner", + Self::AccountKey => "account_key", + }; + } +} + +/// Bounded read-only filter for transaction replay candidates. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct PostgresReplayTransactionFilter { + /// Optional partial signature search. + pub signature_contains: std::option::Option, + /// Optional inclusive minimum slot. + pub min_slot: std::option::Option, + /// Optional inclusive maximum slot. + pub max_slot: std::option::Option, + /// Optional raw processing state. + pub raw_processing_state: std::option::Option, + /// Optional latest core extraction ledger status, including `not_started`. + pub ledger_status: std::option::Option, + /// Optional exact program id. + pub program_id: std::option::Option, + /// Program occurrence scope. + pub program_scope: crate::PostgresReplayProgramScope, + /// Optional core entity kind. + pub entity_kind: std::option::Option, + /// Optional exact entity value. + pub entity_value: std::option::Option, + /// Maximum returned rows. + pub limit: u32, + /// Orders newest slots first when true. + pub newest_first: bool, +} + +impl PostgresReplayTransactionFilter { + /// Creates and validates a bounded transaction candidate filter. + #[allow(clippy::too_many_arguments)] + pub fn new( + signature_contains: std::option::Option, + min_slot: std::option::Option, + max_slot: std::option::Option, + raw_processing_state: std::option::Option, + ledger_status: std::option::Option, + program_id: std::option::Option, + program_scope: crate::PostgresReplayProgramScope, + entity_kind: std::option::Option, + entity_value: std::option::Option, + limit: u32, + newest_first: bool, + ) -> kb_core::Result { + let signature_contains_value = trim_optional_text(signature_contains); + let raw_processing_state_value = trim_optional_text(raw_processing_state); + let ledger_status_value = trim_optional_text(ledger_status); + let program_id_value = trim_optional_text(program_id); + let entity_value_value = trim_optional_text(entity_value); + let slot_result = validate_slot_range(min_slot, max_slot); + if let std::result::Result::Err(error) = slot_result { + return std::result::Result::Err(error); + } + let limit_result = validate_limit(limit); + if let std::result::Result::Err(error) = limit_result { + return std::result::Result::Err(error); + } + let raw_state_result = validate_optional_code( + raw_processing_state_value.as_deref(), + &["received", "core_extracted", "decoded", "materialized", "failed"], + "raw processing state", + ); + if let std::result::Result::Err(error) = raw_state_result { + return std::result::Result::Err(error); + } + let ledger_status_result = validate_optional_code( + ledger_status_value.as_deref(), + &["not_started", "running", "succeeded", "failed"], + "ledger status", + ); + if let std::result::Result::Err(error) = ledger_status_result { + return std::result::Result::Err(error); + } + if entity_kind.is_some() != entity_value_value.is_some() { + return std::result::Result::Err(kb_core::Error::db( + "replay entity kind and value must either both be present or both be absent", + )); + } + return std::result::Result::Ok(Self { + signature_contains: signature_contains_value, + min_slot, + max_slot, + raw_processing_state: raw_processing_state_value, + ledger_status: ledger_status_value, + program_id: program_id_value, + program_scope, + entity_kind, + entity_value: entity_value_value, + limit, + newest_first, + }); + } +} + +/// One raw transaction candidate enriched with core and ledger diagnostics. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct PostgresReplayTransactionCandidate { + /// Canonical transaction signature. + pub signature: std::string::String, + /// Transaction slot. + pub slot: i64, + /// Current raw processing state. + pub raw_processing_state: std::string::String, + /// Current raw retention state. + pub retention_state: std::string::String, + /// Whether a core transaction row exists. + pub has_core_transaction: bool, + /// Core transaction failure flag when a core row exists. + pub transaction_failed: std::option::Option, + /// Latest core extraction ledger status or `not_started`. + pub ledger_status: std::string::String, + /// Latest core extraction processor version when available. + pub processor_version: std::option::Option, + /// Latest core extraction attempt count. + pub attempt_count: i32, + /// Number of top-level instructions. + pub outer_instruction_count: i64, + /// Number of inner instructions. + pub inner_instruction_count: i64, + /// Number of distinct top-level programs. + pub outer_program_count: i64, + /// Number of distinct inner programs. + pub inner_program_count: i64, + /// Raw row update timestamp rendered by PostgreSQL. + pub updated_at: std::string::String, +} + +/// Bounded read-only filter for program summaries. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct PostgresReplayProgramFilter { + /// Optional partial program id search. + pub program_id_contains: std::option::Option, + /// Maximum returned rows. + pub limit: u32, +} + +impl PostgresReplayProgramFilter { + /// Creates and validates a bounded program summary filter. + pub fn new( + program_id_contains: std::option::Option, + limit: u32, + ) -> kb_core::Result { + let limit_result = validate_limit(limit); + if let std::result::Result::Err(error) = limit_result { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(Self { + program_id_contains: trim_optional_text(program_id_contains), + limit, + }); + } +} + +/// Aggregated program occurrences across outer, inner and linked logs. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct PostgresReplayProgramSummary { + /// Program id. + pub program_id: std::string::String, + /// Number of distinct transactions containing the program. + pub transaction_count: i64, + /// Number of top-level instruction occurrences. + pub outer_instruction_count: i64, + /// Number of inner instruction occurrences. + pub inner_instruction_count: i64, + /// Number of reliably linked log occurrences. + pub log_count: i64, + /// Lowest observed slot. + pub min_slot: i64, + /// Highest observed slot. + pub max_slot: i64, +} + +/// Bounded read-only filter for core entity summaries. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct PostgresReplayEntityFilter { + /// Entity kind to aggregate. + pub entity_kind: crate::PostgresReplayEntityKind, + /// Optional partial entity value search. + pub entity_value_contains: std::option::Option, + /// Maximum returned rows. + pub limit: u32, +} + +impl PostgresReplayEntityFilter { + /// Creates and validates a bounded entity summary filter. + pub fn new( + entity_kind: crate::PostgresReplayEntityKind, + entity_value_contains: std::option::Option, + limit: u32, + ) -> kb_core::Result { + let limit_result = validate_limit(limit); + if let std::result::Result::Err(error) = limit_result { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(Self { + entity_kind, + entity_value_contains: trim_optional_text(entity_value_contains), + limit, + }); + } +} + +/// Aggregated mint, owner or account-key occurrences from core tables. +#[derive(Clone, Debug, Eq, PartialEq, serde::Deserialize, serde::Serialize)] +pub struct PostgresReplayEntitySummary { + /// Stable entity kind code. + pub entity_kind: std::string::String, + /// Mint, owner or account-key address. + pub entity_value: std::string::String, + /// Number of distinct transactions containing the entity. + pub transaction_count: i64, + /// Total number of core-table occurrences. + pub occurrence_count: i64, + /// Lowest observed slot. + pub min_slot: i64, + /// Highest observed slot. + pub max_slot: i64, +} + +fn trim_optional_text( + value: std::option::Option, +) -> std::option::Option { + return match value { + std::option::Option::Some(text) => { + let trimmed = text.trim(); + if trimmed.is_empty() { + return std::option::Option::None; + } + std::option::Option::Some(trimmed.to_string()) + }, + std::option::Option::None => std::option::Option::None, + }; +} + +fn validate_slot_range( + min_slot: std::option::Option, + max_slot: std::option::Option, +) -> kb_core::Result<()> { + if let (std::option::Option::Some(minimum), std::option::Option::Some(maximum)) = + (min_slot, max_slot) + { + if minimum > maximum { + return std::result::Result::Err(kb_core::Error::db( + "replay candidate minimum slot must not exceed maximum slot", + )); + } + } + return std::result::Result::Ok(()); +} + +fn validate_limit(limit: u32) -> kb_core::Result<()> { + if limit == 0 || limit > crate::MAX_REPLAY_CANDIDATE_ROWS { + return std::result::Result::Err(kb_core::Error::db(format!( + "replay candidate limit must be between 1 and {}", + crate::MAX_REPLAY_CANDIDATE_ROWS + ))); + } + return std::result::Result::Ok(()); +} + +fn validate_optional_code( + value: std::option::Option<&str>, + allowed: &[&str], + label: &str, +) -> kb_core::Result<()> { + let selected = match value { + std::option::Option::Some(code) => code, + std::option::Option::None => return std::result::Result::Ok(()), + }; + for allowed_code in allowed { + if selected == *allowed_code { + return std::result::Result::Ok(()); + } + } + return std::result::Result::Err(kb_core::Error::db(format!( + "unsupported {label}: {selected}" + ))); +} + +#[cfg(test)] +mod tests { + #[test] + fn transaction_filter_rejects_inverted_slots() { + let result = crate::PostgresReplayTransactionFilter::new( + std::option::Option::None, + std::option::Option::Some(20), + std::option::Option::Some(10), + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + crate::PostgresReplayProgramScope::Any, + std::option::Option::None, + std::option::Option::None, + 100, + true, + ); + assert!(result.is_err()); + } + + #[test] + fn transaction_filter_requires_complete_entity_pair() { + let result = crate::PostgresReplayTransactionFilter::new( + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + crate::PostgresReplayProgramScope::Any, + std::option::Option::Some(crate::PostgresReplayEntityKind::Mint), + std::option::Option::None, + 100, + true, + ); + assert!(result.is_err()); + } + + #[test] + fn program_filter_rejects_limit_above_maximum() { + let result = crate::PostgresReplayProgramFilter::new( + std::option::Option::None, + crate::MAX_REPLAY_CANDIDATE_ROWS + 1, + ); + assert!(result.is_err()); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories.rs new file mode 100644 index 0000000..692db67 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories.rs @@ -0,0 +1,10 @@ +// file: kb_store_pg/src/repositories.rs +// version: 5 + +//! PostgreSQL repository implementations. + +mod core_extraction_repository; +mod core_transaction_repository; +mod decode_pipeline_repository; +mod raw_transaction_repository; +mod store_health_repository; diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/README.md b/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/README.md new file mode 100644 index 0000000..1c6abdb --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/README.md @@ -0,0 +1,17 @@ + + + +# Repositories PostgreSQL + +Ce dossier contient les implémentations PostgreSQL concrètes des traits définis dans `kb_store_core`. + +Les repositories peuvent appeler `queries/`, mais ne doivent pas exposer directement les détails SQL aux applications. + +## Modules actuels + +| Module | Rôle | +|----------------------------------|------------------------------------------------------| +| `store_health_repository.rs` | Implémentation PostgreSQL de `StoreHealthStore`. | +| `raw_transaction_repository.rs` | Implémentation PostgreSQL de `RawTransactionStore`. | +| `core_transaction_repository.rs` | Implémentation PostgreSQL de `CoreTransactionStore`. | + diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/core_extraction_repository.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/core_extraction_repository.rs new file mode 100644 index 0000000..d6c723a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/core_extraction_repository.rs @@ -0,0 +1,53 @@ +// file: kb_store_pg/src/repositories/core_extraction_repository.rs +// version: 2 + +//! PostgreSQL atomic canonical transaction to core extraction repository. + +#[async_trait::async_trait] +impl kb_store_core::CoreExtractionStore for crate::PostgresStore { + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn list_raw_transactions_for_core_extraction( + &self, + filter: &kb_store_core::CoreExtractionSelectionFilter, + ) -> kb_core::Result> { + return crate::queries::list_raw_transactions_for_core_extraction(self.pool(), filter) + .await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn is_core_extraction_current( + &self, + identity: &kb_store_core::ProcessingLedgerIdentity, + ) -> kb_core::Result { + return crate::queries::is_core_extraction_current(self.pool(), identity).await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn persist_core_extraction( + &self, + bundle: &kb_store_core::CoreExtractionBundle, + force_replay: bool, + ) -> kb_core::Result { + return crate::queries::persist_core_extraction(self.pool(), bundle, force_replay).await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn mark_core_extraction_failed( + &self, + failure: &kb_store_core::CoreExtractionFailure, + ) -> kb_core::Result { + return crate::queries::mark_core_extraction_failed(self.pool(), failure).await; + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/core_transaction_repository.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/core_transaction_repository.rs new file mode 100644 index 0000000..8a3f858 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/core_transaction_repository.rs @@ -0,0 +1,118 @@ +// file: kb_store_pg/src/repositories/core_transaction_repository.rs +// version: 2 + +//! PostgreSQL core Solana repository implementation. + +#[async_trait::async_trait] +impl kb_store_core::CoreTransactionStore for crate::PostgresStore { + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn insert_core_transaction( + &self, + input: &kb_store_core::CoreTransactionInsert, + ) -> kb_core::Result { + return crate::queries::insert_core_transaction(self.pool(), input).await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn insert_core_account_keys( + &self, + inputs: &[kb_store_core::CoreAccountKeyInsert], + ) -> kb_core::Result { + return crate::queries::insert_core_account_keys(self.pool(), inputs).await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn insert_core_instructions( + &self, + inputs: &[kb_store_core::CoreInstructionInsert], + ) -> kb_core::Result { + return crate::queries::insert_core_instructions(self.pool(), inputs).await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn insert_core_inner_instructions( + &self, + inputs: &[kb_store_core::CoreInnerInstructionInsert], + ) -> kb_core::Result { + return crate::queries::insert_core_inner_instructions(self.pool(), inputs).await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn insert_core_logs( + &self, + inputs: &[kb_store_core::CoreLogInsert], + ) -> kb_core::Result { + return crate::queries::insert_core_logs(self.pool(), inputs).await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn insert_core_balance_changes( + &self, + inputs: &[kb_store_core::CoreBalanceChangeInsert], + ) -> kb_core::Result { + return crate::queries::insert_core_balance_changes(self.pool(), inputs).await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn list_core_instructions_for_replay( + &self, + filter: &kb_store_core::CoreInstructionReplayFilter, + page_request: &kb_store_core::PageRequest, + ) -> kb_core::Result> { + return crate::queries::list_core_instructions_for_replay( + self.pool(), + filter, + page_request, + ) + .await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn list_core_instruction_replay_inputs( + &self, + filter: &kb_store_core::CoreInstructionReplayFilter, + page_request: &kb_store_core::PageRequest, + ) -> kb_core::Result> { + return crate::queries::list_core_instruction_replay_inputs( + self.pool(), + filter, + page_request, + ) + .await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn mark_core_instruction_lifecycle( + &self, + mark: &kb_store_core::CoreInstructionLifecycleMark, + ) -> kb_core::Result { + return crate::queries::update_core_instruction_lifecycle(self.pool(), mark).await; + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/decode_pipeline_repository.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/decode_pipeline_repository.rs new file mode 100644 index 0000000..fd9bd6d --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/decode_pipeline_repository.rs @@ -0,0 +1,107 @@ +// file: kb_store_pg/src/repositories/decode_pipeline_repository.rs +// version: 3 + +//! PostgreSQL contextual decode and materialization repository. + +#[async_trait::async_trait] +impl kb_store_core::DecodePipelineStore for crate::PostgresStore { + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn list_decode_inputs( + &self, + filter: &kb_store_core::DecodeSelectionFilter, + ) -> kb_core::Result> { + return crate::queries::list_decode_inputs(self.pool(), filter).await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn is_decode_current( + &self, + identity: &kb_store_core::ProcessingLedgerIdentity, + ) -> kb_core::Result { + return crate::queries::is_decode_current(self.pool(), identity).await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn persist_decode_coverage_declarations( + &self, + declarations: &[kb_store_core::DecodeCoverageDeclarationInsert], + ) -> kb_core::Result { + return crate::queries::persist_decode_coverage_declarations(self.pool(), declarations) + .await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn persist_decode_result( + &self, + bundle: &kb_store_core::DecodePersistenceBundle, + force_replay: bool, + ) -> kb_core::Result { + return crate::queries::persist_decode_result(self.pool(), bundle, force_replay).await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn mark_decode_failed( + &self, + failure: &kb_store_core::DecodeFailure, + ) -> kb_core::Result { + return crate::queries::mark_decode_failed(self.pool(), failure).await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn persist_materialization_result( + &self, + bundle: &kb_store_core::MaterializationPersistenceBundle, + force_replay: bool, + ) -> kb_core::Result { + return crate::queries::persist_materialization_result(self.pool(), bundle, force_replay) + .await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn list_decode_coverage_summary( + &self, + processor_name: std::option::Option<&str>, + processor_version: std::option::Option<&str>, + limit: u32, + ) -> kb_core::Result> { + return crate::queries::list_decode_coverage_summary( + self.pool(), + processor_name, + processor_version, + limit, + ) + .await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn list_materialized_events( + &self, + filter: &kb_store_core::MaterializedEventFilter, + ) -> kb_core::Result> { + return crate::queries::list_materialized_events(self.pool(), filter).await; + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/raw_transaction_repository.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/raw_transaction_repository.rs new file mode 100644 index 0000000..ede9936 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/raw_transaction_repository.rs @@ -0,0 +1,63 @@ +// file: kb_store_pg/src/repositories/raw_transaction_repository.rs +// version: 3 + +//! PostgreSQL canonical transaction and acquisition observation repository implementation. + +#[async_trait::async_trait] +impl kb_store_core::RawTransactionStore for crate::PostgresStore { + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn has_raw_transaction_signature( + &self, + signature: &kb_model::Signature, + ) -> kb_core::Result { + return crate::queries::has_raw_transaction_signature(self.pool(), signature.0.as_str()) + .await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn has_transaction_observation_key( + &self, + observation_key: &str, + ) -> kb_core::Result { + return crate::queries::has_transaction_observation_key(self.pool(), observation_key).await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn insert_raw_transaction( + &self, + input: &kb_store_core::RawTransactionInsert, + ) -> kb_core::Result { + return crate::queries::insert_raw_transaction(self.pool(), input).await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn insert_transaction_observation( + &self, + input: &kb_store_core::TransactionObservationInsert, + ) -> kb_core::Result { + return crate::queries::insert_transaction_observation(self.pool(), input).await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn mark_raw_payload_lifecycle( + &self, + mark: &kb_store_core::RawPayloadLifecycleMark, + ) -> kb_core::Result { + return crate::queries::update_raw_payload_lifecycle(self.pool(), mark).await; + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/store_health_repository.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/store_health_repository.rs new file mode 100644 index 0000000..c0f7bca --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/repositories/store_health_repository.rs @@ -0,0 +1,31 @@ +// file: kb_store_pg/src/repositories/store_health_repository.rs +// version: 4 + +//! PostgreSQL store health repository implementation. + +#[async_trait::async_trait] +impl kb_store_core::StoreHealthStore for crate::PostgresStore { + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn backend_descriptor(&self) -> kb_core::Result { + return crate::PostgresStore::backend_descriptor(self).await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn health_snapshot(&self) -> kb_core::Result { + return crate::PostgresStore::health_snapshot(self).await; + } + + #[expect( + clippy::implicit_return, + reason = "async_trait expansion triggers implicit_return on generated async trait methods." + )] + async fn migration_snapshot(&self) -> kb_core::Result { + return crate::PostgresStore::migration_snapshot(self).await; + } +} diff --git a/migration/khadhroony-bot2-reference/kb_store_pg/src/test_serial.rs b/migration/khadhroony-bot2-reference/kb_store_pg/src/test_serial.rs new file mode 100644 index 0000000..d3af622 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_pg/src/test_serial.rs @@ -0,0 +1,15 @@ +// file: kb_store_pg/src/test_serial.rs +// version: 2 + +//! Test-only serialization helpers for optional real PostgreSQL tests. + +static POSTGRES_TEST_MUTEX: std::sync::OnceLock>> = + std::sync::OnceLock::new(); + +/// Acquires the process-local guard shared by optional real PostgreSQL tests. +pub(crate) async fn postgres_test_guard() -> tokio::sync::OwnedMutexGuard<()> { + let mutex = POSTGRES_TEST_MUTEX + .get_or_init(|| return std::sync::Arc::new(tokio::sync::Mutex::new(()))) + .clone(); + return mutex.lock_owned().await; +} diff --git a/migration/khadhroony-bot2-reference/kb_store_sqlite/Cargo.toml b/migration/khadhroony-bot2-reference/kb_store_sqlite/Cargo.toml new file mode 100644 index 0000000..b8375b0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_sqlite/Cargo.toml @@ -0,0 +1,17 @@ +# file: kb_store_sqlite/Cargo.toml +# version: 1 + +[package] +name = "kb_store_sqlite" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_model = { path = "../kb_model" } +kb_store_core = { path = "../kb_store_core" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_store_sqlite/README.md b/migration/khadhroony-bot2-reference/kb_store_sqlite/README.md new file mode 100644 index 0000000..b7d592a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_sqlite/README.md @@ -0,0 +1,17 @@ + + + +# kb_store_sqlite + +Ce crate conserve une compatibilité SQLite limitée pour les tests, imports et corpus legacy. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_store_sqlite/src/lib.rs b/migration/khadhroony-bot2-reference/kb_store_sqlite/src/lib.rs new file mode 100644 index 0000000..056bff8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_sqlite/src/lib.rs @@ -0,0 +1,12 @@ +// file: kb_store_sqlite/src/lib.rs +// version: 1 + +//! SQLite storage implementation boundary for tests and legacy imports. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod store; + +/// Minimal store handle placeholder. +pub use crate::store::SqliteStore; diff --git a/migration/khadhroony-bot2-reference/kb_store_sqlite/src/store.rs b/migration/khadhroony-bot2-reference/kb_store_sqlite/src/store.rs new file mode 100644 index 0000000..108d9b9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_store_sqlite/src/store.rs @@ -0,0 +1,18 @@ +// file: kb_store_sqlite/src/store.rs +// version: 1 + +//! Store implementation scaffold for the `kb_store_sqlite` crate. + +/// Minimal store handle placeholder. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct SqliteStore { + /// Database URL or file path. + pub database_url: std::string::String, +} + +impl SqliteStore { + /// Creates a new store handle. + pub fn new(database_url: impl std::convert::Into) -> Self { + return Self { database_url: database_url.into() }; + } +} diff --git a/migration/khadhroony-bot2-reference/kb_wallet/Cargo.toml b/migration/khadhroony-bot2-reference/kb_wallet/Cargo.toml new file mode 100644 index 0000000..4798c49 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_wallet/Cargo.toml @@ -0,0 +1,24 @@ +# file: kb_wallet/Cargo.toml +# version: 3 + +[package] +name = "kb_wallet" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +serde_json.workspace = true +solana-keypair.workspace = true +solana-signer.workspace = true +tokio.workspace = true +tracing.workspace = true +zeroize.workspace = true + +[dev-dependencies] +tempfile.workspace = true + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_wallet/README.md b/migration/khadhroony-bot2-reference/kb_wallet/README.md new file mode 100644 index 0000000..7ca27a7 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_wallet/README.md @@ -0,0 +1,25 @@ + + + +# kb_wallet + +Ce crate isole les secrets, le stockage local des keypairs et la signature. Les décodeurs, matérialisateurs, exécuteurs et interfaces utilisateur ne doivent jamais lire directement les octets privés. + +## Wallet temporaire persistant + +`TemporaryWalletStore` fournit une première implémentation destinée aux tests d'intégration, aux exécutables de développement et aux campagnes devnet : + +- alias validé et transformé en chemin déterministe `/.json` ; +- création sans écrasement d'un fichier existant ; +- chargement et validation stricte des 64 octets du keypair Solana ; +- format JSON standard sous forme de tableau d'octets ; +- permissions Unix `0700` pour le répertoire et `0600` pour le fichier ; +- copies sérialisées des secrets effacées de la mémoire après utilisation ; +- exposition limitée au public key, au résumé non secret et à l'interface `Signer` ; +- aucune exportation TS-rs du keypair ou de ses octets. + +Le terme « temporaire » décrit son usage de laboratoire et non nécessairement sa durée de vie : lorsque la persistance est activée dans la configuration, le même keypair est rechargé entre deux démarrages afin de pouvoir recevoir un airdrop, signer plusieurs transactions et valider leur historique. + +## Limites + +Cette tranche n'ajoute ni chiffrement par mot de passe, ni coffre système, ni hardware wallet. Ces backends devront implémenter la même frontière de signature sans modifier les exécuteurs. Un wallet temporaire persistant ne doit pas être sélectionné pour un envoi mainnet dans les profils fournis. diff --git a/migration/khadhroony-bot2-reference/kb_wallet/src/constants.rs b/migration/khadhroony-bot2-reference/kb_wallet/src/constants.rs new file mode 100644 index 0000000..80b104a --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_wallet/src/constants.rs @@ -0,0 +1,9 @@ +// file: kb_wallet/src/constants.rs +// version: 1 + +//! Local constants for the `kb_wallet` crate. + +/// Canonical tracing target for this crate. +pub(crate) const TRACING_TARGET: &str = "kb_wallet"; +/// Number of bytes stored by the standard Solana keypair JSON format. +pub(crate) const SOLANA_KEYPAIR_LENGTH: usize = 64; diff --git a/migration/khadhroony-bot2-reference/kb_wallet/src/lib.rs b/migration/khadhroony-bot2-reference/kb_wallet/src/lib.rs new file mode 100644 index 0000000..760c173 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_wallet/src/lib.rs @@ -0,0 +1,26 @@ +// file: kb_wallet/src/lib.rs +// version: 6 + +//! Wallet boundary for local key storage and transaction signing. +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +mod constants; +mod wallet; + +/// Number of bytes stored by the standard Solana keypair JSON format. +pub(crate) use crate::constants::SOLANA_KEYPAIR_LENGTH; +/// Canonical tracing target for this crate. +pub(crate) use crate::constants::TRACING_TARGET; + +/// Solana keypair kept private inside the wallet boundary. +pub use crate::wallet::TemporaryWallet; +/// Filesystem-backed store for development and integration-test wallets. +pub use crate::wallet::TemporaryWalletStore; +/// Validated non-secret wallet alias. +pub use crate::wallet::WalletAlias; +/// Non-secret wallet policy. +pub use crate::wallet::WalletPolicy; +/// Non-secret wallet description. +pub use crate::wallet::WalletSummary; diff --git a/migration/khadhroony-bot2-reference/kb_wallet/src/wallet.rs b/migration/khadhroony-bot2-reference/kb_wallet/src/wallet.rs new file mode 100644 index 0000000..c2e2faf --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_wallet/src/wallet.rs @@ -0,0 +1,545 @@ +// file: kb_wallet/src/wallet.rs +// version: 7 + +//! Local wallet storage and signing primitives. + +use solana_signer::Signer; // rust-rules: trait-import +use zeroize::Zeroize; // rust-rules: trait-import + +/// Validated non-secret wallet alias used as a local filename stem. +#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] +pub struct WalletAlias(std::string::String); + +impl crate::WalletAlias { + /// Parses and validates a wallet alias. + pub fn parse(value: impl std::convert::Into) -> kb_core::Result { + let value = value.into(); + if value.is_empty() || value.len() > 64 { + return std::result::Result::Err(kb_core::Error::new( + "wallet_alias_length_invalid", + "wallet alias length must be between 1 and 64 bytes", + )); + } + if !value.bytes().enumerate().all(|(index, byte)| { + return byte.is_ascii_alphanumeric() || (index > 0 && (byte == b'_' || byte == b'-')); + }) { + return std::result::Result::Err(kb_core::Error::new( + "wallet_alias_invalid", + "wallet alias must start with an ASCII letter or digit and contain only ASCII letters, digits, '_' or '-'", + )); + } + return std::result::Result::Ok(Self(value)); + } + + /// Returns the validated alias text. + pub fn as_str(&self) -> &str { + return self.0.as_str(); + } +} + +/// Non-secret wallet policy used by callers before requesting a signature. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct WalletPolicy { + /// Whether signing is enabled for this wallet. + pub signing_enabled: bool, + /// Optional lamport spend limit enforced by the execution layer. + pub lamport_spend_limit: std::option::Option, +} + +/// Non-secret description of a loaded wallet. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct WalletSummary { + /// Validated local alias. + pub alias: crate::WalletAlias, + /// Base58 Solana public key. + pub public_key: std::string::String, + /// Persistent keypair path when the wallet was loaded from local storage. + pub storage_path: std::option::Option, +} + +/// Solana keypair kept private inside the wallet boundary. +pub struct TemporaryWallet { + alias: crate::WalletAlias, + keypair: solana_keypair::Keypair, + storage_path: std::option::Option, +} + +impl std::fmt::Debug for crate::TemporaryWallet { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + return formatter + .debug_struct("TemporaryWallet") + .field("alias", &self.alias) + .field("public_key", &self.public_key()) + .field("storage_path", &self.storage_path) + .finish(); + } +} + +impl crate::TemporaryWallet { + /// Generates a non-persistent wallet in memory. + pub fn generate(alias: crate::WalletAlias) -> Self { + return Self { + alias, + keypair: solana_keypair::Keypair::new(), + storage_path: std::option::Option::None, + }; + } + + /// Returns a non-secret summary suitable for logs, CLI output or application adapters. + pub fn summary(&self) -> crate::WalletSummary { + return crate::WalletSummary { + alias: self.alias.clone(), + public_key: self.public_key(), + storage_path: self.storage_path.clone(), + }; + } + + /// Returns the wallet public key in base58 form. + pub fn public_key(&self) -> std::string::String { + return self.keypair.pubkey().to_string(); + } + + /// Returns the signer interface without exposing keypair bytes. + pub fn as_signer(&self) -> &dyn solana_signer::Signer { + return &self.keypair; + } + + /// Signs arbitrary message bytes and returns the base58 signature. + pub fn sign_message(&self, message: &[u8]) -> kb_core::Result { + let signature = match self.keypair.try_sign_message(message) { + std::result::Result::Ok(signature) => signature, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "wallet_message_sign_failed", + error.to_string(), + )); + }, + }; + tracing::debug!( + target: crate::TRACING_TARGET, + action = "sign_message", + wallet_alias = self.alias.as_str(), + public_key = %self.keypair.pubkey(), + message_length = message.len(), + "signed message with local wallet" + ); + return std::result::Result::Ok(signature.to_string()); + } +} + +/// Filesystem-backed store for development and integration-test wallets. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct TemporaryWalletStore { + directory: std::path::PathBuf, +} + +impl crate::TemporaryWalletStore { + /// Creates a wallet store rooted at the supplied directory. + pub fn new(directory: impl std::convert::Into) -> kb_core::Result { + let directory = directory.into(); + if directory.as_os_str().is_empty() { + return std::result::Result::Err(kb_core::Error::new( + "wallet_directory_empty", + "wallet directory must not be empty", + )); + } + return std::result::Result::Ok(Self { directory }); + } + + /// Returns the configured wallet directory. + pub fn directory(&self) -> &std::path::Path { + return self.directory.as_path(); + } + + /// Returns the deterministic JSON path for an alias. + pub fn wallet_path(&self, alias: &crate::WalletAlias) -> std::path::PathBuf { + return self.directory.join(format!("{}.json", alias.as_str())); + } + + /// Returns whether a persistent wallet exists for an alias. + pub async fn exists(&self, alias: &crate::WalletAlias) -> kb_core::Result { + let path = self.wallet_path(alias); + return match tokio::fs::try_exists(&path).await { + std::result::Result::Ok(exists) => std::result::Result::Ok(exists), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "wallet_file_exists_check_failed", + format!("{}: {error}", path.display()), + )), + }; + } + + /// Creates and persists a new wallet without overwriting an existing file. + pub async fn create( + &self, + alias: crate::WalletAlias, + ) -> kb_core::Result { + match prepare_wallet_directory(&self.directory).await { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let path = self.wallet_path(&alias); + let keypair = solana_keypair::Keypair::new(); + match write_new_keypair(path.clone(), &keypair).await { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + tracing::info!( + target: crate::TRACING_TARGET, + action = "create_temporary_wallet", + wallet_alias = alias.as_str(), + public_key = %keypair.pubkey(), + wallet_path = %path.display(), + "created persistent temporary wallet" + ); + return std::result::Result::Ok(crate::TemporaryWallet { + alias, + keypair, + storage_path: std::option::Option::Some(path), + }); + } + + /// Loads and validates an existing persistent wallet. + pub async fn load(&self, alias: crate::WalletAlias) -> kb_core::Result { + let path = self.wallet_path(&alias); + let keypair = match read_keypair(&path).await { + std::result::Result::Ok(keypair) => keypair, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + tracing::debug!( + target: crate::TRACING_TARGET, + action = "load_temporary_wallet", + wallet_alias = alias.as_str(), + public_key = %keypair.pubkey(), + wallet_path = %path.display(), + "loaded persistent temporary wallet" + ); + return std::result::Result::Ok(crate::TemporaryWallet { + alias, + keypair, + storage_path: std::option::Option::Some(path), + }); + } + + /// Loads an existing wallet or creates it when absent. + pub async fn load_or_create( + &self, + alias: crate::WalletAlias, + ) -> kb_core::Result { + let exists = match self.exists(&alias).await { + std::result::Result::Ok(exists) => exists, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if exists { + return self.load(alias).await; + } + let create_result = self.create(alias.clone()).await; + return match create_result { + std::result::Result::Ok(wallet) => std::result::Result::Ok(wallet), + std::result::Result::Err(error) if error.code() == "wallet_file_already_exists" => { + return self.load(alias).await; + }, + std::result::Result::Err(error) => std::result::Result::Err(error), + }; + } +} + +async fn prepare_wallet_directory(directory: &std::path::Path) -> kb_core::Result<()> { + match tokio::fs::create_dir_all(directory).await { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "wallet_directory_create_failed", + format!("{}: {error}", directory.display()), + )); + }, + } + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; // rust-rules: trait-import + let permissions = std::fs::Permissions::from_mode(0o700); + match tokio::fs::set_permissions(directory, permissions).await { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "wallet_directory_permissions_failed", + format!("{}: {error}", directory.display()), + )); + }, + } + } + return std::result::Result::Ok(()); +} + +async fn write_new_keypair( + path: std::path::PathBuf, + keypair: &solana_keypair::Keypair, +) -> kb_core::Result<()> { + let mut keypair_bytes = keypair.to_bytes(); + let mut encoded = match serde_json::to_vec(keypair_bytes.as_slice()) { + std::result::Result::Ok(encoded) => encoded, + std::result::Result::Err(error) => { + keypair_bytes.zeroize(); + return std::result::Result::Err(kb_core::Error::new( + "wallet_keypair_serialize_failed", + error.to_string(), + )); + }, + }; + keypair_bytes.zeroize(); + let task_path = path.clone(); + let task_result = tokio::task::spawn_blocking(move || { + use std::io::Write; // rust-rules: trait-import + let mut options = std::fs::OpenOptions::new(); + options.write(true).create_new(true); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; // rust-rules: trait-import + options.mode(0o600); + } + let mut file = match options.open(&task_path) { + std::result::Result::Ok(file) => file, + std::result::Result::Err(error) => { + encoded.zeroize(); + let code = if error.kind() == std::io::ErrorKind::AlreadyExists { + "wallet_file_already_exists" + } else { + "wallet_file_create_failed" + }; + return std::result::Result::Err(kb_core::Error::new( + code, + format!("{}: {error}", task_path.display()), + )); + }, + }; + if let std::result::Result::Err(error) = file.write_all(encoded.as_slice()) { + encoded.zeroize(); + let _ = std::fs::remove_file(&task_path); + return std::result::Result::Err(kb_core::Error::new( + "wallet_file_write_failed", + format!("{}: {error}", task_path.display()), + )); + } + encoded.zeroize(); + if let std::result::Result::Err(error) = file.sync_all() { + let _ = std::fs::remove_file(&task_path); + return std::result::Result::Err(kb_core::Error::new( + "wallet_file_sync_failed", + format!("{}: {error}", task_path.display()), + )); + } + return std::result::Result::Ok(()); + }) + .await; + return match task_result { + std::result::Result::Ok(result) => result, + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "wallet_file_task_failed", + format!("{}: {error}", path.display()), + )), + }; +} + +async fn read_keypair(path: &std::path::Path) -> kb_core::Result { + match validate_wallet_file_metadata(path).await { + std::result::Result::Ok(()) => {}, + std::result::Result::Err(error) => return std::result::Result::Err(error), + } + let mut encoded = match tokio::fs::read(path).await { + std::result::Result::Ok(encoded) => encoded, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "wallet_file_read_failed", + format!("{}: {error}", path.display()), + )); + }, + }; + let parsed_result = serde_json::from_slice::>(encoded.as_slice()); + encoded.zeroize(); + let mut keypair_bytes = match parsed_result { + std::result::Result::Ok(keypair_bytes) => keypair_bytes, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "wallet_keypair_json_invalid", + format!("{}: {error}", path.display()), + )); + }, + }; + if keypair_bytes.len() != crate::SOLANA_KEYPAIR_LENGTH { + let length = keypair_bytes.len(); + keypair_bytes.zeroize(); + return std::result::Result::Err(kb_core::Error::new( + "wallet_keypair_length_invalid", + format!( + "{} contains {length} bytes instead of {}", + path.display(), + crate::SOLANA_KEYPAIR_LENGTH + ), + )); + } + let keypair_result = solana_keypair::Keypair::try_from(keypair_bytes.as_slice()); + keypair_bytes.zeroize(); + return match keypair_result { + std::result::Result::Ok(keypair) => std::result::Result::Ok(keypair), + std::result::Result::Err(error) => std::result::Result::Err(kb_core::Error::new( + "wallet_keypair_invalid", + format!("{}: {error}", path.display()), + )), + }; +} + +async fn validate_wallet_file_metadata(path: &std::path::Path) -> kb_core::Result<()> { + let metadata = match tokio::fs::symlink_metadata(path).await { + std::result::Result::Ok(metadata) => metadata, + std::result::Result::Err(error) => { + return std::result::Result::Err(kb_core::Error::new( + "wallet_file_metadata_failed", + format!("{}: {error}", path.display()), + )); + }, + }; + if metadata.file_type().is_symlink() || !metadata.is_file() { + return std::result::Result::Err(kb_core::Error::new( + "wallet_file_type_invalid", + format!("{} must be a regular file and not a symlink", path.display()), + )); + } + #[cfg(unix)] + { + use std::os::unix::fs::PermissionsExt; // rust-rules: trait-import + let mode = metadata.permissions().mode() & 0o777; + if mode & 0o077 != 0 { + return std::result::Result::Err(kb_core::Error::new( + "wallet_file_permissions_too_open", + format!("{} has mode {mode:o}; expected no group or other access", path.display()), + )); + } + } + return std::result::Result::Ok(()); +} + +#[cfg(test)] +mod tests { + #[test] + fn wallet_alias_rejects_path_traversal_and_invalid_characters() { + for value in ["", ".hidden", "../wallet", "wallet/name", "wallet name"] { + assert!(crate::WalletAlias::parse(value).is_err()); + } + assert!(crate::WalletAlias::parse("devnet-operator_01").is_ok()); + } + + #[test] + fn ephemeral_wallet_signs_without_exposing_secret_bytes() { + let alias = crate::WalletAlias::parse("ephemeral") + .unwrap_or_else(|error| panic!("unexpected alias error: {error}")); + let wallet = crate::TemporaryWallet::generate(alias); + let signature = wallet + .sign_message(b"khadhroony-wallet-test") + .unwrap_or_else(|error| panic!("unexpected signing error: {error}")); + assert!(!signature.is_empty()); + assert_eq!(wallet.public_key(), wallet.as_signer().pubkey().to_string()); + assert!(wallet.summary().storage_path.is_none()); + assert!(!format!("{wallet:?}").contains("secret")); + } + + #[tokio::test] + async fn persistent_wallet_roundtrips_and_preserves_public_key() { + let directory = tempfile::tempdir() + .unwrap_or_else(|error| panic!("temporary directory must exist: {error}")); + let store = crate::TemporaryWalletStore::new(directory.path().join("wallets")) + .unwrap_or_else(|error| panic!("unexpected store error: {error}")); + let alias = crate::WalletAlias::parse("integration-wallet") + .unwrap_or_else(|error| panic!("unexpected alias error: {error}")); + let created = store + .create(alias.clone()) + .await + .unwrap_or_else(|error| panic!("unexpected create error: {error}")); + let loaded = store + .load(alias.clone()) + .await + .unwrap_or_else(|error| panic!("unexpected load error: {error}")); + assert_eq!(created.public_key(), loaded.public_key()); + assert_eq!( + created.summary().storage_path, + std::option::Option::Some(store.wallet_path(&alias)) + ); + let encoded = std::fs::read(store.wallet_path(&alias)) + .unwrap_or_else(|error| panic!("wallet file must be readable: {error}")); + let keypair_bytes = serde_json::from_slice::>(encoded.as_slice()) + .unwrap_or_else(|error| panic!("wallet file must be standard JSON: {error}")); + assert_eq!(keypair_bytes.len(), crate::SOLANA_KEYPAIR_LENGTH); + } + + #[tokio::test] + async fn persistent_wallet_is_never_overwritten() { + let directory = tempfile::tempdir() + .unwrap_or_else(|error| panic!("temporary directory must exist: {error}")); + let store = crate::TemporaryWalletStore::new(directory.path()) + .unwrap_or_else(|error| panic!("unexpected store error: {error}")); + let alias = crate::WalletAlias::parse("existing") + .unwrap_or_else(|error| panic!("unexpected alias error: {error}")); + let first = store + .create(alias.clone()) + .await + .unwrap_or_else(|error| panic!("unexpected create error: {error}")); + let second = store.create(alias.clone()).await; + assert!(second.is_err()); + let loaded = store + .load_or_create(alias) + .await + .unwrap_or_else(|error| panic!("unexpected load-or-create error: {error}")); + assert_eq!(first.public_key(), loaded.public_key()); + } + + #[tokio::test] + async fn corrupted_wallet_file_is_rejected() { + let directory = tempfile::tempdir() + .unwrap_or_else(|error| panic!("temporary directory must exist: {error}")); + let store = crate::TemporaryWalletStore::new(directory.path()) + .unwrap_or_else(|error| panic!("unexpected store error: {error}")); + let alias = crate::WalletAlias::parse("corrupted") + .unwrap_or_else(|error| panic!("unexpected alias error: {error}")); + store + .create(alias.clone()) + .await + .unwrap_or_else(|error| panic!("fixture wallet must be created: {error}")); + std::fs::write(store.wallet_path(&alias), b"[1,2,3]") + .unwrap_or_else(|error| panic!("fixture must be writable: {error}")); + let result = store.load(alias).await; + assert!(result.is_err()); + assert_eq!( + result + .err() + .unwrap_or_else(|| panic!("corrupted wallet must return an error")) + .code(), + "wallet_keypair_length_invalid" + ); + } + + #[cfg(unix)] + #[tokio::test] + async fn persistent_wallet_uses_private_unix_permissions() { + use std::os::unix::fs::PermissionsExt; // rust-rules: trait-import + let directory = tempfile::tempdir() + .unwrap_or_else(|error| panic!("temporary directory must exist: {error}")); + let store = crate::TemporaryWalletStore::new(directory.path().join("private-wallets")) + .unwrap_or_else(|error| panic!("unexpected store error: {error}")); + let alias = crate::WalletAlias::parse("permissions") + .unwrap_or_else(|error| panic!("unexpected alias error: {error}")); + store + .create(alias.clone()) + .await + .unwrap_or_else(|error| panic!("unexpected create error: {error}")); + let directory_mode = std::fs::metadata(store.directory()) + .unwrap_or_else(|error| panic!("directory metadata must exist: {error}")) + .permissions() + .mode() + & 0o777; + let file_mode = std::fs::metadata(store.wallet_path(&alias)) + .unwrap_or_else(|error| panic!("file metadata must exist: {error}")) + .permissions() + .mode() + & 0o777; + assert_eq!(directory_mode, 0o700); + assert_eq!(file_mode, 0o600); + } +} diff --git a/migration/khadhroony-bot2-reference/kb_worker/Cargo.toml b/migration/khadhroony-bot2-reference/kb_worker/Cargo.toml new file mode 100644 index 0000000..2668efd --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_worker/Cargo.toml @@ -0,0 +1,18 @@ +# file: kb_worker/Cargo.toml +# version: 1 + +[package] +name = "kb_worker" +version.workspace = true +edition.workspace = true +license.workspace = true +publish.workspace = true + +[dependencies] +kb_core = { path = "../kb_core" } +kb_logging = { path = "../kb_logging" } +kb_model = { path = "../kb_model" } +kb_pipeline = { path = "../kb_pipeline" } + +[lints] +workspace = true diff --git a/migration/khadhroony-bot2-reference/kb_worker/README.md b/migration/khadhroony-bot2-reference/kb_worker/README.md new file mode 100644 index 0000000..83cd035 --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_worker/README.md @@ -0,0 +1,17 @@ + + + +# kb_worker + +Ce crate contient le futur worker asynchrone chargé des traitements continus. + +## Rôle dans l'écosystème + +Ce module fait partie du découpage strict de Khadhroony Bot2. Il doit conserver des dépendances limitées et ne pas contourner les interfaces communes du workspace. + +## Règles locales + +- Les commentaires de code restent en anglais. +- La documentation Markdown reste en français. +- Les exports publics sont contrôlés depuis `lib.rs` lorsque le crate expose une bibliothèque. +- Les binaires utilisent `main.rs` avec les attributs Rust obligatoires. diff --git a/migration/khadhroony-bot2-reference/kb_worker/src/main.rs b/migration/khadhroony-bot2-reference/kb_worker/src/main.rs new file mode 100644 index 0000000..455efae --- /dev/null +++ b/migration/khadhroony-bot2-reference/kb_worker/src/main.rs @@ -0,0 +1,12 @@ +// file: kb_worker/src/main.rs +// version: 1 + +//! Binary entry point for the `kb_worker` crate. + +#![warn(missing_docs)] +#![deny(unreachable_pub)] +#![forbid(unsafe_code)] + +fn main() { + std::println!("kb_worker skeleton is ready"); +} diff --git a/migration/khadhroony-bot2-reference/prompts/.gitignore b/migration/khadhroony-bot2-reference/prompts/.gitignore new file mode 100644 index 0000000..71e9ff9 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/.gitignore @@ -0,0 +1,3 @@ +# file: prompts/.gitignore +# version: 1 + diff --git a/migration/khadhroony-bot2-reference/prompts/001_version_session_template.md b/migration/khadhroony-bot2-reference/prompts/001_version_session_template.md new file mode 100644 index 0000000..4978000 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/001_version_session_template.md @@ -0,0 +1,60 @@ + + + +# Template de prompt de session + +## Contexte + +Tu travailles sur le workspace `khadhroony-bot2`. Le workspace utilise une architecture modulaire Rust avec séparation stricte entre modèles, RPC, stockage, pipeline, décodeurs, matérialisateurs, exécuteurs, applications et worker. + +## Règles obligatoires + +- Lire `RULES.md` avant toute proposition de modification. +- Ne pas modifier `CHANGELOG.md` avant validation explicite de version. +- Mettre les changements futurs dans `ROADMAP.md`. +- Ne pas utiliser `use` dans le code Rust sauf pour les traits nécessaires. +- Utiliser `crate::` pour les chemins internes à la crate courante. +- Utiliser le chemin complet de crate pour les dépendances externes du workspace. +- Préserver les en-têtes `file:` et `version:`. +- Garder la documentation de code en anglais et la documentation Markdown en français. +- Fournir uniquement un zip delta après le squelette initial. + +## Format de livraison attendu + +- Si le delta modifie la racine du workspace ou plusieurs modules : `khadhroony-bot2_vX.Y.Z-pre.abc.zip`. +- Si le delta modifie un seul module Rust : `kb_modulename_vX.Y.Z-pre.abc.zip`. +- Le zip doit contenir un `delta.md` non versionné. +- `delta.md` doit lister les fichiers ajoutés, modifiés, à supprimer manuellement, et les validations exécutées ou non exécutées. +- Le zip ne doit contenir que les fichiers ajoutés ou modifiés, plus `delta.md`. + +## Objectif de la session + +Décrire ici la version ciblée, le périmètre technique et les résultats attendus. + +## Fichiers à lire en priorité + +- `README.md` +- `RULES.md` +- `ROADMAP.md` +- `docs/ARCHITECTURE.md` +- `docs/DATABASE.md` +- `docs/NOMENCLATURE.md` +- `docs/DECODER_SURFACE_AUDIT.md` +- `docs/DELTA_WORKFLOW.md` + +## Travail demandé + +Décrire ici les fichiers ou crates à modifier, créer ou supprimer. + +## Validations attendues + +- `cargo build` +- `cargo test --workspace` +- `cargo clippy --workspace --all-targets` +- contrôles SQL si une migration ou une table est modifiée. + +## Interdictions + +- Ne pas ajouter de logiques métier dans les crates applicatifs. +- Ne pas fusionner decode et materialize. +- Ne pas promouvoir un décodeur réservé sans `program_id`, corpus et validation locale. diff --git a/migration/khadhroony-bot2-reference/prompts/002_program_registry_and_naming_audit.md b/migration/khadhroony-bot2-reference/prompts/002_program_registry_and_naming_audit.md new file mode 100644 index 0000000..f3ab0a0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/002_program_registry_and_naming_audit.md @@ -0,0 +1,23 @@ + + + +# Prompt de session : audit du registre des programmes + +Objectif : contrôler les `program_id`, les noms sources, les aliases et les crates cibles avant tout renommage physique de dossiers. + +## Instructions + +- Utiliser `registry/program_registry_seed.toml` comme source de départ. +- Vérifier les doublons exacts de `program_id`. +- Vérifier les surfaces qui ont plusieurs noms historiques. +- Ne pas supprimer de crate tant que le `program_id` et la surface ne sont pas confirmés. +- Produire uniquement un delta après validation. +- Respecter la convention `kb_decoder___[_vN]`. + +## Résultat attendu + +- Une liste de renommages sûrs. +- Une liste de crates aliases à conserver temporairement. +- Une liste de surfaces réservées mais non prioritaires. +- Une mise à jour du registre et de la roadmap. + diff --git a/migration/khadhroony-bot2-reference/prompts/003_v0_1_logging_config_profiles.md b/migration/khadhroony-bot2-reference/prompts/003_v0_1_logging_config_profiles.md new file mode 100644 index 0000000..43cfb9d --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/003_v0_1_logging_config_profiles.md @@ -0,0 +1,39 @@ + + + +# Prompt v0.1.x — logging, configuration et profils + +Objectif : finaliser `kb_logging` et `kb_config` avant SQL/RPC/wallet. + +## Phasage cible + +- `0.1.0` : `kb_logging` réel. +- `0.1.1` : `kb_config`, parsing JSON et validation des profils. +- `0.1.2` : liaison `kb_logging` depuis le profil actif. +- `0.1.3` : rôles d'endpoints HTTP/WS et limites par rôle. +- `0.1.4` : démo Tauri configuration/logging. + +## Contraintes + +- Conserver plusieurs profils de configuration avec un seul profil actif. +- Ne pas ajouter `api_key_env_var` : les placeholders de clés restent dans les URLs. +- Couvrir seulement HTTP JSON-RPC et WebSocket Solana standard avant `v1.x`. +- Ne pas implémenter gRPC, Helius enhanced WebSocket, Helius gRPC ou LaserStream dans cette version. +- Router les logs par target, niveau et sortie : console, fichier humain, fichier JSON, fichier erreurs. +- Router les endpoints par rôle et par type de requête. +- Conserver les règles Rust : pas de `use` sauf traits, `crate::` en interne, pas de `?` production, pas de `unwrap`. + +## Format de livraison attendu + +- Fournir un zip delta uniquement. +- Utiliser `khadhroony-bot2_v0.1.X-pre.abc.zip` si le delta touche plusieurs modules ou la racine. +- Utiliser `kb_logging_v0.1.X-pre.abc.zip` ou `kb_config_v0.1.X-pre.abc.zip` si le delta touche un seul module. +- Inclure `delta.md` non versionné dans le zip. + +## Livrables attendus + +- `kb_logging` fonctionnel. +- `kb_config` typé et validé. +- Exemple `config/example.config.json` complet. +- Démo Tauri de chargement de configuration. +- Tests unitaires de parsing/sérialisation. diff --git a/migration/khadhroony-bot2-reference/prompts/004_v0_2_sql_storage_materialization.md b/migration/khadhroony-bot2-reference/prompts/004_v0_2_sql_storage_materialization.md new file mode 100644 index 0000000..400788b --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/004_v0_2_sql_storage_materialization.md @@ -0,0 +1,242 @@ + + + +# Prompt v0.2.x — conventions SQL, stockage PostgreSQL et contrats DB + +Objectif : ouvrir la phase `0.2.x` de `khadhroony-bot2` en cadrant d'abord les conventions de stockage avant d'implémenter les migrations, repositories et diagnostics DB. + +## État de départ attendu + +- `0.1.0` validée : `kb_logging` réel. +- `0.1.1` validée : `kb_config` typé, schéma JSON et profils. +- `0.1.2` validée : liaison `kb_logging` / `kb_config` / `kb_app_demo`. +- `0.1.3` validée : clients HTTP/WS Solana standard, rôles, pools et démos RPC. +- Le commit `v0.1.3` doit être considéré comme base propre. + +## Objectif de `0.2.0` + +`0.2.0` doit d'abord verrouiller les conventions DB. Ne pas commencer directement par un grand schéma SQL. + +Livrables attendus : + +- conventions de nommage PostgreSQL ; +- conventions de tables Solana ; +- conventions `entities/`, `dtos/`, `queries/`, `repositories/` ; +- rôle exact de `kb_store_core` et `kb_store_pg` ; +- premières tables candidates documentées ; +- prompt ou README de suite pour `0.2.1`. + +## Convention PostgreSQL obligatoire + +Ne pas créer de schemas PostgreSQL applicatifs explicites comme `raw`, `core`, `obs`, `decode`, `mat`, `catalog`, `agg`, `ops` ou `wallet`. + +PostgreSQL doit utiliser le schema courant/default du profil, généralement `public`. + +La séparation logique se fait dans le nom de table. + +## Convention de nommage des tables + +Format obligatoire : + +```text +kb_sol__ +``` + +Domaines autorisés au départ : + +```text +raw +core +obs +decode +mat +catalog +agg +ops +wallet +``` + +Exemples de tables candidates : + +```text +kb_sol_raw_rpc_transactions +kb_sol_raw_ws_notifications +kb_sol_core_transactions +kb_sol_core_account_keys +kb_sol_core_instructions +kb_sol_core_inner_instructions +kb_sol_core_logs +kb_sol_core_balance_changes +kb_sol_obs_program_observations +kb_sol_obs_instruction_observations +kb_sol_decode_decoded_events +kb_sol_mat_trade_events +kb_sol_catalog_tokens +kb_sol_catalog_pools +kb_sol_catalog_pairs +kb_sol_ops_processing_ledger +``` + +Interdictions, écrites avec `DOT` pour que les audits textuels simples ne confondent pas documentation et usage réel : + +```text +raw DOT kb_sol_rpc_transactions +core DOT kb_sol_transactions +obs DOT kb_sol_program_observations +decode DOT kb_sol_decoded_events +mat DOT kb_sol_trade_events +catalog DOT kb_sol_tokens +ops DOT kb_sol_processing_ledger +``` + +## Règles SQL générales + +- `id` : clé primaire technique. +- `created_at` : timestamp d'insertion. +- `updated_at` : timestamp de dernière modification si la table est mutable. +- `slot` : `BIGINT` côté SQL, converti explicitement côté Rust si nécessaire. +- `signature` : texte non vide. +- `program_id` : texte non vide quand applicable. +- `raw_json` : JSONB pour payload brut immuable. +- `payload_json` : JSONB pour payload décodé ou matérialisé. +- Index minimaux : signature, slot, program id, created_at selon table. +- Ne pas inventer de contraintes métier trop tôt : commencer strict mais réversible. + +## Layout Rust obligatoire + +Dans `kb_store_core` : + +```text +src/ + lib.rs + pagination.rs + health.rs + db_error.rs + dtos/ + entities/ + repositories/ +``` + +Dans `kb_store_pg` : + +```text +src/ + lib.rs + pg_store.rs + migrations.rs + queries/ + repositories/ +``` + +Règles : + +- `entities/` contient les représentations proches des lignes SQL. +- `dtos/` contient les contrats applicatifs, Tauri ou repository. +- `queries/` contient uniquement SQL, bind et exécution. +- `repositories/` contient les APIs de stockage. +- Ne pas placer de struct métier ou DB dans `queries/`. + +## Crates à auditer avant modification + +- `kb_core` +- `kb_config` +- `kb_store_core` +- `kb_store_pg` +- `kb_app_demo` +- `kb_rpc` seulement si le stockage raw RPC exige un type partagé. + +## Phasage recommandé + +### `0.2.0` + +- conventions storage ; +- conventions tables ; +- conventions Rust store ; +- README des crates store ; +- aucun gros schéma SQL. + +### `0.2.1` + +- contrats `kb_store_core` ; +- types communs ; +- DTO/entities minimaux ; +- traits repository offline. + +### `0.2.2` + +- connexion PostgreSQL dans `kb_store_pg` ; +- pool ; +- healthcheck ; +- stratégie de migrations ; +- tests offline autant que possible. + +### `0.2.3` + +- raw store Solana minimal : + - `kb_sol_raw_rpc_transactions` ; + - `kb_sol_raw_ws_notifications`. + +### `0.2.4` + +- core store Solana minimal : + - transactions ; + - account keys ; + - instructions ; + - inner instructions ; + - logs ; + - balance changes ; + - observations ; + - processing ledger. + +### `0.2.5` + +- fenêtre Tauri diagnostic DB : + - profil actif ; + - backend ; + - DSN masqué ; + - schema courant ; + - tables détectées ; + - migrations ; + - healthcheck. + +## Contraintes Rust à conserver + +- Rust 2024. +- `#![warn(missing_docs)]`. +- `#![deny(unreachable_pub)]`. +- `#![forbid(unsafe_code)]`. +- Pas de `use` sauf traits. +- `crate::` en interne. +- Pas de `?` en production. +- Pas de `unwrap`. +- Pas de `expect`. +- Pas de `anyhow`. +- Pas de `thiserror`. +- Erreurs via `kb_core::Error` et `kb_core::Result`. + +## Contraintes de livraison + +- Fournir un zip delta uniquement. +- Si plusieurs crates ou la racine sont touchées, utiliser : + - `khadhroony-bot2_v0.2.X-pre.abc.zip` +- Si une seule crate est touchée, utiliser : + - `kb_store_core_v0.2.X-pre.abc.zip` + - ou `kb_store_pg_v0.2.X-pre.abc.zip` +- Inclure `delta.md` non versionné dans le zip. +- Ne pas modifier `CHANGELOG.md` avant validation locale du jalon. + +## Validations attendues + +À adapter selon le delta : + +```bash +cargo test -p kb_store_core +cargo test -p kb_store_pg +cargo test -p kb_config +cargo test -p kb_app_demo +cargo clippy --all-targets +cargo tauri dev -c kb_app_demo/tauri.conf.json +``` + +Si aucun code n'est modifié, documenter explicitement que le delta est documentaire. + diff --git a/migration/khadhroony-bot2-reference/prompts/005_v0_2_2_pg_connection_migrations.md b/migration/khadhroony-bot2-reference/prompts/005_v0_2_2_pg_connection_migrations.md new file mode 100644 index 0000000..c1804d3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/005_v0_2_2_pg_connection_migrations.md @@ -0,0 +1,58 @@ + + + +# Prompt de suite — `0.2.2` connexion PostgreSQL et migrations + +Objectif : ouvrir `0.2.2` après validation de `0.2.1`, en ajoutant l'infrastructure PostgreSQL minimale dans `kb_store_pg`, sans créer encore le grand schéma Solana. + +## Base attendue + +- `0.2.0` validée et commitée : conventions PostgreSQL et nommage `kb_sol__`. +- `0.2.1` validée et commitée : contrats `kb_store_core`, DTOs, entities, repositories abstraits, cycle de vie raw, replay par instruction et replay input contextualisé avec account keys, logs et balance changes. + +## Périmètre `0.2.2` + +Ajouter uniquement l'infrastructure PostgreSQL : + +- lecture de la configuration PostgreSQL depuis `kb_config` ; +- création d'un pool PostgreSQL dans `kb_store_pg` ; +- healthcheck minimal ; +- stratégie de migrations ; +- diagnostic du schema courant ; +- tests offline autant que possible. + +Ne pas créer encore les tables Solana finales. Les premières tables réelles restent prévues pour `0.2.3` et `0.2.4`. Les migrations doivent cependant rester compatibles avec les contrats `CoreInstructionReplayInput`, `CoreLogInsert`, `CoreAccountKeyInsert` et `CoreBalanceChangeInsert`. + +## Règle PostgreSQL obligatoire + +Ne pas créer de schemas applicatifs explicites comme `raw`, `core`, `obs`, `decode`, `mat`, `catalog`, `agg`, `ops` ou `wallet`. + +Le store utilise le schema courant du profil PostgreSQL, généralement `public`. + +La séparation logique reste dans le nom de table : + +```text +kb_sol__ +``` + +## Points à respecter + +- `kb_store_core` reste backend-agnostique. +- `kb_store_pg` contient les implémentations PostgreSQL. +- `queries/` contient SQL, bind et exécution. +- `repositories/` contient les repositories concrets. +- Aucune struct métier ou DB ne doit être placée dans `queries/`. +- Pas de `unwrap`, pas de `expect`, pas de `?` en production. +- Erreurs via `kb_core::Error` et `kb_core::Result`. +- `CHANGELOG.md` ne doit être modifié qu'après validation locale. + +## Validations attendues + +```bash +cargo test -p kb_store_core +cargo test -p kb_store_pg +cargo test -p kb_config +cargo test -p kb_app_demo +cargo clippy --all-targets +``` + diff --git a/migration/khadhroony-bot2-reference/prompts/005_v0_3_rpc_ingestion_ws.md b/migration/khadhroony-bot2-reference/prompts/005_v0_3_rpc_ingestion_ws.md new file mode 100644 index 0000000..cae6aa8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/005_v0_3_rpc_ingestion_ws.md @@ -0,0 +1,40 @@ + + + +# Prompt historique — ancien périmètre RPC ingestion déplacé vers v0.4.x + +Objectif historique : ce périmètre est désormais remplacé par le phasage transaction canonique de `0.3.x`. Pour poursuivre la série active, utiliser `prompts/016_v0_3_2_canonical_transaction_contract.md`. + +## À faire + +- Implémenter pool HTTP par rôle. +- Implémenter pool WebSocket standard par rôle. +- Ajouter ingestion par signature. +- Stocker `kb_sol_raw_rpc_transactions`. +- Extraire comptes résolus, instructions, inner instructions, logs et deltas de balances. +- Construire `kb_sol_obs_program_observations`. +- Ajouter listeners `logsSubscribe`, `programSubscribe`, `accountSubscribe`, `slotSubscribe`, `rootSubscribe`. +- Produire des observations brutes sans exécuter d'achat/revente. +- Ajouter une page Tauri de diagnostic des endpoints et listeners. + + +## Rappel DB + +- Ne pas utiliser de schémas PostgreSQL applicatifs explicites. +- Ne pas revenir aux anciennes formes qualifiées écrites ici avec `DOT` : `raw DOT sol_transactions` ou `obs DOT program_observations`. +- Les tables Solana doivent rester au format `kb_sol__`. + +## Hors scope + +- gRPC. +- Helius gRPC. +- LaserStream. +- Matérialisation DEX détaillée. +- Exécution d'achat ou de revente. + +## Format de livraison attendu + +- Fournir un zip delta uniquement. +- Utiliser `khadhroony-bot2_v0.4.X-pre.abc.zip` si plusieurs modules ou fichiers racine sont touchés lorsque ce périmètre sera repris. +- Utiliser `kb_rpc_v0.4.X-pre.abc.zip` si seul `kb_rpc` est touché lorsque ce périmètre sera repris. +- Inclure `delta.md` non versionné dans le zip. diff --git a/migration/khadhroony-bot2-reference/prompts/006_v0_4_wallet_execution_demo.md b/migration/khadhroony-bot2-reference/prompts/006_v0_4_wallet_execution_demo.md new file mode 100644 index 0000000..2c39346 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/006_v0_4_wallet_execution_demo.md @@ -0,0 +1,25 @@ + + + +# Prompt historique — wallet, exécution core et démos devnet déplacés vers v0.5 + +Ce prompt utilisait l’ancien phasage `0.4.x`. Le phasage actuel reporte ce périmètre wallet et sécurité à `0.11.x`, après les décodeurs prioritaires et la validation des sources live. +## Objectif + +Rendre `kb_wallet`, `kb_execution_api`, `kb_execution_safety` et les exécuteurs Solana/SPL testables sur devnet depuis `kb_app_demo`. + +## Travail demandé + +- Charger un wallet local chiffré. +- Afficher l'adresse et les soldes SOL/SPL. +- Simuler un transfert SOL. +- Envoyer un transfert SOL devnet avec confirmation opérateur. +- Préparer un transfert SPL avec ATA contrôlé. +- Bloquer toute exécution mainnet tant que les garde-fous ne sont pas validés. + +## Validations attendues + +- `cargo build` +- démo Tauri wallet +- démo Tauri transfert SOL devnet +- logs de simulation et confirmation diff --git a/migration/khadhroony-bot2-reference/prompts/007_v0_5_core_decoders_listeners.md b/migration/khadhroony-bot2-reference/prompts/007_v0_5_core_decoders_listeners.md new file mode 100644 index 0000000..fccfc69 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/007_v0_5_core_decoders_listeners.md @@ -0,0 +1,23 @@ + + + +# Prompt historique — décodeurs core et premiers listeners déplacés vers v0.6 + +Ce prompt utilisait l’ancien phasage `0.5.x`. Le phasage actuel sépare ce périmètre : décodeurs Core/SPL en `0.4.x`, listeners et trading assisté en `0.12.x`. +## Objectif + +Décoder les programmes Solana/SPL de base et préparer les listeners nécessaires au trading assisté. + +## Travail demandé + +- Décoder system, compute budget, memo, address lookup table. +- Décoder SPL Token, Token-2022, ATA et stake pool si applicable. +- Matérialiser les transferts, mint, burn, close account et ATA. +- Ajouter listeners WebSocket par `program_id` prioritaire. +- Notifier créations de pools, migrations, liquidité et swaps quand les surfaces sont prêtes. + +## Validations attendues + +- `cargo build` +- replay de signatures contenant SPL Token et ATA +- notifications Tauri sur events core diff --git a/migration/khadhroony-bot2-reference/prompts/008_v0_6_priority_dex_router_surfaces.md b/migration/khadhroony-bot2-reference/prompts/008_v0_6_priority_dex_router_surfaces.md new file mode 100644 index 0000000..967b3a1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/008_v0_6_priority_dex_router_surfaces.md @@ -0,0 +1,48 @@ + + + +# Prompt historique — surfaces DEX/router prioritaires déplacées vers v0.8 + +Ce prompt utilisait l’ancien phasage `0.6.x`. Le phasage actuel répartit les surfaces prioritaires de `0.5.x` à `0.9.x` : Pump, Meteora, Raydium, Orca, puis Jupiter et routeurs. +## Objectif + +Développer les premières surfaces prioritaires pour le trading assisté : Pump, Bags Fee Share, Raydium, Meteora, Orca et routers. + +## Travail demandé + +Pour chaque surface : + +- corpus de signatures ; +- décodeur minimal ; +- matérialisation minimale ; +- listener si utile ; +- exécuteur si la surface est appelable ; +- simulation ; +- démo Tauri ; +- validation post-transaction ; +- SQL anti-régression. + +## Surfaces prioritaires + +- `amm_pump_swap` +- `launchpad_pump_fun` +- `admin_pump_fees` +- `fees_bags_fee_share_v1` +- `fees_bags_fee_share_v2` +- `amm_raydium_lp_v4` +- `cpmm_raydium` +- `clmm_raydium` +- `launchpad_raydium_launchlab` +- `launchpad_meteora_dbc` +- `dlmm_meteora` +- `amm_meteora_damm_v1` +- `amm_meteora_damm_v2` +- `clmm_orca_whirlpool` +- `router_jupiter_aggregator_v6` +- `router_okx_labs_v2` + +## Validations attendues + +- démo Tauri par surface terminée ; +- matérialisation et validation post-transaction ; +- aucun envoi mainnet sans confirmation opérateur et limites actives. diff --git a/migration/khadhroony-bot2-reference/prompts/009_v0_2_1_store_core_contracts.md b/migration/khadhroony-bot2-reference/prompts/009_v0_2_1_store_core_contracts.md new file mode 100644 index 0000000..152029b --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/009_v0_2_1_store_core_contracts.md @@ -0,0 +1,119 @@ + + + +# Prompt v0.2.1 — contrats `kb_store_core`, DTOs, entities et repositories offline + +Objectif : ouvrir `0.2.1` après le cadrage `0.2.0` et implémenter les premiers contrats backend-agnostiques de stockage sans écrire de SQL PostgreSQL. + +## État de départ attendu + +- `0.1.3` validée et commitée comme base propre. +- `0.2.0` appliquée : conventions PostgreSQL et table naming verrouillées. +- `CHANGELOG.md` ne doit pas être modifié tant que le jalon courant n'est pas validé localement. + +## Rappels obligatoires + +- Ne pas créer de schémas PostgreSQL applicatifs explicites. +- Utiliser le format de table `kb_sol__`. +- Ne pas créer de grand schéma SQL en `0.2.1`. +- Ne pas implémenter de pool PostgreSQL en `0.2.1`. +- Ne pas placer de struct métier ou DB dans `queries/`. +- Les types communs et traits repository vivent dans `kb_store_core`. +- Les implémentations PostgreSQL restent hors scope jusqu'à `0.2.2` ou `0.2.3` selon le cas. + +## Layout cible `kb_store_core` + +```text +src/ + lib.rs + pagination.rs + health.rs + db_error.rs + dtos/ + entities/ + repositories/ +``` + +## Travail demandé + +### Pagination et limites + +- Définir les types de pagination backend-agnostiques. +- Définir les limites minimales et maximales. +- Ajouter des validations offline sans `unwrap`, sans `expect` et sans `?` en production. + +### Healthcheck + +- Stabiliser le contrat de healthcheck store. +- Prévoir les champs nécessaires pour la future page Tauri DB : backend, statut, message, schema courant, migrations détectées. +- Garder les détails PostgreSQL concrets hors de `kb_store_core`. + +### Erreurs DB + +- Ajouter les helpers ou enums nécessaires autour de `kb_core::Error` et `kb_core::Result`. +- Ne pas ajouter `anyhow` ni `thiserror`. + +### DTOs et entities minimaux + +Créer les premiers types pour : + +- `kb_sol_raw_rpc_transactions` ; +- `kb_sol_raw_ws_notifications` ; +- `kb_sol_core_transactions` ; +- `kb_sol_core_instructions` ; +- `kb_sol_ops_processing_ledger`. + +Conventions de nommage attendues : + +```text +RawRpcTransactionEntity +RawWsNotificationEntity +CoreTransactionEntity +CoreInstructionEntity +ProcessingLedgerEntity +InsertRawRpcTransactionDto +UpsertRawRpcTransactionDto +InsertRawWsNotificationDto +RepositoryPageDto +StoreHealthDto +``` + +### Traits repository offline + +Créer ou stabiliser les traits pour : + +- raw RPC transaction ; +- raw WS notification ; +- core transaction read/write minimal ; +- core instruction read/write minimal ; +- processing ledger. + +Les traits ne doivent pas dépendre de PostgreSQL. + +## Crates à auditer avant modification + +- `kb_core` +- `kb_model` +- `kb_store_core` +- `kb_rpc` seulement si un type RPC doit être partagé + +## Validations attendues + +```bash +cargo test -p kb_store_core +cargo clippy -p kb_store_core --all-targets +``` + +Si `kb_model` est modifié : + +```bash +cargo test -p kb_model +cargo clippy -p kb_model --all-targets +``` + +## Format de livraison attendu + +- Si seul `kb_store_core` est touché : `kb_store_core_v0.2.1-pre.abc.zip`. +- Si plusieurs crates ou fichiers racine sont touchés : `khadhroony-bot2_v0.2.1-pre.abc.zip`. +- Inclure `delta.md` non versionné dans le zip. +- Ne fournir que les fichiers ajoutés ou modifiés. diff --git a/migration/khadhroony-bot2-reference/prompts/010_v0_2_2_postgres_connection_health_migrations.md b/migration/khadhroony-bot2-reference/prompts/010_v0_2_2_postgres_connection_health_migrations.md new file mode 100644 index 0000000..bae6299 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/010_v0_2_2_postgres_connection_health_migrations.md @@ -0,0 +1,52 @@ + + + +# Prompt `0.2.2` — connexion PostgreSQL, pool, healthcheck et stratégie migrations + +Objectif : ouvrir `0.2.2` après validation locale de `0.2.1`, en implémentant uniquement la frontière PostgreSQL minimale dans `kb_store_pg`. + +## Base attendue + +- `0.2.0` validée : conventions DB verrouillées. +- `0.2.1` validée : contrats `kb_store_core`, DTOs, entities et traits repository offline. +- `CHANGELOG.md` ne doit être modifié qu'après validation locale. + +## Contraintes PostgreSQL + +- Ne pas créer de schemas applicatifs PostgreSQL. +- Utiliser le schema courant/default du profil, généralement `public`. +- Les tables futures gardent le format `kb_sol__`. +- Ne pas implémenter le gros schéma raw/core dans `0.2.2`. + +## Livrables attendus + +- lecture du DSN PostgreSQL depuis le profil actif si le contrat de configuration existe déjà ; +- masquage du DSN pour diagnostics ; +- création d'un handle/pool PostgreSQL minimal dans `kb_store_pg` ; +- healthcheck PostgreSQL minimal ; +- inspection du schema courant ; +- stratégie de migrations documentée et scaffoldée ; +- tests offline pour masquage DSN, assemblage de configuration, noms de migrations et diagnostics ; +- README `kb_store_pg` mis à jour ; +- prompt de suite pour `0.2.3` raw store minimal. + +## Hors périmètre de `0.2.2` + +- tables raw concrètes ; +- tables core concrètes ; +- repositories SQL complets ; +- fenêtre Tauri DB ; +- tests d'intégration nécessitant un serveur PostgreSQL local obligatoire. + +## Validations attendues + +```bash +cargo test -p kb_store_core +cargo test -p kb_store_pg +cargo test -p kb_config +cargo test -p kb_app_demo +cargo clippy --all-targets +``` + +Si un serveur PostgreSQL local est disponible, ajouter une validation optionnelle documentée séparément, sans rendre le test obligatoire pour le jalon. + diff --git a/migration/khadhroony-bot2-reference/prompts/011_v0_2_3_raw_store_minimal.md b/migration/khadhroony-bot2-reference/prompts/011_v0_2_3_raw_store_minimal.md new file mode 100644 index 0000000..5549c16 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/011_v0_2_3_raw_store_minimal.md @@ -0,0 +1,167 @@ + + + +# Prompt `0.2.3` — raw store Solana minimal + +Objectif : ouvrir `0.2.3` après validation locale de `0.2.2`, en créant uniquement les premières tables raw PostgreSQL et leurs repositories minimaux. + +## Base attendue + +- `0.2.0` validée : conventions PostgreSQL et noms `kb_sol__`. +- `0.2.1` validée : contrats `kb_store_core`, raw lifecycle, replay instruction-level et contexte extrait. +- `0.2.2` validée : `PostgresStore`, pool PostgreSQL, healthcheck, schéma courant, DSN masqué et stratégie migrations non destructive. +- `CHANGELOG.md` ne doit être modifié qu'après validation locale du jalon. + +## Objectif strict + +Créer le raw store minimal : + +```text +kb_sol_raw_rpc_transactions +kb_sol_raw_ws_notifications +``` + +Ne pas créer les tables `core`, `obs`, `decode`, `mat`, `catalog`, `agg`, `ops` ou `wallet` dans ce jalon. + +## Convention PostgreSQL obligatoire + +- Ne pas créer de schémas applicatifs PostgreSQL. +- Utiliser le schéma courant/default du profil, généralement `public`. +- Créer des tables non qualifiées. +- Ne jamais utiliser `raw DOT ...`, `core DOT ...`, etc. + +## Table `kb_sol_raw_rpc_transactions` + +Objectif : stocker le résultat brut canonique des appels RPC transaction par signature. + +Champs candidats : + +```text +id +signature +slot +provider +endpoint_name +rpc_method +commitment +encoding +raw_json +raw_json_hash +retention_state +processing_state +linked_core_transaction_id +created_at +updated_at +``` + +Contraintes minimales : + +- `signature` non vide ; +- `slot` en `BIGINT` ; +- `raw_json` en `JSONB`, nullable uniquement si la ligne est compactée ou purgée ; +- `retention_state` et `processing_state` en texte contrôlé côté Rust au départ ; +- unique index sur `signature` pour la transaction canonique. + +Index minimaux : + +```text +signature +slot +provider +endpoint_name +created_at +processing_state +retention_state +``` + +## Table `kb_sol_raw_ws_notifications` + +Objectif : stocker les notifications WebSocket brutes utiles, dédupliquées par clé stable. + +Champs candidats : + +```text +id +notification_key +subscription_kind +provider +endpoint_name +slot +signature +program_id +account_key +raw_json +raw_json_hash +retention_state +processing_state +linked_raw_rpc_transaction_id +created_at +updated_at +``` + +Contraintes minimales : + +- `notification_key` non vide ; +- `signature` optionnelle ; +- `program_id` optionnel ; +- `account_key` optionnel ; +- `slot` optionnel au départ selon type de notification ; +- `raw_json` en `JSONB`, nullable uniquement si la ligne est compactée ou purgée ; +- unique index sur `notification_key`. + +Index minimaux : + +```text +notification_key +signature +slot +program_id +account_key +provider +endpoint_name +created_at +processing_state +retention_state +``` + +## Repositories attendus + +Implémenter dans `kb_store_pg` les méthodes de `RawTransactionStore` : + +```text +has_raw_rpc_signature +has_raw_ws_notification_key +insert_raw_rpc_transaction +insert_raw_ws_notification +mark_raw_payload_lifecycle +``` + +Les queries doivent rester dans `kb_store_pg/src/queries/`. + +Les implémentations repositories doivent rester dans `kb_store_pg/src/repositories/`. + +Ne pas créer de struct métier ou DB dans `queries/`. + +## Politique de rétention + +Prévoir sans activer agressivement : + +- `Full` : raw JSON conservé ; +- `Compacted` : raw JSON supprimable après hash et extraction core ; +- `Archived` : raw déplacé hors hot store plus tard ; +- `Purged` : raw supprimé, hash et lifecycle conservés. + +## Validations attendues + +```bash +cargo test -p kb_store_pg +cargo test -p kb_store_core +cargo clippy --all-targets +``` + +Si PostgreSQL local est disponible, ajouter une validation manuelle optionnelle : + +```bash +# créer une base de test dédiée puis lancer les migrations raw +# vérifier \dt kb_sol_raw_* +``` diff --git a/migration/khadhroony-bot2-reference/prompts/012_v0_2_4_core_store_minimal.md b/migration/khadhroony-bot2-reference/prompts/012_v0_2_4_core_store_minimal.md new file mode 100644 index 0000000..f94dab4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/012_v0_2_4_core_store_minimal.md @@ -0,0 +1,92 @@ + + + +# Prompt de session — `0.2.4` core Solana normalisé minimal + +Objectif : ouvrir `0.2.4` après validation et commit de `0.2.3`. + +## État de départ attendu + +- `0.2.0` validée : conventions PostgreSQL et nommage `kb_sol__`. +- `0.2.1` validée : contrats `kb_store_core`, DTOs/entities/repositories et replay instruction-level contextualisé. +- `0.2.2` validée : infrastructure PostgreSQL minimale, pool, healthcheck et diagnostics. +- `0.2.3` validée : raw store minimal PostgreSQL avec : + - `kb_sol_raw_rpc_transactions` ; + - `kb_sol_raw_ws_notifications` ; + - repositories raw PostgreSQL ; + - initialisation idempotente du schéma raw. + +## Objectif de `0.2.4` + +Créer le store `core` minimal permettant d'extraire les composants Solana normalisés depuis le raw sans encore développer le pipeline complet d'extraction. + +## Tables candidates + +Créer uniquement les tables nécessaires au core minimal : + +```text +kb_sol_core_transactions +kb_sol_core_account_keys +kb_sol_core_instructions +kb_sol_core_inner_instructions +kb_sol_core_logs +kb_sol_core_balance_changes +``` + +Ne pas encore créer les tables `obs`, `decode`, `mat`, `catalog`, `agg`, `ops` ou `wallet`, sauf nécessité explicitement justifiée. + +## Principes obligatoires + +- Le replay doit sélectionner des instructions normalisées, pas des transactions entières. +- Les décodeurs doivent recevoir une instruction avec contexte : account keys, inner instructions, logs et balance changes. +- Les tables core doivent référencer la signature et le slot. +- Les instructions doivent avoir un `instruction_path` stable. +- Les inner instructions doivent conserver leur relation parent/enfant. +- Les logs doivent conserver l'ordre original. +- Les balance changes doivent rester suffisamment génériques pour native lamports et SPL tokens. +- Les états de traitement instruction-level doivent permettre : pending, decoded, materialized, ignored, failed, replay_requested. + +## Hors périmètre + +- Ne pas décoder les protocoles DEX. +- Ne pas matérialiser les trades ou liquidités. +- Ne pas supprimer physiquement le raw. +- Ne pas créer de fenêtre Tauri DB. +- Ne pas créer de grand schéma monolithique. + +## Contraintes Rust + +- Rust 2024. +- `#![warn(missing_docs)]`. +- `#![deny(unreachable_pub)]`. +- `#![forbid(unsafe_code)]`. +- Pas de `use` sauf traits. +- `crate::` en interne. +- Pas de `?` en production. +- Pas de `unwrap`. +- Pas de `expect`. +- Pas de `anyhow`. +- Pas de `thiserror`. +- Erreurs via `kb_core::Error` et `kb_core::Result`. + +## Validations attendues + +```bash +cargo test -p kb_store_pg +cargo test -p kb_store_core +cargo test -p kb_config +cargo test -p kb_app_demo +cargo clippy --all-targets +``` + +Test PostgreSQL optionnel à prévoir si une base locale existe : + +```bash +KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana' cargo test -p kb_store_pg optional_postgres_core_store_roundtrip_from_env -- --nocapture +``` + +## Livraison + +- Fournir un zip delta uniquement. +- Ne pas modifier `CHANGELOG.md` avant validation locale. +- Inclure `delta.md` non versionné dans le zip. diff --git a/migration/khadhroony-bot2-reference/prompts/013_v0_2_5_db_diagnostics_tauri.md b/migration/khadhroony-bot2-reference/prompts/013_v0_2_5_db_diagnostics_tauri.md new file mode 100644 index 0000000..e4653f4 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/013_v0_2_5_db_diagnostics_tauri.md @@ -0,0 +1,50 @@ + + + +# Prompt `0.2.5` — diagnostic DB Tauri + +Objectif : ouvrir `0.2.5` après validation et commit de `0.2.4`, en ajoutant une fenêtre Tauri de diagnostic PostgreSQL sans modifier le schéma métier. + +## État attendu + +- `0.2.0` validée : conventions SQL et noms `kb_sol__`. +- `0.2.1` validée : contrats `kb_store_core`, replay instruction-level contextualisé. +- `0.2.2` validée : infrastructure PostgreSQL minimale, pool et healthcheck. +- `0.2.3` validée : raw store minimal. +- `0.2.4` validée : core store minimal avec transactions, account keys, instructions, inner instructions, logs et balance changes. + +## Objectif de `0.2.5` + +Ajouter une fenêtre `demo_db` ou `diagnostic_db` dans `kb_app_demo` affichant : + +- profil actif ; +- backend DB actif ; +- DSN masqué ; +- schema courant PostgreSQL ; +- version PostgreSQL ; +- healthcheck ; +- statut migrations ; +- tables `kb_sol_*` détectées ; +- présence des tables raw/core attendues ; +- compte rapide optionnel des tables raw/core si cela reste léger. + +## Contraintes + +- Ne pas créer de nouvelles tables métier. +- Ne pas exposer le mot de passe PostgreSQL côté UI. +- Utiliser `kb_store_pg::PostgresStoreOptions::from_active_app_config`. +- Garder les commandes Tauri typées et exportées TS-rs. +- Garder les tests offline autant que possible. +- Prévoir un test PostgreSQL optionnel via `KB_POSTGRES_TEST_URL` seulement si nécessaire. + +## Validations attendues + +```bash +cargo test -p kb_store_pg +cargo test -p kb_store_core +cargo test -p kb_config +cargo test -p kb_app_demo +cargo clippy --all-targets +cargo tauri dev -c kb_app_demo/tauri.conf.json +``` + diff --git a/migration/khadhroony-bot2-reference/prompts/014_v0_3_0_agave_local_research.md b/migration/khadhroony-bot2-reference/prompts/014_v0_3_0_agave_local_research.md new file mode 100644 index 0000000..fbb5f3f --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/014_v0_3_0_agave_local_research.md @@ -0,0 +1,210 @@ + + + +# Prompt historique v0.3.0 — expérimentation Agave local et sources temps réel + +Ce prompt correspond au jalon historique `0.3.0`, validé puis abandonné comme axe actif après les mesures matérielles. Il ne doit plus être utilisé pour ouvrir une nouvelle session. + +Objectif historique : ouvrir la nouvelle phase `0.3.x` de `khadhroony-bot2` en testant un nœud Agave local non votant comme source temps réel potentielle avant de développer l'ingestion, le backfill et l'extracteur raw vers core. + +## État de départ attendu + +- `0.2.5` validée : diagnostics SQL PostgreSQL dans `kb_app_demo`. +- Tables raw/core PostgreSQL minimales disponibles. +- `demo_ws` existe déjà et peut servir de base pour tester les subscriptions. +- Le profil `agave_local_research` existe dans `config/example.config.json` mais n'est pas actif par défaut. +- L'ancien périmètre ingestion/backfill/extraction est reporté à `0.4.x`. + +## Objectif de `0.3.0` + +`0.3.0` ne doit pas lancer l'ingestion de production. + +Le jalon doit préparer l'expérimentation : + +- documentation Agave local ; +- profil dédié ; +- correction des rôles de listeners du profil Agave local ; +- stratégie des sources temps réel ; +- probes à ajouter dans `demo_ws` ou dans une nouvelle démo dédiée ; +- mesures à collecter ; +- règles de fallback ; +- critères de décision pour `0.4.x`. + +## Hypothèse à tester + +Un nœud local Agave non votant, avec ledger limité par shreds, pourrait permettre : + +- `blockSubscribe` local avec transactions complètes ; +- moins de `getTransaction` temps réel ; +- moins de listeners par programme ; +- filtrage local par program id, logs, instructions, balances et discriminators ; +- usage des providers externes surtout pour backfill historique, réparation et `sendTransaction`. + +Cette hypothèse doit être mesurée, pas supposée vraie. + +## Commande de départ à documenter/tester + +```bash +agave-validator \ + --no-voting \ + --full-rpc-api \ + --enable-rpc-transaction-history \ + --rpc-pubsub-enable-block-subscription \ + --limit-ledger-size \ + --ledger ./data/agave-ledger \ + --accounts ./data/agave-accounts \ + --rpc-port 8899 \ + --dynamic-port-range 8000-8020 +``` + +La commande peut être ajustée selon la version Agave installée. Ne pas masquer les échecs : documenter les erreurs de snapshots, ledger, ports, ressources et retard. + +## Probes attendus + +`0.3.0` doit documenter ces probes. L'implémentation automatique peut commencer en `0.3.2`. + +Tester au minimum : + +```text +getVersion +getHealth +getSlot +getBlockHeight +getMaxShredInsertSlot +blockSubscribe transactionDetails=none +slotSubscribe +slotsUpdatesSubscribe +rootSubscribe +logsSubscribe mentions +programSubscribe +accountSubscribe +``` + +Pour `blockSubscribe`, le probe minimal doit : + +```text +subscribe avec transactionDetails=none +attendre subscription id ou erreur +désabonner immédiatement +classer supported / unsupported / timeout / error +``` + +Paramètres manuels de départ dans `demo_ws` : + +```text +role: block_stream +method: blockSubscribe +filterJson: "all" +configJson: {"commitment":"confirmed","encoding":"json","transactionDetails":"none","showRewards":false} +``` + +## Mesures attendues + +- taille ledger ; +- taille accounts ; +- CPU ; +- RAM ; +- réseau RX/TX ; +- slot local ; +- slot externe de référence ; +- lag slots ; +- lag secondes ; +- débit WebSocket ; +- taille moyenne des notifications ; +- reconnects ; +- erreurs provider ou Agave. + +## Phasage recommandé + +### `0.3.0` + +- docs Agave local ; +- profil `agave_local_research` ; +- stratégie live source ; +- prompt et roadmap mis à jour. + +### `0.3.1` + +- script ou runbook de lancement Agave local ; +- mesures initiales ledger/shreds/ressources. + +### `0.3.2` + +- probes RPC/WS dans `demo_ws` ou nouvelle démo. + +### `0.3.3` + +- mesures de retard et stabilité avec plusieurs valeurs `--limit-ledger-size`. + +### `0.3.4` + +- test `blockSubscribe` complet : `none`, `signatures`, `full`. + +### `0.3.5` + +- test listeners multiples : logs/program/account/slot/root. + +### `0.3.6` + +- décision d'architecture live source et préparation `0.4.x`. + +## Hors périmètre strict + +- pas d'ingestion de production ; +- pas d'extracteur raw RPC vers core ; +- pas de décodage DEX ; +- pas de matérialisation ; +- pas de backfill historique ; +- pas d'achat/revente ; +- pas de dépendance obligatoire à Agave local. + +## Fichiers à lire/modifier en priorité + +- `ROADMAP.md` +- `config/example.config.json` +- `config/README.md` +- `docs/AGAVE_LOCAL_NODE.md` +- `docs/LIVE_SOURCE_STRATEGY.md` +- `docs/WS_LISTENERS.md` +- `docs/RPC_ENDPOINT_ROLES.md` +- `kb_app_demo/src/demo_ws.rs` +- `kb_app_demo/frontend/demo_ws.html` +- `kb_app_demo/frontend/ts/demo_ws.ts` +- `kb_rpc/` si des probes doivent être factorisés côté RPC. + +Pour `0.3.0`, privilégier les documents et la configuration. Ne modifier `demo_ws` ou `kb_rpc` que si le delta reste strictement préparatoire et sans ingestion. + +## Contraintes + +- Conserver Rust 2024. +- Conserver `#![warn(missing_docs)]`, `#![deny(unreachable_pub)]`, `#![forbid(unsafe_code)]`. +- Pas de `unwrap`, `expect`, `anyhow`, `thiserror`. +- Pas de `?` en production. +- Pas de `use` sauf traits. +- `crate::` en interne. +- Documentation Rust/TypeScript/SQL en anglais. +- Markdown projet en français. +- Ne pas modifier `CHANGELOG.md` avant validation locale du jalon. + +## Format de livraison attendu + +- Fournir un zip delta uniquement. +- Utiliser `khadhroony-bot2_v0.3.0-pre.abc.zip` si plusieurs modules ou la racine sont touchés. +- Utiliser `kb_rpc_v0.3.0-pre.abc.zip` ou `kb_app_demo_v0.3.0-pre.abc.zip` si un seul module est touché. +- Inclure `delta.md` non versionné dans le zip. +- Documenter les validations exécutées et celles non exécutées. + +## Validations attendues + +Selon le delta : + +```bash +cargo test -p kb_config +cargo test -p kb_rpc +cargo test -p kb_app_demo +cargo clippy --all-targets +cargo tauri dev -c kb_app_demo/tauri.conf.json +``` + +Les tests Agave réels peuvent rester manuels au début, mais les probes doivent reporter clairement les erreurs et timeouts. + diff --git a/migration/khadhroony-bot2-reference/prompts/015_v0_3_1_canonical_store_migration.md b/migration/khadhroony-bot2-reference/prompts/015_v0_3_1_canonical_store_migration.md new file mode 100644 index 0000000..b3f8fe6 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/015_v0_3_1_canonical_store_migration.md @@ -0,0 +1,48 @@ + + + +# Prompt historique v0.3.1 — transaction canonique et observations + +Ce prompt décrit le jalon `0.3.1` désormais validé. Il est conservé pour expliquer la transition, mais ne doit plus être utilisé comme prompt actif. + +## Résultat validé + +Le stockage actif repose sur : + +```text +kb_sol_raw_transactions +kb_sol_obs_transaction_observations +kb_sol_core_transactions.raw_transaction_id +``` + +La transaction canonique contient le document rejouable source-indépendant. Les observations contiennent uniquement les informations de provenance, protocole, méthode, session, filtre, timestamps, taille, statut et erreur. Aucun payload complet WebSocket ou Protobuf n’est dupliqué dans les observations. + +## Transition historique + +La base PostgreSQL `0.2.x` a été convertie et contrôlée manuellement : + +- les anciennes tables RPC/WS sont absentes ; +- les tables canoniques sont présentes ; +- les colonnes `canonical_json`, `canonical_json_hash`, `canonical_format_version` et `raw_transaction_id` sont présentes ; +- les fixtures de test ont été nettoyées après validation. + +Après ce contrôle, le dossier `kb_store_pg/migrations/` a été consolidé en : + +```text +0001_canonical_transaction_store.sql +0002_core_store.sql +``` + +Les fichiers SQL actifs ne contiennent plus les anciennes définitions. Le DDL runtime conserve temporairement un chemin de compatibilité idempotent pour les workspaces encore issus de `pre.001`. L’historique reste dans `CHANGELOG.md` et la documentation. + +## Validations réalisées + +```text +cargo test -p kb_app_demo 32 tests +cargo test -p kb_store_core 30 tests +KB_POSTGRES_TEST_URL=... cargo test -p kb_store_pg -- --nocapture 32 tests +cargo clippy --all-targets OK +cargo tauri dev -c kb_app_demo/tauri.conf.json OK +``` + +Le prompt actif suivant est `016_v0_3_2_canonical_transaction_contract.md`. diff --git a/migration/khadhroony-bot2-reference/prompts/016_v0_3_2_canonical_transaction_contract.md b/migration/khadhroony-bot2-reference/prompts/016_v0_3_2_canonical_transaction_contract.md new file mode 100644 index 0000000..b85fbae --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/016_v0_3_2_canonical_transaction_contract.md @@ -0,0 +1,112 @@ + + + +# Prompt v0.3.2 — contrat de transaction canonique et adaptateur HTTP + +## Contexte validé + +`0.3.1` a remplacé les responsabilités historiques RPC/WS par : + +```text +kb_sol_raw_transactions +kb_sol_obs_transaction_observations +RawTransactionInsert +TransactionObservationInsert +``` + +Les observations ne contiennent aucun payload transactionnel complet. Les détails de fournisseur restent hors de la transaction canonique. La baseline SQL active est consolidée dans `0001_canonical_transaction_store.sql` et `0002_core_store.sql`; ne pas réintroduire les anciennes tables RPC/WS ni une logique de migration historique dans le DDL runtime. + +## Objectif + +Définir dans `kb_model` la représentation canonique complète d’une transaction Solana, puis ajouter dans `kb_rpc` l’adaptateur HTTP `getTransaction` qui produit exactement cette représentation. + +## Contrat canonique + +Couvrir au minimum : + +- transaction legacy et version 0 ; +- signatures ; +- message header ; +- static account keys ; +- address table lookups ; +- loaded writable et readonly addresses ; +- recent blockhash ; +- instructions externes ; +- inner instructions ; +- logs ; +- statut et erreur ; +- fee et compute units ; +- balances SOL pré/post ; +- balances SPL pré/post ; +- rewards ; +- return data ; +- block time quand disponible. + +Les signatures et public keys restent en base58. Les payloads binaires d’instruction restent en base64. Les nombres on-chain doivent être conservés sans perte. + +## Sérialisation et hash + +- définir `canonical_format_version = 1` ; +- garantir une sérialisation déterministe ; +- calculer `canonical_json_hash` sur la représentation canonique ; +- exclure du hash tout champ de fournisseur, endpoint, timestamp local ou protocole de transport ; +- tester qu’une même transaction représentée par deux fixtures compatibles produit le même hash. + +## Adaptateur HTTP dans `kb_rpc` + +Ajouter la conversion explicite : + +```text +getTransaction JSON-RPC + -> validation de la réponse + -> modèle canonique kb_model + -> RawTransactionInsert + -> TransactionObservationInsert +``` + +Ne pas créer de crate séparée. Les futurs adaptateurs Helius `transactionSubscribe` et Yellowstone gRPC resteront des modules internes de `kb_rpc` et devront cibler le même modèle. + +## Fixtures offline + +Ajouter des fixtures pour : + +- legacy succès ; +- legacy échec ; +- version 0 avec ALT ; +- CPI et inner instructions ; +- SPL Token ; +- Token-2022 ; +- transaction avec return data ; +- transaction sans block time ; +- champs optionnels absents. + +## Fichiers à lire en priorité + +- `kb_model/` ; +- `kb_rpc/` ; +- `kb_store_core/src/dtos/raw_dtos.rs` ; +- `kb_store_pg/src/queries/raw_queries.rs` ; +- `docs/TRANSACTION_ACQUISITION_MODEL.md` ; +- `docs/RAW_STORE.md` ; +- `ROADMAP.md`. + +## Hors périmètre + +- pas de modification du schéma PostgreSQL `0.3.1` ; +- pas de compte Helius Developer ; +- pas de Yellowstone gRPC réel ; +- pas de backfill paginé ; +- pas d’extraction vers les tables core ; +- pas de décodeur protocolaire. + +## Validation attendue + +```bash +cargo test -p kb_model +cargo test -p kb_rpc +cargo test -p kb_store_core +cargo test -p kb_store_pg +cargo clippy --all-targets +``` + +Vérifier également l’absence de tout nom de provider dans le JSON canonique et la stabilité du hash entre fixtures équivalentes. diff --git a/migration/khadhroony-bot2-reference/prompts/017_v0_3_3_http_backfill_free_tier.md b/migration/khadhroony-bot2-reference/prompts/017_v0_3_3_http_backfill_free_tier.md new file mode 100644 index 0000000..9e2a9eb --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/017_v0_3_3_http_backfill_free_tier.md @@ -0,0 +1,186 @@ + + + +# Prompt historique v0.3.3 — backfill HTTP paginé sur comptes gratuits + +## État final validé + +Ce prompt est conservé comme historique. Le jalon `0.3.3` est clôturé avec `demo_backfill`, la pagination `before` / `until`, la sélection directe des signatures postérieures à l’ancre, la persistance canonique et l’arrêt coopératif borné. + +Validations finales : + +```text +cargo test -p kb_rpc: 54 tests passés +cargo test -p kb_pipeline: 10 tests passés +cargo test -p kb_store_core: 31 tests passés +cargo test -p kb_store_pg: 32 tests passés +cargo test -p kb_app_demo: 38 tests passés +cargo clippy --all-targets: validé +``` + +## Contexte validé attendu + +`0.3.2` est validée et clôturée. La baseline confirmée fournit : + +```text +kb_model::CanonicalTransaction +kb_rpc::GetTransactionConfig +kb_rpc::GetTransactionAdapter +kb_rpc::CanonicalTransactionAdapter +kb_store_core::RawTransactionInsert::from_canonical +``` + +Validations de référence : + +```text +cargo test -p kb_model: 23 tests passés +cargo test -p kb_rpc: 48 tests passés +cargo test -p kb_store_core: 31 tests passés +cargo test -p kb_store_pg avec PostgreSQL réel: 32 tests passés +cargo clippy --all-targets: validé +``` + +Le store actif reste : + +```text +kb_sol_raw_transactions +kb_sol_obs_transaction_observations +``` + +Ne pas modifier le schéma PostgreSQL `0.3.1` sans nécessité démontrée. + +## Objectif + +Ajouter un backfill HTTP réutilisable avec les comptes gratuits des fournisseurs RPC : + +```text +getSignaturesForAddress paginé + -> signatures candidates + -> getTransaction + -> CanonicalTransaction + -> RawTransactionInsert + -> TransactionObservationInsert +``` + +## Périmètre fonctionnel + +- pagination `getSignaturesForAddress` avec `before`, `until`, limite et ordre explicite ; +- le mode `after` doit transmettre l’ancre via `until`, paginer avec `before` et utiliser des pages de 1000 signatures ; +- le mode `after` ne doit pas rechercher manuellement l’ancre depuis la tête sans borne RPC ; +- ne conserver que les `X` signatures plus récentes les plus proches de l’ancre ; +- backfill d’une signature explicite ; +- backfill d’un groupe de signatures ; +- backfill avant/après une signature d’ancrage pour un program id ; +- backfill avant/après une signature d’ancrage pour un mint de token ; +- backfill avant/après une signature d’ancrage pour une adresse de pool ; +- reprise après interruption avec curseur persistant ou exportable ; +- sélection d’endpoint via les pools et rôles existants ; +- rate limits, concurrency, timeout, retries et pause après 429 depuis `kb_config` ; +- idempotence par signature dans `kb_sol_raw_transactions` ; +- une observation par tentative et par source ; +- conservation des statuts `missing` et `failed` sans créer de transaction canonique vide ; +- arrêt propre et résumé de campagne ; +- file d’exécution bornée : ne jamais instancier une future par candidat sélectionné ; +- compteurs distincts pour candidats sélectionnés, démarrés, terminés, annulés et non démarrés ; +- curseur de reprise calculé depuis le dernier préfixe contigu réellement terminé. + +## Observations HTTP + +Chaque tentative doit produire une observation légère avec au minimum : + +```text +provider +endpoint_code +protocol = solana_http_json_rpc +acquisition_method = getTransaction +origin = backfill +commitment +capture_session_id +received_at +normalized_at +payload_size_bytes si disponible +source_payload_hash si disponible +status +error_code +error_message +``` + +Le payload HTTP complet ne doit pas être dupliqué dans la table d’observations. + +## UI et orchestration + +Ajouter une page Tauri de démonstration permettant : + +- une liste de signatures dans un textarea, séparées par des retours à la ligne ; +- un program id avec limite, direction `before`/`after` et signature d’ancrage ; +- un mint de token avec limite, direction `before`/`after` et signature d’ancrage ; +- une adresse de pool avec limite, direction `before`/`after` et signature d’ancrage ; +- les paramètres communs de rôle, commitment, page size, max pages, concurrence et retries ; +- le suivi des compteurs reçus, normalisés, persistés, ignorés, manquants et échoués ; +- un journal UI borné et un résumé JSON ; +- l’arrêt explicite et coopératif de la campagne ; +- un regroupement des sections dans des accordéons Bootstrap 5 en réutilisant le template global. + +La page ne doit pas contenir la logique de backfill profonde : elle appelle les APIs de `kb_pipeline`. + +## Corpus initial + +Préparer les premiers corpus dans cet ordre : + +1. Solana Core et SPL ; +2. Pump Fun, Pump Swap et Pump Fees ; +3. Meteora ; +4. Raydium ; +5. Orca. + +Jupiter peut apparaître dans les transactions routées, mais aucun flux temps réel payant n’est introduit dans cette tranche. + +## Hors périmètre + +- Helius Developer ; +- `transactionSubscribe` ; +- Yellowstone gRPC ; +- `logsSubscribe("all")` ; +- extraction vers les tables core ; +- décodeurs protocolaires ; +- matérialisation ; +- trading. + +## Fichiers à lire en priorité + +- `kb_rpc/src/get_transaction.rs` ; +- `kb_rpc/src/http_pool.rs` ; +- `kb_pipeline/` ; +- `kb_store_core/src/dtos/raw_dtos.rs` ; +- `kb_store_core/src/repositories/` ; +- `kb_store_pg/src/repositories/` ; +- `kb_app_demo/src/` ; +- `kb_app_demo/frontend/ts/` ; +- `docs/TRANSACTION_ACQUISITION_MODEL.md` ; +- `docs/RAW_STORE.md` ; +- `docs/BACKFILL_HTTP.md` ; +- `ROADMAP.md`. + +## Validation attendue + +```bash +cargo test -p kb_rpc +cargo test -p kb_pipeline +cargo test -p kb_store_core +KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' cargo test -p kb_store_pg -- --nocapture +cargo test -p kb_app_demo +cargo clippy --all-targets +cargo tauri dev -c kb_app_demo/tauri.conf.json +``` + +Utiliser de préférence une base PostgreSQL dédiée aux tests. Si la base de développement est utilisée volontairement, identifier puis supprimer explicitement les fixtures de roundtrip après la campagne. Aucun test ne doit effacer des données non créées par sa propre exécution. + +## Livraison + +Produire un zip delta : + +```text +khadhroony-bot2_v0.3.3-pre.004.zip +``` + +Le delta doit contenir `delta.md`, les fichiers ajoutés ou modifiés et la liste explicite des éventuelles suppressions manuelles. Ne pas modifier `CHANGELOG.md` avant validation complète de la tranche. diff --git a/migration/khadhroony-bot2-reference/prompts/018_v0_3_4_canonical_to_core_extraction.md b/migration/khadhroony-bot2-reference/prompts/018_v0_3_4_canonical_to_core_extraction.md new file mode 100644 index 0000000..49c0906 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/018_v0_3_4_canonical_to_core_extraction.md @@ -0,0 +1,335 @@ + + + +# Prompt v0.3.4 — extraction transaction canonique vers core + +## Statut + +Jalon validé et clôturé le 7 juillet 2026. Ce prompt est conservé comme contrat historique ; ne plus l’utiliser comme prompt actif. + +Résultats finaux : `kb_pipeline` 24 tests, `kb_store_pg` 39 tests avec rollback PostgreSQL réel, `kb_app_demo` 60 tests, `kb_program_ids` 2 tests, Clippy sans avertissement, 70 transactions core et 84 tentatives ledger après force replay. + +## État de clôture + +Le périmètre fonctionnel a été livré jusqu’à `0.3.4-pre.011`, puis validé localement. Les contrôles couvrent 70 transactions core, les requêtes d’intégrité, l’écriture CSV native, le skip et le force replay sur 14 signatures, le rollback PostgreSQL intermédiaire et l’annulation des futures RPC, du pacer et des pauses de retry/429. + +L’ancien jalon `0.3.5` est absorbé dans `0.3.4`. `CHANGELOG.md` et `ROADMAP.md` enregistrent la clôture de `0.3.x`; la suite active est directement `0.4.0`. + +## Contexte validé attendu + +`0.3.3` est validée et clôturée. La baseline fournit : + +```text +kb_model::CanonicalTransaction +kb_rpc::getTransaction +kb_pipeline::backfill +kb_sol_raw_transactions +kb_sol_obs_transaction_observations +kb_sol_core_transactions +kb_sol_core_account_keys +kb_sol_core_instructions +kb_sol_core_inner_instructions +kb_sol_core_logs +kb_sol_core_balance_changes +``` + +Validations de référence : + +```text +cargo test -p kb_rpc: 54 tests passés +cargo test -p kb_pipeline: 10 tests passés +cargo test -p kb_store_core: 31 tests passés +cargo test -p kb_store_pg: 32 tests passés +cargo test -p kb_app_demo: 38 tests passés +cargo clippy --all-targets: validé +``` + +Les backfills réels ont produit des transactions canoniques et des observations PostgreSQL. Les tables core sont encore vides par conception. + +## Objectif + +Ajouter un extracteur réutilisable et versionné : + +```text +kb_sol_raw_transactions.canonical_json + -> kb_model::CanonicalTransaction + -> validation + -> extraction transactionnelle + -> kb_sol_core_transactions + -> kb_sol_core_account_keys + -> kb_sol_core_instructions + -> kb_sol_core_inner_instructions + -> kb_sol_core_logs + -> kb_sol_core_balance_changes + -> kb_sol_ops_processing_ledger +``` + +L’extracteur prépare les futurs décodeurs sans effectuer de décodage protocolaire ni de matérialisation métier. + +## Périmètre fonctionnel + +### Lecture et sélection + +- ajouter les contrats backend-agnostiques nécessaires dans `kb_store_core` ; +- lire les transactions canoniques par signature, plage de slots, état de traitement ou lot borné ; +- ne jamais relire le payload fournisseur depuis les observations ; +- refuser un `canonical_json` absent, invalide ou incompatible avec `canonical_format_version` ; +- conserver une erreur explicite et rejouable pour chaque échec d’extraction. + +### Extraction transactionnelle + +- traiter une transaction canonique dans une transaction PostgreSQL unique ; +- ne laisser aucune ligne core partielle en cas d’échec ; +- relier `kb_sol_core_transactions.raw_transaction_id` à la ligne canonique ; +- rendre l’extraction idempotente par signature, index stable et instruction path ; +- permettre un replay forcé d’une signature en supprimant puis recréant ses lignes filles dans la même transaction, sans toucher aux autres signatures ; +- ne marquer le traitement comme réussi qu’après commit PostgreSQL. + +### Account keys + +Construire un espace d’index canonique unique dans cet ordre : + +```text +static account keys +loaded writable addresses +loaded readonly addresses +``` + +Pour chaque compte, extraire : + +- `account_index` stable ; +- public key ; +- source `static`, `loaded_writable` ou `loaded_readonly` ; +- signer ; +- writable ; +- executable uniquement lorsque l’information est réellement disponible, sinon `NULL`. + +Les flags des comptes statiques doivent être calculés strictement depuis le message header Solana. Les loaded writable ne sont pas signers ; les loaded readonly ne sont ni signers ni writable. + +### Instructions + +- résoudre `program_id_index` dans l’espace complet des comptes ; +- résoudre chaque index de compte de l’instruction vers sa public key ; +- conserver dans `accounts_json` au minimum les indices et les clés résolues dans l’ordre original ; +- conserver le payload canonique base64 dans une forme JSON stable ; +- produire des chemins top-level `0`, `1`, `2`, etc. ; +- produire des chemins inner `0/0`, `0/1`, `2/0`, etc. ; +- rattacher chaque inner instruction au chemin top-level parent ; +- refuser les indices hors limites au lieu de produire un programme ou un compte vide. + +### Logs + +- conserver l’ordre global via `log_index` ; +- conserver le texte original et un hash stable ; +- ne rattacher `program_id` ou `instruction_path` que lorsque la pile d’invocation est reconstruite sans ambiguïté ; +- laisser ces champs à `NULL` lorsque le lien n’est pas fiable ; +- tester les séquences `invoke`, `success` et `failed` avec profondeur CPI. + +### Balances + +- extraire les changements natifs en lamports avec précision entière ; +- extraire les changements SPL/Token-2022 sans utiliser les valeurs UI comme source de vérité ; +- conserver pre, post et delta dans des JSON numériques déterministes ; +- conserver `account_index`, `account_key`, `mint` et `owner` lorsque disponibles ; +- gérer les comptes présents uniquement avant ou uniquement après la transaction ; +- garantir un `balance_change_index` déterministe. + +### Ledger de traitement + +Ajouter `kb_sol_ops_processing_ledger` et son implémentation PostgreSQL avec au minimum : + +```text +stage +processor_name +processor_version +input_key +input_hash +status +attempt_count +started_at +finished_at +error_code +error_message +created_at +updated_at +``` + +Pour l’extracteur : + +```text +stage = core_extraction +processor_name = canonical_to_core +input_key = signature +input_hash = canonical_json_hash +``` + +Le ledger doit permettre : + +- skip d’une extraction déjà réussie avec même version et même hash ; +- replay après changement de version ou de hash ; +- retry explicite des statuts failed ; +- diagnostics sans ambiguïté. + +Créer une migration active dédiée sans réintroduire les anciennes tables `0.2.x`. Ne pas qualifier les tables par un schéma PostgreSQL applicatif explicite. + +## Orchestration `kb_pipeline` + +Ajouter un module distinct, par exemple `core_extraction.rs`, exposant : + +- extraction d’une signature ; +- extraction d’un lot borné ; +- filtre par plage de slots ; +- filtre par program id lorsque les comptes/instructions core existent déjà ; +- mode normal et mode force replay ; +- résumé avec selected, skipped, extracted, failed et cancelled ; +- arrêt coopératif et concurrence bornée selon les mêmes principes que le backfill. + +La logique profonde reste dans `kb_pipeline`; Tauri ne fait qu’appeler cette API. + +## Démo Tauri + +Ajouter ou étendre une fenêtre de démonstration permettant : + +- extraction par liste de signatures ; +- extraction d’un lot pending ; +- extraction par plage de slots ; +- force replay ; +- version d’extracteur visible ; +- journal borné ; +- résumé JSON ; +- arrêt coopératif ; +- rafraîchissement des diagnostics raw/core. + +Réutiliser le template global et les accordéons Bootstrap 5. Ne pas mélanger la fenêtre avec le décodage DEX. + +### Sélecteur SQL de candidats replay + +Ajouter une fenêtre read-only distincte `demo_sql_replay_candidates` afin de préparer les corpus sans pgAdmin ou DBeaver : + +- tableau des transactions/signatures avec filtres raw, ledger, slots, programme et entité ; +- tableau agrégé des programmes outer, inner et logs reliés ; +- tableaux distincts des mints, owners et account keys ; +- DataTables avec intégration Bootstrap 5, sélection par checkbox et pagination à 10 lignes ; +- reset des filtres et copie individuelle ou multiligne dans le presse-papiers ; +- affichage tronqué des identifiants longs avec tooltip et valeur intégrale pour tri, recherche, copie et export ; +- export CSV diagnostique écrit par Rust dans `data/exports_csv/` ; +- requêtes PostgreSQL statiques, paramétrées et limitées ; +- aucune exécution de SQL libre et aucune écriture en base. + +La fenêtre doit être accessible depuis le menu principal et depuis `demo_core_extraction`. + +## Tests offline obligatoires + +Couvrir au minimum : + +1. transaction legacy simple ; +2. transaction v0 avec ALT et loaded addresses ; +3. transaction échouée avec `err_json` ; +4. outer instructions multiples ; +5. inner instructions et CPI imbriqués ; +6. logs avec invoke/success/failed ; +7. balances SOL positives, négatives et nulles ; +8. balances SPL et Token-2022 ; +9. compte token créé ou fermé pendant la transaction ; +10. indice de programme hors limites ; +11. indice de compte hors limites ; +12. idempotence sur double extraction ; +13. replay forcé sans doublon ; +14. rollback complet après erreur intermédiaire ; +15. changement de version d’extracteur ; +16. changement de hash canonique ; +17. arrêt coopératif d’un lot. + +Les fixtures ne doivent effectuer aucun appel réseau. + +## Hors périmètre + +- décodeurs Solana Core/SPL ; +- décodeurs DEX ; +- matérialisation métier ; +- sources Helius `transactionSubscribe` ; +- Yellowstone gRPC ; +- Solscan ou `kb_external_sources` ; +- compaction ou purge du canonical JSON ; +- trading et wallet. + +## Fichiers à lire en priorité + +- `kb_model/src/canonical_transaction.rs` ; +- `kb_pipeline/src/backfill.rs` ; +- `kb_pipeline/src/lib.rs` ; +- `kb_store_core/src/dtos/core_dtos.rs` ; +- `kb_store_core/src/dtos/ledger_dtos.rs` ; +- `kb_store_core/src/entities/core_entities.rs` ; +- `kb_store_core/src/entities/ledger_entities.rs` ; +- `kb_store_core/src/repositories/storage_repositories.rs` ; +- `kb_store_pg/src/queries/core_queries.rs` ; +- `kb_store_pg/src/repositories/core_transaction_repository.rs` ; +- `kb_store_pg/migrations/0002_core_store.sql` ; +- `docs/CORE_EXTRACTION_CONTRACTS.md` ; +- `docs/CORE_STORE.md` ; +- `docs/INSTRUCTION_REPLAY_CONTRACTS.md` ; +- `ROADMAP.md` ; +- `RULES.md`. + +## Règles de réalisation + +- Rust 2024 ; +- async-first ; +- tracing obligatoire ; +- erreurs explicites ; +- aucun `?`, `unwrap`, `expect`, `anyhow` ou `thiserror` dans le code de production ; +- aucun `use` sauf pour les traits ; +- appels locaux via `crate::` ; +- `#![warn(missing_docs)]`, `#![deny(unreachable_pub)]` et `#![forbid(unsafe_code)]` ; +- documentation du code en anglais ; +- documentation Markdown en français ; +- headers `file` / `version` et version incrémentée à chaque modification ; +- aucune ligne vide dans les corps de fonctions ou structures ; +- une ligne vide finale dans chaque fichier ; +- TS-rs pour les types exposés à Tauri ; +- ne pas modifier `CHANGELOG.md` avant validation locale complète. + +## Validation attendue + +```bash +cargo test -p kb_model +cargo test -p kb_store_core +KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana' cargo test -p kb_store_pg -- --nocapture +cargo test -p kb_pipeline +cargo test -p kb_app_demo +(cd kb_app_demo && npm run build) +cargo clippy --all-targets +cargo tauri dev -c kb_app_demo/tauri.conf.json +``` + +Ajouter des requêtes SQL de contrôle pour vérifier : + +- aucune transaction core sans raw lineage lorsque la source raw existe ; +- aucun compte, instruction, inner instruction, log ou balance change orphelin ; +- aucune duplication des clés uniques ; +- égalité entre les comptes résolus et le contrat canonique ; +- stabilité des instruction paths ; +- cohérence du ledger avec les lignes core créées. + +## Livraison historique + +La dernière préversion technique a été livrée sous : + +```text +khadhroony-bot2_v0.3.4-pre.011-delta.zip +``` + +Le delta final documentaire enregistre ensuite la version validée dans `CHANGELOG.md` et active `0.4.0`. + +## Clôture finale + +Les validations finales ont été exécutées avec succès : + +```text +cargo test -p kb_pipeline: 24 passed +KB_POSTGRES_TEST_URL=... cargo test -p kb_store_pg -- --nocapture: 39 passed +cargo clippy --all-targets: aucun avertissement +``` + +`0.3.4` est reportée dans `CHANGELOG.md`, l’ancien jalon `0.3.5` est absorbé et le prompt actif devient `prompts/019_v0_4_0_decoder_infrastructure.md`. diff --git a/migration/khadhroony-bot2-reference/prompts/019_v0_4_0_decoder_infrastructure.md b/migration/khadhroony-bot2-reference/prompts/019_v0_4_0_decoder_infrastructure.md new file mode 100644 index 0000000..211056f --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/019_v0_4_0_decoder_infrastructure.md @@ -0,0 +1,288 @@ + + + +# Prompt v0.4.0 — infrastructure commune de décodage et matérialisation + +## Statut + +Prompt actif depuis la clôture de `0.3.4` le 7 juillet 2026. + +## Précondition satisfaite + +`0.3.4` est validée et inscrite dans `CHANGELOG.md`. Les validations finales ont confirmé `kb_pipeline` avec 24 tests, `kb_store_pg` avec 39 tests incluant le rollback PostgreSQL réel et `cargo clippy --all-targets` sans avertissement. + +La fondation disponible est : + +```text +kb_sol_raw_transactions + -> kb_sol_core_transactions + -> kb_sol_core_account_keys + -> kb_sol_core_instructions + -> kb_sol_core_inner_instructions + -> kb_sol_core_logs + -> kb_sol_core_balance_changes + -> kb_sol_ops_processing_ledger +``` + +Les campagnes core disposent déjà de la sélection par signatures, pending, slots et programme, du skip version/hash, du force replay, de la concurrence bornée, de l’arrêt coopératif et de `demo_sql_replay_candidates`. + +## Objectif + +Construire l’infrastructure générique permettant aux futurs décodeurs Solana Core, SPL et protocolaires de recevoir un input core contextualisé, de produire des observations décodées versionnées, puis de déclencher des matérialisations explicitement autorisées. + +`0.4.0` ne doit pas encore devenir une version de décodage maximal d’un protocole précis. Elle stabilise les contrats, le dispatch, le ledger, les stores, la couverture et le replay communs. + +## Principes obligatoires + +### Indépendance + +- un décodeur ne dépend pas de PostgreSQL, Tauri ou d’un fournisseur RPC ; +- `kb_decoder_api` dépend uniquement des modèles et contrats backend-agnostiques nécessaires ; +- `kb_materializer_api` reçoit des événements décodés stables et leur contexte ; +- `kb_pipeline` orchestre sélection, dispatch, persistance, replay et arrêt ; +- `kb_store_pg` implémente les repositories concrets. + +### Input contextualisé + +Le contrat de décodage doit fournir au minimum : + +- signature, slot et statut on-chain de la transaction ; +- `err_json` lorsque la transaction a échoué ; +- instruction path stable ; +- program ID ; +- comptes résolus dans l’ordre original ; +- payload brut déterministe et son hash ; +- inner instructions descendantes utiles ; +- logs reliés prudemment ; +- changements de balances pertinents ; +- version du contrat core. + +Réutiliser ou faire évoluer `CoreInstructionReplayInput` au lieu de recréer un modèle parallèle par décodeur. + +### Transactions échouées + +Les transactions on-chain échouées doivent être décodées lorsqu’un input structurel existe. + +Un événement décodé doit conserver explicitement : + +```text +transaction_failed +transaction_error +observation_committed = false lorsque la mutation a été annulée +``` + +Une transaction échouée peut produire : + +- une observation d’instruction tentée ; +- une observation d’événement loggé avant l’erreur ; +- une classification de l’échec ; +- des métriques de compute et de surface appelée. + +Elle ne doit pas produire automatiquement : + +- un trade réussi ; +- une modification de liquidité réussie ; +- un changement de catalogue considéré comme confirmé ; +- une candle normale issue d’un swap annulé. + +Le matérialiseur doit appliquer une politique explicite par famille d’événement, et non une simple hypothèse basée sur la présence d’un log. + +## Contrats de décodeur + +Stabiliser dans `kb_decoder_api` : + +- identité du décodeur ; +- version ; +- programmes et surfaces supportés ; +- capacité à reconnaître un input ; +- résultat `decoded`, `ignored`, `unsupported` ou `failed` ; +- confiance et preuve de décodage ; +- événements décodés typés ; +- diagnostics sans `anyhow` ni `thiserror` ; +- couverture déclarée des instructions, événements et discriminants. + +Le dispatch doit être déterministe par : + +```text +program_id +surface_code éventuel +instruction_path +payload/discriminator +contexte inner/logs +``` + +Aucun dispatch ne doit reposer sur le nom d’une crate ou sur un ordre implicite non documenté. + +## Contrats de matérialisation + +Stabiliser dans `kb_materializer_api` : + +- identité et version du matérialiseur ; +- familles d’événements acceptées ; +- politique pour transaction réussie/échouée ; +- sortie métier typée ; +- idempotence et clés stables ; +- résultat inséré, remplacé, ignoré ou refusé. + +Le matérialiseur ne doit jamais modifier directement les tables core. + +## Persistance et ledger + +Définir les tables actives nécessaires avec le nommage : + +```text +kb_sol_decode_* +kb_sol_mat_* +kb_sol_ops_processing_ledger +``` + +Ne pas créer de schema PostgreSQL applicatif explicite. + +Le ledger doit distinguer au minimum : + +```text +stage = instruction_decode +stage = event_materialization +processor_name +processor_version +input_key +input_hash +status +attempt_count +error_code +error_message +``` + +Le skip doit dépendre de la version du processor et d’un hash déterministe de l’input pertinent. Le force replay doit remplacer uniquement les sorties appartenant au processor et à l’input ciblés. + +## Couverture + +Ajouter une matrice machine-readable permettant de comparer : + +- entrées déclarées par le décodeur ; +- entrées observées dans le corpus ; +- entrées reconnues ; +- entrées décodées ; +- événements matérialisés ; +- erreurs et inconnues ; +- transactions réussies et échouées. + +Ne pas considérer une surface clôturée tant que toutes les instructions et événements connus, y compris historiques et non directement liés au trading, ne sont pas classifiés. + +## Orchestration `kb_pipeline` + +Ajouter un pipeline versionné avec : + +- sélection bornée des instructions core `pending`, `failed` ou `replay_requested` ; +- sélection explicite par signatures, slots, program IDs et instruction paths ; +- dispatch vers zéro, un ou plusieurs décodeurs compatibles selon la politique déclarée ; +- concurrence bornée ; +- arrêt coopératif ; +- skip version/hash ; +- force replay ; +- résumé par processor et statut ; +- persistance atomique des événements décodés et du ledger ; +- appel optionnel des matérialiseurs après succès de persistance du décodage. + +## Démo Tauri + +Ajouter une fenêtre distincte, par exemple `demo_decode_replay`, sans surcharger `demo_core_extraction`. + +Fonctions minimales : + +- sélection de signatures copiées depuis `demo_sql_replay_candidates` ; +- filtre program ID et instruction state ; +- choix des décodeurs disponibles ; +- version visible ; +- force replay ; +- journal et résumé ; +- arrêt ; +- liens vers diagnostics core, decode, ledger et couverture. + +Aucun SQL libre. + +## Tests obligatoires + +Couvrir au minimum : + +1. dispatch exact par program ID ; +2. instruction inconnue classée sans faux décodage ; +3. décodage réussi avec hash stable ; +4. skip même version/hash ; +5. replay après changement de version ; +6. force replay sans duplication ; +7. rollback des événements décodés et du ledger ; +8. transaction échouée décodée comme observation non commitée ; +9. refus de matérialiser un trade réussi depuis une transaction échouée ; +10. arrêt coopératif et candidats non démarrés ; +11. couverture déclarée contre couverture observée ; +12. tests PostgreSQL optionnels sous `KB_POSTGRES_TEST_URL`. + +Les tests unitaires doivent être offline. + +## Hors périmètre + +- décodage maximal System/SPL, prévu dans `0.4.1+` ; +- Pump, Meteora, Raydium, Orca et autres protocoles ; +- Helius `transactionSubscribe` ; +- Yellowstone gRPC ; +- wallet et trading ; +- pools/paires déduits par heuristique depuis de simples account keys. + +## Fichiers à lire en priorité + +- `ROADMAP.md` ; +- `RULES.md` ; +- `docs/CORE_EXTRACTION_CONTRACTS.md` ; +- `docs/CORE_EXTRACTION_SELECTION_AND_REPLAY.md` ; +- `docs/INSTRUCTION_REPLAY_CONTRACTS.md` ; +- `kb_decoder_api/` ; +- `kb_materializer_api/` ; +- `kb_store_core/src/dtos/core_dtos.rs` ; +- `kb_store_core/src/repositories/storage_repositories.rs` ; +- `kb_pipeline/src/core_extraction.rs` ; +- `kb_store_pg/src/queries/core_extraction_queries.rs` ; +- `kb_app_demo/src/demo_core_extraction.rs` ; +- `kb_program_ids/src/lib.rs`. + +## Règles de réalisation + +- Rust 2024 ; +- async-first ; +- tracing obligatoire ; +- erreurs explicites ; +- aucun `?`, `unwrap`, `expect`, `anyhow` ou `thiserror` dans le code de production ; +- aucun `use` sauf pour les traits ; +- appels locaux via `crate::` ; +- `#![warn(missing_docs)]`, `#![deny(unreachable_pub)]` et `#![forbid(unsafe_code)]` ; +- documentation du code en anglais ; +- documentation Markdown en français ; +- headers `file` / `version` incrémentés à chaque modification ; +- aucune ligne vide dans les corps de fonctions ou structures ; +- TS-rs pour les types Tauri ; +- ne pas modifier `CHANGELOG.md` avant validation complète. + +## Validation attendue + +```bash +cargo test -p kb_decoder_api +cargo test -p kb_materializer_api +cargo test -p kb_store_core +cargo test -p kb_pipeline +KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \ + cargo test -p kb_store_pg -- --nocapture +cargo test -p kb_app_demo +(cd kb_app_demo && npm run build) +cargo clippy --all-targets +cargo tauri dev -c kb_app_demo/tauri.conf.json +``` + +## Livraison + +Produire uniquement un zip delta : + +```text +khadhroony-bot2_v0.4.0-pre.001-delta.zip +``` + +Le delta doit contenir `delta.md`, les fichiers ajoutés ou modifiés et la liste explicite des suppressions manuelles. Ne pas produire d’archive complète sauf demande explicite. diff --git a/migration/khadhroony-bot2-reference/prompts/020_v0_4_1_native_solana_programs.md b/migration/khadhroony-bot2-reference/prompts/020_v0_4_1_native_solana_programs.md new file mode 100644 index 0000000..6c6bc55 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/020_v0_4_1_native_solana_programs.md @@ -0,0 +1,691 @@ + + + +# Prompt de session — `0.4.1` programmes Solana natifs, loaders et précompiles + +## 1. Mission + +Poursuivre `khadhroony-bot2` depuis la version validée `0.4.0` et réaliser `0.4.1`. + +Le but est de transformer le classifieur natif introduit en `0.4.0` en un décodeur maximal, vérifiable et rejouable pour les programmes Solana natifs/runtime, les programmes historiques, les loaders et les précompiles. La version doit décoder toutes les variantes connues et officiellement vérifiables, y compris celles qui ne sont pas directement liées au trading. + +Le travail doit rester dans l’architecture existante : + +```text +canonical transaction + -> core extraction + -> contextual instruction replay + -> kb_decoder_solana_core + -> decoded native events + -> optional generic materialization + -> coverage + processing ledger +``` + +Ne pas commencer SPL Token, Token-2022, Pump, Meteora, Raydium, Orca, Jupiter, wallet, exécution ou ingestion live dans cette version. + +## 2. Base de travail et règle de livraison + +La base attendue est la dernière archive complète ou le dernier workspace utilisateur contenant les correctifs validés jusqu’à `0.4.0-pre.011`. + +Avant toute modification : + +1. lire `RULES.md`, `README.md`, `ROADMAP.md` et `CHANGELOG.md` ; +2. lire `prompts/019_v0_4_0_decoder_infrastructure.md` ; +3. auditer les crates et fichiers réellement présents ; +4. vérifier les versions de headers `file:` / `version:` ; +5. ne pas supposer qu’un fichier ou une API existe sans l’avoir inspecté. + +Après l’archive complète initiale, livrer uniquement des ZIP delta. Le delta racine doit suivre une nomenclature du type : + +```text +khadhroony-bot2_v0.4.1-pre.001-delta.zip +``` + +Chaque delta doit contenir : + +- uniquement les fichiers ajoutés ou modifiés ; +- `delta.md` non versionné ; +- `manifest.json` avec chemins, tailles et SHA-256 ; +- la liste explicite des suppressions manuelles, normalement vide. + +Ne fournir une archive complète que sur demande explicite. + +## 3. État validé à préserver + +`0.4.0` est validée avec : + +- `kb_config` : 36 tests ; +- `kb_pipeline` : 41 tests ; +- `kb_store_pg` : 44 tests, PostgreSQL réel compris ; +- `kb_app_demo` : 75 tests ; +- `cargo clippy --all-targets` propre ; +- replay contextualisé borné par programmes compatibles ; +- skip processor/version/hash ; +- force replay ciblé par signatures ; +- autorisation explicite « toutes les signatures » avec limite ; +- matérialisation refusée sans matérialiseur ; +- persistance atomique ledger/decode/mat/couverture ; +- `campaign_id` corrélé entre application, pipeline, store et décodeur ; +- arrêt coopératif avec : + - `started + not_started = selected` ; + - `completed = started` ; +- validation réelle finale : + - 382 inputs Compute Budget sélectionnés ; + - 45 démarrés et terminés ; + - 337 non démarrés ; + - aucun unmatched ; + - aucun échec ; + - 45 dispatchs `unsupported` ; + - `errors.jsonl` vide ; +- couverture actuelle : + - 18 déclarations natives alors exposées ; `pre.008` a corrigé Stake Config et le registre exécutable en contient désormais 17 ; + - 382 observations Compute Budget reconnues ; + - 94 observations System reconnues ; + - aucun faux événement décodé ou matérialisé. + +Ces comportements ne doivent pas régresser. + +## 4. Contraintes normatives du workspace + +Respecter strictement `RULES.md`, notamment : + +- Rust 2024 ; +- async-first lorsqu’un I/O est impliqué ; +- `kb_core::Error` et `kb_core::Result` ; +- pas de `anyhow` ni `thiserror` ; +- pas de `unwrap`, `expect`, panic volontaire ou `unsafe` en production ; +- pas d’opérateur `?` dans les commandes Tauri si les règles locales l’interdisent ; +- retours explicites selon les lints du workspace ; +- `warn(missing_docs)`, `deny(unreachable_pub)`, `forbid(unsafe)` ; +- imports uniquement pour les traits lorsque possible ; +- chemins pleinement qualifiés et `crate::` pour l’intra-crate ; +- noms de fichiers et dossiers ASCII anglais ; +- documentation de code en anglais ; +- Markdown projet en français ; +- header obligatoire `file:` puis `version:` dans chaque fichier modifié ; +- incrémenter exactement une fois la version d’un fichier modifié ; +- aucune ligne vide inutile à l’intérieur des corps selon les conventions du projet ; +- une ligne vide finale ; +- exports TS-rs obligatoires pour les payloads Tauri partagés ; +- aucune logique métier lourde dans `kb_app_demo` ; +- aucun SQL libre exposé dans l’interface ; +- ne pas modifier `CHANGELOG.md` avant validation complète du jalon ; +- utiliser une interface officielle Solana/SPL étroite lorsqu’elle expose le contrat nécessaire ; +- préférer `wincode`, puis Borsh, et conserver un parser local borné lorsque l’interface officielle n’expose que `bincode` ; +- ne jamais ajouter de dépendance directe à `bincode` ; +- toute crate opérationnelle utilise un target `tracing` canonique égal au package Cargo ; +- tout statut decode `failed`/`unsupported`, input unmatched, matérialisation `failed` ou erreur de persistance doit produire un événement `error` structuré ; +- une transaction on-chain échouée mais correctement décodée et un refus de politique conforme ne sont pas des erreurs logicielles. + +## 5. Sources de vérité + +La précision des formats binaires est bloquante. + +### Ordre de priorité + +1. code et contrats du workspace ; +2. sources officielles actuelles Agave/Solana SDK ; +3. crates officielles d’interface Solana correspondant au programme, avec leurs features de sérialisation réellement activées ; +4. tests, encodeurs et fixtures officiels ; +5. documentation officielle ; +6. transactions mainnet réelles déjà présentes ou acquises par le workspace. + +Ne pas utiliser un blog, un parser tiers ou un souvenir comme source normative lorsqu’une source officielle existe. + +### Règles de recherche + +Pour chaque programme : + +- identifier le program ID depuis `kb_program_ids` ; +- identifier la définition officielle actuelle et les variantes historiques ; +- relever le format de sérialisation exact ; +- relever l’ordre exact des comptes, flags signer/writable et règles optionnelles ; +- relever les variantes ajoutées ou supprimées selon les versions ; +- documenter la source et, lorsque possible, le commit/tag consulté ; +- créer une fixture à partir d’un encodeur officiel ou d’une transaction réelle vérifiée ; +- vérifier d’abord si l’interface expose `wincode` ou Borsh ; ne pas activer une feature `bincode` uniquement pour éviter un parser local ; +- ajouter la dépendance officielle uniquement à la crate qui consomme réellement ses types ; +- ne jamais deviner un discriminant, un tag, un offset, une taille ou une sémantique. + +Si deux sources officielles divergent, conserver les deux variantes lorsque les transactions historiques peuvent encore les contenir et documenter la règle de dispatch. + +## 6. Périmètre exact des programmes + +### 6.1 Runtime natif principal + +Décoder maximalement : + +- `11111111111111111111111111111111` — System Program ; +- `Vote111111111111111111111111111111111111111` — Vote Program ; +- `Stake11111111111111111111111111111111111111` — Stake Program ; +- `Config1111111111111111111111111111111111111` — Config Program ; +- `ComputeBudget111111111111111111111111111111` — Compute Budget Program ; +- `AddressLookupTab1e1111111111111111111111111` — Address Lookup Table Program ; +- `ZkE1Gama1Proof11111111111111111111111111111` — ZK ElGamal Proof Program. + +### 6.2 Programmes historiques encore observables + +Décoder/classifier correctement : + +- `Feature111111111111111111111111111111111111` — Feature Program ; +- `ZkTokenProof1111111111111111111111111111111` — ancien ZK Token Proof Program. + +### 6.3 Compte natif historique non exécutable + +- `StakeConfig11111111111111111111111111111111` — compte de configuration Stake historique déprécié. + +Cette adresse doit rester dans `native_well_known_account_ids()` et ne doit produire aucune surface de décodage, aucun dispatch et aucun plan d’exécution. L’alias historique `STAKE_CONFIG_PROGRAM_ID` reste uniquement déprécié au profit de `STAKE_CONFIG_ACCOUNT_ID`. + +### 6.4 Loaders + +Décoder maximalement : + +- `NativeLoader1111111111111111111111111111111` ; +- `BPFLoader1111111111111111111111111111111111` ; +- `BPFLoader2111111111111111111111111111111111` ; +- `BPFLoaderUpgradeab1e11111111111111111111111` ; +- `LoaderV411111111111111111111111111111111111`. + +### 6.5 Précompiles + +Décoder : + +- `Ed25519SigVerify111111111111111111111111111` ; +- `KeccakSecp256k11111111111111111111111111111` ; +- `Secp256r1SigVerify1111111111111111111111111`. + +Les précompiles n’utilisent pas nécessairement une enum sérialisée classique. Leur parser doit valider les compteurs, tables d’offsets, références à d’autres instructions, tailles et bornes avant toute lecture. + +### 6.6 Exclusion explicite + +`MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr` et `Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo` ne font pas partie de `0.4.1`. Memo appartient aux programmes SPL et sera traité dans la série correspondante. + +## 7. Couverture fonctionnelle minimale attendue + +La liste suivante est un guide de couverture, pas une autorisation à coder depuis la mémoire. Les noms et formats définitifs doivent être confirmés dans les sources officielles. + +### 7.1 System Program + +Couvrir au minimum toutes les variantes officielles observables, notamment : + +- create account ; +- assign ; +- transfer ; +- create account with seed ; +- advance nonce account ; +- withdraw nonce account ; +- initialize nonce account ; +- authorize nonce account ; +- allocate ; +- allocate with seed ; +- assign with seed ; +- transfer with seed ; +- upgrade nonce account ; +- toute variante historique ou checked officiellement définie. + +Produire des événements structurés avec : + +- lamports ; +- space ; +- owner ; +- base/seed lorsque présents ; +- autorités ; +- comptes source/destination/nonce ; +- état on-chain succès/échec. + +### 7.2 Compute Budget + +Couvrir toutes les variantes officielles, y compris les formes historiques encore décodables, par exemple : + +- request units deprecated ; +- request heap frame ; +- set compute unit limit ; +- set compute unit price ; +- set loaded accounts data size limit ; +- toute nouvelle variante réellement présente dans la source officielle utilisée. + +Les paramètres doivent rester numériques sans perte. Le décodeur doit reproduire le comportement réel du runtime : lorsqu’Agave utilise une désérialisation unchecked qui accepte des octets suffixes, décoder la valeur officielle minimale et conserver le suffixe par taille, préfixe borné et SHA-256 au lieu de le rejeter ou de l’ignorer silencieusement. + +### 7.3 Address Lookup Table + +Couvrir : + +- create lookup table ; +- freeze lookup table ; +- extend lookup table ; +- deactivate lookup table ; +- close lookup table. + +Conserver slot récent, bump seed, autorité, adresses ajoutées et comptes concernés lorsque présents. + +### 7.4 Vote + +Couvrir toutes les instructions officielles actuelles et historiques observables : + +- initialize account ; +- authorize et authorize checked ; +- vote et variantes compactes ; +- vote/state update ; +- tower sync lorsqu’officiel ; +- withdraw ; +- update validator identity ; +- update commission ; +- seed/checked variants ; +- toute variante ajoutée dans les interfaces officielles. + +Ne pas matérialiser une mise à jour de vote comme mutation réussie lorsque la transaction on-chain a échoué. + +### 7.5 Stake + +Couvrir toutes les instructions officielles actuelles et historiques observables : + +- initialize et initialize checked ; +- authorize, authorize with seed et variantes checked ; +- delegate stake ; +- split ; +- merge ; +- withdraw ; +- deactivate ; +- set lockup et checked ; +- get minimum delegation ; +- deactivate delinquent ; +- redelegate ; +- move stake / move lamports lorsqu’officiels ; +- toute variante historique encore sérialisable. + +Conserver autorités, vote account, custodian, lockup, lamports, seeds et relations de comptes. + +### 7.6 Config et Feature + +Décoder le format exact de leurs mises à jour et données de configuration. Stake Config reste explicitement exclu, car il s’agit d’un compte et non d’une surface d’instruction. Lorsque le programme ne fournit pas une sémantique métier plus fine, produire un événement générique explicite avec : + +- type d’opération officiel ; +- clés signataires/configurées ; +- payload borné ou hashé selon la taille ; +- statut de décodage ; +- aucune interprétation inventée. + +### 7.7 ZK proofs + +Pour `zk_elgamal_proof` et `zk_token_proof` : + +- couvrir chaque type de preuve officiellement déclaré ; +- valider tailles et tags ; +- éviter de recopier inutilement de gros blobs ; +- conserver le type de preuve, tailles, références et hashes nécessaires à l’audit ; +- distinguer preuve reconnue, preuve complètement décodée et payload invalide. + +### 7.8 Loaders + +Couvrir les variantes officielles de chaque loader, notamment selon le loader concerné : + +- write ; +- finalize ; +- initialize buffer ; +- deploy/deploy with max data length ; +- upgrade ; +- set authority et checked ; +- close ; +- extend program ; +- truncate ; +- retract ; +- transfer authority ; +- finalize ; +- migration éventuelle si officiellement définie. + +Conserver les offsets, longueurs, autorités, program/programdata/buffer accounts et paramètres de déploiement. + +### 7.9 Précompiles + +Pour chaque signature décrite : + +- valider le nombre d’entrées ; +- décoder les offsets et instruction indexes ; +- résoudre les références à l’instruction courante ou à une autre instruction du même message ; +- borner toutes les slices ; +- conserver l’algorithme, la clé/adresse, la signature, le message ou son hash selon la taille ; +- conserver le résultat on-chain de la transaction, sans prétendre revalider cryptographiquement si le décodeur ne le fait pas ; +- tester les offsets invalides, références hors bornes, tailles tronquées et compteurs incohérents. + +## 8. Modèle d’événements + +### 8.1 Principes + +Les événements doivent être : + +- versionnés ; +- déterministes ; +- sérialisables sans perte ; +- indépendants de PostgreSQL ; +- indépendants de Tauri ; +- stables pour le replay ; +- explicites sur le programme et la variante ; +- reliés à la signature, au slot, au chemin d’instruction et au contexte outer/inner ; +- explicites sur `transaction_succeeded` ou équivalent ; +- capables de conserver les comptes résolus et rôles utiles. + +Ne pas créer un type unique rempli de champs optionnels incohérents si des enums typées par famille sont plus sûres. + +### 8.2 Statuts + +Conserver les distinctions : + +- `recognized` : programme/surface reconnue ; +- `decoded` : format complet validé et événement produit ; +- `ignored` : variante volontairement non matérialisée mais comprise ; +- `unsupported` : variante officielle pas encore supportée ; +- `failed` : payload invalide, tronqué ou incohérent ; +- `unmatched` : aucun processor compatible, normalement zéro dans une sélection bornée correctement. + +Une reconnaissance du program ID sans décodage du payload ne doit pas incrémenter `decoded`. + +### 8.3 Inconnus + +Pour un discriminant/tag inconnu : + +- ne pas paniquer ; +- produire un diagnostic borné ; +- enregistrer la couverture inconnue ; +- conserver le tag et un hash du payload ; +- ne pas inventer un nom d’instruction ; +- permettre un futur replay après changement de version du décodeur. + +## 9. Matérialisation + +### 9.1 Principe + +La version vise d’abord le décodage maximal. Ajouter une matérialisation uniquement lorsqu’elle apporte une projection générique stable qui n’est pas déjà représentée proprement dans core. + +Candidats autorisés après justification : + +- transferts SOL ; +- création/initialisation/autorisation/withdraw/upgrade de nonce accounts ; +- changements d’autorité stake/vote/config/loader ; +- lifecycle des Address Lookup Tables ; +- lifecycle des programmes/buffers/programdata upgradeables ; +- paramètres Compute Budget demandés par transaction ; +- opérations administratives natives utiles au risque/audit. + +### 9.2 Interdictions + +- ne pas matérialiser une mutation réussie depuis une transaction échouée ; +- ne pas dupliquer sans raison les account keys, balances ou instructions core ; +- ne pas créer une table par instruction ; +- ne pas introduire de schéma PostgreSQL explicite ; +- conserver la convention `kb_sol__` ; +- documenter chaque clé d’idempotence et règle de replacement par version ; +- ne pas ajouter un matérialiseur vide uniquement pour activer le bouton de démo. + +Si aucune projection n’est suffisamment stable dans un premier delta, conserver la matérialisation désactivée et documenter ce choix. + +## 10. Stockage, ledger et couverture + +Préserver les invariants `0.4.0` : + +- persistance atomique ; +- skip exact par processor/version/input hash ; +- force replay ciblé ou global explicitement autorisé ; +- remplacement versionné sans duplication ; +- couverture déclarée synchronisée ; +- couverture observée par programme/surface/entry ; +- séparation succès/échec on-chain ; +- `campaign_id` propagé ; +- aucune réapplication DDL par commande Tauri ; +- rollback complet en cas d’erreur intermédiaire. + +Une migration SQL n’est autorisée que si les types actuels ne peuvent réellement pas représenter les événements ou matérialisations. Préférer les tables génériques versionnées existantes lorsque leur contrat est suffisant. + +## 11. Corpus et fixtures + +### 11.1 Corpus existant + +La base validée contient au moins : + +- 382 observations Compute Budget ; +- 94 observations System ; +- 170 transactions core au moment des validations précédentes ; +- des transactions réussies et échouées ; +- des instructions outer et inner. + +Commencer par System et Compute Budget pour valider rapidement le passage de `unsupported` à `decoded`. + +### 11.2 Acquisition de nouveaux corpus + +Utiliser les outils existants : + +1. `demo_sql_replay_candidates` pour détecter les programmes présents ; +2. `demo_backfill` pour acquérir des signatures réelles ; +3. `demo_core_extraction` pour créer les inputs core ; +4. `demo_decode_replay` pour décoder/rejouer ; +5. diagnostics decode/ledger/couverture pour vérifier les compteurs. + +Pour les programmes rares, utiliser des fixtures synthétiques dérivées des encodeurs officiels. Ne pas bloquer tout le jalon sur l’absence immédiate d’un corpus mainnet, mais distinguer clairement : + +- couvert par fixture officielle ; +- observé dans une transaction réelle ; +- décodé et rejoué sur PostgreSQL réel. + +### 11.3 Fichiers de corpus + +Conserver les fixtures dans les crates concernées ou dans un dossier de fixtures déjà normalisé par le workspace. Ne pas ajouter de payload massif dupliqué. Pour chaque fixture, documenter : + +- programme ; +- variante ; +- succès/échec ; +- outer/inner ; +- source ; +- attendu ; +- raison de la fixture. + +## 12. Tests obligatoires + +### 12.1 Tests unitaires par décodeur + +Pour chaque variante : + +- payload valide ; +- payload tronqué ; +- tag inconnu ; +- champs numériques limites ; +- comptes manquants ou index invalides ; +- transaction réussie ; +- transaction échouée ; +- sérialisation déterministe ; +- stabilité de l’event kind ; +- input hash stable. + +### 12.2 Précompiles + +Ajouter des tests spécifiques : + +- zéro entrée si autorisé/interdit par le format ; +- plusieurs signatures ; +- références à l’instruction courante ; +- références à une autre instruction ; +- index hors bornes ; +- offset hors bornes ; +- chevauchement/troncature ; +- message vide ; +- message volumineux borné/hashé. + +### 12.3 Pipeline + +Préserver et compléter : + +- dispatch exact ; +- skip même version/hash ; +- replay après changement de version ; +- force replay ciblé ; +- replay global autorisé et limité ; +- annulation avant admission ; +- annulation partielle avec `completed = started` ; +- couverture declared/observed/decoded/error ; +- transaction échouée ; +- matérialisation refusée si aucune implémentation active. + +### 12.4 PostgreSQL réel + +Ajouter ou étendre les tests optionnels contrôlés par `KB_POSTGRES_TEST_URL` : + +- persistance d’événements natifs ; +- idempotence ; +- skip ; +- force replay ; +- remplacement de version ; +- rollback atomique ; +- couverture ; +- aucune matérialisation réussie depuis une transaction échouée. + +## 13. Démo Tauri + +Réutiliser `demo_decode_replay`. + +N’ajouter des champs que s’ils sont nécessaires à l’inspection des événements natifs. Ne pas introduire de SQL libre. + +Le résumé doit continuer à afficher : + +- `campaignId` ; +- selected/started/completed/notStarted ; +- unmatched/failed/cancelled ; +- compteurs par processor ; +- decoded/ignored/unsupported/failed ; +- matérialisations. + +Les diagnostics doivent permettre de vérifier : + +- tables decode/mat/ledger ; +- couverture par programme/surface/entry ; +- événements décodés récents ; +- sorties matérialisées récentes si elles existent. + +Conserver les validations frontend du backfill et la désactivation de la matérialisation lorsque la liste de matérialiseurs est vide. + +## 14. Phasage recommandé + +### Phase A — audit et modèle + +- auditer l’existant ; +- établir la matrice officielle programmes/variantes/sources ; +- stabiliser les enums d’événements et erreurs de parsing ; +- ajouter les utilitaires de lecture bornée nécessaires. + +### Phase B — System et Compute Budget + +- implémenter les deux surfaces déjà présentes dans le corpus ; +- replay réel ; +- vérifier `decoded > 0`, `unsupported` en baisse, `failed = 0` sur les variantes connues ; +- valider skip et force replay. + +### Phase C — Address Lookup Table et loaders + +- ajouter ALT ; +- ajouter loaders deprecated/v2/upgradeable/v4/native ; +- fixtures officielles et corpus réel lorsque disponible ; +- matérialisation lifecycle si justifiée. + +### Phase D — Vote, Stake, Config et programmes historiques + +- couvrir les enums complètes ; +- traiter les variantes checked/seed/historiques ; +- ajouter corpus et tests de transactions échouées. + +### Phase E — Précompiles et ZK + +- implémenter les parsers à offsets bornés ; +- ajouter tests adversariaux ; +- vérifier absence de panic et erreurs explicites. + +### Phase F — matérialisation, diagnostics et clôture + +- ajouter uniquement les projections justifiées ; +- replay global du corpus ; +- contrôler couverture et ledger ; +- vérifier absence de régression ; +- mettre à jour README/ROADMAP/docs ; +- mettre à jour CHANGELOG uniquement après validation utilisateur. + +Chaque phase peut produire un delta `pre.xxx`. Ne pas attendre la fin complète pour livrer une correction bloquante, mais conserver une base cohérente à chaque delta. + +## 15. Validations de clôture + +Adapter les comptes de tests au résultat réel, sans les inventer. + +Exécuter au minimum : + +```bash +cargo test -p kb_program_ids +cargo test -p kb_decoder_api +cargo test -p kb_decoder_solana_core +cargo test -p kb_materializer_api +cargo test -p kb_pipeline +cargo test -p kb_store_core + +KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \ + cargo test -p kb_store_pg -- --nocapture + +cargo test -p kb_app_demo +cargo clippy --all-targets + +(cd kb_app_demo && npm run build) + +cargo tauri dev -c kb_app_demo/tauri.conf.json +``` + +Selon les crates réellement modifiées, ajouter leurs tests spécifiques. + +### Scénarios Tauri obligatoires + +1. replay System/Compute Budget sur signatures explicites ; +2. second replay sans force : tous les inputs courants doivent être skipped ; +3. force replay ciblé : aucun skip ; +4. replay global avec petite limite : limite strictement respectée ; +5. transaction on-chain échouée : événement décodé d’intention/échec, aucune mutation matérialisée réussie ; +6. tag inconnu ou fixture invalide : `failed`/unknown correctement compté sans panic ; +7. annulation : `completed = started` et `started + not_started = selected` ; +8. nouvelle campagne après annulation ; +9. même `campaign_id` dans app/pipeline/store/décodeur ; +10. `error.jsonl` global et les `error.jsonl` par crate vides hors erreurs volontairement injectées et contrôlées ; +11. diagnostics de couverture cohérents ; +12. absence de duplication après replay/force replay. + +## 16. Documentation à maintenir + +Mettre à jour au fil des deltas : + +- `README.md` ; +- `ROADMAP.md` ; +- README des crates touchées ; +- `docs/NATIVE_SOLANA_PROGRAMS.md` ; +- `docs/DECODER_MATERIALIZATION_CONTRACTS.md` si le contrat change ; +- `docs/TRACING_CONTRACT.md`, `docs/LOGGING.md` et `docs/SOLANA_INTERFACE_DEPENDENCIES.md` si leur matrice change ; +- document de matrice/couverture si distinct ; +- exemples ou procédures de corpus. + +Ne modifier `CHANGELOG.md` qu’après validation complète et explicite de l’utilisateur. + +## 17. Critères de sortie de `0.4.1` + +Le jalon est clôturable uniquement si : + +- chaque program ID du périmètre possède une déclaration de couverture ; +- toutes les variantes officiellement identifiées sont décodées ou explicitement documentées comme non observables/non supportables avec justification ; +- aucun discriminant n’est inventé ; +- les payloads invalides ne paniquent pas ; +- System et Compute Budget sont validés sur corpus réel ; +- les autres familles sont couvertes au minimum par fixtures officielles vérifiables ; +- le ledger skip/force/version fonctionne ; +- les transactions échouées respectent la politique de non-mutation ; +- la couverture et les diagnostics sont cohérents ; +- les tests PostgreSQL réels passent ; +- le build frontend passe ; +- clippy est propre ; +- les scénarios Tauri passent ; +- aucune régression `0.4.0` n’est constatée ; +- la documentation est alignée ; +- l’utilisateur a explicitement validé la clôture. + +## 18. Règle de conduite pendant la session + +Ne pas demander confirmation pour chaque détail mineur. Inspecter d’abord le workspace et faire le meilleur choix compatible avec les règles et l’architecture. Signaler rapidement toute incohérence détectée, livrer des deltas minimaux et réutilisables, et ne jamais masquer une validation non exécutée. + +Ne pas modifier ou supprimer des données PostgreSQL utilisateur sans commande explicite. Les scripts destructifs doivent rester manuels, documentés et hors exécution automatique. diff --git a/migration/khadhroony-bot2-reference/prompts/021_v0_4_2_executor_solana_core.md b/migration/khadhroony-bot2-reference/prompts/021_v0_4_2_executor_solana_core.md new file mode 100644 index 0000000..53e7780 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/021_v0_4_2_executor_solana_core.md @@ -0,0 +1,281 @@ + + + +# Prompt de session — `0.4.2` infrastructure d’exécution et `kb_executor_solana_core` + +## 1. Mission + +Reprendre `khadhroony-bot2` après la clôture validée de `0.4.1` et démarrer `0.4.2`. + +Le but de `0.4.2` est de transformer les exécuteurs réservés en une infrastructure d’exécution sûre, testable et explicitement bornée, puis de couvrir progressivement toutes les instructions natives officiellement appelables dans `kb_executor_solana_core`. + +Les crates produites doivent rester universelles et réutilisables par des CLI, workers, applications Tauri, outils d’administration ou futurs exécutables non liés au trading. Une opération dangereuse doit être implémentée et testée avec une politique renforcée ; elle peut rester non exposée dans `kb_app_demo`. + +`0.4.2` ne doit pas commencer les décodeurs SPL, Pump, Meteora, Raydium, Orca, Jupiter ou DEX. Le jalon porte sur l’exécution native Solana Core uniquement. + +## 2. Base de travail + +La base attendue est le workspace complet après application et validation de : + +```text +khadhroony-bot2_v0.4.1-pre.022-full.zip +khadhroony-bot2_v0.4.1-pre.023-delta.zip +``` + +Avant modification : + +1. lire `RULES.md`, `README.md`, `ROADMAP.md` et `CHANGELOG.md` ; +2. lire `docs/EXECUTION_MODEL.md` ; +3. lire `docs/NATIVE_SOLANA_CLOSURE_AUDIT.md` ; +4. lire `docs/NATIVE_SOLANA_MATERIALIZATION_AUDIT.md` ; +5. lire `prompts/020_v0_4_1_native_solana_programs.md` pour comprendre les surfaces décodées ; +6. auditer les crates `kb_executor_solana_core`, `kb_wallet`, `kb_rpc`, `kb_pipeline`, `kb_app_demo`, `kb_decoder_solana_core` et les crates de matérialisation natives ; +7. ne pas supposer qu’un type, builder, fonction ou dépendance existe sans l’avoir inspecté. + +Après l’archive complète initiale, livrer uniquement des ZIP delta. + +## 3. État validé à préserver + +`0.4.1` est validée avec : + +```text +kb_program_ids 5 tests +kb_decoder_solana_core 114 tests +kb_materializer_lifecycle 16 tests +kb_materializer_admin 7 tests +kb_materializer_compliance_audit 7 tests +kb_materializer_staking 8 tests +kb_config 36 tests +kb_pipeline 45 tests +kb_store_pg 45 tests avec PostgreSQL réel +kb_app_demo 78 tests +cargo clippy --all-targets propre +``` + +Les replays mainnet de clôture rapportent notamment : + +```text +System 817 decoded, 258 outputs, 52 expected refusals +Config 96 decoded, 96 outputs +Stake 448 decoded, 447 outputs, 1 expected refusal +Vote 600 decoded, 600 outputs +ComputeBudget 2746 decoded, 1603 transaction profiles +ZkElGamal 151 decoded, 139 context outputs +ALT 104 decoded, 104 lifecycle outputs +``` + +Les transactions échouées restent décodées comme intentions ou diagnostics. Les matérialiseurs mutables ne produisent pas de mutation réussie pour une transaction non commitée. + +## 4. Contraintes normatives + +Respecter strictement les règles du workspace : + +- Rust 2024 ; +- async-first pour l’I/O ; +- `kb_core::Error` et `kb_core::Result` ; +- pas de `anyhow` ni `thiserror` ; +- pas de `unwrap`, `expect`, `unsafe` ou panic de production ; +- retours explicites ; +- `warn(missing_docs)`, `deny(unreachable_pub)`, `forbid(unsafe_code)` ; +- imports seulement pour les traits lorsque possible ; +- chemins pleinement qualifiés et `crate::` en intra-crate ; +- documentation de code en anglais ; +- Markdown projet en français ; +- header `file:` puis `version:` dans chaque fichier modifié ; +- version de fichier incrémentée exactement une fois ; +- aucun `bincode` direct ; +- aucune logique métier lourde dans `kb_app_demo` ; +- aucun SQL libre exposé dans l’interface ; +- `CHANGELOG.md` inchangé jusqu’à validation complète du jalon ; +- toute crate opérationnelle utilise un target `tracing` canonique égal au package Cargo. + +## 5. Principes d’exécution + +Un décodeur lit une transaction passée. Un exécuteur prépare une opération future. + +L’exécution doit être découpée en étapes explicites : + +```text +intent + -> execution plan + -> policy checks + -> transaction build + -> simulation / dry-run + -> signature + -> send + -> confirmation + -> post-execution decode replay validation +``` + +Ne jamais mélanger construction, signature, envoi et validation dans une seule fonction opaque. + +Par défaut : + +- dry-run activé ; +- simulation obligatoire ; +- mainnet désactivé ou exigeant une confirmation explicite ; +- plafond de lamports/frais obligatoire pour les opérations dépensières ; +- cluster attendu vérifié ; +- signataires requis listés avant signature ; +- blockhash ou nonce durable contrôlé ; +- post-validation possible via replay decode/materialization. + +## 6. Objectifs `0.4.2` + +### 6.1 API d’exécution + +Stabiliser ou créer les contrats nécessaires dans `kb_execution_api` et `kb_execution_safety` si ces crates existent déjà, ou les créer uniquement si le ROADMAP et le workspace les attendent réellement. + +Contrats attendus : + +- intent typé ; +- plan d’exécution ; +- instruction planifiée ; +- comptes et signataires requis ; +- politique de cluster ; +- politique de simulation ; +- coût/plafond ; +- résultat de simulation ; +- résultat d’envoi ; +- diagnostic post-exécution. + +Si les crates n’existent pas encore, privilégier un delta minimal et cohérent plutôt qu’un design trop large. + +### 6.2 `kb_executor_solana_core` + +Remplacer tout support générique `Maybe` par des capacités exactes : + +```text +Supported(operation) +Unsupported(reason) +``` + +Premières opérations candidates : + +- System transfer SOL ; +- System create account contrôlé ; +- System allocate ; +- System assign ; +- durable nonce initialize/advance/authorize/withdraw, seulement si la politique de sécurité est claire ; +- Compute Budget set compute unit limit ; +- Compute Budget set compute unit price. + +Après les premières opérations, étendre la bibliothèque à : + +- toutes les variantes System et durable nonce officiellement constructibles ; +- Address Lookup Table ; +- Stake et Vote avec politiques d’autorité dédiées ; +- loaders et changements d’autorité ; +- Config, Feature, ZK et Slashing ; +- précompiles lorsqu’un constructeur officiel ou un layout runtime vérifiable existe. + +`Unsupported(reason)` doit rester temporaire et justifié. La dangerosité impose une politique, des confirmations et des tests cluster ; elle ne constitue pas une raison de supprimer définitivement le builder. + +### 6.3 Builders officiels + +Chaque instruction construite doit être comparée à un builder officiel ou à un layout officiel vérifiable. Ne pas inventer de layout. + +Pour les opérations supportées : + +- tester le program ID ; +- tester le nom d’opération ; +- tester les comptes ; +- tester le payload ; +- tester les signataires ; +- tester les limites et erreurs de politique ; +- tester le refus sur cluster ou contexte interdit. + +### 6.4 Wallet temporaire + +Ajouter dans `kb_wallet` un signer de laboratoire universel avant l’orchestration d’envoi : + +- génération en mémoire ; +- persistance optionnelle sous `wallets/` avec alias validé ; +- création sans écrasement ; +- format keypair Solana standard ; +- permissions privées et exclusion Git ; +- aucune clé privée dans les logs, TS-rs, Tauri ou PostgreSQL ; +- profils mainnet fournis sans wallet temporaire actif. + +Le wallet temporaire persistant sert à conserver la même adresse entre les sessions devnet. Les backends chiffrés, coffre système et hardware wallet restent prévus pour la couche wallet de production. + +### 6.5 Démo Tauri + +Ajouter ou étendre une démo uniquement si nécessaire, sans logique métier lourde : + +- visualiser un plan ; +- afficher les signataires requis ; +- lancer une simulation ; +- ne pas envoyer réellement sur mainnet sans confirmation explicite ; +- ne pas exposer de SQL libre. + +Si l’UI n’est pas indispensable dans un premier delta, valider d’abord les crates d’API et d’exécution par tests unitaires. + +## 7. Matérialisations et validation post-exécution + +Une exécution réussie doit pouvoir être validée après coup par le pipeline existant : + +```text +signature envoyée + -> backfill / canonical insert + -> core extraction + -> decode replay + -> materialization +``` + +Ne pas faire dépendre l’exécuteur du décodeur. La validation post-exécution peut être orchestrée par `kb_pipeline` ou `kb_app_demo`, mais le builder d’instruction doit rester indépendant. + +## 8. Interdictions dans `0.4.2` + +Ne pas : + +- commencer SPL Memo, SPL Token ou Token-2022 ; +- ajouter Pump/Meteora/Raydium/Orca/Jupiter ; +- activer un exécuteur mainnet par défaut ; +- envoyer une transaction sans simulation explicite ; +- cacher les signataires requis ; +- créer une abstraction « supporté peut-être » ; +- exposer dans `kb_app_demo` une opération dangereuse sans besoin opérateur explicite, politique dédiée et validation cluster ; +- utiliser `bincode` directement ; +- déplacer de logique métier dans `kb_app_demo`. + +## 9. Validations attendues + +Selon les fichiers touchés, exécuter au minimum : + +```bash +cargo test -p kb_executor_solana_core +cargo test -p kb_pipeline +cargo test -p kb_app_demo +cargo test -p kb_config +cargo clippy --all-targets +``` + +Si `kb_store_pg` ou l’orchestration post-exécution est touchée : + +```bash +KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \ + cargo test -p kb_store_pg -- --nocapture +``` + +Si Tauri est touché : + +```bash +cargo tauri dev -c kb_app_demo/tauri.conf.json +``` + +## 10. Livraison + +Livrer des deltas incrémentaux nommés : + +```text +khadhroony-bot2_v0.4.2-pre.NNN-delta.zip +``` + +Le ZIP doit contenir : + +- uniquement les fichiers ajoutés ou modifiés ; +- `delta.md` ; +- `manifest.json` ; +- aucune archive complète sauf demande explicite ; +- aucune modification de `CHANGELOG.md` avant validation finale du jalon. diff --git a/migration/khadhroony-bot2-reference/prompts/022_v0_4_3_spl_memo.md b/migration/khadhroony-bot2-reference/prompts/022_v0_4_3_spl_memo.md new file mode 100644 index 0000000..a1cb43f --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/022_v0_4_3_spl_memo.md @@ -0,0 +1,401 @@ + + + +# Prompt de session — `0.4.3` SPL Memo v1, v3 et v4 + +## 1. Mission + +Reprendre `khadhroony-bot2` après la clôture validée de `0.4.2` et démarrer `0.4.3`. + +Le jalon doit traiter **complètement** la surface SPL Memo avant de commencer SPL Token : + +```text +program IDs v1 / v3 / v4 + -> décodage maximal + -> projection métier explicite + -> exécuteur typé pour les générations réellement constructibles + -> simulation et validation post-exécution +``` + +L'objectif n'est pas seulement de reconnaître une chaîne UTF-8. Il faut produire un contrat stable pour l'audit, la recherche, la corrélation de paiements, les annotations de transaction et les futures contraintes Token-2022 MemoTransfer. + +Le worker d'acquisition historique décrit dans `docs/HISTORICAL_DATA_ACQUISITION_PLAN.md` reste différé et hors périmètre. Ne créer aucune crate historique et ne consommer aucun temps du jalon sur Explorer, Solscan, BigQuery ou Old Faithful. + +## 2. Base de travail + +La base attendue est le commit Git qui clôture `0.4.2` et contient notamment : + +- `ROADMAP.md` avec `0.4.3 — SPL Memo v1, v3 et v4` ; +- `RULES.md` avec la règle de livraison `delta-fix-NNN` ; +- `prompts/022_v0_4_3_spl_memo.md` ; +- `docs/HISTORICAL_DATA_ACQUISITION_PLAN.md` hors ROADMAP actif. + +Avant toute modification : + +1. lire `RULES.md`, `README.md`, `ROADMAP.md` et `CHANGELOG.md` ; +2. lire `prompts/020_v0_4_1_native_solana_programs.md` et `prompts/021_v0_4_2_executor_solana_core.md` pour préserver les contrats de décodage, matérialisation et exécution ; +3. lire `docs/EXECUTION_MODEL.md`, `docs/SOLANA_INTERFACE_DEPENDENCIES.md` et les audits de clôture `0.4.1`/`0.4.2` ; +4. auditer `kb_decoder_spl_memo`, `kb_executor_spl_memo`, `kb_program_ids`, `kb_decoder_api`, `kb_materializer_api`, les matérialiseurs existants, `kb_execution_api`, `kb_execution_safety`, `kb_execution_solana`, `kb_pipeline`, `kb_rpc`, `kb_store_core`, `kb_store_pg` et `kb_app_demo` ; +5. inspecter les types et conventions réellement présents ; ne pas supposer qu'un helper ou schéma existe ; +6. vérifier la version exacte de `spl-memo-interface` résolue par Cargo et les sources officielles/historiques des trois générations avant d'écrire le wire. + +## 3. État validé à préserver + +La clôture `0.4.2` a été validée avec : + +```text +kb_program_ids 5 tests +kb_decoder_solana_core 116 tests +kb_executor_solana_core 88 tests +kb_execution_api 22 tests +kb_execution_safety 15 tests +kb_execution_solana 12 tests +kb_rpc 113 tests +kb_pipeline 56 tests +kb_config 41 tests +kb_app_demo 88 tests +kb_store_pg 45 tests avec PostgreSQL réel +cargo clippy --all-targets propre +cargo tauri dev validé avec Vite +``` + +Le parcours Devnet réel System transfer a été validé avec simulation, signature, envoi, confirmation, insertion canonique, extraction core et decode replay. Les 52 méthodes HTTP et neuf paires WS standards restent couvertes ; les méthodes WS instables restent activables par endpoint et désactivables automatiquement après rejet du nœud. + +Aucune régression de ces invariants n'est acceptable. + +## 4. Sources normatives + +Utiliser en priorité : + +- le registre local `kb_program_ids` pour les trois IDs déjà vérifiés ; +- le dépôt officiel Memo : ; +- la crate `spl-memo-interface` déjà cataloguée dans le workspace ; +- les tags et sources historiques officiels pour v1 et v3 ; +- le code runtime officiel pour la validation UTF-8 et les comptes signataires ; +- des transactions réelles et des fixtures synthétiques contrôlées. + +Le dépôt officiel publie actuellement une interface `2.1.x` et une génération de programme v4. Ne pas mettre à jour automatiquement une dépendance ou considérer les trois générations identiques sans audit. Si l'interface officielle ne construit que l'ID courant, les instructions historiques ne peuvent être reproduites manuellement qu'après preuve que leur layout exact est le même. + +## 5. Program IDs + +Le jalon couvre exactement : + +```text +SPL_MEMO_V1_PROGRAM_ID = Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo +SPL_MEMO_V3_PROGRAM_ID = MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr +SPL_MEMO_V4_PROGRAM_ID = Memo4c2pN8afCj432Lb7RMVKi9PbQnnW7ewFFaV3oAH +``` + +`MEMO_PROGRAM_ID` reste l'alias historique déjà défini dans `kb_program_ids`; ne pas l'utiliser lorsqu'une génération exacte est nécessaire. + +## 6. Contraintes normatives du workspace + +Respecter strictement : + +- Rust 2024 ; +- async-first pour l'I/O ; +- `kb_core::Error` et `kb_core::Result` ; +- aucun `anyhow`, `thiserror`, `unwrap`, `expect`, `unsafe`, panic de production ou opérateur `?` dans les nouveaux chemins de production ; +- `warn(missing_docs)`, `deny(unreachable_pub)`, `forbid(unsafe_code)` ; +- imports de traits uniquement lorsque possible, chemins pleinement qualifiés et `crate::` en intra-crate ; +- commentaires/docstrings Rust en anglais, Markdown projet en français ; +- header `file:` puis `version:` avec un seul incrément par fichier modifié ; +- aucun `bincode` direct ; +- aucune logique métier lourde dans `kb_app_demo` ; +- `CHANGELOG.md` inchangé avant validation complète du jalon ; +- `TRACING_TARGET` canonique dans `src/constants.rs` pour toute crate opérationnelle ; +- aucune valeur JSON Tauri exportée en `bigint` lorsqu'elle traverse `invoke`, `emit` ou `JSON.stringify`. + +## 7. Audit préalable obligatoire + +Avant d'implémenter, produire un tableau de vérité pour v1, v3 et v4 : + +- ID et statut actuel/historique ; +- format exact de l'instruction ; +- validation UTF-8 ; +- comportement du payload vide ; +- traitement des comptes ; +- obligation `is_signer` ; +- prise en compte de l'ordre et des doublons ; +- logs runtime ; +- différences de limites ou coûts ; +- disponibilité d'un builder officiel ; +- invocabilité actuelle sur Localnet/Devnet/Mainnet ; +- corpus réel disponible. + +Toute différence entre générations doit apparaître explicitement dans les types, la matrice de couverture et les tests. Ne pas fusionner v1/v3/v4 sous un comportement supposé commun. + +## 8. `kb_decoder_spl_memo` + +### 8.1 Remplacement du scaffold + +- remplacer `DecoderSupport::Maybe` par `Yes` pour les trois IDs exacts et `No` pour les autres ; +- déplacer la cible tracing legacy vers `src/constants.rs` avec la valeur exacte `kb_decoder_spl_memo` ; +- ajouter `tracing` uniquement si des événements runtime réels sont émis ; +- ne produire aucun événement pour une observation hors surface. + +### 8.2 Événement décodé + +Définir un contrat stable qui conserve au minimum : + +- génération Memo v1/v3/v4 ; +- Program ID exact ; +- texte lorsque le payload est UTF-8 valide ; +- longueur exacte en octets ; +- SHA-256 du payload ; +- préfixe hex borné pour diagnostic/replay ; +- liste ordonnée des comptes fournis ; +- pour chaque compte : pubkey, signer, writable et position ; +- liste des signataires exigés/observés selon le contrat de la génération ; +- chemin outer/inner et index stable ; +- succès/échec de la transaction ; +- `committed` ou intention non commitée ; +- statut de validation UTF-8 et de validation des signataires ; +- diagnostic borné pour une tentative invalide. + +Le texte complet peut être conservé si sa taille est intrinsèquement bornée par la transaction Solana et par une limite explicite du décodeur. La limite et la stratégie de troncature éventuelle doivent être testées et documentées. + +### 8.3 Transactions échouées + +Une transaction échouée doit pouvoir produire une intention structurée lorsque le payload et les comptes sont suffisamment lisibles, sans prétendre que le runtime a validé UTF-8, vérifié les signataires ou commit l'annotation. + +Distinguer clairement : + +- payload valide et transaction réussie ; +- payload valide mais transaction échouée ; +- payload UTF-8 invalide ; +- compte requis non signer ; +- observation tronquée ou mal formée ; +- génération/ID inconnu. + +### 8.4 Couverture et corpus + +Créer une matrice machine-readable dédiée, par exemple : + +```text +docs/SPL_MEMO_MATRIX.json +``` + +Elle doit être vérifiée par un test Rust et couvrir les trois program IDs, les cas de succès, les erreurs et les différences de génération. + +Corpus minimal : + +- payload vide ; +- ASCII ; +- Unicode multi-octets ; +- taille proche de la limite pratique ; +- octets UTF-8 invalides ; +- zéro, un et plusieurs signataires ; +- compte non signer ; +- comptes supplémentaires ou dupliqués si acceptés par le runtime ; +- outer instruction ; +- inner/CPI ; +- transaction réussie ; +- transaction échouée ; +- au moins une signature réelle par génération lorsque disponible. + +## 9. Matérialisation + +Le décodage maximal doit être stabilisé avant la projection. + +Le domaine attendu est une **annotation de transaction** consultable. Ne pas détourner arbitrairement `metadata`, `admin`, `lifecycle` ou `trades`. + +Après audit des contrats existants : + +1. utiliser un matérialiseur existant seulement si son domaine couvre explicitement les annotations de transaction ; +2. sinon créer une crate dédiée, par exemple `kb_materializer_transaction_annotations`, avec un contrat réutilisable au-delà de Memo ; +3. garder les faits de vérification/audit séparables du texte si plusieurs consommateurs sont nécessaires. + +Projection minimale : + +- signature et instruction path ; +- génération et Program ID ; +- texte ou représentation bornée ; +- longueur et hash ; +- signataires vérifiés ; +- état committed ; +- provenance du processor et version ; +- clé d'idempotence déterministe. + +Règles : + +- aucune projection réussie mutable pour une transaction non commitée ; +- replay identique = skip/idempotence ; +- changement de version = remplacement déterministe selon le ledger ; +- ne pas dupliquer les logs ou données core déjà stockés ; +- documenter explicitement toute donnée laissée uniquement dans l'événement décodé. + +## 10. `kb_executor_spl_memo` + +### 10.1 Contrat typé + +Remplacer le plan réservé par une voie typée fondée sur `kb_execution_api`. + +Types candidats : + +```text +SplMemoGeneration +SplMemoOperation::AddMemo +SplMemoExecutionIntent +SplMemoSigner +``` + +Le contrat doit permettre : + +- choix explicite de la génération ; +- payload UTF-8 validé avant plan ; +- signataires ordonnés et déclarés ; +- absence de compte writable inventé ; +- limites de taille explicites ; +- coût de dépense nul hors frais réseau ; +- `Supported`/`Unsupported(reason)` exact par génération. + +### 10.2 Builders et wire + +- utiliser `spl-memo-interface` lorsque son builder et son Program ID correspondent exactement à la génération demandée ; +- comparer les instructions produites aux builders officiels ; +- pour une génération historique sans builder courant, construire manuellement uniquement après validation du layout depuis les sources officielles ; +- le payload Memo est constitué des octets exacts du message, sans préfixe, discriminator, Borsh ou `bincode` inventé ; +- chaque compte signer doit apparaître avec les flags officiels exacts ; +- rejeter les doublons ou formes anormales seulement si le contrat officiel les rejette réellement. + +### 10.3 Politiques + +- simulation obligatoire ; +- dry-run par défaut ; +- aucune activation Mainnet par défaut ; +- signataires visibles avant signature ; +- fee payer déclaré ; +- plafond de frais appliqué par l'infrastructure commune ; +- aucune clé privée dans les logs, Tauri ou les payloads JSON ; +- post-validation par hydratation, core extraction et decode replay Memo. + +Les générations historiques peuvent rester `Unsupported(reason)` côté exécuteur si leur invocabilité actuelle ou leur builder ne peut pas être prouvé. Elles restent néanmoins obligatoires côté décodeur. + +## 11. Pipeline, store et démo + +- enregistrer le décodeur et le matérialiseur dans le replay commun ; +- ajouter les déclarations de couverture et tests de sélection ; +- toucher `kb_store_pg` uniquement si une nouvelle projection nécessite réellement un contrat de persistance ; +- conserver les écritures atomiques avec le ledger decode/materialize ; +- utiliser `demo_decode_replay` pour les corpus et résumés ; +- ne créer une nouvelle fenêtre Tauri que si l'exécution Memo Devnet ne peut pas être validée avec les surfaces existantes ; +- une démo d'exécution peut rester backend/test opt-in si l'UI n'apporte pas de valeur supplémentaire. + +## 12. Validation Devnet + +Ajouter un test opt-in ou un petit parcours opérateur qui : + +1. charge le wallet temporaire Devnet existant ; +2. construit un Memo de la génération courante validée ; +3. simule ; +4. signe et envoie seulement avec autorisation explicite ; +5. confirme ; +6. appelle `getTransaction` ; +7. insère la transaction canonique ; +8. exécute core extraction ; +9. exécute decode replay Memo ; +10. vérifie la projection et l'idempotence. + +Ne pas exiger l'envoi des générations historiques si elles ne sont plus officiellement déployées ou supportées sur Devnet. + +## 13. Documentation attendue + +Mettre à jour au minimum : + +```text +kb_decoder_spl_memo/README.md +kb_executor_spl_memo/README.md +README.md +ROADMAP.md +docs/SOLANA_INTERFACE_DEPENDENCIES.md +docs/SPL_MEMO_MATRIX.json +``` + +Ajouter le README du matérialiseur créé ou modifié. Documenter : + +- différences v1/v3/v4 ; +- types d'événements ; +- projection choisie ; +- opérations exécutables ; +- limites et refus ; +- corpus et signatures réelles utilisées ; +- résultats Localnet/Devnet ; +- absence volontaire de projection ou d'exécution, le cas échéant. + +`CHANGELOG.md` ne doit être modifié qu'après validation complète de `0.4.3`. + +## 14. Interdictions + +Ne pas : + +- commencer SPL Token, ATA, Token-2022, Stake Pool, Pump, Meteora, Raydium, Orca ou Jupiter ; +- créer ou commencer W2/historical acquisition ; +- fusionner Memo dans `kb_decoder_solana_core` ; +- considérer les trois IDs comme identiques sans preuve ; +- utiliser les logs RPC comme seule source du texte lorsque l'instruction est disponible ; +- inventer un discriminator ou une structure sérialisée ; +- exposer un envoi Mainnet par défaut ; +- produire une projection committed pour une transaction échouée ; +- ajouter du SQL libre à l'UI ; +- générer des bindings TypeScript contenant des `bigint` dans les frontières Tauri JSON. + +## 15. Validations minimales + +Selon les fichiers touchés : + +```bash +cargo test -p kb_program_ids +cargo test -p kb_decoder_spl_memo +cargo test -p kb_materializer_transaction_annotations # si créée +cargo test -p kb_executor_spl_memo +cargo test -p kb_execution_api +cargo test -p kb_execution_safety +cargo test -p kb_execution_solana +cargo test -p kb_pipeline +cargo test -p kb_rpc +cargo test -p kb_app_demo +cargo clippy --all-targets +``` + +Si le store ou la projection est touché : + +```bash +KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \ + cargo test -p kb_store_pg -- --nocapture +``` + +Si Tauri est touché : + +```bash +cargo tauri dev -c kb_app_demo/tauri.conf.json +``` + +Le serveur Vite lancé par Tauri constitue la validation frontend normale ; ne pas imposer un `npm run build` séparé sans besoin particulier. + +## 16. Livraison + +Livrer des tranches fonctionnelles sous la forme : + +```text +khadhroony-bot2_v0.4.3-pre.NNN-delta.zip +``` + +Après la livraison d'un delta `pre.NNN`, tout correctif conserve le même numéro : + +```text +khadhroony-bot2_v0.4.3-pre.NNN-delta-fix-001.zip +khadhroony-bot2_v0.4.3-pre.NNN-delta-fix-002.zip +``` + +Ne pas incrémenter `NNN` pour réparer une archive déjà livrée. Le numéro suivant est réservé à une nouvelle tranche fonctionnelle. + +Chaque archive doit contenir : + +- uniquement les fichiers ajoutés ou modifiés ; +- `delta.md` non versionné ; +- `manifest.json` ; +- les suppressions manuelles explicitement listées ; +- les validations exécutées et non exécutées ; +- aucune archive complète sauf demande explicite ; +- aucune modification de `CHANGELOG.md` avant validation finale du jalon. diff --git a/migration/khadhroony-bot2-reference/prompts/023_v0_4_4_spl_token.md b/migration/khadhroony-bot2-reference/prompts/023_v0_4_4_spl_token.md new file mode 100644 index 0000000..7cb4ea5 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/023_v0_4_4_spl_token.md @@ -0,0 +1,447 @@ + + + +# Prompt de session — `0.4.4` SPL Token + +## 1. Mission + +Reprendre `khadhroony-bot2` après la clôture validée de `0.4.3` et démarrer `0.4.4`. + +Le jalon couvre exclusivement le programme SPL Token classique : + +```text +TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA + -> audit du wire historique et courant + -> décodage maximal des instructions outer et inner + -> projections métier explicites et idempotentes + -> exécuteur typé pour toutes les opérations officiellement constructibles + -> simulation, politiques d’autorité/multisig et validation post-exécution +``` + +Le décodeur doit comprendre les instructions anciennes, actuelles et officiellement publiées même lorsqu’une opération récente n’est pas encore déployée sur tous les clusters. L’exécuteur est une bibliothèque universelle : il ne doit pas interdire artificiellement un cluster. La disponibilité du programme et d’une instruction est prouvée par l’audit de déploiement et la simulation ; les applications et politiques communes décident ensuite si un envoi est autorisé. + +Ne pas commencer SPL Associated Token Account, Token-2022, ses extensions, Transfer Hook, MemoTransfer, Stake Pool ou une surface DEX. Le worker décrit dans `docs/HISTORICAL_DATA_ACQUISITION_PLAN.md` reste différé et hors périmètre. + +## 2. Base de travail + +La base attendue est le commit Git qui clôture `0.4.3` et contient notamment : + +- `ROADMAP.md` avec `0.4.3` coché et `0.4.4 — SPL Token` actif ; +- `CHANGELOG.md` avec l’entrée validée `0.4.3` ; +- `prompts/022_v0_4_3_spl_memo.md` conservé comme historique ; +- `prompts/023_v0_4_4_spl_token.md` comme contrat actif ; +- `docs/HISTORICAL_DATA_ACQUISITION_PLAN.md` hors ROADMAP actif. + +Avant toute modification : + +1. lire `RULES.md`, `README.md`, `ROADMAP.md` et `CHANGELOG.md` ; +2. lire `prompts/020_v0_4_1_native_solana_programs.md`, `prompts/021_v0_4_2_executor_solana_core.md` et `prompts/022_v0_4_3_spl_memo.md` pour préserver les contrats de décodage, matérialisation, exécution et validation post-exécution ; +3. lire `docs/EXECUTION_MODEL.md`, `docs/SOLANA_INTERFACE_DEPENDENCIES.md`, `docs/SPL_MEMO_MATRIX.json` et les audits de clôture `0.4.1`/`0.4.2` ; +4. auditer `kb_decoder_spl_token`, `kb_executor_spl_token`, `kb_program_ids`, `kb_decoder_api`, `kb_materializer_api`, `kb_materializer_token_accounts`, `kb_materializer_admin`, `kb_materializer_fees`, `kb_materializer_risk`, `kb_execution_api`, `kb_execution_safety`, `kb_execution_solana`, `kb_pipeline`, `kb_rpc`, `kb_store_core`, `kb_store_pg` et `kb_app_demo` ; +5. inspecter les types réellement présents et les événements core déjà extraits, notamment les balances SPL, sans supposer qu’un helper, snapshot de compte ou schéma métier existe ; +6. vérifier avec Cargo la version exacte de `spl-token-interface` résolue depuis la contrainte workspace `^3.0`, ses features effectives et ses sources locales avant d’écrire le wire ; +7. comparer cette version à la version réellement déployée du programme sur Localnet, Devnet et Mainnet, instruction par instruction. + +État de départ connu : `kb_decoder_spl_token` et `kb_executor_spl_token` sont encore des scaffolds `Maybe`; `kb_materializer_token_accounts`, `kb_materializer_fees` et `kb_materializer_risk` sont des scaffolds inactifs ; `kb_materializer_admin` est déjà réel et ne doit pas régresser. + +## 3. État validé à préserver + +La clôture `0.4.3` a été validée avec : + +```text +kb_program_ids 5 tests +kb_decoder_spl_memo 9 tests +kb_materializer_transaction_annotations 7 tests +kb_executor_spl_memo 15 tests +kb_execution_api 22 tests +kb_execution_safety 15 tests +kb_execution_solana 12 tests +kb_pipeline 61 tests +kb_rpc 113 tests +kb_app_demo 96 tests +kb_store_pg 46 tests avec PostgreSQL réel +cargo clippy --all-targets propre +``` + +Le parcours Memo v4 Devnet réel a validé simulation, signature, envoi, confirmation, insertion canonique, extraction core, decode replay, annotation et second replay idempotent. Les trois générations Memo restent décodables et exécutables par la bibliothèque. Les 52 méthodes HTTP et neuf paires WS standard restent couvertes. + +Aucune régression de ces invariants n’est acceptable. + +## 4. Sources normatives + +Utiliser en priorité : + +- `kb_program_ids::SPL_TOKEN_PROGRAM_ID` pour l’ID exact ; +- le dépôt officiel SPL Token : ; +- le tag exact correspondant à la crate `spl-token-interface` résolue ; +- `interface/src/instruction.rs`, les builders officiels et le processor runtime officiel ; +- les layouts officiels `state::Mint`, `state::Account` et `state::Multisig` lorsque leur lecture est nécessaire ; +- la documentation officielle Solana Token ; +- des transactions Mainnet réelles et des fixtures synthétiques contrôlées ; +- des scénarios Localnet/Devnet pour les opérations stateful et les instructions nouvellement publiées. + +Au 15 juillet 2026, le catalogue workspace déclare `spl-token-interface = ^3.0` sans feature. La publication officielle `3.0.0` ajoute notamment `UnwrapLamports` au tag `45` et `Batch` au tag `255`. Ne pas conclure qu’elles sont déployées partout, ni les ignorer parce qu’elles sont récentes. Auditer séparément : + +- le layout publié par l’interface ; +- le processor et le binaire déployé par cluster ; +- la constructibilité du builder ; +- le résultat de simulation ; +- le corpus réel disponible. + +Ne pas activer `bincode`. Utiliser l’unpack officiel lorsqu’il correspond exactement au runtime audité ou reproduire localement un wire borné avec tests différentiels. + +## 5. Program ID et frontières + +Le jalon couvre exactement : + +```text +SPL_TOKEN_PROGRAM_ID = TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA +``` + +Le Native Mint `So11111111111111111111111111111111111111112` est un compte mint spécial du programme SPL Token, pas un autre programme. Il doit être pris en compte pour `SyncNative`, `CloseAccount`, retraits de lamports et transferts wrapped SOL, sans être enregistré comme décodeur distinct. + +Sont hors surface de dispatch `0.4.4` : + +- `TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb` et toutes les extensions Token-2022 ; +- `ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL` ; +- Metaplex Token Metadata ; +- Token Group, Token Metadata Interface et Transfer Hook tiers ; +- les programmes DEX qui invoquent SPL Token par CPI. + +Une instruction SPL Token en CPI reste toutefois décodée par `kb_decoder_spl_token` grâce à son Program ID exact et à son chemin inner stable. + +## 6. Contraintes normatives du workspace + +Respecter strictement : + +- Rust 2024 ; +- async-first pour l’I/O ; +- `kb_core::Error` et `kb_core::Result` ; +- aucun `anyhow`, `thiserror`, `unwrap`, `expect`, `unsafe`, panic de production ou opérateur `?` dans les nouveaux chemins de production ; +- `warn(missing_docs)`, `deny(unreachable_pub)`, `forbid(unsafe_code)` ; +- imports de traits uniquement lorsque possible, chemins pleinement qualifiés et `crate::` en intra-crate ; +- commentaires/docstrings Rust en anglais, Markdown projet en français ; +- header `file:` puis `version:` avec un seul incrément par fichier modifié ; +- aucun `bincode` direct ; +- aucune logique métier lourde dans `kb_app_demo` ; +- `CHANGELOG.md` inchangé avant validation complète de `0.4.4` ; +- `TRACING_TARGET` canonique dans `src/constants.rs` pour toute crate opérationnelle ; +- aucune valeur JSON Tauri exportée en `bigint` lorsqu’elle traverse `invoke`, `emit` ou `JSON.stringify` ; +- montants on-chain bruts sérialisés sans perte, sous forme de chaîne à la frontière Tauri si nécessaire. + +## 7. Audit préalable obligatoire + +Créer une matrice machine-readable dédiée, par exemple : + +```text +docs/SPL_TOKEN_MATRIX.json +``` + +Elle doit être vérifiée par un test Rust et contenir au minimum, pour chaque instruction officielle : + +- nom canonique et tag wire ; +- statut historique, courant, récent ou réservé ; +- layout exact et tailles autorisées ; +- comptes requis, ordre, writable et signer ; +- forme autorité simple et forme multisig ; +- champs décodés et limites ; +- événement produit ; +- projection(s) autorisée(s) ; +- builder officiel disponible ; +- support exécuteur exact avec éventuel motif `Unsupported` ; +- statut observé Localnet/Devnet/Mainnet ; +- fixtures synthétiques et signatures réelles. + +L’audit doit couvrir l’ensemble de `TokenInstruction` de la version résolue, y compris : + +- initialisation mint/account/multisig et variantes sans Rent ; +- `Transfer`, `Approve`, `Revoke`, `SetAuthority`, `MintTo`, `Burn` ; +- `CloseAccount`, `FreezeAccount`, `ThawAccount` ; +- les variantes `Checked` ; +- `SyncNative` ; +- `GetAccountDataSize`, `InitializeImmutableOwner`, conversions amount/UI amount ; +- `WithdrawExcessLamports` ; +- `UnwrapLamports` si présent dans la version résolue ; +- `Batch` si présent, avec parsing récursif borné et interdiction des batchs imbriqués selon le runtime officiel ; +- tout autre tag publié dans la version effectivement résolue. + +Ne pas recopier une ancienne liste supposée de 25 instructions. L’égalité entre la matrice, l’interface résolue, les déclarations de couverture et les opérations compilées doit être testée. + +## 8. `kb_decoder_spl_token` + +### 8.1 Remplacement du scaffold + +- remplacer `DecoderSupport::Maybe` par `Yes` uniquement pour l’ID SPL Token classique exact et `No` pour les autres ; +- déplacer le target tracing legacy vers `src/constants.rs` avec la valeur exacte `kb_decoder_spl_token` ; +- ajouter `tracing` seulement si des événements runtime réels sont émis ; +- ne produire aucun événement pour une observation hors surface. + +### 8.2 Contrat décodé commun + +Chaque observation lisible doit conserver au minimum : + +- Program ID exact ; +- instruction et tag wire ; +- chemin outer/inner et index stable ; +- succès/échec de la transaction et état `committed` ; +- comptes ordonnés avec position, pubkey, signer et writable ; +- rôles de comptes résolus sans inventer un rôle absent ; +- forme d’autorité simple ou multisig ; +- autorité déclarée, signataires fournis et validation structurelle ; +- montants bruts exacts, decimals lorsqu’ils sont dans le wire et aucune valeur UI inventée ; +- mint explicite lorsqu’il est fourni ; +- diagnostic borné et préfixe hex/hash pour un wire invalide ou inconnu ; +- source de toute inférence issue du contexte core. + +Les transactions échouées doivent produire des intentions structurées lorsque le wire et les comptes restent lisibles, sans prétendre que l’état Token a muté. + +### 8.3 Sémantique et inférences + +- Un `Transfer` non checked ne porte pas le mint ni les decimals dans son wire. Ne pas les inventer. Une corrélation via les token balance changes core peut être exposée avec provenance et confiance explicites ; une lecture RPC n’appartient pas au décodeur. +- Les variantes checked portent le mint et les decimals attendus, mais une transaction échouée ne prouve pas qu’ils ont été validés par le runtime. +- Distinguer owner, delegate, mint authority, freeze authority, close authority et multisig authority. +- Conserver l’ordre et les doublons des comptes réellement fournis. Valider `MIN_SIGNERS`, `MAX_SIGNERS`, `M` et `N` selon le runtime, sans confondre comptes membres du multisig et signataires effectifs de l’instruction. +- `InitializeImmutableOwner` est un no-op de compatibilité sur SPL Token classique si le runtime audité le confirme ; le représenter explicitement sans inventer une extension Token-2022. +- Les instructions de return data doivent conserver l’intention et, si la donnée de retour est disponible dans la transaction canonique, la valider sans dépendre des logs texte. +- `Batch` doit conserver ses sous-instructions, slices de comptes, ordre et diagnostics. Imposer des bornes de profondeur, compteur, longueur et comptes ; ne jamais accepter silencieusement un suffixe mal formé. + +### 8.4 Corpus minimal + +Le corpus synthétique et réel doit couvrir : + +- chaque tag officiel de la version résolue ; +- autorité simple et multisig `1..11`, seuils valides et invalides ; +- montants `0`, `1`, `u64::MAX` et valeurs intermédiaires ; +- decimals `0`, `9`, maximum wire et mismatch runtime ; +- authorities présentes, révoquées et `COption::None` ; +- comptes manquants, supplémentaires, dupliqués et flags incorrects ; +- outer instruction et CPI inner ; +- transaction réussie et échouée ; +- wrapped SOL et comptes non natifs ; +- payload vide, tag inconnu, taille tronquée, suffixe et UTF-8 invalide pour `UiAmountToAmount` ; +- batch vide, plusieurs sous-instructions, longueurs invalides et batch imbriqué ; +- transactions Mainnet réelles représentatives, avec signatures documentées. + +## 9. Matérialisation + +Stabiliser le décodage maximal avant les projections. Auditer d’abord les contrats existants ; ne pas conserver les sorties simplistes des scaffolds. + +### 9.1 `kb_materializer_token_accounts` + +Projeter les faits métier commités utiles concernant comptes et mints, par exemple : + +- initialisation de mint, compte et multisig ; +- transfert, mint et burn comme mutation instructionnelle ; +- freeze/thaw ; +- close et wrapped SOL sync/unwrap ; +- état de délégation lorsqu’il relève explicitement du compte token. + +Ne pas dupliquer les balance changes core. Les projections instructionnelles doivent référencer signature, path, comptes, mint connu ou inconnu, montant brut, provenance et clé d’idempotence. Elles ne doivent pas prétendre reconstruire un snapshot final de compte sans lecture stateful prouvée. + +### 9.2 `kb_materializer_admin` + +Étendre le matérialiseur réel uniquement pour les changements d’autorité Token et la configuration multisig qui appartiennent clairement au domaine admin. Préserver ses surfaces natives existantes et ses tests. + +### 9.3 `kb_materializer_risk` + +Activer une projection seulement pour des faits de risque stables et explicitement justifiés, par exemple délégation/allowance, freeze/thaw, autorité révoquée ou configuration multisig faible. Séparer constat on-chain et interprétation de risque ; ne pas produire un score arbitraire. + +### 9.4 `kb_materializer_fees` + +SPL Token classique ne possède pas les transfer fees de Token-2022. Ne pas fabriquer de frais à partir d’un transfert, ni dupliquer les frais réseau core. Si aucune instruction du runtime audité ne justifie une projection `fee`, documenter explicitement `no projection` et laisser le scaffold inactif jusqu’à un futur consommateur légitime. + +### 9.5 Règles communes + +- aucune projection mutable réussie pour une transaction non commitée ; +- replay identique = skip/idempotence ; +- changement de version = remplacement déterministe selon le ledger ; +- une sous-instruction `Batch` produit des identités stables dérivées du path parent et de sa position ; +- ne pas dupliquer logs, account keys, balances ou données core ; +- documenter les faits conservés uniquement dans le decode ; +- réutiliser `kb_sol_mat_events` sauf preuve qu’un contrat de persistance spécialisé est indispensable. + +## 10. `kb_executor_spl_token` + +### 10.1 Contrat typé + +Remplacer le plan réservé par des intents explicites couvrant les opérations officiellement constructibles. Le contrat doit exposer : + +- opération exacte ; +- Program ID exact ; +- comptes et rôles ordonnés ; +- mint, compte source/destination et authority selon l’opération ; +- montant brut et decimals séparés ; +- autorité simple ou multisig avec signataires ordonnés ; +- création ou initialisation stateful clairement séparée ; +- politique de simulation obligatoire, dry-run par défaut et plafond de frais ; +- coût token/SOL déclaré sans confondre montant transféré, rent et frais réseau ; +- `Supported` ou `Unsupported(reason)` exact par opération. + +### 10.2 Builders et wire + +- utiliser les builders `spl-token-interface` lorsqu’ils correspondent exactement à l’opération et au Program ID ; +- comparer chaque instruction produite au builder officiel et aux fixtures wire ; +- ne pas inventer de discriminator, Borsh ou sérialisation ; +- préserver strictement l’ordre, writable et signer des metas ; +- valider les formes multisig et dédupliquer seulement les signataires transactionnels requis, pas les metas si le contrat officiel les conserve ; +- construire les opérations publiées récentes, y compris `UnwrapLamports` ou `Batch`, seulement après preuve exacte du layout et du builder ; +- classer séparément constructibilité, déploiement et autorisation d’envoi. + +La bibliothèque ne doit pas être limitée à Devnet. Mainnet reste gouverné par `kb_execution_safety`, les profils applicatifs et la confirmation explicite. Une instruction future/test peut être constructible et simulable sur un cluster qui la déploie même si Mainnet ne la supporte pas encore. + +### 10.3 Préflight stateful + +Définir dans `kb_pipeline`, lorsque nécessaire, des lectures préflight bornées pour : + +- owner du compte et Program ID ; +- mint, decimals et état initialisé ; +- solde token et native reserve ; +- delegate et allowance ; +- authority/freeze authority ; +- multisig `M/N` et membres ; +- rent exemption et état de comptes à initialiser ; +- cohérence source/destination/mint ; +- préconditions close, freeze/thaw, mint, burn, sync et unwrap. + +Le décodeur reste sans RPC. L’exécuteur reste sans wallet/RPC. L’orchestrateur possède les lectures stateful, la simulation, la signature, l’envoi et la validation. + +## 11. Pipeline, store et démo + +- enregistrer le décodeur et les matérialiseurs Token validés dans le replay commun ; +- ajouter les déclarations de couverture et tests de sélection exacts ; +- conserver les écritures atomiques avec le ledger decode/materialize ; +- toucher `kb_store_pg` seulement si un nouveau contrat de requête ou persistance est réellement nécessaire ; +- utiliser `demo_backfill`, `demo_core_extraction`, `demo_sql_replay_candidates` et `demo_decode_replay` pour le corpus Mainnet ; +- étendre le navigateur de matérialisations seulement avec des filtres bornés et des DTOs typés, sans SQL libre ; +- réutiliser la fenêtre d’exécution Devnet existante si une validation opérateur Token apporte une valeur claire ; +- ne pas créer une WebView par opération. + +Pour la visualisation, afficher les mutations Token comme journal métier filtrable par signature, mint, compte, famille et opération. Les OHLC restent réservées aux futurs trades agrégés ; elles ne sont pas une représentation de SPL Token brut. + +## 12. Validation cluster + +Ajouter des tests opt-in et un parcours opérateur représentatif qui : + +1. utilise Localnet ou Devnet selon le déploiement de l’instruction ; +2. prépare des comptes temporaires sans dépendre de l’ATA pour la logique du jalon ; +3. construit et simule l’opération exacte ; +4. signe et envoie seulement avec autorisation explicite ; +5. confirme ; +6. appelle `getTransaction` ; +7. insère la transaction canonique ; +8. exécute core extraction ; +9. exécute decode replay SPL Token ; +10. vérifie les projections attendues et l’idempotence. + +Le scénario minimal Devnet devrait couvrir au moins initialisation contrôlée, mint, transfer checked, approve/revoke, burn et close, avec autorité simple. Ajouter un scénario multisig si le financement et la complexité restent raisonnables. Tester les instructions récentes sur Localnet lorsque le binaire Devnet ne les expose pas. + +Ne pas utiliser le programme ATA pour masquer l’initialisation de comptes Token dans la couverture `0.4.4`; ATA sera traité en `0.4.5`. + +## 13. Documentation attendue + +Mettre à jour au minimum : + +```text +kb_decoder_spl_token/README.md +kb_executor_spl_token/README.md +kb_materializer_token_accounts/README.md +README.md +ROADMAP.md +docs/SOLANA_INTERFACE_DEPENDENCIES.md +docs/SPL_TOKEN_MATRIX.json +``` + +Mettre aussi à jour les README des matérialiseurs réellement modifiés. Documenter : + +- version résolue de l’interface et différences avec le programme déployé ; +- inventaire exact des instructions et tags ; +- événements et diagnostics ; +- projections par famille et absences volontaires ; +- opérations exécutables et `Unsupported(reason)` ; +- règles simple authority/multisig ; +- corpus et signatures réelles ; +- résultats Localnet/Devnet/Mainnet ; +- limites et non-revendications. + +`CHANGELOG.md` ne doit être modifié qu’après validation complète de `0.4.4`. + +## 14. Interdictions + +Ne pas : + +- commencer ATA ou Token-2022 ; +- absorber les extensions Token-2022 dans le décodeur SPL Token classique ; +- créer ou commencer l’acquisition historique différée ; +- fusionner Token dans `kb_decoder_solana_core` ; +- déduire silencieusement un mint absent d’un `Transfer` non checked ; +- utiliser les logs RPC comme source principale lorsque l’instruction est disponible ; +- inventer des fees, snapshots finaux, balances ou autorités validées ; +- produire une projection committed pour une transaction échouée ; +- considérer les membres d’un multisig comme tous signataires de chaque instruction ; +- désactiver une opération dans la bibliothèque uniquement parce qu’elle n’est pas encore déployée sur Mainnet ; +- exposer un envoi Mainnet par défaut dans la démo ; +- ajouter du SQL libre à l’UI ; +- générer des bindings TypeScript contenant des `bigint` dans les frontières Tauri JSON. + +## 15. Validations minimales + +Selon les fichiers touchés : + +```bash +cargo test -p kb_program_ids +cargo test -p kb_decoder_spl_token +cargo test -p kb_materializer_token_accounts +cargo test -p kb_materializer_admin +cargo test -p kb_materializer_fees +cargo test -p kb_materializer_risk +cargo test -p kb_executor_spl_token +cargo test -p kb_execution_api +cargo test -p kb_execution_safety +cargo test -p kb_execution_solana +cargo test -p kb_pipeline +cargo test -p kb_rpc +cargo test -p kb_app_demo +cargo clippy --all-targets +``` + +Si le store ou une projection est touché : + +```bash +KB_POSTGRES_TEST_URL='postgres://solana:solana@localhost:5432/solana_test' \ + cargo test -p kb_store_pg -- --nocapture +``` + +Si Tauri est touché : + +```bash +cargo tauri dev -c kb_app_demo/tauri.conf.json +``` + +Le serveur Vite lancé par Tauri constitue la validation frontend normale ; ne pas imposer un `npm run build` séparé sans besoin particulier. + +## 16. Livraison + +Livrer des tranches fonctionnelles sous la forme : + +```text +khadhroony-bot2_v0.4.4-pre.NNN-delta.zip +``` + +Après la livraison d’un delta `pre.NNN`, tout correctif conserve le même numéro : + +```text +khadhroony-bot2_v0.4.4-pre.NNN-delta-fix-001.zip +khadhroony-bot2_v0.4.4-pre.NNN-delta-fix-002.zip +``` + +Ne pas incrémenter `NNN` pour réparer une archive déjà livrée. Le numéro suivant est réservé à une nouvelle tranche fonctionnelle. + +Chaque archive doit contenir : + +- uniquement les fichiers ajoutés ou modifiés ; +- `delta.md` non versionné ; +- `manifest.json` ; +- les suppressions manuelles explicitement listées ; +- les validations exécutées et non exécutées ; +- aucune archive complète sauf demande explicite ; +- aucune modification de `CHANGELOG.md` avant validation finale du jalon. diff --git a/migration/khadhroony-bot2-reference/prompts/024_v0_4_5_spl_associated_token_account.md b/migration/khadhroony-bot2-reference/prompts/024_v0_4_5_spl_associated_token_account.md new file mode 100644 index 0000000..6e92ad0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/024_v0_4_5_spl_associated_token_account.md @@ -0,0 +1,244 @@ + + + +# Prompt de session — `0.4.5` SPL Associated Token Account + +## 1. Mission + +Reprendre `khadhroony-bot2` après la clôture validée de `0.4.4` et développer exclusivement la +surface SPL Associated Token Account : + +```text +ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL + -> audit exact de l'interface et du processor résolus + -> décodage maximal de Create, CreateIdempotent et RecoverNested + -> dérivation et validation explicites des adresses associées + -> lifecycle ATA matérialisé sans duplication SPL Token + -> exécuteur typé simulation-first après validation du décodeur et des projections +``` + +Le jalon couvre les ATA ciblant le programme SPL Token classique et le programme Token-2022, car le +Program ID Token cible fait partie du contrat ATA. Il ne commence toutefois pas le décodage général +des instructions ou extensions Token-2022, réservé à `0.4.6`. + +## 2. Base de travail + +La base attendue clôt `0.4.4` et contient : + +- `CHANGELOG.md` avec l'entrée validée SPL Token ; +- `ROADMAP.md` avec `0.4.4` entièrement coché et `0.4.5` actif ; +- `docs/SPL_TOKEN_MATRIX.json` et les preuves Mainnet/Devnet conservées ; +- `kb_decoder_spl_token`, ses matérialiseurs, son exécuteur et son orchestration validés ; +- `kb_decoder_spl_associated_token_account` et + `kb_executor_spl_associated_token_account` encore à l'état de scaffolds ; +- la dépendance workspace `spl-associated-token-account-interface = ^2.0` avec feature `borsh`. + +Avant toute modification : + +1. lire `RULES.md`, `README.md`, `ROADMAP.md`, `CHANGELOG.md` et ce prompt ; +2. relire `prompts/023_v0_4_4_spl_token.md`, `docs/EXECUTION_MODEL.md` et + `docs/SOLANA_INTERFACE_DEPENDENCIES.md` ; +3. inspecter la version réellement résolue de l'interface ATA et ses sources locales avec Cargo ; +4. comparer instruction enum, layouts Borsh, builders, dérivation PDA et processor officiels ; +5. auditer les transactions réelles outer et CPI pour Token classique et Token-2022 ; +6. ne pas supposer qu'un helper, un matérialiseur ou un préflight ATA existe déjà. + +## 3. Invariants `0.4.4` à préserver + +- décodage exact des 28 tags SPL Token classique ; +- montants bruts et decimals sans perte ; +- aucune invention de mint sur les instructions qui ne le transportent pas ; +- projections `token_account`, `admin` et `risk` instructionnelles et idempotentes ; +- 24 opérations exécuteur Token, préflight stateful et garde-fous communs ; +- simulation obligatoire, signature après autorisation seulement et replay post-exécution exact ; +- journal Tauri borné et aucune logique métier lourde dans la WebView ; +- aucune régression Memo, RPC standard, PostgreSQL ou exécution Solana. + +État final de référence communiqué : + +```text +kb_program_ids 5 tests +kb_decoder_spl_token 8 tests +kb_executor_spl_token 15 tests +kb_decoder_spl_memo 9 tests +kb_materializer_transaction_annotations 7 tests +kb_execution_api 22 tests +kb_execution_safety 15 tests +kb_execution_solana 12 tests +kb_pipeline 81 tests +kb_rpc 113 tests +kb_config 41 tests +kb_app_demo 102 tests +kb_store_pg 46 tests avec PostgreSQL réel +cargo clippy --all-targets propre +``` + +## 4. Frontières strictes + +Dans le périmètre : + +- Program ID ATA exact ; +- les variantes réellement publiées par l'interface résolue ; +- ATA destinés à SPL Token classique et Token-2022 ; +- dérivation canonique à partir du wallet, du Token Program ID et du mint ; +- création, création idempotente, récupération nested et échecs lisibles ; +- instructions outer et inner/CPI ; +- simulation et scénarios Localnet/Devnet bornés. + +Hors périmètre : + +- décodage général de `TokenInstruction` Token-2022 et de ses extensions ; +- Transfer Hook, Confidential Transfer, Token Group et Metadata Interface ; +- Metaplex Token Metadata ; +- fermeture ou transfert générique de comptes Token déjà couverts par `0.4.4` ; +- nouveaux DEX, stratégies, trading automatisé ou acquisition historique différée ; +- migration vers une table SQL dédiée sans preuve qu'elle est nécessaire. + +## 5. Matrice machine-readable obligatoire + +Créer `docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json`, vérifiée par un test Rust. Pour chaque +variante publiée, enregistrer : + +- discriminant et encodage exact ; +- comptes requis, ordre, signer et writable ; +- Program IDs système, Token et ATA attendus ; +- seeds PDA et adresse dérivée attendue ; +- différences Token classique/Token-2022 ; +- règles de création normale, idempotente et nested recovery ; +- événements décodés et projections autorisées ; +- support exécuteur et motif stable si decode-only ; +- preuves synthétiques, Localnet, Devnet et corpus réel ; +- limites de ce qui est prouvé et non testé. + +Imposer par test l'égalité entre matrice, surfaces compilées, déclarations de couverture et capacités +de l'exécuteur. Ne pas recopier une ancienne enum supposée sans vérifier la version résolue. + +## 6. Décodeur + +Remplacer `DecoderSupport::Maybe` par un dispatch exact. Le décodeur doit conserver : + +- Program ID, instruction path outer/inner et statut committed ; +- comptes ordonnés, doublons et flags observés ; +- funding account, ATA, wallet owner, mint, System Program et Token Program ; +- adresse ATA attendue, adresse observée et résultat de validation ; +- type de Token Program cible sans prétendre décoder ses extensions ; +- pour RecoverNested, owner ATA, nested ATA, owner mint et nested mint sans inverser les rôles ; +- transaction échouée comme intention structurée lorsque le wire reste lisible ; +- diagnostic borné pour données inconnues, tronquées ou comptes incomplets. + +La dérivation PDA doit être canonique et testée différentiellement contre les helpers officiels. Une +adresse observée incorrecte doit rester décodable avec un diagnostic explicite, jamais être corrigée +silencieusement. + +Déplacer le target tracing legacy vers `src/constants.rs` avec la valeur exacte du nom Cargo. Ne pas +émettre de faux événement pour un autre Program ID. + +## 7. Matérialisation + +Auditer en priorité `kb_materializer_token_accounts`, `kb_materializer_lifecycle`, +`kb_materializer_admin` et `kb_materializer_risk` avant d'ajouter une nouvelle projection. + +Le résultat doit : + +- matérialiser le lifecycle ATA créé/réutilisé/récupéré avec provenance et idempotence ; +- distinguer création normale et idempotente ; +- conserver wallet, mint, ATA et Token Program cible ; +- ne pas dupliquer les mutations SPL Token déjà produites par les CPI internes ; +- ne pas prétendre reconstruire un solde Token ou un snapshot final ; +- refuser les sorties mutables pour les transactions échouées/non commitées ; +- conserver les faits de tentative et diagnostics détaillés dans le decode lorsque nécessaire. + +Tester explicitement une transaction ATA dont les CPI Token sont également décodées : une seule +projection doit posséder chaque fait métier. + +## 8. Exécuteur et préflight + +Après validation du décodeur et des projections, remplacer le scaffold exécuteur par des intents +typés pour toutes les variantes officiellement constructibles. Utiliser les builders officiels +lorsqu'ils correspondent exactement à l'interface auditée. + +Le plan doit exposer : + +- payer, wallet owner, mint, ATA dérivé et Token Program cible ; +- comptes exacts dans l'ordre officiel ; +- signataires uniques sans modifier l'ordre des metas ; +- coût SOL maximal correspondant au rent et aux frais ; +- simulation obligatoire et dry-run par défaut ; +- validation post-exécution adaptée à la création ou à l'idempotence. + +Le préflight stateful Localnet/Devnet doit au minimum vérifier : + +- cluster et Program IDs ; +- existence et owner du mint ; +- dérivation exacte de l'ATA ; +- absence du compte pour Create, ou état compatible pour CreateIdempotent ; +- owner, mint et Token Program du compte existant ; +- conditions exactes de RecoverNested ; +- rent et plafonds de dépense ; +- disponibilité de tous les signataires requis. + +La bibliothèque exécuteur reste universelle. Les politiques de cluster, wallet, confirmation et +Mainnet restent dans les couches communes. + +## 9. Corpus et scénarios cluster + +Prévoir un corpus offline couvrant au minimum : + +- les trois variantes publiées si confirmées par l'interface résolue ; +- Token classique et Token-2022 comme programmes cibles ; +- création réussie, compte déjà existant, idempotence réussie et conflits ; +- ATA attendu correct et adresse incorrecte ; +- wallet/mint/Token Program erronés ; +- comptes absents, supplémentaires, dupliqués et flags incorrects ; +- outer et CPI inner ; +- transaction réussie et échouée ; +- RecoverNested valide et formes structurellement invalides. + +Ajouter ensuite des tests opt-in Localnet/Devnet. Toute dépense réelle exige des comptes contrôlés, +un plafond explicite et une confirmation. Les endpoints publics peuvent retourner `429` : conserver +les signatures déjà confirmées et prévoir une reprise bornée plutôt que recréer aveuglément les +comptes. + +## 10. Tauri + +Réutiliser la fenêtre d'exécution Solana existante. Ne pas créer une WebView par opération. + +Le panneau ATA représentatif doit montrer : + +- profil Devnet ; +- Token Program cible ; +- payer/wallet, mint et ATA dérivé readonly ; +- mode Create ou CreateIdempotent ; +- préflight, simulation, coût/rent et garde-fou de confirmation ; +- confirmation, extraction, decode, matérialisation et idempotence après envoi ; +- journal lifecycle filtrable sans exposer de clé privée. + +`RecoverNested` peut rester hors UI si son scénario contrôlé est mieux couvert par un test opt-in, +mais il ne doit pas être absent du décodeur ni de l'exécuteur si l'interface le publie. + +## 11. Contraintes du workspace + +Respecter Rust 2024, async-first, `kb_core::Error`/`Result`, les lints workspace et les headers de +version. Aucun `anyhow`, `thiserror`, `unsafe`, `unwrap`, `expect`, panic de production, opérateur `?` +dans les nouveaux chemins de production, `bincode` direct ou logique métier lourde dans Tauri. +Commentaires Rust en anglais, Markdown projet en français. Les montants et lamports traversant Tauri +restent JSON-safe et sans perte. + +Mettre `ROADMAP.md` à jour à chaque tranche. Conserver `CHANGELOG.md` inchangé jusqu'à la validation +complète de `0.4.5`. + +## 12. Critères de clôture + +`0.4.5` ne peut être clôturé qu'après : + +- matrice et égalité compilée validées ; +- corpus offline complet ; +- non-duplication avec SPL Token démontrée ; +- préflight et exécuteur simulation-first validés ; +- scénario cluster contrôlé et reprise documentée si nécessaire ; +- PostgreSQL réel, second replay idempotent et journal Tauri validés ; +- régression `0.4.4`, RPC, configuration, application et Clippy propres ; +- README, ROADMAP et CHANGELOG synchronisés seulement après validation finale. + +Commencer par l'audit de l'interface réellement résolue et de son processor. Ne pas écrire le wire, +les événements ou l'exécuteur avant d'avoir publié la première version de la matrice ATA. diff --git a/migration/khadhroony-bot2-reference/prompts/025_v0_4_6_spl_token_2022.md b/migration/khadhroony-bot2-reference/prompts/025_v0_4_6_spl_token_2022.md new file mode 100644 index 0000000..724a462 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/025_v0_4_6_spl_token_2022.md @@ -0,0 +1,338 @@ + + + +# Prompt de session — `0.4.6` Token-2022, extensions et registre ElGamal + +## 1. Mission + +Reprendre `khadhroony-bot2` après la clôture validée de `0.4.5` et développer exclusivement la +surface Token-2022 et les interfaces qui font réellement partie de son contrat audité : + +```text +TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb + -> audit exact des interfaces, versions Cargo et processors résolus + -> décodage maximal des instructions de base et des extensions identifiables + -> lecture exacte des TLV Mint/Account/Multisig sans perte ni invention + -> matérialisation maximale avec un propriétaire unique par fait métier + -> exécuteur extension par extension après validation du decode et des projections + +regVY... registre ElGamal + -> programme distinct du ZK ElGamal Proof natif + -> Program ID exact à confirmer depuis les sources réellement résolues + -> décodage, matérialisation et politique d'exécution propres +``` + +La règle du jalon est exhaustive : tout ce qui est officiellement identifiable doit être décodé, +qu'il soit historique, courant ou expérimental ; tout fait stable et prouvé doit être matérialisé +sans doublon ; toute opération actuelle officiellement constructible doit être exécutable après +préflight, sauf preuve explicite qu'elle est historique, obsolète, désactivée ou non déployée. + +### Décision de périmètre Token-2022 / registre ElGamal + +Token-2022 et le registre ElGamal restent initialement dans le même jalon parce que le registre est +fonctionnellement lié aux extensions confidentielles. Le reporter sans audit pourrait conduire à +déclarer à tort une couverture Token-2022 maximale alors qu'une dépendance directe du parcours +confidentiel resterait inconnue. + +Cette proximité fonctionnelle n'autorise aucune fusion technique. Les deux surfaces doivent garder : + +- des Program IDs, dispatchs et déclarations de couverture séparés ; +- des matrices machine-readable séparées ; +- des événements, diagnostics et propriétaires de matérialisation explicites ; +- des corpus synthétiques et réels ainsi que des preuves cluster séparés ; +- des capacités d'exécution et des critères d'activation indépendants ; +- une distinction stricte avec le programme natif ZK ElGamal Proof déjà couvert. + +Le jalon doit progresser dans cet ordre : + +1. audit complet Token-2022 et publication de sa première matrice ; +2. décodeur Token-2022 de base, puis extensions par groupes cohérents ; +3. audit dédié du registre ElGamal et publication de sa matrice propre ; +4. matérialisations Token-2022 et ElGamal avec preuve de non-duplication ; +5. exécution Token-2022 extension par extension après validation stateful ; +6. exécution du registre uniquement pour ses opérations actuelles, constructibles et validées. + +La taille de `0.4.6` ne justifie jamais une réduction de couverture ou de qualité. Si l'audit prouve +que le registre ElGamal est autonome, sensiblement plus volumineux que prévu ou qu'il exige un +cycle de preuves indépendant, il peut être reporté vers un jalon séparé. Ce report doit être une +décision documentée issue de la première matrice et de l'audit des sources résolues, avec mise à +jour explicite du ROADMAP et des critères de clôture ; il ne doit pas être décidé par commodité +avant l'audit. + +## 2. Base de travail validée + +La base attendue clôt `0.4.5` et contient : + +- le décodeur SPL Token classique couvrant ses 28 tags et son exécuteur de 24 opérations ; +- le décodeur ATA couvrant `Create`, `CreateIdempotent`, `RecoverNested` et `Create` vide ; +- la dérivation ATA dépendant explicitement du Token Program cible ; +- les matérialisations ATA lifecycle/risk sans duplication des CPI Token ; +- l'exécuteur ATA des trois variantes, son préflight stateful et son orchestration Devnet ; +- les preuves Devnet classiques, Token-2022 ATA et RecoverNested ; +- PostgreSQL réel, second replay idempotent, Tauri/Vite et Clippy validés ; +- toutes les versions workspace, npm et Tauri synchronisées en `0.4.5`. + +État final de référence communiqué : + +```text +kb_program_ids 5 tests +kb_decoder_spl_token 8 tests +kb_executor_spl_token 15 tests +kb_decoder_spl_associated_token_account 10 tests +kb_executor_spl_associated_token_account 10 tests +kb_decoder_spl_memo 9 tests +kb_materializer_transaction_annotations 7 tests +kb_materializer_token_accounts 9 tests +kb_materializer_risk 6 tests +kb_materializer_lifecycle 17 tests +kb_materializer_admin 9 tests +kb_materializer_fees 3 tests +kb_execution_api 22 tests +kb_execution_safety 15 tests +kb_execution_solana 12 tests +kb_pipeline 90 tests +kb_rpc 113 tests +kb_config 41 tests +kb_app_demo 111 tests +kb_store_pg 46 tests avec PostgreSQL réel +cargo clippy --all-targets propre +``` + +## 3. Lecture et audit obligatoires avant modification + +1. lire `RULES.md`, `README.md`, `ROADMAP.md`, `CHANGELOG.md` et ce prompt ; +2. relire `prompts/023_v0_4_4_spl_token.md`, + `prompts/024_v0_4_5_spl_associated_token_account.md`, + `docs/SPL_TOKEN_MATRIX.json`, `docs/SPL_ASSOCIATED_TOKEN_ACCOUNT_MATRIX.json`, + `docs/EXECUTION_MODEL.md` et `docs/SOLANA_INTERFACE_DEPENDENCIES.md` ; +3. inspecter `Cargo.toml`, `Cargo.lock`, `cargo tree`, `cargo metadata` et les sources Cargo locales + pour connaître les versions réellement résolues des interfaces Token-2022 et extensions ; +4. comparer enum d'instruction, unpack/pack, TLV, builders, validation d'état et processors officiels ; +5. distinguer strictement ce que publie une interface, ce qu'implémente le processor, ce qui est + déployé sur chaque cluster et ce qu'un corpus réel prouve ; +6. auditer les crates décodeur, exécuteur et matérialiseurs existants avant tout remaniement ; +7. publier la première matrice machine-readable avant d'écrire le wire, les événements ou + l'exécuteur. + +Ne recopier aucune ancienne enum, liste d'extensions, taille de compte ou règle TLV supposée. Une +version ou feature mentionnée dans le catalogue workspace n'est pas une preuve de résolution ni de +consommation par une crate feuille. + +## 4. Frontières strictes + +Dans le périmètre : + +- Program ID Token-2022 exact ; +- instructions de base héritées de SPL Token et variantes propres à Token-2022 ; +- extensions Mint et Account officiellement identifiables, y compris leurs instructions + d'initialisation, mise à jour, lecture et lifecycle lorsqu'elles existent ; +- TLV, préfixes de base Mint/Account/Multisig, longueurs, padding, types et autorités ; +- Transfer Fee, Interest Bearing, NonTransferable, Permanent Delegate, CPI Guard, + Immutable Owner, Default Account State, Memo Transfer, Reallocate, pausable/confidential et + autres extensions uniquement selon les sources réellement résolues ; +- Transfer Hook, Token Group et Token Metadata Interface comme interfaces séparées pouvant être + implémentées par des Program IDs tiers ; +- registre ElGamal comme programme distinct ; +- instructions outer et inner/CPI, transactions réussies et échouées ; +- corpus offline, corpus réel et scénarios Localnet/Devnet bornés ; +- exécution des opérations actuelles constructibles après audit, matérialisation et préflight. + +Hors périmètre : + +- Metaplex Token Metadata ; +- programmes tiers implémentant une interface tant que leur Program ID n'est pas explicitement + audité et inclus ; +- preuve cryptographique locale ou déchiffrement de données confidentielles ; +- invention d'un solde, d'un withheld amount final, d'intérêts courus ou d'un état de compte final + depuis la seule instruction ; +- migration SQL dédiée sans preuve de cardinalité ou de requêtage qui la rende nécessaire ; +- nouveaux DEX, stratégies, trading automatisé ou acquisition historique différée. + +## 5. Matrices machine-readable obligatoires + +Créer au minimum `docs/SPL_TOKEN_2022_MATRIX.json`, vérifiée par des tests Rust. Créer une matrice +séparée pour le registre ElGamal ou les interfaces externes si leur contrat ne peut pas être décrit +sans mélanger des Program IDs et processors distincts. + +Pour chaque instruction et extension, enregistrer : + +- discriminant, sous-discriminant, encodage, taille minimale/exacte et règle de suffixe ; +- état historique, courant, expérimental, désactivé ou obsolète avec preuve ; +- comptes requis dans l'ordre, signer/writable et comptes optionnels ; +- type d'état ciblé : Mint, Account, Multisig, TLV ou compte de programme externe ; +- type TLV, layout, longueur, padding, répétition permise et incompatibilités ; +- autorités, montants bruts, decimals, epochs, timestamps et clés publiques sans perte ; +- événements décodés et projections autorisées avec propriétaire unique ; +- support exécuteur, builder officiel ou miroir justifié, et motif stable si decode-only ; +- support outer/inner, succès/échec et diagnostics attendus ; +- preuves synthétiques, Localnet, Devnet, Mainnet et corpus réel ; +- limites explicites de ce qui n'est pas prouvé. + +Imposer l'égalité entre matrice, surfaces compilées, déclarations de couverture, propriétaires de +matérialisation et capacités exécuteur. Une extension publiée mais non implémentée ne peut pas +disparaître : elle doit être classée avec une raison stable. + +## 6. Décodeur maximal + +Remplacer le scaffold `kb_decoder_spl_token_2022` par un dispatch exact du seul Program ID +Token-2022. Le décodeur doit : + +- conserver Program ID, instruction path outer/inner, accounts ordonnés, doublons, flags, statut + committed et erreur transactionnelle ; +- décoder maximalement les instructions de base et extension selon le processor résolu ; +- préserver montants `u64`, decimals, epochs et timestamps sans conversion flottante ; +- ne jamais inventer un mint lorsqu'il n'est pas dans le wire ou le contrat de comptes ; +- distinguer données d'instruction, état de compte observé et état final non disponible ; +- parser les TLV de façon bornée, conserver ordre/type/longueur, détecter types dupliqués, + tronqués, inconnus, padding invalide et incompatibilités ; +- conserver une extension inconnue comme fait opaque borné lorsque sa structure externe reste + lisible, sans prétendre connaître sa sémantique ; +- décoder les transactions échouées comme intentions structurées lorsque le wire reste lisible ; +- refuser tout faux événement pour SPL Token classique, ATA, programme de hook, groupe, metadata, + registre ElGamal ou programme ZK natif ; +- borner instructions, comptes, vecteurs, strings, TLV, données opaques et diagnostics. + +Les tests différentiels doivent utiliser les unpackers/builders officiels lorsqu'ils existent. Les +miroirs locaux ne sont acceptables qu'après audit prouvant qu'une feature interdite ou l'absence de +helper empêche l'usage direct, avec layout documenté et fixtures officielles comparées. + +## 7. Interfaces externes et registre ElGamal + +Transfer Hook, Token Group et Token Metadata Interface ne doivent pas être absorbés automatiquement +dans le décodeur Token-2022. Auditer leurs Program IDs, discriminants et mécanismes d'interface : + +- une instruction exécutée par Token-2022 appartient au décodeur Token-2022 ; +- une CPI vers un programme implémentant une interface appartient au décodeur de ce Program ID ; +- les données TLV pointant vers un programme externe restent un lien, pas un décodage de ce + programme ; +- un même fait métier ne doit pas être matérialisé à la fois depuis le parent Token-2022 et la CPI. + +Le registre ElGamal doit avoir un Program ID exact confirmé, un dispatch et une matrice propres. Ne +pas le confondre avec `ZkE1Gama1Proof11111111111111111111111111111`, déjà traité comme programme +natif de preuve. Aucun vérificateur cryptographique local n'est requis : conserver les clés, +handles, comptes, hashes, tailles et résultats runtime prouvés. + +Sa présence dans la même version que Token-2022 exprime uniquement la nécessité d'auditer le lien +fonctionnel avec les extensions confidentielles. Elle ne permet pas de partager artificiellement +une enum, un décodeur, une matrice, un événement ou une capacité exécuteur entre les deux Program +IDs. + +## 8. Matérialisation maximale sans doublon + +Auditer au minimum `kb_materializer_token_accounts`, `kb_materializer_admin`, +`kb_materializer_fees`, `kb_materializer_compliance_audit`, `kb_materializer_risk`, +`kb_materializer_lifecycle` et les matérialiseurs metadata existants avant d'en créer un nouveau. + +Attribuer chaque fait stable à un seul propriétaire, par exemple : + +- lifecycle et mutations de comptes/mints dans `token_accounts` ; +- autorités et configuration dans `admin` ; +- paramètres et prélèvements de transfer fee prouvés dans `fees` ; +- garde-fous, exigences memo, hooks et opérations confidentielles dans `compliance_audit` ou + `risk` selon la nature factuelle ; +- metadata/group uniquement si le wire et le Program ID audités le prouvent. + +Chaque projection doit être instructionnelle, commitée, idempotente et explicite sur ses limites. +Elle ne doit pas reconstruire un snapshot, un solde, un rendement, un montant withheld global ou un +état confidentiel final. Tester les transactions contenant parent Token-2022 et CPI externes afin +qu'une seule projection possède chaque fait métier. + +## 9. Exécuteur et préflight + +Ne commencer `kb_executor_spl_token_2022` qu'après validation du décodeur, de la matrice et des +projections. Développer par tranches cohérentes d'extensions, mais maintenir dans la matrice la +surface exhaustive. + +Pour chaque opération actuelle officiellement constructible : + +- utiliser le builder officiel lorsqu'il correspond exactement à l'interface auditée ; +- exposer intent typé, comptes exacts, ordre des metas, signataires uniques et Program IDs ; +- conserver montants, decimals, epochs et coûts en représentations JSON sans perte ; +- calculer le coût SOL maximal pour rent, reallocate et frais réseau ; +- imposer simulation et dry-run par défaut ; +- fournir une validation post-exécution adaptée à l'opération ; +- classer decode-only toute opération historique/obsolète/désactivée avec preuve et motif stable. + +Le préflight stateful Localnet/Devnet doit contrôler cluster, Program IDs, owner des comptes, +préfixe de base, TLV existants, espace requis, rent, compatibilités d'extensions, autorités, +signataires, mint/account relations, soldes nécessaires et plafonds. Les extensions confidentielles +ou à hook doivent vérifier tous les comptes additionnels connus sans exécuter de cryptographie ni +faire confiance à une liste fournie par la WebView. + +La bibliothèque exécuteur reste universelle. Les politiques de cluster, wallet, confirmation et +Mainnet restent dans les couches communes. + +## 10. Corpus et scénarios cluster + +Le corpus offline doit couvrir chaque instruction/extension publiée, ses formes minimales et +limites, inconnus/tronqués/suffixés, comptes manquants/supplémentaires/dupliqués, flags invalides, +outer/inner, succès/échec et incompatibilités TLV. Ajouter des fixtures de comptes Mint et Account +avec zéro, une et plusieurs extensions, ordre différent, padding et types inconnus. + +Auditer ensuite un corpus réel Mainnet/Devnet. Les preuves doivent séparer : + +- interface publiée ; +- processor source ; +- builder constructible ; +- programme déployé ; +- transaction observée ; +- simulation réussie ; +- soumission contrôlée et postconditions. + +Toute dépense réelle exige comptes contrôlés, plafond explicite, confirmation et reprise bornée. +Après `429` ou interruption, conserver les signatures déjà confirmées, vérifier leurs postconditions +et reprendre depuis le dernier jalon exact plutôt que recréer comptes ou extensions. + +## 11. Tauri + +Réutiliser la fenêtre d'exécution Solana existante et son journal borné. Ne pas créer une WebView +par extension. Ajouter seulement un parcours représentatif Devnet lorsque backend, préflight et +post-validation sont déjà validés. + +Le panneau doit montrer profil, Token Program, mint/account, extensions sélectionnées, espace +requis, rent, signataires, préflight, simulation, confirmation et postconditions. Les secrets, +proof data non nécessaires et clés privées restent côté Rust. Les montants et lamports traversent +Tauri sous forme JSON-safe sans perte. Les extensions non exposées dans l'UI restent obligatoires +dans le décodeur et dans la bibliothèque exécuteur lorsqu'elles sont actuelles et constructibles. + +## 12. Contraintes du workspace + +Respecter Rust 2024, async-first, `kb_core::Error`/`Result`, lints workspace et headers de version. +Aucun `anyhow`, `thiserror`, `unsafe`, `unwrap`, `expect`, panic de production, opérateur `?` dans +les nouveaux chemins de production, `bincode` direct ou logique métier lourde dans Tauri. +Commentaires Rust en anglais, Markdown projet en français. + +Conserver le style d'organisation des crates existantes. Un remaniement est autorisé seulement +lorsqu'il est nécessaire pour le contrat Token-2022, avec justification, périmètre borné et tests +de non-régression. Mettre `ROADMAP.md` à jour à chaque tranche et conserver `CHANGELOG.md` inchangé +jusqu'à la validation complète de `0.4.6`. + +Livrer uniquement des deltas : + +```text +khadhroony-bot2_v0.4.6-pre.001-delta.zip +khadhroony-bot2_v0.4.6-pre.001-delta-fix-001.zip +``` + +Chaque archive contient `delta.md` non versionné avec fichiers ajoutés/modifiés, suppressions +manuelles, dépendance aux deltas précédents et validations exécutées ou non exécutées. + +## 13. Critères de clôture + +`0.4.6` ne peut être clôturé qu'après : + +- matrices exhaustives et égalités compilées validées ; +- corpus offline complet et corpus réel documenté ; +- décodage maximal des instructions, extensions et programmes distincts inclus ; +- matérialisation maximale sans doublon parent/CPI ni snapshot inventé ; +- exécuteur couvrant toutes les opérations actuelles constructibles et motifs decode-only stables ; +- préflight, simulation-first et postconditions validés ; +- scénarios cluster contrôlés et reprise documentée ; +- PostgreSQL réel, second replay idempotent et journal Tauri validés ; +- régressions `0.4.5`, RPC, configuration, application et Clippy propres ; +- versions Cargo/npm/Tauri et dépendances consommées auditées ; +- README, ROADMAP et CHANGELOG synchronisés seulement après validation finale. + +Commencer par l'audit des versions réellement résolues, des sources locales et des processors. +Publier la première matrice Token-2022 avant toute implémentation du wire, des événements ou de +l'exécuteur. diff --git a/migration/khadhroony-bot2-reference/prompts/026_v0_4_7_metaplex_token_metadata.md b/migration/khadhroony-bot2-reference/prompts/026_v0_4_7_metaplex_token_metadata.md new file mode 100644 index 0000000..63459b0 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/026_v0_4_7_metaplex_token_metadata.md @@ -0,0 +1,155 @@ + + + +# Prompt de session — `0.4.7` Metaplex Token Metadata + +## 1. Mission + +Reprendre `khadhroony-bot2` après la clôture validée de `0.4.6` et implémenter la couverture maximale et strictement bornée de Metaplex Token Metadata. + +Metaplex Token Metadata est un programme indépendant de la Metaplex Foundation. Il complète les mints SPL Token classique et Token-2022 par des metadata externes, des éditions, collections, standards de token et configurations programmables. Il ne doit être absorbé ni par `kb_decoder_spl_token`, ni par `kb_decoder_spl_token_2022`, ni confondu avec les metadata incorporées de Token-2022 ou avec Metaplex Core. + +Program ID canonique à vérifier depuis les sources officielles avant code : + +```text +metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s +``` + +## 2. Base validée + +La base `0.4.6` comprend : + +- Solana Core, loaders et précompiles ; +- SPL Memo, SPL Token classique, ATA et Token-2022 ; +- registre ElGamal séparé du programme ZK ElGamal Proof ; +- replay contextualisé avec `incomplete_signatures` ; +- PostgreSQL idempotent sans doublon decode/mat ; +- `kb_store_core` 41/41 ; +- `kb_store_pg` 46/46 avec PostgreSQL réel ; +- `kb_pipeline` 124/124 ; +- `kb_app_demo` 118/118 ; +- `cargo clippy --all-targets` propre. + +Lire `docs/V0_4_6_VALIDATION.md` avant toute modification. + +## 3. Lectures obligatoires + +1. `RULES.md`, `README.md`, `ROADMAP.md`, `CHANGELOG.md` et ce prompt ; +2. `docs/DECODER_MATERIALIZATION_CONTRACTS.md`, `docs/EXECUTION_MODEL.md`, `docs/REPLAY_PIPELINE.md`, `docs/INSTRUCTION_REPLAY_CONTRACTS.md` et `docs/SOLANA_INTERFACE_DEPENDENCIES.md` ; +3. `docs/SPL_TOKEN_MATRIX.json`, `docs/SPL_TOKEN_2022_MATRIX.json` et les contrats metadata existants ; +4. `kb_decoder_api`, `kb_store_core`, `kb_store_pg`, `kb_pipeline`, `kb_materializer_metadata`, `kb_materializer_admin`, `kb_materializer_lifecycle`, `kb_materializer_risk` et le registre runtime de `kb_app_demo` ; +5. les sources officielles Metaplex réellement résolues dans Cargo et les IDL/layouts publiés, sans supposer qu’un fichier local est actuel ou authentique. + +## 4. Audit avant code + +Établir d’abord une matrice machine-readable couvrant au minimum : + +- toutes les instructions actuellement publiées et les variantes historiques encore observables ; +- comptes ordonnés, signer/writable, formes optionnelles et autorités ; +- discriminateurs, arguments Borsh, suffixes et limites ; +- PDA `metadata`, `edition`, `master edition`, `collection authority record`, `use authority record`, `token record`, `metadata delegate record` et autres comptes réellement officiels ; +- versions de `Metadata`, `MasterEdition`, `Edition`, `EditionMarker`, `TokenRecord` et comptes auxiliaires ; +- `TokenStandard`, créateurs, collection, uses, programmable config, authorization rules et délégations ; +- différences entre metadata externes Metaplex, metadata Token-2022 incorporées et Metaplex Core ; +- builders/interfaces officiels disponibles et surfaces sans builder stable. + +Ne commencer le décodage maximal qu’après validation de cette matrice contre les sources officielles et un corpus synthétique. + +## 5. Architecture attendue + +Créer une frontière dédiée, typiquement `kb_decoder_metaplex_token_metadata`, avec : + +- compatibilité `Yes` uniquement pour le Program ID Metaplex vérifié ; +- provenance exacte du programme, de la version de layout et de l’instruction ; +- outer/CPI, chemin d’instruction, comptes ordonnés et statut de transaction ; +- diagnostics stables pour troncature, discriminant inconnu, comptes invalides, suffixes et variantes futures ; +- aucune interprétation par simple ressemblance de PDA ou de discriminant sous un autre Program ID. + +Réutiliser les API communes existantes. Ne pas créer une infrastructure Anchor dans ce jalon : Metaplex Token Metadata n’en dépend pas. Anchor reste planifié en `0.4.18`, juste avant Meteora. + +## 6. Décodage instructionnel maximal + +Couvrir les générations actuelles et historiques réellement publiées, notamment les familles : + +- création/mise à jour de metadata ; +- création d’édition et master edition ; +- mint d’éditions ; +- vérification/dé-vérification de créateurs et collections ; +- collection size et collection details ; +- uses ; +- délégations/revocations metadata, collection, use, sale, transfer, utility, staking et programmable ; +- burn, lock, unlock, transfer et autres opérations programmables ; +- migration/versionnement lorsqu’officiellement défini. + +Chaque variante doit préserver les données brutes significatives, les autorités, les comptes et la provenance sans inventer de valeur absente du wire. + +## 7. Comptes et PDA + +Pour chaque compte : + +- vérifier owner/program ID avant layout ; +- vérifier PDA et ordre exact des seeds lorsqu’une dérivation canonique existe ; +- borner chaînes, vecteurs, options, créateurs et enums ; +- distinguer tailles minimales, tailles exactes et comptes extensibles ; +- conserver les variantes futures en `Unsupported` plutôt que produire une projection partielle trompeuse ; +- corréler mint, edition, collection, token account et rule set uniquement lorsque les identités sont prouvées. + +## 8. Matérialisation + +Attribuer un propriétaire unique à chaque fait stable : + +- `metadata` : nom, symbole, URI, seller fee, créateurs, collection, uses, token standard, mutabilité, programmable config et provenance ; +- `admin` : update authority, collection authority, delegates et changements d’autorité ; +- `lifecycle` : création, mise à jour, vérification, édition, burn, lock/unlock et transitions prouvées ; +- `risk/compliance` : royalties, mutabilité, créateurs non vérifiés, rule sets et délégations sensibles, sans dupliquer les faits metadata. + +Conserver simultanément metadata Metaplex externe et metadata Token-2022 incorporée. Ne jamais les fusionner silencieusement ; exposer source, autorité, slot et éventuels conflits. + +## 9. JSON externe référencé par URI + +Le fetch HTTP/IPFS/Arweave doit rester optionnel, borné et séparé du replay canonique on-chain : + +- timeout, taille maximale, content type et redirections bornés ; +- cache/provenance/hash explicites ; +- aucune dépendance du statut de décodage on-chain à la disponibilité du contenu externe ; +- aucune confiance implicite dans le JSON distant ; +- pas de SSRF ni d’accès arbitraire aux réseaux locaux. + +Une première version peut documenter et réserver ce contrat sans activer le fetch si son isolation n’est pas suffisamment prouvée. + +## 10. Exécution + +N’ajouter des builders/exécuteurs qu’après validation exacte des interfaces officielles, comptes, autorités, coûts et préconditions. Toute opération doit rester simulation-first, dry-run par défaut, signataires exacts et plafond de dépense explicite. + +Les opérations destructives ou sensibles — burn, update authority, verify/unverify, delegate/revoke, lock/unlock — exigent une confirmation opérateur dédiée et des postconditions stateful. + +## 11. Corpus et validations + +Ajouter au minimum : + +- corpus synthétique pour chaque famille d’instruction et de compte ; +- fixtures réelles Mainnet avec tokens fongibles, NFT, SFT, collections et programmable NFT ; +- outer et CPI ; +- transactions réussies et échouées ; +- mauvais Program ID, mauvais owner, mauvais PDA, comptes manquants/dupliqués, payloads tronqués, suffixes et discriminants inconnus ; +- conflits entre metadata Metaplex externe et Token-2022 incorporée ; +- replay PostgreSQL idempotent, absence de doublons decode/mat et validation de `incomplete_signatures` ; +- démo `kb_app_demo` pour consulter les observations et projections pertinentes. + +## 12. Documentation et clôture + +Mettre à jour : + +- `README.md` ; +- `ROADMAP.md` ; +- `CHANGELOG.md` seulement après validation ; +- `docs/SOLANA_INTERFACE_DEPENDENCIES.md` ; +- une matrice Metaplex Token Metadata dédiée ; +- les README des crates modifiées ; +- le prompt de la session suivante `0.4.8 — Metaplex Core`. + +La clôture exige les tests ciblés, PostgreSQL réel, replay idempotent, tests Tauri si l’UI est modifiée et `cargo clippy --all-targets` propre. + +## 13. Livraisons + +Après la base initiale, livrer uniquement des ZIP delta avec `delta.md` et `manifest.sha256`. Les correctifs d’une même préversion conservent son numéro et utilisent `-delta-fix-XXX.zip`, avec une numérotation recommençant à `fix-001` pour chaque nouvelle préversion. diff --git a/migration/khadhroony-bot2-reference/prompts/README.md b/migration/khadhroony-bot2-reference/prompts/README.md new file mode 100644 index 0000000..f6d1509 --- /dev/null +++ b/migration/khadhroony-bot2-reference/prompts/README.md @@ -0,0 +1,51 @@ + + + +# Prompts de session + +Ce dossier contient les prompts utilisés pour ouvrir ou reprendre des sessions d'assistance par version. + +## Règles + +- Les noms de fichiers doivent être en anglais, sans espaces, accents ni caractères spéciaux inutiles. +- Les fichiers restent en Markdown et sont rédigés en français. +- Un prompt doit décrire le contexte, les règles applicables, les objectifs, les fichiers à lire, les interdictions et les validations attendues. +- Les prompts ne remplacent pas `ROADMAP.md` et ne doivent pas modifier `CHANGELOG.md` avant validation. +- Les prompts doivent rappeler le format du zip delta attendu. + +## Format de livraison attendu + +- Workspace ou plusieurs modules : `khadhroony-bot2_vX.Y.Z-pre.abc-delta.zip`. +- Un seul module Rust : `kb_modulename_vX.Y.Z-pre.abc-delta.zip`. +- Correctif d'un delta déjà livré : conserver `pre.abc` et utiliser `-delta-fix-001.zip`, puis `-fix-002.zip`; ne pas incrémenter `abc` pour une réparation. +- Chaque zip delta ou correctif doit contenir `delta.md` non versionné. +- `delta.md` doit lister les fichiers ajoutés, modifiés, à supprimer manuellement, les dépendances d'application du correctif, et les validations exécutées ou non exécutées. + +## Prompts actuels + +- `001_version_session_template.md` : modèle générique. +- `002_program_registry_and_naming_audit.md` : audit des noms, aliases et registres. +- `003_v0_1_logging_config_profiles.md` : logging et configuration multi-profils. +- `004_v0_2_sql_storage_materialization.md` : conventions storage, PostgreSQL et contrats DB historiques. +- `005_v0_3_rpc_ingestion_ws.md` : ancien prompt d’ingestion, conservé uniquement comme historique. +- `006_v0_4_wallet_execution_demo.md` : ancien phasage wallet, désormais reporté à `0.11.x`. +- `007_v0_5_core_decoders_listeners.md` : ancien phasage décodeurs, désormais remplacé par `0.4.x` à `0.10.x`. +- `008_v0_6_priority_dex_router_surfaces.md` : ancien phasage surfaces, conservé comme source historique. +- `009_v0_2_1_store_core_contracts.md` : contrats backend-agnostiques `kb_store_core` pour `0.2.1`. +- `010_v0_2_2_postgres_connection_health_migrations.md` : connexion PostgreSQL, pool, healthcheck et stratégie migrations. +- `011_v0_2_3_raw_store_minimal.md` : premières tables raw historiques. +- `012_v0_2_4_core_store_minimal.md` : tables core Solana minimales et repositories historiques. +- `013_v0_2_5_db_diagnostics_tauri.md` : diagnostics DB PostgreSQL dans `kb_app_demo`. +- `014_v0_3_0_agave_local_research.md` : prompt historique du cadrage Agave local validé puis abandonné comme axe actif. +- `015_v0_3_1_canonical_store_migration.md` : prompt historique de la transition vers la transaction canonique et les observations légères. +- `016_v0_3_2_canonical_transaction_contract.md` : prompt historique de l’implémentation du contrat canonique et de l’adaptateur HTTP. +- `017_v0_3_3_http_backfill_free_tier.md` : prompt historique du backfill HTTP paginé et de `demo_backfill`, validé et clôturé. +- `018_v0_3_4_canonical_to_core_extraction.md` : prompt historique validé et clôturé pour l’extraction transactionnelle, le replay core et la fondation `0.3.x`. +- `019_v0_4_0_decoder_infrastructure.md` : prompt historique validé pour les contrats, le dispatch, le ledger et le replay communs aux décodeurs et matérialiseurs. +- `020_v0_4_1_native_solana_programs.md` : prompt historique validé pour le décodage et les matérialisations natives Solana. +- `021_v0_4_2_executor_solana_core.md` : prompt historique validé pour l’infrastructure d’exécution et `kb_executor_solana_core`. +- `022_v0_4_3_spl_memo.md` : prompt historique validé pour le décodeur, la matérialisation et l’exécuteur SPL Memo v1/v3/v4. +- `023_v0_4_4_spl_token.md` : prompt historique validé pour le décodeur maximal, les matérialisations et l’exécuteur SPL Token classique. +- `024_v0_4_5_spl_associated_token_account.md` : prompt historique validé pour le décodeur, les matérialisations et l'exécuteur SPL Associated Token Account. +- `025_v0_4_6_spl_token_2022.md` : prompt historique validé pour Token-2022, ses extensions, ses interfaces externes et le registre ElGamal distinct. +- `026_v0_4_7_metaplex_token_metadata.md` : prompt actif pour Metaplex Token Metadata, ses comptes, PDA, NFT/collections, metadata externes et provenance. diff --git a/migration/khadhroony-bot2-reference/registry/.gitignore b/migration/khadhroony-bot2-reference/registry/.gitignore new file mode 100644 index 0000000..ef96e0b --- /dev/null +++ b/migration/khadhroony-bot2-reference/registry/.gitignore @@ -0,0 +1,3 @@ +# file: registry/.gitignore +# version: 1 + diff --git a/migration/khadhroony-bot2-reference/registry/core_program_id_seed.toml b/migration/khadhroony-bot2-reference/registry/core_program_id_seed.toml new file mode 100644 index 0000000..0ada195 --- /dev/null +++ b/migration/khadhroony-bot2-reference/registry/core_program_id_seed.toml @@ -0,0 +1,366 @@ +# file: registry/core_program_id_seed.toml +# version: 5 + +# This file tracks Solana/SPL primitive identifiers separately from application program surfaces. +# It includes executable programs, loader programs, sysvar accounts, and well-known native accounts. + +[[core_id]] +canonical_code = "core_solana_address_lookup_table_v1" +program_id = "AddressLookupTab1e1111111111111111111111111" +source_code = "address_lookup_table" +identifier_kind = "program" +target_crate = "kb_decoder_solana_core" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_solana_bpf_loader_deprecated_v1" +program_id = "BPFLoader1111111111111111111111111111111111" +source_code = "bpf_loader_deprecated" +identifier_kind = "loader_program" +target_crate = "kb_decoder_solana_core" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_solana_bpf_loader_v2" +program_id = "BPFLoader2111111111111111111111111111111111" +source_code = "bpf_loader" +identifier_kind = "loader_program" +target_crate = "kb_decoder_solana_core" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_solana_bpf_loader_upgradeable_v1" +program_id = "BPFLoaderUpgradeab1e11111111111111111111111" +source_code = "bpf_loader_upgradeable" +identifier_kind = "loader_program" +target_crate = "kb_decoder_solana_core" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_solana_compute_budget_v1" +program_id = "ComputeBudget111111111111111111111111111111" +source_code = "compute_budget" +identifier_kind = "program" +target_crate = "kb_decoder_solana_core" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_solana_config_v1" +program_id = "Config1111111111111111111111111111111111111" +source_code = "config" +identifier_kind = "program" +target_crate = "kb_decoder_solana_core" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_solana_ed25519_v1" +program_id = "Ed25519SigVerify111111111111111111111111111" +source_code = "ed25519" +identifier_kind = "program" +target_crate = "kb_decoder_solana_core" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_solana_feature_v1" +program_id = "Feature111111111111111111111111111111111111" +source_code = "feature" +identifier_kind = "program" +target_crate = "kb_decoder_solana_core" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_solana_incinerator_v1" +program_id = "1nc1nerator11111111111111111111111111111111" +source_code = "incinerator" +identifier_kind = "well_known_account" +target_crate = "kb_decoder_solana_core" +registry_status = "core_recognized_account" + +[[core_id]] +canonical_code = "core_solana_loader_v4" +program_id = "LoaderV411111111111111111111111111111111111" +source_code = "loader_v4" +identifier_kind = "loader_program" +target_crate = "kb_decoder_solana_core" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_solana_native_loader_v1" +program_id = "NativeLoader1111111111111111111111111111111" +source_code = "native_loader" +identifier_kind = "loader_program" +target_crate = "kb_decoder_solana_core" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_solana_secp256k1_v1" +program_id = "KeccakSecp256k11111111111111111111111111111" +source_code = "secp256k1" +identifier_kind = "program" +target_crate = "kb_decoder_solana_core" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_solana_secp256r1_v1" +program_id = "Secp256r1SigVerify1111111111111111111111111" +source_code = "secp256r1" +identifier_kind = "program" +target_crate = "kb_decoder_solana_core" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_solana_slashing_v1" +program_id = "S1ashing11111111111111111111111111111111111" +source_code = "slashing" +identifier_kind = "stateless_program" +target_crate = "kb_decoder_solana_core" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_solana_stake_config_v1" +program_id = "StakeConfig11111111111111111111111111111111" +source_code = "stake_config" +identifier_kind = "well_known_account" +target_crate = "kb_decoder_solana_core" +registry_status = "core_recognized_account" + +[[core_id]] +canonical_code = "core_solana_stake_v1" +program_id = "Stake11111111111111111111111111111111111111" +source_code = "stake" +identifier_kind = "program" +target_crate = "kb_decoder_solana_core" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_solana_system_v1" +program_id = "11111111111111111111111111111111" +source_code = "system" +identifier_kind = "program" +target_crate = "kb_decoder_solana_core" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_solana_sysvar_clock_v1" +program_id = "SysvarC1ock11111111111111111111111111111111" +source_code = "sysvar_clock" +identifier_kind = "sysvar_account" +target_crate = "kb_decoder_solana_core" +registry_status = "core_recognized_account" + +[[core_id]] +canonical_code = "core_solana_sysvar_epoch_rewards_v1" +program_id = "SysvarEpochRewards1111111111111111111111111" +source_code = "sysvar_epoch_rewards" +identifier_kind = "sysvar_account" +target_crate = "kb_decoder_solana_core" +registry_status = "core_recognized_account" + +[[core_id]] +canonical_code = "core_solana_sysvar_epoch_schedule_v1" +program_id = "SysvarEpochSchedu1e111111111111111111111111" +source_code = "sysvar_epoch_schedule" +identifier_kind = "sysvar_account" +target_crate = "kb_decoder_solana_core" +registry_status = "core_recognized_account" + +[[core_id]] +canonical_code = "core_solana_sysvar_fees_v1" +program_id = "SysvarFees111111111111111111111111111111111" +source_code = "sysvar_fees" +identifier_kind = "sysvar_account" +target_crate = "kb_decoder_solana_core" +registry_status = "core_recognized_account" + +[[core_id]] +canonical_code = "core_solana_sysvar_instructions_v1" +program_id = "Sysvar1nstructions1111111111111111111111111" +source_code = "sysvar_instructions" +identifier_kind = "sysvar_account" +target_crate = "kb_decoder_solana_core" +registry_status = "core_recognized_account" + +[[core_id]] +canonical_code = "core_solana_sysvar_last_restart_slot_v1" +program_id = "SysvarLastRestartS1ot1111111111111111111111" +source_code = "sysvar_last_restart_slot" +identifier_kind = "sysvar_account" +target_crate = "kb_decoder_solana_core" +registry_status = "core_recognized_account" + +[[core_id]] +canonical_code = "core_solana_sysvar_recent_blockhashes_v1" +program_id = "SysvarRecentB1ockHashes11111111111111111111" +source_code = "sysvar_recent_blockhashes" +identifier_kind = "sysvar_account" +target_crate = "kb_decoder_solana_core" +registry_status = "core_recognized_account" + +[[core_id]] +canonical_code = "core_solana_sysvar_rent_v1" +program_id = "SysvarRent111111111111111111111111111111111" +source_code = "sysvar_rent" +identifier_kind = "sysvar_account" +target_crate = "kb_decoder_solana_core" +registry_status = "core_recognized_account" + +[[core_id]] +canonical_code = "core_solana_sysvar_rewards_v1" +program_id = "SysvarRewards111111111111111111111111111111" +source_code = "sysvar_rewards" +identifier_kind = "sysvar_account" +target_crate = "kb_decoder_solana_core" +registry_status = "core_recognized_account" + +[[core_id]] +canonical_code = "core_solana_sysvar_slot_hashes_v1" +program_id = "SysvarS1otHashes111111111111111111111111111" +source_code = "sysvar_slot_hashes" +identifier_kind = "sysvar_account" +target_crate = "kb_decoder_solana_core" +registry_status = "core_recognized_account" + +[[core_id]] +canonical_code = "core_solana_sysvar_slot_history_v1" +program_id = "SysvarS1otHistory11111111111111111111111111" +source_code = "sysvar_slot_history" +identifier_kind = "sysvar_account" +target_crate = "kb_decoder_solana_core" +registry_status = "core_recognized_account" + +[[core_id]] +canonical_code = "core_solana_sysvar_stake_history_v1" +program_id = "SysvarStakeHistory1111111111111111111111111" +source_code = "sysvar_stake_history" +identifier_kind = "sysvar_account" +target_crate = "kb_decoder_solana_core" +registry_status = "core_recognized_account" + +[[core_id]] +canonical_code = "core_solana_sysvar_v1" +program_id = "Sysvar1111111111111111111111111111111111111" +source_code = "sysvar" +identifier_kind = "sysvar_account" +target_crate = "kb_decoder_solana_core" +registry_status = "core_recognized_account" + +[[core_id]] +canonical_code = "core_solana_vote_v1" +program_id = "Vote111111111111111111111111111111111111111" +source_code = "vote" +identifier_kind = "program" +target_crate = "kb_decoder_solana_core" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_solana_zk_elgamal_proof_v1" +program_id = "ZkE1Gama1Proof11111111111111111111111111111" +source_code = "zk_elgamal_proof" +identifier_kind = "program" +target_crate = "kb_decoder_solana_core" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_solana_zk_token_proof_v1" +program_id = "ZkTokenProof1111111111111111111111111111111" +source_code = "zk_token_proof" +identifier_kind = "program" +target_crate = "kb_decoder_solana_core" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_spl_associated_token_account_v1" +program_id = "ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL" +source_code = "associated_token_account" +identifier_kind = "program" +target_crate = "kb_decoder_spl_associated_token_account" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_spl_account_compression_v1" +program_id = "cmtDvXumGCrqC1Age74AVPhSRVXJMd8PJS91L8KbNCK" +source_code = "spl_account_compression" +identifier_kind = "program" +target_crate = "kb_decoder_spl_account_compression" +registry_status = "core_specialized_reserved_current" + +[[core_id]] +canonical_code = "core_spl_memo_v1" +program_id = "Memo1UhkJRfHyvLMcVucJwxXeuD728EqVDDwQDxFMNo" +source_code = "spl_memo_v1" +identifier_kind = "program" +target_crate = "kb_decoder_spl_memo" +registry_status = "core_specialized_reserved_current" + +[[core_id]] +canonical_code = "core_spl_memo_v3" +program_id = "MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr" +source_code = "spl_memo_v3" +identifier_kind = "program" +target_crate = "kb_decoder_spl_memo" +registry_status = "core_specialized_reserved_current" + +[[core_id]] +canonical_code = "core_spl_memo_v4" +program_id = "Memo4c2pN8afCj432Lb7RMVKi9PbQnnW7ewFFaV3oAH" +source_code = "spl_memo_v4" +identifier_kind = "program" +target_crate = "kb_decoder_spl_memo" +registry_status = "core_specialized_reserved_current" + +[[core_id]] +canonical_code = "core_spl_noop_v1" +program_id = "noopb9bkMVfRPU8AsbpTUg8AQkHtKwMYZiFUjNRtMmV" +source_code = "spl_noop" +identifier_kind = "program" +target_crate = "kb_decoder_spl_noop" +registry_status = "core_specialized_reserved_current" + +[[core_id]] +canonical_code = "core_spl_single_pool_v1" +program_id = "SVSPxpvHdN29nkVg9rPapPNDddN5DipNLRUFhyjFThE" +source_code = "spl_single_pool" +identifier_kind = "program" +target_crate = "kb_decoder_spl_single_pool" +registry_status = "core_specialized_reserved_current" + +[[core_id]] +canonical_code = "core_spl_name_service_v1" +program_id = "namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX" +source_code = "spl_name_service" +identifier_kind = "program" +target_crate = "kb_decoder_metadata_spl_name_service" +registry_status = "core_specialized_reserved_current" + +[[core_id]] +canonical_code = "core_spl_stake_pool_v1" +program_id = "SPoo1Ku8WFXoNDMHPsrGSTSG1Y47rzgn41SLUNakuHy" +source_code = "stake_pool" +identifier_kind = "program" +target_crate = "kb_decoder_spl_stake_pool" +registry_status = "core_specialized_reserved_current" + +[[core_id]] +canonical_code = "core_spl_token_2022_v2022" +program_id = "TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb" +source_code = "token_2022" +identifier_kind = "program" +target_crate = "kb_decoder_spl_token_2022" +registry_status = "core_handled" + +[[core_id]] +canonical_code = "core_spl_token_2022_elgamal_registry_v1" +program_id = "regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg" +source_code = "spl_token_2022_elgamal_registry" +identifier_kind = "program" +target_crate = "kb_decoder_spl_token_2022" +registry_status = "core_specialized_reserved_current" + +[[core_id]] +canonical_code = "core_spl_token_v1" +program_id = "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA" +source_code = "token" +identifier_kind = "program" +target_crate = "kb_decoder_spl_token" +registry_status = "core_handled" + diff --git a/migration/khadhroony-bot2-reference/registry/program_registry_seed.toml b/migration/khadhroony-bot2-reference/registry/program_registry_seed.toml new file mode 100644 index 0000000..e1ea21d --- /dev/null +++ b/migration/khadhroony-bot2-reference/registry/program_registry_seed.toml @@ -0,0 +1,1919 @@ +# file: registry/program_registry_seed.toml +# version: 2 + +# This registry is a seed file. Field names and comments are in English because TOML is a code/config file. +# Documentation explaining the registry is written in French under docs/. +# Application surface codes are sorted alphabetically by canonical_surface_code. +# The legacy fallback prefix `program_` is forbidden. Unclassified entries use `unknown_` and must not become crates until their functional category is decided. + + +[[program]] +program_id = "FEEhPbKVKnco9EXnaY3i4R5rQVUx91wgVfu8qokixywi" +source_code = "bags_fee_share_v1" +normalized_code = "bags_fee_share_v1" +canonical_surface_code = "fees_bags_fee_share_v1" +target_crate = "kb_decoder_fees_bags_fee_share_v1" +current_crate = "kb_decoder_fees_bags_fee_share_v1" +function_code = "fees" +family_code = "bags" +version_code = "v1" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "FEE2tBhCKAt7shrod19QttSVREUYPiyMzoku1mL1gqVK" +source_code = "bags_fee_share_v2" +normalized_code = "bags_fee_share_v2" +canonical_surface_code = "fees_bags_fee_share_v2" +target_crate = "kb_decoder_fees_bags_fee_share_v2" +current_crate = "kb_decoder_fees_bags_fee_share_v2" +function_code = "fees" +family_code = "bags" +version_code = "v2" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "DecZY86MU5Gj7kppfUCEmd4LbXXuyZH1yHaP2NTqdiZB" +source_code = "sabre_decimal_wrapper" +normalized_code = "sabre_decimal_wrapper" +canonical_surface_code = "adapter_saber_decimal_wrapper" +target_crate = "kb_decoder_adapter_saber_decimal_wrapper" +current_crate = "kb_decoder_adapter_saber_decimal_wrapper" +function_code = "adapter" +family_code = "saber" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "LocpQgucEQHbqNABEYvBvwoxCPsSbG91A1QaQhQQqjn" +source_code = "jupiter_lock" +normalized_code = "jupiter_lock" +canonical_surface_code = "admin_jupiter_lock" +target_crate = "kb_decoder_admin_jupiter_lock" +current_crate = "kb_decoder_admin_jupiter_lock" +function_code = "admin" +family_code = "jupiter" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "pfeeUxB6jkeY1Hxd7CsFCAjcbHA9rWtchMGdZ6VojVZ" +source_code = "pump_fees" +normalized_code = "pump_fees" +canonical_surface_code = "admin_pump_fees" +target_crate = "kb_decoder_admin_pump_fees" +current_crate = "kb_decoder_admin_pump_fees" +function_code = "admin" +family_code = "pump" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "AMM55ShdkoGRB5jVYPjWziwk8m5MpwyDgsMWHaMSQWH6" +source_code = "amm_aldrin_v1" +normalized_code = "amm_aldrin_v1" +canonical_surface_code = "amm_aldrin_v1" +target_crate = "kb_decoder_amm_aldrin_v1" +current_crate = "" +function_code = "amm" +family_code = "aldrin" +version_code = "v1" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "CURVGoZn8zycx6FXwwevgBTB2gVvdbGTEpvMJDbgs2t4" +source_code = "amm_aldrin_v2" +normalized_code = "amm_aldrin_v2" +canonical_surface_code = "amm_aldrin_v2" +target_crate = "kb_decoder_amm_aldrin_v2" +current_crate = "" +function_code = "amm" +family_code = "aldrin" +version_code = "v2" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "ALPHAQmeA7bjrVuccPsYPiCvsi428SNwte66Srvs4pHA" +source_code = "alphaq" +normalized_code = "alphaq" +canonical_surface_code = "amm_alphaq" +target_crate = "kb_decoder_amm_alphaq" +current_crate = "kb_decoder_amm_alphaq" +function_code = "amm" +family_code = "alphaq" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "5qWya6UjwWnGVhdSBL3hyZ7B45jbk6Byt1hwd7ohEGXE" +source_code = "believe" +normalized_code = "believe" +canonical_surface_code = "amm_believe" +target_crate = "kb_decoder_amm_believe" +current_crate = "kb_decoder_amm_believe" +function_code = "amm" +family_code = "believe" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "BSwp6bEBihVLdqJRKGgzjcGLHkcTuzmSo1TQkHepzH8p" +source_code = "bonk_swap" +normalized_code = "bonk_swap" +canonical_surface_code = "amm_bonk_swap" +target_crate = "kb_decoder_amm_bonk_swap" +current_crate = "kb_decoder_amm_bonk_swap" +function_code = "amm" +family_code = "bonk" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "CTMAxxk34HjKWxQ3QLZK1HpaLXmBveao3ESePXbiyfzh" +source_code = "cropper_finance" +normalized_code = "cropper_finance" +canonical_surface_code = "amm_cropper_finance" +target_crate = "kb_decoder_amm_cropper_finance" +current_crate = "" +function_code = "amm" +family_code = "cropper" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "DSwpgjMvXhtGn6BsbqmacdBZyfLj6jSWf3HJpdJtmg6N" +source_code = "dexlab_swap" +normalized_code = "dexlab_swap" +canonical_surface_code = "amm_dexlab_swap" +target_crate = "kb_decoder_amm_dexlab_swap" +current_crate = "" +function_code = "amm" +family_code = "dexlab" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "FLUXubRmkEi2q6K3Y9kBPg9248ggaZVsoSFhtJHSrm1X" +source_code = "fluxbeam" +normalized_code = "fluxbeam" +canonical_surface_code = "amm_fluxbeam" +target_crate = "kb_decoder_amm_fluxbeam" +current_crate = "kb_decoder_amm_fluxbeam" +function_code = "amm" +family_code = "fluxbeam" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "goonERTdGsjnkZqWuVjs73BZ3Pb9qoCUdBUL17BnS5j" +source_code = "goon_fi" +normalized_code = "goon_fi" +canonical_surface_code = "amm_goon_fi" +target_crate = "kb_decoder_amm_goon_fi" +current_crate = "kb_decoder_amm_goon_fi" +function_code = "amm" +family_code = "goon_fi" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "goonuddtQRrWqqn5nFyczVKaie28f3kDkHWkHtURSLE" +source_code = "goon_fi_v2" +normalized_code = "goon_fi_v2" +canonical_surface_code = "amm_goon_fi_v2" +target_crate = "kb_decoder_amm_goon_fi_v2" +current_crate = "" +function_code = "amm" +family_code = "goon_fi" +version_code = "v2" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "GAMMA7meSFWaBXF25oSUgmGRwaW6sCMFLmBNiMSdbHVT" +source_code = "goose_fx_gamma" +normalized_code = "goosefx_gamma" +canonical_surface_code = "amm_goosefx_gamma" +target_crate = "kb_decoder_amm_goosefx_gamma" +current_crate = "kb_decoder_amm_goosefx_gamma" +function_code = "amm" +family_code = "goosefx" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "GFXsSL5sSaDfNFQUYsHekbWBW1TsFdjDYzACh62tEHxn" +source_code = "goose_fx_v2" +normalized_code = "goosefx_v2" +canonical_surface_code = "amm_goosefx_v2" +target_crate = "kb_decoder_amm_goosefx_v2" +current_crate = "kb_decoder_amm_goosefx_v2" +function_code = "amm" +family_code = "goosefx" +version_code = "v2" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "Gswppe6ERWKpUTXvRPfXdzHhiCyJvLadVvXGfdpBqcE1" +source_code = "guac_swap" +normalized_code = "guac_swap" +canonical_surface_code = "amm_guac_swap" +target_crate = "kb_decoder_amm_guac_swap" +current_crate = "kb_decoder_amm_guac_swap" +function_code = "amm" +family_code = "guac" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "HEAVENoP2qxoeuF8Dj2oT1GHEnu49U5mJYkdeC8BAX2o" +source_code = "heaven_dex" +normalized_code = "heaven_dex" +canonical_surface_code = "amm_heaven_dex" +target_crate = "kb_decoder_amm_heaven_dex" +current_crate = "" +function_code = "amm" +family_code = "heaven" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "HyaB3W9q6XdA5xwpU4XnSZV94htfmbmqJXZcEbRaJutt" +source_code = "invariant_swap" +normalized_code = "invariant_swap" +canonical_surface_code = "amm_invariant_swap" +target_crate = "kb_decoder_amm_invariant_swap" +current_crate = "" +function_code = "amm" +family_code = "invariant" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "EewxydAPCCVuNEyrVN68PuSYdQ7wKn27V9Gjeoi8dy3S" +source_code = "lifinity_swap" +normalized_code = "lifinity_swap" +canonical_surface_code = "amm_lifinity_swap" +target_crate = "kb_decoder_amm_lifinity_swap" +current_crate = "" +function_code = "amm" +family_code = "lifinity" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "2wT8Yq49kHgDzXuPxZSaeLaH1qbmGXtEyPy64bL7aD3c" +source_code = "lifinity_swap_v2" +normalized_code = "lifinity_swap_v2" +canonical_surface_code = "amm_lifinity_swap_v2" +target_crate = "kb_decoder_amm_lifinity_swap_v2" +current_crate = "kb_decoder_amm_lifinity_swap_v2" +function_code = "amm" +family_code = "lifinity" +version_code = "v2" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "9tKE7Mbmj4mxDjWatikzGAtkoWosiiZX9y6J4Hfm2R8H" +source_code = "marcopolo_swap" +normalized_code = "marcopolo_swap" +canonical_surface_code = "amm_marcopolo_swap" +target_crate = "kb_decoder_amm_marcopolo_swap" +current_crate = "" +function_code = "amm" +family_code = "marcopolo" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "FUTARELBfJfQ8RDGhg1wdhddq1odMAJUePHFuBYfUxKq" +source_code = "metadao_futarchy_amm" +normalized_code = "metadao_futarchy_amm" +canonical_surface_code = "amm_metadao_futarchy_amm" +target_crate = "kb_decoder_amm_metadao_futarchy_amm" +current_crate = "kb_decoder_amm_metadao_futarchy_amm" +function_code = "amm" +family_code = "metadao" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "AMMJdEiCCa8mdugg6JPF7gFirmmxisTfDJoSNSUi5zDJ" +source_code = "metadao_amm_v0_5" +normalized_code = "metadao_amm_v0_5" +canonical_surface_code = "amm_metadao_v0_5" +target_crate = "kb_decoder_amm_metadao_v0_5" +current_crate = "kb_decoder_amm_metadao_v0_5" +function_code = "amm" +family_code = "metadao" +version_code = "v0_5" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "Eo7WjKq67rjJQSZxS6z3YkapzY3eMj6Xy8X5EQVn5UaB" +source_code = "meteora_damm_v1" +normalized_code = "meteora_damm_v1" +canonical_surface_code = "amm_meteora_damm_v1" +target_crate = "kb_decoder_amm_meteora_damm_v1" +current_crate = "kb_decoder_amm_meteora_damm_v1" +function_code = "amm" +family_code = "meteora" +version_code = "v1" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "cpamdpZCGKUy5JxQXB4dcpGPiikHawvSWAd6mEn1sGG" +source_code = "meteora_damm_v2" +normalized_code = "meteora_damm_v2" +canonical_surface_code = "amm_meteora_damm_v2" +target_crate = "kb_decoder_amm_meteora_damm_v2" +current_crate = "kb_decoder_amm_meteora_damm_v2" +function_code = "amm" +family_code = "meteora" +version_code = "v2" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "obriQD1zbpyLz95G5n7nJe6a4DPjpFwa5XYPoNm113y" +source_code = "obric_v2" +normalized_code = "obric_v2" +canonical_surface_code = "amm_obric_v2" +target_crate = "kb_decoder_amm_obric_v2" +current_crate = "kb_decoder_amm_obric_v2" +function_code = "amm" +family_code = "obric" +version_code = "v2" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "DEXYosS6oEGvk8uCDayvwEZz4qEyDJRf9nFgYCaqPMTm" +source_code = "one_dex" +normalized_code = "one_dex" +canonical_surface_code = "amm_one_dex" +target_crate = "kb_decoder_amm_one_dex" +current_crate = "kb_decoder_amm_one_dex" +function_code = "amm" +family_code = "one" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "DjVE6JNiYqPL2QXyCUUh8rNjHrbz9hXHNYt99MQ59qw1" +source_code = "orca_token_swap" +normalized_code = "orca_token_swap" +canonical_surface_code = "amm_orca_token_swap" +target_crate = "kb_decoder_amm_orca_token_swap" +current_crate = "" +function_code = "amm" +family_code = "orca" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "9W959DqEETiGZocYWCQPaJ6sBmUzgfxXfqGeTEdp3aQP" +source_code = "orca_token_swap_v2" +normalized_code = "orca_token_swap_v2" +canonical_surface_code = "amm_orca_token_swap_v2" +target_crate = "kb_decoder_amm_orca_token_swap_v2" +current_crate = "" +function_code = "amm" +family_code = "orca" +version_code = "v2" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "pAMMBay6oceH9fJKBRHGP5D4bD4sWpmSwMn52FMfXEA" +source_code = "pump_swap" +normalized_code = "pump_swap" +canonical_surface_code = "amm_pump_swap" +target_crate = "kb_decoder_amm_pump_swap" +current_crate = "kb_decoder_amm_pump_swap" +function_code = "amm" +family_code = "pump" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "5quBtoiQqxF9Jv6KYKctB59NT3gtJD2Y65kdnB1Uev3h" +source_code = "raydium_lp_amm" +normalized_code = "raydium_lp_amm" +canonical_surface_code = "amm_raydium_lp_amm" +target_crate = "kb_decoder_amm_raydium_lp_amm" +current_crate = "" +function_code = "lock" +family_code = "raydium" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "RVKd61ztZW9GUwhRbbLoYVRE5Xf1B2tVscKqwZqXgEr" +source_code = "raydium_lp_v2" +normalized_code = "raydium_lp_v2" +canonical_surface_code = "amm_raydium_lp_v2" +target_crate = "kb_decoder_amm_raydium_lp_v2" +current_crate = "" +function_code = "lock" +family_code = "raydium" +version_code = "v2" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "27haf8L6oxUeXrHrgEgsexjSY5hbVUWEmvv9Nyxg8vQv" +source_code = "raydium_lp_v3" +normalized_code = "raydium_lp_v3" +canonical_surface_code = "amm_raydium_lp_v3" +target_crate = "kb_decoder_amm_raydium_lp_v3" +current_crate = "" +function_code = "lock" +family_code = "raydium" +version_code = "v3" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "675kPX9MHTjS2zt1qfr1NYHuzeLXfQM9H24wFSUt1Mp8" +source_code = "raydium_lp_v4" +normalized_code = "raydium_lp_v4" +canonical_surface_code = "amm_raydium_lp_v4" +target_crate = "kb_decoder_amm_raydium_lp_v4" +current_crate = "kb_decoder_amm_raydium_lp_v4" +function_code = "amm" +family_code = "raydium" +version_code = "v4" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "SSwapUtytfBdBn1b9NUGG6foMVPtcWgpRU32HToDUZr" +source_code = "saros_amm" +normalized_code = "saros_amm" +canonical_surface_code = "amm_saros" +target_crate = "kb_decoder_amm_saros" +current_crate = "" +function_code = "amm" +family_code = "saros" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "SoLFiHG9TfgtdUXUjWAxi3LtvYuFyDLVhBWxdMZxyCe" +source_code = "solfi" +normalized_code = "solfi" +canonical_surface_code = "amm_solfi" +target_crate = "kb_decoder_amm_solfi" +current_crate = "kb_decoder_amm_solfi" +function_code = "amm" +family_code = "solfi" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "SV2EYYJyRz2YhfXwXnhNAevDEui5Q6yrfyo13WtupPF" +source_code = "solfi_v2" +normalized_code = "solfi_v2" +canonical_surface_code = "amm_solfi_v2" +target_crate = "kb_decoder_amm_solfi_v2" +current_crate = "kb_decoder_amm_solfi_v2" +function_code = "amm" +family_code = "solfi" +version_code = "v2" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "SSwpMgqNDsyV7mAgN9ady4bDVu5ySjmmXejXvy2vLt1" +source_code = "step_finance_swap" +normalized_code = "step_finance_swap" +canonical_surface_code = "amm_step_finance_swap" +target_crate = "kb_decoder_amm_step_finance_swap" +current_crate = "" +function_code = "amm" +family_code = "step_finance" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "Dooar9JkhdZ7J3LHN3A7YCuoGRUggXhQaG4kijfLGU2j" +source_code = "stepn_dooar_swap" +normalized_code = "stepn_dooar_swap" +canonical_surface_code = "amm_stepn_dooar_swap" +target_crate = "kb_decoder_amm_stepn_dooar_swap" +current_crate = "" +function_code = "amm" +family_code = "stepn" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "SwaPpA9LAaLfeLi3a68M4DjnLqgtticKg6CnyNwgAC8" +source_code = "swap" +normalized_code = "swap" +canonical_surface_code = "amm_swap_v1" +target_crate = "kb_decoder_amm_swap_v1" +current_crate = "" +function_code = "amm" +family_code = "swap" +version_code = "v1" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "vrTGoBuy5rYSxAfV3jaRJWHH6nN9WK4NRExGxsk1bCJ" +source_code = "vertigo" +normalized_code = "vertigo" +canonical_surface_code = "amm_vertigo" +target_crate = "kb_decoder_amm_vertigo" +current_crate = "kb_decoder_amm_vertigo" +function_code = "amm" +family_code = "vertigo" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "5U3EU2ubXtK84QcRjWVmYt9RaDyA8gKxdUrPFXmZyaki" +source_code = "virtuals" +normalized_code = "virtuals" +canonical_surface_code = "amm_virtuals" +target_crate = "kb_decoder_amm_virtuals" +current_crate = "kb_decoder_amm_virtuals" +function_code = "amm" +family_code = "virtuals" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "WooFif76YGRNjk1pA8wCsN67aQsD9f9iLsz4NcJ1AVb" +source_code = "woofi" +normalized_code = "woofi" +canonical_surface_code = "amm_woofi" +target_crate = "kb_decoder_amm_woofi" +current_crate = "kb_decoder_amm_woofi" +function_code = "amm" +family_code = "woofi" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "ZERor4xhbUycZ6gb9ntrhqscUcZmAbQDjEAtCf4hbZY" +source_code = "zero_fi" +normalized_code = "zero_fi" +canonical_surface_code = "amm_zero_fi" +target_crate = "kb_decoder_amm_zero_fi" +current_crate = "kb_decoder_amm_zero_fi" +function_code = "amm" +family_code = "zero" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "zoRabwLGd5zXaV7Gxacppw8tcceXEiTrSKyNLSaSTUc" +source_code = "zora" +normalized_code = "zora" +canonical_surface_code = "amm_zora" +target_crate = "kb_decoder_amm_zora" +current_crate = "kb_decoder_amm_zora" +function_code = "amm" +family_code = "zora" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3" +source_code = "cctp_token_messenger_minter" +normalized_code = "cctp_token_messenger_minter" +canonical_surface_code = "bridge_circle_cctp_token_messenger_minter" +target_crate = "kb_decoder_bridge_circle_cctp_token_messenger_minter" +current_crate = "kb_decoder_bridge_circle_cctp_token_messenger_minter" +function_code = "bridge" +family_code = "circle_cctp" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "CCTPV2vPZJS2u2BBsUoscuikbYjnpFmbFsvVuJdgUMQe" +source_code = "cctp_token_messenger_minter_v2" +normalized_code = "cctp_token_messenger_minter_v2" +canonical_surface_code = "bridge_circle_cctp_token_messenger_minter_v2" +target_crate = "kb_decoder_bridge_circle_cctp_token_messenger_minter_v2" +current_crate = "kb_decoder_bridge_circle_cctp_token_messenger_minter_v2" +function_code = "bridge" +family_code = "circle_cctp" +version_code = "v2" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "dst5MGcFPoBeREFAA5E3tU5ij8m5uVYwkzkSAbsLbNo" +source_code = "de_bridge_destination" +normalized_code = "de_bridge_destination" +canonical_surface_code = "bridge_de_bridge_destination" +target_crate = "kb_decoder_bridge_de_bridge_destination" +current_crate = "" +function_code = "bridge" +family_code = "de_bridge" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "src5qyZHqTqecJV4aY6Cb6zDZLMDzrDKKezs22MPHr4" +source_code = "de_bridge_source" +normalized_code = "de_bridge_source" +canonical_surface_code = "bridge_de_bridge_source" +target_crate = "kb_decoder_bridge_de_bridge_source" +current_crate = "" +function_code = "bridge" +family_code = "de_bridge" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "76y77prsiCMvXMjuoZ5VRrhG5qYBrUMYTE5WgHqgjEn6" +source_code = "layer_zero_endpoint" +normalized_code = "layer_zero_endpoint" +canonical_surface_code = "bridge_layer_zero_endpoint" +target_crate = "kb_decoder_bridge_layer_zero_endpoint" +current_crate = "kb_decoder_bridge_layer_zero_endpoint" +function_code = "bridge" +family_code = "layer_zero" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "6doghB248px58JSSwG4qejQ46kFMW4AMj7vzJnWZHNZn" +source_code = "layer_zero_executor" +normalized_code = "layer_zero_executor" +canonical_surface_code = "bridge_layer_zero_executor" +target_crate = "kb_decoder_bridge_layer_zero_executor" +current_crate = "kb_decoder_bridge_layer_zero_executor" +function_code = "bridge" +family_code = "layer_zero" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "wormDTUJ6AWPNvk59vGQbDvGJmqbDTdgWgAqcLBCgUb" +source_code = "whormhole_bridge" +normalized_code = "wormhole_bridge" +canonical_surface_code = "bridge_wormhole" +target_crate = "kb_decoder_bridge_wormhole" +current_crate = "" +function_code = "bridge" +family_code = "wormhole" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "REALQqNEomY6cQGZJUGwywTBD2UmDT32rZcNnfxQ5N2" +source_code = "byreal_clmm" +normalized_code = "byreal_clmm" +canonical_surface_code = "clmm_byreal" +target_crate = "kb_decoder_clmm_byreal" +current_crate = "kb_decoder_clmm_byreal" +function_code = "clmm" +family_code = "byreal" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "CLMM9tUoggJu2wagPkkqs9eFG4BWhVBZWkP1qv3Sp7tR" +source_code = "crema_finance" +normalized_code = "crema_finance" +canonical_surface_code = "clmm_crema_finance" +target_crate = "kb_decoder_clmm_crema_finance" +current_crate = "" +function_code = "clmm" +family_code = "crema" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "H8W3ctz92svYg6mkn1UtGfu2aQr2fnUFHM1RhScEtQDt" +source_code = "cropper_whirlpool" +normalized_code = "cropper_whirlpool" +canonical_surface_code = "clmm_cropper_whirlpool" +target_crate = "kb_decoder_clmm_cropper_whirlpool" +current_crate = "" +function_code = "clmm" +family_code = "cropper" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "fUSioN9YKKSa3CUC2YUc4tPkHJ5Y6XW1yz8y6F7qWz9" +source_code = "fusion_amm" +normalized_code = "fusion_amm" +canonical_surface_code = "clmm_fusion" +target_crate = "kb_decoder_clmm_fusion" +current_crate = "kb_decoder_clmm_fusion" +function_code = "clmm" +family_code = "fusion" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc" +source_code = "orca_whirlpool" +normalized_code = "orca_whirlpool" +canonical_surface_code = "clmm_orca_whirlpool" +target_crate = "kb_decoder_clmm_orca_whirlpool" +current_crate = "kb_decoder_clmm_orca_whirlpool" +function_code = "clmm" +family_code = "orca" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "orca_whirlpool,orca_whirlpool" + +[[program]] +program_id = "whirLbMiicVdio4qvUfM5KAg6Ct8VwpYzGff3uctyCc" +source_code = "orca_whirlpool" +normalized_code = "orca_whirlpool" +canonical_surface_code = "clmm_orca_whirlpool" +target_crate = "kb_decoder_clmm_orca_whirlpool" +current_crate = "kb_decoder_clmm_orca_whirlpool" +function_code = "clmm" +family_code = "orca" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "orca_whirlpool,orca_whirlpool" + +[[program]] +program_id = "HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq" +source_code = "pancake_swap" +normalized_code = "pancake_swap" +canonical_surface_code = "clmm_pancake_swap" +target_crate = "kb_decoder_clmm_pancake_swap" +current_crate = "kb_decoder_clmm_pancake_swap" +function_code = "clmm" +family_code = "pancake" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK" +source_code = "raydium_clmm" +normalized_code = "raydium_clmm" +canonical_surface_code = "clmm_raydium" +target_crate = "kb_decoder_clmm_raydium" +current_crate = "kb_decoder_clmm_raydium" +function_code = "clmm" +family_code = "raydium" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "6dMXqGZ3ga2dikrYS9ovDXgHGh5RUsb2RTUj6hrQXhk6" +source_code = "stabble_clmm" +normalized_code = "stabble_clmm" +canonical_surface_code = "clmm_stabble" +target_crate = "kb_decoder_clmm_stabble" +current_crate = "kb_decoder_clmm_stabble" +function_code = "clmm" +family_code = "stabble" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "CPMMoo8L3F4NbTegBCKVNunggL7H1ZpdTHKxQB5qKP1C" +source_code = "raydium_cpmm" +normalized_code = "raydium_cpmm" +canonical_surface_code = "cpmm_raydium" +target_crate = "kb_decoder_cpmm_raydium" +current_crate = "kb_decoder_cpmm_raydium" +function_code = "cpmm" +family_code = "raydium" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "LBUZKhRxPF3XUpBCjp4YzTKgLccjZhTSDM9YuVaPwxo" +source_code = "meteora_dlmm" +normalized_code = "meteora_dlmm" +canonical_surface_code = "dlmm_meteora" +target_crate = "kb_decoder_dlmm_meteora" +current_crate = "kb_decoder_dlmm_meteora" +function_code = "dlmm" +family_code = "meteora" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "WALL8ucBuUyL46QYxwYJjidaFYhdvxUFrgvBxPshERx" +source_code = "metadao_bid_wall" +normalized_code = "metadao_bid_wall" +canonical_surface_code = "governance_metadao_bid_wall" +target_crate = "kb_decoder_governance_metadao_bid_wall" +current_crate = "kb_decoder_governance_metadao_bid_wall" +function_code = "governance" +family_code = "metadao" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "boop8hVGQGqehUK2iVEMEnMrL5RbjywRzHKBmBE7ry4" +source_code = "boop_fun" +normalized_code = "boop_fun" +canonical_surface_code = "launchpad_boop_fun" +target_crate = "kb_decoder_launchpad_boop_fun" +current_crate = "kb_decoder_launchpad_boop_fun" +function_code = "launchpad" +family_code = "boop" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "FfYek5vEz23cMkWsdJwG2oa6EphsvXSHrGpdALN4g6W1" +source_code = "letsbonk_fun" +normalized_code = "letsbonk_fun" +canonical_surface_code = "launchpad_letsbonk_fun" +target_crate = "kb_decoder_launchpad_letsbonk_fun" +current_crate = "" +function_code = "launchpad" +family_code = "letsbonk" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "moontUzsdepotRGe5xsfip7vLPTJnVuafqdUWexVnPM" +source_code = "metadao_ico" +normalized_code = "metadao_ico" +canonical_surface_code = "launchpad_metadao_ico" +target_crate = "kb_decoder_launchpad_metadao_ico" +current_crate = "kb_decoder_launchpad_metadao_ico" +function_code = "launchpad" +family_code = "metadao" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "dbcij3LWUppWqq96dh6gJWwBifmcGfLSB5D4DuSMaqN" +source_code = "meteora_dbc" +normalized_code = "meteora_dbc" +canonical_surface_code = "launchpad_meteora_dbc" +target_crate = "kb_decoder_launchpad_meteora_dbc" +current_crate = "kb_decoder_launchpad_meteora_dbc" +function_code = "launchpad" +family_code = "meteora" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "MoonCVVNZFSYkqNXP6bxHLPL6QQJiMagDL3qcqUQTrG" +source_code = "moonit" +normalized_code = "moonit" +canonical_surface_code = "launchpad_moonit" +target_crate = "kb_decoder_launchpad_moonit" +current_crate = "kb_decoder_launchpad_moonit" +function_code = "launchpad" +family_code = "moonit" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "7rtiKSUDLBm59b1SBmD9oajcP8xE64vAGSMbAN5CXy1q" +source_code = "moonshot_token_authority" +normalized_code = "moonshot_token_authority" +canonical_surface_code = "launchpad_moonshot_token_authority" +target_crate = "kb_decoder_launchpad_moonshot_token_authority" +current_crate = "" +function_code = "launchpad" +family_code = "moonshot" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "waveQX2yP3H1pVU8djGvEHmYg8uamQ84AuyGtpsrXTF" +source_code = "orca_wavebreak" +normalized_code = "orca_wavebreak" +canonical_surface_code = "launchpad_orca_wavebreak" +target_crate = "kb_decoder_launchpad_orca_wavebreak" +current_crate = "kb_decoder_launchpad_orca_wavebreak" +function_code = "launchpad" +family_code = "orca" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "T8HsGYv7sMk3kTnyaRqZrbRPuntYzdh12evXBkprint" +source_code = "printr" +normalized_code = "printr" +canonical_surface_code = "launchpad_printr" +target_crate = "kb_decoder_launchpad_printr" +current_crate = "kb_decoder_launchpad_printr" +function_code = "launchpad" +family_code = "printr" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "6EF8rrecthR5Dkzon8Nwu78hRvfCKubJ14M5uBEwF6P" +source_code = "pump_fun" +normalized_code = "pump_fun" +canonical_surface_code = "launchpad_pump_fun" +target_crate = "kb_decoder_launchpad_pump_fun" +current_crate = "kb_decoder_launchpad_pump_fun" +function_code = "launchpad" +family_code = "pump" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "PdMDrKEMaX8q7CCJb7NvUCxerBCcsFUa4LjBEynTtEd" +source_code = "pumpup_ai" +normalized_code = "pumpup_ai" +canonical_surface_code = "launchpad_pump_pumpup_ai" +target_crate = "kb_decoder_launchpad_pump_pumpup_ai" +current_crate = "kb_decoder_launchpad_pump_pumpup_ai" +function_code = "launchpad" +family_code = "pump" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "LanMV9sAd7wArD4vJFi2qDdfnVhFxYSUg6eADduJ3uj" +source_code = "raydium_launchlab" +normalized_code = "raydium_launchlab" +canonical_surface_code = "launchpad_raydium_launchlab" +target_crate = "kb_decoder_launchpad_raydium_launchlab" +current_crate = "kb_decoder_launchpad_raydium_launchlab" +function_code = "launchpad" +family_code = "raydium" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "C1onEW2kPetmHmwe74YC1ESx3LnFEpVau6g2pg4fHycr" +source_code = "clone" +normalized_code = "clone" +canonical_surface_code = "lending_clone" +target_crate = "kb_decoder_lending_clone" +current_crate = "kb_decoder_lending_clone" +function_code = "lending" +family_code = "clone" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "jupr81YtYssSyPt8jbnGuiWon5f6x9TcDEFxYe3Bdzi" +source_code = "jupiter_lend_borrow" +normalized_code = "jupiter_lend_borrow" +canonical_surface_code = "lending_jupiter_lend_borrow" +target_crate = "kb_decoder_lending_jupiter_lend_borrow" +current_crate = "kb_decoder_lending_jupiter_lend_borrow" +function_code = "lending" +family_code = "jupiter" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "jup3YeL8QhtSx1e253b2FDvsMNC87fDrgQZivbrndc9" +source_code = "jupiter_lend_earn" +normalized_code = "jupiter_lend_earn" +canonical_surface_code = "lending_jupiter_lend_earn" +target_crate = "kb_decoder_lending_jupiter_lend_earn" +current_crate = "kb_decoder_lending_jupiter_lend_earn" +function_code = "lending" +family_code = "jupiter" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "jupgfSgfuAXv4B6R2Uxu85Z1qdzgju79s6MfZekN6XS" +source_code = "jupiter_lend_flash_loan" +normalized_code = "jupiter_lend_flash_loan" +canonical_surface_code = "lending_jupiter_lend_flash_loan" +target_crate = "kb_decoder_lending_jupiter_lend_flash_loan" +current_crate = "kb_decoder_lending_jupiter_lend_flash_loan" +function_code = "lending" +family_code = "jupiter" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "jupeiUmn818Jg1ekPURTpr4mFo29p46vygyykFJ3wZC" +source_code = "jupiter_lend_liquidity" +normalized_code = "jupiter_lend_liquidity" +canonical_surface_code = "lending_jupiter_lend_liquidity" +target_crate = "kb_decoder_lending_jupiter_lend_liquidity" +current_crate = "kb_decoder_lending_jupiter_lend_liquidity" +function_code = "lending" +family_code = "jupiter" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD" +source_code = "kamino_lending" +normalized_code = "kamino_lending" +canonical_surface_code = "lending_kamino" +target_crate = "kb_decoder_lending_kamino" +current_crate = "kb_decoder_lending_kamino" +function_code = "lending" +family_code = "kamino" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "MFLQPPPPjNinkdKoy2odNFBhvpY43XtCDZjBwG2fwn5" +source_code = "marginfi" +normalized_code = "marginfi" +canonical_surface_code = "lending_marginfi" +target_crate = "kb_decoder_lending_marginfi" +current_crate = "" +function_code = "lending" +family_code = "marginfi" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "MFv2hWf31Z9kbCa1snEPYctwafyhdvnV7FZnsebVacA" +source_code = "marginfi_v2" +normalized_code = "marginfi_v2" +canonical_surface_code = "lending_marginfi_v2" +target_crate = "kb_decoder_lending_marginfi_v2" +current_crate = "kb_decoder_lending_marginfi_v2" +function_code = "lending" +family_code = "marginfi" +version_code = "v2" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "SHARKobtfF1bHhxD2eqftjHBdVSCbKo9JtgK71FhELP" +source_code = "sharky_fi" +normalized_code = "sharky_fi" +canonical_surface_code = "lending_sharky_fi" +target_crate = "kb_decoder_lending_sharky_fi" +current_crate = "" +function_code = "lending" +family_code = "sharky" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "So1endDq2YkqhipRh3WViPa8hdiSpxWy6z3Z6tMCpAo" +source_code = "solend_protocol" +normalized_code = "solend_protocol" +canonical_surface_code = "lending_solend_protocol" +target_crate = "kb_decoder_lending_solend_protocol" +current_crate = "" +function_code = "lending" +family_code = "solend" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "LockrWmn6K5twhz3y9w1dQERbmgSaRkfnTeTKbpofwE" +source_code = "raydium_lock_lp" +normalized_code = "raydium_lock_lp" +canonical_surface_code = "lock_raydium_lp" +target_crate = "kb_decoder_lock_raydium_lp" +current_crate = "kb_decoder_lock_raydium_lp" +function_code = "lock" +family_code = "raydium" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s" +source_code = "metaplex_token_metadata" +normalized_code = "metaplex_token_metadata" +canonical_surface_code = "metadata_metaplex_token_metadata" +target_crate = "kb_decoder_metadata_metaplex_token_metadata" +current_crate = "" +function_code = "metadata" +family_code = "metaplex" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "namesLPneVptA9Z5rqUDD9tMTWEJwofgaYwp8cawRkX" +source_code = "name_service" +normalized_code = "name_service" +canonical_surface_code = "metadata_name_service" +target_crate = "kb_decoder_metadata_spl_name_service" +current_crate = "" +function_code = "metadata" +family_code = "name" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "BGUMAp9Gq7iTEuizy4pqaxsTyUCBK68MDfK752saRPUY" +source_code = "bubblegum" +normalized_code = "bubblegum" +canonical_surface_code = "nft_metaplex_bubblegum" +target_crate = "kb_decoder_nft_metaplex_bubblegum" +current_crate = "kb_decoder_nft_metaplex_bubblegum" +function_code = "nft" +family_code = "metaplex" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d" +source_code = "mpl_core" +normalized_code = "mpl_core" +canonical_surface_code = "nft_metaplex_mpl_core" +target_crate = "kb_decoder_nft_metaplex_mpl_core" +current_crate = "" +function_code = "nft" +family_code = "metaplex" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "3ZZuTbwC6aJbvteyVxXUS7gtFYdf7AuXeitx6VyvjvUp" +source_code = "jupiter_prediction_market" +normalized_code = "jupiter_prediction_market" +canonical_surface_code = "oracle_jupiter_prediction_market" +target_crate = "kb_decoder_oracle_jupiter_prediction_market" +current_crate = "" +function_code = "oracle" +family_code = "jupiter" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "jupoNjAxXgZ4rjzxzPMP4oxduvQsQtZzyknqvzYNrNu" +source_code = "jupiter_limit_order" +normalized_code = "jupiter_limit_order" +canonical_surface_code = "orderbook_jupiter_limit_order" +target_crate = "kb_decoder_orderbook_jupiter_limit_order" +current_crate = "kb_decoder_orderbook_jupiter_limit_order" +function_code = "orderbook" +family_code = "jupiter" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "j1o2qRpjcyUwEvwtcfhEQefh773ZgjxcVRry7LDqg5X" +source_code = "jupiter_limit_order_v2" +normalized_code = "jupiter_limit_order_v2" +canonical_surface_code = "orderbook_jupiter_limit_order_v2" +target_crate = "kb_decoder_orderbook_jupiter_limit_order_v2" +current_crate = "kb_decoder_orderbook_jupiter_limit_order_v2" +function_code = "orderbook" +family_code = "jupiter" +version_code = "v2" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "MNFSTqtC93rEfYHB6hF82sKdZpUDFWkViLByLd1k1Ms" +source_code = "manifest" +normalized_code = "manifest" +canonical_surface_code = "orderbook_manifest_v1" +target_crate = "kb_decoder_orderbook_manifest_v1" +current_crate = "" +function_code = "orderbook" +family_code = "manifest" +version_code = "v1" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "opnb2LAfJYbRMAHHvqjCwQxanZn7ReEHp1k81EohpZb" +source_code = "openbook_v2" +normalized_code = "openbook_v2" +canonical_surface_code = "orderbook_openbook_v2" +target_crate = "kb_decoder_orderbook_openbook_v2" +current_crate = "kb_decoder_orderbook_openbook_v2" +function_code = "orderbook" +family_code = "openbook" +version_code = "v2" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "PhoeNiXZ8ByJGLkxNfZRnkUfjvmuYqLR89jjFHGqdXY" +source_code = "phoenix" +normalized_code = "phoenix" +canonical_surface_code = "orderbook_phoenix_v1" +target_crate = "kb_decoder_orderbook_phoenix_v1" +current_crate = "" +function_code = "orderbook" +family_code = "phoenix" +version_code = "v1" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "dRiftyHA39MWEi3m9aunc5MzRF1JYuBsbn6VPcn33UH" +source_code = "drift_v2" +normalized_code = "drift_v2" +canonical_surface_code = "perpetuals_drift_v2" +target_crate = "kb_decoder_perpetuals_drift_v2" +current_crate = "kb_decoder_perpetuals_drift_v2" +function_code = "perpetuals" +family_code = "drift" +version_code = "v2" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "PERPHjGBqRHArX4DySjwM6UJHiR3sWAatqfdBS2qQJu" +source_code = "jupiter_perpetuals" +normalized_code = "jupiter_perpetuals" +canonical_surface_code = "perpetuals_jupiter" +target_crate = "kb_decoder_perpetuals_jupiter" +current_crate = "kb_decoder_perpetuals_jupiter" +function_code = "perpetuals" +family_code = "jupiter" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "ZETAxsqBRek56DhiGXrn75yj2NHU3aYUnxvHXpkf3aD" +source_code = "zeta" +normalized_code = "zeta" +canonical_surface_code = "perpetuals_zeta" +target_crate = "kb_decoder_perpetuals_zeta" +current_crate = "kb_decoder_perpetuals_zeta" +function_code = "perpetuals" +family_code = "zeta" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "zDEXqXEG7gAyxb1Kg9mK5fPnUdENCGKzWrM21RMdWRq" +source_code = "zeta_matching_engine" +normalized_code = "zeta_matching_engine" +canonical_surface_code = "perpetuals_zeta_matching_engine" +target_crate = "kb_decoder_perpetuals_zeta_matching_engine" +current_crate = "" +function_code = "perpetuals" +family_code = "zeta" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "DF1ow4tspfHX9WJsAb9epbkA8hmpSEAtxXy1V27QBH" +source_code = "dflow_agregator_v4" +normalized_code = "dflow_aggregator_v4" +canonical_surface_code = "router_dflow_aggregator_v4" +target_crate = "kb_decoder_router_dflow_aggregator_v4" +current_crate = "kb_decoder_router_dflow_aggregator_v4" +function_code = "router" +family_code = "dflow" +version_code = "v4" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "JUP4Fb2cqiRUcaTHdrPC8h2gNsA2ETXiPDD33WcGuJB" +source_code = "jupiter_agregator_v4" +normalized_code = "jupiter_aggregator_v4" +canonical_surface_code = "router_jupiter_aggregator_v4" +target_crate = "kb_decoder_router_jupiter_aggregator_v4" +current_crate = "kb_decoder_router_jupiter_aggregator_v4" +function_code = "router" +family_code = "jupiter" +version_code = "v4" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "JUP6LkbZbjS1jKKwapdHNy74zcZ3tLUZoi5QNyVTaV4" +source_code = "jupiter_agregator_v6" +normalized_code = "jupiter_aggregator_v6" +canonical_surface_code = "router_jupiter_aggregator_v6" +target_crate = "kb_decoder_router_jupiter_aggregator_v6" +current_crate = "kb_decoder_router_jupiter_aggregator_v6" +function_code = "router" +family_code = "jupiter" +version_code = "v6" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "DCA265Vj8a9CEuX1eb1LWRnDT7uK6q1xMipnNyatn23M" +source_code = "jupiter_dca" +normalized_code = "jupiter_dca" +canonical_surface_code = "router_jupiter_dca" +target_crate = "kb_decoder_router_jupiter_dca" +current_crate = "kb_decoder_router_jupiter_dca" +function_code = "router" +family_code = "jupiter" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "6m2CDdhRgxpH4WjvdzxAYbGxwdGUz5MziiL5jek2kBma" +source_code = "okx_labs_v1" +normalized_code = "okx_labs_v1" +canonical_surface_code = "router_okx_labs_v1" +target_crate = "kb_decoder_router_okx_labs_v1" +current_crate = "kb_decoder_router_okx_labs_v1" +function_code = "router" +family_code = "okx" +version_code = "v1" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "proVF4pMXVaYqmy4NjniPh4pqKNfMmsihgd4wdkCX3u" +source_code = "okx_labs_v2" +normalized_code = "okx_labs_v2" +canonical_surface_code = "router_okx_labs_v2" +target_crate = "kb_decoder_router_okx_labs_v2" +current_crate = "kb_decoder_router_okx_labs_v2" +function_code = "router" +family_code = "okx" +version_code = "v2" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "routeUGWgWzqBWFcrCfv8tritsqukccJPu3q5GPP3xS" +source_code = "raydium_amm_routing" +normalized_code = "raydium_amm_routing" +canonical_surface_code = "router_raydium_amm_routing" +target_crate = "kb_decoder_router_raydium_amm_routing" +current_crate = "" +function_code = "router" +family_code = "raydium" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "stkitrT1Uoy18Dk1fTrgPw8W6MVzoCfYoAFT4MLsmhq" +source_code = "sanctum_router" +normalized_code = "sanctum_router" +canonical_surface_code = "router_sanctum" +target_crate = "kb_decoder_router_sanctum" +current_crate = "" +function_code = "router" +family_code = "sanctum" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "TessVdML9pBGgG9yGks7o4HewRaXVAMuoVj4x83GLQH" +source_code = "tessera_v" +normalized_code = "tessera_v" +canonical_surface_code = "router_tessera_v" +target_crate = "kb_decoder_router_tessera_v" +current_crate = "" +function_code = "router" +family_code = "tessera" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "T1TANpTeScyeqVzzgNViGDNrkQ6qHz9KrSBS4aNXvGT" +source_code = "titan_exchange_router" +normalized_code = "titan_exchange_router" +canonical_surface_code = "router_titan_exchange_router" +target_crate = "kb_decoder_router_titan_exchange_router" +current_crate = "" +function_code = "router" +family_code = "titan" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "XzTT4XB8m7sLD2xi6snefSasaswsKCxx5Tifjondogm" +source_code = "ondo_global_markets" +normalized_code = "ondo_global_markets" +canonical_surface_code = "rwa_ondo_global_markets" +target_crate = "kb_decoder_rwa_ondo_global_markets" +current_crate = "kb_decoder_rwa_ondo_global_markets" +function_code = "rwa" +family_code = "ondo" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "HYEXCHtHkBagdStcJCp3xbbb9B7sdMdWXFNj6mdsG4hn" +source_code = "hylo_exchange" +normalized_code = "hylo_exchange" +canonical_surface_code = "stable_swap_hylo_exchange" +target_crate = "kb_decoder_stable_swap_hylo_exchange" +current_crate = "kb_decoder_stable_swap_hylo_exchange" +function_code = "stable_swap" +family_code = "hylo" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "JUPUSDecMzAVgztLe6eGhwUBj1Pn3j9WAXwmtHmfbRr" +source_code = "jupiter_stable" +normalized_code = "jupiter_stable" +canonical_surface_code = "stable_swap_jupiter_stable" +target_crate = "kb_decoder_stable_swap_jupiter_stable" +current_crate = "kb_decoder_stable_swap_jupiter_stable" +function_code = "stable_swap" +family_code = "jupiter" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "MERLuDFBMmsHnsBPZw2sDQZHvXFMwp8EdjudcU2HKky" +source_code = "mercurial_stable_swap" +normalized_code = "mercurial_stable_swap" +canonical_surface_code = "stable_swap_mercurial" +target_crate = "kb_decoder_stable_swap_mercurial" +current_crate = "" +function_code = "stable_swap" +family_code = "mercurial" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "NUMERUNsFCP3kuNmWZuXtm1AaQCPj9uw6Guv2Ekoi5P" +source_code = "numeraire" +normalized_code = "numeraire" +canonical_surface_code = "stable_swap_numeraire" +target_crate = "kb_decoder_stable_swap_numeraire" +current_crate = "kb_decoder_stable_swap_numeraire" +function_code = "stable_swap" +family_code = "numeraire" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "SSwpkEEcbUqx4vtoEByFjSkhKdCT862DNVb52nZg1UZ" +source_code = "sabre_statble_swap" +normalized_code = "sabre_stable_swap" +canonical_surface_code = "stable_swap_saber" +target_crate = "kb_decoder_stable_swap_saber" +current_crate = "" +function_code = "stable_swap" +family_code = "saber" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "swapNyd8XiQwJ6ianp9snpu4brUqFxadzvHebnAXjJZ" +source_code = "stabble_stable_swap" +normalized_code = "stabble_stable_swap" +canonical_surface_code = "stable_swap_stabble" +target_crate = "kb_decoder_stable_swap_stabble" +current_crate = "kb_decoder_stable_swap_stabble" +function_code = "stable_swap" +family_code = "stabble" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "4R3gSG8BpU4t19KYj8CfnbtRpnT8gtk4dvTHxVRwc2r7" +source_code = "jito_tip_distribution" +normalized_code = "jito_tip_distribution" +canonical_surface_code = "staking_jito_tip_distribution" +target_crate = "kb_decoder_staking_jito_tip_distribution" +current_crate = "" +function_code = "staking" +family_code = "jito" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr" +source_code = "kamino_farm" +normalized_code = "kamino_farm" +canonical_surface_code = "staking_kamino_farm" +target_crate = "kb_decoder_staking_kamino_farm" +current_crate = "kb_decoder_staking_kamino_farm" +function_code = "staking" +family_code = "kamino" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "MarBmsSgKXdrN1egZf5sqe1TMai9K1rChYNDJgjq7aD" +source_code = "marinade_finance" +normalized_code = "marinade_finance" +canonical_surface_code = "staking_marinade_finance" +target_crate = "kb_decoder_staking_marinade_finance" +current_crate = "kb_decoder_staking_marinade_finance" +function_code = "staking" +family_code = "marinade" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "SPMBzsVUuoHA4Jm6KunbsotaahvVikZs1JyTW6iJvbn" +source_code = "sanctum_multi_validator_spl_stake_pool" +normalized_code = "sanctum_multi_validator_spl_stake_pool" +canonical_surface_code = "staking_sanctum_multi_validator_spl_stake_pool" +target_crate = "kb_decoder_staking_sanctum_multi_validator_spl_stake_pool" +current_crate = "" +function_code = "staking" +family_code = "sanctum" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "SP12tWFxD9oJsVWNavTTBZvMbA6gkAmxtVgxdqvyvhY" +source_code = "sanctum_single_validator_spl_stake_pool" +normalized_code = "sanctum_single_validator_spl_stake_pool" +canonical_surface_code = "staking_sanctum_single_validator_spl_stake_pool" +target_crate = "kb_decoder_staking_sanctum_single_validator_spl_stake_pool" +current_crate = "" +function_code = "staking" +family_code = "sanctum" +version_code = "" +registry_status = "reserved_missing" +duplicate_program_id = "" + +[[program]] +program_id = "sSo1iU21jBrU9VaJ8PJib1MtorefUV4fzC9GURa2KNn" +source_code = "solayer" +normalized_code = "solayer" +canonical_surface_code = "staking_solayer" +target_crate = "kb_decoder_staking_solayer" +current_crate = "kb_decoder_staking_solayer" +function_code = "staking" +family_code = "solayer" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "treaf4wWBBty3fHdyBpo35Mz84M8k3heKXmjmi9vFt5" +source_code = "helium_treasury_management" +normalized_code = "helium_treasury_management" +canonical_surface_code = "treasury_helium_treasury_management" +target_crate = "kb_decoder_treasury_helium_treasury_management" +current_crate = "kb_decoder_treasury_helium_treasury_management" +function_code = "treasury" +family_code = "helium" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "AQU1FRd7papthgdrwPTTq5JacJh8YtwEXaBfKU3bTz45" +source_code = "aquifer" +normalized_code = "aquifer" +canonical_surface_code = "unknown_aquifer" +target_crate = "" +current_crate = "" +function_code = "unknown" +family_code = "aquifer" +version_code = "" +registry_status = "needs_classification" +duplicate_program_id = "" + +[[program]] +program_id = "FLASHX8DrLbgeR8FcfNV1F5krxYcYMUdBkrP1EPBtxB9" +source_code = "axiom_trade" +normalized_code = "axiom_trade" +canonical_surface_code = "unknown_axiom_trade" +target_crate = "" +current_crate = "" +function_code = "unknown" +family_code = "axiom" +version_code = "" +registry_status = "needs_classification" +duplicate_program_id = "" + +[[program]] +program_id = "BiSoNHVpsVZW2F7rx2eQ59yQwKxzU5NvBcmKshCSUypi" +source_code = "bison_fi" +normalized_code = "bison_fi" +canonical_surface_code = "unknown_bison_fi" +target_crate = "" +current_crate = "kb_decoder_bisonfi" +function_code = "unknown" +family_code = "bison" +version_code = "" +registry_status = "current_alias_needs_classification" +duplicate_program_id = "" + +[[program]] +program_id = "srAMMzfVHVAtgSJc8iH6CfKzuWuUTzLHVCE81QU1rgi" +source_code = "gavel" +normalized_code = "gavel" +canonical_surface_code = "unknown_gavel" +target_crate = "" +current_crate = "kb_decoder_gavel" +function_code = "unknown" +family_code = "gavel" +version_code = "" +registry_status = "current_alias_needs_classification" +duplicate_program_id = "" + +[[program]] +program_id = "FqGg2Y1FNxMiGd51Q6UETixQWkF5fB92MysbYogRJb3P" +source_code = "hawk_fi" +normalized_code = "hawk_fi" +canonical_surface_code = "unknown_hawk_fi" +target_crate = "" +current_crate = "" +function_code = "unknown" +family_code = "hawk" +version_code = "" +registry_status = "needs_classification" +duplicate_program_id = "" + +[[program]] +program_id = "9H6tua7jkLhdm3w8BvgpTn5LZNU7g4ZynDmCiNN3q6Rp" +source_code = "humidi_fi" +normalized_code = "humidi_fi" +canonical_surface_code = "unknown_humidi_fi" +target_crate = "" +current_crate = "" +function_code = "unknown" +family_code = "humidi" +version_code = "" +registry_status = "needs_classification" +duplicate_program_id = "" + +[[program]] +program_id = "8rE9CtCjwhSmbwL5fbJBtRFsS3ohfMcDFeTTC7t4ciUA" +source_code = "jup_studio_authority" +normalized_code = "jup_studio_authority" +canonical_surface_code = "unknown_jup_studio_authority" +target_crate = "" +current_crate = "" +function_code = "unknown" +family_code = "jupiter" +version_code = "" +registry_status = "needs_classification" +duplicate_program_id = "" + +[[program]] +program_id = "oreV3EG1i9BEgiAJ8b177Z2S2rMarzak4NMv1kULvWv" +source_code = "ore_v3" +normalized_code = "ore_v3" +canonical_surface_code = "unknown_ore_v3" +target_crate = "" +current_crate = "" +function_code = "unknown" +family_code = "ore" +version_code = "v3" +registry_status = "needs_classification" +duplicate_program_id = "" + +[[program]] +program_id = "PSwapMdSai8tjrEXcxFeQth87xC4rRsa4VA5mhGhXkP" +source_code = "pinguin_finance" +normalized_code = "penguin_finance" +canonical_surface_code = "unknown_penguin_finance" +target_crate = "" +current_crate = "" +function_code = "unknown" +family_code = "penguin" +version_code = "" +registry_status = "needs_classification" +duplicate_program_id = "" + +[[program]] +program_id = "5ocnV1qiCgaQR8Jb8xWnVbApfaygJ8tNoZfgPwsgx9kx" +source_code = "sanctum_s_controller" +normalized_code = "sanctum_s_controller" +canonical_surface_code = "unknown_sanctum_s_controller" +target_crate = "" +current_crate = "" +function_code = "unknown" +family_code = "sanctum" +version_code = "" +registry_status = "needs_classification" +duplicate_program_id = "" + +[[program]] +program_id = "SCoRcH8c2dpjvcJD6FiPbCSQyQgu3PcUAWj2Xxx3mqn" +source_code = "scorch" +normalized_code = "scorch" +canonical_surface_code = "unknown_scorch" +target_crate = "" +current_crate = "kb_decoder_scorch" +function_code = "unknown" +family_code = "scorch" +version_code = "" +registry_status = "current_alias_needs_classification" +duplicate_program_id = "" + +[[program]] +program_id = "swigypWHEksbC64pWKwah1WTeh9JXwx8H1rJHLdbQMB" +source_code = "swig" +normalized_code = "swig" +canonical_surface_code = "unknown_swig" +target_crate = "" +current_crate = "" +function_code = "unknown" +family_code = "swig" +version_code = "" +registry_status = "needs_classification" +duplicate_program_id = "" + +[[program]] +program_id = "CarrotwivhMpDnm27EHmRLeQ683Z1PufuqEmBZvD282s" +source_code = "carrot_defi" +normalized_code = "carrot_defi" +canonical_surface_code = "vault_carrot_defi" +target_crate = "kb_decoder_vault_carrot_defi" +current_crate = "kb_decoder_vault_carrot_defi" +function_code = "vault" +family_code = "carrot" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "HysTabVUfmQBFcmzu1ctRd1Y1fxd66RBpboy1bmtDSQQ" +source_code = "hylo_stability_pool" +normalized_code = "hylo_stability_pool" +canonical_surface_code = "vault_hylo_stability_pool" +target_crate = "kb_decoder_vault_hylo_stability_pool" +current_crate = "kb_decoder_vault_hylo_stability_pool" +function_code = "vault" +family_code = "hylo" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "kvauTFR8qm1dhniz6pYuBZkuene3Hfrs1VQhVRgCNrr" +source_code = "kamino_vault" +normalized_code = "kamino_vault" +canonical_surface_code = "vault_kamino" +target_crate = "kb_decoder_vault_kamino" +current_crate = "kb_decoder_vault_kamino" +function_code = "vault" +family_code = "kamino" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd" +source_code = "kamino_vault_v2" +normalized_code = "kamino_vault_v2" +canonical_surface_code = "vault_kamino_v2" +target_crate = "kb_decoder_vault_kamino_v2" +current_crate = "kb_decoder_vault_kamino_v2" +function_code = "vault" +family_code = "kamino" +version_code = "v2" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "6LtLpnUFNByNXLyCoK9wA2MykKAmQNZKBdY8s47dehDc" +source_code = "kamino" +normalized_code = "kamino" +canonical_surface_code = "vault_kamino_yvaults" +target_crate = "kb_decoder_vault_kamino_yvaults" +current_crate = "kb_decoder_vault_kamino_yvaults" +function_code = "vault" +family_code = "kamino" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "24Uqj9JCLxUeoC3hGfh5W3s9FM9uCHDS2SG3LYwBpyTi" +source_code = "meteora_vault" +normalized_code = "meteora_vault" +canonical_surface_code = "vault_meteora" +target_crate = "kb_decoder_vault_meteora" +current_crate = "kb_decoder_vault_meteora" +function_code = "vault" +family_code = "meteora" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "strmRqUCoQUgGUan5YhzUZa6KqdzwX5L6FpUxfmKg5m" +source_code = "streamflow" +normalized_code = "streamflow" +canonical_surface_code = "vesting_streamflow" +target_crate = "kb_decoder_vesting_streamflow" +current_crate = "kb_decoder_vesting_streamflow" +function_code = "vesting" +family_code = "streamflow" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "JSW99DKmxNyREQM14SQLDykeBvEUG63TeohrvmofEiw" +source_code = "jupiter_apepro_smart_wallet" +normalized_code = "jupiter_apepro_smart_wallet" +canonical_surface_code = "wallet_jupiter_apepro_smart_wallet" +target_crate = "kb_decoder_wallet_jupiter_apepro_smart_wallet" +current_crate = "kb_decoder_wallet_jupiter_apepro_smart_wallet" +function_code = "wallet" +family_code = "jupiter" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" + +[[program]] +program_id = "swapFpHZwjELNnjvThjajtiVmkz3yPQEHjLtka2fwHW" +source_code = "stabble_weighted_swap" +normalized_code = "stabble_weighted_swap" +canonical_surface_code = "weighted_swap_stabble" +target_crate = "kb_decoder_weighted_swap_stabble" +current_crate = "kb_decoder_weighted_swap_stabble" +function_code = "weighted_swap" +family_code = "stabble" +version_code = "" +registry_status = "canonical_current" +duplicate_program_id = "" diff --git a/migration/khadhroony-bot2-reference/rustfmt.toml b/migration/khadhroony-bot2-reference/rustfmt.toml new file mode 100644 index 0000000..9c55290 --- /dev/null +++ b/migration/khadhroony-bot2-reference/rustfmt.toml @@ -0,0 +1,23 @@ +# file: rustfmt.toml +# version: 1 + +edition = "2024" +newline_style = "Unix" +use_small_heuristics = "Default" +hard_tabs = false +tab_spaces = 4 +max_width = 100 +chain_width = 80 +fn_call_width = 80 +attr_fn_like_width = 80 +struct_lit_width = 40 +struct_variant_width = 40 +array_width = 80 +single_line_if_else_max_width = 80 +single_line_let_else_max_width = 80 +reorder_imports = true +reorder_modules = true +match_block_trailing_comma = true +use_field_init_shorthand = true +use_try_shorthand = false +force_explicit_abi = true diff --git a/migration/khadhroony-bot2-reference/scripts/audit_khadhroony_workspace_rules.py b/migration/khadhroony-bot2-reference/scripts/audit_khadhroony_workspace_rules.py new file mode 100755 index 0000000..e2fa228 --- /dev/null +++ b/migration/khadhroony-bot2-reference/scripts/audit_khadhroony_workspace_rules.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +# file: scripts/audit_khadhroony_workspace_rules.py +# version: 1 + +"""Audit mechanically verifiable rules specific to khadhroony-bot2.""" + +from __future__ import annotations + +import argparse +import dataclasses +import pathlib +import re +import sys +import tomllib + + +@dataclasses.dataclass(frozen=True) +class Violation: + """One project-specific workspace rule violation.""" + + code: str + path: str + line: int + message: str + + +def package_name(crate_dir: pathlib.Path) -> str | None: + """Return a crate package name.""" + + cargo = crate_dir / "Cargo.toml" + if not cargo.exists(): + return None + value = tomllib.loads(cargo.read_text(encoding="utf-8")).get("package", {}).get("name") + return value if isinstance(value, str) else None + + +def relevant_tracing_crate(crate_dir: pathlib.Path) -> bool: + """Return whether the crate owns a tracing target contract.""" + + cargo = crate_dir / "Cargo.toml" + data = tomllib.loads(cargo.read_text(encoding="utf-8")) + if "tracing" in data.get("dependencies", {}): + return True + return any("TRACING_TARGET" in path.read_text(encoding="utf-8") for path in crate_dir.glob("src/**/*.rs")) + + +def audit_tracing(root: pathlib.Path) -> list[Violation]: + """Audit the project tracing target contract.""" + + violations: list[Violation] = [] + for cargo in sorted(root.glob("*/Cargo.toml")): + crate = cargo.parent + if not relevant_tracing_crate(crate): + continue + name = package_name(crate) + if name is None: + continue + constants = crate / "src/constants.rs" + expected = f'pub(crate) const TRACING_TARGET: &str = "{name}";' + relative_constants = constants.relative_to(root).as_posix() + if not constants.exists() or expected not in constants.read_text(encoding="utf-8"): + violations.append(Violation("KH_TRACE001", relative_constants, 1, f"expected `{expected}`")) + crate_root = crate / "src/lib.rs" + if not crate_root.exists(): + crate_root = crate / "src/main.rs" + relative_root = crate_root.relative_to(root).as_posix() + if not crate_root.exists() or "pub(crate) use crate::constants::TRACING_TARGET;" not in crate_root.read_text(encoding="utf-8"): + violations.append(Violation("KH_TRACE002", relative_root, 1, "missing crate-root TRACING_TARGET re-export")) + for path in crate.glob("src/**/*.rs"): + relative = path.relative_to(root).as_posix() + for index, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1): + if "khbot." in line: + violations.append(Violation("KH_TRACE003", relative, index, "legacy khbot tracing target is forbidden")) + if "crate::constants::TRACING_TARGET" in line and path.name not in {"lib.rs", "main.rs"}: + violations.append(Violation("KH_TRACE004", relative, index, "use crate::TRACING_TARGET outside crate root")) + if re.search(r"(?:crate::[A-Za-z0-9_]+::|(? list[Violation]: + """Reject non-canonical Solana address types in workspace Rust code.""" + + violations: list[Violation] = [] + for path in sorted(root.rglob("*.rs")): + if "target" in path.parts or ".git" in path.parts: + continue + relative = path.relative_to(root).as_posix() + for index, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1): + if "solana_address::Address" in line: + violations.append(Violation("KH_SOL001", relative, index, "use solana_pubkey::Pubkey instead of solana_address::Address")) + return violations + + +def main() -> int: + """Run the khadhroony-specific audit.""" + + parser = argparse.ArgumentParser() + parser.add_argument("--root", default=".") + parser.add_argument("--report-only", action="store_true") + arguments = parser.parse_args() + root = pathlib.Path(arguments.root).resolve() + violations = audit_tracing(root) + audit_solana_types(root) + violations.sort(key=lambda item: (item.code, item.path, item.line, item.message)) + if not violations: + sys.stdout.write("Khadhroony workspace rule audit: clean\n") + return 0 + sys.stdout.write(f"Khadhroony workspace rule audit: {len(violations)} violation(s)\n") + for item in violations: + sys.stdout.write(f"{item.code} {item.path}:{item.line}: {item.message}\n") + return 0 if arguments.report_only else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/migration/khadhroony-bot2-reference/scripts/audit_rust_export_completeness.py b/migration/khadhroony-bot2-reference/scripts/audit_rust_export_completeness.py new file mode 100755 index 0000000..d877e76 --- /dev/null +++ b/migration/khadhroony-bot2-reference/scripts/audit_rust_export_completeness.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python3 +# file: scripts/audit_rust_export_completeness.py +# version: 2 + +"""Report missing crate-root exports and replaceable long internal paths. + +The audit distinguishes declarations from their crate-root aliases. Crate-root +files are excluded from long-path findings because re-export declarations must +name their source module. Findings remain advisory until the affected crate is +compiled. +""" + +from __future__ import annotations + +import argparse +import dataclasses +import pathlib +import re +import sys + + +@dataclasses.dataclass(frozen=True) +class Candidate: + """One export-completeness candidate.""" + + code: str + path: str + line: int + message: str + + +@dataclasses.dataclass(frozen=True) +class Declaration: + """One module-level public or crate-public declaration.""" + + module: str + name: str + visibility: str + path: pathlib.Path + line: int + + +def crate_roots(root: pathlib.Path) -> list[pathlib.Path]: + """Return workspace crate directories that contain a Rust crate root.""" + + crates: list[pathlib.Path] = [] + for manifest in root.glob("*/Cargo.toml"): + crate = manifest.parent + if (crate / "src/lib.rs").is_file() or (crate / "src/main.rs").is_file(): + crates.append(crate) + return sorted(crates) + + +def module_path(crate: pathlib.Path, path: pathlib.Path) -> str: + """Return the Rust module path represented by one source file.""" + + relative = path.relative_to(crate / "src").with_suffix("") + return "::".join(relative.parts) + + +def declaration_candidates(crate: pathlib.Path, path: pathlib.Path) -> list[Declaration]: + """Return module-level public declarations from one Rust module.""" + + text = path.read_text(encoding="utf-8") + depth = 0 + found: list[Declaration] = [] + pattern = re.compile( + r"^\s*(pub(?:\(crate\))?)\s+(?:(?:async|unsafe|const)\s+)*(?:const|static|type|struct|enum|trait|union|fn)\s+([A-Za-z_][A-Za-z0-9_]*)" + ) + for number, line in enumerate(text.splitlines(), 1): + if depth == 0: + match = pattern.match(line) + if match is not None: + found.append( + Declaration( + module_path(crate, path), + match.group(2), + match.group(1), + path, + number, + ) + ) + depth += line.count("{") - line.count("}") + if depth < 0: + depth = 0 + return found + + +def root_exports(crate_root: pathlib.Path) -> dict[tuple[str, str], str]: + """Return source module/name pairs and their crate-root aliases.""" + + exports: dict[tuple[str, str], str] = {} + pattern = re.compile( + r"^\s*pub(?:\(crate\))?\s+use\s+crate::([A-Za-z_][A-Za-z0-9_:]*)::([A-Za-z_][A-Za-z0-9_]*)(?:\s+as\s+([A-Za-z_][A-Za-z0-9_]*))?\s*;", + re.MULTILINE, + ) + for match in pattern.finditer(crate_root.read_text(encoding="utf-8")): + exports[(match.group(1), match.group(2))] = match.group(3) or match.group(2) + return exports + + +def audit_crate(workspace: pathlib.Path, crate: pathlib.Path) -> list[Candidate]: + """Audit one crate and return advisory candidates.""" + + crate_root = crate / "src/lib.rs" + if not crate_root.is_file(): + crate_root = crate / "src/main.rs" + exports = root_exports(crate_root) + candidates: list[Candidate] = [] + declarations: dict[tuple[str, str], Declaration] = {} + for path in sorted((crate / "src").rglob("*.rs")): + if path == crate_root: + continue + for declaration in declaration_candidates(crate, path): + key = (declaration.module, declaration.name) + declarations[key] = declaration + if key not in exports: + relative = path.relative_to(workspace).as_posix() + candidates.append( + Candidate( + "EXPORT001", + relative, + declaration.line, + f"{declaration.visibility} `{declaration.name}` has no crate-root re-export", + ) + ) + long_path = re.compile( + r"\bcrate::([A-Za-z_][A-Za-z0-9_:]*)::([A-Za-z_][A-Za-z0-9_]*)" + ) + for path in sorted((crate / "src").rglob("*.rs")): + if path == crate_root: + continue + for number, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1): + for match in long_path.finditer(line): + key = (match.group(1), match.group(2)) + alias = exports.get(key) + if alias is None: + continue + relative = path.relative_to(workspace).as_posix() + candidates.append( + Candidate( + "EXPORT002", + relative, + number, + f"long internal path `crate::{match.group(1)}::{match.group(2)}` must use `crate::{alias}`", + ) + ) + return candidates + + +def main() -> int: + """Run the advisory export-completeness audit.""" + + parser = argparse.ArgumentParser() + parser.add_argument("--root", default=".") + parser.add_argument("--summary-only", action="store_true") + arguments = parser.parse_args() + workspace = pathlib.Path(arguments.root).resolve() + candidates = [item for crate in crate_roots(workspace) for item in audit_crate(workspace, crate)] + candidates.sort(key=lambda item: (item.code, item.path, item.line, item.message)) + counts: dict[str, int] = {} + for item in candidates: + counts[item.code] = counts.get(item.code, 0) + 1 + sys.stdout.write(f"Rust export completeness audit: {len(candidates)} candidate(s)\n") + for code in sorted(counts): + sys.stdout.write(f"{code}: {counts[code]}\n") + if not arguments.summary_only: + for item in candidates: + sys.stdout.write(f"{item.code} {item.path}:{item.line}: {item.message}\n") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/migration/khadhroony-bot2-reference/scripts/audit_rust_general_rules.py b/migration/khadhroony-bot2-reference/scripts/audit_rust_general_rules.py new file mode 100755 index 0000000..c268e98 --- /dev/null +++ b/migration/khadhroony-bot2-reference/scripts/audit_rust_general_rules.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python3 +# file: scripts/audit_rust_general_rules.py +# version: 2 + +"""Audit mechanically verifiable Rust rules shared by all Rust projects.""" + +from __future__ import annotations + +import argparse +import dataclasses +import pathlib +import re +import sys + + +@dataclasses.dataclass(frozen=True) +class Violation: + """One mechanically detected general Rust rule violation.""" + + code: str + path: str + line: int + message: str + + +def rust_files(root: pathlib.Path) -> list[pathlib.Path]: + """Return Rust sources outside generated build directories.""" + + return sorted( + path + for path in root.rglob("*.rs") + if "target" not in path.parts + and ".git" not in path.parts + and "mnt" not in path.relative_to(root).parts + and not any(part.startswith("pre035_") for part in path.relative_to(root).parts) + ) + + +def audit_file(root: pathlib.Path, path: pathlib.Path) -> list[Violation]: + """Audit one Rust source against general rules.""" + + relative = path.relative_to(root).as_posix() + text = path.read_text(encoding="utf-8") + lines = text.splitlines() + violations: list[Violation] = [] + expected_header = f"// file: {relative}" + if not lines or lines[0] != expected_header: + violations.append(Violation("RUST001", relative, 1, f"expected `{expected_header}`")) + if len(lines) < 2 or re.fullmatch(r"// version: [1-9][0-9]*", lines[1]) is None: + violations.append(Violation("RUST002", relative, 2, "missing positive file version")) + if not text.endswith("\n") or text.endswith("\n\n"): + violations.append(Violation("RUST003", relative, max(len(lines), 1), "file must end with exactly one newline")) + for index, line in enumerate(lines, 1): + stripped = line.strip() + if re.match(r"^(?:pub(?:\(crate\))?\s+)?use\s+", stripped) is None: + continue + is_export = stripped.startswith("pub use ") or stripped.startswith("pub(crate) use ") + justified_trait = "rust-rules: trait-import" in stripped + justified_derive = "rust-rules: derive-import" in stripped + if not is_export and not justified_trait and not justified_derive: + violations.append(Violation("RUST010", relative, index, "ordinary use requires a trait or derive import justification")) + if "::*" in stripped: + violations.append(Violation("RUST012", relative, index, "glob imports are forbidden")) + if "{" in stripped or "}" in stripped: + code = "RUST011" if is_export else "RUST013" + violations.append(Violation(code, relative, index, "grouped use declarations are forbidden")) + if path.name in {"lib.rs", "main.rs"}: + for attribute in ("#![warn(missing_docs)]", "#![deny(unreachable_pub)]", "#![forbid(unsafe_code)]"): + if attribute not in lines[:20]: + violations.append(Violation("RUST020", relative, 1, f"missing `{attribute}`")) + for index, line in enumerate(lines, 1): + if not (line.startswith("pub use ") or line.startswith("pub(crate) use ")): + continue + previous = lines[index - 2].strip() if index >= 2 else "" + if not previous.startswith("///"): + violations.append(Violation("RUST021", relative, index, "crate-root re-export requires adjacent rustdoc")) + return violations + + +def main() -> int: + """Run the general Rust audit.""" + + parser = argparse.ArgumentParser() + parser.add_argument("--root", default=".") + parser.add_argument("--report-only", action="store_true") + arguments = parser.parse_args() + root = pathlib.Path(arguments.root).resolve() + violations = [item for path in rust_files(root) for item in audit_file(root, path)] + violations.sort(key=lambda item: (item.code, item.path, item.line, item.message)) + if not violations: + sys.stdout.write("General Rust rule audit: clean\n") + return 0 + sys.stdout.write(f"General Rust rule audit: {len(violations)} violation(s)\n") + for item in violations: + sys.stdout.write(f"{item.code} {item.path}:{item.line}: {item.message}\n") + return 0 if arguments.report_only else 1 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/migration/khadhroony-bot2-reference/scripts/audit_rust_workspace_rules.py b/migration/khadhroony-bot2-reference/scripts/audit_rust_workspace_rules.py new file mode 100755 index 0000000..dcb6f85 --- /dev/null +++ b/migration/khadhroony-bot2-reference/scripts/audit_rust_workspace_rules.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 +# file: scripts/audit_rust_workspace_rules.py +# version: 2 + +"""Run both the general Rust and khadhroony-specific workspace audits.""" + +from __future__ import annotations + +import argparse +import pathlib +import subprocess + + +def main() -> int: + """Run both independent audit scripts.""" + + parser = argparse.ArgumentParser() + parser.add_argument("--root", default=".") + parser.add_argument("--report-only", action="store_true") + arguments = parser.parse_args() + script_dir = pathlib.Path(__file__).resolve().parent + commands = [ + ["python3", str(script_dir / "audit_rust_general_rules.py"), "--root", arguments.root], + ["python3", str(script_dir / "audit_khadhroony_workspace_rules.py"), "--root", arguments.root], + ] + if arguments.report_only: + for command in commands: + command.append("--report-only") + return max(subprocess.run(command, check=False).returncode for command in commands) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/migration/khadhroony-bot2-reference/scripts/devnet/load_elgamal_registry_fixture.sh b/migration/khadhroony-bot2-reference/scripts/devnet/load_elgamal_registry_fixture.sh new file mode 100755 index 0000000..86891a3 --- /dev/null +++ b/migration/khadhroony-bot2-reference/scripts/devnet/load_elgamal_registry_fixture.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# file: scripts/devnet/load_elgamal_registry_fixture.sh +# version: 1 +set -euo pipefail + +PROFILE="${KB_ELGAMAL_PROFILE:-local_devnet}" +FIXTURE_DIR="${KB_ELGAMAL_FIXTURE_DIR:-./wallets/temporary/${PROFILE}/elgamal_registry_validation}" +FIXTURE_FILE="${FIXTURE_DIR}/fixture.env" + +if [[ ! -r "$FIXTURE_FILE" ]]; then + printf 'Fixture ElGamal Registry absente: %s\n' "$FIXTURE_FILE" >&2 + return 1 2>/dev/null || exit 1 +fi + +# shellcheck disable=SC1090 +source "$FIXTURE_FILE" +required_variables=( + KB_ELGAMAL_CLUSTER_URL + TOKEN_2022_PROGRAM + ELGAMAL_REGISTRY_PROGRAM + ZK_ELGAMAL_PROOF_PROGRAM + ELGAMAL_REGISTRY_ADDRESS + ELGAMAL_PUBKEY_BASE64 + PUBKEY_VALIDITY_PROOF_CONTEXT + TOKEN_2022_CONFIDENTIAL_ACCOUNT +) +for variable in "${required_variables[@]}"; do + if [[ -z "${!variable:-}" ]]; then + printf 'Fixture incomplète, variable absente: %s\n' "$variable" >&2 + return 1 2>/dev/null || exit 1 + fi +done + +printf 'Fixture: %s\n' "$FIXTURE_FILE" +printf 'Cluster: %s\n' "$KB_ELGAMAL_CLUSTER_URL" +printf 'Registry program: %s\n' "$ELGAMAL_REGISTRY_PROGRAM" +printf 'Registry PDA: %s\n' "$ELGAMAL_REGISTRY_ADDRESS" +printf 'Proof context: %s\n' "$PUBKEY_VALIDITY_PROOF_CONTEXT" +printf 'Token-2022 confidential account: %s\n' "$TOKEN_2022_CONFIDENTIAL_ACCOUNT" diff --git a/migration/khadhroony-bot2-reference/scripts/devnet/load_spl_fixture.sh b/migration/khadhroony-bot2-reference/scripts/devnet/load_spl_fixture.sh new file mode 100755 index 0000000..813ba39 --- /dev/null +++ b/migration/khadhroony-bot2-reference/scripts/devnet/load_spl_fixture.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +# file: scripts/devnet/load_spl_fixture.sh +# version: 2 +family="${1:-token-2022}" +workspace="${KB_WORKSPACE_ROOT:-$(pwd)}" +wallet_dir="${KB_DEVNET_WALLET_DIR:-$workspace/wallets/temporary/local_devnet}" +case "$family" in + token-2022) fixture="$wallet_dir/spl_token_2022_validation/fixture.env"; prefix='TOKEN_2022' ;; + classic) fixture="$wallet_dir/spl_token_classic_validation/fixture.env"; prefix='TOKEN_CLASSIC' ;; + *) printf 'Usage: source %s token-2022|classic\n' "${BASH_SOURCE[0]}" >&2; return 64 2>/dev/null || exit 64 ;; +esac +test -f "$fixture" +# shellcheck disable=SC1090 +source "$fixture" +program_var="${prefix}_PROGRAM"; mint_var="${prefix}_MINT"; source_var="${prefix}_SOURCE"; destination_var="${prefix}_DESTINATION"; delegate_var="${prefix}_DELEGATE"; close_var="${prefix}_CLOSE_ACCOUNT"; ata_var="${prefix}_ATA" +program="${!program_var}"; mint="${!mint_var}"; source_account="${!source_var}"; destination="${!destination_var}"; delegate="${!delegate_var}"; close_account="${!close_var}"; ata="${!ata_var}" +for value in "$program" "$mint" "$source_account" "$destination" "$delegate" "$close_account" "$ata"; do + [[ "$value" =~ ^[1-9A-HJ-NP-Za-km-z]{32,44}$ ]] || { printf 'Adresse Base58 invalide: %s\n' "$value" >&2; return 65 2>/dev/null || exit 65; } +done +printf 'Fixture: %s\nWallet: %s\nProgram: %s\nMint: %s\nSource: %s\nDestination: %s\nDelegate: %s\nClose account: %s\nATA: %s\n' "$fixture" "$KB_DEVNET_WALLET_ADDRESS" "$program" "$mint" "$source_account" "$destination" "$delegate" "$close_account" "$ata" +spl-token --url devnet --program-id "$program" display "$mint" | sed -n '1,8p' +spl-token --url devnet --program-id "$program" display "$source_account" | sed -n '1,12p' +spl-token --url devnet --program-id "$program" display "$destination" | sed -n '1,12p' +spl-token --url devnet --program-id "$program" display "$close_account" | sed -n '1,12p' diff --git a/migration/khadhroony-bot2-reference/scripts/devnet/prepare_elgamal_registry_fixture.sh b/migration/khadhroony-bot2-reference/scripts/devnet/prepare_elgamal_registry_fixture.sh new file mode 100755 index 0000000..ae188e1 --- /dev/null +++ b/migration/khadhroony-bot2-reference/scripts/devnet/prepare_elgamal_registry_fixture.sh @@ -0,0 +1,120 @@ +#!/usr/bin/env bash +# file: scripts/devnet/prepare_elgamal_registry_fixture.sh +# version: 1 +set -euo pipefail + +CLUSTER_URL="${KB_ELGAMAL_CLUSTER_URL:-https://api.devnet.solana.com}" +PROFILE="${KB_ELGAMAL_PROFILE:-local_devnet}" +FIXTURE_DIR="${KB_ELGAMAL_FIXTURE_DIR:-./wallets/temporary/${PROFILE}/elgamal_registry_validation}" +FIXTURE_FILE="${FIXTURE_DIR}/fixture.env" +TOKEN_2022_PROGRAM="${TOKEN_2022_PROGRAM:-TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb}" +ELGAMAL_REGISTRY_PROGRAM="${ELGAMAL_REGISTRY_PROGRAM:-regVYJW7tcT8zipN5YiBvHsvR5jXW1uLFxaHSbugABg}" +ZK_ELGAMAL_PROOF_PROGRAM="${ZK_ELGAMAL_PROOF_PROGRAM:-ZkE1Gama1Proof11111111111111111111111111111}" + +usage() { + cat < \\ + ELGAMAL_PUBKEY_BASE64= \\ + PUBKEY_VALIDITY_PROOF_CONTEXT= \\ + TOKEN_2022_CONFIDENTIAL_ACCOUNT= \\ + scripts/devnet/prepare_elgamal_registry_fixture.sh [--force] + +The script validates and records an already generated cryptographic fixture. +It never fabricates an ElGamal key or a zero-knowledge proof. +USAGE +} + +FORCE=false +if [[ "${1:-}" == "--force" ]]; then + FORCE=true +elif [[ $# -ne 0 ]]; then + usage + exit 2 +fi + +for command in solana base64; do + if ! command -v "$command" >/dev/null 2>&1; then + printf 'Commande requise absente: %s\n' "$command" >&2 + exit 1 + fi +done + +required_variables=( + ELGAMAL_REGISTRY_ADDRESS + ELGAMAL_PUBKEY_BASE64 + PUBKEY_VALIDITY_PROOF_CONTEXT + TOKEN_2022_CONFIDENTIAL_ACCOUNT +) +for variable in "${required_variables[@]}"; do + if [[ -z "${!variable:-}" ]]; then + printf 'Variable requise absente: %s\n' "$variable" >&2 + usage + exit 1 + fi +done + +validate_existing_account() { + local label="$1" + local value="$2" + if ! solana account "$value" --url "$CLUSTER_URL" --output json-compact >/dev/null 2>&1; then + printf '%s non lisible sur %s: %s\n' "$label" "$CLUSTER_URL" "$value" >&2 + exit 1 + fi +} + +validate_registry_address() { + local output + if output="$(solana account "$ELGAMAL_REGISTRY_ADDRESS" --url "$CLUSTER_URL" --output json-compact 2>&1)"; then + printf 'Registry déjà présent; la fixture conviendra à UpdateRegistry mais CreateRegistry doit utiliser un nouveau propriétaire/PDA.\n' >&2 + return 0 + fi + if [[ "$output" == *"AccountNotFound"* ]]; then + return 0 + fi + printf 'Adresse Registry invalide ou RPC indisponible: %s\n%s\n' "$ELGAMAL_REGISTRY_ADDRESS" "$output" >&2 + exit 1 +} + +if ! decoded_length="$(printf '%s' "$ELGAMAL_PUBKEY_BASE64" | base64 --decode 2>/dev/null | wc -c)"; then + printf 'ELGAMAL_PUBKEY_BASE64 n\x27est pas un Base64 valide.\n' >&2 + exit 1 +fi +if [[ "$decoded_length" -ne 32 ]]; then + printf 'ELGAMAL_PUBKEY_BASE64 doit décoder exactement 32 octets; obtenu: %s.\n' "$decoded_length" >&2 + exit 1 +fi + +validate_registry_address +validate_existing_account "Proof Context State PubkeyValidity" "$PUBKEY_VALIDITY_PROOF_CONTEXT" +validate_existing_account "Token-2022 confidential account" "$TOKEN_2022_CONFIDENTIAL_ACCOUNT" + +for program in "$TOKEN_2022_PROGRAM" "$ELGAMAL_REGISTRY_PROGRAM" "$ZK_ELGAMAL_PROOF_PROGRAM"; do + if ! solana program show "$program" --url "$CLUSTER_URL" >/dev/null 2>&1; then + printf 'Programme requis absent ou non exécutable sur %s: %s\n' "$CLUSTER_URL" "$program" >&2 + exit 1 + fi +done + +if [[ -e "$FIXTURE_FILE" && "$FORCE" != true ]]; then + printf 'Fixture déjà présente: %s; utiliser --force pour la remplacer.\n' "$FIXTURE_FILE" >&2 + exit 1 +fi + +mkdir -p "$FIXTURE_DIR" +umask 077 +cat > "$FIXTURE_FILE" <&2; exit 64 ;; +esac +command -v solana-keygen >/dev/null +command -v solana >/dev/null +command -v spl-token >/dev/null +test -f "$wallet_file" +mkdir -p "$fixture_dir" +chmod 700 "$fixture_dir" +wallet_address="$(solana-keygen pubkey "$wallet_file")" +solana config set --url devnet >/dev/null +balance="$(solana balance "$wallet_address" --url devnet)" +fixture="$fixture_dir/fixture.env" +if [[ -f "$fixture" && "$force" != "--force" ]]; then + required_prefix='TOKEN_2022' + [[ "$family" == classic ]] && required_prefix='TOKEN_CLASSIC' + # shellcheck disable=SC1090 + source "$fixture" + complete=true + for suffix in PROGRAM MINT SOURCE DESTINATION DELEGATE CLOSE_ACCOUNT ATA AUTHORITY DECIMALS; do + variable="${required_prefix}_${suffix}" + [[ -n "${!variable:-}" ]] || complete=false + done + if [[ "$family" == token-2022 ]]; then + variable="${required_prefix}_FREEZE_AUTHORITY" + [[ -n "${!variable:-}" ]] || complete=false + fi + if [[ "$complete" == true ]]; then + printf 'Fixture existante conservée: %s\n' "$fixture" + printf 'Wallet: %s (%s)\n' "$wallet_address" "$balance" + exit 0 + fi + printf 'Fixture incomplète: %s\nRelancer avec: %s %s --force\n' "$fixture" "$0" "$family" >&2 + exit 66 +fi +if [[ "$family" == token-2022 ]]; then + if ! spl-token create-token --help 2>&1 | grep -q -- '--enable-freeze'; then + printf 'spl-token ne publie pas --enable-freeze; impossible de préparer Freeze/Thaw proprement. +' >&2 + exit 69 + fi + create_output="$(spl-token --url devnet --program-id "$program" create-token --decimals 9 --enable-freeze)" +else + create_output="$(spl-token --url devnet --program-id "$program" create-token --decimals 9)" +fi +mint="$(printf '%s\n' "$create_output" | sed -nE 's/^Creating token ([1-9A-HJ-NP-Za-km-z]+).*$/\1/p' | head -n 1)" +test -n "$mint" +source_keypair="$fixture_dir/source-token-account.json" +destination_keypair="$fixture_dir/destination-token-account.json" +delegate_keypair="$fixture_dir/delegate.json" +close_keypair="$fixture_dir/close-token-account.json" +for keypair in "$source_keypair" "$destination_keypair" "$delegate_keypair" "$close_keypair"; do + solana-keygen new --no-bip39-passphrase --silent --force --outfile "$keypair" >/dev/null + chmod 600 "$keypair" +done +source_address="$(solana-keygen pubkey "$source_keypair")" +destination_address="$(solana-keygen pubkey "$destination_keypair")" +delegate_address="$(solana-keygen pubkey "$delegate_keypair")" +close_address="$(solana-keygen pubkey "$close_keypair")" +spl-token --url devnet --program-id "$program" --fee-payer "$wallet_file" create-account "$mint" "$source_keypair" --owner "$wallet_address" >/dev/null +spl-token --url devnet --program-id "$program" --fee-payer "$wallet_file" create-account "$mint" "$destination_keypair" --owner "$wallet_address" >/dev/null +spl-token --url devnet --program-id "$program" --fee-payer "$wallet_file" create-account "$mint" "$close_keypair" --owner "$wallet_address" >/dev/null +spl-token --url devnet --program-id "$program" --fee-payer "$wallet_file" mint "$mint" 10 "$source_address" >/dev/null +ata="$(spl-token --url devnet --program-id "$program" address --verbose --token "$mint" --owner "$wallet_address" | sed -nE 's/^Associated token address:[[:space:]]+//p')" +tmp="$fixture.tmp" +prefix='TOKEN_2022' +freeze_authority="$wallet_address" +if [[ "$family" == classic ]]; then + prefix='TOKEN_CLASSIC' + freeze_authority='' +fi +cat > "$tmp" < + + +# SQL + +Ce répertoire est réservé aux scripts SQL généraux qui ne sont pas des validations. + +Les migrations propres à PostgreSQL doivent rester dans le crate `kb_store_pg`. diff --git a/migration/khadhroony-bot2-reference/sql/schema/.gitignore b/migration/khadhroony-bot2-reference/sql/schema/.gitignore new file mode 100644 index 0000000..cc1280c --- /dev/null +++ b/migration/khadhroony-bot2-reference/sql/schema/.gitignore @@ -0,0 +1,2 @@ +# file: sql/schema/.gitignore +# version: 1 diff --git a/migration/khadhroony-bot2-reference/sql/validation/.gitignore b/migration/khadhroony-bot2-reference/sql/validation/.gitignore new file mode 100644 index 0000000..6d8f581 --- /dev/null +++ b/migration/khadhroony-bot2-reference/sql/validation/.gitignore @@ -0,0 +1,2 @@ +# file: sql/validation/.gitignore +# version: 1 diff --git a/migration/khadhroony-bot2-reference/sql/validation/000_core_integrity.sql b/migration/khadhroony-bot2-reference/sql/validation/000_core_integrity.sql new file mode 100644 index 0000000..9132ca8 --- /dev/null +++ b/migration/khadhroony-bot2-reference/sql/validation/000_core_integrity.sql @@ -0,0 +1,107 @@ +-- file: sql/validation/000_core_integrity.sql +-- version: 5 + +-- Contrôles d'intégrité raw/core/ledger pour l'extracteur 0.3.4. + +-- 1. Aucune signature raw en double. +SELECT signature, COUNT(*) AS duplicate_count +FROM kb_sol_raw_transactions +GROUP BY signature +HAVING COUNT(*) > 1; + +-- 2. Aucune transaction core sans lineage raw valide. +SELECT core.signature, core.raw_transaction_id +FROM kb_sol_core_transactions core +LEFT JOIN kb_sol_raw_transactions raw ON raw.id = core.raw_transaction_id +WHERE core.raw_transaction_id IS NULL OR raw.id IS NULL; + +-- 3. Aucune ligne fille orpheline. +SELECT 'account_keys' AS child_table, child.id +FROM kb_sol_core_account_keys child +LEFT JOIN kb_sol_core_transactions core ON core.id = child.transaction_id +WHERE core.id IS NULL +UNION ALL +SELECT 'instructions', child.id +FROM kb_sol_core_instructions child +LEFT JOIN kb_sol_core_transactions core ON core.id = child.transaction_id +WHERE core.id IS NULL +UNION ALL +SELECT 'inner_instructions', child.id +FROM kb_sol_core_inner_instructions child +LEFT JOIN kb_sol_core_transactions core ON core.id = child.transaction_id +WHERE core.id IS NULL +UNION ALL +SELECT 'logs', child.id +FROM kb_sol_core_logs child +LEFT JOIN kb_sol_core_transactions core ON core.id = child.transaction_id +WHERE core.id IS NULL +UNION ALL +SELECT 'balance_changes', child.id +FROM kb_sol_core_balance_changes child +LEFT JOIN kb_sol_core_transactions core ON core.id = child.transaction_id +WHERE core.id IS NULL; + +-- 4. Aucune duplication des indices et chemins stables. +SELECT signature, account_index::TEXT AS stable_key, COUNT(*) AS duplicate_count +FROM kb_sol_core_account_keys +GROUP BY signature, account_index +HAVING COUNT(*) > 1 +UNION ALL +SELECT signature, instruction_path, COUNT(*) +FROM kb_sol_core_instructions +GROUP BY signature, instruction_path +HAVING COUNT(*) > 1 +UNION ALL +SELECT signature, instruction_path, COUNT(*) +FROM kb_sol_core_inner_instructions +GROUP BY signature, instruction_path +HAVING COUNT(*) > 1 +UNION ALL +SELECT signature, log_index::TEXT, COUNT(*) +FROM kb_sol_core_logs +GROUP BY signature, log_index +HAVING COUNT(*) > 1 +UNION ALL +SELECT signature, balance_change_index::TEXT, COUNT(*) +FROM kb_sol_core_balance_changes +GROUP BY signature, balance_change_index +HAVING COUNT(*) > 1; + +-- 5. Toute extraction réussie doit avoir une transaction core et une ligne raw core_extracted. +SELECT ledger.input_key AS signature, raw.processing_state, core.id AS core_transaction_id +FROM kb_sol_ops_processing_ledger ledger +LEFT JOIN kb_sol_raw_transactions raw ON raw.signature = ledger.input_key +LEFT JOIN kb_sol_core_transactions core ON core.signature = ledger.input_key +WHERE ledger.stage = 'core_extraction' + AND ledger.processor_name = 'canonical_to_core' + AND ledger.status = 'succeeded' + AND (raw.processing_state <> 'core_extracted' OR core.id IS NULL); + +-- 6. Toute ligne raw core_extracted doit avoir un graphe core et un succès ledger correspondant au hash courant. +SELECT raw.signature, raw.canonical_json_hash +FROM kb_sol_raw_transactions raw +LEFT JOIN kb_sol_core_transactions core ON core.raw_transaction_id = raw.id +LEFT JOIN kb_sol_ops_processing_ledger ledger + ON ledger.stage = 'core_extraction' + AND ledger.processor_name = 'canonical_to_core' + AND ledger.input_key = raw.signature + AND ledger.input_hash = raw.canonical_json_hash + AND ledger.status = 'succeeded' +WHERE raw.processing_state = 'core_extracted' + AND (core.id IS NULL OR ledger.id IS NULL); + +-- 7. Les chemins inner doivent référencer un parent top-level existant. +SELECT inner_instruction.signature, inner_instruction.parent_instruction_path, inner_instruction.instruction_path +FROM kb_sol_core_inner_instructions inner_instruction +LEFT JOIN kb_sol_core_instructions parent + ON parent.signature = inner_instruction.signature + AND parent.instruction_path = inner_instruction.parent_instruction_path +WHERE parent.id IS NULL; + +-- 8. Résumé des statuts du processor. +SELECT processor_version, status, COUNT(*) AS row_count, SUM(attempt_count) AS attempts +FROM kb_sol_ops_processing_ledger +WHERE stage = 'core_extraction' + AND processor_name = 'canonical_to_core' +GROUP BY processor_version, status +ORDER BY processor_version, status; diff --git a/migration/module-map.json b/migration/module-map.json new file mode 100644 index 0000000..548237d --- /dev/null +++ b/migration/module-map.json @@ -0,0 +1,1626 @@ +[ + { + "kind": "decoder", + "category": "adapter", + "name": "saber_decimal_wrapper", + "legacy_crate": "kb_decoder_adapter_saber_decimal_wrapper", + "module": "kb_lib::decoder::adapter::saber_decimal_wrapper" + }, + { + "kind": "decoder", + "category": "admin", + "name": "jupiter_lock", + "legacy_crate": "kb_decoder_admin_jupiter_lock", + "module": "kb_lib::decoder::admin::jupiter_lock" + }, + { + "kind": "decoder", + "category": "admin", + "name": "pump_fees", + "legacy_crate": "kb_decoder_admin_pump_fees", + "module": "kb_lib::decoder::admin::pump_fees" + }, + { + "kind": "decoder", + "category": "amm", + "name": "aldrin_v1", + "legacy_crate": "kb_decoder_amm_aldrin_v1", + "module": "kb_lib::decoder::amm::aldrin_v1" + }, + { + "kind": "decoder", + "category": "amm", + "name": "aldrin_v2", + "legacy_crate": "kb_decoder_amm_aldrin_v2", + "module": "kb_lib::decoder::amm::aldrin_v2" + }, + { + "kind": "decoder", + "category": "amm", + "name": "alphaq", + "legacy_crate": "kb_decoder_amm_alphaq", + "module": "kb_lib::decoder::amm::alphaq" + }, + { + "kind": "decoder", + "category": "amm", + "name": "believe", + "legacy_crate": "kb_decoder_amm_believe", + "module": "kb_lib::decoder::amm::believe" + }, + { + "kind": "decoder", + "category": "amm", + "name": "bonk_swap", + "legacy_crate": "kb_decoder_amm_bonk_swap", + "module": "kb_lib::decoder::amm::bonk_swap" + }, + { + "kind": "decoder", + "category": "amm", + "name": "fluxbeam", + "legacy_crate": "kb_decoder_amm_fluxbeam", + "module": "kb_lib::decoder::amm::fluxbeam" + }, + { + "kind": "decoder", + "category": "amm", + "name": "goon_fi", + "legacy_crate": "kb_decoder_amm_goon_fi", + "module": "kb_lib::decoder::amm::goon_fi" + }, + { + "kind": "decoder", + "category": "amm", + "name": "goosefx_gamma", + "legacy_crate": "kb_decoder_amm_goosefx_gamma", + "module": "kb_lib::decoder::amm::goosefx_gamma" + }, + { + "kind": "decoder", + "category": "amm", + "name": "goosefx_v2", + "legacy_crate": "kb_decoder_amm_goosefx_v2", + "module": "kb_lib::decoder::amm::goosefx_v2" + }, + { + "kind": "decoder", + "category": "amm", + "name": "guac_swap", + "legacy_crate": "kb_decoder_amm_guac_swap", + "module": "kb_lib::decoder::amm::guac_swap" + }, + { + "kind": "decoder", + "category": "amm", + "name": "lifinity_swap_v2", + "legacy_crate": "kb_decoder_amm_lifinity_swap_v2", + "module": "kb_lib::decoder::amm::lifinity_swap_v2" + }, + { + "kind": "decoder", + "category": "amm", + "name": "metadao_futarchy_amm", + "legacy_crate": "kb_decoder_amm_metadao_futarchy_amm", + "module": "kb_lib::decoder::amm::metadao_futarchy_amm" + }, + { + "kind": "decoder", + "category": "amm", + "name": "metadao_v0_5", + "legacy_crate": "kb_decoder_amm_metadao_v0_5", + "module": "kb_lib::decoder::amm::metadao_v0_5" + }, + { + "kind": "decoder", + "category": "amm", + "name": "meteora_damm_v1", + "legacy_crate": "kb_decoder_amm_meteora_damm_v1", + "module": "kb_lib::decoder::amm::meteora_damm_v1" + }, + { + "kind": "decoder", + "category": "amm", + "name": "meteora_damm_v2", + "legacy_crate": "kb_decoder_amm_meteora_damm_v2", + "module": "kb_lib::decoder::amm::meteora_damm_v2" + }, + { + "kind": "decoder", + "category": "amm", + "name": "obric_v2", + "legacy_crate": "kb_decoder_amm_obric_v2", + "module": "kb_lib::decoder::amm::obric_v2" + }, + { + "kind": "decoder", + "category": "amm", + "name": "one_dex", + "legacy_crate": "kb_decoder_amm_one_dex", + "module": "kb_lib::decoder::amm::one_dex" + }, + { + "kind": "decoder", + "category": "amm", + "name": "pump_swap", + "legacy_crate": "kb_decoder_amm_pump_swap", + "module": "kb_lib::decoder::amm::pump_swap" + }, + { + "kind": "decoder", + "category": "amm", + "name": "raydium_lp_v4", + "legacy_crate": "kb_decoder_amm_raydium_lp_v4", + "module": "kb_lib::decoder::amm::raydium_lp_v4" + }, + { + "kind": "decoder", + "category": "amm", + "name": "solfi", + "legacy_crate": "kb_decoder_amm_solfi", + "module": "kb_lib::decoder::amm::solfi" + }, + { + "kind": "decoder", + "category": "amm", + "name": "solfi_v2", + "legacy_crate": "kb_decoder_amm_solfi_v2", + "module": "kb_lib::decoder::amm::solfi_v2" + }, + { + "kind": "decoder", + "category": "amm", + "name": "vertigo", + "legacy_crate": "kb_decoder_amm_vertigo", + "module": "kb_lib::decoder::amm::vertigo" + }, + { + "kind": "decoder", + "category": "amm", + "name": "virtuals", + "legacy_crate": "kb_decoder_amm_virtuals", + "module": "kb_lib::decoder::amm::virtuals" + }, + { + "kind": "decoder", + "category": "amm", + "name": "woofi", + "legacy_crate": "kb_decoder_amm_woofi", + "module": "kb_lib::decoder::amm::woofi" + }, + { + "kind": "decoder", + "category": "amm", + "name": "zero_fi", + "legacy_crate": "kb_decoder_amm_zero_fi", + "module": "kb_lib::decoder::amm::zero_fi" + }, + { + "kind": "decoder", + "category": "amm", + "name": "zora", + "legacy_crate": "kb_decoder_amm_zora", + "module": "kb_lib::decoder::amm::zora" + }, + { + "kind": "decoder", + "category": "anchor", + "name": "core", + "legacy_crate": "kb_decoder_anchor", + "module": "kb_lib::decoder::anchor::core" + }, + { + "kind": "decoder", + "category": "bridge", + "name": "circle_cctp_token_messenger_minter", + "legacy_crate": "kb_decoder_bridge_circle_cctp_token_messenger_minter", + "module": "kb_lib::decoder::bridge::circle_cctp_token_messenger_minter" + }, + { + "kind": "decoder", + "category": "bridge", + "name": "circle_cctp_token_messenger_minter_v2", + "legacy_crate": "kb_decoder_bridge_circle_cctp_token_messenger_minter_v2", + "module": "kb_lib::decoder::bridge::circle_cctp_token_messenger_minter_v2" + }, + { + "kind": "decoder", + "category": "bridge", + "name": "layer_zero_endpoint", + "legacy_crate": "kb_decoder_bridge_layer_zero_endpoint", + "module": "kb_lib::decoder::bridge::layer_zero_endpoint" + }, + { + "kind": "decoder", + "category": "bridge", + "name": "layer_zero_executor", + "legacy_crate": "kb_decoder_bridge_layer_zero_executor", + "module": "kb_lib::decoder::bridge::layer_zero_executor" + }, + { + "kind": "decoder", + "category": "clmm", + "name": "byreal", + "legacy_crate": "kb_decoder_clmm_byreal", + "module": "kb_lib::decoder::clmm::byreal" + }, + { + "kind": "decoder", + "category": "clmm", + "name": "fusion", + "legacy_crate": "kb_decoder_clmm_fusion", + "module": "kb_lib::decoder::clmm::fusion" + }, + { + "kind": "decoder", + "category": "clmm", + "name": "orca_whirlpool", + "legacy_crate": "kb_decoder_clmm_orca_whirlpool", + "module": "kb_lib::decoder::clmm::orca_whirlpool" + }, + { + "kind": "decoder", + "category": "clmm", + "name": "pancake_swap", + "legacy_crate": "kb_decoder_clmm_pancake_swap", + "module": "kb_lib::decoder::clmm::pancake_swap" + }, + { + "kind": "decoder", + "category": "clmm", + "name": "raydium", + "legacy_crate": "kb_decoder_clmm_raydium", + "module": "kb_lib::decoder::clmm::raydium" + }, + { + "kind": "decoder", + "category": "clmm", + "name": "stabble", + "legacy_crate": "kb_decoder_clmm_stabble", + "module": "kb_lib::decoder::clmm::stabble" + }, + { + "kind": "decoder", + "category": "cpmm", + "name": "raydium", + "legacy_crate": "kb_decoder_cpmm_raydium", + "module": "kb_lib::decoder::cpmm::raydium" + }, + { + "kind": "decoder", + "category": "dlmm", + "name": "meteora", + "legacy_crate": "kb_decoder_dlmm_meteora", + "module": "kb_lib::decoder::dlmm::meteora" + }, + { + "kind": "decoder", + "category": "fees", + "name": "bags_fee_share_v1", + "legacy_crate": "kb_decoder_fees_bags_fee_share_v1", + "module": "kb_lib::decoder::fees::bags_fee_share_v1" + }, + { + "kind": "decoder", + "category": "fees", + "name": "bags_fee_share_v2", + "legacy_crate": "kb_decoder_fees_bags_fee_share_v2", + "module": "kb_lib::decoder::fees::bags_fee_share_v2" + }, + { + "kind": "decoder", + "category": "governance", + "name": "metadao_bid_wall", + "legacy_crate": "kb_decoder_governance_metadao_bid_wall", + "module": "kb_lib::decoder::governance::metadao_bid_wall" + }, + { + "kind": "decoder", + "category": "launchpad", + "name": "boop_fun", + "legacy_crate": "kb_decoder_launchpad_boop_fun", + "module": "kb_lib::decoder::launchpad::boop_fun" + }, + { + "kind": "decoder", + "category": "launchpad", + "name": "metadao_ico", + "legacy_crate": "kb_decoder_launchpad_metadao_ico", + "module": "kb_lib::decoder::launchpad::metadao_ico" + }, + { + "kind": "decoder", + "category": "launchpad", + "name": "meteora_dbc", + "legacy_crate": "kb_decoder_launchpad_meteora_dbc", + "module": "kb_lib::decoder::launchpad::meteora_dbc" + }, + { + "kind": "decoder", + "category": "launchpad", + "name": "moonit", + "legacy_crate": "kb_decoder_launchpad_moonit", + "module": "kb_lib::decoder::launchpad::moonit" + }, + { + "kind": "decoder", + "category": "launchpad", + "name": "orca_wavebreak", + "legacy_crate": "kb_decoder_launchpad_orca_wavebreak", + "module": "kb_lib::decoder::launchpad::orca_wavebreak" + }, + { + "kind": "decoder", + "category": "launchpad", + "name": "printr", + "legacy_crate": "kb_decoder_launchpad_printr", + "module": "kb_lib::decoder::launchpad::printr" + }, + { + "kind": "decoder", + "category": "launchpad", + "name": "pump_fun", + "legacy_crate": "kb_decoder_launchpad_pump_fun", + "module": "kb_lib::decoder::launchpad::pump_fun" + }, + { + "kind": "decoder", + "category": "launchpad", + "name": "pump_pumpup_ai", + "legacy_crate": "kb_decoder_launchpad_pump_pumpup_ai", + "module": "kb_lib::decoder::launchpad::pump_pumpup_ai" + }, + { + "kind": "decoder", + "category": "launchpad", + "name": "raydium_launchlab", + "legacy_crate": "kb_decoder_launchpad_raydium_launchlab", + "module": "kb_lib::decoder::launchpad::raydium_launchlab" + }, + { + "kind": "decoder", + "category": "lending", + "name": "clone", + "legacy_crate": "kb_decoder_lending_clone", + "module": "kb_lib::decoder::lending::clone" + }, + { + "kind": "decoder", + "category": "lending", + "name": "jupiter_lend_borrow", + "legacy_crate": "kb_decoder_lending_jupiter_lend_borrow", + "module": "kb_lib::decoder::lending::jupiter_lend_borrow" + }, + { + "kind": "decoder", + "category": "lending", + "name": "jupiter_lend_earn", + "legacy_crate": "kb_decoder_lending_jupiter_lend_earn", + "module": "kb_lib::decoder::lending::jupiter_lend_earn" + }, + { + "kind": "decoder", + "category": "lending", + "name": "jupiter_lend_flash_loan", + "legacy_crate": "kb_decoder_lending_jupiter_lend_flash_loan", + "module": "kb_lib::decoder::lending::jupiter_lend_flash_loan" + }, + { + "kind": "decoder", + "category": "lending", + "name": "jupiter_lend_liquidity", + "legacy_crate": "kb_decoder_lending_jupiter_lend_liquidity", + "module": "kb_lib::decoder::lending::jupiter_lend_liquidity" + }, + { + "kind": "decoder", + "category": "lending", + "name": "kamino", + "legacy_crate": "kb_decoder_lending_kamino", + "module": "kb_lib::decoder::lending::kamino" + }, + { + "kind": "decoder", + "category": "lending", + "name": "marginfi_v2", + "legacy_crate": "kb_decoder_lending_marginfi_v2", + "module": "kb_lib::decoder::lending::marginfi_v2" + }, + { + "kind": "decoder", + "category": "lock", + "name": "raydium_lp", + "legacy_crate": "kb_decoder_lock_raydium_lp", + "module": "kb_lib::decoder::lock::raydium_lp" + }, + { + "kind": "decoder", + "category": "metadata", + "name": "metaplex_token_metadata", + "legacy_crate": "kb_decoder_metadata_metaplex_token_metadata", + "module": "kb_lib::decoder::metadata::metaplex_token_metadata" + }, + { + "kind": "decoder", + "category": "metadata", + "name": "spl_name_service", + "legacy_crate": "kb_decoder_metadata_spl_name_service", + "module": "kb_lib::decoder::metadata::spl_name_service" + }, + { + "kind": "decoder", + "category": "nft", + "name": "metaplex_bubblegum", + "legacy_crate": "kb_decoder_nft_metaplex_bubblegum", + "module": "kb_lib::decoder::nft::metaplex_bubblegum" + }, + { + "kind": "decoder", + "category": "orderbook", + "name": "jupiter_limit_order", + "legacy_crate": "kb_decoder_orderbook_jupiter_limit_order", + "module": "kb_lib::decoder::orderbook::jupiter_limit_order" + }, + { + "kind": "decoder", + "category": "orderbook", + "name": "jupiter_limit_order_v2", + "legacy_crate": "kb_decoder_orderbook_jupiter_limit_order_v2", + "module": "kb_lib::decoder::orderbook::jupiter_limit_order_v2" + }, + { + "kind": "decoder", + "category": "orderbook", + "name": "openbook_v2", + "legacy_crate": "kb_decoder_orderbook_openbook_v2", + "module": "kb_lib::decoder::orderbook::openbook_v2" + }, + { + "kind": "decoder", + "category": "perpetuals", + "name": "drift_v2", + "legacy_crate": "kb_decoder_perpetuals_drift_v2", + "module": "kb_lib::decoder::perpetuals::drift_v2" + }, + { + "kind": "decoder", + "category": "perpetuals", + "name": "jupiter", + "legacy_crate": "kb_decoder_perpetuals_jupiter", + "module": "kb_lib::decoder::perpetuals::jupiter" + }, + { + "kind": "decoder", + "category": "perpetuals", + "name": "zeta", + "legacy_crate": "kb_decoder_perpetuals_zeta", + "module": "kb_lib::decoder::perpetuals::zeta" + }, + { + "kind": "decoder", + "category": "router", + "name": "dflow_aggregator_v4", + "legacy_crate": "kb_decoder_router_dflow_aggregator_v4", + "module": "kb_lib::decoder::router::dflow_aggregator_v4" + }, + { + "kind": "decoder", + "category": "router", + "name": "jupiter_aggregator_v4", + "legacy_crate": "kb_decoder_router_jupiter_aggregator_v4", + "module": "kb_lib::decoder::router::jupiter_aggregator_v4" + }, + { + "kind": "decoder", + "category": "router", + "name": "jupiter_aggregator_v6", + "legacy_crate": "kb_decoder_router_jupiter_aggregator_v6", + "module": "kb_lib::decoder::router::jupiter_aggregator_v6" + }, + { + "kind": "decoder", + "category": "router", + "name": "jupiter_dca", + "legacy_crate": "kb_decoder_router_jupiter_dca", + "module": "kb_lib::decoder::router::jupiter_dca" + }, + { + "kind": "decoder", + "category": "router", + "name": "okx_labs_v1", + "legacy_crate": "kb_decoder_router_okx_labs_v1", + "module": "kb_lib::decoder::router::okx_labs_v1" + }, + { + "kind": "decoder", + "category": "router", + "name": "okx_labs_v2", + "legacy_crate": "kb_decoder_router_okx_labs_v2", + "module": "kb_lib::decoder::router::okx_labs_v2" + }, + { + "kind": "decoder", + "category": "rwa", + "name": "ondo_global_markets", + "legacy_crate": "kb_decoder_rwa_ondo_global_markets", + "module": "kb_lib::decoder::rwa::ondo_global_markets" + }, + { + "kind": "decoder", + "category": "solana", + "name": "core", + "legacy_crate": "kb_decoder_solana_core", + "module": "kb_lib::decoder::solana::core" + }, + { + "kind": "decoder", + "category": "spl", + "name": "account_compression", + "legacy_crate": "kb_decoder_spl_account_compression", + "module": "kb_lib::decoder::spl::account_compression" + }, + { + "kind": "decoder", + "category": "spl", + "name": "associated_token_account", + "legacy_crate": "kb_decoder_spl_associated_token_account", + "module": "kb_lib::decoder::spl::associated_token_account" + }, + { + "kind": "decoder", + "category": "spl", + "name": "elgamal_registry", + "legacy_crate": "kb_decoder_spl_elgamal_registry", + "module": "kb_lib::decoder::spl::elgamal_registry" + }, + { + "kind": "decoder", + "category": "spl", + "name": "memo", + "legacy_crate": "kb_decoder_spl_memo", + "module": "kb_lib::decoder::spl::memo" + }, + { + "kind": "decoder", + "category": "spl", + "name": "noop", + "legacy_crate": "kb_decoder_spl_noop", + "module": "kb_lib::decoder::spl::noop" + }, + { + "kind": "decoder", + "category": "spl", + "name": "single_pool", + "legacy_crate": "kb_decoder_spl_single_pool", + "module": "kb_lib::decoder::spl::single_pool" + }, + { + "kind": "decoder", + "category": "spl", + "name": "stake_pool", + "legacy_crate": "kb_decoder_spl_stake_pool", + "module": "kb_lib::decoder::spl::stake_pool" + }, + { + "kind": "decoder", + "category": "spl", + "name": "token", + "legacy_crate": "kb_decoder_spl_token", + "module": "kb_lib::decoder::spl::token" + }, + { + "kind": "decoder", + "category": "spl", + "name": "token_2022", + "legacy_crate": "kb_decoder_spl_token_2022", + "module": "kb_lib::decoder::spl::token_2022" + }, + { + "kind": "decoder", + "category": "stable", + "name": "swap_hylo_exchange", + "legacy_crate": "kb_decoder_stable_swap_hylo_exchange", + "module": "kb_lib::decoder::stable::swap_hylo_exchange" + }, + { + "kind": "decoder", + "category": "stable", + "name": "swap_jupiter_stable", + "legacy_crate": "kb_decoder_stable_swap_jupiter_stable", + "module": "kb_lib::decoder::stable::swap_jupiter_stable" + }, + { + "kind": "decoder", + "category": "stable", + "name": "swap_numeraire", + "legacy_crate": "kb_decoder_stable_swap_numeraire", + "module": "kb_lib::decoder::stable::swap_numeraire" + }, + { + "kind": "decoder", + "category": "stable", + "name": "swap_stabble", + "legacy_crate": "kb_decoder_stable_swap_stabble", + "module": "kb_lib::decoder::stable::swap_stabble" + }, + { + "kind": "decoder", + "category": "staking", + "name": "kamino_farm", + "legacy_crate": "kb_decoder_staking_kamino_farm", + "module": "kb_lib::decoder::staking::kamino_farm" + }, + { + "kind": "decoder", + "category": "staking", + "name": "marinade_finance", + "legacy_crate": "kb_decoder_staking_marinade_finance", + "module": "kb_lib::decoder::staking::marinade_finance" + }, + { + "kind": "decoder", + "category": "staking", + "name": "solayer", + "legacy_crate": "kb_decoder_staking_solayer", + "module": "kb_lib::decoder::staking::solayer" + }, + { + "kind": "decoder", + "category": "treasury", + "name": "helium_treasury_management", + "legacy_crate": "kb_decoder_treasury_helium_treasury_management", + "module": "kb_lib::decoder::treasury::helium_treasury_management" + }, + { + "kind": "decoder", + "category": "vault", + "name": "carrot_defi", + "legacy_crate": "kb_decoder_vault_carrot_defi", + "module": "kb_lib::decoder::vault::carrot_defi" + }, + { + "kind": "decoder", + "category": "vault", + "name": "hylo_stability_pool", + "legacy_crate": "kb_decoder_vault_hylo_stability_pool", + "module": "kb_lib::decoder::vault::hylo_stability_pool" + }, + { + "kind": "decoder", + "category": "vault", + "name": "kamino", + "legacy_crate": "kb_decoder_vault_kamino", + "module": "kb_lib::decoder::vault::kamino" + }, + { + "kind": "decoder", + "category": "vault", + "name": "kamino_v2", + "legacy_crate": "kb_decoder_vault_kamino_v2", + "module": "kb_lib::decoder::vault::kamino_v2" + }, + { + "kind": "decoder", + "category": "vault", + "name": "kamino_yvaults", + "legacy_crate": "kb_decoder_vault_kamino_yvaults", + "module": "kb_lib::decoder::vault::kamino_yvaults" + }, + { + "kind": "decoder", + "category": "vault", + "name": "meteora", + "legacy_crate": "kb_decoder_vault_meteora", + "module": "kb_lib::decoder::vault::meteora" + }, + { + "kind": "decoder", + "category": "vesting", + "name": "streamflow", + "legacy_crate": "kb_decoder_vesting_streamflow", + "module": "kb_lib::decoder::vesting::streamflow" + }, + { + "kind": "decoder", + "category": "wallet", + "name": "jupiter_apepro_smart_wallet", + "legacy_crate": "kb_decoder_wallet_jupiter_apepro_smart_wallet", + "module": "kb_lib::decoder::wallet::jupiter_apepro_smart_wallet" + }, + { + "kind": "decoder", + "category": "weighted", + "name": "swap_stabble", + "legacy_crate": "kb_decoder_weighted_swap_stabble", + "module": "kb_lib::decoder::weighted::swap_stabble" + }, + { + "kind": "executor", + "category": "adapter", + "name": "saber_decimal_wrapper", + "legacy_crate": "kb_executor_adapter_saber_decimal_wrapper", + "module": "kb_lib::executor::adapter::saber_decimal_wrapper" + }, + { + "kind": "executor", + "category": "admin", + "name": "jupiter_lock", + "legacy_crate": "kb_executor_admin_jupiter_lock", + "module": "kb_lib::executor::admin::jupiter_lock" + }, + { + "kind": "executor", + "category": "admin", + "name": "pump_fees", + "legacy_crate": "kb_executor_admin_pump_fees", + "module": "kb_lib::executor::admin::pump_fees" + }, + { + "kind": "executor", + "category": "amm", + "name": "aldrin_v1", + "legacy_crate": "kb_executor_amm_aldrin_v1", + "module": "kb_lib::executor::amm::aldrin_v1" + }, + { + "kind": "executor", + "category": "amm", + "name": "aldrin_v2", + "legacy_crate": "kb_executor_amm_aldrin_v2", + "module": "kb_lib::executor::amm::aldrin_v2" + }, + { + "kind": "executor", + "category": "amm", + "name": "alphaq", + "legacy_crate": "kb_executor_amm_alphaq", + "module": "kb_lib::executor::amm::alphaq" + }, + { + "kind": "executor", + "category": "amm", + "name": "believe", + "legacy_crate": "kb_executor_amm_believe", + "module": "kb_lib::executor::amm::believe" + }, + { + "kind": "executor", + "category": "amm", + "name": "bonk_swap", + "legacy_crate": "kb_executor_amm_bonk_swap", + "module": "kb_lib::executor::amm::bonk_swap" + }, + { + "kind": "executor", + "category": "amm", + "name": "fluxbeam", + "legacy_crate": "kb_executor_amm_fluxbeam", + "module": "kb_lib::executor::amm::fluxbeam" + }, + { + "kind": "executor", + "category": "amm", + "name": "goon_fi", + "legacy_crate": "kb_executor_amm_goon_fi", + "module": "kb_lib::executor::amm::goon_fi" + }, + { + "kind": "executor", + "category": "amm", + "name": "goosefx_gamma", + "legacy_crate": "kb_executor_amm_goosefx_gamma", + "module": "kb_lib::executor::amm::goosefx_gamma" + }, + { + "kind": "executor", + "category": "amm", + "name": "goosefx_v2", + "legacy_crate": "kb_executor_amm_goosefx_v2", + "module": "kb_lib::executor::amm::goosefx_v2" + }, + { + "kind": "executor", + "category": "amm", + "name": "guac_swap", + "legacy_crate": "kb_executor_amm_guac_swap", + "module": "kb_lib::executor::amm::guac_swap" + }, + { + "kind": "executor", + "category": "amm", + "name": "lifinity_swap_v2", + "legacy_crate": "kb_executor_amm_lifinity_swap_v2", + "module": "kb_lib::executor::amm::lifinity_swap_v2" + }, + { + "kind": "executor", + "category": "amm", + "name": "metadao_futarchy_amm", + "legacy_crate": "kb_executor_amm_metadao_futarchy_amm", + "module": "kb_lib::executor::amm::metadao_futarchy_amm" + }, + { + "kind": "executor", + "category": "amm", + "name": "metadao_v0_5", + "legacy_crate": "kb_executor_amm_metadao_v0_5", + "module": "kb_lib::executor::amm::metadao_v0_5" + }, + { + "kind": "executor", + "category": "amm", + "name": "meteora_damm_v1", + "legacy_crate": "kb_executor_amm_meteora_damm_v1", + "module": "kb_lib::executor::amm::meteora_damm_v1" + }, + { + "kind": "executor", + "category": "amm", + "name": "meteora_damm_v2", + "legacy_crate": "kb_executor_amm_meteora_damm_v2", + "module": "kb_lib::executor::amm::meteora_damm_v2" + }, + { + "kind": "executor", + "category": "amm", + "name": "obric_v2", + "legacy_crate": "kb_executor_amm_obric_v2", + "module": "kb_lib::executor::amm::obric_v2" + }, + { + "kind": "executor", + "category": "amm", + "name": "one_dex", + "legacy_crate": "kb_executor_amm_one_dex", + "module": "kb_lib::executor::amm::one_dex" + }, + { + "kind": "executor", + "category": "amm", + "name": "pump_swap", + "legacy_crate": "kb_executor_amm_pump_swap", + "module": "kb_lib::executor::amm::pump_swap" + }, + { + "kind": "executor", + "category": "amm", + "name": "raydium_lp_v4", + "legacy_crate": "kb_executor_amm_raydium_lp_v4", + "module": "kb_lib::executor::amm::raydium_lp_v4" + }, + { + "kind": "executor", + "category": "amm", + "name": "solfi", + "legacy_crate": "kb_executor_amm_solfi", + "module": "kb_lib::executor::amm::solfi" + }, + { + "kind": "executor", + "category": "amm", + "name": "solfi_v2", + "legacy_crate": "kb_executor_amm_solfi_v2", + "module": "kb_lib::executor::amm::solfi_v2" + }, + { + "kind": "executor", + "category": "amm", + "name": "vertigo", + "legacy_crate": "kb_executor_amm_vertigo", + "module": "kb_lib::executor::amm::vertigo" + }, + { + "kind": "executor", + "category": "amm", + "name": "virtuals", + "legacy_crate": "kb_executor_amm_virtuals", + "module": "kb_lib::executor::amm::virtuals" + }, + { + "kind": "executor", + "category": "amm", + "name": "woofi", + "legacy_crate": "kb_executor_amm_woofi", + "module": "kb_lib::executor::amm::woofi" + }, + { + "kind": "executor", + "category": "amm", + "name": "zero_fi", + "legacy_crate": "kb_executor_amm_zero_fi", + "module": "kb_lib::executor::amm::zero_fi" + }, + { + "kind": "executor", + "category": "amm", + "name": "zora", + "legacy_crate": "kb_executor_amm_zora", + "module": "kb_lib::executor::amm::zora" + }, + { + "kind": "executor", + "category": "bridge", + "name": "circle_cctp_token_messenger_minter", + "legacy_crate": "kb_executor_bridge_circle_cctp_token_messenger_minter", + "module": "kb_lib::executor::bridge::circle_cctp_token_messenger_minter" + }, + { + "kind": "executor", + "category": "bridge", + "name": "circle_cctp_token_messenger_minter_v2", + "legacy_crate": "kb_executor_bridge_circle_cctp_token_messenger_minter_v2", + "module": "kb_lib::executor::bridge::circle_cctp_token_messenger_minter_v2" + }, + { + "kind": "executor", + "category": "bridge", + "name": "layer_zero_endpoint", + "legacy_crate": "kb_executor_bridge_layer_zero_endpoint", + "module": "kb_lib::executor::bridge::layer_zero_endpoint" + }, + { + "kind": "executor", + "category": "bridge", + "name": "layer_zero_executor", + "legacy_crate": "kb_executor_bridge_layer_zero_executor", + "module": "kb_lib::executor::bridge::layer_zero_executor" + }, + { + "kind": "executor", + "category": "clmm", + "name": "byreal", + "legacy_crate": "kb_executor_clmm_byreal", + "module": "kb_lib::executor::clmm::byreal" + }, + { + "kind": "executor", + "category": "clmm", + "name": "fusion", + "legacy_crate": "kb_executor_clmm_fusion", + "module": "kb_lib::executor::clmm::fusion" + }, + { + "kind": "executor", + "category": "clmm", + "name": "orca_whirlpool", + "legacy_crate": "kb_executor_clmm_orca_whirlpool", + "module": "kb_lib::executor::clmm::orca_whirlpool" + }, + { + "kind": "executor", + "category": "clmm", + "name": "pancake_swap", + "legacy_crate": "kb_executor_clmm_pancake_swap", + "module": "kb_lib::executor::clmm::pancake_swap" + }, + { + "kind": "executor", + "category": "clmm", + "name": "raydium", + "legacy_crate": "kb_executor_clmm_raydium", + "module": "kb_lib::executor::clmm::raydium" + }, + { + "kind": "executor", + "category": "clmm", + "name": "stabble", + "legacy_crate": "kb_executor_clmm_stabble", + "module": "kb_lib::executor::clmm::stabble" + }, + { + "kind": "executor", + "category": "cpmm", + "name": "raydium", + "legacy_crate": "kb_executor_cpmm_raydium", + "module": "kb_lib::executor::cpmm::raydium" + }, + { + "kind": "executor", + "category": "dlmm", + "name": "meteora", + "legacy_crate": "kb_executor_dlmm_meteora", + "module": "kb_lib::executor::dlmm::meteora" + }, + { + "kind": "executor", + "category": "fees", + "name": "bags_fee_share_v1", + "legacy_crate": "kb_executor_fees_bags_fee_share_v1", + "module": "kb_lib::executor::fees::bags_fee_share_v1" + }, + { + "kind": "executor", + "category": "fees", + "name": "bags_fee_share_v2", + "legacy_crate": "kb_executor_fees_bags_fee_share_v2", + "module": "kb_lib::executor::fees::bags_fee_share_v2" + }, + { + "kind": "executor", + "category": "governance", + "name": "metadao_bid_wall", + "legacy_crate": "kb_executor_governance_metadao_bid_wall", + "module": "kb_lib::executor::governance::metadao_bid_wall" + }, + { + "kind": "executor", + "category": "launchpad", + "name": "boop_fun", + "legacy_crate": "kb_executor_launchpad_boop_fun", + "module": "kb_lib::executor::launchpad::boop_fun" + }, + { + "kind": "executor", + "category": "launchpad", + "name": "metadao_ico", + "legacy_crate": "kb_executor_launchpad_metadao_ico", + "module": "kb_lib::executor::launchpad::metadao_ico" + }, + { + "kind": "executor", + "category": "launchpad", + "name": "meteora_dbc", + "legacy_crate": "kb_executor_launchpad_meteora_dbc", + "module": "kb_lib::executor::launchpad::meteora_dbc" + }, + { + "kind": "executor", + "category": "launchpad", + "name": "moonit", + "legacy_crate": "kb_executor_launchpad_moonit", + "module": "kb_lib::executor::launchpad::moonit" + }, + { + "kind": "executor", + "category": "launchpad", + "name": "orca_wavebreak", + "legacy_crate": "kb_executor_launchpad_orca_wavebreak", + "module": "kb_lib::executor::launchpad::orca_wavebreak" + }, + { + "kind": "executor", + "category": "launchpad", + "name": "printr", + "legacy_crate": "kb_executor_launchpad_printr", + "module": "kb_lib::executor::launchpad::printr" + }, + { + "kind": "executor", + "category": "launchpad", + "name": "pump_fun", + "legacy_crate": "kb_executor_launchpad_pump_fun", + "module": "kb_lib::executor::launchpad::pump_fun" + }, + { + "kind": "executor", + "category": "launchpad", + "name": "pump_pumpup_ai", + "legacy_crate": "kb_executor_launchpad_pump_pumpup_ai", + "module": "kb_lib::executor::launchpad::pump_pumpup_ai" + }, + { + "kind": "executor", + "category": "launchpad", + "name": "raydium_launchlab", + "legacy_crate": "kb_executor_launchpad_raydium_launchlab", + "module": "kb_lib::executor::launchpad::raydium_launchlab" + }, + { + "kind": "executor", + "category": "lending", + "name": "clone", + "legacy_crate": "kb_executor_lending_clone", + "module": "kb_lib::executor::lending::clone" + }, + { + "kind": "executor", + "category": "lending", + "name": "jupiter_lend_borrow", + "legacy_crate": "kb_executor_lending_jupiter_lend_borrow", + "module": "kb_lib::executor::lending::jupiter_lend_borrow" + }, + { + "kind": "executor", + "category": "lending", + "name": "jupiter_lend_earn", + "legacy_crate": "kb_executor_lending_jupiter_lend_earn", + "module": "kb_lib::executor::lending::jupiter_lend_earn" + }, + { + "kind": "executor", + "category": "lending", + "name": "jupiter_lend_flash_loan", + "legacy_crate": "kb_executor_lending_jupiter_lend_flash_loan", + "module": "kb_lib::executor::lending::jupiter_lend_flash_loan" + }, + { + "kind": "executor", + "category": "lending", + "name": "jupiter_lend_liquidity", + "legacy_crate": "kb_executor_lending_jupiter_lend_liquidity", + "module": "kb_lib::executor::lending::jupiter_lend_liquidity" + }, + { + "kind": "executor", + "category": "lending", + "name": "kamino", + "legacy_crate": "kb_executor_lending_kamino", + "module": "kb_lib::executor::lending::kamino" + }, + { + "kind": "executor", + "category": "lending", + "name": "marginfi_v2", + "legacy_crate": "kb_executor_lending_marginfi_v2", + "module": "kb_lib::executor::lending::marginfi_v2" + }, + { + "kind": "executor", + "category": "lock", + "name": "raydium_lp", + "legacy_crate": "kb_executor_lock_raydium_lp", + "module": "kb_lib::executor::lock::raydium_lp" + }, + { + "kind": "executor", + "category": "metadata", + "name": "metaplex_token_metadata", + "legacy_crate": "kb_executor_metadata_metaplex_token_metadata", + "module": "kb_lib::executor::metadata::metaplex_token_metadata" + }, + { + "kind": "executor", + "category": "metadata", + "name": "spl_name_service", + "legacy_crate": "kb_executor_metadata_spl_name_service", + "module": "kb_lib::executor::metadata::spl_name_service" + }, + { + "kind": "executor", + "category": "nft", + "name": "metaplex_bubblegum", + "legacy_crate": "kb_executor_nft_metaplex_bubblegum", + "module": "kb_lib::executor::nft::metaplex_bubblegum" + }, + { + "kind": "executor", + "category": "orderbook", + "name": "jupiter_limit_order", + "legacy_crate": "kb_executor_orderbook_jupiter_limit_order", + "module": "kb_lib::executor::orderbook::jupiter_limit_order" + }, + { + "kind": "executor", + "category": "orderbook", + "name": "jupiter_limit_order_v2", + "legacy_crate": "kb_executor_orderbook_jupiter_limit_order_v2", + "module": "kb_lib::executor::orderbook::jupiter_limit_order_v2" + }, + { + "kind": "executor", + "category": "orderbook", + "name": "openbook_v2", + "legacy_crate": "kb_executor_orderbook_openbook_v2", + "module": "kb_lib::executor::orderbook::openbook_v2" + }, + { + "kind": "executor", + "category": "perpetuals", + "name": "drift_v2", + "legacy_crate": "kb_executor_perpetuals_drift_v2", + "module": "kb_lib::executor::perpetuals::drift_v2" + }, + { + "kind": "executor", + "category": "perpetuals", + "name": "jupiter", + "legacy_crate": "kb_executor_perpetuals_jupiter", + "module": "kb_lib::executor::perpetuals::jupiter" + }, + { + "kind": "executor", + "category": "perpetuals", + "name": "zeta", + "legacy_crate": "kb_executor_perpetuals_zeta", + "module": "kb_lib::executor::perpetuals::zeta" + }, + { + "kind": "executor", + "category": "router", + "name": "dflow_aggregator_v4", + "legacy_crate": "kb_executor_router_dflow_aggregator_v4", + "module": "kb_lib::executor::router::dflow_aggregator_v4" + }, + { + "kind": "executor", + "category": "router", + "name": "jupiter_aggregator_v4", + "legacy_crate": "kb_executor_router_jupiter_aggregator_v4", + "module": "kb_lib::executor::router::jupiter_aggregator_v4" + }, + { + "kind": "executor", + "category": "router", + "name": "jupiter_aggregator_v6", + "legacy_crate": "kb_executor_router_jupiter_aggregator_v6", + "module": "kb_lib::executor::router::jupiter_aggregator_v6" + }, + { + "kind": "executor", + "category": "router", + "name": "jupiter_dca", + "legacy_crate": "kb_executor_router_jupiter_dca", + "module": "kb_lib::executor::router::jupiter_dca" + }, + { + "kind": "executor", + "category": "router", + "name": "okx_labs_v1", + "legacy_crate": "kb_executor_router_okx_labs_v1", + "module": "kb_lib::executor::router::okx_labs_v1" + }, + { + "kind": "executor", + "category": "router", + "name": "okx_labs_v2", + "legacy_crate": "kb_executor_router_okx_labs_v2", + "module": "kb_lib::executor::router::okx_labs_v2" + }, + { + "kind": "executor", + "category": "rwa", + "name": "ondo_global_markets", + "legacy_crate": "kb_executor_rwa_ondo_global_markets", + "module": "kb_lib::executor::rwa::ondo_global_markets" + }, + { + "kind": "executor", + "category": "solana", + "name": "core", + "legacy_crate": "kb_executor_solana_core", + "module": "kb_lib::executor::solana::core" + }, + { + "kind": "executor", + "category": "spl", + "name": "account_compression", + "legacy_crate": "kb_executor_spl_account_compression", + "module": "kb_lib::executor::spl::account_compression" + }, + { + "kind": "executor", + "category": "spl", + "name": "associated_token_account", + "legacy_crate": "kb_executor_spl_associated_token_account", + "module": "kb_lib::executor::spl::associated_token_account" + }, + { + "kind": "executor", + "category": "spl", + "name": "elgamal_registry", + "legacy_crate": "kb_executor_spl_elgamal_registry", + "module": "kb_lib::executor::spl::elgamal_registry" + }, + { + "kind": "executor", + "category": "spl", + "name": "memo", + "legacy_crate": "kb_executor_spl_memo", + "module": "kb_lib::executor::spl::memo" + }, + { + "kind": "executor", + "category": "spl", + "name": "noop", + "legacy_crate": "kb_executor_spl_noop", + "module": "kb_lib::executor::spl::noop" + }, + { + "kind": "executor", + "category": "spl", + "name": "single_pool", + "legacy_crate": "kb_executor_spl_single_pool", + "module": "kb_lib::executor::spl::single_pool" + }, + { + "kind": "executor", + "category": "spl", + "name": "stake_pool", + "legacy_crate": "kb_executor_spl_stake_pool", + "module": "kb_lib::executor::spl::stake_pool" + }, + { + "kind": "executor", + "category": "spl", + "name": "token", + "legacy_crate": "kb_executor_spl_token", + "module": "kb_lib::executor::spl::token" + }, + { + "kind": "executor", + "category": "spl", + "name": "token_2022", + "legacy_crate": "kb_executor_spl_token_2022", + "module": "kb_lib::executor::spl::token_2022" + }, + { + "kind": "executor", + "category": "stable", + "name": "swap_hylo_exchange", + "legacy_crate": "kb_executor_stable_swap_hylo_exchange", + "module": "kb_lib::executor::stable::swap_hylo_exchange" + }, + { + "kind": "executor", + "category": "stable", + "name": "swap_jupiter_stable", + "legacy_crate": "kb_executor_stable_swap_jupiter_stable", + "module": "kb_lib::executor::stable::swap_jupiter_stable" + }, + { + "kind": "executor", + "category": "stable", + "name": "swap_numeraire", + "legacy_crate": "kb_executor_stable_swap_numeraire", + "module": "kb_lib::executor::stable::swap_numeraire" + }, + { + "kind": "executor", + "category": "stable", + "name": "swap_stabble", + "legacy_crate": "kb_executor_stable_swap_stabble", + "module": "kb_lib::executor::stable::swap_stabble" + }, + { + "kind": "executor", + "category": "staking", + "name": "kamino_farm", + "legacy_crate": "kb_executor_staking_kamino_farm", + "module": "kb_lib::executor::staking::kamino_farm" + }, + { + "kind": "executor", + "category": "staking", + "name": "marinade_finance", + "legacy_crate": "kb_executor_staking_marinade_finance", + "module": "kb_lib::executor::staking::marinade_finance" + }, + { + "kind": "executor", + "category": "staking", + "name": "solayer", + "legacy_crate": "kb_executor_staking_solayer", + "module": "kb_lib::executor::staking::solayer" + }, + { + "kind": "executor", + "category": "treasury", + "name": "helium_treasury_management", + "legacy_crate": "kb_executor_treasury_helium_treasury_management", + "module": "kb_lib::executor::treasury::helium_treasury_management" + }, + { + "kind": "executor", + "category": "vault", + "name": "carrot_defi", + "legacy_crate": "kb_executor_vault_carrot_defi", + "module": "kb_lib::executor::vault::carrot_defi" + }, + { + "kind": "executor", + "category": "vault", + "name": "hylo_stability_pool", + "legacy_crate": "kb_executor_vault_hylo_stability_pool", + "module": "kb_lib::executor::vault::hylo_stability_pool" + }, + { + "kind": "executor", + "category": "vault", + "name": "kamino", + "legacy_crate": "kb_executor_vault_kamino", + "module": "kb_lib::executor::vault::kamino" + }, + { + "kind": "executor", + "category": "vault", + "name": "kamino_v2", + "legacy_crate": "kb_executor_vault_kamino_v2", + "module": "kb_lib::executor::vault::kamino_v2" + }, + { + "kind": "executor", + "category": "vault", + "name": "kamino_yvaults", + "legacy_crate": "kb_executor_vault_kamino_yvaults", + "module": "kb_lib::executor::vault::kamino_yvaults" + }, + { + "kind": "executor", + "category": "vault", + "name": "meteora", + "legacy_crate": "kb_executor_vault_meteora", + "module": "kb_lib::executor::vault::meteora" + }, + { + "kind": "executor", + "category": "vesting", + "name": "streamflow", + "legacy_crate": "kb_executor_vesting_streamflow", + "module": "kb_lib::executor::vesting::streamflow" + }, + { + "kind": "executor", + "category": "wallet", + "name": "jupiter_apepro_smart_wallet", + "legacy_crate": "kb_executor_wallet_jupiter_apepro_smart_wallet", + "module": "kb_lib::executor::wallet::jupiter_apepro_smart_wallet" + }, + { + "kind": "executor", + "category": "weighted", + "name": "swap_stabble", + "legacy_crate": "kb_executor_weighted_swap_stabble", + "module": "kb_lib::executor::weighted::swap_stabble" + }, + { + "kind": "materializer", + "category": "admin", + "name": "core", + "legacy_crate": "kb_materializer_admin", + "module": "kb_lib::materializer::admin::core" + }, + { + "kind": "materializer", + "category": "bridge", + "name": "core", + "legacy_crate": "kb_materializer_bridge", + "module": "kb_lib::materializer::bridge::core" + }, + { + "kind": "materializer", + "category": "compliance", + "name": "audit", + "legacy_crate": "kb_materializer_compliance_audit", + "module": "kb_lib::materializer::compliance::audit" + }, + { + "kind": "materializer", + "category": "fees", + "name": "core", + "legacy_crate": "kb_materializer_fees", + "module": "kb_lib::materializer::fees::core" + }, + { + "kind": "materializer", + "category": "governance", + "name": "core", + "legacy_crate": "kb_materializer_governance", + "module": "kb_lib::materializer::governance::core" + }, + { + "kind": "materializer", + "category": "lending", + "name": "core", + "legacy_crate": "kb_materializer_lending", + "module": "kb_lib::materializer::lending::core" + }, + { + "kind": "materializer", + "category": "lifecycle", + "name": "core", + "legacy_crate": "kb_materializer_lifecycle", + "module": "kb_lib::materializer::lifecycle::core" + }, + { + "kind": "materializer", + "category": "liquidity", + "name": "core", + "legacy_crate": "kb_materializer_liquidity", + "module": "kb_lib::materializer::liquidity::core" + }, + { + "kind": "materializer", + "category": "metadata", + "name": "core", + "legacy_crate": "kb_materializer_metadata", + "module": "kb_lib::materializer::metadata::core" + }, + { + "kind": "materializer", + "category": "nft", + "name": "core", + "legacy_crate": "kb_materializer_nft", + "module": "kb_lib::materializer::nft::core" + }, + { + "kind": "materializer", + "category": "oracle", + "name": "core", + "legacy_crate": "kb_materializer_oracle", + "module": "kb_lib::materializer::oracle::core" + }, + { + "kind": "materializer", + "category": "orderbook", + "name": "core", + "legacy_crate": "kb_materializer_orderbook", + "module": "kb_lib::materializer::orderbook::core" + }, + { + "kind": "materializer", + "category": "perpetuals", + "name": "core", + "legacy_crate": "kb_materializer_perpetuals", + "module": "kb_lib::materializer::perpetuals::core" + }, + { + "kind": "materializer", + "category": "pool", + "name": "state", + "legacy_crate": "kb_materializer_pool_state", + "module": "kb_lib::materializer::pool::state" + }, + { + "kind": "materializer", + "category": "rewards", + "name": "core", + "legacy_crate": "kb_materializer_rewards", + "module": "kb_lib::materializer::rewards::core" + }, + { + "kind": "materializer", + "category": "risk", + "name": "core", + "legacy_crate": "kb_materializer_risk", + "module": "kb_lib::materializer::risk::core" + }, + { + "kind": "materializer", + "category": "routing", + "name": "core", + "legacy_crate": "kb_materializer_routing", + "module": "kb_lib::materializer::routing::core" + }, + { + "kind": "materializer", + "category": "staking", + "name": "core", + "legacy_crate": "kb_materializer_staking", + "module": "kb_lib::materializer::staking::core" + }, + { + "kind": "materializer", + "category": "token", + "name": "accounts", + "legacy_crate": "kb_materializer_token_accounts", + "module": "kb_lib::materializer::token::accounts" + }, + { + "kind": "materializer", + "category": "token", + "name": "metadata_risk", + "legacy_crate": "kb_materializer_token_metadata_risk", + "module": "kb_lib::materializer::token::metadata_risk" + }, + { + "kind": "materializer", + "category": "trades", + "name": "core", + "legacy_crate": "kb_materializer_trades", + "module": "kb_lib::materializer::trades::core" + }, + { + "kind": "materializer", + "category": "transaction", + "name": "annotations", + "legacy_crate": "kb_materializer_transaction_annotations", + "module": "kb_lib::materializer::transaction::annotations" + }, + { + "kind": "materializer", + "category": "vault", + "name": "core", + "legacy_crate": "kb_materializer_vault", + "module": "kb_lib::materializer::vault::core" + } +] diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..9c55290 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,23 @@ +# file: rustfmt.toml +# version: 1 + +edition = "2024" +newline_style = "Unix" +use_small_heuristics = "Default" +hard_tabs = false +tab_spaces = 4 +max_width = 100 +chain_width = 80 +fn_call_width = 80 +attr_fn_like_width = 80 +struct_lit_width = 40 +struct_variant_width = 40 +array_width = 80 +single_line_if_else_max_width = 80 +single_line_let_else_max_width = 80 +reorder_imports = true +reorder_modules = true +match_block_trailing_comma = true +use_field_init_shorthand = true +use_try_shorthand = false +force_explicit_abi = true

puUahfH$HY+8uc-vUY3Eomb)ztP#eYE(>u(9cIF5*9j0k z={tS27@R+cx|(cZPQ(PgkcBunJTzQ%@%H1lVbu3}p!>iTNKDxRFO-fe7YE_nm7}ox zvjU$9gZf?7gn3LjMT1DI@Bjg0RO=#D9xim@EjH+{KP*9R@t@JX-(x7~_(%NWy+7jr z=C7F8wV3_LdwA{V-$SsO`gG8`_oE`BCe+HX@~tjtZFvs^Mt+Ipv<(Oa^Kkam7<~1? z7@Rv4uPIWZsT6~Wwe!nRQ~e6YPG5?&%=G{d;L?SWSo_%o9Nd&CZeR*{L1PtucuP9w z);)u%-}wr;1*<`618T1f!GV%Mr%-xOLV8R6Kr6wLkoQ<(PMFOieK8k9Dn?#f`S z`CJAOpRuQVUDrl=l=u-$i{`7QwV@!Bg@GTY4F;KZ(qd#6sWaLyNCVhtP zmGh95-v<|~qok9lSY(vjYo?zqP?4N_v zr}NR+6oarz6IU0&9fU%jZm3V6P7+2diQC{h9Qk3IwuxE}`&4ntyM2px*V z)c4?_QKs3IS&kxSo9HY*d&;5eY9{YngXT$-6e=i-A&+{8zLASbOAGsf>g&x(1VR5(6gshfu7>nCB=)*M*~8MImW2NK<6 zI1aSFjar5;0ar!kQK>nW&!-$}$3{DFDGf8^@iPJ#6VLscZJjYUYDnvmuXZ z8m0pR;u4=pWjG4SEH++wVLhgf`W+~#MaV0`p2IUxbuJeT*Q0e2ttk8z9nYYD_dgh& zFC`|k-C}Wx;#G_#c-Uuf2vy~|zc-fGPc=NeoxudNq(ly-qJFS=#*B$$4LH;sdzfb0 zrk;D;u#YGq?H1U|s7ORo(TL9z?K;vi+iFC(C3^|m`J~sD9HeX9TOR|p>LLAENu)D8 zXF!m9I%hq|nSQ0ucXYt0$_!9?8KZg}#kXhrAuTqDj=3wOK7X)gI0ik>I6!lgm9F+c z1sJK?F+fez7h)VS0mqj|tidsGIMhLqd-&_JvH11uY9z(4!-JC+;Hd@Q!P85I;XD2F zFr?c?B*pDRTPO#|s|Vxtwf%9v$<;%Spmen-1bA}sNZdQ10E5dnA}Rg=T7!8wRy`1} zukVNRjV?UyNy2k(jItTBC0Rlqhs1H3k>~$!CBQu1vV_R_l*nh!w z1c*>x^d>%f;LZQ5`i9M@&Nx=R-VLi>t1yd-HhD6H8->(S z$8`IY5|AjOwVV&)?qG5@z? zeWGq+H+ozY7aYLGk2>M&j~@o-56g{=_{8YknnbFmX3y$O>|1{~xc6O6e-%IE>7t8y z^6W}Y?b!pdc;%-zgY%mObuo;htPRXyXyNCM#N*Xp%mC<2-PBTtAgf~wZocaUPFg=)Zw>~vMcf=$tr28Hrrv8m#w>GGI#l2g=R4+!%;hwc1!>5QETybrs+ z?G{Fzl{uIY(&W&e0eS{N;ba7|+q4NsWfSZiE}UzZ#<;Uh9G zMCWB_27a_0hrE~w)moi0K-S@2cb0p zqVTWzPh;EGEJFpC>!8vkr=~Rd?p5Y7A6w$mr5Loe^+ij0#up9i5pa`GtvQ8;}%4Y9F~bzT%gL%}_cOBVyEIyDO=9bZ6M*_UwLAs8~K z3XXG9XtTa96USmvo7=nI#2z-tCV)B$-H?=JN+b%aGPD^8Bf7x1U893Rf~6}hS0X)4_gvP|3bhO@ly?rW3guQ zRD8anSj$YP{ctPV9_4TqswRhLnodo^)+i1OaWrdrFyz706#VvhzeKJr1l6_CG86a> z46Zzct$Vxpl{VH!$q9#NH^_wRVW`bHWFGgi>jeGW6pV};3X1Iop-#H^yf(4Ls^8T5 z2EXA^cw%(Ky3YT-NJX#D4h10;&LtKvvYZ1U@TJtk?G z0J9o7RSGc9HEmOU!?R=6eCWS1LyIuvw;9noQ<(#vA5a0T0T?$-9$TMHiDgE~;iv5q z2->l}j>_4Vf)FN~X2+++Rgq0pBHrANoU{jBjB4fpC(sQ>xF-hF>lxP)jqv7NQ|!5X z4Jy!Iwep{9h{1{KspwSr7RrlPBg*NG3EisUID(Lk#mucu-J-jxvIaO#!^V{8l`2(_RMA4!5Za=Df0yr59A%I*D*{i9&)~6j!Bnrpk=L(!dbg z0ol+XY-%}`s9GR~;r1WAkJz|X2)6abg4gawYoqI<19PQckcLHEJ9VqgjO#{)Qd2NO zEtHhN3o&piHU^c$3h&>lI6)vXfeShSow|O8ott|^vSTUL5AoH@QTlrciH$~&t2QPp z(>E#Qy8hYb##p#fw0->$@zbZkmF2V1BHLLLc3q}7v+y0B7}s-!(y#fvGX@WzjD*Bx z=utTj8M(btdnwvP3`h}H_%W&iRvBBtYG}eF(N<9_&Ru$jKyR*Iy#c$w$@UwckZ>Ar zc$2L%SH=G~HMqWWueW=`^(N{_)4IgOMyv4P2zgvnv`9PRT^({^Eij6n7^x?YJWl^f zvvThkG;*We=JkS3{8~Y7pRY;S`fZjO_(&ZzXO^y!de&!T2!=dM(jl&CeSHP8vbG~X z{~FTL&ZDJe2rgfamPipteSJAnQx764iX$Om3!0inpy_%5t^*UFV7W*_uJc>B_C`rZ zAUgV63>t7A-Mj4u09?9s7fx0s`qoApDNz(SVo{sa^q0zk0O$VmD75(EUTACcG}}?- zr>c#TSxz`xcI05|&YMx%aV*A+*p9AUK16)nS_~b$7J~+q;_%T~SiQay2ahCbWZt;0 z+Nz^=SO(y5UC&pH5_>3QMNp_uf8tCW{`UH4bT00TX_L31wBtXKk-iYOOk0T2!)IX4 zhSB(RWgg;Vqu@Fej!O}c=!a)9UP2y&$AFL*f+w}A=2=pxte?d}tym%PoO4VbT7}_* zRv=Y6Tzg&#cJI3#pDeG$#ky!C$*^KC2somn9ML376QPcUH5lCg830jOvU)!18(dAV z^&yzDfQ*Y3EQ8jzI8Bda00NGO**6@-kbWN{TI$G~TL$9mZ8PxcnliMs3OcHa$gG6@1fMK|B|ksY1tIe{BrbAIuEf=eTT{)%wMJ3#-S>P|>lEi`(%m z;f$_!W3hGiEa&YT?^$XqQM+*CNDwAmGVE%(4qj=puj6%tq?5=kAT^=cT*vFtU@(r% z6a<$Wt=Ece)TpyEzp~Nmw1gJ@vsbT+=o}(z1&+u)NNI^h-6XPZL);T)N&h7k-JsRV z!(rq8?&wq~+>pn0twHZj2Zdbb$_yN;Nzk$aj!6F-zh>N^!pzbis~8{S^4ng8p;Uw% zn@=a8`0MXs=I~#lFza9V(U=_kae1G1>vf;RG+gE+cUUH4ChgP)s&tV)ia*h`e+|Gm zmPdt^X-q819@EJYMncN0!4#FxvRdO5d%kaAOJ~3 zK~!9Bh>HYs^qRQim z!vlOF>sB*FbPlo6hbs3zPV52_8kP5R_RS)xoU%nj%#5Ci80`;Z98Q4hW=Lh<8ep3vA5HUFf=qM1nA5epG) z*)$DfCoO~P9>B!uyDUD9-Ug}-WfU??<#B{ST`CZch~`(k ze|~=EcDgzkWipQ$8BNFt?#5-O|a#3YBhSC z*{;JpLer;_0*|+2jAF9~B)Y(eMmDtuiWuab*qkqUzI<)ED!`~jvhCr>+ev}6{tRUA2xk65F5V9*LkJ- zZV77+xHa(TRB}?D_Eelco9K7z=jP`$eM=5P4jfCs$=AoBOG!W6IDR8aisvFdZ4qvn zwg_WJ+=vCA+=$g%G7)eoqN5zq3_BD7m%??Z801hQHJ{Lpe<;Kd@;p%DXy;Y!fnYIC z#tk`xvBMW5S?1AtULp1!x(lB!?}Kxfq9k+0pfC0S)sZgGhIa{^IGv550|1~61c?eQdy z(#}IxntySB5MRGTY^jNo_3JoBYlE|OiSWBEhohbru8Y;%3vlCTkqKO0az3I#v{_tD zEe`6hO-4%6GL#hU1L6IIQ$96DnvNshp`x@(-Zxn|a5_ofg$^Z>3e5CComjfy2=gdz zw7ymure#LlH~uq261!e*Z+(mSoZQG3#B&>xgLScYI>g+Qs$eb3z7Rp_Rpw)~o%2q0 z-mNv5p5RLyHG4(b~wA|>%Fload(zIiH=aM9w%(U9qS{>9kOh8KFa+DP8M;ohzO9aD1SHJR{RurGqueJYNiVs-_ zZ%-m**p-U%?GVy_lPK#aKO$$`@>d+r!psrFkQlcS1G^kTL6*ot-g&IjSK~4{^ig$* zqCz`b8*F)>fcc(90-GLfa%j0+>^;xY4-`IvR)v{@Xho(x>B_U zz;k+@Lf_7R2LPu2TQek>@w6bolx~N043gjd0YvD0xk0$Vj1dMr1~t;)jDrP5H%o}1RXPC* zCqUswQ;pK7jaAXcbfl?eT*PJet6Iy5BTRd!LqHuWGi~K2Vr8-_W2~c_GO=OVGlF87 z{yt_s@uhz`R0TdY$hMBZk?m_34tODs zU>h+1&(qM=7TWboRU&{Z>kRi&dsq*Jluit|AO1a5%9amByX z73bfLqULxcO_;h;cS=m$6JwH!B~euogF=0w*u((E#@gSSxC))5Kt#vgu^(M~JOu#Q zvH5rS=JP^PWQT;tTN!>a3w63S$>8dN8 zbg3@jG*O0HR8I-D7`|l zt4@rR=l(<7eA_OwkgDhi#xV2tZHSLQ>SJ9foo*<^;Cal%KZO@XN?)p+oEeWmxnUV} zFw2Z2;ylR4&Y{*lNr+WObN@oifr}UO<@s8Q8>Srd6=?b%cPJ)II)yG>KJicdsnLh4 z?D5N&1qzd%UW>T6Ex2+ePY})I&tASPD2m;?iQ?+Ym0ZViWCI97L1lbIu>Zgq z@ywLI3`)<#^EzPNhGLDx5@~Ls*Hmk2t`HjGxT}WDYB*%BmpI1^p2rainG8ZTQ8p}C zqETe39niVnKWeT-;r)e`c=9(t$CoSqj@r5z0D%0wxtKNMemwWfmvGC}eMnDj)f%k! zfDFcf2_sISYnhPNoIW!fb=OQ(m_fb63UygECB#J`E;fLZXVUSv*Qes2@4Stw)87{e zPwE%wS9u=E2~kLli$av_a6=wPFyx^%=piObux8qV3@vRTv;_qs9u;s99Tl*&hOh<= zrT`w;=UXHveF?_0vG3p$`2FAij8{JxB3z+iA~e+C!NTw0P=dCg^vRV)$Je_uajikD zC!>eX#e~6UeS=F??iA-V##tB@-tvzKXKXaL5ga}~4gk=# zWKURwC7yjM4VfHDvH!>bajjE6!*?g|*V3L4V+hLOqw-4$mR8iG892^&};WT^%yl{!<}UXTJJ4YDkM4iaWI zsr0NI*n6y>SXYym;{LIReEt_|y_?+q0=gEzCk92;(@@v!YI80f1qFhA#|OyQT84*4 zAM{C_)RbpZw+rZ6{JwszYi%wZ$oq;IovIy<9AusbXf((~iEM&tm>!+L0TusHLH{+M znSv4rdyWkU0F)KI14?V)u{osWQQL&GzK=WODeasO{ok=JW8Nx;G&D z-gO8?z@dnCC|v5`uPgiD+~t`7fSU%th9QOZe&&E`gsg#vulZG;29&~eD>P32Zi%H@3o%T?PX*mz9nokQ{%nQT}c#pSWKCrZq-B3_4SR)g>yaI`rvw>-oh zLXaBG@Po0RA<8`pkLBU@wYTDIQ$RCX%-Tv2pft?0-R4^uy}q&?Q}Q1s*)&SKtz-gB z(yCb)>(NRLwVYJB_KBcs&w;wPiTrziUYh%xo40Gkky26ED`OLXKA)VL5P-TcT0LSK z8O`S>(oWk`jgGmo6ndky3xpdaMm20;hO@1b>K&))+FZinBZtjEt$ zTK+4H|9&x6&h23|l*}@wp$Ma45I@J&FMst-gEXSaENPE)W>rQZjB&qdp=|maE<|JT z+YjTmAM8PV!ZOTxycDlIdOJdGGDA&LeihfX$pGZ`gb8LcKvnLs=hx>c zG*;`ZC;@e)^Eb1G62$NdRPoQbt-mxh_8ucHVZ=n?_*{MZhxqvA0X~g0>;hR%bsV`{ zGseI@F7;#LccP;meXPW@^E{;{NYX&L)zDBHiOz*9a8)L7M%H})x~(=ftxW<}EviJ< z?lX{-yabk%pIT+KiZyf z#>($AXeDVkyO%n|$DG-?x(9mpE`{S9$GA!Np`yowIC8KT>gy5_6%|5i+EsL^*o?IF zf8pfupP*yuJ|h*-Xr_67Rxaz0&Sl-;2KHm{upgtK=!ZCTpeL@?$00HKIx4#FLP6on zIC$WfD6iNnKQlwHjp>|d#}S#%K9_E}&Jik2_H-_Id-`#^369J`GRP0ngBs2WxTIn0 zwL(1y#JH}mThk4dl^x+YCoy5-L+IY)ejGZ~6Lq!Xe5IvbMR~;*q@{g`6DJ-;Ny&bz zI5+4QlW4ecdOiw58cqCQG*p7pZQ&|wfun@*ok?5<2QBvbyhR8z&ndh?MRceh$u06 z>Dt;TKL4r|pMO<~p#!I4*x+waSnw8-5|?4r@MRb=GzXVz#-sXNDQYgKp|L3j@o~a^ zc=*6$D9AsB;tsD!q_P5^elb!~H?2pKDZ09~h3NZ*q3&uB9%D#Pjzz=uQoQzVHu5t2 zhOah;c?vBMiKHDLLsRmb17oE1J`xT{b zJ^)qc;!Npb4HUSh&02#%V$cR^IqZ7O!NM z2|QJ_ssWuP7vZ-1Y#955z{(`ID3To9mjSMxZ6V1qD8{CpA`>_(V{s%41eWHK0H3Vu zhVES^B02FZj2!qBIu_iGeMh>X_F63BVp~vAdJ0`i=7Er_2nH*#aOGgrgoO=px$Pd` zE}d1>RaD8NID8aUgWCQZ-l#QT;$cGo8)r4HzQBra%h9)@2##|aQ%3$LD!a_V{^Q+H zdo3POfeF>pmT?#)_Q7m>ftdx%0vlaMXI1+LrbLEYj3%AK780H z2Rs>(!mazvhn|P6B91g#U9h1mdUP6xl*E-7)%RI+%)1MFk5{0sAr|p5Z7453iLS-( zgOF=z3wFhVwF5;>%M6czNz<$ao4TNTsd%l?{hmWf{+-x+yaKh2v51duLq+jPbS?fs zzt)HA2f&ho8>3Is4qIQJQwk4EGy$$PQOa%v@P`F*Kc!W6jf|<7MDa$;cXz^&9&sS# zx@^QJ;zCoDL2qHPd$@fJRz22^nt)i9m|nlEHHS)cl);Kfl6Og)NATA#CgGXcXAmFr zExtGYeVki(KTchX*3?=*ugxM23I_evwCkivP&$iu<4b@4K&P|QWJdCj| z@lmJ!+zf89ZOx=lFpH_olD3G!VvNGg034jdB~*9;Sj-KBZgbtl6FCg<(dz*Geq%p) z8|H#>4p&2bvKitOATmmT7zb=aZBZB2u`;}LK(euH=c-@`%kWe*T}6WdAt0`Gc$OEo zSPHp$f2$}poO|=)i~c$e_y6uB(z9R3;PJo2*<=62p>>&7krt-=C8V9~I39K&My{z| zg99eMNaiLJZX$5(7zrEcB$VLDrVM=D`56qGct3Ir|BhQ8FUE&|9BR;KVr@XgJ|;y2 zgm%Z)h%UFX-?+uE=R}C_Jyl4WO@^pZnWW8fziq47aHxtCG2!kATllhzn=$>nFM`r) zG&S6a4_=(9ha;T%>0z2)N(|>;(d_L!$6%|cn^Gptsl>5B2Z!6HLVh&DY;xSvIuQby z2|NoasUJp6Mn!I?EYul8W4(*_{_z0rcp!k3)CDLgdJzRhku^k1^9U?@{}G%zlqj0o zAeShHVI3$$HzHHldZ?A8K-<6*n}u%$S)0*3Tt_+;TQV=T$gTdY3;GWkgSfcW=+XOK zEYIwXx|)D?M)f1g+=p&y1_autT#+z?qq9{B_~MgiaKp{d!F3NIFaK5KE zInvS?$z#Gf)^SB#MIhiH;0kIqAwoME^2Bh*M-kISr8pn|n0F)U8eGeW&mc`&y1+O* zZ8B(0+nFquWWmH_!ZpBOv^26Mi?Wa&UJR1{5 zkGHD=w(dEZ30yw~sVSdF{;ZU7P>I?$G)Ljp1$W{5w+4`!{5gv9-$qe8>tSqBQ9L7@TNKiE3>5TGfLo~n#--V`49v$sVS32NBbDx!^K*L?V&#%<+f^OA6pw`~ z9>dJ>FTiz9pdjac6y!vHzCDNk1INx5pmT@t!6ET?+>gsCEp&d5Xc!-au(xnV@uZ*6`e?if-yw;fBO2?nEkS< zzO~jq&--VFtAJLaa+AvljL|3tl>kOWtrxIOW7DLjX_{1%Cek)Gu^Jnj^g~H&V$w8e znnL~354AC-wXrR&7NLbPQ*ZzoZW)}Rf|&tk1P12+K4-7h4|`pH>pbts7cw*N`#$G6 zXP>>-TEE-c69-G>i>C*J9U`ZSW*=r}(fB>|rCapx-u2h%XMglTdg>kb(9iv)=g}|z z##`yDJ9nT8+39>Kfg%kfL~hkg0wys`5Oyt6HCJKMWsnEWT*Jj+59}He7Vldm%*?{; z#AsuqpZ}5nPEWhz{iOO_`t|?yQ}m}_y|uiP%3W~X@1|9NGnG)nSQBrxz2_=3yc_=9 zha^ z`EeeX}-e@cbS90}1i?dC2E(%%FWw|)&nT^&3 z92tXv6z1GyK-9)kn_lgAjt*q8MI;fb<&j8oiUP}p{i6kl{?*%l zhHU39D1 z`#`RXRA6_rMcS`HU(OXO9%e<2I_bTVpfKDg@az#k%9a%KC?10n&VxyJP3qIiZmz?b z@wqC~i(dH!`tZF!Nz8wl{^;Isp~pUV<)5jkgJz(gMN2y2GlS-CY9wBakD$9}rdLq& zby@3ixqq^7%K8WkeIj*mUh+ ze8pK97EPp?OHr9Z1*$K^WMh5K*S;!r@B97=z5WM&mrkb-(VO4$hx8lo_-;V|FYgyR z0@I&kb5R=KLK)usKfjqCeBfWu8{Yhf^nw?Dke>GRj}Y^jzWVs9=;4R&qCfcG-$@_5 z=P%G3fB4gv`n7xpDF~tWzx(<0C!cvcz3GSVr5C*D59rQ2KSH-}zl6T{g;&x0-uu0D z&wKtN{ls7Wa7|@B$|rS#e4LhB4t|UvB1taWa4a%~7vgAamu3!JMObS7gNDY%nh`Og z0lIWzdiU?WfIj`{x6=>2={|bF3-6<+`~7D}-7#wt@4oSogD0gJTMe?s%S^uKY~t^r#h$kX4EF{FmC+=plT)Lg^jrhN%m?V!)n{pp2mCXh zMUOuA{q*rqyoUbEyIxI?erc2+0!5Y~G{J6OhE5z;Cs|p+S0k&jlS@}tj`=g`SBFy~2S4)?`rUh8 zN}v7woo<2|Or|ncR4(!3v;UZW=9{&8x==VQ( zH@*A*=g?cf`!g4atRgB8A0*JQHm74+&4V3i(2OHg>&io-@|ZM`3@jSHm9~1liP@CmRZHSU`Jh)}A8#F&p-2M)F*ZtpEo(BL@_zs(H zyi5foSkj%{8^hc;$A?W@==+_~f$LGt)_w$x#^Klc-UpvU&-mTHPcQt2FA@{ew|&!> z>HVL1#*OA^;?Fafr^3tGUfCKeM3^s9q$@hC24Jy%hA>@E1!E>Rb!|f$OD0BXgI6tJ zK72bX(?9slA19(8@6D>yp7Zy>{Ry?eeUDmu#HthZLRx3&vaAukwD#olTr}zl6ng=r z{+%CqF}?GH?jRM~5*$D@KG51|Qb`-x;2HO^EDl zSK=mARQYjg?lN6k(0QKgWj%>UC+VUnw}HSxN)L!o8ef4{J6{Gba_Zl6bCXr0j5+GG zpURIuBEW=(Y(fXh6IRl|D>`QG`9{SB?h0-`Z(JAg2g&gY}wb~gUGh5P#$Oy~0iv#UM~il5D= zA4Lt)S*cnr{`x2U7xikzHI3-s_r##a+9UgHi%{JKk`ff03ZNKL_t)2)>Myn zfqf;4)NNbw7%RG9ndF7OIUK#!^wN188ozXk_ab}S#I;?u7rgCf{s+DMrN2sV|Fz$s zPk-i4A7T&<8JU|(L@Q%jmyyxf9O;)^9PZq{z0+wMB*EZ_JWwvWTgD(M z6y&4s9}cXR(Ums0=%c(DwZidTk{%eo1zeQDXn54u^fK>TCSB8;*Kc|L*XSSq>`&1r z9{79otMB-J+T)pFgX-rIYS>cWMRYLsi2-*t?*^!0HF>pvWAPX!hT<-+mcs;0WmpJ7 z&>Gn=f8O#L--JJ|b*!oPKXw-lZ4XmP*h51C!%4d?If1>0Ai?lmp=6s%*!5UHyiL6^Yn5+2!d^i)~GVTn9g8q!v(N7~LAVuY8U&hNB(SZem zUhC03PgQWyrHb*QhyViPDBpt^a&p)23*58vfncW2LhcNeCs%DG2GWPB11LOfQW>YB*fE3Q*!7H;JBbL;GhZj8a zn*Q0}`gs!mB7NZFZ=-+yJKxm|VKMs40L4e0r(7tf@E6zG+Pv=A{aoH0k=&<3%+L+0 zb+(kF#Jvg0hQS)Cp{q41ysGa{NTCOVG&4GAdTR}3MtHDN{2pjVBpu=;`G~f0Qm8c$ zR09U1_d{kY3zt$p88xQSS&cT~o466>$n4JI;dg-2msoch1LwaS_jH5?I@faPwTp2A zkya?*^J|E~IfDTmqUejJT(okD#y%uBrm0zuwY@v5;pE?EH?7|f$!82$2mEueVK__( zHSt3x&s(nu7)eLpuBhOqN9^|Elr&G47_(Vpte{@h<}zg>qtwl&{@1_A(Xe3UCoXb( zQhaZ4DU8)HBjjE9;9v-r;Ghj|OlSjYTevTCAMP9>vUE}(W4RbRs*Vi^LbB<$3;AfQ zRp>Kj9|yJ0o28);4Z)tN=@S+Gw7Q=5Bdln_VJd zL2FCYa~xx&Kqs-ufrVrsm|jf*gE?7gw>e{Gn4xKDvI29OO7C@1kkVE)%x;oSr{$2n zo{c4XXBJHxXkXAx*P9q?CM&^a;FTE|O~JI8rP>tpFsEZ?M`aR5HYvfT&k&1Z zhQkO>ByOMOs7mm?&euDs*1K&m5oMA7ee<0_%}S4%U@0eajRqG?8j=q0NDwK3j-$-! z(!J2EYrOGY0sV@>;llnyt#C1hzoluU6>@-8oR-WWhdUNSBhCA|ZBhnRe$7+E*lfji zt>J=zsodIg$6(qDreC;EzfBmOO+6{wq!zh-{^j?bX&XZ4^Q3(m?e5gA zWSP-A)#yAY+3hg4TlacSYXw{s4Re-rXt*5fK7B7=YjUguE79}CD5xP!cs@)Rs2W9v zHKl8`FadIZgXD*Z?%1}4#wZ}Xz9&ua2*?O3ppb>W9^VH7W=k3=9w>=11R*2RrhVq- zgqvH5Jc-pQ!MWBpcg_Z5_vomDf~X}CFtIz6<0-^R$WN_h0M-ta5 zanUK^Y^kZD9V*z4Y?4)7m8LXG8L=Y*d#$Y^u^1Qz60LBc_abzbdv7q~D(l=GlA+;b zvSi$)-dXr?>0rm*)O`ltA6@oaex_%XtG|;8j4wl08s<;W;y5>oih!o0cLi-$=PLw&3UDm$n zPjGgp(KbA4ilAbw7&bOc^h4kFpl3W2z2~DZ>je^5osgHp6?>HRx7uX`NR^v8RY`cH z-e1*dPvnwl2GW8!9?m5V$_OCt%o5z^?ZBdF_3QzBUTd^|d%Ag73^+q0&R|rPDKh@~8`i^! z+EU#=Oy`&o&3GCW1v-xAp21`@qR}$2dwkWZQ#1z~adYDRTLx|hy6ZCYi=1ZxKM3}H z6y=3RL|ajXfR{P^pnxa>bai66Ik7RFw@!ER4sH0L;ca z1|i@Sd9=z$K1vmFg>qNkT%oIR9djC~^T4+gC88Cs>lK^i8@EQu-m^^64v&+xKn7)+!>Q3YQEMtqahT#p7}_;L zQCC~CP$&lg1V(0&E1ZWKcpL;3aDs`RA&TNf?0Hw$K$fcnp1t#|HFb{W1KcCa=#R=w z$BfKTTsn<%*7u07Z0Oauxw%*(x1O@C_?vx_Uh6`mTl^X4b)%k^yy=~Dy4=8B;|J6E zY{a~DHpB{g!YI(z5K4sU?z>l{&7+UKg+BC=XA^r)??TnN7#ZPyKML(m)b6;J0Zp%I zKIa5pJmWje7*_<4`2b21-Qg!=fp|fvjqJgVN}UuzD`X~UQTwi$FB-@whfAd+^Qz_A zZnACwx>v?PH|gM@`9;lQ$vO?7Nf#J<<7=)}G_39LJi0p^09ki8I_^-j< zzRzBx`%osHgAg};Um&`1ABWRO?NGRgN4xBE_WO-Nb-{`th8UG@y>aXz?2yUX{3zfj zg-Z`Rz*7TRU=hQ@$$-kL-f7`g@V694Ga`RZ)!^CyH`VQ zF%vchbrj;>1=_C0%!%~xO0SBnTPC^01*<$~Ybt1iCOCd}q#?=ycnSSZ;KnmTe!pmPsUC~CRV2!oa!_S(4Ln9gwUDhB`})?DQ|3m~YK0nE!77=|6_=w0n}1Ehb6*&F9zOR!1zL0!;_eK$;iZ%{>2Dabvx5M`V7 ziOylT*<5`CapI&-azjQi(gt6^cszkFh0|vo&N_&Ar=)0xkrodwy+boXIV=Vya7~L5 z8X&D<@^iyf9KGO@3NF+d4PjcDrGb_j=c0|pW?o4TsThNX&>Y<(-3>W3usjb92A&bx z_M1e+vG7|{Vg=V~$`>HaPk4aol^p58>~v8^q4bJ^8kFw3jI=svwA0wd;$?SqXY^h+ z=^{}WN!Jz|P?mD5HPl1bggDj#ho$;7;tOhgWWUvfyH$z`S_`L{)&#yEv_XMqN28IB zo&hNbXXa|i0SUl7S^*j|cG;Lu3uZMz-D}!>KDa%?`yoVKbM7F0#B2rXG-uGN`eBws zEd`H;o9s%=NF|0QnggV`#;HcKD|ML{)zRI#kx9ao5H+c@dcB1IRPW)+RbM6Y_~WAvP7 z|3^3E`o7#_M5H0bMvEabL#MmPe^aNNE5t=`nfakXi-Lo`Pd?QOK@)fvGhz;qpVV6^ z47dmkv$)P35$drp&;hI8)6E-<)3*NKT#Kt@w4aqu+o<*Kjq#Au=pqAS4O2!^V#N`I zqVIW89lB^@BGJrzP8mrI3F2Nd40wHbYBHPmrXkAad)1v!Ib|ccdt4YsYy``O02L6a zSdnD4SUb!T844q zOX|!n8n7!QRhurD(FByC$qRS;ls##lKgD*(vj}^f%V-q@yCf+uK48;wj%D;P5o=K` z2|Qa3+}l2G_&C|?Jot|Od2{Mv>eL&d%y4$ia8Xn325^*{uVxIcQn!rRtyKFyt_e*Y ze&t9PFpK6p48296P2=F&-+4^jignJk2H4B2OudvYvryN)mn_cJIi{`A0!lZs(>PH$ zG*UFUUhclvtv)&!X&%UMZGb~YuleRL({rD-Vyo`^)N3vpYqBmc?>gCfe_}Aqu)P}a z0tBp(mNAsHcM5ZLXPp0S@B;643}G}r=RgO35Y9&!nxm>dVK)JM;5H1uT7b>UC0G|tqAoRd3hvsYIo9_p)rg)e5&tNz&3)Up(=`hI_i0IF=wsdR38uR|S_tH5!L<%~3FUV&ic@ z1qZD>T5wyogWgPo9_Xkhf!S`UKsQoqEcfqY{dyPkr+(6lNVB5xHb;2vQSksreIQlN)*xr^oniuCr6|Y+4N( zT=cFvsqqIHwDg!#Y882C@M8+s4v{&mWfn$0mkx!S)0Z6?K@f;RMZ~&+~?cdzd{=fupiyIY^{KkD!Ie0%3kf#Rz={+d9GpgBS)~aYWgvNd2aLZFH$jWdSv6z;P7|Bq_6`$B zi2;fB=N|(Y0cUhwsK@$&I!M*mt8Af(s?B(5sA6a4i=ugKtLU7%GX6L;-9v~)=;SEk zex4rN<9X)WSd>-Qc*Z1OQ=7H)#!Fu-bl#O7|H^ghYuCS?-kkm3ZzAjbw`zz9qp0d_Co-Zx{@k7P zGr#nonszejg)jhM-_xQ+9qy^1P>ghD4IL62xS0jsAb@fzDoAN^sH1sCvQSB*G9E}F z#-q%v@{Dk_E^3E8hn-!yYycVz;nHjZtEx z7$MBXV2&{)60)(B}Adi)a#hSkb?$= zK84C0a4~moh~0uAh?N5hj8|;Ys>5N}jW^5(XYBj65V=Xya`@idaF*Obyx(t578I@v zvfczYmV=)U3Imqd!~}F(g~UXPWRT#gTPZkpaTPY(P!*Phcpp&7F*0%UW8} z#D(np{fx%c;`>>jEN8AV zsXG&P`7HhHZ&4XY^>ReF?R9^C(AU94%uZ5@Y`kmL#YF=rmio7~(Y9 zBBU-59hUyl)^$(W@LPBHiR@`!KvBms`BH;|XuhUCCMcW>v6VxXchc&;C`d#j=u?M# zhgrHydnzdxkWso07OmXAKrmsi2?I6}Z4+Pd+*-zAg4NVM?=QJN%P!6F;yav9vd}zT z2SZQ-H5#i;a$TEN{$33@Y6I@EIF8PaDqsgROLy7ME=)CLqpHD>3Uj^)j1Gbuj$RS!I1{YzP#8L)+t2Xd*ou!;Tp@d=|@W%CI za~5vp*a4Sm`O-}vU4FwrC7oG28i{@fkALktwdhDY-^8`dp$%4y^r|VGPIYsygUi07 z+S}k(bQ*OU@aDrGG`hH_n;Ik&_%}Px9eM{5|sDIma@z@ZQd)bM*$6A z3^LQWukTfSMC#c(MH|^MzR0GQfnw6kp%^wWR5YiaqiN&j13LT+<~VEJATibmf-D|^ zC9U7(#)9A`qO`it?nSW%n_A6-2sc0>)0dVHwaTK{TfQfU=v6chs) zf>aWejG-HLHlb2$OA`BR;m(E0wG^s#fmdSmsu#V7h=~5*!*8Jn9(@Wm+QdO?o0@x| zdzUj6mxdp7WAWB)ZZf*`oJK=NUVAX`B%Q%)pf<*8e!g}-FJ{rfrTroh{-$Xfn(G}Z z>_FPnmlY6SKGxgD2OV(L#53!P_4g*6K_gg*qFE24gETkhNH_1zv(oec&Ymd^V>P4uAydpPHowy{j9 zU@FVKX-n#;Po2p^7ir)Wy9o8{4-Cz&!d!*8Q)L3}L~3eK;#8M8m4?xQ98t+K+)DS- za09MPi5PqoC4})ifpL(+rh7xwD7w}*$MZDpsp65rL9ipLhv}&m`l*gSr=dnTFiYL1 zGz!ylQ0Ak=>JTj6rca!4!eF*k)1rfbyc&F<>EeHn=XH#PxZvayvX`N8jTyl}6D?c| z70H2Y0ss!cJPFJ|0?idBo$6*_M03|{iXzXG#huQ%Or~`4L)T!1tWD1PG`uGC&1IbM znu?}um_LoWd%oc;@C8=!W}GN$tl4NVMtr|njaHjHayO|+r(1XKRWJZ6N+CHC*oQ&o zfW6-J-Cc!I&%97#^So3meQh)|F~xL~wThzZqyur6>*cy^?(odu0N-Q4Q>%;FyAjy4sez za=87%As}wpl|@rT6PP;*honUyc0m?utYoCq8^J+zzScB6IiDx(yXJ&GXnib9+aNlf zMrn$SnzKIC$T{p#bS_Og{JOuVO`4$&CxTK=r}57^*U)JLdhW4SY6uoZj7hnBBb9be zONKzXZk8bz9>B34#HrMwWzH-~80-ZrNkD_vOswF2JD+F1k1_mE@HxPoWjfM`x!gI` zU1Alez7!fsD$}ZF=IBnX5|nH-s-uWGI%LcO9X*%oqpZ0Jp zC33<*4=laeac}6)F<3xjK*WcFc9=SY9wSLa7Qz!~aG-wdPTJ>X-S79%b2qh67=|ou zIyl@$PJ0b51hT;a{bPF)%I8fEy@iIdUsE}RV9Mn(EtlKJ$c0D zpvQtVjaub{Yt$)R=A;9VvvfI@a+D=ES+Y3)xe$K5YNSB#o+BDVAuQs971(V+z^@xo zP#7993}3dvtvIKH0bw}lS~ka2{tk37BE#oS6?zH10fTRGX zj4=8Y-m=16akqe_QGn7Yx2`SY%l}S3>y)2Mbm+LK@rd!VNK|^vl&D2rlFpbjm zr1gY>{BH$%ZoWV6nIUbBNr9k;9BRMgMl;otu^?UlTE=G{*`x<*z7eI4IQCE6bC}&Z zJY_G|hh@N$%C+SC1cGSOSyx-U=w;FbdaOK*x?xplNKJ|6wJxMJGG(WE!MBP&79!rd zNz8UjlForUpJ`Pe(Y!G;jVtrUrL=j%xQW6v$}CZaG|dQfW;#V=K=BuwNEi@Ak>&+) zG#3@Bz7EAgHQ)^P1AawCz@d@%cTLX)Oqdu?bEj*^<^(%xpL(m66JInzT6*fS`HN*h zg`Jc!En|~3JrJL2Cpt^Z%~XWT$vGQntDH{*?Qe71vx*Se!q7!vI#9aV*mOdBklGZk2GZiju6A4g7UA_XCW0$0V(z3ga@z%2{l2& zA#KJdalxHFID9hLOlBr79tsc(7EMn?rtN1e1rrX!c(f)KLhd2Rjbhz~pw{P!WU`wLanaM3g=d-S9dc=TCFSxWa&b$7u zzU!Pu+pUdQ%xT~2TKBWiwd-K7ZjE%(Uk%T?mN}L7eZ^4h5!;gkv)GH3_Hb?_arAF> z3xkYKKHLh8cQg)Rj*|luN0GuvXSg$^n&vv-j70%mBos>fxtgFf!y3HY(<9Q$5v=KF z5_?R{02llfe`SZb6x5rA7eJsOFc{&=g%4KGigB_Fd9JQx z;caw%o;eup9tQQEh5tb;-{suz)0Z5*hK#Xq^Q~fKj3DpSEx4tTbb9* zKxvBKnNu?YN(K{K#c3T2*jCnRj}gIlK3)z(U7}SZo{@GKo94)W+!j_L63?s@bo!>2 zb8GH!zMIWT5lz*8!$Y`$T=DQW$neJ}WjoDDq+-5XbspaI(NA zY(h!{H*IJ|S|!^c5q!8`gxSE>-B<}cUM||-1MAb8_hDmOs=;u|>A~fh%jtQ5Acv}Y zL>d?kW@9B#>sPZc2lypEXYFISSJiN92EqkGX7fRzaW%_A*eh+!*~?Iq?>>6- z860O_S-){+#QlrUOcUQ`vxe^GYcB|ok2Jyb_wXcxuIIi6QCfQ7Fc=*;cW|JP;B4Jp z!%R1LM?@TK0;Iq4qyN{&CY4(kF-26AV|C*gCMF*4Vi++&xVXZ3RLwGqUD`ZV&;a+6-4W#wiZ24LWBj+7J4XpL%`5N(X9 zpixKckrlC@)rVA^2bLYssIf^yO2VnM)TU1W00wnQL_t(N8s)Kx$K}w<-JM2rS2|s- zwRe3z*Uz~=H+87-PIkd0W{%!*#x|+Dv@fJ~-NW;) z>3cwtJ8qTLe7YKm7K?zBq#F8lw8Ao?s6>_vapX(bl^Cw8irLqh=prCS{J9P1RRR-dW?AK#-kWvWbSlRAZx?BdetMx zGUCbfFa<-J>GAgs3>iay5bTk7ra=KWx+&+gYe_FTzD8#B=l_py!r<$lPurmLJ`+vO zEKAjn5^}%&2D#U9b_P=>ngWIbHyX|y>_GvKrj;0{*bz2rgTQz`fe=N-uZGU!UAluA zo3UpM;TzdvcA$R~^>uuKPA7xNdM-R%eGJqkrckP?7XH8eXgxcPf)2&$_n$z)dU@%eh zf5XBp6FJL!Ib=1UyDD9YB!Zh}nYc&M(G#LOl?$-Smg968ZtXgA@KDBA+;X8$Ltp@J zbUjxN@L;BsjI6Jkvwl#t$W2=+C&XwdcX}4L_G6aSB@9E z*~UZ-r6o$7lO%7b+RLUcAhqhiEgld| zPrCJBX{5QkC1$91P$P|67O;RzIeT;!8yHKV^gYl7;|rP#Q%5XrqPMr$u$Cjenn(lp z24gf31M@Ju=ycZ0yVY8c>ufHb;P^_!(BKCH!W~@+bu4~Z^2#a>hhm-FCjAz&1spno zHhB;cpUS*o87qb9OFt-(W`*0s>DK7QSuhO(^_zx}cy6+(+eJFO7pogh?kTk^ii5$SOaWz6t zFOK0xG-=o8zUxGslGKJha=+I^L1NG%SU7;r zkHHKl>5uu^mDB)<8>p$5M2{*iuOx0qO)4D;)=fj1)Vc1>leE2Nz=G3szlQVd9tJoB z&Sv3)L3A)Wguff_6Q}>flUS#MX_{FK0}jZ~4Fl1BJ5Y^^a48Hp9T7@*-aucdBF^ZD zW)Y;~89@{kIv#ULS>RkXhM|D#nA>nE_qfvNwXVCv!Ig0y8SSXUPUM!`aKv~a(rkLe zu;B0Nv zMPsV?QwqKfIGz|jm?s0Yjt(<)C7O_i!Bpr}IC+C&j3kjfptFNr$dfg{bm@}Q%yEn_ z`{!f=q{x%9mQ8YBGnM2^J_N^}R#9twgWLiAbC9ugq$)QI!mOEx=4U}iVQ3n|{r!%N zRsusPTz(TiSIB);Vko212X=@e=dHB@(=4PioCz-lavYqc&1fg1TSNNb)4I`UOPs+Q z$p;_-)L<<|rmZb?Ut>3&!kz)HA%+sWt|12Q4ac>2p0?OVwPQIemU~~-1`nN8614NF ztCyG%MP$%W3Rkq3LG_mO>~>x`i0bdCxS?L{g-cSPkV;{m&JlH((aQfVT|0vYo09@< r_ + + + + + + + + Khadhroony-Bot2 + + + + +